- 后端
- 前端
- 开发工具
【免费下载链接】compiler-explorer
Run compilers interactively from your web browser and interact with the assembly
本文围绕 Compiler Explorer(CE)仓库根目录下的独立调试工具compiler-args-app.ts展开,讲解如何用它快速提取、检查并验证各类编译器(GCC、Clang、Rust、Go、Zig 等)的参数解析结果。读完本文,你将掌握该工具的全部命令行用法、输出字段含义、常见故障排查方法,并理解 CE 后端参数解析(lib/compilers/argument-parsers.ts)与参数统计存储(lib/compiler-arguments.ts)的底层工作原理,从而能够在新增编译器版本、排查 UI 中选项缺失或编写自定义解析器时有的放矢。
工具定位:为什么需要单独的参数调试程序
Compiler Explorer 在界面中展示的优化级别、语言标准、目标平台等下拉选项,并非硬编码在配置文件中,而是由后端在运行时实际调用编译器可执行文件、解析其--help输出后动态得出的。这一过程依赖 lib/compilers/argument-parsers.ts 中的大量解析器类。当某个编译器选项没有出现在 UI 中、或某类参数被错误识别时,需要快速定位是「编译器 help 输出格式变化」还是「解析器正则失效」造成的。
compiler-args-app.ts正是为此设计的独立命令行工具:它模拟 CE 后端加载一个「裸编译器」对象,运行指定解析器完成一次完整的参数提取,然后把结果以易读的文本形式打印出来,供开发者对照排查。它不依赖 Web 界面、不依赖数据库,也不需要完整的编译器配置文件,只需一个可执行文件路径即可运行。
运行该工具
基本用法
工具通过node配合tsx直接运行 TypeScript 源码,无需预先编译。仓库根目录下执行:
node --import tsx compiler-args-app.ts \ --parser <compiler-type> \ --exe <path-to-compiler> \ [--padding <number>] \ [--debug]命令行参数
从 compiler-args-app.ts 的 commander 定义可以看到完整的参数契约:
| 参数 | 是否必填 | 说明 |
|---|---|---|
--parser <type> | 必填 | 指定使用的编译器解析器类型,取值见下文「支持的解析器类型」 |
--exe <path> | 必填 | 编译器可执行文件的路径 |
--padding <number> | 可选 | 输出格式化的对齐宽度,默认值40,传入后被parseInt解析为十进制整数 |
--debug | 可选 | 开启调试输出,等价于将 lib/logger.ts 的日志级别设为debug(if (opts.debug) logger.level = 'debug'),用于输出解析过程中的详细信息 |
注意:参数之间应使用空格分隔,不要使用等号(=)。例如应写--parser gcc而不是--parser=gcc。工具通过configureOutput对错误做了拦截:一旦检测到「too many arguments」,会提示该工具只接受--parser、--exe、--padding、--debug四个选项,并打印示例用法(compiler-args-app.ts)。此外,allowUnknownOption(false)意味着任何未知选项都会直接报错退出,不要试图往命令行里追加额外参数或 Shell 重定向(如2>&1),它们会被当作参数处理。
典型示例
调试 GCC 的参数解析(注意--debug会打印底层解析日志):
node --import tsx compiler-args-app.ts \ --parser gcc \ --exe /opt/compiler-explorer/gcc-14.2.0/bin/g++ \ --debug用自定义对齐宽度调试 Clang:
node --import tsx compiler-args-app.ts \ --parser clang \ --exe /opt/compiler-explorer/clang-19.1.0/bin/clang++ \ --padding 50调试 Rust 编译器(Rust 解析器还会额外探测 edition 信息):
node --import tsx compiler-args-app.ts \ --parser rust \ --exe /opt/compiler-explorer/rust-1.80.0/bin/rustc调试 Go 编译器(Go 解析器需要一个真实的示例源文件参与编译,详见下文原理部分):
node --import tsx compiler-args-app.ts \ --parser golang \ --exe /opt/compiler-explorer/golang-1.24.2/go/bin/go支持的解析器类型
compiler-args-app.ts 中的compilerParsers对象把--parser的名称映射到argument-parsers.ts中对应的解析器类,当前支持:
| parser 名称 | 对应编译器 | parser 名称 | 对应编译器 |
|---|---|---|---|
gcc | GNU Compiler Collection(GCCParser) | rust | Rust(RustParser) |
clang | Clang/LLVM(ClangParser) | mrustc | mrustc(MrustcParser) |
ldc | LDC(D 语言) | num | Nim(NimParser) |
erlang | Erlang | crystal | Crystal |
pascal | Pascal 编译器 | ts | TypeScript Native |
ispc | Intel SPMD Program Compiler | turboc | Turbo C |
java | Java | toit | Toit |
kotlin | Kotlin | circle | Circle |
scala | Scala | ghc | Glasgow Haskell Compiler |
vc | Visual C++ | tendra | TenDRA |
golang | Go | zig | Zig |
新增解析器类型必须手工添加到compiler-args-app.ts的compilerParsers类型列表(以及 lib/compilers/argument-parsers.ts 中对应的解析器类),工具才会接受该名称;否则getParser()会打印Unknown parser type并调用process.exit(1)退出(compiler-args-app.ts)。注意该映射表只是调试工具的入口集合,仓库内 lib/compilers/argument-parsers.ts 实际还包含更多解析器(如 ElixirParser、MojoParser、ICCParser、ClojureParser、JuliaParser、Z88dkParser、WasmtimeParser、SwiftParser、GnuCobolParser 等),它们供主服务使用,未全部登记到本调试工具中。
输出解读
doTheParsing()与print()(compiler-args-app.ts)共同决定输出内容,共分六类信息。
1. 可用参数列表(Available Arguments)
逐一列出解析出的全部编译器参数,每行格式为「参数标志 + 对齐填充 + 描述」,例如-O2、--std=c++20。对齐宽度由--padding控制(默认 40),使用String.prototype.padEnd实现。描述文本来自编译器 help 输出,由解析器的正则捕获后存入CompilerArguments.possibleArguments。
2. 标准版本(Stdvers)
展示编译器支持的语言标准版本,例如 C++11、C++14、C++17。不同解析器的探测方式差异很大:
- GCC 系(GCCParser.getPossibleStdvers)运行
-fsyntax-only --help=<lang>,过滤-std=开头且描述不以Deprecated开头的选项; - Clang 系(ClangParser.getPossibleStdvers)反其道而行,主动用
-std=c++9999999触发错误,再从 stderr 的note: use '...' for '...' standard提示行中提取可选值; - Visual C++(VCParser)则解析
/help输出中/std:<...>枚举。
3. 目标平台(Targets)
列出编译器能够生成代码的目标平台。GCC 通过--target-help输出中Known valid arguments for -march= option:之后的部分提取(lib/compilers/argument-parsers.ts);Clang 通过--print-targets输出按正则提取(lib/compilers/argument-parsers.ts);Rust 则直接调用rustc --print target-list(lib/compilers/argument-parsers.ts)。
4. 版本系列(Editions)
主要针对 Rust 编译器:RustParser 通过运行rustc --help -v并用正则--edition <?([\w|]*)>?从帮助文本中提取以|分隔的 edition 列表(lib/compilers/argument-parsers.ts)。对非 Rust 解析器,基类的getPossibleEditions()默认返回空数组。
5. 编译器能力(Compiler Capabilities)
解析结束后工具打印一组布尔/对象状态,与源码中setCompilerSettingsFromOptions的赋值一一对应(lib/compilers/argument-parsers.ts):
supportsOptOutput:是否支持优化输出。GCC 检测到-fopt-info时置为 true,并把optArg设为-fopt-info-all=all.opt;Clang 则检测-fsave-optimization-record(lib/compilers/argument-parsers.ts);supportsStackUsageOutput:是否支持栈用量报告,GCC/Clang 均通过检测-fstack-usage开启;optPipeline:优化管线信息。Clang 只有在-mllvm同时支持--print-before-all与--print-after-all时才填充对象(lib/compilers/argument-parsers.ts),并进一步探测--print-module-scope与-fno-discard-value-names;supportsGccDump:是否支持 GCC dump 输出,检测到任一-fdump-前缀参数即开启(对 Rust、Swift 这类也被 GCCParser 处理的编译器同样生效,源码注释也承认该检测并非绝对可靠)。
6. 目标支持格式探测(Target Support Detection)
doTheParsing()按优先级顺序判断编译器接受哪种目标平台参数格式,只打印命中(或未命中)的一个结果(compiler-args-app.ts):
supportsTargetIs:使用--target=<target>格式(hasSupportStartsWith(options, '--target='));supportsTarget:使用--target <target>空格分隔格式;supportsHyphenTarget:使用-target <target>格式;supportsMarch:使用--march=<arch>格式;- 以上都不命中时打印
none of the things?。
从解析器源码看,GCCParser 同时设置了supportsTargetIs/supportsTarget(基于 help 输出),而 ZigParser 还会在-target命中时额外设置supportsHyphenTarget(lib/compilers/argument-parsers.ts)。
底层原理:参数解析如何工作
解析器基类与行级解析
所有解析器继承自BaseParser(lib/compilers/argument-parsers.ts)。核心方法是parseLines(stdout, optionWithDescRegex, optionWithoutDescRegex):它逐行扫描编译器 help 输出的 stdout 与 stderr 合并文本,先用「带描述正则」捕获「参数 + 两空格以上 + 描述」,不匹配再用「无描述正则」捕获裸参数;对捕获到的连续描述行,还会把上一行以-结尾的续行拼接、压缩多余空格。这是理解「为什么参数描述会出现拼接」的关键实现。
getOptions(helpArg)通过execCompilerCached实际执行编译器命令,仅当退出码为 0 时才解析输出,然后把结果写入this.compiler.possibleArguments.populateOptions(options)(lib/compilers/argument-parsers.ts)。
各解析器的差异化探测策略
- GCCParser:
parse()并行执行 6 组命令——-fsyntax-only --help、--target-help、--help=common、--help=warnings、--help=optimizers、--help=target,合并结果后再统一判定特性(lib/compilers/argument-parsers.ts)。它还专门用-fsyntax-only --target-help -masm=intel验证 Intel 汇编语法是否真正可用(checkAndSetMasmIntelIfSupported,lib/compilers/argument-parsers.ts),因为-masm=虽出现在 help 中但可能不被支持; - ClangParser:主 help 用
--help,隐藏选项用-mllvm --help-list-hidden -x c++ /dev/null -c并放入独立临时目录(createAndUseTempDir)执行(lib/compilers/argument-parsers.ts); - RustParser:并行执行
--help、-C help、--help -v三组命令,其中-C help输出用专门的正则^\s*(-c\s*[\d=a-z-]*)\s--\s(.*)解析(lib/compilers/argument-parsers.ts); - GolangParser:Go 工具链无法单独输出选项列表,解析器必须用真实示例文件参与编译——它通过
getExamplesRoot()定位仓库内 examples/go/default.go,执行build -o /tmp/output.s "-gcflags=-S --help" <example>并配合临时目录(lib/compilers/argument-parsers.ts),所以调试 Go 时需要保证示例路径对当前环境(包括 jail)可见; - JuliaParser:不从 Julia 运行时而是从包装脚本获取 help,执行时会把
compilerWrapperPath作为第一个参数传给编译器,因此 Julia 需要正确设置包装脚本路径(lib/compilers/argument-parsers.ts)。调试工具中对应逻辑是:当--parser为juliawrapper时设置etc/scripts/julia_wrapper.jl路径(compiler-args-app.ts)。
参数存储与排序
解析结果由 lib/compiler-arguments.ts 中的CompilerArguments类管理。populateOptions()把新解析的参数合并进possibleArguments字典(lib/compiler-arguments.ts)。该类还提供两个后续会进入 UI 的关键方法:
getOptimizationArguments():按描述中是否包含optimize/optimization过滤出优化相关参数(lib/compiler-arguments.ts);getPopularArguments():在没有统计数据时优先排序「优化」「标准」类参数,否则按timesused使用频次排序并截取前 5 个(maxPopularArguments = 5),用于 UI 常用参数推荐(lib/compiler-arguments.ts)。
此外match()实现了参数模糊匹配(处理<number>、=val、:、[等占位符形式),统计到的使用次数可通过loadFromFile/loadFromStorage从本地目录或 S3 恢复,实现按真实使用频率排序热门参数。
调试技巧
1. 优先开启 Debug 模式
添加--debug后,GCCParser/ClangParser 会在setCompilerSettingsFromOptions中用logger.debug打印识别出的全部参数键(例如gcc-like compiler options: ...),同时任何解析错误、执行异常都会以 debug 级别输出,是定位问题最直接的手段。
2. 检查解析器输出
若参数未被正确识别,按以下顺序排查:
- 确认编译器可执行文件路径正确、且对当前用户可执行;
- 确认
--parser类型与实际编译器一致(例如用gcc解析器去跑 Clang 可能会部分工作,但会漏掉-fsave-optimization-record、-mllvm等 Clang 特有参数的处理); - 确认编译器运行所需的环境变量(PATH、LD_LIBRARY_PATH 等)已就绪,必要时用
ldd检查动态库依赖是否完整。
3. 常见问题
空参数或参数缺失
- 编译器可能不支持解析器期望的 help 标志格式。可先用
--help(或对应解析器实际调用的标志,如 GCC 的-fsyntax-only --help)手动运行编译器,肉眼比对输出格式与解析器正则是否匹配(正则在 lib/compilers/argument-parsers.ts 各getOptions实现中)。 - 注意有些编译器调用 help 时返回非零退出码(例如源码注释提到 glslang 调用
--help返回码为 1),此时BaseParser.getOptions会因result.code === 0判断失败而返回空对象——需要像 GlslangParser 那样覆写该方法忽略退出码(lib/compilers/argument-parsers.ts)。
解析器崩溃
- 开启
--debug查看确切的报错栈; - 检查可执行文件是否具备执行权限;
- 用
ldd <exe>检查依赖库是否缺失。
解析器类型不匹配
- 使用错误的解析器(如用
gcc解析器解析clang编译器)可能部分工作,但会漏掉特定功能探测。务必让解析器与实际编译器类型匹配。
4. 测试自定义编译器 / 编写新解析器
按以下流程为新增编译器接入:
- 先用一个相近的既有解析器(如 GCC 系)跑一遍,看哪些参数能解析、哪些不能;
- 手动运行编译器 help,观察其输出格式(缩进、分隔符、描述列位置),据此确定新正则;
- 必要时在 lib/compilers/argument-parsers.ts 中新增自定义解析器类,可继承
BaseParser(通用)或GCCParser/ClangParser(复用目标平台与标准版本探测逻辑,参考ICCParser、ZigParser等子类的覆写模式); - 把新解析器注册到 compiler-args-app.ts 的
compilerParsers映射中,即可用本工具反复验证。
5. 环境注意事项
- 工具直接使用当前进程的环境变量(
getDefaultExecOptions返回env: process.env)与工作目录(cwd: process.cwd()),命令超时默认 10 秒(compiler-args-app.ts); - 某些编译器需要特定的环境配置(PATH、LD_LIBRARY_PATH 等);
- 涉及临时目录的解析(Clang 隐藏选项、Go 编译探测)依赖
createAndUseTempDir能力,保证tmp目录可用。
与 CE 开发流程的集成
本工具在以下场景最有用:
- 新增编译器版本:CE 升级编译器镜像后,先用本工具验证新版 help 输出是否仍能被既有解析器正确解析;
- 排查 UI 选项缺失:当某个优化级别、标准版本或目标平台没有出现在 Web 界面中时,用本工具确认是「解析器没解析出来」还是「配置没开启」;
- 理解参数提取能力:快速查看 CE 能从指定编译器提取出哪些参数及其描述;
- 测试自定义解析器:开发新的解析器类后无需启动完整服务即可迭代验证;
- 验证编译器配置:确认
.properties配置指向的编译器与解析器组合行为符合预期。
解析结果最终驱动 CE 的运行时行为:决定可用的优化级别与标志(getOptimizationArguments/getPopularArguments)、配置语言标准选项(Stdvers)、检测支持的目标架构,并依据标志可用性开启特性开关——检测到优化输出标志时启用优化管线查看器(optPipeline)、发现 dump 标志时启用 GCC tree/RTL dump(supportsGccDump)、存在栈输出标志时提供栈用量分析、检测到相关标志时启用 Intel 语法支持与 CFG(控制流图)支持,最终把这些能力写入编译器属性,控制 UI 功能与编译行为。
小结
compiler-args-app.ts是 Compiler Explorer 参数解析链路的「显微镜」:它把 lib/compilers/argument-parsers.ts 中数十个解析器对编译器 help 输出的理解能力完整暴露到命令行。掌握它的用法与输出含义,等于掌握了一条从「编译器原生 help 文本」到「CE UI 下拉选项」的完整调试链路。当你在 CE 开发中遇到任何「选项不出现」「特性没开启」的问题,第一反应就应该是:node --import tsx compiler-args-app.ts --parser <type> --exe <path> --debug。
- 后端
- 前端
- 开发工具
【免费下载链接】compiler-explorer
Run compilers interactively from your web browser and interact with the assembly
相关推荐
Compiler Explorer扩展开发:添加自定义编译器和工具完全指南
Compiler Explorer扩展开发:添加自定义编译器和工具完全指南 Compiler Explorer是一个功能强大的在线编译器和代码分析平台,支持多种
后端前端开发工具Compiler Explorer 完整安装配置指南:快速掌握在线编译器调试神器
Compiler Explorer 完整安装配置指南:快速掌握在线编译器调试神器 Compiler Explorer 是一款强大的在线编译器交互工具,让你直接在
后端前端开发工具Compiler Explorer执行功能详解:如何测试和调试编译结果
Compiler Explorer执行功能详解:如何测试和调试编译结果 Compiler Explorer是一个强大的在线编译器工具,它不仅能够编译代码,还提供
后端前端开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考