| # This Source Code Form is subject to the terms of the Mozilla Public |
| # License, v. 2.0. If a copy of the MPL was not distributed with this |
| # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| |
| '''Expandlibs is a system that allows to replace some libraries with a |
| descriptor file containing some linking information about them. |
| |
| The descriptor file format is as follows: |
| ---8<----- |
| OBJS = a.o b.o ... |
| LIBS = libfoo.a libbar.a ... |
| --->8----- |
| |
| (In the example above, OBJ_SUFFIX is o and LIB_SUFFIX is a). |
| |
| Expandlibs also canonicalizes how to pass libraries to the linker, such |
| that only the ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} form needs to be used: |
| given a list of files, expandlibs will replace items with the form |
| ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} following these rules: |
| |
| - If a ${DLL_PREFIX}${ROOT}.${DLL_SUFFIX} or |
| ${DLL_PREFIX}${ROOT}.${IMPORT_LIB_SUFFIX} file exists, use that instead |
| - If the ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} file exists, use it |
| - If a ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX}.${LIB_DESC_SUFFIX} file exists, |
| 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, errno |
| import expandlibs_config as conf |
| |
| def ensureParentDir(file): |
| '''Ensures the directory parent to the given file exists''' |
| dir = os.path.dirname(file) |
| if dir and not os.path.exists(dir): |
| try: |
| os.makedirs(dir) |
| except OSError, error: |
| if error.errno != errno.EEXIST: |
| raise |
| |
| 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 |
| |
| def isObject(path): |
| '''Returns whether the given path points to an object file, that is, |
| ends with OBJ_SUFFIX or .i_o''' |
| return os.path.splitext(path)[1] in [conf.OBJ_SUFFIX, '.i_o'] |
| |
| def isDynamicLib(path): |
| '''Returns whether the given path points to a dynamic library, that is, |
| ends with DLL_SUFFIX.''' |
| # On mac, the xul library is named XUL, instead of libxul.dylib. Assume any |
| # file by that name is a dynamic library. |
| return os.path.splitext(path)[1] == conf.DLL_SUFFIX or os.path.basename(path) == 'XUL' |
| |
| 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]): |
| pass |
| elif content is not None: |
| raise TypeError("LibDescriptor() arg 1 must be None or a list of strings") |
| super(LibDescriptor, self).__init__() |
| for key in self.KEYS: |
| self[key] = [] |
| if not content: |
| return |
| for key, value in [(s.strip() for s in item.split('=', 2)) for item in content if item.find('=') >= 0]: |
| if key in self.KEYS: |
| self[key] = value.split() |
| |
| def __str__(self): |
| '''Serializes the lib descriptor''' |
| return '\n'.join('%s = %s' % (k, ' '.join(self[k])) for k in self.KEYS if len(self[k])) |
| |
| class ExpandArgs(list): |
| def __init__(self, args): |
| '''Creates a clone of the |args| list and performs file expansion on |
| each item it contains''' |
| super(ExpandArgs, self).__init__() |
| self._descs = set() |
| 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 [relativize(arg)] |
| if conf.LIB_PREFIX: |
| dll = root.replace(conf.LIB_PREFIX, conf.DLL_PREFIX, 1) + conf.DLL_SUFFIX |
| else: |
| dll = root + conf.DLL_SUFFIX |
| if os.path.exists(dll): |
| if conf.IMPORT_LIB_SUFFIX: |
| return [relativize(root + conf.IMPORT_LIB_SUFFIX)] |
| else: |
| return [relativize(dll)] |
| return self._expand_desc(arg) |
| |
| def _expand_desc(self, arg): |
| '''Internal function taking care of lib descriptor expansion only''' |
| desc = os.path.abspath(arg + conf.LIBS_DESC_SUFFIX) |
| if os.path.exists(desc): |
| if desc in self._descs: |
| return [] |
| self._descs.add(desc) |
| with open(desc, 'r') as f: |
| desc = LibDescriptor(f.readlines()) |
| objs = [relativize(o) for o in desc['OBJS']] |
| for lib in desc['LIBS']: |
| objs += self._expand(lib) |
| return objs |
| return [relativize(arg)] |
| |
| if __name__ == '__main__': |
| print " ".join(ExpandArgs(sys.argv[1:])) |