Migrating iOS Project to Bazel – Part 2

To continue the previous part of this series, this part I’ll talk about how to config a sample iOS project using Bazel.

1. Installation

To install Bazel on macOS, too simple. You can go to this link
https://docs.bazel.build/versions/main/install-os-x.html.

To simplest, I will use brew to install bazel.

brew install bazel

After install success, you can verify by running the below command

bazel --version

2. Build config structure

Bazel will use BUILD files to config build for the module in your project. Besides, bazel also use WORKSPACE and .bazel file to config the project.

2.1 WORKSPACE file

WORKSPACE is a configuration file used to pull build rules dependencies. It is placed at the root directory.

We need pull rules_apple and rules_swift to build our iOS App. It is open source at https://github.com/bazelbuild/rules_apple and https://github.com/bazelbuild/rules_swift.

This is an example of the WORKSPACE file.

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
    name = "build_bazel_rules_apple",
    sha256 = "c84962b64d9ae4472adfb01ec2cf1aa73cb2ee8308242add55fa7cc38602d882",
    url = "https://github.com/bazelbuild/rules_apple/releases/download/0.31.2/rules_apple.0.31.2.tar.gz",
)

load(
    "@build_bazel_rules_apple//apple:repositories.bzl",
    "apple_rules_dependencies",
)

apple_rules_dependencies()

load(
    "@build_bazel_rules_swift//swift:repositories.bzl",
    "swift_rules_dependencies",
)

swift_rules_dependencies()

load(
    "@build_bazel_rules_swift//swift:extras.bzl",
    "swift_rules_extra_dependencies",
)

swift_rules_extra_dependencies()

load(
    "@build_bazel_apple_support//lib:repositories.bzl",
    "apple_support_dependencies",
)

apple_support_dependencies()

The WORKSPACE will perform actions follow below steps:

  • Use load function to get http_archive function from Bazel build-in rules.
  • Call http_archive function to pull rules_apple from GitHub.
  • Use load to get apple_rules_dependencies function from rules_apple that we did get from the previous step.
  • Call apple_rules_dependencies to pull others dependencies of rules_apple. rules_swift is an example in list dependency of rules_apple.
  • Continue use load and call other functions to pull dependencies of other tools like rules_swift, apple_support

2.2 .bazelrc

.bazelrc is configuration file placed in root directory like WORKSPACE file. It is used to configure parameters that will be passed to bazel when the Bazel command is called.

Here is an example configuration file to config gell DSYM for debugging purposes and specific version and type of simulator device.

build --apple_platform_type=ios
build --apple_generate_dsym
build --ios_simulator_device="iPhone 12"
build --ios_simulator_version=14.5

2.3 BUILD

Build config dependencies

Continue WORKSPACE and .bazelrc, we need the file name is BUILD at the root of modules that we need to build. It will create a package at the place of the BUILD file.

These BUILD files will contain load and call rules build-in rules_apple and rules_swift to config build target. Build target in BUILD file can depend on other build targets in other files by config deps field.

For demo purposes, I will use a project placed at start folder: https://github.com/vikage/DemoProjectBazel

First, I will write WORKSPACE and .bazelrc first with contents is same as the above example.

BUILD for TLLogging library
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
load("@rules_cc//cc:defs.bzl", "objc_library")

swift_library(
    name = "TLLogging",
    module_name = "TLLogging",
    srcs = glob([
        "*.swift",
    ]),
    deps = [
        ":TLLoggingHelper",
    ],
    visibility = ["//visibility:public"],
)

objc_library(
    name = "TLLoggingHelper",
    module_name = "TLLoggingHelper",
    srcs = glob([
        "*.m",
    ]),
    hdrs = glob([
        "*.h",
    ]),
)

visibility is a field that shows the visible state of that build target. It will allow other build targets in other packages can see that build target or not. In the above config, I use public means all packages can see that build target.

deps is a field that defines dependencies of the current build target.

To learn more about other fields, please find out in the docs of rules_swift and rules_apple or Bazel website.

BUILD for Assets

To build for xcassets, we will use rule named filegroup. This is the build-in rule, so we don’t need to load this rule.

filegroup(
    name = "Assets",
    srcs = glob(["Assets.xcassets/**"]),
    visibility = ["//visibility:public"],
)
BUILD for App_Classes and ios_app
load("@build_bazel_rules_apple//apple:ios.bzl", "ios_application")
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")

swift_library(
    name = "App_Classes",
    srcs = glob([
        "*.swift"
    ]),
    data = [
    	"//Assets:Assets",
    	"Base.lproj/Main.storyboard",
    	"Base.lproj/LaunchScreen.storyboard",
    ],
    deps = [
        "//Lib/Logging:TLLogging"
    ]
)

ios_application(
    name = "ios_app",
    bundle_id = "com.thanhvu.test",
    infoplists = ["Info.plist"],
    families = ["iphone"],
    minimum_os_version = "12.0",
    deps = [
        ":App_Classes",
    ]
)

Look at the App_Classes target, Assets are embedded over the data field.

3. Build project by command line

To build the project by command line. first, we need to change the directory to the project in the command line window. And call blow command.

bazel build //App:ios_app

To run in the simulator, you can run the below command.

bazel run //App:ios_app

So we have completed a demo iOS app that uses Bazel as a build system. Next, I will talk about the challenges that I face off when migrating my project to Bazel.

Demo project: https://github.com/vikage/DemoProjectBazel

Reference resources:
https://docs.bazel.build/versions/main/tutorial/ios-app.html
https://docs.bazel.build/versions/main/migrate-xcode.html

Leave a Reply