Migrate a Bazel project
Bazel is a build system that Google open-sourced in 2015. It's a powerful tool that allows you to build and test software of any size, quickly and reliably. Some large organizations like Spotify, Tinder, or Lyft use it, however, it requires an upfront (i.e., learning the technology) and ongoing investment (i.e., keeping up with Xcode updates) to introduce and maintain. While this works for some organizations that treat it as a cross-cutting concern, it might not be the best fit for others that want to focus on their product development. For instance, we've seen organizations whose iOS platform team introduced Bazel and had to drop it after the engineers that led the effort left the company. Apple's stance on the strong coupling between Xcode and the build system is another factor that makes it hard to maintain Bazel projects over time.
TUIST UNIQUENESS LIES IN ITS FINESSE
Instead of fighting Xcode and Xcode projects, Tuist embraces it. It's the same concepts (e.g., targets, schemes, build settings), a familiar language (i.e., Swift), and a simple and enjoyable experience that makes maintaining and scaling projects everyone's job and not just the iOS platform team's.
Rules
Bazel uses rules to define how to build and test software. The rules are written in Starlark, a Python-like language. Tuist uses Swift as a configuration language, which provides developers with the convenience of using Xcode's autocompletion, type-checking, and validation features. For example, the following rule describes how to build a Swift library in Bazel:
swift_library(
name = "MyLibrary.library",
srcs = glob(["**/*.swift"]),
module_name = "MyLibrary"
)
let project = Project(
// ...
targets: [
.target(name: "MyLibrary", product: .staticLibrary, sources: ["**/*.swift"])
]
)
Here's another example but compating how to define unit tests in Bazel and Tuist:
ios_unit_test(
name = "MyLibraryTests",
bundle_id = "io.tuist.MyLibraryTests",
minimum_os_version = "16.0",
test_host = "//MyApp:MyLibrary",
deps = [":MyLibraryTests.library"],
)
let project = Project(
// ...
targets: [
.target(
name: "MyLibraryTests",
destinations: .iOS,
product: .unitTests,
bundleId: "io.tuist.MyLibraryTests",
sources: "Tests/MyLibraryTests/**",
dependencies: [
.target(name: "MyLibrary"),
]
)
]
)
Swift Package Manager dependencies
In Bazel, you can use the rules_swift_package_manager
Gazelle plugin to use Swift Packages as dependencies. The plugin requires a Package.swift
as a source of truth for the dependencies. Tuist's interface is similar to Bazel's in that sense. You can use the tuist install
command to resolve and pull the dependencies of the package. After the resolution completes, you can then generate the project with the tuist generate
command.
tuist install # Fetch dependencies defined in Tuist/Package.swift
tuist generate # Generate an Xcode project
Project generation
The community provides a set of rules, rules_xcodeproj, to generate Xcode projects off Bazel-declared projects. Unlike Bazel, where you need to add some configuration to your BUILD
file, Tuist doesn't require any configuration at all. You can run tuist generate
in the root directory of your project, and Tuist will generate an Xcode project for you.