Skip to content

Commit 71e09cb

Browse files
authored
Merge pull request #2080 from stonebig/master
one builds toml for every python
2 parents 60326b9 + 35bd8ac commit 71e09cb

6 files changed

Lines changed: 168 additions & 208 deletions

build_winpython_meta.py

Lines changed: 71 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,40 +6,88 @@
66
import subprocess
77
from pathlib import Path
88

9+
# Flavor paths are names, not full paths: these say what they hang from.
10+
UNDER_BASEDIR = ("requirements", "source_dirs", "wheelhousereq")
11+
UNDER_ROOT = ("toolsdirs",)
12+
913
def load_builds(config_file):
1014
with open(config_file, "rb") as f:
1115
config = tomllib.load(f)
12-
builds = config["builds"]
1316
python_versions = config.get("pythons", {})
17+
# A file may still spell out one [[builds]] block per build; otherwise the
18+
# builds are the (python, flavor) pairs [pythons] asks for.
19+
builds = config["builds"] if "builds" in config else expand_builds(config)
1420
return builds, python_versions
1521

16-
def declared_file(build, key, default):
17-
"""Path named by the build for `key`, else `default`.
22+
def expand_builds(config):
23+
"""One build per flavor listed by each [pythons."3XX"], paths derived.
1824
19-
A path a build spells out must exist: pip_install() skips a missing
20-
requirements file without failing, which would drop the packages silently.
25+
A flavor names its files relative to the build directory, so a new Python
26+
minor is one [pythons] block. Where a Python needs its own file - a
27+
requirements list under another name, say - [pythons."3XX".overrides.flavor]
28+
replaces that flavor's entries for that Python alone.
2129
"""
30+
defaults = config.get("defaults", {})
31+
flavors = config.get("flavors", {})
32+
builds = []
33+
for target, vinfo in config.get("pythons", {}).items():
34+
root = Path(vinfo.get("root_dir_for_builds", defaults.get("root_dir_for_builds", "")))
35+
basedir = root / f"bd{target}"
36+
overrides = vinfo.get("overrides", {})
37+
for flavor in vinfo.get("builds", []):
38+
if flavor not in flavors:
39+
raise KeyError(f"python {target} builds {flavor!r}, which has no [flavors.{flavor}]")
40+
build = {**defaults, "name": flavor, "python_target": target, "flavor": flavor}
41+
for key, value in {**flavors[flavor], **overrides.get(flavor, {})}.items():
42+
if key in UNDER_BASEDIR:
43+
value = str(basedir / value)
44+
elif key in UNDER_ROOT:
45+
value = str(root / value)
46+
build[key] = value
47+
builds.append(build)
48+
return builds
49+
50+
def select_builds(builds, wanted):
51+
"""Builds asked for on the command line: "315", "315:slim", ":slim"."""
52+
if not wanted:
53+
return builds
54+
kept = []
55+
for spec in wanted:
56+
target, _, flavor = spec.partition(":")
57+
matching = [b for b in builds
58+
if (not target or b["python_target"] == target)
59+
and (not flavor or b["flavor"] == flavor)]
60+
if not matching:
61+
raise SystemExit(f"no build matches {spec!r}")
62+
kept += [b for b in matching if b not in kept]
63+
return kept
64+
65+
def must_exist(path, build, key):
66+
"""pip_install() skips a missing requirements file without failing, which
67+
would drop those packages silently. Say so instead."""
68+
if path and not Path(path).exists():
69+
raise FileNotFoundError(f"build {build['name']!r} needs {key} = {path!r}, which does not exist")
70+
return path
71+
72+
def declared_file(build, key, default):
73+
"""Path named by the build for `key`, else `default`."""
2274
declared = build.get(key)
23-
if declared is None:
24-
return str(default)
25-
if not Path(declared).exists():
26-
raise FileNotFoundError(f"build {build['name']!r} sets {key} = {declared!r}, which does not exist")
27-
return str(declared)
28-
29-
def run_build(build, python_versions):
30-
print(f"\n=== Building WinPython: {build['name']} ===")
75+
return str(default) if declared is None else must_exist(str(declared), build, key)
76+
77+
def run_build(build, python_versions, dry_run=False):
78+
print(f"\n=== Building WinPython: {build['python_target']} {build['name']} ===")
3179
print(build)
3280

3381
root_dir_for_builds = build["root_dir_for_builds"]
3482
my_python_target = build["python_target"]
3583
my_flavor = build["flavor"]
3684
my_arch = str(build["arch"])
3785
my_create_installer = build.get("create_installer", "True")
38-
my_requirements = build.get("requirements", "")
86+
my_requirements = must_exist(build.get("requirements", ""), build, "requirements")
3987
my_source_dirs = build.get("source_dirs", "")
4088
my_find_links = build.get("find_links", "")
4189
my_toolsdirs = build.get("toolsdirs", "")
42-
wheelhousereq = build.get("wheelhousereq", "")
90+
wheelhousereq = must_exist(build.get("wheelhousereq", ""), build, "wheelhousereq")
4391
# "pip" (default, what ships) | "none" | "parallel" | "parallel-N"
4492
my_bytecode = build.get("bytecode", "pip")
4593

@@ -89,15 +137,18 @@ def run_build(build, python_versions):
89137
"--create-installer", my_create_installer,
90138
]
91139

92-
print("Running build command:")
140+
print("Dry run, build command:" if dry_run else "Running build command:")
93141
print(" ".join(build_cmd))
94-
subprocess.run(build_cmd, cwd=os.getcwd(), check=False)
142+
if not dry_run:
143+
subprocess.run(build_cmd, check=False)
95144

96145
def main():
97-
config_file = sys.argv[1] if len(sys.argv) > 1 else "winpython_buildsNOT.toml"
146+
args = [a for a in sys.argv[1:] if a != "--dry-run"]
147+
dry_run = len(args) != len(sys.argv) - 1
148+
config_file = args[0] if args else "winpython_builds.toml"
98149
builds, python_versions = load_builds(config_file)
99-
for build in builds:
100-
run_build(build, python_versions)
150+
for build in select_builds(builds, args[1:]):
151+
run_build(build, python_versions, dry_run)
101152

102153
if __name__ == "__main__":
103154
main()
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
cd /D %~dp0
2-
call "C:\WinPdev\WPy64-31190\python-3.11.9.amd64\python.exe" %~dp0\build_winpython_meta.py %1
2+
rem <toml> [315 | 315:slim ...] [--dry-run]
3+
call "C:\WinPdev\WPy64-31190\python-3.11.9.amd64\python.exe" %~dp0\build_winpython_meta.py %*

winpython_builds.toml

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# WinPython local builds - every Python minor in one file.
2+
#
3+
# A build is a (python, flavor) pair. Everything else is derived:
4+
#
5+
# basedir = {root_dir_for_builds}\bd{python}
6+
# requirements = {basedir}\{flavor requirements}
7+
# source_dirs = {basedir}\{flavor source_dirs}
8+
# wheelhousereq = {basedir}\{flavor wheelhousereq}
9+
# toolsdirs = {root_dir_for_builds}\{flavor toolsdirs}
10+
#
11+
# so a new Python minor is one [pythons."3XX"] block, and its flavors are the
12+
# names listed in "builds".
13+
#
14+
# Requirement files may differ per Python: name them again in
15+
# [pythons."3XX".overrides.<flavor>], which replaces that flavor's entries for
16+
# that Python alone. Any other build key goes there too (bytecode, ...).
17+
#
18+
# Typical local build:
19+
# generate_a_winpython_distropy_meta.bat winpython_builds.toml 315:slim
20+
# generate_a_winpython_distropy_meta.bat winpython_builds.toml 315
21+
# generate_a_winpython_distropy_meta.bat winpython_builds.toml
22+
# ... --dry-run prints the build commands instead of running them
23+
24+
[defaults]
25+
root_dir_for_builds = "C:\\Winp"
26+
find_links = "C:\\Winp\\packages.srcreq"
27+
arch = "64"
28+
create_installer = "None"
29+
# mandatory_requirements.txt and constraints.txt come from beside this file
30+
# unless a build names its own.
31+
32+
# ---- flavors: what makes a slim a slim --------------------------------------
33+
34+
[flavors.dot]
35+
requirements = "dot_requirements.txt"
36+
source_dirs = "packages.win-amd64"
37+
toolsdirs = "bdTools\\Tools.dot"
38+
39+
[flavors.dotf]
40+
requirements = "dot_requirements.txt"
41+
source_dirs = "packages.win-amd64t"
42+
toolsdirs = "bdTools\\Tools.dot"
43+
44+
[flavors.slim]
45+
requirements = "requirements_slim.txt"
46+
source_dirs = "packages.win-amd64"
47+
toolsdirs = "bdTools\\tools64_pandoc_alone"
48+
49+
[flavors.slimf]
50+
requirements = "requirements_slimf.txt"
51+
source_dirs = "packages.win-amd64t"
52+
toolsdirs = "bdTools\\tools64_pandoc_alone"
53+
54+
# Not built at the moment: no python below lists it.
55+
[flavors.whl]
56+
requirements = "dot_requirements.txt"
57+
source_dirs = "packages.win-amd64"
58+
toolsdirs = "bdTools\\Tools.dot"
59+
wheelhousereq = "requirements_whl.txt"
60+
61+
# ---- pythons: what gets built -----------------------------------------------
62+
63+
[pythons."313"]
64+
python_target_release = "31315"
65+
release = "0"
66+
my_release_level = "b3"
67+
builds = ["dot", "slim"]
68+
69+
# A Python that names a requirements file its own way says so here:
70+
# [pythons."313".overrides.slim]
71+
# requirements = "requirements64_slim.txt"
72+
73+
[pythons."314"]
74+
python_target_release = "3147"
75+
release = "0"
76+
my_release_level = "b3"
77+
builds = ["dot", "dotf", "slim", "slimf"]
78+
79+
[pythons."314".overrides.slimf]
80+
bytecode = "none"
81+
82+
# [pythons."314".overrides.slim]
83+
# create_installer = ".7z-mx7"
84+
85+
[pythons."315"]
86+
python_target_release = "3150"
87+
release = "4"
88+
my_release_level = "b3"
89+
builds = ["dot", "dotf", "slim", "slimf"]
90+
91+
# [pythons."315".overrides.slim]
92+
# bytecode = "none"
93+
94+
# [pythons."315".overrides.slimf]
95+
# bytecode = "none"

winpython_builds_bd13.toml

Lines changed: 0 additions & 45 deletions
This file was deleted.

winpython_builds_bd14.toml

Lines changed: 0 additions & 78 deletions
This file was deleted.

0 commit comments

Comments
 (0)