2017-10-09 15:16:18 +00:00
|
|
|
from __future__ import absolute_import, division, print_function
|
2015-11-19 23:20:12 +00:00
|
|
|
import argparse
|
|
|
|
import contextlib
|
2016-07-02 14:19:27 +00:00
|
|
|
import datetime
|
2016-04-14 02:10:42 +00:00
|
|
|
import hashlib
|
2021-08-26 09:26:03 +00:00
|
|
|
import json
|
2015-11-19 23:20:12 +00:00
|
|
|
import os
|
2017-05-13 05:21:35 +00:00
|
|
|
import re
|
2015-11-19 23:20:12 +00:00
|
|
|
import shutil
|
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
import tarfile
|
2016-04-30 06:09:53 +00:00
|
|
|
import tempfile
|
|
|
|
|
2023-01-27 21:10:10 +00:00
|
|
|
from time import time
|
2023-04-16 20:03:09 +00:00
|
|
|
from multiprocessing import Pool, cpu_count
|
2016-07-02 14:19:27 +00:00
|
|
|
|
2023-01-27 21:10:10 +00:00
|
|
|
try:
|
|
|
|
import lzma
|
|
|
|
except ImportError:
|
|
|
|
lzma = None
|
|
|
|
|
windows: kill rust-analyzer-proc-macro-srv before deleting stage0 directory
This fixes the following recurring error on windows:
```
Traceback (most recent call last):
File "C:\Users\jyn\src\rust\x.py", line 29, in <module>
bootstrap.main()
File "C:\Users\jyn\src\rust\src\bootstrap\bootstrap.py", line 963, in main
bootstrap(args)
File "C:\Users\jyn\src\rust\src\bootstrap\bootstrap.py", line 927, in bootstrap
build.download_toolchain()
File "C:\Users\jyn\src\rust\src\bootstrap\bootstrap.py", line 437, in download_toolchain
shutil.rmtree(bin_root)
File "C:\Users\jyn\AppData\Local\Programs\Python\Python311\Lib\shutil.py", line 759, in rmtree
return _rmtree_unsafe(path, onerror)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\jyn\AppData\Local\Programs\Python\Python311\Lib\shutil.py", line 617, in _rmtree_unsafe
_rmtree_unsafe(fullname, onerror)
File "C:\Users\jyn\AppData\Local\Programs\Python\Python311\Lib\shutil.py", line 622, in _rmtree_unsafe
onerror(os.unlink, fullname, sys.exc_info())
File "C:\Users\jyn\AppData\Local\Programs\Python\Python311\Lib\shutil.py", line 620, in _rmtree_unsafe
os.unlink(fullname)
PermissionError: [WinError 5] Access is denied: 'C:\\Users\\jyn\\src\\rust\\build\\x86_64-pc-windows-msvc\\stage0\\bin\\rust-analyzer-proc-macro-srv.exe'
```
2023-04-29 06:43:20 +00:00
|
|
|
def platform_is_win32():
|
|
|
|
return sys.platform == 'win32'
|
|
|
|
|
|
|
|
if platform_is_win32():
|
2023-01-27 21:10:10 +00:00
|
|
|
EXE_SUFFIX = ".exe"
|
|
|
|
else:
|
|
|
|
EXE_SUFFIX = ""
|
2020-09-04 23:00:04 +00:00
|
|
|
|
2023-05-01 13:46:31 +00:00
|
|
|
def get_cpus():
|
|
|
|
if hasattr(os, "sched_getaffinity"):
|
|
|
|
return len(os.sched_getaffinity(0))
|
|
|
|
if hasattr(os, "cpu_count"):
|
|
|
|
cpus = os.cpu_count()
|
|
|
|
if cpus is not None:
|
|
|
|
return cpus
|
|
|
|
try:
|
|
|
|
return cpu_count()
|
|
|
|
except NotImplementedError:
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
|
|
|
2022-05-29 02:17:28 +00:00
|
|
|
def get(base, url, path, checksums, verbose=False):
|
2016-05-08 07:54:50 +00:00
|
|
|
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
|
|
|
|
temp_path = temp_file.name
|
|
|
|
|
|
|
|
try:
|
2022-05-29 02:17:28 +00:00
|
|
|
if url not in checksums:
|
|
|
|
raise RuntimeError(("src/stage0.json doesn't contain a checksum for {}. "
|
|
|
|
"Pre-built artifacts might not be available for this "
|
|
|
|
"target at this time, see https://doc.rust-lang.org/nightly"
|
|
|
|
"/rustc/platform-support.html for more information.")
|
|
|
|
.format(url))
|
|
|
|
sha256 = checksums[url]
|
|
|
|
if os.path.exists(path):
|
|
|
|
if verify(path, sha256, False):
|
|
|
|
if verbose:
|
2023-04-29 20:53:55 +00:00
|
|
|
print("using already-download file", path, file=sys.stderr)
|
2022-05-29 02:17:28 +00:00
|
|
|
return
|
|
|
|
else:
|
|
|
|
if verbose:
|
|
|
|
print("ignoring already-download file",
|
2023-04-29 20:53:55 +00:00
|
|
|
path, "due to failed verification", file=sys.stderr)
|
2022-05-29 02:17:28 +00:00
|
|
|
os.unlink(path)
|
2022-05-29 14:11:36 +00:00
|
|
|
download(temp_path, "{}/{}".format(base, url), True, verbose)
|
2022-05-29 02:17:28 +00:00
|
|
|
if not verify(temp_path, sha256, verbose):
|
2016-07-05 22:07:26 +00:00
|
|
|
raise RuntimeError("failed verification")
|
2016-11-16 20:31:19 +00:00
|
|
|
if verbose:
|
2023-04-29 20:53:55 +00:00
|
|
|
print("moving {} to {}".format(temp_path, path), file=sys.stderr)
|
2016-05-08 07:54:50 +00:00
|
|
|
shutil.move(temp_path, path)
|
|
|
|
finally:
|
2021-08-26 10:35:32 +00:00
|
|
|
if os.path.isfile(temp_path):
|
|
|
|
if verbose:
|
2023-04-29 20:53:55 +00:00
|
|
|
print("removing", temp_path, file=sys.stderr)
|
2021-08-26 10:35:32 +00:00
|
|
|
os.unlink(temp_path)
|
2016-04-30 06:09:53 +00:00
|
|
|
|
|
|
|
|
2022-05-29 14:11:36 +00:00
|
|
|
def download(path, url, probably_big, verbose):
|
2023-01-27 21:10:10 +00:00
|
|
|
for _ in range(4):
|
2017-02-23 15:04:29 +00:00
|
|
|
try:
|
2022-05-29 14:11:36 +00:00
|
|
|
_download(path, url, probably_big, verbose, True)
|
2017-02-23 15:04:29 +00:00
|
|
|
return
|
|
|
|
except RuntimeError:
|
2023-04-29 20:53:55 +00:00
|
|
|
print("\nspurious failure, trying again", file=sys.stderr)
|
2022-05-29 14:11:36 +00:00
|
|
|
_download(path, url, probably_big, verbose, False)
|
2017-02-23 15:04:29 +00:00
|
|
|
|
|
|
|
|
2022-05-29 14:11:36 +00:00
|
|
|
def _download(path, url, probably_big, verbose, exception):
|
2022-05-01 15:43:03 +00:00
|
|
|
# Try to use curl (potentially available on win32
|
|
|
|
# https://devblogs.microsoft.com/commandline/tar-and-curl-come-to-windows/)
|
|
|
|
# If an error occurs:
|
|
|
|
# - If we are on win32 fallback to powershell
|
|
|
|
# - Otherwise raise the error if appropriate
|
2016-11-16 20:31:19 +00:00
|
|
|
if probably_big or verbose:
|
2023-04-29 20:53:55 +00:00
|
|
|
print("downloading {}".format(url), file=sys.stderr)
|
2022-05-01 15:43:03 +00:00
|
|
|
|
|
|
|
try:
|
2016-11-16 20:31:19 +00:00
|
|
|
if probably_big or verbose:
|
|
|
|
option = "-#"
|
|
|
|
else:
|
|
|
|
option = "-s"
|
2022-08-18 02:13:37 +00:00
|
|
|
# If curl is not present on Win32, we should not sys.exit
|
2022-05-01 15:43:03 +00:00
|
|
|
# but raise `CalledProcessError` or `OSError` instead
|
windows: kill rust-analyzer-proc-macro-srv before deleting stage0 directory
This fixes the following recurring error on windows:
```
Traceback (most recent call last):
File "C:\Users\jyn\src\rust\x.py", line 29, in <module>
bootstrap.main()
File "C:\Users\jyn\src\rust\src\bootstrap\bootstrap.py", line 963, in main
bootstrap(args)
File "C:\Users\jyn\src\rust\src\bootstrap\bootstrap.py", line 927, in bootstrap
build.download_toolchain()
File "C:\Users\jyn\src\rust\src\bootstrap\bootstrap.py", line 437, in download_toolchain
shutil.rmtree(bin_root)
File "C:\Users\jyn\AppData\Local\Programs\Python\Python311\Lib\shutil.py", line 759, in rmtree
return _rmtree_unsafe(path, onerror)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\jyn\AppData\Local\Programs\Python\Python311\Lib\shutil.py", line 617, in _rmtree_unsafe
_rmtree_unsafe(fullname, onerror)
File "C:\Users\jyn\AppData\Local\Programs\Python\Python311\Lib\shutil.py", line 622, in _rmtree_unsafe
onerror(os.unlink, fullname, sys.exc_info())
File "C:\Users\jyn\AppData\Local\Programs\Python\Python311\Lib\shutil.py", line 620, in _rmtree_unsafe
os.unlink(fullname)
PermissionError: [WinError 5] Access is denied: 'C:\\Users\\jyn\\src\\rust\\build\\x86_64-pc-windows-msvc\\stage0\\bin\\rust-analyzer-proc-macro-srv.exe'
```
2023-04-29 06:43:20 +00:00
|
|
|
require(["curl", "--version"], exception=platform_is_win32())
|
2023-02-08 08:07:15 +00:00
|
|
|
with open(path, "wb") as outfile:
|
|
|
|
run(["curl", option,
|
|
|
|
"-L", # Follow redirect.
|
|
|
|
"-y", "30", "-Y", "10", # timeout if speed is < 10 bytes/sec for > 30 seconds
|
|
|
|
"--connect-timeout", "30", # timeout if cannot connect within 30 seconds
|
2023-05-18 23:00:21 +00:00
|
|
|
"--retry", "3", "-SRf", url],
|
2023-02-08 08:07:15 +00:00
|
|
|
stdout=outfile, #Implements cli redirect operator '>'
|
|
|
|
verbose=verbose,
|
|
|
|
exception=True, # Will raise RuntimeError on failure
|
|
|
|
)
|
2022-05-01 15:43:03 +00:00
|
|
|
except (subprocess.CalledProcessError, OSError, RuntimeError):
|
|
|
|
# see http://serverfault.com/questions/301128/how-to-download
|
windows: kill rust-analyzer-proc-macro-srv before deleting stage0 directory
This fixes the following recurring error on windows:
```
Traceback (most recent call last):
File "C:\Users\jyn\src\rust\x.py", line 29, in <module>
bootstrap.main()
File "C:\Users\jyn\src\rust\src\bootstrap\bootstrap.py", line 963, in main
bootstrap(args)
File "C:\Users\jyn\src\rust\src\bootstrap\bootstrap.py", line 927, in bootstrap
build.download_toolchain()
File "C:\Users\jyn\src\rust\src\bootstrap\bootstrap.py", line 437, in download_toolchain
shutil.rmtree(bin_root)
File "C:\Users\jyn\AppData\Local\Programs\Python\Python311\Lib\shutil.py", line 759, in rmtree
return _rmtree_unsafe(path, onerror)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\jyn\AppData\Local\Programs\Python\Python311\Lib\shutil.py", line 617, in _rmtree_unsafe
_rmtree_unsafe(fullname, onerror)
File "C:\Users\jyn\AppData\Local\Programs\Python\Python311\Lib\shutil.py", line 622, in _rmtree_unsafe
onerror(os.unlink, fullname, sys.exc_info())
File "C:\Users\jyn\AppData\Local\Programs\Python\Python311\Lib\shutil.py", line 620, in _rmtree_unsafe
os.unlink(fullname)
PermissionError: [WinError 5] Access is denied: 'C:\\Users\\jyn\\src\\rust\\build\\x86_64-pc-windows-msvc\\stage0\\bin\\rust-analyzer-proc-macro-srv.exe'
```
2023-04-29 06:43:20 +00:00
|
|
|
if platform_is_win32():
|
|
|
|
run_powershell([
|
2022-05-01 15:43:03 +00:00
|
|
|
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;",
|
|
|
|
"(New-Object System.Net.WebClient).DownloadFile('{}', '{}')".format(url, path)],
|
|
|
|
verbose=verbose,
|
|
|
|
exception=exception)
|
|
|
|
# Check if the RuntimeError raised by run(curl) should be silenced
|
|
|
|
elif verbose or exception:
|
|
|
|
raise
|
2016-04-30 06:09:53 +00:00
|
|
|
|
|
|
|
|
2021-08-26 10:35:32 +00:00
|
|
|
def verify(path, expected, verbose):
|
2017-07-01 04:24:35 +00:00
|
|
|
"""Check if the sha256 sum of the given path is valid"""
|
2016-11-16 20:31:19 +00:00
|
|
|
if verbose:
|
2023-04-29 20:53:55 +00:00
|
|
|
print("verifying", path, file=sys.stderr)
|
2017-07-01 04:24:35 +00:00
|
|
|
with open(path, "rb") as source:
|
|
|
|
found = hashlib.sha256(source.read()).hexdigest()
|
2016-07-05 22:07:26 +00:00
|
|
|
verified = found == expected
|
2016-11-16 20:31:19 +00:00
|
|
|
if not verified:
|
2016-07-05 22:07:26 +00:00
|
|
|
print("invalid checksum:\n"
|
2017-05-19 11:16:29 +00:00
|
|
|
" found: {}\n"
|
2023-04-29 20:53:55 +00:00
|
|
|
" expected: {}".format(found, expected), file=sys.stderr)
|
2016-07-05 22:07:26 +00:00
|
|
|
return verified
|
2015-11-19 23:20:12 +00:00
|
|
|
|
2016-04-30 06:09:53 +00:00
|
|
|
|
2019-10-29 08:48:05 +00:00
|
|
|
def unpack(tarball, tarball_suffix, dst, verbose=False, match=None):
|
2017-07-01 04:24:35 +00:00
|
|
|
"""Unpack the given tarball file"""
|
2023-04-29 20:53:55 +00:00
|
|
|
print("extracting", tarball, file=sys.stderr)
|
2019-10-29 08:48:05 +00:00
|
|
|
fname = os.path.basename(tarball).replace(tarball_suffix, "")
|
2015-11-19 23:20:12 +00:00
|
|
|
with contextlib.closing(tarfile.open(tarball)) as tar:
|
2017-07-03 04:32:42 +00:00
|
|
|
for member in tar.getnames():
|
|
|
|
if "/" not in member:
|
2015-11-19 23:20:12 +00:00
|
|
|
continue
|
2017-07-03 04:32:42 +00:00
|
|
|
name = member.replace(fname + "/", "", 1)
|
2015-11-19 23:20:12 +00:00
|
|
|
if match is not None and not name.startswith(match):
|
|
|
|
continue
|
|
|
|
name = name[len(match) + 1:]
|
|
|
|
|
2017-07-03 04:32:42 +00:00
|
|
|
dst_path = os.path.join(dst, name)
|
2015-11-19 23:20:12 +00:00
|
|
|
if verbose:
|
2023-04-29 20:53:55 +00:00
|
|
|
print(" extracting", member, file=sys.stderr)
|
2017-07-03 04:32:42 +00:00
|
|
|
tar.extract(member, dst)
|
|
|
|
src_path = os.path.join(dst, member)
|
|
|
|
if os.path.isdir(src_path) and os.path.exists(dst_path):
|
2015-11-19 23:20:12 +00:00
|
|
|
continue
|
2017-07-03 04:32:42 +00:00
|
|
|
shutil.move(src_path, dst_path)
|
2015-11-19 23:20:12 +00:00
|
|
|
shutil.rmtree(os.path.join(dst, fname))
|
|
|
|
|
2017-07-01 04:24:35 +00:00
|
|
|
|
2022-05-29 14:11:36 +00:00
|
|
|
def run(args, verbose=False, exception=False, is_bootstrap=False, **kwargs):
|
2017-07-03 04:32:42 +00:00
|
|
|
"""Run a child program in a new process"""
|
2015-11-19 23:20:12 +00:00
|
|
|
if verbose:
|
2023-04-29 20:53:55 +00:00
|
|
|
print("running: " + ' '.join(args), file=sys.stderr)
|
2015-11-19 23:20:12 +00:00
|
|
|
sys.stdout.flush()
|
2022-07-05 08:06:43 +00:00
|
|
|
# Ensure that the .exe is used on Windows just in case a Linux ELF has been
|
2022-07-04 14:57:10 +00:00
|
|
|
# compiled in the same directory.
|
|
|
|
if os.name == 'nt' and not args[0].endswith('.exe'):
|
|
|
|
args[0] += '.exe'
|
2015-11-19 23:20:12 +00:00
|
|
|
# Use Popen here instead of call() as it apparently allows powershell on
|
|
|
|
# Windows to not lock up waiting for input presumably.
|
2017-05-18 08:33:24 +00:00
|
|
|
ret = subprocess.Popen(args, **kwargs)
|
2015-11-19 23:20:12 +00:00
|
|
|
code = ret.wait()
|
|
|
|
if code != 0:
|
2016-04-14 02:10:25 +00:00
|
|
|
err = "failed to run: " + ' '.join(args)
|
2017-02-23 15:04:29 +00:00
|
|
|
if verbose or exception:
|
2016-04-14 02:10:25 +00:00
|
|
|
raise RuntimeError(err)
|
2021-07-04 01:33:16 +00:00
|
|
|
# For most failures, we definitely do want to print this error, or the user will have no
|
|
|
|
# idea what went wrong. But when we've successfully built bootstrap and it failed, it will
|
|
|
|
# have already printed an error above, so there's no need to print the exact command we're
|
|
|
|
# running.
|
|
|
|
if is_bootstrap:
|
|
|
|
sys.exit(1)
|
|
|
|
else:
|
|
|
|
sys.exit(err)
|
2015-11-19 23:20:12 +00:00
|
|
|
|
windows: kill rust-analyzer-proc-macro-srv before deleting stage0 directory
This fixes the following recurring error on windows:
```
Traceback (most recent call last):
File "C:\Users\jyn\src\rust\x.py", line 29, in <module>
bootstrap.main()
File "C:\Users\jyn\src\rust\src\bootstrap\bootstrap.py", line 963, in main
bootstrap(args)
File "C:\Users\jyn\src\rust\src\bootstrap\bootstrap.py", line 927, in bootstrap
build.download_toolchain()
File "C:\Users\jyn\src\rust\src\bootstrap\bootstrap.py", line 437, in download_toolchain
shutil.rmtree(bin_root)
File "C:\Users\jyn\AppData\Local\Programs\Python\Python311\Lib\shutil.py", line 759, in rmtree
return _rmtree_unsafe(path, onerror)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\jyn\AppData\Local\Programs\Python\Python311\Lib\shutil.py", line 617, in _rmtree_unsafe
_rmtree_unsafe(fullname, onerror)
File "C:\Users\jyn\AppData\Local\Programs\Python\Python311\Lib\shutil.py", line 622, in _rmtree_unsafe
onerror(os.unlink, fullname, sys.exc_info())
File "C:\Users\jyn\AppData\Local\Programs\Python\Python311\Lib\shutil.py", line 620, in _rmtree_unsafe
os.unlink(fullname)
PermissionError: [WinError 5] Access is denied: 'C:\\Users\\jyn\\src\\rust\\build\\x86_64-pc-windows-msvc\\stage0\\bin\\rust-analyzer-proc-macro-srv.exe'
```
2023-04-29 06:43:20 +00:00
|
|
|
def run_powershell(script, *args, **kwargs):
|
|
|
|
"""Run a powershell script"""
|
|
|
|
run(["PowerShell.exe", "/nologo", "-Command"] + script, *args, **kwargs)
|
|
|
|
|
2017-05-19 11:16:29 +00:00
|
|
|
|
2022-05-01 15:43:03 +00:00
|
|
|
def require(cmd, exit=True, exception=False):
|
x.py: Give a more helpful error message if curl isn't installed
This also abstracts checking for a command into `require`.
Before:
```
Updating only changed submodules
Submodules updated in 0.01 seconds
Traceback (most recent call last):
File "./x.py", line 11, in <module>
bootstrap.main()
...
File "/home/joshua/src/rust/src/bootstrap/bootstrap.py", line 137, in run
ret = subprocess.Popen(args, **kwargs)
File "/usr/lib/python2.7/subprocess.py", line 394, in __init__
errread, errwrite)
File "/usr/lib/python2.7/subprocess.py", line 1047, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory
```
After:
```
error: unable to run `curl --version`: [Errno 2] No such file or directory
Please make sure it's installed and in the path.
```
2020-05-03 00:54:28 +00:00
|
|
|
'''Run a command, returning its output.
|
|
|
|
On error,
|
2022-05-01 15:43:03 +00:00
|
|
|
If `exception` is `True`, raise the error
|
|
|
|
Otherwise If `exit` is `True`, exit the process
|
|
|
|
Else return None.'''
|
x.py: Give a more helpful error message if curl isn't installed
This also abstracts checking for a command into `require`.
Before:
```
Updating only changed submodules
Submodules updated in 0.01 seconds
Traceback (most recent call last):
File "./x.py", line 11, in <module>
bootstrap.main()
...
File "/home/joshua/src/rust/src/bootstrap/bootstrap.py", line 137, in run
ret = subprocess.Popen(args, **kwargs)
File "/usr/lib/python2.7/subprocess.py", line 394, in __init__
errread, errwrite)
File "/usr/lib/python2.7/subprocess.py", line 1047, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory
```
After:
```
error: unable to run `curl --version`: [Errno 2] No such file or directory
Please make sure it's installed and in the path.
```
2020-05-03 00:54:28 +00:00
|
|
|
try:
|
|
|
|
return subprocess.check_output(cmd).strip()
|
|
|
|
except (subprocess.CalledProcessError, OSError) as exc:
|
2022-05-01 15:43:03 +00:00
|
|
|
if exception:
|
|
|
|
raise
|
|
|
|
elif exit:
|
2023-04-29 20:53:55 +00:00
|
|
|
print("error: unable to run `{}`: {}".format(' '.join(cmd), exc), file=sys.stderr)
|
|
|
|
print("Please make sure it's installed and in the path.", file=sys.stderr)
|
2022-05-01 15:43:03 +00:00
|
|
|
sys.exit(1)
|
|
|
|
return None
|
|
|
|
|
x.py: Give a more helpful error message if curl isn't installed
This also abstracts checking for a command into `require`.
Before:
```
Updating only changed submodules
Submodules updated in 0.01 seconds
Traceback (most recent call last):
File "./x.py", line 11, in <module>
bootstrap.main()
...
File "/home/joshua/src/rust/src/bootstrap/bootstrap.py", line 137, in run
ret = subprocess.Popen(args, **kwargs)
File "/usr/lib/python2.7/subprocess.py", line 394, in __init__
errread, errwrite)
File "/usr/lib/python2.7/subprocess.py", line 1047, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory
```
After:
```
error: unable to run `curl --version`: [Errno 2] No such file or directory
Please make sure it's installed and in the path.
```
2020-05-03 00:54:28 +00:00
|
|
|
|
|
|
|
|
2016-07-02 14:19:27 +00:00
|
|
|
def format_build_time(duration):
|
2017-07-03 04:32:42 +00:00
|
|
|
"""Return a nicer format for build time
|
|
|
|
|
|
|
|
>>> format_build_time('300')
|
|
|
|
'0:05:00'
|
|
|
|
"""
|
2016-07-02 14:19:27 +00:00
|
|
|
return str(datetime.timedelta(seconds=int(duration)))
|
|
|
|
|
2016-09-18 06:31:06 +00:00
|
|
|
|
2020-10-29 03:09:41 +00:00
|
|
|
def default_build_triple(verbose):
|
2017-08-26 22:01:48 +00:00
|
|
|
"""Build triple as in LLVM"""
|
2023-05-25 14:17:56 +00:00
|
|
|
# If we're on Windows and have an existing `rustc` toolchain, use `rustc --version --verbose`
|
|
|
|
# to find our host target triple. This fixes an issue with Windows builds being detected
|
|
|
|
# as GNU instead of MSVC.
|
|
|
|
# Otherwise, detect it via `uname`
|
2020-12-09 00:18:06 +00:00
|
|
|
default_encoding = sys.getdefaultencoding()
|
2023-04-27 16:39:58 +00:00
|
|
|
|
2023-05-25 14:17:56 +00:00
|
|
|
if platform_is_win32():
|
2023-04-27 16:39:58 +00:00
|
|
|
try:
|
|
|
|
version = subprocess.check_output(["rustc", "--version", "--verbose"],
|
|
|
|
stderr=subprocess.DEVNULL)
|
|
|
|
version = version.decode(default_encoding)
|
|
|
|
host = next(x for x in version.split('\n') if x.startswith("host: "))
|
|
|
|
triple = host.split("host: ")[1]
|
|
|
|
if verbose:
|
2023-04-29 20:53:55 +00:00
|
|
|
print("detected default triple {} from pre-installed rustc".format(triple),
|
|
|
|
file=sys.stderr)
|
2023-04-27 16:39:58 +00:00
|
|
|
return triple
|
|
|
|
except Exception as e:
|
|
|
|
if verbose:
|
2023-04-29 20:53:55 +00:00
|
|
|
print("pre-installed rustc not detected: {}".format(e),
|
|
|
|
file=sys.stderr)
|
|
|
|
print("falling back to auto-detect", file=sys.stderr)
|
2020-10-29 03:09:41 +00:00
|
|
|
|
windows: kill rust-analyzer-proc-macro-srv before deleting stage0 directory
This fixes the following recurring error on windows:
```
Traceback (most recent call last):
File "C:\Users\jyn\src\rust\x.py", line 29, in <module>
bootstrap.main()
File "C:\Users\jyn\src\rust\src\bootstrap\bootstrap.py", line 963, in main
bootstrap(args)
File "C:\Users\jyn\src\rust\src\bootstrap\bootstrap.py", line 927, in bootstrap
build.download_toolchain()
File "C:\Users\jyn\src\rust\src\bootstrap\bootstrap.py", line 437, in download_toolchain
shutil.rmtree(bin_root)
File "C:\Users\jyn\AppData\Local\Programs\Python\Python311\Lib\shutil.py", line 759, in rmtree
return _rmtree_unsafe(path, onerror)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\jyn\AppData\Local\Programs\Python\Python311\Lib\shutil.py", line 617, in _rmtree_unsafe
_rmtree_unsafe(fullname, onerror)
File "C:\Users\jyn\AppData\Local\Programs\Python\Python311\Lib\shutil.py", line 622, in _rmtree_unsafe
onerror(os.unlink, fullname, sys.exc_info())
File "C:\Users\jyn\AppData\Local\Programs\Python\Python311\Lib\shutil.py", line 620, in _rmtree_unsafe
os.unlink(fullname)
PermissionError: [WinError 5] Access is denied: 'C:\\Users\\jyn\\src\\rust\\build\\x86_64-pc-windows-msvc\\stage0\\bin\\rust-analyzer-proc-macro-srv.exe'
```
2023-04-29 06:43:20 +00:00
|
|
|
required = not platform_is_win32()
|
2023-05-25 14:17:56 +00:00
|
|
|
uname = require(["uname", "-smp"], exit=required)
|
x.py: Give a more helpful error message if curl isn't installed
This also abstracts checking for a command into `require`.
Before:
```
Updating only changed submodules
Submodules updated in 0.01 seconds
Traceback (most recent call last):
File "./x.py", line 11, in <module>
bootstrap.main()
...
File "/home/joshua/src/rust/src/bootstrap/bootstrap.py", line 137, in run
ret = subprocess.Popen(args, **kwargs)
File "/usr/lib/python2.7/subprocess.py", line 394, in __init__
errread, errwrite)
File "/usr/lib/python2.7/subprocess.py", line 1047, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory
```
After:
```
error: unable to run `curl --version`: [Errno 2] No such file or directory
Please make sure it's installed and in the path.
```
2020-05-03 00:54:28 +00:00
|
|
|
|
2020-06-25 14:45:24 +00:00
|
|
|
# If we do not have `uname`, assume Windows.
|
2023-05-25 14:17:56 +00:00
|
|
|
if uname is None:
|
x.py: Give a more helpful error message if curl isn't installed
This also abstracts checking for a command into `require`.
Before:
```
Updating only changed submodules
Submodules updated in 0.01 seconds
Traceback (most recent call last):
File "./x.py", line 11, in <module>
bootstrap.main()
...
File "/home/joshua/src/rust/src/bootstrap/bootstrap.py", line 137, in run
ret = subprocess.Popen(args, **kwargs)
File "/usr/lib/python2.7/subprocess.py", line 394, in __init__
errread, errwrite)
File "/usr/lib/python2.7/subprocess.py", line 1047, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory
```
After:
```
error: unable to run `curl --version`: [Errno 2] No such file or directory
Please make sure it's installed and in the path.
```
2020-05-03 00:54:28 +00:00
|
|
|
return 'x86_64-pc-windows-msvc'
|
2017-08-26 22:01:48 +00:00
|
|
|
|
2023-05-25 14:17:56 +00:00
|
|
|
kernel, cputype, processor = uname.decode(default_encoding).split()
|
2020-05-07 00:24:40 +00:00
|
|
|
|
2017-08-26 22:01:48 +00:00
|
|
|
# The goal here is to come up with the same triple as LLVM would,
|
|
|
|
# at least for the subset of platforms we're willing to target.
|
2023-05-25 14:17:56 +00:00
|
|
|
kerneltype_mapper = {
|
2017-08-26 22:01:48 +00:00
|
|
|
'Darwin': 'apple-darwin',
|
|
|
|
'DragonFly': 'unknown-dragonfly',
|
|
|
|
'FreeBSD': 'unknown-freebsd',
|
|
|
|
'Haiku': 'unknown-haiku',
|
|
|
|
'NetBSD': 'unknown-netbsd',
|
|
|
|
'OpenBSD': 'unknown-openbsd'
|
|
|
|
}
|
|
|
|
|
|
|
|
# Consider the direct transformation first and then the special cases
|
2023-05-25 14:17:56 +00:00
|
|
|
if kernel in kerneltype_mapper:
|
|
|
|
kernel = kerneltype_mapper[kernel]
|
|
|
|
elif kernel == 'Linux':
|
|
|
|
# Apple doesn't support `-o` so this can't be used in the combined
|
|
|
|
# uname invocation above
|
|
|
|
ostype = require(["uname", "-o"], exit=required).decode(default_encoding)
|
|
|
|
if ostype == 'Android':
|
|
|
|
kernel = 'linux-android'
|
2017-08-26 22:01:48 +00:00
|
|
|
else:
|
2023-05-25 14:17:56 +00:00
|
|
|
kernel = 'unknown-linux-gnu'
|
|
|
|
elif kernel == 'SunOS':
|
|
|
|
kernel = 'pc-solaris'
|
2017-08-26 22:01:48 +00:00
|
|
|
# On Solaris, uname -m will return a machine classification instead
|
|
|
|
# of a cpu type, so uname -p is recommended instead. However, the
|
|
|
|
# output from that option is too generic for our purposes (it will
|
|
|
|
# always emit 'i386' on x86/amd64 systems). As such, isainfo -k
|
|
|
|
# must be used instead.
|
x.py: Give a more helpful error message if curl isn't installed
This also abstracts checking for a command into `require`.
Before:
```
Updating only changed submodules
Submodules updated in 0.01 seconds
Traceback (most recent call last):
File "./x.py", line 11, in <module>
bootstrap.main()
...
File "/home/joshua/src/rust/src/bootstrap/bootstrap.py", line 137, in run
ret = subprocess.Popen(args, **kwargs)
File "/usr/lib/python2.7/subprocess.py", line 394, in __init__
errread, errwrite)
File "/usr/lib/python2.7/subprocess.py", line 1047, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory
```
After:
```
error: unable to run `curl --version`: [Errno 2] No such file or directory
Please make sure it's installed and in the path.
```
2020-05-03 00:54:28 +00:00
|
|
|
cputype = require(['isainfo', '-k']).decode(default_encoding)
|
2021-02-16 14:02:04 +00:00
|
|
|
# sparc cpus have sun as a target vendor
|
|
|
|
if 'sparc' in cputype:
|
2023-05-25 14:17:56 +00:00
|
|
|
kernel = 'sun-solaris'
|
|
|
|
elif kernel.startswith('MINGW'):
|
2017-08-26 22:01:48 +00:00
|
|
|
# msys' `uname` does not print gcc configuration, but prints msys
|
|
|
|
# configuration. so we cannot believe `uname -m`:
|
|
|
|
# msys1 is always i686 and msys2 is always x86_64.
|
|
|
|
# instead, msys defines $MSYSTEM which is MINGW32 on i686 and
|
|
|
|
# MINGW64 on x86_64.
|
2023-05-25 14:17:56 +00:00
|
|
|
kernel = 'pc-windows-gnu'
|
2017-08-26 22:01:48 +00:00
|
|
|
cputype = 'i686'
|
|
|
|
if os.environ.get('MSYSTEM') == 'MINGW64':
|
|
|
|
cputype = 'x86_64'
|
2023-05-25 14:17:56 +00:00
|
|
|
elif kernel.startswith('MSYS'):
|
|
|
|
kernel = 'pc-windows-gnu'
|
|
|
|
elif kernel.startswith('CYGWIN_NT'):
|
2017-08-26 22:01:48 +00:00
|
|
|
cputype = 'i686'
|
2023-05-25 14:17:56 +00:00
|
|
|
if kernel.endswith('WOW64'):
|
2017-08-26 22:01:48 +00:00
|
|
|
cputype = 'x86_64'
|
2023-05-25 14:17:56 +00:00
|
|
|
kernel = 'pc-windows-gnu'
|
|
|
|
elif platform_is_win32():
|
2020-06-25 14:45:24 +00:00
|
|
|
# Some Windows platforms might have a `uname` command that returns a
|
|
|
|
# non-standard string (e.g. gnuwin32 tools returns `windows32`). In
|
|
|
|
# these cases, fall back to using sys.platform.
|
|
|
|
return 'x86_64-pc-windows-msvc'
|
2017-08-26 22:01:48 +00:00
|
|
|
else:
|
2023-05-25 14:17:56 +00:00
|
|
|
err = "unknown OS type: {}".format(kernel)
|
2017-08-26 22:01:48 +00:00
|
|
|
sys.exit(err)
|
|
|
|
|
2023-05-25 14:17:56 +00:00
|
|
|
if cputype in ['powerpc', 'riscv'] and kernel == 'unknown-freebsd':
|
2019-01-17 18:20:00 +00:00
|
|
|
cputype = subprocess.check_output(
|
|
|
|
['uname', '-p']).strip().decode(default_encoding)
|
2017-08-26 22:01:48 +00:00
|
|
|
cputype_mapper = {
|
|
|
|
'BePC': 'i686',
|
|
|
|
'aarch64': 'aarch64',
|
|
|
|
'amd64': 'x86_64',
|
|
|
|
'arm64': 'aarch64',
|
|
|
|
'i386': 'i686',
|
|
|
|
'i486': 'i686',
|
|
|
|
'i686': 'i686',
|
|
|
|
'i786': 'i686',
|
2022-07-07 03:35:08 +00:00
|
|
|
'loongarch64': 'loongarch64',
|
2021-08-25 07:43:20 +00:00
|
|
|
'm68k': 'm68k',
|
2017-08-26 22:01:48 +00:00
|
|
|
'powerpc': 'powerpc',
|
|
|
|
'powerpc64': 'powerpc64',
|
|
|
|
'powerpc64le': 'powerpc64le',
|
|
|
|
'ppc': 'powerpc',
|
|
|
|
'ppc64': 'powerpc64',
|
|
|
|
'ppc64le': 'powerpc64le',
|
2021-08-19 12:21:41 +00:00
|
|
|
'riscv64': 'riscv64gc',
|
2017-08-26 22:01:48 +00:00
|
|
|
's390x': 's390x',
|
|
|
|
'x64': 'x86_64',
|
|
|
|
'x86': 'i686',
|
|
|
|
'x86-64': 'x86_64',
|
|
|
|
'x86_64': 'x86_64'
|
|
|
|
}
|
|
|
|
|
|
|
|
# Consider the direct transformation first and then the special cases
|
|
|
|
if cputype in cputype_mapper:
|
|
|
|
cputype = cputype_mapper[cputype]
|
|
|
|
elif cputype in {'xscale', 'arm'}:
|
|
|
|
cputype = 'arm'
|
2023-05-25 14:17:56 +00:00
|
|
|
if kernel == 'linux-android':
|
|
|
|
kernel = 'linux-androideabi'
|
|
|
|
elif kernel == 'unknown-freebsd':
|
|
|
|
cputype = processor
|
|
|
|
kernel = 'unknown-freebsd'
|
2017-08-26 22:01:48 +00:00
|
|
|
elif cputype == 'armv6l':
|
|
|
|
cputype = 'arm'
|
2023-05-25 14:17:56 +00:00
|
|
|
if kernel == 'linux-android':
|
|
|
|
kernel = 'linux-androideabi'
|
2017-08-26 22:01:48 +00:00
|
|
|
else:
|
2023-05-25 14:17:56 +00:00
|
|
|
kernel += 'eabihf'
|
2017-08-26 22:01:48 +00:00
|
|
|
elif cputype in {'armv7l', 'armv8l'}:
|
|
|
|
cputype = 'armv7'
|
2023-05-25 14:17:56 +00:00
|
|
|
if kernel == 'linux-android':
|
|
|
|
kernel = 'linux-androideabi'
|
2017-08-26 22:01:48 +00:00
|
|
|
else:
|
2023-05-25 14:17:56 +00:00
|
|
|
kernel += 'eabihf'
|
2017-08-26 22:01:48 +00:00
|
|
|
elif cputype == 'mips':
|
|
|
|
if sys.byteorder == 'big':
|
|
|
|
cputype = 'mips'
|
|
|
|
elif sys.byteorder == 'little':
|
|
|
|
cputype = 'mipsel'
|
|
|
|
else:
|
|
|
|
raise ValueError("unknown byteorder: {}".format(sys.byteorder))
|
|
|
|
elif cputype == 'mips64':
|
|
|
|
if sys.byteorder == 'big':
|
|
|
|
cputype = 'mips64'
|
|
|
|
elif sys.byteorder == 'little':
|
|
|
|
cputype = 'mips64el'
|
|
|
|
else:
|
|
|
|
raise ValueError('unknown byteorder: {}'.format(sys.byteorder))
|
|
|
|
# only the n64 ABI is supported, indicate it
|
2023-05-25 14:17:56 +00:00
|
|
|
kernel += 'abi64'
|
2018-02-17 14:29:11 +00:00
|
|
|
elif cputype == 'sparc' or cputype == 'sparcv9' or cputype == 'sparc64':
|
2017-08-26 22:01:48 +00:00
|
|
|
pass
|
|
|
|
else:
|
|
|
|
err = "unknown cpu type: {}".format(cputype)
|
|
|
|
sys.exit(err)
|
|
|
|
|
2023-05-25 14:17:56 +00:00
|
|
|
return "{}-{}".format(cputype, kernel)
|
2017-08-26 22:01:48 +00:00
|
|
|
|
2017-10-09 00:08:11 +00:00
|
|
|
|
2018-06-23 08:51:19 +00:00
|
|
|
@contextlib.contextmanager
|
|
|
|
def output(filepath):
|
|
|
|
tmp = filepath + '.tmp'
|
|
|
|
with open(tmp, 'w') as f:
|
|
|
|
yield f
|
2018-06-26 19:23:14 +00:00
|
|
|
try:
|
2020-12-27 04:07:11 +00:00
|
|
|
if os.path.exists(filepath):
|
|
|
|
os.remove(filepath) # PermissionError/OSError on Win32 if in use
|
2018-06-26 19:23:14 +00:00
|
|
|
except OSError:
|
|
|
|
shutil.copy2(tmp, filepath)
|
|
|
|
os.remove(tmp)
|
2020-12-27 04:07:11 +00:00
|
|
|
return
|
|
|
|
os.rename(tmp, filepath)
|
2018-06-23 08:51:19 +00:00
|
|
|
|
|
|
|
|
2021-08-26 09:26:03 +00:00
|
|
|
class Stage0Toolchain:
|
|
|
|
def __init__(self, stage0_payload):
|
|
|
|
self.date = stage0_payload["date"]
|
|
|
|
self.version = stage0_payload["version"]
|
|
|
|
|
|
|
|
def channel(self):
|
|
|
|
return self.version + "-" + self.date
|
|
|
|
|
|
|
|
|
2023-04-16 20:03:09 +00:00
|
|
|
class DownloadInfo:
|
|
|
|
"""A helper class that can be pickled into a parallel subprocess"""
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
base_download_url,
|
|
|
|
download_path,
|
|
|
|
bin_root,
|
|
|
|
tarball_path,
|
|
|
|
tarball_suffix,
|
|
|
|
checksums_sha256,
|
|
|
|
pattern,
|
|
|
|
verbose,
|
|
|
|
):
|
|
|
|
self.base_download_url = base_download_url
|
|
|
|
self.download_path = download_path
|
|
|
|
self.bin_root = bin_root
|
|
|
|
self.tarball_path = tarball_path
|
|
|
|
self.tarball_suffix = tarball_suffix
|
|
|
|
self.checksums_sha256 = checksums_sha256
|
|
|
|
self.pattern = pattern
|
|
|
|
self.verbose = verbose
|
|
|
|
|
|
|
|
def download_component(download_info):
|
|
|
|
if not os.path.exists(download_info.tarball_path):
|
|
|
|
get(
|
|
|
|
download_info.base_download_url,
|
|
|
|
download_info.download_path,
|
|
|
|
download_info.tarball_path,
|
|
|
|
download_info.checksums_sha256,
|
|
|
|
verbose=download_info.verbose,
|
|
|
|
)
|
|
|
|
|
|
|
|
def unpack_component(download_info):
|
|
|
|
unpack(
|
|
|
|
download_info.tarball_path,
|
|
|
|
download_info.tarball_suffix,
|
|
|
|
download_info.bin_root,
|
|
|
|
match=download_info.pattern,
|
|
|
|
verbose=download_info.verbose,
|
|
|
|
)
|
|
|
|
|
2016-09-18 06:31:06 +00:00
|
|
|
class RustBuild(object):
|
2017-07-03 04:32:42 +00:00
|
|
|
"""Provide all the methods required to build Rust"""
|
|
|
|
def __init__(self):
|
2021-08-26 10:35:32 +00:00
|
|
|
self.checksums_sha256 = {}
|
2021-08-26 09:26:03 +00:00
|
|
|
self.stage0_compiler = None
|
2023-01-27 21:10:10 +00:00
|
|
|
self.download_url = ''
|
2017-07-03 04:32:42 +00:00
|
|
|
self.build = ''
|
2020-05-07 20:51:03 +00:00
|
|
|
self.build_dir = ''
|
2017-07-03 04:32:42 +00:00
|
|
|
self.clean = False
|
|
|
|
self.config_toml = ''
|
2018-03-10 01:14:35 +00:00
|
|
|
self.rust_root = ''
|
2023-01-27 21:10:10 +00:00
|
|
|
self.use_locked_deps = False
|
|
|
|
self.use_vendored_sources = False
|
2023-01-30 16:30:39 +00:00
|
|
|
self.verbose = False
|
2020-04-25 19:43:19 +00:00
|
|
|
self.git_version = None
|
2020-07-17 12:35:14 +00:00
|
|
|
self.nix_deps_dir = None
|
2023-01-30 16:30:39 +00:00
|
|
|
self._should_fix_bins_and_dylibs = None
|
2017-05-19 11:16:29 +00:00
|
|
|
|
2022-05-22 02:13:48 +00:00
|
|
|
def download_toolchain(self):
|
2017-07-03 04:32:42 +00:00
|
|
|
"""Fetch the build system for Rust, written in Rust
|
|
|
|
|
|
|
|
This method will build a cache directory, then it will fetch the
|
|
|
|
tarball which has the stage0 compiler used to then bootstrap the Rust
|
|
|
|
compiler itself.
|
2015-11-19 23:20:12 +00:00
|
|
|
|
2017-07-03 04:32:42 +00:00
|
|
|
Each downloaded tarball is extracted, after that, the script
|
|
|
|
will move all the content to the right place.
|
|
|
|
"""
|
2022-05-22 02:13:48 +00:00
|
|
|
rustc_channel = self.stage0_compiler.version
|
2022-05-03 18:29:37 +00:00
|
|
|
bin_root = self.bin_root()
|
2021-02-10 05:33:17 +00:00
|
|
|
|
2021-08-26 09:26:03 +00:00
|
|
|
key = self.stage0_compiler.date
|
2022-05-03 18:29:37 +00:00
|
|
|
if self.rustc().startswith(bin_root) and \
|
|
|
|
(not os.path.exists(self.rustc()) or
|
|
|
|
self.program_out_of_date(self.rustc_stamp(), key)):
|
2021-02-10 05:33:17 +00:00
|
|
|
if os.path.exists(bin_root):
|
windows: kill rust-analyzer-proc-macro-srv before deleting stage0 directory
This fixes the following recurring error on windows:
```
Traceback (most recent call last):
File "C:\Users\jyn\src\rust\x.py", line 29, in <module>
bootstrap.main()
File "C:\Users\jyn\src\rust\src\bootstrap\bootstrap.py", line 963, in main
bootstrap(args)
File "C:\Users\jyn\src\rust\src\bootstrap\bootstrap.py", line 927, in bootstrap
build.download_toolchain()
File "C:\Users\jyn\src\rust\src\bootstrap\bootstrap.py", line 437, in download_toolchain
shutil.rmtree(bin_root)
File "C:\Users\jyn\AppData\Local\Programs\Python\Python311\Lib\shutil.py", line 759, in rmtree
return _rmtree_unsafe(path, onerror)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\jyn\AppData\Local\Programs\Python\Python311\Lib\shutil.py", line 617, in _rmtree_unsafe
_rmtree_unsafe(fullname, onerror)
File "C:\Users\jyn\AppData\Local\Programs\Python\Python311\Lib\shutil.py", line 622, in _rmtree_unsafe
onerror(os.unlink, fullname, sys.exc_info())
File "C:\Users\jyn\AppData\Local\Programs\Python\Python311\Lib\shutil.py", line 620, in _rmtree_unsafe
os.unlink(fullname)
PermissionError: [WinError 5] Access is denied: 'C:\\Users\\jyn\\src\\rust\\build\\x86_64-pc-windows-msvc\\stage0\\bin\\rust-analyzer-proc-macro-srv.exe'
```
2023-04-29 06:43:20 +00:00
|
|
|
# HACK: On Windows, we can't delete rust-analyzer-proc-macro-server while it's
|
|
|
|
# running. Kill it.
|
|
|
|
if platform_is_win32():
|
|
|
|
print("Killing rust-analyzer-proc-macro-srv before deleting stage0 toolchain")
|
|
|
|
regex = '{}\\\\(host|{})\\\\stage0\\\\libexec'.format(
|
|
|
|
os.path.basename(self.build_dir),
|
|
|
|
self.build
|
|
|
|
)
|
|
|
|
script = (
|
|
|
|
# NOTE: can't use `taskkill` or `Get-Process -Name` because they error if
|
|
|
|
# the server isn't running.
|
|
|
|
'Get-Process | ' +
|
|
|
|
'Where-Object {$_.Name -eq "rust-analyzer-proc-macro-srv"} |' +
|
|
|
|
'Where-Object {{$_.Path -match "{}"}} |'.format(regex) +
|
|
|
|
'Stop-Process'
|
|
|
|
)
|
|
|
|
run_powershell([script])
|
2021-02-10 05:33:17 +00:00
|
|
|
shutil.rmtree(bin_root)
|
2023-04-16 19:31:33 +00:00
|
|
|
|
|
|
|
key = self.stage0_compiler.date
|
|
|
|
cache_dst = os.path.join(self.build_dir, "cache")
|
|
|
|
rustc_cache = os.path.join(cache_dst, key)
|
|
|
|
if not os.path.exists(rustc_cache):
|
|
|
|
os.makedirs(rustc_cache)
|
|
|
|
|
2023-01-27 21:10:10 +00:00
|
|
|
tarball_suffix = '.tar.gz' if lzma is None else '.tar.xz'
|
2023-04-16 19:34:55 +00:00
|
|
|
|
2023-04-16 19:36:05 +00:00
|
|
|
toolchain_suffix = "{}-{}{}".format(rustc_channel, self.build, tarball_suffix)
|
|
|
|
|
2023-04-16 19:34:55 +00:00
|
|
|
tarballs_to_download = [
|
2023-04-16 19:36:05 +00:00
|
|
|
("rust-std-{}".format(toolchain_suffix), "rust-std-{}".format(self.build)),
|
|
|
|
("rustc-{}".format(toolchain_suffix), "rustc"),
|
|
|
|
("cargo-{}".format(toolchain_suffix), "cargo"),
|
2023-04-16 19:34:55 +00:00
|
|
|
]
|
|
|
|
|
2023-04-16 20:03:09 +00:00
|
|
|
tarballs_download_info = [
|
|
|
|
DownloadInfo(
|
|
|
|
base_download_url=self.download_url,
|
|
|
|
download_path="dist/{}/{}".format(self.stage0_compiler.date, filename),
|
|
|
|
bin_root=self.bin_root(),
|
|
|
|
tarball_path=os.path.join(rustc_cache, filename),
|
|
|
|
tarball_suffix=tarball_suffix,
|
|
|
|
checksums_sha256=self.checksums_sha256,
|
|
|
|
pattern=pattern,
|
|
|
|
verbose=self.verbose,
|
|
|
|
)
|
|
|
|
for filename, pattern in tarballs_to_download
|
|
|
|
]
|
|
|
|
|
|
|
|
# Download the components serially to show the progress bars properly.
|
|
|
|
for download_info in tarballs_download_info:
|
|
|
|
download_component(download_info)
|
|
|
|
|
|
|
|
# Unpack the tarballs in parallle.
|
|
|
|
# In Python 2.7, Pool cannot be used as a context manager.
|
2023-05-01 13:46:31 +00:00
|
|
|
pool_size = min(len(tarballs_download_info), get_cpus())
|
|
|
|
if self.verbose:
|
|
|
|
print('Choosing a pool size of', pool_size, 'for the unpacking of the tarballs')
|
|
|
|
p = Pool(pool_size)
|
2023-04-16 20:03:09 +00:00
|
|
|
try:
|
|
|
|
p.map(unpack_component, tarballs_download_info)
|
|
|
|
finally:
|
|
|
|
p.close()
|
2023-05-01 13:46:31 +00:00
|
|
|
p.join()
|
2023-04-16 19:34:55 +00:00
|
|
|
|
2023-01-27 21:10:10 +00:00
|
|
|
if self.should_fix_bins_and_dylibs():
|
|
|
|
self.fix_bin_or_dylib("{}/bin/cargo".format(bin_root))
|
|
|
|
|
|
|
|
self.fix_bin_or_dylib("{}/bin/rustc".format(bin_root))
|
|
|
|
self.fix_bin_or_dylib("{}/bin/rustdoc".format(bin_root))
|
|
|
|
self.fix_bin_or_dylib("{}/libexec/rust-analyzer-proc-macro-srv".format(bin_root))
|
|
|
|
lib_dir = "{}/lib".format(bin_root)
|
|
|
|
for lib in os.listdir(lib_dir):
|
|
|
|
if lib.endswith(".so"):
|
|
|
|
self.fix_bin_or_dylib(os.path.join(lib_dir, lib))
|
|
|
|
|
2022-05-03 18:29:37 +00:00
|
|
|
with output(self.rustc_stamp()) as rust_stamp:
|
2021-02-10 05:33:17 +00:00
|
|
|
rust_stamp.write(key)
|
2015-11-19 23:20:12 +00:00
|
|
|
|
2021-01-22 05:31:17 +00:00
|
|
|
def _download_component_helper(
|
2023-04-16 19:31:33 +00:00
|
|
|
self, filename, pattern, tarball_suffix, rustc_cache,
|
2021-01-22 05:31:17 +00:00
|
|
|
):
|
2022-05-29 02:17:28 +00:00
|
|
|
key = self.stage0_compiler.date
|
2017-07-03 04:32:42 +00:00
|
|
|
|
|
|
|
tarball = os.path.join(rustc_cache, filename)
|
|
|
|
if not os.path.exists(tarball):
|
2021-08-26 10:35:32 +00:00
|
|
|
get(
|
2023-01-27 21:10:10 +00:00
|
|
|
self.download_url,
|
|
|
|
"dist/{}/{}".format(key, filename),
|
2021-08-26 10:35:32 +00:00
|
|
|
tarball,
|
|
|
|
self.checksums_sha256,
|
2023-01-30 16:30:39 +00:00
|
|
|
verbose=self.verbose,
|
2021-08-26 10:35:32 +00:00
|
|
|
)
|
2023-01-30 16:30:39 +00:00
|
|
|
unpack(tarball, tarball_suffix, self.bin_root(), match=pattern, verbose=self.verbose)
|
2017-07-03 04:32:42 +00:00
|
|
|
|
2023-01-27 21:10:10 +00:00
|
|
|
def should_fix_bins_and_dylibs(self):
|
|
|
|
"""Whether or not `fix_bin_or_dylib` needs to be run; can only be True
|
|
|
|
on NixOS.
|
2017-07-03 04:32:42 +00:00
|
|
|
"""
|
2023-01-30 16:30:39 +00:00
|
|
|
if self._should_fix_bins_and_dylibs is not None:
|
|
|
|
return self._should_fix_bins_and_dylibs
|
|
|
|
|
|
|
|
def get_answer():
|
|
|
|
default_encoding = sys.getdefaultencoding()
|
|
|
|
try:
|
|
|
|
ostype = subprocess.check_output(
|
|
|
|
['uname', '-s']).strip().decode(default_encoding)
|
|
|
|
except subprocess.CalledProcessError:
|
2023-01-27 21:10:10 +00:00
|
|
|
return False
|
2023-01-30 16:30:39 +00:00
|
|
|
except OSError as reason:
|
|
|
|
if getattr(reason, 'winerror', None) is not None:
|
|
|
|
return False
|
|
|
|
raise reason
|
2017-02-06 08:30:01 +00:00
|
|
|
|
2023-01-30 16:30:39 +00:00
|
|
|
if ostype != "Linux":
|
|
|
|
return False
|
2017-02-06 08:30:01 +00:00
|
|
|
|
2023-01-30 16:30:39 +00:00
|
|
|
# If the user has asked binaries to be patched for Nix, then
|
|
|
|
# don't check for NixOS or `/lib`.
|
|
|
|
if self.get_toml("patch-binaries-for-nix", "build") == "true":
|
|
|
|
return True
|
2017-02-06 08:30:01 +00:00
|
|
|
|
2023-01-30 16:30:39 +00:00
|
|
|
# Use `/etc/os-release` instead of `/etc/NIXOS`.
|
|
|
|
# The latter one does not exist on NixOS when using tmpfs as root.
|
|
|
|
try:
|
|
|
|
with open("/etc/os-release", "r") as f:
|
|
|
|
if not any(l.strip() in ("ID=nixos", "ID='nixos'", 'ID="nixos"') for l in f):
|
|
|
|
return False
|
|
|
|
except FileNotFoundError:
|
|
|
|
return False
|
|
|
|
if os.path.exists("/lib"):
|
|
|
|
return False
|
|
|
|
|
|
|
|
return True
|
2023-01-27 21:10:10 +00:00
|
|
|
|
2023-01-30 16:30:39 +00:00
|
|
|
answer = self._should_fix_bins_and_dylibs = get_answer()
|
|
|
|
if answer:
|
2023-04-29 20:53:55 +00:00
|
|
|
print("info: You seem to be using Nix.", file=sys.stderr)
|
2023-01-30 16:30:39 +00:00
|
|
|
return answer
|
2023-01-27 21:10:10 +00:00
|
|
|
|
|
|
|
def fix_bin_or_dylib(self, fname):
|
|
|
|
"""Modifies the interpreter section of 'fname' to fix the dynamic linker,
|
|
|
|
or the RPATH section, to fix the dynamic library search path
|
|
|
|
|
|
|
|
This method is only required on NixOS and uses the PatchELF utility to
|
|
|
|
change the interpreter/RPATH of ELF executables.
|
|
|
|
|
|
|
|
Please see https://nixos.org/patchelf.html for more information
|
|
|
|
"""
|
2023-01-30 16:30:39 +00:00
|
|
|
assert self._should_fix_bins_and_dylibs is True
|
2023-04-29 20:53:55 +00:00
|
|
|
print("attempting to patch", fname, file=sys.stderr)
|
2017-02-06 08:30:01 +00:00
|
|
|
|
2021-02-10 05:33:17 +00:00
|
|
|
# Only build `.nix-deps` once.
|
2020-07-17 12:35:14 +00:00
|
|
|
nix_deps_dir = self.nix_deps_dir
|
|
|
|
if not nix_deps_dir:
|
|
|
|
# Run `nix-build` to "build" each dependency (which will likely reuse
|
|
|
|
# the existing `/nix/store` copy, or at most download a pre-built copy).
|
2021-04-10 20:05:28 +00:00
|
|
|
#
|
|
|
|
# Importantly, we create a gc-root called `.nix-deps` in the `build/`
|
|
|
|
# directory, but still reference the actual `/nix/store` path in the rpath
|
|
|
|
# as it makes it significantly more robust against changes to the location of
|
|
|
|
# the `.nix-deps` location.
|
|
|
|
#
|
|
|
|
# bintools: Needed for the path of `ld-linux.so` (via `nix-support/dynamic-linker`).
|
|
|
|
# zlib: Needed as a system dependency of `libLLVM-*.so`.
|
|
|
|
# patchelf: Needed for patching ELF binaries (see doc comment above).
|
|
|
|
nix_deps_dir = "{}/{}".format(self.build_dir, ".nix-deps")
|
|
|
|
nix_expr = '''
|
|
|
|
with (import <nixpkgs> {});
|
|
|
|
symlinkJoin {
|
|
|
|
name = "rust-stage0-dependencies";
|
|
|
|
paths = [
|
|
|
|
zlib
|
|
|
|
patchelf
|
|
|
|
stdenv.cc.bintools
|
|
|
|
];
|
|
|
|
}
|
|
|
|
'''
|
|
|
|
try:
|
|
|
|
subprocess.check_output([
|
|
|
|
"nix-build", "-E", nix_expr, "-o", nix_deps_dir,
|
|
|
|
])
|
|
|
|
except subprocess.CalledProcessError as reason:
|
2023-04-29 20:53:55 +00:00
|
|
|
print("warning: failed to call nix-build:", reason, file=sys.stderr)
|
2021-04-10 20:05:28 +00:00
|
|
|
return
|
2020-07-17 12:35:14 +00:00
|
|
|
self.nix_deps_dir = nix_deps_dir
|
|
|
|
|
2021-04-10 20:05:28 +00:00
|
|
|
patchelf = "{}/bin/patchelf".format(nix_deps_dir)
|
|
|
|
rpath_entries = [
|
|
|
|
# Relative default, all binary and dynamic libraries we ship
|
|
|
|
# appear to have this (even when `../lib` is redundant).
|
|
|
|
"$ORIGIN/../lib",
|
|
|
|
os.path.join(os.path.realpath(nix_deps_dir), "lib")
|
|
|
|
]
|
|
|
|
patchelf_args = ["--set-rpath", ":".join(rpath_entries)]
|
2021-01-08 19:21:30 +00:00
|
|
|
if not fname.endswith(".so"):
|
2023-04-09 21:35:23 +00:00
|
|
|
# Finally, set the correct .interp for binaries
|
2021-04-10 20:05:28 +00:00
|
|
|
with open("{}/nix-support/dynamic-linker".format(nix_deps_dir)) as dynamic_linker:
|
2021-01-08 19:21:30 +00:00
|
|
|
patchelf_args += ["--set-interpreter", dynamic_linker.read().rstrip()]
|
2017-02-06 08:30:01 +00:00
|
|
|
|
|
|
|
try:
|
2020-07-17 13:25:05 +00:00
|
|
|
subprocess.check_output([patchelf] + patchelf_args + [fname])
|
2017-07-03 04:32:42 +00:00
|
|
|
except subprocess.CalledProcessError as reason:
|
2023-04-29 20:53:55 +00:00
|
|
|
print("warning: failed to call patchelf:", reason, file=sys.stderr)
|
2017-02-06 08:30:01 +00:00
|
|
|
return
|
|
|
|
|
2022-05-03 18:29:37 +00:00
|
|
|
def rustc_stamp(self):
|
2021-02-10 05:33:17 +00:00
|
|
|
"""Return the path for .rustc-stamp at the given stage
|
2017-07-03 04:32:42 +00:00
|
|
|
|
|
|
|
>>> rb = RustBuild()
|
|
|
|
>>> rb.build_dir = "build"
|
2022-05-03 18:29:37 +00:00
|
|
|
>>> rb.rustc_stamp() == os.path.join("build", "stage0", ".rustc-stamp")
|
2017-07-03 04:32:42 +00:00
|
|
|
True
|
|
|
|
"""
|
2022-05-03 18:29:37 +00:00
|
|
|
return os.path.join(self.bin_root(), '.rustc-stamp')
|
2015-11-19 23:20:12 +00:00
|
|
|
|
2020-12-09 01:18:39 +00:00
|
|
|
def program_out_of_date(self, stamp_path, key):
|
2017-07-03 04:32:42 +00:00
|
|
|
"""Check if the given program stamp is out of date"""
|
|
|
|
if not os.path.exists(stamp_path) or self.clean:
|
2015-11-19 23:20:12 +00:00
|
|
|
return True
|
2017-07-03 04:32:42 +00:00
|
|
|
with open(stamp_path, 'r') as stamp:
|
2020-12-09 01:18:39 +00:00
|
|
|
return key != stamp.read()
|
2015-11-19 23:20:12 +00:00
|
|
|
|
2022-05-03 18:29:37 +00:00
|
|
|
def bin_root(self):
|
2021-02-10 05:33:17 +00:00
|
|
|
"""Return the binary root directory for the given stage
|
2017-07-03 04:32:42 +00:00
|
|
|
|
|
|
|
>>> rb = RustBuild()
|
|
|
|
>>> rb.build_dir = "build"
|
2022-05-03 18:29:37 +00:00
|
|
|
>>> rb.bin_root() == os.path.join("build", "stage0")
|
2017-07-03 04:32:42 +00:00
|
|
|
True
|
|
|
|
|
|
|
|
When the 'build' property is given should be a nested directory:
|
|
|
|
|
|
|
|
>>> rb.build = "devel"
|
2022-05-03 18:29:37 +00:00
|
|
|
>>> rb.bin_root() == os.path.join("build", "devel", "stage0")
|
2017-07-03 04:32:42 +00:00
|
|
|
True
|
|
|
|
"""
|
2022-05-03 18:29:37 +00:00
|
|
|
subdir = "stage0"
|
2021-02-10 05:33:17 +00:00
|
|
|
return os.path.join(self.build_dir, self.build, subdir)
|
2015-11-19 23:20:12 +00:00
|
|
|
|
2018-04-20 18:50:50 +00:00
|
|
|
def get_toml(self, key, section=None):
|
2017-07-03 04:32:42 +00:00
|
|
|
"""Returns the value of the given key in config.toml, otherwise returns None
|
|
|
|
|
|
|
|
>>> rb = RustBuild()
|
|
|
|
>>> rb.config_toml = 'key1 = "value1"\\nkey2 = "value2"'
|
|
|
|
>>> rb.get_toml("key2")
|
|
|
|
'value2'
|
|
|
|
|
2022-01-13 21:17:11 +00:00
|
|
|
If the key does not exist, the result is None:
|
2017-07-03 04:32:42 +00:00
|
|
|
|
2017-10-08 23:46:58 +00:00
|
|
|
>>> rb.get_toml("key3") is None
|
2017-07-03 04:32:42 +00:00
|
|
|
True
|
2018-04-20 18:50:50 +00:00
|
|
|
|
|
|
|
Optionally also matches the section the key appears in
|
|
|
|
|
|
|
|
>>> rb.config_toml = '[a]\\nkey = "value1"\\n[b]\\nkey = "value2"'
|
|
|
|
>>> rb.get_toml('key', 'a')
|
|
|
|
'value1'
|
|
|
|
>>> rb.get_toml('key', 'b')
|
|
|
|
'value2'
|
|
|
|
>>> rb.get_toml('key', 'c') is None
|
|
|
|
True
|
2019-09-08 08:26:06 +00:00
|
|
|
|
|
|
|
>>> rb.config_toml = 'key1 = true'
|
|
|
|
>>> rb.get_toml("key1")
|
|
|
|
'true'
|
2017-07-03 04:32:42 +00:00
|
|
|
"""
|
2018-04-20 18:50:50 +00:00
|
|
|
|
|
|
|
cur_section = None
|
2015-11-19 23:20:12 +00:00
|
|
|
for line in self.config_toml.splitlines():
|
2018-04-20 18:50:50 +00:00
|
|
|
section_match = re.match(r'^\s*\[(.*)\]\s*$', line)
|
|
|
|
if section_match is not None:
|
|
|
|
cur_section = section_match.group(1)
|
|
|
|
|
2017-05-13 05:21:35 +00:00
|
|
|
match = re.match(r'^{}\s*=(.*)$'.format(key), line)
|
|
|
|
if match is not None:
|
|
|
|
value = match.group(1)
|
2018-04-20 18:50:50 +00:00
|
|
|
if section is None or section == cur_section:
|
|
|
|
return self.get_string(value) or value.strip()
|
2015-11-19 23:20:12 +00:00
|
|
|
return None
|
|
|
|
|
|
|
|
def cargo(self):
|
2017-07-03 04:32:42 +00:00
|
|
|
"""Return config path for cargo"""
|
|
|
|
return self.program_config('cargo')
|
2015-11-19 23:20:12 +00:00
|
|
|
|
2022-05-03 18:29:37 +00:00
|
|
|
def rustc(self):
|
2017-07-03 04:32:42 +00:00
|
|
|
"""Return config path for rustc"""
|
2022-05-03 18:29:37 +00:00
|
|
|
return self.program_config('rustc')
|
2017-07-03 04:32:42 +00:00
|
|
|
|
2022-05-03 18:29:37 +00:00
|
|
|
def program_config(self, program):
|
2021-02-10 05:33:17 +00:00
|
|
|
"""Return config path for the given program at the given stage
|
2017-07-03 04:32:42 +00:00
|
|
|
|
|
|
|
>>> rb = RustBuild()
|
|
|
|
>>> rb.config_toml = 'rustc = "rustc"\\n'
|
|
|
|
>>> rb.program_config('rustc')
|
|
|
|
'rustc'
|
|
|
|
>>> rb.config_toml = ''
|
2022-05-03 18:29:37 +00:00
|
|
|
>>> cargo_path = rb.program_config('cargo')
|
|
|
|
>>> cargo_path.rstrip(".exe") == os.path.join(rb.bin_root(),
|
2017-07-03 04:32:42 +00:00
|
|
|
... "bin", "cargo")
|
|
|
|
True
|
|
|
|
"""
|
|
|
|
config = self.get_toml(program)
|
2015-11-19 23:20:12 +00:00
|
|
|
if config:
|
2017-10-18 21:22:32 +00:00
|
|
|
return os.path.expanduser(config)
|
2023-01-27 21:10:10 +00:00
|
|
|
return os.path.join(self.bin_root(), "bin", "{}{}".format(program, EXE_SUFFIX))
|
2017-07-03 04:32:42 +00:00
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def get_string(line):
|
|
|
|
"""Return the value between double quotes
|
|
|
|
|
|
|
|
>>> RustBuild.get_string(' "devel" ')
|
|
|
|
'devel'
|
2019-09-08 08:26:06 +00:00
|
|
|
>>> RustBuild.get_string(" 'devel' ")
|
|
|
|
'devel'
|
|
|
|
>>> RustBuild.get_string('devel') is None
|
|
|
|
True
|
|
|
|
>>> RustBuild.get_string(' "devel ')
|
|
|
|
''
|
2017-07-03 04:32:42 +00:00
|
|
|
"""
|
2015-11-19 23:20:12 +00:00
|
|
|
start = line.find('"')
|
2017-08-26 22:01:48 +00:00
|
|
|
if start != -1:
|
|
|
|
end = start + 1 + line[start + 1:].find('"')
|
|
|
|
return line[start + 1:end]
|
|
|
|
start = line.find('\'')
|
|
|
|
if start != -1:
|
|
|
|
end = start + 1 + line[start + 1:].find('\'')
|
|
|
|
return line[start + 1:end]
|
|
|
|
return None
|
2015-11-19 23:20:12 +00:00
|
|
|
|
2016-11-16 20:31:19 +00:00
|
|
|
def bootstrap_binary(self):
|
2018-10-22 16:21:55 +00:00
|
|
|
"""Return the path of the bootstrap binary
|
2017-07-03 04:32:42 +00:00
|
|
|
|
|
|
|
>>> rb = RustBuild()
|
|
|
|
>>> rb.build_dir = "build"
|
|
|
|
>>> rb.bootstrap_binary() == os.path.join("build", "bootstrap",
|
|
|
|
... "debug", "bootstrap")
|
|
|
|
True
|
|
|
|
"""
|
|
|
|
return os.path.join(self.build_dir, "bootstrap", "debug", "bootstrap")
|
2016-11-16 20:31:19 +00:00
|
|
|
|
2023-05-29 22:03:51 +00:00
|
|
|
def build_bootstrap(self, color, warnings, verbose_count):
|
2017-07-03 04:32:42 +00:00
|
|
|
"""Build bootstrap"""
|
2023-04-21 23:04:32 +00:00
|
|
|
env = os.environ.copy()
|
|
|
|
if "GITHUB_ACTIONS" in env:
|
|
|
|
print("::group::Building bootstrap")
|
|
|
|
else:
|
2023-04-29 20:53:55 +00:00
|
|
|
print("Building bootstrap", file=sys.stderr)
|
2016-05-31 20:24:28 +00:00
|
|
|
build_dir = os.path.join(self.build_dir, "bootstrap")
|
|
|
|
if self.clean and os.path.exists(build_dir):
|
|
|
|
shutil.rmtree(build_dir)
|
2020-03-21 14:54:01 +00:00
|
|
|
# `CARGO_BUILD_TARGET` breaks bootstrap build.
|
|
|
|
# See also: <https://github.com/rust-lang/rust/issues/70208>.
|
|
|
|
if "CARGO_BUILD_TARGET" in env:
|
|
|
|
del env["CARGO_BUILD_TARGET"]
|
2016-05-31 20:24:28 +00:00
|
|
|
env["CARGO_TARGET_DIR"] = build_dir
|
2022-05-03 18:29:37 +00:00
|
|
|
env["RUSTC"] = self.rustc()
|
|
|
|
env["LD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
|
2017-05-19 11:16:29 +00:00
|
|
|
(os.pathsep + env["LD_LIBRARY_PATH"]) \
|
|
|
|
if "LD_LIBRARY_PATH" in env else ""
|
2022-05-03 18:29:37 +00:00
|
|
|
env["DYLD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
|
2017-05-19 11:16:29 +00:00
|
|
|
(os.pathsep + env["DYLD_LIBRARY_PATH"]) \
|
|
|
|
if "DYLD_LIBRARY_PATH" in env else ""
|
2022-05-03 18:29:37 +00:00
|
|
|
env["LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
|
2017-05-19 11:16:29 +00:00
|
|
|
(os.pathsep + env["LIBRARY_PATH"]) \
|
|
|
|
if "LIBRARY_PATH" in env else ""
|
2023-03-28 02:59:45 +00:00
|
|
|
env["LIBPATH"] = os.path.join(self.bin_root(), "lib") + \
|
|
|
|
(os.pathsep + env["LIBPATH"]) \
|
|
|
|
if "LIBPATH" in env else ""
|
2021-10-11 02:16:01 +00:00
|
|
|
|
2022-09-13 23:13:43 +00:00
|
|
|
# Export Stage0 snapshot compiler related env variables
|
|
|
|
build_section = "target.{}".format(self.build)
|
|
|
|
host_triple_sanitized = self.build.replace("-", "_")
|
|
|
|
var_data = {
|
|
|
|
"CC": "cc", "CXX": "cxx", "LD": "linker", "AR": "ar", "RANLIB": "ranlib"
|
|
|
|
}
|
|
|
|
for var_name, toml_key in var_data.items():
|
|
|
|
toml_val = self.get_toml(toml_key, build_section)
|
|
|
|
if toml_val != None:
|
|
|
|
env["{}_{}".format(var_name, host_triple_sanitized)] = toml_val
|
|
|
|
|
2019-11-28 10:11:02 +00:00
|
|
|
# preserve existing RUSTFLAGS
|
|
|
|
env.setdefault("RUSTFLAGS", "")
|
2023-05-19 04:21:59 +00:00
|
|
|
# we need to explicitly add +xgot here so that we can successfully bootstrap
|
|
|
|
# a usable stage1 compiler
|
|
|
|
# FIXME: remove this if condition on the next bootstrap bump
|
|
|
|
# cfg(bootstrap)
|
|
|
|
if self.build_triple().startswith('mips'):
|
|
|
|
env["RUSTFLAGS"] += " -Ctarget-feature=+xgot"
|
2018-04-20 18:50:50 +00:00
|
|
|
target_features = []
|
|
|
|
if self.get_toml("crt-static", build_section) == "true":
|
|
|
|
target_features += ["+crt-static"]
|
|
|
|
elif self.get_toml("crt-static", build_section) == "false":
|
|
|
|
target_features += ["-crt-static"]
|
|
|
|
if target_features:
|
2019-11-28 10:11:02 +00:00
|
|
|
env["RUSTFLAGS"] += " -C target-feature=" + (",".join(target_features))
|
2022-12-26 03:52:14 +00:00
|
|
|
target_linker = self.get_toml("linker", build_section)
|
|
|
|
if target_linker is not None:
|
|
|
|
env["RUSTFLAGS"] += " -C linker=" + target_linker
|
2019-11-28 10:11:02 +00:00
|
|
|
env["RUSTFLAGS"] += " -Wrust_2018_idioms -Wunused_lifetimes"
|
2023-05-29 22:03:51 +00:00
|
|
|
if warnings == "default":
|
|
|
|
deny_warnings = self.get_toml("deny-warnings", "rust") != "false"
|
|
|
|
else:
|
|
|
|
deny_warnings = warnings == "deny"
|
|
|
|
if deny_warnings:
|
2019-11-28 10:11:02 +00:00
|
|
|
env["RUSTFLAGS"] += " -Dwarnings"
|
2018-04-20 18:50:50 +00:00
|
|
|
|
2022-05-03 18:29:37 +00:00
|
|
|
env["PATH"] = os.path.join(self.bin_root(), "bin") + \
|
2017-05-19 11:16:29 +00:00
|
|
|
os.pathsep + env["PATH"]
|
2016-11-16 23:02:56 +00:00
|
|
|
if not os.path.isfile(self.cargo()):
|
2017-07-03 04:32:42 +00:00
|
|
|
raise Exception("no cargo executable found at `{}`".format(
|
|
|
|
self.cargo()))
|
2016-11-01 20:46:38 +00:00
|
|
|
args = [self.cargo(), "build", "--manifest-path",
|
|
|
|
os.path.join(self.rust_root, "src/bootstrap/Cargo.toml")]
|
2023-01-30 16:30:39 +00:00
|
|
|
args.extend("--verbose" for _ in range(verbose_count))
|
2017-02-10 20:59:40 +00:00
|
|
|
if self.use_locked_deps:
|
|
|
|
args.append("--locked")
|
2016-11-01 20:46:38 +00:00
|
|
|
if self.use_vendored_sources:
|
|
|
|
args.append("--frozen")
|
2022-02-06 22:03:55 +00:00
|
|
|
if self.get_toml("metrics", "build"):
|
|
|
|
args.append("--features")
|
|
|
|
args.append("build-metrics")
|
2023-02-11 01:38:33 +00:00
|
|
|
if self.json_output:
|
|
|
|
args.append("--message-format=json")
|
2022-06-19 21:19:56 +00:00
|
|
|
if color == "always":
|
|
|
|
args.append("--color=always")
|
|
|
|
elif color == "never":
|
|
|
|
args.append("--color=never")
|
2023-05-21 21:34:19 +00:00
|
|
|
try:
|
|
|
|
args += env["CARGOFLAGS"].split()
|
|
|
|
except KeyError:
|
|
|
|
pass
|
2022-06-19 21:19:56 +00:00
|
|
|
|
2022-09-15 02:54:16 +00:00
|
|
|
# Run this from the source directory so cargo finds .cargo/config
|
2023-01-30 16:30:39 +00:00
|
|
|
run(args, env=env, verbose=self.verbose, cwd=self.rust_root)
|
2015-11-19 23:20:12 +00:00
|
|
|
|
2023-04-21 23:04:32 +00:00
|
|
|
if "GITHUB_ACTIONS" in env:
|
|
|
|
print("::endgroup::")
|
|
|
|
|
2015-11-19 23:20:12 +00:00
|
|
|
def build_triple(self):
|
2020-12-09 00:18:06 +00:00
|
|
|
"""Build triple as in LLVM
|
|
|
|
|
|
|
|
Note that `default_build_triple` is moderately expensive,
|
|
|
|
so use `self.build` where possible.
|
|
|
|
"""
|
2015-11-19 23:20:12 +00:00
|
|
|
config = self.get_toml('build')
|
2023-01-30 16:30:39 +00:00
|
|
|
return config or default_build_triple(self.verbose)
|
2017-07-03 04:32:42 +00:00
|
|
|
|
2019-07-09 23:12:41 +00:00
|
|
|
def check_vendored_status(self):
|
|
|
|
"""Check that vendoring is configured properly"""
|
2022-08-21 17:51:58 +00:00
|
|
|
# keep this consistent with the equivalent check in rustbuild:
|
|
|
|
# https://github.com/rust-lang/rust/blob/a8a33cf27166d3eabaffc58ed3799e054af3b0c6/src/bootstrap/lib.rs#L399-L405
|
2019-07-09 23:12:41 +00:00
|
|
|
if 'SUDO_USER' in os.environ and not self.use_vendored_sources:
|
2022-04-10 15:21:11 +00:00
|
|
|
if os.getuid() == 0:
|
2019-07-09 23:12:41 +00:00
|
|
|
self.use_vendored_sources = True
|
2023-04-29 20:53:55 +00:00
|
|
|
print('info: looks like you\'re trying to run this command as root',
|
|
|
|
file=sys.stderr)
|
|
|
|
print(' and so in order to preserve your $HOME this will now',
|
|
|
|
file=sys.stderr)
|
|
|
|
print(' use vendored sources by default.',
|
|
|
|
file=sys.stderr)
|
2019-07-09 23:12:41 +00:00
|
|
|
|
2022-06-22 01:40:23 +00:00
|
|
|
cargo_dir = os.path.join(self.rust_root, '.cargo')
|
2019-07-09 23:12:41 +00:00
|
|
|
if self.use_vendored_sources:
|
2022-06-22 01:40:23 +00:00
|
|
|
vendor_dir = os.path.join(self.rust_root, 'vendor')
|
|
|
|
if not os.path.exists(vendor_dir):
|
2023-03-15 12:50:04 +00:00
|
|
|
sync_dirs = "--sync ./src/tools/cargo/Cargo.toml " \
|
|
|
|
"--sync ./src/tools/rust-analyzer/Cargo.toml " \
|
2022-06-22 01:40:23 +00:00
|
|
|
"--sync ./compiler/rustc_codegen_cranelift/Cargo.toml " \
|
|
|
|
"--sync ./src/bootstrap/Cargo.toml "
|
2023-04-29 20:53:55 +00:00
|
|
|
print('error: vendoring required, but vendor directory does not exist.',
|
|
|
|
file=sys.stderr)
|
2022-06-22 01:40:23 +00:00
|
|
|
print(' Run `cargo vendor {}` to initialize the '
|
2023-04-29 20:53:55 +00:00
|
|
|
'vendor directory.'.format(sync_dirs),
|
|
|
|
file=sys.stderr)
|
|
|
|
print('Alternatively, use the pre-vendored `rustc-src` dist component.',
|
|
|
|
file=sys.stderr)
|
2022-06-22 01:40:23 +00:00
|
|
|
raise Exception("{} not found".format(vendor_dir))
|
|
|
|
|
|
|
|
if not os.path.exists(cargo_dir):
|
2023-04-29 20:53:55 +00:00
|
|
|
print('error: vendoring required, but .cargo/config does not exist.',
|
|
|
|
file=sys.stderr)
|
2022-06-22 01:40:23 +00:00
|
|
|
raise Exception("{} not found".format(cargo_dir))
|
2019-07-09 23:12:41 +00:00
|
|
|
else:
|
2022-06-22 01:40:23 +00:00
|
|
|
if os.path.exists(cargo_dir):
|
|
|
|
shutil.rmtree(cargo_dir)
|
2017-05-17 16:15:44 +00:00
|
|
|
|
2023-01-29 20:53:10 +00:00
|
|
|
def parse_args():
|
|
|
|
"""Parse the command line arguments that the python script needs."""
|
|
|
|
parser = argparse.ArgumentParser(add_help=False)
|
|
|
|
parser.add_argument('-h', '--help', action='store_true')
|
2016-04-13 18:18:35 +00:00
|
|
|
parser.add_argument('--config')
|
2022-06-30 22:50:48 +00:00
|
|
|
parser.add_argument('--build-dir')
|
2017-07-21 11:11:18 +00:00
|
|
|
parser.add_argument('--build')
|
2022-06-19 21:19:56 +00:00
|
|
|
parser.add_argument('--color', choices=['always', 'never', 'auto'])
|
2016-05-31 20:24:28 +00:00
|
|
|
parser.add_argument('--clean', action='store_true')
|
2023-02-11 01:38:33 +00:00
|
|
|
parser.add_argument('--json-output', action='store_true')
|
2023-05-29 22:03:51 +00:00
|
|
|
parser.add_argument('--warnings', choices=['deny', 'warn', 'default'], default='default')
|
2018-03-11 23:44:05 +00:00
|
|
|
parser.add_argument('-v', '--verbose', action='count', default=0)
|
2016-04-13 18:18:35 +00:00
|
|
|
|
2023-01-29 20:53:10 +00:00
|
|
|
return parser.parse_known_args(sys.argv)[0]
|
2016-04-13 18:18:35 +00:00
|
|
|
|
2023-01-29 20:53:10 +00:00
|
|
|
def bootstrap(args):
|
|
|
|
"""Configure, fetch, build and run the initial bootstrap"""
|
2016-04-13 18:18:35 +00:00
|
|
|
# Configure initial bootstrap
|
2017-07-03 04:32:42 +00:00
|
|
|
build = RustBuild()
|
2020-09-07 01:32:55 +00:00
|
|
|
build.rust_root = os.path.abspath(os.path.join(__file__, '../../..'))
|
2023-01-30 16:30:39 +00:00
|
|
|
build.verbose = args.verbose != 0
|
2017-07-03 04:32:42 +00:00
|
|
|
build.clean = args.clean
|
2023-02-11 01:38:33 +00:00
|
|
|
build.json_output = args.json_output
|
2016-04-13 18:18:35 +00:00
|
|
|
|
2022-03-04 01:48:26 +00:00
|
|
|
# Read from `--config`, then `RUST_BOOTSTRAP_CONFIG`, then `./config.toml`,
|
|
|
|
# then `config.toml` in the root directory.
|
2022-02-07 12:52:08 +00:00
|
|
|
toml_path = args.config or os.getenv('RUST_BOOTSTRAP_CONFIG')
|
2022-03-04 01:48:26 +00:00
|
|
|
using_default_path = toml_path is None
|
|
|
|
if using_default_path:
|
2020-06-21 19:21:17 +00:00
|
|
|
toml_path = 'config.toml'
|
2020-05-07 20:51:03 +00:00
|
|
|
if not os.path.exists(toml_path):
|
|
|
|
toml_path = os.path.join(build.rust_root, toml_path)
|
|
|
|
|
2022-03-04 01:48:26 +00:00
|
|
|
# Give a hard error if `--config` or `RUST_BOOTSTRAP_CONFIG` are set to a missing path,
|
|
|
|
# but not if `config.toml` hasn't been created.
|
|
|
|
if not using_default_path or os.path.exists(toml_path):
|
2020-05-07 20:51:03 +00:00
|
|
|
with open(toml_path) as config:
|
2017-07-03 04:32:42 +00:00
|
|
|
build.config_toml = config.read()
|
2016-04-13 18:18:35 +00:00
|
|
|
|
2020-10-13 15:58:56 +00:00
|
|
|
profile = build.get_toml('profile')
|
|
|
|
if profile is not None:
|
|
|
|
include_file = 'config.{}.toml'.format(profile)
|
|
|
|
include_dir = os.path.join(build.rust_root, 'src', 'bootstrap', 'defaults')
|
|
|
|
include_path = os.path.join(include_dir, include_file)
|
|
|
|
# HACK: This works because `build.get_toml()` returns the first match it finds for a
|
|
|
|
# specific key, so appending our defaults at the end allows the user to override them
|
|
|
|
with open(include_path) as included_toml:
|
|
|
|
build.config_toml += os.linesep + included_toml.read()
|
|
|
|
|
2023-01-30 16:30:39 +00:00
|
|
|
verbose_count = args.verbose
|
|
|
|
config_verbose_count = build.get_toml('verbose', 'build')
|
|
|
|
if config_verbose_count is not None:
|
|
|
|
verbose_count = max(args.verbose, int(config_verbose_count))
|
2017-05-24 07:09:17 +00:00
|
|
|
|
2019-09-08 08:26:06 +00:00
|
|
|
build.use_vendored_sources = build.get_toml('vendor', 'build') == 'true'
|
|
|
|
build.use_locked_deps = build.get_toml('locked-deps', 'build') == 'true'
|
2017-02-10 20:59:40 +00:00
|
|
|
|
2019-07-09 23:12:41 +00:00
|
|
|
build.check_vendored_status()
|
2016-11-16 20:31:19 +00:00
|
|
|
|
2022-06-30 22:50:48 +00:00
|
|
|
build_dir = args.build_dir or build.get_toml('build-dir', 'build') or 'build'
|
2021-12-24 19:03:02 +00:00
|
|
|
build.build_dir = os.path.abspath(build_dir)
|
2020-05-07 20:51:03 +00:00
|
|
|
|
2021-08-26 09:26:03 +00:00
|
|
|
with open(os.path.join(build.rust_root, "src", "stage0.json")) as f:
|
|
|
|
data = json.load(f)
|
2021-08-26 10:35:32 +00:00
|
|
|
build.checksums_sha256 = data["checksums_sha256"]
|
2021-08-26 09:26:03 +00:00
|
|
|
build.stage0_compiler = Stage0Toolchain(data["compiler"])
|
2023-01-27 21:10:10 +00:00
|
|
|
build.download_url = os.getenv("RUSTUP_DIST_SERVER") or data["config"]["dist_server"]
|
2016-04-13 18:18:35 +00:00
|
|
|
|
2021-01-29 17:23:36 +00:00
|
|
|
build.build = args.build or build.build_triple()
|
2021-08-24 20:45:46 +00:00
|
|
|
|
|
|
|
if not os.path.exists(build.build_dir):
|
|
|
|
os.makedirs(build.build_dir)
|
2017-05-13 05:21:35 +00:00
|
|
|
|
2016-04-13 18:18:35 +00:00
|
|
|
# Fetch/build the bootstrap
|
2021-02-10 05:33:17 +00:00
|
|
|
build.download_toolchain()
|
2016-04-13 18:18:35 +00:00
|
|
|
sys.stdout.flush()
|
2023-05-29 22:03:51 +00:00
|
|
|
build.build_bootstrap(args.color, args.warnings, verbose_count)
|
2016-04-13 18:18:35 +00:00
|
|
|
sys.stdout.flush()
|
|
|
|
|
|
|
|
# Run the bootstrap
|
2017-07-03 04:32:42 +00:00
|
|
|
args = [build.bootstrap_binary()]
|
2016-04-13 18:18:35 +00:00
|
|
|
args.extend(sys.argv[1:])
|
|
|
|
env = os.environ.copy()
|
|
|
|
env["BOOTSTRAP_PARENT_ID"] = str(os.getpid())
|
2017-06-21 16:01:24 +00:00
|
|
|
env["BOOTSTRAP_PYTHON"] = sys.executable
|
2021-07-04 01:33:16 +00:00
|
|
|
run(args, env=env, verbose=build.verbose, is_bootstrap=True)
|
2016-04-13 18:18:35 +00:00
|
|
|
|
2017-05-19 11:16:29 +00:00
|
|
|
|
2017-03-03 02:27:07 +00:00
|
|
|
def main():
|
2017-07-03 04:32:42 +00:00
|
|
|
"""Entry point for the bootstrap process"""
|
2017-03-03 02:27:07 +00:00
|
|
|
start_time = time()
|
2018-10-01 01:06:58 +00:00
|
|
|
|
|
|
|
# x.py help <cmd> ...
|
|
|
|
if len(sys.argv) > 1 and sys.argv[1] == 'help':
|
2023-01-27 21:10:10 +00:00
|
|
|
sys.argv[1] = '-h'
|
2018-10-01 01:06:58 +00:00
|
|
|
|
2023-01-29 20:53:10 +00:00
|
|
|
args = parse_args()
|
|
|
|
help_triggered = args.help or len(sys.argv) == 1
|
|
|
|
|
|
|
|
# If the user is asking for help, let them know that the whole download-and-build
|
|
|
|
# process has to happen before anything is printed out.
|
|
|
|
if help_triggered:
|
|
|
|
print(
|
|
|
|
"info: Downloading and building bootstrap before processing --help command.\n"
|
|
|
|
" See src/bootstrap/README.md for help with common commands."
|
2023-04-29 20:53:55 +00:00
|
|
|
, file=sys.stderr)
|
2023-01-29 20:53:10 +00:00
|
|
|
|
|
|
|
exit_code = 0
|
2023-02-08 18:19:23 +00:00
|
|
|
success_word = "successfully"
|
2017-03-03 02:27:07 +00:00
|
|
|
try:
|
2023-01-29 20:53:10 +00:00
|
|
|
bootstrap(args)
|
2017-07-03 04:32:42 +00:00
|
|
|
except (SystemExit, KeyboardInterrupt) as error:
|
|
|
|
if hasattr(error, 'code') and isinstance(error.code, int):
|
|
|
|
exit_code = error.code
|
2017-03-03 02:27:07 +00:00
|
|
|
else:
|
|
|
|
exit_code = 1
|
2023-04-29 20:53:55 +00:00
|
|
|
print(error, file=sys.stderr)
|
2023-02-08 18:19:23 +00:00
|
|
|
success_word = "unsuccessfully"
|
2023-01-29 20:53:10 +00:00
|
|
|
|
|
|
|
if not help_triggered:
|
2023-04-29 20:53:55 +00:00
|
|
|
print("Build completed", success_word, "in", format_build_time(time() - start_time),
|
|
|
|
file=sys.stderr)
|
2023-01-29 20:53:10 +00:00
|
|
|
sys.exit(exit_code)
|
2016-07-02 14:19:27 +00:00
|
|
|
|
2017-07-01 04:24:35 +00:00
|
|
|
|
2016-04-13 18:18:35 +00:00
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|