【GoLong】使用 go mod 管理Go项目依赖 您所在的位置:网站首页 go命令模式 【GoLong】使用 go mod 管理Go项目依赖

【GoLong】使用 go mod 管理Go项目依赖

2022-11-06 04:05| 来源: 网络整理| 查看: 265

1. 前言

如果让我用一段话来评价 GOPATH 和 go vendor,我会说:GOPATH 做为 Golang 的第一个包管理模式,只能保证你能用,但不保证好用,而 go vendor 解决了 GOPATH 忽视包版的本管理,保证好用,但是还不够好用,直到 go mod 的推出后,才使 Golang 包的依赖管理有了一个能让 Gopher 都统一比较满意的方案,达到了能用且好用的标准。

如果是刚开始学习 Golang ,那么 GOPATH 和 go vendor 可以做适当了解,不必深入研究,除非你要接手的项目由于一些历史原因仍然在使用 go vender 械管理,除此之外,任何 Gopher 应该从此刻就投入 go modules 的怀抱。

2. 开启go modules 模式

go modules 是 golang 1.11 新加的特性。现在1.12 已经发布了,是时候用起来了。Modules官方定义为:

模块是相关Go包的集合。modules是源代码交换和版本控制的单元。 go命令直接支持使用modules,包括记录和解析对其他模块的依赖性。modules替换旧的基于GOPATH的方法来指定在给定构建中使用哪些源文件

把 golang 升级至少到 1.11版本以上 设置 GO111MODULE // 因为我的Go version >= 1.13,直接用go env -w 设置(注意大小写) go env -w GOPROXY=https://goproxy.io,direct go env -w GO111MODULE=on // 注:可以用go env -u 恢复初始设置; // GOPROXY的值应该还可以是https://mirrors.aliyun.com/goproxy/ 或 https://goproxy.cn 查看

   

2.1 GO111MODULE

GO111MODULE 有三个值:off, on和auto(默认值)。

GO111MODULE=off,go命令行将不会支持module功能,寻找依赖包的方式将会沿用旧版本那种通过vendor目录或者GOPATH模式来查找。 GO111MODULE=on,go命令行会使用modules,而一点也不会去GOPATH目录下查找。 GO111MODULE=auto,默认值,go命令行将会根据当前目录来决定是否启用module功能。这种情况下可以分为两种情形: 当前目录在GOPATH/src之外且该目录包含go.mod文件 当前文件在包含go.mod文件的目录下面。

当modules 功能启用时,依赖包的存放位置变更为$GOPATH/pkg,允许同一个package多个版本并存,且多个项目可以共享缓存的 module。

下面是pkg下的目录:E:\2020\wxw-go\pkg

├── darwin_amd64 │ ├── github.com │ ├── go.etcd.io │ ├── golang │ ├── golang.org │ ├── gopkg.in │ ├── quickstart │ └── uc.a ├── mod │ ├── cache │ ├── github.com │ ├── golang.org │ ├── google.golang.org │ └── gopkg.in └── sumdb └── sum.golang.org

注意:goland 开发工具需要开启支持moudles管理,否则引入mod依赖虽然可以使用,但有红色提示。

3. go mod 使用

golang 提供了 go mod命令来管理包。

命令 说明 download download modules to local cache(下载依赖包) edit edit go.mod from tools or scripts(编辑go.mod graph print module requirement graph (打印模块依赖图) init initialize new module in current directory(在当前目录初始化mod) tidy add missing and remove unused modules(拉取缺少的模块,移除不用的模块) vendor make vendored copy of dependencies(将依赖复制到vendor下) verify verify dependencies have expected content (验证依赖是否正确) why explain why packages or modules are needed(解释为什么需要依赖)

常用命令:

go clean -modcache ## 清理moudle 缓存

3.1 使用 go mod 管理一个新项目

(1)初始化项目

  可以随便找一个目录创建项目,我使用习惯用IDEA进行创建

mkdir mysql cd mysql go mod init /com.wxw/basic/mysql[[GOPATH]/project_path]

 查看go mod的文件

module com.wxw/basic/mysql go 1.14

go.mod文件一旦创建后,它的内容将会被go toolchain全面掌控。go toolchain会在各类命令执行时,比如go get、go build、go mod等修改和维护go.mod文件。

go.mod 提供了module, require、replace和exclude 四个命令

module 语句指定包的名字(路径) require 语句指定的依赖项模块 replace 语句可以替换依赖项模块 exclude 语句可以忽略依赖项模块

(2)添加依赖

   创建 main.go文件

package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) // 定义一个全局db var db *sql.DB type course struct { cid int cname string tid int } func main() { err := initDB() // 调用输出化数据库的函数 if err != nil { fmt.Printf("init db failed,err:%v\n", err) return } // 单行查询 queryRowDemo() } // 初始化一个数据库连接的函数 func initDB() (err error) { // DSN:Data Source Name dsn := "root:[email protected](127.0.0.1:3306)/test?&parseTime=True" // 不会校验账号密码是否正确 // 注意!!!这里不要使用:=,我们是给全局变量赋值,然后在main函数中使用全局变量db db, err = sql.Open("mysql", dsn) if err != nil { return err } // 尝试与数据库建立连接(校验dsn是否正确) err = db.Ping() if err != nil { return err } return nil } // 查询单条数据示例 func queryRowDemo() { sqlStr := "select cid, cname, tid from course where cid=?" var c course // 非常重要:确保QueryRow之后调用Scan方法,否则持有的数据库链接不会被释放 err := db.QueryRow(sqlStr, 1).Scan(&c.cid, &c.cname, &c.tid) if err != nil { fmt.Printf("scan failed, err:%v\n", err) return } fmt.Printf("id:%d name:%s age:%d\n", c.cid, c.cname, c.tid) }

