From df30f2bb125d24a3a4f5556d2437df4bd4ac831a Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Fri, 19 Jun 2026 16:20:36 +0200 Subject: [PATCH] [devkit] IJPL-246844 Attach IntelliJ SDK sources automatically on Gradle sync Add AuxiliaryArtifactProvider allowing for contributing additional domain-specific artifact coordinations to be resolved by Gradle. (cherry picked from commit a3abc045e14a4b5fe8d7c3a43047e539e27382eb) IJ-MR-210892 GitOrigin-RevId: eee9d1adb0c95000b643ad216c0c88e53092d882 --- .../GradleDependencyResolver.java | 26 ++++++++++++++++- .../auxiliary/AuxiliaryArtifactProvider.java | 28 +++++++++++++++++++ .../AuxiliaryConfigurationArtifacts.java | 24 +++++++++++++++- 3 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 plugins/gradle/tooling-extension-impl/src/com/intellij/gradle/toolingExtension/impl/model/dependencyModel/auxiliary/AuxiliaryArtifactProvider.java diff --git a/plugins/gradle/tooling-extension-impl/src/com/intellij/gradle/toolingExtension/impl/model/dependencyModel/GradleDependencyResolver.java b/plugins/gradle/tooling-extension-impl/src/com/intellij/gradle/toolingExtension/impl/model/dependencyModel/GradleDependencyResolver.java index 5b615537173a..426918f9731c 100644 --- a/plugins/gradle/tooling-extension-impl/src/com/intellij/gradle/toolingExtension/impl/model/dependencyModel/GradleDependencyResolver.java +++ b/plugins/gradle/tooling-extension-impl/src/com/intellij/gradle/toolingExtension/impl/model/dependencyModel/GradleDependencyResolver.java @@ -1,8 +1,9 @@ -// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.gradle.toolingExtension.impl.model.dependencyModel; import com.intellij.gradle.toolingExtension.impl.model.dependencyDownloadPolicyModel.GradleDependencyDownloadPolicy; import com.intellij.gradle.toolingExtension.impl.model.dependencyDownloadPolicyModel.GradleDependencyDownloadPolicyCache; +import com.intellij.gradle.toolingExtension.impl.model.dependencyModel.auxiliary.AuxiliaryArtifactProvider; import com.intellij.gradle.toolingExtension.impl.model.dependencyModel.auxiliary.AuxiliaryArtifactResolver; import com.intellij.gradle.toolingExtension.impl.model.dependencyModel.auxiliary.AuxiliaryArtifactResolverImpl; import com.intellij.gradle.toolingExtension.impl.model.dependencyModel.auxiliary.AuxiliaryConfigurationArtifacts; @@ -58,6 +59,7 @@ import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; +import java.util.ServiceLoader; import java.util.Set; import java.util.function.Predicate; import java.util.regex.Pattern; @@ -186,6 +188,7 @@ public final class GradleDependencyResolver { // Here we collect java doc and source files for a given dependencies AuxiliaryConfigurationArtifacts auxiliaryArtifacts = getAuxiliaryArtifactResolver(resolvedArtifacts, allowedDependencyGroups) .resolve(configuration); + auxiliaryArtifacts = resolveSupplementaryArtifacts(configuration, auxiliaryArtifacts); Set resolvedFiles = new HashSet<>(); Collection artifactDependencies = resolveArtifactDependencies( resolvedFiles, resolvedArtifacts, auxiliaryArtifacts, transformedProjectDependenciesResultMap @@ -509,6 +512,27 @@ public final class GradleDependencyResolver { } } + private @NotNull AuxiliaryConfigurationArtifacts resolveSupplementaryArtifacts( + @NotNull Configuration configuration, + @NotNull AuxiliaryConfigurationArtifacts primaryArtifacts + ) { + if (!myDownloadPolicy.isDownloadSources() && !myDownloadPolicy.isDownloadJavadoc()) { + return primaryArtifacts; + } + ServiceLoader providers = ServiceLoader.load( + AuxiliaryArtifactProvider.class, AuxiliaryArtifactProvider.class.getClassLoader()); + for (AuxiliaryArtifactProvider provider : providers) { + try { + AuxiliaryConfigurationArtifacts additional = provider.resolve(myProject, configuration, myDownloadPolicy); + primaryArtifacts = primaryArtifacts.mergeWith(additional); + } + catch (Exception ignore) { + // supplementary providers should not break the main resolution + } + } + return primaryArtifacts; + } + private static boolean isIvyRepositoryUsed(@NotNull Project project) { for (ArtifactRepository repository : project.getRepositories()) { if (repository instanceof IvyArtifactRepository) { diff --git a/plugins/gradle/tooling-extension-impl/src/com/intellij/gradle/toolingExtension/impl/model/dependencyModel/auxiliary/AuxiliaryArtifactProvider.java b/plugins/gradle/tooling-extension-impl/src/com/intellij/gradle/toolingExtension/impl/model/dependencyModel/auxiliary/AuxiliaryArtifactProvider.java new file mode 100644 index 000000000000..2f7f31dc9770 --- /dev/null +++ b/plugins/gradle/tooling-extension-impl/src/com/intellij/gradle/toolingExtension/impl/model/dependencyModel/auxiliary/AuxiliaryArtifactProvider.java @@ -0,0 +1,28 @@ +// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.gradle.toolingExtension.impl.model.dependencyModel.auxiliary; + +import com.intellij.gradle.toolingExtension.impl.model.dependencyDownloadPolicyModel.GradleDependencyDownloadPolicy; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * Service provider interface for resolving additional source and Javadoc artifacts + * that the standard {@link AuxiliaryArtifactResolver} cannot find. + *

