]>
git.proxmox.com Git - rustc.git/blob - src/bootstrap/bootstrap.py
1 from __future__
import absolute_import
, division
, print_function
17 def get(url
, path
, verbose
=False):
19 sha_url
= url
+ suffix
20 with tempfile
.NamedTemporaryFile(delete
=False) as temp_file
:
21 temp_path
= temp_file
.name
22 with tempfile
.NamedTemporaryFile(suffix
=suffix
, delete
=False) as sha_file
:
23 sha_path
= sha_file
.name
26 download(sha_path
, sha_url
, False, verbose
)
27 if os
.path
.exists(path
):
28 if verify(path
, sha_path
, False):
30 print("using already-download file", path
)
34 print("ignoring already-download file",
35 path
, "due to failed verification")
37 download(temp_path
, url
, True, verbose
)
38 if not verify(temp_path
, sha_path
, verbose
):
39 raise RuntimeError("failed verification")
41 print("moving {} to {}".format(temp_path
, path
))
42 shutil
.move(temp_path
, path
)
44 delete_if_present(sha_path
, verbose
)
45 delete_if_present(temp_path
, verbose
)
48 def delete_if_present(path
, verbose
):
49 """Remove the given file if present"""
50 if os
.path
.isfile(path
):
52 print("removing", path
)
56 def download(path
, url
, probably_big
, verbose
):
59 _download(path
, url
, probably_big
, verbose
, True)
62 print("\nspurious failure, trying again")
63 _download(path
, url
, probably_big
, verbose
, False)
66 def _download(path
, url
, probably_big
, verbose
, exception
):
67 if probably_big
or verbose
:
68 print("downloading {}".format(url
))
69 # see http://serverfault.com/questions/301128/how-to-download
70 if sys
.platform
== 'win32':
71 run(["PowerShell.exe", "/nologo", "-Command",
72 "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;",
73 "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')".format(url
, path
)],
77 if probably_big
or verbose
:
82 "-y", "30", "-Y", "10", # timeout if speed is < 10 bytes/sec for > 30 seconds
83 "--connect-timeout", "30", # timeout if cannot connect within 30 seconds
84 "--retry", "3", "-Sf", "-o", path
, url
],
89 def verify(path
, sha_path
, verbose
):
90 """Check if the sha256 sum of the given path is valid"""
92 print("verifying", path
)
93 with
open(path
, "rb") as source
:
94 found
= hashlib
.sha256(source
.read()).hexdigest()
95 with
open(sha_path
, "r") as sha256sum
:
96 expected
= sha256sum
.readline().split()[0]
97 verified
= found
== expected
99 print("invalid checksum:\n"
101 " expected: {}".format(found
, expected
))
105 def unpack(tarball
, tarball_suffix
, dst
, verbose
=False, match
=None):
106 """Unpack the given tarball file"""
107 print("extracting", tarball
)
108 fname
= os
.path
.basename(tarball
).replace(tarball_suffix
, "")
109 with contextlib
.closing(tarfile
.open(tarball
)) as tar
:
110 for member
in tar
.getnames():
111 if "/" not in member
:
113 name
= member
.replace(fname
+ "/", "", 1)
114 if match
is not None and not name
.startswith(match
):
116 name
= name
[len(match
) + 1:]
118 dst_path
= os
.path
.join(dst
, name
)
120 print(" extracting", member
)
121 tar
.extract(member
, dst
)
122 src_path
= os
.path
.join(dst
, member
)
123 if os
.path
.isdir(src_path
) and os
.path
.exists(dst_path
):
125 shutil
.move(src_path
, dst_path
)
126 shutil
.rmtree(os
.path
.join(dst
, fname
))
129 def run(args
, verbose
=False, exception
=False, **kwargs
):
130 """Run a child program in a new process"""
132 print("running: " + ' '.join(args
))
134 # Use Popen here instead of call() as it apparently allows powershell on
135 # Windows to not lock up waiting for input presumably.
136 ret
= subprocess
.Popen(args
, **kwargs
)
139 err
= "failed to run: " + ' '.join(args
)
140 if verbose
or exception
:
141 raise RuntimeError(err
)
145 def stage0_data(rust_root
):
146 """Build a dictionary from stage0.txt"""
147 nightlies
= os
.path
.join(rust_root
, "src/stage0.txt")
148 with
open(nightlies
, 'r') as nightlies
:
149 lines
= [line
.rstrip() for line
in nightlies
150 if not line
.startswith("#")]
151 return dict([line
.split(": ", 1) for line
in lines
if line
])
154 def format_build_time(duration
):
155 """Return a nicer format for build time
157 >>> format_build_time('300')
160 return str(datetime
.timedelta(seconds
=int(duration
)))
163 def default_build_triple():
164 """Build triple as in LLVM"""
165 default_encoding
= sys
.getdefaultencoding()
167 ostype
= subprocess
.check_output(
168 ['uname', '-s']).strip().decode(default_encoding
)
169 cputype
= subprocess
.check_output(
170 ['uname', '-m']).strip().decode(default_encoding
)
171 except (subprocess
.CalledProcessError
, OSError):
172 if sys
.platform
== 'win32':
173 return 'x86_64-pc-windows-msvc'
174 err
= "uname not found"
177 # The goal here is to come up with the same triple as LLVM would,
178 # at least for the subset of platforms we're willing to target.
180 'Darwin': 'apple-darwin',
181 'DragonFly': 'unknown-dragonfly',
182 'FreeBSD': 'unknown-freebsd',
183 'Haiku': 'unknown-haiku',
184 'NetBSD': 'unknown-netbsd',
185 'OpenBSD': 'unknown-openbsd'
188 # Consider the direct transformation first and then the special cases
189 if ostype
in ostype_mapper
:
190 ostype
= ostype_mapper
[ostype
]
191 elif ostype
== 'Linux':
192 os_from_sp
= subprocess
.check_output(
193 ['uname', '-o']).strip().decode(default_encoding
)
194 if os_from_sp
== 'Android':
195 ostype
= 'linux-android'
197 ostype
= 'unknown-linux-gnu'
198 elif ostype
== 'SunOS':
199 ostype
= 'sun-solaris'
200 # On Solaris, uname -m will return a machine classification instead
201 # of a cpu type, so uname -p is recommended instead. However, the
202 # output from that option is too generic for our purposes (it will
203 # always emit 'i386' on x86/amd64 systems). As such, isainfo -k
204 # must be used instead.
206 cputype
= subprocess
.check_output(
207 ['isainfo', '-k']).strip().decode(default_encoding
)
208 except (subprocess
.CalledProcessError
, OSError):
209 err
= "isainfo not found"
211 elif ostype
.startswith('MINGW'):
212 # msys' `uname` does not print gcc configuration, but prints msys
213 # configuration. so we cannot believe `uname -m`:
214 # msys1 is always i686 and msys2 is always x86_64.
215 # instead, msys defines $MSYSTEM which is MINGW32 on i686 and
217 ostype
= 'pc-windows-gnu'
219 if os
.environ
.get('MSYSTEM') == 'MINGW64':
221 elif ostype
.startswith('MSYS'):
222 ostype
= 'pc-windows-gnu'
223 elif ostype
.startswith('CYGWIN_NT'):
225 if ostype
.endswith('WOW64'):
227 ostype
= 'pc-windows-gnu'
229 err
= "unknown OS type: {}".format(ostype
)
232 if cputype
== 'powerpc' and ostype
== 'unknown-freebsd':
233 cputype
= subprocess
.check_output(
234 ['uname', '-p']).strip().decode(default_encoding
)
237 'aarch64': 'aarch64',
244 'powerpc': 'powerpc',
245 'powerpc64': 'powerpc64',
246 'powerpc64le': 'powerpc64le',
248 'ppc64': 'powerpc64',
249 'ppc64le': 'powerpc64le',
257 # Consider the direct transformation first and then the special cases
258 if cputype
in cputype_mapper
:
259 cputype
= cputype_mapper
[cputype
]
260 elif cputype
in {'xscale', 'arm'}:
262 if ostype
== 'linux-android':
263 ostype
= 'linux-androideabi'
264 elif ostype
== 'unknown-freebsd':
265 cputype
= subprocess
.check_output(
266 ['uname', '-p']).strip().decode(default_encoding
)
267 ostype
= 'unknown-freebsd'
268 elif cputype
== 'armv6l':
270 if ostype
== 'linux-android':
271 ostype
= 'linux-androideabi'
274 elif cputype
in {'armv7l', 'armv8l'}:
276 if ostype
== 'linux-android':
277 ostype
= 'linux-androideabi'
280 elif cputype
== 'mips':
281 if sys
.byteorder
== 'big':
283 elif sys
.byteorder
== 'little':
286 raise ValueError("unknown byteorder: {}".format(sys
.byteorder
))
287 elif cputype
== 'mips64':
288 if sys
.byteorder
== 'big':
290 elif sys
.byteorder
== 'little':
293 raise ValueError('unknown byteorder: {}'.format(sys
.byteorder
))
294 # only the n64 ABI is supported, indicate it
296 elif cputype
== 'sparc' or cputype
== 'sparcv9' or cputype
== 'sparc64':
299 err
= "unknown cpu type: {}".format(cputype
)
302 return "{}-{}".format(cputype
, ostype
)
305 @contextlib.contextmanager
306 def output(filepath
):
307 tmp
= filepath
+ '.tmp'
308 with
open(tmp
, 'w') as f
:
311 os
.remove(filepath
) # PermissionError/OSError on Win32 if in use
312 os
.rename(tmp
, filepath
)
314 shutil
.copy2(tmp
, filepath
)
318 class RustBuild(object):
319 """Provide all the methods required to build Rust"""
321 self
.cargo_channel
= ''
323 self
._download
_url
= ''
324 self
.rustc_channel
= ''
325 self
.rustfmt_channel
= ''
327 self
.build_dir
= os
.path
.join(os
.getcwd(), "build")
329 self
.config_toml
= ''
331 self
.use_locked_deps
= ''
332 self
.use_vendored_sources
= ''
336 def download_stage0(self
):
337 """Fetch the build system for Rust, written in Rust
339 This method will build a cache directory, then it will fetch the
340 tarball which has the stage0 compiler used to then bootstrap the Rust
343 Each downloaded tarball is extracted, after that, the script
344 will move all the content to the right place.
346 rustc_channel
= self
.rustc_channel
347 cargo_channel
= self
.cargo_channel
348 rustfmt_channel
= self
.rustfmt_channel
352 with tempfile
.NamedTemporaryFile(delete
=False) as temp_file
:
353 temp_path
= temp_file
.name
354 with tarfile
.open(temp_path
, "w:xz") as tar
:
357 except tarfile
.CompressionError
:
360 if self
.rustc().startswith(self
.bin_root()) and \
361 (not os
.path
.exists(self
.rustc()) or
362 self
.program_out_of_date(self
.rustc_stamp())):
363 if os
.path
.exists(self
.bin_root()):
364 shutil
.rmtree(self
.bin_root())
365 tarball_suffix
= '.tar.xz' if support_xz() else '.tar.gz'
366 filename
= "rust-std-{}-{}{}".format(
367 rustc_channel
, self
.build
, tarball_suffix
)
368 pattern
= "rust-std-{}".format(self
.build
)
369 self
._download
_stage
0_helper
(filename
, pattern
, tarball_suffix
)
371 filename
= "rustc-{}-{}{}".format(rustc_channel
, self
.build
,
373 self
._download
_stage
0_helper
(filename
, "rustc", tarball_suffix
)
374 self
.fix_executable("{}/bin/rustc".format(self
.bin_root()))
375 self
.fix_executable("{}/bin/rustdoc".format(self
.bin_root()))
376 with
output(self
.rustc_stamp()) as rust_stamp
:
377 rust_stamp
.write(self
.date
)
379 # This is required so that we don't mix incompatible MinGW
380 # libraries/binaries that are included in rust-std with
381 # the system MinGW ones.
382 if "pc-windows-gnu" in self
.build
:
383 filename
= "rust-mingw-{}-{}{}".format(
384 rustc_channel
, self
.build
, tarball_suffix
)
385 self
._download
_stage
0_helper
(filename
, "rust-mingw", tarball_suffix
)
387 if self
.cargo().startswith(self
.bin_root()) and \
388 (not os
.path
.exists(self
.cargo()) or
389 self
.program_out_of_date(self
.cargo_stamp())):
390 tarball_suffix
= '.tar.xz' if support_xz() else '.tar.gz'
391 filename
= "cargo-{}-{}{}".format(cargo_channel
, self
.build
,
393 self
._download
_stage
0_helper
(filename
, "cargo", tarball_suffix
)
394 self
.fix_executable("{}/bin/cargo".format(self
.bin_root()))
395 with
output(self
.cargo_stamp()) as cargo_stamp
:
396 cargo_stamp
.write(self
.date
)
398 if self
.rustfmt() and self
.rustfmt().startswith(self
.bin_root()) and (
399 not os
.path
.exists(self
.rustfmt())
400 or self
.program_out_of_date(self
.rustfmt_stamp())
403 tarball_suffix
= '.tar.xz' if support_xz() else '.tar.gz'
404 [channel
, date
] = rustfmt_channel
.split('-', 1)
405 filename
= "rustfmt-{}-{}{}".format(channel
, self
.build
, tarball_suffix
)
406 self
._download
_stage
0_helper
(filename
, "rustfmt-preview", tarball_suffix
, date
)
407 self
.fix_executable("{}/bin/rustfmt".format(self
.bin_root()))
408 self
.fix_executable("{}/bin/cargo-fmt".format(self
.bin_root()))
409 with
output(self
.rustfmt_stamp()) as rustfmt_stamp
:
410 rustfmt_stamp
.write(self
.date
)
412 def _download_stage0_helper(self
, filename
, pattern
, tarball_suffix
, date
=None):
415 cache_dst
= os
.path
.join(self
.build_dir
, "cache")
416 rustc_cache
= os
.path
.join(cache_dst
, date
)
417 if not os
.path
.exists(rustc_cache
):
418 os
.makedirs(rustc_cache
)
420 url
= "{}/dist/{}".format(self
._download
_url
, date
)
421 tarball
= os
.path
.join(rustc_cache
, filename
)
422 if not os
.path
.exists(tarball
):
423 get("{}/{}".format(url
, filename
), tarball
, verbose
=self
.verbose
)
424 unpack(tarball
, tarball_suffix
, self
.bin_root(), match
=pattern
, verbose
=self
.verbose
)
427 def fix_executable(fname
):
428 """Modifies the interpreter section of 'fname' to fix the dynamic linker
430 This method is only required on NixOS and uses the PatchELF utility to
431 change the dynamic linker of ELF executables.
433 Please see https://nixos.org/patchelf.html for more information
435 default_encoding
= sys
.getdefaultencoding()
437 ostype
= subprocess
.check_output(
438 ['uname', '-s']).strip().decode(default_encoding
)
439 except subprocess
.CalledProcessError
:
441 except OSError as reason
:
442 if getattr(reason
, 'winerror', None) is not None:
446 if ostype
!= "Linux":
449 if not os
.path
.exists("/etc/NIXOS"):
451 if os
.path
.exists("/lib"):
454 # At this point we're pretty sure the user is running NixOS
455 nix_os_msg
= "info: you seem to be running NixOS. Attempting to patch"
456 print(nix_os_msg
, fname
)
459 interpreter
= subprocess
.check_output(
460 ["patchelf", "--print-interpreter", fname
])
461 interpreter
= interpreter
.strip().decode(default_encoding
)
462 except subprocess
.CalledProcessError
as reason
:
463 print("warning: failed to call patchelf:", reason
)
466 loader
= interpreter
.split("/")[-1]
469 ldd_output
= subprocess
.check_output(
470 ['ldd', '/run/current-system/sw/bin/sh'])
471 ldd_output
= ldd_output
.strip().decode(default_encoding
)
472 except subprocess
.CalledProcessError
as reason
:
473 print("warning: unable to call ldd:", reason
)
476 for line
in ldd_output
.splitlines():
477 libname
= line
.split()[0]
478 if libname
.endswith(loader
):
479 loader_path
= libname
[:len(libname
) - len(loader
)]
482 print("warning: unable to find the path to the dynamic linker")
485 correct_interpreter
= loader_path
+ loader
488 subprocess
.check_output(
489 ["patchelf", "--set-interpreter", correct_interpreter
, fname
])
490 except subprocess
.CalledProcessError
as reason
:
491 print("warning: failed to call patchelf:", reason
)
494 def rustc_stamp(self
):
495 """Return the path for .rustc-stamp
498 >>> rb.build_dir = "build"
499 >>> rb.rustc_stamp() == os.path.join("build", "stage0", ".rustc-stamp")
502 return os
.path
.join(self
.bin_root(), '.rustc-stamp')
504 def cargo_stamp(self
):
505 """Return the path for .cargo-stamp
508 >>> rb.build_dir = "build"
509 >>> rb.cargo_stamp() == os.path.join("build", "stage0", ".cargo-stamp")
512 return os
.path
.join(self
.bin_root(), '.cargo-stamp')
514 def rustfmt_stamp(self
):
515 """Return the path for .rustfmt-stamp
518 >>> rb.build_dir = "build"
519 >>> rb.rustfmt_stamp() == os.path.join("build", "stage0", ".rustfmt-stamp")
522 return os
.path
.join(self
.bin_root(), '.rustfmt-stamp')
524 def program_out_of_date(self
, stamp_path
):
525 """Check if the given program stamp is out of date"""
526 if not os
.path
.exists(stamp_path
) or self
.clean
:
528 with
open(stamp_path
, 'r') as stamp
:
529 return self
.date
!= stamp
.read()
532 """Return the binary root directory
535 >>> rb.build_dir = "build"
536 >>> rb.bin_root() == os.path.join("build", "stage0")
539 When the 'build' property is given should be a nested directory:
541 >>> rb.build = "devel"
542 >>> rb.bin_root() == os.path.join("build", "devel", "stage0")
545 return os
.path
.join(self
.build_dir
, self
.build
, "stage0")
547 def get_toml(self
, key
, section
=None):
548 """Returns the value of the given key in config.toml, otherwise returns None
551 >>> rb.config_toml = 'key1 = "value1"\\nkey2 = "value2"'
552 >>> rb.get_toml("key2")
555 If the key does not exists, the result is None:
557 >>> rb.get_toml("key3") is None
560 Optionally also matches the section the key appears in
562 >>> rb.config_toml = '[a]\\nkey = "value1"\\n[b]\\nkey = "value2"'
563 >>> rb.get_toml('key', 'a')
565 >>> rb.get_toml('key', 'b')
567 >>> rb.get_toml('key', 'c') is None
570 >>> rb.config_toml = 'key1 = true'
571 >>> rb.get_toml("key1")
576 for line
in self
.config_toml
.splitlines():
577 section_match
= re
.match(r
'^\s*\[(.*)\]\s*$', line
)
578 if section_match
is not None:
579 cur_section
= section_match
.group(1)
581 match
= re
.match(r
'^{}\s*=(.*)$'.format(key
), line
)
582 if match
is not None:
583 value
= match
.group(1)
584 if section
is None or section
== cur_section
:
585 return self
.get_string(value
) or value
.strip()
589 """Return config path for cargo"""
590 return self
.program_config('cargo')
593 """Return config path for rustc"""
594 return self
.program_config('rustc')
597 """Return config path for rustfmt"""
598 if not self
.rustfmt_channel
:
600 return self
.program_config('rustfmt')
602 def program_config(self
, program
):
603 """Return config path for the given program
606 >>> rb.config_toml = 'rustc = "rustc"\\n'
607 >>> rb.program_config('rustc')
609 >>> rb.config_toml = ''
610 >>> cargo_path = rb.program_config('cargo')
611 >>> cargo_path.rstrip(".exe") == os.path.join(rb.bin_root(),
615 config
= self
.get_toml(program
)
617 return os
.path
.expanduser(config
)
618 return os
.path
.join(self
.bin_root(), "bin", "{}{}".format(
619 program
, self
.exe_suffix()))
622 def get_string(line
):
623 """Return the value between double quotes
625 >>> RustBuild.get_string(' "devel" ')
627 >>> RustBuild.get_string(" 'devel' ")
629 >>> RustBuild.get_string('devel') is None
631 >>> RustBuild.get_string(' "devel ')
634 start
= line
.find('"')
636 end
= start
+ 1 + line
[start
+ 1:].find('"')
637 return line
[start
+ 1:end
]
638 start
= line
.find('\'')
640 end
= start
+ 1 + line
[start
+ 1:].find('\'')
641 return line
[start
+ 1:end
]
646 """Return a suffix for executables"""
647 if sys
.platform
== 'win32':
651 def bootstrap_binary(self
):
652 """Return the path of the bootstrap binary
655 >>> rb.build_dir = "build"
656 >>> rb.bootstrap_binary() == os.path.join("build", "bootstrap",
657 ... "debug", "bootstrap")
660 return os
.path
.join(self
.build_dir
, "bootstrap", "debug", "bootstrap")
662 def build_bootstrap(self
):
663 """Build bootstrap"""
664 build_dir
= os
.path
.join(self
.build_dir
, "bootstrap")
665 if self
.clean
and os
.path
.exists(build_dir
):
666 shutil
.rmtree(build_dir
)
667 env
= os
.environ
.copy()
668 env
["RUSTC_BOOTSTRAP"] = '1'
669 env
["CARGO_TARGET_DIR"] = build_dir
670 env
["RUSTC"] = self
.rustc()
671 env
["LD_LIBRARY_PATH"] = os
.path
.join(self
.bin_root(), "lib") + \
672 (os
.pathsep
+ env
["LD_LIBRARY_PATH"]) \
673 if "LD_LIBRARY_PATH" in env
else ""
674 env
["DYLD_LIBRARY_PATH"] = os
.path
.join(self
.bin_root(), "lib") + \
675 (os
.pathsep
+ env
["DYLD_LIBRARY_PATH"]) \
676 if "DYLD_LIBRARY_PATH" in env
else ""
677 env
["LIBRARY_PATH"] = os
.path
.join(self
.bin_root(), "lib") + \
678 (os
.pathsep
+ env
["LIBRARY_PATH"]) \
679 if "LIBRARY_PATH" in env
else ""
680 # preserve existing RUSTFLAGS
681 env
.setdefault("RUSTFLAGS", "")
682 env
["RUSTFLAGS"] += " -Cdebuginfo=2"
684 build_section
= "target.{}".format(self
.build_triple())
686 if self
.get_toml("crt-static", build_section
) == "true":
687 target_features
+= ["+crt-static"]
688 elif self
.get_toml("crt-static", build_section
) == "false":
689 target_features
+= ["-crt-static"]
691 env
["RUSTFLAGS"] += " -C target-feature=" + (",".join(target_features
))
692 target_linker
= self
.get_toml("linker", build_section
)
693 if target_linker
is not None:
694 env
["RUSTFLAGS"] += " -C linker=" + target_linker
695 env
["RUSTFLAGS"] += " -Wrust_2018_idioms -Wunused_lifetimes"
696 if self
.get_toml("deny-warnings", "rust") != "false":
697 env
["RUSTFLAGS"] += " -Dwarnings"
699 env
["PATH"] = os
.path
.join(self
.bin_root(), "bin") + \
700 os
.pathsep
+ env
["PATH"]
701 if not os
.path
.isfile(self
.cargo()):
702 raise Exception("no cargo executable found at `{}`".format(
704 args
= [self
.cargo(), "build", "--manifest-path",
705 os
.path
.join(self
.rust_root
, "src/bootstrap/Cargo.toml")]
706 for _
in range(1, self
.verbose
):
707 args
.append("--verbose")
708 if self
.use_locked_deps
:
709 args
.append("--locked")
710 if self
.use_vendored_sources
:
711 args
.append("--frozen")
712 run(args
, env
=env
, verbose
=self
.verbose
)
714 def build_triple(self
):
715 """Build triple as in LLVM"""
716 config
= self
.get_toml('build')
719 return default_build_triple()
721 def check_submodule(self
, module
, slow_submodules
):
722 if not slow_submodules
:
723 checked_out
= subprocess
.Popen(["git", "rev-parse", "HEAD"],
724 cwd
=os
.path
.join(self
.rust_root
, module
),
725 stdout
=subprocess
.PIPE
)
730 def update_submodule(self
, module
, checked_out
, recorded_submodules
):
731 module_path
= os
.path
.join(self
.rust_root
, module
)
733 if checked_out
is not None:
734 default_encoding
= sys
.getdefaultencoding()
735 checked_out
= checked_out
.communicate()[0].decode(default_encoding
).strip()
736 if recorded_submodules
[module
] == checked_out
:
739 print("Updating submodule", module
)
741 run(["git", "submodule", "-q", "sync", module
],
742 cwd
=self
.rust_root
, verbose
=self
.verbose
)
744 run(["git", "submodule", "update",
745 "--init", "--recursive", "--progress", module
],
746 cwd
=self
.rust_root
, verbose
=self
.verbose
, exception
=True)
748 # Some versions of git don't support --progress.
749 run(["git", "submodule", "update",
750 "--init", "--recursive", module
],
751 cwd
=self
.rust_root
, verbose
=self
.verbose
)
752 run(["git", "reset", "-q", "--hard"],
753 cwd
=module_path
, verbose
=self
.verbose
)
754 run(["git", "clean", "-qdfx"],
755 cwd
=module_path
, verbose
=self
.verbose
)
757 def update_submodules(self
):
758 """Update submodules"""
759 if (not os
.path
.exists(os
.path
.join(self
.rust_root
, ".git"))) or \
760 self
.get_toml('submodules') == "false":
763 # check the existence of 'git' command
765 subprocess
.check_output(['git', '--version'])
766 except (subprocess
.CalledProcessError
, OSError):
767 print("error: `git` is not found, please make sure it's installed and in the path.")
770 slow_submodules
= self
.get_toml('fast-submodules') == "false"
773 print('Unconditionally updating all submodules')
775 print('Updating only changed submodules')
776 default_encoding
= sys
.getdefaultencoding()
777 submodules
= [s
.split(' ', 1)[1] for s
in subprocess
.check_output(
778 ["git", "config", "--file",
779 os
.path
.join(self
.rust_root
, ".gitmodules"),
780 "--get-regexp", "path"]
781 ).decode(default_encoding
).splitlines()]
782 filtered_submodules
= []
783 submodules_names
= []
784 for module
in submodules
:
785 if module
.endswith("llvm-project"):
786 if self
.get_toml('llvm-config') and self
.get_toml('lld') != 'true':
788 check
= self
.check_submodule(module
, slow_submodules
)
789 filtered_submodules
.append((module
, check
))
790 submodules_names
.append(module
)
791 recorded
= subprocess
.Popen(["git", "ls-tree", "HEAD"] + submodules_names
,
792 cwd
=self
.rust_root
, stdout
=subprocess
.PIPE
)
793 recorded
= recorded
.communicate()[0].decode(default_encoding
).strip().splitlines()
794 recorded_submodules
= {}
795 for data
in recorded
:
797 recorded_submodules
[data
[3]] = data
[2]
798 for module
in filtered_submodules
:
799 self
.update_submodule(module
[0], module
[1], recorded_submodules
)
800 print("Submodules updated in %.2f seconds" % (time() - start_time
))
802 def set_normal_environment(self
):
803 """Set download URL for normal environment"""
804 if 'RUSTUP_DIST_SERVER' in os
.environ
:
805 self
._download
_url
= os
.environ
['RUSTUP_DIST_SERVER']
807 self
._download
_url
= 'https://static.rust-lang.org'
809 def set_dev_environment(self
):
810 """Set download URL for development environment"""
811 if 'RUSTUP_DEV_DIST_SERVER' in os
.environ
:
812 self
._download
_url
= os
.environ
['RUSTUP_DEV_DIST_SERVER']
814 self
._download
_url
= 'https://dev-static.rust-lang.org'
816 def check_vendored_status(self
):
817 """Check that vendoring is configured properly"""
818 vendor_dir
= os
.path
.join(self
.rust_root
, 'vendor')
819 if 'SUDO_USER' in os
.environ
and not self
.use_vendored_sources
:
820 if os
.environ
.get('USER') != os
.environ
['SUDO_USER']:
821 self
.use_vendored_sources
= True
822 print('info: looks like you are running this command under `sudo`')
823 print(' and so in order to preserve your $HOME this will now')
824 print(' use vendored sources by default.')
825 if not os
.path
.exists(vendor_dir
):
826 print('error: vendoring required, but vendor directory does not exist.')
827 print(' Run `cargo vendor` without sudo to initialize the '
829 raise Exception("{} not found".format(vendor_dir
))
831 if self
.use_vendored_sources
:
832 if not os
.path
.exists('.cargo'):
833 os
.makedirs('.cargo')
834 with
output('.cargo/config') as cargo_config
:
836 "[source.crates-io]\n"
837 "replace-with = 'vendored-sources'\n"
838 "registry = 'https://example.com'\n"
840 "[source.vendored-sources]\n"
841 "directory = '{}/vendor'\n"
842 .format(self
.rust_root
))
844 if os
.path
.exists('.cargo'):
845 shutil
.rmtree('.cargo')
847 def ensure_vendored(self
):
848 """Ensure that the vendored sources are available if needed"""
849 vendor_dir
= os
.path
.join(self
.rust_root
, 'vendor')
850 # Note that this does not handle updating the vendored dependencies if
851 # the rust git repository is updated. Normal development usually does
852 # not use vendoring, so hopefully this isn't too much of a problem.
853 if self
.use_vendored_sources
and not os
.path
.exists(vendor_dir
):
854 run([self
.cargo(), "vendor"],
855 verbose
=self
.verbose
, cwd
=self
.rust_root
)
858 def bootstrap(help_triggered
):
859 """Configure, fetch, build and run the initial bootstrap"""
861 # If the user is asking for help, let them know that the whole download-and-build
862 # process has to happen before anything is printed out.
864 print("info: Downloading and building bootstrap before processing --help")
865 print(" command. See src/bootstrap/README.md for help with common")
868 parser
= argparse
.ArgumentParser(description
='Build rust')
869 parser
.add_argument('--config')
870 parser
.add_argument('--build')
871 parser
.add_argument('--src')
872 parser
.add_argument('--clean', action
='store_true')
873 parser
.add_argument('-v', '--verbose', action
='count', default
=0)
875 args
= [a
for a
in sys
.argv
if a
!= '-h' and a
!= '--help']
876 args
, _
= parser
.parse_known_args(args
)
878 # Configure initial bootstrap
880 build
.rust_root
= args
.src
or os
.path
.abspath(os
.path
.join(__file__
, '../../..'))
881 build
.verbose
= args
.verbose
882 build
.clean
= args
.clean
885 with
open(args
.config
or 'config.toml') as config
:
886 build
.config_toml
= config
.read()
887 except (OSError, IOError):
890 config_verbose
= build
.get_toml('verbose', 'build')
891 if config_verbose
is not None:
892 build
.verbose
= max(build
.verbose
, int(config_verbose
))
894 build
.use_vendored_sources
= build
.get_toml('vendor', 'build') == 'true'
896 build
.use_locked_deps
= build
.get_toml('locked-deps', 'build') == 'true'
898 build
.check_vendored_status()
900 data
= stage0_data(build
.rust_root
)
901 build
.date
= data
['date']
902 build
.rustc_channel
= data
['rustc']
903 build
.cargo_channel
= data
['cargo']
905 if "rustfmt" in data
:
906 build
.rustfmt_channel
= data
['rustfmt']
909 build
.set_dev_environment()
911 build
.set_normal_environment()
913 build
.update_submodules()
915 # Fetch/build the bootstrap
916 build
.build
= args
.build
or build
.build_triple()
917 build
.download_stage0()
919 build
.ensure_vendored()
920 build
.build_bootstrap()
924 args
= [build
.bootstrap_binary()]
925 args
.extend(sys
.argv
[1:])
926 env
= os
.environ
.copy()
927 env
["BUILD"] = build
.build
928 env
["SRC"] = build
.rust_root
929 env
["BOOTSTRAP_PARENT_ID"] = str(os
.getpid())
930 env
["BOOTSTRAP_PYTHON"] = sys
.executable
931 env
["BUILD_DIR"] = build
.build_dir
932 env
["RUSTC_BOOTSTRAP"] = '1'
933 env
["CARGO"] = build
.cargo()
934 env
["RUSTC"] = build
.rustc()
936 env
["RUSTFMT"] = build
.rustfmt()
937 run(args
, env
=env
, verbose
=build
.verbose
)
941 """Entry point for the bootstrap process"""
944 # x.py help <cmd> ...
945 if len(sys
.argv
) > 1 and sys
.argv
[1] == 'help':
946 sys
.argv
= [sys
.argv
[0], '-h'] + sys
.argv
[2:]
949 '-h' in sys
.argv
) or ('--help' in sys
.argv
) or (len(sys
.argv
) == 1)
951 bootstrap(help_triggered
)
952 if not help_triggered
:
953 print("Build completed successfully in {}".format(
954 format_build_time(time() - start_time
)))
955 except (SystemExit, KeyboardInterrupt) as error
:
956 if hasattr(error
, 'code') and isinstance(error
.code
, int):
957 exit_code
= error
.code
961 if not help_triggered
:
962 print("Build completed unsuccessfully in {}".format(
963 format_build_time(time() - start_time
)))
967 if __name__
== '__main__':