我只想将 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”文件,但我需要包含“核心”(默认目标)。
感谢您的帮助。
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
选项的可能性,因为两者都有点复杂,而且并不是问题的重点。)ifeq (core,$(MAKECMDGOALS))
include $(CORE_DEPS)
endif
如果可能存在多个目标,您当然可以使用
ifneq (,$(findstring core,$(MAKECMDGOALS)))
。
注意:这是一个“快速而肮脏”的解决方案——我同意 Beta 的观点,你不应该将其作为一种常见的做法(如果你在很多 makefile 中这样做,这可能会变得混乱......)。
约翰
我对原始问题的回答是,不,您不能包含依赖于目标的规则 - 所有规则都在考虑目标之前进行处理。这是 make 的限制(我猜)。再说一遍,好点,有 MAKECMDGOALS,但这不仅仅是 make 实用程序本身的黑客攻击吗?
Beta的答案足够合理、足够正统,但即使它是最好的,你也不能说它是干净的。如果 make 之前没有处理过特定目标并且相应的 build/*.d 依赖文件不在那里,它将无法工作。
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)依赖文件,该文件对于给定目标正确(可能不同),这些文件对标头做出反应,并且仅在为该目标构建时包含它们,而不是为“干净'。