詳解如何使用Bazel構(gòu)建Golang程序
使用Bazel構(gòu)建Golang程序
在這篇簡(jiǎn)短的文章中,我們將介紹如何將 Golang 與 Bazel 構(gòu)建系統(tǒng)結(jié)合使用。
具體來(lái)說(shuō),我們將討論三個(gè)場(chǎng)景:
- 從頭開始一個(gè) Golang 項(xiàng)目;
- 將一個(gè)現(xiàn)有的 Golang 項(xiàng)目轉(zhuǎn)換為 Bazel 構(gòu)建;
- 以及將一個(gè)第三方 Golang 項(xiàng)目引入到您的 Bazel 構(gòu)建系統(tǒng)。
從頭開始一個(gè) Golang 項(xiàng)目
讓我們從將 Go 與 Bazel 結(jié)合使用的基礎(chǔ)知識(shí)開始。
為此,我們需要從 github.com/bazelbuild/… 獲取 Go 語(yǔ)言的官方構(gòu)建規(guī)則。
在配置部分,您會(huì)發(fā)現(xiàn):我們需要將以下這段 Starlark 語(yǔ)言代碼,放入名為 WORKSPACE 的配置文件里面:
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "io_bazel_rules_go",
sha256 = "8e968b5fcea1d2d64071872b12737bbb5514524ee5f0a4f54f5920266c261acb",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.28.0/rules_go-v0.28.0.zip",
"https://github.com/bazelbuild/rules_go/releases/download/v0.28.0/rules_go-v0.28.0.zip",
],
)
load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies")
go_rules_dependencies()
go_register_toolchains(version = "1.16.5")
讓我們逐步了解這段代碼所做的事情。
首先,我們使用load指令來(lái)加載并提取新功能,以在 Bazel 文件當(dāng)中能夠使用該功能。我們調(diào)用了兩次load指令,第一次用于導(dǎo)入下載 HTTP 代碼庫(kù)的能力,第二次則是從剛下載的代碼庫(kù)中加載特定于 Go 的命令。
對(duì)于導(dǎo)入本身,我們需要提供一個(gè)導(dǎo)入名稱。通常的命名方案是:逆反域名,后面跟命名空間和項(xiàng)目名稱;并需將/和.全部都轉(zhuǎn)換為下劃線_。例如:github.com/user/project變成com_github_user_project。io_bazel_rules_go這個(gè)項(xiàng)目由于是Bazel官方項(xiàng)目中的一部分,所以它使用的是bazel.io而不是github.com。
如果您并不熟悉 Bazel,那么,您需要了解到:實(shí)際的構(gòu)建配置是通過(guò)BUILD文件完成的。我們可以將 Go 視為任何其他語(yǔ)言,并使用遵循相同結(jié)構(gòu)的規(guī)則:go_binary、go_library和go_test。我在我的 Github 上準(zhǔn)備了一個(gè)最小化的例子:github.com/HappyCerber…。您會(huì)注意到,我們需要從導(dǎo)入的io_bazel_rules_go代碼庫(kù)中加載這些規(guī)則,以使其在BUILD文件中可用。
將現(xiàn)有項(xiàng)目轉(zhuǎn)換為 Bazel 構(gòu)建
現(xiàn)在我們知道,從頭全新開始是很容易。但是,如果您已經(jīng)有一個(gè) Golang 項(xiàng)目,并且需要將其轉(zhuǎn)換為 使用 Bazel 構(gòu)建怎么辦?為此,我們需要使用 Bazel 官方項(xiàng)目中提供的另一個(gè)工具 Gazelle ( github.com/bazelbuild/… )。
為了演示,我將使用一個(gè)第三方項(xiàng)目 ( github.com/aler9/rtsp-… ),我目前正在為即將到來(lái)的系統(tǒng)設(shè)計(jì)課程修改該項(xiàng)目。
首先,我們需要?jiǎng)?chuàng)建一個(gè)WORKSPACE文件,并從 Gazelle 代碼庫(kù)的設(shè)置部分復(fù)制粘貼代碼。
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "io_bazel_rules_go",
sha256 = "8e968b5fcea1d2d64071872b12737bbb5514524ee5f0a4f54f5920266c261acb",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.28.0/rules_go-v0.28.0.zip",
"https://github.com/bazelbuild/rules_go/releases/download/v0.28.0/rules_go-v0.28.0.zip",
],
)
http_archive(
name = "bazel_gazelle",
sha256 = "62ca106be173579c0a167deb23358fdfe71ffa1e4cfdddf5582af26520f1c66f",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.23.0/bazel-gazelle-v0.23.0.tar.gz",
"https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.23.0/bazel-gazelle-v0.23.0.tar.gz",
],
)
load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies")
load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies")
go_rules_dependencies()
go_register_toolchains(version = "1.16.5")
gazelle_dependencies()
您會(huì)注意到,上述代碼也導(dǎo)入了上一節(jié)中所提到的規(guī)則。
現(xiàn)在,要真正運(yùn)行 Gazelle,我們需要將以下代碼添加到我們的主BUILD文件中:
load("@bazel_gazelle//:def.bzl", "gazelle")
# gazelle:prefix github.com/aler9/rtsp-simple-server
gazelle(name = "gazelle")
gazelle:prefix注釋后面的路徑是整個(gè)項(xiàng)目所使用的 Go 導(dǎo)入路徑。例如,main.go中有以下包的導(dǎo)入:
import (
"os"
"github.com/aler9/rtsp-simple-server/internal/core"
)
至此,我們終于可以真正運(yùn)行Gazelle,讓它BUILD為我們的項(xiàng)目生成文件了。
bazel run //:gazelle
之后,我們應(yīng)該BUILD自動(dòng)生成項(xiàng)目的所有文件:
git status On branch main Your branch is up to date with 'origin/main'. Untracked files: (use "git add <file>..." to include in what will be committed) BUILD WORKSPACE bazel-bin bazel-out bazel-test bazel-testlogs internal/aac/BUILD.bazel internal/conf/BUILD.bazel internal/confenv/BUILD.bazel internal/confwatcher/BUILD.bazel internal/core/BUILD.bazel internal/externalcmd/BUILD.bazel internal/h264/BUILD.bazel internal/hls/BUILD.bazel internal/logger/BUILD.bazel internal/rlimit/BUILD.bazel internal/rtcpsenderset/BUILD.bazel internal/rtmp/BUILD.bazel nothing added to commit but untracked files present (use "git add" to track)
但是,如果您嘗試使用bazel build //...命令構(gòu)建項(xiàng)目,實(shí)際上您會(huì)看到許多關(guān)于未定義代碼庫(kù)的錯(cuò)誤。這是因?yàn)槲覀內(nèi)匀蝗鄙夙?xiàng)目依賴項(xiàng)的定義。然而,Gazelle 連這件事也可以為我們辦好(to_macro參數(shù)是可選的):
bazel run //:gazelle -- update-repos -from_file=go.mod -to_macro=deps.bzl%go_dependencies
此命令將生成一個(gè)文件名為deps.bzl的新文件(如果我們?cè)?code>WORKSPACE中有使用repository_macro指令定義過(guò),那么我們省略該to_macro指令),加載該文件,以導(dǎo)入構(gòu)建項(xiàng)目所需的所有代碼庫(kù)。
load("@bazel_gazelle//:deps.bzl", "go_repository")
def go_dependencies():
go_repository(
name = "com_github_alecthomas_template",
importpath = "github.com/alecthomas/template",
sum = "h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=",
version = "v0.0.0-20190718012654-fb15b899a751",
)
go_repository(
name = "com_github_alecthomas_units",
importpath = "github.com/alecthomas/units",
sum = "h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E=",
version = "v0.0.0-20190924025748-f65c72e2690d",
)
go_repository(
name = "com_github_aler9_gortsplib",
importpath = "github.com/aler9/gortsplib",
sum = "h1:Bf0hzdN1jUWsb5Mzezq1pd18EIBeKXxk5clIpHZJ1Lk=",
version = "v0.0.0-20210724151831-dae5a1f04033",
)
go_repository(
...
在這個(gè)代碼庫(kù)里,我實(shí)際上遇到了一個(gè)小問(wèn)題。構(gòu)建仍然是失敗的,因?yàn)樗鼘?dǎo)入的org_golang_x_tools被錯(cuò)誤地推斷為依賴項(xiàng)(通過(guò)從deps.bzl中刪除它來(lái)修復(fù)這個(gè)問(wèn)題)。您可以在我的項(xiàng)目分支:github.com/HappyCerber…上看到rtsp-simple-server的最終結(jié)果。
您可以在后續(xù)繼續(xù)使用 Gazelle 來(lái)管理依賴項(xiàng),這也是您可以將代碼庫(kù)引入基于 Bazel 的項(xiàng)目而無(wú)需實(shí)際轉(zhuǎn)換它的方法:
bazel run //:gazelle -- update-repos github.com/some/repo
密封測(cè)試(Hermetic tests)
您可能會(huì)遇到的最后一個(gè)問(wèn)題是密封測(cè)試。如果您看到測(cè)試因訪問(wèn)被拒絕、文件未找到或操作不允許失敗而失敗,那是因?yàn)?Bazel 強(qiáng)制執(zhí)行密封測(cè)試。這意味著每個(gè)測(cè)試都必須完全獨(dú)立并且獨(dú)立于任何其他測(cè)試。
對(duì)于單元測(cè)試,任何文件都需要作為測(cè)試的依賴項(xiàng)提供并通過(guò)運(yùn)行文件機(jī)制訪問(wèn)(github.com/bazelbuild/…)。
環(huán)境變量中提供了每個(gè)測(cè)試的臨時(shí)目錄,您將使用TEST_TMPDIR而不是傳統(tǒng)的os.TempDir()函數(shù)。
密封集成和系統(tǒng)測(cè)試需要從一開始就仔細(xì)設(shè)計(jì),因此轉(zhuǎn)換現(xiàn)有的此類測(cè)試可能很棘手。遺憾的是,我在這里沒(méi)有放之四海而皆準(zhǔn)的建議。
雖然將您的測(cè)試轉(zhuǎn)換為密封測(cè)試可能很煩人,但這是一項(xiàng)值得的努力,它將為您帶來(lái)更好的測(cè)試可重復(fù)性和更低的易碎性。
原文 翻譯自:Golang With Bazel
以上就是詳解如何使用Bazel構(gòu)建Golang程序的詳細(xì)內(nèi)容,更多關(guān)于Bazel構(gòu)建Golang程序的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Go語(yǔ)言panic和recover的用法實(shí)例
panic()和recover()是Go語(yǔ)言中用于處理錯(cuò)誤的兩個(gè)重要函數(shù),本文主要介紹了Go語(yǔ)言panic和recover的用法實(shí)例,panic()用于中止程序并引發(fā)panic,而recover()用于捕獲panic并恢復(fù)程序的執(zhí)行,感興趣的可以了解一下2024-01-01
go select編譯期的優(yōu)化處理邏輯使用場(chǎng)景分析
select 是 Go 中的一個(gè)控制結(jié)構(gòu),類似于用于通信的 switch 語(yǔ)句。每個(gè) case 必須是一個(gè)通信操作,要么是發(fā)送要么是接收。接下來(lái)通過(guò)本文給大家介紹go select編譯期的優(yōu)化處理邏輯使用場(chǎng)景分析,感興趣的朋友一起看看吧2021-06-06
Golang中基礎(chǔ)的命令行模塊urfave/cli的用法說(shuō)明
這篇文章主要介紹了Golang中基礎(chǔ)的命令行模塊urfave/cli的用法說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12
Go 語(yǔ)言中的select語(yǔ)句詳解及工作原理
在Go語(yǔ)言中,select語(yǔ)句是用于處理多個(gè)通道(channel)操作的一種控制結(jié)構(gòu),它類似于 switch 語(yǔ)句,本文給大家介紹Go 語(yǔ)言中的select語(yǔ)句詳解及工作原理,感興趣的朋友一起看看吧2025-04-04
基于gin的golang web開發(fā)之認(rèn)證利器jwt
這篇文章主要介紹了基于gin的golang web開發(fā)之認(rèn)證利器jwt,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12
使用Golang創(chuàng)建單獨(dú)的WebSocket會(huì)話
WebSocket是一種在Web開發(fā)中非常常見的通信協(xié)議,它提供了雙向、持久的連接,適用于實(shí)時(shí)數(shù)據(jù)傳輸和實(shí)時(shí)通信場(chǎng)景,本文將介紹如何使用 Golang 創(chuàng)建單獨(dú)的 WebSocket 會(huì)話,包括建立連接、消息傳遞和關(guān)閉連接等操作,需要的朋友可以參考下2023-12-12
go語(yǔ)言中的json與map相互轉(zhuǎn)換實(shí)現(xiàn)
本文主要介紹了go語(yǔ)言中的json與map相互轉(zhuǎn)換實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08
Go+Redis實(shí)現(xiàn)延遲隊(duì)列實(shí)操
這篇文章主要介紹了Go+Redis實(shí)現(xiàn)延遲隊(duì)列實(shí)操,延遲隊(duì)列是一種非常使用的數(shù)據(jù)結(jié)構(gòu),我們經(jīng)常有需要延遲推送處理消息的場(chǎng)景,比如延遲60秒發(fā)送短信,延遲30分鐘關(guān)閉訂單,消息消費(fèi)失敗延遲重試等2022-09-09

