写点什么

Bazel 构建 Android 项目

用户头像
轻口味
关注
发布于: 刚刚
Bazel构建Android项目

本文我们使用 Bazel 构建一个最简单的 Android 项目。Bazel 提供了编译 Android 程序内置的方法,具体参考:Android Rules

1. 环境准备

Bazel 只是编译工具,不是真正的编译器,所以还是需要 Andorid 开发的 SD、NDK 以及 Android Studio,并配置开发环境。


接下来就是安装 Bazel。由于平时在 macos 开发为主,所以主要介绍 macos 系统的安装。macos 下 bazel 有四种安装方式:


  1. 使用二进制安装器(官方推荐);

  2. 使用 hombrew;

  3. 使用 bazelisk;

  4. 从源码编译安装 bazel。


下面介绍前两种常用方法。

1.1 二进制安装器安装

GitHub releases page 下载对应版本安装器。

第一步安装 Xcode 命令行工具

如果不打算使用ios_*相关的规则编译,则只需要使用 xcode-select 安装 Xcode 命令行工具:


xcode-select --install
复制代码


如果需要构建 ios 相关程序,则必须安装 Xcode6.1 或者最新版本,并且 iOS SDK 版本 8.1 以上。我们可以在 App Store 中下载到 Xcode。


Xcode 安装成功后需要我们通过一下命令接收用户使用说明协议:


sudo xcodebuild -license accept
复制代码

第二步:下载 Bazel 安装器

我们先在 Github basel release 页面下载 bazel 安装器baze-<version>-installer-darwin-x86_64.sh,mac 中沃恩可以使用 curl 工具下载:


# Example installing version `3.2.0`. Replace the version below as appropriate.export BAZEL_VERSION=3.2.0curl -fLO "https://github.com/bazelbuild/bazel/releases/download/${BAZEL_VERSION}/bazel-${BAZEL_VERSION}-installer-darwin-x86_64.sh"
复制代码

第三步:执行安装器脚本

使用下面命令安装 bazel:


chmod +x "bazel-${BAZEL_VERSION}-installer-darwin-x86_64.sh"./bazel-${BAZEL_VERSION}-installer-darwin-x86_64.sh --user
复制代码


--user参数将 Bazel 安装到HOME/bin目录。

第四步:设置环境变量

--user参数将 bazel 可执行文件安装到$HOME/bin目录,我们将该路径配置到环境变量,在~/.bashrc或者~/.zshrc或者~/.profile文件中增加如下配置:


export PATH="$PATH:$HOME/bin"
复制代码


此时我们可以运行bazel --version查看 bazezl 是否安装成功。

1.2 通过 HomeBrew 安装

第一步:安装 HomeBrew

如果系统未安装 HomeBrew,可以通过如下命令安装 HomeBrew:


/bin/bash -c "$(curl -fsSL \https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
复制代码

第二步:安装 Bazel

可以通过下面命令安装 bazel:


$ brew install bazel
复制代码


通过bazel --version既可看到是否安装成功。还可以通过下列命令更新 bazel:


brew upgrade bazel
复制代码

2.准备 Android 项目

我们使用 bazel 官方提供的Android demo程序,这个程序只是在一个 Activity 中放置了一个按钮,点击可以在 TextView 中显示一句话。


下载到代码后我们进入examples/android/tutorial目录,代码结构如下:


.├── README.html└── src    └── main        ├── AndroidManifest.xml        └── java            └── com                └── example                    └── bazel                        ├── AndroidManifest.xml                        ├── Greeter.java                        ├── MainActivity.java                        └── res                            ├── layout                            │   └── activity_main.xml                            └── values                                ├── colors.xml                                └── strings.xml
复制代码


就是一个最简单的 Android 项目结构。

3. 初始化项目空间

工作空间 workspace 是一个一个活多个软件项目源码的目录,在它的根目录下有一个WORKSPACE的文件。


