英文:
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
的子目录src
、pkg
和bin
中查找。例如,
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.Reader
和io.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
andio.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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论