Skip to content

Commit c68399c

Browse files
authored
feat: Update c2pa.py (#105)
* fix: Library loader * fix: Refactor * fix: Loader * fix: Clean up tests * fix: Some renaming going on (#106) * fix: Simplify build (#107) * fix: Some renaming going on * fix: Format * fix: Add scripts to debug builds, and clean env * fix: gitgnore * fix: Update makefile comments * fix: verbose debug logs * fix: Only keep the scripts we need * fix: Improve makefile * chore: rename 1 function * fix: Rename errors camel case * fix: Add debug logs to the loading of libs * fix: Format download logs * fix: Platform checks, download only one platform * fix: Load library, one test failure
1 parent 97ad247 commit c68399c

File tree

11 files changed

+1480
-616
lines changed

11 files changed

+1480
-616
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ __pycache__/
44
*$py.class
55

66
/artifacts
7+
scripts/artifacts
78

89

910
# C extensions
@@ -108,3 +109,4 @@ target/
108109
*.dylib
109110
*.dll
110111
*.so
112+
src/c2pa/libs/

MANIFEST.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
include src/c2pa/libs/*.dylib
22
include src/c2pa/libs/*.dll
3-
include src/c2pa/libs/*.so
3+
include src/c2pa/libs/*.so

Makefile

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,34 @@
33
# Start from clean env: Delete `.venv`, then `python3 -m venv .venv`
44
# Pre-requisite: Python virtual environment is active (source .venv/bin/activate)
55

6-
release:
7-
cargo build --release
6+
clean:
7+
rm -rf artifacts/ build/ dist/
8+
9+
clean-c2pa-env:
10+
python3 -m pip uninstall -y c2pa
11+
python3 -m pip cache purge
812

913
build-python:
10-
rm -rf c2pa/c2pa
11-
python3 -m pip uninstall -y maturin
12-
python3 -m pip uninstall -y uniffi
1314
python3 -m pip install -r requirements.txt
14-
maturin develop
15+
python3 -m pip install -r requirements-dev.txt
16+
pip install -e .
1517

1618
test:
1719
python3 ./tests/test_unit_tests.py
18-
python3 ./tests/test_api.py
20+
21+
test-local-wheel-build:
22+
# Clean any existing builds
23+
rm -rf build/ dist/
24+
# Download artifacts and place them where they should go
25+
python scripts/download_artifacts.py c2pa-v0.49.5
26+
# Install Python
27+
python3 -m pip install -r requirements.txt
28+
python3 -m pip install -r requirements-dev.txt
29+
python setup.py bdist_wheel
30+
# Install local build in venv
31+
pip install $$(ls dist/*.whl)
32+
# Verify installation in local venv
33+
python -c "import c2pa; print('C2PA package installed at:', c2pa.__file__)"
1934

2035
publish: release
2136
python3 -m pip install twine

requirements-dev.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Build dependencies
2+
wheel==0.41.2 # For building wheels
3+
setuptools==68.0.0 # For building packages
4+
5+
# Testing dependencies
6+
pytest==7.4.0
7+
8+
# for downloading the library artifacts
9+
requests>=2.0.0

requirements.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ setuptools==68.0.0 # For building packages
44
toml ==0.10.2 # For reading pyproject.toml files
55

66
# Testing dependencies
7-
pytest==7.4.0
8-
# only used in the training example
9-
cryptography>=41.0.0
7+
pytest==7.4.0
8+
# only used in the training example
9+
cryptography>=41.0.0
1010
# for downloading the library artifacts
11-
requests>=2.0.0
11+
requests>=2.0.0

scripts/download_artifacts.py

Lines changed: 94 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,72 @@
55
from pathlib import Path
66
import zipfile
77
import io
8+
import shutil
9+
import platform
10+
import subprocess
811

912
# Constants
1013
REPO_OWNER = "contentauth"
1114
REPO_NAME = "c2pa-rs"
1215
GITHUB_API_BASE = "https://api.github.com"
13-
ARTIFACTS_DIR = Path("artifacts")
16+
SCRIPTS_ARTIFACTS_DIR = Path("scripts/artifacts")
17+
ROOT_ARTIFACTS_DIR = Path("artifacts")
18+
19+
def detect_os():
20+
"""Detect the operating system and return the corresponding platform identifier."""
21+
system = platform.system().lower()
22+
if system == "darwin":
23+
return "apple-darwin"
24+
elif system == "linux":
25+
return "unknown-linux-gnu"
26+
elif system == "windows":
27+
return "pc-windows-msvc"
28+
else:
29+
raise ValueError(f"Unsupported operating system: {system}")
30+
31+
def detect_arch():
32+
"""Detect the CPU architecture and return the corresponding identifier."""
33+
machine = platform.machine().lower()
34+
35+
# Handle common architecture names
36+
if machine in ["x86_64", "amd64"]:
37+
return "x86_64"
38+
elif machine in ["arm64", "aarch64"]:
39+
return "aarch64"
40+
else:
41+
raise ValueError(f"Unsupported CPU architecture: {machine}")
42+
43+
def get_platform_identifier():
44+
"""Get the full platform identifier (arch-os) for the current system,
45+
matching the identifiers used by the Github publisher.
46+
Returns one of:
47+
- universal-apple-darwin (for Mac)
48+
- x86_64-pc-windows-msvc (for Windows 64-bit)
49+
- x86_64-unknown-linux-gnu (for Linux 64-bit)
50+
"""
51+
system = platform.system().lower()
52+
53+
if system == "darwin":
54+
return "universal-apple-darwin"
55+
elif system == "windows":
56+
return "x86_64-pc-windows-msvc"
57+
elif system == "linux":
58+
return "x86_64-unknown-linux-gnu"
59+
else:
60+
raise ValueError(f"Unsupported operating system: {system}")
1461

1562
def get_release_by_tag(tag):
1663
"""Get release information for a specific tag from GitHub."""
1764
url = f"{GITHUB_API_BASE}/repos/{REPO_OWNER}/{REPO_NAME}/releases/tags/{tag}"
65+
print(f"Fetching release information from {url}...")
1866
response = requests.get(url)
1967
response.raise_for_status()
2068
return response.json()
2169

2270
def download_and_extract_libs(url, platform_name):
2371
"""Download a zip artifact and extract only the libs folder."""
2472
print(f"Downloading artifact for {platform_name}...")
25-
platform_dir = ARTIFACTS_DIR / platform_name
73+
platform_dir = SCRIPTS_ARTIFACTS_DIR / platform_name
2674
platform_dir.mkdir(parents=True, exist_ok=True)
2775

2876
response = requests.get(url)
@@ -31,12 +79,28 @@ def download_and_extract_libs(url, platform_name):
3179
with zipfile.ZipFile(io.BytesIO(response.content)) as zip_ref:
3280
# Extract only files inside the libs/ directory
3381
for member in zip_ref.namelist():
82+
print(f" Processing zip member: {member}")
3483
if member.startswith("lib/") and not member.endswith("/"):
84+
print(f" Processing lib file from downloadedzip: {member}")
3585
target_path = platform_dir / os.path.relpath(member, "lib")
86+
print(f" Moving file to target path: {target_path}")
3687
target_path.parent.mkdir(parents=True, exist_ok=True)
3788
with zip_ref.open(member) as source, open(target_path, "wb") as target:
3889
target.write(source.read())
39-
print(f"Successfully downloaded and extracted libraries for {platform_name}")
90+
91+
print(f"Done downloading and extracting libraries for {platform_name}")
92+
93+
def copy_artifacts_to_root():
94+
"""Copy the artifacts folder from scripts/artifacts to the root of the repository."""
95+
if not SCRIPTS_ARTIFACTS_DIR.exists():
96+
print("No artifacts found in scripts/artifacts")
97+
return
98+
99+
print("Copying artifacts from scripts/artifacts to root...")
100+
if ROOT_ARTIFACTS_DIR.exists():
101+
shutil.rmtree(ROOT_ARTIFACTS_DIR)
102+
shutil.copytree(SCRIPTS_ARTIFACTS_DIR, ROOT_ARTIFACTS_DIR)
103+
print("Done copying artifacts")
40104

41105
def main():
42106
if len(sys.argv) < 2:
@@ -46,31 +110,43 @@ def main():
46110

47111
release_tag = sys.argv[1]
48112
try:
49-
ARTIFACTS_DIR.mkdir(exist_ok=True)
113+
SCRIPTS_ARTIFACTS_DIR.mkdir(exist_ok=True)
50114
print(f"Fetching release information for tag {release_tag}...")
51115
release = get_release_by_tag(release_tag)
52-
print(f"Found release: {release['tag_name']}")
116+
print(f"Found release: {release['tag_name']} \n")
53117

54-
for asset in release['assets']:
55-
if not asset['name'].endswith('.zip'):
56-
continue
118+
# Get the platform identifier for the current system
119+
env_platform = os.environ.get("C2PA_LIBS_PLATFORM")
120+
if env_platform:
121+
print(f"Using platform from environment variable C2PA_LIBS_PLATFORM: {env_platform}")
122+
platform_id = env_platform or get_platform_identifier()
123+
platform_source = "environment variable" if env_platform else "auto-detection"
124+
print(f"Target platform: {platform_id} (set through{platform_source})")
57125

58-
# Example asset name: c2pa-v0.49.5-aarch64-apple-darwin.zip
59-
# Platform name: aarch64-apple-darwin
60-
parts = asset['name'].split('-')
61-
if len(parts) < 4:
62-
continue # Unexpected naming, skip
63-
platform_name = '-'.join(parts[3:]).replace('.zip', '')
126+
# Construct the expected asset name
127+
expected_asset_name = f"{release_tag}-{platform_id}.zip"
128+
print(f"Looking for asset: {expected_asset_name}")
64129

65-
download_and_extract_libs(asset['browser_download_url'], platform_name)
130+
# Find the matching asset in the release
131+
matching_asset = None
132+
for asset in release['assets']:
133+
if asset['name'] == expected_asset_name:
134+
matching_asset = asset
135+
break
66136

67-
print("\nAll artifacts have been downloaded and extracted successfully!")
137+
if matching_asset:
138+
print(f"Found matching asset: {matching_asset['name']}")
139+
download_and_extract_libs(matching_asset['browser_download_url'], platform_id)
140+
print("\nArtifacts have been downloaded and extracted successfully!")
141+
copy_artifacts_to_root()
142+
else:
143+
print(f"\nNo matching asset found: {expected_asset_name}")
68144

69145
except requests.exceptions.RequestException as e:
70-
print(f"Error downloading artifacts: {e}", file=sys.stderr)
146+
print(f"Error: {e}")
71147
sys.exit(1)
72148
except Exception as e:
73-
print(f"Unexpected error: {e}", file=sys.stderr)
149+
print(f"Error: {e}")
74150
sys.exit(1)
75151

76152
if __name__ == "__main__":

setup.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,29 +36,29 @@ def get_current_platform():
3636

3737
def copy_platform_libraries(platform_name, clean_first=False):
3838
"""Copy libraries for a specific platform to the package libs directory.
39-
39+
4040
Args:
4141
platform_name: The platform to copy libraries for
4242
clean_first: If True, remove existing files in PACKAGE_LIBS_DIR first
4343
"""
4444
platform_dir = ARTIFACTS_DIR / platform_name
45-
45+
4646
# Ensure the platform directory exists and contains files
4747
if not platform_dir.exists():
4848
raise ValueError(f"Platform directory not found: {platform_dir}")
49-
49+
5050
# Get list of all files in the platform directory
5151
platform_files = list(platform_dir.glob('*'))
5252
if not platform_files:
5353
raise ValueError(f"No files found in platform directory: {platform_dir}")
54-
54+
5555
# Clean and recreate the package libs directory if requested
5656
if clean_first and PACKAGE_LIBS_DIR.exists():
5757
shutil.rmtree(PACKAGE_LIBS_DIR)
58-
58+
5959
# Ensure the package libs directory exists
6060
PACKAGE_LIBS_DIR.mkdir(parents=True, exist_ok=True)
61-
61+
6262
# Copy files from platform-specific directory to the package libs directory
6363
for file in platform_files:
6464
if file.is_file():
@@ -85,10 +85,10 @@ def find_available_platforms():
8585
platform_dir = ARTIFACTS_DIR / platform_name
8686
if platform_dir.exists() and any(platform_dir.iterdir()):
8787
available_platforms.append(platform_name)
88-
88+
8989
if not available_platforms:
9090
raise ValueError("No platform-specific libraries found in artifacts directory")
91-
91+
9292
return available_platforms
9393

9494
# For development installation
@@ -100,13 +100,13 @@ def find_available_platforms():
100100
if 'bdist_wheel' in sys.argv:
101101
available_platforms = find_available_platforms()
102102
print(f"Found libraries for platforms: {', '.join(available_platforms)}")
103-
103+
104104
for platform_name in available_platforms:
105105
print(f"\nBuilding wheel for {platform_name}...")
106106
try:
107107
# Copy libraries for this platform (cleaning first)
108108
copy_platform_libraries(platform_name, clean_first=True)
109-
109+
110110
# Build the wheel
111111
setup(
112112
name="c2pa",

src/c2pa/build.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,48 +23,48 @@ def get_latest_release() -> dict:
2323
def download_artifact(url: str, platform_name: str) -> None:
2424
"""Download and extract an artifact to the appropriate platform directory."""
2525
print(f"Downloading artifact for {platform_name}...")
26-
26+
2727
# Create platform directory
2828
platform_dir = ARTIFACTS_DIR / platform_name
2929
platform_dir.mkdir(parents=True, exist_ok=True)
30-
30+
3131
# Download the zip file
3232
response = requests.get(url)
3333
response.raise_for_status()
34-
34+
3535
# Extract the zip file
3636
with zipfile.ZipFile(io.BytesIO(response.content)) as zip_ref:
3737
# Extract all files to the platform directory
3838
zip_ref.extractall(platform_dir)
39-
39+
4040
print(f"Successfully downloaded and extracted artifacts for {platform_name}")
4141

4242
def download_artifacts() -> None:
4343
"""Main function to download artifacts. Can be called as a script or from hatch."""
4444
try:
4545
# Create artifacts directory if it doesn't exist
4646
ARTIFACTS_DIR.mkdir(exist_ok=True)
47-
47+
4848
# Get latest release
4949
print("Fetching latest release information...")
5050
release = get_latest_release()
5151
print(f"Found release: {release['tag_name']}")
52-
52+
5353
# Download each asset
5454
for asset in release['assets']:
5555
# Skip non-zip files
5656
if not asset['name'].endswith('.zip'):
5757
continue
58-
58+
5959
# Determine platform from asset name
6060
# Example: c2pa-rs-v1.0.0-macosx-arm64.zip
6161
platform_name = asset['name'].split('-')[-1].replace('.zip', '')
62-
62+
6363
# Download and extract the artifact
6464
download_artifact(asset['browser_download_url'], platform_name)
65-
65+
6666
print("\nAll artifacts have been downloaded successfully!")
67-
67+
6868
except requests.exceptions.RequestException as e:
6969
print(f"Error downloading artifacts: {e}", file=sys.stderr)
7070
sys.exit(1)
@@ -96,4 +96,4 @@ def initialize_build() -> None:
9696

9797
if __name__ == "__main__":
9898
inject_version()
99-
download_artifacts()
99+
download_artifacts()

0 commit comments

Comments
 (0)