Moved clang flags out of the root build.gardle.

Collected clang args in ClangArgs.kt
Switched dependencies/build.gradle to KonanTarget.
Introduced KonanProperties to take care of target specific properties.
This commit is contained in:
Alexander Gorshenev
2017-07-06 12:39:02 +03:00
committed by alexander-gorshenev
parent 1d532729a8
commit 55c4966203
30 changed files with 432 additions and 343 deletions
@@ -0,0 +1,103 @@
/*
* 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 -> 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.target
import org.jetbrains.kotlin.konan.properties.KonanProperties
import org.jetbrains.kotlin.konan.file.File
class ClangTarget(val target: KonanTarget, val konanProperties: KonanProperties) {
val sysRoot = konanProperties.absoluteTargetSysRoot!!
val targetArg = konanProperties.targetArg
val specificClangArgs: List<String> get() {
return when (target) {
KonanTarget.LINUX ->
listOf("--sysroot=$sysRoot",
"-DUSE_GCC_UNWIND=1", "-DUSE_ELF_SYMBOLS=1", "-DELFSIZE=64")
KonanTarget.RASPBERRYPI ->
listOf("-target", targetArg!!,
"-mfpu=vfp", "-mfloat-abi=hard",
"-DUSE_GCC_UNWIND=1", "-DUSE_ELF_SYMBOLS=1", "-DELFSIZE=32",
"--sysroot=$sysRoot",
// TODO: those two are hacks.
"-I$sysRoot/usr/include/c++/4.8.3",
"-I$sysRoot/usr/include/c++/4.8.3/arm-linux-gnueabihf")
KonanTarget.MINGW ->
listOf("-target", targetArg!!, "--sysroot=$sysRoot",
"-DUSE_GCC_UNWIND=1", "-DUSE_PE_COFF_SYMBOLS=1", "-DKONAN_WINDOWS=1")
KonanTarget.MACBOOK ->
listOf("--sysroot=$sysRoot", "-mmacosx-version-min=10.11")
KonanTarget.IPHONE ->
listOf("-stdlib=libc++", "-arch", "arm64", "-isysroot", "$sysRoot", "-miphoneos-version-min=8.0.0")
KonanTarget.IPHONE_SIM ->
listOf("-stdlib=libc++", "-isysroot", "$sysRoot", "-miphoneos-version-min=8.0.0")
KonanTarget.ANDROID_ARM32 ->
listOf("-target", targetArg!!,
"--sysroot=$sysRoot",
"-D__ANDROID__", "-DUSE_GCC_UNWIND=1", "-DUSE_ELF_SYMBOLS=1", "-DELFSIZE=32", "-DKONAN_ANDROID",
// HACKS!
"-I$sysRoot/usr/include/c++/4.9.x",
"-I$sysRoot/usr/include/c++/4.9.x/arm-linux-androideabi")
KonanTarget.ANDROID_ARM64 ->
listOf("-target", targetArg!!,
"--sysroot=$sysRoot",
"-D__ANDROID__", "-DUSE_GCC_UNWIND=1", "-DUSE_ELF_SYMBOLS=1", "-DELFSIZE=64", "-DKONAN_ANDROID",
// HACKS!
"-I$sysRoot/usr/include/c++/4.9.x",
"-I$sysRoot/usr/include/c++/4.9.x/aarch64-linux-android")
}
}
}
class ClangHost(val hostProperties: KonanProperties) {
val targetToolchain get() = hostProperties.absoluteTargetToolchain
val gccToolchain get() = hostProperties.absoluteGccToolchain
val sysRoot get() = hostProperties.absoluteTargetSysRoot
val llvmDir get() = hostProperties.absoluteLlvmHome
val binDir = when(TargetManager.host) {
KonanTarget.LINUX -> "$targetToolchain/bin"
KonanTarget.MINGW -> "$sysRoot/bin"
KonanTarget.MACBOOK -> "$sysRoot/usr/bin"
else -> throw TargetSupportException("Unexpected host platform")
}
private val extraHostClangArgs =
if (TargetManager.host == KonanTarget.LINUX) {
listOf("--gcc-toolchain=$gccToolchain")
} else {
emptyList()
}
val commonClangArgs = listOf("-B$binDir") + extraHostClangArgs
val hostClangPath = listOf("$llvmDir/bin", binDir)
private val jdkHome = File.jdkHome.absoluteFile.parent
val hostCompilerArgsForJni = listOf("", TargetManager.jniHostPlatformIncludeDir).map { "-I$jdkHome/include/$it" }
}
@@ -0,0 +1,43 @@
/*
* 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 -> 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.target
import org.jetbrains.kotlin.konan.properties.*
class ClangManager(val properties: Properties, val baseDir: String) {
private val host = TargetManager.host
private val enabledTargets = TargetManager.enabled
private val konanProperties = enabledTargets.map {
it to KonanProperties(it, properties, baseDir)
}.toMap()
private val targetClangs = enabledTargets.map {
it to ClangTarget(it, konanProperties[it]!!)
}.toMap()
private val hostClang = ClangHost(konanProperties[host]!!)
// These are converted to arrays to be convenient
// in groovy plugins.
val hostClangArgs = (hostClang.commonClangArgs + targetClangs[host]!!.specificClangArgs).toTypedArray()
fun targetClangArgs(target: KonanTarget)
= (hostClang.commonClangArgs + targetClangs[target]!!.specificClangArgs).toTypedArray()
val hostCompilerArgsForJni = hostClang.hostCompilerArgsForJni.toTypedArray()
}
@@ -0,0 +1,72 @@
/*
* 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 -> 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.properties
import org.jetbrains.kotlin.konan.file.*
import org.jetbrains.kotlin.konan.target.*
class KonanProperties(val target: KonanTarget, val properties: Properties, val baseDir: String? = null) {
fun targetString(key: String): String?
= properties.targetString(key, target)
fun targetList(key: String): List<String>
= properties.targetList(key, target)
fun hostString(key: String): String?
= properties.hostString(key)
fun hostList(key: String): List<String>
= properties.hostList(key)
fun hostTargetString(key: String): String?
= properties.hostTargetString(key, target)
fun hostTargetList(key: String): List<String>
= properties.hostTargetList(key, target)
// TODO: Delegate to a map?
val llvmLtoNooptFlags get() = targetList("llvmLtoNooptFlags")
val llvmLtoOptFlags get() = targetList("llvmLtoOptFlags")
val llvmLtoFlags get() = targetList("llvmLtoFlags")
val entrySelector get() = targetList("entrySelector")
val linkerOptimizationFlags get() = targetList("linkerOptimizationFlags")
val linkerKonanFlags get() = targetList("linkerKonanFlags")
val linkerDebugFlags get() = targetList("linkerDebugFlags")
val llvmDebugOptFlags get() = targetList("llvmDebugOptFlags")
val targetSysRoot get() = targetString("targetSysRoot")
val libffiDir get() = targetString("libffiDir")
val gccToolchain get() = targetString("gccToolchain")
val targetArg get() = targetString("quadruple")
val llvmHome get() = targetString("llvmHome")
// Notice: this one is host-target.
val targetToolchain get() = hostTargetString("targetToolchain")
fun absolute(value: String?) = "${baseDir!!}/${value!!}"
val absoluteTargetSysRoot get() = absolute(targetSysRoot)
val absoluteTargetToolchain get() = absolute(targetToolchain)
val absoluteGccToolchain get() = absolute(gccToolchain)
val absoluteLlvmHome get() = absolute(llvmHome)
val absoluteLibffiDir get() = absolute(libffiDir)
val mingwWithLlvm: String?
get() {
if (target != KonanTarget.MINGW) {
error("Only mingw target can have '.mingwWithLlvm' property")
}
// TODO: make it a property in the konan.properties.
// Use (equal) llvmHome fow now.
return targetString("llvmHome")
}
}
@@ -24,9 +24,13 @@ enum class KonanTarget(val targetSuffix: String, val programSuffix: String, var
LINUX("linux", "kexe"),
MINGW("mingw", "exe"),
MACBOOK("osx", "kexe"),
RASPBERRYPI("raspberrypi", "kexe")
RASPBERRYPI("raspberrypi", "kexe");
val userName get() = name.toLowerCase()
}
fun hostTargetSuffix(host: KonanTarget, target: KonanTarget) =
if (target == host) host.targetSuffix else "${host.targetSuffix}-${target.targetSuffix}"
enum class CompilerOutputKind {
PROGRAM {
override fun suffix(target: KonanTarget?) = ".${target!!.programSuffix}"
@@ -42,37 +46,15 @@ enum class CompilerOutputKind {
}
class TargetManager(val userRequest: String? = null) {
val targets = KonanTarget.values().associate{ it.name.toLowerCase() to it }
val targets = KonanTarget.values().associate{ it.userName to it }
val target = determineCurrent()
val targetName
get() = target.name.toLowerCase()
init {
when (host) {
KonanTarget.LINUX -> {
KonanTarget.LINUX.enabled = true
KonanTarget.RASPBERRYPI.enabled = true
KonanTarget.ANDROID_ARM32.enabled = true
KonanTarget.ANDROID_ARM64.enabled = true
}
KonanTarget.MINGW -> {
KonanTarget.MINGW.enabled = true
}
KonanTarget.MACBOOK -> {
KonanTarget.MACBOOK.enabled = true
KonanTarget.IPHONE.enabled = true
KonanTarget.IPHONE_SIM.enabled = true
KonanTarget.ANDROID_ARM32.enabled = true
KonanTarget.ANDROID_ARM64.enabled = true
}
else ->
error("Unknown host platform: $host")
}
}
fun known(name: String): String {
if (targets[name] == null) {
error("Unknown target: $name. Use -list_targets to see the list of available targets")
throw TargetSupportException("Unknown target: $name. Use -list_targets to see the list of available targets")
}
return name
}
@@ -94,36 +76,52 @@ class TargetManager(val userRequest: String? = null) {
}
}
val hostSuffix get() = host.targetSuffix
val hostTargetSuffix get() =
if (target == host) host.targetSuffix else "${host.targetSuffix}-${target.targetSuffix}"
val hostTargetSuffix get() = hostTargetSuffix(host, target)
val targetSuffix get() = target.targetSuffix
val programSuffix get() = CompilerOutputKind.PROGRAM.suffix(target)
companion object {
fun host_os(): String {
val javaOsName = System.getProperty("os.name")
return when {
javaOsName == "Mac OS X" -> "osx"
javaOsName == "Linux" -> "linux"
javaOsName.startsWith("Windows") -> "windows"
else -> error("Unknown operating system: ${javaOsName}")
else -> throw TargetSupportException("Unknown operating system: ${javaOsName}")
}
}
@JvmStatic
fun simpleOsName(): String {
val hostOs = host_os()
return if (hostOs == "osx") "macos" else hostOs
}
@JvmStatic
fun longerSystemName(): String = when (host) {
KonanTarget.MACBOOK -> "darwin-macos"
KonanTarget.LINUX -> "linux-x86-64"
KonanTarget.MINGW -> "windows-x86-64"
else -> throw TargetSupportException("Unknown host: $host")
}
val jniHostPlatformIncludeDir: String
get() = when(host) {
KonanTarget.MACBOOK -> "darwin"
KonanTarget.LINUX -> "linux"
KonanTarget.MINGW ->"win32"
else -> throw TargetSupportException("Unknown host: $host.")
}
fun host_arch(): String {
val javaArch = System.getProperty("os.arch")
return when (javaArch) {
"x86_64" -> "x86_64"
"amd64" -> "x86_64"
"arm64" -> "arm64"
else -> error("Unknown hardware platform: ${javaArch}")
else -> throw TargetSupportException("Unknown hardware platform: ${javaArch}")
}
}
@@ -131,8 +129,41 @@ class TargetManager(val userRequest: String? = null) {
"osx" -> KonanTarget.MACBOOK
"linux" -> KonanTarget.LINUX
"windows" -> KonanTarget.MINGW
else -> error("Unknown host target: ${host_os()} ${host_arch()}")
else -> throw TargetSupportException("Unknown host target: ${host_os()} ${host_arch()}")
}
val hostSuffix get() = host.targetSuffix
@JvmStatic
val hostName get() = host.name.toLowerCase()
init {
when (host) {
KonanTarget.LINUX -> {
KonanTarget.LINUX.enabled = true
KonanTarget.RASPBERRYPI.enabled = true
KonanTarget.ANDROID_ARM32.enabled = true
KonanTarget.ANDROID_ARM64.enabled = true
}
KonanTarget.MINGW -> {
KonanTarget.MINGW.enabled = true
}
KonanTarget.MACBOOK -> {
KonanTarget.MACBOOK.enabled = true
KonanTarget.IPHONE.enabled = true
//KonanTarget.IPHONE_SIM.enabled = true
KonanTarget.ANDROID_ARM32.enabled = true
KonanTarget.ANDROID_ARM64.enabled = true
}
else ->
throw TargetSupportException("Unknown host platform: $host")
}
}
@JvmStatic
val enabled: List<KonanTarget>
get() = KonanTarget.values().asList().filter { it.enabled }
}
}
class TargetSupportException (message: String = "", cause: Throwable? = null) : Exception(message, cause)
@@ -0,0 +1,61 @@
/*
* 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.properties
import org.jetbrains.kotlin.konan.file.*
typealias Properties = java.util.Properties
fun File.loadProperties(): Properties {
val properties = java.util.Properties()
this.bufferedReader().use { reader ->
properties.load(reader)
}
return properties
}
fun loadProperties(path: String): Properties = File(path).loadProperties()
fun File.saveProperties(properties: Properties) {
this.outputStream().use {
properties.store(it, null)
}
}
fun Properties.saveToFile(file: File) = file.saveProperties(this)
fun Properties.propertyString(key: String, suffix: String? = null): String?
= this.getProperty(key.suffix(suffix))
/**
* TODO: this method working with suffixes should be replaced with
* functionality borrowed from def file parser and unified for interop tool
* and kotlin compiler.
*/
fun Properties.propertyList(key: String, suffix: String? = null): List<String> {
val value = this.getProperty(key.suffix(suffix)) ?: this.getProperty(key)
return value?.split(' ') ?: emptyList()
}
fun Properties.hasProperty(key: String, suffix: String? = null): Boolean
= this.getProperty(key.suffix(suffix)) != null
fun String.suffix(suf: String?): String =
if (suf == null) this
else "${this}.$suf"
@@ -0,0 +1,37 @@
/*
* 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.properties
import org.jetbrains.kotlin.konan.target.*
fun Properties.hostString(name: String): String?
= this.propertyString(name, TargetManager.host.targetSuffix)
fun Properties.hostList(name: String): List<String>
= this.propertyList(name, TargetManager.host.targetSuffix)
fun Properties.targetString(name: String, target: KonanTarget): String?
= this.propertyString(name, target.targetSuffix)
fun Properties.targetList(name: String, target: KonanTarget): List<String>
= this.propertyList(name, target.targetSuffix)
fun Properties.hostTargetString(name: String, target: KonanTarget): String?
= this.propertyString(name, hostTargetSuffix(TargetManager.host, target))
fun Properties.hostTargetList(name: String, target: KonanTarget): List<String>
= this.propertyList(name, hostTargetSuffix(TargetManager.host, target))
@@ -0,0 +1,192 @@
/*
* 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.file
import java.io.BufferedReader
import java.io.InputStream
import java.io.InputStreamReader
import java.net.URI
import java.nio.file.*
import java.nio.file.attribute.BasicFileAttributes
class File constructor(internal val javaPath: Path) {
constructor(parent: Path, child: String): this(parent.resolve(child))
constructor(parent: File, child: String): this(parent.javaPath.resolve(child))
constructor(path: String): this(Paths.get(path))
constructor(parent: String, child: String): this(Paths.get(parent, child))
val path: String
get() = javaPath.toString()
val absolutePath: String
get() = javaPath.toAbsolutePath().toString()
val absoluteFile: File
get() = File(absolutePath)
val name: String
get() = javaPath.fileName.toString()
val parent: String
get() = javaPath.parent.toString()
val exists
get() = Files.exists(javaPath)
val isDirectory
get() = Files.isDirectory(javaPath)
val isFile
get() = Files.isRegularFile(javaPath)
val isAbsolute
get() = javaPath.isAbsolute()
val listFiles: List<File>
get() {
val stream = Files.newDirectoryStream(javaPath)
val result = mutableListOf<File>()
for (entry in stream) {
result.add(File(entry))
}
return result
}
fun copyTo(destination: File, vararg options: StandardCopyOption) {
Files.copy(javaPath, destination.javaPath, StandardCopyOption.REPLACE_EXISTING)
}
fun recursiveCopyTo(destination: File) {
val sourcePath = javaPath
val destPath = destination.javaPath
sourcePath.recursiveCopyTo(destPath)
}
fun mkdirs() = Files.createDirectories(javaPath)
fun delete() = Files.deleteIfExists(javaPath)
fun deleteRecursively() = postorder{Files.delete(it)}
fun deleteOnExitRecursively() = preorder{File(it).deleteOnExit()}
fun preorder(task: (Path) -> Unit) {
if (!this.exists) return
Files.walkFileTree(javaPath, object: SimpleFileVisitor<Path>() {
override fun visitFile(file: Path?, attrs: BasicFileAttributes?): FileVisitResult {
task(file!!)
return FileVisitResult.CONTINUE
}
override fun preVisitDirectory(dir: Path?, attrs: BasicFileAttributes?): FileVisitResult {
task(dir!!)
return FileVisitResult.CONTINUE
}
})
}
fun postorder(task: (Path) -> Unit) {
if (!this.exists) return
Files.walkFileTree(javaPath, object: SimpleFileVisitor<Path>() {
override fun visitFile(file: Path?, attrs: BasicFileAttributes?): FileVisitResult {
task(file!!)
return FileVisitResult.CONTINUE
}
override fun postVisitDirectory(dir: Path?, exc: java.io.IOException?): FileVisitResult {
task(dir!!)
return FileVisitResult.CONTINUE
}
})
}
fun deleteOnExit() {
// Works only on the default file system,
// but that's okay for now.
javaPath.toFile().deleteOnExit()
}
fun readBytes() = Files.readAllBytes(javaPath)
fun writeBytes(bytes: ByteArray) = Files.write(javaPath, bytes)
fun forEachLine(action: (String) -> Unit) { Files.lines(javaPath).forEach { action(it) } }
override fun toString() = path
// TODO: Consider removeing these after konanazing java.util.Properties.
fun bufferedReader() = Files.newBufferedReader(javaPath)
fun outputStream() = Files.newOutputStream(javaPath)
companion object {
val userDir
get() = File(System.getProperty("user.dir"))
val userHome
get() = File(System.getProperty("user.home"))
val jdkHome
get() = File(System.getProperty("java.home"))
}
}
fun String.File(): File = File(this)
fun Path.File(): File = File(this)
fun createTempFile(name: String, suffix: String? = null)
= Files.createTempFile(name, suffix).File()
fun createTempDir(name: String): File
= Files.createTempDirectory(name).File()
private val File.zipUri: URI
get() = URI.create("jar:${this.toPath().toUri()}")
fun File.zipFileSystem(mutable: Boolean = false): FileSystem {
val zipUri = this.zipUri
val allowCreation = if (mutable) "true" else "false"
val attributes = hashMapOf("create" to if (mutable) "true" else "false")
return FileSystems.newFileSystem(zipUri, attributes, null)
}
fun File.mutableZipFileSystem() = this.zipFileSystem(mutable = true)
fun File.zipPath(path: String): Path
= this.zipFileSystem().getPath(path)
val File.asZipRoot: File
get() = File(this.zipPath("/"))
val File.asWritableZipRoot: File
get() = File(this.mutableZipFileSystem().getPath("/"))
private fun File.toPath() = Paths.get(this.path)
fun File.zipDirAs(unixFile: File) {
val zipRoot = unixFile.asWritableZipRoot
this.recursiveCopyTo(zipRoot)
zipRoot.javaPath.fileSystem.close()
}
fun Path.recursiveCopyTo(destPath: Path) {
val sourcePath = this
Files.walk(sourcePath).forEach next@ { oldPath ->
val relative = sourcePath.relativize(oldPath)
val destFs = destPath.getFileSystem()
// We are copying files between file systems,
// so pass the relative path through the Sting.
val newPath = destFs.getPath(destPath.toString(), relative.toString())
// File systems don't allow replacing an existing root.
if (newPath == newPath.getRoot()) return@next
Files.copy(oldPath, newPath,
StandardCopyOption.REPLACE_EXISTING)
}
}
fun bufferedReader(errorStream: InputStream?) = BufferedReader(InputStreamReader(errorStream))