我正在使用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
...然后停在那里!
答案 0 :(得分:3)
您的规则
$(OBJECTS): $(SOURCES)
$(CC) $(CFLAGS) -c $< -o $@
具有多个目标。我认为这在这里不合适。参见here,其中有多个目标的规则在哪里有用。
此外,此规则指定了多个先决条件-$<
仅表示第一个先决条件。您可以使用$+
捕获所有的先决条件-但随后您将失去使用-o
选项的能力。如果要使用多个先决条件,请参见下文。
$(OBJECTS): $(SOURCES)
的详细含义例如,假设您的src/
目录包含firstsource.c
和secondsource.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
时-它会调用文件中的第一个规则,而不再执行。
这里更合适的是多个规则-每个规则都只有一个单个目标。因此,尝试将以下内容替换为以下内容。
$(OBJ)/%.o: $(SRC)/%.c
$(CC) $(CFLAGS) -c $< -o $@
compile-only: $(OBJECTS)
您可以在此修改后的Makefile上以以下方式调用make
make -B compile-only
如果目标中有多个先决条件,则可以使用特殊变量$+
在配方中引用它们。但是,在这种情况下,您不能使用-o
选项-这样将无法指定目标文件的输出目录。 (要变通解决此问题,您可以在编译前cd
到obj
目录-但随后您需要调整SOURCES
变量。)
CC := gcc
CFLAGS := -Isrc
SRC := src
SOURCES := $(wildcard $(SRC)/*.c)
myobjs: $(SOURCES)
$(CC) $(CFLAGS) -c $+
这会将所有目标文件放在顶层目录中。如前所述,如果必须将对象文件放在单独的目录中,则可以调整SOURCES
和cd
目录obj
。
我理解将构建输出放置在单独目录中的基本原理,但是-如果您愿意将构建输出与源文件放置在同一目录中,则可以使用make
的{{3}}简化Makefile。
SOURCES := $(wildcard $(SRC)/*.c)
OBJECTS := $(SOURCES:.c=.o)
compile: $(OBJECTS)
答案 1 :(得分:0)
您应该在Makefile中使用standard targets,其中最重要的是“全部”。并且它应该是Makefile中的第一个目标,以便make
和make 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 $<