semantifold 0.8.0 → 0.10.0

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.
Files changed (171) hide show
  1. package/README.md +11 -5
  2. package/bin/semantifold.js +6 -0
  3. package/build/bin/semantifold.d.ts +3 -0
  4. package/build/bin/semantifold.d.ts.map +1 -0
  5. package/build/bin/semantifold.js +5 -0
  6. package/build/index.d.ts +9 -0
  7. package/build/index.d.ts.map +1 -1
  8. package/build/index.js +10 -1
  9. package/build/src/artifact-path.d.ts +16 -0
  10. package/build/src/artifact-path.d.ts.map +1 -1
  11. package/build/src/artifact-path.js +29 -3
  12. package/build/src/backends/c-check.d.ts +7 -0
  13. package/build/src/backends/c-check.d.ts.map +1 -0
  14. package/build/src/backends/c-check.js +65 -0
  15. package/build/src/backends/check-plan.d.ts +96 -0
  16. package/build/src/backends/check-plan.d.ts.map +1 -0
  17. package/build/src/backends/check-plan.js +135 -0
  18. package/build/src/backends/cpp-check.d.ts +7 -0
  19. package/build/src/backends/cpp-check.d.ts.map +1 -0
  20. package/build/src/backends/cpp-check.js +59 -0
  21. package/build/src/backends/csharp-check.d.ts +7 -0
  22. package/build/src/backends/csharp-check.d.ts.map +1 -0
  23. package/build/src/backends/csharp-check.js +125 -0
  24. package/build/src/backends/csharp.d.ts +1 -0
  25. package/build/src/backends/csharp.d.ts.map +1 -1
  26. package/build/src/backends/csharp.js +5 -4
  27. package/build/src/backends/dart-check.d.ts +7 -0
  28. package/build/src/backends/dart-check.d.ts.map +1 -0
  29. package/build/src/backends/dart-check.js +103 -0
  30. package/build/src/backends/dart.d.ts +2 -0
  31. package/build/src/backends/dart.d.ts.map +1 -1
  32. package/build/src/backends/dart.js +5 -5
  33. package/build/src/backends/go-check.d.ts +7 -0
  34. package/build/src/backends/go-check.d.ts.map +1 -0
  35. package/build/src/backends/go-check.js +77 -0
  36. package/build/src/backends/go.d.ts +1 -0
  37. package/build/src/backends/go.d.ts.map +1 -1
  38. package/build/src/backends/go.js +3 -3
  39. package/build/src/backends/java-check.d.ts +21 -0
  40. package/build/src/backends/java-check.d.ts.map +1 -0
  41. package/build/src/backends/java-check.js +73 -0
  42. package/build/src/backends/javascript-check.d.ts +7 -0
  43. package/build/src/backends/javascript-check.d.ts.map +1 -0
  44. package/build/src/backends/javascript-check.js +29 -0
  45. package/build/src/backends/kotlin-check.d.ts +7 -0
  46. package/build/src/backends/kotlin-check.d.ts.map +1 -0
  47. package/build/src/backends/kotlin-check.js +31 -0
  48. package/build/src/backends/php-check.d.ts +7 -0
  49. package/build/src/backends/php-check.d.ts.map +1 -0
  50. package/build/src/backends/php-check.js +21 -0
  51. package/build/src/backends/program.d.ts +7 -0
  52. package/build/src/backends/program.d.ts.map +1 -1
  53. package/build/src/backends/program.js +16 -1
  54. package/build/src/backends/python-check.d.ts +7 -0
  55. package/build/src/backends/python-check.d.ts.map +1 -0
  56. package/build/src/backends/python-check.js +28 -0
  57. package/build/src/backends/ruby-check.d.ts +7 -0
  58. package/build/src/backends/ruby-check.d.ts.map +1 -0
  59. package/build/src/backends/ruby-check.js +21 -0
  60. package/build/src/backends/rust-check.d.ts +7 -0
  61. package/build/src/backends/rust-check.d.ts.map +1 -0
  62. package/build/src/backends/rust-check.js +65 -0
  63. package/build/src/backends/swift-check.d.ts +7 -0
  64. package/build/src/backends/swift-check.d.ts.map +1 -0
  65. package/build/src/backends/swift-check.js +77 -0
  66. package/build/src/backends/typescript-check.d.ts +7 -0
  67. package/build/src/backends/typescript-check.d.ts.map +1 -0
  68. package/build/src/backends/typescript-check.js +33 -0
  69. package/build/src/backends/zig-check.d.ts +7 -0
  70. package/build/src/backends/zig-check.d.ts.map +1 -0
  71. package/build/src/backends/zig-check.js +70 -0
  72. package/build/src/diagnostic.d.ts +13 -1
  73. package/build/src/diagnostic.d.ts.map +1 -1
  74. package/build/src/diagnostic.js +14 -2
  75. package/build/src/frontends/program.d.ts +6 -0
  76. package/build/src/frontends/program.d.ts.map +1 -1
  77. package/build/src/frontends/program.js +9 -1
  78. package/build/src/language-registry.d.ts +54 -0
  79. package/build/src/language-registry.d.ts.map +1 -1
  80. package/build/src/language-registry.js +107 -5
  81. package/build/src/project-build.d.ts +172 -0
  82. package/build/src/project-build.d.ts.map +1 -0
  83. package/build/src/project-build.js +320 -0
  84. package/build/src/project-cli.d.ts +40 -0
  85. package/build/src/project-cli.d.ts.map +1 -0
  86. package/build/src/project-cli.js +145 -0
  87. package/build/src/project-manifest.d.ts +148 -0
  88. package/build/src/project-manifest.d.ts.map +1 -0
  89. package/build/src/project-manifest.js +432 -0
  90. package/build/src/project-reporter.d.ts +81 -0
  91. package/build/src/project-reporter.d.ts.map +1 -0
  92. package/build/src/project-reporter.js +293 -0
  93. package/build/src/project-snapshot.d.ts +129 -0
  94. package/build/src/project-snapshot.d.ts.map +1 -0
  95. package/build/src/project-snapshot.js +362 -0
  96. package/build/src/project-watch-reporter.d.ts +125 -0
  97. package/build/src/project-watch-reporter.d.ts.map +1 -0
  98. package/build/src/project-watch-reporter.js +280 -0
  99. package/build/src/project-watch.d.ts +133 -0
  100. package/build/src/project-watch.d.ts.map +1 -0
  101. package/build/src/project-watch.js +839 -0
  102. package/build/src/publication.d.ts +218 -0
  103. package/build/src/publication.d.ts.map +1 -0
  104. package/build/src/publication.js +1832 -0
  105. package/build/src/semantic/types.d.ts +613 -0
  106. package/build/src/semantic/types.d.ts.map +1 -1
  107. package/build/src/semantic/types.js +163 -1
  108. package/build/src/subprocess.d.ts +8 -0
  109. package/build/src/subprocess.d.ts.map +1 -1
  110. package/build/src/subprocess.js +50 -9
  111. package/build/src/target-check.d.ts +52 -0
  112. package/build/src/target-check.d.ts.map +1 -0
  113. package/build/src/target-check.js +796 -0
  114. package/build/src/toolchains.d.ts.map +1 -1
  115. package/build/src/toolchains.js +3 -3
  116. package/docs/architecture.md +3 -1
  117. package/docs/c.md +4 -0
  118. package/docs/cpp.md +4 -0
  119. package/docs/dart.md +4 -0
  120. package/docs/generated-artifact-publication.md +98 -0
  121. package/docs/kotlin.md +2 -2
  122. package/docs/language-support.md +29 -3
  123. package/docs/project-build-cli.md +147 -0
  124. package/docs/project-watch.md +60 -0
  125. package/docs/rust.md +4 -0
  126. package/docs/swift.md +2 -0
  127. package/docs/testing.md +70 -3
  128. package/docs/watch-build-pipeline.md +42 -26
  129. package/docs/zig.md +2 -0
  130. package/examples/jsdoc-java-watch/expected-output.txt +3 -0
  131. package/examples/jsdoc-java-watch/semantifold.json +23 -0
  132. package/examples/jsdoc-java-watch/src/main.js +21 -0
  133. package/index.js +9 -0
  134. package/package.json +6 -1
  135. package/src/artifact-path.js +31 -2
  136. package/src/backends/c-check.js +80 -0
  137. package/src/backends/check-plan.js +147 -0
  138. package/src/backends/cpp-check.js +73 -0
  139. package/src/backends/csharp-check.js +139 -0
  140. package/src/backends/csharp.js +4 -3
  141. package/src/backends/dart-check.js +117 -0
  142. package/src/backends/dart.js +4 -4
  143. package/src/backends/go-check.js +89 -0
  144. package/src/backends/go.js +2 -2
  145. package/src/backends/java-check.js +77 -0
  146. package/src/backends/javascript-check.js +33 -0
  147. package/src/backends/kotlin-check.js +41 -0
  148. package/src/backends/php-check.js +23 -0
  149. package/src/backends/program.js +17 -0
  150. package/src/backends/python-check.js +30 -0
  151. package/src/backends/ruby-check.js +23 -0
  152. package/src/backends/rust-check.js +78 -0
  153. package/src/backends/swift-check.js +89 -0
  154. package/src/backends/typescript-check.js +37 -0
  155. package/src/backends/zig-check.js +81 -0
  156. package/src/diagnostic.js +9 -1
  157. package/src/frontends/program.js +9 -0
  158. package/src/language-registry.js +113 -4
  159. package/src/project-build.js +353 -0
  160. package/src/project-cli.js +155 -0
  161. package/src/project-manifest.js +479 -0
  162. package/src/project-reporter.js +320 -0
  163. package/src/project-snapshot.js +403 -0
  164. package/src/project-watch-reporter.js +303 -0
  165. package/src/project-watch.js +870 -0
  166. package/src/publication.js +2071 -0
  167. package/src/semantic/types.js +184 -0
  168. package/src/subprocess.js +49 -8
  169. package/src/target-check.js +844 -0
  170. package/src/toolchains.js +3 -2
  171. package/tsconfig.json +1 -1
