author Mike Hommey <>
Fri, 20 Jan 2012 09:50:44 +0100
changeset 86201 283408b8d8a3724ef0f5ee2bce08e21ce3acb924
parent 76759 66fb17949bd61310dcd36f5abb5fb92ffcd6ecea
child 86553 747d47aadb5997892fb3be8c462e361c48f89e5d
permissions -rw-r--r--
Bug 603370 part 1 - Add an option to expandlibs-exec to allow to reorder the objects list. r=ted

# Version: MPL 1.1/GPL 2.0/LGPL 2.1
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
# The Original Code is a build helper for libraries
# The Initial Developer of the Original Code is
# the Mozilla Foundation
# Portions created by the Initial Developer are Copyright (C) 2011
# the Initial Developer. All Rights Reserved.
# Contributor(s):
# Mike Hommey <>
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
# ***** END LICENSE BLOCK *****

''' applies expandlibs rules, and some more (see below) to
a given command line, and executes that command line with the expanded

With the --extract argument (useful for e.g. $(AR)), it extracts object files
from static libraries (or use those listed in library descriptors directly).

With the --uselist argument (useful for e.g. $(CC)), it replaces all object
files with a list file. This can be used to avoid limitations in the length
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 for more details.

With the --reorder argument, followed by a file name, it will reorder the
object files from the command line according to the order given in the file.
Implies --extract.
from __future__ import with_statement
import sys
import os
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 ...: '''
    def __enter__(self):
        self.tmp = []
        return self
    def __exit__(self, type, value, tb):
        '''Automatically remove temporary files'''
        for tmp in self.tmp:
            if os.path.isdir(tmp):
                shutil.rmtree(tmp, True)

    def extract(self):
        self[0:] = self._extract(self)

    def _extract(self, args):
        '''When a static library name is found, either extract its contents
        in a temporary directory or use the information found in the
        corresponding lib descriptor.
        ar_extract = conf.AR_EXTRACT.split()
        newlist = []
        for arg in args:
            if os.path.splitext(arg)[1] == conf.LIB_SUFFIX:
                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)
           + [os.path.abspath(arg)], cwd=tmp)
                    objs = []
                    for root, dirs, files in os.walk(tmp):
                        objs += [relativize(os.path.join(root, f)) for f in files if os.path.splitext(f)[1] in [conf.OBJ_SUFFIX, '.i_o']]
                    newlist += objs
                    newlist += [arg]
                newlist += [arg]
        return newlist

    def makelist(self):
        '''Replaces object file names with a temporary list file, using a
        list format depending on the EXPAND_LIBS_LIST_STYLE variable
        objs = [o for o in self if os.path.splitext(o)[1] == conf.OBJ_SUFFIX]
        if not len(objs): return
        fd, tmp = tempfile.mkstemp(suffix=".list",dir=os.curdir)
        if conf.EXPAND_LIBS_LIST_STYLE == "linkerscript":
            content = ["INPUT(%s)\n" % obj for obj in objs]
            ref = tmp
        elif conf.EXPAND_LIBS_LIST_STYLE == "list":
            content = ["%s\n" % obj for obj in objs]
            ref = "@" + tmp
        f = os.fdopen(fd, "w")
        idx = self.index(objs[0])
        newlist = self[0:idx] + [ref] + [item for item in self[idx:] if item not in objs]
        self[0:] = newlist

    def reorder(self, order_list):
        '''Given a list of file names without OBJ_SUFFIX, rearrange self
        so that the object file names it contains are ordered according to
        that list.
        objs = [o for o in self if o.endswith(conf.OBJ_SUFFIX)]
        if not objs: return
        idx = self.index(objs[0])
        # Keep everything before the first object, then the ordered objects,
        # then any other objects, then any non-objects after the first object
        objnames = dict([(os.path.splitext(os.path.basename(o))[0], o) for o in objs])
        self[0:] = self[0:idx] + [objnames[o] for o in order_list if o in objnames] + \
                   [o for o in objs if os.path.splitext(os.path.basename(o))[0] not in order_list] + \
                   [x for x in self[idx:] if not x.endswith(conf.OBJ_SUFFIX)]

def main():
    parser = OptionParser()
    parser.add_option("--extract", action="store_true", dest="extract",
        help="when a library has no descriptor file, extract it first, when possible")
    parser.add_option("--uselist", action="store_true", dest="uselist",
        help="use a list file for objects when executing a command")
    parser.add_option("--verbose", action="store_true", dest="verbose",
        help="display executed command and temporary files content")
    parser.add_option("--reorder", dest="reorder",
        help="reorder the objects according to the given list", metavar="FILE")

    (options, args) = parser.parse_args()

    with ExpandArgsMore(args) as args:
        if options.extract or options.reorder:
        if options.reorder:
            with open(options.reorder) as file:
                args.reorder([l.strip() for l in file.readlines()])
        if options.uselist:

        if options.verbose:
            print >>sys.stderr, "Executing: " + " ".join(args)
            for tmp in [f for f in args.tmp if os.path.isfile(f)]:
                print >>sys.stderr, tmp + ":"
                with open(tmp) as file:
                    print >>sys.stderr, "".join(["    " + l for l in file.readlines()])

if __name__ == '__main__':