Further refactored external utility execution.
This commit is contained in:
committed by
alexander-gorshenev
parent
4daaf9c99f
commit
e64c2f5990
+1
-1
@@ -22,5 +22,5 @@ import org.jetbrains.kotlin.konan.file.*
|
|||||||
|
|
||||||
fun produceCAdapterBitcode(clang: TargetClang, cppFileName: String, bitcodeFileName: String) {
|
fun produceCAdapterBitcode(clang: TargetClang, cppFileName: String, bitcodeFileName: String) {
|
||||||
val clangCommand = clang.clangCXX("-std=c++11", cppFileName, "-emit-llvm", "-c", "-o", bitcodeFileName)
|
val clangCommand = clang.clangCXX("-std=c++11", cppFileName, "-emit-llvm", "-c", "-o", bitcodeFileName)
|
||||||
runTool(clangCommand)
|
Command(clangCommand).execute()
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-9
@@ -16,15 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.konan
|
package org.jetbrains.kotlin.backend.konan
|
||||||
|
|
||||||
/**
|
import org.jetbrains.kotlin.konan.KonanException
|
||||||
* This is a common ancestor of all Kotlin/Native exceptions.
|
|
||||||
*/
|
|
||||||
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.
|
* Represents a compilation error caused by mistakes in an input file, e.g. undefined reference.
|
||||||
|
|||||||
+57
-76
@@ -16,9 +16,10 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.konan
|
package org.jetbrains.kotlin.backend.konan
|
||||||
|
|
||||||
import org.jetbrains.kotlin.konan.exec.runTool
|
|
||||||
import java.lang.ProcessBuilder
|
import java.lang.ProcessBuilder
|
||||||
import java.lang.ProcessBuilder.Redirect
|
import java.lang.ProcessBuilder.Redirect
|
||||||
|
import org.jetbrains.kotlin.konan.KonanExternalToolFailure
|
||||||
|
import org.jetbrains.kotlin.konan.exec.Command
|
||||||
import org.jetbrains.kotlin.konan.file.*
|
import org.jetbrains.kotlin.konan.file.*
|
||||||
import org.jetbrains.kotlin.konan.properties.*
|
import org.jetbrains.kotlin.konan.properties.*
|
||||||
import org.jetbrains.kotlin.konan.target.*
|
import org.jetbrains.kotlin.konan.target.*
|
||||||
@@ -68,26 +69,6 @@ internal abstract class PlatformFlags(val properties: KonanProperties) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal open class Command(tool:String) {
|
|
||||||
private val opts = mutableListOf(tool)
|
|
||||||
val libs = mutableListOf<String>()
|
|
||||||
operator fun String.unaryPlus():Command {
|
|
||||||
opts += this
|
|
||||||
return this@Command
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun List<String>.unaryPlus():Command {
|
|
||||||
opts.addAll(this)
|
|
||||||
return this@Command
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun execute() = runTool(*opts.toTypedArray())
|
|
||||||
|
|
||||||
fun externalLibraries(deps: List<String>) {
|
|
||||||
libs.addAll(deps)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal open class AndroidPlatform(distribution: Distribution)
|
internal open class AndroidPlatform(distribution: Distribution)
|
||||||
: PlatformFlags(distribution.targetProperties) {
|
: PlatformFlags(distribution.targetProperties) {
|
||||||
|
|
||||||
@@ -133,13 +114,7 @@ internal open class MacOSBasedPlatform(distribution: Distribution)
|
|||||||
= binaries.filter { it.isUnixStaticLib }
|
= binaries.filter { it.isUnixStaticLib }
|
||||||
|
|
||||||
override fun linkCommand(objectFiles: List<ObjectFile>, executable: ExecutableFile, optimize: Boolean, debug: Boolean, dynamic: Boolean): Command {
|
override fun linkCommand(objectFiles: List<ObjectFile>, executable: ExecutableFile, optimize: Boolean, debug: Boolean, dynamic: Boolean): Command {
|
||||||
return object : Command(linker){
|
return object : Command(linker) {} .apply {
|
||||||
override fun execute() {
|
|
||||||
super.execute()
|
|
||||||
if (debug)
|
|
||||||
runTool(*dsymutilCommand(executable).toTypedArray())
|
|
||||||
}
|
|
||||||
}.apply {
|
|
||||||
+ "-demangle"
|
+ "-demangle"
|
||||||
+ listOf("-object_path_lto", "temporary.o", "-lto_library", libLTO)
|
+ listOf("-object_path_lto", "temporary.o", "-lto_library", libLTO)
|
||||||
+ listOf("-dynamic", "-arch", propertyTargetString("arch"))
|
+ listOf("-dynamic", "-arch", propertyTargetString("arch"))
|
||||||
@@ -154,6 +129,42 @@ internal open class MacOSBasedPlatform(distribution: Distribution)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun dsymUtilCommand(executable: ExecutableFile) = object : Command(dsymutilCommand(executable)) {
|
||||||
|
override fun runProcess(): Int =
|
||||||
|
executeCommandWithFilter(command)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: consider introducing a better filtering directly in Command.
|
||||||
|
private fun executeCommandWithFilter(command: List<String>): Int {
|
||||||
|
val builder = ProcessBuilder(command)
|
||||||
|
|
||||||
|
// Inherit main process output streams.
|
||||||
|
val isDsymUtil = (command[0] == dsymutil)
|
||||||
|
|
||||||
|
builder.redirectOutput(Redirect.INHERIT)
|
||||||
|
builder.redirectInput(Redirect.INHERIT)
|
||||||
|
if (!isDsymUtil)
|
||||||
|
builder.redirectError(Redirect.INHERIT)
|
||||||
|
|
||||||
|
val process = builder.start()
|
||||||
|
if (isDsymUtil) {
|
||||||
|
/**
|
||||||
|
* llvm-lto has option -alias that lets tool to know which symbol we use instead of _main,
|
||||||
|
* llvm-dsym doesn't have such a option, so we ignore annoying warning manually.
|
||||||
|
*/
|
||||||
|
val errorStream = process.errorStream
|
||||||
|
val outputStream = bufferedReader(errorStream)
|
||||||
|
while (true) {
|
||||||
|
val line = outputStream.readLine() ?: break
|
||||||
|
if (!line.contains("warning: could not find object file symbol for symbol _main"))
|
||||||
|
System.err.println(line)
|
||||||
|
}
|
||||||
|
outputStream.close()
|
||||||
|
}
|
||||||
|
val exitCode = process.waitFor()
|
||||||
|
return exitCode
|
||||||
|
}
|
||||||
|
|
||||||
open fun dsymutilCommand(executable: ExecutableFile): List<String> = listOf(dsymutil, executable)
|
open fun dsymutilCommand(executable: ExecutableFile): List<String> = listOf(dsymutil, executable)
|
||||||
|
|
||||||
open fun dsymutilDryRunVerboseCommand(executable: ExecutableFile): List<String> =
|
open fun dsymutilDryRunVerboseCommand(executable: ExecutableFile): List<String> =
|
||||||
@@ -253,7 +264,7 @@ internal open class WasmPlatform(distribution: Distribution)
|
|||||||
override val useCompilerDriverAsLinker: Boolean get() = false
|
override val useCompilerDriverAsLinker: Boolean get() = false
|
||||||
|
|
||||||
override fun filterStaticLibraries(binaries: List<String>)
|
override fun filterStaticLibraries(binaries: List<String>)
|
||||||
= emptyList<String>()
|
= binaries.filter{it.isJavaScript}
|
||||||
|
|
||||||
override fun linkCommand(objectFiles: List<ObjectFile>, executable: ExecutableFile, optimize: Boolean, debug: Boolean, dynamic: Boolean): Command {
|
override fun linkCommand(objectFiles: List<ObjectFile>, executable: ExecutableFile, optimize: Boolean, debug: Boolean, dynamic: Boolean): Command {
|
||||||
return object: Command("") {
|
return object: Command("") {
|
||||||
@@ -261,7 +272,7 @@ internal open class WasmPlatform(distribution: Distribution)
|
|||||||
val src = File(objectFiles.single())
|
val src = File(objectFiles.single())
|
||||||
val dst = File(executable)
|
val dst = File(executable)
|
||||||
src.recursiveCopyTo(dst)
|
src.recursiveCopyTo(dst)
|
||||||
javaScriptLink(libs.filter{it.isJavaScript}, executable)
|
javaScriptLink(args, executable)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun javaScriptLink(jsFiles: List<String>, executable: String): String {
|
private fun javaScriptLink(jsFiles: List<String>, executable: String): String {
|
||||||
@@ -312,6 +323,12 @@ internal class LinkStage(val context: Context) {
|
|||||||
addAll(elements.filter { !it.isEmpty() })
|
addAll(elements.filter { !it.isEmpty() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun runTool(command: List<String>) = runTool(*command.toTypedArray())
|
||||||
|
private fun runTool(vararg command: String) =
|
||||||
|
Command(*command)
|
||||||
|
.logWith(context::log)
|
||||||
|
.execute()
|
||||||
|
|
||||||
private fun llvmLto(files: List<BitcodeFile>): ObjectFile {
|
private fun llvmLto(files: List<BitcodeFile>): ObjectFile {
|
||||||
val combined = temporary("combined", ".o")
|
val combined = temporary("combined", ".o")
|
||||||
|
|
||||||
@@ -325,7 +342,7 @@ internal class LinkStage(val context: Context) {
|
|||||||
}
|
}
|
||||||
command.addNonEmpty(platform.llvmLtoDynamicFlags)
|
command.addNonEmpty(platform.llvmLtoDynamicFlags)
|
||||||
command.addNonEmpty(files)
|
command.addNonEmpty(files)
|
||||||
runTool(*command.toTypedArray())
|
runTool(command)
|
||||||
|
|
||||||
return combined
|
return combined
|
||||||
}
|
}
|
||||||
@@ -344,7 +361,7 @@ internal class LinkStage(val context: Context) {
|
|||||||
private fun hostLlvmTool(tool: String, args: List<String>) {
|
private fun hostLlvmTool(tool: String, args: List<String>) {
|
||||||
val absoluteToolName = "${distribution.llvmBin}/$tool"
|
val absoluteToolName = "${distribution.llvmBin}/$tool"
|
||||||
val command = listOf(absoluteToolName) + args
|
val command = listOf(absoluteToolName) + args
|
||||||
runTool(*command.toTypedArray())
|
runTool(command)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bitcodeToWasm(bitcodeFiles: List<BitcodeFile>): String {
|
private fun bitcodeToWasm(bitcodeFiles: List<BitcodeFile>): String {
|
||||||
@@ -422,58 +439,22 @@ internal class LinkStage(val context: Context) {
|
|||||||
+ platform.linkCommandSuffix()
|
+ platform.linkCommandSuffix()
|
||||||
+ platform.linkStaticLibraries(includedBinaries)
|
+ platform.linkStaticLibraries(includedBinaries)
|
||||||
+ libraryProvidedLinkerFlags
|
+ libraryProvidedLinkerFlags
|
||||||
externalLibraries(includedBinaries)
|
logger = context::log
|
||||||
}.execute()
|
}.execute()
|
||||||
|
|
||||||
|
if (debug && platform is MacOSBasedPlatform) {
|
||||||
|
platform.dsymUtilCommand(executable)
|
||||||
|
.logWith(context::log)
|
||||||
|
.execute()
|
||||||
|
}
|
||||||
} catch (e: KonanExternalToolFailure) {
|
} catch (e: KonanExternalToolFailure) {
|
||||||
context.reportCompilationError("linker invocation reported errors")
|
context.reportCompilationError("${e.toolName} invocation reported errors")
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return executable
|
return executable
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun executeCommand(vararg command: String): Int {
|
|
||||||
|
|
||||||
context.log{""}
|
|
||||||
context.log{command.asList<String>().joinToString(" ")}
|
|
||||||
|
|
||||||
val builder = ProcessBuilder(command.asList())
|
|
||||||
|
|
||||||
// Inherit main process output streams.
|
|
||||||
val isDsymUtil = platform is MacOSBasedPlatform && command[0] == platform.dsymutil
|
|
||||||
|
|
||||||
builder.redirectOutput(Redirect.INHERIT)
|
|
||||||
builder.redirectInput(Redirect.INHERIT)
|
|
||||||
if (!isDsymUtil)
|
|
||||||
builder.redirectError(Redirect.INHERIT)
|
|
||||||
|
|
||||||
|
|
||||||
val process = builder.start()
|
|
||||||
if (isDsymUtil) {
|
|
||||||
/**
|
|
||||||
* llvm-lto has option -alias that lets tool to know which symbol we use instead of _main,
|
|
||||||
* llvm-dsym doesn't have such a option, so we ignore annoying warning manually.
|
|
||||||
*/
|
|
||||||
val errorStream = process.errorStream
|
|
||||||
val outputStream = bufferedReader(errorStream)
|
|
||||||
while (true) {
|
|
||||||
val line = outputStream.readLine() ?: break
|
|
||||||
if (!line.contains("warning: could not find object file symbol for symbol _main"))
|
|
||||||
System.err.println(line)
|
|
||||||
}
|
|
||||||
outputStream.close()
|
|
||||||
}
|
|
||||||
val exitCode = process.waitFor()
|
|
||||||
return exitCode
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun runTool(vararg command: String) {
|
|
||||||
val code = executeCommand(*command)
|
|
||||||
if (code != 0) throw KonanExternalToolFailure("The ${command[0]} command returned non-zero exit code: $code.")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun linkStage() {
|
fun linkStage() {
|
||||||
context.log{"# Compiler root: ${distribution.konanHome}"}
|
|
||||||
|
|
||||||
val bitcodeFiles = listOf(emitted) +
|
val bitcodeFiles = listOf(emitted) +
|
||||||
libraries.map{it -> it.bitcodePaths}.flatten()
|
libraries.map{it -> it.bitcodePaths}.flatten()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a common ancestor of all Kotlin/Native exceptions.
|
||||||
|
*/
|
||||||
|
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, val toolName: String, cause: Throwable? = null) : KonanException(message, cause)
|
||||||
|
|
||||||
@@ -18,29 +18,51 @@ package org.jetbrains.kotlin.konan.exec
|
|||||||
|
|
||||||
import java.lang.ProcessBuilder
|
import java.lang.ProcessBuilder
|
||||||
import java.lang.ProcessBuilder.Redirect
|
import java.lang.ProcessBuilder.Redirect
|
||||||
|
import org.jetbrains.kotlin.konan.KonanExternalToolFailure
|
||||||
|
|
||||||
fun executeCommand(command: List<String>) =
|
|
||||||
executeCommand(*command.toTypedArray())
|
|
||||||
|
|
||||||
fun executeCommand(vararg command: String): Int {
|
open class Command(initialCommand: List<String>) {
|
||||||
// TODO: need a verbose logger here.
|
|
||||||
|
|
||||||
val builder = ProcessBuilder(command.asList())
|
constructor(tool: String) : this(listOf(tool))
|
||||||
|
constructor(vararg command: String) : this(command.toList<String>())
|
||||||
|
protected val command = initialCommand.toMutableList()
|
||||||
|
|
||||||
builder.redirectOutput(Redirect.INHERIT)
|
val args: List<String>
|
||||||
builder.redirectInput(Redirect.INHERIT)
|
get() = command.drop(1)
|
||||||
builder.redirectError(Redirect.INHERIT)
|
|
||||||
|
|
||||||
val process = builder.start()
|
operator fun String.unaryPlus(): Command {
|
||||||
val exitCode = process.waitFor()
|
command += this
|
||||||
return exitCode
|
return this@Command
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun List<String>.unaryPlus(): Command {
|
||||||
|
command.addAll(this)
|
||||||
|
return this@Command
|
||||||
|
}
|
||||||
|
|
||||||
|
var logger: ((() -> String)->Unit)? = null
|
||||||
|
|
||||||
|
fun logWith(newLogger: ((() -> String)->Unit)): Command {
|
||||||
|
logger = newLogger
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun runProcess(): Int {
|
||||||
|
val builder = ProcessBuilder(command)
|
||||||
|
|
||||||
|
builder.redirectOutput(Redirect.INHERIT)
|
||||||
|
builder.redirectInput(Redirect.INHERIT)
|
||||||
|
builder.redirectError(Redirect.INHERIT)
|
||||||
|
|
||||||
|
val process = builder.start()
|
||||||
|
val exitCode = process.waitFor()
|
||||||
|
return exitCode
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun execute() {
|
||||||
|
if (logger != null) logger!! { command.toList<String>().joinToString(" ") }
|
||||||
|
|
||||||
|
val code = runProcess()
|
||||||
|
if (code != 0) throw KonanExternalToolFailure("The ${command[0]} command returned non-zero exit code: $code.", command[0])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun runTool(command: List<String>) =
|
|
||||||
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.")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user