Bug 1302704 - part 3 - propagate information about CARGO_TARGET_DIR from the frontend into the backend; r=chmanchester
authorNathan Froyd <froydnj@mozilla.com>
Thu, 23 Feb 2017 10:35:07 -0500
changeset 373623 3c3ffbed3a4b22e613f0912c2ecb0378e7f8a35e
parent 373622 17b7f6a479363762110c33625771be609be00990
child 373624 95b0a622699271828ef3bcb644837be4a5bf0d7d
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerschmanchester
bugs1302704
milestone54.0a1
Bug 1302704 - part 3 - propagate information about CARGO_TARGET_DIR from the frontend into the backend; r=chmanchester Rust libraries can set RUST_LIBRARY_TARGET_DIR so that they can share compilation artifacts with other libraries. This setting needs to be propagated to the backend so it can be communicated to Cargo.
build/templates.mozbuild
python/mozbuild/mozbuild/backend/recursivemake.py
python/mozbuild/mozbuild/frontend/context.py
python/mozbuild/mozbuild/frontend/data.py
python/mozbuild/mozbuild/frontend/emitter.py
python/mozbuild/mozbuild/test/backend/test_recursivemake.py
--- a/build/templates.mozbuild
+++ b/build/templates.mozbuild
@@ -56,25 +56,28 @@ def CppUnitTests(names, ext='.cpp'):
 
 @template
 def Library(name):
     '''Template for libraries.'''
     LIBRARY_NAME = name
 
 
 @template
-def RustLibrary(name, features=None):
+def RustLibrary(name, features=None, target_dir=None):
     '''Template for Rust libraries.'''
     Library(name)
 
     IS_RUST_LIBRARY = True
 
     if features:
         RUST_LIBRARY_FEATURES = features
 
+    if target_dir:
+        RUST_LIBRARY_TARGET_DIR = target_dir
+
 
 @template
 def SharedLibrary(name):
     '''Template for shared libraries.'''
     Library(name)
 
     FORCE_SHARED_LIB = True
 
