feat: add candidate FreeCAD naming SDK and attachment oracles
Build and verify the candidate-only FreeCAD naming bridge and OCCT worker path, including three-stage StringHasher restoration. Add Datum, ShapeBinder, attachment-mode, and PartDesign structure oracles plus offline SDK build plans and CI boundary checks.
This commit is contained in:
92
scripts/generate-freecad-naming-sdk-manifest.mjs
Normal file
92
scripts/generate-freecad-naming-sdk-manifest.mjs
Normal file
@@ -0,0 +1,92 @@
|
||||
import { mkdir, writeFile } from 'node:fs/promises'
|
||||
import { resolve, relative } from 'node:path'
|
||||
import {
|
||||
REQUIRED_FREECAD_NAMING_CALLBACKS,
|
||||
REQUIRED_FREECAD_NAMING_DEFINITIONS,
|
||||
inspectIncludeDirectory,
|
||||
inspectPlannedLibrary,
|
||||
inspectPlannedRuntimeAsset,
|
||||
loadSdkPlan,
|
||||
resolveFrom,
|
||||
exists,
|
||||
sha256File,
|
||||
} from './freecad-naming-sdk-lib.mjs'
|
||||
|
||||
const root = resolve(new URL('..', import.meta.url).pathname)
|
||||
const outputRoot = process.env.FREECAD_WASM_SDK_OUTPUT_DIR
|
||||
? resolve(root, process.env.FREECAD_WASM_SDK_OUTPUT_DIR)
|
||||
: resolve(root, '.cache/toolchains/freecad-naming-sdk')
|
||||
const plan = await loadSdkPlan(root)
|
||||
const fail = (message) => { throw new Error(`FreeCAD WASM SDK manifest generation: ${message}`) }
|
||||
|
||||
const libraries = []
|
||||
for (const library of [...(plan.libraries ?? []), ...(plan.linkDependencies ?? [])]) {
|
||||
const inspected = await inspectPlannedLibrary(root, library)
|
||||
if (inspected.status !== 'verified') fail(`missing ${library.name} wasm static library: ${resolveFrom(root, library.path)}`)
|
||||
libraries.push({
|
||||
name: library.name,
|
||||
path: relative(outputRoot, resolveFrom(root, library.path)),
|
||||
sha256: inspected.sha256,
|
||||
})
|
||||
}
|
||||
const includeDirs = []
|
||||
for (const includeDir of plan.includeDirs ?? []) {
|
||||
const inspected = await inspectIncludeDirectory(root, includeDir)
|
||||
if (inspected.status !== 'verified') fail(`include directory ${includeDir.name} is incomplete: ${inspected.missingHeaders.join(', ')}`)
|
||||
includeDirs.push(relative(outputRoot, resolveFrom(root, includeDir.path)))
|
||||
}
|
||||
const bridge = resolveFrom(root, plan.namingBridge.path)
|
||||
if (!await exists(bridge)) fail(`missing naming bridge source: ${bridge}`)
|
||||
const hostAdapter = resolveFrom(root, plan.namingBridge.hostAdapter)
|
||||
if (!await exists(hostAdapter)) fail(`missing naming host adapter: ${hostAdapter}`)
|
||||
const forceInclude = resolveFrom(root, plan.compileOptions.forceInclude)
|
||||
if (!await exists(forceInclude)) fail(`missing force-include header: ${forceInclude}`)
|
||||
if (plan.compileOptions.cxxStandard !== 'c++20' || plan.compileOptions.pthread !== true || JSON.stringify(plan.compileOptions.definitions) !== JSON.stringify(REQUIRED_FREECAD_NAMING_DEFINITIONS)) fail('compile options do not match the locked SDK contract')
|
||||
const bridgeSha256 = await sha256File(bridge)
|
||||
const runtimeAssets = []
|
||||
for (const asset of plan.runtimeAssets ?? []) {
|
||||
const inspected = await inspectPlannedRuntimeAsset(root, asset)
|
||||
if (inspected.status !== 'verified') fail(`runtime asset ${asset.name} is incomplete: ${inspected.missingFiles.join(', ')}`)
|
||||
runtimeAssets.push({
|
||||
name: asset.name,
|
||||
path: relative(outputRoot, resolveFrom(root, asset.path)),
|
||||
preloadTo: asset.preloadTo,
|
||||
files: inspected.files,
|
||||
})
|
||||
}
|
||||
const manifest = {
|
||||
schemaVersion: 1,
|
||||
freecadVersion: plan.baseline.freecadVersion,
|
||||
sourceCommit: plan.baseline.sourceCommit,
|
||||
emscriptenVersion: plan.baseline.emscriptenVersion,
|
||||
qtTarget: plan.baseline.target,
|
||||
pythonTarget: plan.baseline.target,
|
||||
includeDirs,
|
||||
libraries,
|
||||
namingBridge: {
|
||||
source: relative(outputRoot, bridge),
|
||||
sha256: bridgeSha256,
|
||||
hostAdapter: relative(outputRoot, hostAdapter),
|
||||
hostAdapterSha256: await sha256File(hostAdapter),
|
||||
exports: [...REQUIRED_FREECAD_NAMING_CALLBACKS],
|
||||
},
|
||||
compileOptions: {
|
||||
cxxStandard: plan.compileOptions.cxxStandard,
|
||||
pthread: plan.compileOptions.pthread,
|
||||
forceInclude: relative(outputRoot, forceInclude),
|
||||
forceIncludeSha256: await sha256File(forceInclude),
|
||||
definitions: [...plan.compileOptions.definitions],
|
||||
},
|
||||
runtimeAssets,
|
||||
productionPublication: false,
|
||||
boundary: { ...plan.boundary },
|
||||
}
|
||||
await mkdir(outputRoot, { recursive: true })
|
||||
const output = resolve(outputRoot, 'manifest.json')
|
||||
await writeFile(output, `${JSON.stringify(manifest, null, 2)}\n`)
|
||||
console.log(JSON.stringify({
|
||||
status: 'freecad-naming-sdk-manifest-generated',
|
||||
output,
|
||||
productionPublished: false,
|
||||
systemExact: false,
|
||||
}, null, 2))
|
||||
Reference in New Issue
Block a user