75 lines
3.5 KiB
JavaScript
75 lines
3.5 KiB
JavaScript
import { readFile, readdir, writeFile, access } from 'node:fs/promises'
|
|
import { join, resolve } from 'node:path'
|
|
|
|
const root = resolve(new URL('..', import.meta.url).pathname)
|
|
const sourceRoot = resolve(process.env.FREECAD_SOURCE_DIR || join(root, '.cache/freecad/FreeCAD'))
|
|
const outputPath = resolve(process.env.FREECAD_INVENTORY_FILE || join(root, 'config/freecad-source-inventory.json'))
|
|
const baselineCommit = '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d'
|
|
const cmakeAliases = {
|
|
AddonManager: 'ADDONMGR',
|
|
JtReader: 'JTREADER',
|
|
MeshPart: 'MESH_PART',
|
|
PartDesign: 'PART_DESIGN',
|
|
TechDraw: 'TECHDRAW',
|
|
ReverseEngineering: 'REVERSEENGINEERING',
|
|
OpenSCAD: 'OPENSCAD',
|
|
}
|
|
const read = async (path) => readFile(path, 'utf8')
|
|
const exists = async (path) => access(path).then(() => true).catch(() => false)
|
|
const moduleRoot = join(sourceRoot, 'src/Mod')
|
|
if (!(await exists(moduleRoot))) throw new Error(`FreeCAD source directory is unavailable: ${moduleRoot}`)
|
|
|
|
const buildOptions = await read(join(sourceRoot, 'cMake/FreeCAD_Helpers/InitializeFreeCADBuildOptions.cmake'))
|
|
const optionDefaults = new Map([...buildOptions.matchAll(/option\(BUILD_([A-Z0-9_]+)[^\n]*?\s(ON|OFF)\)/g)].map((match) => [match[1], match[2] === 'ON']))
|
|
const moduleNames = (await readdir(moduleRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort()
|
|
const commandPattern = /(?:addCommand|addCommand\s*\(|registerCommand)\s*\(\s*["']([^"']+)["']/g
|
|
const commandIds = (text) => [...text.matchAll(commandPattern)].map((match) => match[1]).filter((value, index, values) => values.indexOf(value) === index).sort()
|
|
|
|
const modules = []
|
|
for (const name of moduleNames) {
|
|
const modulePath = join(moduleRoot, name)
|
|
const option = cmakeAliases[name] || name.toUpperCase()
|
|
const cmakePath = join(modulePath, 'CMakeLists.txt')
|
|
const appPath = join(modulePath, 'App')
|
|
const guiPath = join(modulePath, 'Gui')
|
|
const cmake = await exists(cmakePath) ? await read(cmakePath) : ''
|
|
const pythonFiles = []
|
|
const collectPython = async (directory) => {
|
|
if (!(await exists(directory))) return
|
|
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
|
const path = join(directory, entry.name)
|
|
if (entry.isDirectory()) await collectPython(path)
|
|
else if (entry.name.endsWith('.py')) pythonFiles.push(path)
|
|
}
|
|
}
|
|
await collectPython(modulePath)
|
|
const commandNames = []
|
|
for (const path of pythonFiles) {
|
|
const text = await read(path)
|
|
commandNames.push(...commandIds(text))
|
|
}
|
|
modules.push({
|
|
name,
|
|
sourcePath: `src/Mod/${name}`,
|
|
cmakeOption: `BUILD_${option}`,
|
|
cmakeDefault: optionDefaults.has(option) ? optionDefaults.get(option) : null,
|
|
hasCMake: Boolean(cmake),
|
|
hasApp: await exists(appPath),
|
|
hasGui: await exists(guiPath),
|
|
pythonFileCount: pythonFiles.length,
|
|
commandIds: [...new Set(commandNames)].sort(),
|
|
runtimeStatus: 'requires-freecad-reference-probe',
|
|
})
|
|
}
|
|
|
|
const inventory = {
|
|
schemaVersion: 1,
|
|
baseline: { freecadVersion: '1.1.1', commit: baselineCommit, sourcePath: '.cache/freecad/FreeCAD' },
|
|
generatedBy: 'scripts/generate-freecad-source-inventory.mjs',
|
|
moduleCount: modules.length,
|
|
modules,
|
|
}
|
|
if (modules.length !== 34) throw new Error(`Expected 34 FreeCAD modules, found ${modules.length}.`)
|
|
await writeFile(outputPath, `${JSON.stringify(inventory, null, 2)}\n`, 'utf8')
|
|
console.log(JSON.stringify({ status: 'source-inventory-generated', output: outputPath, moduleCount: modules.length, commandCount: modules.reduce((total, module) => total + module.commandIds.length, 0) }, null, 2))
|