Skip to content
On this page

Go 项目工程管理示例

上一篇教程[第一个GO程序] 中,安装了 Go 开发环境,并且编写了第一个 Go 程序。

不过在实际开发过程中,直接调用编译器进行编译和链接(调用 go buildgo run 命令)的场景很少,因为在项目工程中不会简单到只有一个源代码文件,往往会有很多源代码文件以及各种包,并且之间会有相互依赖的关系,如果都要一个文件一个文件的逐步编译,那么真是无法想象。

Go 语言的设计者作为行业老将,自然不会忽略这一点,说到这里,我们来看看 Go 语言的主要设计者都是何方神圣:

个顶个的都是大神级人物,早起 Go 语言使用 makefile 作为临时方案,到了 Go 1 发布时引入了强大无比的 Go 命令行工具 ,Go 命令工具彻底消除了工程文件的概念,完全用目录结构和包名来推导工程结构和构建顺序。

想想 C 语言编译、链接和安装可执行程序的繁琐步骤,首先要通过 makefile 脚本对特定平台进行检测,比如是否包含了 GCC 等编译工具,然后生成 Makefile 文件,该文件定义了编译、链接规则,为下一步编译工作做准备,接下来就是执行 make 命令进行编译工作,最后再执行 make install 命令完成应用程序的安装,经历过这些步骤才可以实现将 C 源代码文件编译为可执行程序。

与之相比,Go 语言则要简单得多,针对只有一个源文件的情况(如上篇教程所示),引入工程管理看起来比较多余,因为直接使用 go run 或者 go build 搞定,下面我们通过 GO 语言构建一个简单的计算器项目来演示 Go 语言的基本工程管理方法。

通过 Go Modules 创建新项目

从 Go 1.11 版本开始,官方提供了 Go Modules 管理项目和依赖,从 1.13 版本开始,更是默认开启了对 Go Modules 的支持,使用 Go Modules 的好处是显而易见的 —— 不需要再依赖 GOPATH,你可以在任何位置创建 Go 项目,并且在国内,还可以通过 GOPROXY 配置镜像源加速依赖包的下载,彻底解决之前依赖包下载慢的问题。

通过 Go Modules 管理项目非常简单,我们可以在任意位置创建项目,比如作者将本地所有 Go 项目存储在了 ~/GolandProjects 这个目录下。以最新版本的 Go 为例,在 GoLand IDE 中可以直接基于 Go Modules 创建计算器项目 calc

新建项目时左侧选择 Go(GO modules),在 Location 中输入项目目录,如果需要配置依赖包镜像,可以在 环境: 输入框中点击右侧图标,在弹出窗口新增 GOPROXY 环境变量进行设置:

点击 + 号新建环境变量,名称为:GOPROXY,值为:https://goproxy.io,direct,这个是 Go 官方提供的全球代理。除此之外,也可以通过终端命令进行代理的设置:

Go
go env -w GOPROXY=https://goproxy.io,direct

点击 确定 保存环境变量配置,在 GoLand 新建项目窗口点击右下角 创建 按钮来创建这个新项目。

与上篇教程创建项目不同的是,calc 项目根目录下默认包含了一个 go.mod 文件,我们可以通过该项目来管理项目依赖,就像 PHP 的 composer.json 或者 JavaScript 的 package.json 那样。

目前这个文件只包含了项目模块名称:

编写计算机工程源码

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

  • 计算器程序的入口文件 main.go,该文件最终会被编译为计算器程序的可执行文件;
  • 算法库,名为 simplemath,每个计算操作对应于一个同名的 Go 文件,比如 add.go 用于计算加法运算。

然后我们来编写每个 Go 文件的源代码。

首先在项目根目录下新建入口文件 main.go,并编写代码如下:

Go
package main  
  
// 引入其它包  
import (  
   "calc/simplemath"  
   "fmt"   "os"   "strconv")  
  