执行 go run main.go 运行代码会发现 go mod 会自动查找依赖自动下载,再查看 go.mod

module com.wxw/basic/mysql go 1.14 require github.com/go-sql-driver/mysql v1.5.0 go module 安装 package 的原則是先拉最新的 release tag,若无tag则拉最新的commit go 会自动生成一个 go.sum 文件来记录 dependency tree

      

再次执行脚本 go run main.go发现跳过了检查并安装依赖的步骤。

可以使用命令 go list -m -u all 来检查可以升级的package,使用go get -u need-upgrade-package 升级后会将新的依赖版本更新到go.mod * 也可以使用 go get -u 升级所有依赖

3.2 go mod 依赖项使用 go get 升级

运行 go get -u 将会升级到最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号) 运行 go get -u=patch 将会升级到最新的修订版本 运行 go get [email protected] 将会升级到指定的版本号version 运行go get如果有版本的更改,那么go.mod文件也会更改

3.3 使用replace替换无法直接获取的package

由于某些已知的原因,并不是所有的package都能成功下载,比如:golang.org下的包。modules 可以通过在 go.mod 文件中使用 replace 指令替换成github上对应的库,比如:

replace ( golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a => github.com/golang/crypto v0.0.0-20190313024323-a1f597ede03a ) 4. go mod 自己发布并使用

4.1 创建并发布

(1)创建一个模块

如果你设置好go mod了,那你就可以在任何目录下随便创建

$mkdir gomodone $cd gomodone

(2)在这个目录下创建一个文件say.go

package gomodone import "fmt" // say Hi to someone func SayHi(name string) string { return fmt.Sprintf("Hi, %s", name) }

(3)该目录下初始化一个 go.mod文件

$ go mod init github.com/jacksonyoudi/gomodone go: creating new go.mod: module github.com/jacksonyoudi/gomodone

  查看 go.mod内容如下:

github.com/jacksonyoudi/gomodone go 1.14

(4)下面我们要将这个module发布到github上,然后在另外一个程序使用

$git init $vim .gitignore $git commit -am "init" // github创建对应的repo $git remote add origin [email protected]:jacksonyoudi/gomodone.git $git push -u origin master

执行完,上面我们就相当于发布完了。

4.2 加载并使用方法

(1)如果有人需要使用,就可以使用

go get github.com/jacksonyoudi/gomodone

这个时候没有加tag,所以,没有版本的控制。默认是v0.0.0后面接上时间和commitid。如下:

[email protected]

官方不建议这样做,没有进行版本控制管理。

4.3 版本控制管理

(1)使用tag,进行版本控制并发布

git tag v1.0.0 git push --tags

4.4 其它项目中引用

上面已经发布了一个v1.0.0的版本,我们可以在另一个项目中使用,创建一个go的项目

$mkdir Gone $cd Gone $vim main.go package main import ( "fmt" "github.com/jacksonyoudi/gomodone" ) func main() { fmt.Println(gomodone.SayHi("Roberto")) }

代码写好了,我们生成 go mod文件

go mod init Gone

上面命令执行完,会生成 go mod文件,看下mod文件:

module Gone go 1.14 require ( github.com/jacksonyoudi/gomodone v1.0.0 ) $go mod tidy go: finding module for package github.com/jacksonyoudi/gomodone go: found github.com/jacksonyoudi/gomodone in github.com/jacksonyoudi/gomodone v1.0.0

同时还生成了go.sum, 其中包含软件包的哈希值,以确保我们具有正确的版本和文件。

