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

the*_*oid 8 c c++ build-process build build-system

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

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

构建系统/工具应该 -

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

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

Jon*_*ler 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
Run Code Online (Sandbox Code Playgroud)

编译 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
Run Code Online (Sandbox Code Playgroud)

现在,如果 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...
Run Code Online (Sandbox Code Playgroud)

这远非自动的。

您可以对包含的文件进行分析,并要求存在依赖关系的无循环拓扑排序。UNIX 系统上曾经有一个程序tsort(以及一个配套程序,lorder),它们一起提供所需的服务,以便.a可以创建一个静态 ( ) 库,该库以不需要重新扫描存档的顺序包含目标文件。该ranlib程序最终ar承担ld了管理单个库重新扫描的职责,从而变得lorder特别多余。但tsort有更普遍的用途;它在某些系统上可用(例如 MacOS X;RHEL 5 Linux)。

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

可能有一些 IDE 或其他工具集可以自动处理这些事情。但通常情况下,只要仔细记录需求和模块间依赖关系,程序员就可以遵守足够的纪律来避免出现问题。