假设您在构建中需要的 C 源文件是bar.c,
并且它有一个相关的头文件bar.h 你是
#include-ing in list.cpp,并且您已正确编码 extern C
bar.h 中的样板。
那么下面的makefile就会满足你的需要:
生成文件
CXX_SOURCES := list.cpp
C_SOURCES := bar.c
OBJECTS = $(C_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o)
CXXFLAGS := -g -Wall -std=c++11
CFLAGS := -g -Wall
CPPFLAGS :=
LDFLAGS :=
LDLIBS :=
EXEC := a.out
.PHONY: all clean test
all: $(EXEC)
test: $(EXEC)
./$<
$(EXEC): $(OBJECTS)
$(CXX) $(LDFLAGS) $^ -o $@ $(LDLIBS)
list.o: bar.h
clean:
rm -f $(EXEC) *.o
这里有很多学习点:
1. 使用立即评估 (:=) 而不是递归评估 (=)
除非您特别想要递归评估,否则请制作变量。看
6.2 The Two Flavors of Variables
2. 如果目标只是任务的名称而不是文件名
任务将创建,然后它是phony target
你应该告诉make这是一个虚假的目标,比如:
.PHONY: all clean test
3. 构建程序的make-recipe运行程序是不正常的
好吧,就像你的:
@$(CXX) $(OBJECTS) -o $@ && $(EXEC)
您并不总是想运行一个程序,因为您已经构建了它,并且
如果程序是长期运行的或交互式的,那么这种方法
会让构建程序变得不切实际。
您可能希望运行程序以测试它是否已正确构建。
但是构建是一项任务,测试是另一项任务(可能需要更长的时间和
涉及额外资源);所以你应该提供一个单独的虚假目标
供测试用。我在这个 makefile 中将其称为 test:通常称为 check。
要构建程序而不对其进行测试,只需运行make。为了测试它,
运行make test - 这将也(重新)构建程序如果需要(重新)构建。
4. 您无需编写规则即可从name.cpp 生成name.o,或者
从name.c 生成name.o 的规则。 GNU make 有 builtin rules 做
这是正确的,只要你正确设置了make-variables
make 在这些内置规则中使用:
-
CC:调用 C 编译或链接的命令,例如gcc
-
CXX:调用 C++ 编译或链接的命令,例如g++
-
CFLAGS: C 编译选项
-
CXXFLAGS:C++ 编译选项
-
CPPFLAGS:C/C++ 预处理器的选项
5.另外两个具有传统含义的重要生成变量是:
-
LDFLAGS:链接选项,不包括库 (-l) 选项
-
LDLIBS:用于链接的库选项 (-l)。
在上面的简单makefile中,CPPFLAGS、LDFLAGS和LDLIBS不是
需要并且可以省略。相反,我为它们分配了空值
只是为了说明它们的用途。
6. 生成文件应该有一个虚假目标clean 可以删除所有文件
makefile 可能已经创建,所以make clean 让你
准备好从头开始构建任何东西。
7.。如果name.o是从name.c或name.cpp编译而来,那么
当然name.o 取决于name.c|name.cpp,但它也取决于
在由 name.c|name.cpp 包含的每个头文件上,以及
makefile 需要表达所有这些依赖关系才能可靠地工作。所以
在这种情况下,您(至少)需要规则:
list.o: bar.h
因此,如果您更改 bar.h 然后 make 将看到 foo.o 不在
日期并将执行其重新制作foo.o的配方。当你
开始构建复杂的程序,这对您来说将变得不切实际
自己弄清楚所有这些头文件依赖项:那么你需要
了解自动依赖生成。
这里是the GNU Make manual