Bazel入門

安裝

安裝過程請參考: http://bazel.io/docs/install.html

使用工作區(workspace)

所有的Bazel構建都是基于一個 工作區(workspace) 概念,它是文件系統中一個保存了全部源代碼的目錄,同時還將包含一些構建后的輸出目錄的符號鏈接(例如:bazel-binbazel-out 等輸出目錄)。工作區目錄可以隨意放在哪里,但是工作區的根目錄必須包含一個名為 WORKSPACE 的工作區配置文件。工作區配置文件可以是一個空文件,也可以包含引用外部構建輸出所需的 依賴關系

在一個工作區內,可以根據需要共享多個項目。為了簡單,我們先從只有一個項目的工作區開始介紹。

先假設你已經有了一個項目,對應 ~/gitroot/my-project/ 目錄。我們先創建一個空的 ~/gitroot/my-project/WORKSPACE 工作區配置文件,用于表示這是Bazel項目對應的根目錄。

創建自己的Build構建文件

使用下面的命令創建一個簡單的Java項目:

$ # If you're not already there, move to your workspace directory.
$ cd ~/gitroot/my-project
$ mkdir -p src/main/java/com/example
$ cat > src/main/java/com/example/ProjectRunner.java <<EOF
package com.example;

public class ProjectRunner {
    public static void main(String args[]) {
        Greeting.sayHi();
    }
}
EOF
$ cat > src/main/java/com/example/Greeting.java <<EOF
package com.example;

public class Greeting {
    public static void sayHi() {
        System.out.println("Hi!");
    }
}
EOF

Bazel通過工作區中所有名為 BUILD 的文件來解析需要構建的項目信息,因此,我們需要先在 ~/gitroot/my-project 目錄創建一個 BUILD 構建文件。下面是BUILD構建文件的內容:

# ~/gitroot/my-project/BUILD
java_binary(
    name = "my-runner",
    srcs = glob(["**/*.java"]),
    main_class = "com.example.ProjectRunner",
)

BUILD文件采用類似Python的語法。雖然不能包含任意的Python語法,但是BUILD文件中的每個構建規則看起來都象是一個Python函數調用,而且你也可以用 "#" 開頭來添加單行注釋。

java_binary 是一個構建規則。其中 name 對應一個構建目標的標識符,可用用它來向Bazel指定構建哪個項目。srcs 對應一個源文件列表,Bazel需要將這些源文件編譯為二進制文件。其中 glob(["**/*.java"]) 表示遞歸包含每個子目錄中以每個 .java 為后綴名的文件。com.example.ProjectRunner 指定包含main方法的類。

現在可以用下面的命令構建這個Java程序了:

$ cd ~/gitroot/my-project
$ bazel build //:my-runner
INFO: Found 1 target...
Target //:my-runner up-to-date:
  bazel-bin/my-runner.jar
  bazel-bin/my-runner
INFO: Elapsed time: 1.021s, Critical Path: 0.83s
$ bazel-bin/my-runner
Hi!

恭喜,你已經成功構建了第一個Bazel項目了!

添加依賴關系

對于小項目創建一個規則是可以的,但是隨著項目的變大,則需要分別構建項目的不同的部件,最終再組裝成產品。這種構建方式可以避免因為局部細小的修改兒導致重現構建整個應用,同時不同的構建步驟可以很好地并發執行以提高構建效率。

我們現在將一個項目拆分為兩個部分獨立構建,同時設置它們之間的依賴關系。基于上面的例子,我們重寫了BUILD構建文件:

java_binary(
    name = "my-other-runner",
    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 庫,然后是構建 my-other-runner。可以在構建成功后立刻運行 //:my-other-runner

$ bazel run //:my-other-runner
INFO: Found 1 target...
Target //:my-other-runner up-to-date:
  bazel-bin/my-other-runner.jar
  bazel-bin/my-other-runner
INFO: Elapsed time: 2.454s, Critical Path: 1.58s

INFO: Running command line: bazel-bin/my-other-runner
Hi!

現在如果你改動ProjectRunner.java代碼并重新構建my-other-runner目標,Greeting.java文件因為沒有變化而不會重現編譯。

使用多個包(Packages)

對于更大的項目,我們通常需要將它們拆分到多個目錄中。你可以用類似//path/to/directory:target-name的名字引用在其他BUILD文件定義的目標。假設src/main/java/com/example/有一個cmdline/子目錄,包含下面的文件:

$ mkdir -p src/main/java/com/example/cmdline
$ cat > src/main/java/com/example/cmdline/Runner.java <<EOF
package com.example.cmdline;

import com.example.Greeting;

public class Runner {
    public static void main(String args[]) {
        Greeting.sayHi();
    }
}
EOF

Runner.java依賴com.example.Greeting,因此我們需要在src/main/java/com/example/cmdline/BUILD構建文件中添加相應的依賴規則:

# ~/gitroot/my-project/src/main/java/com/example/cmdline/BUILD
java_binary(
    name = "runner",
    srcs = ["Runner.java"],
    main_class = "com.example.cmdline.Runner",
    deps = ["http://:greeter"]
)

然而,默認情況下構建目標都是 私有 的。也就是說,我們只能在同一個BUILD文件中被引用。這可以避免將很多實現的細節暴漏給公共的接口,但是也意味著我們需要手工允許runner所依賴的//:greeter目標。就是類似下面這個在構建runner目標時遇到的錯誤:

$ bazel build //src/main/java/com/example/cmdline:runner
ERROR: /home/user/gitroot/my-project/src/main/java/com/example/cmdline/BUILD:2:1:
  Target '//:greeter' is not visible from target '//src/main/java/com/example/cmdline:runner'.
  Check the visibility declaration of the former target if you think the dependency is legitimate.
ERROR: Analysis of target '//src/main/java/com/example/cmdline:runner' failed; build aborted.
INFO: Elapsed time: 0.091s

可用通過在BUILD文件增加visibility = level屬性來改變目標的可間范圍。下面是通過在~/gitroot/my-project/BUILD文件增加可見規則,來改變greeter目標的可見范圍:

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

這個規則表示//:greeter目標對于//src/main/java/com/example/cmdline包是可見的。現在我們可以重新構建runner目標程序:

$ bazel run //src/main/java/com/example/cmdline:runner
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

INFO: Running command line: bazel-bin/src/main/java/com/example/cmdline/runner
Hi!

參考文檔 中有可見性配置說明。

部署

如果你查看 bazel-bin/src/main/java/com/example/cmdline/runner.jar 的內容,可以看到里面只包含了Runner.class,并沒有保護所依賴的Greeting.class

$ 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

這只能在本機正常工作(因為Bazel的runner腳本已經將greeter jar添加到了classpath),但是如果將runner.jar單獨復制到另一臺機器上講不能正常運行。如果想要構建可用于部署發布的自包含所有依賴的目標,可以構建runner_deploy.jar目標(類似<target-name>_deploy.jar_deploy為后綴的名字對應可部署目標)。

$ bazel build //src/main/java/com/example/cmdline:runner_deploy.jar
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中將包含全部的依賴。

下一步

現在,您可以創建自己的目標并組裝最終產品了。接下來,可查看 相關教程 分別學習如何用Bazel構建一個服務器、Android和iOS應用。也可以參考 構建百科用戶手冊獲得更多的信息。如果有問題的話,可以到 bazel-discuss 論壇提問。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容