将外部代码检查器集成到 Eclipse CDT 中

使用 Codan 在 Eclipse 中执行 C/C++ 分析工具

Codan 是针对 C/C++ 项目的 Eclipse CDT(C/C++ 开发工具)内置代码分析框架。Codan 提供了基础架构来执行静态代码分析,并提供了随时可用的问题检查器。随着 Eclipse Juno 的发布,Codan 被扩展为支持开发人员自动执行外部代码分析工具。Alex Ruiz 解释了为什么该更新对于 Eclipse CDT 用户来说是一个好消息,然后展示了如何使用 Java™ 代码和少许 XML 快速将您最喜爱的代码检查器集成到 Eclipse C/C++ 开发环境中。

Alex Ruiz, 软件工程师, Google

http://www.ibm.com/developerworks/i/p-aruiz.jpgAlex Ruiz 喜欢阅读与 Java 开发、面向对象编程、API 设计及测试相关的任何资料。编程是他的最爱。Alex 是 FEST 的创建者之一,那是个创新的 Java 库,定位于实现 UI 测试以及常规测试更加简便。他在 Google 工作。查看 Alex 的 博客



2012 年 10 月 08 日

Codan 是在 C/C++ 项目上执行代码检查的代码分析框架。自 2011 年起,Codan 已成为 Eclipse CDT(C/C++ 开发工具)一部分,它不仅提供执行静态代码分析所需的全部基础架构,还提供了一些有用的、随时可用的问题检查器(参阅 参考资料)。

Codan 于 2012 年 6 月随 Eclipse Juno 进行了更新,支持开发人员在 Eclipse 中自动执行外部代码分析工具。对于 Eclipse CDT 和 C/C++ 开发人员来说,这是一个令人鼓舞的进步。尽管之前提供的问题检查程序表现良好,但仍然需要更多地提供与现有外部代码分析工具看齐的 Codan 功能。现在,Codan 可轻松实现与成熟外部工具(比如 Cppcheck 和 clang_check)的集成。

developerWorks 中的相关教程

有关使用 Eclipse CDT 进行应用程序开发的更多信息:

与开发人员单独使用 Codan 相比,将外部代码分析工具与 Eclipse CDT 集成能够提供更多更好的代码检查,还会极大地改进综合开发生产力。现在,我们可以从 Codan 的 Preferences 页面对外部代码分析工具进行配置。一旦与 Codan 集成,就可以自动调用该工具,以编辑器标记的形式显示其输出。

在本文中,将向您展示如何使用 Java 代码和少许 XML 将您最喜爱的代码分析器集成到您的 Eclipse C/C++ 开发环境中。我的示例基于 Cppcheck 和 Codan 的集成,但该过程应该同样适用于您选择的工具。

安装 Eclipse Juno 和 CDT

要跟随本文中的示例进行学习,需要安装 Eclipse Juno 和 CDT。如果您尚未安装 Eclipse,可以安装一个带有 CDT 预安装程序的版本。为此,只需从 Eclipse downloads 页面选择 Eclipse IDE for C/C++ Developers 即可。

如果您已经安装了一个不包括 CDT 的 Eclipse,那么请按照下列指令来更新您的开发环境:

  1. 在 Eclipse 中,选择菜单 Help > Install New Software...
  2. 在 Install 对话框中,从下拉列表中选择 Juno
  3. 在 Programming Languages 目录中,选择 C/C++ Development Tools SDK
图 1. 安装 CDT
Eclipse 插件安装页面的屏幕截图

除了 CDT 之外,还需要安装标准 GNU C/C++ 开发工具来编译、构建和调试您的代码。请参阅 参考资料,获取如何安装这些工具的说明。

启动 Codan

大多数 Codan 代码检查器都是默认启用的。您可在工作区或项目层分别使用 Eclipse 的 Preferences 或 Project Property 页面单独配置 Codan 代码检查。

在 Codan 的 Preferences 页面中,如 图 2 所示,您可以看到提供的所有代码检查器以及每个检查器上报告的代码问题。

图 2. Codan Preferences 页面上的代码检查器
Codan Preferences 页面的屏幕截图

