@typecad/cuttlefish 1.0.0-alpha.13 → 1.0.0-alpha.15
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/README.md +82 -79
- package/assets/editor-extensions/typecad-debug/README.md +141 -0
- package/assets/editor-extensions/typecad-debug/out/extension.js +347 -0
- package/assets/editor-extensions/typecad-debug/out/extension.js.map +1 -0
- package/assets/editor-extensions/typecad-debug/package.json +76 -0
- package/assets/editor-extensions/typecad-ui/LICENSE +27 -0
- package/assets/editor-extensions/typecad-ui/README.md +52 -0
- package/assets/editor-extensions/typecad-ui/file-icon-dark.png +0 -0
- package/assets/editor-extensions/typecad-ui/file-icon-light.png +0 -0
- package/assets/editor-extensions/typecad-ui/icon.png +0 -0
- package/assets/editor-extensions/typecad-ui/language-configuration.json +43 -0
- package/assets/editor-extensions/typecad-ui/package.json +54 -0
- package/assets/editor-extensions/typecad-ui/snippets/typecad-ui.json +80 -0
- package/assets/editor-extensions/typecad-ui/syntaxes/markdown-ui.json +45 -0
- package/assets/editor-extensions/typecad-ui/syntaxes/typecad-ui.tmLanguage.json +1269 -0
- package/dist/api/config.d.ts +30 -27
- package/dist/api/schema/types.d.ts +20 -3
- package/dist/api/shared/async-runtime-static.d.ts +1 -1
- package/dist/api/shared/async-runtime-static.js +5 -5
- package/dist/api/shared/async-types.d.ts +3 -2
- package/dist/api/shared/board-catalog.d.ts +2 -0
- package/dist/api/shared/board-catalog.js +6 -0
- package/dist/api/shared/board-resolver.d.ts +4 -7
- package/dist/api/shared/board-resolver.js +2 -1
- package/dist/api/shared/coop-scheduler.d.ts +1 -1
- package/dist/api/shared/coop-scheduler.js +1 -1
- package/dist/api/shared/cpp-type-ir.d.ts +1 -1
- package/dist/api/shared/cpp-type-ir.js +1 -1
- package/dist/api/shared/display-profile.d.ts +1 -1
- package/dist/api/shared/display-profile.js +3 -5
- package/dist/api/shared/framework-manifest-registry.d.ts +1 -1
- package/dist/api/shared/framework-manifest-registry.js +0 -1
- package/dist/api/shared/framework-manifest.d.ts +65 -507
- package/dist/api/shared/framework-manifest.js +6 -11
- package/dist/api/shared/hal-op-ir.d.ts +294 -685
- package/dist/api/shared/hal-op-ir.js +31 -65
- package/dist/api/shared/index.d.ts +0 -4
- package/dist/api/shared/index.js +1 -7
- package/dist/api/shared/ir-core.d.ts +6 -0
- package/dist/api/shared/ir-declarations.d.ts +4 -0
- package/dist/api/shared/ir.d.ts +1 -3
- package/dist/api/shared/platform-strategy.d.ts +14 -37
- package/dist/api/shared/polyfill-helper-registry.js +0 -5
- package/dist/api/shared/polyfill-types.js +0 -27
- package/dist/api/shared/promise-runtime.js +3 -3
- package/dist/api/shared/spdx-licenses.d.ts +12 -10
- package/dist/api/shared/spdx-licenses.js +1 -1
- package/dist/api/shared/toolchain-types.d.ts +1 -1
- package/dist/api/shared/toolchain-types.js +3 -3
- package/dist/api/shared/types.d.ts +0 -3
- package/dist/api/shared/validate-framework-manifest.js +13 -33
- package/dist/board-catalog/dts-reader.d.ts +220 -0
- package/dist/board-catalog/dts-reader.js +1052 -0
- package/dist/board-catalog/index.d.ts +11 -0
- package/dist/board-catalog/index.js +12 -0
- package/dist/board-catalog/pinconfig.d.ts +48 -0
- package/dist/board-catalog/pinconfig.js +263 -0
- package/dist/board-catalog/sdk.d.ts +82 -0
- package/dist/board-catalog/sdk.js +180 -0
- package/dist/board-catalog/store.d.ts +196 -0
- package/dist/board-catalog/store.js +363 -0
- package/dist/board-catalog/types.d.ts +233 -0
- package/dist/board-catalog/types.js +7 -0
- package/dist/board-catalog/walker.d.ts +112 -0
- package/dist/board-catalog/walker.js +1101 -0
- package/dist/cli-utils.d.ts +2 -2
- package/dist/cli-utils.js +5 -5
- package/dist/cli.js +227 -244
- package/dist/config-loader.d.ts +14 -19
- package/dist/config-loader.js +242 -78
- package/dist/config-schema.d.ts +19 -227
- package/dist/config-schema.js +9 -8
- package/dist/contract/board-generator.d.ts +4 -4
- package/dist/contract/board-generator.js +16 -5
- package/dist/contract/contract-parser.d.ts +36 -127
- package/dist/contract/contract-parser.js +76 -0
- package/dist/contract/index.d.ts +1 -1
- package/dist/contract/index.js +70 -21
- package/dist/create/active-board-catalog.d.ts +9 -0
- package/dist/create/active-board-catalog.js +94 -0
- package/dist/create/board-catalog.generated.d.ts +96 -0
- package/dist/create/board-catalog.generated.js +128737 -0
- package/dist/create/board-search.d.ts +23 -0
- package/dist/create/board-search.js +88 -0
- package/dist/create/editor-integration.d.ts +32 -0
- package/dist/create/editor-integration.js +257 -0
- package/dist/create/eslint-rules-template.d.ts +2 -2
- package/dist/create/framework-catalog.d.ts +48 -8
- package/dist/create/framework-catalog.js +58 -44
- package/dist/create/index.d.ts +8 -13
- package/dist/create/index.js +5 -9
- package/dist/create/mcu-target.d.ts +39 -0
- package/dist/create/mcu-target.js +80 -0
- package/dist/create/pack-targets.d.ts +19 -0
- package/dist/create/pack-targets.js +56 -0
- package/dist/create/{init-scaffold.d.ts → scaffold.d.ts} +8 -11
- package/dist/create/{init-scaffold.js → scaffold.js} +28 -87
- package/dist/create/templates.d.ts +51 -0
- package/dist/create/{init-templates.js → templates.js} +135 -116
- package/dist/create/wizard.d.ts +19 -0
- package/dist/create/wizard.js +314 -0
- package/dist/debug/preprocessor.d.ts +5 -0
- package/dist/debug/preprocessor.js +31 -7
- package/dist/diagnostics/diagnostics-report.d.ts +1 -1
- package/dist/diagnostics/diagnostics-report.js +3 -11
- package/dist/diagnostics/json-schema.d.ts +1 -1
- package/dist/diagnostics/md-writer.js +2 -2
- package/dist/diagnostics/mermaid-builder.d.ts +1 -1
- package/dist/diagnostics/mermaid-builder.js +1 -1
- package/dist/emit/cpp-emitter.js +4 -1
- package/dist/emit/emitters/class-emitter.js +32 -4
- package/dist/emit/emitters/emitter-context.d.ts +11 -0
- package/dist/emit/emitters/entrypoint-synthesizer.js +1 -1
- package/dist/emit/emitters/function-emitter-impl.js +33 -17
- package/dist/emit/emitters/output-finalizer.d.ts +7 -0
- package/dist/emit/emitters/output-finalizer.js +31 -23
- package/dist/emit/emitters/setup.js +147 -63
- package/dist/emit/emitters/top-level-prep.js +1 -1
- package/dist/emit/emitters/type-decl-emitter.js +13 -8
- package/dist/emit/emitters/ui-emitter.js +1 -1
- package/dist/emit/expression-renderer.d.ts +3 -3
- package/dist/emit/expression-renderer.js +17 -7
- package/dist/emit/route-hal-op.js +17 -48
- package/dist/emit/snprintf-helpers.d.ts +1 -3
- package/dist/emit/snprintf-helpers.js +1 -39
- package/dist/emit/statement-renderer.d.ts +0 -4
- package/dist/emit/statement-renderer.js +54 -45
- package/dist/emit/utils/async-state-machine.d.ts +16 -9
- package/dist/emit/utils/async-state-machine.js +56 -54
- package/dist/emit/utils/comment-helpers.d.ts +0 -8
- package/dist/emit/utils/comment-helpers.js +1 -1
- package/dist/emit/utils/cpp-helpers.d.ts +2 -2
- package/dist/emit/utils/cpp-helpers.js +2 -2
- package/dist/emit/utils/hal-op-cpp-type.js +4 -6
- package/dist/emit/utils/index.d.ts +3 -3
- package/dist/emit/utils/index.js +3 -3
- package/dist/emit/utils/type-inference.d.ts +1 -29
- package/dist/emit/utils/type-inference.js +0 -75
- package/dist/framework-package.d.ts +3 -0
- package/dist/framework-package.js +23 -6
- package/dist/framework-registry.d.ts +5 -5
- package/dist/frameworks/native/graphics/terminal-preview.d.ts +5 -0
- package/dist/frameworks/native/graphics/terminal-preview.js +33 -0
- package/dist/frameworks/native/index.d.ts +3 -0
- package/dist/frameworks/native/index.js +15 -0
- package/dist/frameworks/native/native-compile.d.ts +17 -0
- package/dist/frameworks/native/native-compile.js +170 -0
- package/dist/frameworks/native/native-config.d.ts +41 -0
- package/dist/frameworks/native/native-config.js +7 -0
- package/dist/frameworks/native/strategy.d.ts +97 -0
- package/dist/frameworks/native/strategy.js +664 -0
- package/dist/ir/board-resolver.d.ts +18 -23
- package/dist/ir/board-resolver.js +43 -401
- package/dist/ir/build-ir-state.d.ts +3 -0
- package/dist/ir/build-ir-state.js +42 -0
- package/dist/ir/build-ir.d.ts +1 -1
- package/dist/ir/build-ir.js +64 -45
- package/dist/ir/declaration-builders.js +1 -0
- package/dist/ir/expression-to-ir.js +17 -10
- package/dist/ir/hal/hal-emitter.d.ts +0 -2
- package/dist/ir/hal/hal-emitter.js +92 -41
- package/dist/ir/hal/hal-parser.d.ts +5 -5
- package/dist/ir/hal/hal-parser.js +128 -36
- package/dist/ir/hal/hal-plugins.d.ts +0 -1
- package/dist/ir/hal/hal-plugins.js +425 -716
- package/dist/ir/heap-analysis.js +2 -41
- package/dist/ir/identifier-collector.js +32 -20
- package/dist/ir/network-validation.js +7 -11
- package/dist/ir/ownership-analysis.js +75 -13
- package/dist/ir/peripheral-usage.d.ts +8 -2
- package/dist/ir/peripheral-usage.js +78 -149
- package/dist/ir/peripheral-validation.js +23 -3
- package/dist/ir/pin-capability-validation.d.ts +12 -0
- package/dist/ir/pin-capability-validation.js +61 -97
- package/dist/ir/pin-mode-validation.js +5 -5
- package/dist/ir/program-analysis.d.ts +8 -15
- package/dist/ir/program-analysis.js +140 -323
- package/dist/ir/pulldown-validation.js +11 -4
- package/dist/ir/resource-analysis.js +15 -3
- package/dist/ir/statement-to-ir.js +0 -6
- package/dist/ir/timing-validation.js +1 -1
- package/dist/ir/transformers/array-methods.js +11 -5
- package/dist/ir/transformers/call-statement.js +20 -87
- package/dist/ir/transformers/callback-context-registry.d.ts +1 -1
- package/dist/ir/transformers/callback-context-registry.js +8 -16
- package/dist/ir/transformers/control-flow.js +0 -13
- package/dist/ir/transformers/expressions.js +10 -9
- package/dist/ir/transformers/hal-call-resolver.d.ts +2 -2
- package/dist/ir/transformers/hal-call-resolver.js +24 -10
- package/dist/ir/transformers/hal-emit-helpers.d.ts +2 -2
- package/dist/ir/transformers/hal-emit-helpers.js +2 -2
- package/dist/ir/transformers/namespace-methods.js +1 -1
- package/dist/ir/transformers/ui-callback-lowering.d.ts +0 -2
- package/dist/ir/transformers/ui-callback-lowering.js +0 -24
- package/dist/ir/transformers/variables.js +338 -42
- package/dist/ir/try-catch-validation.js +4 -3
- package/dist/ir/type-resolution.js +15 -4
- package/dist/ir/validation-orchestrator.js +0 -9
- package/dist/libdef/cpp-to-decl.d.ts +0 -9
- package/dist/libdef/cpp-to-decl.js +0 -72
- package/dist/libdef/registry.d.ts +0 -1
- package/dist/libdef/registry.js +1 -24
- package/dist/library/catalog.d.ts +35 -0
- package/dist/library/catalog.js +67 -0
- package/dist/library/cli.d.ts +2 -0
- package/dist/library/cli.js +168 -0
- package/dist/library/init.d.ts +25 -0
- package/dist/library/init.js +397 -0
- package/dist/library/install.d.ts +9 -0
- package/dist/library/install.js +80 -0
- package/dist/library/registry-search.d.ts +34 -0
- package/dist/library/registry-search.js +50 -0
- package/dist/library/validate.d.ts +17 -0
- package/dist/library/validate.js +175 -0
- package/dist/library-packages.d.ts +95 -0
- package/dist/library-packages.js +275 -0
- package/dist/mapping/peripheral-names.js +9 -4
- package/dist/mapping/source-map.d.ts +3 -4
- package/dist/mapping/source-map.js +12 -18
- package/dist/orchestrator/graph-builder.d.ts +3 -3
- package/dist/orchestrator/graph-builder.js +29 -12
- package/dist/orchestrator/type-checker.d.ts +3 -3
- package/dist/orchestrator/type-checker.js +3 -3
- package/dist/platform/async-runtime.js +1 -1
- package/dist/platform/coop-scheduler-runtime.js +2 -2
- package/dist/platform/generic-debug-codegen.d.ts +36 -0
- package/dist/platform/generic-debug-codegen.js +185 -0
- package/dist/platform/generic-strategy.d.ts +3 -4
- package/dist/platform/generic-strategy.js +5 -80
- package/dist/platform/index.js +1 -1
- package/dist/platform/registry.d.ts +2 -2
- package/dist/platform/registry.js +2 -2
- package/dist/preview/client.js +17 -3
- package/dist/safety/asil-decorators.d.ts +32 -0
- package/dist/safety/asil-decorators.js +32 -0
- package/dist/safety/authoring.d.ts +85 -0
- package/dist/safety/authoring.js +61 -0
- package/dist/safety/engine.d.ts +3 -0
- package/dist/safety/engine.js +83 -0
- package/dist/safety/hal/ops.d.ts +48 -0
- package/dist/safety/hal/ops.js +46 -0
- package/dist/safety/iso26262/analyze.d.ts +13 -0
- package/dist/safety/iso26262/analyze.js +29 -0
- package/dist/safety/iso26262/asil.d.ts +10 -0
- package/dist/safety/iso26262/asil.js +22 -0
- package/dist/safety/iso26262/collect.d.ts +14 -0
- package/dist/safety/iso26262/collect.js +137 -0
- package/dist/safety/iso26262/goto-checker.d.ts +10 -0
- package/dist/safety/iso26262/goto-checker.js +72 -0
- package/dist/safety/iso26262/heap-checker.d.ts +7 -0
- package/dist/safety/iso26262/heap-checker.js +57 -0
- package/dist/safety/iso26262/init-checker.d.ts +13 -0
- package/dist/safety/iso26262/init-checker.js +127 -0
- package/dist/safety/iso26262/loop-checker.d.ts +7 -0
- package/dist/safety/iso26262/loop-checker.js +86 -0
- package/dist/safety/iso26262/recursion-checker.d.ts +9 -0
- package/dist/safety/iso26262/recursion-checker.js +134 -0
- package/dist/safety/iso26262/sidecar-writer.d.ts +25 -0
- package/dist/safety/iso26262/sidecar-writer.js +13 -0
- package/dist/safety/passes/pinMode-intercept.d.ts +3 -0
- package/dist/safety/passes/pinMode-intercept.js +60 -0
- package/dist/safety/runtime/mode-table.d.ts +5 -0
- package/dist/safety/runtime/mode-table.js +55 -0
- package/dist/safety/runtime/polyfills.d.ts +6 -0
- package/dist/safety/runtime/polyfills.js +12 -0
- package/dist/safety/runtime/result-struct.d.ts +11 -0
- package/dist/safety/runtime/result-struct.js +98 -0
- package/dist/safety/runtime/safe-int.d.ts +33 -0
- package/dist/safety/runtime/safe-int.js +170 -0
- package/dist/safety/runtime/safe-traits.d.ts +4 -0
- package/dist/safety/runtime/safe-traits.js +95 -0
- package/dist/safety/runtime/safe-variable.d.ts +39 -0
- package/dist/safety/runtime/safe-variable.js +226 -0
- package/dist/safety/runtime/vote.d.ts +8 -0
- package/dist/safety/runtime/vote.js +92 -0
- package/dist/safety/runtime/write-verify.d.ts +8 -0
- package/dist/safety/runtime/write-verify.js +71 -0
- package/dist/safety/safe-int-types.d.ts +45 -0
- package/dist/safety/safe-int-types.js +25 -0
- package/dist/safety/safe-variable-types.d.ts +29 -0
- package/dist/safety/safe-variable-types.js +19 -0
- package/dist/safety/safety-bridge.d.ts +5 -6
- package/dist/safety/safety-bridge.js +15 -28
- package/dist/safety/sidecar-bridge.d.ts +1 -3
- package/dist/safety/sidecar-bridge.js +3 -20
- package/dist/safety/specifiers.d.ts +6 -0
- package/dist/safety/specifiers.js +14 -0
- package/dist/safety-hook.d.ts +8 -7
- package/dist/safety-hook.js +12 -13
- package/dist/testing.d.ts +5 -6
- package/dist/testing.js +5 -5
- package/dist/transpile/resolution.d.ts +1 -1
- package/dist/transpile/resolution.js +18 -9
- package/dist/transpile/test-pins.d.ts +18 -0
- package/dist/transpile/test-pins.js +142 -0
- package/dist/transpile.d.ts +1 -2
- package/dist/transpile.js +90 -29
- package/dist/types.d.ts +45 -26
- package/dist/ui-hook.d.ts +1 -1
- package/dist/utils/cli.d.ts +2 -2
- package/dist/utils/cli.js +160 -157
- package/dist/utils/fs.d.ts +3 -4
- package/dist/utils/fs.js +4 -5
- package/dist/utils/strings.d.ts +0 -1
- package/dist/utils/strings.js +0 -8
- package/dist/utils/ui.d.ts +5 -2
- package/dist/utils/ui.js +13 -3
- package/package.json +158 -149
- package/dist/add-preset.d.ts +0 -4
- package/dist/add-preset.js +0 -74
- package/dist/api/shared/async-symbol-detector.d.ts +0 -11
- package/dist/api/shared/async-symbol-detector.js +0 -140
- package/dist/api/shared/native-display-op-resolver.d.ts +0 -10
- package/dist/api/shared/native-display-op-resolver.js +0 -64
- package/dist/create/board-checklist.d.ts +0 -2
- package/dist/create/board-checklist.js +0 -52
- package/dist/create/board-codegen.d.ts +0 -9
- package/dist/create/board-codegen.js +0 -238
- package/dist/create/board-generators.d.ts +0 -12
- package/dist/create/board-generators.js +0 -651
- package/dist/create/board-spec.d.ts +0 -637
- package/dist/create/board-spec.js +0 -214
- package/dist/create/init-templates.d.ts +0 -28
- package/dist/create/init-wizard.d.ts +0 -8
- package/dist/create/init-wizard.js +0 -182
- package/dist/install/framework-catalog.d.ts +0 -53
- package/dist/install/framework-catalog.js +0 -107
- package/dist/install/handle-install.d.ts +0 -35
- package/dist/install/handle-install.js +0 -177
- package/dist/install/index.d.ts +0 -4
- package/dist/install/index.js +0 -3
- package/dist/ir/pin-state-tracking.d.ts +0 -58
- package/dist/ir/pin-state-tracking.js +0 -182
- package/dist/ir/pwm-timer-sharing.d.ts +0 -4
- package/dist/ir/pwm-timer-sharing.js +0 -94
- package/dist/ir/timer0-pwm-timing-conflict.d.ts +0 -4
- package/dist/ir/timer0-pwm-timing-conflict.js +0 -72
- package/dist/libdef/component-decls.d.ts +0 -2
- package/dist/libdef/component-decls.js +0 -6
- package/dist/libdef/component-discovery.d.ts +0 -43
- package/dist/libdef/component-discovery.js +0 -83
- package/dist/theme-tokens.d.ts +0 -22
- package/dist/theme-tokens.js +0 -172
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import * as readline from "node:readline/promises";
|
|
2
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
4
|
+
import { readdirSync } from "node:fs";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import { KNOWN_TARGETS } from "./scaffold.js";
|
|
7
|
+
import { findPackBoard, packBoardAsTarget } from "./pack-targets.js";
|
|
8
|
+
import { pickTarget } from "./board-search.js";
|
|
9
|
+
import { activeBoardCatalog } from "../board-catalog/index.js";
|
|
10
|
+
import { frameworksForTarget, frameworkCatalogEntry, frameworkCompatibleWithTarget, FRAMEWORK_CATALOG, frameworkTargetProfile, probeMethodsForBoard } from './framework-catalog.js';
|
|
11
|
+
/**
|
|
12
|
+
* Attached serial ports for the wizard's port question — dependency-free
|
|
13
|
+
* (the wizard runs before any project deps exist, and cuttlefish itself
|
|
14
|
+
* carries no native serialport binding). Windows asks the .NET SerialPort
|
|
15
|
+
* class via PowerShell; macOS/Linux scan /dev for USB CDC/bridge nodes.
|
|
16
|
+
* Returns [] when enumeration fails — the question then falls back to a
|
|
17
|
+
* free-text prompt with the platform hint as default.
|
|
18
|
+
*/
|
|
19
|
+
export function detectSerialPorts() {
|
|
20
|
+
try {
|
|
21
|
+
if (process.platform === "win32") {
|
|
22
|
+
const out = execSync("[System.IO.Ports.SerialPort]::GetPortNames() -join ','", { shell: "powershell.exe", stdio: ["ignore", "pipe", "ignore"], timeout: 5000 }).toString();
|
|
23
|
+
return out.split(",").map((s) => s.trim()).filter((s) => /^[Cc][Oo][Mm]\d+$/.test(s));
|
|
24
|
+
}
|
|
25
|
+
const dev = readdirSync("/dev");
|
|
26
|
+
return dev
|
|
27
|
+
.filter((n) => /^tty(ACM|USB)/.test(n) || /^cu\.(usb|USB|modem)/.test(n))
|
|
28
|
+
.map((n) => (process.platform === "darwin" ? `/dev/${n}` : `/dev/${n}`))
|
|
29
|
+
.sort();
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function validateProjectName(name) {
|
|
36
|
+
if (!name || name.trim().length === 0) {
|
|
37
|
+
return "Project name cannot be empty.";
|
|
38
|
+
}
|
|
39
|
+
const normalized = name.trim().toLowerCase();
|
|
40
|
+
if (!/^[a-z][a-z0-9-]*$/.test(normalized)) {
|
|
41
|
+
return "Project name must start with a letter and contain only lowercase letters, digits, and hyphens.";
|
|
42
|
+
}
|
|
43
|
+
if (normalized.length > 64) {
|
|
44
|
+
return "Project name must be 64 characters or fewer.";
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
async function promptText(rl, prompt, defaultValue, validate) {
|
|
49
|
+
while (true) {
|
|
50
|
+
const suffix = defaultValue ? ` (${defaultValue})` : "";
|
|
51
|
+
const answer = await rl.question(`${chalk.cyan("?")} ${prompt}${suffix}: `);
|
|
52
|
+
const value = (answer.trim() || (defaultValue ?? "")).trim();
|
|
53
|
+
if (validate) {
|
|
54
|
+
const error = validate(value);
|
|
55
|
+
if (error) {
|
|
56
|
+
console.log(` ${chalk.red("✗")} ${error}`);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return value;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async function promptSelect(rl, prompt, options) {
|
|
64
|
+
console.log(`${chalk.cyan("?")} ${prompt}:`);
|
|
65
|
+
for (let i = 0; i < options.length; i++) {
|
|
66
|
+
console.log(` ${chalk.dim(`${i + 1})`)} ${options[i].label}`);
|
|
67
|
+
}
|
|
68
|
+
while (true) {
|
|
69
|
+
const answer = await rl.question(` Enter number (1-${options.length}): `);
|
|
70
|
+
const idx = parseInt(answer.trim(), 10) - 1;
|
|
71
|
+
if (idx >= 0 && idx < options.length) {
|
|
72
|
+
return options[idx].value;
|
|
73
|
+
}
|
|
74
|
+
console.log(` ${chalk.red("✗")} Please enter a number between 1 and ${options.length}.`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function promptConfirm(rl, prompt, defaultValue) {
|
|
78
|
+
const suffix = defaultValue ? " (Y/n)" : " (y/N)";
|
|
79
|
+
const answer = await rl.question(`${chalk.cyan("?")} ${prompt}${suffix}: `);
|
|
80
|
+
const trimmed = answer.trim().toLowerCase();
|
|
81
|
+
if (trimmed === "")
|
|
82
|
+
return defaultValue;
|
|
83
|
+
return trimmed === "y" || trimmed === "yes";
|
|
84
|
+
}
|
|
85
|
+
export async function runCreateWizard(partialOptions) {
|
|
86
|
+
let rl = readline.createInterface({ input, output });
|
|
87
|
+
try {
|
|
88
|
+
console.log();
|
|
89
|
+
console.log(chalk.cyan("⤳ Cuttlefish") + chalk.dim(" — Project Setup"));
|
|
90
|
+
console.log();
|
|
91
|
+
// 1. Project name
|
|
92
|
+
const projectName = partialOptions?.projectName
|
|
93
|
+
?? await promptText(rl, "Project name", "my-project", validateProjectName);
|
|
94
|
+
// 2. Target selection — the filterable board picker covers everything:
|
|
95
|
+
// native, bare silicon (soc entries), and every Zephyr board from the
|
|
96
|
+
// catalog. One search box, no two-stage list. A preseeded --board that
|
|
97
|
+
// resolves skips the picker entirely.
|
|
98
|
+
let packBoard;
|
|
99
|
+
let foundTarget;
|
|
100
|
+
if (partialOptions?.board) {
|
|
101
|
+
const found = KNOWN_TARGETS.find((t) => t.id === partialOptions.board);
|
|
102
|
+
const packFound = found ? undefined : findPackBoard(partialOptions.board);
|
|
103
|
+
if (found) {
|
|
104
|
+
foundTarget = found;
|
|
105
|
+
console.log(`${chalk.cyan("?")} Target: ${chalk.white(found.displayName)} (${chalk.dim(partialOptions.board)})`);
|
|
106
|
+
}
|
|
107
|
+
else if (packFound) {
|
|
108
|
+
packBoard = packFound;
|
|
109
|
+
console.log(`${chalk.cyan("?")} Target: ${chalk.white(packFound.name)} (${chalk.dim(packFound.identifier)})`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (Object.keys(activeBoardCatalog()).length === 0) {
|
|
113
|
+
console.log(` ${chalk.yellow("!")} No board catalog on this machine — the catalog is generated`);
|
|
114
|
+
console.log(` from your Zephyr tree. Run the installer or 'cuttlefish board sync' to`);
|
|
115
|
+
console.log(` list boards; a preseeded --board <target> still works and validates at`);
|
|
116
|
+
console.log(` the first build.`);
|
|
117
|
+
}
|
|
118
|
+
if (!foundTarget && !packBoard) {
|
|
119
|
+
// Close the readline interface for the filterable select (it owns
|
|
120
|
+
// stdin in raw mode), then recreate it for the remaining prompts.
|
|
121
|
+
// Closing a readline pauses stdin; resume it so the select's internal
|
|
122
|
+
// readline receives character bytes (arrow keys arrive via keypress
|
|
123
|
+
// events either way, but typed characters need flowing mode).
|
|
124
|
+
rl.close();
|
|
125
|
+
input.resume();
|
|
126
|
+
const pick = await pickTarget();
|
|
127
|
+
rl = readline.createInterface({ input, output });
|
|
128
|
+
if (!pick) {
|
|
129
|
+
console.log(` ${chalk.yellow("!")} Target selection cancelled.`);
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
if (pick.kind === 'native') {
|
|
133
|
+
foundTarget = KNOWN_TARGETS.find((t) => t.isNative);
|
|
134
|
+
}
|
|
135
|
+
else if (pick.kind === 'mcu') {
|
|
136
|
+
// Bare-silicon targets were removed with the curated soc layer.
|
|
137
|
+
console.log(` ${chalk.yellow("!")} Bare-silicon targets no longer exist — pick a board from the catalog.`);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
packBoard = pick.entry;
|
|
141
|
+
console.log(`${chalk.cyan("?")} Target: ${chalk.white(pick.entry.name)} (${chalk.dim(pick.entry.identifier)})`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const target = packBoard
|
|
145
|
+
? packBoardAsTarget(packBoard)
|
|
146
|
+
: foundTarget;
|
|
147
|
+
// 3. Framework. Narrow to the frameworks compatible with the selected board
|
|
148
|
+
// (see framework-catalog), then let the user pick. The chosen package is
|
|
149
|
+
// installed into the new project by `cuttlefish create`, so we offer every
|
|
150
|
+
// compatible framework regardless of what is currently installed — no
|
|
151
|
+
// require.resolve discovery (which also avoided an ESM `require` pitfall
|
|
152
|
+
// where the lookup always failed and made the wizard dead-end).
|
|
153
|
+
let framework = target.framework;
|
|
154
|
+
let frameworkPackage = target.frameworkPackage;
|
|
155
|
+
const compatible = frameworksForTarget(target)
|
|
156
|
+
.filter((f) => f.installable);
|
|
157
|
+
if (compatible.length === 0) {
|
|
158
|
+
// Defensive: every known target maps to at least one framework in the catalog.
|
|
159
|
+
throw new Error(`No installable frameworks are compatible with target '${target.id}'.`);
|
|
160
|
+
}
|
|
161
|
+
if (partialOptions?.framework) {
|
|
162
|
+
const requested = frameworkCatalogEntry(partialOptions.framework);
|
|
163
|
+
if (!requested) {
|
|
164
|
+
const available = FRAMEWORK_CATALOG.filter(f => f.installable).map(f => f.id).join(", ");
|
|
165
|
+
throw new Error(`Unknown framework '${partialOptions.framework}'. Available: ${available}`);
|
|
166
|
+
}
|
|
167
|
+
// Same compatibility guard as the non-interactive CLI path: a preseeded
|
|
168
|
+
// --framework bypasses the narrowing below, so validate it explicitly.
|
|
169
|
+
if (!frameworkCompatibleWithTarget(target, requested.id)) {
|
|
170
|
+
const list = compatible.map(f => f.id).join(", ");
|
|
171
|
+
throw new Error(`Framework '${requested.id}' is not compatible with target '${target.id}' (${target.displayName}). ` +
|
|
172
|
+
`Compatible frameworks: ${list}`);
|
|
173
|
+
}
|
|
174
|
+
framework = requested.id;
|
|
175
|
+
frameworkPackage = requested.packageName;
|
|
176
|
+
console.log(`${chalk.cyan("?")} Framework: ${chalk.white(requested.label)}`);
|
|
177
|
+
}
|
|
178
|
+
else if (compatible.length === 1) {
|
|
179
|
+
framework = compatible[0].id;
|
|
180
|
+
frameworkPackage = compatible[0].packageName;
|
|
181
|
+
console.log(`${chalk.cyan("?")} Framework: ${chalk.white(compatible[0].label)}`);
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
const selected = await promptSelect(rl, "Framework", compatible.map((f) => ({ label: f.label, value: f.id })));
|
|
185
|
+
const match = compatible.find((f) => f.id === selected);
|
|
186
|
+
framework = match.id;
|
|
187
|
+
frameworkPackage = match.packageName;
|
|
188
|
+
}
|
|
189
|
+
// Resolve the framework-specific build target + toolchain (the Zephyr
|
|
190
|
+
// board id + 'west'). See framework-catalog.
|
|
191
|
+
const profile = frameworkTargetProfile(target, framework);
|
|
192
|
+
let buildTarget = profile.buildTarget ?? target.buildTarget;
|
|
193
|
+
let zephyrCustomBoard = false;
|
|
194
|
+
// 3.5 Probe method (Zephyr boards that ship a table). The probe in the
|
|
195
|
+
// user's hand becomes the scaffolded zephyr.probe entry — it serves both
|
|
196
|
+
// flashing and debugging; "board default" omits the section.
|
|
197
|
+
let probeMethod;
|
|
198
|
+
const probeMethods = framework === 'zephyr' ? probeMethodsForBoard(target.id) : [];
|
|
199
|
+
if (probeMethods.length > 0) {
|
|
200
|
+
if (partialOptions?.probe) {
|
|
201
|
+
probeMethod = partialOptions.probe;
|
|
202
|
+
console.log(`${chalk.cyan("?")} Probe method: ${chalk.white(probeMethod)}`);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
const chosen = await promptSelect(rl, "Which probe will you attach to this board?", [
|
|
206
|
+
...probeMethods.map((m) => ({ label: `${m.id} — ${m.description}`, value: m.id })),
|
|
207
|
+
{ label: "board default (no zephyr.probe entry)", value: "" },
|
|
208
|
+
]);
|
|
209
|
+
probeMethod = chosen === "" ? undefined : chosen;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// 4. Serial port (only for embedded) — the port the board rides on.
|
|
213
|
+
// Detected ports are offered as a choice; the answer seeds test.port in
|
|
214
|
+
// the scaffolded config (no more guessing COM4). "skip" leaves the
|
|
215
|
+
// platform-hint placeholder.
|
|
216
|
+
let serialPort;
|
|
217
|
+
if (!target.isNative) {
|
|
218
|
+
if (partialOptions?.port) {
|
|
219
|
+
serialPort = partialOptions.port;
|
|
220
|
+
console.log(`${chalk.cyan("?")} Serial port: ${chalk.white(serialPort)}`);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
const detected = detectSerialPorts();
|
|
224
|
+
if (detected.length > 0) {
|
|
225
|
+
const chosenPort = await promptSelect(rl, "Which serial port is the board attached to?", [
|
|
226
|
+
...detected.map((p) => ({ label: p, value: p })),
|
|
227
|
+
{ label: "not listed / set later (type a path)", value: "__other__" },
|
|
228
|
+
{ label: "skip — use the placeholder, configure later", value: "" },
|
|
229
|
+
]);
|
|
230
|
+
serialPort = chosenPort === "" ? undefined
|
|
231
|
+
: chosenPort === "__other__" ? await promptText(rl, "Serial port path", detected[0])
|
|
232
|
+
: chosenPort;
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
const hint = process.platform === "win32" ? "COM4" : "/dev/ttyACM0";
|
|
236
|
+
const typed = await promptText(rl, "Serial port (none detected — plug the board in, or type a path)", hint);
|
|
237
|
+
serialPort = typed === hint ? undefined : typed;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
// 5. Baud rate (only for embedded)
|
|
242
|
+
let baudRate;
|
|
243
|
+
if (!target.isNative) {
|
|
244
|
+
// Zephyr consoles default to 115200; 9600 is the Arduino-class default.
|
|
245
|
+
const defaultBaud = framework === 'zephyr' ? 115200 : 9600;
|
|
246
|
+
if (partialOptions?.baud) {
|
|
247
|
+
baudRate = partialOptions.baud;
|
|
248
|
+
console.log(`${chalk.cyan("?")} Serial baud rate: ${chalk.white(baudRate)}`);
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
const baudAnswer = await promptText(rl, "Serial baud rate", String(defaultBaud));
|
|
252
|
+
baudRate = parseInt(baudAnswer, 10);
|
|
253
|
+
if (Number.isNaN(baudRate) || baudRate <= 0) {
|
|
254
|
+
baudRate = defaultBaud;
|
|
255
|
+
console.log(` ${chalk.dim(`Using default: ${defaultBaud}`)}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// 5. Starter program
|
|
260
|
+
let includeStarter;
|
|
261
|
+
if (partialOptions?.noStarter) {
|
|
262
|
+
includeStarter = false;
|
|
263
|
+
console.log(`${chalk.cyan("?")} Create starter program: ${chalk.dim("no")}`);
|
|
264
|
+
}
|
|
265
|
+
else if (partialOptions?.noStarter === false) {
|
|
266
|
+
includeStarter = true;
|
|
267
|
+
console.log(`${chalk.cyan("?")} Create starter program: ${chalk.green("yes")}`);
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
includeStarter = await promptConfirm(rl, "Create starter program?", true);
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
probeMethod,
|
|
274
|
+
probeMethods,
|
|
275
|
+
projectName,
|
|
276
|
+
targetId: target.id,
|
|
277
|
+
targetDisplayName: target.displayName,
|
|
278
|
+
isNative: target.isNative,
|
|
279
|
+
architecture: target.architecture,
|
|
280
|
+
board: target.board,
|
|
281
|
+
// Pack fact: does the board's devicetree declare an LED? Drives the
|
|
282
|
+
// starter between LED-blink and an I/O skeleton.
|
|
283
|
+
...(target.board ? { hasLed: Boolean(activeBoardCatalog()[target.board]?.led) } : {}),
|
|
284
|
+
...(serialPort ? { port: serialPort } : {}),
|
|
285
|
+
frameworkPackage: frameworkPackage ?? '',
|
|
286
|
+
framework: framework ?? '',
|
|
287
|
+
buildTarget,
|
|
288
|
+
...(profile.toolchainType ? { toolchainType: profile.toolchainType } : {}),
|
|
289
|
+
// soc rides the config only for native projects — a board target's
|
|
290
|
+
// soc derives from the identifier at boardgen time.
|
|
291
|
+
...(target.board ? {} : { soc: target.soc }),
|
|
292
|
+
...(zephyrCustomBoard ? { zephyrCustomBoard: true } : {}),
|
|
293
|
+
baudRate,
|
|
294
|
+
includeStarter,
|
|
295
|
+
...(target.frameworkData
|
|
296
|
+
? { frameworkData: target.frameworkData }
|
|
297
|
+
: {}),
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
catch (err) {
|
|
301
|
+
if (err && typeof err === 'object' && err.code === 'ERR_USE_AFTER_CLOSE') {
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
throw err;
|
|
305
|
+
}
|
|
306
|
+
finally {
|
|
307
|
+
// The board picker closes rl itself (it hands stdin to the filterable
|
|
308
|
+
// select); a second close is harmless but explicit is cleaner.
|
|
309
|
+
try {
|
|
310
|
+
rl.close();
|
|
311
|
+
}
|
|
312
|
+
catch { /* already closed */ }
|
|
313
|
+
}
|
|
314
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BreakpointMap } from './types.js';
|
|
2
|
+
import type { PlatformStrategy } from '../api/shared/platform-strategy.js';
|
|
2
3
|
export declare function preprocess(options: {
|
|
3
4
|
/** The source file path (for line number calculation) */
|
|
4
5
|
fileName: string;
|
|
@@ -6,4 +7,8 @@ export declare function preprocess(options: {
|
|
|
6
7
|
breakpoints: BreakpointMap;
|
|
7
8
|
/** The source text to transform */
|
|
8
9
|
source: string;
|
|
10
|
+
/** The platform strategy that will emit this file — its debug dialect
|
|
11
|
+
* (generateDebug*Code) is used. Omit when unknown; the global loaded
|
|
12
|
+
* framework is consulted, then the generic printf codegen. */
|
|
13
|
+
strategy?: PlatformStrategy;
|
|
9
14
|
}): string;
|
|
@@ -10,20 +10,32 @@ import path from 'node:path';
|
|
|
10
10
|
import { getBreakpointsForFile } from './breakpoint-loader.js';
|
|
11
11
|
import { getLoadedFramework, hasLoadedFramework } from '../framework-registry.js';
|
|
12
12
|
import { GenericStrategy } from '../platform/generic-strategy.js';
|
|
13
|
-
function getDebugStrategy() {
|
|
13
|
+
function getDebugStrategy(explicit) {
|
|
14
|
+
// The strategy that will EMIT this file decides the debug dialect — its
|
|
15
|
+
// codegen must match the runtime the emitted code links against. The
|
|
16
|
+
// global loaded-framework registry is only a fallback for callers that
|
|
17
|
+
// don't know their strategy (picking it blind once emitted Zephyr printk
|
|
18
|
+
// code into a native build).
|
|
19
|
+
if (explicit) {
|
|
20
|
+
if (explicit.generateDebugInitCode)
|
|
21
|
+
return explicit;
|
|
22
|
+
// The emitting strategy has no debug dialect (e.g. NativeStrategy) —
|
|
23
|
+
// its embedded fallback is the generic printf codegen.
|
|
24
|
+
return new GenericStrategy();
|
|
25
|
+
}
|
|
14
26
|
if (hasLoadedFramework()) {
|
|
15
27
|
const { strategy } = getLoadedFramework();
|
|
16
28
|
if (strategy.generateDebugInitCode)
|
|
17
29
|
return strategy;
|
|
18
30
|
}
|
|
19
|
-
// Fall back to the generic strategy's
|
|
31
|
+
// Fall back to the generic strategy's printf-based debug output
|
|
20
32
|
return new GenericStrategy();
|
|
21
33
|
}
|
|
22
34
|
// ---------------------------------------------------------------------------
|
|
23
35
|
// Public API
|
|
24
36
|
// ---------------------------------------------------------------------------
|
|
25
37
|
export function preprocess(options) {
|
|
26
|
-
const { fileName, breakpoints, source } = options;
|
|
38
|
+
const { fileName, breakpoints, source, strategy: emittingStrategy } = options;
|
|
27
39
|
const breakpointLines = getBreakpointsForFile(breakpoints, fileName);
|
|
28
40
|
if (breakpointLines.length === 0) {
|
|
29
41
|
return source; // No breakpoints, return unchanged
|
|
@@ -39,7 +51,7 @@ export function preprocess(options) {
|
|
|
39
51
|
}
|
|
40
52
|
// Collect variables in scope at each breakpoint
|
|
41
53
|
const scopeAnalyzer = new ScopeAnalyzer(sf);
|
|
42
|
-
const debugStrategy = getDebugStrategy();
|
|
54
|
+
const debugStrategy = getDebugStrategy(emittingStrategy);
|
|
43
55
|
// Per-file breakpoint ID counter. Each HALTING breakpoint gets a stable
|
|
44
56
|
// integer ID so the codegen can emit a per-breakpoint disable flag (skipped
|
|
45
57
|
// via the 's' key at runtime). IDs are scoped to this file and reset per
|
|
@@ -198,7 +210,11 @@ class ScopeAnalyzer {
|
|
|
198
210
|
// so a local declared on line N is not yet in scope at line N. Params and
|
|
199
211
|
// enclosing-scope vars are in scope from the function's first line.
|
|
200
212
|
const recordFunction = (node, currentVars) => {
|
|
201
|
-
|
|
213
|
+
// Scope starts at the BODY, not the signature: a breakpoint on the
|
|
214
|
+
// signature line is injected BEFORE the function exists (top-level
|
|
215
|
+
// position), so params are not in scope there — treating it as
|
|
216
|
+
// in-function dumped undeclared identifiers into main().
|
|
217
|
+
const startLine = this.sf.getLineAndCharacterOfPosition((node.body ?? node).getStart()).line;
|
|
202
218
|
const endLine = node.body
|
|
203
219
|
? this.sf.getLineAndCharacterOfPosition(node.body.getEnd()).line
|
|
204
220
|
: startLine;
|
|
@@ -227,9 +243,17 @@ class ScopeAnalyzer {
|
|
|
227
243
|
this.functionVars.set(line, [...alwaysInScope, ...localsInScope]);
|
|
228
244
|
}
|
|
229
245
|
};
|
|
230
|
-
// Find top-level functions, then recurse into their bodies for nested
|
|
246
|
+
// Find top-level functions, then recurse into their bodies for nested
|
|
247
|
+
// ones. Top-level functions start from an EMPTY enclosing scope: the
|
|
248
|
+
// module-scope variables live in the enclosing scope chain only for
|
|
249
|
+
// NESTED functions. Top-level statements lower into main() locals, so a
|
|
250
|
+
// module-scope name is not visible inside a top-level function — dumping
|
|
251
|
+
// one there emitted references to undeclared identifiers (a breakpoint in
|
|
252
|
+
// any function flagged every top-level const). A breakpoint placed on a
|
|
253
|
+
// top-level statement still sees them via getVariablesInScope's top-level
|
|
254
|
+
// path, filtered by declaration order.
|
|
231
255
|
for (const stmt of this.sf.statements) {
|
|
232
|
-
this.findAndRecordFunctions(stmt,
|
|
256
|
+
this.findAndRecordFunctions(stmt, [], recordFunction);
|
|
233
257
|
}
|
|
234
258
|
}
|
|
235
259
|
/** Collect variables declared directly by a single statement (top-level use). */
|
|
@@ -15,7 +15,7 @@ export interface DiagnosticsReportInput {
|
|
|
15
15
|
/** Target architecture (e.g. "avr", "esp32") */
|
|
16
16
|
target: string;
|
|
17
17
|
/** Board package name */
|
|
18
|
-
|
|
18
|
+
boardTarget?: string;
|
|
19
19
|
/** Framework package name */
|
|
20
20
|
frameworkPackage?: string;
|
|
21
21
|
/** Build target identifier */
|
|
@@ -102,7 +102,7 @@ function buildPinUsage(program, boardPinsData) {
|
|
|
102
102
|
const usage = program.peripheralUsage;
|
|
103
103
|
// Map board pins to GPIO entries
|
|
104
104
|
for (const pin of boardPinsData) {
|
|
105
|
-
// Only include pins that are explicitly used in the
|
|
105
|
+
// Only include pins that are explicitly used in the program to keep the table clean
|
|
106
106
|
const isExplicitlyUsed = usage.pinsUsed.has(pin.name) ||
|
|
107
107
|
pin.aliases.some(a => usage.pinsUsed.has(a)) ||
|
|
108
108
|
(pin.number !== undefined && (usage.outputPins.has(pin.number) ||
|
|
@@ -161,14 +161,6 @@ function buildPinUsage(program, boardPinsData) {
|
|
|
161
161
|
pins: [...usage.pwmPinsUsed].map((n) => `D${n}`),
|
|
162
162
|
});
|
|
163
163
|
}
|
|
164
|
-
if (usage.timer0) {
|
|
165
|
-
peripherals.push({
|
|
166
|
-
type: "timer",
|
|
167
|
-
instance: 0,
|
|
168
|
-
displayName: "Timer0 (millis/micros)",
|
|
169
|
-
pins: [],
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
164
|
if (usage.externalInterrupts) {
|
|
173
165
|
peripherals.push({
|
|
174
166
|
type: "interrupt",
|
|
@@ -261,10 +253,10 @@ export function buildDiagnosticsReport(input) {
|
|
|
261
253
|
timestamp: new Date().toISOString(),
|
|
262
254
|
sourceFile: path.basename(input.entryFile),
|
|
263
255
|
target: input.target,
|
|
264
|
-
board: input.
|
|
256
|
+
board: input.boardTarget,
|
|
265
257
|
framework: input.frameworkPackage,
|
|
266
258
|
buildTarget: input.buildTarget,
|
|
267
|
-
outputFile: input.outputFile ?? path.basename(input.entryFile, path.extname(input.entryFile)) + ".
|
|
259
|
+
outputFile: input.outputFile ?? path.basename(input.entryFile, path.extname(input.entryFile)) + ".cpp",
|
|
268
260
|
boardDetails: program?.boardConstants ? {
|
|
269
261
|
mcu: program.boardConstants.get("mcu"),
|
|
270
262
|
architecture: program.boardConstants.get("architecture"),
|
|
@@ -8,7 +8,7 @@ export interface GpioPinEntry {
|
|
|
8
8
|
peripheralRole?: string;
|
|
9
9
|
}
|
|
10
10
|
export interface PeripheralAllocation {
|
|
11
|
-
type: "i2c" | "spi" | "uart" | "adc" | "pwm" | "
|
|
11
|
+
type: "i2c" | "spi" | "uart" | "adc" | "pwm" | "interrupt";
|
|
12
12
|
instance: number;
|
|
13
13
|
displayName: string;
|
|
14
14
|
pins: string[];
|
|
@@ -55,7 +55,7 @@ function buildExecutionFlowSection(flow, callGraph) {
|
|
|
55
55
|
const lines = [];
|
|
56
56
|
lines.push("## Execution Flow");
|
|
57
57
|
lines.push("");
|
|
58
|
-
lines.push("> This graph shows the relationships between entry points, HAL objects, and
|
|
58
|
+
lines.push("> This graph shows the relationships between entry points, HAL objects, and framework APIs.");
|
|
59
59
|
lines.push("");
|
|
60
60
|
lines.push(buildExecutionFlowDiagram(flow, callGraph));
|
|
61
61
|
lines.push("");
|
|
@@ -174,7 +174,7 @@ function buildAsyncTasksSection(tasks) {
|
|
|
174
174
|
lines.push("## Cooperative Multitasking");
|
|
175
175
|
lines.push("");
|
|
176
176
|
if (tasks.length === 0) {
|
|
177
|
-
lines.push("> _No async tasks registered in this
|
|
177
|
+
lines.push("> _No async tasks registered in this program._");
|
|
178
178
|
lines.push("");
|
|
179
179
|
return lines.join("\n");
|
|
180
180
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CallGraph } from "../ir/call-graph.js";
|
|
2
2
|
import type { GpioPinEntry, PeripheralAllocation, AsyncTaskEntry, ExecutionFlow, HeapEstimate, PeripheralConflictEntry, ModuleGraph, TreeShakingReport, BuildTiming } from "./json-schema.js";
|
|
3
3
|
/**
|
|
4
|
-
* Build a categorized architecture diagram showing how the
|
|
4
|
+
* Build a categorized architecture diagram showing how the program
|
|
5
5
|
* is organized: entry points, HAL objects, HAL APIs (grouped by
|
|
6
6
|
* subsystem), state variables, and custom functions.
|
|
7
7
|
*
|
|
@@ -73,7 +73,7 @@ function isHalApi(name) {
|
|
|
73
73
|
}
|
|
74
74
|
// ── Execution Flow Diagram ──────────────────────────────────────────────────
|
|
75
75
|
/**
|
|
76
|
-
* Build a categorized architecture diagram showing how the
|
|
76
|
+
* Build a categorized architecture diagram showing how the program
|
|
77
77
|
* is organized: entry points, HAL objects, HAL APIs (grouped by
|
|
78
78
|
* subsystem), state variables, and custom functions.
|
|
79
79
|
*
|
package/dist/emit/cpp-emitter.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { buildEmitterContext } from "./emitters/setup.js";
|
|
2
|
-
import { emitPreamble, emitAsyncTaskClasses, finalizeOutput } from "./emitters/output-finalizer.js";
|
|
2
|
+
import { emitPreamble, emitAsyncTaskClasses, emitAsyncMethodTasks, finalizeOutput } from "./emitters/output-finalizer.js";
|
|
3
3
|
import { runTopLevelPreprocessing } from "./emitters/top-level-prep.js";
|
|
4
4
|
import { synthesizeEntrypoints } from "./emitters/entrypoint-synthesizer.js";
|
|
5
5
|
import { emitTypeDeclarations } from "./emitters/type-decl-emitter.js";
|
|
@@ -39,6 +39,9 @@ export function emitCpp(program, options) {
|
|
|
39
39
|
emitFunctionForwardDeclarations(ctx);
|
|
40
40
|
// 7. Emit classes
|
|
41
41
|
emitClasses(ctx);
|
|
42
|
+
// 7.5. Async-method tasks — after the owning classes (segments dereference
|
|
43
|
+
// _owner->field on a complete type), before functions (loop pump + starters).
|
|
44
|
+
emitAsyncMethodTasks(ctx);
|
|
42
45
|
// 8. Emit post-class declarations (runtime vars, promoted vars, object literal structs)
|
|
43
46
|
emitPostClassDeclarations(ctx);
|
|
44
47
|
// 9. Emit callbacks + forward declarations + function definitions
|
|
@@ -142,7 +142,7 @@ export function emitClasses(ctx) {
|
|
|
142
142
|
// Build variable → accessor map. NOTE: the variable/parameter registration
|
|
143
143
|
// now lives in setup.ts so it runs before any emit pass. The classAccessorNames
|
|
144
144
|
// map built here is still used below for the per-method "this" registration.
|
|
145
|
-
//
|
|
145
|
+
// (demo #4 fix.)
|
|
146
146
|
// Build virtual/override maps for inherited methods
|
|
147
147
|
const classMethodNames = new Map();
|
|
148
148
|
for (const cls of program.classes) {
|
|
@@ -331,7 +331,19 @@ export function emitClasses(ctx) {
|
|
|
331
331
|
if (method.typeParameters && method.typeParameters.length > 0) {
|
|
332
332
|
appendSourceLine(ctx, ` template<typename ${method.typeParameters.join(", typename ")}>`);
|
|
333
333
|
}
|
|
334
|
-
|
|
334
|
+
// Async methods: the body becomes an owner-bound cooperative task
|
|
335
|
+
// (setup.ts generates it); the in-class body just binds the receiver
|
|
336
|
+
// via the forward-declared starter. Return type is void — the task
|
|
337
|
+
// is fire-and-forget.
|
|
338
|
+
const asyncKick = method.isAsync && ctx.hasAsyncRuntime;
|
|
339
|
+
const emittedReturnType = asyncKick ? "void" : returnType;
|
|
340
|
+
appendSourceLine(ctx, ` ${virtualPrefix}${staticPrefix}${emittedReturnType} ${escapeCppKeyword(method.name, reservedNames)}(${methodParams})${overrideSuffix} {`);
|
|
341
|
+
if (asyncKick) {
|
|
342
|
+
appendSourceLine(ctx, ` __tc_async_start_${classDef.name}_${method.name}(this);`);
|
|
343
|
+
appendSourceLine(ctx, " }");
|
|
344
|
+
appendSourceLine(ctx, "");
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
335
347
|
const methodScope = createChildEmissionScope(topLevelScope, method.parameters);
|
|
336
348
|
addClassFieldsToScope(classDef, methodScope);
|
|
337
349
|
const classAccessors = classAccessorNames.get(classDef.name);
|
|
@@ -403,7 +415,15 @@ export function emitClasses(ctx) {
|
|
|
403
415
|
if (method.typeParameters && method.typeParameters.length > 0) {
|
|
404
416
|
appendSourceLine(ctx, ` template<typename ${method.typeParameters.join(", typename ")}>`);
|
|
405
417
|
}
|
|
406
|
-
|
|
418
|
+
const asyncKick = method.isAsync && ctx.hasAsyncRuntime;
|
|
419
|
+
const emittedReturnType = asyncKick ? "void" : normalizeCppTypeForTarget(method.returnType);
|
|
420
|
+
appendSourceLine(ctx, ` ${virtualPrefix}${staticPrefix}${emittedReturnType} ${escapeCppKeyword(method.name, reservedNames)}(${methodParams})${overrideSuffix} {`);
|
|
421
|
+
if (asyncKick) {
|
|
422
|
+
appendSourceLine(ctx, ` __tc_async_start_${classDef.name}_${method.name}(this);`);
|
|
423
|
+
appendSourceLine(ctx, " }");
|
|
424
|
+
appendSourceLine(ctx, "");
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
407
427
|
const methodScope = createChildEmissionScope(topLevelScope, method.parameters);
|
|
408
428
|
addClassFieldsToScope(classDef, methodScope);
|
|
409
429
|
withThisAccessors(classDef, method.isStatic, () => {
|
|
@@ -469,7 +489,15 @@ export function emitClasses(ctx) {
|
|
|
469
489
|
if (method.typeParameters && method.typeParameters.length > 0) {
|
|
470
490
|
appendSourceLine(ctx, ` template<typename ${method.typeParameters.join(", typename ")}>`);
|
|
471
491
|
}
|
|
472
|
-
|
|
492
|
+
const asyncKick = method.isAsync && ctx.hasAsyncRuntime;
|
|
493
|
+
const emittedReturnType = asyncKick ? "void" : normalizeCppTypeForTarget(method.returnType);
|
|
494
|
+
appendSourceLine(ctx, ` ${virtualPrefix}${staticPrefix}${emittedReturnType} ${escapeCppKeyword(method.name, reservedNames)}(${methodParams})${overrideSuffix} {`);
|
|
495
|
+
if (asyncKick) {
|
|
496
|
+
appendSourceLine(ctx, ` __tc_async_start_${classDef.name}_${method.name}(this);`);
|
|
497
|
+
appendSourceLine(ctx, " }");
|
|
498
|
+
appendSourceLine(ctx, "");
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
473
501
|
const methodScope = createChildEmissionScope(topLevelScope, method.parameters);
|
|
474
502
|
addClassFieldsToScope(classDef, methodScope);
|
|
475
503
|
withThisAccessors(classDef, method.isStatic, () => {
|
|
@@ -90,6 +90,16 @@ export interface AsyncTaskClass {
|
|
|
90
90
|
classDef: string;
|
|
91
91
|
instanceDecl: string;
|
|
92
92
|
taskVarName: string;
|
|
93
|
+
/** Async methods: the starter-function DEFINITION (binds the owner and
|
|
94
|
+
* arms the task). Emitted after the task class; the matching prototype
|
|
95
|
+
* (asyncMethodStarters) precedes the owning class. */
|
|
96
|
+
starterDef?: string;
|
|
97
|
+
}
|
|
98
|
+
/** Forward prototypes for async-method starters — emitted before the class
|
|
99
|
+
* definitions whose method bodies call them. */
|
|
100
|
+
export interface AsyncMethodStarter {
|
|
101
|
+
/** The `void __tc_async_start_<Class>_<method>(<Class>*);` prototype. */
|
|
102
|
+
proto: string;
|
|
93
103
|
}
|
|
94
104
|
export interface FixPointerFieldAccessFn {
|
|
95
105
|
(callee: string): string;
|
|
@@ -139,6 +149,7 @@ export interface EmitterContext {
|
|
|
139
149
|
boardConstants?: BoardConstants;
|
|
140
150
|
restParamFunctions?: Map<string, string>;
|
|
141
151
|
asyncTaskClasses: AsyncTaskClass[];
|
|
152
|
+
asyncMethodStarters: AsyncMethodStarter[];
|
|
142
153
|
asyncFunctionOriginalNames: Set<string>;
|
|
143
154
|
asyncFunctionMappedNames: Set<string>;
|
|
144
155
|
hasAsyncRuntime: boolean;
|
|
@@ -53,7 +53,7 @@ export function synthesizeEntrypoints(ctx) {
|
|
|
53
53
|
ctx.emitDiagnostics.push({
|
|
54
54
|
severity: "error",
|
|
55
55
|
code: "gpio-unsupported-on-target",
|
|
56
|
-
message: `${which} require GPIO hardware, which this target (${ctx.options.target}) does not model. Use the browser preview to exercise pin-driven UI, or target
|
|
56
|
+
message: `${which} require GPIO hardware, which this target (${ctx.options.target}) does not model. Use the browser preview to exercise pin-driven UI, or target a framework that models GPIO pins.`,
|
|
57
57
|
source: program.fileName,
|
|
58
58
|
});
|
|
59
59
|
}
|