Skip to content

Commit 03598a2

Browse files
authored
Allow building CPython from a local source directory (#3)
* Allow building CPython from a local source directory * Allow `version` override in `ContainerContext.install_toolchain_archive` as well * Remove schedule?
1 parent e37e463 commit 03598a2

File tree

7 files changed

+96
-23
lines changed

7 files changed

+96
-23
lines changed

.github/workflows/linux.yml

-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ name: Linux Python build
33
on:
44
push:
55
pull_request:
6-
schedule:
7-
- cron: '13 11 * * *'
86
jobs:
97
pythonbuild:
108
runs-on: ubuntu-22.04

cpython-unix/Makefile

+5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ ifndef PYBUILD_HOST_PLATFORM
1717
$(error PYBUILD_HOST_PLATFORM not defined)
1818
endif
1919

20+
ifndef PYBUILD_PYTHON_SOURCE
21+
$(error PYBUILD_PYTHON_SOURCE not defined)
22+
endif
23+
2024
ifndef PYBUILD_PYTHON_VERSION
2125
$(error PYBUILD_PYTHON_VERSION not defined)
2226
endif
@@ -33,6 +37,7 @@ RUN_BUILD = $(BUILD) \
3337
--host-platform $(HOST_PLATFORM) \
3438
--target-triple $(TARGET_TRIPLE) \
3539
--optimizations $(PYBUILD_OPTIMIZATIONS) \
40+
--python-source $(PYBUILD_PYTHON_SOURCE) \
3641
--dest-archive $@ \
3742
$(NULL)
3843

cpython-unix/build-main.py

+23-4
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ def main():
7171
default="cpython-3.11",
7272
help="Python distribution to build",
7373
)
74+
parser.add_argument(
75+
"--python-source",
76+
default=None,
77+
help="A custom path to CPython source files to use",
78+
)
7479
parser.add_argument(
7580
"--break-on-failure",
7681
action="store_true",
@@ -118,31 +123,45 @@ def main():
118123
)
119124
return 1
120125

126+
python_source = (
127+
(str(pathlib.Path(args.python_source).resolve()))
128+
if args.python_source
129+
else "null"
130+
)
131+
121132
musl = "musl" in target_triple
122133

123134
env = dict(os.environ)
124135

125136
env["PYBUILD_HOST_PLATFORM"] = host_platform
126137
env["PYBUILD_TARGET_TRIPLE"] = target_triple
127138
env["PYBUILD_OPTIMIZATIONS"] = args.optimizations
139+
env["PYBUILD_PYTHON_SOURCE"] = python_source
128140
if musl:
129141
env["PYBUILD_MUSL"] = "1"
130142
if args.break_on_failure:
131143
env["PYBUILD_BREAK_ON_FAILURE"] = "1"
132144
if args.no_docker:
133145
env["PYBUILD_NO_DOCKER"] = "1"
134146

135-
entry = DOWNLOADS[args.python]
136-
env["PYBUILD_PYTHON_VERSION"] = entry["version"]
137-
env["PYBUILD_PYTHON_MAJOR_VERSION"] = ".".join(entry["version"].split(".")[0:2])
147+
if not args.python_source:
148+
entry = DOWNLOADS[args.python]
149+
env["PYBUILD_PYTHON_VERSION"] = cpython_version = entry["version"]
150+
else:
151+
if "PYBUILD_PYTHON_VERSION" not in env:
152+
print("PYBUILD_PYTHON_VERSION must be set when using `--python-source`")
153+
return 1
154+
cpython_version = env["PYBUILD_PYTHON_VERSION"]
155+
156+
env["PYBUILD_PYTHON_MAJOR_VERSION"] = ".".join(cpython_version.split(".")[0:2])
138157

139158
if "PYBUILD_RELEASE_TAG" in os.environ:
140159
release_tag = os.environ["PYBUILD_RELEASE_TAG"]
141160
else:
142161
release_tag = release_tag_from_git()
143162

144163
archive_components = [
145-
"cpython-%s" % entry["version"],
164+
"cpython-%s" % cpython_version,
146165
target_triple,
147166
args.optimizations,
148167
]

cpython-unix/build.py

