summaryrefslogtreecommitdiffstats
path: root/config/makefiles/rust.mk
blob: 894e2b341a53be0b3baeb7ff256eb1edd9fef598 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.

# /!\ In this file, we export multiple variables globally via make rather than
# in recipes via the `env` command to avoid round-trips to msys on Windows, which
# tend to break environment variable values in interesting ways.

# /!\ Avoid the use of double-quotes in this file, so that the cargo
# commands can be executed directly by make, without doing a round-trip
# through a shell.

cargo_host_flag := --target=$(RUST_HOST_TARGET)
cargo_target_flag := --target=$(RUST_TARGET)

# Permit users to pass flags to cargo from their mozconfigs (e.g. --color=always).
cargo_build_flags = $(CARGOFLAGS)
ifndef MOZ_DEBUG_RUST
cargo_build_flags += --release
endif

# The Spidermonkey library can be built from a package tarball outside the
# tree, so we want to let Cargo create lock files in this case. When built
# within a tree, the Rust dependencies have been vendored in so Cargo won't
# touch the lock file.
ifndef JS_STANDALONE
cargo_build_flags += --frozen
endif

cargo_build_flags += --manifest-path $(CARGO_FILE)
ifdef BUILD_VERBOSE_LOG
cargo_build_flags += -vv
endif

ifneq (,$(USE_CARGO_JSON_MESSAGE_FORMAT))
cargo_build_flags += --message-format=json
endif

# Enable color output if original stdout was a TTY and color settings
# aren't already present. This essentially restores the default behavior
# of cargo when running via `mach`.
ifdef MACH_STDOUT_ISATTY
ifeq (,$(findstring --color,$(cargo_build_flags)))
ifdef NO_ANSI
cargo_build_flags += --color=never
else
cargo_build_flags += --color=always
endif
endif
endif

# Without -j > 1, make will not pass jobserver info down to cargo. Force
# one job when requested as a special case.
cargo_build_flags += $(filter -j1,$(MAKEFLAGS))

# We also need to rebuild the rust stdlib so that it's instrumented. Because
# build-std is still pretty experimental, we need to explicitly request
# the panic_abort crate for `panic = "abort"` support.
ifdef MOZ_TSAN
cargo_build_flags += -Zbuild-std=std,panic_abort
RUSTFLAGS += -Zsanitizer=thread
endif

rustflags_sancov =
ifdef LIBFUZZER
ifndef MOZ_TSAN
ifndef FUZZING_JS_FUZZILLI
# These options should match what is implicitly enabled for `clang -fsanitize=fuzzer`
#   here: https://github.com/llvm/llvm-project/blob/release/13.x/clang/lib/Driver/SanitizerArgs.cpp#L422
#
#  -sanitizer-coverage-inline-8bit-counters      Increments 8-bit counter for every edge.
#  -sanitizer-coverage-level=4                   Enable coverage for all blocks, critical edges, and indirect calls.
#  -sanitizer-coverage-trace-compares            Tracing of CMP and similar instructions.
#  -sanitizer-coverage-pc-table                  Create a static PC table.
#
# In TSan builds, we must not pass any of these, because sanitizer coverage is incompatible with TSan.
rustflags_sancov += -Cpasses=sancov-module -Cllvm-args=-sanitizer-coverage-inline-8bit-counters -Cllvm-args=-sanitizer-coverage-level=4 -Cllvm-args=-sanitizer-coverage-trace-compares -Cllvm-args=-sanitizer-coverage-pc-table
endif
endif
endif

