Makefile

Makefile

安装命令

sudo apt install make

GCC 编译工具链在编译一个C源文件时需要经过以下 4 步:

  • 预处理:为把头文件的代码、宏之类的内容转换成生成的.i文件,还是C代码。
  • 编译:把预处理后的.i文件通过编译成.s文件,汇编语言。
  • 汇编:将汇编语言文件生成目标文件.o文件,机器码。
  • 链接:将每个源文件对应的.o文件链接起来,就生成一个可执行程序文件

在这里插入图片描述

main_exec : main.o head.o #注意空格,否则会报错
	gcc main.o head.o -o main_exec
head.o : head.c
	gcc -c head.c -o head.o
main.o : main.c
	gcc -c main.c -o main.o
clean:
	rm -rf *.o main_exec

Makefile中的变量

普通变量

一般是用户自己根据需求定义的变量。变量的定义要做到见名知意。

  • = 是最普通的等号,在 Makefile 中容易搞错赋值等号,使用 “=” 进行赋值,
  • 变量的值是整个 Makefile 中最后被指定的值。
OBJ_A = A
OBJ_B = $(OBJ_A)B
OBJ_A = AA
all:
	@echo ${OBJ_B}

打印结果:AAB

  • “:=” 表示直接赋值,赋予当前位置的值。
OBJ_A := A
OBJ_B := $(OBJ_A)B
OBJ_A := AA
all:
	@echo ${OBJ_B}

打印结果:AB

  • “?=” 表示如果该变量没有被赋值,赋值予等号后面的值。(可理解为初始化变量)

自动变量

变量名含义
$@规则中的目标集合,在模式规则中,如果有多个目标的话,“$@”表示匹配模式中定义的目标集合。
$<依赖文件集合中的第一个文件.
$^所有依赖文件的集合,使用空格分开,如果在依赖文件中有多个重复的文件,会去除重复的依赖文件,只保留一份。

代码实例:

CC := gcc
TARGET := main_exec
OBJECT := main.o head.o
$(TARGET) : $(OBJECT)
	$(CC) $^ -o $@
%.o : %.c
	$(CC) -c $^ -o $@
clean :
	rm -rf *.o main_exec


等同于

main_exec : main.o head.o #注意空格,否则会报错
	gcc main.o head.o -o main_exec
head.o : head.c
	gcc -c head.c -o head.o
main.o : main.c
	gcc -c main.c -o main.o
clean:
	rm -rf *.o main_exec

伪目标

当 makefile 目录下有一个和目标相同的文件时,例如 clean 文件。我们在执行 make clean 命令的时候会出现错误。伪目标就是用于解决此种错误而产生。
伪目标只是一个标签。

当前项目目录下有 Makefile fun.c fun.h main.c clean此时我们执行make clean命令不生效。修改Makefile的代码。在重复的标签前面加上.PHONY

.PHONY : clean
CC := gcc
TARGET := main_exec
OBJECT := main.o head.o
$(TARGET) : $(OBJECT)
	$(CC) $^ -o $@
%.o : %.c   //一个.o对应一个.c文件
	$(CC) -c $^ -o $@
clean :
	rm -rf *.o main_exec

函数

1、wildcard

