#!/usr/bin/env python3 """Produce the changed WASM and web bundle without running acceptance commands.""" import argparse import datetime import hashlib import json import os from pathlib import Path import subprocess ROOT = Path(__file__).resolve().parents[2] def publish(destination, data): destination.parent.mkdir(parents=True, exist_ok=True) temporary = destination.with_name(destination.name + f'.tmp-{os.getpid()}') temporary.write_bytes(data) temporary.replace(destination) def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--output', default='tests/golden/WBV2-P1424-001/desktop-workflow-build') args = parser.parse_args() output = ROOT / args.output / datetime.datetime.now(datetime.timezone.utc).strftime('%Y%m%dT%H%M%SZ') output.mkdir(parents=True, exist_ok=False) report = {'schemaVersion': 1, 'task': 'WBV2-P1424-001', 'status': 'BUILDING', 'validation': 'NOT_RUN', 'acceptance': 'NOT_CLAIMED', 'steps': []} try: commands = [ *(([('cpython', ['python3', 'tools/web/build-webengine-python.py'], ROOT)]) if not (ROOT / 'build_web_python313/build/libpython3.13.a').exists() else []), ('wasm', ['bash', '-c', 'source tools/web/emscripten-env.sh && cmake --build build_web_blender6 --target web_engine --parallel 4'], ROOT), ] for name, command, directory in commands: with (output / (name + '.log')).open('w') as log: completed = subprocess.run(command, cwd=directory, stdout=log, stderr=subprocess.STDOUT) report['steps'].append({'name': name, 'exitCode': completed.returncode}) if completed.returncode: raise RuntimeError(name + ' build failed; see ' + str(output)) build = ROOT / 'build_web_blender6/bin' manifest_path = ROOT / 'web/app/public/wasm-module-manifest.json' manifest = json.loads(manifest_path.read_text()) report['artifacts'] = {} for name in ('web_engine.js', 'web_engine.wasm'): data = (build / name).read_bytes() digest = hashlib.sha256(data).hexdigest() for directory in ('web/app/src/vendor/blender', 'web/app/public/vendor/blender'): publish(ROOT / directory / name, data) for module in manifest['modules']: for resource in module.get('resources', {}).values(): if resource.get('url') == '/vendor/blender/' + name: resource['sha256'] = digest report['artifacts'][name] = {'sha256': digest, 'bytes': len(data)} publish(manifest_path, (json.dumps(manifest, indent=2) + '\n').encode()) with (output / 'web.log').open('w') as log: completed = subprocess.run(['node', 'tools/web/incremental-web-build.mjs'], cwd=ROOT, env=dict(os.environ, WBV2_SKIP_TYPECHECK='1'), stdout=log, stderr=subprocess.STDOUT) report['steps'].append({'name': 'web', 'exitCode': completed.returncode}) if completed.returncode: raise RuntimeError('web build failed; see ' + str(output)) report['status'] = 'BUILT_UNVERIFIED' except BaseException as error: report.update(status='BUILD_INCOMPLETE', error=str(error)) raise finally: publish(output / 'build.json', (json.dumps(report, indent=2) + '\n').encode()) print(json.dumps({'directory': str(output), **report}), flush=True) if __name__ == '__main__': main()