Skip to content

Commit f9794d7

Browse files
authored
Merge pull request #85 from gradle/gh/fix-merge-issues
Task specific room schema dir is populated with existing schemas
2 parents 96968bd + 32eb8f1 commit f9794d7

File tree

3 files changed

+129
-18
lines changed

3 files changed

+129
-18
lines changed

src/main/groovy/org/gradle/android/workarounds/RoomSchemaLocationWorkaround.groovy

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ import java.lang.reflect.Field
3333
* aforementioned merge task. Note that this workaround only works with Kotlin Gradle plugin 1.3.70 or
3434
* higher.
3535
*
36+
* There are multiple issues related to these problems:
37+
* - https://issuetracker.google.com/issues/132245929
38+
* - https://issuetracker.google.com/issues/139438151
3639
*/
3740
@AndroidIssue(introducedIn = "3.5.0", fixedIn = [], link = "https://issuetracker.google.com/issues/132245929")
3841
class RoomSchemaLocationWorkaround implements Workaround {
@@ -102,11 +105,18 @@ class RoomSchemaLocationWorkaround implements Workaround {
102105

103106
// Register the generated schemas to be merged back to the original specified schema directory
104107
task.project.roomSchemaMergeLocations.registerMerge(schemaDestinationDir, taskSpecificSchemaDir)
108+
109+
// Seed the task-specific generated schema dir with the existing schemas
110+
task.doFirst {
111+
copyExistingSchemasToTaskSpecificTmpDir(task, schemaDestinationDir, taskSpecificSchemaDir)
112+
}
105113
}
106114
)
107115

108116
// Change the room schema location back to an absolute path right before the kapt tasks execute.
109-
// This allows other annotation processors that rely on the path being absolute to still function.
117+
// This allows other annotation processors that rely on the path being absolute to still function and
118+
// makes it resilient when the working directory is something other than the project directory.
119+
// See https://issuetracker.google.com/issues/139438151
110120
project.plugins.withId("kotlin-kapt") {
111121
project.tasks.withType(kaptWithoutKotlincTaskClass).configureEach { Task task ->
112122
task.finalizedBy mergeTask
@@ -121,7 +131,12 @@ class RoomSchemaLocationWorkaround implements Workaround {
121131
}
122132

123133
doFirst {
124-
setRoomSchemaLocationToTaskSpecificDirForKaptWithoutKotlinc(task)
134+
// Setup a task-specific tmp dir and populate it with the existing schemas
135+
setRoomSchemaLocationToTaskSpecificTmpDirForKaptWithoutKotlinc(task)
136+
}
137+
doLast {
138+
// Copy the generated schemas from the tmp dir to the tracked output dir
139+
copyGeneratedSchemasToOutputDir(task, getTaskSpecificSchemaTmpDir(task), getTaskSpecificSchemaDir(task))
125140
}
126141
}
127142

@@ -138,7 +153,12 @@ class RoomSchemaLocationWorkaround implements Workaround {
138153
}
139154

140155
doFirst {
141-
setRoomSchemaLocationToTaskSpecificDirForKaptWithKotlinc(task)
156+
// Setup a task-specific tmp dir and populate it with the existing schemas
157+
setRoomSchemaLocationToTaskSpecificTmpDirForKaptWithKotlinc(task)
158+
}
159+
doLast {
160+
// Copy the generated schemas from the tmp dir to the tracked output dir
161+
copyGeneratedSchemasToOutputDir(task, getTaskSpecificSchemaTmpDir(task), getTaskSpecificSchemaDir(task))
142162
}
143163
}
144164
}
@@ -149,6 +169,11 @@ class RoomSchemaLocationWorkaround implements Workaround {
149169
return new File(schemaBaseDir, task.name)
150170
}
151171

