Bug 1024620 - Use object files next to archives when extracting them at link time, or in a subdirectory. r=ted, a=lmandel
authorMike Hommey <mh+mozilla@glandium.org>
Sat, 14 Jun 2014 09:08:22 +0900
changeset 207710 692e8a051e75ed78bb43dabd09dcd964c3251b9c
parent 207709 5784bfcffa5ef8f741e55bf5597fae7fe15264cd
child 207711 46c8bd676a5057c23cdd0b2275efff3b7c86a079
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersted, lmandel
bugs1024620
milestone32.0a2
Bug 1024620 - Use object files next to archives when extracting them at link time, or in a subdirectory. r=ted, a=lmandel
config/expandlibs_exec.py
config/tests/unit-expandlibs.py
--- a/config/expandlibs_exec.py
+++ b/config/expandlibs_exec.py
@@ -69,16 +69,22 @@ class ExpandArgsMore(ExpandArgs):
 
     def _extract(self, args):
         '''When a static library name is found, either extract its contents
         in a temporary directory or use the information found in the
         corresponding lib descriptor.
         '''
         ar_extract = conf.AR_EXTRACT.split()
         newlist = []
+
+        def lookup(base, f):
+            for root, dirs, files in os.walk(base):
+                if f in files:
+                    return os.path.join(root, f)
+
         for arg in args:
             if os.path.splitext(arg)[1] == conf.LIB_SUFFIX:
                 if os.path.exists(arg + conf.LIBS_DESC_SUFFIX):
                     newlist += self._extract(self._expand_desc(arg))
                     continue
                 elif os.path.exists(arg) and (len(ar_extract) or conf.AR == 'lib'):
                     tmp = tempfile.mkdtemp(dir=os.curdir)
                     self.tmp.append(tmp)
@@ -90,18 +96,29 @@ class ExpandArgsMore(ExpandArgs):
                         if all(isDynamicLib(f) for f in files):
                             newlist += [arg]
                             continue
                         for f in files:
                             subprocess.call([conf.AR, '-NOLOGO', '-EXTRACT:%s' % f, os.path.abspath(arg)], cwd=tmp)
                     else:
                         subprocess.call(ar_extract + [os.path.abspath(arg)], cwd=tmp)
                     objs = []
+                    basedir = os.path.dirname(arg)
                     for root, dirs, files in os.walk(tmp):
