构建系统
SuperJ 在 superj 二进制中内置了一个受 Cargo 启发的构建工具。对于单文件,你仍然可以手动调用 superj compile … && clang …(见 快速开始),但对于多于一个源文件或带依赖的任何项目,请使用构建系统:它掌管项目布局、依赖解析、链接,以及一个可复现的 lockfile,所以你永远不需要自己写一行 clang。
构建工具就是 superj 本身 — 没有独立的工具链要装,也没有别的东西要跑。它在进程内用 SuperJ 编译你的项目,并用你系统的 C 编译器链接。superj --help 列出每条命令。
本页是操作指南;superj --help 列出每个子命令与标志。
第一个项目
superj new hello
cd hello
superj run
superj new 生成脚手架:
hello/
Build.sj # 清单(见下文)
src/hello/Main.sj # package hello; public class Main { … main … }
.gitignore # 忽略 target/
superj run 构建到 target/debug/hello 并执行它。superj build 只构建;superj build --release 使用 release profile。
清单 — Build.sj
清单是项目根目录下的普通 SuperJ 源码,通过解析(绝不执行)一个 public class Build 中的已知 static final 字段来读取:
public class Build {
static final String name = "hello";
static final String version = "0.1.0";
static final String spec = "1.0"; // 目标语言规格版本
static final String entry = "hello.Main"; // 全限定主类
// 依赖(全部可选;见"依赖")
static final String[] dependencies = { "util", "../util" }; // 路径
static final String[] gitDependencies = { "json", "http://…/sj-json", "v1.0.0" }; // git
// release profile 旋钮
static final String releaseSimd = "avx2";
static final boolean releaseBoundsCheck = false;
static final int releaseOpt = 3;
}
源码位于 src/ 下,遵循通常的 package x.y; ⇔ 路径规则(src/hello/Main.sj ⇒ package hello;)。未知的清单字段被忽略,所以较新的清单仍能被较旧的工具链读取。
命令
| 命令 | 作用 |
|---|---|
superj new <name> | 搭建新项目。 |
superj build [--release] [--enterprise] | 编译 + 链接到 target/{debug,release}/<name>。 |
superj run [--release] [-- <args>] | 构建,然后运行二进制(-- 之后的参数被转发)。 |
superj check | 对整个项目做类型检查 — 无代码生成或链接。 |
superj test [-p <member>] | 把包的库编译进 test/ 下每个 // UNIT 套件并运行(见 编写与运行测试)。 |
superj add <name> <path> | 向 Build.sj 添加路径依赖。(--git <url> --rev <rev> 用于 git 依赖。) |
superj clean | 删除 target/。 |
superj --help | 列出每个编译器与构建系统命令。 |
依赖
SuperJ 做全程序编译:依赖的源码被编译进你的程序(没有按包的二进制 ABI — 这就是依赖以源码形式发布的原因)。两种依赖,都解析进一次合并编译:
- 路径 —
dependencies = { "util", "../util" }:另一个本地项目(有自己的Build.sj+src/),路径相对于本包。解析是递归的,带菱形去重;依赖循环与缺失依赖都是明确的错误。 - Git —
gitDependencies = { name, url, rev }:在固定提交或标签处拉取到内容寻址缓存($SUPERJ_CACHE_DIR,否则~/.superj/cache),然后完全像路径依赖一样处理。热的缓存可离线构建。
每个解析后的依赖 — 其路径或 git url+commit,以及一个内容哈希 — 都记录在 superj.lock 中,你提交它以获得可复现的构建。
(包注册中心 — 以及 superj publish — 刻意不在范围内:它是一个独立项目。请使用路径与 git 依赖。)
工作区
在一个仓库下将多个包分组。在根 Build.sj 中声明成员(它没有自己的 name/entry — 只有成员列表):
public class Build {
static final String[] workspaceMembers = { "app", "libs/util" };
}
从工作区根目录执行 superj build/check/test 作用于所有成员(或用 -p <member> 指定一个);从根目录执行 superj run 需要 -p <member>(或单成员工作区)。从成员目录内部执行任何命令时,它只作用于该成员,完全像一个独立项目。成员之间作为普通路径依赖相互依赖,而一个库成员(无 entry)只做类型检查而非链接(在全程序编译下它没有可运行产物 — 它以源码形式链接进其消费者)。
Profile
dev(默认)构建时开启数组越界检查并使用可移植 SIMD。--release 应用清单的 release* 字段:releaseSimd → --simd,releaseBoundsCheck = false → --no-bounds-check,releaseOpt → 链接优化级别(clang -O<n>,钳制在 0–3)。编译器在链接时始终向 clang 传入 -O3(除非 --debug);releaseOpt 保留给将来的细粒度控制。
Enterprise(全程序 LTO)
--enterprise 启用全程序优化:编译器把 SDK 作为外部发出,然后 llvm-link 预构建的 sdk.ll 与你的 IR,再 clang -O3 合并后的模块 — 跨 SDK 边界的跨编译单元内联与去虚化。最佳运行时性能,链接时间较慢。
superj build --enterprise # dev + LTO
superj build --release --enterprise # release profile + LTO(最大性能)
构建工具会从 $SJ_HOME/sdk/build/ 中的内容自动检测已安装的 SDK 变体:libsuperj_sdk.a → community,sdk.ll → enterprise,两者皆无 → vip(--sdk-source)。显式的 --enterprise 覆盖自动检测。你通常不需要操心 — superj build/run 会为你安装的变体发出正确的链接行。
构建钩子(代码生成)
设置 buildScript = "tools/Gen.sj" 为一个独立的 SuperJ 程序(带 main,位于 src/ 之外)。每次构建前它被编译并运行,传入一个生成源码目录的路径;它写入那里的每个 .sj 都被纳入构建。钩子按其源码哈希缓存。(这是构建代码运行的唯一位置 — 清单本身从不被执行。)
Feature flag
有条件地编译可选源码目录:
static final String[] features = { "json", "features/json", "tls", "features/tls" };
static final String[] defaultFeatures = { "json" };
启用的 feature 的目录加入构建。用 --features a,b、--no-default-features 或 --all-features 选择。(粒度是整个源码文件,不是文件内的 #[cfg]。)
SDK 链接
标准库是一个隐式的、由工具链锁定的依赖。构建工具为你选择 SDK 模式 — 默认是 community 归档,或 --enterprise 做全程序 LTO — 并发出正确的、针对 OS 的链接行。你永远不需要写明 clang 或运行时对象。