Go语言入门——dep入门

  本文出现了大量maven的内容,更适合java程序员阅读,如果你的语言做依赖管理的方案与maven差异很大,可能在有些地方会不理解

  从很久之前go语言在依赖解决和管理方面方案的匮乏就被不少人诟病。光指望go get指令,很多事办不成。我也不清楚从什么时候开始,dep,这个官方的解决方案开始被推广了。从说明上看,不会早于go 1.8,从github的源代码上看,至少开源不会超过1年

  官方对于dep的介绍是“dep is the official experiment, but not yet the official tool.” 但又说 “dep is safe for production use.”。我并没在实践中真实使用,但也做了不少试验,基本可以认定其稳定,且“知名”,未来如果有更好的依赖管理方案,十有八九也是上面加个UI的壳吧

  当然还有一个问题摆在面前:jenkins似乎没有dep的插件?那只能靠手写脚本了

  在往下看之前先确定你清楚两个术语:我们把 github.com/apodemakeles/ugo 这玩意叫project,这是一个github上的仓库,也代表一个项目,把github.com/apodemakeles/ugo/time 这玩意儿叫package,是project下面一个go 语言的包。current project表示当前你正在编写的项目  

  前置知识点

  首先你要清楚,go中获取依赖包都是通过执行go get命令,通过解析代码中的import语句,去下载相应的源代码到$GOPATH/src下,然后再进行install,安装在$GOPATH/pkg下。所以你如果要对应maven中下载的java的jar包的话,那实际上等于go中的源码和一堆.a文件。go get的说明

  但go get的问题是没有版本上的控制,今天你运行的代码,可能和明天在jenkins上生成的代码完全不一样

  另外一个问题也是拜go语言的特性所赐,由于所有的依赖项必须在$GOPATH下有对应的源码和.a文件,所以在同一台机上的不同项目,没法使用同一依赖项的不同版本。难道你要在不同的项目编译时git checkout一下?

  所以在go 1.5开始官方引入了go vendor机制,简单点说就是在原来的project目录下加一个vendor文件夹,源码都“照搬”到这里,目录结构不动。所有import优先使用vendor中的源码。这个口子一开,一时间第三方纷纷推出自己的依赖解决工具了,比如我以前用过的govendor。 go vendor的说明

  dep一瞥

  要使用dep,首先要去github下载dep源码,编译成本地的可执行文件。确保你的$GOPATH/bin在你的PATH中,这样就可以在命令行执行dep命令了。具体详见 github 和 dep官方文档 ,不在此赘述

  随便建立一个项目,当然要在$GOPATH下,此时先不要写代码,至少不要import远程的依赖,执行

dep init

  你能看到project路径下多了三个东西,一个vendor文件夹,这个未来要来放current project的远程依赖的源代码,一个Gopkg.toml文件,你可以暂时将其类比为pom文件,或者npm的package.json文件,还有一个Gopkg.lock文件,这是啥?

  

  既然toml文件类似于pom或者package.json,那按照里面的注释,试着添加一个依赖项试试,比如像我一样

[[constraint]]
  name = "github.com/apodemakeles/ugo"
  version = "=0.1.0"

  看起来意思是需要下载ugo这个project,并且限制版本为0.1.0,下面执行

dep ensure

  这个指令类似于install,compile之类,就是根据依赖的配置内容,下载依赖,编译依赖。指令很快就执行完了,但什么也没发生?这就是我之前说“暂时”类比为pom文件的原因,dep中要结合toml和代码中的import语句,才会真的下载,编译依赖项。把下面的代码贴到项目中去

package main

import (
    "fmt"
    "github.com/apodemakeles/ugo/time"
)

func main() {
    fmt.Println(utime.NowUnixTS())
}

  重新执行dep ensure,你会看到vendor中有了源代码,而那个不知道是什么的lock文件里是这样的:

# This file is autogenerated, do not edit; changes may be undone by the next ‘dep ensure‘.

[[projects]]
  name = "github.com/apodemakeles/ugo"
  packages = ["time"]
  revision = "96e9671d8beda19466b4296a8939ebfe26210683"
  version = "v0.1.0"

