[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
This commit is contained in:
Karol Lewandowski
2026-08-03 12:33:13 +00:00
committed by intellij-monorepo-bot
parent f34fe6365d
commit df30f2bb12
3 changed files with 76 additions and 2 deletions
@@ -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<String> resolvedFiles = new HashSet<>();
Collection<ExternalDependency> 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<AuxiliaryArtifactProvider> 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) {
@@ -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.
* <p>
* This is useful for dependencies whose source artifacts are published under
* different Maven coordinates than the main artifact (e.g., IntelliJ Platform dependencies).
* <p>
* 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
);
}
@@ -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<ComponentIdentifier, Set<File>> 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<ComponentIdentifier, Set<File>> mergedSources = new HashMap<>(this.sources);
for (Map.Entry<ComponentIdentifier, Set<File>> entry : other.sources.entrySet()) {
mergedSources.putIfAbsent(entry.getKey(), entry.getValue());
}
Map<ComponentIdentifier, Set<File>> mergedJavadocs = new HashMap<>(this.javadocs);
for (Map.Entry<ComponentIdentifier, Set<File>> entry : other.javadocs.entrySet()) {
mergedJavadocs.putIfAbsent(entry.getKey(), entry.getValue());
}
return new AuxiliaryConfigurationArtifacts(mergedSources, mergedJavadocs);
}
}