什么时候真正需要将Go源代码放在$GOPATH/src目录中?

huangapple go评论81阅读模式
英文:

When is it really necessary to place Go source code in $GOPATH/src?

问题

看一下这个shell会话,我在其中使用Go构建了一个简单的hello world程序。

$ cd ~/lab/hello/
$ ls
hello.go
$ cat hello.go 
package main

import "fmt"

func main() {
    fmt.Printf("hello, world\n")
}
$ go build
$ ./hello 
hello, world
$ go env
GOARCH="amd64"
GOBIN=""
GOCHAR="6"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH=""
GORACE=""
GOROOT="/usr/lib/go"
GOTOOLDIR="/usr/lib/go/pkg/tool/linux_amd64"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0"
CXX="g++"
CGO_ENABLED="1"
$ lsb_release -a
No LSB modules are available.
Distributor ID:	Debian
Description:	Debian GNU/Linux 8.7 (jessie)
Release:	8.7
Codename:	jessie

这里有一点我不明白。https://golang.org/doc/install#testing 上的教程说我应该将hello.go文件放在~/go/src/hello目录下。但是我没有按照这个做,为什么我的程序还能编译通过呢?如果我的程序以这种方式编译正常,为什么文档说我应该将源代码放在~/go/src$GOPATH/src,而实际上似乎并不重要呢?

是否有一种情况下,将源代码放在$GOPATH/src下是真正必要的?

英文:

Take a look at this shell session, where I build a simple hello world program in Go.

$ cd ~/lab/hello/
$ ls
hello.go
$ cat hello.go 
package main

import "fmt"

func main() {
    fmt.Printf("hello, world\n")
}
$ go build
$ ./hello 
hello, world
$ go env
GOARCH="amd64"
GOBIN=""
GOCHAR="6"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH=""
GORACE=""
GOROOT="/usr/lib/go"
GOTOOLDIR="/usr/lib/go/pkg/tool/linux_amd64"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0"
CXX="g++"
CGO_ENABLED="1"
$ lsb_release -a
No LSB modules are available.
Distributor ID:	Debian
Description:	Debian GNU/Linux 8.7 (jessie)
Release:	8.7
Codename:	jessie

Here is what I don't understand. The tutorial at https://golang.org/doc/install#testing says that I should place my hello.go file at ~/go/src/hello. But I am not following this. How is my program compiling then? If my program is compiling fine in this manner, why does the documentation say that I should keep my source code at ~/go/src or $GOPATH/src when it does not seem to matter?

Is there a scenario where it is really necessary to place the source code at $GOPATH/src?

答案1

得分: 4

标准的Go工具会在$GOPATH的子目录srcpkgbin中查找。例如,

currency.go:

package main

import (
	"fmt"
	"time"

	"golang.org/x/text/currency"
)

func main() {
	t1799, _ := time.Parse("2006-01-02", "1799-01-01")
	for it := currency.Query(currency.Date(t1799)); it.Next(); {
		from := ""
		if t, ok := it.From(); ok {
			from = t.Format("2006-01-01")
		}
		fmt.Printf("%v is used in %v since: %v\n", it.Unit(), it.Region(), from)
	}
}

输出:

$ go build currency.go
currency.go:7:2: 找不到包"golang.org/x/text/currency",在以下任何位置都找不到:
	/home/peter/go/src/golang.org/x/text/currency (来自$GOROOT)
	/home/peter/gopath/src/golang.org/x/text/currency (来自$GOPATH)
$ 

如果我们将缺失的包放在$GOPATH/src中,标准的Go工具就能找到它。

$ go get golang.org/x/text/currency
$ go build currency.go
$ ./currency
GBP is used in GB since: 1694-07-07
GIP is used in GI since: 1713-01-01
USD is used in US since: 1792-01-01
$
英文:

The standard Go tools look in the $GOPATH subdirectories src, pkg, and bin. For example,

currency.go:

package main

import (
	"fmt"
	"time"

	"golang.org/x/text/currency"
)

func main() {
	t1799, _ := time.Parse("2006-01-02", "1799-01-01")
	for it := currency.Query(currency.Date(t1799)); it.Next(); {
		from := ""
		if t, ok := it.From(); ok {
			from = t.Format("2006-01-01")
		}
		fmt.Printf("%v is used in %v since: %v\n", it.Unit(), it.Region(), from)
	}
}

Output:

$ go build currency.go
currency.go:7:2: cannot find package "golang.org/x/text/currency" in any of:
	/home/peter/go/src/golang.org/x/text/currency (from $GOROOT)
	/home/peter/gopath/src/golang.org/x/text/currency (from $GOPATH)
