Makefile初识

目录

  • 0.前期准备
    • 0.1、程序编译链接:
  • 1.Makefile基础
    • 1.1、认识Makefile
    • 1.2、Makefile定义模式:
      • (1) 定义模式:
      • (2) 执行Makefile:
    • 1.3、Makefile的变量
      • (1) 变量定义:
      • (2) **变量的赋值符**:
      • (3) 自动化变量
    • 1.4 伪目标
    • 1.5 文件路径搜索
      • (1) VPATH 变量
      • (2) vpath关键字
  • 2.Makefile高级语法
    • 2.1、**使用其他Makefile**
      • (1) 引用Makefile
      • (2) 嵌套Makefile
    • 2.2、定于命令包
    • 2.3、条件判断语句
    • 2.4、函数
      • (1) 内嵌函数
      • (2) 自定义函数
  • 3.实例演示
    • 版本一(同目录)
      • **Makefile 写法1:**(最简单)
      • **Makefile 写法2:**(使用变量)
    • 版本二(不同目录)
      • **Makefile 写法3:**(不同目录)

0.前期准备

0.1、程序编译链接:

(以 hello.c 程序为例)
在这里插入图片描述

预处理阶段:将引入的头文件 #… 对于文件内容插入程序中,得到 hello.i

编译阶段:高级语言转换为汇编语言。得到 hello.s

汇编阶段:翻译成机器语言指令,打包成可重定位目标程序。得到 hello.o二进制文件

链接阶段:将 printf.ohello.o 链接合并。得到 hello 可执行文件。

1.Makefile基础

1.1、认识Makefile

make(工程管理工具):帮助我们实现项目的自动编译。

Makefile(实现工程管理的脚本文件):制订规则,来说明如何编译,编译的顺序等等。由make工具来执行。

Makefile的五个主要部分

  • 显示规则:说明如何生成一个或多个目标文件。

  • 隐晦规则:make的自动推导功能所执行的规则。(make -p 可以查看)

  • 变量定义:Makefile中定义的变量。

  • 文件指示:Makefile中引用其他makefile;指定Makefile中有效部分;定义一个多行命令。

  • 注释:使用 “#" 注释,(如需使用 “#” 字符,需要进行转义”#“)。

make的工作流程:

  1. 读入主Makefile (主Makefile中可以引用其他Makefile)

  2. 读入被 include 的其他Makefile

  3. 初始化文件中的变量

  4. 推导隐晦规则, 并分析所有规则

  5. 为所有的目标文件创建依赖关系链

  6. 根据依赖关系, 决定哪些目标要重新生成

  7. 执行生成命令

1.2、Makefile定义模式:

(1) 定义模式:

target ... : prerequisites ...command	#注意:command前面给一定得有【tab】键才能识别......
  • target (目标) :可以是Object File,也可以是执行文件。还可以是一个标签(Label)。

  • prerequisites (依赖) :生成target所需的文件或目标。

  • command (命令) :也就是make需要执行的命令。每个命令一定以**【tab】**键为开头。

重点注意:

​ 每个命令一定以**【tab】**键为开头。

​ 第一个目标为最终目标

示例:

main.o: main.c head.hgcc -c main.c -o main.o

注意前缀:

@:命令前加 @ 符号后,可取消当前命令的打印。

- :命令执行有错的话,忽略错误,继续执行。

(2) 执行Makefile:

$ make  				#第一种:直接终端输入【make】执行
$ make -f my_mkfile		#第二种:自定义的文件名,【make -f 文件名】执行

1.3、Makefile的变量

(1) 变量定义:

#定义
[变量名] [赋值符] [变量值]#使用
$(变量名)

export 可以声明全局变量。

%.o:%c 表示: [任意].o : [与之匹配的任意].c

*c表示:所有从 .c 文件。

示例:

#定义
SRCS = main.c
export LD = ld  #export指定为全局变量#使用
$(SRCS)
$(LD)

(2) 变量的赋值符:

= 是最基本的赋值。

:= 是覆盖之前的值。

