From 96e5b2be795c67fa81905a37dcc5f8ac81adbaca Mon Sep 17 00:00:00 2001 From: Sergey Bogolepov Date: Sat, 9 Dec 2023 14:11:50 +0200 Subject: [PATCH] [Native][tests] Improve clang invocation * Wrap it into a proper TestCompilationResult * Pass additional flags in case of dynamic library test --- .../blackbox/AbstractNativeCExportTest.kt | 31 +++++-- .../ObjCToKotlinSteppingInLLDBTest.kt | 8 +- .../konan/test/blackbox/support/LoggedData.kt | 1 + .../konan/test/blackbox/support/util/Clang.kt | 91 ++++++++++++++++--- 4 files changed, 104 insertions(+), 27 deletions(-) diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox/AbstractNativeCExportTest.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox/AbstractNativeCExportTest.kt index c154c17d7bc..d575db2d475 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox/AbstractNativeCExportTest.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox/AbstractNativeCExportTest.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.konan.test.blackbox import com.intellij.testFramework.TestDataFile +import org.jetbrains.kotlin.konan.target.Family import org.jetbrains.kotlin.konan.test.blackbox.support.* import org.jetbrains.kotlin.konan.test.blackbox.support.PackageName import org.jetbrains.kotlin.konan.test.blackbox.support.TestCase @@ -20,6 +21,7 @@ import org.jetbrains.kotlin.konan.test.blackbox.support.compilation.TestCompilat import org.jetbrains.kotlin.konan.test.blackbox.support.runner.TestExecutable import org.jetbrains.kotlin.konan.test.blackbox.support.runner.TestRunCheck import org.jetbrains.kotlin.konan.test.blackbox.support.runner.TestRunChecks +import org.jetbrains.kotlin.konan.test.blackbox.support.settings.KotlinNativeTargets import org.jetbrains.kotlin.konan.test.blackbox.support.settings.Timeouts import org.jetbrains.kotlin.konan.test.blackbox.support.settings.configurables import org.jetbrains.kotlin.konan.test.blackbox.support.util.ClangDistribution @@ -38,7 +40,7 @@ abstract class AbstractNativeCExportTest( STATIC, DYNAMIC } - protected open val kindSpecificClangFlags: List = emptyList() + internal open fun getKindSpecificClangFlags(binaryLibrary: TestCompilationArtifact.BinaryLibrary): List = emptyList() private val testCompilationFactory = TestCompilationFactory() @@ -68,19 +70,19 @@ abstract class AbstractNativeCExportTest( val executableFile = File(buildDir, clangExecutableName) val includeDirectories = binaryLibrary.headerFile?.let { listOf(it.parentFile) } ?: emptyList() val libraryName = binaryLibrary.libraryFile.nameWithoutExtension.substringAfter("lib") - compileWithClang( - clangDistribution = ClangDistribution.LlvmDistribution, + val clangResult = compileWithClang( + clangDistribution = ClangDistribution.Llvm, sourceFiles = cSources, includeDirectories = includeDirectories, outputFile = executableFile, libraryDirectories = listOf(binaryLibrary.libraryFile.parentFile), libraries = listOf(libraryName), - additionalLinkerFlags = kindSpecificClangFlags, - ) + additionalClangFlags = getKindSpecificClangFlags(binaryLibrary), + ).assertSuccess() val testExecutable = TestExecutable( - TestCompilationArtifact.Executable(executableFile), - loggedCompilationToolCall = LoggedData.NoopCompilerCall(buildDir), + clangResult.resultingArtifact, + loggedCompilationToolCall = clangResult.loggedData, testNames = listOf(TestName("TMP")), ) @@ -118,7 +120,16 @@ abstract class AbstractNativeCExportTest( } abstract class AbstractNativeCExportStaticTest() : AbstractNativeCExportTest(libraryKind = BinaryLibraryKind.STATIC) { - override val kindSpecificClangFlags: List - get() = testRunSettings.configurables.linkerKonanFlags.flatMap { listOf("-Xlinker", it) } + override fun getKindSpecificClangFlags(binaryLibrary: TestCompilationArtifact.BinaryLibrary): List = + testRunSettings.configurables.linkerKonanFlags.flatMap { listOf("-Xlinker", it) } } -abstract class AbstractNativeCExportDynamicTest() : AbstractNativeCExportTest(libraryKind = BinaryLibraryKind.DYNAMIC) \ No newline at end of file +abstract class AbstractNativeCExportDynamicTest() : AbstractNativeCExportTest(libraryKind = BinaryLibraryKind.DYNAMIC) { + override fun getKindSpecificClangFlags(binaryLibrary: TestCompilationArtifact.BinaryLibrary): List = + if (testRunSettings.get().testTarget.family != Family.MINGW) { + listOf("-rpath", binaryLibrary.libraryFile.parentFile.absolutePath) + } else { + // --allow-multiple-definition is needed because finalLinkCommands statically links a lot of MinGW-specific libraries, + // that are already included in DLL produced by Kotlin/Native. + listOf("-Wl,--allow-multiple-definition") + } +} \ No newline at end of file diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox/ObjCToKotlinSteppingInLLDBTest.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox/ObjCToKotlinSteppingInLLDBTest.kt index ebe124de69f..147d3075d01 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox/ObjCToKotlinSteppingInLLDBTest.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox/ObjCToKotlinSteppingInLLDBTest.kt @@ -224,19 +224,19 @@ class ObjCToKotlinSteppingInLLDBTest : AbstractNativeSimpleTest() { val clangExecutableName = "clangMain" val executableFile = File(buildDir, clangExecutableName) - compileWithClang( + val clangResult = compileWithClang( // This code was initially written against clang from toolchain. // Changing it to another one probably won't hurt, but it was not tested. clangDistribution = ClangDistribution.Toolchain, sourceFiles = listOf(clangFile), outputFile = executableFile, frameworkDirectories = listOf(buildDir), - ) + ).assertSuccess() // 4. Generate the test case val testExecutable = TestExecutable( - TestCompilationArtifact.Executable(executableFile), - loggedCompilationToolCall = LoggedData.NoopCompilerCall(buildDir), + clangResult.resultingArtifact, + loggedCompilationToolCall = clangResult.loggedData, testNames = listOf(TestName(testName)), ) val spec = LLDBSessionSpec.parse(lldbSpec) diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox/support/LoggedData.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox/support/LoggedData.kt index c741be89d65..177e0102b05 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox/support/LoggedData.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox/support/LoggedData.kt @@ -224,6 +224,7 @@ internal abstract class LoggedData { return this } + @JvmStatic protected fun StringBuilder.appendArguments(header: String, args: Iterable): StringBuilder { appendLine(header) diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox/support/util/Clang.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox/support/util/Clang.kt index 93e1dc450de..a23a61857ca 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox/support/util/Clang.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox/support/util/Clang.kt @@ -5,9 +5,14 @@ package org.jetbrains.kotlin.konan.test.blackbox.support.util +import org.jetbrains.kotlin.cli.common.ExitCode import org.jetbrains.kotlin.konan.test.blackbox.AbstractNativeSimpleTest +import org.jetbrains.kotlin.konan.test.blackbox.support.LoggedData +import org.jetbrains.kotlin.konan.test.blackbox.support.compilation.TestCompilationArtifact +import org.jetbrains.kotlin.konan.test.blackbox.support.compilation.TestCompilationResult import org.jetbrains.kotlin.konan.test.blackbox.support.settings.configurables import java.io.File +import kotlin.time.measureTime /** * Specifies which Clang should be used for compilation. @@ -23,27 +28,36 @@ internal enum class ClangDistribution { /** * Use Clang from Kotlin/Native LLVM distribution. */ - LlvmDistribution + Llvm +} + +internal enum class ClangMode { + C, CXX } // FIXME: absoluteTargetToolchain might not work correctly with KONAN_USE_INTERNAL_SERVER because // :kotlin-native:dependencies:update is not a dependency of :native:native.tests:test where this test runs internal fun AbstractNativeSimpleTest.compileWithClang( - clangDistribution: ClangDistribution = ClangDistribution.LlvmDistribution, + clangDistribution: ClangDistribution = ClangDistribution.Llvm, + clangMode: ClangMode = ClangMode.C, sourceFiles: List, outputFile: File, includeDirectories: List = emptyList(), frameworkDirectories: List = emptyList(), libraryDirectories: List = emptyList(), libraries: List = emptyList(), - additionalLinkerFlags: List = emptyList(), -) { - val clang = when (clangDistribution) { - ClangDistribution.Toolchain -> "${testRunSettings.configurables.absoluteTargetToolchain}/bin/clang" - ClangDistribution.LlvmDistribution -> "${testRunSettings.configurables.absoluteLlvmHome}/bin/clang" + additionalClangFlags: List = emptyList(), +): TestCompilationResult { + val clangExecutableName = when (clangMode) { + ClangMode.C -> "clang" + ClangMode.CXX -> "clang++" + } + val clangPath = when (clangDistribution) { + ClangDistribution.Toolchain -> "${testRunSettings.configurables.absoluteTargetToolchain}/bin/$clangExecutableName" + ClangDistribution.Llvm -> "${testRunSettings.configurables.absoluteLlvmHome}/bin/$clangExecutableName" } val process = ProcessBuilder( - clang, + clangPath, *sourceFiles.map { it.absolutePath }.toTypedArray(), *includeDirectories.flatMap { listOf("-I", it.absolutePath) }.toTypedArray(), "-isysroot", testRunSettings.configurables.absoluteTargetSysRoot, @@ -52,12 +66,63 @@ internal fun AbstractNativeSimpleTest.compileWithClang( *frameworkDirectories.flatMap { listOf("-F", it.absolutePath) }.toTypedArray(), *libraryDirectories.flatMap { listOf("-L", it.absolutePath) }.toTypedArray(), *libraries.map { "-l$it" }.toTypedArray(), - *additionalLinkerFlags.toTypedArray(), + *additionalClangFlags.toTypedArray(), "-o", outputFile.absolutePath - ).redirectErrorStream(true).start() + ).start() + val exitCode: Int + val duration = measureTime { + exitCode = process.waitFor() + } + val clangErrorOutput = process.errorStream.readBytes() val clangOutput = process.inputStream.readBytes() - check( - process.waitFor() == 0 - ) { clangOutput.decodeToString() } + val parameters = ClangParameters( + clangPath, + sourceFiles, + outputFile, + includeDirectories, + frameworkDirectories, + libraryDirectories, + libraries, + additionalClangFlags + ) + val loggedData = LoggedData.CompilationToolCall( + toolName = "CLANG", + parameters = parameters, + exitCode = if (exitCode == 0) ExitCode.OK else ExitCode.COMPILATION_ERROR, + toolOutput = clangOutput.decodeToString() + clangErrorOutput.decodeToString(), + toolOutputHasErrors = clangErrorOutput.isNotEmpty(), + duration = duration, + input = null, + ) + return if (exitCode != 0) { + TestCompilationResult.CompilationToolFailure(loggedData) + } else { + val executable = TestCompilationArtifact.Executable(outputFile) + TestCompilationResult.Success(executable, loggedData) + } +} + +internal class ClangParameters( + private val clangPath: String, + private val sourceFiles: List, + private val outputFile: File, + private val includeDirectories: List = emptyList(), + private val frameworkDirectories: List = emptyList(), + private val libraryDirectories: List = emptyList(), + private val libraries: List = emptyList(), + private val additionalClangFlags: List = emptyList(), +) : LoggedData() { + override fun computeText(): String = buildString { + appendLine("CLANG") + appendLine("- $clangPath") + appendLine("OUTPUT FILE") + appendLine("- ${outputFile.absolutePath}") + appendArguments("SOURCES", sourceFiles.map { it.absolutePath }) + appendArguments("INCLUDE DIRECTORIES", includeDirectories.map { it.absolutePath }) + appendArguments("LIBRARY DIRECTORIES", libraryDirectories.map { it.absolutePath }) + appendArguments("FRAMEWORK DIRECTORIES", frameworkDirectories.map { it.absolutePath }) + appendArguments("LIBRARIES", libraries) + appendArguments("ADDITIONAL CLANG FLAGS", additionalClangFlags) + } } \ No newline at end of file