文章目录
- 开发环境与工具
- 编辑器、集成开发环境
- 调试器
- 构建并运行 Go 程序
- 格式化代码
- 生成代码文档
- 其它工具
- 与其它语言进行交互
- 与 C 进行交互
- 与 C++ 进行交互
开发环境与工具
https://github.com/avelino/awesome-go
因为 Go 语言还是一门相对年轻的编程语言,所以不管是在集成开发环境(IDE)还是相关的插件方面,发展都不是很成熟。不过目前还是有一些 IDE 能够较好地支持 Go 的开发,有些开发工具甚至是跨平台的,你可以在 Linux、Mac OS X 或者 Windows 下工作。
你可以通过查阅 编辑器和 IDE 扩展 页面来获取 Go 开发工具的最新信息。
编辑器、集成开发环境
你可以将 Linux 的文本编辑器 GEdit 改造成一个很好的 Go 开发工具,详见页面:http://gohelp.wordpress.com/。
Sublime Text 是一个革命性的跨平台(Linux、Mac OS X、Windows)文本编辑器,它支持编写非常多的编程语言代码。对于 Go 而言,它有一个插件叫做 GoSublime 来支持代码补全和代码模版。
这里还有一些更加高级的 Go 开发工具,其中一些是以插件的形式利用本身是作为开发 Java 的工具。
如果你对集成开发环境都不是很熟悉,那就使用 LiteIDE 吧,另外使用 GoClipse 或者 IntelliJ Idea Plugin 也是不错的选择。
代码补全 一般都是通过内置 GoCode 实现的(如:LieteIDE、GoClipse),如果需要手动安装 GoCode,在命令行输入指令 go get -u github.com/nsf/gocode
即可(务必事先配置好 Go 环境变量)
。
调试器
应用程序的开发过程中调试是必不可少的一个环节,因此有一个好的调试器是非常重要的,可惜的是,Go 在这方面的发展还不是很完善。目前可用的调试器是 gdb,最新版均以内置在集成开发环境 LiteIDE 和 GoClipse 中,但是该调试器的调试方式并不灵活且操作难度较大。
如果你不想使用调试器,你可以按照下面的一些有用的方法来达到基本调试的目的:
- 在合适的位置使用打印语句输出相关变量的值(
print
/println
和fmt.Print
/fmt.Println
/fmt.Printf
)。 - 在
fmt.Printf
中使用下面的说明符来打印有关变量的相关信息:
-
%+v
打印包括字段在内的实例的完整信息 -
%#v
打印包括字段和限定类型名称在内的实例的完整信息 -
%T
打印某个类型的完整说明
- 使用 panic 语句(第 13.2 节)来获取栈跟踪信息(直到 panic 时所有被调用函数的列表)。
- 使用关键字 defer 来跟踪代码执行过程。
构建并运行 Go 程序
在大多数 IDE 中,每次构建程序之前都会自动调用源码格式化工具 gofmt
并保存格式化后的源文件。如果构建成功则不会输出任何信息,而当发生编译时错误时,则会指明源码中具体第几行出现了什么错误,如:a declared and not used
。一般情况下,你可以双击 IDE 中的错误信息直接跳转到发生错误的那一行。
如果程序执行一切顺利并成功退出后,将会在控制台输出 Program exited with code 0
。
从 Go 1 版本开始,使用 Go 自带的更加方便的工具来构建应用程序:
-
go build
编译自身包和依赖包 -
go install
编译并安装自身包和依赖包
格式化代码
Go 开发团队不想要 Go 语言像许多其它语言那样总是在为代码风格而引发无休止的争论,浪费大量宝贵的开发时间,因此他们制作了一个工具:go fmt
(gofmt
)。这个工具可以将你的源代码格式化成符合官方统一标准的风格,属于语法风格层面上的小型重构。遵循统一的代码风格是 Go 开发中无可撼动的铁律,因此你必须在编译或提交版本管理系统之前使用 gofmt
来格式化你的代码。
尽管这种做法也存在一些争论,但使用 gofmt
后你不再需要自成一套代码风格而是和所有人使用相同的规则。这不仅增强了代码的可读性,而且在接手外部 Go 项目时,可以更快地了解其代码的含义。此外,大多数开发工具也都内置了这一功能。
Go 对于代码的缩进层级方面使用 tab 还是空格并没有强制规定,一个 tab 可以代表 4 个或 8 个空格。在实际开发中,1 个 tab 应该代表 4 个空格,而在本身的例子当中,每个 tab 代表 8 个空格。至于开发工具方面,一般都是直接使用 tab 而不替换成空格。
在命令行输入 gofmt –w program.go
会格式化该源文件的代码然后将格式化后的代码覆盖原始内容(如果不加参数 -w
则只会打印格式化后的结果而不重写文件);gofmt -w *.go
会格式化并重写所有 Go 源文件;gofmt map1
会格式化并重写 map1 目录及其子目录下的所有 Go 源文件。
gofmt
也可以通过在参数 -r
后面加入用双引号括起来的替换规则实现代码的简单重构,规则的格式:<原始内容> -> <替换内容>
。
实例:
gofmt -r '(a) -> a' –w *.go
上面的代码会将源文件中没有意义的括号去掉。
gofmt -r 'a[n:len(a)] -> a[n:]' –w *.go
上面的代码会将源文件中多余的 len(a)
去掉。( 译者注:了解切片(slice)之后就明白这为什么是多余的了 )
gofmt –r 'A.Func1(a,b) -> A.Func2(b,a)' –w *.go
上面的代码会将源文件中符合条件的函数的参数调换位置。
如果想要了解有关 gofmt
的更多信息,请访问该页面:http://golang.org/cmd/gofmt/。
生成代码文档
go doc
工具会从 Go 程序和包文件中提取顶级声明的首行注释以及每个对象的相关注释,并生成相关文档。
它也可以作为一个提供在线文档浏览的 web 服务器,http://golang.org 就是通过这种形式实现的。
一般用法
-
go doc package
获取包的文档注释,例如:go doc fmt
会显示使用godoc
生成的fmt
包的文档注释。 -
go doc package/subpackage
获取子包的文档注释,例如:go doc container/list
。 -
go doc package function
获取某个函数在某个包中的文档注释,例如:go doc fmt Printf
会显示有关fmt.Printf()
的使用说明。
这个工具只能获取在 Go 安装目录下 ../go/src
中的注释内容。此外,它还可以作为一个本地文档浏览 web 服务器。在命令行输入 godoc -http=:6060
,然后使用浏览器打开 http://localhost:6060 后,你就可以看到本地文档浏览服务器提供的页面。
其它工具
Go 自带的工具集主要使用脚本和 Go 语言自身编写的,目前版本的 Go 实现了以下工具:
go install
是安装 Go 包的工具,类似 Ruby 中的 rubygems。主要用于安装非标准库的包文件,将源代码编译成对象文件。go clean
是用来移除当前源码包里面编译生成的文件,这些文件包括:
- _obj/ 旧的object目录,由Makefiles遗留
- _test/ 旧的test目录,由Makefiles遗留
- _testmain.go 旧的gotest文件,由Makefiles遗留
- test.out 旧的test记录,由Makefiles遗留
- build.out 旧的test记录,由Makefiles遗留
- *.[568ao] object文件,由Makefiles遗留
- DIR(.exe) 由 go build 产生
- DIR.test(.exe) 由 go test -c 产生
- MAINFILE(.exe) 由 go build MAINFILE.go产生
go get
主要是用来动态获取远程代码包的,目前支持的有BitBucket、GitHub、Google Code和Launchpad。这个命令在内部实际上分成了两步操作:第一步是下载源码包,第二步是执行go install。所以为了go get 能正常工作,你必须确保安装了合适的源码管理工具,并同时把这些命令加入你的PATH中。其实go get支持自定义域名的功能,具体参见go help remote。go get 命令本质上可以理解为:首先通过源码工具clone代码到src目录,然后执行go install。go fix
用于将你的 Go 代码从旧的发行版迁移到最新的发行版,它主要负责简单的、重复的、枯燥无味的修改工作,如果像 API 等复杂的函数修改,工具则会给出文件名和代码行数的提示以便让开发人员快速定位并升级代码。Go 开发团队一般也使用这个工具升级 Go 内置工具以及 谷歌内部项目的代码。go fix
之所以能够正常工作是因为 Go 在标准库就提供生成抽象语法树和通过抽象语法树对代码进行还原的功能。该工具会尝试更新当前目录下的所有 Go 源文件,并在完成代码更新后在控制台输出相关的文件名称。go pprof
可以帮助开发者快速分析及定位各种性能问题,如 CPU 消耗、内存分配及阻塞分析。性能分析首先需要使用 runtime.pprof 包嵌入到待分析程序的入口和结束处。runtime.pprof 包在运行时对程序进行每秒 100 次的采样,最少采样 1 秒。然后将生成的数据输出,让开发者写入文件或者其他媒介上进行分析。go pprof 工具链配合 Graphviz 图形化工具可以将 runtime.pprof 包生成的数据转换为 PDF 格式,以图片的方式展示程序的性能分析结果。go test
是一个轻量级的单元测试框架。go version
查看go当前的版本go env
查看当前go的环境变量go list
列出当前全部安装的packagego run
编译并运行Go程序
与其它语言进行交互
与 C 进行交互
工具 cgo 提供了对 FFI(外部函数接口)的支持,能够使用 Go 代码安全地调用 C 语言库,你可以访问 cgo 文档主页:http://golang.org/cmd/cgo。cgo 会替代 Go 编译器来产生可以组合在同一个包中的 Go 和 C 代码。在实际开发中一般使用 cgo 创建单独的 C 代码包。
如果你想要在你的 Go 程序中使用 cgo,则必须在单独的一行使用 import "C"
来导入,一般来说你可能还需要 import "unsafe"
。
然后,你可以在 import "C"
之前使用注释(单行或多行注释均可)的形式导入 C 语言库(甚至有效的 C 语言代码),它们之间没有空行,例如:
// #include <stdio.h>
// #include <stdlib.h>
import "C"
名称 “C” 并不属于标准库的一部分,这只是 cgo 集成的一个特殊名称用于引用 C 的命名空间。在这个命名空间里所包含的 C 类型都可以被使用,例如 C.uint
、C.long
等等,还有 libc 中的函数 C.random()
等也可以被调用。
当你想要使用某个类型作为 C 中函数的参数时,必须将其转换为 C 中的类型,反之亦然,例如:
var i int
C.uint(i) // 从 Go 中的 int 转换为 C 中的无符号 int
int(C.random()) // 从 C 中 random() 函数返回的 long 转换为 Go 中的 int
下面的 2 个 Go 函数 Random()
和 Seed()
分别调用了 C 中的 C.random()
和 C.srandom()
。
示例 3.2
package rand
// #include <stdlib.h>
import "C"
func Random() int {
return int(C.random())
}
func Seed(i int) {
C.srandom(C.uint(i))
}
C 当中并没有明确的字符串类型,如果你想要将一个 string 类型的变量从 Go 转换到 C 时,可以使用 C.CString(s)
;同样,可以使用 C.GoString(cs)
从 C 转换到 Go 中的 string 类型。
Go 的内存管理机制无法管理通过 C 代码分配的内存。
开发人员需要通过手动调用 C.free
来释放变量的内存:
defer C.free(unsafe.Pointer(Cvariable))
这一行最好紧跟在使用 C 代码创建某个变量之后,这样就不会忘记释放内存了。下面的代码展示了如何使用 cgo 创建变量、使用并释放其内存:
示例 3.3
package print
// #include <stdio.h>
// #include <stdlib.h>
import "C"
import "unsafe"
func Print(s string) {
cs := C.CString(s)
defer C.free(unsafe.Pointer(cs))
C.fputs(cs, (*C.FILE)(C.stdout))
}
构建 cgo 包
你可以在使用将会在第 9.5 节讲到的 Makefile 文件(因为我们使用了一个独立的包),除了使用变量 GOFILES 之外,还需要使用变量 CGOFILES 来列出需要使用 cgo 编译的文件列表。例如,示例 3.2 中的代码就可以使用包含以下内容的 Makefile 文件来编译,你可以使用 gomake 或 make:
include $(GOROOT)/src/Make.inc
TARG=rand
CGOFILES=\
c1.go\
include $(GOROOT)/src/Make.pkg
与 C++ 进行交互
SWIG(简化封装器和接口生成器)支持在 Linux 系统下使用 Go 代码调用 C 或者 C++ 代码。这里有一些使用 SWIG 的注意事项:
- 编写需要封装的库的 SWIG 接口。
- SWIG 会产生 C 的存根函数。
- 这些库可以使用 cgo 来调用。
- 相关的 Go 文件也可以被自动生成。
这类接口支持方法重载、多重继承以及使用 Go 代码实现 C++ 的抽象类。
目前使用 SWIG 存在的一个问题是它无法支持所有的 C++ 库,比如说它就无法解析 TObject.h。