使用建造標籤自訂 Go 二進制文件

介紹

在Go語言中,構建標籤或稱構建約束是一個標識符,它被添加到代碼片段中,用於決定在構建過程中何時應將文件包含在包內。這允許您從同一個源代碼構建Go應用程序的不同版本,並以快速且有組織的方式在它們之間切換。許多開發人員使用構建標籤來改善構建跨平台兼容應用程序的工作流程,例如需要更改代碼以適應不同操作系統之間差異的程序。構建標籤還用於整合測試,讓您可以快速在整合的代碼與帶有模擬服務或存根的代碼之間切換,以及應用程序中不同級別的功能集。

以不同客戶功能集的問題為例。在編寫某些應用程序時,您可能想要控制要將哪些功能包含在二進制中,例如一個提供免費專業企業等級的應用程序。當客戶在這些應用程序中提高訂閱等級時,會有更多的功能被解鎖並可用。為了解決這個問題,您可以維護不同的專案,並嘗試通過使用import語句將它們保持同步。雖然這種方法可以工作,但隨著時間的推移,它會變得繁瑣且容易出錯。一種替代方法將是使用構建標籤。

在本文中,您將使用Go中的構建標籤生成提供樣本應用程序的免費、專業和企業功能集的不同可執行二進制。每個都將有不同的功能集可用,其中免費版本為默認。

先決條件

要跟隨本文中的示例,您需要:

建立免費版本

讓我們從建立應用程式的免費版本開始,因為當您在沒有任何構建標籤的情況下運行go build時,這將成為默認版本。稍後,我們將使用構建標籤選擇性地向我們的程序添加其他部分。

src目錄中,創建一個以您的應用程式名稱命名的資料夾。本教程將使用app

  1. mkdir app

移動到這個資料夾中:

  1. cd app

接下來,在您選擇的文本編輯器中建立一個新文本檔案,名為main.go

  1. nano main.go

現在,我們將定義應用程式的免費版本。將以下內容添加到main.go中:

main.go
package main

import "fmt"

var features = []string{
  "Free Feature #1",
  "Free Feature #2",
}

func main() {
  for _, f := range features {
    fmt.Println(">", f)
  }
}

在此檔案中,我們創建了一個程序,聲明了一個名為features切片,其中包含兩個表示我們免費應用程式功能的字串。應用程式中的main()函數使用一個for循環來range遍歷features切片,並將所有可用的功能打印到屏幕上。

儲存並離開該檔案。現在這個檔案已經儲存,我們在本文其餘部分就不再需要編輯它了。相反,我們將使用構建標籤來更改我們從其構建的二進制檔案的特性。

構建並運行程序:

  1. go build
  2. ./app

您將收到以下輸出:

Output
> Free Feature #1 > Free Feature #2

程序已打印出我們的两個免費特性,完成了我們應用程式的免費版本。

目前為止,您創建了一個具有非常基本功能集的應用程式。接下來,您將構建一種在構建時將更多特 性添加到應用程式中的方法。

使用 go build 添加 Pro 特性

到目前為止,我們避免對 main.go 進行更改,模擬一個常見的生產環境,在該環境中需要添加代碼而無需更改並可能破壞主代碼。由於我們不能編輯 main.go 檔案,因此我們需要使用另一種機制,利用構建標籤將更多特 性注入 features 切片。

讓我們創建一個名為 pro.go 的新檔案,它將使用一個 init() 函數向 features 切片追加更多特 性:

  1. nano pro.go

一旦編輯器打開了該檔案,請添加以下行:

pro.go
package main

func init() {
  features = append(features,
    "Pro Feature #1",
    "Pro Feature #2",
  )
}

>tag_name

在這段程式碼中,我們使用了init()在應用的main()函數之前運行程式碼,然後使用append()將Pro功能添加到features切片中。儲存並離開文件。

使用go build編譯和運行應用程序:

  1. go build

由於我們當前目錄中有兩個文件(pro.gomain.go),go build將從它們兩者創建一個二進制文件。執行這個二進制文件:

  1. ./app

這將給你以下功能集:

Output
> Free Feature #1 > Free Feature #2 > Pro Feature #1 > Pro Feature #2

