[java] module path in app. run configuration

This commit is contained in:
Roman Shevchenko
2016-10-20 18:39:08 +02:00
parent 6e0f0b26c8
commit 6d7f9b72db
7 changed files with 196 additions and 75 deletions
@@ -15,6 +15,7 @@
*/
package com.intellij.execution.application;
import com.intellij.codeInsight.daemon.impl.analysis.JavaModuleGraphUtil;
import com.intellij.diagnostic.logging.LogConfigurationPanel;
import com.intellij.execution.*;
import com.intellij.execution.configuration.EnvironmentVariablesComponent;
@@ -29,13 +30,19 @@ import com.intellij.openapi.module.Module;
import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.options.SettingsEditorGroup;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.OrderEnumerator;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.util.DefaultJDOMExternalizer;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiJavaModule;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.util.PsiMethodUtil;
import com.intellij.refactoring.listeners.RefactoringElementListener;
import com.intellij.util.PathsList;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -43,6 +50,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Stream;
public class ApplicationConfiguration extends ModuleBasedConfiguration<JavaRunConfigurationModule>
implements CommonJavaRunConfigurationParameters, SingleClassConfiguration, RefactoringListenerProvider {
@@ -206,24 +214,24 @@ public class ApplicationConfiguration extends ModuleBasedConfiguration<JavaRunCo
@Override
public boolean isAlternativeJrePathEnabled() {
return ALTERNATIVE_JRE_PATH_ENABLED;
}
return ALTERNATIVE_JRE_PATH_ENABLED;
}
@Override
public void setAlternativeJrePathEnabled(boolean enabled) {
ALTERNATIVE_JRE_PATH_ENABLED = enabled;
}
@Override
public void setAlternativeJrePathEnabled(boolean enabled) {
ALTERNATIVE_JRE_PATH_ENABLED = enabled;
}
@Nullable
@Override
public String getAlternativeJrePath() {
return ALTERNATIVE_JRE_PATH;
}
@Nullable
@Override
public String getAlternativeJrePath() {
return ALTERNATIVE_JRE_PATH;
}
@Override
public void setAlternativeJrePath(String path) {
ALTERNATIVE_JRE_PATH = path;
}
@Override
public void setAlternativeJrePath(String path) {
ALTERNATIVE_JRE_PATH = path;
}
@Override
public Collection<Module> getValidModules() {
@@ -258,9 +266,9 @@ public class ApplicationConfiguration extends ModuleBasedConfiguration<JavaRunCo
protected JavaParameters createJavaParameters() throws ExecutionException {
final JavaParameters params = new JavaParameters();
params.setUseClasspathJar(true);
final JavaRunConfigurationModule module = myConfiguration.getConfigurationModule();
final String jreHome = myConfiguration.ALTERNATIVE_JRE_PATH_ENABLED ? myConfiguration.ALTERNATIVE_JRE_PATH : null;
if (module.getModule() != null) {
final int classPathType = JavaParametersUtil.getClasspathType(module, myConfiguration.MAIN_CLASS_NAME, false);
JavaParametersUtil.configureModule(module, params, classPathType, jreHome);
@@ -268,10 +276,50 @@ public class ApplicationConfiguration extends ModuleBasedConfiguration<JavaRunCo
else {
JavaParametersUtil.configureProject(module.getProject(), params, JavaParameters.JDK_AND_CLASSES_AND_TESTS, jreHome);
}
params.setMainClass(myConfiguration.MAIN_CLASS_NAME);
setupJavaParameters(params);
setupModulePath(params, module);
return params;
}
private static void setupModulePath(JavaParameters params, JavaRunConfigurationModule module) {
PsiClass mainClass = module.findClass(params.getMainClass());
if (mainClass != null) {
PsiJavaModule mainModule = JavaModuleGraphUtil.findDescriptorByElement(mainClass);
if (mainModule != null) {
params.setModuleName(mainModule.getModuleName());
PathsList modulePath = params.getModulePath(), classPath = params.getClassPath();
ProjectFileIndex index = ProjectFileIndex.SERVICE.getInstance(module.getProject());
JavaModuleGraphUtil.moduleDependencies(mainModule).stream()
.map(PsiImplUtil::getModuleVirtualFile)
.flatMap(f -> classRoots(f, index))
.forEach(f -> {
modulePath.add(f);
classPath.remove(f);
});
}
}
}
private static Stream<VirtualFile> classRoots(VirtualFile file, ProjectFileIndex index) {
if (index.isInSourceContent(file)) {
Module module = index.getModuleForFile(file);
if (module != null) {
return Stream.of(OrderEnumerator.orderEntries(module).runtimeOnly().withoutSdk().withoutLibraries().withoutDepModules().getClassesRoots());
}
}
else {
VirtualFile root = index.getClassRootForFile(file);
if (root != null) {
return Stream.of(root);
}
}
return Stream.empty();
}
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,42 +20,46 @@ import com.intellij.execution.configurations.JavaCommandLine;
import com.intellij.execution.configurations.JavaParameters;
import com.intellij.execution.configurations.ParametersList;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.openapi.projectRoots.ex.JavaSdkUtil;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.projectRoots.ex.JavaSdkUtil;
import com.intellij.openapi.util.SystemInfo;
import org.jetbrains.annotations.NonNls;
import java.io.File;
public class ProcessProxyFactoryImpl extends ProcessProxyFactory {
public ProcessProxy createCommandLineProxy(final JavaCommandLine javaCmdLine) throws ExecutionException {
ProcessProxyImpl proxy = null;
final JavaParameters javaParameters = javaCmdLine.getJavaParameters();
String mainClass = javaParameters.getMainClass();
if (ProcessProxyImpl.useLauncher() && mainClass != null) {
try {
proxy = new ProcessProxyImpl();
JavaSdkUtil.addRtJar(javaParameters.getClassPath());
final ParametersList vmParametersList = javaParameters.getVMParametersList();
vmParametersList.defineProperty(ProcessProxyImpl.PROPERTY_PORT_NUMBER, String.valueOf(proxy.getPortNumber()));
vmParametersList.defineProperty(ProcessProxyImpl.PROPERTY_BINPATH, PathManager.getBinPath());
javaParameters.getProgramParametersList().prepend(mainClass);
javaParameters.setMainClass(ProcessProxyImpl.LAUNCH_MAIN_CLASS);
}
catch (ProcessProxyImpl.NoMoreSocketsException e) {
proxy = null;
private static final String DONT_USE_LAUNCHER_PROPERTY = "idea.no.launcher";
@Override
public ProcessProxy createCommandLineProxy(JavaCommandLine javaCmdLine) throws ExecutionException {
JavaParameters javaParameters = javaCmdLine.getJavaParameters();
if (useLauncher(javaParameters)) {
String mainClass = javaParameters.getMainClass();
if (mainClass != null) {
try {
ProcessProxyImpl proxy = new ProcessProxyImpl();
JavaSdkUtil.addRtJar(javaParameters.getClassPath());
ParametersList vmParametersList = javaParameters.getVMParametersList();
vmParametersList.defineProperty(ProcessProxyImpl.PROPERTY_PORT_NUMBER, String.valueOf(proxy.getPortNumber()));
vmParametersList.defineProperty(ProcessProxyImpl.PROPERTY_BIN_PATH, PathManager.getBinPath());
javaParameters.getProgramParametersList().prepend(mainClass);
javaParameters.setMainClass(ProcessProxyImpl.LAUNCH_MAIN_CLASS);
return proxy;
}
catch (ProcessProxyImpl.NoMoreSocketsException ignored) { }
}
}
return proxy;
return null;
}
public ProcessProxy getAttachedProxy(final ProcessHandler processHandler) {
@Override
public ProcessProxy getAttachedProxy(ProcessHandler processHandler) {
return processHandler != null ? processHandler.getUserData(ProcessProxyImpl.KEY) : null;
}
@Override
public boolean isBreakGenLibraryAvailable() {
@NonNls final String libName;
String libName;
if (SystemInfo.isWindows) {
libName = "breakgen.dll";
}
@@ -65,6 +69,10 @@ public class ProcessProxyFactoryImpl extends ProcessProxyFactory {
else {
libName = "libbreakgen.so";
}
return new File(PathManager.getBinPath() + File.separator + libName).exists();
return new File(PathManager.getBinPath(), libName).exists();
}
private static boolean useLauncher(JavaParameters parameters) {
return !Boolean.getBoolean(DONT_USE_LAUNCHER_PROPERTY) && parameters.getModuleName() == null;
}
}
@@ -17,7 +17,6 @@ package com.intellij.execution.runners;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.openapi.util.Key;
import org.jetbrains.annotations.NonNls;
import java.io.BufferedWriter;
import java.io.IOException;
@@ -33,11 +32,10 @@ import java.net.Socket;
class ProcessProxyImpl implements ProcessProxy {
public static final Key<ProcessProxyImpl> KEY = Key.create("ProcessProxyImpl");
@NonNls public static final String PROPERTY_BINPATH = "idea.launcher.bin.path";
@NonNls public static final String PROPERTY_PORT_NUMBER = "idea.launcher.port";
@NonNls public static final String LAUNCH_MAIN_CLASS = "com.intellij.rt.execution.application.AppMain";
public static final String PROPERTY_BIN_PATH = "idea.launcher.bin.path";
public static final String PROPERTY_PORT_NUMBER = "idea.launcher.port";
public static final String LAUNCH_MAIN_CLASS = "com.intellij.rt.execution.application.AppMain";
@NonNls private static final String DONT_USE_LAUNCHER_PROPERTY = "idea.no.launcher";
private static final int SOCKET_NUMBER_START = 7532;
private static final int SOCKET_NUMBER = 100;
private static final boolean[] ourUsedSockets = new boolean[SOCKET_NUMBER];
@@ -46,8 +44,7 @@ class ProcessProxyImpl implements ProcessProxy {
private PrintWriter myWriter;
private Socket mySocket;
public static class NoMoreSocketsException extends Exception {
}
public static class NoMoreSocketsException extends Exception { }
public ProcessProxyImpl() throws NoMoreSocketsException {
myPortNumber = findFreePort();
@@ -91,7 +88,7 @@ class ProcessProxyImpl implements ProcessProxy {
}
@SuppressWarnings({"SocketOpenedButNotSafelyClosed", "IOResourceOpenedButNotSafelyClosed"})
private synchronized void writeLine(@NonNls final String s) {
private synchronized void writeLine(String s) {
if (myWriter == null) {
try {
if (mySocket == null) {
@@ -116,8 +113,4 @@ class ProcessProxyImpl implements ProcessProxy {
public void sendStop() {
writeLine("STOP");
}
public static boolean useLauncher() {
return !Boolean.valueOf(System.getProperty(DONT_USE_LAUNCHER_PROPERTY));
}
}
}
@@ -41,6 +41,12 @@ import static com.intellij.psi.util.PsiModificationTracker.OUT_OF_CODE_BLOCK_MOD
public class JavaModuleGraphUtil {
private JavaModuleGraphUtil() { }
@Nullable
public static PsiJavaModule findDescriptorByElement(@NotNull PsiElement element) {
PsiFileSystemItem fsItem = element instanceof PsiFileSystemItem ? (PsiFileSystemItem)element : element.getContainingFile();
return fsItem != null ? ModuleHighlightUtil.getModuleDescriptor(fsItem) : null;
}
@Nullable
public static Collection<PsiJavaModule> findCycle(@NotNull PsiJavaModule module) {
Project project = module.getProject();
@@ -57,10 +63,14 @@ public class JavaModuleGraphUtil {
}
public static boolean reads(@NotNull PsiJavaModule source, @NotNull PsiJavaModule destination) {
Project project = source.getProject();
RequiresGraph graph = CachedValuesManager.getManager(project).getCachedValue(project, () ->
Result.create(buildRequiresGraph(project), OUT_OF_CODE_BLOCK_MODIFICATION_COUNT));
return graph.reads(source, destination);
return getRequiresGraph(source).reads(source, destination);
}
@NotNull
public static Collection<PsiJavaModule> moduleDependencies(@NotNull PsiJavaModule start) {
Set<PsiJavaModule> result = ContainerUtil.newHashSet();
visit(getRequiresGraph(start), start, result);
return result;
}
// Looks for cycles between Java modules in the project sources.
@@ -111,6 +121,12 @@ public class JavaModuleGraphUtil {
return map;
}
private static RequiresGraph getRequiresGraph(@NotNull PsiJavaModule source) {
Project project = source.getProject();
return CachedValuesManager.getManager(project).getCachedValue(project, () ->
Result.create(buildRequiresGraph(project), OUT_OF_CODE_BLOCK_MODIFICATION_COUNT));
}
// Starting from source modules, collects all module dependencies in the project.
// The resulting graph is used for tracing readability.
private static RequiresGraph buildRequiresGraph(Project project) {
@@ -142,6 +158,12 @@ public class JavaModuleGraphUtil {
}
}
private static void visit(RequiresGraph graph, PsiJavaModule module, Set<PsiJavaModule> result) {
if (result.add(module)) {
graph.dependencies(module).forEach(dependency -> visit(graph, dependency, result));
}
}
private static class RequiresGraph {
private final Graph<PsiJavaModule> myGraph;
private final Set<String> myPublicEdges;
@@ -165,6 +187,10 @@ public class JavaModuleGraphUtil {
return false;
}
public Iterable<PsiJavaModule> dependencies(PsiJavaModule node) {
return myGraph.getNodes().contains(node) ? () -> myGraph.getIn(node) : Collections.emptyList();
}
public static String key(PsiJavaModule module, PsiJavaModule exporter) {
return module.getModuleName() + '/' + exporter.getModuleName();
}
@@ -63,6 +63,10 @@ public class PathsList {
}
};
public boolean isEmpty() {
return myPathSet.isEmpty();
}
public void add(String path) {
addAllLast(chooseFirstTimeItems(path), myPath);
}
@@ -77,6 +81,13 @@ public class PathsList {
add(LOCAL_PATH.fun(file));
}
public void remove(VirtualFile file) {
String path = LOCAL_PATH.fun(file);
if (path != null) {
remove(path);
}
}
public void addFirst(String path) {
int index = 0;
for (String element : chooseFirstTimeItems(path)) {
@@ -38,6 +38,8 @@ public class SimpleJavaParameters extends SimpleProgramParameters {
private Sdk myJdk;
private String myMainClass;
private final PathsList myClassPath = new PathsList();
private String myModuleName;
private final PathsList myModulePath = new PathsList();
private final ParametersList myVmParameters = new ParametersList();
private Charset myCharset = CharsetToolkit.getDefaultSystemCharset();
private boolean myUseDynamicClasspath;
@@ -67,6 +69,18 @@ public class SimpleJavaParameters extends SimpleProgramParameters {
return myClassPath;
}
public String getModuleName() {
return myModuleName;
}
public void setModuleName(String moduleName) {
myModuleName = moduleName;
}
public PathsList getModulePath() {
return myModulePath;
}
public ParametersList getVMParametersList() {
return myVmParameters;
}
@@ -33,6 +33,7 @@ import com.intellij.openapi.util.text.StringUtilRt;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.encoding.EncodingManager;
import com.intellij.util.PathUtil;
import com.intellij.util.PathsList;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.execution.ParametersListUtil;
import com.intellij.util.lang.ClassPath;
@@ -167,33 +168,36 @@ public class JdkUtil {
final boolean forceDynamicClasspath) {
final GeneralCommandLine commandLine = new GeneralCommandLine(exePath);
final ParametersList vmParametersList = javaParameters.getVMParametersList();
final ParametersList vmParameters = javaParameters.getVMParametersList();
commandLine.withEnvironment(javaParameters.getEnv());
commandLine.withParentEnvironmentType(javaParameters.isPassParentEnvs() ? ParentEnvironmentType.CONSOLE : ParentEnvironmentType.NONE);
final Class commandLineWrapper;
boolean passProgramParametersViaClassPathJar = false;
if ((commandLineWrapper = getCommandLineWrapperClass()) != null) {
if (forceDynamicClasspath && !vmParametersList.hasParameter("-classpath") && !vmParametersList.hasParameter("-cp")) {
if (isClassPathJarEnabled(javaParameters, PathUtil.getJarPathForClass(ClassPath.class))) {
passProgramParametersViaClassPathJar = javaParameters.isPassProgramParametersViaClasspathJar();
appendJarClasspathParams(javaParameters, commandLine, vmParametersList, commandLineWrapper, passProgramParametersViaClassPathJar);
}
else {
appendOldCommandLineWrapper(javaParameters, commandLine, vmParametersList, commandLineWrapper);
}
if (forceDynamicClasspath &&
!explicitClassPath(vmParameters) &&
javaParameters.getModulePath().isEmpty() &&
(commandLineWrapper = getCommandLineWrapperClass()) != null) {
if (isClassPathJarEnabled(javaParameters, PathUtil.getJarPathForClass(ClassPath.class))) {
passProgramParametersViaClassPathJar = javaParameters.isPassProgramParametersViaClasspathJar();
appendJarClasspathParams(javaParameters, commandLine, vmParameters, commandLineWrapper, passProgramParametersViaClassPathJar);
}
else {
appendParamsEncodingClasspath(javaParameters, commandLine, vmParametersList);
appendOldCommandLineWrapper(javaParameters, commandLine, vmParameters, commandLineWrapper);
}
}
else {
appendParamsEncodingClasspath(javaParameters, commandLine, vmParametersList);
appendParamsEncodingClasspath(javaParameters, commandLine, vmParameters);
}
final String mainClass = javaParameters.getMainClass();
final String moduleName = javaParameters.getModuleName();
final String jarPath = javaParameters.getJarPath();
if (mainClass != null) {
if (moduleName != null && mainClass != null) {
commandLine.addParameter("-m");
commandLine.addParameter(moduleName + '/' + mainClass);
}
else if (mainClass != null) {
commandLine.addParameter(mainClass);
}
else if (jarPath != null) {
@@ -210,6 +214,14 @@ public class JdkUtil {
return commandLine;
}
private static boolean explicitClassPath(ParametersList vmParameters) {
return vmParameters.hasParameter("-cp") || vmParameters.hasParameter("-classpath") || vmParameters.hasParameter("--class-path");
}
private static boolean explicitModulePath(ParametersList vmParameters) {
return vmParameters.hasParameter("-p") || vmParameters.hasParameter("--module-path");
}
private static void appendOldCommandLineWrapper(SimpleJavaParameters javaParameters,
GeneralCommandLine commandLine,
ParametersList vmParametersList, Class commandLineWrapper) {
@@ -374,12 +386,21 @@ public class JdkUtil {
private static void appendParamsEncodingClasspath(SimpleJavaParameters javaParameters,
GeneralCommandLine commandLine,
ParametersList parametersList) {
commandLine.addParameters(parametersList.getList());
appendEncoding(javaParameters, commandLine, parametersList);
if (!parametersList.hasParameter("-classpath") && !parametersList.hasParameter("-cp") && !javaParameters.getClassPath().getPathList().isEmpty()){
ParametersList vmParameters) {
commandLine.addParameters(vmParameters.getList());
appendEncoding(javaParameters, commandLine, vmParameters);
PathsList classPath = javaParameters.getClassPath();
if (!classPath.isEmpty() && !explicitClassPath(vmParameters)) {
commandLine.addParameter("-classpath");
commandLine.addParameter(javaParameters.getClassPath().getPathsString());
commandLine.addParameter(classPath.getPathsString());
}
PathsList modulePath = javaParameters.getModulePath();
if (!modulePath.isEmpty() && !explicitModulePath(vmParameters)) {
commandLine.addParameter("-p");
commandLine.addParameter(modulePath.getPathsString());
}
}