blob: 4d1411cf98f2822f0b8322f35e237005248ec9d8 [file] [log] [blame]
/*
* Copyright (C) 2016 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 androidx.build.BuildOnServerKt
import androidx.build.LibraryType
import androidx.build.SupportConfig
import androidx.build.SdkHelperKt
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("AndroidXPlugin")
id("kotlin")
id("com.github.johnrengelman.shadow")
}
def antlrOut = "$buildDir/generated/antlr/grammar-gen/"
sourceSets {
main.java.srcDirs += "src/main/grammar-gen"
main.java.srcDirs += antlrOut
}
configurations {
/**
* shadowed is used for dependencies which we jarjar into the library jar instead of adding it
* as a pom dependency
*/
shadowed
// make sure shadowed dependencies show up as compileOnly so that normal compilation works
compileOnly.extendsFrom(shadowed)
// compiler tests run w/o shadowed classes so we should add those dependencies into test
// configuration
testCompile.extendsFrom(shadowed)
// for downstream tests, provide a configuration that includes the shadow output + other
// dependencies that are not shadowed
shadowAndImplementation.extendsFrom(shadow)
shadowAndImplementation.extendsFrom(implementation)
}
shadowJar {
// set classifier to empty string so that it doesn't append anything to the jar.
archiveClassifier = ""
configurations = [project.configurations.shadowed]
dependencies {
// antlr has dependencies on unrelated projects for its gui stuff, do not include them
exclude(dependency("org.abego.treelayout:.*"))
exclude(dependency("org.glassfish:.*"))
exclude(dependency("com.ibm.icu:.*"))
}
relocate("org.antlr", "androidx.room.jarjarred.org.antlr")
relocate("org.stringtemplate", "androidx.room.jarjarred.org.stringtemplate")
}
jar {
// set a classifier on this one so that the output does not clash with the output from
// shadowJar task. We should never use this directly as it won't have the shadowed classes that
// are necessary to run.
archiveClassifier = "before-jarjar"
}
configurations {
// replace the standard jar with the one built by 'shadowJar' in both api and runtime variants
apiElements.outgoing.artifacts.clear()
apiElements.outgoing.artifact(shadowJar) {
builtBy shadowJar
}
runtimeElements.outgoing.artifacts.clear()
runtimeElements.outgoing.artifact(shadowJar) {
builtBy shadowJar
}
}
dependencies {
implementation(project(":room:room-common"))
implementation(project(":room:room-migration"))
implementation(project(":room:room-compiler-processing"))
implementation(libs.kotlinStdlib)
implementation(libs.autoCommon)
implementation(libs.autoValueAnnotations)
implementation(libs.javapoet)
implementation(libs.kspApi)
shadowed(libs.antlr4)
implementation(libs.sqliteJdbc)
implementation(libs.kotlinMetadataJvm)
implementation(libs.apacheCommonsCodec)
implementation(libs.intellijAnnotations)
testImplementation(libs.truth)
testImplementation(libs.autoValue) // to access the processor in tests
testImplementation(libs.autoServiceAnnotations)
testImplementation(libs.autoService) // to access the processor in tests
testImplementation(projectOrArtifact(":paging:paging-common"))
testImplementation(project(":room:room-compiler-processing-testing"))
testImplementation(libs.junit)
testImplementation(libs.jsr250)
testImplementation(libs.mockitoCore)
testImplementation(libs.antlr4)
testImplementation(libs.kotlinCompilerEmbeddable)
testImplementation(fileTree(
dir: "${SdkHelperKt.getSdkPath(project)}/platforms/$SupportConfig.COMPILE_SDK_VERSION/",
include : "android.jar"
))
testImplementation(fileTree(
dir: "${new File(project(":room:room-runtime").buildDir, "libJar")}",
include : "*.jar"
))
testImplementation(fileTree(
dir: "${new File(project(":sqlite:sqlite").buildDir, "libJar")}",
include : "*.jar"
))
testImplementation(project(":internal-testutils-common"))
}
def generateAntlrTask = task("generateAntlrGrammar", type: GenerateAntlrGrammar) {
sqliteFile = file("$projectDir/SQLite.g4")
antlrClasspath = configurations.compileClasspath
outputDirectory = file(antlrOut)
}
@CacheableTask
abstract class GenerateAntlrGrammar extends DefaultTask {
@PathSensitive(PathSensitivity.NONE)
@InputFile
File sqliteFile
@Classpath
FileCollection antlrClasspath
@OutputDirectory
File outputDirectory
@Inject
abstract ExecOperations getExecOperations()
@Inject
public GenerateAntlrGrammar() {
description("Generates Antlr Grammar used by room")
group("build")
}
@TaskAction
void generateAntlrGrammar() {
execOperations.javaexec {
mainClass.set("org.antlr.v4.Tool")
classpath = antlrClasspath
args "SQLite.g4",
"-visitor",
"-o", new File(outputDirectory, "androidx/room/parser").path,
"-package", "androidx.room.parser"
}
}
}
/**
* Room compiler jarjars some dependencies. This task validates the published artifacts of room
* compiler to ensure dependencies are properly jarjarred.
*/
class CheckArtifactTask extends DefaultTask {
@InputFiles
FileCollection artifactInputs = project.objects.fileCollection()
@InputFile
File pomFile
@OutputFile
File result = new File(project.buildDir, "checkArtifactOutput.txt")
/**
* Checks the publish task's artifacts to make sure the classes.jar does include jarjarred
* antlr classes.
*/
def validatePublishTaskOutputs() {
if (artifactInputs.files.isEmpty()) {
throw new GradleException("Couldn't find the classes.jar for the room-compiler " +
"artifact. Ensure that publish is setup properly.")
}
artifactInputs.forEach {
validateJarContents(it)
}
}
/**
* Traverses the given jar file, looks for the classes that should be jarjarred and validates
* their location.
*/
def validateJarContents(File jarFile) {
Boolean found = false
ZipFile zip = new ZipFile(jarFile)
try {
for (Enumeration list = zip.entries(); list.hasMoreElements(); ) {
String entry = ((ZipEntry) list.nextElement()).name
if (!entry.endsWith(".class")) continue
if (entry.contains("org/antlr")) {
found = true
if (!entry.contains("androidx/room/jarjarred/org/antlr")) {
throw new GradleException("Any Antlr class included in the Room Compiler's" +
" jar file should be moved into androidx/room/jarjarred.\n" +
"Looks like $entry has not been moved")
}
}
if (!entry.startsWith("androidx/room/")) {
throw new GradleException("Found a class that is not in androidx.room " +
"package: $entry")
}
}
} finally {
zip.close()
}
if (!found) {
throw new GradleException("Couldn't find any Antlr classes in room-compiler artifact" +
".\n Antlr is jarjarred into room-compiler so there should be some files")
}
}
/**
* Checks the generated pom file to ensure it does not depend on any jarjarred dependencies
* but still depends on others.
*/
def validatePomTaskOutputs() {
if (!pomFile.canRead()) {
throw new GradleException("Cannot find the pom file for room-compiler")
}
def pomContents = pomFile.newReader().text
if (pomContents.contains("antlr")) {
throw new GradleException("Room-compiler pom file should not depend on antlr.\n" +
"Pom Contents:\n $pomContents")
}
if(!pomContents.contains("<artifactId>kotlin-stdlib</artifactId>")) {
throw new GradleException("room-compiler should depend on kotlin stdlib.\n" +
"Pom Contents:\n $pomContents")
}
}
@TaskAction
def validate() {
result.write("fail\n")
validatePublishTaskOutputs()
validatePomTaskOutputs()
// have a no-op output to make gradle happy w/ input/output checking.
result.write("ok\n")
}
}
def checkArtifactContentsTask = tasks.register("checkArtifactTask", CheckArtifactTask) {
it.artifactInputs.from {
((PublishToMavenRepository) project.tasks
.named("publishMavenPublicationToMavenRepository").get()).getPublication()
.artifacts.matching {
it.classifier == null
}.collect {
it.file
}
}
def pomTask = (GenerateMavenPom) project.tasks
.named("generatePomFileForMavenPublication").get()
it.pomFile = pomTask.destination
it.dependsOn("publishMavenPublicationToMavenRepository")
}
// make sure we validate published artifacts on the build server.
BuildOnServerKt.addToBuildOnServer(project, checkArtifactContentsTask)
tasks.findByName("compileKotlin").dependsOn(generateAntlrTask)
tasks.findByName("sourceJar").dependsOn(generateAntlrTask)
tasks.findByName("compileKotlin").dependsOn(":room:room-runtime:jarRelease")
tasks.findByName("compileKotlin").dependsOn(":sqlite:sqlite:jarRelease")
tasks.withType(KotlinCompile).configureEach {
kotlinOptions {
freeCompilerArgs += [
"-Xjvm-default=all",
"-Xopt-in=kotlin.RequiresOptIn",
"-Xopt-in=kotlin.contracts.ExperimentalContracts",
"-Xopt-in=androidx.room.compiler.processing.ExperimentalProcessingApi"
]
}
}
tasks.withType(Test).configureEach {
it.systemProperty("androidx.room.compiler.processing.strict", "true")
}
androidx {
name = "Android Room Compiler"
type = LibraryType.COMPILER_PLUGIN
mavenGroup = LibraryGroups.ROOM
inceptionYear = "2017"
description = "Android Room annotation processor"
}