Go 工作空间 深度解析

xiaorenwuzyh · · 9552 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

本文章来自于 谷歌官方的翻译: How to Write Go Code?
http://godoc.golangtc.com/doc/code.html

介绍

这篇文档举例证明了一个简单地 Go package 并且介绍了 go tool,标准的方法来 fetch, build,and install Go package and commands.

如果要使用 go tool,那么就必须将代码组织成一种特殊的形式。 请仔细的阅读这篇文档,它会教你采用最简单的方法来安装运行你的Go程序。

代码组织

Workspace 工作空间

Go tool 被设计成用来开源公共仓库中的代码,尽管或许你不需要发布你的代码, 但是环境的设置还是一样的。

Go的源代码必须存放在 workspace中。 workspace 是一个目录(directory hierachy),在这个目录下有三个子目录

  • src 包含 Go 源代码文件, 源代码文件组织成 packages (one package per directory)
  • pkg 包含 package objects (二进制的包)
  • bin 包含 可执行的命令 command(可执行的二进制文件)

go tool 编译(build) 源代码文件(source packages),并且将 resulting binaries 安装在 pkgbin 目录下面。

src 目录下面包含多个 版本控制的仓库, (比如 Git 或者 Mercurial) 用来 跟踪一个或者多个 source package的开发进度。

下面给你一个真实环境中的 workspace 是什么样子的:

bin/
    hello              # 可执行的命令
    outyet             # 可执行的命令
pkg/
    linux_amd64/
        github/golang/example/
            stringutil.a          # pakcage objects
src/
    github.com/golang/example/
        .git/                     # git repository metadata
        hello/
            hello.go              # command source
        outyet/
            main.go               # command source
            main_test.go          # test source
        stringuitl/
            reverse.go            # package source
            reverse_test.go       # test source

这个工作空间 包含一个 代码仓库repository( example仓库),example 仓库由两个命令 command (hello, outyet)和一个库library组成(stringutil)

一个典型的 workspace 会包含多个 source repositories,包含很多 pakcages 和 commands。

大部分的 Go programmers 会 keep all their Go source code and dependencies in a single workspace. (将所有的Go源代码和依赖保存在一个工作空间中)。

另外 从上述例子中也可以看到, bin/ 中命令的名字,pkg/中库的名字 都是文件夹的文字。 src/中包的名字和你 import 时候的名字不一定一样,这一点要区别。

GOPATH 环境变量

GOPATH 环境变量 指明了 你的工作空间的位置。 它很可能是你在编写Go代码的时候需要唯一设置的环境变量。

开始编程:
1. 创建一个工作空间目录。
2. 设置 GOPATH 环境变量。

GOPATH目录可以是任意的,唯一的要求是不能是你安装Go时的目录(/usr/local/go)。

$ mkdir $HOME/go
$ export GOPATH=$HOME/go

为了方便,将工作空间的 bin 子目录 加入到 你的 PATH 环境变量中。

$ export PATH=$PATH:$GOPATH/bin

Package pathes 包路径

packages path 不是 library path,虽然二者有联系。
标准库中的包,是由短路径(short path)决定的,比如 “fmt”和”net/http”。对于你自己的包,你必须选择一个 base path,这个base path不能和别的包路径冲突。

如果你将你的代码保存在一个 源仓库中(source repository),那么你应该使用 the root of that source repository as your base path.
比如你有一个 GitHub账号: github.com/usr,这 应该是你的 base path.

注意: 你或许不需要发布你的代码 to remote repository before you can build it. 但是将代码组成成 code repository是一个好的习惯。

举例:我们使用 github.com/usr作为你的base path. 在你的workspace中创建一个目录来保存你的代码。

$ mkdir -p $GOPATH/src/github.com/usr

第一个 program

为了编译和运行一个简单地程序,首先选择一个 package path(这里使用 github.com/usr),创建一个 package directory (包目录)

$ mkdir $GOPATH/src/github.com/usr/hello

第二:在包目录下创建一个文件 名为 hello.go,包含如下的代码

package main

import "fmt"

func main() {
    fmt.Println("Hello, World.\n")
}

第三: 使用 go tool 来 build and install 这个程序

$ go install github.com/user/hello

