|
| 1 | +如何编写Go代码 |
| 2 | +======================== |
| 3 | + |
| 4 | +简介:介绍如何使用go命令获取、构建和安装包,命令和运行测试。 |
| 5 | + |
| 6 | +## 概述 |
| 7 | + |
| 8 | +> 目录 |
| 9 | + |
| 10 | +* 介绍 |
| 11 | +* 代码组织 |
| 12 | + * 概观 |
| 13 | + * 工作区 |
| 14 | + * GOPATH环境变量 |
| 15 | + * 导入路径 |
| 16 | + * 你的第一个程序 |
| 17 | + * 你的第一个库 |
| 18 | +* 包名称 |
| 19 | +* 测试 |
| 20 | +* 远程软件包 |
| 21 | + |
| 22 | +> 介绍 |
| 23 | + |
| 24 | +本文档演示了一个简单Go包的开发过程,并介绍了go工具,这是获取、构建和安装Go包和命令的标准方法。 |
| 25 | + |
| 26 | +该go工具要求您以特定方式组织您的代码。 |
| 27 | + |
| 28 | +## 代码组织 |
| 29 | + |
| 30 | +> 概观 |
| 31 | + |
| 32 | +请注意,这与其他编程环境不同,在这些编程环境中,每个项目都有一个单独的工作区,工作区与版本控制存储库紧密相关。 |
| 33 | + |
| 34 | +* Go程序员通常将他们所有的Go代码保存在一个工作区中。 |
| 35 | +* 工作区包含许多版本控制存储库 (例如,由Git管理)。 |
| 36 | +* 每个存储库都包含一个或多个包。 |
| 37 | +* 每个软件包由一个目录中的一个或多个Go源文件组成。 |
| 38 | +* 包的目录的路径决定了它的导入路径。 |
| 39 | + |
| 40 | +> 工作区 |
| 41 | + |
| 42 | +工作空间是一个目录层次结构,其根目录包含三个目录: |
| 43 | + |
| 44 | +* src 包含Go源文件, |
| 45 | +* pkg 包含包对象 |
| 46 | +* bin 包含可执行命令。 |
| 47 | + |
| 48 | +该go工具构建源包并将生成的二进制文件安装到pkg和bin目录。 |
| 49 | + |
| 50 | +该`src`子目录通常包含多个版本控制存储库(例如Git或Mercurial),用于跟踪一个或多个源包的开发。 |
| 51 | + |
| 52 | +为了让您了解工作空间在实践中的外观,下面是一个例子: |
| 53 | + |
| 54 | +```golang |
| 55 | +bin/ |
| 56 | + hello # 命令可执行文件 |
| 57 | + outyet # 命令可执行文件 |
| 58 | +pkg/ |
| 59 | + linux_amd64/ |
| 60 | + github.com/golang/example/ |
| 61 | + stringutil.a # 包对象 |
| 62 | +src/ |
| 63 | + github.com/golang/example/ |
| 64 | + .git/ # Git存储库元数据 |
| 65 | + hello/ |
| 66 | + hello.go # 命令源 |
| 67 | + outyet/ |
| 68 | + main.go # command source |
| 69 | + main_test.go # test source |
| 70 | + stringutil/ |
| 71 | + reverse.go # 包源码 |
| 72 | + reverse_test.go # 测试源码 |
| 73 | + golang.org/x/image/ |
| 74 | + .git/ # Git存储库元数据 |
| 75 | + bmp/ |
| 76 | + reader.go # package source |
| 77 | + writer.go # package source |
| 78 | + ... (many more repositories and packages omitted) ... |
| 79 | +``` |
| 80 | + |
| 81 | +上面的树显示了一个包含两个存储库(example和image)的工作空间。该example库包含两个命令(hello 和outyet)和一个库(`stringutil`)。该image存储库包含该bmp包和其他几个包。 |
| 82 | + |
| 83 | +典型的工作空间包含许多包含许多包和命令的源代码库。大多数Go程序员将他们所有的Go源代码和依赖关系保存在一个工作区中。 |
| 84 | + |
| 85 | +命令和库由不同类型的源代码包构建而成。 |
| 86 | + |
| 87 | +> 第一个程序 |
| 88 | + |
| 89 | +要编译并运行一个简单的程序,首先选择一个包路径(我们将使用 `github.com/user/hello`)并在工作区内创建一个相应的包目录: |
| 90 | + |
| 91 | +```bash |
| 92 | +mkdir $GOPATH/src/github.com/user/hello |
| 93 | +``` |
| 94 | + |
| 95 | +接下来,创建一个名为hello.go该目录内的文件,其中包含以下Go代码。 |
| 96 | + |
| 97 | +```golang |
| 98 | +package main |
| 99 | + |
| 100 | +import "fmt" |
| 101 | + |
| 102 | +func main() { |
| 103 | + fmt.Printf("Hello, world.\n") |
| 104 | +} |
| 105 | +``` |
| 106 | + |
| 107 | +现在,您可以使用该go工具构建和安装该程序 |
| 108 | + |
| 109 | +```golang |
| 110 | +$ go install github.com/user/hello // 编译包文件并且编译整个程序 |
| 111 | +``` |
| 112 | + |
| 113 | +请注意,您可以从系统上的任何位置运行此命令。该 go工具通过查找 `github.com/user/hello ` 指定的工作区内的程序包来查找源代码 `GOPATH`。 |
| 114 | + |
| 115 | +如果`go install`从软件包目录运行,也可以省略软件包路径: |
| 116 | + |
| 117 | +```golang |
| 118 | +$ cd $GOPATH/src/github.com/user/hello |
| 119 | +$ go install |
| 120 | +``` |
| 121 | + |
| 122 | +该命令构建`hello`命令,生成可执行的二进制文件。然后它将该二进制文件安装到工作空间的`bin`目录`hello`(或者在Windows下`hello.exe`)。在我们的例子中,那将是`$GOPATH/bin/hello`,这是`$HOME/go/bin/hello`。 |
| 123 | + |
| 124 | +该`go`工具只会在发生错误时打印输出,因此如果这些命令不产生任何输出,它们将成功执行。 |
| 125 | + |
| 126 | +您现在可以通过在命令行键入完整路径来运行该程序: |
| 127 | + |
| 128 | +```bash |
| 129 | +$ $GOPATH/bin/hello |
| 130 | +Hello, world. |
| 131 | +``` |
| 132 | +或者,如您添加`$GOPATH/bin`到您的`PATH`,只需键入二进制名称: |
| 133 | + |
| 134 | +```golang |
| 135 | +$ hello |
| 136 | +Hello, world. |
| 137 | +``` |
| 138 | + |
| 139 | +如果您正在使用源代码管理系统,现在应该是初始化存储库,添加文件并提交第一个更改的好时机。再一次,这一步是可选的:你不需要使用源代码控制来编写Go代码。 |
| 140 | + |
| 141 | +```bash |
| 142 | +$ cd $GOPATH/src/github.com/user/hello |
| 143 | +$ git init |
| 144 | +Initialized empty Git repository in /home/user/work/src/github.com/user/hello/.git/ |
| 145 | +$ git add hello.go |
| 146 | +$ git commit -m "initial commit" |
| 147 | +[master (root-commit) 0b4507d] initial commit |
| 148 | + 1 file changed, 1 insertion(+) |
| 149 | + create mode 100644 hello.go |
| 150 | +$ git remote add origin https://github.com/Tinywan/hello.git |
| 151 | +$ git push -u origin master |
| 152 | +``` |
| 153 | +将代码推送到远程存储库作为自己的练习。 |
| 154 | + |
| 155 | +> 你的第一库 |
| 156 | + |
| 157 | +我们来编写一个库并从`hello`程序中使用它。 |
| 158 | + |
| 159 | +再次,第一步是选择一个包路径(我们将使用 `github.com/user/stringutil`)并创建包目录: |
| 160 | + |
| 161 | +```bash |
| 162 | +$ mkdir $GOPATH/src/github.com/user/stringutil |
| 163 | +``` |
| 164 | + |
| 165 | +接下来,使用以下内容创建一个名称在该目录中的文件。 |
| 166 | + |
| 167 | +```golang |
| 168 | +// Package stringutil contains utility functions for working with strings. |
| 169 | +package stringutil |
| 170 | + |
| 171 | +// Reverse returns its argument string reversed rune-wise left to right. |
| 172 | +func Reverse(s string) string { |
| 173 | + r := []rune(s) |
| 174 | + for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 { |
| 175 | + r[i], r[j] = r[j], r[i] |
| 176 | + } |
| 177 | + return string(r) |
| 178 | +} |
| 179 | +``` |
| 180 | + |
| 181 | +现在,测试该软件包是用下面用`go build`命令进行代码编译: |
| 182 | + |
| 183 | +```bash |
| 184 | +$ go build github.com/user/stringutil // 测试编译,检查编译是否有编译错误 |
| 185 | +``` |
| 186 | + |
| 187 | +或者,如果您在包的源目录中工作,只需: |
| 188 | + |
| 189 | +```bash |
| 190 | +$ go build |
| 191 | +``` |
| 192 | + |
| 193 | +这不会产生输出文件。为此,必须使用`go install`将包对象放置在工作空间的`pkg`目录中的方法。 |
| 194 | + |
| 195 | +确认stringutil软件包构建完成后,修改原始文件`hello.go`(位于 `$GOPATH/src/github.com/user/hello`)以使用它: |
| 196 | + |
| 197 | +```golang |
| 198 | +package main |
| 199 | + |
| 200 | +import ( |
| 201 | + "fmt" |
| 202 | + "github.com/user/stringutil" |
| 203 | +) |
| 204 | + |
| 205 | +func main() { |
| 206 | + fmt.Printf(stringutil.Reverse("!oG ,olleH")) |
| 207 | +} |
| 208 | +``` |
| 209 | + |
| 210 | +无论何时该go工具**安装包或二进制文件**,**它也会安装它所具有的任何依赖关系**。所以当你安装hello程序 |
| 211 | + |
| 212 | +```bash |
| 213 | +$ go install github.com/user/hello |
| 214 | +``` |
| 215 | + |
| 216 | +该`stringutil`包也将自动安装。 |
| 217 | + |
| 218 | +运行该程序的新版本,您应该看到一条新的反转消息: |
| 219 | + |
| 220 | +```bash |
| 221 | +$ hello |
| 222 | +Hello, Go! |
| 223 | +``` |
| 224 | + |
| 225 | +完成上述步骤后,您的工作空间应如下所示: |
| 226 | + |
| 227 | +```bash |
| 228 | +bin/ |
| 229 | + hello # command executable |
| 230 | +pkg/ |
| 231 | + linux_amd64/ # this will reflect your OS and architecture |
| 232 | + github.com/user/ |
| 233 | + stringutil.a # package object |
| 234 | +src/ |
| 235 | + github.com/user/ |
| 236 | + hello/ |
| 237 | + hello.go # command source |
| 238 | + stringutil/ |
| 239 | + reverse.go # package source |
| 240 | +``` |
| 241 | + |
| 242 | +请注意,`go install`将`stringutil.a`对象放置在`pkg/linux_amd64`镜像源目录中的目录中。这样未来的`go`工具调用可以找到包对象并避免不必要地重新编译包。该`linux_amd64`部分有助于交叉编译,并将反映您的系统的操作系统和体系结构。 |
| 243 | + |
| 244 | +**Go命令可执行文件是静态链接的**,包对象不需要存在来运行Go程序。 |
| 245 | + |
| 246 | +> 包名称 |
| 247 | + |
| 248 | +Go源文件中的第一条语句必须是 |
| 249 | + |
| 250 | +```bash |
| 251 | +package name |
| 252 | +``` |
| 253 | + |
| 254 | +这里`name`是对进口的包的默认名称。(包中的所有文件都必须使用相同的文件`name`) |
| 255 | + |
| 256 | +Go的惯例是包名称是导入路径的最后一个元素:`crypto/rot13`应该命名导入为“ ” 的包rot13。 |
| 257 | + |
| 258 | +可执行命令必须始终使用`package main`。 |
| 259 | + |
| 260 | +没有要求软件包名称在链接到一个二进制文件的所有软件包中唯一,**只需要导入路径(它们的完整文件名)是唯一的**。 |
| 261 | + |
| 262 | +请参阅[Effective Go](http://127.0.0.1:8080/doc/effective_go.html#names)详细了解Go的命名约定。 |
| 263 | + |
| 264 | +> 测试 |
| 265 | + |
| 266 | +Go有一个由`go test `命令和`testing`包组成的轻量级测试框架。 |
| 267 | + |
| 268 | +您通过创建一个名称以文件名结尾的文件来编写一个测试`_test.go` ,其中包含以TestXXX签名 命名的函数`func (t *testing.T)`。测试框架运行每个这样的功能; 如果函数调用失败函数(如t.Error或)` t.Fail`,则认为测试失败。 |
| 269 | + |
| 270 | +`stringutil`通过创建`$GOPATH/src/github.com/user/stringutil/reverse_test.go`包含以下Go代码的文件 向测试包 添加测试。 |
| 271 | + |
| 272 | +```golang |
| 273 | +package stringutil |
| 274 | + |
| 275 | +import "testing" |
| 276 | + |
| 277 | +func TestReverse(t *testing.T) { |
| 278 | + cases := []struct { |
| 279 | + in, want string |
| 280 | + }{ |
| 281 | + {"Hello, world", "dlrow ,olleH"}, |
| 282 | + {"Hello, 世界", "界世 ,olleH"}, |
| 283 | + {"", ""}, |
| 284 | + } |
| 285 | + for _, c := range cases { |
| 286 | + got := Reverse(c.in) |
| 287 | + if got != c.want { |
| 288 | + t.Errorf("Reverse(%q) == %q, want %q", c.in, got, c.want) |
| 289 | + } |
| 290 | + } |
| 291 | +} |
| 292 | +``` |
| 293 | + |
| 294 | +然后运行测试`go test`: |
| 295 | + |
| 296 | +```bash |
| 297 | +$ go test github.com/user/stringutil |
| 298 | +ok github.com/user/stringutil 0.165s |
| 299 | +``` |
| 300 | + |
| 301 | +与往常一样,如果您正在go从软件包目录运行该工具,则可以省略软件包路径: |
| 302 | + |
| 303 | +```bash |
| 304 | +$ go test |
| 305 | +ok github.com/user/stringutil 0.165s |
| 306 | +``` |
| 307 | +运行`go help test`并查看 测试包文档以获取更多详细信息。 |
| 308 | + |
| 309 | +> 远程软件包 |
| 310 | + |
| 311 | +导入路径可以描述如何使用Git或Mercurial等版本控制系统获取软件包源代码。该go工具使用此属性从远程存储库自动获取软件包。例如,本文档中描述的示例也保存在GitHub托管的Git存储库中` github.com/golang/example`。如果您在存储库的导入路径中包含存储库URL,` go get`将自动获取,构建和安装它: |
| 312 | + |
| 313 | +```bash |
| 314 | +$ go get github.com/golang/example/hello |
| 315 | +$ $GOPATH/bin/hello |
| 316 | +Hello, Go examples! |
| 317 | +``` |
| 318 | + |
| 319 | +如果指定的软件包不在工作区中,`go get `则将其放入由指定的第一个工作区内`GOPATH`。(如果包已经存在,则`go get`跳过远程抓取并且行为与`go install`相同) |
| 320 | + |
| 321 | +发出上述`go get`命令后,工作空间目录树现在应该如下所示: |
| 322 | + |
| 323 | +```golang |
| 324 | +bin/ |
| 325 | + hello # command executable |
| 326 | +pkg/ |
| 327 | + linux_amd64/ |
| 328 | + github.com/golang/example/ |
| 329 | + stringutil.a # package object |
| 330 | + github.com/user/ |
| 331 | + stringutil.a # package object |
| 332 | +src/ |
| 333 | + github.com/golang/example/ |
| 334 | + .git/ # Git repository metadata |
| 335 | + hello/ |
| 336 | + hello.go # command source |
| 337 | + stringutil/ |
| 338 | + reverse.go # package source |
| 339 | + reverse_test.go # test source |
| 340 | + github.com/user/ |
| 341 | + hello/ |
| 342 | + hello.go # command source |
| 343 | + stringutil/ |
| 344 | + reverse.go # package source |
| 345 | + reverse_test.go # test source |
| 346 | +``` |
| 347 | + |
| 348 | +hello在GitHub上托管的命令取决于`stringutil`同一个存储库中的软件包。在导入`hello.go`文件中使用相同的导入路径约定,所以` go get`命令能够找到并安装相关的包了。 |
| 349 | + |
| 350 | +导入`“github.com/golang/example/stringutil”`这个约定是让你的Go软件包可供其他人使用的最简单的方法。在转到维基 和`godoc.org` 提供外部围棋项目清单。 |
| 351 | + |
| 352 | +有关在go工具中使用远程存储库的更多信息,请参阅 [go help importpath](http://127.0.0.1:8080/cmd/go/#hdr-Remote_import_paths)。 |
0 commit comments