+ * This is useful for dependencies whose source artifacts are published under + * different Maven coordinates than the main artifact (e.g., IntelliJ Platform dependencies). + *

+ * Implementations are discovered via {@link java.util.ServiceLoader}. + */ +@ApiStatus.Internal +public interface AuxiliaryArtifactProvider { + + @NotNull + AuxiliaryConfigurationArtifacts resolve( + @NotNull Project project, + @NotNull Configuration configuration, + @NotNull GradleDependencyDownloadPolicy policy + ); +} diff --git a/plugins/gradle/tooling-extension-impl/src/com/intellij/gradle/toolingExtension/impl/model/dependencyModel/auxiliary/AuxiliaryConfigurationArtifacts.java b/plugins/gradle/tooling-extension-impl/src/com/intellij/gradle/toolingExtension/impl/model/dependencyModel/auxiliary/AuxiliaryConfigurationArtifacts.java index 97a63c609feb..b506f8365bd2 100644 --- a/plugins/gradle/tooling-extension-impl/src/com/intellij/gradle/toolingExtension/impl/model/dependencyModel/auxiliary/AuxiliaryConfigurationArtifacts.java +++ b/plugins/gradle/tooling-extension-impl/src/com/intellij/gradle/toolingExtension/impl/model/dependencyModel/auxiliary/AuxiliaryConfigurationArtifacts.java @@ -1,4 +1,4 @@ -// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.gradle.toolingExtension.impl.model.dependencyModel.auxiliary; import org.gradle.api.artifacts.component.ComponentIdentifier; @@ -9,7 +9,9 @@ import org.jetbrains.annotations.VisibleForTesting; import org.jetbrains.plugins.gradle.tooling.util.StringUtils; import java.io.File; +import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; @@ -20,6 +22,9 @@ import java.util.regex.Pattern; @ApiStatus.Internal public class AuxiliaryConfigurationArtifacts { + public static final AuxiliaryConfigurationArtifacts EMPTY = + new AuxiliaryConfigurationArtifacts(Collections.emptyMap(), Collections.emptyMap()); + private static final Pattern PUNCTUATION_IN_SUFFIX_PATTERN = Pattern.compile("[\\p{Punct}\\s]+$"); private final @NotNull Map> sources; @@ -117,4 +122,21 @@ public class AuxiliaryConfigurationArtifacts { return shortlistedAuxiliariesBySuffixlessName.firstEntry().getValue(); } + + /** + * Merges this instance with additional artifacts. Primary (this) results take precedence. + */ + public @NotNull AuxiliaryConfigurationArtifacts mergeWith(@NotNull AuxiliaryConfigurationArtifacts other) { + if (other == EMPTY) return this; + if (this == EMPTY) return other; + Map> mergedSources = new HashMap<>(this.sources); + for (Map.Entry> entry : other.sources.entrySet()) { + mergedSources.putIfAbsent(entry.getKey(), entry.getValue()); + } + Map> mergedJavadocs = new HashMap<>(this.javadocs); + for (Map.Entry> entry : other.javadocs.entrySet()) { + mergedJavadocs.putIfAbsent(entry.getKey(), entry.getValue()); + } + return new AuxiliaryConfigurationArtifacts(mergedSources, mergedJavadocs); + } }