Android / Gradle / Kotlin / Programming

Gradle「Cannot add a ~~~ already exists」の対処方法

下記のコードで発生しました(2行目のregister)

androidComponents.beforeVariants {
  android.sourceSets.register(it.name) {
      val buildDir = layout.buildDirectory.get().asFile
      java.srcDir(buildDir.resolve("generated/source/proto/${it.name}/java"))
      kotlin.srcDir(buildDir.resolve("generated/source/proto/${it.name}/kotlin"))
  }
}

原因は、register関数の第一引数に指定している名前のオブジェクトが、既に登録されているためのエラーです

register関数は、必要な時に設定を反映したオブジェクトが作成されるように登録をするための関数です
同じ名前で登録されているにもかかわらず、再度登録しようとすると「org.gradle.api.InvalidUserDataException: Cannot add a XXXX with name ‘register関数に渡した値’ as a XXXX with that name already exists.」
(XXXXに’register関数に渡した値’名で追加できません。XXXXにその名前は既に存在します)
エラーが出力されます

回避方法はこんな感じ

androidComponents.beforeVariants {
  val sourceSet = android.sourceSets.findByName(it.name)
  
  if (sourceSet == null) {
    android.sourceSets.register(it.name) {
      val buildDir = layout.buildDirectory.get().asFile
      java.srcDir(buildDir.resolve("generated/source/proto/${it.name}/java"))
      kotlin.srcDir(buildDir.resolve("generated/source/proto/${it.name}/kotlin"))
    }
  } else {
    android.sourceSets.getByName(it.name) {
      val buildDir = layout.buildDirectory.get().asFile
      java.srcDir(buildDir.resolve("generated/source/proto/${it.name}/java"))
      kotlin.srcDir(buildDir.resolve("generated/source/proto/${it.name}/kotlin"))
    }
  }
}

重要なのはifとその中身です
sourceSetにandroidのsourceSetsの中身を検索した結果を保持し、ifで中身がなければ登録、あれば変更としています
これでエラーを回避できます

ちなみに実際は下記のようにまとめてます(読み飛ばし可)

androidComponents.beforeVariants {
    android.sourceSets {
        addProto(
            variantName = it.name,
            androidSourceSetScope = this,
        )
    }
}

fun addProto(
    variantName: String,
    androidSourceSetScope: NamedDomainObjectContainer<out AndroidSourceSet>
) {
    val sourceSet = android.sourceSets.findByName(variantName)
    val buildDir = layout.buildDirectory.get().asFile
    val protoDirectoryPath = "generated/source/proto/"
    val javaDirectory = buildDir.resolve("${protoDirectoryPath + variantName}/java")
    val kotlinDirectory = buildDir.resolve("${protoDirectoryPath + variantName}/kotlin")
    val addProtoDirectory: AndroidSourceSet.() -> Unit = {
        java.srcDir(javaDirectory)
        kotlin.srcDir(kotlinDirectory)
    }

    if (sourceSet == null) {
        androidSourceSetScope {
            register(variantName) {
                addProtoDirectory()
            }
        }
    } else {
        androidSourceSetScope {
            getByName(variantName) {
                addProtoDirectory()
            }
        }
    }
}

何かあればお気軽にコメントまで!

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です