diff --git a/Interop/StubGenerator/build.gradle b/Interop/StubGenerator/build.gradle index 8373b69c7d2..0af4c5cef90 100644 --- a/Interop/StubGenerator/build.gradle +++ b/Interop/StubGenerator/build.gradle @@ -26,4 +26,5 @@ mainClassName = "org.jetbrains.kotlin.native.interop.gen.jvm.MainKt" dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" compile project(':Interop:Indexer') + compile project(':shared') } diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt index e06e937bf96..e414f145aff 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt @@ -16,11 +16,11 @@ package org.jetbrains.kotlin.native.interop.gen.jvm +import org.jetbrains.kotlin.konan.util.DefFile import org.jetbrains.kotlin.native.interop.gen.HeadersInclusionPolicyImpl import org.jetbrains.kotlin.native.interop.gen.ImportsImpl import org.jetbrains.kotlin.native.interop.indexer.* import java.io.File -import java.io.StringReader import java.lang.IllegalArgumentException import java.util.* import kotlin.reflect.KFunction @@ -126,10 +126,6 @@ private fun ProcessBuilder.runExpectingSuccess() { } } -private fun Properties.getSpaceSeparated(name: String): List { - return this.getProperty(name)?.split(' ')?.filter { it.isNotEmpty() } ?: emptyList() -} - private fun Collection.atMostOne(): T? { return when (this.size) { 0 -> null @@ -271,43 +267,6 @@ private fun Properties.storeProperties(file: File) { } } -private fun parseDefFile(file: File?, substitutions: Map): Triple> { - val properties = Properties() - - if (file == null) { - return Triple(properties, Properties(), emptyList()) - } - - val lines = file.readLines() - - val separator = "---" - val separatorIndex = lines.indexOf(separator) - - val propertyLines: List - val headerLines: List - - if (separatorIndex != -1) { - propertyLines = lines.subList(0, separatorIndex) - headerLines = lines.subList(separatorIndex + 1, lines.size) - } else { - propertyLines = lines - headerLines = emptyList() - } - - val propertiesReader = StringReader(propertyLines.joinToString(System.lineSeparator())) - properties.load(propertiesReader) - - // Pass unsubstituted copy of properties we have obtained from `.def` - // to compiler `-maniest`. - val manifestAddendProperties = properties.duplicate() - - substitute(properties, substitutions) - - return Triple(properties, manifestAddendProperties, headerLines) -} - -private fun Properties.duplicate() = Properties().apply { putAll(this@duplicate) } - private fun usage() { println(""" Run interop tool with -def .def @@ -327,13 +286,13 @@ private fun downloadDependencies(dependenciesRoot: String, target: String, konan maybeExecuteHelper(dependenciesRoot, konanProperties, dependencyList) } -private fun selectNativeLanguage(config: Properties): Language { +private fun selectNativeLanguage(config: DefFile.DefFileConfig): Language { val languages = mapOf( "C" to Language.C, "Objective-C" to Language.OBJECTIVE_C ) - val language = config.getProperty("language") ?: return Language.C + val language = config.language ?: return Language.C return languages[language] ?: error("Unexpected language '$language'. Possible values are: ${languages.keys.joinToString { "'$it'" }}") @@ -389,8 +348,7 @@ private fun processLib(konanHome: String, return } - val (config, manifestAddendProperties, defHeaderLines) - = parseDefFile(defFile, substitutions) + val def = DefFile(defFile, substitutions) val konanFileName = args["-properties"]?.single() ?: "${konanHome}/konan/konan.properties" @@ -409,10 +367,10 @@ private fun processLib(konanHome: String, val verbose = args["-verbose"].isTrue() val defaultOpts = konanProperties.defaultCompilerOpts(target, dependencies) - val headerFiles = config.getSpaceSeparated("headers") + additionalHeaders - val language = selectNativeLanguage(config) + val headerFiles = def.config.headers + additionalHeaders + val language = selectNativeLanguage(def.config) val compilerOpts: List = mutableListOf().apply { - addAll(config.getSpaceSeparated("compilerOpts")) + addAll(def.config.compilerOpts) addAll(defaultOpts) addAll(additionalCompilerOpts) addAll(when (language) { @@ -428,19 +386,19 @@ private fun processLib(konanHome: String, }) } val compiler = "clang" - val excludeSystemLibs = config.getProperty("excludeSystemLibs")?.toBoolean() ?: false - val excludeDependentModules = config.getProperty("excludeDependentModules")?.toBoolean() ?: false + val excludeSystemLibs = def.config.excludeSystemLibs + val excludeDependentModules = def.config.excludeDependentModules - val entryPoint = config.getSpaceSeparated("entryPoint").atMostOne() + val entryPoint = def.config.entryPoints.atMostOne() val linkerOpts = - config.getSpaceSeparated("linkerOpts").toTypedArray() + defaultOpts + additionalLinkerOpts - val linker = args["-linker"]?.atMostOne() ?: config.getProperty("linker") ?: "clang" - val excludedFunctions = config.getSpaceSeparated("excludedFunctions").toSet() - val staticLibraries = config.getSpaceSeparated("staticLibraries") + args["-staticLibrary"].orEmpty() - val libraryPaths = config.getSpaceSeparated("libraryPaths") + args["-libraryPath"].orEmpty() + def.config.linkerOpts.toTypedArray() + defaultOpts + additionalLinkerOpts + val linker = args["-linker"]?.atMostOne() ?: def.config.linker + val excludedFunctions = def.config.excludedFunctions.toSet() + val staticLibraries = def.config.staticLibraries + args["-staticLibrary"].orEmpty() + val libraryPaths = def.config.libraryPaths + args["-libraryPath"].orEmpty() argsToCompiler ?. let { it.addAll(argsToCompiler(staticLibraries, libraryPaths)) } - val fqParts = (args["-pkg"]?.atMostOne() ?: config.getProperty("package"))?.let { + val fqParts = (args["-pkg"]?.atMostOne() ?: def.config.packageName)?.let { it.split('.') } ?: defFile!!.name.split('.').reversed().drop(1) @@ -452,13 +410,13 @@ private fun processLib(konanHome: String, val libName = args["-cstubsname"]?.atMostOne() ?: fqParts.joinToString("") + "stubs" - val headerFilterGlobs = config.getSpaceSeparated("headerFilter") + val headerFilterGlobs = def.config.headerFilter val imports = parseImports(args) val headerInclusionPolicy = HeadersInclusionPolicyImpl(headerFilterGlobs, imports) val library = NativeLibrary( includes = headerFiles, - additionalPreambleLines = defHeaderLines, + additionalPreambleLines = def.defHeaderLines, compilerArgs = compilerOpts, language = language, excludeSystemLibs = excludeSystemLibs, @@ -470,8 +428,8 @@ private fun processLib(konanHome: String, library = library, pkgName = outKtPkg, excludedFunctions = excludedFunctions, - strictEnums = config.getSpaceSeparated("strictEnums").toSet(), - nonStrictEnums = config.getSpaceSeparated("nonStrictEnums").toSet() + strictEnums = def.config.strictEnums.toSet(), + nonStrictEnums = def.config.nonStrictEnums.toSet() ) val nativeIndex = buildNativeIndex(library) @@ -490,13 +448,13 @@ private fun processLib(konanHome: String, } // TODO: if a library has partially included headers, then it shouldn't be used as a dependency. - manifestAddendProperties["includedHeaders"] = nativeIndex.includedHeaders.joinToString(" ") { it.value } - manifestAddendProperties["pkg"] = outKtPkg + def.manifestAddendProperties["includedHeaders"] = nativeIndex.includedHeaders.joinToString(" ") { it.value } + def.manifestAddendProperties["pkg"] = outKtPkg - gen.addManifestProperties(manifestAddendProperties) + gen.addManifestProperties(def.manifestAddendProperties) manifestAddend?.parentFile?.mkdirs() - manifestAddend?.let { manifestAddendProperties.storeProperties(it) } + manifestAddend?.let { def.manifestAddendProperties.storeProperties(it) } val workDir = defFile?.absoluteFile?.parentFile ?: File(userDir) diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DefFile.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DefFile.kt new file mode 100644 index 00000000000..edd45e65413 --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DefFile.kt @@ -0,0 +1,133 @@ +package org.jetbrains.kotlin.konan.util + +import java.io.File +import java.io.StringReader +import java.util.* + +class DefFile(val file:File?, val config:DefFileConfig, val manifestAddendProperties:Properties, val defHeaderLines:List) { + private constructor(file0:File?, triple: Triple>): this(file0, DefFileConfig(triple.first), triple.second, triple.third) + constructor(file:File?, substitutions: Map) : this(file, parseDefFile(file, substitutions)) + + val name by lazy { + file?.nameWithoutExtension ?: "" + } + class DefFileConfig(private val properties: Properties) { + val headers by lazy { + properties.getSpaceSeparated("headers") + } + + val language by lazy { + properties.getProperty("language") + } + + val compilerOpts by lazy { + properties.getSpaceSeparated("compilerOpts") + } + + val excludeSystemLibs by lazy { + properties.getProperty("excludeSystemLibs")?.toBoolean() ?: false + } + + val excludeDependentModules by lazy { + properties.getProperty("excludeDependentModules")?.toBoolean() ?: false + } + + val entryPoints by lazy { + properties.getSpaceSeparated("entryPoint") + } + + val linkerOpts by lazy { + properties.getSpaceSeparated("linkerOpts") + } + + val linker by lazy { + properties.getProperty("linker", "clang") + } + + val excludedFunctions by lazy { + properties.getSpaceSeparated("excludedFunctions") + } + + val staticLibraries by lazy { + properties.getSpaceSeparated("staticLibraries") + } + + val libraryPaths by lazy { + properties.getSpaceSeparated("libraryPaths") + } + + val packageName by lazy { + properties.getProperty("package") + } + + val headerFilter by lazy { + properties.getSpaceSeparated("headerFilter") + } + + val strictEnums by lazy { + properties.getSpaceSeparated("strictEnums") + } + + val nonStrictEnums by lazy { + properties.getSpaceSeparated("nonStrictEnums") + } + } +} + +private fun Properties.getSpaceSeparated(name: String): List { + return this.getProperty(name)?.split(' ')?.filter { it.isNotEmpty() } ?: emptyList() +} + +private fun parseDefFile(file: File?, substitutions: Map): Triple> { + val properties = Properties() + + if (file == null) { + return Triple(properties, Properties(), emptyList()) + } + + val lines = file.readLines() + + val separator = "---" + val separatorIndex = lines.indexOf(separator) + + val propertyLines: List + val headerLines: List + + if (separatorIndex != -1) { + propertyLines = lines.subList(0, separatorIndex) + headerLines = lines.subList(separatorIndex + 1, lines.size) + } else { + propertyLines = lines + headerLines = emptyList() + } + + val propertiesReader = StringReader(propertyLines.joinToString(System.lineSeparator())) + properties.load(propertiesReader) + + // Pass unsubstituted copy of properties we have obtained from `.def` + // to compiler `-maniest`. + val manifestAddendProperties = properties.duplicate() + + substitute(properties, substitutions) + + return Triple(properties, manifestAddendProperties, headerLines) +} + +// Performs substitution similar to: +// foo = ${foo} ${foo.${arch}} ${foo.${os}} +fun substitute(properties: Properties, substitutions: Map) { + for (key in properties.stringPropertyNames()) { + for (substitution in substitutions.values) { + val suffix = ".$substitution" + if (key.endsWith(suffix)) { + val baseKey = key.removeSuffix(suffix) + val oldValue = properties.getProperty(baseKey, "") + val appendedValue = properties.getProperty(key, "") + val newValue = if (oldValue != "") "$oldValue $appendedValue" else appendedValue + properties.setProperty(baseKey, newValue) + } + } + } +} + +private fun Properties.duplicate() = Properties().apply { putAll(this@duplicate) }