diff --git a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Utils.kt b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Utils.kt index c159cc12eb3..9cafab38858 100644 --- a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Utils.kt +++ b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Utils.kt @@ -20,6 +20,8 @@ import clang.* import kotlinx.cinterop.* import java.io.Closeable import java.io.File +import java.nio.file.Files +import java.nio.file.Path import java.nio.file.Paths import java.security.DigestInputStream import java.security.MessageDigest @@ -542,3 +544,58 @@ internal fun CValue.getContainingFile(): CXFile? = memScoped { clang_getFileLocation(this@getContainingFile, fileVar.ptr, null, null, null) fileVar.value } + +private fun createVfsOverlayFileContents(virtualPathToReal: Map): ByteArray { + val overlay = clang_VirtualFileOverlay_create(0) + + try { + fun addFileMapping(realPath: Path, virtualPath: Path) { + clang_VirtualFileOverlay_addFileMapping( + overlay, + virtualPath = virtualPath.toAbsolutePath().toString(), + realPath = realPath.toAbsolutePath().toString() + ) + } + + virtualPathToReal.forEach { virtualPath, realPath -> + if (Files.isDirectory(realPath)) { + realPath.toFile().walkTopDown().forEach { + if (!it.isDirectory) { + addFileMapping( + realPath = it.toPath(), + virtualPath = virtualPath.resolve(realPath.relativize(it.toPath())) + ) + } + } + } else { + addFileMapping(realPath = realPath, virtualPath = virtualPath) + } + } + + memScoped { + val bufferVar = alloc>().apply { value = null } + val bufferSizeVar = alloc() + + val res = clang_VirtualFileOverlay_writeToBuffer(overlay, 0, bufferVar.ptr, bufferSizeVar.ptr) + if (res != CXErrorCode.CXError_Success) { + // TODO: shall we free the buffer in this case? + error(res) + } + + return bufferVar.value!!.readBytes(bufferSizeVar.value) + } + } finally { + clang_VirtualFileOverlay_dispose(overlay) + } +} + +fun createVfsOverlayFile(virtualPathToReal: Map): Path { + val bytes = createVfsOverlayFileContents(virtualPathToReal) + + return createTempFile(prefix = "konan", suffix = ".vfsoverlay").apply { + bufferedWriter().use { + writeBytes(bytes) + } + deleteOnExit() + }.toPath() +} 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 8f9dd4452cd..de10bb07ee3 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 @@ -23,8 +23,8 @@ import org.jetbrains.kotlin.native.interop.gen.ImportsImpl import org.jetbrains.kotlin.native.interop.indexer.* import java.io.File import java.lang.IllegalArgumentException +import java.nio.file.* import java.util.* -import kotlin.reflect.KFunction fun main(args: Array) = interop(args, null) @@ -211,6 +211,55 @@ private fun parseImports(args: Map>): ImportsImpl { return ImportsImpl(headerIdToPackage) } +fun getCompilerFlagsForVfsOverlay(args: Map>, def: DefFile): List { + val relativeToRoot = mutableMapOf() // TODO: handle clashes + + val HEADER_FILTER_ADDITIONAL_SEARCH_PREFIX = "-headerFilterAdditionalSearchPrefix" + + val filteredIncludeDirs = args[HEADER_FILTER_ADDITIONAL_SEARCH_PREFIX]?.map { Paths.get(it) } + if (filteredIncludeDirs != null) { + val headerFilterGlobs = def.config.headerFilter + if (headerFilterGlobs.isEmpty()) { + error("'$HEADER_FILTER_ADDITIONAL_SEARCH_PREFIX' option requires " + + "'headerFilter' to be specified in .def file") + } + + relativeToRoot += findFilesByGlobs(roots = filteredIncludeDirs, globs = headerFilterGlobs) + } + + if (relativeToRoot.isEmpty()) { + return emptyList() + } + + val virtualRoot = Paths.get(System.getProperty("java.io.tmpdir")).resolve("konanSystemInclude") + + val virtualPathToReal = relativeToRoot.map { (relativePath, realRoot) -> + virtualRoot.resolve(relativePath) to realRoot.resolve(relativePath) + }.toMap() + + val vfsOverlayFile = createVfsOverlayFile(virtualPathToReal) + + return listOf("-I${virtualRoot.toAbsolutePath()}", "-ivfsoverlay", vfsOverlayFile.toAbsolutePath().toString()) +} + +private fun findFilesByGlobs(roots: List, globs: List): Map { + val relativeToRoot = mutableMapOf() + + val pathMatchers = globs.map { FileSystems.getDefault().getPathMatcher("glob:$it") } + + roots.reversed().forEach { root -> + // TODO: don't scan the entire tree, skip subdirectories according to globs. + Files.walk(root, FileVisitOption.FOLLOW_LINKS).forEach { path -> + val relativePath = root.relativize(path) + if (!Files.isDirectory(path) && pathMatchers.any { it.matches(relativePath) }) { + relativeToRoot[relativePath] = root + } + } + } + return relativeToRoot +} + + private fun processLib(args: Map>, argsToCompiler: MutableList?) { @@ -250,6 +299,7 @@ private fun processLib(args: Map>, addAll(def.config.compilerOpts) addAll(tool.defaultCompilerOpts) addAll(additionalCompilerOpts) + addAll(getCompilerFlagsForVfsOverlay(args, def)) addAll(when (language) { Language.C -> emptyList() Language.OBJECTIVE_C -> {