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

Run ARQ during task serialization #72

Merged
merged 14 commits into from
Dec 5, 2023

Conversation

eygraber
Copy link
Contributor

@eygraber eygraber commented Nov 8, 2023

No description provided.

@eygraber eygraber changed the title Arq serialization Run ARQ during task serialization Nov 8, 2023
@loosebazooka
Copy link
Collaborator

loosebazooka commented Nov 9, 2023

alright one final rebase I guess 🎉 thanks for splitting everything up

  - Solves the issue with configuration variants and mismatched attributes
  - Reflects the current best approach that Gradle suggests
    - They're hopefully going to provide better APIs in the future
@eygraber
Copy link
Contributor Author

eygraber commented Nov 9, 2023

Done!

t.getRootComponents().add(rootComponent);
}

Provider<List<ResolvedArtifactResult>> resolvedArtifactsProvider =
t.getRootComponents()
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think accessing the getRootComponents() here feels a bit weird to me. Can we create an intermediate that can be shared so we can still set t.getRootComponents()... and then reuse here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated

Collection<ResolvedArtifactResult> resolvedArtifactResults) {
return resolvedArtifactResults.stream()
// ignore gradle API components as they cannot be serialized
.filter(x -> !(x.getId().getComponentIdentifier() instanceof OpaqueComponentIdentifier))
Copy link
Collaborator

Choose a reason for hiding this comment

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

is there a reason we don't need this filter anymore?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated, must've lost it in the refactor

@loosebazooka
Copy link
Collaborator

LGTM, except some very minor things. Sorry it took me a little to read and understand what was going on.

Copy link
Collaborator

@loosebazooka loosebazooka left a comment

Choose a reason for hiding this comment

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

I believe a separation of the arq result of poms and archives needs to happen.

@loosebazooka
Copy link
Collaborator

Hey yeah sorry I'm doing some of my own experiments to understand this.

for (var configurationName : configurationNames) {
Provider<Set<ResolvedArtifactResult>> artifacts =
Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead of inferring the artifact from the pom resolution, why not just keep this section in?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I believe that's what causing the attribute mismatch errors

Copy link
Collaborator

Choose a reason for hiding this comment

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

Correct me if I'm wrong, but I thought that was the pom resolver stuff, while the artifact resolver was actually fine? I'm suggesting we still keep the code in here for dealing with POMs, and for the actual artifacts themselves keep the other section?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll try again and see what happens

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Although I'd think that it would be simpler to derive it from the POM vs resolving the artifact.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Okay just for posterity, conversation continued here: https://gradle-community.slack.com/archives/CAHSN3LDN/p1699854619838439

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Based on that conversation I'll investigate some alternatives.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Instead of inferring the artifact from the pom resolution, why not just keep this section in?

I tested this and it has issues resolving variants.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you provide an example where this issue arises? It'd be nice to have a test to prevent regression and provide a sort of documentation via code on this specific case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it comes up with Kotlin Multiplatform dependencies. I'll try to get a failing test on main

@eygraber eygraber force-pushed the arq-serialization branch 5 times, most recently from a963c25 to 629b042 Compare November 24, 2023 08:25
Comment on lines 140 to 146
.artifactView(
viewConfiguration ->
viewConfiguration.attributes(
attributes ->
attributes.attribute(
Attribute.of("artifactType", String.class),
"android-aar-or-jar")))
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 turned out to be the solution for resolving normal artifacts. I was inspired by gradle/gradle#20779.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Attribute.of("artifactType", String.class) comes from here

"android-aar-or-jar" comes from here

Copy link
Collaborator

@loosebazooka loosebazooka left a comment

Choose a reason for hiding this comment

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

Sorry it took a while, I was on vacation.

* @see org.gradle.api.artifacts.dsl.DependencyHandler
* @see org.gradle.api.artifacts.result.ResolvedArtifactResult
*/
public class DependencyResolver {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this DependencyResolver is now only used for POMs, so can we rename it a bit? DependecyPomMapper? or something along those lines?

It might even make sense to just move this whole thing into PomResolver

.filter(ResolvedArtifactResult.class::isInstance)
.map(ResolvedArtifactResult.class::cast)
// ignore gradle API components as they cannot be serialized
.filter(x -> !(x.getId().getComponentIdentifier() instanceof OpaqueComponentIdentifier))
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not actually sure this filter is useful here? Since we apply it above on the artifact transformer?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

True I'll remove it

Comment on lines 147 to 153
.artifactView(
viewConfiguration ->
viewConfiguration.attributes(
attributes ->
attributes.attribute(
Attribute.of("artifactType", String.class),
artifactType)))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would this now provide an incomplete sbom on a project with a war dependency or something (in the weird war overlay case)? or if someone was importing any other non jar/aar type from a repository (a zip or rar for instance) that pulls in resources and stuff?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, technically. We can try covering as many cases as possible, and add support for plugins that provide artifact types (like Android), but I don't know if there's a way to get everything. Maybe there's an attribute other than artifactType to use. I'll look into it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe it's possible to map the POM info into this and get a unique set of the packaging from the POM and use that to set up the attributes. Not sure if that'll work, but it's an idea 😅

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we can wait on that. This PR has had a lot of work done on it, I'd like to get it in soonish

Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we just apply this filter in the android usecase? It appears this only happens when referencing a subproject from within an android project -- because android subprojects output all these crazy variants?

if android project {
  apply artifactView
}
else {
  just do what we were doing before
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm just not super fond of applying the filter and potentially missing some cases (war, zip, rar, etc)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think just applying the filter in the Android use case would work for now until there's another type of plugin that causes the same issue.

I'm experimenting with making filters based on the packaging in the poms.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was able to get it working by using a separate artifactView for each packaging found in the POMs, but it required some provider magic redirections (i.e. declare a property for the set of packagings at the top, flatMapping it to the artifactViews, and populating the packagings property later on in the file mapping from the pomInfo property).

It seems to be working pretty well. I tested on a regular Kotlin JVM project, a Kotlin JVM project that depends on a KMP project, and a KMP project, and it all worked fine. For the KMP project, using the jvm configuration resulted in an sbom referencing jar files, and using the js configuration resulted in an sbom referencing klib files (which was pretty cool).

Android was a trouble maker again, but that was because there are conflicts where requesting jar artifacts returns the classes.jar from the aar file. The workaround was to detect if there was an Android plugin and replace aar and jar with android-aar-or-jar in the list of packagings.

It seems to be working with the configuration cache, and from what I can tell the resolution is happening during the configuration phase, so it should be fine. Let me know what you want to do with that.

for (var configurationName : configurationNames) {
Provider<Set<ResolvedArtifactResult>> artifacts =
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you provide an example where this issue arises? It'd be nice to have a test to prevent regression and provide a sort of documentation via code on this specific case?

src/main/java/org/spdx/sbom/gradle/maven/PomResolver.java Outdated Show resolved Hide resolved
@eygraber
Copy link
Contributor Author

Addressed the PR comments. Working on getting a failing test case to demonstrate the attribute/variant issue

@eygraber
Copy link
Contributor Author

Pushed a test case that fails on main but not on this branch. It is technically failing on this branch because there's no Android SDK defined. Options are:

  1. Add a local.properties file to the test project that points to a local Android SDK
  2. Don't expect build success, but look for the Android SDK missing error, since that means that the dependency resolution succeeded

Downside to 1 is that it will require that anyone running tests has the Android SDK at that specific location. Downside to 2 is that it is brittle.

@loosebazooka
Copy link
Collaborator

loosebazooka commented Nov 30, 2023

The test case was super helpful for me to understand the problem. We can apply a conditional run on the testcase using @EnabledIfEnvironmentVariable or something from junit5. Then we just add an android sdk to CI and always set the ENV to catch this at least in CI. But shouldn't break standard dev flows.

@eygraber eygraber force-pushed the arq-serialization branch 3 times, most recently from 63b88bd to 2222683 Compare November 30, 2023 05:52
@eygraber
Copy link
Contributor Author

Adding the setup-android action requires using at least JDK 17 to build. Bumping that in the setup-java action should be fine (and updating Gradle to 8.5 should allow using JDK 21), as it's just used for building, and the target compat is set to 11.

@loosebazooka
Copy link
Collaborator

I think the problem with depending on the pom is that it's not an accurate representation of a variant. A variant can have any packaging, but the pom only shows what the "main" variant's packaging is.

@eygraber
Copy link
Contributor Author

True, so then I guess the best approach would be to special case Android, and deal with any other variant issues similarly when they come up.

@eygraber
Copy link
Contributor Author

OK I made those changes, and surprise! there's another issue 😅

A Kotlin multiplatform (KMP) build that includes an android target would cause issues because the Android plugin is present, so we use the artifactView for aar-or-jar, but if you generate an sbom for the js target (for example, would apply to any non JVM target) then the klib dependencies aren't found (since the attribute filter is only looking for aar and jar).

So we can change the condition to only use the artifactView if the android plugin is present, and the KMP plugin is not present, but that will cause the variant errors when generating an sbom for the android target.

This might not be an issue in practice, since the user would probably be generating the sbom for a leaf module (e.g. an app), and with KMP those tend to only target one platform. What are your thoughts on that?

@loosebazooka
Copy link
Collaborator

loosebazooka commented Dec 1, 2023

So I'm not familiar enough with kotlin multiplatform to know user behavior with it. I think we get a version of this PR in now with what we kind of landed on earlier. And then we can deal with KMP in a followup.

@loosebazooka
Copy link
Collaborator

@eygraber so this is ready to merge?

@eygraber
Copy link
Contributor Author

eygraber commented Dec 4, 2023

Yes, it should be!

@loosebazooka loosebazooka merged commit e89f622 into spdx:main Dec 5, 2023
1 check passed
@eygraber eygraber deleted the arq-serialization branch December 6, 2023 00:24
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.

2 participants