标签 Package 下的文章

Go module机制下升级major版本号的实践

Go module机制Go 1.11版本引入,虽然也伴随着不小的质疑声,但总体上Go社区多数Gopher是接受go module的,很多标杆式的Go项目(比如kubernetes、kubernetes client-go等)也都逐渐转向了Go module,并且Gopher也在向core team反馈了自己的建议和问题。Go core team也在go module最初设计的基础上持续进行着改进,比如:即将到来的Go 1.13版本中将增加默认GOPROXY(https://proxy.golang.org)、GOSUMDB(sum.golang.org);增加GONOPROXY、GONOSUMDB以应对私有module的处理;不断丰富的go mod子命令功能等。

随着Go module应用的日益逐步广泛和深入,Gopher们也开始遇到一些最初使用Go module时未曾遇到过的问题,比如升级major版本号(这是由于多数Go project仍处于untag的状态或者1.x.x状态,因此在go module引入初期,少有gopher遇到该类问题)。这篇文章我们就来简单看看如何在go module机制下面升级库的主版本号(major version number)。

一. Go module的“semantic import versioning”

在Russ Cox关于go module的系列论述文章“semantic import versioning”一文中,Russ说明了Go import包兼容性的总原则:

如果新旧版本的包使用相同的导入路径(import path),那么新包与旧包是兼容的。

也就是说如果新旧两个包不兼容,那么应该采用不同的导入路径。

因此,Russ采用了将“major版本”作为导入路径的一部分的设计。这种设计支持在同一个项目或go source文件中import同一个module下的package的不同版本。同一个package虽然包名字相同,但是import path不同。vN作为import path的一部分将用于区分包的不同版本。同时在同一个源文件中,我们可以使用包别名的方式来区分同一个包的不同版本,比如:

import (

    "github.com/bigwhite/foo/bar"

    barV2 "github.com/bigwhite/foo/v2/bar"

    ... ...

)

go module的这种设计对Go包的consumer(包的使用者)来说似乎并未有太多额外工作,但是这给Go包的author们带来了一定的复杂性,他们需要考虑在go module机制下如何将自己的Go module升级major version。稍有不慎,可能就会导致自身代码库的混乱或者package consumer侧无法通过编译或执行行为的混乱。

下面我们就从go package author的角度实践一下究竟该如何做module major版本号的升级。Go module为go package author提供了两种major version升级的方案,我们下面逐一看一下。我们的实验环境基于go 1.12.5 (ubuntu 16.04)。

二. 使用“major branch”方案

“major branch”方案对于多数gopher来说是一个过渡比较自然的方案,它通过建立vN分支并基于vN分支打vN.x.x的tag的方式做major version的升级。当然是否建立vN分支以及打vN.x.x tag都是一个可选的操作。

我们在bitbucket.org上建立一个公共仓库:bitbucket.org/bigwhite/modules-major-branch,其初始结构和代码如下(注意:此时本地开发环境中GO111MODULE=on):

# tree -LF 2 modules-major-branch
modules-major-branch
├── foo/
│   └── foo.go
├── go.mod
└── README.md

1 directory, 3 files

//go.mod

# cat go.mod
module bitbucket.org/bigwhite/modules-major-branch

go 1.12

// foo.go

package foo

import "fmt"

func Foo() {
        fmt.Println("foo.Foo of module: bitbucket.org/bigwhite/modules-major-branch pre-v1")
}

接下来,我们建立modules-major-branch/foo包的消费者项目:modules-major-branch-test

# tree -LF 1 ./modules-major-branch-test/
./modules-major-branch-test/
├── go.mod
├── go.sum
└── main.go

0 directories, 3 files

# cat go.mod
module bitbucket.org/bigwhite/modules-major-branch-test

go 1.12

# cat main.go
package main

import (
    "bitbucket.org/bigwhite/modules-major-branch/foo"
)

func main() {
    foo.Foo()
}

我们run一下“消费者”:

# go run main.go
go: finding bitbucket.org/bigwhite/modules-major-branch/foo latest
go: finding bitbucket.org/bigwhite/modules-major-branch latest
go: downloading bitbucket.org/bigwhite/modules-major-branch v0.0.0-20190602132049-2d924da2e295
go: extracting bitbucket.org/bigwhite/modules-major-branch v0.0.0-20190602132049-2d924da2e295
foo.Foo of module: bitbucket.org/bigwhite/modules-major-branch pre-v1

我们看到在这个阶段消费成功。

作为modules-major-branch的author,随着module功能演进,modules-major-branch到达了发布1.0版本的节点:

# cat foo/foo.go
package foo

import "fmt"

func Foo() {
    fmt.Println("foo.Foo of module: bitbucket.org/bigwhite/modules-major-branch v1.0.0")
}

# git tag v1.0.0
# git push --tag origin master

接下来,我们让consumer升级对modules-major-branch/foo的依赖到v1.0.0。这种升级是不会自动进行,是需要consumer的开发者自己决策后手工进行的,否则会给开发者带来困惑。我们通过go mod edit命令修改consumer的require:

# go mod edit -require=bitbucket.org/bigwhite/modules-major-branch@v1.0.0

# cat go.mod
module bitbucket.org/bigwhite/modules-major-branch-test

go 1.12

require bitbucket.org/bigwhite/modules-major-branch v1.0.0

我们来运行一下升级依赖后的程序:

# go run main.go
go: finding bitbucket.org/bigwhite/modules-major-branch v1.0.0
go: downloading bitbucket.org/bigwhite/modules-major-branch v1.0.0
go: extracting bitbucket.org/bigwhite/modules-major-branch v1.0.0
foo.Foo of module: bitbucket.org/bigwhite/modules-major-branch v1.0.0

从pre-v1到v1在最新的go module机制中还算不上major版本的升级,接下来我们就来看看foo包的作者应该如何对modules-major-branch module做出不兼容的升级:v1 -> v2。

当modules-major-branch module即将做出不兼容升级时,一般会为当前版本建立维护分支(比如:v1分支,并在v1分支上继续对v1版本进行维护、打补丁),然后再在master分支上做出不兼容的修改。

# git checkout -b v1

# git checkout master

# cat foo/foo.go
package foo

import "fmt"

func Foo2() {
    fmt.Println("foo.Foo2 of module: bitbucket.org/bigwhite/modules-major-branch v2.0.0")
}

从代码可以看到,在master分支上,我们删除了foo包中的Foo函数,新增了Foo2函数。但仅做这些还不够。在本文一开始我们就提到过原则:如果新旧两个包不兼容,那么应该采用不同的导入路径。我们为modules-major-branch module做出了不兼容的修改,也需要modules-major-branch module有着不同的导入路径,我们需要修改modules-major-branch module的module根路径:

# cat go.mod
module bitbucket.org/bigwhite/modules-major-branch/v2

go 1.12

# git tag v2.0.0
# git push --tag origin master

我们在module根路径后面加上了v2,并基于master建立了tag: v2.0.0。

我们再来看看consumer端应该如何应对modules-major-branch module的不兼容修改。如果consumer要使用最新的Foo2函数的话,我们需要对main.go做出如下改动:

//modules-major-branch-test/main.go

package main

import (
    "bitbucket.org/bigwhite/modules-major-branch/v2/foo"
)

func main() {
    foo.Foo2()
}

接下来我们不需要手工修改modules-major-branch-test的go.mod中依赖,直接运行go run即可:

# go run main.go
go: finding bitbucket.org/bigwhite/modules-major-branch/v2/foo latest
go: finding bitbucket.org/bigwhite/modules-major-branch/v2 v2.0.0
go: downloading bitbucket.org/bigwhite/modules-major-branch/v2 v2.0.0
go: extracting bitbucket.org/bigwhite/modules-major-branch/v2 v2.0.0
foo.Foo2 of module: bitbucket.org/bigwhite/modules-major-branch v2.0.0

我们看到go编译器会自动发现依赖变更,并下载对应的包并更新go.mod和go.num:

# cat go.mod
module bitbucket.org/bigwhite/modules-major-branch-test

go 1.12

require (
    bitbucket.org/bigwhite/modules-major-branch v1.0.0
    bitbucket.org/bigwhite/modules-major-branch/v2 v2.0.0 // indirect
)

modules-major-branch-test此时已经不再需要依赖v1.0.0了,我们可以通过go mod tidy清理一下go.mod中的依赖:

# go mod tidy
# cat go.mod
module bitbucket.org/bigwhite/modules-major-branch-test

go 1.12

require bitbucket.org/bigwhite/modules-major-branch/v2 v2.0.0

我们看到:现在就只剩下对modules-major-branch v2的依赖了。

后续modules-major-branch可以在master分支上持续演进,直到又有不兼容改动时,可以基于master建立v2维护分支,master分支将升级为v3(module)。

再小结一下:

对包的作者而言,升级major版本号需要:

  • 升级module的根路径,增加vN

  • 建立vN.x.x形式的tag(可选,如果不打tag,go会在consumer的go.mod中使用伪版本号,比如:bitbucket.org/bigwhite/modules-major-branch/v2 v2.0.0-20190603050009-28a5b8da279e)

如果modules-major-branch内部有相互的包引用,那么在升级major号的时候,这些包的import路径也要增加vN,否则就会存在在高major version的代码中引用低major version包代码的情况,这也是包作者最容易忽略的事情。github.com/marwan-at-work/mod是一个为module作者提供的升级/降级major version号的工具,它可以帮助包作者方便地自动修改项目内所有源文件中的import path。有gopher已经提出希望go官方提供upgrade/downgrade的支持,但目前core team尚未明确是否增加。

对于consumer而言,升级依赖包的major版本号,只需要在import包时在import path中增加vN即可,当然代码中也要针对不兼容的部分进行修改,然后go工具会自动下载相关包。

三. 使用 “major subdirectory”方案

go module机制还提供了一种我个人觉得较为怪异的方案或者说用起来不那么自然的方案,那就是利用子目录分割不同主版本。如果某个module目前已经演化到v3版本了,那么这个module所在仓库的目录结构应该是这样的:

# tree modules-major-subdir
modules-major-subdir
├── bar
│   └── bar.go
├── go.mod
├── v2
│   ├── bar
│   │   └── bar.go
│   └── go.mod
└── v3
    ├── bar
    │   └── bar.go
    └── go.mod

这里直接用vN作为子目录名字,在代码仓库中将不同版本module放置在不同的subdir中,这样即便不打tag,通过subdir也能找到对应版本的module包。以上图中的v2为例,该子目录下go.mod如下:

# cat go.mod
module bitbucket.org/bigwhite/modules-major-subdir/v2

go 1.12

v3也是类似。在各自子目录中,module的根路径都是带有vN扩展的。

接下来,我们就来创建consumer来分别调用不同版本的modules-major-subdir/bar包。和modules-major-branch-test类似,我们建立modules-major-subdir-test来作为consumer调用modules-major-subdir/bar包:

// modules-major-subdir-test

# cat go.mod
module bitbucket.org/bigwhite/modules-major-subdir-test

go 1.12

# cat main.go
package main

import (
    "bitbucket.org/bigwhite/modules-major-subdir/bar"
)

func main() {
    bar.Bar()
}

运行一下consumer:

# go run main.go

go: finding bitbucket.org/bigwhite/modules-major-subdir/bar latest
go: finding bitbucket.org/bigwhite/modules-major-subdir latest
go: downloading bitbucket.org/bigwhite/modules-major-subdir v0.0.0-20190603053114-50b15f581aba
go: extracting bitbucket.org/bigwhite/modules-major-subdir v0.0.0-20190603053114-50b15f581aba
bar.Bar of module: bitbucket.org/bigwhite/modules-major-subdir

我们修改main.go,调用v2版本bar包中Bar2函数:

package main

import (
    "bitbucket.org/bigwhite/modules-major-subdir/v2/bar"
)

func main() {
    bar.Bar2()
}

再次运行main.go:

# go run main.go
go: finding bitbucket.org/bigwhite/modules-major-subdir v0.0.0-20190603053114-50b15f581aba
go: downloading bitbucket.org/bigwhite/modules-major-subdir v0.0.0-20190603053114-50b15f581aba
go: extracting bitbucket.org/bigwhite/modules-major-subdir v0.0.0-20190603053114-50b15f581aba
go: finding bitbucket.org/bigwhite/modules-major-subdir/v2/bar latest
go: finding bitbucket.org/bigwhite/modules-major-subdir/v2 latest
go: downloading bitbucket.org/bigwhite/modules-major-subdir/v2 v2.0.0-20190603063223-4be5d54167e9
go: extracting bitbucket.org/bigwhite/modules-major-subdir/v2 v2.0.0-20190603063223-4be5d54167e9
bar.Bar2 of module: bitbucket.org/bigwhite/modules-major-subdir v2

我们看到:go编译器自动找到了位于modules-major-subdir仓库下v2子目录下的v2版本bar包。

从demo来看,似乎这种通过subdir方式来实现major version升级的方式更为“简单”一些。但笔者总感觉这种方式有些“怪”,尤其是在与tag交叉使用时可能会带来一些困惑,其他主流语言也鲜有使用这种方式进行major version升级的。另外一旦使用这种方式,似乎也很难利用git工具在不同major版本之间进行代码的merge(复用)了。

另外和major branch方案一样,如果module内部有相互的包引用,那么在升级major号的时候,这些包的import路径也要增加vN,否则也会存在在高major version的代码中引用低major version包代码的情况。

四. 小结

Go module作为主流语言依赖管理思路之外的一个“探索性”创新,势必在初期要有一段坎坷的道路要走。好事多磨,相信经过Go 1.11~Go 1.13三个版本的改进以及社区在工具方面对go module的逐渐的完善的支持,Go module会成为gopher日常Go开发的一柄利器,彻底解决Go的包依赖问题。

上述demo源码可在bitbucket.org/bigwhite下找到。

另外这里要提一点:国内的码云(gitee.com)目前对go module major version升级支持的还有问题。同样的操作,但在gitee.com下总是提示:“go.mod has post-v0 module path”


我的网课“Kubernetes实战:高可用集群搭建、配置、运维与应用”在慕课网上线了,感谢小伙伴们学习支持!

我爱发短信:企业级短信平台定制开发专家 https://tonybai.com/
smspush : 可部署在企业内部的定制化短信平台,三网覆盖,不惧大并发接入,可定制扩展; 短信内容你来定,不再受约束, 接口丰富,支持长短信,签名可选。

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

我的联系方式:

微博:https://weibo.com/bigwhite20xx
微信公众号:iamtonybai
博客:tonybai.com
github: https://github.com/bigwhite

微信赞赏:
img{512x368}

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

Go 1.12中值得关注的几个变化

Go team如期在2月末发布了Go 1.12版本。从Go 1.12的Release Notes粗略来看,这个版本相较于之前增加了go modules机制WebAssembly支持Go 1.11,变化略“小”。这也给下一个Go 1.13版本预留了足够的“惊喜”空间:)。从目前的plan来看,Go 1.13很可能落地的包括:Go2的几个proposals:Go 2 number literals, error valuessigned shift counts等,以及优化版Escape Analysis等。

