mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
make ExternalJavacManager WSL-aware (IDEA-267753, IDEA-286844)
GitOrigin-RevId: dc487141f56192507046fba2d49c50e3688a00fc
This commit is contained in:
committed by
intellij-monorepo-bot
parent
98a709c5da
commit
329030f0b1
@@ -5,6 +5,7 @@ import com.intellij.compiler.impl.*;
|
||||
import com.intellij.compiler.impl.javaCompiler.BackendCompiler;
|
||||
import com.intellij.compiler.server.BuildManager;
|
||||
import com.intellij.execution.process.ProcessIOExecutorService;
|
||||
import com.intellij.execution.wsl.WSLDistribution;
|
||||
import com.intellij.ide.IdeEventQueue;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.compiler.Compiler;
|
||||
@@ -500,6 +501,7 @@ public class CompilerManagerImpl extends CompilerManager {
|
||||
manager = new ExternalJavacManager(
|
||||
compilerWorkingDir, ProcessIOExecutorService.INSTANCE, Registry.intValue("compiler.external.javac.keep.alive.timeout", 5*60*1000)
|
||||
);
|
||||
manager.setWslExecutablePath(WSLDistribution.findWslExe());
|
||||
manager.start(listenPort);
|
||||
myExternalJavacManager = manager;
|
||||
IdeEventQueue.getInstance().addIdleListener(new IdleTask(manager), IdleTask.CHECK_PERIOD);
|
||||
|
||||
@@ -3,10 +3,14 @@ package org.jetbrains.jps.javac;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.MessageLite;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jps.incremental.BinaryContent;
|
||||
|
||||
import javax.tools.*;
|
||||
import javax.tools.Diagnostic;
|
||||
import javax.tools.JavaFileManager;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardLocation;
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
@@ -22,13 +26,27 @@ public final class ExternalJavacMessageHandler {
|
||||
@Nullable
|
||||
private final String myEncodingName;
|
||||
private volatile boolean myTerminatedSuccessfully;
|
||||
private final WslSupport myWslSupport;
|
||||
|
||||
interface WslSupport {
|
||||
WslSupport DIRECT = new WslSupport() {
|
||||
@Override
|
||||
public String convertPath(String path) {
|
||||
return path != null? path.replace('\\', '/') : null;
|
||||
}
|
||||
};
|
||||
|
||||
String convertPath(String path);
|
||||
}
|
||||
|
||||
public ExternalJavacMessageHandler(DiagnosticOutputConsumer diagnosticSink,
|
||||
OutputFileConsumer outputSink,
|
||||
@Nullable final String encodingName) {
|
||||
@Nullable final String encodingName,
|
||||
@NotNull WslSupport wslSupport) {
|
||||
myDiagnosticSink = diagnosticSink;
|
||||
myOutputSink = outputSink;
|
||||
myEncodingName = encodingName;
|
||||
myWslSupport = wslSupport;
|
||||
}
|
||||
|
||||
public DiagnosticOutputConsumer getDiagnosticSink() {
|
||||
@@ -67,7 +85,7 @@ public final class ExternalJavacMessageHandler {
|
||||
final JavacRemoteProto.Message.Response.OutputObject.Kind kind = outputObject.getKind();
|
||||
|
||||
final String outputRoot = outputObject.hasOutputRoot()? outputObject.getOutputRoot() : null;
|
||||
final File outputRootFile = outputRoot != null? new File(outputRoot) : null;
|
||||
final File outputRootFile = outputRoot != null? new File(myWslSupport.convertPath(outputRoot)) : null;
|
||||
|
||||
final BinaryContent fileObjectContent;
|
||||
final ByteString content = outputObject.hasContent()? outputObject.getContent() : null;
|
||||
@@ -90,7 +108,7 @@ public final class ExternalJavacMessageHandler {
|
||||
null,
|
||||
outputRootFile,
|
||||
outputObject.hasRelativePath()? outputObject.getRelativePath() : null,
|
||||
new File(outputObject.getFilePath()),
|
||||
new File(myWslSupport.convertPath(outputObject.getFilePath())),
|
||||
convertKind(kind),
|
||||
outputObject.hasClassName()? outputObject.getClassName() : null,
|
||||
sources,
|
||||
@@ -104,7 +122,7 @@ public final class ExternalJavacMessageHandler {
|
||||
|
||||
if (responseType == JavacRemoteProto.Message.Response.Type.SRC_FILE_LOADED) {
|
||||
final JavacRemoteProto.Message.Response.OutputObject outputObject = response.getOutputObject();
|
||||
final File file = new File(outputObject.getFilePath());
|
||||
final File file = new File(myWslSupport.convertPath(outputObject.getFilePath()));
|
||||
myDiagnosticSink.javaFileLoaded(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -34,21 +34,22 @@ public final class JavacProtoUtil {
|
||||
ModulePath modulePath,
|
||||
Iterable<? extends File> upgradeModulePath,
|
||||
Iterable<? extends File> sourcePath,
|
||||
Map<File, Set<File>> outs) {
|
||||
Map<File, Set<File>> outs,
|
||||
ExternalJavacMessageHandler.WslSupport wslSupport) {
|
||||
final JavacRemoteProto.Message.Request.Builder builder = JavacRemoteProto.Message.Request.newBuilder();
|
||||
builder.setRequestType(JavacRemoteProto.Message.Request.Type.COMPILE);
|
||||
builder.addAllOption(options);
|
||||
for (File file : files) {
|
||||
builder.addFile(FileUtilRt.toSystemIndependentName(file.getPath()));
|
||||
builder.addFile(wslSupport.convertPath(file.getPath()));
|
||||
}
|
||||
for (File file : classpath) {
|
||||
builder.addClasspath(FileUtilRt.toSystemIndependentName(file.getPath()));
|
||||
builder.addClasspath(wslSupport.convertPath(file.getPath()));
|
||||
}
|
||||
for (File file : platformCp) {
|
||||
builder.addPlatformClasspath(FileUtilRt.toSystemIndependentName(file.getPath()));
|
||||
builder.addPlatformClasspath(wslSupport.convertPath(file.getPath()));
|
||||
}
|
||||
for (File file : modulePath.getPath()) {
|
||||
final String pathEntry = FileUtilRt.toSystemIndependentName(file.getPath());
|
||||
final String pathEntry = wslSupport.convertPath(file.getPath());
|
||||
builder.addModulePath(pathEntry);
|
||||
final String moduleName = modulePath.getModuleName(file);
|
||||
if (moduleName != null) {
|
||||
@@ -56,16 +57,16 @@ public final class JavacProtoUtil {
|
||||
}
|
||||
}
|
||||
for (File file : upgradeModulePath) {
|
||||
builder.addUpgradeModulePath(FileUtilRt.toSystemIndependentName(file.getPath()));
|
||||
builder.addUpgradeModulePath(wslSupport.convertPath(file.getPath()));
|
||||
}
|
||||
for (File file : sourcePath) {
|
||||
builder.addSourcepath(FileUtilRt.toSystemIndependentName(file.getPath()));
|
||||
builder.addSourcepath(wslSupport.convertPath(file.getPath()));
|
||||
}
|
||||
for (Map.Entry<File, Set<File>> entry : outs.entrySet()) {
|
||||
final JavacRemoteProto.Message.Request.OutputGroup.Builder groupBuilder = JavacRemoteProto.Message.Request.OutputGroup.newBuilder();
|
||||
groupBuilder.setOutputRoot(FileUtilRt.toSystemIndependentName(entry.getKey().getPath()));
|
||||
groupBuilder.setOutputRoot(wslSupport.convertPath(entry.getKey().getPath()));
|
||||
for (File srcRoot : entry.getValue()) {
|
||||
groupBuilder.addSourceRoot(FileUtilRt.toSystemIndependentName(srcRoot.getPath()));
|
||||
groupBuilder.addSourceRoot(wslSupport.convertPath(srcRoot.getPath()));
|
||||
}
|
||||
builder.addOutput(groupBuilder.build());
|
||||
}
|
||||
|
||||
@@ -398,7 +398,7 @@ public final class JavaBuilder extends ModuleLevelBuilder {
|
||||
DiagnosticOutputConsumer diagnosticSink,
|
||||
OutputFileConsumer outputSink,
|
||||
JavaCompilingTool compilingTool,
|
||||
File moduleInfoFile) {
|
||||
File moduleInfoFile) throws IOException {
|
||||
final Semaphore counter = new Semaphore();
|
||||
COUNTER_KEY.set(context, counter);
|
||||
|
||||
@@ -766,7 +766,7 @@ public final class JavaBuilder extends ModuleLevelBuilder {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static synchronized ExternalJavacManager ensureJavacServerStarted(@NotNull CompileContext context) {
|
||||
private static synchronized ExternalJavacManager ensureJavacServerStarted(@NotNull CompileContext context) throws IOException {
|
||||
ExternalJavacManager server = ExternalJavacManager.KEY.get(context);
|
||||
if (server != null) {
|
||||
return server;
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.io.FileUtilRt;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.concurrency.Semaphore;
|
||||
import com.intellij.util.io.BaseOutputReader;
|
||||
@@ -33,6 +34,8 @@ import org.jetbrains.jps.incremental.GlobalContextKey;
|
||||
import javax.tools.Diagnostic;
|
||||
import java.io.File;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
@@ -52,16 +55,19 @@ public class ExternalJavacManager extends ProcessAdapter {
|
||||
|
||||
private static final AttributeKey<UUID> PROCESS_ID_KEY = AttributeKey.valueOf("ExternalJavacServer.ProcessId");
|
||||
private static final Key<Integer> PROCESS_HASH = Key.create("ExternalJavacServer.SdkHomePath");
|
||||
private static final Key<ExternalJavacMessageHandler.WslSupport> WSL_SUPPORT = Key.create("_wsl_support_");
|
||||
|
||||
private final File myWorkingDir;
|
||||
private final ChannelRegistrar myChannelRegistrar;
|
||||
private int myListenPort = DEFAULT_SERVER_PORT;
|
||||
private InetAddress myListenAddress;
|
||||
private final Map<UUID, CompileSession> mySessions = Collections.synchronizedMap(new HashMap<>());
|
||||
private final Map<UUID, ExternalJavacProcessHandler> myRunningProcesses = Collections.synchronizedMap(new HashMap<>());
|
||||
private final Map<UUID, Channel> myConnections = Collections.synchronizedMap(new HashMap<>()); // processId->channel
|
||||
private final Executor myExecutor;
|
||||
private boolean myOwnExecutor;
|
||||
private final long myKeepAliveTimeout;
|
||||
private String myWslExePath = "wsl";
|
||||
|
||||
public ExternalJavacManager(@NotNull final File workingDir, @NotNull Executor executor) {
|
||||
this(workingDir, executor, 5 * 60 * 1000L /* 5 minutes default*/);
|
||||
@@ -74,7 +80,7 @@ public class ExternalJavacManager extends ProcessAdapter {
|
||||
myKeepAliveTimeout = keepAliveTimeout;
|
||||
}
|
||||
|
||||
public void start(int listenPort) {
|
||||
public void start(int listenPort) throws UnknownHostException {
|
||||
final ChannelHandler compilationRequestsHandler = new CompilationRequestsHandler();
|
||||
final ServerBootstrap bootstrap = new ServerBootstrap()
|
||||
.group(new NioEventLoopGroup(1, myExecutor))
|
||||
@@ -92,7 +98,9 @@ public class ExternalJavacManager extends ProcessAdapter {
|
||||
compilationRequestsHandler);
|
||||
}
|
||||
});
|
||||
myChannelRegistrar.add(bootstrap.bind(InetAddress.getLoopbackAddress(), listenPort).syncUninterruptibly().channel());
|
||||
// javac process forked in a Linux VM can only access ExternalJavacManager, which is listening in a host machine, using real IP
|
||||
myListenAddress = WslToLinuxPathConverter.isWslPath(myWorkingDir)? InetAddress.getLocalHost() : InetAddress.getLoopbackAddress();
|
||||
myChannelRegistrar.add(bootstrap.bind(myListenAddress, listenPort).syncUninterruptibly().channel());
|
||||
myListenPort = listenPort;
|
||||
}
|
||||
|
||||
@@ -116,12 +124,14 @@ public class ExternalJavacManager extends ProcessAdapter {
|
||||
|
||||
final Channel channel = lookupChannel(processHandler.getProcessId());
|
||||
if (channel != null) {
|
||||
final ExternalJavacMessageHandler.WslSupport wslSupport = WSL_SUPPORT.get(processHandler);
|
||||
final ExternalJavacMessageHandler.WslSupport converter = wslSupport instanceof WslToLinuxPathConverter? ((WslToLinuxPathConverter)wslSupport).reverseConverter() : ExternalJavacMessageHandler.WslSupport.DIRECT;
|
||||
final CompileSession session = new CompileSession(
|
||||
processHandler.getProcessId(), new ExternalJavacMessageHandler(diagnosticSink, outputSink, getEncodingName(options)), cancelStatus
|
||||
processHandler.getProcessId(), new ExternalJavacMessageHandler(diagnosticSink, outputSink, getEncodingName(options), converter), cancelStatus
|
||||
);
|
||||
mySessions.put(session.getId(), session);
|
||||
channel.writeAndFlush(JavacProtoUtil.toMessage(session.getId(), JavacProtoUtil.createCompilationRequest(
|
||||
options, files, paths.getClasspath(), paths.getPlatformClasspath(), paths.getModulePath(), paths.getUpgradeModulePath(), paths.getSourcePath(), outs
|
||||
options, files, paths.getClasspath(), paths.getPlatformClasspath(), paths.getModulePath(), paths.getUpgradeModulePath(), paths.getSourcePath(), outs, wslSupport
|
||||
)));
|
||||
return session;
|
||||
}
|
||||
@@ -283,7 +293,7 @@ public class ExternalJavacManager extends ProcessAdapter {
|
||||
return false;
|
||||
}
|
||||
|
||||
private ExternalJavacProcessHandler launchExternalJavacProcess(String sdkHomePath,
|
||||
private ExternalJavacProcessHandler launchExternalJavacProcess(final String sdkHomePath,
|
||||
int heapSize,
|
||||
int port,
|
||||
File workingDir,
|
||||
@@ -293,8 +303,10 @@ public class ExternalJavacManager extends ProcessAdapter {
|
||||
final UUID processId = UUID.randomUUID();
|
||||
final List<String> cmdLine = new ArrayList<>();
|
||||
|
||||
appendParam(cmdLine, getVMExecutablePath(sdkHomePath));
|
||||
|
||||
final WslToLinuxPathConverter wslConverter = WslToLinuxPathConverter.createFrom(sdkHomePath);
|
||||
final boolean launchInLinuxVM = wslConverter != null;
|
||||
final ExternalJavacMessageHandler.WslSupport wslSupport = launchInLinuxVM? wslConverter : ExternalJavacMessageHandler.WslSupport.DIRECT;
|
||||
appendParam(cmdLine, wslSupport.convertPath(sdkHomePath + "/bin/java"));
|
||||
appendParam(cmdLine, "-Djava.awt.headless=true");
|
||||
|
||||
//appendParam(cmdLine, "-XX:MaxPermSize=150m");
|
||||
@@ -330,21 +342,44 @@ public class ExternalJavacManager extends ProcessAdapter {
|
||||
|
||||
appendParam(cmdLine, "-classpath");
|
||||
List<File> cp = ClasspathBootstrap.getExternalJavacProcessClasspath(sdkHomePath, compilingTool);
|
||||
appendParam(cmdLine, cp.stream().map(File::getPath).collect(Collectors.joining(File.pathSeparator)));
|
||||
final String pathSeparator = launchInLinuxVM? ":" : File.pathSeparator;
|
||||
appendParam(cmdLine, cp.stream().map(f -> wslSupport.convertPath(f.getPath())).collect(Collectors.joining(pathSeparator)));
|
||||
|
||||
appendParam(cmdLine, ExternalJavacProcess.class.getName());
|
||||
appendParam(cmdLine, processId.toString());
|
||||
appendParam(cmdLine, InetAddress.getLoopbackAddress().getHostAddress());
|
||||
InetAddress targetAddress = myListenAddress;
|
||||
if (targetAddress == null) {
|
||||
targetAddress = InetAddress.getLoopbackAddress();
|
||||
}
|
||||
appendParam(cmdLine, targetAddress.getHostAddress());
|
||||
appendParam(cmdLine, Integer.toString(port));
|
||||
appendParam(cmdLine, Boolean.toString(keepProcessAlive)); // keep in memory after build finished
|
||||
|
||||
appendParam(cmdLine, FileUtil.toSystemIndependentName(workingDir.getPath()));
|
||||
if (launchInLinuxVM) {
|
||||
cmdLine.add(0, "&&");
|
||||
cmdLine.add(0, wslSupport.convertPath(workingDir.getPath()));
|
||||
cmdLine.add(0, "cd");
|
||||
final String command = StringUtil.join(cmdLine, " ");
|
||||
cmdLine.clear();
|
||||
cmdLine.add(myWslExePath);
|
||||
cmdLine.add("--distribution");
|
||||
cmdLine.add(wslConverter.getDistributionId());
|
||||
cmdLine.add("--exec");
|
||||
cmdLine.add("/bin/sh");
|
||||
cmdLine.add("-c");
|
||||
cmdLine.add("\"" + command + "\"");
|
||||
}
|
||||
|
||||
debug(()-> "starting external compiler: " + cmdLine);
|
||||
FileUtil.createDirectory(workingDir);
|
||||
|
||||
final int processHash = processHash(sdkHomePath, vmOptions, compilingTool);
|
||||
final ExternalJavacProcessHandler processHandler = createProcessHandler(processId, new ProcessBuilder(cmdLine).directory(workingDir).start(), StringUtil.join(cmdLine, " "), keepProcessAlive);
|
||||
final ProcessBuilder processBuilder = new ProcessBuilder(cmdLine);
|
||||
if (!launchInLinuxVM) {
|
||||
processBuilder.directory(workingDir);
|
||||
}
|
||||
final ExternalJavacProcessHandler processHandler = createProcessHandler(processId, processBuilder.start(), StringUtil.join(cmdLine, " "), keepProcessAlive);
|
||||
WSL_SUPPORT.set(processHandler, wslSupport);
|
||||
PROCESS_HASH.set(processHandler, processHash);
|
||||
processHandler.lock();
|
||||
myRunningProcesses.put(processId, processHandler);
|
||||
@@ -442,8 +477,10 @@ public class ExternalJavacManager extends ProcessAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
private static String getVMExecutablePath(String sdkHome) {
|
||||
return sdkHome + "/bin/java";
|
||||
public void setWslExecutablePath(@Nullable Path wslExePath) {
|
||||
if (wslExePath != null) {
|
||||
myWslExePath = wslExePath.toAbsolutePath().toString();
|
||||
}
|
||||
}
|
||||
|
||||
protected static class ExternalJavacProcessHandler extends BaseOSProcessHandler {
|
||||
@@ -707,4 +744,71 @@ public class ExternalJavacManager extends ProcessAdapter {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class WslToLinuxPathConverter implements ExternalJavacMessageHandler.WslSupport {
|
||||
private static final String WSL_PATH_PREFIX = "//wsl$/";
|
||||
private static final String MNT_PREFIX = "/mnt/";
|
||||
private static final int MNT_PATTERN_LENGTH = MNT_PREFIX.length() + 2;
|
||||
private final String myDistributionId;
|
||||
|
||||
WslToLinuxPathConverter(String distributionId) {
|
||||
myDistributionId = distributionId;
|
||||
}
|
||||
|
||||
public String getDistributionId() {
|
||||
return myDistributionId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String convertPath(String path) {
|
||||
final String normalized = FileUtilRt.toSystemIndependentName(path);
|
||||
if (isWslPath(normalized)) {
|
||||
final int distrSeparatorIndex = normalized.indexOf('/', WSL_PATH_PREFIX.length());
|
||||
return distrSeparatorIndex > WSL_PATH_PREFIX.length()? normalized.substring(distrSeparatorIndex) : normalized;
|
||||
}
|
||||
if (isWinPath(normalized)) {
|
||||
return MNT_PREFIX + Character.toLowerCase(normalized.charAt(0)) + normalized.substring(2);
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
public ExternalJavacMessageHandler.WslSupport reverseConverter() {
|
||||
final String prefix = WSL_PATH_PREFIX + myDistributionId;
|
||||
return path -> {
|
||||
if (path.startsWith(MNT_PREFIX) && path.length() >= MNT_PATTERN_LENGTH) {
|
||||
final char driveLetter = path.charAt(MNT_PREFIX.length());
|
||||
if (Character.isLetter(driveLetter) && path.charAt(MNT_PATTERN_LENGTH - 1) == '/') {
|
||||
return driveLetter + ":/" + path.substring(MNT_PATTERN_LENGTH);
|
||||
}
|
||||
}
|
||||
return path.startsWith("/")? prefix + path : prefix + "/" + path;
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static ExternalJavacManager.WslToLinuxPathConverter createFrom(String path) {
|
||||
if (SystemInfo.isWin10OrNewer) {
|
||||
path = FileUtilRt.toSystemIndependentName(path);
|
||||
if (path.startsWith(WSL_PATH_PREFIX)) {
|
||||
final int distrSeparatorIndex = path.indexOf('/', WSL_PATH_PREFIX.length());
|
||||
if (distrSeparatorIndex > WSL_PATH_PREFIX.length()) {
|
||||
return new WslToLinuxPathConverter(path.substring(WSL_PATH_PREFIX.length(), distrSeparatorIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static boolean isWslPath(File file) {
|
||||
return file != null && isWslPath(FileUtilRt.toSystemIndependentName(file.getAbsolutePath()));
|
||||
}
|
||||
|
||||
static boolean isWslPath(String path) {
|
||||
return path.startsWith(WSL_PATH_PREFIX);
|
||||
}
|
||||
|
||||
static boolean isWinPath(String path) {
|
||||
return path.length() >= 3 && Character.isLetter(path.charAt(0)) && path.charAt(1) == ':' && path.charAt(2) == '/';
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user