首页 > 其他分享 >最全个人笔记【Makefile工程管理】

最全个人笔记【Makefile工程管理】

时间:2024-08-01 20:53:46浏览次数:14  
标签:文件 变量 TEXT 最全 Makefile 笔记 编译 make

1. 基本概念

1.1 make是什么

当一个项目中要编译的文件很多时,手工使用编译器一个个进行编译,很明显不具有可操作性,此时必须借助某些软件,协助我们有序地、正确地自动编译整个工程的所有该编译的文件。这样的软件被称为 工程管理器make 就是一款工程管理器软件。

1.2 Makefile是什么

make 正常工作时,会读取一个称为 Makefile 的配置文件,该配置文件可以为 make 指明细致的工作规则,比如所使用的工具链、要编译的目标文件名称、要递归编译的子文件夹路径等等。

对工程管理器软件的学习,主要就是对其配置文件 Makefile 的语法的学习。

1.3 Makefile在哪里

Makefile 是用来指导make对源代码进行编译的,因此在一个多目录结构的工程项目中,凡是有源码出现的目录,都会有一个 Makefile 去管理,而所有的 Makefile,都通过工程项目顶层目录下的 Makefile 去直接或简洁调用。

2. 目标与依赖

目标和依赖是 Makefile 语法中最基本的概念,假设有一个源文件 a.c,编译生成 a.o ,那么前者是依赖,后者 a.o 是目标,但进一步将 a.o 编译成可执行文件 a,那么 a.o 此时就变成依赖,最终的文件 a 是目标,因此目标和依赖是相对的概念。

在这里插入图片描述

目标和依赖

Makefile 中,使用冒号来区隔它们:

# 目标:依赖
a.o:a.c

# 目标:依赖列表c
image:a.o b.o c.o d.o

3. 规则

在目标与依赖下面,使用一种特殊的语法 "<tab>语句" 来构成一个规则,比如:

# 一套规则:
a.o:a.c c
    gcc a.c -o a.o -c -fPIC  # 行首必须是制表符tab

请注意:在上述语句中,目标与依赖、命令共同构成了一个规则,命令的行首必须是制表符 tab 键,不能是空格,否则会报错。另外,命令可以是多行:

image:a.o b.o c.o d.o 
    gcc a.c -o a.o -c -fPIC
    gcc b.c -o b.o -c -fPIC
    gcc c.c -o c.o -c -fPIC
    gcc d.c -o d.o -c -fPIC
    gcc a.o b.o c.o d.o -o image

重点:规则中的各个命令什么时候被执行?

  • 当目标文件不存在时。
  • 当目标文件存在,但时间戳比依赖列表中的某一文件旧时。

因此,当目标文件已经被编译且其依赖文件没有修改,那么再次执行make就不会触发任何动作,这就是make和 Makefile 的最基本的逻辑:只在有需要的时候编译,尽量提高编译效率。

「课堂练习1」

编写一个最简单的 Makefile,管理一个单一源码文件。

在这里插入图片描述

4. 终极目标

在一个 Makefile 中,可以有多套规则,也就说可以有多个目标,在这多个目标中,最先出现的被称为终极目标,它是执行make时默认的目标,比如:

a:a.c
    gcc a.c -o a

b:b.c
    gcc b.c -o b

以上 Makefile 中,a是终极目标,b不是,因此直接执行make时,只会针对第一套规则进行推导:

gec@ubuntu:~$ ls
a.c b.c Makefile
gec@ubuntu:~$ make
gcc a.c -o a

要执行第二套规则,则需要在执行make命令时特意指定,比如:

gec@ubuntu:~$ make b
gcc b.c -o b

或令其间接依赖于终极目标,比如:

a:a.c b
    gcc a.c -o a

b:b.c
    gcc b.c -o b

执行结果是:

gec@ubuntu:~$ make
gcc b.c -o b
gcc a.c -o a

5. 多目标编译

