This page describes how to build or test an Xcode project with Bazel. It describes the differences between Xcode and Bazel, and provides the steps for converting an Xcode project to a Bazel project. It also provides troubleshooting solutions to address common errors.
Differences between Xcode and Bazel
Bazel requires you to explicitly specify every build target and its dependencies, plus the corresponding build settings via build rules.
Bazel requires all files on which the project depends to be present within the workspace directory or specified as imports in the
When building Xcode projects with Bazel, the
BUILDfile(s) become the source of truth. If you work on the project in Xcode, you must generate a new version of the Xcode project that matches the
BUILDfiles using rules_xcodeproj whenever you update the
BUILDfiles. Certain changes to the
BUILDfiles such as adding dependencies to a target don't require regenerating the project which can speed up development. If you're not using Xcode, the
bazel testcommands provide build and test capabilities with certain limitations described later in this guide.
Before you begin
Before you begin, do the following:
Install Bazel if you have not already done so.
If you're not familiar with Bazel and its concepts, complete the iOS app tutorial). You should understand the Bazel workspace, including the
BUILDfiles, as well as the concepts of targets, build rules, and Bazel packages.
Analyze and understand the project's dependencies.
Analyze project dependencies
Unlike Xcode, Bazel requires you to explicitly declare all dependencies for
every target in the
For more information on external dependencies, see Working with external dependencies.
Build or test an Xcode project with Bazel
To build or test an Xcode project with Bazel, do the following:
Step 1: Create the
WORKSPACE file in a new directory. This directory becomes the Bazel
workspace root. If the project uses no external dependencies, this file can be
empty. If the project depends on files or packages that are not in one of the
project's directories, specify these external dependencies in the
Step 2: (Experimental) Integrate SwiftPM dependencies
Step 3: Create a
Once you have defined the workspace and external dependencies, you need to
BUILD file that tells Bazel how the project is structured. Create
BUILD file at the root of the Bazel workspace and configure it to do an
initial build of the project as follows:
- Step 3a: Add the application target
- Step 3b: (Optional) Add the test target(s)
- Step 3c: Add the library target(s)
Tip: To learn more about packages and other Bazel concepts, see Workspaces, packages, and targets.
Step 3a: Add the application target
bundle_id- the bundle ID (reverse-DNS path followed by app name) of the binary.
provisioning_profile- provisioning profile from your Apple Developer account (if building for an iOS device device).
families(iOS only) - whether to build the application for iPhone, iPad, or both.
infoplists- list of .plist files to merge into the final Info.plist file.
minimum_os_version- the minimum version of macOS or iOS that the application supports. This ensures Bazel builds the application with the correct API levels.
Step 3b: (Optional) Add the test target(s)
Bazel's Apple build rules support running unit and UI tests on all Apple platforms. Add test targets as follows:
macos_unit_testto run library-based and application-based unit tests on a macOS.
ios_unit_testto build and run library-based unit tests on iOS.
ios_ui_testto build and run user interface tests in the iOS simulator.
At the minimum, specify a value for the
minimum_os_version attribute. While
other packaging attributes, such as
default to most commonly used values, ensure that those defaults are compatible
with the project and adjust them as necessary. For tests that require the iOS
simulator, also specify the
ios_application target name as the value of the
Step 3c: Add the library target(s)
Add the library targets as follows:
Add the application library targets as dependencies to the application targets.
Add the test library targets as dependencies to the test targets.
List the implementation sources in the
List the headers in the
You can browse existing examples for various types of applications directly in the rules_apple examples directory. For example:
For more information on build rules, see Apple Rules for Bazel.
At this point, it is a good idea to test the build:
bazel build //:<application_target>
Step 4: (Optional) Granularize the build
If the project is large, or as it grows, consider chunking it into multiple Bazel packages. This increased granularity provides:
Increased incrementality of builds,
Increased parallelization of build tasks,
Better maintainability for future users,
Better control over source code visibility across targets and packages. This prevents issues such as libraries containing implementation details leaking into public APIs.
Tips for granularizing the project:
Put each library in its own Bazel package. Start with those requiring the fewest dependencies and work your way up the dependency tree.
As you add
BUILDfiles and specify targets, add these new targets to the
depsattributes of targets that depend on them.
glob()function does not cross package boundaries, so as the number of packages grows the files matched by
When adding a
BUILDfile to a
maindirectory, also add a
BUILDfile to the corresponding
Enforce healthy visibility limits across packages.
Build the project after each major change to the
BUILDfiles and fix build errors as you encounter them.
Step 5: Run the build
Run the fully migrated build to ensure it completes with no errors or warnings. Run every application and test target individually to more easily find sources of any errors that occur.
bazel build //:my-target
Step 6: Generate the Xcode project with rules_xcodeproj
When building with Bazel, the
BUILD files become the source
of truth about the build. To make Xcode aware of this, you must generate a
Bazel-compatible Xcode project using rules_xcodeproj.
Bazel errors can arise when it gets out of sync with the selected Xcode version, like when you apply an update. Here are some things to try if you're experiencing errors with Xcode, for example "Xcode version must be specified to use an Apple CROSSTOOL".
Manually run Xcode and accept any terms and conditions.
Use Xcode select to indicate the correct version, accept the license, and clear Bazel's state.
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
sudo xcodebuild -license
bazel sync --configure
- If this does not work, you may also try running
bazel clean --expunge.