前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅谈Golang包管理module-aware

浅谈Golang包管理module-aware

作者头像
用户9822880
发布2022-06-13 15:17:00
7210
发布2022-06-13 15:17:00
举报
文章被收录于专栏:3分钟云计算

从Go 1.16 开始, module-aware 成为了默认模式,这表示 GO111MODULE 默认值为on. 换句话说就是,即使GO111MODULE不设置,也是使用 module-aware模式。 其实go mod从Go1.11就开始加入, Go1.13有重大变化,直到Go1.16成为默认,完全取代GOPATH模式。历史变化,见下表:

GO111MODULE

< Go 1.13

Go 1.13

Go 1.16

on

任何路径下都开启module模式

任何路径下都开启module模式

(默认)任何路径下都开启module模式

auto

(默认)源码位置不在$GOPATH/src 目录下,并且包含go.mod 文件,则使用module 模式,否则使用GOPATH模式。

(默认)当前目录或者父目录包含go.mod文件时,开启module模式,无论源码是否在$GOPATH/src目录外面

当前目录或者父目录包含go.mod文件时,开启module模式,无论源码是否在$GOPATH/src目录外面

off

GOPATH模式

GOPATH模式

GOPATH模式

本文基于Go1.16.6 版本, 我们从一个示例展开,该示例调用了semver 包,对semver语义化版本感兴趣的读者可以参考 https://semver.org/lang/zh-CN/ 本文略过。

代码语言:javascript
复制
package main

import (
  "fmt"
  
  "github.com/blang/semver"
)

func main() {
  inRange, err := semver.ParseRange(">=4.3.0-0 <4.3.0-201912111117")
  if err != nil {
    panic(err)
  }
  version := semver.MustParse("4.3.0-201912060615")
  fmt.Printf("within range: %t\n", inRange(version))
}

直接运行,报错,显示找不到semver包,没有go.mod 文件。

代码语言:javascript
复制
mac:blog jianzhang$ go run main.go
main.go:6:2: no required module provides package github.com/blang/semver: go.mod file not found in current directory or any parent directory; see 'go help modules'
mac:blog jianzhang$ go build main.go
main.go:6:2: no required module provides package github.com/blang/semver: go.mod file not found in current directory or any parent directory; see 'go help modules'

然后,我们使用go mod init 来添加go.mod 文件

代码语言:javascript
复制
mac:blog jianzhang$ pwd
/Users/jianzhang/blog
mac:blog jianzhang$ ls
main.go
mac:blog jianzhang$ go mod init blog
go: creating new go.mod: module blog
go: to add module requirements and sums:
  go mod tidy
mac:blog jianzhang$ ls
go.mod  main.go

然后运行,go run/build 竟然出错了,这表示这两个指令已经不再自动拉取依赖包到go.mod文件中了。

代码语言:javascript
复制
mac:blog jianzhang$ go run main.go
main.go:6:2: no required module provides package github.com/blang/semver; to add it:
  go get github.com/blang/semver
mac:blog jianzhang$ go build main.go
main.go:6:2: no required module provides package github.com/blang/semver; to add it:
  go get github.com/blang/semver

根据提示,我们可以使用 go mod tidy 或者 go get xxx 来获取所需的依赖包。 运行go mod tidy可以看到,所需的依赖包已经被下载到go.mod 文件中了。同时生成了一个go.sum文件, 该文件包含了依赖包的哈希值,保证你的项目依赖的模块不会遇到恶意代码、随机异常等原因导致的异常变化。该文件自动生成,不需要人为干预,略过。

代码语言:javascript
复制
mac:blog jianzhang$ go mod tidy
go: finding module for package github.com/blang/semver
go: found github.com/blang/semver in github.com/blang/semver v3.5.1+incompatible
mac:blog jianzhang$ ls
go.mod  go.sum  main.go
mac:blog jianzhang$ cat go.sum 
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=

我们来看下 go.mod 文件, 默认拉取最新版本,版本格式遵循semver语法,这个v3.5.1 表示的是该依赖包的tag version, 如果依赖包中并没有tag 版本,那么go mod 会自动设置该依赖包的版本为v0.0.0+git-commit-date+随机数, 例如 github.com/openshift/openshift-tests v0.0.0-20210916082130-4fca21c38ee6

代码语言:javascript
复制
mac:blog jianzhang$ cat go.mod 
module blog

go 1.16

require github.com/blang/semver v3.5.1+incompatible

注意:只有源码直接依赖的包才会被记录到go.mod 文件中。

那么这些依赖包存储在哪里呢?

代码语言:javascript
复制
mac:blog jianzhang$ go env |grep GOMODCACHE
GOMODCACHE="/Users/jianzhang/pkg/mod"

