【问题标题】:makefile skips making the build directorymakefile 跳过构建目录
【发布时间】:2016-09-01 02:18:14
【问题描述】:

我希望我的所有构建文件都存储在项目根文件夹内的build 目录中。不过,如果我添加调试标志,我不想重建所有文件。因此,我有两个目录.build_release.build_debug。然后我创建一个从build 到正确目录的符号链接。

我希望所有这些都由make 处理。这是我的makefile:

## setup

ifdef DEBUG
BUILDDIR=.build_debug
else
BUILDDIR=.build_release
endif

BLACKLIST:=bayesP obsDataStats test3 test bayesPsamplesBR test2 tuneSp \
toyFeatures2Multi getCov getDist isConnected mergeClusters sample \
toyFeatures0 toyFeatures1 toyFeatures2 toyFeatures3 toyFeatures4 \
toyFeatures6 toyFeatures7 wnsFeatures0 wnsFeatures1 wnsFeatures2

## make code

PROGS:=$(shell find ./src/ -maxdepth 1 -name "*.cpp" -exec grep -l "int main" {} \;)
PROGS:=$(notdir $(basename $(PROGS)))
PROGS:=$(filter-out $(BLACKLIST),$(PROGS))

CPP_SRC:=$(wildcard src/*.cpp)
CPP_SRC:=$(notdir $(basename $(CPP_SRC)))
CPP_SRC:=$(filter-out $(PROGS) $(BLACKLIST),$(CPP_SRC))

PROGS:=$(PROGS:=.bin)
PROGS:=$(PROGS:%=$(BUILDDIR)/%)

CPP_SRC:=$(CPP_SRC:%=src/%.cpp)
CPP_OBJ:=$(CPP_SRC:src/%.cpp=$(BUILDDIR)/%.o)

LIB=$(BUILDDIR)/libspatialDecisionMaking.so

## test code

CPP_SRC_TEST:=$(wildcard src/test/*.cpp)
CPP_SRC_TEST:=$(notdir $(basename $(CPP_SRC_TEST)))
CPP_SRC_TEST:=$(filter-out $(BLACKLIST),$(CPP_SRC_TEST))

PROGS_TEST:=$(CPP_SRC_TEST:%=$(BUILDDIR)/test/%.bin)

CPP_OBJ_TEST:=$(CPP_SRC_TEST:%=$(BUILDDIR)/test/%.o)

CPP_SRC_TEST:=$(CPP_SRC_TEST:%=src/test/%)

## options

CC=g++-4.9

ifdef DEBUG
CPP_FLAGS=-std=c++11 -ggdb
else
CPP_FLAGS=-std=c++11 -O3
endif
LD_FLAGS=-Isrc -L$(BUILDDIR) -lgsl -larmadillo -fPIC -fopenmp

## rules

all: | $(BUILDDIR) $(LIB) $(PROGS) build

test: | $(BUILDDIR)/test $(LIB) $(PROGS_TEST) build

build: $(BUILDDIR)
    ln -rfs $(BUILDDIR) build

$(BUILDDIR)/test: $(BUILDDIR)
    mkdir $(BUILDDIR)/test

$(BUILDDIR):
    mkdir $(BUILDDIR)

$(BUILDDIR)/%.bin: src/%.cpp $(LIB)
    $(CC) $(CPP_FLAGS) -o $@ $< $(LD_FLAGS) -l$(LIB:$(BUILDDIR)/lib%.so=%)
    ln -rfs $@ $(@:%.bin=%)

$(LIB): $(CPP_OBJ)
    $(CC) $(CPP_FLAGS) -o $@ $^ $(LD_FLAGS) -shared

$(BUILDDIR)/%.o: src/%.cpp $(BUILDDIR)/%.d
    $(CC) $(CPP_FLAGS) -c $< -o $@ $(LD_FLAGS)

$(BUILDDIR)/%.d: src/%.cpp
    $(CC) $(CPP_FLAGS) -MM $< -MT $(@:%.d=%.o) > $@ $(LD_FLAGS)

%.cpp:

%.hpp:


# include dependencies
-include $(CPP_OBJ:%.o=%.d)

clean:
    rm -rf $(BUILDDIR)

不过,它似乎跳过了制作$(BUILDDIR)。我在每次运行 make 之前删除目录,它直接根据目标$(BUILDDIR)/%.d 的规则构建依赖生成文件。但是,它在尝试构建依赖项时自然会抱怨,因为 $(BUILDDIR) 不存在。

知道为什么它会跳过制作$(BUILDDIR) 的秘诀吗?

【问题讨论】:

  • 构建目录存在,并且根据为其列出的依赖项是最新的(无),因此make 正确地推断出它无事可做并且什么也不做。如果目录不存在,则创建目录;然后就完成了。我可能遗漏了一些东西,但这是我的直接看法。
  • 我也是这么理解的。但是我在运行之前明确删除了目录,它仍然跳过了构建目录的配方。
  • 所以它不创建目录,或者它不做任何关于在目录中放东西的事情?
  • 两者。它跳过制作目录。然后当它尝试将任何内容放入目录中时抱怨它不存在的明显原因哈哈。
  • 让我想一想。我可以观察到您创建的示例不是 MCVE (minimal reproducible example)。例如,您可能会在没有黑名单的情况下显示问题,也可能没有测试等——这将使人们更容易模拟您的环境。

标签: makefile


【解决方案1】:

由于我对 SO 的问题比答案多一个,所以我必须找到一个问题来回答 :),而且您似乎不喜欢已经存在的答案,所以好吧,即使您的问题不是“最小",我花了一个小时来处理它。

您的 Makefile 总体上还不错,但它没有遵循一些“良好做法”。一旦我把所有东西都整理好,所有的问题都消失了。我希望它可以帮助您从这个示例中学习 - 我如何更改您的原始 makefile 以遵循良好的做法。

剩下的唯一小问题是build 每次都会重新链接。这是因为通常 Make 不“依赖”变量值(例如 DEBUG),仅依赖于文件。可以通过创建“可靠变量”来解决这个问题(在这种小情况下它并不重要,但也许以后你会需要这个解决方案)。在

处查看我的答案

How do I force a target to be rebuilt if a variable is set?

下面是完整的工作 makefile,我把 cmets 放在代码之外的更改上。

## setup

尽可能使用:=

ifdef DEBUG
BUILDDIR:=.build_debug
else
BUILDDIR:=.build_release
endif

不要使用find列出文件,最好明确声明文件

PROGS:=\
    prog0   \
    prog1   \

CPP_SRC:=\
    spam    \
    eggs    \

CPP_SRC_TEST:=\
    spam_test   \
    eggs_test   \

拆分链接目标,因此仅在规则中创建目标:

PROG_LINKS:=$(addprefix $(BUILDDIR)/, $(PROGS))

PROGS:=$(PROGS:=.bin)
PROGS:=$(PROGS:%=$(BUILDDIR)/%)

CPP_SRC:=$(CPP_SRC:%=src/%.cpp)
CPP_OBJ:=$(CPP_SRC:src/%.cpp=$(BUILDDIR)/%.o)

LIB:=$(BUILDDIR)/libspatialDecisionMaking.so

PROGS_TEST:=$(CPP_SRC_TEST:%=$(BUILDDIR)/test/%.bin)

PROGS_TEST_LINKS:=$(addprefix $(BUILDDIR)/test, $(CPP_SRC_TEST))

## options

CC=g++-4.9

您混淆了CPP_FLAGSLD_FLAGS,我在每个中都放置了正确的标志
还有,你找共享库的方法,太复杂了,我做的很简单

CPP_FLAGS:= -std=c++11 -Isrc -fPIC -fopenmp

ifdef DEBUG
CPP_FLAGS+= -ggdb
else
CPP_FLAGS+= -O3
endif

LD_FLAGS:= -L$(BUILDDIR) -lgsl -larmadillo

## rules

你有太多的依赖 - 只列出手头目标在概念上需要的那些,然后递归

all: | $(PROGS) $(PROG_LINKS) build

test: | $(PROGS_TEST) $(PROGS_TEST_LINKS) build

链接文件不依赖于链接目标

在你的情况下,它实际上取决于 DEBUG 的值,但就像我上面所说的那样,实现它并不容易,所以我在这里跳过它并有一个假的,它一直在重新链接

.PHONY: build
build:
    ln -srf $(BUILDDIR) $@

这是处理目录创建的最佳方式

%/.:
    mkdir -p $(@D)

不幸的是,这是必需的,因为mkdir -p 不能重入并且受制于竞争条件

$(BUILDDIR)/test/.: | $(BUILDDIR)/.

.SECONDEXPANSION:

$(PROGS_TEST_LINKS) $(PROG_LINKS): %: | %.bin
    ln -sr $| $@

所有重要的配方都应该依赖于这个makefile,将Makefile更改为正确的(有一种更复杂的方法,可以自动处理)

$(BUILDDIR)/%.bin: src/%.cpp $(LIB) Makefile | $$(@D)/.
    $(CC) $(CPP_FLAGS) -o $@ $< $(LD_FLAGS) $(LIB)

$(LIB): $(CPP_OBJ) Makefile | $$(@D)/.
    $(CC) $(CPP_FLAGS) -o $@ $(CPP_OBJ) $(LD_FLAGS) -shared

这是处理“自动”依赖生成的最有效方法 - 它只调用预处理器一次,而不是原始 makefile 中的两次

我把它放在引号中,因为自动依赖的整个方法存在细微的缺陷,不能在所有情况下都有效 - 但在你的简单情况下,你不太可能遇到那个微妙的缺陷

是的,我违反了我上面提到的良好做法 - 只有在规则中创建的目标。如果一个人明白一个好的做法是为了什么,并且仍然认为违反它更好,那么好吧。

$(BUILDDIR)/%.o: src/%.cpp Makefile | $$(@D)/.
    $(CC) $(CPP_FLAGS) -MMD -MP -c $< -o $@ $(LD_FLAGS)



# include dependencies
-include $(CPP_OBJ:%.o=%.d)

clean:
    rm -rf $(BUILDDIR)

【讨论】:

  • 马克,首先。哇。感谢您投入这么多时间。当我登录并看到您的答案时,我实际上正要坐下来设置一个 MCVE。可以的话问几个问题。 1)“将链接目标拆分为每个规则有一个目标”是什么意思?在没有 .bin 扩展名的可执行文件之前总是链接并且不是 Make 定义的“目标”。你是说让他们自己成为目标吗? 2)为什么构建需要是一个虚假的目标? 3) 最后几个食谱中的$$ 是否与.SECONDEXPANSION 相关?我以前从未使用过这个,所以我现在正在阅读这个。
  • 这非常有帮助。在接下来的几天里,我将多次阅读这篇文章,以准确了解您的更改完成了什么。我有很多东西要学!
  • 自从我发布了这个,我在依赖文件的-include 语句周围添加了ifneq ($(MAKECMDGOALS),clean)。如果没有这个,$(BUILDDIR)/%.o 的配方将在运行make clean 时执行。构建对象后,将删除该目录。大量浪费计算。你也有这种情况吗?如果我不检查 clean 的目标,我仍然会看到上述更改的这种行为。
  • @nick 1. "split off" - 你把链接到它的prog.binprog 都交给它,一起在一个配方中制作,最佳实践是,在一个配方中制作一个目标,所以我将prog 目标拆分为自己的配方
  • @nick 是的,对不起,我没有回答“为什么完全不起作用”的问题。很多时候,如果问题不是最小的,重做整个事情会更容易,然后,问题通常会“神奇地”消失。
【解决方案2】:

.d、.o 和 .bin 文件在逻辑上依赖于 $(BUILDDIR),所以告诉 make 就是这种情况

$(BUILDDIR)/%.d: src/%.cpp | $(BUILDDIR)

【讨论】:

  • 这似乎有效。尽管现在它多次运行$(BUILDDIR) 规则的配方,因为时间戳在不断变化。不是什么大问题,只是似乎有其他问题。我将按照 Leffler 在问题 cmets 中推荐的 MCVE 工作。
  • @nick 您需要将其设为仅订单依赖项,无论您想在哪里使用它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-08-18
  • 2017-10-17
  • 2013-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-23
相关资源
最近更新 更多