[solve-meta]
  analyzer-name = "dep"
  analyzer-version = 1
  inputs-digest = "4b0b8768bb38a412e1bbfd9952fe578e6f5b1a7469e3f44e444d66ca0c7ffaf6"
  solver-name = "gps-cdcl"
  solver-version = 1

  现在再正式解释toml文件和lock文件:

  toml文件记录着current project依赖项project的约束,而并不是应该有哪些project,有哪些project还是要看import了哪些package。这个约束主要体现在到底要采用目标project的某个tag的版本(version),还是某个branch,或者是某个commit sha1(revision),后面我们会称其"type", 这三个对于一个constraint只能选一个。你可以试试去掉toml的内容,执行dep ensure,依旧可以下载文件到vendor,依旧会修改lock文件

  实际上除了constraint,还有其他几个约束, 比较重要的有required,ignored,override,前两个本文不会重点说明,override会在后面重点说明。toml文件的说明

  lock文件是工具生成的,你不应该手工编辑,lock文件的packages对应你import的内容,而revision(一定会有,和type无关)和version则为vendor中源码的真实反映。lock文件的说明

  注意到version那项写的是"v0.1.0"了没有?这是我github上代码真正的tag,在toml文件中的约束忽略了首字母v,直接拉取了tag为v0.1.0的代码

  接下来把toml中的version改为“=0.1.1” 试试,假设你需要更高版本ugo的一个功能,需要升级依赖。执行完dep ensure你会发现vendor中变化了,lock文件也变了

  以上就是dep的简略介绍,我建议大家看一下 dep的运行机制 。里面提到了dep ensure这个命令,可以类比为函数的执行:toml和import就好比一个函数的输入,经过第一个函数resolving,输出的是lock文件,把其当做输入传入到第二个函数vendoring,输出的是vendor文件夹中的内容。这样有助于大家的理解,在本文没涉及到的情况,可以自己推理出来

  模拟maven的一个方案

  以下是我假象的,没经过验证的一个实际开发中的工作流程

  首先,最好确保你的项目使用semver标准——语义化的版本标准,semver说明 ,这里规定了版本号代表的意思,比较,以及一些操作符。还规定最初版本从0.1.0开始(看到此一阵惊喜,我们组蒙对了)

  然后,了解你依赖的代码,需要使用哪个版本。如果是自己团队的类库,遵守semver标准,利用git的tag功能来表示version, 比如打一个v0.1.0的标签

  之后规规矩矩的按照go的要求新建一个项目,并且搞定版本控制。在ignore中写上 vendor/。这里我曾疑问,lock是否也可以排除?实际上可以,但官方文档曾经提到过"commit" lock文件

  在项目根目录执行dep init。生成这三个东西。如果你可以copy一份toml过来,dep init完全不用执行,只要有toml文件,dep ensure完全可以生成其他两个

  之后编码,涉及到远程依赖的内容,先在import中导入,再选择对应版本,在toml中修改,之后执行dep ensure

  如果开发过程中需要升级,修改toml文件,再执行dep ensure

  CI工具拉去到代码后,由于有toml文件,直接执行dep ensure就可以了,解决完依赖最后再执行go build

  这么看起来似乎全局只有这一个命令是必须

  注意,在我建议的方案中,toml文件要写成这样 version=“0.1.0”,不要再0.1.0前加等于号,为什么这样,你先最好了解了解go dep 的version rule

  version rules

  version rules在此,简单说,三位版本号第一位为major,跨major可以不兼容,后两位为minor和patch,必须保证在同一个major范围内向后兼容。说白了我以前用1.2.1版本,现在换成1.2.2或者1.3.1了,一定要没错误,但2.1.0不行(这就是咱们架构组的规则)

  在dep中, =0.1.0代表确定这个版本, ^0.1.0代表 >=0.1.0 且 <1.0.0

