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