]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/configure.py
Rollup merge of #58182 - jethrogb:jb/sgx-bytebuffer-len-0, r=joshtriplett
[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("debuginfo-tests", "rust.debuginfo-tests", "build tests with debugger metadata")
41 o("verbose-tests", "rust.verbose-tests", "enable verbose output when running tests")
42 o("ccache", "llvm.ccache", "invoke gcc/clang via ccache to reuse object files between builds")
43 o("sccache", None, "invoke gcc/clang via sccache to reuse object files between builds")
44 o("local-rust", None, "use an installed rustc rather than downloading a snapshot")
45 v("local-rust-root", None, "set prefix for local rust binary")
46 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")
47 o("llvm-static-stdcpp", "llvm.static-libstdcpp", "statically link to libstdc++ for LLVM")
48 o("llvm-link-shared", "llvm.link-shared", "prefer shared linking to LLVM (llvm-config --link-shared)")
49 o("rpath", "rust.rpath", "build rpaths into rustc itself")
50 o("llvm-version-check", "llvm.version-check", "check if the LLVM version is supported, build anyway")
51 o("codegen-tests", "rust.codegen-tests", "run the src/test/codegen tests")
52 o("option-checking", None, "complain about unrecognized options in this configure script")
53 o("ninja", "llvm.ninja", "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)")
54 o("locked-deps", "build.locked-deps", "force Cargo.lock to be up to date")
55 o("vendor", "build.vendor", "enable usage of vendored Rust crates")
56 o("sanitizers", "build.sanitizers", "build the sanitizer runtimes (asan, lsan, msan, tsan)")
57 o("dist-src", "rust.dist-src", "when building tarballs enables building a source tarball")
58 o("cargo-native-static", "build.cargo-native-static", "static native libraries in cargo")
59 o("profiler", "build.profiler", "build the profiler runtime")
60 o("emscripten", None, "compile the emscripten backend as well as LLVM")
61 o("full-tools", None, "enable all tools")
62 o("lld", "rust.lld", "build lld")
63 o("lldb", "rust.lldb", "build lldb")
64 o("missing-tools", "dist.missing-tools", "allow failures when building tools")
65 o("use-libcxx", "llvm.use_libcxx", "build LLVM with libc++")
66
67 o("cflags", "llvm.cflags", "build LLVM with these extra compiler flags")
68 o("cxxflags", "llvm.cxxflags", "build LLVM with these extra compiler flags")
69 o("ldflags", "llvm.ldflags", "build LLVM with these extra linker flags")
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 o("debuginfo", "rust.debuginfo", "build with debugger metadata")
79 o("debuginfo-lines", "rust.debuginfo-lines", "build with line number debugger metadata")
80 o("debuginfo-only-std", "rust.debuginfo-only-std", "build only libstd with debugging information")
81 o("debuginfo-tools", "rust.debuginfo-tools", "build extended tools with debugging information")
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-musleabihf.musl-root",
127   "armv7-unknown-linux-musleabihf install directory")
128 v("musl-root-aarch64", "target.aarch64-unknown-linux-musl.musl-root",
129   "aarch64-unknown-linux-musl install directory")
130 v("musl-root-mips", "target.mips-unknown-linux-musl.musl-root",
131   "mips-unknown-linux-musl install directory")
132 v("musl-root-mipsel", "target.mipsel-unknown-linux-musl.musl-root",
133   "mipsel-unknown-linux-musl install directory")
134 v("qemu-armhf-rootfs", "target.arm-unknown-linux-gnueabihf.qemu-rootfs",
135   "rootfs in qemu testing, you probably don't want to use this")
136 v("qemu-aarch64-rootfs", "target.aarch64-unknown-linux-gnu.qemu-rootfs",
137   "rootfs in qemu testing, you probably don't want to use this")
138 v("experimental-targets", "llvm.experimental-targets",
139   "experimental LLVM targets to build")
140 v("release-channel", "rust.channel", "the name of the release channel to build")
141
142 # Used on systems where "cc" is unavailable
143 v("default-linker", "rust.default-linker", "the default linker")
144
145 # Many of these are saved below during the "writing configuration" step
146 # (others are conditionally saved).
147 o("manage-submodules", "build.submodules", "let the build manage the git submodules")
148 o("full-bootstrap", "build.full-bootstrap", "build three compilers instead of two")
149 o("extended", "build.extended", "build an extended rust tool set")
150
151 v("tools", None, "List of extended tools will be installed")
152 v("build", "build.build", "GNUs ./configure syntax LLVM build triple")
153 v("host", None, "GNUs ./configure syntax LLVM host triples")
154 v("target", None, "GNUs ./configure syntax LLVM target triples")
155
156 v("set", None, "set arbitrary key/value pairs in TOML configuration")
157
158
159 def p(msg):
160     print("configure: " + msg)
161
162
163 def err(msg):
164     print("configure: error: " + msg)
165     sys.exit(1)
166
167
168 if '--help' in sys.argv or '-h' in sys.argv:
169     print('Usage: ./configure [options]')
170     print('')
171     print('Options')
172     for option in options:
173         if 'android' in option.name:
174             # no one needs to know about these obscure options
175             continue
176         if option.value:
177             print('\t{:30} {}'.format('--{}=VAL'.format(option.name), option.desc))
178         else:
179             print('\t{:30} {}'.format('--enable-{}'.format(option.name), option.desc))
180     print('')
181     print('This configure script is a thin configuration shim over the true')
182     print('configuration system, `config.toml`. You can explore the comments')
183     print('in `config.toml.example` next to this configure script to see')
184     print('more information about what each option is. Additionally you can')
185     print('pass `--set` as an argument to set arbitrary key/value pairs')
186     print('in the TOML configuration if desired')
187     print('')
188     print('Also note that all options which take `--enable` can similarly')
189     print('be passed with `--disable-foo` to forcibly disable the option')
190     sys.exit(0)
191
192 # Parse all command line arguments into one of these three lists, handling
193 # boolean and value-based options separately
194 unknown_args = []
195 need_value_args = []
196 known_args = {}
197
198 p("processing command line")
199 i = 1
200 while i < len(sys.argv):
201     arg = sys.argv[i]
202     i += 1
203     if not arg.startswith('--'):
204         unknown_args.append(arg)
205         continue
206
207     found = False
208     for option in options:
209         value = None
210         if option.value:
211             keyval = arg[2:].split('=', 1)
212             key = keyval[0]
213             if option.name != key:
214                 continue
215
216             if len(keyval) > 1:
217                 value = keyval[1]
218             elif i < len(sys.argv):
219                 value = sys.argv[i]
220                 i += 1
221             else:
222                 need_value_args.append(arg)
223                 continue
224         else:
225             if arg[2:] == 'enable-' + option.name:
226                 value = True
227             elif arg[2:] == 'disable-' + option.name:
228                 value = False
229             else:
230                 continue
231
232         found = True
233         if option.name not in known_args:
234             known_args[option.name] = []
235         known_args[option.name].append((option, value))
236         break
237
238     if not found:
239         unknown_args.append(arg)
240 p("")
241
242 # Note: here and a few other places, we use [-1] to apply the *last* value
243 # passed.  But if option-checking is enabled, then the known_args loop will
244 # also assert that options are only passed once.
245 option_checking = ('option-checking' not in known_args
246                    or known_args['option-checking'][-1][1])
247 if option_checking:
248     if len(unknown_args) > 0:
249         err("Option '" + unknown_args[0] + "' is not recognized")
250     if len(need_value_args) > 0:
251         err("Option '{0}' needs a value ({0}=val)".format(need_value_args[0]))
252
253 # Parse all known arguments into a configuration structure that reflects the
254 # TOML we're going to write out
255 config = {}
256
257
258 def build():
259     if 'build' in known_args:
260         return known_args['build'][-1][1]
261     return bootstrap.default_build_triple()
262
263
264 def set(key, value):
265     s = "{:20} := {}".format(key, value)
266     if len(s) < 70:
267         p(s)
268     else:
269         p(s[:70] + " ...")
270
271     arr = config
272     parts = key.split('.')
273     for i, part in enumerate(parts):
274         if i == len(parts) - 1:
275             arr[part] = value
276         else:
277             if part not in arr:
278                 arr[part] = {}
279             arr = arr[part]
280
281
282 for key in known_args:
283     # The `set` option is special and can be passed a bunch of times
284     if key == 'set':
285         for option, value in known_args[key]:
286             keyval = value.split('=', 1)
287             if len(keyval) == 1 or keyval[1] == "true":
288                 value = True
289             elif keyval[1] == "false":
290                 value = False
291             else:
292                 value = keyval[1]
293             set(keyval[0], value)
294         continue
295
296     # Ensure each option is only passed once
297     arr = known_args[key]
298     if option_checking and len(arr) > 1:
299         err("Option '{}' provided more than once".format(key))
300     option, value = arr[-1]
301
302     # If we have a clear avenue to set our value in rustbuild, do so
303     if option.rustbuild is not None:
304         set(option.rustbuild, value)
305         continue
306
307     # Otherwise we're a "special" option and need some extra handling, so do
308     # that here.
309     if option.name == 'sccache':
310         set('llvm.ccache', 'sccache')
311     elif option.name == 'local-rust':
312         for path in os.environ['PATH'].split(os.pathsep):
313             if os.path.exists(path + '/rustc'):
314                 set('build.rustc', path + '/rustc')
315                 break
316         for path in os.environ['PATH'].split(os.pathsep):
317             if os.path.exists(path + '/cargo'):
318                 set('build.cargo', path + '/cargo')
319                 break
320     elif option.name == 'local-rust-root':
321         set('build.rustc', value + '/bin/rustc')
322         set('build.cargo', value + '/bin/cargo')
323     elif option.name == 'llvm-root':
324         set('target.{}.llvm-config'.format(build()), value + '/bin/llvm-config')
325     elif option.name == 'llvm-config':
326         set('target.{}.llvm-config'.format(build()), value)
327     elif option.name == 'llvm-filecheck':
328         set('target.{}.llvm-filecheck'.format(build()), value)
329     elif option.name == 'tools':
330         set('build.tools', value.split(','))
331     elif option.name == 'host':
332         set('build.host', value.split(','))
333     elif option.name == 'target':
334         set('build.target', value.split(','))
335     elif option.name == 'emscripten':
336         set('rust.codegen-backends', ['llvm', 'emscripten'])
337     elif option.name == 'full-tools':
338         set('rust.codegen-backends', ['llvm', 'emscripten'])
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("")