[Makefile] Makefile 及其工作原理
转自:https://www.linuxidc.com/Linux/2018-09/154071.htm
当你需要在一些源文件改变后运行或更新一个任务时,通常会用到 make 工具。make 工具需要读取一个 Makefile(或 makefile)文件,在该文件中定义了一系列需要执行的任务。你可以使用 make 来将源代码编译为可执行程序。大部分开源项目会使用 make 来实现最终的二进制文件的编译,然后使用 make install 命令来执行安装。
本文将通过一些基础和进阶的示例来展示 make 和 Makefile 的使用方法。在开始前,请确保你的系统中安装了 make。
基础示例
依然从打印 “Hello World” 开始。首先创建一个名字为 myproject 的目录,目录下新建 Makefile 文件,文件内容为:
say_hello:echo"Hello World"
在 myproject 目录下执行 make,会有如下输出:
$ makeecho"Hello World"HelloWorld
在上面的例子中,“say_hello” 类似于其他编程语言中的函数名。这被称之为目标target。在该目标之后的是预置条件或依赖。为了简单起见,我们在这个示例中没有定义预置条件。echo ‘Hello World' 命令被称为步骤recipe。这些步骤基于预置条件来实现目标。目标、预置条件和步骤共同构成一个规则。
总结一下,一个典型的规则的语法为:
目标:预置条件<TAB>步骤
作为示例,目标可以是一个基于预置条件(源代码)的二进制文件。另一方面,预置条件也可以是依赖其他预置条件的目标。
final_target: sub_target final_target.cRecipe_to_create_final_targetsub_target: sub_target.cRecipe_to_create_sub_target
目标并不要求是一个文件,也可以只是步骤的名字,就如我们的例子中一样。我们称之为“伪目标”。
再回到上面的示例中,当 make 被执行时,整条指令 echo "Hello World" 都被显示出来,之后才是真正的执行结果。如果不希望指令本身被打印处理,需要在 echo 前添加 @。
say_hello:
@echo "Hello World"
重新运行 make,将会只有如下输出:
$ makeHelloWorld
接下来在 Makefile 中添加如下伪目标:generate 和 clean:
say_hello:@echo"Hello World"generate:@echo"Creating empty text files..."touchfile-{1..10}.txtclean:@echo"Cleaning up..."rm*.txt
随后当我们运行 make 时,只有 say_hello 这个目标被执行。这是因为Makefile 中的第一个目标为默认目标。通常情况下会调用默认目标,这就是你在大多数项目中看到 all 作为第一个目标而出现。all 负责来调用它他的目标。我们可以通过 .DEFAULT_GOAL 这个特殊的伪目标来覆盖掉默认的行为。
在 Makefile 文件开头增加 .DEFAULT_GOAL:
.DEFAULT_GOAL := generate
make 会将 generate 作为默认目标:
$ makeCreatingempty text files...touchfile-{1..10}.txt
顾名思义,.DEFAULT_GOAL 伪目标仅能定义一个目标。这就是为什么很多 Makefile 会包括 all 这个目标,这样可以调用多个目标。
下面删除掉 .DEFAULT_GOAL,增加 all 目标:
all: say_hello generatesay_hello:@echo"Hello World"generate:@echo"Creating empty text files..."touchfile-{1..10}.txtclean:@echo"Cleaning up..."rm*.txt
运行之前,我们再增加一些特殊的伪目标。.PHONY 用来定义这些不是文件的目标。make 会默认调用这些伪目标下的步骤,而不去检查文件名是否存在或最后修改日期。完整的 Makefile 如下:
.PHONY: all say_hello generate cleanall: say_hello generatesay_hello:@echo"Hello World"generate:@echo"Creating empty text files..."touchfile-{1..10}.txtclean:@echo"Cleaning up..."rm*.txt
make 命令会调用 say_hello 和 generate:
$ makeHelloWorldCreatingempty text files...touchfile-{1..10}.txt
clean 不应该被放入 all 中,或者被放入第一个目标中。clean 应当在需要清理时手动调用,调用方法为 make clean。
$ make cleanCleaning up...rm*.txt
现在你应该已经对 Makefile 有了基础的了解,接下来我们看一些进阶的示例。
进阶示例
变量
在之前的实例中,大部分目标和预置条件是已经固定了的,但在实际项目中,它们通常用变量和模式来代替。
定义变量最简单的方式是使用 = 操作符。例如,将命令 gcc 赋值给变量 CC:
CC =gcc
这被称为递归扩展变量,用于如下所示的规则中:
hello: hello.c${CC} hello.c -o hello
你可能已经想到了,这些步骤将会在传递给终端时展开为:
gcc hello.c -o hello
${CC} 和 $(CC) 都能对 gcc 进行引用。但如果一个变量尝试将它本身赋值给自己,将会造成死循环。让我们验证一下:
CC =gccCC = ${CC}all:@echo ${CC}
此时运行 make 会导致:
$ makeMakefile:8:***Recursive variable 'CC' references itself (eventually). Stop.
为了避免这种情况发生,可以使用 := 操作符(这被称为简单扩展变量)。以下代码不会造成上述问题:
CC :=gccCC := ${CC}all:@echo ${CC}
模式和函数
下面的 Makefile 使用了变量、模式和函数来实现所有 C 代码的编译。我们来逐行分析下:
#Usage:#make # compile all binary#make clean # remove ALL binaries and objects.PHONY = all cleanCC =gcc # compiler to useLINKERFLAG =-lmSRCS := $(wildcard *.c)BINS := $(SRCS:%.c=%)all: ${BINS}%:%.o@echo"Checking.."${CC} ${LINKERFLAG} $<-o $@%.o:%.c@echo"Creating object.."${CC}-c $<clean:@echo"Cleaning up..."rm-rvf *.o ${BINS}
- 以
#开头的行是评论。 .PHONY = all clean行定义了all和clean两个伪目标。- 变量
LINKERFLAG定义了在步骤中gcc命令需要用到的参数。 SRCS := $(wildcard *.c):$(wildcard pattern)是与文件名相关的一个函数。在本示例中,所有 “.c”后缀的文件会被存入SRCS变量。BINS := $(SRCS:%.c=%):这被称为替代引用。本例中,如果SRCS的值为'foo.c bar.c',则BINS的值为'foo bar'。all: ${BINS}行:伪目标all调用${BINS}变量中的所有值作为子目标。规则:
%:%.o@echo"Checking.."${CC} ${LINKERFLAG} $<-o $@
下面通过一个示例来理解这条规则。假定
foo是变量${BINS}中的一个值。%会匹配到foo(%匹配任意一个目标)。下面是规则展开后的内容:foo: foo.o@echo"Checking.."gcc-lm foo.o -o foo
如上所示,
%被foo替换掉了。$<被foo.o替换掉。$<用于匹配预置条件,$@匹配目标。对${BINS}中的每个值,这条规则都会被调用一遍。规则:
%.o:%.c@echo"Creating object.."${CC}-c $<
之前规则中的每个预置条件在这条规则中都会都被作为一个目标。下面是展开后的内容:
foo.o: foo.c@echo"Creating object.."gcc-c foo.c
最后,在
clean目标中,所有的二进制文件和编译文件将被删除。
下面是重写后的 Makefile,该文件应该被放置在一个有 foo.c 文件的目录下:
#Usage:#make # compile all binary#make clean # remove ALL binaries and objects.PHONY = all cleanCC =gcc # compiler to useLINKERFLAG =-lmSRCS := foo.cBINS := fooall: foofoo: foo.o@echo"Checking.."gcc-lm foo.o -o foofoo.o: foo.c@echo"Creating object.."gcc-c foo.cclean:@echo"Cleaning up..."rm-rvf foo.o foo
[Makefile] Makefile 及其工作原理的更多相关文章
- Android系统Recovery工作原理之使用update.zip升级过程分析(一)
通过分析update.zip包在具体Android系统升级的过程,来理解Android系统中Recovery模式服务的工作原理.我们先从update.zip包的制作开始,然后是Android系统的启动 ...
- Linux Kbuild工作原理分析(以DVSDK生成PowerVR显卡内核模块为例)
一.引文 前篇博文<Makefile之Linux内核模块的Makefile写法分析>,介绍了Linux编译生成内核驱动模块的Makefile的写法,但最近在DVSDK下使用Linux2.6 ...
- Android系统Recovery工作原理之使用update.zip升级过程分析(一)---update.zip包的制作【转】
本文转载自:http://blog.csdn.net/mu0206mu/article/details/7399822 这篇及以后的篇幅将通过分析update.zip包在具体Android系统升级的过 ...
- Android系统Recovery工作原理之使用update.zip升级过程分析(八)---解析并执行升级脚本updater-script【转】
本文转载自:http://blog.csdn.net/mu0206mu/article/details/7465551 Android系统Recovery工作原理之使用update.zip升级过程分 ...
- Maven - 工作原理
章节 Maven – 简介 Maven – 工作原理 Maven – Repository(存储库) Maven – pom.xml 文件 Maven – 依赖管理 Maven – 构建生命周期.阶段 ...
- 菜鸟学Struts2——Struts工作原理
在完成Struts2的HelloWorld后,对Struts2的工作原理进行学习.Struts2框架可以按照模块来划分为Servlet Filters,Struts核心模块,拦截器和用户实现部分,其中 ...
- 【夯实Nginx基础】Nginx工作原理和优化、漏洞
本文地址 原文地址 本文提纲: 1. Nginx的模块与工作原理 2. Nginx的进程模型 3 . NginxFastCGI运行原理 3.1 什么是 FastCGI ...
- HashMap的工作原理
HashMap的工作原理 HashMap的工作原理是近年来常见的Java面试题.几乎每个Java程序员都知道HashMap,都知道哪里要用HashMap,知道HashTable和HashMap之间 ...
- 【Oracle 集群】ORACLE DATABASE 11G RAC 知识图文详细教程之RAC 工作原理和相关组件(三)
RAC 工作原理和相关组件(三) 概述:写下本文档的初衷和动力,来源于上篇的<oracle基本操作手册>.oracle基本操作手册是作者研一假期对oracle基础知识学习的汇总.然后形成体 ...
- ThreadLocal 工作原理、部分源码分析
1.大概去哪里看 ThreadLocal 其根本实现方法,是在Thread里面,有一个ThreadLocal.ThreadLocalMap属性 ThreadLocal.ThreadLocalMap t ...
随机推荐
- 用 Apache POI 读取 XLSX 数据
最近因为项目的原因,需要从一些 Microsoft Office Excel 文件读取数据并加载到数据库. Google了一下方法,发现其实可以用的 Java 第三方库很多,最著名的是 Apache ...
- spring运行步骤
Spring确实使你能通过最简单可行的解决的方法来解决你的问题. 而这是有有非常大价值的.同一时候他的源码的设计理念也受到非常多程序猿的追捧,简洁,易用.但是从哪着手研究Spring却是非常多新手头疼 ...
- 扫盲--.net 程序集
前言:用了几天的时间把高级编程里面程序集一章看完了,原来自己只知道写代码,右键添加引用,从来也不知道操作的实质是什么,微软总是这个套路,鼠标点点就能把任务完成,这对新手友好但是对要通透了解程序执行和内 ...
- logback 配置详解(上)
logback 配置详解(一)<configuration> and <logger> 一:根节点<configuration>包含的属性: scan: 当此属性设 ...
- 如何浏览github上所有的公开的项目?
github 上面项目多如牛毛,没有维护的.没有意义的或太过偏门的项目也是数不胜数,所以直接按照字母或者更新顺序浏览实在没什么意义. 有一个做法是去 github 搜 awesome list,比如通 ...
- ManualResetEvent使用
1.定义 MSDN定义: 通知一个或多个正在等待的线程已发生事件.此类不能被继承. 详细说明: ManualResetEvent 允许线程通过发信号互相通信.通常,此通信涉及一个线程在其他线程进行之前 ...
- wordpress,cos-html-cache静态化后,点击数失效问题的解决方案
装了wordpress cos-html-cache 静态插件后,生成了静态文件,post-views等点击数插件就失效了, 找了一些,包括有个js版本的,需要用到post-views插件,我也不想装 ...
- html5--2.1新的布局元素概述
html5--2.1新的布局元素概述 学习要点 了解HTML5新标签(元素)的优点 了解本章要学习的新的布局元素 了解本章课程的安排 HTML5新标签的优点: 更注重于内容而不是形式 对人的友好:更加 ...
- enable nested VT in VM
问题描述: 处理器支持VT-x,并且已经在BIOS中开启了VT-x.在host os上用VMware Workstation安装了一个Ubuntu虚拟机,在虚拟机中执行“cat /proc/cpuin ...
- IDEAL葵花宝典:java代码开发规范插件 p3c
前言: P3C插件 是阿里巴巴p3c项目组进行研发.这个项目组是阿里巴巴开发爱好者自发组织形成的虚拟项目组,根据<阿里巴巴Java开发规范>转化而成的自动化插件,并且实现了部分自动编程. ...