private Process launchBuildProcess(Project project, final int port, final UUID sessionId) throws ExecutionException { // choosing sdk with which the build process should be run final Sdk internalJdk = JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk(); Sdk projectJdk = internalJdk; final String versionString = projectJdk.getVersionString(); JavaSdkVersion sdkVersion = versionString != null ? ((JavaSdk) projectJdk.getSdkType()).getVersion(versionString) : null; if (sdkVersion != null) { final Set<Sdk> candidates = new HashSet<Sdk>(); for (Module module : ModuleManager.getInstance(project).getModules()) { final Sdk sdk = ModuleRootManager.getInstance(module).getSdk(); if (sdk != null && sdk.getSdkType() instanceof JavaSdk) { candidates.add(sdk); } } // now select the latest version from the sdks that are used in the project, but not older // than the internal sdk version for (Sdk candidate : candidates) { final String vs = candidate.getVersionString(); if (vs != null) { final JavaSdkVersion candidateVersion = ((JavaSdk) candidate.getSdkType()).getVersion(vs); if (candidateVersion != null) { if (candidateVersion.compareTo(sdkVersion) > 0) { sdkVersion = candidateVersion; projectJdk = candidate; } } } } } // validate tools.jar presence final File compilerPath; if (projectJdk.equals(internalJdk)) { final JavaCompiler systemCompiler = ToolProvider.getSystemJavaCompiler(); if (systemCompiler == null) { throw new ExecutionException( "No system java compiler is provided by the JRE. Make sure tools.jar is present in IntelliJ IDEA classpath."); } compilerPath = ClasspathBootstrap.getResourcePath(systemCompiler.getClass()); } else { final String path = ((JavaSdk) projectJdk.getSdkType()).getToolsPath(projectJdk); if (path == null) { throw new ExecutionException( "Cannot determine path to 'tools.jar' library for " + projectJdk.getName() + " (" + projectJdk.getHomePath() + ")"); } compilerPath = new File(path); } final GeneralCommandLine cmdLine = new GeneralCommandLine(); final String vmExecutablePath = ((JavaSdkType) projectJdk.getSdkType()).getVMExecutablePath(projectJdk); cmdLine.setExePath(vmExecutablePath); cmdLine.addParameter("-XX:MaxPermSize=150m"); cmdLine.addParameter("-XX:ReservedCodeCacheSize=64m"); final int heapSize = Registry.intValue("compiler.process.heap.size"); final int xms = heapSize / 2; if (xms > 32) { cmdLine.addParameter("-Xms" + xms + "m"); } cmdLine.addParameter("-Xmx" + heapSize + "m"); if (SystemInfo.isMac && sdkVersion != null && JavaSdkVersion.JDK_1_6.equals(sdkVersion) && Registry.is("compiler.process.32bit.vm.on.mac")) { // unfortunately -d32 is supported on jdk 1.6 only cmdLine.addParameter("-d32"); } cmdLine.addParameter("-Djava.awt.headless=true"); final String shouldGenerateIndex = System.getProperty(GlobalOptions.GENERATE_CLASSPATH_INDEX_OPTION); if (shouldGenerateIndex != null) { cmdLine.addParameter( "-D" + GlobalOptions.GENERATE_CLASSPATH_INDEX_OPTION + "=" + shouldGenerateIndex); } final String additionalOptions = Registry.stringValue("compiler.process.vm.options"); if (!StringUtil.isEmpty(additionalOptions)) { final StringTokenizer tokenizer = new StringTokenizer(additionalOptions, " ", false); while (tokenizer.hasMoreTokens()) { cmdLine.addParameter(tokenizer.nextToken()); } } // debugging final int debugPort = Registry.intValue("compiler.process.debug.port"); if (debugPort > 0) { cmdLine.addParameter("-XX:+HeapDumpOnOutOfMemoryError"); cmdLine.addParameter( "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=" + debugPort); } if (Registry.is("compiler.process.use.memory.temp.cache")) { cmdLine.addParameter("-D" + GlobalOptions.USE_MEMORY_TEMP_CACHE_OPTION); } if (Registry.is("compiler.process.use.external.javac")) { cmdLine.addParameter("-D" + GlobalOptions.USE_EXTERNAL_JAVAC_OPTION); } final String host = NetUtils.getLocalHostString(); cmdLine.addParameter("-D" + GlobalOptions.HOSTNAME_OPTION + "=" + host); // javac's VM should use the same default locale that IDEA uses in order for javac to print // messages in 'correct' language final String lang = System.getProperty("user.language"); if (lang != null) { //noinspection HardCodedStringLiteral cmdLine.addParameter("-Duser.language=" + lang); } final String country = System.getProperty("user.country"); if (country != null) { //noinspection HardCodedStringLiteral cmdLine.addParameter("-Duser.country=" + country); } //noinspection HardCodedStringLiteral final String region = System.getProperty("user.region"); if (region != null) { //noinspection HardCodedStringLiteral cmdLine.addParameter("-Duser.region=" + region); } cmdLine.addParameter("-classpath"); final List<File> cp = ClasspathBootstrap.getBuildProcessApplicationClasspath(); cp.add(compilerPath); cp.addAll(myClasspathManager.getCompileServerPluginsClasspath()); cmdLine.addParameter(classpathToString(cp)); cmdLine.addParameter(BuildMain.class.getName()); cmdLine.addParameter(host); cmdLine.addParameter(Integer.toString(port)); cmdLine.addParameter(sessionId.toString()); final File workDirectory = new File(mySystemDirectory, SYSTEM_ROOT); workDirectory.mkdirs(); ensureLogConfigExists(workDirectory); cmdLine.addParameter(FileUtil.toSystemIndependentName(workDirectory.getPath())); cmdLine.setWorkDirectory(workDirectory); return cmdLine.createProcess(); }
@Nullable public RequestFuture scheduleBuild( final Project project, final boolean isRebuild, final boolean isMake, final Collection<String> modules, final Collection<String> artifacts, final Collection<String> paths, final Map<String, String> userData, DefaultMessageHandler handler) { final String projectPath = getProjectPath(project); final UUID sessionId = UUID.randomUUID(); final CmdlineRemoteProto.Message.ControllerMessage params; CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings globals = myGlobals; if (globals == null) { globals = buildGlobalSettings(); myGlobals = globals; } CmdlineRemoteProto.Message.ControllerMessage.FSEvent currentFSChanges = null; final SequentialTaskExecutor projectTaskQueue; synchronized (myProjectDataMap) { ProjectData data = myProjectDataMap.get(projectPath); if (data == null) { data = new ProjectData(new SequentialTaskExecutor(myPooledThreadExecutor)); myProjectDataMap.put(projectPath, data); } if (isRebuild) { data.dropChanges(); } currentFSChanges = data.getAndResetRescanFlag() ? null : data.createNextEvent(); projectTaskQueue = data.taskQueue; } if (isRebuild) { params = CmdlineProtoUtil.createRebuildRequest(projectPath, userData, globals); } else { params = isMake ? CmdlineProtoUtil.createMakeRequest( projectPath, modules, artifacts, userData, globals, currentFSChanges) : CmdlineProtoUtil.createForceCompileRequest( projectPath, modules, artifacts, paths, userData, globals, currentFSChanges); } myMessageDispatcher.registerBuildMessageHandler(sessionId, handler, params); // ensure server is listening if (myListenPort < 0) { try { myListenPort = startListening(); } catch (Exception e) { myMessageDispatcher.unregisterBuildMessageHandler(sessionId); handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), null)); handler.sessionTerminated(); return null; } } final RequestFuture<BuilderMessageHandler> future = new RequestFuture<BuilderMessageHandler>( handler, sessionId, new RequestFuture.CancelAction<BuilderMessageHandler>() { @Override public void cancel(RequestFuture<BuilderMessageHandler> future) throws Exception { myMessageDispatcher.cancelSession(future.getRequestID()); } }); projectTaskQueue.submit( new Runnable() { @Override public void run() { try { if (project.isDisposed()) { future.cancel(false); return; } myBuildsInProgress.put(projectPath, future); final Process process = launchBuildProcess(project, myListenPort, sessionId); final OSProcessHandler processHandler = new OSProcessHandler(process, null) { @Override protected boolean shouldDestroyProcessRecursively() { return true; } }; final StringBuilder stdErrOutput = new StringBuilder(); processHandler.addProcessListener( new ProcessAdapter() { @Override public void processTerminated(ProcessEvent event) { final BuilderMessageHandler handler = myMessageDispatcher.unregisterBuildMessageHandler(sessionId); if (handler != null) { handler.sessionTerminated(); } } @Override public void onTextAvailable(ProcessEvent event, Key outputType) { // re-translate builder's output to idea.log final String text = event.getText(); if (!StringUtil.isEmpty(text)) { LOG.info("BUILDER_PROCESS [" + outputType.toString() + "]: " + text.trim()); if (stdErrOutput.length() < 1024 && ProcessOutputTypes.STDERR.equals(outputType)) { stdErrOutput.append(text); } } } }); processHandler.startNotify(); final boolean terminated = processHandler.waitFor(); if (terminated) { final int exitValue = processHandler.getProcess().exitValue(); if (exitValue != 0) { final StringBuilder msg = new StringBuilder(); msg.append("Abnormal build process termination: "); if (stdErrOutput.length() > 0) { msg.append("\n").append(stdErrOutput); } else { msg.append("unknown error"); } future .getMessageHandler() .handleFailure( sessionId, CmdlineProtoUtil.createFailure(msg.toString(), null)); } } else { future .getMessageHandler() .handleFailure( sessionId, CmdlineProtoUtil.createFailure("Disconnected from build process", null)); } } catch (ExecutionException e) { myMessageDispatcher.unregisterBuildMessageHandler(sessionId); future .getMessageHandler() .handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), e)); future.getMessageHandler().sessionTerminated(); } finally { myBuildsInProgress.remove(projectPath); future.setDone(); } } }); return future; }