48

golang的 GOPATH和vendor的搜索关系

 5 years ago
source link: https://studygolang.com/articles/15463?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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为止。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK