文章目錄
  1. 1. 简介
  2. 2. 代码组织
    1. 2.1. overview
    2. 2.2. workspace
  3. 3. GOPATH
  4. 4. import path
  5. 5. 第一个程序
  6. 6. 第一个库
  7. 7. package name
  8. 8. Testing
  9. 9. remote package
  10. 10. 更多

简介

本文演示一个简单的Go包开发过程,并介绍`go tool—-获取,build,安装Go包的标准方式。

go tool要求以特定的方式组织代码,请仔细阅读本文,以便以最简单的方式运行安装你的代码。

代码组织

overview

  • 一般将所有代码保存在单个workspace
  • 单个workspace包含多个git仓库
  • 每个git仓库包含一个或多个package
  • 每个package包含一个或多个源文件
  • pacage的目录决定其import path

注意这和其它每个工程有单独的workspace、每个workspace都与git仓库关联的编程环境是不太相同的。

workspace

worksapce一个包含以下三个目录的目录结构。

  • src 源文件
  • pk 包目标文件
  • bin 可执行文件

go toolbuild源文package包到pkg目录,install目标二进制文件到Bin目录。

典型的src目录下包含多个git仓库,以跟踪每个package的开发。

以下例子是一个包含两个package的workspace :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
bin/
hello # command executable
outyet # command executable
pkg/
linux_amd64/
github.com/golang/example/
stringutil.a # package object
src/
github.com/golang/example/
.git/ # Git repository metadata
hello/
hello.go # command source
outyet/
main.go # command source
main_test.go # test source
stringutil/
reverse.go # package source
reverse_test.go # test source
golang.org/x/image/
.git/ # Git repository metadata
bmp/
reader.go # package source
writer.go # package source
... (many more repositories and packages omitted) ...

一个典型的工作空间包含很多源文件仓库,每个仓库有多个package和command。大多数Go程序员将所有源代码与依赖保存在单个workspace下。

command和library从不同的package中build来,我们将稍后讨论。

GOPATH

GOPATH指示工作空间,它可能是你唯一需要指定的环境变量。

接下来,我们将创建工作空间并设置对应的GOPATH。它可以是任意目录,我们使用$HOME/work为例,注意它不能与Go安装目录相同。

1
2
$ mkdir $HOME/work
$ export GOPATH=$HOME/work

方便起见,将工作空间的bin目录加入path

1
$ export PATH=$PATH:$GOPATH/bin

更多内容参见go help gopath

import path

import path 是一个唯一标识package的字符串。package的import path对应它在工作空间或者远程仓库中的位置。

标准库中的包有短Improt path如”fmt” 和 “net/http”, 自己的包则必须选择一个不太可能与将来可能添加的标准库或者外部库冲突的基础目录。

如果你将代码放置在某个源代码库下,那么你应该用那个源代码库的root作为你的基础目录。例如你有github帐号”github.com/user”,那么它应该作为你的基础目录。

编译Go程序并不要求你一定将代码发布到远程仓库,它只是希望你能以一种好的习惯来组织代码。实际上你可以任意选择path name,只要它不同于标准库或者大Go生态冲突。

我们使用github.com/work作为基础目录,在workspace中创建此目录以存储代码。

1
$ mkdir -p $GOPATH/src/github.com/user

第一个程序

选择一个package名字并在基础目录下创建同名目录。

1
$ mkdir $GOPATH/src/github.com/user/hello

在hello目录下创建一个hello.go文件,内容如下

1
2
3
4
5
6
7
package main

import "fmt"

func main() {
fmt.Printf("Hello, world.\n")
}

现在你可以用go tool编译和安装该程序

1
$ go install github.com/user/hello

你可以在任意地方运行以上命令,go tool会在GOPATH指定的workspace下寻找github.com/user/hello包。

如果当前在package所在目录,那么package path可以省略

1
2
$ cd $GOPATH/src/github.com/user/hello
$ go install

以上命令编译生成可执行的hello二进制命令将其安装到工作空间下的bin目录,在本例中是 $HOME/work/bin/hello

go tool只在错误时输出信息,如果没有输出,则安装成功。

现在你可以直接输入hello来运行命令。

1
2
$ hello
Hello, world.

如果你使用git,那么现在是建仓的好时候。当然,这也是可选的。

1
2
3
4
5
6
7
8
$ cd $GOPATH/src/github.com/user/hello
$ git init
Initialized empty Git repository in /home/user/work/src/github.com/user/hello/.git/
$ git add hello.go
$ git commit -m "initial commit"
[master (root-commit) 0b4507d] initial commit
1 file changed, 1 insertion(+)
create mode 100644 hello.go

第一个库

我们将编写一个库并在hello程序中使用。

同样第一步是选择package path并建立目录

1
$ mkdir $GOPATH/src/github.com/user/stringutil

接下来创建reverse.go文件并添加以下内容

1
2
3
4
5
6
7
8
9
10
11
// Package stringutil contains utility functions for working with strings.
package stringutil

// Reverse returns its argument string reversed rune-wise left to right.
func Reverse(s string) string {
r := []rune(s)
for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
r[i], r[j] = r[j], r[i]
}
return string(r)
}

测试编译package

1
$ go build github.com/user/stringutil

这不会生成输出文件,如果需要,使用go install,它会在在pkg目录下生成package的目标文件。

确认编译通过后,修改hello.go

1
2
3
4
5
6
7
8
9
10
package main

import (
"fmt"
"github.com/user/stringutil"
)

func main() {
fmt.Printf(stringutil.Reverse("!oG ,olleH"))
}

注意import中的变化。

当go tool安装包或者二进制时,它也安装所有的依赖,所以当你

1
$ go install github.com/user/hello

stringutil包也会自动安装。

运行更新后的程序

1
2
$ hello
Hello, Go!

现在目录结构应该是这样的。

1
2
3
4
5
6
7
8
9
10
11
12
bin/
hello # command executable
pkg/
linux_amd64/ # this will reflect your OS and architecture
github.com/user/
stringutil.a # package object
src/
github.com/user/
hello/
hello.go # command source
stringutil/
reverse.go # package source

请注意go install将stirngutil包安装到了pkg的子目录linux_amd64下的对应目录中,目标文件为stringutil.a,这便于go tool接下来寻找,避免不必要的重复编译。linux_amd64有助于cross-compilation,指示了系统OS和架构。

Go可执行文件是静态链接的,程序运行并不需要package目录文件。

package name

go程序的第一句代码必须是

1
package name

name是包的默认import名,包中的所有文件使用相同的name. Go的惯例是name与目录名最后一级相同,例如import path是”crypto/rot13”的包,其package name为“rot13”.

可执行命令必须使用”package main”.

更多详细信息可参见Effective Go

Testing

Go有一个轻量级的测试框架,由go test 命令和testing包组成。

测试文件名以”_test.go”结尾,其中包含以”Testxxx”命名,签名为func (t *testing.T)的函数。test框架会运行每一个函数,如果函数调用了t.Error 或 t.Fail类似方法,则认为测试失败。

添加$GOPATH/src/github.com/user/stringutil/reverse_test.go,内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package stringutil

import "testing"

func TestReverse(t *testing.T) {
cases := []struct {
in, want string
}{
{"Hello, world", "dlrow ,olleH"},
{"Hello, 世界", "界世 ,olleH"},
{"", ""},
}
for _, c := range cases {
got := Reverse(c.in)
if got != c.want {
t.Errorf("Reverse(%q) == %q, want %q", c.in, got, c.want)
}
}
}

然后运行

1
2
$ go test github.com/user/stringutil
ok github.com/user/stringutil 0.165s

一如继往地,如果在package所有目录运行,可忽略path

1
2
$ go test
ok github.com/user/stringutil 0.165s

更多信息可参见 go help test testing package documentation

remote package

通过import path就能得知如何使用git/Mercurial等工具获得源代码。本例已经放置在github.com/golang/example.通过go get可以自动获取、编译和安装代码。

1
2
3
$ go get github.com/golang/example/hello
$ $GOPATH/bin/hello
Hello, Go examples!

如果指定的package不在工作区下,go get会将获取并置于GOPATH指定的第一个工作空间。

以上命名惯例保证了代码便于被其它人使用go wikigodoc.org提供外部Go项目的列表。

更多使用远程仓库的信息可参见go help importpath.

更多

Effective Go

Documentation page

文章目錄
  1. 1. 简介
  2. 2. 代码组织
    1. 2.1. overview
    2. 2.2. workspace
  3. 3. GOPATH
  4. 4. import path
  5. 5. 第一个程序
  6. 6. 第一个库
  7. 7. package name
  8. 8. Testing
  9. 9. remote package
  10. 10. 更多