Bug 757339 - Make expandlibs commands generate dependencies like gcc does. r=ted
authorMike Hommey <mh+mozilla@glandium.org>
Fri, 08 Jun 2012 08:59:02 +0200
changeset 100950 2d48da1f2bd79658ee42dd92898ec0f0cc96fd69
parent 100948 13ef4ab18b45c68aefe7a1feadf5b11b7b924076
child 100951 22a835a8a15fca8012b304b5778f35895f904b74
push id1316
push userakeybl@mozilla.com
push dateMon, 27 Aug 2012 22:37:00 +0000
treeherdermozilla-beta@db4b09302ee2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersted
bugs757339
milestone16.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 757339 - Make expandlibs commands generate dependencies like gcc does. r=ted
config/config.mk
config/expandlibs.py
config/expandlibs_deps.py
config/expandlibs_exec.py
config/expandlibs_gen.py
config/rules.mk
config/tests/unit-expandlibs.py
js/src/config/config.mk
js/src/config/expandlibs.py
js/src/config/expandlibs_deps.py
js/src/config/expandlibs_exec.py
js/src/config/expandlibs_gen.py
js/src/config/rules.mk
--- a/config/config.mk
+++ b/config/config.mk
@@ -726,19 +726,18 @@ ifdef TIERS
 DIRS += $(foreach tier,$(TIERS),$(tier_$(tier)_dirs))
 STATIC_DIRS += $(foreach tier,$(TIERS),$(tier_$(tier)_staticdirs))
 endif
 
 OPTIMIZE_JARS_CMD = $(PYTHON) $(call core_abspath,$(topsrcdir)/config/optimizejars.py)
 
 CREATE_PRECOMPLETE_CMD = $(PYTHON) $(call core_abspath,$(topsrcdir)/config/createprecomplete.py)
 
-EXPAND_LIBS_DEPS = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config $(topsrcdir)/config/expandlibs_deps.py
-EXPAND_LIBS_EXEC = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config $(topsrcdir)/config/expandlibs_exec.py
-EXPAND_LIBS_GEN = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config $(topsrcdir)/config/expandlibs_gen.py
+EXPAND_LIBS_EXEC = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config $(topsrcdir)/config/expandlibs_exec.py $(if $@,--depend $(MDDEPDIR)/$(basename $(@F)).pp --target $@)
+EXPAND_LIBS_GEN = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config $(topsrcdir)/config/expandlibs_gen.py $(if $@,--depend $(MDDEPDIR)/$(basename $(@F)).pp)
 EXPAND_AR = $(EXPAND_LIBS_EXEC) --extract -- $(AR)
 EXPAND_CC = $(EXPAND_LIBS_EXEC) --uselist -- $(CC)
 EXPAND_CCC = $(EXPAND_LIBS_EXEC) --uselist -- $(CCC)
 EXPAND_LD = $(EXPAND_LIBS_EXEC) --uselist -- $(LD)
 EXPAND_MKSHLIB_ARGS = --uselist
 ifdef SYMBOL_ORDER
 EXPAND_MKSHLIB_ARGS += --symbol-order $(SYMBOL_ORDER)
 endif