在此页中,您可以启用、禁用或修改一个问题的严重程度。如果您想配置某个问题的其他属性,可以选中该问题并单击 Customize Selected... 按钮。图 3 显示了问题 “Name convention for function” 的配置选项。

图 3. 配置一个问题
显示使用 Codan 配置问题的路径的屏幕截图

图 3 中的第 3 个选项卡允许您指定问题检查的启动形式:

  • Run as you type:当用户在 CDT 编辑器中更改某个文件时。
  • Run on file open:当文件在 CDT 编辑器中打开时。
  • Run on file save:当 CDT 编辑器上未保存的更改被保存时。
  • Run on incremental build:当发布增量构建时(通常在保存一个文件并启用应用程序级选项 “Build Automatically” 时)。如果您同时启用该选项和 “Run on file save”,那么代码检查将会运行两次。
  • Run on full build:当发布完全构建时(例如,当清理某个项目时)。
  • Run on demand:当用户从上下文按钮项 “Run C/C++ Code Analysis” 手动触发代码检查时。

使用 Codan 进行代码检查

为了使您能看到运行的 Codan,我会创建一个 C++ 项目和一个小的 C++ 文件。在此文件中,我将为文件本身分配一个变量。Codan 包括代码检查 “Assignment to itself”,这是默认启动的,严重程度级别为 “error”。该代码检查被配置为在您输入时运行,因此错误会立即弹出。

图 4. 执行一个代码检查的 Codan
屏幕截图显示了执行代码检查的 Codan

图 4 中,您会看到,Codan 已经发现了自赋值错误,并在我还没有来得及保存该文件之前报告错误。

要学习关于使用 Codan 的更多信息,请访问项目的主页(参阅 参考资料)。

将 Cppcheck 集成到 Eclipse CDT 中

要集成外部代码分析工具与 Codan,需要编写一个知道如何调用此工具的特殊检查器,一个检查器 是 Codan 的 IChecker 接口的一个实现,在给定的 IResource(通常是一个 IFile)上执行某种代码检查。

为了演示如何轻松创建一个基于外部工具的检查器,我们会创建一个调用流行工具 Cppcheck 的检查器(参阅 参考资料)。下面我们将执行以下操作:

  • 创建一个 Eclipse 插件项目,并添加 Codan 作为一个依赖项。
  • 创建一个错误分析器来分析 Cppcheck 输出,并创建编辑器标记(如果需要的话)。
  • 创建代码检查器,该检查器是负责调用 Cppcheck 的类。

步骤 1. 创建一个 Eclipse 插件项目

要创建一个 Codan 检查器,首先要创建一个新 Eclipse 插件项目:

  1. 选择菜单 File > New > Project...
  2. 在类别 Plug-in Development 中,选择 Plug-in Project
  3. 为项目输入一个名称(我使用的名称是 “CppcheckChecker”)并单击 Next
  4. 接受默认值并单击 Finish
图 5. 创建一个插件项目
展示了如何创建插件项目的屏幕截图

创建新的插件项目之后,Eclipse 会自动打开 MANIFEST.MF 文件,该文件就是我们即将添加 Codan 依赖项的文件。

在编辑器中,选择 Dependencies 选项卡,并将以下内容添加到 Required Plug-ins 列表中:

org.eclipse.cdt.codan.core
org.eclipse.cdt.codan.core.cxx
org.eclipse.cdt.codan.ui
org.eclipse.cdt.codan.ui.cxx
org.eclipse.cdt.core
org.eclipse.cdt.ui
org.eclipse.core.resources
org.eclipse.core.runtime
org.eclipse.ui

Eclipse 插件

我就不在本文中介绍 插件开发 101,第 1 部分:基本原理 的所有细节,因为 Eclipse 并不是我的讨论重点。请参阅 参考资料,以获取编写 Eclipse 插件的介绍。

步骤 2. 创建一个错误分析器

我们将需要使用一个错误分析器从 Cppcheck 输出中创建编辑器标记,因此,我们的下一步是使用一个插件来扩展 Eclipse 的 C/C++ 工具。为此,我们将使用 Java 代码,因为 Eclipse 本身就是一个 Java 应用程序。

