不要吝啬你的批评与感悟,敬请留言,我们一起进步。

如果你有过以下问题,欢迎阅读文章,提出意见与建议

  1. go mod 怎么使用?
  2. GOPATH是什么?
  3. GO111MODULE="" 这个参数决定了什么?
  4. go get、go download 有什么区别?
  5. import到底import的什么东西?

依赖管理工具

用过Java 的同学都知道,对依赖的管理经历了从原始的手动引入jar包,到使用maven等自动化管理工具去引入第三方依赖的过程,从而可以使用别人已经开发好的优秀工具。如果使用过Python的同学可能会熟练的使用pip install 第三方的工具包。Java 和Python的第三方工具包都是集中式管理的,使用maven 或者是pip 都是从对应的管理中心下载更新依赖。当然还有 npm、yarn、gradle等其他语言的依赖版本工具。

在go语言中,第三方依赖的管理工具经过了一个漫长的发展过程。在GO1.11 发布之前govendor、dep等工具百花齐放。直到go mod 出现,开始一统天下。go 的依赖非常简单粗暴,只要依赖源码就可以了。例如:

1import  "github.com/jinzhu/gorm"

github.com/jinzhu/gorm 就是gorm的GitHub项目路径。

GOPATH时期

Go 在1.11 之前使用GOPATH模式进行依赖的管理。安装部署go环境,使用go 进行开发的时候强制被要求要设置GOPATH(当然安装过程中也会默认指定$GOPATH=~/go)。 要在GOPATH路径下新建 /src /bin /pkg文件夹。

1➜ ~/go
2├── bin  # 存储go编译生成的二进制可执行文件,一般会把该路径配置到PATH中,PATH=$PATH:$GOPATH/bin
3├── pkg  # 存储预编译的目标文件,以加快后续的编译速度 
4└── src  # 存储Go的源代码,一般以$GOPATH/src/github.com/foo/bar的路径存放
1➜  ~ go env |grep GOPATH
2GOPATH="/Users/bytedance/go"

在这种模式下,如果使用go get 拉取外部依赖会自动下载并安装到$GOPATH/src 目录下。

这种模式下,go get没有版本管理的概念,无法处理依赖不同版本的问题,因为同一个依赖都存在同一个路径下面。

在Go官方还没有推出Go Modules 的时候,go的依赖管理工具可谓是百花齐放,例如 govendordep,但是最终Go Modules发布,平息了诸侯割据的局面。

GO Modules

Go1.11 开始推出Go Modules ,Go1.13开始不再推荐使用GOPATH。意思就是说你可以在任何路径下存放你的Go源码文件, 不用再像以前一样非得放到$GOPATH/src中。 每一个go项目 都是一个 Modules。vgo 是Go Modules的前身。

在Go Modules环境下出现了一个很重要的环境变量GO111MODULE

1➜ ~ go env
2GO111MODULE="auto"  # 当为 aotu 模式时候如果当前目录中有go.mod则为GO Module模式
3GOPROXY="https://proxy.golang.org,direct"
4GONOPROXY=""
5GOSUMDB="sum.golang.org"
6GONOSUMDB=""
7GOPRIVATE=""

如果要对go 的环境变量进行设置,可以使用

1go env -w GO111MODULE=on  # 设置go 环境变量
2go env -u # 恢复初始设置

GO111MODULE

GO111MODULE 这个环境变量是用来作为使用Go Modules 的开关。可以说这个变量是历史产物,很有肯能会在将来Go的新版本中去除掉。

1GO111MODULE="auto" # 只要项目包含了go.mod 文件的话就启用Go modules, 在Go1.11-1.14 中是默认值
2GO111MODULE="on"   # 启用Go Modules
3GO111MODULE="off"  # 禁用Go Modules, 对老的项目进行兼容

GOPROXY

GOPROXY 是Go Modules的代理,可以通过镜像站点快速拉取(很多公司都会设置自己的镜像站点),可以设置多个代理。

1GOPROXY="https://proxy.golang.org,direct"

direct的意思是如果通过代理获取不到go get 就会通过源地址直接去抓取

go mod

