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