差不多该认真考虑一下关于Golang开发环境的问题了——GOPATH污染的难题

由于「はじめての Go 言語」系列已经结束了,所以我想开始认真地搭建开发环境并尝试各种事情。首先,从准备环境开始。

GoPath 污染问题

去拿取指令是一個非常強大的功能,對像我這樣在Windows和UNIX環境之間移動的人來說,可以完全使用go get指令完成從存儲庫的獲取到構建/安裝的過程,而無需依賴於環境特定的工具如curl或make。這真是令人感激(當然,仍然可以使用make等工具進行更精細的控制)。

如果在一台机器上(包括虚拟机)管理一个项目的话,这样就足够了,但遗憾的是,工作中很少会出现这种情况。能够为每个项目准备虚拟机还算可以,但更多的情况是在仅限于最低配置,无法做到甚至这样的情况下同时处理多个项目的烦恼(这与Google大神是不同的)。而且,令人困扰的是,GOPATH环境变量并没有考虑到多个项目管理的情况。

在GOPATH环境变量中,可以设置多个路径。如果是在Windows环境下,可以像这样设置。

SET GOPATH=C:\golib;C:\workspace\project1;C:\workspace\project1;...

然而,只有在Go编译器搜索外部包时,这些路径才能全部有效,而在使用go get命令来获取存储库的情况下,则需要在GOPATH中指定第一个路径(在上述示例中为C:\golib)。这样一来,尽管我们为每个项目创建了单独的文件夹,但外部包却被整合到一个文件夹中,导致管理变得混乱。

【解决方案1】为每个项目重新设置GOPATH。

对于这个问题,最直接的答案是重新设置每个项目的 GOPATH。例如,在上述例子中介绍的构建 gb 时,可以按以下方式操作。

C:>SET GOPATH=C:\workspace\gb

C:>go get -v github.com/constabulary/gb/...
github.com/constabulary/gb (download)
github.com/constabulary/gb
github.com/constabulary/gb/cmd
github.com/constabulary/gb/vendor
github.com/constabulary/gb/cmd/gb
github.com/constabulary/gb/cmd/gb-vendor

C:>cd C:\workspace\gb

C:\workspace\gb>tree /f
C:.
├─bin
│      gb-vendor.exe
│      gb.exe
│
├─pkg
│  └─windows_amd64
│      └─github.com
│          └─constabulary
│              └─gb
│
└─src
    └─github.com
        └─constabulary
            └─gb
                ├─cmd
                │  ├─gb
                │  └─gb-vendor
                ├─testdata
                └─vendor
                    └─_testdata

(源文件和pkg文件夹以下部分已经省略)

只需完成以下步骤之一:将路径添加到 bin 文件夹或将执行文件复制到已通行路径的文件夹。如果将执行历史保存在批处理文件(或者 shell 脚本)中,随时可以恢复。

确实,每次都需要设置环境确实是麻烦的,但对于项目管理来说,并不需要额外的工具,只需使用Go编译器的标准功能即可进行管理。仅凭标准功能进行管理是相当重要的,例如,如果使用CI工具,可以简单地进行配置,因此更容易管理。

使用基于项目的管理工具

还有一种方法是使用类似于GB的项目基础的代码管理工具。例如,在“第10节”中介绍的tcnksm/gcli的构建环境可以使用GB进行搭建。

C:> cd C:\workspace\gcli

C:\workspace\gcli>git clone https://github.com/tcnksm/gcli.git src\github.com/tcnksm/gcli
Cloning into 'src\github.com/tcnksm/gcli'...
remote: Counting objects: 766, done.
remote: Total 766 (delta 0), reused 0 (delta 0), pack-reused 766Receiving objects:  90% (690/766), 2.11 MiB | 828.00 KiB/s
Receiving objects: 100% (766/766), 2.50 MiB | 828.00 KiB/s, done.
Resolving deltas: 100% (415/415), done.
Checking connectivity... done.

C:\workspace\gcli>gb vendor fetch github.com/mitchellh/cli
fetching recursive dependency golang.org/x/crypto/ssh/terminal

C:\workspace\gcli>gb vendor fetch github.com/olekukonko/tablewriter

C:\workspace\gcli>gb vendor fetch github.com/tcnksm/go-gitconfig

C:\workspace\gcli>gb vendor fetch github.com/tcnksm/go-latest
fetching recursive dependency github.com/google/go-github/github
fetching recursive dependency github.com/google/go-querystring/query
fetching recursive dependency github.com/hashicorp/go-version
fetching recursive dependency golang.org/x/net/html

