From c77130d0f3855355e63d6d4eedc21e70258d4a12 Mon Sep 17 00:00:00 2001 From: Vyacheslav Gerasimov Date: Mon, 2 Mar 2020 22:13:54 +0300 Subject: [PATCH] Build: Add test for reporting all gradle build cache misses `kotlin.build.cache.check.enabled` will be used for cacheability testing on teamcity --- .idea/dictionaries/4u7.xml | 2 + build.gradle.kts | 1 + gradle/checkCacheability.gradle.kts | 59 +++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 gradle/checkCacheability.gradle.kts diff --git a/.idea/dictionaries/4u7.xml b/.idea/dictionaries/4u7.xml index ec16da6a994..6b2f1c7ec64 100644 --- a/.idea/dictionaries/4u7.xml +++ b/.idea/dictionaries/4u7.xml @@ -2,6 +2,8 @@ bintray + cacheability + cacheable cidr foldable instrumentator diff --git a/build.gradle.kts b/build.gradle.kts index b77e4e90226..d9cddba45c9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -149,6 +149,7 @@ rootProject.apply { from(rootProject.file("gradle/javaInstrumentation.gradle.kts")) from(rootProject.file("gradle/jps.gradle.kts")) from(rootProject.file("gradle/checkArtifacts.gradle.kts")) + from(rootProject.file("gradle/checkCacheability.gradle.kts")) } IdeVersionConfigurator.setCurrentIde(project) diff --git a/gradle/checkCacheability.gradle.kts b/gradle/checkCacheability.gradle.kts new file mode 100644 index 00000000000..fcb1acef476 --- /dev/null +++ b/gradle/checkCacheability.gradle.kts @@ -0,0 +1,59 @@ +import org.gradle.api.internal.GeneratedSubclasses +import org.gradle.api.internal.TaskInternal + +val checkCacheability = findProperty("kotlin.build.cache.check.enabled") as String? == "true" +val isTeamcityBuild = project.hasProperty("teamcity") || System.getenv("TEAMCITY_VERSION") != null + +if (checkCacheability && buildCacheEnabled()) { + gradle.taskGraph.afterTask { + if (isCacheable()) { + if (isTeamcityBuild) + testStarted(path) + + if (!state.skipped) + reportCacheMiss() + + if (isTeamcityBuild) + testFinished(path) + } + } +} + +fun Task.reportCacheMiss() { + if (isTeamcityBuild) + testFailed(path, "Build cache MISS", "$path task outputs expected to be taken from Gradle build cache") + else + println("BUILD CACHE MISS - $path") +} + +fun Project.buildCacheEnabled() = gradle.startParameter.isBuildCacheEnabled + +fun Task.isCacheable(): Boolean { + this as TaskInternal + return cachingEnabled() && !cachingDisabled() +} + +fun TaskInternal.cachingEnabled(): Boolean { + return if (outputs.cacheIfSpecs.isEmpty()) + GeneratedSubclasses.unpackType(this).isAnnotationPresent(CacheableTask::class.java) + else + outputs.cacheIfSpecs.all { it.invoke(this) } +} + +fun TaskInternal.cachingDisabled(): Boolean = outputs.doNotCacheIfSpecs.any { it.invoke(this) } + +fun escape(s: String): String { + return s.replace("[\\|'\\[\\]]".toRegex(), "\\|$0").replace("\n".toRegex(), "|n").replace("\r".toRegex(), "|r") +} + +fun testStarted(testName: String) { + println("##teamcity[testStarted name='%s']".format(escape(testName))) +} + +fun testFinished(testName: String) { + println("##teamcity[testFinished name='%s']".format(escape(testName))) +} + +fun testFailed(name: String, message: String, details: String) { + println("##teamcity[testFailed name='%s' message='%s' details='%s']".format(escape(name), escape(message), escape(details))) +} \ No newline at end of file