十、Make 简介


一、Make 简介

  • 编译(compile): 代码变成可执行文件,例如 gcc(C)、g++(C++)
  • 构建(build):
    • 编译众多相互关联的源码文件(且可以只对在上次编译后修改过的部分进行编译),以实现工程化的管理
    • Make 是最常用的构建工具,通常构建规则写在 Makeflie 中(要构建哪个文件、它依赖哪些源文件,当那些文件有变动时,如何重新构建它)
    • 语法:make -f target,其中 target 为编译目标(makefile 文件)
  • make 重新编译的原理:
    • 编译文件时make检查其目标文件并比较文件的更新时间戳
    • 如果源文件比目标文件具有更新的时间戳,则假定源文件已更改,那么 make 会生成新的目标文件,否则不会重新编译

二、Makefile 文件的格式

1、概述

  • Makefile 文件由一系列规则构成,每条规则的形式如下:
    • 第一行:冒号前面的部分,叫做目标,冒号后面的部分叫做前置条件
    • 第二行:必须由一个 tab 键开始(可以用 Notepad++ 空格转 Tab),后面跟着需要执行的命令
    • 目标是必需的,不可省略,前置条件和命令是可选的,但是两者之中必须至少存在一个
# 每条规则的格式
<target>: <prerequisites> 
[tab]  <commands>

# eg:
c.txt: a.txt b.txt
    cat a.txt b.txt > c.txt
    
# 井号(#)在 Makefile 中表示注释

2、目标(target)

  • 一个目标就构成一条规则,目标通常是需要构建对象的文件名(可以是多个)
  • 伪目标:可以不是文件名,而是某个操作的名字
# 伪目标:删除对象文件,常见的有 all、install、clean
clean:
	rm *.o

# 终端执行命令
make -f makefile clean

# 常见的伪目标命令
# make all: It compiles everything so that you can do local testing before installing applications.
# make install:It installs applications at right places.
# make clean:It cleans applications, gets rid of the executables, any temporary files, object files, etc

3、前置条件(prerequisites)

  • 前置条件通常是一组文件名,之间用空格分隔;若目标后面没有前置条件,意味着它跟其他文件都无关
  • 前置条件指定了目标是否重新构建的判断标准:只要有一个前置文件不存在,或者有过更新,目标就需要重新构建

4、命令(commands)

  • 构建目标的具体指令,它的运行结果通常就是生成目标文件
  • 指令:由一行或多行的 Shell 命令组成,每行命令在一个单独的 shell 中执行(不同的进程中执行)
  • Note:每行命令之前必须有一个 tab 键;命令的前面加上@,可以关闭屏幕打印当前命令或注释
# 取不到 foo 的值,因为两行命令在两个不同的进程执行
var-lost:
    export foo=bar
    echo "foo=[$$foo]"

# 解决办法 1:两行命令写在一行,中间用分号分隔
var-kept:
    export foo=bar; echo "foo=[$$foo]"

# 解决办法 2:加换行符
var-kept:
    export foo=bar; \
    echo "foo=[$$foo]"

# 解决办法 3:加 .ONESHELL: 命令
.ONESHELL:
var-kept:
    export foo=bar; 
    echo "foo=[$$foo]"

# 关闭屏幕命令输出
all:
    @# 这是测试
    @echo TODO

# .cpp 文件创建 .o 文件(隐式规则,未提到特定的目标)
.cpp.o:
   $(CC) $(CFLAGS) -c $<

# 等同于下面的写法
.cpp.o:
   $(CC) $(CFLAGS) -c $*.cpp

三、Makefile 文件的语法

1、通配符和模式匹配(待扩展,bash 通配符)

  • Makefile 的通配符与 Bash 一致,主要有星号×、问号?和点号. ,eg: ×.o 表示所有后缀名为 o 的文件
  • Make 命令允许对文件名进行类似正则运算的匹配,主要用到的匹配符是 %
# 假定当前目录下有 f1.c 和 f2.c 两个源码文件,需要将它们编译为对应的对象文件
%.o: %.c

