]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/configure.py
Rollup merge of #65539 - traxys:fix_62334, r=petrochenkov
[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("lldb", "rust.lldb", "build lldb")
61 o("missing-tools", "dist.missing-tools", "allow failures when building tools")
62 o("use-libcxx", "llvm.use_libcxx", "build LLVM with libc++")
63
64 o("cflags", "llvm.cflags", "build LLVM with these extra compiler flags")
65 o("cxxflags", "llvm.cxxflags", "build LLVM with these extra compiler flags")
66 o("ldflags", "llvm.ldflags", "build LLVM with these extra linker flags")
67
68 o("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("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 == 'full-tools':
338         set('rust.codegen-backends', ['llvm'])
339         set('rust.lld', True)
340         set('rust.llvm-tools', True)
341         set('build.extended', True)
342     elif option.name == 'option-checking':
343         # this was handled above
344         pass
345     else:
346         raise RuntimeError("unhandled option {}".format(option.name))
347
348 set('build.configure-args', sys.argv[1:])
349
350 # "Parse" the `config.toml.example` file into the various sections, and we'll
351 # use this as a template of a `config.toml` to write out which preserves
352 # all the various comments and whatnot.
353 #
354 # Note that the `target` section is handled separately as we'll duplicate it
355 # per configured target, so there's a bit of special handling for that here.
356 sections = {}
357 cur_section = None
358 sections[None] = []
359 section_order = [None]
360 targets = {}
361
362 for line in open(rust_dir + '/config.toml.example').read().split("\n"):
363     if line.startswith('['):
364         cur_section = line[1:-1]
365         if cur_section.startswith('target'):
366             cur_section = 'target'
367         elif '.' in cur_section:
368             raise RuntimeError("don't know how to deal with section: {}".format(cur_section))
369         sections[cur_section] = [line]
370         section_order.append(cur_section)
371     else:
372         sections[cur_section].append(line)
373
374 # Fill out the `targets` array by giving all configured targets a copy of the
375 # `target` section we just loaded from the example config
376 configured_targets = [build()]
377 if 'build' in config:
378     if 'host' in config['build']:
379         configured_targets += config['build']['host']
380     if 'target' in config['build']:
381         configured_targets += config['build']['target']
382 if 'target' in config:
383     for target in config['target']:
384         configured_targets.append(target)
385 for target in configured_targets:
386     targets[target] = sections['target'][:]
387     targets[target][0] = targets[target][0].replace("x86_64-unknown-linux-gnu", target)
388
389
390 def is_number(value):
391   try:
392     float(value)
393     return True
394   except ValueError:
395     return False
396
397 # Here we walk through the constructed configuration we have from the parsed
398 # command line arguments. We then apply each piece of configuration by
399 # basically just doing a `sed` to change the various configuration line to what
400 # we've got configure.
401 def to_toml(value):
402     if isinstance(value, bool):
403         if value:
404             return "true"
405         else:
406             return "false"
407     elif isinstance(value, list):
408         return '[' + ', '.join(map(to_toml, value)) + ']'
409     elif isinstance(value, str):
410         # Don't put quotes around numeric values
411         if is_number(value):
412             return value
413         else:
414             return "'" + value + "'"
415     else:
416         raise RuntimeError('no toml')
417
418
419 def configure_section(lines, config):
420     for key in config:
421         value = config[key]
422         found = False
423         for i, line in enumerate(lines):
424             if not line.startswith('#' + key + ' = '):
425                 continue
426             found = True
427             lines[i] = "{} = {}".format(key, to_toml(value))
428             break
429         if not found:
430             raise RuntimeError("failed to find config line for {}".format(key))
431
432
433 for section_key in config:
434     section_config = config[section_key]
435     if section_key not in sections:
436         raise RuntimeError("config key {} not in sections".format(section_key))
437
438     if section_key == 'target':
439         for target in section_config:
440             configure_section(targets[target], section_config[target])
441     else:
442         configure_section(sections[section_key], section_config)
443
444 # Now that we've built up our `config.toml`, write it all out in the same
445 # order that we read it in.
446 p("")
447 p("writing `config.toml` in current directory")
448 with bootstrap.output('config.toml') as f:
449     for section in section_order:
450         if section == 'target':
451             for target in targets:
452                 for line in targets[target]:
453                     f.write(line + "\n")
454         else:
455             for line in sections[section]:
456                 f.write(line + "\n")
457
458 with bootstrap.output('Makefile') as f:
459     contents = os.path.join(rust_dir, 'src', 'bootstrap', 'mk', 'Makefile.in')
460     contents = open(contents).read()
461     contents = contents.replace("$(CFG_SRC_DIR)", rust_dir + '/')
462     contents = contents.replace("$(CFG_PYTHON)", sys.executable)
463     f.write(contents)
464
465 p("")
466 p("run `python {}/x.py --help`".format(rust_dir))
467 p("")