diff options
Diffstat (limited to 'python/mozbuild/mozbuild/frontend/context.py')
-rw-r--r-- | python/mozbuild/mozbuild/frontend/context.py | 3144 |
1 files changed, 3144 insertions, 0 deletions
diff --git a/python/mozbuild/mozbuild/frontend/context.py b/python/mozbuild/mozbuild/frontend/context.py new file mode 100644 index 0000000000..1e241c5656 --- /dev/null +++ b/python/mozbuild/mozbuild/frontend/context.py @@ -0,0 +1,3144 @@ +# 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/. + +###################################################################### +# DO NOT UPDATE THIS FILE WITHOUT SIGN-OFF FROM A BUILD MODULE PEER. # +###################################################################### + +r"""This module contains the data structure (context) holding the configuration +from a moz.build. The data emitted by the frontend derives from those contexts. + +It also defines the set of variables and functions available in moz.build. +If you are looking for the absolute authority on what moz.build files can +contain, you've come to the right place. +""" + +import itertools +import operator +import os +from collections import Counter, OrderedDict +from types import FunctionType + +import mozpack.path as mozpath +import six + +from mozbuild.util import ( + HierarchicalStringList, + ImmutableStrictOrderingOnAppendList, + KeyedDefaultDict, + List, + ReadOnlyKeyedDefaultDict, + StrictOrderingOnAppendList, + StrictOrderingOnAppendListWithAction, + StrictOrderingOnAppendListWithFlagsFactory, + TypedList, + TypedNamedTuple, + memoize, + memoized_property, +) + +from .. import schedules +from ..testing import read_manifestparser_manifest, read_reftest_manifest + + +class ContextDerivedValue(object): + """Classes deriving from this one receive a special treatment in a + Context. See Context documentation. + """ + + __slots__ = () + + +class Context(KeyedDefaultDict): + """Represents a moz.build configuration context. + + Instances of this class are filled by the execution of sandboxes. + At the core, a Context is a dict, with a defined set of possible keys we'll + call variables. Each variable is associated with a type. + + When reading a value for a given key, we first try to read the existing + value. If a value is not found and it is defined in the allowed variables + set, we return a new instance of the class for that variable. We don't + assign default instances until they are accessed because this makes + debugging the end-result much simpler. Instead of a data structure with + lots of empty/default values, you have a data structure with only the + values that were read or touched. + + Instances of variables classes are created by invoking ``class_name()``, + except when class_name derives from ``ContextDerivedValue`` or + ``SubContext``, in which case ``class_name(instance_of_the_context)`` or + ``class_name(self)`` is invoked. A value is added to those calls when + instances are created during assignment (setitem). + + allowed_variables is a dict of the variables that can be set and read in + this context instance. Keys in this dict are the strings representing keys + in this context which are valid. Values are tuples of stored type, + assigned type, default value, a docstring describing the purpose of the + variable, and a tier indicator (see comment above the VARIABLES declaration + in this module). + + config is the ConfigEnvironment for this context. + """ + + def __init__(self, allowed_variables={}, config=None, finder=None): + self._allowed_variables = allowed_variables + self.main_path = None + self.current_path = None + # There aren't going to be enough paths for the performance of scanning + # a list to be a problem. + self._all_paths = [] + self.config = config + self._sandbox = None + self._finder = finder + KeyedDefaultDict.__init__(self, self._factory) + + def push_source(self, path): + """Adds the given path as source of the data from this context and make + it the current path for the context.""" + assert os.path.isabs(path) + if not self.main_path: + self.main_path = path + else: + # Callers shouldn't push after main_path has been popped. + assert self.current_path + self.current_path = path + # The same file can be pushed twice, so don't remove any previous + # occurrence. + self._all_paths.append(path) + + def pop_source(self): + """Get back to the previous current path for the context.""" + assert self.main_path + assert self.current_path + last = self._all_paths.pop() + # Keep the popped path in the list of all paths, but before the main + # path so that it's not popped again. + self._all_paths.insert(0, last) + if last == self.main_path: + self.current_path = None + else: + self.current_path = self._all_paths[-1] + return last + + def add_source(self, path): + """Adds the given path as source of the data from this context.""" + assert os.path.isabs(path) + if not self.main_path: + self.main_path = self.current_path = path + # Insert at the beginning of the list so that it's always before the + # main path. + if path not in self._all_paths: + self._all_paths.insert(0, path) + + @property + def error_is_fatal(self): + """Returns True if the error function should be fatal.""" + return self.config and getattr(self.config, "error_is_fatal", True) + + @property + def all_paths(self): + """Returns all paths ever added to the context.""" + return set(self._all_paths) + + @property + def source_stack(self): + """Returns the current stack of pushed sources.""" + if not self.current_path: + return [] + return self._all_paths[self._all_paths.index(self.main_path) :] + + @memoized_property + def objdir(self): + return mozpath.join(self.config.topobjdir, self.relobjdir).rstrip("/") + + @memoize + def _srcdir(self, path): + return mozpath.join(self.config.topsrcdir, self._relsrcdir(path)).rstrip("/") + + @property + def srcdir(self): + return self._srcdir(self.current_path or self.main_path) + + @memoize + def _relsrcdir(self, path): + return mozpath.relpath(mozpath.dirname(path), self.config.topsrcdir) + + @property + def relsrcdir(self): + assert self.main_path + return self._relsrcdir(self.current_path or self.main_path) + + @memoized_property + def relobjdir(self): + assert self.main_path + return mozpath.relpath(mozpath.dirname(self.main_path), self.config.topsrcdir) + + def _factory(self, key): + """Function called when requesting a missing key.""" + defaults = self._allowed_variables.get(key) + if not defaults: + raise KeyError("global_ns", "get_unknown", key) + + # If the default is specifically a lambda (or, rather, any function + # --but not a class that can be called), then it is actually a rule to + # generate the default that should be used. + default = defaults[0] + if issubclass(default, ContextDerivedValue): + return default(self) + else: + return default() + + def _validate(self, key, value, is_template=False): + """Validates whether the key is allowed and if the value's type + matches. + """ + stored_type, input_type, docs = self._allowed_variables.get( + key, (None, None, None) + ) + + if stored_type is None or not is_template and key in TEMPLATE_VARIABLES: + raise KeyError("global_ns", "set_unknown", key, value) + + # If the incoming value is not the type we store, we try to convert + # it to that type. This relies on proper coercion rules existing. This + # is the responsibility of whoever defined the symbols: a type should + # not be in the allowed set if the constructor function for the stored + # type does not accept an instance of that type. + if not isinstance(value, (stored_type, input_type)): + raise ValueError("global_ns", "set_type", key, value, input_type) + + return stored_type + + def __setitem__(self, key, value): + stored_type = self._validate(key, value) + + if not isinstance(value, stored_type): + if issubclass(stored_type, ContextDerivedValue): + value = stored_type(self, value) + else: + value = stored_type(value) + + return KeyedDefaultDict.__setitem__(self, key, value) + + def update(self, iterable={}, **kwargs): + """Like dict.update(), but using the context's setitem. + + This function is transactional: if setitem fails for one of the values, + the context is not updated at all.""" + if isinstance(iterable, dict): + iterable = iterable.items() + + update = {} + for key, value in itertools.chain(iterable, kwargs.items()): + stored_type = self._validate(key, value) + # Don't create an instance of stored_type if coercion is needed, + # until all values are validated. + update[key] = (value, stored_type) + for key, (value, stored_type) in update.items(): + if not isinstance(value, stored_type): + update[key] = stored_type(value) + else: + update[key] = value + KeyedDefaultDict.update(self, update) + + +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) + + +class SubContext(Context, ContextDerivedValue): + """A Context derived from another Context. + + Sub-contexts are intended to be used as context managers. + + Sub-contexts inherit paths and other relevant state from the parent + context. + """ + + def __init__(self, parent): + assert isinstance(parent, Context) + + Context.__init__(self, allowed_variables=self.VARIABLES, config=parent.config) + + # Copy state from parent. + for p in parent.source_stack: + self.push_source(p) + self._sandbox = parent._sandbox + + def __enter__(self): + if not self._sandbox or self._sandbox() is None: + raise Exception("a sandbox is required") + + self._sandbox().push_subcontext(self) + + def __exit__(self, exc_type, exc_value, traceback): + self._sandbox().pop_subcontext(self) + + +class InitializedDefines(ContextDerivedValue, OrderedDict): + def __init__(self, context, value=None): + OrderedDict.__init__(self) + for define in context.config.substs.get("MOZ_DEBUG_DEFINES", ()): + self[define] = 1 + if value: + if not isinstance(value, OrderedDict): + raise ValueError("Can only initialize with another OrderedDict") + self.update(value) + + def update(self, *other, **kwargs): + # Since iteration over non-ordered dicts is non-deterministic, this dict + # will be populated in an unpredictable order unless the argument to + # update() is also ordered. (It's important that we maintain this + # invariant so we can be sure that running `./mach build-backend` twice + # in a row without updating any files in the workspace generates exactly + # the same output.) + if kwargs: + raise ValueError("Cannot call update() with kwargs") + if other: + if not isinstance(other[0], OrderedDict): + raise ValueError("Can only call update() with another OrderedDict") + return super(InitializedDefines, self).update(*other, **kwargs) + raise ValueError("No arguments passed to update()") + + +class BaseCompileFlags(ContextDerivedValue, dict): + def __init__(self, context): + self._context = context + + klass_name = self.__class__.__name__ + for k, v, build_vars in self.flag_variables: + if not isinstance(k, six.text_type): + raise ValueError("Flag %s for %s is not a string" % (k, klass_name)) + if not isinstance(build_vars, tuple): + raise ValueError( + "Build variables `%s` for %s in %s is not a tuple" + % (build_vars, k, klass_name) + ) + + self._known_keys = set(k for k, v, _ in self.flag_variables) + + # Providing defaults here doesn't play well with multiple templates + # modifying COMPILE_FLAGS from the same moz.build, because the merge + # done after the template runs can't tell which values coming from + # a template were set and which were provided as defaults. + template_name = getattr(context, "template", None) + if template_name in (None, "Gyp"): + dict.__init__( + self, + ( + (k, v if v is None else TypedList(six.text_type)(v)) + for k, v, _ in self.flag_variables + ), + ) + else: + dict.__init__(self) + + +class HostCompileFlags(BaseCompileFlags): + def __init__(self, context): + self._context = context + main_src_dir = mozpath.dirname(context.main_path) + + self.flag_variables = ( + ( + "HOST_CXXFLAGS", + context.config.substs.get("HOST_CXXFLAGS"), + ("HOST_CXXFLAGS", "HOST_CXX_LDFLAGS"), + ), + ( + "HOST_CFLAGS", + context.config.substs.get("HOST_CFLAGS"), + ("HOST_CFLAGS", "HOST_C_LDFLAGS"), + ), + ( + "HOST_OPTIMIZE", + self._optimize_flags(), + ("HOST_CFLAGS", "HOST_CXXFLAGS", "HOST_C_LDFLAGS", "HOST_CXX_LDFLAGS"), + ), + ("RTL", None, ("HOST_CFLAGS", "HOST_C_LDFLAGS")), + ("HOST_DEFINES", None, ("HOST_CFLAGS", "HOST_CXXFLAGS")), + ("MOZBUILD_HOST_CFLAGS", [], ("HOST_CFLAGS", "HOST_C_LDFLAGS")), + ("MOZBUILD_HOST_CXXFLAGS", [], ("HOST_CXXFLAGS", "HOST_CXX_LDFLAGS")), + ( + "BASE_INCLUDES", + ["-I%s" % main_src_dir, "-I%s" % context.objdir], + ("HOST_CFLAGS", "HOST_CXXFLAGS"), + ), + ("LOCAL_INCLUDES", None, ("HOST_CFLAGS", "HOST_CXXFLAGS")), + ( + "EXTRA_INCLUDES", + ["-I%s/dist/include" % context.config.topobjdir], + ("HOST_CFLAGS", "HOST_CXXFLAGS"), + ), + ( + "WARNINGS_CFLAGS", + context.config.substs.get("WARNINGS_HOST_CFLAGS"), + ("HOST_CFLAGS",), + ), + ( + "WARNINGS_CXXFLAGS", + context.config.substs.get("WARNINGS_HOST_CXXFLAGS"), + ("HOST_CXXFLAGS",), + ), + ) + BaseCompileFlags.__init__(self, context) + + def _optimize_flags(self): + optimize_flags = [] + if self._context.config.substs.get("CROSS_COMPILE"): + optimize_flags += self._context.config.substs.get("HOST_OPTIMIZE_FLAGS") + elif self._context.config.substs.get("MOZ_OPTIMIZE"): + optimize_flags += self._context.config.substs.get("MOZ_OPTIMIZE_FLAGS") + return optimize_flags + + +class AsmFlags(BaseCompileFlags): + def __init__(self, context): + self._context = context + self.flag_variables = ( + ("DEFINES", None, ("SFLAGS",)), + ("LIBRARY_DEFINES", None, ("SFLAGS",)), + ("OS", context.config.substs.get("ASFLAGS"), ("ASFLAGS", "SFLAGS")), + ("DEBUG", self._debug_flags(), ("ASFLAGS", "SFLAGS")), + ("LOCAL_INCLUDES", None, ("SFLAGS",)), + ("MOZBUILD", None, ("ASFLAGS", "SFLAGS")), + ) + BaseCompileFlags.__init__(self, context) + + def _debug_flags(self): + debug_flags = [] + if self._context.config.substs.get( + "MOZ_DEBUG" + ) or self._context.config.substs.get("MOZ_DEBUG_SYMBOLS"): + if self._context.get("USE_NASM"): + if self._context.config.substs.get("OS_ARCH") == "WINNT": + debug_flags += ["-F", "cv8"] + elif self._context.config.substs.get("OS_ARCH") != "Darwin": + debug_flags += ["-F", "dwarf"] + elif ( + self._context.config.substs.get("OS_ARCH") == "WINNT" + and self._context.config.substs.get("CPU_ARCH") == "aarch64" + ): + # armasm64 accepts a paucity of options compared to ml/ml64. + pass + else: + debug_flags += self._context.config.substs.get( + "MOZ_DEBUG_FLAGS", "" + ).split() + return debug_flags + + +class LinkFlags(BaseCompileFlags): + def __init__(self, context): + self._context = context + + self.flag_variables = ( + ("OS", self._os_ldflags(), ("LDFLAGS",)), + ( + "MOZ_HARDENING_LDFLAGS", + context.config.substs.get("MOZ_HARDENING_LDFLAGS"), + ("LDFLAGS",), + ), + ("DEFFILE", None, ("LDFLAGS",)), + ("MOZBUILD", None, ("LDFLAGS",)), + ( + "FIX_LINK_PATHS", + context.config.substs.get("MOZ_FIX_LINK_PATHS"), + ("LDFLAGS",), + ), + ( + "OPTIMIZE", + ( + context.config.substs.get("MOZ_OPTIMIZE_LDFLAGS", []) + if context.config.substs.get("MOZ_OPTIMIZE") + else [] + ), + ("LDFLAGS",), + ), + ( + "CETCOMPAT", + ( + context.config.substs.get("MOZ_CETCOMPAT_LDFLAGS") + if context.config.substs.get("NIGHTLY_BUILD") + else [] + ), + ("LDFLAGS",), + ), + ) + BaseCompileFlags.__init__(self, context) + + def _os_ldflags(self): + flags = self._context.config.substs.get("OS_LDFLAGS", [])[:] + + if self._context.config.substs.get( + "MOZ_DEBUG" + ) or self._context.config.substs.get("MOZ_DEBUG_SYMBOLS"): + flags += self._context.config.substs.get("MOZ_DEBUG_LDFLAGS", []) + + # TODO: This is pretty convoluted, and isn't really a per-context thing, + # configure would be a better place to aggregate these. + if all( + [ + self._context.config.substs.get("OS_ARCH") == "WINNT", + not self._context.config.substs.get("GNU_CC"), + not self._context.config.substs.get("MOZ_DEBUG"), + ] + ): + + if self._context.config.substs.get("MOZ_OPTIMIZE"): + flags.append("-OPT:REF,ICF") + + return flags + + +class TargetCompileFlags(BaseCompileFlags): + """Base class that encapsulates some common logic between CompileFlags and + WasmCompileFlags. + """ + + def _debug_flags(self): + if self._context.config.substs.get( + "MOZ_DEBUG" + ) or self._context.config.substs.get("MOZ_DEBUG_SYMBOLS"): + return self._context.config.substs.get("MOZ_DEBUG_FLAGS", "").split() + return [] + + def _warnings_as_errors(self): + warnings_as_errors = self._context.config.substs.get("WARNINGS_AS_ERRORS") + if warnings_as_errors: + return [warnings_as_errors] + + def _optimize_flags(self): + if not self._context.config.substs.get("MOZ_OPTIMIZE"): + return [] + optimize_flags = None + if self._context.config.substs.get("MOZ_PGO"): + optimize_flags = self._context.config.substs.get("MOZ_PGO_OPTIMIZE_FLAGS") + if not optimize_flags: + # If MOZ_PGO_OPTIMIZE_FLAGS is empty we fall back to + # MOZ_OPTIMIZE_FLAGS. Presently this occurs on Windows. + optimize_flags = self._context.config.substs.get("MOZ_OPTIMIZE_FLAGS") + return optimize_flags + + def __setitem__(self, key, value): + if key not in self._known_keys: + raise ValueError( + "Invalid value. `%s` is not a compile flags " "category." % key + ) + if key in self and self[key] is None: + raise ValueError( + "`%s` may not be set in COMPILE_FLAGS from moz.build, this " + "value is resolved from the emitter." % key + ) + if not ( + isinstance(value, list) + and all(isinstance(v, six.string_types) for v in value) + ): + raise ValueError( + "A list of strings must be provided as a value for a compile " + "flags category." + ) + dict.__setitem__(self, key, value) + + +class CompileFlags(TargetCompileFlags): + def __init__(self, context): + main_src_dir = mozpath.dirname(context.main_path) + self._context = context + + self.flag_variables = ( + ("STL", context.config.substs.get("STL_FLAGS"), ("CXXFLAGS",)), + ( + "VISIBILITY", + context.config.substs.get("VISIBILITY_FLAGS"), + ("CXXFLAGS", "CFLAGS"), + ), + ( + "MOZ_HARDENING_CFLAGS", + context.config.substs.get("MOZ_HARDENING_CFLAGS"), + ("CXXFLAGS", "CFLAGS", "CXX_LDFLAGS", "C_LDFLAGS"), + ), + ("DEFINES", None, ("CXXFLAGS", "CFLAGS")), + ("LIBRARY_DEFINES", None, ("CXXFLAGS", "CFLAGS")), + ( + "BASE_INCLUDES", + ["-I%s" % main_src_dir, "-I%s" % context.objdir], + ("CXXFLAGS", "CFLAGS"), + ), + ("LOCAL_INCLUDES", None, ("CXXFLAGS", "CFLAGS")), + ( + "EXTRA_INCLUDES", + ["-I%s/dist/include" % context.config.topobjdir], + ("CXXFLAGS", "CFLAGS"), + ), + ( + "OS_INCLUDES", + list( + itertools.chain( + *( + context.config.substs.get(v, []) + for v in ( + "NSPR_CFLAGS", + "NSS_CFLAGS", + "MOZ_JPEG_CFLAGS", + "MOZ_PNG_CFLAGS", + "MOZ_ZLIB_CFLAGS", + "MOZ_PIXMAN_CFLAGS", + "MOZ_ICU_CFLAGS", + ) + ) + ) + ), + ("CXXFLAGS", "CFLAGS"), + ), + ("RTL", None, ("CXXFLAGS", "CFLAGS")), + ( + "OS_COMPILE_CFLAGS", + context.config.substs.get("OS_COMPILE_CFLAGS"), + ("CFLAGS",), + ), + ( + "OS_COMPILE_CXXFLAGS", + context.config.substs.get("OS_COMPILE_CXXFLAGS"), + ("CXXFLAGS",), + ), + ( + "OS_CPPFLAGS", + context.config.substs.get("OS_CPPFLAGS"), + ("CXXFLAGS", "CFLAGS", "CXX_LDFLAGS", "C_LDFLAGS"), + ), + ( + "OS_CFLAGS", + context.config.substs.get("OS_CFLAGS"), + ("CFLAGS", "C_LDFLAGS"), + ), + ( + "OS_CXXFLAGS", + context.config.substs.get("OS_CXXFLAGS"), + ("CXXFLAGS", "CXX_LDFLAGS"), + ), + ( + "DEBUG", + self._debug_flags(), + ("CFLAGS", "CXXFLAGS", "CXX_LDFLAGS", "C_LDFLAGS"), + ), + ( + "CLANG_PLUGIN", + context.config.substs.get("CLANG_PLUGIN_FLAGS"), + ("CFLAGS", "CXXFLAGS", "CXX_LDFLAGS", "C_LDFLAGS"), + ), + ( + "OPTIMIZE", + self._optimize_flags(), + ("CFLAGS", "CXXFLAGS", "CXX_LDFLAGS", "C_LDFLAGS"), + ), + ( + "FRAMEPTR", + context.config.substs.get("MOZ_FRAMEPTR_FLAGS"), + ("CFLAGS", "CXXFLAGS", "CXX_LDFLAGS", "C_LDFLAGS"), + ), + ( + "WARNINGS_AS_ERRORS", + self._warnings_as_errors(), + ("CXXFLAGS", "CFLAGS", "CXX_LDFLAGS", "C_LDFLAGS"), + ), + ( + "WARNINGS_CFLAGS", + context.config.substs.get("WARNINGS_CFLAGS"), + ("CFLAGS",), + ), + ( + "WARNINGS_CXXFLAGS", + context.config.substs.get("WARNINGS_CXXFLAGS"), + ("CXXFLAGS",), + ), + ("MOZBUILD_CFLAGS", None, ("CFLAGS",)), + ("MOZBUILD_CXXFLAGS", None, ("CXXFLAGS",)), + ( + "COVERAGE", + context.config.substs.get("COVERAGE_CFLAGS"), + ("CXXFLAGS", "CFLAGS"), + ), + ( + "PASS_MANAGER", + context.config.substs.get("MOZ_PASS_MANAGER_FLAGS"), + ("CXXFLAGS", "CFLAGS"), + ), + ( + "FILE_PREFIX_MAP", + context.config.substs.get("MOZ_FILE_PREFIX_MAP_FLAGS"), + ("CXXFLAGS", "CFLAGS"), + ), + ( + # See bug 414641 + "NO_STRICT_ALIASING", + ["-fno-strict-aliasing"], + ("CXXFLAGS", "CFLAGS"), + ), + ( + # Disable floating-point contraction by default. + "FP_CONTRACT", + ( + ["-Xclang"] + if context.config.substs.get("CC_TYPE") == "clang-cl" + else [] + ) + + ["-ffp-contract=off"], + ("CXXFLAGS", "CFLAGS"), + ), + ) + + TargetCompileFlags.__init__(self, context) + + +class WasmFlags(TargetCompileFlags): + def __init__(self, context): + main_src_dir = mozpath.dirname(context.main_path) + self._context = context + + self.flag_variables = ( + ("LIBRARY_DEFINES", None, ("WASM_CXXFLAGS", "WASM_CFLAGS")), + ( + "BASE_INCLUDES", + ["-I%s" % main_src_dir, "-I%s" % context.objdir], + ("WASM_CXXFLAGS", "WASM_CFLAGS"), + ), + ("LOCAL_INCLUDES", None, ("WASM_CXXFLAGS", "WASM_CFLAGS")), + ( + "EXTRA_INCLUDES", + ["-I%s/dist/include" % context.config.topobjdir], + ("WASM_CXXFLAGS", "WASM_CFLAGS"), + ), + ( + "OS_INCLUDES", + list( + itertools.chain( + *( + context.config.substs.get(v, []) + for v in ( + "NSPR_CFLAGS", + "NSS_CFLAGS", + "MOZ_JPEG_CFLAGS", + "MOZ_PNG_CFLAGS", + "MOZ_ZLIB_CFLAGS", + "MOZ_PIXMAN_CFLAGS", + ) + ) + ) + ), + ("WASM_CXXFLAGS", "WASM_CFLAGS"), + ), + ("DEBUG", self._debug_flags(), ("WASM_CFLAGS", "WASM_CXXFLAGS")), + ( + "CLANG_PLUGIN", + context.config.substs.get("CLANG_PLUGIN_FLAGS"), + ("WASM_CFLAGS", "WASM_CXXFLAGS"), + ), + ("OPTIMIZE", self._optimize_flags(), ("WASM_CFLAGS", "WASM_CXXFLAGS")), + ( + "WARNINGS_AS_ERRORS", + self._warnings_as_errors(), + ("WASM_CXXFLAGS", "WASM_CFLAGS"), + ), + ("MOZBUILD_CFLAGS", None, ("WASM_CFLAGS",)), + ("MOZBUILD_CXXFLAGS", None, ("WASM_CXXFLAGS",)), + ("WASM_CFLAGS", context.config.substs.get("WASM_CFLAGS"), ("WASM_CFLAGS",)), + ( + "WASM_CXXFLAGS", + context.config.substs.get("WASM_CXXFLAGS"), + ("WASM_CXXFLAGS",), + ), + ("WASM_DEFINES", None, ("WASM_CFLAGS", "WASM_CXXFLAGS")), + ("MOZBUILD_WASM_CFLAGS", None, ("WASM_CFLAGS",)), + ("MOZBUILD_WASM_CXXFLAGS", None, ("WASM_CXXFLAGS",)), + ( + "NEWPM", + context.config.substs.get("MOZ_NEW_PASS_MANAGER_FLAGS"), + ("WASM_CFLAGS", "WASM_CXXFLAGS"), + ), + ( + "FILE_PREFIX_MAP", + context.config.substs.get("MOZ_FILE_PREFIX_MAP_FLAGS"), + ("WASM_CFLAGS", "WASM_CXXFLAGS"), + ), + ("STL", context.config.substs.get("STL_FLAGS"), ("WASM_CXXFLAGS",)), + ) + + TargetCompileFlags.__init__(self, context) + + def _debug_flags(self): + substs = self._context.config.substs + if substs.get("MOZ_DEBUG") or substs.get("MOZ_DEBUG_SYMBOLS"): + return ["-g"] + return [] + + def _optimize_flags(self): + if not self._context.config.substs.get("MOZ_OPTIMIZE"): + return [] + + # We don't want `MOZ_{PGO_,}OPTIMIZE_FLAGS here because they may contain + # optimization flags that aren't suitable for wasm (e.g. -freorder-blocks). + # Just optimize for size in all cases; we may want to make this + # configurable. + return ["-Os"] + + +class FinalTargetValue(ContextDerivedValue, six.text_type): + def __new__(cls, context, value=""): + if not value: + value = "dist/" + if context["XPI_NAME"]: + value += "xpi-stage/" + context["XPI_NAME"] + else: + value += "bin" + if context["DIST_SUBDIR"]: + value += "/" + context["DIST_SUBDIR"] + return six.text_type.__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 PathMeta(type): + """Meta class for the Path family of classes. + + It handles calling __new__ with the right arguments in cases where a Path + is instantiated with another instance of Path instead of having received a + context. + + It also makes Path(context, value) instantiate one of the + subclasses depending on the value, allowing callers to do + standard type checking (isinstance(path, ObjDirPath)) instead + of checking the value itself (path.startswith('!')). + """ + + def __call__(cls, context, value=None): + if isinstance(context, Path): + assert value is None + value = context + context = context.context + else: + assert isinstance(context, Context) + if isinstance(value, Path): + context = value.context + if not issubclass(cls, (SourcePath, ObjDirPath, AbsolutePath)): + if value.startswith("!"): + cls = ObjDirPath + elif value.startswith("%"): + cls = AbsolutePath + else: + cls = SourcePath + return super(PathMeta, cls).__call__(context, value) + + +class Path(six.with_metaclass(PathMeta, ContextDerivedValue, six.text_type)): + """Stores and resolves a source path relative to a given context + + This class is used as a backing type for some of the sandbox variables. + It expresses paths relative to a context. Supported paths are: + - '/topsrcdir/relative/paths' + - 'srcdir/relative/paths' + - '!/topobjdir/relative/paths' + - '!objdir/relative/paths' + - '%/filesystem/absolute/paths' + """ + + def __new__(cls, context, value=None): + self = super(Path, cls).__new__(cls, value) + self.context = context + self.srcdir = context.srcdir + return self + + def join(self, *p): + """ContextDerived equivalent of `mozpath.join(self, *p)`, returning a + new Path instance. + """ + return Path(self.context, mozpath.join(self, *p)) + + def __cmp__(self, other): + # We expect this function to never be called to avoid issues in the + # switch from Python 2 to 3. + raise AssertionError() + + def _cmp(self, other, op): + if isinstance(other, Path) and self.srcdir != other.srcdir: + return op(self.full_path, other.full_path) + return op(six.text_type(self), other) + + def __eq__(self, other): + return self._cmp(other, operator.eq) + + def __ne__(self, other): + return self._cmp(other, operator.ne) + + def __lt__(self, other): + return self._cmp(other, operator.lt) + + def __gt__(self, other): + return self._cmp(other, operator.gt) + + def __le__(self, other): + return self._cmp(other, operator.le) + + def __ge__(self, other): + return self._cmp(other, operator.ge) + + def __repr__(self): + return "<%s (%s)%s>" % (self.__class__.__name__, self.srcdir, self) + + def __hash__(self): + return hash(self.full_path) + + @memoized_property + def target_basename(self): + return mozpath.basename(self.full_path) + + +class SourcePath(Path): + """Like Path, but limited to paths in the source directory.""" + + def __new__(cls, context, value=None): + if value.startswith("!"): + raise ValueError(f'Object directory paths are not allowed\nPath: "{value}"') + if value.startswith("%"): + raise ValueError( + f'Filesystem absolute paths are not allowed\nPath: "{value}"' + ) + self = super(SourcePath, cls).__new__(cls, context, value) + + if value.startswith("/"): + path = None + if not path or not os.path.exists(path): + path = mozpath.join(context.config.topsrcdir, value[1:]) + else: + path = mozpath.join(self.srcdir, value) + self.full_path = mozpath.normpath(path) + return self + + @memoized_property + def translated(self): + """Returns the corresponding path in the objdir. + + Ideally, we wouldn't need this function, but the fact that both source + path under topsrcdir and the external source dir end up mixed in the + objdir (aka pseudo-rework), this is needed. + """ + return ObjDirPath(self.context, "!%s" % self).full_path + + +class RenamedSourcePath(SourcePath): + """Like SourcePath, but with a different base name when installed. + + The constructor takes a tuple of (source, target_basename). + + This class is not meant to be exposed to moz.build sandboxes as of now, + and is not supported by the RecursiveMake backend. + """ + + def __new__(cls, context, value): + assert isinstance(value, tuple) + source, target_basename = value + self = super(RenamedSourcePath, cls).__new__(cls, context, source) + self._target_basename = target_basename + return self + + @property + def target_basename(self): + return self._target_basename + + +class ObjDirPath(Path): + """Like Path, but limited to paths in the object directory.""" + + def __new__(cls, context, value=None): + if not value.startswith("!"): + raise ValueError("Object directory paths must start with ! prefix") + self = super(ObjDirPath, cls).__new__(cls, context, value) + + if value.startswith("!/"): + path = mozpath.join(context.config.topobjdir, value[2:]) + else: + path = mozpath.join(context.objdir, value[1:]) + self.full_path = mozpath.normpath(path) + return self + + +class AbsolutePath(Path): + """Like Path, but allows arbitrary paths outside the source and object directories.""" + + def __new__(cls, context, value=None): + if not value.startswith("%"): + raise ValueError("Absolute paths must start with % prefix") + if not os.path.isabs(value[1:]): + raise ValueError("Path '%s' is not absolute" % value[1:]) + self = super(AbsolutePath, cls).__new__(cls, context, value) + self.full_path = mozpath.normpath(value[1:]) + return self + + +@memoize +def ContextDerivedTypedList(klass, base_class=List): + """Specialized TypedList for use with ContextDerivedValue types.""" + assert issubclass(klass, ContextDerivedValue) + + class _TypedList(ContextDerivedValue, TypedList(klass, base_class)): + def __init__(self, context, iterable=[], **kwargs): + self.context = context + super(_TypedList, self).__init__(iterable, **kwargs) + + def normalize(self, e): + if not isinstance(e, klass): + e = klass(self.context, e) + return e + + return _TypedList + + +@memoize +def ContextDerivedTypedListWithItems(type, base_class=List): + """Specialized TypedList for use with ContextDerivedValue types.""" + + class _TypedListWithItems(ContextDerivedTypedList(type, base_class)): + def __getitem__(self, name): + name = self.normalize(name) + return super(_TypedListWithItems, self).__getitem__(name) + + return _TypedListWithItems + + +@memoize +def ContextDerivedTypedRecord(*fields): + """Factory for objects with certain properties and dynamic + type checks. + + This API is extremely similar to the TypedNamedTuple API, + except that properties may be mutated. This supports syntax like: + + .. code-block:: python + + VARIABLE_NAME.property += [ + 'item1', + 'item2', + ] + """ + + class _TypedRecord(ContextDerivedValue): + __slots__ = tuple([name for name, _ in fields]) + + def __init__(self, context): + for fname, ftype in self._fields.items(): + if issubclass(ftype, ContextDerivedValue): + setattr(self, fname, self._fields[fname](context)) + else: + setattr(self, fname, self._fields[fname]()) + + def __setattr__(self, name, value): + if name in self._fields and not isinstance(value, self._fields[name]): + value = self._fields[name](value) + object.__setattr__(self, name, value) + + _TypedRecord._fields = dict(fields) + return _TypedRecord + + +class Schedules(object): + """Similar to a ContextDerivedTypedRecord, but with different behavior + for the properties: + + * VAR.inclusive can only be appended to (+=), and can only contain values + from mozbuild.schedules.INCLUSIVE_COMPONENTS + + * VAR.exclusive can only be assigned to (no +=), and can only contain + values from mozbuild.schedules.ALL_COMPONENTS + """ + + __slots__ = ("_exclusive", "_inclusive") + + def __init__(self, inclusive=None, exclusive=None): + if inclusive is None: + self._inclusive = TypedList(Enum(*schedules.INCLUSIVE_COMPONENTS))() + else: + self._inclusive = inclusive + if exclusive is None: + self._exclusive = ImmutableStrictOrderingOnAppendList( + schedules.EXCLUSIVE_COMPONENTS + ) + else: + self._exclusive = exclusive + + # inclusive is mutable but cannot be assigned to (+= only) + @property + def inclusive(self): + return self._inclusive + + @inclusive.setter + def inclusive(self, value): + if value is not self._inclusive: + raise AttributeError("Cannot assign to this value - use += instead") + unexpected = [v for v in value if v not in schedules.INCLUSIVE_COMPONENTS] + if unexpected: + raise Exception( + "unexpected inclusive component(s) " + ", ".join(unexpected) + ) + + # exclusive is immutable but can be set (= only) + @property + def exclusive(self): + return self._exclusive + + @exclusive.setter + def exclusive(self, value): + if not isinstance(value, (tuple, list)): + raise Exception("expected a tuple or list") + unexpected = [v for v in value if v not in schedules.ALL_COMPONENTS] + if unexpected: + raise Exception( + "unexpected exclusive component(s) " + ", ".join(unexpected) + ) + self._exclusive = ImmutableStrictOrderingOnAppendList(sorted(value)) + + # components provides a synthetic summary of all components + @property + def components(self): + return list(sorted(set(self._inclusive) | set(self._exclusive))) + + # The `Files` context uses | to combine SCHEDULES from multiple levels; at this + # point the immutability is no longer needed so we use plain lists + def __or__(self, other): + inclusive = self._inclusive + other._inclusive + if other._exclusive == self._exclusive: + exclusive = self._exclusive + elif self._exclusive == schedules.EXCLUSIVE_COMPONENTS: + exclusive = other._exclusive + elif other._exclusive == schedules.EXCLUSIVE_COMPONENTS: + exclusive = self._exclusive + else: + # in a case where two SCHEDULES.exclusive set different values, take + # the later one; this acts the way we expect assignment to work. + exclusive = other._exclusive + return Schedules(inclusive=inclusive, exclusive=exclusive) + + +@memoize +def ContextDerivedTypedHierarchicalStringList(type): + """Specialized HierarchicalStringList for use with ContextDerivedValue + types.""" + + class _TypedListWithItems(ContextDerivedValue, HierarchicalStringList): + __slots__ = ("_strings", "_children", "_context") + + def __init__(self, context): + self._strings = ContextDerivedTypedList(type, StrictOrderingOnAppendList)( + context + ) + self._children = {} + self._context = context + + def _get_exportvariable(self, name): + child = self._children.get(name) + if not child: + child = self._children[name] = _TypedListWithItems(self._context) + return child + + return _TypedListWithItems + + +def OrderedPathListWithAction(action): + """Returns a class which behaves as a StrictOrderingOnAppendList, but + invokes the given callable with each input and a context as it is + read, storing a tuple including the result and the original item. + + This used to extend moz.build reading to make more data available in + filesystem-reading mode. + """ + + class _OrderedListWithAction( + ContextDerivedTypedList(SourcePath, StrictOrderingOnAppendListWithAction) + ): + def __init__(self, context, *args): + def _action(item): + return item, action(context, item) + + super(_OrderedListWithAction, self).__init__(context, action=_action, *args) + + return _OrderedListWithAction + + +ManifestparserManifestList = OrderedPathListWithAction(read_manifestparser_manifest) +ReftestManifestList = OrderedPathListWithAction(read_reftest_manifest) + +BugzillaComponent = TypedNamedTuple( + "BugzillaComponent", [("product", six.text_type), ("component", six.text_type)] +) +SchedulingComponents = ContextDerivedTypedRecord( + ("inclusive", TypedList(six.text_type, StrictOrderingOnAppendList)), + ("exclusive", TypedList(six.text_type, StrictOrderingOnAppendList)), +) + +GeneratedFilesList = StrictOrderingOnAppendListWithFlagsFactory( + {"script": six.text_type, "inputs": list, "force": bool, "flags": list} +) + + +class Files(SubContext): + """Metadata attached to files. + + It is common to want to annotate files with metadata, such as which + Bugzilla component tracks issues with certain files. This sub-context is + where we stick that metadata. + + The argument to this sub-context is a file matching pattern that is applied + against the host file's directory. If the pattern matches a file whose info + is currently being sought, the metadata attached to this instance will be + applied to that file. + + Patterns are collections of filename characters with ``/`` used as the + directory separate (UNIX-style paths) and ``*`` and ``**`` used to denote + wildcard matching. + + Patterns without the ``*`` character are literal matches and will match at + most one entity. + + Patterns with ``*`` or ``**`` are wildcard matches. ``*`` matches files + at least within a single directory. ``**`` matches files across several + directories. + + ``foo.html`` + Will match only the ``foo.html`` file in the current directory. + ``*.jsm`` + Will match all ``.jsm`` files in the current directory. + ``**/*.cpp`` + Will match all ``.cpp`` files in this and all child directories. + ``foo/*.css`` + Will match all ``.css`` files in the ``foo/`` directory. + ``bar/*`` + Will match all files in the ``bar/`` directory and all of its + children directories. + ``bar/**`` + This is equivalent to ``bar/*`` above. + ``bar/**/foo`` + Will match all ``foo`` files in the ``bar/`` directory and all of its + children directories. + + The difference in behavior between ``*`` and ``**`` is only evident if + a pattern follows the ``*`` or ``**``. A pattern ending with ``*`` is + greedy. ``**`` is needed when you need an additional pattern after the + wildcard. e.g. ``**/foo``. + """ + + VARIABLES = { + "BUG_COMPONENT": ( + BugzillaComponent, + tuple, + """The bug component that tracks changes to these files. + + Values are a 2-tuple of unicode describing the Bugzilla product and + component. e.g. ``('Firefox Build System', 'General')``. + """, + ), + "FINAL": ( + bool, + bool, + """Mark variable assignments as finalized. + + During normal processing, values from newer Files contexts + overwrite previously set values. Last write wins. This behavior is + not always desired. ``FINAL`` provides a mechanism to prevent + further updates to a variable. + + When ``FINAL`` is set, the value of all variables defined in this + context are marked as frozen and all subsequent writes to them + are ignored during metadata reading. + + See :ref:`mozbuild_files_metadata_finalizing` for more info. + """, + ), + "SCHEDULES": ( + Schedules, + list, + """Maps source files to the CI tasks that should be scheduled when + they change. The tasks are grouped by named components, and those + names appear again in the taskgraph configuration + `($topsrcdir/taskgraph/). + + Some components are "inclusive", meaning that changes to most files + do not schedule them, aside from those described in a Files + subcontext. For example, py-lint tasks need not be scheduled for + most changes, but should be scheduled when any Python file changes. + Such components are named by appending to `SCHEDULES.inclusive`: + + with Files('**.py'): + SCHEDULES.inclusive += ['py-lint'] + + Other components are 'exclusive', meaning that changes to most + files schedule them, but some files affect only one or two + components. For example, most files schedule builds and tests of + Firefox for Android, OS X, Windows, and Linux, but files under + `mobile/android/` affect Android builds and tests exclusively, so + builds for other operating systems are not needed. Test suites + provide another example: most files schedule reftests, but changes + to reftest scripts need only schedule reftests and no other suites. + + Exclusive components are named by setting `SCHEDULES.exclusive`: + + with Files('mobile/android/**'): + SCHEDULES.exclusive = ['android'] + """, + ), + } + + def __init__(self, parent, *patterns): + super(Files, self).__init__(parent) + self.patterns = patterns + self.finalized = set() + + def __iadd__(self, other): + assert isinstance(other, Files) + + for k, v in other.items(): + if k == "SCHEDULES" and "SCHEDULES" in self: + self["SCHEDULES"] = self["SCHEDULES"] | v + continue + + # Ignore updates to finalized flags. + if k in self.finalized: + continue + + # Only finalize variables defined in this instance. + if k == "FINAL": + self.finalized |= set(other) - {"FINAL"} + continue + + self[k] = v + + return self + + def asdict(self): + """Return this instance as a dict with built-in data structures. + + Call this to obtain an object suitable for serializing. + """ + d = {} + if "BUG_COMPONENT" in self: + bc = self["BUG_COMPONENT"] + d["bug_component"] = (bc.product, bc.component) + + return d + + @staticmethod + def aggregate(files): + """Given a mapping of path to Files, obtain aggregate results. + + Consumers may want to extract useful information from a collection of + Files describing paths. e.g. given the files info data for N paths, + recommend a single bug component based on the most frequent one. This + function provides logic for deriving aggregate knowledge from a + collection of path File metadata. + + Note: the intent of this function is to operate on the result of + :py:func:`mozbuild.frontend.reader.BuildReader.files_info`. The + :py:func:`mozbuild.frontend.context.Files` instances passed in are + thus the "collapsed" (``__iadd__``ed) results of all ``Files`` from all + moz.build files relevant to a specific path, not individual ``Files`` + instances from a single moz.build file. + """ + d = {} + + bug_components = Counter() + + for f in files.values(): + bug_component = f.get("BUG_COMPONENT") + if bug_component: + bug_components[bug_component] += 1 + + d["bug_component_counts"] = [] + for c, count in bug_components.most_common(): + component = (c.product, c.component) + d["bug_component_counts"].append((c, count)) + + if "recommended_bug_component" not in d: + d["recommended_bug_component"] = component + recommended_count = count + elif count == recommended_count: + # Don't recommend a component if it doesn't have a clear lead. + d["recommended_bug_component"] = None + + # In case no bug components. + d.setdefault("recommended_bug_component", None) + + return d + + +# This defines functions that create sub-contexts. +# +# Values are classes that are SubContexts. The class name will be turned into +# a function that when called emits an instance of that class. +# +# Arbitrary arguments can be passed to the class constructor. The first +# argument is always the parent context. It is up to each class to perform +# argument validation. +SUBCONTEXTS = [Files] + +for cls in SUBCONTEXTS: + if not issubclass(cls, SubContext): + raise ValueError("SUBCONTEXTS entry not a SubContext class: %s" % cls) + + if not hasattr(cls, "VARIABLES"): + raise ValueError("SUBCONTEXTS entry does not have VARIABLES: %s" % cls) + +SUBCONTEXTS = {cls.__name__: cls for cls in SUBCONTEXTS} + + +# This defines the set of mutable global variables. +# +# Each variable is a tuple of: +# +# (storage_type, input_types, docs) + +VARIABLES = { + "SOURCES": ( + ContextDerivedTypedListWithItems( + Path, + StrictOrderingOnAppendListWithFlagsFactory({"no_pgo": bool, "flags": List}), + ), + list, + """Source code files. + + This variable contains a list of source code files to compile. + Accepts assembler, C, C++, Objective C/C++. + """, + ), + "FILES_PER_UNIFIED_FILE": ( + int, + int, + """The number of source files to compile into each unified source file. + + """, + ), + "IS_RUST_LIBRARY": ( + bool, + bool, + """Whether the current library defined by this moz.build is built by Rust. + + The library defined by this moz.build should have a build definition in + a Cargo.toml file that exists in this moz.build's directory. + """, + ), + "IS_GKRUST": ( + bool, + bool, + """Whether the current library defined by this moz.build is gkrust. + + Indicates whether the current library contains rust for libxul. + """, + ), + "RUST_LIBRARY_FEATURES": ( + List, + list, + """Cargo features to activate for this library. + + This variable should not be used directly; you should be using the + RustLibrary template instead. + """, + ), + "HOST_RUST_LIBRARY_FEATURES": ( + List, + list, + """Cargo features to activate for this host library. + + This variable should not be used directly; you should be using the + HostRustLibrary template instead. + """, + ), + "RUST_TESTS": ( + TypedList(six.text_type), + list, + """Names of Rust tests to build and run via `cargo test`. + """, + ), + "RUST_TEST_FEATURES": ( + TypedList(six.text_type), + list, + """Cargo features to activate for RUST_TESTS. + """, + ), + "UNIFIED_SOURCES": ( + ContextDerivedTypedList(Path, StrictOrderingOnAppendList), + list, + """Source code files that can be compiled together. + + This variable contains a list of source code files to compile, + that can be concatenated all together and built as a single source + file. This can help make the build faster and reduce the debug info + size. + """, + ), + "GENERATED_FILES": ( + GeneratedFilesList, + list, + """Generic generated files. + + Unless you have a reason not to, use the GeneratedFile template rather + than referencing GENERATED_FILES directly. The GeneratedFile template + has all the same arguments as the attributes listed below (``script``, + ``inputs``, ``flags``, ``force``), plus an additional ``entry_point`` + argument to specify a particular function to run in the given script. + + This variable contains a list of files for the build system to + generate at export time. The generation method may be declared + with optional ``script``, ``inputs``, ``flags``, and ``force`` + attributes on individual entries. + If the optional ``script`` attribute is not present on an entry, it + is assumed that rules for generating the file are present in + the associated Makefile.in. + + Example:: + + GENERATED_FILES += ['bar.c', 'baz.c', 'foo.c'] + bar = GENERATED_FILES['bar.c'] + bar.script = 'generate.py' + bar.inputs = ['datafile-for-bar'] + foo = GENERATED_FILES['foo.c'] + foo.script = 'generate.py' + foo.inputs = ['datafile-for-foo'] + + This definition will generate bar.c by calling the main method of + generate.py with a open (for writing) file object for bar.c, and + the string ``datafile-for-bar``. In a similar fashion, the main + method of generate.py will also be called with an open + (for writing) file object for foo.c and the string + ``datafile-for-foo``. Please note that only string arguments are + 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' + + The chosen script entry point may optionally return a set of strings, + indicating extra files the output depends on. + + When the ``flags`` attribute is present, the given list of flags is + passed as extra arguments following the inputs. + + When the ``force`` attribute is present, the file is generated every + build, regardless of whether it is stale. This is special to the + RecursiveMake backend and intended for special situations only (e.g., + localization). Please consult a build peer (on the #build channel at + https://chat.mozilla.org) before using ``force``. + """, + ), + "DEFINES": ( + InitializedDefines, + dict, + """Dictionary of compiler defines to declare. + + These are passed in to the compiler as ``-Dkey='value'`` for string + values, ``-Dkey=value`` for numeric values, or ``-Dkey`` if the + value is True. Note that for string values, the outer-level of + single-quotes will be consumed by the shell. If you want to have + a string-literal in the program, the value needs to have + double-quotes. + + Example:: + + DEFINES['NS_NO_XPCOM'] = True + DEFINES['MOZ_EXTENSIONS_DB_SCHEMA'] = 15 + DEFINES['DLL_SUFFIX'] = '".so"' + + This will result in the compiler flags ``-DNS_NO_XPCOM``, + ``-DMOZ_EXTENSIONS_DB_SCHEMA=15``, and ``-DDLL_SUFFIX='".so"'``, + respectively. + + Note that these entries are not necessarily passed to the assembler. + Whether they are depends on the type of assembly file. As an + alternative, you may add a ``-DKEY=value`` entry to ``ASFLAGS``. + """, + ), + "DELAYLOAD_DLLS": ( + List, + list, + """Delay-loaded DLLs. + + This variable contains a list of DLL files which the module being linked + should load lazily. This only has an effect when building with MSVC. + """, + ), + "DIRS": ( + ContextDerivedTypedList(SourcePath), + list, + """Child directories to descend into looking for build frontend files. + + This works similarly to the ``DIRS`` variable in make files. Each str + value in the list is the name of a child directory. When this file is + done parsing, the build reader will descend into each listed directory + and read the frontend file there. If there is no frontend file, an error + is raised. + + Values are relative paths. They can be multiple directory levels + above or below. Use ``..`` for parent directories and ``/`` for path + delimiters. + """, + ), + "FINAL_TARGET_FILES": ( + ContextDerivedTypedHierarchicalStringList(Path), + list, + """List of files to be installed into the application directory. + + ``FINAL_TARGET_FILES`` will copy (or symlink, if the platform supports it) + the contents of its files to the directory specified by + ``FINAL_TARGET`` (typically ``dist/bin``). Files that are destined for a + subdirectory can be specified by accessing a field, or as a dict access. + For example, to export ``foo.png`` to the top-level directory and + ``bar.svg`` to the directory ``images/do-not-use``, append to + ``FINAL_TARGET_FILES`` like so:: + + FINAL_TARGET_FILES += ['foo.png'] + FINAL_TARGET_FILES.images['do-not-use'] += ['bar.svg'] + """, + ), + "FINAL_TARGET_PP_FILES": ( + ContextDerivedTypedHierarchicalStringList(Path), + list, + """Like ``FINAL_TARGET_FILES``, with preprocessing. + """, + ), + "LOCALIZED_FILES": ( + ContextDerivedTypedHierarchicalStringList(Path), + list, + """List of locale-dependent files to be installed into the application + directory. + + This functions similarly to ``FINAL_TARGET_FILES``, but the files are + sourced from the locale directory and will vary per localization. + For an en-US build, this is functionally equivalent to + ``FINAL_TARGET_FILES``. For a build with ``--enable-ui-locale``, + the file will be taken from ``$LOCALE_SRCDIR``, with the leading + ``en-US`` removed. For a l10n repack of an en-US build, the file + will be taken from the first location where it exists from: + * the merged locale directory if it exists + * ``$LOCALE_SRCDIR`` with the leading ``en-US`` removed + * the in-tree en-US location + + Source directory paths specified here must must include a leading ``en-US``. + Wildcards are allowed, and will be expanded at the time of locale packaging to match + files in the locale directory. + + Object directory paths are allowed here only if the path matches an entry in + ``LOCALIZED_GENERATED_FILES``. + + Files that are missing from a locale will typically have the en-US + version used, but for wildcard expansions only files from the + locale directory will be used, even if that means no files will + be copied. + + Example:: + + LOCALIZED_FILES.foo += [ + 'en-US/foo.js', + 'en-US/things/*.ini', + ] + + If this was placed in ``toolkit/locales/moz.build``, it would copy + ``toolkit/locales/en-US/foo.js`` and + ``toolkit/locales/en-US/things/*.ini`` to ``$(DIST)/bin/foo`` in an + en-US build, and in a build of a different locale (or a repack), + it would copy ``$(LOCALE_SRCDIR)/toolkit/foo.js`` and + ``$(LOCALE_SRCDIR)/toolkit/things/*.ini``. + """, + ), + "LOCALIZED_PP_FILES": ( + ContextDerivedTypedHierarchicalStringList(Path), + list, + """Like ``LOCALIZED_FILES``, with preprocessing. + + Note that the ``AB_CD`` define is available and expands to the current + locale being packaged, as with preprocessed entries in jar manifests. + """, + ), + "LOCALIZED_GENERATED_FILES": ( + GeneratedFilesList, + list, + """Like ``GENERATED_FILES``, but for files whose content varies based on the locale in use. + + For simple cases of text substitution, prefer ``LOCALIZED_PP_FILES``. + + Refer to the documentation of ``GENERATED_FILES``; for the most part things work the same. + The two major differences are: + 1. The function in the Python script will be passed an additional keyword argument `locale` + which provides the locale in use, i.e. ``en-US``. + 2. The ``inputs`` list may contain paths to files that will be taken from the locale + source directory (see ``LOCALIZED_FILES`` for a discussion of the specifics). Paths + in ``inputs`` starting with ``en-US/`` or containing ``locales/en-US/`` are considered + localized files. + + To place the generated output file in a specific location, list its objdir path in + ``LOCALIZED_FILES``. + + In addition, ``LOCALIZED_GENERATED_FILES`` can use the special substitutions ``{AB_CD}`` + and ``{AB_rCD}`` in their output paths. ``{AB_CD}`` expands to the current locale during + multi-locale builds and single-locale repacks and ``{AB_rCD}`` expands to an + Android-specific encoding of the current locale. Both expand to the empty string when the + current locale is ``en-US``. + """, + ), + "OBJDIR_FILES": ( + ContextDerivedTypedHierarchicalStringList(Path), + list, + """List of files to be installed anywhere in the objdir. Use sparingly. + + ``OBJDIR_FILES`` is similar to FINAL_TARGET_FILES, but it allows copying + anywhere in the object directory. This is intended for various one-off + cases, not for general use. If you wish to add entries to OBJDIR_FILES, + please consult a build peer (on the #build channel at https://chat.mozilla.org). + """, + ), + "OBJDIR_PP_FILES": ( + ContextDerivedTypedHierarchicalStringList(Path), + list, + """Like ``OBJDIR_FILES``, with preprocessing. Use sparingly. + """, + ), + "FINAL_LIBRARY": ( + six.text_type, + six.text_type, + """Library in which the objects of the current directory will be linked. + + This variable contains the name of a library, defined elsewhere with + ``LIBRARY_NAME``, in which the objects of the current directory will be + linked. + """, + ), + "CPP_UNIT_TESTS": ( + StrictOrderingOnAppendList, + list, + """Compile a list of C++ unit test names. + + Each name in this variable corresponds to an executable built from the + corresponding source file with the same base name. + + If the configuration token ``BIN_SUFFIX`` is set, its value will be + automatically appended to each name. If a name already ends with + ``BIN_SUFFIX``, the name will remain unchanged. + """, + ), + "FORCE_SHARED_LIB": ( + bool, + bool, + """Whether the library in this directory is a shared library. + """, + ), + "FORCE_STATIC_LIB": ( + bool, + bool, + """Whether the library in this directory is a static library. + """, + ), + "USE_STATIC_LIBS": ( + bool, + bool, + """Whether the code in this directory is a built against the static + runtime library. + + This variable only has an effect when building with MSVC. + """, + ), + "HOST_SOURCES": ( + ContextDerivedTypedList(Path, StrictOrderingOnAppendList), + list, + """Source code files to compile with the host compiler. + + This variable contains a list of source code files to compile. + with the host compiler. + """, + ), + "WASM_SOURCES": ( + ContextDerivedTypedList(Path, StrictOrderingOnAppendList), + list, + """Source code files to compile with the wasm compiler. + """, + ), + "HOST_LIBRARY_NAME": ( + six.text_type, + six.text_type, + """Name of target library generated when cross compiling. + """, + ), + "LIBRARY_DEFINES": ( + OrderedDict, + dict, + """Dictionary of compiler defines to declare for the entire library. + + This variable works like DEFINES, except that declarations apply to all + libraries that link into this library via FINAL_LIBRARY. + """, + ), + "LIBRARY_NAME": ( + six.text_type, + six.text_type, + """The code name of the library generated for a directory. + + By default STATIC_LIBRARY_NAME and SHARED_LIBRARY_NAME take this name. + In ``example/components/moz.build``,:: + + LIBRARY_NAME = 'xpcomsample' + + would generate ``example/components/libxpcomsample.so`` on Linux, or + ``example/components/xpcomsample.lib`` on Windows. + """, + ), + "SHARED_LIBRARY_NAME": ( + six.text_type, + six.text_type, + """The name of the static library generated for a directory, if it needs to + differ from the library code name. + + Implies FORCE_SHARED_LIB. + """, + ), + "SANDBOXED_WASM_LIBRARY_NAME": ( + six.text_type, + six.text_type, + """The name of the static sandboxed wasm library generated for a directory. + """, + ), + "SHARED_LIBRARY_OUTPUT_CATEGORY": ( + six.text_type, + six.text_type, + """The output category for this context's shared library. If set this will + correspond to the build command that will build this shared library, and + the library will not be built as part of the default build. + """, + ), + "RUST_LIBRARY_OUTPUT_CATEGORY": ( + six.text_type, + six.text_type, + """The output category for this context's rust library. If set this will + correspond to the build command that will build this rust library, and + the library will not be built as part of the default build. + """, + ), + "IS_FRAMEWORK": ( + bool, + bool, + """Whether the library to build should be built as a framework on OSX. + + This implies the name of the library won't be prefixed nor suffixed. + Implies FORCE_SHARED_LIB. + """, + ), + "STATIC_LIBRARY_NAME": ( + six.text_type, + six.text_type, + """The name of the static library generated for a directory, if it needs to + differ from the library code name. + + Implies FORCE_STATIC_LIB. + """, + ), + "USE_LIBS": ( + StrictOrderingOnAppendList, + list, + """List of libraries to link to programs and libraries. + """, + ), + "HOST_USE_LIBS": ( + StrictOrderingOnAppendList, + list, + """List of libraries to link to host programs and libraries. + """, + ), + "HOST_OS_LIBS": ( + List, + list, + """List of system libraries for host programs and libraries. + """, + ), + "LOCAL_INCLUDES": ( + ContextDerivedTypedList(Path, StrictOrderingOnAppendList), + list, + """Additional directories to be searched for include files by the compiler. + """, + ), + "NO_PGO": ( + bool, + bool, + """Whether profile-guided optimization is disable in this directory. + """, + ), + "OS_LIBS": ( + List, + list, + """System link libraries. + + This variable contains a list of system libaries to link against. + """, + ), + "RCFILE": ( + Path, + six.text_type, + """The program .rc file. + + This variable can only be used on Windows. + """, + ), + "RCINCLUDE": ( + Path, + six.text_type, + """The resource script file to be included in the default .res file. + + This variable can only be used on Windows. + """, + ), + "DEFFILE": ( + Path, + six.text_type, + """The program .def (module definition) file. + + This variable can only be used on Windows. + """, + ), + "SYMBOLS_FILE": ( + Path, + six.text_type, + """A file containing a list of symbols to export from a shared library. + + The given file contains a list of symbols to be exported, and is + preprocessed. + A special marker "@DATA@" must be added after a symbol name if it + points to data instead of code, so that the Windows linker can treat + them correctly. + """, + ), + "SIMPLE_PROGRAMS": ( + StrictOrderingOnAppendList, + list, + """Compile a list of executable names. + + Each name in this variable corresponds to an executable built from the + corresponding source file with the same base name. + + If the configuration token ``BIN_SUFFIX`` is set, its value will be + automatically appended to each name. If a name already ends with + ``BIN_SUFFIX``, the name will remain unchanged. + """, + ), + "SONAME": ( + six.text_type, + six.text_type, + """The soname of the shared object currently being linked + + soname is the "logical name" of a shared object, often used to provide + version backwards compatibility. This variable makes sense only for + shared objects, and is supported only on some unix platforms. + """, + ), + "HOST_SIMPLE_PROGRAMS": ( + StrictOrderingOnAppendList, + list, + """Compile a list of host executable names. + + Each name in this variable corresponds to a hosst executable built + from the corresponding source file with the same base name. + + If the configuration token ``HOST_BIN_SUFFIX`` is set, its value will + be automatically appended to each name. If a name already ends with + ``HOST_BIN_SUFFIX``, the name will remain unchanged. + """, + ), + "RUST_PROGRAMS": ( + StrictOrderingOnAppendList, + list, + """Compile a list of Rust host executable names. + + Each name in this variable corresponds to an executable built from + the Cargo.toml in the same directory. + """, + ), + "HOST_RUST_PROGRAMS": ( + StrictOrderingOnAppendList, + list, + """Compile a list of Rust executable names. + + Each name in this variable corresponds to an executable built from + the Cargo.toml in the same directory. + """, + ), + "CONFIGURE_SUBST_FILES": ( + ContextDerivedTypedList(SourcePath, StrictOrderingOnAppendList), + list, + """Output files that will be generated using configure-like substitution. + + This is a substitute for ``AC_OUTPUT`` in autoconf. For each path in this + list, we will search for a file in the srcdir having the name + ``{path}.in``. The contents of this file will be read and variable + patterns like ``@foo@`` will be substituted with the values of the + ``AC_SUBST`` variables declared during configure. + """, + ), + "CONFIGURE_DEFINE_FILES": ( + ContextDerivedTypedList(SourcePath, StrictOrderingOnAppendList), + list, + """Output files generated from configure/config.status. + + This is a substitute for ``AC_CONFIG_HEADER`` in autoconf. This is very + similar to ``CONFIGURE_SUBST_FILES`` except the generation logic takes + into account the values of ``AC_DEFINE`` instead of ``AC_SUBST``. + """, + ), + "EXPORTS": ( + ContextDerivedTypedHierarchicalStringList(Path), + list, + """List of files to be exported, and in which subdirectories. + + ``EXPORTS`` is generally used to list the include files to be exported to + ``dist/include``, but it can be used for other files as well. This variable + behaves as a list when appending filenames for export in the top-level + directory. Files can also be appended to a field to indicate which + subdirectory they should be exported to. For example, to export + ``foo.h`` to the top-level directory, and ``bar.h`` to ``mozilla/dom/``, + append to ``EXPORTS`` like so:: + + EXPORTS += ['foo.h'] + EXPORTS.mozilla.dom += ['bar.h'] + + Entries in ``EXPORTS`` are paths, so objdir paths may be used, but + any files listed from the objdir must also be listed in + ``GENERATED_FILES``. + """, + ), + "PROGRAM": ( + six.text_type, + six.text_type, + """Compiled executable name. + + If the configuration token ``BIN_SUFFIX`` is set, its value will be + automatically appended to ``PROGRAM``. If ``PROGRAM`` already ends with + ``BIN_SUFFIX``, ``PROGRAM`` will remain unchanged. + """, + ), + "HOST_PROGRAM": ( + six.text_type, + six.text_type, + """Compiled host executable name. + + If the configuration token ``HOST_BIN_SUFFIX`` is set, its value will be + automatically appended to ``HOST_PROGRAM``. If ``HOST_PROGRAM`` already + ends with ``HOST_BIN_SUFFIX``, ``HOST_PROGRAM`` will remain unchanged. + """, + ), + "DIST_INSTALL": ( + Enum(None, False, True), + bool, + """Whether to install certain files into the dist directory. + + 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. + """, + ), + "JAR_MANIFESTS": ( + ContextDerivedTypedList(SourcePath, StrictOrderingOnAppendList), + list, + """JAR manifest files that should be processed as part of the build. + + JAR manifests are files in the tree that define how to package files + into JARs and how chrome registration is performed. For more info, + see :ref:`jar_manifests`. + """, + ), + # IDL Generation. + "XPIDL_SOURCES": ( + ContextDerivedTypedList(SourcePath, StrictOrderingOnAppendList), + list, + """XPCOM Interface Definition Files (xpidl). + + This is a list of files that define XPCOM interface definitions. + Entries must be files that exist. Entries are almost certainly ``.idl`` + files. + """, + ), + "XPIDL_MODULE": ( + six.text_type, + six.text_type, + """XPCOM Interface Definition Module Name. + + This is the name of the ``.xpt`` file that is created by linking + ``XPIDL_SOURCES`` together. If unspecified, it defaults to be the same + as ``MODULE``. + """, + ), + "XPCOM_MANIFESTS": ( + ContextDerivedTypedList(SourcePath, StrictOrderingOnAppendList), + list, + """XPCOM Component Manifest Files. + + This is a list of files that define XPCOM components to be added + to the component registry. + """, + ), + "PREPROCESSED_IPDL_SOURCES": ( + StrictOrderingOnAppendList, + list, + """Preprocessed IPDL source files. + + These files will be preprocessed, then parsed and converted to + ``.cpp`` files. + """, + ), + "IPDL_SOURCES": ( + StrictOrderingOnAppendList, + list, + """IPDL source files. + + These are ``.ipdl`` files that will be parsed and converted to + ``.cpp`` files. + """, + ), + "WEBIDL_FILES": ( + StrictOrderingOnAppendList, + list, + """WebIDL source files. + + These will be parsed and converted to ``.cpp`` and ``.h`` files. + """, + ), + "GENERATED_EVENTS_WEBIDL_FILES": ( + StrictOrderingOnAppendList, + list, + """WebIDL source files for generated events. + + These will be parsed and converted to ``.cpp`` and ``.h`` files. + """, + ), + "TEST_WEBIDL_FILES": ( + StrictOrderingOnAppendList, + list, + """Test WebIDL source files. + + These will be parsed and converted to ``.cpp`` and ``.h`` files + if tests are enabled. + """, + ), + "GENERATED_WEBIDL_FILES": ( + StrictOrderingOnAppendList, + list, + """Generated WebIDL source files. + + These will be generated from some other files. + """, + ), + "PREPROCESSED_TEST_WEBIDL_FILES": ( + StrictOrderingOnAppendList, + list, + """Preprocessed test WebIDL source files. + + These will be preprocessed, then parsed and converted to .cpp + and ``.h`` files if tests are enabled. + """, + ), + "PREPROCESSED_WEBIDL_FILES": ( + StrictOrderingOnAppendList, + list, + """Preprocessed WebIDL source files. + + These will be preprocessed before being parsed and converted. + """, + ), + "WEBIDL_EXAMPLE_INTERFACES": ( + StrictOrderingOnAppendList, + list, + """Names of example WebIDL interfaces to build as part of the build. + + Names in this list correspond to WebIDL interface names defined in + WebIDL files included in the build from one of the \*WEBIDL_FILES + variables. + """, + ), + # Test declaration. + "A11Y_MANIFESTS": ( + ManifestparserManifestList, + list, + """List of manifest files defining a11y tests. + """, + ), + "BROWSER_CHROME_MANIFESTS": ( + ManifestparserManifestList, + list, + """List of manifest files defining browser chrome tests. + """, + ), + "ANDROID_INSTRUMENTATION_MANIFESTS": ( + ManifestparserManifestList, + list, + """List of manifest files defining Android instrumentation tests. + """, + ), + "FIREFOX_UI_FUNCTIONAL_MANIFESTS": ( + ManifestparserManifestList, + list, + """List of manifest files defining firefox-ui-functional tests. + """, + ), + "MARIONETTE_LAYOUT_MANIFESTS": ( + ManifestparserManifestList, + list, + """List of manifest files defining marionette-layout tests. + """, + ), + "MARIONETTE_UNIT_MANIFESTS": ( + ManifestparserManifestList, + list, + """List of manifest files defining marionette-unit tests. + """, + ), + "METRO_CHROME_MANIFESTS": ( + ManifestparserManifestList, + list, + """List of manifest files defining metro browser chrome tests. + """, + ), + "MOCHITEST_CHROME_MANIFESTS": ( + ManifestparserManifestList, + list, + """List of manifest files defining mochitest chrome tests. + """, + ), + "MOCHITEST_MANIFESTS": ( + ManifestparserManifestList, + list, + """List of manifest files defining mochitest tests. + """, + ), + "REFTEST_MANIFESTS": ( + ReftestManifestList, + list, + """List of manifest files defining reftests. + + These are commonly named reftest.list. + """, + ), + "CRASHTEST_MANIFESTS": ( + ReftestManifestList, + list, + """List of manifest files defining crashtests. + + These are commonly named crashtests.list. + """, + ), + "XPCSHELL_TESTS_MANIFESTS": ( + ManifestparserManifestList, + list, + """List of manifest files defining xpcshell tests. + """, + ), + "PYTHON_UNITTEST_MANIFESTS": ( + ManifestparserManifestList, + list, + """List of manifest files defining python unit tests. + """, + ), + "PERFTESTS_MANIFESTS": ( + ManifestparserManifestList, + list, + """List of manifest files defining MozPerftest performance tests. + """, + ), + "CRAMTEST_MANIFESTS": ( + ManifestparserManifestList, + list, + """List of manifest files defining cram unit tests. + """, + ), + "TELEMETRY_TESTS_CLIENT_MANIFESTS": ( + ManifestparserManifestList, + list, + """List of manifest files defining telemetry client tests. + """, + ), + # The following variables are used to control the target of installed files. + "XPI_NAME": ( + six.text_type, + six.text_type, + """The name of an extension XPI to generate. + + When this variable is present, the results of this directory will end up + being packaged into an extension instead of the main dist/bin results. + """, + ), + "DIST_SUBDIR": ( + six.text_type, + six.text_type, + """The name of an alternate directory to install files to. + + When this variable is present, the results of this directory will end up + being placed in the $(DIST_SUBDIR) subdirectory of where it would + otherwise be placed. + """, + ), + "FINAL_TARGET": ( + FinalTargetValue, + six.text_type, + """The name of the directory to install targets to. + + The directory is relative to the top of the object directory. The + default value is dependent on the values of XPI_NAME and DIST_SUBDIR. If + neither are present, the result is dist/bin. If XPI_NAME is present, the + result is dist/xpi-stage/$(XPI_NAME). If DIST_SUBDIR is present, then + the $(DIST_SUBDIR) directory of the otherwise default value is used. + """, + ), + "USE_EXTENSION_MANIFEST": ( + bool, + bool, + """Controls the name of the manifest for JAR files. + + By default, the name of the manifest is ${JAR_MANIFEST}.manifest. + Setting this variable to ``True`` changes the name of the manifest to + chrome.manifest. + """, + ), + "GYP_DIRS": ( + StrictOrderingOnAppendListWithFlagsFactory( + { + "variables": dict, + "input": six.text_type, + "sandbox_vars": dict, + "no_chromium": bool, + "no_unified": bool, + "non_unified_sources": StrictOrderingOnAppendList, + "action_overrides": dict, + } + ), + list, + """Defines a list of object directories handled by gyp configurations. + + Elements of this list give the relative object directory. For each + element of the list, GYP_DIRS may be accessed as a dictionary + (GYP_DIRS[foo]). The object this returns has attributes that need to be + set to further specify gyp processing: + - input, gives the path to the root gyp configuration file for that + object directory. + - variables, a dictionary containing variables and values to pass + to the gyp processor. + - sandbox_vars, a dictionary containing variables and values to + pass to the mozbuild processor on top of those derived from gyp + configuration. + - no_chromium, a boolean which if set to True disables some + special handling that emulates gyp_chromium. + - no_unified, a boolean which if set to True disables source + file unification entirely. + - non_unified_sources, a list containing sources files, relative to + the current moz.build, that should be excluded from source file + unification. + - action_overrides, a dict of action_name to values of the `script` + attribute to use for GENERATED_FILES for the specified action. + + Typical use looks like: + GYP_DIRS += ['foo', 'bar'] + GYP_DIRS['foo'].input = 'foo/foo.gyp' + GYP_DIRS['foo'].variables = { + 'foo': 'bar', + (...) + } + (...) + """, + ), + "SPHINX_TREES": ( + dict, + dict, + """Describes what the Sphinx documentation tree will look like. + + Keys are relative directories inside the final Sphinx documentation + tree to install files into. Values are directories (relative to this + file) whose content to copy into the Sphinx documentation tree. + """, + ), + "SPHINX_PYTHON_PACKAGE_DIRS": ( + StrictOrderingOnAppendList, + list, + """Directories containing Python packages that Sphinx documents. + """, + ), + "COMPILE_FLAGS": ( + CompileFlags, + dict, + """Recipe for compile flags for this context. Not to be manipulated + directly. + """, + ), + "LINK_FLAGS": ( + LinkFlags, + dict, + """Recipe for linker flags for this context. Not to be manipulated + directly. + """, + ), + "WASM_FLAGS": ( + WasmFlags, + dict, + """Recipe for wasm flags for this context. Not to be + manipulated directly. + """, + ), + "ASM_FLAGS": ( + AsmFlags, + dict, + """Recipe for linker flags for this context. Not to be + manipulated directly. + """, + ), + "CFLAGS": ( + List, + list, + """Flags passed to the C compiler for all of the C source files + declared in this directory. + + Note that the ordering of flags matters here, these flags will be + added to the compiler's command line in the same order as they + appear in the moz.build file. + """, + ), + "CXXFLAGS": ( + List, + list, + """Flags passed to the C++ compiler for all of the C++ source files + declared in this directory. + + Note that the ordering of flags matters here; these flags will be + added to the compiler's command line in the same order as they + appear in the moz.build file. + """, + ), + "HOST_COMPILE_FLAGS": ( + HostCompileFlags, + dict, + """Recipe for host compile flags for this context. Not to be manipulated + directly. + """, + ), + "HOST_DEFINES": ( + InitializedDefines, + dict, + """Dictionary of compiler defines to declare for host compilation. + See ``DEFINES`` for specifics. + """, + ), + "WASM_CFLAGS": ( + List, + list, + """Flags passed to the C-to-wasm compiler for all of the C + source files declared in this directory. + + Note that the ordering of flags matters here, these flags will be + added to the compiler's command line in the same order as they + appear in the moz.build file. + """, + ), + "WASM_CXXFLAGS": ( + List, + list, + """Flags passed to the C++-to-wasm compiler for all of the + C++ source files declared in this directory. + + Note that the ordering of flags matters here; these flags will be + added to the compiler's command line in the same order as they + appear in the moz.build file. + """, + ), + "WASM_DEFINES": ( + InitializedDefines, + dict, + """Dictionary of compiler defines to declare for wasm compilation. + See ``DEFINES`` for specifics. + """, + ), + "CMFLAGS": ( + List, + list, + """Flags passed to the Objective-C compiler for all of the Objective-C + source files declared in this directory. + + Note that the ordering of flags matters here; these flags will be + added to the compiler's command line in the same order as they + appear in the moz.build file. + """, + ), + "CMMFLAGS": ( + List, + list, + """Flags passed to the Objective-C++ compiler for all of the + Objective-C++ source files declared in this directory. + + Note that the ordering of flags matters here; these flags will be + added to the compiler's command line in the same order as they + appear in the moz.build file. + """, + ), + "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. + """, + ), + "HOST_CFLAGS": ( + List, + list, + """Flags passed to the host C compiler for all of the C source files + declared in this directory. + + Note that the ordering of flags matters here, these flags will be + added to the compiler's command line in the same order as they + appear in the moz.build file. + """, + ), + "HOST_CXXFLAGS": ( + List, + list, + """Flags passed to the host C++ compiler for all of the C++ source files + declared in this directory. + + Note that the ordering of flags matters here; these flags will be + added to the compiler's command line in the same order as they + appear in the moz.build file. + """, + ), + "LDFLAGS": ( + List, + list, + """Flags passed to the linker when linking all of the libraries and + executables declared in this directory. + + Note that the ordering of flags matters here; these flags will be + added to the linker's command line in the same order as they + appear in the moz.build file. + """, + ), + "EXTRA_DSO_LDOPTS": ( + List, + list, + """Flags passed to the linker when linking a shared library. + + Note that the ordering of flags matter here, these flags will be + added to the linker's command line in the same order as they + appear in the moz.build file. + """, + ), + "WIN32_EXE_LDFLAGS": ( + List, + list, + """Flags passed to the linker when linking a Windows .exe executable + declared in this directory. + + Note that the ordering of flags matter here, these flags will be + added to the linker's command line in the same order as they + appear in the moz.build file. + + This variable only has an effect on Windows. + """, + ), + "TEST_HARNESS_FILES": ( + ContextDerivedTypedHierarchicalStringList(Path), + list, + """List of files to be installed for test harnesses. + + ``TEST_HARNESS_FILES`` can be used to install files to any directory + under $objdir/_tests. Files can be appended to a field to indicate + which subdirectory they should be exported to. For example, + to export ``foo.py`` to ``_tests/foo``, append to + ``TEST_HARNESS_FILES`` like so:: + TEST_HARNESS_FILES.foo += ['foo.py'] + + Files from topsrcdir and the objdir can also be installed by prefixing + the path(s) with a '/' character and a '!' character, respectively:: + TEST_HARNESS_FILES.path += ['/build/bar.py', '!quux.py'] + """, + ), + "NO_EXPAND_LIBS": ( + bool, + bool, + """Forces to build a real static library, and no corresponding fake + library. + """, + ), + "USE_NASM": ( + bool, + bool, + """Use the nasm assembler to assemble assembly files from SOURCES. + + By default, the build will use the toolchain assembler, $(AS), to + assemble source files in assembly language (.s or .asm files). Setting + this value to ``True`` will cause it to use nasm instead. + + If nasm is not available on this system, or does not support the + current target architecture, an error will be raised. + """, + ), + "USE_INTEGRATED_CLANGCL_AS": ( + bool, + bool, + """Use the integrated clang-cl assembler to assemble assembly files from SOURCES. + + This allows using clang-cl to assemble assembly files which is useful + on platforms like aarch64 where the alternative is to have to run a + pre-processor to generate files with suitable syntax. + """, + ), +} + +# Sanity check: we don't want any variable above to have a list as storage type. +for name, (storage_type, input_types, docs) in VARIABLES.items(): + if storage_type == list: + raise RuntimeError('%s has a "list" storage type. Use "List" instead.' % name) + +# Set of variables that are only allowed in templates: +TEMPLATE_VARIABLES = { + "CPP_UNIT_TESTS", + "FORCE_SHARED_LIB", + "HOST_PROGRAM", + "HOST_LIBRARY_NAME", + "HOST_SIMPLE_PROGRAMS", + "IS_FRAMEWORK", + "IS_GKRUST", + "LIBRARY_NAME", + "PROGRAM", + "SIMPLE_PROGRAMS", +} + +# Add a note to template variable documentation. +for name in TEMPLATE_VARIABLES: + if name not in VARIABLES: + raise RuntimeError("%s is in TEMPLATE_VARIABLES but not in VARIABLES." % name) + storage_type, input_types, docs = VARIABLES[name] + docs += "This variable is only available in templates.\n" + VARIABLES[name] = (storage_type, input_types, docs) + + +# The set of functions exposed to the sandbox. +# +# Each entry is a tuple of: +# +# (function returning the corresponding function from a given sandbox, +# (argument types), docs) +# +# The first element is an attribute on Sandbox that should be a function type. +# +FUNCTIONS = { + "include": ( + lambda self: self._include, + (SourcePath,), + """Include another mozbuild file in the context of this one. + + This is similar to a ``#include`` in C languages. The filename passed to + the function will be read and its contents will be evaluated within the + context of the calling file. + + If a relative path is given, it is evaluated as relative to the file + currently being processed. If there is a chain of multiple include(), + the relative path computation is from the most recent/active file. + + If an absolute path is given, it is evaluated from ``TOPSRCDIR``. In + other words, ``include('/foo')`` references the path + ``TOPSRCDIR + '/foo'``. + + Example usage + ^^^^^^^^^^^^^ + + Include ``sibling.build`` from the current directory.:: + + include('sibling.build') + + Include ``foo.build`` from a path within the top source directory:: + + include('/elsewhere/foo.build') + """, + ), + "export": ( + lambda self: self._export, + (str,), + """Make the specified variable available to all child directories. + + The variable specified by the argument string is added to the + environment of all directories specified in the DIRS and TEST_DIRS + variables. If those directories themselves have child directories, + the variable will be exported to all of them. + + The value used for the variable is the final value at the end of the + moz.build file, so it is possible (but not recommended style) to place + the export before the definition of the variable. + + This function is limited to the upper-case variables that have special + meaning in moz.build files. + + NOTE: Please consult with a build peer (on the #build channel at + https://chat.mozilla.org) before adding a new use of this function. + + Example usage + ^^^^^^^^^^^^^ + + To make all children directories install as the given extension:: + + XPI_NAME = 'cool-extension' + export('XPI_NAME') + """, + ), + "warning": ( + lambda self: self._warning, + (str,), + """Issue a warning. + + Warnings are string messages that are printed during execution. + + Warnings are ignored during execution. + """, + ), + "error": ( + lambda self: self._error, + (str,), + """Issue a fatal error. + + If this function is called, processing is aborted immediately. + """, + ), + "template": ( + lambda self: self._template_decorator, + (FunctionType,), + """Decorator for template declarations. + + Templates are a special kind of functions that can be declared in + mozbuild files. Uppercase variables assigned in the function scope + are considered to be the result of the template. + + Contrary to traditional python functions: + - return values from template functions are ignored, + - template functions don't have access to the global scope. + + Example template + ^^^^^^^^^^^^^^^^ + + The following ``Program`` template sets two variables ``PROGRAM`` and + ``USE_LIBS``. ``PROGRAM`` is set to the argument given on the template + invocation, and ``USE_LIBS`` to contain "mozglue":: + + @template + def Program(name): + PROGRAM = name + USE_LIBS += ['mozglue'] + + Template invocation + ^^^^^^^^^^^^^^^^^^^ + + A template is invoked in the form of a function call:: + + Program('myprog') + + The result of the template, being all the uppercase variable it sets + is mixed to the existing set of variables defined in the mozbuild file + invoking the template:: + + FINAL_TARGET = 'dist/other' + USE_LIBS += ['mylib'] + Program('myprog') + USE_LIBS += ['otherlib'] + + The above mozbuild results in the following variables set: + + - ``FINAL_TARGET`` is 'dist/other' + - ``USE_LIBS`` is ['mylib', 'mozglue', 'otherlib'] + - ``PROGRAM`` is 'myprog' + + """, + ), +} + + +TestDirsPlaceHolder = List() + + +# Special variables. These complement VARIABLES. +# +# Each entry is a tuple of: +# +# (function returning the corresponding value from a given context, type, docs) +# +SPECIAL_VARIABLES = { + "TOPSRCDIR": ( + lambda context: context.config.topsrcdir, + str, + """Constant defining the top source directory. + + The top source directory is the parent directory containing the source + code and all build files. It is typically the root directory of a + cloned repository. + """, + ), + "TOPOBJDIR": ( + lambda context: context.config.topobjdir, + str, + """Constant defining the top object directory. + + The top object directory is the parent directory which will contain + the output of the build. This is commonly referred to as "the object + directory." + """, + ), + "RELATIVEDIR": ( + lambda context: context.relsrcdir, + str, + """Constant defining the relative path of this file. + + The relative path is from ``TOPSRCDIR``. This is defined as relative + to the main file being executed, regardless of whether additional + files have been included using ``include()``. + """, + ), + "SRCDIR": ( + lambda context: context.srcdir, + str, + """Constant defining the source directory of this file. + + This is the path inside ``TOPSRCDIR`` where this file is located. It + is the same as ``TOPSRCDIR + RELATIVEDIR``. + """, + ), + "OBJDIR": ( + lambda context: context.objdir, + str, + """The path to the object directory for this file. + + Is is the same as ``TOPOBJDIR + RELATIVEDIR``. + """, + ), + "CONFIG": ( + lambda context: ReadOnlyKeyedDefaultDict( + lambda key: context.config.substs.get(key) + ), + dict, + """Dictionary containing the current configuration variables. + + All the variables defined by the configuration system are available + through this object. e.g. ``ENABLE_TESTS``, ``CFLAGS``, etc. + + Values in this container are read-only. Attempts at changing values + will result in a run-time error. + + Access to an unknown variable will return None. + """, + ), + "EXTRA_COMPONENTS": ( + lambda context: context["FINAL_TARGET_FILES"].components._strings, + list, + """Additional component files to distribute. + + This variable contains a list of files to copy into + ``$(FINAL_TARGET)/components/``. + """, + ), + "EXTRA_PP_COMPONENTS": ( + lambda context: context["FINAL_TARGET_PP_FILES"].components._strings, + list, + """Javascript XPCOM files. + + This variable contains a list of files to preprocess. Generated + files will be installed in the ``/components`` directory of the distribution. + """, + ), + "JS_PREFERENCE_FILES": ( + lambda context: context["FINAL_TARGET_FILES"].defaults.pref._strings, + list, + """Exported JavaScript files. + + A list of files copied into the dist directory for packaging and installation. + Path will be defined for gre or application prefs dir based on what is building. + """, + ), + "JS_PREFERENCE_PP_FILES": ( + lambda context: context["FINAL_TARGET_PP_FILES"].defaults.pref._strings, + list, + """Like JS_PREFERENCE_FILES, preprocessed.. + """, + ), + "RESOURCE_FILES": ( + lambda context: context["FINAL_TARGET_FILES"].res, + list, + """List of resources to be exported, and in which subdirectories. + + ``RESOURCE_FILES`` is used to list the resource files to be exported to + ``dist/bin/res``, but it can be used for other files as well. This variable + behaves as a list when appending filenames for resources in the top-level + directory. Files can also be appended to a field to indicate which + subdirectory they should be exported to. For example, to export + ``foo.res`` to the top-level directory, and ``bar.res`` to ``fonts/``, + append to ``RESOURCE_FILES`` like so:: + + RESOURCE_FILES += ['foo.res'] + RESOURCE_FILES.fonts += ['bar.res'] + """, + ), + "CONTENT_ACCESSIBLE_FILES": ( + lambda context: context["FINAL_TARGET_FILES"].contentaccessible, + list, + """List of files which can be accessed by web content through resource:// URIs. + + ``CONTENT_ACCESSIBLE_FILES`` is used to list the files to be exported + to ``dist/bin/contentaccessible``. Files can also be appended to a + field to indicate which subdirectory they should be exported to. + """, + ), + "EXTRA_JS_MODULES": ( + lambda context: context["FINAL_TARGET_FILES"].modules, + list, + """Additional JavaScript files to distribute. + + This variable contains a list of files to copy into + ``$(FINAL_TARGET)/modules. + """, + ), + "EXTRA_PP_JS_MODULES": ( + lambda context: context["FINAL_TARGET_PP_FILES"].modules, + list, + """Additional JavaScript files to distribute. + + This variable contains a list of files to copy into + ``$(FINAL_TARGET)/modules``, after preprocessing. + """, + ), + "TESTING_JS_MODULES": ( + lambda context: context["TEST_HARNESS_FILES"].modules, + list, + """JavaScript modules to install in the test-only destination. + + Some JavaScript modules (JSMs) are test-only and not distributed + with Firefox. This variable defines them. + + To install modules in a subdirectory, use properties of this + variable to control the final destination. e.g. + + ``TESTING_JS_MODULES.foo += ['module.jsm']``. + """, + ), + "TEST_DIRS": ( + lambda context: context["DIRS"] + if context.config.substs.get("ENABLE_TESTS") + else TestDirsPlaceHolder, + list, + """Like DIRS but only for directories that contain test-only code. + + If tests are not enabled, this variable will be ignored. + + This variable may go away once the transition away from Makefiles is + complete. + """, + ), +} + +# Deprecation hints. +DEPRECATION_HINTS = { + "ASM_FLAGS": """ + Please use + + ASFLAGS + + instead of manipulating ASM_FLAGS directly. + """, + "CPP_UNIT_TESTS": """ + Please use' + + CppUnitTests(['foo', 'bar']) + + instead of + + CPP_UNIT_TESTS += ['foo', 'bar'] + """, + "DISABLE_STL_WRAPPING": """ + Please use + + DisableStlWrapping() + + instead of + + DISABLE_STL_WRAPPING = True + """, + "HOST_PROGRAM": """ + Please use + + HostProgram('foo') + + instead of + + HOST_PROGRAM = 'foo' + """, + "HOST_LIBRARY_NAME": """ + Please use + + HostLibrary('foo') + + instead of + + HOST_LIBRARY_NAME = 'foo' + """, + "HOST_SIMPLE_PROGRAMS": """ + Please use + + HostSimplePrograms(['foo', 'bar']) + + instead of + + HOST_SIMPLE_PROGRAMS += ['foo', 'bar']" + """, + "LIBRARY_NAME": """ + Please use + + Library('foo') + + instead of + + LIBRARY_NAME = 'foo' + """, + "NO_VISIBILITY_FLAGS": """ + Please use + + NoVisibilityFlags() + + instead of + + NO_VISIBILITY_FLAGS = True + """, + "PROGRAM": """ + Please use + + Program('foo') + + instead of + + PROGRAM = 'foo'" + """, + "SIMPLE_PROGRAMS": """ + Please use + + SimplePrograms(['foo', 'bar']) + + instead of + + SIMPLE_PROGRAMS += ['foo', 'bar']" + """, + "ALLOW_COMPILER_WARNINGS": """ + Please use + + AllowCompilerWarnings() + + instead of + + ALLOW_COMPILER_WARNINGS = True + """, + "FORCE_SHARED_LIB": """ + Please use + + SharedLibrary('foo') + + instead of + + Library('foo') [ or LIBRARY_NAME = 'foo' ] + FORCE_SHARED_LIB = True + """, + "IS_FRAMEWORK": """ + Please use + + Framework('foo') + + instead of + + Library('foo') [ or LIBRARY_NAME = 'foo' ] + IS_FRAMEWORK = True + """, + "IS_GKRUST": """ + Please use + + RustLibrary('gkrust', ... is_gkrust=True) + + instead of + + RustLibrary('gkrust') [ or LIBRARY_NAME = 'gkrust' ] + IS_GKRUST = True + """, + "TOOL_DIRS": "Please use the DIRS variable instead.", + "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 + """, + "GENERATED_SOURCES": """ + Please use + + SOURCES += [ '!foo.cpp' ] + + instead of + + GENERATED_SOURCES += [ 'foo.cpp'] + """, + "GENERATED_INCLUDES": """ + Please use + + LOCAL_INCLUDES += [ '!foo' ] + + instead of + + GENERATED_INCLUDES += [ 'foo' ] + """, + "DIST_FILES": """ + Please use + + FINAL_TARGET_PP_FILES += [ 'foo' ] + + instead of + + DIST_FILES += [ 'foo' ] + """, +} + +# Make sure that all template variables have a deprecation hint. +for name in TEMPLATE_VARIABLES: + if name not in DEPRECATION_HINTS: + raise RuntimeError("Missing deprecation hint for %s" % name) |