import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { spawn } from 'node:child_process'; import { createHash } from 'node:crypto'; const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..'); const dependencies=path.join(root,'freecad-source/build-web'); const source=process.env.FREECAD_IFC_SOURCE; if(!source||!fs.existsSync(path.join(source,'cmake/CMakeLists.txt')))throw Error('Set FREECAD_IFC_SOURCE to a real IfcOpenShell source tree'); const build=path.resolve(process.env.FREECAD_IFC_BUILD??path.join(dependencies,'ifcopenshell-native/build')); const output=path.join(root,'docs/current/user-tests-20260915',`ifc-build-${new Date().toISOString().replace(/[:.]/g,'-')}`); fs.mkdirSync(output,{recursive:true}); const stages=[]; async function run(stage,command,args,timeout=1800000){ const started=performance.now(),log=fs.openSync(path.join(output,`${stage}.log`),'w'); const child=spawn(command,args,{cwd:root,stdio:['ignore',log,log],detached:true}); let timedOut=false; const timer=setTimeout(()=>{timedOut=true;process.kill(-child.pid,'SIGTERM');},timeout); const code=await new Promise(resolve=>{child.once('close',resolve);child.once('error',()=>resolve(-1));}); clearTimeout(timer);fs.closeSync(log); stages.push({stage,command,args,exitCode:code,timedOut,seconds:(performance.now()-started)/1000}); fs.writeFileSync(path.join(output,'results.json'),JSON.stringify({source,build,stages},null,2)+'\n'); console.log(JSON.stringify(stages.at(-1))); if(code!==0)throw Error(`${stage} failed; see ${output}`); } await run('configure','cmake',['-S',path.join(source,'cmake'),'-B',build,'-G','Ninja', '-DCMAKE_TOOLCHAIN_FILE=/usr/share/emscripten/cmake/Modules/Platform/Emscripten.cmake', '-DCMAKE_BUILD_TYPE=Release','-DCMAKE_CXX_FLAGS=-pthread -fexceptions','-DCMAKE_C_FLAGS=-pthread', '-DCMAKE_CXX_FLAGS_RELEASE=-O1 -DNDEBUG','-DWASM_BUILD=ON','-DBUILD_SHARED_LIBS=OFF', '-DBUILD_IFCPYTHON=OFF','-DBUILD_CONVERT=OFF','-DBUILD_GEOMSERVER=OFF','-DBUILD_IFCGEOM=ON', '-DWITH_OPENCASCADE=ON','-DWITH_CGAL=OFF','-DCOLLADA_SUPPORT=OFF','-DHDF5_SUPPORT=OFF','-DIFCXML_SUPPORT=OFF', '-DSCHEMA_VERSIONS=2x3;4','-DUSE_CCACHE=OFF','-DNO_WARN=ON', `-DOCC_INCLUDE_DIR=${dependencies}/occt-pthread-install/include/opencascade`, `-DOCC_LIBRARY_DIR=${dependencies}/occt-pthread-install/lib`, `-DBOOST_ROOT=${dependencies}/boost-pthread-install`,'-DBoost_NO_SYSTEM_PATHS=ON', '-DEigen3_DIR=/usr/share/eigen3/cmake','-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=NEVER', '-DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=NEVER','-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=NEVER']); await run('compile','cmake',['--build',build,'--target','IfcParse','IfcGeom','geometry_kernel_opencascade','-j2']); const artifacts=['ifcparse/libIfcParse.a','ifcgeom/libIfcGeom.a','ifcgeom/kernels/libgeometry_kernel_opencascade.a'].map(name=>{ const file=path.join(build,name);return {file,size:fs.statSync(file).size,sha256:createHash('sha256').update(fs.readFileSync(file)).digest('hex')}; }); fs.writeFileSync(path.join(output,'artifacts.json'),JSON.stringify(artifacts,null,2)+'\n'); await run('host-configure','cmake',['-S','freecad-source','-B',path.join(dependencies,'configure'), `-DFREECAD_WEB_IFCOPENSHELL_SOURCE=${path.resolve(source)}`,`-DFREECAD_WEB_IFCOPENSHELL_BUILD=${build}`]); await run('host-link','python3',['tools/build_web_module_artifacts.py','--target','FreeCADWebPartBoxProbe','--jobs','2','--timeout','600']); console.log(output);