[go]go依赖包管理

运行main.go

go run单个文件

testapp $: tree
.
└── main.go
//main.go

package main

import "fmt"

func main() {
	fmt.Println("hello world")
}
testapp $: go run main.go
hello world

go run多个文件

testapp $: tree
.
├── lib.go
└── main.go
testapp $: cat lib.go
package main

import "fmt"

func test() {
	fmt.Println("test func")
}
testapp $: cat main.go
package main

import "fmt"

func main() {
    test()
	fmt.Println("hello world")
}
testapp $: go run main.go lib.go
test func
hello world

go build单个文件

testapp $: tree
.
└── main.go
testapp $: go build main.go
testapp $: ls
main	main.go

testapp $: ./main
hello world
testapp $: go build      //会在当前目录生成可执行文件(跟当前文件夹名字一样)
testapp $: ls
main.go	testapp

testapp $: ./testapp
hello world

go build多个文件

testapp $: tree
.
├── lib.go
└── main.go
testapp $: go build main.go
# command-line-arguments
./main.go:6:5: undefined: test
testapp $: go build main.go lib.go
testapp $: ls
lib.go	main	main.go
testapp $: ./main
test func
hello world
testapp $: go build
testapp $: tree
.
├── lib.go
├── main.go
└── testapp
testapp $: ./testapp
test func
hello world

go install

testapp $: go install  //项目不再GOPATH下
go install: no install location for directory /Users/mao tai/test/testapp outside GOPATH
	For more details see: 'go help gopath'
//将项目移到GOPATH下
$: mv testapp $GOPATH/src
$: cd $GOPATH/src/testapp
maotai@n1 ~/goproject/src/testapp $: go install
maotai@n1 ~/goproject/src/testapp $: ls
lib.go	main.go

maotai@n1 ~/goproject/bin $: ./testapp //被安装到了在GOPATH/bin/testapp
test func
hello world

go clean

go clean命令使用的可能不是很多,一般都是利用go clean命令清除编译文件,然后再将源码递交到 github 上


testapp $: go clean -n
cd /Users/maotai/test/testapp
rm -f testapp testapp.exe testapp.test testapp.test.exe lib lib.exe

testapp $: go clean -i -n
cd /Users/maotai/test/testapp
rm -f testapp testapp.exe testapp.test testapp.test.exe lib lib.exe

testapp $: go clean -x
cd /Users/maotai/test/testapp
rm -f testapp testapp.exe testapp.test testapp.test.exe lib lib.exe

-n 把需要执行的清除命令打印出来,但是不执行,这样就可以很容易的知道底层是如何运行的;
-i 清除关联的安装的包和可运行文件,也就是通过go install安装的文件;

-x 打印出来执行的详细命令,其实就是 -n 打印的执行版本;

go的包管理工具godep和dep

go包管理的发展

Go语言从v1.5开始开始引入vendor模式,如果项目目录下有vendor目录,那么go工具链会优先使用vendor内的包进行编译、测试等。
godep是一个通过vendor模式实现的Go语言的第三方依赖管理工具,类似的还有由社区维护准官方包管理工具dep。

godep从go 1.0开始就可以使用了,是比较老的包管理工具,早期使用go的人应该都用过godep.
dep是2017年初才发布的包管理工具,要求go >= 1.7.
随着go的发展,包管理工具日渐进化, 目前新项目都用go mod管理. 难免一些老项目用vendor等管理.

// 包管理的发展
go get(无版本概念) -> vendor(godep/dep) -> go modules

- GOPATH管理(go get)
- godep
- dep
- go mod
// vendor方式 包查找顺序

1. project/vendor
2. GOPATH/src
3. GOROOT/src

go get(GOPATH方式)

A. 所有的第三方包都放在$GOPATH的src目录下。
B. 如果不同程序依赖的版本不一样,怎么管理?
C. 每个程序依赖的包,没有版本号的概念。
//设置go get代理

go env -w GOPROXY=https://goproxy.cn,direct

项目组织

  • 适合个人

  • 适合公司

godep管理依赖

go get github.com/tools/godep

godep --help
godep save

该命令会在根目录下自动生成一个Godeps和vendor目录,并将项目所依赖的第三方包信息写入Godeps/Godeps.json,同时复制包源码到vendor目录。
注意:godep save并不会自动从远程下载依赖包,需要我们通过go get或godep get手动下载,godep save只是将下载的包源码复制到vendor目录。

举个例子

├── myapp
│   └── main.go
//main.go
package main

import (
    "fmt"

    "github.com/shopspring/decimal"
)

func main() {
    price, err := decimal.NewFromString("136.204")
    if err != nil {         panic(err)
    }
    fmt.Println(price.StringFixed(2))
}
go get github.com/shopspring/decimal
godep save

这里我们导入了一个decimal包,通过go get下载下来后,执行godep save,查看项目目录结构:

myapp
├── Godeps
│   ├── Godeps.json
│   ├── Readme
│── vendor
│   └── github.com
│       └── shopspring
│           └── decimal
│              │── decimal.go
│              │── LICENSE
│              │── README.md
└── main.go

如果我们的项目新增了某些依赖包,只需执行godep save就可以了,非常方便。但这种包管理方式,也有个不好的地方,
就是除了Godep.json文件,还需要将vendor目录也提交到代码仓库,而且只要变更了引入的第三方包,则要重新提交vendor目录,只有这样才能保证其他人使用的包版本一致。如果引用的包较多,则代码仓库将变得很庞大。

dep方式

go get -u github.com/golang/dep/cmd/dep

dep init
dep init -v

与godep不同的是,dep可以直接从远程下载依赖包,但目前下载的速度较慢,而如果是需要翻墙才能下载的包,那么dep可能会一直堵塞。

dep init执行完后,查看当前目录结构,dep生成了两个文件和一个vendor目录:

├── Gopkg.lock
├── Gopkg.toml
├── main.go
└── vendor/

dep是如何下载依赖包的呢?
dep在下载依赖包时,会先去检查$GOPATH下是否已经存在该包,若存在,则dep直接将其拷贝到vendor目录下;若不存在,则会从远程下载。

dep status命令用于查看当前项目依赖了哪些包,以及包的版本号:

➜  dep status
PROJECT                        CONSTRAINT     VERSION        REVISION  LATEST   PKGS USED
github.com/shopspring/decimal  branch master  branch master  16a9418   16a9418  1

dep ensure命令会根据项目代码依赖的包,将对应包信息写入Gopkg.lock文件,将包源码下载到vendor目录,当不再使用某个包时,执行该命令也会将其从vendor中移除,并更新Gopkg.lock文件。但是,dep ensure不会去更新Gopkg.toml。

那么,dep ensure是如何知道要下载的包的版本号呢?下面要划重点了:dep ensure下载依赖包的版本是根据Gopkg.toml来的,而Gopkg.lock只是用来记录下载的各个依赖包的具体版本信息。

go mod使用

拜拜了,GOPATH君!新版本Golang的包管理入门教程

gopath管理依赖存在的问题:
0. 项目被强制存放到GOPATH/src下

  1. 项目A依赖v1 项目B依赖v2,将依赖包都存放到 src下会冲突. 因为src下文件名没包含版本信息
  2. 需要手动去下载依赖包
自动下载依赖包
项目不必放在GOPATH/src内了
所以来的第三方包会准确的指定版本号
对于已经转移的包,可以用replace 申明替换,不需要改代码
项目内会生成一个go.mod文件,列出包依赖

使用例子

testapp $: cat main.go
package main

import (
    "fmt"

    "github.com/shopspring/decimal"
)

func main() {
    price, err := decimal.NewFromString("136.204")
    if err != nil {         panic(err)
    }
    fmt.Println(price.StringFixed(2))
}
testapp $: go run main.go
main.go:6:5: cannot find package "github.com/shopspring/decimal" in any of:
	/usr/local/go/src/github.com/shopspring/decimal (from $GOROOT)
	/Users/xiaolangma/goproject/src/github.com/shopspring/decimal (from $GOPATH)
testapp $: go mod init testapp
go: creating new go.mod: module testapp
testapp $: tree
.
├── go.mod
├── go.sum
└── main.go
testapp $: go run main.go
go: finding github.com/shopspring/decimal latest
136.20
问题一:依赖的包下载到哪里了?还在GOPATH里吗? 不在。
使用Go的包管理方式,依赖的第三方包被下载到了$GOPATH/pkg/mod路径下。
如果你成功运行了本例,可以在您的$GOPATH/pkg/mod 下找到一个这样的包 github.com/astaxie/beego@v1.11.1
问题二: 依赖包的版本是怎么控制的?

可以看到最终下载在$GOPATH/pkg/mod 下的包 github.com/astaxie/beego@v1.11.1
最后会有一个版本号 1.11.1,也就是说,$GOPATH/pkg/mod里可以保存相同包的不同版本。
//问题三: 可以把项目放在$GOPATH/src下吗? 可以

auto 自动模式下,项目在$GOPATH/src里会使用$GOPATH/src的依赖包,在$GOPATH/src外,就使用go.mod 里 require的包
on   开启模式,1.12后,无论在$GOPATH/src里还是在外面,都会使用go.mod 里 require的包
off  关闭模式,就是老规矩。
问题四: 依赖包中的地址失效了怎么办?比如 golang.org/x/… 下的包都无法下载怎么办?
在go快速发展的过程中,有一些依赖包地址变更了。 以前的做法

方法1: 修改源码,用新路径替换import的地址
方法2: git clone 或 go get 新包后,copy到$GOPATH/src里旧的路径下 无论什么方法,都不便于维护,特别是多人协同开发时。

replace golang.org/x/text => github.com/golang/text latest
问题五: init生成的go.mod的模块名称有什么用?
本例里,用 go mod init hello 生成的go.mod文件里的第一行会申明 module hello

因为我们的项目已经不在$GOPATH/src里了,那么引用自己怎么办?就用模块名+路径。
testapp $: go mod vendor //将项目代码中import的第三方包copy到vendor下. 1.便于阅读 2.便于存档提交git.
testapp $: tree
.
├── go.mod
├── go.sum
├── main.go
└── vendor
    ├── github.com
    │   └── shopspring
    │       └── decimal
    │           ├── LICENSE
    │           ├── README.md
    │           ├── decimal-go.go
    │           ├── decimal.go
    │           ├── go.mod
    │           └── rounding.go
    └── modules.txt
//依赖更新
go get -u          //升级所有依赖

go list -m all     //列出当前依赖
go ilst -m -u all  //列出可升级的依赖
  • incomba

  • 下划线导入

  • 静态导入

goland使用

Go进阶21:Goland 6条Tips让编程更有效率
构造方法
struct实现接口

原文地址:https://www.cnblogs.com/iiiiiher/p/12290951.html