Skip to content

Commit 21e778a

Browse files
authored
fix: Wheels for macOS arch (arm, intel, universal) (#148)
* feat: wheel for mac is universal wheel now * chore: Clean up * fix: Universal pipeline * fix: Universal pipeline * fix: Universal pipeline * fix: Universal pipeline * fix: Universal pipeline * fix: Universal pipeline * fix: Universal pipeline * fix: Universal pipeline * fix: Universal pipeline * fix: Run universal mac wheel build also on mac Intel * fix: Proper build check * fix: Proper build check 2 * feat: Multiarch wheel builds * fix: Build updates * fix: Naming * fix: COmments * fix: Docs * fix: Version number * fix: setup to publish to pypi test * fix: Saving the file after solving conflcits may actually help * fix: Revert to PyPI after verifying publish
1 parent 78b14ac commit 21e778a

File tree

6 files changed

+190
-83
lines changed

6 files changed

+190
-83
lines changed

.github/workflows/build-wheel.yml

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ jobs:
121121
# Verify wheel structure
122122
twine check dist/*
123123
124-
- name: Build macOS wheel (Universal)
124+
- name: Build macOS wheel
125125
if: runner.os == 'macOS'
126126
run: |
127127
# Create necessary directories
@@ -135,16 +135,40 @@ jobs:
135135
pip install wheel
136136
pip install twine
137137
138-
# Download native artifacts
139-
python scripts/download_artifacts.py $C2PA_VERSION
138+
# Download native artifacts for the target architecture
139+
if [ "${{ inputs.architecture }}" = "universal2" ]; then
140+
python scripts/download_artifacts.py $C2PA_VERSION universal2
141+
elif [ "${{ inputs.architecture }}" = "arm64" ]; then
142+
python scripts/download_artifacts.py $C2PA_VERSION arm64
143+
elif [ "${{ inputs.architecture }}" = "x86_64" ]; then
144+
python scripts/download_artifacts.py $C2PA_VERSION x86_64
145+
else
146+
echo "Unknown architecture: ${{ inputs.architecture }}"
147+
exit 1
148+
fi
140149
141-
# Build wheel
142-
python setup.py bdist_wheel --plat-name macosx_10_9_universal2
150+
# Determine platform name based on target architecture
151+
if [ "${{ inputs.architecture }}" = "universal2" ]; then
152+
PLATFORM_NAME="macosx_10_9_universal2"
153+
elif [ "${{ inputs.architecture }}" = "arm64" ]; then
154+
PLATFORM_NAME="macosx_11_0_arm64"
155+
elif [ "${{ inputs.architecture }}" = "x86_64" ]; then
156+
PLATFORM_NAME="macosx_10_9_x86_64"
157+
else
158+
echo "Unknown architecture: ${{ inputs.architecture }}"
159+
exit 1
160+
fi
161+
162+
echo "Building wheel for architecture: ${{ inputs.architecture }}"
163+
echo "Using platform name: $PLATFORM_NAME"
164+
165+
# Build wheel with appropriate platform name
166+
python setup.py bdist_wheel --plat-name $PLATFORM_NAME
143167
144168
# Rename wheel to ensure unique filename
145169
cd dist
146170
for wheel in *.whl; do
147-
mv "$wheel" "${wheel/macosx_10_9_universal2/macosx_10_9_universal2}"
171+
mv "$wheel" "${wheel/$PLATFORM_NAME/$PLATFORM_NAME}"
148172
done
149173
cd ..
150174

.github/workflows/build.yml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -326,12 +326,12 @@ jobs:
326326
.\venv\Scripts\pytest .\tests\test_unit_tests.py -v
327327
328328
build-macos-wheel:
329-
name: Build macOS wheel (Universal)
329+
name: Build macOS wheels
330330
uses: ./.github/workflows/build-wheel.yml
331331
needs: [tests-unix, read-version]
332332
with:
333333
python-version: "3.10"
334-
runs-on: macos-latest
334+
runs-on: ${{ matrix.runs-on }}
335335
artifact-name: wheels-macos-${{ matrix.target }}
336336
architecture: ${{ matrix.target }}
337337
c2pa-version: ${{ needs.read-version.outputs.c2pa-native-version }}
@@ -342,6 +342,10 @@ jobs:
342342
include:
343343
- target: universal2
344344
runs-on: macos-latest
345+
- target: arm64
346+
runs-on: macos-latest
347+
- target: x86_64
348+
runs-on: macos-13
345349

346350
if: |
347351
github.event_name != 'pull_request' ||
@@ -351,15 +355,15 @@ jobs:
351355
contains(github.event.pull_request.labels.*.name, 'safe to test')
352356
353357
test-built-macos-wheel:
354-
name: Test macOS built wheel (Universal)
358+
name: Test macOS built wheels
355359
needs: build-macos-wheel
356360
runs-on: ${{ matrix.runs-on }}
357361
strategy:
358362
matrix:
359363
include:
360-
- target: universal2
364+
- target: arm64
361365
runs-on: macos-latest
362-
- target: universal2
366+
- target: x86_64
363367
runs-on: macos-13
364368

365369
if: |

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "c2pa-python"
7-
version = "0.18.0"
7+
version = "0.19.0"
88
requires-python = ">=3.10"
99
description = "Python bindings for the C2PA Content Authenticity Initiative (CAI) library"
1010
readme = { file = "README.md", content-type = "text/markdown" }

scripts/download_artifacts.py

Lines changed: 68 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,20 @@ def detect_arch():
5454
else:
5555
raise ValueError(f"Unsupported CPU architecture: {machine}")
5656

57-
def get_platform_identifier():
58-
"""Get the full platform identifier (arch-os) for the current system,
59-
matching the identifiers used by the Github publisher.
57+
def get_platform_identifier(target_arch=None):
58+
"""Get the full platform identifier (arch-os) for the current system or target.
59+
60+
Args:
61+
target_arch: Optional target architecture.
62+
If provided, overrides auto-detection.
63+
For macOS: 'universal2', 'arm64', or 'x86_64'
64+
For Linux: 'aarch64' or 'x86_64'
65+
For Windows: 'arm64' or 'x64'
66+
6067
Returns one of:
61-
- universal-apple-darwin (for Mac)
68+
- universal-apple-darwin (for macOS universal)
69+
- aarch64-apple-darwin (for macOS ARM64)
70+
- x86_64-apple-darwin (for macOS x86_64)
6271
- x86_64-pc-windows-msvc (for Windows 64-bit)
6372
- x86_64-unknown-linux-gnu (for Linux x86_64)
6473
- aarch64-unknown-linux-gnu (for Linux ARM64)
@@ -67,11 +76,27 @@ def get_platform_identifier():
6776
machine = platform.machine().lower()
6877

6978
if system == "darwin":
70-
return "universal-apple-darwin"
79+
if target_arch == "arm64":
80+
return "aarch64-apple-darwin"
81+
elif target_arch == "x86_64":
82+
return "x86_64-apple-darwin"
83+
elif target_arch == "universal2":
84+
return "universal-apple-darwin"
85+
else:
86+
# Auto-detect: prefer specific architecture over universal
87+
if machine == "arm64":
88+
return "aarch64-apple-darwin"
89+
elif machine == "x86_64":
90+
return "x86_64-apple-darwin"
91+
else:
92+
return "universal-apple-darwin"
7193
elif system == "windows":
72-
return "x86_64-pc-windows-msvc"
94+
if target_arch == "arm64":
95+
return "aarch64-pc-windows-msvc"
96+
else:
97+
return "x86_64-pc-windows-msvc"
7398
elif system == "linux":
74-
if machine in ["arm64", "aarch64"]:
99+
if target_arch == "aarch64" or machine in ["arm64", "aarch64"]:
75100
return "aarch64-unknown-linux-gnu"
76101
else:
77102
return "x86_64-unknown-linux-gnu"
@@ -101,19 +126,20 @@ def download_and_extract_libs(url, platform_name):
101126
response = requests.get(url, headers=headers)
102127
response.raise_for_status()
103128

129+
print(f"Downloaded zip file, extracting lib files...")
104130
with zipfile.ZipFile(io.BytesIO(response.content)) as zip_ref:
105131
# Extract only files inside the libs/ directory
132+
extracted_count = 0
106133
for member in zip_ref.namelist():
107-
print(f" Processing zip member: {member}")
108134
if member.startswith("lib/") and not member.endswith("/"):
109-
print(f" Processing lib file from downloadedzip: {member}")
110135
target_path = platform_dir / os.path.relpath(member, "lib")
111-
print(f" Moving file to target path: {target_path}")
112136
target_path.parent.mkdir(parents=True, exist_ok=True)
113137
with zip_ref.open(member) as source, open(target_path, "wb") as target:
114138
target.write(source.read())
139+
extracted_count += 1
140+
print(f" Extracted: {member} -> {target_path}")
115141

116-
print(f"Done downloading and extracting libraries for {platform_name}")
142+
print(f"Done downloading and extracting {extracted_count} library files for {platform_name}")
117143

118144
def copy_artifacts_to_root():
119145
"""Copy the artifacts folder from scripts/artifacts to the root of the repository."""
@@ -122,56 +148,77 @@ def copy_artifacts_to_root():
122148
return
123149

124150
print("Copying artifacts from scripts/artifacts to root...")
151+
print("Contents of scripts/artifacts before copying:")
152+
for item in sorted(SCRIPTS_ARTIFACTS_DIR.iterdir()):
153+
print(f" {item.name}")
154+
125155
if ROOT_ARTIFACTS_DIR.exists():
126156
shutil.rmtree(ROOT_ARTIFACTS_DIR)
127157
print(f"Copying from {SCRIPTS_ARTIFACTS_DIR} to {ROOT_ARTIFACTS_DIR}")
128158
shutil.copytree(SCRIPTS_ARTIFACTS_DIR, ROOT_ARTIFACTS_DIR)
129159
print("Done copying artifacts")
130-
print("\nFolder content of artifacts directory:")
160+
print("\nFolder content of root artifacts directory:")
131161
for item in sorted(ROOT_ARTIFACTS_DIR.iterdir()):
132162
print(f" {item.name}")
133163

134164
def main():
135165
if len(sys.argv) < 2:
136-
print("Usage: python download_artifacts.py <release_tag>")
166+
print("Usage: python download_artifacts.py <release_tag> [target_architecture]")
137167
print("Example: python download_artifacts.py c2pa-v0.49.5")
168+
print("Example: python download_artifacts.py c2pa-v0.49.5 arm64")
138169
sys.exit(1)
139170

140171
release_tag = sys.argv[1]
172+
target_arch = sys.argv[2] if len(sys.argv) > 2 else None
173+
141174
try:
175+
# Clean up any existing artifacts before starting
176+
print("Cleaning up existing artifacts...")
177+
if SCRIPTS_ARTIFACTS_DIR.exists():
178+
shutil.rmtree(SCRIPTS_ARTIFACTS_DIR)
142179
SCRIPTS_ARTIFACTS_DIR.mkdir(exist_ok=True)
143180
print(f"Fetching release information for tag {release_tag}...")
144181
release = get_release_by_tag(release_tag)
145182
print(f"Found release: {release['tag_name']} \n")
146183

147-
# Get the platform identifier for the current system
184+
# Get the platform identifier for the target architecture
148185
env_platform = os.environ.get("C2PA_LIBS_PLATFORM")
149186
if env_platform:
150187
print(f"Using platform from environment variable C2PA_LIBS_PLATFORM: {env_platform}")
151-
platform_id = env_platform or get_platform_identifier()
188+
platform_id = env_platform
189+
else:
190+
platform_id = get_platform_identifier(target_arch)
191+
print(f"Using target architecture: {target_arch or 'auto-detected'}")
192+
print(f"Detected machine architecture: {platform.machine()}")
193+
print(f"Detected system: {platform.system()}")
194+
152195
print("Looking up releases for platform id: ", platform_id)
153-
print("Environment variable set for lookup: ", env_platform)
154-
platform_source = "environment variable" if env_platform else "auto-detection"
155-
print(f"Target platform: {platform_id} (set through{platform_source})")
196+
platform_source = "environment variable" if env_platform else "target architecture" if target_arch else "auto-detection"
197+
print(f"Target platform: {platform_id} (set through {platform_source})")
156198

157199
# Construct the expected asset name
158200
expected_asset_name = f"{release_tag}-{platform_id}.zip"
159201
print(f"Looking for asset: {expected_asset_name}")
160202

161203
# Find the matching asset in the release
162204
matching_asset = None
205+
print(f"Looking for asset: {expected_asset_name}")
206+
print("Available assets in release:")
163207
for asset in release['assets']:
208+
print(f" - {asset['name']}")
164209
if asset['name'] == expected_asset_name:
165210
matching_asset = asset
166-
break
211+
print(f"Using native library: {matching_asset['name']}")
167212

168213
if matching_asset:
169-
print(f"Found matching asset: {matching_asset['name']}")
214+
print(f"\nDownloading asset: {matching_asset['name']}")
170215
download_and_extract_libs(matching_asset['browser_download_url'], platform_id)
171216
print("\nArtifacts have been downloaded and extracted successfully!")
172217
copy_artifacts_to_root()
173218
else:
174-
print(f"\nNo matching asset found: {expected_asset_name}")
219+
print(f"\nNo matching asset found for platform: {platform_id}")
220+
print(f"Expected asset name: {expected_asset_name}")
221+
print("Please check if the asset exists in the release or if the platform identifier is correct.")
175222

176223
except requests.exceptions.RequestException as e:
177224
print(f"Error: {e}")

0 commit comments

Comments
 (0)