在實際的開發工做中,直接調用編譯器進行編譯和連接的場景是少而又少,由於在工程中不會簡單到只有一個源代碼文件,且源文件之間會有相互的依賴關係。若是這樣一個文件一個文件逐步編譯,那不亞於一場災難。 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:
在源碼目錄,敲go install: