Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gradle tweaks #4222

Merged
merged 14 commits into from
Jun 15, 2023
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package app.cash.sqldelight.gradle

import app.cash.sqldelight.VERSION
import app.cash.sqldelight.core.SqlDelightCompilationUnit
import app.cash.sqldelight.core.SqlDelightDatabaseProperties
import app.cash.sqldelight.core.SqlDelightEnvironment
Expand All @@ -26,21 +25,16 @@ import java.util.ServiceLoader

@CacheableTask
abstract class GenerateMigrationOutputTask : SqlDelightWorkerTask() {
@Suppress("unused")
// Required to invalidate the task on version updates.
@Input
val pluginVersion = VERSION
hfhbd marked this conversation as resolved.
Show resolved Hide resolved

@get:OutputDirectory
var outputDirectory: File? = null
abstract val outputDirectory: DirectoryProperty

@get:Input abstract val projectName: Property<String>

@get:Nested abstract var properties: SqlDelightDatabasePropertiesImpl

@get:Nested abstract var compilationUnit: SqlDelightCompilationUnitImpl

@get:Input abstract var migrationOutputExtension: String
@get:Input abstract val migrationOutputExtension: Property<String>

@TaskAction
fun generateSchemaFile() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package app.cash.sqldelight.gradle

import app.cash.sqldelight.VERSION
import app.cash.sqldelight.core.SqlDelightCompilationUnit
import app.cash.sqldelight.core.SqlDelightDatabaseProperties
import app.cash.sqldelight.core.SqlDelightEnvironment
Expand Down Expand Up @@ -30,21 +29,16 @@ import java.util.ServiceLoader

@CacheableTask
abstract class GenerateSchemaTask : SqlDelightWorkerTask() {
@Suppress("unused")
// Required to invalidate the task on version updates.
@Input
val pluginVersion = VERSION

@get:OutputDirectory
var outputDirectory: File? = null
abstract val outputDirectory: DirectoryProperty

@get:Input abstract val projectName: Property<String>

@get:Nested abstract var properties: SqlDelightDatabasePropertiesImpl

@get:Nested abstract var compilationUnit: SqlDelightCompilationUnitImpl

@Input var verifyMigrations: Boolean = false
@get:Input abstract val verifyMigrations: Property<Boolean>

@TaskAction
fun generateSchemaFile() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ abstract class SqlDelightDatabase @Inject constructor(
abstract val srcDirs: ConfigurableFileCollection
val deriveSchemaFromMigrations: Property<Boolean> = project.objects.property(Boolean::class.java).convention(false)
val verifyMigrations: Property<Boolean> = project.objects.property(Boolean::class.java).convention(false)
val verifyDefinitions: Property<Boolean> = project.objects.property(Boolean::class.java).convention(true)
abstract val migrationOutputDirectory: DirectoryProperty
val migrationOutputFileFormat: Property<String> = project.objects.property(String::class.java).convention(".sql")
val generateAsync: Property<Boolean> = project.objects.property(Boolean::class.java).convention(false)
Expand Down Expand Up @@ -135,7 +136,7 @@ abstract class SqlDelightDatabase @Inject constructor(
}

internal fun getProperties(): SqlDelightDatabasePropertiesImpl {
val packageName = requireNotNull(packageName.getOrNull()) { "property packageName for $name database must be provided" }
require(packageName.isPresent) { "property packageName for $name database must be provided" }

check(!recursionGuard) { "Found a circular dependency in $project with database $name" }
recursionGuard = true
Expand All @@ -156,7 +157,7 @@ abstract class SqlDelightDatabase @Inject constructor(

try {
return SqlDelightDatabasePropertiesImpl(
packageName = packageName,
packageName = packageName.get(),
compilationUnits = sources.map { source ->
SqlDelightCompilationUnitImpl(
name = source.name,
Expand Down Expand Up @@ -219,17 +220,17 @@ abstract class SqlDelightDatabase @Inject constructor(
it.projectName.set(project.name)
it.properties = getProperties()
it.compilationUnit = getProperties().compilationUnits.single { it.name == source.name }
it.outputDirectory = source.outputDir
it.outputDirectory.set(source.outputDir)
it.source(sourceFiles)
it.include("**${File.separatorChar}*.$SQLDELIGHT_EXTENSION")
it.include("**${File.separatorChar}*.$MIGRATION_EXTENSION")
it.group = SqlDelightPlugin.GROUP
it.description = "Generate ${source.name} Kotlin interface for $name"
it.verifyMigrations = verifyMigrations.get()
it.verifyMigrations.set(verifyMigrations)
it.classpath.setFrom(intellijEnv, migrationEnv, configuration)
}

val outputDirectoryProvider: Provider<File> = task.map { it.outputDirectory!! }
val outputDirectoryProvider: Provider<File> = task.flatMap { it.outputDirectory.asFile }

// Add the source dependency on the generated code.
// Use a Provider generated from the task to carry task dependencies
Expand All @@ -250,7 +251,7 @@ abstract class SqlDelightDatabase @Inject constructor(
addSquashTask(sourceFiles, source)
}

if (migrationOutputDirectory.getOrNull() != null) {
if (migrationOutputDirectory.isPresent) {
addMigrationOutputTasks(sourceFiles, source)
}
}
Expand All @@ -267,26 +268,27 @@ abstract class SqlDelightDatabase @Inject constructor(
it.source(sourceSet)
it.include("**${File.separatorChar}*.$SQLDELIGHT_EXTENSION")
it.include("**${File.separatorChar}*.$MIGRATION_EXTENSION")
it.workingDirectory = File(project.buildDir, "sqldelight/migration_verification/${source.name.capitalize()}$name")
it.workingDirectory.set(File(project.buildDir, "sqldelight/migration_verification/${source.name.capitalize()}$name"))
it.group = SqlDelightPlugin.GROUP
it.description = "Verify ${source.name} $name migrations and CREATE statements match."
it.properties = getProperties()
it.verifyMigrations = verifyMigrations.get()
it.verifyMigrations.set(verifyMigrations)
it.verifyDefinitions.set(verifyDefinitions)
it.classpath.setFrom(intellijEnv, migrationEnv, configuration)
}

if (schemaOutputDirectory.getOrNull() != null) {
if (schemaOutputDirectory.isPresent) {
project.tasks.register("generate${source.name.capitalize()}${name}Schema", GenerateSchemaTask::class.java) {
it.projectName.set(project.name)
it.compilationUnit = getProperties().compilationUnits.single { it.name == source.name }
it.outputDirectory = schemaOutputDirectory.get().asFile
it.outputDirectory.set(schemaOutputDirectory)
it.source(sourceSet)
it.include("**${File.separatorChar}*.$SQLDELIGHT_EXTENSION")
it.include("**${File.separatorChar}*.$MIGRATION_EXTENSION")
it.group = SqlDelightPlugin.GROUP
it.description = "Generate a .db file containing the current $name schema for ${source.name}."
it.properties = getProperties()
it.verifyMigrations = verifyMigrations.get()
it.verifyMigrations.set(verifyMigrations)
it.classpath.setFrom(intellijEnv, migrationEnv, configuration)
}
}
Expand All @@ -307,8 +309,8 @@ abstract class SqlDelightDatabase @Inject constructor(
it.compilationUnit = getProperties().compilationUnits.single { it.name == source.name }
it.source(sourceSet)
it.include("**${File.separatorChar}*.$MIGRATION_EXTENSION")
it.migrationOutputExtension = migrationOutputFileFormat.get()
it.outputDirectory = migrationOutputDirectory.get().asFile
it.migrationOutputExtension.set(migrationOutputFileFormat)
it.outputDirectory.set(migrationOutputDirectory)
it.group = SqlDelightPlugin.GROUP
it.description = "Generate valid sql migration files for ${source.name} $name."
it.properties = getProperties()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,14 @@ import app.cash.sqldelight.core.SqlDelightPropertiesFile
import app.cash.sqldelight.gradle.android.packageName
import app.cash.sqldelight.gradle.android.sqliteVersion
import app.cash.sqldelight.gradle.kotlin.linkSqlite
import com.android.build.gradle.api.AndroidBasePlugin
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.Dependency
import org.gradle.tooling.provider.model.ToolingModelBuilder
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.dsl.KotlinJsProjectExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetContainer
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject

Expand All @@ -42,40 +41,42 @@ abstract class SqlDelightPlugin : Plugin<Project> {
@get:Inject
abstract val registry: ToolingModelBuilderRegistry

private lateinit var extension: SqlDelightExtension

override fun apply(project: Project) {
require(GradleVersion.current() >= GradleVersion.version(MIN_GRADLE_VERSION)) {
"SQLDelight requires Gradle version $MIN_GRADLE_VERSION or greater."
}

extension = project.extensions.create("sqldelight", SqlDelightExtension::class.java)
val extension = project.extensions.create("sqldelight", SqlDelightExtension::class.java).apply {
linkSqlite.convention(true)
}

val androidPluginHandler = { _: Plugin<*> ->
project.plugins.withType(AndroidBasePlugin::class.java) {
android.set(true)
project.afterEvaluate {
project.setupSqlDelightTasks(afterAndroid = true)
project.setupSqlDelightTasks(afterAndroid = true, extension)
}
}
project.plugins.withId("com.android.application", androidPluginHandler)
project.plugins.withId("com.android.library", androidPluginHandler)
project.plugins.withId("com.android.instantapp", androidPluginHandler)
project.plugins.withId("com.android.feature", androidPluginHandler)
project.plugins.withId("com.android.dynamic-feature", androidPluginHandler)

val kotlinPluginHandler = { _: Plugin<*> -> kotlin.set(true) }
project.plugins.withId("org.jetbrains.kotlin.multiplatform", kotlinPluginHandler)
project.plugins.withId("org.jetbrains.kotlin.android", kotlinPluginHandler)
project.plugins.withId("org.jetbrains.kotlin.jvm", kotlinPluginHandler)
project.plugins.withId("org.jetbrains.kotlin.js", kotlinPluginHandler)
project.plugins.withId("kotlin2js", kotlinPluginHandler)

project.plugins.withType(KotlinBasePlugin::class.java) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should note that KotlinBasePlugin was only added in Kotlin 1.7.0, so if compatibility with older versions is needed, this should be reverted.

https://kotlinlang.org/docs/whatsnew17.html#updates-in-the-kotlin-gradle-plugin-api

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I am fine with requiring Kotlin 1.7.0, could you split this change from the PR to merge the other Gradle plugin changes and we can discuss the Kotlin requirement in separate PR?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think its fine, I can't imagine folks upgrading SQLDelight to not also be upgrading kotlin

kotlin.set(true)
}

project.tasks.register("generateSqlDelightInterface") {
it.group = GROUP
it.description = "Aggregation task which runs every interface generation task for every given source"
}

project.tasks.register("verifySqlDelightMigration") {
it.group = GROUP
it.description = "Aggregation task which runs every migration task for every given source"
}

project.afterEvaluate {
project.setupSqlDelightTasks(afterAndroid = false)
project.setupSqlDelightTasks(afterAndroid = false, extension)
}
}

private fun Project.setupSqlDelightTasks(afterAndroid: Boolean) {
private fun Project.setupSqlDelightTasks(afterAndroid: Boolean, extension: SqlDelightExtension) {
if (android.get() && !afterAndroid) return

check(kotlin.get()) {
Expand All @@ -84,64 +85,36 @@ abstract class SqlDelightPlugin : Plugin<Project> {
}

val isMultiplatform = project.plugins.hasPlugin("org.jetbrains.kotlin.multiplatform")
val isJsOnly = if (isMultiplatform) false else project.plugins.hasPlugin("org.jetbrains.kotlin.js")

val needsAsyncRuntime = extension.databases.any { it.generateAsync.get() }
val runtimeDependencies = mutableListOf<Dependency>().apply {
val runtimeDependencies = buildList {
add(project.dependencies.create("app.cash.sqldelight:runtime:$VERSION"))
if (needsAsyncRuntime) add(project.dependencies.create("app.cash.sqldelight:async-extensions:$VERSION"))
}

// Add the runtime dependency.
when {
isMultiplatform -> {
val sourceSets =
project.extensions.getByType(KotlinMultiplatformExtension::class.java).sourceSets
val sourceSet = (sourceSets.getByName("commonMain") as DefaultKotlinSourceSet)
project.configurations.getByName(sourceSet.apiConfigurationName)
.dependencies.addAll(runtimeDependencies)
}
isJsOnly -> {
val sourceSets =
project.extensions.getByType(KotlinJsProjectExtension::class.java).sourceSets
val sourceSet = (sourceSets.getByName("main") as DefaultKotlinSourceSet)
project.configurations.getByName(sourceSet.apiConfigurationName)
.dependencies.addAll(runtimeDependencies)
}
else -> {
project.configurations.getByName("api").dependencies.addAll(runtimeDependencies)
}
}
val sourceSetName = if (isMultiplatform) "commonMain" else "main"
val sourceSetApiConfigName =
project.extensions.getByType(KotlinSourceSetContainer::class.java).sourceSets.getByName(sourceSetName).apiConfigurationName
project.configurations.getByName(sourceSetApiConfigName).dependencies.addAll(runtimeDependencies)

if (extension.linkSqlite.getOrElse(true)) {
if (extension.linkSqlite.get()) {
project.linkSqlite()
}

extension.run {
if (databases.isEmpty() && android.get() && !isMultiplatform) {
// Default to a database for android named "Database" to keep things simple.
databases.add(
objects.newInstance(SqlDelightDatabase::class.java, project, "Database").apply {
packageName.set(project.packageName())
project.sqliteVersion()?.let(::dialect)
},
)
databases.create("Database") { database ->
database.packageName.set(project.packageName())
project.sqliteVersion()?.let(database::dialect)
}
} else if (databases.isEmpty()) {
logger.warn("SQLDelight Gradle plugin was applied but there are no databases set up.")
}

project.tasks.register("generateSqlDelightInterface") {
it.group = GROUP
it.description = "Aggregation task which runs every interface generation task for every given source"
}

project.tasks.register("verifySqlDelightMigration") {
it.group = GROUP
it.description = "Aggregation task which runs every migration task for every given source"
}

databases.forEach { database ->
if (database.packageName.getOrNull() == null && android.get() && !isMultiplatform) {
if (!database.packageName.isPresent && android.get() && !isMultiplatform) {
database.packageName.set(project.packageName())
}
if (!database.addedDialect && android.get() && !isMultiplatform) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/
package app.cash.sqldelight.gradle

import app.cash.sqldelight.VERSION
import app.cash.sqldelight.core.SqlDelightCompilationUnit
import app.cash.sqldelight.core.SqlDelightDatabaseProperties
import app.cash.sqldelight.core.SqlDelightEnvironment
Expand All @@ -41,26 +40,20 @@ import org.gradle.api.tasks.SkipWhenEmpty
import org.gradle.api.tasks.TaskAction
import org.gradle.workers.WorkAction
import org.gradle.workers.WorkParameters
import java.io.File
import java.util.ServiceLoader

@CacheableTask
abstract class SqlDelightTask : SqlDelightWorkerTask() {
@Suppress("unused")
// Required to invalidate the task on version updates.
@Input
val pluginVersion = VERSION

@get:OutputDirectory
var outputDirectory: File? = null
abstract val outputDirectory: DirectoryProperty

@get:Input abstract val projectName: Property<String>

@get:Nested abstract var properties: SqlDelightDatabasePropertiesImpl

@get:Nested abstract var compilationUnit: SqlDelightCompilationUnitImpl

@Input var verifyMigrations: Boolean = false
@get:Input abstract val verifyMigrations: Property<Boolean>

@TaskAction
fun generateSqlDelightFiles() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package app.cash.sqldelight.gradle

import app.cash.sqldelight.VERSION
import app.cash.sqldelight.core.SqlDelightCompilationUnit
import app.cash.sqldelight.core.SqlDelightDatabaseProperties
import app.cash.sqldelight.core.SqlDelightEnvironment
Expand Down Expand Up @@ -38,23 +37,18 @@ import kotlin.collections.ArrayList

@CacheableTask
abstract class VerifyMigrationTask : SqlDelightWorkerTask() {
@Suppress("unused")
// Required to invalidate the task on version updates.
@Input
val pluginVersion = VERSION

@get:Input abstract val projectName: Property<String>

/** Directory where the database files are copied for the migration scripts to run against. */
@get:Internal abstract var workingDirectory: File
@get:Internal abstract val workingDirectory: DirectoryProperty

@get:Nested abstract var properties: SqlDelightDatabasePropertiesImpl

@get:Nested abstract var compilationUnit: SqlDelightCompilationUnitImpl

@Input var verifyMigrations: Boolean = false
@get:Input abstract val verifyMigrations: Property<Boolean>

@Input var verifyDefinitions: Boolean = true
@get:Input abstract val verifyDefinitions: Property<Boolean>

@get:Input abstract val driverProperties: MapProperty<String, String>

Expand All @@ -74,7 +68,7 @@ abstract class VerifyMigrationTask : SqlDelightWorkerTask() {
it.verifyMigrations.set(verifyMigrations)
it.compilationUnit.set(compilationUnit)
it.verifyDefinitions.set(verifyDefinitions)
it.driverProperties.set(driverProperties.get())
it.driverProperties.set(driverProperties)
it.outputFile.set(getDummyOutputFile())
}
}
Expand Down
Loading