go语言中的package和module是我们经常会用到的功能,本文将详细的描述这两个功能的用法.

本文基于golang 1.12.2和go module,之前老的gopath的使用方式不再推荐.

package

在golang中我们使用到的很多功能都会由第三方提供,那么在第三方提供这些包的时候,我们会使用import在go文件中引入package,那么package的一些知识你有过了解吗?

  1. go的package是由多个xxx.go的文件组成的,package内的函数是分布在各个go文件中的(感觉这样不利于查找…)
  2. 在同一个package中可以访问私有变量和函数(小写),即时跨越文件.
  3. package和目录名称可以不一致,不过文件夹内的package名称必须一致.
  4. package不能循环依赖,推荐的方式是子package依赖父package,但是父package不能依赖子package
  5. package引入的路径书写方式为<ModuleName>/包所在的目录路径,也就是说import 后的路径实际上是Module+package的路径(注意,不是名称,因为包的名称和路径不一样)
  6. package路径的寻址是从$Go_path/pkg/mod开始的,例如client-go@v0.0.0-20190425172711-65184652c889,实际的地址为/home/tangxu/go/pkg/mod/k8s.io/client-go@v0.0.0-20190425172711-65184652c889,其中/home/tangxu/gogopath
  7. 对于go.mod中的依赖的修改,推荐使用go get方式,避免出现修改不了.

module

  1. 使用go mod init "导入路径"来生成go.mod文件,例如:

    $ go mod init github.com/robteix/testmod
    go: creating new go.mod: module github.com/robteix/testmod
    
  2. 使用go mod vendorgo build -mod=vendor可以很好的避免库的变动引起的问题

  3. 在go.mod中indirect标示间接依赖,+incompatible标示不兼容

  4. go使用semver (Semantic Versioning)控制版本,格式为:vMAJOR.MINOR.PATCH

module的版本管理

这里以一个实例的方式为大家举例,现有一个testmod模块,go.mod声明如下:

module github.com/robteix/testmod

现在将模块提交到github:

$ git init 
$ git add * 
$ git commit -am "First commit" 
$ git push -u origin master
$ git tag v1.0.0
$ git push --tags

向github提交了此包,并且生成了第一个版本v1.0.0,提交完成后,其他人就可以通过go get获取此包:

$ go get github.com/robteix/testmod

但此时请注意,获取到的代码是最新的master的代码,master的代码是变动的,所以极有可能其他人会遇到不兼容等问题.

那么我们如何做呢? 在go的文档中有详细说明,特别是针对go mod的版本

“If an old package and a new package have the same import path, the new package must be backwards compatible with the old package.”

“如果旧软件包和新软件包具有相同的导入路径,则新软件包必须向后兼容旧软件包。”

那么具体怎么操作呢,接下来我们一起操作一下:

1.建立新的分支

$ git checkout -b v2 

建立一个新的分支来开发我们的代码,方便进行代码的管理

2.修改模块描述

$ echo "module github.com/robteix/testmod/v2" > go.mod

在此主要是修改go.mod文件,将导入路径加上/v2

3.发布新版本

$ git commit go.mod -m "升级到v2"
$ git tag v2.0.0
$ git push --tags origin v2 

4.其他使用者更新到新版本

$ go get github.com/robteix/testmod/v2

使用go get命令依赖v2版本,但是之前的版本依然可以使用,实际上这被看做两个依赖了.

如果不想依赖新版本,只想升级到v1的最新版本,可以使用

$ go get -u=patch github.com/robteix/testmod

5.修改代码中的依赖,并使用tidy整理依赖

$ go mod tidy

6.如果发布错了,那么其他人获取的为什么还是旧的内容?

在golang的module中,依赖不会重复去获取,而是缓存在本地的,缓存的地址为:$GO_PATH/pkg/mod/cache

如果发布错了,有两个办法

1,通过版本升级的方式升级到v2.0.1

2,删除tag重新发布,那么就需要手动清理缓存

module命令参照

go mod init:初始化modules

go mod download:下载modules到本地cache

go mod edit:编辑go.mod文件,选项有-json、-require和-exclude,可以使用帮助go help mod edit

go mod graph:以文本模式打印模块需求图

go mod tidy:删除错误或者不使用的modules

go mod vendor:生成vendor目录

go mod verify:验证依赖是否正确

go mod why:查找依赖

go test :执行一下,自动导包

go list -m 主模块的打印路径

go list -m -f={{.Dir}} print主模块的根目录

go list -m all 查看当前的依赖和版本信息

参照

Go模块简介

Module compatibility and semantic versioning

Semantic Import Versioning

go语义化版本控制