使用 Makefile 构建指令集
使用 Makefile 构建指令集
make
是一个历史悠久的构建工具,通过配置 Makefile
文件就可以很方便的使用你自己自定义的各种指令集,且与具体的编程语言无关。例如配置如下的 Makefile
:
run dev: NODE_ENV=development nodemon server.js
这样当你在命令行执行 make run dev
时其实就会执行 NODE_ENV=development nodemon server.js
指令。
使用 Makefile
构建指令集可以很大的提升工作效率。
Makefile 基本语法
<target>: <prerequisites> <commands>
target
其实就是执行的目标, prerequisites
是执行这条指令的前置条件, commands
就是具体的指令内容。
示例:
build: clean go build -o myapp main.go
clean: rm -rf myapp
这里的 build
有一个前置条件 clean
,意思就是当你执行 make build
时,会先执行 clean
的指令内容 rm-rf myapp
,然后再执行 build
的内容 go build-o myapp main.go
。
变量
自定义变量,示例:
APP=myapp
build: clean go build -o ${APP} main.go
clean: rm -rf ${APP}
PHONY
上例中的定义了 target
目标有 build
和 clean
,如果当前目录中正好有一个文件叫做 build
或 clean
,那么其指令内容不会执行,这是因为 make 会把 target
视为文件,只有当文件不存在或发生改变时才会去执行命令。
为了解决这个问题,我们需要使用 PHONY
声明 target
其实是伪目标:
APP=myapp
.PHONY: buildbuild: clean go build -o ${APP} main.go
.PHONY: cleanclean: rm -rf ${APP}
多个 PHONY 也可以统一声明在一行中:
.PHONY: build clean
递归的目标
假设我们的工程目录结构如下:
~/project
├── main.go├── Makefile└── mymodule/ ├── main.go └── Makefile
文件根目录下还有一个文件夹 mymodule
,它可能是一个单独的模块,也需要打包构建,并且定义有自己的 Makefile
:
# ~/project/mymodule/Makefile
APP=module
build: go build -o ${APP} main.go
现在当你处于项目的根目录时,如何去执行 mymodule 子目录下定义的 Makefile
呢?
使用 cd
命令也可以,不过我们有其它的方式去解决这个问题:使用 -C
标志和特定的 ${MAKE}
变量。
修改项目根目录中的 Makefile
为:
APP=myapp
.PHONY: buildbuild: clean go build -o ${APP} main.go
.PHONY: cleanclean: rm -rf ${APP}
.PHONY: build-mymodulebuild-mymodule: ${MAKE} -C mymodule build
这样,当你执行 make build-mymodule
时,其将会自动切换到 mymodule
目录,并且执行 mymodule
目录下的 Makefile
中定义的 build
指令。
shell 输出作为变量
我们可以把 shell 中执行的指令的输出作为变量:
V=$(shell go version)
gv: echo ${V}
这里执行 make gv
就会先执行 go version
指令然后把输出的内容赋值给变量 V 。
判断语句
假设我们的指令依赖于环境变量 ENV
,我们可以使用一个前置条件去检查是否忘了输入 ENV
:
.PHONY: runrun: check-env echo ${ENV}
check-env:ifndef ENV $(error ENV not set, allowed values - `staging` or `production`)endif
这里当我们执行 make run
时,因为有前置条件 check-env
会先执行前置条件中的内容,指令内容是一个判断语句,判断 ENV
是否未定义,如果未定义,则会抛出一个错误,错误提示就是 error
后面的内容。
帮助提示
添加 help
帮助提示:
.PHONY: build## build: build the applicationbuild: clean @echo "Building..." @go build -o ${APP} main.go
.PHONY: run## run: runs go run main.gorun: go run -race main.go
.PHONY: clean## clean: cleans the binaryclean: @echo "Cleaning" @rm -rf ${APP}
.PHONY: setup## setup: setup go modulessetup: @go mod init && go mod tidy && go mod vendor
.PHONY: help## help: prints this help messagehelp: @echo "Usage: n" @sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /'
这样当你执行 make help
时,就是打印如下的提示内容:
Usage:
build build the application run runs go run main.go clean cleans the binary setup setup go modules help prints this help message
参考资料
- https://danishpraka.sh/2019/12/07/using-makefiles-for-go.html
- http://www.ruanyifeng.com/blog/2015/02/make.html
- https://www.gnu.org/software/make/manual/make.html
- ConcurrencyMode.Multiple模式下的WCF服务就一定是并发执行的吗:探讨同步上下文对并发的影响[上篇]
- WCF技术剖析之二十一:WCF基本异常处理模式[下篇]
- AngularJS in Action读书笔记5(实战篇)——在directive中引入D3饼状图显示
- WCF中并发(Concurrency)与限流(Throttling)体系深入解析系列[共7篇]
- AngularJS in Action读书笔记6(实战篇)——bug hunting
- FreeMarker模板开发指南知识点梳理
- WCF技术剖析之二十: 服务在WCF体系中是如何被描述的?
- WCF如何克服HTTP传输协议的局限提供对不同消息传输模式的实现
- H5手游大事件:腾讯上线“微信小游戏”!支持群分享与内购
- 我所理解的Remoting(2):远程对象生命周期的管理[上篇]
- 谈谈分布式事务(Distributed Transaction)[共5篇]
- SQLXML初体验:用XML代替T-SQL来操作数据库
- 自己动手写可视化软件(代码已开源)
- 探秘Tomcat——连接篇
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 细品Reids的HyperLogLog数据结构
- 布隆过滤器与缓存击穿
- spring之通过注解方式配置Bean(二)
- 【python-leetcode103-树的宽度遍历】二叉树的锯齿形层次遍历
- 图卷积网络(GCN)python实现
- hadoop伪分布式之配置yarn并运行MR程序(WordCount)
- 【python-leetcode637-树的宽度遍历】二叉树的层平均值
- graphSAGE的python实现
- 【python-leetcode111-树的宽度遍历】二叉树的最小深度
- 基于TypeScript封装Axios笔记(四)
- spring之泛型依赖注入
- 【python-leetcode113-树的深度遍历】路径总和Ⅱ
- spring之为什么要使用AOP(面向切面编程)?
- useContext更佳实践
- 【论文笔记】张航和李沐等提出:ResNeSt: Split-Attention Networks(ResNet改进版本)