San*_*ago 6 c conditional-compilation c-preprocessor
我的C项目使用预处理程序指令来激活/停用某些功能.由于几天前发生的变化导致一些较不常见的配置不再编译,这并不罕见#ifdef.
我们使用脚本来编译最常见的配置,但我正在寻找一种工具来确保编译所有内容(在我们的情况下测试不是问题,我们只想检测ASAP没有停止编译).通常ifdefs/ifndefs是独立的,因此通常每个模块只需编译两次(所有符号都定义,都是未定义的).但有时ifdefs是嵌套的,所以这些模块必须编译更多次.
您知道搜索所有ifdef/ifndef(也是嵌套的)的任何工具,并给出了一个模块必须编译多少次(每个模块中要定义的预处理器符号集)以确保每个源代码行是由编译器分析的?
这是一个 Perl 脚本,它执行解析 #ifdef 条目并组装特定文件中使用的符号列表的黑客工作。然后,它打印出该符号打开或关闭的所有可能组合的笛卡尔积。这适用于我的 C++ 项目,并且可能需要对您的设置进行细微调整。
#!/usr/bin/perl
use strict;
use warnings;
use File::Find;
my $path = $ENV{PWD};
my $symbol_map = {};
find( make_ifdef_processor( $symbol_map ), $path );
foreach my $fn ( keys %$symbol_map ) {
my @symbols = @{ $symbol_map->{$fn} };
my @options;
foreach my $symbol (@symbols) {
push @options, [
"-D$symbol=0",
"-D$symbol=1"
];
}
my @combinations = @{ cartesian( @options ) };
foreach my $combination (@combinations) {
print "compile $fn with these symbols defined:\n";
print "\t", join ' ', ( @$combination );
print "\n";
}
}
sub make_ifdef_processor {
my $map_symbols = shift;
return sub {
my $fn = $_;
if ( $fn =~ /svn-base/ ) {
return;
}
open FILE, "<$fn" or die "Error opening file $fn ($!)";
while ( my $line = <FILE> ) {
if ( $line =~ /^\/\// ) { # skip C-style comments
next;
}
if ( $line =~ /#ifdef\s+(.*)$/ ) {
print "matched line $line\n";
my $symbol = $1;
push @{ $map_symbols->{$fn} }, $symbol;
}
}
}
}
sub cartesian {
my $first_set = shift @_;
my @product = map { [ $_ ] } @$first_set;
foreach my $set (@_) {
my @new_product;
foreach my $s (@$set) {
foreach my $list (@product) {
push @new_product, [ @$list, $s ];
}
}
@product = @new_product;
}
return \@product;
}
Run Code Online (Sandbox Code Playgroud)
这肯定会因 C 风格的 /* */ 注释而失败,因为我懒得有效地解析这些注释。另一件需要考虑的事情是,对于要测试的所有符号组合可能没有意义,您可能会将其构建到脚本或测试服务器中。例如,您可能有互斥的符号来指定平台:
-DMAC
-DLINUX
-DWINDOWS
Run Code Online (Sandbox Code Playgroud)
测试这些打开和关闭的组合并没有真正的意义。一种快速的解决方案就是编译所有组合,并放心有些组合会失败。然后,您对正确性的测试可以是编译总是失败并使用相同的组合成功。
另一件要记住的事情是并非所有组合都有效,因为其中许多组合不是嵌套的。我认为编译相对便宜,但如果不小心,组合的数量可能会增长得非常快。您可以让脚本解析出哪些符号位于相同的控制结构中(例如嵌套的#ifdefs),但这更难实现,我在这里没有这样做。