初玩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

相关文章
|
6月前
|
Linux Go Windows
Go 项目使用 Makefile
Go 项目使用 Makefile
22 0
|
6月前
|
负载均衡 Go 数据库
Go 语言基于 Go kit 开发 Web 项目
Go 语言基于 Go kit 开发 Web 项目
45 0
|
6月前
|
Linux Go Docker
Go 语言怎么使用 Docker 部署项目?
Go 语言怎么使用 Docker 部署项目?
157 0
|
2月前
|
设计模式 测试技术 Go
Go 项目必备:Wire 依赖注入工具的深度解析与实战应用
在现代软件开发中,依赖注入(Dependency Injection,简称 DI)已经成为一种广泛采用的设计模式。它的核心思想是通过外部定义的方式,将组件之间的依赖关系解耦,从而提高代码的可维护性、可扩展性和可测试性。然而,随着项目规模的增长,手动管理复杂的依赖关系变得日益困难。这时,依赖注入代码生成工具就显得尤为重要。在众多工具中,Wire 以其简洁、强大和易用性脱颖而出,成为 Go 语言项目中的宠儿。本文将带你深入了解 Wire 的安装、基本使用、核心概念以及高级用法,并通过一个实际的 web 博客项目示例,展示如何利用 Wire 简化依赖注入的实现。准备好了吗?让我们开始这场代码解耦的奇
|
4月前
|
消息中间件 NoSQL 中间件
【Go电商实战05】结合商业项目讲解中间件的概念和应用
把Go语言中的知识点结合商业项目,理论联系实践,更好的学习理解,高效学,少踩坑。
|
18天前
|
Go
go语言中的数据类型
go语言中的数据类型
13 0
|
5天前
|
数据采集 存储 Go
使用Go语言和chromedp库下载Instagram图片:简易指南
Go语言爬虫示例使用chromedp库下载Instagram图片,关键步骤包括设置代理IP、创建带代理的浏览器上下文及执行任务,如导航至用户页面、截图并存储图片。代码中新增`analyzeAndStoreImage`函数对图片进行分析和分类后存储。注意Instagram的反爬策略可能需要代码适时调整。
使用Go语言和chromedp库下载Instagram图片:简易指南
|
24天前
|
存储 安全 Go
掌握Go语言:Go语言类型转换,无缝处理数据类型、接口和自定义类型的转换细节解析(29)
掌握Go语言:Go语言类型转换,无缝处理数据类型、接口和自定义类型的转换细节解析(29)
|
1天前
|
Go 开发者
Golang深入浅出之-Go语言上下文(context)包:处理取消与超时
【4月更文挑战第23天】Go语言的`context`包提供`Context`接口用于处理任务取消、超时和截止日期。通过传递`Context`对象,开发者能轻松实现复杂控制流。本文解析`context`包特性,讨论常见问题和解决方案,并给出代码示例。关键点包括:1) 确保将`Context`传递给所有相关任务;2) 根据需求选择适当的`Context`创建函数;3) 定期检查`Done()`通道以响应取消请求。正确使用`context`包能提升Go程序的控制流管理效率。
6 1
|
2天前
|
安全 Go 开发者
Golang深入浅出之-Go语言并发编程面试:Goroutine简介与创建
【4月更文挑战第22天】Go语言的Goroutine是其并发模型的核心,是一种轻量级线程,能低成本创建和销毁,支持并发和并行执行。创建Goroutine使用`go`关键字,如`go sayHello(&quot;Alice&quot;)`。常见问题包括忘记使用`go`关键字、不正确处理通道同步和关闭、以及Goroutine泄漏。解决方法包括确保使用`go`启动函数、在发送完数据后关闭通道、设置Goroutine退出条件。理解并掌握这些能帮助开发者编写高效、安全的并发程序。
13 1