# These flags are passed via `cargo rustc` and only apply to the final rustc
# invocation (i.e., only the top-level crate, not its dependencies).
cargo_rustc_flags = $(CARGO_RUSTCFLAGS)
ifndef DEVELOPER_OPTIONS
ifndef MOZ_DEBUG_RUST
# Enable link-time optimization for release builds, but not when linking
# gkrust_gtest. And not when doing cross-language LTO.
ifndef MOZ_LTO_RUST_CROSS
# Never enable when sancov is enabled to work around https://github.com/rust-lang/rust/issues/90300.
ifndef rustflags_sancov
# Never enable when coverage is enabled to work around https://github.com/rust-lang/rust/issues/90045.
ifndef MOZ_CODE_COVERAGE
ifeq (,$(findstring gkrust_gtest,$(RUST_LIBRARY_FILE)))
cargo_rustc_flags += -Clto$(if $(filter full,$(MOZ_LTO_RUST_CROSS)),=fat)
endif
# We need -Cembed-bitcode=yes for all crates when using -Clto.
RUSTFLAGS += -Cembed-bitcode=yes
endif
endif
endif
endif
endif

ifdef CARGO_INCREMENTAL
export CARGO_INCREMENTAL
endif

rustflags_neon =
ifeq (neon,$(MOZ_FPU))
ifneq (,$(filter thumbv7neon-,$(RUST_TARGET)))
# Enable neon and disable restriction to 16 FPU registers when neon is enabled
# but we're not using a thumbv7neon target, where it's already the default.
# (CPUs with neon have 32 FPU registers available)
rustflags_neon += -C target_feature=+neon,-d16
endif
endif

rustflags_override = $(MOZ_RUST_DEFAULT_FLAGS) $(rustflags_neon)

ifdef DEVELOPER_OPTIONS
# By default the Rust compiler will perform a limited kind of ThinLTO on each
# crate. For local builds this additional optimization is not worth the
# increase in compile time so we opt out of it.
rustflags_override += -Clto=off
endif

ifdef MOZ_USING_SCCACHE
export RUSTC_WRAPPER=$(CCACHE)
endif

ifndef CROSS_COMPILE
ifdef MOZ_TSAN
PASS_ONLY_BASE_CFLAGS_TO_RUST=1
else
ifneq (,$(MOZ_ASAN)$(MOZ_UBSAN))
ifneq ($(OS_ARCH), Linux)
PASS_ONLY_BASE_CFLAGS_TO_RUST=1
endif # !Linux
endif # MOZ_ASAN || MOZ_UBSAN
endif # MOZ_TSAN
endif # !CROSS_COMPILE

ifeq (WINNT,$(HOST_OS_ARCH))
ifdef MOZ_CODE_COVERAGE
PASS_ONLY_BASE_CFLAGS_TO_RUST=1
endif # MOZ_CODE_COVERAGE
endif # WINNT

# We start with host variables because the rust host and the rust target might be the same,
# in which case we want the latter to take priority.

# We're passing these for consumption by the `cc` crate, which doesn't use the same
# convention as cargo itself:
# https://github.com/alexcrichton/cc-rs/blob/baa71c0e298d9ad7ac30f0ad78f20b4b3b3a8fb2/src/lib.rs#L1715
rust_host_cc_env_name := $(subst -,_,$(RUST_HOST_TARGET))

# HOST_CC/HOST_CXX/CC/CXX usually contain base flags for e.g. the build target.
# We want to pass those through CFLAGS_*/CXXFLAGS_* instead, so that they end up
# after whatever cc-rs adds to the compiler command line, so that they win.
# Ideally, we'd use CRATE_CC_NO_DEFAULTS=1, but that causes other problems at the
# moment.
export CC_$(rust_host_cc_env_name)=$(filter-out $(HOST_CC_BASE_FLAGS),$(HOST_CC))
export CXX_$(rust_host_cc_env_name)=$(filter-out $(HOST_CXX_BASE_FLAGS),$(HOST_CXX))
export AR_$(rust_host_cc_env_name)=$(HOST_AR)

rust_cc_env_name := $(subst -,_,$(RUST_TARGET))

