@@ -35,6 +35,8 @@ def _find_rustfmtable_srcs(crate_info, aspect_ctx = None):
3535 list: A list of formattable sources (`File`).
3636 """
3737
38+ crate_srcs = crate_info .srcs
39+
3840 # Targets with specific tags will not be formatted
3941 if aspect_ctx :
4042 ignore_tags = [
@@ -47,8 +49,10 @@ def _find_rustfmtable_srcs(crate_info, aspect_ctx = None):
4749 if tag .replace ("-" , "_" ).lower () in ignore_tags :
4850 return []
4951
52+ crate_srcs = depset (getattr (aspect_ctx .rule .files , "srcs" , []), transitive = [crate_info .srcs ])
53+
5054 # Filter out any generated files
51- srcs = [src for src in crate_info . srcs .to_list () if src .is_source ]
55+ srcs = [src for src in crate_srcs .to_list () if src .is_source ]
5256
5357 return srcs
5458
@@ -97,27 +101,55 @@ def _perform_check(edition, srcs, ctx):
97101
98102 return marker
99103
104+ RustfmtTargetInfo = provider (
105+ doc = "A provider containing rustfmt formattable sources for a target." ,
106+ fields = {
107+ "edition" : "str: The Rust edition of the target." ,
108+ "srcs" : "list[File]: The formattable sources." ,
109+ },
110+ )
111+
112+ def _rustfmt_srcs_aspect_impl (target , ctx ):
113+ crate_info = _get_rustfmt_ready_crate_info (target )
114+
115+ if not crate_info :
116+ return []
117+
118+ srcs = _find_rustfmtable_srcs (crate_info , ctx )
119+
120+ return [
121+ RustfmtTargetInfo (
122+ srcs = srcs ,
123+ edition = crate_info .edition ,
124+ ),
125+ ]
126+
127+ rustfmt_srcs_aspect = aspect (
128+ implementation = _rustfmt_srcs_aspect_impl ,
129+ doc = "This aspect collects formattable sources from a Rust target." ,
130+ required_providers = [
131+ [rust_common .crate_info ],
132+ [rust_common .test_crate_info ],
133+ ],
134+ fragments = ["cpp" ],
135+ )
136+
100137def _rustfmt_aspect_impl (target , ctx ):
101138 # Exit early if a target already has a rustfmt output group. This
102139 # can be useful for rules which always want to inhibit rustfmt.
103140 if OutputGroupInfo in target :
104141 if hasattr (target [OutputGroupInfo ], "rustfmt_checks" ):
105142 return []
106143
107- crate_info = _get_rustfmt_ready_crate_info (target )
108-
109- if not crate_info :
144+ if RustfmtTargetInfo not in target :
110145 return []
111146
112- srcs = _find_rustfmtable_srcs ( crate_info , ctx )
147+ info = target [ RustfmtTargetInfo ]
113148
114- # If there are no formattable sources, do nothing.
115- if not srcs :
149+ if not info .srcs :
116150 return []
117151
118- edition = crate_info .edition
119-
120- marker = _perform_check (edition , srcs , ctx )
152+ marker = _perform_check (info .edition , info .srcs , ctx )
121153
122154 return [
123155 OutputGroupInfo (
@@ -160,48 +192,51 @@ generated source files are also ignored by this aspect.
160192 [rust_common .crate_info ],
161193 [rust_common .test_crate_info ],
162194 ],
195+ requires = [rustfmt_srcs_aspect ],
163196 fragments = ["cpp" ],
164197 toolchains = [
165198 str (Label ("//rust/rustfmt:toolchain_type" )),
166199 ],
167200)
168201
169- def _rustfmt_test_manifest_aspect_impl (target , ctx ):
170- crate_info = _get_rustfmt_ready_crate_info (target )
202+ _RustfmtTestManifestInfo = provider (
203+ doc = "A container for rustfmt manifest info to use in `rustfmt_test`" ,
204+ fields = {
205+ "manifest" : "File: The manifest of formattable sources." ,
206+ "srcs" : "depset[File]: The formattable sources." ,
207+ },
208+ )
171209
172- if not crate_info :
210+ def _rustfmt_test_target_aspect_impl (target , ctx ):
211+ if RustfmtTargetInfo not in target :
173212 return []
174213
175- # Parse the edition to use for formatting from the target
176- edition = crate_info .edition
177-
178- srcs = _find_rustfmtable_srcs (crate_info , ctx )
179- manifest = _generate_manifest (edition , srcs , ctx )
214+ info = target [RustfmtTargetInfo ]
215+ manifest = _generate_manifest (info .edition , info .srcs , ctx )
180216
181217 return [
182- OutputGroupInfo (
183- rustfmt_manifest = depset ([manifest ]),
218+ _RustfmtTestManifestInfo (
219+ manifest = manifest ,
220+ srcs = depset (info .srcs ),
184221 ),
185222 ]
186223
187- # This aspect contains functionality split out of `rustfmt_aspect` which broke when
188- # `required_providers` was added to it. Aspects which have `required_providers` seems
189- # to not function with attributes that also require providers.
190- _rustfmt_test_manifest_aspect = aspect (
191- implementation = _rustfmt_test_manifest_aspect_impl ,
192- doc = """\
193- This aspect is used to gather information about a crate for use in `rustfmt_test`
194-
195- Output Groups:
196-
197- - `rustfmt_manifest`: A manifest used by rustfmt binaries to provide crate specific settings.
198- """ ,
224+ _rustfmt_test_target_aspect = aspect (
225+ implementation = _rustfmt_test_target_aspect_impl ,
226+ doc = """This aspect is used to gather information about a crate for use in `rustfmt_test`""" ,
227+ requires = [rustfmt_srcs_aspect ],
199228 fragments = ["cpp" ],
200229 toolchains = [
201230 str (Label ("//rust/rustfmt:toolchain_type" )),
202231 ],
203232)
204233
234+ def _rlocationpath (file , workspace_name ):
235+ if file .short_path .startswith ("../" ):
236+ return file .short_path [len ("../" ):]
237+
238+ return "{}/{}" .format (workspace_name , file .short_path )
239+
205240def _rustfmt_test_impl (ctx ):
206241 # The executable of a test target must be the output of an action in
207242 # the rule implementation. This file is simply a symlink to the real
@@ -218,24 +253,23 @@ def _rustfmt_test_impl(ctx):
218253 is_executable = True ,
219254 )
220255
221- crate_infos = [_get_rustfmt_ready_crate_info (target ) for target in ctx .attr .targets ]
222- srcs = [depset (_find_rustfmtable_srcs (crate_info )) for crate_info in crate_infos if crate_info ]
223-
224- # Some targets may be included in tests but tagged as "no-format". In this
225- # case, there will be no manifest.
226- manifests = [getattr (target [OutputGroupInfo ], "rustfmt_manifest" , None ) for target in ctx .attr .targets ]
227- manifests = depset (transitive = [manifest for manifest in manifests if manifest ])
256+ srcs = []
257+ manifests = []
258+ for target in ctx .attr .targets :
259+ if _RustfmtTestManifestInfo not in target :
260+ continue
261+ info = target [_RustfmtTestManifestInfo ]
262+ manifests .append (info .manifest )
263+ srcs .append (info .srcs )
228264
229265 runfiles = ctx .runfiles (
230- transitive_files = depset (transitive = srcs + [ manifests ] ),
266+ transitive_files = depset (manifests , transitive = srcs ),
231267 )
232268
233269 runfiles = runfiles .merge (
234270 ctx .attr ._runner [DefaultInfo ].default_runfiles ,
235271 )
236272
237- workspace = ctx .label .workspace_name or ctx .workspace_name
238-
239273 return [
240274 DefaultInfo (
241275 files = depset ([runner ]),
@@ -245,8 +279,8 @@ def _rustfmt_test_impl(ctx):
245279 RunEnvironmentInfo (
246280 environment = {
247281 "RUSTFMT_MANIFESTS" : ctx .configuration .host_path_separator .join ([
248- workspace + "/" + manifest . short_path
249- for manifest in sorted ( manifests . to_list ())
282+ _rlocationpath ( manifest , ctx . workspace_name )
283+ for manifest in manifests
250284 ]),
251285 "RUST_BACKTRACE" : "1" ,
252286 },
@@ -263,7 +297,7 @@ rustfmt_test = rule(
263297 [rust_common .crate_info ],
264298 [rust_common .test_crate_info ],
265299 ],
266- aspects = [_rustfmt_test_manifest_aspect ],
300+ aspects = [_rustfmt_test_target_aspect ],
267301 ),
268302 "_runner" : attr .label (
269303 doc = "The rustfmt test runner" ,
0 commit comments