これは、#ifdef エントリを解析し、特定のファイルで使用されているシンボルのリストを組み立てるハックな仕事をする Perl スクリプトです。次に、そのシンボルをオンまたはオフにするすべての可能な組み合わせのデカルト積を出力します。これは私の 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;
}
これは、C スタイルの /* */ コメントでは確実に失敗します。それらを効果的に解析することを気にしなかったからです。考慮すべきもう 1 つのことは、すべてのシンボルの組み合わせをテストするのは意味がない可能性があり、それをスクリプトまたはテスト サーバーに組み込む可能性があるということです。たとえば、プラットフォームを指定するために相互に排他的なシンボルがある場合があります。
-DMAC
-DLINUX
-DWINDOWS
これらのオンとオフの組み合わせをテストすることは、実際には意味がありません。簡単な解決策の 1 つは、すべての組み合わせをコンパイルすることです。正しさのテストは、コンパイルが常に失敗し、同じ組み合わせで成功することです。
覚えておくべきもう 1 つのことは、それらの多くはネストされていないため、すべての組み合わせが有効であるとは限らないということです。コンパイルは比較的安価だと思いますが、注意しないと組み合わせの数がすぐに増えてしまいます。どのシンボルが同じ制御構造 (ネストされた #ifdefs など) にあるかをスクリプトが解析するようにすることもできますが、それを実装するのははるかに難しいため、ここでは実行しませんでした。