#!/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)