Newer
Older

Mike Hommey
committed
# 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, print_function, unicode_literals

Mike Hommey
committed

Mike Hommey
committed
import codecs
import errno

Ricky Stewart
committed
import io

Chris Manchester
committed
import itertools

Mike Hommey
committed
import logging

Mike Hommey
committed
import os
import sys
import textwrap

Alex Hochheiden
committed
from collections.abc import Iterable

Mike Hommey
committed
base_dir = os.path.abspath(os.path.dirname(__file__))
sys.path.insert(0, os.path.join(base_dir, "python", "mach"))
sys.path.insert(0, os.path.join(base_dir, "python", "mozboot"))
sys.path.insert(0, os.path.join(base_dir, "python", "mozbuild"))

Mitchell Hentges
committed
sys.path.insert(0, os.path.join(base_dir, "third_party", "python", "packaging"))
sys.path.insert(0, os.path.join(base_dir, "third_party", "python", "pyparsing"))
sys.path.insert(0, os.path.join(base_dir, "third_party", "python", "six"))

Mike Hommey
committed
from mach.site import (
CommandSiteManager,
ExternalPythonSite,
MachSiteManager,
MozSiteMetadata,
SitePackagesSource,
)
from mach.requirements import MachEnvRequirements

Mike Hommey
committed
from mozbuild.configure import (
ConfigureSandbox,
TRACE,
)

Chris Manchester
committed
from mozbuild.pythonutil import iter_modules_in_path
from mozbuild.backend.configenvironment import PartialConfigEnvironment
from mozbuild.util import write_indented_repr

Chris Manchester
committed
import mozpack.path as mozpath
import six

Mike Hommey
committed

Mike Hommey
committed
def main(argv):

Mike Hommey
committed
# Check for CRLF line endings.
with open(__file__, "r") as fh:
data = fh.read()
if "\r" in data:
print(
"\n ***\n"
" * The source tree appears to have Windows-style line endings.\n"
" *\n"
" * If using Git, Git is likely configured to use Windows-style\n"
" * line endings.\n"
" *\n"
" * To convert the working copy to UNIX-style line endings, run\n"
" * the following:\n"
" *\n"
" * $ git config core.autocrlf false\n"
" * $ git config core.eof lf\n"
" * $ git rm --cached -r .\n"
" * $ git reset --hard\n"
" *\n"
" * If not using Git, the tool you used to obtain the source\n"
" * code likely converted files to Windows line endings. See\n"
" * usage information for that tool for more.\n"
" ***",
file=sys.stderr,
)
return 1

Mike Hommey
committed
config = {}

Mike Hommey
committed
if "OLD_CONFIGURE" not in os.environ:
os.environ["OLD_CONFIGURE"] = os.path.join(base_dir, "old-configure")

Mike Hommey
committed
sandbox = ConfigureSandbox(config, os.environ, argv)

Mike Hommey
committed

Mike Hommey
committed
if not sandbox._help:

Mike Hommey
committed
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# This limitation has mostly to do with GNU make. Since make can't represent
# variables with spaces without correct quoting and many paths are used
# without proper quoting, using paths with spaces commonly results in
# targets or dependencies being treated as multiple paths. This, of course,
# undermines the ability for make to perform up-to-date checks and makes
# the build system not work very efficiently. In theory, a non-make build
# backend will make this limitation go away. But there is likely a long tail
# of things that will need fixing due to e.g. lack of proper path quoting.
topsrcdir = os.path.realpath(os.path.dirname(__file__))
if len(topsrcdir.split()) > 1:
print(
"Source directory cannot be located in a path with spaces: %s"
% topsrcdir,
file=sys.stderr,
)
return 1
topobjdir = os.path.realpath(os.curdir)
if len(topobjdir.split()) > 1:
print(
"Object directory cannot be located in a path with spaces: %s"
% topobjdir,
file=sys.stderr,
)
return 1
# Do not allow topobjdir == topsrcdir
if os.path.samefile(topsrcdir, topobjdir):
print(
" ***\n"
" * Building directly in the main source directory is not allowed.\n"
" *\n"
" * To build, you must run configure from a separate directory\n"
" * (referred to as an object directory).\n"
" *\n"
" * If you are building with a mozconfig, you will need to change your\n"
" * mozconfig to point to a different object directory.\n"
" ***",
file=sys.stderr,
)
return 1

