[maven] [IDEA-101997] replacing classpath in junit tests to filtered jar

GitOrigin-RevId: da2e645e39f5083f3f25a0351699f5798cfb81d8
This commit is contained in:
Alexander Bubenchikov
2024-09-17 13:05:20 +02:00
committed by intellij-monorepo-bot
parent 38679ee435
commit ad6a65873a
4 changed files with 154 additions and 74 deletions

View File

@@ -30,4 +30,7 @@ public class MavenFilteredJarConfiguration {
@Tag("jarOutput")
public @NotNull String jarOutput;
@Tag("name")
public @NotNull String name;
}

View File

@@ -9,7 +9,10 @@ import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.CompilerModuleExtension;
import com.intellij.openapi.util.PropertiesUtil;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import one.util.streamex.StreamEx;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
@@ -23,7 +26,9 @@ import org.jetbrains.idea.maven.project.MavenProject;
import org.jetbrains.idea.maven.project.MavenProjectSettings;
import org.jetbrains.idea.maven.project.MavenProjectsManager;
import org.jetbrains.idea.maven.project.MavenTestRunningSettings;
import org.jetbrains.idea.maven.utils.MavenFilteredJarUtils;
import org.jetbrains.idea.maven.utils.MavenJDOMUtil;
import org.jetbrains.jps.maven.model.impl.MavenFilteredJarConfiguration;
import java.io.File;
import java.io.IOException;
@@ -54,15 +59,71 @@ public final class MavenJUnitPatcher extends JUnitPatcher {
public void patchJavaParameters(@Nullable Module module, JavaParameters javaParameters) {
if (module == null) return;
MavenProject mavenProject = MavenProjectsManager.getInstance(module.getProject()).findProject(module);
MavenProjectsManager projectsManager = MavenProjectsManager.getInstance(module.getProject());
MavenProject mavenProject = projectsManager.findProject(module);
if (mavenProject == null) return;
UnaryOperator<String> runtimeProperties = getDynamicConfigurationProperties(module, mavenProject, javaParameters);
configureFromPlugin(module, javaParameters, mavenProject, runtimeProperties, "maven-surefire-plugin", "surefire");
configureFromPlugin(module, javaParameters, mavenProject, runtimeProperties, "maven-failsafe-plugin", "failsafe");
replaceFilteredJarDirectories(projectsManager, module, javaParameters, mavenProject);
}
private static void replaceFilteredJarDirectories(MavenProjectsManager projectsManager,
@NotNull Module module,
JavaParameters parameters,
MavenProject project) {
if (!Registry.is("maven.build.additional.jars")) return;
//todo: We do dependency traversing every time, we need another structure in project tree for this to retrieve this data in a fast way
Set<MavenArtifact> visited = new HashSet<>();
ArrayDeque<MavenArtifact> queue = new ArrayDeque<>(project.getDependencies());
List<MavenFilteredJarConfiguration> toReplace = new ArrayList<>();
while (!queue.isEmpty()) {
var dependency = queue.poll();
var depProject = projectsManager.findProject(dependency);
if (depProject == null) continue;
if (!visited.add(dependency)) continue;
MavenFilteredJarConfiguration jarConfiguration =
findFilteredJarConfig(projectsManager, depProject, dependency.getClassifier());
if (jarConfiguration != null) {
LOG.debug(
"found additional jar configuration for " + dependency + ", classpath will be replaced in tests for module " + module.getName());
toReplace.add(jarConfiguration);
}
queue.addAll(depProject.getDependencies());
}
if (toReplace.isEmpty()) return;
//do not expect a lot of MavenFilteredJarConfiguration here, O(n^2) should be fine
String[] paths = ArrayUtil.toStringArray(parameters.getClassPath().getPathList());
boolean replaced = false;
for (int i = 0; i < paths.length; i++) {
String path = paths[i];
MavenFilteredJarConfiguration config = ContainerUtil.find(toReplace, c -> FileUtil.pathsEqual(path, c.originalOutput));
if (config != null) {
paths[i] = config.jarOutput;
replaced = true;
}
}
if (!replaced) {
LOG.warn(
"expected to replace " + toReplace.size() + " dependencies in running module " + module.getName() + ", but replaced 0");
}
else {
parameters.getClassPath().clear();
parameters.getClassPath().addAll(Arrays.asList(paths));
}
}
private static @Nullable MavenFilteredJarConfiguration findFilteredJarConfig(MavenProjectsManager projectsManager,
MavenProject mavenProject, String classifier) {
List<@NotNull MavenFilteredJarConfiguration> configurations =
MavenFilteredJarUtils.getAllFilteredConfigurations(projectsManager, mavenProject);
return ContainerUtil.find(configurations, c -> StringUtil.equals(c.classifier, classifier));
}
private static void configureFromPlugin(@NotNull Module module,
JavaParameters javaParameters,
MavenProject mavenProject,
@@ -155,7 +216,7 @@ public final class MavenJUnitPatcher extends JUnitPatcher {
MavenProjectsManager mavenProjectsManager = MavenProjectsManager.getInstance(module.getProject());
if (scopeExclude != null || !excludes.isEmpty()) {
for (MavenArtifact dependency : mavenProject.getDependencies()) {
if (scopeExclude!=null && SCOPE_FILTER.getOrDefault(scopeExclude, Collections.emptyList()).contains(dependency.getScope()) ||
if (scopeExclude != null && SCOPE_FILTER.getOrDefault(scopeExclude, Collections.emptyList()).contains(dependency.getScope()) ||
excludes.contains(dependency.getGroupId() + ":" + dependency.getArtifactId())) {
File file = dependency.getFile();
javaParameters.getClassPath().remove(file.getAbsolutePath());
@@ -281,7 +342,7 @@ public final class MavenJUnitPatcher extends JUnitPatcher {
while (matcher.find()) {
String finding = matcher.group();
final String propertyValue = vmParameters.getPropertyValue(finding.substring(2, finding.length() - 1));
if(propertyValue == null) continue;
if (propertyValue == null) continue;
toReplace.put(finding, propertyValue);
}
for (Map.Entry<String, String> entry : toReplace.entrySet()) {

View File

@@ -1,17 +1,13 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.idea.maven.project.compilation
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.roots.ProjectFileIndex
import com.intellij.openapi.util.registry.Registry
import com.intellij.util.text.nullize
import org.jdom.Element
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.idea.maven.project.MavenProject
import org.jetbrains.idea.maven.project.MavenProjectsManager
import org.jetbrains.idea.maven.server.RemotePathTransformerFactory
import org.jetbrains.idea.maven.utils.MavenJDOMUtil.findChildrenValuesByPath
import org.jetbrains.jps.maven.model.impl.MavenFilteredJarConfiguration
import org.jetbrains.idea.maven.utils.MavenFilteredJarUtils
import org.jetbrains.jps.maven.model.impl.MavenProjectConfiguration
@ApiStatus.Internal
@@ -27,72 +23,8 @@ class FilteredJarConfigGenerator(
return
}
if ("pom".equals(mavenProject.packaging)) return;
GOALS.forEach { g, _ ->
mavenProject.getPluginGoalConfiguration("org.apache.maven.plugins", "maven-jar-plugin", g)?.let {
addConfiguration(g, it)
}
MavenFilteredJarUtils.getAllFilteredConfigurations(mavenProjectsManager, mavenProject).forEach { jarConfig ->
config.jarsConfiguration[jarConfig.name] = jarConfig
}
}
private fun addConfiguration(goal: String, element: Element) {
val includes = findChildrenValuesByPath(element, "includes", "include").toMutableList()
val excludes = findChildrenValuesByPath(element, "excludes", "exclude").toMutableList()
val classifier = element.getChildTextTrim("classifier") ?: GOALS[goal] ?: ""
val excludeDefaults: Boolean
if ("false".equals(element.getChildTextTrim("addDefaultExcludes"), true)) {
excludeDefaults = false
}
else {
excludeDefaults = true
}
if (excludeDefaults) {
DEFAULTEXCLUDES.forEach { _, v -> v.forEach(excludes::add) }
}
doAddConfiguration(goal == "test-jar", classifier, excludes, includes)
}
private fun doAddConfiguration(tests: Boolean, classifier: String, excludes: MutableList<String>, includes: MutableList<String>) {
val module = mavenProjectsManager.findModule(mavenProject) ?: return
val configuration = MavenFilteredJarConfiguration()
configuration.classifier = classifier
configuration.excludes = excludes.toSet()
configuration.includes = includes.toSet()
configuration.moduleName = module.name
val name = mavenProject.mavenId.toString() + (classifier.nullize(true)?.map { "-$it" } ?: "")
configuration.isTest = tests
configuration.originalOutput = if (tests) mavenProject.testOutputDirectory else mavenProject.outputDirectory;
configuration.jarOutput = configuration.originalOutput + "-jar-" + classifier
config.jarsConfiguration[name] = configuration
}
companion object {
private val LOG = Logger.getInstance(FilteredJarConfigGenerator::class.java)
private val GOALS = mapOf("jar" to "", "test-jar" to "tests")
//https://codehaus-plexus.github.io/plexus-utils/apidocs/org/codehaus/plexus/util/AbstractScanner.html#DEFAULTEXCLUDES
private val DEFAULTEXCLUDES = mapOf<String, Array<String>>(
"Misc" to arrayOf(" **/*~", "**/#*#", "**/.#*", "**/%*%", "*/._*"),
"CVS" to arrayOf("*/CVS", "**/CVS/**", "**/.cvsignore"),
"RCS" to arrayOf("**/RCS", "**/RCS/**"),
"SCCS" to arrayOf("**/SCCS", "**/SCCS/**"),
"VSSercer" to arrayOf(" **/vssver.scc"),
"MKS" to arrayOf("**/project.pj"),
"SVN" to arrayOf("**/.svn", "**/.svn/**"),
"GNU" to arrayOf("**/.arch-ids", "**/.arch-ids/**"),
"Bazaar" to arrayOf("**/.bzr", "**/.bzr/**"),
"SurroundSCM" to arrayOf("**/.MySCMServerInfo"),
"Mac" to arrayOf("**/.DS_Store"),
"Serena Dimension" to arrayOf("**/.metadata", "**/.metadata/**"),
"Mercurial" to arrayOf("**/.hg", "**/.hg/**"),
"Git" to arrayOf("**/.git", "**/.git/**", "**/.gitignore"),
"Bitkeeper" to arrayOf("**/BitKeeper", "**/BitKeeper/**", "**/ChangeSet", "**/ChangeSet/**"),
"Darcs" to arrayOf("**/_darcs", "**/_darcs/**", "**/.darcsrepo", "**/.darcsrepo/****/-darcs-backup*", "**/.darcs-temp-mail"),
)
}
}

View File

@@ -0,0 +1,84 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.idea.maven.utils
import com.intellij.util.text.nullize
import org.jdom.Element
import org.jetbrains.idea.maven.project.MavenProject
import org.jetbrains.idea.maven.project.MavenProjectsManager
import org.jetbrains.idea.maven.utils.MavenJDOMUtil.findChildrenValuesByPath
import org.jetbrains.jps.maven.model.impl.MavenFilteredJarConfiguration
class MavenFilteredJarUtils {
companion object {
@JvmStatic
fun getAllFilteredConfigurations(mavenProjectsManager: MavenProjectsManager, mavenProject: MavenProject): List<MavenFilteredJarConfiguration> {
if ("pom".equals(mavenProject.packaging)) return emptyList()
val result = ArrayList<MavenFilteredJarConfiguration>()
for (e in GOALS) {
val element = mavenProject.getPluginGoalConfiguration("org.apache.maven.plugins", "maven-jar-plugin", e.key) ?: continue
loadConfiguration(mavenProjectsManager, mavenProject, element, e.key)?.also {
result.add(it)
}
}
return result
}
private fun loadConfiguration(mavenProjectsManager: MavenProjectsManager, mavenProject: MavenProject, element: Element, goal: String): MavenFilteredJarConfiguration? {
val includes = findChildrenValuesByPath(element, "includes", "include").toMutableList()
val excludes = findChildrenValuesByPath(element, "excludes", "exclude").toMutableList()
if (excludes.isEmpty() && includes.isEmpty()) return null //no configurations if jar is not filtered
val classifier = element.getChildTextTrim("classifier") ?: GOALS[goal] ?: ""
val excludeDefaults: Boolean
if ("false".equals(element.getChildTextTrim("addDefaultExcludes"), true)) {
excludeDefaults = false
}
else {
excludeDefaults = true
}
if (excludeDefaults) {
DEFAULTEXCLUDES.forEach { _, v -> v.forEach(excludes::add) }
}
val module = mavenProjectsManager.findModule(mavenProject) ?: return null
val configuration = MavenFilteredJarConfiguration()
val tests = goal == "test-jar"
configuration.classifier = classifier
configuration.excludes = excludes.toSet()
configuration.includes = includes.toSet()
configuration.moduleName = module.name
configuration.isTest = tests
configuration.originalOutput = if (tests) mavenProject.testOutputDirectory else mavenProject.outputDirectory;
configuration.jarOutput = configuration.originalOutput + "-jar-" + classifier
configuration.name = mavenProject.mavenId.toString() + (classifier.nullize(true)?.map { "-$it" } ?: "")
return configuration
}
private val GOALS = mapOf("jar" to "", "test-jar" to "tests")
//https://codehaus-plexus.github.io/plexus-utils/apidocs/org/codehaus/plexus/util/AbstractScanner.html#DEFAULTEXCLUDES
private val DEFAULTEXCLUDES = mapOf<String, Array<String>>(
"Misc" to arrayOf(" **/*~", "**/#*#", "**/.#*", "**/%*%", "*/._*"),
"CVS" to arrayOf("*/CVS", "**/CVS/**", "**/.cvsignore"),
"RCS" to arrayOf("**/RCS", "**/RCS/**"),
"SCCS" to arrayOf("**/SCCS", "**/SCCS/**"),
"VSSercer" to arrayOf(" **/vssver.scc"),
"MKS" to arrayOf("**/project.pj"),
"SVN" to arrayOf("**/.svn", "**/.svn/**"),
"GNU" to arrayOf("**/.arch-ids", "**/.arch-ids/**"),
"Bazaar" to arrayOf("**/.bzr", "**/.bzr/**"),
"SurroundSCM" to arrayOf("**/.MySCMServerInfo"),
"Mac" to arrayOf("**/.DS_Store"),
"Serena Dimension" to arrayOf("**/.metadata", "**/.metadata/**"),
"Mercurial" to arrayOf("**/.hg", "**/.hg/**"),
"Git" to arrayOf("**/.git", "**/.git/**", "**/.gitignore"),
"Bitkeeper" to arrayOf("**/BitKeeper", "**/BitKeeper/**", "**/ChangeSet", "**/ChangeSet/**"),
"Darcs" to arrayOf("**/_darcs", "**/_darcs/**", "**/.darcsrepo", "**/.darcsrepo/****/-darcs-backup*", "**/.darcs-temp-mail"),
)
}
}