Modules简介

奇技指南

最近项目需要开始正式的接触golang的项目,开源项目中使用了最新的以来管理go mod,就跟进了解了相关的一些内容,经过几年的尝试和改进, 现在的版本也是作者希望能够持续十年的设计,也期望能够学习到作者们的思想,提升自己对版本依赖的认识。

本文转载自360云计算

背景介绍

历史介绍

1.Makefile, goinstall和go get

最早是通过Makefile的形式进行编译,连接程序。后来的goinstall能够在无额外配置的情况拉取到相应的代码,此时只能使用在标准库中。go get解决了开发者之间的代码共享问题,能够通过git等进行代码共享。

2.Versioning和API Stability

通过import的路经中添加相应的版本信息达到了引用不同版的效果。

3.Vendoring和Reproducible Builds

由于go get没有版本相关的信息,所以不能提供一个可以可稳定重复的编译过程。vendor通过拷贝相应的代码到vendor目录下,依赖项不会变更除非手动修改其中的依赖项。

4.An Official Package Management Experiment

dep是官方的依赖管理实验项目,主要是用来探索最佳的实践,是找到最终的解决的重要的一步。
 

解决的问题

  1. 官方的定义

moduleis a collection of related Go packages that are versioned together as a single unit.	
Modules record precise dependency requirements and create reproducible builds.	
Most often, a version control repository contains exactly one moduledefinedin the repository root.	
(Multiple modules are supported in a single repository, but typically that would result in more work	
on an on-going basis than a single module per repository).	
Summarizing the relationship between repositories, modules, and packages:	
A repository contains one or more Go modules.	
Eachmodule contains one or more Go packages.	
Eachpackage consists of one or more Go source files in a single directory.	
Modules must be semantically versioned according to semver, usually in the	
form v(major).(minor).(patch), such as v0.1.0, v1.2.3, or v1.5.0-rc.1. 	
The leading v is required. IfusingGit, tag released commits with their versions.	
Publicandprivatemodule repositories and proxies are becoming available (see FAQ below).

module是一组相关的package的集合,作为一个整体进行版本控制。module记录精确的依赖需求并创建可重复的构建。

一个repo可以包含一个或者多个module,一个module可以包含一个或者多个package,一个package是一个路经下包含一个或者多个文件。

module必须是经过语义版本semver的。

1. 版本控制

    

    如何引入一个向下不兼容的版本?

    import的兼容规则如下

If an old package and a new package have the same import path,

the new package must be backwards compatible with the old package.

如果使用相同的import路经,则新版本必须是兼容就版本的。go是通过不兼容的版本使用不同的import路经来区分的,

 例如 import path和import path/v2的方式

2.同一个主版本采用最低版本选择策略进行版本选择?

最小版本选择,即为选择所有的版本中(不同的主版本是不同的package)选择版本最大的一个版本,也是可用的最小版本。

设计目标

鼓励用户打标签,使得更加的可读,并且能够帮助使用者明确哪些是已经发布的,哪些是正在开发中的不依赖具体的版本控制既可以在一个repo中使用一个module,也可以一个repo下使用多个module,并且各自进行版本控制

能够添加自己的代理

删除之前的vendor目录

基本操作

创建Module

  1. 创建一个目录,新建hello.go文档

package hello	
func Hello() string{	
return"Hello, world."	
}

     2.添加相应的测试文档

package hello	
import"testing"	
func TestHello(t *testing.T) {	
    want := "Hello, world."	
if got := Hello(); got != want {	
        t.Errorf("Hello() = %q, want %q", got, want)	
}	
}

     3.执行测试,确认程序正常运行

注意需要关闭掉相应的module强制设置,确保GO111MODULE不为on,否则会得到 go: cannot find main module; see 'go help modules'

➜  hello go test	
go test	
PASS	
ok      _/home/zhangchao11/work/golang/hello    0.003s	
➜  hello

初始化为module

go mod init example.com/hello

查看相应的go.mod

module example.com/hello	
go 1.13

执行测试

$ go test	
go: finding rsc.io/quote v1.5.2	
go: downloading rsc.io/quote v1.5.2	
go: extracting rsc.io/quote v1.5.2	
go: finding rsc.io/sampler v1.3.0	
go: finding golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c	
go: downloading rsc.io/sampler v1.3.0	
go: extracting rsc.io/sampler v1.3.0	
go: downloading golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c	
go: extracting golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c	
PASS	
ok      example.com/hello    0.023s	
$

说明: go命令能够自动从go.mod中导入指定的依赖项。如果在go.mod中没有找到特定的依赖项的情况

则会从对应的package中自动的查找相应的module,并且把结果写入到go.mod中。导入的module的版本默认选择最新的版本(latest),go.mod中默认自动添加的只有直接依赖的module,并不包含间接依赖的module。

查看新的go.mod

module example.com/hello	
go 1.13	
require rsc.io/quote v1.5.2

执行test

➜  hello go test	
go test	
PASS	
ok      example.com/hello   0.006s	
➜  hello

通过上述结果可以看出,第二次执行go test的时候,由于go.mod已经是最新状态,并且需要的module已经缓存在本地了,就不再执行上述的获取,展开的过程了。

查看module的所有依赖

➜  hello go list -m all	
go list -m all	
example.com/hello	
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c	
rsc.io/quote v1.5.2	
rsc.io/sampler v1.3.0	
➜  hello

列出了module的所以依赖项,第一行列出的是当前module(也称为主module),依赖的module则按照module的路径进行排序列在下面的内容.

查看生成的go.sum

➜  hello cat go.sum	
cat go.sum	
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8=	
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=	
rsc.io/quote v1.5.2 h1:w5fcysjrx7yqtD/aO+QwRjYZOKnaM9Uh2b40tElTs3Y=	
rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0=	
rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=	
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=	
➜  hello

