forked from abayer/gradle-hpi-plugin
-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #132 from jjohannes/gradle-6
Use variant-aware dependency management
- Loading branch information
Showing
41 changed files
with
2,008 additions
and
874 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
146 changes: 146 additions & 0 deletions
146
src/main/groovy/org/jenkinsci/gradle/plugins/jpi/DependencyAnalysis.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
package org.jenkinsci.gradle.plugins.jpi | ||
|
||
import groovy.transform.CompileStatic | ||
import org.gradle.api.Project | ||
import org.gradle.api.artifacts.Configuration | ||
import org.gradle.api.artifacts.ModuleDependency | ||
import org.gradle.api.artifacts.ModuleVersionIdentifier | ||
import org.gradle.api.artifacts.result.DependencyResult | ||
import org.gradle.api.artifacts.result.ResolvedComponentResult | ||
import org.gradle.api.artifacts.result.ResolvedDependencyResult | ||
import org.gradle.api.artifacts.result.ResolvedVariantResult | ||
import org.gradle.api.attributes.Attribute | ||
import org.gradle.api.attributes.Category | ||
import org.gradle.api.attributes.LibraryElements | ||
|
||
@CompileStatic | ||
class DependencyAnalysis { | ||
|
||
private class JpiConfigurations { | ||
Configuration consumableLibraries | ||
Configuration consumablePlugins | ||
Configuration resolvablePlugins | ||
|
||
JpiConfigurations(Configuration consumableLibraries, | ||
Configuration consumablePlugins, | ||
Configuration resolvablePlugins) { | ||
this.consumableLibraries = consumableLibraries | ||
this.consumablePlugins = consumablePlugins | ||
this.resolvablePlugins = resolvablePlugins | ||
} | ||
} | ||
|
||
final Configuration allLibraryDependencies | ||
|
||
private static final Attribute CATEGORY_ATTRIBUTE = | ||
Attribute.of(Category.CATEGORY_ATTRIBUTE.name, String) | ||
private static final Attribute LIBRARY_ELEMENTS_ATTRIBUTE = | ||
Attribute.of(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE.name, String) | ||
private final List<JpiConfigurations> jpiConfigurations = [] | ||
|
||
private DependencyAnalysisResult analysisResult | ||
|
||
DependencyAnalysis(Project project) { | ||
this.allLibraryDependencies = project.configurations.detachedConfiguration() | ||
this.allLibraryDependencies.withDependencies { | ||
// do the analysis when this configuration is resolved | ||
analyse() | ||
} | ||
} | ||
|
||
void registerJpiConfigurations(Configuration consumableLibraries, | ||
Configuration consumablePlugins, | ||
Configuration resolvablePlugins) { | ||
jpiConfigurations.add(new JpiConfigurations(consumableLibraries, consumablePlugins, resolvablePlugins)) | ||
} | ||
|
||
DependencyAnalysisResult analyse() { | ||
if (analysisResult) { | ||
return analysisResult | ||
} | ||
|
||
def manifestEntry = new StringBuilder() | ||
|
||
jpiConfigurations.each { confs -> | ||
analyseDependencies(confs, allLibraryDependencies, manifestEntry) | ||
} | ||
analysisResult = new DependencyAnalysisResult(manifestEntry.toString()) | ||
analysisResult | ||
} | ||
|
||
private analyseDependencies(JpiConfigurations configurations, | ||
Configuration allLibraries, StringBuilder manifestEntry) { | ||
def optional = configurations.resolvablePlugins.name != JpiPlugin.JENKINS_RUNTIME_CLASSPATH_CONFIGURATION_NAME | ||
|
||
List<ModuleVersionIdentifier> processedComponents = [] | ||
configurations.resolvablePlugins.incoming.resolutionResult.root.dependencies.each { DependencyResult result -> | ||
def selected = getSelectedComponent(result, processedComponents) | ||
selected?.variants?.each { variant -> | ||
if (variant.attributes.getAttribute(CATEGORY_ATTRIBUTE) != Category.LIBRARY | ||
|| variant.attributes.getAttribute(LIBRARY_ELEMENTS_ATTRIBUTE) != JpiPlugin.JPI) { | ||
// Skip dependencies that are not libraries with JPI files. | ||
// We request these in the setup in JpiPlugin.configureConfigurations(). | ||
// However, an individual dependency can override attributes, for example 'category=platform'. | ||
return | ||
} | ||
|
||
def moduleVersion = selected.moduleVersion | ||
if (isMainFeature(moduleVersion, variant)) { | ||
addToManifestEntry(manifestEntry, selected, optional) | ||
} else { | ||
selected.getDependenciesForVariant(variant).each { featureDependency -> | ||
// add dependencies of the selected optional feature | ||
addToManifestEntry(manifestEntry, | ||
getSelectedComponent(featureDependency, processedComponents), optional) | ||
} | ||
} | ||
|
||
def moduleDependencies = configurations.resolvablePlugins.allDependencies.findAll { | ||
it instanceof ModuleDependency && it.group == moduleVersion.group && it.name == moduleVersion.name | ||
} | ||
configurations.consumablePlugins.dependencies.addAll(moduleDependencies) | ||
} | ||
} | ||
allLibraries.dependencies.addAll(configurations.consumableLibraries.allDependencies | ||
- configurations.consumablePlugins.allDependencies) | ||
} | ||
|
||
private static ResolvedComponentResult getSelectedComponent(DependencyResult dependency, | ||
List<ModuleVersionIdentifier> processedComponents) { | ||
if (dependency.constraint || !(dependency instanceof ResolvedDependencyResult)) { | ||
return null | ||
} | ||
def selected = ((ResolvedDependencyResult) dependency).selected | ||
def moduleVersion = selected.moduleVersion | ||
if (moduleVersion == null || processedComponents.contains(moduleVersion)) { | ||
// If feature variants are used, it is common to have multiple dependencies to the same component. | ||
// These then turn up in the result multiple times. | ||
return null | ||
} | ||
processedComponents.add(moduleVersion) | ||
selected | ||
} | ||
|
||
static boolean isMainFeature(ModuleVersionIdentifier component, ResolvedVariantResult variant) { | ||
// either no capability definition of main capability is explicitly defined | ||
variant.capabilities.isEmpty() || variant.capabilities.any { | ||
it.group == component.group && it.name == component.name | ||
} | ||
} | ||
|
||
private static void addToManifestEntry(StringBuilder manifestEntry, | ||
ResolvedComponentResult selected, | ||
boolean optional) { | ||
if (selected) { | ||
if (manifestEntry.length() > 0) { | ||
manifestEntry.append(',') | ||
} | ||
manifestEntry.append(selected.moduleVersion.name) | ||
manifestEntry.append(':') | ||
manifestEntry.append(selected.moduleVersion.version) | ||
if (optional) { | ||
manifestEntry.append(';resolution:=optional') | ||
} | ||
} | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
src/main/groovy/org/jenkinsci/gradle/plugins/jpi/DependencyAnalysisResult.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package org.jenkinsci.gradle.plugins.jpi | ||
|
||
import groovy.transform.CompileStatic | ||
|
||
@CompileStatic | ||
class DependencyAnalysisResult { | ||
|
||
final String manifestPluginDependencies | ||
|
||
DependencyAnalysisResult(String manifestPluginDependencies) { | ||
this.manifestPluginDependencies = manifestPluginDependencies | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
src/main/groovy/org/jenkinsci/gradle/plugins/jpi/JenkinsWarRule.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package org.jenkinsci.gradle.plugins.jpi | ||
|
||
import hudson.util.VersionNumber | ||
import org.gradle.api.artifacts.CacheableRule | ||
import org.gradle.api.artifacts.ComponentMetadataContext | ||
import org.gradle.api.artifacts.ComponentMetadataRule | ||
import org.gradle.api.attributes.LibraryElements | ||
import org.gradle.api.model.ObjectFactory | ||
|
||
import javax.inject.Inject | ||
|
||
@CacheableRule | ||
abstract class JenkinsWarRule implements ComponentMetadataRule { | ||
|
||
static final JENKINS_WAR_COORDINATES = 'org.jenkins-ci.main:jenkins-war' | ||
|
||
@Inject | ||
abstract ObjectFactory getObjects() | ||
|
||
/** | ||
* A Jenkins 'war' or 'war-for-test' is required on the Jenkins test classpath. This classpath expects JPI | ||
* variants. This rule adds such a variant to the Jenkins war module pointing at the right artifact depending | ||
* on the version of the module. | ||
*/ | ||
@Override | ||
void execute(ComponentMetadataContext ctx) { | ||
def id = ctx.details.id | ||
ctx.details.addVariant('jenkinsTestRuntimeElements', 'runtime') { | ||
it.attributes { | ||
it.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, | ||
objects.named(LibraryElements, JpiPlugin.JPI)) | ||
} | ||
it.withDependencies { | ||
// Dependencies with a classifier point at JARs and can be removed | ||
// TODO needs public API - https://github.com/gradle/gradle/issues/11975 | ||
it.removeAll { it.originalMetadata?.dependencyDescriptor?.dependencyArtifact?.classifier } | ||
} | ||
if (new VersionNumber(id.version) < new VersionNumber('2.64')) { | ||
it.withFiles { | ||
it.removeAllFiles() | ||
it.addFile("${id.name}-${id.version}-war-for-test.jar") | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.