visaljs 0.0.1 → 0.0.2
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 +17 -1
- package/cli.js +194 -35
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,9 +9,24 @@ visaljs build app.js -o app
|
|
|
9
9
|
./app
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
Needs Node 22 or later and a C compiler (`cc`, `gcc` or `clang`). Linux and macOS for now.
|
|
12
|
+
Needs Node 22 or later and a C compiler (`cc`, `gcc` or `clang`). Linux and macOS for now; on Windows, run it under WSL.
|
|
13
13
|
The C runtime is built on first use and cached in `~/.cache/visaljs`.
|
|
14
14
|
|
|
15
|
+
`visaljs build` reports each step as it goes:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
✓ 🔍 Detected GCC 13.3.0 10ms
|
|
19
|
+
✓ 📦 Rolled 6 modules into a single file (63.7 kB) 37ms
|
|
20
|
+
✓ 🌳 Parsed 1,988 lines of JavaScript 60ms
|
|
21
|
+
✓ 🧩 Compiled 1 regular expression 6ms
|
|
22
|
+
✓ 🔧 Converted 272 functions to IR and optimized them 46ms
|
|
23
|
+
✓ 📝 Emitted 21,951 lines of C code (1.2 MB) 39ms
|
|
24
|
+
✓ 📚 Runtime libraries ready (cached ⚡) 39ms
|
|
25
|
+
✓ 🔨 Compiled the binary (-O2, stripped) 6.57s
|
|
26
|
+
|
|
27
|
+
🎉 Built srv in 6.85s: 1.2 MB, fits on a floppy disk 💾
|
|
28
|
+
```
|
|
29
|
+
|
|
15
30
|
```
|
|
16
31
|
usage: visaljs build <entry.js> [options]
|
|
17
32
|
|
|
@@ -25,6 +40,7 @@ options:
|
|
|
25
40
|
--all-builtins install every built-in, not only those the program names
|
|
26
41
|
--no-bundle skip the bundler: the input is one ES module (a bundle)
|
|
27
42
|
--script compile as a classic script (sloppy mode, no bundler)
|
|
43
|
+
-q, --quiet don't report each step of the build
|
|
28
44
|
--version print the version
|
|
29
45
|
--help print this help
|
|
30
46
|
```
|
package/cli.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { isBuiltin } from "node:module";
|
|
3
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
5
|
import { parseArgs } from "node:util";
|
|
6
6
|
import ts from "typescript";
|
|
7
7
|
import { rolldown } from "rolldown";
|
|
8
|
-
import { execFileSync, spawnSync } from "node:child_process";
|
|
8
|
+
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
9
9
|
import { createHash } from "node:crypto";
|
|
10
10
|
import { availableParallelism, homedir, tmpdir } from "node:os";
|
|
11
11
|
//#region \0rolldown/runtime.js
|
|
@@ -7470,7 +7470,8 @@ async function bundle(entry, hosts, warn) {
|
|
|
7470
7470
|
const chunk = output[0];
|
|
7471
7471
|
return {
|
|
7472
7472
|
code: chunk.code,
|
|
7473
|
-
map: chunk.map?.toString() ?? ""
|
|
7473
|
+
map: chunk.map?.toString() ?? "",
|
|
7474
|
+
modules: chunk.moduleIds.filter((id) => !id.startsWith("\0")).length
|
|
7474
7475
|
};
|
|
7475
7476
|
} finally {
|
|
7476
7477
|
await build.close();
|
|
@@ -7531,7 +7532,10 @@ function formatLog(log, kind) {
|
|
|
7531
7532
|
//#region cli/src/cc.ts
|
|
7532
7533
|
/** $CC, else the first of the usual C compilers on the PATH. */
|
|
7533
7534
|
function findCompiler() {
|
|
7534
|
-
if (process.env.CC)
|
|
7535
|
+
if (process.env.CC) {
|
|
7536
|
+
if (!compilerVersion(process.env.CC)) throw new Error(`$CC (${process.env.CC}) doesn't run`);
|
|
7537
|
+
return process.env.CC;
|
|
7538
|
+
}
|
|
7535
7539
|
for (const cc of [
|
|
7536
7540
|
"cc",
|
|
7537
7541
|
"clang",
|
|
@@ -7552,6 +7556,16 @@ const COMMON = [
|
|
|
7552
7556
|
];
|
|
7553
7557
|
const LINK = process.platform === "darwin" ? ["-Wl,-dead_strip"] : ["-Wl,--gc-sections"];
|
|
7554
7558
|
const STRIP = process.platform === "darwin" ? ["-Wl,-S,-x"] : ["-s"];
|
|
7559
|
+
/** "GCC 13.3.0", "clang 18.1.3": the compiler's name and version, else its command. */
|
|
7560
|
+
function describeCompiler(cc) {
|
|
7561
|
+
const text = compilerVersion(cc);
|
|
7562
|
+
const first = text.split("\n")[0] ?? "";
|
|
7563
|
+
const version = (/version (\d+\.\d+(?:\.\d+)?)/.exec(first) ?? /(\d+\.\d+(?:\.\d+)?)\S*\s*$/.exec(first))?.[1];
|
|
7564
|
+
if (!version) return cc;
|
|
7565
|
+
if (/clang/i.test(text)) return `${/apple/i.test(text) ? "Apple clang" : "clang"} ${version}`;
|
|
7566
|
+
if (/gcc|free software foundation/i.test(text)) return `GCC ${version}`;
|
|
7567
|
+
return `${cc} ${version}`;
|
|
7568
|
+
}
|
|
7555
7569
|
function compilerVersion(cc) {
|
|
7556
7570
|
try {
|
|
7557
7571
|
return execFileSync(cc, ["--version"], { encoding: "utf8" });
|
|
@@ -7559,8 +7573,9 @@ function compilerVersion(cc) {
|
|
|
7559
7573
|
return "";
|
|
7560
7574
|
}
|
|
7561
7575
|
}
|
|
7562
|
-
/** Builds (or reuses) lib<name>.a for this toolchain; returns its path.
|
|
7563
|
-
|
|
7576
|
+
/** Builds (or reuses) lib<name>.a for this toolchain; returns its path. `building` hears
|
|
7577
|
+
* about a library that isn't cached, before it's built. */
|
|
7578
|
+
function libraryArchive(tc, lib, building) {
|
|
7564
7579
|
const hash = createHash("sha256");
|
|
7565
7580
|
hash.update(`${tc.cc}\n${compilerVersion(tc.cc)}\n${[
|
|
7566
7581
|
...COMMON,
|
|
@@ -7573,6 +7588,7 @@ function libraryArchive(tc, lib) {
|
|
|
7573
7588
|
const dir = join(root, `${lib.name}-${key}`);
|
|
7574
7589
|
const archive = join(dir, `lib${lib.name}.a`);
|
|
7575
7590
|
if (existsSync(archive)) return archive;
|
|
7591
|
+
building?.(lib.name);
|
|
7576
7592
|
const work = join(root, `tmp-${process.pid}-${Date.now()}`);
|
|
7577
7593
|
mkdirSync(work, { recursive: true });
|
|
7578
7594
|
try {
|
|
@@ -7636,11 +7652,6 @@ function compileAll(commands) {
|
|
|
7636
7652
|
]
|
|
7637
7653
|
});
|
|
7638
7654
|
}
|
|
7639
|
-
/** The runtime's archives for this toolchain, in link order: libjsrt.a, then libbearssl.a
|
|
7640
|
-
* (the test262 host links them too). */
|
|
7641
|
-
function runtimeArchives(tc) {
|
|
7642
|
-
return runtimeLibraries().map((l) => libraryArchive(tc, l));
|
|
7643
|
-
}
|
|
7644
7655
|
function cacheRoot() {
|
|
7645
7656
|
return process.env.VISALJS_CACHE ?? join(process.env.XDG_CACHE_HOME ?? join(homedir(), ".cache"), "visaljs");
|
|
7646
7657
|
}
|
|
@@ -7680,12 +7691,17 @@ function regexpCompiler(cc) {
|
|
|
7680
7691
|
renameSync(tmp, tool);
|
|
7681
7692
|
return tool;
|
|
7682
7693
|
}
|
|
7683
|
-
/**
|
|
7684
|
-
* use).
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
|
|
7694
|
+
/** A program's archives, in link order: the host packages' libraries, then libjsrt (which they
|
|
7695
|
+
* use). Each is built the first time; `building` hears about it. */
|
|
7696
|
+
function programArchives(tc, hosts, building) {
|
|
7697
|
+
return [...hosts.flatMap((h) => h.libraries(RUNTIME_INCLUDE)).filter((l) => l.sources.length), ...runtimeLibraries()].map((l) => libraryArchive(tc, l, building));
|
|
7698
|
+
}
|
|
7699
|
+
/** Compiles program.c and links it with `archives` (programArchives). The linker takes only
|
|
7700
|
+
* what the program reaches: a program without I/O links no libuv. The libraries keep their
|
|
7701
|
+
* debug info (-g); stripping, when asked, happens at this link. Asynchronous: the build's
|
|
7702
|
+
* report keeps drawing while the compiler runs. */
|
|
7703
|
+
function compileProgram(tc, cFile, output, archives, extraFlags = []) {
|
|
7704
|
+
const args = [
|
|
7689
7705
|
...COMMON,
|
|
7690
7706
|
...tc.opt,
|
|
7691
7707
|
...extraFlags,
|
|
@@ -7697,11 +7713,14 @@ function compileProgram(tc, cFile, output, hosts, extraFlags = []) {
|
|
|
7697
7713
|
...tc.strip ? STRIP : [],
|
|
7698
7714
|
"-o",
|
|
7699
7715
|
output
|
|
7700
|
-
]
|
|
7701
|
-
|
|
7702
|
-
|
|
7703
|
-
|
|
7704
|
-
|
|
7716
|
+
];
|
|
7717
|
+
return new Promise((resolve, reject) => {
|
|
7718
|
+
spawn(tc.cc, args, { stdio: [
|
|
7719
|
+
"ignore",
|
|
7720
|
+
"inherit",
|
|
7721
|
+
"inherit"
|
|
7722
|
+
] }).on("error", reject).on("close", (code) => code === 0 ? resolve() : reject(/* @__PURE__ */ new Error(`${tc.cc} exited with code ${code}`)));
|
|
7723
|
+
});
|
|
7705
7724
|
}
|
|
7706
7725
|
function scratchDir() {
|
|
7707
7726
|
const dir = join(tmpdir(), `visaljs-${process.pid}-${Date.now()}`);
|
|
@@ -7758,8 +7777,115 @@ function compileRegExps(cc, literals) {
|
|
|
7758
7777
|
return bytecode;
|
|
7759
7778
|
}
|
|
7760
7779
|
//#endregion
|
|
7780
|
+
//#region cli/src/report.ts
|
|
7781
|
+
const CLEAR = "\r\x1B[2K";
|
|
7782
|
+
const FRAMES = [
|
|
7783
|
+
"⠋",
|
|
7784
|
+
"⠙",
|
|
7785
|
+
"⠹",
|
|
7786
|
+
"⠸",
|
|
7787
|
+
"⠼",
|
|
7788
|
+
"⠴",
|
|
7789
|
+
"⠦",
|
|
7790
|
+
"⠧",
|
|
7791
|
+
"⠇",
|
|
7792
|
+
"⠏"
|
|
7793
|
+
];
|
|
7794
|
+
var Report = class {
|
|
7795
|
+
#write;
|
|
7796
|
+
#style;
|
|
7797
|
+
#start = performance.now();
|
|
7798
|
+
#running;
|
|
7799
|
+
#timer;
|
|
7800
|
+
#frame = 0;
|
|
7801
|
+
/** No style: reports nothing (--quiet, and callers that capture the CLI's output). */
|
|
7802
|
+
constructor(write, style) {
|
|
7803
|
+
this.#write = write;
|
|
7804
|
+
this.#style = style;
|
|
7805
|
+
}
|
|
7806
|
+
/** Runs one step: `label` while it runs, then `done(result)`. A failed step is crossed out
|
|
7807
|
+
* and its error rethrown, for the caller to print. The spinner turns only while the event
|
|
7808
|
+
* loop is free: an asynchronous step. */
|
|
7809
|
+
async step(icon, label, run, done) {
|
|
7810
|
+
this.#running = {
|
|
7811
|
+
icon,
|
|
7812
|
+
label,
|
|
7813
|
+
since: performance.now()
|
|
7814
|
+
};
|
|
7815
|
+
if (this.#live) {
|
|
7816
|
+
this.#draw();
|
|
7817
|
+
this.#timer = setInterval(() => (this.#frame++, this.#draw()), 80);
|
|
7818
|
+
this.#timer.unref();
|
|
7819
|
+
}
|
|
7820
|
+
let result;
|
|
7821
|
+
try {
|
|
7822
|
+
result = await run();
|
|
7823
|
+
} catch (e) {
|
|
7824
|
+
this.#end(this.#paint("31", `✗ ${label}`));
|
|
7825
|
+
throw e;
|
|
7826
|
+
}
|
|
7827
|
+
this.#end(`${this.#paint("32", "✓")} ${icon} ${done(result)}`);
|
|
7828
|
+
return result;
|
|
7829
|
+
}
|
|
7830
|
+
/** Renames the running step (what it turned out to be doing). */
|
|
7831
|
+
relabel(label) {
|
|
7832
|
+
if (!this.#running) return;
|
|
7833
|
+
this.#running.label = label;
|
|
7834
|
+
if (this.#live) this.#draw();
|
|
7835
|
+
}
|
|
7836
|
+
/** A message from inside a step (a bundler warning): printed even when quiet. */
|
|
7837
|
+
note(text) {
|
|
7838
|
+
if (this.#live && this.#running) this.#write(CLEAR);
|
|
7839
|
+
this.#write(`${text}\n`);
|
|
7840
|
+
if (this.#live && this.#running) this.#draw();
|
|
7841
|
+
}
|
|
7842
|
+
/** The last line, given the whole build's time. */
|
|
7843
|
+
finish(icon, text) {
|
|
7844
|
+
if (this.#style) this.#write(`\n ${icon} ${this.#paint("1", text(since(this.#start)))}\n`);
|
|
7845
|
+
}
|
|
7846
|
+
get #live() {
|
|
7847
|
+
return this.#style?.live === true;
|
|
7848
|
+
}
|
|
7849
|
+
#paint(code, text) {
|
|
7850
|
+
return this.#style?.color ? `\x1b[${code}m${text}\x1b[0m` : text;
|
|
7851
|
+
}
|
|
7852
|
+
#draw() {
|
|
7853
|
+
const { icon, label } = this.#running;
|
|
7854
|
+
this.#write(`${CLEAR} ${this.#paint("36", FRAMES[this.#frame % FRAMES.length])} ${icon} ${this.#paint("2", `${label}…`)}`);
|
|
7855
|
+
}
|
|
7856
|
+
#end(line) {
|
|
7857
|
+
clearInterval(this.#timer);
|
|
7858
|
+
const start = this.#running.since;
|
|
7859
|
+
this.#running = void 0;
|
|
7860
|
+
if (this.#style) this.#write(`${this.#live ? CLEAR : ""} ${line} ${this.#paint("2", since(start))}\n`);
|
|
7861
|
+
}
|
|
7862
|
+
};
|
|
7863
|
+
function since(start) {
|
|
7864
|
+
const ms = performance.now() - start;
|
|
7865
|
+
return ms < 1e3 ? `${Math.round(ms)}ms` : `${(ms / 1e3).toFixed(2)}s`;
|
|
7866
|
+
}
|
|
7867
|
+
/** 383.2 kB, 1.2 MB (decimal units). */
|
|
7868
|
+
function size(bytes) {
|
|
7869
|
+
if (bytes < 1e3) return `${bytes} B`;
|
|
7870
|
+
return bytes < 1e6 ? `${(bytes / 1e3).toFixed(1)} kB` : `${(bytes / 1e6).toFixed(1)} MB`;
|
|
7871
|
+
}
|
|
7872
|
+
/** The binary's size, put in scale. */
|
|
7873
|
+
function sizeQuip(bytes) {
|
|
7874
|
+
const FLOPPY = 1474560;
|
|
7875
|
+
if (bytes <= 65536) return "small enough for a Commodore 64 🕹️";
|
|
7876
|
+
if (bytes <= FLOPPY) return "fits on a floppy disk 💾";
|
|
7877
|
+
return `${Math.ceil(bytes / FLOPPY)} floppy disks' worth 💾`;
|
|
7878
|
+
}
|
|
7879
|
+
/** "1 module", "1,204 lines". */
|
|
7880
|
+
const count = (n, one, many = `${one}s`) => `${n.toLocaleString("en-US")} ${n === 1 ? one : many}`;
|
|
7881
|
+
function lineCount(text) {
|
|
7882
|
+
let n = text.length > 0 && !text.endsWith("\n") ? 1 : 0;
|
|
7883
|
+
for (let i = text.indexOf("\n"); i !== -1; i = text.indexOf("\n", i + 1)) n++;
|
|
7884
|
+
return n;
|
|
7885
|
+
}
|
|
7886
|
+
//#endregion
|
|
7761
7887
|
//#region cli/src/main.ts
|
|
7762
|
-
const VERSION = "0.0.
|
|
7888
|
+
const VERSION = "0.0.2";
|
|
7763
7889
|
const USAGE = `usage: visaljs build <entry.js> [options]
|
|
7764
7890
|
|
|
7765
7891
|
options:
|
|
@@ -7772,6 +7898,7 @@ options:
|
|
|
7772
7898
|
--all-builtins install every built-in, not only those the program names
|
|
7773
7899
|
--no-bundle skip the bundler: the input is one ES module (a bundle)
|
|
7774
7900
|
--script compile as a classic script (sloppy mode, no bundler)
|
|
7901
|
+
-q, --quiet don't report each step of the build
|
|
7775
7902
|
--version print the version
|
|
7776
7903
|
--help print this help
|
|
7777
7904
|
`;
|
|
@@ -7795,6 +7922,10 @@ async function main(argv, io) {
|
|
|
7795
7922
|
"all-builtins": { type: "boolean" },
|
|
7796
7923
|
"no-bundle": { type: "boolean" },
|
|
7797
7924
|
script: { type: "boolean" },
|
|
7925
|
+
quiet: {
|
|
7926
|
+
type: "boolean",
|
|
7927
|
+
short: "q"
|
|
7928
|
+
},
|
|
7798
7929
|
version: { type: "boolean" },
|
|
7799
7930
|
help: {
|
|
7800
7931
|
type: "boolean",
|
|
@@ -7815,6 +7946,10 @@ async function main(argv, io) {
|
|
|
7815
7946
|
io.stdout(USAGE);
|
|
7816
7947
|
return values.help ? 0 : 2;
|
|
7817
7948
|
}
|
|
7949
|
+
if ((io.platform ?? process.platform) === "win32") {
|
|
7950
|
+
io.stderr("visaljs: Windows is not supported yet; use Linux or macOS (on Windows, WSL works)\n");
|
|
7951
|
+
return 1;
|
|
7952
|
+
}
|
|
7818
7953
|
const [command, ...rest] = positionals;
|
|
7819
7954
|
switch (command) {
|
|
7820
7955
|
case "build":
|
|
@@ -7836,13 +7971,23 @@ async function build(entry, options, io) {
|
|
|
7836
7971
|
io.stderr(`visaljs: cannot read ${entry}: ${e.message}\n`);
|
|
7837
7972
|
return 1;
|
|
7838
7973
|
}
|
|
7974
|
+
const dump = options["dump-ast"] === true || options["dump-ir"] === true;
|
|
7975
|
+
const report = new Report(io.stderr, options.quiet || dump ? void 0 : io.report);
|
|
7976
|
+
const binary = !dump && !options["emit-c"];
|
|
7839
7977
|
const hosts = DEFAULT_HOSTS;
|
|
7840
7978
|
const primitives = hostPrimitives(hosts);
|
|
7979
|
+
let cc;
|
|
7980
|
+
if (binary) try {
|
|
7981
|
+
cc = await report.step("🔍", "Detecting a C compiler", findCompiler, (cc) => `Detected ${describeCompiler(cc)}${process.env.CC ? " (from $CC)" : ""}`);
|
|
7982
|
+
} catch (e) {
|
|
7983
|
+
io.stderr(`visaljs: ${e.message}\n`);
|
|
7984
|
+
return 1;
|
|
7985
|
+
}
|
|
7841
7986
|
const script = options.script === true;
|
|
7842
7987
|
const bundled = !script && options["no-bundle"] !== true;
|
|
7843
7988
|
let map;
|
|
7844
7989
|
if (bundled) try {
|
|
7845
|
-
const b = await bundle(entry, hosts, (message) =>
|
|
7990
|
+
const b = await report.step("📦", "Rolling the modules into a single file", () => bundle(entry, hosts, (message) => report.note(message)), (b) => `Rolled ${count(b.modules, "module")} into a single file (${size(Buffer.byteLength(b.code))})`);
|
|
7846
7991
|
source = b.code;
|
|
7847
7992
|
if (b.map) map = new SourceMap(b.map, displayPath);
|
|
7848
7993
|
} catch (e) {
|
|
@@ -7852,30 +7997,30 @@ async function build(entry, options, io) {
|
|
|
7852
7997
|
}
|
|
7853
7998
|
let c;
|
|
7854
7999
|
try {
|
|
7855
|
-
const ast = script ? parseScript(source, entry) : parseModule(source, map ? "bundle.js" : entry, map, resolve(entry), primitives);
|
|
8000
|
+
const ast = await report.step("🌳", "Parsing the JavaScript", () => script ? parseScript(source, entry) : parseModule(source, map ? "bundle.js" : entry, map, resolve(entry), primitives), () => `Parsed ${count(lineCount(source), "line")} of JavaScript`);
|
|
7856
8001
|
if (options["dump-ast"]) {
|
|
7857
8002
|
io.stdout(JSON.stringify(ast, null, 2) + "\n");
|
|
7858
8003
|
return 0;
|
|
7859
8004
|
}
|
|
7860
8005
|
const info = analyze(ast);
|
|
7861
8006
|
const literals = regexpLiterals(ast);
|
|
7862
|
-
const regexps = literals.length ? compileRegExps(findCompiler(), literals) : /* @__PURE__ */ new Map();
|
|
8007
|
+
const regexps = literals.length ? await report.step("🧩", "Compiling the regular expressions", () => compileRegExps(cc ??= findCompiler(), literals), () => `Compiled ${count(literals.length, "regular expression")}`) : /* @__PURE__ */ new Map();
|
|
7863
8008
|
const unsupported = firstUnsupported(ast);
|
|
7864
8009
|
if (unsupported) throw unsupported;
|
|
7865
|
-
const ir = optimize(lower(ast, info));
|
|
8010
|
+
const ir = await report.step("🔧", "Converting to IR and optimizing", () => optimize(lower(ast, info)), (ir) => `Converted ${count(ir.functions.length + 1, "function")} to IR and optimized them`);
|
|
7866
8011
|
if (options["dump-ir"]) {
|
|
7867
8012
|
io.stdout(printModule(ir));
|
|
7868
8013
|
return 0;
|
|
7869
8014
|
}
|
|
7870
8015
|
const names = programNames(ast);
|
|
7871
8016
|
const all = options["all-builtins"] === true;
|
|
7872
|
-
c = emitProgram(ir, {
|
|
8017
|
+
c = await report.step("📝", "Emitting C code", () => emitProgram(ir, {
|
|
7873
8018
|
embedSource: options["embed-source"] === true,
|
|
7874
8019
|
components: (all ? COMPONENTS : reachableComponents(names)).map((c) => c.symbol),
|
|
7875
8020
|
core: all ? void 0 : namedCore(names),
|
|
7876
8021
|
regexps,
|
|
7877
8022
|
hostPrimitives: primitives
|
|
7878
|
-
});
|
|
8023
|
+
}), (c) => `Emitted ${count(lineCount(c), "line")} of C code (${size(Buffer.byteLength(c))})`);
|
|
7879
8024
|
} catch (e) {
|
|
7880
8025
|
if (!(e instanceof CompileError)) throw e;
|
|
7881
8026
|
io.stderr(`${e.message}\n`);
|
|
@@ -7885,18 +8030,25 @@ async function build(entry, options, io) {
|
|
|
7885
8030
|
mkdirSync(options["emit-c"], { recursive: true });
|
|
7886
8031
|
writeFileSync(join(options["emit-c"], "program.c"), c);
|
|
7887
8032
|
if (bundled) writeFileSync(join(options["emit-c"], "bundle.js"), source);
|
|
8033
|
+
report.finish("📂", (time) => `Wrote ${displayPath(resolve(options["emit-c"], "program.c"))} in ${time}`);
|
|
7888
8034
|
return 0;
|
|
7889
8035
|
}
|
|
7890
8036
|
const output = options.output ?? basename(entry).replace(/\.[cm]?js$/, "");
|
|
8037
|
+
const tc = {
|
|
8038
|
+
cc,
|
|
8039
|
+
opt: options.debug ? ["-O0"] : ["-O2"],
|
|
8040
|
+
strip: !options.debug
|
|
8041
|
+
};
|
|
7891
8042
|
const dir = scratchDir();
|
|
7892
8043
|
try {
|
|
7893
8044
|
const file = join(dir, "program.c");
|
|
7894
8045
|
writeFileSync(file, c);
|
|
7895
|
-
|
|
7896
|
-
|
|
7897
|
-
|
|
7898
|
-
|
|
7899
|
-
},
|
|
8046
|
+
const built = [];
|
|
8047
|
+
const archives = await report.step("📚", "Preparing the runtime libraries", () => programArchives(tc, hosts, (name) => {
|
|
8048
|
+
built.push(`lib${name}`);
|
|
8049
|
+
report.relabel(`Building lib${name} (first build only: it's cached after)`);
|
|
8050
|
+
}), () => built.length ? `Built ${built.join(", ")} (cached for next time)` : "Runtime libraries ready (cached ⚡)");
|
|
8051
|
+
await report.step("🔨", `Compiling with ${describeCompiler(tc.cc)}`, () => compileProgram(tc, file, output, archives), () => `Compiled the binary (${tc.opt.join(" ")}, ${tc.strip ? "stripped" : "with debug info"})`);
|
|
7900
8052
|
} catch (e) {
|
|
7901
8053
|
io.stderr(`visaljs: C compilation failed: ${e.message}\n`);
|
|
7902
8054
|
return 1;
|
|
@@ -7906,6 +8058,8 @@ async function build(entry, options, io) {
|
|
|
7906
8058
|
force: true
|
|
7907
8059
|
});
|
|
7908
8060
|
}
|
|
8061
|
+
const bytes = statSync(output).size;
|
|
8062
|
+
report.finish("🎉", (time) => `Built ${displayPath(resolve(output))} in ${time}: ${size(bytes)}, ${sizeQuip(bytes)}`);
|
|
7909
8063
|
return 0;
|
|
7910
8064
|
}
|
|
7911
8065
|
/** How diagnostics and #line name an original file: relative to the working directory when
|
|
@@ -7917,9 +8071,14 @@ function displayPath(source) {
|
|
|
7917
8071
|
}
|
|
7918
8072
|
//#endregion
|
|
7919
8073
|
//#region cli/src/cli.ts
|
|
8074
|
+
const tty = process.stderr.isTTY === true;
|
|
7920
8075
|
process.exitCode = await main(process.argv.slice(2), {
|
|
7921
8076
|
stdout: (text) => process.stdout.write(text),
|
|
7922
|
-
stderr: (text) => process.stderr.write(text)
|
|
8077
|
+
stderr: (text) => process.stderr.write(text),
|
|
8078
|
+
report: {
|
|
8079
|
+
live: tty,
|
|
8080
|
+
color: tty && !process.env.NO_COLOR && process.env.TERM !== "dumb"
|
|
8081
|
+
}
|
|
7923
8082
|
});
|
|
7924
8083
|
//#endregion
|
|
7925
8084
|
export {};
|