Skip to content

Commit 28d3573

Browse files
Generating platform-specific wheels (#64)
* Trying cibuildwheel * Fix requires section * Remove is-purelib = false * Try building wheels with Python * Remove manylinux2010 * is-purelib = false * Update build-wheels.py * Try macosx_10_14_arm64 * Try cp38-cp38-macosx_10_14_arm64 * Try py3-none-macosx_12_0_arm64 * pip3 debug -v * Try ubuntu1604 * Disable pip3 debug * Embed pglet server version into constants * Run on ubuntu 18.04 and python 3.7 * Re-enable tests * Test multi-job build
1 parent 39aa105 commit 28d3573

5 files changed

Lines changed: 312 additions & 93 deletions

File tree

appveyor.yml

Lines changed: 103 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -5,86 +5,112 @@ skip_branch_with_pr: true
55

66
image: ubuntu
77

8-
stack: python 3.9
9-
108
environment:
11-
PGLET_VERSION: 0.5.6
12-
TWINE_USERNAME: __token__
13-
TWINE_NON_INTERACTIVE: true
14-
pypi_key:
15-
secure: bPSgMbRTG8EIrSW8RUbb8go0xbmg4/E4NnYfGl8h+L+KlWEk5mHLojLG/b6S6nVSCRXEHU/Vc0KjxLO+6mDrC3OT13kwv3fRRT90JNkOIlxePeoEhRFnh3u9nxlxeWncYBayef3U/zdxTTBd4YXqMPZ22qvCqeIRcOi/fzTRrfZ84Wr5V/JS7Jpmzg2yc2B/kzUBiQlfvflY7YUw/QFaiPhYytIglE7XY+r04JwNfgA=
16-
test_pypi_key:
17-
secure: LAsjCb0tqzjUEPxHTq8TPmTDzERQpSS0po96HhXEEczG8EaWqCUdtb+bhT3QLihy+4X4MvErEYXYieUT+KjpuVdgjc/UrqDEuQngBL9r7k5cRMV878c232zbLGljHckNr4TCzHJosANrYBrx0sNg9gVnAoJOxNbu40A1rVB8Qj9ob/eCKwXHHRxfrf0d/NwoLZViCE4toV333tVr5vshlLkQA3oRiS2OZG3VKagTI4nsKCaOFqJHFlvy2a0LEUdc
18-
19-
install:
20-
- python --version
21-
- pip install --upgrade setuptools wheel twine pdm
22-
- pdm install
23-
- echo Using Pglet ${PGLET_VERSION}
24-
25-
# downloading binaries
26-
- sh: |
27-
function download_binary() {
28-
echo "Downloading $1"
29-
mkdir -p pglet/bin/$1
30-
curl -L https://github.com/pglet/pglet/releases/download/v${PGLET_VERSION}/pglet-${PGLET_VERSION}-$1 -o pglet/bin/$1/pglet
31-
chmod a+x pglet/bin/$1/pglet
32-
}
33-
34-
echo "Downloading windows-amd64"
35-
mkdir -p pglet/bin/windows-amd64
36-
curl -L https://github.com/pglet/pglet/releases/download/v${PGLET_VERSION}/pglet-${PGLET_VERSION}-windows-amd64.exe -o pglet/bin/windows-amd64/pglet.exe
37-
38-
download_binary "linux-amd64"
39-
download_binary "linux-arm64"
40-
download_binary "linux-arm"
41-
download_binary "darwin-amd64"
42-
download_binary "darwin-arm64"
43-
44-
build_script:
45-
# run tests
46-
#- pytest
47-
- ls -alR pglet/bin
48-
49-
# build and publish package
50-
- ps: |
51-
$ErrorActionPreference = "Stop"
52-
53-
if ($env:APPVEYOR_REPO_TAG -eq 'true') {
54-
# release mode
55-
56-
# version
57-
$ver = $env:APPVEYOR_REPO_TAG_NAME
58-
if ($ver.StartsWith('v')) { $ver = $ver.Substring(1) }
59-
60-
# prerelease moniker
61-
$idx = $ver.indexOf('-')
62-
if ($idx -ne -1) {
63-
$prerelease = $ver.Substring($idx + 1)
64-
$ver = $ver.Substring(0, $idx)
9+
matrix:
10+
- job_name: Test Python 3.7
11+
job_group: tests
12+
python_version: 3.7
13+
14+
- job_name: Test Python 3.8
15+
job_group: tests
16+
python_version: 3.8
17+
18+
- job_name: Test Python 3.9
19+
job_group: tests
20+
python_version: 3.9
21+
22+
- job_name: Test Python 3.10
23+
job_group: tests
24+
python_version: 3.10
25+
26+
- job_name: Build package
27+
job_group: build
28+
job_depends_on: tests
29+
python_version: 3.10
30+
TWINE_USERNAME: __token__
31+
TWINE_NON_INTERACTIVE: true
32+
pypi_key:
33+
secure: bPSgMbRTG8EIrSW8RUbb8go0xbmg4/E4NnYfGl8h+L+KlWEk5mHLojLG/b6S6nVSCRXEHU/Vc0KjxLO+6mDrC3OT13kwv3fRRT90JNkOIlxePeoEhRFnh3u9nxlxeWncYBayef3U/zdxTTBd4YXqMPZ22qvCqeIRcOi/fzTRrfZ84Wr5V/JS7Jpmzg2yc2B/kzUBiQlfvflY7YUw/QFaiPhYytIglE7XY+r04JwNfgA=
34+
test_pypi_key:
35+
secure: LAsjCb0tqzjUEPxHTq8TPmTDzERQpSS0po96HhXEEczG8EaWqCUdtb+bhT3QLihy+4X4MvErEYXYieUT+KjpuVdgjc/UrqDEuQngBL9r7k5cRMV878c232zbLGljHckNr4TCzHJosANrYBrx0sNg9gVnAoJOxNbu40A1rVB8Qj9ob/eCKwXHHRxfrf0d/NwoLZViCE4toV333tVr5vshlLkQA3oRiS2OZG3VKagTI4nsKCaOFqJHFlvy2a0LEUdc
36+
37+
38+
stack: python $python_version
39+
40+
for:
41+
42+
###############
43+
# Tests #
44+
###############
45+
-
46+
matrix:
47+
only:
48+
- job_group: tests
49+
50+
install:
51+
- python --version
52+
- pip install pdm
53+
- pdm install
54+
55+
build: off
56+
57+
test_script:
58+
- pdm run pytest tests
59+
60+
###############
61+
# Build #
62+
###############
63+
-
64+
matrix:
65+
only:
66+
- job_group: build
67+
68+
install:
69+
- python --version
70+
- pip install --upgrade setuptools wheel twine pdm
71+
- pdm install
72+
73+
test: off
74+
75+
build_script:
76+
- ps: |
77+
$ErrorActionPreference = "Stop"
78+
79+
if ($env:APPVEYOR_REPO_TAG -eq 'true') {
80+
# release mode
81+
82+
# version
83+
$ver = $env:APPVEYOR_REPO_TAG_NAME
84+
if ($ver.StartsWith('v')) { $ver = $ver.Substring(1) }
85+
86+
# prerelease moniker
87+
$idx = $ver.indexOf('-')
88+
if ($idx -ne -1) {
89+
$prerelease = $ver.Substring($idx + 1)
90+
$ver = $ver.Substring(0, $idx)
91+
}
92+
$env:TWINE_PASSWORD = $env:pypi_key
93+
} else {
94+
95+
# build mode
96+
$ver = $env:APPVEYOR_BUILD_VERSION
97+
$env:TWINE_PASSWORD = $env:test_pypi_key
98+
$env:TWINE_REPOSITORY = 'testpypi'
6599
}
66-
$env:TWINE_PASSWORD = $env:pypi_key
67-
} else {
68-
69-
# build mode
70-
$ver = $env:APPVEYOR_BUILD_VERSION
71-
$env:TWINE_PASSWORD = $env:test_pypi_key
72-
$env:TWINE_REPOSITORY = 'testpypi'
73-
}
74-
75-
# patch version
76-
(Get-Content pyproject.toml).replace("version = `"0.1.0`"", "version = `"$ver`"") | Set-Content pyproject.toml
77100
78-
# build package
79-
- pdm build
101+
# patch version
102+
$env:PACKAGE_VERSION = $ver
103+
(Get-Content pyproject.toml).replace("version = `"0.1.0`"", "version = `"$ver`"") | Set-Content pyproject.toml
80104
81-
# publish package
82-
- sh: |
83-
if [[ "$APPVEYOR_PULL_REQUEST_NUMBER" == "" ]]; then
84-
twine upload dist/*
85-
fi
105+
# build package
106+
- pdm build
107+
- python3 build-wheels.py
86108

87-
test: off
109+
# publish package
110+
- sh: |
111+
if [[ "$APPVEYOR_PULL_REQUEST_NUMBER" == "" ]]; then
112+
twine upload dist/*
113+
fi
88114
89-
artifacts:
90-
path: dist/*
115+
artifacts:
116+
path: dist/*

build-wheels.py

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
import glob
2+
import hashlib
3+
import io
4+
import os
5+
import pathlib
6+
import shutil
7+
import stat
8+
import sys
9+
import urllib.request
10+
import zipfile
11+
from base64 import urlsafe_b64encode
12+
13+
packages = {
14+
"Windows amd64": {
15+
"asset": "windows-amd64.exe",
16+
"exec": "pglet.exe",
17+
"wheel_tags": ["py3-none-win_amd64"],
18+
"file_suffix": "py3-none-win_amd64",
19+
},
20+
"Linux amd64": {
21+
"asset": "linux-amd64",
22+
"exec": "pglet",
23+
"wheel_tags": [
24+
"py3-none-manylinux_2_17_x86_64",
25+
"py3-none-manylinux2014_x86_64",
26+
],
27+
"file_suffix": "py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64",
28+
},
29+
"Linux arm64": {
30+
"asset": "linux-arm64",
31+
"exec": "pglet",
32+
"wheel_tags": [
33+
"py3-none-manylinux_2_17_aarch64",
34+
"py3-none-manylinux2014_aarch64",
35+
],
36+
"file_suffix": "py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64",
37+
},
38+
"Linux arm": {
39+
"asset": "linux-arm",
40+
"exec": "pglet",
41+
"wheel_tags": [
42+
"py3-none-manylinux_2_17_armv7l",
43+
"py3-none-manylinux2014_armv7l",
44+
],
45+
"file_suffix": "py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l",
46+
},
47+
"macOS amd64": {
48+
"asset": "darwin-amd64",
49+
"exec": "pglet",
50+
"wheel_tags": ["py3-none-macosx_10_14_x86_64"],
51+
"file_suffix": "py3-none-macosx_10_14_x86_64",
52+
},
53+
"macOS arm64": {
54+
"asset": "darwin-arm64",
55+
"exec": "pglet",
56+
"wheel_tags": ["py3-none-macosx_12_0_arm64"],
57+
"file_suffix": "py3-none-macosx_12_0_arm64",
58+
},
59+
}
60+
61+
62+
def unpack_zip(zip_path, dest_dir):
63+
zf = zipfile.ZipFile(zip_path)
64+
zf.extractall(path=dest_dir)
65+
66+
67+
def download_pglet(version, suffix, dest_file):
68+
file_name = f"pglet-{version}-{suffix}"
69+
pglet_url = (
70+
f"https://github.com/pglet/pglet/releases/download/v{version}/{file_name}"
71+
)
72+
print(f"Downloading {pglet_url}...")
73+
urllib.request.urlretrieve(pglet_url, dest_file)
74+
st = os.stat(dest_file)
75+
os.chmod(dest_file, st.st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
76+
77+
78+
def get_pglet_version(current_dir):
79+
constants_py = current_dir.joinpath("pglet", "constants.py")
80+
81+
version_prefix = "PGLET_SERVER_VERSION"
82+
with open(constants_py) as yml:
83+
for line in yml:
84+
if version_prefix in line:
85+
return line.split("=")[1].strip().strip('"')
86+
raise f"{version_prefix} not found in constants.py"
87+
88+
89+
def read_chunks(file, size=io.DEFAULT_BUFFER_SIZE):
90+
"""Yield pieces of data from a file-like object until EOF."""
91+
while True:
92+
chunk = file.read(size)
93+
if not chunk:
94+
break
95+
yield chunk
96+
97+
98+
def rehash(path, blocksize=1 << 20):
99+
"""Return (hash, length) for path using hashlib.sha256()"""
100+
h = hashlib.sha256()
101+
length = 0
102+
with open(path, "rb") as f:
103+
for block in read_chunks(f, size=blocksize):
104+
length += len(block)
105+
h.update(block)
106+
digest = "sha256=" + urlsafe_b64encode(h.digest()).decode("latin1").rstrip("=")
107+
# unicode/str python2 issues
108+
return (digest, str(length)) # type: ignore
109+
110+
111+
current_dir = pathlib.Path(os.getcwd())
112+
print("current_dir", current_dir)
113+
114+
whl_files = glob.glob(str(current_dir.joinpath("dist", "*.whl")))
115+
if len(whl_files) == 0:
116+
print("No .whl files found. Run 'pdm build' first.")
117+
sys.exit(1)
118+
119+
orig_whl = whl_files[0]
120+
121+
package_version = os.path.basename(orig_whl).split("-")[1]
122+
pglet_version = get_pglet_version(current_dir)
123+
124+
print("package_version", package_version)
125+
print("pglet_version", pglet_version)
126+
127+
for name, package in packages.items():
128+
print(f"Building {name}...")
129+
130+
print("Unpacking original wheel file...")
131+
unpacked_whl = current_dir.joinpath("dist", "wheel")
132+
unpacked_whl.mkdir(exist_ok=True)
133+
unpack_zip(orig_whl, unpacked_whl)
134+
135+
# read original WHEEL file omitting tags
136+
wheel_path = str(
137+
current_dir.joinpath(
138+
"dist", "wheel", f"pglet-{package_version}.dist-info", "WHEEL"
139+
)
140+
)
141+
wheel_lines = []
142+
143+
with open(wheel_path, "r") as f:
144+
for line in f.readlines():
145+
if not "Tag: " in line:
146+
wheel_lines.append(line)
147+
148+
# print(wheel_lines)
149+
150+
# read original RECORD file
151+
record_path = str(
152+
current_dir.joinpath(
153+
"dist", "wheel", f"pglet-{package_version}.dist-info", "RECORD"
154+
)
155+
)
156+
record_lines = []
157+
158+
with open(record_path, "r") as f:
159+
for line in f.readlines():
160+
if not "dist-info/WHEEL," in line:
161+
record_lines.append(line)
162+
163+
# print(record_lines)
164+
165+
# create "bin" directory
166+
bin_path = current_dir.joinpath("dist", "wheel", "pglet", "bin")
167+
bin_path.mkdir(exist_ok=True)
168+
asset = package["asset"]
169+
exec_filename = package["exec"]
170+
exec_path = str(bin_path.joinpath(exec_filename))
171+
download_pglet(pglet_version, asset, exec_path)
172+
173+
# update RECORD
174+
h, l = rehash(exec_path)
175+
record_lines.insert(len(record_lines) - 3, f"pglet/bin/{exec_filename},{h},{l}\n")
176+
# for line in record_lines:
177+
# print(line.strip())
178+
179+
# update WHEEL file
180+
for tag in package["wheel_tags"]:
181+
wheel_lines.append(f"Tag: {tag}\n")
182+
183+
# save WHEEL
184+
with open(wheel_path, "w") as f:
185+
f.writelines(wheel_lines)
186+
187+
# update RECORD
188+
h, l = rehash(wheel_path)
189+
record_lines.insert(
190+
len(record_lines) - 3,
191+
f"pglet-{package_version}.dist-info/WHEEL,{h},{l}\n",
192+
)
193+
194+
# save RECORD
195+
with open(record_path, "w") as f:
196+
f.writelines(record_lines)
197+
198+
# zip
199+
suffix = package["file_suffix"]
200+
zip_filename = current_dir.joinpath("dist", f"pglet-{package_version}-{suffix}")
201+
shutil.make_archive(zip_filename, "zip", unpacked_whl)
202+
os.rename(f"{zip_filename}.zip", f"{zip_filename}.whl")
203+
204+
# cleanup
205+
shutil.rmtree(str(unpacked_whl))

0 commit comments

Comments
 (0)