WORKSPACE可能是空文件也可能包含外部依赖配置。我们可以通过bazel info workspace查看是否运行在正确的工作空间下。如果打印当前路径的地址,则表示是合理的空间,如果WORKSPACE文件不存在,会有如下报错:


ERROR: The 'info' command is only supported from within a workspace.
复制代码

4.集成 Android SDK

编译 Android 程序肯定要用到 Android 编译器,我们需要告诉 Bazel Android 相关 SDK 路径,我们可以在 WORKSPACE 文件中指定。


在 WORKSPACE 文件中增加如下代码:


android_sdk_repository(name = "androidsdk")
复制代码


这里指定了编译 Android 程序的 SDK 路径通过ANDROID_HOME环境变量获取,并且会自动检测最高 API 版本以及已经安装的最新 build tools 工具。


我们还可以通过包含 path、api_level 和 build_tools_version 属性来明确指定 Android SDK 的绝对路径、API 级别和要使用的构建工具的版本。如果 api_level 和 build_tools_version 没有指定,android_sdk_repository 规则将使用 SDK 中相应的最新版本。只要它们在 SDK 中存在,我们可以任意组合这些属性,例如:


android_sdk_repository(    name = "androidsdk",    path = "/path/to/Android/sdk",    api_level = 25,    build_tools_version = "30.0.3")
复制代码


如果我们还要编译 JNI 代码,还需要在 WORKSPACE 中指定 Android NDK 路径:


ndroid_ndk_repository(name = "androidndk")
复制代码


与 Android SDK 类似,这里面也是通过ANDROID_NDK_HOME环境变量控制 NDK 路径。

5. 创建 BUILD 文件

BUILD 文件描述了一组构建输出(比如来自 aapt 的编译过的 Android 资源或来自 javac 编译的类文件)和它们之间的关系。这些依赖项可能是工作区中的源文件(Java、c++)或其他构建输出。BUILD 文件是用 Starlark 语言编写的。


在 Bazel 中,BUILD 文件是包层次结构概念的一部分。包层次结构是覆盖工作区中的目录结构的逻辑结构。每个包都是一个目录(及其子目录),其中包含一组相关的源文件和一个 BUILD 文件。包还包括任何子目录,不包括那些包含自己的 BUILD 文件的子目录。包名是相对于工作区的 BUILD 文件的路径。


注意:Bazel 的包层次结构在概念上不同于 BUILD 文件所在的 Android App 目录的 Java 包层次结构,即使目录可能是相同的组织方式。


对于本文使用的简单 Android Demo,src/main/中的源文件包含一个 Bazel 包。更复杂的项目可能有许多嵌套的包。

5.1 添加 android_library 规则

BUILD 文件包含几种不同类型的 Bazel 声明。最重要的类型是构建规则,它告诉 Bazel 如何从一组源文件或其他依赖项构建中间或最终的软件输出。Bazel 提供了两个构建规则,android_library 和 android_binary,我们可以用它们来构建 Android 应用。


我们首先使用 android_library 规则来告诉 Bazel 从应用程序源代码和资源文件构建一个 Android 库模块。然后使用 android_binary 规则告诉 Bazel 如何构建 Android 应用程序包。


我们在src/main/java/com/example/bazel目录中创建 BUILD 文件,并且声明一个新的android_library目标:


package(    default_visibility = ["//src:__subpackages__"],)
android_library( name = "greeter_activity", srcs = [ "Greeter.java", "MainActivity.java", ], manifest = "AndroidManifest.xml", resource_files = glob(["res/**"]),)
复制代码


android_library 构建规则包含一组属性,这些属性指定了 Bazel 从源文件构建库模块所需的信息。还要注意,规则的名称是 greeter_activity,我们将在 android_binary 规则中使用这个名字作为依赖项引用规则。

5.2 添加 android_binary 规则

android_binary 规则帮助我们编译成最终的 apk 文件。


我们在src/main/路径中增加 BUILD 文件,并且声明android_binary目标:


