概述
日常开发、测试工作中,大家一定会各个场景遇到一些流程繁琐但固定的动作,比如jar包发布、依赖检查等。 如果大家熟悉一门用起来糙快猛的脚本语言,那上述的很多流程其实都可以用命令脚本固定下来,比如shell、python等。 MacBook给了大家一个*unix开发环境,预装了简单易用、兼容性强的shell脚本引擎。 所以一般情况下都可以使用shell语句解决,大家比较常见的使用方式是写过一次脚本后,就保存到一个备忘录或者单独的文档中,等需要使用了再去检索、复制、使用。 这种使用方式存在如下问题:
- 不便检索:粘贴、搜集的shell语句不太好关联元信息,文档里搜集的shell语句多了以后就很难检索
- 不够内聚:shell语句存放的位置没有靠近使用的地方,这种场景特定的自动化命令应该和项目代码放在一起,避免离散、碎片化
- 不可复用:实践、归档后的shell语句、脚本应该可以在团队内共享、复用的,同时可以被大家检查、评审。只放在自己的备忘录里会导致大家有需要的时候都要重复开发
出于上述考虑,笔者给大家分享一下make工具的使用,希望后续大家可以把make工具使用到自己的项目中,将自己项目中特定的自动化流程脚本化、持久化,让项目相关的人都能复用起来。
Make介绍
在日常工作中,不管是什么语言开发者,都会熟练使用语言相关的自动化构建工具。比如maven、npm、gradle等。
在Java后端生态里,Maven是事实上的构建工具标准。
是有其他同类型工具比如gradle试图挑战这一地位,但是在常见的、标准化的业务代码迭代场景下,因为整体构建流程比较简单,不像安卓客户端开发需要做灵活的前置、后置构建动作,引入Gradle带来的问题其实会比解决的问题要多。
所以除了类似Spring这种基础架构可能需要依托Gradle的灵活性实现文档自动生成所以选择了Gradle以外,Java业务后端开发领域还是Maven大行其道。
上述语言特定的自动化构建工具存在如下问题:
- 特定语言、生态相关
- 表达能力受限。使用xml、groovy等DSL描述构建过程,一来不直观,二来存在学习成本
make是历史悠久的、语言生态中立的、依托于shell脚本的自动化工具。它使用的DSL就是shell脚本,没有平台绑定,借助shell提供了足够的表达能力。[<维基百科>](https://zh.wikipedia.org/wiki/Make)维基百科>
make是一个在软件开发中所使用的工具程序(Utility software),经由读取“makefile”的文件以自动化建构软件。
它是一种转化文件形式的工具,转换的目标称为“target”;与此同时,它也检查文件的依赖关系,如果需要的话,它会调用一些外部软件来完成任务。
它的依赖关系检查系统非常简单,主要根据依赖文件的修改时间进行判断。
大多数情况下,它被用来编译源代码,生成结果代码,然后把结果代码连接起来生成可执行文件或者库文件。
它使用叫做“makefile”的文件来确定一个target文件的依赖关系,然后把生成这个target的相关命令传给shell去执行。
许多现代软件的开发中(如Microsoft Visual Studio),集成开发环境已经取代make,但是在Unix环境中,仍然有许多工程师采用make来协助软件开发。
得益于MacBook的类Unix环境,出厂即预装了make,大家借助make沉淀下来的脚本、工具可以零成本的在团队内传播、复用。
Make使用
Makefile
概括来说,自动化流程工具暴露给使用者的界面主要包含两要素:
- 具体有哪些子任务
- 子任务之间的依赖关系是什么
语法
任务名: 依赖的任务名列表
shell语句
shell语句
shell语句
- 行首开始指定任务名
- 任务名后英文冒号隔开
- 冒号后是当前任务依赖的、前置的任务
- 换行
- 行首输入制表符
注意一定是制表符,也就是大家熟知的\t
- 用shell语句表达任务的具体内容
- shell语句可多行,每行都要用制表符开头
解析
make工具执行时,会在当前目录寻找Makefile文件,然后解析文件,构建子任务的DAG,然后从用户指定的任务节点开始遍历DAG执行以来的子任务。
# 任务1
task1:
@echo "task1运行"
# 任务2
task2: task1
@echo "task2运行"
如上Makefile内容解析如下:
- 任务内容
- 任务1:在控制台输出 “task1”
- 任务2:在控制台输出 “task2”
- 任务依赖:任务2以来任务1,执行任务2之前会先执行任务1
执行任务
make命令的入门使用姿势如下:
# 在Makefile文件所在目录执行如下命令
make 任务名
执行任务1
执行任务2
task2依赖task1,我们指定task2执行时,会自动先执行task1,然后再执行task2