Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tool executables (from FilesToRunProvider) to action inputs. #10110

Conversation

jmillikin
Copy link
Contributor

Fixes #7390

Summary: rules are allowed to set DefaultInfo(files = depset(), executable = ...). If the resulting targets are used as tool inputs to another target, the executable will not be added to the downstream targets' action inputs. This manifests as either an execvp() error (from ctx.actions.run()) or a Bash error in genrules.

This PR fixes that so the use case of non-default executables works as expected. This is useful for building scripts with complex runfiles, where bazel run //:something and bazel build //:something && bazel-bin/something cannot be supported by the same output file.

cc @ccate @jmmv

@jmillikin jmillikin force-pushed the add-tool-executables-to-action-inputs branch from b4991d2 to ca0e703 Compare October 26, 2019 10:55
@irengrig irengrig added the team-Rules-Server Issues for serverside rules included with Bazel label Oct 28, 2019
@irengrig
Copy link
Contributor

/cc @lberki I am not sure about the team label here

@lberki lberki added team-Core Skyframe, bazel query, BEP, options parsing, bazelrc and removed team-Rules-Server Issues for serverside rules included with Bazel labels Oct 29, 2019
@lberki lberki assigned laurentlb and lberki and unassigned hlopko Oct 29, 2019
@lberki
Copy link
Contributor

lberki commented Oct 29, 2019

Well, that's an interesting one. I didn't expect that we allow the creation of a FilesToRunProvider where the executable is not in getFiles().

Since we use FilesToRunProvider in many places other than CommandHelper, I think this pull request only fixes a part of the problem. I see two viable solutions:

  1. Make it an error to create a FilesToRunProvider where the executable is not in files. This would be an incompatible change and it appears that there are places where this is used.
  2. Just silently add the executable to FilesToRunProvider#getFiles() when it's not already there. This would cause a minor amount of RAM wastage.

I think the best approach is to do something halfway between: do (1) for call sites from Java and (2) for call sites from Starlark.

@laurentlb , WDYT?

@benjaminp
Copy link
Collaborator

