config/expandlibs.py
author Dorel Luca <dluca@mozilla.com>
Sat, 13 Jan 2018 15:17:49 +0200
changeset 399195 64f82460345884dd0f5935645765acccf771d7c4
parent 208775 d959a6081ceacde13d41b8a4ee192c912c85ef02
permissions -rw-r--r--
Backed out 19 changesets (bug 1411654) for Android nightly bustages a=backout Backed out changeset 649e7aa405ca (bug 1411654) Backed out changeset c2e51b70519f (bug 1411654) Backed out changeset a371f3ef4312 (bug 1411654) Backed out changeset db978e230556 (bug 1411654) Backed out changeset 56538ed998cf (bug 1411654) Backed out changeset 6ff0cdf46a3d (bug 1411654) Backed out changeset 0e493bacc5e3 (bug 1411654) Backed out changeset 23cbcf427745 (bug 1411654) Backed out changeset eda74143389f (bug 1411654) Backed out changeset 359fadf9b3e9 (bug 1411654) Backed out changeset 5c64eda20f1e (bug 1411654) Backed out changeset bffb6a5b78d1 (bug 1411654) Backed out changeset 43787f4089c3 (bug 1411654) Backed out changeset 9141bbdfd13b (bug 1411654) Backed out changeset 108674372ef7 (bug 1411654) Backed out changeset fb15e1f54987 (bug 1411654) Backed out changeset 264476c77210 (bug 1411654) Backed out changeset d23f467218da (bug 1411654) Backed out changeset 78576ff98660 (bug 1411654)

# 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:]))