| import org.jetbrains.kotlin.gradle.tasks.KotlinCompile |
| |
| apply plugin: "kotlin" |
| apply from: "../kotlin-dsl-dependency.gradle" |
| |
| buildscript { |
| project.ext.supportRootFolder = project.projectDir.getParentFile().getParentFile() |
| apply from: "../repos.gradle" |
| repos.addMavenRepositories(repositories) |
| dependencies { |
| classpath(libs.kotlinGradlePluginz) |
| } |
| } |
| |
| configurations { |
| // Dependencies added to these configurations get copied into the corresponding configuration |
| // (cacheableApi gets copied into api, etc). |
| // Because we cache the resolutions of these configurations, performance is faster when |
| // artifacts are put into these configurations than when those artifacts are put into their |
| // corresponding configuration. |
| cacheableApi |
| cacheableImplementation { |
| extendsFrom(project.configurations.cacheableApi) |
| } |
| cacheableRuntimeOnly |
| } |
| |
| dependencies { |
| cacheableApi(libs.androidGradlePluginz) |
| cacheableImplementation(libs.dexMemberList) |
| cacheableApi(libs.kotlinGradlePluginz) |
| cacheableImplementation(gradleApi()) |
| cacheableApi(libs.dokkaGradlePluginz) |
| // needed by inspection plugin |
| cacheableImplementation(libs.protobufGradlePluginz) |
| cacheableImplementation(libs.wireGradlePluginz) |
| cacheableImplementation(libs.shadow) |
| // dependencies that aren't used by buildSrc directly but that we resolve here so that the |
| // root project doesn't need to re-resolve them and their dependencies on every build |
| cacheableRuntimeOnly(libs.hiltAndroidGradlePluginz) |
| // room kotlintestapp uses the ksp plugin but it does not publish a plugin marker yet |
| cacheableApi(libs.kspGradlePluginz) |
| cacheableApi(libs.japicmpPluginz) |
| // dependencies whose resolutions we don't need to cache |
| compileOnly(findGradleKotlinDsl()) // Only one file in this configuration, no need to cache it |
| implementation(project(":jetpad-integration")) // Doesn't have a .pom, so not slow to load |
| } |
| |
| // Exclude dokka coming from AGP. We don't need it and it conflicts with dackka: b/195305339 |
| configurations.configureEach { conf -> |
| conf.exclude(group:"org.jetbrains.dokka", module:"dokka-core") |
| } |
| |
| // Saves configuration into destFile |
| // Each line of destFile will be the absolute filepath of one of the files in configuration |
| def saveConfigurationResolution(configuration, destFile) { |
| def resolvedConfiguration = configuration.resolvedConfiguration |
| def files = resolvedConfiguration.files |
| def paths = files.collect { f -> f.toString() } |
| def serialized = paths.join("\n") |
| destFile.text = serialized |
| } |
| |
| // Parses a file into a list of Dependency objects representing a ResolvedConfiguration |
| def parseConfigurationResolution(savedFile, throwOnError) { |
| def savedText = savedFile.text |
| def filenames = savedText.split("\n") |
| def valid = true |
| def dependencies = filenames.collect { filename -> |
| if (!project.file(filename).exists()) { |
| if (throwOnError) { |
| throw new GradleException("\nFile " + filename + " listed as a resolved dependency in " + savedFile + " does not exist!\n\nFor more information, see b/187075069") |
| } else { |
| valid = false |
| } |
| } |
| project.dependencies.create(project.files(filename)) |
| } |
| if (!valid) { |
| return null |
| } |
| return dependencies |
| } |
| |
| // Resolves a Configuration into a list of Dependency objects |
| def resolveConfiguration(configuration) { |
| def resolvedName = configuration.name |
| def cacheDir = new File(project.buildDir, "/" + resolvedName) |
| def inputsFile = new File(cacheDir, "/deps") |
| def outputsFile = new File(cacheDir, "/result") |
| |
| def inputText = fingerprintConfiguration(configuration) |
| def parsed = null |
| if (inputsFile.exists() && inputsFile.text == inputText) { |
| // Try to parse the previously resolved configuration, but don't give up if it mentions a |
| // nonexistent file. If something has since deleted one of the referenced files, we will |
| // try to reresolve that file later |
| parsed = parseConfigurationResolution(outputsFile, false) |
| } |
| // If the configuration has changed or if any of its files have been deleted, reresolve it |
| if (parsed == null) { |
| cacheDir.mkdirs() |
| saveConfigurationResolution(configuration, outputsFile) |
| inputsFile.text = inputText |
| // confirm that the resolved configuration parses successfully |
| parsed = parseConfigurationResolution(outputsFile, true) |
| } |
| return parsed |
| } |
| |
| // Computes a unique string from a Configuration based on its dependencies |
| // This is used for up-to-date checks |
| def fingerprintConfiguration(configuration) { |
| def dependencies = configuration.allDependencies |
| def dependencyTexts = dependencies.collect { dep -> dep.group + ":" + dep.name + ":" + dep.version } |
| return dependencyTexts.join("\n") |
| } |
| |
| // Imports the contents of fromConf into toConf |
| // Uses caching to often short-circuit the resolution of fromConf |
| def loadConfigurationQuicklyInto(fromConf, toConf) { |
| def resolved = resolveConfiguration(fromConf) |
| resolved.each { dep -> |
| project.dependencies.add(toConf.name, dep) |
| } |
| } |
| |
| loadConfigurationQuicklyInto(configurations.cacheableApi, configurations.api) |
| loadConfigurationQuicklyInto(configurations.cacheableImplementation, configurations.implementation) |
| loadConfigurationQuicklyInto(configurations.cacheableRuntimeOnly, configurations.runtimeOnly) |
| |
| project.tasks.withType(Jar) { task -> |
| task.reproducibleFileOrder = true |
| task.preserveFileTimestamps = false |
| } |