+39-12
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,14 @@
3232
add_env_common,
3333
add_licenses_to_extension_entry,
3434
clang_toolchain,
35+
create_tar_from_directory,
3536
download_entry,
3637
get_targets,
3738
get_target_settings,
3839
target_needs,
3940
validate_python_json,
4041
write_package_versions,
42+
write_cpython_version,
4143
write_target_settings,
4244
write_triples_makefiles,
4345
)
@@ -62,8 +64,7 @@ def install_sccache(build_env):
6264
"""
6365
candidates = [
6466
# Prefer a binary in the project itself.
65-
ROOT
66-
/ "sccache",
67+
ROOT / "sccache",
6768
]
6869

6970
# Look for sccache in $PATH, but only if the build environment
@@ -255,10 +256,9 @@ def simple_build(
255256
build_env.copy_file(SUPPORT / ("build-%s.sh" % entry))
256257

257258
env = {
258-
"%s_VERSION"
259-
% entry.upper()
260-
.replace("-", "_")
261-
.replace(".", "_"): DOWNLOADS[entry]["version"],
259+
"%s_VERSION" % entry.upper().replace("-", "_").replace(".", "_"): DOWNLOADS[
260+
entry
261+
]["version"],
262262
}
263263

264264
add_target_env(env, host_platform, target_triple, build_env)
@@ -684,13 +684,23 @@ def build_cpython(
684684
optimizations,
685685
dest_archive,
686686
version=None,
687+
python_source=None,
687688
):
688689
"""Build CPython in a Docker image'"""
689690
entry_name = "cpython-%s" % version
690691
entry = DOWNLOADS[entry_name]
691-
python_version = entry["version"]
692+
if not python_source:
693+
python_version = entry["version"]
694+
python_archive = download_entry(entry_name, DOWNLOADS_PATH)
695+
else:
696+
python_version = os.environ["PYBUILD_PYTHON_VERSION"]
697+
python_archive = DOWNLOADS_PATH / ("Python-%s.tar.xz" % python_version)
698+
print("Compressing %s to %s" % (python_source, python_archive))
699+
with python_archive.open("wb") as fh:
700+
create_tar_from_directory(
701+
fh, python_source, path_prefix="Python-%s" % python_version
702+
)
692703

693-
python_archive = download_entry(entry_name, DOWNLOADS_PATH)
694704
setuptools_archive = download_entry("setuptools", DOWNLOADS_PATH)
695705
pip_archive = download_entry("pip", DOWNLOADS_PATH)
696706

