题外话:不知不觉,达成了llvm-project 1900 commits的成就。
LLVM中命令行选项的处理有两个库。
llvm/Support/ComandLine.h
文档参见https://llvm.org/docs/CommandLine.html
简单来说,用全局变量(llvm::cl::opt<type> var
最常见,也有llvm::cl::list
等)表示命令行选项。opt
的构造函数会在一个全局的registry中注册这个命令行选项。
在main
中调用llvm::cl::ParseCommandLineOptions(argc, argv, ...)
解析命令行。opt
支持很多类型,如各种integer types、bool、std::string等,还支持自定义enum类型。
1 | static cl::OptionCategory cat("split-file Options"); |
LLVM中有很多开发者使用的命令行选项,除了功能选项外,还有:
- 对某一pass有较大改动,in-tree开发时为了防止衰退,设置一个预设为false的enable变量
- 一段时间功能稳定,把预设值改为true。在某些场合下发现衰退的用户可以使用false作为workaround
- 给一个pass提供更多输入,用于测试
这个库使用便捷,添加一个新选项只需要在一个局部文件中加一个变量。还提供了一些锦上添花的小功能,如推荐拼写接近的选项。
但命令行解析的定制性很弱。比如:
- 一个
cl::opt<bool>
选项接受-v 0 -v=0 -v false -v=false -v=False
等多种输入方式 - 不便同时支持
--long
和--no-long
。偶尔有需求时的workaround是给--no-long
也设置一个变量。假如要处理两个选项互相override,就要判断两个选项在命令行中的相对位置
面向用户的外部工具往往有这类定制需求。GNU getopt_long
的风格是--long
。llvm/Support/ComandLine.h
里-long
和--long
可以混用,很长一段时间不支持强制--
。
LLVM binary utilities (llvm-nm、llvm-objdump、llvm-readelf等)为了替代GNU binutils,
需要提供POSIX shell utilities风格的grouped short options (-ab
表示-a -b
)。
很长一段时间这个功能不被支持,困扰了想要迁移到LLVM binary utilities的用户。
另外cl::opt
是singleton,也可以定义局部变量动态增加选项,但这种用法很少见(llvm-readobj和llvm-cov)。
还有个很奇特的用法,opt工具中legacy pass manager自动获取pass name列表,并注册大量全局选项。
为了防止错误,cl::opt
不支持多次定义同一个选项。如果同时链接了shared object和archive两种LLVM库,就会触发经典错误:
1 | : CommandLine Error: Option 'help-list' registered more than once! |
在Clang里如果要设置cl::opt
变量的值,可以用-mllvm -option=value
。使用LLD/LLVMgold.so Full/Thin LTO也可以设置这些选项值,用-plugin-opt=-option=value
(LLD也可用-mllvm
)。
llvm/Option/OptTable.h
原先给Clang开发,后来移入llvm,被llvm-objcopy、lld、llvm-symbolizer等采用。
用一个domain-specific language (TableGen)描述选项,生成一个parser。解析过的选项组织成一个object,每个选项用一个integer表示。
检查一对预设值不定的boolean选项(--demangle --no-demangle
)是否生效很容易:Args.hasFlag(OPT_demangle, OPT_no_demangle, !IsAddr2Line)
。
1 | multiclass B<string name, string help1, string help2> { |
1 | opt::InputArgList Args = parseOptions(argc, argv, IsAddr2Line, Saver, Tbl); |
注意GCC的命令行选项不支持grouped short options,因此Clang也没有需求。很长一段时间因为缺少这个功能限制了它的使用场景。我在2020年7月加入了grouped short options (D83639)。
轶闻:LLD采用这个库解析命令行选项。GNU ld实际上支持grouped short options,比如ld.bfd -vvv
表示-v -v -v
。我提出GNU ld实际上支持很多-long
风格的选项,再支持grouped short options容易引起混乱。
1 | % touch an ommand ':)' |
binutils 2.36有望deprecate grouped short options:)
再拓展一下,很多getopt_long
用户用一个switch加大量case处理命令行选项,很容易弄出各种各样position dependent行为。
对于大型build system,有时候搞不清楚compiler/linker options是在什么地方添加的,有些position dependent行为挺讨厌的。