172+
static File getTaskSpecificSchemaTmpDir(Task task) {
173+
def schemaBaseDir = task.project.layout.buildDirectory.dir("roomSchemas/tmp").get().asFile
174+
return new File(schemaBaseDir, task.name)
175+
}
176+
152177
static def getCompilerPluginOptions(Task task) {
153178
def processorOptionsField = getAccessibleField(task.class, "processorOptions")
154179
def compilerPluginOptions = processorOptionsField.get(task)
@@ -208,20 +233,44 @@ class RoomSchemaLocationWorkaround implements Workaround {
208233
task.project.roomSchemaMergeLocations.registerMerge(schemaDestinationDir, taskSpecificSchemaDir)
209234
}
210235

211-
private static void setRoomSchemaLocationToTaskSpecificDirForKaptWithoutKotlinc(Task task) {
236+
private static void copyExistingSchemasToTaskSpecificTmpDir(Task task, File existingSchemaDir, File taskSpecificTmpDir) {
237+
// populate the task-specific tmp dir with any existing (non-generated) schemas
238+
// this allows other annotation processors that might operate on these schemas
239+
// to find them via the schema location argument
240+
task.project.sync {
241+
from existingSchemaDir
242+
into taskSpecificTmpDir
243+
}
244+
}
245+
246+
private static void copyGeneratedSchemasToOutputDir(Task task, File taskSpecificTmpDir, File outputDir) {
247+
// Copy the generated generated schemas from the task-specific tmp dir to the
248+
// task-specific output dir. This dance prevents the kapt task from clearing out
249+
// the existing schemas before the annotation processors run
250+
task.project.sync {
251+
from taskSpecificTmpDir
252+
into outputDir
253+
}
254+
}
255+
256+
private static void setRoomSchemaLocationToTaskSpecificTmpDirForKaptWithoutKotlinc(Task task) {
212257
def processorOptions = getCompilerPluginOptions(task)
213258
processorOptions.each { option ->
214259
if (option.key == ROOM_SCHEMA_LOCATION) {
215-
setOptionValue(option, getTaskSpecificSchemaDir(task).absolutePath)
260+
def taskSpecificTmpDir = getTaskSpecificSchemaTmpDir(task)
261+
copyExistingSchemasToTaskSpecificTmpDir(task, task.project.file(option.value), taskSpecificTmpDir)
262+
setOptionValue(option, taskSpecificTmpDir.absolutePath)
216263
}
217264
}
218265
}
219266

220-
private static void setRoomSchemaLocationToTaskSpecificDirForKaptWithKotlinc(Task task) {
267+
private static void setRoomSchemaLocationToTaskSpecificTmpDirForKaptWithKotlinc(Task task) {
221268
def encodedOptions = getEncodedCompilerPluginOptions(task)
222269
def apOptions = decode(encodedOptions.value)
223270
if (apOptions.containsKey(ROOM_SCHEMA_LOCATION)) {
224-
apOptions[ROOM_SCHEMA_LOCATION] = getTaskSpecificSchemaDir(task).absolutePath
271+
def taskSpecificTmpDir = getTaskSpecificSchemaTmpDir(task)
272+
copyExistingSchemasToTaskSpecificTmpDir(task, task.project.file(apOptions[ROOM_SCHEMA_LOCATION]), taskSpecificTmpDir)
273+
apOptions[ROOM_SCHEMA_LOCATION] = taskSpecificTmpDir.absolutePath
225274
setOptionValue(encodedOptions, encode(apOptions))
226275
}
227276
}
@@ -313,7 +362,6 @@ class RoomSchemaLocationWorkaround implements Workaround {
313362
@TaskAction
314363
void mergeSourcesToDestinations() {
315364
roomSchemaMergeLocations.mergeAssociations.each { destination, source ->
316-
project.delete(destination)
317365
println "Merging schemas to ${destination}"
318366
project.copy {
319367
duplicatesStrategy(DuplicatesStrategy.INCLUDE)

src/test/groovy/org/gradle/android/RoomSchemaLocationWorkaroundTest.groovy

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -215,23 +215,34 @@ class RoomSchemaLocationWorkaroundTest extends AbstractTest {
215215
216216
void assertKaptSchemaOutputsExist() {
217217
// Task specific schemas
218-
assert file("app/build/roomSchemas/kaptDebugKotlin/org.gradle.android.example.app.AppDatabase/2.json").exists()
219-
assert file("app/build/roomSchemas/kaptReleaseKotlin/org.gradle.android.example.app.AppDatabase/2.json").exists()
220-
assert file("library/build/roomSchemas/kaptDebugKotlin/org.gradle.android.example.library.AppDatabase/2.json").exists()
221-
assert file("library/build/roomSchemas/kaptReleaseKotlin/org.gradle.android.example.library.AppDatabase/2.json").exists()
218+
assertKaptSchemaOutputsExistFor("debug")
219+
assertKaptSchemaOutputsExistFor("release")
220+
}
221+
222+
void assertKaptSchemaOutputsExistFor(String variant) {
223+
assertSchemasExist("app", "build/roomSchemas/kapt${variant.capitalize()}Kotlin")
224+
assertSchemasExist("library", "build/roomSchemas/kapt${variant.capitalize()}Kotlin")
222225
}
223226
224227
void assertCompileJavaSchemaOutputsExist() {
225228
// Task specific schemas
226-
assert file("app/build/roomSchemas/compileDebugJavaWithJavac/org.gradle.android.example.app.AppDatabase/2.json").exists()
227-
assert file("app/build/roomSchemas/compileReleaseJavaWithJavac/org.gradle.android.example.app.AppDatabase/2.json").exists()
228-
assert file("library/build/roomSchemas/compileDebugJavaWithJavac/org.gradle.android.example.library.AppDatabase/2.json").exists()
229-
assert file("library/build/roomSchemas/compileReleaseJavaWithJavac/org.gradle.android.example.library.AppDatabase/2.json").exists()
229+
assertCompileJavaSchemaOutputExistsFor("debug")
230+
assertCompileJavaSchemaOutputExistsFor("release")
231+
}
232+
233+
void assertCompileJavaSchemaOutputExistsFor(String variant) {
234+
assertSchemasExist("app", "build/roomSchemas/compile${variant.capitalize()}JavaWithJavac")
235+
assertSchemasExist("library", "build/roomSchemas/compile${variant.capitalize()}JavaWithJavac")
230236
}
231237
232238
void assertMergedSchemaOutputsExist() {
233239
// Merged schemas
234-
assert file("app/schemas/org.gradle.android.example.app.AppDatabase/2.json").exists()
235-
assert file("library/schemas/org.gradle.android.example.library.AppDatabase/2.json").exists()
240+
assertSchemasExist("app", "schemas")
241+
assertSchemasExist("library", "schemas")
242+
}
243+
244+
void assertSchemasExist(String project, String baseDirPath) {
245+
assert file("${project}/${baseDirPath}/org.gradle.android.example.${project}.AppDatabase/1.json").exists()
246+
assert file("${project}/${baseDirPath}/org.gradle.android.example.${project}.AppDatabase/2.json").exists()
236247
}
237248
}

src/test/groovy/org/gradle/android/SimpleAndroidApp.groovy

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,9 @@ class SimpleAndroidApp {
257257
258258
@ColumnInfo(name = "last_name")
259259
public String lastName;
260+
261+
@ColumnInfo(name = "last_update")
262+
public int lastUpdate;
260263
}
261264
""".stripIndent()
262265

@@ -329,6 +332,55 @@ class SimpleAndroidApp {
329332
}
330333
""".stripIndent()
331334

335+
file("${basedir}/schemas/${packageName}.AppDatabase/1.json") << '''
336+
{
337+
"formatVersion": 1,
338+
"database": {
339+
"version": 1,
340+
"identityHash": "ce7bbbf6ddf39482eddc7248f4f61e8a",
341+
"entities": [
342+
{
343+
"tableName": "user",
344+
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER NOT NULL, `first_name` TEXT, `last_name` TEXT, PRIMARY KEY(`uid`))",
345+
"fields": [
346+
{
347+
"fieldPath": "uid",
348+
"columnName": "uid",
349+
"affinity": "INTEGER",
350+
"notNull": true
351+
},
352+
{
353+
"fieldPath": "firstName",
354+
"columnName": "first_name",
355+
"affinity": "TEXT",
356+
"notNull": false
357+
},
358+
{
359+
"fieldPath": "lastName",
360+
"columnName": "last_name",
361+
"affinity": "TEXT",
362+
"notNull": false
363+
}
364+
],
365+
"primaryKey": {
366+
"columnNames": [
367+
"uid"
368+
],
369+
"autoGenerate": false
370+
},
371+
"indices": [],
372+
"foreignKeys": []
373+
}
374+
],
375+
"views": [],
376+
"setupQueries": [
377+
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
378+
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ce7bbbf6ddf39482eddc7248f4f61e8a')"
379+
]
380+
}
381+
}
382+
'''.stripIndent()
383+
332384
file("${basedir}/src/main/res/layout/${resourceName}_layout.xml") << '''<?xml version="1.0" encoding="utf-8"?>
333385
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
334386
android:orientation="vertical"

0 commit comments

Comments
 (0)