工具

00-Go工具 阅读更多

0.1. 命令 0.1.1. go build 0.1.1.1. 编译单个包 0.1.2. 参数解释 0.2. go get 0.2.1. 参数解释 0.2.2. 修改源码存储路径 0.1. 命令 Go语言常用内置工具的使用方法及参数含义。 Go是一个管理Go源代码的工具。 用法: go <命令> [参数] 可选的命令如下: 命令 描述 中文 bug start a bug report 启动一个Bug报告 build compile packages and dependencies 编译包和依赖项 clean remove object files and cached files 删除目标文件和缓存文件 doc show documentation for package or symbol 显示包或符号的文档 env print Go environment information 打印Go环境信息 fix update packages to use new APIs 更新包以使用新API fmt gofmt (reformat) package sources 重新格式化源码包 generate generate Go files by processing source 通过处理源生成Go文件 get download and install packages and dependencies 下载并安装包和依赖项 install compile and install packages and dependencies 编译和安装包和依赖项 list list packages or modules 列出包或模块 mod module maintenance 模块维护 run compile and run Go program 编译并运行Go程序 test test packages 测试包 tool run specified go tool 运行指定的go工具 version print Go version 打印Go版本 vet report likely mistakes in packages 报告包中可能出现的错误 使用 go help <命令> 来获取更多关于命令的信息。 其他帮助主题: 主题 描述 中文 buildmode build modes 编译模式 c calling between Go and C 在GO和C之间调用 cache build and test caching 编译和测试缓存 environment environment variables 环境变量 filetype file types 文件类型 go.mod the go.mod file go.mod文件 gopath GOPATH environment variable GOPATH环境变量 gopath-get legacy GOPATH go get goproxy module proxy protocol 模块代理协议 importpath import path syntax 导入路径语法 modules modules, module versions, and more 模块和模块版本等 module-get module-aware go get packages package lists and patterns 包列表和模式 testflag testing flags 测试标志 testfunc testing functions 测试功能 使用 go help <主题> 来获取更多关于主题的信息。 0.1.1. go build 默认不会编译目标代码包所依赖的那些代码包。如果被依赖的代码包的归档文件不存在,或者源码文件有了变化,那么它还是会被编译的。 用法: go build [-o output] [-i] [build flag] [packages] # 编译导入路径命名的包以及它的依赖项,但是不会安装编译的结果 # 如果编译的参数是.go文件的列表,编译过程将它们作为单个包的一系列源文件 0.1.1.1. 编译单个包 当编译单个main包时,编译输出的结果文件命名如下,(编写windows可执行文件时,以.exe为后缀): # 同一个main包,对多个源文件编译 go build ed.go rx.go # 输出结果为 ed 或 ed.exe,以第一个源码文件名为输出文件名 # 同一个main包,对源码目录编译 go build unix/sam # 输出结果为 sam 或 sam.exe,以源码目录为输出文件名 当编译多个包或一个非main包时,能够编译这些包但会丢弃结果文件,仅用作检查包是否可以编译。 在编译包的时候,编译过程会忽略以_test.go结尾的文件。 0.1.2. 参数解释 编译时用到的参数标志被build、clean、get、install、list、run、test共享。 -a,强制重新编译所有代码包(含标准库) -i,安装被编译目标依赖的包 -n,仅显示编译命令,但不执行 -o,只能在编译单个包时使用,指定输出结果名(取代上面两种方式生成的默认名) -p n,可以并行运行的程序(例如编译命令或测试二进制文件)的数量。默认值是可用的CPU数。 -v,显示待编译包名,通常与-a搭配使用 -x,显示正在执行的编译命令 asmflag,传递给asm的参数 buildmode mode,编译模式 compiler name,要使用的编译器名称(如运行时编译器gccgo或gc) gccflags,传递给gcc编译器或链接器的参数 -gcflags,传递给编译器的参数 installsuffix suffix,在代码包安装目录名称中使用的后缀,以便将输出与默认编译分开 -ldflags,传递给链接器的参数 linkshared,链接使用-buildmode=shared创建的共享库 -mod mode,下载模块来使用(readonly或vendor) pkgdir dir,从dir安装并加载所有的包(如使用非标准编译时,使用该参数将生成的包保存在单独的位置) masn,启动与内存清理程序的互操作(仅支持amd64,clang/LLVM作为C编译器) -race,启动数据竞争检查(仅支持amd64) tags, 以空格分隔的编译标志列表 toolexec,用于调用vet和asm等工具链程序的程序 -work,显示临时工作目录,完成后不删除 0.2. go get go get命令会自动从一些主流的公用代码库(如GitHub)下载目标代码包,并安装到GOPATH内第一个工作区对应目录中。 如果存在GOBIN环境变量,那么仅包含命令源码文件的代码会被安装到GOBIN指向的目录中。 用法: go get [-d] [-f] [-t] [-u] [-v] [-fix] [-insecure] [build flags] [packages] 0.2.1. 参数解释 -d,只下载代码包,不安装 -f,仅在设置-u时有效,强制get -u不验证代码包是否为源仓库的分支(如果源是原始代码的本地分支,这很有用) -fix,在下载代码包后先根据当前Go版本执行代码修正工具,然后再进行依赖项解析或编译代码包 -insecure,允许通过非安全的网络协议(如HTTP)下载和安装代码包 -t,同时下载测试所需的代码包 -u,通过网络更新指定的代码包和它的依赖(默认情况下,get使用网络检查出缺失的包,但不使用它来查找现有包的更新) -v,启用详细进度和调试输出 get命令同样支持使用build的参数来控制安装过程。 Go语言官方提供的go get命令是非常基础的,其中并没有提供依赖管理功能。目前GitHub上有很多提供这类功能的第三方工具,比如glide、gb以及官方出品的dep、vgo等,它们在内部大多会直接受用go get。 查看或更新代码包时,查找源端与本地安装的Go版本匹配的分支或标记。 如果本地安装运行版本为“go1”,则搜索名为“go1”的分支或标记。 如果不存在此类版本,则会检索包的默认分支。 当get查看或更新git存储库是,它还会更新存储库引用的任何git子模块。 0.2.2. 修改源码存储路径 当修改存储源码的代码仓库或者代码包的相对路径时,为了让代码包的远程导入路径不受此类变更的影响,可以使用自定义的代码包导入路径。 对代码包的远程导入路径进行自定义:在该代码包中的库源码文件的包声明语句的右侧加入导入注释,如下: package semaphore // import "golang.org/x/sync/semaphore" 这个代码包原来的完整导入路径是github/golang/x/sync/semaphore,这与实际存储它的网络地址一样,该代码包的源码实际存放在GitHub网站的golang组的sync代码仓库的semaphore目录下。加入导入注释后,用以下命令即可下载并安装该代码包了: go get golang.org/x/sync/semaphore 在Go语言官网golang.org下的路径/x/sync/semaphore并不存放semaphore包的真实地址。称之为代码包的自定义导入路径。 不过,这还是需要在golang.org这个域名背后的服务端程序上,添加一些支持才能使这条命令成功。具体参考这里:https://github.com/hyper0x/go_command_tutorial/blob/master/0.3.md

