override fun apply()

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)
    }