这是我在学习了几篇教程和gnu-make手册后第一次尝试制作Makefile。Makefile工作并在BIN文件夹中创建.o、.a和.exe文件。但是,我已经为所有文件添加了src\和bin\前缀。我知道在使用Makefiles时,一定有更好的方法来解决文件夹问题。唯一的问题是,经过数小时的编辑和基于教程的不同尝试,我无法弄清楚。在我学习的这个阶段,我发现GNU让手册太难了。
我在Windows7上使用MinGW GCC工具链。我已经将mingw32-make.exe复制到make.exe,目的是尝试我所经历的教程和示例。
如果能在这个问题上得到任何帮助,我将不胜感激。非常感谢。
我的Makefile如下:
CC = gcc
CFLAGS = -O3 -Wall -c
BIN = bin/
LDFLAGS = -L$(BIN) -lmyLib
all: test.exe
test.exe: test.o libmyLib.a
gcc bintest.o -o bintest.exe $(LDFLAGS)
test.o: srctest.c srcmyLib.h
$(CC) $(CFLAGS) -o bintest.o srctest.c
libmyLib.a: myLib.o
ar rcs binlibmyLib.a binmyLib.o
myLib.o: srctest.c srcmyLib.h
$(CC) $(CFLAGS) -o binmyLib.o srcmyLib.c
clean:
del bin*.* /Q
首先,Makefile存在一些问题,即使它显然有效。当你写:
myLib.o: srctest.c srcmyLib.h
$(CC) $(CFLAGS) -o binmyLib.o srcmyLib.c
你在撒谎:
- 您告诉它,规则的结果是
myLib.o
,而它是binmyLib.o
,即不同的文件 - 你告诉make,
myLib.o
依赖于srctest.c
,而实际上它依赖于srcmyLib.c
与中的其他规则相同
libmyLib.a: myLib.o
ar rcs binlibmyLib.a binmyLib.o
您告诉make,如果myLib.o
比libmyLib.a
新,而真正的先决条件是binmyLib.o
,真正的目标是binlibmyLib.a
,则应执行该规则。
通过这样做,你完全阻止了make做它应该做的事情:根据目标文件和先决条件文件的最后修改时间,决定是否必须执行配方。试试看:运行make两次,你会发现它毫无用处地重做了它已经做过的事情。永远不要撒谎。
其次,您可以使用一些高级功能来改进Makefile,如自动($@
、$<
、$^
(、标准(LDLIBS
、AR
、ARFLAGS
(和常规(BIN
、SRC
(生成变量。以下是您可以尝试的一个示例,在解决了上述问题并更好地使用变量后(再加上添加缺失的-I
gcc选项,并将all
和clean
声明为虚假文件,因为这些目标不是真实的文件,我们不想撒谎(:
BIN = bin
SRC = src
CC = gcc
CFLAGS = -O3 -Wall -c -I$(SRC)
LDFLAGS = -L$(BIN)
LDLIBS = -lmyLib
AR = ar
ARFLAGS = rcs
.PHONY: all clean
all: $(BIN)/test.exe
$(BIN)/test.exe: $(BIN)/test.o $(BIN)/libmyLib.a
$(CC) $< -o $@ $(LDFLAGS) $(LDLIBS)
$(BIN)/test.o: $(SRC)/test.c $(SRC)/myLib.h
$(CC) $(CFLAGS) -o $@ $<
$(BIN)/libmyLib.a: $(BIN)/myLib.o
$(AR) $(ARFLAGS) $@ $^
$(BIN)/myLib.o: $(SRC)/myLib.c $(SRC)/myLib.h
$(CC) $(CFLAGS) -o $@ $<
clean:
del $(BIN)*.* /Q
现在,所有非虚假的目标和先决条件都是规则中真正涉及的常规文件。再次尝试一下,你会发现make重建只是过时的,因此需要重建。
如果你想去掉$(SRC)/
前缀,你可以使用vpath
指令,告诉在哪里查找源文件(我坚持源,很多人试图将其用于目标文件,这不是它的用途(:
vpath %.h $(SRC)
vpath %.c $(SRC)
然后:
$(BIN)/test.o: test.c myLib.h
$(CC) $(CFLAGS) -o $@ $<
$(BIN)/myLib.o: myLib.c myLib.h
$(CC) $(CFLAGS) -o $@ $<
注意:您也可以使用
VPATH
变量而不是vpath
指令。
模式规则用于考虑类似的规则,例如,您的编译规则只因源文件和对象文件的名称而异:
$(BIN)/%.o: %.c myLib.h
$(CC) $(CFLAGS) -o $@ $<
总而言之:
BIN = bin
SRC = src
CC = gcc
CFLAGS = -O3 -Wall -c -I$(SRC)
LDFLAGS = -L$(BIN)
LDLIBS = -lmyLib
AR = ar
ARFLAGS = rcs
vpath %.h $(SRC)
vpath %.c $(SRC)
.PHONY: all clean
all: $(BIN)/test.exe
$(BIN)/test.exe: $(BIN)/test.o $(BIN)/libmyLib.a
$(CC) $< -o $@ $(LDFLAGS) $(LDLIBS)
$(BIN)/%.o: %.c myLib.h
$(CC) $(CFLAGS) -o $@ $<
$(BIN)/libmyLib.a: $(BIN)/myLib.o
$(AR) $(ARFLAGS) $@ $^
clean:
del $(BIN)*.* /Q
最后,如果您真的想在规则中避免使用$(BIN)/
前缀,则必须移动到$(BIN)
目录并从那里调用make。如果愿意,可以将Makefile保留在主目录中,并使用-f ../Makefile
选项。
当然,这比只在主目录中键入make [goals]
更不方便。有一些方法可以让make从调用它的地方进行测试,如果它不在构建目录中,请使用-C
和-f
选项重新调用它自己,这样它就可以从构建目录中完成它的工作。但如果你是新手,可能会有点太复杂。
然而,如果你感兴趣,可以看看这篇涵盖这个主题的文章(以及更多(。如果我们尽可能简化帖子中的建议,并将其专门用于您的案例,那么最终的Makefile可能是这样的:
# here starts the black magic that makes it possible
.SUFFIXES:
BIN := bin
SRC := src
ifneq ($(notdir $(CURDIR)),$(BIN))
.PHONY: $(BIN) clean
$(BIN):
@$(MAKE) --no-print-directory -C $@ -f ../Makefile SRC=$(CURDIR)/$(SRC) $(MAKECMDGOALS)
Makefile: ;
% :: $(BIN) ; :
clean:
del $(BIN)*.* /Q
else
# here ends the black magic that makes it possible
# here starts the Makefile you would really like to write
CC := gcc
CFLAGS := -O3 -Wall -c -I$(SRC)
LDFLAGS := -L.
LDLIBS := -lmyLib
AR := ar
ARFLAGS := rcs
vpath %.h $(SRC)
vpath %.c $(SRC)
.PHONY: all
all: test.exe
test.exe: test.o libmyLib.a
$(CC) $< -o $@ $(LDFLAGS) $(LDLIBS)
%.o: %.c myLib.h
$(CC) $(CFLAGS) -o $@ $<
libmyLib.a: myLib.o
$(AR) $(ARFLAGS) $@ $^
# here ends the Makefile you would really like to write
# a last bit of black magic
endif
如果源文件和目标文件都在源目录中,那么您真正想写的Makefile就是。不再有前缀;vpath
负责$(SRC)/
前缀,而$(BIN)/
是无用的,因为当使用Makefile的这一部分时,我们已经在$(BIN)
中了。
注意:我对Windows及其各种命令行接口一无所知,所以可能有一些东西需要调整(例如,反斜杠而不是斜杠(。