Bazel構(gòu)建

簡介

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

  1. 下載并安裝Bazel财饥。
  2. 設(shè)置項(xiàng)目工作區(qū),這是Bazel查找構(gòu)建輸入和BUILD文件以及存儲(chǔ)構(gòu)建輸出的目錄折晦。
  3. 寫一個(gè)BUILD文件钥星,告訴Bazel要構(gòu)建什么以及如何構(gòu)建它。BUILD使用類似抽象的Python語言來編寫筋遭,指定Bazel將構(gòu)建的一組輸入及其依賴項(xiàng)
  4. 在命令行運(yùn)行bazel打颤,bazel將輸出放在工作區(qū)內(nèi)

Bazel如何運(yùn)作

運(yùn)行構(gòu)建或測試時(shí),Bazel執(zhí)行以下操作:

  1. 加載BUILD與目標(biāo)相關(guān)的文件漓滔。
  2. 分析輸入及其依賴關(guān)系编饺,應(yīng)用指定的構(gòu)建規(guī)則。并生產(chǎn)action
  3. 對(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-binbazel-out 等輸出目錄)惜互。工作區(qū)目錄可以隨意放在哪里,但是工作區(qū)的根目錄必須包含一個(gè)名為 WORKSPACE 的工作區(qū)配置文件琳拭。工作區(qū)配置文件可以是一個(gè)空文件训堆,也可以包含引用外部構(gòu)建輸出所需的 依賴關(guān)系

  1. 創(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方法的類蔼两。

  2. 執(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)用程序

  1. 設(shè)置ANDROID_HOME變量

    將其設(shè)置為Android SDK的位置,默認(rèn)為$HOME/Android/Sdk/ 逞度。

    例如:

    export ANDROID_HOME=$HOME/Android/Sdk/
    

    為方便起見额划,請(qǐng)將以上語句添加到您的~/.bashrc文件中。

  2. 獲取示例項(xiàng)目

    需要從GitHub獲取示例項(xiàng)目档泽。有兩個(gè)分支:source-onlymaster俊戳。source-only分支只包含對(duì)項(xiàng)目的源文件揖赴。master分支包含源文件和完成的Bazel WORKSPACEBUILD文件。

    獲取source-only 分支中的文件:

    cd ~/Desktop
    git clone -b source-only https://github.com/bazelbuild/examples
    
  3. 設(shè)置工作區(qū)

    一個(gè)工作空間是包含一個(gè)或多個(gè)軟件項(xiàng)目中的源文件抑胎,以及一個(gè)目錄WORKSPACE文件燥滑,BUILD是Bazel用來構(gòu)建軟件的說明文件。工作空間還可以包含指向輸出目錄的符號(hào)鏈接阿逃。

    Bazel本身對(duì)您在工作區(qū)中組織源文件的方式?jīng)]有任何要求铭拧。

  4. 創(chuàng)建一個(gè)WORKSPACE文件

    每個(gè)工作空間必須具有一個(gè)名為WORKSPACE位于頂級(jí)工作空間目錄中的文本文件。此文件可能為空,也可能包含對(duì)構(gòu)建軟件所需的外部依賴項(xiàng)引用。

  5. 更新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)建工具的版本通過包括使用 pathapi_levelbuild_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"
    )
    
  6. 創(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)用程序包文件。

  7. 構(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
    
  8. 運(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)用程序后伐坏,下一步是安裝它,最好盡可能少握联。安裝包括以下步驟:

  1. 安裝.apk(即adb install
  2. 將.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_binaryandroid_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)圖:

image

如何工作

Bazel 在構(gòu)建目標(biāo)時(shí)經(jīng)歷了三個(gè)步驟

  1. 加載階段脏答,Bazel會(huì)解析BUILD正在構(gòu)建的目標(biāo)BUILD文件以及文件傳遞依賴的所有文件。
  2. 分析階段亩鬼,Bazel構(gòu)建了構(gòu)建指定目標(biāo)所需的操作圖殖告。
  3. 執(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.classR.txt文件)以包含對(duì)可用資源的引用。這些R文件包含開發(fā)人員可用于引用其資源的整數(shù)資源ID袋马。在應(yīng)用程序中初澎,每個(gè)資源ID都指向一個(gè)唯一資源。

aaptaapt2處理資源

image

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庫

image

一個(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ì)編譯的代碼做了一些額外的工作:

  1. 編譯后的.class替換只有java8才支持的屬性。

  2. 處理后的 .class文件轉(zhuǎn)換.dex

  3. hjar使用.java源文件生成一個(gè) jar文件灼伤。方法體和私有字段將被刪除触徐,依賴時(shí)將依賴于此庫.jar文件。由于這些jar只包含庫的接口狐赡,因此當(dāng)私有字段或方法實(shí)現(xiàn)發(fā)生更改時(shí)撞鹉,不需要重新編譯依賴庫(只有在庫的接口更改時(shí)才需要重新編譯它們),這樣可以加快構(gòu)建速度颖侄。

Android二進(jìn)制文件

image

對(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)的已編譯CC++本機(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)制文件

image

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_repositorygit_repositoryhttp_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_repositorynew_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未指定servernor的 repository印蔗。如果沒有maven_server命名默認(rèn)值扒最,則默認(rèn)將從Maven Central獲取而不啟用身份驗(yàn)證。

參數(shù)

屬性
name Name; required此規(guī)則的唯一名稱华嘹。
settings_file String; optionalsettings.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/"
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末泛豪,一起剝皮案震驚了整個(gè)濱河市稠诲,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌诡曙,老刑警劉巖臀叙,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異价卤,居然都是意外死亡劝萤,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門慎璧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來床嫌,“玉大人,你說我怎么就攤上這事胸私⊙岽Γ” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵岁疼,是天一觀的道長阔涉。 經(jīng)常有香客問我,道長捷绒,這世上最難降的妖魔是什么瑰排? 我笑而不...
    開封第一講書人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮疙驾,結(jié)果婚禮上凶伙,老公的妹妹穿的比我還像新娘。我一直安慰自己它碎,他們只是感情好函荣,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著扳肛,像睡著了一般傻挂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上挖息,一...
    開封第一講書人閱讀 49,079評(píng)論 1 285
  • 那天金拒,我揣著相機(jī)與錄音,去河邊找鬼。 笑死绪抛,一個(gè)胖子當(dāng)著我的面吹牛资铡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播幢码,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼笤休,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了症副?” 一聲冷哼從身側(cè)響起店雅,我...
    開封第一講書人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎贞铣,沒想到半個(gè)月后闹啦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡辕坝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年窍奋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片圣勒。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡费变,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出圣贸,到底是詐尸還是另有隱情挚歧,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布吁峻,位于F島的核電站滑负,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏用含。R本人自食惡果不足惜矮慕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望啄骇。 院中可真熱鬧痴鳄,春花似錦、人聲如沸缸夹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽虽惭。三九已至橡类,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間芽唇,已是汗流浹背顾画。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人研侣。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓谱邪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親庶诡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子虾标,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 春日游,杏花吹滿頭傀蚌,美好的春天里適合做各種美好的事基显,比如讀一本溫暖的書,一本關(guān)于愛情的小說善炫,讓自己治愈一下撩幽。 剛讀...
    競走的蝸牛閱讀 591評(píng)論 0 1
  • 其實(shí),寫這篇文的想法在我心中已經(jīng)縈繞很久了箩艺,只是最近突如其來的一些事情讓自己變得好忙窜醉。也算是為了逃避這種情緒而找的...
    寧夏思溢閱讀 726評(píng)論 0 0
  • 現(xiàn)在住在小區(qū)里,綠化都是大同小異艺谆,說起樹榨惰,想起來的還是小時(shí)候老家院子里的樹。 老家的院子很小静汤,有一顆很大的臭椿樹裳擎,...
    肉肉2017閱讀 460評(píng)論 0 0
  • 敬愛的老師 智慧的教授親愛的家人們 我是來自鑫山力機(jī)械的王齊港 攜手前行共創(chuàng)輝煌察绷,每天進(jìn)步一點(diǎn)點(diǎn)距離成功變不遠(yuǎn)。 ...
    正在加載中_bfa6閱讀 176評(píng)論 0 1
  • 我們有個(gè)群,都是全職媽媽或soho媽媽盯蝴,成員包括前牙醫(yī),英語超八級(jí)的前外貿(mào)專業(yè)人才溉潭,搞藝術(shù)的倦微,搞財(cái)務(wù)的,搞生意的药蜻,...
    張小妮在簡書閱讀 378評(píng)論 6 2