到我的面向对象编程课程,我必须做一个最终的项目(学术目的)。我想让一个proyect“正确的方式”(即:makefile,模块化,DRY,易于扩展等),以便更好地理解类,makefile和C ++。
我的想法是拥有一个“tree-source-file-structure-directory”,所以在每个子文件夹中我都会得到包含它的头文件,测试文件和单个makefile的源文件。 因此,如果我想在界面上工作,我会转到子文件夹界面,我编辑文件,运行测试,如果一切正常,只需将对象链接在我的根目录上。同样的事情,如果我想处理我的数据结构,所以继续。好的功能是每个子文件夹都驻留在源代码和目标文件中,因此我的根目录中的链接器将搜索已在子文件夹上编译的目标文件并将它们链接在一起
我一直在互联网上搜索,我可以看到许多不同的解决方案: - 递归,例如:
SUBDIRS=eda
.PHONY: subdirs $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $@
我发现的问题是我在“eda”文件夹中的先决条件是“古怪”
- 使用自动变量 $(@ D),但我不太明白它是如何工作的 - 也许使用通配符函数,但我对这个选项有点困惑。
无论如何,对我来说最诱人的解决方案是第一个(使用make递归),但我发现很多评论说不建议使用 make 递归地Interesting article
所以我想问你们一些建议:我如何实现目标并将每个重要模块放在一个单独的文件夹中?是递归制作最佳解决方案吗?也许我应该潜入“automake”?或者将所有目标文件带到根目录下的新“对象”子文件夹然后将它们链接在一起会更好?
顺便说一下,我通过嗅探Amarok源代码来获取灵感,通过这个树结构来实现我的项目:它有一个名为“src”的子文件夹,当你进入那里时,你可以看到很多子文件夹:均衡器,播放列表,动态,状态栏,核心,播放列表,播放列表管理器等。许多子文件夹都有自己的子目录......结果是一个令人难以置信的音乐播放器。如果这种方法适用于Amarok团队......我可以做类似的事情!
欢迎任何评论,反馈,建议和其他人,提前感谢!
编辑#1
Beta ,我有一些隐式规则(后缀)和需要 eda 文件夹上的对象的链接器的目标。此目标的每个其他先决条件都建立在当前文件夹上。 我遇到的问题是,当我运行make来构建该目标时,它会将“eda”文件夹上的先决条件的名称作为使用隐式规则构建的目标。这是我的proyect中递归的makefile的棘手/不洁的部分:我想我必须为每个必须在子文件夹中搜索的目标文件创建一个特殊的隐式规则。
这就是我想要一些反馈的原因:¿有更好的选择吗?或者在我的proyects中使用make递归的优势压倒了其他替代方案?
无论如何,如果你能更好地理解,这是我的草稿Makefile (它是spnish-english:P)
#Makefile hecho para las pruebas de los archivos dentro de esta carpeta
FLAGS=-g -DDEBUG
OUT_TI=TIndividuo
OUT_TP=TProfesor
OUT_TA=TAula
.SUFFIXES: .cpp .c .h .o
.c.o: ; cc $(FLAGS) -c $*.c
.cc.o: ; gcc $(FLAGS) -c $*.cc
.cpp.o: ; g++ $(FLAGS) -c $*.cpp
SUBDIRS=eda
.PHONY: subdirs $(SUBDIRS)
$(OUT_TI): eda/TAula.o CandidatoHorario.o TIndividuo.o TIndividuoTest.o TGen.o
g++ CandidatoHorario.o TIndividuo.o TIndividuoTest.o TGen.o eda/TAula.o -o $@
CandidatoHorario.o: CandidatoHorario.cpp CandidatoHorario.h
TIndividuoTest.o: TIndividuoTest.cpp TIndividuo.h
TIndividuo.o: TIndividuo.cpp TIndividuo.h
TGen.o: TGen.cpp
#eda/TAula.o: eda/TAula.cpp eda/TAula.h
# g++ -c eda/TAula.cpp -o $@
$(SUBDIRS):
$(MAKE) -C $@
clean:
rm -f *.o $(OUT_TI) $(OUT_TA) eda/TAula.o
答案 0 :(得分:1)
“Recursive Make Considered Harmful”当然是一篇阅读和理解的论文。之后,您选择的工具应该根据您的具体项目量身定制。
对于您发起的小型项目(或者您有影响力来指导高层决策的地方),我建议花一些时间来确定您的偏好(项目布局,目录结构,单元测试框架等)和编写一组通用的makefile,用于所有项目。您可以轻松地使用通用主makefile,可能还有一些通用的包含makefile的模块化(例如构建库,或单元测试或自动依赖检测)。您还可以使用可选的包含配置makefile提供一些额外的灵活性(例如,指定库的顺序)。大多数DAG构造都会严重依赖项目目录的内容。示例可能如下所示:
include config.mk
sources := $(wildcard *.cpp)
programs := $(sources:%.cpp=%)
lib_sources := $(wildcard lib/*/*.cpp)
lib_dirs := $(sort $(patsubst %/, %, $(dir $(lib_sources:lib/%=%))))
lib_objects := $(lib_sources:lib/%.cpp=$(BUILD)/%.o)
all: $(programs:%=$(BUILD)/%)
.PRECIOUS: %/.sentinel %.d
# for dependencies on directories in build tree
%/.sentinel:
@mkdir -p $* && touch $@
clean:
$(RM) -r $(BUILD)
programs_aux:=$(programs)
include $(foreach program, $(programs), program.mk)
lib_dirs_aux:=$(lib_dirs)
include $(foreach lib_dir, $(lib_dirs), lib.mk)
# this should probably be in lib.mk
-include $(lib_objects:%.o=%.d)
包含的program.mk
(和lib.mk
)将包含一些样板代码,用于迭代程序列表(和库列表),并将生成文件的特定部分分解为构建程序(和图书馆)。
为了帮助实现这样的makefile,你可以使用像http://gmsl.sourceforge.net这样的标准库。
这种方法有几个问题: *它导致需要强大技能的makefile *对于非常大的项目,它并不总能很好地扩展 *它在很大程度上依赖于“约定而不是配置”,并且需要明确预先定义您将使用的约定(IMO这是好的,其他人可能会认为它缺乏灵活性) *生命太短暂,无法与makefile混乱
否则,我建议使用更高级别的配置工具,如SCons或CMake,因为它们在概念上更简单,并且还允许其他类型的生成器。