Questions about validation actions and aspects

167 views
Skip to first unread message

Konstantin

unread,
May 29, 2023, 11:10:34 PM5/29/23
to bazel-discuss
Hello,

Our build needs to take the list of requested targets and besides building those we need to run rather complex "binplace" rule on each target which copies given target output files to the required folder structure.

This "smells" like a job for validation actions with or without aspect. My first attempts to make validation work are here and they are not successful. Looks like I need some help making validation actions work.

Also I have some related questions:
1. Our binplace rule is rather complex and may end up generating multiple actions for each input target. Can I have "validation target" which depends on the artifact generating target OR validation actions must be part of the same target? We don't want to pollute C++ cc_binary (for example) with some highly specialized actions and rather do it externally to the artifact rules.

2. I noticed new flag experimental_use_validation_aspect but cannot find what it's doing or how to use it. Curious.

3. In this mailing list I found older discussion which seems to touch similar problem. Is it still actual?

Any help?
Thank you!
Konstantin

Konstantin

unread,
May 30, 2023, 9:04:34 AM5/30/23
to bazel-discuss
And one more:
4. When I supply my artifact generating actions with the validation actions - does it mean that no matter which targets are actually requested all validated targets will behave like they are requested? Or what is actually requested still can change it somehow and we can build less than everything which is validated?

Alex Humesky

unread,
May 30, 2023, 7:43:30 PM5/30/23
to Konstantin, bazel-discuss
On Monday, May 29, 2023 at 8:10:34 PM UTC-7 Konstantin wrote:
Hello,

Our build needs to take the list of requested targets and besides building those we need to run rather complex "binplace" rule on each target which copies given target output files to the required folder structure.

This "smells" like a job for validation actions with or without aspect. My first attempts to make validation work are here and they are not successful. Looks like I need some help making validation actions work.

Thanks for the minimal repro, it's helpful.

The issue in validation_action is here:

The validation output group takes the outputs of validation actions, so that should be "binplaced_fileb" instead of "dst_file".

The issue in validation_target is that the macro creates the two targets, but there is no connection from the cc_library target to the mock_binplace target, so when you ask bazel to build the cc_library target, it ignores the mock_binplace target (by design).

                                          bazel build foo
mock_binplace(name = "foo_binplace")           |
                    \                          v
                     \---------------> cc_library(name = "foo")
                                               |
                                               v
                                             foo.cpp


The key thing is that validation actions are run only when the target that created them is at least analyzed*.

This actually creates a problem for your use case, because that would mean that the cc_library target has to depend on the mock_binplace target so that it will get run, but the mock_binplace target has to depend on the cc_library target so that it can copy its outputs -- and now it's a cycle.
 
