位元詩人 [Golang] 程式設計教學:藉由 Build Constrants 使用條件編譯 (conditional compilation)

Golang
Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

前言

條件編譯 (conditional compilation) 是指針對不同平台 (platform) 在編譯時期選擇性地引入特定的程式碼,藉以封裝不同平台間的差異性,達到跨平台的效果。條件編譯最常見的情境是 C 或 C++ 的前置處理器 (preprocessor);雖然 Go 語言 (golang) 沒有 C 或 C++ 的前置處理器,但 Go 語言也可透過 build constraints 來達到類似的功能。

如果使用英文的相關關鍵字去查詢,可以查到幾個國外的部落客寫到 Go 語言的這項特性;但若使用中文的相關關鍵字去查詢,則幾乎清一色是來自對岸的簡體中文文章,代表這項特性算是相對冷門的。官方文件中關於 build constraints 的描述在 go/build 套件中,而本文會用中文介紹這項特性,讀者可以相互參照著看。

在 Go 語言中要達到條件編譯的特性,有兩種方式:

  • 檔案後綴
  • build tags

本文將分別說明。

檔案後綴

檔案後綴比較容易理解,就是在檔名中加入代表特定平台的後綴 (suffix);加入後綴的 Go 程式碼檔案只有在特定平台才會引入,在其他平台則會忽略。檔案後綴可限定作業系統 (operating systems) 或 CPU 架構 (CPU architecture),其語法如下:

  • _GOOS
  • _GOARCH
  • _GOOS_GOARCH

以下是實例:

  • mypkg_windows.go :只在 Windows 系統上引入
  • mypkg_amd64.go :只在 Intel 相容的 64 位元架構下引入
  • mypkg_linux_arm.go :只在 ARM 架構下的 GNU/Linux 系統上引入

一個實例是用檔案後綴來宣告字串行尾 (EOL, end of line)。以下是 const_windows.go 的內容:

package main

const LineBreak = "\r\n"

以下則是 const_linux.goconst_darwin.go (Mac 系統) 的內容:

package main

const LineBreak = "\n"

使用方式如下:

msg := fmt.Sprintf("Some message%s", LineBreak)

透過這個手法,Go 編譯器會在不同平台塞入相對應的字串行尾,藉此達到跨平台的效果。

國外有強者整理出 GOOS 和 GOARCH 的清單,甚至連 go/build 的原始碼都挖出來了,相當具有求知精神。

此外,可以用 go tool dist list 指令在終端機中顯示所有支援的平台和架構。

Build Tag

上一節介紹的方法相當簡單,但每個檔案只能用在單一平台或架構中,比較不靈活;因此,Go 語言引入 build tag,使用起來會比檔案後綴來得靈活一些。

Build tag 以註解 (comments) 的形式撰寫,一定要放在 Go 程式碼檔案的第一行,以下是實例:

// +build windows

package mylib

// More code here.

上述範例程式碼只在 Windows 平台下才會引入。我們接下來會省略其他部分,只留 build tag。

以下程式只會在 Intel 相容 64 位元架構下的 GNU/Linux 系統上引入:

// +build linux,amd64

Build tags 可以納入多個架構或平台,如下例:

// +build linux darwin

上述程式碼會在 GNU/Linux 和 Mac 平台上納入。

Build tag 可以多行敘述,如下例:

// +build linux darwin
// +build amd64

上述程式碼會在 Intel 相容 64 位元架構下的 GNU/Linux 和 Mac 平台上納入。

也可以使用否定敘述:

// +build !windows

上述程式碼會在 Windows 以外的平台上納入。

最後,我們用 build tag 改寫先前的例子。在 const_unix.go 中加入以下內容:

// +build linux darwin freebsd

package main

const LineBreak = "\n"

由於 _unix 不是真正的檔案後綴,剛好可以用來命名檔案。藉由使用 build tag,我們可以減少重複的程式碼。

後記

雖然檔案後綴和 build tag 只有一小部分的前置處理器的功能,但前置處理器缺乏型別安全且難以除錯,故 Go 語言未引入整個前置處理器。透過 build constraint,我們可以在 Go 語言中使用條件編譯。

關於作者

身為資訊領域碩士,位元詩人 (ByteBard) 認為開發應用程式的目的是為社會帶來價值。如果在這個過程中該軟體能成為永續經營的項目,那就是開發者和使用者雙贏的局面。

位元詩人喜歡用開源技術來解決各式各樣的問題,但必要時對專有技術也不排斥。閒暇之餘,位元詩人將所學寫成文章,放在這個網站上和大家分享。