@@ -726,7 +736,9 @@ def build_cpython(
726736
for p in sorted(packages):
727737
build_env.install_artifact_archive(BUILD, p, target_triple, optimizations)
728738

729-
build_env.install_toolchain_archive(BUILD, entry_name, host_platform)
739+
build_env.install_toolchain_archive(
740+
BUILD, entry_name, host_platform, version=python_version
741+
)
730742

731743
for p in (
732744
python_archive,
@@ -762,8 +774,8 @@ def build_cpython(
762774

763775
env = {
764776
"PIP_VERSION": DOWNLOADS["pip"]["version"],
765-
"PYTHON_VERSION": entry["version"],
766-
"PYTHON_MAJMIN_VERSION": ".".join(entry["version"].split(".")[0:2]),
777+
"PYTHON_VERSION": python_version,
778+
"PYTHON_MAJMIN_VERSION": ".".join(python_version.split(".")[0:2]),
767779
"SETUPTOOLS_VERSION": DOWNLOADS["setuptools"]["version"],
768780
"TOOLCHAIN": "clang-%s" % host_platform,
769781
}
@@ -824,7 +836,7 @@ def build_cpython(
824836
"target_triple": target_triple,
825837
"optimizations": optimizations,
826838
"python_tag": entry["python_tag"],
827-
"python_version": entry["version"],
839+
"python_version": python_version,
828840
"python_stdlib_test_packages": sorted(STDLIB_TEST_PACKAGES),
829841
"python_symbol_visibility": python_symbol_visibility,
830842
"python_extension_module_loading": extension_module_loading,
@@ -924,6 +936,11 @@ def main():
924936
"--dest-archive", required=True, help="Path to archive that we are producing"
925937
)
926938
parser.add_argument("--docker-image", help="Docker image to use for building")
939+
parser.add_argument(
940+
"--python-source",
941+
default=None,
942+
help="A custom path to CPython source files to use",
943+
)
927944
parser.add_argument("action")
928945

929946
args = parser.parse_args()
@@ -933,6 +950,9 @@ def main():
933950
target_triple = args.target_triple
934951
host_platform = args.host_platform
935952
optimizations = args.optimizations
953+
python_source = (
954+
pathlib.Path(args.python_source) if args.python_source != "null" else None
955+
)
936956
dest_archive = pathlib.Path(args.dest_archive)
937957
docker_image = args.docker_image
938958

@@ -969,6 +989,12 @@ def main():
969989
write_target_settings(targets, BUILD / "targets")
970990
write_package_versions(BUILD / "versions")
971991

992+
# Override the DOWNLOADS package entry for CPython for the local build
993+
if python_source:
994+
write_cpython_version(
995+
BUILD / "versions", os.environ["PYBUILD_PYTHON_VERSION"]
996+
)
997+
972998
elif action.startswith("image-"):
973999
image_name = action[6:]
9741000
image_path = BUILD / ("%s.Dockerfile" % image_name)
@@ -1179,6 +1205,7 @@ def main():
11791205
optimizations=optimizations,
11801206
dest_archive=dest_archive,
11811207
version=action.split("-")[1],
1208+
python_source=python_source,
11821209
)
11831210

11841211
else:

cpython-windows/build.py

+1
Original file line numberDiff line numberDiff line change
@@ -2656,6 +2656,7 @@ def main():
26562656
"cpython-3.10",
26572657
"cpython-3.11",
26582658
"cpython-3.12",
2659+
"cpython-3.13",
26592660
},
26602661
default="cpython-3.11",
26612662
help="Python distribution to build",

pythonbuild/buildenv.py

+16-4
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,15 @@ def copy_file(self, source: pathlib.Path, dest_path=None, dest_name=None):
3838
dest_path = dest_path or "/build"
3939
copy_file_to_container(source, self.container, dest_path, dest_name)
4040

41-
def install_toolchain_archive(self, build_dir, package_name, host_platform):
41+
def install_toolchain_archive(
42+
self, build_dir, package_name, host_platform, version=None
43+
):
4244
entry = DOWNLOADS[package_name]
43-
basename = "%s-%s-%s.tar" % (package_name, entry["version"], host_platform)
45+
basename = "%s-%s-%s.tar" % (
46+
package_name,
47+
version or entry["version"],
48+
host_platform,
49+
)
4450

4551
p = build_dir / basename
4652
self.copy_file(p)
@@ -152,9 +158,15 @@ def copy_file(self, source: pathlib.Path, dest_path=None, dest_name=None):
152158
log("copying %s to %s/%s" % (source, dest_dir, dest_name))
153159
shutil.copy(source, dest_dir / dest_name)
154160

155-
def install_toolchain_archive(self, build_dir, package_name, host_platform):
161+
def install_toolchain_archive(
162+
self, build_dir, package_name, host_platform, version=None
163+
):
156164
entry = DOWNLOADS[package_name]
157-
basename = "%s-%s-%s.tar" % (package_name, entry["version"], host_platform)
165+
basename = "%s-%s-%s.tar" % (
166+
package_name,
167+
version or entry["version"],
168+
host_platform,
169+
)
158170

159171
p = build_dir / basename
160172
dest_path = self.td / "tools"

pythonbuild/utils.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,17 @@ def write_package_versions(dest_path: pathlib.Path):
182182
write_if_different(p, content.encode("ascii"))
183183

184184

185+
def write_cpython_version(dest_path: pathlib.Path, version: str):
186+
"""Write a CPython version in a directory."""
187+
dest_path.mkdir(parents=True, exist_ok=True)
188+
189+
major_minor = ".".join(version.split(".")[:2])
190+
k = "cpython-%s" % major_minor
191+
p = dest_path / ("VERSION.%s" % k)
192+
content = "%s_VERSION := %s\n" % (k.upper().replace("-", "_"), version)
193+
write_if_different(p, content.encode("ascii"))
194+
195+
185196
def write_target_settings(targets, dest_path: pathlib.Path):
186197
dest_path.mkdir(parents=True, exist_ok=True)
187198

@@ -621,7 +632,7 @@ def release_download_statistics(mode="by_asset"):
621632
print("%d\t%s" % (count, build))
622633
elif mode == "by_tag":
623634
for tag, count in sorted(by_tag.items()):
624-
print("%d\t%s"% (count, tag))
635+
print("%d\t%s" % (count, tag))
625636
elif mode == "total":
626637
print("%d" % by_tag.total())
627638
else:

0 commit comments

Comments
 (0)