Merge "Create reportModifications task" into oc-mr1-support-27.0-dev
diff --git a/app-toolkit/dependencies.gradle b/app-toolkit/dependencies.gradle
index cb68089..eb90b12 100644
--- a/app-toolkit/dependencies.gradle
+++ b/app-toolkit/dependencies.gradle
@@ -22,7 +22,6 @@
ffLibs = libs
}
def ffVersions = [:]
-ffVersions.kotlin = "1.1.3"
ffVersions.auto_common = "0.6"
ffVersions.javapoet = "1.8.0"
ffVersions.compile_testing = "0.11"
@@ -40,10 +39,6 @@
ffVersions.guava = "21.0"
ffVersions.jsr250 = "1.2"
-ffLibs.kotlin = [
- stdlib : "org.jetbrains.kotlin:kotlin-stdlib:$ffVersions.kotlin",
- gradle_plugin : "org.jetbrains.kotlin:kotlin-gradle-plugin:$ffVersions.kotlin"
-]
ffLibs.auto_common = "com.google.auto:auto-common:$ffVersions.auto_common"
ffLibs.apache = [
commons : [
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index 9f648d9..11b9a27 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -1,13 +1,21 @@
-apply plugin: 'groovy'
-apply plugin: 'java'
-
-apply from: "dependencies.gradle"
+buildscript {
+ ext.supportRootFolder = project.projectDir.getParentFile()
+ apply from: 'repos.gradle'
+ repos.addMavenRepositories(repositories)
+ apply from: "dependencies.gradle"
+ dependencies {
+ classpath libs.kotlin.gradle_plugin
+ }
+}
ext.supportRootFolder = project.projectDir.getParentFile()
apply from: 'repos.gradle'
-
repos.addMavenRepositories(repositories)
+apply plugin: 'groovy'
+apply plugin: 'java'
+apply plugin: 'kotlin'
+
dependencies {
compile libs.gradle
compile libs.jacoco
diff --git a/buildSrc/dependencies.gradle b/buildSrc/dependencies.gradle
index 124f046..ef538d1 100644
--- a/buildSrc/dependencies.gradle
+++ b/buildSrc/dependencies.gradle
@@ -26,6 +26,11 @@
libs.espresso_contrib = 'com.android.support.test.espresso:espresso-contrib:3.0.1'
libs.jacoco = 'org.jacoco:org.jacoco.core:0.7.8'
+libs.kotlin = [
+ stdlib : "org.jetbrains.kotlin:kotlin-stdlib:1.1.3",
+ gradle_plugin : "org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.3"
+]
+
def androidPluginVersionOverride = System.getenv("GRADLE_PLUGIN_VERSION")
if (androidPluginVersionOverride != null) {
diff --git a/buildSrc/init.gradle b/buildSrc/init.gradle
index a81a2a9..b3cd58f 100644
--- a/buildSrc/init.gradle
+++ b/buildSrc/init.gradle
@@ -332,3 +332,6 @@
ext.init.setupRelease = this.&setupRelease
ext.init.configureSubProjects = this.&configureSubProjects
ext.init.configureBuildOnServer = this.&configureBuildOnServer
+ext.init.enableModificationsReporter = this.&enableModificationsReporter
+
+apply from: "${ext.supportRootFolder}/buildSrc/modifications_reporter.gradle"
\ No newline at end of file
diff --git a/buildSrc/modifications_reporter.gradle b/buildSrc/modifications_reporter.gradle
new file mode 100644
index 0000000..b8fcbc5
--- /dev/null
+++ b/buildSrc/modifications_reporter.gradle
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import android.support.ModifiedProjectsReporter
+
+task(reportModifications) {
+ doLast{
+ if (!rootProject.hasProperty("SHAs")) {
+ throw new GradleException("You must specify SHAs range: -PSHAs=<rev1>...<rev2>")
+ }
+ def range = SHAs.tokenize('...')
+ if (range.size() != 2) {
+ throw new GradleException("You must specify SHAs in format: <rev1>...<rev2>")
+ }
+ printModifications(range[0], range[1])
+ }
+}
+
+def printModifications(sha1, sha2) {
+ def modificationsReporter = new ModifiedProjectsReporter()
+ modificationsReporter.printModifiedProjects(ext.supportRootFolder, sha1, sha2, rootProject)
+}
diff --git a/buildSrc/src/main/kotlin/android/support/ModifiedProjectsReporter.kt b/buildSrc/src/main/kotlin/android/support/ModifiedProjectsReporter.kt
new file mode 100644
index 0000000..1adbd0b
--- /dev/null
+++ b/buildSrc/src/main/kotlin/android/support/ModifiedProjectsReporter.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support
+
+import org.gradle.api.GradleException
+import java.io.File
+import java.util.concurrent.Executors
+import java.util.concurrent.Future
+import java.util.concurrent.TimeUnit
+import org.gradle.api.Project
+
+
+@Suppress("unused")
+class ModifiedProjectsReporter {
+
+ private val ioExecutor = Executors.newFixedThreadPool(5)
+ private fun <T> ioThread(f: () -> T): Future<T> = ioExecutor.submit(f)
+
+ fun printModifiedProjects(gitRoot: File,
+ shaFirst: String, shaLast: String, rootProject: Project) {
+
+ val modifiedRootProjects = rootProject.subprojects
+ .filter { project ->
+ project.hasMavenArtifact()
+ && project.projectDir.canonicalPath.startsWith(gitRoot.canonicalPath)
+ }
+ .map { project ->
+ project to hasModification(shaFirst, shaLast, project.projectDir)
+ }
+ .associate { (project, future) -> project to future.get() }
+ .filterValues { it }
+ .keys
+ val graph = PublicProjectsGraph(rootProject)
+ val modifiedProjects = markDependentProjects(graph, modifiedRootProjects)
+
+ if (modifiedProjects.isEmpty()) {
+ println("No modifications found in public modules")
+ return
+ }
+
+ println("Modified Projects:")
+ modifiedProjects.forEach {
+ println(" $it")
+ }
+ }
+
+ private fun markDependentProjects(graph: PublicProjectsGraph,
+ modified: Set<Project>): Set<Project> {
+ val result = mutableSetOf<Project>()
+
+ fun visit(project: Project) {
+ if (project in result) {
+ return
+ }
+ result.add(project)
+ graph.dependents(project).forEach(::visit)
+ }
+ modified.forEach(::visit)
+ return result
+ }
+
+ private fun hasModification(shaFirst: String, shaLast: String, projectDir: File) = ioThread {
+ // sample: git log 367f3f6c46af1591..3636a95826a9 -- lifecycle/
+ val process = ProcessBuilder("git", "log", "$shaFirst..$shaLast", "--",
+ projectDir.absolutePath).start()
+
+ val awaitResult = process.waitFor(1, TimeUnit.SECONDS)
+ if (!awaitResult) {
+ throw GradleException("Failed to await for git")
+ }
+ if (process.exitValue() != 0) {
+ val errorMessage = process.errorStream.bufferedReader().use { it.readText() }
+ throw GradleException("Git command failed: $errorMessage")
+ }
+ process.inputStream.bufferedReader().use { !it.readLine().isNullOrEmpty() }
+ }
+}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/android/support/PublicProjectsGraph.kt b/buildSrc/src/main/kotlin/android/support/PublicProjectsGraph.kt
new file mode 100644
index 0000000..5f8e952
--- /dev/null
+++ b/buildSrc/src/main/kotlin/android/support/PublicProjectsGraph.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support
+
+import org.gradle.api.Project
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.artifacts.ProjectDependency
+import org.gradle.api.tasks.Upload
+
+class PublicProjectsGraph(rootProject: Project) {
+ private val graph: MutableMap<Project, MutableSet<Project>> = mutableMapOf()
+
+ init {
+ val logger = rootProject.logger
+ rootProject.subprojects.filter { project ->
+ if (project.subprojects.size != 0) {
+ logger.info("PublicModuleGraph: $project ignored because it's top level")
+ false
+ } else {
+ val hasMavenArtifact = project.hasMavenArtifact()
+ if (!hasMavenArtifact) {
+ logger.info("PublicModuleGraph: $project ignored because " +
+ "it doesn't have upload task")
+ }
+ hasMavenArtifact
+ }
+ }.forEach { project ->
+ addDependenciesFrom(project, "compile")
+ addDependenciesFrom(project, "api")
+ addDependenciesFrom(project, "implementation")
+ }
+ }
+
+ fun addDependenciesFrom(project: Project, configName: String) {
+ val config: Configuration = project.configurations.findByName(configName) ?: return
+ config.dependencies.withType(ProjectDependency::class.java).forEach { dep ->
+ graph.getOrPut(dep.dependencyProject, { mutableSetOf<Project>() }).add(project)
+ }
+ }
+
+ fun dependents(project: Project): Set<Project> = graph.getOrDefault(project, mutableSetOf())
+}
+
+fun Project.hasMavenArtifact() = tasks.withType(Upload::class.java).any { task -> task.enabled }