Slang 编译器子系统依赖图:从 CMake 构建文件推导架构边界与风险面
2026/9/18 15:25:17 网站建设 项目流程

Slang 编译器子系统依赖图:从 CMake 构建文件推导架构边界与风险面

【免费下载链接】slangMaking it easier to work with shaders项目地址: https://gitcode.com/GitHub_Trending/sl/slang

本文讲解 Slang 着色器编译器(Shader Language 编译器)如何以子系统级(subsystem-level)依赖图来描述source/目录树中各大逻辑单元之间的链接关系,并给出从构建系统事实推导架构约束(不变量)的完整方法论。通过阅读本文,你将掌握:如何从CMakeLists.txtLINK_WITH_*条款还原出 Slang 的整体分层结构、如何预判"改动某个子系统会波及其他哪些子系统"的风险面、以及source/core/source/compiler-core/source/slang/等核心模块之间必须遵守的方向性规则。

文档的定位:一份由构建系统驱动的架构图

docs/generated/design/architecture/dependency-graph.md是 Slang 生成式设计文档体系(docs/generated/design/)中专门记录静态链接依赖的一页。它的源头是一份提示词规范(docs/generated/design/_meta/prompts/architecture-dependency-graph.md),该规范把生成这一页的全部约束固化下来,包括:

  • 图的粒度必须是子系统级(每个节点对应一个source/<subsystem>/目录),而不是文件级;
  • 图中每一条边都必须由source/下各CMakeLists.txt中的target_link_libraries(...)(等价于slang_add_targetLINK_WITH_PRIVATE/LINK_WITH_PUBLIC条款)支撑,禁止凭空捏造边
  • 必须列出若干"值得注意的不变量(Notable invariants)",且每条不变量都要给出构建文件或头文件依据;
  • 必须说明是否存在依赖环及已知的不规则现象;
  • 图与文合计体积限制在 16 KB 以内,以保证文档可维护。

依赖图的目标读者非常明确:想要预判一次改动影响面的人——当你修改某个子系统时,这张图能告诉你哪些子系统处于风险之中。文件级清单则见 docs/generated/design/architecture/module-map.md。

图例与粒度:为什么是"子系统级"而不是"文件级"

依赖图刻意选择了粗粒度。以source/slang/为例,该目录下仅 IR 相关文件就有上百个(module-map.md 记录 IR passes 一族包含 163 个.cpp文件,加上头文件共 326 个),若把每条文件级依赖都画出来,图会立刻失去可读性。子系统级图的收益在于:读者只要记住十几个节点,就能对全项目的构建依赖结构建立整体认知

图中的边语义有两种:"编译期针对某子系统的公共头文件编译(compiles against the public headers of)"与"运行时依赖(depends on at runtime)",其事实来源统一是slang_add_target(... LINK_WITH_PRIVATE ...)/LINK_WITH_PUBLIC条款。绘制该图时必须遵守项目统一的 mermaid 语法约定:节点 ID 使用 camelCase、节点名不含空格、不显式着色。

依赖图全景:Slang 内部子系统链接结构

以下是 dependency-graph.md 呈现的完整子系统级依赖图(外部依赖已从图中剔除,聚焦内部结构):

图中所有实线边都可以在对应CMakeLists.txt中找到逐字依据,汇总如下:

