]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/configure.py
Rollup merge of #44548 - oyvindln:rustc_help_fix, r=arielb1
[rust.git] / src / bootstrap / configure.py
1 #!/usr/bin/env python
2 # Copyright 2017 The Rust Project Developers. See the COPYRIGHT
3 # file at the top-level directory of this distribution and at
4 # http://rust-lang.org/COPYRIGHT.
5 #
6 # Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
7 # http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
8 # <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
9 # option. This file may not be copied, modified, or distributed
10 # except according to those terms.
11
12 # ignore-tidy-linelength
13
14 import sys
15 import os
16 rust_dir = os.path.dirname(os.path.abspath(__file__))
17 rust_dir = os.path.dirname(rust_dir)
18 rust_dir = os.path.dirname(rust_dir)
19 sys.path.append(os.path.join(rust_dir, "src", "bootstrap"))
20 import bootstrap
21
22 class Option:
23     def __init__(self, name, rustbuild, desc, value):
24         self.name = name
25         self.rustbuild = rustbuild
26         self.desc = desc
27         self.value = value
28
29 options = []
30
31 def o(*args):
32     options.append(Option(*args, value=False))
33
34 def v(*args):
35     options.append(Option(*args, value=True))
36
37 o("debug", "rust.debug", "debug mode; disables optimization unless `--enable-optimize` given")
38 o("docs", "build.docs", "build standard library documentation")
39 o("compiler-docs", "build.compiler-docs", "build compiler documentation")
40 o("optimize-tests", "rust.optimize-tests", "build tests with optimizations")
41 o("test-miri", "rust.test-miri", "run miri's test suite")
42 o("debuginfo-tests", "rust.debuginfo-tests", "build tests with debugger metadata")
43 o("quiet-tests", "rust.quiet-tests", "enable quieter output when running tests")
44 o("ccache", "llvm.ccache", "invoke gcc/clang via ccache to reuse object files between builds")
45 o("sccache", None, "invoke gcc/clang via sccache to reuse object files between builds")
46 o("local-rust", None, "use an installed rustc rather than downloading a snapshot")
47 v("local-rust-root", None, "set prefix for local rust binary")
48 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")
49 o("llvm-static-stdcpp", "llvm.static-libstdcpp", "statically link to libstdc++ for LLVM")
50 o("llvm-link-shared", "llvm.link-shared", "prefer shared linking to LLVM (llvm-config --link-shared)")
51 o("rpath", "rust.rpath", "build rpaths into rustc itself")
52 o("llvm-version-check", "llvm.version-check", "check if the LLVM version is supported, build anyway")
53 o("codegen-tests", "rust.codegen-tests", "run the src/test/codegen tests")
54 o("option-checking", None, "complain about unrecognized options in this configure script")
55 o("ninja", "llvm.ninja", "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)")
56 o("locked-deps", "build.locked-deps", "force Cargo.lock to be up to date")
57 o("vendor", "build.vendor", "enable usage of vendored Rust crates")
58 o("sanitizers", "build.sanitizers", "build the sanitizer runtimes (asan, lsan, msan, tsan)")
59 o("dist-src", "rust.dist-src", "when building tarballs enables building a source tarball")
60 o("cargo-openssl-static", "build.openssl-static", "static openssl in cargo")
61 o("profiler", "build.profiler", "build the profiler runtime")
62
63 # Optimization and debugging options. These may be overridden by the release
64 # channel, etc.
65 o("optimize", "rust.optimize", "build optimized rust code")
66 o("optimize-llvm", "llvm.optimize", "build optimized LLVM")
67 o("llvm-assertions", "llvm.assertions", "build LLVM with assertions")
68 o("debug-assertions", "rust.debug-assertions", "build with debugging assertions")
69 o("llvm-release-debuginfo", "llvm.release-debuginfo", "build LLVM with debugger metadata")
70 o("debuginfo", "rust.debuginfo", "build with debugger metadata")
71 o("debuginfo-lines", "rust.debuginfo-lines", "build with line number debugger metadata")
72 o("debuginfo-only-std", "rust.debuginfo-only-std", "build only libstd with debugging information")
73 o("debug-jemalloc", "rust.debug-jemalloc", "build jemalloc with --enable-debug --enable-fill")
74
75 v("prefix", "install.prefix", "set installation prefix")
76 v("localstatedir", "install.localstatedir", "local state directory")
77 v("datadir", "install.datadir", "install data")
78 v("sysconfdir", "install.sysconfdir", "install system configuration files")
79 v("infodir", "install.infodir", "install additional info")
80 v("libdir", "install.libdir", "install libraries")
81 v("mandir", "install.mandir", "install man pages in PATH")
82 v("docdir", "install.docdir", "install documentation in PATH")
83 v("bindir", "install.bindir", "install binaries")
84
85 v("llvm-root", None, "set LLVM root")
86 v("python", "build.python", "set path to python")
87 v("jemalloc-root", None, "set directory where libjemalloc_pic.a is located")
88 v("android-cross-path", "target.arm-linux-androideabi.android-ndk",
89   "Android NDK standalone path (deprecated)")
90 v("i686-linux-android-ndk", "target.i686-linux-android.android-ndk",
91   "i686-linux-android NDK standalone path")
92 v("arm-linux-androideabi-ndk", "target.arm-linux-androideabi.android-ndk",
93   "arm-linux-androideabi NDK standalone path")
94 v("armv7-linux-androideabi-ndk", "target.armv7-linux-androideabi.android-ndk",
95   "armv7-linux-androideabi NDK standalone path")
96 v("aarch64-linux-android-ndk", "target.aarch64-linux-android.android-ndk",
97   "aarch64-linux-android NDK standalone path")
98 v("x86_64-linux-android-ndk", "target.x86_64-linux-android.android-ndk",
99   "x86_64-linux-android NDK standalone path")
100 v("musl-root", "target.x86_64-unknown-linux-musl.musl-root",
101   "MUSL root installation directory (deprecated)")
102 v("musl-root-x86_64", "target.x86_64-unknown-linux-musl.musl-root",
103   "x86_64-unknown-linux-musl install directory")
104 v("musl-root-i686", "target.i686-unknown-linux-musl.musl-root",
105   "i686-unknown-linux-musl install directory")
106 v("musl-root-arm", "target.arm-unknown-linux-musleabi.musl-root",
107   "arm-unknown-linux-musleabi install directory")
108 v("musl-root-armhf", "target.arm-unknown-linux-musleabihf.musl-root",
109   "arm-unknown-linux-musleabihf install directory")
110 v("musl-root-armv7", "target.armv7-unknown-linux-musleabihf.musl-root",
111   "armv7-unknown-linux-musleabihf install directory")
112 v("qemu-armhf-rootfs", "target.arm-unknown-linux-gnueabihf.qemu-rootfs",
113   "rootfs in qemu testing, you probably don't want to use this")
114 v("qemu-aarch64-rootfs", "target.aarch64-unknown-linux-gnu.qemu-rootfs",
115   "rootfs in qemu testing, you probably don't want to use this")
116 v("experimental-targets", "llvm.experimental-targets",
117   "experimental LLVM targets to build")
118 v("release-channel", "rust.channel", "the name of the release channel to build")
119
120 # Used on systems where "cc" and "ar" are unavailable
121 v("default-linker", "rust.default-linker", "the default linker")
122 v("default-ar", "rust.default-ar", "the default ar")
123
124 # Many of these are saved below during the "writing configuration" step
125 # (others are conditionally saved).
126 o("manage-submodules", "build.submodules", "let the build manage the git submodules")
127 o("jemalloc", "rust.use-jemalloc", "build liballoc with jemalloc")
128 o("full-bootstrap", "build.full-bootstrap", "build three compilers instead of two")
129 o("extended", "build.extended", "build an extended rust tool set")
130
131 v("build", "build.build", "GNUs ./configure syntax LLVM build triple")
132 v("host", None, "GNUs ./configure syntax LLVM host triples")
133 v("target", None, "GNUs ./configure syntax LLVM target triples")
134
135 v("set", None, "set arbitrary key/value pairs in TOML configuration")
136
137 def p(msg):
138     print("configure: " + msg)
139
140 def err(msg):
141     print("configure: error: " + msg)
142     sys.exit(1)
143
144 if '--help' in sys.argv or '-h' in sys.argv:
145     print('Usage: ./configure [options]')
146     print('')
147     print('Options')
148     for option in options:
149         if 'android' in option.name:
150             # no one needs to know about these obscure options
151             continue
152         if option.value:
153             print('\t{:30} {}'.format('--{}=VAL'.format(option.name), option.desc))
154         else:
155             print('\t{:30} {}'.format('--enable-{}'.format(option.name), option.desc))
156     print('')
157     print('This configure script is a thin configuration shim over the true')
158     print('configuration system, `config.toml`. You can explore the comments')
159     print('in `config.toml.example` next to this configure script to see')
160     print('more information about what each option is. Additionally you can')
161     print('pass `--set` as an argument to set arbitrary key/value pairs')
162     print('in the TOML configuration if desired')
163     print('')
164     print('Also note that all options which take `--enable` can similarly')
165     print('be passed with `--disable-foo` to forcibly disable the option')
166     sys.exit(0)
167
168 # Parse all command line arguments into one of these three lists, handling
169 # boolean and value-based options separately
170 unknown_args = []
171 need_value_args = []
172 known_args = {}
173
174 p("processing command line")
175 i = 1
176 while i < len(sys.argv):
177     arg = sys.argv[i]
178     i += 1
179     if not arg.startswith('--'):
180         unknown_args.append(arg)
181         continue
182
183     found = False
184     for option in options:
185         value = None
186         if option.value:
187             keyval = arg[2:].split('=', 1)
188             key = keyval[0]
189             if option.name != key:
190                 continue
191
192             if len(keyval) > 1:
193                 value = keyval[1]
194             elif i < len(sys.argv):
195                 value = sys.argv[i]
196                 i += 1
197             else:
198                 need_value_args.append(arg)
199                 continue
200         else:
201             if arg[2:] == 'enable-' + option.name:
202                 value = True
203             elif arg[2:] == 'disable-' + option.name:
204                 value = False
205             else:
206                 continue
207
208         found = True
209         if not option.name in known_args:
210             known_args[option.name] = []
211         known_args[option.name].append((option, value))
212         break
213
214     if not found:
215         unknown_args.append(arg)
216 p("")
217
218 if 'option-checking' not in known_args or known_args['option-checking'][1]:
219     if len(unknown_args) > 0:
220         err("Option '" + unknown_args[0] + "' is not recognized")
221     if len(need_value_args) > 0:
222         err("Option '{0}' needs a value ({0}=val)".format(need_value_args[0]))
223
224 # Parse all known arguments into a configuration structure that reflects the
225 # TOML we're going to write out
226 config = {}
227
228 def build():
229     if 'build' in known_args:
230         return known_args['build'][0][1]
231     return bootstrap.default_build_triple()
232
233 def set(key, value):
234       s = "{:20} := {}".format(key, value)
235       if len(s) < 70:
236           p(s)
237       else:
238           p(s[:70] + " ...")
239
240       arr = config
241       parts = key.split('.')
242       for i, part in enumerate(parts):
243           if i == len(parts) - 1:
244               arr[part] = value
245           else:
246               if not part in arr:
247                   arr[part] = {}
248               arr = arr[part]
249
250 for key in known_args:
251     # The `set` option is special and can be passed a bunch of times
252     if key == 'set':
253         for option, value in known_args[key]:
254             keyval = value.split('=', 1)
255             if len(keyval) == 1 or keyval[1] == "true":
256                 value = True
257             elif keyval[1] == "false":
258                 value = False
259             else:
260                 value = keyval[1]
261             set(keyval[0], value)
262         continue
263
264     # Ensure each option is only passed once
265     arr = known_args[key]
266     if len(arr) > 1:
267         err("Option '{}' provided more than once".format(key))
268     option, value = arr[0]
269
270     # If we have a clear avenue to set our value in rustbuild, do so
271     if option.rustbuild is not None:
272         set(option.rustbuild, value)
273         continue
274
275     # Otherwise we're a "special" option and need some extra handling, so do
276     # that here.
277     if option.name == 'sccache':
278         set('llvm.ccache', 'sccache')
279     elif option.name == 'local-rust':
280         for path in os.environ['PATH'].split(os.pathsep):
281             if os.path.exists(path + '/rustc'):
282                 set('build.rustc', path + '/rustc')
283                 break
284         for path in os.environ['PATH'].split(os.pathsep):
285             if os.path.exists(path + '/cargo'):
286                 set('build.cargo', path + '/cargo')
287                 break
288     elif option.name == 'local-rust-root':
289         set('build.rustc', value + '/bin/rustc')
290         set('build.cargo', value + '/bin/cargo')
291     elif option.name == 'llvm-root':
292         set('target.{}.llvm-config'.format(build()), value + '/bin/llvm-config')
293     elif option.name == 'jemalloc-root':
294         set('target.{}.jemalloc'.format(build()), value + '/libjemalloc_pic.a')
295     elif option.name == 'host':
296         set('build.host', value.split(','))
297     elif option.name == 'target':
298         set('build.target', value.split(','))
299     elif option.name == 'option-checking':
300         # this was handled above
301         pass
302     else:
303         raise RuntimeError("unhandled option {}".format(option.name))
304
305 set('build.configure-args', sys.argv[1:])
306
307 # "Parse" the `config.toml.example` file into the various sections, and we'll
308 # use this as a template of a `config.toml` to write out which preserves
309 # all the various comments and whatnot.
310 #
311 # Note that the `target` section is handled separately as we'll duplicate it
312 # per configure dtarget, so there's a bit of special handling for that here.
313 sections = {}
314 cur_section = None
315 sections[None] = []
316 section_order = [None]
317 targets = {}
318
319 for line in open(rust_dir + '/config.toml.example').read().split("\n"):
320     if line.startswith('['):
321         cur_section = line[1:-1]
322         if cur_section.startswith('target'):
323             cur_section = 'target'
324         elif '.' in cur_section:
325             raise RuntimeError("don't know how to deal with section: {}".format(cur_section))
326         sections[cur_section] = [line]
327         section_order.append(cur_section)
328     else:
329         sections[cur_section].append(line)
330
331 # Fill out the `targets` array by giving all configured targets a copy of the
332 # `target` section we just loaded from the example config
333 configured_targets = [build()]
334 if 'build' in config:
335     if 'host' in config['build']:
336         configured_targets += config['build']['host']
337     if 'target' in config['build']:
338         configured_targets += config['build']['target']
339 if 'target' in config:
340     for target in config['target']:
341         configured_targets.append(target)
342 for target in configured_targets:
343     targets[target] = sections['target'][:]
344     targets[target][0] = targets[target][0].replace("x86_64-unknown-linux-gnu", target)
345
346 # Here we walk through the constructed configuration we have from the parsed
347 # command line arguemnts. We then apply each piece of configuration by
348 # basically just doing a `sed` to change the various configuration line to what
349 # we've got configure.
350 def to_toml(value):
351     if isinstance(value, bool):
352         if value:
353             return "true"
354         else:
355             return "false"
356     elif isinstance(value, list):
357         return '[' + ', '.join(map(to_toml, value)) + ']'
358     elif isinstance(value, str):
359         return "'" + value + "'"
360     else:
361         raise 'no toml'
362
363 def configure_section(lines, config):
364     for key in config:
365         value = config[key]
366         found = False
367         for i, line in enumerate(lines):
368             if not line.startswith('#' + key + ' = '):
369                 continue
370             found = True
371             lines[i] = "{} = {}".format(key, to_toml(value))
372             break
373         if not found:
374             raise RuntimeError("failed to find config line for {}".format(key))
375
376 for section_key in config:
377     section_config = config[section_key]
378     if not section_key in sections:
379         raise RuntimeError("config key {} not in sections".format(key))
380
381     if section_key == 'target':
382         for target in section_config:
383             configure_section(targets[target], section_config[target])
384     else:
385         configure_section(sections[section_key], section_config)
386
387 # Now that we've built up our `config.toml`, write it all out in the same
388 # order that we read it in.
389 p("")
390 p("writing `config.toml` in current directory")
391 with open('config.toml', 'w') as f:
392     for section in section_order:
393         if section == 'target':
394             for target in targets:
395                 for line in targets[target]:
396                     f.write(line + "\n")
397         else:
398             for line in sections[section]:
399                 f.write(line + "\n")
400
401 with open('Makefile', 'w') as f:
402     contents = os.path.join(rust_dir, 'src', 'bootstrap', 'mk', 'Makefile.in')
403     contents = open(contents).read()
404     contents = contents.replace("$(CFG_SRC_DIR)", rust_dir + '/')
405     contents = contents.replace("$(CFG_PYTHON)", sys.executable)
406     f.write(contents)
407
408 # Finally, clean up with a bit of a help message
409 relpath = os.path.dirname(__file__)
410 if relpath == '':
411     relpath = '.'
412
413 p("")
414 p("run `python {}/x.py --help`".format(relpath))
415 p("")