Makefile仅执行一次命令

时间:2018-07-29 07:12:52

标签: c gcc makefile gnu-make

我正在使用MinGW的Makefile(Windows 8.1,GCC 7.3.0)构建一个中等大小的项目,该项目自动检测文件夹src下的所有源文件并将所有目标文件编译到{{1} }文件夹,但不幸的是,它仅对检测到的第一个文件执行命令,并在那里停止。

这是我第一次为除源文件之外的任何内容编写Makefile脚本,也许我弄错了一些规则。预先感谢!

obj

输出:

CC := gcc SRC := src OBJ := obj MAIN := main PACK := libbundle SOURCES := $(wildcard $(SRC)/*.c) OBJECTS := $(patsubst $(SRC)/%.c,$(OBJ)/%.o, $(SOURCES)) CFLAGS := -I$(SRC) $(OBJECTS): $(SOURCES) $(CC) $(CFLAGS) -c $< -o $@ # build: # ar rcs $(PACK).a $(OBJECTS) # $(CC) -shared -o $(PACK).so $(OBJECTS) # $(CC) -o $(MAIN).c $(PACK).so

...然后停在那里!

3 个答案:

答案 0 :(得分:3)

问题-有多个目标的规则

您的规则

$(OBJECTS): $(SOURCES)
    $(CC) $(CFLAGS) -c $< -o $@

具有多个目标。我认为这在这里不合适。参见here,其中有多个目标的规则在哪里有用。

此外,此规则指定了多个先决条件-$<仅表示第一个先决条件。您可以使用$+捕获所有的先决条件-但随后您将失去使用-o选项的能力。如果要使用多个先决条件,请参见下文。

$(OBJECTS): $(SOURCES)的详细含义

例如,假设您的src/目录包含firstsource.csecondsource.c。然后您的变量变为

$(SOURCES) -> src/firstsource.c src/secondsource.c
$(OBJECTS) -> obj/firstsource.o obj/secondsource.o

(实际上-有点非直觉-firstsource将放置在 第二个源之后,但是为了简单起见,我们忽略它。)

所以规则

$(OBJECTS): $(SOURCES)
    $(CC) $(CFLAGS) -c $< -o $@

等同于

obj/firstsource.o obj/secondsource.o: src/firstsource.c src/secondsource.c
    $(CC) $(CFLAGS) -c $< -o $@

此规则又等效于两个规则(因为它有多个目标)-每个都有相同的先决条件:

obj/firstsource.o: src/firstsource.c src/secondsource.c
    $(CC) $(CFLAGS) -c $< -o $@

obj/secondsource.o: src/firstsource.c src/secondsource.c
    $(CC) $(CFLAGS) -c $< -o $@

您能在这里看到问题吗?

由于$<仅表示第一个前提条件,因此第一个规则的配方变为

    gcc -Isrc -c src/firstsource.c -o obj/firstsource.o

这对于第一个规则很好,但是对于第二个规则却不起作用

    gcc -Isrc -c src/firstsource.c -o obj/secondsource.o

因为您使用了错误的输入文件。

顺便说一句...你提到

  

不幸的是[即make]仅在检测到的第一个文件上执行命令,并在那里停止。

这是因为-当您在不带任何参数的情况下调用make时-它会调用文件中的第一个规则,而不再执行。

选项1:使用多个规则

这里更合适的是多个规则-每个规则都只有一个单个目标。因此,尝试将以下内容替换为以下内容。

$(OBJ)/%.o: $(SRC)/%.c
    $(CC) $(CFLAGS) -c $< -o $@

compile-only: $(OBJECTS)

您可以在此修改后的Makefile上以以下方式调用make

make -B compile-only

选项2:具有多个先决条件的单个目标

如果目标中有多个先决条件,则可以使用特殊变量$+在配方中引用它们。但是,在这种情况下,您不能使用-o选项-这样将无法指定目标文件的输出目录。 (要变通解决此问题,您可以在编译前cdobj目录-但随后您需要调整SOURCES变量。)

CC := gcc
CFLAGS := -Isrc
SRC := src

SOURCES := $(wildcard $(SRC)/*.c)

myobjs: $(SOURCES)
    $(CC) $(CFLAGS) -c $+

这会将所有目标文件放在顶层目录中。如前所述,如果必须将对象文件放在单独的目录中,则可以调整SOURCEScd目录obj

在旁边-模式规则的预定义配方

我理解将构建输出放置在单独目录中的基本原理,但是-如果您愿意将构建输出与源文件放置在同一目录中,则可以使用make的{​​{3}}简化Makefile。

SOURCES := $(wildcard $(SRC)/*.c)
OBJECTS := $(SOURCES:.c=.o)

compile: $(OBJECTS)

答案 1 :(得分:0)

您应该在Makefile中使用standard targets,其中最重要的是“全部”。并且它应该是Makefile中的第一个目标,以便makemake all做同样的事情。

all: $(OBJECTS)

答案 2 :(得分:0)

使用$(OBJECTS): $(SOURCES)时,您要告诉make $(OBJECTS)中的每个文件都取决于$(SOURCES)中的每个文件,并且将执行以下命令,因为任何对象都无法通过以下测试:任何来源。该命令将仅执行一次并停止。

您需要指定每个目标文件取决于其对应的源文件。正如我所看到的,您正在使用GMAKE语法,我将向您展示该规则的GNU make语法:

$(OBJECTS): obj/%.o: src/%.c
    $(CC) $(CFLAGS) -c $< -o $@

这就像您对每个.o文件都有一条规则,说明如何从其正确的源文件进行编译。

您还需要说出哪些文件是您的默认目标,例如:

.PHONY: all
all: $(OBJECTS)
clean:
     $(RM) $(TOCLEAN)

以第一个规则为准,因此默认情况下将其选中。

这将使all成为您的默认目标。它会爆炸到您的所有目标文件中,并且对于每个对象,您都有一条规则说明如何编译它(不是必需的,因为gnu已经知道如何编译C程序,但是在此处重复执行不会有任何伤害)

您的最终Makefile是:

CC   := gcc

SRC  := src
OBJ  := obj

MAIN := main
PACK := libbundle
CFLAGS := -I$(SRC)
PICFLAGS := -fPIC


SOURCES := $(wildcard $(SRC)/*.c)
OBJECTS := $(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(SOURCES))
TOCLEAN += $(OBJECTS)
PICOBJECTS := $(patsubst $(OBJ)/%.o, $(OBJ)/%.pic, $(OBJECTS))
TOCLEAN += $(PICOBJECTS)

.PHONY: all

.SUFFIXES: .c .o .pic

all: $(PACK).a $(MAIN)
clean:
    $(RM) $(TOCLEAN)

$(MAIN): $(MAIN).o $(PACK).so
    $(CC) $(LDFLAGS) -o $@ $+
TOCLEAN += $(MAIN)

$(PACK).a: $(OBJECTS)
    ar r $(PACK).a $(OBJECTS)
TOCLEAN += $(PACK).a

$(PACK).so: $(PICOBJECTS)
    $(LD) $(LDFLAGS) -shared -o $(PACK).so $(PICOBJECTS)
TOCLEAN += $(PACK).so

# this to create a normal .o file in $(OBJ) directory.
$(OBJECTS): $(OBJ)/%.o: $(SRC)/%.c
    $(CC) $(CFLAGS) -o $@ -c $<

# this to create a PIC (Position Independent Code) .pic object in $(OBJ) directory.
# (REQUIRED FOR .so SHARED OBJECT)
$(PICOBJECTS): $(OBJ)/%.pic: $(SRC)/%.c
    $(CC) $(CFLAGS) $(PICFLAGS) -o $@ -c $<