export CC_$(rust_cc_env_name)=$(filter-out $(CC_BASE_FLAGS),$(CC))
export CXX_$(rust_cc_env_name)=$(filter-out $(CXX_BASE_FLAGS),$(CXX))
export AR_$(rust_cc_env_name)=$(AR)

ifeq (WINNT,$(HOST_OS_ARCH))
HOST_CC_BASE_FLAGS += -DUNICODE
HOST_CXX_BASE_FLAGS += -DUNICODE
CC_BASE_FLAGS += -DUNICODE
CXX_BASE_FLAGS += -DUNICODE
endif

ifneq (1,$(PASS_ONLY_BASE_CFLAGS_TO_RUST))
# -DMOZILLA_CONFIG_H is added to prevent mozilla-config.h from injecting anything
# in C/C++ compiles from rust. That's not needed in the other branch because the
# base flags don't force-include mozilla-config.h.
export CFLAGS_$(rust_host_cc_env_name)=$(HOST_CC_BASE_FLAGS) $(COMPUTED_HOST_CFLAGS) -DMOZILLA_CONFIG_H
export CXXFLAGS_$(rust_host_cc_env_name)=$(HOST_CXX_BASE_FLAGS) $(COMPUTED_HOST_CXXFLAGS) -DMOZILLA_CONFIG_H
export CFLAGS_$(rust_cc_env_name)=$(CC_BASE_FLAGS) $(COMPUTED_CFLAGS) -DMOZILLA_CONFIG_H
export CXXFLAGS_$(rust_cc_env_name)=$(CXX_BASE_FLAGS) $(COMPUTED_CXXFLAGS) -DMOZILLA_CONFIG_H
else
# Because cargo doesn't allow to distinguish builds happening for build
# scripts/procedural macros vs. those happening for the rust target,
# we can't blindly pass all our flags down for cc-rs to use them, because of the
# side effects they can have on what otherwise should be host builds.
# So for sanitizer and coverage builds, we only pass the base compiler flags.
# This means C code built by rust is not going to be covered by sanitizers
# and coverage. But at least we control what compiler is being used,
# rather than relying on cc-rs guesses, which, sometimes fail us.
export CFLAGS_$(rust_host_cc_env_name)=$(HOST_CC_BASE_FLAGS)
export CXXFLAGS_$(rust_host_cc_env_name)=$(HOST_CXX_BASE_FLAGS)
export CFLAGS_$(rust_cc_env_name)=$(CC_BASE_FLAGS)
export CXXFLAGS_$(rust_cc_env_name)=$(CXX_BASE_FLAGS)
endif

# When host == target, cargo will compile build scripts with sanitizers enabled
# if sanitizers are enabled, which may randomly fail when they execute
# because of https://github.com/google/sanitizers/issues/1322.
# Work around by disabling __tls_get_addr interception (bug 1635327).
ifeq ($(RUST_TARGET),$(RUST_HOST_TARGET))
define sanitizer_options
ifdef MOZ_$1
export $1_OPTIONS:=$$($1_OPTIONS:%=%:)intercept_tls_get_addr=0
endif
endef
$(foreach san,ASAN TSAN UBSAN,$(eval $(call sanitizer_options,$(san))))
endif

# Force the target down to all bindgen callers, even those that may not
# read BINDGEN_SYSTEM_FLAGS some way or another.
export BINDGEN_EXTRA_CLANG_ARGS:=$(filter --target=%,$(BINDGEN_SYSTEM_FLAGS))
export CARGO_TARGET_DIR
export RUSTFLAGS
export RUSTC
export RUSTDOC
export RUSTFMT
export LIBCLANG_PATH=$(MOZ_LIBCLANG_PATH)
export CLANG_PATH=$(MOZ_CLANG_PATH)
export PKG_CONFIG
export PKG_CONFIG_ALLOW_CROSS=1
export PKG_CONFIG_PATH
ifneq (,$(PKG_CONFIG_SYSROOT_DIR))
export PKG_CONFIG_SYSROOT_DIR
endif
ifneq (,$(PKG_CONFIG_LIBDIR))
export PKG_CONFIG_LIBDIR
endif
export RUST_BACKTRACE=full
export MOZ_TOPOBJDIR=$(topobjdir)
export MOZ_FOLD_LIBS
export PYTHON3
export CARGO_PROFILE_RELEASE_OPT_LEVEL
export CARGO_PROFILE_DEV_OPT_LEVEL