(*Validation actions from a target will run even if no other actions from that target are run, which was originally by design, but in the end this has caused some problems with running too many validation actions -- e.g. when a target only wants to get some information about another target via a provider, that other target's validation actions will run, which causes some things to take longer / execute more than might be expected. The bazel team is considering how to improve this.)

 

Also I have some related questions:
1. Our binplace rule is rather complex and may end up generating multiple actions for each input target. Can I have "validation target" which depends on the artifact generating target OR validation actions must be part of the same target? We don't want to pollute C++ cc_binary (for example) with some highly specialized actions and rather do it externally to the artifact rules.

As above, if you have a separate validation target, then it's tricky to actually trigger the validation target. Validation actions were basically intended to be put inside the target that creates the artifact to be validated. But the aspect approach below should work.
 

2. I noticed new flag experimental_use_validation_aspect but cannot find what it's doing or how to use it. Curious.

We noticed that tests would not start executing with `bazel test` until all validation actions had finished running, which wasn't actually really necessary, so that flag is a workaround which allows the tests to run before the validation actions are complete.
 

3. In this mailing list I found older discussion which seems to touch similar problem. Is it still actual?

I was going to suggest to adapt this exact example. I think it should work for your case. I don't think you actually need to use validation actions / the _validation output group for this, you could simply add --output_groups=+your_output_group_name to your bazelrc along with the --aspects flag.

The "you'll have to do that manually" note is referring to the "for d in ctx.rule.attr.deps" bit of code.

On Tue, May 30, 2023 at 9:04 AM Konstantin <kon...@ermank.com> wrote:
And one more:
4. When I supply my artifact generating actions with the validation actions - does it mean that no matter which targets are actually requested all validated targets will behave like they are requested? Or what is actually requested still can change it somehow and we can build less than everything which is validated?

The latter: bazel starts from the targets that are requested, and analyzes (i.e. runs the implementation functions of rules) all the targets that are needed to build the top-level targets. Validation actions will run for any target that was analyzed (even if nothing else from that target is needed to build the top-level targets, see note above).
 

Any help?
Thank you!
Konstantin

--
You received this message because you are subscribed to the Google Groups "bazel-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bazel-discus...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/bazel-discuss/ac6e51c7-0102-4738-8fcd-1ef6f0a11138n%40googlegroups.com.

Konstantin

unread,
May 31, 2023, 12:40:42 PM5/31/23
to bazel-discuss
Alex, thank you for the elaborate answer!

I have experience with Aspects, but never applied those from the command line.
Question: if we request multiple targets and they have some common dependencies - will the Aspect implementation function be invoked only once for each dependency?

Konstantin Erman

unread,
Jun 3, 2023, 6:27:29 PM6/3/23
to Alex Humesky, bazel-discuss

I got it working, but let me add one more twist to the problem.

 

Our current binplace rule is instantiated just once and points at its source targets with transition, like this:

binplace_rule = rule(

    implementation = _binplace_rule_impl,

    attrs = {

        "srcs": attr.label(cfg = binplace_transition, providers = [SubsetInfo, TransitiveClosureInfo]),

 

This transition is one to many and it allows us to build multiple platforms/flavors in one Bazel invocation

 

Now instead of a single binplace target we are going to have many instances of binplace aspect where each one supposed to binplace one target.

It is all great, but how do we apply transition to aspects?

Aspects don’t get the target to work on as the attribute, instead we just get it as the first parameter of _binplace_aspect_impl(target, ctx):

I don’t understand how to make aspect to follow different flavors of the given targets.

Alex Humesky

unread,
Jun 5, 2023, 6:22:23 PM6/5/23
to Konstantin, bazel-discuss
On Wed, May 31, 2023 at 12:40 PM Konstantin <kon...@ermank.com> wrote:
Alex, thank you for the elaborate answer!

I have experience with Aspects, but never applied those from the command line.
Question: if we request multiple targets and they have some common dependencies - will the Aspect implementation function be invoked only once for each dependency?

I believe the aspect would run only once for each dependency.

There are ways to have the same aspect run multiple times with different parameters though:

Alex Humesky

unread,
Jun 5, 2023, 9:37:47 PM6/5/23
to Konstantin Erman, bazel-discuss
I believe that this should work -- if you add aspects = [aspect_name] to the srcs attribute, the aspect should be executed on each branch of the one-to-many ("split") transition

Konstantin Erman

unread,
Jun 6, 2023, 11:56:11 PM6/6/23
to Alex Humesky, bazel-discuss

Aspects in Bazel are mind bending.

I don’t understand why aspects parameters are needed when all aspects have access to all attributes of the rules they traverse through ctx.rule.attr?

 

From: Alex Humesky <ahum...@google.com>
Sent: Monday, June 5, 2023 3:22 PM
To: Konstantin Erman <kon...@ermank.com>
Cc: bazel-discuss <bazel-...@googlegroups.com>
Subject: Re: [bazel-discuss] Re: Questions about validation actions and aspects

 

 

 

On Wed, May 31, 2023 at 12:40 PM Konstantin <kon...@ermank.com> wrote:

محمدعبدالله محمد العولقي

unread,
Jun 7, 2023, 5:04:14 PM6/7/23
to Konstantin Erman, Alex Humesky, bazel-discuss

Alex Humesky

unread,
Jun 7, 2023, 5:13:13 PM6/7/23
to Konstantin Erman, bazel-discuss
Could you talk a bit more about where you're seeing you need aspect parameters?
The example in https://groups.google.com/g/bazel-discuss/c/TCo0msh1My4/m/V3ET0eXKAwAJ didn't use parameters I think

Konstantin Erman

unread,
Jun 7, 2023, 5:29:58 PM6/7/23
to Alex Humesky, bazel-discuss

No – no, I don’t need aspect parameters or at least I am not aware of it yet 😊

 

 

From that document I learned about aspect parameters and got confused why they are even needed, but that interest at the moment is purely theoretical.

If you have time a short explanation would be nice, but it is very low priority request. Nothing is blocked on it.

Alex Humesky

unread,
Jun 7, 2023, 6:57:05 PM6/7/23
to Konstantin Erman, bazel-discuss
Gotcha, a more general question, thanks for clarifying.
So the key thing is that the parameters come from the attributes of the target that originates the aspect, and those parameters are propagated with the aspect. Importantly, this means that there could be multiple versions of the same aspect traversing the same set of targets.

So if you have

foo_binary(
  name = "A",
  deps = [":C"],
  value = 123,
)

foo_binary(
  name = "B",
  deps = [":C"],
  value = 456,
)

foo_library(
  name = "C",
)

and if foo_binary has an aspect that takes "value" as a parameter, then foo_library C will have the aspect applied twice: one with value = 123, and another with value = 456.
This is one of the few places where information can go "down" the graph (as opposed to the typical situation where providers go "up" the graph) ("few places" because of the scalability issues -- for every "foo_binary" target with a different value of "value", there's a "shadow graph" of aspect targets based on C -- and if C is not just 1 target but a large number of target and their dependencies, that can grow large quickly)

Konstantin Erman

unread,
Jun 14, 2023, 11:01:29 PM6/14/23
to Alex Humesky, bazel-discuss

Wow, I am glad I asked – it is something very new to me.

Could you add a sample aspect implementation to make it more clear?

The whole idea that the aspect can go “in reverse” to account for the existence of some rules is very novel to me.

Can the parameter be simply “name”?

Konstantin Erman

unread,
Jun 15, 2023, 5:05:15 PM6/15/23
to Alex Humesky, bazel-discuss

I consider the following design for binplace, but cannot see it through to the end.

 

Target B depends on target A. Each target T has a corresponding “T_bin” target which binplaces it. Instead of coding “B depends on A” I am going to code “B depends on A_bin”.

Each binplace target just passes its source targets with all their providers to the DefaultInfo. Also each binplace target defines _validation action which does actual binplacing – copying the sources to the correct place. Those copies in the correct place are bundled to the _validation output group of the binplace target. Example:

 

cc_library(

    name = "A",

    # ....

)

 

binplace(

    name = "A_bin",

    srcs = ["A"],

)

 

cc_library(

    name = "B",

    deps = ["A_bin"],

    # ....

)

 

binplace(

    name = "B_bin",

    srcs = ["B"],

)

 

 

In this example when we request B we get both A and B built and binplaced.

 

Now the part I struggle with: build has a flag “target_platforms” which could be set for example to “win64, win32, asmjs”. We want one to many transition to build all three target platforms in parallel. I wonder how to wire up this transition to the above design, so that when I do “bazel build B –target_platforms=win64,win32,asmjs” both A and B get built and binplaced for each of the three specified platforms.

 

One possible solution is for each module (A and B) create “_req” target which would build that module with transition, like this:

 

requestable_target(

    name = "A_req",

    srcs = ["A_bin"],

)

 

requestable_target = rule(

    implementation = _impl,

    attrs = {

        "srcs": attr.label_list(cfg = platforms_transition),

    },

)

 

Binplace location is not per-configuration, it is just one folder structure under the default (root) configuration folder. In the existing design we have single binplace rule (not one per module) which does the transition and therefore knows where the binplace folder is and what configuration specific folders it should pick the binaries from.

With the design above each binplace rule would be invoked  for each platform individually and would not know where the central platform independed binplace location is. We need to provide them with this information somehow, i.e. send information from the root of the graph to the dependencies, which is unusual for Bazel.

 

Also it is ugly to create extra targets “_req” for each module.

 

How can we design it better?

 

Konstantin

Reply all
Reply to author
Forward
0 new messages