--- a/config/expandlibs.py
+++ b/config/expandlibs.py
@@ -22,19 +22,29 @@ given a list of files, expandlibs will r
   ${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
+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]
@@ -111,10 +121,18 @@ class ExpandArgs(list):
             with open(arg + conf.LIBS_DESC_SUFFIX, '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 [arg]
 
+class ExpandLibsDeps(ExpandArgs):
+    '''Same as ExpandArgs, but also adds the library descriptor to the list'''
+    def _expand_desc(self, arg):
+        objs = super(ExpandLibsDeps, self)._expand_desc(arg)
+        if os.path.exists(arg + conf.LIBS_DESC_SUFFIX):
+            objs += [relativize(arg + conf.LIBS_DESC_SUFFIX)]
+        return objs
+
 if __name__ == '__main__':
     print " ".join(ExpandArgs(sys.argv[1:]))
deleted file mode 100644
--- a/config/expandlibs_deps.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# 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_deps.py takes a list of key/value pairs and prints, for each
-key, a list of all dependencies corresponding to the corresponding value.
-Libraries are thus expanded, and their descriptor is also made part of this
-list.
-The list of key/value is passed on the command line in the form
-"var1 = value , var2 = value ..."
-'''
-import sys
-import os
-from expandlibs import ExpandArgs, relativize
-import expandlibs_config as conf
-
-class ExpandLibsDeps(ExpandArgs):
-    def _expand_desc(self, arg):
-        objs = super(ExpandLibsDeps, self)._expand_desc(arg)
-        if os.path.exists(arg + conf.LIBS_DESC_SUFFIX):
-            objs += [relativize(arg + conf.LIBS_DESC_SUFFIX)]
-        return objs
-
-def split_args(args):
-    '''Transforms a list in the form
-    ['var1', '=', 'value', ',', 'var2', '=', 'other', 'value', ',' ...]
-    into a corresponding dict 
-    { 'var1': ['value'],
-      'var2': ['other', 'value'], ... }'''
-
-    result = {}
-    while args:
-        try:
-            i = args.index(',')
-            l = args[:i]
-            args[:i + 1] = []
-        except:
-            l = args
-            args = []
-        if l[1] != '=':
-            raise RuntimeError('Expected "var = value" format')
-        result[l[0]] = l[2:]
-    return result
-
-if __name__ == '__main__':
-    for key, value in split_args(sys.argv[1:]).iteritems():
-        expanded = ExpandLibsDeps(value)
-        if os.sep == '\\':
-            expanded = [o.replace(os.sep, '/') for o in expanded]
-        print "%s = %s" % (key, " ".join(expanded))
--- a/config/expandlibs_exec.py
+++ b/config/expandlibs_exec.py
@@ -18,17 +18,17 @@ See https://bugzilla.mozilla.org/show_bu
 
 With the --symbol-order argument, followed by a file name, it will add the
 relevant linker options to change the order in which the linker puts the
 symbols appear in the resulting binary. Only works for ELF targets.
 '''
 from __future__ import with_statement
 import sys
 import os
-from expandlibs import ExpandArgs, relativize, isObject
+from expandlibs import ExpandArgs, relativize, isObject, ensureParentDir, ExpandLibsDeps
 import expandlibs_config as conf
 from optparse import OptionParser
 import subprocess
 import tempfile
 import shutil
 import subprocess
 import re
 
@@ -264,38 +264,59 @@ class SectionFinder(object):
             if len(tmp) > 1 and len(tmp[1]) > 6 and tmp[1][6] in ['O', 'F']:
                 tmp = tmp[1][8:].split()
                 # That gives us ["<section>","<length>", "<symbol>"]
                 syms.append((tmp[-1], tmp[0]))
         return syms
 
 def main():
     parser = OptionParser()
+    parser.add_option("--depend", dest="depend", metavar="FILE",
+        help="generate dependencies for the given execution and store it in the given file")
+    parser.add_option("--target", dest="target", metavar="FILE",
+        help="designate the target for dependencies")
     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("--symbol-order", dest="symbol_order", metavar="FILE",
         help="use the given list of symbols to order symbols in the resulting binary when using with a linker")
 
     (options, args) = parser.parse_args()
 
+    if not options.target:
+        options.depend = False
+    if options.depend:
+        deps = ExpandLibsDeps(args)
+        # Filter out common command wrappers
+        while os.path.basename(deps[0]) in ['ccache', 'distcc']:
+            deps.pop(0)
+        # Remove command
+        deps.pop(0)
     with ExpandArgsMore(args) as args:
         if options.extract:
             args.extract()
         if options.symbol_order:
             args.orderSymbols(options.symbol_order)
         if options.uselist:
             args.makelist()
 
         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()])
             sys.stderr.flush()
-        exit(subprocess.call(args))
+        ret = subprocess.call(args)
+        if ret:
+            exit(ret)
+    if not options.depend:
+        return
+    ensureParentDir(options.depend)
+    with open(options.depend, 'w') as depfile:
+        depfile.write("%s : %s\n" % (options.target, ' '.join(dep for dep in deps if os.path.isfile(dep) and dep != options.target)))
+
 
 if __name__ == '__main__':
     main()
--- a/config/expandlibs_gen.py
+++ b/config/expandlibs_gen.py
@@ -1,19 +1,21 @@
 # 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/.
 
 '''Given a list of object files and library names, prints a library
 descriptor to standard output'''
 
+from __future__ import with_statement
 import sys
 import os
 import expandlibs_config as conf
-from expandlibs import LibDescriptor, isObject
+from expandlibs import LibDescriptor, isObject, ensureParentDir, ExpandLibsDeps
+from optparse import OptionParser
 
 def generate(args):
     desc = LibDescriptor()
     for arg in args:
         if isObject(arg):
             if os.path.exists(arg):
                 desc['OBJS'].append(os.path.abspath(arg))
             else:
@@ -21,9 +23,25 @@ def generate(args):
         elif os.path.splitext(arg)[1] == conf.LIB_SUFFIX:
             if os.path.exists(arg) or os.path.exists(arg + conf.LIBS_DESC_SUFFIX):
                 desc['LIBS'].append(os.path.abspath(arg))
             else:
                 raise Exception("File not found: %s" % arg)
     return desc
 
 if __name__ == '__main__':
-    print generate(sys.argv[1:])
+    parser = OptionParser()
+    parser.add_option("--depend", dest="depend", metavar="FILE",
+        help="generate dependencies for the given execution and store it in the given file")
+    parser.add_option("-o", dest="output", metavar="FILE",
+        help="send output to the given file")
+
+    (options, args) = parser.parse_args()
+    if not options.output:
+        raise Exception("Missing option: -o")
+
+    ensureParentDir(options.output)
+    with open(options.output, 'w') as outfile:
+        print >>outfile, generate(args)
+    if options.depend:
+        ensureParentDir(options.depend)
+        with open(options.depend, 'w') as depfile:
+            depfile.write("%s : %s\n" % (options.output, ' '.join(ExpandLibsDeps(args))))
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -638,33 +638,16 @@ else # !IS_COMPONENT
 endif # IS_COMPONENT
 endif # EXPORT_LIBRARY
 endif # LIBRARY_NAME
 
 ifneq (,$(filter-out %.$(LIB_SUFFIX),$(SHARED_LIBRARY_LIBS)))
 $(error SHARED_LIBRARY_LIBS must contain .$(LIB_SUFFIX) files only)
 endif
 
