-
Notifications
You must be signed in to change notification settings - Fork 10
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
Several Gradle improvements #27
Conversation
Provider<Set<ResolvedArtifactResult>> artifacts = | ||
project | ||
.getConfigurations() | ||
.getByName(configurationName) | ||
.getIncoming() | ||
.getArtifacts() | ||
.getResolvedArtifacts(); | ||
t.getResolvedArtifacts().putAll(artifacts.map(new ArtifactTransformer())); | ||
|
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.
This was causing resolution issues in my project because of variants and mismatched attributes.
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.
can you describe the issue a little more for us to understand?
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 wish I could 😅
Gradle has a concept of variants, and for some reason this code causes Gradle to use the wrong attributes when resolving some dependencies from some configurations.
The issue with the pomsConfig
below is similar, and I think it is related to detached configurations not copying the attributes. I tried manually copying the attributes from the original configuration to the detached one, but there where still issues.
This is an area that I don't have the most expertise in with Gradle, so I'm not much help here.
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'm working on a standalone repro, but in the meantime here are some of the failures from my project:
spdxSbom {
targets {
create("release") {
configurations.set(listOf("devDebugRuntimeClasspath"))
}
}
}
> Could not create task ':app:spdxSbomForRelease'.
> Could not resolve all artifacts for configuration ':app:detachedConfiguration9'.
> Could not resolve com.eygraber:android-date-time-input-view:0.9.16.
Required by:
project :app
> Cannot choose between the following variants of com.eygraber:android-date-time-input-view:0.9.16:
- debugVariantMavenRuntimePublication
- releaseVariantMavenRuntimePublication
All of them match the consumer attributes:
- Variant 'debugVariantMavenRuntimePublication' capability com.eygraber:android-date-time-input-view:0.9.16:
- Unmatched attributes:
- Provides com.android.build.api.attributes.BuildTypeAttr 'debug' but the consumer didn't ask for it
- Provides org.gradle.category 'library' but the consumer didn't ask for it
- Provides org.gradle.dependency.bundling 'external' but the consumer didn't ask for it
- Provides org.gradle.libraryelements 'aar' but the consumer didn't ask for it
- Provides org.gradle.status 'release' but the consumer didn't ask for it
- Provides org.gradle.usage 'java-runtime' but the consumer didn't ask for it
- Variant 'releaseVariantMavenRuntimePublication' capability com.eygraber:android-date-time-input-view:0.9.16:
- Unmatched attributes:
- Provides com.android.build.api.attributes.BuildTypeAttr 'release' but the consumer didn't ask for it
- Provides org.gradle.category 'library' but the consumer didn't ask for it
- Provides org.gradle.dependency.bundling 'external' but the consumer didn't ask for it
- Provides org.gradle.libraryelements 'aar' but the consumer didn't ask for it
- Provides org.gradle.status 'release' but the consumer didn't ask for it
- Provides org.gradle.usage 'java-runtime' but the consumer didn't ask for it
> Could not resolve com.eygraber:android-date-time-input-common:0.9.16.
Required by:
project :app
> Cannot choose between the following variants of com.eygraber:android-date-time-input-common:0.9.16:
- debugVariantMavenRuntimePublication
- releaseVariantMavenRuntimePublication
All of them match the consumer attributes:
- Variant 'debugVariantMavenRuntimePublication' capability com.eygraber:android-date-time-input-common:0.9.16:
- Unmatched attributes:
- Provides com.android.build.api.attributes.BuildTypeAttr 'debug' but the consumer didn't ask for it
- Provides org.gradle.category 'library' but the consumer didn't ask for it
- Provides org.gradle.dependency.bundling 'external' but the consumer didn't ask for it
- Provides org.gradle.libraryelements 'aar' but the consumer didn't ask for it
- Provides org.gradle.status 'release' but the consumer didn't ask for it
- Provides org.gradle.usage 'java-runtime' but the consumer didn't ask for it
- Variant 'releaseVariantMavenRuntimePublication' capability com.eygraber:android-date-time-input-common:0.9.16:
- Unmatched attributes:
- Provides com.android.build.api.attributes.BuildTypeAttr 'release' but the consumer didn't ask for it
- Provides org.gradle.category 'library' but the consumer didn't ask for it
- Provides org.gradle.dependency.bundling 'external' but the consumer didn't ask for it
- Provides org.gradle.libraryelements 'aar' but the consumer didn't ask for it
- Provides org.gradle.status 'release' but the consumer didn't ask for it
- Provides org.gradle.usage 'java-runtime' but the consumer didn't ask for it
> Could not resolve app.cash.sqldelight:android-driver:2.0.0-alpha05.
Required by:
project :app
> Cannot choose between the following variants of app.cash.sqldelight:android-driver:2.0.0-alpha05:
- debugVariantMavenRuntimePublication
- releaseVariantMavenRuntimePublication
All of them match the consumer attributes:
- Variant 'debugVariantMavenRuntimePublication' capability app.cash.sqldelight:android-driver:2.0.0-alpha05:
- Unmatched attributes:
- Provides com.android.build.api.attributes.BuildTypeAttr 'debug' but the consumer didn't ask for it
- Provides org.gradle.category 'library' but the consumer didn't ask for it
- Provides org.gradle.dependency.bundling 'external' but the consumer didn't ask for it
- Provides org.gradle.libraryelements 'aar' but the consumer didn't ask for it
- Provides org.gradle.status 'release' but the consumer didn't ask for it
- Provides org.gradle.usage 'java-runtime' but the consumer didn't ask for it
- Variant 'releaseVariantMavenRuntimePublication' capability app.cash.sqldelight:android-driver:2.0.0-alpha05:
- Unmatched attributes:
- Provides com.android.build.api.attributes.BuildTypeAttr 'release' but the consumer didn't ask for it
- Provides org.gradle.category 'library' but the consumer didn't ask for it
- Provides org.gradle.dependency.bundling 'external' but the consumer didn't ask for it
- Provides org.gradle.libraryelements 'aar' but the consumer didn't ask for it
- Provides org.gradle.status 'release' but the consumer didn't ask for it
- Provides org.gradle.usage 'java-runtime' but the consumer didn't ask for it
Configuration pomsConfig = | ||
project | ||
.getConfigurations() | ||
.detachedConfiguration( | ||
project | ||
.getConfigurations() | ||
.getByName(configurationName) | ||
.getIncoming() | ||
.getResolutionResult() | ||
.getAllComponents() | ||
.stream() | ||
.filter(rcr -> rcr.getId() instanceof ModuleComponentIdentifier) | ||
.map(rcr -> rcr.getId().getDisplayName() + "@pom") | ||
.map(pom -> project.getDependencies().create(pom)) | ||
.toArray(Dependency[]::new)); | ||
t.getPoms() | ||
.putAll(PomResolver.newPomResolver(project).effectivePoms(pomsConfig)); |
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.
This was causing resolution issues in my project because of variants and mismatched attributes.
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.
You can see the information Gradle uses to resolve the variant here - https://repo1.maven.org/maven2/com/eygraber/android-date-time-input-common/0.9.16/android-date-time-input-common-0.9.16.module
private Map<String, ArtifactRepository> getAllRepositories(Project project) { | ||
Map<String, ArtifactRepository> projectRepositories = project.getRepositories().getAsMap(); | ||
|
||
// nasty case because of https://github.com/gradle/gradle/issues/17295 | ||
Map<String, ArtifactRepository> settingsRepositories = ((GradleInternal) project.getGradle()) | ||
.getSettings() | ||
.getDependencyResolutionManagement() | ||
.getRepositories() | ||
.getAsMap(); |
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 only use dependencyResolutionManagement.repositories
to add my repositories, so the project one is empty. There could technically be repositories added in both places, so that's why I'm doing this.
@Inject protected abstract DependencyHandler getDependencyHandler(); | ||
@Inject protected abstract ConfigurationContainer getConfigurations(); |
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.
Injecting like this allows the task to be configuration cache compliant.
Set<ComponentIdentifier> componentIds = new HashSet<>(); | ||
for (var rootComponent : getRootComponents().get()) { | ||
componentIds.addAll( | ||
gatherSelectedDependencies( | ||
rootComponent, | ||
new HashSet<>(), | ||
new HashSet<>() | ||
) | ||
); | ||
} | ||
|
||
Map<ComponentArtifactIdentifier, File> resolvedArtifactsById = new HashMap<>(); | ||
|
||
@SuppressWarnings("unchecked") | ||
List<ResolvedArtifactResult> resolvedArtifactResults = | ||
getDependencyHandler() | ||
.createArtifactResolutionQuery() | ||
.forComponents(componentIds) | ||
.withArtifacts(MavenModule.class, MavenPomArtifact.class) | ||
.execute() | ||
.getResolvedComponents() | ||
.stream() | ||
.flatMap(componentArtifactsResult -> componentArtifactsResult.getArtifacts(MavenPomArtifact.class).stream()) | ||
.filter(artifactResult -> artifactResult instanceof ResolvedArtifactResult) | ||
.map(artifactResult -> (ResolvedArtifactResult) artifactResult) | ||
.peek(resolvedArtifactResult -> resolvedArtifactsById.put(resolvedArtifactResult.getId(), resolvedArtifactResult.getFile())) | ||
.collect(Collectors.toList()); | ||
|
||
PomResolver pomResolver = PomResolver.newPomResolver(getDependencyHandler(), getConfigurations(), getLogger()); |
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.
The tests and functionalTests both pass with this, and my project looks OK with it. I think it should be fine, but I'm not 100% sure.
4b690c1
to
45ffa80
Compare
Map<ComponentArtifactIdentifier, File> resolvedArtifactsById = new HashMap<>(); | ||
|
||
@SuppressWarnings("unchecked") | ||
List<ResolvedArtifactResult> resolvedArtifactResults = |
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 guess fundamentally, is this okay by gradle's build conventions? Resolving artifacts at task execution time?
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've been using this in my project for ~8 months and it's been working great. I think it just looks at the result of the resolution that is done before execution time. The javadoc for ArtifactResolutionQuery
shows an example of using it in a task.
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.
So further research is telling me this is not compatible with task configuration avoidance, someone from game recommended against this approach
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'm pretty sure Gradle uses this approach themselves, and I've been using it with task configuration avoidance for a while now. I can look for more info from Gradle on this.
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.
gotcha, I'll see if I can validate what I've been told too
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.
From a configuration cache perspective it should be fine
Waiting to hear from someone at Gradle conclusively about usage of the API in general, but in this case the existing code is using the same underlying API, and it won't work with projects using variants, so it's probably fine.
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 haven't looked very closely, but Gradle has been thinking about disallowing resolving dependencies at configuration time: gradle/gradle#2298 . Are there any other times at which we might resolve these artifacts? (Resolving dependencies during task execution makes sense to me)
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 haven't heard from anyone at gradle directly, but it looks like they're working on a new API too address this. Until that's released this is one of at least two options.
The other option is something that CashApp is doing in Licensee where they used detached configurations and copy all the variant information into it. I think the sentiment is that this solution is more "correct" (despite being a little hacky), but it is vastly more complex.
Provider<Set<ResolvedArtifactResult>> artifacts = | ||
project | ||
.getConfigurations() | ||
.getByName(configurationName) | ||
.getIncoming() | ||
.getArtifacts() | ||
.getResolvedArtifacts(); | ||
t.getResolvedArtifacts().putAll(artifacts.map(new ArtifactTransformer())); | ||
|
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.
can you describe the issue a little more for us to understand?
@eygraber just fyi I'm on parental leave and will be quite slow to respond on these. |
45ffa80
to
be27066
Compare
Hey @eygraber, sorry I let this PR kinda fall to the side. Are there parts specifically that are still failing with 0.3.0, that we can begin to address again. Would prefer each issue is its own PR if possible (as mentioned up top). |
@loosebazooka I tested 0.3.0 (I've been using my own internal builds for now) and it is still failing because of the mismatched variants and attributes. As discussed here this PR has a fix for that, but Gradle plans on removing that workaround in the future. They don't have a proper workaround for now. There are other workarounds that involve a lot more work, but aren't using APIs that Gradle plans on removing (for now). I think there's one other issue addressed here that I can separate out (i.e. |
Cool, lets do the ArtifactRepository one at least for now. That seems pretty straight forward? |
I got some guidance from Gradle on how to handle the rest of it
I'm a little busy at the moment, but I'll get back to all of this when I have a few minutes. |
be27066
to
c3608b0
Compare
This needs to be cleaned up (and possibly broken up into multiple PRs) but I wanted to get opinions on this before doing any more work.