Bazel 教學課程:建構 Java 專案

回報問題 查看原始碼 夜間 7.2 7.1 7.0 6.5 6.4

本教學課程涵蓋使用 Bazel。您會設定工作區並建構簡單的 Java 專案, 說明 Bazel 的重要概念,例如目標和 BUILD 檔案。

預計完成時間:30 分鐘。

課程內容

在本教學課程中,您會瞭解如何執行下列作業:

  • 建立目標
  • 以圖表呈現專案的依附元件
  • 將專案分割為多個目標和套件
  • 控管所有套件的目標瀏覽權限
  • 透過標籤參照目標
  • 部署目標

事前準備

安裝 Bazel

如要為教學課程做好準備,請先安裝 Bazel (如有 你還沒有安裝這個應用程式。

安裝 JDK

  1. 安裝 Java JDK (建議版本為 11,但系統支援 8 到 15 的版本)。

  2. 將 JAVA_HOME 環境變數設為指向 JDK。

    • Linux/macOS:

      export JAVA_HOME="$(dirname $(dirname $(realpath $(which javac))))"
      
    • Windows:

      1. 開啟控制台。
      2. 前往「系統與安全性」頁面>「系統」>「進階系統設定」>「進階」Tab >「環境變數...」。
      3. 在「使用者變數」下方清單,然後按一下「新增...」即可。
      4. 在「變數名稱」中欄位中輸入 JAVA_HOME
      5. 按一下 [瀏覽目錄...]。
      6. 前往 JDK 目錄 (例如 C:\Program Files\Java\jdk1.8.0_152)。
      7. 按一下「確定」所有對話方塊視窗

取得範例專案

從 Bazel 的 GitHub 存放區擷取範例專案:

git clone https://github.com/bazelbuild/examples

本教學課程的範例專案位於 examples/java-tutorial ,其結構如下:

java-tutorial
├── BUILD
├── src
│   └── main
│       └── java
│           └── com
│               └── example
│                   ├── cmdline
│                   │   ├── BUILD
│                   │   └── Runner.java
│                   ├── Greeting.java
│                   └── ProjectRunner.java
└── WORKSPACE

使用 Bazel 進行建構

設定工作區

您必須先設定專案工作區,才能建立專案。工作區是 存放專案來源檔案和 Bazel 建構輸出內容的目錄。這項服務 也包含 Bazel 視為特殊的檔案:

  • WORKSPACE 檔案,用來將目錄和其內容識別為 Bazel 工作區和專案目錄結構的根目錄

  • 一或多個 BUILD 檔案,用於告知 Bazel 如何建構 專案。(工作區中含有 BUILD 檔案的目錄) 是套件。稍後,本教學課程會說明套件。)

如要將目錄指定為 Bazel 工作區,請建立名為 WORKSPACE

當 Bazel 建構專案時,所有輸入和依附元件都必須相同 工作區位於不同工作區中的檔案各自獨立 除非連結 不在本教學課程的討論範圍內

瞭解 BUILD 檔案

BUILD 檔案包含多種不同類型的 Bazel 指示。 最重要的類型是建構規則,用於告知 Bazel 如何建構 所需的輸出內容,例如可執行的二進位檔或程式庫每個執行個體 BUILD 檔案中的建構規則稱為「目標」,並指向 特定的來源檔案和依附元件組合指定目標也可以指向其他 目標。

請看看 java-tutorial/BUILD 檔案:

java_binary(
    name = "ProjectRunner",
    srcs = glob(["src/main/java/com/example/*.java"]),
)

在我們的範例中,ProjectRunner 目標會將 Bazel 的內建項目執行個體化 java_binary 規則。規則會指示 Bazel 執行 建立 .jar 檔案和包裝函式殼層指令碼 (兩者都以目標命名)。

目標中的屬性明確指出其依附元件和選項。 雖然 name 為必要屬性,但許多屬性仍為選用屬性。例如,在 ProjectRunner 規則目標,name 是目標名稱,srcs 指定 Bazel 用來建構目標的來源檔案,而 main_class 會指定 包含主要方法的類別。(您可能已經注意到我們的範例 使用 glob 將一組來源檔案傳送至 Bazel 而不要逐一列出)。

建構專案

如要建立範例專案,請前往 java-tutorial 目錄 並執行:

bazel build //:ProjectRunner

在目標標籤中,// 部分是 BUILD 檔案的位置 相對於工作區的根層級 (在這個案例中為根本身), 而 ProjectRunnerBUILD 檔案中的目標名稱。(您將 請參閱本教學課程最後的詳細說明,瞭解目標標籤)。

Bazel 會產生類似以下的輸出內容:

   INFO: Found 1 target...
   Target //:ProjectRunner up-to-date:
      bazel-bin/ProjectRunner.jar
      bazel-bin/ProjectRunner
   INFO: Elapsed time: 1.021s, Critical Path: 0.83s

恭喜,您已成功建構第一個 Bazel 目標!Bazel 會放置建構 會在工作區根目錄的 bazel-bin 目錄中輸出。瀏覽 以瞭解 Bazel 的輸出結構的概念

現在請測試剛建構的二進位檔:

bazel-bin/ProjectRunner

查看依附關係圖

Bazel 需要在 BUILD 檔案中明確宣告建構依附元件。 Bazel 會使用這些陳述式建立專案的依附元件圖表。 提供準確的漸進式建構作業

如要視覺化範例專案的依附元件,您可以產生文字 以表示依附元件圖表,方法是在 工作區根目錄:

bazel query  --notool_deps --noimplicit_deps "deps(//:ProjectRunner)" --output graph

上述指令會指示 Bazel 尋找目標的所有依附元件 //:ProjectRunner (排除主機和隱含依附元件) 的格式,並將 並轉換為圖表

然後將文字貼到 GraphViz 中。

如您所見,專案只有一個目標,可透過 沒有其他依附元件:

目標「ProjectRunner」的依附元件圖表

設定工作區後,即可建構及檢查專案 那就增加一些複雜度

修正 Bazel 建構作業

雖然一個目標就足以滿足小型專案的需求,而您可能想要 轉化成多個目標和套件 (也就是只重新建構已變更的內容) 並利用 同時建構專案的多個部分

指定多個建構目標

您可以將範例專案版本分割為兩個目標。將內容 加入含有以下內容的 java-tutorial/BUILD 檔案:

java_binary(
    name = "ProjectRunner",
    srcs = ["src/main/java/com/example/ProjectRunner.java"],
    main_class = "com.example.ProjectRunner",
    deps = [":greeter"],
)

java_library(
    name = "greeter",
    srcs = ["src/main/java/com/example/Greeting.java"],
)

透過這項設定,Bazel 會先建構 greeter 程式庫,然後 ProjectRunner 二進位檔。java_binary 中的 deps 屬性會向 Bazel 告知 需要 greeter 程式庫才能建構 ProjectRunner 二進位檔。

如要建構這個新版專案,請執行下列指令:

bazel build //:ProjectRunner

Bazel 會產生類似以下的輸出內容:

INFO: Found 1 target...
Target //:ProjectRunner up-to-date:
  bazel-bin/ProjectRunner.jar
  bazel-bin/ProjectRunner
INFO: Elapsed time: 2.454s, Critical Path: 1.58s

現在請測試剛建構的二進位檔:

bazel-bin/ProjectRunner

如果您現在修改 ProjectRunner.java 並重新建構專案,則只有 Bazel 就會重新編譯該檔案

查看依附元件圖表時,您會發現 ProjectRunner 依附於 輸入與先前相同的輸入內容,但建構作業的結構不同:

目標「ProjectRunner」的依附元件圖表新增依附元件後

您現在已建立具有兩個目標的專案。ProjectRunner 目標版本 兩個來源檔案並依附於另一個目標 (:greeter),而該目標 還有一個來源檔案

使用多個套件

現在,我們將專案拆分為多個套件。如果查看 src/main/java/com/example/cmdline 目錄,就能看到該目錄包含 BUILD 檔案和一些來源檔案。因此,在 Bazel 中 包含兩個套件://src/main/java/com/example/cmdline// (自 工作區的根目錄下有 BUILD 檔案)。

請看看 src/main/java/com/example/cmdline/BUILD 檔案:

java_binary(
    name = "runner",
    srcs = ["Runner.java"],
    main_class = "com.example.cmdline.Runner",
    deps = ["//:greeter"],
)

runner 目標取決於 // 套件中的 greeter 目標 (因此) 目標標籤 //:greeter) - Bazel 會透過 deps 屬性知道這一點。 查看依附元件圖表:

目標「執行器」的依附元件圖