言归正传,我们来看看Go 1.12版本中值得我们关注的几个变化。

一. Go 1.12的可移植性

Go 1.12一如既往的保持了Go1兼容性规范,使用Go 1.12编译以往编写的遗留代码,理论上都可以编译通过并正常运行起来。这是很难得的,尤其是在”Go2″有关proposal逐步落地的“时间节点”,想必Go team为了保持Go1付出了不少额外的努力。

Go语言具有超强的可移植性。在Go 1.12中,Go又增加了对aix/ppc64、windows/arm的支持,我们可以在运行于树莓派3的Windows 10 IoT Core上运行Go程序了。

但是对于一些较老的平台系统,Go也不想背上较重的包袱。Go也在逐渐“放弃”一些老版本的系统,比如Go 1.12是最后一个支持macOS 10.10、FreeBSD 10.x的版本。在我的一台Mac 10.9.2的老机器上运行go 1.12将会得到下面错误:

$./go version
dyld: Symbol not found: _unlinkat
  Referenced from: /Users/tony/.bin/go1.12/bin/./go
  Expected in: flat namespace

[1]    2403 trace trap  ./go version

二. Go modules机制的优化

1. GO111MODULE=on时,获取go module不再显式需要go.mod

用过Go 1.11 go module机制的童鞋可能都遇到过这个问题,那就是在GO111MODULE=on的情况下(非GOPATH路径),我要go get某个package时,如果compiler没有在适当位置找到go.mod,就会提示如下错误:

