Princípio e uso do makefile

makefile

make é uma ferramenta de comando, uma ferramenta de comando que interpreta as instruções em um makefile. A ferramenta make precisa carregar um arquivo chamado makefile ao construir o projeto, e o makefile está relacionado às regras de compilação de todo o projeto.


1. Regras

Cada regra consiste em três partes, respectivamente 目标(target), 依赖(depend) e 命令(command).
insira a descrição da imagem aqui
命令(command): A ação da regra atual, em circunstâncias normais, esta ação é um comando shell
Por exemplo: compilar um arquivo, gerar um arquivo de biblioteca, inserir um diretório, etc. através de um determinado comando. Pode haver várias ações, e cada comando deve ser precedido por um recuo de tabulação e ocupar uma linha exclusiva.
依赖(depend): Dependências exigidas pela regra, que podem ser utilizadas nos comandos da regra.
Por exemplo: o arquivo de destino (*.o) que gera um arquivo executável pode ser usado como dependência. Se nenhuma dependência for necessária no comando da regra, então a dependência da regra pode estar vazia. A dependência no atual regra pode ser um alvo em outras regras, desta forma, as dependências aninhadas entre as regras podem ser especificadas de acordo com as necessidades reais dos comandos a serem executados. : O
目标(target)alvo na regra, o alvo e o comando na regra são correspondente. Ao executar o comando na regra, você pode gerar vários comandos em uma regra de arquivo com o mesmo nome do destino, portanto, esses vários comandos podem ser usados ​​para gerar vários destinos, e todos os destinos também podem ter muitos. Ao executar os comandos da regra, apenas uma ação pode ser executada sem gerar nenhum arquivo, esses alvos são chamados de pseudo-destinos. Por exemplo:

# 举例: 有源文件 a.c b.c c.c head.h, 需要生成可执行程序 app
################# 例1 #################
app:a.c b.c c.c
	gcc a.c b.c c.c -o app

################# 例2 #################
# 有多个目标, 多个依赖, 多个命令
app,app1:a.c b.c c.c d.c
	gcc a.c b.c -o app
	gcc c.c d.c -o app1
	
################# 例3 #################	
# 规则之间的嵌套
app:a.o b.o c.o
	gcc a.o b.o c.o -o app
# a.o 是第一条规则中的依赖
a.o:a.c
	gcc -c a.c
# b.o 是第一条规则中的依赖
b.o:b.c
	gcc -c b.c
# c.o 是第一条规则中的依赖
c.o:c.c
	gcc -c c.c

2. Princípio de funcionamento

Ao chamar o comando make para compilar o programa, o make primeiro encontrará a primeira regra no Makefile no diretório atual, analisará e executará as ações relacionadas (o make não funcionará se não houver Makefile no diretório atual). Porém, vale ressaltar que em muitos casos as dependências utilizadas nas ações (comandos) a serem executadas não existem, caso não existam as dependências utilizadas as ações não serão executadas.

A solução correspondente é gerar primeiro as dependências necessárias, então podemos adicionar uma nova regra no makefile e usar as dependências inexistentes como alvos da nova regra. Quando o comando correspondente à nova regra é executado, o correspondente target é gerado e as dependências exigidas pela outra regra existem.

Desta forma, uma determinada regra no makefile será chamada por outras regras quando necessário, até que todas as dependências da primeira regra no makefile sejam geradas, e os comandos da primeira regra possam ser baseados nessas dependências O destino correspondente é gerado , e a tarefa de make é concluída.

MAKEFILE
# makefile
# 规则之间的嵌套
# 规则1
app:a.o b.o c.o
	gcc a.o b.o c.o -o app
# 规则2
a.o:a.c
	gcc -c a.c
# 规则3
b.o:b.c
	gcc -c b.c
# 规则4
c.o:c.c
	gcc -c c.c

Expansão do conhecimento:
Se você quiser executar o comando que não corresponde à primeira regra no makefile, não pode fazer diretamente, você precisa escrever o destino dessa regra após o make, por exemplo, você só precisa executar o comando na regra 3, você precisa: faça bo .

3. Derivação automática

O make tem a capacidade de deduzir automaticamente e não depende totalmente do makefile. Por exemplo:
insira a descrição da imagem aqui

4. Variáveis

Ao usar o Makefile para definição de regras, podemos usar variáveis ​​nele para uma escrita mais flexível. Existem três tipos de variáveis ​​em makefile: 自定义变量, 预定义变量e 自动变量.

4.1 Variáveis ​​personalizadas

