align changes from rmottola/Arctic-Fox (rev e67e868c) to our tree Part 2: mozbuild changes

This commit is contained in:
2020-01-12 15:03:20 +08:00
parent 17f5e5203e
commit 60cea5c35f
83 changed files with 841 additions and 542 deletions
@@ -20,6 +20,8 @@ def main(argv):
add_help=False)
parser.add_argument('python_script', metavar='python-script', type=str,
help='The Python script to run')
parser.add_argument('method_name', metavar='method-name', type=str,
help='The method of the script to invoke')
parser.add_argument('output_file', metavar='output-file', type=str,
help='The file to generate')
parser.add_argument('additional_arguments', metavar='arg', nargs='*',
@@ -31,15 +33,16 @@ def main(argv):
with open(script, 'r') as fh:
module = imp.load_module('script', fh, script,
('.py', 'r', imp.PY_SOURCE))
if not hasattr(module, 'main'):
print('Error: script "{0}" is missing a main method'.format(script),
method = args.method_name
if not hasattr(module, method):
print('Error: script "{0}" is missing a {1} method'.format(script, method),
file=sys.stderr)
return 1
ret = 1
try:
with FileAvoidWrite(args.output_file) as output:
ret = module.main(output, *args.additional_arguments)
ret = module.__dict__[method](output, *args.additional_arguments)
except IOError as e:
print('Error opening file "{0}"'.format(e.filename), file=sys.stderr)
traceback.print_exc()
@@ -69,6 +69,77 @@ from ..util import (
)
from ..makeutil import Makefile
MOZBUILD_VARIABLES = [
'ANDROID_GENERATED_RESFILES',
'ANDROID_RES_DIRS',
'ASFLAGS',
'CMSRCS',
'CMMSRCS',
'CPP_UNIT_TESTS',
'DIRS',
'DIST_INSTALL',
'EXTRA_DSO_LDOPTS',
'EXTRA_JS_MODULES',
'EXTRA_PP_COMPONENTS',
'EXTRA_PP_JS_MODULES',
'FORCE_SHARED_LIB',
'FORCE_STATIC_LIB',
'FINAL_LIBRARY',
'HOST_CSRCS',
'HOST_CMMSRCS',
'HOST_EXTRA_LIBS',
'HOST_LIBRARY_NAME',
'HOST_PROGRAM',
'HOST_SIMPLE_PROGRAMS',
'IS_COMPONENT',
'JAR_MANIFEST',
'JAVA_JAR_TARGETS',
'LD_VERSION_SCRIPT',
'LIBRARY_NAME',
'LIBS',
'MAKE_FRAMEWORK',
'MODULE',
'MSVC_ENABLE_PGO',
'NO_DIST_INSTALL',
'NO_EXPAND_LIBS',
'OS_LIBS',
'PARALLEL_DIRS',
'PREF_JS_EXPORTS',
'PROGRAM',
'PYTHON_UNIT_TESTS',
'RESOURCE_FILES',
'SDK_HEADERS',
'SDK_LIBRARY',
'SHARED_LIBRARY_LIBS',
'SHARED_LIBRARY_NAME',
'SIMPLE_PROGRAMS',
'SONAME',
'STATIC_LIBRARY_NAME',
'TEST_DIRS',
'TOOL_DIRS',
'XPCSHELL_TESTS',
'XPIDL_MODULE',
]
DEPRECATED_VARIABLES = [
'ANDROID_RESFILES',
'EXPORT_LIBRARY',
'EXTRA_LIBS',
'HOST_LIBS',
'LIBXUL_LIBRARY',
'MOCHITEST_A11Y_FILES',
'MOCHITEST_BROWSER_FILES',
'MOCHITEST_BROWSER_FILES_PARTS',
'MOCHITEST_CHROME_FILES',
'MOCHITEST_FILES',
'MOCHITEST_FILES_PARTS',
'MOCHITEST_METRO_FILES',
'MOCHITEST_ROBOCOP_FILES',
'SHORT_LIBNAME',
'TESTING_JS_MODULES',
'TESTING_JS_MODULE_DIR',
]
class BackendMakeFile(object):
"""Represents a generated backend.mk file.
@@ -411,11 +482,12 @@ class RecursiveMakeBackend(CommonBackend):
backend_file.write('GENERATED_FILES += %s\n' % obj.output)
if obj.script:
backend_file.write("""{output}: {script}{inputs}
\t$(call py_action,file_generate,{script} {output}{inputs})
\t$(call py_action,file_generate,{script} {method} {output}{inputs})
""".format(output=obj.output,
inputs=' ' + ' '.join(obj.inputs) if obj.inputs else '',
script=obj.script))
script=obj.script,
method=obj.method))
elif isinstance(obj, TestHarnessFiles):
self._process_test_harness_files(obj, backend_file)
@@ -627,6 +699,18 @@ class RecursiveMakeBackend(CommonBackend):
rule = makefile.create_rule(['$(all_absolute_unified_files)'])
rule.add_dependencies(['$(CURDIR)/%: %'])
def _check_blacklisted_variables(self, makefile_in, makefile_content):
for x in MOZBUILD_VARIABLES:
if re.search(r'[^#]\b%s\s*[:?+]?=' % x, makefile_content, re.M):
raise Exception('Variable %s is defined in %s. It should '
'only be defined in moz.build files.' % (x, makefile_in))
for x in DEPRECATED_VARIABLES:
if re.search(r'[^#]\b%s\s*[:?+]?=' % x, makefile_content, re.M):
raise Exception('Variable %s is defined in %s. This variable '
'has been deprecated. It does nothing. It must be removed '
'in order to build.' % (x, makefile_in))
def consume_finished(self):
CommonBackend.consume_finished(self)
@@ -674,6 +758,10 @@ class RecursiveMakeBackend(CommonBackend):
self._no_skip['tools'].add(mozpath.relpath(objdir,
self.environment.topobjdir))
# Detect any Makefile.ins that contain variables on the
# moz.build-only list
self._check_blacklisted_variables(makefile_in, content)
self._fill_root_mk()
# Write out a dependency file used to determine whether a config.status
@@ -1114,6 +1202,8 @@ INSTALL_TARGETS += %(prefix)s
backend_file.write('REAL_LIBRARY := %s\n' % libdef.lib_name)
if libdef.is_sdk:
backend_file.write('SDK_LIBRARY := %s\n' % libdef.import_name)
if libdef.no_expand_lib:
backend_file.write('NO_EXPAND_LIBS := 1\n')
def _process_host_library(self, libdef, backend_file):
backend_file.write('HOST_LIBRARY_NAME = %s\n' % libdef.basename)
+61 -4
View File
@@ -268,6 +268,10 @@ class Context(KeyedDefaultDict):
class TemplateContext(Context):
def __init__(self, template=None, allowed_variables={}, config=None):
self.template = template
super(TemplateContext, self).__init__(allowed_variables, config)
def _validate(self, key, value):
return Context._validate(self, key, value, True)
@@ -314,6 +318,21 @@ class FinalTargetValue(ContextDerivedValue, unicode):
return unicode.__new__(cls, value)
def Enum(*values):
assert len(values)
default = values[0]
class EnumClass(object):
def __new__(cls, value=None):
if value is None:
return default
if value in values:
return value
raise ValueError('Invalid value. Allowed values are: %s'
% ', '.join(repr(v) for v in values))
return EnumClass
class SourcePath(ContextDerivedValue, UserString):
"""Stores and resolves a source path relative to a given context
@@ -628,6 +647,14 @@ VARIABLES = {
supported for passing to scripts, and that all arguments provided
to the script should be filenames relative to the directory in which
the moz.build file is located.
To enable using the same script for generating multiple files with
slightly different non-filename parameters, alternative entry points
into ``script`` can be specified::
GENERATED_FILES += ['bar.c']
bar = GENERATED_FILES['bar.c']
bar.script = 'generate.py:make_bar'
""", 'export'),
'DEFINES': (OrderedDict, dict,
@@ -1073,11 +1100,17 @@ VARIABLES = {
ends with ``HOST_BIN_SUFFIX``, ``HOST_PROGRAM`` will remain unchanged.
""", None),
'NO_DIST_INSTALL': (bool, bool,
"""Disable installing certain files into the distribution directory.
'DIST_INSTALL': (Enum(None, False, True), bool,
"""Whether to install certain files into the dist directory.
If present, some files defined by other variables won't be
distributed/shipped with the produced build.
By default, some files types are installed in the dist directory, and
some aren't. Set this variable to True to force the installation of
some files that wouldn't be installed by default. Set this variable to
False to force to not install some files that would be installed by
default.
This is confusing for historical reasons, but eventually, the behavior
will be made explicit.
""", None),
'JAR_MANIFESTS': (StrictOrderingOnAppendList, list,
@@ -1318,6 +1351,15 @@ VARIABLES = {
appear in the moz.build file.
""", None),
'ASFLAGS': (List, list,
"""Flags passed to the assembler for all of the assembly source files
declared in this directory.
Note that the ordering of flags matters here; these flags will be
added to the assembler's command line in the same order as they
appear in the moz.build file.
""", None),
'LDFLAGS': (List, list,
"""Flags passed to the linker when linking all of the libraries and
executables declared in this directory.
@@ -1360,6 +1402,11 @@ VARIABLES = {
the path(s) with a '/' character and a '!' character, respectively::
TEST_HARNESS_FILES.path += ['/build/bar.py', '!quux.py']
""", 'libs'),
'NO_EXPAND_LIBS': (bool, bool,
"""Forces to build a real static library, and no corresponding fake
library.
""", None),
}
# Sanity check: we don't want any variable above to have a list as storage type.
@@ -1728,6 +1775,16 @@ DEPRECATION_HINTS = {
'TEST_TOOL_DIRS': 'Please use the TEST_DIRS variable instead.',
'PARALLEL_DIRS': 'Please use the DIRS variable instead.',
'NO_DIST_INSTALL': '''
Please use
DIST_INSTALL = False
instead of
NO_DIST_INSTALL = True
''',
}
# Make sure that all template variables have a deprecation hint.
+7 -3
View File
@@ -502,12 +502,14 @@ class StaticLibrary(Library):
"""Context derived container object for a static library"""
__slots__ = (
'link_into',
'no_expand_lib',
)
def __init__(self, context, basename, real_name=None, is_sdk=False,
link_into=None):
link_into=None, no_expand_lib=False):
Library.__init__(self, context, basename, real_name, is_sdk)
self.link_into = link_into
self.no_expand_lib = no_expand_lib
class SharedLibrary(Library):
@@ -840,7 +842,7 @@ class InstallationTarget(ContextDerived):
self.xpiname = context.get('XPI_NAME', '')
self.subdir = context.get('DIST_SUBDIR', '')
self.target = context['FINAL_TARGET']
self.enabled = not context.get('NO_DIST_INSTALL', False)
self.enabled = context['DIST_INSTALL'] is not False
def is_custom(self):
"""Returns whether or not the target is not derived from the default
@@ -872,13 +874,15 @@ class GeneratedFile(ContextDerived):
__slots__ = (
'script',
'method',
'output',
'inputs',
)
def __init__(self, context, script, output, inputs):
def __init__(self, context, script, method, output, inputs):
ContextDerived.__init__(self, context)
self.script = script
self.method = method
self.output = output
self.inputs = inputs
+428 -368
View File
@@ -78,6 +78,7 @@ from .reader import SandboxValidationError
from .context import (
Context,
SubContext,
TemplateContext,
)
@@ -354,334 +355,7 @@ class TreeMetadataEmitter(LoggingMixin):
else:
return ExternalSharedLibrary(context, name)
def emit_from_context(self, context):
"""Convert a Context to tree metadata objects.
This is a generator of mozbuild.frontend.data.ContextDerived instances.
"""
# We only want to emit an InstallationTarget if one of the consulted
# variables is defined. Later on, we look up FINAL_TARGET, which has
# the side-effect of populating it. So, we need to do this lookup
# early.
if any(k in context for k in ('FINAL_TARGET', 'XPI_NAME', 'DIST_SUBDIR')):
yield InstallationTarget(context)
# We always emit a directory traversal descriptor. This is needed by
# the recursive make backend.
for o in self._emit_directory_traversal_from_context(context): yield o
for path in context['CONFIGURE_SUBST_FILES']:
yield self._create_substitution(ConfigFileSubstitution, context,
path)
for path in context['CONFIGURE_DEFINE_FILES']:
yield self._create_substitution(HeaderFileSubstitution, context,
path)
# XPIDL source files get processed and turned into .h and .xpt files.
# If there are multiple XPIDL files in a directory, they get linked
# together into a final .xpt, which has the name defined by
# XPIDL_MODULE.
xpidl_module = context['XPIDL_MODULE']
if context['XPIDL_SOURCES'] and not xpidl_module:
raise SandboxValidationError('XPIDL_MODULE must be defined if '
'XPIDL_SOURCES is defined.', context)
if xpidl_module and not context['XPIDL_SOURCES']:
raise SandboxValidationError('XPIDL_MODULE cannot be defined '
'unless there are XPIDL_SOURCES', context)
if context['XPIDL_SOURCES'] and context['NO_DIST_INSTALL']:
self.log(logging.WARN, 'mozbuild_warning', dict(
path=context.main_path),
'{path}: NO_DIST_INSTALL has no effect on XPIDL_SOURCES.')
for idl in context['XPIDL_SOURCES']:
yield XPIDLFile(context, mozpath.join(context.srcdir, idl),
xpidl_module)
for symbol in ('SOURCES', 'HOST_SOURCES', 'UNIFIED_SOURCES'):
for src in (context[symbol] or []):
if not os.path.exists(mozpath.join(context.srcdir, src)):
raise SandboxValidationError('File listed in %s does not '
'exist: \'%s\'' % (symbol, src), context)
# Proxy some variables as-is until we have richer classes to represent
# them. We should aim to keep this set small because it violates the
# desired abstraction of the build definition away from makefiles.
passthru = VariablePassthru(context)
varlist = [
'ANDROID_GENERATED_RESFILES',
'ANDROID_RES_DIRS',
'DISABLE_STL_WRAPPING',
'EXTRA_ASSEMBLER_FLAGS',
'EXTRA_COMPILE_FLAGS',
'EXTRA_COMPONENTS',
'EXTRA_DSO_LDOPTS',
'EXTRA_PP_COMPONENTS',
'FAIL_ON_WARNINGS',
'USE_STATIC_LIBS',
'IS_GYP_DIR',
'MSVC_ENABLE_PGO',
'NO_DIST_INSTALL',
'PYTHON_UNIT_TESTS',
'RCFILE',
'RESFILE',
'RCINCLUDE',
'DEFFILE',
'WIN32_EXE_LDFLAGS',
'LD_VERSION_SCRIPT',
]
for v in varlist:
if v in context and context[v]:
passthru.variables[v] = context[v]
if context.config.substs.get('OS_TARGET') == 'WINNT' and \
context['DELAYLOAD_DLLS']:
context['LDFLAGS'].extend([('-DELAYLOAD:%s' % dll)
for dll in context['DELAYLOAD_DLLS']])
context['OS_LIBS'].append('delayimp')
for v in ['CFLAGS', 'CXXFLAGS', 'CMFLAGS', 'CMMFLAGS', 'LDFLAGS']:
if v in context and context[v]:
passthru.variables['MOZBUILD_' + v] = context[v]
# NO_VISIBILITY_FLAGS is slightly different
if context['NO_VISIBILITY_FLAGS']:
passthru.variables['VISIBILITY_FLAGS'] = ''
no_pgo = context.get('NO_PGO')
sources = context.get('SOURCES', [])
no_pgo_sources = [f for f in sources if sources[f].no_pgo]
if no_pgo:
if no_pgo_sources:
raise SandboxValidationError('NO_PGO and SOURCES[...].no_pgo '
'cannot be set at the same time', context)
passthru.variables['NO_PROFILE_GUIDED_OPTIMIZE'] = no_pgo
if no_pgo_sources:
passthru.variables['NO_PROFILE_GUIDED_OPTIMIZE'] = no_pgo_sources
# A map from "canonical suffixes" for a particular source file
# language to the range of suffixes associated with that language.
#
# We deliberately don't list the canonical suffix in the suffix list
# in the definition; we'll add it in programmatically after defining
# things.
suffix_map = {
'.s': set(['.asm']),
'.c': set(),
'.m': set(),
'.mm': set(),
'.cpp': set(['.cc', '.cxx']),
'.S': set(),
}
# The inverse of the above, mapping suffixes to their canonical suffix.
canonicalized_suffix_map = {}
for suffix, alternatives in suffix_map.iteritems():
alternatives.add(suffix)
for a in alternatives:
canonicalized_suffix_map[a] = suffix
def canonical_suffix_for_file(f):
return canonicalized_suffix_map[mozpath.splitext(f)[1]]
# A map from moz.build variables to the canonical suffixes of file
# kinds that can be listed therein.
all_suffixes = list(suffix_map.keys())
varmap = dict(
SOURCES=(Sources, all_suffixes),
HOST_SOURCES=(HostSources, ['.c', '.mm', '.cpp']),
UNIFIED_SOURCES=(UnifiedSources, ['.c', '.mm', '.cpp']),
GENERATED_SOURCES=(GeneratedSources, all_suffixes),
)
for variable, (klass, suffixes) in varmap.items():
allowed_suffixes = set().union(*[suffix_map[s] for s in suffixes])
# First ensure that we haven't been given filetypes that we don't
# recognize.
for f in context[variable]:
ext = mozpath.splitext(f)[1]
if ext not in allowed_suffixes:
raise SandboxValidationError(
'%s has an unknown file type.' % f, context)
if variable.startswith('GENERATED_'):
l = passthru.variables.setdefault('GARBAGE', [])
l.append(f)
# Now sort the files to let groupby work.
sorted_files = sorted(context[variable], key=canonical_suffix_for_file)
for canonical_suffix, files in itertools.groupby(sorted_files, canonical_suffix_for_file):
arglist = [context, list(files), canonical_suffix]
if variable.startswith('UNIFIED_') and 'FILES_PER_UNIFIED_FILE' in context:
arglist.append(context['FILES_PER_UNIFIED_FILE'])
yield klass(*arglist)
sources_with_flags = [f for f in sources if sources[f].flags]
for f in sources_with_flags:
ext = mozpath.splitext(f)[1]
yield PerSourceFlag(context, f, sources[f].flags)
exports = context.get('EXPORTS')
if exports:
yield Exports(context, exports,
dist_install=not context.get('NO_DIST_INSTALL', False))
generated_files = context.get('GENERATED_FILES')
if generated_files:
for f in generated_files:
flags = generated_files[f]
output = f
if flags.script:
script = mozpath.join(context.srcdir, flags.script)
inputs = [mozpath.join(context.srcdir, i) for i in flags.inputs]
if not os.path.exists(script):
raise SandboxValidationError(
'Script for generating %s does not exist: %s'
% (f, script), context)
if os.path.splitext(script)[1] != '.py':
raise SandboxValidationError(
'Script for generating %s does not end in .py: %s'
% (f, script), context)
for i in inputs:
if not os.path.exists(i):
raise SandboxValidationError(
'Input for generating %s does not exist: %s'
% (f, i), context)
else:
script = None
inputs = []
yield GeneratedFile(context, script, output, inputs)
test_harness_files = context.get('TEST_HARNESS_FILES')
if test_harness_files:
srcdir_files = defaultdict(list)
srcdir_pattern_files = defaultdict(list)
objdir_files = defaultdict(list)
for path, strings in test_harness_files.walk():
if not path and strings:
raise SandboxValidationError(
'Cannot install files to the root of TEST_HARNESS_FILES', context)
for s in strings:
if context.is_objdir_path(s):
if s.startswith('!/'):
objdir_files[path].append('$(DEPTH)/%s' % s[2:])
else:
objdir_files[path].append(s[1:])
else:
resolved = context.resolve_path(s)
if '*' in s:
if s[0] == '/':
pattern_start = resolved.index('*')
base_path = mozpath.dirname(resolved[:pattern_start])
pattern = resolved[len(base_path)+1:]
else:
base_path = context.srcdir
pattern = s
srcdir_pattern_files[path].append((base_path, pattern));
elif not os.path.exists(resolved):
raise SandboxValidationError(
'File listed in TEST_HARNESS_FILES does not exist: %s' % s, context)
else:
srcdir_files[path].append(resolved)
yield TestHarnessFiles(context, srcdir_files,
srcdir_pattern_files, objdir_files)
defines = context.get('DEFINES')
if defines:
yield Defines(context, defines)
resources = context.get('RESOURCE_FILES')
if resources:
yield Resources(context, resources, defines)
for pref in sorted(context['JS_PREFERENCE_FILES']):
yield JsPreferenceFile(context, pref)
for kind, cls in [('PROGRAM', Program), ('HOST_PROGRAM', HostProgram)]:
program = context.get(kind)
if program:
if program in self._binaries:
raise SandboxValidationError(
'Cannot use "%s" as %s name, '
'because it is already used in %s' % (program, kind,
self._binaries[program].relativedir), context)
self._binaries[program] = cls(context, program)
self._linkage.append((context, self._binaries[program],
kind.replace('PROGRAM', 'USE_LIBS')))
for kind, cls in [
('SIMPLE_PROGRAMS', SimpleProgram),
('CPP_UNIT_TESTS', SimpleProgram),
('HOST_SIMPLE_PROGRAMS', HostSimpleProgram)]:
for program in context[kind]:
if program in self._binaries:
raise SandboxValidationError(
'Cannot use "%s" in %s, '
'because it is already used in %s' % (program, kind,
self._binaries[program].relativedir), context)
self._binaries[program] = cls(context, program,
is_unit_test=kind == 'CPP_UNIT_TESTS')
self._linkage.append((context, self._binaries[program],
'HOST_USE_LIBS' if kind == 'HOST_SIMPLE_PROGRAMS'
else 'USE_LIBS'))
extra_js_modules = context.get('EXTRA_JS_MODULES')
if extra_js_modules:
yield JavaScriptModules(context, extra_js_modules, 'extra')
extra_pp_js_modules = context.get('EXTRA_PP_JS_MODULES')
if extra_pp_js_modules:
yield JavaScriptModules(context, extra_pp_js_modules, 'extra_pp')
test_js_modules = context.get('TESTING_JS_MODULES')
if test_js_modules:
yield JavaScriptModules(context, test_js_modules, 'testing')
simple_lists = [
('GENERATED_EVENTS_WEBIDL_FILES', GeneratedEventWebIDLFile),
('GENERATED_WEBIDL_FILES', GeneratedWebIDLFile),
('IPDL_SOURCES', IPDLFile),
('GENERATED_INCLUDES', GeneratedInclude),
('PREPROCESSED_TEST_WEBIDL_FILES', PreprocessedTestWebIDLFile),
('PREPROCESSED_WEBIDL_FILES', PreprocessedWebIDLFile),
('TEST_WEBIDL_FILES', TestWebIDLFile),
('WEBIDL_FILES', WebIDLFile),
('WEBIDL_EXAMPLE_INTERFACES', ExampleWebIDLInterface),
]
for context_var, klass in simple_lists:
for name in context.get(context_var, []):
yield klass(context, name)
for local_include in context.get('LOCAL_INCLUDES', []):
if local_include.startswith('/'):
path = context.config.topsrcdir
relative_include = local_include[1:]
else:
path = context.srcdir
relative_include = local_include
actual_include = os.path.join(path, relative_include)
if not os.path.exists(actual_include):
raise SandboxValidationError('Path specified in LOCAL_INCLUDES '
'does not exist: %s (resolved to %s)' % (local_include, actual_include), context)
yield LocalInclude(context, local_include)
final_target_files = context.get('FINAL_TARGET_FILES')
if final_target_files:
yield FinalTargetFiles(context, final_target_files, context['FINAL_TARGET'])
branding_files = context.get('BRANDING_FILES')
if branding_files:
yield BrandingFiles(context, branding_files)
def _handle_libraries(self, context):
host_libname = context.get('HOST_LIBRARY_NAME')
libname = context.get('LIBRARY_NAME')
@@ -782,6 +456,13 @@ class TreeMetadataEmitter(LoggingMixin):
elif static_lib:
static_args['is_sdk'] = True
if context.get('NO_EXPAND_LIBS'):
if not static_lib:
raise SandboxValidationError(
'NO_EXPAND_LIBS can only be set for static libraries.',
context)
static_args['no_expand_lib'] = True
if shared_lib and static_lib:
if not static_name and not shared_name:
raise SandboxValidationError(
@@ -825,6 +506,401 @@ class TreeMetadataEmitter(LoggingMixin):
'LIBRARY_NAME to take effect', context)
lib.defines.update(lib_defines)
def emit_from_context(self, context):
"""Convert a Context to tree metadata objects.
This is a generator of mozbuild.frontend.data.ContextDerived instances.
"""
# We only want to emit an InstallationTarget if one of the consulted
# variables is defined. Later on, we look up FINAL_TARGET, which has
# the side-effect of populating it. So, we need to do this lookup
# early.
if any(k in context for k in ('FINAL_TARGET', 'XPI_NAME', 'DIST_SUBDIR')):
yield InstallationTarget(context)
# We always emit a directory traversal descriptor. This is needed by
# the recursive make backend.
for o in self._emit_directory_traversal_from_context(context): yield o
for path in context['CONFIGURE_SUBST_FILES']:
yield self._create_substitution(ConfigFileSubstitution, context,
path)
for path in context['CONFIGURE_DEFINE_FILES']:
yield self._create_substitution(HeaderFileSubstitution, context,
path)
for obj in self._process_xpidl(context):
yield obj
# Proxy some variables as-is until we have richer classes to represent
# them. We should aim to keep this set small because it violates the
# desired abstraction of the build definition away from makefiles.
passthru = VariablePassthru(context)
varlist = [
'ANDROID_GENERATED_RESFILES',
'ANDROID_RES_DIRS',
'DISABLE_STL_WRAPPING',
'EXTRA_ASSEMBLER_FLAGS',
'EXTRA_COMPILE_FLAGS',
'EXTRA_COMPONENTS',
'EXTRA_DSO_LDOPTS',
'EXTRA_PP_COMPONENTS',
'FAIL_ON_WARNINGS',
'USE_STATIC_LIBS',
'MSVC_ENABLE_PGO',
'PYTHON_UNIT_TESTS',
'RCFILE',
'RESFILE',
'RCINCLUDE',
'DEFFILE',
'WIN32_EXE_LDFLAGS',
'LD_VERSION_SCRIPT',
]
for v in varlist:
if v in context and context[v]:
passthru.variables[v] = context[v]
if context.config.substs.get('OS_TARGET') == 'WINNT' and \
context['DELAYLOAD_DLLS']:
context['LDFLAGS'].extend([('-DELAYLOAD:%s' % dll)
for dll in context['DELAYLOAD_DLLS']])
context['OS_LIBS'].append('delayimp')
for v in ['CFLAGS', 'CXXFLAGS', 'CMFLAGS', 'CMMFLAGS', 'ASFLAGS',
'LDFLAGS']:
if v in context and context[v]:
passthru.variables['MOZBUILD_' + v] = context[v]
# NO_VISIBILITY_FLAGS is slightly different
if context['NO_VISIBILITY_FLAGS']:
passthru.variables['VISIBILITY_FLAGS'] = ''
if isinstance(context, TemplateContext) and context.template == 'Gyp':
passthru.variables['IS_GYP_DIR'] = True
dist_install = context['DIST_INSTALL']
if dist_install is True:
passthru.variables['DIST_INSTALL'] = True
elif dist_install is False:
passthru.variables['NO_DIST_INSTALL'] = True
for obj in self._process_sources(context, passthru):
yield obj
exports = context.get('EXPORTS')
if exports:
yield Exports(context, exports,
dist_install=dist_install is not False)
for obj in self._process_generated_files(context):
yield obj
for obj in self._process_test_harness_files(context):
yield obj
defines = context.get('DEFINES')
if defines:
yield Defines(context, defines)
resources = context.get('RESOURCE_FILES')
if resources:
yield Resources(context, resources, defines)
for pref in sorted(context['JS_PREFERENCE_FILES']):
yield JsPreferenceFile(context, pref)
self._handle_programs(context)
extra_js_modules = context.get('EXTRA_JS_MODULES')
if extra_js_modules:
yield JavaScriptModules(context, extra_js_modules, 'extra')
extra_pp_js_modules = context.get('EXTRA_PP_JS_MODULES')
if extra_pp_js_modules:
yield JavaScriptModules(context, extra_pp_js_modules, 'extra_pp')
test_js_modules = context.get('TESTING_JS_MODULES')
if test_js_modules:
yield JavaScriptModules(context, test_js_modules, 'testing')
simple_lists = [
('GENERATED_EVENTS_WEBIDL_FILES', GeneratedEventWebIDLFile),
('GENERATED_WEBIDL_FILES', GeneratedWebIDLFile),
('IPDL_SOURCES', IPDLFile),
('GENERATED_INCLUDES', GeneratedInclude),
('PREPROCESSED_TEST_WEBIDL_FILES', PreprocessedTestWebIDLFile),
('PREPROCESSED_WEBIDL_FILES', PreprocessedWebIDLFile),
('TEST_WEBIDL_FILES', TestWebIDLFile),
('WEBIDL_FILES', WebIDLFile),
('WEBIDL_EXAMPLE_INTERFACES', ExampleWebIDLInterface),
]
for context_var, klass in simple_lists:
for name in context.get(context_var, []):
yield klass(context, name)
for local_include in context.get('LOCAL_INCLUDES', []):
if local_include.startswith('/'):
path = context.config.topsrcdir
relative_include = local_include[1:]
else:
path = context.srcdir
relative_include = local_include
actual_include = os.path.join(path, relative_include)
if not os.path.exists(actual_include):
raise SandboxValidationError('Path specified in LOCAL_INCLUDES '
'does not exist: %s (resolved to %s)' % (local_include, actual_include), context)
yield LocalInclude(context, local_include)
final_target_files = context.get('FINAL_TARGET_FILES')
if final_target_files:
yield FinalTargetFiles(context, final_target_files, context['FINAL_TARGET'])
branding_files = context.get('BRANDING_FILES')
if branding_files:
yield BrandingFiles(context, branding_files)
self._handle_libraries(context)
for obj in self._process_test_manifests(context):
yield obj
for obj in self._process_jar_manifests(context):
yield obj
for name, jar in context.get('JAVA_JAR_TARGETS', {}).items():
yield ContextWrapped(context, jar)
for name, data in context.get('ANDROID_ECLIPSE_PROJECT_TARGETS', {}).items():
yield ContextWrapped(context, data)
if passthru.variables:
yield passthru
def _create_substitution(self, cls, context, path):
if os.path.isabs(path):
path = path[1:]
sub = cls(context)
sub.input_path = mozpath.join(context.srcdir, '%s.in' % path)
sub.output_path = mozpath.join(context.objdir, path)
sub.relpath = path
return sub
def _process_sources(self, context, passthru):
for symbol in ('SOURCES', 'HOST_SOURCES', 'UNIFIED_SOURCES'):
for src in (context[symbol] or []):
if not os.path.exists(mozpath.join(context.srcdir, src)):
raise SandboxValidationError('File listed in %s does not '
'exist: \'%s\'' % (symbol, src), context)
no_pgo = context.get('NO_PGO')
sources = context.get('SOURCES', [])
no_pgo_sources = [f for f in sources if sources[f].no_pgo]
if no_pgo:
if no_pgo_sources:
raise SandboxValidationError('NO_PGO and SOURCES[...].no_pgo '
'cannot be set at the same time', context)
passthru.variables['NO_PROFILE_GUIDED_OPTIMIZE'] = no_pgo
if no_pgo_sources:
passthru.variables['NO_PROFILE_GUIDED_OPTIMIZE'] = no_pgo_sources
# A map from "canonical suffixes" for a particular source file
# language to the range of suffixes associated with that language.
#
# We deliberately don't list the canonical suffix in the suffix list
# in the definition; we'll add it in programmatically after defining
# things.
suffix_map = {
'.s': set(['.asm']),
'.c': set(),
'.m': set(),
'.mm': set(),
'.cpp': set(['.cc', '.cxx']),
'.S': set(),
}
# The inverse of the above, mapping suffixes to their canonical suffix.
canonicalized_suffix_map = {}
for suffix, alternatives in suffix_map.iteritems():
alternatives.add(suffix)
for a in alternatives:
canonicalized_suffix_map[a] = suffix
def canonical_suffix_for_file(f):
return canonicalized_suffix_map[mozpath.splitext(f)[1]]
# A map from moz.build variables to the canonical suffixes of file
# kinds that can be listed therein.
all_suffixes = list(suffix_map.keys())
varmap = dict(
SOURCES=(Sources, all_suffixes),
HOST_SOURCES=(HostSources, ['.c', '.mm', '.cpp']),
UNIFIED_SOURCES=(UnifiedSources, ['.c', '.mm', '.cpp']),
GENERATED_SOURCES=(GeneratedSources, all_suffixes),
)
for variable, (klass, suffixes) in varmap.items():
allowed_suffixes = set().union(*[suffix_map[s] for s in suffixes])
# First ensure that we haven't been given filetypes that we don't
# recognize.
for f in context[variable]:
ext = mozpath.splitext(f)[1]
if ext not in allowed_suffixes:
raise SandboxValidationError(
'%s has an unknown file type.' % f, context)
if variable.startswith('GENERATED_'):
l = passthru.variables.setdefault('GARBAGE', [])
l.append(f)
# Now sort the files to let groupby work.
sorted_files = sorted(context[variable], key=canonical_suffix_for_file)
for canonical_suffix, files in itertools.groupby(sorted_files, canonical_suffix_for_file):
arglist = [context, list(files), canonical_suffix]
if variable.startswith('UNIFIED_') and 'FILES_PER_UNIFIED_FILE' in context:
arglist.append(context['FILES_PER_UNIFIED_FILE'])
yield klass(*arglist)
sources_with_flags = [f for f in sources if sources[f].flags]
for f in sources_with_flags:
ext = mozpath.splitext(f)[1]
yield PerSourceFlag(context, f, sources[f].flags)
def _process_xpidl(self, context):
# XPIDL source files get processed and turned into .h and .xpt files.
# If there are multiple XPIDL files in a directory, they get linked
# together into a final .xpt, which has the name defined by
# XPIDL_MODULE.
xpidl_module = context['XPIDL_MODULE']
if context['XPIDL_SOURCES'] and not xpidl_module:
raise SandboxValidationError('XPIDL_MODULE must be defined if '
'XPIDL_SOURCES is defined.', context)
if xpidl_module and not context['XPIDL_SOURCES']:
raise SandboxValidationError('XPIDL_MODULE cannot be defined '
'unless there are XPIDL_SOURCES', context)
if context['XPIDL_SOURCES'] and context['DIST_INSTALL'] is False:
self.log(logging.WARN, 'mozbuild_warning', dict(
path=context.main_path),
'{path}: DIST_INSTALL = False has no effect on XPIDL_SOURCES.')
for idl in context['XPIDL_SOURCES']:
yield XPIDLFile(context, mozpath.join(context.srcdir, idl),
xpidl_module)
def _process_generated_files(self, context):
generated_files = context.get('GENERATED_FILES')
if not generated_files:
return
for f in generated_files:
flags = generated_files[f]
output = f
if flags.script:
method = "main"
# Deal with cases like "C:\\path\\to\\script.py:function".
if not flags.script.endswith('.py') and ':' in flags.script:
script, method = flags.script.rsplit(':', 1)
else:
script = flags.script
script = mozpath.join(context.srcdir, script)
inputs = [mozpath.join(context.srcdir, i) for i in flags.inputs]
if not os.path.exists(script):
raise SandboxValidationError(
'Script for generating %s does not exist: %s'
% (f, script), context)
if os.path.splitext(script)[1] != '.py':
raise SandboxValidationError(
'Script for generating %s does not end in .py: %s'
% (f, script), context)
for i in inputs:
if not os.path.exists(i):
raise SandboxValidationError(
'Input for generating %s does not exist: %s'
% (f, i), context)
else:
script = None
method = None
inputs = []
yield GeneratedFile(context, script, method, output, inputs)
def _process_test_harness_files(self, context):
test_harness_files = context.get('TEST_HARNESS_FILES')
if not test_harness_files:
return
srcdir_files = defaultdict(list)
srcdir_pattern_files = defaultdict(list)
objdir_files = defaultdict(list)
for path, strings in test_harness_files.walk():
if not path and strings:
raise SandboxValidationError(
'Cannot install files to the root of TEST_HARNESS_FILES', context)
for s in strings:
if context.is_objdir_path(s):
if s.startswith('!/'):
objdir_files[path].append('$(DEPTH)/%s' % s[2:])
else:
objdir_files[path].append(s[1:])
else:
resolved = context.resolve_path(s)
if '*' in s:
if s[0] == '/':
pattern_start = resolved.index('*')
base_path = mozpath.dirname(resolved[:pattern_start])
pattern = resolved[len(base_path)+1:]
else:
base_path = context.srcdir
pattern = s
srcdir_pattern_files[path].append((base_path, pattern));
elif not os.path.exists(resolved):
raise SandboxValidationError(
'File listed in TEST_HARNESS_FILES does not exist: %s' % s, context)
else:
srcdir_files[path].append(resolved)
yield TestHarnessFiles(context, srcdir_files,
srcdir_pattern_files, objdir_files)
def _handle_programs(self, context):
for kind, cls in [('PROGRAM', Program), ('HOST_PROGRAM', HostProgram)]:
program = context.get(kind)
if program:
if program in self._binaries:
raise SandboxValidationError(
'Cannot use "%s" as %s name, '
'because it is already used in %s' % (program, kind,
self._binaries[program].relativedir), context)
self._binaries[program] = cls(context, program)
self._linkage.append((context, self._binaries[program],
kind.replace('PROGRAM', 'USE_LIBS')))
for kind, cls in [
('SIMPLE_PROGRAMS', SimpleProgram),
('CPP_UNIT_TESTS', SimpleProgram),
('HOST_SIMPLE_PROGRAMS', HostSimpleProgram)]:
for program in context[kind]:
if program in self._binaries:
raise SandboxValidationError(
'Cannot use "%s" in %s, '
'because it is already used in %s' % (program, kind,
self._binaries[program].relativedir), context)
self._binaries[program] = cls(context, program,
is_unit_test=kind == 'CPP_UNIT_TESTS')
self._linkage.append((context, self._binaries[program],
'HOST_USE_LIBS' if kind == 'HOST_SIMPLE_PROGRAMS'
else 'USE_LIBS'))
def _process_test_manifests(self, context):
# While there are multiple test manifests, the behavior is very similar
# across them. We enforce this by having common handling of all
# manifests and outputting a single class type with the differences
@@ -865,44 +941,6 @@ class TreeMetadataEmitter(LoggingMixin):
for obj in self._process_reftest_manifest(context, flavor, path):
yield obj
jar_manifests = context.get('JAR_MANIFESTS', [])
if len(jar_manifests) > 1:
raise SandboxValidationError('While JAR_MANIFESTS is a list, '
'it is currently limited to one value.', context)
for path in jar_manifests:
yield JARManifest(context, mozpath.join(context.srcdir, path))
# Temporary test to look for jar.mn files that creep in without using
# the new declaration. Before, we didn't require jar.mn files to
# declared anywhere (they were discovered). This will detect people
# relying on the old behavior.
if os.path.exists(os.path.join(context.srcdir, 'jar.mn')):
if 'jar.mn' not in jar_manifests:
raise SandboxValidationError('A jar.mn exists but it '
'is not referenced in the moz.build file. '
'Please define JAR_MANIFESTS.', context)
for name, jar in context.get('JAVA_JAR_TARGETS', {}).items():
yield ContextWrapped(context, jar)
for name, data in context.get('ANDROID_ECLIPSE_PROJECT_TARGETS', {}).items():
yield ContextWrapped(context, data)
if passthru.variables:
yield passthru
def _create_substitution(self, cls, context, path):
if os.path.isabs(path):
path = path[1:]
sub = cls(context)
sub.input_path = mozpath.join(context.srcdir, '%s.in' % path)
sub.output_path = mozpath.join(context.objdir, path)
sub.relpath = path
return sub
def _process_test_manifest(self, context, info, manifest_path):
flavor, install_root, install_subdir, package_tests = info
@@ -914,7 +952,8 @@ class TreeMetadataEmitter(LoggingMixin):
install_prefix = mozpath.join(install_root, install_subdir)
try:
m = manifestparser.TestManifest(manifests=[path], strict=True)
m = manifestparser.TestManifest(manifests=[path], strict=True,
rootdir=context.config.topsrcdir)
defaults = m.manifest_defaults[os.path.normpath(path)]
if not m.tests and not 'support-files' in defaults:
raise SandboxValidationError('Empty test manifest: %s'
@@ -1008,8 +1047,10 @@ class TreeMetadataEmitter(LoggingMixin):
# Some test files are compiled and should not be copied into the
# test package. They function as identifiers rather than files.
if package_tests:
manifest_relpath = mozpath.relpath(test['path'],
mozpath.dirname(test['manifest']))
obj.installs[mozpath.normpath(test['path'])] = \
(mozpath.join(out_dir, test['relpath']), True)
((mozpath.join(out_dir, manifest_relpath)), True)
process_support_files(test)
@@ -1079,6 +1120,25 @@ class TreeMetadataEmitter(LoggingMixin):
yield obj
def _process_jar_manifests(self, context):
jar_manifests = context.get('JAR_MANIFESTS', [])
if len(jar_manifests) > 1:
raise SandboxValidationError('While JAR_MANIFESTS is a list, '
'it is currently limited to one value.', context)
for path in jar_manifests:
yield JARManifest(context, mozpath.join(context.srcdir, path))
# Temporary test to look for jar.mn files that creep in without using
# the new declaration. Before, we didn't require jar.mn files to
# declared anywhere (they were discovered). This will detect people
# relying on the old behavior.
if os.path.exists(os.path.join(context.srcdir, 'jar.mn')):
if 'jar.mn' not in jar_manifests:
raise SandboxValidationError('A jar.mn exists but it '
'is not referenced in the moz.build file. '
'Please define JAR_MANIFESTS.', context)
def _emit_directory_traversal_from_context(self, context):
o = DirectoryTraversal(context)
o.dirs = context.get('DIRS', [])
@@ -61,8 +61,8 @@ class GypContext(TemplateContext):
"""
def __init__(self, config, relobjdir):
self._relobjdir = relobjdir
TemplateContext.__init__(self, allowed_variables=self.VARIABLES(),
config=config)
TemplateContext.__init__(self, template='Gyp',
allowed_variables=self.VARIABLES(), config=config)
@classmethod
@memoize
@@ -71,7 +71,6 @@ class GypContext(TemplateContext):
# Using a class method instead of a class variable to hide the content
# from sphinx.
return dict(VARIABLES,
IS_GYP_DIR=(bool, bool, '', None),
EXTRA_ASSEMBLER_FLAGS=(List, list, '', None),
EXTRA_COMPILE_FLAGS=(List, list, '', None),
)
@@ -148,8 +147,6 @@ def read_from_gyp(config, path, output, vars, non_unified_sources = set()):
context.add_source(mozpath.abspath(mozpath.join(
mozpath.dirname(build_file), f)))
context['IS_GYP_DIR'] = True
spec = targets[target]
# Derive which gyp configuration to use based on MOZ_DEBUG.
+4 -2
View File
@@ -411,8 +411,10 @@ class MozbuildSandbox(Sandbox):
func, code, path = template
def template_function(*args, **kwargs):
context = TemplateContext(self._context._allowed_variables,
self._context.config)
context = TemplateContext(
template=func.func_name,
allowed_variables=self._context._allowed_variables,
config=self._context.config)
context.add_source(self._context.current_path)
for p in self._context.all_paths:
context.add_source(p)
@@ -5,7 +5,7 @@
GENERATED_FILES += [ 'bar.c', 'foo.c', 'quux.c' ]
bar = GENERATED_FILES['bar.c']
bar.script = 'generate-bar.py'
bar.script = 'generate-bar.py:baz'
foo = GENERATED_FILES['foo.c']
foo.script = 'generate-foo.py'
@@ -379,11 +379,11 @@ class TestRecursiveMakeBackend(BackendTester):
expected = [
'GENERATED_FILES += bar.c',
'bar.c: %s/generate-bar.py' % env.topsrcdir,
'$(call py_action,file_generate,%s/generate-bar.py bar.c)' % env.topsrcdir,
'$(call py_action,file_generate,%s/generate-bar.py baz bar.c)' % env.topsrcdir,
'',
'GENERATED_FILES += foo.c',
'foo.c: %s/generate-foo.py %s/foo-data' % (env.topsrcdir, env.topsrcdir),
'$(call py_action,file_generate,%s/generate-foo.py foo.c %s/foo-data)' % (env.topsrcdir, env.topsrcdir),
'$(call py_action,file_generate,%s/generate-foo.py main foo.c %s/foo-data)' % (env.topsrcdir, env.topsrcdir),
'',
'GENERATED_FILES += quux.c',
]
@@ -0,0 +1,9 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# Any copyright is dedicated to the Public Domain.
# http://creativecommons.org/publicdomain/zero/1.0/
GENERATED_FILES += ['bar.c']
bar = GENERATED_FILES['bar.c']
bar.script = TOPSRCDIR + '/script.py:make_bar'
bar.inputs = []
@@ -0,0 +1,13 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# Any copyright is dedicated to the Public Domain.
# http://creativecommons.org/publicdomain/zero/1.0/
GENERATED_FILES += [ 'bar.c', 'foo.c' ]
bar = GENERATED_FILES['bar.c']
bar.script = 'script.py:make_bar'
bar.inputs = []
foo = GENERATED_FILES['foo.c']
foo.script = 'script.py'
foo.inputs = []
@@ -8,7 +8,7 @@ EXTRA_PP_COMPONENTS=['fans.pp.js', 'tans.pp.js']
FAIL_ON_WARNINGS = True
MSVC_ENABLE_PGO = True
NO_DIST_INSTALL = True
DIST_INSTALL = False
NO_VISIBILITY_FLAGS = True
@@ -194,6 +194,37 @@ class TestEmitterBasic(unittest.TestCase):
expected = ['bar.c', 'foo.c']
for o, expected_filename in zip(objs, expected):
self.assertEqual(o.output, expected_filename)
self.assertEqual(o.script, None)
self.assertEqual(o.method, None)
self.assertEqual(o.inputs, [])
def test_generated_files_method_names(self):
reader = self.reader('generated-files-method-names')
objs = self.read_topsrcdir(reader)
self.assertEqual(len(objs), 2)
for o in objs:
self.assertIsInstance(o, GeneratedFile)
expected = ['bar.c', 'foo.c']
expected_method_names = ['make_bar', 'main']
for o, expected_filename, expected_method in zip(objs, expected, expected_method_names):
self.assertEqual(o.output, expected_filename)
self.assertEqual(o.method, expected_method)
self.assertEqual(o.inputs, [])
def test_generated_files_absolute_script(self):
reader = self.reader('generated-files-absolute-script')
objs = self.read_topsrcdir(reader)
self.assertEqual(len(objs), 1)
o = objs[0]
self.assertIsInstance(o, GeneratedFile)
self.assertEqual(o.output, 'bar.c')
self.assertRegexpMatches(o.script, 'script.py$')
self.assertEqual(o.method, 'make_bar')
self.assertEqual(o.inputs, [])
def test_generated_files_no_script(self):
reader = self.reader('generated-files-no-script')
+3 -1
View File
@@ -29,8 +29,10 @@ def rewrite_test_base(test, new_base, honor_install_to_subdir=False):
test['here'] = mozpath.join(new_base, test['dir_relpath'])
if honor_install_to_subdir and test.get('install-to-subdir'):
manifest_relpath = mozpath.relpath(test['path'],
mozpath.dirname(test['manifest']))
test['path'] = mozpath.join(new_base, test['dir_relpath'],
test['install-to-subdir'], test['relpath'])
test['install-to-subdir'], manifest_relpath)
else:
test['path'] = mozpath.join(new_base, test['file_relpath'])