从上述第4小节可见, Makefile 中可以通过目标的相互依赖来递推整条编译链,当然像上述那样将a强行依赖于b并不是一种可取的做法,因为这么做虽然可以达到目的,但在逻辑上却让人陷入困惑,毕竟在上述例子中,a和b是两个不相干的程序,他们之间并没有依赖关系。

对于这种多目标编译,更传统的做法是,虚构一个被大家共同依赖的伪目标,利用 Makefile 编译链自动编译所有的目标,比如:

all:a b

a:a.c
    gcc a.c -o a

b:b.c
    gcc b.c -o b

执行结果是:

gec@ubuntu:~$ make
gcc a.c -o a
gcc b.c -o b

6. 隐式规则

Makefile 会根据目标和依赖简单地自动推导出编译语句,这种情况叫隐式规则,比如:

all:a b

在上述 Makefile 中,没有任何编译语句,甚至连a和b的依赖文件都没写,但这个 Makefile 可以正常执行:

gec@ubuntu:~$ make
cc a.c -o a
cc b.c -o b

此时,Makefile 的执行逻辑是:监测到终极目标的依赖文件a和b不存在,就会自动寻找以a和b为目标的规则,在本文件中没有,然后就会尝试在本目录中寻找 a.c 和 b.c ,如果找到了就以它们为依赖文件,自动编译它们,这个过程就是隐式规则。

注意到,隐式规则可以帮忙处理一些比较简单地编译,它要求目标文件和依赖文件同名(除了后缀不同),不支持多文件编译,也不支持个性化编译选项。

7. 伪目标

由于有隐式规则的存在,因此伪目标在某些极端情况下可能会被误编译,比如上述例子中,all 是伪目标,不是真正要编译生成的目标,但如果源码目录中恰巧有一个文件叫 all.c ,那么根据 Makefile 的隐式规则,将会触发 all.c 的编译动作。

如何规避隐式规则这种误操作呢?很简单,明确告诉 Makefileall是伪目标,不要编译他:

all:a b

.PHONY:all

上述语句中,.PHONYMakefile 的一个关键字,用来声明伪目标,防止隐式规则滥用。

Makefile 中,常见的伪目标除了all之外,还有clean、distclean等,用来清除生成的中间文件,例如:

all:a b

clean:  # 清除所有目标文件、可重定位文件
    rm a b *.o

distclean:clean  # 先执行clean,然后清除所有交换文件、核心转储文件
    rm .*.sw? core

.PHONY:all clean

动态库

​ 动态库的作用是加密某个文件,外界无法查阅此源码,起到保护核心程序作用。

​ 一般是将模块功能文件生成动态库,不能将main.c生成动态库

动态库有两版本,一版本是windows:以.dll结尾 二版本是linux:以libxxx.so

如何生成动态库

生成动态库步骤如下

gcc xxx.c -shared -fPIC -o libxxx.so

编译程序

​ 版本一:

gcc main.c libxxx.so -o main

版本二

gcc main.c -o main -I../include -L../lib -ladd -lsub
说明 : -I ../include 查找头文件所在的目录在上一级的include目录里面
       -L 指定动态库的路径
       -l 指定需要编译的动态库库名

执行程序

./main

在这里插入图片描述

解决方法:

将 报错的动态库文件拷贝到/lib
例如:
sudo cp ../lib/libadd.so /lib
最后:重新执行程序
./main

工程结构如下:

在这里插入图片描述

1. 自定义变量

类似于shell脚本,可以在 Makefile 定义变量和引用变量:

BIN=a b

all:$(BIN)

clean:
    rm $(BIN)

2. 内置变量

Makefile有许多跟编译相关的内置变量,比如:

CFLAGS  = "-O2 -Wall" # C编译选项
LDFLAGS = "-lpthread -ladd -lsub" # 链接器参数
CC  = aarch-linux-gnu-gcc # C编译器名称,编译c文件
CXX = aarch-linux-gnu-g++ # C编译器名称,编译c++文件

可以通过修改上述变量的值,来个性化各种编译场景,例如:

CC = gcc
CXX = g++

CFLAGS = -Wall

