Skip to content
This repository was archived by the owner on Aug 19, 2020. It is now read-only.

Let Project scripts be cached in the Gradle build cache #978

Merged
merged 11 commits into from
Jul 20, 2018
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ fun buildAccessorsClassPathFor(project: Project, classPath: ClassPath) =
configuredProjectSchemaOf(project)?.let { projectSchema ->
val cacheDir =
scriptCacheOf(project)
.cacheDirFor(cacheKeyFor(projectSchema)) {
.cacheDirFor(cacheKeyFor(projectSchema)) { baseDir, _ ->
buildAccessorsJarFor(projectSchema, classPath, outputDir = baseDir)
}
AccessorsClassPath(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2018 the original author or authors.
*
* 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 org.gradle.kotlin.dsl.cache

import org.gradle.caching.BuildCacheKey
import org.gradle.caching.internal.controller.BuildCacheLoadCommand
import org.gradle.caching.internal.controller.BuildCacheStoreCommand

import java.io.File
import java.io.InputStream
import java.io.OutputStream


class ScriptBuildCacheKey(
private val displayName: String,
private val cacheKey: String
) : BuildCacheKey {

override fun getDisplayName(): String = displayName

override fun getHashCode(): String = cacheKey
}


/**
* Loads a directory previously stored by [StoreDirectory] from the build cache.
*/
class LoadDirectory(
private val directory: File,
private val cacheKey: BuildCacheKey
) : BuildCacheLoadCommand<Unit> {

override fun getKey(): BuildCacheKey = cacheKey

override fun load(inputStream: InputStream): BuildCacheLoadCommand.Result<Unit> {

val entryCount = unpack(inputStream, directory)

return object : BuildCacheLoadCommand.Result<Unit> {
override fun getMetadata() = Unit
override fun getArtifactEntryCount(): Long = entryCount
}
}
}


/**
* Stores a directory in the build cache.
*/
class StoreDirectory(
private val directory: File,
private val cacheKey: BuildCacheKey
) : BuildCacheStoreCommand {

override fun getKey(): BuildCacheKey = cacheKey

override fun store(outputStream: OutputStream): BuildCacheStoreCommand.Result {

val entryCount = pack(directory, outputStream)

return BuildCacheStoreCommand.Result { entryCount }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright 2018 the original author or authors.
*
* 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 org.gradle.kotlin.dsl.cache

import org.gradle.kotlin.dsl.support.normalisedPathRelativeTo

import java.io.DataInputStream
import java.io.DataOutputStream
import java.io.File
import java.io.InputStream
import java.io.OutputStream

import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream


internal
fun pack(inputDir: File, outputStream: OutputStream): Long {

var entryCount = 0L

DataOutputStream(GZIPOutputStream(outputStream)).useToRun {

val buffer = ByteArray(DEFAULT_BUFFER_SIZE)

inputDir.walkTopDown().drop(1).forEach { file ->

val path = file.normalisedPathRelativeTo(inputDir)
val isFile = file.isFile

writeUTF(path)
writeBoolean(isFile)

if (isFile) {
writeLong(file.length())
file.copyTo(this, buffer)
}

entryCount += 1
}

writeUTF("")
}

return entryCount
}


internal
fun unpack(inputStream: InputStream, outputDir: File): Long =

DataInputStream(GZIPInputStream(inputStream)).useToRun {

val buffer = ByteArray(DEFAULT_BUFFER_SIZE)

var entryCount = 0L

while (true) {

val path = readUTF()
if (path.isEmpty()) break

val isFile = readBoolean()

val file = File(outputDir, path)
if (isFile) {
val length = readLong()
copyTo(file, length, buffer)
} else {
file.mkdir()
}

entryCount += 1
}

entryCount
}


private
inline fun <T : AutoCloseable, U> T.useToRun(action: T.() -> U): U =
use { run(action) }


private
fun File.copyTo(out: OutputStream, buffer: ByteArray) {
inputStream().use { input ->
var read = input.read(buffer)
while (read >= 0) {
out.write(buffer, 0, read)
read = input.read(buffer)
}
}
}


private
fun InputStream.copyTo(file: File, length: Long, buffer: ByteArray) {
file.outputStream().use { output ->
var remaining = length
val bufferSize = buffer.size.toLong()
while (remaining > 0) {
val read = read(buffer, 0, remaining.coerceAtMost(bufferSize).toInt())
output.write(buffer, 0, read)
remaining -= read
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,21 @@ object BuildServices {
cacheKeyBuilder: CacheKeyBuilder,
cacheRepository: CacheRepository,
startParameters: StartParameter
) =
): ScriptCache {

ScriptCache(
val hasBuildCacheIntegration =
startParameters.isBuildCacheEnabled && startParameters.isKotlinDslBuildCacheEnabled

return ScriptCache(
cacheRepository,
cacheKeyBuilder,
startParameters.isRecompileScripts
startParameters.isRecompileScripts,
hasBuildCacheIntegration
)
}
}


private
val StartParameter.isKotlinDslBuildCacheEnabled: Boolean
get() = projectProperties.getOrDefault("org.gradle.kotlin.dsl.caching.buildcache", null) == "true"
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package org.gradle.kotlin.dsl.cache

import org.gradle.cache.CacheRepository
import org.gradle.cache.PersistentCache

import org.gradle.cache.internal.CacheKeyBuilder
import org.gradle.cache.internal.CacheKeyBuilder.CacheKeySpec
Expand All @@ -35,28 +34,29 @@ class ScriptCache(
val cacheKeyBuilder: CacheKeyBuilder,

private
val recompileScripts: Boolean
val recompileScripts: Boolean,

val hasBuildCacheIntegration: Boolean
) {

fun cacheDirFor(
keySpec: CacheKeySpec,
properties: Map<String, Any?>? = null,
scope: Any? = null,
initializer: PersistentCache.() -> Unit
): File =

cacheRepository
.cache(scope, cacheKeyFor(keySpec))
initializer: (File, String) -> Unit
): File {
val cacheKey = cacheKeyFor(keySpec)
return cacheRepository.cache(cacheKey)
.apply { properties?.let { withProperties(it) } }
.apply { if (recompileScripts) withValidator { false } }
.withInitializer(initializer)
.withInitializer { initializer(it.baseDir, cacheKey) }
.open().run {
close()
baseDir
}
}

private
fun cacheKeyFor(spec: CacheKeySpec) = cacheKeyBuilder.build(spec)
fun cacheKeyFor(spec: CacheKeySpec): String = cacheKeyBuilder.build(spec)
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class Interpreter(val host: Host) {
)

fun cachedDirFor(
scriptHost: KotlinScriptHost<*>,
templateId: String,
sourceHash: HashCode,
parentClassLoader: ClassLoader,
Expand Down Expand Up @@ -236,6 +237,7 @@ class Interpreter(val host: Host) {

val cachedDir =
host.cachedDirFor(
scriptHost,
templateId,
sourceHash,
parentClassLoader,
Expand Down Expand Up @@ -438,6 +440,7 @@ class Interpreter(val host: Host) {

val cacheDir =
host.cachedDirFor(
scriptHost,
scriptTemplateId,
sourceHash,
parentClassLoader,
Expand Down
Loading