From ad2b8b83469a8c483ef7e293bc899f28a0cfea3e Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Wed, 23 Jul 2014 13:33:06 +0900 Subject: [PATCH] Bug 1041936 - Allow static library definitions to depend on shared libraries. r=gps --- build/docs/defining-binaries.rst | 4 + config/rules.mk | 5 ++ .../mozbuild/backend/recursivemake.py | 32 ++++++-- python/mozbuild/mozbuild/frontend/data.py | 6 +- python/mozbuild/mozbuild/frontend/emitter.py | 73 +++++++++++++------ 5 files changed, 89 insertions(+), 31 deletions(-) diff --git a/build/docs/defining-binaries.rst b/build/docs/defining-binaries.rst index bbbbdac46279..e04253aa16bc 100644 --- a/build/docs/defining-binaries.rst +++ b/build/docs/defining-binaries.rst @@ -68,6 +68,10 @@ Note that currently, the build system may not create an actual library for static libraries. It is an implementation detail that shouldn't need to be worried about. +As a special rule, USE_LIBS is allowed to contain references to shared +libraries. In such cases, programs and shared libraries linking this static +library will inherit those shared library dependencies. + Intermediate (Static) Libraries =============================== diff --git a/config/rules.mk b/config/rules.mk index db04e859fc4f..e16a6ac5d26c 100644 --- a/config/rules.mk +++ b/config/rules.mk @@ -136,12 +136,17 @@ endif # ENABLE_TESTS ifndef LIBRARY ifdef REAL_LIBRARY +# Don't build actual static library if a shared library is also built +ifdef FORCE_SHARED_LIB +LIBRARY := $(REAL_LIBRARY).$(LIBS_DESC_SUFFIX) +else # Only build actual library if it is installed in DIST/lib or SDK ifeq (,$(SDK_LIBRARY)$(DIST_INSTALL)$(NO_EXPAND_LIBS)) LIBRARY := $(REAL_LIBRARY).$(LIBS_DESC_SUFFIX) else LIBRARY := $(REAL_LIBRARY) $(REAL_LIBRARY).$(LIBS_DESC_SUFFIX) endif +endif endif # REAL_LIBRARY endif # LIBRARY diff --git a/python/mozbuild/mozbuild/backend/recursivemake.py b/python/mozbuild/mozbuild/backend/recursivemake.py index 31d7cfb29f25..250370ac1ae6 100644 --- a/python/mozbuild/mozbuild/backend/recursivemake.py +++ b/python/mozbuild/mozbuild/backend/recursivemake.py @@ -1145,20 +1145,33 @@ class RecursiveMakeBackend(CommonBackend): backend_file.write('HOST_LIBRARY_NAME = %s\n' % libdef.basename) def _process_linked_libraries(self, obj, backend_file): - topobjdir = mozpath.normsep(obj.topobjdir) - for lib in obj.linked_libraries: + def recurse_lib(lib): + for l in lib.linked_libraries: + if isinstance(l, StaticLibrary): + for q in recurse_lib(l): + yield q + else: + yield l + + def pretty_relpath(lib): # If this is an external objdir (i.e., comm-central), use the other # directory instead of $(DEPTH). if lib.objdir.startswith(topobjdir + '/'): - relpath = '$(DEPTH)/%s' % mozpath.relpath(lib.objdir, topobjdir) + return '$(DEPTH)/%s' % mozpath.relpath(lib.objdir, topobjdir) else: - relpath = mozpath.relpath(lib.objdir, obj.objdir) + return mozpath.relpath(lib.objdir, obj.objdir) + + topobjdir = mozpath.normsep(obj.topobjdir) + for lib in obj.linked_libraries: + relpath = pretty_relpath(lib) if isinstance(obj, Library): if isinstance(lib, StaticLibrary): backend_file.write_once('SHARED_LIBRARY_LIBS += %s/%s\n' % (relpath, lib.lib_name)) - else: - assert isinstance(obj, SharedLibrary) + for l in recurse_lib(lib): + backend_file.write_once('EXTRA_DSO_LDOPTS += %s/%s\n' + % (pretty_relpath(l), l.import_name)) + elif isinstance(obj, SharedLibrary): assert lib.variant != lib.COMPONENT backend_file.write_once('EXTRA_DSO_LDOPTS += %s/%s\n' % (relpath, lib.import_name)) @@ -1166,6 +1179,13 @@ class RecursiveMakeBackend(CommonBackend): if isinstance(lib, StaticLibrary): backend_file.write_once('LIBS += %s/%s\n' % (relpath, lib.lib_name)) + for l in recurse_lib(lib): + backend_file.write_once('LIBS += %s/%s\n' + % (pretty_relpath(l), l.import_name)) + elif isinstance(obj, SharedLibrary): + assert lib.variant != lib.COMPONENT + backend_file.write_once('EXTRA_DSO_LDOPTS += %s/%s\n' + % (relpath, lib.import_name)) else: assert lib.variant != lib.COMPONENT backend_file.write_once('LIBS += %s/%s\n' diff --git a/python/mozbuild/mozbuild/frontend/data.py b/python/mozbuild/mozbuild/frontend/data.py index b8d09e066631..8cb50411c534 100644 --- a/python/mozbuild/mozbuild/frontend/data.py +++ b/python/mozbuild/mozbuild/frontend/data.py @@ -348,7 +348,7 @@ class Linkable(SandboxDerived): if obj.KIND != self.KIND: raise LinkageWrongKindError('%s != %s' % (obj.KIND, self.KIND)) self.linked_libraries.append(obj) - obj.refcount += 1 + obj.refs.append(self) class BaseProgram(Linkable): @@ -402,7 +402,7 @@ class BaseLibrary(Linkable): 'basename', 'lib_name', 'import_name', - 'refcount', + 'refs', ) def __init__(self, sandbox, basename): @@ -417,7 +417,7 @@ class BaseLibrary(Linkable): ) self.import_name = self.lib_name - self.refcount = 0 + self.refs = [] class Library(BaseLibrary): diff --git a/python/mozbuild/mozbuild/frontend/emitter.py b/python/mozbuild/mozbuild/frontend/emitter.py index 9da7e7a16ac0..0cafcf3e0933 100644 --- a/python/mozbuild/mozbuild/frontend/emitter.py +++ b/python/mozbuild/mozbuild/frontend/emitter.py @@ -93,6 +93,7 @@ class TreeMetadataEmitter(LoggingMixin): self._libs = OrderedDefaultDict(list) self._binaries = OrderedDict() self._linkage = [] + self._static_linking_shared = set() def emit(self, output): """Convert the BuildReader output into data structures. @@ -139,22 +140,56 @@ class TreeMetadataEmitter(LoggingMixin): yield ReaderSummary(file_count, sandbox_execution_time, emitter_time) def _emit_libs_derived(self, sandboxes): - for sandbox, obj, variable in self._linkage: - self._link_libraries(sandbox, obj, variable) - + # First do FINAL_LIBRARY linkage. for lib in (l for libs in self._libs.values() for l in libs): if not isinstance(lib, StaticLibrary) or not lib.link_into: continue if lib.link_into not in self._libs: - raise Exception('FINAL_LIBRARY in %s (%s) does not match any ' - 'LIBRARY_NAME' % (lib.objdir, lib.link_info)) + raise SandboxValidationError( + 'FINAL_LIBRARY ("%s") does not match any LIBRARY_NAME' + % lib.link_into, sandboxes[lib.objdir]) candidates = self._libs[lib.link_into] - if len(candidates) > 1: - raise Exception('FINAL_LIBRARY in %s (%s) matches a ' - 'LIBRARY_NAME defined in multiple places (%s)' % - (lib.objdir, lib.link_into, - ', '.join(l.objdir for l in candidates))) - candidates[0].link_library(lib) + + # When there are multiple candidates, but all are in the same + # directory and have a different type, we want all of them to + # have the library linked. The typical usecase is when building + # both a static and a shared library in a directory, and having + # that as a FINAL_LIBRARY. + if len(set(type(l) for l in candidates)) == len(candidates) and \ + len(set(l.objdir for l in candidates)) == 1: + for c in candidates: + c.link_library(lib) + else: + raise SandboxValidationError( + 'FINAL_LIBRARY ("%s") matches a LIBRARY_NAME defined in ' + 'multiple places:\n %s' % (lib.link_into, + '\n '.join(l.objdir for l in candidates)), + sandboxes[lib.objdir]) + + # Next, USE_LIBS linkage. + for sandbox, obj, variable in self._linkage: + self._link_libraries(sandbox, obj, variable) + + def recurse_refs(lib): + for o in lib.refs: + yield o + if isinstance(o, StaticLibrary): + for q in recurse_refs(o): + yield q + + # Check that all static libraries refering shared libraries in + # USE_LIBS are linked into a shared library or program. + for lib in self._static_linking_shared: + if all(isinstance(o, StaticLibrary) for o in recurse_refs(lib)): + shared_libs = sorted(l.basename for l in lib.linked_libraries + if isinstance(l, SharedLibrary)) + raise SandboxValidationError( + 'The static "%s" library is not used in a shared library ' + 'or a program, but USE_LIBS contains the following shared ' + 'library names:\n %s\n\nMaybe you can remove the ' + 'static "%s" library?' % (lib.basename, + '\n '.join(shared_libs), lib.basename), + sandboxes[lib.objdir]) def recurse_libs(lib): for obj in lib.linked_libraries: @@ -164,6 +199,7 @@ class TreeMetadataEmitter(LoggingMixin): for q in recurse_libs(obj): yield q + sent_passthru = set() for lib in (l for libs in self._libs.values() for l in libs): # For all root libraries (i.e. libraries that don't have a # FINAL_LIBRARY), record, for each static library it links @@ -171,6 +207,9 @@ class TreeMetadataEmitter(LoggingMixin): if isinstance(lib, Library): if isinstance(lib, SharedLibrary) or not lib.link_into: for p in recurse_libs(lib): + if p in sent_passthru: + continue + sent_passthru.add(p) passthru = VariablePassthru(sandboxes[p]) passthru.variables['FINAL_LIBRARY'] = lib.basename yield passthru @@ -261,13 +300,7 @@ class TreeMetadataEmitter(LoggingMixin): elif isinstance(obj, StaticLibrary) and isinstance(candidates[0], SharedLibrary): - raise SandboxValidationError( - '%s contains "%s", but there is only a shared "%s" ' - 'in %s, and "%s" is built static. You may want to ' - 'add FORCE_STATIC_LIB=True in %s/moz.build, or remove ' - '"%s" from USE_LIBS.' % (variable, path, name, - candidates[0].relobjdir, obj.basename, - candidates[0].relobjdir, path), sandbox) + self._static_linking_shared.add(obj) obj.link_library(candidates[0]) def emit_from_sandbox(self, sandbox): @@ -559,10 +592,6 @@ class TreeMetadataEmitter(LoggingMixin): raise SandboxValidationError( 'IS_FRAMEWORK implies FORCE_SHARED_LIB. ' 'Please remove the latter.', sandbox) - if static_lib: - raise SandboxValidationError( - 'IS_FRAMEWORK conflicts with FORCE_STATIC_LIB. ' - 'Please remove one.', sandbox) if soname: raise SandboxValidationError( 'IS_FRAMEWORK conflicts with SONAME. '