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