# Set COREAUDIO_SDK_PATH for third_party/rust/coreaudio-sys/build.rs
ifeq ($(OS_ARCH), Darwin)
ifdef MACOS_SDK_DIR
export COREAUDIO_SDK_PATH=$(MACOS_SDK_DIR)
endif
endif

ifndef RUSTC_BOOTSTRAP
RUSTC_BOOTSTRAP := mozglue_static,qcms
ifdef MOZ_RUST_SIMD
RUSTC_BOOTSTRAP := $(RUSTC_BOOTSTRAP),encoding_rs,packed_simd
endif
export RUSTC_BOOTSTRAP
endif

target_rust_ltoable := force-cargo-library-build $(ADD_RUST_LTOABLE)
target_rust_nonltoable := force-cargo-test-run force-cargo-program-build

ifdef MOZ_PGO_RUST
ifdef MOZ_PROFILE_GENERATE
# Our top-level Cargo.toml sets panic to abort, so we technically don't need -C panic=abort,
# but the autocfg crate takes RUSTFLAGS verbatim and runs its compiler tests without
# -C panic=abort (because it doesn't know it's what cargo uses), which fail on Windows
# because -C panic=unwind (the compiler default) is not compatible with -C profile-generate
# (https://github.com/rust-lang/rust/issues/61002).
rust_pgo_flags := -C panic=abort -C profile-generate=$(topobjdir)
ifeq (1,$(words $(filter 5.% 6.% 7.% 8.% 9.% 10.% 11.%,$(CC_VERSION) $(RUSTC_LLVM_VERSION))))
# Disable value profiling when:
# (RUSTC_LLVM_VERSION < 12 and CC_VERSION >= 12) or (RUSTC_LLVM_VERSION >= 12 and CC_VERSION < 12)
rust_pgo_flags += -C llvm-args=--disable-vp=true
endif
# The C compiler may be passed extra llvm flags for PGO that we also want to pass to rust as well.
# In PROFILE_GEN_CFLAGS, they look like "-mllvm foo", and we want "-C llvm-args=foo", so first turn
# "-mllvm foo" into "-mllvm:foo" so that it becomes a unique argument, that we can then filter for,
# excluding other flags, and then turn into the right string.
rust_pgo_flags += $(patsubst -mllvm:%,-C llvm-args=%,$(filter -mllvm:%,$(subst -mllvm ,-mllvm:,$(PROFILE_GEN_CFLAGS))))
else # MOZ_PROFILE_USE
rust_pgo_flags := -C profile-use=$(PGO_PROFILE_PATH)
endif
endif

$(target_rust_ltoable): RUSTFLAGS:=$(rustflags_override) $(rustflags_sancov) $(RUSTFLAGS) $(rust_pgo_flags) \
								$(if $(MOZ_LTO_RUST_CROSS),\
								    -Clinker-plugin-lto \
									,)
$(target_rust_nonltoable): RUSTFLAGS:=$(rustflags_override) $(rustflags_sancov) $(RUSTFLAGS)

TARGET_RECIPES := $(target_rust_ltoable) $(target_rust_nonltoable)

HOST_RECIPES := \
  $(foreach a,library program,$(foreach b,build check udeps clippy,force-cargo-host-$(a)-$(b)))

$(HOST_RECIPES): RUSTFLAGS:=$(rustflags_override)

# If this is a release build we want rustc to generate one codegen unit per
# crate. This results in better optimization and less code duplication at the
# cost of longer compile times.
ifndef DEVELOPER_OPTIONS
$(TARGET_RECIPES) $(HOST_RECIPES): RUSTFLAGS += -C codegen-units=1
endif

