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