「go get」指令可以藉助程式碼管理工具透過遠端拉取或更新程式碼包及其依賴套件,並自動完成編譯和安裝。 「go get」指令可以動態取得遠端程式碼包,在使用「go get」指令前,需要安裝與遠端套件相符的程式碼管理工具,如Git、SVN、HG等,參數中需要提供一個套件名稱。
本教學操作環境:windows7系統、GO 1.18版本、Dell G3電腦。
go get 指令可以藉助程式碼管理工具透過遠端拉取或更新程式碼包及其依賴套件,並自動完成編譯和安裝。整個過程就像安裝一個 App 一樣簡單。
這個指令可以動態取得遠端程式碼包,目前支援的有 BitBucket、GitHub、Google Code 和 Launchpad。在使用 go get 指令前,需要安裝與遠端套件相符的程式碼管理工具,如 Git、SVN、HG 等,參數中需要提供一個套件名稱。
這個指令在內部實際上分成了兩步驟操作:第一步是下載原始碼包,第二步是執行 go install。
go get指令-一鍵取得程式碼、編譯並安裝
hc@ubt:~$ go get github.com/hyper-carrot/go_lib/logging
指令go get
可以依照要求和實際情況從互聯網上下載或更新指定的程式碼包及其依賴包,並對它們進行編譯和安裝。在上面這個範例中,我們從著名的程式碼託管網站Github上下載了一個專案(或稱為程式碼包),並安裝到了環境變數GOPATH中包含的第一個工作區中。同時,我們也知道了這個程式碼包的導入路徑就是github.com/hyper-carrot/go_lib/logging。
一般情況下,為了分離自己與第三方的程式碼,我們會設定兩個或更多的工作區。我們現在有一個目錄路徑為/home/hc/golang/lib的工作區,並且它是環境變數GOPATH值中的第一個目錄路徑。注意,環境變數GOPATH中包含的路徑不能與環境變數GOROOT的值重複。好了,如果我們使用go get
指令下載和安裝程式碼包,那麼這些程式碼包都會被安裝在上面這個工作區。我們暫且把這個工作區叫做Lib工作區。在我們執行go get github.com/hyper-carrot/go_lib/logging
之後,這個程式碼包就應該會被保存在Lib工作的src目錄下,並且已經被安裝妥當,如下所示:
/home/hc/golang/lib: bin/ pkg/ linux_386/ github.com/ hyper-carrot/ go_lib/ logging.a src/ github.com/ hyper-carrot/ go_lib/ logging/ ...
另一方面,如果我們想把一個專案上傳到Github網站(或其他程式碼託管網站)上並被其他人使用的話,那麼我們就應該把這個專案當作一個程式碼包來看待。其實我們在之前已經提到過原因,go get
指令會將專案下的所有子目錄和原始碼檔案存放到第一個工作區的src目錄下,而src目錄下的所有子目錄都會是某個程式碼包導入路徑的一部分或全部。也就是說,我們應該直接在專案目錄下存放子代碼包和源碼文件,並且直接存放在專案目錄下的源碼文件所聲明的包名應該與該項目名相同(除非它是命令源碼文件)。這樣做可以讓其他人使用go get
命令從Github網站下載你的專案之後直接就能使用它。
實際上,像goc2p專案這樣直接以專案根目錄的路徑作為工作區路徑的做法是不被推薦的。之所以這樣做主要是想讓讀者更容易的理解Go語言的工程結構和工作區概念,也可以讓讀者看到另一個專案結構。當然,如果你的專案使用了gb這樣的工具那就是另外一回事了。這樣的專案的根目錄就應該被視為一個工作區(但你不必把它加入到GOPATH環境變數中)。它應該由git clone
下載到Go語言工作區之外的某處,而不是使用go get
命令。
遠端導入路徑分析
實際上,go get
指令所做的動作也被稱為程式碼包遠端匯入,而傳遞給該命令的作為代碼包導入路徑的那個參數又被叫做代碼包遠端導入路徑。
go get
指令不僅可以從像Github這樣著名的程式碼託管網站下載程式碼包,還可以從任何指令支援的程式碼版本控制系統(英文為Version Control System,簡稱為VCS)檢出代碼包。任何程式碼託管網站都是透過某個或某些程式碼版本控制系統來提供程式碼上傳下載服務的。所以,更嚴格地講,go get
命令所做的是從程式碼版本控制系統的遠端倉庫中檢出/更新程式碼包並對其進行編譯和安裝。
該指令所支援的VCS的資訊如下表:
表0-2 go get
指令支援的VCS
名稱 | 主指令 | 說明 |
---|---|---|
Mercurial | hg | Mercurial是一種輕量級分散式版本控制系統,採用Python語言實現,易於學習和使用,擴展性強。 |
Git | git | Git最開始是Linux Torvalds為了幫助管理 Linux 核心開發而開發的一個開源的分散式版本控制軟體。但現在已被廣泛使用。它是用來進行有效、高速的各種規模專案的版本管理。 |
Subversion | svn | Subversion是一個版本控制系統,也是第一個將分支概念和功能納入版本控制模型的系統。但相對於Git和Mercurial而言,它只算是傳統版本控制系統的一員。 |
Bazaar | bzr | Bazaar是一個開源的分散式版本控制系統。但相比而言,用它來作為VCS的專案並不多。 |
go get
指令在檢出程式碼包之前必須知道程式碼包遠端導入路徑所對應的版本控制系統和遠端倉庫的URL。
如果程式碼套件在本機工作區中已經存在,則會直接透過分析其路徑來確定這幾項資訊。 go get
指令支援的幾個版本控制系統都有一個共同點,那就是會在檢出的專案目錄中存放一個元資料目錄,名稱為「.」前綴加其主命令名。例如,Git會在檢出的專案目錄中加入一個名為「.git」的子目錄。所以,這樣就很容易判定程式碼包所用的版本控制系統。另外,又由於代碼包已經存在,我們只需透過代碼版本控制系統的更新指令來更新代碼包,因此也就不需要知道其遠端倉庫的URL了。對於已存在於本機工作區的程式碼包,除非要求強行更新程式碼包,否則go get
指令不會進行重複下載。如果想要強行更新程式碼包,可以在執行go get
指令時加入-u
標記。這項標記會稍後介紹。
如果本地工作區中不存在該程式碼包,那麼就只能透過對程式碼包遠端匯入路徑進行分析來取得相關資訊了。首先,go get
指令會對程式碼包遠端導入路徑進行靜態分析。為了讓分析過程更加方便快捷,go get
命令程式中已經預置了幾個著名程式碼託管網站的資訊。如下表:
表0-3 預置的程式碼託管網站的資訊
名稱 | 主網域名稱 | 支援的VCS | 程式碼套件遠端匯入路徑範例 |
---|---|---|---|
Bitbucket | bitbucket.org | Git, Mercurial | bitbucket.org/user/project bitbucket.org/user/project/sub/directory |
#GitHub | github.com | Git | github.com/user/project github.com/user/project/sub/directory |
#Google Code Project Hosting | code.google.com | Git, Mercurial, Subversion | code.google.com/p/project code. google.com/p/project/sub/directory code.google.com/p/project.subrepository code.google.com/p/project.subrepository/sub/directory |
#Launchpad | launchpad.net | Bazaar | launchpad.net/project launchpad.net/project/series launchpad.net/ project/series/sub/directory launchpad.net/user/project/branch launchpad.net/user/project/branch/sub/directory |
#IBM DevOps Services | hub.jazz.net | Git | hub.jazz.net/git/user/project hub.jazz.net/ git/user/project/sub/directory |
一般情况下,代码包远程导入路径中的第一个元素就是代码托管网站的主域名。在静态分析的时候,go get
命令会将代码包远程导入路径与预置的代码托管站点的主域名进行匹配。如果匹配成功,则在对代码包远程导入路径的初步检查后返回正常的返回值或错误信息。如果匹配不成功,则会再对代码包远程导入路径进行动态分析。至于动态分析的过程,我就不在这里详细展开了。
如果对代码包远程导入路径的静态分析或/和动态分析成功并获取到对应的版本控制系统和远程仓库URL,那么go get
命令就会进行代码包检出或更新的操作。随后,go get
命令会在必要时以同样的方式检出或更新这个代码包的所有依赖包。
自定义代码包远程导入路径
如果你想把你编写的(被托管在不同的代码托管网站上的)代码包的远程导入路径统一起来,或者不希望让你的代码包中夹杂某个代码托管网站的域名,那么你可以选择自定义你的代码包远程导入路径。这种自定义的实现手段叫做“导入注释”。导入注释的写法示例如下:
package analyzer // import "hypermind.cn/talon/analyzer"
代码包analyzer
实际上属于我的一个网络爬虫项目。这个项目的代码被托管在了Github网站上。它的网址是:https://github.com/hyper-carrot/talon。如果用标准的导入路径来下载analyzer
代码包的话,命令应该这样写go get github.com/hyper-carrot/talon/analyzer
。不过,如果我们像上面的示例那样在该代码包中的一个源码文件中加入导入注释的话,这样下载它就行不通了。我们来看一看这个导入注释。
导入注释的写法如同一条代码包导入语句。不同的是,它出现在了单行注释符//
的右边,因此Go语言编译器会忽略掉它。另外,它必须出现在源码文件的第一行语句(也就是代码包声明语句)的右边。只有符合上述这两个位置条件的导入注释才是有效的。再来看其中的引号部分。被双引号包裹的应该是一个符合导入路径语法规则的字符串。其中,hypermind.cn
是我自己的一个域名。实际上,这也是用来替换掉我想隐去的代码托管网站域名及部分路径(这里是github.com/hyper-carrot
)的那部分。在hypermind.cn
右边的依次是我的项目的名称以及要下载的那个代码包的相对路径。这些与其标准导入路径中的内容都是一致的。为了清晰起见,我们再来做下对比。
github.com/hyper-carrot/talon/analyzer // 标准的导入路径 hypermind.cn /talon/analyzer // 导入注释中的导入路径
你想用你自己的域名替换掉标准导入路径中的哪部分由你自己说了算。不过一般情况下,被替换的部分包括代码托管网站的域名以及你在那里的用户ID就可以了。这足以达到我们最开始说的那两个目的。
虽然我们在talon项目中的所有代码包中都加入了类似的导入注释,但是我们依然无法通过go get hypermind.cn/talon/analyzer
命令来下载这个代码包。因为域名hypermind.cn
所指向的网站并没有加入相应的处理逻辑。具体的实现步骤应该是这样的:
编写一个可处理HTTP请求的程序。这里无所谓用什么编程语言去实现。当然,我推荐你用Go语言去做。
将这个处理程序与hypermind.cn/talon
这个路径关联在一起,并总是在作为响应的HTML文档的头中写入下面这行内容:
<meta name="go-import" content="hypermind.cn/talon git https://github.com/hyper-carrot/talon">
hypermind.cn/talon/analyzer熟悉HTML的读者都应该知道,这行内容会被视为HTML文档的元数据。它实际上go get
命令的文档中要求的写法。它的模式是这样的:
<meta name="go-import" content="import-prefix vcs repo-root">
实际上,content
属性中的import-prefix
的位置上应该填入我们自定义的远程代码包导入路径的前缀。这个前缀应该与我们的处理程序关联的那个路径相一致。而vcs
显然应该代表与版本控制系统有关的标识。还记得表0-2中的主命令列吗?这里的填入内容就应该该列中的某一项。在这里,由于talon项目使用的是Git,所以这里应该填入git
。至于repo-root
,它应该是与该处理程序关联的路径对应的Github网站的URL。在这里,这个路径是hypermind.cn/talon
,那么这个URL就应该是https://github.com/hyper-carrot/talon
。后者也是talon项目的实际网址。
好了,在我们做好上述处理程序之后,go get hypermind.cn/talon/analyzer
命令的执行结果就会是正确的。analyzer
代码包及其依赖包中的代码会被下载到GOPATH环境变量中的第一个工作区目录的src子目录中,然后被编译并安装。
注意,具体的代码包源码存放路径会是/home/hc/golang/lib/src/hypermind.cn/talon/analyzer。也就是说,存放路径(包括代码包源码文件以及相应的归档文件的存放路径)会遵循导入注释中的路径(这里是hypermind.cn/talon/analyzer
),而不是原始的导入路径(这里是github.com/hyper-carrot/talon/analyzer
)。另外,我们只需在talon项目的每个代码包中的某一个源码文件中加入导入注释,但这些导入注释中的路径都必须是一致的。在这之后,我们就只能使用hypermind.cn/talon/
作为talon项目中的代码包的导入路径前缀了。一个反例如下:
hc@ubt:~$ go get github.com/hyper-carrot/talon/analyzer package github.com/hyper-carrot/talon/analyzer: code in directory /home/hc/golang/lib/src/github.com/hyper-carrot/talon/analyzer expects import "hypermind.cn/talon/analyzer"
与自定义的代码包远程导入路径有关的内容我们就介绍到这里。从中我们也可以看出,Go语言为了让使用者的项目与代码托管网站隔离所作出的努力。只要你有自己的网站和一个不错的域名,这就很容易搞定并且非常值得。这会在你的代码包的使用者面前强化你的品牌,而不是某个代码托管网站的。当然,使你的代码包导入路径整齐划一是最直接的好处。
OK,言归正传,我下面继续关注go get
这个命令本身。
命令特有标记
go get
命令可以接受所有可用于go build
命令和go install
命令的标记。这是因为go get
命令的内部步骤中完全包含了编译和安装这两个动作。另外,go get
命令还有一些特有的标记,如下表所示:
表0-4 go get
命令的特有标记说明
标记名称 | 标记描述 |
---|---|
-d | 让命令程序只执行下载动作,而不执行安装动作。 |
-f | 仅在使用-u 标记时才有效。该标记会让命令程序忽略掉对已下载代码包的导入路径的检查。如果下载并安装的代码包所属的项目是你从别人那里Fork过来的,那么这样做就尤为重要了。 |
-fix | 让命令程序在下载代码包后先执行修正动作,而后再进行编译和安装。 |
-insecure | 允许命令程序使用非安全的scheme(如HTTP)去下载指定的代码包。如果你用的代码仓库(如公司内部的Gitlab)没有HTTPS支持,可以添加此标记。请在确定安全的情况下使用它。 |
-t | 让命令程序同时下载并安装指定的代码包中的测试源码文件中依赖的代码包。 |
-u | 让命令利用网络来更新已有代码包及其依赖包。默认情况下,该命令只会从网络上下载本地不存在的代码包,而不会更新已有的代码包。 |
为了更好的理解这几个特有标记,我们先清除Lib工作区的src目录和pkg目录中的所有子目录和文件。现在我们使用带有-d
标记的go get
命令来下载同样的代码包:
hc@ubt:~$ go get -d github.com/hyper-carrot/go_lib/logging
现在,让我们再来看一下Lib工作区的目录结构:
/home/hc/golang/lib: bin/ pkg/ src/ github.com/ hyper-carrot/ go_lib/ logging/ ...
我们可以看到,go get
命令只将代码包下载到了Lib工作区的src目录,而没有进行后续的编译和安装动作。这个加入-d
标记的结果。
再来看-fix
标记。我们知道,绝大多数计算机编程语言在进行升级和演进过程中,不可能保证100%的向后兼容(Backward Compatibility)。在计算机世界中,向后兼容是指在一个程序或者代码库在更新到较新的版本后,用旧的版本程序创建的软件和系统仍能被正常操作或使用,或在旧版本的代码库的基础上编写的程序仍能正常编译运行的能力。Go语言的开发者们已想到了这点,并提供了官方的代码升级工具——fix
。fix
工具可以修复因Go语言规范变更而造成的语法级别的错误。关于fix
工具,我们将放在本节的稍后位置予以说明。
假设我们本机安装的Go语言版本是1.5,但我们的程序需要用到一个很早之前用Go语言的0.9版本开发的代码包。那么我们在使用go get
命令的时候可以加入-fix
标记。这个标记的作用是在检出代码包之后,先对该代码包中不符合Go语言1.5版本的语言规范的语法进行修正,然后再下载它的依赖包,最后再对它们进行编译和安装。
标记-u
的意图和执行的动作都比较简单。我们在执行go get
命令时加入-u
标记就意味着,如果在本地工作区中已存在相关的代码包,那么就是用对应的代码版本控制系统的更新命令更新它,并进行编译和安装。这相当于强行更新指定的代码包及其依赖包。我们来看如下示例:
hc@ubt:~$ go get -v github.com/hyper-carrot/go_lib/logging
因为我们在之前已经检出并安装了这个代码包,所以我们执行上面这条命令后什么也没发生。还记得加入标记-v
标记意味着会打印出被构建的代码包的名字吗?现在我们使用标记-u
来强行更新代码包:
hc@ubt:~$ go get -v -u github.com/hyper-carrot/go_lib/logging github.com/hyper-carrot/go_lib (download)
其中,“(download)”后缀意味着命令从远程仓库检出或更新了该行显示的代码包。如果我们要查看附带-u
的go get
命令到底做了些什么,还可以加上一个-x
标记,以打印出用到的命令。读者可以自己试用一下它。
智能的下载
命令go get
还有一个很值得称道的功能。在使用它检出或更新代码包之后,它会寻找与本地已安装Go语言的版本号相对应的标签(tag)或分支(branch)。比如,本机安装Go语言的版本是1.x,那么go get
命令会在该代码包的远程仓库中寻找名为“go1”的标签或者分支。如果找到指定的标签或者分支,则将本地代码包的版本切换到此标签或者分支。如果没有找到指定的标签或者分支,则将本地代码包的版本切换到主干的最新版本。
前面我们说在执行go get
命令时也可以加入-x
标记,这样可以看到go get
命令执行过程中所使用的所有命令。不知道读者是否已经自己尝试了。下面我们还是以代码包github.com/hyper-carrot/go_lib
为例,并且通过之前示例中的命令的执行此代码包已经被检出到本地。这时我们再次更新这个代码包:
hc@ubt:~$ go get -v -u -x github.com/hyper-carrot/go_lib github.com/hyper-carrot/go_lib (download) cd /home/hc/golang/lib/src/github.com/hyper-carrot/go_lib git fetch cd /home/hc/golang/lib/src/github.com/hyper-carrot/go_lib git show-refcd /home/hc/golang/lib/src/github.com/hyper-carrot/go_lib git checkout origin/masterWORK=/tmp/go-build034263530
在上述示例中,go get
命令通过git fetch
命令将所有远程分支更新到本地,而后有用git show-ref
命令列出本地和远程仓库中记录的代码包的所有分支和标签。最后,当确定没有名为“go1”的标签或者分支后,go get
命令使用git checkout origin/master
命令将代码包的版本切换到主干的最新版本。下面,我们在本地增加一个名为“go1”的标签,看看go get
命令的执行过程又会发生什么改变:
hc@ubt:~$ cd ~/golang/lib/src/github.com/hyper-carrot/go_lib hc@ubt:~/golang/lib/src/github.com/hyper-carrot/go_lib$ git tag go1 hc@ubt:~$ go get -v -u -x github.com/hyper-carrot/go_lib github.com/hyper-carrot/go_lib (download) cd /home/hc/golang/lib/src/github.com/hyper-carrot/go_lib git fetch cd /home/hc/golang/lib/src/github.com/hyper-carrot/go_lib git show-ref cd /home/hc/golang/lib/src/github.com/hyper-carrot/go_lib git show-ref tags/go1 origin/go1 cd /home/hc/golang/lib/src/github.com/hyper-carrot/go_lib git checkout tags/go1 WORK=/tmp/go-build636338114
将这两个示例进行对比,我们会很容易发现它们之间的区别。第二个示例的命令执行过程中使用git show-ref
查看所有分支和标签,当发现有匹配的信息又通过git show-ref tags/go1 origin/go1
命令进行精确查找,在确认无误后将本地代码包的版本切换到标签“go1”之上。
指令go get
的這項功能是非常有用的。我們的程式碼在直接或間接依賴某些同時針對多個Go語言版本開發的程式碼套件時,可以自動的檢出其正確的版本。也可以說,go get
指令內建了一定的程式碼包多版本依賴管理的功能。
到這裡,我向大家介紹了go get
指令的使用方式。 go get
指令與先前介紹的兩個指令一樣,是我們在編寫Go語言程式、建構Go語言專案時不可或缺的輔助工具。
以上是go get指令有什麼用的詳細內容。更多資訊請關注PHP中文網其他相關文章!