# We use the + prefix to pass down the jobserver fds to cargo, but we
# don't use the prefix when make -n is used, so that cargo doesn't run
# in that case)
define RUN_CARGO_INNER
$(if $(findstring n,$(filter-out --%, $(MAKEFLAGS))),,+)$(CARGO) $(1) $(cargo_build_flags) $(CARGO_EXTRA_FLAGS) $(cargo_extra_cli_flags)
endef

ifdef CARGO_CONTINUE_ON_ERROR
define RUN_CARGO
-$(RUN_CARGO_INNER)
endef
else
define RUN_CARGO
$(RUN_CARGO_INNER)
endef
endif

# This function is intended to be called by:
#
#   $(call CARGO_BUILD,EXTRA_ENV_VAR1=X EXTRA_ENV_VAR2=Y ...)
#
# but, given the idiosyncracies of make, can also be called without arguments:
#
#   $(call CARGO_BUILD)
define CARGO_BUILD
$(call RUN_CARGO,rustc)
endef

cargo_host_linker_env_var := CARGO_TARGET_$(call varize,$(RUST_HOST_TARGET))_LINKER
cargo_linker_env_var := CARGO_TARGET_$(call varize,$(RUST_TARGET))_LINKER

export MOZ_CLANG_NEWER_THAN_RUSTC_LLVM
export MOZ_CARGO_WRAP_LDFLAGS
export MOZ_CARGO_WRAP_LD
export MOZ_CARGO_WRAP_LD_CXX
export MOZ_CARGO_WRAP_HOST_LDFLAGS
export MOZ_CARGO_WRAP_HOST_LD
export MOZ_CARGO_WRAP_HOST_LD_CXX
# Exporting from make always exports a value. Setting a value per-recipe
# would export an empty value for the host recipes. When not doing a
# cross-compile, the --target for those is the same, and cargo will use
# CARGO_TARGET_*_LINKER for its linker, so we always pass the
# cargo-linker wrapper, and fill MOZ_CARGO_WRAP_{HOST_,}LD* more or less
# appropriately for all recipes.
ifeq (WINNT,$(HOST_OS_ARCH))
# Use .bat wrapping on Windows hosts, and shell wrapping on other hosts.
# Like for CC/C*FLAGS, we want the target values to trump the host values when
# both variables are the same.
export $(cargo_host_linker_env_var):=$(topsrcdir)/build/cargo-host-linker.bat
export $(cargo_linker_env_var):=$(topsrcdir)/build/cargo-linker.bat
WRAP_HOST_LINKER_LIBPATHS:=$(HOST_LINKER_LIBPATHS_BAT)
else
export $(cargo_host_linker_env_var):=$(topsrcdir)/build/cargo-host-linker
export $(cargo_linker_env_var):=$(topsrcdir)/build/cargo-linker
WRAP_HOST_LINKER_LIBPATHS:=$(HOST_LINKER_LIBPATHS)
endif

# Cargo needs the same linker flags as the C/C++ compiler,
# but not the final libraries. Filter those out because they
# cause problems on macOS 10.7; see bug 1365993 for details.
# Also, we don't want to pass PGO flags until cargo supports them.
$(TARGET_RECIPES): MOZ_CARGO_WRAP_LDFLAGS:=$(filter-out -fsanitize=cfi% -framework Cocoa -lobjc AudioToolbox ExceptionHandling -fprofile-%,$(LDFLAGS))