//go 1.11.2

# go get github.com/bigwhite/gocmpp
go: cannot find main module; see 'go help modules'

或

# go get github.com/bigwhite/gocmpp
go: cannot determine module path for source directory /Users/tony/test/go (outside GOPATH, no import comments)

这显然非常不方便。为了go get 一个package,我还需要显式地创建一个go.mod文件。在Go 1.12版本中,这个问题被优化掉了。

//go 1.12

# go get github.com/bigwhite/gocmpp
go: finding github.com/bigwhite/gocmpp latest
go: finding golang.org/x/text/encoding/unicode latest
go: finding golang.org/x/text/transform latest
go: finding golang.org/x/text/encoding/simplifiedchinese latest
go: finding golang.org/x/text/encoding latest
go: downloading golang.org/x/text v0.3.0
go: extracting golang.org/x/text v0.3.0

其他在go 1.11.x中对go.mod显式依赖的命令,诸如go list、go mod download也在Go 1.12版本中和go get一样不再显式依赖go.mod。

并且在Go 1.12中go module的下载、解压操作支持并发进行,前提是go module的Cache路径:$GOPATH/pkg/mod必须在一个支持file locking的文件系统中。

2. go.mod中增加go指示字段(go directive)

go 1.12版本在go.mod文件中增加了一个go version的指示字段,用于指示该module内源码所使用的 go版本。使用go 1.12创建的go.mod类似下面这样:

