关于在具有许多子目录的项目上使用make的反馈

Kar*_*arl 5 c++ tree recursion makefile

对于我的面向对象编程课程,我必须做一个最终的项目(学术目的).我想让一个proyect"正确的方式"(即:makefile,模块化,DRY,易于扩展等),以便更好地理解类,makefile和C++.

我的想法是拥有一个"tree-source-file-structure-directory",所以在每个子文件夹中我都有源文件,包含它的头文件,测试文件和单个makefile.因此,如果我想在界面上工作,我会转到子文件夹界面,我编辑文件,运行测试,如果一切正常,只需将对象链接在我的根目录上.同样的事情,如果我想处理我的数据结构,所以继续.好的功能是每个子文件夹都驻留在源代码和目标文件中,因此我的根目录中的链接器将搜索已在子文件夹上编译的目标文件并将它们链接在一起

我一直在网上搜索,我可以看到许多不同的解决方案: - 做递归,例如:

SUBDIRS=eda
.PHONY: subdirs $(SUBDIRS)
$(SUBDIRS):
    $(MAKE) -C $@
Run Code Online (Sandbox Code Playgroud)

我发现的问题是我在"eda"文件夹中的先决条件是"古怪"

- 使用自动变量 $(@ D),但我不太明白它是如何工作的 - 也许使用通配符函数,但我对这个选项有点困惑.

无论如何,对我来说最诱人的解决方案是第一个(使用make递归),但我发现很多评论说不建议使用make递归有趣的文章

所以我想问你们一些建议:我如何实现目标并将每个重要模块放在一个单独的文件夹中?是递归制作最佳解决方案吗?也许我应该潜入"automake"?或者将所有目标文件带到根目录下的新"对象"子文件夹然后将它们链接在一起会更好?

顺便说一句,我通过嗅探Amarok源代码来获取灵感来使用这个树结构:它有一个名为"src"的子文件夹,当你进入那里时,你可以看到很多子文件夹:均衡器,播放列表,动态,statusbar,core,playlistgenerator,playlistmanager等.许多子文件夹都有自己的子目录......结果是一个令人难以置信的音乐播放器.如果这种方法适用于Amarok团队......我可以做类似的事情!

欢迎任何评论,反馈,建议和其他人,提前感谢!


编辑#1

Beta,我有一些隐式规则(后缀)和链接器的目标,需要我的eda文件夹上的对象.此目标的每个其他先决条件都建立在当前文件夹上.我遇到的问题是,当我运行make来构建该目标时,它将我在"eda"文件夹上的先决条件名称作为使用隐式规则构建的目标.这是makefile在我的proyect中递归的棘手/不洁的部分:我想我必须为每个必须在子文件夹中搜索的目标文件创建一个特殊的隐式规则.

这就是我想要一些反馈的原因:¿有更好的选择吗?或者在我的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
Run Code Online (Sandbox Code Playgroud)

Com*_*czy 1

《递归使人有害》无疑是一篇值得阅读和理解的论文。然后,您选择的工具应该真正适合您的特定项目。

对于您发起的小型项目(或者您有影响力指导高层决策的项目),我建议花一些时间确定您的偏好(项目布局、目录结构、单元测试框架等)并编写一个通用的项目您将用于所有项目的一组 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)
Run Code Online (Sandbox Code Playgroud)

包含的program.mk( 和lib.mk) 将包含一些样板代码来迭代程序列表(和库列表),并提取 makefile 的特定部分来构建程序(和库)。

为了帮助实现此类 makefile,您可以使用一些标准库,例如http://gmsl.sourceforge.net

这种方法有几个问题: * 它导致 makefile 需要强大的技能 * 它并不总是能够很好地扩展到非常大的项目 * 它严重依赖于“约定而不是配置”,并且需要对您所使用的约定有一个清晰的预先定义会使用(IMO,这很好,其他人可能会认为它缺乏灵活性)*生命太短,不能乱搞 makefile

否则,我建议使用更高级别的配置工具,例如 SCons 或 CMake,因为它们在概念上往往更简单,并且还允许使用其他风格的生成器。