--- a/python/mozbuild/mozbuild/backend/tup.py
+++ b/python/mozbuild/mozbuild/backend/tup.py
@@ -29,17 +29,21 @@ from ..frontend.data import (
FinalTargetPreprocessedFiles,
GeneratedFile,
GeneratedSources,
HostDefines,
HostSources,
JARManifest,
ObjdirFiles,
PerSourceFlag,
+ Program,
+ HostProgram,
+ SharedLibrary,
Sources,
+ StaticLibrary,
VariablePassthru,
)
from ..util import (
FileAvoidWrite,
expand_variables,
)
from ..frontend.context import (
AbsolutePath,
@@ -57,21 +61,25 @@ class BackendTupfile(object):
self.relobjdir = mozpath.relpath(objdir, topobjdir)
self.environment = environment
self.name = mozpath.join(objdir, 'Tupfile')
self.rules_included = False
self.shell_exported = False
self.defines = []
self.host_defines = []
self.delayed_generated_files = []
+ self.delayed_installed_files = []
self.per_source_flags = defaultdict(list)
self.local_flags = defaultdict(list)
self.sources = defaultdict(list)
self.host_sources = defaultdict(list)
self.variables = {}
+ self.static_lib = None
+ self.shared_lib = None
+ self.program = None
self.fh = FileAvoidWrite(self.name, capture_diff=True)
self.fh.write('# THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT.\n')
self.fh.write('\n')
def write(self, buf):
self.fh.write(buf)
@@ -188,16 +196,28 @@ class TupOnly(CommonBackend, PartialBack
# will be built before any rules that list this as an input.
self._installed_idls = '$(MOZ_OBJ_ROOT)/<installed-idls>'
self._installed_files = '$(MOZ_OBJ_ROOT)/<installed-files>'
# The preprocessor including source-repo.h and buildid.h creates
# dependencies that aren't specified by moz.build and cause errors
# in Tup. Express these as a group dependency.
self._early_generated_files = '$(MOZ_OBJ_ROOT)/<early-generated-files>'
+ # application.ini.h is a special case since we need to process
+ # the FINAL_TARGET_PP_FILES for application.ini before running
+ # the GENERATED_FILES script, and tup doesn't handle the rules
+ # out of order. Similarly, dependentlibs.list uses libxul as
+ # an input, so must be written after the rule for libxul.
+ self._delayed_files = (
+ 'application.ini.h',
+ 'dependentlibs.list',
+ 'dependentlibs.list.gtest'
+ )
+
+
def _get_backend_file(self, relobjdir):
objdir = mozpath.normpath(mozpath.join(self.environment.topobjdir, relobjdir))
if objdir not in self._backend_files:
self._backend_files[objdir] = \
BackendTupfile(objdir, self.environment,
self.environment.topsrcdir, self.environment.topobjdir)
return self._backend_files[objdir]
@@ -207,16 +227,139 @@ class TupOnly(CommonBackend, PartialBack
def _py_action(self, action):
cmd = [
'$(PYTHON)',
'-m',
'mozbuild.action.%s' % action,
]
return cmd
+ def _lib_paths(self, objdir, libs):
+ return [mozpath.relpath(mozpath.join(l.objdir, l.import_name), objdir)
+ for l in libs]
+
+ def _gen_shared_library(self, backend_file):
+ if backend_file.shared_lib.name == 'libxul.so':
+ # This will fail to link currently due to missing rust symbols.
+ return
+
+ if backend_file.shared_lib.cxx_link:
+ mkshlib = (
+ [backend_file.environment.substs['CXX']] +
+ backend_file.local_flags['CXX_LDFLAGS']
+ )
+ else:
+ mkshlib = (
+ [backend_file.environment.substs['CC']] +
+ backend_file.local_flags['C_LDFLAGS']
+ )
+
+ mkshlib += (
+ backend_file.environment.substs['DSO_PIC_CFLAGS'] +
+ [backend_file.environment.substs['DSO_LDOPTS']] +
+ ['-Wl,-h,%s' % backend_file.shared_lib.soname] +
+ ['-o', backend_file.shared_lib.lib_name]
+ )
+
+ objs, _, shared_libs, os_libs, static_libs = self._expand_libs(backend_file.shared_lib)
+ static_libs = self._lib_paths(backend_file.objdir, static_libs)
+ shared_libs = self._lib_paths(backend_file.objdir, shared_libs)
+
+ list_file_name = '%s.list' % backend_file.shared_lib.name.replace('.', '_')
+ list_file = self._make_list_file(backend_file.objdir, objs, list_file_name)
+
+ inputs = objs + static_libs + shared_libs
+ if any(i.endswith('libxul.so') for i in inputs):
+ # Don't attempt to link anything that depends on libxul.
+ return
+
+ symbols_file = []
+ if backend_file.shared_lib.symbols_file:
+ inputs.append(backend_file.shared_lib.symbols_file)
+ # TODO: Assumes GNU LD
+ symbols_file = ['-Wl,--version-script,%s' % backend_file.shared_lib.symbols_file]
+
+ cmd = (
+ mkshlib +
+ [list_file] +
+ backend_file.local_flags['LDFLAGS'] +
+ static_libs +
+ shared_libs +
+ symbols_file +
+ [backend_file.environment.substs['OS_LIBS']] +
+ os_libs
+ )
+ backend_file.rule(
+ cmd=cmd,
+ inputs=inputs,
+ outputs=[backend_file.shared_lib.lib_name],
+ display='LINK %o'
+ )
+
+
+ def _gen_program(self, backend_file):
+ cc_or_cxx = 'CXX' if backend_file.program.cxx_link else 'CC'
+ objs, _, shared_libs, os_libs, static_libs = self._expand_libs(backend_file.program)
+ static_libs = self._lib_paths(backend_file.objdir, static_libs)
+ shared_libs = self._lib_paths(backend_file.objdir, shared_libs)
+
+ inputs = objs + static_libs + shared_libs
+ if any(i.endswith('libxul.so') for i in inputs):
+ # Don't attempt to link anything that depends on libxul.
+ return
+
+ list_file_name = '%s.list' % backend_file.program.name.replace('.', '_')
+ list_file = self._make_list_file(backend_file.objdir, objs, list_file_name)
+
+ outputs = [mozpath.relpath(backend_file.program.output_path.full_path,
+ backend_file.objdir)]
+ cmd = (
+ [backend_file.environment.substs[cc_or_cxx], '-o', '%o'] +
+ backend_file.local_flags['CXX_LDFLAGS'] +
+ [list_file] +
+ backend_file.local_flags['LDFLAGS'] +
+ static_libs +
+ [backend_file.environment.substs['MOZ_PROGRAM_LDFLAGS']] +
+ shared_libs +
+ [backend_file.environment.substs['OS_LIBS']] +
+ os_libs
+ )
+ backend_file.rule(
+ cmd=cmd,
+ inputs=inputs,
+ outputs=outputs,
+ display='LINK %o'
+ )
+
+
+ def _gen_static_library(self, backend_file):
+ ar = [
+ backend_file.environment.substs['AR'],
+ backend_file.environment.substs['AR_FLAGS'].replace('$@', '%o')
+ ]
+
+ objs, _, shared_libs, _, static_libs = self._expand_libs(backend_file.static_lib)
+ static_libs = self._lib_paths(backend_file.objdir, static_libs)
+ shared_libs = self._lib_paths(backend_file.objdir, shared_libs)
+
+ inputs = objs + static_libs
+
+ cmd = (
+ ar +
+ inputs
+ )
+
+ backend_file.rule(
+ cmd=cmd,
+ inputs=inputs,
+ outputs=[backend_file.static_lib.name],
+ display='AR %o'
+ )
+
+
def consume_object(self, obj):
"""Write out build files necessary to build with tup."""
if not isinstance(obj, ContextDerived):
return False
consumed = CommonBackend.consume_object(self, obj)
if consumed:
@@ -229,21 +372,17 @@ class TupOnly(CommonBackend, PartialBack
if self.environment.is_artifact_build:
skip_files = self._compile_env_gen_files
for f in obj.outputs:
if any(mozpath.match(f, p) for p in skip_files):
return False
- if 'application.ini.h' in obj.outputs:
- # application.ini.h is a special case since we need to process
- # the FINAL_TARGET_PP_FILES for application.ini before running
- # the GENERATED_FILES script, and tup doesn't handle the rules
- # out of order.
+ if any([f in obj.outputs for f in self._delayed_files]):
backend_file.delayed_generated_files.append(obj)
else:
self._process_generated_file(backend_file, obj)
elif (isinstance(obj, ChromeManifestEntry) and
obj.install_target.startswith('dist/bin')):
top_level = mozpath.join(obj.install_target, 'chrome.manifest')
if obj.path != top_level:
entry = 'manifest %s' % mozpath.relpath(obj.path,
@@ -265,16 +404,24 @@ class TupOnly(CommonBackend, PartialBack
elif isinstance(obj, ComputedFlags):
self._process_computed_flags(obj, backend_file)
elif isinstance(obj, (Sources, GeneratedSources)):
backend_file.sources[obj.canonical_suffix].extend(obj.files)
elif isinstance(obj, HostSources):
backend_file.host_sources[obj.canonical_suffix].extend(obj.files)
elif isinstance(obj, VariablePassthru):
backend_file.variables = obj.variables
+ elif isinstance(obj, StaticLibrary):
+ backend_file.static_lib = obj
+ elif isinstance(obj, SharedLibrary):
+ backend_file.shared_lib = obj
+ elif isinstance(obj, HostProgram):
+ pass
+ elif isinstance(obj, Program):
+ backend_file.program = obj
# The top-level Makefile.in still contains our driver target and some
# things related to artifact builds, so as a special case ensure the
# make backend generates a Makefile there.
if obj.objdir == self.environment.topobjdir:
return False
return True
@@ -285,19 +432,28 @@ class TupOnly(CommonBackend, PartialBack
# The approach here is similar to fastermake.py, but we
# simply write out the resulting files here.
for target, entries in self._manifest_entries.iteritems():
with self._write_file(mozpath.join(self.environment.topobjdir,
target)) as fh:
fh.write(''.join('%s\n' % e for e in sorted(entries)))
for objdir, backend_file in sorted(self._backend_files.items()):
+ backend_file.gen_sources_rules([self._installed_files])
+ for condition, gen_method in ((backend_file.shared_lib, self._gen_shared_library),
+ (backend_file.static_lib and backend_file.static_lib.no_expand_lib,
+ self._gen_static_library),
+ (backend_file.program, self._gen_program)):
+ if condition:
+ backend_file.export_shell()
+ gen_method(backend_file)
for obj in backend_file.delayed_generated_files:
self._process_generated_file(backend_file, obj)
- backend_file.gen_sources_rules([self._installed_files])
+ for path, output in backend_file.delayed_installed_files:
+ backend_file.symlink_rule(path, output=output)
with self._write_file(fh=backend_file):
pass
with self._write_file(mozpath.join(self.environment.topobjdir, 'Tuprules.tup')) as fh:
acdefines_flags = ' '.join(['-D%s=%s' % (name, shell_quote(value))
for (name, value) in sorted(self.environment.acdefines.iteritems())])
# TODO: AB_CD only exists in Makefiles at the moment.
acdefines_flags += ' -DAB_CD=en-US'
@@ -431,18 +587,22 @@ class TupOnly(CommonBackend, PartialBack
# We're not generating files in these directories yet, so
# don't attempt to install files generated from them.
if f.context.relobjdir not in ('layout/style/test',
'toolkit/library',
'js/src/shell'):
output = mozpath.join('$(MOZ_OBJ_ROOT)', target, path,
f.target_basename)
gen_backend_file = self._get_backend_file(f.context.relobjdir)
- gen_backend_file.symlink_rule(f.full_path, output=output,
- output_group=self._installed_files)
+ if f.target_basename in self._delayed_files:
+ gen_backend_file.delayed_installed_files.append((f.full_path, output))
+ else:
+ gen_backend_file.symlink_rule(f.full_path, output=output,
+ output_group=self._installed_files)
+
def _process_final_target_pp_files(self, obj, backend_file):
for i, (path, files) in enumerate(obj.files.walk()):
for f in files:
self._preprocess(backend_file, f.full_path,
destdir=mozpath.join(self.environment.topobjdir, obj.install_target, path),
target=f.target_basename)