# go mod init github.com/bigwhite/test
go: creating new go.mod: module github.com/bigwhite/test
# cat go.mod
module github.com/bigwhite/test

go 1.12

按照release notes中的说法,如果go.mod中go指示器指示的版本高于你使用的go tool链版本,那么go也会尝试继续编译。如果编译成功了,那也是ok的。但是如果编译失败,那么会提示:module编译需要更新版本的go tool链。

我们使用go 1.11.4版本go compiler编译下面的上面github.com/bigwhite/test module的代码:

// main.go

package main

import (
    "fmt"
)

func main() {
    fmt.Println("go world")
}

# go build main.go
# ./main
go world

我们看到,虽然go tool chain版本是1.11.4,低于go.mod中的go 1.12,但go 1.11.4仍然尝试继续编译代码,并且顺利通过。

如果我们将代码“故意”修改为下面这样:

//main.go

package main

import (
        "fmt"
)

func main() {
        fmt.Printl("go world") // 这里我们故意将Println写成Printl
}

再用go 1.11.4编译这段代码:

# go build main.go
# command-line-arguments
./main.go:8:2: undefined: fmt.Printl
note: module requires Go 1.12

我们看到go 1.11.4 compiler提示“需要go 1.12″版本编译器。从这里我们看出,我们可以使用go指示器用作module最低version约束的标识。在没有go指示器时,我们只能在文档上显式增加这种约束的描述。