?= 是如果没有被赋值过就赋予等号后面的值。

+= 是添加等号后面的值。

其中 = 和 := 的区别在于:

:= 只能使用前面定义好的变量,

= 可以使用整个文件中定义的变量

示例:
在这里插入图片描述

(3) 自动化变量

自动变量含义
$@当前目标集合
$<第一个依赖. 多个时, 逐个取出
$?比目标新的依赖的集合
$^所有依赖的集合, 会去除重复的依赖
$+所有依赖的集合, 不去除重复的依赖
$*这个变量表示目标模式中"%"及其之前的部分
$%当目标是函数库文件时, 表示其中的目标文件名

1.4 伪目标

指明:使用 “.PHONY” 指明是"伪目标”。

使用:make [伪目标]

作用:能防止重命名,不检查是否更新,不会生成文件。

示例:

.PHONY: cleanclean:rm -rf *.o my_exe

使用:

make clean

1.5 文件路径搜索

(1) VPATH 变量

VPATH = src:../headers

指定 VPATH 后,如果当前目录没有找到文件,就会去定义的目录下找。

查找顺序:当前目录 > src目录 > …/headers目录。(用 : 分隔)

(2) vpath关键字

它可以指定不同的文件在不同的搜索目录中。使用方法有三种:

  • vpath 为符合模式的文件指定搜索目录。

  • vpath 清除符合模式的文件的搜索目录。

  • vpath 清除所有已被设置好了的文件搜索目录。

vapth使用方法中的需要包含%字符。%的意思是匹配零或若干字符,例如,%.h表示所有以.h结尾的文件。指定了要搜索的文件集,而则指定了的文件集的搜索的目录。例如:

vpath %.h ../headers

该语句表示,要求make在…/headers目录下搜索所有以.h结尾的文件。(如果某文件在当前目录没有找到的话)

2.Makefile高级语法

2.1、使用其他Makefile

(1) 引用Makefile

语法:

include <filename>  #(filename 可以包含通配符和路径)

include 前面可以有一些空字符,但是绝不能是[Tab]键开始。

找不到文件时,不会立即报错,再尝试寻找,如果再找不到,报致命错误。使用减号可以跳过错误继续执行。

例如:-include 和其它版本make兼容的相关命令是 sinclude(一样效果)。

示例

(other/文件夹在主Makefile文件夹下)

# Makefile 内容---------------------------------------------------
all:@echo "主 Makefile begin"@make other-all@echo "主 Makefile end"include ./other/Makefile# ./other/Makefile 内容
other-all:@echo "other makefile begin"@echo "other makefile end"
# bash中执行 make-------------------------------------------------------
$ make
主 Makefile begin
make[1]: Entering directory `/path/to/test/makefile'
other makefile begin
other makefile end
make[1]: Leaving directory `/path/to/test/makefile'
主 Makefile end

(2) 嵌套Makefile

使用其它 Makefile的可以引用,可以嵌套。 这里我们来看看嵌套 Makefile。

示例:

# Makefile 内容
export VALUE1 := export.c    <-- 用了 export, 此变量能够传递到 ./other/Makefile 中
VALUE2 := no-export.c        <-- 此变量不能传递到 ./other/Makefile 中all:@echo "主 Makefile begin"@cd ./other && make@echo "主 Makefile end"# ./other/Makefile 内容
other-all:@echo "other makefile begin"@echo "VALUE1: " $(VALUE1)@echo "VALUE2: " $(VALUE2)@echo "other makefile end"
# bash中执行 make
$ make
主 Makefile begin
make[1]: Entering directory `/path/to/test/makefile/other'
other makefile begin
VALUE1:  export.c        <-- VALUE1 传递成功
VALUE2:                  <-- VALUE2 传递失败
other makefile end
make[1]: Leaving directory `/path/to/test/makefile/other'
主 Makefile end

2.2、定于命令包

命令包有点像是个函数, 将连续的相同的命令合成一条, 减少 Makefile 中的代码量, 便于以后维护。