# When building with sanitizer, rustc links its own runtime, which conflicts
# with the one that passing -fsanitize=* to the linker would add.
# Ideally, we'd always do this filtering, but because the flags may also apply
# to build scripts because cargo doesn't allow the distinction, we only filter
# when building programs, except when using thread sanitizer where we filter
# everywhere.
ifneq (,$(filter -Zsanitizer=%,$(RUSTFLAGS)))
$(if $(filter -Zsanitizer=thread,$(RUSTFLAGS)),$(TARGET_RECIPES),force-cargo-program-build): MOZ_CARGO_WRAP_LDFLAGS:=$(filter-out -fsanitize=%,$(MOZ_CARGO_WRAP_LDFLAGS))
endif

# Rustc assumes that *-windows-gnu targets build with mingw-gcc and manually
# add runtime libraries that don't exist with mingw-clang. We created dummy
# libraries in $(topobjdir)/build/win32, but that's not enough, because some
# of the wanted symbols that come from these libraries are available in a
# different library, that we add manually. We also need to avoid rustc
# passing -nodefaultlibs to clang so that it adds clang_rt.
ifeq (WINNT_clang,$(OS_ARCH)_$(CC_TYPE))
force-cargo-program-build: MOZ_CARGO_WRAP_LDFLAGS+=-L$(topobjdir)/build/win32 -lunwind
force-cargo-program-build: CARGO_RUSTCFLAGS += -C default-linker-libraries=yes
endif

# Rustc passes -nodefaultlibs to the linker (clang) on mac, which prevents
# clang from adding the necessary sanitizer runtimes when building with
# C/C++ sanitizer but without rust sanitizer.
ifeq (Darwin,$(OS_ARCH))
ifeq (,$(filter -Zsanitizer=%,$(RUSTFLAGS)))
ifneq (,$(filter -fsanitize=%,$(LDFLAGS)))
force-cargo-program-build: CARGO_RUSTCFLAGS += -C default-linker-libraries=yes
endif
endif
endif

$(HOST_RECIPES): MOZ_CARGO_WRAP_LDFLAGS:=$(HOST_LDFLAGS) $(WRAP_HOST_LINKER_LIBPATHS)
$(TARGET_RECIPES) $(HOST_RECIPES): MOZ_CARGO_WRAP_HOST_LDFLAGS:=$(HOST_LDFLAGS) $(WRAP_HOST_LINKER_LIBPATHS)

ifeq (,$(filter clang-cl,$(CC_TYPE)))
$(TARGET_RECIPES): MOZ_CARGO_WRAP_LD:=$(CC)
$(TARGET_RECIPES): MOZ_CARGO_WRAP_LD_CXX:=$(CXX)
else
$(TARGET_RECIPES): MOZ_CARGO_WRAP_LD:=$(LINKER)
$(TARGET_RECIPES): MOZ_CARGO_WRAP_LD_CXX:=$(LINKER)
endif

ifeq (,$(filter clang-cl,$(HOST_CC_TYPE)))
$(HOST_RECIPES): MOZ_CARGO_WRAP_LD:=$(HOST_CC)
$(HOST_RECIPES): MOZ_CARGO_WRAP_LD_CXX:=$(HOST_CXX)
$(TARGET_RECIPES) $(HOST_RECIPES): MOZ_CARGO_WRAP_HOST_LD:=$(HOST_CC)
$(TARGET_RECIPES) $(HOST_RECIPES): MOZ_CARGO_WRAP_HOST_LD_CXX:=$(HOST_CXX)
else
$(HOST_RECIPES): MOZ_CARGO_WRAP_LD:=$(HOST_LINKER)
$(HOST_RECIPES): MOZ_CARGO_WRAP_LD_CXX:=$(HOST_LINKER)
$(TARGET_RECIPES) $(HOST_RECIPES): MOZ_CARGO_WRAP_HOST_LD:=$(HOST_LINKER)
$(TARGET_RECIPES) $(HOST_RECIPES): MOZ_CARGO_WRAP_HOST_LD_CXX:=$(HOST_LINKER)
endif

ifdef RUST_LIBRARY_FILE

ifdef RUST_LIBRARY_FEATURES
rust_features_flag := --features '$(RUST_LIBRARY_FEATURES)'
endif