依据文件条款
compiler-core → coresource/compiler-core/CMakeLists.txtLINK_WITH_PRIVATE core(实际为LINK_WITH_PRIVATE core fast_floatfast_float为外部库)
core-module → corecore-module → slang(生成目标)source/slang-core-module/CMakeLists.txtLINK_WITH_PRIVATE core slang-capability-defs slang-fiddle-output
glsl-module → coresource/slang-glsl-module/CMakeLists.txtLINK_WITH_PRIVATE core
slang → {core, prelude, compiler-core, core-module}source/slang/内生成目标source/slang/CMakeLists.txtslang_add_target(slang ...)LINK_WITH_PRIVATE core prelude compiler-core slang-capability-defs slang-capability-lookup slang-fiddle-output slang-lookup-tables SPIRV-Headers::SPIRV-Headers libcmark-gfmprelude实际是私有包含依赖而非静态链接
slangc → coreslangc → slangsource/slangc/CMakeLists.txtLINK_WITH_PRIVATE core slang Threads::Threads(外加按条件附加的 glsl-module 与 core-module-cache 依赖)
slang-dispatcher → coresource/slang-dispatcher/CMakeLists.txtLINK_WITH_PRIVATE中包含core
slang-wasm → {slang, core, compiler-core}source/slang/内生成目标source/slang-wasm/CMakeLists.txtLINK_WITH_PRIVATE miniz lz4_static slang core compiler-core slang-capability-defs slang-capability-lookup slang-fiddle-output slang-lookup-tables

虚线边slang -.-> slang-record-replay并非由LINK_WITH_*条款支撑,而是源码列表直接并入:source/slang/CMakeLists.txt第 164-167 行把SLANG_RECORD_REPLAY_SYSTEM变量(指向source/slang-record-replay/及其proxy/子目录)传入EXTRA_SOURCE_DIRS,使记录/回放源码被直接编译进slang库,因此它不构成独立的链接目标。

三个没有普通链接边的子系统

对照 module-map.md 中的子系统清单,有三个子系统在上图中没有普通LINK_WITH_*,需要单独理解:

  • source/standard-modules/:它的 CMakeLists.txt 只做configure_file生成配置头、并add_subdirectory引入neuralexperimentalnumerics三个模块,本身不声明链接目标;模块产物以独立的.slang-module文件形式随编译器分发。
  • source/slang-record-replay/:没有自己的CMakeLists.txt,如上所述其源码通过SLANG_RECORD_REPLAY_SYSTEM变量并入slang目标(见 source/slang/CMakeLists.txt 第 164-167 行)。
  • source/slang-llvm/:同样没有自己的CMakeLists.txtslang-llvm库在源码树外构建(或由根 CMakeLists.txt 第 385-401 行通过SLANG_SLANG_LLVM_FLAVOR选项控制下载预编译产物),因此源码树内没有任何目标直接链接它。

图中刻意不展开的生成代码目标

source/slang/的构建还定义了四个生成代码目标,依赖图故意不把它们当作独立子系统展示:

  • slang-fiddle-output:FIDDLE 生成的 AST/IR 支持代码(由slang-fiddle工具驱动,见 source/slang/CMakeLists.txt 顶部的SLANG_FIDDLE_*逻辑);
  • slang-capability-defsslang-capability-lookup:由slang-capability-generator*.capdef生成的 capability 表(分别对应生成头文件库与生成源码库);
  • slang-lookup-tables:由slang-lookup-generator/slang-spirv-embed-generator生成的 SPIR-V 等查找表。

这四个目标被slangslang-wasm共同消费。同时它们也解释了图中slang-core-module → slang这条"生成目标"边:source/slang-core-module/链接了slang-capability-defsslang-fiddle-output,即它依赖的是source/slang/拥有的生成产物,而并不链接编译器库本体。

外部依赖:被排除在图的内部结构之外

