[Gradle][MPP] Implement CompositeMetadataArtifact
^KT-48135 WIP
This commit is contained in:
committed by
Space
parent
244094501b
commit
7a1f4304d0
+77
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+253
@@ -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
|
||||||
|
}
|
||||||
|
|
||||||
+320
@@ -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() }
|
||||||
Reference in New Issue
Block a user