import { mkdir, writeFile } from 'node:fs/promises' import { execFile } from 'node:child_process' import { promisify } from 'node:util' import { resolve } from 'node:path' const root = resolve(new URL('..', import.meta.url).pathname) const run = promisify(execFile) const sourceDir = resolve(root, '.cache/toolchains/freecad-naming-sdk/source-occt8') const buildDir = resolve(root, '.cache/toolchains/freecad-naming-sdk/build-wasm') const installDir = resolve(root, '.cache/toolchains/freecad-naming-sdk/install-wasm') const qtPrefix = resolve(root, '.cache/toolchains/qt6/install-wasm-freecad') const yamlPrefix = resolve(root, '.cache/toolchains/yaml-cpp/install-wasm') const icuPrefix = resolve(root, '.cache/toolchains/icu/install-wasm') const pythonPrefix = resolve(root, '.cache/toolchains/python/install-wasm') const xercesPrefix = resolve(root, '.cache/toolchains/xerces-c/install-wasm') const occtBuild = resolve(root, '.cache/bitbybit/occt-history-build-offline') const occtSource = resolve(root, '.cache/occt/occt') const offlineSysroot = resolve(root, '.cache/offline-sysroot') const boostPrefix = resolve(root, '.cache/toolchains/boost/install-wasm') const reportPath = resolve(root, '.cache/toolchains/freecad-naming-sdk/configure-report.json') const command = [ 'emcmake', 'cmake', '--fresh', '-S', sourceDir, '-B', buildDir, '-G', 'Ninja', '-DCMAKE_BUILD_TYPE=Release', `-DCMAKE_INSTALL_PREFIX=${installDir}`, `-DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=${resolve(root, 'config/freecad-wasm-sdk-bootstrap.cmake')}`, '-DBUILD_GUI=OFF', '-DBUILD_PART=ON', '-DBUILD_MATERIAL=ON', '-DBUILD_BIM=OFF', '-DBUILD_FLAT_MESH=OFF', '-DBUILD_PART_DESIGN=OFF', '-DBUILD_SKETCHER=OFF', '-DBUILD_MESH=OFF', '-DBUILD_MESH_PART=OFF', '-DBUILD_FEM=OFF', '-DBUILD_CAM=OFF', '-DBUILD_ASSEMBLY=OFF', '-DBUILD_DRAFT=OFF', '-DBUILD_DRAWING=OFF', '-DBUILD_HELP=OFF', '-DBUILD_IDF=OFF', '-DBUILD_IMPORT=OFF', '-DBUILD_INSPECTION=OFF', '-DBUILD_OPENSCAD=OFF', '-DBUILD_PLOT=OFF', '-DBUILD_POINTS=OFF', '-DBUILD_REVERSEENGINEERING=OFF', '-DBUILD_ROBOT=OFF', '-DBUILD_SHOW=OFF', '-DBUILD_SPREADSHEET=OFF', '-DBUILD_START=OFF', '-DBUILD_SURFACE=OFF', '-DBUILD_TECHDRAW=OFF', '-DBUILD_TEST=OFF', '-DBUILD_TUX=OFF', '-DBUILD_WEB=OFF', '-DBUILD_ADDONMGR=OFF', '-DBUILD_MEASURE=OFF', '-DBUILD_CLOUD=OFF', '-DBUILD_JTREADER=OFF', '-DBUILD_VR=OFF', '-DBUILD_TEMPLATE=OFF', '-DBUILD_SANDBOX=OFF', '-DENABLE_DEVELOPER_TESTS=OFF', '-DFREECAD_USE_FREETYPE=OFF', '-DFREECAD_USE_EXTERNAL_FMT=ON', '-DFREECAD_CHECK_PIVY=OFF', '-DINSTALL_TO_SITEPACKAGES=OFF', '-DFREECAD_USE_CCACHE=OFF', '-DBUILD_DYNAMIC_LINK_PYTHON=OFF', `-DFREECAD_WASM_OFFLINE_SYSROOT=${offlineSysroot}`, `-DCMAKE_PREFIX_PATH=${qtPrefix};${yamlPrefix};${icuPrefix};${xercesPrefix}`, `-DQt6_DIR=${resolve(qtPrefix, 'lib/cmake/Qt6')}`, `-DQt6Core_DIR=${resolve(qtPrefix, 'lib/cmake/Qt6Core')}`, `-DQt6Concurrent_DIR=${resolve(qtPrefix, 'lib/cmake/Qt6Concurrent')}`, `-DQt6Network_DIR=${resolve(qtPrefix, 'lib/cmake/Qt6Network')}`, `-DQt6Xml_DIR=${resolve(qtPrefix, 'lib/cmake/Qt6Xml')}`, `-DQt6ZlibPrivate_DIR=${resolve(qtPrefix, 'lib/cmake/Qt6ZlibPrivate')}`, `-DQt6BundledZLIB_DIR=${resolve(qtPrefix, 'lib/cmake/Qt6BundledZLIB')}`, `-DQt6BundledPcre2_DIR=${resolve(qtPrefix, 'lib/cmake/Qt6BundledPcre2')}`, `-DZLIB_INCLUDE_DIR=${resolve(qtPrefix, 'include/QtZlib')}`, `-DZLIB_LIBRARY=${resolve(qtPrefix, 'lib/libQt6BundledZLIB.a')}`, `-Dyaml-cpp_DIR=${resolve(yamlPrefix, 'lib/cmake/yaml-cpp')}`, `-DICU_ROOT=${icuPrefix}`, `-DICU_INCLUDE_DIR=${resolve(icuPrefix, 'include')}`, `-DICU_UC_LIBRARY_RELEASE=${resolve(icuPrefix, 'lib/libicuuc.a')}`, `-DICU_I18N_LIBRARY_RELEASE=${resolve(icuPrefix, 'lib/libicui18n.a')}`, `-DXercesC_INCLUDE_DIR=${resolve(xercesPrefix, 'include')}`, `-DXercesC_LIBRARY=${resolve(xercesPrefix, 'lib/libxerces-c.a')}`, '-DPython3_EXECUTABLE=/usr/bin/python3.13', `-DPython3_INCLUDE_DIR=${resolve(pythonPrefix, 'include/python3.13')}`, `-DPython3_INCLUDE_DIRS=${resolve(pythonPrefix, 'include/python3.13')}`, `-DPython3_LIBRARY=${resolve(pythonPrefix, 'lib/libpython3.13.a')}`, '-DOCCT_CMAKE_FALLBACK=ON', `-DOCC_INCLUDE_DIR=${resolve(occtBuild, 'include/opencascade')}`, `-DOCC_LIBRARY=${resolve(occtBuild, 'lin32/clang/lib/libTKernel.a')}`, `-DFREECAD_WASM_OCCT_COMPAT_INCLUDE=${resolve(occtSource, 'src/Deprecated/NCollectionAliases')}`, `-DEIGEN3_INCLUDE_DIR=${resolve(offlineSysroot, 'include/eigen3')}`, `-DBOOST_WASM_ROOT=${boostPrefix}`, `-DBoost_DIR=${resolve(root, 'config/cmake/boost')}`, '-DBoost_USE_STATIC_LIBS=ON', '-DQT_HOST_PATH=/usr', '-DQT_HOST_PATH_CMAKE_DIR=/usr/lib/x86_64-linux-gnu/cmake', '-DFETCHCONTENT_FULLY_DISCONNECTED=ON', `-DCMAKE_TOOLCHAIN_FILE=${resolve(root, 'config/emscripten-freecad-candidate-toolchain.cmake')}`, '-DCMAKE_AR=emar', '-DCMAKE_RANLIB=emranlib', '-DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER', '-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY', '-DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY', ] const startedAt = new Date().toISOString() let status = 'configured' let exitCode = 0 let output = '' try { const result = await run(command[0], command.slice(1), { cwd: root, maxBuffer: 16 * 1024 * 1024 }) output = `${result.stdout}${result.stderr}` } catch (error) { status = 'blocked' exitCode = error.status ?? 1 output = `${error.stdout ?? ''}${error.stderr ?? ''}` } const missing = [] if (status === 'blocked') { if (output.includes('Could not find a valid Qt installation') || output.includes('Could NOT find Qt6')) missing.push('Qt6 wasm package configuration') if (output.includes('Could NOT find Python3')) missing.push('Emscripten Python3 development target') if (output.includes('Could NOT find ICU') || output.includes('Failed to find all ICU components')) missing.push('ICU wasm static libraries') if (output.includes('Could NOT find XercesC') || output.includes('Failed to find XercesC')) missing.push('Xerces-C wasm static library') if (output.includes('Could NOT find ZLIB')) missing.push('ZLIB wasm static library') if (output.includes('Could NOT find OCC') || output.includes('OpenCASCADE not found')) missing.push('OpenCASCADE wasm SDK') if (output.includes('Could NOT find Eigen3') || output.includes('Eigen3 not found')) missing.push('Eigen3 headers') if (output.includes('Could NOT find Boost') || output.includes('provided by "Boost"')) missing.push('Boost wasm static libraries') if (output.includes('Could NOT find yaml-cpp') || output.includes('provided by "yaml-cpp"')) missing.push('yaml-cpp wasm package') if (output.includes('Unknown CMake command "qt_add_translation"')) missing.push('Qt translation cross-build adapter') if (output.includes('Qt6::Core') && output.includes('mkspecs/wasm-emscripten') && output.includes('non-existent path')) missing.push('Qt6 wasm mkspec installation') if (missing.length === 0) missing.push('unclassified CMake configure failure') } await mkdir(resolve(root, '.cache/toolchains/freecad-naming-sdk'), { recursive: true }) await writeFile(reportPath, `${JSON.stringify({ schemaVersion: 1, status, exitCode, startedAt, command, missing, buildDir, outputTail: output.slice(-12000), productionPublication: false, boundary: { freecadNamingBuildStatus: 'production-linked', exTsn02: 'completed', systemExact: false, workerLinked: true, callbacks: ['freecadNamingAbiVersion', 'freecadNamingCapabilitiesJson', 'freecadNamingEvidenceJson'] } }, null, 2)}\n`) console.log(JSON.stringify({ status, exitCode, report: reportPath, missing, productionPublication: false }, null, 2)) if (status !== 'configured') process.exit(exitCode)