From 1125014ab9ff4d90fef30be8b0c3b34c78ba1300 Mon Sep 17 00:00:00 2001 From: Konstantin Yegupov Date: Wed, 26 Jun 2019 16:10:21 +0100 Subject: [PATCH] fix: scan all subprojects when selecting attributes for conifguration (#81) --- lib/index.ts | 9 +- lib/init.gradle | 2 +- .../build.gradle | 102 ++++++++++++++++++ .../settings.gradle | 3 + .../subproj/build.gradle | 60 +++++++++++ test/system/plugin.test.ts | 22 +++- 6 files changed, 194 insertions(+), 4 deletions(-) create mode 100644 test/fixtures/multi-config-attributes-subproject/build.gradle create mode 100644 test/fixtures/multi-config-attributes-subproject/settings.gradle create mode 100644 test/fixtures/multi-config-attributes-subproject/subproj/build.gradle diff --git a/lib/index.ts b/lib/index.ts index a778637..d1ce5a9 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -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 { @@ -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( diff --git a/lib/init.gradle b/lib/init.gradle index 3764b2f..ba7c15a 100644 --- a/lib/init.gradle +++ b/lib/init.gradle @@ -154,7 +154,7 @@ allprojects { everyProj -> // when resolving the project A, so that it selects a concrete variant of dependency B. def allConfigurationAttributes = [:] // Map, Set> def attributesAsStrings = [:] // Map> - 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 diff --git a/test/fixtures/multi-config-attributes-subproject/build.gradle b/test/fixtures/multi-config-attributes-subproject/build.gradle new file mode 100644 index 0000000..dd82e1c --- /dev/null +++ b/test/fixtures/multi-config-attributes-subproject/build.gradle @@ -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' + } + } + } + } +} diff --git a/test/fixtures/multi-config-attributes-subproject/settings.gradle b/test/fixtures/multi-config-attributes-subproject/settings.gradle new file mode 100644 index 0000000..7832b18 --- /dev/null +++ b/test/fixtures/multi-config-attributes-subproject/settings.gradle @@ -0,0 +1,3 @@ +rootProject.name = 'root-proj' + +include 'subproj' diff --git a/test/fixtures/multi-config-attributes-subproject/subproj/build.gradle b/test/fixtures/multi-config-attributes-subproject/subproj/build.gradle new file mode 100644 index 0000000..f87808c --- /dev/null +++ b/test/fixtures/multi-config-attributes-subproject/subproj/build.gradle @@ -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) +} diff --git a/test/system/plugin.test.ts b/test/system/plugin.test.ts index 1587dbe..97e94c6 100644 --- a/test/system/plugin.test.ts +++ b/test/system/plugin.test.ts @@ -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'}); @@ -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 + }); + } })