ifeq (WASI,$(OS_ARCH))
# The rust wasi target defaults to statically link the wasi crt, but when we
# build static libraries from rust and link them with C/C++ code, we also link
# a wasi crt, which may conflict with rust's.
force-cargo-library-build: CARGO_RUSTCFLAGS += -C target-feature=-crt-static
endif

# Assume any system libraries rustc links against are already in the target's LIBS.
#
# We need to run cargo unconditionally, because cargo is the only thing that
# has full visibility into how changes in Rust sources might affect the final
# build.
force-cargo-library-build:
	$(REPORT_BUILD)
	$(call CARGO_BUILD) --lib $(cargo_target_flag) $(rust_features_flag) -- $(cargo_rustc_flags)

RUST_LIBRARY_DEP_FILE := $(basename $(RUST_LIBRARY_FILE)).d
RUST_LIBRARY_DEPS := $(wordlist 2, 10000000, $(if $(wildcard $(RUST_LIBRARY_DEP_FILE)),$(shell cat $(RUST_LIBRARY_DEP_FILE))))
$(RUST_LIBRARY_FILE): $(CARGO_FILE) $(if $(RUST_LIBRARY_DEPS),$(RUST_LIBRARY_DEPS), force-cargo-library-build)
	$(if $(RUST_LIBRARY_DEPS),+$(MAKE) force-cargo-library-build,:)
# When we are building in --enable-release mode; we add an additional check to confirm
# that we are not importing any networking-related functions in rust code. This reduces
# the chance of proxy bypasses originating from rust code.
# The check only works when rust code is built with -Clto but without MOZ_LTO_RUST_CROSS.
# Sanitizers and sancov also fail because compiler-rt hooks network functions.
ifndef MOZ_PROFILE_GENERATE
ifeq ($(OS_ARCH), Linux)
ifeq (,$(rustflags_sancov)$(MOZ_ASAN)$(MOZ_TSAN)$(MOZ_UBSAN))
ifndef MOZ_LTO_RUST_CROSS
ifneq (,$(filter -Clto,$(cargo_rustc_flags)))
	$(call py_action,check_binary,--target --networking $@)
endif
endif
endif
endif
endif

define make_default_rule
$(1):

endef
$(foreach dep, $(filter %.h,$(RUST_LIBRARY_DEPS)),$(eval $(call make_default_rule,$(dep))))


SUGGEST_INSTALL_ON_FAILURE = (ret=$$?; if [ $$ret = 101 ]; then echo If $1 is not installed, install it using: cargo install $1; fi; exit $$ret)

ifndef CARGO_NO_AUTO_ARG
force-cargo-library-%:
	$(call RUN_CARGO,$*) --lib $(cargo_target_flag) $(rust_features_flag) || $(call SUGGEST_INSTALL_ON_FAILURE,cargo-$*)
else
force-cargo-library-%:
	$(call RUN_CARGO,$*) || $(call SUGGEST_INSTALL_ON_FAILURE,cargo-$*)
endif

else
force-cargo-library-%:
	@true

endif # RUST_LIBRARY_FILE

ifdef RUST_TESTS

rust_test_options := $(foreach test,$(RUST_TESTS),-p $(test))

ifdef RUST_TEST_FEATURES
rust_test_features_flag := --features '$(RUST_TEST_FEATURES)'
endif

# Don't stop at the first failure. We want to list all failures together.
rust_test_flag := --no-fail-fast

force-cargo-test-run:
	$(call RUN_CARGO,test $(cargo_target_flag) $(rust_test_flag) $(rust_test_options) $(rust_test_features_flag))

endif # RUST_TESTS

ifdef HOST_RUST_LIBRARY_FILE

ifdef HOST_RUST_LIBRARY_FEATURES
host_rust_features_flag := --features '$(HOST_RUST_LIBRARY_FEATURES)'
endif

