-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Update Target Based Resolution #3838
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
Conversation
Looks as if this plugin depends on an executable built from another target, and that is the one that's failing to build first? The plugin dependencies use the normal dependencies machinery underneath, so in theory this should already provide the right dependencies. I'll take a closer looks and see if I have some advice.
I thought that |
Thanks, @abertelrud. I have already been debugging the plugin one some more. I have discovered that |
That was probably a wild goose chase. Adding the plug‐in tool product as a dependency in addition to the plug‐in script restores the missing links in the chain and llbuild knows what to do. But it does not look like that extra declaration is supposed to be necessary. Back to square one. |
I figured out the problem with the plug‐ins. The part that creates the executable binary comes from the product, not the target. Since the declared dependencies go (After figuring that out, I went back to |
In addition to the |
I went searching in the wild until I found a broken package and then traced it through the resolver. Reading back over the earlier threads, this bug was probably the cause of the issues @neonichu and I were trying to solve months ago. The symptoms certainly match. At this point, it would be helpful to know how this fares against smoke testing and the source compatibility suite. |
Wow, thank you for digging into this and finding this! I'll take a look at the specifics of what you had to do with synthesizing a product, but it sounds like the right approach — I actually thought that all packages already had this synthesis. The only thing to watch out for is that dependencies shouldn't have access to the synthesized executables if the package author didn't intentionally make them public. The product vs target distinction did cause a certain amount of grief when the plugin support was added (as it also did when testable executable modules were addded). (By the way, the product vs target distinction may have seemed like a good idea initially but at this point costs everyone more than it's worth, and I think is worth revisiting at some point — but that is obviously a later topic for a discussion and proposal.) |
@swift-ci please smoke test |
packageBuilder.dependencies = Array(dependencies.values) | ||
packageBuilder.dependencies = dependencies.values | ||
// Restore determinism: | ||
.sorted(by: { $0.package.identity < $1.package.identity }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorting these for stability is great, I think. In the long run I think it would actually be nice if we could get back to using the order in which dependencies were declared in the manifest. It shouldn't make a difference to the logic of course, but it seems to me that using the user's given order would be more natural to read as a package author. It's a total detail, just something I hope that SwiftPM can get back to at some point (I think it used to do this early on).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about that too, and considered refactoring to use an OrderedDictionary
. But pulling in swift-collections
seemed too far off‐topic for this PR. It is certainly something to consider in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely. It was more of a comment about a general approach here. I should have made that clear.
let implicitPlugInExecutables = Set(targets.lazy | ||
.filter({ $0.type == .plugin }) | ||
.flatMap({ $0.dependencies }) | ||
.map { $0.name }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I read this correctly, this prevents non-plugin targets from depending on these implicit products (which would be great). Or am I reading that wrong?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably...? It certainly means they are not there to depend on during resolution or loading. But later when the build system is constructed there is no insulation to prevent dangling references from pointing where it will end up. What I do not know for sure, is if there is a way for it to sneak under the diagnostic checks to get that far, since some dangling references are temporarily permitted so that later diagnostics have more information available to suggest fixes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for clarifying — it's very possible that we should (in a separate PR, by me) add more checks for that kind of thing.
Representing plugins as targets definitely has some trade-offs. It's a bit odd to do so, but making them completely separate would have made it more difficult to fit them into the dependency graph. I still think it would be even more complicated to make them participate in the dependency graph without being targets, but it definitely does introduce some edge cases.
To make sure I understand what you mean, let me ask it like this: Should the following work or not, assuming the intent is to have SwiftPM to build // Dependency
let package = Package(
// ...
products: [
.plugin(name: "PlugInScriptProduct", targets: ["PlugInScriptTarget"]),
// (There is no product for “PlugInExecutableTarget”.)
],
targets: [
.plugin(
name: "PluginScriptTarget",
// ...
dependencies: ["PlugInExecutableTarget"]
),
.executableTarget(name: "PlugInExecutableTarget")
]
) // Root
let package = Package(
// ...
targets: [
.target(
// ...
plugins: [
.plugin(name: "PlugInScriptProduct", package: "Dependency")
]
)
]
) |
Well the CI log tells me half of the pkg-config issue is real, since CI did not wine about it the installation the way it does on my machine, but it did hit other trouble later. I will keep investigating. |
I figured out the Looking over other |
That last commit fixes a bug where packages with tools version from 5.2 to 5.4 that had a name property different from the last path component behaved like 5.1 manifests do, asking the resolver to look for more than necessary. This was because the identity work broke the lookup from the target dependency to the package dependency, and the manifest could not tell which package the product was supposed to belong to. Due to having no known connection, the logic was landing in the code branches intended for resolving 5.1 manifests. Locally all tests are green and every package have tried now seems to be working as expected. Let me know:
|
Thanks for your work, @SDGGiesbrecht! I think leaving |
I adjusted the manifest so that you can set |
Thanks for laying this out in such detail. Yes, this should work, since it's the plugin inside the package that depends on an executable inside the package. What I think should not happen would be for |
I agree, thank you very much @SDGGiesbrecht!
This seems reasonable to me we as well. |
The expected errors vanish, and this warning appears instead:
The manifest does not think any target uses This might also be broken even in legacy resolution. Right now the test might only be succeeding because it happens to be in a root manifest, which bypasses lookup altogether (because in legacy it does not need to figure out its product filters). Transitive manifests still filter through |
there are two dependencies in play here: registry one ( |
The de‐duplication you speak of does not seem to be happening at a low enough level. Nothing in But now that I know understand how it is intended to work, I think I will be able to find and sink the logic in order to fix it. Thank you. |
Never mind, I see now that the manifest is being completely overwritten and thus does not need to know about any of the higher level stuff. I also neglected to notice earlier that the I am now pretty sure everything is working properly, and the test failure was just because the message changed along with the particular downstream error. Both the old and new errors indicate that it successfully navigated the stuff the test is about before any problems occurred. |
@elsh, would you double check this last commit for me? These two tests started failing after #5635. Part of the tests’ dependency graphs is unreachable, and thus only present under legacy resolution. Prior to your recent update, I had simply added the missing links necessary to ensure the entire graph was reachable. But since the update, that no longer works.† So my latest commit instead just adapts the expectations to what the current manifests are actually asking for. What I want to know from you is if the tests still test what they are supposed to, or if the tests become meaningless because the relevant part was in the unreachable portion of the dependency graph that is now skipped entirely. |
Closing. This and related PRs are stale and I do not have time to maintain them anymore. I will leave the related branches around for now in case anyone else wants to pick them up. |
Associated pull requests:
testDuplicateTransitiveIdentityWithoutNames
#3877testTransitiveDependencySwitchWithSameIdentity
ENABLE_TARGET_BASED_DEPENDENCY_RESOLUTION
by default (Enable Target‐Based Resolution #3880)See this comment in #3829 for the impetus.
I have brought all but two tests up to date, and now I have questions about them. (For one of @tomerd, @abertelrud or @neonichu?)
FunctionalTests.PluginTests/testUseOfBuildToolPluginProductByExecutableAcrossPackages
is trying to launch a plugin executable before building it. Can someone point me roughly to where or how SwiftPM communicates to llbuild that one command cannot start until another has finished? (All the necessary commands seem to be there, just not not in the right order.)XCBuildSupportTests.PIFBuilderTests/testTestProducts
is objecting with “failed to retrieve search paths with pkg-config; maybe pkg-config is not installed”. I do not know whether that is a real failure or just due to the machine I am using. Can someone else confirm one way or the other?(I will list and explain all the other updates once I am finished; all are minor and self‐contained.)