應用程序現在包括Pro和Free功能。然而,這不是我們想要的:由於版本之間沒有區別,Free版本現在包括了應該只在Pro版本中提供的功能。為了解決這個問題,你可以添加更多程式碼來管理應用的不同層次,或者你可以使用構建標籤來告訴Go工具鏈要構建哪些.go文件,哪些要忽略。我們在下一步添加構建標籤。

添加構建標籤

現在,你可以使用構建標籤來區分應用程序的Pro版本和Free版本。

讓我們從檢查構建標籤的樣式開始:

// +build tag_name

將以下這行程式碼作為您的套件中的第一行程式碼,並將tag_name替換為您的構建標籤的名稱,您將會將此套件標記為可以在最終二進制中選擇性包含的代碼。讓我們通過向pro.go文件中添加一個構建標籤來看看這是如何實現的,告訴go build命令在沒有指定標籤的情況下忽略它。打開您的文字編輯器中的文件:

  1. nano pro.go

然後添加以下突出顯示的行:

pro.go
// +build pro

package main

func init() {
  features = append(features,
    "Pro Feature #1",
    "Pro Feature #2",
  )
}

pro.go文件的頂部,我們添加了// +build pro,後面跟著一個空行。這個後續空行是必需的,否則Go會將其解釋為註釋。構建標籤聲明也必須位於.go文件的頂部。任何東西,甚至註釋,都不能在構建標籤之上。

+build聲明告訴go build命令這不是註釋,而是一個構建標籤。第二部分是pro標籤。通過在pro.go文件的頂部添加此標籤,go build命令現在只會在存在pro標籤時包含pro.go文件。

再次編譯並運行應用程序:

  1. go build
  2. ./app

您將收到以下輸出:

Output
> Free Feature #1 > Free Feature #2

由於pro.go文件需要存在pro標籤,因此該文件被忽略,應用程序在沒有它的情況下編譯。

當運行go build命令時,我們可以使用-tags標誌通過添加標籤本身作為參數,條件性地包含編譯源中的代碼。讓我們為pro標籤做這件事:

  1. go build -tags pro

以下是您要求的傳統中文翻譯:

這將輸出以下內容:

Output
> Free Feature #1 > Free Feature #2 > Pro Feature #1 > Pro Feature #2

現在我們只在構建應用程序時使用pro構建標籤才能獲得額外功能。

如果只有兩個版本,這沒有問題,但當您添加更多標籤時,情況就變得複雜了。在下一步中添加我們應用程序的企业版時,我們將使用布林邏輯連接多個構建標籤。

構建標籤布林邏輯

當Go包中有多個構建標籤時,這些標籤會使用布林邏輯相互交互。為了演示這一點,我們將使用pro標籤和enterprise標籤添加我們应用程序的企业級別。

為了構建企业二進制文件,我們將需要包含默認功能、Pro級別功能以及新的一組企业功能。首先,打開編輯器並創建一個新文件enterprise.go,該文件將添加新的企业功能:

  1. nano enterprise.go

enterprise.go的內容將與pro.go幾乎相同,但將包含新功能。將以下行添加到文件中:

enterprise.go
package main

func init() {
  features = append(features,
    "Enterprise Feature #1",
    "Enterprise Feature #2",
  )
}

保存並退出文件。

目前enterprise.go檔案中沒有任何編譯標籤,就像您在添加pro.go時所學到的,這意味著當執行go.build時,這些功能將被添加到免費版本中。對於pro.go,您在檔案頂部添加了// +build pro和一個新行,以告訴go build只有在使用-tags pro時才應包含它。在這種情況下,您只需要一個編譯標籤就能達成目標。然而,在添加新的企業版功能時,您首先必須也具有Pro功能。

讓我們先為enterprise.go添加對pro編譯標籤的支持。打開檔案並使用您的文字編輯器:

  1. nano enterprise.go

接下來,在package main聲明之前添加編譯標籤,並確保在編譯標籤後包含一個新行:

enterprise.go
// +build pro

package main

func init() {
  features = append(features,
    "Enterprise Feature #1",
    "Enterprise Feature #2",
  )
}

