Bug 644081 - Use relative paths as much as possible in expandlibs.py. r=ted
authorMike Hommey <mh+mozilla@glandium.org>
Fri, 25 Mar 2011 19:50:29 +0100
changeset 63919 208e88837e650ef6ca535f9eae60474488a80b2d
parent 63918 5100b8349ba974ba3de091144be81304fcaf9e85
child 63920 4599ba769a4894e246a92f60ca6ace80b93415ed
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersted
bugs644081
milestone2.2a1pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 644081 - Use relative paths as much as possible in expandlibs.py. r=ted
config/expandlibs.py
config/expandlibs_exec.py
config/tests/unit-expandlibs.py
js/src/config/expandlibs.py
js/src/config/expandlibs_exec.py
--- a/config/expandlibs.py
+++ b/config/expandlibs.py
@@ -58,16 +58,39 @@ given a list of files, expandlibs will r
   replace ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} with the OBJS and LIBS the
   descriptor contains. And for each of these LIBS, also apply the same
   rules.
 '''
 from __future__ import with_statement
 import sys, os
 import expandlibs_config as conf
 
+def relativize(path):
+    '''Returns a path relative to the current working directory, if it is
+    shorter than the given path'''
+    def splitpath(path):
+        dir, file = os.path.split(path)
+        if os.path.splitdrive(dir)[1] == os.sep:
+            return [file]
+        return splitpath(dir) + [file]
+
+    if not os.path.exists(path):
+        return path
+    curdir = splitpath(os.path.abspath(os.curdir))
+    abspath = splitpath(os.path.abspath(path))
+    while curdir and abspath and curdir[0] == abspath[0]:
+        del curdir[0]
+        del abspath[0]
+    if not curdir and not abspath:
+        return '.'
+    relpath = os.path.join(*[os.pardir for i in curdir] + abspath)
+    if len(path) > len(relpath):
+        return relpath
+    return path
+
 class LibDescriptor(dict):
     KEYS = ['OBJS', 'LIBS']
 
     def __init__(self, content=None):
         '''Creates an instance of a lib descriptor, initialized with contents
         from a list of strings when given. This is intended for use with
         file.readlines()'''
         if isinstance(content, list) and all([isinstance(item, str) for item in content]):
@@ -94,32 +117,32 @@ class ExpandArgs(list):
         super(ExpandArgs, self).__init__()
         for arg in args:
             self += self._expand(arg)
 
     def _expand(self, arg):
         '''Internal function doing the actual work'''
         (root, ext) = os.path.splitext(arg)
         if ext != conf.LIB_SUFFIX or not os.path.basename(root).startswith(conf.LIB_PREFIX):
-            return [arg]
+            return [relativize(arg)]
         if len(conf.IMPORT_LIB_SUFFIX):
             dll = root + conf.IMPORT_LIB_SUFFIX
         else:
             dll = root.replace(conf.LIB_PREFIX, conf.DLL_PREFIX, 1) + conf.DLL_SUFFIX
         if os.path.exists(dll):
-            return [dll]
+            return [relativize(dll)]
         if os.path.exists(arg):
-            return [arg]
+            return [relativize(arg)]
         return self._expand_desc(arg)
 
     def _expand_desc(self, arg):
         '''Internal function taking care of lib descriptor expansion only'''
         if os.path.exists(arg + conf.LIBS_DESC_SUFFIX):
             with open(arg + conf.LIBS_DESC_SUFFIX, 'r') as f:
                 desc = LibDescriptor(f.readlines())
-            objs = desc['OBJS']
+            objs = [relativize(o) for o in desc['OBJS']]
             for lib in desc['LIBS']:
                 objs += self._expand(lib)
             return objs
         return [arg]
 
 if __name__ == '__main__':
     print " ".join(ExpandArgs(sys.argv[1:]))
--- a/config/expandlibs_exec.py
+++ b/config/expandlibs_exec.py
@@ -47,17 +47,17 @@ files with a list file. This can be used
 of a command line. The kind of list file format used depends on the
 EXPAND_LIBS_LIST_STYLE variable: 'list' for MSVC style lists (@file.list)
 or 'linkerscript' for GNU ld linker scripts.
 See https://bugzilla.mozilla.org/show_bug.cgi?id=584474#c59 for more details.
 '''
 from __future__ import with_statement
 import sys
 import os