[[constraint]]
  name = "github.com/apodemakeles/ugo"
  version = "0.1.0"

  这种写法等价于^0.1.0,默认的一般是推荐的方案,为什么推荐这种呢?你可以理解,如果你按照semver的规范,没跨major的一定向后兼容,所以即使获取到0.1.1, 0.2.0,也不会出错。可为什么不能像maven一样固定一个坐标呢?

 

  传递依赖

  假设A依赖于B的一个功能(A,B是project, jar,或者dll),我们用A->B来表示,如果有A->B->C,则B对C为直接依赖,A对C为传递依赖。如果恰巧A->B->C且A->C呢,但这两个C又不是一个版本,会发生什么?

  在node.js中并没有这种困扰,在依赖文件夹中会有两个C存在,然而对于C#,Java,Go这些语言,他们共同特点是current project下最终一个C只会有一个真实在磁盘上的产物,(dll, jar,带路径的.a),这时候就发生了依赖冲突问题

  在C#的MSBuild中,随便选一个C(我感觉总是选高版本),生成dll,但在运行时,任何一个用到C.dll的dll,会检查当前依赖的版本范围内有没有C的版本,如果没有则在运行时出错

  在Java的Maven中,会采用“最短路径”原则,此时A->C这条路径比较近,采用这条路径的pom中的版本。但这就又引来一个问题,如果这条短路径的C版本比较低,恰好B要用一个更高的C版本,因为里面有一个新方法,在运行时就会出错

  那Go的dep呢?如果你在toml文件中这么写

[[constraint]]
  name = "github.com/apodemakeles/B"
  version = "=xxx"

[[constraint]]
  name = "github.com/apodemakeles/C"
  version = "=0.1.1"

  此时不管B->C的版本比0.1.1高还是低(B->C也写为version="=x.x.x"),dep都不允许,都会报错,理由是“has no overlap”。如果B->C和A->C在一个major中,那就放开吧,直接写 verion="x.x.x"

  这就是我推荐不带等号的原因

  但有时候就是要确定某一个版本怎么办?可以使用toml中另一个约束override

[[constraint]]
  name = "github.com/apodemakeles/B"
  version = "=xxx"

[[override]]
  name = "github.com/apodemakeles/C"
  version = "=0.1.1"

  这样会强制使用0.1.1的版本的C,但有可能出现上面maven同样的情况,需要你自己负责

  

  零零散散的细节

  截止到这里基本我觉得重要的内容就都说完了,toml和lock中还有很多内容没说,比如required,ignored,这些东西自己看就好。还有一些dep ensure的参数,比如-update,-add,我觉得大家不知道更好,可以统一使用规范,就只用dep ensure。而且官方也不建议把branch或者revision作为版本控制

  •   如果你想要一个0.0.1版本的project,而服务器只有0.1.0以上的版本,即使使用范围比如^,~,也获取不到。我猜是因为dep把0.1.0作为最初始的版本
  • dep status可以查看当前依赖信息
  •   dep ensure真挺慢

  

原文地址:https://www.cnblogs.com/anti-archs/p/8431474.html

时间: 2024-10-10 09:28:42

Go语言入门——dep入门的相关文章

R语言快速上手入门

R语言快速上手入门 课程学习网址:http://www.xuetuwuyou.com/course/196 课程出自学途无忧网:http://www.xuetuwuyou.com 课程简介 本教程深入浅出地讲解如何使用R语言玩转数据.课程中涵盖R语言编程的方方面面,内容涉及R对象的类型.R的记号体系和环境系统.自定义函数.if else语句.for循环.S3类R的包系统以及调试工具等.本课程还通过示例演示如何进行向量化编程,从而对代码进行提速并尽可能地发挥R的潜能.本课程适合立志成为数据科学家的

C语言细节——献给入门者(一)

C语言细节——献给入门者(一) 主题  输入输出需要注意的细节 首先我们要知道大致有scanf(),printf(),getchar(),putchar(),gets(),puts()这几种输入方式. 1??.scanf()&gets() 首先看个最普通的例子: char str[20]; scanf(“%s”,str); 当我们输入helloworld,此时str为“helloworld” 但是当我们输入hello world,此时str为“hello” 原因是scanf输入字符串时遇到空格和

[Go语言]一、入门Hello,World

