diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterCompile.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterCompile.kt new file mode 100644 index 00000000000..b37087d00fc --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterCompile.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.backend.konan + +import org.jetbrains.kotlin.konan.exec.* +import org.jetbrains.kotlin.konan.target.* +import org.jetbrains.kotlin.konan.file.* + +fun produceCAdapterBitcode(clang: TargetClang, headerFileName: String, cppFileName: String, bitcodeFileName: String) { + + val headerDirName = File(headerFileName).absoluteFile.parent + + val clangCommand = clang.clangCXX("-std=c++11", cppFileName, "-I", headerDirName, "-emit-llvm", "-c", "-o", bitcodeFileName) + + runTool(clangCommand) +} diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt index bc2a5f5b7b6..a865558b2ab 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt @@ -32,7 +32,7 @@ import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyPublicApi import org.jetbrains.kotlin.types.TypeUtils -import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.konan.file.* private enum class ScopeKind { TOP, @@ -426,7 +426,10 @@ internal class CAdapterGenerator(val context: Context, } private fun makeGlobalStruct(top: ExportedElementScope) { - outputStreamWriter = PrintWriter(File(".", "${prefix}_api.h").outputStream()) + outputStreamWriter = context.config.tempFiles + .cAdapterHeader + .printWriter() + output("#ifndef KONAN_${prefix.toUpperCase()}_H") output("#define KONAN_${prefix.toUpperCase()}_H") // TODO: use namespace for C++ case? @@ -472,7 +475,10 @@ internal class CAdapterGenerator(val context: Context, outputStreamWriter.close() println("Produced dynamic library API in ${prefix}_api.h") - outputStreamWriter = PrintWriter(File(".", "${prefix}_api.cpp").outputStream()) + outputStreamWriter = context.config.tempFiles + .cAdapterCpp + .printWriter() + output("#include \"${prefix}_api.h\"") output(""" |struct KObjHeader; diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt index 557e8a866fc..099e525034f 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt @@ -31,18 +31,31 @@ internal fun produceOutput(context: Context) { val llvmModule = context.llvmModule!! val config = context.config.configuration + val tempFiles = context.config.tempFiles + val produce = config.get(KonanConfigKeys.PRODUCE) - when (config.get(KonanConfigKeys.PRODUCE)) { + when (produce) { CompilerOutputKind.DYNAMIC, CompilerOutputKind.FRAMEWORK, CompilerOutputKind.PROGRAM -> { val program = context.config.outputName - val output = "$program.kt.bc" + val output = tempFiles.nativeBinaryFileName context.bitcodeFileName = output + val generatedBitcodeFiles = + if (produce == CompilerOutputKind.DYNAMIC) { + produceCAdapterBitcode( + context.config.clang, + tempFiles.cAdapterHeaderName, + tempFiles.cAdapterCppName, + tempFiles.cAdapterBitcodeName) + listOf(tempFiles.cAdapterBitcodeName) + } else emptyList() + val nativeLibraries = context.config.nativeLibraries + - context.config.defaultNativeLibraries + context.config.defaultNativeLibraries + + generatedBitcodeFiles PhaseManager(context).phase(KonanPhase.BITCODE_LINKER) { for (library in nativeLibraries) { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Exceptions.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Exceptions.kt index f319a74e09f..8886ece78e5 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Exceptions.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Exceptions.kt @@ -21,6 +21,11 @@ package org.jetbrains.kotlin.backend.konan */ open class KonanException(message: String = "", cause: Throwable? = null) : Exception(message, cause) +/** + * An error occured during external tool invocation. Such as non-zero exit code. + */ +class KonanExternalToolFailure(message: String = "", cause: Throwable? = null) : KonanException(message, cause) + /** * Represents a compilation error caused by mistakes in an input file, e.g. undefined reference. */ @@ -31,8 +36,3 @@ class KonanCompilationException(message: String = "", cause: Throwable? = null) */ class KonanIrDeserializationException(message: String = "", cause: Throwable? = null) : KonanException(message, cause) -/** - * An error occured during external tool invocation. Such as non-zero exit code. - */ -class KonanExternalToolFailure(message: String = "", cause: Throwable? = null) : KonanException(message, cause) - diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt index babefb2da96..6f414e1ab1d 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt @@ -20,9 +20,7 @@ import com.intellij.openapi.project.Project import org.jetbrains.kotlin.backend.konan.descriptors.createForwardDeclarationsModule import org.jetbrains.kotlin.backend.konan.library.* import org.jetbrains.kotlin.backend.konan.library.impl.* -import org.jetbrains.kotlin.backend.konan.util.profile -import org.jetbrains.kotlin.backend.konan.util.removeSuffixIfPresent -import org.jetbrains.kotlin.backend.konan.util.suffixIfNot +import org.jetbrains.kotlin.backend.konan.util.* import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity @@ -44,15 +42,16 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration internal val targetManager = TargetManager( configuration.get(KonanConfigKeys.TARGET)) - val indirectBranchesAreAllowed = targetManager.target != KonanTarget.WASM32 + private val target = targetManager.target init { - val target = targetManager.target if (!target.enabled) { error("Target $target is not available on the ${TargetManager.host} host") } } + val indirectBranchesAreAllowed = target != KonanTarget.WASM32 + private fun Distribution.prepareDependencies(checkDependencies: Boolean) { if (checkDependencies) { DependencyProcessor(java.io.File(dependenciesDir), targetProperties).run() @@ -65,10 +64,17 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration prepareDependencies(configuration.getBoolean(KonanConfigKeys.CHECK_DEPENDENCIES)) } + internal val clang = TargetClang(distribution.properties, distribution.dependenciesDir, target) + internal val produce get() = configuration.get(KonanConfigKeys.PRODUCE)!! - private val suffix = produce.suffix(targetManager.target) + private val prefix = produce.prefix(target) + private val suffix = produce.suffix(target) val outputName = configuration.get(KonanConfigKeys.OUTPUT)?.removeSuffixIfPresent(suffix) ?: produce.name.toLowerCase() - val outputFile = outputName.suffixIfNot(produce.suffix(targetManager.target)) + val outputFile = outputName + .prefixIfNot(prefix) + .suffixIfNot(suffix) + + val tempFiles = TempFiles(outputName) val moduleId: String get() = configuration.get(KonanConfigKeys.MODULE_NAME) ?: File(outputName).name @@ -85,13 +91,13 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration internal val immediateLibraries: List by lazy { val result = resolver.resolveImmediateLibraries( libraryNames, - targetManager.target, + target, currentAbiVersion, configuration.getBoolean(KonanConfigKeys.NOSTDLIB), configuration.getBoolean(KonanConfigKeys.NODEFAULTLIBS), { msg -> configuration.report(STRONG_WARNING, msg) } ) - resolver.resolveLibrariesRecursive(result, targetManager.target, currentAbiVersion) + resolver.resolveLibrariesRecursive(result, target, currentAbiVersion) result } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/TempFiles.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/TempFiles.kt new file mode 100644 index 00000000000..fba7eb7a0ec --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/TempFiles.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.kotlin.backend.konan + +import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.konan.file.* + +class TempFiles(val outputName: String) { + val nativeBinaryFile by lazy { File("${outputName}.kt.bc") } + val cAdapterHeader by lazy { File("${outputName}_api.h") } + val cAdapterCpp by lazy { createTempFile("api", ".cpp").deleteOnExit() } + val cAdapterBitcode by lazy { createTempFile("api", ".bc").deleteOnExit() } + + val nativeBinaryFileName get() = nativeBinaryFile.absolutePath + val cAdapterHeaderName get() = cAdapterHeader.absolutePath + val cAdapterCppName get() = cAdapterCpp.absolutePath + val cAdapterBitcodeName get() = cAdapterBitcode.absolutePath +} + diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/util/util.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/util/util.kt index 566cf77566e..1170f0a8047 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/util/util.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/util/util.kt @@ -36,10 +36,13 @@ fun nTabs(amount: Int): String { return String.format("%1$-${(amount+1)*4}s", "") } +fun String.prefixIfNot(prefix: String) = + if (this.startsWith(prefix)) this else "$prefix$this" + fun String.suffixIfNot(suffix: String) = if (this.endsWith(suffix)) this else "$this$suffix" fun String.removeSuffixIfPresent(suffix: String) = if (this.endsWith(suffix)) this.dropLast(suffix.length) else this -fun Lazy.getValueOrNull(): T? = if (isInitialized()) value else null \ No newline at end of file +fun Lazy.getValueOrNull(): T? = if (isInitialized()) value else null diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/exec/ExecuteCommand.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/exec/ExecuteCommand.kt new file mode 100644 index 00000000000..b04e2a36d16 --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/exec/ExecuteCommand.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.exec + +import java.lang.ProcessBuilder +import java.lang.ProcessBuilder.Redirect + +fun executeCommand(command: List) = + executeCommand(*command.toTypedArray()) + +fun executeCommand(vararg command: String): Int { + // TODO: need a verbose logger here. + + val builder = ProcessBuilder(command.asList()) + + builder.redirectOutput(Redirect.INHERIT) + builder.redirectInput(Redirect.INHERIT) + builder.redirectError(Redirect.INHERIT) + + val process = builder.start() + val exitCode = process.waitFor() + return exitCode +} + +fun runTool(command: List) = + runTool(*command.toTypedArray()) + +fun runTool(vararg command: String) { + val code = executeCommand(*command) + if (code != 0) error("The ${command[0]} command returned non-zero exit code: $code.") +} + diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/File.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/File.kt index 2b28475dd1e..74f322db625 100644 --- a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/File.kt +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/File.kt @@ -103,10 +103,11 @@ data class File constructor(internal val javaPath: Path) { }) } - fun deleteOnExit() { + fun deleteOnExit(): File { // Works only on the default file system, // but that's okay for now. javaPath.toFile().deleteOnExit() + return this // Allow streaming. } fun readBytes() = Files.readAllBytes(javaPath) fun writeBytes(bytes: ByteArray) = Files.write(javaPath, bytes) @@ -136,6 +137,7 @@ data class File constructor(internal val javaPath: Path) { // TODO: Consider removeing these after konanazing java.util.Properties. fun bufferedReader() = Files.newBufferedReader(javaPath) fun outputStream() = Files.newOutputStream(javaPath) + fun printWriter() = javaPath.toFile().printWriter() companion object { val userDir diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangManager.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangManager.kt index 03a63382a38..b292f378f94 100644 --- a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangManager.kt +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangManager.kt @@ -39,5 +39,21 @@ class ClangManager(val properties: Properties, val baseDir: String) { = (hostClang.commonClangArgs + targetClangArgs[target]!!.specificClangArgs).toTypedArray() val hostCompilerArgsForJni = hostClang.hostCompilerArgsForJni.toTypedArray() + + fun targetClangCmd(target: KonanTarget) + = listOf("${hostClang.llvmDir}/bin/clang") + targetClangArgs(target) + + fun targetClangXXCmd(target: KonanTarget) + = listOf("${hostClang.llvmDir}/bin/clang++") + targetClangArgs(target) } +class TargetClang(private val clangManager: ClangManager, private val target: KonanTarget) { + constructor (properties: Properties, baseDir: String, target: KonanTarget) + : this (ClangManager(properties, baseDir), target) + + fun clangC(vararg userArgs: String) = clangManager.targetClangCmd(target) + userArgs.asList() + + fun clangCXX(vararg userArgs: String) = clangManager.targetClangXXCmd(target) + userArgs.asList() +} + + diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/KonanTarget.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/KonanTarget.kt index 3ffc72925b0..9cff96ee7af 100644 --- a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/KonanTarget.kt +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/KonanTarget.kt @@ -16,13 +16,13 @@ package org.jetbrains.kotlin.konan.target -enum class Family(name:String, val exeSuffix:String, val dynamicSuffix: String?) { - OSX("osx", "kexe", "dylib"), - IOS("ios", "kexe", "dylib"), - LINUX("linux", "kexe", "so"), - WINDOWS("windows", "exe", "dll"), - ANDROID("android", "so", "so"), - WASM("wasm", "wasm", "wasm") +enum class Family(name:String, val exeSuffix:String, val dynamicPrefix: String, val dynamicSuffix: String) { + OSX( "osx" , "kexe", "lib", "dylib"), + IOS( "ios" , "kexe", "lib", "dylib"), + LINUX( "linux" , "kexe", "lib", "so" ), + WINDOWS("windows", "exe" , "" , "dll" ), + ANDROID("android", "so" , "lib", "so" ), + WASM( "wasm" , "wasm", "" , "wasm" ) } enum class Architecture(val bitness: Int) { @@ -62,6 +62,7 @@ enum class CompilerOutputKind { }, DYNAMIC { override fun suffix(target: KonanTarget?) = ".${target!!.family.dynamicSuffix}" + override fun prefix(target: KonanTarget?) = ".${target!!.family.dynamicPrefix}" }, FRAMEWORK { override fun suffix(target: KonanTarget?): String = ".framework" @@ -74,6 +75,7 @@ enum class CompilerOutputKind { }; abstract fun suffix(target: KonanTarget? = null): String + open fun prefix(target: KonanTarget? = null): String = "" } class TargetManager(val userRequest: String? = null) {