[maven] [IDEA-342187] retrieve dependency management data into MavenProject during resolve

(cherry picked from commit 19b968000c9f72b1dde626e2699b8e62c6624b08)

IJ-CR-152420

GitOrigin-RevId: 49432cd7619ad15bbd9a3b62befcc261401cc9bc
This commit is contained in:
Alexander Bubenchikov
2024-10-21 16:42:05 +02:00
committed by intellij-monorepo-bot
parent c786682e6c
commit 7d3ee50fcc
7 changed files with 211 additions and 43 deletions

View File

@@ -22,14 +22,12 @@ import org.jetbrains.idea.maven.model.MavenModel;
import org.jetbrains.idea.maven.model.MavenProjectProblem;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.*;
public class MavenServerExecutionResult implements Serializable {
public static final MavenServerExecutionResult EMPTY = new MavenServerExecutionResult(null, Collections.emptyList(), Collections.emptySet());
public static final MavenServerExecutionResult EMPTY =
new MavenServerExecutionResult(null, Collections.emptyList(), Collections.emptySet());
@Nullable public final ProjectData projectData;
@NotNull public final Collection<MavenProjectProblem> problems;
@@ -55,17 +53,21 @@ public class MavenServerExecutionResult implements Serializable {
public static class ProjectData implements Serializable {
@NotNull
public final MavenModel mavenModel;
@NotNull
public final List<MavenId> managedDependencies;
public final String dependencyHash;
public final boolean dependencyResolutionSkipped;
public final Map<String, String> mavenModelMap;
public final Collection<String> activatedProfiles;
public ProjectData(@NotNull MavenModel mavenModel,
@NotNull List<MavenId> managedDependencies,
@Nullable String dependencyHash,
boolean dependencyResolutionSkipped,
Map<String, String> mavenModelMap,
Collection<String> activatedProfiles) {
this.mavenModel = mavenModel;
this.managedDependencies = managedDependencies;
this.dependencyHash = dependencyHash;
this.dependencyResolutionSkipped = dependencyResolutionSkipped;
this.mavenModelMap = mavenModelMap;

View File

@@ -37,6 +37,7 @@ import org.jetbrains.idea.maven.server.embedder.CustomMaven3ModelInterpolator2;
import java.io.File;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import static org.jetbrains.idea.maven.server.MavenServerEmbedder.MAVEN_EMBEDDER_VERSION;
@@ -199,7 +200,8 @@ public class Maven3XProjectResolver {
if (myLongRunningTask.isCanceled()) return MavenServerExecutionResult.EMPTY;
MavenServerExecutionResult result = myTelemetry.callWithSpan(
"resolveBuildingResult " + br.projectId, () ->
resolveBuildingResult(repositorySession, addUnresolved, br.mavenProject, br.modelProblems, br.exceptions, br.dependencyHash));
resolveBuildingResult(repositorySession, addUnresolved, br.mavenProject, br.modelProblems, br.exceptions,
br.dependencyHash));
myLongRunningTask.incrementFinishedRequests();
return result;
}
@@ -213,7 +215,9 @@ public class Maven3XProjectResolver {
return executionResults;
}
private boolean transitiveDependenciesChanged(@NotNull File pomFile, String newDependencyHash, Map<File, String> fileToNewDependencyHash) {
private boolean transitiveDependenciesChanged(@NotNull File pomFile,
String newDependencyHash,
Map<File, String> fileToNewDependencyHash) {
if (dependenciesChanged(pomFile, newDependencyHash)) return true;
for (File dependencyPomFile : myPomHashMap.getFileDependencies(pomFile)) {
if (dependenciesChanged(dependencyPomFile, fileToNewDependencyHash.get(dependencyPomFile))) return true;
@@ -345,12 +349,27 @@ public class Maven3XProjectResolver {
Map<String, String> mavenModelMap = Maven3ModelConverter.convertToMap(mavenProject.getModel());
MavenServerExecutionResult.ProjectData data =
new MavenServerExecutionResult.ProjectData(model, dependencyHash, dependencyResolutionSkipped, mavenModelMap, activatedProfiles);
new MavenServerExecutionResult.ProjectData(model, getManagedDependencies(mavenProject), dependencyHash, dependencyResolutionSkipped,
mavenModelMap, activatedProfiles);
Collection<MavenProjectProblem> unresolvedProblems = new HashSet<>();
collectUnresolvedArtifactProblems(file, dependencyResolutionResult, unresolvedProblems);
return new MavenServerExecutionResult(data, problems, Collections.emptySet(), unresolvedProblems);
}
@NotNull
private static List<MavenId> getManagedDependencies(@Nullable MavenProject project) {
if (project == null ||
project.getDependencyManagement() == null ||
project.getDependencyManagement().getDependencies() == null) {
return Collections.emptyList();
}
//noinspection SSBasedInspection
return project.getDependencyManagement().getDependencies().stream().map(
dep -> new MavenId(dep.getGroupId(), dep.getArtifactId(), dep.getVersion())
).collect(Collectors.toList());
}
private static void fillSessionCache(MavenSession mavenSession,
RepositorySystemSession session,
List<ProjectBuildingResult> buildingResults) {

View File

@@ -32,6 +32,7 @@ import org.jetbrains.idea.maven.server.PomHashMap;
import java.io.File;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
public class Maven40ProjectResolver {
@NotNull private final Maven40ServerEmbedderImpl myEmbedder;
@@ -194,7 +195,9 @@ public class Maven40ProjectResolver {
return executionResults;
}
private boolean transitiveDependenciesChanged(@NotNull File pomFile, String newDependencyHash, Map<File, String> fileToNewDependencyHash) {
private boolean transitiveDependenciesChanged(@NotNull File pomFile,
String newDependencyHash,
Map<File, String> fileToNewDependencyHash) {
if (dependenciesChanged(pomFile, newDependencyHash)) return true;
for (File dependencyPomFile : myPomHashMap.getFileDependencies(pomFile)) {
if (dependenciesChanged(dependencyPomFile, fileToNewDependencyHash.get(dependencyPomFile))) return true;
@@ -288,7 +291,8 @@ public class Maven40ProjectResolver {
@NotNull
private MavenServerExecutionResult createExecutionResult(@NotNull MavenProject mavenProject, String dependencyHash) {
return createExecutionResult(mavenProject.getFile(), Collections.emptyList(), Collections.emptyList(), mavenProject, null, dependencyHash, true);
return createExecutionResult(mavenProject.getFile(), Collections.emptyList(), Collections.emptyList(), mavenProject, null,
dependencyHash, true);
}
@NotNull
@@ -357,13 +361,28 @@ public class Maven40ProjectResolver {
Map<String, String> mavenModelMap = Maven40ModelConverter.convertToMap(mavenProject.getModel());
MavenServerExecutionResult.ProjectData data =
new MavenServerExecutionResult.ProjectData(model, dependencyHash, dependencyResolutionSkipped, mavenModelMap, activatedProfiles);
new MavenServerExecutionResult.ProjectData(model, getManagedDependencies(mavenProject), dependencyHash, dependencyResolutionSkipped,
mavenModelMap, activatedProfiles);
if (null == model.getBuild() || null == model.getBuild().getDirectory()) {
data = null;
}
return new MavenServerExecutionResult(data, problems, Collections.emptySet(), unresolvedProblems);
}
@NotNull
private static List<MavenId> getManagedDependencies(@Nullable MavenProject project) {
if (project == null ||
project.getDependencyManagement() == null ||
project.getDependencyManagement().getDependencies() == null) {
return Collections.emptyList();
}
//noinspection SSBasedInspection
return project.getDependencyManagement().getDependencies().stream().map(
dep -> new MavenId(dep.getGroupId(), dep.getArtifactId(), dep.getVersion())
).collect(Collectors.toList());
}
private void collectUnresolvedArtifactProblems(@Nullable File file,
@Nullable DependencyResolutionResult result,

View File

@@ -35,6 +35,7 @@ import java.io.*
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import java.util.function.Predicate
import kotlin.Throws
class MavenProject(val file: VirtualFile) {
enum class ConfigFileKind(val myRelativeFilePath: String, val myValueIfMissing: String) {
@@ -79,6 +80,7 @@ class MavenProject(val file: VirtualFile) {
myState,
true,
readerResult.mavenModel,
emptyList(),
readerResult.readingProblems,
readerResult.activatedProfiles,
setOf(),
@@ -98,6 +100,7 @@ class MavenProject(val file: VirtualFile) {
@Internal
fun updateState(
model: MavenModel,
managedDependencies: List<MavenId>,
dependencyHash: String?,
readingProblems: Collection<MavenProjectProblem>,
activatedProfiles: MavenExplicitProfiles,
@@ -111,6 +114,7 @@ class MavenProject(val file: VirtualFile) {
myState,
false,
model,
managedDependencies,
readingProblems,
activatedProfiles,
unresolvedArtifactIds,
@@ -139,7 +143,7 @@ class MavenProject(val file: VirtualFile) {
@Internal
fun updateState(readingProblems: Collection<MavenProjectProblem>): MavenProjectChanges {
val newState= myState.copy(readingProblems = readingProblems)
val newState = myState.copy(readingProblems = readingProblems)
return setState(newState)
}
@@ -714,6 +718,8 @@ class MavenProject(val file: VirtualFile) {
setState(newState)
}
fun findManagedDependency(groupId: String, artifactId: String): MavenId? = myState.managedDependencies["$groupId:$artifactId"]
fun findDependencies(depProject: MavenProject): List<MavenArtifact> {
return findDependencies(depProject.mavenId)
}
@@ -856,10 +862,10 @@ class MavenProject(val file: VirtualFile) {
if (configs.size == 1) return getCompilerLevel(level, configs[0])
return configs
.mapNotNull { findChildValueByPath(it, level) }
.map { LanguageLevel.parse(it) ?: LanguageLevel.HIGHEST }
.maxWithOrNull(Comparator.naturalOrder())
?.toJavaVersion()?.toFeatureString() ?: myState.properties!!.getProperty("maven.compiler.$level")
.mapNotNull { findChildValueByPath(it, level) }
.map { LanguageLevel.parse(it) ?: LanguageLevel.HIGHEST }
.maxWithOrNull(Comparator.naturalOrder())
?.toJavaVersion()?.toFeatureString() ?: myState.properties!!.getProperty("maven.compiler.$level")
}
private fun getCompilerLevel(level: String, config: Element): String? {
@@ -1104,6 +1110,7 @@ class MavenProject(val file: VirtualFile) {
state: MavenProjectState,
incLastReadStamp: Boolean,
model: MavenModel,
managedDependencies: List<MavenId>,
readingProblems: Collection<MavenProjectProblem>,
activatedProfiles: MavenExplicitProfiles,
unresolvedArtifactIds: Set<MavenId>,
@@ -1126,6 +1133,7 @@ class MavenProject(val file: VirtualFile) {
val newPluginInfos = LinkedHashSet<MavenPluginInfo>()
val newExtensions = LinkedHashSet<MavenArtifact>()
val newAnnotationProcessors = LinkedHashSet<MavenArtifact>()
val newManagedDeps = LinkedHashMap<String, MavenId>()
if (keepPreviousArtifacts) {
newUnresolvedArtifacts.addAll(state.unresolvedArtifactIds)
@@ -1135,6 +1143,7 @@ class MavenProject(val file: VirtualFile) {
newDependencyTree.addAll(state.dependencyTree)
newExtensions.addAll(state.extensions)
newAnnotationProcessors.addAll(state.annotationProcessors)
newManagedDeps.putAll(state.managedDependencies)
}
// either keep all previous plugins or only those that are present in the new list
@@ -1154,6 +1163,7 @@ class MavenProject(val file: VirtualFile) {
newDependencyTree.addAll(model.dependencyTree)
newDependencies.addAll(model.dependencies)
newExtensions.addAll(model.extensions)
managedDependencies.forEach { md -> newManagedDeps.put("${md.groupId}:${md.artifactId}", md) }
val remoteRepositories = ArrayList(newRepositories)
val remotePluginRepositories = ArrayList(newPluginRepositories)
@@ -1162,6 +1172,7 @@ class MavenProject(val file: VirtualFile) {
val pluginInfos = ArrayList(newPluginInfos)
val extensions = ArrayList(newExtensions)
val annotationProcessors = ArrayList(newAnnotationProcessors)
val managedDependenciesMap = LinkedHashMap(newManagedDeps)
val newDependencyHash = dependencyHash ?: state.dependencyHash
val lastReadStamp = state.lastReadStamp + if (incLastReadStamp) 1 else 0
@@ -1199,6 +1210,7 @@ class MavenProject(val file: VirtualFile) {
pluginInfos = pluginInfos,
extensions = extensions,
annotationProcessors = annotationProcessors,
managedDependencies = managedDependenciesMap,
dependencyHash = newDependencyHash,
)
}

View File

@@ -38,14 +38,17 @@ import java.util.concurrent.ConcurrentLinkedQueue
data class MavenProjectResolutionResult(val mavenProjectMap: Map<String, Collection<MavenProject>>)
@ApiStatus.Internal
class MavenProjectResolverResult(@JvmField val mavenModel: MavenModel,
@JvmField val dependencyHash: String?,
@JvmField val dependencyResolutionSkipped: Boolean,
@JvmField val nativeModelMap: Map<String, String>,
@JvmField val activatedProfiles: MavenExplicitProfiles,
@JvmField val readingProblems: MutableCollection<MavenProjectProblem>,
@JvmField val unresolvedArtifactIds: MutableSet<MavenId>,
val unresolvedProblems: Collection<MavenProjectProblem>)
class MavenProjectResolverResult(
@JvmField val mavenModel: MavenModel,
@JvmField val managedDependencies: List<MavenId>,
@JvmField val dependencyHash: String?,
@JvmField val dependencyResolutionSkipped: Boolean,
@JvmField val nativeModelMap: Map<String, String>,
@JvmField val activatedProfiles: MavenExplicitProfiles,
@JvmField val readingProblems: MutableCollection<MavenProjectProblem>,
@JvmField val unresolvedArtifactIds: MutableSet<MavenId>,
val unresolvedProblems: Collection<MavenProjectProblem>,
)
@ApiStatus.Internal
interface MavenProjectResolutionContributor {
@@ -73,14 +76,16 @@ interface MavenProjectResolutionContributor {
@ApiStatus.Internal
class MavenProjectResolver(private val myProject: Project) {
suspend fun resolve(incrementally: Boolean,
mavenProjects: Collection<MavenProject>,
tree: MavenProjectsTree,
workspaceMap: MavenWorkspaceMap,
generalSettings: MavenGeneralSettings,
embeddersManager: MavenEmbeddersManager,
progressReporter: RawProgressReporter,
eventHandler: MavenEventHandler): MavenProjectResolutionResult {
suspend fun resolve(
incrementally: Boolean,
mavenProjects: Collection<MavenProject>,
tree: MavenProjectsTree,
workspaceMap: MavenWorkspaceMap,
generalSettings: MavenGeneralSettings,
embeddersManager: MavenEmbeddersManager,
progressReporter: RawProgressReporter,
eventHandler: MavenEventHandler,
): MavenProjectResolutionResult {
val updateSnapshots = MavenProjectsManager.getInstance(myProject).forceUpdateSnapshots || generalSettings.isAlwaysUpdateSnapshots
val projectsWithUnresolvedPlugins = HashMap<String, Collection<MavenProject>>()
val projectMultiMap = MavenUtil.groupByBasedir(mavenProjects, tree)
@@ -324,7 +329,7 @@ class MavenProjectResolver(private val myProject: Project) {
eventHandler: MavenEventHandler,
workspaceMap: MavenWorkspaceMap?,
updateSnapshots: Boolean,
userProperties: Properties
userProperties: Properties,
): Pair<Collection<MavenProjectResolverResult>, Collection<MavenProjectProblem>> {
val files = pomToDependencyHash.keys
val resolverResults: MutableCollection<MavenProjectResolverResult> = ArrayList()
@@ -353,6 +358,7 @@ class MavenProjectResolver(private val myProject: Project) {
else {
resolverResults.add(MavenProjectResolverResult(
projectData.mavenModel,
projectData.managedDependencies,
projectData.dependencyHash,
projectData.dependencyResolutionSkipped,
projectData.mavenModelMap,
@@ -382,12 +388,14 @@ class MavenProjectResolver(private val myProject: Project) {
return null
}
private suspend fun collectProjectWithUnresolvedPlugins(result: MavenProjectResolverResult,
artifactIdToMavenProjects: Map<String, List<MavenProject>>,
generalSettings: MavenGeneralSettings,
embedder: MavenEmbedderWrapper,
tree: MavenProjectsTree,
projectsWithUnresolvedPlugins: ConcurrentLinkedQueue<MavenProject>) {
private suspend fun collectProjectWithUnresolvedPlugins(
result: MavenProjectResolverResult,
artifactIdToMavenProjects: Map<String, List<MavenProject>>,
generalSettings: MavenGeneralSettings,
embedder: MavenEmbedderWrapper,
tree: MavenProjectsTree,
projectsWithUnresolvedPlugins: ConcurrentLinkedQueue<MavenProject>,
) {
val mavenId = result.mavenModel.mavenId
val artifactId = mavenId.artifactId
val mavenProjects = artifactIdToMavenProjects[artifactId]
@@ -414,13 +422,14 @@ class MavenProjectResolver(private val myProject: Project) {
val keepPreviousArtifacts = keepPreviousResolutionResults || result.dependencyResolutionSkipped
MavenLog.LOG.debug(
"Project resolution: updating maven project $mavenProjectCandidate, keepPreviousArtifacts=$keepPreviousArtifacts, dependencies: ${result.mavenModel.dependencies.size}")
"Project resolution: updating maven project $mavenProjectCandidate, keepPreviousArtifacts=$keepPreviousArtifacts, dependencies: ${result.mavenModel.dependencies.size}, managedDeps: ${result.managedDependencies.size}")
MavenServerResultTransformer.getInstance(myProject)
.transform(result.mavenModel, mavenProjectCandidate.file)
mavenProjectCandidate.updateState(
result.mavenModel,
result.managedDependencies,
result.dependencyHash,
result.readingProblems,
result.activatedProfiles,
@@ -459,9 +468,11 @@ class MavenProjectResolver(private val myProject: Project) {
@ApiStatus.ScheduledForRemoval
@Deprecated("Use {@link #resolveProject()}")
internal fun resolveProjectSync(embedder: MavenEmbedderWrapper,
files: Collection<VirtualFile>,
explicitProfiles: MavenExplicitProfiles): Collection<MavenProjectResolverResult> {
internal fun resolveProjectSync(
embedder: MavenEmbedderWrapper,
files: Collection<VirtualFile>,
explicitProfiles: MavenExplicitProfiles,
): Collection<MavenProjectResolverResult> {
return runBlockingMaybeCancellable {
resolveProjectsInEmbedder(
embedder,

View File

@@ -34,6 +34,7 @@ internal data class MavenProjectState(
val remoteRepositories: List<MavenRemoteRepository> = emptyList(),
val remotePluginRepositories: List<MavenRemoteRepository> = emptyList(),
val annotationProcessors: List<MavenArtifact> = emptyList(),
val managedDependencies: Map<String, MavenId> = emptyMap(),
val modulesPathsAndNames: Map<String, String> = emptyMap(),
val modelMap: Map<String, String> = emptyMap(),
val profilesIds: Collection<String> = emptySet(),

View File

@@ -999,6 +999,110 @@ class MavenProjectTest : MavenMultiVersionImportingTestCase() {
nodes[1]!!.dependencies[0].relatedArtifact)
}
@Test
fun testManagedDependencies() = runBlocking{
val p = createProjectPom("""
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>1</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.0</version>
</dependency>
</dependencies>
</dependencyManagement>
""")
importProjectAsync()
assertEquals("4.0", projectsTree.findProject(p)!!.findManagedDependency("junit", "junit")!!.version)
}
@Test
fun testManagedDependenciesFromParent() = runBlocking{
val p = createProjectPom("""
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>1</version>
<packaging>pom</packaging>
<modules>
<module>m1</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.0</version>
</dependency>
</dependencies>
</dependencyManagement>
""")
val m1 = createModulePom("m1",
"""
<parent>
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>1</version>
</parent>
<artifactId>m1</artifactId>
""")
importProjectAsync()
assertEquals("4.0", projectsTree.findProject(m1)!!.findManagedDependency("junit", "junit")!!.version)
}
@Test
fun testManagedDependenciesFromParentAndModule() = runBlocking{
val p = createProjectPom("""
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>1</version>
<packaging>pom</packaging>
<modules>
<module>m1</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.0</version>
</dependency>
</dependencies>
</dependencyManagement>
""")
val m1 = createModulePom("m1",
"""
<parent>
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>1</version>
</parent>
<artifactId>m1</artifactId>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>another</groupId>
<artifactId>dep</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</dependencyManagement>
""")
importProjectAsync()
assertEquals("4.0", projectsTree.findProject(m1)!!.findManagedDependency("junit", "junit")!!.version)
assertEquals("1.0", projectsTree.findProject(m1)!!.findManagedDependency("another", "dep")!!.version)
}
protected fun assertDependenciesNodes(nodes: List<MavenArtifactNode?>?, expected: String?) {
assertEquals(expected, StringUtil.join(nodes!!, ","))
}