Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / config / expandlibs.py
blobb4f35b8f6a97e1eeee0e2ffbcf987473fbb0924a
1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 '''Expandlibs is a system that allows to replace some libraries with a
6 descriptor file containing some linking information about them.
8 The descriptor file format is as follows:
9 ---8<-----
10 OBJS = a.o b.o ...
11 LIBS = libfoo.a libbar.a ...
12 --->8-----
14 (In the example above, OBJ_SUFFIX is o and LIB_SUFFIX is a).
16 Expandlibs also canonicalizes how to pass libraries to the linker, such
17 that only the ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} form needs to be used:
18 given a list of files, expandlibs will replace items with the form
19 ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} following these rules:
21 - If a ${DLL_PREFIX}${ROOT}.${DLL_SUFFIX} or
22 ${DLL_PREFIX}${ROOT}.${IMPORT_LIB_SUFFIX} file exists, use that instead
23 - If the ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} file exists, use it
24 - If a ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX}.${LIB_DESC_SUFFIX} file exists,
25 replace ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} with the OBJS and LIBS the
26 descriptor contains. And for each of these LIBS, also apply the same
27 rules.
28 '''
29 from __future__ import with_statement
30 import sys, os, errno
31 import expandlibs_config as conf
33 def ensureParentDir(file):
34 '''Ensures the directory parent to the given file exists'''
35 dir = os.path.dirname(file)
36 if dir and not os.path.exists(dir):
37 try:
38 os.makedirs(dir)
39 except OSError, error:
40 if error.errno != errno.EEXIST:
41 raise
43 def relativize(path):
44 '''Returns a path relative to the current working directory, if it is
45 shorter than the given path'''
46 def splitpath(path):
47 dir, file = os.path.split(path)
48 if os.path.splitdrive(dir)[1] == os.sep:
49 return [file]
50 return splitpath(dir) + [file]
52 if not os.path.exists(path):
53 return path
54 curdir = splitpath(os.path.abspath(os.curdir))
55 abspath = splitpath(os.path.abspath(path))
56 while curdir and abspath and curdir[0] == abspath[0]:
57 del curdir[0]
58 del abspath[0]
59 if not curdir and not abspath:
60 return '.'
61 relpath = os.path.join(*[os.pardir for i in curdir] + abspath)
62 if len(path) > len(relpath):
63 return relpath
64 return path
66 def isObject(path):
67 '''Returns whether the given path points to an object file, that is,
68 ends with OBJ_SUFFIX or .i_o'''
69 return os.path.splitext(path)[1] in [conf.OBJ_SUFFIX, '.i_o']
71 def isDynamicLib(path):
72 '''Returns whether the given path points to a dynamic library, that is,
73 ends with DLL_SUFFIX.'''
74 # On mac, the xul library is named XUL, instead of libxul.dylib. Assume any
75 # file by that name is a dynamic library.
76 return os.path.splitext(path)[1] == conf.DLL_SUFFIX or os.path.basename(path) == 'XUL'
78 class LibDescriptor(dict):
79 KEYS = ['OBJS', 'LIBS']
81 def __init__(self, content=None):
82 '''Creates an instance of a lib descriptor, initialized with contents
83 from a list of strings when given. This is intended for use with
84 file.readlines()'''
85 if isinstance(content, list) and all([isinstance(item, str) for item in content]):
86 pass
87 elif content is not None:
88 raise TypeError("LibDescriptor() arg 1 must be None or a list of strings")
89 super(LibDescriptor, self).__init__()
90 for key in self.KEYS:
91 self[key] = []
92 if not content:
93 return
94 for key, value in [(s.strip() for s in item.split('=', 2)) for item in content if item.find('=') >= 0]:
95 if key in self.KEYS:
96 self[key] = value.split()
98 def __str__(self):
99 '''Serializes the lib descriptor'''
100 return '\n'.join('%s = %s' % (k, ' '.join(self[k])) for k in self.KEYS if len(self[k]))
102 class ExpandArgs(list):
103 def __init__(self, args):
104 '''Creates a clone of the |args| list and performs file expansion on
105 each item it contains'''
106 super(ExpandArgs, self).__init__()
107 self._descs = set()
108 for arg in args:
109 self += self._expand(arg)
111 def _expand(self, arg):
112 '''Internal function doing the actual work'''
113 (root, ext) = os.path.splitext(arg)
114 if ext != conf.LIB_SUFFIX or not os.path.basename(root).startswith(conf.LIB_PREFIX):
115 return [relativize(arg)]
116 if len(conf.IMPORT_LIB_SUFFIX):
117 dll = root + conf.IMPORT_LIB_SUFFIX
118 else:
119 dll = root.replace(conf.LIB_PREFIX, conf.DLL_PREFIX, 1) + conf.DLL_SUFFIX
120 if os.path.exists(dll):
121 return [relativize(dll)]
122 if os.path.exists(arg):
123 return [relativize(arg)]
124 return self._expand_desc(arg)
126 def _expand_desc(self, arg):
127 '''Internal function taking care of lib descriptor expansion only'''
128 desc = os.path.abspath(arg + conf.LIBS_DESC_SUFFIX)
129 if os.path.exists(desc):
130 if desc in self._descs:
131 return []
132 self._descs.add(desc)
133 with open(desc, 'r') as f:
134 desc = LibDescriptor(f.readlines())
135 objs = [relativize(o) for o in desc['OBJS']]
136 for lib in desc['LIBS']:
137 objs += self._expand(lib)
138 return objs
139 return [arg]
141 if __name__ == '__main__':
142 print " ".join(ExpandArgs(sys.argv[1:]))