使用make文件创建目录

时间:2009-12-23 05:58:12

标签: makefile object directory

我是makefile的新手,我想使用makefile创建目录。我的项目目录就像这样

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

我想将所有对象和输出放入相应的输出文件夹中。我想在编译后创建文件夹结构。

+--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)

提前致谢。如果我犯了任何错误,请指导我。

9 个答案:

答案 0 :(得分:122)

在我看来,无论是技术还是设计,目录都不应被视为makefile的目标。您应该创建文件,如果文件创建需要新目录,那么悄悄地在规则中为相关文件创建目录。

如果您定位的是常规或“图案化”文件,只需使用make的内部变量$(@D),即“当前目标所在的目录”(cmp。with {{ 1}}为目标)。例如,

$@

然后,你实际上是这样做的:为所有$(OUT_O_DIR)/%.o: %.cpp @mkdir -p $(@D) @$(CC) -c $< -o $@ title: $(OBJS) 创建目录,但你会以一种不那么复杂的方式完成。

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


注意:如果您打算使用它,引入便利变量并使用git的扩展规则可能会很有用。

make

答案 1 :(得分:73)

这样做 - 假设类似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)。它需要对我作为测试场所使用的软件套件进行一些调整。该套件有十几个独立的程序,源代码分布在15个目录中,其中一些是共享的。但有点小心,可以做到。 OTOH,它可能不适合新手。


如评论中所述,列出'mkdir'命令作为'目录'的操作是错误的。正如评论中所指出的,还有其他方法可以解决导致“不知道如何输出/调试”错误的问题。一种是删除对'目录'行的依赖。这是因为'mkdir -p'如果要求创建的所有目录都已存在,则不会生成错误。另一种是显示的机制,如果它不存在,它将只尝试创建目录。 “修改后的”版本是我昨晚想到的 - 但这两种技术都有效(如果输出/调试存在但是文件而不是目录,则两者都有问题。)

答案 2 :(得分:18)

或者,KISS。

DIRS=build build/bins

... 

$(shell mkdir -p $(DIRS))

这将在解析Makefile后创建所有目录。

答案 3 :(得分:6)

包括已接受的解决方案在内的所有解决方案都存在各自评论中所述的一些问题。 accepted answer by @jonathan-leffler已经非常好了,但没有实现先决条件不一定按顺序构建(例如在make -j期间)。但是,只需将directories先决条件从all移至program,即可在每次运行AFAICT时引发重建。 以下解决方案没有该问题,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)

答案 4 :(得分:6)

"scripts": [ "node_modules/bootstrap/dist/js/bootstrap.bundle.min.js" ] 本身(包括内部和外部)处理的目录目标与文件目标相同。因此,编写这样的规则很容易:

make

唯一的问题是目录时间戳取决于对内部文件的处理方式。对于上述规则,这将导致以下结果:

outDir/someTarget: Makefile outDir
    touch outDir/someTarget

outDir:
    mkdir -p outDir

这绝对不是您想要的。每当您触摸文件时,您也会触摸目录。而且由于该文件取决于目录,因此该文件似乎已过时,因此不得不对其进行重建。

但是,您可以通过告诉make忽略目录的时间戳来轻松地打破此循环。这是通过将目录声明为仅订购的先决条件来完成的:

$ make
mkdir -p outDir
touch outDir/someTarget
$ make
touch outDir/someTarget
$ make
touch outDir/someTarget
$ make
touch outDir/someTarget

这正确地产生:

# The pipe symbol tells make that the following prerequisites are order-only
#                           |
#                           v
outDir/someTarget: Makefile | outDir
    touch outDir/someTarget

outDir:
    mkdir -p outDir

TL; DR:

编写规则以创建目录:

$ make
mkdir -p outDir
touch outDir/someTarget
$ make
make: 'outDir/someTarget' is up to date.

并使内部对象的目标取决于仅订购目录:

$(OUT_DIR):
    mkdir -p $(OUT_DIR)

答案 5 :(得分:4)

我刚刚提出了一个相当合理的解决方案,它允许您定义要构建的文件并自动创建目录。首先,定义一个变量ALL_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,它接受​​一个文件名,并生成一个规则,使该文件依赖于包含它的目录,然后定义一个规则,以便在必要时创建该目录。然后我在每个文件名上使用foreachcall这个函数,并eval结果。

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

答案 6 :(得分:3)

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

说,构建在与源目录不同的目录中的一种方法是VPATH;我更喜欢pattern rules

答案 7 :(得分:3)

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

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/a.o obj/three/b.o) -> 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 > $@

答案 8 :(得分:0)

请参见https://www.oreilly.com/library/view/managing-projects-with/0596006101/ch12.html

REQUIRED_DIRS = ...
_MKDIRS := $(shell for d in $(REQUIRED_DIRS); \
             do                               \
               [[ -d $$d ]] || mkdir -p $$d;  \
             done)

$(objects) : $(sources)

在使用Ubuntu时,我还需要将其添加到我的Makefile的顶部:

SHELL := /bin/bash # Use bash syntax