afe0350f3d5331d0e1b8a1cd7d8ffde8468a80cd
[idea/community.git] / jps / jps-builders / src / org / jetbrains / jps / incremental / ExternalProcessUtil.java
1 // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package org.jetbrains.jps.incremental;
3
4 import com.intellij.execution.CommandLineWrapperUtil;
5 import com.intellij.openapi.diagnostic.Logger;
6 import com.intellij.openapi.util.text.StringUtil;
7 import org.jetbrains.jps.cmdline.ClasspathBootstrap;
8
9 import java.io.*;
10 import java.nio.charset.Charset;
11 import java.util.ArrayList;
12 import java.util.Arrays;
13 import java.util.Collections;
14 import java.util.List;
15 import java.util.jar.Manifest;
16
17 /**
18  * @author Eugene Zhuravlev
19  */
20 public class ExternalProcessUtil {
21   private static final Logger LOG = Logger.getInstance(ExternalProcessUtil.class);
22
23   private static class CommandLineWrapperClassHolder {
24     static final Class<?> ourWrapperClass;
25     static {
26       Class<?> aClass = null;
27       try {
28         aClass = Class.forName("com.intellij.rt.execution.CommandLineWrapper");
29       }
30       catch (Throwable ignored) { }
31       ourWrapperClass = aClass;
32     }
33   }
34
35   public static List<String> buildJavaCommandLine(String javaExecutable,
36                                                   String mainClass,
37                                                   List<String> bootClasspath,
38                                                   List<String> classpath,
39                                                   List<String> vmParams,
40                                                   List<String> programParams) {
41     return buildJavaCommandLine(javaExecutable, mainClass, bootClasspath, classpath, vmParams, programParams, true);
42   }
43
44   public static List<String> buildJavaCommandLine(String javaExecutable,
45                                                   String mainClass,
46                                                   List<String> bootClasspath,
47                                                   List<String> classpath,
48                                                   List<String> vmParams,
49                                                   List<String> programParams,
50                                                   boolean shortenClasspath) {
51     return buildJavaCommandLine(javaExecutable, mainClass, bootClasspath, classpath, vmParams, programParams, shortenClasspath, true);
52   }
53
54   public static List<String> buildJavaCommandLine(String javaExecutable,
55                                                   String mainClass,
56                                                   List<String> bootClasspath,
57                                                   List<String> classpath,
58                                                   List<String> vmParams,
59                                                   List<String> programParams,
60                                                   boolean shortenClasspath,
61                                                   boolean preferClasspathJar) {
62     List<String> cmdLine = new ArrayList<>();
63
64     cmdLine.add(javaExecutable);
65
66     cmdLine.addAll(vmParams);
67
68     if (!bootClasspath.isEmpty()) {
69       cmdLine.add("-bootclasspath");
70       cmdLine.add(StringUtil.join(bootClasspath, File.pathSeparator));
71     }
72
73     if (!classpath.isEmpty()) {
74       List<String> shortenedCp = null;
75
76       if (shortenClasspath) {
77         try {
78           Charset cs = Charset.defaultCharset();  // todo detect JNU charset from VM options?
79           if (isModularRuntime(javaExecutable)) {
80             List<String> args = Arrays.asList("-classpath", StringUtil.join(classpath, File.pathSeparator));
81             File argFile = CommandLineWrapperUtil.createArgumentFile(args, cs);
82             shortenedCp = Collections.singletonList('@' + argFile.getAbsolutePath());
83           }
84           else if (preferClasspathJar) {
85             File classpathJar = CommandLineWrapperUtil.createClasspathJarFile(new Manifest(), classpath);
86             shortenedCp = Arrays.asList("-classpath", classpathJar.getAbsolutePath());
87           }
88           else {
89             Class<?> wrapperClass = CommandLineWrapperClassHolder.ourWrapperClass;
90             if (wrapperClass != null) {
91               File classpathFile = CommandLineWrapperUtil.createWrapperFile(classpath, cs);
92               shortenedCp = Arrays.asList(
93                 "-classpath", ClasspathBootstrap.getResourcePath(wrapperClass), wrapperClass.getName(), classpathFile.getAbsolutePath());
94             }
95             else {
96               LOG.info("CommandLineWrapper class not found; classpath shortening won't be used");
97             }
98           }
99         }
100         catch (IOException e) {
101           LOG.warn("can't create temp file; classpath shortening won't be used", e);
102         }
103       }
104
105       // classpath
106       if (shortenedCp != null) {
107         cmdLine.addAll(shortenedCp);
108       }
109       else {
110         cmdLine.add("-classpath");
111         cmdLine.add(StringUtil.join(classpath, File.pathSeparator));
112       }
113     }
114
115     // main class and params
116     cmdLine.add(mainClass);
117
118     cmdLine.addAll(programParams);
119
120     return cmdLine;
121   }
122
123   private static boolean isModularRuntime(String javaExec) {
124     File jreHome = new File(javaExec).getParentFile().getParentFile();
125     return jreHome != null && (new File(jreHome, "lib/jrt-fs.jar").isFile() || new File(jreHome, "modules/java.base").isDirectory());
126   }
127 }