Skip to content

Commit 3832f7f

Browse files
authored
[plugin] support android compile targets (ExpediaGroup#1231)
* [plugin] support android compile targets Default (JVM) build use `compileKotlin`/`compileTestKotlin` tasks. Android builds use variants which has their own targets (e.g. `compileDebugKotlin`). Since our tasks are finalized by the compile tasks we cannot apply it on android builds as default JVM tasks dont exist. * fix compile dependency Gradle scans all decorated plugin/tasks/extensions classes to find all the injection points, apply validations etc. This means that we cannot have any method signatures in the plugin code that references compile only dependencies. Simple fix is to move the affected code to a separate file. See: gradle/gradle#8411 for details. * fix android build dependency * parameterize android plugin version
1 parent 73c5bcc commit 3832f7f

File tree

14 files changed

+432
-15
lines changed

14 files changed

+432
-15
lines changed

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ allprojects {
2828

2929
repositories {
3030
mavenCentral()
31+
google()
3132
mavenLocal {
3233
content {
3334
includeGroup("com.expediagroup")

gradle.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ kotlinVersion = 1.5.21
1919
kotlinCoroutinesVersion = 1.5.0
2020
kotlinxSerializationVersion = 1.2.1
2121

22+
androidPluginVersion = 4.2.2
2223
classGraphVersion = 4.8.110
2324
federationGraphQLVersion = 0.6.4
2425
graphQLJavaVersion = 16.2

plugins/graphql-kotlin-gradle-plugin/build.gradle.kts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,22 @@ plugins {
55
id("com.gradle.plugin-publish")
66
}
77

8+
val androidPluginVersion: String by project
9+
val junitVersion: String by project
810
val kotlinCoroutinesVersion: String by project
911
val wireMockVersion: String by project
1012
val mustacheVersion: String by project
1113

1214
dependencies {
1315
implementation(kotlin("gradle-plugin-api"))
16+
compileOnly("com.android.tools.build:gradle:$androidPluginVersion")
1417

1518
compileOnly(project(":graphql-kotlin-client-generator"))
1619
compileOnly(project(":graphql-kotlin-sdl-generator"))
1720

1821
testImplementation("com.github.tomakehurst:wiremock-jre8:$wireMockVersion")
1922
testImplementation("com.github.spullara.mustache.java:compiler:$mustacheVersion")
23+
testImplementation("org.junit.jupiter:junit-jupiter-params:$junitVersion")
2024
}
2125

2226
gradlePlugin {
@@ -97,6 +101,7 @@ tasks {
97101
val kotlinVersion: String by project
98102
val junitVersion: String by project
99103
val springBootVersion: String by project
104+
systemProperty("androidPluginVersion", androidPluginVersion)
100105
systemProperty("kotlinVersion", kotlinVersion)
101106
systemProperty("springBootVersion", springBootVersion)
102107
systemProperty("junitVersion", junitVersion)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
plugins {
2+
id("com.android.application")
3+
id("com.expediagroup.graphql")
4+
kotlin("android")
5+
kotlin("plugin.serialization")
6+
}
7+
8+
android {
9+
compileSdkVersion(30)
10+
compileOptions {
11+
sourceCompatibility = JavaVersion.VERSION_1_8
12+
targetCompatibility = JavaVersion.VERSION_1_8
13+
}
14+
kotlinOptions {
15+
jvmTarget = "1.8"
16+
}
17+
}
18+
19+
val graphQLKotlinVersion = System.getenv("GRAPHQL_KOTLIN_VERSION") ?: "5.0.0-SNAPSHOT"
20+
val kotlinVersion = System.getenv("KOTLIN_VERSION") ?: "1.5.21"
21+
dependencies {
22+
implementation("com.expediagroup:graphql-kotlin-ktor-client:$graphQLKotlinVersion")
23+
implementation("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
24+
}
25+
26+
graphql {
27+
client {
28+
schemaFileName = "${project.projectDir}/schema.graphql"
29+
packageName = "com.expediagroup.android.generated"
30+
serializer = com.expediagroup.graphql.plugin.gradle.config.GraphQLSerializer.KOTLINX
31+
}
32+
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
schema {
2+
query: Query
3+
mutation: Mutation
4+
}
5+
"Directs the executor to include this field or fragment only when the `if` argument is true"
6+
directive @include(
7+
"Included when true."
8+
if: Boolean!
9+
) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
10+
"Directs the executor to skip this field or fragment when the `if`'argument is true."
11+
directive @skip(
12+
"Skipped when true."
13+
if: Boolean!
14+
) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
15+
"Marks the field or enum value as deprecated"
16+
directive @deprecated(
17+
"The reason for the deprecation"
18+
reason: String! = "No longer supported"
19+
) on FIELD_DEFINITION | ENUM_VALUE
20+
"Very basic interface"
21+
interface BasicInterface {
22+
"Unique identifier of an interface"
23+
id: Int!
24+
"Name field"
25+
name: String!
26+
}
27+
"Very basic union of BasicObject and ComplexObject"
28+
union BasicUnion = BasicObject | ComplexObject
29+
"Some basic description"
30+
type BasicObject {
31+
id: Int!
32+
"Object name"
33+
name: String!
34+
}
35+
"""
36+
Multi line description of a complex type.
37+
This is a second line of the paragraph.
38+
This is final line of the description.
39+
"""
40+
type ComplexObject {
41+
"Some additional details"
42+
details: DetailsObject!
43+
"Some unique identifier"
44+
id: Int!
45+
"Some object name"
46+
name: String!
47+
"""
48+
Optional value
49+
Second line of the description
50+
"""
51+
optional: String
52+
}
53+
"Inner type object description"
54+
type DetailsObject {
55+
"Boolean flag"
56+
flag: Boolean!
57+
"Unique identifier"
58+
id: Int!
59+
"Actual detail value"
60+
value: String!
61+
}
62+
"Example interface implementation where value is an integer"
63+
type FirstInterfaceImplementation implements BasicInterface {
64+
"Unique identifier of the first implementation"
65+
id: Int!
66+
"Custom field integer value"
67+
intValue: Int!
68+
"Name of the first implementation"
69+
name: String!
70+
}
71+
type Mutation {
72+
"Example of a muation"
73+
simpleMutation(update: SimpleArgumentInput!): BasicObject!
74+
}
75+
"Example of an object self-referencing itself"
76+
type NestedObject {
77+
"Children elements"
78+
children: [NestedObject!]!
79+
"Unique identifier"
80+
id: Int!
81+
"Name of the object"
82+
name: String!
83+
}
84+
type Query {
85+
"Query returning an object that references another object"
86+
complexObjectQuery: ComplexObject!
87+
"Deprecated query that should not be used anymore"
88+
deprecatedQuery: String! @deprecated(reason : "old query should not be used")
89+
"Query that returns enum value"
90+
enumQuery: CustomEnum!
91+
"Query that accepts some input arguments"
92+
inputObjectQuery(criteria: SimpleArgumentInput!): Boolean!
93+
"Query returning an interface"
94+
interfaceQuery: BasicInterface!
95+
"Query returning list of simple objects"
96+
listQuery: [BasicObject!]!
97+
"Query returning object referencing itself"
98+
nestedObjectQuery: NestedObject!
99+
"Query that returns wrapper object with all supported scalar types"
100+
scalarQuery: ScalarWrapper!
101+
"Query returning union"
102+
unionQuery: BasicUnion!
103+
}
104+
"Wrapper that holds all supported scalar types"
105+
type ScalarWrapper {
106+
"A signed 32-bit nullable integer value"
107+
count: Int
108+
"Custom scalar"
109+
custom: UUID!
110+
"ID represents unique identifier that is not intended to be human readable"
111+
id: ID!
112+
"UTF-8 character sequence"
113+
name: String!
114+
"A nullable signed double-precision floating-point value"
115+
rating: Float
116+
"Either true or false"
117+
valid: Boolean!
118+
}
119+
"Example interface implementation where value is a float"
120+
type SecondInterfaceImplementation implements BasicInterface {
121+
"Custom field float value"
122+
floatValue: Float!
123+
"Unique identifier of the second implementation"
124+
id: Int!
125+
"Name of the second implementation"
126+
name: String!
127+
}
128+
"Custom enum description"
129+
enum CustomEnum {
130+
"First enum value"
131+
ONE
132+
"Third enum value"
133+
THREE @deprecated(reason : "only goes up to two")
134+
"Second enum value"
135+
TWO
136+
}
137+
"Custom scalar representing UUID"
138+
scalar UUID
139+
"Test input object"
140+
input SimpleArgumentInput {
141+
"Maximum value for test criteria"
142+
max: Float
143+
"Minimum value for test criteria"
144+
min: Float
145+
"New value to be set"
146+
newName: String
147+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.expediagroup.graphqlkotlin">
3+
4+
</manifest>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
query ExampleQuery($simpleCriteria: SimpleArgumentInput!) {
2+
enumQuery
3+
scalarQuery {
4+
count
5+
custom
6+
id
7+
name
8+
rating
9+
valid
10+
}
11+
listQuery {
12+
id
13+
name
14+
}
15+
complexObjectQuery {
16+
id
17+
name
18+
optional
19+
details {
20+
id
21+
flag
22+
value
23+
}
24+
}
25+
interfaceQuery {
26+
__typename
27+
id
28+
name
29+
... on FirstInterfaceImplementation {
30+
intValue
31+
}
32+
... on SecondInterfaceImplementation {
33+
floatValue
34+
}
35+
}
36+
unionQuery {
37+
__typename
38+
... on BasicObject {
39+
id
40+
name
41+
}
42+
... on ComplexObject {
43+
id
44+
name
45+
optional
46+
details {
47+
id
48+
flag
49+
value
50+
}
51+
}
52+
}
53+
inputObjectQuery(criteria: $simpleCriteria)
54+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
buildscript {
2+
repositories {
3+
mavenCentral()
4+
google()
5+
mavenLocal {
6+
content {
7+
includeGroup("com.expediagroup")
8+
}
9+
}
10+
}
11+
12+
val androidPluginVersion = System.getenv("ANDROID_PLUGIN_VERSION") ?: "4.2.2"
13+
val graphQLKotlinVersion = System.getenv("GRAPHQL_KOTLIN_VERSION") ?: "5.0.0-SNAPSHOT"
14+
val kotlinVersion = System.getenv("KOTLIN_VERSION") ?: "1.5.21"
15+
dependencies {
16+
classpath("com.android.tools.build:gradle:$androidPluginVersion")
17+
classpath("com.expediagroup:graphql-kotlin-gradle-plugin:$graphQLKotlinVersion")
18+
classpath("org.jetbrains.kotlin:kotlin-serialization:$kotlinVersion")
19+
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
20+
}
21+
}
22+
23+
allprojects {
24+
repositories {
25+
mavenCentral()
26+
google()
27+
mavenLocal {
28+
content {
29+
includeGroup("com.expediagroup")
30+
}
31+
}
32+
}
33+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
org.gradle.jvmargs=-Xmx3g -XX:MaxMetaspaceSize=1g
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
rootProject.name = "androidGraphQLClient"
2+
3+
include(":app")

plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/GraphQLGradlePlugin.kt

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,6 @@ class GraphQLGradlePlugin : Plugin<Project> {
7878
project.tasks.register(INTROSPECT_SCHEMA_TASK_NAME, GraphQLIntrospectSchemaTask::class.java)
7979
}
8080

81-
private fun configureProjectSourceSet(project: Project, outputDirectory: DirectoryProperty, targetSourceSet: String = "main") {
82-
val sourceSetContainer = project.findProperty("sourceSets") as? SourceSetContainer
83-
sourceSetContainer?.findByName(targetSourceSet)?.java?.srcDir(outputDirectory)
84-
}
85-
8681
private fun processExtensionConfiguration(project: Project, extension: GraphQLPluginExtension) {
8782
if (extension.isClientConfigurationAvailable()) {
8883
if (extension.clientExtension.packageName != null) {
@@ -137,23 +132,29 @@ class GraphQLGradlePlugin : Plugin<Project> {
137132
}
138133

139134
private fun configureTaskClasspaths(project: Project) {
135+
val isAndroidProject = project.plugins.hasPlugin("com.android.application") || project.plugins.hasPlugin("com.android.library")
136+
val clientGeneratingTaskNames = mutableListOf<GraphQLGenerateClientTask>()
137+
val testClientGeneratingTaskNames = mutableListOf<GraphQLGenerateTestClientTask>()
138+
140139
project.tasks.withType(GraphQLDownloadSDLTask::class.java).configureEach { downloadSDLTask ->
141140
val configuration = project.configurations.getAt(GENERATE_CLIENT_CONFIGURATION)
142141
downloadSDLTask.pluginClasspath.setFrom(configuration)
143142
}
144143
project.tasks.withType(GraphQLGenerateClientTask::class.java).configureEach { generateClientTask ->
144+
clientGeneratingTaskNames.add(generateClientTask)
145145
val configuration = project.configurations.getAt(GENERATE_CLIENT_CONFIGURATION)
146146
generateClientTask.pluginClasspath.setFrom(configuration)
147-
148-
generateClientTask.finalizedBy(project.tasks.named("compileKotlin"))
149-
configureProjectSourceSet(project = project, outputDirectory = generateClientTask.outputDirectory)
147+
if (!isAndroidProject) {
148+
configureDefaultProjectSourceSet(project = project, outputDirectory = generateClientTask.outputDirectory)
149+
}
150150
}
151151
project.tasks.withType(GraphQLGenerateTestClientTask::class.java).configureEach { generateTestClientTask ->
152+
testClientGeneratingTaskNames.add(generateTestClientTask)
152153
val configuration = project.configurations.getAt(GENERATE_CLIENT_CONFIGURATION)
153154
generateTestClientTask.pluginClasspath.setFrom(configuration)
154-
155-
generateTestClientTask.finalizedBy(project.tasks.named("compileTestKotlin"))
156-
configureProjectSourceSet(project = project, outputDirectory = generateTestClientTask.outputDirectory, targetSourceSet = "test")
155+
if (!isAndroidProject) {
156+
configureDefaultProjectSourceSet(project = project, outputDirectory = generateTestClientTask.outputDirectory, targetSourceSet = "test")
157+
}
157158
}
158159
project.tasks.withType(GraphQLIntrospectSchemaTask::class.java).configureEach { introspectionTask ->
159160
val configuration = project.configurations.getAt(GENERATE_CLIENT_CONFIGURATION)
@@ -167,8 +168,18 @@ class GraphQLGradlePlugin : Plugin<Project> {
167168

168169
val configuration = project.configurations.getAt(GENERATE_SDL_CONFIGURATION)
169170
generateSDLTask.pluginClasspath.setFrom(configuration)
170-
171171
generateSDLTask.dependsOn(project.tasks.named("compileKotlin"))
172172
}
173+
174+
if (isAndroidProject) {
175+
// Android plugins are eagerly configured so just adding the tasks outputs to the source set is not enough,
176+
// we also need to explicitly configure compile dependencies
177+
configureAndroidCompileTasks(project, clientGeneratingTaskNames, testClientGeneratingTaskNames)
178+
}
179+
}
180+
181+
private fun configureDefaultProjectSourceSet(project: Project, outputDirectory: DirectoryProperty, targetSourceSet: String = "main") {
182+
val sourceSetContainer = project.findProperty("sourceSets") as? SourceSetContainer
183+
sourceSetContainer?.findByName(targetSourceSet)?.java?.srcDir(outputDirectory)
173184
}
174185
}

0 commit comments

Comments
 (0)