package/index.js CHANGED
@@ -16,6 +16,15 @@ export {SemantifoldDiagnostic}
16
16
  export {createCapabilityAuthority} from "./src/semantic/capabilities.js"
17
17
  export {languageCapabilities, supportedLanguages} from "./src/language-registry.js"
18
18
  export {createGeneratedArtifactSet} from "./src/artifacts.js"
19
+ export {GeneratedArtifactPublisher} from "./src/publication.js"
20
+ export {ProjectManifestLoader, SemantifoldProject} from "./src/project-manifest.js"
21
+ export {ProjectSnapshot, ProjectSnapshotBuilder} from "./src/project-snapshot.js"
22
+ export {ProjectBuilder} from "./src/project-build.js"
23
+ export {parseSemantifoldCliArguments, SemantifoldCli} from "./src/project-cli.js"
24
+ export {ProjectBuildReporter} from "./src/project-reporter.js"
25
+ export {ProjectWatchCoordinator} from "./src/project-watch.js"
26
+ export {ProjectWatchReporter} from "./src/project-watch-reporter.js"
27
+ export {createTargetCheckPlan, TargetCheckRunner} from "./src/target-check.js"
19
28
  export {createStdlibContractRegistry, listStdlibModules, resolveStdlibModule} from "./src/semantic/stdlib.js"