外部依赖(minizlz4_staticThreads::Threadsunordered_densefast_floatSPIRV-HeadersSPIRV-Tools-optSPIRV-Tools-linkSPIRVglslang${CMAKE_DL_LIBS}等)从图中剔除,以保证图聚焦内部结构,但规范要求以"逐节点备注"的方式交代最重要的几项:

  • core(coreLib):私有链接minizlz4_staticThreads::Threads${CMAKE_DL_LIBS},公共链接unordered_dense::unordered_dense;当SLANG_ENABLE_MIMALLOC开启时,额外以PUBLIC方式链接mimalloc-static并传播PUBLIC SLANG_ENABLE_MIMALLOC=1编译宏,使所有下游看到一致的分配器选择——配置阶段若找不到mimalloc-static目标会直接FATAL_ERROR(见 source/core/CMakeLists.txt 第 16-25 行)。
  • compiler-core(compilerCore):除内部core链接外,私有链接fast_float(用于快速浮点解析),条款为LINK_WITH_PRIVATE core fast_float(见 source/compiler-core/CMakeLists.txt)。
  • slangslang-wasm:依赖SPIRV-Headers;wasm 目标额外使用miniz/lz4_static
  • slang-rt:链接minizlz4_staticThreadsunordered_dense${CMAKE_DL_LIBS}——注意其私有依赖列表中没有任何内部 Slang 库。不过slang-rt并非与编译器源码完全无关:它的 CMakeLists.txt 通过EXTRA_SOURCE_DIRS ${slang_SOURCE_DIR}/source/coresource/core/的源码以SLANG_RT_DYNAMIC_EXPORT宏重新编译进运行时,并通过INCLUDE_DIRECTORIES_PRIVATE ${slang_SOURCE_DIR}/source让这些源码能解析#include "core/slang-basic.h"这类直接路径包含。
  • slang-glslang:链接glslangSPIRVSPIRV-Tools-optSPIRV-Tools-link(见 source/slang-glslang/CMakeLists.txt)。
  • slang-lookup-tables:依赖SPIRV-Headers

值得注意的不变量:架构边界的硬约束

依赖图揭示的分层结构可以提炼为以下方向性规则,每条都有构建文件或头文件做依据。这些不变量正是"预测改动风险面"的核心工具。

  • source/core/不依赖项目内任何其他子系统。其 CMakeLists.txt 的slang_add_target(... LINK_WITH_PRIVATE miniz lz4_static Threads::Threads ${CMAKE_DL_LIBS})+LINK_WITH_PUBLIC unordered_dense::unordered_dense只列出外部库。它是整个项目的基座层,被其他每个子目录使用。
  • source/compiler-core/可以依赖source/core/,但绝不能依赖source/slang/其构建块只含LINK_WITH_PRIVATE core fast_float。这一条把"语言无关的编译器基础设施"(词法、诊断、artifact 模型、下游编译器胶水等,详见 module-map.md)与"Slang 语言本体"严格隔离。
  • source/slang/是唯一收拢 AST / IR / emit / check 源码的子系统。具体承载这些源码的目标在非嵌入构建下就是slang库本身;当SLANG_EMBED_CORE_MODULE开启时则改为slang-common-objects对象库,两个库目标声明为NO_SOURCE后从对象库重链接(见 source/slang/CMakeLists.txt)。其他任何需要编译服务的二进制(例如slangc,source/slangc/CMakeLists.txt)都是链接slang库而非直接取用单个源文件。
  • capability 子系统被拆成两个库。slang-capability-defs是生成的"头文件库",slang-capability-lookup是生成的"源码库",主slang目标两者都消费(见 source/slang/CMakeLists.txt)。
  • 核心模块是可选链接的。slang-embedded-core-moduleslang-no-embedded-core-module之间的选择由 CMake 选项SLANG_EMBED_CORE_MODULE控制,并以生成器表达式体现在 source/slang/CMakeLists.txt 中。当该选项关闭SLANG_LIB_TYPESHARED时,同一文件还会添加generate_core_module_cache目标:它运行slang-core-module-cache工具,把刚链接好的库与generate_core_module产出的无时间戳归档(core_module_archive_without_timestamp,见 source/slang-core-module/CMakeLists.txt)合成为带时间戳前缀的slang-core-module.bin放在库旁边。这是对 tools/ 下某个目标的构建顺序依赖而非链接边,且它把库文件在磁盘上的时间戳纳入缓存有效性判断。
  • slang-rt不依赖编译器。它的LINK_WITH_PRIVATE列表中不含任何编译器内部库,运行时与 CPU 目标的发射产物一起分发。
  • slang-glslang的导出面由一个文件约束,而不是由编译器的可见性设置约束。CXX_VISIBILITY_PRESET hidden-Wl,--exclude-libs,ALL分别隐藏 shim 自身与静态链接依赖的非导出符号,但都不能决定最终导出清单;slang-glslang.version-script才是导出名的唯一事实来源(见 source/slang-glslang/CMakeLists.txt)。ELF 直接消费该文件;Mach-O 没有 version-script 概念,因此同一 CMake 文件在配置期解析脚本的global:块并推导出-exported_symbols_list(ld64 会给 C 符号加下划线前缀,例如glslang_compile变成_glslang_compile)。推导而非手工维护第二份清单,正是为了两种格式不漂移;解析逻辑被设计为"宁可大声失败":提取不到任何名字、或去掉name;条目后还有残余非空白字符(说明有条目格式不符合预期会被静默丢弃)时,都会message(FATAL_ERROR)。对贡献者的实际含义记录在 shim 自身的头注释里——新增导出入口必须同时加到 version-script 与 C++ 中,否则在 ELF 和 macOS 上都不会被导出。
  • include/中的公共头文件不得包含source/中的私有头文件。这不是构建系统约束而是项目规则(见 CLAUDE.md),遵守它才能让下游用户只消费include/slang.h即可使用编译器。

