bug 1399882 - Wrap cargo invocation in a Python script. r?froydnj
Currently rules.mk contains a lot of logic to assemble the commandline and
environment for running cargo. This is messy because GNU make does not
give you very good tools to work with (and shell isn't much better).
This patch adds a `cargo` script to mozbuild.action that wraps the cargo
invocation and moves most of the logic into the script.
MozReview-Commit-ID: yuSwB9gEss
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -837,229 +837,90 @@ endif
ifdef MOZ_AUTOMATION
ifeq (,$(filter 1,$(MOZ_AUTOMATION_BUILD_SYMBOLS)))
DUMP_SYMS_TARGETS :=
endif
endif
$(foreach file,$(DUMP_SYMS_TARGETS),$(eval $(call syms_template,$(file),$(file)_syms.track)))
-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
-cargo_build_flags += --frozen
-
-cargo_build_flags += --manifest-path $(CARGO_FILE)
-ifdef BUILD_VERBOSE_LOG
-cargo_build_flags += --verbose
-else
-ifdef MOZ_AUTOMATION
-cargo_build_flags += --verbose
-endif # MOZ_AUTOMATION
-endif # BUILD_VERBOSE_LOG
-
-# 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)))
-cargo_build_flags += --color=always
-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.
-cargo_rustc_flags += -C lto
-endif
-endif
-
-# Cargo currently supports only two interesting profiles for building:
-# development and release. Those map (roughly) to --enable-debug and
-# --disable-debug in Gecko, respectively, but there's another axis that we'd
-# like to support: --{disable,enable}-optimize. Since that would be four
-# choices, and Cargo only supports two, we choose to enable various
-# optimization levels in our Cargo.toml files all the time, and override the
-# optimization level here, if necessary. (The Cargo.toml files already
-# specify debug-assertions appropriately for --{disable,enable}-debug.)
-default_rustflags =
-ifndef MOZ_OPTIMIZE
-default_rustflags = -C opt-level=0
-# Unfortunately, -C opt-level=0 implies -C debug-assertions, so we need
-# to explicitly disable them when MOZ_DEBUG_RUST is not set.
-ifndef MOZ_DEBUG_RUST
-default_rustflags += -C debug-assertions=no
-endif
-endif
-rustflags_override = RUSTFLAGS='$(default_rustflags) $(RUSTFLAGS)'
-
-ifdef MOZ_MSVCBITS
-# If we are building a MozillaBuild shell, we want to clear out the
-# vcvars.bat environment variables for cargo builds. This is because
-# a 32-bit MozillaBuild shell on a 64-bit machine will try to use
-# the 32-bit compiler/linker for everything, while cargo/rustc wants
-# to use the 64-bit linker for build.rs scripts. This conflict results
-# in a build failure (see bug 1350001). So we clear out the environment
-# variables that are actually relevant to 32- vs 64-bit builds.
-environment_cleaner = -u VCINSTALLDIR PATH='' LIB='' LIBPATH=''
-# The servo build needs to know where python is, and we're removing the PATH
-# so we tell it explicitly via the PYTHON env var.
-environment_cleaner += PYTHON='$(shell which $(PYTHON))'
-else
-environment_cleaner =
-endif
-
-rust_unlock_unstable =
-ifdef MOZ_RUST_SIMD
-rust_unlock_unstable += RUSTC_BOOTSTRAP=1
-endif
-
-ifdef MOZ_USING_SCCACHE
-sccache_wrap := RUSTC_WRAPPER='$(CCACHE)'
-endif
-
-# XXX hack to work around dsymutil failing on cross-OSX builds (bug 1380381)
-ifeq ($(HOST_OS_ARCH)-$(OS_ARCH),Linux-Darwin)
-default_rustflags += -C debuginfo=1
-else
-default_rustflags += -C debuginfo=2
-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
-$(if $(findstring n,$(filter-out --%, $(MAKEFLAGS))),,+)env $(environment_cleaner) $(rust_unlock_unstable) $(rustflags_override) $(sccache_wrap) \
- CARGO_TARGET_DIR=$(CARGO_TARGET_DIR) \
- RUSTC=$(RUSTC) \
- MOZ_SRC=$(topsrcdir) \
- MOZ_DIST=$(ABS_DIST) \
- LIBCLANG_PATH="$(MOZ_LIBCLANG_PATH)" \
- CLANG_PATH="$(MOZ_CLANG_PATH)" \
- PKG_CONFIG_ALLOW_CROSS=1 \
- RUST_BACKTRACE=full \
- MOZ_TOPOBJDIR=$(topobjdir) \
- $(2) \
- $(CARGO) $(1) $(cargo_build_flags)
-endef
-
-# 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,$(1))
+$(if $(findstring n,$(filter-out --%, $(MAKEFLAGS))),,+)$(call py_action,cargo,$(1) $(rust_features_flag) $(CARGO_FILE) $(CARGO_TARGET_DIR))
endef
-define CARGO_CHECK
-$(call RUN_CARGO,check,$(1))
-endef
-
-cargo_linker_env_var := CARGO_TARGET_$(RUST_TARGET_ENV_NAME)_LINKER
-
-# Don't define a custom linker on Windows, as it's difficult to have a
-# non-binary file that will get executed correctly by Cargo. We don't
-# have to worry about a cross-compiling (besides x86-64 -> x86, which
-# already works with the current setup) setup on Windows, and we don't
-# have to pass in any special linker options on Windows.
-ifneq (WINNT,$(OS_ARCH))
-
-# Defining all of this for ASan/TSan builds results in crashes while running
-# some crates's build scripts (!), so disable it for now.
-ifndef MOZ_ASAN
-ifndef MOZ_TSAN
-# 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.
-target_cargo_env_vars := \
- MOZ_CARGO_WRAP_LDFLAGS="$(filter-out -framework Cocoa -lobjc AudioToolbox ExceptionHandling,$(LDFLAGS))" \
- MOZ_CARGO_WRAP_LD="$(CC)" \
- $(cargo_linker_env_var)=$(topsrcdir)/build/cargo-linker
-endif # MOZ_TSAN
-endif # MOZ_ASAN
-
-endif # ifneq WINNT
-
ifdef RUST_LIBRARY_FILE
ifdef RUST_LIBRARY_FEATURES
rust_features_flag := --features "$(RUST_LIBRARY_FEATURES)"
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,$(target_cargo_env_vars)) --lib $(cargo_target_flag) $(rust_features_flag) -- $(cargo_rustc_flags)
+ $(call RUN_CARGO,rustc --lib)
$(RUST_LIBRARY_FILE): force-cargo-library-build
force-cargo-library-check:
- $(call CARGO_CHECK,$(target_cargo_env_vars)) --lib $(cargo_target_flag) $(rust_features_flag)
+ $(call RUN_CARGO,check --lib)
else
force-cargo-library-check:
@true
endif # RUST_LIBRARY_FILE
ifdef HOST_RUST_LIBRARY_FILE
ifdef HOST_RUST_LIBRARY_FEATURES
-host_rust_features_flag := --features "$(HOST_RUST_LIBRARY_FEATURES)"
+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)
+ $(call RUN_CARGO,rustc --host-lib)
$(HOST_RUST_LIBRARY_FILE): force-cargo-host-library-build
force-cargo-host-library-check:
- $(call CARGO_CHECK) --lib $(cargo_host_flag) $(host_rust_features_flag)
+ $(call RUN_CARGO,check --host-lib)
else
force-cargo-host-library-check:
@true
endif # HOST_RUST_LIBRARY_FILE
ifdef RUST_PROGRAMS
force-cargo-program-build:
$(REPORT_BUILD)
- $(call CARGO_BUILD,$(target_cargo_env_vars)) $(addprefix --bin ,$(RUST_CARGO_PROGRAMS)) $(cargo_target_flag)
+ $(call RUN_CARGO,rustc $(addprefix --bin ,$(RUST_CARGO_PROGRAMS)))
$(RUST_PROGRAMS): force-cargo-program-build
force-cargo-program-check:
- $(call CARGO_CHECK,$(target_cargo_env_vars)) $(addprefix --bin ,$(RUST_CARGO_PROGRAMS)) $(cargo_target_flag)
+ $(REPORT_BUILD)
+ $(call RUN_CARGO,check $(addprefix --bin ,$(RUST_CARGO_PROGRAMS)))
else
force-cargo-program-check:
@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)
+ $(call RUN_CARGO,rustc $(addprefix --host-bin ,$(HOST_RUST_CARGO_PROGRAMS)))
$(HOST_RUST_PROGRAMS): force-cargo-host-program-build
force-cargo-host-program-check:
$(REPORT_BUILD)
- $(call CARGO_CHECK) $(addprefix --bin ,$(HOST_RUST_CARGO_PROGRAMS)) $(cargo_host_flag)
+ $(call RUN_CARGO,check $(addprefix --host-bin ,$(HOST_RUST_CARGO_PROGRAMS)))
else
force-cargo-host-program-check:
@true
endif # HOST_RUST_PROGRAMS
$(SOBJS):
$(REPORT_BUILD)
"$(AS)" -o $@ $(DEFINES) $(ASFLAGS) $($(notdir $<)_FLAGS) $(LOCAL_INCLUDES) -c $<
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/action/cargo.py
@@ -0,0 +1,160 @@
+# 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/.
+
+# This script wraps cargo execution for building Rust code.
+
+from __future__ import absolute_import, print_function
+
+import argparse
+import buildconfig
+import itertools
+from mozbuild.shellutil import quote
+import mozpack.path as mozpath
+import os
+import platform
+import shlex
+import subprocess
+import sys
+
+def main(argv):
+ parser = argparse.ArgumentParser('Run cargo to build Rust code',
+ add_help=False)
+ parser.add_argument('--lib', action='store_true',
+ help='Build a library')
+ parser.add_argument('--host-lib', action='store_true',
+ help='Build a host library')
+ parser.add_argument('--bin', nargs='*',
+ help='Build a binary')
+ parser.add_argument('--host-bin', nargs='*',
+ help='Build a host binary')
+ parser.add_argument('--features',
+ help='Cargo features to build')
+ parser.add_argument('command', help='cargo subcommand to invoke')
+ parser.add_argument('manifest_path',
+ help='Cargo manifest to build')
+ parser.add_argument('target_dir',
+ help='Directory in which to build')
+ args = parser.parse_args(argv)
+
+ s = buildconfig.substs
+ building_host = args.host_lib or args.host_bin
+ target = '--target=%s' % (s['RUST_HOST_TARGET'] if building_host else s['RUST_TARGET'])
+ cmd = [s['CARGO'], args.command, target]
+ # Figure out what we're building.
+ if args.lib or args.host_lib:
+ cmd.append('--lib')
+ elif args.bin:
+ cmd.extend(itertools.chain.from_iterable(('--bin', b) for b in args.bin))
+ elif args.host_bin:
+ cmd.extend(itertools.chain.from_iterable(('--bin', b) for b in args.host_bin))
+ if args.features:
+ cmd.extend(('--features', args.features))
+ cmd.extend(os.environ.get('CARGOFLAGS', []))
+ if not s.get('MOZ_DEBUG_RUST'):
+ cmd.append('--release')
+ cmd.extend((
+ '--frozen',
+ '--manifest-path', args.manifest_path,
+ ))
+ if os.environ.get('BUILD_VERBOSE_LOG') or s.get('MOZ_AUTOMATION'):
+ cmd.append('--verbose')
+ # 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`.
+ if os.environ.get('MACH_STDOUT_ISATTY') and not any(a.startswith('--color') for a in shlex.split(os.environ.get('CARGOFLAGS', ''))):
+ cmd.append('--color=always')
+
+ if args.command == 'rustc' and args.lib:
+ # 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).
+ cmd.append('--')
+ cmd.extend(os.environ.get('CARGO_RUSTCFLAGS', []))
+ # Enable link-time optimization for release builds.
+ if not (s.get('DEVELOPER_OPTIONS') or s.get('MOZ_DEBUG_RUST')):
+ cmd.extend(('-C', 'lto'))
+
+ # Cargo currently supports only two interesting profiles for building:
+ # development and release. Those map (roughly) to --enable-debug and
+ # --disable-debug in Gecko, respectively, but there's another axis that we'd
+ # like to support: --{disable,enable}-optimize. Since that would be four
+ # choices, and Cargo only supports two, we choose to enable various
+ # optimization levels in our Cargo.toml files all the time, and override the
+ # optimization level here, if necessary. (The Cargo.toml files already
+ # specify debug-assertions appropriately for --{disable,enable}-debug.)
+ rustflags = []
+ if not s.get('MOZ_OPTIMIZE'):
+ rustflags.extend(('-C', 'opt-level=0'))
+ # Unfortunately, -C opt-level=0 implies -C debug-assertions, so we need
+ # to explicitly disable them when MOZ_DEBUG_RUST is not set.
+ if not s.get('MOZ_DEBUG_RUST'):
+ rustflags.extend(('-C', 'debug-assertions=no'))
+ # XXX hack to work around dsymutil failing on cross-OSX builds (bug 1380381)
+ if s['OS_ARCH'] == 'Darwin' and platform.system() == 'Linux':
+ rustflags.extend(('-C', 'debuginfo=1'))
+ else:
+ rustflags.extend(('-C', 'debuginfo=2'))
+ rustflags.extend(s['RUSTFLAGS'])
+
+ env = dict(os.environ)
+ if env.get('MOZ_MSVCBITS'):
+ # If we are building a MozillaBuild shell, we want to clear out the
+ # vcvars.bat environment variables for cargo builds. This is because
+ # a 32-bit MozillaBuild shell on a 64-bit machine will try to use
+ # the 32-bit compiler/linker for everything, while cargo/rustc wants
+ # to use the 64-bit linker for build.rs scripts. This conflict results
+ # in a build failure (see bug 1350001). So we clear out the environment
+ # variables that are actually relevant to 32- vs 64-bit builds.
+ if 'VCINSTALLDIR' in env:
+ del env['VCINSTALLDIR']
+ for v in ('PATH', 'LIB', 'LIBPATH'):
+ env[v] = ''
+ # The servo build needs to know where python is, and we're removing the PATH
+ # so we tell it explicitly via the PYTHON env var.
+ env['PYTHON'] = sys.executable
+
+ if s.get('MOZ_RUST_SIMD'):
+ env['RUSTC_BOOTSTRAP'] = '1'
+ if s.get('MOZ_USING_SCCACHE'):
+ env['RUSTC_WRAPPER'] = s['CCACHE']
+
+ # Don't define a custom linker on Windows, as it's difficult to have a
+ # non-binary file that will get executed correctly by Cargo. We don't
+ # have to worry about a cross-compiling (besides x86-64 -> x86, which
+ # already works with the current setup) setup on Windows, and we don't
+ # have to pass in any special linker options on Windows.
+ #
+ # Defining all of this for ASan/TSan builds results in crashes while running
+ # some crates's build scripts (!), so disable it for now.
+ if not building_host and s['OS_ARCH'] != 'WINNT' and not (s['MOZ_ASAN'] or s['MOZ_TSAN']):
+ # 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.
+ env.update(
+ MOZ_CARGO_WRAP_LDFLAGS = env.get('LDFLAGS', '').replace('-framework Cocoa -lobjc AudioToolbox ExceptionHandling', ''),
+ MOZ_CARGO_WRAP_LD = s['CC']
+ )
+ linker_env_name = 'CARGO_TARGET_%s_LINKER' % s['RUST_TARGET_ENV_NAME']
+ env[linker_env_name] = mozpath.join(buildconfig.topsrcdir, 'build/cargo-linker')
+ env.update(
+ CARGO_TARGET_DIR=args.target_dir,
+ RUSTFLAGS=' '.join(rustflags),
+ RUSTC=s['RUSTC'],
+ MOZ_SRC=buildconfig.topsrcdir,
+ MOZ_DIST=mozpath.join(buildconfig.topobjdir, 'dist'),
+ LIBCLANG_PATH=s['MOZ_LIBCLANG_PATH'],
+ CLANG_PATH=s['MOZ_CLANG_PATH'],
+ PKG_CONFIG_ALLOW_CROSS='1',
+ RUST_BACKTRACE='full',
+ MOZ_TOPOBJDIR=buildconfig.topobjdir,
+ )
+ for k in env.keys():
+ if isinstance(env[k], unicode):
+ env[k] = env[k].encode(sys.getfilesystemencoding() or 'mbcs')
+ keys = set(env.keys()) - set(os.environ.keys())
+ print('%s %s' % (' '.join('%s=%s' % (k, quote(env[k])) for k in keys), ' '.join(quote(c) for c in cmd)))
+ return subprocess.call(cmd, env=env)
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))