IDEA-149864 Switch boot JDK on Windows shouldn't appear since unavailable
[idea/community.git] / platform / lang-impl / src / com / intellij / util / JdkBundle.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.util;
17
18 import com.intellij.execution.ExecutionException;
19 import com.intellij.execution.configurations.GeneralCommandLine;
20 import com.intellij.execution.util.ExecUtil;
21 import com.intellij.openapi.application.PathManager;
22 import com.intellij.openapi.diagnostic.Logger;
23 import com.intellij.openapi.util.Pair;
24 import com.intellij.openapi.util.SystemInfo;
25 import com.intellij.openapi.util.Version;
26 import org.jetbrains.annotations.NotNull;
27 import org.jetbrains.annotations.Nullable;
28
29 import java.io.File;
30 import java.util.List;
31 import java.util.regex.Pattern;
32
33 public class JdkBundle {
34   @NotNull private static final Logger LOG = Logger.getInstance("#com.intellij.util.JdkBundle");
35
36   @NotNull
37   private static final Pattern[] VERSION_UPDATE_PATTERNS = {
38     Pattern.compile("^java version \"([\\d]+\\.[\\d]+\\.[\\d]+)_([\\d]+)\".*", Pattern.MULTILINE),
39     Pattern.compile("^openjdk version \"([\\d]+\\.[\\d]+\\.[\\d]+)_([\\d]+).*\".*", Pattern.MULTILINE),
40     Pattern.compile("^[a-zA-Z() \"\\d]*([\\d]+\\.[\\d]+\\.?[\\d]*).*", Pattern.MULTILINE)
41   };
42
43   @NotNull
44   private static final Pattern ARCH_64_BIT_PATTERN = Pattern.compile(".*64-Bit.*", Pattern.MULTILINE);
45
46   @NotNull private File myBundleAsFile;
47   @NotNull private String myBundleName;
48   @Nullable private Pair<Version, Integer> myVersionUpdate;
49   private boolean myBoot;
50   private boolean myBundled;
51
52   JdkBundle(@NotNull File bundleAsFile,
53             @NotNull String bundleName,
54             @Nullable Pair<Version, Integer> versionUpdate, boolean boot, boolean bundled) {
55     myBundleAsFile = bundleAsFile;
56     myBundleName = bundleName;
57     myVersionUpdate = versionUpdate;
58     myBoot = boot;
59     myBundled = bundled;
60   }
61
62
63   @Nullable
64   public static JdkBundle createBundle(@NotNull File jvm, boolean boot, boolean bundled) {
65     String homeSubPath = SystemInfo.isMac ? "Contents/Home" : "";
66     return createBundle(jvm, homeSubPath, boot, bundled);
67   }
68
69   @Nullable
70   static JdkBundle createBundle(@NotNull File jvm, @NotNull String homeSubPath, boolean boot, boolean bundled) {
71     File javaHome = SystemInfo.isMac ? new File(jvm, homeSubPath) : jvm;
72     if (bundled) javaHome = new File(PathManager.getHomePath(), javaHome.getPath());
73
74     boolean isValidBundle = true;
75
76     String jreCheck = System.getProperty("idea.jre.check");
77     if (jreCheck != null && "true".equals(jreCheck)) {
78       isValidBundle = new File(javaHome, "lib" + File.separator + "tools.jar").exists();
79     }
80
81     if (!SystemInfo.isMac && !isValidBundle) return null; // Skip jre
82
83     File absJvmLocation = bundled ? new File(PathManager.getHomePath(), jvm.getPath()) : jvm;
84     Pair<Pair<String, Boolean>, Pair<Version, Integer>> nameArchVersionAndUpdate = getJDKNameArchVersionAndUpdate(absJvmLocation, homeSubPath);
85     if (nameArchVersionAndUpdate.first.second == null || (nameArchVersionAndUpdate.first.second != SystemInfo.is64Bit)) {
86       return null; // Skip unknown or incompatible arch
87     }
88
89     if (SystemInfo.isMac && nameArchVersionAndUpdate.second != null && nameArchVersionAndUpdate.second.first.isOrGreaterThan(1, 7) &&
90         !isValidBundle) {
91       return null; // Skip jre
92     }
93
94     return new JdkBundle(jvm, nameArchVersionAndUpdate.first.first, nameArchVersionAndUpdate.second, boot, bundled);
95   }
96
97   @Nullable
98   public static JdkBundle createBoot() {
99     return createBoot(true);
100   }
101
102   @Nullable
103   static JdkBundle createBoot(boolean adjustToMacBundle) {
104     File bootJDK = new File(System.getProperty("java.home")).getParentFile();
105     if (SystemInfo.isMac && adjustToMacBundle) {
106       bootJDK = bootJDK.getParentFile().getParentFile();
107       return createBundle(bootJDK, true, false);
108     }
109     return createBundle(bootJDK, "", true, false);
110   }
111
112   @NotNull
113   File getAbsoluteLocation() {
114     return myBundled ? new File(PathManager.getHomePath(), myBundleAsFile.getPath()) : myBundleAsFile;
115   }
116
117   @NotNull
118   public File getLocation() {
119     return myBundleAsFile;
120   }
121
122   public String getVisualRepresentation() {
123     StringBuilder representation = new StringBuilder(myBundleName);
124     if (myVersionUpdate != null) {
125       representation.append(myVersionUpdate.first.toString()).append((myVersionUpdate.second > 0 ? "_" + myVersionUpdate.second : ""));
126     }
127
128     if (myBoot || myBundled) {
129       representation.append(" [");
130       if (myBoot) representation.append(myBundled ? "boot, " : "boot");
131       if (myBundled) representation.append("bundled");
132       representation.append("]");
133     }
134     return representation.toString();
135   }
136
137   public void setBundled(boolean bundled) {
138     myBundled = bundled;
139   }
140
141   public boolean isBoot() {
142     return myBoot;
143   }
144
145   @NotNull
146   public String getBundleName() {
147     return myBundleName;
148   }
149
150   @Nullable
151   Pair<Version, Integer> getVersionUpdate() {
152     return myVersionUpdate;
153   }
154
155   @Nullable
156   public Version getVersion() {
157     return myVersionUpdate != null ? myVersionUpdate.first : null;
158   }
159
160   @NotNull
161   String getNameVersion() {
162     return myBundleName + ((myVersionUpdate != null) ? myVersionUpdate.first.toString() : "");
163   }
164
165   private static Pair<Pair<String, Boolean>, Pair<Version, Integer>> getJDKNameArchVersionAndUpdate(File jvm, String homeSubPath) {
166     GeneralCommandLine commandLine = new GeneralCommandLine().withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.NONE);
167     commandLine.setExePath(new File(jvm,  homeSubPath + File.separator +  "jre" +
168                            File.separator + "bin" + File.separator + "java").getAbsolutePath());
169     commandLine.addParameter("-version");
170
171     String displayVersion;
172     Boolean is64Bit = null;
173     Pair<Version, Integer> versionAndUpdate = null;
174     List<String> outputLines = null;
175
176     try {
177       outputLines = ExecUtil.execAndGetOutput(commandLine).getStderrLines();
178     }
179     catch (ExecutionException e) {
180       // Checking for jdk 6 on mac
181       if (SystemInfo.isMac) {
182         commandLine.setExePath(new File(jvm,  homeSubPath + File.separator +  "bin" + File.separator + "java").getAbsolutePath());
183         try {
184           outputLines = ExecUtil.execAndGetOutput(commandLine).getStderrLines();
185         }
186         catch (ExecutionException e1) {
187           LOG.debug(e);
188         }
189       }
190       LOG.debug(e);
191     }
192
193     if (outputLines != null && outputLines.size() >= 1) {
194       String versionLine = outputLines.get(0);
195       versionAndUpdate = VersionUtil.parseVersionAndUpdate(versionLine, VERSION_UPDATE_PATTERNS);
196       displayVersion = versionLine.replaceFirst("\".*\"", "");
197       if (outputLines.size() >= 3) {
198         String archLine = outputLines.get(2);
199         is64Bit = ARCH_64_BIT_PATTERN.matcher(archLine).find();
200       }
201     }
202     else {
203       displayVersion = jvm.getName();
204     }
205
206     return Pair.create(Pair.create(displayVersion, is64Bit), versionAndUpdate);
207   }
208
209   public boolean isBundled() {
210     return myBundled;
211   }
212
213   public void setBoot(boolean boot) {
214     myBoot = boot;
215   }
216 }