5

对于我的面向对象编程课程,我必须完成一个最终项目(学术目的)。我想以“正确的方式”制作一个项目(即:makefile、模块化、DRY、易于扩展等),以便更好地理解类、makefile 和 C++。

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

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

SUBDIRS=eda
.PHONY: subdirs $(SUBDIRS)
$(SUBDIRS):
    $(MAKE) -C $@

我发现的问题是我对“eda”文件夹的先决条件是“古怪的”

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

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

所以我想问你们一些建议:我怎样才能实现我的目标并将每个重要的模块放在一个单独的文件夹中?递归是最好的解决方案吗?也许我应该潜入“automake”?或者最好将所有对象文件放到根目录上的新“对象”子文件夹中,然后将它们链接在一起?

顺便说一句,我通过嗅探Amarok源代码得到了这个树结构的灵感:它有一个名为“src”的子文件夹,当你进入那里时,你可以看到很多子文件夹:均衡器,播放列表,动态、statusbar、core、playlistgenerator、playlistmanager等。而且很多子文件夹都有自己的子目录……结果就是一个不可思议的音乐播放器。如果这种方法对 Amarok 团队有效……我可以做类似的事情!

欢迎任何意见,反馈,建议和其他,在此先感谢!


编辑#1

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

这就是为什么我想要一些反馈:¿有更好的选择吗?或者在我的项目中使用 make recursive 的优势压倒了其他替代方案?

无论如何,如果让您更好地理解,这是我的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
4

1 回答 1

1

“Recursive Make Considered Harmful”当然是一篇值得阅读和理解的论文。之后,您选择的工具应该真正适合您的特定项目。

对于您发起的小型项目(或您对指导高层决策具有影响力的项目),我建议您花一些时间确定您的偏好(项目布局、目录结构、单元测试框架等)并编写一个通用的您将用于所有项目的一组生成文件。你可以很容易地得到一个通用的主生成文件,可能还有一些更通用的包含用于模块化的生成文件(例如构建库,或单元测试或自动依赖检测)。您还可以通过可选的包含配置生成文件(例如指定库的顺序)提供一些额外的灵活性。大多数 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(and lib.mk) 将包含一些样板代码来迭代程序列表(和库列表),并将分解生成文件的特定部分来构建程序(和库)。

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

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

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

于 2013-07-25T08:25:53.447 回答