内容简介:Golang的包管理分为三个阶段,version < 1.11、 1.11 <= version < 1.13、 version >= 1.13。在这个阶段,Golang的包管理存在以下不足:之所以设置GOPATH环境变量有两个原因:
Golang的包管理分为三个阶段,version < 1.11、 1.11 <= version < 1.13、 version >= 1.13。
version < 1.11
在这个阶段,Golang的包管理存在以下不足:
- 必须设置GOPATH环境变量,且源代码必须存放在GOPATH下
- 拉取外部依赖包时,总是拉取最新的版本,无法指定需要的版本
之所以设置GOPATH环境变量有两个原因:
go get
另外,由于无法指定依赖包的版本,因此容易导致“本地测试OK,但线上部署失败”的问题。这样的问题是广大开发者无法忍受的,所以,各种包管理 工具 开始涌现出来,典型的有dep,glide等,这里不再赘述。
1.11 <= version < 1.13
这个阶段默认使用的还是GOPATH的管理方式,但是开始支持 Go Module
的管理方式。
Go Module解决了上述的阶段存在的不足:
1.它不再需要GOPATH,即你的项目代码可以随意存放
2.它通过go.mod + go.sum解决依赖包的版本问题(后面会讲到)
如果需要迁移到Go Module,需要设置以下环境变量
vim ~/.bash_profile export GO111MODULE=on 复制代码
version >= 1.13
从这个阶段开始,Golang的包管理默认使用的是Go Module。
使用GOPATH进行包管理
注:为了完整性,这里尝试使用 go 1.11复现之前使用GOPATH进行包管理的情况。
1.下拉 docker 镜像
$ docker pull ubuntu:16.04 $ docker run -itd --name golang-lab ubuntu:16.04 /bin/bash $ apt-get update && apt-get install wget 复制代码
2.安装go 1.11
$ wget https://dl.google.com/go/go1.11.10.linux-amd64.tar.gz $ tar -zxvf go1.11.10.linux-amd64.tar.gz $ go/bin/go version go version go1.11.10 linux/amd64 复制代码
3.新建项目
3.1 这里我们假定 /home/go-projects
为我们的工作区
3.2 新建bin目录用于存放 可执行文件 ; 新建pkg目录用于存放 静态链接库文件 ; 新建src目录用于存放的我们 源码文件 , 一般我们写的代码都会放到这个目录下。
3.3 git.own.com
名称可自定义,这里只是个人编程习惯,表示这里存放的都是个人项目
$ mkdir /home/go-projects $ cd /home/go-projects && mkdir src && mkdir pkg && mkdir bin $ cd src && mkdir git.own.com && cd git.own.com $ mkdir gopath-lab && cd gopath-lab && touch main.go 复制代码
4.目录树
root@ebca4ae962aa:/home/go-projects# tree -L 4 . |-- bin |-- pkg `-- src `-- git.own.com `-- gopath-lab `-- main.go 复制代码
5.设置环境变量
- GOPATH:工作区路径,存放源代码。
- GOBIN:当使用go install xx.go 时, 生成的可执行文件就会放在此目录
- GOROOT:Go的安装位置,用于寻找标准库,这里是/home/go
$ vim ~/.bashrc export PATH=$PATH:/home/go/bin export GOPATH=/home/go-projects export GOBIN=/home/go-projects/bin export GOROOT=/home/go 复制代码
如果没有设置GOBIN,会报错
$ go install main.go go install: no install location for .go files listed on command line (GOBIN not set) 复制代码
6.main.go 代码如下:
package main import "github.com/gin-gonic/gin" func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080") } 复制代码
可以看到,直接 go run
并不能自动下载依赖
$ go run main.go main.go:3:8: cannot find package "github.com/gin-gonic/gin" in any of: /home/go/src/github.com/gin-gonic/gin (from $GOROOT) /home/go-projects/src/github.com/gin-gonic/gin (from $GOPATH) 复制代码
7.手动下载并测试
# 居然奇迹般下载成功了,一般这个时候需要设置代理 $ go get -v github.com/gin-gonic/gin # 可以看到,源码已经下载到src目录了 $ ls /home/go-projects/src/ git.own.com github.com golang.org gopkg.in # 再次执行,运行成功 $ go run main.go [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode) [GIN-debug] GET /ping --> main.main.func1 (3 handlers) [GIN-debug] Environment variable PORT is undefined. Using port :8080 by default [GIN-debug] Listening and serving HTTP on :8080 复制代码
使用Go Module进行包管理
本节翻译自 《Using Go Modules》
Module 是一系列依赖包的集合,通过 go mod init xxx
可初始化一份空的go.mod和go.sum,这两份文件存放于项目的根路径下。
对于go.mod,它不仅存储了这些依赖包的路径及其版本,同时 也指定了import的根路径 ,对于go.sum,它存放了依赖包内容的预期校验和,保证前一次下载的代码和现在下载的代码是一致的。
配置代理
由于Golang大部分依赖包都在国外,直接下载非常缓慢,在没有Go Module的时候,需要自己配置代理,比如socks;但是有了Go Module,就可通过设置环境变量来配置代理了,具体参考: goproxy.io/zh/。
配置时有几个注意点:
1.如果你有私有仓库和公共仓库,则需要加上 direct
参数,并配置 GOPRIVATE
(针对Go1.13)
# 有了direct,GOPRIVATE指定的仓库不会使用代理 go env -w GOPROXY=https://goproxy.io,direct # 设置不走代理的私有仓库,多个用逗号相隔 go env -w GOPRIVATE=*.corp.example.com 复制代码
2.如果你使用的是Golang IDE,则注意该IDE也要配置
3.如果你的~/.bash_profile或~./bashrc 文件存在GO111MODULE等环境变量,则go env 写入时会冲突
warning: go env -w GOPROXY=... does not override conflicting OS environment variable
初始化项目
1.新建文件夹
mkdir go-module-lab && cd go-module-lab
2.初始化Go Module项目,git.own.com/go-module是自定义的
go mod init git.own.com/go-module
3.查看go.mod
module git.own.com/go-module go 1.13 复制代码
添加代码测试
1.自定义库
mkdir hello && touch hello/hello.go
hello.go 内容
package hello func Hello() string { return "Hello, world." } 复制代码
2.新建main.go测试,内容如下
package main import ( "fmt" // 前面提过,go.mod 指定了import时的根路径 "git.own.com/go-module/hello" ) func main() { fmt.Println(hello.Hello()) } 复制代码
添加外部依赖
1.更新hello.go文件,引入 rsc.io/quote
依赖
package hello import "rsc.io/quote" func Hello() string { return quote.Hello() } 复制代码
2.执行 go run main.go
,会自动下载依赖
➜ go-module-lab go run main.go 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: 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 go: finding rsc.io/sampler v1.3.0 go: finding golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c Hello, world. 复制代码
3.查看go.mod
module git.own.com/go-module go 1.13 require rsc.io/quote v1.5.2 复制代码
可以看到,使用Go Module的包管理方式,Golang会自动帮我们处理包的依赖关系,并把缺失的包添加到go.mod,并使用 rsc.io/quote
的最新版本。(这里的最新版本应理解为最新并打了tag的版本,如果没有打tag,则会使用一种 pseudo-version
的方式标识,下文会说到)
4.借助go list命令查看所有依赖
$ go list -m all git.own.com/go-module golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c rsc.io/quote v1.5.2 rsc.io/sampler v1.3.0 复制代码
补充:
pseudo-versions(伪版本)
一般情况下,go.mod使用 语义化版本
来标志依赖包的版本号,比如v1.0.0、v1.0.1。
它包含三个部分:
- 主版本号:当你做了不兼容的 API 修改,比如v1.5.2的1
- 次版本号:当你做了向下兼容的功能性新增,比如v1.5.2的5
- 修订号:当你做了向下兼容的问题修正,比如1.5.2的2
语义化版本规定,同一个主版本号的必须向下兼容,比如v1.5.2必须向下兼容v1.1.0;如果代码不兼容,则必须使用新的版本号。
但是语义化版本是基于项目有打tag的情况下,如果一些项目没有打tag,则Golang会使用一种 pseudo-version
来标识,类似 v0.0.0-yyyymmddhhmmss-abcdefabcdef
的形式。
其中,yyyymmddhhmmss使用的是UTC时间,abcdefabcdef对应的是你这次commit的哈希值(前12位),
对于前缀v0.0.0,则有三种情况:
1.当你的项目一个tag都没有的时候,形式为v0.0.0-yyyymmddhhmmss-abcdefabcdef
2.当你项目最近打的tag的名称为vX.Y.Z-pre的时候,形式为vX.Y.Z-pre.0.yyyymmddhhmmss-abcdefabcdef
3.当你的项目最近打的tag的名称是vX.Y.Z的时候,形式为vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdefabcdef
go.sum
之所以有go.sum文件,是因为单纯地通过语义化版本(v1.5.2)无法确定每次通过v1.5.2标签下载的都是同一份代码。
比如发布者在 GitHub 上给自己的项目打上 v1.5.2 的tag之后,依旧可以删掉这个tag ,提交不同的内容后再重新打个 1.5.2 的 tag。
为了确定是否是同一份代码,go.sum存放了特定模块版本的内容的预期校验和,如果该代码有改动,则预期校验和不匹配,就会导致编译错误。
verifying xxx/base@v1.3.0: checksum mismatch downloaded: h1:T2eK+D0jzzeu4+S+oP9KvGgovPnl4FjxYShqdNSPrjc= go.sum: h1:Crwm2FliMjZ3BABjnydOpoJiFPaKcod/zYNOtcB9Xkw= 复制代码
更新外部依赖
更新次版本号
更新次版本号比较简单,直接使用go get即可,比如更新golang.org/x/text
go get golang.org/x/text
通过查看go.mod的变化,我们可以看到 golang.org/x/text
的版本号由v0.0.0-20170915032832-14c0d48ead0c升级到v0.3.2。(indirect表明该依赖包在源码中没有用到,是间接依赖的)
module git.own.com/go-module go 1.13 require ( golang.org/x/text v0.3.2 // indirect rsc.io/quote v1.5.2 ) 复制代码
除此之外,我们还可以更新到特定版本,在此之前,我们先看看该模块有哪些可用版本(以rsc.io/quote为例)
$ go list -m -versions rsc.io/quote rsc.io/quote v1.0.0 v1.1.0 v1.2.0 v1.2.1 v1.3.0 v1.4.0 v1.5.0 v1.5.1 v1.5.2 v1.5.3-pre1 复制代码
更新到特定版本:
go get rsc.io/quote@v1.4.0
如果想要使用特定的分支,只需要把版本号换成分支名即可(如果分支名包含特定符号,如"/",可用双引号将分支名括起来):
go get rsc.io/quote@dev
更新主版本号
如果需要更新主版本号,需要在代码中手动指定,因为不同主版本号相当于一个新的依赖(库)。
1.添加新函数
package hello import ( "rsc.io/quote" quoteV3 "rsc.io/quote/v3" ) func Hello() string { return quote.Hello() } func Proverb() string { return quoteV3.Concurrency() } 复制代码
2.自动下载依赖
package main import ( "fmt" "git.own.com/go-module/hello" ) func main() { fmt.Println(hello.Hello()) fmt.Println("proverb", hello.Proverb()) } 复制代码
3.查看go.mod
module git.own.com/go-module 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 ) 复制代码
从上面可以看出,Go Module每一个主版本号使用不同的路径表示,如v1,v2,v3;另外,Golang允许同时存在多个主版本号,因为路径不同,相当于是一个新的库,这样做的目的是保持增量迁移。
比如我一开始使用 rsc.io/quote
,后面有改动,且与之前不兼容,这是我就可以使用新的主版本号,比如 rsc.io/quote/v3
,但是Hello这个函数暂时还不能迁移到V3版本,这是多版本的作用就凸显出来了
删除多余依赖
当过了一段时间,我们已经把把 rsc.io/quote
的代码全部迁移到新版本 rsc.io/quote/v3
, 类似下面的代码
package hello import ( quoteV3 "rsc.io/quote/v3" ) func Hello() string { return quoteV3.HelloV3() } func Proverb() string { return quoteV3.Concurrency() } 复制代码
这时之前的go.mod里面的 rsc.io/quote
是多余的,我们可以通过 go mod tidy
删除多余的 rsc.io/quote
$ go mod tidy $ cat go.mod module git.own.com/go-module 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 ) 复制代码
总结
1.go mod init: 初始化一个Go Module项目,同时生成go.mod和go.sum文件
2.go build/go test/go run: 会自动下载依赖,并更新go.mod和go.sum文件
3.go list -m all:打印目前的所有依赖包
4.go get:手动下载依赖包,或者更改依赖包版本
5.go mod tidy:增加缺失的依赖,删除没有用到的依赖
其他命令
go env
配置一些环境变量。
# 环境变量说明文档 go help environment # 环境变量配置文件路径 $ go env GOENV /Users/xxx/Library/Application Support/go/env # 列出所有环境变量 go env # 列出所有环境变量(以json格式) go env -json # 修改某个环境变量 go env -w GOPROXY=https://goproxy.io,direct # 重置某个变量 go env -u GOPROXY 复制代码
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 数据管理流程,基础入门简介
- Rust包管理器Cargo入门
- 全栈开发入门实战:后台管理系统
- kustomize 入门:Kubernetes 原生配置管理工具
- Akka入门系列(四):akka cluster管理
- Helm 入门介绍:Kubernetes 上的包管理软件
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Numerical Methods and Methods of Approximation in Science and En
Karan Surana / CRC Press / 2018-10-31
ABOUT THIS BOOK Numerical Methods and Methods of Approximation in Science and Engineering prepares students and other readers for advanced studies involving applied numerical and computational anal......一起来看看 《Numerical Methods and Methods of Approximation in Science and En》 这本书的介绍吧!