]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/configure.py
Rollup merge of #44117 - frewsxcv:frewsxcv-to-socket-addrs-examples, r=QuietMisdreavus
[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 an be passed a bunch of times
251     if key == 'set':
252         for option, value in known_args[key]:
253             keyval = value.split('=', 1)
254             set(keyval[0], True if len(keyval) == 1 else keyval[1])
255         continue
256
257     # Ensure each option is only passed once
258     arr = known_args[key]
259     if len(arr) > 1:
260         err("Option '{}' provided more than once".format(key))
261     option, value = arr[0]
262
263     # If we have a clear avenue to set our value in rustbuild, do so
264     if option.rustbuild is not None:
265         set(option.rustbuild, value)
266         continue
267
268     # Otherwise we're a "special" option and need some extra handling, so do
269     # that here.
270     if option.name == 'sccache':
271         set('llvm.ccache', 'sccache')
272     elif option.name == 'local-rust':
273         for path in os.environ['PATH'].split(os.pathsep):
274             if os.path.exists(path + '/rustc'):
275                 set('build.rustc', path + '/rustc')
276                 break
277         for path in os.environ['PATH'].split(os.pathsep):
278             if os.path.exists(path + '/cargo'):
279                 set('build.cargo', path + '/cargo')
280                 break
281     elif option.name == 'local-rust-root':
282         set('build.rustc', value + '/bin/rustc')
283         set('build.cargo', value + '/bin/cargo')
284     elif option.name == 'llvm-root':
285         set('target.{}.llvm-config'.format(build()), value + '/bin/llvm-config')
286     elif option.name == 'jemalloc-root':
287         set('target.{}.jemalloc'.format(build()), value + '/libjemalloc_pic.a')
288     elif option.name == 'host':
289         set('build.host', value.split(','))
290     elif option.name == 'target':
291         set('build.target', value.split(','))
292     elif option.name == 'option-checking':
293         # this was handled above
294         pass
295     else:
296         raise RuntimeError("unhandled option {}".format(option.name))
297
298 set('build.configure-args', sys.argv[1:])
299
300 # "Parse" the `config.toml.example` file into the various sections, and we'll
301 # use this as a template of a `config.toml` to write out which preserves
302 # all the various comments and whatnot.
303 #
304 # Note that the `target` section is handled separately as we'll duplicate it
305 # per configure dtarget, so there's a bit of special handling for that here.
306 sections = {}
307 cur_section = None
308 sections[None] = []
309 section_order = [None]
310 targets = {}
311
312 for line in open(rust_dir + '/config.toml.example').read().split("\n"):
313     if line.startswith('['):
314         cur_section = line[1:-1]
315         if cur_section.startswith('target'):
316             cur_section = 'target'
317         elif '.' in cur_section:
318             raise RuntimeError("don't know how to deal with section: {}".format(cur_section))
319         sections[cur_section] = [line]
320         section_order.append(cur_section)
321     else:
322         sections[cur_section].append(line)
323
324 # Fill out the `targets` array by giving all configured targets a copy of the
325 # `target` section we just loaded from the example config
326 configured_targets = [build()]
327 if 'build' in config:
328     if 'host' in config['build']:
329         configured_targets += config['build']['host']
330     if 'target' in config['build']:
331         configured_targets += config['build']['target']
332 if 'target' in config:
333     for target in config['target']:
334         configured_targets.append(target)
335 for target in configured_targets:
336     targets[target] = sections['target'][:]
337     targets[target][0] = targets[target][0].replace("x86_64-unknown-linux-gnu", target)
338
339 # Here we walk through the constructed configuration we have from the parsed
340 # command line arguemnts. We then apply each piece of configuration by
341 # basically just doing a `sed` to change the various configuration line to what
342 # we've got configure.
343 def to_toml(value):
344     if isinstance(value, bool):
345         if value:
346             return "true"
347         else:
348             return "false"
349     elif isinstance(value, list):
350         return '[' + ', '.join(map(to_toml, value)) + ']'
351     elif isinstance(value, str):
352         return "'" + value + "'"
353     else:
354         raise 'no toml'
355
356 def configure_section(lines, config):
357     for key in config:
358         value = config[key]
359         found = False
360         for i, line in enumerate(lines):
361             if not line.startswith('#' + key + ' = '):
362                 continue
363             found = True
364             lines[i] = "{} = {}".format(key, to_toml(value))
365             break
366         if not found:
367             raise RuntimeError("failed to find config line for {}".format(key))
368
369 for section_key in config:
370     section_config = config[section_key]
371     if not section_key in sections:
372         raise RuntimeError("config key {} not in sections".format(key))
373
374     if section_key == 'target':
375         for target in section_config:
376             configure_section(targets[target], section_config[target])
377     else:
378         configure_section(sections[section_key], section_config)
379
380 # Now that we've built up our `config.toml`, write it all out in the same
381 # order that we read it in.
382 p("")
383 p("writing `config.toml` in current directory")
384 with open('config.toml', 'w') as f:
385     for section in section_order:
386         if section == 'target':
387             for target in targets:
388                 for line in targets[target]:
389                     f.write(line + "\n")
390         else:
391             for line in sections[section]:
392                 f.write(line + "\n")
393
394 with open('Makefile', 'w') as f:
395     contents = os.path.join(rust_dir, 'src', 'bootstrap', 'mk', 'Makefile.in')
396     contents = open(contents).read()
397     contents = contents.replace("$(CFG_SRC_DIR)", rust_dir + '/')
398     contents = contents.replace("$(CFG_PYTHON)", sys.executable)
399     f.write(contents)
400
401 # Finally, clean up with a bit of a help message
402 relpath = os.path.dirname(__file__)
403 if relpath == '':
404     relpath = '.'
405
406 p("")
407 p("run `python {}/x.py --help`".format(relpath))
408 p("")