不过,这里有一个小插曲,那就是这种不管go.mod中go版本号是多少,仍然尝试继续编译的机制仅适用于go 1.11.4以及后续高版本。从引入go module的go 1.11到go 1.11.3目前都还不支持这种机制,如果用go 1.11.3尝试编译以下上面的代码,会得到如下结果:

# go build main.go
go build command-line-arguments: module requires Go 1.12

go 1.11.3不会继续尝试编译,而是在对比当前go tool chain版本与go.mod中go指示器的version后,给出了错误的提示并退出。

如果非要使用低于go 1.11.4版本的编译器去编译的话,我们可以使用go 1.12工具链的go mod edit -go命令来修改一下go.mod中的版本为go 1.11。然后再用go 1.11.4以下的版本去编译:

# go mod edit -go=1.11
# cat go.mod
module github.com/bigwhite/test

go 1.11

# go build main.go  //使用go 1.11.3编译器

这样,我们就可用go 1.11~go 1.11.3正常编译源码了。

三. 对binary-only package的最后支持

我在2015的一篇文章 《理解Golang包导入》中提及到Go的编译对源码的依赖性。对于开源工程中的包,这完全不是问题。但是对于一些商业公司而言,源码是公司资产,是不能作为交付物提供给买方的。为此,Go team在Go 1.7中增加了对binary-only package的机制。

