GO語言學習筆記-包結構篇 Study for Go ! Chapter eight - Package Structure

slowlydance2me發表於2023-03-13

持續更新 Go 語言學習進度中 ......

  1. GO語言學習筆記-型別篇 Study for Go! Chapter one - Type - slowlydance2me - 部落格園 (cnblogs.com)
  2. GO語言學習筆記-表示式篇 Study for Go ! Chapter two - Expression - slowlydance2me - 部落格園 (cnblogs.com)
  3. GO語言學習筆記-函式篇 Study for Go ! Chapter three - Function - slowlydance2me - 部落格園 (cnblogs.com)

Study for Go ! Chapter eight - Package Structure

1. Workspace

  • 工作空間主要由 src、bin、pkg 三個目錄組成。通常需要將空間路徑新增到 GOPATH 環境變數列表中,一遍相關工具能正常工作

  • 在工作空間裡,包括子包在內的所有原始碼檔案都儲存在 src 目錄下。至於bin、pkg兩個目錄,其主要影響 go install/get 命令,它們會將編譯結果 (可執行檔案或靜態庫)安裝到這兩個目錄下,以實現增量編譯

 

環境變數

  • 編譯器等相關工具按 GOPATH 設定的路徑搜尋目標。也就是說在匯入目標庫時,排在列表前面的路徑比當前工作空間優先順序更高

  • 另外,go get 預設將下載的第三方包儲存到列表中第一個工作空間內

  •  

  • 環境變數 GOROOT 用於指示工具鏈和標準庫的存放位置。在生成工具鏈時,相關路徑就已經嵌入到可執行檔案內,故無須格外設定,但如果出現類似下面這樣的錯誤提示,請檢查路徑是否一致

  •  

  • 除透過設定 GOROOT 環境變數覆蓋內部路徑外,還可以移動目錄 (改名、符號連線等),或重新編譯工具鏈來解決

  • 至於 GOBIN ,則是強制代替工作空間的 bin 目錄,作為 go install 目標儲存路徑,這可避免將所有工作空間的 bin 路徑 新增到 PATH 環境變數中

  • 在使用 Git 等版本控制工具時,建議忽略 pkg、bin 目錄。直接在 src, 或具體的子包下建立程式碼倉庫 (repository)

 

2. 匯入包

  • 使用標準庫或第三方包前,需使用 import 匯入,引數是工作空間中以 src 為七十的絕對路徑。編譯器從標準庫開始搜尋,然後依次搜尋 GOPATH 列表中的各個工作空間

  • 除了使用預設包名外還可以使用別名,以解決同名衝突問題

 

Attention;

  • import 匯入引數是路徑,而非包名,儘管習慣將包和目錄名保持一致,但這不是強制規定,在程式碼中引用包成員時,使用包名而非目錄名

 

  • 有四種匯入方式

    • 預設方式

    • 別名方式

    • 簡便方式 (常用於單元測試程式碼中,不推薦在正式專案於程式碼中使用。)

    • 初始化方式 (無法引用,僅用來初始化目標包,讓目標包的初始化函式得以執行)

    不能直接或者間接匯入自己,不支援任何形式的迴圈匯入

 

  • 未使用的匯入(不包含初始化方式)會被編譯器視為錯誤

     

    相對路徑

    • 除了工作空間和絕對路徑外,部分工具還支援相對路徑。可在非工作空間目錄下,直接執行,編譯一些測試程式碼

    • 相對路徑是指:當前目錄,或以“ ./ ” 和 “ ../ ” 開頭的路徑

    • 不管是否在 test 目錄下,只要命令列路徑正確,就可以用go build/run/test 進行編譯,執行或測試。但因缺少工作空間相關目錄,go install 會無法工作

    • 在設定了 GOPATH 的工作空間中,相對路徑會導致編譯失敗

    • go run 不受此影響,可正常執行

     

    自定義路徑

    • 即便將程式碼託管在 GitHub ,但我們依然希望使用自有域名定義下載和匯入路徑。方法很簡單,在 Web 伺服器對應路徑返回中包含 “ go-import ” 跳轉資訊即可

    • 使用唯一的匯入路徑,方便日後遷移儲存端,但此方法對 vendor 機制無效

 

 

