GOPATH、Go Vendor、Go Modules的来龙去脉,我想这篇文章足够了吧!

可以关注我的公众号:【前端驿站Lite】,一个不止分享前端的地方 ᕦ( •̀∀•́)ᕤ

阅读收获

阅读完本篇文章,你将会有以下收获:

  1. GOPATH如何使用,有哪些缺点。
  2. Go Vendor如何使用,有哪些缺点。
  3. Go Modules 详细使用过程,解决了哪些问题。
  4. go install、go get、go mod等常用命令详解。
  5. GOPATH、GOROOT、GOBIN、GOPROXY、GO111MODULE、GOPRIVATE等环境变量详解。

使用go

下载安装golang以后,配置系统PATH环境变量,设置为go安装目录下的bin目录,这样我们就可以在终端使用go命令了,如:go versiongo envgo run等。

GO发展历史

在GO1.11之前,我们都是使用GOPATH的方式来管理我们的项目的,但经过开发者们的实操检验,发现GOPATH的方式,存在很多问题,关于GOPATH方式带来的问题,我们下面会详细介绍。

因为GOPATH的方式存在很多问题,所以GO1.11之后,官方推出了Go modules的方式来管理我们的项目,发布于 Go1.11,成长于 Go1.12,丰富于 Go1.13,正式于 Go1.14 推荐在生产上使用。

现在,大多是情况下,我们都是使用Go modules方式来管理我们的项目了。

为了更好的学习Go modules,我们有必要先了解下如何使用GOPATH方式管理项目,GOPATH有哪些缺点需要改进。

GOPATH 是什么呢?

使用GOPATH方式管理项目之前,我们需要先配置一个go的环境变量,叫做GOPATH,可以执行go env来查看该环境变量的值。

设置go env方式有两种:

  • 第一种:使用 go env -w GOPATH=xxx 方式来设置。
  • 第二种:直接在系统环境变量中设置环境变量GOPATH

注意: 系统环境变量优先级最高,如果设置了系统级环境变量GOPATH,无法使用go env -w GOPATH=xxx方式去修改。

假设,我们目前的GOPATH为/Users/mac/go,那么我们需要在/Users/mac/go目录下创建三个子目录,分别是srcpkgbin

/Users/mac/go  
├── bin  
├── pkg  
└── src  

这三个目录是干嘛的呢? 我们一一来讲解。 首先说一下src目录。

GOPATH/src

GOPATH方式强制规定,我们所有的go项目都必须在GOPATH/src目录下,否则就无法使用各种go命令,来完成我们的开发工作。

假设,我们创建了一个项目my-project

当我们项目中需要引入三方包时,要使用go get命令下载第三方依赖,如go get github.com/gin-gonic/gingo get会将下载的三方包,放在GOPATH/src目录下。

此时呢,我们的GOPATH/src目录结构如下:

/Users/mac/go  
├── bin  
├── pkg  
└── src  
├── my-project  
└── github.com  
└── gin-gonic  
└── gin  

这时候就会发现GOPATH/src目录下即有我们自己的项目,也有我们下载的三方包,这样会导致我们的项目管理起来比较混乱,不方便管理。

当我们导入一个包时,会先向GOROOT/src下查找,如果找不到,再去GOPATH/src下查找,如果还是找不到,就会报错。

go install

关于GOPATH/binGOPATH/pkg两个目录,我们会通过go install去讲解。

go install命令会将我们的go源代码编译生成二进制可执行文件,并将该可执行文件放到GOPATH/bin目录下。

没错GOPATH/bin目录,就是用来给go install存放可执行文件的。

1. go install命令有两种使用方式:

  • 加包名:go install [package],该package必须在GOPATH/src目录下,否则会报错。
  • 不加包名:go install,该命令会将当前目录当作要编译的包,该目录也必须在GOPATH/src目录下,否在会报错。

2. go install什么时候会生成可执行文件?

要编译的包下,必须有main包,且main包下必须有main方法,才会生成可执行文件,文件名默认为包名。

