|
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/. |
|
4 |
|
5 '''Expandlibs is a system that allows to replace some libraries with a |
|
6 descriptor file containing some linking information about them. |
|
7 |
|
8 The descriptor file format is as follows: |
|
9 ---8<----- |
|
10 OBJS = a.o b.o ... |
|
11 LIBS = libfoo.a libbar.a ... |
|
12 --->8----- |
|
13 |
|
14 (In the example above, OBJ_SUFFIX is o and LIB_SUFFIX is a). |
|
15 |
|
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: |
|
20 |
|
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 |
|
32 |
|
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 |
|
42 |
|
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] |
|
51 |
|
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 |
|
65 |
|
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'] |
|
70 |
|
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' |
|
77 |
|
78 class LibDescriptor(dict): |
|
79 KEYS = ['OBJS', 'LIBS'] |
|
80 |
|
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() |
|
97 |
|
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])) |
|
101 |
|
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 for arg in args: |
|
108 self += self._expand(arg) |
|
109 |
|
110 def _expand(self, arg): |
|
111 '''Internal function doing the actual work''' |
|
112 (root, ext) = os.path.splitext(arg) |
|
113 if ext != conf.LIB_SUFFIX or not os.path.basename(root).startswith(conf.LIB_PREFIX): |
|
114 return [relativize(arg)] |
|
115 if len(conf.IMPORT_LIB_SUFFIX): |
|
116 dll = root + conf.IMPORT_LIB_SUFFIX |
|
117 else: |
|
118 dll = root.replace(conf.LIB_PREFIX, conf.DLL_PREFIX, 1) + conf.DLL_SUFFIX |
|
119 if os.path.exists(dll): |
|
120 return [relativize(dll)] |
|
121 if os.path.exists(arg): |
|
122 return [relativize(arg)] |
|
123 return self._expand_desc(arg) |
|
124 |
|
125 def _expand_desc(self, arg): |
|
126 '''Internal function taking care of lib descriptor expansion only''' |
|
127 if os.path.exists(arg + conf.LIBS_DESC_SUFFIX): |
|
128 with open(arg + conf.LIBS_DESC_SUFFIX, 'r') as f: |
|
129 desc = LibDescriptor(f.readlines()) |
|
130 objs = [relativize(o) for o in desc['OBJS']] |
|
131 for lib in desc['LIBS']: |
|
132 objs += self._expand(lib) |
|
133 return objs |
|
134 return [arg] |
|
135 |
|
136 class ExpandLibsDeps(ExpandArgs): |
|
137 '''Same as ExpandArgs, but also adds the library descriptor to the list''' |
|
138 def _expand_desc(self, arg): |
|
139 objs = super(ExpandLibsDeps, self)._expand_desc(arg) |
|
140 if os.path.exists(arg + conf.LIBS_DESC_SUFFIX): |
|
141 objs += [relativize(arg + conf.LIBS_DESC_SUFFIX)] |
|
142 return objs |
|
143 |
|
144 if __name__ == '__main__': |
|
145 print " ".join(ExpandArgs(sys.argv[1:])) |