Ao usar Makefile para definir regras, os usuários podem definir suas próprias variáveis, que são chamadas de variáveis ​​definidas pelo usuário. As variáveis ​​no makefile não têm tipo, apenas crie a variável e atribua um valor a ela.

# 错误, 只创建了变量名, 没有赋值
变量名 
# 正确, 创建一个变量名并且给其赋值
变量名=变量值

在给 makefile 中的变量赋值之后,如何在需要的时候将变量值取出来呢?

# 如果将变量的值取出?
$(变量的名字)

# 举例 add.o  div.o  main.o  mult.o  sub.o
# 定义变量并赋值
obj=add.o  div.o  main.o  mult.o  sub.o
# 取变量的值
$(obj)

Exemplo de uso de variáveis ​​personalizadas:

# 这是一个规则,普通写法
calc:add.o  div.o  main.o  mult.o  sub.o
        gcc  add.o  div.o  main.o  mult.o  sub.o -o calc
        
# 这是一个规则,里边使用了自定义变量
obj=add.o  div.o  main.o  mult.o  sub.o
target=calc
$(target):$(obj)
        gcc  $(obj) -o $(target)

4.2 Variáveis ​​predefinidas

Existem algumas variáveis ​​já definidas no Makefile, os usuários podem usar diretamente essas variáveis ​​sem defini-las. Ao compilar, o Makefile usará os valores dessas variáveis ​​pré-definidas para compilação sob certas condições. Os nomes dessas variáveis ​​predefinidas são geralmente em letras maiúsculas e as variáveis ​​predefinidas comumente usadas são mostradas na tabela a seguir:
insira a descrição da imagem aqui
Exemplo:

# 这是一个规则,普通写法
calc:add.o  div.o  main.o  mult.o  sub.o
        gcc  add.o  div.o  main.o  mult.o  sub.o -o calc
        
# 这是一个规则,里边使用了自定义变量和预定义变量
obj=add.o  div.o  main.o  mult.o  sub.o
target=calc
CFLAGS=-O3 # 代码优化
$(target):$(obj)
        $(CC)  $(obj) -o $(target) $(CFLAGS)

4.3 Variáveis ​​automáticas

Além das variáveis ​​definidas pelo usuário e variáveis ​​predefinidas, as variáveis ​​no Makefile também possuem um tipo de variável automática. Arquivos de objeto e arquivos dependentes geralmente aparecem em instruções de regra no Makefile, e variáveis ​​automáticas são usadas para representar arquivos de objeto e arquivos dependentes nessas regras e só podem ser usadas em comandos de regra.

Na tabela abaixo estão algumas variáveis ​​automáticas comuns.
insira a descrição da imagem aqui
Exemplo:

# 这是一个规则,普通写法
calc:add.o  div.o  main.o  mult.o  sub.o
        gcc  add.o  div.o  main.o  mult.o  sub.o -o calc
        
# 这是一个规则,里边使用了自定义变量
# 使用自动变量, 替换相关的内容
calc:add.o  div.o  main.o  mult.o  sub.o
	gcc $^ -o $@ 			# 自动变量只能在规则的命令中使用

5. Correspondência de padrões

Antes de introduzir o conceito, leia o seguinte makefile:

calc:add.o  div.o  main.o  mult.o  sub.o
        gcc  add.o  div.o  main.o  mult.o  sub.o -o calc
# 语法格式重复的规则, 将 .c -> .o, 使用的命令都是一样的 gcc *.c -c
add.o:add.c
        gcc add.c -c

div.o:div.c
        gcc div.c -c

main.o:main.c
        gcc main.c -c

sub.o:sub.c
        gcc sub.c -c

mult.o:mult.c
        gcc mult.c -c

Durante o processo de leitura, pode-se descobrir que a segunda regra para a sexta regra faz a mesma coisa, mas devido aos nomes de arquivos diferentes, várias regras devem ser escritas no arquivo, o que torna o makefile muito redundante, podemos organize esta série das mesmas operações em um modelo, e todas as operações semelhantes serão bastante simplificadas ao corresponder o makefile ao modelo, mas a legibilidade será reduzida.

# 模式匹配 -> 通过一个公式, 代表若干个满足条件的规则
# 依赖有一个, 后缀为.c, 生成的目标是一个 .o 的文件, % 是一个通配符, 匹配的是文件名
%.o:%.c
	gcc $< -c

insira a descrição da imagem aqui

6. Função

Existem muitas funções no makefile e todas as funções retornam valores. O formato da função no makefile também é diferente daquele em C/C++, é escrito assim: $(函数名 参数1, 参数2, 参数3, ...), o objetivo principal é nos permitir obter o valor de retorno da função de forma rápida e conveniente.
Aqui estão duas funções usadas com frequência no makefile: curinga e patsubst.

