Skip to content

Commit d71cc3e

Browse files
authored
Merge branch 'main' into sync/20260310
2 parents 6f6aaa1 + 441077e commit d71cc3e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+7545
-274
lines changed

.gitattributes

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Normalize line endings for our patch files
2+
pystandalone/patch/*.patch text eol=lf
3+
pystandalone/patch/*-windows.patch text eol=crlf

.github/workflows/sync.yml

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
name: sync
2+
3+
on:
4+
schedule:
5+
- cron: '0 2 * * *' # daily at 02:00 UTC
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: write
10+
pull-requests: write
11+
12+
jobs:
13+
sync:
14+
runs-on: ubuntu-latest
15+
16+
env:
17+
GH_TOKEN: ${{ secrets.GH_PYSTANDALONE_SYNC_TOKEN }}
18+
GH_REPO: ${{ github.repository }}
19+
20+
steps:
21+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
22+
with:
23+
fetch-depth: 0
24+
token: ${{ secrets.GH_PYSTANDALONE_SYNC_TOKEN }}
25+
26+
- name: Configure Git
27+
run: |
28+
git config user.name "github-actions[bot]"
29+
git config user.email "github-actions[bot]@users.noreply.github.com"
30+
31+
- name: Fetch upstream tags
32+
run: |
33+
git remote add upstream https://github.com/astral-sh/python-build-standalone.git
34+
git fetch upstream --tags --no-recurse-submodules
35+
36+
- name: Detect latest upstream tag
37+
id: detect
38+
run: |
39+
# Upstream uses date-format tags (e.g. 20260310); version:refname sorts these correctly.
40+
UPSTREAM_TAG=$(git tag -l --sort=-version:refname | head -n 1)
41+
# Latest tag already merged into our main - the last sync point.
42+
LOCAL_TAG=$(git tag -l --sort=-version:refname --merged HEAD | head -n 1)
43+
44+
echo "upstream=${UPSTREAM_TAG}" >> "$GITHUB_OUTPUT"
45+
echo "local=${LOCAL_TAG}" >> "$GITHUB_OUTPUT"
46+
echo "Upstream: ${UPSTREAM_TAG} Local: ${LOCAL_TAG}"
47+
48+
if [ -z "$UPSTREAM_TAG" ]; then
49+
echo "::error::No upstream tags found."
50+
exit 1
51+
fi
52+
53+
# When multiple upstream tags have landed since our last sync, we target only the latest one.
54+
if [ "$UPSTREAM_TAG" = "$LOCAL_TAG" ]; then
55+
echo "needs_sync=false" >> "$GITHUB_OUTPUT"
56+
echo "Already up-to-date."
57+
else
58+
echo "needs_sync=true" >> "$GITHUB_OUTPUT"
59+
fi
60+
61+
- name: Check for existing PR
62+
id: pr_check
63+
if: steps.detect.outputs.needs_sync == 'true'
64+
run: |
65+
BRANCH="sync/${{ steps.detect.outputs.upstream }}"
66+
# Check open *and* closed/merged PRs so we don't re-open for the same tag.
67+
PR_NUMBER=$(gh pr list --head "$BRANCH" --state all --json number --jq '.[0].number // ""')
68+
if [ -n "$PR_NUMBER" ]; then
69+
echo "PR #${PR_NUMBER} already exists for ${BRANCH} — skipping."
70+
echo "pr_exists=true" >> "$GITHUB_OUTPUT"
71+
else
72+
echo "pr_exists=false" >> "$GITHUB_OUTPUT"
73+
fi
74+
75+
- name: Create sync branch and merge upstream tag
76+
id: merge
77+
if: steps.detect.outputs.needs_sync == 'true' && steps.pr_check.outputs.pr_exists == 'false'
78+
run: |
79+
TAG="${{ steps.detect.outputs.upstream }}"
80+
BRANCH="sync/${TAG}"
81+
82+
# Push the upstream tag commit directly as a branch.
83+
git push origin "refs/tags/${TAG}:refs/heads/${BRANCH}"
84+
echo "branch=${BRANCH}" >> "$GITHUB_OUTPUT"
85+
86+
- name: Ensure sync label exists
87+
if: steps.detect.outputs.needs_sync == 'true' && steps.pr_check.outputs.pr_exists == 'false'
88+
run: |
89+
gh label create "sync" \
90+
--color "0075ca" \
91+
--description "Automated upstream sync" \
92+
|| true
93+
94+
- name: Open pull request
95+
if: steps.detect.outputs.needs_sync == 'true' && steps.pr_check.outputs.pr_exists == 'false'
96+
run: |
97+
TAG="${{ steps.detect.outputs.upstream }}"
98+
DATE=$(date -u +'%Y-%m-%d')
99+
BRANCH="${{ steps.merge.outputs.branch }}"
100+
101+
gh pr create \
102+
--title "Sync upstream tag ${TAG}" \
103+
--body "Automated sync triggered by upstream tag **${TAG}** (${DATE})." \
104+
--head "${BRANCH}" \
105+
--base main \
106+
--label "sync"

Cargo.lock

Lines changed: 29 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,6 @@ tokio = "1.48.0"
4242
tokio-util = "0.7.17"
4343
url = "2.5.7"
4444
version-compare = "0.2.1"
45+
walkdir = "2.5.0"
4546
zip = "6.0.0"
4647
zstd = "0.13.3"

build-linux.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
PYTHON = VENV / "bin" / "python"
1818
REQUIREMENTS = ROOT / "requirements.txt"
1919
MAKE_DIR = ROOT / "cpython-unix"
20+
PYSTANDALONE_DIR = ROOT / "pystandalone"
2021

2122

2223
def bootstrap():
@@ -41,7 +42,7 @@ def run():
4142

4243
args = [
4344
str(PYTHON),
44-
"build-main.py",
45+
str(PYSTANDALONE_DIR / "build.py"),
4546
*sys.argv[1:],
4647
]
4748

build-macos.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
PYTHON = VENV / "bin" / "python"
1818
REQUIREMENTS = ROOT / "requirements.txt"
1919
MAKE_DIR = ROOT / "cpython-unix"
20+
PYSTANDALONE_DIR = ROOT / "pystandalone"
2021

2122

2223
def bootstrap():
@@ -41,7 +42,7 @@ def run():
4142

4243
args = [
4344
str(PYTHON),
44-
"build-main.py",
45+
str(PYSTANDALONE_DIR / "build.py"),
4546
*sys.argv[1:],
4647
]
4748

build-windows.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
ARCH = "-arm64" if platform.machine() == "ARM64" else ""
2020
REQUIREMENTS = ROOT / f"requirements.win{ARCH}.txt"
2121
WINDOWS_DIR = ROOT / "cpython-windows"
22+
PYSTANDALONE_DIR = ROOT / "pystandalone"
2223

2324

2425
def bootstrap():
@@ -41,7 +42,7 @@ def run():
4142
env = dict(os.environ)
4243
env["PYTHONUNBUFFERED"] = "1"
4344

44-
args = [str(PYTHON), "build.py"]
45+
args = [str(PYTHON), str(PYSTANDALONE_DIR / "build.py")]
4546
args.extend(sys.argv[1:])
4647

4748
subprocess.run(args, cwd=str(WINDOWS_DIR), env=env, check=True, bufsize=0)

cpython-unix/build-cpython.sh

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ cat Makefile.extra
5858

5959
pushd "Python-${PYTHON_VERSION}"
6060

61+
# PYSTANDALONE: extract pystandalone tar, copy source and apply patches
62+
tar -xf ${ROOT}/pystandalone.tar -C ${ROOT}
63+
cp -a ${ROOT}/pystandalone/src/. .
64+
for file in ${ROOT}/pystandalone/patch/*.patch; do
65+
patch -p1 -i $file
66+
done
67+
6168
# configure doesn't support cross-compiling on Apple. Teach it.
6269
if [[ "${PYBUILD_PLATFORM}" = macos* && -n "${PYTHON_MEETS_MAXIMUM_VERSION_3_13}" ]]; then
6370
if [ "${PYTHON_MAJMIN_VERSION}" = "3.12" ]; then
@@ -151,15 +158,7 @@ fi
151158
# linked modules. But those libraries should only get linked into libpython, not the
152159
# executable. This behavior is kinda suspect on all platforms, as it could be adding
153160
# library dependencies that shouldn't need to be there.
154-
if [[ "${PYBUILD_PLATFORM}" = macos* ]]; then
155-
if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_15}" ]; then
156-
patch -p1 -i "${ROOT}/patch-python-link-modules-3.15.patch"
157-
elif [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_11}" ]; then
158-
patch -p1 -i "${ROOT}/patch-python-link-modules-3.11.patch"
159-
elif [ "${PYTHON_MAJMIN_VERSION}" = "3.10" ]; then
160-
patch -p1 -i "${ROOT}/patch-python-link-modules-3.10.patch"
161-
fi
162-
fi
161+
# PYSTANDALONE: skip this patch.
163162

164163
# The macOS code for sniffing for _dyld_shared_cache_contains_path falls back on a
165164
# possibly inappropriate code path if a configure time check fails. This is not
@@ -342,7 +341,8 @@ if [[ "${PYBUILD_PLATFORM}" != macos* ]]; then
342341
LDFLAGS="${LDFLAGS} -Wl,--exclude-libs,ALL"
343342
fi
344343

345-
EXTRA_CONFIGURE_FLAGS=
344+
# PYSTANDALONE: additional configure flags
345+
EXTRA_CONFIGURE_FLAGS="--without-doc-strings"
346346

347347
if [[ "${PYBUILD_PLATFORM}" = macos* ]]; then
348348
CFLAGS="${CFLAGS} -I${TOOLS_PATH}/deps/include/uuid"
@@ -389,6 +389,15 @@ if [[ "${PYBUILD_PLATFORM}" = macos* ]]; then
389389
LDFLAGS="${LDFLAGS} -Wl,-headerpad,40"
390390
fi
391391

392+
# PYSTANDALONE: move the static mapping to a different base offset that's rare.
393+
# This is useful for when we need to manually map an ELF, we need to avoid
394+
# address conflicts with the executable we're mapping from.
395+
# Also add a custom loading script to the default script to move our
396+
# our payload section to the end of the binary.
397+
if [[ "${PYBUILD_PLATFORM}" != macos* ]]; then
398+
LDFLAGS="${LDFLAGS} -Wl,-Ttext-segment=0x1000000,-Tpystandalone.ld"
399+
fi
400+
392401
CPPFLAGS=$CFLAGS
393402

394403
CONFIGURE_FLAGS="
@@ -454,12 +463,17 @@ if [ -n "${CPYTHON_STATIC}" ]; then
454463
CFLAGS="${CFLAGS} -static"
455464
CPPFLAGS="${CPPFLAGS} -static"
456465
LDFLAGS="${LDFLAGS} -static"
457-
PYBUILD_SHARED=0
466+
PYBUILD_SHARED=0
458467
else
459468
CONFIGURE_FLAGS="${CONFIGURE_FLAGS} --enable-shared"
460469
PYBUILD_SHARED=1
461470
fi
462471

472+
if [[ "${PYBUILD_PLATFORM}" = macos* ]]; then
473+
# PYSTANDALONE: we can't build completely statically on macOS, but at least disable shared building
474+
PYBUILD_SHARED=0
475+
fi
476+
463477
if [ -n "${CPYTHON_DEBUG}" ]; then
464478
CONFIGURE_FLAGS="${CONFIGURE_FLAGS} --with-pydebug"
465479
fi
@@ -716,6 +730,25 @@ CFLAGS=$CFLAGS CPPFLAGS=$CFLAGS CFLAGS_JIT=$CFLAGS_JIT LDFLAGS=$LDFLAGS \
716730
# Supplement produced Makefile with our modifications.
717731
cat ../Makefile.extra >> Makefile
718732

733+
# PYSTANDALONE: do a silly dance to temporarily disable the _pystandalone module to avoid compilation issues during regen
734+
sed -E "${sed_args[@]}" 's/^(_pystandalone .+)/#\1/g' Modules/Setup.local
735+
rm Modules/config.c
736+
make -j "${NUM_CPUS}" Modules/config.c
737+
cat ../Makefile.extra >> Makefile
738+
739+
# PYSTANDALONE: regenerate files (clinic, global objects, importlib because of changes to zipimport.py, etc.)
740+
make -j "${NUM_CPUS}" clinic
741+
if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_11}" ]; then
742+
make -j ${NUM_CPUS} regen-global-objects
743+
fi
744+
make -j "${NUM_CPUS}" regen-importlib
745+
746+
# PYSTANDALONE: re-enable the _pystandalone module
747+
sed -E "${sed_args[@]}" 's/^#(_pystandalone .+)/\1/g' Modules/Setup.local
748+
rm Modules/config.c
749+
make -j "${NUM_CPUS}" Modules/config.c
750+
cat ../Makefile.extra >> Makefile
751+
719752
make -j "${NUM_CPUS}"
720753
make -j "${NUM_CPUS}" sharedinstall DESTDIR="${ROOT}/out/python"
721754
make -j "${NUM_CPUS}" install DESTDIR="${ROOT}/out/python"

cpython-unix/build-musl.sh

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ index b54a051f..194c2033 100644
3030
#define WIFCONTINUED(s) ((s) == 0xffff)
3131
-void *reallocarray (void *, size_t, size_t);
3232
#endif
33-
33+
3434
#ifdef _GNU_SOURCE
3535
diff --git a/src/malloc/reallocarray.c b/src/malloc/reallocarray.c
3636
deleted file mode 100644
@@ -66,7 +66,7 @@ index b507ca3..8259e27 100644
6666
-void *reallocarray (void *, size_t, size_t);
6767
void qsort_r (void *, size_t, size_t, int (*)(const void *, const void *, void *), void *);
6868
#endif
69-
69+
7070
diff --git a/src/malloc/reallocarray.c b/src/malloc/reallocarray.c
7171
deleted file mode 100644
7272
index 4a6ebe4..0000000
@@ -89,6 +89,37 @@ index 4a6ebe4..0000000
8989
EOF
9090
fi
9191

92+
# PYSTANDALONE: change fork call to always use clone
93+
patch -p1 <<EOF
94+
diff --git a/src/process/_Fork.c b/src/process/_Fork.c
95+
index da06386..10691fa 100644
96+
--- a/src/process/_Fork.c
97+
+++ b/src/process/_Fork.c
98+
@@ -6,6 +6,9 @@
99+
#include "pthread_impl.h"
100+
#include "aio_impl.h"
101+
102+
+#define CLONE_CHILD_CLEARTID 0x00200000
103+
+#define CLONE_CHILD_SETTID 0x01000000
104+
+
105+
static void dummy(int x) { }
106+
weak_alias(dummy, __aio_atfork);
107+
108+
@@ -16,11 +16,7 @@ pid_t _Fork(void)
109+
__block_all_sigs(&set);
110+
__aio_atfork(-1);
111+
LOCK(__abort_lock);
112+
-#ifdef SYS_fork
113+
- ret = __syscall(SYS_fork);
114+
-#else
115+
- ret = __syscall(SYS_clone, SIGCHLD, 0);
116+
-#endif
117+
+ ret = __syscall(SYS_clone, CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, 0, NULL, &__pthread_self()->tid);
118+
if (!ret) {
119+
pthread_t self = __pthread_self();
120+
self->tid = __syscall(SYS_gettid);
121+
EOF
122+
92123
SHARED=
93124
if [ -n "${STATIC}" ]; then
94125
SHARED="--disable-shared"

0 commit comments

Comments
 (0)