3. go install 会生成 .a 文件

.a文件是编译过程中产生的中间文件,当下次执行go install时,会检查该包下的go源码有没有变动,如果没有变动,直接使用上次生成的.a文件进行编译,加快编译速度。

GOPATH/pkg目录的作用,就是用来存放.a文件的。

4. 什么时候会用到 go install

有些场景下,我们需要把我们的go项目或三方包生成可执行文件去使用,此时,就要用到go install命令了。

5. go installgo get有什么区别

  • go get 会下载并安装三方包,也就说go get 包含了 go install 的功能。
  • go install 只能用来安装本地已经存在的依赖,不能用来下载依赖。

6. go install 注意事项

  • go install 的使用,有一个前提,我们的项目必须在GOPATH/src目录下,否则会报错。

GOPATH 缺点

  • 自己编写的包,和网络上的第三方包,都放置在GOPATH/src下,容易造成混乱,不方便管理。
  • 项目中用到的依赖包,都需要手动go get下载,大项目来说非常麻烦
  • 如果引入的三方包中又引入了其他三方包,就不好处理了,需要找到以后,使用go get去下载。
  • go get没有版本的概念,团队合作中,很容易出现使用了不同版本的包,造成不必要的错误。
  • 协作开发时,需要统一各个开发成员本地$GOPATH/src下的依赖包。
  • 引用的包引用了已经转移的包,而作者没改的话,需要自己修改引用。

GOPATH方式管理项目存在太多不方便了,所以Go1.11之后,官方推出了Go modules的方式来管理我们的项目。

Go Vendor

为了解决GOPATH方式 三方依赖与项目都放在GOPATH/src下的问题,Go1.5之后,官方推出了Go Vendor的方式来管理项目。

go vendor就是将依赖的包,特指外部包,复制到当前项目下的vendor目录下,这样go build的时候,go会优先从vendor目录寻找依赖包。

Go Vender的出现解决了GOPATH不能多版本控制的问题。

Go Vender放弃了依赖重用,使冗余度上升。同一个依赖包如果不同工程想复用,都必须各自复制一份在自己的vendor目录下。

开启方式

  • golang 1.5默认是关闭的,需要设置go env -w GO15VENDOREXPERIMENT=1 来手动开启。
  • golang 1.6默认开启
  • goalng 1.7 已去掉该环境变量,默认开启 vendor 特性。

govendor

那有了Vendor 特性,我们如何去用呢,总不能手动去搞吧? 于是govendor就出现了。

govendor 是一个基于 vendor 机制实现的 Go 包依赖管理命令行工具。

# 1.下载安装  
go get -u github.com/kardianos/govendor  
# 2.初始化项目  
govendor init  

项目根目录下即会自动生成 vendor 目录和 vendor.json 文件。此时 vendor.json 文件内容为:

{  
"comment": "",  
"ignore": "test",  
"package": [],  
"rootPath": "govendor-example"  
}  

常用命令

  • govendor add +external 将已被引用且在 $GOPATH 下的所有包复制到 vendor 目录
  • govendor add gopkg.in/yaml.v2 将指定包复制到 vendor 目录
  • govendor list 列出代码中所有被引用到的包及其状态
  • govendor list -v fmt 列出一个包被哪些包引用
  • govendor fetch golang.org/x/net/context 从远程仓库添加或更新某个包(不会在 $GOPATH 也存一份)
  • govendor fetch golang.org/x/net/context@v1 安装指定版本的包

还有很多,因为现在不怎么用了,不一一列举了,有需要可以去官网查看。

未解决的问题

无法精确的引用 外部包进行版本控制,不能指定引用某个特定版本的外部包,只是在开发时将其拷贝过来,但是一旦外部包升级,vendor 下面的包会跟着升级,而且 vendor 下面没有完整的引用包的版本信息, 对包升级带来了无法评估的风险。

Go modules

因为GOPATHGo Vendor都存在很多问题,官方又推出了Go modules

首先,Go Modules 允许我们把项目创建到任意目录,这就解决了项目与依赖包混乱的问题。