创建Go Modules的基本命令

 1➜  ~ go mod
 2Go mod provides access to operations on modules.
 3
 4Note that support for modules is built into all the go commands,
 5not just 'go mod'. For example, day-to-day adding, removing, upgrading,
 6and downgrading of dependencies should be done using 'go get'.
 7See 'go help modules' for an overview of module functionality.
 8# 所有的go commands 都支持 modules。
 9Usage:
10
11        go mod <command> [arguments]
12
13The commands are:
14
15        download    download modules to local cache
16        edit        edit go.mod from tools or scripts
17        graph       print module requirement graph
18        init        initialize new module in current directory 
19        tidy        add missing and remove unused modules
20        vendor      make vendored copy of dependencies
21        verify      verify dependencies have expected content
22        why         explain why packages or modules are needed
23
24Use "go help mod <command>" for more information about a command.

go mod init

在当前目录初始化一个新的module, 就是说将该目录下的工程文件初始化为一个Go Module.

如果当前目录在GOPATH中,这条命令无需传入参数,参数默认为 GOPATH/src 到该目录的相对路径

 1➜  demo pwd
 2/Users/bytedance/go/src/github.com/airren/demo  # 当前路径
 3➜  demo go mod init
 4go: creating new go.mod: module github.com/airren/demo
 5➜  demo ls
 6go.mod
 7➜  demo cat go.mod
 8module github.com/airren/demo
 9
10go 1.14
11➜  demo

如果当前目录不在GOPATH中,要手动指定module的名字

 1➜  gotest pwd                                 
 2/Users/bytedance/Desktop/gotest
 3➜  gotest go mod init       # 如果不在GOPATH路径下                  
 4go: cannot determine module path for source directory /Users/bytedance/Desktop/gotest (outside GOPATH, module path must be specified)
 5
 6Example usage:
 7        'go mod init example.com/m' to initialize a v0 or v1 module
 8        'go mod init example.com/m/v2' to initialize a v2 module
 9
10Run 'go help mod init' for more information.
11➜  gotest go mod init github.com/airren/gotest 
12go: creating new go.mod: module github.com/airren/gotest
13➜  gotest cat go.mod 
14module github.com/airren/gotest
15
16go 1.14

go mod download

go mod download 命令会把package下载到 GOPATH/pkg/mod路径下,下载时要指定版本可以用@latest。可以任意路径下使用go mod download

 1➜  jinzhu pwd
 2/Users/bytedance/go/pkg/mod/github.com/jinzhu
 3➜  jinzhu ls
 4gorm@v1.9.11-0.20190912141731-0c98e7d712e2    inflection@v1.0.0
 5gorm@v1.9.12                                  now@v1.0.1
 6inflection@v0.0.0-20180308033659-04140366298a now@v1.1.1
 7➜  jinzhu go mod download -x github.com/jinzhu/gorm@v1.9.8  # 要指定版本
 8➜  jinzhu ls
 9gorm@v1.9.11-0.20190912141731-0c98e7d712e2    inflection@v1.0.0
10gorm@v1.9.12                                  now@v1.0.1
11gorm@v1.9.8                                   now@v1.1.1
12inflection@v0.0.0-20180308033659-04140366298a
13➜  jinzhu cd gorm@v1.9.8

vendor

go mod vendor会在项目中创建一个vendor文件夹。在编译时,可以使用 -mod=vendor ,使用代码主目录文件夹下vendor目录满足依赖获取,go build -mod=vendor。此时,go build 忽略go.mod 中的依赖,(这里仅使用代码root目录下的vendor其他地方的将忽略)

1GOFLAGS=-mod=vendor` 设置顶级vendor作为依赖 `go env -w GOFLAGS="-mod=vendor"` 进行设置。 取消 `go env -w GOFLAGS="-mod="

import

import 导入的是文件存储的相对路径(不能使用绝对路径)或者是Modules Name,并不是package的name,但是在调用Function的时候使用的是package的name

import 首先会从$GOROOT中寻找,然后从$GOPATH/src 中寻找,如果是以./ 或者 ../ 开头的import 直接去对应的相对路径寻找。

import 时候是不区分大小写的,所以在go项目中folder的name尽量是小写,可以有下划线_, 但是packagename一定不能有 _,否则golint会有提示。

举个例子🌰文件结构如图所示

1gotest
2├── format_print
3│   └── colorprintpath.go  # 文件名并不影响 import
4└── sdemo
5    ├── demo.go