--- a/python/mozbuild/mozbuild/backend/recursivemake.py
+++ b/python/mozbuild/mozbuild/backend/recursivemake.py
@@ -1246,17 +1246,23 @@ class RecursiveMakeBackend(CommonBackend
     def _process_rust_library(self, libdef, backend_file):
         lib_var = 'RUST_LIBRARY_FILE'
         feature_var = 'RUST_LIBRARY_FEATURES'
         if isinstance(libdef, HostRustLibrary):
             lib_var = 'HOST_RUST_LIBRARY_FILE'
             feature_var = 'HOST_RUST_LIBRARY_FEATURES'
         backend_file.write_once('%s := %s\n' % (libdef.LIB_FILE_VAR, libdef.import_name))
         backend_file.write('CARGO_FILE := $(srcdir)/Cargo.toml\n')
-        backend_file.write('CARGO_TARGET_DIR := .\n')
+        # Need to normalize the path so Cargo sees the same paths from all
+        # possible invocations of Cargo with this CARGO_TARGET_DIR.  Otherwise,
+        # Cargo's dependency calculations don't work as we expect and we wind
+        # up recompiling lots of things.
+        target_dir = mozpath.join(backend_file.objdir, libdef.target_dir)
+        target_dir = mozpath.normpath(target_dir)
+        backend_file.write('CARGO_TARGET_DIR := %s\n' % target_dir)
         if libdef.features:
             backend_file.write('%s := %s\n' % (libdef.FEATURES_VAR, ' '.join(libdef.features)))
 
     def _process_host_library(self, libdef, backend_file):
         backend_file.write('HOST_LIBRARY_NAME = %s\n' % libdef.basename)
 
     def _build_target_for_obj(self, obj):
         return '%s/%s' % (mozpath.relpath(obj.objdir,
--- a/python/mozbuild/mozbuild/frontend/context.py
+++ b/python/mozbuild/mozbuild/frontend/context.py
@@ -954,16 +954,25 @@ VARIABLES = {
 
     'RUST_LIBRARY_FEATURES': (List, list,
         """Cargo features to activate for this library.
 
         This variable should not be used directly; you should be using the
         RustLibrary template instead.
         """),
 
+    'RUST_LIBRARY_TARGET_DIR': (unicode, unicode,
+        """Where CARGO_TARGET_DIR should point when compiling this library.  If
+        not set, it defaults to the current objdir.  It should be a relative path
+        to the current objdir; absolute paths should not be used.
+
+        This variable should not be used directly; you should be using the
+        RustLibrary template instead.
+        """),
+
     'HOST_RUST_LIBRARY_FEATURES': (List, list,
         """Cargo features to activate for this host library.
 
         This variable should not be used directly; you should be using the
         HostRustLibrary template instead.
         """),
 
     'UNIFIED_SOURCES': (ContextDerivedTypedList(SourcePath, StrictOrderingOnAppendList), list,
--- a/python/mozbuild/mozbuild/frontend/data.py
+++ b/python/mozbuild/mozbuild/frontend/data.py
@@ -518,39 +518,42 @@ class StaticLibrary(Library):
 class RustLibrary(StaticLibrary):
     """Context derived container object for a static library"""
     __slots__ = (
         'cargo_file',
         'crate_type',
         'dependencies',
         'deps_path',
         'features',
+        'target_dir',
     )
     TARGET_SUBST_VAR = 'RUST_TARGET'
     FEATURES_VAR = 'RUST_LIBRARY_FEATURES'
     LIB_FILE_VAR = 'RUST_LIBRARY_FILE'
 
     def __init__(self, context, basename, cargo_file, crate_type, dependencies,
-                 features, **args):
+                 features, target_dir, **args):
         StaticLibrary.__init__(self, context, basename, **args)
         self.cargo_file = cargo_file
         self.crate_type = crate_type
         # We need to adjust our naming here because cargo replaces '-' in
         # package names defined in Cargo.toml with underscores in actual
         # filenames. But we need to keep the basename consistent because
         # many other things in the build system depend on that.
         assert self.crate_type == 'staticlib'
         self.lib_name = '%s%s%s' % (context.config.lib_prefix,
                                      basename.replace('-', '_'),
                                      context.config.lib_suffix)
         self.dependencies = dependencies
-        build_dir = cargo_output_directory(context, self.TARGET_SUBST_VAR)
+        build_dir = mozpath.join(target_dir,
+                                 cargo_output_directory(context, self.TARGET_SUBST_VAR))
         self.import_name = mozpath.join(build_dir, self.lib_name)
         self.deps_path = mozpath.join(build_dir, 'deps')
         self.features = features
+        self.target_dir = target_dir
 
 
 class SharedLibrary(Library):
     """Context derived container object for a shared library"""
     __slots__ = (
         'soname',
         'variant',
         'symbols_file',
--- a/python/mozbuild/mozbuild/frontend/emitter.py
+++ b/python/mozbuild/mozbuild/frontend/emitter.py
@@ -460,27 +460,29 @@ class TreeMetadataEmitter(LoggingMixin):
 
             panic = profile.get('panic', None)
             if panic != 'abort':
                 raise SandboxValidationError(
                     ('Cargo.toml for %s does not specify `panic = "abort"`'
                      ' in [profile.%s] section') % (libname, profile_name),
                     context)
 
+        cargo_target_dir = context.get('RUST_LIBRARY_TARGET_DIR', '.')
+
         dependencies = set(config.get('dependencies', {}).iterkeys())
 
         features = context.get(cls.FEATURES_VAR, [])
         unique_features = set(features)
         if len(features) != len(unique_features):
             raise SandboxValidationError(
                 'features for %s should not contain duplicates: %s' % (libname, features),
                 context)
 
         return cls(context, libname, cargo_file, crate_type, dependencies,
-                   features, **static_args)
+                   features, cargo_target_dir, **static_args)
 
 
     def _handle_linkables(self, context, passthru, generated_files):
         linkables = []
         host_linkables = []
         def add_program(prog, var):
             if var.startswith('HOST_'):
                 host_linkables.append(prog)
--- a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
+++ b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
@@ -759,63 +759,63 @@ class TestRecursiveMakeBackend(BackendTe
         env = self._consume('rust-library', RecursiveMakeBackend)
 
         backend_path = mozpath.join(env.topobjdir, 'backend.mk')
         lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]]
 
         expected = [
             'RUST_LIBRARY_FILE := x86_64-unknown-linux-gnu/release/libgkrust.a',
             'CARGO_FILE := $(srcdir)/Cargo.toml',
-            'CARGO_TARGET_DIR := .',
+            'CARGO_TARGET_DIR := %s' % env.topobjdir,
         ]
 
         self.assertEqual(lines, expected)
 
     def test_host_rust_library(self):
         """Test that a Rust library is written to backend.mk correctly."""
         env = self._consume('host-rust-library', RecursiveMakeBackend)
 
         backend_path = mozpath.join(env.topobjdir, 'backend.mk')
         lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]]
 
         expected = [
             'HOST_RUST_LIBRARY_FILE := x86_64-unknown-linux-gnu/release/libhostrusttool.a',
             'CARGO_FILE := $(srcdir)/Cargo.toml',
-            'CARGO_TARGET_DIR := .',
+            'CARGO_TARGET_DIR := %s' % env.topobjdir,
         ]
 
         self.assertEqual(lines, expected)
 
     def test_host_rust_library_with_features(self):
         """Test that a host Rust library with features is written to backend.mk correctly."""
         env = self._consume('host-rust-library-features', RecursiveMakeBackend)
 
         backend_path = mozpath.join(env.topobjdir, 'backend.mk')
         lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]]
 
         expected = [
             'HOST_RUST_LIBRARY_FILE := x86_64-unknown-linux-gnu/release/libhostrusttool.a',
             'CARGO_FILE := $(srcdir)/Cargo.toml',
-            'CARGO_TARGET_DIR := .',
+            'CARGO_TARGET_DIR := %s' % env.topobjdir,
             'HOST_RUST_LIBRARY_FEATURES := musthave cantlivewithout',
         ]
 
         self.assertEqual(lines, expected)
 
     def test_rust_library_with_features(self):
         """Test that a Rust library with features is written to backend.mk correctly."""
         env = self._consume('rust-library-features', RecursiveMakeBackend)
 
         backend_path = mozpath.join(env.topobjdir, 'backend.mk')
         lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]]
 
         expected = [
             'RUST_LIBRARY_FILE := x86_64-unknown-linux-gnu/release/libgkrust.a',
             'CARGO_FILE := $(srcdir)/Cargo.toml',
-            'CARGO_TARGET_DIR := .',
+            'CARGO_TARGET_DIR := %s' % env.topobjdir,
             'RUST_LIBRARY_FEATURES := musthave cantlivewithout',
         ]
 
         self.assertEqual(lines, expected)
 
     def test_rust_programs(self):
         """Test that {HOST_,}RUST_PROGRAMS are written to backend.mk correctly."""
         env = self._consume('rust-programs', RecursiveMakeBackend)