force-cargo-host-library-build:
	$(REPORT_BUILD)
	$(call CARGO_BUILD) --lib $(cargo_host_flag) $(host_rust_features_flag)

$(HOST_RUST_LIBRARY_FILE): force-cargo-host-library-build ;

ifndef CARGO_NO_AUTO_ARG
force-cargo-host-library-%:
	$(call RUN_CARGO,$*) --lib $(cargo_host_flag) $(host_rust_features_flag)
else
force-cargo-host-library-%:
	$(call RUN_CARGO,$*) --lib $(filter-out --release $(cargo_host_flag)) $(host_rust_features_flag)
endif

else
force-cargo-host-library-%:
	@true
endif # HOST_RUST_LIBRARY_FILE

ifdef RUST_PROGRAMS

force-cargo-program-build: $(call resfile,module)
	$(REPORT_BUILD)
	$(call CARGO_BUILD) $(addprefix --bin ,$(RUST_CARGO_PROGRAMS)) $(cargo_target_flag) -- $(addprefix -C link-arg=$(CURDIR)/,$(call resfile,module)) $(CARGO_RUSTCFLAGS)

# RUST_PROGRAM_DEPENDENCIES(RUST_PROGRAM)
# Generates a rule suitable to rebuild RUST_PROGRAM only if its dependencies are
# obsolete.
# It relies on the fact that upon build, cargo generates a dependency file named
# `$(RUST_PROGRAM).d'. Unfortunately the lhs of the rule has an absolute path,
# so we extract it under the name $(RUST_PROGRAM)_deps below.
#
# If the dependencies are empty, the file was not created so we force a rebuild.
# Otherwise we add it to the dependency list.
#
# The actual rule is a bit tricky. The `+' prefix allow for recursive parallel
# make, and it's skipped (`:') if we already triggered a rebuild as part of the
# dependency chain.
#
define RUST_PROGRAM_DEPENDENCIES
$(1)_deps := $(wordlist 2, 10000000, $(if $(wildcard $(1).d),$(shell cat $(1).d)))
$(1): $(CARGO_FILE) $(call resfile,module) $(if $$($(1)_deps),$$($(1)_deps),force-cargo-program-build)
	$(if $$($(1)_deps),+$(MAKE) force-cargo-program-build,:)
$(foreach dep,$(filter %.h,$$($(1)_deps)),$(eval $(call make_default_rule,$(dep))))
endef

$(foreach RUST_PROGRAM,$(RUST_PROGRAMS), $(eval $(call RUST_PROGRAM_DEPENDENCIES,$(RUST_PROGRAM))))

ifndef CARGO_NO_AUTO_ARG
force-cargo-program-%:
	$(call RUN_CARGO,$*) $(addprefix --bin ,$(RUST_CARGO_PROGRAMS)) $(cargo_target_flag)
else
force-cargo-program-%:
	$(call RUN_CARGO,$*)
endif

else
force-cargo-program-%:
	@true
endif # RUST_PROGRAMS
ifdef HOST_RUST_PROGRAMS

force-cargo-host-program-build:
	$(REPORT_BUILD)
	$(call CARGO_BUILD) $(addprefix --bin ,$(HOST_RUST_CARGO_PROGRAMS)) $(cargo_host_flag)

$(HOST_RUST_PROGRAMS): force-cargo-host-program-build ;

ifndef CARGO_NO_AUTO_ARG
force-cargo-host-program-%:
	$(REPORT_BUILD)
	$(call RUN_CARGO,$*) $(addprefix --bin ,$(HOST_RUST_CARGO_PROGRAMS)) $(cargo_host_flag)
else
force-cargo-host-program-%:
	$(call RUN_CARGO,$*) $(addprefix --bin ,$(HOST_RUST_CARGO_PROGRAMS)) $(filter-out --release $(cargo_target_flag))
endif

else
force-cargo-host-program-%:
	@true

endif # HOST_RUST_PROGRAMS