|
1 | 1 | import os
|
2 | 2 | import hashlib
|
3 | 3 | import pathlib
|
| 4 | +import io |
| 5 | +import subprocess |
4 | 6 | import urllib.request
|
5 | 7 | import libarchive
|
| 8 | +import tempfile |
6 | 9 | from email.message import EmailMessage
|
7 | 10 | from wheel.wheelfile import WheelFile
|
8 |
| -from zipfile import ZipInfo, ZIP_DEFLATED |
| 11 | +from zipfile import ZipInfo, ZIP_DEFLATED, ZipFile |
9 | 12 | from inspect import cleandoc
|
10 | 13 |
|
11 | 14 |
|
|
46 | 49 | 'linux-x64': 'manylinux_2_12_x86_64.manylinux2010_x86_64',
|
47 | 50 | 'linux-armv7l': 'manylinux_2_17_armv7l.manylinux2014_armv7l',
|
48 | 51 | 'linux-arm64': 'manylinux_2_17_aarch64.manylinux2014_aarch64',
|
49 |
| - 'linux-x64-musl': 'musllinux_1_1_x86_64' |
| 52 | + 'linux-x64-musl': 'musllinux_1_1_x86_64', |
| 53 | + 'linux-arm64-musl': 'musllinux_1_1_aarch64' |
50 | 54 | }
|
51 | 55 |
|
52 | 56 | # https://github.com/nodejs/unofficial-builds/
|
53 | 57 | # Versions added here should match the keys above
|
54 | 58 | UNOFFICIAL_NODEJS_BUILDS = {'linux-x64-musl'}
|
| 59 | +DOCKER_BASED_BUILDS = {"linux-arm64-musl"} |
55 | 60 |
|
56 |
| -_mismatched_versions = UNOFFICIAL_NODEJS_BUILDS - set(PLATFORMS.keys()) |
| 61 | +_mismatched_versions = (UNOFFICIAL_NODEJS_BUILDS|DOCKER_BASED_BUILDS) - set(PLATFORMS.keys()) |
57 | 62 | if _mismatched_versions:
|
58 | 63 | raise Exception(f"A version mismatch occurred. Check the usage of {_mismatched_versions}")
|
59 | 64 |
|
| 65 | +def _build_virtual_release_archive(docker_image: str, platform: str) -> bytes: |
| 66 | + binaries_to_copy = [x.split("bin/")[1] for x in [*NODE_BINS, *NODE_OTHER_BINS] if x.startswith("bin")] |
| 67 | + zip_bytes = io.BytesIO() |
| 68 | + with tempfile.TemporaryDirectory() as tmpdirname, ZipFile(zip_bytes, "w") as zip_file: |
| 69 | + subprocess.check_call([ |
| 70 | + "docker", |
| 71 | + "run", |
| 72 | + "--rm", |
| 73 | + f"--platform={platform}", |
| 74 | + f"--volume={tmpdirname}:/external", |
| 75 | + "--entrypoint=sh", |
| 76 | + docker_image, |
| 77 | + "-c", |
| 78 | + f""" |
| 79 | + for binary in {" ".join(binaries_to_copy)}; do |
| 80 | + cp $(which $binary) /external |
| 81 | + done |
| 82 | + chmod 755 /external/* |
| 83 | + """ |
| 84 | + ], |
| 85 | + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) |
| 86 | + |
| 87 | + tmpdir_contents = list(pathlib.Path(tmpdirname).glob("*")) |
| 88 | + for binary in tmpdir_contents: |
| 89 | + with open(binary, "rb") as f: |
| 90 | + file_info = ZipInfo( |
| 91 | + filename=f"node/bin/{binary.name}", |
| 92 | + ) |
| 93 | + file_info.external_attr = 0o755 << 16 |
| 94 | + zip_file.writestr(file_info, f.read()) |
| 95 | + |
| 96 | + zip_bytes.seek(0) |
| 97 | + return zip_bytes.read() |
60 | 98 |
|
61 | 99 | class ReproducibleWheelFile(WheelFile):
|
62 | 100 | def writestr(self, zinfo, *args, **kwargs):
|
@@ -186,7 +224,7 @@ def main() -> None:
|
186 | 224 | elif entry_name in NODE_OTHER_BINS and NODE_OTHER_BINS[entry_name][1]:
|
187 | 225 | other_bin = NODE_OTHER_BINS[entry_name][0]
|
188 | 226 | init_imports.append(f'from . import {other_bin} as {other_bin}')
|
189 |
| - script_name = '/'.join(os.path.normpath(os.path.join(os.path.dirname(entry.name), entry.linkpath)).split('/')[1:]) |
| 227 | + script_name = '/'.join(os.path.normpath(os.path.join(os.path.dirname(entry.name), entry.linkpath or other_bin)).split('/')[1:]) |
190 | 228 | contents[f'nodejs/{NODE_OTHER_BINS[entry_name][0]}.py'] = cleandoc(f"""
|
191 | 229 | import os, sys
|
192 | 230 | from typing import TYPE_CHECKING
|
@@ -312,22 +350,27 @@ def make_nodejs_version(node_version, suffix=''):
|
312 | 350 | print('Suffix:', suffix)
|
313 | 351 |
|
314 | 352 | for node_platform, python_platform in PLATFORMS.items():
|
315 |
| - filetype = 'zip' if node_platform.startswith('win-') else 'tar.xz' |
316 |
| - if node_platform in UNOFFICIAL_NODEJS_BUILDS: |
317 |
| - node_url = f'https://unofficial-builds.nodejs.org/download/release/v{node_version}/node-v{node_version}-{node_platform}.{filetype}' |
| 353 | + if node_platform in DOCKER_BASED_BUILDS: |
| 354 | + docker_image = f"node:{node_version}-alpine" |
| 355 | + print(f'- Making Wheel for {node_platform} from docker image {docker_image}') |
| 356 | + node_archive = _build_virtual_release_archive(docker_image=docker_image, platform="linux/arm64") |
318 | 357 | else:
|
319 |
| - node_url = f'https://nodejs.org/dist/v{node_version}/node-v{node_version}-{node_platform}.{filetype}' |
320 |
| - |
321 |
| - print(f'- Making Wheel for {node_platform} from {node_url}') |
322 |
| - try: |
323 |
| - with urllib.request.urlopen(node_url) as request: |
324 |
| - node_archive = request.read() |
325 |
| - print(f' {node_url}') |
326 |
| - print(f' {hashlib.sha256(node_archive).hexdigest()}') |
327 |
| - except urllib.error.HTTPError as e: |
328 |
| - print(f' {e.code} {e.reason}') |
329 |
| - print(f' Skipping {node_platform}') |
330 |
| - continue |
| 358 | + filetype = 'zip' if node_platform.startswith('win-') else 'tar.xz' |
| 359 | + if node_platform in UNOFFICIAL_NODEJS_BUILDS: |
| 360 | + node_url = f'https://unofficial-builds.nodejs.org/download/release/v{node_version}/node-v{node_version}-{node_platform}.{filetype}' |
| 361 | + else: |
| 362 | + node_url = f'https://nodejs.org/dist/v{node_version}/node-v{node_version}-{node_platform}.{filetype}' |
| 363 | + |
| 364 | + print(f'- Making Wheel for {node_platform} from {node_url}') |
| 365 | + try: |
| 366 | + with urllib.request.urlopen(node_url) as request: |
| 367 | + node_archive: bytes = request.read() |
| 368 | + print(f' {node_url}') |
| 369 | + print(f' {hashlib.sha256(node_archive).hexdigest()}') |
| 370 | + except urllib.error.HTTPError as e: |
| 371 | + print(f' {e.code} {e.reason}') |
| 372 | + print(f' Skipping {node_platform}') |
| 373 | + continue |
331 | 374 |
|
332 | 375 | wheel_path = write_nodejs_wheel('dist/',
|
333 | 376 | node_version=node_version,
|
|
0 commit comments