[Coverage]Enable code coverage for linux_x64 and mingw_x64
This commit is contained in:
committed by
Sergey Bogolepov
parent
4782c2adeb
commit
8622eb3be4
+1
-2
@@ -3,10 +3,9 @@ Kotlin/Native has a code coverage support that is based on Clang's
|
||||
[Source-based Code Coverage](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html).
|
||||
|
||||
**Please note**:
|
||||
1. Coverage support is in it's very early days and is in active development. Known issues and restrictions are:
|
||||
1. Coverage support is in it's very early days and is in active development. Known issues are:
|
||||
* Coverage information may be inaccurate.
|
||||
* Line execution counts may be wrong.
|
||||
* Only macOS and iOS simulator binaries are supported.
|
||||
|
||||
2. Most of described functionality will be incorporated into Gradle plugin.
|
||||
|
||||
|
||||
+4
-1
@@ -79,6 +79,8 @@ internal class Linker(val context: Context) {
|
||||
executable = dylibPath.absolutePath
|
||||
}
|
||||
|
||||
val needsProfileLibrary = context.coverage.enabled
|
||||
|
||||
try {
|
||||
File(executable).delete()
|
||||
linker.linkCommands(objectFiles = objectFiles, executable = executable,
|
||||
@@ -87,7 +89,8 @@ internal class Linker(val context: Context) {
|
||||
BitcodeEmbedding.getLinkerOptions(context.config) +
|
||||
libraryProvidedLinkerFlags + frameworkLinkerArgs,
|
||||
optimize = optimize, debug = debug, kind = linkerOutput,
|
||||
outputDsymBundle = context.config.outputFile + ".dSYM").forEach {
|
||||
outputDsymBundle = context.config.outputFile + ".dSYM",
|
||||
needsProfileLibrary = needsProfileLibrary).forEach {
|
||||
it.logWith(context::log)
|
||||
it.execute()
|
||||
}
|
||||
|
||||
+3
-3
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.konan.file.File
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.jetbrains.kotlin.konan.target.supportsCodeCoverage
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
|
||||
/**
|
||||
@@ -43,14 +43,14 @@ internal class CoverageManager(val context: Context) {
|
||||
|
||||
init {
|
||||
if (enabled && !checkRestrictions()) {
|
||||
context.reportCompilationError("Coverage is only supported for macOS and iOS simulator binaries for now.")
|
||||
context.reportCompilationError("Coverage is not supported for ${context.config.target}.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkRestrictions(): Boolean {
|
||||
val isKindAllowed = context.config.produce.involvesBitcodeGeneration
|
||||
val target = context.config.target
|
||||
val isTargetAllowed = target == KonanTarget.MACOS_X64 || target == KonanTarget.IOS_X64
|
||||
val isTargetAllowed = target.supportsCodeCoverage()
|
||||
return isKindAllowed && isTargetAllowed
|
||||
}
|
||||
|
||||
|
||||
@@ -3756,7 +3756,7 @@ KotlinNativeTestKt.createTest(project, 'harmonyRegexTest', KonanGTest) { task ->
|
||||
|
||||
standaloneTest("coverage_basic_program") {
|
||||
|
||||
disabled = PlatformInfo.getTarget(project).name != "ios_x64" && PlatformInfo.getTarget(project).name != "macos_x64"
|
||||
disabled = !UtilsKt.testTargetSupportsCodeCoverage(project)
|
||||
|
||||
description = "Test that `-Xcoverage` generates correct __llvm_coverage information"
|
||||
|
||||
@@ -3783,7 +3783,7 @@ standaloneTest("coverage_basic_program") {
|
||||
|
||||
standaloneTest("coverage_basic_library") {
|
||||
|
||||
disabled = PlatformInfo.getTarget(project).name != "ios_x64" && PlatformInfo.getTarget(project).name != "macos_x64"
|
||||
disabled = !UtilsKt.testTargetSupportsCodeCoverage(project)
|
||||
|
||||
description = "Test that `-Xlibrary-to-cover` generates correct __llvm_coverage information"
|
||||
|
||||
|
||||
@@ -7,10 +7,7 @@ package org.jetbrains.kotlin
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.jetbrains.kotlin.konan.target.AppleConfigurables
|
||||
import org.jetbrains.kotlin.konan.target.HostManager
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.jetbrains.kotlin.konan.target.PlatformManager
|
||||
import org.jetbrains.kotlin.konan.target.*
|
||||
import java.io.FileInputStream
|
||||
import java.io.IOException
|
||||
import java.io.File
|
||||
@@ -54,6 +51,9 @@ val Project.globalTestArgs: List<String>
|
||||
fun Project.platformManager() = findProperty("platformManager") as PlatformManager
|
||||
fun Project.testTarget() = findProperty("target") as KonanTarget
|
||||
|
||||
fun Project.testTargetSupportsCodeCoverage(): Boolean =
|
||||
this.testTarget.supportsCodeCoverage()
|
||||
|
||||
/**
|
||||
* Ad-hoc signing of the specified path.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.jetbrains.kotlin.konan.target
|
||||
|
||||
fun KonanTarget.supportsCodeCoverage(): Boolean =
|
||||
this == KonanTarget.MINGW_X64 ||
|
||||
this == KonanTarget.LINUX_X64 ||
|
||||
this == KonanTarget.MACOS_X64 ||
|
||||
this == KonanTarget.IOS_X64
|
||||
@@ -75,10 +75,12 @@ abstract class LinkerFlags(val configurables: Configurables)
|
||||
|
||||
open val useCompilerDriverAsLinker: Boolean get() = false // TODO: refactor.
|
||||
|
||||
// TODO: Number of arguments is quite big. Better to pass args via object.
|
||||
abstract fun linkCommands(objectFiles: List<ObjectFile>, executable: ExecutableFile,
|
||||
libraries: List<String>, linkerArgs: List<String>,
|
||||
optimize: Boolean, debug: Boolean,
|
||||
kind: LinkerOutputKind, outputDsymBundle: String): List<Command>
|
||||
kind: LinkerOutputKind, outputDsymBundle: String,
|
||||
needsProfileLibrary: Boolean): List<Command>
|
||||
|
||||
abstract fun filterStaticLibraries(binaries: List<String>): List<String>
|
||||
|
||||
@@ -87,6 +89,16 @@ abstract class LinkerFlags(val configurables: Configurables)
|
||||
// Let's just pass them as absolute paths.
|
||||
return libraries
|
||||
}
|
||||
|
||||
protected open fun provideCompilerRtLibrary(libraryName: String): String? {
|
||||
System.err.println("Can't provide $libraryName.")
|
||||
return null
|
||||
}
|
||||
|
||||
// Code coverage requires this library.
|
||||
protected val profileLibrary: String? by lazy {
|
||||
provideCompilerRtLibrary("profile")
|
||||
}
|
||||
}
|
||||
|
||||
open class AndroidLinker(targetProperties: AndroidConfigurables)
|
||||
@@ -107,7 +119,8 @@ open class AndroidLinker(targetProperties: AndroidConfigurables)
|
||||
override fun linkCommands(objectFiles: List<ObjectFile>, executable: ExecutableFile,
|
||||
libraries: List<String>, linkerArgs: List<String>,
|
||||
optimize: Boolean, debug: Boolean,
|
||||
kind: LinkerOutputKind, outputDsymBundle: String): List<Command> {
|
||||
kind: LinkerOutputKind, outputDsymBundle: String,
|
||||
needsProfileLibrary: Boolean): List<Command> {
|
||||
if (kind == LinkerOutputKind.STATIC_LIBRARY)
|
||||
return staticGnuArCommands(ar, executable, objectFiles, libraries)
|
||||
|
||||
@@ -153,7 +166,7 @@ open class MacOSBasedLinker(targetProperties: AppleConfigurables)
|
||||
get() = this == KonanTarget.TVOS_X64 || this == KonanTarget.IOS_X64 ||
|
||||
this == KonanTarget.WATCHOS_X86 || this == KonanTarget.WATCHOS_X64
|
||||
|
||||
private fun provideCompilerRtLibrary(libraryName: String): String? {
|
||||
override fun provideCompilerRtLibrary(libraryName: String): String? {
|
||||
val prefix = when (target.family) {
|
||||
Family.IOS -> "ios"
|
||||
Family.WATCHOS -> "watchos"
|
||||
@@ -173,15 +186,6 @@ open class MacOSBasedLinker(targetProperties: AppleConfigurables)
|
||||
return if (dir != null) "$dir/lib/darwin/libclang_rt.$mangledLibraryName$prefix$suffix.a" else null
|
||||
}
|
||||
|
||||
private val compilerRtLibrary: String? by lazy {
|
||||
provideCompilerRtLibrary("")
|
||||
}
|
||||
|
||||
// Code coverage requires this library.
|
||||
private val profileLibrary: String? by lazy {
|
||||
provideCompilerRtLibrary("profile")
|
||||
}
|
||||
|
||||
private val osVersionMinFlags: List<String> by lazy {
|
||||
listOf(osVersionMinFlagLd, osVersionMin + ".0")
|
||||
}
|
||||
@@ -191,7 +195,8 @@ open class MacOSBasedLinker(targetProperties: AppleConfigurables)
|
||||
override fun linkCommands(objectFiles: List<ObjectFile>, executable: ExecutableFile,
|
||||
libraries: List<String>, linkerArgs: List<String>,
|
||||
optimize: Boolean, debug: Boolean, kind: LinkerOutputKind,
|
||||
outputDsymBundle: String): List<Command> {
|
||||
outputDsymBundle: String,
|
||||
needsProfileLibrary: Boolean): List<Command> {
|
||||
if (kind == LinkerOutputKind.STATIC_LIBRARY)
|
||||
return listOf(Command(libtool).apply {
|
||||
+"-static"
|
||||
@@ -214,7 +219,7 @@ open class MacOSBasedLinker(targetProperties: AppleConfigurables)
|
||||
if (dynamic) +linkerDynamicFlags
|
||||
+linkerKonanFlags
|
||||
if (compilerRtLibrary != null) +compilerRtLibrary!!
|
||||
if (profileLibrary != null) +profileLibrary!!
|
||||
if (needsProfileLibrary) +profileLibrary!!
|
||||
+libraries
|
||||
+linkerArgs
|
||||
+rpath(dynamic)
|
||||
@@ -231,6 +236,10 @@ open class MacOSBasedLinker(targetProperties: AppleConfigurables)
|
||||
return result
|
||||
}
|
||||
|
||||
private val compilerRtLibrary: String? by lazy {
|
||||
provideCompilerRtLibrary("")
|
||||
}
|
||||
|
||||
private fun rpath(dynamic: Boolean): List<String> = listOfNotNull(
|
||||
when (target.family) {
|
||||
Family.OSX -> "@executable_path/../Frameworks"
|
||||
@@ -295,12 +304,22 @@ open class LinuxBasedLinker(targetProperties: LinuxBasedConfigurables)
|
||||
private val linker = "$absoluteLlvmHome/bin/ld.lld"
|
||||
private val specificLibs = abiSpecificLibraries.map { "-L${absoluteTargetSysRoot}/$it" }
|
||||
|
||||
override fun provideCompilerRtLibrary(libraryName: String): String? {
|
||||
val targetSuffix = when (target) {
|
||||
KonanTarget.LINUX_X64 -> "x86_64"
|
||||
else -> error("$target is not supported.")
|
||||
}
|
||||
val dir = File("$absoluteLlvmHome/lib/clang/").listFiles.firstOrNull()?.absolutePath
|
||||
return if (dir != null) "$dir/lib/linux/libclang_rt.$libraryName-$targetSuffix.a" else null
|
||||
}
|
||||
|
||||
override fun filterStaticLibraries(binaries: List<String>) = binaries.filter { it.isUnixStaticLib }
|
||||
|
||||
override fun linkCommands(objectFiles: List<ObjectFile>, executable: ExecutableFile,
|
||||
libraries: List<String>, linkerArgs: List<String>,
|
||||
optimize: Boolean, debug: Boolean,
|
||||
kind: LinkerOutputKind, outputDsymBundle: String): List<Command> {
|
||||
kind: LinkerOutputKind, outputDsymBundle: String,
|
||||
needsProfileLibrary: Boolean): List<Command> {
|
||||
if (kind == LinkerOutputKind.STATIC_LIBRARY)
|
||||
return staticGnuArCommands(ar, executable, objectFiles, libraries)
|
||||
|
||||
@@ -331,6 +350,9 @@ open class LinuxBasedLinker(targetProperties: LinuxBasedConfigurables)
|
||||
if (!debug) +linkerNoDebugFlags
|
||||
if (dynamic) +linkerDynamicFlags
|
||||
+objectFiles
|
||||
// See explanation about `-u__llvm_profile_runtime` here:
|
||||
// https://github.com/llvm/llvm-project/blob/21e270a479a24738d641e641115bce6af6ed360a/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp#L930
|
||||
if (needsProfileLibrary) +listOf("-u__llvm_profile_runtime", profileLibrary!!)
|
||||
+linkerKonanFlags
|
||||
+listOf("-lgcc", "--as-needed", "-lgcc_s", "--no-as-needed",
|
||||
"-lc", "-lgcc", "--as-needed", "-lgcc_s", "--no-as-needed")
|
||||
@@ -352,10 +374,20 @@ open class MingwLinker(targetProperties: MingwConfigurables)
|
||||
|
||||
override fun filterStaticLibraries(binaries: List<String>) = binaries.filter { it.isWindowsStaticLib || it.isUnixStaticLib }
|
||||
|
||||
override fun provideCompilerRtLibrary(libraryName: String): String? {
|
||||
val targetSuffix = when (target) {
|
||||
KonanTarget.MINGW_X64 -> "x86_64"
|
||||
else -> error("$target is not supported.")
|
||||
}
|
||||
val dir = File("$absoluteLlvmHome/lib/clang/").listFiles.firstOrNull()?.absolutePath
|
||||
return if (dir != null) "$dir/lib/windows/libclang_rt.$libraryName-$targetSuffix.a" else null
|
||||
}
|
||||
|
||||
override fun linkCommands(objectFiles: List<ObjectFile>, executable: ExecutableFile,
|
||||
libraries: List<String>, linkerArgs: List<String>,
|
||||
optimize: Boolean, debug: Boolean,
|
||||
kind: LinkerOutputKind, outputDsymBundle: String): List<Command> {
|
||||
kind: LinkerOutputKind, outputDsymBundle: String,
|
||||
needsProfileLibrary: Boolean): List<Command> {
|
||||
if (kind == LinkerOutputKind.STATIC_LIBRARY)
|
||||
return staticGnuArCommands(ar, executable, objectFiles, libraries)
|
||||
|
||||
@@ -366,10 +398,14 @@ open class MingwLinker(targetProperties: MingwConfigurables)
|
||||
}.apply {
|
||||
+listOf("-o", executable)
|
||||
+objectFiles
|
||||
if (optimize) +linkerOptimizationFlags
|
||||
// --gc-sections flag may affect profiling.
|
||||
// See https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#drawbacks-and-limitations.
|
||||
// TODO: switching to lld may help.
|
||||
if (optimize && !needsProfileLibrary) +linkerOptimizationFlags
|
||||
if (!debug) +linkerNoDebugFlags
|
||||
if (dynamic) +linkerDynamicFlags
|
||||
+libraries
|
||||
if (needsProfileLibrary) +profileLibrary!!
|
||||
+linkerArgs
|
||||
+linkerKonanFlags
|
||||
})
|
||||
@@ -386,7 +422,8 @@ open class WasmLinker(targetProperties: WasmConfigurables)
|
||||
override fun linkCommands(objectFiles: List<ObjectFile>, executable: ExecutableFile,
|
||||
libraries: List<String>, linkerArgs: List<String>,
|
||||
optimize: Boolean, debug: Boolean,
|
||||
kind: LinkerOutputKind, outputDsymBundle: String): List<Command> {
|
||||
kind: LinkerOutputKind, outputDsymBundle: String,
|
||||
needsProfileLibrary: Boolean): List<Command> {
|
||||
if (kind != LinkerOutputKind.EXECUTABLE) throw Error("Unsupported linker output kind")
|
||||
|
||||
// TODO(horsh): maybe rethink it.
|
||||
@@ -433,7 +470,8 @@ open class ZephyrLinker(targetProperties: ZephyrConfigurables)
|
||||
override fun linkCommands(objectFiles: List<ObjectFile>, executable: ExecutableFile,
|
||||
libraries: List<String>, linkerArgs: List<String>,
|
||||
optimize: Boolean, debug: Boolean,
|
||||
kind: LinkerOutputKind, outputDsymBundle: String): List<Command> {
|
||||
kind: LinkerOutputKind, outputDsymBundle: String,
|
||||
needsProfileLibrary: Boolean): List<Command> {
|
||||
if (kind != LinkerOutputKind.EXECUTABLE) throw Error("Unsupported linker output kind: $kind")
|
||||
return listOf(Command(linker).apply {
|
||||
+listOf("-r", "--gc-sections", "--entry", "main")
|
||||
|
||||
Reference in New Issue
Block a user