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.
Instructions can be found in the project's README
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 bazel_build.py
script and their operation can be retrieved from Bazel's help.
Flags used for all build modes:
--config=ios_$(arch)
- sets up predefined Bazel options for the architecture exposed by Xcode.--ios_minimum_os
- passed through from the Tulsi IPHONEOS_DEPLOYMENT_TARGET
option.--xcode_version
- passes the version of Xcode that invokes the compilation script.Mode dependent flags:
--compilation_mode
- set to dbg
, opt
, or fastbuild
depending on the Xcode build mode (Debug, Release, or Fastbuild).Debug mode flags:
--copt
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.--objccopt
flags:-fdebug-compilation-dir
- as above.--apple_generate_dsym
- generates dSYM bundles.Release mode flags:
--apple_generate_dsym
- generates dSYM bundles.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.
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 ios_application
and another containing ios_test
rules.
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.
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 tulsiconf-user
files, which contain settings that are likely to be user specific (such as absolute paths).
Tulsi generator configuration files are created through a simple wizard flow.
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 ios_application
and maybe associated ios_test
targets.
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.
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.
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.
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.
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.
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.