【问题标题】:Feedback about using make on a project with many subdirectories关于在具有许多子目录的项目上使用 make 的反馈
【发布时间】:2013-06-20 01:02:11
【问题描述】:

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

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

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

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

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

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

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

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

顺便说一句,我通过嗅探Amarok源代码获得了这个树形结构的灵感:它有一个名为“src”的子文件夹,当你进入那里时,你可以看到很多子文件夹:均衡器,播放列表、动态、状态栏、核心、播放列表生成器、播放列表管理器等。许多子文件夹都有自己的子目录……结果是一个令人难以置信的音乐播放器。如果这种方法对 Amarok 团队有效……我可以做类似的事情!

欢迎任何 cmet、反馈、建议和其他人,提前谢谢!


编辑#1

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

这就是我想要一些反馈的原因:¿有更好的选择吗?还是在我的项目中使用 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

【问题讨论】:

  • 您能详细解释一下eda的先决条件吗?
  • 好的,我会编辑我的主帖!
  • 只是我的两分钱:使用“非递归制作”论文,您已经得到了正确的方法,至少在我看来……您可能已经知道 Android 使用(并且仍在使用?)这个方法。我会遵循evbergen.home.xs4all.nl/nonrecursive-make.html 中给出的细节,因为这是相当具体的。此外,我认为您需要一些“目标”来生成+运行每个子目录的测试,但我认为这并不复杂......
  • Ok Ingo,所以您告诉我您的方法将使用“非递归生成”以便将每个对象文件放在“对象”文件夹中,然后在需要时链接它们,对吧?我要找杯咖啡看你的文件,谢谢!顺便说一句,在我的帖子中,我写了我的“测试文件夹”的生成文件:它们是故意的几个“运行目标”,因为在这个“测试文件夹”中我使用了最小的 source-stable-files-已经测试过来测试我在互联网上找到的关于 Makefiles 和子文件夹的材料。
  • scons 也是一个不错的构建系统。每个目录脚本的清晰性(如果需要),没有递归 make 的缺点。

标签: c++ recursion tree makefile


【解决方案1】:

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

对于您发起的小型项目(或您对指导高层决策具有影响力的项目),我建议您花一些时间确定您的偏好(项目布局、目录结构、单元测试框架等),并且编写将用于所有项目的通用生成文件集。你可以很容易地得到一个通用的主 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,因为它们在概念上往往更简单,并且还允许使用其他风格的生成器。

【讨论】:

  • 感谢您的反馈,最后我选择学习CMake。我的动机是你写的最后一点:makefiles 的生命太短了,不能乱搞 我认为 makefiles 是有用的工具,但也非常复杂并且有很多技巧(看看 makefile 的手册) ;完全掌握 makefile 可能需要几个月甚至几年的时间,但您仍然会遇到递归 makefile 的问题。另一方面,CMake 非常容易、简单、可扩展并且没有这个问题。我的结论:makefile 用于非常简单的小项目,cmake 用于其余的
猜你喜欢
  • 1970-01-01
  • 2016-06-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多