构建嵌入式C / C ++项目的系统

时间:2011-09-30 06:16:54

标签: c++ c build build-process build-system

我正在寻找一个高级构建系统/工具,可以帮助我将嵌入式C项目组织成“模块”和“组件”。请注意,这两个术语非常主观,所以我的定义如下。

  • 模块是c和h文件的紧密集合,但只有一个公共h文件可供其他模块看到。
  • 另一方面,组件(或层)是模块的集合(例如,应用层,库层,驱动器层,RTOS层等)。

构建系统/工具应该 -

  • 防止组件和模块之间的循环依赖关系(模块内的循环依赖关系是可以的)
  • 阻止访问模块的私有障碍。如果其他模块尝试包含模块专用的头文件,则构建系统必须抛出错误。但是,私有屏障内的文件必须能够在该屏障内包含其他文件。
  • 支持在主机上自动构建和执行单元测试(TDD的快速反馈循环)
  • 支持在目标模拟器上运行的单元测试
  • 支持代码静态分析
  • 支持代码生成
  • 支持代码重复检测(强制执行DRY原则)
  • 支持代码美化
  • 支持生成单元测试代码覆盖率指标
  • 支持生成代码质量指标
  • 独立于平台

我可以编写自己的构建工具并花费大量时间。然而,这不是我的专业领域,如果有人已经创造了这样的工具,我宁愿不重新发明轮子。

2 个答案:

答案 0 :(得分:4)

实现这一目标的传统方法是将每个模块的源代码放入一个单独的目录中。每个目录都可以包含模块的所有源文件和头文件。

每个模块的公共标头可以放在一个单独的公共头文件目录中。我可能会使用公共目录中的符号链接到每个标题的相关模块目录。

编译规则只是说明除了公共目录中的标题之外,任何模块都不能包含来自其他模块的标头。这实现了以下结果:没有模块可以包含来自另一个模块的头 - 除了公共头(因此强制执行私有障碍)。

自动防止循环依赖并非易事。问题是你只能通过一次查看几个源文件来确定存在循环依赖,并且编译器一次只查看一个。

考虑一对模块ModuleA和ModuleB,以及使用这两个模块的程序Program1。

base/include
        ModuleA.h
        ModuleB.h
base/ModuleA
        ModuleA.h
        ModuleA1.c
        ModuleA2.c
base/ModuleB
        ModuleB.h
        ModuleB1.c
        ModuleB2.c
base/Program1
        Program1.c

编译Program1.c时,如果它使用两个模块的服务,则包含ModuleA.h和ModuleB.h是完全合法的。因此,如果ModuleB.h包含在同一个翻译单元(TU)中,ModuleA.h就不会抱怨,如果ModuleA.h包含在同一个TU中,ModuleB.h也不会抱怨。

让我们假设ModuleA使用ModuleB的功能是合法的。因此,在编译ModuleA1.c或ModuleA2.c时,包含ModuleA.h和ModuleB.h都没有问题。

但是,为了防止循环依赖,您必须能够禁止ModuleB1.c和ModuleB2.c中的代码使用ModuleA.h。

据我所知,唯一的方法是需要一个ModuleB的私有标题的技术,即“ModuleA已经包含”,即使它不是,并且在ModuleA.h之前包含它曾经包括在内。

ModuleA.h的框架将是标准格式(和ModuleB.h类似):

#ifndef MODULEA_H_INCLUDED
#define MODULEA_H_INCLUDED
...contents of ModuleA.h...
#endif

现在,如果ModuleB1.c中的代码包含:

#define MODULEA_H_INCLUDED
#include "ModuleB.h"
...if ModuleA.h is also included, it will declare nothing...
...so anything that depends on its contents will fail to compile...

这远不是自动的。

您可以对包含的文件进行分析,并要求存在无循环拓扑类型的依赖项。 UNIX系统上曾经有一个程序tsort(和一个配套程序lorder),它们共同提供了所需的服务,因此可以创建一个包含静态(.a)库的库。对象文件的顺序不需要重新扫描存档。 ranlib程序,最终arld承担了管理单个库重新扫描的职责,从而使lorder特别多余。但是tsort有更多的一般用途;它可以在某些系统上使用(例如MacOS X;也可以是RHEL 5 Linux)。

因此,使用GCC加上tsort的依赖关系跟踪,您应该能够检查模块之间是否存在循环。但这必须要小心处理。

可能会有一些IDE或其他工具集自动处理这些内容。但通常程序员可以足够规范以避免出现问题 - 只要仔细记录需求和模块间依赖关系。

答案 1 :(得分:2)

对于一般解决方案,我完全建议采用Jonathan Leffler的解决方案。但是,如果您绝对需要自动化测试,无论您的模块是独立的还是独立的,您都可以尝试使用Debian的构建系统。

将每个模块打包成Debian软件包(当它已经自动配置时非常快速地完成),正确地声明Build-Depends并在pbuilder环境中构建软件包。这确保了只有每个模块的公共头文件可用(因为只有那些是由pbuilder安装的.deb软件包来构建其他软件包),并且有很好的工具可以查看Debian软件包树并确保它们是循环的 - 免费。

然而,这可能是过度杀戮。只是说明它的完整性和案例,你肯定需要一个自动化解决方案。