Go语言开发工具及工程管理

开发工具简介:
安装了GoClipse插件的Eclipse,集成性做得很好;
Vim/Emacs,万能开发工具;
LiteIDE,一款专为Go语言开发的集成开发环境。
Go命令行工具的革命性之处在于彻底消除了工程文件的概念,
完全用目录结构和包名来推导工程结构和构建顺序。

1.4 开发工具选择

Google并没有随着Go  1的发布推出官方的Go集成开发环境(IDE),因此开发者需要自行考虑和选择合适的开发工具。目前比较流行的开发工具如下:

  • 文本编辑工具gedit(Linux)/Notepad++(Windows)/Fraise(Mac OS X);
  • 安装了GoClipse插件的Eclipse,集成性做得很好;
  • Vim/Emacs,万能开发工具;
  • LiteIDE,一款专为Go语言开发的集成开发环境。

由于Go代码的轻巧和模块化特征,其实一般的文本编辑工具就可以胜任Go开发工作。本书的所有代码均使用Linux上的gedit工具完成。

Go社区提供了各种文本编辑器的语法高亮设置方法,这在本书最后一章也有所介绍。

1.5 工程管理

在实际的开发工作中,直接调用编译器进行编译和链接的场景是少而又少,因为在工程中不会简单到只有一个源代码文件,且源文件之间会有相互的依赖关系。如果这样一个文件一个文件逐步编译,那不亚于一场灾难。Go语言的设计者作为行业老将,自然不会忽略这一点。早期Go 语言使用makefile作为临时方案,到了Go 1发布时引入了强大无比的Go命令行工具。

Go命令行工具的革命性之处在于彻底消除了工程文件的概念,完全用目录结构和包名来推导工程结构和构建顺序。针对只有一个源文件的情况讨论工程管理看起来会比较多余,因为这可以直接用go run和go build搞定。下面我们将用一个更接近现实的虚拟项目来展示Go语言的基本工程管理方法。

假设有这样一个场景:我们需要开发一个基于命令行的计算器程序。下面为此程序的基本用法:

$ calc help
USAGE: calc command [arguments] …

The commands are:
sqrt	Square root of a non-negative value. add	Addition of two values.

$ calc sqrt 4 # 开根号
2
$ calc add 1 2 # 加 法
3

我们假设这个工程被分割为两个部分:

  • 可执行程序,名为calc,内部只包含一个calc.go文件;
  • 算法库,名为simplemath,每个command对应于一个同名的go文件,比如add.go。则一个正常的工程目录组织应该如下所示:

在上面的结构里,带尖括号的名字表示其为目录。xxx_test.go表示的是一个对于xxx.go的单元测试,这也是Go工程里的命名规则。

为了让读者能够动手实践,这里我们会列出所有的源代码并以注释的方式解释关键内容,如代码清单1-5至代码清单1-9所示。需要注意的是,本示例主要用于示范工程管理,并不保证代码达到产品级质量。

代码清单1-5 calc.go

//calc.go
package main

import “os”// 用于获得命令行参数os.Args
import “fmt”
import “simplemath”
import “strconv”

var Usage = func() {
    fmt.Println(“USAGE: calc command [arguments] …”)
    fmt.Println(“\nThe commands are:\n\tadd\tAddition of two values.\n\tsqrt\tSquare
    root of a non-negative value.”)
}

func main() {
    args := os.Args
    if args == nil || len(args) < 2 { 
        Usage()
        return
}

switch args[0] {
    case “add”:
        if len(args) != 3 {
            fmt.Println(“USAGE: calc add <integer1><integer2>”)
            return
}

    v1, err1 := strconv.Atoi(args[1]) 
    v2, err2 := strconv.Atoi(args[2]) 
    if err1 != nil || err2 != nil {
        fmt.Println(“USAGE: calc add <integer1><integer2>”)
        return
    }
    ret := simplemath.Add(v1, v2) 
    fmt.Println(“Result: “, ret)

    case “sqrt”:
        if len(args) != 2 {
            fmt.Println(“USAGE: calc sqrt <integer>”)
            return
        }
        v, err := strconv.Atoi(args[1])
        if err != nil {
            fmt.Println(“USAGE: calc sqrt <integer>”)
               return
        }
        ret := simplemath.Sqrt(v) fmt.Println(“Result: “, ret)

    default:
        Usage()
    }
}