保存並退出檔案。

編譯並運行沒有任何標籤的應用程序:

  1. go build
  2. ./app

您將收到以下輸出:

Output
> Free Feature #1 > Free Feature #2

企業版功能不再顯示在免費版本中。現在讓我們添加pro編譯標籤,並再次編譯和運行應用程序:

  1. go build -tags pro
  2. ./app

您將收到以下輸出:

Output
> Free Feature #1 > Free Feature #2 > Enterprise Feature #1 > Enterprise Feature #2 > Pro Feature #1 > Pro Feature #2

這仍然不是我們需要的結果:當我們嘗試編譯Pro版本時,企業版功能現在會出現。為了解決這個問題,我們需要使用另一個編譯標籤。與pro標籤不同,然而,我們現在需要確保proenterprise功能都可用。

Go編譯系統通過允許在編譯標籤系統中使用一些基本的布爾邏輯來處理這種情況。

再次打開enterprise.go

  1. nano enterprise.go

在同一行新增另一個構建標籤enterprise,與pro標籤在同一行:

enterprise.go
// +build pro enterprise

package main

func init() {
  features = append(features,
    "Enterprise Feature #1",
    "Enterprise Feature #2",
  )
}

儲存並關閉檔案。

現在让我们用新的enterprise構建標籤編譯和運行應用程序。

  1. go build -tags enterprise
  2. ./app

這將會得到以下結果:

Output
> Free Feature #1 > Free Feature #2 > Enterprise Feature #1 > Enterprise Feature #2

現在我們失去了Pro功能。這是因為當我們在.go檔案中的同一行放置多個構建標籤時,go build會將它們解釋為使用OR邏輯。在添加了行// +build pro enterprise之後,如果存在任何一個pro構建標籤或enterprise構建標籤,則會構建enterprise.go檔案。我們需要正確設置構建標籤,以要求同時存在並使用AND邏輯。

與其將兩個標籤放在同一行,不如將它們放在不同的行上,這樣go build就會使用AND邏輯來解釋這些標籤。

再次打開enterprise.go,讓我們將構建標籤分開到多行。

enterprise.go
// +build pro
// +build enterprise

package main

func init() {
  features = append(features,
    "Enterprise Feature #1",
    "Enterprise Feature #2",
  )
}

現在用新的enterprise構建標籤編譯和運行應用程序。

  1. go build -tags enterprise
  2. ./app

你將會收到以下輸出:

Output
> Free Feature #1 > Free Feature #2

但還是沒有達到我們的要求:由於AND陳述需要兩個元素都被視為true,我們需要同時使用proenterprise構建標籤。

讓我們再試一次:

  1. go build -tags "enterprise pro"
  2. ./app

您將收到以下輸出:

Output
> Free Feature #1 > Free Feature #2 > Enterprise Feature #1 > Enterprise Feature #2 > Pro Feature #1 > Pro Feature #2

現在我們的應用程序可以從同一個源代碼樹以多種方式構建,相應地解鎖應用程序的功能。

在這個例子中,我們使用了一個新的 // +build 標籤來表示 AND 邏輯,但還有其他方法可以用構建標籤表示布爾邏輯。以下表格列出了一些其他構建標籤的語法格式以及它們的布爾等價物:

Build Tag Syntax Build Tag Sample Boolean Statement
Space-separated elements // +build pro enterprise pro OR enterprise
Comma-separated elements // +build pro,enterprise pro AND enterprise
Exclamation point elements // +build !pro NOT pro

結論

在這篇教程中,您使用了構建標籤來控制哪些代碼被編譯到二進制文件中。首先,您聲明了構建標籤並與 go build 一起使用,然後您將多個標籤與布爾邏輯結合起來。然後您構建了一個程序,表示免費、專業和企業版的不同的功能集,展示了構建標籤可以為您的項目提供強大的控制級別。

如果您想進一步了解構建標籤,請查看 關於該主題的 Golang 文檔,或繼續探索我們的 如何用 Go 語言編碼系列

Source:
https://www.digitalocean.com/community/tutorials/customizing-go-binaries-with-build-tags