]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/configure.py
Auto merge of #83314 - Aaron1011:print-unstable-value, r=lcnr
[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("build", "build.build", "GNUs ./configure syntax LLVM build triple")
164 v("host", None, "GNUs ./configure syntax LLVM host triples")
165 v("target", None, "GNUs ./configure syntax LLVM target triples")
166
167 v("set", None, "set arbitrary key/value pairs in TOML configuration")
168
169
170 def p(msg):
171     print("configure: " + msg)
172
173
174 def err(msg):
175     print("configure: error: " + msg)
176     sys.exit(1)
177
178
179 if '--help' in sys.argv or '-h' in sys.argv:
180     print('Usage: ./configure [options]')
181     print('')
182     print('Options')
183     for option in options:
184         if 'android' in option.name:
185             # no one needs to know about these obscure options
186             continue
187         if option.value:
188             print('\t{:30} {}'.format('--{}=VAL'.format(option.name), option.desc))
189         else:
190             print('\t{:30} {}'.format('--enable-{}'.format(option.name), option.desc))
191     print('')
192     print('This configure script is a thin configuration shim over the true')
193     print('configuration system, `config.toml`. You can explore the comments')
194     print('in `config.toml.example` next to this configure script to see')
195     print('more information about what each option is. Additionally you can')
196     print('pass `--set` as an argument to set arbitrary key/value pairs')
197     print('in the TOML configuration if desired')
198     print('')
199     print('Also note that all options which take `--enable` can similarly')
200     print('be passed with `--disable-foo` to forcibly disable the option')
201     sys.exit(0)
202
203 # Parse all command line arguments into one of these three lists, handling
204 # boolean and value-based options separately
205 unknown_args = []
206 need_value_args = []
207 known_args = {}
208
209 p("processing command line")
210 i = 1
211 while i < len(sys.argv):
212     arg = sys.argv[i]
213     i += 1
214     if not arg.startswith('--'):
215         unknown_args.append(arg)
216         continue
217
218     found = False
219     for option in options:
220         value = None
221         if option.value:
222             keyval = arg[2:].split('=', 1)
223             key = keyval[0]
224             if option.name != key:
225                 continue
226
227             if len(keyval) > 1:
228                 value = keyval[1]
229             elif i < len(sys.argv):
230                 value = sys.argv[i]
231                 i += 1
232             else:
233                 need_value_args.append(arg)
234                 continue
235         else:
236             if arg[2:] == 'enable-' + option.name:
237                 value = True
238             elif arg[2:] == 'disable-' + option.name:
239                 value = False
240             else:
241                 continue
242
243         found = True
244         if option.name not in known_args:
245             known_args[option.name] = []
246         known_args[option.name].append((option, value))
247         break
248
249     if not found:
250         unknown_args.append(arg)
251 p("")
252
253 # Note: here and a few other places, we use [-1] to apply the *last* value
254 # passed.  But if option-checking is enabled, then the known_args loop will
255 # also assert that options are only passed once.
256 option_checking = ('option-checking' not in known_args
257                    or known_args['option-checking'][-1][1])
258 if option_checking:
259     if len(unknown_args) > 0:
260         err("Option '" + unknown_args[0] + "' is not recognized")
261     if len(need_value_args) > 0:
262         err("Option '{0}' needs a value ({0}=val)".format(need_value_args[0]))
263
264 # Parse all known arguments into a configuration structure that reflects the
265 # TOML we're going to write out
266 config = {}
267
268
269 def build():
270     if 'build' in known_args:
271         return known_args['build'][-1][1]
272     return bootstrap.default_build_triple(verbose=False)
273
274
275 def set(key, value):
276     s = "{:20} := {}".format(key, value)
277     if len(s) < 70:
278         p(s)
279     else:
280         p(s[:70] + " ...")
281
282     arr = config
283     parts = key.split('.')
284     for i, part in enumerate(parts):
285         if i == len(parts) - 1:
286             arr[part] = value
287         else:
288             if part not in arr:
289                 arr[part] = {}
290             arr = arr[part]
291
292
293 for key in known_args:
294     # The `set` option is special and can be passed a bunch of times
295     if key == 'set':
296         for option, value in known_args[key]:
297             keyval = value.split('=', 1)
298             if len(keyval) == 1 or keyval[1] == "true":
299                 value = True
300             elif keyval[1] == "false":
301                 value = False
302             else:
303                 value = keyval[1]
304             set(keyval[0], value)
305         continue
306
307     # Ensure each option is only passed once
308     arr = known_args[key]
309     if option_checking and len(arr) > 1:
310         err("Option '{}' provided more than once".format(key))
311     option, value = arr[-1]
312
313     # If we have a clear avenue to set our value in rustbuild, do so
314     if option.rustbuild is not None:
315         set(option.rustbuild, value)
316         continue
317
318     # Otherwise we're a "special" option and need some extra handling, so do
319     # that here.
320     if option.name == 'sccache':
321         set('llvm.ccache', 'sccache')
322     elif option.name == 'local-rust':
323         for path in os.environ['PATH'].split(os.pathsep):
324             if os.path.exists(path + '/rustc'):
325                 set('build.rustc', path + '/rustc')
326                 break
327         for path in os.environ['PATH'].split(os.pathsep):
328             if os.path.exists(path + '/cargo'):
329                 set('build.cargo', path + '/cargo')
330                 break
331     elif option.name == 'local-rust-root':
332         set('build.rustc', value + '/bin/rustc')
333         set('build.cargo', value + '/bin/cargo')
334     elif option.name == 'llvm-root':
335         set('target.{}.llvm-config'.format(build()), value + '/bin/llvm-config')
336     elif option.name == 'llvm-config':
337         set('target.{}.llvm-config'.format(build()), value)
338     elif option.name == 'llvm-filecheck':
339         set('target.{}.llvm-filecheck'.format(build()), value)
340     elif option.name == 'tools':
341         set('build.tools', value.split(','))
342     elif option.name == 'host':
343         set('build.host', value.split(','))
344     elif option.name == 'target':
345         set('build.target', value.split(','))
346     elif option.name == 'full-tools':
347         set('rust.codegen-backends', ['llvm'])
348         set('rust.lld', True)
349         set('rust.llvm-tools', True)
350         set('build.extended', True)
351     elif option.name == 'option-checking':
352         # this was handled above
353         pass
354     elif option.name == 'dist-compression-formats':
355         set('dist.compression-formats', value.split(','))
356     else:
357         raise RuntimeError("unhandled option {}".format(option.name))
358
359 set('build.configure-args', sys.argv[1:])
360
361 # "Parse" the `config.toml.example` file into the various sections, and we'll
362 # use this as a template of a `config.toml` to write out which preserves
363 # all the various comments and whatnot.
364 #
365 # Note that the `target` section is handled separately as we'll duplicate it
366 # per configured target, so there's a bit of special handling for that here.
367 sections = {}
368 cur_section = None
369 sections[None] = []
370 section_order = [None]
371 targets = {}
372
373 for line in open(rust_dir + '/config.toml.example').read().split("\n"):
374     if line.startswith('['):
375         cur_section = line[1:-1]
376         if cur_section.startswith('target'):
377             cur_section = 'target'
378         elif '.' in cur_section:
379             raise RuntimeError("don't know how to deal with section: {}".format(cur_section))
380         sections[cur_section] = [line]
381         section_order.append(cur_section)
382     else:
383         sections[cur_section].append(line)
384
385 # Fill out the `targets` array by giving all configured targets a copy of the
386 # `target` section we just loaded from the example config
387 configured_targets = [build()]
388 if 'build' in config:
389     if 'host' in config['build']:
390         configured_targets += config['build']['host']
391     if 'target' in config['build']:
392         configured_targets += config['build']['target']
393 if 'target' in config:
394     for target in config['target']:
395         configured_targets.append(target)
396 for target in configured_targets:
397     targets[target] = sections['target'][:]
398     targets[target][0] = targets[target][0].replace("x86_64-unknown-linux-gnu", target)
399
400
401 def is_number(value):
402     try:
403         float(value)
404         return True
405     except ValueError:
406         return False
407
408
409 # Here we walk through the constructed configuration we have from the parsed
410 # command line arguments. We then apply each piece of configuration by
411 # basically just doing a `sed` to change the various configuration line to what
412 # we've got configure.
413 def to_toml(value):
414     if isinstance(value, bool):
415         if value:
416             return "true"
417         else:
418             return "false"
419     elif isinstance(value, list):
420         return '[' + ', '.join(map(to_toml, value)) + ']'
421     elif isinstance(value, str):
422         # Don't put quotes around numeric values
423         if is_number(value):
424             return value
425         else:
426             return "'" + value + "'"
427     else:
428         raise RuntimeError('no toml')
429
430
431 def configure_section(lines, config):
432     for key in config:
433         value = config[key]
434         found = False
435         for i, line in enumerate(lines):
436             if not line.startswith('#' + key + ' = '):
437                 continue
438             found = True
439             lines[i] = "{} = {}".format(key, to_toml(value))
440             break
441         if not found:
442             raise RuntimeError("failed to find config line for {}".format(key))
443
444
445 for section_key in config:
446     section_config = config[section_key]
447     if section_key not in sections:
448         raise RuntimeError("config key {} not in sections".format(section_key))
449
450     if section_key == 'target':
451         for target in section_config:
452             configure_section(targets[target], section_config[target])
453     else:
454         configure_section(sections[section_key], section_config)
455
456 # Now that we've built up our `config.toml`, write it all out in the same
457 # order that we read it in.
458 p("")
459 p("writing `config.toml` in current directory")
460 with bootstrap.output('config.toml') as f:
461     for section in section_order:
462         if section == 'target':
463             for target in targets:
464                 for line in targets[target]:
465                     f.write(line + "\n")
466         else:
467             for line in sections[section]:
468                 f.write(line + "\n")
469
470 with bootstrap.output('Makefile') as f:
471     contents = os.path.join(rust_dir, 'src', 'bootstrap', 'mk', 'Makefile.in')
472     contents = open(contents).read()
473     contents = contents.replace("$(CFG_SRC_DIR)", rust_dir + '/')
474     contents = contents.replace("$(CFG_PYTHON)", sys.executable)
475     f.write(contents)
476
477 p("")
478 p("run `python {}/x.py --help`".format(rust_dir))
479 p("")