colorprintpath.go

1package colorprintfile // 调用方法时候通过package name 调用
2import "fmt"
3// NewPirnt is the new format print  公开方法要有注释
4func NewPrint(content string) {
5	fmt.Printf("This is the content: %v \n", content)
6}

demo.go 调用上述方法

1package main
2import  "../format_print"  // import 使用的是相对路径
3func main(){
4	colorprintfile.NewPrint("hello")	 // 调用package中的公开方法
5}

如果使用了go mod, 在项目文件下有了go.mod, 文件中会有

1module github.com/airren/gotest

此时就不能使用相对路径引用package, 要通过modules的方式引用

1package main
2import  "github.com/airren/gotest/format_print"
3func main(){
4	colorprintfile.NewPrint("hello")	 // 调用package中的公开方法
5}

go get

可以在任意路径下执行go get

  • 用于从远程代码仓库(github, gitlab,gogs)上下载并安装代码包

  • 支持的代码版本控制系统有: Git , Mercurial(hg),SVN, Bazaar

  • 指定的代码包会被下载到$GOPATH中包含的第一个工作区的src目录中,然后再安装

    常用参数

    1-d         # 只执行下载动作,而不执行安装动作
    2-fix       # 在下载代码包后先执行修正动作,而后再进行编译和安装
    3-u         # 利用网络来更新已有的代码包及其依赖包
    4-x         # 显示过程
    

例如使用go get 获取 gorm。 通过-x 参数可以展示详细的过程。

 1➜  ~ go get -u -x github.com/jinzhu/gorm
 2cd .
 3git clone -- https://github.com/jinzhu/gorm /Users/bytedance/go/src/github.com/jinzhu/gorm
 4cd /Users/bytedance/go/src/github.com/jinzhu/gorm
 5git submodule update --init --recursive
 6cd /Users/bytedance/go/src/github.com/jinzhu/gorm
 7git show-ref
 8cd /Users/bytedance/go/src/github.com/jinzhu/gorm
 9git submodule update --init --recursive
10cd /Users/bytedance/go/src/github.com/jinzhu/inflection
11git config remote.origin.url
12cd /Users/bytedance/go/src/github.com/jinzhu/inflection
13git pull --ff-only
14cd /Users/bytedance/go/src/github.com/jinzhu/inflection
15git submodule update --init --recursive
16cd /Users/bytedance/go/src/github.com/jinzhu/inflection
17git show-ref
18cd /Users/bytedance/go/src/github.com/jinzhu/inflection
19git submodule update --init --recursive
20WORK=/var/folders/pz/w7jm4wm933lcspm82kff26600000gn/T/go-build644292157

如果在具有go.mod 的项目的文件夹下使用go get , 会将对应的依赖以及版本写入go.mod, go.sum 是自动生成的,具体介绍可以查看https://studygolang.com/articles/25658

 1➜  gotest cat go.mod 
 2module github.com/airren/gotest
 3
 4go 1.14
 5➜  gotest cat go.sum 
 6cat: go.sum: No such file or directory
 7➜  gotest go get -u github.com/jinzhu/gorm/  # 拉取gorm  
 8go: github.com/jinzhu/gorm upgrade => v1.9.12
 9➜  gotest cat go.mod 
10module github.com/airren/gotest
11
12go 1.14
13
14require github.com/jinzhu/gorm v1.9.12 // indirect  # 新增依赖, 未被使用或者间接使用会有 indirect
15➜  gotest cat go.sum 
16github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
17github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
18github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
19github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
20github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
21github.com/jinzhu/gorm v1.9.12 h1:Drgk1clyWT9t9ERbzHza6Mj/8FY/CqMyVzOiHviMo6Q=
22github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs=
23github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
24github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
25github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
26github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
27github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
28golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
29golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
30golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
31golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
32golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
33golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
34golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
35golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
36google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=

不要吝啬你的批评与感悟,敬请留言,我们一起进步。

参考资料

https://github.com/golang/go/wiki/Modules

https://blog.golang.org/using-go-modules

https://juejin.im/post/5e57537cf265da57584da62b

https://learnku.com/go/t/39086

https://studygolang.com/articles/25658