private OSProcessHandler launchBuildProcess(@NotNull Project project, @NotNull UUID sessionId, boolean requestProjectPreload, @Nullable WSLDistribution projectWslDistribution, @Nullable ProgressIndicator progressIndicator) throws ExecutionException {
    String compilerPath = null;
    final String vmExecutablePath;
    JavaSdkVersion sdkVersion = null;

    final String forcedCompiledJdkHome = Registry.stringValue(COMPILER_PROCESS_JDK_PROPERTY);
    String sdkName;
    if (Strings.isEmptyOrSpaces(forcedCompiledJdkHome)) {
      // choosing sdk with which the build process should be run
      final Pair<Sdk, JavaSdkVersion> pair = getBuildProcessRuntimeSdk(project);
      final Sdk projectJdk = pair.first;
      sdkName = projectJdk.getName();
      sdkVersion = pair.second;

      final JavaSdkType projectJdkType = (JavaSdkType)projectJdk.getSdkType();

      // validate tools.jar presence for jdk8 and older
      if (!JavaSdkUtil.isJdkAtLeast(projectJdk, JavaSdkVersion.JDK_1_9)) {
        @SuppressWarnings("removal") final Sdk internalJdk = JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk();
        if (FileUtil.pathsEqual(projectJdk.getHomePath(), internalJdk.getHomePath())) {
          // important: because internal JDK can be either JDK or JRE,
          // this is the most universal way to obtain tools.jar path in this particular case
          final JavaCompiler systemCompiler = ToolProvider.getSystemJavaCompiler();
          if (systemCompiler == null) {
            //temporary workaround for IDEA-169747
            try {
              compilerPath = ClasspathBootstrap.getResourcePath(Class.forName("com.sun.tools.javac.api.JavacTool", false, BuildManager.class.getClassLoader()));
            }
            catch (Throwable t) {
              LOG.info(t);
            }
            if (compilerPath == null) {
              throw new ExecutionException(JavaCompilerBundle.message("build.process.no.javac.found"));
            }
          }
          else {
            compilerPath = ClasspathBootstrap.getResourcePath(systemCompiler.getClass());
          }
        }
        else {
          compilerPath = projectJdkType.getToolsPath(projectJdk);
          if (compilerPath == null) {
            throw new ExecutionException(JavaCompilerBundle.message("build.process.no.javac.path.found", sdkName, projectJdk.getHomePath()));
          }
        }
      }

      vmExecutablePath = projectJdkType.getVMExecutablePath(projectJdk);
      project.getService(BuildManagerVersionChecker.class).checkArch(projectJdk.getHomePath());
    }
    else {
      sdkName = forcedCompiledJdkHome;
      compilerPath = new File(forcedCompiledJdkHome, "lib/tools.jar").getAbsolutePath();
      vmExecutablePath = new File(forcedCompiledJdkHome, "bin/java").getAbsolutePath();
      project.getService(BuildManagerVersionChecker.class).checkArch(forcedCompiledJdkHome);
    }

    final CompilerConfiguration projectConfig = CompilerConfiguration.getInstance(project);
    final CompilerWorkspaceConfiguration config = CompilerWorkspaceConfiguration.getInstance(project);

    InetAddress listenAddress = InetAddress.getLoopbackAddress();
    int listenPort = ensureListening(listenAddress);
    String buildProcessConnectHost = listenAddress.getHostAddress();
    int buildProcessConnectPort = listenPort;

    BuildCommandLineBuilder cmdLine;
    WslPath wslPath = WslPath.parseWindowsUncPath(vmExecutablePath);
    if (wslPath != null) {
      WSLDistribution sdkDistribution = wslPath.getDistribution();
      if (!sdkDistribution.equals(projectWslDistribution)) {
        throw new ExecutionException(JavaCompilerBundle.message("build.process.wsl.distribution.dont.match", sdkName + " (WSL " + sdkDistribution.getPresentableName() + ")", MINIMUM_REQUIRED_JPS_BUILD_JAVA_VERSION));
      }
      cmdLine = new WslBuildCommandLineBuilder(project, sdkDistribution, wslPath.getLinuxPath(), progressIndicator);
      buildProcessConnectHost = "127.0.0.1"; // WslProxy listen address on linux side
      buildProcessConnectPort = getWslPort(sdkDistribution, listenPort);
    }
    else {
      if (projectWslDistribution != null) {
        throw new ExecutionException(JavaCompilerBundle.message("build.process.wsl.distribution.dont.match", sdkName, MINIMUM_REQUIRED_JPS_BUILD_JAVA_VERSION));
      }
      cmdLine = new LocalBuildCommandLineBuilder(vmExecutablePath);
    }

    boolean profileWithYourKit = false;
    boolean isAgentpathSet = false;
    String userDefinedHeapSize = null;
    final List<String> userAdditionalOptionsList = new SmartList<>();
    final String userAdditionalVMOptions = config.COMPILER_PROCESS_ADDITIONAL_VM_OPTIONS;
    final boolean userLocalOptionsActive = !StringUtil.isEmptyOrSpaces(userAdditionalVMOptions);
    final String additionalOptions = userLocalOptionsActive ? userAdditionalVMOptions : projectConfig.getBuildProcessVMOptions();
    if (!StringUtil.isEmptyOrSpaces(additionalOptions)) {
      final StringTokenizer tokenizer = new StringTokenizer(additionalOptions, " ", false);
      while (tokenizer.hasMoreTokens()) {
        final String option = tokenizer.nextToken();
        if (StringUtil.startsWithIgnoreCase(option, "-Xmx")) {
          if (userLocalOptionsActive) {
            userDefinedHeapSize = option;
          }
        }
        else {
          //noinspection SpellCheckingInspection
          if (option.startsWith("-Dprofiling.mode=") && !option.equals("-Dprofiling.mode=false")) {
            profileWithYourKit = true;
          }
          if (option.startsWith("-agentpath:")) {
            isAgentpathSet = true;
          }
          userAdditionalOptionsList.add(option);
        }
      }
    }

    final int localHeapSize = config.COMPILER_PROCESS_HEAP_SIZE;
    if (localHeapSize > 0) {
      cmdLine.addParameter("-Xmx" + localHeapSize + "m");
    }
    else if (userDefinedHeapSize != null) {
      cmdLine.addParameter(userDefinedHeapSize);
    }
    else {
      final int heapSize = projectConfig.getBuildProcessHeapSize(JavacConfiguration.getOptions(project, JavacConfiguration.class).MAXIMUM_HEAP_SIZE);
      cmdLine.addParameter("-Xmx" + heapSize + "m");
    }

    cmdLine.addParameter("-Djava.awt.headless=true");

    String jnaBootLibraryPath = System.getProperty("jna.boot.library.path");
    if (jnaBootLibraryPath != null) {
      //noinspection SpellCheckingInspection
      cmdLine.addParameter("-Djna.boot.library.path=" + jnaBootLibraryPath);
      //noinspection SpellCheckingInspection
      cmdLine.addParameter("-Djna.nosys=true");
      //noinspection SpellCheckingInspection
      cmdLine.addParameter("-Djna.noclasspath=true");
    }
    if (Registry.is("jps.build.use.workspace.model")) {
      cmdLine.addParameter("-Dintellij.jps.use.workspace.model=true");
      WorkspaceModelCache cache = WorkspaceModelCache.getInstance(project);
      GlobalWorkspaceModelCache globalCache = GlobalWorkspaceModelCache.getInstance();
      if (cache != null && globalCache != null) {
        //todo ensure that caches are up-to-date or use a different way to pass serialized workspace model to the build process
        cmdLine.addParameter("-Djps.workspace.storage.project.cache.path=" + cache.getCacheFile());
        cmdLine.addParameter("-Djps.workspace.storage.global.cache.path=" + globalCache.getCacheFile());
      }
      else {
        LOG.info("Workspace model caches aren't available and won't be used in the build process");
      }
      cmdLine.addParameter("--add-opens=java.base/java.util=ALL-UNNAMED");//used by com.esotericsoftware.kryo.kryo5.serializers.CachedFields.addField
    }

    if (sdkVersion != null) {
      if (sdkVersion.compareTo(JavaSdkVersion.JDK_1_9) < 0) {
        //-Djava.endorsed.dirs is not supported in JDK 9+, may result in abnormal process termination
        cmdLine.addParameter("-Djava.endorsed.dirs=\"\""); // turn off all jre customizations for predictable behaviour
      }
      if (sdkVersion.isAtLeast(JavaSdkVersion.JDK_16)) {
        // enable javac-related reflection tricks in JPS
        ClasspathBootstrap.configureReflectionOpenPackages(cmdLine::addParameter);
      }
    }
    if (IS_UNIT_TEST_MODE) {
      //noinspection SpellCheckingInspection
      cmdLine.addParameter("-Dtest.mode=true");
    }

    if (requestProjectPreload) {
      //noinspection SpellCheckingInspection
      cmdLine.addPathParameter("-Dpreload.project.path=", FileUtil.toCanonicalPath(getProjectPath(project)));
      //noinspection SpellCheckingInspection
      cmdLine.addPathParameter("-Dpreload.config.path=", FileUtil.toCanonicalPath(PathManager.getOptionsPath()));
    }

    if (ProjectUtilCore.isExternalStorageEnabled(project)) {
      cmdLine.addPathParameter("-D" + GlobalOptions.EXTERNAL_PROJECT_CONFIG + '=', ProjectUtil.getExternalConfigurationDir(project));
    }

    cmdLine.addParameter("-D" + GlobalOptions.COMPILE_PARALLEL_OPTION + '=' + projectConfig.isParallelCompilationEnabled());
    if (projectConfig.isParallelCompilationEnabled()) {
      if (!Registry.is("compiler.automake.allow.parallel", true)) {
        cmdLine.addParameter("-D" + GlobalOptions.ALLOW_PARALLEL_AUTOMAKE_OPTION + "=false");
      }
    }
    cmdLine.addParameter("-D" + GlobalOptions.REBUILD_ON_DEPENDENCY_CHANGE_OPTION + '=' + config.REBUILD_ON_DEPENDENCY_CHANGE);
    cmdLine.addParameter("-Didea.IntToIntBtree.page.size=32768");

    if (Registry.is("compiler.build.report.statistics")) {
      cmdLine.addParameter("-D" + GlobalOptions.REPORT_BUILD_STATISTICS + "=true");
    }
    if (Registry.is("compiler.natural.int.multimap.impl")) {  // todo: temporary flag to evaluate experimental multi-map implementation
      //noinspection SpellCheckingInspection
      cmdLine.addParameter("-Djps.mappings.natural.int.multimap.impl=true");
    }

    // third-party library tweaks
    //noinspection SpellCheckingInspection
    cmdLine.addParameter("-Djdt.compiler.useSingleThread=true"); // always run Eclipse compiler in single-threaded mode
    //noinspection SpellCheckingInspection
    cmdLine.addParameter("-Daether.connector.resumeDownloads=false"); // always re-download maven libraries if partially downloaded
    cmdLine.addParameter("-Dio.netty.initialSeedUniquifier=" + ThreadLocalRandom.getInitialSeedUniquifier()); // this will make netty initialization faster on some systems

    for (String option : userAdditionalOptionsList) {
      cmdLine.addParameter(option);
    }

    cmdLine.setupAdditionalVMOptions();

    Path hostWorkingDirectory = cmdLine.getHostWorkingDirectory();
    if (!FileUtil.createDirectory(hostWorkingDirectory.toFile())) {
      LOG.warn("Failed to create build working directory " + hostWorkingDirectory);
    }

    if (profileWithYourKit) {
      YourKitProfilerService service = ApplicationManager.getApplication().getService(YourKitProfilerService.class);
      if (service != null) {
        try {
          service.copyYKLibraries(hostWorkingDirectory);
        }
        catch (IOException e) {
          LOG.warn("Failed to copy YK libraries", e);
        }
        @SuppressWarnings("SpellCheckingInspection")
        final StringBuilder parameters = new StringBuilder().append("-agentpath:").append(cmdLine.getYjpAgentPath(service)).append("=disablealloc,delay=10000,sessionname=ExternalBuild");
        final String buildSnapshotPath = System.getProperty("build.snapshots.path");
        if (buildSnapshotPath != null) {
          parameters.append(",dir=").append(buildSnapshotPath);
        }
        cmdLine.addParameter(parameters.toString());
        showSnapshotNotificationAfterFinish(project);
      }
      else {
        LOG.warn("Performance Plugin is missing or disabled; skipping YJP agent configuration");
        if (isAgentpathSet) {
          showSnapshotNotificationAfterFinish(project);
        }
      }
    }

    // debugging
    String debugPort = null;
    if (myBuildProcessDebuggingEnabled) {
      debugPort = StringUtil.nullize(Registry.stringValue("compiler.process.debug.port"));
      if (debugPort == null) {
        try {
          debugPort = String.valueOf(NetUtils.findAvailableSocketPort());
        }
        catch (IOException e) {
          throw new ExecutionException(JavaCompilerBundle.message("build.process.no.free.debug.port"), e);
        }
      }
      cmdLine.addParameter("-XX:+HeapDumpOnOutOfMemoryError");
      // Both formats "<host>:<port>" and "<port>" are accepted.
      // https://docs.oracle.com/en/java/javase/11/docs/specs/jpda/conninv.html#socket-transport
      cmdLine.addParameter("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=" + debugPort);
    }

    // portable caches
    if (Registry.is("compiler.process.use.portable.caches") &&
        CompilerCacheConfigurator.isServerUrlConfigured(project) &&
        CompilerCacheStartupActivity.isLineEndingsConfiguredCorrectly()) {
      cmdLine.addParameter("-D" + ProjectStamps.PORTABLE_CACHES_PROPERTY + "=true");
    }
    else {
      // Unified IC implementation is not tested with portable caches yet, so if caches are enabled, stick with the old implementation
      // DepGraph-based IC implementation
      if (AdvancedSettings.getBoolean("compiler.unified.ic.implementation")) {
        cmdLine.addParameter("-D" + GlobalOptions.DEPENDENCY_GRAPH_ENABLED + "=true");
      }
    }

    // Java compiler's VM should use the same default locale that IDEA uses in order for javac to print messages in 'correct' language
    cmdLine.setCharset(mySystemCharset);
    cmdLine.addParameter("-D" + CharsetToolkit.FILE_ENCODING_PROPERTY + '=' + mySystemCharset.name());
    for (String name : INHERITED_IDE_VM_OPTIONS) {
      final String value = System.getProperty(name);
      if (value != null) {
        cmdLine.addParameter("-D" + name + '=' + value);
      }
    }
    final DynamicBundle.LanguageBundleEP languageBundle = LocalizationUtil.INSTANCE.findLanguageBundle();
    if (languageBundle != null) {
      final PluginDescriptor pluginDescriptor = languageBundle.pluginDescriptor;
      final ClassLoader loader = pluginDescriptor == null ? null : pluginDescriptor.getClassLoader();
      final String bundlePath = loader == null ? null : PathManager.getResourceRoot(loader, "META-INF/plugin.xml");
      if (bundlePath != null) {
        cmdLine.addParameter("-D"+ GlobalOptions.LANGUAGE_BUNDLE + '=' + FileUtil.toSystemIndependentName(bundlePath));
      }
    }
    cmdLine.addPathParameter("-D" + PathManager.PROPERTY_HOME_PATH + '=', FileUtil.toSystemIndependentName(PathManager.getHomePath()));
    cmdLine.addPathParameter("-D" + PathManager.PROPERTY_CONFIG_PATH + '=', FileUtil.toSystemIndependentName(PathManager.getConfigPath()));
    cmdLine.addPathParameter("-D" + PathManager.PROPERTY_PLUGINS_PATH + '=', FileUtil.toSystemIndependentName(PathManager.getPluginsPath()));

    cmdLine.addPathParameter("-D" + GlobalOptions.LOG_DIR_OPTION + '=', FileUtil.toSystemIndependentName(getBuildLogDirectory().getAbsolutePath()));
    if (myFallbackSdkHome != null && myFallbackSdkVersion != null) {
      cmdLine.addPathParameter("-D" + GlobalOptions.FALLBACK_JDK_HOME + '=', myFallbackSdkHome);
      cmdLine.addParameter("-D" + GlobalOptions.FALLBACK_JDK_VERSION + '=' + myFallbackSdkVersion);
    }
    cmdLine.addParameter("-Dio.netty.noUnsafe=true");


    final File projectSystemRoot = getProjectSystemDirectory(project);
    cmdLine.addPathParameter("-Djava.io.tmpdir=", FileUtil.toSystemIndependentName(projectSystemRoot.getPath()) + "/" + TEMP_DIR_NAME);

    for (BuildProcessParametersProvider provider : BuildProcessParametersProvider.EP_NAME.getExtensions(project)) {
      for (String arg : provider.getVMArguments()) {
        cmdLine.addParameter(arg);
      }

      for (Pair<String, Path> parameter : provider.getPathParameters()) {
        cmdLine.addPathParameter(parameter.getFirst(), cmdLine.copyPathToTargetIfRequired(parameter.getSecond()));
      }
    }

    //noinspection SpellCheckingInspection
    cmdLine.addParameter("-Dide.propagate.context=false");

    @SuppressWarnings("UnnecessaryFullyQualifiedName") final Class<?> launcherClass = org.jetbrains.jps.cmdline.Launcher.class;

    final List<String> launcherCp = new ArrayList<>();
    launcherCp.add(ClasspathBootstrap.getResourcePath(launcherClass));
    launcherCp.addAll(BuildProcessClasspathManager.getLauncherClasspath(project));
    if (compilerPath != null) {   // can be null (in the case of JDK 9+)
      launcherCp.add(compilerPath);
    }

    boolean includeBundledEcj = shouldIncludeEclipseCompiler(projectConfig);
    File customEcjPath = null;
    if (includeBundledEcj) {
      final String path = EclipseCompilerConfiguration.getOptions(project, EclipseCompilerConfiguration.class).ECJ_TOOL_PATH;
      if (!StringUtil.isEmptyOrSpaces(path)) {
        customEcjPath = new File(path);
        if (customEcjPath.exists()) {
          includeBundledEcj = false;
        }
        else {
          throw new ExecutionException(JavaCompilerBundle.message("build.process.ecj.path.does.not.exist", customEcjPath.getAbsolutePath()));
          //customEcjPath = null;
        }
      }
    }

    ClasspathBootstrap.appendJavaCompilerClasspath(launcherCp, includeBundledEcj);
    if (customEcjPath != null) {
      launcherCp.add(customEcjPath.getAbsolutePath());
    }

    cmdLine.addParameter("-classpath");
    cmdLine.addClasspathParameter(launcherCp, Collections.emptyList());

    cmdLine.addParameter(launcherClass.getName());

    final List<String> cp = myClasspathManager.getBuildProcessClasspath(project);
    if (profileWithYourKit) {
      String yjpControllerFileName = "yjp-controller-api-redist.jar";
      Path yjpControllerPath = Path.of(cmdLine.getWorkingDirectory(), yjpControllerFileName);
      if (LOG.isDebugEnabled()) {
        LOG.debug("JPS process profiling with YourKit is enabled, " +
                  "adding '" + yjpControllerFileName + "' to classpath, " +
                  "full path '" + yjpControllerPath + "'");
      }
      if (!Files.exists(yjpControllerPath)) {
        LOG.warn("JPS process profiling is enabled, but '" + yjpControllerPath + "' is missing");
      }

      cmdLine.addClasspathParameter(cp, Collections.singletonList(yjpControllerFileName));
    }
    else {
      cmdLine.addClasspathParameter(cp, Collections.emptyList());
    }

    for (BuildProcessParametersProvider buildProcessParametersProvider : BuildProcessParametersProvider.EP_NAME.getExtensions(project)) {
      for (String path : buildProcessParametersProvider.getAdditionalPluginPaths()) {
        cmdLine.copyPathToTargetIfRequired(Paths.get(path));
      }
    }

    cmdLine.addParameter(BuildMain.class.getName());
    cmdLine.addParameter(buildProcessConnectHost);
    cmdLine.addParameter(Integer.toString(buildProcessConnectPort));
    cmdLine.addParameter(sessionId.toString());

    cmdLine.addParameter(cmdLine.getWorkingDirectory());

    boolean lowPriority = AdvancedSettings.getBoolean("compiler.lower.process.priority");
    if (SystemInfo.isUnix && lowPriority) {
      cmdLine.setUnixProcessPriority(10);
    }

    try {
      ApplicationManager.getApplication().getMessageBus().syncPublisher(BuildManagerListener.TOPIC).beforeBuildProcessStarted(project, sessionId);
    }
    catch (Throwable e) {
      LOG.error(e);
    }

    final OSProcessHandler processHandler = new OSProcessHandler(cmdLine.buildCommandLine()) {
      @Override
      protected boolean shouldDestroyProcessRecursively() {
        return true;
      }

      @Override
      protected @NotNull BaseOutputReader.Options readerOptions() {
        return BaseOutputReader.Options.BLOCKING;
      }
    };
    processHandler.addProcessListener(new ProcessAdapter() {
      @Override
      public void onTextAvailable(@NotNull ProcessEvent event, @NotNull Key outputType) {
        // re-translate builder's output to idea.log
        final String text = event.getText();
        if (!StringUtil.isEmptyOrSpaces(text)) {
          if (ProcessOutputTypes.SYSTEM.equals(outputType)) {
            if (LOG.isDebugEnabled()) {
              LOG.debug("BUILDER_PROCESS [" + outputType + "]: " + text.trim());
            }
          }
          else {
            LOG.info("BUILDER_PROCESS [" + outputType + "]: " + text.trim());
          }
        }
      }
    });
    if (debugPort != null) {
      processHandler.putUserData(COMPILER_PROCESS_DEBUG_HOST_PORT, debugPort);
    }

    if (SystemInfo.isWindows && lowPriority) {
      try {
        WinProcess winProcess = new WinProcess((int)processHandler.getProcess().pid());
        winProcess.setPriority(Priority.IDLE);
      }
      catch (UnsupportedOperationException ignored) {
        // Process.pid may throw this error. Just ignoring it.
      }
      catch (Throwable e) {
        LOG.error("Cannot set priority", e);
      }
    }

    return processHandler;
  }
private OSProcessHandler launchBuildProcess(@NotNull Project project, @NotNull UUID sessionId, boolean requestProjectPreload, @Nullable WSLDistribution projectWslDistribution, @Nullable ProgressIndicator progressIndicator)