Go-Modules 阅读更多

Go 语言中一直被人诟病的一个问题就是没有一个比较好用的依赖管理系统。 GOPATH 的设计让开发者一直有很多怨言,在 Go 语言快速发展的过程中也出现了一些比较优秀的依赖管理工具,比如: govendor dep glide 有一些差不多成了半官方的工具了,但是这些工具都还是需要依赖于GOPATH。 随着 Go1.11 的发布,Golang 官方给我们带来了依赖管理的全新特性 Go Modules,这是 Golang 全新的一套依赖管理系统。 新建Modules 要使用 Go Modules 首先需要保证你环境中 Golang 版本大于 1.11: $ go version go version go1.12.7 linux/amd64 Go Modules 主要就是为了消除 GOPATH 的,所以新建的项目可以完全不用放在 $GOPATH/src 目录下面,任何地方都可以。 第一步 在bashrc中配置环境变量以启动Go Modules: vim ~/bashrc export GO111MODULE=on 编写库函数: package stringsx import ( "fmt" ) func Hello(name string) string{ return fmt.Sprintf("Hello, %s", name), nil } 第二步 在项目根目录初始化GO Modules: go mod init <模块名> # 该命令会在当前目录下生成go.mod文件 # 生成的内容就是包含一个模块名称的声明 module <模块名> 注意:模块名非常重要,相当于声明了模块的名称,后面使用该模块就需要使用这个名称来获取模块。 第三步 上述第二步,将当前包变成了一个Module,将代码推送到仓库,以Github为例,仓库地址为:https://github.com/sample_golang_module/example。 git init git add . git commit -am "init commit" git remote add origin git@github.com:sample_golang_module/example.git git push -u origin master 第四步 至此,完成了最简单的Go Module编写,其他任何开发者想要使用这个模块,通过go get命令来获取: go get github.com/sample_golang_module/example 上面的命令是获取master分支的最新代码,这没有问题但不是最佳实践,模块可能要更新内容或者修复Bug,放在master分支会造成使用者的混乱,很可能使用者的代码在模块更新后就不兼容了,Go Modules可以很好的解决版本问题。 Module版本管理 Go Modules是需要进行版本化管理的,强烈推荐使用语义化版本控制,最主要的版本规则如下: 版本格式:主版本号.次版本号.修订号 版本号递增规则如下: 主版本号:当你做了不兼容的API修改 次版本号:当你做了向下兼容的功能性新增 修订号:当你做了向下兼容的问题修正 先行版本号及版本编译元数据可以加到“主版本号.次版本号.修订号”的后面,作为延伸。 在使用Go Modules查找版本的时候,会使用仓库中的tags并且某些版本和其他版本有一些不同之处,比如V2或者更高的版本要和V1的版本模块的导入路径是不同的,这样才能通过模块来区分使用的是不同的版本,默认情况下,Golang会获取仓库中最新的tag版本。 最重要的一点,发布模块时,要使用git tag来标记仓库的版本。 发布第一个版本 发布release包,需要给当前的包打上tag,使用语义化的版本,如v1.0.0: git tag v1.0.0 git push --tags 更好的方法是创建一个名叫v1的新分支,这样可以方便以后修复当前版本代码中的Bug,也不会影响到master或者其他分支的代码: git checkout -b v1 git push -u origin v1 模块使用 模块已经准备好,创建一个简单的程序来使用上面的模块: package main import ( "fmt" "github.com/sample_golang_module/example/stringsx" ) func main() { fmt.Println(stringsx.Hello("cnych")) } 在程序中使用github.com/sample_golang_module/example/stringsx这个包,在导入之前,使用go get命令将这个包拉到GOPATH或者vendor目录下即可,将这个包当成module来使用。 # 在当前项目下初始化module go mod init # 不写模块名,则与package名相同 go run main.go go: finding github.com/sample_golang_module/example v1.0.0 go: downloading github.com/sample_golang_module/example v1.0.0 Hello, cnych 执行完成后,上面的命令会自动下载程序中导入的包,下载完成后查看当前项目的go.mod文件: module <当前项目模块名>require github.com/cnych/stardust v1.0.0 并且还在当前目录下生成一个名为go.sum的新文件,里面包含了依赖包的一些hash信息,用来确保文件和版本的正确性: github.com/sample_golang_module/example v1.0.0 h1:8EcmmpIoIxq2VrzXdkwUYTD4OcMnYlZuLgNntZ+DxUE=github.com/sample_golang_module/example v1.0.0/go.mod h1:Qgo0xT9MhtGo0zz48gnmbT9XjO/9kuuWKIOIKVqAv28= 模块会被下载到$GOPATH/pkg/mod目录下: $ ls $GOPATH/pkg/mod/github.com/sample_golang_module/example example@v1.0.0 这样就成功使用了编写的模块。 发布一个bugfix版本 发现模块中的Hello函数有bug,需要修复并发布一个新版本: func Hello(name string) string{ return fmt.Sprintf("Hello, %s!!!", name) } 在v1这个分支上进行fix,完成之后merge到master分支上去,然后发布一个新的版本,遵从语义化版本骨子额,修正一个bug之后需要添加修正版本号,即v1.0.1: git add . git commit -m "fix Hello function #123" git tag v1.0.1 git push --tags origin v1 更新modules 默认情况下,Golang不会自动更新模块,如果自动更新会造成版本管理混乱,所以需要明确告知Golang需要更新的模块,通过以下几种方式: 运行go get -u xxx命令来获取最新版的模块 运行go get package@version命令来更新指定版本的模块 直接更新go.mod文件中的模块依赖版本,然后执行go mod tidy命令来更新 更新之后,go.mod文件中的依赖模块的版本会变化,¥GOPATH/pkg/mod文件夹中会增加新版本的模块。 在Go Modules中,每个版本都是独立的文件夹,这样就不会出现版本冲突。 主版本升级 根据语义化版本规则,主版本升级的不向后兼容的,从Go Modules的角度来看,主版本是一个完全不同的模块了,因为两个大版本之间是互相不兼容的。 修改模块中的Hello函数: func Hello(name, lang string) (string, error) { switch lang { case "en": return fmt.Sprintf("Hi, %s!", name), nil case "zh": return fmt.Sprintf("你好, %s!", name), nil case "fr": return fmt.Sprintf("Bonjour, %s!", name), nil default: return "", fmt.Errorf("unknow language") } } 这里需要切换到master分支进行修改,因为v1分支和现在修改的内容是完全不同的版本。 函数有两个参数,返回值也有两个,与v1不兼容。需要更新版本到v2.0.0,通过更改v2版本的模块路径来区分两个大版本。比如github.com/sample_golang_module/example/v2,这样v2版本的模块和v1版本的模块就是两个完全不同的模块了,在使用新版模块时在模块名称后面加上v2即可。 module github.com/cnych/stardust/v2 接下来的操作给当前版本添加一个v2.0.0的git tag或者创建一个名为v2的分支,这样可以将版本之间的影响降到最低: git add . git commit -m "change Hello function to support lang" git checkout -b v2 git tag v2.0.0 git push origin v2 --tags v2 版本的模块就发布成功,之前程序也不会有任何的影响,还是继续使用现有的 v1.0.1 版本,而且使用 go get -u 命令也不会拉取最新的 v2.0.0 版本代码。 用户要试用v2.0.0版本的模块,只需要单独引入v2版本的模块即可: package main import ( "fmt" "github.com/sample_golang_module/example/stringsx" stringsV2 "github.com/sample_golang_module/example/v2/stringsx" ) func main() { fmt.Println(stringsx.Hello("cnych")) if greet, err := stringsV2.Hello("cnych", "zh"); err != nil { fmt.Println(err) } else { fmt.Println(greet) } } go run main.go命令会自动拉取v2模块的代码: $ go run main.go go: finding github.com/sample_golang_module/example/v2 v2.0.0 go: downloading github.com/sample_golang_module/example/v2 v1.0.0 Hi, cnych!!! 你好, cnych! 在同一个 go 文件中就使用两个不兼容版本的模块。同样这个时候再次查看下 go.mod 文件的变化: module <模块名>require github.com/sample_golang_module/example v1.0.1require github.com/sample_golang_module/example/v2 v2.0.0 默认情况下,Golang 是不会从 go.mod 文件中删除依赖项的,如果我们有不使用的一些依赖项需要清理,可以使用 tidy 命令: go mod tidy 该命令会清除没有使用的模块,也会更新模块到指定的最新版本。 Vendor Go Modules 默认会忽略 vendor/ 这个目录,但是如果还想将依赖放入 vendor 目录的话,可以执行下面的命令: go mod vendor 该命令会在项目根目录下面创建一个 vendor/ 的文件夹,里面会包含所有的依赖模块代码,并且会在该目录下面添加一个名为 modules.txt 的文件,用来记录依赖包的一些信息,比较类似于 govendor 中的 vendor.json 文件。 不过建议还是不要使用该命令,尽量去忘掉 vendor 的存在。 如何将vendor和modules一起使用 vendor是不是渐行渐远了? vgo博客文章中建议完全放弃vendor,但是社区的反馈意见导致对vendor的支持。 简而言之,在modules中使用vendor: go mod vendor会重置moudules中的vendor目录,并基于go.mod的内容和Go源代码,将build和test需要的所有moudels包都放入这个目录中 默认情况下,开启module模式后,go的命令如go build会忽略vendor目录 标识参数-mod=vendor(如go buld -mod=vendor)告诉go命令使用主模块中顶级目录下的vendor目录来满足依赖。这种模式下,这个go命令会因此而忽略在go.mod中描述的依赖并假设vendor目录中存放着所有依赖的正确拷贝。注意:只有在主模块的顶级目录下的vendor目录会被使用,其他路径下的vendor目录依然会被忽略 一些人可能希望通过设置GOFLAGS=-mod=vendor环境变量,来习惯性的加入vendor Go的老版本(如1.10)知道如何使用通过go mod vendor命令创建vendor目录,在禁用module模块的Go1.11和1.12+版本也是如此。 因此,vendor是module提供依赖的一种方式,对于不能完全理解module模块的老版本和没有启用module模块的项目。 用例 制作依赖副本:go mod vendor -v -v :打印出依赖模块的名字到stderr 镜像仓库 如果有一些依赖包下载不下来的,我们可以使用 GOPROXY 这个参数来设置模块代理,比如: export GOPROXY="https://goproxy.io" 阿里云也提供了 Go Modules 代理仓库服务:http://mirrors.aliyun.com/goproxy/,使用很简单就两步: 使用 go1.11 以上版本并开启 go module 机制:export GO111MODULE=on 导出 GOPROXY 环境变量:export GOPROXY=https://mirrors.aliyun.com/goproxy/ 如果你想上面的配置始终生效,可以将这两条命令添加到. bashrc 中去。 搭建私有仓库 除了使用公有的 Go Modules 代理仓库服务之外,很多时候我们在公司内部需要搭建私有的代理服务,特别是在使用 CI/CD 的时候,如果有一个私有代理仓库服务,会大大的提供应用的构建效率。 可以使用 Athens 来搭建私有的代理仓库服务,搭建非常简单,直接用 docker 镜像运行一个服务即可: export ATHENS_STORAGE=~/athens-storage mkdir -p $ATHENS_STORAGE docker run -d -v $ATHENS_STORAGE:/var/lib/athens \ -e ATHENS_DISK_STORAGE_ROOT=/var/lib/athens \ -e ATHENS_STORAGE_TYPE=disk \ --name goproxy \ --restart always \ -p 3000:3000 \ gomods/athens:latest 其中 ATHENS_STORAGE 是用来存放我们下载下来的模块的本地路径,另外 ATHENS 还支持其他类型的存储,比如 内存, AWS S3 或 Minio,都是 OK 的。 然后修改 GOPROXY 配置: export GOPROXY=http://127.0.0.1:3000 总结 一句话:Go Modules 真的用起来非常爽,特别是消除了 GOPATH,这个东西对于 Golang 初学者来说是非常烦人的,很难理解为什么需要进入到特定目录下面才可以编写 Go 代码,现在不用担心了,直接使用 Go Modules 就行。