makefile是否可以在C++中强制执行依赖项限制
Can a makefile enforce dependency restrictions in C++
我们正在重构我们的代码库,并试图限制不同组件之间的直接依赖关系。我们的源代码树有几个顶级目录:src/a、src/b和src/c。
我们想实施一系列限制:
- a中的文件不能依赖于b或c中的文件
- b中的文件可以依赖于文件a,但不能依赖于文件c
- c中的文件可以直接依赖于b中的文件,但不能依赖于a
强制执行第一个很简单。我有一个隐含的规则:
build/a/%.o : src/a/%.cpp
$(CXX) -I src/a $(OTHER_FLAGS) -o $@ $<
如果a下的文件试图包含b或c中的头文件,则生成将失败,因为找不到头。
第二个规则有一个类似的规则,将src/a和src/b指定为include目录。c号楼出现了问题。以下情况是允许的。
src/c/C.cpp
#include "b.h"
void C() { ... }
src/b/b.h
#include "a.h"
class B { ... };
src/a/a.h
class A { ... };
这里,来自c的文件包括来自b的文件(允许),而b又包括来自a的文件(也允许)。我们想防止这样的代码:
src/c/C_bad.cpp
// Direct inclusion of a
#include "a.h"
src/c/c_bad.h
// Direct inclusion of a
#include "a.h"
对于允许编译的情况,用于在src/c中构建文件的编译命令必须包括一个-Isrc/a,但这允许第二种情况也进行编译。
我怀疑我的问题的答案是编写一个脚本,该脚本查看编译器生成的依赖项,发现潜在的非法依赖项,然后查看源文件以确定这是否是直接依赖项。有没有一种合理的方法来结合编译器和/或makefile构造来实现这一点?
如果重要的话,我们使用GNUMake3.81和g++4.5.3,但如果可能的话,希望是可移植的。
更新
我们正在寻找一种需要努力才能违反规则的东西,而不是一种需要付出努力才能遵守规则的东西。(过去的经验表明,后者不太可能奏效。)虽然另一个答案中有一些好主意,但我接受了写剧本的说法,因为这是最需要努力解决的问题。
感谢大家的回答。
考虑到您在现有的代码库上应用这一点,我会选择"验证脚本"方法。
因此,当构建失败时,您不会修改构建过程并一次一个地切断依赖关系,而是会看到一个无投诉的文件列表。然后,您可以在考虑"大局"的情况下重构代码库,您所做的任何更改都将使用与以前相同的Makefile构建,从而简化测试和调试。
一旦重构,分析脚本就可以继续用作合规性检查器来验证未来的更新。
这种分析的一个可能的起点是使用makedepend
或cpp -MM
。例如,使用问题中列出的cpp/h文件:
[me@home]$find。。./b./b/b.h./a./a/a.h./c./c/c_bad.cpp./c/c.cpp./c/c_bad.h[me@home]$cpp-MM-Ia-Ib-Ic*/*.cppC_bad.o:C_bad.cpp a/a.hC.o:C.cpp b/b.h a/a.h[me@home]$#这也适用于头文件[me@home]$cpp-Ia-Ib-Ic-MM c/c_bad.hc_bad.o:c/c_bad.h a/a.h
解析这些输出以确定每个cpp文件的依赖关系并标记那些不符合要求的文件应该是相当直接的。
这种方法的缺点是无法区分直接依赖项和间接依赖项,因此,如果这很重要,您可能需要包括一个额外的步骤来检查源并找出直接依赖项。
您可以使-I
选项特定于目标:
build/b/%.o: CPPFLAGS += -Isrc/a
build/c/%.o: CPPFLAGS += -Isrc/b
不过,这是gnu-make特有的,所以它不是可移植的。
是。但这需要一些体力劳动和纪律。
当构建C时,您可以依赖src/b/*.h中的头文件。
在项目B中,主目录中的任何头文件都应该是自包含的,并且不依赖于其他项目。您还需要B src/B/detail/*.h中的一个子目录。在这里,头文件可以包括src/a/*.h和src/B/*.h,但这是一个私有的实现细节,仅可用于B项目的源文件。
最简单的方法是将所有内容的包含路径更改为-Isrc
。Include语句然后具有完整的相对路径
#include <a/a.h>
例如。这使得自动检查代码变得更加容易(也许是在提交挂钩中,而不是在makefile中)。
或者,可以对A和B标头中的宏做一些讨厌的事情:
// src/a/a.h
#ifndef SRC_A_H
#define SRC_A_H
#ifndef ALLOW_A
#error "you're not allowed to include A headers here"
#endif
//...
和
// src/b/b.h
#ifndef SRC_B_H
#define SRC_B_H
#ifdef ALLOW_A_INDIRECT
#define ALLOW_A
#endif
#include <a/a.h>
//...
#ifdef ALLOW_A_INDIRECT
#undef ALLOW_A
#endif
#endif // include guard
现在,这些制定规则将允许A和B构建好:
build/a/%.o: CPPFLAGS += -DALLOW_A
build/b/%.o: CPPFLAGS += -DALLOW_A
这将允许C仅通过B(以及B报头中的宏)进行访问
build/c/%.o: CPPFLAGS += -DALLOW_A_INDIRECT
注意,这需要一些纪律,尤其是在B的头球中,但我想如果它与现有的包括后卫放在一起,它。。。好吧,其实还是挺恶心的。
- 如何对模板类型强制执行常量
- C++:我可以在模板参数包中强制执行至少1个agment吗
- 编译器未强制执行返回协定
- 如何在 Linux 上强制执行矢量下标超出范围的调试断言
- 如何使用C++模板强制执行正式协议?
- 为什么在与静态库链接时强制执行 order(例如 source.cxx -lstatic)
- 当函数在 GCC 中没有任何返回时,如何强制执行错误?
- 强制执行执行顺序
- 替换decorator模式以强制执行创建顺序
- 我可以强制执行标量类型通过 int{} 初始化为零吗?
- 如何在C++17中强制执行模板和模板模板参数之间的约束
- 如何在Boost::DateTime中强制执行严格的解析
- 强制执行C++语句顺序
- makefile是否可以在C++中强制执行依赖项限制
- 强制执行此模板的更好方法
- 为非专用模板实例化强制执行编译错误
- 在编译时强制执行静态存储
- 在代码(C/C++)中强制执行自定义规则
- InterlockedCompareExchange - 确切的对齐要求是什么以及如何强制执行
- 使用无符号数据类型强制执行非负值和/或有效值是否是一种最佳做法