override suspend fun run()

in sources/amper-cli/src/org/jetbrains/amper/tasks/jvm/JvmCompileTask.kt [133:271]


    override suspend fun run(dependenciesResult: List<TaskResult>, executionContext: TaskGraphExecutionContext): TaskResult {
        require(fragments.isNotEmpty()) {
            "fragments list is empty for jvm compile task, module=${module.userReadableName}"
        }

        logger.debug("compile ${module.userReadableName} -- ${fragments.userReadableList()}")

        val mavenDependencies = dependenciesResult
            .filterIsInstance<ResolveExternalDependenciesTask.Result>()
            .singleOrNull()
            ?: error("Expected one and only one dependency on (${ResolveExternalDependenciesTask.Result::class.java.simpleName}) input, but got: ${dependenciesResult.joinToString { it.javaClass.simpleName }}")

        val compileModuleDependencies = dependenciesResult.filterIsInstance<Result>()
        val javaAnnotationProcessorClasspath = dependenciesResult
            .filterIsInstance<JavaAnnotationProcessorClasspathTask.Result>()
            .singleOrNull()
            ?.processorClasspath
            ?: emptyList()

        val productionJvmCompileResult = if (isTest) {
            compileModuleDependencies.firstOrNull { it.module == module && !it.isTest }
                ?: error("jvm compilation result from production compilation result was not found for module=${module.userReadableName}, task=$taskName")
        } else null

        val userSettings = fragments.singleLeafFragment().serializableCompilationSettings()

        val additionalClasspath = dependenciesResult.filterIsInstance<AdditionalClasspathProvider>().flatMap { it.compileClasspath }
        val classpath =
            compileModuleDependencies.flatMap { it.classesOutputRoots } + mavenDependencies.compileClasspath + additionalClasspath

        // Collect additional source roots.
        val additionalArtifactSources = additionalKotlinJavaSourceDirs.map { artifact ->
            SourceRoot(
                fragmentName = artifact.fragmentName,
                path = artifact.path,
            )
        }
        val additionalSourceRootsFromMaven = dependenciesResult
            .filterIsInstance<MavenPhaseResult>()
            .flatMap { it.sourceRoots }
            .distinctBy { it.path } // Need to remove duplicates, because a same path can be provided by multiple providers.
        
        val additionalSources = additionalArtifactSources + additionalSourceRootsFromMaven

        val additionalResources = additionalResourcesDirs.map { artifact ->
            SourceRoot(
                fragmentName = artifact.fragmentName,
                path = artifact.path,
            )
        }

        val jdk = jdkProvider.getJdkOrUserError(jdkSettings = module.jdkSettings)

        val javaAnnotationProcessorsGeneratedDir =
            fragments.singleLeafFragment().javaAnnotationProcessingGeneratedSourcesPath(buildOutputRoot.path)

        val inputValues = mapOf(
            "jdk.version" to jdk.version,
            "jdk.home" to jdk.homeDir.pathString,
            "user.settings" to Json.encodeToString(userSettings),
            "task.output.root" to taskOutputRoot.pathString,
            "target.platforms" to module.leafPlatforms.map { it.name }.sorted().joinToString(),
            "java.annotation.processor.generated.dir" to javaAnnotationProcessorsGeneratedDir.pathString
        )

        val sources = fragments.flatMap { it.sourceRoots }.map { it.toAbsolutePath() } + additionalSources.map { it.path }
        val resources = fragments.map { it.resourcesPath }.map { it.toAbsolutePath() } + additionalResources.map { it.path }
        val inputFiles = sources + resources + classpath + javaAnnotationProcessorClasspath

        val result = incrementalCache.execute(taskName.name, inputValues, inputFiles) {
            cleanDirectory(javaAnnotationProcessorsGeneratedDir)
            if (!shouldCompileJavaIncrementally(userSettings.java, javaAnnotationProcessorClasspath)) {
                cleanDirectory(taskOutputRoot)
            }
            javaCompilerOutputRoot.createDirectories()
            kotlinCompilerOutputRoot.createDirectories()
            resourcesRoot.createDirectories()

            val nonEmptySourceDirs = sources
                .filter {
                    when {
                        it.isDirectory() -> it.listDirectoryEntries().isNotEmpty()
                        it.exists() ->
                            error("Source directory at '$it' exists, but it's not a directory, this is currently unsupported")
                        else -> false
                    }
                }

            val outputPaths = mutableListOf<Path>()
            outputPaths.add(javaCompilerOutputRoot.toAbsolutePath())
            outputPaths.add(kotlinCompilerOutputRoot.toAbsolutePath())

            if (nonEmptySourceDirs.isNotEmpty()) {
                compileSources(
                    jdk = jdk,
                    sourceDirectories = nonEmptySourceDirs,
                    additionalSources = additionalSources,
                    userSettings = userSettings,
                    classpath = classpath,
                    friendPaths = productionJvmCompileResult?.classesOutputRoots.orEmpty(),
                    javaAnnotationProcessorClasspath = javaAnnotationProcessorClasspath,
                    javaAnnotationProcessorsGeneratedDir = javaAnnotationProcessorsGeneratedDir,
                    tempRoot = tempRoot,
                )
                if (javaAnnotationProcessorsGeneratedDir.exists()) {
                    outputPaths.add(javaAnnotationProcessorsGeneratedDir)
                }
            } else {
                logger.debug("No sources were found for ${fragments.identificationPhrase()}, skipping compilation")
            }

            val presentResources = resources.filter { it.exists() }
            for (resource in presentResources) {
                val dest = if (resource.isDirectory()) {
                    resourcesRoot
                } else {
                    resourcesRoot / resource.fileName
                }
                logger.debug("Copying resources from '{}' to '{}'...", resource, dest)

                // if we compile incrementally, then we don't clean the output dir => overwrite instead of failing that a file exists
                val overwrite = shouldCompileJavaIncrementally(userSettings.java, javaAnnotationProcessorClasspath)
                BuildPrimitives.copy(from = resource, to = dest, overwrite = overwrite)
            }

            return@execute IncrementalCache.ExecutionResult(outputPaths)
        }

        return Result(
            classesOutputRoots = listOf(
                javaCompilerOutputRoot,
                kotlinCompilerOutputRoot,
                resourcesRoot
            ).map { it.toAbsolutePath() },
            module = module,
            isTest = isTest,
            changes = result.changes,
        )
    }