go.mod

Go Modules使用go.mod文件来管理我们的项目,类似前端的package.json文件。

下面是一个go.mod文件的例子:

module github.com/nev/module1  
  
go 1.19  
  
require (  
example.com/apple v0.1.2  
example.com/banana v1.2.3  
example.com/banana/v2 v2.3.4  
example.com/pear // indirect  
example.com/strawberry // incompatible  
)  
  
exclude example.com/banana v1.2.4  
replace example.com/apple v0.1.2 => example.com/fried v0.1.0  
replace example.com/banana => example.com/fish  
  • module 是我们该项目包的名字。
  • go 初始化项目时,使用的go版本。
  • require 我们项目使用的依赖包及版本。
  • exclude 跳过某个依赖库的版本,使用场景一般是我们知道某个版本有bug或者不兼容,为了安全起可以使用exclude跳过此版本。
  • replace 将一个模块版本替换为另外一个模块版本。

go.sum

Go Modules在做依赖管理时会创建两个文件,go.modgo.sum

go.sum 防下载的依赖被恶意篡改,主要用于安全校验。

go.sum 记录了所有依赖的 module 的校验信息,在项目构建时,使用的依赖包必须跟 go.sum 中记录的完全一致, 否则就会报错。

  • go.sum 文件中会把直接依赖、间接依赖全部记录;
  • 项目中依赖的每一个包在 go.sum中都会有两条记录,第一条是整个包所有文件一起计算得到哈希值,第二条表示该依赖包的 go.mod 文件计算得到的哈希值。
# go.sum文件中每行记录由module名、版本和哈希组成,并由空格分开:  
<module> <version> <hash>  
<module> <version>/go.mod <hash>  

比如,某个go.sum文件中记录了github.com/google/uuid 这个依赖包的v1.1.1版本的哈希值:

github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=  
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=  

使用Go Modules

要想使用Go Modules, 我们需要先在项目下执行go mod init 包名,这样就会在项目下生成一个go.mod文件。

go mod init example.com/myapp  

执行完上面命令后,会在项目下生成一个go.mod文件,内容如下:

module example.com/myapp  
  
go 1.21  

添加三方依赖

我们项目中,需要安装三方依赖时,可以使用go get命令。

使用go modules之后,go get 拉取依赖的方式就发生了变化。 注意:在go1.16之后,go get 只会下载依赖,不会进行安装了。

# 拉取最新的版本(优先择取 tag)  
go get golang.org/x/text@latest  
  
# 拉取 master 分支的最新 commit  
go get golang.org/x/text@master  
  
# 拉取 tag 为 v0.3.2 的 commit  
go get golang.org/x/text@v0.3.2  
  
# 拉取 hash 为 342b231 的 commit,最终会被转换为 v0.3.2:  
go get golang.org/x/text@342b231  
  
# 指定版本拉取,拉取v3版本  
go get github.com/smartwalle/alipay/v3  
  
# 更新依赖  
go get -u github.com/smartwalle/alipay/v4  

go get 会将依赖下载到$GOPATH/pkg/mod目录下,同时会在go.mod文件中添加依赖信息。

go mod download

go mod download 会根据go.mod自动下载依赖到$GOPATH/pkg/mod目录下,但不会更新go.mod文件。

也可以下载 go.mod 中单个依赖 go mod download github.com/jinzhu/now

但不能下载 go.mod 中没有的依赖,否则会报错。

go mod tidy

go mod tidy 会自动检查依赖项,删除go.mod存在,但项目中没用到依赖,并更新go.mod文件。

还会自动下载项目中使用到,但还没有下载的依赖。

刚下载的go项目,可以使用该命令,自动下载依赖。

Go Modules 其他常用命令

  • go mod graph 查看当前项目的依赖树
  • go mod edit 编辑go.mod文件
  • go mod vendor 将依赖转移至本地的vendor文件
  • go mod verify 检查本地依赖与项目依赖是否匹配

开启Go Modules

上面讲到了 Go modules 的使用,那如何开启Go modules呢?

