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,一经查实,立即删除!

相关文章

Top 5 Cutting-edge technology examples 2023

文章目录 Top 5 Cutting-edge technology examples 20231、Computer Vision2、Natural Language Processing3、Virtual Reality & Augmented Reality4、Deep Machine Learning5、Neuralink Top 5 Cutting-edge technology examples 2023 Cutting-edge technology in 2023 …

python里面的浅拷贝和深拷贝

目录 浅拷贝&#xff08;Shallow Copy&#xff09;&#xff1a;深拷贝&#xff08;Deep Copy&#xff09;&#xff1a;实现方式&#xff1a;使用copy模块进行拷贝&#xff1a;使用切片&#xff08;只适用于列表和其他序列类型&#xff09;进行浅拷贝&#xff1a;使用list()、di…

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. 可选依…

iOS NSKeyedUnarchiver归档和读取

使用NSKeyedUnarchiver归档数据到本地&#xff0c;很多时候保存的并不是基础数据类型&#xff0c;更多是自己定义的Model。有时会碰到归档或者读取的内容跟自己保存的数据类型不匹配。 现在按照思路一步一步解决&#xff1a; 1.先保存文件 保存的数据的类型 #import <Fou…

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;将…

MySql表自修改报错:You can‘t specify target table ‘student‘ for update in FROM clause

文章目录 一、发现问题二、场景1&#xff1a;在where条件中查询了修改表的数据三、场景2&#xff1a;在set语句中查询了修改表的数据 一、发现问题 在一次准备处理历史数据sql时&#xff0c;出现这么一个问题&#xff1a;You cant specify target table 表名 for update in FR…

【JavaScript】Date 日期对象

日期对象 实例化 // 1. 实例化// const date new Date(); // 系统默认时间const date new Date(2020-05-01) // 指定时间// date 变量即所谓的时间对象console.log(typeof date)方法 // 1. 实例化const date new Date();// 2. 调用时间对象方法// 通过方法分别获取年、月、…

React Native自学笔记

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目…

Python教程---Python简介以及安装解释器

1.Python简介 Python是解释型语言,Python(英国发音:/ˈpaɪθən/ 美国发音:/ˈpaɪθɑːn/),是一种广泛使用的高级编程语言,属于通用型编程语言,由吉多范罗苏姆创造,第一版发布于1991年。可以视之为一种改良(加入一些其他编程语言的优点,如面向对象)的LISP。作为…

如何设置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) {// 第一种写…