-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathpacketize.py
More file actions
157 lines (112 loc) · 4.9 KB
/
packetize.py
File metadata and controls
157 lines (112 loc) · 4.9 KB
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#!/usr/bin/env python3
"""pack.py
Build and stage Python distribution artefacts for a CMake-driven project.
Features
~~~~~~~~
* **--dry-run** prints each command instead of executing it.
* Automatically appends an appropriate ``--plat-name`` to ``bdist_wheel``:
* macOS → ``macosx_11_0_arm64`` (Apple Silicon)
* Windows → ``win_amd64``
* Other platforms keep the default produced by *wheel*.
Steps performed (unless ``--dry-run``):
1. Delete any previously staged ``dist`` folder from the *binary* directory.
2. Run ``setup.py sdist bdist_wheel [--plat-name=…]`` inside the source dir.
3. Copy the freshly-built wheels/SDists into the *binary* directory.
4. Remove ``dist`` and ``build`` left in the source tree.
Typical CMake invocation::
add_custom_target(
pip_package
COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/scripts/pack.py \
--source-dir ${CMAKE_SOURCE_DIR}/python \
--binary-dir ${CMAKE_CURRENT_BINARY_DIR}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
"""
from __future__ import annotations
import argparse
import platform
import shutil
import subprocess
import sys
from pathlib import Path
from typing import Iterable, List
# ---------------------------------------------------------------------------
# Utility helpers
# ---------------------------------------------------------------------------
def _echo(cmd: str) -> None: # noqa: D401
"""Uniformly print a shell-style command that will (or would) run."""
print("+", cmd, flush=True)
def _run(cmd: List[str], cwd: Path, dry: bool) -> None:
shell_line = " ".join(str(c) for c in cmd)
_echo(f"(cwd={cwd}) {shell_line}")
if not dry:
subprocess.check_call(cmd, cwd=cwd)
def _rm_rf(path: Path, dry: bool) -> None:
_echo(f"rm -rf {path}")
if not dry and path.exists():
shutil.rmtree(path, ignore_errors=True)
def _cp_r(src: Path, dst: Path, dry: bool) -> None:
_echo(f"cp -r {src} {dst}")
if not dry:
shutil.copytree(src, dst, dirs_exist_ok=True)
# ---------------------------------------------------------------------------
# Platform helper
# ---------------------------------------------------------------------------
def _plat_name_for_current_host() -> str | None:
"""Return the wheel *plat-name* tag for this host or ``None`` if default."""
sys_plat = sys.platform
if sys_plat == "darwin":
return "macosx_11_0_arm64" # assume Apple Silicon (arm64)
if sys_plat.startswith("win"):
return "win_amd64"
return None
# ---------------------------------------------------------------------------
# Core actions
# ---------------------------------------------------------------------------
def build_package(src_dir: Path, python: str, dry: bool) -> None:
"""Run ``setup.py sdist bdist_wheel`` in *src_dir* with proper plat-name."""
cmd: list[str] = [python, "setup.py", "sdist", "bdist_wheel"]
plat_name = _plat_name_for_current_host()
if plat_name:
cmd.append(f"--plat-name={plat_name}")
_run(cmd, cwd=src_dir, dry=dry)
def copy_dist(src_dir: Path, bin_dir: Path, dry: bool) -> None:
"""Copy the ``dist`` folder from *src_dir* to *bin_dir*."""
_cp_r(src_dir / "dist", bin_dir / "dist", dry=dry)
def clean_artifacts(paths: Iterable[Path], dry: bool) -> None:
for p in paths:
_rm_rf(p, dry=dry)
# ---------------------------------------------------------------------------
# CLI handling
# ---------------------------------------------------------------------------
def _parse_args(argv: list[str] | None = None) -> argparse.Namespace: # noqa: ANN001
p = argparse.ArgumentParser(description="Build & stage Python sdists/wheels for CMake")
p.add_argument("--source-dir", required=True, type=Path, help="Path containing setup.py")
p.add_argument("--binary-dir", required=True, type=Path, help="CMake binary dir (stage area)")
p.add_argument("--python", default=sys.executable, help="Python interpreter to run setup.py")
p.add_argument("--dry-run", action="store_true", help="Print commands only; do nothing")
return p.parse_args(argv)
def main(argv: list[str] | None = None) -> None: # noqa: ANN001
args = _parse_args(argv)
src_dir = args.source_dir.resolve()
bin_dir = args.binary_dir.resolve()
dry = bool(args.dry_run)
# 1. Clear out any previously staged artefacts
clean_artifacts([bin_dir / "dist"], dry)
# 2. Build new artefacts
build_package(src_dir, args.python, dry)
# 3. Stage them into the build tree
copy_dist(src_dir, bin_dir, dry)
# 4. Clean up temporary build folders inside the source tree
clean_artifacts([src_dir / "dist", src_dir / "build"], dry)
msg = (
"(dry run) Packaging steps simulated"
if dry
else f"Packaging complete → {bin_dir / 'dist'}"
)
print(msg)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
sys.exit(130)