標籤是目標的 ID,完整正式形式的標籤通常如下所示:
@@myrepo//my/app/main:app_binary
標籤的第一部分是存放區名稱 @@myrepo
。雙 @
語法表示這是標準存放區名稱,在工作區中不得重複。無論標籤出現在哪個環境,只要使用標準存放區名稱,就能明確識別目標。
正規存放區名稱通常是類似 @@rules_java++toolchains+local_jdk
的神秘字串。更常見的是具有「明顯」存放區名稱的標籤,如下所示:
@myrepo//my/app/main:app_binary
唯一的差別在於,存放區名稱的前置字串為一個 @
,而非兩個。這指的是名稱為「myrepo
」的存放區,該名稱可能會因這個標籤顯示的內容而有所不同。
在標籤參照的存放區與使用標籤的存放區相同的情況下,可以省略存放區名稱部分。因此,在第一個標籤內,通常會寫成@@myrepo
//my/app/main:app_binary
標籤的第二部分是不合格的套件名稱 my/app/main
,也就是相對於存放區根目錄的套件路徑。存放區名稱和不合格的套件名稱會共同組成完整套件名稱 @@myrepo//my/app/main
。如果標籤參照的是所用套件,則可省略套件名稱 (和選用的半形冒號)。因此,在 @@myrepo//my/app/main
內,這個標籤可以採用下列任一寫法:
app_binary
:app_binary
根據慣例,檔案會省略冒號,但規則會保留冒號,除此之外並無其他意義。
冒號後方的標籤部分 (app_binary
) 是不合格的目標名稱。如果與套件路徑的最後一個元件相符,則可省略該元件和半形冒號。因此,這兩個標籤的作用相同:
//my/app/lib
//my/app/lib:lib
套件子目錄中的檔案目標名稱是檔案路徑,與套件根目錄 (包含 BUILD
檔案的目錄) 相關。因此,這個檔案位於存放區的 my/app/main/testdata
子目錄中:
//my/app/main:testdata/input.txt
視使用情境而定,//my/app
和 @@some_repo//my/app
等字串有兩種含義:當 Bazel 預期標籤時,這些字串分別代表 //my/app:app
和 @@some_repo//my/app:app
。不過,當 Bazel 預期會有套件 (例如在 package_group
規格中),系統會參照包含該標籤的套件。
BUILD
檔案中常見的錯誤是使用 //my/app
參照套件,或是參照套件中的「所有」目標,但這是不正確的做法。請注意,這相當於 //my/app:app
,因此會命名目前存放區 my/app
套件中的 app
目標。
不過,在 package_group
的規格或 .bzl
檔案中,建議使用 //my/app
參照套件,因為這樣可清楚表明套件名稱是絕對名稱,且位於工作區的頂層目錄中。
相對標籤無法用於參照其他套件中的目標;在這種情況下,一律必須指定存放區 ID 和套件名稱。舉例來說,如果來源樹狀結構同時包含套件 my/app
和套件 my/app/testdata
(這兩個目錄各有自己的 BUILD
檔案),則後者套件包含名為 testdepot.zip
的檔案。以下是在 //my/app:BUILD
中參照這個檔案的兩種方式 (一種錯誤,一種正確):
錯誤 - testdata
是不同的套件,因此您無法使用相對路徑
testdata/testdepot.zip
正確 - 參照 testdata
的完整路徑
//my/app/testdata:testdepot.zip
以 @@//
開頭的標籤是指主要存放區,即使來自外部存放區,仍可正常運作。因此,從外部存放區參照時,@@//a/b/c
與 //a/b/c
不同。前者是指回主要存放區,後者則是在外部存放區本身尋找 //a/b/c
。如果您在主要存放區中編寫規則,並參照主要存放區中的目標,且這些規則會從外部存放區使用,這項功能就特別實用。
如要瞭解參照目標的不同方式,請參閱目標模式。
標籤的詞彙規格
標籤語法不建議使用對殼層具有特殊意義的中繼字元。這有助於避免意外的引號問題,並方便建構可操控標籤的工具和指令碼,例如 Bazel 查詢語言。
允許的目標名稱詳細資料如下。
目標名稱 - package-name:target-name
target-name
是套件中的目標名稱。規則名稱是 BUILD
檔案中規則宣告的 name
屬性值;檔案名稱則是相對於包含 BUILD
檔案的目錄路徑名稱。
目標名稱必須完全由 a
至 z
、A
至 Z
、0
至 9
這幾組字元,以及標點符號 !%-@^_"#$&'()*-+,;<=>?[]{|}~/.
組成。
檔案名稱必須是正常形式的相對路徑名稱,也就是說,檔案名稱不得以斜線開頭或結尾 (例如 /foo
和 foo/
都是禁止的),也不得包含多個連續斜線做為路徑分隔符號 (例如 foo//bar
)。同樣地,禁止使用上層參照 (..
) 和目前目錄參照 (./
)。
錯誤 - 請勿使用 ..
參照其他套件中的檔案
正確 - 使用
//package-name:filename
雖然在檔案目標名稱中使用 /
很常見,但請避免在規則名稱中使用 /
。特別是使用標籤的簡寫形式時,可能會讓讀者感到困惑。即使沒有 foo/bar/wiz
這類套件,標籤 //foo/bar/wiz
一律是 //foo/bar/wiz:wiz
的簡寫,絕不會參照 //foo:bar/wiz
,即使該目標存在也一樣。
不過,在某些情況下,使用斜線很方便,有時甚至必要。舉例來說,某些規則的名稱必須與其主要來源檔案相符,而該檔案可能位於套件的子目錄中。
套件名稱 - //package-name:target-name
套件名稱是包含 BUILD
檔案的目錄名稱,與包含存放區的頂層目錄相關。例如:my/app
。
在技術層面,Bazel 會強制執行下列操作:
- 套件名稱可用的字元包括小寫字母
a
至z
、大寫字母A
至Z
、數字0
至9
、字元! \"#$%&'()*+,-.;<=>?@[]^_`{|}
(沒錯,這裡有空格字元!),以及正斜線/
(因為這是目錄分隔符)。 - 套件名稱的開頭或結尾不得為正斜線字元
/
。 - 套件名稱不得包含子字串
//
。這沒有意義,因為對應的目錄路徑會是什麼? - 套件名稱不得包含
/./
、/../
或/.../
等子字串。這是為了避免在邏輯套件名稱和實體目錄名稱之間進行翻譯時發生混淆,因為路徑字串中的點字元具有語意意義。
實務層面:
- 如果語言的目錄結構對模組系統很重要 (例如 Java),請務必選擇該語言的有效 ID 做為目錄名稱。舉例來說,請勿以開頭數字命名,並避免使用特殊字元,尤其是底線和連字號。
- 雖然 Bazel 支援工作區根套件中的目標 (例如
//:foo
),但最好將該套件留空,這樣所有有意義的套件都會有描述性名稱。
規則
規則會指定輸入和輸出內容之間的關係,以及建構輸出內容的步驟。規則可以是多種不同類型 (有時稱為「規則類別」),可產生編譯的可執行檔和程式庫、測試可執行檔,以及其他支援的輸出內容,詳情請參閱「建構百科全書」。
BUILD
檔案會透過叫用 rules 宣告目標。
在以下範例中,我們會看到使用 cc_binary
規則宣告的目標 my_app
。
cc_binary(
name = "my_app",
srcs = ["my_app.cc"],
deps = [
"//absl/base",
"//absl/strings",
],
)
每個規則調用都有 name
屬性 (必須是有效的目標名稱),用於在 BUILD
檔案的套件中宣告目標。
每項規則都有一組屬性;特定規則的適用屬性,以及每個屬性的重要性和語意,都是規則種類的函式;如需規則及其對應屬性的清單,請參閱「建構百科全書」。每個屬性都有名稱和型別。屬性常見的類型包括整數、標籤、標籤清單、字串、字串清單、輸出標籤、輸出標籤清單。並非所有屬性都必須在每條規則中指定。因此,屬性會形成從鍵 (名稱) 到選用型別值的字典。
許多規則中都有 srcs
屬性,其類型為「標籤清單」;如果存在,其值為標籤清單,每個標籤都是目標的名稱,也就是這項規則的輸入內容。
在某些情況下,規則種類的名稱有些隨意,更有趣的是規則產生的檔案名稱,genrule 就是如此。詳情請參閱一般規則:genrule。
在其他情況下,名稱就非常重要:以 *_binary
和 *_test
規則為例,規則名稱會決定建構作業產生的可執行檔名稱。
這個目標的有向非循環圖稱為「目標圖」或「建構依附元件圖」,也是 Bazel 查詢工具運作的網域。
目標 | BUILD 檔案 |