语法:

define <command-name>
command
...
endef

示例:

# Makefile 内容
define run-hello-makefile
@echo -n "Hello"
@echo " Makefile!"
@echo "这里可以执行多条 Shell 命令!"
endefall:$(run-hello-makefile)
# bash 中运行make
$ make
Hello Makefile!
这里可以执行多条 Shell 命令!

2.3、条件判断语句

语法:

<conditional-directive>
<text-if-true>
endif# 或者
<conditional-directive>
<text-if-true>
else
<text-if-false>
endif

条件关键字:

ifeq (<arg1>, <arg2>)    # arg1和arg2相同为真
ifneq (<arg1>, <arg2>)   # arg1和arg2不同为真
ifdef <variable-name>    # <variable-name>值非空为真
ifndef <variable-name>   # <variable-name>值为空为真

示例:

示例1: ifeq的例子, ifneq和ifeq的使用方法类似, 就是取反

# Makefile 内容------------------------------
all:
ifeq ("aa", "bb")@echo "equal"
else@echo "not equal"
endif# bash 中执行 make----------------------------------
$ make
not equal

示例2**: ifdef**的例子, ifndef和ifdef的使用方法类似, 就是取反

# Makefile 内容
SRCS := program.call:
ifdef SRCS@echo $(SRCS)
else@echo "no SRCS"
endif# bash 中执行 make
$ make
program.c

2.4、函数

函数主要分为两类:make内嵌函数和用户自定义函数。

(1) 内嵌函数

Makefile 中自带了一些函数, 利用这些函数可以简化 Makefile 的编写.

函数调用语法如下:

$(<function> <arguments>)     # 或 ${<function> <arguments>}#- <function> 是函数名#- <arguments> 是函数参数

其中,函数名与参数之间以空格间隔,参数之间以逗号分隔。

特殊函数:

  • foreach函数:用来做循环用的

  • if函数:作用与ifeq条件判断语句类似

  • call函数:用来替换某个表达式中的参数

  • origin函数:用于查询变量的来源

  • shell函数:用于执行shell命令

  • error函数:用于产生一个致命错误

  • warning函数:用于产生一个警告

更多函数及细节使用参考文档:使用函数 — 跟我一起写Makefile 1.0 文档)

(2) 自定义函数

示例:

PHONY: all#自定义函数部分
define func@echo "pram1 = $(0)"@echo "pram2 = $(1)"
endef#函数调用
all:$(call func, hello zhaixue.cc)

3.实例演示

版本一(同目录)

目录结构:

# 目录结构└── 01Makefile├── head.h├── main.c├── Makefile├── test1.c└── test2.c

程序代码:

//main.c文件-----------------------------------------------
#include<stdio.h>
#include"head.h"   //注意这里head.h一定要用“”,同时要保证路径正确int main(){test1();test2();return 0;
}//head.h文件-----------------------------------------------
#include<stdio.h>void test1();
void test2();//test1.c文件----------------------------------------------
#include<stdio.h>void test1(){printf("I am T1\n");return ;
}//test2.c文件---------------------------------------------
#include<stdio.h>void test2(){printf("I am T2\n");return ;
}

Makefile 写法1:(最简单)

#a.out : main.c test1.c test2.c head.h
#	gcc -o a.out main.c test1.c test2.cmy_exe: main.o test1.o test2.ogcc main.o test1.o test2.o -o my_exemain.o: main.c head.hgcc -c main.c -o main.otest1.o: test1.cgcc -c test1.c -o test1.otest2.o: test2.cgcc -c test2.c -o test2.o.PHONY:cleanclean:rm -rf *.o my_exe

Makefile 写法2:(使用变量)

