makefile跳过生成生成目录

makefile跳过生成生成目录,makefile,Makefile,我希望所有生成文件都存储在项目根文件夹中的build目录中。但是,如果添加调试标志,我不想重新生成所有文件。因此,我有两个目录.build\u release和.build\u debug。然后我创建一个从build到正确目录的符号链接 我希望所有这些都由make处理。这是我的makefile: ## setup ifdef DEBUG BUILDDIR=.build_debug else BUILDDIR=.build_release endif BLACKLIST:=bayesP obs

我希望所有生成文件都存储在项目根文件夹中的
build
目录中。但是,如果添加调试标志,我不想重新生成所有文件。因此,我有两个目录
.build\u release
.build\u 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)
##设置
ifdef调试
BUILDDIR=.build\u调试
其他的
BUILDDIR=.build\u发布
恩迪夫
黑名单:=bayesP obsDataStats test3测试bayespsamplebr test2 tuneSp\
ToyFeatures 2多个getCov getDist已连接合并群集示例\
ToyFeatures 0 ToyFeatures 1 ToyFeatures 2 ToyFeatures 3 ToyFeatures 4\
ToyFeatures 6 ToyFeatures 7 WnsFeatures 0 WnsFeatures 1 WnsFeatures 2
##编代码
PROGS:=$(shell find./src/-maxdepth 1-name“*.cpp”-exec grep-l“int main”{}\;)
PROGS:=$(notdir$(basename$(PROGS)))
程序:=$(过滤掉$(黑名单),$(程序))
CPP_SRC:=$(通配符SRC/*.CPP)
CPP_SRC:=$(notdir$(basename$(CPP_SRC)))
CPP_SRC:=$(过滤掉$(程序)$(黑名单),$(CPP_SRC))
程序:=$(程序:=.bin)
程序:=$(程序:%=$(BUILDDIR)/%)
CPP_SRC:=$(CPP_SRC:%=SRC/%.CPP)
CPP_OBJ:=$(CPP_SRC:SRC/%.CPP=$(BUILDDIR)/%.o)
LIB=$(BUILDDIR)/libspatialDecisionMaking.so
##测试代码
CPP_SRC_TEST:=$(通配符SRC/TEST/*.CPP)
CPP_SRC_测试:=$(notdir$(basename$(CPP_SRC_测试)))
CPP_SRC_测试:=$(过滤掉$(黑名单),$(CPP_SRC_测试))
程序测试:=$(CPP\u SRC\u测试:%=$(BUILDDIR)/TEST/%.bin)
CPP_OBJ_测试:=$(CPP_SRC_测试:%=$(BUILDDIR)/TEST/.o)
CPP\u SRC\u测试:=$(CPP\u SRC\u测试:%=SRC/TEST/%)
##选择权
CC=g++-4.9
ifdef调试
CPP_标志=-std=c++11-ggdb
其他的
CPP_标志=-std=c++11-O3
恩迪夫
LD_FLAGS=-Isrc-L$(BUILDDIR)-lgsl-larmadillo-fPIC-fopenmp
##规则
全部:|$(BUILDDIR)$(LIB)$(PROGS)构建
测试:|$(BUILDDIR)/test$(LIB)$(PROGS_test)构建
生成:$(BUILDDIR)
ln-rfs$(BUILDDIR)生成
$(BUILDDIR)/测试:$(BUILDDIR)
mkdir$(BUILDDIR)/测试
$(BUILDDIR):
mkdir$(BUILDDIR)
$(BUILDDIR)/%.bin:src/%.cpp$(LIB)
$(CC)$(CPP_标志)-o$@$<$(LD_标志)-l$(LIB:$(BUILDDIR)/LIB%.so=%)
ln-rfs$@$(@:%.bin=%)
$(LIB):$(CPP_OBJ)
$(CC)$(CPP_标志)-o$@$^$(LD_标志)-共享
$(BUILDDIR)/%.o:src/%.cpp$(BUILDDIR)/%.d
$(CC)$(CPP_标志)-c$<-o$@$(LD_标志)
$(BUILDDIR)/%.d:src/%.cpp
$(CC)$(CPP_标志)-MM$<-MT$(@:%.d=%.o)>$@$(LD_标志)
%.cpp:
%.hpp:
#包含依赖项
-包括美元(CPP_对象:%.o=%.d)
清洁:
rm-rf$(BUILDDIR)
不过,它似乎跳过了制作
$(BUILDDIR)
。每次运行make之前,我都会删除目录,它会根据target
$(BUILDDIR)/%.d
的规则直接构建依赖关系makefile。但是,当试图构建依赖项时,它自然会抱怨,因为
$(BUILDDIR)
不存在


你知道为什么它会跳过制作
$(BUILDDIR)
的方法吗?

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

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

因为我还有一个问题要回答,所以我必须找到一个问题来回答:),而你似乎不喜欢已经存在的答案,所以好吧,即使你的问题不是“最小的”,我还是花了一个小时来研究它

您的Makefile通常是不错的,但是它没有遵循一些“好的实践”。一旦我把一切整理好,所有的问题都消失了。我希望它能帮助您从这个示例中学习——我是如何更改您的原始makefile以遵循良好实践的

剩下的唯一一个小问题是,
build
每次都会重新链接。这是因为Make通常不“依赖”变量值(例如
DEBUG
),只依赖于文件。通过创建“可靠的变量”,可以解决这个问题(在这个小案例中,这并不重要,但可能以后您将需要此解决方案)。请参阅我的答案

下面是完整的工作makefile,我对代码之外的更改进行了注释

## 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_标志
LD_标志
,我在每个
另外,您查找共享库的方法太复杂了,我简化了

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_标志)-o$@$<$(LD_标志)$(LIB)
$(LIB):$(CPP_OBJ)Makefile |$$(@D)/。
$(CC)$(CPP_标志)-o$@$(CPP_OBJ)$(LD_标志)-共享
这是处理“自动”依赖项生成的最有效方法——它只调用预处理器一次,而不是在原始makefile中调用两次

我引用了这句话,因为自动依赖的整个方法都有微妙的缺陷,不能在所有情况下都有效——但在您的简单案例中,您不太可能遇到这种微妙的缺陷

是的,我违反了我上面提到的良好实践-只有在规则中创建的目标。如果一个人理解良好实践的目的,并且仍然认为违反它更好,那么OK
$(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
$(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)