我们可以通过设置go envGO111MODULE来开启Go modules,该环境变量有三个值:offonauto,默认值是auto

  • GO111MODULE=off,无模块支持,go命令行将不会支持module功能,寻找依赖包的方式将会沿用旧版本那种通过vendor目录或者GOPATH模式来查找。意思就是,当我们编译执行一个go源码文件的时候,如果文件中有依赖其它包,它会首先去GOROOT指定的路径下查找,然后再去GOPATH指定的路径下查找。
  • GO111MODULE=on,模块支持。go命令行会使用modules,而一点也不会去GOPATH目录下查找。
  • GO111MODULE=auto,默认值。 若出现如下情况,则会使用Go modules,否则使用GOPATH方式。
# GO111MODULE=auto时,出现以下两种情况会启用Go modules:  
1. 项目目录不在go path下  
2. 当前或者上一级目录存在go.mod文件  

设置代理

在一些网络较为特殊的环境下,我们若没办法直接访问库地址,我们可以通过代理去镜像拉取,通过go环境变量GOPROXY来设置。

该值有2个参数: goProxyUrl,[off,direct]

  • 若为off: 表示不会回源,即: 当代理拉取不到数据的时候,不会去源站镜像拉取。
  • 若为direct: 表示会回源,当代理拉取不到的时候,会去源站尝试拉取
# 推荐使用七牛云goproxy.cn  
go env -w GOPROXY=https://goproxy.cn,direct  

设置私有库

有时候,我们自己的仓库不需要走代理,我们可以通过GOPRIVATE来设置。

go env -w GOPRIVATE=git.mycompany.com,github.com/my/private  

老项如何使用Go Modules

  • 在项目下执行go mod init 包名,生成go.mod文件
  • 在项目下执行go mod tidy,自动下载依赖

Go Modules 好处

  • 可以自动下载依赖包
  • 项目不必放在GOPATH/src内了
  • 项目内会生成一个go.mod文件,列出包依赖
  • 所以来的第三方包会准确的指定版本号
  • 对于已经转移的包,可以用replace 申明替换,不需要改代码

现在基本都是使用Go Modules来管理项目了。

项目打包

项目部署前,需要先打包项目,我们需要使用go build命令,该命令会将我们的go源代码编译生成二进制可执行文件。 默认将可执行文件输出到当前目录下。

也可以使用-o参数指定输出目录与打包后的文件名。

# 将可执行文件输出到test目录下  
go build -o ./test main.go  
  
# 将可执行文件输出到test目录下,并命名为demo.exe  
go build -o ./test/demo.exe main.go  

go build 与 go install 的区别

  • go install 会将生成的二进制可执行文件,放到$GOBIN目录下。 go build 默认将可执行文件生成在当前目录下。
  • go install 会生成库文件(.a文件),go build不会。

其他一些环境变量

GOROOT

go envGOROOT是Golang的安装目录,我们在终端执行go version命令时,会去GOROOT目录下查找go的版本信息。

当我们程序使用导入系统库,例如: fmt的时候,其实会先到GOROOT中进行寻找,假设GOROOT = /usr/local/go,那么import fmt 其实该库的绝对路径为/usr/local/go/src/fmt/print.go

GOBIN

go envGOBIN是Golang的可执行文件目录,我们在终端执行go install命令时,会将编译后的可执行文件放到GOBIN目录下。

不设置的话,go env 查看为空,默认为GOPATH/bin

我们也可以把GOBIN,加入到系统PATH环境变量中,方便我们随时执行里面的可执行文件。

go 常用命令

go run

编译并运行go代码,不会生成可执行文件,只会在临时目录下生成一个临时可执行文件,执行完毕后,会自动删除。 不需要设置GOPATH

go run main.go  

go get

下载并安装依赖包到$GOPATH/pkg/mod 目录下, 会自动更新go.mod和go.sum文件。 不需要设置GOPATH

指定参数