// Usage 定义一个用于打印程序使用指南的函数  
var Usage = func() {  
   fmt.Println("USAGE: calc command [arguments] ...")  
   fmt.Println("\nThe commands are:\n\tadd\t计算两个数值相加\n\tsqrt\t计算一个非负数的平方根")  
}  
  
// 程序入口函数  
func main() {  
   /*  
    * 用于获取命令行参数,注意程序名本身是第一个参数,  
    * 比如 calc add 1 2 这条指令,第一个参数是 calc    */   args := os.Args  
   // 除程序名本身外,至少需要传入两个其它参数,否则退出  
   if args == nil || len(args) < 3 {  
      Usage()  
      return  
   }  
  
   // 第二个参数表示计算方法  
   switch args[1] {  
   // 如果是加法的话  
   case "add":  
      // 至少需要包含四个参数  
      if len(args) != 4 {  
         fmt.Println("USAGE: calc add <integer1><integer2>")  
         return  
      }  
      // 获取待相加的数值,并将类型转化为整型  
      v1, err1 := strconv.Atoi(args[2])  
      v2, err2 := strconv.Atoi(args[3])  
      // 获取参数出错,则退出  
      if err1 != nil || err2 != nil {  
         fmt.Println("USAGE: calc add <integer1><integer2>")  
         return  
      }  
      // 从 simplemath 包引入 Add 方法进行加法计算  
      ret := simplemath.Add(v1, v2)  
      // 打印计算结果  
      fmt.Println("Result: ", ret)  
   // 如果是计算平方根的话  
   case "sqrt":  
      // 至少需要包含三个参数  
      if len(args) != 3 {  
         fmt.Println("USAGE: calc sqrt <integer>")  
         return  
      }  
      // 获取待计算平方根的数值,并将类型转化为整型  
      v, err := strconv.Atoi(args[2])  
      // 获取参数出错,则退出  
      if err != nil {  
         fmt.Println("USAGE: calc sqrt <integer>")  
         return  
      }  
      // 从 simplemath 包引入 Sqrt 方法进行平方根计算  
      ret := simplemath.Sqrt(v)  
      // 打印计算结果  
      fmt.Println("Result: ", ret)  
   // 如果计算方法不支持,打印程序使用指南  
   default:  
      Usage()  
   }  
}

这段代码相信对于有其他语言编程基础的人来说,都不困难。

需要注意的是,在 Go 语言中,通过 var 来声明变量,并且可以将匿名函数赋值给变量以便后续使用,此外,还可以通过 := 运算符来声明并初始化变量,这个时候,不需要通过 var 声明该变量,比如 args := os.Args 就是如此,需要明确的是,虽然这看起来有点和动态语言声明变量类似,但与 PHP/Python/JavaScript 不同,Go 是强类型语言,底层会自动根据赋值判断对应变量的类型,如果你试图声明一个没有初始化值的变量,就会报错,关于 Go 语言变量声明和初始化我们后面还会详细介绍,这里简单了解下即可。

接下来,我们在 calc 根目录下新建一个 simplemath 子目录作为算法库,并在该目录下新建 add.go 用于编写计算加法的实现代码如下:

Go
package simplemath

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

以及 sqrt.go 用于编写计算平方根的实现代码如下:

Go
package simplemath

import "math"

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

构建 Go 工程

截至目前,计算器项目的代码结构是这样的:

我们可以在 calc 目录下运行构建指令 go build 构建该项目,默认会在项目根目录下生成与项目目录同名的可执行文件:

然后直接运行 calc 通过命令行与基于 Go 实现的计算器应用进行交互了:

非常简单,是吧。你也可以以此为参考自行编写乘法、减法、除法的实现代码。

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

Go
go build

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

当然,这里只是一个最简单、最基本工程管理的构建示例,后面我们还会就更复杂的工程项目管理进行介绍,比如工程组织、文档与代码风格、跨平台开发、单元测试、打包分发等。

Released under the MIT License.