我正在寻找可以帮助将嵌入式C项目组织为“模块”和“组件”的高级构建系统/工具。请注意,这两个术语具有很高的主观性,因此以下给出了我的定义。

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

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

  • 我可以编写自己的构建工具,并在其中花费大量时间。但是,这不是我的专业领域,如果有人已经创建了这样的工具,我就不想重新发明轮子。

    最佳答案

    实现该目标的常规方法是将每个模块的源代码放在单独的目录中。每个目录可以包含该模块的所有源文件和头文件。

    每个模块的公共(public)头可以放置在单独的头通用目录中。对于每个 header ,我可能会使用从公共(public)目录到相关模块目录的符号链接(symbolic link)。

    编译规则只是声明,除了公共(public)目录中的头文件外,没有模块可以包含其他模块的头文件。这样可以达到以下结果:除了公共(public) header (因此强制执行私有(private)屏障)之外,没有模块可以包含其他模块的 header 。

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

    考虑一对模块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也不能提示。

    让我们假设使用ModuleB的设施对ModuleA是合法的。因此,在编译ModuleA1.c或ModuleA2.c时,同时包含ModuleA.h和ModuleB.h不会有问题。

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

    据我所知,唯一的方法是某种技术,该技术要求ModuleB的私有(private) header 说“已经包含了ModuleA”,即使没有,并且在包含ModuleA.h之前就已经包含了此 header 。

    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或其他工具集可以自动处理这些问题。但是通常程序员可以受到足够的训练,避免出现问题-只要仔细记录了需求和模块间的依赖关系即可。

    关于c++ - 嵌入式C/C++项目的构建系统,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/7606604/

    10-10 16:10