PPysolate SpineREAD THE CODE · UNDERSTAND THE SYSTEM
课程与章节
源码索引 / CURRENT IMPLEMENTATION

tools/setup-build-inputs.py

86 行 · 点击行号可定位;函数链接随声明位置更新,不手填行号。

跳到函数 / 声明
#!/usr/bin/env python3
"""One-time Linux x86_64 CPython/WASI preparation; later Guest rebuilds are local.

No sudo, no remote build host. Downloads are pinned; interrupted builds can resume.
"""
import hashlib
import json
import os
from pathlib import Path
import platform
import shutil
import subprocess
import sys
import tempfile

ROOT = Path(__file__).resolve().parents[1]

def main():
    if platform.system() != 'Linux' or platform.machine() != 'x86_64':
        raise SystemExit('Build inputs require Linux x86_64.')
    if sys.version_info < (3, 11):
        raise SystemExit('Use Python 3.11 or newer for the CPython WASI build driver.')
    for tool in ('cc', 'make', 'curl', 'tar', 'unzip'):
        if not shutil.which(tool):
            raise SystemExit(f'Missing {tool}; install build tools before running setup.')
    base = Path(os.environ.get('SPINE_BUILD_INPUTS', ROOT / 'build/inputs')).resolve()
    base.mkdir(parents=True, exist_ok=True)
    downloads = base / 'downloads'
    downloads.mkdir(exist_ok=True)
    lock = json.loads((ROOT / 'tools/build-inputs.lock.json').read_text())
    jobs = int(os.environ.get('SPINE_BUILD_JOBS', '6'))
    if jobs < 1: raise SystemExit('SPINE_BUILD_JOBS must be positive')
    os.sched_setaffinity(0, sorted(os.sched_getaffinity(0))[:jobs])
    env = os.environ.copy()
    env['MAKEFLAGS'] = f'-j{jobs}'
    layout = {
        'cpython-source': ('cpython', False),
        'wasi-sdk-linux-x86_64': ('tools/wasi-sdk', False),
        'wasm-tools-linux-x86_64': ('tools/wasm-tools', False),
        'wasmtime-linux-x86_64': ('tools/wasmtime', False),
        'wasi-vfs-cli-linux-x86_64': ('tools/wasi-vfs-cli', True),
        'wasi-vfs-static-library': ('tools/wasi-vfs-lib', True),
    }
    def digest(file):
        with file.open('rb') as stream:
            return hashlib.file_digest(stream, 'sha256').hexdigest()
    for name, (directory, zipped) in layout.items():
        source = lock[name]
        archive = downloads / name
        if not archive.exists() or digest(archive) != source['sha256']:
            partial = archive.with_suffix('.part')
            print(f'Downloading {name} {source["version"]}', flush=True)
            subprocess.run(['curl', '-fL', '--retry', '2', '--connect-timeout', '20', '--max-time', '900', '-C', '-', '-o', str(partial), source['url']], check=True)
            if digest(partial) != source['sha256']:
                raise SystemExit(f'Checksum mismatch: {partial}; no extraction performed')
            partial.replace(archive)
        dest = base / directory
        if not dest.exists():
            dest.parent.mkdir(parents=True, exist_ok=True)
            with tempfile.TemporaryDirectory(dir=base, prefix='extract-') as temporary:
                if zipped:
                    subprocess.run(['unzip', '-q', str(archive), '-d', temporary], check=True)
                else:
                    subprocess.run(['tar', '-xf', str(archive), '-C', temporary, '--strip-components=1'], check=True)
                Path(temporary).rename(dest)
    for relative in ('tools/wasmtime/wasmtime', 'tools/wasm-tools/wasm-tools', 'tools/wasi-vfs-cli/wasi-vfs'):
        (base / relative).chmod(0o755)
    python = base / 'cpython'
    config = python / 'Tools/wasm/wasi/config.site-wasm32-wasi'
    text = config.read_text()
    if 'ac_cv_func_clock_nanosleep=no' not in text:
        config.write_text(text + '\n# wazero: use relative clock sleeps\nac_cv_func_clock_nanosleep=no\nac_cv_lib_rt_clock_nanosleep=no\n')
    env['WASI_SDK_PATH'] = str(base / 'tools/wasi-sdk')
    env['WASMTIME'] = str(base / 'tools/wasmtime/wasmtime')
    env['PATH'] = str(base / 'tools/wasmtime') + os.pathsep + env['PATH']
    print('Building CPython locally (incremental on repeat runs)', flush=True)
    subprocess.run([sys.executable, 'Tools/wasm/wasi', 'build', '--wasi-sdk', env['WASI_SDK_PATH']], cwd=python, env=env, check=True)
    target = python / 'cross-build/wasm32-wasip1'
    for file in ('libpython3.14.a', 'pyconfig.h', 'Modules/_decimal/libmpdec/libmpdec.a', 'Modules/expat/libexpat.a'):
        if not (target / file).is_file(): raise SystemExit(f'Missing build output: {file}')
    if not list((target / 'Modules/_hacl').glob('*.a')):
        raise SystemExit('Missing HACL archives')
    print(f'Ready: {base}\nRun bash build-guest.sh to rebuild this project\'s actual Guest.', flush=True)

if __name__ == '__main__':
    main()