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