-
Notifications
You must be signed in to change notification settings - Fork 38
/
Copy pathbuild.py
98 lines (76 loc) · 3.72 KB
/
build.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# Parsec Cloud (https://parsec.cloud) Copyright (c) AGPL-3.0 2016-present Scille SAS
import os
import pathlib
import subprocess
import sys
import tempfile
import zipfile
# The profile we pass to `make.py` to get the flags (cargo profile & features) for cargo build
DEFAULT_BUILD_PROFILE = "release"
def display(line: str):
YELLOW_FG = "\x1b[33m"
DEFAULT_FG = "\x1b[39m"
# Flush is required to prevent mixing with the output of sub-command with the output of the script
print(f"{YELLOW_FG}{line}{DEFAULT_FG}", flush=True)
BASEDIR = pathlib.Path(__file__).parent
PYTHON_EXECUTABLE_PATH = sys.executable
display(f"PYTHON_EXECUTABLE_PATH={PYTHON_EXECUTABLE_PATH}")
def run(cmd: str, **kwargs) -> subprocess.CompletedProcess:
display(f">>> {cmd}")
ret = subprocess.run(cmd, shell=True, check=True, **kwargs)
return ret
def build():
run(f"{PYTHON_EXECUTABLE_PATH} --version")
run(f"maturin --version")
if os.environ.get("POETRY_PYQT_BUILD_STRATEGY", "") != "no_build":
run(f"{PYTHON_EXECUTABLE_PATH} misc/generate_pyqt.py")
if sys.platform == "linux":
run("patchelf --version")
if sys.platform == "win32":
libparsec_path = "parsec/_parsec.cp39-win_amd64.pyd"
elif sys.platform == "darwin":
libparsec_path = "parsec/_parsec.cpython-39-darwin.so"
else:
libparsec_path = "parsec/_parsec.cpython-39-x86_64-linux-gnu.so"
build_strategy = (
os.environ.get("POETRY_LIBPARSEC_BUILD_STRATEGY", "always_build").strip().lower()
)
if build_strategy == "no_build":
display(f"Skipping maturin build: POETRY_LIBPARSEC_BUILD_STRATEGY set to `no_build`")
return
elif build_strategy == "build_if_missing" and (BASEDIR / libparsec_path).exists():
display(
f"Skipping maturin build: POETRY_LIBPARSEC_BUILD_STRATEGY set to `build_if_missing` and {libparsec_path} already exists"
)
return
# Retrieve the options to use for Cargo Compilation.
# The idea here is to have the per-profile options centralized at the same place
# (i.e. within `make.py`) to have a readable single source of truth (including
# when compiling bindings unrelated to Python) on this sensible piece of configuration.
build_profile = os.environ.get("POETRY_LIBPARSEC_BUILD_PROFILE", DEFAULT_BUILD_PROFILE)
ret = run(
f"{PYTHON_EXECUTABLE_PATH} make.py --quiet python-{build_profile}-libparsec-cargo-flags",
stdout=subprocess.PIPE,
)
cargo_flags = ret.stdout.decode("ascii").strip()
# Maturin provides two commands to compile the Rust code as a Python native module:
# - `maturin develop` that only compile the native module
# - `maturin build` that generates an entire wheel of the project
#
# Here in theory we'd like to use `maturin develop`, then leave poetry does the
# wheel generation. However the develop mode expects to be run from a virtualenv
# with pip available which makes it unreliable to call it from here.
# So we must ask maturin to build a wheel of the project, only to extract the
# native module and discard the rest !
with tempfile.TemporaryDirectory() as distdir:
run(f"maturin build {cargo_flags} --interpreter {PYTHON_EXECUTABLE_PATH} --out {distdir}")
outputs = list(pathlib.Path(distdir).iterdir())
if len(outputs) != 1:
raise RuntimeError(f"Target dir unexpectedly contains multiple files: {outputs}")
wheel_path = outputs[0]
display(f"extracting {wheel_path}:{libparsec_path} -> {BASEDIR / libparsec_path}")
with zipfile.ZipFile(wheel_path) as wheel:
wheel.extract(libparsec_path, path=BASEDIR)
if __name__ == "__main__":
display("Launching poetry build.py script")
build()