android_binary(    name = "app",    manifest = "AndroidManifest.xml",    deps = ["//src/main/java/com/example/bazel:greeter_activity"],)
复制代码


在这里,deps 属性引用我们之前添加到上面 BUILD 文件中的 greeter_activity 规则的输出。这意味着当 Bazel 构建该规则的输出时,它首先检查 greeter_activity 库规则的输出是否已经构建并且是最新的。如果没有,Bazel 构建它,然后使用该输出构建应用程序包文件。

6.构建 APP

通过运行bazel build //src/main:app构建 android_binary 目标。


build 子命令指示 Bazel 构建下面的目标。目标被指定为 build 文件中的构建规则的名称,以及相对于工作区目录的包路径。在本例中,目标是 app,包路径是//src/main/。


注意:有时我们可以忽略包路径或目标名称,这取决于我们在命令行上的当前工作目录和目标名称。


Bazel 将开始构建样例应用程序。在构建过程中,它的输出如下所示:


INFO: Analysed target //src/main:app (0 packages loaded, 0 targets configured).INFO: Found 1 target...Target //src/main:app up-to-date:  bazel-bin/src/main/app_deploy.jar  bazel-bin/src/main/app_unsigned.apk  bazel-bin/src/main/app.apk
复制代码

7.构建输出文件路径

Bazel 将中间和最终构建操作的输出都放在一组每个用户、每个工作区的输出目录中。这些目录从项目目录的顶层的以下位置进行 symlink,其中的 WORKSPACE 是:


  • bazel-bin 存储二进制可执行文件和其他可运行的构建输出

  • bazel-genfiles存储由 Bazel 规则生成的中间源文件

  • bazel-out 存储其他类型的构建输出


Bazel 将使用 android_binary 规则生成的 Android .apk 文件存储在 Bazel -bin/src/main 目录中,其中的子目录名 src/main 来自于 Bazel 包的名称。

8. 运行 APP

现在我们可以通过命令行使用bazel mobile-install命令将应用程序部署到连接的 Android 设备或模拟器上。该命令使用 adb 与设备通信。在部署之前,我们必须按照 adb 中的说明设置我们的设备来使用 adb。我们也可以选择在 Android Studio 中包含的 Android 模拟器上安装应用程序。


bazel mobile-install //src/main:app
复制代码


接下来我们在手机上找到"Bazel Tutorial App"程序点击打开即可看到下面效果:



现在我们跑通了第一个 Bazel 构建的 Android 项目。


注意:mobile-install 子命令还支持——incremental 标志,该标志可用于仅部署自上次部署以来已更改的应用程序部分;它还支持——start_app 标志,以便在安装应用程序时立即启动它。

9. 总结

我们我们使用 Bazel 构建了一个 Android 应用程序。主要步骤总结如下:


  • 通过安装 Bazel 和 Android Studio 来设置环境,并下载示例项目。

  • 设置一个 Bazel 工作空间,其中包含应用程序的源代码和一个识别工作空间目录顶层的 workspace 文件。

  • 更新 WORKSPACE 文件,以包含对所需外部依赖项的引用,如 Android SDK。

  • 创建了 BUILD 文件。

  • 使用 bazel 构建该应用。

  • 在 Android 模拟器或物理设备上部署和运行应用程序。


我们通过最简单的 Android 项目编译了解了 Bazel 相关语法及规则,后续对我们学习谷歌新的基于 Bazel 构建的项目提供了很大帮助。后续我们介绍 TensorFlow Lite 在 Android 平台和 iOS 平台的编译。

发布于: 刚刚阅读数: 2
用户头像

轻口味

关注

🏆2021年InfoQ写作平台-签约作者 🏆 2017.10.17 加入

Android音视频、AI相关领域从业者,开源RTMP播放器:https://github.com/qingkouwei/oarplayer

评论

发布
暂无评论
Bazel构建Android项目