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

Simplify output dir connection #3276

Merged
merged 2 commits into from
Jul 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,73 +1,24 @@
package com.apollographql.apollo3.gradle.api

import com.android.build.gradle.AppExtension
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.LibraryExtension
import com.android.build.gradle.TestedExtension
import com.android.build.gradle.api.BaseVariant
import com.apollographql.apollo3.compiler.capitalizeFirstLetter
import com.apollographql.apollo3.gradle.internal.ApolloGenerateSourcesTask
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.file.Directory
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.TaskContainer
import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

object AndroidProject {
fun onEachVariant(project: Project, withTestVariants: Boolean = false, block: (BaseVariant) -> Unit) {
val androidExtension = project.androidExtensionOrFail

val androidVariants = when (androidExtension) {
is LibraryExtension -> androidExtension.libraryVariants
is AppExtension -> androidExtension.applicationVariants
else -> {
// InstantAppExtension or something else we don't support yet
throw IllegalArgumentException("${androidExtension.javaClass.name} is not supported at the moment")
}
project.applicationVariants?.all {
block(it)
}

androidVariants.all {
project.libraryVariants?.all {
block(it)
}

if (withTestVariants && androidExtension is TestedExtension) {
androidExtension.testVariants.all {
if (withTestVariants) {
project.testVariants?.all {
block(it)
}
androidExtension.unitTestVariants.all {
project.unitTestVariants?.all {
block(it)
}
}
}

fun registerGeneratedDirectory(
project: Project,
variant: BaseVariant,
wire: Service.OutputDirWire
) {
val tasks = project.tasks

// This doesn't seem to do much besides addJavaSourceFoldersToModel
// variant.registerJavaGeneratingTask(codegenProvider.get(), codegenProvider.get().outputDir.get().asFile)

// This is apparently needed for intelliJ to find the generated files
// TODO: make this lazy (https://github.com/apollographql/apollo-android/issues/1454)
variant.addJavaSourceFoldersToModel(wire.outputDir.get().asFile)
// Tell the kotlin compiler to compile our files
tasks.named("compile${variant.name.capitalizeFirstLetter()}Kotlin").configure {
it.dependsOn(wire.task)
(it as KotlinCompile).source(wire.outputDir.get())
}
}

fun registerGeneratedDirectoryToAllVariants(
project: Project,
wire: Service.OutputDirWire,
) {
onEachVariant(project) { variant ->
registerGeneratedDirectory(project, variant, wire)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ interface ApolloExtension : Service {
* registers multiple services for an android project
*
* Android projects have variants for buildType/flavor combinations as well as test. Using this method will create a service for
* each variant and add the generated sources to the variant.
* each application/library variant and add the generated sources to the variant.
* A variant typically contains several source sets as described in
* https://developer.android.com/studio/build/build-variants?authuser=2#sourceset-build. This means you can put graphql files in
* several folders:
Expand Down Expand Up @@ -68,7 +68,7 @@ interface ApolloExtension : Service {
*
* If your project has more Kotlin source sets, services will be created for those as well
*/
fun createAllKotlinJvmSourceSetServices(sourceFolder: String, nameSuffix: String, action: Action<Service> = Action<Service> {})
fun createAllKotlinSourceSetServices(sourceFolder: String, nameSuffix: String, action: Action<Service> = Action<Service> {})

/**
* For Kotlin native projects, whether to link Sqlite (-lsqlite3). This is required by `apollo-normalized-cache-sqlite` but
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.apollographql.apollo3.gradle.api

import com.android.build.gradle.api.BaseVariant
import com.apollographql.apollo3.compiler.OperationIdGenerator
import com.apollographql.apollo3.compiler.OperationOutputGenerator
import com.apollographql.apollo3.compiler.PackageNameGenerator
Expand All @@ -10,7 +11,6 @@ import org.gradle.api.file.Directory
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFile
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.MapProperty
import org.gradle.api.provider.Property
Expand Down Expand Up @@ -51,7 +51,7 @@ interface Service {
/**
* Adds the given directory as a GraphQL source root
*
* Use [srcDir] if your files are outside of "src/main/graphql" or to have them in multiple folders.
* Use [srcDir] if your files are outside "src/main/graphql" or to have them in multiple folders.
*
*/
fun srcDir(directory: Any)
Expand Down Expand Up @@ -95,7 +95,7 @@ interface Service {
/**
* By default, Apollo uses `Sha256` hashing algorithm to generate an ID for the query.
* To provide a custom ID generation logic, pass an `instance` that implements the [OperationIdGenerator]. How the ID is generated is
* indifferent to the compiler. It can be an hashing algorithm or generated by a backend.
* indifferent to the compiler. It can be a hashing algorithm or generated by a backend.
*
* Example Md5 hash generator:
* ```groovy
Expand Down Expand Up @@ -214,7 +214,7 @@ interface Service {
val generateApolloMetadata: Property<Boolean>

/**
* A list of [Regex] patterns for input/scalar/enum types that should be generated whether or not they are used by queries/fragments
* A list of [Regex] patterns for input/scalar/enum types that should be generated whether they are used by queries/fragments
* in this module. When using multiple modules, Apollo Android will generate all the types by default in the root module
* because the root module doesn't know what types are going to be used by dependent modules. This can be prohibitive in terms
* of compilation speed for large projects. If that's the case, opt-in the types that are used by multiple dependent modules here.
Expand All @@ -231,7 +231,7 @@ interface Service {
* Default value is `false`, means only interfaces are been generated.
*
* Most of the time, fragment implementations are not needed because you can easily access fragments interfaces and read all
* data from your queries. They are needed if you want to be able to build fragments outside of an operation. For an exemple
* data from your queries. They are needed if you want to be able to build fragments outside an operation. For an exemple
* to programmatically build a fragment that is reused in another part of your code or to read and write fragments to the cache.
*/
val generateFragmentImplementations: Property<Boolean>
Expand All @@ -257,7 +257,7 @@ interface Service {

/**
* The directory where the generated models will be written. It's called [outputDir] but this an "input" parameter for the compiler
* If you want a [DirectoryProperty] that carries the task dependency, use [withOutputDir]
* If you want a [DirectoryProperty] that carries the task dependency, use [outputDirConnection]
*/
val outputDir: DirectoryProperty

Expand All @@ -270,7 +270,7 @@ interface Service {

/**
* The file where the operation output will be written. It's called [operationOutputFile] but this an "input" parameter for the compiler
* If you want a [RegularFileProperty] that carries the task dependency, use [withOperationOutput]
* If you want a [RegularFileProperty] that carries the task dependency, use [operationOutputConnection]
*/
val operationOutputFile: RegularFileProperty

Expand All @@ -290,12 +290,15 @@ interface Service {
fun registry(configure: Action<in Registry>)

/**
* overrides the way operationOutput is wired. Use this if you want to wire the generated operationOutput. By default, oeprationOutput
* is not generated and therefore not wired.
* overrides the way operationOutput is connected.
* Use this if you want to connect the generated operationOutput. For an example
* you can use this to send the modified queries to your backend for whitelisting
*
* By default, operationOutput is not connected
*/
fun withOperationOutput(action: Action<in OperationOutputWire>)
fun operationOutputConnection(action: Action<in OperationOutputConnection>)

class OperationOutputWire(
class OperationOutputConnection(
/**
* The task that produces operationOutput
*/
Expand All @@ -311,23 +314,76 @@ interface Service {
)

/**
* overrides the way the task is wired. Use this if you want to wire the generated sources to another task than the default destination.
* overrides the way the task is connected.
* Use this if you want to connect the generated sources to another task than the default destination.
*
* By default, the generated sources are wired to:
* By default, the generated sources are connected to:
* - main sourceSet for Kotlin projects
* - commonMain sourceSet for Kotlin multiplatform projects
* - all variants for Android projects
* - all application variants for Android projects
*/
fun withOutputDir(action: Action<in OutputDirWire>)
fun outputDirConnection(action: Action<in OutputDirConnection>)

class OutputDirWire(
/**
* The task that produces outputDir
*/
val task: TaskProvider<out Task>,
/**
* The directory where the generated models will be written
*/
val outputDir: Provider<Directory>,
)
/**
* An [OutputDirConnection] defines how the generated sources are connected to the rest of the
* build.
*
* It provides helpers for the most common options as well as direct access to an output [Provider]
* that will carry task dependency.
*/
interface OutputDirConnection {
/**
* Connects the generated sources to the given Kotlin source set.
* Throws if the Kotlin plugin is not applied
*
* @param name: the name of the source set. For an example, "commonTest"
*/
fun connectToKotlinSourceSet(name: String)

/**
* Connects the generated sources to the given Java source set.
* Throws if the Java plugin is not applied
*
* @param name: the name of the source set. For an example, "test"
*/
fun connectToJavaSourceSet(name: String)

/**
* Connects the generated sources to all the Android application variants.
* Throws if the Android Application plugin is not applied
*/
fun connectToAllAndroidApplicationVariants()

/**
* Connects the generated sources to all the Android application variants.
* Throws if the Android Library plugin is not applied
*/
fun connectToAllAndroidLibraryVariants()

/**
* Connects the generated sources to all the Android instrumented test variants.
* Throws if the Android plugin is not applied
*/
fun connectToAllAndroidInstrumentedTestVariants()

/**
* Connects the generated sources to all the Android unit test variants.
* Throws if the Android plugin is not applied
*/
fun connectToAllAndroidUnitTestVariants()

fun connectToAndroidVariant(variant: BaseVariant)

/**
* The directory where the generated models will be written.
* This provider carries task dependency information.
*/
val outputDir: Provider<Directory>

/**
* The task that produces outputDir. Usually this is not needed as [outputDir] carries
* task dependency.
*/
val task: TaskProvider<out Task>
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
package com.apollographql.apollo3.gradle.api

import com.android.build.gradle.AppExtension
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.LibraryExtension
import com.android.build.gradle.TestedExtension
import com.android.build.gradle.api.ApplicationVariant
import com.android.build.gradle.api.BaseVariant
import com.android.build.gradle.api.LibraryVariant
import com.android.build.gradle.api.TestVariant
import com.android.build.gradle.api.UnitTestVariant
import org.gradle.api.DomainObjectSet
import org.gradle.api.Project
import org.gradle.api.internal.DefaultDomainObjectSet
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension

val Project.kotlinMultiplatformExtension
get() = extensions.findByName("kotlin") as? KotlinMultiplatformExtension

val Project.kotlinJvmExtension
val Project.kotlinProjectExtension
get() = extensions.findByName("kotlin") as? KotlinProjectExtension

val Project.androidExtension
Expand All @@ -17,10 +27,34 @@ val Project.androidExtension
val Project.kotlinMultiplatformExtensionOrFail
get() = kotlinMultiplatformExtension ?: throw IllegalStateException("ApolloGraphQL: no 'kotlin' extension found. Did you apply the Kotlin multiplatform plugin?")

val Project.kotlinJvmExtensionOrFail
get() = kotlinJvmExtension ?: throw IllegalStateException("ApolloGraphQL: no 'kotlin' extension found. Did you apply the Kotlin jvm plugin?")
val Project.kotlinProjectExtensionOrThrow
get() = kotlinProjectExtension ?: throw IllegalStateException("ApolloGraphQL: no 'kotlin' extension found. Did you apply the Kotlin jvm plugin?")

val Project.androidExtensionOrFail
val Project.androidExtensionOrThrow
get() = androidExtension ?: throw IllegalStateException("ApolloGraphQL: no 'android' extension found. Did you apply the Android plugin?")

val Project.isKotlinMultiplatform get() = pluginManager.hasPlugin("org.jetbrains.kotlin.multiplatform")

val Project.libraryVariants: DomainObjectSet<LibraryVariant>?
get() {
return (androidExtensionOrThrow as? LibraryExtension)
?.libraryVariants
}

val Project.applicationVariants: DomainObjectSet<ApplicationVariant>?
get() {
return (androidExtensionOrThrow as? AppExtension)
?.applicationVariants
}

val Project.unitTestVariants: DomainObjectSet<UnitTestVariant>?
get() {
return (androidExtensionOrThrow as? TestedExtension)
?.unitTestVariants
}

val Project.testVariants: DomainObjectSet<TestVariant>?
get() {
return (androidExtensionOrThrow as? TestedExtension)
?.testVariants
}
Loading