diff --git a/CHANGELOG.md b/CHANGELOG.md
index c45df2f84..1fddbbd52 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -48,11 +48,12 @@ END_UNRELEASED_TEMPLATE
### New
-* TBD
+* Added support for pre- and post-actions on autogenerated schemes, including explicit build post-action failure handling: [#3307](https://github.com/MobileNativeFoundation/rules_xcodeproj/pull/3307)
+* Added `run_build_post_actions_on_failure` to custom scheme `xcschemes.run(...)` configuration for explicit build post-action failure handling: [#3307](https://github.com/MobileNativeFoundation/rules_xcodeproj/pull/3307)
### Adjusted
-* TBD
+* We now resolve `bazel_env` earlier to reduce analysis cache invalidation when environment values are resolved dynamically: [#3305](https://github.com/MobileNativeFoundation/rules_xcodeproj/pull/3305)
### Fixed
diff --git a/docs/bazel.md b/docs/bazel.md
index 415b9037e..179dc0051 100755
--- a/docs/bazel.md
+++ b/docs/bazel.md
@@ -107,7 +107,7 @@ xcodeproj(
| project_name | Optional. The name to use for the `.xcodeproj` file.
If not specified, the value of the `name` argument is used. | `None` |
| project_options | Optional. A value returned by `project_options`. | `None` |
| scheme_autogeneration_mode | Optional. Specifies how Xcode schemes are automatically generated:
- `auto`: If no custom schemes are specified, via `schemes`, an Xcode scheme will be created for every buildable target. If custom schemes are provided, no autogenerated schemes will be created.
- `none`: No schemes are automatically generated.
- `all`: A scheme is generated for every buildable target even if custom schemes are provided.
| `"auto"` |
-| scheme_autogeneration_config | Optional. A value returned by [`xcschemes.autogeneration_config`](#xcschemes.autogeneration_config).
Allows further configuration of `scheme_autogeneration_mode`. | `{}` |
+| scheme_autogeneration_config | Optional. A value returned by [`xcschemes.autogeneration_config`](#xcschemes.autogeneration_config).
Allows further configuration of `scheme_autogeneration_mode`. | `None` |
| target_name_mode | Optional. Specifies how Xcode targets names are represented:
- `auto`: Use the product name if it is available and there is no collision. Otherwise select the target name from the label. And if there is a collision, use the full label.
- `label`: Always use full label for Xcode targets names.
| `"auto"` |
| top_level_targets | A `list` of a list of top-level targets.
Each target can be specified as either a `Label` (or label-like `string`), a value returned by `top_level_target`, or a value returned by `top_level_targets`. | none |
| tvos_device_cpus | Optional. The value to use for `--tvos_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 tvOS targets. | `"arm64"` |
@@ -235,6 +235,61 @@ Defines a command-line argument.
| literal_string | Whether `value` should be interpreted as a literal string.
If `True`, any spaces will be escaped. This means that `value` will be passed to the launch target as a single string. If `False`, any spaces will not be escaped. This is useful to group multiple arguments under a single checkbox in Xcode. | `True` |
+
+
+## xcschemes.autogeneration.profile
+
+
+load("@rules_xcodeproj//xcodeproj/internal/docs:xcschemes.bzl", "xcschemes")
+
+xcschemes.autogeneration.profile(*, post_actions, pre_actions)
+
+
+Creates a value for the `profile` argument of `xcschemes.autogeneration_config`.
+
+**PARAMETERS**
+
+
+| Name | Description | Default Value |
+| :------------- | :------------- | :------------- |
+| post_actions | Profile post-actions for autogenerated schemes.
Defaults to `[]`. | `[]` |
+| pre_actions | Profile pre-actions for autogenerated schemes.
Defaults to `[]`. | `[]` |
+
+**RETURNS**
+
+An opaque value for the
+ [`profile`](user-content-xcschemes.autogeneration_config-profile)
+ argument of `xcschemes.autogeneration_config`.
+
+
+
+
+## xcschemes.autogeneration.run
+
+
+load("@rules_xcodeproj//xcodeproj/internal/docs:xcschemes.bzl", "xcschemes")
+
+xcschemes.autogeneration.run(*, post_actions, pre_actions, run_build_post_actions_on_failure)
+
+
+Creates a value for the `run` argument of `xcschemes.autogeneration_config`.
+
+**PARAMETERS**
+
+
+| Name | Description | Default Value |
+| :------------- | :------------- | :------------- |
+| post_actions | Build and run post-actions for autogenerated schemes.
Defaults to `[]`. | `[]` |
+| pre_actions | Build and run pre-actions for autogenerated schemes.
Defaults to `[]`. | `[]` |
+| run_build_post_actions_on_failure | Whether autogenerated build post-actions should run even when the build fails. | `False` |
+
+**RETURNS**
+
+An opaque value for the
+ [`run`](user-content-xcschemes.autogeneration_config-run)
+ argument of `xcschemes.autogeneration_config`.
+
+
## xcschemes.autogeneration.test
@@ -242,7 +297,7 @@ Defines a command-line argument.
load("@rules_xcodeproj//xcodeproj/internal/docs:xcschemes.bzl", "xcschemes")
-xcschemes.autogeneration.test(*, options)
+xcschemes.autogeneration.test(*, options, post_actions, pre_actions)
Creates a value for the `test` argument of `xcschemes.autogeneration_config`.
@@ -253,6 +308,8 @@ Creates a value for the `test` argument of `xcschemes.autogeneration_config`.
| Name | Description | Default Value |
| :------------- | :------------- | :------------- |
| options | Test options for autogeneration.
Defaults to `None`. | `None` |
+| post_actions | Test post-actions for autogenerated schemes.
Defaults to `[]`. | `[]` |
+| pre_actions | Test pre-actions for autogenerated schemes.
Defaults to `[]`. | `[]` |
**RETURNS**
@@ -268,7 +325,7 @@ An opaque value for the
load("@rules_xcodeproj//xcodeproj/internal/docs:xcschemes.bzl", "xcschemes")
-xcschemes.autogeneration_config(*, scheme_name_exclude_patterns, test)
+xcschemes.autogeneration_config(*, profile, run, scheme_name_exclude_patterns, test)
Creates a value for the [`scheme_autogeneration_config`](xcodeproj-scheme_autogeneration_config) attribute of `xcodeproj`.
@@ -278,6 +335,8 @@ Creates a value for the [`scheme_autogeneration_config`](xcodeproj-scheme_autoge
| Name | Description | Default Value |
| :------------- | :------------- | :------------- |
+| profile | Options to use for the profile action.
Example:
xcodeproj(
...
scheme_autogeneration_config = xcschemes.autogeneration_config(
profile = xcschemes.autogeneration.profile(
pre_actions = [
xcschemes.pre_post_actions.launch_script(
title = "Profile Start",
script_text = "echo profile",
),
],
),
),
)
| `None` |
+| run | Options to use for the build and run actions.
Example:
xcodeproj(
...
scheme_autogeneration_config = xcschemes.autogeneration_config(
run = xcschemes.autogeneration.run(
pre_actions = [
xcschemes.pre_post_actions.build_script(
title = "Build Start",
script_text = "echo build start",
),
xcschemes.pre_post_actions.launch_script(
title = "Run Start",
script_text = "echo run",
),
],
),
),
)
| `None` |
| scheme_name_exclude_patterns | A `list` of regex patterns used to skip creating matching autogenerated schemes.
Example:
xcodeproj(
...
scheme_name_exclude_patterns = xcschemes.autogeneration_config(
scheme_name_exclude_patterns = [
".*somePattern.*",
"^AnotherPattern.*",
],
),
)
| `None` |
| test | Options to use for the test action.
Example:
xcodeproj(
...
scheme_autogeneration_config = xcschemes.autogeneration_config(
test = xcschemes.autogeneration.test(
options = xcschemes.test_options(
app_language = "en",
app_region = "US",
code_coverage = False,
)
)
)
)
| `None` |
@@ -496,7 +555,7 @@ Defines the Profile action.
load("@rules_xcodeproj//xcodeproj/internal/docs:xcschemes.bzl", "xcschemes")
xcschemes.run(*, args, build_targets, diagnostics, env, env_include_defaults, launch_target,
- storekit_configuration, xcode_configuration)
+ run_build_post_actions_on_failure, storekit_configuration, xcode_configuration)
Defines the Run action.
@@ -512,6 +571,7 @@ Defines the Run action.
| env | Environment variables to use when running the launch target.
If set to `"inherit"`, then the environment variables will be supplied by the launch target (e.g. [`cc_binary.env`](https://bazel.build/reference/be/common-definitions#binary.env)). Otherwise, the `dict` of environment variables will be set as provided, and `None` or `{}` will result in no environment variables.
Each value of the `dict` can either be a string or a value returned by [`xcschemes.env_value`](#xcschemes.env_value). If a value is a string, it will be transformed into `xcschemes.env_value(value)`. For example, xcschemes.run(
env = {
"VAR1": "value 1",
"VAR 2": xcschemes.env_value("value2", enabled = False),
},
)
will be transformed into: xcschemes.run(
env = {
"VAR1": xcschemes.env_value("value 1"),
"VAR 2": xcschemes.env_value("value2", enabled = False),
},
)
| `"inherit"` |
| env_include_defaults | Whether to include the rules_xcodeproj provided default Bazel environment variables (e.g. `BUILD_WORKING_DIRECTORY` and `BUILD_WORKSPACE_DIRECTORY`), in addition to any set by [`env`](#xcschemes.run-env). This does not apply to [`xcschemes.launch_path`](#xcschemes.launch_path)s. | `True` |
| launch_target | The target to launch when running.
Can be `None`, a label string, a value returned by [`xcschemes.launch_target`](#xcschemes.launch_target), or a value returned by [`xcschemes.launch_path`](#xcschemes.launch_path). If a label string, `xcschemes.launch_target(label_str)` will be used. If `None`, `xcschemes.launch_target()` will be used, which means no launch target will be set (i.e. the `Executable` dropdown will be set to `None`). | `None` |
+| run_build_post_actions_on_failure | Whether build post-actions should run even when the scheme's `BuildAction` fails. | `False` |
| storekit_configuration | A StoreKit configuration file for use with [StoreKit Testing](https://developer.apple.com/documentation/xcode/setting-up-storekit-testing-in-xcode).
Can be `None`, or a label string referring to a single configuration file. | `None` |
| xcode_configuration | The name of the Xcode configuration to use to build the targets referenced in the Run action (i.e in the [`build_targets`](#xcschemes.run-build_targets) and [`launch_target`](#xcschemes.run-launch_target) attributes).
If not set, the value of [`xcodeproj.default_xcode_configuration`](#xcodeproj-default_xcode_configuration) is used. | `None` |
diff --git a/test/internal/xcschemes/info_constructors_tests.bzl b/test/internal/xcschemes/info_constructors_tests.bzl
index 29301adc9..ea6d6db7d 100644
--- a/test/internal/xcschemes/info_constructors_tests.bzl
+++ b/test/internal/xcschemes/info_constructors_tests.bzl
@@ -592,6 +592,7 @@ def info_constructors_test_suite(name):
env = None,
env_include_defaults = "1",
launch_target = xcscheme_infos_testable.make_launch_target(),
+ run_build_post_actions_on_failure = "0",
storekit_configuration = "",
xcode_configuration = "",
),
@@ -625,6 +626,7 @@ def info_constructors_test_suite(name):
},
env_include_defaults = "0",
launch_target = xcscheme_infos_testable.make_launch_target("L"),
+ run_build_post_actions_on_failure = "1",
storekit_configuration = "",
xcode_configuration = "Run",
),
@@ -656,6 +658,7 @@ def info_constructors_test_suite(name):
launch_target = xcscheme_infos_testable.make_launch_target(
id = "L",
),
+ run_build_post_actions_on_failure = "1",
storekit_configuration = "",
xcode_configuration = "Run",
),
@@ -689,6 +692,7 @@ def info_constructors_test_suite(name):
),
run = xcscheme_infos_testable.make_run(
xcode_configuration = "R",
+ run_build_post_actions_on_failure = "1",
),
test = xcscheme_infos_testable.make_test(
xcode_configuration = "R",
@@ -703,6 +707,7 @@ def info_constructors_test_suite(name):
),
run = xcscheme_infos_testable.make_run(
xcode_configuration = "R",
+ run_build_post_actions_on_failure = "1",
),
test = xcscheme_infos_testable.make_test(
xcode_configuration = "R",
diff --git a/test/internal/xcschemes/infos_from_json_tests.bzl b/test/internal/xcschemes/infos_from_json_tests.bzl
index ba85de718..2db1cae88 100644
--- a/test/internal/xcschemes/infos_from_json_tests.bzl
+++ b/test/internal/xcschemes/infos_from_json_tests.bzl
@@ -613,6 +613,7 @@ def infos_from_json_test_suite(name):
env = {"A": "B"},
env_include_defaults = "1",
launch_target = full_launch_target,
+ run_build_post_actions_on_failure = "0",
storekit_configuration = "",
xcode_configuration = "custom",
),
@@ -704,6 +705,7 @@ def infos_from_json_test_suite(name):
target_environment = "",
working_directory = "",
),
+ run_build_post_actions_on_failure = "0",
storekit_configuration = "",
xcode_configuration = "",
),
@@ -807,6 +809,7 @@ def infos_from_json_test_suite(name):
],
working_directory = "wd",
),
+ run_build_post_actions_on_failure = "0",
storekit_configuration = "",
xcode_configuration = "custom",
),
@@ -867,6 +870,7 @@ def infos_from_json_test_suite(name):
env = full_env,
env_include_defaults = "0",
launch_target = full_launch_target,
+ run_build_post_actions_on_failure = "0",
use_run_args_and_env = "0",
storekit_configuration = "//test/internal/xcschemes:fixture.storekit",
xcode_configuration = "custom",
diff --git a/test/internal/xcschemes/utils.bzl b/test/internal/xcschemes/utils.bzl
index 759885740..06d2caf35 100644
--- a/test/internal/xcschemes/utils.bzl
+++ b/test/internal/xcschemes/utils.bzl
@@ -65,6 +65,7 @@ def _dict_to_run_info(d):
env = _dict_of_dicts_to_env_infos(d["env"]),
env_include_defaults = d["env_include_defaults"],
launch_target = _dict_to_launch_target_info(d["launch_target"]),
+ run_build_post_actions_on_failure = d["run_build_post_actions_on_failure"],
storekit_configuration = d["storekit_configuration"],
xcode_configuration = d["xcode_configuration"],
)
diff --git a/test/internal/xcschemes/write_schemes_tests.bzl b/test/internal/xcschemes/write_schemes_tests.bzl
index 452b95a32..352baec39 100644
--- a/test/internal/xcschemes/write_schemes_tests.bzl
+++ b/test/internal/xcschemes/write_schemes_tests.bzl
@@ -20,6 +20,9 @@ load(
# Utility
+_AUTOGENERATION_CONFIG_DECLARED_FILE = mock_actions.mock_file(
+ "a_generator_name-autogeneration-config-file",
+)
_CUSTOM_SCHEMES_DECLARED_FILE = mock_actions.mock_file(
"a_generator_name_pbxproj_partials/custom_schemes_file",
)
@@ -73,13 +76,14 @@ def _write_schemes_test_impl(ctx):
_OUTPUT_DECLARED_DIRECTORY: None,
}
expected_declared_files = {
+ _AUTOGENERATION_CONFIG_DECLARED_FILE: None,
_CUSTOM_SCHEMES_DECLARED_FILE: None,
_EXECUTION_ACTIONS_DECLARED_FILE: None,
_TARGETS_ARGS_ENV_DECLARED_FILE: None,
_XCSCHEMEMANAGEMENT_DECLARED_FILE: None,
}
expected_inputs = ctx.attr.consolidation_maps + [
- ctx.attr.autogeneration_config_file,
+ _AUTOGENERATION_CONFIG_DECLARED_FILE,
_CUSTOM_SCHEMES_DECLARED_FILE,
_EXECUTION_ACTIONS_DECLARED_FILE,
ctx.attr.extension_point_identifiers_file,
@@ -98,7 +102,7 @@ def _write_schemes_test_impl(ctx):
) = xcschemes_execution.write_schemes(
actions = actions.mock,
autogeneration_mode = ctx.attr.autogeneration_mode,
- autogeneration_config_file = ctx.attr.autogeneration_config_file,
+ autogeneration_config = json.decode(ctx.attr.autogeneration_config),
colorize = ctx.attr.colorize,
consolidation_maps = ctx.attr.consolidation_maps,
default_xcode_configuration = ctx.attr.default_xcode_configuration,
@@ -203,7 +207,7 @@ write_schemes_test = unittest.make(
attrs = {
# Inputs
"autogeneration_mode": attr.string(mandatory = True),
- "autogeneration_config_file": attr.string(mandatory = True),
+ "autogeneration_config": attr.string(mandatory = True),
"colorize": attr.bool(mandatory = True),
"consolidation_maps": attr.string_list(mandatory = True),
"default_xcode_configuration": attr.string(mandatory = True),
@@ -236,7 +240,19 @@ def write_schemes_test_suite(name):
# Inputs
autogeneration_mode,
- autogeneration_config_file,
+ autogeneration_config = {
+ "build_post_actions": [],
+ "build_pre_actions": [],
+ "build_run_post_actions_on_failure": ["0"],
+ "profile_post_actions": [],
+ "profile_pre_actions": [],
+ "run_post_actions": [],
+ "run_pre_actions": [],
+ "scheme_name_exclude_patterns": [],
+ "test_options": ["", "", "0"],
+ "test_post_actions": [],
+ "test_pre_actions": [],
+ },
colorize = False,
consolidation_maps,
default_xcode_configuration,
@@ -257,7 +273,7 @@ def write_schemes_test_suite(name):
# Inputs
autogeneration_mode = autogeneration_mode,
- autogeneration_config_file = autogeneration_config_file,
+ autogeneration_config = json.encode(autogeneration_config),
colorize = colorize,
consolidation_maps = consolidation_maps,
default_xcode_configuration = default_xcode_configuration,
@@ -282,6 +298,35 @@ def write_schemes_test_suite(name):
"0",
]) + "\n"
+ no_autogeneration_config_content = "\n".join([
+ # appLanguage
+ "",
+ # appRegion
+ "",
+ # codeCoverage
+ "0",
+ # buildPreActionsCount
+ "0",
+ # buildPostActionsCount
+ "0",
+ # buildRunPostActionsOnFailure
+ "0",
+ # profilePreActionsCount
+ "0",
+ # profilePostActionsCount
+ "0",
+ # runPreActionsCount
+ "0",
+ # runPostActionsCount
+ "0",
+ # testPreActionsCount
+ "0",
+ # testPostActionsCount
+ "0",
+ # schemeNameExcludePatterns
+ "",
+ ]) + "\n"
+
no_target_args_and_env_content = "\n".join([
# argsCount
"0",
@@ -296,7 +341,29 @@ def write_schemes_test_suite(name):
# Inputs
autogeneration_mode = "none",
- autogeneration_config_file = "some/autogeneration-config-file",
+ autogeneration_config = {
+ "build_post_actions": [],
+ "build_pre_actions": [],
+ "build_run_post_actions_on_failure": ["1"],
+ "profile_post_actions": [],
+ "profile_pre_actions": [],
+ "run_post_actions": [],
+ "run_pre_actions": [
+ "Run Start (DevX & \"Logs\")",
+ "echo \"\"\n",
+ "-100",
+ ],
+ "scheme_name_exclude_patterns": [
+ "^App$",
+ ],
+ "test_options": [
+ "en",
+ "US",
+ "1",
+ ],
+ "test_post_actions": [],
+ "test_pre_actions": [],
+ },
colorize = True,
consolidation_maps = [
"some/consolidation_maps/0",
@@ -316,7 +383,7 @@ def write_schemes_test_suite(name):
# autogenerationMode
"none",
# autogenerationConfigFile
- "some/autogeneration-config-file",
+ _AUTOGENERATION_CONFIG_DECLARED_FILE.path,
# defaultXcodeConfiguration
"Debug",
# workspace
@@ -339,6 +406,41 @@ def write_schemes_test_suite(name):
"--colorize",
],
expected_writes = {
+ _AUTOGENERATION_CONFIG_DECLARED_FILE: "\n".join([
+ # appLanguage
+ "en",
+ # appRegion
+ "US",
+ # codeCoverage
+ "1",
+ # buildPreActionsCount
+ "0",
+ # buildPostActionsCount
+ "0",
+ # buildRunPostActionsOnFailure
+ "1",
+ # profilePreActionsCount
+ "0",
+ # profilePostActionsCount
+ "0",
+ # runPreActionsCount
+ "1",
+ # runPreActions - title
+ "Run Start (DevX & \"Logs\")",
+ # runPreActions - scriptText
+ "echo \"\"\0",
+ # runPreActions - order
+ "-100",
+ # runPostActionsCount
+ "0",
+ # testPreActionsCount
+ "0",
+ # testPostActionsCount
+ "0",
+ # schemeNameExcludePatterns
+ "^App$",
+ "",
+ ]) + "\n",
_CUSTOM_SCHEMES_DECLARED_FILE: no_custom_schemes_content,
_EXECUTION_ACTIONS_DECLARED_FILE: "\n",
_TARGETS_ARGS_ENV_DECLARED_FILE: no_target_args_and_env_content,
@@ -352,7 +454,6 @@ def write_schemes_test_suite(name):
# Inputs
autogeneration_mode = "auto",
- autogeneration_config_file = "some/autogeneration-config-file",
consolidation_maps = [
"some/consolidation_maps/0",
"some/consolidation_maps/1",
@@ -552,6 +653,7 @@ def write_schemes_test_suite(name):
],
working_directory = "run working dir",
),
+ run_build_post_actions_on_failure = "1",
storekit_configuration = "StoreKitConfig",
xcode_configuration = "Run",
),
@@ -678,7 +780,7 @@ def write_schemes_test_suite(name):
# autogenerationMode
"auto",
# autogenerationConfigFile
- "some/autogeneration-config-file",
+ _AUTOGENERATION_CONFIG_DECLARED_FILE.path,
# defaultXcodeConfiguration
"AppStore",
# workspace
@@ -699,6 +801,9 @@ def write_schemes_test_suite(name):
"some/consolidation_maps/1",
],
expected_writes = {
+ _AUTOGENERATION_CONFIG_DECLARED_FILE: (
+ no_autogeneration_config_content
+ ),
_EXECUTION_ACTIONS_DECLARED_FILE: "\n".join([
# schemeName
"Scheme 1",
@@ -1126,6 +1231,8 @@ def write_schemes_test_suite(name):
"",
# - run - customWorkingDirectory
"",
+ # - run - runBuildPostActionsOnFailure
+ "0",
# - profile - buildTargets
"",
# - profile - commandLineArguments count
@@ -1256,6 +1363,8 @@ def write_schemes_test_suite(name):
"run extension host id",
# - run - customWorkingDirectory
"run working dir",
+ # - run - runBuildPostActionsOnFailure
+ "1",
# - profile - buildTargets
"profile bt",
"",
@@ -1362,6 +1471,8 @@ def write_schemes_test_suite(name):
"/Foo/Bar.app",
# - run - customWorkingDirectory
"",
+ # - run - runBuildPostActionsOnFailure
+ "0",
# - profile - buildTargets
"",
# - profile - commandLineArguments count
@@ -1393,7 +1504,6 @@ def write_schemes_test_suite(name):
# Inputs
autogeneration_mode = "auto",
- autogeneration_config_file = "some/autogeneration-config-file",
consolidation_maps = [
"some/consolidation_maps/0",
"some/consolidation_maps/1",
@@ -1426,7 +1536,7 @@ def write_schemes_test_suite(name):
# autogenerationMode
"auto",
# autogenerationConfigFile
- "some/autogeneration-config-file",
+ _AUTOGENERATION_CONFIG_DECLARED_FILE.path,
# defaultXcodeConfiguration
"AppStore",
# workspace
@@ -1455,6 +1565,9 @@ def write_schemes_test_suite(name):
"IOS_APP_2",
],
expected_writes = {
+ _AUTOGENERATION_CONFIG_DECLARED_FILE: (
+ no_autogeneration_config_content
+ ),
_CUSTOM_SCHEMES_DECLARED_FILE: no_custom_schemes_content,
_EXECUTION_ACTIONS_DECLARED_FILE: "\n",
_TARGETS_ARGS_ENV_DECLARED_FILE: no_target_args_and_env_content,
@@ -1468,7 +1581,6 @@ def write_schemes_test_suite(name):
# Inputs
autogeneration_mode = "auto",
- autogeneration_config_file = "some/autogeneration-config-file",
consolidation_maps = [
"some/consolidation_maps/0",
"some/consolidation_maps/1",
@@ -1503,7 +1615,7 @@ def write_schemes_test_suite(name):
# autogenerationMode
"auto",
# autogenerationConfigFile
- "some/autogeneration-config-file",
+ _AUTOGENERATION_CONFIG_DECLARED_FILE.path,
# defaultXcodeConfiguration
"AppStore",
# workspace
@@ -1524,6 +1636,9 @@ def write_schemes_test_suite(name):
"some/consolidation_maps/1",
],
expected_writes = {
+ _AUTOGENERATION_CONFIG_DECLARED_FILE: (
+ no_autogeneration_config_content
+ ),
_CUSTOM_SCHEMES_DECLARED_FILE: no_custom_schemes_content,
_EXECUTION_ACTIONS_DECLARED_FILE: "\n",
_TARGETS_ARGS_ENV_DECLARED_FILE: "\n".join([
diff --git a/tools/generators/lib/XCScheme/src/CreateBuildAction.swift b/tools/generators/lib/XCScheme/src/CreateBuildAction.swift
index 00d2d8290..43db2ab98 100644
--- a/tools/generators/lib/XCScheme/src/CreateBuildAction.swift
+++ b/tools/generators/lib/XCScheme/src/CreateBuildAction.swift
@@ -12,12 +12,14 @@ public struct CreateBuildAction {
public func callAsFunction(
entries: [BuildActionEntry],
postActions: [ExecutionAction],
- preActions: [ExecutionAction]
+ preActions: [ExecutionAction],
+ runPostActionsOnFailure: Bool = false
) -> String {
return callable(
/*entries:*/ entries,
/*postActions:*/ postActions,
- /*preActions:*/ preActions
+ /*preActions:*/ preActions,
+ /*runPostActionsOnFailure:*/ runPostActionsOnFailure
)
}
}
@@ -55,19 +57,27 @@ extension CreateBuildAction {
public typealias Callable = (
_ entries: [BuildActionEntry],
_ postActions: [ExecutionAction],
- _ preActions: [ExecutionAction]
+ _ preActions: [ExecutionAction],
+ _ runPostActionsOnFailure: Bool
) -> String
public static func defaultCallable(
entries: [BuildActionEntry],
postActions: [ExecutionAction],
- preActions: [ExecutionAction]
+ preActions: [ExecutionAction],
+ runPostActionsOnFailure: Bool
) -> String {
+ let runPostActionsOnFailureAttribute = runPostActionsOnFailure ?
+ #"""
+
+ runPostActionsOnFailure = "YES"
+"""# : ""
+
// 3 spaces for indentation is intentional
return #"""
+ buildImplicitDependencies = "NO"\#(runPostActionsOnFailureAttribute)>
\#(preActions.preActionsString)\#
\#(postActions.postActionsString)\#
diff --git a/tools/generators/lib/XCScheme/test/CreateBuildActionTests.swift b/tools/generators/lib/XCScheme/test/CreateBuildActionTests.swift
index 2b1cd25db..b84b03cb6 100644
--- a/tools/generators/lib/XCScheme/test/CreateBuildActionTests.swift
+++ b/tools/generators/lib/XCScheme/test/CreateBuildActionTests.swift
@@ -74,16 +74,46 @@ final class CreateBuildActionTests: XCTestCase {
XCTAssertNoDifference(action, expectedAction)
}
+
+ func test_runPostActionsOnFailure() {
+ // Arrange
+
+ let entries: [BuildActionEntry] = []
+
+ let expectedAction = #"""
+
+
+
+
+
+"""#
+
+ // Act
+
+ let action = createBuildActionWithDefaults(
+ entries: entries,
+ runPostActionsOnFailure: true
+ )
+
+ // Assert
+
+ XCTAssertNoDifference(action, expectedAction)
+ }
}
private func createBuildActionWithDefaults(
entries: [BuildActionEntry],
postActions: [ExecutionAction] = [],
- preActions: [ExecutionAction] = []
+ preActions: [ExecutionAction] = [],
+ runPostActionsOnFailure: Bool = false
) -> String {
return CreateBuildAction.defaultCallable(
entries: entries,
postActions: postActions,
- preActions: preActions
+ preActions: preActions,
+ runPostActionsOnFailure: runPostActionsOnFailure
)
}
diff --git a/tools/generators/xcschemes/src/Generator/AutogenerationConfig.swift b/tools/generators/xcschemes/src/Generator/AutogenerationConfig.swift
new file mode 100644
index 000000000..87de56066
--- /dev/null
+++ b/tools/generators/xcschemes/src/Generator/AutogenerationConfig.swift
@@ -0,0 +1,153 @@
+import Foundation
+import ToolCommon
+
+struct AutogenerationConfig {
+ struct Action: Equatable {
+ let title: String
+ let scriptText: String
+ let order: Int?
+ }
+
+ let appLanguage: String?
+ let appRegion: String?
+ let buildPreActions: [Action]
+ let buildPostActions: [Action]
+ let buildRunPostActionsOnFailure: Bool
+ let codeCoverage: Bool
+ let profilePreActions: [Action]
+ let profilePostActions: [Action]
+ let runPreActions: [Action]
+ let runPostActions: [Action]
+ let testPreActions: [Action]
+ let testPostActions: [Action]
+ let schemeNameExcludePatterns: [String]
+
+ static func parse(
+ from url: URL
+ ) async throws -> Self {
+ var rawArgs = ArraySlice(try await url.allLines.collect())
+
+ let appLanguage = try rawArgs.consumeArg(
+ "app-language",
+ as: String?.self,
+ in: url
+ )
+ let appRegion = try rawArgs.consumeArg(
+ "app-region",
+ as: String?.self,
+ in: url
+ )
+ let codeCoverage = try rawArgs.consumeArg(
+ "code-coverage",
+ as: Bool.self,
+ in: url
+ )
+ let buildPreActions = try rawArgs.consumeBuildActions(
+ "build-pre-actions",
+ in: url
+ )
+ let buildPostActions = try rawArgs.consumeBuildActions(
+ "build-post-actions",
+ in: url
+ )
+ let buildRunPostActionsOnFailure = try rawArgs.consumeArg(
+ "build-run-post-actions-on-failure",
+ as: Bool.self,
+ in: url
+ )
+ let profilePreActions = try rawArgs.consumeBuildActions(
+ "profile-pre-actions",
+ in: url
+ )
+ let profilePostActions = try rawArgs.consumeBuildActions(
+ "profile-post-actions",
+ in: url
+ )
+ let runPreActions = try rawArgs.consumeBuildActions(
+ "run-pre-actions",
+ in: url
+ )
+ let runPostActions = try rawArgs.consumeBuildActions(
+ "run-post-actions",
+ in: url
+ )
+ let testPreActions = try rawArgs.consumeBuildActions(
+ "test-pre-actions",
+ in: url
+ )
+ let testPostActions = try rawArgs.consumeBuildActions(
+ "test-post-actions",
+ in: url
+ )
+ let schemeNameExcludePatterns = try rawArgs.consumeArgs(
+ "scheme-name-exclude-patterns",
+ in: url
+ )
+
+ return AutogenerationConfig(
+ appLanguage: appLanguage,
+ appRegion: appRegion,
+ buildPreActions: buildPreActions,
+ buildPostActions: buildPostActions,
+ buildRunPostActionsOnFailure: buildRunPostActionsOnFailure,
+ codeCoverage: codeCoverage,
+ profilePreActions: profilePreActions,
+ profilePostActions: profilePostActions,
+ runPreActions: runPreActions,
+ runPostActions: runPostActions,
+ testPreActions: testPreActions,
+ testPostActions: testPostActions,
+ schemeNameExcludePatterns: schemeNameExcludePatterns
+ )
+ }
+}
+
+private extension ArraySlice where Element == String {
+ mutating func consumeBuildActions(
+ _ namePrefix: String,
+ in url: URL,
+ file: StaticString = #filePath,
+ line: UInt = #line
+ ) throws -> [AutogenerationConfig.Action] {
+ let count = try consumeArg(
+ "\(namePrefix)-count",
+ as: Int.self,
+ in: url,
+ file: file,
+ line: line
+ )
+
+ var buildActions: [AutogenerationConfig.Action] = []
+ for _ in (0.. Self {
- var rawArgs = ArraySlice(try await url.allLines.collect())
-
- let appLanguage = try rawArgs.consumeArg(
- "app-language",
- as: String?.self,
- in: url
- )
- let appRegion = try rawArgs.consumeArg(
- "app-region",
- as: String?.self,
- in: url
- )
- let codeCoverage = try rawArgs.consumeArg(
- "code-coverage",
- as: Bool.self,
- in: url
- )
- let schemeNameExcludePatterns = try rawArgs.consumeArgs(
- "scheme-name-exclude-patterns",
- in: url
- )
-
- return AutogenerationConfigArguments(
- appLanguage: appLanguage,
- appRegion: appRegion,
- codeCoverage: codeCoverage,
- schemeNameExcludePatterns: schemeNameExcludePatterns
- )
- }
-}
diff --git a/tools/generators/xcschemes/src/Generator/CreateAutomaticSchemeInfo.swift b/tools/generators/xcschemes/src/Generator/CreateAutomaticSchemeInfo.swift
index 1b3320473..8f8dc6596 100644
--- a/tools/generators/xcschemes/src/Generator/CreateAutomaticSchemeInfo.swift
+++ b/tools/generators/xcschemes/src/Generator/CreateAutomaticSchemeInfo.swift
@@ -16,19 +16,38 @@ extension Generator {
/// Creates a `SchemeInfo` for an automatically generated scheme.
func callAsFunction(
+ buildPostActions: [AutogenerationConfig.Action],
+ buildPreActions: [AutogenerationConfig.Action],
+ buildRunPostActionsOnFailure: Bool,
+ profilePostActions: [AutogenerationConfig.Action],
+ profilePreActions: [AutogenerationConfig.Action],
commandLineArguments: [CommandLineArgument],
customSchemeNames: Set,
environmentVariables: [EnvironmentVariable],
extensionHost: Target?,
+ runPostActions: [AutogenerationConfig.Action],
+ runPreActions: [AutogenerationConfig.Action],
target: Target,
+ testPostActions: [AutogenerationConfig.Action],
+ testPreActions: [AutogenerationConfig.Action],
testOptions: SchemeInfo.Test.Options?
) throws -> SchemeInfo? {
return try callable(
+ /*buildPostActions:*/ buildPostActions,
+ /*buildPreActions:*/ buildPreActions,
+ /*buildRunPostActionsOnFailure:*/
+ buildRunPostActionsOnFailure,
+ /*profilePostActions:*/ profilePostActions,
+ /*profilePreActions:*/ profilePreActions,
/*commandLineArguments:*/ commandLineArguments,
/*customSchemeNames:*/ customSchemeNames,
/*environmentVariables:*/ environmentVariables,
/*extensionHost:*/ extensionHost,
+ /*runPostActions:*/ runPostActions,
+ /*runPreActions:*/ runPreActions,
/*target:*/ target,
+ /*testPostActions:*/ testPostActions,
+ /*testPreActions:*/ testPreActions,
/*testOptions:*/ testOptions
)
}
@@ -39,20 +58,38 @@ extension Generator {
extension Generator.CreateAutomaticSchemeInfo {
typealias Callable = (
+ _ buildPostActions: [AutogenerationConfig.Action],
+ _ buildPreActions: [AutogenerationConfig.Action],
+ _ buildRunPostActionsOnFailure: Bool,
+ _ profilePostActions: [AutogenerationConfig.Action],
+ _ profilePreActions: [AutogenerationConfig.Action],
_ commandLineArguments: [CommandLineArgument],
_ customSchemeNames: Set,
_ environmentVariables: [EnvironmentVariable],
_ extensionHost: Target?,
+ _ runPostActions: [AutogenerationConfig.Action],
+ _ runPreActions: [AutogenerationConfig.Action],
_ target: Target,
+ _ testPostActions: [AutogenerationConfig.Action],
+ _ testPreActions: [AutogenerationConfig.Action],
_ testOptions: SchemeInfo.Test.Options?
) throws -> SchemeInfo?
static func defaultCallable(
+ buildPostActions: [AutogenerationConfig.Action],
+ buildPreActions: [AutogenerationConfig.Action],
+ buildRunPostActionsOnFailure: Bool,
+ profilePostActions: [AutogenerationConfig.Action],
+ profilePreActions: [AutogenerationConfig.Action],
commandLineArguments: [CommandLineArgument],
customSchemeNames: Set,
environmentVariables: [EnvironmentVariable],
extensionHost: Target?,
+ runPostActions: [AutogenerationConfig.Action],
+ runPreActions: [AutogenerationConfig.Action],
target: Target,
+ testPostActions: [AutogenerationConfig.Action],
+ testPreActions: [AutogenerationConfig.Action],
testOptions: SchemeInfo.Test.Options?
) throws -> SchemeInfo? {
let baseSchemeName = target.buildableReference.blueprintName.schemeName
@@ -111,6 +148,83 @@ extension Generator.CreateAutomaticSchemeInfo {
.defaultEnvironmentVariables + environmentVariables
}
+ let buildExecutionActions = buildPreActions.map {
+ SchemeInfo.ExecutionAction(
+ title: $0.title,
+ scriptText: $0.scriptText,
+ action: .build,
+ isPreAction: true,
+ target: target,
+ order: $0.order
+ )
+ } + buildPostActions.map {
+ SchemeInfo.ExecutionAction(
+ title: $0.title,
+ scriptText: $0.scriptText,
+ action: .build,
+ isPreAction: false,
+ target: target,
+ order: $0.order
+ )
+ }
+ let runExecutionActions = runPreActions.map {
+ SchemeInfo.ExecutionAction(
+ title: $0.title,
+ scriptText: $0.scriptText,
+ action: .run,
+ isPreAction: true,
+ target: target,
+ order: $0.order
+ )
+ } + runPostActions.map {
+ SchemeInfo.ExecutionAction(
+ title: $0.title,
+ scriptText: $0.scriptText,
+ action: .run,
+ isPreAction: false,
+ target: target,
+ order: $0.order
+ )
+ }
+ let testExecutionActions = testPreActions.map {
+ SchemeInfo.ExecutionAction(
+ title: $0.title,
+ scriptText: $0.scriptText,
+ action: .test,
+ isPreAction: true,
+ target: target,
+ order: $0.order
+ )
+ } + testPostActions.map {
+ SchemeInfo.ExecutionAction(
+ title: $0.title,
+ scriptText: $0.scriptText,
+ action: .test,
+ isPreAction: false,
+ target: target,
+ order: $0.order
+ )
+ }
+ let profileExecutionActions = profilePreActions.map {
+ SchemeInfo.ExecutionAction(
+ title: $0.title,
+ scriptText: $0.scriptText,
+ action: .profile,
+ isPreAction: true,
+ target: target,
+ order: $0.order
+ )
+ } + profilePostActions.map {
+ SchemeInfo.ExecutionAction(
+ title: $0.title,
+ scriptText: $0.scriptText,
+ action: .profile,
+ isPreAction: false,
+ target: target,
+ order: $0.order
+ )
+ }
+
return SchemeInfo(
name: name,
test: .init(
@@ -139,6 +253,8 @@ extension Generator.CreateAutomaticSchemeInfo {
enableThreadPerformanceChecker: false,
environmentVariables: runEnvironmentVariables,
launchTarget: launchTarget,
+ runBuildPostActionsOnFailure:
+ buildRunPostActionsOnFailure,
storeKitConfiguration: nil,
xcodeConfiguration: nil
),
@@ -151,7 +267,11 @@ extension Generator.CreateAutomaticSchemeInfo {
useRunArgsAndEnv: true,
xcodeConfiguration: nil
),
- executionActions: []
+ executionActions:
+ buildExecutionActions +
+ runExecutionActions +
+ testExecutionActions +
+ profileExecutionActions
)
}
}
diff --git a/tools/generators/xcschemes/src/Generator/CreateAutomaticSchemeInfos.swift b/tools/generators/xcschemes/src/Generator/CreateAutomaticSchemeInfos.swift
index 3596259a3..62452f356 100644
--- a/tools/generators/xcschemes/src/Generator/CreateAutomaticSchemeInfos.swift
+++ b/tools/generators/xcschemes/src/Generator/CreateAutomaticSchemeInfos.swift
@@ -26,24 +26,43 @@ extension Generator {
/// Creates `SchemeInfo`s for automatically generated schemes.
func callAsFunction(
autogenerationMode: AutogenerationMode,
+ buildPostActions: [AutogenerationConfig.Action],
+ buildPreActions: [AutogenerationConfig.Action],
+ buildRunPostActionsOnFailure: Bool,
+ profilePostActions: [AutogenerationConfig.Action],
+ profilePreActions: [AutogenerationConfig.Action],
commandLineArguments: [TargetID: [CommandLineArgument]],
customSchemeNames: Set,
environmentVariables: [TargetID: [EnvironmentVariable]],
extensionHostIDs: [TargetID: [TargetID]],
+ runPostActions: [AutogenerationConfig.Action],
+ runPreActions: [AutogenerationConfig.Action],
targets: [Target],
targetsByID: [TargetID: Target],
targetsByKey: [Target.Key: Target],
+ testPostActions: [AutogenerationConfig.Action],
+ testPreActions: [AutogenerationConfig.Action],
testOptions: SchemeInfo.Test.Options?
) throws -> [SchemeInfo] {
return try callable(
/*autogenerationMode:*/ autogenerationMode,
+ /*buildPostActions:*/ buildPostActions,
+ /*buildPreActions:*/ buildPreActions,
+ /*buildRunPostActionsOnFailure:*/
+ buildRunPostActionsOnFailure,
+ /*profilePostActions:*/ profilePostActions,
+ /*profilePreActions:*/ profilePreActions,
/*commandLineArguments:*/ commandLineArguments,
/*customSchemeNames:*/ customSchemeNames,
/*environmentVariables:*/ environmentVariables,
/*extensionHostIDs:*/ extensionHostIDs,
+ /*runPostActions:*/ runPostActions,
+ /*runPreActions:*/ runPreActions,
/*targets:*/ targets,
/*targetsByID:*/ targetsByID,
/*targetsByKey:*/ targetsByKey,
+ /*testPostActions:*/ testPostActions,
+ /*testPreActions:*/ testPreActions,
/*createTargetAutomaticSchemeInfos:*/
createTargetAutomaticSchemeInfos,
/*testOptions:*/ testOptions
@@ -57,13 +76,22 @@ extension Generator {
extension Generator.CreateAutomaticSchemeInfos {
typealias Callable = (
_ autogenerationMode: AutogenerationMode,
+ _ buildPostActions: [AutogenerationConfig.Action],
+ _ buildPreActions: [AutogenerationConfig.Action],
+ _ buildRunPostActionsOnFailure: Bool,
+ _ profilePostActions: [AutogenerationConfig.Action],
+ _ profilePreActions: [AutogenerationConfig.Action],
_ commandLineArguments: [TargetID: [CommandLineArgument]],
_ customSchemeNames: Set,
_ environmentVariables: [TargetID: [EnvironmentVariable]],
_ extensionHostIDs: [TargetID: [TargetID]],
+ _ runPostActions: [AutogenerationConfig.Action],
+ _ runPreActions: [AutogenerationConfig.Action],
_ targets: [Target],
_ targetsByID: [TargetID: Target],
_ targetsByKey: [Target.Key: Target],
+ _ testPostActions: [AutogenerationConfig.Action],
+ _ testPreActions: [AutogenerationConfig.Action],
_ createTargetAutomaticSchemeInfos:
Generator.CreateTargetAutomaticSchemeInfos,
_ testOptions: SchemeInfo.Test.Options?
@@ -71,13 +99,22 @@ extension Generator.CreateAutomaticSchemeInfos {
static func defaultCallable(
autogenerationMode: AutogenerationMode,
+ buildPostActions: [AutogenerationConfig.Action],
+ buildPreActions: [AutogenerationConfig.Action],
+ buildRunPostActionsOnFailure: Bool,
+ profilePostActions: [AutogenerationConfig.Action],
+ profilePreActions: [AutogenerationConfig.Action],
commandLineArguments: [TargetID: [CommandLineArgument]],
customSchemeNames: Set,
environmentVariables: [TargetID: [EnvironmentVariable]],
extensionHostIDs: [TargetID: [TargetID]],
+ runPostActions: [AutogenerationConfig.Action],
+ runPreActions: [AutogenerationConfig.Action],
targets: [Target],
targetsByID: [TargetID: Target],
targetsByKey: [Target.Key: Target],
+ testPostActions: [AutogenerationConfig.Action],
+ testPreActions: [AutogenerationConfig.Action],
createTargetAutomaticSchemeInfos:
Generator.CreateTargetAutomaticSchemeInfos,
testOptions: SchemeInfo.Test.Options?
@@ -116,13 +153,23 @@ extension Generator.CreateAutomaticSchemeInfos {
let id = target.key.sortedIds.first!
return try createTargetAutomaticSchemeInfos(
+ buildPostActions: buildPostActions,
+ buildPreActions: buildPreActions,
+ buildRunPostActionsOnFailure:
+ buildRunPostActionsOnFailure,
+ profilePostActions: profilePostActions,
+ profilePreActions: profilePreActions,
commandLineArguments: commandLineArguments[id, default: []],
customSchemeNames: customSchemeNames,
environmentVariables: environmentVariables[id, default: []],
extensionHostIDs: extensionHostIDs,
+ runPostActions: runPostActions,
+ runPreActions: runPreActions,
target: target,
targetsByID: targetsByID,
targetsByKey: targetsByKey,
+ testPostActions: testPostActions,
+ testPreActions: testPreActions,
testOptions: testOptions
)
}
diff --git a/tools/generators/xcschemes/src/Generator/CreateCustomSchemeInfos.swift b/tools/generators/xcschemes/src/Generator/CreateCustomSchemeInfos.swift
index a7d38ac9b..ee0738553 100644
--- a/tools/generators/xcschemes/src/Generator/CreateCustomSchemeInfos.swift
+++ b/tools/generators/xcschemes/src/Generator/CreateCustomSchemeInfos.swift
@@ -543,6 +543,11 @@ set
as: String?.self,
in: url
)
+ let runBuildPostActionsOnFailure = try consumeArg(
+ "run-build-post-actions-on-failure",
+ as: Bool.self,
+ in: url
+ )
if let launchTarget,
launchTarget.canExpandMacros && environmentVariablesIncludeDefaults
@@ -564,6 +569,8 @@ set
enableThreadPerformanceChecker: enableThreadPerformanceChecker,
environmentVariables: environmentVariables,
launchTarget: launchTarget,
+ runBuildPostActionsOnFailure:
+ runBuildPostActionsOnFailure,
storeKitConfiguration: storeKitConfiguration,
xcodeConfiguration: xcodeConfiguration
)
diff --git a/tools/generators/xcschemes/src/Generator/CreateScheme.swift b/tools/generators/xcschemes/src/Generator/CreateScheme.swift
index 860e1ab6d..7ce19009a 100644
--- a/tools/generators/xcschemes/src/Generator/CreateScheme.swift
+++ b/tools/generators/xcschemes/src/Generator/CreateScheme.swift
@@ -366,7 +366,9 @@ extension Generator.CreateScheme {
.map(\.action),
preActions: buildPreActions
.sorted(by: compareExecutionActions)
- .map(\.action)
+ .map(\.action),
+ runPostActionsOnFailure:
+ schemeInfo.run.runBuildPostActionsOnFailure
),
testAction: createTestAction(
appLanguage: schemeInfo.test.options?.appLanguage,
diff --git a/tools/generators/xcschemes/src/Generator/CreateTargetAutomaticSchemeInfo.swift b/tools/generators/xcschemes/src/Generator/CreateTargetAutomaticSchemeInfo.swift
index 3aa07587e..26c08ab33 100644
--- a/tools/generators/xcschemes/src/Generator/CreateTargetAutomaticSchemeInfo.swift
+++ b/tools/generators/xcschemes/src/Generator/CreateTargetAutomaticSchemeInfo.swift
@@ -23,23 +23,42 @@ extension Generator {
/// Creates `SchemeInfo`s for a target's automatically generated
/// schemes.
func callAsFunction(
+ buildPostActions: [AutogenerationConfig.Action],
+ buildPreActions: [AutogenerationConfig.Action],
+ buildRunPostActionsOnFailure: Bool,
+ profilePostActions: [AutogenerationConfig.Action],
+ profilePreActions: [AutogenerationConfig.Action],
commandLineArguments: [CommandLineArgument],
customSchemeNames: Set,
environmentVariables: [EnvironmentVariable],
extensionHostIDs: [TargetID: [TargetID]],
+ runPostActions: [AutogenerationConfig.Action],
+ runPreActions: [AutogenerationConfig.Action],
target: Target,
targetsByID: [TargetID: Target],
targetsByKey: [Target.Key: Target],
+ testPostActions: [AutogenerationConfig.Action],
+ testPreActions: [AutogenerationConfig.Action],
testOptions: SchemeInfo.Test.Options?
) throws -> [SchemeInfo] {
return try callable(
+ /*buildPostActions:*/ buildPostActions,
+ /*buildPreActions:*/ buildPreActions,
+ /*buildRunPostActionsOnFailure:*/
+ buildRunPostActionsOnFailure,
+ /*profilePostActions:*/ profilePostActions,
+ /*profilePreActions:*/ profilePreActions,
/*commandLineArguments:*/ commandLineArguments,
/*customSchemeNames:*/ customSchemeNames,
/*environmentVariables:*/ environmentVariables,
/*extensionHostIDs:*/ extensionHostIDs,
+ /*runPostActions:*/ runPostActions,
+ /*runPreActions:*/ runPreActions,
/*target:*/ target,
/*targetsByID:*/ targetsByID,
/*targetsByKey:*/ targetsByKey,
+ /*testPostActions:*/ testPostActions,
+ /*testPreActions:*/ testPreActions,
/*testOptions:*/ testOptions,
/*createAutomaticSchemeInfo:*/ createAutomaticSchemeInfo
)
@@ -51,25 +70,43 @@ extension Generator {
extension Generator.CreateTargetAutomaticSchemeInfos {
typealias Callable = (
+ _ buildPostActions: [AutogenerationConfig.Action],
+ _ buildPreActions: [AutogenerationConfig.Action],
+ _ buildRunPostActionsOnFailure: Bool,
+ _ profilePostActions: [AutogenerationConfig.Action],
+ _ profilePreActions: [AutogenerationConfig.Action],
_ commandLineArguments: [CommandLineArgument],
_ customSchemeNames: Set,
_ environmentVariables: [EnvironmentVariable],
_ extensionHostIDs: [TargetID: [TargetID]],
+ _ runPostActions: [AutogenerationConfig.Action],
+ _ runPreActions: [AutogenerationConfig.Action],
_ target: Target,
_ targetsByID: [TargetID: Target],
_ targetsByKey: [Target.Key: Target],
+ _ testPostActions: [AutogenerationConfig.Action],
+ _ testPreActions: [AutogenerationConfig.Action],
_ testOptions: SchemeInfo.Test.Options?,
_ createAutomaticSchemeInfo: Generator.CreateAutomaticSchemeInfo
) throws -> [SchemeInfo]
static func defaultCallable(
+ buildPostActions: [AutogenerationConfig.Action],
+ buildPreActions: [AutogenerationConfig.Action],
+ buildRunPostActionsOnFailure: Bool,
+ profilePostActions: [AutogenerationConfig.Action],
+ profilePreActions: [AutogenerationConfig.Action],
commandLineArguments: [CommandLineArgument],
customSchemeNames: Set,
environmentVariables: [EnvironmentVariable],
extensionHostIDs: [TargetID: [TargetID]],
+ runPostActions: [AutogenerationConfig.Action],
+ runPreActions: [AutogenerationConfig.Action],
target: Target,
targetsByID: [TargetID: Target],
targetsByKey: [Target.Key: Target],
+ testPostActions: [AutogenerationConfig.Action],
+ testPreActions: [AutogenerationConfig.Action],
testOptions: SchemeInfo.Test.Options?,
createAutomaticSchemeInfo: Generator.CreateAutomaticSchemeInfo
) throws -> [SchemeInfo] {
@@ -93,11 +130,21 @@ extension Generator.CreateTargetAutomaticSchemeInfos {
if extensionHostKeys.isEmpty {
guard let schemeInfo = try createAutomaticSchemeInfo(
+ buildPostActions: buildPostActions,
+ buildPreActions: buildPreActions,
+ buildRunPostActionsOnFailure:
+ buildRunPostActionsOnFailure,
+ profilePostActions: profilePostActions,
+ profilePreActions: profilePreActions,
commandLineArguments: commandLineArguments,
customSchemeNames: customSchemeNames,
environmentVariables: environmentVariables,
extensionHost: nil,
+ runPostActions: runPostActions,
+ runPreActions: runPreActions,
target: target,
+ testPostActions: testPostActions,
+ testPreActions: testPreActions,
testOptions: testOptions
) else {
return []
@@ -106,11 +153,21 @@ extension Generator.CreateTargetAutomaticSchemeInfos {
} else {
return try extensionHostKeys.compactMap { key in
return try createAutomaticSchemeInfo(
+ buildPostActions: buildPostActions,
+ buildPreActions: buildPreActions,
+ buildRunPostActionsOnFailure:
+ buildRunPostActionsOnFailure,
+ profilePostActions: profilePostActions,
+ profilePreActions: profilePreActions,
commandLineArguments: commandLineArguments,
customSchemeNames: customSchemeNames,
environmentVariables: environmentVariables,
extensionHost: targetsByKey[key]!,
+ runPostActions: runPostActions,
+ runPreActions: runPreActions,
target: target,
+ testPostActions: testPostActions,
+ testPreActions: testPreActions,
testOptions: testOptions
)
}
diff --git a/tools/generators/xcschemes/src/Generator/Generator.swift b/tools/generators/xcschemes/src/Generator/Generator.swift
index 95a7ba1ef..729f66186 100644
--- a/tools/generators/xcschemes/src/Generator/Generator.swift
+++ b/tools/generators/xcschemes/src/Generator/Generator.swift
@@ -36,7 +36,7 @@ struct Generator {
.readTargetArgsAndEnvFile(arguments.targetsArgsEnvFile)
let extensionHostIDs = arguments.calculateExtensionHostIDs()
- let autogenerationConfigArguments = try await AutogenerationConfigArguments.parse(
+ let autogenerationConfig = try await AutogenerationConfig.parse(
from: arguments.autogenerationConfigFile
)
@@ -61,23 +61,33 @@ struct Generator {
let automaticSchemeInfos = try environment.createAutomaticSchemeInfos(
autogenerationMode: arguments.autogenerationMode,
+ buildPostActions: autogenerationConfig.buildPostActions,
+ buildPreActions: autogenerationConfig.buildPreActions,
+ buildRunPostActionsOnFailure:
+ autogenerationConfig.buildRunPostActionsOnFailure,
+ profilePostActions: autogenerationConfig.profilePostActions,
+ profilePreActions: autogenerationConfig.profilePreActions,
commandLineArguments: commandLineArguments,
customSchemeNames: Set(customSchemeInfos.map(\.name)),
environmentVariables: environmentVariables,
extensionHostIDs: extensionHostIDs,
+ runPostActions: autogenerationConfig.runPostActions,
+ runPreActions: autogenerationConfig.runPreActions,
targets: targets,
targetsByID: targetsByID,
targetsByKey: targetsByKey,
+ testPostActions: autogenerationConfig.testPostActions,
+ testPreActions: autogenerationConfig.testPreActions,
testOptions: .init(
- appLanguage: autogenerationConfigArguments.appLanguage,
- appRegion: autogenerationConfigArguments.appRegion,
- codeCoverage: autogenerationConfigArguments.codeCoverage
+ appLanguage: autogenerationConfig.appLanguage,
+ appRegion: autogenerationConfig.appRegion,
+ codeCoverage: autogenerationConfig.codeCoverage
)
)
let filteredAutomaticSchemeInfos = try automaticSchemeInfos.filter { scheme in
// Apply scheme auto-generation exclude patterns
- for pattern in autogenerationConfigArguments.schemeNameExcludePatterns {
+ for pattern in autogenerationConfig.schemeNameExcludePatterns {
do {
let regex = try Regex(pattern)
let matches = scheme.name.matches(of: regex)
diff --git a/tools/generators/xcschemes/src/Generator/GeneratorArguments.swift b/tools/generators/xcschemes/src/Generator/GeneratorArguments.swift
index 53fcedb49..d605daa71 100644
--- a/tools/generators/xcschemes/src/Generator/GeneratorArguments.swift
+++ b/tools/generators/xcschemes/src/Generator/GeneratorArguments.swift
@@ -35,7 +35,7 @@ schemes are provided.
var autogenerationMode: AutogenerationMode
@Argument(
- help: "Path to a file containing `AutogenerationConfigArguments` inputs.",
+ help: "Path to a file containing `AutogenerationConfig` inputs.",
transform: { URL(fileURLWithPath: $0, isDirectory: false) }
)
var autogenerationConfigFile: URL
diff --git a/tools/generators/xcschemes/src/Generator/SchemeInfo.swift b/tools/generators/xcschemes/src/Generator/SchemeInfo.swift
index dce3f91d5..2312958b8 100644
--- a/tools/generators/xcschemes/src/Generator/SchemeInfo.swift
+++ b/tools/generators/xcschemes/src/Generator/SchemeInfo.swift
@@ -71,6 +71,7 @@ struct SchemeInfo: Equatable {
let enableThreadPerformanceChecker: Bool
let environmentVariables: [EnvironmentVariable]
let launchTarget: LaunchTarget?
+ let runBuildPostActionsOnFailure: Bool
let storeKitConfiguration: String?
let xcodeConfiguration: String?
}
diff --git a/tools/generators/xcschemes/test/CreateAutomaticSchemeInfo+Testing.swift b/tools/generators/xcschemes/test/CreateAutomaticSchemeInfo+Testing.swift
index c780447e7..9b3a3779e 100644
--- a/tools/generators/xcschemes/test/CreateAutomaticSchemeInfo+Testing.swift
+++ b/tools/generators/xcschemes/test/CreateAutomaticSchemeInfo+Testing.swift
@@ -8,11 +8,20 @@ import XCScheme
extension Generator.CreateAutomaticSchemeInfo {
final class MockTracker {
struct Called: Equatable {
+ let buildPostActions: [AutogenerationConfig.Action]
+ let buildPreActions: [AutogenerationConfig.Action]
+ let buildRunPostActionsOnFailure: Bool
+ let profilePostActions: [AutogenerationConfig.Action]
+ let profilePreActions: [AutogenerationConfig.Action]
let commandLineArguments: [CommandLineArgument]
let customSchemeNames: Set
let environmentVariables: [EnvironmentVariable]
let extensionHost: Target?
+ let runPostActions: [AutogenerationConfig.Action]
+ let runPreActions: [AutogenerationConfig.Action]
let target: Target
+ let testPostActions: [AutogenerationConfig.Action]
+ let testPreActions: [AutogenerationConfig.Action]
let testOptions: SchemeInfo.Test.Options?
}
@@ -39,18 +48,37 @@ extension Generator.CreateAutomaticSchemeInfo {
let mocked = Self(
callable: {
+ buildPostActions,
+ buildPreActions,
+ buildRunPostActionsOnFailure,
+ profilePostActions,
+ profilePreActions,
commandLineArguments,
customSchemeNames,
environmentVariables,
extensionHost,
+ runPostActions,
+ runPreActions,
target,
+ testPostActions,
+ testPreActions,
testOptions in
mockTracker.called.append(.init(
+ buildPostActions: buildPostActions,
+ buildPreActions: buildPreActions,
+ buildRunPostActionsOnFailure:
+ buildRunPostActionsOnFailure,
+ profilePostActions: profilePostActions,
+ profilePreActions: profilePreActions,
commandLineArguments: commandLineArguments,
customSchemeNames: customSchemeNames,
environmentVariables: environmentVariables,
extensionHost: extensionHost,
+ runPostActions: runPostActions,
+ runPreActions: runPreActions,
target: target,
+ testPostActions: testPostActions,
+ testPreActions: testPreActions,
testOptions: testOptions
))
return mockTracker.nextResult()
diff --git a/tools/generators/xcschemes/test/CreateAutomaticSchemeInfoTests.swift b/tools/generators/xcschemes/test/CreateAutomaticSchemeInfoTests.swift
index 02eb5b8c5..354990735 100644
--- a/tools/generators/xcschemes/test/CreateAutomaticSchemeInfoTests.swift
+++ b/tools/generators/xcschemes/test/CreateAutomaticSchemeInfoTests.swift
@@ -155,6 +155,7 @@ final class CreateAutomaticSchemeInfoTests: XCTestCase {
primary: launchable,
extensionHost: nil
),
+ runBuildPostActionsOnFailure: false,
storeKitConfiguration: nil,
xcodeConfiguration: nil
),
@@ -238,6 +239,7 @@ final class CreateAutomaticSchemeInfoTests: XCTestCase {
primary: launchable,
extensionHost: extensionHost
),
+ runBuildPostActionsOnFailure: false,
storeKitConfiguration: nil,
xcodeConfiguration: nil
),
@@ -317,6 +319,7 @@ final class CreateAutomaticSchemeInfoTests: XCTestCase {
primary: launchable,
extensionHost: nil
),
+ runBuildPostActionsOnFailure: false,
storeKitConfiguration: nil,
xcodeConfiguration: nil
),
@@ -396,6 +399,7 @@ final class CreateAutomaticSchemeInfoTests: XCTestCase {
primary: launchable,
extensionHost: nil
),
+ runBuildPostActionsOnFailure: false,
storeKitConfiguration: nil,
xcodeConfiguration: nil
),
@@ -469,6 +473,7 @@ final class CreateAutomaticSchemeInfoTests: XCTestCase {
enableThreadPerformanceChecker: false,
environmentVariables: baseEnvironmentVariables,
launchTarget: nil,
+ runBuildPostActionsOnFailure: false,
storeKitConfiguration: nil,
xcodeConfiguration: nil
),
@@ -538,6 +543,7 @@ final class CreateAutomaticSchemeInfoTests: XCTestCase {
enableThreadPerformanceChecker: false,
environmentVariables: [],
launchTarget: nil,
+ runBuildPostActionsOnFailure: false,
storeKitConfiguration: nil,
xcodeConfiguration: nil
),
@@ -610,6 +616,7 @@ final class CreateAutomaticSchemeInfoTests: XCTestCase {
enableThreadPerformanceChecker: false,
environmentVariables: [],
launchTarget: nil,
+ runBuildPostActionsOnFailure: false,
storeKitConfiguration: nil,
xcodeConfiguration: nil
),
@@ -683,6 +690,7 @@ final class CreateAutomaticSchemeInfoTests: XCTestCase {
enableThreadPerformanceChecker: false,
environmentVariables: [],
launchTarget: nil,
+ runBuildPostActionsOnFailure: false,
storeKitConfiguration: nil,
xcodeConfiguration: nil
),
@@ -755,6 +763,7 @@ final class CreateAutomaticSchemeInfoTests: XCTestCase {
enableThreadPerformanceChecker: false,
environmentVariables: [],
launchTarget: nil,
+ runBuildPostActionsOnFailure: false,
storeKitConfiguration: nil,
xcodeConfiguration: nil
),
@@ -785,6 +794,210 @@ final class CreateAutomaticSchemeInfoTests: XCTestCase {
XCTAssertNoDifference(schemeInfo, expectedSchemeInfo)
}
+
+ func test_buildActions() throws {
+ // Arrange
+
+ let launchable = Target(
+ key: "Launchable",
+ productType: .application,
+ buildableReference: .init(
+ blueprintIdentifier: "BLUEPRINT_IDENTIFIER_Launchable",
+ buildableName: "BUILDABLE_NAME_Launchable",
+ blueprintName: "BLUEPRINT_NAME_Launchable",
+ referencedContainer: "REFERENCED_CONTAINER_Launchable"
+ )
+ )
+ let buildPreActions: [AutogenerationConfig.Action] = [
+ .init(
+ title: "Build Start",
+ scriptText: "echo start\n",
+ order: -200
+ ),
+ ]
+ let buildPostActions: [AutogenerationConfig.Action] = [
+ .init(
+ title: "Build End",
+ scriptText: "echo end\n",
+ order: 100
+ ),
+ ]
+ let runPreActions: [AutogenerationConfig.Action] = [
+ .init(
+ title: #"Run Start (DevX & "Logs")"#,
+ scriptText: "echo \"\"\n",
+ order: -100
+ ),
+ ]
+ let runPostActions: [AutogenerationConfig.Action] = [
+ .init(
+ title: #"Run End (DevX & "Logs")"#,
+ scriptText: "echo \"\"\n",
+ order: 200
+ ),
+ ]
+ let testPreActions: [AutogenerationConfig.Action] = [
+ .init(
+ title: "Test Start",
+ scriptText: "echo test-start\n",
+ order: -50
+ ),
+ ]
+ let testPostActions: [AutogenerationConfig.Action] = [
+ .init(
+ title: "Test End",
+ scriptText: "echo test-end\n",
+ order: 50
+ ),
+ ]
+ let profilePreActions: [AutogenerationConfig.Action] = [
+ .init(
+ title: "Profile Start",
+ scriptText: "echo profile-start\n",
+ order: -75
+ ),
+ ]
+ let profilePostActions: [AutogenerationConfig.Action] = [
+ .init(
+ title: "Profile End",
+ scriptText: "echo profile-end\n",
+ order: 75
+ ),
+ ]
+
+ let expectedSchemeInfo = SchemeInfo(
+ name: "BLUEPRINT_NAME_Launchable",
+ test: .init(
+ buildTargets: [],
+ commandLineArguments: [],
+ enableAddressSanitizer: false,
+ enableThreadSanitizer: false,
+ enableUBSanitizer: false,
+ enableMainThreadChecker: false,
+ enableThreadPerformanceChecker: false,
+ environmentVariables: [],
+ options: nil,
+ testTargets: [],
+ useRunArgsAndEnv: true,
+ xcodeConfiguration: nil
+ ),
+ run: .init(
+ buildTargets: [],
+ commandLineArguments: [],
+ customWorkingDirectory: nil,
+ enableAddressSanitizer: false,
+ enableThreadSanitizer: false,
+ enableUBSanitizer: false,
+ enableMainThreadChecker: false,
+ enableThreadPerformanceChecker: false,
+ environmentVariables: baseEnvironmentVariables,
+ launchTarget: .target(
+ primary: launchable,
+ extensionHost: nil
+ ),
+ runBuildPostActionsOnFailure: true,
+ storeKitConfiguration: nil,
+ xcodeConfiguration: nil
+ ),
+ profile: .init(
+ buildTargets: [],
+ commandLineArguments: [],
+ customWorkingDirectory: nil,
+ environmentVariables: [],
+ launchTarget: .target(
+ primary: launchable,
+ extensionHost: nil
+ ),
+ useRunArgsAndEnv: true,
+ xcodeConfiguration: nil
+ ),
+ executionActions: [
+ .init(
+ title: "Build Start",
+ scriptText: "echo start\n",
+ action: .build,
+ isPreAction: true,
+ target: launchable,
+ order: -200
+ ),
+ .init(
+ title: "Build End",
+ scriptText: "echo end\n",
+ action: .build,
+ isPreAction: false,
+ target: launchable,
+ order: 100
+ ),
+ .init(
+ title: #"Run Start (DevX & "Logs")"#,
+ scriptText: "echo \"\"\n",
+ action: .run,
+ isPreAction: true,
+ target: launchable,
+ order: -100
+ ),
+ .init(
+ title: #"Run End (DevX & "Logs")"#,
+ scriptText: "echo \"\"\n",
+ action: .run,
+ isPreAction: false,
+ target: launchable,
+ order: 200
+ ),
+ .init(
+ title: "Test Start",
+ scriptText: "echo test-start\n",
+ action: .test,
+ isPreAction: true,
+ target: launchable,
+ order: -50
+ ),
+ .init(
+ title: "Test End",
+ scriptText: "echo test-end\n",
+ action: .test,
+ isPreAction: false,
+ target: launchable,
+ order: 50
+ ),
+ .init(
+ title: "Profile Start",
+ scriptText: "echo profile-start\n",
+ action: .profile,
+ isPreAction: true,
+ target: launchable,
+ order: -75
+ ),
+ .init(
+ title: "Profile End",
+ scriptText: "echo profile-end\n",
+ action: .profile,
+ isPreAction: false,
+ target: launchable,
+ order: 75
+ ),
+ ]
+ )
+
+ // Act
+
+ let schemeInfo = try createAutomaticSchemeInfoWithDefaults(
+ buildPostActions: buildPostActions,
+ buildPreActions: buildPreActions,
+ buildRunPostActionsOnFailure: true,
+ profilePostActions: profilePostActions,
+ profilePreActions: profilePreActions,
+ runPostActions: runPostActions,
+ runPreActions: runPreActions,
+ target: launchable,
+ testPostActions: testPostActions,
+ testPreActions: testPreActions
+ )
+
+ // Assert
+
+ XCTAssertNoDifference(schemeInfo, expectedSchemeInfo)
+ }
}
private let baseEnvironmentVariables: [EnvironmentVariable] = [
@@ -796,19 +1009,37 @@ private let baseEnvironmentVariables: [EnvironmentVariable] = [
]
private func createAutomaticSchemeInfoWithDefaults(
+ buildPostActions: [AutogenerationConfig.Action] = [],
+ buildPreActions: [AutogenerationConfig.Action] = [],
+ buildRunPostActionsOnFailure: Bool = false,
+ profilePostActions: [AutogenerationConfig.Action] = [],
+ profilePreActions: [AutogenerationConfig.Action] = [],
commandLineArguments: [CommandLineArgument] = [],
customSchemeNames: Set = [],
environmentVariables: [EnvironmentVariable] = [],
extensionHost: Target? = nil,
+ runPostActions: [AutogenerationConfig.Action] = [],
+ runPreActions: [AutogenerationConfig.Action] = [],
target: Target,
+ testPostActions: [AutogenerationConfig.Action] = [],
+ testPreActions: [AutogenerationConfig.Action] = [],
testOptions: SchemeInfo.Test.Options? = nil
) throws -> SchemeInfo? {
return try Generator.CreateAutomaticSchemeInfo.defaultCallable(
+ buildPostActions: buildPostActions,
+ buildPreActions: buildPreActions,
+ buildRunPostActionsOnFailure: buildRunPostActionsOnFailure,
+ profilePostActions: profilePostActions,
+ profilePreActions: profilePreActions,
commandLineArguments: commandLineArguments,
customSchemeNames: customSchemeNames,
environmentVariables: environmentVariables,
extensionHost: extensionHost,
+ runPostActions: runPostActions,
+ runPreActions: runPreActions,
target: target,
+ testPostActions: testPostActions,
+ testPreActions: testPreActions,
testOptions: testOptions
)
}
diff --git a/tools/generators/xcschemes/test/CreateAutomaticSchemeInfosTests.swift b/tools/generators/xcschemes/test/CreateAutomaticSchemeInfosTests.swift
index 1cee69d30..def98d157 100644
--- a/tools/generators/xcschemes/test/CreateAutomaticSchemeInfosTests.swift
+++ b/tools/generators/xcschemes/test/CreateAutomaticSchemeInfosTests.swift
@@ -167,6 +167,30 @@ final class CreateAutomaticSchemeInfosTests: XCTestCase {
func test_createTargetAutomaticSchemeInfos() throws {
// Arrange
+ let buildPostActions: [AutogenerationConfig.Action] = [
+ .init(title: "Build End", scriptText: "echo end\n", order: 100),
+ ]
+ let buildPreActions: [AutogenerationConfig.Action] = [
+ .init(title: "Build Start", scriptText: "echo start\n", order: -200),
+ ]
+ let profilePostActions: [AutogenerationConfig.Action] = [
+ .init(title: "Profile End", scriptText: "echo profile-end\n", order: 75),
+ ]
+ let profilePreActions: [AutogenerationConfig.Action] = [
+ .init(title: "Profile Start", scriptText: "echo profile-start\n", order: -75),
+ ]
+ let runPostActions: [AutogenerationConfig.Action] = [
+ .init(title: "Run End", scriptText: "echo run-end\n", order: 200),
+ ]
+ let runPreActions: [AutogenerationConfig.Action] = [
+ .init(title: "Run Start", scriptText: "echo run-start\n", order: -100),
+ ]
+ let testPostActions: [AutogenerationConfig.Action] = [
+ .init(title: "Test End", scriptText: "echo test-end\n", order: 50),
+ ]
+ let testPreActions: [AutogenerationConfig.Action] = [
+ .init(title: "Test Start", scriptText: "echo test-start\n", order: -50),
+ ]
let commandLineArguments: [TargetID: [CommandLineArgument]] = [
"A": [
.init(value: "-v", isEnabled: true),
@@ -210,16 +234,30 @@ final class CreateAutomaticSchemeInfosTests: XCTestCase {
Generator.CreateTargetAutomaticSchemeInfos.MockTracker.Called
] = [
.init(
+ buildPostActions: buildPostActions,
+ buildPreActions: buildPreActions,
+ buildRunPostActionsOnFailure: true,
+ profilePostActions: profilePostActions,
+ profilePreActions: profilePreActions,
commandLineArguments: [],
customSchemeNames: customSchemeNames,
environmentVariables: [],
extensionHostIDs: extensionHostIDs,
+ runPostActions: runPostActions,
+ runPreActions: runPreActions,
target: .mock(key: "C", productType: .application),
+ testPostActions: testPostActions,
+ testPreActions: testPreActions,
targetsByID: targetsByID,
targetsByKey: targetsByKey,
testOptions: nil
),
.init(
+ buildPostActions: buildPostActions,
+ buildPreActions: buildPreActions,
+ buildRunPostActionsOnFailure: true,
+ profilePostActions: profilePostActions,
+ profilePreActions: profilePreActions,
commandLineArguments: [
.init(value: "-v", isEnabled: true),
.init(value: "version", isEnabled: false),
@@ -228,12 +266,21 @@ final class CreateAutomaticSchemeInfosTests: XCTestCase {
customSchemeNames: customSchemeNames,
environmentVariables: [],
extensionHostIDs: extensionHostIDs,
+ runPostActions: runPostActions,
+ runPreActions: runPreActions,
target: .mock(key: "A", productType: .messagesExtension),
+ testPostActions: testPostActions,
+ testPreActions: testPreActions,
targetsByID: targetsByID,
targetsByKey: targetsByKey,
testOptions: nil
),
.init(
+ buildPostActions: buildPostActions,
+ buildPreActions: buildPreActions,
+ buildRunPostActionsOnFailure: true,
+ profilePostActions: profilePostActions,
+ profilePreActions: profilePreActions,
commandLineArguments: [],
customSchemeNames: customSchemeNames,
environmentVariables: [
@@ -241,7 +288,11 @@ final class CreateAutomaticSchemeInfosTests: XCTestCase {
.init(key: "ENV VAR", value: "1", isEnabled: true),
],
extensionHostIDs: extensionHostIDs,
+ runPostActions: runPostActions,
+ runPreActions: runPreActions,
target: .mock(key: "B", productType: .appExtension),
+ testPostActions: testPostActions,
+ testPreActions: testPreActions,
targetsByID: targetsByID,
targetsByKey: targetsByKey,
testOptions: nil
@@ -266,11 +317,20 @@ final class CreateAutomaticSchemeInfosTests: XCTestCase {
// Act
let schemeInfos = try createAutomaticSchemeInfosWithDefaults(
+ buildPostActions: buildPostActions,
+ buildPreActions: buildPreActions,
+ buildRunPostActionsOnFailure: true,
+ profilePostActions: profilePostActions,
+ profilePreActions: profilePreActions,
commandLineArguments: commandLineArguments,
customSchemeNames: customSchemeNames,
environmentVariables: environmentVariables,
extensionHostIDs: extensionHostIDs,
+ runPostActions: runPostActions,
+ runPreActions: runPreActions,
targets: targets,
+ testPostActions: testPostActions,
+ testPreActions: testPreActions,
targetsByID: targetsByID,
targetsByKey: targetsByKey,
createTargetAutomaticSchemeInfos:
@@ -289,11 +349,20 @@ final class CreateAutomaticSchemeInfosTests: XCTestCase {
private func createAutomaticSchemeInfosWithDefaults(
autogenerationMode: AutogenerationMode = .all,
+ buildPostActions: [AutogenerationConfig.Action] = [],
+ buildPreActions: [AutogenerationConfig.Action] = [],
+ buildRunPostActionsOnFailure: Bool = false,
+ profilePostActions: [AutogenerationConfig.Action] = [],
+ profilePreActions: [AutogenerationConfig.Action] = [],
commandLineArguments: [TargetID: [CommandLineArgument]] = [:],
customSchemeNames: Set = [],
environmentVariables: [TargetID: [EnvironmentVariable]] = [:],
extensionHostIDs: [TargetID : [TargetID]] = [:],
+ runPostActions: [AutogenerationConfig.Action] = [],
+ runPreActions: [AutogenerationConfig.Action] = [],
targets: [Target],
+ testPostActions: [AutogenerationConfig.Action] = [],
+ testPreActions: [AutogenerationConfig.Action] = [],
targetsByID: [TargetID : Target] = [:],
targetsByKey: [Target.Key : Target] = [:],
createTargetAutomaticSchemeInfos: Generator.CreateTargetAutomaticSchemeInfos,
@@ -301,13 +370,22 @@ private func createAutomaticSchemeInfosWithDefaults(
) throws -> [SchemeInfo] {
return try Generator.CreateAutomaticSchemeInfos.defaultCallable(
autogenerationMode: autogenerationMode,
+ buildPostActions: buildPostActions,
+ buildPreActions: buildPreActions,
+ buildRunPostActionsOnFailure: buildRunPostActionsOnFailure,
+ profilePostActions: profilePostActions,
+ profilePreActions: profilePreActions,
commandLineArguments: commandLineArguments,
customSchemeNames: customSchemeNames,
environmentVariables: environmentVariables,
extensionHostIDs: extensionHostIDs,
+ runPostActions: runPostActions,
+ runPreActions: runPreActions,
targets: targets,
targetsByID: targetsByID,
targetsByKey: targetsByKey,
+ testPostActions: testPostActions,
+ testPreActions: testPreActions,
createTargetAutomaticSchemeInfos: createTargetAutomaticSchemeInfos,
testOptions: testOptions
)
diff --git a/tools/generators/xcschemes/test/CreateCustomSchemeInfosTests.swift b/tools/generators/xcschemes/test/CreateCustomSchemeInfosTests.swift
index 8a44de8b7..8e4a06c0c 100644
--- a/tools/generators/xcschemes/test/CreateCustomSchemeInfosTests.swift
+++ b/tools/generators/xcschemes/test/CreateCustomSchemeInfosTests.swift
@@ -1,3 +1,4 @@
+import Foundation
import PBXProj
import XCScheme
import XCTest
@@ -5,6 +6,142 @@ import XCTest
@testable import xcschemes
final class CreateCustomSchemeInfosTests: XCTestCase {
+ // MARK: - defaultCallable
+
+ func test_defaultCallable_parsesCustomSchemeInfo() async throws {
+ let parsed = try await createCustomSchemeInfos()
+
+ XCTAssertEqual(
+ parsed.schemeInfos,
+ [
+ .mock(
+ name: "Scheme",
+ test: .mock(
+ buildTargets: [parsed.testBuildTarget],
+ commandLineArguments: [
+ .init(
+ value: "--test-target",
+ isEnabled: false,
+ isLiteralString: true
+ ),
+ ],
+ enableAddressSanitizer: true,
+ enableThreadSanitizer: false,
+ enableUBSanitizer: true,
+ enableMainThreadChecker: false,
+ enableThreadPerformanceChecker: true,
+ environmentVariables: .defaultEnvironmentVariables + [
+ .init(key: "TEST_ENV", value: "2", isEnabled: false),
+ ],
+ options: .init(
+ appLanguage: "en",
+ appRegion: "US",
+ codeCoverage: true
+ ),
+ testTargets: [
+ .init(target: parsed.testTarget, isEnabled: true),
+ ],
+ useRunArgsAndEnv: false,
+ xcodeConfiguration: "TestCfg"
+ ),
+ run: .mock(
+ buildTargets: [parsed.runBuildTarget],
+ commandLineArguments: [
+ .init(
+ value: "--run-target",
+ isEnabled: true,
+ isLiteralString: true
+ ),
+ ],
+ customWorkingDirectory: "/tmp/run",
+ enableMainThreadChecker: true,
+ enableThreadPerformanceChecker: false,
+ enableAddressSanitizer: false,
+ enableThreadSanitizer: true,
+ enableUBSanitizer: false,
+ environmentVariables: .defaultEnvironmentVariables + [
+ .init(key: "RUN_ENV", value: "1", isEnabled: true),
+ ],
+ launchTarget: .target(
+ primary: parsed.runTarget,
+ extensionHost: parsed.extensionHost
+ ),
+ runBuildPostActionsOnFailure: false,
+ storeKitConfiguration: parsed.expectedStoreKitConfiguration,
+ xcodeConfiguration: "RunCfg"
+ ),
+ profile: .mock(
+ commandLineArguments: [
+ .init(
+ value: "profiling\narg",
+ isEnabled: true,
+ isLiteralString: true
+ ),
+ ],
+ customWorkingDirectory: "/tmp/profile",
+ environmentVariables: [
+ .init(
+ key: "PROFILE_KEY",
+ value: "profile\nvalue",
+ isEnabled: true
+ ),
+ ],
+ launchTarget: .path("/tmp/Profile.app"),
+ useRunArgsAndEnv: false,
+ xcodeConfiguration: "ProfileCfg"
+ ),
+ executionActions: [
+ .init(
+ title: "Build Start\nTitle",
+ scriptText: "echo build\nstart",
+ action: .build,
+ isPreAction: true,
+ target: parsed.runTarget,
+ order: -10
+ ),
+ .init(
+ title: "Run End",
+ scriptText: "echo run end",
+ action: .run,
+ isPreAction: false,
+ target: parsed.runTarget,
+ order: 10
+ ),
+ .init(
+ title: "Test Start",
+ scriptText: "echo test start",
+ action: .test,
+ isPreAction: true,
+ target: parsed.testTarget,
+ order: nil
+ ),
+ .init(
+ title: "Profile End",
+ scriptText: "echo profile end",
+ action: .profile,
+ isPreAction: false,
+ target: parsed.runTarget,
+ order: 20
+ ),
+ ]
+ ),
+ ]
+ )
+ }
+
+ func test_defaultCallable_parsesRunBuildPostActionsOnFailure() async throws {
+ let parsed = try await createCustomSchemeInfos(
+ runBuildPostActionsOnFailure: true
+ )
+
+ XCTAssertEqual(
+ parsed.schemeInfos.map(\.run.runBuildPostActionsOnFailure),
+ [true]
+ )
+ }
+
+ // MARK: - mergingEnvironmentVariables
+
func test_merging_environment_variables() throws {
let targets: [SchemeInfo.TestTarget] = [
.init(
@@ -77,6 +214,8 @@ final class CreateCustomSchemeInfosTests: XCTestCase {
)
}
+ // MARK: - URL.relativize
+
func test_url_relativize() {
typealias TestCase = (dest: URL, source: URL, expected: String?)
let testCases: [TestCase] = [
@@ -105,3 +244,231 @@ final class CreateCustomSchemeInfosTests: XCTestCase {
}
}
}
+
+private struct ParsedCustomSchemeInfos {
+ let expectedStoreKitConfiguration: String?
+ let schemeInfos: [SchemeInfo]
+ let extensionHost: Target
+ let runBuildTarget: Target
+ let runTarget: Target
+ let testBuildTarget: Target
+ let testTarget: Target
+}
+
+private func createCustomSchemeInfos(
+ runBuildPostActionsOnFailure: Bool = false
+) async throws -> ParsedCustomSchemeInfos {
+ let directory = FileManager.default.temporaryDirectory
+ .appendingPathComponent(UUID().uuidString, isDirectory: true)
+ try FileManager.default.createDirectory(
+ at: directory,
+ withIntermediateDirectories: true
+ )
+ defer { try? FileManager.default.removeItem(at: directory) }
+
+ let schemesDirectory = directory.appendingPathComponent(
+ "Project.xcodeproj/xcshareddata/xcschemes",
+ isDirectory: true
+ )
+ try FileManager.default.createDirectory(
+ at: schemesDirectory,
+ withIntermediateDirectories: true
+ )
+
+ let runTarget = makeTarget(
+ "run_extension",
+ productType: .appExtension
+ )
+ let extensionHost = makeTarget(
+ "host_app",
+ productType: .application
+ )
+ let runBuildTarget = makeTarget("run_build_target")
+ let testBuildTarget = makeTarget("test_build_target")
+ let testTarget = makeTarget(
+ "test_target",
+ productType: .unitTestBundle
+ )
+ let targetsByID = Dictionary(
+ uniqueKeysWithValues: [
+ runTarget,
+ extensionHost,
+ runBuildTarget,
+ testBuildTarget,
+ testTarget,
+ ].map { ($0.key.sortedIds.first!, $0) }
+ )
+
+ let customSchemesFile = directory.appendingPathComponent(
+ "custom_schemes_file"
+ )
+ try writeLines([
+ "1",
+ "Scheme",
+ "1",
+ "test_target",
+ "1",
+ "test_build_target",
+ "",
+ "-1",
+ "-1",
+ "1",
+ "0",
+ "1",
+ "0",
+ "1",
+ "0",
+ "1",
+ "en",
+ "US",
+ "1",
+ "TestCfg",
+ "run_build_target",
+ "",
+ "-1",
+ "-1",
+ "1",
+ "0",
+ "1",
+ "0",
+ "1",
+ "0",
+ "Configs/Fixture.storekit",
+ "RunCfg",
+ "0",
+ "run_extension",
+ "host_app",
+ "/tmp/run",
+ runBuildPostActionsOnFailure ? "1" : "0",
+ "",
+ "1",
+ "profiling\0arg",
+ "1",
+ "1",
+ "1",
+ "PROFILE_KEY",
+ "profile\0value",
+ "1",
+ "0",
+ "0",
+ "ProfileCfg",
+ "1",
+ "/tmp/Profile.app",
+ "/tmp/profile",
+ ], to: customSchemesFile)
+
+ let executionActionsFile = directory.appendingPathComponent(
+ "execution_actions_file"
+ )
+ try writeLines([
+ "Scheme",
+ "build",
+ "1",
+ "Build Start\0Title",
+ "echo build\0start",
+ "run_extension",
+ "-10",
+ "Scheme",
+ "run",
+ "0",
+ "Run End",
+ "echo run end",
+ "run_extension",
+ "10",
+ "Scheme",
+ "test",
+ "1",
+ "Test Start",
+ "echo test start",
+ "test_target",
+ "",
+ "Scheme",
+ "profile",
+ "0",
+ "Profile End",
+ "echo profile end",
+ "run_extension",
+ "20",
+ ], to: executionActionsFile)
+
+ let expectedStoreKitConfiguration = URL(
+ filePath: "Configs/Fixture.storekit",
+ relativeTo: directory
+ ).relativize(from: schemesDirectory)
+
+ let schemeInfos = try await Generator.CreateCustomSchemeInfos
+ .defaultCallable(
+ commandLineArguments: [
+ runTarget.key.sortedIds.first!: [
+ .init(
+ value: "--run-target",
+ isEnabled: true,
+ isLiteralString: true
+ ),
+ ],
+ testTarget.key.sortedIds.first!: [
+ .init(
+ value: "--test-target",
+ isEnabled: false,
+ isLiteralString: true
+ ),
+ ],
+ ],
+ customSchemesFile: customSchemesFile,
+ environmentVariables: [
+ runTarget.key.sortedIds.first!: [
+ .init(key: "RUN_ENV", value: "1", isEnabled: true),
+ ],
+ testTarget.key.sortedIds.first!: [
+ .init(key: "TEST_ENV", value: "2", isEnabled: false),
+ ],
+ ],
+ executionActionsFile: executionActionsFile,
+ extensionHostIDs: [
+ runTarget.key.sortedIds.first!: [
+ extensionHost.key.sortedIds.first!,
+ ],
+ ],
+ schemesDirectory: schemesDirectory,
+ targetsByID: targetsByID,
+ workspace: directory
+ )
+
+ return ParsedCustomSchemeInfos(
+ expectedStoreKitConfiguration: expectedStoreKitConfiguration,
+ schemeInfos: schemeInfos,
+ extensionHost: extensionHost,
+ runBuildTarget: runBuildTarget,
+ runTarget: runTarget,
+ testBuildTarget: testBuildTarget,
+ testTarget: testTarget
+ )
+}
+
+private func makeTarget(
+ _ id: String,
+ productType: PBXProductType = .staticLibrary
+) -> Target {
+ let targetID = TargetID(id)
+ return Target(
+ key: .init([targetID]),
+ productType: productType,
+ buildableReference: .init(
+ blueprintIdentifier: "\(id)_blueprintIdentifier",
+ buildableName: "\(id)_buildableName",
+ blueprintName: id,
+ referencedContainer: "container:/tmp/\(id).xcodeproj"
+ )
+ )
+}
+
+private func writeLines(
+ _ lines: [String],
+ to url: URL
+) throws {
+ try (lines.joined(separator: "\n") + "\n").write(
+ to: url,
+ atomically: true,
+ encoding: .utf8
+ )
+}
diff --git a/tools/generators/xcschemes/test/CreateSchemeTests.swift b/tools/generators/xcschemes/test/CreateSchemeTests.swift
index 5a1850b9c..ab9a4737f 100644
--- a/tools/generators/xcschemes/test/CreateSchemeTests.swift
+++ b/tools/generators/xcschemes/test/CreateSchemeTests.swift
@@ -6,6 +6,107 @@ import XCTest
@testable import xcschemes
final class CreateSchemeTests: XCTestCase {
+ func test_buildAction_includesAutogeneratedBuildActions() throws {
+ // Arrange
+
+ let target = Target.mock(
+ key: "App",
+ productType: .application,
+ buildableReference: buildableReference(
+ name: "App",
+ referencedContainer: "container:/tmp/App.xcodeproj"
+ )
+ )
+ let expectedBuildAction = CreateBuildAction.defaultCallable(
+ entries: [
+ .init(
+ buildableReference: target.buildableReference,
+ buildFor: [.running, .profiling, .analyzing]
+ ),
+ ],
+ postActions: [
+ .init(
+ title: #"Build End (DevX & "Logs")"#,
+ escapedScriptText: "echo \"\"\n".schemeXmlEscaped,
+ expandVariablesBasedOn: target.buildableReference
+ ),
+ ],
+ preActions: [
+ .init(
+ title: #"Build Start (DevX & "Logs")"#,
+ escapedScriptText: "echo \"\"\n".schemeXmlEscaped,
+ expandVariablesBasedOn: target.buildableReference
+ ),
+ .init(
+ title: "Initialize Bazel Build Output Groups File",
+ escapedScriptText: #"""
+mkdir -p "${BUILD_MARKER_FILE%/*}"
+touch "$BUILD_MARKER_FILE"
+
+"""#.schemeXmlEscaped,
+ expandVariablesBasedOn: target.buildableReference
+ ),
+ .init(
+ title: "Prepare BazelDependencies",
+ escapedScriptText: #"""
+mkdir -p "$PROJECT_DIR"
+
+if [[ "${ENABLE_ADDRESS_SANITIZER:-}" == "YES" || \
+ "${ENABLE_THREAD_SANITIZER:-}" == "YES" || \
+ "${ENABLE_UNDEFINED_BEHAVIOR_SANITIZER:-}" == "YES" ]]
+then
+ # TODO: Support custom toolchains once clang.sh supports them
+ cd "$INTERNAL_DIR" || exit 1
+ ln -shfF "$DEVELOPER_DIR/Toolchains/XcodeDefault.xctoolchain/usr/lib" lib
+fi
+
+"""#.schemeXmlEscaped,
+ expandVariablesBasedOn: target.buildableReference
+ ),
+ ],
+ runPostActionsOnFailure: true
+ )
+
+ // Act
+
+ let scheme = try createSchemeWithDefaults(
+ schemeInfo: .mock(
+ name: "Scheme",
+ run: .mock(
+ launchTarget: .target(primary: target, extensionHost: nil),
+ runBuildPostActionsOnFailure: true
+ ),
+ profile: .mock(
+ launchTarget: .target(primary: target, extensionHost: nil)
+ ),
+ executionActions: [
+ .init(
+ title: #"Build Start (DevX & "Logs")"#,
+ scriptText: "echo \"\"\n",
+ action: .build,
+ isPreAction: true,
+ target: target,
+ order: -200
+ ),
+ .init(
+ title: #"Build End (DevX & "Logs")"#,
+ scriptText: "echo \"\"\n",
+ action: .build,
+ isPreAction: false,
+ target: target,
+ order: 100
+ ),
+ ]
+ )
+ )
+
+ let buildAction = try XCTUnwrap(extractBuildAction(from: scheme))
+
+ // Assert
+
+ XCTAssertNoDifference(buildAction, expectedBuildAction)
+ }
+
func test_testAction_usesFirstTestableForCustomLLDBInitFile() throws {
// Arrange
@@ -144,6 +245,181 @@ final class CreateSchemeTests: XCTestCase {
XCTAssertNoDifference(launchAction, expectedLaunchAction)
}
+
+ func test_runTestAndProfileActions_includeAutogeneratedActions() throws {
+ // Arrange
+
+ let target = Target.mock(
+ key: "App",
+ productType: .application,
+ buildableReference: buildableReference(
+ name: "App",
+ referencedContainer: "container:/tmp/App.xcodeproj"
+ )
+ )
+ let runPreAction = ExecutionAction(
+ title: #"Run Start (DevX & "Logs")"#,
+ escapedScriptText: "echo \"\"\n".schemeXmlEscaped,
+ expandVariablesBasedOn: target.buildableReference
+ )
+ let runPostAction = ExecutionAction(
+ title: #"Run End (DevX & "Logs")"#,
+ escapedScriptText: "echo \"\"\n".schemeXmlEscaped,
+ expandVariablesBasedOn: target.buildableReference
+ )
+ let testPreAction = ExecutionAction(
+ title: #"Test Start (DevX & "Logs")"#,
+ escapedScriptText: "echo \"\"\n".schemeXmlEscaped,
+ expandVariablesBasedOn: target.buildableReference
+ )
+ let testPostAction = ExecutionAction(
+ title: #"Test End (DevX & "Logs")"#,
+ escapedScriptText: "echo \"\"\n".schemeXmlEscaped,
+ expandVariablesBasedOn: target.buildableReference
+ )
+ let profilePreAction = ExecutionAction(
+ title: #"Profile Start (DevX & "Logs")"#,
+ escapedScriptText: "echo \"\"\n".schemeXmlEscaped,
+ expandVariablesBasedOn: target.buildableReference
+ )
+ let profilePostAction = ExecutionAction(
+ title: #"Profile End (DevX & "Logs")"#,
+ escapedScriptText: "echo \"\"\n".schemeXmlEscaped,
+ expandVariablesBasedOn: target.buildableReference
+ )
+ let expectedLaunchAction = CreateLaunchAction.defaultCallable(
+ buildConfiguration: "Debug",
+ commandLineArguments: [],
+ customLLDBInitFile: "/tmp/App.xcodeproj/rules_xcodeproj/bazel.lldbinit",
+ customWorkingDirectory: nil,
+ enableAddressSanitizer: false,
+ enableThreadSanitizer: false,
+ enableUBSanitizer: false,
+ enableMainThreadChecker: false,
+ enableThreadPerformanceChecker: false,
+ environmentVariables: [],
+ postActions: [runPostAction],
+ preActions: [
+ runPreAction,
+ updateLldbInitAndCopyDSYMsAction(
+ buildableReference: target.buildableReference
+ ),
+ ],
+ runnable: .plain(buildableReference: target.buildableReference),
+ storeKitConfiguration: nil
+ )
+ let expectedTestAction = CreateTestAction.defaultCallable(
+ appLanguage: nil,
+ appRegion: nil,
+ codeCoverage: false,
+ buildConfiguration: "Debug",
+ commandLineArguments: [],
+ customLLDBInitFile: nil,
+ enableAddressSanitizer: false,
+ enableThreadSanitizer: false,
+ enableUBSanitizer: false,
+ enableMainThreadChecker: false,
+ enableThreadPerformanceChecker: false,
+ environmentVariables: [],
+ expandVariablesBasedOn: nil,
+ postActions: [testPostAction],
+ preActions: [testPreAction],
+ testables: [],
+ useLaunchSchemeArgsEnv: true
+ )
+ let expectedProfileAction = CreateProfileAction.defaultCallable(
+ buildConfiguration: "Debug",
+ commandLineArguments: [],
+ customLLDBInitFile: "/tmp/App.xcodeproj/rules_xcodeproj/bazel.lldbinit",
+ customWorkingDirectory: nil,
+ environmentVariables: [],
+ postActions: [profilePostAction],
+ preActions: [
+ profilePreAction,
+ updateLldbInitAndCopyDSYMsAction(
+ buildableReference: target.buildableReference
+ ),
+ ],
+ useLaunchSchemeArgsEnv: true,
+ runnable: .plain(buildableReference: target.buildableReference)
+ )
+
+ // Act
+
+ let scheme = try createSchemeWithDefaults(
+ schemeInfo: .mock(
+ name: "Scheme",
+ test: .mock(),
+ run: .mock(
+ launchTarget: .target(primary: target, extensionHost: nil),
+ runBuildPostActionsOnFailure: true
+ ),
+ profile: .mock(
+ launchTarget: .target(primary: target, extensionHost: nil)
+ ),
+ executionActions: [
+ .init(
+ title: #"Run Start (DevX & "Logs")"#,
+ scriptText: "echo \"\"\n",
+ action: .run,
+ isPreAction: true,
+ target: target,
+ order: -10
+ ),
+ .init(
+ title: #"Run End (DevX & "Logs")"#,
+ scriptText: "echo \"\"\n",
+ action: .run,
+ isPreAction: false,
+ target: target,
+ order: 10
+ ),
+ .init(
+ title: #"Test Start (DevX & "Logs")"#,
+ scriptText: "echo \"\"\n",
+ action: .test,
+ isPreAction: true,
+ target: target,
+ order: -20
+ ),
+ .init(
+ title: #"Test End (DevX & "Logs")"#,
+ scriptText: "echo \"\"\n",
+ action: .test,
+ isPreAction: false,
+ target: target,
+ order: 20
+ ),
+ .init(
+ title: #"Profile Start (DevX & "Logs")"#,
+ scriptText: "echo \"\"\n",
+ action: .profile,
+ isPreAction: true,
+ target: target,
+ order: -30
+ ),
+ .init(
+ title: #"Profile End (DevX & "Logs")"#,
+ scriptText: "echo \"\"\n",
+ action: .profile,
+ isPreAction: false,
+ target: target,
+ order: 30
+ ),
+ ]
+ )
+ )
+
+ let launchAction = try XCTUnwrap(extractLaunchAction(from: scheme))
+ let testAction = try XCTUnwrap(extractTestAction(from: scheme))
+ let profileAction = try XCTUnwrap(extractProfileAction(from: scheme))
+
+ // Assert
+
+ XCTAssertNoDifference(launchAction, expectedLaunchAction)
+ XCTAssertNoDifference(testAction, expectedTestAction)
+ XCTAssertNoDifference(profileAction, expectedProfileAction)
+ }
}
private func createSchemeWithDefaults(
@@ -167,6 +443,10 @@ private func extractTestAction(from scheme: String) -> String? {
extractElement(named: "TestAction", from: scheme)
}
+private func extractBuildAction(from scheme: String) -> String? {
+ extractElement(named: "BuildAction", from: scheme)
+}
+
private func extractProfileAction(from scheme: String) -> String? {
extractElement(named: "ProfileAction", from: scheme)
}
diff --git a/tools/generators/xcschemes/test/CreateTargetAutomaticSchemeInfos+Testing.swift b/tools/generators/xcschemes/test/CreateTargetAutomaticSchemeInfos+Testing.swift
index ed0098fd3..8a3598392 100644
--- a/tools/generators/xcschemes/test/CreateTargetAutomaticSchemeInfos+Testing.swift
+++ b/tools/generators/xcschemes/test/CreateTargetAutomaticSchemeInfos+Testing.swift
@@ -8,11 +8,20 @@ import XCScheme
extension Generator.CreateTargetAutomaticSchemeInfos {
final class MockTracker {
struct Called: Equatable {
+ let buildPostActions: [AutogenerationConfig.Action]
+ let buildPreActions: [AutogenerationConfig.Action]
+ let buildRunPostActionsOnFailure: Bool
+ let profilePostActions: [AutogenerationConfig.Action]
+ let profilePreActions: [AutogenerationConfig.Action]
let commandLineArguments: [CommandLineArgument]
let customSchemeNames: Set
let environmentVariables: [EnvironmentVariable]
let extensionHostIDs: [TargetID: [TargetID]]
+ let runPostActions: [AutogenerationConfig.Action]
+ let runPreActions: [AutogenerationConfig.Action]
let target: Target
+ let testPostActions: [AutogenerationConfig.Action]
+ let testPreActions: [AutogenerationConfig.Action]
let targetsByID: [TargetID: Target]
let targetsByKey: [Target.Key: Target]
let testOptions: SchemeInfo.Test.Options?
@@ -43,21 +52,40 @@ extension Generator.CreateTargetAutomaticSchemeInfos {
createAutomaticSchemeInfo:
Generator.Stubs.createAutomaticSchemeInfo,
callable: {
+ buildPostActions,
+ buildPreActions,
+ buildRunPostActionsOnFailure,
+ profilePostActions,
+ profilePreActions,
commandLineArguments,
customSchemeNames,
environmentVariables,
extensionHostIDs,
+ runPostActions,
+ runPreActions,
target,
targetsByID,
targetsByKey,
+ testPostActions,
+ testPreActions,
testOptions,
_ in
mockTracker.called.append(.init(
+ buildPostActions: buildPostActions,
+ buildPreActions: buildPreActions,
+ buildRunPostActionsOnFailure:
+ buildRunPostActionsOnFailure,
+ profilePostActions: profilePostActions,
+ profilePreActions: profilePreActions,
commandLineArguments: commandLineArguments,
customSchemeNames: customSchemeNames,
environmentVariables: environmentVariables,
extensionHostIDs: extensionHostIDs,
+ runPostActions: runPostActions,
+ runPreActions: runPreActions,
target: target,
+ testPostActions: testPostActions,
+ testPreActions: testPreActions,
targetsByID: targetsByID,
targetsByKey: targetsByKey,
testOptions: testOptions
diff --git a/tools/generators/xcschemes/test/SchemeInfo+Testing.swift b/tools/generators/xcschemes/test/SchemeInfo+Testing.swift
index 35df0ce70..1be140fc2 100644
--- a/tools/generators/xcschemes/test/SchemeInfo+Testing.swift
+++ b/tools/generators/xcschemes/test/SchemeInfo+Testing.swift
@@ -54,6 +54,7 @@ extension SchemeInfo.Run {
enableUBSanitizer: Bool = false,
environmentVariables: [EnvironmentVariable] = [],
launchTarget: SchemeInfo.LaunchTarget? = nil,
+ runBuildPostActionsOnFailure: Bool = false,
storeKitConfiguration: String? = nil,
xcodeConfiguration: String? = nil
) -> Self {
@@ -68,6 +69,8 @@ extension SchemeInfo.Run {
enableThreadPerformanceChecker: enableThreadPerformanceChecker,
environmentVariables: environmentVariables,
launchTarget: launchTarget,
+ runBuildPostActionsOnFailure:
+ runBuildPostActionsOnFailure,
storeKitConfiguration: storeKitConfiguration,
xcodeConfiguration: xcodeConfiguration
)
diff --git a/xcodeproj/internal/xcodeproj_rule.bzl b/xcodeproj/internal/xcodeproj_rule.bzl
index 7aa25315d..9116e99c3 100644
--- a/xcodeproj/internal/xcodeproj_rule.bzl
+++ b/xcodeproj/internal/xcodeproj_rule.bzl
@@ -203,27 +203,6 @@ https://github.com/MobileNativeFoundation/rules_xcodeproj/issues/new?template=bu
# Actions
-def _write_autogeneration_config_file(
- actions,
- config,
- name):
- autogeneration_config_file = actions.declare_file(
- "{}-autogeneration-config-file".format(name),
- )
-
- args = actions.args()
- args.set_param_file_format("multiline")
-
- args.add_all(config.get("test_options", ["", "", False]))
- args.add_all(
- config.get("scheme_name_exclude_patterns", []),
- omit_if_empty = False,
- terminate_with = "",
- )
- actions.write(autogeneration_config_file, args)
-
- return autogeneration_config_file
-
def _write_bazel_integration_files(
*,
actions,
@@ -547,16 +526,10 @@ def _write_schemes(
top_level_deps = top_level_deps,
)
- autogeneration_config_file = _write_autogeneration_config_file(
- actions = actions,
- config = autogeneration_config,
- name = name,
- )
-
return xcschemes_execution.write_schemes(
actions = actions,
autogeneration_mode = autogeneration_mode,
- autogeneration_config_file = autogeneration_config_file,
+ autogeneration_config = autogeneration_config,
default_xcode_configuration = default_xcode_configuration,
colorize = colorize,
consolidation_maps = consolidation_maps,
diff --git a/xcodeproj/internal/xcschemes/xcscheme_infos.bzl b/xcodeproj/internal/xcschemes/xcscheme_infos.bzl
index 9aca1d8f1..232a24130 100644
--- a/xcodeproj/internal/xcschemes/xcscheme_infos.bzl
+++ b/xcodeproj/internal/xcschemes/xcscheme_infos.bzl
@@ -145,6 +145,7 @@ def _make_run(
env = None,
env_include_defaults = TRUE_ARG,
launch_target = _make_launch_target(),
+ run_build_post_actions_on_failure = FALSE_ARG,
storekit_configuration = EMPTY_STRING,
xcode_configuration = EMPTY_STRING):
return struct(
@@ -154,6 +155,7 @@ def _make_run(
env = env,
env_include_defaults = env_include_defaults,
launch_target = launch_target,
+ run_build_post_actions_on_failure = run_build_post_actions_on_failure,
storekit_configuration = storekit_configuration,
xcode_configuration = xcode_configuration,
)
@@ -636,6 +638,9 @@ def _run_info_from_dict(
env = _env_infos_from_dict(run["env"]),
env_include_defaults = run["env_include_defaults"],
launch_target = launch_target,
+ run_build_post_actions_on_failure = (
+ run["run_build_post_actions_on_failure"]
+ ),
storekit_configuration = _storekit_configuration_info(
run["storekit_configuration"],
storekit_configurations_map,
diff --git a/xcodeproj/internal/xcschemes/xcscheme_labels.bzl b/xcodeproj/internal/xcschemes/xcscheme_labels.bzl
index 0962fac64..20deea00b 100644
--- a/xcodeproj/internal/xcschemes/xcscheme_labels.bzl
+++ b/xcodeproj/internal/xcschemes/xcscheme_labels.bzl
@@ -111,6 +111,9 @@ def _resolve_run_labels(run):
env = run.env,
env_include_defaults = run.env_include_defaults,
launch_target = _resolve_launch_target_labels(run.launch_target),
+ run_build_post_actions_on_failure = (
+ run.run_build_post_actions_on_failure
+ ),
storekit_configuration = _resolve_label(run.storekit_configuration),
xcode_configuration = run.xcode_configuration,
)
diff --git a/xcodeproj/internal/xcschemes/xcschemes_execution.bzl b/xcodeproj/internal/xcschemes/xcschemes_execution.bzl
index 0a76bb6b6..133ca307d 100644
--- a/xcodeproj/internal/xcschemes/xcschemes_execution.bzl
+++ b/xcodeproj/internal/xcschemes/xcschemes_execution.bzl
@@ -32,7 +32,7 @@ def _write_schemes(
*,
actions,
autogeneration_mode,
- autogeneration_config_file,
+ autogeneration_config,
colorize,
consolidation_maps,
default_xcode_configuration,
@@ -51,7 +51,8 @@ def _write_schemes(
actions: `ctx.actions`.
autogeneration_mode: Specifies how Xcode schemes are automatically
generated.
- autogeneration_config_file: A `File` containing `AutogenerationConfigArguments` inputs.
+ autogeneration_config: A `dict` of
+ `AutogenerationConfig` inputs.
colorize: A `bool` indicating whether to colorize the output.
consolidation_maps: A `list` of `File`s containing target consolidation
maps.
@@ -98,6 +99,9 @@ def _write_schemes(
custom_schemes_file = actions.declare_file(
"{}_pbxproj_partials/custom_schemes_file".format(generator_name),
)
+ autogeneration_config_file = actions.declare_file(
+ "{}-autogeneration-config-file".format(generator_name),
+ )
inputs = consolidation_maps + [
autogeneration_config_file,
@@ -120,6 +124,9 @@ def _write_schemes(
custom_scheme_args = actions.args()
custom_scheme_args.set_param_file_format("multiline")
+ autogeneration_config_args = actions.args()
+ autogeneration_config_args.set_param_file_format("multiline")
+
# outputDirectory
args.add_all([output], expand_directories = False)
@@ -181,6 +188,44 @@ def _write_schemes(
map_each = _null_newlines,
)
+ # AutogenerationConfig
+
+ autogeneration_config_args.add_all(
+ autogeneration_config["test_options"],
+ )
+ for pre_post_actions_key in (
+ "build_pre_actions",
+ "build_post_actions",
+ ):
+ pre_post_actions = autogeneration_config[pre_post_actions_key]
+ autogeneration_config_args.add(len(pre_post_actions) // 3)
+ autogeneration_config_args.add_all(
+ pre_post_actions,
+ map_each = _null_newlines,
+ )
+ autogeneration_config_args.add_all(
+ autogeneration_config["build_run_post_actions_on_failure"],
+ )
+ for pre_post_actions_key in (
+ "profile_pre_actions",
+ "profile_post_actions",
+ "run_pre_actions",
+ "run_post_actions",
+ "test_pre_actions",
+ "test_post_actions",
+ ):
+ pre_post_actions = autogeneration_config[pre_post_actions_key]
+ autogeneration_config_args.add(len(pre_post_actions) // 3)
+ autogeneration_config_args.add_all(
+ pre_post_actions,
+ map_each = _null_newlines,
+ )
+ autogeneration_config_args.add_all(
+ autogeneration_config["scheme_name_exclude_patterns"],
+ omit_if_empty = False,
+ terminate_with = "",
+ )
+
# CreateCustomSchemeInfos
def _add_args(args):
@@ -351,6 +396,8 @@ def _write_schemes(
scheme_name = scheme_name,
)
+ custom_scheme_args.add(info.run.run_build_post_actions_on_failure)
+
# Profile
_add_build_targets(
@@ -378,6 +425,7 @@ def _write_schemes(
actions.write(execution_actions_file, execution_actions_args)
actions.write(targets_args_env_file, targets_args_env_args)
actions.write(custom_schemes_file, custom_scheme_args)
+ actions.write(autogeneration_config_file, autogeneration_config_args)
actions.run(
arguments = [args],
diff --git a/xcodeproj/xcodeproj.bzl b/xcodeproj/xcodeproj.bzl
index ad60147a0..76f6a13a5 100644
--- a/xcodeproj/xcodeproj.bzl
+++ b/xcodeproj/xcodeproj.bzl
@@ -5,6 +5,7 @@ load(
_default_project_options = "project_options",
)
load("//xcodeproj:top_level_target.bzl", "top_level_target")
+load("//xcodeproj:xcschemes.bzl", _xcschemes = "xcschemes")
load("//xcodeproj/internal:bazel_labels.bzl", "bazel_labels")
load("//xcodeproj/internal:xcodeproj_runner.bzl", "xcodeproj_runner")
load(
@@ -40,7 +41,7 @@ def xcodeproj(
project_name = None,
project_options = None,
scheme_autogeneration_mode = "auto",
- scheme_autogeneration_config = {},
+ scheme_autogeneration_config = None,
target_name_mode = "auto",
top_level_targets,
tvos_device_cpus = "arm64",
@@ -374,6 +375,8 @@ def xcodeproj(
project_name = name
if not project_options:
project_options = _default_project_options()
+ if not scheme_autogeneration_config:
+ scheme_autogeneration_config = _xcschemes.autogeneration_config()
if not xcode_configurations:
xcode_configurations = {"Debug": {}}
diff --git a/xcodeproj/xcschemes.bzl b/xcodeproj/xcschemes.bzl
index 1590e1edb..76f5cb01e 100644
--- a/xcodeproj/xcschemes.bzl
+++ b/xcodeproj/xcschemes.bzl
@@ -212,6 +212,7 @@ def _run(
env = "inherit",
env_include_defaults = True,
launch_target = None,
+ run_build_post_actions_on_failure = False,
storekit_configuration = None,
xcode_configuration = None):
"""Defines the Run action.
@@ -336,6 +337,8 @@ def _run(
`None`, `xcschemes.launch_target()` will be used, which means no
launch target will be set (i.e. the `Executable` dropdown will be
set to `None`).
+ run_build_post_actions_on_failure: Whether build post-actions should
+ run even when the scheme's `BuildAction` fails.
storekit_configuration: A StoreKit configuration file for use with
[StoreKit Testing](https://developer.apple.com/documentation/xcode/setting-up-storekit-testing-in-xcode).
@@ -356,6 +359,9 @@ def _run(
env = env or {},
env_include_defaults = TRUE_ARG if env_include_defaults else FALSE_ARG,
launch_target = launch_target,
+ run_build_post_actions_on_failure = (
+ TRUE_ARG if run_build_post_actions_on_failure else FALSE_ARG
+ ),
storekit_configuration = storekit_configuration,
xcode_configuration = xcode_configuration or "",
)
@@ -1232,28 +1238,158 @@ def _test_options(
),
)
-def _autogeneration_test(*, options = None):
+def _autogeneration_profile(*, post_actions = [], pre_actions = []):
+ """Creates a value for the `profile` argument of `xcschemes.autogeneration_config`.
+
+ Args:
+ post_actions: Profile post-actions for autogenerated schemes.
+
+ Defaults to `[]`.
+ pre_actions: Profile pre-actions for autogenerated schemes.
+
+ Defaults to `[]`.
+
+ Returns:
+ An opaque value for the
+ [`profile`](user-content-xcschemes.autogeneration_config-profile)
+ argument of `xcschemes.autogeneration_config`.
+ """
+ for action in post_actions or []:
+ if action.for_build:
+ fail("""
+`post_actions` for `xcschemes.autogeneration.profile` must use `xcschemes.pre_post_actions.launch_script`.
+""")
+
+ for action in pre_actions or []:
+ if action.for_build:
+ fail("""
+`pre_actions` for `xcschemes.autogeneration.profile` must use `xcschemes.pre_post_actions.launch_script`.
+""")
+
+ return struct(
+ post_actions = post_actions or [],
+ pre_actions = pre_actions or [],
+ )
+
+def _autogeneration_run(
+ *,
+ post_actions = [],
+ pre_actions = [],
+ run_build_post_actions_on_failure = False):
+ """Creates a value for the `run` argument of `xcschemes.autogeneration_config`.
+
+ Args:
+ post_actions: Build and run post-actions for autogenerated schemes.
+
+ Defaults to `[]`.
+ pre_actions: Build and run pre-actions for autogenerated schemes.
+
+ Defaults to `[]`.
+ run_build_post_actions_on_failure: Whether autogenerated build
+ post-actions should run even when the build fails.
+
+ Returns:
+ An opaque value for the
+ [`run`](user-content-xcschemes.autogeneration_config-run)
+ argument of `xcschemes.autogeneration_config`.
+ """
+ return struct(
+ post_actions = post_actions or [],
+ pre_actions = pre_actions or [],
+ run_build_post_actions_on_failure = (
+ TRUE_ARG if run_build_post_actions_on_failure else FALSE_ARG
+ ),
+ )
+
+def _autogeneration_test(*, options = None, post_actions = [], pre_actions = []):
"""Creates a value for the `test` argument of `xcschemes.autogeneration_config`.
Args:
options: Test options for autogeneration.
Defaults to `None`.
+ post_actions: Test post-actions for autogenerated schemes.
+
+ Defaults to `[]`.
+ pre_actions: Test pre-actions for autogenerated schemes.
+
+ Defaults to `[]`.
Returns:
An opaque value for the
[`test`](user-content-xcschemes.autogeneration_config-test)
argument of `xcschemes.autogeneration_config`.
"""
+ for action in post_actions or []:
+ if action.for_build:
+ fail("""
+`post_actions` for `xcschemes.autogeneration.test` must use `xcschemes.pre_post_actions.launch_script`.
+""")
+
+ for action in pre_actions or []:
+ if action.for_build:
+ fail("""
+`pre_actions` for `xcschemes.autogeneration.test` must use `xcschemes.pre_post_actions.launch_script`.
+""")
return struct(
+ post_actions = post_actions or [],
+ pre_actions = pre_actions or [],
test_options = options,
)
-def _autogeneration_config(*, scheme_name_exclude_patterns = None, test = None):
+def _autogeneration_config(
+ *,
+ profile = None,
+ run = None,
+ scheme_name_exclude_patterns = None,
+ test = None):
"""Creates a value for the [`scheme_autogeneration_config`](xcodeproj-scheme_autogeneration_config) attribute of `xcodeproj`.
Args:
+ profile: Options to use for the profile action.
+
+ Example:
+
+ ```starlark
+ xcodeproj(
+ ...
+ scheme_autogeneration_config = xcschemes.autogeneration_config(
+ profile = xcschemes.autogeneration.profile(
+ pre_actions = [
+ xcschemes.pre_post_actions.launch_script(
+ title = "Profile Start",
+ script_text = "echo profile",
+ ),
+ ],
+ ),
+ ),
+ )
+ ```
+
+ run: Options to use for the build and run actions.
+
+ Example:
+
+ ```starlark
+ xcodeproj(
+ ...
+ scheme_autogeneration_config = xcschemes.autogeneration_config(
+ run = xcschemes.autogeneration.run(
+ pre_actions = [
+ xcschemes.pre_post_actions.build_script(
+ title = "Build Start",
+ script_text = "echo build start",
+ ),
+ xcschemes.pre_post_actions.launch_script(
+ title = "Run Start",
+ script_text = "echo run",
+ ),
+ ],
+ ),
+ ),
+ )
+ ```
scheme_name_exclude_patterns: A `list` of regex patterns used to skip
creating matching autogenerated schemes.
@@ -1293,11 +1429,92 @@ def _autogeneration_config(*, scheme_name_exclude_patterns = None, test = None):
Returns:
An opaque value for the [`scheme_autogeneration_config`](xcodeproj-scheme_autogeneration_config) attribute of `xcodeproj`.
"""
- d = {}
+ d = {
+ "build_post_actions": [],
+ "build_pre_actions": [],
+ "build_run_post_actions_on_failure": [FALSE_ARG],
+ "profile_post_actions": [],
+ "profile_pre_actions": [],
+ "run_post_actions": [],
+ "run_pre_actions": [],
+ "scheme_name_exclude_patterns": [],
+ "test_options": ["", "", FALSE_ARG],
+ "test_post_actions": [],
+ "test_pre_actions": [],
+ }
+ if run:
+ d["build_run_post_actions_on_failure"] = [
+ run.run_build_post_actions_on_failure,
+ ]
+ d["build_post_actions"] = [
+ field
+ for action in run.post_actions
+ if action.for_build
+ for field in [
+ action.title,
+ action.script_text,
+ str(action.order) if action.order != None else "",
+ ]
+ ]
+ d["build_pre_actions"] = [
+ field
+ for action in run.pre_actions
+ if action.for_build
+ for field in [
+ action.title,
+ action.script_text,
+ str(action.order) if action.order != None else "",
+ ]
+ ]
+ d["run_post_actions"] = [
+ field
+ for action in run.post_actions
+ if not action.for_build
+ for field in [
+ action.title,
+ action.script_text,
+ str(action.order) if action.order != None else "",
+ ]
+ ]
+ d["run_pre_actions"] = [
+ field
+ for action in run.pre_actions
+ if not action.for_build
+ for field in [
+ action.title,
+ action.script_text,
+ str(action.order) if action.order != None else "",
+ ]
+ ]
+
+ for action_name, action in (
+ ("profile", profile),
+ ("test", test),
+ ):
+ if action:
+ d["{}_post_actions".format(action_name)] = [
+ field
+ for pre_post_action in action.post_actions
+ for field in [
+ pre_post_action.title,
+ pre_post_action.script_text,
+ str(pre_post_action.order) if pre_post_action.order != None else "",
+ ]
+ ]
+ d["{}_pre_actions".format(action_name)] = [
+ field
+ for pre_post_action in action.pre_actions
+ for field in [
+ pre_post_action.title,
+ pre_post_action.script_text,
+ str(pre_post_action.order) if pre_post_action.order != None else "",
+ ]
+ ]
+
if scheme_name_exclude_patterns:
d["scheme_name_exclude_patterns"] = scheme_name_exclude_patterns
- if test:
+ if test and test.test_options:
d["test_options"] = [
test.test_options.app_language or "",
test.test_options.app_region or "",
@@ -1311,6 +1528,8 @@ def _autogeneration_config(*, scheme_name_exclude_patterns = None, test = None):
xcschemes = struct(
arg = _arg,
autogeneration = struct(
+ profile = _autogeneration_profile,
+ run = _autogeneration_run,
test = _autogeneration_test,
),
autogeneration_config = _autogeneration_config,