Makefile 速查备忘
Makefile 是在 linux/unix 系统中用于自动化构建项目的工具,简化手动进行编译和链接的过程。相比于 CMake,Makefile 更加轻量和直接,并且语法更加简单易学。
Makefile 语法速成¶
以下是一个 Makefile 的简单示例:
# compilor and flags
CXX = g++
CXXFLAGS = -lpthread
# source files and target executable
SRC = hello.cpp
TARGET = hello
# default target
all: $(TARGET)
$(TARGET): $(SRC)
$(CXX) -o $@ $^ $(CXXFLAGS)
# clean target
clean:
rm -f $(TARGET)
# debug target
debug: CXXFLAGS += -DDEBUG
debug: clean $(TARGET)
.PHONY: all clean debug
Makefile 由一系列的规则组成,每个规则由三部分组成:
1. 目标:例如 all、$(TARGET)、clean 和 debug。
2. 依赖:位于目标后面的冒号 : 之后,表示生成目标所需的文件或其他目标。如果依赖项是一个其他目标,则会递归地构建该目标。
3. 命令:位于依赖下面的缩进行,表示生成目标所时执行的命令。
Makefile 中可以定义变量,例如 CXX、CXXFLAGS、SRC 和 TARGET,可以通过 $(变量名) 来引用变量。对变量可以使用以下几种赋值方式:
1. =:简单赋值,变量在使用时才会被展开。
2. :=:立即赋值,变量在定义时就会被展开。
3. ?=:条件赋值,只有变量未定义时才会赋值
4. +=:追加赋值,将新值追加到变量的现有值之后。
在规则内,可以使用 $@ 来表示目标文件,$^ 来表示所有的依赖文件,$< 来表示第一个依赖文件。
Makefile 中的第一个目标被认为是默认目标,运行 make 命令时会执行该目标。
.PHONY 用于声明伪目标,表示这些目标不是实际的文件名,避免与同名文件冲突。
在上面的 Makefile 文件中,目标 debug 可以解释为:依赖于变量 CXXFLAGS,并向其追加 -DDEBUG,然后依赖于目标 clean 和 $(TARGET)。此时 $(TARGET) 目标又可以展开为:当 hello.cpp 文件存在时,执行 g++ -o hello hello.cpp -lpthread -DDEBUG 命令来生成可执行文件 hello。
Makefile 中的命令行必须以 Tab 键缩进,而不能使用空格键缩进。否则会报错 Makefile:114: *** 缺失分隔符。 停止。。
在 Makefile 中通过重定向输出字面的 $()¶
需要在 Makefile 中生成另一个 Makefile,遇到的问题是无法重定向输出字面的 $() 至需要生成的 Makefile 中,代码如下:
bitstream-w:
...
echo " $(VIVADO_SETUP) && set DIR_SRC=$$(DIR_SRC)&&set DIR_SYN=$$(DIR_SYN) && \
vivado -mode batch -nojournal -source vivado.tcl -tclargs -top-module $(TOP_MODULE) -board $(BOARD)" >> $(DIR_PROJECT)/temp
...
此时 make bitstream-w 在屏幕上的输出为:
$ make bitstream-w
...
echo " call ...\Xilinx\Vivado\2017.4\settings64.bat && set DIR_SRC=$(DIR_SRC)&&set DIR_SYN=$(DIR_SYN) && \
vivado -mode batch -nojournal -source vivado.tcl -tclargs -top-module top -board ..." >> .../temp
/bin/sh: 1: DIR_SRC: not found
/bin/sh: 1: DIR_SYN: not found
...
然而在重定向输出时,最后一个 echo 中的 $(DIR_SRC) 与 $(DIR_SYN) 会被尝试替换为变量实际的值,而非预期的字面量 $(DIR_SRC) 与 $(DIR_SYN)。
如果在 Makefile 中改用 $$$$(DIR_SRC), make bitstream-w 在屏幕上的输出为:
$ make bitstream-w
...
echo " call ...\Xilinx\Vivado\2017.4\settings64.bat && set DIR_SRC=$$(DIR_SRC)&&set DIR_SYN=$$(DIR_SYN) && \
vivado -mode batch -nojournal -source vivado.tcl -tclargs -top-module top -board ..." >> .../temp
...
在重定向输出时,最后一个 echo 中的 $$(DIR_SRC) 与 $$(DIR_SYN) 中的 $$ 会被替换为 shell 进程的 PID,不能够在生成的 Makefile 文件中获得预期的字面量 $(DIR_SRC) 与 $(DIR_SYN)。
解决方案是使用单引号 '' 而非双引号 ""。
原因是 make 不会尝试解析命令行,因此 make 对 "$name" 和 "$name" 的处理是一样的:
在这两种情况下,$name 都将被替换为 Makefile 中变量 $(name) 的值。在这两种情况下,shell 都不会看到 $。
如果要在 Makefile 的命令中插入字面量 $,则需要写入 $$。
然而 shell 对单引号 ''与双引号 "" 的处理是不同的:
echo '$a'将显示字面量$a。echo "$a"会回显环境变量a的值。
因此,需要同时考虑 make 的扩展规则和 shell 的扩展规则,make 的扩展规则使得在这里的 $ 应该被替换为 $$,而 shell 扩展规则则要求命令行参数包含在单引号中。
因而上面的 Makefile 语句应该改写为:
bitstream-w:
...
# use single quotes rather than double quotes
echo ' $(VIVADO_SETUP) && set DIR_SRC=$$(DIR_SRC)&&set DIR_SYN=$$(DIR_SYN) && \
vivado -mode batch -nojournal -source vivado.tcl -tclargs -top-module $(TOP_MODULE) -board $(BOARD)' >> $(DIR_PROJECT)/temp
...
参见:
https://stackoverflow.com/questions/22825640/simple-echo-in-makefile-escape-issue