-                        objs += [relativize(os.path.join(root, f)) for f in files if isObject(f)]
+                        for f in files:
+                            if isObject(f):
+                                # If the file extracted from the library also
+                                # exists in the directory containing the
+                                # library, or one of its subdirectories, use
+                                # that instead.
+                                maybe_obj = lookup(os.path.join(basedir, os.path.relpath(root, tmp)), f)
+                                if maybe_obj:
+                                    objs.append(relativize(maybe_obj))
+                                else:
+                                    objs.append(relativize(os.path.join(root, f)))
                     newlist += sorted(objs)
                     continue
             newlist += [arg]
         return newlist
 
     def makelist(self):
         '''Replaces object file names with a temporary list file, using a
         list format depending on the EXPAND_LIBS_LIST_STYLE variable
--- a/config/tests/unit-expandlibs.py
+++ b/config/tests/unit-expandlibs.py
@@ -245,17 +245,17 @@ class TestExpandArgsMore(TestExpandInit)
             tmp = args.tmp
         # Check that all temporary files are properly removed
         self.assertEqual(True, all([not os.path.exists(f) for f in tmp]))
 
     def test_extract(self):
         '''Test library extraction'''
         # Divert subprocess.call
         subprocess_call = subprocess.call
-        extracted = {}
+        subprocess_check_output = subprocess.check_output
         def call(args, **kargs):
             if config.AR == 'lib':
                 self.assertEqual(args[:2], [config.AR, '-NOLOGO'])
                 self.assertTrue(args[2].startswith('-EXTRACT:'))
                 extract = [args[2][len('-EXTRACT:'):]]
                 self.assertTrue(extract)
                 args = args[3:]
             else:
@@ -289,41 +289,69 @@ class TestExpandArgsMore(TestExpandInit)
 [config.LIB_SUFFIX])
             # Simulate LIB -NOLOGO -LIST
             lib = os.path.splitext(os.path.basename(args[3]))[0]
             return '%s\n%s\n' % (Obj(lib), Obj(lib + '2'))
         subprocess.check_output = check_output
 
         # ExpandArgsMore does the same as ExpandArgs
         self.touch([self.tmpfile('liby', Lib('y'))])
-        with ExpandArgsMore(['foo', '-bar'] + self.arg_files + [self.tmpfile('liby', Lib('y'))]) as args:
-            self.assertRelEqual(args, ['foo', '-bar'] + self.files + [self.tmpfile('liby', Lib('y'))])
+        for iteration in (1, 2):
+            with ExpandArgsMore(['foo', '-bar'] + self.arg_files + [self.tmpfile('liby', Lib('y'))]) as args:
+                self.assertRelEqual(args, ['foo', '-bar'] + self.files + [self.tmpfile('liby', Lib('y'))])
 
-            # ExpandArgsMore also has an extra method extracting static libraries
-            # when possible
-            args.extract()
+                extracted = {}
+                # ExpandArgsMore also has an extra method extracting static libraries
+                # when possible
+                args.extract()
 
-            files = self.files + self.liby_files + self.libx_files
-            # With AR_EXTRACT, it uses the descriptors when there are, and
-            # actually
-            # extracts the remaining libraries
-            extracted_args = []
-            for f in files:
-                if f.endswith(config.LIB_SUFFIX):
-                    extracted_args.extend(sorted(extracted[os.path.splitext(os.path.basename(f))[0]]))
-                else:
-                    extracted_args.append(f)
-            self.assertRelEqual(args, ['foo', '-bar'] + extracted_args)
+                files = self.files + self.liby_files + self.libx_files
+                # With AR_EXTRACT, it uses the descriptors when there are, and
+                # actually
+                # extracts the remaining libraries
+                extracted_args = []
+                for f in files:
+                    if f.endswith(config.LIB_SUFFIX):
+                        base = os.path.splitext(os.path.basename(f))[0]
+                        # On the first iteration, we test the behavior of
+                        # extracting archives that don't have a copy of their
+                        # contents next to them, which is to use the file
+                        # extracted from the archive in a temporary directory.
+                        # On the second iteration, we test extracting archives
+                        # that do have a copy of their contents next to them,
+                        # in which case those contents are used instead of the
+                        # temporarily extracted files.
+                        if iteration == 1:
+                            extracted_args.extend(sorted(extracted[base]))
+                        else:
+                            dirname = os.path.dirname(f[len(self.tmpdir)+1:])
+                            if base.endswith('f'):
+                                dirname = os.path.join(dirname, 'foo', 'bar')
+                            extracted_args.extend([self.tmpfile(dirname, Obj(base)), self.tmpfile(dirname, Obj(base + '2'))])
+                    else:
+                        extracted_args.append(f)
+                self.assertRelEqual(args, ['foo', '-bar'] + extracted_args)
 
-            tmp = args.tmp
-        # Check that all temporary files are properly removed
-        self.assertEqual(True, all([not os.path.exists(f) for f in tmp]))
+                tmp = args.tmp
+            # Check that all temporary files are properly removed
+            self.assertEqual(True, all([not os.path.exists(f) for f in tmp]))
 
-        # Restore subprocess.call
+            # Create archives contents next to them for the second iteration.
+            base = os.path.splitext(Lib('_'))[0]
+            self.touch(self.tmpfile(Obj(base.replace('_', suffix))) for suffix in ('a', 'a2', 'd', 'd2'))
+            try:
+                os.makedirs(self.tmpfile('foo', 'bar'))
+            except:
+                pass
+            self.touch(self.tmpfile('foo', 'bar', Obj(base.replace('_', suffix))) for suffix in ('f', 'f2'))
+            self.touch(self.tmpfile('liby', Obj(base.replace('_', suffix))) for suffix in ('z', 'z2'))
+
+        # Restore subprocess.call and subprocess.check_output
         subprocess.call = subprocess_call
+        subprocess.check_output = subprocess_check_output
 
 class FakeProcess(object):
     def __init__(self, out, err = ''):
         self.out = out
         self.err = err
 
     def communicate(self):
         return (self.out, self.err)