diff options
author | Raphael <raphael@google.com> | 2012-01-30 14:41:44 -0800 |
---|---|---|
committer | Raphael <raphael@google.com> | 2012-01-31 14:47:59 -0800 |
commit | 6123b532fc0e57e33a09dc429f8ec708a88aedcc (patch) | |
tree | 40848e20d7b32dabd4f21f359a0a76db6c6f1217 /find_java | |
parent | 56233709efa0ab5b41b22a83d325fe51de9009a6 (diff) | |
download | sdk-6123b532fc0e57e33a09dc429f8ec708a88aedcc.zip sdk-6123b532fc0e57e33a09dc429f8ec708a88aedcc.tar.gz sdk-6123b532fc0e57e33a09dc429f8ec708a88aedcc.tar.bz2 |
Win SDK: find max version of java.exe
When we find a java.exe file, run java -version and
parse its version number. Only accept java 1.5 or
better.
There's a moderate effort to find the highest
version available in each category: for starter
scripts, it doesn't matter if we actually have
the highest. However within a given category
(env path, program files, registry), picking
up the highest available make sense. In normal
cases users won't have many concurrent versions.
Change-Id: I4f2504642a1712b62aa303562578572066d82d3b
Diffstat (limited to 'find_java')
-rwxr-xr-x | find_java/find_java.h | 14 | ||||
-rw-r--r-- | find_java/find_java_exe.cpp | 44 | ||||
-rwxr-xr-x | find_java/find_java_lib.cpp | 279 |
3 files changed, 242 insertions, 95 deletions
diff --git a/find_java/find_java.h b/find_java/find_java.h index b578903..0a61e73 100755 --- a/find_java/find_java.h +++ b/find_java/find_java.h @@ -21,10 +21,16 @@ #include "utils.h"
-bool findJavaInEnvPath(CPath *outJavaPath);
-bool findJavaInRegistry(CPath *outJavaPath);
-bool findJavaInProgramFiles(CPath *outJavaPath);
-bool getJavaVersion(CPath &javaPath, CString *version);
+// We currently search for a Java version for at least 1.5
+#define MIN_JAVA_VERSION_MAJOR 1
+#define MIN_JAVA_VERSION_MINOR 5
+#define MIN_JAVA_VERSION (MIN_JAVA_VERSION_MAJOR * 1000 + MIN_JAVA_VERSION_MINOR)
+
+
+int findJavaInEnvPath(CPath *outJavaPath);
+int findJavaInRegistry(CPath *outJavaPath);
+int findJavaInProgramFiles(CPath *outJavaPath);
+bool getJavaVersion(CPath &javaPath, CString *outVersionStr, int *outVersionInt);
#endif /* _WIN32 */
#endif /* _H_FIND_JAVA */
diff --git a/find_java/find_java_exe.cpp b/find_java/find_java_exe.cpp index c74cfd9..c8e0e49 100644 --- a/find_java/find_java_exe.cpp +++ b/find_java/find_java_exe.cpp @@ -46,16 +46,16 @@ static void testFindJava() {
CPath javaPath("<not found>");
- bool ok = findJavaInEnvPath(&javaPath);
- printf("findJavaInEnvPath: [%s] %s\n", ok ? "OK" : "FAIL", javaPath.cstr());
+ int v = findJavaInEnvPath(&javaPath);
+ printf("findJavaInEnvPath: [%d] %s\n", v, javaPath.cstr());
javaPath.set("<not found>");
- ok = findJavaInRegistry(&javaPath);
- printf("findJavaInRegistry [%s] %s\n", ok ? "OK" : "FAIL", javaPath.cstr());
+ v = findJavaInRegistry(&javaPath);
+ printf("findJavaInRegistry [%d] %s\n", v, javaPath.cstr());
javaPath.set("<not found>");
- ok = findJavaInProgramFiles(&javaPath);
- printf("findJavaInProgramFiles [%s] %s\n", ok ? "OK" : "FAIL", javaPath.cstr());
+ v = findJavaInProgramFiles(&javaPath);
+ printf("findJavaInProgramFiles [%d] %s\n", v, javaPath.cstr());
}
@@ -99,14 +99,20 @@ int main(int argc, char* argv[]) { }
}
+ // Find the first suitable version of Java we can use.
CPath javaPath;
- if (!findJavaInEnvPath(&javaPath) &&
- !findJavaInRegistry(&javaPath) &&
- !findJavaInProgramFiles(&javaPath)) {
- if (gIsDebug) {
- fprintf(stderr, "Failed to find Java on your system.\n");
- }
- return 1;
+ int version = findJavaInEnvPath(&javaPath);
+ if (version < MIN_JAVA_VERSION) {
+ version = findJavaInRegistry(&javaPath);
+ }
+ if (version < MIN_JAVA_VERSION) {
+ version = findJavaInProgramFiles(&javaPath);
+ }
+ if (version < MIN_JAVA_VERSION || javaPath.isEmpty()) {
+ if (gIsDebug) {
+ fprintf(stderr, "Failed to find Java on your system.\n");
+ }
+ return 1;
}
_ASSERT(!javaPath.isEmpty());
@@ -120,14 +126,10 @@ int main(int argc, char* argv[]) { }
if (doVersion) {
- // Print version found
- CString version;
- if (getJavaVersion(javaPath, &version)) {
- printf("%s", version.cstr());
- return 0;
- } else {
- fprintf(stderr, "Failed to get version of %s\n", javaPath.cstr());
- }
+ // Print version found. We already have the version as an integer
+ // so we don't need to run java -version a second time.
+ printf("%d.%d", version / 1000, version % 1000);
+ return 0;
}
if (doJavaW) {
diff --git a/find_java/find_java_lib.cpp b/find_java/find_java_lib.cpp index 5a1c3f8..4c04e7f 100755 --- a/find_java/find_java_lib.cpp +++ b/find_java/find_java_lib.cpp @@ -35,18 +35,57 @@ typedef LONG LSTATUS;
#endif
-// Check whether we can find $PATH/java.exe
-static bool checkPath(CPath *inOutPath) {
+
+// Extract the first thing that looks like (digit.digit+).
+// Note: this will break when java reports a version with major > 9.
+// However it will reasonably cope with "1.10", if that ever happens.
+static bool extractJavaVersion(const char *start,
+ int length,
+ CString *outVersionStr,
+ int *outVersionInt) {
+ const char *end = start + length;
+ for (const char *c = start; c < end - 2; c++) {
+ if (isdigit(c[0]) &&
+ c[1] == '.' &&
+ isdigit(c[2])) {
+ const char *e = c+2;
+ while (isdigit(e[1])) {
+ e++;
+ }
+ if (outVersionStr != NULL) {
+ outVersionStr->set(c, e - c + 1);
+ }
+ if (outVersionInt != NULL) {
+ // add major * 1000, currently only 1 digit
+ int value = (*c - '0') * 1000;
+ // add minor
+ for (int m = 1; *e != '.'; e--, m *= 10) {
+ value += (*e - '0') * m;
+ }
+ *outVersionInt = value;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+// Check whether we can find $PATH/java.exe.
+// inOutPath should be the directory where we're looking at.
+// In output, it will be the java path we tested.
+// Returns the java version integer found (e.g. 1006 for 1.6).
+// Return 0 in case of error.
+static int checkPath(CPath *inOutPath) {
inOutPath->addPath("java.exe");
- bool result = false;
+ int result = 0;
PVOID oldWow64Value = disableWow64FsRedirection();
if (inOutPath->fileExists()) {
- // Make sure we can actually run "java -version".
- CString cmd;
- cmd.setf("\"%s\" -version", inOutPath->cstr());
- int code = execWait(cmd.cstr());
- result = (code == 0);
+ // Run java -version
+ // Reject the version if it's not at least our current minimum.
+ if (!getJavaVersion(*inOutPath, NULL /*versionStr*/, &result)) {
+ result = 0;
+ }
}
revertWow64FsRedirection(oldWow64Value);
@@ -54,46 +93,64 @@ static bool checkPath(CPath *inOutPath) { }
// Check whether we can find $PATH/bin/java.exe
-static bool checkBinPath(CPath *inOutPath) {
+// Returns the Java version found (e.g. 1006 for 1.6) or 0 in case of error.
+static int checkBinPath(CPath *inOutPath) {
inOutPath->addPath("bin");
return checkPath(inOutPath);
}
// Search java.exe in the environment
-bool findJavaInEnvPath(CPath *outJavaPath) {
+int findJavaInEnvPath(CPath *outJavaPath) {
SetLastError(0);
+ int currVersion = 0;
+
const char* envPath = getenv("JAVA_HOME");
if (envPath != NULL) {
CPath p(envPath);
- if (checkBinPath(&p)) {
- if (gIsDebug) msgBox("Java found via JAVA_HOME: %s", p.cstr());
+ currVersion = checkBinPath(&p);
+ if (currVersion > 0) {
+ if (gIsDebug) {
+ fprintf(stderr, "Java %d found via JAVA_HOME: %s\n", currVersion, p.cstr());
+ }
*outJavaPath = p;
- return true;
+ }
+ if (currVersion >= MIN_JAVA_VERSION) {
+ // As an optimization for runtime, if we find a suitable java
+ // version in JAVA_HOME we won't waste time looking at the PATH.
+ return currVersion;
}
}
envPath = getenv("PATH");
- if (!envPath) return false;
+ if (!envPath) return currVersion;
+
+ // Otherwise look at the entries in the current path.
+ // If we find more than one, keep the one with the highest version.
CArray<CString> *paths = CString(envPath).split(';');
for(int i = 0; i < paths->size(); i++) {
CPath p((*paths)[i].cstr());
- if (checkPath(&p)) {
- if (gIsDebug) msgBox("Java found via env PATH: %s", p.cstr());
+ int v = checkPath(&p);
+ if (v > currVersion) {
+ if (gIsDebug) {
+ fprintf(stderr, "Java %d found via env PATH: %s\n", v, p.cstr());
+ }
+ currVersion = v;
*outJavaPath = p;
- delete paths;
- return true;
}
}
delete paths;
- return false;
+ return currVersion;
}
// --------------
-bool getRegValue(const char *keyPath, const char *keyName, REGSAM access, CString *outValue) {
+static bool getRegValue(const char *keyPath,
+ const char *keyName,
+ REGSAM access,
+ CString *outValue) {
HKEY key;
LSTATUS status = RegOpenKeyExA(
HKEY_LOCAL_MACHINE, // hKey
@@ -135,67 +192,149 @@ bool getRegValue(const char *keyPath, const char *keyName, REGSAM access, CStrin return false;
}
-bool exploreJavaRegistry(const char *entry, REGSAM access, CPath *outJavaPath) {
+// Explore the registry to find a suitable version of Java.
+// Returns an int which is the version of Java found (e.g. 1006 for 1.6) and the
+// matching path in outJavaPath.
+// Returns 0 if nothing suitable was found.
+static int exploreJavaRegistry(const char *entry, REGSAM access, CPath *outJavaPath) {
// Let's visit HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment [CurrentVersion]
- CPath subKey("SOFTWARE\\JavaSoft\\");
- subKey.addPath(entry);
+ CPath rootKey("SOFTWARE\\JavaSoft\\");
+ rootKey.addPath(entry);
- CString currVersion;
- if (getRegValue(subKey.cstr(), "CurrentVersion", access, &currVersion)) {
+ int versionInt = 0;
+ CString currentVersion;
+ CPath subKey(rootKey);
+ if (getRegValue(subKey.cstr(), "CurrentVersion", access, ¤tVersion)) {
// CurrentVersion should be something like "1.7".
// We want to read HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment\1.7 [JavaHome]
- subKey.addPath(currVersion);
+ subKey.addPath(currentVersion);
CPath javaHome;
if (getRegValue(subKey.cstr(), "JavaHome", access, &javaHome)) {
- if (checkBinPath(&javaHome)) {
+ versionInt = checkBinPath(&javaHome);
+ if (versionInt >= 0) {
+ if (gIsDebug) {
+ fprintf(stderr,
+ "Java %d found via registry: %s\n",
+ versionInt, javaHome.cstr());
+ }
*outJavaPath = javaHome;
- return true;
+ }
+ if (versionInt >= MIN_JAVA_VERSION) {
+ // Heuristic: if the current version is good enough, stop here
+ return versionInt;
}
}
}
+ // Try again, but this time look at all the versions available
+ HKEY javaHomeKey;
+ LSTATUS status = RegOpenKeyExA(
+ HKEY_LOCAL_MACHINE, // hKey
+ "SOFTWARE\\JavaSoft", // lpSubKey
+ 0, // ulOptions
+ KEY_READ | access, // samDesired
+ &javaHomeKey); // phkResult
+ if (status == ERROR_SUCCESS) {
+ char name[256];
+ DWORD index = 0;
+ CPath javaHome;
+ for (LONG result = ERROR_SUCCESS; result == ERROR_SUCCESS; index++) {
+ DWORD nameLen = 255;
+ name[nameLen] = 0;
+ result = RegEnumKeyExA(
+ javaHomeKey, // hKey
+ index, // dwIndex
+ name, // lpName
+ &nameLen, // lpcName
+ NULL, // lpReserved
+ NULL, // lpClass
+ NULL, // lpcClass,
+ NULL); // lpftLastWriteTime
+ if (result == ERROR_SUCCESS && nameLen < 256) {
+ name[nameLen] = 0;
+ CPath subKey(rootKey);
+ subKey.addPath(name);
+
+ if (getRegValue(subKey.cstr(), "JavaHome", access, &javaHome)) {
+ int v = checkBinPath(&javaHome);
+ if (v > versionInt) {
+ if (gIsDebug) {
+ fprintf(stderr,
+ "Java %d found via registry: %s\n",
+ versionInt, javaHome.cstr());
+ }
+ *outJavaPath = javaHome;
+ versionInt = v;
+ }
+ }
+ }
+ }
+
+ RegCloseKey(javaHomeKey);
+ }
+
+ return 0;
+}
+
+static bool getMaxJavaInRegistry(const char *entry, REGSAM access, CPath *outJavaPath, int *inOutVersion) {
+ CPath path;
+ int version = exploreJavaRegistry(entry, access, &path);
+ if (version > *inOutVersion) {
+ *outJavaPath = path;
+ *inOutVersion = version;
+ return true;
+ }
return false;
}
-bool findJavaInRegistry(CPath *outJavaPath) {
+int findJavaInRegistry(CPath *outJavaPath) {
// We'll do the registry test 3 times: first using the default mode,
// then forcing the use of the 32-bit registry then forcing the use of
// 64-bit registry. On Windows 2k, the 2 latter will fail since the
// flags are not supported. On a 32-bit OS the 64-bit is obviously
- // useless and the 2 first test should be equivalent so we just
+ // useless and the 2 first tests should be equivalent so we just
// need the first case.
// Check the JRE first, then the JDK.
- if (exploreJavaRegistry("Java Runtime Environment", 0, outJavaPath) ||
- exploreJavaRegistry("Java Development Kit", 0, outJavaPath)) {
- return true;
- }
+ int version = MIN_JAVA_VERSION - 1;
+ bool result = false;
+ result |= getMaxJavaInRegistry("Java Runtime Environment", 0, outJavaPath, &version);
+ result |= getMaxJavaInRegistry("Java Development Kit", 0, outJavaPath, &version);
- // Check the real sysinfo state (not the one hidden by WOW64) for x86
+ // Get the app sysinfo state (the one hidden by WOW64)
SYSTEM_INFO sysInfo;
+ GetSystemInfo(&sysInfo);
+ WORD programArch = sysInfo.wProcessorArchitecture;
+ // Check the real sysinfo state (not the one hidden by WOW64) for x86
GetNativeSystemInfo(&sysInfo);
+ WORD actualArch = sysInfo.wProcessorArchitecture;
// Only try to access the WOW64-32 redirected keys on a 64-bit system.
- // There's no point in doing that on a 32-bit system.
- if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
- if (exploreJavaRegistry("Java Runtime Environment", KEY_WOW64_32KEY, outJavaPath) ||
- exploreJavaRegistry("Java Development Kit", KEY_WOW64_32KEY, outJavaPath)) {
- return true;
- }
-
- if (exploreJavaRegistry("Java Runtime Environment", KEY_WOW64_64KEY, outJavaPath) ||
- exploreJavaRegistry("Java Development Kit", KEY_WOW64_64KEY, outJavaPath)) {
- return true;
+ // There's no point in doing this on a 32-bit system.
+ if (actualArch == PROCESSOR_ARCHITECTURE_AMD64) {
+ if (programArch != PROCESSOR_ARCHITECTURE_INTEL) {
+ // If we did the 32-bit case earlier, don't do it twice.
+ result |= getMaxJavaInRegistry(
+ "Java Runtime Environment", KEY_WOW64_32KEY, outJavaPath, &version);
+ result |= getMaxJavaInRegistry(
+ "Java Development Kit", KEY_WOW64_32KEY, outJavaPath, &version);
+
+ } else if (programArch != PROCESSOR_ARCHITECTURE_AMD64) {
+ // If we did the 64-bit case earlier, don't do it twice.
+ result |= getMaxJavaInRegistry(
+ "Java Runtime Environment", KEY_WOW64_64KEY, outJavaPath, &version);
+ result |= getMaxJavaInRegistry(
+ "Java Development Kit", KEY_WOW64_64KEY, outJavaPath, &version);
}
}
- return false;
+ return result ? version : 0;
}
// --------------
-static bool checkProgramFiles(CPath *outJavaPath) {
+static bool checkProgramFiles(CPath *outJavaPath, int *inOutVersion) {
char programFilesPath[MAX_PATH + 1];
HRESULT result = SHGetFolderPathA(
@@ -223,9 +362,11 @@ static bool checkProgramFiles(CPath *outJavaPath) { if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
CPath temp(path);
temp.addPath(findData.cFileName);
- // Check C:\\Program Files[x86]\\Java\\{jdk,jre}*\\bin\\java.exe
- if (checkBinPath(&temp)) {
+ // Check C:\\Program Files[x86]\\Java\\j*\\bin\\java.exe
+ int v = checkBinPath(&temp);
+ if (v > *inOutVersion) {
found = true;
+ *inOutVersion = v;
*outJavaPath = temp;
}
}
@@ -235,11 +376,14 @@ static bool checkProgramFiles(CPath *outJavaPath) { return found;
}
-bool findJavaInProgramFiles(CPath *outJavaPath) {
+int findJavaInProgramFiles(CPath *outJavaPath) {
+
// Check the C:\\Program Files (x86) directory
// With WOW64 fs redirection in place by default, we should get the x86
// version on a 64-bit OS since this app is a 32-bit itself.
- if (checkProgramFiles(outJavaPath)) return true;
+ bool result = false;
+ int version = MIN_JAVA_VERSION - 1;
+ result |= checkProgramFiles(outJavaPath, &version);
// Check the real sysinfo state (not the one hidden by WOW64) for x86
SYSTEM_INFO sysInfo;
@@ -249,17 +393,21 @@ bool findJavaInProgramFiles(CPath *outJavaPath) { // On a 64-bit OS, try again by disabling the fs redirection so
// that we can try the real C:\\Program Files directory.
PVOID oldWow64Value = disableWow64FsRedirection();
- bool found = checkProgramFiles(outJavaPath);
+ result |= checkProgramFiles(outJavaPath, &version);
revertWow64FsRedirection(oldWow64Value);
- return found;
}
- return false;
+ return result ? version : 0;
}
// --------------
-bool getJavaVersion(CPath &javaPath, CString *version) {
+
+// Tries to invoke the java.exe at the given path and extract it's
+// version number.
+// - outVersionStr: if not null, will capture version as a string (e.g. "1.6")
+// - outVersionInt: if not null, will capture version as an int (major * 1000 + minor, e.g. 1006).
+bool getJavaVersion(CPath &javaPath, CString *outVersionStr, int *outVersionInt) {
bool result = false;
// Run "java -version", which outputs something like to *STDERR*:
@@ -291,11 +439,11 @@ bool getJavaVersion(CPath &javaPath, CString *version) { &stdoutPipeWt, // hWritePipe,
&saAttr, // lpPipeAttributes,
0)) { // nSize (0=default buffer size)
- displayLastError("CreatePipe failed: ");
+ if (gIsConsole || gIsDebug) displayLastError("CreatePipe failed: ");
return false;
}
if (!SetHandleInformation(stdoutPipeRd, HANDLE_FLAG_INHERIT, 0)) {
- displayLastError("SetHandleInformation failed: ");
+ if (gIsConsole || gIsDebug) displayLastError("SetHandleInformation failed: ");
return false;
}
@@ -322,7 +470,7 @@ bool getJavaVersion(CPath &javaPath, CString *version) { &startup, // startup info, i.e. std handles
&pinfo);
- if (gIsConsole && !ok) displayLastError("CreateProcess failed: ");
+ if ((gIsConsole || gIsDebug) && !ok) displayLastError("CreateProcess failed: ");
// Close the write-end of the output pipe (we're only reading from it)
CloseHandle(stdoutPipeWt);
@@ -376,26 +524,17 @@ bool getJavaVersion(CPath &javaPath, CString *version) { }
CloseHandle(stdoutPipeRd);
- if (index > 0) {
+ if (result && index > 0) {
// Look for a few keywords in the output however we don't
// care about specific ordering or case-senstiviness.
// We only captures roughtly the first line in lower case.
char *j = strstr(first32, "java");
char *v = strstr(first32, "version");
- if (gIsDebug && gIsConsole && (!j || !v)) {
+ if ((gIsConsole || gIsDebug) && (!j || !v)) {
fprintf(stderr, "Error: keywords 'java version' not found in '%s'\n", first32);
}
if (j != NULL && v != NULL) {
- // Now extract the first thing that looks like digit.digit
- for (int i = 0; i < index - 2; i++) {
- if (isdigit(first32[i]) &&
- first32[i+1] == '.' &&
- isdigit(first32[i+2])) {
- version->set(first32 + i, 3);
- result = true;
- break;
- }
- }
+ result = extractJavaVersion(first32, index, outVersionStr, outVersionInt);
}
}
|