如何在 makefile 中针对特定目标使用 include 指令

问题描述 投票:0回答:4

我只想将 include 指令用于特定目标。当不需要目标时,我不想运行其他 makefile,因为这意味着 makefile 是不必要生成的。

那么有没有一种方法可以有条件地使用 include 指令,该指令以目标为条件?或者以某种方式使 include 指令成为目标的先决条件。

这是我到目前为止所拥有的:

# Flags

INCDIR = $(CURDIR)/include
CFLAGS = -Wall -Wno-overflow -Wno-uninitialized -pedantic -std=c99 -I$(INCDIR) -O3
LFLAGS = -flat_namespace -dynamiclib -undefined dynamic_lookup

# Directory names

# Set vpath search paths

vpath %.h include
vpath %.c src
vpath %.o build
vpath %.d build

# Get files for the core library

CORE_FILES = $(wildcard src/*.c)
CORE_OBJS = $(patsubst src/%.c, build/%.o, $(CORE_FILES))
CORE_DEPS = $(CORE_OBJS:.o=.d)

# Core library target linking

core : $(CORE_OBJS) | bin
    $(CC) $(LFLAGS) -o bin/libcbitcoin.2.0.dylib $(CORE_OBJS)

# Include header prerequisites (How to do only for "core" target?)

include $(CORE_DEPS)

# Makefiles for header dependencies. 

$(CORE_DEPS): build/%.d: src/%.c | build
    rm -f $@; \
    $(CC) -I$(INCDIR) -MM $< -MT '$(@:.d=.o) $@' > $@

# Objects depend on directory

$(CORE_OBS) : | build

# Create build directory

build:
    mkdir build

# Create bin directory

bin:
    mkdir bin

# Core Compilation

$(CORE_OBJS): build/%.o: src/%.c
    $(CC) -c $(CFLAGS) $< -o $@

# Depencies require include/CBDependencies.h as a prerequisite

build/CBOpenSSLCrypto.o: include/CBDependencies.h

# Crypto library target linking

crypto : build/CBOpenSSLCrypto.o -lcrypto -lssl | bin
    $(CC) $(LFLAGS) -o bin/libcbitcoin-crypto.2.0.dylib build/CBOpenSSLCrypto.o -lcrypto -lssl

# Crypto library compile

build/CBOpenSSLCrypto.o: dependencies/crypto/CBOpenSSLCrypto.c
    $(CC) -c $(CFLAGS) $< -o $@

#Clean

clean:
    rm -f $(CORE_OBJS) $(CORE_DEPS) build/CBOpenSSLCrypto.o

正如您应该能够知道的那样,我不需要包含“加密”的“.d”文件,但我需要包含“核心”(默认目标)。

感谢您的帮助。

makefile gnu-make
4个回答
21
投票

Make 不是一种过程语言,因此将其视为一种违背常规的语言;你的 makefile 将很难扩展,并且可能会导致微妙的错误。

Tom Tromey 有一种“更好的方法”,干净、高效且可扩展。诀窍是要认识到您可以在与目标文件相同的步骤中构建依赖项文件。依赖关系只是告诉 Make 对象何时需要重建;第一次构建对象时不需要它们,因为 Make 知道必须构建该对象。如果依赖关系发生变化,那只能是因为源代码或旧依赖关系中的某些内容发生了变化,因此 Make 再次知道必须重建该对象。 (这个不太明显,所以可能需要一点思考。) $(CORE_OBJS): build/%.o: src/%.c $(CC) -c $(CFLAGS) $< -o $@ $(CC) -MM -MF build/$*.d $< -include build/*.d

还有一个问题:如果您更改代码以删除依赖项 - 并且还删除该文件 - 您将无法重建,因为旧的依赖项列表仍然需要一个不再可以的文件成立。复杂的解决方案是处理依赖文件,以使每个先决条件(例如标头)本身成为目标,无需命令,以便可以假设在需要时重建它:

$(CORE_OBJS): build/%.o: src/%.c $(CC) -c $(CFLAGS) $< -o $@ $(CC) -MM -MF build/$*.d $< @cp build/$*.d build/$*.P @sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ -e '/^$$/ d' -e 's/$$/ :/' < build/$*.P >> build/$*.d; @rm build/$*.P

一种更粗暴的方法,但几乎同样万无一失,是为标头和源添加包罗万象的规则:

$(CORE_OBJS): build/%.o: src/%.c $(CC) -c $(CFLAGS) $< -o $@ $(CC) -MM -MF build/$*.d $< %.cc %.h:

编辑:

分解新命令:

-MM

选项告诉gcc为目标文件生成

make
规则,而不是预处理或编译。默认情况下是将规则发送到它将发送预处理输出的任何位置,通常是标准输出。

-MF

选项与

-MM
一起使用,指定输出文件。因此
-MM -MF build/$*.d
会将规则放在我们想要的位置。

因此以下两个命令(几乎总是)是等效的:

$(CC) -MM -MF build/$*.d $< $(CC) -MM $< > build/$*.d

(我省略了 
-I$(...)

和使用

-MMD
选项的可能性,因为两者都有点复杂,而且并不是问题的重点。)
    


3
投票

ifeq (core,$(MAKECMDGOALS)) include $(CORE_DEPS) endif

如果可能存在多个目标,您当然可以使用
ifneq (,$(findstring core,$(MAKECMDGOALS)))


注意:这是一个“快速而肮脏”的解决方案——我同意 Beta 的观点,你不应该将其作为一种常见的做法(如果你在很多 makefile 中这样做,这可能会变得混乱......)。

约翰


1
投票

我对原始问题的回答是,不,您不能包含依赖于目标的规则 - 所有规则都在考虑目标之前进行处理。这是 make 的限制(我猜)。再说一遍,好点,有 MAKECMDGOALS,但这不仅仅是 make 实用程序本身的黑客攻击吗?

Beta的答案足够合理、足够正统,但即使它是最好的,你也不能说它是干净的。如果 make 之前没有处理过特定目标并且相应的 build/*.d 依赖文件不在那里,它将无法工作。


0
投票

DBG_DEPS ::= $(addprefix $(DBG_OBJS_DIR)/, $(DEPS)) REL_DEPS ::= $(addprefix $(REL_OBJS_DIR)/, $(DEPS)) ... $(REL_OBJS_DIR)/%.o $(REL_OBJS_DIR)/%.d: %.cpp $(REL_OBJS_DIR)/.dirstamp $(info Compiling release: $<) @$(CXX) $(CXXFLAGS) $(REL_CXXFLAGS) -MMD -o $(basename $@).o -c $< ... $(DBG_OBJS_DIR)/%.o $(DBG_OBJS_DIR)/%.d: %.cpp $(DBG_OBJS_DIR)/.dirstamp $(info Compiling debug: $<) @$(CXX) $(CXXFLAGS) $(DBG_CXXFLAGS) -MMD -o $(basename $@).o -c $<

因此,为了正确构建,我们需要包含一个用于“调试”的文件和不同的“发布”文件。
如果没有条件包含,每个连续的 
make clean

将重建所有 .o 和 .d 文件。另外

make release
将构建调试 .d 和 .o 文件。
这是按预期工作的结果:

ifeq (,$(MAKECMDGOALS)) -include $(REL_DEPS) endif ifneq (,$(findstring release,$(MAKECMDGOALS))) -include $(REL_DEPS) endif ifneq (,$(findstring all,$(MAKECMDGOALS))) -include $(REL_DEPS) endif ifneq (,$(findstring debug,$(MAKECMDGOALS))) -include $(DBG_DEPS) endif

这会生成正确的(几乎仍然需要使用 MT 选项在 .o 目标旁边添加 .d)依赖文件,该文件对于给定目标正确(可能不同),这些文件对标头做出反应,并且仅在为该目标构建时包含它们,而不是为“干净'。

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.