# 等同于下面的写法,使用匹配符 %,可以将大量同类型的文件,只用一条规则就完成构建
f1.o: f1.c
f2.o: f2.c

2、宏定义(变量替换)

宏在 Makefile 中使用 名称 = 值 的形式来定义

a、特殊的宏

  • $@:make 命令当前构建的那个目标,即每条规则中 target 的名字
  • $?:the names of the changed dependents,代表前置条件中所有已修改的源文件
  • $<:the name of the related file that caused the action,通常指代第一个前置条件,比如,规则为 t: p1 p2,那么$< 就指代p1
  • $*:the prefix shared by target and dependent files,通常指代匹配符 % 匹配的部分, 比如 % 匹配 f1.txt 中的 f1 ,$* 就表示 f1
  • $^:指代所有前置条件,比如,规则为 t: p1 p2,那么 $^ 就指代 p1 p2
# $@ represents hello and $? or $@.cpp picks up all the changed source files
hello: main.cpp hello.cpp factorial.cpp
   $(CC) $(CFLAGS) $? $(LDFLAGS) -o $@

# 等同于下面的写法
hello: main.cpp hello.cpp factorial.cpp
   $(CC) $(CFLAGS) $@.cpp $(LDFLAGS) -o $@


# $< 的用法
a.txt: b.txt c.txt
    cp $< $@

# 等同于下面的写法
a.txt: b.txt c.txt
    cp b.txt a.txt 

b、常规宏

  • CC:编译 C 语言文件的程序,默认是 cc
  • CXX:编译 C++ 文件的程序,默认是 g++
  • CPP:运行 C 预处理器并输出到当前输出流的程序,默认是 $(CC) -E
  • PWD:获取当前路径
  • LD_LIBRARY_PATH:是 Linux 环境变量名,该环境变量主要用于指定查找共享库(动态链接库)时除了默认路径之外的其他路径
    • 临时修改:export LD_LIBRARY_PATH=/usr/local/cuda-9.0/lib64:$LD_LIBRARY_PATH
    • 永久生效:可用 vim /etc/profile ,修改其中的 LD_LIBRARY_PATH 变量
    • LIBRARY_PATH is used by gcc before compilation to search for directories containing libraries that need to be linked to your program
    • LD_LIBRARY_PATH is used by your program to search for directories containing the libraries after it has been successfully compiled and linked
  • 常见宏变量的附加参数:
    • CFLAGS:传递给 C 编译器的额外 flag
    • CXXFLAGS:传递给 C++ 编译器的额外 flag
    • LDFLAGS:Extra flags to give to compilers when they are supposed to invoke the linker, ld
    • CPPFLAGS:Extra flags to give to the C preprocessor and programs, which use it

3、赋值运算符 (=、:=、?=、+=)

# 在执行时扩展,允许递归扩展
VARIABLE = value

# 在定义时扩展
VARIABLE := value

# 将值追加到变量的尾端
VARIABLE += value

# 只有在该变量为空时才设置值
VARIABLE ?= value

四、Makefile 常用的三个函数(wildcard/notdir/patsubst)

  • wildcard:扩展通配符函数,src=$(wildcard *.c),匹配所有 .c 文件,列表赋值给 src,保留了路径
  • notdir:去掉目标的路径函数,dir=$(notdir $(wildcard ./sub/*.c)),去除掉 ./sub/ 路径,只保留源代码名称
  • patsubst:替换通配符函数,obj=$(patsubst %.c, %.o, $(wildcard *.c)),将 .c 文件替换成 .o
src=$(wildcard *.c ./sub/*.c)
dir=$(notdir $(src))
obj=$(patsubst %.c, %.o, $(dir))

all:
	@echo $(src)  # 注意 TAB 键,@echo 和echo 的区别,前者会隐藏命令,后者会输出命令

tdir:
	@echo $(dir)

tobj:
	@echo $(obj)

在这里插入图片描述


五、参考资料

1、Make 命令教程
2、Makefile 基础教程
3、跟我一起写Makefile
4、Makefile中的 patsubst 和 wildcard 函数

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页