SRC = $(wildcard ./*.c)

功能: 匹配目录下所有.c 文件,并将其赋值给SRC变量

2、patsubst

$(patsubst 原模式, 目标模式, 文件列表)

示例:

OBJ = $(patsubst %.c, %.o, $(SRC))

功能:这个函数有三个参数,意思是取出SRC中的所有值,然后将.c 替换为.o 最后赋值给OBJ变量。

代码实例:

CC := gcc
TARGET := main_exec
SRC := $(wildcard *.c)
OBJECT := $(patsubst %.c,%.o,$(SRC))
$(TARGET) : $(OBJECT)
	$(CC) $^ -o $@
%.o : %.c
	$(CC) -c $^ -o $@
clean :
	rm -rf *.o main_exec

3、foreach

$(foreach <var>,<list>,<text>)

这个函数的意思是,把参数<list>中的单词逐一取出放到参数<var>;所指定的变量中,然后再执行< text>;所包含的表达式。每一次生成一个<text>,就会返回一个字符串,循环过程中,<text>;的所返回的每个字符串会以空格分隔,最后当整个循环结束时,<text>;所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。

eg:
在这里插入图片描述

SRCDIRS			:= bsp/clk \
				   bsp/led \
				   bsp/delay 


CFILES			:= $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))

print:
	@echo    CFILES=$(CFILES)	

打印结果 make print

bsp/clk/bsp_clk.c bsp/led/bsp_led.c bsp/delay/bsp_delay.c

4、notdir

格式:

$(notdir 文件列表)

剥离文件的绝对路径,只保留文件名。

5、$(SRC:%.c=%.o)

$(SRC:%.c=%.o)

含义:将SRC变量中所有以.c结尾的文件名替换成对应的以.o结尾的文件名,然后赋回给SRC。

目标文件搜索

如果需要的文件是存在于不同的路径下(即源文件与 Makefile 文件不在同一个路径下),在编译的时候就用到了 Makefile 中为我们提供的目录搜索文件的功能。

常见的搜索的方法的主要有两种:
VPATH 是变量,使用时需要指定文件的路径,是搜索路径下所有的文件;

.
├── header
│   └── name.h
├── Makefile
└── source
    ├── main.cpp
    └── name.cpp


加上路径搜索VPATH = source header

VPATH = source header

main: main.o name.o
	g++ main.o name.o -o main

指定头文件

linux 中一般通过 “-I” (大写 i) 来指定头文件, 形式如下:

gcc -c a.c -I /home/linux/include -o a.o

Makefile 中常用写法

CFLAGS = -I /home/linux/include
myapp: *.c
      gcc $(CFLAGS) -o myapp

指定库文件路径

linux 中一般通过 “-L” (大写 l) 来指定库文件的路径, 形式如下:

-L /usr/lib

Makefile 中常用写法

LDFLAGS = -L /usr/lib

链接具体的库

linux 中一般用 “-l 库名 " 来指定链接对应的库,形式如下:

-lpthread -liconv

Makefile 中常用写法

LIBS = -lpthread -liconv

项目工程管理

global.h

#ifndef __GLOBAL_H__
#define __GLOBAL_H__
#include <stdio.h>
extern int a;
#endif

global.c

#include "global.h"
int a = 20;

fun.h

#ifndef __FUN_H__
#define __FUN_H__
#include "global.h"
extern int fun();
#endif

fun.c

#include "fun.h"
int fun(){
printf("a = %d\n",a);
}

main.c

#include "fun.h"
int main(int argc, const char *argv[]){
fun(); 
return 0;
}

Makefile 工程架构

创建文件夹,把对应的文件放入

在这里插入图片描述

简易版

1、fun中的Makefile

../obj/fun.o : fun.c
	gcc -c -I ../include/ $< -o $@

2、global中的Makefile

../obj/global.o : global.c
	gcc -c -I ../include/ $< -o $@

3、main中的Makefile

../obj/main.o : main.c
	gcc -c -I ../include/ $< -o $@

这三个Makefile的作用都是将当前目录的.c文件编译成.o文件放到obj目录下

4、obj中的Makefile

../bin/my_exec : *.o
	gcc -I ../include/ $^ -o $@

obj中的Makefile的作用是将前面生成的.o文件编译成可执行文件放到bin目录下

5、主工程架构Makefile 编写

SUB_DIR := main fun global obj
export SUB_DIR #将SUB_DIR变量导出为全局,其他文件也可以使用

all : $(SUB_DIR)

$(SUB_DIR) : MK_BIN
	make -C $@       //在Makefile中,-C选项的作用是改变当前的工作目录到指定的路径
MK_BIN:
	mkdir -p ./bin
clean:
	rm -rf ./bin ./obj/*.o

执行上面几个目录下的Makefile

在这里插入图片描述

运行命令:

make #生成可执行文件
./bin/my_exec #执行文件,输出结果
make clean #清除可执行文件

工程版

Makefile 的静态模式

Makefile 的静态模式指的是一种自动编译模式,在这种模式下,我们可以很容易的定义 “多目标”
规则,让我们的规则变得更加有弹性和灵活。

格式:
<targets …> : : <prereq-patterns…>
说明:

  • targets 定义了一系列目标,也就是多个目标。可以是通配符,也可以是多个目标的集合。
  • target-pattern 是 targets 的模式,也就是目标集模式。可以理解
  • prereq-patterns 则是目标的 “依赖” 元素, 可以理解为 %.c, 意思就是对target-pattern 中的目标进行二次定义。其操作方式为取 target-pattern 中去掉. o 后的文件名,并加上. c 形成新的集合。

示例如下:

$(OBJS) : %.o : %.c
    gcc -c $< -o $@

说明:
$(OBJS)是多个. o 文件的集合。例如: fun.o global.o main.o
%.o 是取里面某个. o , 例如 fun.o
%.c 是对应取的某个. c, 例如 fun.c

1、主Makefile编写
启动各子目录下的Makefile文件

CC := gcc
INCLUDE_DIR := ../include/
OBJ_DIR := $(shell pwd)/obj
BIN_DIR := $(shell pwd)/bin

SUB_DIR := main fun global obj
TARGET := my_exec

export CC INCLUDE_DIR OBJ_DIR BIN_DIR SUB_DIR TARGET

all : $(SUB_DIR)
$(SUB_DIR) : MK_BIN
	make -C $@
MK_BIN:
	mkdir -p $(BIN_DIR)
clean:
	rm -rf ./bin ./obj/*.o
install:
	sudo cp $(BIN_DIR)/$(TARGET) /usr/bin 
uninstall:
	sudo rm -rf /usr/bin/$(TARGET)

2、fun、global、main文件夹使用同样的 Makefile文件

SRC :=$(wildcard *.c)
OBJ :=$(patsubst %.c,%.o,$(SRC))
all : $(OBJ)
$(OBJ) : %.o:%.c
	$(CC) -c -I $(INCLUDE_DIR) $^ -o $(OBJ_DIR)/$@

3、obj中的Makefile文件

$(BIN_DIR)/$(TARGET) : *.o
	$(CC) -I $(INCLUDE_DIR) $^ -o $@
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值