blob: 9f379657dd473086aab5f433e78b76d1f73177c6 [file] [log] [blame]
# 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/.
from __future__ import absolute_import
import os
import sys
from collections import Iterable
from types import StringTypes
import mozpack.path as mozpath
from mozbuild.util import ReadOnlyDict
from mozbuild.shellutil import quote as shell_quote
if sys.version_info.major == 2:
text_type = unicode
else:
text_type = str
class BuildConfig(object):
"""Represents the output of configure."""
_CODE_CACHE = {}
def __init__(self):
self.topsrcdir = None
self.topobjdir = None
self.defines = {}
self.non_global_defines = []
self.substs = {}
self.files = []
@classmethod
def from_config_status(cls, path):
"""Create an instance from a config.status file."""
code_cache = cls._CODE_CACHE
mtime = os.path.getmtime(path)
# cache the compiled code as it can be reused
# we cache it the first time, or if the file changed
if not path in code_cache or code_cache[path][0] != mtime:
with open(path, 'rt') as fh:
source = fh.read()
code_cache[path] = (
mtime,
compile(source, path, 'exec', dont_inherit=1)
)
g = {
'__builtins__': __builtins__,
'__file__': path,
}
l = {}
exec(code_cache[path][1], g, l)
config = BuildConfig()
for name in l['__all__']:
setattr(config, name, l[name])
return config
class ConfigEnvironment(object):
"""Perform actions associated with a configured but bare objdir.
The purpose of this class is to preprocess files from the source directory
and output results in the object directory.
There are two types of files: config files and config headers,
each treated through a different member function.
Creating a ConfigEnvironment requires a few arguments:
- topsrcdir and topobjdir are, respectively, the top source and
the top object directory.
- defines is a list of (name, value) tuples. In autoconf, these are
set with AC_DEFINE and AC_DEFINE_UNQUOTED
- non_global_defines are a list of names appearing in defines above
that are not meant to be exported in ACDEFINES and ALLDEFINES (see
below)
- substs is a list of (name, value) tuples. In autoconf, these are
set with AC_SUBST.
ConfigEnvironment automatically defines two additional substs variables
from all the defines not appearing in non_global_defines:
- ACDEFINES contains the defines in the form -DNAME=VALUE, for use on
preprocessor command lines. The order in which defines were given
when creating the ConfigEnvironment is preserved.
- ALLDEFINES contains the defines in the form #define NAME VALUE, in
sorted order, for use in config files, for an automatic listing of
defines.
and two other additional subst variables from all the other substs:
- ALLSUBSTS contains the substs in the form NAME = VALUE, in sorted
order, for use in autoconf.mk. It includes ACDEFINES, but doesn't
include ALLDEFINES. Only substs with a VALUE are included, such that
the resulting file doesn't change when new empty substs are added.
This results in less invalidation of build dependencies in the case
of autoconf.mk..
- ALLEMPTYSUBSTS contains the substs with an empty value, in the form
NAME =.
ConfigEnvironment expects a "top_srcdir" subst to be set with the top
source directory, in msys format on windows. It is used to derive a
"srcdir" subst when treating config files. It can either be an absolute
path or a path relative to the topobjdir.
"""
def __init__(self, topsrcdir, topobjdir, defines=[], non_global_defines=[],
substs=[], source=None):
if not source:
source = mozpath.join(topobjdir, 'config.status')
self.source = source
self.defines = ReadOnlyDict(defines)
self.substs = dict(substs)
self.topsrcdir = mozpath.abspath(topsrcdir)
self.topobjdir = mozpath.abspath(topobjdir)
self.lib_prefix = self.substs.get('LIB_PREFIX', '')
if 'LIB_SUFFIX' in self.substs:
self.lib_suffix = '.%s' % self.substs['LIB_SUFFIX']
self.dll_prefix = self.substs.get('DLL_PREFIX', '')
self.dll_suffix = self.substs.get('DLL_SUFFIX', '')
if self.substs.get('IMPORT_LIB_SUFFIX'):
self.import_prefix = self.lib_prefix
self.import_suffix = '.%s' % self.substs['IMPORT_LIB_SUFFIX']
else:
self.import_prefix = self.dll_prefix
self.import_suffix = self.dll_suffix
global_defines = [name for name, value in defines
if not name in non_global_defines]
self.substs['ACDEFINES'] = ' '.join(['-D%s=%s' % (name,
shell_quote(self.defines[name]).replace('$', '$$')) for name in global_defines])
def serialize(obj):
if isinstance(obj, StringTypes):
return obj
if isinstance(obj, Iterable):
return ' '.join(obj)
raise Exception('Unhandled type %s', type(obj))
self.substs['ALLSUBSTS'] = '\n'.join(sorted(['%s = %s' % (name,
serialize(self.substs[name])) for name in self.substs if self.substs[name]]))
self.substs['ALLEMPTYSUBSTS'] = '\n'.join(sorted(['%s =' % name
for name in self.substs if not self.substs[name]]))
self.substs['ALLDEFINES'] = '\n'.join(sorted(['#define %s %s' % (name,
self.defines[name]) for name in global_defines]))
self.substs = ReadOnlyDict(self.substs)
self.external_source_dir = None
external = self.substs.get('EXTERNAL_SOURCE_DIR', '')
if external:
external = mozpath.normpath(external)
if not os.path.isabs(external):
external = mozpath.join(self.topsrcdir, external)
self.external_source_dir = mozpath.normpath(external)
# Populate a Unicode version of substs. This is an optimization to make
# moz.build reading faster, since each sandbox needs a Unicode version
# of these variables and doing it over a thousand times is a hotspot
# during sandbox execution!
# Bug 844509 tracks moving everything to Unicode.
self.substs_unicode = {}
def decode(v):
if not isinstance(v, text_type):
try:
return v.decode('utf-8')
except UnicodeDecodeError:
return v.decode('utf-8', 'replace')
for k, v in self.substs.items():
if not isinstance(v, StringTypes):
if isinstance(v, Iterable):
type(v)(decode(i) for i in v)
elif not isinstance(v, text_type):
v = decode(v)
self.substs_unicode[k] = v
self.substs_unicode = ReadOnlyDict(self.substs_unicode)
@staticmethod
def from_config_status(path):
config = BuildConfig.from_config_status(path)
return ConfigEnvironment(config.topsrcdir, config.topobjdir,
config.defines, config.non_global_defines, config.substs, path)