#定义参数------------------------------------------------------------TGT = my_exe
SRCS = main.c test1.c test2.c
OBJ = $(SRCS:.c=.o)     #相当于OBJ=main.o test1.o test2.oCC = gccHEAD_PATH = $(shell pwd)    #找到head.h文件
CFLAGS = -I$( HEAD_PATH) -Wall   #编译选项,同时可以找到头文件#开始编辑规则---------------------------------------------------------$(TGT): $(OBJ)$(CC) $(CFLAGS) $^ -o $@    #有了$(CFLAGS)就可以找到head.h文件%.o:%.c$(CC) -c $< -o $@.PHONY:clean
clean:rm -rf $(OBJ) $(TGT)

版本二(不同目录)

目录结构变更:

03Makefile├── head│   └── head.h├── main│   └── main.c├── Makefile└── test1├── test1.c└── test2└── test2.c

Makefile 写法3:(不同目录)

(1)主Makefile

作用:将子目录下 .c 生成的 .o 文件,在当前目录下链接生成可执行文件。

#主Makefile#变量参数---------------------------------------------------------
#指定路径
export TOP_DIR = $(shell pwd)    #当前目录
#export HEAD_DIR = $(TOP_DIR)/head  #头文件目录
export HEAD_DIR = /home/wjh/桌面/3.1Makefile/head  #使用$(TOP_DIR)/head,会在/head前面多空格,不是想要的路径。SUB_DIR = main test1  #子目录#目标、依赖
TGT = my_exe
export SUB_TGT = built-in.o    #子目标#编译器、链接器
#CROSS_COMPILER = arm-linux-      #交叉编译环境
export CC = $(CROSS_COMPILER)gcc
export LD = ld
export CFLAGS = -I $(HEAD_DIR) -Wall   #编译选项,同时可以找到头文件#$( HEAD_DIR)或$(HEAD_PATH) 。$(HEAD_DIR)过不了。因为(TOP_DIR)/head不是想要的路径。
export LDFLAGS =    #链接选项#开始编辑规则----------------------------------------------------------
#最终目标
$(TGT): $(SUB_DIR)$(CC) $(CFLAGS) $(^:=/$(SUB_TGT)) -o $(TGT)    #有了$(CFLAGS)就可以找到头文件#$(^:=/$(SUB_TGT))相当于$^和:=和子目录下的子目标/$(SUB_TGT)的组合
#进入所有子目录
$(SUB_DIR):make -C $@  #-C可以让make进入到后面指定目录.PHONY:clean $(SUB_DIR)
clean:rm -rf $(TGT)#for..in..作用是进入子目录清理文件for dir in $(SUB_DIR); do \make -C $$dir clean;\done

(2)子Makefile

作用:说明如何生成当前目录下的子目标。(是由当前目录下的.c生成的.o和当前下的子目录下的子目标临时打包生成的)

#子Makefile#修改下面两项即可  
#main文件夹		#test1文件夹			#test1.c文件夹
SRCS = main.c	  #SRCS = test1.c		#SRCS = test2.c
SUB_DIR =		  #SUB_DIR = test2		#SUB_DIR =#生成当前目录下的子目标。-r为生成临时文件
#(是由当前目录下的.c生成的.o和当前下的子目录下的子目标临时打包生成的) 
$(SUB_TGT): $(SRCS:.c=.o) $(SUB_DIR)$(LD) $(LDFLAGS) $(SRCS:.c=.o) $(SUB_DIR:=/$(SUB_TGT)) \-r -o $@%.o: %.c$(CC) $(CFLAGS) $< -c%.d: %.c$(CC) $(CFLAGS) $< -MM > $@  #将 %.c 所有依赖写入对于的 %.d 文件中。(-MM :显示依赖关系)sinclude $(SRCS:.c=.d)$(SUB_DIR):make -C $@    #-C参数常用来实现递归调用,加该参数选项,意为进入指定目录.PHONY:clean $(SUB_DIR)
clean:rm -rf $(SUB_TGT) $(SRCS:.c=.o) $(SRCS:.c=.d)#for..in..作用是进入子目录清理文件for dir in $(SUB_DIR); do\make -C $$dir clean;\done

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/131650.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2022年12月 Python(三级)真题解析#中国电子学会#全国青少年软件编程等级考试

