GO程式碼生成程式碼小思小試

三人行工作室發表於2019-07-05

推進需求

GO 專案,可整體生成一個執行檔案到處跑,是極爽之事。但如果有資原始檔要得帶著跑,則破壞了這種體驗。

例如下邊這個專案結構,resource 目錄下為資原始檔,main.go 中會通過路徑引用到這些資原始檔,於是新的需求產生了。

|- hello
----|- resource
---------|- note.txt
----|- main.go

需求推進一步:將資原始檔打包至執行檔案中,在程式碼中仍然使用類似相對路徑的引用方式使用它(這個很重要,使用在概念上保持一致)。

解決方案跟上:把資原始檔資料轉換成 GO 程式碼中的變數值儲存,並提供函式可根據路徑差數返回相應的資料。這樣一來資原始檔就變成了程式碼檔案,又可以一個執行檔案到處跑了。

工具跟進 go-bindata

於是,工具 go-bindata 就來了,它來完成將資原始檔變成 GO 程式碼的工作。

go get -u github.com/jteeuwen/go-bindata/...
go install

在 hello 專案目錄下執行這個工具,就會生成 -o 指定的 bindata.go 程式碼檔案。

go-bindata -pkg main -o bindata.go resource/

此時,在 hello 目錄下就多了 bindata.go 程式碼檔案,然後,在 main.go 中,就可以使用以下方式指定相對路徑引數來取得資源資料。

bs, _ := Asset("resource/note.txt")

生成程式碼機制 go generate

有了上述工具,當 note.txt 資料有更新時,就需要使用 go-bindata 工具重新生成程式碼檔案再編譯,可不可以形成一種專門的機制來規範化這類動作呢?

於是,再進一步,若能如此甚好:

(1)GO 專案的程式碼中,能自描述的表明有些相關依賴程式碼由需要由工具產生。

(2)支援簡單的方式來完成自描述所表明的操作動作。

於是,go generate 應運而生,來看一下它的大概描述。

go generate 命令是 go 1.4 版新新增的一個命令,它將掃描與當前包相關的原始碼檔案,找出所有包含 "//go:generate" 的特殊註釋,提取並執行該特殊註釋後面的命令,命令為可執行程式。

是不是完美的實現了上述兩項訴求。

(1)在原始碼,即 GO 檔案中,進行註釋(自描述)即可指定要執行的工具命令。
除了 go-bindata 工具之外,類似的工具定是還有很多。工具抽象出來可認為就是可執行命令,指定命令甚是靈活。

(2)只要執行一條命令,即可自動掃描當前包相關的原始碼檔案來完成相應的執行。
這說明,不論有多少個這樣的命令需要執行,它會負責找到並執行。

小小例項,一目瞭然

開頭專案結構中,main.go 的完整程式碼如下:

//go:generate go-bindata -pkg main -o bindata.go resource/
//go:generate go build
//go:generate hello

package main

import (
    "fmt"
)

func main() {
    bs, _ := Asset("resource/note.txt")
    fmt.Println(string(bs))
}

註釋部分即描述了三個動作,完成資原始檔程式碼生成,編譯與執行命令,當執行 go generate 時,三條命令執行下來執行的結果為顯示 note.txt 中的文字內容。執行效果如下,當修改了 note.txt,執行 go generate,會自動完成重新生成程式碼,編譯和執行。

GO程式碼生成程式碼小思小試

相關文章