go module
前言
go 1.5 引进了vendor管理工程依赖包,但是vendor的存放路径是在GOPATH底下,另外每个依赖还可以有自己的vendor,通常会弄得很乱,尽管dep管理工具可以将vendor平级化管理,但是相对GOPATH的路径是逃不掉的。另外,各个包的版本管理也显得原始,甚至有的开发将依赖包从github直接download下来自己放到GOPATH底下的vendor。go的依赖包管理一致是开发者诟病的一个痛点。所以在千呼万唤中,go 1.11 终于引进了go module管理工程的包依赖,去除了项目包管理对GOPATH的依赖,明确了依赖包的版本管理。
定义
一个module是go相关包版本信息的收集单元。记录了精准的必须依赖信息和重新编译依赖。
从示例开始
go module的使用其实十分容易上手,下面我会以一个例子来说明。
示例的go环境信息:
$ go version
go version go1.12.4 darwin/amd64
下面这个例子是依赖github.com/sirupsen/logrus 输出一行日志。在GOPATH外创建一个mytest的目录,然后创建一个main.go的文件,内容如下:
- package main
- import (
- log "github.com/sirupsen/logrus"
- )
- func main() {
- // Add this line for logging filename and line number!
- log.SetReportCaller(true)
- log.Println("hello world")
- }
执行
- go mod init mytest
其实mytest是我指定的module名称,可以是任意的命名,但是一定要指定,否则会报错 go: cannot determine module path for source directory。
然后执行go build就会成功编译,并且多了go.mod和go.sum两个module相关的文件:
- $ ls
- go.mod go.sum main.go mytest
- $ cat go.mod
- module mytest
- go 1.12
- require github.com/sirupsen/logrus v1.4.2
- $ cat go.sum
- github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
- github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
- github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/=
- github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
- github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
- github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
- github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
- golang.org/x/sys v0.0.0--953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
- golang.org/x/sys v0.0.0--953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
从示例中可以看出go.mod文件存放的是工程包依赖信息,而go.sum里面存放的是依赖包的校验信息。主要关注go.mod的信息。可以看到,如果我们不指定依赖包的版本信息,go build默认是会替我们去拉取该依赖包的最新版本。
所以可以总结,go module的使用分为以下几步:
- go mod init $moduleName 初始化module信息。
- go build或者go test等标准命令自动更新工程的依赖包信息。
- 如果有需要可以使用go get $packageName@$version,例如go get foo@v1.2.3, go get foo@master, go get foo@e3702bed2,也可以直接修改go.mod或者使用go mod edit(文章后面会讲到)获取特定的依赖包版本。
以上就是基本的go module工作流程,已经可以满足日常的工作流程要求,下面会详细的讲解go module的其他用法。
详细用法
那么go module一共有多少种玩法呢?直接运行go mod就会有答案:
- $ go mod
- Go mod provides access to operations on modules.
- Note that support for modules is built into all the go commands,
- not just 'go mod'. For example, day-to-day adding, removing, upgrading,
- and downgrading of dependencies should be done using 'go get'.
- See 'go help modules' for an overview of module functionality.
- Usage:
- go mod <command> [arguments]
- The commands are:
- download download modules to local cache
- edit edit go.mod from tools or scripts
- graph print module requirement graph
- init initialize new module in current directory
- tidy add missing and remove unused modules
- vendor make vendored copy of dependencies
- verify verify dependencies have expected content
- why explain why packages or modules are needed
- Use "go help mod <command>" for more information about a command.
其中init前面我已经讲过了,这里就不再重复。
download
下载依赖包到缓存目录。
edit
提供命令版本编辑go.mod的功能,例如go mod edit -fmt go.mod 会格式化go.mod。
用法 go mod edit [flag] [go.mod]
其中flag选项有:
- -fmt 格式化go.mod文件
- -require=$package:@version添加依赖,会覆盖已存在的相同依赖。添加依赖更推荐使用go get,因为go get会更新相关的go.mod文件,而edit只会更新你指定的go.mod文件。
- -droprequire=$package:@version 移除依赖
- -replace=$oldPackage=$newPackage 更新已经存在的依赖。通常用于私有仓库代码覆盖共有仓库。
这里我重点说下-replace 选项,因为在生产中经常遇到的一种情况是由于这样那样的原因我们需要fork一个私有仓库去改动第三方开源库,例如有个小哥针对logrus做了二次开发github.com/gogap/logrus,这个时候就需要用github.com/gogap/logrus替换之前的第三方开源库github.com/sirupsen/logrus,操作如下:
- $ go mod edit -replace="github.com/sirupsen/logrus=github.com/gogap/logrus@v0.8.2"
- $ go build
- go: finding github.com/gogap/logrus v0.8.2
- go: downloading github.com/gogap/logrus v0.8.2
- go: extracting github.com/gogap/logrus v0.8.2
- $ cat go.mod
- module mytest
- go 1.12
- require github.com/sirupsen/logrus v1.4.2
- replace github.com/sirupsen/logrus => github.com/gogap/logrus v0.8.2
graph
显示依赖关系(图)。
- $ go mod graph
- mytest github.com/sirupsen/logrus@v1.4.2
- github.com/sirupsen/logrus@v1.4.2 github.com/davecgh/go-spew@v1.1.1
- github.com/sirupsen/logrus@v1.4.2 github.com/konsorten/go-windows-terminal-sequences@v1.0.1
- github.com/sirupsen/logrus@v1.4.2 github.com/pmezard/go-difflib@v1.0.0
- github.com/sirupsen/logrus@v1.4.2 github.com/stretchr/objx@v0.1.1
- github.com/sirupsen/logrus@v1.4.2 github.com/stretchr/testify@v1.2.2
- github.com/sirupsen/logrus@v1.4.2 golang.org/x/sys@v0.0.0--953cdadca894
tidy
增加缺失的包并且移除没有依赖的包。自动去下载依赖包,并且缓存到$GOPATH/pkg/mod目录下。
需要注意的是,tidy会自动更新依赖包的版本,所以如果不是初建的项目还是尽量少用tidy,尽量用go get精准控制新增的依赖包。
vendor
把依赖包拷贝到vendor目录底下。前面说了那么多想必你一定有一个疑问:go build的时候需要现场去拉取依赖包,如果我的编译机没有外网(访问不了github)怎么办?vendor就是为了应用这种情况,在本地开发机(有外网)执行 go mod vendor 将依赖包拷贝到vendor底下,然后将代码push到编译机 执行 go build -mod=vendor。示例:
- $ go mod vendor
- $ ls
- go.mod go.sum main.go mytest vendor
- $ go build -mod=vendor
verify
校验依赖关系
- $ go mod verify
- all modules verified
why
指出为什么需要依赖包。与graph的区别是,why只能解释某一个特定的依赖包,而graph则是给出完整的依赖关系图。
- $ go mod why github.com/konsorten/go-windows-terminal-sequences
- # github.com/konsorten/go-windows-terminal-sequences
- mytest
- github.com/sirupsen/logrus
- github.com/konsorten/go-windows-terminal-sequences
同工程下的依赖管理
例如建立一个webserver的工程,目录为/Users/saas/src/awesomeProject/webserver,GOPATH设置为/Users/saas, webserver下的目录结构为:
- $ tree
- .
- ├── go.mod
- │ └── google.go
- ├── helloworld
- ├── server.go
- └── userip
- └── userip.go
- directories, files
其中google目录为packge google,userip目录为package userip,那么我们要在server.go如何引用google和userip这两个包呢?只需:
- import (
- "helloworld/google"
- "helloworld/userip"
- )
helloworld 是go mod init helloworld时指定的module名称,所以helloworld索引到了webserver目录,helloworld/google指的是webserver底下的google包。如果不指定module的名称,默认是GOPATH下的路径,即为awesomeProject/webserver,引用google包时就需要指定awesomeProject/webserver/google。如果GOPATH没有指定,又没有指定module的名字则报错:
- $ export GOPATH=""
- $ go mod init
- go: cannot determine module path for source directory /Users/saas/src/awesomeProject/webserver (outside GOPATH, no import comments)
指定module就可以了,即便没有GOPATH:
- $ go mod init helloworld
- go: creating new go.mod: module helloworld
go build时默认会用module的名字(base name)给程序名称,这里是helloworld。如果module名称为 awesomeProject/webserver则是webserver。
Goland IDE打开Go Module
上面例子中发现在Goland IDE中helloworld/google会被标红,说找不到helloworld这个目录,说明IDE的Go Module功能还没有打开,需要如下设置:
总结
文章通过一个打印日志的例子演示了所有go module的用法,其中包括日常基本用法和全面的用法介绍。新增依赖包的更新推荐使用go get。依赖包的替换推荐使用go mod edit -replace。在编译机网络有限制的时候提供了vendor的解决方案。
参考
https://github.com/golang/go/wiki/Modules
go module的更多相关文章
- Android Studio 编译单个module
前期自己要把gradle环境变量配置好 在Terminal中gradle命令行编译apk 输入gradle assembleRelease 会编译全部module编译单个modulecd ./xiru ...
- ABP源码分析三:ABP Module
Abp是一种基于模块化设计的思想构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modu ...
- nodejs模块中exports和module.exports的区别
通过Node.js的官方API可以看到Node.js本身提供了很多核心模块 http://nodejs.org/api/ ,这些核心模块被编译成二进制文件,可以require('模块名')去获取:核心 ...
- ES6之module
该博客原文地址:http://www.cnblogs.com/giggle/p/5572118.html 一.module概述 JavaScript一直没有模块体系,但是伴随着ES6的到来,modul ...
- [python] CSV read and write using module xlrd and xlwt
1. get data from csv, skip header of the file. with open('test_data.csv','rb,) as csvfile: readCSV = ...
- Yii2.0.7 限制user module登录遇到的问题
在Yii2.0.6的时候我是在以下文件通过以下方法实现的. frontend/modules/user/Module.php namespace frontend\modules\user; clas ...
- Android Studio导入github下载的project和module
前言:我们以前eclispe时代, 经常都是跑到github浏览第三方开源资源,然后下载下来,运行一下sample之类的,学习没有接触的第三方安卓库,但是到了Android Studio,在githu ...
- Android Studio导入Project、Module的正确方法
Gradle Project项目.Module模块导入 最近看到网上很多人在抱怨,Android Studio很难导入github上下载下来的一些项目,主要包括: 1.导入就在下载Gradle2.根本 ...
- ImportError: No module named 'requests'
补充说明: 当前环境是在windows环境下 python版本是:python 3.4. 刚开始学习python,一边看书一边论坛里阅读感兴趣的代码, http://www.oschina.net/c ...
- android studio 中移除module和恢复module
一.移除Android Studio中module 在Android Studio中想要删除某个module时,在Android Studio中选中module,右键发现没有delete,如图: An ...
随机推荐
- 【剑指offer】数组中重复的数字
题目描述 在一个长度为n的数组里的所有数字都在0到n-1的范围内. 数组中某些数字是重复的,但不知道有几个数字是重复的.也不知道每个数字重复几次.请找出数组中任意一个重复的数字. 例如,如果输入长度为 ...
- HDU 5135.Little Zu Chongzhi's Triangles-字符串 (2014ACM/ICPC亚洲区广州站-重现赛)
Little Zu Chongzhi's Triangles Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 512000/512000 ...
- validate插件使用
validate插件使用 官网:http://jqueryvalidation.org/ 项目实操 引入文件 add.html调用(注意顺序问题) 为form表单定义一个ID,以方便获取该元素 添加验 ...
- 洛谷——P1186 玛丽卡
P1186 玛丽卡 题目描述 麦克找了个新女朋友,玛丽卡对他非常恼火并伺机报复. 因为她和他们不住在同一个城市,因此她开始准备她的长途旅行. 在这个国家中每两个城市之间最多只有一条路相通,并且我们知道 ...
- 基于VUE开发项目
前言 最近由于公司需要,需要写一个相对来说比较大型的后台管理系统.为了保证管理系统操作体验较为舒适并且项目后期益于维护,最后决定基于VUE全家桶来开发一个高度组件化的单页SPA应用. 技术选型 vue ...
- Xamarin XAML语言教程Xamarin.Forms中程序状态与进度(一)
Xamarin XAML语言教程Xamarin.Forms中程序状态与进度(一) 在Xamarin.Forms中,提供了两个控件用来指示程序的状态和进度.他们分别为活动指示器和进度条.其中,活动指示器 ...
- Linux网络协议栈之数据包处理过程
http://blog.csdn.net/cheng_fangang/article/details/8966242
- Python将JSON格式数据转换为SQL语句以便导入MySQL数据库
前文中我们把网络爬虫爬取的数据保存为JSON格式,但为了能够更方便地处理数据.我们希望把这些数据导入到MySQL数据库中.phpMyadmin能够把MySQL数据库中的数据导出为JSON格式文件,但却 ...
- vs:如何添加.dll文件
Newtonsoft.Json.dll 一个第三方的json序列化和反序列化dll,转换成json供页面使用,页面json数据转换成对象,供.net使用 Mysql.Data.dll mysql数 ...
- 获取URL的name值 getUrl(url,name) 传入url和key 得到key对应的value
<body> <script type="text/javascript"> var url = "http://192.168.1.82:802 ...