Makefile:如何确定构建目标?

时间:2019-02-08 19:30:55

标签: makefile gnu-make makefile-project

我正在尝试为当前项目扩展一个继承的Makefile中的有趣行为。

LIBS := lib/precompiled1.a lib/precompiled2.a lib/mylib1.a lib/mylib2.a

mylib1-src := (source directory)
mylib1-obj := $(patsubst $(mylib1-src)/%.c, obj/LIB1/%.o, $(wildcard $(mylib1-src)/*.c))

mylib2-src := (source directory)
mylib2-obj := $(patsubst $(mylib2-src)/%.cpp, obj/LIB2/%.o, $(wildcard $(mylib2-src)/*.cpp))

default: all

lib/mylib1.a: $(mylib1-obj)
    ar -r $@ $^

lib/mylib2.a: $(mylib2-obj)
    ar -r $@ $^

all: exe/my_out

exe/my_out: $(my_out-obj)
    g++ $^ $(COMMON_FLAGS) $(LIBS) $(MY_FLAGS)

当我运行make时,遇到一个错误,它无法与lib / mylib1.a链接,并中断了。太酷了,我只需要确保正在构建.a目标即可。像这样天真地弄混makefile:

lib/mylib1.a: $(mylib1-obj)
    ar -r $@ $^

lib/mylib2.a: $(mylib2-obj)
    ar -r $@ $^

default: all

现在可以编译mylib1.a了,但是既不编译mylib2.a也不编译“全部”。

这是我卡住的地方。如果我执行以下操作:

default: all

lib/mylib1.a: $(mylib1-obj)
    ar -r $@ $^

lib/mylib2.a: $(mylib2-obj)
    ar -r $@ $^

all: lib/mylib1.a exe/my_out

然后,编译器会抱怨lib / mylib1.a不是可执行文件。虽然不是很好,但是却让我陷入了怪异的境地。我可以单独编译.a文件。我可以将它们链接到可执行文件。我不能一次做完所有这些事情,只能弄乱makefile,这在一定程度上是错误的。

我的问题是,为什么更改默认顺序会更改构建的内容?如何获得所有.a文件来构建并链接到主要可执行文件,而不必每次都手动弄乱makefile?一般而言,我对makefile还是相当陌生,我不确定为什么会发生这种情况。

谢谢!

3 个答案:

答案 0 :(得分:1)

makefile中目标的顺序无关紧要,只有一个例外:第一个目标是默认目标,即在您没有目标的情况下调用make时的目标。可以使用.DEFAULT_GOAL := ...

在GNU make中更改此行为

依赖顺序

串行构建(make -j1)中重要的是目标定义中的依存顺序,例如

T: A B C

T: A B
T: C
    rule

在串行构建make中,将首先满足“ A”,然后满足“ B”,然后满足“ C”。如果在构建“ A”时缺少满足的“ C”依赖关系,则构建将成功。将生成文件更改为

T: C A B

...,串行构建将开始失败。

在并行构建(make -jN中,依赖性顺序无关紧要,因为make将尝试并行构建“ A”,“ B”和“ C”(如果允许)

依赖关系不完整

如果未为目标“ T”列出强制性依赖性“ D”,则一旦满足列出的依赖性,make将允许构建目标“ T”。但是它不会等待“ D”完成。因此,构建成功取决于未定义的行为,

  1. “ D”在构建开始之前已经存在,并且当前构建未触及它,或者
  2. 在启动目标“ T”之前满足目标“ D”。

并行构建的最坏情况:如果在读取“ D”的同时“ T”的规则没有失败,并且同时编写了“ D”,则最终构建成功,但是构建无效文物。

不完全依赖项的常用指标是

    从串行转换为并行时,
  • 构建开始(随机)失败
  • 在移至另一台构建机器时,构建开始随机失败,或者
  • 从成功的构建中生成工件,并带有奇怪的测试失败。

解决方案

始终为所有目标指定完全依赖项。在您的情况下,至少是:

# Add internal dependencies included in $(LIBS)
exe/my_out: $(my_out-obj) $(filter lib/%.a, $(LIBS))
    g++ $^ $(COMMON_FLAGS) $(LIBS) $(MY_FLAGS)

答案 1 :(得分:0)

默认目标是Makefile中的第一个目标,名为“ default”的目标没有特殊意义。

对于最新版本的Make,可以像这样显式指定默认目标:

DEFAULT_GOAL:=全部

应该将库添加为可执行文件的先决条件,而不是添加到所有目标,例如:

exe/my_out: $(my_out-obj) lib/mylib1.a lib/mylib2.a
    g++ $^ $(COMMON_FLAGS) $(LIBS) $(MY_FLAGS)

lib/mylib1.a: $(mylib1-obj)
    ar -r $@ $^

lib/mylib2.a: $(mylib2-obj)
    ar -r $@ $^

答案 2 :(得分:0)

如安迪(Andy)建议的那样,通过执行此操作(通常在makefile的顶部附近)来将默认规则设置为all:

.DEFAULT_GOAL = all

然后将所有定义为:

LIB_DEPS = lib/mylib1.a lib/mylib2.a
all: exe/my_out $(LIB_DEPS)

# Also to ensure that the libs are built first (for example with parallel builds):
exe/my_out: $(my_out-obj) $(LIB_DEPS)
    ... recipe here...

这样,如果您执行make或make -j,则可以确保您的构建顺序。

注意:我没有看到$(my_out-obj)的规则-您错过了还是只是没有打印?