在实际的开发工做中,直接调用编译器进行编译和连接的场景是少而又少,由于在工程中不会简单到只有一个源代码文件,且源文件之间会有相互的依赖关系。若是这样一个文件一个文件逐步编译,那不亚于一场灾难。 Go语言的设计者做为行业老将,天然不会忽略这一点。早期Go语言使用makefile做为临时方案,到了Go 1发布时引入了强大无比的Go命令行工具。linux
Go命令行工具的革命性之处在于完全消除了工程文件的概念,彻底用目录结构和包名来推导工程结构和构建顺序。针对只有一个源文件的状况讨论工程管理看起来会比较多余,由于这能够直接用go run和go build搞定。下面咱们将用一个更接近现实的虚拟项目来展现Go语言的基本工程管理方法。windows
Go代码必须放在工做区中。工做区其实就是一个对应于特定工程的目录,它应包含3个子目录:src目录、pkg目录和bin目录。bash
l src目录:用于以代码包的形式组织并保存Go源码文件。(好比:.go .c .h .s等)ide
l pkg目录:用于存放经由go install命令构建安装后的代码包(包含Go库源码文件)的“.a”归档文件。函数
l bin目录:与pkg目录相似,在经过go install命令完成安装后,保存由Go命令源码文件生成的可执行文件。工具
目录src用于包含全部的源代码,是Go命令行工具一个强制的规则,而pkg和bin则无需手动建立,若是必要Go命令行工具在构建过程当中会自动建立这些目录。测试
须要特别注意的是,只有当环境变量GOPATH中只包含一个工做区的目录路径时,go install命令才会把命令源码安装到当前工做区的bin目录下。若环境变量GOPATH中包含多个工做区的目录路径,像这样执行go install命令就会失效,此时必须设置环境变量GOBIN。ui
为了可以构建这个工程,须要先把所需工程的根目录加入到环境变量GOPATH中。不然,即便处于同一工做目录(工做区),代码之间也没法经过绝对代码包路径完成调用。spa
在实际开发环境中,工做目录每每有多个。这些工做目录的目录路径都须要添加至GOPATH。当有多个目录时,请注意分隔符,多个目录的时候Windows是分号,Linux系统是冒号,当有多个GOPATH时,默认会将go get的内容放在第一个目录下。命令行
全部 Go 语言的程序都会组织成若干组文件,每组文件被称为一个包。这样每一个包的代码均可以做为很小的复用单元,被其余项目引用。
一个包的源代码保存在一个或多个以.go为文件后缀名的源文件中,一般一个包所在目录路径的后缀是包的导入路径。
对于一个较大的应用程序,咱们应该将它的功能性分隔成逻辑的单元,分别在不一样的包里实现。咱们建立的的自定义包最好放在GOPATH的src目录下(或者GOPATH src的某个子目录)。
在Go语言中,代码包中的源码文件名能够是任意的。可是,这些任意名称的源码文件都必须以包声明语句做为文件中的第一行,每一个包都对应一个独立的名字空间:
package calc
复制代码
包中成员以名称⾸字母⼤⼩写决定访问权限:
l public: ⾸字母⼤写,可被包外访问
l private: ⾸字母⼩写,仅包内成员能够访问
**注意:**同一个目录下不能定义不一样的package。
在 Go 语言里,命名为 main 的包具备特殊的含义。 Go 语言的编译程序会试图把这种名字的包编译为二进制可执行文件。全部用 Go 语言编译的可执行程序都必须有一个名叫 main 的包。一个可执行程序有且仅有一个 main 包。
当编译器发现某个包的名字为 main 时,它必定也会发现名为 main()的函数,不然不会建立可执行文件。 main()函数是程序的入口,因此,若是没有这个函数,程序就没有办法开始执行。程序编译时,会使用声明 main 包的代码所在的目录的目录名做为二进制可执行文件的文件名。
Go里面有两个保留的函数:init函数(可以应用于全部的package)和main函数(只能应用于package main)。这两个函数在定义时不能有任何的参数和返回值。虽然一个package里面能够写任意多个init函数,但这不管是对于可读性仍是之后的可维护性来讲,咱们都强烈建议用户在一个package中每一个文件只写一个init函数。
Go程序会自动调用init()和main(),因此你不须要在任何地方调用这两个函数。每一个package中的init函数都是可选的,但package main就必须包含一个main函数。
每一个包能够包含任意多个 init 函数,这些函数都会在程序执行开始的时候被调用。全部被编译器发现的 init 函数都会安排在 main 函数以前执行。 init 函数用在设置包、初始化变量或者其余要在程序运行前优先完成的引导工做。
程序的初始化和执行都起始于main包。若是main包还导入了其它的包,那么就会在编译时将它们依次导入。
有时一个包会被多个包同时导入,那么它只会被导入一次(例如不少包可能都会用到fmt包,但它只会被导入一次,由于没有必要导入屡次)。
当一个包被导入时,若是该包还导入了其它的包,那么会先将其它包导入进来,而后再对这些包中的包级常量和变量进行初始化,接着执行init函数(若是有的话),依次类推。等全部被导入的包都加载完毕了,就会开始对main包中的包级常量和变量进行初始化,而后执行main包中的init函数(若是存在的话),最后执行main函数。下图详细地解释了整个执行过程:
示例代码目录结构:
main.go示例代码以下:
// main.go
package main
import (
"fmt"
"test"
)
func main() {
fmt.Println("main.go main() is called")
test.Test()
}
复制代码
test.go示例代码以下:
//test.go
package test
import "fmt"
func init() {
fmt.Println("test.go init() is called")
}
func Test() {
fmt.Println("test.go Test() is called")
}
复制代码
运行结果:
导入包须要使用关键字import,它会告诉编译器你想引用该位置的包内的代码。包的路径能够是相对路径,也能够是绝对路径。
//方法1
import "calc"
import "fmt"
//方法2
import (
"calc"
"fmt"
)
复制代码
标准库中的包会在安装 Go 的位置找到。 Go 开发者建立的包会在 GOPATH 环境变量指定的目录里查找。GOPATH 指定的这些目录就是开发者的我的工做空间。
若是编译器查遍 GOPATH 也没有找到要导入的包,那么在试图对程序执行 run 或者 build的时候就会出错。
注意:若是导入包以后,未调用其中的函数或者类型将会报出编译错误。
6.2.4.1 点操做
import (
//这个点操做的含义是这个包导入以后在你调用这个包的函数时,能够省略前缀的包名
. "fmt"
)
func main() {
Println("hello go")
}
复制代码
6.2.4.2 别名操做
在导⼊时,可指定包成员访问⽅式,⽐如对包重命名,以免同名冲突:
import (
io "fmt" //fmt改成为io
)
func main() {
io.Println("hello go") //经过io别名调用
}
复制代码
6.2.4.3 _操做
有时,用户可能须要导入一个包,可是不须要引用这个包的标识符。在这种状况,可使用空白标识符_来重命名这个导入:
import (
_ "fmt"
)
复制代码
_操做实际上是引入该包,而不直接使用包里面的函数,而是调用了该包里面的init函数。
calc.go代码以下:
package calc
func Add(a, b int) int { //加
return a + b
}
func Minus(a, b int) int { //减
return a - b
}
func Multiply(a, b int) int { //乘
return a * b
}
func Divide(a, b int) int { //除
return a / b
}
复制代码
main.go代码以下:
package main
import (
"calc"
"fmt"
)
func main() {
a := calc.Add(1, 2)
fmt.Println("a = ", a)
}
复制代码
6.3.2.1 windows
6.3.2.2 linux
设置环境变量GOBIN: