1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
14 from collections
.abc
import Iterable
16 base_dir
= os
.path
.abspath(os
.path
.dirname(__file__
))
17 sys
.path
.insert(0, os
.path
.join(base_dir
, "python", "mach"))
18 sys
.path
.insert(0, os
.path
.join(base_dir
, "python", "mozboot"))
19 sys
.path
.insert(0, os
.path
.join(base_dir
, "python", "mozbuild"))
20 sys
.path
.insert(0, os
.path
.join(base_dir
, "third_party", "python", "packaging"))
21 sys
.path
.insert(0, os
.path
.join(base_dir
, "third_party", "python", "six"))
22 sys
.path
.insert(0, os
.path
.join(base_dir
, "third_party", "python", "looseversion"))
23 import mozpack
.path
as mozpath
25 from mach
.requirements
import MachEnvRequirements
26 from mach
.site
import (
33 from mozbuild
.backend
.configenvironment
import PartialConfigEnvironment
34 from mozbuild
.configure
import TRACE
, ConfigureSandbox
35 from mozbuild
.pythonutil
import iter_modules_in_path
39 # Check for CRLF line endings.
40 with
open(__file__
, "r") as fh
:
45 " * The source tree appears to have Windows-style line endings.\n"
47 " * If using Git, Git is likely configured to use Windows-style\n"
50 " * To convert the working copy to UNIX-style line endings, run\n"
53 " * $ git config core.autocrlf false\n"
54 " * $ git config core.eof lf\n"
55 " * $ git rm --cached -r .\n"
56 " * $ git reset --hard\n"
58 " * If not using Git, the tool you used to obtain the source\n"
59 " * code likely converted files to Windows line endings. See\n"
60 " * usage information for that tool for more.\n"
68 if "OLD_CONFIGURE" not in os
.environ
:
69 os
.environ
["OLD_CONFIGURE"] = os
.path
.join(base_dir
, "old-configure")
71 sandbox
= ConfigureSandbox(config
, os
.environ
, argv
)
74 # This limitation has mostly to do with GNU make. Since make can't represent
75 # variables with spaces without correct quoting and many paths are used
76 # without proper quoting, using paths with spaces commonly results in
77 # targets or dependencies being treated as multiple paths. This, of course,
78 # undermines the ability for make to perform up-to-date checks and makes
79 # the build system not work very efficiently. In theory, a non-make build
80 # backend will make this limitation go away. But there is likely a long tail
81 # of things that will need fixing due to e.g. lack of proper path quoting.
82 topsrcdir
= os
.path
.realpath(os
.path
.dirname(__file__
))
83 if len(topsrcdir
.split()) > 1:
85 "Source directory cannot be located in a path with spaces: %s"
90 topobjdir
= os
.path
.realpath(os
.curdir
)
91 if len(topobjdir
.split()) > 1:
93 "Object directory cannot be located in a path with spaces: %s"
99 # Do not allow topobjdir == topsrcdir
100 if os
.path
.samefile(topsrcdir
, topobjdir
):
103 " * Building directly in the main source directory is not allowed.\n"
105 " * To build, you must run configure from a separate directory\n"
106 " * (referred to as an object directory).\n"
108 " * If you are building with a mozconfig, you will need to change your\n"
109 " * mozconfig to point to a different object directory.\n"
114 _activate_build_virtualenv()
116 clobber_file
= "CLOBBER"
117 if not os
.path
.exists(clobber_file
):
118 # Simply touch the file.
119 with
open(clobber_file
, "a"):
122 if os
.environ
.get("MOZ_CONFIGURE_TRACE"):
123 sandbox
._logger
.setLevel(TRACE
)
125 sandbox
.run(os
.path
.join(os
.path
.dirname(__file__
), "moz.configure"))
130 logging
.getLogger("moz.configure").info("Creating config.status")
132 old_js_configure_substs
= config
.pop("OLD_JS_CONFIGURE_SUBSTS", None)
133 old_js_configure_defines
= config
.pop("OLD_JS_CONFIGURE_DEFINES", None)
134 if old_js_configure_substs
or old_js_configure_defines
:
135 js_config
= config
.copy()
139 os
.makedirs("js/src")
141 if e
.errno
!= errno
.EEXIST
:
145 js_config
["OLD_CONFIGURE_SUBSTS"] = old_js_configure_substs
146 js_config
["OLD_CONFIGURE_DEFINES"] = old_js_configure_defines
147 # The build system frontend expects $objdir/js/src/config.status
148 # to have $objdir/js/src as topobjdir.
149 # We want forward slashes on all platforms.
150 js_config
["TOPOBJDIR"] += "/js/src"
151 config_status(js_config
, execute
=False)
155 return config_status(config
)
158 def check_unicode(obj
):
159 """Recursively check that all strings in the object are unicode strings."""
160 if isinstance(obj
, dict):
162 for k
, v
in six
.iteritems(obj
):
163 if not check_unicode(k
):
164 print("%s key is not unicode." % k
, file=sys
.stderr
)
166 elif not check_unicode(v
):
167 print("%s value is not unicode." % k
, file=sys
.stderr
)
170 if isinstance(obj
, bytes
):
172 if isinstance(obj
, six
.text_type
):
174 if isinstance(obj
, Iterable
):
175 return all(check_unicode(o
) for o
in obj
)
179 def config_status(config
, execute
=True):
180 # Sanitize config data to feed config.status
181 # Ideally, all the backend and frontend code would handle the booleans, but
182 # there are so many things involved, that it's easier to keep config.status
184 def sanitize_config(v
):
189 # Serialize types that look like lists and tuples as lists.
190 if not isinstance(v
, (bytes
, six
.text_type
, dict)) and isinstance(v
, Iterable
):
194 sanitized_config
= {}
195 sanitized_config
["substs"] = {
196 k
: sanitize_config(v
)
197 for k
, v
in six
.iteritems(config
)
203 "CONFIG_STATUS_DEPS",
204 "OLD_CONFIGURE_SUBSTS",
205 "OLD_CONFIGURE_DEFINES",
208 for k
, v
in config
["OLD_CONFIGURE_SUBSTS"]:
209 sanitized_config
["substs"][k
] = sanitize_config(v
)
210 sanitized_config
["defines"] = {
211 k
: sanitize_config(v
) for k
, v
in six
.iteritems(config
["DEFINES"])
213 for k
, v
in config
["OLD_CONFIGURE_DEFINES"]:
214 sanitized_config
["defines"][k
] = sanitize_config(v
)
215 sanitized_config
["topsrcdir"] = config
["TOPSRCDIR"]
216 sanitized_config
["topobjdir"] = config
["TOPOBJDIR"]
217 sanitized_config
["mozconfig"] = config
.get("MOZCONFIG")
219 if not check_unicode(sanitized_config
):
220 print("Configuration should be all unicode.", file=sys
.stderr
)
221 print("Please file a bug for the above.", file=sys
.stderr
)
224 # Create config.status. Eventually, we'll want to just do the work it does
225 # here, when we're able to skip configure tests/use cached results/not rely
227 with codecs
.open("config.status", "w", "utf-8") as fh
:
233 from mozbuild.configure.constants import *
236 % {"python": config
["PYTHON3"]}
238 for k
, v
in sorted(six
.iteritems(sanitized_config
)):
239 fh
.write("%s = " % k
)
240 pprint
.pprint(v
, stream
=fh
, indent
=4)
242 "__all__ = ['topobjdir', 'topsrcdir', 'defines', " "'substs', 'mozconfig']"
249 if __name__ == '__main__':
250 from mozbuild.config_status import config_status
251 args = dict([(name, globals()[name]) for name in __all__])
252 config_status(**args)
257 partial_config
= PartialConfigEnvironment(config
["TOPOBJDIR"])
258 partial_config
.write_vars(sanitized_config
)
260 # Write out a file so the build backend knows to re-run configure when
261 # relevant Python changes.
262 with io
.open("config_status_deps.in", "w", encoding
="utf-8", newline
="\n") as fh
:
265 config
["CONFIG_STATUS_DEPS"],
266 iter_modules_in_path(config
["TOPOBJDIR"], config
["TOPSRCDIR"]),
269 fh
.write("%s\n" % mozpath
.normpath(f
))
271 # Other things than us are going to run this file, so we need to give it
272 # executable permissions.
273 os
.chmod("config.status", 0o755)
275 from mozbuild
.config_status
import config_status
277 return config_status(args
=[], **sanitized_config
)
281 def _activate_build_virtualenv():
282 """Ensure that the build virtualenv is activated
284 configure.py may be executed through Mach, or via "./configure, make".
285 In the first case, the build virtualenv should already be activated.
286 In the second case, we're likely being executed with the system Python, and must
287 prepare the virtualenv and activate it ourselves.
290 version
= ".".join(str(i
) for i
in sys
.version_info
[0:3])
291 print(f
"Using Python {version} from {sys.executable}")
293 active_site
= MozSiteMetadata
.from_runtime()
294 if active_site
and active_site
.site_name
== "build":
295 # We're already running within the "build" virtualenv, no additional work is
299 # We're using the system python (or are nested within a non-build mach-managed
300 # virtualenv), so we should activate the build virtualenv as expected by the rest of
303 topsrcdir
= os
.path
.realpath(os
.path
.dirname(__file__
))
305 mach_site
= MachSiteManager(
308 MachEnvRequirements(),
309 ExternalPythonSite(sys
.executable
),
310 SitePackagesSource
.NONE
,
314 from mach
.util
import get_virtualenv_base_dir
316 build_site
= CommandSiteManager
.from_environment(
320 get_virtualenv_base_dir(topsrcdir
),
322 if not build_site
.ensure():
323 print("Created Python 3 virtualenv")
324 build_site
.activate()
327 if __name__
== "__main__":
328 sys
.exit(main(sys
.argv
))