Skip to content

Commit 156448c

Browse files
luispadronbrentleyjones
authored andcommitted
Add compile_only aspect
Adds an aspect that can be used to skip actions which are not compilation related. This is useful for warming projects in CI to maintain a Bazel remote cache for users to use. It is also useful when compilation actions are the only thing that need to be validated as it allows skipping potentially longer actions like bundling, signing, etc. Example usage: `bazel run //:LocalDevCacheWarming_xcodeproj -- 'build --aspects=//:compile_only_aspect.bzl%compile_only_aspect --output_groups=compiles $_GENERATOR_LABEL_'` Signed-off-by: Luis Padron <heyluispadron@gmail.com>
1 parent d80ed05 commit 156448c

File tree

2 files changed

+139
-0
lines changed

2 files changed

+139
-0
lines changed

docs/usage.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
- [Bazel aspects](#bazel-aspects)
2+
- [`compile_only_aspect`](#compile_only_aspect)
13
- [Bazel configs](#bazel-configs)
24
- [`rules_xcodeproj`](#rules_xcodeproj)
35
- [`rules_xcodeproj_generator`](#rules_xcodeproj_generator)
@@ -11,6 +13,41 @@
1113
- [Options](#options)
1214
- [Substitutions](#substitutions)
1315

16+
# Bazel aspects
17+
18+
## `compile_only_aspect`
19+
20+
The `compile_only_aspect` aspect is useful for skipping non-compilation
21+
related actions. For example, in CI it can be used to help with disk
22+
space usage by skipping actions that are typically not cached during
23+
a cache warming job. It can also be used to validate that the targets
24+
compile while skipping costly actions like bundling, signing, etc.
25+
26+
To use the aspect, you apply it at the command line:
27+
28+
```
29+
bazel build //some:target \
30+
--aspects=@rules_xcodeproj//xcodeproj:compile_only_aspect.bzl%compile_only_aspect \
31+
--output_groups=compile_only
32+
```
33+
34+
You can also create a Bazel configuration in a `.bazelrc` file to reuse the
35+
aspect easily:
36+
37+
```
38+
common:compile_only --aspects=@rules_xcodeproj//xcodeproj:compile_only_aspect.bzl%compile_only_aspect
39+
common:compile_only --output_groups=compile_only
40+
```
41+
42+
And use it, for example, with the command-line API:
43+
44+
```
45+
bazel run //label/to:xcodeproj \
46+
-- \
47+
--generator_output_groups=all_targets \
48+
'build --config=compile_only --remote_download_minimal'
49+
```
50+
1451
# Bazel configs
1552

1653
The way your project is generated, and the way Bazel builds it inside of Xcode,

xcodeproj/compile_only_aspect.bzl

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
"""
2+
Aspect that collects the outputs of all compile actions of a given build.
3+
Useful to be able to build and cache compile actions on CI, without having to
4+
link, bundle, or codesign as well (which we don't cache anyway).
5+
"""
6+
7+
_DOWNSTREAM_VALID_RULE_KINDS = {
8+
"apple_dynamic_framework_import": None,
9+
"apple_dynamic_xcframework_import": None,
10+
"apple_static_framework_import": None,
11+
"apple_static_xcframework_import": None,
12+
"cc_binary": None,
13+
"ios_app_clip": None,
14+
"ios_application": None,
15+
"ios_extension": None,
16+
"ios_framework": None,
17+
"ios_ui_test": None,
18+
"ios_unit_test": None,
19+
"swift_binary": None,
20+
"swift_test": None,
21+
"_ios_internal_ui_test_bundle": None,
22+
"_ios_internal_unit_test_bundle": None,
23+
"_precompiled_apple_resource_bundle": None,
24+
}
25+
26+
_SWIFT_LIBRARY_KINDS = [
27+
"swift_library",
28+
"swift_test",
29+
]
30+
31+
def _compile_only_aspect_impl(target, ctx):
32+
outs = []
33+
deps = []
34+
if ctx.rule.kind in _DOWNSTREAM_VALID_RULE_KINDS or CcInfo in target:
35+
if ctx.rule.kind in _SWIFT_LIBRARY_KINDS:
36+
for action in target.actions:
37+
if action.mnemonic == "SwiftCompile":
38+
outs = [action.outputs]
39+
break
40+
elif ctx.rule.kind == "objc_library":
41+
outs = [
42+
action.outputs
43+
for action in target.actions
44+
if action.mnemonic == "ObjcCompile"
45+
]
46+
deps = (
47+
getattr(ctx.rule.attr, "deps", []) +
48+
getattr(ctx.rule.attr, "implementation_deps", []) +
49+
getattr(ctx.rule.attr, "private_deps", [])
50+
)
51+
swift_target = getattr(ctx.rule.attr, "swift_target", None)
52+
if swift_target:
53+
deps.append(swift_target)
54+
clang_target = getattr(ctx.rule.attr, "clang_target", None)
55+
if clang_target:
56+
deps.append(clang_target)
57+
elif ctx.rule.kind == "test_suite":
58+
deps = ctx.rule.attr.tests
59+
elif ctx.rule.kind == "ios_build_test":
60+
deps = ctx.rule.attr.targets
61+
elif ctx.rule.kind == "xcodeproj":
62+
deps = (
63+
getattr(ctx.rule.attr, "top_level_device_targets", []) +
64+
getattr(ctx.rule.attr, "top_level_simulator_targets", [])
65+
)
66+
else:
67+
return []
68+
69+
for dep in deps:
70+
if OutputGroupInfo in dep and not hasattr(dep[OutputGroupInfo], "compiles"):
71+
fail(target, dep)
72+
73+
return [
74+
OutputGroupInfo(
75+
compiles = depset(
76+
transitive = outs + [
77+
dep[OutputGroupInfo].compiles
78+
for dep in deps
79+
if OutputGroupInfo in dep
80+
],
81+
),
82+
),
83+
]
84+
85+
compile_only_aspect = aspect(
86+
implementation = _compile_only_aspect_impl,
87+
attr_aspects = [
88+
"deps",
89+
"implementation_deps",
90+
"private_deps",
91+
# from `mixed_language_library`
92+
"clang_target",
93+
"swift_target",
94+
# from `test_suite`
95+
"targets",
96+
# from `*_build_test`
97+
"tests",
98+
# from `xcodeproj`
99+
"top_level_device_targets",
100+
"top_level_simulator_targets",
101+
],
102+
)

0 commit comments

Comments
 (0)