LDFLAGS = -ladd

all:main b a #生成的目标文件

main:main.c
	$(CC) main.c -o main $(CFLAGS) -L./ $(LDFLAGS)
b:b.c
	$(CC) b.c -o b $(CFLAGS)
a:a.cpp
	$(CXX) a.cpp -o a

# 清除可执行程序
clean:
	rm main b a .*.sw? core
# 将all设置为伪目标,就是不要编译all.c clean.c
.PHONY:all clean

3. 变量的定义引用

所谓定义引用,指的是在定义一个变量A的时候,其值引用了另一个变量B,例如:

A = China
B = I love $(A)

all:
    echo $(B)

执行结果:

gec@ubuntu:~$ make
echo I love China
I love China

3.1 全文搜索模式

注意到,上述变量A和B的定义,可以任意调换其顺序,比如:

B = I love $(A) # 照样可以引用出现在后面的变量A的值

all:
    echo $(B)

A = China

这不会有任何影响,这是因为 Makefile 中直接用等号 “=” 定义变量时若存在对其他变量的引用,会采取全文搜索的策略去找引用值。

3.2 简单定义模式

如果不想要 Makefile 的这种全文搜索的特性,而希望只引用定义语句之前出现过的变量的值的话,就要用 “:=” 简单模式,例如:

B := I love $(A) # 只引用在此之前有定义的A的值

all:
    echo $(B)    # 输出"I love"

A = China

3.3 变量追加

Makefile 中的变量都是字符串,可以使用 “+=” 进行追加,例如:

CFLAGS  = -O2    # 全文搜索模式
CFLAGS += -Wall
CFLAGS += -Werror

# 等价于
CFLAGS = -O2 -Wall -Werror

3.4 变量值修改

Makefile 中的变量本质是一连串单词,通常是待处理的一系列文件名称,在实际操作中经常需要对这些文件名进行模式替换,比如有一串由C语言源文件组成的字串,希望将其中的文件后缀 *.c 变成 *.o,可以这么做:

A = srt.c string.c tcl.c
B = $(A:%.c=%.o)  # 此处,变量B的值是 srt.o string.o tcl.o
A = str.c string.c tcl.c                                                                 B = $(A:%.c=%.o)
all:
	echo $(B)                   

4. override

在执行make时,通常可以在命令行中携带一个变量的定义,如果这个变量跟Makefile中出现的某一变量重名,那么命令行变量的定义将会覆盖Makefile中的变量。就是说,对于一个在Makefile中使用常规方式(使用“=”、“:=”或者“define”)定义的变量,我们可以在执行make时通过命令行方式重新指定这个变量的值,命令行指定的值将替代出现在Makefile中此变量的值。比如:

A = an apple tree
all:
	@echo $(A)  # 输出变量A的值,符号@代表不输出命令本身

直接执行 make 的结果是:

gec@ubuntu:~$  make A="an elephant"
an elephant

可见,虽然Makefile定义了A的值为”an apple tree”,但被命令行定义的A的值覆盖了,变成了”an elephant”。如果不想被覆盖,则可以写成:

override A = an apple tree
all:
	@echo $(A)

此时,执行结果是:

gec@ubuntu:~$  make A="an elephant"
an apple tree

但是请注意:指示符 override 并不是用来防止Makefile的内部变量被命令行参数覆盖的,其真正存在的目的是:

  • 为了使用户可以改变或者追加那些使用make的命令行指定的变量的定义。
  • 即:实现了在Makefile中增加或者修改命令行参数的一种机制。

通常,我们会通过命令行来指定一些附加的、个性化的编译参数,而对一些通用的参数或者必需的编译参数,我们则在Makefile中指定,为了使两个地方指定的参数和谐相处,不相互覆盖,一般就用指示符 override 来实现。

例如,无论命令行指定那些编译参数,必须打开所有的编译警告信息“-Wall”,则 Makefile 的变量 CFLAGS 应该这样写:

override CFLAGS += -Wall
test:test.c

执行结果是:

gec@ubuntu:~$ make CFLAGS="-g"
cc -g -Wall a.c -o a

在这里插入图片描述

6. 静态规则与自动化变量

所谓静态规则,就是可以使用模式匹配的方式,自动生成若干规则的机制。例如:

OBJ = a.o b.o c.o

image:$(OBJ)
	$(CC) $(OBJ) -o image

#静态规则
$(OBJ):%.o:%.c  
	$(CC) $^ -o $@ -c # 运用了自动化变量自适应不同的目标和依赖

clean:
	$(RM) $(OBJ) image

.PHONY: clean

在上述静态规则中,从变量OBJ中按模式%.o抽取单词出来,作为新规则的目标,然后又从模式%.c匹配出来的单词,作为新规则的依赖,这样一来就形成了3组目标与依赖:

​ 静态规则生成目标和依赖

最后,每个规则都生成了:

#静态规则
a.o:a.c
	$(CC) $^ -o $@ -c
b.o:b.c
	$(CC) $^ -o $@ -c
c.o:c.c
	$(CC) $^ -o $@ -c

注意到,自动生成的规则中的编译语句包含了自动化变量:

  • ^ :代表所在规则的依赖列表
  • @ :代表所在规则的目标

所谓自动化变量,指的是它们的值会随着规则自动地发生变化,它们的含义是确定的,但是它们的值会自适应不同的规则,这个特性刚好与静态规则自动产生规则像。除了上面两个常见的自动化变量外,还有下述这些自动化变量。

变量名含义备注
@代表其所在规则的目标的完整名称
%代表其所在规则的静态库文件的一个成员名
<代表其所在规则的依赖列表的第一个文件的完整名称
?代表所有时间戳比目标文件新的依赖文件列表 用空格隔开
^代表其所在规则的依赖列表同一文件不可重复
+代表其所在规则的依赖列表同一文件可重复 主要用在程序链接时,库的交叉引用场合。
demo

在这里插入图片描述

在这里插入图片描述

make
make clean

0. Makefile函数

Makefile 中的函数可以实现一些特性的功能,其基本语法是:

VAR = $(函数 参数1[,参数2,参数3,...])

语法要点有:

  • 函数及其参数用 $() 包含
  • 函数与参数之间用空格隔开
  • 若函数需要多个参数,则参数之间用逗号隔开
  • 若函数有返回值,其值可以直接赋值给变量

1. 内置文本处理函数

1.1 $(subst FROM,TO,TEXT)

功能:
  将字符串TEXT中的字符FROM替换为TO。
返回:
  替换之后的新字符串。
范例:

A = $(subst pp,PP,apple tree)

替换之后变量A的值是"aPPle tree"

1.2 $(patsubst PATTERN,REPLACEMENT,TEXT)

功能:
  按照PATTERN搜索TEXT中所有以空格隔开的单词,并将它们替换为REPLACEMENT。注意: 参数PATTERN可以使用模式通配符%来代表一个单词中的若干字符,如果此时REPLACEMENT中也出现%,那么REPLACEMENT中的%跟PATTERN中的%是一样的。
返回:
  替换之后的新字符串。
范例:

A = $(patsubst %.c,%.o,a.c b.c)

替换之后变量A的值是 a.o b.o

1.3 $(strip STRING)

功能:
  去掉字符串中开头和结尾的多余的空白符(掐头去尾),并将其中连续的多个空白符合并为一个。注意: 所谓的空白符指的是空格、制表符。
返回:
  去掉多余空白符之后的新字符串。
范例:

A = $(strip "  apple     tree  ")

处理之后,变量A的值是 apple tree

1.4 $(findstring NEEDLE, HAYSTACK)

功能:
  在给定的字符串HAYSTACK中查找子串NEEDLE。
返回:
  找到则返回NEEDLE,否额返回空。
范例:

A = $(findstring pp, apple tree)
B = $(findstring xx, apple tree)

变量A的值是pp,变量B的值是空。

1.5 $(filter PATTERN,TEXT)

