@typecad/cuttlefish 1.0.0-alpha.10 → 1.0.0-alpha.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/config.d.ts +11 -3
- package/dist/api/shared/framework-manifest.d.ts +85 -78
- package/dist/api/shared/framework-manifest.js +1 -0
- package/dist/api/shared/index.d.ts +2 -0
- package/dist/api/shared/index.js +1 -0
- package/dist/api/shared/spdx-licenses.d.ts +139 -0
- package/dist/api/shared/spdx-licenses.js +385 -0
- package/dist/cli.js +6 -0
- package/dist/config-loader.d.ts +8 -0
- package/dist/config-loader.js +192 -48
- package/dist/config-schema.d.ts +96 -89
- package/dist/config-schema.js +3 -0
- package/dist/create/board-spec.d.ts +122 -122
- package/dist/create/eslint-rules-template.js +6 -3
- package/dist/create/init-templates.js +9 -1
- package/dist/emit/compliance/rules.js +34 -0
- package/dist/emit/emitters/setup.js +15 -2
- package/dist/ir/feature-registry.js +22 -6
- package/dist/ir/program-analysis.d.ts +4 -2
- package/dist/ir/program-analysis.js +2 -4
- package/dist/platform/async-runtime.d.ts +1 -1
- package/dist/platform/async-runtime.js +12 -3
- package/dist/platform/generic-strategy.js +1 -1
- package/dist/transpile.js +55 -7
- package/dist/utils/cli.js +3 -2
- package/dist/utils/fs.d.ts +2 -0
- package/dist/utils/fs.js +16 -0
- package/package.json +5 -4
|
@@ -5,4 +5,4 @@ import type { RuntimePolyfillIR } from "../api/shared/index.js";
|
|
|
5
5
|
* Build a RuntimePolyfillIR for the async Promise runtime, if the program
|
|
6
6
|
* has async functions and the target architecture has stdlib support.
|
|
7
7
|
*/
|
|
8
|
-
export declare function buildAsyncRuntimePolyfill(program: ProgramIR, ctx: PlatformContext | undefined, target: string, queueCapacity?: number): RuntimePolyfillIR | null;
|
|
8
|
+
export declare function buildAsyncRuntimePolyfill(program: ProgramIR, ctx: PlatformContext | undefined, target: string, queueCapacity?: number, strategy?: import("../api/shared/platform-strategy.js").PlatformStrategy): RuntimePolyfillIR | null;
|
|
@@ -3,7 +3,7 @@ import { getStdLibSupport, generatePromiseRuntime } from "../api/shared/index.js
|
|
|
3
3
|
* Build a RuntimePolyfillIR for the async Promise runtime, if the program
|
|
4
4
|
* has async functions and the target architecture has stdlib support.
|
|
5
5
|
*/
|
|
6
|
-
export function buildAsyncRuntimePolyfill(program, ctx, target, queueCapacity) {
|
|
6
|
+
export function buildAsyncRuntimePolyfill(program, ctx, target, queueCapacity, strategy) {
|
|
7
7
|
const hasAsync = program.functions.some(fn => fn.isAsync);
|
|
8
8
|
if (!hasAsync)
|
|
9
9
|
return null;
|
|
@@ -11,13 +11,22 @@ export function buildAsyncRuntimePolyfill(program, ctx, target, queueCapacity) {
|
|
|
11
11
|
const stdlib = getStdLibSupport(architecture);
|
|
12
12
|
if (!stdlib.hasVector || !stdlib.hasString)
|
|
13
13
|
return null;
|
|
14
|
+
// Pass the strategy through so the runtime's now-expression matches the
|
|
15
|
+
// target (generic bakes a std::chrono expression via currentTimeMillis()
|
|
16
|
+
// instead of a millis() token the generic target never defines). When the
|
|
17
|
+
// expression uses std::chrono, the polyfill must carry <chrono> itself —
|
|
18
|
+
// the generic strategy's forcedIncludes are empty by design.
|
|
19
|
+
const now = strategy?.currentTimeMillis?.() ?? "millis()";
|
|
20
|
+
const requiredIncludes = ["<functional>", "<vector>", "<utility>", "<string>"];
|
|
21
|
+
if (now.includes("std::chrono"))
|
|
22
|
+
requiredIncludes.push("<chrono>");
|
|
14
23
|
return {
|
|
15
24
|
kind: "polyfill",
|
|
16
25
|
id: "async_runtime",
|
|
17
26
|
domain: "standard",
|
|
18
|
-
requiredIncludes
|
|
27
|
+
requiredIncludes,
|
|
19
28
|
forwardDeclarations: [],
|
|
20
|
-
helperStructs: [generatePromiseRuntime(queueCapacity ?? 256)],
|
|
29
|
+
helperStructs: [generatePromiseRuntime(queueCapacity ?? 256, false, strategy)],
|
|
21
30
|
helperFunctions: [],
|
|
22
31
|
shimMacros: [],
|
|
23
32
|
dependencies: [],
|
|
@@ -184,7 +184,7 @@ export class GenericStrategy {
|
|
|
184
184
|
}
|
|
185
185
|
generateNativePolyfills(program, ctx) {
|
|
186
186
|
const helpers = [];
|
|
187
|
-
const asyncRuntime = buildAsyncRuntimePolyfill(program, ctx, "generic", this.asyncQueueCapacity());
|
|
187
|
+
const asyncRuntime = buildAsyncRuntimePolyfill(program, ctx, "generic", this.asyncQueueCapacity(), this);
|
|
188
188
|
if (asyncRuntime)
|
|
189
189
|
helpers.push(asyncRuntime);
|
|
190
190
|
return helpers;
|
package/dist/transpile.js
CHANGED
|
@@ -29,7 +29,7 @@ import { loadSafetyEngine } from "./safety/safety-bridge.js";
|
|
|
29
29
|
import { setDisplayProfile, resetDisplayProfile } from "./stores/display-profile-store.js";
|
|
30
30
|
import { setThemeCss, resetThemeCss, setThemeClass } from "./stores/theme-store.js";
|
|
31
31
|
import { emitCpp, registerAllEnumNames } from "./emit/cpp-emitter.js";
|
|
32
|
-
import { readText, writeText } from "./utils/fs.js";
|
|
32
|
+
import { readText, writeText, resetWrittenFiles, wasWrittenThisRun } from "./utils/fs.js";
|
|
33
33
|
import { debug as logDebug, info } from "./utils/logger.js";
|
|
34
34
|
import { loadLibraryDefinitions, generateLibdefStubs } from "./libdef/registry.js";
|
|
35
35
|
import { buildCallGraph } from "./ir/call-graph.js";
|
|
@@ -66,12 +66,53 @@ function loadExpectPreprocessor() {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
function cleanOutput(_entryDir, outDir) {
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
//
|
|
69
|
+
// The out dir is NOT wiped: writeText skips writing when content is
|
|
70
|
+
// identical, so keeping it lets downstream build tools (idf.py/ninja,
|
|
71
|
+
// arduino-cli) reuse their build caches. Stale generated SOURCES are handled
|
|
72
|
+
// precisely instead — after emission, sweepStaleGeneratedSources() removes
|
|
73
|
+
// compiled-source files in the out dir that this run did not write (e.g. a
|
|
74
|
+
// main.cpp left behind by the old emit naming next to the current src.cpp;
|
|
75
|
+
// Zephyr's CMakeLists globs src/*.cpp, so a leftover compiles into
|
|
76
|
+
// duplicate-symbol link errors). The output dir is still created (via
|
|
77
|
+
// writeText → ensureDir) on first run.
|
|
78
|
+
void outDir;
|
|
79
|
+
resetWrittenFiles();
|
|
80
|
+
}
|
|
81
|
+
const GENERATED_SOURCE_EXTENSIONS = [".cpp", ".cc", ".c", ".h", ".ino"];
|
|
82
|
+
/**
|
|
83
|
+
* Remove stale generated source files from the out dir: compiled-source files
|
|
84
|
+
* that THIS transpile run did not write. Sweeps only the source layouts the
|
|
85
|
+
* emit pipeline uses (out dir root, src/, main/) and never recurses — build
|
|
86
|
+
* trees (e.g. Zephyr's out/build with its own generated .c files) are
|
|
87
|
+
* untouched, and neither are sidecar JSONs.
|
|
88
|
+
*/
|
|
89
|
+
function sweepStaleGeneratedSources(outDir) {
|
|
90
|
+
for (const sub of ["", "src", "main"]) {
|
|
91
|
+
const dir = sub ? path.join(outDir, sub) : outDir;
|
|
92
|
+
let entries;
|
|
93
|
+
try {
|
|
94
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
continue; // layout subdir not used by this framework
|
|
98
|
+
}
|
|
99
|
+
for (const entry of entries) {
|
|
100
|
+
if (!entry.isFile())
|
|
101
|
+
continue;
|
|
102
|
+
if (!GENERATED_SOURCE_EXTENSIONS.some((ext) => entry.name.toLowerCase().endsWith(ext)))
|
|
103
|
+
continue;
|
|
104
|
+
const full = path.join(dir, entry.name);
|
|
105
|
+
if (wasWrittenThisRun(full))
|
|
106
|
+
continue;
|
|
107
|
+
try {
|
|
108
|
+
fs.unlinkSync(full);
|
|
109
|
+
info(`Removed stale generated source: ${path.relative(process.cwd(), full)}`);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// Locked/read-only file — leave it; best-effort cleanup.
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
75
116
|
}
|
|
76
117
|
/**
|
|
77
118
|
* Auto-generates .d.ts files for C++ modules that are missing declarations.
|
|
@@ -822,6 +863,13 @@ export async function transpileFile(options) {
|
|
|
822
863
|
}
|
|
823
864
|
}
|
|
824
865
|
profiler.endTimer("post:flatten");
|
|
866
|
+
// Remove stale generated sources (renamed entries, removed modules) so
|
|
867
|
+
// downstream globs (Zephyr's CMakeLists src/*.cpp) don't compile leftovers
|
|
868
|
+
// into duplicate-symbol link errors. Runs after every write of this run,
|
|
869
|
+
// including the toolchain prepare hook above.
|
|
870
|
+
profiler.startTimer("post:sweep-stale");
|
|
871
|
+
sweepStaleGeneratedSources(outDir);
|
|
872
|
+
profiler.endTimer("post:sweep-stale");
|
|
825
873
|
// Profiler session ends (profiling disabled - no report generation)
|
|
826
874
|
// ── Generate diagnostics report if enabled ──────────────────────────────
|
|
827
875
|
let diagnosticsReportPath;
|
package/dist/utils/cli.js
CHANGED
|
@@ -409,8 +409,9 @@ export function parseCommandLine(argv) {
|
|
|
409
409
|
platformContext: {},
|
|
410
410
|
};
|
|
411
411
|
}
|
|
412
|
-
// licenses subcommand — scan
|
|
413
|
-
|
|
412
|
+
// licenses subcommand — scan the project's dependencies for SPDX licenses.
|
|
413
|
+
// Accept `license` (singular) as an alias so both spellings work.
|
|
414
|
+
if (firstArg === "licenses" || firstArg === "license") {
|
|
414
415
|
return {
|
|
415
416
|
command: "licenses",
|
|
416
417
|
strict: argv.includes("--strict"),
|
package/dist/utils/fs.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export declare function ensureDir(dirPath: string): void;
|
|
2
2
|
export declare function readText(filePath: string): string;
|
|
3
|
+
export declare function resetWrittenFiles(): void;
|
|
4
|
+
export declare function wasWrittenThisRun(filePath: string): boolean;
|
|
3
5
|
export declare function writeText(filePath: string, content: string): void;
|
|
4
6
|
export declare function listFiles(dirPath: string, extension: string): string[];
|
|
5
7
|
/**
|
package/dist/utils/fs.js
CHANGED
|
@@ -8,10 +8,26 @@ export function ensureDir(dirPath) {
|
|
|
8
8
|
export function readText(filePath) {
|
|
9
9
|
return fs.readFileSync(filePath, "utf8");
|
|
10
10
|
}
|
|
11
|
+
// Paths written (or confirmed identical) via writeText since the last
|
|
12
|
+
// resetWrittenFiles() call. The transpiler uses this to sweep stale generated
|
|
13
|
+
// sources from the out dir — files left behind by renamed entries or removed
|
|
14
|
+
// source modules that a downstream glob (e.g. Zephyr's CMakeLists
|
|
15
|
+
// `file(GLOB src/*.cpp)`) would otherwise compile, producing duplicate-symbol
|
|
16
|
+
// link errors.
|
|
17
|
+
const writtenFiles = new Set();
|
|
18
|
+
export function resetWrittenFiles() {
|
|
19
|
+
writtenFiles.clear();
|
|
20
|
+
}
|
|
21
|
+
export function wasWrittenThisRun(filePath) {
|
|
22
|
+
return writtenFiles.has(path.resolve(filePath));
|
|
23
|
+
}
|
|
11
24
|
export function writeText(filePath, content) {
|
|
12
25
|
ensureDir(path.dirname(filePath));
|
|
26
|
+
const resolved = path.resolve(filePath);
|
|
13
27
|
// Skip writing when content is identical — preserves mtime so downstream
|
|
14
28
|
// build tools (idf.py/ninja, arduino-cli, make) can skip recompilation.
|
|
29
|
+
// The file still counts as "written this run" (it is current output).
|
|
30
|
+
writtenFiles.add(resolved);
|
|
15
31
|
try {
|
|
16
32
|
if (fs.readFileSync(filePath, "utf8") === content)
|
|
17
33
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typecad/cuttlefish",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.12",
|
|
4
4
|
"description": "TypeScript to C++ transpiler — native, Arduino, and bare-metal targets",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/transpile.js",
|
|
@@ -99,8 +99,8 @@
|
|
|
99
99
|
"zod": "^3.24.0"
|
|
100
100
|
},
|
|
101
101
|
"peerDependencies": {
|
|
102
|
-
"@typecad/ui": "1.0.0-alpha.
|
|
103
|
-
"@typecad/safety": "1.0.0-alpha.
|
|
102
|
+
"@typecad/ui": "1.0.0-alpha.12",
|
|
103
|
+
"@typecad/safety": "1.0.0-alpha.12"
|
|
104
104
|
},
|
|
105
105
|
"peerDependenciesMeta": {
|
|
106
106
|
"@typecad/ui": {
|
|
@@ -111,7 +111,8 @@
|
|
|
111
111
|
}
|
|
112
112
|
},
|
|
113
113
|
"optionalDependencies": {
|
|
114
|
-
"@typecad/
|
|
114
|
+
"@typecad/expect": "1.0.0-alpha.12",
|
|
115
|
+
"@typecad/framework-native": "1.0.0-alpha.12"
|
|
115
116
|
},
|
|
116
117
|
"devDependencies": {
|
|
117
118
|
"@types/node": "^22.10.7"
|