21

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

那么有没有办法有条件地使用以目标为条件的包含指令?或者以某种方式使包含指令成为目标的先决条件。

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

# 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

正如您应该能够告诉我的,我不需要为“crypto”包含“.d”文件,但我为“core”(默认目标)包含。

感谢您的任何帮助。

4

3 回答 3

19

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选项的可能性,因为两者都变得有点复杂,并不是问题的重点。)

于 2012-11-18T15:26:12.487 回答
3

您可以使用 MAKECMDGOALS。

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

ifneq (,$(findstring core,$(MAKECMDGOALS)))如果有多个目标的可能性,您当然可以使用。

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

约翰

于 2012-11-17T23:16:33.603 回答
1

我忍不住打破了关于什么是好的答案的准则。

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

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

于 2012-12-02T12:55:45.610 回答