功能:
  过滤掉TEXT中所有不符合给定模式PATTERN的单词。其中PATTERN可以是多个模式的组合。
返回:
  TEXT中所有符合模式组合PATTERN的单词组成的子串。
范例:

A = a.c b.o c.s d.txt
B = $(filter %.c %.o,$(A))

过滤后变量B的值是 a.c b.o

1.6 $(filter-out PATTERN,TEXT)

功能:
  过滤掉TEXT中所有符合给定模式PATTERN的单词,与函数filter功能相反。
返回:
  TEXT中所有不符合模式组合PATTERN的单词组成的子串。
范例:

A = a.c b.o c.s d.txt
B = $(filter %.c %.o,$(A))

过滤后变量B的值是 c.s d.txt

1.7 $(sort LIST)

功能:
  将字符串LIST中的单词按字母升序的顺序排序,并且去掉重复的单词。
返回:
  排完序且没有重复单词的新字符串。
范例:

A = foo bar lose foo ugh
B = $(sort $(A))

处理后变量B的值是 bar foo lose ugh

1.8 $(word N,TEXT)

功能:
  取字符串TEXT中的第N个单词。注意,N必须为正整数。
返回:
  第N个单词(如果N大于TEXT中单词的总数则返回空)。
范例:

A = an apple tree
B = $(word 2 $(A))

处理后变量B的值是 apple

1.9 $(wordlist START,END,TEXT)

功能:
  取字符串TEXT中介于START和END之间的子串。
返回:
  介于START和END之间的子串(如果START大于TEXT中单词的总数或者START大于END时返回空,否则如果END大于TEXT中单词的总数则返回从START开始到TEXT的最后一个单词的子串)。
范例:

A = the apple tree is over 5 meters tall
B = $(wordlist 4,100,$(A))

处理后变量B的值是 is over 5 meters tall

以上9个函数是make内嵌的的文本处理函数。在书写Makefile时可搭配使用,来实现复杂功能。

2. 文件名处理函数

GNU make除了这些内嵌的文本处理函数之外,还存在一些针对于文件名的处理函数。这些函数主要用来对一系列空格分割的文件名进行转换,这些函数的参数被作为若干个文件名来对待,函数对这样的一组文件名按照一定方式进行处理,并返回以空格分隔的多个文件名序列。他们是:

2.1 $(dir NAMES)

功能:
  取文件列表NAMES中每一个路径的目录部分。
返回:
  每一个路径的目录部分组成的新的字符串。
范例:

A = /etc/init.d /home/gec/.bashrc /usr/bin/man
B = $(dir $(A))

处理后变量B的值是"/etc/ /home/gec/ /usr/bin/"

2.2 $(notdir NAMES)

功能:
  取文件列表NAMES中每一个路径的文件名部分。
返回:
  每一个路径的文件名部分组成的新的字符串。注意: 如果NAMES中存在不包含斜线的文件名,则不改变这个文件名,而以反斜线结尾的文件名,用空串代替。
范例:

A = /etc/init.d /home/vincent/.bashrc /usr/bin/man
B = $(dir $(A))

处理后变量B的值是"init.d .bashrc man"

2.3 $(suffix NAMES)

功能:
  取文件列表NAMES中每一个路径的文件的后缀部分。后缀指的是最后一个.后面的子串。
返回:
  每一个路径的文件名的后缀部分组成的新的字符串。
范例:

A = /etc/init.d /home/vincent/.bashrc /usr/bin/man
B = $(suffix $(A))

处理后变量B的值是".d .bashrc"

2.4 $(basename NAMES)

功能:
  取文件列表NAMES中每一个路径的文件的前缀部分。前缀指的是最后一个.后面除了后缀的子串。
返回:
  每一个路径的文件名的前缀部分组成的新的字符串。
范例:

A = /etc/init.d /home/vincent/.bashrc /usr/bin/man
B = $(basename $(A))

处理后变量B的值是"/etc/init /home/vincent/ /usr/bin/man"

2.5 $(addsuffix SUFFIX,NAMES)

