关于go的包管理
以前在给开源项目贡献代码的时候,遇到过因为 golang 的 import path 导致的问题,详细可以参考这里。
由于 golang 本身没有像 java 或者 java 的儿子 javascript 那样集中式的包管理方式,例如 maven/gradle 或者即使是稀烂的 npm。便导致了 import path 里有各种网址开头的包。github.com/xxx/yyy。一开始的时候 go 社区的人还把这个当作是优点来宣传,不过现在看起来已经成为了 golang 的历史包袱。让人郁闷的是 golang 官方似乎并不准备解决这个问题。在最有可能成为官方包管理工具的 dep 这个项目里,也是铁了心把无中心的包管理一条路走到黑(没办法啊,因为我不是官方)。
在这个过程中,有不少人建议在开发 golang 项目的时候,程序的内部包在被引用的时候也写全路径,就像上面链接里的那样,github.com/tyba/beanstool/cli。如果是一个 web 项目,我们在 route.go 里设置全部的路由,import 部分就可能会变成下面这个样子:
import (
"github.com/cch123/projectname/controller/hello"
"github.com/cch123/projectname/controller/star"
"net/http"
"github.com/pressly/chi"
"github.com/pressly/chi/middleware"
)
表面看着没问题,如果说你们团队在 gitlab 或者 github 上的工作方式都是在同一个主仓库下建分支,做开发,提 pull request 也没什么问题。
然而现实哪有那么美好啊。想想开源社区最普遍的是什么样的开发模式。你向大多数项目提代码的时候基本都是使用的 forking 工作流。这也是在没有 contributor 权限的时候唯一能够贡献代码的方式(你总不至于去issue里说,你看这个文件这里有问题,应该这样那样改吧)。
使用 forking 工作流来开发像上面这样的项目。最大的问题就是fork之后工作路径就错位了。可能有人又说我 fork 出来以后可以再手动 clone 到和 import 路径一致的 GOPATH 下的目录里啊?
呵呵,那原来那个目录里的东西怎么办。如果还有正在依赖原来版本的本地项目也在开发中呢?
总还是有点蛋疼的。
如果我能够使用相对路径的话,那么在别人 fork 走我的项目的时候是不是就没有这个问题了?
然而相对路径也有一些问题,比如你在自己的 web 项目里写了
import "controller/star"
go build 的时候会告诉你找不到啊,你要是想要让它能够找到,又只能去为自己的项目设置一个单独的 GOPATH,然后把这个项目放在 src 文件夹下。然而你就发现 vs code 项目里有人去问怎么给项目设置独立的 GOPATH 了。马上有人跳出来说你这么做是不对的!golang 应该只设置一个全局的 GOPATH!靠。
这个问题怎么解决比较好我之前也一直没有想明白。恰巧最近有个b站大佬入职我司,和他聊起来,给了一些hint。比较好的做法可以参考 goim 或者 beego 的 bee 工具默认生成的 web 项目。在 import 相对路径的时候,把项目名也带上。当然,这种情况下,整个项目就要直接放在系统全局的 GOPATH 下了。还是上面的例子,现在可能会变成下面这种形式:
import "credit/controller/star"
import "credit/controller/model"
credit是项目名。放在 GOPATH/src 目录下。可以直接在该目录下 go build。
这样做的话,如果你在一个 GOPATH 下同时开发多个项目也不会有啥问题,而且如果别人 fork 出去,只要用 git clone,把项目 clone 到 GOPATH 文件夹下就可以。同时还可以通过设置不同的 remote 来实现 origin/upstream的切换。
但是这个方案是完美的么?
不是,你没法用 go get 了。所以这种组织方式比较适合独立运行的工具类或者业务系统,不适合通用的开源项目库类型项目。
如果是 lib 类型的项目呢?还是没啥好办法。。。你总不至于和别人说依赖我的项目不能用 go get 吧?