xbintsc 0.3.46 → 0.3.49
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/AGENTS.md +95 -0
- package/README.md +25 -0
- package/README.zh-CN.md +23 -0
- package/dist/src/cli/hints.d.ts +54 -0
- package/dist/src/cli/hints.js +165 -0
- package/dist/src/cli/hints.js.map +1 -0
- package/dist/src/cli/main.js +73 -9
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +26 -0
- package/dist/src/codegen/generator/tables.js +64 -12
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/diagnostics/source-text.d.ts +22 -0
- package/dist/src/diagnostics/source-text.js +76 -0
- package/dist/src/diagnostics/source-text.js.map +1 -0
- package/dist/src/driver/bundler/graph.js +2 -1
- package/dist/src/driver/bundler/graph.js.map +1 -1
- package/dist/src/driver/compiler.js +3 -2
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/lexer/scanner/strings.js +16 -3
- package/dist/src/lexer/scanner/strings.js.map +1 -1
- package/dist/tests/cli/hints.test.d.ts +9 -0
- package/dist/tests/cli/hints.test.js +143 -0
- package/dist/tests/cli/hints.test.js.map +1 -0
- package/dist/tests/cli/main.test.js +6 -4
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/codegen/llvm.test.js +17 -2
- package/dist/tests/codegen/llvm.test.js.map +1 -1
- package/dist/tests/helpers.js +3 -2
- package/dist/tests/helpers.js.map +1 -1
- package/dist/tests/lexer/strings.test.js +14 -2
- package/dist/tests/lexer/strings.test.js.map +1 -1
- package/doc/DESIGN.md +117 -0
- package/doc/ai/README.md +63 -0
- package/doc/ai/build-recipe.md +137 -0
- package/doc/ai/cli.md +142 -0
- package/doc/ai/contributing.md +196 -0
- package/doc/ai/extensions.md +148 -0
- package/doc/ai/language-support.md +152 -0
- package/doc/ai/troubleshooting.md +163 -0
- package/doc/ai/zh-CN/README.md +56 -0
- package/doc/ai/zh-CN/build-recipe.md +132 -0
- package/doc/ai/zh-CN/cli.md +127 -0
- package/doc/ai/zh-CN/contributing.md +173 -0
- package/doc/ai/zh-CN/extensions.md +139 -0
- package/doc/ai/zh-CN/language-support.md +147 -0
- package/doc/ai/zh-CN/troubleshooting.md +150 -0
- package/doc/gui-scripts.md +350 -0
- package/doc/gui.md +646 -0
- package/doc/icon.md +265 -0
- package/doc/implemented.md +373 -0
- package/doc/node-implemented.md +588 -0
- package/doc/node-unimplemented.md +167 -0
- package/doc/post/announce.md +43 -0
- package/doc/requirements.md +145 -0
- package/doc/unimplemented.md +286 -0
- package/doc/xbintsc.config.schema.json +67 -0
- package/doc/zh-CN/DESIGN.md +104 -0
- package/doc/zh-CN/gui-scripts.md +329 -0
- package/doc/zh-CN/gui.md +588 -0
- package/doc/zh-CN/icon.md +241 -0
- package/doc/zh-CN/implemented.md +365 -0
- package/doc/zh-CN/node-implemented.md +533 -0
- package/doc/zh-CN/node-unimplemented.md +141 -0
- package/doc/zh-CN/plan-require-node-modules.md +284 -0
- package/doc/zh-CN/post/announce.md +47 -0
- package/doc/zh-CN/requirements.md +134 -0
- package/doc/zh-CN/unimplemented.md +247 -0
- package/llms.txt +45 -0
- package/package.json +4 -1
- package/runtime/ext_gui/gui.cpp +3 -1
- package/runtime/ext_gui/renderer.cpp +13 -11
- package/runtime/ext_gui/renderer_image.cpp +12 -8
- package/runtime/ext_gui/renderer_shaders.h +131 -4
- package/runtime/ext_gui/renderer_shaders_data.h +1809 -0
- package/runtime/ext_gui/renderer_text.cpp +12 -8
- package/runtime/ext_gui/shaders.hlsl +98 -0
- package/runtime/ext_gui/spirv/fill.frag +19 -0
- package/runtime/ext_gui/spirv/fill.vert +42 -0
- package/runtime/ext_gui/spirv/image.frag +16 -0
- package/runtime/ext_gui/spirv/quad.vert +30 -0
- package/runtime/ext_gui/spirv/text.frag +16 -0
- package/scripts/build-gui-shaders.mjs +204 -0
- package/scripts/build-gui.ts +35 -0
- package/scripts/check-file-length.ts +5 -1
- package/src/cli/hints.ts +194 -0
- package/src/cli/main.ts +82 -9
- package/src/codegen/generator/tables.ts +60 -14
- package/src/diagnostics/source-text.ts +78 -0
- package/src/driver/bundler/graph.ts +2 -1
- package/src/driver/compiler.ts +3 -2
- package/src/lexer/scanner/strings.ts +16 -3
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# xbintsc Node 扩展未实现功能
|
|
2
|
+
|
|
3
|
+
> 语言 / Language:[English](../node-unimplemented.md) | **简体中文**
|
|
4
|
+
|
|
5
|
+
本文档基于对 Node 扩展源码(`src/extensions/node/`)与 C 运行时(`runtime/ext_node/`)的逐文件核对整理,列出 **Node 扩展当前未实现 / 缺失** 的功能。
|
|
6
|
+
|
|
7
|
+
> 相关文档:
|
|
8
|
+
> - 核心语言能力见 [implemented.md](implemented.md) / [unimplemented.md](unimplemented.md)
|
|
9
|
+
> - Node 扩展已实现部分见 [node-implemented.md](node-implemented.md)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1. `fs` 模块未实现
|
|
14
|
+
|
|
15
|
+
`fs` 现在实现了**同步** API 表面(文件、目录、描述符、元数据、链接、`cp`、`glob`、`Dir`、`constants`),`fs/promises` 将每个同步函数包进已 settle 的 Promise(见 [node-implemented.md](node-implemented.md))。其余仍未实现,因为 xbintsc 的 `fs` **没有异步 I/O 调度器后端**(核心事件循环已提供定时器与套接字):
|
|
16
|
+
|
|
17
|
+
| 未实现 API | 类别 |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| `readFile` / `writeFile` / `appendFile` / `open` / `close` / `read` / `write` / `stat` / … | 回调式异步文件操作(`fs/promises` 提供 Promise 形式) |
|
|
20
|
+
| `createReadStream` / `createWriteStream` | 流式读写 |
|
|
21
|
+
| `watch` / `watchFile` | **真正的**文件监听:函数存在但返回对象从不触发(无 inotify/kqueue 后端) |
|
|
22
|
+
| `openAsBlob`、`statfs` 回调形式、`rmdir` 的 `maxRetries` / `retryDelay` | 需异步重试的杂项选项 |
|
|
23
|
+
| `glob` 的 `exclude` / `follow`、`cp` 的 `filter` | 选项回调 |
|
|
24
|
+
|
|
25
|
+
### 1.1 读取语义缺口
|
|
26
|
+
|
|
27
|
+
- `readFileSync` 支持编码选项(默认/`utf8`/`ascii`/`latin1`/`binary`/`hex`/`base64`/`base64url`),但默认**不返回 `Buffer`**:即使提供了 `Buffer`(以普通对象模拟),`readFileSync` 仍以字符串返回。要读取原始字节请显式指定。
|
|
28
|
+
- 未实现 `utf16le` / `ucs2` 解码(按文本返回字节)。
|
|
29
|
+
- `watch` / `watchFile` / `unwatchFile` 返回 API 形态的发射器对象,`.close()` / `.on()` 方法存在但**不会触发**事件。
|
|
30
|
+
- `Dir.read(cb)` / `Dir.close(cb)` 与 `FileHandle` 异步方法都会**同步**完成(回调/Promise 立即被调用)。
|
|
31
|
+
- `mkdtempSync` 总是向前缀追加 6 个随机字符(不要求以 `XXXXXX` 结尾)。
|
|
32
|
+
- `globSync` 支持 `*`、`?`、`[...]`、`**`,但不支持 `exclude` 回调与 `follow`;`**` 不跟随符号链接(与 Node 默认一致)。
|
|
33
|
+
- `cpSync` 的符号链接处理在常见场景下与 Node 一致,但不支持 `verbatimSymlinks`。
|
|
34
|
+
- 平台差异:Windows 上 `readlinkSync` 抛 `ENOSYS`,`chmodSync` / `lchmodSync` / `chownSync` / `lchownSync` / `fchmodSync` / `fchownSync` 为空操作;`statfsSync` 在 Windows/AIX/Sun 返回全零字段;`lutimesSync` 在 macOS/Windows 退化为 `utimesSync`。
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 2. 其它 Node 模块完全未实现
|
|
39
|
+
|
|
40
|
+
以下 Node 内置模块没有任何对应扩展 / 内置函数(`fs` / `fs/promises` / `path` / `os` / `process` / `buffer` / `stream` / `dgram` / `http` / `events` / `util` / `querystring` / `assert` / `test` 已实现,`crypto` / `url` / `child_process` / `zlib` / `stream/promises` / `worker_threads` 部分实现,见 [node-implemented.md](node-implemented.md)):
|
|
41
|
+
|
|
42
|
+
| 模块 | 说明 |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| `https` | TLS 版 HTTP |
|
|
45
|
+
| `readline` | 命令行读取 |
|
|
46
|
+
| `tls` / `cluster` / `vm` / `os`(部分)等 | 其余未列出的模块 |
|
|
47
|
+
|
|
48
|
+
> `crypto`(仅 `createHash`,现支持流式 API)、`url`(仅 `pathToFileURL` / `fileURLToPath`)、
|
|
49
|
+
> `child_process`(仅 `spawnSync`)、`zlib`(仅 `createGzip`)与
|
|
50
|
+
> `worker_threads`(仅 `Worker` / `isMainThread` / `workerData` / `parentPort`)目前为部分实现。
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 3. Node 全局对象 / 命名空间
|
|
55
|
+
|
|
56
|
+
| 未实现 | 说明 |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| `global` / `globalThis` | 无 |
|
|
59
|
+
| `__dirname` / `__filename` | 无 |
|
|
60
|
+
| `require` / `module` / `exports` | 仅限 `node_modules` 内的包(打包器会降级);用户代码需使用 ESM `import` |
|
|
61
|
+
| 未 `import` 的 `fs.readFileSync(...)` | ✗ 不支持。`fs` 不是全局对象;请先导入(`import fs from "fs"` 或 `import * as fs from "node:fs"`),之后 `fs.readFileSync(...)` 会下降为该模块的运行时符号。 |
|
|
62
|
+
|
|
63
|
+
Node 模块通过裸名称或 `node:` 前缀的 `import` 引入(`import { readFileSync } from "fs"`、`import path from "path"`、`import { platform } from "node:os"`)。具名与命名空间导入都会解析到扩展模块的运行时入口。
|
|
64
|
+
|
|
65
|
+
已支持的命名空间调用:`path.*`、`os.*`、`process.*`、`fs.*`、`fs/promises.*`(方法)、`child_process.*`、`crypto.*`、`url.*`(需先导入;`path`/`os`/`process` 也可使用全局名),以及基于分发器的 `Buffer.*`、`stream.*`、`net.*`、`dgram.*`、`http.*` 和 `process.platform` / `process.argv` 等属性。`Readable` / `Writable` / `Duplex` / `Transform` / `PassThrough` / `Buffer` 亦可用作全局构造函数。
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 4. 异步 / 事件循环模型
|
|
70
|
+
|
|
71
|
+
已实现部分:
|
|
72
|
+
|
|
73
|
+
- 核心事件循环 `runtime/xt_loop.c`(`select(2)` 反应堆),`net` / `dgram` / `http` 均构建于其上。
|
|
74
|
+
- `Promise`、`async` / `await` 已可用(见核心文档),`fs/promises` 返回 Promise。
|
|
75
|
+
- 定时器已作为全局函数提供:`setTimeout` / `clearTimeout` / `setInterval` / `clearInterval` 运行在核心反应堆上,并在仍有定时器时保持进程存活。
|
|
76
|
+
- 提供独立的 `EventEmitter`(`events` 模块,同时也是全局构造函数);流与套接字额外具备内部发射器方法(`on` / `addListener` / `once` / `off` / `removeListener` / `emit`)。
|
|
77
|
+
|
|
78
|
+
仍未实现:
|
|
79
|
+
|
|
80
|
+
- `setImmediate` / `queueMicrotask` 以及 Node 的 `Timeout` 对象(`setTimeout` 返回数字 id)。
|
|
81
|
+
- 真正的异步 I/O 调度:`fs/promises` 实质是同步操作的即时 settle 包装,不会在等待 I/O 时让出。
|
|
82
|
+
- 流与套接字的内置 `once` 等同 `on`(不具「触发一次后移除」语义);`events` 模块的 `EventEmitter` 实现了真正的 `once`。
|
|
83
|
+
- `net` 的 `connect` 为阻塞式;HTTP 响应要求 `Connection: close`,不做 keep-alive / 分块传输 / 流水线复用。
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 5. Bun 扩展未实现
|
|
88
|
+
|
|
89
|
+
设计文档中提到的 `Bun.file` 等 Bun API 仅作为扩展机制的设计示例,**尚未实现**:
|
|
90
|
+
|
|
91
|
+
- 无 `bun` 扩展目录、无 Bun 内置函数、无 Bun C 运行时。
|
|
92
|
+
- `src/extensions/` 下目前只有 `node` 一个平台扩展(外加核心扩展)。
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 6. 速查:Node 扩展未实现清单
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
已实现(fs):完整同步表面 —— readFileSync(含编码)、readTextFile、
|
|
100
|
+
writeFileSync、appendFileSync、existsSync、readdirSync(withFileTypes/recursive)、
|
|
101
|
+
mkdirSync、rmSync、unlinkSync、rmdirSync、renameSync、copyFileSync、cpSync、
|
|
102
|
+
realpathSync、statSync、lstatSync、statfsSync、accessSync、chmodSync、lchmodSync、
|
|
103
|
+
chownSync、lchownSync、truncateSync、utimesSync、lutimesSync、mkdtempSync、
|
|
104
|
+
linkSync、symlinkSync、readlinkSync、opendirSync(Dir)、globSync、watch、
|
|
105
|
+
watchFile、unwatchFile、constants、
|
|
106
|
+
openSync、closeSync、readSync、writeSync、readvSync、writevSync、fstatSync、
|
|
107
|
+
fsyncSync、fdatasyncSync、ftruncateSync、fchmodSync、fchownSync、futimesSync
|
|
108
|
+
已实现(fs/promises):readFile、writeFile、appendFile、mkdir、readdir、rm、unlink、
|
|
109
|
+
rmdir、rename、copyFile、cp、realpath、stat、lstat、statfs、access、
|
|
110
|
+
open(FileHandle)、chmod、lchmod、chown、lchown、truncate、utimes、
|
|
111
|
+
lutimes、link、symlink、readlink、mkdtemp、opendir、glob、watch、constants
|
|
112
|
+
已实现(其它模块):path(join/resolve/normalize/dirname/basename/extname/isAbsolute/relative)、
|
|
113
|
+
os(platform/arch/type/release/endianness/homedir/tmpdir/hostname/totalmem/freemem/cpus)、
|
|
114
|
+
process(cwd/exit/uptime/hrtime/getuid/platform/arch/pid/ppid/argv/env/version/title)、
|
|
115
|
+
buffer(from/alloc/isBuffer/byteLength/concat/compare + 实例方法)、
|
|
116
|
+
stream(Readable/Writable/Duplex/Transform/PassThrough、pipe)、
|
|
117
|
+
net(createServer/connect/isIP + Server/Socket)、
|
|
118
|
+
dgram(createSocket + bind/send/close/address)、
|
|
119
|
+
http(createServer/request/get + req/res)、
|
|
120
|
+
events(EventEmitter:on/once/off/emit/listeners/listenerCount/eventNames)、
|
|
121
|
+
util(format/inspect/isDeepStrictEqual/inherits/promisify + isX)、
|
|
122
|
+
querystring(parse/stringify/escape/unescape)、
|
|
123
|
+
crypto(createHash + 流式 API,SHA-1/SHA-256)、
|
|
124
|
+
url(仅 pathToFileURL/fileURLToPath)、
|
|
125
|
+
child_process(仅 spawnSync)、
|
|
126
|
+
zlib(仅 createGzip)、stream/promises(仅 pipeline)、
|
|
127
|
+
worker_threads(仅 Worker/isMainThread/workerData/parentPort)
|
|
128
|
+
|
|
129
|
+
未实现(fs):回调式异步文件操作(readFile/writeFile/appendFile/open/read/write/close)、
|
|
130
|
+
createReadStream/createWriteStream、真正的文件监听(watch/watchFile 对象从不触发)、
|
|
131
|
+
glob exclude/follow、cp filter、utf16le 解码、readFileSync 返回 Buffer
|
|
132
|
+
|
|
133
|
+
未实现(其它模块):https、readline、tls、cluster、vm
|
|
134
|
+
|
|
135
|
+
未实现(全局/命名空间):global/globalThis、__dirname、__filename、
|
|
136
|
+
require/module/exports、fs.readFileSync(...) 命名空间调用
|
|
137
|
+
|
|
138
|
+
未实现(异步):setImmediate / queueMicrotask、真正异步 I/O 调度、keep-alive
|
|
139
|
+
|
|
140
|
+
未实现(平台):Bun 扩展(仅设计示例)
|
|
141
|
+
```
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# 计划:仅 `node_modules` 支持 `require`(CommonJS 兼容)
|
|
2
|
+
|
|
3
|
+
> 状态:**部分实施**(0.4.x)。实际落地采用**静态降级**而非本文 4.4/4.5 的运行时工厂/注册表方案:
|
|
4
|
+
> 每个 CJS 模块在打包阶段注入 `const <prefix>$cjs_module = { exports: {} }` 与 `<prefix>$cjs_exports`,
|
|
5
|
+
> `require("pkg")` 直接改写为依赖的 `$cjs_module.exports`,导出名通过末尾的快照常量暴露给 ESM。
|
|
6
|
+
> 已实现:解析器修复(P0)、`node_modules` 内 CJS→CJS / CJS→ESM、ESM→CJS 的 default/named、
|
|
7
|
+
> 外部/内置 `require` 提升为 ESM import、非字面量 `require` 诊断。
|
|
8
|
+
> 未实现(相对本文的差异):循环 `require` 的部分导出时序、懒执行、`require.resolve` / `require.cache`、
|
|
9
|
+
> `__dirname` / `__filename`、`Object.defineProperty` getter 形式。详见下文 4.x 与 `unimplemented.md`。
|
|
10
|
+
>
|
|
11
|
+
> 目标版本:0.4.x
|
|
12
|
+
> 关联文档:[unimplemented.md](./unimplemented.md) 第 4 节(模块系统)、[implemented.md](./implemented.md) 第 3.4 节
|
|
13
|
+
|
|
14
|
+
## 1. 背景与现状
|
|
15
|
+
|
|
16
|
+
xbintsc 目前在 **driver 层做源码级打包**(`src/driver/bundler/`):
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
entry.ts
|
|
20
|
+
│ loadGraph 解析并绑定 entry + 所有可达模块(import/export)
|
|
21
|
+
▼
|
|
22
|
+
ModuleRecord[] (依赖在前,入口在最后)
|
|
23
|
+
│ merge.ts 按模块前缀重命名顶层符号、改写引用、合并成单个 SourceFile
|
|
24
|
+
▼
|
|
25
|
+
merged SourceFile → bind → codegen(LLVM IR) → clang → 原生二进制
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
现状约束:
|
|
29
|
+
|
|
30
|
+
- 只支持 ESM:`import` / `export`,`node_modules` 里**只支持 ESM 包**。
|
|
31
|
+
- `require()` 在 codegen 被显式拒绝:
|
|
32
|
+
- `src/codegen/generator/calls/invocation.ts`(`require("x")` 调用)
|
|
33
|
+
- `src/codegen/generator/expressions/primary.ts`(裸 `require` 引用)
|
|
34
|
+
- `src/codegen/generator/context.ts` → `reportRequireUse()`
|
|
35
|
+
- `src/driver/bundler/resolve.ts` 已经能把裸 specifier 解析到 `node_modules`(含 `exports` / `module` / `main` / scoped / 子路径),但解析出的 CJS 源码在 codegen 阶段会因为 `require` 而失败。
|
|
36
|
+
- `resolve.ts` 的 `EXPORT_CONDITIONS` 固定为 `["import", "module", "default", "node", "require"]`:对 `require` 站点会错误地优先选中 ESM 入口。
|
|
37
|
+
- `isModule` 判定(`compiler.ts`)只看 entry 是否有 `import`/`export`/`export =`;只有 `require` 的入口不会触发打包。
|
|
38
|
+
- **解析器缺口**:`module` 是上下文关键字,但 `parseStatement` 对 `ModuleKeyword` 直接进入 `parseModuleDeclaration`,导致 CJS 常见的 `module.exports = ...`(语句首)解析失败:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
module.exports.b = 2;
|
|
42
|
+
^^^^^^ error TS2003 / TS4002
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
实测确认(`src/parser/statements.ts:198`、`:231`)。参数位置使用 `module` 是合法的,`module.exports` 作为表达式也合法,问题只在语句首的判定。
|
|
46
|
+
|
|
47
|
+
## 2. 目标与非目标
|
|
48
|
+
|
|
49
|
+
### 目标
|
|
50
|
+
|
|
51
|
+
1. `require("pkg")` 在 specifier **解析后位于任意 `node_modules/` 目录内**时可用(入口用户代码、以及 CJS 包内部都算)。
|
|
52
|
+
2. 支持 CJS 包内部的相对 `require("./util")`(这类路径同样落在 `node_modules` 内)。
|
|
53
|
+
3. 支持 `module.exports`、`exports.x = ...`、懒加载、按模块缓存、循环 `require` 的部分导出语义。
|
|
54
|
+
4. ESM `import` 一个 CJS 包时提供 default / named / namespace 互操作。
|
|
55
|
+
5. 二进制保持**自包含**:编译期完成打包与解析,运行期不访问 `node_modules`、不做文件系统解析。
|
|
56
|
+
|
|
57
|
+
### 非目标(本期不做,或另立计划)
|
|
58
|
+
|
|
59
|
+
- 用户自有代码里的**相对** `require("./local")` 仍报错(仅 `node_modules` 开放)。
|
|
60
|
+
- 任意动态 `require(expr)`:只静态解析**字符串字面量**;动态调用保留运行期兜底(可能在已知依赖里查找,否则抛错)。
|
|
61
|
+
- 完整 Node CJS 宿主语义:`global` / `process` / `Buffer` / `module.parent` / `require.resolve` / `require.cache` / `__dirname` 的完整行为。
|
|
62
|
+
- `require` ESM 包(Node ≥22 的 `require(esm)`)——列为可选 Phase。
|
|
63
|
+
- 循环 ESM、live binding(沿用现状)。
|
|
64
|
+
- JSON 模块(`require("./x.json")`)——列为可选 Phase。
|
|
65
|
+
|
|
66
|
+
## 3. 核心规则(一句话)
|
|
67
|
+
|
|
68
|
+
> **只有当 `require(spec)` 的目标路径位于某个 `node_modules/` 目录内时才允许;否则维持现有诊断(引导改用 `import`)。**
|
|
69
|
+
|
|
70
|
+
规则作用于 **解析后的目标位置**,而不是调用方文件位置。由此自然得到:
|
|
71
|
+
|
|
72
|
+
- 入口 `const _ = require("lodash")` ✅(lodash 在 node_modules)
|
|
73
|
+
- CJS 包内 `require("./util")` ✅(util 在 node_modules 包目录内)
|
|
74
|
+
- 用户 `require("./local")` ❌(不在 node_modules)
|
|
75
|
+
- 用户 `require("/abs/path")` ❌
|
|
76
|
+
|
|
77
|
+
## 4. 设计
|
|
78
|
+
|
|
79
|
+
### 4.1 P0 解析器修复:语句首的 `module` / `namespace`
|
|
80
|
+
|
|
81
|
+
`src/parser/statements.ts` 的两处 `case TokenKind.ModuleKeyword:` / `NamespaceKeyword`:
|
|
82
|
+
|
|
83
|
+
只有当下一个 token 是**标识符类**(`Foo`)或字符串字面量(`declare module "x"`)时才进入 `parseModuleDeclaration`;否则 `break` 落到 `parseExpressionStatement`,让 `module` 作为普通标识符参与表达式。
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
case TokenKind.ModuleKeyword:
|
|
87
|
+
case TokenKind.NamespaceKeyword: {
|
|
88
|
+
const next = this.lookAhead(1);
|
|
89
|
+
if (this.isIdentifierLike(next) || next.kind === TokenKind.StringLiteral) {
|
|
90
|
+
return this.parseModuleDeclaration(...);
|
|
91
|
+
}
|
|
92
|
+
break; // module.exports / module["x"] / module(...)
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
> 该修复独立、低风险,应最先合入;否则任何 CJS 包都无法解析。
|
|
97
|
+
|
|
98
|
+
### 4.2 模块格式判定(ESM vs CJS)
|
|
99
|
+
|
|
100
|
+
给 `ModuleRecord` 增加 `format: "esm" | "cjs"`、`isNodeModules: boolean`、`cjsId?: string`。
|
|
101
|
+
|
|
102
|
+
判定顺序(`loadGraph` 内,`src/driver/bundler/graph.ts`):
|
|
103
|
+
|
|
104
|
+
1. 扩展名:`.mjs` / `.mts` → ESM;`.cjs` / `.cts` → CJS。
|
|
105
|
+
2. 向上查找最近的 `package.json` 的 `"type"`:
|
|
106
|
+
- `"module"` → ESM;`"commonjs"` / 缺省 → CJS。
|
|
107
|
+
3. 兜底嗅探:顶层出现 `module.exports` / `exports.` / `require(` 且没有 ESM `import`/`export` → CJS。
|
|
108
|
+
|
|
109
|
+
`node_modules` 内默认(无 `"type"`)即 CJS,这与 Node 一致。
|
|
110
|
+
|
|
111
|
+
### 4.3 require 解析与导出条件
|
|
112
|
+
|
|
113
|
+
`src/driver/bundler/resolve.ts`:
|
|
114
|
+
|
|
115
|
+
- `classifyDependency` / `resolveNodePackage` 增加一个 `conditions`(`"import" | "require"`)参数。
|
|
116
|
+
- 从 CJS 模块或 `require(...)` 站点解析时,条件优先级改为
|
|
117
|
+
`["require", "node", "default"]`,并优先 `main` 而非打包器字段 `module`。
|
|
118
|
+
- 从 ESM `import(...)` 解析时保持现状 `["import", "module", "default", "node", "require"]`。
|
|
119
|
+
- 解析结果附带 `isNodeModules`(路径包含 `/node_modules/` 段)。
|
|
120
|
+
|
|
121
|
+
### 4.4 CJS 打包模型:工厂 + 注册 + 懒加载
|
|
122
|
+
|
|
123
|
+
对每个 CJS 模块,在 merge 阶段把它的顶层语句包进一个工厂函数:
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
function <prefix>require(spec) {
|
|
127
|
+
const target = <prefix>deps[spec];
|
|
128
|
+
if (target === undefined) throw new Error("Cannot find module '" + spec + "'");
|
|
129
|
+
return __xbintsc_cjs_load(target);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
__xbintsc_cjs_register(
|
|
133
|
+
"<prefix>",
|
|
134
|
+
function (module, exports, require, __filename, __dirname) {
|
|
135
|
+
/* 原 CJS 顶层语句(已按 <prefix> 重命名) */
|
|
136
|
+
/* ………………………………………………………………………… */
|
|
137
|
+
},
|
|
138
|
+
<prefix>require,
|
|
139
|
+
);
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
要点:
|
|
143
|
+
|
|
144
|
+
- 工厂参数 `module` / `exports` / `require` / `__filename` / `__dirname` 在 codegen 的**重新 bind**(`GeneratorContext` 构造时 `bind(sourceFile)`)时会解析为函数参数,不会触发 `CannotFindName`。
|
|
145
|
+
- 顶层 `var` / `function` 进入工厂后天然变成模块私有,符合 CJS 语义。
|
|
146
|
+
- `<prefix>deps` 是 `{ "./util": "m7", "dep": "m3" }` 形状的对象字面量,**编译期**由 `classifyDependency` 解析生成(因此运行期不需要 fs)。
|
|
147
|
+
- 依赖收集:遍历每个模块 AST 里 `require("字面量")` 的 `CallExpression`(callee 为无 symbol 的标识符 `require`),解析并写入 deps 表。
|
|
148
|
+
- 工厂注册语句统一放在合并产物的**最前面**(注册只是赋值,顺序无关),然后才是各模块语句;这样任何 `__xbintsc_cjs_load` 调用前注册都已就绪。
|
|
149
|
+
- 循环 require:`load` 在**执行工厂前**就把 `module.exports` 放进缓存,保证部分导出可见,与 Node 一致。
|
|
150
|
+
|
|
151
|
+
### 4.5 预置加载器(运行时垫片)
|
|
152
|
+
|
|
153
|
+
优先方案:把加载器作为**一段 JS/TS 源码字符串**注入合并产物(解析成 AST 语句),命名统一加 `__xbintsc_cjs_*` 前缀,避开用户符号。
|
|
154
|
+
|
|
155
|
+
```js
|
|
156
|
+
var __xbintsc_cjs_cache = {};
|
|
157
|
+
var __xbintsc_cjs_factories = {};
|
|
158
|
+
var __xbintsc_cjs_requires = {};
|
|
159
|
+
|
|
160
|
+
function __xbintsc_cjs_register(id, factory, requireFn) {
|
|
161
|
+
__xbintsc_cjs_factories[id] = factory;
|
|
162
|
+
__xbintsc_cjs_requires[id] = requireFn;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function __xbintsc_cjs_load(id) {
|
|
166
|
+
var cached = __xbintsc_cjs_cache[id];
|
|
167
|
+
if (cached !== undefined) return cached;
|
|
168
|
+
var factory = __xbintsc_cjs_factories[id];
|
|
169
|
+
if (factory === undefined) throw new Error("Cannot find module '" + id + "'");
|
|
170
|
+
var module = { exports: {} };
|
|
171
|
+
__xbintsc_cjs_cache[id] = module.exports; // 先入缓存,支持循环 require
|
|
172
|
+
factory(module, module.exports, __xbintsc_cjs_requires[id], id, "");
|
|
173
|
+
__xbintsc_cjs_cache[id] = module.exports; // 处理 module.exports 重赋值
|
|
174
|
+
return module.exports;
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
- 该子集(对象字面量、元素访问、函数值调用、`throw new Error`、闭包)现有 codegen 全部支持,**无需改 C runtime**。
|
|
179
|
+
- 备选方案:在 C runtime 增加 `xt_cjs_register` / `xt_cjs_load`(性能更好、产物更干净),但需要把工厂作为 `xt_value` 回调用通用 ABI 调用,工作量和风险更高。**建议先走源码垫片**,后续需要再下沉到 C。
|
|
180
|
+
|
|
181
|
+
### 4.6 ESM ↔ CJS 互操作
|
|
182
|
+
|
|
183
|
+
在 `merge.ts` Phase 2/3 增加分支:
|
|
184
|
+
|
|
185
|
+
- 依赖为 CJS 时,不再扫描 `dependency.exports`(为空),而是:
|
|
186
|
+
1. 在依赖顺序位置插入 `const <prefix>exports = __xbintsc_cjs_load("<prefix>");`(只对**被 ESM import 引用**的 CJS 模块做急切加载;纯 `require` 的仍懒加载)。
|
|
187
|
+
2. `import d from "cjs"` → 引用改写为 `<prefix>exports`;若 `exports.__esModule` 为真则取 `exports.default`(运行期判定,可用一个小 helper)。
|
|
188
|
+
3. `import { a, b as c } from "cjs"` → 生成 `const <prefix>a = <prefix>exports.a;` 之类的快照常量,引用改名到该常量(与现有 ESM 快照语义一致)。
|
|
189
|
+
4. `import * as ns from "cjs"` → 命名空间直接用 `<prefix>exports`。
|
|
190
|
+
- `export { x } from "cjs"` / `export * from "cjs"` 同理。
|
|
191
|
+
- CJS 内部 `require("path")` 等**扩展模块**:deps 表把 specifier 映射到扩展模块的命名空间对象(复用 `extension registry` 的 exports),Phase 3 可选支持。
|
|
192
|
+
|
|
193
|
+
### 4.7 `require` 调用点重写
|
|
194
|
+
|
|
195
|
+
在 merge 的最终阶段,遍历所有模块(含入口):
|
|
196
|
+
|
|
197
|
+
- `require("literal")` 且目标是 **已打包的 CJS 模块** → 重写为 `__xbintsc_cjs_load("<id>")`(入口/ESM 中直接内联;CJS 工厂内因为工厂参数已叫 `require`,可直接保留调用,由传入的 `<prefix>require` 承担查找)。
|
|
198
|
+
- 目标是 **ESM 模块** → 本期报“require 不支持 ESM”,或列为可选 Phase 支持。
|
|
199
|
+
- 目标是 **扩展模块 / node 内置模块** → 可选:转成命名空间对象。
|
|
200
|
+
- **不在 node_modules** → 保留(更清晰的)诊断:`CommonJS \`require()\` is only supported for packages under node_modules; use an ESM import`。
|
|
201
|
+
- 非字面量参数 → 保留裸 `require` 调用;运行时由 `<prefix>require` 兜底抛错(并在有把握时给出诊断)。
|
|
202
|
+
|
|
203
|
+
codegen 里现有的裸 `require` 诊断(`reportRequireUse`)保留为**兜底**:只有未被 bundler 重写的 `require` 才会走到那里。
|
|
204
|
+
|
|
205
|
+
### 4.8 触发打包的条件
|
|
206
|
+
|
|
207
|
+
`compiler.ts` 的 `isModule` 判定扩展为:
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
hasModuleSyntax(entry) || entryAstContainsRequire(entry)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
即入口只有 `require("pkg")`(没有 import/export)也要触发 `bundleModules`,否则 `require` 不会进入打包流程。`loadGraph` 同步支持从 `require` 字面量递归加载依赖。
|
|
214
|
+
|
|
215
|
+
## 5. 分阶段实施
|
|
216
|
+
|
|
217
|
+
| 阶段 | 内容 | 交付/验证 |
|
|
218
|
+
| --- | --- | --- |
|
|
219
|
+
| **P0** | 解析器修复:语句首 `module`/`namespace` 判定(4.1) | 新增 parser 单测:`module.exports.x = 1;`、`namespace.foo()` 可解析 |
|
|
220
|
+
| **P1** | 格式判定 + `isNodeModules` + `conditions` 参数(4.2/4.3) | `bundler-resolve.test.ts` 覆盖 cjs/esm 判定、`require` 条件、`type` 字段 |
|
|
221
|
+
| **P2** | loadGraph 收集 `require` 依赖 + `isModule` 扩展(4.8) | 入口仅 `require("pkg")` 也能打包;相对 require 仍报错 |
|
|
222
|
+
| **P3** | CJS 工厂包装 + 注册表 + 预置加载器(4.4/4.5) | e2e:`module.exports`、`exports.x`、嵌套相对 require、循环 require、重赋值 |
|
|
223
|
+
| **P4** | require 调用点重写 + 诊断(4.7) | 单测:node_modules 内重写为 `__load`,外部保持报错 |
|
|
224
|
+
| **P5** | ESM→CJS 互操作 default/named/namespace(4.6) | 差分测试:与 Node 输出一致(default 函数、命名导出、`__esModule`) |
|
|
225
|
+
| **P6** | 扩展模块 / 可选:`require(esm)`、JSON 模块 | 按需 |
|
|
226
|
+
| **P7** | 文档更新:implemented / unimplemented 第 4 节、README | 文档评审 |
|
|
227
|
+
|
|
228
|
+
每个阶段都应保持现有测试全绿;P0 单独可合入。
|
|
229
|
+
|
|
230
|
+
## 6. 涉及文件
|
|
231
|
+
|
|
232
|
+
| 文件 | 改动 |
|
|
233
|
+
| --- | --- |
|
|
234
|
+
| `src/parser/statements.ts` | 4.1 语句首 `module`/`namespace` 判定 |
|
|
235
|
+
| `src/driver/bundler/types.ts` | `ModuleRecord.format` / `isNodeModules` / `cjsId`;`BundleResult` 可能需要暴露统计 |
|
|
236
|
+
| `src/driver/bundler/resolve.ts` | `conditions` 参数、`isNodeModules`、require 条件优先级 |
|
|
237
|
+
| `src/driver/bundler/graph.ts` | 格式判定、收集 `require` 依赖、递归加载 |
|
|
238
|
+
| `src/driver/bundler/merge.ts` | CJS 工厂包装、注册/加载语句注入、require 重写、互操作、依赖表 |
|
|
239
|
+
| `src/driver/bundler/cjs.ts`(新增) | 加载器源码字符串 + 工厂/依赖表/DepsMap 的 AST 构造 helper |
|
|
240
|
+
| `src/driver/compiler.ts` | `isModule` 扩展(4.8),`cacheText` 覆盖新注入代码 |
|
|
241
|
+
| `src/codegen/generator/context.ts` | `reportRequireUse` 文案更新(node_modules 限定) |
|
|
242
|
+
| `tests/driver/bundler-resolve.test.ts` | 解析/格式/条件单测 |
|
|
243
|
+
| `tests/driver/bundler-cjs.test.ts`(新增) | 工厂包装与重写单测 |
|
|
244
|
+
| `tests/e2e/npm-modules.test.ts` | CJS 包 e2e + 差分 |
|
|
245
|
+
| `tests/parser/*` | P0 回归 |
|
|
246
|
+
| `doc/implemented.md`、`doc/unimplemented.md`、`doc/zh-CN/*` | 第 4 节状态更新 |
|
|
247
|
+
|
|
248
|
+
## 7. 测试计划
|
|
249
|
+
|
|
250
|
+
- **单测**
|
|
251
|
+
- 解析器:`module.exports`、`module.exports.fn = ...`、`exports.x`、`module["exports"]`。
|
|
252
|
+
- 解析:`.cjs`/`.mjs`、`package.json#type`、无 type 默认 CJS、`require` 条件选 `main`、`import` 条件选 `exports.import`。
|
|
253
|
+
- 打包:工厂签名、deps 表内容、require 重写、非 node_modules 相对 require 报错。
|
|
254
|
+
- **e2e(与 Node 差分)**
|
|
255
|
+
- `module.exports = function`(默认函数导出)。
|
|
256
|
+
- `exports.a` / `exports.b` 命名导出。
|
|
257
|
+
- CJS 内部相对 `require("./util")`、二级依赖。
|
|
258
|
+
- 循环 require 的部分导出。
|
|
259
|
+
- `module.exports` 在工厂执行中重赋值。
|
|
260
|
+
- ESM `import make, { add } from "cjs-pkg"` 差分。
|
|
261
|
+
- `import * as ns`。
|
|
262
|
+
- 入口仅 `require("pkg")` 无 import。
|
|
263
|
+
- 回归:`import` ESM 包仍正常;用户 `require("./local")` 仍报错。
|
|
264
|
+
- **CI**:沿用 `describeE2E` 的 clang 存在性跳过逻辑;不得引入 Node 运行期依赖。
|
|
265
|
+
|
|
266
|
+
## 8. 风险与取舍
|
|
267
|
+
|
|
268
|
+
| 风险 | 说明 / 缓解 |
|
|
269
|
+
| --- | --- |
|
|
270
|
+
| 动态 `require(expr)` | 无法静态打包;运行时兜底抛错,文档明确说明 |
|
|
271
|
+
| 依赖宿主对象(`process`、`Buffer`、`global`) | 不在本期范围;这类包会报 `Cannot find name`,文档列出边界 |
|
|
272
|
+
| 命名导入的静态分析 | 不做 `cjs-module-lexer`;用运行期属性 + 快照常量,语义与现有 ESM 快照一致 |
|
|
273
|
+
| `resolve.ts` 现有 `module` 字段偏好 | 仅对 `import` 保持;`require` 站点改走 `main`/`require` 条件,避免破坏现有 ESM 行为 |
|
|
274
|
+
| 注入代码与缓存 | 注入的加载器/工厂必须计入 `cacheText` 与缓存指纹,否则增量编译会复用旧产物 |
|
|
275
|
+
| 注入源码的符号冲突 | 统一 `__xbintsc_cjs_*` 前缀;用户顶层符号已全部带 `mN_` 前缀,冲突概率极低 |
|
|
276
|
+
| 与 `export =` 的关系 | 现有 `ExportAssignment`(`export =`)先不动;CJS 判定以此为准另行处理,避免回归 |
|
|
277
|
+
|
|
278
|
+
## 9. 验收标准
|
|
279
|
+
|
|
280
|
+
1. 入口 `const fn = require("some-cjs-pkg"); fn();` 能编译运行且与 Node 输出一致。
|
|
281
|
+
2. CJS 包内部相对 `require`、嵌套依赖、循环依赖均与 Node 行为一致。
|
|
282
|
+
3. ESM `import` CJS 包的 default / named / namespace 与 Node 一致。
|
|
283
|
+
4. 用户自有代码的相对 `require("./x")` 仍给出清晰错误。
|
|
284
|
+
5. 现有 ESM `node_modules` 与所有测试保持通过。
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# 兄弟们 typescript可以编译成原生二进制了
|
|
2
|
+
|
|
3
|
+
## 缘由
|
|
4
|
+
2019年的时候研究过一段时间编译器,当时写了一个迷你的JS编译器,只支持function和number类型,但别小看就这两个类型,差不多花费了两周时间去实现,所以编译器是需要大量的时间和精力去做这个事情,当时就感叹可能我这辈子都不太可能能撸出一个相对完整的编译器了。
|
|
5
|
+
|
|
6
|
+
2026年9月的某个下午,群里有人聊到了前端/js/ts永远都只能在鄙视链的最底端,因为它不能像C++和rust一样生成二进制文件。
|
|
7
|
+
|
|
8
|
+
## 触发
|
|
9
|
+
这件事就像触发了我的底层代码一样,脑子里疯狂出现了一个念头,然后回过头去翻了翻当年写的编辑器文章,不!不对!如果用AI的话,现在可能真的有可能可以实现,然后我开始列出了我的思路,写了第一个文档 DESIGN.md,接下来打开pi,开撸!
|
|
10
|
+
|
|
11
|
+
很快不到一个小时,就出现第一个原型,我看完何其丑陋和简单,甚至不如我当年写的demo,于是我觉得自己重写把整个框架重新写一遍,若干天,总算是达到了我想要的水平,然后我开启了第二次尝试,这次我继续启动pi,这次发现虽然AI在架构上表现一般,但是架构完善后,它完成功能的速度不是一般的快,于是我开始了急速的迭代过程。
|
|
12
|
+
|
|
13
|
+
## 核心进展
|
|
14
|
+
|
|
15
|
+
* 绝大部分的js/ts语法以及node.js的主流方法(可选包)
|
|
16
|
+
* 支持了C++和rust扩展,用户可以自由扩展
|
|
17
|
+
* 实现了自举,即自己编译了自己
|
|
18
|
+
* 实现无GNU依赖运行
|
|
19
|
+
|
|
20
|
+
当然未实现的在这里:
|
|
21
|
+
[unimplemented.md](https://github.com/zy445566/xbintsc/blob/main/doc/unimplemented.md)
|
|
22
|
+
[node-unimplemented.md](https://github.com/zy445566/xbintsc/blob/main/doc/node-unimplemented.md)
|
|
23
|
+
|
|
24
|
+
当然对于实现二进制编译最大的优势主要是两点:
|
|
25
|
+
* 包体积巨小,编译后的二进制文件低至200KB (--ext node 300KB),且可以直接运行,不再需要再捆绑几十MB的node.js运行时了
|
|
26
|
+
* 冷启动速度巨快,本机实测比node.js原生大概快了120-200倍左右
|
|
27
|
+
|
|
28
|
+
## 使用方法
|
|
29
|
+
在 [releases页面](https://github.com/zy445566/xbintsc/releases) 下载zst压缩包解压后,运行即可,下面以windows作为案例
|
|
30
|
+
```
|
|
31
|
+
# 编译
|
|
32
|
+
.\xbintsc-win32-x64\bin\xbintsc.exe build .\hello.ts --out .\build
|
|
33
|
+
# 运行
|
|
34
|
+
.\build\hello.exe
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
# 附录
|
|
38
|
+
Github 地址: [https://github.com/zy445566/xbintsc](https://github.com/zy445566/xbintsc)
|
|
39
|
+
兄弟们 有兴趣可以一起研究 `;)`
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# xbintsc 使用前置要求
|
|
2
|
+
|
|
3
|
+
本文档说明一台机器要让 `xbintsc` **编译并链接出原生二进制**需要具备什么。
|
|
4
|
+
xbintsc 不再自带编译器,因此每个平台都需要安装一个兼容 clang 的工具链。
|
|
5
|
+
`xbintsc emit`(仅输出 LLVM IR 文本)在任何平台都没有要求。
|
|
6
|
+
|
|
7
|
+
## 最低版本
|
|
8
|
+
|
|
9
|
+
| 要求 | 最低版本 | 说明 |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| clang / LLVM | **16** | 生成的 IR 使用不透明指针,而 LLVM 16 起不透明指针是唯一的指针表示。 |
|
|
12
|
+
| Node.js | **22** | 仅用于从源码运行或构建编译器;发布二进制是独立的,不需要 Node。 |
|
|
13
|
+
|
|
14
|
+
clang 15 及更旧版本会在任何程序的第一个字符串字面量处失败:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
build/app.ll:382:37: error: '@.str.0' defined with type '[6 x i8]*' but expected 'i8*'
|
|
18
|
+
%r0 = call i64 @xt_string_new(i8* @.str.0, i64 5)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
有类型指针在 LLVM 16 中被移除([发布说明](https://github.com/llvm/llvm-project/blob/release/15.x/llvm/docs/ReleaseNotes.rst#changes-to-the-llvm-ir)),
|
|
22
|
+
所以请先确认 `clang --version`;系统 clang 较旧时用 `xbintsc_CLANG` 指向新版。
|
|
23
|
+
`xbintsc doctor` 会报告解析到的工具链及其版本。把 IR 拿到别处编译来绕过该限制
|
|
24
|
+
是不受支持的。
|
|
25
|
+
|
|
26
|
+
## 总览
|
|
27
|
+
|
|
28
|
+
| 平台 | 前置要求 | 由谁提供 |
|
|
29
|
+
| --- | --- | --- |
|
|
30
|
+
| Linux | `clang` + `lld`(glibc 发行版) | 用户(包管理器) |
|
|
31
|
+
| macOS | **Xcode Command Line Tools** | 用户一次性安装 |
|
|
32
|
+
| Windows | **LLVM** + **Visual Studio C++ 生成工具**(MSVC ABI) | 用户一次性安装 |
|
|
33
|
+
| 任意 | `xbintsc emit` | 无需任何东西 |
|
|
34
|
+
|
|
35
|
+
Windows 发布包面向 **MSVC ABI**:clang 使用 Windows SDK
|
|
36
|
+
以及 MSVC 的 C/C++ 运行库与头文件。
|
|
37
|
+
|
|
38
|
+
## 预编译发布包(推荐)
|
|
39
|
+
|
|
40
|
+
从 [GitHub Releases](https://github.com/zy445566/xbintsc/releases/latest) 下载对应
|
|
41
|
+
平台的归档(每个都附带 `.sha256`):
|
|
42
|
+
|
|
43
|
+
| 平台 | 归档 |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| Windows x64 / arm64 | `xbintsc-<version>-win32-x64.tar.zst` / `xbintsc-<version>-win32-arm64.tar.zst` |
|
|
46
|
+
| Linux x64 / arm64 | `xbintsc-<version>-linux-x64.tar.zst` / `xbintsc-<version>-linux-arm64.tar.zst` |
|
|
47
|
+
| macOS x64 / arm64(Apple Silicon) | `xbintsc-<version>-darwin-x64.tar.zst` / `xbintsc-<version>-darwin-arm64.tar.zst` |
|
|
48
|
+
|
|
49
|
+
(仅当构建机没有 `zstd` 时才会回退为 `.tar.gz`。)
|
|
50
|
+
|
|
51
|
+
`<version>` 是发布版本号(例如 `0.3.14`),会写入归档文件名,避免不同版本的下载文件相互覆盖。
|
|
52
|
+
|
|
53
|
+
每个归档解压出的结构一致:
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
xbintsc-<os>-<arch>/
|
|
57
|
+
bin/xbintsc[.exe] 编译器
|
|
58
|
+
runtime/ C 运行期 + 预编译 runtime/lib/<os>-<arch>/
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Windows 归档不含预编译 `runtime/lib/`;驱动会用用户的 clang 按需编译 C 运行期。
|
|
62
|
+
|
|
63
|
+
解压后把 `bin/` 加入 `PATH`(或直接调用 `bin/xbintsc`),再按下面的平台说明安装
|
|
64
|
+
工具链。建议先校验下载内容:
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
sha256sum -c xbintsc-<version>-linux-x64.tar.zst.sha256 # macOS 用:shasum -a 256 -c
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## macOS —— 安装 Xcode Command Line Tools
|
|
71
|
+
|
|
72
|
+
xbintsc 使用 Command Line Tools 自带的链接器与系统 SDK(`libSystem` 等)来产出
|
|
73
|
+
可执行文件。请先安装一次:
|
|
74
|
+
|
|
75
|
+
```sh
|
|
76
|
+
xcode-select --install
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
验证:
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
xcode-select -p # 打印当前开发者目录
|
|
83
|
+
xcrun --show-sdk-path # 打印 SDK 路径
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Linux —— 安装 clang 与 lld
|
|
87
|
+
|
|
88
|
+
常规的 glibc 发行版加上 `clang`、`lld` 即可:
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
sudo apt-get install -y clang lld # Debian / Ubuntu
|
|
92
|
+
sudo dnf install -y clang lld # Fedora / RHEL
|
|
93
|
+
sudo pacman -S --needed clang lld # Arch
|
|
94
|
+
sudo zypper install -y clang lld # openSUSE
|
|
95
|
+
sudo apk add clang lld # Alpine
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
用 `clang --version` 验证。如果 xbintsc 用错了链接器,可用 `xbintsc_CLANG` 指定
|
|
99
|
+
clang,并通过 `xbintsc_LINKER_ARGS` 追加参数(例如 `-fuse-ld=lld`)。
|
|
100
|
+
|
|
101
|
+
## Windows —— 安装 LLVM 与 MSVC C++ 生成工具
|
|
102
|
+
|
|
103
|
+
用 `winget` 安装 LLVM 和 Visual Studio C++ 生成工具:
|
|
104
|
+
|
|
105
|
+
```powershell
|
|
106
|
+
winget install -e --id LLVM.LLVM
|
|
107
|
+
winget install -e --id Microsoft.VisualStudio.2022.BuildTools `
|
|
108
|
+
--override "--quiet --wait --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
clang 需要 MSVC/SDK 的环境变量(`INCLUDE`、`LIB`、`PATH`)才能找到 C 标准库头文件、
|
|
112
|
+
导入库和链接器。请在 **x64 Native Tools Command Prompt for VS 2022**(Windows on
|
|
113
|
+
ARM 上用 **ARM64 Native Tools** 提示符)中运行 `xbintsc`,或自行导入环境:
|
|
114
|
+
|
|
115
|
+
```powershell
|
|
116
|
+
& "$env:ProgramFiles\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" x64
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
用 `clang --version` 验证。(也可用 chocolatey:`choco install llvm`。)
|
|
120
|
+
|
|
121
|
+
## 从源码构建(仅贡献者需要)
|
|
122
|
+
|
|
123
|
+
- Node.js ≥ 22 仅在运行/构建编译器本身时需要;发布的独立二进制**不需要** Node。
|
|
124
|
+
- 重建运行期(`npm run runtime`)需要 C 编译器。
|
|
125
|
+
- `npm run package-release` 会在 `dist/release/` 下组装出各平台发布归档
|
|
126
|
+
(`xbintsc-<os>-<arch>.tar.{gz,zst}` + `.sha256`)。
|
|
127
|
+
|
|
128
|
+
## 检查环境
|
|
129
|
+
|
|
130
|
+
```sh
|
|
131
|
+
xbintsc doctor
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`doctor` 会报告解析到的工具链(env / 系统)、其版本与位置,以及运行期库目录。
|