代码清单1-6 add.go

// add.go
package simplemath

func Add(a int, b int) int {
    return a + b
}

代码清单1-7 add_test.go

// add_test.go
package simplemath

import “testing”

func TestAdd1(t *testing.T) { 
    r := Add(1, 2)
    if r != 3 {
        t.Errorf(“Add(1, 2) failed. Got %d, expected 3.”, r)
    }
}

代码清单1-8 sqrt.go

// sqrt.go
package simplemath

import “math”

func Sqrt(i int) int {
    v := math.Sqrt(float64(i))
    return int(v)
}

代码清单1-9 sqrt_test.go

// sqrt_test.go

package simplemath

import “testing”

func TestSqrt1(t *testing.T) { 
    v := Sqrt(16)
    if v != 4 {
        t.Errorf(“Sqrt(16) failed. Got %v, expected 4.”, v)
    }
}

为了能够构建这个工程,需要先把这个工程的根目录加入到环境变量GOPATH中。假设calcproj 目录位于~/goyard下,则应编辑~/.bashrc文件,并添加下面这行代码:

export GOPATH=~/goyard/calcproj

然后执行以下命令应用该设置:

$ source ~/.bashrc

GOPATH和PATH环境变量一样,也可以接受多个路径,并且路径和路径之间用冒号分割。设置完GOPATH后,现在我们开始构建工程。假设我们希望把生成的可执行文件放到

calcproj/bin目录中,需要执行的一系列指令如下:

$ cd ~/goyard/calcproj

$ mkdir bin

$ cd bin

$ go build calc

顺利的话,将在该目录下发现生成的一个叫做calc的可执行文件,执行该文件以查看帮助信息并进行算术运算:

$ ./calc

USAGE: calc command [arguments] …

The commands are: addAddition of two values.

sqrtSquare root of a non-negative value.

$ ./calc add 2 3

Result: 5

$ ./calc sqrt 9 Result: 3

从上面的构建过程中可以看到,真正的构建命令就一句:

go build calc

这就是为什么说Go命令行工具是非常强大的。我们不需要写makefile,因为这个工具会替我们分析,知道目标代码的编译结果应该是一个包还是一个可执行文件,并分析import语句以了解包的依赖关系,从而在编译calc.go之前先把依赖的simplemath编译打包好。Go命令行程序制定的目录结构规则让代码管理变得非常简单。

另外,我们在写simplemath包时,为每一个关键的函数编写了对应的单元测试代码,分别位于add_test.go和sqrt_test.go中。那么我们到底怎么运行这些单元测试呢?这也非常简单。因为已经设置了GOPATH,所以可以在任意目录下执行以下命令:

$ go test simplemath ok simplemath0.014s

可以看到,运行结果列出了测试的内容、测试结果和测试时间。如果我故意把add_test.go的代码改成这样的错误场景:

func TestAdd1(t *testing.T) { 
    r := Add(1, 2)
    if r != 2 { // 这里本该是3,故意改成2测试错误场景
        t.Errorf(“Add(1, 2) failed. Got %d, expected 3.”, r)
    }
}

然后我们再次执行单元测试,将得到如下的结果:

$ go test simplemath

— FAIL: TestAdd1 (0.00 seconds)

add_test.go:8: Add(1, 2) failed. Got 3, expected 3. FAIL

FAILsimplemath0.013s

打印的错误信息非常简洁,却已经足够让开发者快速定位到问题代码所在的文件和行数,从而在最短的时间内确认是单元测试的问题还是程序的问题。

  1. 6问题追踪和调试

