From 57cfc617a64639e59f97fe045754c2bd82f89970 Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Wed, 12 Oct 2016 16:25:01 +0300 Subject: [PATCH] Interop: represent it as dependency in Gradle plugin --- .../kotlin/native/interop/gen/jvm/main.kt | 82 +++++---- InteropExample/build.gradle | 18 +- InteropExample/src/main/kotlin/llvm/LLVM.def | 41 ----- backend.native/build.gradle | 19 +- .../src/llvm/LLVM.def => llvm.def} | 3 - .../kotlin/NativeInteropPlugin.groovy | 168 +++++++++++++----- 6 files changed, 195 insertions(+), 136 deletions(-) delete mode 100644 InteropExample/src/main/kotlin/llvm/LLVM.def rename backend.native/{compiler/ir/backend.native/src/llvm/LLVM.def => llvm.def} (98%) 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 c81260852b4..407a072495f 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 @@ -1,33 +1,26 @@ package org.jetbrains.kotlin.native.interop.gen.jvm -import kotlin_native.interop.Ref.to import org.jetbrains.kotlin.native.interop.indexer.NativeIndex import org.jetbrains.kotlin.native.interop.indexer.buildNativeIndex import java.io.File import java.lang.IllegalArgumentException import java.util.* -import kotlin.system.exitProcess fun main(args: Array) { val llvmInstallPath = System.getProperty("llvmInstallPath")!! val ktGenRoot = args[0] val nativeLibsDir = args[1] - val ktSrcRoots = args.drop(2) + val defFile = args[2] + val otherArgs = args.drop(3) + // TODO: remove OSX defaults. val substitutions = mapOf( "arch" to (System.getenv("TARGET_ARCH") ?: "x86-64"), "os" to (System.getenv("TARGET_OS") ?: detectHost()) ) - - ktSrcRoots.forEach { ktSrcRoot -> - val defFiles = File(ktSrcRoot).walk().filter { it.name.endsWith(".def") } - - defFiles.forEach { defFile -> - processDefFile(ktSrcRoot, defFile, ktGenRoot, nativeLibsDir, llvmInstallPath, substitutions) - } - } + processDefFile(File(defFile), ktGenRoot, nativeLibsDir, llvmInstallPath, substitutions, otherArgs) } private fun detectHost():String { @@ -63,26 +56,55 @@ private fun substitute(properties: Properties, substitutions: Map) { +private fun String.removePrefixOrNull(prefix: String): String? { + if (this.startsWith(prefix)) { + return this.substring(prefix.length) + } else { + return null + } +} + +private fun ProcessBuilder.runExpectingSuccess() { + println(this.command().joinToString(" ")) + + val res = this.start().waitFor() + if (res != 0) { + throw Error("Process finished with non-zero exit code: $res") + } +} + +private fun processDefFile(defFile: File, + ktGenRoot: String, + nativeLibsDir: String, + llvmInstallPath: String, + substitutions: Map, + additionalArgs: List) { + val config = Properties() defFile.bufferedReader().use { reader -> config.load(reader) } substitute(config, substitutions) + val additionalCompilerOpts = additionalArgs.mapNotNull { it.removePrefixOrNull("-copt:") } + val additionalLinkerOpts = additionalArgs.mapNotNull { it.removePrefixOrNull("-lopt:") } + val headerFiles = config.getProperty("headers").split(' ') - val compilerOpts = config.getProperty("compilerOpts").split(' ') + val compilerOpts = config.getProperty("compilerOpts").split(' ') + additionalCompilerOpts val compiler = config.getProperty("compiler") - val libName = config.getProperty("libName") - val linkerOpts = config.getProperty("linkerOpts").split(' ').toTypedArray() + val linkerOpts = config.getProperty("linkerOpts").split(' ').toTypedArray() + additionalLinkerOpts val linker = config.getProperty("linker") val excludedFunctions = config.getProperty("excludedFunctions")?.split(' ')?.toSet() ?: emptySet() + val fqParts = defFile.name.split('.').reversed().drop(1) - val defFileRelative = defFile.relativeTo(File(ktSrcRoot)) - val outKtFile = File(ktGenRoot, defFileRelative.toString().substringBeforeLast(".def") + ".kt") - val outKtPkg = defFileRelative.parentFile?.path?.replace(File.separatorChar, '.') ?: "" + val outKtFileName = fqParts.last() + ".kt" + val outKtPkg = fqParts.joinToString(".") + val outKtFileRelative = (fqParts + outKtFileName).joinToString("/") + val outKtFile = File(ktGenRoot, outKtFileRelative) + + val libName = fqParts.joinToString("") + "stubs" val nativeIndex = buildNativeIndex(headerFiles, compilerOpts) @@ -113,16 +135,12 @@ private fun processDefFile(ktSrcRoot: String, defFile: File, ktGenRoot: String, *compilerArgsForJniIncludes, "-c", outCFile.path, "-o", outOFile.path) - println(compilerCmd.joinToString(" ")) + val defFileDir = defFile.parentFile - val compilerRes = ProcessBuilder(*compilerCmd) + ProcessBuilder(*compilerCmd) + .directory(defFileDir) .inheritIO() - .start() - .waitFor() - - if (compilerRes != 0) { - exitProcess(compilerRes) - } + .runExpectingSuccess() File(nativeLibsDir).mkdirs() @@ -131,16 +149,10 @@ private fun processDefFile(ktSrcRoot: String, defFile: File, ktGenRoot: String, val linkerCmd = arrayOf("$llvmInstallPath/bin/$linker", *linkerOpts, outOFile.path, "-shared", "-o", outLib, "-Wl,-flat_namespace,-undefined,dynamic_lookup") - println(linkerCmd.joinToString(" ")) - - val linkerRes = ProcessBuilder(*linkerCmd) + ProcessBuilder(*linkerCmd) + .directory(defFileDir) .inheritIO() - .start() - .waitFor() - - if (linkerRes != 0) { - exitProcess(linkerRes) - } + .runExpectingSuccess() outCFile.delete() outOFile.delete() diff --git a/InteropExample/build.gradle b/InteropExample/build.gradle index c6e3ce27cc8..74a6f498b63 100644 --- a/InteropExample/build.gradle +++ b/InteropExample/build.gradle @@ -16,16 +16,18 @@ repositories { mavenCentral() } -dependencies { - compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" +kotlinNativeInterop { + llvm { + defFile '../backend.native/llvm.def' + compilerOpts "-I$llvmInstallPath/include" + linkerOpts "-L$llvmInstallPath/lib" + } + } -genInteropStubs { - // these variables will be passed to native toolchain used by stub generator: - environment 'CPATH' : "$llvmInstallPath/include" - environment 'LIBRARY_PATH' : "$llvmInstallPath/lib" - //environment 'LD_DEBUG' : "all" - environment 'LD_LIBRARY_PATH' : "$llvmInstallPath/lib" +dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + compile kotlinNativeInterop['llvm'] } mainClassName = 'MainKt' diff --git a/InteropExample/src/main/kotlin/llvm/LLVM.def b/InteropExample/src/main/kotlin/llvm/LLVM.def deleted file mode 100644 index 504a298cddc..00000000000 --- a/InteropExample/src/main/kotlin/llvm/LLVM.def +++ /dev/null @@ -1,41 +0,0 @@ -libName = llvmbridge - -headers = llvm-c/Core.h llvm-c/ExecutionEngine.h llvm-c/Target.h llvm-c/Analysis.h llvm-c/BitWriter.h - - -compiler = clang - -compilerOpts = -std=c99 -fPIC \ - -Wall -W -Wno-unused-parameter -Wwrite-strings -Wmissing-field-initializers \ - -pedantic -Wno-long-long -Wcovered-switch-default -Wdelete-non-virtual-dtor \ - -DNDEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS - - -linker = clang++ - -linkerOpts = -Wall - -linkerOpts.osx = -stdlib=libc++ -fPIC -Wl,-search_paths_first \ - -Wl,-headerpad_max_install_names -fvisibility-inlines-hidden \ - -Wall -W -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers \ - -pedantic -Wno-long-long -Wcovered-switch-default -Wnon-virtual-dtor -Wdelete-non-virtual-dtor \ - -std=c++11 \ - -DNDEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS \ - -lLLVMX86Disassembler -lLLVMX86AsmParser -lLLVMX86CodeGen -lLLVMSelectionDAG -lLLVMAsmPrinter \ - -lLLVMX86Desc -lLLVMMCDisassembler -lLLVMX86Info -lLLVMX86AsmPrinter -lLLVMX86Utils -lLLVMInterpreter \ - -lLLVMCodeGen -lLLVMScalarOpts -lLLVMInstCombine -lLLVMInstrumentation -lLLVMProfileData -lLLVMTransformUtils \ - -lLLVMBitWriter -lLLVMExecutionEngine -lLLVMTarget -lLLVMAnalysis -lLLVMRuntimeDyld -lLLVMObject -lLLVMMCParser \ - -lLLVMBitReader -lLLVMMC -lLLVMCore -lLLVMSupport - -linkerOpts.linux = -Wl,-z,noexecstack -fvisibility=default \ - -Wl,--whole-archive \ - -lLLVMX86Disassembler -lLLVMX86AsmParser -lLLVMX86CodeGen \ - -lLLVMSelectionDAG -lLLVMAsmPrinter -lLLVMX86Desc -lLLVMMCDisassembler \ - -lLLVMX86Info -lLLVMX86AsmPrinter -lLLVMX86Utils -lLLVMInterpreter \ - -lLLVMCodeGen -lLLVMScalarOpts -lLLVMInstCombine -lLLVMInstrumentation \ - -lLLVMProfileData -lLLVMTransformUtils -lLLVMBitWriter \ - -lLLVMExecutionEngine -lLLVMTarget -lLLVMAnalysis -lLLVMRuntimeDyld \ - -lLLVMObject -lLLVMMCParser -lLLVMBitReader -lLLVMMC -lLLVMCore \ - -lLLVMSupport \ - -Wl,--no-whole-archive \ - -lrt -ldl -ltinfo -lpthread -lz -lm -lffi diff --git a/backend.native/build.gradle b/backend.native/build.gradle index 67462f210fc..211e0fba13b 100644 --- a/backend.native/build.gradle +++ b/backend.native/build.gradle @@ -33,8 +33,17 @@ sourceSets { } } +kotlinNativeInterop { + llvm { + defFile 'llvm.def' + compilerOpts "-I$llvmInstallPath/include" + linkerOpts "-L$llvmInstallPath/lib" + } +} + dependencies { compilerCompile deps + compilerCompile kotlinNativeInterop['llvm'] cli_bcCompile deps cli_bcCompile sourceSets.compiler.output @@ -42,6 +51,10 @@ dependencies { bc_frontendCompile deps } +configurations { + cli_bcRuntime.extendsFrom compilerRuntime +} + build.dependsOn 'compilerClasses','cli_bcClasses','bc_frontendClasses' @@ -73,9 +86,3 @@ task jars { repositories { mavenCentral() } - -genCompilerInteropStubs { - environment 'CPATH' : "$llvmInstallPath/include" - environment 'LIBRARY_PATH' : "$llvmInstallPath/lib" - environment 'LD_LIBRARY_PATH' : "$llvmInstallPath/lib" -} diff --git a/backend.native/compiler/ir/backend.native/src/llvm/LLVM.def b/backend.native/llvm.def similarity index 98% rename from backend.native/compiler/ir/backend.native/src/llvm/LLVM.def rename to backend.native/llvm.def index b028ab00de8..d55dd8dcecb 100644 --- a/backend.native/compiler/ir/backend.native/src/llvm/LLVM.def +++ b/backend.native/llvm.def @@ -1,8 +1,5 @@ -libName = llvmbridge - headers = llvm-c/Core.h llvm-c/ExecutionEngine.h llvm-c/Target.h llvm-c/Analysis.h llvm-c/BitWriter.h llvm-c/BitReader.h - compiler = clang compilerOpts = -std=c99 -fPIC \ diff --git a/buildSrc/src/main/groovy/org/jetbrains/kotlin/NativeInteropPlugin.groovy b/buildSrc/src/main/groovy/org/jetbrains/kotlin/NativeInteropPlugin.groovy index 88321f4c474..ab398129a7f 100644 --- a/buildSrc/src/main/groovy/org/jetbrains/kotlin/NativeInteropPlugin.groovy +++ b/buildSrc/src/main/groovy/org/jetbrains/kotlin/NativeInteropPlugin.groovy @@ -1,13 +1,138 @@ package org.jetbrains.kotlin +import org.gradle.api.Named import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.internal.AbstractNamedDomainObjectContainer +import org.gradle.api.internal.file.AbstractFileCollection import org.gradle.api.tasks.JavaExec +import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.TaskDependency +import org.gradle.internal.reflect.Instantiator + +class NamedNativeInteropConfig extends AbstractFileCollection implements Named { + + private final Project project + final String name + + private final SourceSet interopStubs + private final JavaExec genTask + + private String defFile + + private List compilerOpts = [] + private List linkerOpts = [] + + void defFile(String value) { + defFile = value + genTask.inputs.file(project.file(defFile)) + } + + void compilerOpts(String... values) { + compilerOpts.addAll(values) + } + + void linkerOpts(String... values) { + linkerOpts.addAll(values) + } + + void includeDirs(String... values) { + compilerOpts.addAll(values.collect {"-I$it"}) + } + + private File getNativeLibsDir() { + return new File(project.buildDir, "nativelibs") + } + + private File getGeneratedSrcDir() { + return new File(project.buildDir, "nativeInteropStubs/$name/kotlin") + } + + NamedNativeInteropConfig(Project project, String name) { + this.name = name + this.project = project + + interopStubs = project.sourceSets.create(name + "InteropStubs") + genTask = project.task(interopStubs.getTaskName("gen", ""), type: JavaExec) + + this.configure() + } + + private void configure() { + project.tasks.getByName(interopStubs.getTaskName("compile", "Kotlin")) { + dependsOn genTask + } + + interopStubs.kotlin.srcDirs generatedSrcDir + + project.dependencies { + add interopStubs.getCompileConfigurationName(), project(path: ':Interop:Runtime') + } + + genTask.configure { + classpath = project.configurations.interopStubGenerator + main = "org.jetbrains.kotlin.native.interop.gen.jvm.MainKt" + jvmArgs '-ea' + + systemProperties "java.library.path" : project.files( + new File(project.findProject(":Interop:Indexer").buildDir, "nativelibs"), + new File(project.findProject(":Interop:Runtime").buildDir, "nativelibs") + ).asPath + systemProperties "llvmInstallPath" : project.llvmInstallPath + environment "LIBCLANG_DISABLE_CRASH_RECOVERY": "1" + environment "DYLD_LIBRARY_PATH": "${project.llvmInstallPath}/lib" + + outputs.dir generatedSrcDir + outputs.dir nativeLibsDir + + // defer as much as possible + doFirst { + args = [generatedSrcDir, nativeLibsDir, project.file(defFile)] + + args compilerOpts.collect { "-copt:$it" } + args linkerOpts.collect { "-lopt:$it" } + } + } + } + + @Override + String getDisplayName() { + return "Native interop config $name" + } + + @Override + Set getFiles() { + return interopStubs.output.getFiles() + + interopStubs.compileClasspath.files // TODO: workaround to add Interop:Runtime + } + + @Override + TaskDependency getBuildDependencies() { + return interopStubs.output.getBuildDependencies() + } +} + +class NativeInteropExtension extends AbstractNamedDomainObjectContainer { + + private final Project project + + protected NativeInteropExtension(Project project) { + super(NamedNativeInteropConfig, project.gradle.services.get(Instantiator)) + this.project = project + } + + @Override + protected NamedNativeInteropConfig doCreate(String name) { + return new NamedNativeInteropConfig(project, name) + } +} class NativeInteropPlugin implements Plugin { @Override void apply(Project prj) { + prj.extensions.add("kotlinNativeInterop", new NativeInteropExtension(prj)) + def runtimeNativeLibsDir = new File(prj.findProject(':Interop:Runtime').buildDir, 'nativelibs') def nativeLibsDir = new File(prj.buildDir, "nativelibs") @@ -20,49 +145,6 @@ class NativeInteropPlugin implements Plugin { interopStubGenerator project(path: ":Interop:StubGenerator") } - prj.sourceSets.all { sourceSet -> - def generatedSrcDir = new File(prj.buildDir, "nativeInteropStubs/${sourceSet.name}/kotlin") - - prj.dependencies { - add sourceSet.getCompileConfigurationName(), project(path: ':Interop:Runtime') - } - - def genStubsTask = prj.task(sourceSet.getTaskName("gen", "interopStubs"), type: JavaExec) { - classpath = prj.configurations.interopStubGenerator - main = "org.jetbrains.kotlin.native.interop.gen.jvm.MainKt" - jvmArgs '-ea' - - - systemProperties "java.library.path" : prj.files( - new File(prj.findProject(":Interop:Indexer").buildDir, "nativelibs"), - runtimeNativeLibsDir - ).asPath - systemProperties "llvmInstallPath" : prj.llvmInstallPath - environment "LIBCLANG_DISABLE_CRASH_RECOVERY": "1" - environment "DYLD_LIBRARY_PATH": "${prj.llvmInstallPath}/lib" - - outputs.dir generatedSrcDir - outputs.dir nativeLibsDir - - args = [generatedSrcDir, nativeLibsDir] - - prj.afterEvaluate { // FIXME: it is a hack - sourceSet.kotlin.srcDirs.each { srcDir -> - if (srcDir != generatedSrcDir) { - args srcDir - inputs.files prj.fileTree(srcDir.path).include('**/*.def') - } - } - } - } - - sourceSet.kotlin.srcDirs generatedSrcDir - - prj.tasks.getByName(sourceSet.getTaskName("compile", "Kotlin")) { - dependsOn genStubsTask - } - } - // FIXME: choose tasks more wisely prj.tasks.withType(JavaExec) { if (!name.endsWith("InteropStubs")) {