-from expandlibs import ExpandArgs
+from expandlibs import ExpandArgs, relativize
 import expandlibs_config as conf
 from optparse import OptionParser
 import subprocess
 import tempfile
 import shutil
 
 class ExpandArgsMore(ExpandArgs):
     ''' Meant to be used as 'with ExpandArgsMore(args) as ...: '''
@@ -88,17 +88,17 @@ class ExpandArgsMore(ExpandArgs):
                 if os.path.exists(arg + conf.LIBS_DESC_SUFFIX):
                     newlist += self._extract(self._expand_desc(arg))
                 elif os.path.exists(arg) and len(ar_extract):
                     tmp = tempfile.mkdtemp(dir=os.curdir)
                     self.tmp.append(tmp)
                     subprocess.call(ar_extract + [os.path.abspath(arg)], cwd=tmp)
                     objs = []
                     for root, dirs, files in os.walk(tmp):
-                        objs += [os.path.join(root, f) for f in files if os.path.splitext(f)[1] == conf.OBJ_SUFFIX]
+                        objs += [relativize(os.path.join(root, f)) for f in files if os.path.splitext(f)[1] == conf.OBJ_SUFFIX]
                     newlist += objs
                 else:
                     newlist += [arg]
             else:
                 newlist += [arg]
         return newlist
 
     def makelist(self):
--- a/config/tests/unit-expandlibs.py
+++ b/config/tests/unit-expandlibs.py
@@ -2,16 +2,17 @@ from __future__ import with_statement
 import subprocess
 import unittest
 import sys
 import os
 import imp
 from tempfile import mkdtemp
 from shutil import rmtree
 sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
