Android NDK support and build system refactoring (#585)

This commit is contained in:
Nikolay Igotti
2017-05-24 00:20:19 +03:00
committed by GitHub
parent 48742b7f3f
commit 42197a2bec
26 changed files with 404 additions and 232 deletions
@@ -169,7 +169,7 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
}
if (name == "__gnuc_va_list" || name == "va_list") {
if (name == "__gnuc_va_list" || name == "va_list" || name == "__va_list") {
// TODO: fix GNUC varargs support.
return UnsupportedType
}
@@ -26,10 +26,9 @@ import kotlin.reflect.KFunction
fun main(args: Array<String>) {
val konanHome = File(System.getProperty("konan.home")).absolutePath
// TODO: remove OSX defaults.
val substitutions = mapOf(
"arch" to (System.getenv("TARGET_ARCH") ?: "x86-64"),
"os" to (System.getenv("TARGET_OS") ?: detectHost())
"os" to (System.getenv("TARGET_OS") ?: host)
)
processLib(konanHome, substitutions, parseArgs(args))
@@ -37,46 +36,54 @@ fun main(args: Array<String>) {
private fun parseArgs(args: Array<String>): Map<String, List<String>> {
val commandLine = mutableMapOf<String, MutableList<String>>()
for(index in 0..args.size-1 step 2) {
for (index in 0..args.size - 1 step 2) {
val key = args[index]
if (key[0] != '-') {
throw IllegalArgumentException("Expected a flag with initial dash: $key")
}
if (index+1 == args.size) {
if (index + 1 == args.size) {
throw IllegalArgumentException("Expected an value after $key")
}
val value = args[index+1]
commandLine[key] ?. add(value) ?: commandLine.put(key, mutableListOf(value))
val value = args[index + 1]
commandLine[key]?.add(value) ?: commandLine.put(key, mutableListOf(value))
}
return commandLine
}
private fun detectHost(): String {
private val host: String by lazy {
val os = System.getProperty("os.name")
when (os) {
"Linux" -> return "linux"
"Windows" -> return "win"
"Mac OS X" -> return "osx"
"FreeBSD" -> return "freebsd"
"Linux" -> "linux"
"Windows" -> "win"
"Mac OS X" -> "osx"
"FreeBSD" -> "freebsd"
else -> {
throw IllegalArgumentException("we don't know ${os} value")
}
}
}
private fun defaultTarget() = detectHost()
private val defaultTarget: String by lazy {
host
}
// TODO: share KonanTarget class here.
private val knownTargets = mapOf(
"host" to defaultTarget(),
"linux" to "linux",
"macbook" to "osx",
"iphone" to "osx-ios",
"iphone_sim" to "osx-ios-sim",
"raspberrypi" to "linux-raspberrypi")
"host" to host,
"linux" to "linux",
"macbook" to "osx",
"osx" to "osx",
"iphone" to "ios",
"ios" to "ios",
"iphone_sim" to "ios_sim",
"ios_sim" to "ios_sim",
"raspberrypi" to "raspberrypi",
"android_arm32" to "android_arm32"
)
private fun String.targetSuffix(): String =
knownTargets[this] ?: error("Unsupported target $this.")
knownTargets[this] ?: error("Unsupported target $this.")
// Performs substitution similar to:
// foo = ${foo} ${foo.${arch}} ${foo.${os}}
@@ -118,11 +125,17 @@ private fun String.matchesToGlob(glob: String): Boolean =
java.nio.file.FileSystems.getDefault()
.getPathMatcher("glob:$glob").matches(java.nio.file.Paths.get(this))
private fun Properties.getOsSpecific(name: String,
host: String = detectHost()): String? {
private fun Properties.getHostSpecific(
name: String) = getProperty("$name.$host")
return this.getProperty("$name.$host")
}
private fun Properties.getTargetSpecific(
name: String, target: String) = getProperty("$name.$target")
private fun Properties.getHostTargetSpecific(
name: String, target: String) = if (host != target)
getProperty("$name.$host-$target")
else
getProperty("$name.$host")
private fun List<String>?.isTrue(): Boolean {
// The rightmost wins, null != "true".
@@ -176,51 +189,41 @@ private fun maybeExecuteHelper(dependenciesRoot: String, properties: Properties,
}
private fun Properties.defaultCompilerOpts(target: String, dependencies: String): List<String> {
val arch = this.getOsSpecific("arch", target)!!
val hostSysRootDir = this.getOsSpecific("sysRoot")!!
val hostSysRoot = "$dependencies/$hostSysRootDir"
val targetSysRootDir = this.getOsSpecific("targetSysRoot", target) ?: hostSysRootDir
val targetToolchainDir = getHostTargetSpecific("targetToolchain", target)!!
val targetToolchain = "$dependencies/$targetToolchainDir"
val targetSysRootDir = getTargetSpecific("targetSysRoot", target)!!
val targetSysRoot = "$dependencies/$targetSysRootDir"
val sysRoot = targetSysRoot
val llvmHomeDir = this.getOsSpecific("llvmHome")!!
val llvmHomeDir = getHostSpecific("llvmHome")!!
val llvmHome = "$dependencies/$llvmHomeDir"
System.load("$llvmHome/lib/${System.mapLibraryName("clang")}")
val llvmVersion = this.getProperty("llvmVersion")!!
val llvmVersion = getProperty("llvmVersion")!!
// StubGenerator passes the arguments to libclang which
// works not exactly the same way as the clang binary and
// StubGenerator passes the arguments to libclang which
// works not exactly the same way as the clang binary and
// (in particular) uses different default header search path.
// See e.g. http://lists.llvm.org/pipermail/cfe-dev/2013-November/033680.html
// We workaround the problem with -isystem flag below.
val isystem = "$llvmHome/lib/clang/$llvmVersion/include"
when (detectHost()) {
val quadruple = getTargetSpecific("quadruple", target)
val arch = getTargetSpecific("arch", target)
val archSelector = if (quadruple != null)
listOf("-target", quadruple) else listOf("-arch", arch!!)
val commonArgs = listOf("-isystem", isystem, "--sysroot=$targetSysRoot")
when (host) {
"osx" -> {
val osVersionMinFlag = this.getOsSpecific("osVersionMinFlagClang", target)!!
val osVersionMinValue = this.getOsSpecific("osVersionMin", target)!!
return listOf(
"-arch", arch,
"-isystem", isystem,
"-B$hostSysRoot/usr/bin",
"--sysroot=$sysRoot",
"$osVersionMinFlag=$osVersionMinValue")
val osVersionMinFlag = getTargetSpecific("osVersionMinFlagClang", target)
val osVersionMinValue = getTargetSpecific("osVersionMin", target)
return archSelector + commonArgs + listOf("-B$targetToolchain/bin") +
(if (osVersionMinFlag != null && osVersionMinValue != null)
listOf("$osVersionMinFlag=$osVersionMinValue") else emptyList())
}
"linux" -> {
val gccToolChainDir = this.getOsSpecific("gccToolChain", target)!!
val gccToolChain= "$dependencies/$gccToolChainDir"
val quadruple = this.getOsSpecific("quadruple", target)!!
return listOf(
"-target", quadruple,
"-isystem", isystem,
"--gcc-toolchain=$gccToolChain",
"-L$llvmHome/lib",
"-B$hostSysRoot/../bin",
"--sysroot=$sysRoot")
val libGcc = getTargetSpecific("libGcc", target)
val binDir = "$targetSysRoot/${libGcc ?: "bin"}"
return archSelector + commonArgs + listOf(
"-B$binDir", "--gcc-toolchain=$targetToolchain")
}
else -> error("Unexpected target: ${target}")
}
@@ -280,7 +283,7 @@ Following flags are supported:
}
private fun downloadDependencies(dependenciesRoot: String, target: String, konanProperties: Properties) {
val dependencyList = konanProperties.getOsSpecific("dependencies", target)?.split(' ') ?: listOf<String>()
val dependencyList = konanProperties.getHostTargetSpecific("dependencies", target)?.split(' ') ?: listOf<String>()
maybeExecuteHelper(dependenciesRoot, konanProperties, dependencyList)
}
@@ -293,7 +296,7 @@ private fun processLib(konanHome: String,
val nativeLibsDir = args["-natives"]?.single() ?: userDir
val flavorName = args["-flavor"]?.single() ?: "jvm"
val flavor = KotlinPlatform.values().single { it.name.equals(flavorName, ignoreCase = true) }
val target = args["-target"]?.single()?.targetSuffix() ?: defaultTarget()
val target = args["-target"]?.single()?.targetSuffix() ?: defaultTarget
val defFile = args["-def"]?.single()?.let { File(it) }
if (defFile == null && args["-pkg"] == null) {
@@ -304,14 +307,14 @@ private fun processLib(konanHome: String,
val (config, defHeaderLines) = parseDefFile(defFile, substitutions)
val konanFileName = args["-properties"]?.single() ?:
"${konanHome}/konan/konan.properties"
"${konanHome}/konan/konan.properties"
val konanFile = File(konanFileName)
val konanProperties = loadProperties(konanFile, mapOf())
val dependencies = "$konanHome/dependencies"
val dependencies = "$konanHome/dependencies"
downloadDependencies(dependencies, target, konanProperties)
// TODO: We can provide a set of flags to find the components in the absence of 'dist' or 'dist/dependencies'.
val llvmHome = konanProperties.getOsSpecific("llvmHome")!!
val llvmHome = konanProperties.getHostSpecific("llvmHome")!!
val llvmInstallPath = "$dependencies/$llvmHome"
val additionalHeaders = args["-h"].orEmpty()
val additionalCompilerOpts = args["-copt"].orEmpty()
@@ -321,18 +324,18 @@ private fun processLib(konanHome: String,
val defaultOpts = konanProperties.defaultCompilerOpts(target, dependencies)
val headerFiles = config.getSpaceSeparated("headers") + additionalHeaders
val compilerOpts =
config.getSpaceSeparated("compilerOpts") +
defaultOpts + additionalCompilerOpts
val compilerOpts =
config.getSpaceSeparated("compilerOpts") +
defaultOpts + additionalCompilerOpts
val compiler = "clang"
val language = Language.C
val excludeSystemLibs = config.getProperty("excludeSystemLibs")?.toBoolean() ?: false
val excludeDependentModules = config.getProperty("excludeDependentModules")?.toBoolean() ?: false
val entryPoint = config.getSpaceSeparated("entryPoint").atMostOne()
val linkerOpts =
config.getSpaceSeparated("linkerOpts").toTypedArray() +
defaultOpts + additionalLinkerOpts
val linkerOpts =
config.getSpaceSeparated("linkerOpts").toTypedArray() +
defaultOpts + additionalLinkerOpts
val linker = args["-linker"]?.atMostOne() ?: config.getProperty("linker") ?: "clang"
val excludedFunctions = config.getSpaceSeparated("excludedFunctions").toSet()