所谓”binary-only package”就是允许开发人员发布不包含源码的二进制形式的package,并且可直接基于该二进制package进行编译。比如下面这个例子:

// 创建二进制package

# cat $GOPATH/src/github.com/bigwhite/foo.go
package foo

import "fmt"

func HelloGo() {
    fmt.Println("Hello,Go")
}

# go build -o  $GOPATH/pkg/linux_amd64/github.com/bigwhite/foo.a

# ls $GOPATH/pkg/linux_amd64/github.com/bigwhite/foo.a
/root/.go/pkg/linux_amd64/github.com/bigwhite/foo.a

# mkdir temp
# mv foo.go temp

# touch foo.go

# cat foo.go

//go:binary-only-package

package foo

import "fmt"

# cd $GOPATH

# zip -r foo-binary.zip src/github.com/bigwhite/foo/foo.go pkg/linux_amd64/github.com/bigwhite/foo.a
updating: pkg/linux_amd64/github.com/bigwhite/foo.a (deflated 42%)
  adding: src/github.com/bigwhite/foo/foo.go (deflated 11%)

我们将foo-binary.zip发布到目标机器上后,进行如下操作:

# unzip foo-binary.zip -d $GOPATH/
Archive:  foo-binary.zip
  inflating: /root/.go/pkg/linux_amd64/github.com/bigwhite/foo.a
  inflating: /root/.go/src/github.com/bigwhite/foo/foo.go

接下来,我们就基于二进制的foo.a来编译依赖它的包:

//$GOPATH/src/bar.go

package main

import "github.com/bigwhite/foo"

func main() {
        foo.HelloGo()
}

# go build -o bar bar.go
# ./bar
Hello,Go

但是经过几个版本的迭代,Go team发现:对binary-only package越来越难以提供安全支持,无法保证binary-only package的编译使用的是与最终链接时相同的依赖版本,这很可能会造成因内存问题而导致的崩溃。并且经过调查,似乎用binary-only package的gopher并不多,并且gopher可以使用plugin、shared library、c-shared library等来替代binary-only package,以避免源码分发。于是Go 1.12版本将成为支持binary-only package的最后版本。

四. 运行时与标准库

经过Go 1.5~Go 1.10对运行时,尤其是GC的大幅优化和改善后,Go 1.11、Go 1.12对运行时的改善相比之下都是小幅度的。

