How does it work?
Tulsi uses information in Bazel
BUILD files in order to generate Xcode
projects. The primary difference between Tulsi-generated projects and those
created by the native Bazel "xcodeprojgen" is that Tulsi projects use Bazel to
compile and sign binaries, rather than Xcode's native infrastructure. This means
that the binary you use during development will be exactly the same as the one
you make via a commandline build. In addition, Tulsi projects can operate on a
subset of the source files in your project. For large projects this can mean a
significant reduction in the amount of time Xcode spends indexing files.
Tulsi's reliance on Bazel also has some interesting side effects. For instance, Tulsi-generated projects do not include Info.plist files directly as they are governed by the Bazel BUILD infrastructure. It also means that changes made to your BUILD files, such as adding new library dependencies, are incorporated automatically when building your generated project. The only time you need to re-run Tulsi is if you want to add additional build targets or have new source files show up in Xcode for editing.
What Bazel flags are used when building?
Note: this list is limited to "interesting" flags and is not necessarily
perfectly up to date. The current set of flags can be seen in the
and their operation can be retrieved from Bazel's help.
Flags used for all build modes:
--experimental_enable_objc_cc_deps- allows cc_library dependencies.
--config=ios_$(arch)- sets up predefined Bazel options for the architecture exposed by Xcode.
--ios_minimum_os- passed through from the Tulsi
--xcode_version- passes the version of Xcode that invokes the compilation script.
Mode dependent flags:
--compilation_mode- set to
fastbuilddepending on the Xcode build mode (Debug, Release, or Fastbuild).
Debug mode flags:
-fdebug-compilation-dir- instructs the compiler to use the specified path when generating debug symbols. This allows Xcode to find source files when debugging Bazel-generated binaries.
-fdebug-compilation-dir- as above.
--apple_generate_dsym- generates dSYM bundles.
Release mode flags:
--apple_generate_dsym- generates dSYM bundles.
How do I use it?
The Tulsi project editor
1. Create a new Tulsi project
Tulsi leverages a project bundle which captures the set of Bazel packages in your project and provides a convenient location to store shared generator configuration files. Generator configuration files ("gen configs") capture a set of Bazel targets to build, sources to expose in Xcode, and associated options and are translated directly into Xcode projects by Tulsi.
The first step is to create a Tulsi project by launching the Tulsi app.
Give your project a name and select the location of your Bazel WORKSPACE file, then click "Next".
At this point you should see the "Packages" tab for your project. This is where you'll add any BUILD files in your project as well as set the path to the Bazel binary that will be used both to generate an Xcode project and to compile.
2. Add BUILD files
Click on the "+" button to add your BUILD file. Repeat this step if you have
more than one BUILD file containing targets you wish to build directly. For
example, you might have one BUILD file with your
3. Set default options if applicable
Tulsi allows you to set various options that are used by the generated Xcode project. Probably the most interesting are the "'build' options", which are used directly by Bazel during compilation. Tulsi options may be set in two places, at the project level (via the "Default options" tab) and on a per-gen config basis. The values set in the "Default options" tab will be used when creating new Tulsi gen configs and are most useful for options that will be the same for every developer working on your project.
4. Create project generator configs
The final step in setting up your project is to create one or more generator configurations. A larger project might have several gen configs, perhaps one with sources for the UI layer, another with an important supporting library, etc... Gen configs allow you to tailor the set of sources indexed by Xcode to your preference without needing to include every source file in order to compile.
Clicking the "+" button will allow you to add new generator configs. Double clicking on an existing config will allow you to edit it, and the "-" button can be used to permanently delete previously created configs.
Note that if you haven't already saved the project, you'll be asked to do so
the first time you add a config. You can save the project pretty much
wherever you like, but you'll get the most benefit out of checking it into
your source tree so it may be shared by other developers on your team. The
project bundle is entirely shareable apart from the
which contain settings that are likely to be user specific (such as absolute
The Tulsi generator configuration editor
Tulsi generator configuration files are created through a simple wizard flow.
1. Select build targets
The first page of the config editor shows the full list of targets contained in the BUILD files associated with the project.
Select one or more targets that you want to build in Xcode. For a typical
project, like the PrenotCalculator demo, you'll choose your
and maybe associated
2. Set options
If you need to set any options for your config, this is the place. The option editor will be prepopulated with the values set in the "Default options" tab in the project, but you may modify or add anything you'd like here.
Options may be set both at a project-level (affecting all build targets)...
... and a per-target level, affecting only one selected build target.
To edit an option's value, click on the cell. Values may also be double clicked in order to pop a larger editor.
3. Select source targets
The wizard will then show the dependencies of the build targets you've selected that contain source files. This allows you to select a working set from your full source tree that best matches the portion of the project that you're likely to edit. There is also a "recursive" option, which will include the selected folder and any folder inside of it (even folders that are created after the config). PrenotCalculator is a small enough project that it makes sense to select all of the source targets.
4. Set the config's name if necessary
If this is a new generator config, you'll be asked to provide a name before saving. This name is used only to differentiate configs and does not have a direct effect on the generated Xcode project.
For instance, if the "PrenotCalculator" project had two configs named "Small config" and "Full config", both would generate an Xcode bundle named PrenotCalculator.xcodeproj.
Generating Xcode projects
Once your project is set up, you'll want to generate an Xcode project from one of your configs. This is done by navigating to the "Configs" tab...
... then selecting the config to generate and pressing the "Generate" button.
Tulsi will ask you where to save the generated Xcode project...
... and will then do the actual generation. This process can take some time for larger projects. Progress bars will be displayed so you know something interesting is happening.
Finally, Tulsi will launch Xcode with the newly generated project file.
Tulsi-Xcode project features
Of note is the fact that the generated file does not have an Info.plist, as the contents are entirely driven by the contents of your Bazel BUILD files.
Also, rather than associating source files with your build target, a "Run Script" phase is generated that uses a helper script to invoke Bazel when you build the project. This also means that the majority of the Build Settings displayed in Xcode do not affect the build; Bazel's BUILD files are used for more or less everything.
One or more "__indexer_" targets will also be created, this is how Tulsi interacts with Xcode's indexer to get things like autocomplete and cmd-click navigation to work.
Generating Xcode projects from the command-line
Tulsi can also create Xcode projects from generator configs without launching the Tulsi UI. There is a helper script to seek out and apply command line parameters to the Tulsi app binary.
Running this script with no arguments will print information on usage.
Note: the script uses mdfind by default, so Tulsi must be installed in a
Spotlight-indexed location or the
TULSI_APP environment variable must be used.
Please see the script for details.