Getting started
Tulsi

Getting started

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 bazel_build.py script 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 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.

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.

NewProject

Give your project a name and select the location of your Bazel WORKSPACE file, then click "Next".

SelectWorkspace

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.

EmptyPackages

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 ios_application and another containing ios_test rules.

SelectBUILDFile

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.

DefaultOptions

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.

EmptyConfigs

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).

ForcedProjectSave

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.

ConfigBuildTargets

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.

ConfigBuildTargetSelected

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)...

ConfigProjectOptions

... and a per-target level, affecting only one selected build target. ConfigTargetOptions

To edit an option's value, click on the cell. Values may also be double clicked in order to pop a larger editor. ConfigEditingProjectOption ConfigModifiedProjectOption

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.

ConfigAllSourceTargetsSelected

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.

ConfigSetName

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...

GeneratedConfig

... then selecting the config to generate and pressing the "Generate" button. SelectedConfig

Tulsi will ask you where to save the generated Xcode project...

XcodeProjectFolderSelection

... 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.

XcodeProject

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. XcodeEmptyInfoPlist

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.

XcodeRunScript

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.