功能:
  为文件列表NAMES中每一个路径的文件名添加后缀SUFFIX。
返回:
  添加了后缀SUFFIX的字符串。
范例:

A = /etc/init.d /home/gec/.bashrc /usr/bin/man
B = $(addsuffix .bk,$(A))

处理后B为"/etc/init.d.bk /home/gec/.bashrc.bk /usr/bin/man.bk"

2.6 $(addprefix PREFIX,NAMES)

功能:
  为文件列表NAMES中每一个路径的文件名添加前缀PREFIX。
返回:
  添加了前缀PREFIX的字符串。
范例:

A = /etc/init.d /usr/bin/man
B = $(addprefix host:,$(A))

处理后B的值为:
host:/etc/init.d host:/usr/bin/man

2.7 $(wildcard PATTERN)

功能:
  获取匹配模式为PATTERN的文件名。
返回:
  匹配模式为PATTERN的文件名。
范例:

A = $(wildcard *.c)

假设当前路径下有两个.c文件a.c和b.c,则处理后A的值为:“a.c b.c

2.8 $(foreach VAR,LIST,TEXT)

功能:
  首先展开变量“VAR”和“LIST”,而表达式“TEXT”中的变量引用不被展开。执行时把“LIST”中使用空格分割的单词依次取出赋值给变量“VAR”,然后执行“TEXT”表达式,重复直到“LIST”的最后一个单词(为空时结束)。
它是一个循环函数,类似于Linux的Shell中的循环。注意:由于“TEXT”中的变量或者函数引用在执行时才被展开,因此如果在“TEXT”中存在对“VAR”的引用,那么“VAR”的值在每一次展开式将会到的不同的值。
返回:
  以空格分隔的多次表达式“TEXT”的计算的结果。
范例:
假设当前目录下有两个子目录 dir1/dir2/,先要将他们里面的所有文件赋值给变量FILES,可以这么写:

gec@ubuntu:~$ tree
.
├── dir1/
│   ├── file1
│   └── file2
├── dir2/
│   ├── a.c
│   └── b.c
└── Makefile
# Makefile
DIR = dir1/ dir2/
FILES = $(foreach dir,$(DIR),$(wildcard $(dir)/*))

all:
    @echo $(FILES)

执行结果如下:

gec@ubuntu:~$ make
dir1/file1  dir1/file2  dir2/a.c  dir2/b.c
作业:

创建Makefile管理
在这里插入图片描述
在这里插入图片描述

swap.h

#ifndef SWAP_H
#define SWAP_H

void swap(int *a, int *b);

#endif // SWAP_H

main.c

#include <stdio.h>
#include "swap.h"

int main() {
    int a = 10;
    int b = 20;

    printf("交换前: a = %d, b = %d\n", a, b);
    swap(&a, &b);
    printf("交换后: a = %d, b = %d\n", a, b);

    return 0;
}

swap.c

#include "swap.h"

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

Makefile

# 指定编译器
CC = gcc

# 指定编译选项
CFLAGS = -Wall -fPIC -Iinc

# 指定目标文件和源文件目录
SRCDIR = source
INCDIR = inc
LIBDIR = lib
BINDIR = bin

# 指定输出的可执行文件
TARGET = $(BINDIR)/a.out
LIBTARGET = $(LIBDIR)/libswap.so

# 找到所有源文件
SRCS = $(wildcard $(SRCDIR)/*.c)

# 将源文件名转换为对象文件名
OBJS = $(SRCS:$(SRCDIR)/%.c=$(BINDIR)/%.o)

# 默认目标
all: $(TARGET)

# 链接可执行文件
$(TARGET): $(OBJS) $(LIBTARGET) | $(BINDIR)
	$(CC) $(CFLAGS) -o $@ $(OBJS) -L$(LIBDIR) -lswap

# 编译共享库
$(LIBTARGET): $(BINDIR)/swap.o | $(LIBDIR)
	$(CC) -shared -o $@ $<

# 编译对象文件
$(BINDIR)/%.o: $(SRCDIR)/%.c | $(BINDIR)
	$(CC) $(CFLAGS) -c $< -o $@

# 创建bin和lib目录
$(BINDIR):
	mkdir -p $(BINDIR)

$(LIBDIR):
	mkdir -p $(LIBDIR)

# 清理
clean:
	rm -f $(BINDIR)/*.o $(TARGET) $(LIBTARGET)

.PHONY: all clean

1. 嵌套Makefile

在多目录结构中,Makefile可以通过内置命令嵌套调用。例如有如下目录结构:

gec@ubuntu:~$ tree
.                   
├── dir/
│   └── Makefile   # 子Makefile
└── Makefile       # 顶层Makefile

要在顶层 Makefile 中调用子 Makefile ,只需执行如下语句:

all:
    $(MAKE) -C dir/  # 调用指定目录下的子Makefile

2. 变量导出

在嵌套调用子 Makefile 的过程中,如果需要将变量传递给子 Makefile ,可以使用如下语句:

# 顶层Makefile
export A = apple    # 在顶层Makefile中,将变量A导出
B = banana          # 在顶层Makefile中,变量B未导出

all:
	@echo "rank 1: $(A)"
	@echo "rank 1: $(B)"
	@$(MAKE) -C dir/ # 调用位于dir/中的子Makefile
all:
	@echo "rank 2: $(A)" # 从顶层Makefile获得变量的值
	@echo "rank 2: $(B)" # 空值

执行结果是:

gec@ubuntu:~$ make
rank 1: apple
rank 1: banana
make[1]: Entering directory '/home/gec/dir'
rank 2: apple
rank 2:
make[1]: Leaving directory '/home/gec/dir'

3. 实用make选项

3.1 强制重建所有规则中目标

make -B
make --always-make

3.2 指定Makefile的所在路径

make -C dir/
make --directory=dir/

假如要执行的Makefile文件不在当前目录,可以使用该选项指定。这个选项一般用在一个Makefile内部调用另一个子Makefile的场景

Makefile的详细的语法非常多,本章只是一个引子,但对于认识Makefile以及做一些基本的应用足已,毕竟复杂的Makefile都不可能手工写,而是会用其他的软件自动生成的。

在嵌套调用子 Makefile 的过程中,如果需要将变量传递给子 Makefile ,可以使用如下语句:

# 顶层Makefile
export A = apple    # 在顶层Makefile中,将变量A导出
B = banana          # 在顶层Makefile中,变量B未导出

all:
	@echo "rank 1: $(A)"
	@echo "rank 1: $(B)"
	@$(MAKE) -C dir/ # 调用位于dir/中的子Makefile
all:
	@echo "rank 2: $(A)" # 从顶层Makefile获得变量的值
	@echo "rank 2: $(B)" # 空值

执行结果是:

gec@ubuntu:~$ make
rank 1: apple
rank 1: banana
make[1]: Entering directory '/home/gec/dir'
rank 2: apple
rank 2:
make[1]: Leaving directory '/home/gec/dir'

3. 实用make选项

3.1 强制重建所有规则中目标

make -B
make --always-make

3.2 指定Makefile的所在路径

make -C dir/
make --directory=dir/

假如要执行的Makefile文件不在当前目录,可以使用该选项指定。这个选项一般用在一个Makefile内部调用另一个子Makefile的场景

Makefile的详细的语法非常多,本章只是一个引子,但对于认识Makefile以及做一些基本的应用足已,毕竟复杂的Makefile都不可能手工写,而是会用其他的软件自动生成的。

点击下载官方文档:[GNU make中文手册.pdf](http://vm.yueqian.com.cn:8886/group1/M00/00/09/rBJlJmKeBnaAFF4cABSX3jQpiOc925.pdf?token=null&ts=null&filename=GNU make中文手册.pdf)

标签:文件,变量,TEXT,最全,Makefile,笔记,编译,make
From: https://blog.csdn.net/qq_58204972/article/details/140857026

相关文章

  • Linux基础笔记
    快捷键的使用1、终端操作打开终端(图像化界面)1.鼠标右击+E键(先后按键)2.ctrl键+shift键+t键打开多个终端2、什么是Linux终端?Linux终端又称为什么?Linux终端也称为虚拟控制台,是Linux从UNIX继承来的标准特性。显示器和键盘合称为终端,因为它们可以对系统进行控制,所以又......
  • PCIe学习笔记(11)
    TPH规则•TPH指定了两种格式。所有提供TPH的请求都必须使用Baseline(基线)TPH格式。带有可选TPHTLP前缀的格式扩展了TPH字段,为SteetingTag(转向标签,ST)字段提供了额外的位,此时,TLPheaderByte0-3如下图。•可选的TPHTLPPrefix用于扩展TPH字段。◦TPHTLP前缀的存在是......
  • PCIe学习笔记(14)
    Vendor_Defined消息Vendor_DefinedMessages允许扩展PCIExpress消息传递功能,既可以作为PCIExpress规范的一般扩展,也可以作为特定于供应商的扩展。此处定义与这些消息关联的规则。MessageCode数量有限,PCIE协议定义了VDM(VendorDefinedMessage),以此来扩展Message种类。......
  • Living-Dream 系列笔记 第71期
    众所周知,换根dp是非常套路的。换根真好玩(换根dp:当不同节点作为根时,dp结果不一致,若枚举每个节点作为根,则时间复杂度过高,在此种情形下,可使用换根dp处理相邻两节点间的贡献,从而达到快速换根的效果。使用场景:对于一棵树,寻找以某节点\(u\)为根时取得的最大值/最小值......
  • 旧笔记本安装Win8.1实录
    昨天发现一台尘封已久的LenovoideapadY550,给它装上了Windows10然后第二天系统挂掉了挂的原因是半夜万恶之源Windows更新开始造孽了刚好没电文件全坏了真解除封印因为文件已经没了我索性直接重装系统,降级到Win8.1真香!系统是Win8.1withupdate的精简版,开始菜单有关......
  • [学习笔记] 斜率优化
    引入斜率优化用于求解类似于\(f_i=f_j+a_ib_j+c_i\)使\(f_i\)最大或最小之类的形式的DP转移,标志就是其中有一项(如\(a_ib_j\))与\(i,j\)均有关联。求解令\(j\)为\(i\)的最优决策点,也就是\(f_i=f_j+a_ib_j+c_i\),我们将其进行一些移项可以得到\(f_j=-......
  • Pytorch笔记|小土堆|P5-6|Dataset类
    Dataset类作用:模型的数据集接口__init__将对象实例化,创建对象时obj=class(...,...)会立即被调用,需要提供(输入)类中使用到的变量。__getitem__通过img,label=obj[idx]获取(返回)每一个数据和label__len__通过len(obj)获取(返回)数据量点击查看代码fromtorch.utils.dataim......
  • 学习笔记第十六天
    1.结构体        1.1结构体的定义        结构体(Struct)是C语言中一种重要的复合数据类型,允许将不同类型的数据项组合成一个单一的类型。定义结构体使用struct关键字,其基本语法为:structStudent{         成员列表;};         //;不......
  • Obsidian学习笔记-界面图标介绍(上)
     背景打开Obsidian,会看到界面是极简画风,初学者或许难以弄清界面边框上诸多小图标的含义,本文将详细介绍。(发现有点多,遂分量篇分享)一、功能页(左)这里用功能页代指页面左侧第一栏,这块也是Obsidian的功能标密集区。这里按照下面第一张图的划分,分区讲解。(1号下方目录上方......
  • Pixel Aligned Language Models论文阅读笔记
    Motivation&Abs近年来,大语言模型在视觉方面取得了极大的进步,但其如何完成定位任务(如wordgrounding等)仍然不清楚。本文旨在设计一种模型能够将一系列点/边界框作为输入或者输出。当模型接受定位信息作为输入时,可以进行以定位为condition的captioning。当生成位置作为输出时,模型......