6

我的C项目使用预处理器指令来激活/停用某些功能。由于几天前在#ifdef.

我们使用脚本来编译最常见的配置,但我正在寻找一种工具来确保编译所有内容(在我们的案例中测试不是问题,我们只想尽快检测到没有停止编译)。通常 ifdefs / ifndefs 是独立的,因此通常每个模块只需编译两次(所有符号已定义,所有符号均未定义)。但有时 ifdef 是嵌套的,因此必须多次编译这些模块。

你知道有什么工具可以搜索所有 ifdef / ifndef(也是嵌套的)并给出一个模块必须编译多少次(每个模块中定义一组预处理器符号)以确保每个源代码行由编译器分析?

4

8 回答 8

4

我不知道有任何工具可以做你想做的事。但是看看你的问题,我认为你所需要的只是一个脚本,它将用预处理器符号的所有可能组合编译源代码。

就像说如果你有..

#ifdef A 
    CallFnA();
#ifdef B
    CallFnB();
#endif
    CallFnC();
#endif

您必须使用以下组合触发构建

  1. A 和 B 都定义了
  2. A 未定义和 B 已定义(在这里没有意义,但整个模块都需要)
  3. A已定义,B未定义
  4. A 和 B 都未定义

希望看到一些将grep源代码和产生组合的脚本。就像是

find ./ -name '*.cpp' -exec egrep -h '^#ifdef' {} \; | awk '{ print $2}' | sort | uniq

将 *.cpp 替换为您要搜索的任何文件。

于 2010-01-06T11:55:40.300 回答
2

这是一个 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;
}

这肯定会因 C 风格的 /* */ 注释而失败,因为我没有费心去有效地解析它们。要考虑的另一件事是,对所有要测试的符号组合可能没有意义,您可以将其构建到脚本或测试服务器中。例如,您可能有用于指定平台的互斥符号:

-DMAC
-DLINUX
-DWINDOWS

测试这些开关的组合并没有真正的意义。一种快速的解决方案是编译所有组合,并且对某些组合会失败感到满意。然后,您对正确性的测试可以是编译总是失败并使用相同的组合成功。

要记住的另一件事是并非所有组合都是有效的,因为它们中的许多不是嵌套的。我认为编译相对便宜,但如果你不小心,组合的数量会增长得非常快。你可以让脚本解析出哪些符号在同一个控制结构中(例如嵌套的#ifdefs),但这更难实现,我在这里没有这样做。

于 2010-01-07T08:14:39.960 回答
1

您可以使用unifdef -s获取预处理器条件中使用的所有预处理器符号的列表。基于围绕其他答案的讨论,这显然不是您需要的信息,但如果您也使用 -d 选项,调试输出包括嵌套级别。过滤输出以生成所需的符号组合应该相当简单。

于 2010-01-18T21:03:53.887 回答
0

另一种解决方案是编译所有功能并对这些功能进行运行时配置测试。这是一个很酷的“技巧”,因为它允许营销人员通过简单地在配置文件中设置值来销售不同的配置并节省工程时间。

否则,我建议使用脚本语言来构建所有配置。

于 2010-01-07T00:28:11.203 回答
0

抱歉,我不知道任何工具可以帮助您,但如果我必须这样做,我会使用一个简单的脚本来执行此操作: - 将所有源文件复制到另一个地方, - 添加一个运行编号(在评论中,显然)在每一行的开头(第一个文件的第一个代码行 = 1,不要在文件之间重置), - 使用所有预定义的配置进行预处理并检查哪些行被包含,哪些行不包含, - 检查哪些行已包含在内,哪些行缺失。

使用 Perl 或 Python 不应该花费超过几天的时间来运行它。需要的是一个包含配置的文件。在此脚本的帮助下,这应该足够快。只需检查哪些行已不包含在配置中并编辑文件,直到包含每一行。然后偶尔运行它以确保没有新路径。

像您想要的那样分析源将是一个更复杂的脚本,我不确定它是否值得麻烦。

于 2010-01-07T08:15:12.637 回答
-2

嗯,我最初认为unifdef可能会有所帮助,但进一步了解您的要求,不,它不会立即提供帮助。

于 2010-01-06T12:00:40.203 回答
-2

您可以将Hudson矩阵项目一起使用。(请注意,Hudson 不仅仅是一个 Java 测试工具,而是一个非常灵活的构建服务器,它可以构建几乎任何东西,包括 C/C++ 项目)。如果您设置一个矩阵项目,您可以选择创建一个或多个。对于每个轴,您可以指定一个或多个值。然后,Hudson 将使用变量值的所有可能组合运行您的构建。例如,如果您指定

os=windows,linux,osx
wordsize=32,64

哈德森将建立六种组合;每个 windows、linux 和 osx 的 32 位和 64 位版本。在构建“自由式项目”(即启动外部构建脚本)时,使用环境变量指定配置。在上面的示例中,“os”和“windows”将被指定为环境变量。

Hudson 还支持过滤组合以避免构建某些无效组合(例如,可以删除 Windows 64 位,但保留所有其他组合)。

(编辑帖子以提供有关矩阵项目的更多详细信息。)

于 2010-01-06T12:22:14.693 回答
-3

可能是你需要grep?

于 2010-01-06T11:40:51.190 回答