20
29
  export {createStdlibProviderRegistry, listStdlibProviders} from "./src/stdlib-providers.js"
21
30
  export {negotiateStdlibProviders} from "./src/stdlib-negotiation.js"
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "semantifold",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "description": "Language-neutral semantic code analysis, transformation, generation, and execution toolkit",
5
5
  "type": "module",
6
6
  "main": "build/index.js",
7
7
  "types": "build/index.d.ts",
8
+ "bin": {
9
+ "semantifold": "bin/semantifold.js"
10
+ },
8
11
  "exports": {
9
12
  ".": {
10
13
  "types": "./build/index.d.ts",
@@ -13,7 +16,9 @@
13
16
  },
14
17
  "files": [
15
18
  "build/**",
19
+ "bin/**",
16
20
  "docs/**",
21
+ "examples/**",
17
22
  "index.js",
18
23
  "LICENSE",
19
24
  "README.md",
@@ -52,18 +52,47 @@ export function portableArtifactPathKey(value) {
52
52
  return value.toLowerCase()
53
53
  }
54
54
 
55
+ /**
56
+ * Produces a deterministic case-fold comparison key for a validated source path.
57
+ * @param {string} value - Safe relative POSIX source path.
58
+ * @returns {string} Unicode case-fold comparison key.
59
+ */
60
+ export function portableSourcePathKey(value) {
61
+ return value.toLowerCase()
62
+ }
63
+
55
64
  /**
56
65
  * Finds the first unsafe, duplicate, portable case-fold, or file/directory-prefix conflict.
57
66
  * @param {readonly string[]} paths - Ordered candidate artifact paths.
58
67
  * @returns {{kind: "case-fold" | "duplicate" | "prefix" | "unsafe", path: string, other?: string} | null} First conflict.
59
68
  */
60
69
  export function findPortableArtifactPathConflict(paths) {
70
+ return findPortablePathConflict(paths, isSafeArtifactPath, portableArtifactPathKey)
71
+ }
72
+
73
+ /**
74
+ * Finds the first unsafe, duplicate, portable case-fold, or file/directory-prefix source conflict.
75
+ * @param {readonly string[]} paths - Ordered candidate source paths.
76
+ * @returns {{kind: "case-fold" | "duplicate" | "prefix" | "unsafe", path: string, other?: string} | null} First conflict.
77
+ */
78
+ export function findPortableSourcePathConflict(paths) {
79
+ return findPortablePathConflict(paths, isSafeSourcePath, portableSourcePathKey)
80
+ }
81
+
82
+ /**
83
+ * Compares paths inside one explicitly selected validation domain.
84
+ * @param {readonly string[]} paths - Ordered candidate paths.
85
+ * @param {(value: unknown) => boolean} isSafe - Domain-specific path validator.
86
+ * @param {(value: string) => string} comparisonKey - Domain-specific portable comparison key.
87
+ * @returns {{kind: "case-fold" | "duplicate" | "prefix" | "unsafe", path: string, other?: string} | null} First conflict.
88
+ */
89
+ function findPortablePathConflict(paths, isSafe, comparisonKey) {
61
90
  /** @type {{key: string, path: string}[]} */
62
91
  const accepted = []
63
92
 
64
93
  for (const path of paths) {
65
- if (!isSafeArtifactPath(path)) return {kind: "unsafe", path: String(path)}
66
- const key = portableArtifactPathKey(path)
94
+ if (!isSafe(path)) return {kind: "unsafe", path: String(path)}
95
+ const key = comparisonKey(path)
67
96
 
68
97
  for (const previous of accepted) {
69
98
  if (path == previous.path) return {kind: "duplicate", other: previous.path, path}
@@ -0,0 +1,80 @@
1
+ // @ts-check
2
+
3
+ import path from "node:path"
4
+ import {SemantifoldDiagnostic} from "../diagnostic.js"
5
+ import {
6
+ checkArtifacts,
7
+ checkOutput,
8
+ checkPlan,
9
+ checkStage,
10
+ checkTool,
11
+ environmentPath,
12
+ pathArgument
13
+ } from "./check-plan.js"
14
+ import {cRuntimeHeader} from "./c-runtime.js"
15
+
16
+ const compilerFlags = Object.freeze([
17
+ "--no-default-config", "-std=c17", "-Wall", "-Wextra", "-Werror", "-pedantic-errors", "-Wconversion",
18
+ "-Wsign-conversion", "-Wshadow", "-Wstrict-prototypes", "-Wmissing-prototypes", "-Wformat=2", "-ftrapv",
19
+ "-finput-charset=UTF-8", "-fexec-charset=UTF-8", "-fno-color-diagnostics", "-O0"
20
+ ])
21
+
22
+ /**
23
+ * Constructs C's strict Clang C17 compile/link plan.
24
+ * @param {import("../language-registry.js").TargetCheckPlanContext} context - Validated candidate context.
25
+ * @returns {import("../semantic/types.js").TargetCheckPlan} Immutable C plan.
26
+ */
27
+ export function createCCheckPlan(context) {
28
+ const tool = checkTool(context, "clang", "c")
29
+ const artifacts = checkArtifacts(context, artifact => artifact.path.endsWith(".c") || artifact.path.endsWith(".h"),
30
+ "c", "only staged C translation units and headers")
31
+ const sources = artifacts.filter(artifact => artifact.endsWith(".c"))
32
+ const headers = artifacts.filter(artifact => artifact.endsWith(".h"))
33
+ const support = context.artifacts.artifacts.filter(artifact => artifact.path == "semantifold_runtime.h")
34
+
35
+ if (artifacts.length != 2 || sources.length != 1 || sources[0] != path.join(context.sourcePath, "program.c") ||
36
+ headers.length != 1 || support.length != 1 || support[0].contentKind != "text" || support[0].role != "support" ||
37
+ support[0].content != cRuntimeHeader) {
38
+ throw new SemantifoldDiagnostic({code: "INVALID_TARGET_CHECK_PLAN", language: "c",
39
+ message: "C developer checks require the exact producer-owned translation unit and support header."})
40
+ }
41
+ const objects = path.join(context.buildPath, "objects")
42
+ const temporary = path.join(context.buildPath, "tmp")
43
+ const environment = {TMPDIR: temporary}
44
+ const environmentPaths = [environmentPath("TMPDIR", "build")]
45
+ const objectPaths = sources.map((_, index) => path.join(objects, `${String(index).padStart(6, "0")}.o`))
46
+ const stages = sources.map((source, index) => {
47
+ const argv = [...compilerFlags, "-c", source, "-o", objectPaths[index]]
48
+
49
+ return checkStage({
50
+ argv,
51
+ cwd: context.sourcePath,
52
+ environment,
53
+ environmentPaths,
54
+ inputs: [source, ...headers],
55
+ output: null,
56
+ pathArguments: [pathArgument(argv.length - 3, "source"), pathArgument(argv.length - 1, "build")],
57
+ stage: /** @type {const} */ ("compile"),
58
+ tool,
59
+ transientPaths: [objects]
60
+ })
61
+ })
62
+ const binary = path.join(context.buildPath, "semantifold-c")
63
+ const linkArgv = ["--no-default-config", "-O0", ...objectPaths, "-o", binary]
64
+
65
+ stages.push(checkStage({
66
+ argv: linkArgv,
67
+ cwd: context.sourcePath,
68
+ environment,
69
+ environmentPaths,
70
+ inputs: objectPaths,
71
+ output: checkOutput(context.buildPath, "application/x-executable", "linker-output"),
72
+ pathArguments: [...objectPaths.map((_, index) => pathArgument(index + 2, "build")),
73
+ pathArgument(linkArgv.length - 1, "build")],
74
+ stage: "link",
75
+ tool,
76
+ transientPaths: [objects]
77
+ }))
78
+
79
+ return checkPlan(context, "c", artifacts, stages)
80
+ }
@@ -0,0 +1,147 @@
1
+ // @ts-check
2
+
3
+ import path from "node:path"
4
+ import {SemantifoldDiagnostic} from "../diagnostic.js"
5
+ import {deterministicEnvironment} from "../toolchains.js"
6
+
7
+ /**
8
+ * Resolves one target-declared discovered tool from immutable plan context.
9
+ * @param {Readonly<{tools: readonly import("../semantic/types.js").DiscoveredToolchain[]}>} context - Plan context.
10
+ * @param {string} id - Declared toolchain identity.
11
+ * @param {string} target - Target diagnostic identity.
12
+ * @returns {import("../semantic/types.js").DiscoveredToolchain} Exact tool.
13
+ */
14
+ export function checkTool(context, id, target) {
15
+ const tool = context.tools.find(candidate => candidate.id == id)
16
+
17
+ if (!tool) {
18
+ throw new SemantifoldDiagnostic({
19
+ code: "INVALID_TARGET_CHECK_PLAN",
20
+ language: target,
21
+ message: `Target '${target}' developer checks require the declared '${id}' toolchain.`
22
+ })
23
+ }
24
+
25
+ return tool
26
+ }
27
+
28
+ /**
29
+ * Resolves all staged artifacts matching one target-owned predicate.
30
+ * @param {Readonly<{artifacts: import("../semantic/types.js").GeneratedArtifactSet, sourcePath: string}>} context - Plan context.
31
+ * @param {(artifact: import("../semantic/types.js").GeneratedSetArtifact) => boolean} accepts - Target-owned artifact predicate.
32
+ * @param {string} target - Target diagnostic identity.
33
+ * @param {string} description - Required artifact description.
34
+ * @returns {readonly string[]} Ordered absolute artifact paths.
35
+ */
36
+ export function checkArtifacts(context, accepts, target, description) {
37
+ const accepted = context.artifacts.artifacts.filter(accepts)
38
+
39
+ if (accepted.length == 0 || accepted.length != context.artifacts.artifacts.length) {
40
+ throw new SemantifoldDiagnostic({
41
+ code: "INVALID_TARGET_CHECK_PLAN",
42
+ language: target,
43
+ message: `Target '${target}' developer checks require ${description}.`
44
+ })
45
+ }
46
+
47
+ return Object.freeze(accepted.map(artifact => path.join(context.sourcePath, artifact.path))
48
+ .sort((left, right) => left.localeCompare(right, "en")))
49
+ }
50
+
51
+ /**
52
+ * Freezes one closed target-check stage request.
53
+ * @param {object} input - Complete stage data.
54
+ * @param {readonly string[]} input.argv - Exact argv.
55
+ * @param {string} input.cwd - Candidate-owned working directory.
56
+ * @param {Readonly<Record<string, string | undefined>>} [input.environment] - Target-owned environment additions.
57
+ * @param {readonly import("../semantic/types.js").TargetCheckEnvironmentPath[]} [input.environmentPaths] - Owned environment paths.
58
+ * @param {string | null} [input.inputHash] - Restore-input hash when applicable.
59
+ * @param {readonly string[]} input.inputs - Exact staged inputs read by the tool.
60
+ * @param {import("../semantic/types.js").TargetCheckOutputOwnership | null} input.output - Produced build ownership.
61
+ * @param {readonly import("../semantic/types.js").TargetCheckPathArgument[]} input.pathArguments - Owned argv paths.
62
+ * @param {import("../semantic/types.js").AcceptanceStage} input.stage - Stage identity.
63
+ * @param {import("../semantic/types.js").DiscoveredToolchain} input.tool - Exact tool.
64
+ * @param {readonly string[]} [input.transientPaths] - Owned cache/intermediate directories.
65
+ * @returns {import("../semantic/types.js").TargetCheckStageRequest} Immutable stage.
66
+ */
67
+ export function checkStage(input) {
68
+ return Object.freeze({
69
+ argv: Object.freeze([...input.argv]),
70
+ cwd: input.cwd,
71
+ environment: deterministicEnvironment(input.environment ?? {}),
72
+ environmentPaths: Object.freeze([...(input.environmentPaths ?? [])]),
73
+ executable: input.tool.executable,
74
+ inputHash: input.inputHash ?? null,
75
+ inputs: Object.freeze([...input.inputs]),
76
+ output: input.output,
77
+ pathArguments: Object.freeze([...input.pathArguments]),
78
+ stage: input.stage,
79
+ tool: input.tool,
80
+ transientPaths: Object.freeze([...(input.transientPaths ?? [])])
81
+ })
82
+ }
83
+
84
+ /**
85
+ * Freezes one closed target-owned plan.
86
+ * @param {Readonly<{buildPath: string, projectId: string, sourcePath: string, targetId: string}>} context - Plan context.
87
+ * @param {string} target - Target identity.
88
+ * @param {readonly string[]} artifactPaths - Complete staged artifact vector.
89
+ * @param {readonly import("../semantic/types.js").TargetCheckStageRequest[]} stages - Ordered stages.
90
+ * @returns {import("../semantic/types.js").TargetCheckPlan} Immutable plan.
91
+ */
92
+ export function checkPlan(context, target, artifactPaths, stages) {
93
+ return Object.freeze({
94
+ artifactPaths,
95
+ buildPath: context.buildPath,
96
+ mode: /** @type {const} */ ("developer-check"),
97
+ projectId: context.projectId,
98
+ schema: /** @type {const} */ ("SemantifoldTargetCheckPlan"),
99
+ sourcePath: context.sourcePath,
100
+ stages: Object.freeze([...stages]),
101
+ target,
102
+ targetId: context.targetId,
103
+ version: /** @type {const} */ (1)
104
+ })
105
+ }
106
+
107
+ /**
108
+ * Freezes one owned argv-path declaration.
109
+ * @param {number} index - Exact argv index.
110
+ * @param {"source" | "build"} ownership - Owning candidate subtree.
111
+ * @param {string} [prefix] - Optional non-path argument prefix.
112
+ * @returns {import("../semantic/types.js").TargetCheckPathArgument} Immutable declaration.
113
+ */
114
+ export function pathArgument(index, ownership, prefix) {
115
+ return Object.freeze({index, ownership, ...(prefix === undefined ? {} : {prefix})})
116
+ }
117
+
118
+ /**
119
+ * Freezes one owned environment-directory declaration.
120
+ * @param {string} name - Exact environment variable.
121
+ * @param {"source" | "build"} ownership - Owning candidate subtree.
122
+ * @returns {import("../semantic/types.js").TargetCheckEnvironmentPath} Immutable declaration.
123
+ */
124
+ export function environmentPath(name, ownership) {
125
+ return Object.freeze({name, ownership})
126
+ }
127
+
128
+ /**
129
+ * Freezes one exact tool-executable environment binding.
130
+ * @param {string} name - Exact environment variable.
131
+ * @param {import("../semantic/types.js").DiscoveredToolchain} tool - Exact discovered tool.
132
+ * @returns {import("../semantic/types.js").TargetCheckEnvironmentPath} Immutable declaration.
133
+ */
134
+ export function environmentTool(name, tool) {
135
+ return Object.freeze({name, ownership: /** @type {const} */ ("tool"), tool})
136
+ }
137
+
138
+ /**
139
+ * Freezes build-output ownership metadata.
140
+ * @param {string} buildPath - Exact candidate build root.
141
+ * @param {string} mediaType - Output media type.
142
+ * @param {string} role - Language-neutral output role.
143
+ * @returns {import("../semantic/types.js").TargetCheckOutputOwnership} Immutable ownership.
144
+ */
145
+ export function checkOutput(buildPath, mediaType, role) {
146
+ return Object.freeze({mediaType, ownership: /** @type {const} */ ("build"), path: buildPath, role})
147
+ }
@@ -0,0 +1,73 @@
1
+ // @ts-check
2
+
3
+ import path from "node:path"
4
+ import {SemantifoldDiagnostic} from "../diagnostic.js"
5
+ import {
6
+ checkArtifacts,
7
+ checkOutput,
8
+ checkPlan,
9
+ checkStage,
10
+ checkTool,
11
+ environmentPath,
12
+ pathArgument
13
+ } from "./check-plan.js"
14
+
15
+ const compilerFlags = Object.freeze([
16
+ "--no-default-config", "--driver-mode=g++", "-std=c++20", "-stdlib=libstdc++", "-Wall", "-Wextra", "-Werror",
17
+ "-pedantic-errors", "-Wconversion", "-Wsign-conversion", "-Wshadow", "-Wformat=2", "-fno-exceptions", "-fno-rtti",
18
+ "-ftrapv", "-finput-charset=UTF-8", "-fexec-charset=UTF-8", "-fno-color-diagnostics", "-O0"
19
+ ])
20
+
21
+ /**
22
+ * Constructs C++'s strict Clang C++20/libstdc++ compile/link plan.
23
+ * @param {import("../language-registry.js").TargetCheckPlanContext} context - Validated candidate context.
24
+ * @returns {import("../semantic/types.js").TargetCheckPlan} Immutable C++ plan.
25
+ */
26
+ export function createCppCheckPlan(context) {
27
+ const tool = checkTool(context, "clangpp", "cpp")
28
+ const artifacts = checkArtifacts(context, artifact => artifact.path.endsWith(".cpp"), "cpp", "only staged '.cpp' translation units")
29
+
30
+ if (artifacts.length != 1 || artifacts[0] != path.join(context.sourcePath, "program.cpp")) {
31
+ throw new SemantifoldDiagnostic({code: "INVALID_TARGET_CHECK_PLAN", language: "cpp",
32
+ message: "C++ developer checks require the exact producer-owned 'program.cpp' translation unit."})
33
+ }
34
+ const objects = path.join(context.buildPath, "objects")
35
+ const temporary = path.join(context.buildPath, "tmp")
36
+ const environment = {TMPDIR: temporary}
37
+ const environmentPaths = [environmentPath("TMPDIR", "build")]
38
+ const objectPaths = artifacts.map((_, index) => path.join(objects, `${String(index).padStart(6, "0")}.o`))
39
+ const stages = artifacts.map((source, index) => {
40
+ const argv = [...compilerFlags, "-c", source, "-o", objectPaths[index]]
41
+
42
+ return checkStage({
43
+ argv,
44
+ cwd: context.sourcePath,
45
+ environment,
46
+ environmentPaths,
47
+ inputs: [source],
48
+ output: null,
49
+ pathArguments: [pathArgument(argv.length - 3, "source"), pathArgument(argv.length - 1, "build")],
50
+ stage: /** @type {const} */ ("compile"),
51
+ tool,
52
+ transientPaths: [objects]
53
+ })
54
+ })
55
+ const binary = path.join(context.buildPath, "semantifold-cpp")
56
+ const linkArgv = ["--no-default-config", "--driver-mode=g++", "-stdlib=libstdc++", "-O0", ...objectPaths, "-o", binary]
57
+
58
+ stages.push(checkStage({
59
+ argv: linkArgv,
60
+ cwd: context.sourcePath,
61
+ environment,
62
+ environmentPaths,
63
+ inputs: objectPaths,
64
+ output: checkOutput(context.buildPath, "application/x-executable", "linker-output"),
65
+ pathArguments: [...objectPaths.map((_, index) => pathArgument(index + 4, "build")),
66
+ pathArgument(linkArgv.length - 1, "build")],
67
+ stage: "link",
68
+ tool,
69
+ transientPaths: [objects]
70
+ }))
71
+
72
+ return checkPlan(context, "cpp", artifacts, stages)
73
+ }
@@ -0,0 +1,139 @@
1
+ // @ts-check
2
+
3
+ import {createHash} from "node:crypto"
4
+ import path from "node:path"
5
+ import {SemantifoldDiagnostic} from "../diagnostic.js"
6
+ import {
7
+ checkArtifacts,
8
+ checkOutput,
9
+ checkPlan,
10
+ checkStage,
11
+ checkTool,
12
+ environmentPath,
13
+ pathArgument
14
+ } from "./check-plan.js"
15
+ import {csharpProjectManifest} from "./csharp.js"
16
+
17
+ /**
18
+ * Constructs C#'s offline restore and non-executing build plan.
19
+ * @param {import("../language-registry.js").TargetCheckPlanContext} context - Validated candidate context.
20
+ * @returns {import("../semantic/types.js").TargetCheckPlan} Immutable C# plan.
21
+ */
22
+ export function createCSharpCheckPlan(context) {
23
+ const tool = checkTool(context, "dotnet", "csharp")
24
+ const artifacts = checkArtifacts(context, artifact => artifact.path.endsWith(".cs") ||
25
+ artifact.path == "Semantifold.csproj" || artifact.path == "packages.lock.json",
26
+ "csharp", "only staged C# source and canonical project/lock artifacts")
27
+ const projects = context.artifacts.artifacts.filter(artifact => artifact.path == "Semantifold.csproj")
28
+ const sources = artifacts.filter(artifact => artifact.endsWith(".cs"))
29
+
30
+ if (projects.length != 1 || projects[0].contentKind != "text" || projects[0].role != "manifest" ||
31
+ projects[0].content != csharpProjectManifest) {
32
+ throw new SemantifoldDiagnostic({
33
+ code: "INVALID_TARGET_CHECK_PLAN",
34
+ language: "csharp",
35
+ message: "C# developer checks require the exact producer-owned 'Semantifold.csproj' artifact."
36
+ })
37
+ }
38
+ if (sources.length == 0) {
39
+ throw new SemantifoldDiagnostic({
40
+ code: "INVALID_TARGET_CHECK_PLAN",
41
+ language: "csharp",
42
+ message: "C# developer checks require at least one staged '.cs' artifact."
43
+ })
44
+ }
45
+ const restoreArtifacts = context.artifacts.artifacts
46
+ .filter(artifact => artifact.path == "Semantifold.csproj" || artifact.path == "packages.lock.json")
47
+ .sort((left, right) => left.path.localeCompare(right.path, "en"))
48
+ const restoreInputs = Object.freeze(restoreArtifacts.map(artifact => path.join(context.sourcePath, artifact.path)))
49
+ const restoreHash = createHash("sha256")
50
+
51
+ for (const artifact of restoreArtifacts) {
52
+ const contentLength = typeof artifact.content == "string" ? Buffer.byteLength(artifact.content) : artifact.content.byteLength
53
+
54
+ restoreHash.update(`${Buffer.byteLength(artifact.path)}:`).update(artifact.path)
55
+ .update(`${contentLength}:`).update(artifact.content)
56
+ }
57
+ const inputHash = restoreHash.digest("hex")
58
+ const projectPath = path.join(context.sourcePath, projects[0].path)
59
+ const packages = path.join(context.buildPath, "nuget-packages")
60
+ const intermediatePath = path.join(context.buildPath, "obj")
61
+ const intermediate = `${intermediatePath}/`
62
+ const output = path.join(context.buildPath, "bin")
63
+ const compileItems = sources.map(source => path.relative(context.sourcePath, source).split(path.sep).join("/")).join("%3B")
64
+ const isolatedProjectProperties = [
65
+ "--property:ImportDirectoryBuildProps=false",
66
+ "--property:ImportDirectoryBuildTargets=false"
67
+ ]
68
+ const restoreArgv = [
69
+ "restore", projectPath, "--source", context.sourcePath, "--packages", packages, "--no-cache", "--force",
70
+ "--disable-parallel",
71
+ ...(restoreArtifacts.some(artifact => artifact.path == "packages.lock.json") ? ["--locked-mode"] : []),
72
+ "--nologo", ...isolatedProjectProperties, `--property:BaseIntermediateOutputPath=${intermediate}`,
73
+ `--property:MSBuildProjectExtensionsPath=${intermediate}`
74
+ ]
75
+ const compileArgv = [
76
+ "build", projectPath, "--configuration", "Release", "--no-restore", "--nologo", "--warnaserror",
77
+ "--disable-build-servers", "--output", output, ...isolatedProjectProperties,
78
+ `--property:SemantifoldCompileItems=${compileItems}`, "--property:UseSharedCompilation=false",
79
+ `--property:BaseIntermediateOutputPath=${intermediate}`,
80
+ `--property:MSBuildProjectExtensionsPath=${intermediate}`
81
+ ]
82
+ const environment = {
83
+ DOTNET_CLI_HOME: path.join(context.buildPath, "dotnet-home"),
84
+ DOTNET_CLI_TELEMETRY_OPTOUT: "1",
85
+ DOTNET_CLI_USE_MSBUILD_SERVER: "0",
86
+ DOTNET_CLI_WORKLOAD_UPDATE_NOTIFY_DISABLE: "1",
87
+ DOTNET_NOLOGO: "1",
88
+ DOTNET_SKIP_FIRST_TIME_EXPERIENCE: "1",
89
+ DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: "1",
90
+ HOME: path.join(context.buildPath, "home"),
91
+ NUGET_HTTP_CACHE_PATH: path.join(context.buildPath, "nuget-http-cache"),
92
+ NUGET_PACKAGES: packages,
93
+ NUGET_PLUGINS_CACHE_PATH: path.join(context.buildPath, "nuget-plugins-cache"),
94
+ TMPDIR: path.join(context.buildPath, "tmp"),
95
+ XDG_CACHE_HOME: path.join(context.buildPath, "cache")
96
+ }
97
+ const environmentPaths = [
98
+ environmentPath("DOTNET_CLI_HOME", "build"), environmentPath("HOME", "build"),
99
+ environmentPath("NUGET_HTTP_CACHE_PATH", "build"), environmentPath("NUGET_PACKAGES", "build"),
100
+ environmentPath("NUGET_PLUGINS_CACHE_PATH", "build"), environmentPath("TMPDIR", "build"),
101
+ environmentPath("XDG_CACHE_HOME", "build")
102
+ ]
103
+ const ownedOutput = checkOutput(context.buildPath, "application/octet-stream", "compiler-output")
104
+ const restore = checkStage({
105
+ argv: restoreArgv,
106
+ cwd: context.sourcePath,
107
+ environment,
108
+ environmentPaths,
109
+ inputHash,
110
+ inputs: restoreInputs,
111
+ output: null,
112
+ pathArguments: [
113
+ pathArgument(1, "source"), pathArgument(3, "source"), pathArgument(5, "build"),
114
+ pathArgument(restoreArgv.length - 2, "build", "--property:BaseIntermediateOutputPath="),
115
+ pathArgument(restoreArgv.length - 1, "build", "--property:MSBuildProjectExtensionsPath=")
116
+ ],
117
+ stage: "restore",
118
+ tool,
119
+ transientPaths: [intermediatePath]
120
+ })
121
+ const compile = checkStage({
122
+ argv: compileArgv,
123
+ cwd: context.sourcePath,
124
+ environment,
125
+ environmentPaths,
126
+ inputs: artifacts,
127
+ output: ownedOutput,
128
+ pathArguments: [
129
+ pathArgument(1, "source"), pathArgument(9, "build"),
130
+ pathArgument(compileArgv.length - 2, "build", "--property:BaseIntermediateOutputPath="),
131
+ pathArgument(compileArgv.length - 1, "build", "--property:MSBuildProjectExtensionsPath=")
132
+ ],
133
+ stage: "compile",
134
+ tool,
135
+ transientPaths: [intermediatePath]
136
+ })
137
+
138
+ return checkPlan(context, "csharp", artifacts, [restore, compile])
139
+ }
@@ -6,7 +6,7 @@ import {validateBackendModule, emitExpression} from "./shared.js"
6
6
  import {emitScalarType} from "./scalars.js"
7
7
  import {SourceWriter} from "./writer.js"
8
8
 
9
- const project = `<Project Sdk="Microsoft.NET.Sdk">
9
+ export const csharpProjectManifest = `<Project Sdk="Microsoft.NET.Sdk">
10
10
  <PropertyGroup>
11
11
  <OutputType>Exe</OutputType>
12
12
  <TargetFramework>net10.0</TargetFramework>
@@ -25,13 +25,14 @@ const project = `<Project Sdk="Microsoft.NET.Sdk">
25
25
  <DebugType>none</DebugType>
26
26
  <UseAppHost>false</UseAppHost>
27
27
  <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
28
+ <SemantifoldCompileItems Condition="'$(SemantifoldCompileItems)' == ''">Program.cs</SemantifoldCompileItems>
28
29
  <AssemblyName>Semantifold</AssemblyName>
29
30
  <RootNamespace>Semantifold.Generated</RootNamespace>
30
31
  <StartupObject>Semantifold.Generated.Program</StartupObject>
31
32
  <PathMap>$(MSBuildProjectDirectory)=/_/</PathMap>
32
33
  </PropertyGroup>
33
34
  <ItemGroup>
34
- <Compile Include="Program.cs" />
35
+ <Compile Include="$(SemantifoldCompileItems)" />
35
36
  </ItemGroup>
36
37
  </Project>
37
38
  `
@@ -66,7 +67,7 @@ export function generateCSharpProject({filename, mapDirective, module, sourceMap
66
67
  provenance: {kind: "text", mapping, sourceMap: toSourceMapV3(mapping)},
67
68
  role: "entry"
68
69
  }, {
69
- content: project,
70
+ content: csharpProjectManifest,
70
71
  contentKind: "text",
71
72
  mediaType: "application/xml",
72
73
  ownership: "generated",