豫构原生构建设计
此为议案,尚未施行。新字段、命令与内部协议,皆拟定之制,非今已有之能。详辞与全例见汉语本。所先者本地构建之正与工具链之分,不议远程取包、版本求解与性能优化。
今已行最小 C 注册、编译归档与预构建核心之链接,实制见包内原生源码。下文为全议,不谓所列皆已行。
一、纲
编译器司语法、类型、优化与目标代码;豫构司运行时、原生依赖、归档、终链接及测试。先分二者之职,再迁 Makefile 诸组合目标,不徒译其 shell 命令入配置。
包即库,不别立二层;包根取包文件所在,身份以主、名二字段存之。语言导入唯自身与直依,独书包名则取包根总集。配置唯解其文,不行其代码。
C 绑定有二义:其一,源文与头文件何以成对象、归档及程序;其二,豫言值与 C 值何以相通、久存与释放。前者豫构司之,后者编译器、运行时 SDK 与桥接代码共守之。链接能成,不足以证 ABI 无误。
二、今状
根 Makefile 司初举、后举、比较、测试与发布;运行时 Makefile 司调试、优化、静态、动态、旧桥接与 WASI。编译器于终链接之前尚召 make,按工作目录寻运行时;豫构亦须移至编译器目录。此皆当改为明授工具与产物。
原生配方共用对象目录,不分调试、优化与动态之别;又以 notdir 去目录,异处同名之源可相撞。头文件与源码总系一归档目标,未逐翻译单元记其真依赖。Homebrew 与本机目录复硬书于参数,不宜为众项目之约。
今豫言值为一百二十八位;外调亦以 i128 传参返值。故 C调用 非任意 C 签名之通道。数据库现以 sqlite3、psql 子进程行之,尚非两库之原生绑定。
三、三图不可混
语言导入边限模块可见;原生编译边定头文件与必要宏;链接边传归档及系统库。应用直依数据库绑定,绑定复需 SQLite,则终链接必具 SQLite,而语言层不得因传递依赖而任意导入他包。
豫构由包配置与工具链择目标,得动作图:豫言编译、C 编译、归档、终链接、测试与打包。编译器仍知自身模块阶段,不由豫构再造一套类型或优化之义。
四、包中字段
拟增程序、测试、原生三可选字段,皆属本包,不另成包/库身份。旧入口字段仍定默认程序;总集与程序入口别。程序之典以名称映包内路径段;测试之典明入口、种类、退出码、超时与所比基准。种类分运行、编译成功、编译失败;基准之更新须明命,不自动改之。
拟设 构建目标 包名 目标名,不改旧 构建 包名 [模块段…],免目标与模块争名。测试 包名 [测试名] 唯行明列之项。首版语言依赖仍包级一份;测试所依有异,则别立测试包。
原生之典明 C 标准、源文件、私有与公开包含目录、宏、系统依赖、绑定表。源文件必列全相对路径与扩展名,不遍收所有 C 文。私有路径皆从包根,不受绝对路径、越界与子包穿越。系统路径惟由工具链探测得之,不以路径代包身份。
首版每包一原生组成。需相反参数或独立 ABI 者可分包,不先造多层原生目标制。首版只受 C 与工具链既有 LLVM IR;C++ 之运行库、异常与名字修饰另议。
原生可有依赖包字段,唯列已声明之直依短名,择其原生接口,不自动导入语言模块。纯原生包可无总集,供原生依赖;语言独名导入仍报缺总集。
以实际模块所属包为种,再沿显式原生依赖得闭包;纯原生包不漏,徒被发现者不滥链。首版以包为原生粒度,不许诺逐 C 符号裁剪。未知字段、重名目标、类型不合皆报之。
五、编译协议
拟增只生成产物之模式,不召 make,不编 C 运行时,不终链接,不运行程序。所受为包上下文、入口、构建机与目标、编译配置、工具身份、输出目录、绑定映射。
所返为实际模块闭包、有序对象或位码及主入口清单、初始化之序、运行时 ABI、外调符号与参数数及源码位置、各产物之状态。协议有版本,独立于配置 parser,不析人用日志,不猜缓存文件名。败则不发布成功清单;链接次序不得擅自排序。
先以整次豫言编译为复合动作,仍用旧模块调度;后乃议细粒度协议。首版复合动作独占构建额度,以 -j 限其内进程;C 动作于余时同额调度,不使每一目标各启满核之众程。
六、原生构建
工具链
静态工具链描述明编译器、clang、归档器、LLVM 工具、目标三元组、sysroot/SDK、运行时与其 ABI、支持能力。工具实径一次解析而定。普通包不可换编译器或运行时。
运行时为工具链所具组件,复用原生动作引擎,无须众语言包各列之,亦不成运行时与标准库互依之环。构建机上所行工具,与目标代码所用编译器及库分之;交叉编译不得运行目标探测程序,亦不以 uname 猜目标。
LLVM 与 LTO 之组合须实编、归档、链接小例而验,不以名同或版本大号同为足。失败则告何器不合,不暗换工具或关闭 LTO。先验 Linux、macOS;WASI 保留可移植核心与替代接口之明表,后迁同一引擎,不借宿主动态库。Windows 与通用交叉编译暂不承诺。
源文与头文件
一 C 文件一编译动作。对象名保留目录与源扩展名,不取 basename。编译时以 -MD、-MF 得预处理器所知依赖,记用户与系统头文件,解析转义与续行;不以正则遍搜 include 冒充编译器。所据见 Clang 参数。
初编无依赖记录必编;生成头文件先有生成边,后编消费者。旧记录尚不能察高优先级目录新添同名头文件,故亦须记用户包含目录清单与次序;系统头由 SDK 内容身份或重扫依赖保障。
归档只收当前对象,以临时文件成而原子替换;删源不得留旧成员。目标架构、调试/优化、静态/动态、PIC、LTO 各别输出域。运行时惟造所需变体,不复逢链接即造全部。
系统依赖与传播
系统依赖由明定查询工具探之,探测为可缓存动作,不于解析配置时行。记录工具、环境、目标、版本、元数据与最终文件。缺者报错,不自动装库与联网。
静态查询具其私依,动态查询不滥加静态依赖。查询参数按引号与转义解析,不执行 shell,亦不徒以空格分之。目标搜索域与宿主隔离。
私有包含、宏与编译参数止于本包;公开头与必要宏传原生消费者;链接需求沿终目标所需闭包传之。优化与警告不因所依包而强施于消费者。可参考 CMake 使用需求 之分,而不尽取其属性系统;外部源码随包构建之例见 Dune foreign stubs。
首版先用结构化参数,不开任意命令。其后若设原始参数,不许夺输出、目标、sysroot 与依赖文件之控制;且视为执行代码之信任,不称其天然安全。
链接
自编译器所授有序对象起,沿依赖加归档与运行时。静态库使用者在提供者之前,不乱序去重。跨归档环首版报之,不将 GNU group 参数强施 macOS。
运行时 ABI 惟一。同名语言包异主可共存,不代表 C 全局符号亦可同。绑定导出冲突、不兼容 ABI,皆先报之;第三方冲突须改名、明隔或拒同链,不以顺序侥幸胜出。
开发运行库位置与发布布局分之。发布用目标平台之相对布局,并记所携动态库;不默认载入开发机绝对 rpath。链接静态归档,亦非全程序尽静态。
七、绑定之约
首版守豫言值 ABI。普通 C 函数如 sqlite3_open 须经桥接转换参数、调用及返值,不可径以其名充 C调用。包以普通豫言模块出有类型之接口,桥接藏于其内。自动读 C 头、可变参数、结构体按值传递与回调生成,皆后议。
运行时 SDK 只公开稳定值转换、句柄、内存保护及错误接口,不令绑定尽含内部头。ABI 身份兼记值表示、调用约定、目标布局、GC 之约,非语言版本一串而已。
绑定表明逻辑名与参数数;编译器据当前包校验并报源码位置。物理符号以前缀别包,前缀由主、包与 ABI 的结构化身份派生;豫构生成 C 声明头与名称映射,二边共用。运行时旧豫言_符号由工具链提供表管之,不一次尽改。原始直连唯备明确兼容/不安全模式。
自动原型能验签名与参数数,不能证 GC、所有权无误。链接后符号检查亦不能代 C 编译器之 ABI 检验。
安全之约如次:
- 整数须验范围;字符串明 UTF-8、长度与 NUL,任意二进制用字节串。
- 跨分配或阻塞调用,首选复制到原生内存,不久存可能移动之豫言堆裸指针。
- 外部对象用显式句柄与关闭状态,明创建、释放之主,防再关闭;暂不倚自动析构。
- 临时 GC 根与释放 API 未备,则不开放长期持值、回调及线程内分配。现全局根登记不等于安全作用域根,不永久登记 C 栈址。
- 原生工作线程不触豫言堆、不召豫言闭包,结果经原生缓冲送回调度线程。
- 阻塞函数不能仅冠异步之名;须明启动、完成、取消与清理协议。
- 普通失败返结果;跨非局部异常边界须有资源清理之约;C++ 异常不得越界。
先验纯值桥接,再验资源型绑定;现数据库 CLI 不随此轮改作原生。
八、动作与缓存
动作记种类、依赖、工具实径、参数向量、环境白名单、工作目录、输入、输出、动态依赖及可缓存性。首批种类固定为豫言编译、C 编译、归档、链接、测试、比较、复制、打包,无任意 shell 字段。
键记协议版本、配置、工具、ABI、构建机与目标、PIC/LTO/优化、参数次序、环境与输入内容摘要。时间戳可助速,不独任正确性。删头、换系统库、换工具链、改符号映射与删 C 源,皆须正确失效;普通豫言源码之变不必重编无关 C 对象。
同动作加锁,单输出惟一生产者,临时结果成则原子发布。败不记成功。异配置别域,同配置多进程等待或复用。日志按构建与动作分,不共写一个根日志;可告“为何重建”。运行测试默认不缓存其结果。
显式输入与受控输出非安全沙箱。系统工具、第三方 C 仍须可信,不称跨机缓存可无条件共用。
九、Makefile 迁移
根 Makefile 的后级自举、字节比较、测试集合、类型检查、发布与清理,皆渐归豫构;旧命令先保留为薄入口,验等效后再减。可移植包由显式产物清单打包,不猜缓存路径。清理只及所拥有的产物,不删任意目录。
初举之链为:稳定编译器与稳定豫构俱备,按包造 yy2_bs;仍以稳定豫构造 yy3_bs、yy4_bs,比之,再造正式豫构。
发布俱载两种之源码,任缺其一则止,不退旧制。以文件名召编译器者,亦委豫构立其包境。平台运行时仍以专属 Makefile 备之;包内 C 归豫构。旧单种配方、引导器与兼容桥皆去。
首版薄 Make 入口串行启一次豫构;顶层多个并行目标保守串行化,免内外各计满额。后可接 GNU Make jobserver,不先允无控递归并行。
上游既有 Makefile,毋须尽重写。后设外部构建适配器,明源码树、构建机工具、独立构建目录、参数、环境、目标、归档及公开头。未知其内部依赖则以源码树全摘要保守失效;不支持出树者复制入暂存域。不于依赖原处运行 make install,不装系统、不自动下载。此晚于内建 C 引擎。
十、验收
异目录同名 C 源无碰撞;直接、间接、删除及新增遮蔽头正确失效;删源归档无遗物;多配置与多进程不相覆;链接依赖传而语言可见不滥;静态、动态及发布布局各正确;缺库、签名误、ABI 误、符号冲突皆可诊断。
复验 GC 压力、再关闭、错误清理与异步交付;异目录及含空格路径构建;新路径编译器不召 make;新旧运行时行为相合;三级自举得字节固定点。Linux、macOS 各验,WASI 别验;无修改不执行 C 编译、归档或链接,败不记成。冷启动不劣原 Python 基线即足,不别兴优化之役。
十一、先后
第一阶段定产物协议、工具链身份、原生动作与终链接,可先用旧配方所得归档验新接口。第二阶段以豫构造静态运行时,具头依赖、缓存与配置隔离,验自举固定点。第三阶段开放包原生配置、系统依赖与最小桥接 SDK。第四阶段迁多程序、测试、自举与打包目标,使 Makefile 为薄引导。第五阶段迁动态库分发、WASI 与外部 Make 适配。
宜先定四界:编译器不终链接;首版每包一原生组成;首版绑定守豫言值 ABI;Makefile 留受限初举兼容。字段之名尚可简,不同时改 FFI 语法、值布局与构建引擎。