-# Create dependencies on static (and shared EXTRA_DSO_LIBS) libraries
-ifneq (,$(strip $(filter %.$(LIB_SUFFIX),$(LIBS) $(EXTRA_DSO_LDOPTS)) $(SHARED_LIBRARY_LIBS) $(EXTRA_DSO_LIBS)))
-$(MDDEPDIR)/libs: Makefile.in
-	@mkdir -p $(MDDEPDIR)
-	@$(EXPAND_LIBS_DEPS) LIBS_DEPS = $(filter %.$(LIB_SUFFIX),$(LIBS)) , \
-	                     SHARED_LIBRARY_LIBS_DEPS = $(SHARED_LIBRARY_LIBS) , \
-	                     DSO_LDOPTS_DEPS = $(EXTRA_DSO_LIBS) $(filter %.$(LIB_SUFFIX), $(EXTRA_DSO_LDOPTS)) > $@
-
-ifneq (,$(wildcard $(MDDEPDIR)/libs))
-include $(MDDEPDIR)/libs
-endif
-
-$(MDDEPDIR)/libs: $(wildcard $(filter %.$(LIBS_DESC_SUFFIX),$(LIBS_DEPS) $(SHARED_LIBRARY_LIBS_DEPS) $(DSO_LDOPTS_DEPS)))
-
-EXTRA_DEPS += $(MDDEPDIR)/libs
-endif
-
 HOST_LIBS_DEPS = $(filter %.$(LIB_SUFFIX),$(HOST_LIBS))
 
 # Dependencies which, if modified, should cause everything to rebuild
 GLOBAL_DEPS += Makefile Makefile.in $(DEPTH)/config/autoconf.mk $(topsrcdir)/config/config.mk
 
 ##############################################
 include $(topsrcdir)/config/makefiles/target_libs.mk
 
@@ -747,17 +730,17 @@ endif
 alltags:
 	$(RM) TAGS
 	find $(topsrcdir) -name dist -prune -o \( -name '*.[hc]' -o -name '*.cp' -o -name '*.cpp' -o -name '*.idl' \) -print | $(TAG_PROGRAM)
 
 #
 # PROGRAM = Foo
 # creates OBJS, links with LIBS to create Foo
 #
-$(PROGRAM): $(PROGOBJS) $(LIBS_DEPS) $(EXTRA_DEPS) $(EXE_DEF_FILE) $(RESFILE) $(GLOBAL_DEPS)
+$(PROGRAM): $(PROGOBJS) $(EXTRA_DEPS) $(EXE_DEF_FILE) $(RESFILE) $(GLOBAL_DEPS)
 	@$(RM) $@.manifest
 ifeq (_WINNT,$(GNU_CC)_$(OS_ARCH))
 	$(EXPAND_LD) -NOLOGO -OUT:$@ -PDB:$(LINK_PDBFILE) $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(MOZ_GLUE_PROGRAM_LDFLAGS) $(PROGOBJS) $(RESFILE) $(LIBS) $(EXTRA_LIBS) $(OS_LIBS)
 ifdef MSMANIFEST_TOOL
 	@if test -f $@.manifest; then \
 		if test -f "$(srcdir)/$@.manifest"; then \
 			echo "Embedding manifest from $(srcdir)/$@.manifest and $@.manifest"; \
 			mt.exe -NOLOGO -MANIFEST "$(win_srcdir)/$@.manifest" $@.manifest -OUTPUTRESOURCE:$@\;1; \
@@ -815,17 +798,17 @@ endif
 #
 # This is an attempt to support generation of multiple binaries
 # in one directory, it assumes everything to compile Foo is in
 # Foo.o (from either Foo.c or Foo.cpp).
 #
 # SIMPLE_PROGRAMS = Foo Bar
 # creates Foo.o Bar.o, links with LIBS to create Foo, Bar.
 #