go.sum中保存的是模块对应的哈希值,该值可以保证后续下载的版本和第一次下载的版本没有被非预期的修改。

升级依赖

升级golang.org/x/text

➜  hello go get golang.org/x/text	
go get golang.org/x/text

重新检查依赖列表

➜  hello go list -m all	
go list -m all	
example.com/hello	
golang.org/x/text v0.3.2	
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e	
rsc.io/quote v1.5.2	
rsc.io/sampler v1.3.0	
➜  hello cat go.mod	
cat go.mod	
module example.com/hello	
go 1.13	
require (	
    golang.org/x/text v0.3.2// indirect	
    rsc.io/quote v1.5.2	
)	
➜  hello

从上面的结果中可以看出,golang.org/x/text从之前的版本已经升级到了v0.3.2的版本,更新之后的版本信息已经添加到了go.mod,后面添加了indirect表明该依赖项不是主module直接依赖的。

查询可用的所有依赖

➜  hello go list -m -versions rsc.io/sampler	
go list -m -versions rsc.io/sampler	
rsc.io/sampler v1.0.0 v1.2.0 v1.2.1 v1.3.0 v1.3.1 v1.99.99	
➜  hello

更新到指定版本

➜  hello go get rsc.io/sampler@v1.3.1	
go get rsc.io/sampler@v1.3.1	
go: finding rsc.io/sampler v1.3.1	
go: downloading rsc.io/sampler v1.3.1	
go: extracting rsc.io/sampler v1.3.1	
➜  hello

添加一个新的主版本

添加quoteV3版本

package hello	
import(	
"rsc.io/quote"	
    quoteV3 "rsc.io/quote/v3"	
)	
func Hello() string{	
return quote.Hello()	
}	
func Proverb() string{	
return quoteV3.Concurrency()	
}

添加对应的测试代码

func TestProverb(t *testing.T) {	
    want := "Concurrency is not parallelism."	
if got := Proverb(); got != want {	
        t.Errorf("Proverb() = %q, want %q", got, want)	
}	
}

执行测试

➜  hello go test	
go test	
go: finding rsc.io/quote/v3 v3.1.0	
go: downloading rsc.io/quote/v3 v3.1.0	
go: extracting rsc.io/quote/v3 v3.1.0	
PASS	
ok      example.com/hello   0.006s	
➜  hello

获取最新的依赖关系

➜  hello go list -m all	
go list -m all	
example.com/hello	
golang.org/x/text v0.3.2	
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e	
rsc.io/quote v1.5.2	
rsc.io/quote/v3 v3.1.0	
rsc.io/sampler v1.3.1	
➜  hello

不同的主版本采用不同的导入路径,通过这种方式来提供不兼容的版本升级。v0,v1是直接使用module的路径就可以了,v2及以上的版本必须要添加相应的版本信息path/v2等方式。同一个主版本的不同子版本必须要保证向下的兼容性,也就是同一个主版本只会选择一个版本。这种方式保证了我们在使用新版本的特性的时候,又可以暂时不迁移依赖旧版本的代码。

删除不再使用的依赖

查看现有的依赖关系

➜  hello go list -m all	
go list -m all	
example.com/hello	
golang.org/x/text v0.3.2	
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e	
rsc.io/quote v1.5.2	
rsc.io/quote/v3 v3.1.0	
rsc.io/sampler v1.3.1	
➜  hello cat go.mod	
cat go.mod	
module example.com/hello	
go 1.13	
require (	
    golang.org/x/text v0.3.2// indirect	
    rsc.io/quote v1.5.2	
    rsc.io/quote/v3 v3.1.0	
    rsc.io/sampler v1.3.1// indirect	
)	
➜  hello

从上面的依赖列表中还是依然又v1.5.2,go build或者go test这样的命令,能够添加没有的以来项目但是不能够安全的删除掉相应的依赖。安全的删除一个依赖需要检查module里面的所有package是否依赖该module,而build,test命令都没有加载相应的信息,所以不能够安全的删除相应的module。

go mod tidy

➜  hello go mod tidy	
go mod tidy	
➜  hello go list -m all	
go list -m all	
example.com/hello	
golang.org/x/text v0.3.2	
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e	
rsc.io/quote/v3 v3.1.0	
rsc.io/sampler v1.3.1	
➜  hello cat go.mod	
cat go.mod	
module example.com/hello	
go 1.13	
require (	
    golang.org/x/text v0.3.2// indirect	
    rsc.io/quote/v3 v3.1.0	
    rsc.io/sampler v1.3.1// indirect	
)	
➜  hello

通过这个命令能够删除掉不再需要的依赖module

问题

1.go list -m all?

按照什么排序策略进行排序

2.提交代码的时候一定要提交go.mod和go.sum不然会出现checksum的时候出错的情况

使用中遇到的问题

如何进行依赖库的代码开发?

可以用replace的方式,replace可以替换成本地的路经,来达到使用本地代码的作用。

replace github.com/brocaar/lorawan v0.0.0-20190814113539-8eb2a8d6da09=> path/lorawan

如何使用自由仓库进行依赖库的开发?

设置git的地址转化就可以达到预期的目的,是否可以通过proxy的形式来做到类似的效果,还没有具体研究,后面会去研究以下proxy相关的内容。

git config --global url."${UrlPath}lorawan.git".insteadOf "https://github.com/brocaar/lorawan"

go.mod和go.sum的提交?

提交相应的变更的时候,务必记得提交相应的go.sum,不然会出现后续的sum检查不通过。

关注我们

界世的你当不

只做你的肩膀

640?wx_fmt=jpeg
640?wx_fmt=jpeg

 360官方技术公众号 

技术干货|一手资讯|精彩活动

空·

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值