$ 

If we put the missing package in $GOPATH/src, the standard Go tools will find it.

$ go get golang.org/x/text/currency
$ go build currency.go
$ ./currency
GBP is used in GB since: 1694-07-07
GIP is used in GI since: 1713-01-01
USD is used in US since: 1792-01-01
$ 

答案2

得分: 3

当你的代码超过一个package main时,你真的需要将代码放在GOPATH中。当你导入"github.com/me/myapp/mylib"时,Go会在你的GOPATH下查找。像go test这样的工具也是基于GOPATH下的包来工作的,而不是.go文件。

一旦你的代码超过一个文件,将代码放在GOPATH中会变得更加实用。这就像直接调用cc/gcc等编译你的C程序和使用make这样的工具之间的区别。

如果你刚开始并想知道为什么人们首先要将项目分成多个包,原因包括:

  • 项目会不断增长,当代码达到1万行或10万行时,你真的需要进行组织。
  • 项目通常包含可重用的工具,而包可以让其他项目单独import它们。
  • 包可以跟踪和控制哪些代码可以访问其他代码和变量。例如,一个包中的私有名称对其他包是不可访问的,所以你可以在该私有内容上进行操作而不会破坏包外的代码,并且你知道没有外部代码在背后调用私有字段/变量或私有代码。
  • 包可以最小化命名空间冲突,例如你可以有gzip.Readerio.Reader。如果你选择合适的名称,packagename.ThingName可以使代码读起来更自然。
  • 包可以单独重新编译、测试等,这样可以加快编辑/构建/测试的循环速度。
  • 在Go中,包还强制执行关于代码组织的其他规定,比如没有循环依赖(如果A导入B,B不会直接或间接地导入A),以及/foo/internal/下的包只能被/foo/下的包使用。这些约束可以帮助避免一个大型项目变得像纠结的意面一样。

还有其他好处,但这些应该能帮助你理解为什么值得养成这个习惯。最好先在一个大包中编写一段时间的代码,然后根据需要将一些类型、函数等移动到其他包中,这样自然的边界会随着时间的推移变得更加清晰。

英文:

You really need to put your code in a GOPATH once you write more than a package main. When you import "github.com/me/myapp/mylib", Go will look under your GOPATH for that. Tools like go test also work in terms of packages under GOPATH, not .go files.

It also becomes the more practical thing to do as soon as your code is in more than one file. It's like the difference between compiling your C program by directly invoking cc/gcc/etc. and using a tool like make.

If you're starting out and wondering why people want to break up projects into multiple packages in the first place, reasons include:

  • Projects grow, and by 10K or 100K lines you really need to organize.
  • Projects often turn out to contain reusable tools, and packages let other projects import them separately.
  • Packages let you track and control what code has access to what other code and variables. For instance, private names in one package aren't accessible for other packages, so you know you can mess around with that private stuff without breaking code outside the package, and you know that no code outside is futzing with private fields/variables or calling private code behind your back.
  • Packages minimize namespace collisions, i.e., you can have gzip.Reader and io.Reader. If you choose your names right, packagename.ThingName can make code read naturally.
  • Packages can be recompiled, tested, etc. individually, which keeps your edit/build/test cycle faster.
  • In Go specifically, packages enforce some other things about your code organization, like no cyclic dependencies (if A imports B, B doesn't directly or indirectly import A) and that packages under /foo/internal/ only get used by packages under /foo/. Constraints like these can help keep a big project from ending up like tangled spaghetti.

There are other benefits but those should help show why it's worth getting in the habit. It's fine to just charge forward writing things in a big package for a little bit and start breaking up files moving some types, functions, etc. out into other packages as needed; the 'natural' boundaries will start to make more sense over time.

答案3

得分: 2

正如JimB在他的评论中所说,Go的文档已经很清楚地解释了这一点;基本上,GOPATH是一个工作空间,它允许你将所有的项目文件、导入和构建产物保存在一个位置。

对于一个简单的项目来说,这并不是严格必需的,但当你开始导入依赖项并希望管理库时,它变得更加有用。

英文:

As JimB says in his comment, the Go documentation makes this clear; basically, the GOPATH is workspace which allows you to keep all your project files and imports and artifacts in one location.

For a simple project it's not strictly necessary, but when you begin importing dependencies and want to manage libraries it becomes more helpful.

huangapple
  • 本文由 发表于 2017年4月19日 22:26:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/43498585.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定