工具链开发规范
本规范约束 Norm 官方 Java 工具链的代码组织、依赖方向和执行后端。技术栈选择见实现策略决议,语言行为由语言规范定义。
仓库边界
cli/ 命令行产品
compiler/ Java 编译器、执行运行时、CLI 与 Language Server
extensions/ 编辑器扩展
norm/stdlib/ 使用 Norm 编写的标准库
norm/tests/ 可执行的 Norm 验收程序compiler 是唯一产品与 JPMS 模块;根 Gradle Kotlin DSL 另含仅用于构建的 gradle/build-logic。领域 package 负责分层,跨层数据只使用下层拥有的强类型模型;架构测试禁止逆向依赖。
领域边界
| Package | 职责 |
|---|---|
source / syntax | 源码身份、位置与语法模型 |
abi / pattern | 中立运行契约与模式覆盖算法 |
semantic / builtin | 语义模型与内建契约的语义投影 |
frontend / bound | 分析、已解析语义与 Core 构建 |
core / core.store | 内容寻址定义、制品与存储 |
project | 项目发现、模块解析与输入快照 |
packages | Norm 仓库访问、版本选择、离线缓存与完整性 |
lsp | 协议转换与独占工作区会话 |
application | 应用编译产物、执行准备与资源所有权 |
build | 应用构建用例、Native 计划、工具链与交付 |
language / workspace | 快照查询、项目分析调度与版本发布 |
jvm | Java 类型投影、绑定规划与注解处理 |
execution / platform | 执行与宿主能力契约 |
truffle / polyglot | Core 执行实现与 Polyglot 接入 |
diagnostic / value | 诊断与其余跨阶段值 |
DependencyArchitectureTest 是依赖方向的可执行真相源,包含包级无环约束。bound 只由前端消费;Core 不依赖前端语义或内建目录;project、jvm 和 truffle 不反向依赖应用编排。完整阶段和生命周期见编译器架构。
CLI package
dev.w0fv1.norm.cli JVM 入口
dev.w0fv1.norm.cli.controller 命令解析、路由与执行
dev.w0fv1.norm.cli.component 版本组件
dev.w0fv1.norm.cli.value CLI 公共数据
dev.w0fv1.norm.cli.utils 无状态文本工具CLI 中只有 Main 可以终止 JVM;原生应用的进程入口为 runtime.NativeApplicationMain。Controller 通过返回退出码报告结果,component 不读取命令行参数。
应用构建入口为 ApplicationBuilder;CLI 只解析参数、组装服务和展示进度与结果。build 不依赖 CLI、Workspace 或 Truffle 节点,其他下层不反向依赖 build。约束与验证复用 DependencyArchitectureTest 和 build 测试。
LSP 启动入口为 LanguageServerLauncher,CLI 移交工作区,会话负责关闭并返回退出码。真实协议验收见 verify-lsp.mjs。
编辑器能力以 language.LanguageService 和不可变语义快照为唯一语义实现。补全排序、期望类型、泛型替换、调用参数和导入候选均在 dev.w0fv1.norm.language 中计算;Workspace 管理项目分析与诊断发布,Language Server 只负责 LSP 类型转换,编辑器扩展只负责生命周期和编辑器接入。
命名与可见性
dev.w0fv1.norm已经提供语言上下文,类型名不增加Norm前缀;使用Compiler、Analyzer、Lowerer、ApplicationRunner等领域名称。- 只有真实的跨进程或扩展契约才形成对外 API。Lexer、Parser、Analyzer、Truffle 节点和运行时表示保持模块内部可见。
value只存放跨阶段不可变数据;具有明确领域的数据保留在对应领域,例如 Syntax AST 属于syntax。utils只接受静态、无状态、可独立复用的工具。生命周期、I/O 和可变状态不进入utils。- 同一概念只保留一个模型,禁止并行维护旧 AST、临时 IR 或第二条执行链。
编译与执行阶段
阶段、产物及所有权以编译器架构和其中的代码入口为准。应用入口使用 ApplicationCompiler、CompiledApplication 与 ApplicationRunner;编辑器入口使用 Workspace。调用方关闭自己持有的应用产物,每次运行拥有独立的运行资源。
ResolvedCall 是已解析调用的单一结果,语言服务和绑定阶段复用它;尚未完成的类型输入使用 TypeSyntaxParser,不能另写类型语法。内建签名只声明在 stdlib-abi.json,语义对象与 Core 校验契约均从它派生。
值表示、复制、相等性与哈希归属 RuntimeValues;调用准备归属 RuntimeInvocation,Unicode 文本操作归属 RuntimeText。Truffle 节点不能捕获单次运行的外部资源。系统资源契约见系统运行时架构。
ABI 代码生成
BuiltinAbiGenerator 属于构建期 gradle/build-logic,不进入产品模块。根 Gradle Kotlin DSL 声明构建工具与 compiler 的依赖和生命周期;内建签名的单一声明源仍为 stdlib-abi.json。Java 格式与 qualityCheck 由同一构建入口执行。
版本元数据由同一构建逻辑的 BuildMetadataGenerator 生成;版本、GraalVM 与输出目录由 compiler Gradle 配置 接入。
工具链依赖清单由 ToolchainArtifactCatalogGenerator 生成并校验;Gradle 构建任务从实际解析图和 JAR 文件提供输入。
gradle/build-logic 调用同一组装器与清单生成器,不进入产品运行时;compiler 仍以 Java 25 编译。
生成字节与指纹的约束见 BuiltinAbiGeneratorTest。干净构建、输入变化、重建及发行隔离由 verify-codegen.mjs 验证;不在用户工作区内修改 schema。
测试
- 先写或迁移失败测试,再修改实现。
- 单元测试与被测 package 对齐,内部组件不因测试而扩大可见性。
- 语法或执行变更必须覆盖诊断测试,以及
norm/tests中的单文件和模块程序。 - Java 修改先运行相关 package 测试;提交前执行格式检查。发布前才运行完整发布验证。
- 后端变更必须通过 Polyglot 注册入口和 CLI 的真实
.norm文件执行测试。
验收测试的领域、目录、命名、发现入口与运行命令统一由 norm/tests/README.md 定义。
文档同步
语言行为修改语言规范;实现结构修改本规范;技术栈决策修改实现策略决议。其他页面只链接这些入口,不复制规则。
项目加载门面为 project.ProjectLoader;源码装载、依赖图、归档缓存及绑定准备分别由 ProjectModuleSources、ProjectDependencyGraph、ArchivedModuleLoader 和 JarBindingPreparer 承担。捕获输入、失败重试和边界契约见 ProjectLoadingBoundaryTest。
本地验收与测量入口
Windows 本地 CLI 与扩展使用根 Gradle 构建的 gradlew.bat :compiler:installRuntimeDist;正式发行资产由 :compiler:packageDistribution 写入根 build/distributions/。安装树路径与编译器摘要由 resolve-toolchain.ps1 核验;默认版本与 -PnormVersion 覆盖由根 Gradle printNormVersion 输出,正式发布只取 SemVer tag。CLI、扩展与 GUI 验收应记录实际产物身份,不只比较版本号。
网络受限构建仍须提供已校验的 reachability metadata。归档来源与校验值只在 ReachabilityMetadataArchive 声明;其他构建工具、插件与 Java 依赖仍须预先供应。官方发行版源码包路线另按源码构建方案单独验收。
自包含安装树位于 build/compiler/norm-runtime;lib 由 RuntimeModuleAssembler 组装,bin 由 RuntimeLauncherGenerator 生成,随包 JDK 由 RuntimeImageGenerator 经 jlink 生成。自有 APT/RPM 包使用应用私有依赖,不能视为使用发行版系统库的官方源码包。
网络受限环境的定向测试可使用隔离的本地 Java 依赖仓库;夹具装载见 MavenTestRepository。缓存命中不代表干净网络或发行版系统依赖验收。
compare-compiler.ps1 用相同 Java、参数和源码交替运行两份完整依赖目录,保存编译、增量分析、执行耗时、主线程分配和观测峰值工作集。指标定义与预热次数见 CompilerBenchmark。主线程分配不是进程总分配,峰值工作集包含启动与预热;样例结果不能直接推广为工具链整体性能提升。