使用make文件创build目录

我是一个非常新的makefile,我想使用makefile创build目录。 我的项目目录是这样的

+--Project +--output +--source +Testfile.cpp +Makefile 

我想把所有的对象和输出到各自的输出文件夹。 我想在编译后创build这样的文件夹结构。

 +--Project +--output +--debug (or release) +--objs +Testfile.o +Testfile (my executable file) +--source +Testfile.cpp +Makefile 

我尝试了几个选项,但不能成功。 请帮助我使用make文件制作目录。 我发布我的Makefile供您考虑。

 #--------------------------------------------------------------------- # Input dirs, names, files #--------------------------------------------------------------------- OUTPUT_ROOT := output/ TITLE_NAME := TestProj ifdef DEBUG TITLE_NAME += _DEBUG else ifdef RELEASE TITLE_NAME += _RELEASE endif endif # Include all the source files here with the directory tree SOURCES := \ source/TestFile.cpp \ #--------------------------------------------------------------------- # configs #--------------------------------------------------------------------- ifdef DEBUG OUT_DIR := $(OUTPUT_ROOT)debug CC_FLAGS := -c -Wall else ifdef RELEASE OUT_DIR := $(OUTPUT_ROOT)release CC_FLAGS := -c -Wall else $(error no build type defined) endif endif # Put objects in the output directory. OUT_O_DIR := $(OUT_DIR)/objs #--------------------------------------------------------------------- # settings #--------------------------------------------------------------------- OBJS = $(SOURCES:.cpp=.o) DIRS = $(subst /,/,$(sort $(dir $(OBJS)))) DIR_TARGET = $(OUT_DIR) OUTPUT_TARGET = $(OUT_DIR)/$(TITLE_NAME) CC_FLAGS += LCF_FLAGS := LD_FLAGS := #--------------------------------------------------------------------- # executables #--------------------------------------------------------------------- MD := mkdir RM := rm CC := g++ #--------------------------------------------------------------------- # rules #--------------------------------------------------------------------- .PHONY: all clean title all: title clean: $(RM) -rf $(OUT_DIR) $(DIR_TARGET): $(MD) -p $(DIRS) .cpp.o: @$(CC) -c $< -o $@ $(OBJS): $(OUT_O_DIR)/%.o: %.cpp @$(CC) -c $< -o $@ title: $(DIR_TARGET) $(OBJS) 

提前致谢。 请指导我,如果我犯了什么错误也。

这将做到这一点 – 假设一个类似Unix的环境。

 MKDIR_P = mkdir -p .PHONY: directories all: directories program directories: ${OUT_DIR} ${OUT_DIR}: ${MKDIR_P} ${OUT_DIR} 

这必须在顶层目录中运行 – 否则$ {OUT_DIR}的定义必须相对于其运行的位置是正确的。 当然,如果你按照Peter Miller的“ Recursive Make Considered Harmful ”这篇文章的话来说,那么你将会在顶层目录中运行make。

我正在玩这个(RMCH)。 它需要一些适应我用作testing场的软件套件。 该套件有十几个独立的程序,源码遍布15个目录,其中一些共享。 但是有一点小心,可以做到。 OTOH,可能不适合新手。


正如在评论中指出的那样,将“mkdir”命令列为“目录”的操作是错误的。 正如评论中所指出的,还有其他方法可以解决导致“不知道如何进行输出/debugging”的错误。 一个是删除对“目录”行的依赖。 这是有效的,因为如果所有被要求创build的目录已经存在,那么'mkdir -p'不会产生错误。 另一个是显示的机制,它只会试图创build目录,如果它不存在。 “修正”的版本是我昨天晚上想到的 – 但是这两种技术都可以工作(如果输出/debugging存在但是文件而不是目录都有问题)。

在我看来,目录不应该被认为是你的makefile的目标,无论是在技术上还是在devise意义上。 你应该创build文件 ,如果文件创build需要一个新的目录,然后静静地创build在相关文件的规则内的目录。

如果你的目标是一个通常的或“模式化”的文件,只需使用make的内部variables$(@D) ,这意味着“当前目标所在的目录”(cmp。with $@作为目标)。 例如,

 $(OUT_O_DIR)/%.o: %.cpp @mkdir -p $(@D) @$(CC) -c $< -o $@ title: $(OBJS) 

然后,你实际上也是这么做的:为所有$(OBJS)创build目录,但是你可以用更简单的方式来创build目录。

相同的策略(文件是目标,永远不会有目录)在各种应用程序中使用。 例如, git版本控制系统不存储目录。


注意:如果你打算使用它,引入一个便利variables并利用make的扩展规则可能是有用的。

 dir_guard=@mkdir -p $(@D) $(OUT_O_DIR)/%.o: %.cpp $(dir_guard) @$(CC) -c $< -o $@ $(OUT_O_DIR_DEBUG)/%.o: %.cpp $(dir_guard) @$(CC) -g -c $< -o $@ title: $(OBJS) 