Python等级考试&#xff08;1~6级&#xff09;全部真题・点这里 一、单选题&#xff08;共25题&#xff0c;每题2分&#xff0c;共50分&#xff09; 第1题 列表L1中全是整数&#xff0c;小明想将其中所有奇数都增加1&#xff0c;偶数不变&#xff0c;于是编写了如下图所示的代…

gorm的自动化工具gen

gorm的自动化工具gen 官方 https://gorm.io/zh_CN/gen/假设数据库结构如 这里使用gen-tool 安装 go install gorm.io/gen/tools/gentoollatest用法 gentool -hUsage of gentool:-c string配置文件名、默认值 “”、命令行选项的优先级高于配置文件。 -db string指定Driver…

volatile-禁重排案例详解

在每一个volatile写操作前面插入一个StoreStore屏障--->StoreStore屏障可以保证在volatile写之前&#xff0c;其前面所有的普通写操作都已经刷新到主内存中。 在每一个volatile写操作后面插入一个StoreLoad屏障--->StoreLoad屏障的作用是避免volatile写与后面可能有的vo…

Python:PDF转长图像和分页图像

简介&#xff1a;随着电子化文档的普及&#xff0c;PDF文件的使用频率越来越高。有时我们需要将PDF中的内容转化为图片格式进行分享或编辑&#xff0c;那么如何才能轻松地完成此任务呢&#xff1f;本文将为你展示一个Python工具&#xff1a;如何将PDF文件转化为图片&#xff0c…

【黑马程序员】Maven 进阶

文章目录 前言一、分模块开发与设计1. 分模块开发意义2. 分模块开发&#xff08;模块拆分&#xff09;2.1 创建 Maven 模块2.2 书写模块代码2.3 通过 Maven 指令安装模块到本地仓库&#xff08;install 指令&#xff09; 二、依赖管理1. 依赖传递1.1 依赖传递冲突问题 2. 可选依…

Rust编程基础核心之所有权(上)

1.什么是所有权? Rust 的核心功能&#xff08;之一&#xff09;是 所有权&#xff08;ownership&#xff09;。虽然该功能很容易解释&#xff0c;但它对语言的其他部分有着深刻的影响。 所有程序都必须管理其运行时使用计算机内存的方式。一些语言中具有垃圾回收机制&#x…

【Linux】第十二站:进程

文章目录 1.windows和linux中的进程2.先描述3.在组织4.具体的Linux系统是如何做的&#xff1f;1.基本概念2.描述进程-PCB3.task_struct和PCB的关系4.task_struct内容分类5.linux具体如何做的&#xff1f;6.查看进程 1.windows和linux中的进程 一个已经加载到内存的程序&#xf…

【MySQL】MVCC机制(undo log,read view)

文章目录 前言一. 预备知识二. 模拟MVCC三. Read View四. RC与RR的本质区别结束语 前言 MVCC&#xff08;多版本并发控制&#xff09;是一种用来解决读-写冲突的无锁并发控制 MVCC为事务分配单向增长的事务ID&#xff0c;为每个修改保存一个版本&#xff0c;版本与事物ID相关联…

全球首款双模型AI手机METAVERTU2,为用户开发“第二大脑”

在2023年11月1日&#xff0c;英国奢侈手机品牌VERTU在香港举办了一场新品发布会&#xff0c;它推出了一款全新的AI手机称为METAVERTU2&#xff0c;这是全球首款双模型AI手机。此款手机将Web3技术与人工智能相结合&#xff0c;通过AI模型标记数据和AI Agent的方式&#xff0c;将…

如何设置OBS虚拟摄像头给钉钉视频会议使用

环境&#xff1a; OBS Studio 29.1.3 Win10 专业版 钉钉7.1.0 问题描述&#xff1a; 如何设置OBS虚拟摄像头给钉钉视频会议使用 解决方案&#xff1a; 1.打开OBS 底下来源这添加视频采集设备 选择OBS虚拟摄像头 2.源那再建一个图像&#xff0c;随便选一张图片 3.点击虚…

SpringBoot项目打包与运行

