diff --git a/docs/bazel.md b/docs/bazel.md
index 179dc0051..fcdfa4e88 100755
--- a/docs/bazel.md
+++ b/docs/bazel.md
@@ -53,12 +53,13 @@ load("@rules_xcodeproj//xcodeproj:xcodeproj.bzl", "xcodeproj")
load("@rules_xcodeproj//xcodeproj/internal/docs:xcodeproj.bzl", "xcodeproj")
xcodeproj(*, name, associated_extra_files, bazel_path, bazel_env, config,
- default_xcode_configuration, extra_files, focused_targets, import_index_build_indexstores,
- install_directory, ios_device_cpus, ios_simulator_cpus, minimum_xcode_version, post_build,
- pre_build, project_name, project_options, scheme_autogeneration_mode,
- scheme_autogeneration_config, target_name_mode, top_level_targets, tvos_device_cpus,
- tvos_simulator_cpus, unfocused_targets, visionos_device_cpus, visionos_simulator_cpus,
- watchos_device_cpus, watchos_simulator_cpus, xcode_configurations, xcschemes, **kwargs)
+ default_xcode_configuration, extra_files, focused_targets, generator_bazel_env,
+ import_index_build_indexstores, install_directory, ios_device_cpus, ios_simulator_cpus,
+ minimum_xcode_version, post_build, pre_build, project_name, project_options,
+ scheme_autogeneration_mode, scheme_autogeneration_config, target_name_mode,
+ top_level_targets, tvos_device_cpus, tvos_simulator_cpus, unfocused_targets,
+ visionos_device_cpus, visionos_simulator_cpus, watchos_device_cpus, watchos_simulator_cpus,
+ xcode_configurations, xcschemes, **kwargs)
Creates an `.xcodeproj` file in the workspace when run.
@@ -97,6 +98,7 @@ xcodeproj(
| default_xcode_configuration | Optional. The name of the the Xcode configuration to use when building, if not overridden by custom schemes.
If not set, the first Xcode configuration alphabetically will be used. Use [`xcode_configurations`](#xcodeproj-xcode_configurations) to adjust Xcode configurations. | `None` |
| extra_files | Optional. A `list` of extra `File`s to be added to the project. | `[]` |
| focused_targets | Optional. A `list` of target labels as `string` values.
If specified, only these targets will be included in the generated project; all other targets will be excluded, as if they were listed explicitly in the `unfocused_targets` argument. The labels must match transitive dependencies of the targets specified in the `top_level_targets` argument. | `[]` |
+| generator_bazel_env | Optional. A `dict` of environment variables to set when invoking `bazel_path` during project generation only.
This behaves the same as `bazel_env`, but only applies to the generated runner's Bazel invocations while generating the project. These values are not written into the generated `.xcodeproj`, and they do not apply to Bazel builds launched later from Xcode. | `{}` |
| import_index_build_indexstores | Optional. Whether to import the index stores generated by Index Build.
This is useful if you want to use the index stores generated by Index Build to speed up Xcode's indexing process. You may not want this enabled if the additional work (mainly disk IO) of importing the index stores is not worth it for your project. | `True` |
| install_directory | Optional. The directory where the generated project will be written to.
The path is relative to the workspace root.
Defaults to the directory that the `xcodeproj` target is declared in (e.g. if the `xcodeproj` target is declared in `//foo/bar:BUILD` then the default value is `"foo/bar"`). Use `""` to have the project generated in the workspace root. | `None` |
| ios_device_cpus | Optional. The value to use for `--ios_multi_cpus` when building the transitive dependencies of the targets specified in the `top_level_targets` argument with the `"device"` `target_environment`.
**Warning:** Changing this value will affect the Starlark transition hash of all transitive dependencies of the targets specified in the `top_level_targets` argument with the `"device"` `target_environment`, even if they aren't iOS targets. | `"arm64"` |
diff --git a/xcodeproj/internal/xcodeproj_runner.bzl b/xcodeproj/internal/xcodeproj_runner.bzl
index 9454b5490..f94889627 100644
--- a/xcodeproj/internal/xcodeproj_runner.bzl
+++ b/xcodeproj/internal/xcodeproj_runner.bzl
@@ -6,6 +6,11 @@ load(":collections.bzl", "uniq")
load(":execution_root.bzl", "write_execution_root_file")
load(":providers.bzl", "XcodeProjRunnerOutputInfo")
+# Null character is used to represent `None`, since `attr.string_dict`
+# requires non-`None` values.
+_NULL_BAZEL_ENV_VALUE = "\0"
+_NULL_BAZEL_ENV_VALUE_LITERAL = "\\0"
+
def _process_extra_flags(*, attr, content, setting, config, config_suffix):
extra_flags = getattr(attr, setting)[BuildSettingInfo].value
if extra_flags:
@@ -272,6 +277,7 @@ def _write_runner(
execution_root_file,
extra_flags_bazelrc,
extra_generator_flags,
+ generator_bazel_env,
generator_build_file,
generator_defs_bzl,
install_path,
@@ -286,8 +292,10 @@ def _write_runner(
base_envs_values = []
collect_statements = []
for key, value in bazel_env.items():
- if value == "\0":
- base_def_env_values.append(' \\"{}\\": \\"\\\\0\\",'.format(key))
+ if value == _NULL_BAZEL_ENV_VALUE:
+ base_def_env_values.append(
+ ' \\"{}\\": \\"{}\\",'.format(key, _NULL_BAZEL_ENV_VALUE_LITERAL),
+ )
collect_statements.append("""\
if [[ -n "${{{key}:-}}" ]]; then
envs+=("{key}=${key}")
@@ -323,6 +331,25 @@ fi
),
))
+ for key, value in generator_bazel_env.items():
+ if value == _NULL_BAZEL_ENV_VALUE:
+ collect_statements.append("""\
+if [[ -n "${{{key}:-}}" ]]; then
+ envs+=("{key}=${key}")
+fi
+""".format(key = key))
+ else:
+ base_envs_values.append(" \"{}={}\"".format(
+ key,
+ (
+ value.replace(
+ # Escape double quotes for bash
+ "\"",
+ "\\\"",
+ )
+ ),
+ ))
+
collect_bazel_env = """\
envs=(
{base_envs_values}
@@ -439,6 +466,7 @@ def _xcodeproj_runner_impl(ctx):
extra_generator_flags = (
ctx.attr._extra_generator_flags[BuildSettingInfo].value
),
+ generator_bazel_env = ctx.attr.generator_bazel_env,
generator_build_file = generator_build_file,
generator_defs_bzl = generator_defs_bzl,
install_path = install_path,
@@ -475,6 +503,7 @@ xcodeproj_runner = rule(
"default_xcode_configuration": attr.string(),
"focused_labels": attr.string_list(default = []),
"generation_shard_count": attr.int(mandatory = True),
+ "generator_bazel_env": attr.string_dict(mandatory = True),
"import_index_build_indexstores": attr.bool(mandatory = True),
"install_directory": attr.string(mandatory = True),
"ios_device_cpus": attr.string(mandatory = True),
diff --git a/xcodeproj/xcodeproj.bzl b/xcodeproj/xcodeproj.bzl
index 76f6a13a5..2743390f8 100644
--- a/xcodeproj/xcodeproj.bzl
+++ b/xcodeproj/xcodeproj.bzl
@@ -13,6 +13,10 @@ load(
"xcscheme_labels",
)
+# Null character is used to represent `None`, since `attr.string_dict`
+# requires non-`None` values.
+_NULL_BAZEL_ENV_VALUE = "\0"
+
def _normalize_build_setting(flag):
if flag.startswith("//command_line_option:"):
return flag
@@ -31,6 +35,7 @@ def xcodeproj(
default_xcode_configuration = None,
extra_files = [],
focused_targets = [],
+ generator_bazel_env = {},
import_index_build_indexstores = True,
install_directory = None,
ios_device_cpus = "arm64",
@@ -150,6 +155,13 @@ def xcodeproj(
listed explicitly in the `unfocused_targets` argument. The labels
must match transitive dependencies of the targets specified in the
`top_level_targets` argument.
+ generator_bazel_env: Optional. A `dict` of environment variables to
+ set when invoking `bazel_path` during project generation only.
+
+ This behaves the same as `bazel_env`, but only applies to the
+ generated runner's Bazel invocations while generating the project.
+ These values are not written into the generated `.xcodeproj`, and
+ they do not apply to Bazel builds launched later from Xcode.
import_index_build_indexstores: Optional. Whether to import the index
stores generated by Index Build.
@@ -365,6 +377,7 @@ def xcodeproj(
if not bazel_path:
bazel_path = "bazel"
bazel_env = dict(bazel_env) if bazel_env else {}
+ generator_bazel_env = dict(generator_bazel_env) if generator_bazel_env else {}
if "PATH" not in bazel_env:
bazel_env["PATH"] = "/bin:/usr/bin"
if "LANG" not in bazel_env:
@@ -386,11 +399,13 @@ def xcodeproj(
bazel_env["BAZELISK_SKIP_WRAPPER"] = None
bazel_env = {
- # Null character is used to represent `None`, since `attr.string_dict`
- # requires non-`None` values.
- key: "\0" if value == None else value
+ key: _NULL_BAZEL_ENV_VALUE if value == None else value
for key, value in sorted(bazel_env.items())
}
+ generator_bazel_env = {
+ key: _NULL_BAZEL_ENV_VALUE if value == None else value
+ for key, value in sorted(generator_bazel_env.items())
+ }
if default_xcode_configuration and default_xcode_configuration not in xcode_configurations:
keys = sorted(xcode_configurations.keys())
@@ -530,6 +545,7 @@ for {configuration} ({new_keys}) do not match keys of other configurations \
config = config,
default_xcode_configuration = default_xcode_configuration,
focused_labels = focused_labels,
+ generator_bazel_env = generator_bazel_env,
generation_shard_count = generation_shard_count,
import_index_build_indexstores = import_index_build_indexstores,
install_directory = install_directory,