@gasm-compiler/cli 0.1.1 → 0.2.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.
- package/README.md +3 -0
- package/dist/cli.js +89 -15
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
Compile WebAssembly (`.wasm`) or AssemblyScript (`.as`/`.ts`) files to WGSL compute shaders from the command line. Built on top of [`@gasm-compiler/core`](https://www.npmjs.com/package/@gasm-compiler/core).
|
|
6
6
|
|
|
7
|
+
Version 0.2 defaults to strict Gasm v0.2 compilation. Use
|
|
8
|
+
`--spec-version 0.1` only for archived v0.1 inputs.
|
|
9
|
+
|
|
7
10
|
---
|
|
8
11
|
|
|
9
12
|
## Installation
|
package/dist/cli.js
CHANGED
|
@@ -9,10 +9,12 @@ import { parseArgs } from "jsr:@std/cli/parse-args";
|
|
|
9
9
|
import { resolve } from "jsr:@std/path";
|
|
10
10
|
import { ensureFile } from "jsr:@std/fs";
|
|
11
11
|
import {
|
|
12
|
+
compileToArtifact,
|
|
12
13
|
compileWithDiagnostics,
|
|
13
14
|
disassembleToWAT,
|
|
15
|
+
isParseError,
|
|
14
16
|
parseWasmModule,
|
|
15
|
-
|
|
17
|
+
validateGasmMetadataSchema
|
|
16
18
|
} from "@gasm-compiler/core";
|
|
17
19
|
function printDiagnostics(diagnostics, verbose) {
|
|
18
20
|
for (const w of diagnostics.warnings) {
|
|
@@ -24,13 +26,17 @@ function printDiagnostics(diagnostics, verbose) {
|
|
|
24
26
|
for (const d of diagnostics.demotions) {
|
|
25
27
|
counts.set(d.kind, (counts.get(d.kind) ?? 0) + 1);
|
|
26
28
|
}
|
|
27
|
-
const parts = [...counts.entries()].map(
|
|
29
|
+
const parts = [...counts.entries()].map(
|
|
30
|
+
([kind, count]) => `${kind} (${count})`
|
|
31
|
+
);
|
|
28
32
|
console.error(`demotions: ${parts.join(", ")}`);
|
|
29
33
|
}
|
|
30
34
|
}
|
|
31
35
|
async function compileAssemblyScriptToWGSL(inputFile, options = {}) {
|
|
32
36
|
const {
|
|
33
37
|
output: outputFile,
|
|
38
|
+
metadata: metadataFile,
|
|
39
|
+
specVersion: requestedSpecVersion,
|
|
34
40
|
watOutput: watOutputFile,
|
|
35
41
|
verbose = false,
|
|
36
42
|
keepWasm = false,
|
|
@@ -41,6 +47,17 @@ async function compileAssemblyScriptToWGSL(inputFile, options = {}) {
|
|
|
41
47
|
sourceMapping
|
|
42
48
|
} = options;
|
|
43
49
|
try {
|
|
50
|
+
if (requestedSpecVersion !== void 0 && requestedSpecVersion !== "0.1" && requestedSpecVersion !== "0.2") {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`Unsupported spec version "${requestedSpecVersion}"; expected 0.1 or 0.2`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
const specVersion = requestedSpecVersion ?? "0.2";
|
|
56
|
+
if (metadataFile && specVersion !== "0.2") {
|
|
57
|
+
throw new Error(
|
|
58
|
+
"--metadata requires --spec-version 0.2"
|
|
59
|
+
);
|
|
60
|
+
}
|
|
44
61
|
if (verbose) console.log(`[1/4] Validating input file: ${inputFile}`);
|
|
45
62
|
const resolvedInput = resolve(inputFile);
|
|
46
63
|
const stats = await Deno.stat(resolvedInput);
|
|
@@ -49,7 +66,9 @@ async function compileAssemblyScriptToWGSL(inputFile, options = {}) {
|
|
|
49
66
|
}
|
|
50
67
|
const isWasm = resolvedInput.endsWith(".wasm");
|
|
51
68
|
if (!isWasm && !resolvedInput.endsWith(".as") && !resolvedInput.endsWith(".ts")) {
|
|
52
|
-
console.warn(
|
|
69
|
+
console.warn(
|
|
70
|
+
"Warning: Input file does not have .as, .ts, or .wasm extension"
|
|
71
|
+
);
|
|
53
72
|
}
|
|
54
73
|
let wasmPath = resolvedInput.replace(/\.(as|ts)$/, ".wasm");
|
|
55
74
|
if (!isWasm) {
|
|
@@ -104,28 +123,63 @@ ${errorMsg}`);
|
|
|
104
123
|
const demotionPolicy = {};
|
|
105
124
|
if (denyF64Demotion) demotionPolicy.f64 = "deny";
|
|
106
125
|
if (allowI64Demotion) demotionPolicy.i64 = "allow-lossy";
|
|
107
|
-
const validLevels = [
|
|
126
|
+
const validLevels = [
|
|
127
|
+
"none",
|
|
128
|
+
"minimal",
|
|
129
|
+
"normal",
|
|
130
|
+
"detailed",
|
|
131
|
+
"verbose"
|
|
132
|
+
];
|
|
108
133
|
let sourceMappingLevel;
|
|
109
134
|
if (sourceMapping) {
|
|
110
135
|
if (validLevels.includes(sourceMapping)) {
|
|
111
136
|
sourceMappingLevel = sourceMapping;
|
|
112
137
|
} else {
|
|
113
|
-
console.warn(
|
|
138
|
+
console.warn(
|
|
139
|
+
`Warning: Unknown source-mapping level "${sourceMapping}", using "normal"`
|
|
140
|
+
);
|
|
114
141
|
sourceMappingLevel = "normal";
|
|
115
142
|
}
|
|
116
143
|
}
|
|
117
|
-
const
|
|
144
|
+
const compileOptions = {
|
|
118
145
|
optimize,
|
|
119
146
|
demotionPolicy,
|
|
120
147
|
warningsAsErrors,
|
|
121
|
-
sourceMapping: sourceMappingLevel
|
|
122
|
-
|
|
148
|
+
sourceMapping: sourceMappingLevel,
|
|
149
|
+
specVersion
|
|
150
|
+
};
|
|
151
|
+
const result = metadataFile ? compileToArtifact(wasmBytes, compileOptions) : compileWithDiagnostics(wasmBytes, compileOptions);
|
|
123
152
|
printDiagnostics(result.diagnostics, verbose);
|
|
124
153
|
if (!result.ok) {
|
|
125
154
|
const first = result.diagnostics.errors[0];
|
|
126
|
-
throw new Error(
|
|
155
|
+
throw new Error(
|
|
156
|
+
first ? `${first.code}: ${first.message}` : "Compilation failed"
|
|
157
|
+
);
|
|
127
158
|
}
|
|
128
159
|
const wgslCode = result.wgsl;
|
|
160
|
+
if (metadataFile) {
|
|
161
|
+
if (!("metadata" in result)) {
|
|
162
|
+
throw new Error("Compiler did not produce metadata");
|
|
163
|
+
}
|
|
164
|
+
const schema = validateGasmMetadataSchema(result.metadata);
|
|
165
|
+
if (!schema.valid) {
|
|
166
|
+
throw new Error(
|
|
167
|
+
`Generated metadata failed schema validation: ${schema.errors.join(", ")}`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
const resolvedMetadata = resolve(metadataFile);
|
|
171
|
+
await ensureFile(resolvedMetadata);
|
|
172
|
+
await Deno.writeTextFile(
|
|
173
|
+
resolvedMetadata,
|
|
174
|
+
`${JSON.stringify(result.metadata, null, 2)}
|
|
175
|
+
`
|
|
176
|
+
);
|
|
177
|
+
if (verbose) {
|
|
178
|
+
console.log(`\u2713 Metadata written to: ${resolvedMetadata}`);
|
|
179
|
+
} else {
|
|
180
|
+
console.log(`\u2713 Metadata written to: ${metadataFile}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
129
183
|
if (watOutputFile) {
|
|
130
184
|
if (verbose) console.log("[4a/4] Generating WAT disassembly...");
|
|
131
185
|
const parseResult = parseWasmModule(wasmBytes);
|
|
@@ -140,7 +194,9 @@ ${errorMsg}`);
|
|
|
140
194
|
console.log(`\u2713 WAT written to: ${watOutputFile}`);
|
|
141
195
|
}
|
|
142
196
|
} else {
|
|
143
|
-
console.warn(
|
|
197
|
+
console.warn(
|
|
198
|
+
`Warning: Failed to parse WASM for WAT disassembly: ${parseResult.message}`
|
|
199
|
+
);
|
|
144
200
|
}
|
|
145
201
|
}
|
|
146
202
|
if (outputFile) {
|
|
@@ -174,7 +230,10 @@ async function getAssemblyScriptPath() {
|
|
|
174
230
|
const possiblePaths = [
|
|
175
231
|
// Relative to CLI package
|
|
176
232
|
resolve(scriptDir, "../../node_modules/.bin/asc"),
|
|
177
|
-
resolve(
|
|
233
|
+
resolve(
|
|
234
|
+
scriptDir,
|
|
235
|
+
"../../node_modules/.pnpm/assemblyscript@0.28.9/node_modules/assemblyscript/node_modules/.bin/asc"
|
|
236
|
+
),
|
|
178
237
|
resolve(scriptDir, "../node_modules/.bin/asc"),
|
|
179
238
|
resolve(scriptDir, "../examples/node_modules/.bin/asc"),
|
|
180
239
|
// Relative to workspace root
|
|
@@ -196,10 +255,16 @@ async function getAssemblyScriptPath() {
|
|
|
196
255
|
}
|
|
197
256
|
|
|
198
257
|
// src/cli.ts
|
|
199
|
-
var VERSION = "0.
|
|
258
|
+
var VERSION = "0.2.0";
|
|
200
259
|
async function main() {
|
|
201
260
|
const args = parseArgs(Deno.args, {
|
|
202
|
-
string: [
|
|
261
|
+
string: [
|
|
262
|
+
"output",
|
|
263
|
+
"metadata",
|
|
264
|
+
"spec-version",
|
|
265
|
+
"wat-output",
|
|
266
|
+
"source-mapping"
|
|
267
|
+
],
|
|
203
268
|
boolean: [
|
|
204
269
|
"help",
|
|
205
270
|
"version",
|
|
@@ -229,12 +294,16 @@ async function main() {
|
|
|
229
294
|
if (command === "compile") {
|
|
230
295
|
if (args._.length < 2) {
|
|
231
296
|
console.error("Error: compile requires an input file");
|
|
232
|
-
console.error(
|
|
297
|
+
console.error(
|
|
298
|
+
"Usage: cli compile <input.as|wasm> [--output <output.wgsl>]"
|
|
299
|
+
);
|
|
233
300
|
Deno.exit(1);
|
|
234
301
|
}
|
|
235
302
|
const inputFile = args._[1];
|
|
236
303
|
const options = {
|
|
237
304
|
output: args.output,
|
|
305
|
+
metadata: args.metadata,
|
|
306
|
+
specVersion: args["spec-version"],
|
|
238
307
|
watOutput: args["wat-output"],
|
|
239
308
|
verbose: args.verbose,
|
|
240
309
|
keepWasm: args["keep-wasm"],
|
|
@@ -251,7 +320,10 @@ async function main() {
|
|
|
251
320
|
Deno.exit(1);
|
|
252
321
|
}
|
|
253
322
|
} catch (error) {
|
|
254
|
-
console.error(
|
|
323
|
+
console.error(
|
|
324
|
+
"Error:",
|
|
325
|
+
error instanceof Error ? error.message : String(error)
|
|
326
|
+
);
|
|
255
327
|
if (args.verbose && error instanceof Error && error.stack) {
|
|
256
328
|
console.error("\nStack trace:");
|
|
257
329
|
console.error(error.stack);
|
|
@@ -274,6 +346,8 @@ COMMANDS:
|
|
|
274
346
|
|
|
275
347
|
OPTIONS:
|
|
276
348
|
--output <file> Write WGSL output to file (default: stdout)
|
|
349
|
+
--metadata <file> Write validated Gasm v0.2 metadata sidecar JSON
|
|
350
|
+
--spec-version <version> Compile as Gasm 0.1 or 0.2 (default: 0.2)
|
|
277
351
|
--wat-output <file> Also write WAT (WebAssembly Text) disassembly to file
|
|
278
352
|
--verbose Show detailed compilation steps
|
|
279
353
|
--keep-wasm Keep intermediate WASM files
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gasm-compiler/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "CLI for compiling WebAssembly/AssemblyScript to WGSL using Gasm Compiler",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cli.js",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"cli"
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@gasm-compiler/core": "0.
|
|
38
|
+
"@gasm-compiler/core": "0.4.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"assemblyscript": "v0.28.9",
|