The way your project is generated, and the way Bazel builds it inside of Xcode,
can be customized with configs in .bazelrc
files.
The rules_xcodeproj
config is used when building the project inside of Xcode.
It’s also inherited by all of the other rules_xcodeproj_*
configs.
Warning
Build affecting flags need to be the same between all rules_xcodeproj{_*}
configs, so it’s usually better to adjust this config (rules_xcodeproj
) over
the other ones, unless you actually need to target them specifically.
The rules_xcodeproj_generator
config is used when generating the project (i.e.
when you run bazel run //:xcodeproj
).
The types of things you might want to adjust on this config are non-build affecting, like adjusting build log output.
The rules_xcodeproj_indexbuild
config is used when Xcode performs an Index
Build (also known as “Prepare for Indexing”), where it builds the project in the
background to a separate output directory, to ensure that it has the required
artifacts to perform per-file indexing based compiles.
Since Index Build runs in the background quite frequently, sometimes once per
target in the project, and then once per saving of a file, the types of things
you might want to adjust on this config are probably log output or telemetry
related (and the default config disables BES upload for this reason). For
example, if you set --profile
globally or on the rules_xcodeproj
config,
you will want to set --profile=
(clearing the value) on
rules_xcodeproj_indexbuild
to prevent Index Builds from overwriting the
profile:
build:rules_xcodeproj --profile=/tmp/profile.gz
build:rules_xcodeproj_indexbuild --profile=
The rules_xcodeproj_swiftuipreviews
config is used when Xcode performs a
SwiftUI Preview build.
You shouldn’t need to adjust this config. The default config applies the needed build adjusting flags.
Each xcodeproj
can specify a set of configs which inherit from the
rules_xcodeproj
family of configs. You can specify the base config with the
config
attribute. For example, if you set config = "projectx_xcodeproj"
,
then the projectx_xcodeproj
, projectx_xcodeproj_generator
,
projectx_xcodeproj_indexbuild
, and projectx_xcodeproj_swiftuipreviews
configs are available to adjust, and they all inherit from their respective
rules_xcodeproj{_*}
configs.
Using this feature adds a layer of indirection, and should only be used if you have project-specific configurations you need to apply.
Finally, there is one last way to adjust the Bazel configs, through the use of
the --@rules_xcodeproj//xcodeproj:extra_*_flags
family of build flags. These flags apply changes to the configs after all other
sources, and work when calling bazel run
to generate the project. If using
project-level configs, these flags adjust those instead of the base configs.
extra_common_flags
: Applied to the parent config (i.e.rules_xcodeproj
)extra_generator_flags
: Applied to the generator config (i.e.rules_xcodeproj_generator
)extra_indexbuild_flags
: Applied to the Index Build config (i.e.rules_xcodeproj_indexbuild
)extra_swiftuipreviews_flags
: Applied to the Xcode Previews build config (i.e.rules_xcodeproj_swiftuipreviews
)
A project xcodeproj.bazelrc
file is loaded before the workspace .bazelrc
.
It’s created from a
template, which contains
the default configs mentioned above, and will also contain stubs for the
project-level configs if they are used.
At the end of the project xcodeproj.bazelrc
file is a conditional import of a
workspace level xcodeproj.bazelrc
file. Since startup flags (e.g.
--host_jvm_args
) can’t be applied to configs, they can instead be set in this
file, and they will only apply to rules_xcodeproj bazel
invocations. If you
have to generate all or part of your rules_xcodeproj configs, this is a
convenient file to use for that.
Note
--output_base
is set by rules_xcodeproj in order to nest the output base inside of the primary output base (see the command-line API section for more details). Thus, settingstartup --output_base
inxcodeproj.bazelrc
will have no effect.
Next the normal workspace .bazelrc
file is imported. Here you can adjust the
configs further.
Finally, if any
--@rules_xcodeproj//xcodeproj:extra_*_flags
build
flags were used during project generation, then those adjustments are made in
a project xcodeproj_extra_flags.bazelrc
file, which is loaded after the
workspace .bazelrc
file. This ensures that they override any flags set
earlier, mimicking the behavior of command-line set flags taking precedence.
rules_xcodeproj builds targets in its own
output base.
It does this to ensure that the
analysis cache
isn’t affected by other Bazel commands, including project generation itself.
In addition, rules_xcodeproj sets one of the project’s
Bazel configs. Because of this, normal Bazel commands, such
as bazel build
or bazel clean
, won’t be the same as what is performed in
Xcode.
To enable you to perform Bazel commands in the same “environment” that
rules_xcodeproj itself uses, we provide a command-line API. Assuming your
xcodeproj
target is defined at //:xcodeproj
, this is how you can call this
API:
bazel run //:xcodeproj -- [option ...] command_string
For example, this will call bazel info output_path
in the rules_xcodeproj
environment:
bazel run //:xcodeproj -- 'info output_path'
This will build all targets in the project the same way as Xcode Previews does:
bazel run //:xcodeproj -- --config=swiftuipreviews --generator_output_groups=all_targets build
The API supports all the
commands
bazel
supports. It does not support providing
startup options,
though you can specify those in the
xcodeproj.bazelrc
file.
Below are notes about various commands.
To build targets the same way as rules_xcodeproj requires more than just using
this API, because the xcodeproj
rule applies a
configuration transition
to targets. That means that building targets by specifying their labels will
build potentially different versions of those targets, and minimally versions
that have different cache keys.
rules_xcodeproj uses output groups to “address” these correctly configured targets. It uses a set of private output groups, but it also exposes some public ones.
To build these output groups with this API you would have to craft a call like this (note: this is not the recommended way to do this, and might break in the future, continue reading after the example for the recommended way):
bazel run //:xcodeproj -- 'build --remote_download_minimal --output_groups=all_targets //:xcodeproj.generator'
This requires knowing the internal name of the generator target
(//:xcodeproj.generator
in this example), and it also doesn’t apply some flags
that Xcode bazel build
command applies (e.g.
--experimental_remote_download_regex
). Instead, it’s recommended that you use
the --generator_output_groups
option:
bazel run //:xcodeproj -- --generator_output_groups=all_targets 'build --remote_download_minimal'
When you run bazel clean
normally (i.e. not using this API), it won’t affect
the rules_xcodeproj output base the way you expect. bazel clean --expunge
will though, as it will blow away both environments. To clean the
rules_xcodeproj output base use the API instead:
bazel run //:xcodeproj -- clean
Depending on how you have your rules_xcodeproj Bazel configs
set up, you might be able to run bazel query
without using the API. I
recommend using the API instead though, to prevent fetching external
dependencies in the primary output base. The other queries, cquery
and
aquery
, should always be performed through the API, to ensure the targets
are properly configured:
bazel run //:xcodeproj -- 'aquery "set(//some:target)"'
You can specify some options before the Bazel command:
Prints the command that was executed.
Without -v
:
$ bazel run --config=cache //:xcodeproj -- clean
INFO: Invocation ID: e4be5bb9-1823-4ca9-a3fd-6066f936460a
INFO: Analyzed target //:xcodeproj (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //:xcodeproj up-to-date:
/Users/brentley/Developer/rules_xcodeproj/bazel-output-base/execroot/rules_xcodeproj/bazel-out/darwin_arm64-fastbuild/bin/xcodeproj-runner.sh
INFO: Elapsed time: 0.285s, Critical Path: 0.00s
INFO: 1 process: 1 internal.
INFO: Running command line: /Users/brentley/Developer/rules_xcodeproj/bazel-output-base/execroot/rules_xcodeproj/bazel-out/darwin_arm64-fastbuild/bin/xcodeproj-runner.sh -v clean
INFO: Build completed successfully, 1 total action
INFO: Invocation ID: 84c53471-73a4-4267-9289-0ad076ee94fb
INFO: Starting clean.
With -v
:
$ bazel run --config=cache //:xcodeproj -- -v clean
INFO: Invocation ID: e4be5bb9-1823-4ca9-a3fd-6066f936460a
INFO: Analyzed target //:xcodeproj (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //:xcodeproj up-to-date:
/Users/brentley/Developer/rules_xcodeproj/bazel-output-base/execroot/rules_xcodeproj/bazel-out/darwin_arm64-fastbuild/bin/xcodeproj-runner.sh
INFO: Elapsed time: 0.285s, Critical Path: 0.00s
INFO: 1 process: 1 internal.
INFO: Running command line: /Users/brentley/Developer/rules_xcodeproj/bazel-output-base/execroot/rules_xcodeproj/bazel-out/darwin_arm64-fastbuild/bin/xcodeproj-runner.sh -v clean
INFO: Build completed successfully, 1 total action
Running Bazel command:
+ env PATH=/bin:/usr/bin /Users/brentley/Library/Caches/bazelisk/downloads/bazelbuild/bazel-5.3.0-darwin-arm64/bin/bazel --host_jvm_args=-Xdock:name=/Applications/Xcode-14.0.1.app/Contents/Developer --noworkspace_rc --bazelrc=/Users/brentley/Developer/rules_xcodeproj/bazel-output-base/execroot/rules_xcodeproj/bazel-out/darwin_arm64-fastbuild/bin/xcodeproj-runner.sh.runfiles/rules_xcodeproj/xcodeproj.bazelrc --bazelrc=.bazelrc --bazelrc=/Users/brentley/Developer/rules_xcodeproj/bazel-output-base/execroot/rules_xcodeproj/bazel-out/darwin_arm64-fastbuild/bin/xcodeproj-runner.sh.runfiles/rules_xcodeproj/xcodeproj-extra-flags.bazelrc --output_base /Users/brentley/Developer/rules_xcodeproj/bazel-output-base/execroot/_rules_xcodeproj/build_output_base clean --repo_env=DEVELOPER_DIR=/Applications/Xcode-14.0.1.app/Contents/Developer --repo_env=USE_CLANG_CL=14A400 --config=_rules_xcodeproj_build
INFO: Invocation ID: 84c53471-73a4-4267-9289-0ad076ee94fb
INFO: Starting clean.
Changes the Bazel config that is used. Valid values are:
build
:rules_xcodeproj
or the project-level equivalent. This is the default if--config
isn’t specified.indexbuild
:rules_xcodeproj_indexbuild
or the project-level equivalent.swiftuipreviews
:rules_xcodeproj_swiftuipreviews
or the project-level equivalent.
For example, this will build all targets in the project the same way as Xcode Previews does:
bazel run //:xcodeproj -- --config=swiftuipreviews --generator_output_groups=all_targets build
If the Bazel command is build
, then this builds the specified generator
outputs groups, potentially adding additional flags to match the behavior of
Xcode’s bazel build
(e.g. --experimental_remote_download_regex
).
These are the available output groups to use:
all_targets
: This will build every target specified bytop_level_targets
. This is useful to build in “cache warming” jobs.
For example, this will build all targets the same way that Xcode does:
bazel run //:xcodeproj -- --generator_output_groups=all_targets build
By default, when using
[--generator_output_groups
][#--generator_output_groups],
intermediate outputs (such as generated files and index stores) won't be
downloaded from a remote cache if --remote_download_toplevel
or
--remote_download_minimal
is used. This is to support the common use case of
using the command-line API to warm a remote cache on a CI job that uses
--remote_download_minimal
and doesn't want anything downloaded.
To cause intermediates to be downloaded the same way that a normal Xcode build
downloads them, pass the --download_intermediates
flag:
bazel run //:xcodeproj -- --generator_output_groups=all_targets --download_intermediates build
In your command, any reference to these variables will be expanded:
-
$_GENERATOR_LABEL_
: The label of the generator target (e.g.@@_main~internal~rules_xcodeproj_generated//generator/xcodeproj
). Useful for certainaquery
commands:$bazel run //:xcodeproj -- 'aquery $_GENERATOR_LABEL_' ... INFO: Invocation ID: ca31d4b4-0df5-49de-a020-c70a922521af INFO: Streaming build results to: https://app.buildbuddy.io/invocation/ca31d4b4-0df5-49de-a020-c70a922521af INFO: Analyzed target @_main~internal~rules_xcodeproj_generated//generator/tools/generators/legacy/xcodeproj:xcodeproj (1 packages loaded, 2 targets configured). INFO: Found 1 target... action 'Writing file external/_main~internal~rules_xcodeproj_generated/generator/xcodeproj/xcodeproj-xcode_generated_paths.json' Mnemonic: FileWrite Target: @rules_xcodeproj_generated//generator/xcodeproj:xcodeproj Configuration: darwin_arm64-dbg Execution platform: @local_config_platform//:host ActionKey: f0332c573c0b3ae2ebe525959057fd901d6ce582948b19f7b8e2fdee3f43f045 Inputs: [] Outputs: [bazel-out/darwin_arm64-dbg/bin/external/_main~internal~rules_xcodeproj_generated/generator/xcodeproj/xcodeproj-xcode_generated_paths.json] ...