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