]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/configure.py
Rollup merge of #86183 - inquisitivecrystal:env-nul, r=m-ou-se
[rust.git] / src / bootstrap / configure.py
1 #!/usr/bin/env python
2
3 # ignore-tidy-linelength
4
5 from __future__ import absolute_import, division, print_function
6 import sys
7 import os
8 rust_dir = os.path.dirname(os.path.abspath(__file__))
9 rust_dir = os.path.dirname(rust_dir)
10 rust_dir = os.path.dirname(rust_dir)
11 sys.path.append(os.path.join(rust_dir, "src", "bootstrap"))
12 import bootstrap
13
14
15 class Option(object):
16     def __init__(self, name, rustbuild, desc, value):
17         self.name = name
18         self.rustbuild = rustbuild
19         self.desc = desc
20         self.value = value
21
22
23 options = []
24
25
26 def o(*args):
27     options.append(Option(*args, value=False))
28
29
30 def v(*args):
31     options.append(Option(*args, value=True))
32
33
34 o("debug", "rust.debug", "enables debugging environment; does not affect optimization of bootstrapped code (use `--disable-optimize` for that)")
35 o("docs", "build.docs", "build standard library documentation")
36 o("compiler-docs", "build.compiler-docs", "build compiler documentation")
37 o("optimize-tests", "rust.optimize-tests", "build tests with optimizations")
38 o("parallel-compiler", "rust.parallel-compiler", "build a multi-threaded rustc")
39 o("verbose-tests", "rust.verbose-tests", "enable verbose output when running tests")
40 o("ccache", "llvm.ccache", "invoke gcc/clang via ccache to reuse object files between builds")
41 o("sccache", None, "invoke gcc/clang via sccache to reuse object files between builds")
42 o("local-rust", None, "use an installed rustc rather than downloading a snapshot")
43 v("local-rust-root", None, "set prefix for local rust binary")
44 o("local-rebuild", "build.local-rebuild", "assume local-rust matches the current version, for rebuilds; implies local-rust, and is implied if local-rust already matches the current version")
45 o("llvm-static-stdcpp", "llvm.static-libstdcpp", "statically link to libstdc++ for LLVM")
46 o("llvm-link-shared", "llvm.link-shared", "prefer shared linking to LLVM (llvm-config --link-shared)")
47 o("rpath", "rust.rpath", "build rpaths into rustc itself")
48 o("llvm-version-check", "llvm.version-check", "check if the LLVM version is supported, build anyway")
49 o("codegen-tests", "rust.codegen-tests", "run the src/test/codegen tests")
50 o("option-checking", None, "complain about unrecognized options in this configure script")
51 o("ninja", "llvm.ninja", "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)")
52 o("locked-deps", "build.locked-deps", "force Cargo.lock to be up to date")
53 o("vendor", "build.vendor", "enable usage of vendored Rust crates")
54 o("sanitizers", "build.sanitizers", "build the sanitizer runtimes (asan, lsan, msan, tsan, hwasan)")
55 o("dist-src", "rust.dist-src", "when building tarballs enables building a source tarball")
56 o("cargo-native-static", "build.cargo-native-static", "static native libraries in cargo")
57 o("profiler", "build.profiler", "build the profiler runtime")
58 o("full-tools", None, "enable all tools")
59 o("lld", "rust.lld", "build lld")
60 o("missing-tools", "dist.missing-tools", "allow failures when building tools")
61 o("use-libcxx", "llvm.use-libcxx", "build LLVM with libc++")
62 o("control-flow-guard", "rust.control-flow-guard", "Enable Control Flow Guard")
63
64 v("llvm-cflags", "llvm.cflags", "build LLVM with these extra compiler flags")
65 v("llvm-cxxflags", "llvm.cxxflags", "build LLVM with these extra compiler flags")
66 v("llvm-ldflags", "llvm.ldflags", "build LLVM with these extra linker flags")
67
68 v("llvm-libunwind", "rust.llvm-libunwind", "use LLVM libunwind")
69
70 # Optimization and debugging options. These may be overridden by the release
71 # channel, etc.
72 o("optimize", "rust.optimize", "build optimized rust code")
73 o("optimize-llvm", "llvm.optimize", "build optimized LLVM")
74 o("llvm-assertions", "llvm.assertions", "build LLVM with assertions")
75 o("debug-assertions", "rust.debug-assertions", "build with debugging assertions")
76 o("llvm-release-debuginfo", "llvm.release-debuginfo", "build LLVM with debugger metadata")
77 v("debuginfo-level", "rust.debuginfo-level", "debuginfo level for Rust code")
78 v("debuginfo-level-rustc", "rust.debuginfo-level-rustc", "debuginfo level for the compiler")
79 v("debuginfo-level-std", "rust.debuginfo-level-std", "debuginfo level for the standard library")
80 v("debuginfo-level-tools", "rust.debuginfo-level-tools", "debuginfo level for the tools")
81 v("debuginfo-level-tests", "rust.debuginfo-level-tests", "debuginfo level for the test suites run with compiletest")
82 v("save-toolstates", "rust.save-toolstates", "save build and test status of external tools into this file")
83
84 v("prefix", "install.prefix", "set installation prefix")
85 v("localstatedir", "install.localstatedir", "local state directory")
86 v("datadir", "install.datadir", "install data")
87 v("sysconfdir", "install.sysconfdir", "install system configuration files")
88 v("infodir", "install.infodir", "install additional info")
89 v("libdir", "install.libdir", "install libraries")
90 v("mandir", "install.mandir", "install man pages in PATH")
91 v("docdir", "install.docdir", "install documentation in PATH")
92 v("bindir", "install.bindir", "install binaries")
93
94 v("llvm-root", None, "set LLVM root")
95 v("llvm-config", None, "set path to llvm-config")
96 v("llvm-filecheck", None, "set path to LLVM's FileCheck utility")
97 v("python", "build.python", "set path to python")
98 v("android-cross-path", "target.arm-linux-androideabi.android-ndk",
99   "Android NDK standalone path (deprecated)")
100 v("i686-linux-android-ndk", "target.i686-linux-android.android-ndk",
101   "i686-linux-android NDK standalone path")
102 v("arm-linux-androideabi-ndk", "target.arm-linux-androideabi.android-ndk",
103   "arm-linux-androideabi NDK standalone path")
104 v("armv7-linux-androideabi-ndk", "target.armv7-linux-androideabi.android-ndk",
105   "armv7-linux-androideabi NDK standalone path")
106 v("thumbv7neon-linux-androideabi-ndk", "target.thumbv7neon-linux-androideabi.android-ndk",
107   "thumbv7neon-linux-androideabi NDK standalone path")
108 v("aarch64-linux-android-ndk", "target.aarch64-linux-android.android-ndk",
109   "aarch64-linux-android NDK standalone path")
110 v("x86_64-linux-android-ndk", "target.x86_64-linux-android.android-ndk",
111   "x86_64-linux-android NDK standalone path")
112 v("musl-root", "target.x86_64-unknown-linux-musl.musl-root",
113   "MUSL root installation directory (deprecated)")
114 v("musl-root-x86_64", "target.x86_64-unknown-linux-musl.musl-root",
115   "x86_64-unknown-linux-musl install directory")
116 v("musl-root-i586", "target.i586-unknown-linux-musl.musl-root",
117   "i586-unknown-linux-musl install directory")
118 v("musl-root-i686", "target.i686-unknown-linux-musl.musl-root",
119   "i686-unknown-linux-musl install directory")
120 v("musl-root-arm", "target.arm-unknown-linux-musleabi.musl-root",
121   "arm-unknown-linux-musleabi install directory")
122 v("musl-root-armhf", "target.arm-unknown-linux-musleabihf.musl-root",
123   "arm-unknown-linux-musleabihf install directory")
124 v("musl-root-armv5te", "target.armv5te-unknown-linux-musleabi.musl-root",
125   "armv5te-unknown-linux-musleabi install directory")
126 v("musl-root-armv7", "target.armv7-unknown-linux-musleabi.musl-root",
127   "armv7-unknown-linux-musleabi install directory")
128 v("musl-root-armv7hf", "target.armv7-unknown-linux-musleabihf.musl-root",
129   "armv7-unknown-linux-musleabihf install directory")
130 v("musl-root-aarch64", "target.aarch64-unknown-linux-musl.musl-root",
131   "aarch64-unknown-linux-musl install directory")
132 v("musl-root-mips", "target.mips-unknown-linux-musl.musl-root",
133   "mips-unknown-linux-musl install directory")
134 v("musl-root-mipsel", "target.mipsel-unknown-linux-musl.musl-root",
135   "mipsel-unknown-linux-musl install directory")
136 v("musl-root-mips64", "target.mips64-unknown-linux-muslabi64.musl-root",
137   "mips64-unknown-linux-muslabi64 install directory")
138 v("musl-root-mips64el", "target.mips64el-unknown-linux-muslabi64.musl-root",
139   "mips64el-unknown-linux-muslabi64 install directory")
140 v("qemu-armhf-rootfs", "target.arm-unknown-linux-gnueabihf.qemu-rootfs",
141   "rootfs in qemu testing, you probably don't want to use this")
142 v("qemu-aarch64-rootfs", "target.aarch64-unknown-linux-gnu.qemu-rootfs",
143   "rootfs in qemu testing, you probably don't want to use this")
144 v("qemu-riscv64-rootfs", "target.riscv64gc-unknown-linux-gnu.qemu-rootfs",
145   "rootfs in qemu testing, you probably don't want to use this")
146 v("experimental-targets", "llvm.experimental-targets",
147   "experimental LLVM targets to build")
148 v("release-channel", "rust.channel", "the name of the release channel to build")
149 v("release-description", "rust.description", "optional descriptive string for version output")
150 v("dist-compression-formats", None,
151   "comma-separated list of compression formats to use")
152
153 # Used on systems where "cc" is unavailable
154 v("default-linker", "rust.default-linker", "the default linker")
155
156 # Many of these are saved below during the "writing configuration" step
157 # (others are conditionally saved).
158 o("manage-submodules", "build.submodules", "let the build manage the git submodules")
159 o("full-bootstrap", "build.full-bootstrap", "build three compilers instead of two")
160 o("extended", "build.extended", "build an extended rust tool set")
161
162 v("tools", None, "List of extended tools will be installed")
163 v("codegen-backends", None, "List of codegen backends to build")
164 v("build", "build.build", "GNUs ./configure syntax LLVM build triple")
165 v("host", None, "GNUs ./configure syntax LLVM host triples")
166 v("target", None, "GNUs ./configure syntax LLVM target triples")
167
168 v("set", None, "set arbitrary key/value pairs in TOML configuration")
169
170
171 def p(msg):
172     print("configure: " + msg)
173
174
175 def err(msg):
176     print("configure: error: " + msg)
177     sys.exit(1)
178
179
180 if '--help' in sys.argv or '-h' in sys.argv:
181     print('Usage: ./configure [options]')
182     print('')
183     print('Options')
184     for option in options:
185         if 'android' in option.name:
186             # no one needs to know about these obscure options
187             continue
188         if option.value:
189             print('\t{:30} {}'.format('--{}=VAL'.format(option.name), option.desc))
190         else:
191             print('\t{:30} {}'.format('--enable-{}'.format(option.name), option.desc))
192     print('')
193     print('This configure script is a thin configuration shim over the true')
194     print('configuration system, `config.toml`. You can explore the comments')
195     print('in `config.toml.example` next to this configure script to see')
196     print('more information about what each option is. Additionally you can')
197     print('pass `--set` as an argument to set arbitrary key/value pairs')
198     print('in the TOML configuration if desired')
199     print('')
200     print('Also note that all options which take `--enable` can similarly')
201     print('be passed with `--disable-foo` to forcibly disable the option')
202     sys.exit(0)
203
204 # Parse all command line arguments into one of these three lists, handling
205 # boolean and value-based options separately
206 unknown_args = []
207 need_value_args = []
208 known_args = {}
209
210 p("processing command line")
211 i = 1
212 while i < len(sys.argv):
213     arg = sys.argv[i]
214     i += 1
215     if not arg.startswith('--'):
216         unknown_args.append(arg)
217         continue
218
219     found = False
220     for option in options:
221         value = None
222         if option.value:
223             keyval = arg[2:].split('=', 1)
224             key = keyval[0]
225             if option.name != key:
226                 continue
227
228             if len(keyval) > 1:
229                 value = keyval[1]
230             elif i < len(sys.argv):
231                 value = sys.argv[i]
232                 i += 1
233             else:
234                 need_value_args.append(arg)
235                 continue
236         else:
237             if arg[2:] == 'enable-' + option.name:
238                 value = True
239             elif arg[2:] == 'disable-' + option.name:
240                 value = False
241             else:
242                 continue
243
244         found = True
245         if option.name not in known_args:
246             known_args[option.name] = []
247         known_args[option.name].append((option, value))
248         break
249
250     if not found:
251         unknown_args.append(arg)
252 p("")
253
254 # Note: here and a few other places, we use [-1] to apply the *last* value
255 # passed.  But if option-checking is enabled, then the known_args loop will
256 # also assert that options are only passed once.
257 option_checking = ('option-checking' not in known_args
258                    or known_args['option-checking'][-1][1])
259 if option_checking:
260     if len(unknown_args) > 0:
261         err("Option '" + unknown_args[0] + "' is not recognized")
262     if len(need_value_args) > 0:
263         err("Option '{0}' needs a value ({0}=val)".format(need_value_args[0]))
264
265 # Parse all known arguments into a configuration structure that reflects the
266 # TOML we're going to write out
267 config = {}
268
269
270 def build():
271     if 'build' in known_args:
272         return known_args['build'][-1][1]
273     return bootstrap.default_build_triple(verbose=False)
274
275
276 def set(key, value):
277     s = "{:20} := {}".format(key, value)
278     if len(s) < 70:
279         p(s)
280     else:
281         p(s[:70] + " ...")
282
283     arr = config
284     parts = key.split('.')
285     for i, part in enumerate(parts):
286         if i == len(parts) - 1:
287             arr[part] = value
288         else:
289             if part not in arr:
290                 arr[part] = {}
291             arr = arr[part]
292
293
294 for key in known_args:
295     # The `set` option is special and can be passed a bunch of times
296     if key == 'set':
297         for option, value in known_args[key]:
298             keyval = value.split('=', 1)
299             if len(keyval) == 1 or keyval[1] == "true":
300                 value = True
301             elif keyval[1] == "false":
302                 value = False
303             else:
304                 value = keyval[1]
305             set(keyval[0], value)
306         continue
307
308     # Ensure each option is only passed once
309     arr = known_args[key]
310     if option_checking and len(arr) > 1:
311         err("Option '{}' provided more than once".format(key))
312     option, value = arr[-1]
313
314     # If we have a clear avenue to set our value in rustbuild, do so
315     if option.rustbuild is not None:
316         set(option.rustbuild, value)
317         continue
318
319     # Otherwise we're a "special" option and need some extra handling, so do
320     # that here.
321     if option.name == 'sccache':
322         set('llvm.ccache', 'sccache')
323     elif option.name == 'local-rust':
324         for path in os.environ['PATH'].split(os.pathsep):
325             if os.path.exists(path + '/rustc'):
326                 set('build.rustc', path + '/rustc')
327                 break
328         for path in os.environ['PATH'].split(os.pathsep):
329             if os.path.exists(path + '/cargo'):
330                 set('build.cargo', path + '/cargo')
331                 break
332     elif option.name == 'local-rust-root':
333         set('build.rustc', value + '/bin/rustc')
334         set('build.cargo', value + '/bin/cargo')
335     elif option.name == 'llvm-root':
336         set('target.{}.llvm-config'.format(build()), value + '/bin/llvm-config')
337     elif option.name == 'llvm-config':
338         set('target.{}.llvm-config'.format(build()), value)
339     elif option.name == 'llvm-filecheck':
340         set('target.{}.llvm-filecheck'.format(build()), value)
341     elif option.name == 'tools':
342         set('build.tools', value.split(','))
343     elif option.name == 'codegen-backends':
344         set('rust.codegen-backends', value.split(','))
345     elif option.name == 'host':
346         set('build.host', value.split(','))
347     elif option.name == 'target':
348         set('build.target', value.split(','))
349     elif option.name == 'full-tools':
350         set('rust.codegen-backends', ['llvm'])
351         set('rust.lld', True)
352         set('rust.llvm-tools', True)
353         set('build.extended', True)
354     elif option.name == 'option-checking':
355         # this was handled above
356         pass
357     elif option.name == 'dist-compression-formats':
358         set('dist.compression-formats', value.split(','))
359     else:
360         raise RuntimeError("unhandled option {}".format(option.name))
361
362 set('build.configure-args', sys.argv[1:])
363
364 # "Parse" the `config.toml.example` file into the various sections, and we'll
365 # use this as a template of a `config.toml` to write out which preserves
366 # all the various comments and whatnot.
367 #
368 # Note that the `target` section is handled separately as we'll duplicate it
369 # per configured target, so there's a bit of special handling for that here.
370 sections = {}
371 cur_section = None
372 sections[None] = []
373 section_order = [None]
374 targets = {}
375
376 for line in open(rust_dir + '/config.toml.example').read().split("\n"):
377     if line.startswith('['):
378         cur_section = line[1:-1]
379         if cur_section.startswith('target'):
380             cur_section = 'target'
381         elif '.' in cur_section:
382             raise RuntimeError("don't know how to deal with section: {}".format(cur_section))
383         sections[cur_section] = [line]
384         section_order.append(cur_section)
385     else:
386         sections[cur_section].append(line)
387
388 # Fill out the `targets` array by giving all configured targets a copy of the
389 # `target` section we just loaded from the example config
390 configured_targets = [build()]
391 if 'build' in config:
392     if 'host' in config['build']:
393         configured_targets += config['build']['host']
394     if 'target' in config['build']:
395         configured_targets += config['build']['target']
396 if 'target' in config:
397     for target in config['target']:
398         configured_targets.append(target)
399 for target in configured_targets:
400     targets[target] = sections['target'][:]
401     targets[target][0] = targets[target][0].replace("x86_64-unknown-linux-gnu", target)
402
403
404 def is_number(value):
405     try:
406         float(value)
407         return True
408     except ValueError:
409         return False
410
411
412 # Here we walk through the constructed configuration we have from the parsed
413 # command line arguments. We then apply each piece of configuration by
414 # basically just doing a `sed` to change the various configuration line to what
415 # we've got configure.
416 def to_toml(value):
417     if isinstance(value, bool):
418         if value:
419             return "true"
420         else:
421             return "false"
422     elif isinstance(value, list):
423         return '[' + ', '.join(map(to_toml, value)) + ']'
424     elif isinstance(value, str):
425         # Don't put quotes around numeric values
426         if is_number(value):
427             return value
428         else:
429             return "'" + value + "'"
430     else:
431         raise RuntimeError('no toml')
432
433
434 def configure_section(lines, config):
435     for key in config:
436         value = config[key]
437         found = False
438         for i, line in enumerate(lines):
439             if not line.startswith('#' + key + ' = '):
440                 continue
441             found = True
442             lines[i] = "{} = {}".format(key, to_toml(value))
443             break
444         if not found:
445             # These are used by rpm, but aren't accepted by x.py.
446             # Give a warning that they're ignored, but not a hard error.
447             if key in ["infodir", "localstatedir"]:
448                 print("warning: {} will be ignored".format(key))
449             else:
450                 raise RuntimeError("failed to find config line for {}".format(key))
451
452
453 for section_key in config:
454     section_config = config[section_key]
455     if section_key not in sections:
456         raise RuntimeError("config key {} not in sections".format(section_key))
457
458     if section_key == 'target':
459         for target in section_config:
460             configure_section(targets[target], section_config[target])
461     else:
462         configure_section(sections[section_key], section_config)
463
464 # Now that we've built up our `config.toml`, write it all out in the same
465 # order that we read it in.
466 p("")
467 p("writing `config.toml` in current directory")
468 with bootstrap.output('config.toml') as f:
469     for section in section_order:
470         if section == 'target':
471             for target in targets:
472                 for line in targets[target]:
473                     f.write(line + "\n")
474         else:
475             for line in sections[section]:
476                 f.write(line + "\n")
477
478 with bootstrap.output('Makefile') as f:
479     contents = os.path.join(rust_dir, 'src', 'bootstrap', 'mk', 'Makefile.in')
480     contents = open(contents).read()
481     contents = contents.replace("$(CFG_SRC_DIR)", rust_dir + '/')
482     contents = contents.replace("$(CFG_PYTHON)", sys.executable)
483     f.write(contents)
484
485 p("")
486 p("run `python {}/x.py --help`".format(rust_dir))
487 p("")