初玩Makefile与Go项目中Makefile的简单使用

简介: 初玩Makefile与Go项目中Makefile的简单使用

1 回顾程序的编译和链接流程

1.1 一个C语言源文件变为可执行程序的过程

简单来讲,源代码文件一般由程序员使用高级计算机语言(C、Go、Java、C++等)进行编写,不同的语言有不同的文件后缀名,但是他们的共同点就是他们都是程序的源文件,这些文件(一个或多个)需要进行编译和链接后生成可直接运行的程序,才能真正的被运行。

当然有些编程语言并没有这种机制,这种语言一般为解释执行型的语言,比如Java,以及解析型的脚本语言,比如Python。拥有这种机制的语言一般为编译性语言,比如经典的C语言。

1.2 程序源文件如何编译和链接

那么问题来了,程序的源文件是如何编译和链接的呢?

我们那最经典的C语言举例子,其实上图针对C语言的编译过程描述的并不是很全面,只是一个大概的过程,C语言较为完整的编译过程是C源文件->(预编译)->中间文件->(编译)->中间文件->(链接)->可执行文件并且其中还依赖了众多的库文件。

1.3 源代码编译为程序的必要条件—编译器

以上并不是重点,我们都知道C语言是具有很好的移植性,并且能够在绝大多数操作系统下进行编译,可谓是一次编写,处处编译,但是任何语言都是由编译器的,不同操作系统下C语言源文件编译成的可执行文件不同(比如在Windows下编译成xx.exe,而在Linux下编译成xx.sh或xx,所以我们可以逆向推导出,C语言的编译器在不同的操作系统中具有不同的种类,但是所有的编译器种类都要符合一个特点的规范,如下:

  • C语言规范:ANSI C
    ANSI C是由美国国家标准协会(ANSI)及国际标准化组织(ISO)推出的关于C语言的标准。ANSI C 主要标准化了现存的实现, 同时增加了一些来自 C++ 的内容 (主要是函数原型) 并支持多国字符集 (包括备受争议的三字符序列)。 ANSI C 标准同时规定了 C 运行期库例程的标准。
  • 流行的C语言编译器有以下几种:
  • GNU Compiler Collection 或称 GCC
  • Microsoft C 或称 MS C
  • Borland Turbo C 或称 Turbo C
1.4 小结

通过以上内容我们知道了一个main.c程序是如何变为可执行程序的简单过程,这个过程看起来可以说并不简单,但是为什么我们测试程序的时候完全无感知呢?

答案就是——IDE(Integrated Development Environment ),中文名称:集成开发环境,正是因为有IDE我们才不需要每次启动程序时都进行重复流程的操作,而是他已经帮我们搞好了一套流程用来对源代码进行编译和运行。

所以本文的重点——Makefile也是如此,因为我们的项目代码最终都是要放到服务器上去运行的,但是服务器上并没有我们个人电脑上存在非常好用的IDE,最大的原因就是耗费服务器的资源,因此我们使用Makefile来进行对程序的自动化编译,下面步入正题。

2 Makefile快速入门

2.1 核心语法
target ... : prerequisites ...
    command
    ...
    ...

解释

  • target
    可以是一个object file(目标文件),也可以是一个执行文件,还可以是一个标签(label)。对于标签这种特性,在后续的“伪目标”章节中会有叙述。
  • prerequisites
    生成该target所依赖的文件和/或target
  • command
    该target要执行的命令(任意的shell命令)
2.2 编译和运行单个源代码文件实战
2.2.1 Linux下gcc环境检测
gcc -v

2.2.2 编写main.c源代码文件
#include<stdio.h>
  
int main(){
   printf("Hello Makefile ~\n");
   return 0;
}
2.2.3 编写Makefile文件
main: main.o
        gcc -o main main.o
main.o: main.c
        gcc -c main.c
clean:
        rm main.o

注意

Makefile命令的开头一定要使用Tab键而不是空格键,否则就会报错。

2.2.4 运行Makefile

自定义运行:

2.2.5 Makefile中使用shell命令

我们也可以在Makefile中使用shell命令,举个例子:

hello:
        echo Hello Makefile
        @echo Hello Makefile Too
        @ls -al

运行结果:

由此我们知道了,在shell命令前加@的不会输出命令的本身,反之则输出命令的本身。

3 Go项目中Makefile的简单使用

3.1 伪目标和变量
3.1.1 伪目标

最早先的一个例子中,我们提到过一个“clean”的目标,这是一个“伪目标”,

clean:
    rm *.o temp

更为稳健的做法是:

.PHONY : clean
clean :
    - rm *.o temp

前面说过, .PHONY 表示 clean 是一个“伪目标”。而在 rm 命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。当然, clean 的规则不要放在文件的开头,不然,这就会变成make的默认目标,相信谁也不愿意这样。不成文的规矩是——“clean从来都是放在文件的最后”。

.PHONY : all 声明了“all”这个目标为“伪目标”。(注:这里的显式“.PHONY : all” 不写的话一般情况也可以正确的执行,这样make可通过隐式规则推导出, “all” 是一个伪目标,执行make不会生成“all”文件,而执行后面的多个目标。建议:显式写出是一个好习惯。)

小结:因为伪目标总是会被执行,所以其依赖总是会被决议。通过这种方式,可以达到同时执行所有依赖项的目的。

3.1.2 变量的使用

(1)变量的声明:

变量在声明时需要给予初值,而在使用时,需要给在变量名前加上 $ 符号,但最好用小括号 () 或是大括号 {} 把变量给包括起来。如果你要使用真实的 $ 字符,那么你需要用 $$ 来表示。

(2)演示

hello.txt

Hello~

Makefile

i=10
txt=./hello.txt
run:
        @echo $(i)
        @cat $(txt)

执行

(3)扩展

变量的命名字可以包含字符、数字,下划线(可以是数字开头),但不应该含有 :#= 或是空字符(空格、回车等)。变量是大小写敏感的,“foo”、“Foo”和“FOO”是三个不同的变量名。传统的Makefile的变量名是全大写的命名方式,但我推荐使用大小写搭配的变量名,如:MakeFlags。这样可以避免和系统的变量冲突,而发生意外的事情。

3.2 Go项目的常用命令

为什么要说一下Go中的常用命令呢,因为Go项目中Makefile的使用较为简单,大多都是对Go命令的一些执行操作,因此我们首先要熟知Go语言中的常用命令,才能更好的编写Makefile。

  • Go一般命令:
  • build: 编译包和依赖。
  • clean: 移除当前源码包里面编译生成的文件。
  • doc: 显示包或者符号的文档。
  • env: 打印go的环境信息,查看GOPATH,GOROOT环境变量都可以使用该命令查看。
  • bug: 启动错误报告。
  • fix: 运行go tool fix。
  • fmt: 运行gofmt进行格式化。
  • generate: 从processing source生成go文件。
  • get: 下载并安装包和依赖。
  • install: 编译并安装包和依赖。
  • list: 列出包。
  • run: 编译并运行go程序。
  • test: 自动读取源码目录下面名为*_test.go的文件,生成并运行测试用的可执行文件。
  • tool: 运行go提供的工具。
  • version: 显示当前环境安装go的版本。
  • vet: 运行go tool vet。
  • Go mod命令:
  • go mod init [项目名称]:初始化工程项目。
  • go mod graph:输入工程当前所有的依赖。
  • go mod download: 使用此命令来下载指定的模块到本地。
  • go mod tidy:就是把不需要的依赖给删除掉。
  • go mod verify:验证mod里依赖是否正确,同时也会验证go源代码其它依赖的内容。
  • go mod why:可以展示及指定依赖关系。比如代码中有个依赖包,但是并不知道这个包的依赖关系,这时候就可以使用这个命令查看。
3.3 使用实践

项目目录:

Makefile文件内容:

.PHONY:tidy
tidy:
   go mod tidy
.PHONY:build
build:
   go build ./cmd/main.go
.PHONY:run
run:
   @./main

执行:

4 总结

Makefile文件是在项目构建这非常重要的一环,最初认识它是在GitHub上的开源项目中,发现每个项目都有一个Makefile,而且语法看不懂,于是就专门抽时间学习一下,当然由于本人初次学习,本文涵盖的知识点有限,更详细的学习请看 【这篇文章】,在本文这如有错误和不足,感谢读者指出!

参考链接:

https://github.com/seisman/how-to-write-makefile

https://seisman.github.io/how-to-write-makefile/

https://www.cnblogs.com/songgj/p/13167251.html

相关文章
|
2月前
|
JSON 运维 Go
Go 项目配置文件的定义和读取
Go 项目配置文件的定义和读取
|
17小时前
|
SQL 关系型数据库 MySQL
Go语言项目高效对接SQL数据库:实践技巧与方法
在Go语言项目中,与SQL数据库进行对接是一项基础且重要的任务
15 10
|
4天前
|
存储 Kubernetes Go
Go语言项目组织架构
Go语言项目组织架构
|
2月前
|
API
企业项目迁移go-zero实战(二)
企业项目迁移go-zero实战(二)
|
2月前
|
Kubernetes API Go
企业项目迁移go-zero实战(一)
企业项目迁移go-zero实战(一)
|
2月前
|
存储 Prometheus 中间件
2020最佳人气项目之Go Web框架
2020最佳人气项目之Go Web框架
|
2月前
|
Java Go API
我用go-zero开发了第一个线上项目
我用go-zero开发了第一个线上项目
|
7天前
|
存储 Go 容器
深入探究Go语言中的数据结构
深入探究Go语言中的数据结构
25 3
|
18天前
|
Go
Go 语言循环语句
在不少实际问题中有许多具有规律性的重复操作,因此在程序中就需要重复执行某些语句。
25 1
|
7天前
|
Go
GO语言时间转换
GO语言时间转换
17 0