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

Support buildfiles function for external repositories in Bazel query #14497

Closed
wants to merge 1 commit into from

Conversation

linzhp
Copy link
Contributor

@linzhp linzhp commented Dec 28, 2021

All repository rules are in the package //external, which is different from other packages. Calling Package.getBuildFile() returns the WORKSPACE file and Package.getStarlarkFileDependencies() returns empty. A possible fix is to have the graph keep track of all Starlark files loaded by WORKSPACE, so we can treat the package //external in the same way as other packages during query execution. However, this means that evaluating buildfiles(//external:foo) is the same as evaluating buildfiles(//external:*), which would include Starlark files not related to the repository @foo.

This PR takes a different approach by using the call stack of an external repository to get the precise set of Starlark files needed by //external:foo.

Fixed #14280

@aiuto aiuto requested a review from zhengwei143 January 20, 2022 01:52
@aiuto aiuto requested a review from janakdr January 20, 2022 01:53
@aiuto
Copy link
Contributor

aiuto commented Jan 20, 2022

@zhengwei143 You've been in this code more than most other people. Can you take a look.

Copy link
Contributor

@janakdr janakdr left a comment

Choose a reason for hiding this comment

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

Leaving comments on overall design and utility to @zhengwei143, just commenting on performance and bugs.

Package pkg = x.getPackage();
if (seenPackages.add(pkg.getPackageIdentifier())) {
if (x instanceof Rule && pkg.getPackageIdentifier().getCanonicalForm().equals("//external")) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please use LabelConstants#EXTERNAL_PACKAGE_IDENTIFIER. PackageIdentifiers are interned, so this check becomes instant, as opposed to potentially a character-by-character String equality check (depending on how the JVM canonicalizes string constants).

.toList();
for (CallStackEntry entry : callStackEntries) {
PathFragment extFile = PathFragment.create(entry.location.file());
PathFragment workspaceRoot = pkg.getPackageDirectory().asFragment();
Copy link
Contributor

Choose a reason for hiding this comment

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

Please pull this out of the for loop so you're not doing the same work on each iteration.

// External repository rules
if (seenRepos.add(x.getName())) {
ImmutableList<CallStackEntry> callStackEntries = x.getAssociatedRule().getCallStack()
.toList();
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: if this is a performance issue, could provide an iterator from CallStack, since you don't need anything else about the immutable list here.

for (CallStackEntry entry : callStackEntries) {
PathFragment extFile = PathFragment.create(entry.location.file());
PathFragment workspaceRoot = pkg.getPackageDirectory().asFragment();
if (extFile.startsWith(workspaceRoot)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

when does this check fail?

Copy link
Contributor Author

@linzhp linzhp Jan 22, 2022

Choose a reason for hiding this comment

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

If the extension file is in an external repo, then this check will fail. For example, if we have this in WORKSPACE:

load("@io_bazel_rules_go//go:deps.bzl", "go_rules_dependencies")

go_rules_dependencies()

Then the location of the extension file for the target //external:platforms (which is one of the repos loaded by go_rule_dependencies()) will be in somewhere like /private/var/tmp/_bazel_zplin/db67431f618bf23018777556a2edc2eb/external/io_bazel_rules_go/go/private/repositories.bzl, which is outside the workspace root.

Copy link
Contributor

Choose a reason for hiding this comment

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

Wouldn't that external bzl file be a valid result if buildfiles returned it, though? I can imagine a user being surprised that only bzl files in the main repository are returned.

ImmutableList<CallStackEntry> callStackEntries = x.getAssociatedRule().getCallStack()
.toList();
for (CallStackEntry entry : callStackEntries) {
PathFragment extFile = PathFragment.create(entry.location.file());
Copy link
Contributor

Choose a reason for hiding this comment

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

couldn't there be duplicates here? Please add a test that covers this situation (the bzl file calls another function in the same file)

if (extFile.startsWith(workspaceRoot)) {
try {
Label extension = Label.create(
extFile.relativeTo(workspaceRoot).getParentDirectory().getPathString(),
Copy link
Contributor

Choose a reason for hiding this comment

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

is this construction true? A bzl file can be in a subdirectory of a package. Please add a test for this situation.

' name = "io_bazel_rules_go",',
' sha256 = "2b1641428dff9018f9e85c0384f03ec6c10660d935b750e3fa1492a281a53b0f",',
' urls = [',
' "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.29.0/rules_go-v0.29.0.zip",',
Copy link
Contributor

Choose a reason for hiding this comment

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

Please change test to not require network access.

Package pkg = x.getPackage();
if (seenPackages.add(pkg.getPackageIdentifier())) {
if (x instanceof Rule && pkg.getPackageIdentifier().getCanonicalForm().equals("//external")) {
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 still be incomplete if you have a non-Rule target in a Package, right? You can get a situation with exports_files([...]) or just if the query restricts the output to source files or output files. The package will still be loaded and require those bzl files to work correctly (and since buildfiles is often used to check if a package needs to be reloaded, that's important), but those bzl files won't be listed here. That makes me think you're doing this at a fundamentally wrong place, although I don't have a better one to suggest since I'm not familiar with external repos. In any case, please add a test for these situations: querying for a source file exported via exports_files([...]); querying for an output file (the output of a genrule, not the genrule itself); and a query that filters for source files (kind('source_file',...)).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a special case for //external. The only non-rule target there is WORKSPACE. For example, from Bazel's own repo:

bazel query 'kind("source file", //external:all-targets)'
//external:WORKSPACE

For genrule and exports_files in external repos, their labels are like @some_repo//:data.txt, which is not in the //external package, and out of scope of this pull request. I can write tests to cover them, but their behavior in buildfiles is not affected by this pull request.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, I see. Thanks, you can disregard this comment in that case.

@linzhp
Copy link
Contributor Author

linzhp commented Jan 24, 2022

That makes me think you're doing this at a fundamentally wrong place

@janakdr I started to think that you may be right about this. So I implement another approach in #14630. Can you take a look?

@linzhp
Copy link
Contributor Author

linzhp commented Jan 24, 2022

Closing in favor of #14630

@linzhp linzhp closed this Jan 24, 2022
bazel-io pushed a commit that referenced this pull request Jan 25, 2022
This is an alternative approach to fix #14280. It adds transitive closure of Starlark dependencies to `//external` package when loading `WORKSPACE` file, so it can be processed in the same way as `BUILD` files during query execution. Comparing to the approach taken in #14497, this approach is less intrusive, but not able to distinguish the extension files needed by `//external:foo` and `//external:bar`, meaning `buildfiles(//external:foo)` returns the same result as `buildfiles(//external:*)`. However, this behavior is consistent with other packages. For example, `buildfiles(//foo:bar)` has the same result as `buildfiles(//foo:*)`.

Closes #14630.

PiperOrigin-RevId: 424092916
@linzhp linzhp deleted the buildfiles branch January 25, 2022 18:10
brentleyjones pushed a commit to brentleyjones/bazel that referenced this pull request Mar 7, 2022
This is an alternative approach to fix bazelbuild#14280. It adds transitive closure of Starlark dependencies to `//external` package when loading `WORKSPACE` file, so it can be processed in the same way as `BUILD` files during query execution. Comparing to the approach taken in bazelbuild#14497, this approach is less intrusive, but not able to distinguish the extension files needed by `//external:foo` and `//external:bar`, meaning `buildfiles(//external:foo)` returns the same result as `buildfiles(//external:*)`. However, this behavior is consistent with other packages. For example, `buildfiles(//foo:bar)` has the same result as `buildfiles(//foo:*)`.

Closes bazelbuild#14630.

PiperOrigin-RevId: 424092916
(cherry picked from commit a6c3f23)
brentleyjones pushed a commit to brentleyjones/bazel that referenced this pull request Mar 7, 2022
This is an alternative approach to fix bazelbuild#14280. It adds transitive closure of Starlark dependencies to `//external` package when loading `WORKSPACE` file, so it can be processed in the same way as `BUILD` files during query execution. Comparing to the approach taken in bazelbuild#14497, this approach is less intrusive, but not able to distinguish the extension files needed by `//external:foo` and `//external:bar`, meaning `buildfiles(//external:foo)` returns the same result as `buildfiles(//external:*)`. However, this behavior is consistent with other packages. For example, `buildfiles(//foo:bar)` has the same result as `buildfiles(//foo:*)`.

Closes bazelbuild#14630.

PiperOrigin-RevId: 424092916
(cherry picked from commit a6c3f23)
Wyverald pushed a commit that referenced this pull request Mar 8, 2022
* Adding Starlark dependencies to the package //external

This is an alternative approach to fix #14280. It adds transitive closure of Starlark dependencies to `//external` package when loading `WORKSPACE` file, so it can be processed in the same way as `BUILD` files during query execution. Comparing to the approach taken in #14497, this approach is less intrusive, but not able to distinguish the extension files needed by `//external:foo` and `//external:bar`, meaning `buildfiles(//external:foo)` returns the same result as `buildfiles(//external:*)`. However, this behavior is consistent with other packages. For example, `buildfiles(//foo:bar)` has the same result as `buildfiles(//foo:*)`.

Closes #14630.

PiperOrigin-RevId: 424092916
(cherry picked from commit a6c3f23)

* Avoid bazel_module_test timeout on macos

https://buildkite.com/bazel/bazel-bazel/builds/17785#9a1fb564-c6f9-4e69-ac8f-87c422a002b0
By setting the test size to "large".

RELNOTES:None
PiperOrigin-RevId: 409114345
(cherry picked from commit 1d99391)

Co-authored-by: Zhongpeng Lin <zplin@uber.com>
Co-authored-by: pcloudy <pcloudy@google.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

bazel query doesn't return the .bzl files that load external repositories
4 participants