+from mozunit import MozTestRunner
 
 from UserString import UserString
 # Create a controlled configuration for use by expandlibs
 config_win = {
     'AR_EXTRACT': '',
     'DLL_PREFIX': '',
     'LIB_PREFIX': '',
     'OBJ_SUFFIX': '.obj',
@@ -30,33 +31,48 @@ config_unix = {
     'DLL_SUFFIX': '.so',
     'IMPORT_LIB_SUFFIX': '',
     'LIBS_DESC_SUFFIX': '.desc',
     'EXPAND_LIBS_LIST_STYLE': 'linkerscript',
 }
 
 config = sys.modules['expandlibs_config'] = imp.new_module('expandlibs_config')
 
-from expandlibs import LibDescriptor, ExpandArgs
+from expandlibs import LibDescriptor, ExpandArgs, relativize
 from expandlibs_gen import generate
 from expandlibs_exec import ExpandArgsMore
 
 def Lib(name):
     return config.LIB_PREFIX + name + config.LIB_SUFFIX
 
 def Obj(name):
     return name + config.OBJ_SUFFIX
 
 def Dll(name):
     return config.DLL_PREFIX + name + config.DLL_SUFFIX
 
 def ImportLib(name):
     if not len(config.IMPORT_LIB_SUFFIX): return Dll(name)
     return config.LIB_PREFIX + name + config.IMPORT_LIB_SUFFIX
 
+class TestRelativize(unittest.TestCase):
+    def test_relativize(self):
+        '''Test relativize()'''
+        os_path_exists = os.path.exists
+        def exists(path):
+            return True
+        os.path.exists = exists
+        self.assertEqual(relativize(os.path.abspath(os.curdir)), os.curdir)
+        self.assertEqual(relativize(os.path.abspath(os.pardir)), os.pardir)
+        self.assertEqual(relativize(os.path.join(os.curdir, 'a')), 'a')
+        self.assertEqual(relativize(os.path.join(os.path.abspath(os.curdir), 'a')), 'a')
+        # relativize is expected to return the absolute path if it is shorter
+        self.assertEqual(relativize(os.sep), os.sep)
+        os.path.exists = os.path.exists
+
 class TestLibDescriptor(unittest.TestCase):
     def test_serialize(self):
         '''Test LibDescriptor's serialization'''
         desc = LibDescriptor()
         desc[LibDescriptor.KEYS[0]] = ['a', 'b']
         self.assertEqual(str(desc), "%s = a b" % LibDescriptor.KEYS[0])
         desc['unsupported-key'] = ['a']
         self.assertEqual(str(desc), "%s = a b" % LibDescriptor.KEYS[0])
@@ -78,35 +94,39 @@ class TestLibDescriptor(unittest.TestCas
         self.assertEqual(False, 'foo' in desc)
 
 def wrap_method(conf, wrapped_method):
     '''Wrapper used to call a test with a specific configuration'''
     def _method(self):
         for key in conf:
             setattr(config, key, conf[key])
         self.init()
-        wrapped_method(self)
-        self.cleanup()
+        try:
+            wrapped_method(self)
+        except:
+            raise
+        finally:
+            self.cleanup()
     return _method
 
 class ReplicateTests(type):
     '''Replicates tests for unix and windows variants'''
     def __new__(cls, clsName, bases, dict):
         for name in [key for key in dict if key.startswith('test_')]:
             dict[name + '_unix'] = wrap_method(config_unix, dict[name])
             dict[name + '_unix'].__doc__ = dict[name].__doc__ + ' (unix)'
             dict[name + '_win'] = wrap_method(config_win, dict[name])
             dict[name + '_win'].__doc__ = dict[name].__doc__ + ' (win)'
             del dict[name]
         return type.__new__(cls, clsName, bases, dict)
 
 class TestCaseWithTmpDir(unittest.TestCase):
     __metaclass__ = ReplicateTests
     def init(self):
-        self.tmpdir = mkdtemp()
+        self.tmpdir = os.path.abspath(mkdtemp(dir=os.curdir))
 
     def cleanup(self):
         rmtree(self.tmpdir)
 
     def touch(self, files):
         for f in files:
             open(f, 'w').close()
 
@@ -144,104 +164,108 @@ class TestExpandInit(TestCaseWithTmpDir)
         # Create various objects and libraries 
         self.arg_files = [self.tmpfile(f) for f in [Lib('a'), Obj('b'), Obj('c'), Lib('d'), Obj('e')]]
         # We always give library names (LIB_PREFIX/SUFFIX), even for
         # dynamic/import libraries
         self.files = self.arg_files + [self.tmpfile(ImportLib('f'))]
         self.arg_files += [self.tmpfile(Lib('f'))]
         self.touch(self.files)
 
+    def assertRelEqual(self, args1, args2):
+        self.assertEqual(args1, [relativize(a) for a in args2])
+
 class TestExpandArgs(TestExpandInit):
     def test_expand(self):
         '''Test library expansion'''
         # Expanding arguments means libraries with a descriptor are expanded
         # with the descriptor content, and import libraries are used when
         # a library doesn't exist
         args = ExpandArgs(['foo', '-bar'] + self.arg_files + [self.tmpfile('liby', Lib('y'))])
-        self.assertEqual(args, ['foo', '-bar'] + self.files + self.liby_files + self.libx_files) 
+        self.assertRelEqual(args, ['foo', '-bar'] + self.files + self.liby_files + self.libx_files) 
 
         # When a library exists at the same time as a descriptor, we just use
         # the library
         self.touch([self.tmpfile('libx', Lib('x'))])
         args = ExpandArgs(['foo', '-bar'] + self.arg_files + [self.tmpfile('liby', Lib('y'))])
-        self.assertEqual(args, ['foo', '-bar'] + self.files + self.liby_files + [self.tmpfile('libx', Lib('x'))]) 
+        self.assertRelEqual(args, ['foo', '-bar'] + self.files + self.liby_files + [self.tmpfile('libx', Lib('x'))]) 
 
         self.touch([self.tmpfile('liby', Lib('y'))])
         args = ExpandArgs(['foo', '-bar'] + self.arg_files + [self.tmpfile('liby', Lib('y'))])
-        self.assertEqual(args, ['foo', '-bar'] + self.files + [self.tmpfile('liby', Lib('y'))])
+        self.assertRelEqual(args, ['foo', '-bar'] + self.files + [self.tmpfile('liby', Lib('y'))])
 
 class TestExpandArgsMore(TestExpandInit):
     def test_makelist(self):
         '''Test grouping object files in lists'''
         # ExpandArgsMore does the same as ExpandArgs
         with ExpandArgsMore(['foo', '-bar'] + self.arg_files + [self.tmpfile('liby', Lib('y'))]) as args:
-            self.assertEqual(args, ['foo', '-bar'] + self.files + self.liby_files + self.libx_files) 
+            self.assertRelEqual(args, ['foo', '-bar'] + self.files + self.liby_files + self.libx_files) 
 
             # But also has an extra method replacing object files with a list
             args.makelist()
             # self.files has objects at #1, #2, #4
-            self.assertEqual(args[:3], ['foo', '-bar'] + self.files[:1])
-            self.assertEqual(args[4:], [self.files[3]] + self.files[5:] + [self.tmpfile('liby', Lib('z'))])
+            self.assertRelEqual(args[:3], ['foo', '-bar'] + self.files[:1])
+            self.assertRelEqual(args[4:], [self.files[3]] + self.files[5:] + [self.tmpfile('liby', Lib('z'))])
 
             # Check the list file content
             objs = [f for f in self.files + self.liby_files + self.libx_files if f.endswith(config.OBJ_SUFFIX)]
             if config.EXPAND_LIBS_LIST_STYLE == "linkerscript":
                 self.assertNotEqual(args[3][0], '@')
                 filename = args[3]
-                content = ["INPUT(%s)" % f for f in objs]
+                content = ["INPUT(%s)" % relativize(f) for f in objs]
+                with open(filename, 'r') as f:
+                    self.assertEqual([l.strip() for l in f.readlines() if len(l.strip())], content)
             elif config.EXPAND_LIBS_LIST_STYLE == "list":
                 self.assertEqual(args[3][0], '@')
                 filename = args[3][1:]
                 content = objs
-
-            with open(filename, 'r') as f:
-                self.assertEqual([l.strip() for l in f.readlines() if len(l.strip())], content)
+                with open(filename, 'r') as f:
+                    self.assertRelEqual([l.strip() for l in f.readlines() if len(l.strip())], content)
 
             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 = {}
         def call(args, **kargs):
             # The command called is always AR_EXTRACT
             ar_extract = config.AR_EXTRACT.split()
-            self.assertEqual(args[:len(ar_extract)], ar_extract)
+            self.assertRelEqual(args[:len(ar_extract)], ar_extract)
             # Remaining argument is always one library
-            self.assertEqual([os.path.splitext(arg)[1] for arg in args[len(ar_extract):]], [config.LIB_SUFFIX])
+            self.assertRelEqual([os.path.splitext(arg)[1] for arg in args[len(ar_extract):]], [config.LIB_SUFFIX])
             # Simulate AR_EXTRACT extracting one object file for the library
             lib = os.path.splitext(os.path.basename(args[len(ar_extract)]))[0]
             extracted[lib] = os.path.join(kargs['cwd'], "%s" % Obj(lib))
             self.touch([extracted[lib]])
         subprocess.call = call
 
         # 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.assertEqual(args, ['foo', '-bar'] + self.files + [self.tmpfile('liby', Lib('y'))])
+            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()
 
             files = self.files + self.liby_files + self.libx_files
             if not len(config.AR_EXTRACT):
                 # If we don't have an AR_EXTRACT, extract() expands libraries with a
                 # descriptor when the corresponding library exists (which ExpandArgs
                 # alone doesn't)
-                self.assertEqual(args, ['foo', '-bar'] + files)
+                self.assertRelEqual(args, ['foo', '-bar'] + files)
             else:
                 # With AR_EXTRACT, it uses the descriptors when there are, and actually
                 # extracts the remaining libraries
-                self.assertEqual(args, ['foo', '-bar'] + [extracted[os.path.splitext(os.path.basename(f))[0]] if f.endswith(config.LIB_SUFFIX) else f for f in files])
+                self.assertRelEqual(args, ['foo', '-bar'] + [extracted[os.path.splitext(os.path.basename(f))[0]] if f.endswith(config.LIB_SUFFIX) else f for f in files])
 
             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
         subprocess.call = subprocess_call
 
 if __name__ == '__main__':
-    unittest.main()
+    unittest.main(testRunner=MozTestRunner())
--- a/js/src/config/expandlibs.py
+++ b/js/src/config/expandlibs.py
@@ -58,16 +58,39 @@ given a list of files, expandlibs will r
   replace ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} with the OBJS and LIBS the
   descriptor contains. And for each of these LIBS, also apply the same
   rules.
 '''
 from __future__ import with_statement
 import sys, os
 import expandlibs_config as conf
 
+def relativize(path):
+    '''Returns a path relative to the current working directory, if it is
+    shorter than the given path'''
+    def splitpath(path):
+        dir, file = os.path.split(path)
+        if os.path.splitdrive(dir)[1] == os.sep:
+            return [file]
+        return splitpath(dir) + [file]
+
+    if not os.path.exists(path):
+        return path
+    curdir = splitpath(os.path.abspath(os.curdir))
+    abspath = splitpath(os.path.abspath(path))
+    while curdir and abspath and curdir[0] == abspath[0]:
+        del curdir[0]
+        del abspath[0]
+    if not curdir and not abspath:
+        return '.'
+    relpath = os.path.join(*[os.pardir for i in curdir] + abspath)
+    if len(path) > len(relpath):
+        return relpath
+    return path
+
 class LibDescriptor(dict):
     KEYS = ['OBJS', 'LIBS']
 
     def __init__(self, content=None):
         '''Creates an instance of a lib descriptor, initialized with contents
         from a list of strings when given. This is intended for use with
         file.readlines()'''
         if isinstance(content, list) and all([isinstance(item, str) for item in content]):
@@ -94,32 +117,32 @@ class ExpandArgs(list):
         super(ExpandArgs, self).__init__()
         for arg in args:
             self += self._expand(arg)
 
     def _expand(self, arg):
         '''Internal function doing the actual work'''
         (root, ext) = os.path.splitext(arg)
         if ext != conf.LIB_SUFFIX or not os.path.basename(root).startswith(conf.LIB_PREFIX):
-            return [arg]
+            return [relativize(arg)]
         if len(conf.IMPORT_LIB_SUFFIX):
             dll = root + conf.IMPORT_LIB_SUFFIX
         else:
             dll = root.replace(conf.LIB_PREFIX, conf.DLL_PREFIX, 1) + conf.DLL_SUFFIX
         if os.path.exists(dll):
-            return [dll]
+            return [relativize(dll)]
         if os.path.exists(arg):
-            return [arg]
+            return [relativize(arg)]
         return self._expand_desc(arg)
 
     def _expand_desc(self, arg):
         '''Internal function taking care of lib descriptor expansion only'''
         if os.path.exists(arg + conf.LIBS_DESC_SUFFIX):
             with open(arg + conf.LIBS_DESC_SUFFIX, 'r') as f:
                 desc = LibDescriptor(f.readlines())
-            objs = desc['OBJS']
+            objs = [relativize(o) for o in desc['OBJS']]
             for lib in desc['LIBS']:
                 objs += self._expand(lib)
             return objs
         return [arg]
 
 if __name__ == '__main__':
     print " ".join(ExpandArgs(sys.argv[1:]))
--- a/js/src/config/expandlibs_exec.py
+++ b/js/src/config/expandlibs_exec.py
@@ -47,17 +47,17 @@ files with a list file. This can be used
 of a command line. The kind of list file format used depends on the
 EXPAND_LIBS_LIST_STYLE variable: 'list' for MSVC style lists (@file.list)
 or 'linkerscript' for GNU ld linker scripts.
 See https://bugzilla.mozilla.org/show_bug.cgi?id=584474#c59 for more details.
 '''
 from __future__ import with_statement
 import sys
 import os
-from expandlibs import ExpandArgs
+from expandlibs import ExpandArgs, relativize
 import expandlibs_config as conf
 from optparse import OptionParser
 import subprocess
 import tempfile
 import shutil
 
 class ExpandArgsMore(ExpandArgs):
     ''' Meant to be used as 'with ExpandArgsMore(args) as ...: '''
@@ -88,17 +88,17 @@ class ExpandArgsMore(ExpandArgs):
                 if os.path.exists(arg + conf.LIBS_DESC_SUFFIX):
                     newlist += self._extract(self._expand_desc(arg))
                 elif os.path.exists(arg) and len(ar_extract):
                     tmp = tempfile.mkdtemp(dir=os.curdir)
                     self.tmp.append(tmp)
                     subprocess.call(ar_extract + [os.path.abspath(arg)], cwd=tmp)
                     objs = []
                     for root, dirs, files in os.walk(tmp):
-                        objs += [os.path.join(root, f) for f in files if os.path.splitext(f)[1] == conf.OBJ_SUFFIX]
+                        objs += [relativize(os.path.join(root, f)) for f in files if os.path.splitext(f)[1] == conf.OBJ_SUFFIX]
                     newlist += objs
                 else:
                     newlist += [arg]
             else:
                 newlist += [arg]
         return newlist
 
     def makelist(self):