6.1 curinga

A função principal desta função é obter o nome do arquivo do tipo especificado no diretório especificado e seu valor de retorno é uma lista separada por espaço de todos os nomes de arquivo elegíveis no diretório especificado. O protótipo da função é o seguinte:

# 该函数的参数只有一个, 但是这个参数可以分成若干个部分, 通过空格间隔
$(wildcard PATTERN...)
	参数:	指定某个目录, 搜索这个路径下指定类型的文件,比如: *.c
  • Função de parâmetro:
    • PATTERN refere-se a um certo tipo de arquivo em um ou mais diretórios, por exemplo, um arquivo .c no diretório atual pode ser escrito como *.c
    • Vários diretórios podem ser especificados, com espaços entre cada caminho
  • valor de retorno:
    • Obtenha uma lista de arquivos de vários arquivos, use espaços entre os nomes dos arquivos
    • Exemplo: $(curinga .c ./sub/ .c)
      • Formato do valor de retorno: ac bc cc dc ec fc ./sub/aa.c ./sub/bb.c
        Exemplo:
# 使用举例: 分别搜索三个不同目录下的 .c 格式的源文件
src = $(wildcard /home/robin/a/*.c /home/robin/b/*.c *.c)  # *.c == ./*.c
# 返回值: 得到一个大的字符串, 里边有若干个满足条件的文件名, 文件名之间使用空格间隔
/home/robin/a/a.c /home/robin/a/b.c /home/robin/b/c.c /home/robin/b/d.c e.c f.c

6.2 patsubst

A função desta função é substituir o sufixo do nome do arquivo especificado de acordo com o padrão especificado. O protótipo da função é o seguinte:

# 有三个参数, 参数之间使用 逗号间隔
$(patsubst <pattern>,<replacement>,<text>)
  • Função de parâmetro:
    • pattern: Esta é uma string padrão que precisa especificar qual é o sufixo no nome do arquivo a ser substituído
      • O nome do arquivo e o caminho não precisam se preocupar, então use % para expressar [curinga é %]
      • Especifique o sufixo a ser substituído após o caractere curinga, por exemplo: %.c, o que significa que o sufixo .c será substituído
    • substituição: Esta é uma string de padrão especificando o sufixo no padrão de parâmetro que será eventualmente substituído por
      • Ainda use% para representar o caminho e o nome do arquivo no padrão de parâmetro
      • Especifique um novo sufixo após o curinga %, por exemplo: %.o Isso significa que o sufixo original é substituído por .o
    • texto: Este parâmetro armazena os dados originais a serem substituídos
    • valor de retorno:
      • A função retorna a string substituída.
        Exemplo:
src = a.cpp b.cpp c.cpp e.cpp
# 把变量 src 中的所有文件名的后缀从 .cpp 替换为 .o
obj = $(patsubst %.cpp, %.o, $(src)) 
# obj 的值为: a.o b.o c.o e.o

Exemplo de makefile:

# 添加自定义变量 -> makefile中注释前 使用 # 
# 使用函数搜索当前目录下的源文件 .c
src=$(wildcard *.c)
# 将源文件的后缀替换为 .o
obj=$(patsubst %.c, %.o, $(src))
target=calc

$(target):$(obj)
        gcc $(obj)  -o $(target)

%.o:%.c
        gcc $< -c

# 添加规则, 删除生成文件 *.o 可执行程序
# 声明clean为伪文件
.PHONY:clean
clean:
        # shell命令前的 - 表示强制这个指令执行, 如果执行失败也不会终止
        -rm $(obj) $(target) 
        echo "hello, 我是测试字符串"

Perceber:clean é um pseudo-target que não corresponde a nenhum arquivo físico. Como falei anteriormente sobre a atualização do timestamp do arquivo, se o target não existir, o comando da regra será executado. Se o arquivo target existir, o target arquivo na regra precisa ser comparado. E o carimbo de tempo do arquivo dependente, o comando da regra é executado somente quando a condição for atendida, caso contrário, não é executado.

Para resolver este problema, você precisa declarar clean como um pseudo-target no makefile, para que o make não verifique o carimbo de data/hora do arquivo e os comandos na regra sejam executados todas as vezes.

A declaração de um pseudo-destino no makefile requer o uso da palavra-chave .PHONY. A instrução é: .PHONY: nome do pseudo-arquivo

Acho que você gosta

Origin blog.csdn.net/qq_39030233/article/details/129099312
Recomendado
Clasificación