首先,我们将创建一个类 CppcheckErrorParser,该类实现了 org.eclipse.cdt.core.IErrorParser。我们从寻找报告代码问题时 Cppcheck 所用的模式开始。错误分析器会使用这个模式来识别代表错误报告的一行输出,然后从输出中提取所需的信息来创建一个编辑器标记。

清单 1. 与 Cppcheck 输出匹配的模式
  // sample line to parse:
  //
  // [/src/HelloWorld.cpp:19]: (style) The scope of the variable 'i' can be reduced
  // ----------1--------- -2    --3--  ------------------4-------------------------
  //
  // groups:
  // 1: file path and name
  // 2: line where problem was found
  // 3: problem severity
  // 4: problem description
  private static Pattern pattern = 
      Pattern.compile("\\[(.*):(\\d+)\\]:\\s*\\((.*)\\)\\s*(.*)");

清单 2 显示了错误分析器如何使用该模式来提取将要检查的文件的路径和名称,以及位置、描述和错误严重程度。有了这些信息,错误分析器就可以创建一个新的 ProblemMarkerInfo,并将它传递给给定的 ErrorParserManagerErrorParserManager 是负责创建编辑器标记的类。

清单 2. 处理 Cppcheck 的输出
  @Override
  public boolean processLine(String line, ErrorParserManager parserManager) {
    Matcher matcher = pattern.matcher(line);
    if (!matcher.matches()) {
      return false;
    }
    IFile fileName = parserManager.findFileName(matcher.group(1));
    if (fileName != null) {
      int lineNumber = Integer.parseInt(matcher.group(2));
      String description = matcher.group(4);
      int severity = findSeverityCode(matcher.group(3));
      ProblemMarkerInfo info = 
          new ProblemMarkerInfo(fileName, lineNumber, description, severity, null);
      parserManager.addProblemMarker(info);
      return true;
    }
    return false;
  }
}

映射问题严重程度

Cppcheck 定义了自己的问题的严重程度,与编辑器标记使用的不一样。例如,Cppcheck 严重程度 “style” 在 Eclipse 中并没有与之相对应的项。为了解决这个问题,我们需要在这两类问题严重程度之间创建一个映射。方法 findSeverityCode(如 清单 3 所示)演示了一个实现该映射的简单方法:

清单 3. 映射问题严重程度
  private static Map<String, Integer> SEVERITY_MAPPING = new HashMap<String, Integer>();
  
  static {
    SEVERITY_MAPPING.put("error", IMarkerGenerator.SEVERITY_ERROR_RESOURCE);
    SEVERITY_MAPPING.put("warning", IMarkerGenerator.SEVERITY_WARNING);
    SEVERITY_MAPPING.put("style", IMarkerGenerator.SEVERITY_INFO);
  }
  
  private int findSeverityCode(String text) {
    Integer code = SEVERITY_MAPPING.get(text);
    if (code != null) {
      return code;
    }
    return IMarkerGenerator.SEVERITY_INFO;
  }

创建完映射之后,Cppcheck 报告的严重程度为 “style” 的问题在 Eclipse 中将使用严重程度 SEVERITY_INFO 显示。该映射仅定义了问题严重程度的默认值。稍后您会看到如何从 Codan Preferences 页面配置该映射。

对于 Codan 认可的 CppcheckErrorParser,需要在 plugin.xml 文件中注册,并使用扩展点 org.eclipse.cdt.core.ErrorParser

清单 4. 注册错误分析器
  <extension id="com.developerworks.cdt.checkers" name="Cppcheck error parsers" 
      point="org.eclipse.cdt.core.ErrorParser">
    <errorparser class="cppcheckchecker.CppcheckErrorParser" 
        id="com.dw.cdt.checkers.CppcheckErrorParser"
        name="Cppcheck">
      <context type="codan" />
    </errorparser>
  </extension>

注意,在 清单 4 中,创建 ErrorParser 扩展点最初是为了注册用于 CDT 构建工具的分析器。该扩展点并不是特定于 Codan 的。为了表明 CppcheckErrorParser 仅与 Codan 同时使用,我们添加了上下文 “codan”。

步骤 3. 创建代码检查器