A similar situation happens with the presence of the executable in the runfiles. That case is handled with an implementation similar to what lberki suggests here. Broken native rules receive an IllegalStateException. Starlark rules typically implicitly have the executable added to their runfiles or receive a Starlark-level error in some more obscure cases (at least since #6607 fixed the Java-level crash leaking through).

@laurentlb laurentlb requested a review from c-parsons October 30, 2019 18:16
@jmillikin
Copy link
Contributor Author

New commit added to implement "Just silently add the executable to FilesToRunProvider#getFiles() when it's not already there." -- there seems to be only one place in the code where FilesToRunProvider can get constructed with that condition, and I couldn't figure out how to tell whether the caller is from Starlark or Java.

@@ -44,6 +44,9 @@ public FilesToRunProvider(
NestedSet<Artifact> filesToRun,
@Nullable RunfilesSupport runfilesSupport,
@Nullable Artifact executable) {
if (executable != null) {
filesToRun = NestedSetBuilder.fromNestedSet(filesToRun).add(executable).build();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will create an extra NestedSet instance for every call site, most importantly, in the constructor of FileConfiguredTarget, which means an extra object for each input file, which is probably unacceptable overhead.

How about special-casing at least #fromSingleExecutableArtifact() so that this overhead is not present?

Given that the only other call site is in RuleConfiguredTargetBuilder#build(), I'd prefer putting logic there because, well, that place already has logic. In addition to being a personal preference, you also have more information, notably, ruleContext.getRule().getRuleClassObject().isSkylark(), which you can use to determine whether this is a Starlark rule and thus adding the binary to the nested set is necessary.

Even better is changing SkylarkRuleConfiguredTargetUtil#addSimpleProviders() -- that place has control over both the executable and filesToBuildfields ofRuleConfiguredTargetBuilderand is specific to Starlark rules, so you can just add the "executable is forcibly added tofilesToRun`" logic there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that the only other call site is in RuleConfiguredTargetBuilder#build(), I'd prefer putting logic there because, well, that place already has logic.

Done.

@lberki
Copy link
Contributor

lberki commented Nov 4, 2019

Added a comment -- TL;DR: I think it's better to tweak SkylarkRuleConfiguredTargetUtil#addSimpleProviders() because that's specific to Starlark.

What I'm slightly unhappy about is that you probably can't just assert whether the executable is in filesToRun for Java rules because you'd need to iterate over the contents of the latter nested set just for error checking, which sounds wasteful.

@jmillikin
Copy link
Contributor Author

addSimpleProviders() seems to be used only for constructing filesToBuild, but this change is about adding the executable to filesToRun.

I've expanded RuleConfiguredTargetBuilder::build() (via buildFilesToRun()) to inject the executable. It's applied to all rules, since as you note it's difficult to figure out whether an Artifact is present in a NestedSet without iterating over it.

@jmillikin
Copy link
Contributor Author

Gentle ping @lberki

@philwo
Copy link
Member

philwo commented Nov 13, 2019

@lberki As requested, here's your ping. :) Please let me know if I can import and cherry-pick this into Bazel 1.2.0.

Copy link
Contributor

@lberki lberki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry for the delay. I've had a little too many issues that landed on my desk in the last few days.

@bazel-io bazel-io closed this in ceadf0a Nov 13, 2019
philwo pushed a commit that referenced this pull request Nov 14, 2019
Fixes #7390

Summary: rules are allowed to set `DefaultInfo(files = depset(), executable = ...)`. If the resulting targets are used as tool inputs to another target, the executable will not be added to the downstream targets' action inputs. This manifests as either an `execvp()` error (from `ctx.actions.run()`) or a Bash error in genrules.

This PR fixes that so the use case of non-default executables works as expected. This is useful for building scripts with complex runfiles, where `bazel run //:something` and `bazel build //:something && bazel-bin/something` cannot be supported by the same output file.

cc @ccate @jmmv

Closes #10110.

PiperOrigin-RevId: 280170524
philwo pushed a commit that referenced this pull request Nov 20, 2019
Fixes #7390

Summary: rules are allowed to set `DefaultInfo(files = depset(), executable = ...)`. If the resulting targets are used as tool inputs to another target, the executable will not be added to the downstream targets' action inputs. This manifests as either an `execvp()` error (from `ctx.actions.run()`) or a Bash error in genrules.

This PR fixes that so the use case of non-default executables works as expected. This is useful for building scripts with complex runfiles, where `bazel run //:something` and `bazel build //:something && bazel-bin/something` cannot be supported by the same output file.

cc @ccate @jmmv

Closes #10110.

PiperOrigin-RevId: 280170524
philwo pushed a commit that referenced this pull request Nov 25, 2019
Fixes #7390

Summary: rules are allowed to set `DefaultInfo(files = depset(), executable = ...)`. If the resulting targets are used as tool inputs to another target, the executable will not be added to the downstream targets' action inputs. This manifests as either an `execvp()` error (from `ctx.actions.run()`) or a Bash error in genrules.

This PR fixes that so the use case of non-default executables works as expected. This is useful for building scripts with complex runfiles, where `bazel run //:something` and `bazel build //:something && bazel-bin/something` cannot be supported by the same output file.

cc @ccate @jmmv

Closes #10110.

PiperOrigin-RevId: 280170524
jheaff1 added a commit to jheaff1/rules_foreign_cc that referenced this pull request Jul 19, 2022
Previously, tool runfiles were not present at execution time. For
example, if the provided tool was a py_binary, the py_binary would fail
to run due to the omission of the runfiles.

This relates to bazelbuild/bazel#10110
jheaff1 added a commit to jheaff1/rules_foreign_cc that referenced this pull request Jul 19, 2022
Previously, tool runfiles were not present at execution time. For
example, if the provided tool was a py_binary, the py_binary would fail
to run due to the omission of the runfiles.

This relates to bazelbuild/bazel#10110
jheaff1 added a commit to jheaff1/rules_foreign_cc that referenced this pull request Jul 19, 2022
Previously, tool runfiles were not present at execution time. For
example, if the provided tool was a py_binary, the py_binary would fail
to run due to the omission of the runfiles.

This relates to bazelbuild/bazel#10110
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes team-Core Skyframe, bazel query, BEP, options parsing, bazelrc
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Host executable with non-empty DefaultInfo files missing from sandbox
8 participants