Skip to content

Commit

Permalink
Merge pull request #5 from simonschiller/feature/gradle-property-api
Browse files Browse the repository at this point in the history
Switch to Gradle Property API
  • Loading branch information
simonschiller authored Dec 30, 2020
2 parents 8fe99cf + 8f9c176 commit 3e87aa4
Show file tree
Hide file tree
Showing 17 changed files with 130 additions and 72 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ before_install:
- echo "24333f8a63b6825ea9c5514f83c2829b004d1fee" >> "$ANDROID_HOME/licenses/android-sdk-license"

install:
- ./gradlew :prefiller:publishToMavenLocal --stacktrace
- ./gradlew :prefiller:publishToMavenLocal -PexcludeSample --stacktrace

script:
- ./gradlew test --stacktrace
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ First you need to add the Prefiller plugin to your project by adding this block

```groovy
plugins {
id "io.github.simonschiller.prefiller" version "0.1.0"
id "io.github.simonschiller.prefiller" version "1.0.0"
}
```

Expand All @@ -36,7 +36,7 @@ buildscript {
}
}
dependencies {
classpath "io.github.simonschiller:prefiller:0.1.0"
classpath "io.github.simonschiller:prefiller:1.0.0"
}
}
```
Expand Down Expand Up @@ -68,8 +68,8 @@ Lastly, you have to configure the Prefiller plugin in your `build.gradle` by lin
```groovy
prefiller {
database("people") {
classname = "com.example.PeopleDatabase"
script = file("$projectDir/src/main/sql/setup.sql")
classname.set("com.example.PeopleDatabase")
script.set(layout.projectDirectory.file("src/main/sql/setup.sql"))
}
}
```
Expand Down
2 changes: 1 addition & 1 deletion prefiller/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ plugins {
}

group = "io.github.simonschiller"
version = "0.1.0" // Also update the version in the README
version = "1.0.0" // Also update the version in the README

repositories {
google()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,65 +7,85 @@ import com.android.build.gradle.api.AndroidSourceSet
import com.android.build.gradle.api.BaseVariant
import com.android.builder.model.AndroidProject.FD_GENERATED
import org.gradle.api.Project
import org.gradle.api.file.Directory
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.kotlin.dsl.property
import org.gradle.util.VersionNumber
import org.jetbrains.kotlin.gradle.plugin.KaptExtension
import java.io.File
import java.util.*

class DatabaseConfig(val name: String) {
var classname: String? = null
var script: File? = null
@Suppress("UnstableApiUsage")
class DatabaseConfig internal constructor(val name: String, objects: ObjectFactory) {
val classname: Property<String> = objects.property()
val script: RegularFileProperty = objects.fileProperty()

internal fun registerTasks(project: Project, variant: BaseVariant) {
val variantName = variant.name.capitalize(Locale.ROOT)
val databaseName = name.capitalize(Locale.ROOT)
val taskName = "prefill$databaseName${variantName}Database"

// Validate configuration
val classname = classname ?: error("No classname configured for database $name")
val script = script ?: error("No script configured for database $name")
val classname = classname.orNull ?: error("No classname configured for database $name")
val script = script.asFile.orNull ?: error("No script configured for database $name")
check(script.isFile && script.canRead() && script.extension.equals("sql", ignoreCase = true)) {
"Cannot locate script at $script, make sure you specify a valid .sql file"
}

val databaseFile = File(project.buildDir, "${FD_GENERATED}/prefiller/${variant.name}/$name.db")
val databaseDir = project.layout.buildDirectory.dir("$FD_GENERATED/prefiller/${variant.name}")
val databaseFile = databaseDir.map { dir -> dir.file("$name.db") }
val schemaLocation = getSchemaLocation(project, variant)

// Register task for database
val task = project.tasks.register(taskName, PrefillerTask::class.java) {
description = "Generates and pre-fills ${this@DatabaseConfig.name} database for variant ${variant.name}"
schemaDirectory = schemaLocation.resolve(classname)
scriptFile = script
generatedDatabaseFile = databaseFile
scriptFile.set(script)
generatedDatabaseFile.set(databaseFile)

// On Gradle versions earlier than 6.3, we have to resolve the path manually due to a bug
val schemaDir = schemaLocation.map { parentDir ->
if (VersionNumber.parse(project.gradle.gradleVersion) < VersionNumber.parse("6.3")) {
val fileProvider = project.provider { parentDir.asFile.resolve(classname) }
project.layout.dir(fileProvider).get()
} else {
parentDir.dir(classname)
}
}
schemaDirectory.set(schemaDir)

// Room schema has to be generated before the Prefiller task runs
dependsOn(variant.javaCompileProvider)
}

// Register the output directory as asset directory and hook task into build process
val sourceSets = variant.sourceSets.filterIsInstance<AndroidSourceSet>()
sourceSets.last().assets.srcDir(databaseFile.parent) // Last source set is the most specific one
sourceSets.last().assets.srcDir(databaseDir) // Last source set is the most specific one
variant.mergeAssetsProvider.configure {
dependsOn(task)
}
}

// Read the Room schema location from the annotation processor options
private fun getSchemaLocation(project: Project, variant: BaseVariant): File {
val kaptExtension = try {
project.extensions.findByType(KaptExtension::class.java)
} catch (exception: NoClassDefFoundError) {
null // Kotlin plugin not applied -> Java project
}
private fun getSchemaLocation(project: Project, variant: BaseVariant): Provider<Directory> {
val fileProvider = project.provider {
val kaptExtension = try {
project.extensions.findByType(KaptExtension::class.java)
} catch (exception: NoClassDefFoundError) {
null // Kotlin plugin not applied -> Java project
}

val arguments = if (kaptExtension != null) {
val androidExtension = project.extensions.getByType(BaseExtension::class.java)
kaptExtension.getAdditionalArguments(project, variant, androidExtension)
} else {
variant.javaCompileOptions.annotationProcessorOptions.arguments
}
val arguments = if (kaptExtension != null) {
val androidExtension = project.extensions.getByType(BaseExtension::class.java)
kaptExtension.getAdditionalArguments(project, variant, androidExtension)
} else {
variant.javaCompileOptions.annotationProcessorOptions.arguments
}

// Try to read the schema location from the annotation processor arguments
val location = arguments?.get("room.schemaLocation") ?: error("Could not find schema location")
return File(location)
// Try to read the schema location from the annotation processor arguments
project.file(arguments?.get("room.schemaLocation") ?: error("Could not find schema location"))
}
return project.layout.dir(fileProvider)
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
package io.github.simonschiller.prefiller

import groovy.lang.Closure
import org.gradle.api.model.ObjectFactory
import org.gradle.util.ConfigureUtil

open class PrefillerExtension {
open class PrefillerExtension(private val objects: ObjectFactory) {
internal val databaseConfigs = mutableListOf<DatabaseConfig>()

// For configuration from Groovy DSL build files
fun database(name: String, config: Closure<Unit>) {
val databaseConfig = DatabaseConfig(name)
val databaseConfig = DatabaseConfig(name, objects)
ConfigureUtil.configure(config, databaseConfig)
addConfig(databaseConfig)
}

// For configuration from Kotlin DSL build files
fun database(name: String, config: DatabaseConfig.() -> Unit) {
val databaseConfig = DatabaseConfig(name).apply(config)
val databaseConfig = DatabaseConfig(name, objects).apply(config)
addConfig(databaseConfig)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,41 @@ import io.github.simonschiller.prefiller.internal.DatabasePopulator
import io.github.simonschiller.prefiller.internal.RoomSchemaLocator
import io.github.simonschiller.prefiller.internal.parser.StatementParserFactory
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.*
import java.io.File

@CacheableTask
@Suppress("UnstableApiUsage")
open class PrefillerTask : DefaultTask() {

@InputDirectory
@PathSensitive(PathSensitivity.RELATIVE)
lateinit var schemaDirectory: File
val schemaDirectory: DirectoryProperty = project.objects.directoryProperty()

@InputFile
@PathSensitive(PathSensitivity.RELATIVE)
lateinit var scriptFile: File
val scriptFile: RegularFileProperty = project.objects.fileProperty()

@OutputFile
lateinit var generatedDatabaseFile: File
val generatedDatabaseFile: RegularFileProperty = project.objects.fileProperty()

@TaskAction
fun generateDatabase() {

// Find the latest database schema file
val schemaLocator = RoomSchemaLocator()
val schemaFile = schemaLocator.findLatestRoomSchema(schemaDirectory)
val schemaFile = schemaLocator.findLatestRoomSchema(schemaDirectory.get().asFile)

// Parse the statements
val parserFactory = StatementParserFactory()
val setupStatements = parserFactory.createParser(schemaFile).parse()
val scriptStatements = parserFactory.createParser(scriptFile).parse()
val scriptStatements = parserFactory.createParser(scriptFile.get().asFile).parse()

// Clear the old and populate the new database
val databaseFile = generatedDatabaseFile.get().asFile
val databasePopulator = DatabasePopulator()
databasePopulator.populateDatabase(generatedDatabaseFile, setupStatements, overwrite = true)
databasePopulator.populateDatabase(generatedDatabaseFile, scriptStatements, overwrite = false)
databasePopulator.populateDatabase(databaseFile, setupStatements, overwrite = true)
databasePopulator.populateDatabase(databaseFile, scriptStatements, overwrite = false)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import io.github.simonschiller.prefiller.testutil.*
import io.github.simonschiller.prefiller.testutil.spec.KotlinProjectSpec
import io.github.simonschiller.prefiller.testutil.spec.NonAndroidProjectSpec
import io.github.simonschiller.prefiller.testutil.spec.ProjectSpec
import org.gradle.testkit.runner.TaskOutcome
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.extension.RegisterExtension
import org.junit.jupiter.params.ParameterizedTest
Expand Down Expand Up @@ -34,7 +35,6 @@ class PrefillerIntegrationTest {

val result = project.run("prefillPeopleDebugDatabase", expectFailure = true)
assertTrue(result.output.contains("'schemaDirectory' does not exist"))

}

@ParameterizedTest
Expand Down Expand Up @@ -85,4 +85,27 @@ class PrefillerIntegrationTest {
val databaseFile = mergedAssetsDir.resolve("debug/out/people.db")
assertTrue(databaseFile.exists())
}

@ParameterizedTest
@ArgumentsSource(TestVersions::class)
fun `Up-to-date checks work correctly`(gradleVersion: String, agpVersion: String) {
project.setup(gradleVersion, agpVersion, KotlinProjectSpec())

var result = project.run("prefillPeopleDebugDatabase")
assertEquals(TaskOutcome.SUCCESS, result.tasks.outcomeOf("prefillPeopleDebugDatabase"))

result = project.run("prefillPeopleDebugDatabase")
assertEquals(TaskOutcome.UP_TO_DATE, result.tasks.outcomeOf("prefillPeopleDebugDatabase"))

// Trigger change
project.scriptFile.appendText("""
INSERT INTO people(name, age) VALUES ("Jorja Maddox", 39);
""".trimIndent())

result = project.run("prefillPeopleDebugDatabase")
assertEquals(TaskOutcome.SUCCESS, result.tasks.outcomeOf("prefillPeopleDebugDatabase"))

result = project.run("prefillPeopleDebugDatabase")
assertEquals(TaskOutcome.UP_TO_DATE, result.tasks.outcomeOf("prefillPeopleDebugDatabase"))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package io.github.simonschiller.prefiller.testutil

import io.github.simonschiller.prefiller.testutil.spec.ProjectSpec
import org.gradle.testkit.runner.BuildResult
import org.gradle.testkit.runner.BuildTask
import org.gradle.testkit.runner.GradleRunner
import org.gradle.testkit.runner.TaskOutcome
import org.junit.jupiter.api.extension.AfterEachCallback
import org.junit.jupiter.api.extension.BeforeEachCallback
import org.junit.jupiter.api.extension.ExtensionContext
Expand Down Expand Up @@ -72,3 +74,8 @@ class ProjectExtension : BeforeEachCallback, AfterEachCallback {
}
}
}

fun List<BuildTask>.outcomeOf(taskName: String): TaskOutcome {
val task = singleOrNull { it.path == ":module:$taskName" } ?: error("Could not find task with name $taskName")
return task.outcome
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ open class JavaNoSchemaLocationProjectSpec : JavaProjectSpec() {
}
prefiller {
database("people") {
classname = "com.test.PeopleDatabase"
script = file(projectDir.absolutePath + "/setup.sql")
classname.set("com.test.PeopleDatabase")
script.set(layout.projectDirectory.file("setup.sql"))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ open class JavaProjectSpec : BaseProjectSpec() {
}
prefiller {
database("people") {
classname = "com.test.PeopleDatabase"
script = file(projectDir.absolutePath + "/setup.sql")
classname.set("com.test.PeopleDatabase")
script.set(layout.projectDirectory.file("setup.sql"))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ open class KotlinNoSchemaLocationProjectSpec : KotlinProjectSpec() {
}
prefiller {
database("people") {
classname = "com.test.PeopleDatabase"
script = file(projectDir.absolutePath + "/setup.sql")
classname.set("com.test.PeopleDatabase")
script.set(layout.projectDirectory.file("setup.sql"))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ open class KotlinProjectSpec : BaseProjectSpec() {
}
prefiller {
database("people") {
classname = "com.test.PeopleDatabase"
script = file(projectDir.absolutePath + "/setup.sql")
classname.set("com.test.PeopleDatabase")
script.set(layout.projectDirectory.file("setup.sql"))
}
}
Expand Down
8 changes: 4 additions & 4 deletions sample/java-groovy-dsl/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ dependencies {

prefiller {
database("customers") {
classname = "io.github.simonschiller.prefiller.sample.customer.CustomerDatabase"
script = file("$projectDir/src/main/sql/customers.sql")
classname.set("io.github.simonschiller.prefiller.sample.customer.CustomerDatabase")
script.set(layout.projectDirectory.file("src/main/sql/customers.sql"))
}

database("products") {
classname = "io.github.simonschiller.prefiller.sample.product.ProductDatabase"
script = file("$projectDir/src/main/sql/products.sql")
classname.set("io.github.simonschiller.prefiller.sample.product.ProductDatabase")
script.set(layout.projectDirectory.file("src/main/sql/products.sql"))
}
}
8 changes: 4 additions & 4 deletions sample/java-kotlin-dsl/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ dependencies {

prefiller {
database("customers") {
classname = "io.github.simonschiller.prefiller.sample.customer.CustomerDatabase"
script = file("$projectDir/src/main/sql/customers.sql")
classname.set("io.github.simonschiller.prefiller.sample.customer.CustomerDatabase")
script.set(layout.projectDirectory.file("src/main/sql/customers.sql"))
}

database("products") {
classname = "io.github.simonschiller.prefiller.sample.product.ProductDatabase"
script = file("$projectDir/src/main/sql/products.sql")
classname.set("io.github.simonschiller.prefiller.sample.product.ProductDatabase")
script.set(layout.projectDirectory.file("src/main/sql/products.sql"))
}
}
8 changes: 4 additions & 4 deletions sample/kotlin-groovy-dsl/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ dependencies {

prefiller {
database("customers") {
classname = "io.github.simonschiller.prefiller.sample.customer.CustomerDatabase"
script = file("$projectDir/src/main/sql/customers.sql")
classname.set("io.github.simonschiller.prefiller.sample.customer.CustomerDatabase")
script.set(layout.projectDirectory.file("src/main/sql/customers.sql"))
}

database("products") {
classname = "io.github.simonschiller.prefiller.sample.product.ProductDatabase"
script = file("$projectDir/src/main/sql/products.sql")
classname.set("io.github.simonschiller.prefiller.sample.product.ProductDatabase")
script.set(layout.projectDirectory.file("src/main/sql/products.sql"))
}
}
Loading

0 comments on commit 3e87aa4

Please sign in to comment.