Skip to content

Commit

Permalink
fix: scan all subprojects when selecting attributes for conifguration (
Browse files Browse the repository at this point in the history
  • Loading branch information
Konstantin Yegupov authored Jun 26, 2019
1 parent 3ac5b5c commit 1125014
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 4 deletions.
9 changes: 7 additions & 2 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ const packageFormatVersion = 'mvn:0.0.1';
const isWin = /^win/.test(os.platform());
const quot = isWin ? '"' : '\'';

const cannotResolveVariantMarkers = [
'Cannot choose between the following',
'Could not select value from candidates',
'Unable to find a matching variant of project',
];

// TODO(kyegupov): the types below will be extracted to a common plugin interface library

export interface BaseInspectOptions {
Expand Down Expand Up @@ -360,8 +366,7 @@ message from above, starting with ===== DEBUG INFORMATION START =====.`;
// There are no automated tests for this yet (setting up Android SDK is quite problematic).
// See test/manual/README.md

if (/Cannot choose between the following/.test(error.message)
|| /Could not select value from candidates/.test(error.message)) {
if (cannotResolveVariantMarkers.find((m) => error.message.includes(m))) {

// Extract attribute information via JSONATTRS marker:
const jsonAttrs = JSON.parse(
Expand Down
2 changes: 1 addition & 1 deletion lib/init.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ allprojects { everyProj ->
// when resolving the project A, so that it selects a concrete variant of dependency B.
def allConfigurationAttributes = [:] // Map<Attribute<?>, Set<?>>
def attributesAsStrings = [:] // Map<String, Set<string>>
rootProject.allprojects.findAll(shouldScanProject).each { proj ->
rootProject.allprojects.each { proj ->
proj.configurations.findAll({ it.name != 'snykMergedDepsConf' && it.name =~ confNameFilter && matchesAttributeFilter(it) }).each { conf ->
if (!conf.hasProperty('attributes')) {
// Gradle before version 3 does not support attributes
Expand Down
102 changes: 102 additions & 0 deletions test/fixtures/multi-config-attributes-subproject/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
apply plugin: 'java'
apply plugin: 'maven'

group = 'com.github.jitpack'

sourceCompatibility = 1.8 // java 8
targetCompatibility = 1.8

repositories {
mavenCentral()
}

// See https://docs.gradle.org/current/userguide/dependency_management_attribute_based_matching.html

def specificAttr = Attribute.of("specificAttr", String)
def commonAttr = Attribute.of("specificAttr", String)
def usageAttr = null

if (project.hasProperty('objects')) {
usageAttr = Attribute.of("org.gradle.usage", Usage)
// Gradle 4+
configurations {
apiConf {
attributes {
attribute(usageAttr, project.objects.named(Usage, "java-api"))
attribute(specificAttr, "rootprojValue")
attribute(commonAttr, "common")
}
}
runtimeConf {
attributes {
attribute(usageAttr, project.objects.named(Usage, "java-runtime"))
attribute(specificAttr, "rootprojValue")
attribute(commonAttr, "common")
}
}
}
} else {
// Gradle 3
usageAttr = Attribute.of('usage', String)
configurations {
apiConf {
attributes {
attribute(usageAttr, "java-api")
attribute(specificAttr, "rootprojValue")
attribute(commonAttr, "common")
}
}
runtimeConf {
attributes {
attribute(usageAttr, "java-runtime")
attribute(specificAttr, "rootprojValue")
attribute(commonAttr, "common")
}
}
}
}

dependencies.attributesSchema {
attribute(specificAttr)
attribute(usageAttr)

println('SNYKECHO compatchain ' + attribute(specificAttr).compatibilityRules)
}


dependencies {
compile 'com.google.guava:guava:18.0'
apiConf 'commons-httpclient:commons-httpclient:3.1'
runtimeConf 'org.apache.commons:commons-lang3:3.8.1'
compile project(':subproj')
}

task sourcesJar(type: Jar, dependsOn: classes) {
classifier = 'sources'
from sourceSets.main.allSource
}

task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}

artifacts {
archives sourcesJar
archives javadocJar
}

// To specify a license in the pom:
install {
repositories.mavenInstaller {
pom.project {
licenses {
license {
name 'The Apache Software License, Version 2.0'
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
distribution 'repo'
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
rootProject.name = 'root-proj'

include 'subproj'
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
apply plugin: 'java'
apply plugin: 'maven'

group = 'com.github.jitpack'

sourceCompatibility = 1.8 // java 8
targetCompatibility = 1.8

repositories {
mavenCentral()
}

def specificAttr = Attribute.of("specificAttr", String)
def commonAttr = Attribute.of("specificAttr", String)
def usageAttr = null

if (project.hasProperty('objects')) {
usageAttr = Attribute.of("org.gradle.usage", Usage)
// Gradle 4+
configurations {
apiConf {
attributes {
attribute(usageAttr, project.objects.named(Usage, "java-api"))
attribute(specificAttr, "subproj1Value")
attribute(commonAttr, "common")
}
}
runtimeConf {
attributes {
attribute(usageAttr, project.objects.named(Usage, "java-runtime"))
attribute(specificAttr, "subproj1Value")
attribute(commonAttr, "common")
}
}
}
} else {
// Gradle 3
usageAttr = Attribute.of('usage', String)
configurations {
apiConf {
attributes {
attribute(usageAttr, "java-api")
attribute(specificAttr, "subproj1Value")
attribute(commonAttr, "common")
}
}
runtimeConf {
attributes {
attribute(usageAttr, "java-runtime")
attribute(specificAttr, "subproj2Value")
attribute(commonAttr, "common")
}
}
}
}

dependencies.attributesSchema {
attribute(specificAttr)
attribute(usageAttr)
}
22 changes: 21 additions & 1 deletion test/system/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ test('tests for Gradle 3+', async (t0) => {
const gradleVersionOutput = await subProcess.execute('gradle', ['-v'], {});
const isGradle3Plus = parseInt(gradleVersionOutput.match(/Gradle (\d+)\.\d+(\.\d+)?/)![1]) >= 3;
if (isGradle3Plus) {
t0.test('multi-confg: only deps for specified conf are picked up (attribute match)', async (t) => {

t0.test('multi-config: only deps for specified conf are picked up (attribute match)', async (t) => {
const result = await inspect('.',
path.join(fixtureDir('multi-config-attributes'), 'build.gradle'),
{'configuration-attributes': 'usage:java-api'});
Expand All @@ -107,5 +107,25 @@ test('tests for Gradle 3+', async (t0) => {
'correct version of api dep found');
t.equal(Object.keys(result.package.dependencies!).length, 2, 'top level deps: 2'); // 1 with good attr, 1 with no attr
});

t0.test('multi-config: only deps for specified conf are picked up (subproject variants)', async (t) => {
// This test is different from the previous because of specificAttr
// When constructing a merged configuration, it's important to scan all the subprojects and discover all the
// values of specificAttr to make sure the configuration
const result = await inspect('.',
path.join(fixtureDir('multi-config-attributes-subproject'), 'build.gradle'),
{'configuration-attributes': 'usage:java-api'}); // there's also specificAttr but it won't be picked up
t.match(result.package.name, '.',
'returned project name is not sub-project');
t.notOk(result.package
.dependencies!['org.apache.commons:commons-lang3'],
'no runtime dep found');
t.equal(result.package
.dependencies!['commons-httpclient:commons-httpclient'].version,
'3.1',
'correct version of api dep found');
t.equal(Object.keys(result.package.dependencies!).length, 3, 'top level deps: '); // 1 with good attr, 1 with no attr
});

}
})

0 comments on commit 1125014

Please sign in to comment.