From d2d6999d779fffe976c55db04bf523907a8743ed Mon Sep 17 00:00:00 2001 From: Raphael Date: Tue, 24 Jan 2012 15:06:56 -0800 Subject: Windows: "find_java" exe and lib for android.bat This adds a "find_java.exe" that will be packages in SDK/tools/lib. It will be used by android.bat and the other launchers to locate the best version of java to use for our tools (currently we have a find_java.bat that uses DOS commands to achieve something similar but more limited). In addition this creates a static "findjavalib" that is used by the NSIS installer to locate java and get its version (to complain in case we only find a Java 1.4 or lesser). The goal is for the installer to use the same logic as the tools will use to locate the java binary. Change-Id: Ic2efb388135087bab9687c3332882047fd041b1c --- find_java/.gitignore | 1 + find_java/Android.mk | 70 +++++++ find_java/NOTICE | 190 +++++++++++++++++++ find_java/find_java.exe.manifest | 33 ++++ find_java/find_java.h | 30 +++ find_java/find_java_exe.cpp | 133 +++++++++++++ find_java/find_java_lib.cpp | 388 ++++++++++++++++++++++++++++++++++++++ find_java/images/android_icon.ico | Bin 0 -> 300318 bytes find_java/images/android_icon.rc | 2 + find_java/utils.cpp | 202 ++++++++++++++++++++ find_java/utils.h | 375 ++++++++++++++++++++++++++++++++++++ 11 files changed, 1424 insertions(+) create mode 100644 find_java/.gitignore create mode 100644 find_java/Android.mk create mode 100644 find_java/NOTICE create mode 100755 find_java/find_java.exe.manifest create mode 100755 find_java/find_java.h create mode 100644 find_java/find_java_exe.cpp create mode 100755 find_java/find_java_lib.cpp create mode 100644 find_java/images/android_icon.ico create mode 100644 find_java/images/android_icon.rc create mode 100755 find_java/utils.cpp create mode 100755 find_java/utils.h (limited to 'find_java') diff --git a/find_java/.gitignore b/find_java/.gitignore new file mode 100644 index 0000000..c2e7014 --- /dev/null +++ b/find_java/.gitignore @@ -0,0 +1 @@ +images/find_java_icon.o diff --git a/find_java/Android.mk b/find_java/Android.mk new file mode 100644 index 0000000..892f772 --- /dev/null +++ b/find_java/Android.mk @@ -0,0 +1,70 @@ +# Copyright 2011 The Android Open Source Project +# +# Android.mk for find_java.exe & static library + + +LOCAL_PATH := $(call my-dir) + +# find_java static library for host (used by find_java.exe and installer) +# ======================================================================= + +include $(CLEAR_VARS) + +LOCAL_MODULE := libfindjava +LOCAL_SRC_FILES := find_java.cpp utils.cpp + +LOCAL_CFLAGS += -Wall -Wno-unused-parameter +LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE -DSH_HISTORY -DUSE_MINGW + +include $(BUILD_HOST_STATIC_LIBRARY) + + +# "find_java.exe", to be used from android.bat & co +# ================================================= + +include $(CLEAR_VARS) + +ifeq ($(HOST_OS),windows) + +LOCAL_SRC_FILES := \ + win_android.cpp + +LOCAL_MODULE := find_java +LOCAL_STATIC_LIBRARIES := libfindjava + +LOCAL_CFLAGS += -Wall -Wno-unused-parameter +LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE -DSH_HISTORY -DUSE_MINGW + +LOCAL_MODULE_TAGS := optional + +# Locate windres executable +WINDRES := windres +ifneq ($(USE_MINGW),) + # When building the Windows resources under Linux, use the MinGW one + WINDRES := i586-mingw32msvc-windres +endif + +# Link the Windows icon file as well into the executable, based on the technique +# used in external/qemu/Makefile.android. The variables need to have different +# names to not interfere with the ones from qemu/Makefile.android. +# +INTERMEDIATE := $(call intermediates-dir-for,EXECUTABLES,$(LOCAL_MODULE),true) +FIND_JAVA_ICON_OBJ := find_java_icon.o +FIND_JAVA_ICON_PATH := $(LOCAL_PATH)/images +$(FIND_JAVA_ICON_PATH)/$(FIND_JAVA_ICON_OBJ): $(FIND_JAVA_ICON_PATH)/android_icon.rc + $(WINDRES) $< -I $(FIND_JAVA_ICON_PATH) -o $@ + +# seems to be the only way to add an object file that was not generated from +# a C/C++/Java source file to our build system. and very unfortunately, +# $(TOPDIR)/$(LOCALPATH) will always be prepended to this value, which forces +# us to put the object file in the source directory... +# +LOCAL_PREBUILT_OBJ_FILES += images/$(FIND_JAVA_ICON_OBJ) + +include $(BUILD_HOST_EXECUTABLE) + +$(call dist-for-goals,droid,$(LOCAL_BUILT_MODULE)) + +endif + + diff --git a/find_java/NOTICE b/find_java/NOTICE new file mode 100644 index 0000000..7317ae2 --- /dev/null +++ b/find_java/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2011, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/find_java/find_java.exe.manifest b/find_java/find_java.exe.manifest new file mode 100755 index 0000000..d949f07 --- /dev/null +++ b/find_java/find_java.exe.manifest @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + Utility to find java.exe on the local system. + + + + + + + + + + diff --git a/find_java/find_java.h b/find_java/find_java.h new file mode 100755 index 0000000..b578903 --- /dev/null +++ b/find_java/find_java.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _H_FIND_JAVA +#define _H_FIND_JAVA + +#ifdef _WIN32 + +#include "utils.h" + +bool findJavaInEnvPath(CPath *outJavaPath); +bool findJavaInRegistry(CPath *outJavaPath); +bool findJavaInProgramFiles(CPath *outJavaPath); +bool getJavaVersion(CPath &javaPath, CString *version); + +#endif /* _WIN32 */ +#endif /* _H_FIND_JAVA */ diff --git a/find_java/find_java_exe.cpp b/find_java/find_java_exe.cpp new file mode 100644 index 0000000..74f8ac1 --- /dev/null +++ b/find_java/find_java_exe.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * "find_java.exe", for Windows only. + * Tries to find a Java binary in a variety of places and prints the + * first one found on STDOUT and returns 0. + * + * If not found, returns error 1 with no message + * (unless ANDROID_SDKMAN_DEBUG or -d if set, in which case there's a message on STDERR). + * + * Implementation details: + * - We don't have access to ATL or MFC. + * - We don't want to pull in things like STL. + * - No Unicode/MBCS support for now. + * + * TODO for later version: + * - provide an env variable to let users override which version is being used. + * - if there's more than one java.exe found, enumerate them all. + * - and in that case take the one with the highest Java version number. + * - since that operation is expensive, do it only once and cache the result + * in a temp file. If the temp file is not found or the java binary no + * longer exists, re-run the enumaration. + */ + +#ifdef _WIN32 + +#include "utils.h" +#include "find_java.h" +#include +#include + +static void testFindJava() { + + CPath javaPath(""); + bool ok = findJavaInEnvPath(&javaPath); + printf("findJavaInEnvPath: [%s] %s\n", ok ? "OK" : "FAIL", javaPath.cstr()); + + javaPath.set(""); + ok = findJavaInRegistry(&javaPath); + printf("findJavaInRegistry [%s] %s\n", ok ? "OK" : "FAIL", javaPath.cstr()); + + javaPath.set(""); + ok = findJavaInProgramFiles(&javaPath); + printf("findJavaInProgramFiles [%s] %s\n", ok ? "OK" : "FAIL", javaPath.cstr()); +} + + +int main(int argc, char* argv[]) { + + gIsConsole = true; // tell utils to to print errors to stderr + gIsDebug = (getenv("ANDROID_SDKMAN_DEBUG") != NULL); + bool doShortPath = false; + bool doVersion = false; + + for (int i = 1; i < argc; i++) { + if (strncmp(argv[i], "-t", 2) == 0) { + testFindJava(); + return 0; + + } else if (strncmp(argv[i], "-d", 2) == 0) { + gIsDebug = true; + + } else if (strncmp(argv[i], "-s", 2) == 0) { + doShortPath = true; + + } else if (strncmp(argv[i], "-v", 2) == 0) { + doVersion = true; + + } else { + printf( + "Outputs the path of the first Java.exe found on the local system.\n" + "Returns code 0 when found, 1 when not found.\n" + "Options:\n" + "-h / -help : This help.\n" + "-t / -test : Internal test.\n" + "-s / -short : Print path in short DOS form.\n" + "-v / -version: Only prints the Java version found.\n" + ); + return 2; + } + } + + CPath javaPath; + if (!findJavaInEnvPath(&javaPath) && + !findJavaInRegistry(&javaPath) && + !findJavaInProgramFiles(&javaPath)) { + if (gIsDebug) { + fprintf(stderr, "Failed to find Java on your system.\n"); + } + return 1; + } + _ASSERT(!javaPath.isEmpty()); + + if (doShortPath) { + if (!javaPath.toShortPath(&javaPath)) { + fprintf(stderr, + "Failed to convert path to a short DOS path: %s\n", + javaPath.cstr()); + return 1; + } + } + + 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 java.exe path found + printf("%s", javaPath.cstr()); + return 0; +} + +#endif /* _WIN32 */ diff --git a/find_java/find_java_lib.cpp b/find_java/find_java_lib.cpp new file mode 100755 index 0000000..508147b --- /dev/null +++ b/find_java/find_java_lib.cpp @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef _WIN32 + +#include "find_java.h" +#include + +// Check whether we can find $PATH/java.exe +static bool checkPath(CPath *inOutPath) { + inOutPath->addPath("java.exe"); + + bool result = false; + 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); + } + + revertWow64FsRedirection(oldWow64Value); + return result; +} + +// Check whether we can find $PATH/bin/java.exe +static bool checkBinPath(CPath *inOutPath) { + inOutPath->addPath("bin"); + return checkPath(inOutPath); +} + +// Search java.exe in the environment +bool findJavaInEnvPath(CPath *outJavaPath) { + SetLastError(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()); + *outJavaPath = p; + return true; + } + } + + envPath = getenv("PATH"); + if (!envPath) return false; + + CArray *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()); + *outJavaPath = p; + delete paths; + return true; + } + } + + delete paths; + return false; +} + +// -------------- + +bool getRegValue(const char *keyPath, const char *keyName, REGSAM access, CString *outValue) { + HKEY key; + LSTATUS status = RegOpenKeyExA( + HKEY_LOCAL_MACHINE, // hKey + keyPath, // lpSubKey + 0, // ulOptions + KEY_READ | access, // samDesired, + &key); // phkResult + if (status == ERROR_SUCCESS) { + + LSTATUS ret = ERROR_MORE_DATA; + DWORD size = 4096; // MAX_PATH is 260, so 4 KB should be good enough + char* buffer = (char*) malloc(size); + + while (ret == ERROR_MORE_DATA && size < (1<<16) /*64 KB*/) { + ret = RegQueryValueExA( + key, // hKey + keyName, // lpValueName + NULL, // lpReserved + NULL, // lpType + (LPBYTE) buffer, // lpData + &size); // lpcbData + + if (ret == ERROR_MORE_DATA) { + size *= 2; + buffer = (char*) realloc(buffer, size); + } else { + buffer[size] = 0; + } + } + + if (ret != ERROR_MORE_DATA) outValue->set(buffer); + + free(buffer); + RegCloseKey(key); + + return (ret != ERROR_MORE_DATA); + } + + return false; +} + +bool 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); + + CString currVersion; + if (getRegValue(subKey.cstr(), "CurrentVersion", access, &currVersion)) { + // 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); + CPath javaHome; + if (getRegValue(subKey.cstr(), "JavaHome", access, &javaHome)) { + if (checkBinPath(&javaHome)) { + *outJavaPath = javaHome; + return true; + } + } + } + + return false; +} + +bool 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 + // 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; + } + + // Check the real sysinfo state (not the one hidden by WOW64) for x86 + SYSTEM_INFO sysInfo; + GetNativeSystemInfo(&sysInfo); + + 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; + } + } + + return false; +} + +// -------------- + +static bool checkProgramFiles(CPath *outJavaPath) { + + char programFilesPath[MAX_PATH + 1]; + HRESULT result = SHGetFolderPathA( + NULL, // hwndOwner + CSIDL_PROGRAM_FILES, // nFolder + NULL, // hToken + SHGFP_TYPE_CURRENT, // dwFlags + programFilesPath); // pszPath + if (FAILED(result)) return false; + + CPath path(programFilesPath); + path.addPath("Java"); + + // Do we have a C:\\Program Files\\Java directory? + if (!path.dirExists()) return false; + + CPath glob(path); + glob.addPath("j*"); + + bool found = false; + WIN32_FIND_DATAA findData; + HANDLE findH = FindFirstFileA(glob.cstr(), &findData); + if (findH == INVALID_HANDLE_VALUE) return false; + do { + 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)) { + found = true; + *outJavaPath = temp; + } + } + } while (!found && FindNextFileA(findH, &findData) != 0); + FindClose(findH); + + return found; +} + +bool 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; + + // Check the real sysinfo state (not the one hidden by WOW64) for x86 + SYSTEM_INFO sysInfo; + GetNativeSystemInfo(&sysInfo); + + if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { + // 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); + revertWow64FsRedirection(oldWow64Value); + return found; + } + + return false; +} + +// -------------- + +bool getJavaVersion(CPath &javaPath, CString *version) { + bool result = false; + + // Run "java -version", which outputs something like to *STDERR*: + // + // java version "1.6.0_29" + // Java(TM) SE Runtime Environment (build 1.6.0_29-b11) + // Java HotSpot(TM) Client VM (build 20.4-b02, mixed mode, sharing) + // + // We want to capture the first line, and more exactly the "1.6" part. + + + CString cmd; + cmd.setf("\"%s\" -version", javaPath.cstr()); + + SECURITY_ATTRIBUTES saAttr; + STARTUPINFO startup; + PROCESS_INFORMATION pinfo; + + // Want to inherit pipe handle + ZeroMemory(&saAttr, sizeof(saAttr)); + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + // Create pipe for stdout + HANDLE stdoutPipeRd, stdoutPipeWt; + if (!CreatePipe( + &stdoutPipeRd, // hReadPipe, + &stdoutPipeWt, // hWritePipe, + &saAttr, // lpPipeAttributes, + 0)) { // nSize (0=default buffer size) + displayLastError("CreatePipe failed: "); + return false; + } + if (!SetHandleInformation(stdoutPipeRd, HANDLE_FLAG_INHERIT, 0)) { + displayLastError("SetHandleInformation failed: "); + return false; + } + + ZeroMemory(&pinfo, sizeof(pinfo)); + + ZeroMemory(&startup, sizeof(startup)); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + startup.wShowWindow = SW_HIDE|SW_MINIMIZE; + // Capture both stderr and stdout + startup.hStdError = stdoutPipeWt; + startup.hStdOutput = stdoutPipeWt; + startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + + BOOL ok = CreateProcessA( + NULL, // program path + (LPSTR) cmd.cstr(), // command-line + NULL, // process handle is not inheritable + NULL, // thread handle is not inheritable + TRUE, // yes, inherit some handles + 0, // process creation flags + NULL, // use parent's environment block + NULL, // use parent's starting directory + &startup, // startup info, i.e. std handles + &pinfo); + + if (gIsConsole && !ok) displayLastError("CreateProcess failed: "); + + // Close the write-end of the output pipe (we're only reading from it) + CloseHandle(stdoutPipeWt); + + // Read from the output pipe. We don't need to read everything, + // the first line should be 'Java version "1.2.3_45"\r\n' + // so reading about 32 chars is all we need. + char first32[32 + 1]; + int index = 0; + first32[0] = 0; + + if (ok) { + + #define SIZE 1024 + char buffer[SIZE]; + DWORD sizeRead = 0; + + while (ok) { + // Keep reading in the same buffer location + ok = ReadFile(stdoutPipeRd, // hFile + buffer, // lpBuffer + SIZE, // DWORD buffer size to read + &sizeRead, // DWORD buffer size read + NULL); // overlapped + if (!ok || sizeRead == 0 || sizeRead > SIZE) break; + + // Copy up to the first 32 characters + if (index < 32) { + DWORD n = 32 - index; + if (n > sizeRead) n = sizeRead; + // copy as lowercase to simplify checks later + for (char *b = buffer; n > 0; n--, b++, index++) { + char c = *b; + if (c >= 'A' && c <= 'Z') c += 'a' - 'A'; + first32[index] = c; + } + first32[index] = 0; + } + } + + WaitForSingleObject(pinfo.hProcess, INFINITE); + + DWORD exitCode; + if (GetExitCodeProcess(pinfo.hProcess, &exitCode)) { + // this should not return STILL_ACTIVE (259) + result = exitCode == 0; + } + + CloseHandle(pinfo.hProcess); + CloseHandle(pinfo.hThread); + } + CloseHandle(stdoutPipeRd); + + if (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)) { + 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; + } + } + } + } + + return result; +} + + +#endif /* _WIN32 */ diff --git a/find_java/images/android_icon.ico b/find_java/images/android_icon.ico new file mode 100644 index 0000000..bd25179 Binary files /dev/null and b/find_java/images/android_icon.ico differ diff --git a/find_java/images/android_icon.rc b/find_java/images/android_icon.rc new file mode 100644 index 0000000..7ff37f2 --- /dev/null +++ b/find_java/images/android_icon.rc @@ -0,0 +1,2 @@ +1 ICON "../images/android_icon.ico" +1 RT_MANIFEST "../find_java.exe.manifest" diff --git a/find_java/utils.cpp b/find_java/utils.cpp new file mode 100755 index 0000000..bb679b8 --- /dev/null +++ b/find_java/utils.cpp @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef _WIN32 + +#include "utils.h" + +#define _CRT_SECURE_NO_WARNINGS 1 + +// Set to true to get some extra debug information +bool gIsDebug = false; +// Set to true to output errors to stderr (for a Console app) +// or to false to output using msg box (for a Windows UI app) +bool gIsConsole = false; + +// Displays a message in an ok+info dialog box. +void msgBox(const char* text, ...) { + CString formatted; + va_list ap; + va_start(ap, text); + formatted.setv(text, ap); + va_end(ap); + + MessageBoxA(NULL, formatted.cstr(), "Android SDK Manager", MB_OK | MB_ICONINFORMATION); +} + +// Displays GetLastError prefixed with a description in an error dialog box +void displayLastError(const char *description, ...) { + CString formatted; + va_list ap; + va_start(ap, description); + formatted.setv(description, ap); + va_end(ap); + + CString error; + error.setLastWin32Error(); + formatted.add("\r\n"); + formatted.add(error.cstr()); + + if (gIsConsole) { + fprintf(stderr, "%s\n", formatted.cstr()); + } else { + MessageBox(NULL, formatted.cstr(), "Android SDK Manager - Error", MB_OK | MB_ICONERROR); + } +} + +// Executes the command line. Does not wait for the program to finish. +// The return code is from CreateProcess (0 means failure), not the running app. +int execNoWait(const char *app, const char *params, const char *workDir) { + STARTUPINFO startup; + PROCESS_INFORMATION pinfo; + + ZeroMemory(&pinfo, sizeof(pinfo)); + + ZeroMemory(&startup, sizeof(startup)); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW; + startup.wShowWindow = SW_SHOWDEFAULT; + + int ret = CreateProcessA( + (LPSTR) app, /* program path */ + (LPSTR) params, /* command-line */ + NULL, /* process handle is not inheritable */ + NULL, /* thread handle is not inheritable */ + TRUE, /* yes, inherit some handles */ + 0, /* create flags */ + NULL, /* use parent's environment block */ + workDir, /* use parent's starting directory */ + &startup, /* startup info, i.e. std handles */ + &pinfo); + + if (ret) { + CloseHandle(pinfo.hProcess); + CloseHandle(pinfo.hThread); + } + + return ret; +} + +// Executes command, waits for completion and returns exit code. +// As indicated in MSDN for CreateProcess, callers should double-quote the program name +// e.g. cmd="\"c:\program files\myapp.exe\" arg1 arg2"; +int execWait(const char *cmd) { + STARTUPINFO startup; + PROCESS_INFORMATION pinfo; + + ZeroMemory(&pinfo, sizeof(pinfo)); + + ZeroMemory(&startup, sizeof(startup)); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW; + startup.wShowWindow = SW_HIDE|SW_MINIMIZE; + + int ret = CreateProcessA( + NULL, /* program path */ + (LPSTR) cmd, /* command-line */ + NULL, /* process handle is not inheritable */ + NULL, /* thread handle is not inheritable */ + TRUE, /* yes, inherit some handles */ + CREATE_NO_WINDOW, /* we don't want a console */ + NULL, /* use parent's environment block */ + NULL, /* use parent's starting directory */ + &startup, /* startup info, i.e. std handles */ + &pinfo); + + int result = -1; + if (ret) { + WaitForSingleObject(pinfo.hProcess, INFINITE); + + DWORD exitCode; + if (GetExitCodeProcess(pinfo.hProcess, &exitCode)) { + // this should not return STILL_ACTIVE (259) + result = exitCode; + } + CloseHandle(pinfo.hProcess); + CloseHandle(pinfo.hThread); + } + + return result; +} + +bool getModuleDir(CPath *outDir) { + CHAR programDir[MAX_PATH]; + int ret = GetModuleFileName(NULL, programDir, sizeof(programDir)); + if (ret != 0) { + // Remove the last segment to keep only the directory. + int pos = ret - 1; + while (pos > 0 && programDir[pos] != '\\') { + --pos; + } + outDir->set(programDir, pos); + return true; + } + return false; +} + +// Disables the FS redirection done by WOW64. +// Because this runs as a 32-bit app, Windows automagically remaps some +// folder under the hood (e.g. "Programs Files(x86)" is mapped as "Program Files"). +// This prevents the app from correctly searching for java.exe in these folders. +// The registry is also remapped. This method disables this redirection. +// Caller should restore the redirection later by using revertWow64FsRedirection(). +PVOID disableWow64FsRedirection() { + + // The call we want to make is the following: + // PVOID oldWow64Value; + // Wow64DisableWow64FsRedirection(&oldWow64Value); + // However that method may not exist (e.g. on XP non-64 systems) so + // we must not call it directly. + + PVOID oldWow64Value = 0; + + HMODULE hmod = LoadLibrary("kernel32.dll"); + if (hmod != NULL) { + FARPROC proc = GetProcAddress(hmod, "Wow64DisableWow64FsRedirection"); + if (proc != NULL) { + typedef BOOL (WINAPI *disableWow64FuncType)(PVOID *); + disableWow64FuncType funcPtr = (disableWow64FuncType)proc; + funcPtr(&oldWow64Value); + } + + FreeLibrary(hmod); + } + + return oldWow64Value; +} + +// Reverts the redirection disabled in disableWow64FsRedirection. +void revertWow64FsRedirection(PVOID oldWow64Value) { + + // The call we want to make is the following: + // Wow64RevertWow64FsRedirection(oldWow64Value); + // However that method may not exist (e.g. on XP non-64 systems) so + // we must not call it directly. + + HMODULE hmod = LoadLibrary("kernel32.dll"); + if (hmod != NULL) { + FARPROC proc = GetProcAddress(hmod, "Wow64RevertWow64FsRedirection"); + if (proc != NULL) { + typedef BOOL (WINAPI *revertWow64FuncType)(PVOID); + revertWow64FuncType funcPtr = (revertWow64FuncType)proc; + funcPtr(oldWow64Value); + } + + FreeLibrary(hmod); + } +} + +#endif /* _WIN32 */ diff --git a/find_java/utils.h b/find_java/utils.h new file mode 100755 index 0000000..a2260b6 --- /dev/null +++ b/find_java/utils.h @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _H_UTILS +#define _H_UTILS + +#ifdef _WIN32 + +#define _CRT_SECURE_NO_WARNINGS 1 + +#include +#include +#include +#include +#include + +// VS vs MINGW specific includes +#ifdef USE_VS_CRT + #include // for _ASSERT +#else + #define _ASSERT(x) // undef +#endif + +extern bool gIsDebug; +extern bool gIsConsole; + +// An array that knows its own size. Not dynamically resizable. +template class CArray { + T* mPtr; + int mSize; +public: + explicit CArray(int size) { + mSize = size; + mPtr = new T[size]; + } + + ~CArray() { + if (mPtr != NULL) { + delete[] mPtr; + mPtr = NULL; + } + mSize = 0; + } + + T& operator[](int i) { + _ASSERT(i >= 0 && i < mSize); + return mPtr[i]; + } + + int size() const { + return mSize; + } +}; + +// A simple string class wrapper. +class CString { +protected: + char *mStr; +public: + CString() { mStr = NULL; } + CString(const CString &str) { mStr = NULL; set(str.mStr); } + explicit CString(const char *str) { mStr = NULL; set(str); } + CString(const char *start, int length) { mStr = NULL; set(start, length); } + + CString& operator=(const CString &str) { + return set(str.cstr()); + } + + CString& set(const char *str) { + if (str != mStr) { + _free(); + if (str != NULL) { + mStr = _strdup(str); + } + } + return *this; + } + + CString& set(const char *start, int length) { + _free(); + if (start != NULL) { + mStr = (char *)malloc(length + 1); + strncpy(mStr, start, length); + mStr[length] = 0; + } + return *this; + } + + CString& setv(const char *str, va_list ap) { + _free(); + // _vscprintf(str, ap) is only available with the MSVCRT, not MinGW. + // Instead we'll iterate till we have enough space to generate the string. + int len = strlen(str) + 1024; + mStr = (char *)malloc(len); + strcpy(mStr, str); // provide a default in case vsnprintf totally fails + for (int guard = 0; guard < 10; guard++) { + int ret = vsnprintf(mStr, len, str, ap); + if (ret == -1) { + // Some implementations don't give the proper size needed + // so double the space and try again. + len *= 2; + } else if (ret >= len) { + len = ret + 1; + } else { + // There was enough space to write. + break; + } + mStr = (char *)realloc((void *)mStr, len); + strcpy(mStr, str); // provide a default in case vsnprintf totally fails + } + return *this; + } + + CString& setf(const char *str, ...) { + _free(); + va_list ap; + va_start(ap, str); + setv(str, ap); + va_end(ap); + return *this; + } + + virtual ~CString() { _free(); } + + // Returns the C string owned by this CString. It will be + // invalid as soon as this CString is deleted or out of scope. + const char * cstr() const { + return mStr; + } + + bool isEmpty() const { + return mStr == NULL || *mStr == 0; + } + + int length() const { + return mStr == NULL ? 0 : strlen(mStr); + } + + CString& add(const char *str) { + if (mStr == NULL) { + set(str); + } else { + mStr = (char *)realloc((void *)mStr, strlen(mStr) + strlen(str) + 1); + strcat(mStr, str); + } + return *this; + } + + CString& add(const char *str, int length) { + if (mStr == NULL) { + set(str, length); + } else { + int l1 = strlen(mStr); + mStr = (char *)realloc((void *)mStr, l1 + length + 1); + strncpy(mStr + l1, str, length); + mStr[l1 + length] = 0; + } + return *this; + } + + CArray * split(char sep) const { + if (mStr == NULL) { + return new CArray(0); + } + const char *last = NULL; + int n = 0; + for (const char *s = mStr; *s; s++) { + if (*s == sep && s != mStr && (last == NULL || s > last+1)) { + n++; + last = s; + } + } + + CArray *result = new CArray(n); + last = NULL; + n = 0; + for (const char *s = mStr; *s; s++) { + if (*s == sep) { + if (s != mStr && (last == NULL || s > last+1)) { + const char *start = last ? last : mStr; + (*result)[n++].set(start, s-start); + } + last = s+1; + } + } + + return result; + } + + // Sets the string to the message matching Win32 GetLastError. + CString& setLastWin32Error() { + DWORD err = GetLastError(); + LPSTR errStr; + if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | /* dwFlags */ + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, /* lpSource */ + err, /* dwMessageId */ + 0, /* dwLanguageId */ + (LPSTR)&errStr, /* lpBuffer */ + 0, /* nSize */ + NULL) != 0) { /* va_list args */ + setf("[%d] %s", err, errStr); + LocalFree(errStr); + } + return *this; + } + +private: + void _free() { + if (mStr != NULL) { + free((void *)mStr); + mStr = NULL; + } + } + +}; + +// A simple path class wrapper. +class CPath : public CString { +public: + CPath() : CString() { } + CPath(const CString &str) : CString(str) { } + CPath(const CPath &str) : CString(str) { } + explicit CPath(const char *str) : CString(str) { } + CPath(const char *start, int length) : CString(start, length) { } + + CPath& operator=(const CPath &str) { + set(str.cstr()); + return *this; + } + + // Appends a path segment, adding a \ as necessary. + CPath& addPath(const CString &s) { + return addPath(s.cstr()); + } + + // Appends a path segment, adding a \ as necessary. + CPath& addPath(const char *s) { + _ASSERT(s != NULL); + if (s != NULL && s[0] != 0) { + int n = length(); + if (n > 0 && s[0] != '\\' && mStr[n-1] != '\\') add("\\"); + add(s); + } + return *this; + } + + // Returns true if file exist and is not a directory. + // There's no garantee we have rights to access it. + bool fileExists() const { + if (mStr == NULL) return false; + DWORD attribs = GetFileAttributesA(mStr); + return attribs != INVALID_FILE_ATTRIBUTES && + !(attribs & FILE_ATTRIBUTE_DIRECTORY); + } + + // Returns true if file exist and is a directory. + // There's no garantee we have rights to access it. + bool dirExists() const { + if (mStr == NULL) return false; + DWORD attribs = GetFileAttributesA(mStr); + return attribs != INVALID_FILE_ATTRIBUTES && + (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0; + } + + // Returns a copy of the directory portion of the path, if any + CPath dirName() const { + CPath result; + if (mStr != NULL) { + char *pos = strrchr(mStr, '\\'); + if (pos != NULL) { + result.set(mStr, pos - mStr); + } + } + return result; + } + + // Returns a pointer to the baseName part of the path. + // It becomes invalid if the path changes. + const char * baseName() const { + if (mStr != NULL) { + char *pos = strrchr(mStr, '\\'); + if (pos != NULL) { + return pos + 1; + } + } + return NULL; + } + + // If the path ends with the given searchName, replace in-place by the new name + void replaceName(const char *searchName, const char* newName) { + if (mStr == NULL) return; + int n = length(); + int sn = strlen(searchName); + if (n < sn) return; + // if mStr ends with searchName + if (strcmp(mStr + n - sn, searchName) == 0) { + int sn2 = strlen(newName); + if (sn2 > sn) { + mStr = (char *)realloc((void *)mStr, n + sn2 - sn + 1); + } + strcpy(mStr + n - sn, newName); + mStr[n + sn2 - sn] = 0; + } + } + + // Returns a copy of this path as a DOS short path in the destination. + // Returns true if the Win32 getShortPathName method worked. + // In case of error, returns false and does not change the destination. + // It's OK to invoke this->toShortPath(this). + bool toShortPath(CPath *dest) { + const char *longPath = mStr; + if (mStr == NULL) return false; + + DWORD lenShort = strlen(longPath) + 1; + char * shortPath = (char *)malloc(lenShort); + + DWORD length = GetShortPathName(longPath, shortPath, lenShort); + if (length > lenShort) { + // The buffer wasn't big enough, this is the size to use. + free(shortPath); + lenShort = length; + shortPath = (char *)malloc(length); + length = GetShortPathName(longPath, shortPath, lenShort); + } + + if (length != 0) dest->set(shortPath); + + free(shortPath); + return length != 0; + } +}; + +// Displays a message in an ok+info dialog box. +void msgBox(const char* text, ...); + +// Displays GetLastError prefixed with a description in an error dialog box +void displayLastError(const char *description, ...); + +// Executes the command line. Does not wait for the program to finish. +// The return code is from CreateProcess (0 means failure), not the running app. +int execNoWait(const char *app, const char *params, const char *workDir); + +// Executes command, waits for completion and returns exit code. +// As indicated in MSDN for CreateProcess, callers should double-quote the program name +// e.g. cmd="\"c:\program files\myapp.exe\" arg1 arg2"; +int execWait(const char *cmd); + +bool getModuleDir(CPath *outDir); + +// Disables the FS redirection done by WOW64. +// Because this runs as a 32-bit app, Windows automagically remaps some +// folder under the hood (e.g. "Programs Files(x86)" is mapped as "Program Files"). +// This prevents the app from correctly searching for java.exe in these folders. +// The registry is also remapped. +PVOID disableWow64FsRedirection(); + +// Reverts the redirection disabled in disableWow64FsRedirection. +void revertWow64FsRedirection(PVOID oldWow64Value); + +#endif /* _WIN32 */ +#endif /* _H_UTILS */ -- cgit v1.1