但如要成功建構,您必須明確指定 runner 目標 //src/main/java/com/example/cmdline/BUILD與以下區域的目標可見度: //BUILD 使用 visibility 屬性。這是因為系統會根據預設目標 只會向同一個 BUILD 檔案中的其他目標顯示。(Bazel 會使用目標 防止出現問題,例如程式庫內含實作詳細資料 外洩至公用 API)

方法是將 visibility 屬性新增至 greeter 目標: java-tutorial/BUILD,如下所示:

java_library(
    name = "greeter",
    srcs = ["src/main/java/com/example/Greeting.java"],
    visibility = ["//src/main/java/com/example/cmdline:__pkg__"],
)

現在,您可以在根目錄執行下列指令,以建構新的套件 工作區:

bazel build //src/main/java/com/example/cmdline:runner

Bazel 會產生類似以下的輸出內容:

INFO: Found 1 target...
Target //src/main/java/com/example/cmdline:runner up-to-date:
  bazel-bin/src/main/java/com/example/cmdline/runner.jar
  bazel-bin/src/main/java/com/example/cmdline/runner
  INFO: Elapsed time: 1.576s, Critical Path: 0.81s

現在請測試剛建構的二進位檔:

./bazel-bin/src/main/java/com/example/cmdline/runner

您已修改專案,建構為兩個套件,每個套件各有一個套件 並掌握兩者之間的依附關係

使用標籤參照目標

BUILD 檔案中和指令列,Bazel 會使用目標標籤來參照 指定目標:例如 //:ProjectRunner//src/main/java/com/example/cmdline:runner。其語法如下:

//path/to/package:target-name

如果目標為規則目標,則 path/to/package 是 目錄,其中含有 BUILD 檔案,而 target-name 是您命名的 BUILD 檔案 (name 屬性) 中的指定目標。如果目標為檔案 目標,則 path/to/package 是套件的根目錄路徑, target-name 是目標檔案的名稱,包括其完整路徑。

在存放區根目錄參照目標時,套件路徑為空白。 只要使用 //:target-name。參照相同BUILD內的目標時 檔案,您甚至可以略過 // 工作區的根 ID,只使用 :target-name

例如,對於 java-tutorial/BUILD 檔案中的目標,您無須 指定套件路徑,因為工作區根目錄本身是套件 (//)。 您的兩個目標標籤都是 //:ProjectRunner//:greeter

不過,對於 //src/main/java/com/example/cmdline/BUILD 檔案中目標 必須指定 //src/main/java/com/example/cmdline 的完整套件路徑 ,您的目標標籤為 //src/main/java/com/example/cmdline:runner

封裝用於部署的 Java 目標

現在,請建構用於部署的 Java 目標的 其執行階段依附元件的容器這可讓您在 開發環境的專區

如您所知,java_binary 建構規則 會產生 .jar 和包裝函式殼層指令碼。查看 runner.jar

jar tf bazel-bin/src/main/java/com/example/cmdline/runner.jar

其內容如下:

META-INF/
META-INF/MANIFEST.MF
com/
com/example/
com/example/cmdline/
com/example/cmdline/Runner.class

如您所見,runner.jar 包含 Runner.class,但不含依附元件。 Greeting.class。Bazel 產生的 runner 指令碼會新增 greeter.jar 加入類別路徑,因此如果您選擇讓路徑如下所示,系統就會在本機執行,但 無法在其他機器上獨立運作所幸 java_binary 規則 可讓您建構獨立且可部署的二進位檔如要建立這個存放區,請將 _deploy.jar 設為目標名稱:

bazel build //src/main/java/com/example/cmdline:runner_deploy.jar

Bazel 會產生類似以下的輸出內容:

INFO: Found 1 target...
Target //src/main/java/com/example/cmdline:runner_deploy.jar up-to-date:
  bazel-bin/src/main/java/com/example/cmdline/runner_deploy.jar
INFO: Elapsed time: 1.700s, Critical Path: 0.23s

您剛建構了 runner_deploy.jar,現在可以獨立執行 您的開發環境,因為其中含有必要的執行階段 依附元件查看這個獨立 JAR 的內容 使用與之前相同的指令

jar tf bazel-bin/src/main/java/com/example/cmdline/runner_deploy.jar

內容會包含所有必要的執行類別:

META-INF/
META-INF/MANIFEST.MF
build-data.properties
com/
com/example/
com/example/cmdline/
com/example/cmdline/Runner.class
com/example/Greeting.class

延伸閱讀

相關詳情請參閱:

祝您建構愉快!