AbstractExternalToolBasedChecker 是任何基于外部工具的 Codan 代码检查器的超类。它提供了调用外部代码分析工具的大部分基础架构。由于我们集成了 Cppcheck,所以将调用 CppcheckChecker 这个类。

我们所要做的第一件事就是为该信息指定默认值,此消息与外部工具有关,会显示在 Codan Preferences 页面中。

应该将该信息传递给检查器构造器,此信息包含以下内容:

  • 外部代码分析工具名称,在本例中是 Cppcheck。
  • 工具的可执行文件名,在这里是 cppcheck。我们不需要为可执行文件指定一个路径,因为我们假设它位于系统的 PATH 中。
  • 传递到可执行文件的参数包含在单个 String 中。我们指定了 “--enable=all”,以支持所有 Cppcheck 检查。
清单 5. 默认 Cppcheck 信息
  public CppCheckChecker() {
    super(new ConfigurationSettings("Cppcheck", new File("cppcheck"), "--enable=all"));
  }

请注意,Codan Preferences 页面允许我们修改可执行路径和传递参数。

将问题严重程度映射到问题 ID

接下来,指定我们将要使用的错误分析器的 ID,如清单 6 所示。ID 必须与 plugin.xml 文件中所用的 ID 相同。

清单 6. 指定将要使用的错误分析器的 ID
  @Override
  protected String[] getParserIDs() {
    return new String[] { "com.dw.cdt.checkers.CppcheckErrorParser" };
  }

回到 清单 2,我们创建了一个 ProblemMarkerInfo,并将它传递给给定 ErrorParserManager 来创建编辑器标记。ErrorParserManager 将编辑器标记创建委托给我们最新创建的检查器。

为了让检查器创建一个编辑器标记,我们需要重写方法 addMarker(ProblemMarkerInfo)(它的工作是发现另一种类型的不匹配)。Codan 检查程序不能直接从 ProblemMarkerInfo 创建编辑器标记。它们有自己的机制,使用问题 ID 描述已创建的编辑器标记的相应严重程度。

问题 ID 是一个独一无二的 ID,Codan 用它来识别编辑器检查器报告的代码问题。所有代码问题都在 Codan Preferences 页面显示(参见 图 2)。

清单 7. 创建错误标记
  @Override
  public void addMarker(ProblemMarkerInfo info) {
    String problemId = PROBLEM_IDS.get(info.severity);
    String description = String.format("[cppcheck] %s", info.description);
    reportProblem(problemId, createProblemLocation(info), description);
  }

要找到符合 ProblemMarkerInfo 严重程度的问题 ID,需要在严重程度和问题 ID 之间创建一个映射。清单 8 显示了该映射是如何实现的:

清单 8. 将问题严重程度映射到问题 ID
  private static final String ERROR_PROBLEM_ID = 
      "com.dw.cdt.checkers.cppcheck.error";

  private static final Map<Integer, String> PROBLEM_IDS = 
      new HashMap<Integer, String>();

  static {
    PROBLEM_IDS.put(
        IMarkerGenerator.SEVERITY_ERROR_RESOURCE, ERROR_PROBLEM_ID);
    PROBLEM_IDS.put(
        IMarkerGenerator.SEVERITY_WARNING, "com.dw.cdt.checkers.cppcheck.warning");
    PROBLEM_IDS.put(
        IMarkerGenerator.SEVERITY_INFO, "com.dw.cdt.checkers.cppcheck.style");
  }

使用外部代码分析工具的代码检查器需要指出使用哪个问题 ID 作为 “首选”。首选问题 ID 被用来获取检查器的首选值(例如,外部工具名,如 清单 5 所示)。哪个问题 ID 是首选无关要紧,因为所有问题都将共享该首选。

清单 9. 指定首选问题 ID
  @Override
  protected String getReferenceProblemId() {
    return ERROR_PROBLEM_ID;
  }

常量 ERROR_PROBLEM_ID 是在 清单 8 中定义的。

注册检查器

为了将代码检查器和它报告的所有问题都显示在 Codan Preferences 页面上(从而可以让用户访问它),我们需要向 Codan 的 plugin.xml 文件注册该检查器。

