前言
环境:go1.11.13 linux/amd64
修订:2020.09.17
GOPATH是Golang早期依赖管理的一种方式,随着Go的不断发展,现在已经慢慢被GoModule替代,本文算是GOPATH的一篇纪念文(爷青结)。
环境模拟
本次环境模拟使用 Golang1.11 镜像
# docker 三件套
$ docker pull golang:1.11
$ docker run -itd --name gopath golang:1.11 bash
$ docker exec -it gopath /bin/bash
# 查看GOPATH
root@75bf2f4ce5e5:/go# export
declare -x GOLANG_VERSION="1.11.13"
declare -x GOPATH="/go"
运行入口
一般来讲,学习一门编程语言时,第一件事应该是让我们编写的代码跑起来,Golang也不例外。
要想让Go程序跑起来非常简单,随便找个目录,新建一份 test.go 文件,写入下面的内容:
// package main 代表这是主程序, 可运行的
package main
import "fmt"
func main() {
fmt.Println("Hello world")
}
接着运行 go run test.go 就会出现 "Hello world" 了,Golang的运行就是如此简单。
GOPATH
就个人的使用而言,GOPATH既是一种依赖管理机制,也是一种项目管理机制。
GOPATH的主要作用有两个:
- 1.指定我们的工作区目录,并规定bin、pkg、src三个目录来存放不同类型的文件(体现了项目管理)
- 2.指定我们的依赖下载路径以及导入路径(体现了依赖管理)
GOPATH项目管理
工作区其实就是将所有Go代码放在同一个地方/目录,它不像Python项目那样可以随处创建,随处运行(后面GoModule支持了),具体项目结构如下:
$ pwd
/go
# 单工作区管理形式
$ tree -L 3
.
├── bin
│ ├── glide
│ ├── gocode
│ ├── ...
├── pkg
│ └── linux_amd64
└── src
├── github.com # 外部库
└── gopkg.in
├── git.own.com # 自己的项目集合
│ ├── project1 # 项目1
│ │ ├── .git
│ │ ├── main.go # 主程序
│ ├── project2 # 项目2
│
├── git.alibaba.com # 如果你在阿里巴巴工作过,就可以建一个阿里巴巴项目集合
├── git.tencent.com # 如果你在腾讯工作过,就可以建一个腾讯项目集合
├── git.baidu.com # 百度亦是如此
这种方式的核心概念有:
- 1.必须在环境变量中声明GOPATH路径,即我们的工作区根路径,如上文中的
GOPATH="/go" - 2.每个工作区下包含三个目录:bin、pkg和src。bin目录存放的是可执行文件,pkg目录存放的是静态链接库文件,src目录存放的是源码文件,即我们编写的代码。
由于这种方式将所有Go代码都集中在一起,所以怎么管理以及命名也是一个问题,在上面我也提供了一个思路(具体可以看个人):
1.首先是按公司名进行第一层级划分,比如 git.alibaba.com,这个名字一般是用公司Gitlab/Github仓库的域名。
2.按项目名称进行第二层级的划分,如上文的 project1和project2,每个项目单独管理。
以上是一种单工作区的管理方式,实际上由于需要,也衍生出了多工作区的概念。不过多工作区的管理比较麻烦,需要随时切换GOPATH环境变量,因为go get下载的依赖的路径是依据GOPATH的,所以如果没有及时切换工作区路径,就有可能出现想要在A工作区下载依赖,但却不小心下载到B工作区。
GOPATH依赖管理
前面提到,GOPATH其实也规定了依赖下载路径以及导入路径,即 $GOPATH/src 目录下。
自己封装的依赖
从下面的例子可以看到,依赖的导入路径是相对于 $GOPATH/src 的。
# 进入源码目录,我的GOPATH路径是 /go
$ cd /go/src
# 创建项目以及自定义依赖
$ mkdir -p gopath-test/hello
# 新建一份hello.go; vim gopath-test/hello/hello.go
package hello
func HelloWorld() string {
return "Hello world"
}
# 新建一份main.go用于测试: vim gopath-test/main.go
package main
import (
"fmt"
"gopath-test/hello"
)
func main() {
text := hello.HelloWorld()
fmt.Println(text)
}
下载第三方依赖
这里以依赖第三方库gin,并启动一个HTTP服务器为例:
1.编写测试代码:
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
可以看到,直接go run并不能自动下载依赖
$ go run main.go
main.go:3:8: cannot find package "github.com/gin-gonic/gin" in any of:
/home/go/src/github.com/gin-gonic/gin (from $GOROOT)
/home/go-projects/src/github.com/gin-gonic/gin (from $GOPATH)
2.手动下载依赖并测试
$ go get -v github.com/gin-gonic/gin
# 可以看到,源码已经下载到src目录了
$ ls /go/src/github.com/
gin-contrib gin-gonic go-kit go-playground golang leodido mattn ugorji
# 再次执行,运行成功
$ go run main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /ping --> main.main.func1 (3 handlers)
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080
可以看到,当使用GOPATH进行依赖管理时,第三方下载的依赖也会放在 $GOPATH/src 目录下
补充
1.GOBIN VS GOROOT VS GOPATH
这应该是初学者最困惑的问题之一,这三个环境变量到底有什么区别?
GOPATH:这是我们的工作区路径,上面已经讲过,这里不再阐述,一般设置这个即可。
GOBIN: 当使用 go install xx.go 时, 生成的可执行文件就会放在此目录,如果没有设置,一般会报这样的错
$ go install test.go
go install: no install location for .go files listed on command line (GOBIN not set)
GOROOT:Go的安装位置,对于我来说是 /usr/local/go, 一般用于寻找标准库。
2.源码文件类型
- 1.command source:这是命令源码文件,即包名是
main的源码文件,能够被执行 - 2.package source:这是包源码文件, 即包名非
main的源码文件,不能够被执行,一般是自己封装的一些功能函数 - 3.test source:这是测试源码文件,用于单元测试
3.Go常用命令
Go 常用的命令一般有以下几个:
- go get: 安装外部库
- go install: 编译源码文件, 生成可执行文件, 可执行文件存放在GOBIN目录
- go run: 运行源码文件, 不会生成可执行文件
- go build: 编译源码文件,生成可执行文件, 可执行文件在当前目录
- go test: 运行测试源码文件
4.其他依赖管理方式
虽然 Golang 提供了 go get 命令下载外部库,不过细心的你可能发现,go get并没有版本管理,它默认安装的是外部库的最新版本。
这会导致一个问题,即我们本地环境与正式环境下载的依赖版本不一定一致,从而导致一些意想不到的问题。比如我们本地用的是1.1.1版本,但是上到服务器部署时,外部库更新了版本,变成了1.1.2,如果外部库改动大,难免就会发生很多不兼容的问题。
所以在早期的时候,出现了很多的依赖管理工具,比如 go vendor、glide等,不过现在推荐使用 GoModule,毕竟是亲儿子。