From 66ddd157988b80b398b9bea3d50adee0d2056268 Mon Sep 17 00:00:00 2001 From: Sergey Bogolepov Date: Fri, 23 Jul 2021 12:46:49 +0700 Subject: [PATCH] [K/N] Support compiler compilation in MSVC environment The right way is to add something like KonanTarget.MSVC_X64, but doing so requires changes throughout whole compiler. It would be especially painful in HostManager, where we would need to deprecate KonanTarget.MINGW_X64 as host. Instead we "hack" ClangArgs to compile for x86_64-pc-windows-msvc instead of x86_64-pc-windows-gnu in JNI case. CI may contain custom MSVC and Windows Kit installation path, so we should support it. Things might break when machine has several MSVC installed (at custom and default path), but it sounds more like incorrect environment setup problem than ours. --- .../Interop/Indexer/build.gradle.kts | 2 +- kotlin-native/konan/konan.properties | 8 ++ .../kotlin/konan/target/ClangArgs.kt | 60 ++++++---- .../kotlin/konan/target/Configurables.kt | 6 + .../kotlin/konan/target/ConfigurablesImpl.kt | 3 - .../jetbrains/kotlin/konan/target/Windows.kt | 111 ++++++++++++++++++ 6 files changed, 165 insertions(+), 25 deletions(-) create mode 100644 kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Windows.kt diff --git a/kotlin-native/Interop/Indexer/build.gradle.kts b/kotlin-native/Interop/Indexer/build.gradle.kts index a1c9e8d0ef4..8e4ad1f33a5 100644 --- a/kotlin-native/Interop/Indexer/build.gradle.kts +++ b/kotlin-native/Interop/Indexer/build.gradle.kts @@ -38,7 +38,7 @@ val llvmDir = project.findProperty("llvmDir") val libclang = if (HostManager.hostIsMingw) { - "bin/libclang.dll" + "lib/libclang.lib" } else { "lib/${System.mapLibraryName("clang")}" } diff --git a/kotlin-native/konan/konan.properties b/kotlin-native/konan/konan.properties index e24e322c3fc..fe7fb36b649 100644 --- a/kotlin-native/konan/konan.properties +++ b/kotlin-native/konan/konan.properties @@ -867,8 +867,16 @@ runtimeDefinitions.android_x64 = __ANDROID__ USE_GCC_UNWIND=1 USE_ELF_SYMBOLS=1 llvmHome.mingw_x64 = $llvm.mingw_x64.dev targetToolchain.mingw_x64 = msys2-mingw-w64-x86_64-clang-llvm-lld-compiler_rt-8.0.1 libffiDir.mingw_x64 = libffi-3.2.1-mingw-w64-x86-64 +windowsKitParts.mingw_x64 = windows-kit-x64-v1-alpha2 +msvcParts.mingw_x64 = msvc-x64-v1-alpha2 lldLocation.mingw_x64 = lld-11.1.0-windows-x64/ld.lld.exe +windows-kit-x64-v1-alpha2.default = \ + remote:internal + +msvc-x64-v1-alpha2.default = \ + remote:internal + targetToolchain.linux_x64-mingw_x64 = msys2-mingw-w64-x86_64-clang-llvm-lld-compiler_rt-8.0.1 targetToolchain.macos_x64-mingw_x64 = msys2-mingw-w64-x86_64-clang-llvm-lld-compiler_rt-8.0.1 # for Windows we are currently using LLDB 9 diff --git a/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangArgs.kt b/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangArgs.kt index f52fa8662e2..8c716215a3f 100644 --- a/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangArgs.kt +++ b/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangArgs.kt @@ -42,6 +42,9 @@ sealed class ClangArgs( private val target = configurables.target private val targetTriple = configurables.targetTriple + // TODO: Should be dropped in favor of real MSVC target. + private val argsForWindowsJni = forJni && target == KonanTarget.MINGW_X64 + private val clangArgsSpecificForKonanSources get() = configurables.runtimeDefinitions.map { "-D$it" } @@ -54,34 +57,50 @@ sealed class ClangArgs( } // TODO: Use buildList private val commonClangArgs: List = mutableListOf>().apply { - add(listOf("-B$binDir", "-fno-stack-protector")) + // Currently, MinGW toolchain contains old LLVM 8, and -fuse-ld=lld picks linker from there. + // And, unfortunately, `-fuse-ld=` doesn't work correctly for MSVC toolchain. + // That's why we just don't add $absoluteTargetToolchain/bin to binary search path in case of JNI compilation. + // TODO: Can be removed after MinGW sysroot update. + if (!argsForWindowsJni) { + add(listOf("-B$binDir")) + } else { + require(configurables is MingwConfigurables) + add(configurables.msvc.compilerFlags()) + add(configurables.windowsKit.compilerFlags()) + // Do not depend on link.exe from Visual Studio. + add(listOf("-fuse-ld=lld")) + } + add(listOf("-fno-stack-protector")) if (configurables is GccConfigurables) { add(listOf("--gcc-toolchain=${configurables.absoluteGccToolchain}")) } - if (configurables is AppleConfigurables) { - val osVersionMin = when (target) { - // Here we workaround Clang 8 limitation: macOS major version should be 10. - // So we compile runtime with version 10.16 and then override version in BitcodeCompiler. - // TODO: Fix with LLVM Update. - KonanTarget.MACOS_ARM64 -> "10.16" - else -> configurables.osVersionMin + val targetString: String = when { + argsForWindowsJni -> "x86_64-pc-windows-msvc" + configurables is AppleConfigurables -> { + val osVersionMin = when (target) { + // Here we workaround Clang 8 limitation: macOS major version should be 10. + // So we compile runtime with version 10.16 and then override version in BitcodeCompiler. + // TODO: Fix with LLVM Update. + KonanTarget.MACOS_ARM64 -> "10.16" + else -> configurables.osVersionMin + } + targetTriple.copy( + architecture = when (targetTriple.architecture) { + // TODO: LLVM 8 doesn't support arm64_32. + // We can use armv7k because they are compatible at bitcode level. + "arm64_32" -> "armv7k" + else -> targetTriple.architecture + }, + os = "${targetTriple.os}$osVersionMin" + ).toString() } - val targetArg = targetTriple.copy( - architecture = when (targetTriple.architecture) { - // TODO: LLVM 8 doesn't support arm64_32. - // We can use armv7k because they are compatible at bitcode level. - "arm64_32" -> "armv7k" - else -> targetTriple.architecture - }, - os = "${targetTriple.os}$osVersionMin" - ) - add(listOf("-target", targetArg.toString())) - } else { - add(listOf("-target", configurables.targetTriple.toString())) + else -> configurables.targetTriple.toString() } + add(listOf("-target", targetString)) val hasCustomSysroot = configurables is ZephyrConfigurables || configurables is WasmConfigurables || configurables is AndroidConfigurables + || argsForWindowsJni if (!hasCustomSysroot) { when (configurables) { // isysroot and sysroot on darwin are _almost_ synonyms. @@ -89,7 +108,6 @@ sealed class ClangArgs( is AppleConfigurables -> add(listOf("-isysroot", absoluteTargetSysRoot)) else -> add(listOf("--sysroot=$absoluteTargetSysRoot")) } - } // PIC is not required on Windows (and Clang will fail with `error: unsupported option '-fPIC'`) if (configurables !is MingwConfigurables) { diff --git a/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Configurables.kt b/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Configurables.kt index 92e2a0b8c2b..f89ebbd4683 100644 --- a/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Configurables.kt +++ b/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Configurables.kt @@ -107,6 +107,12 @@ interface AppleConfigurables : Configurables, ClangFlags { interface MingwConfigurables : Configurables, ClangFlags { val lldLocation get() = targetString("lldLocation")!! val absoluteLldLocation get() = absolute(lldLocation) + + val windowsKit: WindowsKit + val msvc: Msvc + + val windowsKitParts get() = hostString("windowsKitParts")!! + val msvcParts get() = hostString("msvcParts")!! } interface GccConfigurables : Configurables, ClangFlags { diff --git a/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ConfigurablesImpl.kt b/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ConfigurablesImpl.kt index 1089c0087f9..d5af616a59b 100644 --- a/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ConfigurablesImpl.kt +++ b/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ConfigurablesImpl.kt @@ -27,9 +27,6 @@ class GccConfigurablesImpl(target: KonanTarget, properties: Properties, baseDir: class AndroidConfigurablesImpl(target: KonanTarget, properties: Properties, baseDir: String?) : AndroidConfigurables, KonanPropertiesLoader(target, properties, baseDir) -class MingwConfigurablesImpl(target: KonanTarget, properties: Properties, baseDir: String?) - : MingwConfigurables, KonanPropertiesLoader(target, properties, baseDir) - class WasmConfigurablesImpl(target: KonanTarget, properties: Properties, baseDir: String?) : WasmConfigurables, KonanPropertiesLoader(target, properties, baseDir) diff --git a/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Windows.kt b/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Windows.kt new file mode 100644 index 00000000000..86381d10ebc --- /dev/null +++ b/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Windows.kt @@ -0,0 +1,111 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.konan.target + +import org.jetbrains.kotlin.konan.util.InternalServer +import java.nio.file.Path +import org.jetbrains.kotlin.konan.properties.KonanPropertiesLoader +import org.jetbrains.kotlin.konan.properties.Properties +import java.nio.file.Paths + +class MingwConfigurablesImpl(target: KonanTarget, properties: Properties, baseDir: String?) + : MingwConfigurables, KonanPropertiesLoader(target, properties, baseDir) { + override val windowsKit: WindowsKit by lazy { + when (windowsSdkPartsProvider) { + WindowsSdkPartsProvider.InternalServer -> createCustomWindowsKitPath(Paths.get(absolute(windowsKitParts))) + WindowsSdkPartsProvider.Local -> WindowsKit.DefaultPath + } + } + override val msvc: Msvc by lazy { + when (windowsSdkPartsProvider) { + WindowsSdkPartsProvider.InternalServer -> createCustomMsvcPath(Paths.get(absolute(msvcParts))) + WindowsSdkPartsProvider.Local -> Msvc.DefaultPath + } + } + + private val windowsSdkPartsProvider by lazy { + if (InternalServer.isAvailable) { + WindowsSdkPartsProvider.InternalServer + } else { + WindowsSdkPartsProvider.Local + } + } + + override val dependencies + get() = super.dependencies + when (windowsSdkPartsProvider) { + WindowsSdkPartsProvider.InternalServer -> listOf(windowsKitParts, msvcParts) + WindowsSdkPartsProvider.Local -> emptyList() + } +} + +private fun createCustomWindowsKitPath(windowsKitParts: Path): WindowsKit.CustomPath { + return WindowsKit.CustomPath( + libraryDirectories = listOf( + windowsKitParts.resolve("Lib").resolve("ucrt").resolve("x64"), + windowsKitParts.resolve("Lib").resolve("um").resolve("x64") + ), + includeDirectories = listOf( + windowsKitParts.resolve("Include").resolve("ucrt") + ) + ) +} + +private fun createCustomMsvcPath(msvcParts: Path): Msvc.CustomPath { + return Msvc.CustomPath( + libraryDirectories = listOf( + msvcParts.resolve("lib").resolve("x64") + ), + includeDirectories = listOf( + msvcParts.resolve("include") + ) + ) +} + +sealed class Msvc { + + abstract fun compilerFlags(): List + + object DefaultPath : Msvc() { + override fun compilerFlags(): List = emptyList() + } + + class CustomPath( + private val includeDirectories: List, + private val libraryDirectories: List + ) : Msvc() { + // Note that this approach doesn't exclude default VS path. + // TODO: A better (but harder) way would be LIB environment variable. + override fun compilerFlags(): List = + includeDirectories.flatMap { listOf("-isystem", it.toAbsolutePath().toString()) } + + libraryDirectories.flatMap { listOf("-L", it.toAbsolutePath().toString()) } + + } +} + +sealed class WindowsKit { + abstract fun compilerFlags(): List + + object DefaultPath : WindowsKit() { + override fun compilerFlags(): List = emptyList() + } + + class CustomPath( + private val includeDirectories: List, + private val libraryDirectories: List + ) : WindowsKit() { + // Note that this approach doesn't exclude default Windows Kit path. + // TODO: A better (but harder) way would be LIB environment variable. + override fun compilerFlags(): List = + includeDirectories.flatMap { listOf("-isystem", it.toAbsolutePath().toString()) } + + libraryDirectories.flatMap { listOf("-L", it.toAbsolutePath().toString()) } + + } +} + +private sealed class WindowsSdkPartsProvider { + object Local : WindowsSdkPartsProvider() + object InternalServer : WindowsSdkPartsProvider() +} \ No newline at end of file