如何使用go mod的方式来编写第一个 go helloworld
目录
如果你在互联网上查找 go 语言的 helloworld 示例程序时,还看到别人的例子,让你如何配置 GOPATH 的话,基本可以说明那篇文章大概率是比较老的。
从 golang 1.11 版本以上, 就开始支持 go modules。当前使用 go modules 来管理工程的项目也越来越多。
那到底有什么好处呢?
不知你是否有过那种经历,当你想学习 go 语言,在互联网上搜索到一篇教程,按照其中的例子写了个 helloworld.go,结果使用 go build、go run 怎么弄都跑不起来,编译器告诉你找不到相应文件。
是的,的确如此,go 程序的代码工程是需要 GOPATH 环境变量,并且要按照如下的目录结构来组织:
path of $GOPATH:
├─bin
├─pkg
├─src
│ └─helloworld
│ └─helloworld.go
是不是相当麻烦(因为你在网上找到的helloworld例子,不一定会告诉你需要这么做,或者假定你已经这么做)。
当有了 go modules 之后,你的目录可以这么组织:
path of everywhere:
├─go.mod
└─helloworld.go
😱😱😱WHAT??? 是的,就是这么简单。有没有一种 Deja vu 的感觉! go.mod 与 npm的 package.json 作用类似,前端工程师一定非常熟悉。
下面将介绍如何使用编写一个 go mod 的 helloworld 程序。
假设你还没有安装go的编译环境 #
- windows安装,
scoop install go
- macos 安装,
brew install go
使用 windows 操作系统的同学,不知道什么叫
scoop
的,可以看本站相关文章。
你的第一个helloworld例子 #
进入到一个目录,比如 ~/Projects/go
(你可以自己选择,这里只是为了示意方便)。
~/Projects/go
➜ mkdir hello
~/Projects/go
➜ cd hello
~/Projects/go/hello
➜ go mod init world
go: creating new go.mod: module world
创建一个 go 文件 hellohello.go ,写上如下内容,并保存:
插播一下,这里使用 hellohello 作为文件名不是手误,而是故意的,为了说明一个问题,你也可以使用其它名称一做文件名,感兴趣请看 go build
package main
import "fmt"
func main() {
fmt.Println("hello world 😀😀😀")
}
如果你机器上所安装的 go 环境是正常的,按照下面执行,结果是这样的:
~/Projects/go/hello
➜ ls
go.mod hellohello.go
~/Projects/go/hello
➜ go run hellohello.go
hello world 😀😀😀
如果想把它编译成可执行程序,按照如下步骤,结果是:
~/Projects/go/hello
➜ go build hellohello.go
~/Projects/go/hello
➜ ls
go.mod hellohello hellohello.go
~/Projects/go/hello
➜ ./hellohello
hello world 😀😀😀
~/Projects/go/hello
➜ go build
~/Projects/go/hello
➜ ls
go.mod hellohello hellohello.go world
~/Projects/go/hello
➜ ./world
hello world 😀😀😀
这就是一个最简单的 go 程序。以上过程不小心把 go build
和 go build hellohello.go
的差异也透露出来了,想知道这两者的差别,请看。
拆解 #
go mod init #
在上面例子,go mod init world
,其中的 world 表示所定义的模块名称,模块名称也可以是一个路径,比如 world/youwu.today。这个名称将决定了后续包引用时的前缀,若此时你还未涉及包依赖引用的,请忽略。
如果你使用 go build
来生成可执行程序,那么这个模块名称就是可执行程序的名称。如果是带有路径的,如使用 world/youwu.today,那么生成的可执行程序名称为 youwu.today。
具体可看文章
go build
。
go.mod #
执行 go mod init world
命令,go 会生成一个依赖声明文件,文件名为 go.mod,内容如下:
module world
go 1.16
如果你执行 go mod init world/youwu.today
,那么生成的 go.mod 内容长这样:
module world/youwu.today
go 1.16
规律尽在不言中。
如果你的程序引用了外部的go 程序库,使用 go mod tidy
或者 go get 包url路径
会帮助你将引用库的路径以及对应版本号记录到这个文件,不需要手动修改。
进阶 #
以上章节,都假设你是一位想学习 golang 编程的新手。
关于 go modules,只使用到了 go mod init
命令。若想看看 go mod
还有哪些功能,使用 go mod help
查看。
随着你的项目越来越复杂,会引用别人写好的库,这时你会需要 go mod tidy
来帮助管理。
GOPATH 真的就不产生作用了吗? #
不是的。原有显式设置 GOPATH
方式与当前 go module 是两种 go 代码工程管理方式。如果你选择了 go modules,那么那个讨厌的 GOPATH
就无须你时刻关注了,同时在多个 go module 工程目录来回切换,go toolchains 工具链就以 go.mod
这个文件来实现依赖管理。
但 GOPATH
在背后还是一直在起作用。
还是本文中这个例子
package main
import (
"log"
)
func main() {
log.Println("hello world 😀😀😀")
}
编译并运行:
~/Projects/go/examples/hello
➜ ./world
2021/06/17 05:47:05 hello world 😀😀😀
尝试把 GOPATH
改为当前目录:
~/Projects/go/examples/hello
➜ go env GOPATH
/Users/youwu.today/go # 原来默认的目录
~/Projects/go/examples/hello
➜ GOPATH=$(PWD) # 改到当前目录
~/Projects/go/examples/hello
➜ go env GOPATH
$GOPATH/go.mod exists but should not # 提示 GOPATH 与 go.mod 不要在同一目录
~/Projects/go/examples/hello
➜ cd .. && GOPATH=$(PWD) && go env GOPATH
/Users/youwu.today/Projects/go/examples # 那么把 GOPATH 设置到上一级目录
~/Projects/go/examples
➜ cd hello
~/Projects/go/examples/hello
➜ go build . && ./world
2021/06/17 05:54:48 hello world 😀😀😀
上述过程,是尝试把 GOPATH
指向 hello 目录,看会产生什么化学反应,结果是$GOPATH/go.mod exists but should not
。为了两者都可以同时工作,那么把 GOPATH
指到上一级的 examples,暂时通过。回到 hello 目录重新编译看能否正常运行。答案是可以的。
接下来,重点来了。
package main
import (
log "github.com/sirupsen/logrus"
)
func main() {
log.Println("hello world 😀😀😀")
}
使用 golang 比较有人气的日志框架
logrus
,替换 go 标准 log
库(logrus 可以无逢替换 log ,也就是 API 兼容),然后编译运行看看。
~/Projects/go/examples/hello
➜ go mod tidy
go: downloading github.com/sirupsen/logrus v1.8.1
go: downloading golang.org/x/sys v0.0.0-20191026070338-33540a1f6037
go: downloading github.com/stretchr/testify v1.2.2
go: downloading github.com/davecgh/go-spew v1.1.1
go: downloading github.com/pmezard/go-difflib v1.0.0
~/Projects/go/examples/hello
➜ go build . && ./world
INFO[0000] hello world 😀😀😀
~/Projects/go/examples/hello
➜ go build . && ./world
INFO[0000] hello world 😀😀😀
~/Projects/go/examples/hello
➜
记得使用 go mod tidy
下载依赖库,编译可以正常运行。go mod tidy
时,终端提示下载了若干个库。返回到上级 examples ,多了一个 pkg 目录,GOPATH
起作用了。
~/Projects/go/examples/pkg 目录内容如下:
└─┬ .
└─┬ mod
├─┬ golang.org
│ └─┬ x
│ └─┬ sys@v0.0.0-20191026070338-33540a1f6037
│ ├── unix
│ └─┬ windows
├─┬ cache
│ └─┬ download
│ └─┬ golang.org
│ └─┬ x
│ └─┬ sys
│ └─┬ @v
│ └── v0.0.0-20191026070338-33540a1f6037.zip
└─┬ github.com
├─┬ stretchr
│ └── testify@v1.2.2
└─┬ sirupsen
└── logrus@v1.8.1
因此可见,go mod tidy
会把当前工程所须的依赖包下载到 GOPATH
的 pkg 目录缓存起来,这与之前纯 GOPATH 的方式没差别。(go 会为当前用户设置默认的GOPATH,即使你不显式去设置它。)
这一节花了这么长的篇幅说 GOPATH,主要还是为了提醒大家,因为不去主动管理,就容易把它忘记。当你在本地机器上开发了很长时间的 go 程序,说不定默认的 GOPATH pkg 下缓存了很多库,占用了比较大的空间,定期清理即可。当磁盘空间比较紧张时,说不定还可以挤点空间出来。