在Go 1.12中,一次GC后的内存分配延迟得以改善,这得益于在大量heap依然存在时清理性能的提升。运行时也会更加积极地将释放的内存归还给操作系统,以应对大块内存分配无法重用已存在的堆空间的问题。在linux上,运行时使用MADV_FREE释放未使用的内存,这更为高效,操作系统内核可以在需要时重用这些内存。

在多CPU的机器上,运行时的timer和deadline代码运行性能更高了,这对于提升网络连接的deadline性能大有裨益。

标准库最大的改变应该算是对TLS 1.3的支持了。不过默认不开启。Go 1.13中将成为默认开启功能。大多数涉及TLS的代码无需修改,使用Go 1.12重新编译后即可无缝支持TLS 1.3。

另一个”有趣“的变化是syscall包增加了Syscall18,依据syscall包中函数名字惯例,Syscall18支持最多传入18个参数,这个函数的引入是为了Windows准备的。现在少有程序员会设计包含10多个参数的函数或方法了,这估计也是为了满足Windows中“遗留代码”的需求。

五. 工具链及其他

1. go安装包中移除go tour

go tour被从go的安装包中移除了,Go的安装包从go 1.4.x开始到go 1.11.x变得日益“庞大”:以linux/amd64的tar.gz包为例,变化趋势如下:

go 1.4.3:  53MB
go 1.5.4:  76MB
go 1.6.4:  83MB
go 1.7.6:  80MB
go 1.8.7:  96MB
go 1.9.7:  113MB
go 1.10.8: 97MB
go 1.11.5: 134MB
go 1.12:   121MB

后续预计会有更多的非核心功能将会从go安装包中移除来对Go安装包进行瘦身,即便不能瘦身,也至少要保持在现有的size水平上。

本次go tour被挪到:golang.org/x/tour中了,gopher们可单独安装tour:

# go get -u golang.org/x/tour

# tour //启动tour

Go 1.12也是godoc作为web server被内置在Go安装包的最后一个版本,在Go 1.13中该工具也会被从安装包中剔除,如有需要,可像go tour一样通过go get来单独安装。

2. Build cache成为必需

build cache在Go 1.10被引入以加快Go包编译构建速度,但是在Go 1.10和Go 1.11中都可以使用GOCACHE=off关闭build cache机制。但是在Go 1.12中build cache成为必需。如果设置GOCACHE=off,那么编译器将报错:

# GOCACHE=off  go build github.com/bigwhite/gocmpp
build cache is disabled by GOCACHE=off, but required as of Go 1.12

3. Go compiler支持-lang flag

Go compiler支持-lang flag,可以指示编译过程使用哪个版本的Go语法(注意不包括标准库变化等,仅限于语言自身语法)。比如:

//main.go

package main

import "fmt"

type Int = int

func main() {
        var a Int = 5
        fmt.Println(a)
}

# go run main.go
5

上面是一个使用了Go 1.9才引入的type alias语法的Go代码,我们使用Go 1.12可以正常编译运行它。但是如果我使用-lang flag,指定使用go1.8语法编译该代码,我们会得到如下错误提示:

# go build  -gcflags "-lang=go1.8" main.go
# command-line-arguments
./main.go:5:6: type aliases only supported as of -lang=go1.9

换成-lang=go1.9就会得到正确结果:

# go build  -gcflags "-lang=go1.9" main.go
# ./main
5


我的网课“Kubernetes实战:高可用集群搭建、配置、运维与应用”在慕课网上线了,感谢小伙伴们学习支持!

我爱发短信:企业级短信平台定制开发专家 https://tonybai.com/
smspush : 可部署在企业内部的定制化短信平台,三网覆盖,不惧大并发接入,可定制扩展; 短信内容你来定,不再受约束, 接口丰富,支持长短信,签名可选。

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

我的联系方式:

微博:https://weibo.com/bigwhite20xx
微信公众号:iamtonybai
博客:tonybai.com
github: https://github.com/bigwhite

微信赞赏:
img{512x368}

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



StatCounter - Free Web Tracker and Counter View My Stats