我刚刚提出了一个相当合理的解决scheme,可以让您定义要build立的文件,并自动创build目录。 首先,定义一个variablesALL_TARGET_FILES ,其中包含makefile将生成的每个文件的文件名。 然后使用下面的代码:

 define depend_on_dir $(1): | $(dir $(1)) ifndef $(dir $(1))_DIRECTORY_RULE_IS_DEFINED $(dir $(1)): mkdir -p $$@ $(dir $(1))_DIRECTORY_RULE_IS_DEFINED := 1 endif endef $(foreach file,$(ALL_TARGET_FILES),$(eval $(call depend_on_dir,$(file)))) 

这是如何工作的。 我定义了一个函数depend_on_dir ,它取得一个文件名并生成一个规则,使文件依赖于包含它的目录,然后定义一个规则来创build该目录(如果有必要的话)。 然后我使用foreach在每个文件名称上call这个函数并且eval结果。

请注意,您需要一个支持eval的GNU make版本,我认为它是版本3.81和更高版本。

鉴于你是一个新手,我会说不要尝试这样做呢。 这绝对是可能的,但会不必要地使你的Makefile复杂化。 坚持简单的方法,直到你更舒适的制作。

也就是说,build立一个与源目录不同的目录的方法是VPATH ; 我更喜欢花样规则

所有的解决scheme,包括接受的解决scheme,都有其各自评论中所述的一些问 @ jonathan-leffler所接受的答案已经相当不错了,但并没有实现先决条件不一定要按顺序构build(例如make -j )。 然而,简单地将directories先决条件从all program移动到program都会在每次运行AFAICT时引发重build。 以下解决scheme没有这个问题,AFAICS按预期工作。

 MKDIR_P := mkdir -p OUT_DIR := build .PHONY: directories all clean all: $(OUT_DIR)/program directories: $(OUT_DIR) $(OUT_DIR): ${MKDIR_P} $(OUT_DIR) $(OUT_DIR)/program: | directories touch $(OUT_DIR)/program clean: rm -rf $(OUT_DIR) 

操作系统的独立性对我来说很重要,所以mkdir -p不是一个选项。 我创build了这个系列的函数,使用eval来创build具有父目录先决条件的目录目标。 这样做的好处是make -j 2可以正常工作,因为依赖关系是正确的。

 # convenience function for getting parent directory, will eventually return ./ # $(call get_parent_dir,somewhere/on/earth/) -> somewhere/on/ get_parent_dir=$(dir $(patsubst %/,%,$1)) # function to create directory targets. # All directories have order-only-prerequisites on their parent directories # https://www.gnu.org/software/make/manual/html_node/Prerequisite-Types.html#Prerequisite-Types TARGET_DIRS:= define make_dirs_recursively TARGET_DIRS+=$1 $1: | $(if $(subst ./,,$(call get_parent_dir,$1)),$(call get_parent_dir,$1)) mkdir $1 endef # function to recursively get all directories # $(call get_all_dirs,things/and/places/) -> things/ things/and/ things/and/places/ # $(call get_all_dirs,things/and/places) -> things/ things/and/ get_all_dirs=$(if $(subst ./,,$(dir $1)),$(call get_all_dirs,$(call get_parent_dir,$1)) $1) # function to turn all targets into directories # $(call get_all_target_dirs,obj/ao obj/three/bo) -> obj/ obj/three/ get_all_target_dirs=$(sort $(foreach target,$1,$(call get_all_dirs,$(dir $(target))))) # create target dirs create_dirs=$(foreach dirname,$(call get_all_target_dirs,$1),$(eval $(call make_dirs_recursively,$(dirname)))) TARGETS := w/h/a/t/e/v/e/r/things.dat w/h/a/t/things.dat all: $(TARGETS) # this must be placed after your .DEFAULT_GOAL, or you can manually state what it is # https://www.gnu.org/software/make/manual/html_node/Special-Variables.html $(call create_dirs,$(TARGETS)) # $(TARGET_DIRS) needs to be an order-only-prerequisite w/h/a/t/e/v/e/r/things.dat: w/h/a/t/things.dat | $(TARGET_DIRS) echo whatever happens > $@ w/h/a/t/things.dat: | $(TARGET_DIRS) echo whatever happens > $@ 

例如,运行上述将创build:

 $ make mkdir w/ mkdir w/h/ mkdir w/h/a/ mkdir w/h/a/t/ mkdir w/h/a/t/e/ mkdir w/h/a/t/e/v/ mkdir w/h/a/t/e/v/e/ mkdir w/h/a/t/e/v/e/r/ echo whatever happens > w/h/a/t/things.dat echo whatever happens > w/h/a/t/e/v/e/r/things.dat 

或者,KISS。

 DIRS=build build/bins ... $(shell mkdir -p $(DIRS)) 

这将在parsingMakefile后创build所有的目录。