@gasm-compiler/cli 0.1.1 → 0.3.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 +123 -16
- package/package.json +2 -4
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,13 @@ 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
|
+
prepareModule,
|
|
18
|
+
validateGasmMetadataSchema
|
|
16
19
|
} from "@gasm-compiler/core";
|
|
17
20
|
function printDiagnostics(diagnostics, verbose) {
|
|
18
21
|
for (const w of diagnostics.warnings) {
|
|
@@ -24,13 +27,19 @@ function printDiagnostics(diagnostics, verbose) {
|
|
|
24
27
|
for (const d of diagnostics.demotions) {
|
|
25
28
|
counts.set(d.kind, (counts.get(d.kind) ?? 0) + 1);
|
|
26
29
|
}
|
|
27
|
-
const parts = [...counts.entries()].map(
|
|
30
|
+
const parts = [...counts.entries()].map(
|
|
31
|
+
([kind, count]) => `${kind} (${count})`
|
|
32
|
+
);
|
|
28
33
|
console.error(`demotions: ${parts.join(", ")}`);
|
|
29
34
|
}
|
|
30
35
|
}
|
|
31
36
|
async function compileAssemblyScriptToWGSL(inputFile, options = {}) {
|
|
32
37
|
const {
|
|
33
38
|
output: outputFile,
|
|
39
|
+
metadata: metadataFile,
|
|
40
|
+
prepare,
|
|
41
|
+
strict = false,
|
|
42
|
+
specVersion: requestedSpecVersion,
|
|
34
43
|
watOutput: watOutputFile,
|
|
35
44
|
verbose = false,
|
|
36
45
|
keepWasm = false,
|
|
@@ -41,6 +50,25 @@ async function compileAssemblyScriptToWGSL(inputFile, options = {}) {
|
|
|
41
50
|
sourceMapping
|
|
42
51
|
} = options;
|
|
43
52
|
try {
|
|
53
|
+
if (requestedSpecVersion !== void 0 && requestedSpecVersion !== "0.1" && requestedSpecVersion !== "0.2") {
|
|
54
|
+
throw new Error(
|
|
55
|
+
`Unsupported spec version "${requestedSpecVersion}"; expected 0.1 or 0.2`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
if (prepare !== void 0 && prepare !== "auto") {
|
|
59
|
+
throw new Error(
|
|
60
|
+
`Unsupported prepare mode "${prepare}"; expected "auto"`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
if (prepare && strict) {
|
|
64
|
+
throw new Error("--prepare and --strict cannot be used together");
|
|
65
|
+
}
|
|
66
|
+
const specVersion = requestedSpecVersion ?? "0.2";
|
|
67
|
+
if (metadataFile && specVersion !== "0.2") {
|
|
68
|
+
throw new Error(
|
|
69
|
+
"--metadata requires --spec-version 0.2"
|
|
70
|
+
);
|
|
71
|
+
}
|
|
44
72
|
if (verbose) console.log(`[1/4] Validating input file: ${inputFile}`);
|
|
45
73
|
const resolvedInput = resolve(inputFile);
|
|
46
74
|
const stats = await Deno.stat(resolvedInput);
|
|
@@ -49,7 +77,9 @@ async function compileAssemblyScriptToWGSL(inputFile, options = {}) {
|
|
|
49
77
|
}
|
|
50
78
|
const isWasm = resolvedInput.endsWith(".wasm");
|
|
51
79
|
if (!isWasm && !resolvedInput.endsWith(".as") && !resolvedInput.endsWith(".ts")) {
|
|
52
|
-
console.warn(
|
|
80
|
+
console.warn(
|
|
81
|
+
"Warning: Input file does not have .as, .ts, or .wasm extension"
|
|
82
|
+
);
|
|
53
83
|
}
|
|
54
84
|
let wasmPath = resolvedInput.replace(/\.(as|ts)$/, ".wasm");
|
|
55
85
|
if (!isWasm) {
|
|
@@ -104,31 +134,82 @@ ${errorMsg}`);
|
|
|
104
134
|
const demotionPolicy = {};
|
|
105
135
|
if (denyF64Demotion) demotionPolicy.f64 = "deny";
|
|
106
136
|
if (allowI64Demotion) demotionPolicy.i64 = "allow-lossy";
|
|
107
|
-
const validLevels = [
|
|
137
|
+
const validLevels = [
|
|
138
|
+
"none",
|
|
139
|
+
"minimal",
|
|
140
|
+
"normal",
|
|
141
|
+
"detailed",
|
|
142
|
+
"verbose"
|
|
143
|
+
];
|
|
108
144
|
let sourceMappingLevel;
|
|
109
145
|
if (sourceMapping) {
|
|
110
146
|
if (validLevels.includes(sourceMapping)) {
|
|
111
147
|
sourceMappingLevel = sourceMapping;
|
|
112
148
|
} else {
|
|
113
|
-
console.warn(
|
|
149
|
+
console.warn(
|
|
150
|
+
`Warning: Unknown source-mapping level "${sourceMapping}", using "normal"`
|
|
151
|
+
);
|
|
114
152
|
sourceMappingLevel = "normal";
|
|
115
153
|
}
|
|
116
154
|
}
|
|
117
|
-
|
|
155
|
+
let compileOptions = {
|
|
118
156
|
optimize,
|
|
119
157
|
demotionPolicy,
|
|
120
158
|
warningsAsErrors,
|
|
121
|
-
sourceMapping: sourceMappingLevel
|
|
122
|
-
|
|
159
|
+
sourceMapping: sourceMappingLevel,
|
|
160
|
+
specVersion
|
|
161
|
+
};
|
|
162
|
+
let compilationBytes = wasmBytes;
|
|
163
|
+
if (prepare === "auto" || strict) {
|
|
164
|
+
const prepared = prepareModule(wasmBytes, {
|
|
165
|
+
...compileOptions,
|
|
166
|
+
mode: strict ? "strict" : "integrator"
|
|
167
|
+
});
|
|
168
|
+
for (const advisory of prepared.advisories) {
|
|
169
|
+
console.error(`advisory: ${advisory.message}`);
|
|
170
|
+
}
|
|
171
|
+
if (prepared.errors.length > 0) {
|
|
172
|
+
const first = prepared.errors[0];
|
|
173
|
+
throw new Error(`${first.code}: ${first.message}`);
|
|
174
|
+
}
|
|
175
|
+
compilationBytes = prepared.wasmBytes;
|
|
176
|
+
compileOptions = prepared.compileOptions;
|
|
177
|
+
}
|
|
178
|
+
const result = metadataFile ? compileToArtifact(compilationBytes, compileOptions) : compileWithDiagnostics(compilationBytes, compileOptions);
|
|
123
179
|
printDiagnostics(result.diagnostics, verbose);
|
|
124
180
|
if (!result.ok) {
|
|
125
181
|
const first = result.diagnostics.errors[0];
|
|
126
|
-
throw new Error(
|
|
182
|
+
throw new Error(
|
|
183
|
+
first ? `${first.code}: ${first.message}` : "Compilation failed"
|
|
184
|
+
);
|
|
127
185
|
}
|
|
128
186
|
const wgslCode = result.wgsl;
|
|
187
|
+
if (metadataFile) {
|
|
188
|
+
if (!("metadata" in result)) {
|
|
189
|
+
throw new Error("Compiler did not produce metadata");
|
|
190
|
+
}
|
|
191
|
+
const schema = validateGasmMetadataSchema(result.metadata);
|
|
192
|
+
if (!schema.valid) {
|
|
193
|
+
throw new Error(
|
|
194
|
+
`Generated metadata failed schema validation: ${schema.errors.join(", ")}`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
const resolvedMetadata = resolve(metadataFile);
|
|
198
|
+
await ensureFile(resolvedMetadata);
|
|
199
|
+
await Deno.writeTextFile(
|
|
200
|
+
resolvedMetadata,
|
|
201
|
+
`${JSON.stringify(result.metadata, null, 2)}
|
|
202
|
+
`
|
|
203
|
+
);
|
|
204
|
+
if (verbose) {
|
|
205
|
+
console.log(`\u2713 Metadata written to: ${resolvedMetadata}`);
|
|
206
|
+
} else {
|
|
207
|
+
console.log(`\u2713 Metadata written to: ${metadataFile}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
129
210
|
if (watOutputFile) {
|
|
130
211
|
if (verbose) console.log("[4a/4] Generating WAT disassembly...");
|
|
131
|
-
const parseResult = parseWasmModule(
|
|
212
|
+
const parseResult = parseWasmModule(compilationBytes);
|
|
132
213
|
if (!isParseError(parseResult)) {
|
|
133
214
|
const watCode = disassembleToWAT(parseResult);
|
|
134
215
|
const resolvedWatOutput = resolve(watOutputFile);
|
|
@@ -140,7 +221,9 @@ ${errorMsg}`);
|
|
|
140
221
|
console.log(`\u2713 WAT written to: ${watOutputFile}`);
|
|
141
222
|
}
|
|
142
223
|
} else {
|
|
143
|
-
console.warn(
|
|
224
|
+
console.warn(
|
|
225
|
+
`Warning: Failed to parse WASM for WAT disassembly: ${parseResult.message}`
|
|
226
|
+
);
|
|
144
227
|
}
|
|
145
228
|
}
|
|
146
229
|
if (outputFile) {
|
|
@@ -174,7 +257,10 @@ async function getAssemblyScriptPath() {
|
|
|
174
257
|
const possiblePaths = [
|
|
175
258
|
// Relative to CLI package
|
|
176
259
|
resolve(scriptDir, "../../node_modules/.bin/asc"),
|
|
177
|
-
resolve(
|
|
260
|
+
resolve(
|
|
261
|
+
scriptDir,
|
|
262
|
+
"../../node_modules/.pnpm/assemblyscript@0.28.9/node_modules/assemblyscript/node_modules/.bin/asc"
|
|
263
|
+
),
|
|
178
264
|
resolve(scriptDir, "../node_modules/.bin/asc"),
|
|
179
265
|
resolve(scriptDir, "../examples/node_modules/.bin/asc"),
|
|
180
266
|
// Relative to workspace root
|
|
@@ -196,16 +282,24 @@ async function getAssemblyScriptPath() {
|
|
|
196
282
|
}
|
|
197
283
|
|
|
198
284
|
// src/cli.ts
|
|
199
|
-
var VERSION = "0.
|
|
285
|
+
var VERSION = "0.3.0";
|
|
200
286
|
async function main() {
|
|
201
287
|
const args = parseArgs(Deno.args, {
|
|
202
|
-
string: [
|
|
288
|
+
string: [
|
|
289
|
+
"output",
|
|
290
|
+
"metadata",
|
|
291
|
+
"prepare",
|
|
292
|
+
"spec-version",
|
|
293
|
+
"wat-output",
|
|
294
|
+
"source-mapping"
|
|
295
|
+
],
|
|
203
296
|
boolean: [
|
|
204
297
|
"help",
|
|
205
298
|
"version",
|
|
206
299
|
"verbose",
|
|
207
300
|
"keep-wasm",
|
|
208
301
|
"optimize",
|
|
302
|
+
"strict",
|
|
209
303
|
"deny-f64-demotion",
|
|
210
304
|
"allow-i64-demotion",
|
|
211
305
|
"warnings-as-errors"
|
|
@@ -229,12 +323,18 @@ async function main() {
|
|
|
229
323
|
if (command === "compile") {
|
|
230
324
|
if (args._.length < 2) {
|
|
231
325
|
console.error("Error: compile requires an input file");
|
|
232
|
-
console.error(
|
|
326
|
+
console.error(
|
|
327
|
+
"Usage: cli compile <input.as|wasm> [--output <output.wgsl>]"
|
|
328
|
+
);
|
|
233
329
|
Deno.exit(1);
|
|
234
330
|
}
|
|
235
331
|
const inputFile = args._[1];
|
|
236
332
|
const options = {
|
|
237
333
|
output: args.output,
|
|
334
|
+
metadata: args.metadata,
|
|
335
|
+
prepare: args.prepare,
|
|
336
|
+
strict: args.strict,
|
|
337
|
+
specVersion: args["spec-version"],
|
|
238
338
|
watOutput: args["wat-output"],
|
|
239
339
|
verbose: args.verbose,
|
|
240
340
|
keepWasm: args["keep-wasm"],
|
|
@@ -251,7 +351,10 @@ async function main() {
|
|
|
251
351
|
Deno.exit(1);
|
|
252
352
|
}
|
|
253
353
|
} catch (error) {
|
|
254
|
-
console.error(
|
|
354
|
+
console.error(
|
|
355
|
+
"Error:",
|
|
356
|
+
error instanceof Error ? error.message : String(error)
|
|
357
|
+
);
|
|
255
358
|
if (args.verbose && error instanceof Error && error.stack) {
|
|
256
359
|
console.error("\nStack trace:");
|
|
257
360
|
console.error(error.stack);
|
|
@@ -274,6 +377,10 @@ COMMANDS:
|
|
|
274
377
|
|
|
275
378
|
OPTIONS:
|
|
276
379
|
--output <file> Write WGSL output to file (default: stdout)
|
|
380
|
+
--metadata <file> Write validated Gasm v0.2 metadata sidecar JSON
|
|
381
|
+
--prepare auto Infer and inject extensions for transient builds
|
|
382
|
+
--strict Analyze without modifying the input Wasm
|
|
383
|
+
--spec-version <version> Compile as Gasm 0.1 or 0.2 (default: 0.2)
|
|
277
384
|
--wat-output <file> Also write WAT (WebAssembly Text) disassembly to file
|
|
278
385
|
--verbose Show detailed compilation steps
|
|
279
386
|
--keep-wasm Keep intermediate WASM files
|
package/package.json
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gasm-compiler/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "CLI for compiling WebAssembly/AssemblyScript to WGSL using Gasm Compiler",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cli.js",
|
|
7
|
-
"types": "./dist/cli.d.ts",
|
|
8
7
|
"bin": {
|
|
9
8
|
"gasm-compiler": "./dist/cli.js"
|
|
10
9
|
},
|
|
11
10
|
"exports": {
|
|
12
11
|
".": {
|
|
13
|
-
"types": "./dist/cli.d.ts",
|
|
14
12
|
"import": "./dist/cli.js",
|
|
15
13
|
"default": "./dist/cli.js"
|
|
16
14
|
}
|
|
@@ -35,7 +33,7 @@
|
|
|
35
33
|
"cli"
|
|
36
34
|
],
|
|
37
35
|
"dependencies": {
|
|
38
|
-
"@gasm-compiler/core": "0.
|
|
36
|
+
"@gasm-compiler/core": "0.5.0"
|
|
39
37
|
},
|
|
40
38
|
"devDependencies": {
|
|
41
39
|
"assemblyscript": "v0.28.9",
|