[Gradle][MPP] Implement CompositeMetadataArtifact

^KT-48135 WIP
This commit is contained in:
Sebastian Sellmair
2022-09-14 11:36:14 +02:00
committed by Space
parent 244094501b
commit 7a1f4304d0
3 changed files with 650 additions and 0 deletions
@@ -0,0 +1,77 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.plugin.mpp
import java.io.Closeable
import java.io.File
internal interface CompositeMetadataArtifact {
interface Library {
val artifactHandle: ArtifactHandle
val sourceSet: SourceSet
val archiveExtension: String
/**
* The proposed file-output path.
* This can actually include several path parts if the [Library] requires additional scoping (e.g. [CInteropMetadataLibrary]s
* need to be put into another folder)
*/
val relativeFile: File
/**
* Copies the content of this [Library] directly into the given [file].
* The [file] will be overwritten when it already exists.
* Parent directories will be created if necessary.
*/
fun copyTo(file: File): Boolean
/**
* Copies the content of this [Library] into the [directory] appending the [relativeFile] to it.
* @see copyTo
*/
fun copyIntoDirectory(directory: File) = copyTo(directory.resolve(relativeFile))
}
/**
* Represents a Kotlin Metadata library produced by compiling the contained [sourceSet]
*/
interface MetadataLibrary : Library
/**
* Represents a CInterop Metadata library, produced by the commonizer and attached to the [sourceSet]
*/
interface CInteropMetadataLibrary : Library {
val cinteropLibraryName: String
}
/**
* Represents a SourceSet packaged into the [CompositeMetadataArtifact]
*/
interface SourceSet {
val artifactHandle: ArtifactHandle
val sourceSetName: String
val metadataLibrary: MetadataLibrary?
val cinteropMetadataLibraries: List<CInteropMetadataLibrary>
}
interface ArtifactHandle : Closeable {
val moduleDependencyIdentifier: ModuleDependencyIdentifier
val moduleDependencyVersion: String
val sourceSets: List<SourceSet>
fun getSourceSet(name: String): SourceSet
fun findSourceSet(name: String): SourceSet?
}
val moduleDependencyIdentifier: ModuleDependencyIdentifier
val moduleDependencyVersion: String
fun open(): ArtifactHandle
fun <T> read(action: (artifactHandle: ArtifactHandle) -> T): T {
return open().use(action)
}
}
@@ -0,0 +1,253 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.plugin.mpp
import org.jetbrains.kotlin.gradle.utils.copyPartially
import org.jetbrains.kotlin.gradle.utils.ensureValidZipDirectoryPath
import org.jetbrains.kotlin.gradle.utils.listDescendants
import java.io.Closeable
import java.io.File
import java.io.IOException
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
internal class CompositeMetadataArtifactImpl(
override val moduleDependencyIdentifier: ModuleDependencyIdentifier,
override val moduleDependencyVersion: String,
private val kotlinProjectStructureMetadata: KotlinProjectStructureMetadata,
private val primaryArtifactFile: File,
private val hostSpecificArtifactFilesBySourceSetName: Map<String, File>
) : CompositeMetadataArtifact {
override fun open(): CompositeMetadataArtifact.ArtifactHandle {
return HandlerImpl()
}
inner class HandlerImpl : CompositeMetadataArtifact.ArtifactHandle {
override val moduleDependencyIdentifier: ModuleDependencyIdentifier
get() = this@CompositeMetadataArtifactImpl.moduleDependencyIdentifier
override val moduleDependencyVersion: String
get() = this@CompositeMetadataArtifactImpl.moduleDependencyVersion
/* Creating SourceSet instances eagerly, as they will only lazily access files */
private val sourceSetsImpl = kotlinProjectStructureMetadata.sourceSetNames.associateWith { sourceSetName ->
SourceSetImpl(this, sourceSetName, ArtifactFile(hostSpecificArtifactFilesBySourceSetName[sourceSetName] ?: primaryArtifactFile))
}
override val sourceSets: List<CompositeMetadataArtifact.SourceSet> =
sourceSetsImpl.values.toList()
override fun getSourceSet(name: String): CompositeMetadataArtifact.SourceSet {
return findSourceSet(name)
?: throw IllegalArgumentException("No SourceSet with name $name found. Known SourceSets: ${sourceSetsImpl.keys}")
}
override fun findSourceSet(name: String): CompositeMetadataArtifact.SourceSet? =
sourceSetsImpl[name]
override fun close() {
sourceSetsImpl.values.forEach { it.close() }
}
}
private inner class SourceSetImpl(
override val artifactHandle: CompositeMetadataArtifact.ArtifactHandle,
override val sourceSetName: String,
private val artifactFile: ArtifactFile
) : CompositeMetadataArtifact.SourceSet, Closeable {
override val metadataLibrary: CompositeMetadataArtifact.MetadataLibrary? by lazy {
/*
There are published multiplatform libraries that indeed suppress, disable certain compilations.
In this scenario, the sourceSetName might still be mentioned in the artifact, but there will be no
metadata-library packaged into the composite artifact.
In this case, return null
*/
if (artifactFile.containsDirectory(sourceSetName)) MetadataLibraryImpl(artifactHandle, this, artifactFile) else null
}
override val cinteropMetadataLibraries: List<CompositeMetadataArtifact.CInteropMetadataLibrary> by lazy {
val cinteropMetadataDirectory = kotlinProjectStructureMetadata.sourceSetCInteropMetadataDirectory[sourceSetName]
?: return@lazy emptyList()
val cinteropMetadataDirectoryPath = ensureValidZipDirectoryPath(cinteropMetadataDirectory)
val cinteropEntries = artifactFile.zip.listDescendants(cinteropMetadataDirectoryPath)
val cinteropLibraryNames = cinteropEntries.map { entry ->
entry.name.removePrefix(cinteropMetadataDirectoryPath).split("/", limit = 2).first()
}.toSet()
cinteropLibraryNames.map { cinteropLibraryName ->
CInteropMetadataLibraryImpl(artifactHandle, this, cinteropLibraryName, artifactFile)
}
}
override fun close() {
artifactFile.close()
}
}
private inner class MetadataLibraryImpl(
override val artifactHandle: CompositeMetadataArtifact.ArtifactHandle,
override val sourceSet: CompositeMetadataArtifact.SourceSet,
private val artifactFile: ArtifactFile
) : CompositeMetadataArtifact.MetadataLibrary {
override val archiveExtension: String
get() = kotlinProjectStructureMetadata.sourceSetBinaryLayout[sourceSet.sourceSetName]?.archiveExtension
?: SourceSetMetadataLayout.METADATA.archiveExtension
/**
* Example:
* org.jetbrains.sample-sampleLibrary-1.0.0-SNAPSHOT-appleAndLinuxMain.klib
*/
override val relativeFile: File = File(buildString {
append(artifactHandle.moduleDependencyIdentifier)
append("-")
append(artifactHandle.moduleDependencyVersion)
append("-")
append(sourceSet.sourceSetName)
append(".")
append(archiveExtension)
})
override fun copyTo(file: File): Boolean {
require(file.extension == archiveExtension) {
"Expected file.extension == '$archiveExtension'. Found ${file.extension}"
}
val libraryPath = "${sourceSet.sourceSetName}/"
if (!artifactFile.containsDirectory(libraryPath)) return false
file.parentFile.mkdirs()
artifactFile.zip.copyPartially(file, libraryPath)
return true
}
}
private inner class CInteropMetadataLibraryImpl(
override val artifactHandle: CompositeMetadataArtifact.ArtifactHandle,
override val sourceSet: CompositeMetadataArtifact.SourceSet,
override val cinteropLibraryName: String,
private val artifact: ArtifactFile,
) : CompositeMetadataArtifact.CInteropMetadataLibrary {
override val archiveExtension: String
get() = SourceSetMetadataLayout.KLIB.archiveExtension
/**
* Example:
* org.jetbrains.sample-sampleLibrary-1.0.0-SNAPSHOT-appleAndLinuxMain-cinterop/
* org.jetbrains.sample_sampleLibrary-cinterop-simple.klib
*/
override val relativeFile: File = File(buildString {
append(artifactHandle.moduleDependencyIdentifier)
append("-")
append(artifactHandle.moduleDependencyVersion)
append("-")
append(sourceSet.sourceSetName)
append("-cinterop")
}).resolve("$cinteropLibraryName.${archiveExtension}")
override fun copyTo(file: File): Boolean {
require(file.extension == archiveExtension) {
"Expected 'file.extension == '${SourceSetMetadataLayout.KLIB.archiveExtension}'. Found ${file.extension}"
}
val sourceSetName = sourceSet.sourceSetName
val cinteropMetadataDirectory = kotlinProjectStructureMetadata.sourceSetCInteropMetadataDirectory[sourceSetName]
?: error("Missing CInteropMetadataDirectory for SourceSet $sourceSetName")
val cinteropMetadataDirectoryPath = ensureValidZipDirectoryPath(cinteropMetadataDirectory)
val libraryPath = "$cinteropMetadataDirectoryPath$cinteropLibraryName/"
if (!artifact.containsDirectory(libraryPath)) return false
file.parentFile.mkdirs()
artifact.zip.copyPartially(file, "$cinteropMetadataDirectoryPath$cinteropLibraryName/")
return true
}
}
/**
* Interface to the underlying [zip][file] that only opens the file lazily and keeps references to
* all [entries] and infers all potential directory paths (see [directoryPaths] and [containsDirectory])
*/
private class ArtifactFile(private val file: File) : Closeable {
private var isClosed = false
private val lazyZip = lazy {
ensureNotClosed()
ZipFile(file)
}
val zip: ZipFile get() = lazyZip.value
val entries: List<ZipEntry> by lazy { zip.entries().toList() }
/**
* All potential directory paths, including inferred directory paths when the [zip] file does
* not include directory entries.
* @see collectAllDirectoryPaths
*/
val directoryPaths: Set<String> by lazy { collectAllDirectoryPaths(entries) }
/**
* Check if the underlying [zip] file contains this directory.
* Note: This check also works for zip files that did not include directory entries.
* This will return true, if any other zip-entry is placed inside this directory [path]
*/
fun containsDirectory(path: String): Boolean {
val validPath = ensureValidZipDirectoryPath(path)
if (zip.getEntry(validPath) != null) return true
return validPath in directoryPaths
}
private fun ensureNotClosed() {
if (isClosed) throw IOException("LazyZipFile is already closed!")
}
override fun close() {
isClosed = true
if (lazyZip.isInitialized()) {
lazyZip.value.close()
}
}
}
}
/**
* Zip files are not **forced** to include entries for directories.
* In order to do preliminary checks, if some directory is present in Zip Files it is
* often useful to infer the directories included in any Zip File by looking into file entries
* and inferring their directories.
*/
private fun collectAllDirectoryPaths(entries: List<ZipEntry>): Set<String> {
/*
The 'root' directory is represented as empty String in ZipFile
*/
val set = hashSetOf("")
entries.forEach { entry ->
if (entry.isDirectory) {
set.add(entry.name)
return@forEach
}
/* Collect all 'intermediate' directories found by looking at the files path */
val pathParts = entry.name.split("/")
pathParts.runningReduce { currentPath, nextPart ->
set.add("$currentPath/")
"$currentPath/$nextPart"
}
}
return set
}
@@ -0,0 +1,320 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:Suppress("FunctionName")
package org.jetbrains.kotlin.gradle
import org.gradle.kotlin.dsl.support.unzipTo
import org.gradle.kotlin.dsl.support.zipTo
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.plugin.mpp.SourceSetMetadataLayout.KLIB
import org.jetbrains.kotlin.gradle.plugin.mpp.SourceSetMetadataLayout.METADATA
import org.junit.Rule
import org.junit.rules.TemporaryFolder
import java.io.File
import kotlin.test.*
class CompositeMetadataArtifactTest {
@get:Rule
val temporaryFolder = TemporaryFolder()
@Test
fun `empty jar - contains no metadataLibrary and no cinteropMetadataLibraries`() {
val primaryArtifactContent = temporaryFolder.newFolder()
val primaryArtifactFile = temporaryFolder.newFile("metadata.jar")
zipTo(primaryArtifactFile, primaryArtifactContent)
assertTrue(primaryArtifactFile.isFile, "Expected primaryArtifactFile.isFile")
val metadataArtifact = CompositeMetadataArtifactImpl(
moduleDependencyIdentifier = ModuleDependencyIdentifier("test-group", "test-module"),
moduleDependencyVersion = "1.0.0",
kotlinProjectStructureMetadata = createProjectStructureMetadata(
sourceSetNames = mutableSetOf("testSourceSetName"),
sourceSetBinaryLayout = mapOf("testSourceSetName" to KLIB),
sourceSetCInteropMetadataDirectory = mapOf("testSourceSetName" to "cinterop/testSourceSetName")
),
primaryArtifactFile = primaryArtifactFile,
hostSpecificArtifactFilesBySourceSetName = emptyMap()
)
metadataArtifact.read { artifactHandle ->
if (artifactHandle.sourceSets.size != 1) fail("Expected one SourceSet in metadataArtifact")
val sourceSet = artifactHandle.sourceSets.first()
assertEquals("testSourceSetName", sourceSet.sourceSetName)
assertNull(sourceSet.metadataLibrary, "Expected no 'metadataLibrary' listed for SourceSet")
if (sourceSet.cinteropMetadataLibraries.isNotEmpty()) {
fail(
"Expected no 'cinteropMetadataLibraries' in 'testSourceSet'. " +
"Found ${sourceSet.cinteropMetadataLibraries.map { it.cinteropLibraryName }}"
)
}
}
}
@Test
fun `stub metadata library - can be unzipped`() {
val testSourceSetName = "testSourceSetName"
val primaryArtifactContent = temporaryFolder.newFolder()
val stubFile = primaryArtifactContent.resolve(testSourceSetName).resolve("stub.txt")
stubFile.parentFile.mkdirs()
stubFile.writeText("stub!")
val primaryArtifactFile = temporaryFolder.newFile("metadata.jar")
zipTo(primaryArtifactFile, primaryArtifactContent)
assertTrue(primaryArtifactFile.isFile, "Expected primaryArtifactFile.isFile")
val metadataArtifact = CompositeMetadataArtifactImpl(
moduleDependencyIdentifier = ModuleDependencyIdentifier("test-group", "test-module"),
moduleDependencyVersion = "1.0.0",
kotlinProjectStructureMetadata = createProjectStructureMetadata(
sourceSetBinaryLayout = mapOf(testSourceSetName to KLIB),
sourceSetCInteropMetadataDirectory = emptyMap(),
),
primaryArtifactFile = primaryArtifactFile,
hostSpecificArtifactFilesBySourceSetName = emptyMap()
)
metadataArtifact.read { artifactHandle ->
if (artifactHandle.sourceSets.size != 1)
fail("Expected exactly one SourceSet in ${artifactHandle.sourceSets.map { it.sourceSetName }}")
val sourceSet = artifactHandle.getSourceSet(testSourceSetName)
val metadataOutputDirectory = temporaryFolder.newFolder()
val metadataFile = metadataOutputDirectory.resolve("testSourceSet.klib")
val metadataLibrary = sourceSet.metadataLibrary ?: fail("Missing metadataLibrary for ${sourceSet.sourceSetName}")
assertTrue(metadataLibrary.copyTo(metadataFile), "Expected 'copyTo' to perform copy action")
assertTrue(metadataFile.isFile)
val unzippedMetadataFile = metadataOutputDirectory.resolve("unzipped")
unzipTo(unzippedMetadataFile, metadataFile)
assertEquals(setOf(unzippedMetadataFile.resolve("stub.txt")), unzippedMetadataFile.listFiles().orEmpty().toSet())
assertEquals("stub!", unzippedMetadataFile.resolve("stub.txt").readText())
}
}
@Test
fun `empty jar - returns no cinteropMetadataLibraries`() {
val primaryArtifactContent = temporaryFolder.newFolder()
val primaryArtifactFile = temporaryFolder.newFile("metadata.jar")
zipTo(primaryArtifactFile, primaryArtifactContent)
assertTrue(primaryArtifactFile.isFile, "Expected primaryArtifactFile.isFile")
val metadataArtifact = CompositeMetadataArtifactImpl(
moduleDependencyIdentifier = ModuleDependencyIdentifier("test-group", "test-module"),
moduleDependencyVersion = "1.0.0",
kotlinProjectStructureMetadata = createProjectStructureMetadata(
sourceSetNames = setOf("testSourceSetName")
),
primaryArtifactFile = primaryArtifactFile,
hostSpecificArtifactFilesBySourceSetName = emptyMap()
)
metadataArtifact.read { artifactHandle ->
val sourceSet = artifactHandle.getSourceSet("testSourceSetName")
assertEquals(listOf(sourceSet), artifactHandle.sourceSets)
assertEquals(emptyList(), sourceSet.cinteropMetadataLibraries, "Expected empty cinteropMetadataLibraries")
}
}
@Test
fun `copy metadataLibrary`() {
/* Setup Artifact content */
val primaryArtifactContent = temporaryFolder.newFolder()
primaryArtifactContent.resolve("sourceSetA/sourceSetAStub1.txt")
.withParentDirectoriesCreated()
.writeText("Content of sourceSetA stub1")
primaryArtifactContent.resolve("sourceSetA/nested/sourceSetAStub2.txt")
.withParentDirectoriesCreated()
.writeText("Content of sourceSetB stub2")
primaryArtifactContent.resolve("sourceSetB/sourceSetBStub1.txt")
.withParentDirectoriesCreated()
.writeText("Content of sourceSetB stub1")
primaryArtifactContent.resolve("sourceSetB/nested/sourceSetBStub2.txt")
.withParentDirectoriesCreated()
.writeText("Content of sourceSetB stub2")
/* Create metadata jar */
val primaryArtifactFile = temporaryFolder.newFile("metadata.jar")
zipTo(primaryArtifactFile, primaryArtifactContent)
val metadataArtifact = CompositeMetadataArtifactImpl(
moduleDependencyIdentifier = ModuleDependencyIdentifier("test-group", "test-module"),
moduleDependencyVersion = "1.0.0",
kotlinProjectStructureMetadata = createProjectStructureMetadata(
sourceSetBinaryLayout = mapOf("sourceSetA" to KLIB, "sourceSetB" to METADATA)
),
primaryArtifactFile = primaryArtifactFile,
hostSpecificArtifactFilesBySourceSetName = emptyMap()
)
metadataArtifact.read { artifactHandle ->
val metadataOutputDirectory = temporaryFolder.newFolder()
/* Extract and assert sourceSetA */
artifactHandle.getSourceSet("sourceSetA").also { sourceSetA ->
val sourceSetAMetadataFile = metadataOutputDirectory.resolve("sourceSetA.klib")
assertNotNull(sourceSetA.metadataLibrary).copyTo(sourceSetAMetadataFile)
assertTrue(sourceSetAMetadataFile.isFile, "Expected sourceSetAMetadataFile.isFile")
assertEquals(
KLIB.archiveExtension, sourceSetAMetadataFile.extension,
"Expected correct archiveExtension for extracted sourceSetA"
)
assertZipContentEquals(
temporaryFolder,
primaryArtifactContent.resolve("sourceSetA"), sourceSetAMetadataFile,
"Expected correct content of extracted 'sourceSetA'"
)
}
/* Extract and assert sourceSetB */
artifactHandle.getSourceSet("sourceSetB").also { sourceSetB ->
val sourceSetBMetadataFile = metadataOutputDirectory.resolve("sourceSetB.jar")
assertNotNull(sourceSetB.metadataLibrary).copyTo(sourceSetBMetadataFile)
assertTrue(sourceSetBMetadataFile.isFile, "Expected sourceSetBMetadataFile.isFile")
assertEquals(
METADATA.archiveExtension, sourceSetBMetadataFile.extension,
"Expected correct archiveExtension for extracted sourceSetB"
)
assertZipContentEquals(
temporaryFolder,
primaryArtifactContent.resolve("sourceSetB"), sourceSetBMetadataFile,
"Expected correct content of extracted 'sourceSetA'"
)
}
}
}
@Test
fun `copy cinteropMetadataLibraries`() {
/* Setup Artifact content */
val primaryArtifactContent = temporaryFolder.newFolder()
primaryArtifactContent.resolve("sourceSetA-cinterop/interopA0/stub0")
.withParentDirectoriesCreated()
.writeText("stub0 content")
primaryArtifactContent.resolve("sourceSetA-cinterop/interopA0/nested/stub1")
.withParentDirectoriesCreated()
.writeText("stub1 content")
primaryArtifactContent.resolve("sourceSetA-cinterop/interopA1/stub2")
.withParentDirectoriesCreated()
.writeText("stub2 content")
primaryArtifactContent.resolve("nested/sourceSetB/interops/interopB0/stub3")
.withParentDirectoriesCreated()
.writeText("stub3 content")
/* Create metadata jar */
val primaryArtifactFile = temporaryFolder.newFile("metadata.jar")
zipTo(primaryArtifactFile, primaryArtifactContent)
val metadataArtifact = CompositeMetadataArtifactImpl(
moduleDependencyIdentifier = ModuleDependencyIdentifier("test-group", "test-module"),
moduleDependencyVersion = "1.0.0",
kotlinProjectStructureMetadata = createProjectStructureMetadata(
sourceSetCInteropMetadataDirectory = mapOf(
"sourceSetA" to "sourceSetA-cinterop",
"sourceSetB" to "nested/sourceSetB/interops/"
)
),
primaryArtifactFile = primaryArtifactFile,
hostSpecificArtifactFilesBySourceSetName = emptyMap()
)
metadataArtifact.read { artifactHandle ->
/* Assertions on sourceSetA */
artifactHandle.getSourceSet("sourceSetA").also { sourceSetA ->
val sourceSetAOutputDirectory = temporaryFolder.newFolder()
val sourceSetAInteropMetadataFiles = sourceSetA.cinteropMetadataLibraries.map { cinteropLibrary ->
sourceSetAOutputDirectory.resolve("${cinteropLibrary.cinteropLibraryName}.klib").also { file ->
cinteropLibrary.copyTo(file)
}
}
assertEquals(2, sourceSetAInteropMetadataFiles.size, "Expected 2 cinterops in sourceSetA")
/* Assertions on interopA0 */
run {
val interopA0MetadataFile = sourceSetAInteropMetadataFiles.firstOrNull { it.name == "interopA0.klib" }
?: fail("Failed to find 'interopA0.klib'")
assertZipContentEquals(
temporaryFolder,
primaryArtifactContent.resolve("sourceSetA-cinterop/interopA0"), interopA0MetadataFile,
"Expected correct content for extracted 'interopA0'"
)
}
/* Assertions on interopA1 */
run {
val interopA1MetadataFile = sourceSetAInteropMetadataFiles.firstOrNull { it.name == "interopA1.klib" }
?: fail("Failed to find 'interopA1.klib")
assertZipContentEquals(
temporaryFolder,
primaryArtifactContent.resolve("sourceSetA-cinterop/interopA1"), interopA1MetadataFile,
"Expected correct content for extracted 'interopA1'"
)
}
}
/* Assertions on sourceSetB */
artifactHandle.getSourceSet("sourceSetB").also { sourceSetB ->
val sourceSetBOutputDirectory = temporaryFolder.newFolder()
val sourceSetBInteropMetadataFiles = sourceSetB.cinteropMetadataLibraries.map { cinteropLibrary ->
sourceSetBOutputDirectory.resolve("${cinteropLibrary.cinteropLibraryName}.klib").also { file ->
cinteropLibrary.copyTo(file)
}
}
assertEquals(1, sourceSetBInteropMetadataFiles.size, "Expected only one cinterop in sourceSetB")
val interopB0MetadataFile = sourceSetBInteropMetadataFiles.firstOrNull { it.name == "interopB0.klib" }
?: fail("Failed to find 'interopB0.klib'")
assertZipContentEquals(
temporaryFolder,
primaryArtifactContent.resolve("nested/sourceSetB/interops/interopB0"), interopB0MetadataFile,
"Expected correct content for extracted 'interopB0'"
)
}
}
}
}
private fun createProjectStructureMetadata(
sourceSetNamesByVariantName: Map<String, Set<String>> = emptyMap(),
sourceSetsDependsOnRelation: Map<String, Set<String>> = emptyMap(),
sourceSetCInteropMetadataDirectory: Map<String, String> = emptyMap(),
sourceSetBinaryLayout: Map<String, SourceSetMetadataLayout> = emptyMap(),
sourceSetModuleDependencies: Map<String, Set<ModuleDependencyIdentifier>> = emptyMap(),
hostSpecificSourceSets: Set<String> = emptySet(),
sourceSetNames: Set<String> = hostSpecificSourceSets +
sourceSetNamesByVariantName.values.flatten() +
sourceSetsDependsOnRelation.keys +
sourceSetCInteropMetadataDirectory.keys +
sourceSetBinaryLayout.keys +
sourceSetModuleDependencies.keys,
isPublishedAsRoot: Boolean = true,
): KotlinProjectStructureMetadata {
return KotlinProjectStructureMetadata(
sourceSetNamesByVariantName = sourceSetNamesByVariantName,
sourceSetsDependsOnRelation = sourceSetsDependsOnRelation,
sourceSetBinaryLayout = sourceSetBinaryLayout,
sourceSetCInteropMetadataDirectory = sourceSetCInteropMetadataDirectory,
sourceSetModuleDependencies = sourceSetModuleDependencies,
hostSpecificSourceSets = hostSpecificSourceSets,
sourceSetNames = sourceSetNames,
isPublishedAsRoot = isPublishedAsRoot
)
}
private fun File.withParentDirectoriesCreated(): File = apply { parentFile.mkdirs() }