in code/plugin/src/main/kotlin/com/android/gradle/replicator/codegen/plugin/CodegenPlugin.kt [33:189]
override fun apply(project: Project) {
val topProjectName by lazy {
var current = project
while (current.parent != null) current = current.parent!!
current.name
}
val generateResourcesTask = project.tasks.register(
"generateResources",
GenerateResources::class.java) { task ->
task.parameters.set(project.layout.projectDirectory.file("resource-metadata.json"))
task.seed.set(Random.nextInt())
task.androidOutputDirectory.set(project.layout.projectDirectory.dir("src/main/res"))
task.javaOutputDirectory.set(project.layout.projectDirectory.dir("src/main/resources"))
task.assetOutputDirectory.set(project.layout.projectDirectory.dir("src/main/assets"))
task.generationProperties.set(project.rootProject.layout.projectDirectory.file("generation.properties"))
}
val generateTask = project.tasks.register(
"generateCodegenParams",
GenerateCodegenParamsTask::class.java) { task ->
// elements we publish to others, all of our public methods must only use types coming from these elements.
val configApi = project.configurations.getAt("debugApiElements")
// construct our API modules list so we can filter them out from the implementation classpath.
val apiModules = mutableSetOf<String>()
val projectDependencies = mutableListOf<String>()
configApi.allDependencies.forEach { dependency ->
when (dependency) {
is ProjectDependency -> {
val projectName = dependency.group?.removePrefix(topProjectName)?.replace('.', ':')
apiModules.add("$projectName:${dependency.name}")
projectDependencies.add("$projectName:${dependency.name}")
}
is ExternalModuleDependency -> {
apiModules.add("${dependency.group}:${dependency.name}")
}
else -> {
println("Ignored API module $dependency")
}
}
}
// resolvable compile classpath, needs to be reduced.
val config = project.configurations.getAt("debugCompileClasspath")
val filter = { componentIdentifier: ComponentIdentifier ->
when (componentIdentifier) {
is ProjectComponentIdentifier -> apiModules.contains(componentIdentifier.projectPath)
is ModuleComponentIdentifier -> apiModules.contains("${componentIdentifier.group}:${componentIdentifier.module}")
else -> false
}
}
// filter for modules present in the API configuration of this module.
val apiFilter = { componentIdentifier: ComponentIdentifier ->
filter(componentIdentifier)
}
// filter for modules present in the Implementation configuration of this module
val implFilter = { componentIdentifier: ComponentIdentifier ->
!filter(componentIdentifier)
}
// we only care about classes artifact from aar.
val attributeFilter: Action<AttributeContainer> = Action<AttributeContainer> {
attributeContainer: AttributeContainer ->
attributeContainer.attribute(
ArtifactAttributes.ARTIFACT_FORMAT,
"android-classes-jar"
)
}
// create an artifact collection that contains only the project dependencies, external dependencies
// will be separated in a different artifact collection.
val apiProjectArtifacts = config.incoming.artifactView { t: ArtifactView.ViewConfiguration ->
t.componentFilter { componentIdentifier ->
when (componentIdentifier) {
is ProjectComponentIdentifier -> apiModules.contains(componentIdentifier.projectPath)
else -> false
}
}
t.attributes(attributeFilter)
}.artifacts
val implProjectArtifacts = config.incoming.artifactView { t: ArtifactView.ViewConfiguration ->
t.componentFilter { componentIdentifier ->
when (componentIdentifier) {
is ProjectComponentIdentifier -> !apiModules.contains(componentIdentifier.projectPath)
else -> false
}
}
t.attributes(attributeFilter)
}.artifacts
// create an artifact collection for both Api and Implementation.
val apiArtifacts = config.incoming.artifactView { t: ArtifactView.ViewConfiguration ->
t.componentFilter(apiFilter)
t.attributes(attributeFilter)
}.artifacts
val implArtifacts = config.incoming.artifactView { t: ArtifactView.ViewConfiguration ->
t.componentFilter(implFilter)
t.attributes(attributeFilter)
}.artifacts
// get the android APIs, this will be added to our classloader and API which is done automatically by AGP.
val androidApis = project.configurations.getAt("androidApis").incoming.files
config.allDependencies.forEach {
task.gradleDependencies.add("${it.group}:${it.name}:${it.version}")
}
val runtimeClasspath = project.configurations.getAt("debugRuntimeClasspath")
val runtimeClasspathArtifacts = runtimeClasspath.incoming.artifactView {
it.attributes(attributeFilter)
}.artifacts
task.runtimeClasspath.from(runtimeClasspathArtifacts.artifactFiles)
task.codeGeneratedModuleApiClasspath.from(apiProjectArtifacts.artifactFiles)
task.codeGeneratedModuleImplClasspath.from(implProjectArtifacts.artifactFiles)
task.apiJarFiles.from(apiArtifacts.artifactFiles)
task.implJarFiles.from(androidApis, implArtifacts.artifactFiles)
task.paramsFile.set(project.layout.buildDirectory.file("test.params"))
// Randomizer values should be set during project replication along the number of java and kotlin files.
task.seed.set(Random.nextInt())
task.moduleMetadataJson.set(project.file("module-metadata.json"))
// make sure we depend on our dependencies built artifacts so we have access to their generated classes.
projectDependencies.forEach {
// hack : use Variant API when dealing with android.
task.dependsOn(if (project.tasks.findByName("$it:assembleDebug") != null) {
"$it:assembleDebug"
} else "$it:assemble")
}
task.dependsOn(generateResourcesTask)
}
val generateCodeTask = project.tasks.register(
"generateCode",
GenerateCode::class.java) { task ->
task.parameters.set(generateTask.flatMap(GenerateCodegenParamsTask::paramsFile))
task.outputDirectory.set(
project.layout.projectDirectory.dir("src/main/java")
)
}
// generate the code before we start compiling it.
// hack, use variant API.
project.tasks.findByName("preBuild")!!.dependsOn(generateCodeTask)
}