我们正在重构我们的代码库,并尝试限制不同组件之间的直接依赖关系。我们的源代码树有几个顶级目录:src / a,src / b和src / c。
我们希望强制执行一系列的撤销:
执行第一个很简单。我有一个这样的隐含规则:
build/a/%.o : src/a/%.cpp
$(CXX) -I src/a $(OTHER_FLAGS) -o $@ $<
如果某个文件试图包含b或c中的头文件,则构建将失败,因为找不到标题。
第二条规则有一条类似的规则,它将src / a和src / b指定为包含目录。问题出现在建筑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(允许)的文件,该文件又包括来自(也是允许的)的文件。我们想要阻止这样的代码:
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构造?
如果重要,我们正在使用GNU Make 3.81和g ++ 4.5.3,但如果可能的话,我希望能够移植。
更新
我们正在寻找违反规则的努力,而不是需要努力遵守规则的事情。 (过去的经验表明,后者不太可能奏效。)虽然在另一个答案中有一些好的想法,但我接受了写剧本的那个,因为那是最需要努力工作的。周围。
感谢大家的回答。
答案 0 :(得分:0)
是。但它需要一些手动的努力和纪律。
构建C时,您可以依赖src / b / * .h。
中的标题在项目B中,主目录中的任何头文件都应该是自包含的,并且不依赖于其他项目。您还需要B src / b / detail / * .h中的子目录。在这里,头文件允许包含src / a / * .h和src / b / *。h,但这是一个私有实现细节,仅适用于b项目的源文件。
答案 1 :(得分:0)
您可以将-I
选项设为特定目标:
build/b/%.o: CPPFLAGS += -Isrc/a
build/c/%.o: CPPFLAGS += -Isrc/b
这是特定于gnu-make的,因此它不可移植。
答案 2 :(得分:0)
最简单的方法是将所有内容的包含路径更改为-Isrc
。然后包含语句具有完整的相对路径
#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
这样只允许通过B(以及B&#39标头中的宏)进行C访问
build/c/%.o: CPPFLAGS += -DALLOW_A_INDIRECT
请注意,这需要一些纪律,特别是在B&B的标题中,但我想如果它与现有的包含警卫并列,那么......好吧,它实际上仍然非常讨厌。
答案 3 :(得分:0)
考虑到您在现有代码库中应用此功能,我会选择“验证脚本”方法。
因此,在构建失败时,不会一次修改构建过程和切断依赖关系,而是会显示一个不投诉的文件列表。然后,您可以重构具有“全局”的代码库,并且使用与以前相同的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 */*.cpp C_bad.o: c/C_bad.cpp a/a.h C.o: c/C.cpp b/b.h a/a.h [me@home]$ # This also works for header files [me@home]$ cpp -Ia -Ib -Ic -MM c/c_bad.h c_bad.o: c/c_bad.h a/a.h
解析这些输出以确定每个cpp文件的依赖关系并标记那些不合规的文件应该是相当直截了当的。
这种方法的缺点是它无法区分直接和间接依赖关系,因此如果这很重要,您可能需要包含一个额外的步骤来检查源并选择直接依赖关系。