Go简介 为什么有了系统编程级别的C/C++以及后来的Java,也有脚本级别的Ruby/Perl/Python语言可用,Google为什么还要发布Go语言? "我们开发Go,是因为近10年左右开发程序之难让我们有点沮丧" --首席软件工程师Rob Pike Go完全支持coroutine,即协程,也称为轻量级的线程.多数语言在语法层面不直接支持协程.一些语言或许可以通过第三方库来支持协程,但是功能并不完整.比如仅仅提供协程的创建, 销毁和切换能力.如果在这样的协程中调用一个同步IO,如

Swift语言Auto Layout入门教程

Swift语言Auto Layout入门教程:上篇 开始用自动布局约束的方式思考吧! 更新记录:该教程由Brad Johnson更新Swift和iOS 8内容,原文第一版作者为教程编纂组的Matthijs Hollemans. 你可曾为了让App在横竖屏模式下都能展现整洁的界面而感到苦恼?你可曾为了让布局同时支持iPhone和iPad而感到心烦?别灰心,好消息来啦! 为某种确切尺寸的屏幕设计用户界面并不麻烦,但如果屏幕画面的框架不固定,为适应新环境,App中各个UI元素的位置和大小都需要相应调整

如何R语言快速上手入门

R语言快速上手入门 课程学习网址:http://www.xuetuwuyou.com/course/196 课程出自学途无忧网:http://www.xuetuwuyou.com 课程简介 本教程深入浅出地讲解如何使用R语言玩转数据.课程中涵盖R语言编程的方方面面,内容涉及R对象的类型.R的记号体系和环境系统.自定义函数.if else语句.for循环.S3类R的包系统以及调试工具等.本课程还通过示例演示如何进行向量化编程,从而对代码进行提速并尽可能地发挥R的潜能.本课程适合立志成为数据科学家的

Mysql C语言API编程入门讲解

原文:Mysql C语言API编程入门讲解 软件开发中我们经常要访问数据库,存取数据,之前已经有网友提出让鸡啄米讲讲数据库编程的知识,本文就详细讲解如何使用Mysql的C语言API进行数据库编程.  API,全称Application Programming Interfaces,即应用程序编程接口,我们可以调用这些接口,执行API函数提供的功能.  Mysql C语言API就是用C语言编写的Mysql编程接口,使用这些接口函数可以实现对Mysql数据库的查询等操作.  Mysql的安装  要进

Swift语言Auto Layout入门教程:上篇

原文:Beginning Auto Layout Tutorial in Swift: Part 1/2,译者:@TurtleFromMars 开始用自动布局约束的方式思考吧! 更新记录:该教程由Brad Johnson更新Swift和iOS 8内容,原文第一版作者为教程编纂组的Matthijs Hollemans. 你可曾为了让App在横竖屏模式下都能展现整洁的界面而感到苦恼?你可曾为了让布局同时支持iPhone和iPad而感到心烦?别灰心,好消息来啦! 为某种确切尺寸的屏幕设计用户界面并不麻

C语言学习从入门到精通书籍,10万读者都认可

C语言程序设计从入门到精通 10万读者认可的编程图书精粹 零基础自学编程的入门图书 详解C语言编程思想和核心技术 很多初学者,对C语言.c++的概念都是模糊不清的,C语言.c++是什么,能做什么,学的时候该按照什么线路去学习,学完往哪方面发展,想深入了解,详情可以点击有道云笔记链接了解:http://note.youdao.com/noteshare?id=bd7b6584fb92a7af9851901d7af4dd77 原文地址:https://www.cnblogs.com/ITbianch

Android视频录制从不入门到入门系列教程(三)————视频方向

运行Android视频录制从不入门到入门系列教程(二)————显示视频图像中的Demo后,我们应该能发现视频的方向是错误的. 由于Android中,Camera给我们的视频图片的原始方向是下图这个样子的: 就是说,即使你是竖着拿手机的,Camera提供给你的视频图像的方向还是上图那样横着的图片. 我们可以通过下述方向改变Camera提供的视频图像的方法: camera.setDisplayOrientation(90); 让图像顺时针旋转90度,视频图像的方向就正常的. 本篇文章DEMO下载.