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

【分享本文】
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 語言中使用條件編譯。

    【分享本文】
    Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email
    【追蹤新文章】
    Facebook Twitter Plurk