这些依赖包会被存储到 GOMODCACHE, 而这个路径是根据GOPATH自动生成的。可以看到这个GOMODCACHE路径就是 $GOPATH/pkg/mod, 如果该路径不存在,会被自动创建。

代码语言:javascript
复制
mac:blog jianzhang$ go env |grep GOPATH
GOPATH="/Users/jianzhang"

我们来看下 该路径下存储的包:

代码语言:javascript
复制
mac:blog jianzhang$ ls /Users/jianzhang/pkg/mod
cache      github.com
mac:blog jianzhang$ tree /Users/jianzhang/pkg/mod
/Users/jianzhang/pkg/mod
├── cache
│   ├── download
│   │   ├── github.com
│   │   │   └── blang
│   │   │       └── semver
│   │   │           └── @v
│   │   │               ├── list
│   │   │               ├── list.lock
│   │   │               ├── v1.1.0.lock
│   │   │               ├── v1.1.0.zip
│   │   │               ├── v1.1.0.ziphash
│   │   │               ├── v3.5.1+incompatible.info
│   │   │               ├── v3.5.1+incompatible.lock
│   │   │               ├── v3.5.1+incompatible.mod
│   │   │               ├── v3.5.1+incompatible.zip
│   │   │               └── v3.5.1+incompatible.ziphash
│   │   └── sumdb
│   │       └── sum.golang.org
│   │           ├── lookup
│   │           │   └── github.com
│   │           │       └── blang
│   │           │           ├── semver@v1.1.0
│   │           │           └── semver@v3.5.1+incompatible
│   │           └── tile
│   │               └── 8
│   │                   ├── 0
│   │                   │   ├── 005
│   │                   │   ├── 268
│   │                   │   └── x027
│   │                   │       ├── 639
│   │                   │       └── 643.p
│   │                   │           └── 51
│   │                   ├── 1
│   │                   │   ├── 000
│   │                   │   ├── 001
│   │                   │   └── 107.p
│   │                   │       └── 251
│   │                   └── 2
│   │                       └── 000.p
│   │                           └── 107
│   └── lock
└── github.com
    └── blang
        ├── semver@v1.1.0
        │   ├── LICENSE
        │   ├── README.md
        │   ├── examples
        │   │   └── main.go
        │   ├── semver.go
        │   ├── semver_test.go
        │   ├── sql.go
        │   └── sql_test.go
        └── semver@v3.5.1+incompatible
            ├── LICENSE
            ├── README.md
            ├── examples
            │   └── main.go
            ├── json.go
            ├── json_test.go
            ├── package.json
            ├── range.go
            ├── range_test.go
            ├── semver.go
            ├── semver_test.go
            ├── sort.go
            ├── sort_test.go
            ├── sql.go
            └── sql_test.go

26 directories, 42 files

好的,恭喜读到这里的朋友,你们已经掌握了go mod的基本用法了,我们总结下:

  • 包缓存在$GOPATH/pkg/mod 中;
  • indirect 说明这个包不是当前模块的直接依赖,而是其他模块的间接依赖;incompatible 表示该包有了v2版本,但是没有使用/v2分离包版本;详情:https://golang.org/ref/mod#incompatible-versions
  • go get 来更新某个依赖,默认@latest, 可以指定具体版本,例如:go get example/sampler@v1.3.1
  • go list -m xxx 列出某个包的依赖和间接依赖
  • go mod tidy 可以移除不再使用的依赖包

Vendoring

一直以来,Goalng 核心团队追求“可重复性构建”,就是让不同用户拿到相同的代码,使用相同的依赖包,构建出相同的成果。按理说,module-aware模式已经实现了这一愿景,但为什么还要保留vendor呢?这其实是来自于社区的反馈:https://groups.google.com/g/golang-dev/c/FTMScX1fsYk/m/uEUSjBAHAwAJ ,主要原因如下:

  • 向后兼容,仍然有些项目在使用GOPATH模式,利用Vendor 管理依赖包
  • 随着时间的流逝,有些依赖包可能会从github等网站删掉了,导致无法下载。如果把这些依赖包保存在本地项目Vendor中,就不会遇到此类问题;
  • 使用CI/CD 时,都是重新构建,下载依赖包太耗费时间;
  • 有些企业可能只使用内网或者断网状态下,无法下载;

这也是为什么目前很多项目仍然在使用 Vendor(go build -mod=vendor) 的原因。当使用Vendor 时,建议把vendor 目录提交到代码库中,虽然这会使代码库文件增多,体积变大,依赖包变动频繁,但如果不添加的话,就无法实现上面提到的这些优势,也就没有意义了。

那么在CI/CD 中,使用vendor 时,如何加快编译呢?敬请期待下一篇。提前祝大家国庆节假期愉快,不妨停下脚步,虚度时光~

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-09-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 3分钟云计算 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档