SerendipityEx

关注成长,记录生活

Go 1.11 新特性:Go Modules

Go 1.11 为 modules 带来了实验性支持,这是Go的一个新的依赖管理系统。

1. Modules

示例来自 Introduction to Go Modules

go module 支持在 $GOPATH 之外建立 package。并且在 $GOPATH 中是默认禁止的。

$GOPATH 使用 go module,要设置 GO111MODULE=on,如果没设置,执行命令的时候会有提示。

go.mod

download    download modules to local cache (下载依赖的module到本地cache))
edit        edit go.mod from tools or scripts (编辑go.mod文件)
graph       print module requirement graph (打印模块依赖图))
init        initialize new module in current directory (再当前文件夹下初始化一个新的module, 创建go.mod文件))
tidy        add missing and remove unused modules (增加丢失的module,去掉未用的module)
vendor      make vendored copy of dependencies (将依赖复制到vendor下)
verify      verify dependencies have expected content (校验依赖)
why         explain why packages or modules are needed (解释为什么需要依赖)

创建一个Module

$ mkdir testmod
$ cd testmod

$ go mod init github.com/robteix/testmod
go: creating new go.mod: module github.com/robteix/testmod

创建一个简单的package 文件,

package testmod

import "fmt" 

// Hi returns a friendly greeting
func Hi(name string) string {
   return fmt.Sprintf("Hi, %s", name)
}

让我们看看 go.mod 中的内容:

 cat go.mod
module github.com/mjyi/testmod

下面就可以把代码提交到github上面使用了。

$ git init 
$ git add * 
$ git commit -am "First commit" 
$ git push -u origin master

现在,我们可以通过 go get 在任何地方使用它:

$ go get github.com/robteix/testmod

这样将获取 master 上的最新代码,但是,本质上,获取 master 使用是非常危险的,因为我们不知道包的作者作出了什么改变可能影响我们的使用。这也是 modules 旨在解决的问题。

Module 版本

一个 module 必须以 v(major).(minor).(patch) 的形式进行语义版本化,例如v0.1.0,v1.2.3或v3.0.1。 v 是必需的。如果使用Git,发布 tag 提交版本。

关于语义化版本参考:语义化版本 2.0

发布一个 release 版本

git tag v1.0.0
git push --tags

这样就创建了在 Github 上面创建了一个tag,release 1.0.0。

git checkout -b v1
git push -u origin v1

创建一个新的分支(v1),方便我们推送 bug 修复。 现在在 master 上发布修改就不用担心会破坏我们的发布版本。

发布一个 bugfix release

发布了一个版本之后,如果发现了一些小的bug,那么我们需要发布一个新版本来修复它们。 我们在 v1 分支中修改这些,因为它与之后为 v2 做的事情无关,但在实际上,你也许会在 master 中进行修改,然后再向后移植。无论哪种方式,我们都需要在 v1 分支中进行修复并将其标记为新版本。

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

更新 modules

  • go get -u 更新到最新的 minor 或者 patch realease (i.e. 从 1.0.0 更新到 1.0.1,如果有 1.1.0, 则更新到 1.1.0)
  • go get -u=patch 更新到最新的 patch release (i.e. 从 1.0.0 更新到 1.0.1, 不会更新1.1.0)
  • go get package@version 更新到指定版本 (i.e. github.com/robteix/testmod@v1.0.1)

Major versions

根据语义版本语义,major 版本与 minors 不同。major 版本可以破坏向后兼容性。 从 Go modules 的角度来看,major 版本是一个完全不同的包。这听起来可能很奇怪,但它是有道理的:一个库的两个彼此不兼容的版本是两个不同的库。

我们来提交一个新版本 v2,将新版本路径附加到模块名称的末尾来完成此操作。

module github.com/robteix/testmod/v2
git commit testmod.go -m "Change Hi to allow multilang"
git checkout -b v2 # optional but recommended
echo "module github.com/robteix/testmod/v2" > go.mod
git commit go.mod -m "Bump version to v2"
git tag v2.0.0
git push --tags origin v2 # or master if we don't have a branch

更新到新的 major 版本

即使我们发布了一个新的不兼容版本的库,现有的软件也不会被破坏,因为它将继续使用现有的1.0.1版本。 go get -u 将无法获得2.0.0版本。

我们可以这样使用 v2 版本:

package main

import (
    "fmt"
    "github.com/robteix/testmod/v2" 
)

func main() {
    g, err := testmod.Hi("Roberto", "pt")
    if err != nil {
        panic(err)
    }
    fmt.Println(g)
}

正如之前据说,v2版本是一个完全不同的包。 所以我们可以在同一个二进制文件中使用两个不兼容的库:

package main
import (
    "fmt"
    "github.com/robteix/testmod"
    testmodML "github.com/robteix/testmod/v2"
)

func main() {
    fmt.Println(testmod.Hi("Roberto"))
    g, err := testmodML.Hi("Roberto", "pt")
    if err != nil {
        panic(err)
    }
    fmt.Println(g)
}

Vendoring

Go modules 默认会忽略 vendor/ 目录。

go mod vendor 会在项目根目录创建一个 vendor/ 目录,其包含项目的所有依赖项。

参考

Go Modules Introduction to Go Modules

Clang 简单使用