因为我们不知道 Cppcheck 报告的所有问题,我们也不能阻止 Cppcheck 在未来的版本中添加或删除代码检查,所以我们不能注册每一个问题。相反,我们可以通过严重程度对问题进行分组,然后将每一组作为一个问题进行处理。在 清单 10 中,我们将注册 errorswarningsstyle violations 作为 3 个独立问题:

清单 10. 注册检查器和问题报告
  <extension point="org.eclipse.cdt.codan.core.checkers">
    <category id="cppcheckChecker.category" name="Cppcheck" />
    <checker class="cppcheckchecker.CppcheckChecker" id="cppcheckChecker.cppChecker"
      name="CppcheckChecker">
      <problem id="com.dw.cdt.checkers.cppcheck.error" name="Error" 
        defaultEnabled="true" defaultSeverity="Error" messagePattern="{0}"
        category="cppcheckChecker.category"/>
      <problem id="com.dw.cdt.checkers.cppcheck.warning" name="Warning" 
        defaultEnabled="true" defaultSeverity="Warning" messagePattern="{0}"
        category="cppcheckChecker.category"/>
      <problem id="com.dw.cdt.checkers.cppcheck.style" name="Style" 
        defaultEnabled="true" defaultSeverity="Info" messagePattern="{0}"
        category="cppcheckChecker.category"/>
    </checker>
  </extension>

我们在 category 元素中指定了将向用户显示的检查器名称。问题 ID 应该与检查器中所用的 ID 相同(参见 清单 8)。

在 plugin.xml 文件中,关于所有这 3 类问题,我们进行了以下指定:

  • 都是默认启用的。
  • 分别具有默认严重程度 “Error”、“Warning” 和 “Info”。
  • 都具有以下消息模式:“{0},”,这表示强制 Codan 使用问题描述,因为它是由 Cppcheck 报告的。

在 Codan 中使用 Cppcheck

现在,在 Codan Preferences 页面上我们可以看到 CppcheckChecker,如 图 6 所示:

图 6. Codan Preferences 页面上显示的 Cppcheck
已在 Codan Preferences 页面上列出的 CppcheckChecker 的屏幕截图

图 7 显示配置 Cppcheck 应如何报告代码错误的选项:

图 7. 配置 Cppcheck 的错误报告
配置 Cppcheck 错误的选项

图 8 显示了 Cppcheck 如何报告代码问题。请注意,Cppcheck 是在保存文件后自动调用的。

图 8. 一个 Cppcheck 报告
报告代码问题的 Cppcheck 的屏幕截图

集成的缺点

集成 Codan 与外部代码分析工具的一个限制是:当用户输入时,基于外部工具的检查器不能运行。这是因为外部工具无法看到文件的未保存的更改。因此,外部检查器仅在打开文件并且保存该该文件的时候才能运行。

然而,这一限制超过了使用成熟代码分析工具所带来的优势。与创建一个正规检查器相比,集成外部工具和 Codan 更轻松、更简单,创建一个正规检查器需要深入了解 C 或 C++ 语言。相比之下,我们仅使用 100 行简单的 Java 代码(包含在 2 个类中)和 30 行 XML 就可以编写 CppcheckChecker

结束语

在发布 Eclipse Juno 之前,为 Codan 创建自定义代码检查需要对 C/C++ 语言和 CDT 的 AST 实现有一个很好的理解。Eclipse CDT 的 Juno 版本解决了让开发人员创建 Codan 代码检查器的问题,然后将这个重任委托给了外部代码分析工具。

在本文中,我们仅使用少许 Java 代码和 XML 就集成了 Cppcheck 和 Codan,将这个流行的 C/C++ 代码分析工具和 Eclipse 中用于 C/C++ 程序的内置代码分析框架结合在一起。正如我之前提到的,您可以将本文演示的流程用于您最喜爱的代码分析工具。请参阅 参考资料,了解有关的更多信息。

参考资料

学习

获得产品和技术

讨论

  • 加入 developerWorks 中文社区。浏览开发人员驱动的博客、论坛、组和维基,并与其他 developerWorks 用户进行交流。

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology, Open source, XML
ArticleID=839406
ArticleTitle=将外部代码检查器集成到 Eclipse CDT 中
publish-date=10082012