We don开发者_JS百科’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 6 years ago.
Improve this questionMy C project uses preprocessor directives to activate / deactivate some features. It's not unusual to find some of the less common configurations do not compile anymore due to a change made a few days ago within an #ifdef
.
We use a script to compile the most common configurations, but I'm looking for a tool to ensure everything is compiled (testing is not a problem in our case, we just want to detect ASAP nothing stops compiling). Usually ifdefs / ifndefs are independent, so normally each module have to be compiled just twice (all symbols defined, all undefined). But sometimes the ifdefs are nested, so those modules have to be compiled more times.
Do you know of any tool to search all ifdef / ifndef (also nested ones) and gives how many times a module have to be compiled (with the set of preprocessor symbols to be defined in each one) to ensure every single source line of code is analyzed by the compiler?
I am not aware of any tool for doing what you want to do. But looking at your problem i think all you need is a script that will compile the source with all possible combinations of the preprocessor symbols.
Like say if you have..
#ifdef A
CallFnA();
#ifdef B
CallFnB();
#endif
CallFnC();
#endif
You will have to trigger the build with the foll combinations
- A and B both defined
- A not defined and B defined ( will not make sense here, but required for entire module)
- A defined and B not defined
- A and B both not defined
Would love to see some script that will grep
the source code and produce the combinations.
Something like
find ./ -name '*.cpp' -exec egrep -h '^#ifdef' {} \; | awk '{ print $2}' | sort | uniq
With *.cpp replaced with whatever files you want to search.
Here's a Perl script that does a hacky job of parsing #ifdef entries and assembles a list of the symbols used in a particular file. It then prints out the Cartesian Product of all the possible combinations of having that symbol on or off. This works for a C++ project of mine, and might require minor tweaking for your setup.
#!/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;
}
This will definitely fail with C-style /* */ comments, as I didn't bother to parse those effectively. The other thing to think about is that it might not make sense for all of the symbol combinations to be tested, and you might build that into the script or your testing server. For example, you might have mutually exclusive symbols for specifying a platform:
-DMAC
-DLINUX
-DWINDOWS
Testing the combinations of having these on and off doesn't really make sense. One quick solution is just to compile all combinations, and be comfortable that some will fail. Your test for correctness can then be that the compilation always fails and succeeds with the same combinations.
The other thing to remember is not all combinations are valid because many of them aren't nested. I think that compilation is relatively cheap, but the number of combinations can grow very quickly if you're not careful. You could make the script parse out which symbols are in the same control structure (nested #ifdefs for example), but that's much harder to implement and I've not done that here.
You can use unifdef -s to get a list of all preprocessor symbols used in preprocessor conditionals. Based on the discussion around the other answers this is clearly not quite the information you need, but if you use the -d option as well, the debugging output includes the nesting level. It should be fairly simple to filter the output to produce the symbol combinations you want.
Another solution is to compile in all the features and have run-time configuration testing for the features. This is a cool "trick" since it allows Marketing to sell different configurations and saves Engineering time by simply setting values in a configuration file.
Otherwise, I suggest a scripting language for building all the configurations.
Sorry, I don't know any tools to help you, but if I had to do this I would go with a simple script that does this: - Copy all source files to another place, - Add a running number (in a comment, obviously) at the start of each line (first code line of first file = 1, do not reset between files), - Pre-process using all the pre-defined configurations and check which lines were included and which weren't, - Check which lines have been included and which are missing.
Shouldn't take more than a couple of days to get that running using e.g. Perl or Python. What is required is a file that has a line including the configurations. This should be quick enough to do with the help of this script. Just check which lines are not included with the configurations already and edit the file until every line is included. Then just run this occasionally to make sure that there are no new paths.
Analyzing the source like you want would be a much more complex script and I'm not sure if it would be worth the trouble.
Hm, I initially thought that unifdef might be helpful, but looking further as to what you're asking for, no, it wouldn't be of any immediate help.
You can use Hudson with a matrix project. (Note that Hudson is not just a Java testing tool, but a very flexible build server which can build just about anything, including C/C++ projects). If you set up a matrix project, you get the option to create one or more axes. For each axis you can specify one ore more values. Hudson will then run your build using all possible combinations of the variable values. For example, if you specify
os=windows,linux,osx
wordsize=32,64
Hudson will build six combinations; 32- and 64-bit versions for each windows, linux, and osx. When building a "free-style project" (i.e. launching an external build script), the configurations are specified using environment variables. in the example above, "os" and "windows" will be specified as environment variables.
Hudson also has support for filtering combinations in order to avoid building certain invalid combinations (for example, windows 64-bit can be removed, but all others kept).
(Edited post to provide more details about matrix projects.)
May be You need grep?
精彩评论