Files
workinf_Blender_Wasm/tools/web/build-native-desktop-adapters.py
mes123456 f3e21c6b98
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled
chore: checkpoint web Blender migration source, tools and docs
Accumulated uncommitted work: GHOST Web backend, WebGL/GPU web layer,
web workflow engine and protocol, tooling, docs and task records.
Large generated evidence, user-testing archives, build snapshots and
imported test assets remain untracked.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-11-10 11:18:01 +08:00

82 lines
4.6 KiB
Python

#!/usr/bin/env python3
"""Build WM/RNA objects and link an isolated candidate from existing native libraries."""
import datetime
import hashlib
import json
import os
from pathlib import Path
import shlex
import shutil
import subprocess
import sys
root=Path(__file__).resolve().parents[2]
if '--configured' not in sys.argv:
result=subprocess.run(['bash','-c','source tools/web/native-build-env.sh && exec "$1" "$2" --configured',
'_',sys.executable,str(Path(__file__).resolve())],cwd=root)
sys.exit(result.returncode)
build=root/'build_blender_native'
candidate=build/'desktop-adapter'
directory=root/'tests/golden/WBV2-P1424-001/native-desktop-adapter-build'/datetime.datetime.now(datetime.timezone.utc).strftime('%Y%m%dT%H%M%SZ')
directory.mkdir(parents=True)
for name in ('objects','lib','bin','generated'):(candidate/name).mkdir(parents=True,exist_ok=True)
report={'schemaVersion':1,'status':'BUILDING','validation':'NOT_RUN','steps':[],'strategy':'REUSE_EXISTING_LIBRARIES_NO_DNA_LAYOUT_CHANGE'}
def run(name,command,cwd=build):
with (directory/(name+'.log')).open('w') as log:
result=subprocess.run(command,cwd=cwd,env=dict(os.environ,WBV2_SKIP_TYPECHECK='1'),stdout=log,stderr=subprocess.STDOUT)
report['steps'].append({'name':name,'exitCode':result.returncode})
if result.returncode:raise RuntimeError(name+' artifact build failed')
def digest(file):
sha=hashlib.sha256()
with file.open('rb') as stream:
for data in iter(lambda:stream.read(1024*1024),b''):sha.update(data)
return {'sha256':sha.hexdigest(),'bytes':file.stat().st_size}
def link_command(target):
output=subprocess.check_output(['ninja','-t','commands',target],cwd=build,text=True)
tokens=shlex.split(output.strip().splitlines()[-1])
if tokens[:2]==[':','&&']:tokens=tokens[2:]
if '&&' in tokens:tokens=tokens[:tokens.index('&&')]
if not tokens or 'g++' not in Path(tokens[0]).name or '-o' not in tokens:raise RuntimeError('NATIVE_LINK_RULE_UNSUPPORTED')
return tokens
try:
rules=['CXX_COMPILER__makesrna_unscanned_Release','CXX_COMPILER__bf_rna_unscanned_Release','CXX_COMPILER__bf_windowmanager_unscanned_Release']
commands=json.loads(subprocess.check_output(['ninja','-t','compdb',*rules],cwd=build,text=True))
def compile_object(suffix,name,source=None):
entry=next(item for item in commands if item['file'].endswith(suffix))
args=shlex.split(entry['command']);destination=candidate/'objects'/name
for flag in ('-o','-MT','-MF'):
if flag in args:args[args.index(flag)+1]=str(destination)+('.d' if flag=='-MF' else '')
if source:args[args.index('-c')+1]=str(source)
args.insert(1,'-I'+str(candidate/'generated'))
run(name,args)
return destination,entry['output']
generator_object,old_generator_object=compile_object('/rna_wm_api.cc','rna_wm_api.cc.o')
args=link_command('bin/makesrna');args=[str(generator_object) if value==old_generator_object else value for value in args]
generator=candidate/'bin/makesrna';args[args.index('-o')+1]=str(generator);run('link-makesrna',args)
run('generate-rna',[str(generator),str(root/'blender-5.2.0/source/blender/makesdna')+'/',str(candidate/'generated')+'/',str(candidate/'generated')+'/'])
wm_object,_=compile_object('/wm_window.cc','wm_window.cc.o')
rna_object,_=compile_object('/rna_wm_gen.cc','rna_wm_gen.cc.o',candidate/'generated/rna_wm_gen.cc')
replacements={};report['reusedArchives']={}
for name,object_file in [('libbf_windowmanager.a',wm_object),('libbf_rna.a',rna_object)]:
archive=build/'lib'/name;report['reusedArchives'][name]=digest(archive)
target=candidate/'lib'/name;shutil.copyfile(archive,target)
run('archive-'+name,['ar','rcs',str(target),str(object_file)])
replacements['lib/'+name]=str(target)
args=link_command('bin/blender');args=[replacements.get(value,value) for value in args]
pending=candidate/'bin/blender.pending';args[args.index('-o')+1]=str(pending)
run('link-blender',args)
runtime=candidate/'bin/5.2'
if not runtime.exists():runtime.symlink_to(build/'bin/5.2',target_is_directory=True)
pending.replace(candidate/'bin/blender')
run('web',['node','tools/web/incremental-web-build.mjs'],root)
report.update(status='BUILT_UNVERIFIED',nativeBinary=digest(candidate/'bin/blender'),binary=str(candidate/'bin/blender'))
except BaseException as error:
report.update(status='BUILD_INCOMPLETE',error=str(error));raise
finally:
(directory/'build.json').write_text(json.dumps(report,indent=2)+'\n')
print(json.dumps({'directory':str(directory),**report}),flush=True)