diff --git a/CHANGELOG.md b/CHANGELOG.md index 4835401113..8bf389af42 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Fixed issue #1778 by removing a hash of the absolute artifact path appended to the end of the version string. That hash made artifact version different on different PCs and also breaks Gradle dependency locking. - Add the missing `org.jetbrains.kotlin.platform.type=jvm` attribute to the `intellijPlatformRuntimeClasspath` configuration manually as it's not inherited from the `runtimeClasspath`. - Fixed `Could not generate a decorated class for type PluginArtifactRepository.` when creating a custom plugin repository. +- #1779 Fixes compatibility with Gradle dependency verification. Previously it was failing with "Failed to create MD5 hash for file". ## [2.1.0] diff --git a/api/IntelliJPlatformGradlePlugin.api b/api/IntelliJPlatformGradlePlugin.api index 771bc0a4fa..efa5b841a9 100644 --- a/api/IntelliJPlatformGradlePlugin.api +++ b/api/IntelliJPlatformGradlePlugin.api @@ -471,7 +471,7 @@ public abstract class org/jetbrains/intellij/platform/gradle/attributes/Distribu public abstract class org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformDependenciesExtension { public static final field Companion Lorg/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformDependenciesExtension$Companion; - public fun (Lorg/gradle/api/artifacts/ConfigurationContainer;Lorg/gradle/api/artifacts/dsl/DependencyHandler;Lorg/gradle/api/file/ProjectLayout;Lorg/gradle/api/model/ObjectFactory;Lorg/gradle/api/provider/ProviderFactory;Lorg/gradle/api/resources/ResourceHandler;Ljava/nio/file/Path;)V + public fun (Lorg/gradle/api/artifacts/dsl/RepositoryHandler;Lorg/gradle/api/artifacts/ConfigurationContainer;Lorg/gradle/api/artifacts/dsl/DependencyHandler;Lorg/gradle/api/file/ProjectLayout;Lorg/gradle/api/model/ObjectFactory;Lorg/gradle/api/provider/ProviderFactory;Lorg/gradle/api/resources/ResourceHandler;Ljava/nio/file/Path;)V public final fun androidStudio (Ljava/lang/String;)V public final fun androidStudio (Ljava/lang/String;Z)V public final fun androidStudio (Lorg/gradle/api/provider/Provider;)V @@ -695,7 +695,7 @@ public final class org/jetbrains/intellij/platform/gradle/extensions/IntelliJPla } public final class org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformDependenciesHelper { - public fun (Lorg/gradle/api/artifacts/ConfigurationContainer;Lorg/gradle/api/artifacts/dsl/DependencyHandler;Lorg/gradle/api/file/ProjectLayout;Lorg/gradle/api/model/ObjectFactory;Lorg/gradle/api/provider/ProviderFactory;Lorg/gradle/api/resources/ResourceHandler;Ljava/nio/file/Path;)V + public fun (Lorg/gradle/api/artifacts/dsl/RepositoryHandler;Lorg/gradle/api/artifacts/ConfigurationContainer;Lorg/gradle/api/artifacts/dsl/DependencyHandler;Lorg/gradle/api/file/ProjectLayout;Lorg/gradle/api/model/ObjectFactory;Lorg/gradle/api/provider/ProviderFactory;Lorg/gradle/api/resources/ResourceHandler;Ljava/nio/file/Path;)V } public abstract class org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformExtension : org/gradle/api/plugins/ExtensionAware { @@ -816,7 +816,7 @@ public final class org/jetbrains/intellij/platform/gradle/extensions/IntelliJPla public abstract class org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformExtension$PluginVerification$Ides { public static final field Companion Lorg/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformExtension$PluginVerification$Ides$Companion; - public fun (Lorg/gradle/api/artifacts/ConfigurationContainer;Lorg/gradle/api/artifacts/dsl/DependencyHandler;Lorg/gradle/api/file/ProjectLayout;Lorg/gradle/api/model/ObjectFactory;Lorg/gradle/api/provider/ProviderFactory;Lorg/gradle/api/resources/ResourceHandler;Ljava/nio/file/Path;Lorg/gradle/api/provider/Provider;)V + public fun (Lorg/gradle/api/artifacts/dsl/RepositoryHandler;Lorg/gradle/api/artifacts/ConfigurationContainer;Lorg/gradle/api/artifacts/dsl/DependencyHandler;Lorg/gradle/api/file/ProjectLayout;Lorg/gradle/api/model/ObjectFactory;Lorg/gradle/api/provider/ProviderFactory;Lorg/gradle/api/resources/ResourceHandler;Ljava/nio/file/Path;Lorg/gradle/api/provider/Provider;)V public final fun ProductReleasesValueSource (Lkotlin/jvm/functions/Function1;)Lorg/gradle/api/provider/Provider; public static synthetic fun ProductReleasesValueSource$default (Lorg/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformExtension$PluginVerification$Ides;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/gradle/api/provider/Provider; public final fun ide (Ljava/lang/String;)V @@ -885,7 +885,7 @@ public abstract interface class org/jetbrains/intellij/platform/gradle/extension public abstract class org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformPluginsExtension : org/gradle/api/plugins/ExtensionAware { public static final field Companion Lorg/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformPluginsExtension$Companion; - public fun (Lorg/gradle/api/artifacts/ConfigurationContainer;Lorg/gradle/api/artifacts/dsl/DependencyHandler;Lorg/gradle/api/file/ProjectLayout;Lorg/gradle/api/model/ObjectFactory;Lorg/gradle/api/provider/ProviderFactory;Lorg/gradle/api/resources/ResourceHandler;Ljava/nio/file/Path;)V + public fun (Lorg/gradle/api/artifacts/dsl/RepositoryHandler;Lorg/gradle/api/artifacts/ConfigurationContainer;Lorg/gradle/api/artifacts/dsl/DependencyHandler;Lorg/gradle/api/file/ProjectLayout;Lorg/gradle/api/model/ObjectFactory;Lorg/gradle/api/provider/ProviderFactory;Lorg/gradle/api/resources/ResourceHandler;Ljava/nio/file/Path;)V public final fun disablePlugin (Ljava/lang/String;)V public final fun disablePlugin (Lorg/gradle/api/provider/Provider;)V public final fun disablePlugins (Ljava/util/List;)V diff --git a/src/integrationTest/kotlin/org/jetbrains/intellij/platform/gradle/DependencyLockingAndVerificationIntegrationTest.kt b/src/integrationTest/kotlin/org/jetbrains/intellij/platform/gradle/DependencyLockingAndVerificationIntegrationTest.kt index 62c00269af..762bedc516 100644 --- a/src/integrationTest/kotlin/org/jetbrains/intellij/platform/gradle/DependencyLockingAndVerificationIntegrationTest.kt +++ b/src/integrationTest/kotlin/org/jetbrains/intellij/platform/gradle/DependencyLockingAndVerificationIntegrationTest.kt @@ -20,7 +20,6 @@ class DependencyLockingAndVerificationIntegrationTest : IntelliJPlatformIntegrat ) { @Test - @Ignore("https://github.com/JetBrains/intellij-platform-gradle-plugin/issues/1779") fun `build plugin with dependency locks & hash verification`() { build( Tasks.External.CLEAN, @@ -44,7 +43,6 @@ class DependencyLockingAndVerificationIntegrationTest : IntelliJPlatformIntegrat } @Test - @Ignore("https://github.com/JetBrains/intellij-platform-gradle-plugin/issues/1779") fun `build plugin with dependency locks, hash & signature verification`() { build( Tasks.External.CLEAN, diff --git a/src/integrationTest/kotlin/org/jetbrains/intellij/platform/gradle/DependencyVerificationIgnoreIntellijIntegrationTest.kt b/src/integrationTest/kotlin/org/jetbrains/intellij/platform/gradle/DependencyVerificationIgnoreIntellijIntegrationTest.kt index d6c5d3b613..0c8c37ba90 100644 --- a/src/integrationTest/kotlin/org/jetbrains/intellij/platform/gradle/DependencyVerificationIgnoreIntellijIntegrationTest.kt +++ b/src/integrationTest/kotlin/org/jetbrains/intellij/platform/gradle/DependencyVerificationIgnoreIntellijIntegrationTest.kt @@ -44,7 +44,6 @@ class DependencyVerificationIgnoreIntellijIntegrationTest : IntelliJPlatformInte } @Test - @Ignore("https://github.com/JetBrains/intellij-platform-gradle-plugin/issues/1779") fun `build plugin with dependency locks, hash & signature verification ignoring intellij artifacts`() { build( Tasks.External.CLEAN, diff --git a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/artifacts/transform/CollectorTransformer.kt b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/artifacts/transform/CollectorTransformer.kt index 86ced29783..085de2261d 100644 --- a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/artifacts/transform/CollectorTransformer.kt +++ b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/artifacts/transform/CollectorTransformer.kt @@ -87,11 +87,16 @@ abstract class CollectorTransformer : TransformAction { + // Normally we get into here for 3rd party marketplace plugins. + // https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-dependencies-extension.html#non-bundled-plugin + // For other plugins, we never (usually?) get into this block, because their Ivy artifacts already + // list jars, instead of pointing to a directory, see: + // See also org.jetbrains.intellij.platform.gradle.models.IvyModuleKt.explodeIntoIvyJarsArtifacts plugin.originalFile?.let { pluginPath -> - collectJars( - pluginPath.resolve("lib"), - pluginPath.resolve("lib/modules"), - ).forEach { outputs.file(it) } + val jars = collectJars(pluginPath) + jars.forEach { + outputs.file(it) + } } } @@ -102,11 +107,16 @@ abstract class CollectorTransformer : TransformAction { + val libPath = path.resolve("lib") + val libModulesPath = path.resolve("lib/modules") + + return listOf(libPath, libModulesPath) + .mapNotNull { it.takeIfExists() } + .flatMap { it.listDirectoryEntries("*.jar") } + } + internal fun register( dependencies: DependencyHandler, compileClasspathConfiguration: Configuration, diff --git a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformDependenciesExtension.kt b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformDependenciesExtension.kt index 093b109912..923248a8ab 100644 --- a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformDependenciesExtension.kt +++ b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformDependenciesExtension.kt @@ -7,6 +7,7 @@ import org.gradle.api.artifacts.ConfigurationContainer import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.ProjectDependency import org.gradle.api.artifacts.dsl.DependencyHandler +import org.gradle.api.artifacts.dsl.RepositoryHandler import org.gradle.api.file.Directory import org.gradle.api.file.ProjectLayout import org.gradle.api.model.ObjectFactory @@ -51,6 +52,7 @@ import kotlin.io.path.absolute @Suppress("unused", "MemberVisibilityCanBePrivate") @IntelliJPlatform abstract class IntelliJPlatformDependenciesExtension @Inject constructor( + repositories: RepositoryHandler, configurations: ConfigurationContainer, dependencies: DependencyHandler, layout: ProjectLayout, @@ -60,7 +62,7 @@ abstract class IntelliJPlatformDependenciesExtension @Inject constructor( rootProjectDirectory: Path, ) { - private val delegate = IntelliJPlatformDependenciesHelper(configurations, dependencies, layout, objects, providers, resources, rootProjectDirectory) + private val delegate = IntelliJPlatformDependenciesHelper(repositories, configurations, dependencies, layout, objects, providers, resources, rootProjectDirectory) /** * Adds a dependency on the IntelliJ Platform. @@ -1309,6 +1311,7 @@ abstract class IntelliJPlatformDependenciesExtension @Inject constructor( override fun register(project: Project, target: Any) = target.configureExtension( Extensions.INTELLIJ_PLATFORM, + project.repositories, project.configurations, project.dependencies, project.layout, diff --git a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformDependenciesHelper.kt b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformDependenciesHelper.kt index 95ede4e796..12a0a8173e 100644 --- a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformDependenciesHelper.kt +++ b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformDependenciesHelper.kt @@ -11,6 +11,7 @@ import org.gradle.api.artifacts.ConfigurationContainer import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.ProjectDependency import org.gradle.api.artifacts.dsl.DependencyHandler +import org.gradle.api.artifacts.dsl.RepositoryHandler import org.gradle.api.file.Directory import org.gradle.api.file.ProjectLayout import org.gradle.api.model.ObjectFactory @@ -40,7 +41,6 @@ import java.io.File import java.io.FileReader import java.nio.file.Path import java.util.* -import kotlin.Throws import kotlin.io.path.* /** @@ -55,6 +55,7 @@ import kotlin.io.path.* * @param rootProjectDirectory The root directory of the Gradle project. */ class IntelliJPlatformDependenciesHelper( + private val repositories: RepositoryHandler, private val configurations: ConfigurationContainer, private val dependencies: DependencyHandler, private val layout: ProjectLayout, @@ -742,6 +743,7 @@ class IntelliJPlatformDependenciesHelper( */ private fun DependencyHandler.createIntelliJPlatformBundledPlugin(id: String): Dependency { val plugin = bundledPlugins[id] + requireNotNull(plugin) { val unresolvedPluginId = when (id) { "copyright" -> "Use correct plugin ID 'com.intellij.copyright' instead of 'copyright'." @@ -769,8 +771,8 @@ class IntelliJPlatformDependenciesHelper( module = id, revision = version, ), - publications = listOf(artifactPath.toIvyArtifact()), - dependencies = plugin.collectDependencies(), + publications = artifactPath.toBundledPluginIvyArtifacts(), + dependencies = plugin.collectBundledPluginDependencies(), ) } @@ -791,7 +793,9 @@ class IntelliJPlatformDependenciesHelper( .find { layout -> layout.name == id } .let { requireNotNull(it) { "Specified bundledModule '$id' doesn't exist." } } val platformPath = platformPath.get() - val artifactPaths = bundledModule.classPath.map { path -> platformPath.resolve(path).toIvyArtifact() } + val artifactPaths = bundledModule.classPath.flatMap { path -> + platformPath.resolve(path).toBundledModuleIvyArtifacts() + } val version = baseVersion.orElse(productInfo.map { it.version }).get() writeIvyModule(Dependencies.BUNDLED_MODULE_GROUP, id, version) { @@ -818,7 +822,7 @@ class IntelliJPlatformDependenciesHelper( * * @param path IDs of already traversed plugins or modules. */ - private fun IdePlugin.collectDependencies(path: List = emptyList()): List { + private fun IdePlugin.collectBundledPluginDependencies(path: List = emptyList()): List { val id = requireNotNull(pluginId) val dependencyIds = (dependencies.map { it.id } + optionalDescriptors.map { it.dependency.id } + modulesDescriptors.map { it.name } - id).toSet() val buildNumber by lazy { productInfo.get().buildNumber } @@ -835,10 +839,10 @@ class IntelliJPlatformDependenciesHelper( writeIvyModule(group, name, version) { IvyModule( info = IvyModule.Info(group, name, version), - publications = listOf(artifactPath.toIvyArtifact()), + publications = artifactPath.toBundledPluginIvyArtifacts(), dependencies = when { id in path -> emptyList() - else -> plugin.collectDependencies(path + id) + else -> plugin.collectBundledPluginDependencies(path + id) }, ) } @@ -855,7 +859,9 @@ class IntelliJPlatformDependenciesHelper( .mapNotNull { layoutItems.find { layout -> layout.name == it } } .filterNot { it.classPath.isEmpty() } .map { - val artifactPaths = it.classPath.map { path -> platformPath.resolve(path).toIvyArtifact() } + val artifactPaths = it.classPath.flatMap { path -> + platformPath.resolve(path).toBundledModuleIvyArtifacts() + } val group = Dependencies.BUNDLED_MODULE_GROUP val name = it.name val version = buildNumber @@ -904,7 +910,7 @@ class IntelliJPlatformDependenciesHelper( module = name, revision = version, ), - publications = listOf(artifactPath.toIvyArtifact()), + publications = artifactPath.toLocalPluginIvyArtifacts(), ) } diff --git a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformExtension.kt b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformExtension.kt index ab5a9bf6ab..6ce554f923 100644 --- a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformExtension.kt +++ b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformExtension.kt @@ -8,6 +8,7 @@ import org.gradle.api.Action import org.gradle.api.Project import org.gradle.api.artifacts.ConfigurationContainer import org.gradle.api.artifacts.dsl.DependencyHandler +import org.gradle.api.artifacts.dsl.RepositoryHandler import org.gradle.api.file.Directory import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.ProjectLayout @@ -767,6 +768,7 @@ abstract class IntelliJPlatformExtension @Inject constructor( */ @IntelliJPlatform abstract class Ides @Inject constructor( + repositories: RepositoryHandler, configurations: ConfigurationContainer, dependencies: DependencyHandler, layout: ProjectLayout, @@ -778,6 +780,7 @@ abstract class IntelliJPlatformExtension @Inject constructor( ) { private val delegate = IntelliJPlatformDependenciesHelper( + repositories, configurations, dependencies, layout, @@ -950,6 +953,7 @@ abstract class IntelliJPlatformExtension @Inject constructor( override fun register(project: Project, target: Any) = target.configureExtension( Extensions.IDES, + project.repositories, project.configurations, project.dependencies, project.layout, diff --git a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformPluginsExtension.kt b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformPluginsExtension.kt index 0a227036ef..0babaa90bd 100644 --- a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformPluginsExtension.kt +++ b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformPluginsExtension.kt @@ -6,6 +6,7 @@ import org.gradle.api.Project import org.gradle.api.artifacts.ConfigurationContainer import org.gradle.api.artifacts.ProjectDependency import org.gradle.api.artifacts.dsl.DependencyHandler +import org.gradle.api.artifacts.dsl.RepositoryHandler import org.gradle.api.file.Directory import org.gradle.api.file.ProjectLayout import org.gradle.api.model.ObjectFactory @@ -32,6 +33,7 @@ import javax.inject.Inject */ @IntelliJPlatform abstract class IntelliJPlatformPluginsExtension @Inject constructor( + repositories: RepositoryHandler, configurations: ConfigurationContainer, dependencies: DependencyHandler, layout: ProjectLayout, @@ -45,6 +47,7 @@ abstract class IntelliJPlatformPluginsExtension @Inject constructor( internal val intellijPlatformPluginLocalConfigurationName = objects.property() private val delegate = IntelliJPlatformDependenciesHelper( + repositories, configurations, dependencies, layout, @@ -242,6 +245,7 @@ abstract class IntelliJPlatformPluginsExtension @Inject constructor( override fun register(project: Project, target: Any) = target.configureExtension( Extensions.PLUGINS, + project.repositories, project.configurations, project.dependencies, project.layout, diff --git a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformRepositoriesHelper.kt b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformRepositoriesHelper.kt index 8477c2d453..449109a08c 100644 --- a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformRepositoriesHelper.kt +++ b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformRepositoriesHelper.kt @@ -149,8 +149,18 @@ class IntelliJPlatformRepositoriesHelper( val localPlatformArtifactsPath = providers.localPlatformArtifactsPath(rootProjectDirectory) ivyPattern("${localPlatformArtifactsPath.pathString}/[organization]-[module]-[revision].[ext]") - // As all artifacts defined in Ivy repositories have a full artifact path set as their names, we can use them to locate artifact files - artifactPattern("/[artifact]") + // "type" may contain an optional absolute path to the actual location of the artifact, in different situations + // it may point to a completely unrelated locations on the file system. + // Yes, attribute named types does not fit very good for storing a path, but there is no better alternative. + // "artifact" maps to IvyModule#name here we expect to have the second part of the path. + // "ext" is file extension, e.g. "jar" or "directory", not used here. + // + // The type can be empty fur the "artifact" (IvyModule#name) can not. + // + // It has to be prefixed by "/" because: If this pattern is not a fully-qualified URL, it will be interpreted + // as a file relative to the project directory. + val pattern = "/([type])[artifact]" + artifactPattern(pattern) /** * Because artifact paths always start with `/` (see [toPublication] for details), @@ -159,7 +169,7 @@ class IntelliJPlatformRepositoriesHelper( * starting with `c` for the sake of micro-optimization. */ if (OperatingSystem.current().isWindows) { - (('c'..'z') + 'a' + 'b').forEach { artifactPattern("$it:/[artifact]") } + (('c'..'z') + 'a' + 'b').forEach { artifactPattern("$it:$pattern") } } }.apply { content { diff --git a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/models/IvyModule.kt b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/models/IvyModule.kt index 9eedc342a3..f41a09871e 100644 --- a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/models/IvyModule.kt +++ b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/models/IvyModule.kt @@ -6,11 +6,11 @@ import kotlinx.serialization.Serializable import nl.adaptivity.xmlutil.serialization.XmlChildrenName import nl.adaptivity.xmlutil.serialization.XmlElement import nl.adaptivity.xmlutil.serialization.XmlSerialName +import org.jetbrains.intellij.platform.gradle.artifacts.transform.CollectorTransformer import org.jetbrains.intellij.platform.gradle.extensions.IntelliJPlatformRepositoriesExtension +import org.jetbrains.kotlin.util.suffixIfNot import java.nio.file.Path -import kotlin.io.path.extension -import kotlin.io.path.invariantSeparatorsPathString -import kotlin.io.path.isDirectory +import kotlin.io.path.* @Serializable @XmlSerialName("ivy-module") @@ -42,6 +42,9 @@ data class IvyModule( val type: String? = null, val ext: String? = null, val conf: String? = "default", + // Does not seem to be supported by Gradle: + // https://ant.apache.org/ivy/history/2.4.0/ivyfile/artifact.html + // https://docs.gradle.org/current/javadoc/org/gradle/api/publish/ivy/IvyArtifact.html val url: String? = null, val packaging: String? = null, ) @@ -75,6 +78,15 @@ data class IvyModule( * * * ``` + * Or + * ```XML + * + * ... + * + * + * + * + * ``` * * The artifact name is an actual path of the file or directory, later used to locate all entries belonging to the current local dependency. * Note that the path is built using [invariantSeparatorsPathString] due to path separator differences on Windows. @@ -88,10 +100,79 @@ data class IvyModule( * * @see IntelliJPlatformRepositoriesExtension.jetbrainsIdeInstallers */ -internal fun Path.toIvyArtifact() = IvyModule.Artifact( - name = invariantSeparatorsPathString.replaceFirst(Regex("^[a-zA-Z]:/"), "/"), - type = when { - isDirectory() -> "directory" - else -> extension - }, -) +internal fun Path.toIvyArtifact(): IvyModule.Artifact { + val alwaysEmpty = "" + val trimmedAbsolutePath = this.removeLeadingPathSeparator() + val ext = when { + this.isDirectory() -> "directory" + else -> this.extension + } + + // In this case, name is the full path, so type is not needed. It can be empty because in: + // IntelliJPlatformRepositoriesHelper.createLocalIvyRepository the pattern is "/([type])[artifact]" where the type + // is marked as optional. + // In this case, it should be ok to have an absolute path in the name, because this is a fallback method, + // which called for directories only (at least at the moment of writing this). + // See comments in explodeIntoIvyJarsArtifacts on why having abs path in name may be bad. + return IvyModule.Artifact(type = alwaysEmpty, name = trimmedAbsolutePath, ext = ext) +} + +internal fun Path.toBundledPluginIvyArtifacts(): List { + return this.explodeIntoIvyJarsArtifacts() +} + +internal fun Path.toBundledModuleIvyArtifacts(): List { + return this.explodeIntoIvyJarsArtifacts() +} + +internal fun Path.toLocalPluginIvyArtifacts(): List { + return this.explodeIntoIvyJarsArtifacts() +} + +/** This method is a bit too universal. It could be split into 3 separate with unnecessary logic removed. */ +private fun Path.explodeIntoIvyJarsArtifacts(): List { + if (this.isDirectory()) { + val jars: List = CollectorTransformer.collectJars(this) + return jars.map { it: Path -> + val containingDirTrimmedAbsPath = it.containingDirTrimmedAbsPath() + val fileNameWithExt = it.fileName.toString() + val ext = it.extension + + // E.g. + // + // The reason why we put tha absolute path into type is that the name should not have it, because artifact name + // may come up in files like Gradle's verification-metadata.xml + // https://docs.gradle.org/current/userguide/dependency_verification.html + // Which will make them not portable between different environments. + IvyModule.Artifact(type = containingDirTrimmedAbsPath, name = fileNameWithExt, ext = ext) + } + } else { + val containingDirTrimmedAbsPath = this.containingDirTrimmedAbsPath() + val fileNameWithExt = this.fileName.toString() + val ext = this.extension + + return listOf(IvyModule.Artifact(type = containingDirTrimmedAbsPath, name = fileNameWithExt, ext = ext)) + } +} + +private fun Path.containingDirTrimmedAbsPath(): String { + return this.removeLeadingPathSeparator().substringBeforeLast("/").suffixIfNot("/") +} + +/** + * For explanation on why we need to remove the leading separator, see + * [org.jetbrains.intellij.platform.gradle.extensions.IntelliJPlatformRepositoriesHelper.createLocalIvyRepository] + * + * There the artifact pattern has a slash in the beginning, so we have to remove it here. + */ +private fun Path.removeLeadingPathSeparator(): String { + return this.normalizeWindowsPath().removePrefix("/") +} + +private fun Path.normalizeWindowsPath(): String { + return this.invariantSeparatorsPathString.replaceFirst(Regex("^[a-zA-Z]:/"), "/") +}