Go语言所提供的是尽量简单的语法和尽量完善的库,以尽可能降低问题的发生概率。当然, 问题还是会发生的,这时需要用到问题追踪和调试技能。这里我们简单介绍下两个最常规的问题跟踪方法:打印日志和使用GDB进行逐步调试。

  1. 6.1 打印日志

Go语言包中包含一个fmt包,其中提供了大量易用的打印函数,我们会接触到的主要是

Printf()和Println()。这两个函数可以满足我们的基本调试需求,比如临时打印某个变量。这两个函数的参数非常类似于C语言运行库中的Printf(),有C语言开发经验的同学会很容易上手。下面是几个使用Printf()和Println()的例子:

fval := 110.48

ival := 200

sval := “This is a string. ” fmt.Println(“The value of fval is”, fval)

fmt.Printf(“fval=%f, ival=%d, sval=%s\n”, fval, ival, sval) fmt.Printf(“fval=%v, ival=%v, sval=%v\n”, fval, ival, sval)

输出结果为:

The value of fval is 100.48

fval=100.48, ival=200, sval=This is a string. fval=100.48, ival=200, sval=This is a string.

fmt包的这一系列格式化打印函数使用起来非常方便,但在正式开始用Go开发服务器系统时,我们就不能只依赖fmt包了,而是需要设计严格的日志规范。Go语言的log包提供了基础的日志功能。如果有需要,你也可以引入自己的log模块。

1.6.2 GDB调试

不用设置什么编译选项,Go语言编译的二进制程序直接支持GDB调试,比如之前用go build

calc编译出来的可执行文件calc,就可以直接用以下命令以调试模式运行:

$ gdb calc

因为GDB的标准用法与Go没有特别关联,这里就不详细展开了,有兴趣的读者可以自行查看对应的文档。需要注意的是,Go编译器生成的调试信息格式为DWARFv3,只要版本高于7.1 的GDB应该都支持它。

1.7 如何寻求帮助

Go语言已经发展了两年时间,凭借着语言本身的优越品质和Google的强大号召力,在推出正式版本之前就已经拥有了广大的爱好者和社区,本节就介绍一些不错的Go语言社区。在遇到问题时,请随时访问这些社区,并勇敢地提问,相信你能得到满意的解决方法。

  1. 7.1 邮件列表

邮件列表是Go语言最活跃的社区之一,而且与其他语言社区不同的是,在这里你可以很频繁地看到好多Go语言的核心开发成员(比如Ross Cox)亲自回答问题,其权威程度和对学习Go 语言的价值显而易见。

Go邮件组的地址为http://groups.google.com/group/golang-nuts 。该邮件列表对所有人公开,你可以在这个页面上直接加入。该邮件列表的沟通语言为英语。根据我们的经验,在该邮件列表上提出的问题通常在24小时内可以得到解决。

Go的中文邮件组为http://groups.google.com/group/golang-china。如果你更习惯中文讨论环境, 可以参与。另外,尽管http://groups.google.com/group/ecug不是以Go语言为专题,但有关Go语言的服务端开发,也是它最重要的话题之一。

1.7.2 网站资源

Go语言的官方网站为 http://golang.org,这个网站只随着Go的主要版本发布而更新,因此并不反映Go的最新进展。如果读者希望跟进Go语言的最新进展,可以到http://code.google.com/p/go/ 直接下载最新代码。这里持续对Go资料进行了整理:http://github.com/wonderfo/wonderfogo/wiki

1.8 小结

本章我们简要介绍了Go语言的起源和背景,并结合若干代码示例简要介绍了我们认为最值得关注的关键特性,之后按老规矩以Hello, world这个例子作为起点帮助读者快速熟悉这门新语言,消除对Go语言的陌生感,并搭建好自己的Go开发环境。

通过这一章的学习,我们相信读者对于Go语言的简单易学特性已经有了比较直接的了解。在后续的章节中,各位读者可以利用在本章中搭建的开发环境和学习的工程管理知识,快速动手尝试各种Go语言令人兴奋的语言功能。

作者:

喜欢围棋和编程。

 
发布于 分类 编程标签

发表评论

电子邮件地址不会被公开。