-$(SIMPLE_PROGRAMS): %$(BIN_SUFFIX): %.$(OBJ_SUFFIX) $(LIBS_DEPS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
+$(SIMPLE_PROGRAMS): %$(BIN_SUFFIX): %.$(OBJ_SUFFIX) $(EXTRA_DEPS) $(GLOBAL_DEPS)
 ifeq (_WINNT,$(GNU_CC)_$(OS_ARCH))
 	$(EXPAND_LD) -nologo -out:$@ -pdb:$(LINK_PDBFILE) $< $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(MOZ_GLUE_PROGRAM_LDFLAGS) $(LIBS) $(EXTRA_LIBS) $(OS_LIBS)
 ifdef MSMANIFEST_TOOL
 	@if test -f $@.manifest; then \
 		mt.exe -NOLOGO -MANIFEST $@.manifest -OUTPUTRESOURCE:$@\;1; \
 		rm -f $@.manifest; \
 	fi
 endif	# MSVC with manifest tool
@@ -852,25 +835,25 @@ else
 endif
 endif
 
 ifdef DTRACE_PROBE_OBJ
 EXTRA_DEPS += $(DTRACE_PROBE_OBJ)
 OBJS += $(DTRACE_PROBE_OBJ)
 endif
 
-$(filter %.$(LIB_SUFFIX),$(LIBRARY)): $(OBJS) $(LOBJS) $(SHARED_LIBRARY_LIBS_DEPS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
+$(filter %.$(LIB_SUFFIX),$(LIBRARY)): $(OBJS) $(LOBJS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
 	$(RM) $(LIBRARY)
 	$(EXPAND_AR) $(AR_FLAGS) $(OBJS) $(LOBJS) $(SHARED_LIBRARY_LIBS)
 	$(RANLIB) $@
 
-$(filter-out %.$(LIB_SUFFIX),$(LIBRARY)): $(filter %.$(LIB_SUFFIX),$(LIBRARY)) $(OBJS) $(LOBJS) $(SHARED_LIBRARY_LIBS_DEPS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
+$(filter-out %.$(LIB_SUFFIX),$(LIBRARY)): $(filter %.$(LIB_SUFFIX),$(LIBRARY)) $(OBJS) $(LOBJS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
 # When we only build a library descriptor, blow out any existing library
 	$(if $(filter %.$(LIB_SUFFIX),$(LIBRARY)),,$(RM) $(REAL_LIBRARY) $(EXPORT_LIBRARY:%=%/$(REAL_LIBRARY)))
-	$(EXPAND_LIBS_GEN) $(OBJS) $(LOBJS) $(SHARED_LIBRARY_LIBS) > $@
+	$(EXPAND_LIBS_GEN) -o $@ $(OBJS) $(LOBJS) $(SHARED_LIBRARY_LIBS)
 
 ifeq ($(OS_ARCH),WINNT)
 $(IMPORT_LIBRARY): $(SHARED_LIBRARY)
 endif
 
 ifeq ($(OS_ARCH),OS2)
 $(DEF_FILE): $(OBJS) $(SHARED_LIBRARY_LIBS)
 	$(RM) $@
@@ -905,17 +888,17 @@ endif
 endif
 endif
 
 # On Darwin (Mac OS X), dwarf2 debugging uses debug info left in .o files,
 # so instead of deleting .o files after repacking them into a dylib, we make
 # symlinks back to the originals. The symlinks are a no-op for stabs debugging,
 # so no need to conditionalize on OS version or debugging format.
 
-$(SHARED_LIBRARY): $(OBJS) $(LOBJS) $(DEF_FILE) $(RESFILE) $(SHARED_LIBRARY_LIBS_DEPS) $(LIBRARY) $(EXTRA_DEPS) $(DSO_LDOPTS_DEPS) $(GLOBAL_DEPS)
+$(SHARED_LIBRARY): $(OBJS) $(LOBJS) $(DEF_FILE) $(RESFILE) $(LIBRARY) $(EXTRA_DEPS) $(GLOBAL_DEPS)
 ifndef INCREMENTAL_LINKER
 	$(RM) $@
 endif
 ifdef DTRACE_LIB_DEPENDENT
 ifndef XP_MACOSX
 	dtrace -G -C -s $(MOZILLA_DTRACE_SRC) -o  $(DTRACE_PROBE_OBJ) $(shell $(EXPAND_LIBS) $(MOZILLA_PROBE_LIBS))
 endif
 	$(EXPAND_MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(LOBJS) $(SUB_SHLOBJS) $(DTRACE_PROBE_OBJ) $(MOZILLA_PROBE_LIBS) $(RESFILE) $(LDFLAGS) $(WRAP_LDFLAGS) $(SHARED_LIBRARY_LIBS) $(EXTRA_DSO_LDOPTS) $(MOZ_GLUE_LDFLAGS) $(OS_LIBS) $(EXTRA_LIBS) $(DEF_FILE) $(SHLIB_LDENDFILE)
--- a/config/tests/unit-expandlibs.py
+++ b/config/tests/unit-expandlibs.py
@@ -31,18 +31,17 @@ config_unix = {
     'DLL_SUFFIX': '.so',
     'IMPORT_LIB_SUFFIX': '',
     'LIBS_DESC_SUFFIX': '.desc',
     'EXPAND_LIBS_LIST_STYLE': 'linkerscript',
 }
 
 config = sys.modules['expandlibs_config'] = imp.new_module('expandlibs_config')
 
-from expandlibs import LibDescriptor, ExpandArgs, relativize
-from expandlibs_deps import ExpandLibsDeps, split_args
+from expandlibs import LibDescriptor, ExpandArgs, relativize, ExpandLibsDeps
 from expandlibs_gen import generate
 from expandlibs_exec import ExpandArgsMore, SectionFinder
 
 def Lib(name):
     return config.LIB_PREFIX + name + config.LIB_SUFFIX
 
 def Obj(name):
     return name + config.OBJ_SUFFIX
@@ -208,22 +207,16 @@ class TestExpandLibsDeps(TestExpandInit)
         self.touch([self.tmpfile('libx', Lib('x'))])
         args = self.arg_files + [self.tmpfile('liby', Lib('y'))]
         self.assertRelEqual(ExpandLibsDeps(args), ExpandArgs(args) + [self.tmpfile('liby', Lib('y') + config.LIBS_DESC_SUFFIX)])
 
         self.touch([self.tmpfile('liby', Lib('y'))])
         args = self.arg_files + [self.tmpfile('liby', Lib('y'))]
         self.assertRelEqual(ExpandLibsDeps(args), ExpandArgs(args))
 
-class TestSplitArgs(unittest.TestCase):
-    def test_split_args(self):
-        self.assertEqual(split_args(['a', '=', 'b', 'c']), {'a': ['b', 'c']})
-        self.assertEqual(split_args(['a', '=', 'b', 'c', ',', 'd', '=', 'e', 'f', 'g', ',', 'h', '=', 'i']),
-                         {'a': ['b', 'c'], 'd': ['e', 'f', 'g'], 'h': ['i']})
-
 class TestExpandArgsMore(TestExpandInit):
     def test_makelist(self):
         '''Test grouping object files in lists'''
         # ExpandArgsMore does the same as ExpandArgs
         with ExpandArgsMore(['foo', '-bar'] + self.arg_files + [self.tmpfile('liby', Lib('y'))]) as args:
             self.assertRelEqual(args, ['foo', '-bar'] + self.files + self.liby_files + self.libx_files) 
 
             # But also has an extra method replacing object files with a list
--- a/js/src/config/config.mk
+++ b/js/src/config/config.mk
@@ -726,19 +726,18 @@ ifdef TIERS
 DIRS += $(foreach tier,$(TIERS),$(tier_$(tier)_dirs))
 STATIC_DIRS += $(foreach tier,$(TIERS),$(tier_$(tier)_staticdirs))
 endif
 
 OPTIMIZE_JARS_CMD = $(PYTHON) $(call core_abspath,$(topsrcdir)/config/optimizejars.py)
 
 CREATE_PRECOMPLETE_CMD = $(PYTHON) $(call core_abspath,$(topsrcdir)/config/createprecomplete.py)
 
-EXPAND_LIBS_DEPS = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config $(topsrcdir)/config/expandlibs_deps.py
-EXPAND_LIBS_EXEC = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config $(topsrcdir)/config/expandlibs_exec.py
-EXPAND_LIBS_GEN = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config $(topsrcdir)/config/expandlibs_gen.py
+EXPAND_LIBS_EXEC = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config $(topsrcdir)/config/expandlibs_exec.py $(if $@,--depend $(MDDEPDIR)/$(basename $(@F)).pp --target $@)
+EXPAND_LIBS_GEN = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config $(topsrcdir)/config/expandlibs_gen.py $(if $@,--depend $(MDDEPDIR)/$(basename $(@F)).pp)
 EXPAND_AR = $(EXPAND_LIBS_EXEC) --extract -- $(AR)
 EXPAND_CC = $(EXPAND_LIBS_EXEC) --uselist -- $(CC)
 EXPAND_CCC = $(EXPAND_LIBS_EXEC) --uselist -- $(CCC)
 EXPAND_LD = $(EXPAND_LIBS_EXEC) --uselist -- $(LD)
 EXPAND_MKSHLIB_ARGS = --uselist
 ifdef SYMBOL_ORDER
 EXPAND_MKSHLIB_ARGS += --symbol-order $(SYMBOL_ORDER)
 endif
--- a/js/src/config/expandlibs.py
+++ b/js/src/config/expandlibs.py
@@ -22,19 +22,29 @@ given a list of files, expandlibs will r
   ${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
+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]
@@ -111,10 +121,18 @@ class ExpandArgs(list):
             with open(arg + conf.LIBS_DESC_SUFFIX, '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 [arg]
 
+class ExpandLibsDeps(ExpandArgs):
+    '''Same as ExpandArgs, but also adds the library descriptor to the list'''
+    def _expand_desc(self, arg):
+        objs = super(ExpandLibsDeps, self)._expand_desc(arg)
+        if os.path.exists(arg + conf.LIBS_DESC_SUFFIX):
+            objs += [relativize(arg + conf.LIBS_DESC_SUFFIX)]
+        return objs
+
 if __name__ == '__main__':
     print " ".join(ExpandArgs(sys.argv[1:]))
deleted file mode 100644
--- a/js/src/config/expandlibs_deps.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# 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_deps.py takes a list of key/value pairs and prints, for each
-key, a list of all dependencies corresponding to the corresponding value.
-Libraries are thus expanded, and their descriptor is also made part of this
-list.
-The list of key/value is passed on the command line in the form
-"var1 = value , var2 = value ..."
-'''
-import sys
-import os
-from expandlibs import ExpandArgs, relativize
-import expandlibs_config as conf
-
-class ExpandLibsDeps(ExpandArgs):
-    def _expand_desc(self, arg):
-        objs = super(ExpandLibsDeps, self)._expand_desc(arg)
-        if os.path.exists(arg + conf.LIBS_DESC_SUFFIX):
-            objs += [relativize(arg + conf.LIBS_DESC_SUFFIX)]
-        return objs
-
-def split_args(args):
-    '''Transforms a list in the form
-    ['var1', '=', 'value', ',', 'var2', '=', 'other', 'value', ',' ...]
-    into a corresponding dict 
-    { 'var1': ['value'],
-      'var2': ['other', 'value'], ... }'''
-
-    result = {}
-    while args:
-        try:
-            i = args.index(',')
-            l = args[:i]
-            args[:i + 1] = []
-        except:
-            l = args
-            args = []
-        if l[1] != '=':
-            raise RuntimeError('Expected "var = value" format')
-        result[l[0]] = l[2:]
-    return result
-
-if __name__ == '__main__':
-    for key, value in split_args(sys.argv[1:]).iteritems():
-        expanded = ExpandLibsDeps(value)
-        if os.sep == '\\':
-            expanded = [o.replace(os.sep, '/') for o in expanded]
-        print "%s = %s" % (key, " ".join(expanded))
--- a/js/src/config/expandlibs_exec.py
+++ b/js/src/config/expandlibs_exec.py
@@ -18,17 +18,17 @@ See https://bugzilla.mozilla.org/show_bu
 
 With the --symbol-order argument, followed by a file name, it will add the
 relevant linker options to change the order in which the linker puts the
 symbols appear in the resulting binary. Only works for ELF targets.
 '''
 from __future__ import with_statement
 import sys
 import os
-from expandlibs import ExpandArgs, relativize, isObject
+from expandlibs import ExpandArgs, relativize, isObject, ensureParentDir, ExpandLibsDeps
 import expandlibs_config as conf
 from optparse import OptionParser
 import subprocess
 import tempfile
 import shutil
 import subprocess
 import re
 
@@ -264,38 +264,59 @@ class SectionFinder(object):
             if len(tmp) > 1 and len(tmp[1]) > 6 and tmp[1][6] in ['O', 'F']:
                 tmp = tmp[1][8:].split()
                 # That gives us ["<section>","<length>", "<symbol>"]
                 syms.append((tmp[-1], tmp[0]))
         return syms
 
 def main():
     parser = OptionParser()
+    parser.add_option("--depend", dest="depend", metavar="FILE",
+        help="generate dependencies for the given execution and store it in the given file")
+    parser.add_option("--target", dest="target", metavar="FILE",
+        help="designate the target for dependencies")
     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("--symbol-order", dest="symbol_order", metavar="FILE",
         help="use the given list of symbols to order symbols in the resulting binary when using with a linker")
 
     (options, args) = parser.parse_args()
 
+    if not options.target:
+        options.depend = False
+    if options.depend:
+        deps = ExpandLibsDeps(args)
+        # Filter out common command wrappers
+        while os.path.basename(deps[0]) in ['ccache', 'distcc']:
+            deps.pop(0)
+        # Remove command
+        deps.pop(0)
     with ExpandArgsMore(args) as args:
         if options.extract:
             args.extract()
         if options.symbol_order:
             args.orderSymbols(options.symbol_order)
         if options.uselist:
             args.makelist()
 
         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()])
             sys.stderr.flush()
-        exit(subprocess.call(args))
+        ret = subprocess.call(args)
+        if ret:
+            exit(ret)
+    if not options.depend:
+        return
+    ensureParentDir(options.depend)
+    with open(options.depend, 'w') as depfile:
+        depfile.write("%s : %s\n" % (options.target, ' '.join(dep for dep in deps if os.path.isfile(dep) and dep != options.target)))
+
 
 if __name__ == '__main__':
     main()
--- a/js/src/config/expandlibs_gen.py
+++ b/js/src/config/expandlibs_gen.py
@@ -1,19 +1,21 @@
 # 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/.
 
 '''Given a list of object files and library names, prints a library
 descriptor to standard output'''
 
+from __future__ import with_statement
 import sys
 import os
 import expandlibs_config as conf
-from expandlibs import LibDescriptor, isObject
+from expandlibs import LibDescriptor, isObject, ensureParentDir, ExpandLibsDeps
+from optparse import OptionParser
 
 def generate(args):
     desc = LibDescriptor()
     for arg in args:
         if isObject(arg):
             if os.path.exists(arg):
                 desc['OBJS'].append(os.path.abspath(arg))
             else:
@@ -21,9 +23,25 @@ def generate(args):
         elif os.path.splitext(arg)[1] == conf.LIB_SUFFIX:
             if os.path.exists(arg) or os.path.exists(arg + conf.LIBS_DESC_SUFFIX):
                 desc['LIBS'].append(os.path.abspath(arg))
             else:
                 raise Exception("File not found: %s" % arg)
     return desc
 
 if __name__ == '__main__':
-    print generate(sys.argv[1:])
+    parser = OptionParser()
+    parser.add_option("--depend", dest="depend", metavar="FILE",
+        help="generate dependencies for the given execution and store it in the given file")
+    parser.add_option("-o", dest="output", metavar="FILE",
+        help="send output to the given file")
+
+    (options, args) = parser.parse_args()
+    if not options.output:
+        raise Exception("Missing option: -o")
+
+    ensureParentDir(options.output)
+    with open(options.output, 'w') as outfile:
+        print >>outfile, generate(args)
+    if options.depend:
+        ensureParentDir(options.depend)
+        with open(options.depend, 'w') as depfile:
+            depfile.write("%s : %s\n" % (options.output, ' '.join(ExpandLibsDeps(args))))
--- a/js/src/config/rules.mk
+++ b/js/src/config/rules.mk
@@ -638,33 +638,16 @@ else # !IS_COMPONENT
 endif # IS_COMPONENT
 endif # EXPORT_LIBRARY
 endif # LIBRARY_NAME
 
 ifneq (,$(filter-out %.$(LIB_SUFFIX),$(SHARED_LIBRARY_LIBS)))
 $(error SHARED_LIBRARY_LIBS must contain .$(LIB_SUFFIX) files only)
 endif
 
-# Create dependencies on static (and shared EXTRA_DSO_LIBS) libraries
-ifneq (,$(strip $(filter %.$(LIB_SUFFIX),$(LIBS) $(EXTRA_DSO_LDOPTS)) $(SHARED_LIBRARY_LIBS) $(EXTRA_DSO_LIBS)))
-$(MDDEPDIR)/libs: Makefile.in
-	@mkdir -p $(MDDEPDIR)
-	@$(EXPAND_LIBS_DEPS) LIBS_DEPS = $(filter %.$(LIB_SUFFIX),$(LIBS)) , \
-	                     SHARED_LIBRARY_LIBS_DEPS = $(SHARED_LIBRARY_LIBS) , \
-	                     DSO_LDOPTS_DEPS = $(EXTRA_DSO_LIBS) $(filter %.$(LIB_SUFFIX), $(EXTRA_DSO_LDOPTS)) > $@
-
-ifneq (,$(wildcard $(MDDEPDIR)/libs))
-include $(MDDEPDIR)/libs
-endif
-
-$(MDDEPDIR)/libs: $(wildcard $(filter %.$(LIBS_DESC_SUFFIX),$(LIBS_DEPS) $(SHARED_LIBRARY_LIBS_DEPS) $(DSO_LDOPTS_DEPS)))
-
-EXTRA_DEPS += $(MDDEPDIR)/libs
-endif
-
 HOST_LIBS_DEPS = $(filter %.$(LIB_SUFFIX),$(HOST_LIBS))
 
 # Dependencies which, if modified, should cause everything to rebuild
 GLOBAL_DEPS += Makefile Makefile.in $(DEPTH)/config/autoconf.mk $(topsrcdir)/config/config.mk
 
 ##############################################
 include $(topsrcdir)/config/makefiles/target_libs.mk
 
@@ -747,17 +730,17 @@ endif
 alltags:
 	$(RM) TAGS
 	find $(topsrcdir) -name dist -prune -o \( -name '*.[hc]' -o -name '*.cp' -o -name '*.cpp' -o -name '*.idl' \) -print | $(TAG_PROGRAM)
 
 #
 # PROGRAM = Foo
 # creates OBJS, links with LIBS to create Foo
 #
-$(PROGRAM): $(PROGOBJS) $(LIBS_DEPS) $(EXTRA_DEPS) $(EXE_DEF_FILE) $(RESFILE) $(GLOBAL_DEPS)
+$(PROGRAM): $(PROGOBJS) $(EXTRA_DEPS) $(EXE_DEF_FILE) $(RESFILE) $(GLOBAL_DEPS)
 	@$(RM) $@.manifest
 ifeq (_WINNT,$(GNU_CC)_$(OS_ARCH))
 	$(EXPAND_LD) -NOLOGO -OUT:$@ -PDB:$(LINK_PDBFILE) $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(MOZ_GLUE_PROGRAM_LDFLAGS) $(PROGOBJS) $(RESFILE) $(LIBS) $(EXTRA_LIBS) $(OS_LIBS)
 ifdef MSMANIFEST_TOOL
 	@if test -f $@.manifest; then \
 		if test -f "$(srcdir)/$@.manifest"; then \
 			echo "Embedding manifest from $(srcdir)/$@.manifest and $@.manifest"; \
 			mt.exe -NOLOGO -MANIFEST "$(win_srcdir)/$@.manifest" $@.manifest -OUTPUTRESOURCE:$@\;1; \
@@ -815,17 +798,17 @@ endif
 #
 # This is an attempt to support generation of multiple binaries
 # in one directory, it assumes everything to compile Foo is in
 # Foo.o (from either Foo.c or Foo.cpp).
 #
 # SIMPLE_PROGRAMS = Foo Bar
 # creates Foo.o Bar.o, links with LIBS to create Foo, Bar.
 #
-$(SIMPLE_PROGRAMS): %$(BIN_SUFFIX): %.$(OBJ_SUFFIX) $(LIBS_DEPS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
+$(SIMPLE_PROGRAMS): %$(BIN_SUFFIX): %.$(OBJ_SUFFIX) $(EXTRA_DEPS) $(GLOBAL_DEPS)
 ifeq (_WINNT,$(GNU_CC)_$(OS_ARCH))
 	$(EXPAND_LD) -nologo -out:$@ -pdb:$(LINK_PDBFILE) $< $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(MOZ_GLUE_PROGRAM_LDFLAGS) $(LIBS) $(EXTRA_LIBS) $(OS_LIBS)
 ifdef MSMANIFEST_TOOL
 	@if test -f $@.manifest; then \
 		mt.exe -NOLOGO -MANIFEST $@.manifest -OUTPUTRESOURCE:$@\;1; \
 		rm -f $@.manifest; \
 	fi
 endif	# MSVC with manifest tool
@@ -852,25 +835,25 @@ else
 endif
 endif
 
 ifdef DTRACE_PROBE_OBJ
 EXTRA_DEPS += $(DTRACE_PROBE_OBJ)
 OBJS += $(DTRACE_PROBE_OBJ)
 endif
 
-$(filter %.$(LIB_SUFFIX),$(LIBRARY)): $(OBJS) $(LOBJS) $(SHARED_LIBRARY_LIBS_DEPS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
+$(filter %.$(LIB_SUFFIX),$(LIBRARY)): $(OBJS) $(LOBJS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
 	$(RM) $(LIBRARY)
 	$(EXPAND_AR) $(AR_FLAGS) $(OBJS) $(LOBJS) $(SHARED_LIBRARY_LIBS)
 	$(RANLIB) $@
 
-$(filter-out %.$(LIB_SUFFIX),$(LIBRARY)): $(filter %.$(LIB_SUFFIX),$(LIBRARY)) $(OBJS) $(LOBJS) $(SHARED_LIBRARY_LIBS_DEPS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
+$(filter-out %.$(LIB_SUFFIX),$(LIBRARY)): $(filter %.$(LIB_SUFFIX),$(LIBRARY)) $(OBJS) $(LOBJS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
 # When we only build a library descriptor, blow out any existing library
 	$(if $(filter %.$(LIB_SUFFIX),$(LIBRARY)),,$(RM) $(REAL_LIBRARY) $(EXPORT_LIBRARY:%=%/$(REAL_LIBRARY)))
-	$(EXPAND_LIBS_GEN) $(OBJS) $(LOBJS) $(SHARED_LIBRARY_LIBS) > $@
+	$(EXPAND_LIBS_GEN) -o $@ $(OBJS) $(LOBJS) $(SHARED_LIBRARY_LIBS)
 
 ifeq ($(OS_ARCH),WINNT)
 $(IMPORT_LIBRARY): $(SHARED_LIBRARY)
 endif
 
 ifeq ($(OS_ARCH),OS2)
 $(DEF_FILE): $(OBJS) $(SHARED_LIBRARY_LIBS)
 	$(RM) $@
@@ -905,17 +888,17 @@ endif
 endif
 endif
 
 # On Darwin (Mac OS X), dwarf2 debugging uses debug info left in .o files,
 # so instead of deleting .o files after repacking them into a dylib, we make
 # symlinks back to the originals. The symlinks are a no-op for stabs debugging,
 # so no need to conditionalize on OS version or debugging format.
 
-$(SHARED_LIBRARY): $(OBJS) $(LOBJS) $(DEF_FILE) $(RESFILE) $(SHARED_LIBRARY_LIBS_DEPS) $(LIBRARY) $(EXTRA_DEPS) $(DSO_LDOPTS_DEPS) $(GLOBAL_DEPS)
+$(SHARED_LIBRARY): $(OBJS) $(LOBJS) $(DEF_FILE) $(RESFILE) $(LIBRARY) $(EXTRA_DEPS) $(GLOBAL_DEPS)
 ifndef INCREMENTAL_LINKER
 	$(RM) $@
 endif
 ifdef DTRACE_LIB_DEPENDENT
 ifndef XP_MACOSX
 	dtrace -G -C -s $(MOZILLA_DTRACE_SRC) -o  $(DTRACE_PROBE_OBJ) $(shell $(EXPAND_LIBS) $(MOZILLA_PROBE_LIBS))
 endif
 	$(EXPAND_MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(LOBJS) $(SUB_SHLOBJS) $(DTRACE_PROBE_OBJ) $(MOZILLA_PROBE_LIBS) $(RESFILE) $(LDFLAGS) $(WRAP_LDFLAGS) $(SHARED_LIBRARY_LIBS) $(EXTRA_DSO_LDOPTS) $(MOZ_GLUE_LDFLAGS) $(OS_LIBS) $(EXTRA_LIBS) $(DEF_FILE) $(SHLIB_LDENDFILE)