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