override suspend fun run()

in sources/amper-cli/src/org/jetbrains/amper/tasks/native/NativeLinkTask.kt [70:192]


    override suspend fun run(dependenciesResult: List<TaskResult>, executionContext: TaskGraphExecutionContext): Result {
        val fragments = module.fragments.filter {
            it.platforms.contains(platform) && it.isTest == isTest
        }
        if (fragments.isEmpty()) {
            error("Zero fragments in module ${module.userReadableName} for platform $platform isTest=$isTest")
        }

        val externalKLibs = dependenciesResult
            .filterIsInstance<ResolveExternalDependenciesTask.Result>()
            .flatMap { it.compileClasspath } // runtime dependencies including transitive
            .distinct()
            .filterKLibs()
            .toList()

        val includeArtifactDependency = dependenciesResult
            .filterIsInstance<NativeCompileKlibTask.Result>()
            .firstOrNull { it.taskName == compileKLibTaskName }
            ?: error("The result of the klib compilation task (${compileKLibTaskName.name}) was not found")
        val includeArtifact = includeArtifactDependency.compiledKlib
        if (includeArtifact == null && isTest) {
            // We may skip linking for test specifically if there's no compiled code in the fragments.
            // Libraries are of no interest here because they can't contain any tests
            logger.info("No test code was found compiled for ${fragments.identificationPhrase()}, skipping linking")
            return Result(
                linkedBinary = null,
            )
        }

        val compileKLibDependencies = dependenciesResult
            .filterIsInstance<NativeCompileKlibTask.Result>()
            .filter { it.taskName != compileKLibTaskName }

        val exportedKLibDependencies = compileKLibDependencies
            .filter { it.taskName in exportedKLibTaskNames }
        check(exportedKLibDependencies.size == exportedKLibTaskNames.size)

        val compiledKLibs = compileKLibDependencies.mapNotNull { it.compiledKlib }
        val exportedKLibs = exportedKLibDependencies.mapNotNull { it.compiledKlib }

        val kotlinUserSettings = fragments.singleLeafFragment().serializableKotlinSettings()

        logger.debug("native link '${module.userReadableName}' -- ${fragments.joinToString(" ") { it.name }}")

        val entryPoints = if (module.type.isApplication()) {
            fragments.mapNotNull { it.settings.native?.entryPoint }.distinct()
        } else emptyList()
        if (entryPoints.size > 1) {
            // TODO raise this error in the frontend?
            userReadableError("Multiple entry points defined in ${fragments.identificationPhrase()}:\n${entryPoints.joinToString("\n")}")
        }
        val entryPoint = entryPoints.singleOrNull()

        val binaryOptions = if (compilationType == KotlinCompilationType.IOS_FRAMEWORK) {
            val appBundleId = dependenciesResult.requireSingleDependency<ManageXCodeProjectTask.Result>()
                .debugResolvedXcodeSettings.bundleId
            // Format framework's bundleId based on app's bundleId
            val frameworkBundleId = "$appBundleId.kotlin.framework"
            logger.debug("Using framework bundleId: `$frameworkBundleId`")
            mapOf("bundleId" to frameworkBundleId)
        } else emptyMap()

        val inputFiles = listOfNotNull(includeArtifact) + compiledKLibs
        val artifact = incrementalCache.execute(
            key = taskName.name,
            inputValues = mapOf(
                "kotlin.settings" to Json.encodeToString(kotlinUserSettings),
                "entry.point" to (entryPoint ?: ""),
                "task.output.root" to taskOutputRoot.path.pathString,
                "binary.options" to Json.encodeToString(binaryOptions),
            ),
            inputFiles = inputFiles,
        ) {
            cleanDirectory(taskOutputRoot.path)

            if (isTest) {
                logger.debug("Linking native test executable for module '${module.userReadableName}' on platform '${platform.pretty}'...")
            } else {
                val binaryKind = when(compilationType) {
                    KotlinCompilationType.IOS_FRAMEWORK -> "framework"
                    else -> "executable"
                }
                if (inputFiles.isEmpty()) {
                    val fragmentsString = module.fragmentsTargeting(platform, includeTestFragments = false)
                        .identificationPhrase()
                    userReadableError("Unable to link: there are no inputs (libraries or compiled source code). " +
                            "Ensure that there are sources and/or dependencies for $fragmentsString")
                }
                logger.info("Linking native '${platform.pretty}' $binaryKind for module '${module.userReadableName}'...")
            }

            val artifactPath = taskOutputRoot.path.resolve(compilationType.outputFilename(module, platform, isTest))

            val nativeCompiler = downloadNativeCompiler(kotlinUserSettings.compilerVersion, userCacheRoot, jdkProvider)
            val compilerPlugins = kotlinArtifactsDownloader.downloadCompilerPlugins(
                plugins = kotlinUserSettings.compilerPlugins,
            )
            val args = kotlinNativeCompilerArgs(
                buildType = buildType,
                kotlinUserSettings = kotlinUserSettings,
                compilerPlugins = compilerPlugins,
                entryPoint = entryPoint,
                libraryPaths = compiledKLibs + externalKLibs,
                exportedLibraryPaths = exportedKLibs,
                // no need to pass fragments nor sources, we only build from klibs
                fragments = emptyList(),
                sourceFiles = emptyList(),
                additionalSourceRoots = emptyList(),
                outputPath = artifactPath,
                compilationType = compilationType,
                binaryOptions = binaryOptions,
                include = includeArtifact,
            )

            nativeCompiler.compile(processRunner, args, tempRoot, module)

            return@execute IncrementalCache.ExecutionResult(listOf(artifactPath))
        }.outputFiles.single()

        return Result(
            linkedBinary = artifact,
        )
    }