C:\workspace\gcli>pushd src\github.com\tcnksm\gcli\skeleton

C:\workspace\gcli\src\github.com\tcnksm\gcli\skeleton>go-bindata -pkg="skeleton" resource/...

C:\workspace\gcli\src\github.com\tcnksm\gcli\skeleton>popd

C:\workspace\gcli>gb build
github.com/tcnksm/gcli/helper
github.com/tcnksm/go-gitconfig
github.com/google/go-querystring/query
golang.org/x/crypto/ssh/terminal
github.com/hashicorp/go-version
golang.org/x/net/html/atom
github.com/olekukonko/tablewriter
github.com/google/go-github/github
github.com/tcnksm/gcli/skeleton
github.com/mitchellh/cli
golang.org/x/net/html
github.com/tcnksm/go-latest
github.com/tcnksm/gcli/command
github.com/tcnksm/gcli

C:\workspace\gcli>bin\gcli.exe version
[0;0mgcli version v0.2.0[0m
[0;31m
Your versin of gcli is out of date! The latest version is 0.2.1.[0m

C:\workspace\gcli>
C:.
├─bin
│      gcli.exe
│
├─pkg
│  └─windows
│      └─amd64
│          ├─github.com
│          │  ├─google
│          │  │  ├─go-github
│          │  │  └─go-querystring
│          │  ├─hashicorp
│          │  ├─mitchellh
│          │  ├─olekukonko
│          │  └─tcnksm
│          │      └─gcli
│          └─golang.org
│              └─x
│                  ├─crypto
│                  │  └─ssh
│                  └─net
│                      └─html
├─src
│  └─github.com
│      └─tcnksm
│          └─gcli
│              ├─command
│              ├─helper
│              ├─skeleton
│              │  └─resource
│              └─tests
└─vendor
    │  manifest
    │  
    └─src
        ├─github.com
        │  ├─google
        │  │  ├─go-github
        │  │  │  └─github
        │  │  └─go-querystring
        │  │      └─query
        │  ├─hashicorp
        │  │  └─go-version
        │  ├─mitchellh
        │  │  └─cli
        │  ├─olekukonko
        │  │  └─tablewriter
        │  │      └─csv2table
        │  └─tcnksm
        │      ├─go-gitconfig
        │      └─go-latest
        │          └─latest
        └─golang.org
            └─x
                ├─crypto
                │  └─ssh
                │      └─terminal
                └─net
                    └─html
                        ├─atom
                        ├─charset
                        └─testdata

(src和pkg文件夹及其子文件夹部分内容已被省略)

在 gb 中,外部包是通过 gb vendor fetch 命令引入的,而不是克隆仓库,只需简单地复制它们。外部包的信息存储在 vendor/manifest 文件中。

{
    "version": 0,
    "dependencies": [
        {
            "importpath": "github.com/google/go-github/github",
            "repository": "https://github.com/google/go-github",
            "revision": "7277108aa3e8823e0e028f6c74aea2f4ce4a1b5a",
            "branch": "master",
            "path": "/github"
        },
        {
            "importpath": "github.com/google/go-querystring/query",
            "repository": "https://github.com/google/go-querystring",
            "revision": "547ef5ac979778feb2f760cdb5f4eae1a2207b86",
            "branch": "master",
            "path": "/query"
        },
        {
            "importpath": "github.com/hashicorp/go-version",
            "repository": "https://github.com/hashicorp/go-version",
            "revision": "999359b6b7a041ce16e695d51e92145b83f01087",
            "branch": "master"
        },
        {
            "importpath": "github.com/mitchellh/cli",
            "repository": "https://github.com/mitchellh/cli",
            "revision": "8102d0ed5ea2709ade1243798785888175f6e415",
            "branch": "master"
        },
        {
            "importpath": "github.com/olekukonko/tablewriter",
            "repository": "https://github.com/olekukonko/tablewriter",
            "revision": "b9346ac189c55dd419f85c7ad2cd56f810bf19d6",
            "branch": "master"
        },
        {
            "importpath": "github.com/tcnksm/go-gitconfig",
            "repository": "https://github.com/tcnksm/go-gitconfig",
            "revision": "6411ba19847f20afe47f603328d97aaeca6def6f",
            "branch": "master"
        },
        {
            "importpath": "github.com/tcnksm/go-latest",
            "repository": "https://github.com/tcnksm/go-latest",
            "revision": "ef81df8e23895f6e86f9bdfea0576b9c17b9f1f4",
            "branch": "master"
        },
        {
            "importpath": "golang.org/x/crypto/ssh/terminal",
            "repository": "https://go.googlesource.com/crypto",
            "revision": "81bf7719a6b7ce9b665598222362b50122dfc13b",
            "branch": "master",
            "path": "/ssh/terminal"
        },
        {
            "importpath": "golang.org/x/net/html",
            "repository": "https://go.googlesource.com/net",
            "revision": "7654728e381988afd88e58cabfd6363a5ea91810",
            "branch": "master",
            "path": "/html"
        }
    ]
}

换句话说,使用GB创建的开发环境可以将整个文件夹分发和同步给开发团队成员。(当使用Git等工具对使用GB创建的开发环境进行管理时,如果src文件夹中的文件被其他存储库管理,则需要将其作为子模块添加,而不是简单地进行克隆操作)。

gb 的缺点是不能使用 go test。虽然有一个名为 gb test 的选项,几乎与 go test 兼容,但如果采用特殊的文件夹结构,测试结果可能会有所不同。因此,在使用 CI 工具时需要注意。

【解决方案3】使用Go 1.5的vendoring功能

目前正在调查这个问题。如果有好的文章,之后会放上链接。暂时先参考以下内容。

Go 1.5 Release Notes – The Go Programming Language

Go 1.5 Vendor Experiment

补充:

我写了一篇文章:关于内部软件包和供应商签约。

那么,我应该把什么放入GOPATH呢?

最好使用go get来引入诸如godoc和golint等标准工具以及项目成员之间共同使用的工具。

C:>go get -v golang.org/x/tools/cmd/godoc
golang.org/x/tools/blog/atom
golang.org/x/tools/present
golang.org/x/tools/go/ast/astutil
golang.org/x/tools/go/types/typeutil
golang.org/x/tools/go/buildutil
golang.org/x/tools/container/intsets
golang.org/x/tools/blog
golang.org/x/tools/go/ssa
golang.org/x/tools/go/loader
golang.org/x/tools/godoc/vfs
golang.org/x/tools/godoc/redirect
golang.org/x/tools/godoc/static
golang.org/x/tools/go/callgraph
golang.org/x/tools/go/ssa/ssautil
golang.org/x/tools/godoc/util
golang.org/x/tools/godoc/vfs/httpfs
golang.org/x/tools/godoc/vfs/gatefs
golang.org/x/tools/go/pointer
golang.org/x/tools/godoc/vfs/mapfs
golang.org/x/tools/godoc/vfs/zipfs
golang.org/x/tools/playground
golang.org/x/tools/godoc/analysis
golang.org/x/tools/godoc
golang.org/x/tools/cmd/godoc

C:>go get -v golang.org/x/tools/cmd/vet
Fetching https://golang.org/x/tools/cmd/vet?go-get=1
Parsing meta tags from https://golang.org/x/tools/cmd/vet?go-get=1 (status code 200)
get "golang.org/x/tools/cmd/vet": found meta tag main.metaImport{Prefix:"golang.org/x/tools", VCS:"git", RepoRoot:"https://go.googlesource.com/tools"} at https://golang.org/x/tools/cmd/vet?go-get=1
get "golang.org/x/tools/cmd/vet": verifying non-authoritative meta tag
Fetching https://golang.org/x/tools?go-get=1
Parsing meta tags from https://golang.org/x/tools?go-get=1 (status code 200)
golang.org/x/tools (download)
golang.org/x/tools/go/exact
golang.org/x/tools/cmd/vet/whitelist
golang.org/x/tools/go/types
golang.org/x/tools/go/gcimporter
golang.org/x/tools/cmd/vet

C:>go get -v github.com/golang/lint/golint
github.com/golang/lint (download)
github.com/golang/lint
github.com/golang/lint/golint

C:>go get -v github.com/jteeuwen/go-bindata/...
github.com/jteeuwen/go-bindata (download)
github.com/jteeuwen/go-bindata
github.com/jteeuwen/go-bindata/go-bindata

如果您使用Windows作为开发平台,那么最好指定一个工具管理负责人,并使用该负责人分发的二进制文件,这样可能更安全。如果您是独自工作,那么就无所谓了。但实际上,我认为独自开发业务系统的情况并不常见,所以重要的是在团队成员之间进行调整,以确保他们使用相同的环境。

让我们愉快地工作吧。

书签

    Go言語のDependency/Vendoringの問題と今後.gbあるいはGo1.5 | SOTA