github.com/jacksonyoudi/gomodone v1.0.1 h1:jFd+qZlAB0R3zqrC9kwO8IgPrAdayMUS0rSHMDc/uG8= github.com/jacksonyoudi/gomodone v1.0.1/go.mod h1:XWi+BLbuiuC2YM8Qz4yQzTSPtHt3T3hrlNN2pNlyA94= github.com/jacksonyoudi/gomodone/v2 v2.0.0 h1:GpzGeXCx/Xv2ueiZJ8hEhFwLu7xjxLBjkOYSmg8Ya/w= github.com/jacksonyoudi/gomodone/v2 v2.0.0/go.mod h1:L8uFPSZNHoAhpaePWUfKmGinjufYdw9c2i70xtBorSw=

这个内容是下面的,需要操作执行的结果,go run main.go就可以运行了

4.4 修复已发布的版本(小改动)

(1)修复依赖

假如fix一个bug,我们在v1版本上进行修复,修改代码如下:

// say Hi to someone func SayHi(name string) string { - return fmt.Sprintf("Hi, %s", name) + return fmt.Sprintf("Hi, %s!", name) }

修复好,我们开始push

$ git commit -m "Emphasize our friendliness" say.go $ git tag v1.0.1 $ git push --tags origin v1

(2)更新依赖模块

刚才fix bug,所以要在我们使用项目中更新,这个需要我们手动执行更新module操作,我们通过使用我们的好朋友来做到这一点go get:

运行 go get -u 以使用最新的 minor 版本或修补程序版本(即它将从1.0.0更新到例如1.0.1,或者,如果可用,则更新为1.1.0) 运行 go get -u=patch 以使用最新的 修补程序 版本(即,将更新为1.0.1但不更新 为1.1.0) 运行go get [email protected] 以更新到特定版本(例如github.com/jacksonyoudi/[email protected])

 4.5 大版本更新(大改动)

根据语义版本语义,主要版本与次要版本 不同。主要版本可能会破坏向后兼容性。从Go模块的角度来看,主要版本是 完全不同的软件包。乍一看这听起来很奇怪,但这是有道理的:两个不兼容的库版本是两个不同的库。 比如下面修改,完全破坏了兼容性。

package gomodone import ( "errors" "fmt" ) // Hi returns a friendly greeting // Hi returns a friendly greeting in language lang func SayHi(name, lang string) (string, error) { switch lang { case "en": return fmt.Sprintf("Hi, %s!", name), nil case "pt": return fmt.Sprintf("Oi, %s!", name), nil case "es": return fmt.Sprintf("¡Hola, %s!", name), nil case "fr": return fmt.Sprintf("Bonjour, %s!", name), nil default: return "", errors.New("unknown language") } }

如上,我们需要不同的大版本,这种情况下

修改 go.mod如下

module github.com/jacksonyoudi/gomodone/v2 go 1.14

然后,重新tag,push

$ git commit say.go -m "Change Hi to allow multilang" $ git checkout -b v2 # 用于v2版本,后续修复v2 $ git commit go.mod -m "Bump version to v2" $ git tag v2.0.0 $ git push --tags origin v2

 4.6 更新大版本并刷新依赖

即使发布了库的新不兼容版本,现有软件 也不会中断,因为它将继续使用现有版本1.0.1。go get -u 将不会获得版本2.0.0。 如果想使用v2.0.0,代码改成如下:

package main import ( "fmt" "github.com/jacksonyoudi/gomodone/v2" ) func main() { g, err := gomodone.SayHi("Roberto", "pt") if err != nil { panic(err) } fmt.Println(g) }

执行 go mod tidy

go: finding module for package github.com/jacksonyoudi/gomodone/v2 go: downloading github.com/jacksonyoudi/gomodone/v2 v2.0.0 go: found github.com/jacksonyoudi/gomodone/v2 in github.com/jacksonyoudi/gomodone/v2 v2.0.0

当然,两个版本都可以同时使用, 使用别名,如下:

package main import ( "fmt" "github.com/jacksonyoudi/gomodone" mv2 "github.com/jacksonyoudi/gomodone/v2" ) func main() { g, err := mv2.SayHi("Roberto", "pt") if err != nil { panic(err) } fmt.Println(g) fmt.Println(gomodone.SayHi("Roberto")) }

执行一下 go mod tidy

4.6 Vendoring

默认是忽略vendor的,如果想在项目目录下有vendor可以执行下面命令

$go vendor

当然,如果构建程序的时候,希望使用vendor中的依赖,

$ go build -mod vendor 5. Idea 下开发Go

5.1 创建项目

5.2 创建完项目,会自动生成go mod文件

如果需要修改,可以手动修改,加入git等操作

5.3 写业务逻辑代码

5.4 解决依赖,更新go.mod

5.5 go build

6. 常见问题

6.1 go.mod文件不能初始化在GOPATH根目录下,报错如下:

   

解决办法:

项目目录切换到非GOPATH根目录下 在GOPATH目录的子目录也可以初始化go.mod

推荐参考文章

浅谈goland导入自定义包时出错(一招解决问题)

一文搞懂 Go Modules 前世今生及入门使用

 



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有