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