blob: f654954c889081fee8e77ae4e2f7d7294a19c202 [file] [log] [blame] [view]
fweacae1cd2022-02-17 09:45:38 -08001Project: /_project.yaml
2Book: /_book.yaml
3
4# Migrating from Xcode to Bazel
5
6This page describes how to build or test an Xcode project with Bazel. It
7describes the differences between Xcode and Bazel, and provides the steps
8for converting an Xcode project to a Bazel project. It also provides
9troubleshooting solutions to address common errors.
10
11## Differences between Xcode and Bazel {:#dif-xcode-bazel}
12
13* Bazel requires you to explicitly specify every build target and its
14 dependencies, plus the corresponding build settings via build rules.
15
16* Bazel requires all files on which the project depends to be present
17 within the workspace directory or specified as imports in the `WORKSPACE`
18 file.
19
20* When building Xcode projects with Bazel, the `BUILD` file(s) become the
21 source of truth. If you work on the project in Xcode, you must generate a
22 new version of the Xcode project that matches the `BUILD` files using
23 [Tulsi](http://tulsi.bazel.build/) whenever you update the `BUILD` files. If
24 you're not using Xcode, the `bazel build` and `bazel test` commands provide
25 build and test capabilities with certain limitations described later in this
26 guide.
27
28* Due to differences in build configuration schemas, such as directory layouts
29 or build flags, Xcode might not be fully aware of the "big picture" of the
30 build and thus some Xcode features might not work. Namely:
31
32 * Depending on the targets you select for conversion in Tulsi, Xcode might
33 not be able to properly index the project source. This affects code
34 completion and navigation in Xcode, since Xcode won't be able to see all
35 of the project's source code.
36
37 * Static analysis, address sanitizers, and thread sanitizers might not
38 work, since Bazel does not produce the outputs that Xcode expects for
39 those features.
40
41 * If you generate an Xcode project with Tulsi and use that project to run
42 tests from within Xcode, Xcode will run the tests instead of
43 Bazel. To run tests with Bazel, run the `bazel test` command manually.
44
45## Before you begin {:#before-you-begin}
46
47Before you begin, do the following:
48
491. [Install Bazel](/install) if you have not already done so.
50
512. If you're not familiar with Bazel and its concepts, complete the
52 [iOS app tutorial](/tutorials/ios-app). You should understand the Bazel
53 workspace, including the `WORKSPACE` and `BUILD` files, as well as the
54 concepts of targets, build rules, and Bazel packages.
55
563. Analyze and understand the project's dependencies.
57
58### Analyze project dependencies {:#analyze-project-dependencies}
59
60Unlike Xcode, Bazel requires you to explicitly declare all dependencies for
61every target in the `BUILD` file.
62
63For more information on external dependencies, see
64[Working with external dependencies](/docs/external).
65
66## Build or test an Xcode project with Bazel {:#build-xcode-project}
67
68To build or test an Xcode project with Bazel, do the following:
69
fwe6c2bd4a2022-02-18 10:47:46 -0800701. [Create the `WORKSPACE` file](#create-workspace)
fweacae1cd2022-02-17 09:45:38 -080071
fwe6c2bd4a2022-02-18 10:47:46 -0800722. [(Experimental) Integrate CocoaPods dependencies](#integrate-cocoapods)
fweacae1cd2022-02-17 09:45:38 -080073
fwe6c2bd4a2022-02-18 10:47:46 -0800743. [Create a `BUILD` file:](#create-build-file)
fweacae1cd2022-02-17 09:45:38 -080075
fwe6c2bd4a2022-02-18 10:47:46 -080076 a. [Add the application target](#add-app-target)
fweacae1cd2022-02-17 09:45:38 -080077
fwe6c2bd4a2022-02-18 10:47:46 -080078 b. [(Optional) Add the test target(s)](#add-test-target)
fweacae1cd2022-02-17 09:45:38 -080079
fwe6c2bd4a2022-02-18 10:47:46 -080080 c. [Add the library target(s)](#add-library-target)
fweacae1cd2022-02-17 09:45:38 -080081
fwe6c2bd4a2022-02-18 10:47:46 -0800824. [(Optional) Granularize the build](#granularize-build)
fweacae1cd2022-02-17 09:45:38 -080083
fwe6c2bd4a2022-02-18 10:47:46 -0800845. [Run the build](#run-build)
fweacae1cd2022-02-17 09:45:38 -080085
fwe6c2bd4a2022-02-18 10:47:46 -0800866. [Generate the Xcode project with Tulsi](#generate-xcode-tulsi)
fweacae1cd2022-02-17 09:45:38 -080087
88### Step 1: Create the `WORKSPACE` file {:#create-workspace}
89
90Create a `WORKSPACE` file in a new directory. This directory becomes the Bazel
91workspace root. If the project uses no external dependencies, this file can be
92empty. If the project depends on files or packages that are not in one of the
93project's directories, specify these external dependencies in the `WORKSPACE`
94file.
95
96Note: Place the project source code within the directory tree containing the
97 `WORKSPACE` file.
98
99### Step 2: (Experimental) Integrate CocoaPods dependencies {:#integrate-cocoapods}
100
101To integrate CocoaPods dependencies into the Bazel workspace, you must convert
102them into Bazel packages as described in [Converting CocoaPods dependencies](/migrate/cocoapods).
103
104Note: CocoaPods conversion is a manual process with many variables.
105CocoaPods integration with Bazel has not been fully verified and is not
106officially supported.
107
108### Step 3: Create a `BUILD` file {:#create-build-file}
109
110Once you have defined the workspace and external dependencies, you need to
111create a `BUILD` file that tells Bazel how the project is structured. Create
112the `BUILD` file at the root of the Bazel workspace and configure it to do an
113initial build of the project as follows:
114
115* [Step 3a: Add the application target](#step-3a-add-the-application-target)
116* [Step 3b: (Optional) Add the test target(s)](#step-3b-optional-add-the-test-target-s)
117* [Step 3c: Add the library target(s)](#step-3c-add-the-library-target-s)
118
119**Tip:** To learn more about packages and other Bazel concepts, see
120[Workspaces, packages, and targets](/concepts/build-ref).
121
122#### Step 3a: Add the application target {:#add-app-target}
123
124Add a [`macos_application`](https://github.com/bazelbuild/rules_apple/blob/master/doc/rules-macos.md#macos_application){: .external}
125or an [`ios_application`](https://github.com/bazelbuild/rules_apple/blob/master/doc/rules-ios.md#ios_application){: .external}
126rule target. This target builds a macOS or iOS application bundle, respectively.
127In the target, specify the following at the minimum:
128
129* `bundle_id` - the bundle ID (reverse-DNS path followed by app name) of the
130 binary.
131
132* `provisioning_profile` - provisioning profile from your Apple Developer
133 account (if building for an iOS device device).
134
135* `families` (iOS only) - whether to build the application for iPhone, iPad,
136 or both.
137
138* `infoplists` - list of .plist files to merge into the final Info.plist file.
139
140* `minimum_os_version` - the minimum version of macOS or iOS that the
141 application supports. This ensures Bazel builds the application with the
142 correct API levels.
143
144#### Step 3b: (Optional) Add the test target(s) {:#add-test-target}
145
146Bazel's [Apple build rules](https://github.com/bazelbuild/rules_apple){: .external} support
147running library-based unit tests on iOS and macOS, as well as application-based
148tests on macOS. For application-based tests on iOS or UI tests on either
149platform, Bazel will build the test outputs but the tests must run within Xcode
150through a project generated with Tulsi. Add test targets as follows:
151
152* [`macos_unit_test`](https://github.com/bazelbuild/rules_apple/blob/master/doc/rules-macos.md#macos_unit_test){: .external} to run library-based and application-based unit tests on a macOS.
153
154* [`ios_unit_test`](https://github.com/bazelbuild/rules_apple/blob/master/doc/rules-ios.md#ios_unit_test){: .external}
155 to run library-based unit tests on iOS. For tests requiring the iOS
156 simulator, Bazel will build the test outputs but not run the tests. You must
157 [generate an Xcode project with Tulsi](#step-5-generate-the-xcode-project-with-tulsi)
158 and run the tests from within Xcode.
159
160* [`ios_ui_test`](https://github.com/bazelbuild/rules_apple/blob/master/doc/rules-ios.md#ios_ui_test){: .external}
161 to build outputs required to run user interface tests in the iOS simulator
162 using Xcode. You must [generate an Xcode project with Tulsi](#step-5-generate-the-xcode-project-with-tulsi)
163 and run the tests from within Xcode. Bazel cannot natively run UI tests.
164
165At the minimum, specify a value for the `minimum_os_version` attribute. While
166other packaging attributes, such as `bundle_identifier` and `infoplists`,
167default to most commonly used values, ensure that those defaults are compatible
168with the project and adjust them as necessary. For tests that require the iOS
169simulator, also specify the `ios_application` target name as the value of the
170`test_host` attribute.
171
172
173#### Step 3c: Add the library target(s) {:#add-library-target}
174
175Add an [`objc_library`](/reference/be/objective-c#objc_library)
176target for each Objective C library and a [`swift_library`](https://github.com/bazelbuild/rules_apple/blob/master/doc/rules-swift.md){: .external}
177target for each Swift library on which the application and/or tests depend.
178
179
180Add the library targets as follows:
181
182* Add the application library targets as dependencies to the application
183 targets.
184
185* Add the test library targets as dependencies to the test targets.
186
187* List the implementation sources in the `srcs` attribute.
188
189* List the headers in the `hdrs` attribute.
190
191Note: You can use the [`glob`](/reference/be/functions#glob)
192function to include all sources and/or headers of a certain type. Use it
193carefully as it might include files you do not want Bazel to build.
194
195For more information on build rules, see [Apple Rules for Bazel](https://github.com/bazelbuild/rules_apple){: .external}.
196
197At this point, it is a good idea to test the build:
198
199`bazel build //:<application_target>`
200
201### Step 4: (Optional) Granularize the build {:#granularize-build}
202
203If the project is large, or as it grows, consider chunking it into multiple
204Bazel packages. This increased granularity provides:
205
206* Increased incrementality of builds,
207
208* Increased parallelization of build tasks,
209
210* Better maintainability for future users,
211
212* Better control over source code visibility across targets and packages. This
213 prevents issues such as libraries containing implementation details leaking
214 into public APIs.
215
216Tips for granularizing the project:
217
218* Put each library in its own Bazel package. Start with those requiring the
219 fewest dependencies and work your way up the dependency tree.
220
221* As you add `BUILD` files and specify targets, add these new targets to the
222 `deps` attributes of targets that depend on them.
223
224* The `glob()` function does not cross package boundaries, so as the number
225 of packages grows the files matched by `glob()` will shrink.
226
227* When adding a `BUILD` file to a `main` directory, also add a `BUILD` file to
228 the corresponding `test` directory.
229
230* Enforce healthy visibility limits across packages.
231
232* Build the project after each major change to the `BUILD` files and fix
233 build errors as you encounter them.
234
235### Step 5: Run the build {:#run-build}
236
237Run the fully migrated build to ensure it completes with no errors or warnings.
238Run every application and test target individually to more easily find sources
239of any errors that occur.
240
241For example:
242
243```posix-terminal
244bazel build //:my-target
245```
246
247### Step 6: Generate the Xcode project with Tulsi {:#generate-xcode-tulsi}
248
249When building with Bazel, the `WORKSPACE` and `BUILD` files become the source
250of truth about the build. To make Xcode aware of this, you must generate a
251Bazel-compatible Xcode project using [Tulsi](http://tulsi.bazel.build/).
252
253### Troubleshooting {:#troubleshooting}
254
255Bazel errors can arise when it gets out of sync with the selected Xcode version,
256like when you apply an update. Here are some things to try if you're
257experiencing errors with Xcode, for example "Xcode version must be specified to
258use an Apple CROSSTOOL".
259
260* Manually run Xcode and accept any terms and conditions.
261
262* Use Xcode select to indicate the correct version, accept the license, and
263 clear Bazel's state.
264
265```posix-terminal
266 sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
267
268 sudo xcodebuild -license
269
270 bazel sync --configure
271```
272
273* If this does not work, you may also try running `bazel clean --expunge`.
274
275Note: If you've saved your Xcode to a different path, you can use `xcode-select
276-s` to point to that path.