]>
Commit | Line | Data |
---|---|---|
7453a54e SL |
1 | # Copyright 2015-2016 The Rust Project Developers. See the COPYRIGHT |
2 | # file at the top-level directory of this distribution and at | |
3 | # http://rust-lang.org/COPYRIGHT. | |
4 | # | |
5 | # Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | # http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | # <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | # option. This file may not be copied, modified, or distributed | |
9 | # except according to those terms. | |
10 | ||
11 | import argparse | |
12 | import contextlib | |
13 | import os | |
14 | import shutil | |
15 | import subprocess | |
16 | import sys | |
17 | import tarfile | |
18 | ||
19 | def get(url, path, verbose=False): | |
20 | print("downloading " + url) | |
21 | # see http://serverfault.com/questions/301128/how-to-download | |
22 | if sys.platform == 'win32': | |
23 | run(["PowerShell.exe", "/nologo", "-Command", | |
24 | "(New-Object System.Net.WebClient).DownloadFile('" + url + | |
25 | "', '" + path + "')"], verbose=verbose) | |
26 | else: | |
27 | run(["curl", "-o", path, url], verbose=verbose) | |
28 | ||
29 | def unpack(tarball, dst, verbose=False, match=None): | |
30 | print("extracting " + tarball) | |
31 | fname = os.path.basename(tarball).replace(".tar.gz", "") | |
32 | with contextlib.closing(tarfile.open(tarball)) as tar: | |
33 | for p in tar.getnames(): | |
34 | if "/" not in p: | |
35 | continue | |
36 | name = p.replace(fname + "/", "", 1) | |
37 | if match is not None and not name.startswith(match): | |
38 | continue | |
39 | name = name[len(match) + 1:] | |
40 | ||
41 | fp = os.path.join(dst, name) | |
42 | if verbose: | |
43 | print(" extracting " + p) | |
44 | tar.extract(p, dst) | |
45 | tp = os.path.join(dst, p) | |
46 | if os.path.isdir(tp) and os.path.exists(fp): | |
47 | continue | |
48 | shutil.move(tp, fp) | |
49 | shutil.rmtree(os.path.join(dst, fname)) | |
50 | ||
51 | def run(args, verbose=False): | |
52 | if verbose: | |
53 | print("running: " + ' '.join(args)) | |
54 | sys.stdout.flush() | |
55 | # Use Popen here instead of call() as it apparently allows powershell on | |
56 | # Windows to not lock up waiting for input presumably. | |
57 | ret = subprocess.Popen(args) | |
58 | code = ret.wait() | |
59 | if code != 0: | |
60 | if not verbose: | |
61 | print("failed to run: " + ' '.join(args)) | |
62 | raise RuntimeError("failed to run command") | |
63 | ||
64 | class RustBuild: | |
65 | def download_rust_nightly(self): | |
66 | cache_dst = os.path.join(self.build_dir, "cache") | |
67 | rustc_cache = os.path.join(cache_dst, self.snap_rustc_date()) | |
68 | cargo_cache = os.path.join(cache_dst, self.snap_cargo_date()) | |
69 | if not os.path.exists(rustc_cache): | |
70 | os.makedirs(rustc_cache) | |
71 | if not os.path.exists(cargo_cache): | |
72 | os.makedirs(cargo_cache) | |
73 | ||
74 | if self.rustc().startswith(self.bin_root()) and \ | |
75 | (not os.path.exists(self.rustc()) or self.rustc_out_of_date()): | |
54a0048b SL |
76 | if os.path.exists(self.bin_root()): |
77 | shutil.rmtree(self.bin_root()) | |
7453a54e SL |
78 | filename = "rust-std-nightly-" + self.build + ".tar.gz" |
79 | url = "https://static.rust-lang.org/dist/" + self.snap_rustc_date() | |
80 | tarball = os.path.join(rustc_cache, filename) | |
81 | if not os.path.exists(tarball): | |
82 | get(url + "/" + filename, tarball, verbose=self.verbose) | |
83 | unpack(tarball, self.bin_root(), | |
84 | match="rust-std-" + self.build, | |
85 | verbose=self.verbose) | |
86 | ||
87 | filename = "rustc-nightly-" + self.build + ".tar.gz" | |
88 | url = "https://static.rust-lang.org/dist/" + self.snap_rustc_date() | |
89 | tarball = os.path.join(rustc_cache, filename) | |
90 | if not os.path.exists(tarball): | |
91 | get(url + "/" + filename, tarball, verbose=self.verbose) | |
92 | unpack(tarball, self.bin_root(), match="rustc", verbose=self.verbose) | |
93 | with open(self.rustc_stamp(), 'w') as f: | |
94 | f.write(self.snap_rustc_date()) | |
95 | ||
96 | if self.cargo().startswith(self.bin_root()) and \ | |
97 | (not os.path.exists(self.cargo()) or self.cargo_out_of_date()): | |
98 | filename = "cargo-nightly-" + self.build + ".tar.gz" | |
99 | url = "https://static.rust-lang.org/cargo-dist/" + self.snap_cargo_date() | |
100 | tarball = os.path.join(cargo_cache, filename) | |
101 | if not os.path.exists(tarball): | |
102 | get(url + "/" + filename, tarball, verbose=self.verbose) | |
103 | unpack(tarball, self.bin_root(), match="cargo", verbose=self.verbose) | |
104 | with open(self.cargo_stamp(), 'w') as f: | |
105 | f.write(self.snap_cargo_date()) | |
106 | ||
107 | def snap_cargo_date(self): | |
108 | return self._cargo_date | |
109 | ||
110 | def snap_rustc_date(self): | |
111 | return self._rustc_date | |
112 | ||
113 | def rustc_stamp(self): | |
114 | return os.path.join(self.bin_root(), '.rustc-stamp') | |
115 | ||
116 | def cargo_stamp(self): | |
117 | return os.path.join(self.bin_root(), '.cargo-stamp') | |
118 | ||
119 | def rustc_out_of_date(self): | |
120 | if not os.path.exists(self.rustc_stamp()): | |
121 | return True | |
122 | with open(self.rustc_stamp(), 'r') as f: | |
123 | return self.snap_rustc_date() != f.read() | |
124 | ||
125 | def cargo_out_of_date(self): | |
126 | if not os.path.exists(self.cargo_stamp()): | |
127 | return True | |
128 | with open(self.cargo_stamp(), 'r') as f: | |
129 | return self.snap_cargo_date() != f.read() | |
130 | ||
131 | def bin_root(self): | |
132 | return os.path.join(self.build_dir, self.build, "stage0") | |
133 | ||
134 | def get_toml(self, key): | |
135 | for line in self.config_toml.splitlines(): | |
136 | if line.startswith(key + ' ='): | |
137 | return self.get_string(line) | |
138 | return None | |
139 | ||
140 | def get_mk(self, key): | |
141 | for line in iter(self.config_mk.splitlines()): | |
142 | if line.startswith(key): | |
143 | return line[line.find(':=') + 2:].strip() | |
144 | return None | |
145 | ||
146 | def cargo(self): | |
147 | config = self.get_toml('cargo') | |
148 | if config: | |
149 | return config | |
150 | return os.path.join(self.bin_root(), "bin/cargo" + self.exe_suffix()) | |
151 | ||
152 | def rustc(self): | |
153 | config = self.get_toml('rustc') | |
154 | if config: | |
155 | return config | |
156 | config = self.get_mk('CFG_LOCAL_RUST') | |
157 | if config: | |
158 | return config + '/bin/rustc' + self.exe_suffix() | |
159 | return os.path.join(self.bin_root(), "bin/rustc" + self.exe_suffix()) | |
160 | ||
161 | def get_string(self, line): | |
162 | start = line.find('"') | |
163 | end = start + 1 + line[start+1:].find('"') | |
164 | return line[start+1:end] | |
165 | ||
166 | def exe_suffix(self): | |
167 | if sys.platform == 'win32': | |
168 | return '.exe' | |
169 | else: | |
170 | return '' | |
171 | ||
172 | def parse_nightly_dates(self): | |
173 | nightlies = os.path.join(self.rust_root, "src/nightlies.txt") | |
174 | with open(nightlies, 'r') as nightlies: | |
175 | rustc, cargo = nightlies.read().split("\n")[:2] | |
176 | assert rustc.startswith("rustc: ") | |
177 | assert cargo.startswith("cargo: ") | |
178 | self._rustc_date = rustc[len("rustc: "):] | |
179 | self._cargo_date = cargo[len("cargo: "):] | |
180 | ||
181 | def build_bootstrap(self): | |
182 | env = os.environ.copy() | |
183 | env["CARGO_TARGET_DIR"] = os.path.join(self.build_dir, "bootstrap") | |
184 | env["RUSTC"] = self.rustc() | |
185 | env["LD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") | |
186 | env["DYLD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") | |
187 | env["PATH"] = os.path.join(self.bin_root(), "bin") + \ | |
188 | os.pathsep + env["PATH"] | |
189 | self.run([self.cargo(), "build", "--manifest-path", | |
190 | os.path.join(self.rust_root, "src/bootstrap/Cargo.toml")], | |
191 | env) | |
192 | ||
193 | def run(self, args, env): | |
194 | proc = subprocess.Popen(args, env = env) | |
195 | ret = proc.wait() | |
196 | if ret != 0: | |
197 | sys.exit(ret) | |
198 | ||
199 | def build_triple(self): | |
200 | config = self.get_toml('build') | |
201 | if config: | |
202 | return config | |
203 | config = self.get_mk('CFG_BUILD') | |
204 | if config: | |
205 | return config | |
206 | try: | |
207 | ostype = subprocess.check_output(['uname', '-s']).strip() | |
208 | cputype = subprocess.check_output(['uname', '-m']).strip() | |
209 | except FileNotFoundError: | |
210 | if sys.platform == 'win32': | |
211 | return 'x86_64-pc-windows-msvc' | |
212 | else: | |
213 | raise | |
214 | ||
215 | # Darwin's `uname -s` lies and always returns i386. We have to use | |
216 | # sysctl instead. | |
217 | if ostype == 'Darwin' and cputype == 'i686': | |
218 | sysctl = subprocess.check_output(['sysctl', 'hw.optional.x86_64']) | |
219 | if sysctl.contains(': 1'): | |
220 | cputype = 'x86_64' | |
221 | ||
222 | # The goal here is to come up with the same triple as LLVM would, | |
223 | # at least for the subset of platforms we're willing to target. | |
224 | if ostype == 'Linux': | |
225 | ostype = 'unknown-linux-gnu' | |
226 | elif ostype == 'FreeBSD': | |
227 | ostype = 'unknown-freebsd' | |
228 | elif ostype == 'DragonFly': | |
229 | ostype = 'unknown-dragonfly' | |
230 | elif ostype == 'Bitrig': | |
231 | ostype = 'unknown-bitrig' | |
232 | elif ostype == 'OpenBSD': | |
233 | ostype = 'unknown-openbsd' | |
234 | elif ostype == 'NetBSD': | |
235 | ostype = 'unknown-netbsd' | |
236 | elif ostype == 'Darwin': | |
237 | ostype = 'apple-darwin' | |
238 | elif ostype.startswith('MINGW'): | |
239 | # msys' `uname` does not print gcc configuration, but prints msys | |
240 | # configuration. so we cannot believe `uname -m`: | |
241 | # msys1 is always i686 and msys2 is always x86_64. | |
242 | # instead, msys defines $MSYSTEM which is MINGW32 on i686 and | |
243 | # MINGW64 on x86_64. | |
244 | ostype = 'pc-windows-gnu' | |
245 | cputype = 'i686' | |
246 | if os.environ.get('MSYSTEM') == 'MINGW64': | |
247 | cputype = 'x86_64' | |
248 | elif ostype.startswith('MSYS'): | |
249 | ostype = 'pc-windows-gnu' | |
250 | elif ostype.startswith('CYGWIN_NT'): | |
251 | cputype = 'i686' | |
252 | if ostype.endswith('WOW64'): | |
253 | cputype = 'x86_64' | |
254 | ostype = 'pc-windows-gnu' | |
255 | else: | |
256 | raise ValueError("unknown OS type: " + ostype) | |
257 | ||
258 | if cputype in {'i386', 'i486', 'i686', 'i786', 'x86'}: | |
259 | cputype = 'i686' | |
260 | elif cputype in {'xscale', 'arm'}: | |
261 | cputype = 'arm' | |
262 | elif cputype == 'armv7l': | |
263 | cputype = 'arm' | |
264 | ostype += 'eabihf' | |
265 | elif cputype == 'aarch64': | |
266 | cputype = 'aarch64' | |
267 | elif cputype in {'powerpc', 'ppc', 'ppc64'}: | |
268 | cputype = 'powerpc' | |
269 | elif cputype in {'amd64', 'x86_64', 'x86-64', 'x64'}: | |
270 | cputype = 'x86_64' | |
271 | else: | |
272 | raise ValueError("unknown cpu type: " + cputype) | |
273 | ||
274 | return cputype + '-' + ostype | |
275 | ||
276 | parser = argparse.ArgumentParser(description='Build rust') | |
277 | parser.add_argument('--config') | |
278 | parser.add_argument('-v', '--verbose', action='store_true') | |
279 | ||
280 | args = [a for a in sys.argv if a != '-h'] | |
281 | args, _ = parser.parse_known_args(args) | |
282 | ||
283 | # Configure initial bootstrap | |
284 | rb = RustBuild() | |
285 | rb.config_toml = '' | |
286 | rb.config_mk = '' | |
287 | rb.rust_root = os.path.abspath(os.path.join(__file__, '../../..')) | |
288 | rb.build_dir = os.path.join(os.getcwd(), "build") | |
289 | rb.verbose = args.verbose | |
290 | ||
291 | try: | |
292 | with open(args.config or 'config.toml') as config: | |
293 | rb.config_toml = config.read() | |
294 | except: | |
295 | pass | |
296 | try: | |
297 | rb.config_mk = open('config.mk').read() | |
298 | except: | |
299 | pass | |
300 | ||
301 | # Fetch/build the bootstrap | |
302 | rb.build = rb.build_triple() | |
303 | rb.parse_nightly_dates() | |
304 | rb.download_rust_nightly() | |
305 | sys.stdout.flush() | |
306 | rb.build_bootstrap() | |
307 | sys.stdout.flush() | |
308 | ||
309 | # Run the bootstrap | |
310 | args = [os.path.join(rb.build_dir, "bootstrap/debug/bootstrap")] | |
311 | args.extend(sys.argv[1:]) | |
312 | args.append('--src') | |
313 | args.append(rb.rust_root) | |
314 | args.append('--build') | |
315 | args.append(rb.build) | |
316 | env = os.environ.copy() | |
317 | env["BOOTSTRAP_PARENT_ID"] = str(os.getpid()) | |
318 | rb.run(args, env) |