簡介
Bazel是一個(gè)類似于Make埃难,Maven和Gradle的開源構(gòu)建和測試工具唐片。Bazel支持多種語言混編的項(xiàng)目并且可以根據(jù)不同的平臺(tái)輸出不同的構(gòu)建結(jié)果串前。
優(yōu)勢(shì):
- 加快構(gòu)建和測試速度:Bazel只重建必要的東西宵溅,借助緩存度硝,優(yōu)化依賴關(guān)系分析和并行執(zhí)行肿轨,可以獲得快速的和增量的構(gòu)建。
- 支持多種語言:支持Java, C++, Android, iOS, Go和各種其他語言蕊程,可以運(yùn)行在Windows椒袍,macOS和Linux上。
- 可擴(kuò)展:可以擴(kuò)展Bazel以支持您選擇的語言
基本概念
工作區(qū)
一個(gè)工作空間
是你的文件系統(tǒng)藻茂,它包含了源文件驹暑、符號(hào)鏈接以及輸出目錄玫恳。每個(gè)工作空間都有一個(gè)名為WORKSPACE
的文本文件,該文件可能為空优俘,或者包含對(duì)外部依賴引用
包
包被定義為包含文件的目錄
目標(biāo)
包是一個(gè)容器京办。包的元素稱為 目標(biāo)
,大多數(shù)目標(biāo)有兩種類型帆焕,即文件 和規(guī)則
文件進(jìn)一步分為兩種惭婿。 源文件、生成的文件
第二種目標(biāo)是規(guī)則叶雹,
如何使用bazel
- 下載并安裝Bazel财饥。
- 設(shè)置項(xiàng)目工作區(qū),這是Bazel查找構(gòu)建輸入和BUILD文件以及存儲(chǔ)構(gòu)建輸出的目錄折晦。
- 寫一個(gè)
BUILD
文件钥星,告訴Bazel要構(gòu)建什么以及如何構(gòu)建它。BUILD使用類似抽象的Python語言來編寫筋遭,指定Bazel將構(gòu)建的一組輸入及其依賴項(xiàng) - 在命令行運(yùn)行bazel打颤,bazel將輸出放在工作區(qū)內(nèi)
Bazel如何運(yùn)作
運(yùn)行構(gòu)建或測試時(shí),Bazel執(zhí)行以下操作:
- 加載BUILD與目標(biāo)相關(guān)的文件漓滔。
- 分析輸入及其依賴關(guān)系编饺,應(yīng)用指定的構(gòu)建規(guī)則。并生產(chǎn)
action
圖 - 對(duì)輸入執(zhí)行構(gòu)建操作响驴,直到生成最終構(gòu)建輸出透且。
由于所有以前的構(gòu)建工作都是緩存的,因此Bazel可以識(shí)別并重用緩存豁鲤,只重建或重新測試更改的內(nèi)容
什么是action圖
action圖表示各個(gè)構(gòu)建輸入和他們之間的關(guān)系秽誊,以及Bazel將執(zhí)行的構(gòu)建操作×章猓基于這張圖Bazel可以跟蹤文件內(nèi)容的更改
安裝
通過Homebrew安裝Bazel包锅论,如下所示:
brew install bazel
可以了,好了楣号!您可以通過運(yùn)行以下命令確認(rèn)Bazel已成功安裝:
bazel version
安裝后最易,您可以使用以下命令升級(jí)到較新版本的Bazel:
brew upgrade bazel
常用命令
$bazel help
用法:bazel <command> <options> ...
可用命令:
analyze-profile 分析構(gòu)建配置文件數(shù)據(jù)。
aquery 對(duì)分析后的操作圖執(zhí)行查詢炫狱。
build 構(gòu)建指定的目標(biāo)藻懒。
canonicalize-flags Canonicalize Bazel flags。
clean 刪除輸出文件视译,并可選擇停止服務(wù)器嬉荆。
cquery 執(zhí)行分析后依賴圖查詢。
dump 轉(zhuǎn)儲(chǔ)Bazel服務(wù)器進(jìn)程的內(nèi)部狀態(tài)酷含。
help 打印命令或索引的幫助鄙早。
info 顯示有關(guān)bazel服務(wù)器的運(yùn)行時(shí)信息汪茧。
fetch 獲取目標(biāo)的所有外部依賴項(xiàng)。
mobile-install 在移動(dòng)設(shè)備上安裝應(yīng)用程序蝶锋。
query 執(zhí)行依賴關(guān)系圖查詢陆爽。
run 運(yùn)行指定的目標(biāo)什往。
shutdown 停止Bazel服務(wù)器扳缕。
test 構(gòu)建并運(yùn)行指定的測試目標(biāo)。
version 打印Bazel的版本信息别威。
獲得更多幫助:
bazel help <command>
打印<command>的幫助和選項(xiàng)躯舔。
bazel幫助startup_options
JVM托管Bazel的選項(xiàng)。
bazel幫助目標(biāo)語法
解釋指定目標(biāo)的語法省古。
bazel幫助信息鍵
顯示info命令使用的鍵列表粥庄。
構(gòu)建Java項(xiàng)目
所有的Bazel構(gòu)建都是基于一個(gè) 工作區(qū)(workspace) 概念,它是文件系統(tǒng)中一個(gè)保存了全部源代碼的目錄豺妓,同時(shí)還將包含一些構(gòu)建后的輸出目錄的符號(hào)鏈接(例如:bazel-bin
和 bazel-out
等輸出目錄)惜互。工作區(qū)目錄可以隨意放在哪里,但是工作區(qū)的根目錄必須包含一個(gè)名為 WORKSPACE
的工作區(qū)配置文件琳拭。工作區(qū)配置文件可以是一個(gè)空文件训堆,也可以包含引用外部構(gòu)建輸出所需的 依賴關(guān)系。
-
創(chuàng)建BUILD文件
$ cd ~/demo $ mkdir -p src/main/java/com/test $ cat > src/main/java/com/test/Main.java <<EOF package com.test; public class Main { public static void main(String args[]) { Test.sayHi(); } } EOF $ cat > src/main/java/com/test/Test.java <<EOF package com.test; public class Test { public static void sayHi() { System.out.println("Hello World!"); } } EOF
Bazel通過工作區(qū)中所有名為
BUILD
的文件來解析需要構(gòu)建的項(xiàng)目信息白嘁,因此坑鱼,我們需要先在~/gitroot/my-project
目錄創(chuàng)建一個(gè)BUILD
構(gòu)建文件。下面是BUILD構(gòu)建文件的內(nèi)容:# ~/demo/BUILD java_binary( name = "test", srcs = glob(["**/*.java"]), main_class = "com.test.Main", )
BUILD文件采用類似Python的語法絮缅。雖然不能包含任意的Python語法鲁沥,但是BUILD文件中的每個(gè)構(gòu)建規(guī)則看起來都象是一個(gè)Python函數(shù)調(diào)用,而且你也可以用
"#"
開頭來添加單行注釋耕魄。java_binary
是一個(gè)構(gòu)建規(guī)則画恰。其中name
對(duì)應(yīng)一個(gè)構(gòu)建目標(biāo)的標(biāo)識(shí)符,可用用它來向Bazel指定構(gòu)建哪個(gè)項(xiàng)目吸奴。srcs
對(duì)應(yīng)一個(gè)源文件列表允扇,Bazel需要將這些源文件編譯為二進(jìn)制文件。其中glob(["**/*.java"])
表示遞歸包含每個(gè)子目錄中以每個(gè).java
為后綴名的文件奄抽。com.test.Main
指定包含main方法的類蔼两。 -
執(zhí)行構(gòu)建命令
$ bazel build //:test INFO: Analysed target //:test (0 packages loaded). INFO: Found 1 target... Target //:test up-to-date: bazel-bin/test.jar bazel-bin/test INFO: Elapsed time: 0.458s, Critical Path: 0.01s INFO: 0 processes. INFO: Build completed successfully, 1 total action $ bazel-bin/test Hello World!
Bazel和Android
構(gòu)建Android應(yīng)用程序
-
設(shè)置
ANDROID_HOME
變量將其設(shè)置為Android SDK的位置,默認(rèn)為
$HOME/Android/Sdk/
逞度。例如:
export ANDROID_HOME=$HOME/Android/Sdk/
為方便起見额划,請(qǐng)將以上語句添加到您的
~/.bashrc
文件中。 -
獲取示例項(xiàng)目
需要從GitHub獲取示例項(xiàng)目档泽。有兩個(gè)分支:
source-only
和master
俊戳。source-only
分支只包含對(duì)項(xiàng)目的源文件揖赴。master
分支包含源文件和完成的BazelWORKSPACE
和BUILD
文件。獲取
source-only
分支中的文件:cd ~/Desktop git clone -b source-only https://github.com/bazelbuild/examples
-
設(shè)置工作區(qū)
一個(gè)工作空間是包含一個(gè)或多個(gè)軟件項(xiàng)目中的源文件抑胎,以及一個(gè)目錄
WORKSPACE
文件燥滑,BUILD
是Bazel用來構(gòu)建軟件的說明文件。工作空間還可以包含指向輸出目錄的符號(hào)鏈接阿逃。Bazel本身對(duì)您在工作區(qū)中組織源文件的方式?jīng)]有任何要求铭拧。
-
創(chuàng)建一個(gè)
WORKSPACE
文件每個(gè)工作空間必須具有一個(gè)名為
WORKSPACE
位于頂級(jí)工作空間目錄中的文本文件。此文件可能為空,也可能包含對(duì)構(gòu)建軟件所需的外部依賴項(xiàng)引用。 -
更新
WORKSPACE
文件Bazel需要運(yùn)行Android SDK 構(gòu)建工具 并使用SDK庫來構(gòu)建應(yīng)用程序犹菱。這意味著您需要向
WORKSPACE
文件中添加一些信息,以便Bazel知道在哪里找到它們肪跋。為其他平臺(tái)構(gòu)建時(shí),不需要執(zhí)行此步驟土砂。Bazel會(huì)自動(dòng)從您環(huán)境中的設(shè)置中檢測Java州既,C ++和Objective-C編譯器的位置。將以下行添加到您的
WORKSPACE
文件中:android_sdk_repository( name = "androidsdk" )
這將使用
ANDROID_HOME
環(huán)境變量引用的Android SDK 萝映,并自動(dòng)檢測安裝在該位置的最高API級(jí)別和最新版本的構(gòu)建工具吴叶。或者,你可以明確地指定了Android SDK的位置锌俱,API級(jí)別晤郑,以及構(gòu)建工具的版本通過包括使用
path
,api_level
和build_tools_version
屬性贸宏。您可以指定這些屬性的任何子集:android_sdk_repository( name = "androidsdk", path = "~/Android/sdk", api_level = 25, build_tools_version = "26.0.1" )
如果使用ndk還需要通過在
WORKSPACE
文件中添加以下規(guī)則告訴Bazel在哪里找到它:android_ndk_repository( name = "androidndk" )
-
創(chuàng)建一個(gè)
BUILD
文件一個(gè)
BUILD
文件是描述一組輸入輸出之間的關(guān)系的文本文件造寝,添加android_library規(guī)則
一個(gè)
BUILD
文件包含Bazel的幾種不同類型的指令。最重要的類型是構(gòu)建規(guī)則吭练,它告訴Bazel如何從一組源文件或其他依賴項(xiàng)構(gòu)建最終軟件輸出诫龙。Bazel提供了兩種生成規(guī)則,
android_library
并且android_binary
鲫咽,首先使用android_library
規(guī)則告訴Bazel如何從應(yīng)用程序源代碼和資源文件構(gòu)建 Android庫模塊签赃。然后,您將使用android_binary
規(guī)則告訴它如何構(gòu)建Android應(yīng)用程序包分尸。將以下內(nèi)容添加到您的
BUILD
文件中:android_library( name = "activities", srcs = glob(["src/main/java/com/google/bazel/example/android/activities/*.java"]), custom_package = "com.google.bazel.example.android.activities", manifest = "src/main/java/com/google/bazel/example/android/activities/AndroidManifest.xml", resource_files = glob(["src/main/java/com/google/bazel/example/android/activities/res/**"]), )
锦聊,
android_library
構(gòu)建規(guī)則包含一組屬性,這些屬性指定Bazel從源文件構(gòu)建庫模塊所需的信息箩绍。另請(qǐng)注意孔庭,規(guī)則的名稱是activities
。您將使用此名稱作為規(guī)則的依賴項(xiàng)被android_binary
引用。添加android_binary規(guī)則
該
android_binary
規(guī)則.apk
為您的應(yīng)用構(gòu)建Android應(yīng)用程序包(文件)圆到。將以下內(nèi)容添加到構(gòu)建文件中:
android_binary( name = "android", custom_package = "com.google.bazel.example.android", manifest = "src/main/java/com/google/bazel/example/android/AndroidManifest.xml", resource_files = glob(["src/main/java/com/google/bazel/example/android/res/**"]), deps = [":activities"], )
此處怎抛,該
deps
屬性引用activities
您添加到上述BUILD
文件的規(guī)則的輸出。這意味著芽淡,當(dāng)Bazel構(gòu)建此規(guī)則的輸出時(shí)马绝,它首先檢查activities
庫規(guī)則的輸出是否已構(gòu)建并且是最新的。如果沒有挣菲,它會(huì)構(gòu)建它富稻,然后使用該輸出來構(gòu)建應(yīng)用程序包文件。 -
構(gòu)建應(yīng)用程序
輸入以下內(nèi)容來構(gòu)建示例應(yīng)用程序:
bazel build //android:android
該
build
子命令指示巴澤勒構(gòu)建跟蹤目標(biāo)己单。目標(biāo)被指定為BUILD
文件內(nèi)部的構(gòu)建規(guī)則的名稱唉窃,以及相對(duì)于工作區(qū)目錄的程序包路徑。請(qǐng)注意纹笼,有時(shí)可以省略包路徑或目標(biāo)名稱,具體取決于命令行中的當(dāng)前工作目錄和目標(biāo)名稱苟跪。Bazel現(xiàn)在啟動(dòng)并構(gòu)建示例應(yīng)用程序廷痘。在構(gòu)建過程中,其輸出將顯示類似于以下內(nèi)容:
INFO: Found 1 target... Target //android:android up-to-date: bazel-bin/android/android_deploy.jar bazel-bin/android/android_unsigned.apk bazel-bin/android/android.apk INFO: Elapsed time: 7.237s, Critical Path: 5.81s
-
運(yùn)行該應(yīng)用程序
bazel mobile-install
命令從命令行將應(yīng)用程序部署到連接的Android設(shè)備或模擬器 件已。此命令使用Android Debug Bridge(adb
)與設(shè)備通信笋额。您必須將設(shè)備設(shè)置為在部署之前adb
按照Android Debug Bridge中的說明 使用。您還可以選擇在Android Studio中包含的Android模擬器上安裝該應(yīng)用篷扩。在執(zhí)行以下命令之前兄猩,請(qǐng)確保模擬器正在運(yùn)行。輸入以下內(nèi)容:
bazel mobile-install //android:android
Android規(guī)則
描述了可用于使用Bazel構(gòu)建和測試Android應(yīng)用程序的規(guī)則
android_binary
屬性 | 描述 |
---|---|
name | 規(guī)則的唯一名稱 |
deps | 要鏈接到二進(jìn)制目標(biāo)的其他庫的列表鉴未。允許庫類型是:android_library 枢冤, java_library 與.so
|
srcs | 源文件列表 |
aapt_version | aapt版本。aapt_version = "aapt" :使用aapt默認(rèn)值铜秆;aapt_version = "aapt2":使用aapt2淹真。這是新的資源打包系統(tǒng),可提供改進(jìn)的增量資源處理连茧,更小的apks等核蘸。 |
assets | 要打包的資產(chǎn)列表 |
custom_package | 包名 |
debug_key | apk簽名文件 |
dex_shards | dex的分片數(shù)。這使得dex的速度更快啸驯,但代價(jià)是應(yīng)用安裝和啟動(dòng)時(shí)間客扎。二進(jìn)制文件越大,應(yīng)使用的分片越多罚斗。25是開始試驗(yàn)的好價(jià)值徙鱼。 |
dexopts | 生成classes.dex時(shí)dx工具的附加命令行標(biāo)志 |
main_dex_list | 這些類文件定義的類放在主classes.dex中 |
manifest | Android清單文件的名稱AndroidManifest.xml
|
multidex | 是否將代碼拆分為多個(gè)dex文件 |
proguard_specs | 用作Proguard規(guī)范的文件 |
shrink_resources | 是否執(zhí)行資源縮減。二進(jìn)制文件未使用的資源將從APK中刪除惰聂。 |
aar_import
屬性 | 描述 |
---|---|
name | 此規(guī)則的唯一名稱疆偿。 |
aar | aar文件 |
例子:
aar_import(
name =“google-vr-sdk”咱筛,
aar =“gvr-android-sdk / libraries / sdk-common-1.10.0.aar”,
)
android_binary(
name =“app”杆故,
manifest =“AndroidManifest.xml”迅箩,
srcs = glob([“**。java”])处铛,
deps = [“:google-vr-sdk”]饲趋,
)
android_library
主要規(guī)則同android_binary
android_ndk_repository
配置Bazel使用Android NDK。目前支持NDK版本10到16撤蟆。
還需要配置android_sdk_repository
到您的WORKSPACE
文件中
例子
android_ndk_repository(
name =“androidndk”奕塑,
)
上面的示例將從您的Android NDK中找到$ANDROID_NDK_HOME
并檢測它支持的最高API級(jí)別。
android_ndk_repository(
name =“androidndk”家肯,
path =“./android-ndk-r12b”龄砰,
api_level = 24,
)
上面的示例將使用位于工作區(qū)內(nèi)的 ./android-ndk-r12b
#BUILD
cc_library(
name =“jni”讨衣,
srcs = [“jni.cc”]换棚,
deps = [“@androidndk //:cpufeatures”],
)
android_sdk_repository
配置Bazel使用本地Android SDK來支持構(gòu)建Android目標(biāo)反镇。
為Bazel設(shè)置Android SDK的最低要求是
android_sdk_repository
在
WORKSPACE
文件中添加名為“androidsdk” 的規(guī)則固蚤,并將
$ANDROID_HOME
環(huán)境變量設(shè)置為Android SDK的路徑。默認(rèn)情況下歹茶,Bazel將使用安裝在Android SDK中的最高Android API級(jí)別和構(gòu)建工具版本夕玩。
android_sdk_repository(
name =“androidsdk”,
)
為了確保重現(xiàn)建立的path
惊豺,api_level
并且 build_tools_version
屬性可以設(shè)置為特定值燎孟。如果Android SDK沒有安裝指定的API級(jí)別或構(gòu)建工具版本,則構(gòu)建將失敗扮叨。
android_sdk_repository(
name =“androidsdk”缤弦,
path =“./ sdk”,
api_level = 19彻磁,
build_tools_version =“25.0.0”碍沐,
)
Mobile-install
用于構(gòu)建.apk的傳統(tǒng)Android工具鏈需要許多單片,順序步驟衷蜓,所有這些都必須完成才能構(gòu)建Android應(yīng)用程序累提。在大型項(xiàng)目中,等待五分鐘建立單線改變并不罕見磁浇。
bazel mobile-install
通過結(jié)合使用更改修剪斋陪,工作分片和Android內(nèi)部的巧妙操作,可以更快地為Android進(jìn)行迭代開發(fā),所有這些都不會(huì)更改任何應(yīng)用程序的代碼无虚。
傳統(tǒng)應(yīng)用安裝的問題
我們發(fā)現(xiàn)了構(gòu)建Android應(yīng)用程序的以下瓶頸:
- 默認(rèn)情況下缔赠,“dx”在構(gòu)建中,并不知道如何重用以前構(gòu)建的工作:它會(huì)再次重新dex每個(gè)方法友题,即使只更改了一個(gè)方法嗤堰。
- 將數(shù)據(jù)上傳到設(shè)備。adb不使用USB 2.0連接的全部帶寬度宦,較大的應(yīng)用程序可能需要花費(fèi)大量時(shí)間上傳踢匣。整個(gè)應(yīng)用程序上傳,即使只有小部分發(fā)生了變化戈抄,例如資源或單個(gè)方法离唬,因此這是一個(gè)主要的瓶頸。
bazel mobile-install
做出以下改進(jìn):
- Sharded dexing划鸽。在構(gòu)建應(yīng)用程序的Java代碼之后输莺,Bazel將類文件分片為大致相等大小的部分,并
dx
在它們上單獨(dú)調(diào)用漾稀。dx
在自上次構(gòu)建后未更改的分片上未調(diào)用模闲。 - 增量文件傳輸。Android資源崭捍,.dex文件和本機(jī)庫將從主.apk中刪除,并存儲(chǔ)在單獨(dú)的移動(dòng)安裝目錄下啰脚。這使得可以獨(dú)立更新代碼和Android資源殷蛇,而無需重新安裝整個(gè)應(yīng)用程序。因此橄浓,傳輸文件所花費(fèi)的時(shí)間更少粒梦,只有已更改的.dex文件才會(huì)在設(shè)備上重新編譯。
- 從.apk外部加載應(yīng)用程序的部分內(nèi)容荸实。一個(gè)小的存根應(yīng)用程序放入.apk中匀们,從設(shè)備上的移動(dòng)安裝目錄加載Android資源,Java代碼和本機(jī)代碼准给,然后將控制權(quán)轉(zhuǎn)移到實(shí)際的應(yīng)用程序
Sharded dexing
Sharded dexing相當(dāng)簡單:一旦構(gòu)建了.jar文件泄朴, 工具 會(huì)將它們分割成大小相等的單獨(dú).jar文件,然后調(diào)用 dx
自上一次構(gòu)建以來更改的文件露氮。確定dex的哪些分片不是特定于Android的邏輯:它只使用Bazel的一般更改修剪算法祖灰。
第一個(gè)版本的分片算法只是按字母順序排序.class文件,然后將列表切割成相等大小的部分畔规,但事實(shí)證明這是次優(yōu)的:如果添加或刪除了一個(gè)類(甚至是嵌套的或匿名的)局扶,它會(huì)導(dǎo)致所有類按字母順序移動(dòng)一個(gè),從而導(dǎo)致再次刪除這些分片。因此三妈,我們決定不分割單個(gè)類畜埋,而是使用Java包。當(dāng)然畴蒲,如果添加或刪除新包悠鞍,這仍然會(huì)導(dǎo)致許多分片變形,但這比添加或刪除單個(gè)類要少得多饿凛。
分片數(shù)由BUILD文件控制(使用 android_binary.dex_shards
屬性)狞玛。在一個(gè)理想的世界中,Bazel會(huì)自動(dòng)確定有多少分片是最好的涧窒,但Bazel目前必須知道在執(zhí)行任何分片之前的動(dòng)作集(即在構(gòu)建期間要執(zhí)行的命令)心肪,因此它無法確定最佳分片數(shù),因?yàn)樗恢缿?yīng)用程序中最終會(huì)有多少Java類纠吴。一般來說硬鞍,分片越多,構(gòu)建和安裝就越快戴已,但app啟動(dòng)速度變慢固该,因?yàn)閯?dòng)態(tài)鏈接器必須做更多的工作。通常在10到50個(gè)碎片之間糖儡。
增量文件傳輸
構(gòu)建應(yīng)用程序后伐坏,下一步是安裝它,最好盡可能少握联。安裝包括以下步驟:
- 安裝.apk(即
adb install
) - 將.dex文件桦沉,Android資源和本機(jī)庫上載到移動(dòng)安裝目錄
第一步?jīng)]有太多增量:應(yīng)用程序是否已安裝。Bazel目前依賴于用戶來指示它是否應(yīng)該通過--incremental
命令行選項(xiàng)執(zhí)行此步驟金闽,因?yàn)樗鼰o法在所有情況下確定是否有必要纯露。
在第二步中,將構(gòu)建中的應(yīng)用程序文件與設(shè)備上清單文件進(jìn)行比較代芜,該文件列出設(shè)備上的應(yīng)用程序文件及其校驗(yàn)和埠褪。將任何新文件上載到設(shè)備,更新任何已更改的文件挤庇,并從設(shè)備中刪除已刪除的所有文件钞速。如果清單不存在,則假定需要上載每個(gè)文件罚随。
請(qǐng)注意玉工,可以通過更改設(shè)備上的文件來欺騙增量安裝算法,但不能清除清單中的校驗(yàn)和淘菩。我們可以通過計(jì)算設(shè)備上文件的校驗(yàn)和來防范這種情況遵班,但這被認(rèn)為不值得增加安裝時(shí)間屠升。
性能
通常,bazel mobile-install
只需稍加更改即可構(gòu)建和安裝大型應(yīng)用程序狭郑,速度提高4倍到10倍腹暖。我們?yōu)橐恍〨oogle產(chǎn)品計(jì)算了以下數(shù)字:
[圖片上傳失敗...(image-62a112-1539661819363)]
AndroidStudio集成
無
NDK
要為Android構(gòu)建C ++,只需將cc_library
依賴項(xiàng)添加到您的 android_binary
或android_library
規(guī)則中翰萨。
給定Android應(yīng)用程序的以下BUILD文件:
# In <project>/app/src/main/BUILD.bazel
cc_library(
name = "jni_lib",
srcs = ["cpp/native-lib.cpp"],
)
android_library(
name = "lib",
srcs = ["java/com/example/android/bazel/MainActivity.java"],
resource_files = glob(["res/**/*"]),
custom_package = "com.example.android.bazel",
manifest = "LibraryManifest.xml",
deps = [":jni_lib"],
)
android_binary(
name = "app",
deps = [":lib"],
manifest = "AndroidManifest.xml",
)
此BUILD文件生成以下目標(biāo)圖:
如何工作
Bazel 在構(gòu)建目標(biāo)時(shí)經(jīng)歷了三個(gè)步驟:
- 在加載階段脏答,Bazel會(huì)解析
BUILD
正在構(gòu)建的目標(biāo)BUILD
文件以及文件傳遞依賴的所有文件。 - 在分析階段亩鬼,Bazel構(gòu)建了構(gòu)建指定目標(biāo)所需的操作圖殖告。
- 在執(zhí)行階段,Bazel運(yùn)行這些操作雳锋。
在分析過程中黄绩,Bazel為每個(gè)正在構(gòu)建的目標(biāo)及其傳遞依賴性運(yùn)行規(guī)則。每個(gè)規(guī)則都會(huì)生成并記錄它依賴的所有操作玷过。
Android資源
Android庫構(gòu)建過程與普通Java構(gòu)建過程的不同之處是Android 資源爽丹。資源是任何不是代碼的東西 - 字符串,圖像辛蚊,布局等等粤蝎。
Bazel生成R.java文件(以及相關(guān) 文件R.class
和R.txt
文件)以包含對(duì)可用資源的引用。這些R文件包含開發(fā)人員可用于引用其資源的整數(shù)資源ID袋马。在應(yīng)用程序中初澎,每個(gè)資源ID都指向一個(gè)唯一資源。
用aapt
和aapt2
處理資源
Bazel支持使用原始Android資源處理器aapt
或新版本處理資源aapt2
虑凛。這兩種方法基本相似谤狡,但有一些重要的區(qū)別。
Bazel經(jīng)歷了三個(gè)步驟來構(gòu)建資源卧檐。
首先,Bazel序列化定義資源的文件焰宣。在aapt
管道中霉囚,解析操作將有關(guān)資源的信息序列化為 symbols.bin
文件。在aapt2
管道中匕积,動(dòng)作調(diào)用aapt2 編譯命令盈罐,該命令將信息序列化為 aapt2
使用的格式。
接下來闪唆,序列化資源與從依賴項(xiàng)繼承的類似序列化資源合并盅粪。識(shí)別具有相同名稱的資源之間的沖突,并且如果可能悄蕾,在此合并期間解決票顾。values
資源文件的內(nèi)容通常是顯式合并的础浮。對(duì)于其他文件,如果來自目標(biāo)或其依賴項(xiàng)的資源具有相同的名稱和限定符奠骄,則會(huì)比較文件的內(nèi)容豆同,如果它們不同,則會(huì)生成警告含鳞,并選擇使用最后提供的資源影锈。
最后,Bazel檢查目標(biāo)的資源是否合理并將其打包蝉绷。在aapt
中調(diào)用 aapt
package命令鸭廷,在aapt2
中調(diào)用aapt2link命令。任何格式錯(cuò)誤的資源或?qū)Σ豢捎觅Y源的引用都會(huì)導(dǎo)致失敗熔吗,如果未遇到任何故障辆床,生成 R.java
文件和資源APK。
Android庫
一個(gè) android_library
規(guī)則是一個(gè)非常簡單的規(guī)則磁滚,建立并組織一個(gè)Android庫在另一個(gè)Android的目標(biāo)使用佛吓。在分析階段,基本上生成了三組操作:
首先垂攘,Bazel處理庫的資源维雇, 如上所述。
接下來是庫的實(shí)際編譯晒他。這主要是使用常規(guī)的Bazel Java編譯路徑吱型。最大的區(qū)別是R.class
資源處理中生成的 文件也包含在編譯路徑中(但不會(huì)被使用者繼承,因?yàn)樾枰獮槊總€(gè)目標(biāo)重新生成R文件)陨仅。
最后津滞,Bazel對(duì)編譯的代碼做了一些額外的工作:
編譯后的
.class
替換只有java8才支持的屬性。處理后的
.class
文件轉(zhuǎn)換.dex
hjar
使用.java
源文件生成一個(gè)jar
文件灼伤。方法體和私有字段將被刪除触徐,依賴時(shí)將依賴于此庫.jar
文件。由于這些jar只包含庫的接口狐赡,因此當(dāng)私有字段或方法實(shí)現(xiàn)發(fā)生更改時(shí)撞鹉,不需要重新編譯依賴庫(只有在庫的接口更改時(shí)才需要重新編譯它們),這樣可以加快構(gòu)建速度颖侄。
Android二進(jìn)制文件
對(duì)于二進(jìn)制文件鸟雏,三個(gè)主要的資源處理操作(解析,合并和驗(yàn)證)都合并為一個(gè)大型操作览祖。在庫中孝鹊,Java編譯可以在驗(yàn)證仍在進(jìn)行時(shí)啟動(dòng),但在二進(jìn)制文件中展蒂,由于我們需要驗(yàn)證的最終資源ID又活,因此我們無法利用類似的并行化苔咪。由于創(chuàng)建更多操作總是會(huì)帶來很小的成本,并且沒有可用的并行化來彌補(bǔ)它皇钞,因此單個(gè)資源處理操作實(shí)際上更有效悼泌。
在二進(jìn)制文件中,Java代碼被編譯夹界,刪除和dexed馆里,就像在庫中一樣。但是可柿,之后鸠踪,.dex
二進(jìn)制.dex
文件與依賴項(xiàng)中的文件合并在一起。
Bazel還將來自依賴項(xiàng)的已編譯C
和C++
本機(jī)代碼鏈接到.so
合并的.dex
文件复斥,.so
文件和資源APK都組合在一起構(gòu)建一個(gè)初始的二進(jìn)制APK营密,然后進(jìn)行 zipaligned以生成一個(gè)未簽名的APK。最后目锭,使用二進(jìn)制文件的調(diào)試密鑰對(duì)未簽名的APK進(jìn)行簽名评汰,以生成簽名的APK。
合并的.dex
文件與資源APK組合以構(gòu)建初始二進(jìn)制APK痢虹,然后將其 壓縮以生成未簽名的APK被去。最后,使用二進(jìn)制文件的調(diào)試密鑰對(duì)未簽名的APK進(jìn)行簽名奖唯,以生成簽名的APK惨缆。
ProGuarded Android二進(jìn)制文件
Bazel支持 針對(duì)目標(biāo)運(yùn)行ProGuardandroid_binary
以優(yōu)化它們并減小其尺寸。
ProGuarding使用一個(gè)deploy.jar
文件丰捷,一個(gè).jar
包含所有二進(jìn)制Java字節(jié)碼的文件坯墨,由二進(jìn)制文件的desugared(但不是dexed).class
文件以及二進(jìn)制文件的傳遞運(yùn)行時(shí).jar
文件創(chuàng)建。(此 deploy.jar
文件是所有android_binary
目標(biāo)的輸出病往,但在沒有ProGuarding的構(gòu)建中它不起重要作用捣染。)
基于一系列Proguard規(guī)范(來自二進(jìn)制及其傳遞依賴性)的信息,ProGuard進(jìn)行了多次傳遞deploy.jar
停巷,以優(yōu)化代碼液斜,刪除未使用的方法和字段,并縮短和混淆方法和字段的名稱剩下的叠穆。除了生成的proguarded .jar
文件之外,ProGuard還會(huì)輸出從舊名稱到新名稱的方法和字段的映射臼膏。
外部依賴
Bazel項(xiàng)目
想使用bazel項(xiàng)目硼被,您可以使用 local_repository
, git_repository
或http_archive
從本地文件系統(tǒng)中渗磅,一個(gè)Git倉庫或下載嚷硫。
例如检访,假設(shè)您正在處理一個(gè)項(xiàng)目,my-project/
并且您希望依賴同事的項(xiàng)目coworkers-project
仔掸。這兩個(gè)項(xiàng)目都使用Bazel脆贵,因此您可以將同事的項(xiàng)目添加為外部依賴項(xiàng),然后使用您的同事從您自己的BUILD文件中定義的任何目標(biāo)起暮。您可以將以下內(nèi)容添加到my_project/WORKSPACE
:
local_repository(
name = "coworkers_project",
path = "/path/to/coworkers-project",
)
如果您的同事有目標(biāo)//foo:bar
卖氨,您的項(xiàng)目可以將其稱為 @coworkers_project//foo:bar
。
非Bazel項(xiàng)目
前綴new_
(例如 new_local_repository
负懦, new_git_repository
和new_http_archive
)允許您創(chuàng)建不使用Bazel的目標(biāo)筒捺。
例如,假設(shè)您正在處理一個(gè)項(xiàng)目my-project/
纸厉,并且您希望依賴于您的同事的項(xiàng)目系吭,coworkers-project/
。您的同事的項(xiàng)目用于make
構(gòu)建颗品,但您希望依賴于它生成的.so文件之一肯尺。為此,請(qǐng)將以下內(nèi)容添加到my_project/WORKSPACE
:
new_local_repository(
name = "coworkers_project",
path = "/path/to/coworkers-project",
build_file = "coworker.BUILD",
)
build_file
指定要覆蓋現(xiàn)有項(xiàng)目的BUILD文件躯枢,例如:
cc_library(
name = "some-lib",
srcs = glob(["**"]),
visibility = ["http://visibility:public"],
)
然后则吟,您可以依賴@coworkers_project//:some-lib
項(xiàng)目的BUILD文件。
外部包裝
使用規(guī)則maven_jar
(以及可選的規(guī)則maven_server
)從Maven存儲(chǔ)庫下載jar并使其可用作Java依賴項(xiàng)闺金。
maven_jar(name逾滥,artifact,repository败匹,server寨昙,sha1,sha1_src)
從Maven下載jar并使其可用作Java依賴項(xiàng)掀亩。請(qǐng)注意舔哪,maven_jar名稱用作存儲(chǔ)庫名稱,因此受限于管理工作空間名稱的規(guī)則:它不能包含破折號(hào)和點(diǎn)(有關(guān)確切規(guī)范槽棍,請(qǐng)參閱 有關(guān)工作空間名稱的文檔)捉蚤。按照慣例,maven_jar名稱應(yīng)該與工件名稱匹配炼七,用下劃線替換非法字符并且不使用版本缆巧。例如,artifact = "org.apache.commons:commons-lang3:3.4"
應(yīng)該具有 的規(guī)則name = "org_apache_commons_commons_lang3"
豌拙。
例子
假設(shè)當(dāng)前的repostory包含一個(gè)需要依賴Guava的java_library目標(biāo)陕悬。使用Maven,這個(gè)依賴項(xiàng)將在pom.xml文件中定義為:
<依賴性>
<的groupId> com.google.guava </的groupId>
<artifactId的>番石榴</ artifactId的>
<版本> 18.0 </版本>
</依賴性>
使用Bazel按傅,將以下行添加到WORKSPACE文件中:
maven_jar(
name =“com_google_guava_guava”捉超,
artifact =“com.google.guava:guava:18.0”胧卤,
sha1 =“cce0823396aa693798f8882e64213b1772032b09”,
sha1_src =“ad97fe8faaf01a3d3faacecd58e8fa6e78a973ca”拼岳,
)
目標(biāo)可以指定@com_google_guava_guava//jar
依賴于此jar的依賴項(xiàng)枝誊。
參數(shù)
屬性 | |
---|---|
name |
Name; required 此規(guī)則的唯一名稱。 |
artifact |
String; optional 使用Maven坐標(biāo)描述Maven工件 惜纸。這些描述的形式為<groupId>:<artifactId>:<version>叶撒,請(qǐng)參閱下面的文檔以獲取示例。 |
repository |
String; optional 用于從中獲取jar的Maven存儲(chǔ)庫的URL堪簿。這個(gè)或者server 可以指定痊乾。默認(rèn)為Maven Central(“central.maven.org”)。 |
server |
String; optional 用于此工件的maven_server椭更。這個(gè)或者repository 可以指定哪审。 |
sha1 |
String; optional 所需jar的SHA-1哈希。如果下載的jar與此哈希不匹配虑瀑,則Bazel將出錯(cuò)湿滓。由于遠(yuǎn)程文件可以更改,因此省略SHA-1是一種安全風(fēng)險(xiǎn)舌狗。最多省略此字段將使您的構(gòu)建非密封叽奥。可以選擇使開發(fā)更容易痛侍,但應(yīng)在發(fā)貨前進(jìn)行設(shè)置朝氓。 |
sha1_src |
String; optional 所需jar源文件的SHA-1哈希。 |
maven_server
maven_server(name主届,settings_file赵哲,url)
如何訪問Maven存儲(chǔ)庫。
這是來自pom.xml文件的<repository>定義和settings.xml文件中的<server>定義的組合君丁。
運(yùn)用 maven_server
maven_jar
規(guī)則可以maven_server
在其 server
字段中指定a的名稱枫夺。例如,假設(shè)我們有以下WORKSPACE文件:
maven_jar(
name =“junit”,
artifact =“junit:junit-dep:4.10”,
server =“my_server”,
)
maven_server(
name =“my_server”绘闷,
url =“http://intranet.mycorp.net”
)
這指定應(yīng)使用?/ .m2 / settings.xml中的身份驗(yàn)證信息(具體地說橡庞,具有id的服務(wù)器的設(shè)置)從http://intranet.mycorp.net下載junit my_server
指定默認(rèn)服務(wù)器
如果maven_server
使用name
“默認(rèn)” 創(chuàng)建一個(gè),它將用于任何maven_jar
未指定server
nor的 repository
印蔗。如果沒有maven_server
命名默認(rèn)值扒最,則默認(rèn)將從Maven Central獲取而不啟用身份驗(yàn)證。
參數(shù)
屬性 | |
---|---|
name |
Name; required 此規(guī)則的唯一名稱华嘹。 |
settings_file |
String; optional settings.xml文件的路徑扼倘。用于測試。如果未指定,則默認(rèn)使用 $M2_HOME/conf/settings.xml 全局設(shè)置和 $HOME/.m2/settings.xml 用戶設(shè)置再菊。 |
url |
String; optional 用于訪問服務(wù)器的URL。例如颜曾,Maven Central(默認(rèn)情況下纠拔,不需要定義)將被指定為url = "http://central.maven.org/maven2/" 。 |