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 idunknown
push userunknown
push dateunknown
reviewersted
bugs644081
milestone2.2a1pre
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):