使用make文件创建目录

Jab*_*bez 90 directory makefile object

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

+--Project  
   +--output  
   +--source  
     +Testfile.cpp  
   +Makefile  
Run Code Online (Sandbox Code Playgroud)

我想将所有对象和输出放入相应的输出文件夹中.我想创建文件夹结构,编译后就像这样.

+--Project
   +--output
     +--debug (or release)
       +--objs
         +Testfile.o
       +Testfile (my executable file)
   +--source
     +Testfile.cpp
   +Makefile
Run Code Online (Sandbox Code Playgroud)

我尝试了几种选择,但无法成功.请帮我用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)
Run Code Online (Sandbox Code Playgroud)

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

P S*_*ved 129

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

如果您的目标是通常的或"图案化"的文件,只需使用make内部变量$(@D),即"当前目标所在的目录"(cmp.$@目标为).例如,

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

title: $(OBJS)
Run Code Online (Sandbox Code Playgroud)

然后,你实际上是这样做的:为所有人创建目录$(OBJS),但你会以一种不那么复杂的方式来做.

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


注意:如果您打算使用它,引入便利变量并利用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)
Run Code Online (Sandbox Code Playgroud)

  • 虽然在我看来将目录需求链接到文件是一个更好的选择,但是你的解决方案也有一个明显的缺点,即make文件会为每个重建的文件调用mkdir进程,其中大部分都不需要目录再次.当适用于像Windows这样的非Linux构建系统时,它实际上会导致两个不可阻塞的错误输出,因为没有与mkdir命令等效的-p,更重要的是因为shell调用不是微创的,所以开销量很大. (12认同)
  • 我没有直接调用 mkdir,而是执行了以下操作以避免尝试创建已存在的目录: $(shell [ ! -d $(@D) ] &amp;&amp; mkdir -p $(@D)) (2认同)

Jon*_*ler 79

这样做 - 假设类似Unix的环境.

MKDIR_P = mkdir -p

.PHONY: directories

all: directories program

directories: ${OUT_DIR}

${OUT_DIR}:
        ${MKDIR_P} ${OUT_DIR}
Run Code Online (Sandbox Code Playgroud)

这必须在顶级目录中运行 - 或者$ {OUT_DIR}的定义必须相对于它运行的位置是正确的.当然,如果你遵循Peter Miller的" Recursive Make Considered Harmful "论文的规定,那么无论如何你都将在顶级目录中运行make.

我现在正在玩这个(RMCH).它需要对我作为测试场所使用的软件套件进行一些调整.该套件有十几个单独的程序,源代码分布在15个目录中,其中一些是共享的.但有点小心,可以做到.OTOH,它可能不适合新手.


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


Nic*_*kiy 21

或者,亲吻.

DIRS=build build/bins

... 

$(shell mkdir -p $(DIRS))
Run Code Online (Sandbox Code Playgroud)

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

  • 这是更好的:`$(info $(shell mkdir -p $(DIRS)))`没有`$(info ...)`,`mkdir`命令**的输出将被粘贴到Makefile**,最多导致语法错误.`$(info ...)`调用确保a)错误(如果有的话)对用户可见,以及b)函数调用扩展为空. (5认同)
  • 我喜欢这种方法,因为我不必用命令处理目录来混淆每个目标. (3认同)

cma*_*ter 8

makein和off本身处理与文件目标相同的目录目标.所以,编写这样的规则很容易:

outDir/someTarget: Makefile outDir
    touch outDir/someTarget

outDir:
    mkdir -p outDir
Run Code Online (Sandbox Code Playgroud)

唯一的问题是,目录时间戳取决于对内部文件所做的操作.对于上述规则,这会导致以下结果:

$ make
mkdir -p outDir
touch outDir/someTarget
$ make
touch outDir/someTarget
$ make
touch outDir/someTarget
$ make
touch outDir/someTarget
Run Code Online (Sandbox Code Playgroud)

这绝对不是你想要的.无论何时触摸该文件,您还可以触摸该目录.由于文件取决于目录,因此文件似乎已过期,迫使文件重建.

但是,您可以通过告诉make忽略目录的时间戳来轻松打破此循环.这是通过将目录声明为仅订单的预备点来完成的:

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

outDir:
    mkdir -p outDir
Run Code Online (Sandbox Code Playgroud)

这正确地产生:

$ make
mkdir -p outDir
touch outDir/someTarget
$ make
make: 'outDir/someTarget' is up to date.
Run Code Online (Sandbox Code Playgroud)

TL; DR:

编写规则来创建目录:

$(OUT_DIR):
    mkdir -p $(OUT_DIR)
Run Code Online (Sandbox Code Playgroud)

内部的东西的目标取决于目录顺序:

$(OUT_DIR)/someTarget: ... | $(OUT_DIR)
Run Code Online (Sandbox Code Playgroud)

  • @vbezhenar 只需看一下问题下的日期及其答案:我的答案不仅是最后发布的答案,它还比最近的答案年轻大约一岁,比评分最高的答案年轻了 9 岁接受的答案。我只是参加聚会迟到了。这可能是 SO 评分系统的主要缺点:快速且稍微正确的答案通常比迟到且更好的答案得分高得多。这是因为快速回答享有先机,但也因为高分答案的强化更有可能获得额外选票。 (3认同)
  • @JohanBoulé Debian。 (2认同)
  • 我不知道为什么这个答案没有得到足够的支持,因为我认为这是一个正确的答案,没有与其他答案相关的问题。谢谢。 (2认同)
  • @vbezhenar 顺便说一句,我发布这个答案的原因正是因为我对其他答案完全失望,而且我不想对我自己的 Makefile 做出妥协。因此,我深入研究了 make 文档并进行了一些测试来提出这个解决方案;我发布它是希望其他人能够免受我必须投入的努力,尽管我非常确定我的答案需要几十年的时间才能达到顶峰(如果有的话)。 (2认同)

ste*_*nct 6

包括已接受的解决方案在内的所有解决方案都存在一些问 在通过@乔纳森-莱弗勒接受的答案已经是相当不错,但并没有考虑到效果的先决条件不一定是为了建立(期间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)
Run Code Online (Sandbox Code Playgroud)