循环与已知不规则现象

在逐目录的 CMake 文件中没有观察到任何链接级依赖环。依赖图是有向无环的,这本身就是架构健康度的重要信号。

不过有两个已知的不规则现象值得留意:

  1. slang库"向上"反向引用工具树的头文件。source/slang/CMakeLists.txt 把${slang_SOURCE_DIR}/tools加进INCLUDE_DIRECTORIES_PRIVATE,这使得slang-language-server.cpp能够编译#include "platform/performance-counter.h"(来自 tools/platform/)。这条路径没有伴随任何链接边——头文件只被用于其内联定义——但它意味着tools/platform/不能随意搬移而不触碰编译器库。
  2. slang-common-objects间接层。SLANG_EMBED_CORE_MODULE开启的某些模式下,同一批源文件先被编译进一个对象库,再被同时重链接进slang-without-embedded-core-module与主slang库(见 source/slang/CMakeLists.txt)。这是构建系统的便利设计,用于在用户面向的slang之外,额外产出一个"无内嵌核心模块的编译器"生成器(供slang-bootstrap使用)。

质量红线:如何保证这张图始终可信

因为依赖图是从构建文件机械推导的,规范为其设定了明确的质检清单,这些红线同样值得任何维护者遵守:

  • 每个节点都必须对应source/下的一个目录(或 module-map.md 中的一个小节标题);
  • 每条边都必须由某个CMakeLists.txt的条款背书——regenerate.py show <doc>可以列出目标文档的 watched paths,即当前提交下需要盯防的实际文件;
  • mermaid 图遵循项目约定:camelCase ID、节点名无空格、不显式着色、标签中的特殊字符用引号转义;
  • 文档体积上限 16 KB
  • 通用契约(docs/generated/design/_meta/prompts/_common.md)还要求:页面必须带 YAML front-matter(generatedmodelgenerated_atsource_commitwatched_paths_digestwarning),且正文中严禁出现裸{{/{%(GitHub Pages 的 Jekyll 会对全文执行 Liquid,未闭合的标签曾导致整个站点构建中断五天)。

阅读路径建议

  • 需要每个子系统的文件级清单与职责说明:见 docs/generated/design/architecture/module-map.md;
  • 需要更高层的整体架构导览:见 docs/generated/design/architecture/overview.md;
  • 需要运行时数据流(而非构建期依赖)视角:从 docs/generated/design/pipeline/overview.md 开始沿着编译管线阅读;
  • 需要生成式文档体系的通用规则(front-matter、Liquid 安全、标识符扫描等):见 docs/generated/design/_meta/prompts/_common.md。

【免费下载链接】slangMaking it easier to work with shaders项目地址: https://gitcode.com/GitHub_Trending/sl/slang

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询