1.clean生命周期 说明&#xff1a;为了项目能够正确打包&#xff0c;先清理打包文件。 2.package生命周期 说明&#xff1a;打包后生成以下目录。 2.1问题 说明&#xff1a;springboot_08_ssmp-0.0.1-SNAPSHOT.jar中没有主清单属性。 2.2解决 说明&#xff1a;注释skip&…

Python之Excel数据相关

Excel Microsoft Excel是Microsoft为使用Windows和Apple Macintosh操作系统的电脑编写的一款电子表格软件。直观的界面、出色的计算功能和图表工具&#xff0c;再加上成功的市场营销&#xff0c;使Excel成为最流行的个人计算机数据处理软件。在1993年&#xff0c;作为Microsof…

JS异常处理——throw和try、catch以及debugger

让我为大家介绍一下异常处理吧&#xff01; 异常处理是指预估代码执行过程中可能发生的错误&#xff0c;然后最大程度的避免错误的发生导致整个程序无法继续运行 throw 抛异常 第一种写法 function fun(x, y) {// undefined是false 但取反就是trueif (!x || !y) {// 第一种写…

Linux基本指令

目录 1.ls指令 2.pwd指令 3.cd指令 4.touch指令 5.mkdir指令 6.rmdir指令和rm指令 7.man指令 8.cp指令 9.mv指令 10.cat指令 11.more指令 12.less指令 13.head指令 14.tail指令 15.date指令 16.cal指令 17.find指令 18.grep指令 19.zip/unzip指令 2…

leetcode:387. 字符串中的第一个唯一字符

一、题目 函数原型 int firstUniqChar(char* s) 二、算法 设置一个大小为26的字符数组&#xff0c;位置0 - 25 分别对应字符 a - z 。遍历两次字符串&#xff0c;第一次记录下每个字符出现的次数&#xff0c;第二次检查哪个字符最先遍历到且出现次数为1&#xff0c;返回该字符即…

如何将R128的lspsram频率提高至200M?

一、修改频率方法 首先通过cboot0命令&#xff0c;跳转到boot0的代码中&#xff0c;路径为&#xff1a; ${root_dir}/lichee/brandy-2.0/spl/ 找到lspsram的代码&#xff0c;路径为&#xff1a; ${root_dir}/lichee/brandy-2.0/spl/drivers/psram 修改头文件&#xff0c;将2…

毅速丨3D打印结合拓扑优化让轻量化制造更容易

轻量化可以减少产品的重量&#xff0c;提高产品的性能和效率&#xff0c;同时减少能源消耗和排放。尤其在航空航天、汽车制造造等行业对轻量化追求更高。当前&#xff0c;随着制造技术的发展&#xff0c;拓扑优化结合3D打印为轻量化制造带来的显著的优势正在逐渐凸显。 首先&am…

【黑马程序员】SSM框架——MyBatisPlus

文章目录 前言一、MyBatisPlus 简介1. 入门案例1.1 创建新模块1.2 选择需要的技术集1.3 添加 mp 起步依赖1.4 设置 Jdbc 参数1.5 实体类与表结构1.6 定义数据接口1.7 测试功能 2. MyBatisPlus 概述3. MyBatisPlus 特性 二、标准数据层开发1. 标准数据层 CRUD 功能1.1 Lombok1.2…

Hadoop环境搭建及Demo

参考博客 Windows 10安装Hadoop 3.3.0教程 (kontext.tech) Hadoop入门篇——伪分布模式安装 & WordCount词频统计 | Liu Baoshuai’s Blog Hadoop安装教程 Linux版_linux和hadoop的安装_lnlnldczxy的博客-CSDN博客 hadoop启动出错 The value of property bind.address …

linux 安装 elasticsearch 全教程

一、去 elasticsearch官网找到Linux版本的下载链接 地址https://www.elastic.co/cn/downloads/elasticsearch 二、在linux 中用wget下载 wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.10.4-linux-x86_64.tar.gz三、下载成功后解压文件 tar -x…