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