Publish multiplatform resources as variants for wasm, js and native targets
^KT-65540
This commit is contained in:
committed by
Space Team
parent
5bbec23123
commit
b145796e14
@@ -765,6 +765,7 @@ public abstract class org/jetbrains/kotlin/gradle/plugin/mpp/external/DecoratedE
|
||||
public fun getPreset ()Lorg/jetbrains/kotlin/gradle/plugin/KotlinTargetPreset;
|
||||
public fun getProject ()Lorg/gradle/api/Project;
|
||||
public fun getPublishable ()Z
|
||||
public fun getResourcesElementsConfigurationName ()Ljava/lang/String;
|
||||
public final fun getRuntimeElementsConfiguration ()Lorg/gradle/api/artifacts/Configuration;
|
||||
public fun getRuntimeElementsConfigurationName ()Ljava/lang/String;
|
||||
public final fun getRuntimeElementsPublishedConfiguration ()Lorg/gradle/api/artifacts/Configuration;
|
||||
|
||||
+7
-1
@@ -31,9 +31,11 @@ import org.jetbrains.kotlin.gradle.internal.KOTLIN_BUILD_TOOLS_API_IMPL
|
||||
import org.jetbrains.kotlin.gradle.internal.KOTLIN_COMPILER_EMBEDDABLE
|
||||
import org.jetbrains.kotlin.gradle.internal.KOTLIN_MODULE_GROUP
|
||||
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.internal.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.swiftexport.initSwiftExportClasspathConfigurations
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.resources.resolve.KotlinTargetResourcesResolutionStrategy
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSetFactory
|
||||
import org.jetbrains.kotlin.gradle.plugin.statistics.BuildFusService
|
||||
import org.jetbrains.kotlin.gradle.report.BuildMetricsService
|
||||
@@ -210,7 +212,11 @@ abstract class DefaultKotlinBasePlugin : KotlinBasePlugin {
|
||||
isKotlinGranularMetadata: Boolean = project.isKotlinGranularMetadataEnabled,
|
||||
) = with(project.dependencies.attributesSchema) {
|
||||
KotlinPlatformType.setupAttributesMatchingStrategy(this)
|
||||
KotlinUsages.setupAttributesMatchingStrategy(this, isKotlinGranularMetadata)
|
||||
KotlinUsages.setupAttributesMatchingStrategy(
|
||||
this,
|
||||
isKotlinGranularMetadata,
|
||||
project.kotlinPropertiesProvider.mppResourcesResolutionStrategy == KotlinTargetResourcesResolutionStrategy.ResourcesConfiguration
|
||||
)
|
||||
ProjectLocalConfigurations.setupAttributesMatchingStrategy(this)
|
||||
|
||||
project.whenJsOrMppEnabled {
|
||||
|
||||
+4
-2
@@ -55,7 +55,9 @@ internal fun Project.categoryByName(categoryName: String): Category =
|
||||
internal inline fun <reified T : Named> Project.attributeValueByName(attributeValueName: String): T =
|
||||
objects.named(T::class.java, attributeValueName)
|
||||
|
||||
fun Configuration.usesPlatformOf(target: KotlinTarget): Configuration {
|
||||
fun Configuration.usesPlatformOf(target: KotlinTarget) = setUsesPlatformOf(target)
|
||||
|
||||
internal fun <T : HasAttributes> T.setUsesPlatformOf(target: KotlinTarget): T {
|
||||
attributes.setAttribute(KotlinPlatformType.attribute, target.platformType)
|
||||
|
||||
when (target.platformType) {
|
||||
@@ -94,7 +96,7 @@ fun Configuration.usesPlatformOf(target: KotlinTarget): Configuration {
|
||||
return this
|
||||
}
|
||||
|
||||
private fun Configuration.setJavaTargetEnvironmentAttributeIfSupported(project: Project, value: String) {
|
||||
private fun HasAttributes.setJavaTargetEnvironmentAttributeIfSupported(project: Project, value: String) {
|
||||
if (GradleVersion.current() >= GradleVersion.version("7.0")) {
|
||||
attributes.setAttribute(
|
||||
TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE,
|
||||
|
||||
+20
@@ -39,10 +39,12 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLI
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_OPTIMISTIC_NUMBER_COMMONIZATION
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_PLATFORM_INTEGER_COMMONIZATION
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_RESOURCES_PUBLICATION
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_FILTER_RESOURCES_BY_EXTENSION
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_HIERARCHICAL_STRUCTURE_BY_DEFAULT
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_HIERARCHICAL_STRUCTURE_SUPPORT
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_IMPORT_ENABLE_KGP_DEPENDENCY_RESOLUTION
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_IMPORT_ENABLE_SLOW_SOURCES_JAR_RESOLVER
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_RESOURCES_RESOLUTION_STRATEGY
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_NATIVE_IGNORE_DISABLED_TARGETS
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_NATIVE_SUPPRESS_EXPERIMENTAL_ARTIFACTS_DSL_WARNING
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_NATIVE_TOOLCHAIN_ENABLED
|
||||
@@ -53,7 +55,9 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLI
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_STDLIB_JDK_VARIANTS_VERSION_ALIGNMENT
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.MPP_13X_FLAGS_SET_BY_PLUGIN
|
||||
import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics
|
||||
import org.jetbrains.kotlin.gradle.plugin.diagnostics.reportDiagnostic
|
||||
import org.jetbrains.kotlin.gradle.plugin.diagnostics.reportDiagnosticOncePerBuild
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.resources.resolve.KotlinTargetResourcesResolutionStrategy
|
||||
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinIrJsGeneratedTSValidationStrategy
|
||||
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrOutputGranularity
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy
|
||||
@@ -203,6 +207,20 @@ internal class PropertiesProvider private constructor(private val project: Proje
|
||||
val mppResourcesPublication: Boolean
|
||||
get() = this.booleanProperty(KOTLIN_MPP_ENABLE_RESOURCES_PUBLICATION) ?: true
|
||||
|
||||
val mppResourcesResolutionStrategy: KotlinTargetResourcesResolutionStrategy
|
||||
get() = this.get(KOTLIN_MPP_RESOURCES_RESOLUTION_STRATEGY)?.let {
|
||||
val strategy = KotlinTargetResourcesResolutionStrategy.fromProperty(it)
|
||||
if (strategy == null) { project.reportDiagnostic(KotlinToolingDiagnostics.UnknownValueProvidedForResourcesStrategy(it)) }
|
||||
return@let strategy
|
||||
} ?: KotlinTargetResourcesResolutionStrategy.ResourcesConfiguration
|
||||
|
||||
val mppFilterResourcesByExtension: Provider<Boolean>
|
||||
get() = project.providers
|
||||
.provider<Boolean> {
|
||||
booleanProperty(KOTLIN_MPP_FILTER_RESOURCES_BY_EXTENSION)
|
||||
}
|
||||
.orElse(false)
|
||||
|
||||
val wasmStabilityNoWarn: Boolean
|
||||
get() = booleanProperty("kotlin.wasm.stability.nowarn") ?: false
|
||||
|
||||
@@ -635,6 +653,8 @@ internal class PropertiesProvider private constructor(private val project: Proje
|
||||
val KOTLIN_MPP_ENABLE_INTRANSITIVE_METADATA_CONFIGURATION = property("kotlin.mpp.enableIntransitiveMetadataConfiguration")
|
||||
val KOTLIN_MPP_APPLY_DEFAULT_HIERARCHY_TEMPLATE = property("kotlin.mpp.applyDefaultHierarchyTemplate")
|
||||
val KOTLIN_MPP_ENABLE_RESOURCES_PUBLICATION = property("kotlin.mpp.enableResourcesPublication")
|
||||
val KOTLIN_MPP_RESOURCES_RESOLUTION_STRATEGY = property("kotlin.mpp.resourcesResolutionStrategy")
|
||||
val KOTLIN_MPP_FILTER_RESOURCES_BY_EXTENSION = property("kotlin.mpp.filterResourcesByExtension")
|
||||
val KOTLIN_NATIVE_DEPENDENCY_PROPAGATION = property("kotlin.native.enableDependencyPropagation")
|
||||
val KOTLIN_NATIVE_CACHE_ORCHESTRATION = property("kotlin.native.cacheOrchestration")
|
||||
val KOTLIN_NATIVE_PARALLEL_THREADS = property("kotlin.native.parallelThreads")
|
||||
|
||||
+38
@@ -796,6 +796,44 @@ object KotlinToolingDiagnostics {
|
||||
)
|
||||
}
|
||||
|
||||
object ResourceMayNotBeResolvedForTarget : ToolingDiagnosticFactory(ERROR) {
|
||||
operator fun invoke(targetName: String) = build(
|
||||
"""
|
||||
Resources resolution for target $targetName is not supported.
|
||||
|
||||
$resourcesBugReportRequest
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
object UnknownValueProvidedForResourcesStrategy : ToolingDiagnosticFactory(ERROR) {
|
||||
operator fun invoke(value: String) = build(
|
||||
"""
|
||||
Unknown value $value provided for ${PropertiesProvider.PropertyNames.KOTLIN_MPP_RESOURCES_RESOLUTION_STRATEGY}
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
object MissingConfigurationForWasmTarget : ToolingDiagnosticFactory(ERROR) {
|
||||
operator fun invoke(targetName: String) = build(
|
||||
"""
|
||||
Resources will not be resolved for $targetName as it is missing runtimeDependencyConfiguration.
|
||||
|
||||
$resourcesBugReportRequest
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
object MissingResourcesConfigurationForTarget : ToolingDiagnosticFactory(ERROR) {
|
||||
operator fun invoke(targetName: String) = build(
|
||||
"""
|
||||
Resources will not be resolved for $targetName as it is missing resourcesConfiguration.
|
||||
|
||||
$resourcesBugReportRequest
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
object DependencyDoesNotPhysicallyExist : ToolingDiagnosticFactory(WARNING) {
|
||||
operator fun invoke(dependency: File) = build(
|
||||
"""
|
||||
|
||||
+4
@@ -54,6 +54,10 @@ abstract class AbstractKotlinTarget(
|
||||
override val sourcesElementsConfigurationName: String
|
||||
get() = disambiguateName("sourcesElements")
|
||||
|
||||
@InternalKotlinGradlePluginApi
|
||||
override val resourcesElementsConfigurationName: String
|
||||
get() = disambiguateName("resourcesElements")
|
||||
|
||||
override val artifactsTaskName: String
|
||||
get() = disambiguateName("jar")
|
||||
|
||||
|
||||
+2
@@ -39,6 +39,8 @@ internal interface InternalKotlinTarget : KotlinTarget, HasMutableExtras {
|
||||
|
||||
@Deprecated(TOOLCHAIN_DSL_WRONG_USAGE_ERROR, level = DeprecationLevel.ERROR)
|
||||
fun jvmToolchain(jdkVersion: Int): Unit = error(TOOLCHAIN_DSL_WRONG_USAGE_ERROR)
|
||||
|
||||
val resourcesElementsConfigurationName: String
|
||||
}
|
||||
|
||||
private const val TOOLCHAIN_DSL_WRONG_USAGE_ERROR =
|
||||
|
||||
+40
-1
@@ -37,6 +37,15 @@ object KotlinUsages {
|
||||
const val KOTLIN_COMMONIZED_CINTEROP = "kotlin-commonized-cinterop"
|
||||
const val KOTLIN_SOURCES = "kotlin-sources"
|
||||
|
||||
/**
|
||||
* Multiplatform resources usage:
|
||||
* Resources variants for native, wasmJs and wasmWasi targets publish with these usages. To resolve a resources configuration with
|
||||
* transitive dependencies that might not have a resource variant, we must use a compatibility rule to take dependencies from
|
||||
* the klib variant. For native that is a "kotlin-api" variant and for wasmJs and wasmWasi that is a "kotlin-runtime" variant.
|
||||
*/
|
||||
const val KOTLIN_RESOURCES = "kotlin-multiplatformresources"
|
||||
const val KOTLIN_RESOURCES_JS = "kotlin-multiplatformresourcesjs"
|
||||
|
||||
// Following two constants were removed in Gradle 8.0 from 'Usages' class
|
||||
private const val JAVA_RUNTIME_CLASSES = "java-runtime-classes"
|
||||
private const val JAVA_RUNTIME_RESOURCES = "java-runtime-resources"
|
||||
@@ -125,6 +134,27 @@ object KotlinUsages {
|
||||
}
|
||||
}
|
||||
|
||||
private class KotlinResourcesCompatibility : AttributeCompatibilityRule<Usage> {
|
||||
override fun execute(details: CompatibilityCheckDetails<Usage>) = with(details) {
|
||||
/**
|
||||
* When resolving resources using KotlinTarget.resourcesConfiguration, if a dependency doesn't have resources variant, we must
|
||||
* take dependencies from the klib variant because they might contain transitive resources variants.
|
||||
* */
|
||||
val consumerValueName = consumerValue?.name
|
||||
val producerValueName = producerValue?.name
|
||||
if (consumerValueName == null || producerValueName == null) return
|
||||
|
||||
if (
|
||||
mapOf(
|
||||
KOTLIN_RESOURCES to KOTLIN_API,
|
||||
KOTLIN_RESOURCES_JS to KOTLIN_RUNTIME,
|
||||
)[consumerValueName] == producerValueName
|
||||
) {
|
||||
compatible()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class KotlinCinteropDisambiguation : AttributeDisambiguationRule<Usage> {
|
||||
override fun execute(details: MultipleCandidatesDetails<Usage?>) = details.run {
|
||||
if (consumerValue?.name == KOTLIN_CINTEROP) {
|
||||
@@ -186,7 +216,11 @@ object KotlinUsages {
|
||||
closestMatch(candidateValues.single { it?.name == name }!!)
|
||||
}
|
||||
|
||||
internal fun setupAttributesMatchingStrategy(attributesSchema: AttributesSchema, isKotlinGranularMetadata: Boolean) {
|
||||
internal fun setupAttributesMatchingStrategy(
|
||||
attributesSchema: AttributesSchema,
|
||||
isKotlinGranularMetadata: Boolean,
|
||||
isKotlinResourcesCompatibilityRuleEnabled: Boolean
|
||||
) {
|
||||
attributesSchema.attribute(USAGE_ATTRIBUTE) { strategy ->
|
||||
strategy.compatibilityRules.add(KotlinJavaRuntimeJarsCompatibility::class.java)
|
||||
strategy.disambiguationRules.add(KotlinUsagesDisambiguation::class.java)
|
||||
@@ -198,6 +232,11 @@ object KotlinUsages {
|
||||
strategy.compatibilityRules.add(KotlinMetadataCompatibility::class.java)
|
||||
strategy.disambiguationRules.add(KotlinMetadataDisambiguation::class.java)
|
||||
}
|
||||
|
||||
// Only enable resources compatibility rule when resources configuration is used, so that for variant reselection klibs aren't selected
|
||||
if (isKotlinResourcesCompatibilityRuleEnabled) {
|
||||
strategy.compatibilityRules.add(KotlinResourcesCompatibility::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -10,6 +10,8 @@ import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.attributes.Bundling
|
||||
import org.gradle.api.attributes.Category
|
||||
import org.gradle.api.attributes.DocsType
|
||||
import org.gradle.api.attributes.HasAttributes
|
||||
import org.gradle.api.attributes.LibraryElements
|
||||
import org.gradle.api.attributes.Usage
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.publish.PublicationContainer
|
||||
@@ -24,6 +26,7 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
|
||||
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
|
||||
import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled
|
||||
import org.jetbrains.kotlin.gradle.tooling.buildKotlinToolingMetadataTask
|
||||
import org.jetbrains.kotlin.gradle.utils.*
|
||||
@@ -177,3 +180,26 @@ internal fun Configuration.configureSourcesPublicationAttributes(target: KotlinT
|
||||
attributes.setAttribute(Bundling.BUNDLING_ATTRIBUTE, project.attributeValueByName(Bundling.EXTERNAL))
|
||||
usesPlatformOf(target)
|
||||
}
|
||||
|
||||
internal fun HasAttributes.configureResourcesPublicationAttributes(target: KotlinTarget) {
|
||||
val project = target.project
|
||||
|
||||
val usage = if (target is KotlinJsIrTarget) {
|
||||
KotlinUsages.KOTLIN_RESOURCES_JS
|
||||
} else {
|
||||
KotlinUsages.KOTLIN_RESOURCES
|
||||
}
|
||||
attributes.setAttribute(
|
||||
Usage.USAGE_ATTRIBUTE,
|
||||
project.usageByName(usage)
|
||||
)
|
||||
attributes.setAttribute(
|
||||
LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
|
||||
project.objects.named(usage)
|
||||
)
|
||||
|
||||
attributes.setAttribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category.LIBRARY))
|
||||
attributes.setAttribute(Bundling.BUNDLING_ATTRIBUTE, project.attributeValueByName(Bundling.EXTERNAL))
|
||||
|
||||
setUsesPlatformOf(target)
|
||||
}
|
||||
|
||||
+3
-1
@@ -23,6 +23,7 @@ interface KotlinCompilationConfigurationsContainer {
|
||||
val runtimeDependencyConfiguration: Configuration?
|
||||
val hostSpecificMetadataConfiguration: Configuration?
|
||||
val pluginConfiguration: Configuration
|
||||
val resourcesConfiguration: Configuration?
|
||||
}
|
||||
|
||||
internal class DefaultKotlinCompilationConfigurationsContainer(
|
||||
@@ -35,7 +36,8 @@ internal class DefaultKotlinCompilationConfigurationsContainer(
|
||||
override val compileDependencyConfiguration: Configuration,
|
||||
override val runtimeDependencyConfiguration: Configuration?,
|
||||
override val hostSpecificMetadataConfiguration: Configuration?,
|
||||
override val pluginConfiguration: Configuration
|
||||
override val pluginConfiguration: Configuration,
|
||||
override val resourcesConfiguration: Configuration?,
|
||||
) : KotlinCompilationConfigurationsContainer
|
||||
|
||||
internal fun HasKotlinDependencies(
|
||||
|
||||
+29
-5
@@ -5,15 +5,19 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory
|
||||
|
||||
import org.gradle.api.attributes.Bundling
|
||||
import org.gradle.api.attributes.Category
|
||||
import org.gradle.api.attributes.LibraryElements
|
||||
import org.gradle.api.attributes.Usage
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.DefaultKotlinCompilationConfigurationsContainer
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.KotlinCompilationConfigurationsContainer
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.configureResourcesPublicationAttributes
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.javaSourceSets
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.resources.KotlinTargetResourcesPublicationImpl.Companion.RESOURCES_PATH
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.METADATA_CONFIGURATION_NAME_SUFFIX
|
||||
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
|
||||
import org.jetbrains.kotlin.gradle.utils.*
|
||||
|
||||
internal sealed class DefaultKotlinCompilationDependencyConfigurationsFactory :
|
||||
@@ -43,6 +47,7 @@ internal object NativeKotlinCompilationDependencyConfigurationsFactory :
|
||||
naming = naming,
|
||||
withRuntime = false,
|
||||
withHostSpecificMetadata = true,
|
||||
withResources = true,
|
||||
compileClasspathConfigurationName = naming.name("compileKlibraries")
|
||||
)
|
||||
}
|
||||
@@ -82,7 +87,7 @@ private const val compileClasspath = "compileClasspath"
|
||||
private const val runtimeClasspath = "runtimeClasspath"
|
||||
|
||||
private fun KotlinCompilationDependencyConfigurationsContainer(
|
||||
target: KotlinTarget, compilationName: String, withRuntime: Boolean, withHostSpecificMetadata: Boolean = false,
|
||||
target: KotlinTarget, compilationName: String, withRuntime: Boolean, withHostSpecificMetadata: Boolean = false, withResources: Boolean = false,
|
||||
naming: ConfigurationNaming = ConfigurationNaming.Default(target, compilationName),
|
||||
apiConfigurationName: String = naming.name(compilation, API),
|
||||
implementationConfigurationName: String = naming.name(compilation, IMPLEMENTATION),
|
||||
@@ -95,7 +100,8 @@ private fun KotlinCompilationDependencyConfigurationsContainer(
|
||||
PLUGIN_CLASSPATH_CONFIGURATION_NAME,
|
||||
target.disambiguationClassifier,
|
||||
compilationName
|
||||
)
|
||||
),
|
||||
resourcesPathConfigurationName: String = naming.name(RESOURCES_PATH),
|
||||
): KotlinCompilationConfigurationsContainer {
|
||||
val compilationCoordinates = "${target.disambiguationClassifier}/$compilationName"
|
||||
|
||||
@@ -198,6 +204,23 @@ private fun KotlinCompilationDependencyConfigurationsContainer(
|
||||
description = "Kotlin compiler plugins for $compilation"
|
||||
}
|
||||
|
||||
val resourcesConfiguration = if (withResources || target is KotlinJsIrTarget) {
|
||||
target.project.configurations.maybeCreateResolvable(resourcesPathConfigurationName).apply {
|
||||
// Inherit from compile dependency configuration, i.e. from the configuration that consumes apiElements
|
||||
if (target is KotlinJsIrTarget) {
|
||||
extendsFrom(runtimeDependencyConfiguration)
|
||||
} else {
|
||||
extendsFrom(compileDependencyConfiguration)
|
||||
}
|
||||
|
||||
isVisible = false
|
||||
|
||||
configureResourcesPublicationAttributes(target)
|
||||
|
||||
description = "Kotlin resources for $compilation"
|
||||
}
|
||||
} else null
|
||||
|
||||
return DefaultKotlinCompilationConfigurationsContainer(
|
||||
deprecatedCompileConfiguration = deprecatedCompileConfiguration,
|
||||
deprecatedRuntimeConfiguration = deprecatedRuntimeConfiguration,
|
||||
@@ -208,6 +231,7 @@ private fun KotlinCompilationDependencyConfigurationsContainer(
|
||||
compileDependencyConfiguration = compileDependencyConfiguration,
|
||||
runtimeDependencyConfiguration = runtimeDependencyConfiguration,
|
||||
hostSpecificMetadataConfiguration = hostSpecificMetadataConfiguration,
|
||||
pluginConfiguration = pluginConfiguration
|
||||
pluginConfiguration = pluginConfiguration,
|
||||
resourcesConfiguration = resourcesConfiguration,
|
||||
)
|
||||
}
|
||||
+4
@@ -36,6 +36,7 @@ internal class ExternalKotlinTargetImpl internal constructor(
|
||||
val apiElementsPublishedConfiguration: Configuration,
|
||||
val runtimeElementsPublishedConfiguration: Configuration,
|
||||
val sourcesElementsPublishedConfiguration: Configuration,
|
||||
val resourcesElementsPublishedConfiguration: Configuration,
|
||||
val kotlinTargetComponent: ExternalKotlinTargetComponent,
|
||||
private val artifactsTaskLocator: ArtifactsTaskLocator,
|
||||
) : InternalKotlinTarget,
|
||||
@@ -86,6 +87,9 @@ internal class ExternalKotlinTargetImpl internal constructor(
|
||||
override val sourcesElementsConfigurationName: String
|
||||
get() = sourcesElementsConfiguration.name
|
||||
|
||||
override val resourcesElementsConfigurationName: String
|
||||
get() = resourcesElementsPublishedConfiguration.name
|
||||
|
||||
@InternalKotlinGradlePluginApi
|
||||
override val kotlinComponents: Set<KotlinTargetComponent> = setOf(kotlinTargetComponent)
|
||||
|
||||
|
||||
+5
@@ -59,6 +59,10 @@ fun <T : DecoratedExternalKotlinTarget> KotlinMultiplatformExtension.createExter
|
||||
.maybeCreateDependencyScope(lowerCamelCaseName(descriptor.targetName, "sourcesElements-published"))
|
||||
.notVisible()
|
||||
|
||||
val resourcesElementsPublishedConfiguration = project.configurations
|
||||
.maybeCreateDependencyScope(lowerCamelCaseName(descriptor.targetName, "resourcesElements-published"))
|
||||
.notVisible()
|
||||
|
||||
val kotlinTargetComponent = ExternalKotlinTargetComponent(
|
||||
ExternalKotlinTargetComponent.TargetProvider.byTargetName(this, descriptor.targetName)
|
||||
)
|
||||
@@ -88,6 +92,7 @@ fun <T : DecoratedExternalKotlinTarget> KotlinMultiplatformExtension.createExter
|
||||
apiElementsPublishedConfiguration = apiElementsPublishedConfiguration,
|
||||
runtimeElementsPublishedConfiguration = runtimeElementsPublishedConfiguration,
|
||||
sourcesElementsPublishedConfiguration = sourcesElementsPublishedConfiguration,
|
||||
resourcesElementsPublishedConfiguration = resourcesElementsPublishedConfiguration,
|
||||
kotlinTargetComponent = kotlinTargetComponent,
|
||||
artifactsTaskLocator = artifactsTaskLocator
|
||||
)
|
||||
|
||||
+9
@@ -36,6 +36,8 @@ internal abstract class KotlinTargetResourcesPublicationImpl @Inject constructor
|
||||
)
|
||||
|
||||
private val targetsThatSupportPublication = listOf(
|
||||
KotlinJsIrTarget::class,
|
||||
KotlinNativeTarget::class,
|
||||
KotlinJvmTarget::class,
|
||||
KotlinAndroidTarget::class,
|
||||
)
|
||||
@@ -64,6 +66,9 @@ internal abstract class KotlinTargetResourcesPublicationImpl @Inject constructor
|
||||
|
||||
override fun canPublishResources(target: KotlinTarget): Boolean {
|
||||
if (targetsThatSupportPublication.none { it.isInstance(target) }) return false
|
||||
if (target is KotlinJsIrTarget) {
|
||||
return target.platformType == KotlinPlatformType.wasm
|
||||
}
|
||||
if (target is KotlinAndroidTarget) {
|
||||
return AndroidGradlePluginVersion.current >= KotlinAndroidTargetResourcesPublication.MIN_AGP_VERSION
|
||||
}
|
||||
@@ -114,6 +119,10 @@ internal abstract class KotlinTargetResourcesPublicationImpl @Inject constructor
|
||||
|
||||
internal companion object {
|
||||
const val MULTIPLATFORM_RESOURCES_DIRECTORY = "kotlin-multiplatform-resources"
|
||||
const val RESOURCES_CLASSIFIER = "kotlin_resources"
|
||||
const val RESOURCES_ZIP_EXTENSION = "${RESOURCES_CLASSIFIER}.zip"
|
||||
|
||||
const val RESOURCES_PATH = "ResourcesPath"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2010-2024 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.resources.publication
|
||||
|
||||
import org.gradle.api.file.DuplicatesStrategy
|
||||
import org.gradle.api.tasks.bundling.Zip
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.launch
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.AbstractKotlinTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.DefaultKotlinUsageContext
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.internal
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.resources.KotlinTargetResourcesPublicationImpl
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.resources.KotlinTargetResourcesPublicationImpl.Companion.RESOURCES_CLASSIFIER
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.resources.KotlinTargetResourcesPublicationImpl.Companion.RESOURCES_ZIP_EXTENSION
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.resources.assembleHierarchicalResources
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.resources.resourcesPublicationExtension
|
||||
import org.jetbrains.kotlin.gradle.tasks.registerTask
|
||||
|
||||
// Use KotlinMultiplatformExtension to make sure this usage context is only creatable in MPP
|
||||
@Suppress("UnusedReceiverParameter")
|
||||
internal fun KotlinMultiplatformExtension.setUpResourcesVariant(
|
||||
target: AbstractKotlinTarget,
|
||||
compilation: KotlinCompilation<*>,
|
||||
): DefaultKotlinUsageContext = with(target) {
|
||||
var targetRegistersResourcesForPublication = false
|
||||
val resourcesVariant = DefaultKotlinUsageContext(
|
||||
compilation = compilation,
|
||||
dependencyConfigurationName = resourcesElementsConfigurationName,
|
||||
includeIntoProjectStructureMetadata = false,
|
||||
publishOnlyIf = {
|
||||
targetRegistersResourcesForPublication
|
||||
}
|
||||
)
|
||||
|
||||
project.multiplatformExtension.resourcesPublicationExtension?.subscribeOnPublishResources(this) { resources ->
|
||||
targetRegistersResourcesForPublication = true
|
||||
val copyTask = compilation.assembleHierarchicalResources(
|
||||
targetName,
|
||||
resources,
|
||||
)
|
||||
val zippedResourcesDirectory = project.layout.buildDirectory.dir(
|
||||
"${KotlinTargetResourcesPublicationImpl.MULTIPLATFORM_RESOURCES_DIRECTORY}/zip-for-publication/${targetName}"
|
||||
)
|
||||
val zipResourcesForPublication = project.registerTask<Zip>(
|
||||
"${targetName}ZipMultiplatformResourcesForPublication"
|
||||
) { copy ->
|
||||
copy.destinationDirectory.set(zippedResourcesDirectory)
|
||||
copy.duplicatesStrategy = DuplicatesStrategy.FAIL
|
||||
copy.archiveExtension.set(RESOURCES_ZIP_EXTENSION)
|
||||
}
|
||||
zipResourcesForPublication.configure {
|
||||
it.from(copyTask)
|
||||
}
|
||||
|
||||
project.artifacts.add(
|
||||
compilation.target.internal.resourcesElementsConfigurationName,
|
||||
zipResourcesForPublication
|
||||
) { artifact ->
|
||||
artifact.extension = RESOURCES_ZIP_EXTENSION
|
||||
artifact.classifier = RESOURCES_CLASSIFIER
|
||||
artifact.type = "zip"
|
||||
}
|
||||
}
|
||||
|
||||
return resourcesVariant
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2010-2024 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.resources.resolve
|
||||
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics
|
||||
import org.jetbrains.kotlin.gradle.plugin.diagnostics.reportDiagnostic
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.configureResourcesPublicationAttributes
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.internal
|
||||
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
|
||||
import org.jetbrains.kotlin.gradle.utils.copyAttributesTo
|
||||
|
||||
enum class KotlinTargetResourcesResolutionStrategy {
|
||||
VariantReselection,
|
||||
ResourcesConfiguration;
|
||||
|
||||
fun resourceArchives(
|
||||
compilation: KotlinCompilation<*>,
|
||||
): FileCollection {
|
||||
return when (this) {
|
||||
VariantReselection -> {
|
||||
val dependenciesConfiguration = if (compilation.target is KotlinJsIrTarget) {
|
||||
compilation.internal.configurations.runtimeDependencyConfiguration
|
||||
?: return compilation.project.files().also {
|
||||
compilation.project.reportDiagnostic(
|
||||
KotlinToolingDiagnostics.MissingConfigurationForWasmTarget(compilation.target.name)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
compilation.internal.configurations.compileDependencyConfiguration
|
||||
}
|
||||
dependenciesConfiguration.incoming.artifactView {
|
||||
it.withVariantReselection()
|
||||
it.attributes { viewAttributes ->
|
||||
viewAttributes.configureResourcesPublicationAttributes(compilation.target)
|
||||
}
|
||||
it.isLenient = true
|
||||
}.files
|
||||
}
|
||||
|
||||
ResourcesConfiguration -> compilation.internal.configurations.resourcesConfiguration
|
||||
?: return compilation.project.files().also {
|
||||
compilation.project.reportDiagnostic(
|
||||
KotlinToolingDiagnostics.MissingResourcesConfigurationForTarget(compilation.target.name)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val propertyName: String
|
||||
get() = when (this) {
|
||||
VariantReselection -> "variantReselection"
|
||||
ResourcesConfiguration -> "resourcesConfiguration"
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromProperty(name: String): KotlinTargetResourcesResolutionStrategy? = when (name) {
|
||||
VariantReselection.propertyName -> VariantReselection
|
||||
ResourcesConfiguration.propertyName -> ResourcesConfiguration
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
-2
@@ -7,11 +7,13 @@ package org.jetbrains.kotlin.gradle.targets
|
||||
|
||||
import org.gradle.api.attributes.Category
|
||||
import org.gradle.api.attributes.Usage
|
||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.configureSourcesPublicationAttributes
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.internal
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.isSourcesPublishableFuture
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.resources.resourcesPublicationExtension
|
||||
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
|
||||
import org.jetbrains.kotlin.gradle.utils.maybeCreateConsumable
|
||||
import org.jetbrains.kotlin.gradle.utils.maybeCreateDependencyScope
|
||||
@@ -33,12 +35,13 @@ internal val CreateTargetConfigurationsSideEffect = KotlinTargetSideEffect { tar
|
||||
else -> null
|
||||
}
|
||||
|
||||
val apiElementScope = configurations.maybeCreateDependencyScope(mainCompilation.apiConfigurationName)
|
||||
configurations.maybeCreateConsumable(target.apiElementsConfigurationName).apply {
|
||||
description = "API elements for main."
|
||||
isVisible = false
|
||||
attributes.setAttribute(Usage.USAGE_ATTRIBUTE, KotlinUsages.producerApiUsage(target))
|
||||
attributes.setAttribute(Category.CATEGORY_ATTRIBUTE, project.categoryByName(Category.LIBRARY))
|
||||
extendsFrom(configurations.maybeCreateDependencyScope(mainCompilation.apiConfigurationName))
|
||||
extendsFrom(apiElementScope)
|
||||
@Suppress("TYPEALIAS_EXPANSION_DEPRECATION")
|
||||
if (mainCompilation is DeprecatedKotlinCompilationToRunnableFiles) {
|
||||
val runtimeConfiguration = mainCompilation.internal.configurations.deprecatedRuntimeConfiguration
|
||||
@@ -69,6 +72,20 @@ internal val CreateTargetConfigurationsSideEffect = KotlinTargetSideEffect { tar
|
||||
project.launch { isCanBeConsumed = target.internal.isSourcesPublishableFuture.await() }
|
||||
}
|
||||
|
||||
project.multiplatformExtensionOrNull?.resourcesPublicationExtension?.subscribeOnPublishResources(target) {
|
||||
configurations.maybeCreateConsumable(target.internal.resourcesElementsConfigurationName).apply {
|
||||
description = "Resource files of main compilation of ${target.name}."
|
||||
isVisible = false
|
||||
// Publish with dependencies of apiElements configuration, so that transitives are resolved correctly. Don't inherit from
|
||||
// apiElementsConfiguration directly, because it contains klibs in project dependencies.
|
||||
extendsFrom(apiElementScope)
|
||||
// Resources from implementation scope should also be resolved transitively, hence extend from it.
|
||||
extendsFrom(implementationConfiguration)
|
||||
|
||||
configureResourcesPublicationAttributes(target)
|
||||
}
|
||||
}
|
||||
|
||||
if (target !is KotlinMetadataTarget) {
|
||||
val testCompilation = target.compilations.getByName(KotlinCompilation.TEST_COMPILATION_NAME)
|
||||
val compileTestsConfiguration = testCompilation.internal.configurations.deprecatedCompileConfiguration
|
||||
@@ -104,4 +121,4 @@ internal val CreateTargetConfigurationsSideEffect = KotlinTargetSideEffect { tar
|
||||
attributes.setAttribute(KotlinPlatformType.attribute, KotlinPlatformType.common)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
@@ -20,7 +20,9 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetComponent
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetWithTests
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.resources.publication.setUpResourcesVariant
|
||||
import org.jetbrains.kotlin.gradle.targets.js.JsAggregatingExecutionSource
|
||||
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsReportAggregatingTestRun
|
||||
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTestRunFactory
|
||||
@@ -90,6 +92,17 @@ constructor(
|
||||
)
|
||||
)
|
||||
|
||||
if (project.kotlinPropertiesProvider.mppResourcesPublication) {
|
||||
project.multiplatformExtensionOrNull?.let {
|
||||
usageContexts.add(
|
||||
it.setUpResourcesVariant(
|
||||
this,
|
||||
mainCompilation
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val result = createKotlinVariant(componentName, mainCompilation, usageContexts)
|
||||
|
||||
setOf(result)
|
||||
|
||||
+11
@@ -12,6 +12,8 @@ import org.gradle.api.attributes.Attribute
|
||||
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
|
||||
import org.jetbrains.kotlin.gradle.dsl.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.resources.publication.setUpResourcesVariant
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.awaitPlatformCompilations
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.internal
|
||||
import org.jetbrains.kotlin.gradle.targets.metadata.*
|
||||
@@ -81,6 +83,15 @@ abstract class KotlinNativeTarget @Inject constructor(
|
||||
)
|
||||
)
|
||||
|
||||
if (project.kotlinPropertiesProvider.mppResourcesPublication) {
|
||||
mutableUsageContexts.add(
|
||||
project.multiplatformExtension.setUpResourcesVariant(
|
||||
this,
|
||||
mainCompilation,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val result = createKotlinVariant(targetName, mainCompilation, mutableUsageContexts)
|
||||
|
||||
setOf(result)
|
||||
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright 2010-2024 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:OptIn(ExperimentalWasmDsl::class)
|
||||
|
||||
package org.jetbrains.kotlin.gradle.unitTests
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.internal
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.resources.KotlinTargetResourcesPublication
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.resources.resourcesPublicationExtension
|
||||
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
|
||||
import org.jetbrains.kotlin.gradle.util.buildProjectWithMPP
|
||||
import org.jetbrains.kotlin.gradle.util.enableMppResourcesPublication
|
||||
import org.jetbrains.kotlin.gradle.util.kotlin
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNull
|
||||
|
||||
class KotlinTargetVariantResourcesPublicationTests {
|
||||
|
||||
@Test
|
||||
fun `test variant publication - doesn't happen - when resources publication is disabled`() {
|
||||
val enableResourcePublication = false
|
||||
testPublishedVariants(
|
||||
enableResourcePublication = enableResourcePublication,
|
||||
afterEvaluation = { target ->
|
||||
publishFakeResources(target)
|
||||
},
|
||||
shouldPublishVariant = enableResourcePublication,
|
||||
targetsToTest = listOf(
|
||||
{ linuxX64() },
|
||||
{ wasmWasi() },
|
||||
{ wasmJs() },
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test variant publication - doesn't happen - when publishing api is not called`() {
|
||||
testPublishedVariants(
|
||||
enableResourcePublication = true,
|
||||
afterEvaluation = { /* don't call publishing api */ },
|
||||
shouldPublishVariant = false,
|
||||
targetsToTest = listOf(
|
||||
{ linuxX64() },
|
||||
{ wasmWasi() },
|
||||
{ wasmJs() },
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test variant publication - creates a variant - when publishing api is called`() {
|
||||
testPublishedVariants(
|
||||
enableResourcePublication = true,
|
||||
afterEvaluation = { target ->
|
||||
publishFakeResources(target)
|
||||
},
|
||||
shouldPublishVariant = true,
|
||||
targetsToTest = listOf(
|
||||
{ linuxX64() },
|
||||
{ wasmWasi() },
|
||||
{ wasmJs() },
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test resources configuration - outputs a single zip file - in outgoing artifacts`() {
|
||||
val project = buildProjectWithMPP {
|
||||
kotlin {
|
||||
linuxX64()
|
||||
}
|
||||
enableMppResourcesPublication(true)
|
||||
}.evaluate()
|
||||
|
||||
assertNull(
|
||||
project.configurations.findByName(
|
||||
project.multiplatformExtension.linuxX64().resourcesElementsConfigurationName
|
||||
)
|
||||
)
|
||||
|
||||
project.publishFakeResources(project.multiplatformExtension.linuxX64())
|
||||
|
||||
assertEquals(
|
||||
listOf(
|
||||
project.layout.buildDirectory.file(
|
||||
"kotlin-multiplatform-resources/zip-for-publication/linuxX64/test.kotlin_resources.zip"
|
||||
).get().asFile
|
||||
),
|
||||
project.configurations.findByName(
|
||||
project.multiplatformExtension.linuxX64().resourcesElementsConfigurationName
|
||||
)?.outgoing?.artifacts?.map { it.file },
|
||||
)
|
||||
}
|
||||
|
||||
private fun testPublishedVariants(
|
||||
enableResourcePublication: Boolean,
|
||||
afterEvaluation: Project.(KotlinTarget) -> Unit,
|
||||
shouldPublishVariant: Boolean,
|
||||
targetsToTest: List<KotlinMultiplatformExtension.() -> KotlinTarget>,
|
||||
) {
|
||||
val project = buildProjectWithMPP {
|
||||
kotlin {
|
||||
targetsToTest.forEach { createTarget ->
|
||||
createTarget()
|
||||
}
|
||||
}
|
||||
enableMppResourcesPublication(enableResourcePublication)
|
||||
}.evaluate()
|
||||
|
||||
targetsToTest.forEach { targetToTest ->
|
||||
val target = project.multiplatformExtension.targetToTest()
|
||||
|
||||
// Resources configuration should never be published before publishing API is called
|
||||
assert(target.internal.resourcesElementsConfigurationName !in configurationNamesUsedInVariantPublications(target))
|
||||
project.afterEvaluation(target)
|
||||
assert(
|
||||
shouldPublishVariant == target.internal.resourcesElementsConfigurationName in configurationNamesUsedInVariantPublications(target)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun configurationNamesUsedInVariantPublications(target: KotlinTarget): Set<String> {
|
||||
return target.internal.kotlinComponents.flatMap { component ->
|
||||
component.internal.usages.map { it.dependencyConfigurationName }
|
||||
}.toSet()
|
||||
}
|
||||
|
||||
private fun Project.publishFakeResources(target: KotlinTarget) {
|
||||
project.multiplatformExtension.resourcesPublicationExtension?.publishResourcesAsKotlinComponent(
|
||||
target,
|
||||
resourcePathForSourceSet = {
|
||||
KotlinTargetResourcesPublication.ResourceRoot(
|
||||
project.provider { File(it.name) },
|
||||
emptyList(),
|
||||
emptyList(),
|
||||
)
|
||||
},
|
||||
relativeResourcePlacement = project.provider { File("test") },
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
+564
@@ -0,0 +1,564 @@
|
||||
/*
|
||||
* Copyright 2010-2024 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:OptIn(ExperimentalWasmDsl::class)
|
||||
|
||||
package org.jetbrains.kotlin.gradle.unitTests
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.artifacts.Dependency
|
||||
import org.gradle.api.artifacts.ResolveException
|
||||
import org.gradle.api.attributes.Attribute
|
||||
import org.gradle.api.attributes.Usage
|
||||
import org.gradle.api.internal.project.ProjectInternal
|
||||
import org.gradle.kotlin.dsl.project
|
||||
import org.gradle.testfixtures.ProjectBuilder
|
||||
import org.jetbrains.kotlin.gradle.dependencyResolutionTests.mavenCentralCacheRedirector
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.internal
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.resources.KotlinTargetResourcesPublication
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.resources.resolve.KotlinTargetResourcesResolutionStrategy
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.resources.resourcesPublicationExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.usageByName
|
||||
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
|
||||
import org.jetbrains.kotlin.gradle.util.*
|
||||
import org.jetbrains.kotlin.gradle.utils.buildOrNull
|
||||
import org.jetbrains.kotlin.util.assertThrows
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class KotlinTargetVariantResourcesResolutionTests {
|
||||
|
||||
@Test
|
||||
fun `test direct dependency - is the same - for all resolution methods and supported scopes`() {
|
||||
KotlinTargetResourcesResolutionStrategy.values().forEach { resolutionMethod ->
|
||||
dependencyScopesWithResources().forEach { dependencyScope ->
|
||||
testDirectDependencyOnResourcesProducer(
|
||||
producerTarget = { linuxX64() },
|
||||
consumerTarget = { linuxX64() },
|
||||
dependencyScope = dependencyScope,
|
||||
resolutionStrategy = resolutionMethod,
|
||||
expectedResult = { _, producer ->
|
||||
hashSetOf(producer.buildFile("kotlin-multiplatform-resources/zip-for-publication/linuxX64/producer.kotlin_resources.zip"))
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test direct dependency - between different native targets - with artifact view`() {
|
||||
testDirectDependencyOnResourcesProducer(
|
||||
producerTarget = { linuxX64() },
|
||||
consumerTarget = { linuxArm64() },
|
||||
resolutionStrategy = KotlinTargetResourcesResolutionStrategy.VariantReselection,
|
||||
expectedResult = { _, _ -> emptySet() },
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test direct dependency - between different native targets - with resources configuration`() {
|
||||
directDependencyOnResourcesProducer(
|
||||
producerTarget = { linuxX64() },
|
||||
consumerTarget = { linuxArm64() },
|
||||
strategy = KotlinTargetResourcesResolutionStrategy.ResourcesConfiguration,
|
||||
assert = { consumer, _ ->
|
||||
assertThrows<ResolveException> {
|
||||
KotlinTargetResourcesResolutionStrategy.ResourcesConfiguration.resourceArchives(
|
||||
consumer.multiplatformExtension.linuxArm64().compilations.getByName("main")
|
||||
).files
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test direct dependency - for wasmJs and wasmWasi targets - when using artifact view`() {
|
||||
listOf<TargetProvider>(
|
||||
{ wasmJs() },
|
||||
{ wasmWasi() },
|
||||
).forEach { target ->
|
||||
testDirectDependencyOnResourcesProducer(
|
||||
producerTarget = { target() },
|
||||
consumerTarget = { target() },
|
||||
resolutionStrategy = KotlinTargetResourcesResolutionStrategy.VariantReselection,
|
||||
expectedResult = { _, producer ->
|
||||
hashSetOf(
|
||||
producer.buildFile(
|
||||
"kotlin-multiplatform-resources/zip-for-publication/${producer.multiplatformExtension.target().name}/producer.kotlin_resources.zip"
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test direct dependency - for wasmJs - when using resources configuration`() {
|
||||
testDirectDependencyOnResourcesProducer(
|
||||
producerTarget = { wasmJs() },
|
||||
consumerTarget = { wasmJs() },
|
||||
resolutionStrategy = KotlinTargetResourcesResolutionStrategy.ResourcesConfiguration,
|
||||
filterResolvedFiles = {
|
||||
it.filterNot {
|
||||
it.path.contains("kotlin-stdlib-wasm-js")
|
||||
}.toSet()
|
||||
},
|
||||
expectedResult = { _, producer ->
|
||||
hashSetOf(
|
||||
producer.buildFile(
|
||||
"kotlin-multiplatform-resources/zip-for-publication/wasmJs/producer.kotlin_resources.zip"
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test direct dependency - for wasmWasi - when using resources configuration`() {
|
||||
testDirectDependencyOnResourcesProducer(
|
||||
producerTarget = { wasmWasi() },
|
||||
consumerTarget = { wasmWasi() },
|
||||
resolutionStrategy = KotlinTargetResourcesResolutionStrategy.ResourcesConfiguration,
|
||||
filterResolvedFiles = {
|
||||
it.filterNot {
|
||||
it.path.contains("kotlin-stdlib-wasm-wasi")
|
||||
}.toSet()
|
||||
},
|
||||
expectedResult = { _, producer ->
|
||||
hashSetOf(
|
||||
producer.buildFile("kotlin-multiplatform-resources/zip-for-publication/wasmWasi/producer.kotlin_resources.zip"),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test transitive dependency - without resources in middle project - with configuration`() {
|
||||
testTransitiveDependencyOnResourcesProducer(
|
||||
targetProvider = { linuxX64() },
|
||||
resolutionStrategy = KotlinTargetResourcesResolutionStrategy.ResourcesConfiguration,
|
||||
expectedResult = { _, middle, producer ->
|
||||
setOf(
|
||||
producer.buildFile("kotlin-multiplatform-resources/zip-for-publication/linuxX64/producer.kotlin_resources.zip"),
|
||||
middle.buildFile("classes/kotlin/linuxX64/main/klib/middle.klib"),
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test transitive dependency - without resources in middle project in wasm - with configuration`() {
|
||||
dependencyScopesWithResources().forEach { dependencyScope ->
|
||||
testTransitiveDependencyOnResourcesProducer(
|
||||
targetProvider = { wasmJs() },
|
||||
resolutionStrategy = KotlinTargetResourcesResolutionStrategy.ResourcesConfiguration,
|
||||
dependencyScope = dependencyScope,
|
||||
filterResolvedFiles = {
|
||||
it.filterNot {
|
||||
it.path.contains("kotlin-stdlib-wasm-js")
|
||||
}.toSet()
|
||||
},
|
||||
expectedResult = { _, middle, producer ->
|
||||
setOf(
|
||||
producer.buildFile("kotlin-multiplatform-resources/zip-for-publication/wasmJs/producer.kotlin_resources.zip"),
|
||||
middle.buildFile("libs/middle-wasm-js.klib"),
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test transitive dependency - without resources in middle project in wasm - with artifact view`() {
|
||||
dependencyScopesWithResources().forEach { dependencyScope ->
|
||||
testTransitiveDependencyOnResourcesProducer(
|
||||
targetProvider = { wasmJs() },
|
||||
resolutionStrategy = KotlinTargetResourcesResolutionStrategy.VariantReselection,
|
||||
dependencyScope = dependencyScope,
|
||||
filterResolvedFiles = {
|
||||
it.filterNot {
|
||||
it.path.contains("kotlin-stdlib-wasm-js")
|
||||
}.toSet()
|
||||
},
|
||||
expectedResult = { _, _, producer ->
|
||||
setOf(
|
||||
producer.buildFile("kotlin-multiplatform-resources/zip-for-publication/wasmJs/producer.kotlin_resources.zip"),
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test transitive dependency - without resources in middle project - with artifact view`() {
|
||||
testTransitiveDependencyOnResourcesProducer(
|
||||
targetProvider = { linuxX64() },
|
||||
resolutionStrategy = KotlinTargetResourcesResolutionStrategy.VariantReselection,
|
||||
expectedResult = { _, _, producer ->
|
||||
setOf(
|
||||
producer.buildFile("kotlin-multiplatform-resources/zip-for-publication/linuxX64/producer.kotlin_resources.zip"),
|
||||
// VariantReselection strategy disables compatibility rule that resolves klibs for resources configuration
|
||||
// middle.buildFile("classes/kotlin/linuxX64/main/klib/middle.klib"),
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test transitive dependency - with resources in middle project - with artifact view`() {
|
||||
dependencyScopesWithResources().forEach { dependencyScope ->
|
||||
testTransitiveDependencyOnResourcesProducer(
|
||||
targetProvider = { linuxX64() },
|
||||
resolutionStrategy = KotlinTargetResourcesResolutionStrategy.VariantReselection,
|
||||
dependencyScope = dependencyScope,
|
||||
middlePublishesResources = true,
|
||||
expectedResult = { _, middle, producer ->
|
||||
setOf(
|
||||
middle.buildFile("kotlin-multiplatform-resources/zip-for-publication/linuxX64/middle.kotlin_resources.zip"),
|
||||
producer.buildFile("kotlin-multiplatform-resources/zip-for-publication/linuxX64/producer.kotlin_resources.zip"),
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
testTransitiveDependencyOnResourcesProducer(
|
||||
targetProvider = { linuxX64() },
|
||||
resolutionStrategy = KotlinTargetResourcesResolutionStrategy.VariantReselection,
|
||||
middlePublishesResources = true,
|
||||
consumerPublishesResources = true,
|
||||
expectedResult = { _, middle, producer ->
|
||||
setOf(
|
||||
middle.buildFile("kotlin-multiplatform-resources/zip-for-publication/linuxX64/middle.kotlin_resources.zip"),
|
||||
producer.buildFile("kotlin-multiplatform-resources/zip-for-publication/linuxX64/producer.kotlin_resources.zip"),
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test transitive dependency - with resources in middle project - with configuration`() {
|
||||
dependencyScopesWithResources().forEach { dependencyScope ->
|
||||
testTransitiveDependencyOnResourcesProducer(
|
||||
targetProvider = { linuxX64() },
|
||||
resolutionStrategy = KotlinTargetResourcesResolutionStrategy.ResourcesConfiguration,
|
||||
dependencyScope = dependencyScope,
|
||||
middlePublishesResources = true,
|
||||
expectedResult = { _, middle, producer ->
|
||||
setOf(
|
||||
middle.buildFile("kotlin-multiplatform-resources/zip-for-publication/linuxX64/middle.kotlin_resources.zip"),
|
||||
producer.buildFile("kotlin-multiplatform-resources/zip-for-publication/linuxX64/producer.kotlin_resources.zip"),
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
testTransitiveDependencyOnResourcesProducer(
|
||||
targetProvider = { linuxX64() },
|
||||
resolutionStrategy = KotlinTargetResourcesResolutionStrategy.ResourcesConfiguration,
|
||||
middlePublishesResources = true,
|
||||
consumerPublishesResources = true,
|
||||
expectedResult = { _, middle, producer ->
|
||||
setOf(
|
||||
middle.buildFile("kotlin-multiplatform-resources/zip-for-publication/linuxX64/middle.kotlin_resources.zip"),
|
||||
producer.buildFile("kotlin-multiplatform-resources/zip-for-publication/linuxX64/producer.kotlin_resources.zip"),
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test resources - don't leak into non-resources configurations`() {
|
||||
resourcesConfigurationResolutionSanityCheck()
|
||||
|
||||
val targetsToTest = listOf<TargetProvider>(
|
||||
{ wasmJs() },
|
||||
{ wasmWasi() },
|
||||
{ linuxX64() },
|
||||
{ iosArm64() },
|
||||
)
|
||||
targetsToTest.indices.forEach { index ->
|
||||
// Test when target is matching
|
||||
testNonResourcesConfigurationDontResolveResourceVariants(
|
||||
producerTarget = targetsToTest[index],
|
||||
consumerTarget = targetsToTest[index],
|
||||
strategy = KotlinTargetResourcesResolutionStrategy.ResourcesConfiguration,
|
||||
)
|
||||
|
||||
// Test when target is not matching
|
||||
testNonResourcesConfigurationDontResolveResourceVariants(
|
||||
producerTarget = targetsToTest[index],
|
||||
consumerTarget = targetsToTest[(index + 1) % targetsToTest.count()],
|
||||
strategy = KotlinTargetResourcesResolutionStrategy.ResourcesConfiguration,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun dependencyScopesWithResources(): List<DependencyScopeProvider> {
|
||||
return listOf(
|
||||
{ this::implementation },
|
||||
{ this::api },
|
||||
// What are these supposed to mean for resources?
|
||||
// { this::compileOnly }, ?
|
||||
// { this::runtimeOnly }, ?
|
||||
)
|
||||
}
|
||||
|
||||
private fun resourcesConfigurationResolutionSanityCheck() {
|
||||
directDependencyOnResourcesProducer(
|
||||
producerTarget = { linuxX64() },
|
||||
consumerTarget = { linuxX64() },
|
||||
strategy = KotlinTargetResourcesResolutionStrategy.ResourcesConfiguration,
|
||||
assert = { consumer, _ ->
|
||||
val resourcesConfiguration = consumer.multiplatformExtension.linuxX64()
|
||||
.compilations.getByName("main")
|
||||
.internal.configurations.resourcesConfiguration ?: error("Missing resources configuration")
|
||||
|
||||
assertEquals(
|
||||
mapOf(
|
||||
// linuxX64ResourcesPath is the root resolvable configuration for the resolution
|
||||
"test:consumer" to listOf(listOf("linuxX64ResourcesPath")),
|
||||
// stdlib doesn't have resources, so apiElements is selected as per compatibility rule
|
||||
"org.jetbrains.kotlin:kotlin-stdlib" to listOf(listOf("nativeApiElements")),
|
||||
// producer provides the consumable configuration with resources for the consumer
|
||||
"test:producer" to listOf(listOf("linuxX64ResourcesElements")),
|
||||
),
|
||||
resourcesConfiguration.incoming.resolutionResult.allComponents.groupBy(
|
||||
keySelector = { "${it.moduleVersion?.group}:${it.moduleVersion?.name}" },
|
||||
valueTransform = { it.variants.map { it.displayName } },
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun testNonResourcesConfigurationDontResolveResourceVariants(
|
||||
producerTarget: TargetProvider,
|
||||
consumerTarget: TargetProvider,
|
||||
strategy: KotlinTargetResourcesResolutionStrategy,
|
||||
) {
|
||||
directDependencyOnResourcesProducer(
|
||||
producerTarget = producerTarget,
|
||||
consumerTarget = consumerTarget,
|
||||
strategy = strategy,
|
||||
assert = { consumer, _ ->
|
||||
val resourcesConfigurations = consumer.multiplatformExtension.targets.flatMap {
|
||||
it.compilations.mapNotNull { it.internal.configurations.resourcesConfiguration }
|
||||
}
|
||||
|
||||
val nonResourcesConfigurations: Set<Configuration> = consumer.configurations.filter {
|
||||
it.isCanBeResolved
|
||||
}.toHashSet() - resourcesConfigurations
|
||||
assert(nonResourcesConfigurations.isNotEmpty())
|
||||
|
||||
nonResourcesConfigurations.forEach { resolvableConfiguration ->
|
||||
val resolvedComponents = resolvableConfiguration.incoming.resolutionResult.allComponents
|
||||
|
||||
resolvedComponents.forEach { resolvedComponent ->
|
||||
val resolvedVariants = resolvedComponent.variants
|
||||
|
||||
resolvedVariants.forEach { variant ->
|
||||
val variantPath = { "$resolvableConfiguration -> $resolvedComponent -> $variant" }
|
||||
val typedUsage = variant.attributes.getAttribute(Usage.USAGE_ATTRIBUTE)
|
||||
val stringUsage = variant.attributes.getAttribute(Attribute.of(Usage.USAGE_ATTRIBUTE.name, String::class.java))
|
||||
val isKotlinCompilerClasspath = variant.attributes.keySet().isEmpty()
|
||||
assert(
|
||||
typedUsage != null || stringUsage != null || isKotlinCompilerClasspath,
|
||||
variantPath
|
||||
)
|
||||
assert(
|
||||
typedUsage != consumer.project.usageByName(KotlinUsages.KOTLIN_RESOURCES),
|
||||
variantPath,
|
||||
)
|
||||
assert(
|
||||
stringUsage != KotlinUsages.KOTLIN_RESOURCES,
|
||||
variantPath,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun testDirectDependencyOnResourcesProducer(
|
||||
producerTarget: TargetProvider,
|
||||
consumerTarget: TargetProvider,
|
||||
dependencyScope: DependencyScopeProvider = { ::implementation },
|
||||
resolutionStrategy: KotlinTargetResourcesResolutionStrategy,
|
||||
filterResolvedFiles: (Set<File>) -> Set<File> = { it },
|
||||
expectedResult: (consumer: Project, producer: Project) -> Set<File>,
|
||||
) = directDependencyOnResourcesProducer(
|
||||
producerTarget = producerTarget,
|
||||
consumerTarget = consumerTarget,
|
||||
dependencyScope = dependencyScope,
|
||||
strategy = resolutionStrategy,
|
||||
assert = { consumer: Project, producer: Project ->
|
||||
consumer.setMppResourcesResolutionStrategy(resolutionStrategy)
|
||||
assertEquals(
|
||||
expectedResult(consumer, producer),
|
||||
filterResolvedFiles(
|
||||
resolutionStrategy.resourceArchives(
|
||||
consumer.multiplatformExtension.consumerTarget().compilations.getByName("main"),
|
||||
).files
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
private fun directDependencyOnResourcesProducer(
|
||||
producerTarget: TargetProvider,
|
||||
consumerTarget: TargetProvider,
|
||||
strategy: KotlinTargetResourcesResolutionStrategy,
|
||||
dependencyScope: DependencyScopeProvider = { ::implementation },
|
||||
assert: (consumer: Project, producer: Project) -> Unit,
|
||||
) {
|
||||
val rootProject = buildProject()
|
||||
val producer = rootProject.createSubproject("producer") {
|
||||
kotlin { producerTarget() }
|
||||
}
|
||||
val consumer = rootProject.createSubproject(
|
||||
"consumer",
|
||||
preApplyCode = { setMppResourcesResolutionStrategy(strategy) }
|
||||
) {
|
||||
kotlin {
|
||||
consumerTarget()
|
||||
sourceSets.commonMain {
|
||||
dependencies {
|
||||
dependencyScope()(project(":${producer.name}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
listOf(rootProject, producer, consumer).forEach { it.evaluate() }
|
||||
producer.publishFakeResources(producer.multiplatformExtension.producerTarget())
|
||||
|
||||
assert(consumer, producer)
|
||||
}
|
||||
|
||||
private fun testTransitiveDependencyOnResourcesProducer(
|
||||
targetProvider: TargetProvider,
|
||||
resolutionStrategy: KotlinTargetResourcesResolutionStrategy,
|
||||
filterResolvedFiles: (Set<File>) -> Set<File> = { it },
|
||||
dependencyScope: DependencyScopeProvider = { ::implementation },
|
||||
middlePublishesResources: Boolean = false,
|
||||
consumerPublishesResources: Boolean = false,
|
||||
expectedResult: (consumer: Project, middle: Project, producer: Project) -> Set<File>,
|
||||
) {
|
||||
val rootProject = buildProject()
|
||||
val producer = rootProject.createSubproject(
|
||||
"producer",
|
||||
preApplyCode = {
|
||||
enableMppResourcesPublication(true)
|
||||
setMppResourcesResolutionStrategy(resolutionStrategy)
|
||||
}
|
||||
) {
|
||||
kotlin { targetProvider() }
|
||||
}
|
||||
|
||||
val middle = rootProject.createSubproject(
|
||||
"middle",
|
||||
preApplyCode = {
|
||||
enableMppResourcesPublication(middlePublishesResources)
|
||||
setMppResourcesResolutionStrategy(resolutionStrategy)
|
||||
}
|
||||
) {
|
||||
kotlin {
|
||||
targetProvider()
|
||||
sourceSets.commonMain {
|
||||
dependencies {
|
||||
dependencyScope()(dependencies.project(":${producer.name}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val consumer = rootProject.createSubproject(
|
||||
"consumer",
|
||||
preApplyCode = {
|
||||
enableMppResourcesPublication(consumerPublishesResources)
|
||||
setMppResourcesResolutionStrategy(resolutionStrategy)
|
||||
}
|
||||
) {
|
||||
kotlin {
|
||||
targetProvider()
|
||||
sourceSets.commonMain {
|
||||
dependencies {
|
||||
dependencyScope()(dependencies.project(":${middle.name}"))
|
||||
}
|
||||
}
|
||||
setMppResourcesResolutionStrategy(resolutionStrategy)
|
||||
}
|
||||
}
|
||||
|
||||
listOf(rootProject, producer, middle, consumer).forEach { it.evaluate() }
|
||||
|
||||
producer.publishFakeResources(producer.multiplatformExtension.targetProvider())
|
||||
if (middlePublishesResources) middle.publishFakeResources(middle.multiplatformExtension.targetProvider())
|
||||
if (consumerPublishesResources) consumer.publishFakeResources(consumer.multiplatformExtension.targetProvider())
|
||||
|
||||
assertEquals(
|
||||
expectedResult(consumer, middle, producer),
|
||||
filterResolvedFiles(
|
||||
resolutionStrategy.resourceArchives(
|
||||
consumer.multiplatformExtension.targetProvider().compilations.getByName("main"),
|
||||
).files
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun Project.buildFile(path: String) = layout.buildDirectory.file(path).get().asFile
|
||||
|
||||
private fun ProjectInternal.createSubproject(
|
||||
name: String,
|
||||
preApplyCode: Project.() -> Unit = {},
|
||||
code: Project.() -> Unit = {},
|
||||
) = buildProjectWithMPPAndStdlib(
|
||||
projectBuilder = {
|
||||
withParent(this@createSubproject)
|
||||
withName(name)
|
||||
},
|
||||
preApplyCode = preApplyCode,
|
||||
code = code,
|
||||
)
|
||||
|
||||
private fun buildProjectWithMPPAndStdlib(
|
||||
projectBuilder: ProjectBuilder.() -> Unit = { },
|
||||
preApplyCode: Project.() -> Unit = {},
|
||||
code: Project.() -> Unit = {},
|
||||
) = buildProjectWithMPP(
|
||||
projectBuilder = projectBuilder,
|
||||
preApplyCode = preApplyCode,
|
||||
) {
|
||||
enableDependencyVerification(false)
|
||||
enableDefaultStdlibDependency(true)
|
||||
repositories.mavenLocal()
|
||||
repositories.mavenCentralCacheRedirector()
|
||||
code()
|
||||
}
|
||||
|
||||
private fun Project.publishFakeResources(target: KotlinTarget) {
|
||||
project.multiplatformExtension.resourcesPublicationExtension?.publishResourcesAsKotlinComponent(
|
||||
target,
|
||||
resourcePathForSourceSet = {
|
||||
KotlinTargetResourcesPublication.ResourceRoot(
|
||||
project.provider { File(it.name) },
|
||||
emptyList(),
|
||||
emptyList(),
|
||||
)
|
||||
},
|
||||
relativeResourcePlacement = project.provider { File("test") },
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private typealias TargetProvider = KotlinMultiplatformExtension.() -> (KotlinTarget)
|
||||
private typealias DependencyScopeProvider = KotlinDependencyHandler.() -> ((Any) -> Dependency?)
|
||||
+5
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_INTRANSITIVE_METADATA_CONFIGURATION
|
||||
import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.getExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.resources.resolve.KotlinTargetResourcesResolutionStrategy
|
||||
import org.jetbrains.kotlin.gradle.targets.native.tasks.artifact.KotlinArtifactsExtensionImpl
|
||||
import org.jetbrains.kotlin.gradle.targets.native.tasks.artifact.kotlinArtifactsExtension
|
||||
|
||||
@@ -117,6 +118,10 @@ fun Project.enableMppResourcesPublication(enabled: Boolean = true) {
|
||||
propertiesExtension.set(PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_RESOURCES_PUBLICATION, enabled.toString())
|
||||
}
|
||||
|
||||
fun Project.setMppResourcesResolutionStrategy(strategy: KotlinTargetResourcesResolutionStrategy) {
|
||||
propertiesExtension.set(PropertiesProvider.PropertyNames.KOTLIN_MPP_RESOURCES_RESOLUTION_STRATEGY, strategy.propertyName)
|
||||
}
|
||||
|
||||
fun Project.enableIntransitiveMetadataConfiguration(enabled: Boolean = true) {
|
||||
propertiesExtension.set(KOTLIN_MPP_ENABLE_INTRANSITIVE_METADATA_CONFIGURATION, enabled.toString())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user