3. 組織結構

  • 包由一個或多個儲存在同一目錄下(不含子目錄)的原始碼檔案組成。包的用途類似名字空間(namespace),是成員作用域和訪問許可權的邊界

  • 包名和目錄名並無關係,不要求保持一致

  • 包名常用單數形式

  • 原始碼檔案必須使用 UTF-8 格式,否則會導致編譯出錯

  • 同一目錄下所有原始碼檔案必須使用相同包名稱,因匯入是使用絕對路徑,所以在搜尋路徑下,包必須有唯一路徑,但無須是唯一名字

     

    有幾個被保留、有特殊含義的包名稱;

    • main:可執行入口(入口函式 mian.mian)

    • all:標準庫以及 GOPATH 中能找到的所有包

    • std,cmd:標準庫及工具鏈

    • documentation:儲存文件資訊,無法匯入(和目錄名無關)

    (相關工具忽略以” . “ 或 ” _ “ 開頭的目錄或檔案,但是又允許匯入儲存在這些目錄中的包! )

     

    許可權

    • 所有成員在包內均可訪問,無論是否在同一原始碼檔案中。但只有名稱首字母大寫的為可匯出成員,在包外可視 (該規則適用於全域性變數、全域性常量、型別、結構欄位、函式、方法等)

    • 可透過指標轉換等方式繞開該限制

     

    初始化

    • 包內每個原始碼檔案都可定義一到多個初始化函式,但編譯器不保證執行次序。

    • 實際上,所有這些初始化函式(包括標準庫和匯入的第三方包)都由編譯器自動生成的一個包裝函式進行呼叫,因此可保證在單一執行緒上執行,且只執行一次

    • 編譯器首先確保完成所有全域性變數初始化,然後才開始執行初始化函式,知道這些全部結束後,執行時才正式進入 main.main 入口函式

    • 可以在初始化函式中建立 goroutine,或等到它執行結束

    • 如果在多個初始化函式中引用全域性變數,那麼最好在變數定義出直接賦值,因無法保證執行次序,所以任何初始化函式中的賦值都有可能“ 延遲無效 ”

     

    延遲包

    • 在進行程式碼重構時,我們會將一些內部模組陸續分離出來,以獨立包形式維護。此時,基於首字母大小寫的訪問許可權控制就顯得過於粗獷。因為我們希望這些包匯出成員僅在特定範圍內訪問,而不是向所有使用者公開

    • 內部包機制相當於增加了新的訪問許可權控制:所有儲存在 internal 目錄下的包 ( 包括自身 )僅能被其父目錄下的包 ( 含所有層次的子目錄 )訪問

    • 匯入內部包必須使用完整路徑

 

 

4. 依賴管理

  • 如何管理和儲存第三方包,一致存在爭議。將專案所有的第三方依賴都放到一個獨立工作空間中,可能會導致版本衝突。放到專案工作空間,又會把工作目錄搞的面目全非。為此,引入了名為 vendor 的機制,專門存放第三方包,實現將原始碼和依賴完整打包分發

  • 如果說 internal 針對內部,那麼 vender 就是 針對外部 (external)

  • 匯入 vendor 中的第三方包,引數是以 vendor/ 為起點的絕對路徑。這就避免了 vendor 目錄位置帶來的麻煩,讓匯入無論使用 vender,還是 GOPATH 都能保持一致

ATTENTION

  • vendor 比標準庫優先順序更高

 

question:當多個 vendor 目錄巢狀時,如何正確查詢目標 ?要知道引入的第三方包也可能存在有自己的 vendor 依賴目錄

answer:從當前原始檔所在目錄開始,逐級向上構造 vendor 全路徑,知道發現路徑匹配的目標為止。匹配失敗,則依舊搜尋 GOPATH

 

  • 要使用 vendor機制,須開啟 ” GO15VENDOREXPERIMENT=1 “ 環境變數開關 ( GO 1.6 以上預設開啟 )且必須是設定了 GOPATH 的工作空間

使用 go get 下載第三方包時,依舊使用 GOPATH 第一個工作空間,而非 vendor 目錄。當前工具鏈中並沒有真正意義上的包管理依賴,好在有不少的第三方工具可以選擇

 

 

相關文章