From 89dd66521b55c0f3daacf4d253248cc8e63b8738 Mon Sep 17 00:00:00 2001 From: Niklas Baudy Date: Tue, 6 Mar 2018 15:34:53 +0100 Subject: [PATCH] Support Android flavors and buildTypes out of the box and add include Configuration extension point to Generator. (#9) --- .travis.yml | 27 ++-- build.gradle | 1 + .../DependencyGraphGeneratorExtension.kt | 9 +- .../graph/generator/DotGenerator.kt | 7 +- .../graph/generator/DotGeneratorTest.kt | 140 +++++++++++++++++- 5 files changed, 164 insertions(+), 20 deletions(-) diff --git a/.travis.yml b/.travis.yml index ea5de42..cd65c8b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,17 @@ -language: groovy +language: android -groovy: - - 2.3.6 - - 2.3.9 - -jdk: - - oraclejdk8 +jdk: oraclejdk8 before_install: - pip install --user codecov - -install: true + # Download SDK + - yes | sdkmanager "tools" &>/dev/null + - yes | sdkmanager "platform-tools" &>/dev/null + - yes | sdkmanager "build-tools;27.0.3" &>/dev/null + - yes | sdkmanager "platforms;android-27" &>/dev/null + # Update remaining dependencies and accept licenses + - yes | sdkmanager --update &>/dev/null + - yes | sdkmanager --licenses &>/dev/null script: ./gradlew clean build @@ -32,7 +33,11 @@ notifications: sudo: false +before_cache: + - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock + - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ cache: directories: - - $HOME/.gradle - - $HOME/.m2 + - $HOME/.gradle/caches/ + - $HOME/.gradle/wrapper/ + - $HOME/.android/build-cache \ No newline at end of file diff --git a/build.gradle b/build.gradle index 2f9b847..5d59451 100755 --- a/build.gradle +++ b/build.gradle @@ -70,6 +70,7 @@ dependencies { testImplementation 'junit:junit:4.12' testImplementation 'org.assertj:assertj-core:3.9.1' + testImplementation 'com.android.tools.build:gradle:3.0.1' testImplementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion" } diff --git a/src/main/kotlin/com/vanniktech/dependency/graph/generator/DependencyGraphGeneratorExtension.kt b/src/main/kotlin/com/vanniktech/dependency/graph/generator/DependencyGraphGeneratorExtension.kt index 3d65283..b87dfa9 100644 --- a/src/main/kotlin/com/vanniktech/dependency/graph/generator/DependencyGraphGeneratorExtension.kt +++ b/src/main/kotlin/com/vanniktech/dependency/graph/generator/DependencyGraphGeneratorExtension.kt @@ -3,6 +3,7 @@ package com.vanniktech.dependency.graph.generator import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorExtension.Generator.Companion.ALL import com.vanniktech.dependency.graph.generator.dot.GraphFormattingOptions import com.vanniktech.dependency.graph.generator.dot.Header +import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.ResolvedDependency /** @@ -33,7 +34,13 @@ open class DependencyGraphGeneratorExtension { /** The formatting options for the root - the project this generator is applied too. */ val rootFormattingOptions: GraphFormattingOptions = GraphFormattingOptions(), /** Optional header that can be displayed wrapped around the graph. */ - val header: Header? = null + val header: Header? = null, + /** Return true when you want to include this configuration, false otherwise. */ + val includeConfiguration: (Configuration) -> Boolean = { + // By default we'll include everything that's on the compileClassPath except test, UnitTest and AndroidTest configurations. + val raw = it.name.replace("compileClasspath", "", ignoreCase = true) + it.name.contains("compileClassPath", ignoreCase = true) && listOf("test", "AndroidTest", "UnitTest").none { raw.contains(it) } + } ) { /** Gradle task name that is associated with this generator. */ val gradleTaskName = "generateDependencyGraph${name.capitalize()}" diff --git a/src/main/kotlin/com/vanniktech/dependency/graph/generator/DotGenerator.kt b/src/main/kotlin/com/vanniktech/dependency/graph/generator/DotGenerator.kt index 2d553db..067ee02 100644 --- a/src/main/kotlin/com/vanniktech/dependency/graph/generator/DotGenerator.kt +++ b/src/main/kotlin/com/vanniktech/dependency/graph/generator/DotGenerator.kt @@ -43,12 +43,7 @@ internal class DotGenerator( .flatMap { project -> project.configurations .filter { it.isCanBeResolved } - .filter { - // TODO this is not optimal but will do for now. - val isCompileClassPath = it.name == "compileClasspath" - val isAndroidProjectClassPath = it.name == "debugCompileClasspath" || it.name == "releaseCompileClasspath" - isCompileClassPath || isAndroidProjectClassPath - } + .filter { generator.includeConfiguration.invoke(it) } .flatMap { it.resolvedConfiguration.firstLevelModuleDependencies } .map { project to it } } diff --git a/src/test/java/com/vanniktech/dependency/graph/generator/DotGeneratorTest.kt b/src/test/java/com/vanniktech/dependency/graph/generator/DotGeneratorTest.kt index a7809de..5d7d364 100644 --- a/src/test/java/com/vanniktech/dependency/graph/generator/DotGeneratorTest.kt +++ b/src/test/java/com/vanniktech/dependency/graph/generator/DotGeneratorTest.kt @@ -1,5 +1,7 @@ package com.vanniktech.dependency.graph.generator +import com.android.build.gradle.AppExtension +import com.android.build.gradle.AppPlugin import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorExtension.Generator.Companion.ALL import com.vanniktech.dependency.graph.generator.dot.Header import com.vanniktech.dependency.graph.generator.dot.Color.Companion.MAX_COLOR_VALUE @@ -10,20 +12,25 @@ import com.vanniktech.dependency.graph.generator.dot.GraphFormattingOptions import org.assertj.core.api.Java6Assertions.assertThat import org.gradle.api.Project import org.gradle.api.artifacts.ResolvedDependency +import org.gradle.api.internal.project.DefaultProject import org.gradle.api.plugins.JavaLibraryPlugin import org.gradle.testfixtures.ProjectBuilder import org.junit.Before import org.junit.Test +import java.io.File import java.util.Random class DotGeneratorTest { private lateinit var singleEmpty: Project private lateinit var singleProject: Project - private lateinit var multiProject: Project + private lateinit var androidProject: DefaultProject + private lateinit var androidProjectExtension: AppExtension - @Before fun setUp() { + @Before @Suppress("Detekt.LongMethod") fun setUp() { singleEmpty = ProjectBuilder.builder().withName("singleempty").build() + singleEmpty.plugins.apply(JavaLibraryPlugin::class.java) + singleEmpty.repositories.run { add(mavenCentral()) } singleProject = ProjectBuilder.builder().withName("single").build() singleProject.plugins.apply(JavaLibraryPlugin::class.java) @@ -44,6 +51,30 @@ class DotGeneratorTest { multiProject2.repositories.run { add(mavenCentral()) } multiProject2.dependencies.add("implementation", "io.reactivex.rxjava2:rxjava:2.1.10") multiProject2.dependencies.add("implementation", "io.reactivex.rxjava2:rxandroid:2.0.2") + + androidProject = ProjectBuilder.builder().withName("android").build() as DefaultProject + androidProject.plugins.apply(AppPlugin::class.java) + androidProject.repositories.run { add(mavenCentral()) } + + androidProjectExtension = androidProject.extensions.getByType(AppExtension::class.java) + androidProjectExtension.compileSdkVersion(27) + val manifestFile = File(androidProject.projectDir, "src/main/AndroidManifest.xml") + manifestFile.parentFile.mkdirs() + manifestFile.writeText(""" + | + | + | + |""".trimMargin()) + } + + @Test fun singleProjectAllNoTestDependencies() { + singleEmpty.dependencies.add("testImplementation", "junit:junit:4.12") + + assertThat(DotGenerator(singleEmpty, ALL).generateContent()).isEqualTo(""" + |digraph G { + | singleempty [label="singleempty", shape="box"]; + |} + |""".trimMargin()) } @Test fun singleProjectEmptyAll() { @@ -190,4 +221,109 @@ class DotGeneratorTest { |} |""".trimMargin()) } + + @Test fun androidProjectIncludeAllFlavorsByDefault() { + androidProjectExtension.flavorDimensions("test") + androidProjectExtension.productFlavors { + it.create("flavor1").dimension = "test" + it.create("flavor2").dimension = "test" + } + + androidProject.evaluate() // Since we're adding custom productFlavors we need this. + + androidProject.dependencies.add("flavor1Implementation", "io.reactivex.rxjava2:rxandroid:2.0.2") + androidProject.dependencies.add("flavor2DebugImplementation", "io.reactivex.rxjava2:rxjava:2.1.10") + androidProject.dependencies.add("flavor2ReleaseImplementation", "org.jetbrains.kotlin:kotlin-stdlib:1.2.30") + + assertThat(DotGenerator(androidProject, ALL).generateContent()).isEqualTo(""" + |digraph G { + | android [label="android", shape="box"]; + | ioreactivexrxjava2rxandroid [label="rxandroid", shape="box"]; + | android -> ioreactivexrxjava2rxandroid; + | ioreactivexrxjava2rxjava [label="rxjava", shape="box"]; + | ioreactivexrxjava2rxandroid -> ioreactivexrxjava2rxjava; + | orgreactivestreamsreactivestreams [label="reactive-streams", shape="box"]; + | ioreactivexrxjava2rxjava -> orgreactivestreamsreactivestreams; + | ioreactivexrxjava2rxjava [label="rxjava", shape="box"]; + | android -> ioreactivexrxjava2rxjava; + | orgjetbrainskotlinkotlinstdlib [label="kotlin-stdlib", shape="box"]; + | android -> orgjetbrainskotlinkotlinstdlib; + | orgjetbrainsannotations [label="annotations", shape="box"]; + | orgjetbrainskotlinkotlinstdlib -> orgjetbrainsannotations; + |} + |""".trimMargin()) + } + + @Test fun androidProjectIncludeAllBuildTypesByDefault() { + androidProjectExtension.buildTypes { + it.create("staging") + } + + androidProject.evaluate() // Since we're adding a custom buildType we need this. + + androidProject.dependencies.add("releaseImplementation", "io.reactivex.rxjava2:rxandroid:2.0.2") + androidProject.dependencies.add("debugImplementation", "io.reactivex.rxjava2:rxjava:2.1.10") + androidProject.dependencies.add("stagingImplementation", "org.jetbrains.kotlin:kotlin-stdlib:1.2.30") + + assertThat(DotGenerator(androidProject, ALL).generateContent()).isEqualTo(""" + |digraph G { + | android [label="android", shape="box"]; + | ioreactivexrxjava2rxjava [label="rxjava", shape="box"]; + | android -> ioreactivexrxjava2rxjava; + | orgreactivestreamsreactivestreams [label="reactive-streams", shape="box"]; + | ioreactivexrxjava2rxjava -> orgreactivestreamsreactivestreams; + | ioreactivexrxjava2rxandroid [label="rxandroid", shape="box"]; + | android -> ioreactivexrxjava2rxandroid; + | ioreactivexrxjava2rxjava [label="rxjava", shape="box"]; + | ioreactivexrxjava2rxandroid -> ioreactivexrxjava2rxjava; + | orgjetbrainskotlinkotlinstdlib [label="kotlin-stdlib", shape="box"]; + | android -> orgjetbrainskotlinkotlinstdlib; + | orgjetbrainsannotations [label="annotations", shape="box"]; + | orgjetbrainskotlinkotlinstdlib -> orgjetbrainsannotations; + |} + |""".trimMargin()) + } + + @Suppress("Detekt.UnnecessaryParentheses") // https://github.com/arturbosch/detekt/issues/767 + @Test fun androidProjectIncludeOnlyStagingCompileClasspath() { + androidProjectExtension.buildTypes { + it.create("staging") + } + + androidProject.evaluate() // Since we're adding a custom buildType we need this. + + androidProject.dependencies.add("releaseImplementation", "io.reactivex.rxjava2:rxandroid:2.0.2") + androidProject.dependencies.add("debugImplementation", "io.reactivex.rxjava2:rxjava:2.1.10") + androidProject.dependencies.add("stagingImplementation", "org.jetbrains.kotlin:kotlin-stdlib:1.2.30") + + assertThat(DotGenerator(androidProject, ALL.copy(includeConfiguration = { it.name == "stagingCompileClasspath" })).generateContent()).isEqualTo(""" + |digraph G { + | android [label="android", shape="box"]; + | orgjetbrainskotlinkotlinstdlib [label="kotlin-stdlib", shape="box"]; + | android -> orgjetbrainskotlinkotlinstdlib; + | orgjetbrainsannotations [label="annotations", shape="box"]; + | orgjetbrainskotlinkotlinstdlib -> orgjetbrainsannotations; + |} + |""".trimMargin()) + } + + @Test fun androidProjectDoNotIncludeTestDependency() { + androidProject.dependencies.add("testImplementation", "junit:junit:4.12") + + assertThat(DotGenerator(androidProject, ALL).generateContent()).isEqualTo(""" + |digraph G { + | android [label="android", shape="box"]; + |} + |""".trimMargin()) + } + + @Test fun androidProjectDoNotIncludeAndroidTestDependency() { + androidProject.dependencies.add("androidTestImplementation", "junit:junit:4.12") + + assertThat(DotGenerator(androidProject, ALL).generateContent()).isEqualTo(""" + |digraph G { + | android [label="android", shape="box"]; + |} + |""".trimMargin()) + } }