golang的 GOPATH和vendor的搜索关系

CodingCode · · 4511 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

golang的 GOPATH和vendor的搜索关系

基本规则

    1. 所有的go文件都是必须组织成包的形式,放在相应文件夹下:
    • 1.1 建议包名和文件夹名字相同;虽然也可以不同,但会引发使用误解。
    • 1.2 对于主程序包,也需要放在文件夹下面,注意:
      • 1.2.1 不建议使用main作为文件夹名,虽然这个包名是main。
      • 1.2.2 也不建议使用src作为文件名,尽管这是允许的,但是会引发误解。
      • 1.2.3 建议使用项目名字作为包名。
    1. go build命令如果不带参数,就是build当前包,当前目录所在的包,即当前目录下面的所有go文件。
    • 1.2 如果go build指定了目标包,那么就会从GOPATH路径下面搜索包,如果找不到,就报失败;哪怕当前路径就在目标包里,但是GOPATH没有包含,也会报失败。
    • 1.2 如果GOPATH没有设置,其缺省路径就是$HOME/gp

例子1:完全自包含项目

项目只有一个包,即main包,没有引用其他的包(golang自带的系统包除外)。

  • 1.新建文件夹,例如myproject。
    mkdir myproject
    1. 编辑项目文件
[~/myproject]$ cat main.go 
package main

import "fmt"

func main() {
    fmt.Printf("main::main\n");
    foo()
}

[~/myproject]$ cat foo.go 
package main

import "fmt"

func foo() {
    fmt.Printf("main::foo\n");
}
    1. 编译项目
[~/myproject]$ unset GOPATH
[~/myproject]$ go build
[~/myproject]$ ls -1
foo.go
main.go
myproject
  1. 直接进入项目目录运行 go build,即编译当前包。
  2. 不需要设置GOPATH值,缺省就是~/go,因为这是一个自包含项目,不需要引用GOPATH的任何值。
  3. 编译生成的可执行文件名就是项目文件夹名。
  4. 注意当前目录必须是项目文件所在目录,因为go build没有指定目标包,缺省编译当前目录包;如果不是就不行,那得必须按照golang的项目组织规范来组织。
  <goproject>
   |-- src
        |-- myproject
             |-- main.go
             |-- foo.go

然后设置GOPATH=path/to/<goproject>,再运行go build myproject,这样就可以在任何目录下面编译,编译生成的可执行文件就在编译所在的目录下,而不是包源文件所在的目录。

例子2:引用了其他的包

基本规则:

  • 1.import <package>总是从$GOPATH/src目录下面搜索包,如果找不到就报错。
    • 1.2 并不会从当前目录下面去搜索,也不会从源文件相对目录下面去搜索。
  • 1.GOPATH可以包含多个路径,中间用冒号(:)隔开,就像PATH一样。

鉴于此,建议golang项目必须严格按照规范的目录结构组织,哪怕是前面这种自包含的项目。

例子3:vendor目录的使用

基本规则:

  • 1.使用vendor,项目必须严格按照规范的目录结构组织。
    • 1.2 即使像例子1中自包含的项目也不能使用vendor
  • 2.vender需要在原文件下面创建vendor目录,然后把vendor的文件包放入vendor目录即可,在引用的时候不需要指定vendor路径。
[~/]$ find <goproject>
<goproject>
<goproject>/src
<goproject>/src/myproject
<goproject>/src/myproject/main.go
<goproject>/src/myproject/vendor
<goproject>/src/myproject/vendor/mydeps
<goproject>/src/myproject/vendor/mydeps/dep1.go

[~/<goproject>]$ cat <goproject>/src/myproject/main.go 
package main

import "fmt"
import "mydeps"

func main() {
    fmt.Printf("main::main\n");
    mydeps.Foo()
}

[~/<goproject>]$ cat <goproject>/src/myproject/vendor/mydeps/dep1.go 
package mydeps

import "fmt"

func Foo() {
    fmt.Println("in mydeps::Foo")
}

例子4:vendor和GOPATH谁优先使用

如果一个包在vendor和GOPATH下面都存在那么谁会优先使用呢。
结论是:

    1. 优先使用vendor目录下面的包。
    1. 如果vendor下面没有搜索到,再搜索GOPATH下面的包。
    1. 要么完整使用vendor下面的包,要么完整使用GOPATH下面的包,不会混合使用:
      -3.1 假如一个函数定义再GOPATH下面的包里,而没有定义在vendor路径下的同名包里,那么调用者就会报函数未定义错误,因为调用者如果找到有vendor路径下面的包,就不会去找GOPATH下面的包了。
[~/<goproject>]$ find src
src
src/myproject
src/myproject/main.go
src/myproject/vendor
src/myproject/vendor/mydeps
src/myproject/vendor/mydeps/dep1.go
src/mydeps
src/mydeps/dep1.go

包mydeps在vendor目录下面和GOPATH路径下面都存在了,那么main.go引用的时候只会引用vendor下面的mydeps(src/myproject/vendor/mydeps),而忽略GOPATH下面的mydeps包(src/mydeps)。

例子5:vendor的层级搜索

前面提到GOPATH和PATH类似,可以包含多个路径,中间用分号隔开,go在搜索包的时候会按手续从前往后搜搜。那么vendor怎么处理层级关系呢。

规则是:

  • 1.从引用文件所在的vendor路径下面搜索。
  • 2.如果没有找到,那么从上层目录的vendor路径下面搜索。
  • 3.直到src的vendor路径下面搜索。

举例:

[~/<goproject>]$ find ./
./src
./src/myproject
./src/myproject/myproject
./src/myproject/main.go
./src/mydep
./src/mydep/mydep1
./src/mydep/mydep1/mydep.go
./src/mydep/mydep1/vendor
./src/mydep/mydep1/vendor/myvendor1
./src/mydep/mydep1/vendor/myvendor1/myvendor.go
./src/mydep/mydep.go
./src/mydep/vendor
./src/mydep/vendor/myvendor
./src/mydep/vendor/myvendor/myvendor.go

如果src/mydep/mydep1/mydep.go引用了myvendor1和myvendor,那是怎么搜索的呢

  1. 先从src/mydep/mydep1/vendor下面搜索myvendor1。
    找到了,直接使用。
  2. 先从src/mydep/mydep1/vendor下面搜索myvendor。
    发现没有找到,那么从上层路径搜索,即:
  3. 先从src/mydep/vendor下面搜索myvendor。
    找到了,直接使用。
  4. 如果还没有找到,那么继续向上一级搜索,即
    src/vendor
  5. 如果找到了,则使用;如果还没有找到,那么继续从GOPATH里搜索,直到找到或者失败。

总结

  1. 建议golang项目严格按照golang项目组织方式,即使只是一个自包含的项目。
<goproject>
   |-- src
        |-- mainpackage
             |-- XXX.go
             |-- YYY.go
             |-- vendor
        |-- deppackage1
             |-- XXX1.go
             |-- YYY1.go
             |-- vendor
        |-- deppackage2
             |-- XXX2.go
             |-- YYY2.go
             |-- vendor
                 |-- VVV1.go
                 |-- VVV2.go
                 |-- vendor

  1. GOPATH使用分号(:)隔开的多个路径。
    go编译的时候会从GOPATH/src目录下面搜索import的包。
  2. vender目录放在源文件目录同级,下面包含各个包。
    3.1 vendor的搜索优先于GOPATH的搜索。
    3.2 vendor按照路径深度向外按顺序搜索,直到$GOPATH/src/vendor为止。

有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:CodingCode

查看原文:golang的 GOPATH和vendor的搜索关系

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

4511 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传