-d # 只执行下载动作,而不执行安装动作  
-fix # 在下载代码包后先执行修正动作,而后再进行编译和安装  
-u # 利用网络来更新已有的代码包及其依赖包  
-x # 显示过程  

go get 变更记录

  • 自 Go 1.17 起, 弃用 go get 命令安装可执行文件,使用 go install 命令替代。
# Go 1.17 之前:go get 通过远程拉取或更新代码包及其依赖包,并自动完成编译和安装。实际分成两步操作:  
# Go 1.17 之后: 弃用go get命令的编译和安装功能  
  
# 1. 下载源码包  
# 2. 执行 go install  
  • 自Go 1.18起,go get 命令不再有编译包的功能。将只有添加,更新,移除 go.mod 文件中的依赖项的功能。
  • go get 命令将默认启用 -d 选项。

go env

查看go环境变量

# 环境变量配置文件路径  
go env GOENV  
  
# 列出所有环境变量  
go env  
  
# 列出所有环境变量(以json格式)  
go env -json  
  
# 修改某个环境变量  
go env -w GOPROXY=https://goproxy.io,direct  
  
# 重置某个变量  
go env -u GOPROXY  

go 其他常用命令

  • go list -m -versions github.com/gin-gonic/gin 查看可下载版本
  • go list -m all 查看当前项目的所有依赖,也可以添加 -json 参数,例如: go list -m -json all
  • go test 运行测试源码文件
  • go build/test/run 这三个命令都会自动下载依赖,并更新go.modgo.sum文件

完结撒花🎉🎉🎉,你又进步了一点点。

朋友,如果觉得本文章对你有帮助,可以点点赞支持一下嘛。先谢谢啦 ᕦ( •̀∀•́)ᕤ

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,下面是一个将之前的脚本改为在 `/vendor/bin` 目录下,并将切换屏幕的按键改为 TV 键的示例。 首先,我们将之前的脚本保存为 `/vendor/bin/switch_screen` 文件,并赋予可执行权限: ```bash chmod +x /vendor/bin/switch_screen ``` 然后,我们在 `/vendor/etc/init/hw/init.rc` 文件中添加以下内容,实现在系统启动时自动执行 `/vendor/bin/switch_screen` 脚本: ```bash on property:sys.boot_completed=1 start switch_screen ``` 最后,我们在 `/vendor/etc/init/hw/post-init.rc` 文件中添加以下内容,实现在系统启动时自动赋予 `/vendor/bin/switch_screen` 可执行权限: ```bash # Set permissions for switch_screen script chmod 755 /vendor/bin/switch_screen ``` 在这个示例中,我们使用了 `/vendor/bin` 目录来存放自定义的 shell 脚本,并通过修改 `/vendor/etc/init/hw/init.rc` 文件来实现在系统启动时自动执行自定义的 shell 脚本。需要注意的是,不同的 Android 平台可能会有所不同,具体的实现方法可能需要根据实际情况进行调整。 另外,我们将切换屏幕的按键码改为 TV 键(即 `KEYCODE_TV`),你可以根据实际情况进行调整。下面是修改后的脚本: ```bash #!/vendor/bin/sh # 设置主屏幕为默认显示屏幕 setprop sys.display.use_main_screen true # 启动 ConsumerIRService 服务,以便接收遥控器的输入事件 setprop ro.remotecontroller.modules consumerir start consumer_ir_service # 循环监听遥控器的输入事件 while true; do # 读取遥控器的输入事件,并解析出按键码 ir_code=$(getevent -t -c 1 /dev/input/event0 | grep -oP ".*?((?<=KEYCODE_)[A-Z0-9]+).*" | awk '{print $10}') # 如果按键码是 KEYCODE_TV,则切换主副屏幕的显示状态 if [ "$ir_code" = "KEYCODE_TV" ]; then if [ "$(getprop sys.display.use_main_screen)" = "true" ]; then setprop sys.display.use_main_screen false else setprop sys.display.use_main_screen true fi fi # 降低 CPU 占用率,避免过度消耗系统资源 sleep 0.1 done ``` 希望这个示例对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值