Mike Hommey
committed
_activate_build_virtualenv()
clobber_file = "CLOBBER"
if not os.path.exists(clobber_file):
# Simply touch the file.
with open(clobber_file, "a"):
pass
if os.environ.get("MOZ_CONFIGURE_TRACE"):

Mike Hommey
committed
sandbox._logger.setLevel(TRACE)
sandbox.run(os.path.join(os.path.dirname(__file__), "moz.configure"))

Mike Hommey
committed

Mike Hommey
committed
if sandbox._help:
return 0

Mike Hommey
committed
logging.getLogger("moz.configure").info("Creating config.status")
old_js_configure_substs = config.pop("OLD_JS_CONFIGURE_SUBSTS", None)
old_js_configure_defines = config.pop("OLD_JS_CONFIGURE_DEFINES", None)
if old_js_configure_substs or old_js_configure_defines:
js_config = config.copy()
pwd = os.getcwd()
try:
try:
os.makedirs("js/src")
except OSError as e:
if e.errno != errno.EEXIST:
raise
os.chdir("js/src")
js_config["OLD_CONFIGURE_SUBSTS"] = old_js_configure_substs
js_config["OLD_CONFIGURE_DEFINES"] = old_js_configure_defines
# The build system frontend expects $objdir/js/src/config.status
# to have $objdir/js/src as topobjdir.
# We want forward slashes on all platforms.
js_config["TOPOBJDIR"] += "/js/src"
config_status(js_config, execute=False)
finally:
os.chdir(pwd)

Mike Hommey
committed
return config_status(config)
def check_unicode(obj):
"""Recursively check that all strings in the object are unicode strings."""
if isinstance(obj, dict):
result = True
for k, v in six.iteritems(obj):
if not check_unicode(k):
print("%s key is not unicode." % k, file=sys.stderr)
result = False
elif not check_unicode(v):
print("%s value is not unicode." % k, file=sys.stderr)
result = False
return result
if isinstance(obj, bytes):
return False
if isinstance(obj, six.text_type):
return True
if isinstance(obj, Iterable):
return all(check_unicode(o) for o in obj)
return True
def config_status(config, execute=True):

Mike Hommey
committed
# Sanitize config data to feed config.status
# Ideally, all the backend and frontend code would handle the booleans, but
# there are so many things involved, that it's easier to keep config.status
# untouched for now.

Mike Hommey
committed
def sanitize_config(v):
if v is True:
return "1"
if v is False:
return ""

Mike Hommey
committed
# Serialize types that look like lists and tuples as lists.
if not isinstance(v, (bytes, six.text_type, dict)) and isinstance(v, Iterable):
return list(v)
return v

Mike Hommey
committed
sanitized_config = {}
sanitized_config["substs"] = {
k: sanitize_config(v)
for k, v in six.iteritems(config)
if k
not in (
"DEFINES",
"TOPSRCDIR",
"TOPOBJDIR",
"CONFIG_STATUS_DEPS",
"OLD_CONFIGURE_SUBSTS",
"OLD_CONFIGURE_DEFINES",
)

Mike Hommey
committed
}
for k, v in config["OLD_CONFIGURE_SUBSTS"]:
sanitized_config["substs"][k] = sanitize_config(v)
sanitized_config["defines"] = {
k: sanitize_config(v) for k, v in six.iteritems(config["DEFINES"])
}
for k, v in config["OLD_CONFIGURE_DEFINES"]:
sanitized_config["defines"][k] = sanitize_config(v)
sanitized_config["topsrcdir"] = config["TOPSRCDIR"]
sanitized_config["topobjdir"] = config["TOPOBJDIR"]
sanitized_config["mozconfig"] = config.get("MOZCONFIG")

Mike Hommey
committed
if not check_unicode(sanitized_config):
print("Configuration should be all unicode.", file=sys.stderr)
print("Please file a bug for the above.", file=sys.stderr)
sys.exit(1)

Mike Hommey
committed
# Some values in sanitized_config also have more complex types, such as
# EnumString, which using when calling config_status would currently
# break the build, as well as making it inconsistent with re-running
# config.status, for which they are normalized to plain strings via
# indented_repr. Likewise for non-dict non-string iterables being
# converted to lists.
def normalize(obj):
if isinstance(obj, dict):
return {k: normalize(v) for k, v in six.iteritems(obj)}

Mike Hommey
committed
if isinstance(obj, six.text_type):
return six.text_type(obj)
if isinstance(obj, Iterable):
return [normalize(o) for o in obj]
return obj
sanitized_config = normalize(sanitized_config)

Mike Hommey
committed
# Create config.status. Eventually, we'll want to just do the work it does
# here, when we're able to skip configure tests/use cached results/not rely
# on autoconf.
with codecs.open("config.status", "w", "utf-8") as fh:
fh.write(
textwrap.dedent(
"""\
#!%(python)s
# coding=utf-8
from __future__ import unicode_literals
"""
)
% {"python": config["PYTHON3"]}
)
for k, v in sorted(six.iteritems(sanitized_config)):
fh.write("%s = " % k)
write_indented_repr(fh, v)
fh.write(
"__all__ = ['topobjdir', 'topsrcdir', 'defines', " "'substs', 'mozconfig']"
)

Mike Hommey
committed
if execute:
fh.write(
textwrap.dedent(
"""
if __name__ == '__main__':
from mozbuild.config_status import config_status
args = dict([(name, globals()[name]) for name in __all__])
config_status(**args)
"""
)
)

Mike Hommey
committed
partial_config = PartialConfigEnvironment(config["TOPOBJDIR"])
partial_config.write_vars(sanitized_config)

Chris Manchester
committed
# Write out a file so the build backend knows to re-run configure when
# relevant Python changes.
with io.open("config_status_deps.in", "w", encoding="utf-8", newline="\n") as fh:

Ricky Stewart
committed
for f in sorted(
itertools.chain(
config["CONFIG_STATUS_DEPS"],
iter_modules_in_path(config["TOPOBJDIR"], config["TOPSRCDIR"]),
)
):
fh.write("%s\n" % mozpath.normpath(f))

Chris Manchester
committed

Mike Hommey
committed
# Other things than us are going to run this file, so we need to give it
# executable permissions.
os.chmod("config.status", 0o755)
if execute:

Mike Hommey
committed
from mozbuild.config_status import config_status

Mike Hommey
committed
return config_status(args=[], **sanitized_config)

Mike Hommey
committed
return 0

Mike Hommey
committed

Mike Hommey
committed
def _activate_build_virtualenv():

Mitchell Hentges
committed
"""Ensure that the build virtualenv is activated
configure.py may be executed through Mach, or via "./configure, make".
In the first case, the build virtualenv should already be activated.
In the second case, we're likely being executed with the system Python, and must
prepare the virtualenv and activate it ourselves.
"""
version = ".".join(str(i) for i in sys.version_info[0:3])
print(f"Using Python {version} from {sys.executable}")

Mitchell Hentges
committed
active_site = MozSiteMetadata.from_runtime()
if active_site and active_site.site_name == "build":
# We're already running within the "build" virtualenv, no additional work is
# needed.
return
# We're using the system python (or are nested within a non-build mach-managed
# virtualenv), so we should activate the build virtualenv as expected by the rest of
# configure.
topobjdir = os.path.realpath(".")
topsrcdir = os.path.realpath(os.path.dirname(__file__))

Mike Hommey
committed
mach_site = MachSiteManager(
topsrcdir,
None,
MachEnvRequirements(),
ExternalPythonSite(sys.executable),
SitePackagesSource.NONE,
)

Mitchell Hentges
committed
mach_site.activate()
build_site = CommandSiteManager.from_environment(
topsrcdir,

Mike Hommey
committed
None,
"build",

Mitchell Hentges
committed
os.path.join(topobjdir, "_virtualenvs"),
)
if not build_site.ensure():
print("Created Python 3 virtualenv")
build_site.activate()
if __name__ == "__main__":

Mike Hommey
committed
sys.exit(main(sys.argv))