注意: 你可以你的计算机的任何文件夹下面运行这个命令。 go tool 会在 $GOPATH 指定工作空间下面搜索 github.com/usr/hello 这个包,并且编译安装这个包。

当然, 如果你在 package directory 下面运行 go install 你就可以忽略 package path。

$ cd $GOPATH/github.com/usr/hello
$ go install

这个命令会 build the hello command, 产生一个可执行的二进制文件。然后将这个可执行的二进制文件安装到工作目录的 bin目录下面,文件名称为 hello (在Windows下面为 hello.ext,可以看到可执行二进制文件名称和目录的名称保持一致)。 在我们的例子中是: $GOPATH/bin/hello,也就是 $HOME/go/bin/hello

当有错误发生的时候,go tool只会 print output,而不会产生二进制文件。 如果这些命令没有产生 output(输出),就表示他们运行成功了。

现在你可以 通过输入 hello命令的全名 来运行这个命令了

$ $GOPATH/bin/hello
hello, world

或者如果你已经将 $GOPATH/bin 加入到 PATH中:

$ hello
hello, world

如果你使用源代码版本控制系统,现在这是一个好的时机来 initialize a repository, add the files,and commit you first chagne. Again, this step is optional: you do not need to use source control to write Go code.

$ cd $GOPATH/src/github.com/usr/hello
$ git init
Initialized empty Git repository in /home/user/go/src/github.com/user/hello/.git/
$ git add hello.go
$ git commit -m "initial commit"
[master (root-commit) 0b4507d] initial commit
  1 file chagned, 1 insertion(+)
      create mode 100644 hello.go

将代码 Push 到远程的仓库留作读者作为一个联系吧。

第一个 Library

现在我们写一个 Library,并且在 hello 程序中使用这个库。

  1. 首先, 选择一个 package path (我们使用 github.com/user/stringutil),创建这个 package directory
$ mkdir $GOPATH/src/github.com/usr/stringutil
  1. 在这个目录中创建一个文件 reverse.go
// Package stringutil contains utility functions for working with strings.

//Reverse returns its argument string reversed run-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)
}
  1. 测试 这个 pckage compiles with 命令 go build:
$ go build github.com/user/stringutil

或者你在 这个目录下面,只需要

$ go build

即可。

  1. go build 不会产生任何的文件。 我们需要使用 go install 可以 将 package object 放置在 workspace下的 pkg目录下。
$ go install
  1. 为了确认 stringutil 已经建立(build),我们需要修改 hello.go 文件。
package main
import (
    "fmt"
    "github.com/usr/stringutil"
)

func main() {
    fmt.Printf(stirngutil.Reverse("!oG, olleH"))
}
  1. 当 go tool 安装一个 package 或者 一个 binary(可执行二进制),它也会 installs whatever dependencies it has (安装它所依赖的文件). 所以当你 intall hello 程序的时候
$ go install github.com/user/hell

stringutil 也会自动安装。

运行新版本的程序,你会看见如下结果:

$ hello
Hello, Go!

经过了上述的步骤,你的工作空间现在应该是下面的情况:

bin/
    hello                 # command executalbe 可执行命令
pkg/
    linux_amd64/          # this will reflect you OS and architecture
        github.com/usre/
            stringutil.a  # package object
src/
    github.com/user/
        hello/
            hello.go      # command source
        stringutil/
            reverse.go    # package source

可以看到, go installstringutil.a放置在 pkg/linux_amd64的目录下, 这个目录 mirrors its source directory. 这是因为 以后 go tool 会找到 package object,并且会避免重新编译。 linux_amd64是为了平台交叉编译,将会反映你的操作系统和计算机的体系结构。

Go command exectualbes are statically linked。(go command 执行是静态链接的) The package objects need not be present to run Go program.(pakcage object 在Go程序执行的时候是不需要的)。

Package names

在Go源文件中, 第一行语句一定是

package name

其中, name is the package’s default name for imports. (All files in a package must use the same name)

Go的惯例是, package name 是 import path 的最后一个元素: 比如,导入的包是 "crypto/rot13" 那么,它的 package name 就应该是 rot13。 (其实,导入的包名 和 实际的 package name 并不必须要一致。 因为导入的 package 是这个包所在的目录,而这个包所在的目录并不一定和 package name是一致的。但是惯例二者是一样的

可执行的命令 必须 使用 package main

包名 没有必要在 所有的包中(linked to a single binary)是唯一的,之哟啊 import path(它们的 full file name)是唯一的就可以了。

Testing

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

Testing 可以用作单元测试,也可以用来进行压力测试。

如何使用 Testing 写单元测试:
1. 创建一个文件, 这个文件以 _test.go 结束,比如: reverse_test.go
2. 这个文件包含测试函数: TestXXX ,其中XXX的起始字符不可是小写字母[a-z],测试函数签名为: func (t *testing.T)。

Testing 测试框架会 运行每个测试函数,一旦这个测试函数 调用了 failure function, 比如: t.Error 或者 t.Fail,那么这个测试就表明失败了。

举例:
1. 向 stringutil 包中加入一个 测试文件。 也就是创建文件 $GOPATH/src/github.com/user/stringutil/reverse_test.go ,包含的代码如下

package stringutil

import "testing"

func TestReverse(t *testing.T) {
    cases := []struct {
        in, want string
    } {
        {"Hello, World", "dlrow, olleH"}
        {"Hello, Go", "oG, olleH"}
        {"", ""}
    }

    for _, c := range case {
        got := Reverse(c, in)
        if got != c.want {
            t.Errorf("Reverse(%q) == %q, want %q", c.in, got, c.want)
        }
    }
}
  1. 运行测试用例
$ go test github.com/user/stringutil
ok      github.com/user/stringutil    0.165s

As always,如果你在 package directory下面运行 go tool,你就可以忽略 package path。

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

如果你有疑问可以,运行go help test 并且看一下 testing package documentation 了解详情。

Remote packages (远程包安装)

go tool 可以用来自动的 fetch packages from remote repositories。(比如 Git 或者 Mercurial)。

举例, 在 Github github.com/golang/example 上有我们上文写过的代码。 如果你在 package’s import path中 包含这个 repository URL,那么 go get 命令将会 自动的 fetch build and install 这个包。(自动的获取,build,安装这个包)

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

如果这个指定的 package 没有在 workspace中,那么 go get 命令将会 把 它放在 $GOPATH 指定的第一个 workspace中。(如果这个 package 已经存在了,那么 go get将会忽略远程的 fetch,并且执行和 go install一样的操作)

在 issuing go get 命令之后,现在的工作空间应该是这个样子:

bin/
    hello              # 可执行的命令
pkg/
    linux_amd64/
        github/golang/example/
            stringutil.a          # pakcage objects
        github/user/
            stringutil.a          # package objects
src/
    github.com/golang/example/
        .git/                     # git repository metadata
        hello/
            hello.go              # command source
        outyet/
            main.go               # command source
            main_test.go          # test source
        stringuitl/
            reverse.go            # package source
            reverse_test.go       # test source
    github.com/user/
        hello/
            hello.go              # command source
        stringutil/
            reverse.go            # package source
            reverse_test.go       # test source

Github中的 hello 命令 依赖于同一个代码仓库中的 stringutil。hello.go 文件中的 imports 需要使用同样的 import path convention(惯例),这样 go get命令可以 定位并且安装依赖的 package.

import "github.com/golang/example/stringutil"

这个惯例很重要,大家一定要遵守。并且这个惯例 可以让你的包很轻松地被别人使用。

Go Wikigodoc.org提供了一些列的 external Go progects.

What’s next ? (接下来干什么)

订阅 golang-announce邮件列表,这样当有一个新的 go 版本出来的时候,会通知你。

阅读 Effective Go 来写出干净 优美的 Go 代码。

阅读 A Tour of Go 来学习 Go的语法

访问 Documentation Page 上面有一些文章, 来深度了解 Go 语言 和它的工具,以及它的库。

看一下 Go Documentation 上面也有很多不错的资源。

看一下 Go Command 了解一下 Go 的 命令的详细用法。

Go Nuts Go 语言的官方讨论列表。


有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:xiaorenwuzyh

查看原文:Go 工作空间 深度解析

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

9552 次点击  
加入收藏 微博
2 回复  |  直到 2018-06-04 08:53:13
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传