diff options
author | Raphael <raphael@google.com> | 2012-01-24 15:06:56 -0800 |
---|---|---|
committer | Raphael <raphael@google.com> | 2012-01-25 10:16:18 -0800 |
commit | d2d6999d779fffe976c55db04bf523907a8743ed (patch) | |
tree | 5a5ea7db3cac33e6f8403992ad47b1111c71c3f4 | |
parent | 22fd3c107616214d2a6baecb64fe1354686cb470 (diff) | |
download | sdk-d2d6999d779fffe976c55db04bf523907a8743ed.zip sdk-d2d6999d779fffe976c55db04bf523907a8743ed.tar.gz sdk-d2d6999d779fffe976c55db04bf523907a8743ed.tar.bz2 |
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
-rwxr-xr-x | build/tools.windows.atree | 2 | ||||
-rw-r--r-- | find_java/.gitignore | 1 | ||||
-rw-r--r-- | find_java/Android.mk (renamed from sdkmanager/win_android/Android.mk) | 22 | ||||
-rw-r--r-- | find_java/NOTICE (renamed from sdkmanager/win_android/NOTICE) | 0 | ||||
-rwxr-xr-x | find_java/find_java.exe.manifest (renamed from sdkmanager/win_android/win_android.exe.manifest) | 4 | ||||
-rwxr-xr-x | find_java/find_java.h (renamed from sdkmanager/win_android/find_java.h) | 1 | ||||
-rw-r--r-- | find_java/find_java_exe.cpp | 133 | ||||
-rwxr-xr-x | find_java/find_java_lib.cpp (renamed from sdkmanager/win_android/find_java.cpp) | 125 | ||||
-rw-r--r-- | find_java/images/android_icon.ico (renamed from sdkmanager/win_android/images/android_icon.ico) | bin | 300318 -> 300318 bytes | |||
-rw-r--r-- | find_java/images/android_icon.rc | 2 | ||||
-rwxr-xr-x | find_java/utils.cpp (renamed from sdkmanager/win_android/utils.cpp) | 13 | ||||
-rwxr-xr-x | find_java/utils.h (renamed from sdkmanager/win_android/utils.h) | 49 | ||||
-rw-r--r-- | sdkmanager/win_android/.gitignore | 1 | ||||
-rw-r--r-- | sdkmanager/win_android/images/android_icon.rc | 2 | ||||
-rw-r--r-- | sdkmanager/win_android/win_android.cpp | 373 |
15 files changed, 302 insertions, 426 deletions
diff --git a/build/tools.windows.atree b/build/tools.windows.atree index 2087441..49bf10b 100755 --- a/build/tools.windows.atree +++ b/build/tools.windows.atree @@ -94,4 +94,4 @@ bin/sdklauncher.exe "SDK Manager.exe" # Supporting bat files sdk/files/post_tools_install.bat tools/lib/post_tools_install.bat sdk/files/find_java.bat tools/lib/find_java.bat - +bin/find_java.exe tools/lib/find_java.exe 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/sdkmanager/win_android/Android.mk b/find_java/Android.mk index 2f111a0..892f772 100644 --- a/sdkmanager/win_android/Android.mk +++ b/find_java/Android.mk @@ -1,12 +1,12 @@ # Copyright 2011 The Android Open Source Project # -# Android.mk for sdkmanager/win_android +# Android.mk for find_java.exe & static library LOCAL_PATH := $(call my-dir) -# find_java static library for host -# ======================================================== +# find_java static library for host (used by find_java.exe and installer) +# ======================================================================= include $(CLEAR_VARS) @@ -19,8 +19,8 @@ LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE -DSH_HISTORY -DUSE_MINGW include $(BUILD_HOST_STATIC_LIBRARY) -# "win_android.exe", a host replacement for "android.bat". For the Windows SDK only. -# ======================================================== +# "find_java.exe", to be used from android.bat & co +# ================================================= include $(CLEAR_VARS) @@ -29,7 +29,7 @@ ifeq ($(HOST_OS),windows) LOCAL_SRC_FILES := \ win_android.cpp -LOCAL_MODULE := win_android +LOCAL_MODULE := find_java LOCAL_STATIC_LIBRARIES := libfindjava LOCAL_CFLAGS += -Wall -Wno-unused-parameter @@ -49,17 +49,17 @@ endif # names to not interfere with the ones from qemu/Makefile.android. # INTERMEDIATE := $(call intermediates-dir-for,EXECUTABLES,$(LOCAL_MODULE),true) -WIN_ANDROID_ICON_OBJ := win_android_icon.o -WIN_ANDROID_ICON_PATH := $(LOCAL_PATH)/images -$(WIN_ANDROID_ICON_PATH)/$(WIN_ANDROID_ICON_OBJ): $(WIN_ANDROID_ICON_PATH)/android_icon.rc - $(WINDRES) $< -I $(WIN_ANDROID_ICON_PATH) -o $@ +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/$(WIN_ANDROID_ICON_OBJ) +LOCAL_PREBUILT_OBJ_FILES += images/$(FIND_JAVA_ICON_OBJ) include $(BUILD_HOST_EXECUTABLE) diff --git a/sdkmanager/win_android/NOTICE b/find_java/NOTICE index 7317ae2..7317ae2 100644 --- a/sdkmanager/win_android/NOTICE +++ b/find_java/NOTICE diff --git a/sdkmanager/win_android/win_android.exe.manifest b/find_java/find_java.exe.manifest index fd6c264..d949f07 100755 --- a/sdkmanager/win_android/win_android.exe.manifest +++ b/find_java/find_java.exe.manifest @@ -16,11 +16,11 @@ <assemblyIdentity version="1.0.0.0"
processorArchitecture="x86"
- name="Android.SDK.AndroidTool"
+ name="Android.SDK.FindJava"
type="win32"
/>
- <description>Wrapper to start the command-line "android" tool or the Android SDK Manager user interface.</description>
+ <description>Utility to find java.exe on the local system.</description>
<!-- Identify the application security requirements. -->
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
diff --git a/sdkmanager/win_android/find_java.h b/find_java/find_java.h index 41e5720..b578903 100755 --- a/sdkmanager/win_android/find_java.h +++ b/find_java/find_java.h @@ -24,6 +24,7 @@ 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 <io.h>
+#include <fcntl.h>
+
+static void testFindJava() {
+
+ CPath javaPath("<not found>");
+ bool ok = findJavaInEnvPath(&javaPath);
+ printf("findJavaInEnvPath: [%s] %s\n", ok ? "OK" : "FAIL", javaPath.cstr());
+
+ javaPath.set("<not found>");
+ ok = findJavaInRegistry(&javaPath);
+ printf("findJavaInRegistry [%s] %s\n", ok ? "OK" : "FAIL", javaPath.cstr());
+
+ javaPath.set("<not found>");
+ 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/sdkmanager/win_android/find_java.cpp b/find_java/find_java_lib.cpp index eea09ef..508147b 100755 --- a/sdkmanager/win_android/find_java.cpp +++ b/find_java/find_java_lib.cpp @@ -19,8 +19,6 @@ #include "find_java.h"
#include <shlobj.h>
-extern bool gDebug;
-
// Check whether we can find $PATH/java.exe
static bool checkPath(CPath *inOutPath) {
inOutPath->addPath("java.exe");
@@ -53,7 +51,7 @@ bool findJavaInEnvPath(CPath *outJavaPath) { if (envPath != NULL) {
CPath p(envPath);
if (checkBinPath(&p)) {
- if (gDebug) msgBox("Java found via JAVA_HOME: %s", p.cstr());
+ if (gIsDebug) msgBox("Java found via JAVA_HOME: %s", p.cstr());
*outJavaPath = p;
return true;
}
@@ -66,7 +64,7 @@ bool findJavaInEnvPath(CPath *outJavaPath) { for(int i = 0; i < paths->size(); i++) {
CPath p((*paths)[i].cstr());
if (checkPath(&p)) {
- if (gDebug) msgBox("Java found via env PATH: %s", p.cstr());
+ if (gIsDebug) msgBox("Java found via env PATH: %s", p.cstr());
*outJavaPath = p;
delete paths;
return true;
@@ -243,15 +241,22 @@ bool findJavaInProgramFiles(CPath *outJavaPath) { // --------------
-static bool getJavaVersion(CPath &javaPath, CString *version) {
+bool getJavaVersion(CPath &javaPath, CString *version) {
bool result = false;
- // Run "java -version".
- // TODO: capture output to string.
+ // 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;
+ SECURITY_ATTRIBUTES saAttr;
STARTUPINFO startup;
PROCESS_INFORMATION pinfo;
@@ -271,28 +276,75 @@ static bool getJavaVersion(CPath &javaPath, CString *version) { 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;
+ startup.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
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 */
+ // 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 (ret) {
+ 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;
@@ -300,18 +352,33 @@ static bool getJavaVersion(CPath &javaPath, CString *version) { // this should not return STILL_ACTIVE (259)
result = exitCode == 0;
}
+
CloseHandle(pinfo.hProcess);
CloseHandle(pinfo.hThread);
}
CloseHandle(stdoutPipeRd);
- CloseHandle(stdoutPipeWt);
- if (result) {
- // TODO
- // Parse output of "java -version".
- // It should be something like:
- // java version "1.6.0_29"
- // (including the quotes.)
+ 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;
diff --git a/sdkmanager/win_android/images/android_icon.ico b/find_java/images/android_icon.ico Binary files differindex bd25179..bd25179 100644 --- a/sdkmanager/win_android/images/android_icon.ico +++ b/find_java/images/android_icon.ico 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/sdkmanager/win_android/utils.cpp b/find_java/utils.cpp index bef1aa7..bb679b8 100755 --- a/sdkmanager/win_android/utils.cpp +++ b/find_java/utils.cpp @@ -20,7 +20,11 @@ #define _CRT_SECURE_NO_WARNINGS 1
-bool gDebug = false;
+// 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, ...) {
@@ -45,7 +49,12 @@ void displayLastError(const char *description, ...) { error.setLastWin32Error();
formatted.add("\r\n");
formatted.add(error.cstr());
- MessageBox(NULL, formatted.cstr(), "Android SDK Manager - Error", MB_OK | MB_ICONERROR);
+
+ 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.
diff --git a/sdkmanager/win_android/utils.h b/find_java/utils.h index 4530380..a2260b6 100755 --- a/sdkmanager/win_android/utils.h +++ b/find_java/utils.h @@ -34,7 +34,8 @@ #define _ASSERT(x) // undef
#endif
-extern bool gDebug;
+extern bool gIsDebug;
+extern bool gIsConsole;
// An array that knows its own size. Not dynamically resizable.
template <class T> class CArray {
@@ -148,12 +149,24 @@ public: return mStr == NULL ? 0 : strlen(mStr);
}
- CString& add(const char *s) {
+ CString& add(const char *str) {
if (mStr == NULL) {
- set(s);
+ set(str);
} else {
- mStr = (char *)realloc((void *)mStr, strlen(mStr) + strlen(s) + 1);
- strcat(mStr, s);
+ 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;
}
@@ -303,6 +316,32 @@ public: 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.
diff --git a/sdkmanager/win_android/.gitignore b/sdkmanager/win_android/.gitignore deleted file mode 100644 index 589b106..0000000 --- a/sdkmanager/win_android/.gitignore +++ /dev/null @@ -1 +0,0 @@ -images/win_android_icon.o diff --git a/sdkmanager/win_android/images/android_icon.rc b/sdkmanager/win_android/images/android_icon.rc deleted file mode 100644 index 42298a9..0000000 --- a/sdkmanager/win_android/images/android_icon.rc +++ /dev/null @@ -1,2 +0,0 @@ -1 ICON "../images/android_icon.ico"
-1 RT_MANIFEST "../win_android.exe.manifest"
diff --git a/sdkmanager/win_android/win_android.cpp b/sdkmanager/win_android/win_android.cpp deleted file mode 100644 index 3a768ec..0000000 --- a/sdkmanager/win_android/win_android.cpp +++ /dev/null @@ -1,373 +0,0 @@ -/*
- * 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.
- */
-
-/*
- * "win_android.exe", for Windows only. Replaces the previous android.bat.
- * In the final SDK, this is what becomes tools\android.exe and it runs
- * the UI for the SDK Manager or the AVD Manager.
- *
- * 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.
- */
-
-#ifdef _WIN32
-
-#include "utils.h"
-#include "find_java.h"
-#include <io.h>
-#include <fcntl.h>
-
-// A NULL-terminated list of directory to create in the temp folder.
-static const char * sMkDirList[] = {
- "lib",
- "lib\\x86",
- "lib\\x86_64",
- NULL,
-};
-
-// A NULL-terminated list of file patterns to copy in the temp folder.
-// The folders must be listed in sMkDirList
-static const char * sFilesToCopy[] = {
- "lib\\x86\\swt.jar",
- "lib\\x86_64\\swt.jar",
- "lib\\androidprefs.jar",
- "lib\\org.eclipse.*",
- "lib\\sdk*",
- "lib\\common.jar",
- "lib\\commons-compress*",
- "lib\\swtmenubar.jar",
- "lib\\commons-logging*",
- "lib\\commons-codec*",
- "lib\\httpclient*",
- "lib\\httpcore*",
- "lib\\httpmime*",
- NULL,
-};
-
-
-// Creates a directory named dirLeafName in the TEMP directory.
-// Returns the path in outDir on success.
-static bool mkTempDir(const char *dirLeafName, CPath *outDir) {
- SetLastError(0);
- char tempPath[MAX_PATH + 1] = "";
- DWORD len = GetTempPathA(MAX_PATH, tempPath);
- if (len > 0 && len <= MAX_PATH) {
- _ASSERT(tempPath[len-1] == '\\');
- _ASSERT(len + strlen(dirLeafName) < MAX_PATH);
- if (len + strlen(dirLeafName) >= MAX_PATH) {
- displayLastError("TEMP path too long to create a temporary directory: %s", tempPath);
- return false;
- }
- strcat(tempPath, dirLeafName);
- outDir->set(tempPath);
-
- if (outDir->dirExists() ||
- CreateDirectoryA(tempPath, NULL /*lpSecurityAttributes*/) != 0) {
- return true;
- }
- }
- displayLastError("Failed to create a temporary directory: %s", tempPath);
- return false;
-}
-
-// Creates all the directories from sMkDirList in the specified base tmpDir.
-static bool mkDirs(const char *tmpDir, const char * dirList[]) {
- SetLastError(0);
- for (const char **dir = dirList; *dir != NULL; dir++) {
- CPath path(tmpDir);
- path.addPath(*dir);
- if (!path.dirExists()) {
- if (!CreateDirectoryA(path.cstr(), NULL /*lpSecurityAttributes*/)) {
- displayLastError("Failed to create directory: %s", path.cstr());
- return false;
- }
- }
- }
- return true;
-}
-
-static bool copyFiles(const char *toolsDir, const char *tmpDir, const char *globList[]) {
- SetLastError(0);
- WIN32_FIND_DATAA srcFindData;
- WIN32_FIND_DATAA destFindData;
- for (const char **glob = globList; *glob != NULL; glob++) {
- CPath globDir = CPath(*glob).dirName();
-
- CPath fullGlob(toolsDir);
- fullGlob.addPath(*glob);
-
- HANDLE srcH = FindFirstFileA(fullGlob.cstr(), &srcFindData);
- if (srcH == INVALID_HANDLE_VALUE) {
- displayLastError("Failed to list files: %s", *glob);
- return false;
- }
- do {
- // Skip directories
- if ((srcFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
- continue;
- }
- CPath srcPath(toolsDir);
- srcPath.addPath(globDir).addPath(srcFindData.cFileName);
-
- CPath destPath(tmpDir);
- destPath.addPath(globDir).addPath(srcFindData.cFileName);
-
- // Skip copy if files are likely to not have changed.
- HANDLE destH = FindFirstFileA(destPath.cstr(), &destFindData);
- if (destH != INVALID_HANDLE_VALUE) {
- // Size must be same for us to skip it.
- if (srcFindData.nFileSizeHigh == destFindData.nFileSizeHigh &&
- srcFindData.nFileSizeLow == destFindData.nFileSizeLow) {
- // Creation & access times can differ. However if the dest write time
- // is >= than the source write time, it should be the same file.
- LARGE_INTEGER srcWriteTime;
- LARGE_INTEGER dstWriteTime;
- srcWriteTime.HighPart = srcFindData.ftLastWriteTime.dwHighDateTime;
- srcWriteTime.LowPart = srcFindData.ftLastWriteTime.dwLowDateTime;
- dstWriteTime.HighPart = destFindData.ftLastWriteTime.dwHighDateTime;
- dstWriteTime.LowPart = destFindData.ftLastWriteTime.dwLowDateTime;
- if (dstWriteTime.QuadPart >= srcWriteTime.QuadPart) {
- FindClose(destH);
- continue;
- }
- }
-
- FindClose(destH);
-
- // CopyFile copies some attributes. It's common for tools to be unzipped
- // as read-only so we need to remove any r-o attribute on existing
- // files if we want a recopy to succeed.
- if ((destFindData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0) {
- SetFileAttributes(destPath.cstr(),
- destFindData.dwFileAttributes ^ FILE_ATTRIBUTE_READONLY);
- }
- }
-
- if (!CopyFileA(srcPath.cstr(), destPath.cstr(), false /*bFailIfExists*/)) {
- FindClose(srcH);
- displayLastError("Failed to copy file: %s", destPath.cstr());
- return false;
- }
- } while (FindNextFileA(srcH, &srcFindData) != 0);
- FindClose(srcH);
- }
- return true;
-}
-
-static bool execSdkManager(const char *javaPath,
- const char *toolsDir,
- const char *tmpDir,
- const char *lpCmdLine) {
- SetLastError(0);
-
- // Which java binary to call.
- // The default is to use java.exe to automatically dump stdout in
- // the parent console.
- CPath javaExecPath(javaPath);
- bool redirectStdout = true;
-
- // Attach to the parent console, if there's one.
- if (AttachConsole(-1) == 0) {
- // This can fail with ERROR_ACCESS_DENIED if the process is already
- // attached to the parent console. That means there's a console so
- // we want to keep invoking java.exe to get stdout into it.
- //
- // This also fails if there is no parent console, in which
- // it means this was invoked not from a shell. It's a good
- // signal we don't want a new console to show up so we'll
- // switch to javaw.exe instead, if available.
-
- if (GetLastError() != ERROR_ACCESS_DENIED) {
- SetLastError(0);
-
- javaExecPath.replaceName("java.exe", "javaw.exe");
- // Only accept it if we can actually find the exec
- PVOID oldWow64Value = disableWow64FsRedirection();
- if (!javaExecPath.fileExists()) {
- javaExecPath.set(javaPath);
- redirectStdout = false;
- }
- revertWow64FsRedirection(&oldWow64Value);
- }
- }
- if (redirectStdout && GetStdHandle(STD_OUTPUT_HANDLE) != NULL) {
- // If we have an output, redirect to it.
- freopen("CONOUT$", "w", stdout);
- freopen("CONOUT$", "w", stderr);
- }
-
- // Check whether the underlying system is x86 or x86_64.
- // We use GetSystemInfo which will see the one masqueraded by Wow64.
- // (to get the real info, we would use GetNativeSystemInfo instead.)
- SYSTEM_INFO sysInfo;
- GetSystemInfo(&sysInfo);
-
- CString arch("x86");
- if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
- arch.set("x86_64");
- } else if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) {
- // Skip this. We'll just assume x86 and let it fail later.
- // Keep this line for debugging purposes:
- // displayLastError("Unknown Processor Architecture: %d", sysInfo.wProcessorArchitecture);
- }
-
- // Now build the command line.
- // Note that we pass the absolute javaExecPath both to CreateProcess (via execNoWait)
- // and we set it as argv[0] in the command line just for the show.
- // Important: for the classpath to be able to contain "lib\\sdkmanager.jar", etc.,
- // we need to set the toolsDir as the *temp* directory in execNoWait.
- // It's important to not use toolsDir otherwise it would lock that diretory.
-
- CString cmdLine;
- cmdLine.setf("\"%s\" " // javaExecPath basename
- "-Dcom.android.sdkmanager.toolsdir=\"%s\" " // toolsDir
- "-Dcom.android.sdkmanager.workdir=\"%s\" " // workDir==toolsdir
- "-classpath \"lib\\sdkmanager.jar;lib\\swtmenubar.jar;lib\\%s\\swt.jar\" " // arch
- "com.android.sdkmanager.Main "
- "%s", // extra parameters
- javaExecPath.baseName(), toolsDir, tmpDir, arch.cstr(), lpCmdLine);
-
- // Tip: to connect the Java debugging to a running process, add this to the Java command line:
- // "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000"
-
- if (gDebug) msgBox("Executing: %s", cmdLine.cstr());
-
- if (!execNoWait(javaExecPath.cstr(), cmdLine.cstr(), tmpDir)) {
- displayLastError("Failed to run %s", cmdLine.cstr());
- return false;
- }
-
- return true;
-}
-
-// Attaches to a parent console or create one and then redirect stdout
-// and stderr to this console. Returns false if it failed to attach
-// or create the console.
-static bool sendStdoutToConsole() {
- // See http://stackoverflow.com/questions/4028353 for some background.
-
- HANDLE hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
- if (hConOut != NULL) {
- // Std output is set. Might or might not be a console though.
- // Try to attach to the parent console and use its ConOut.
- if (AttachConsole(ATTACH_PARENT_PROCESS) != 0) {
- goto redirect;
- }
- }
-
- // There is no current console output.
- // Create one and attach ConOut to stdout.
- if (AllocConsole() == 0) {
- displayLastError("AllocConsole failed: ");
- return false;
- }
-
-redirect:
- // Redirect both stdout and stderr.
- HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
- int fdOut = _open_osfhandle((intptr_t) hOut, _O_TEXT);
- if (fdOut != -1) {
- FILE *fpOut = _fdopen(fdOut, "w");
- *stdout = *fpOut;
- } else {
- // Workaround for Cygwin when not redirecting to a pipe
- freopen("CONOUT$", "w", stdout);
- }
-
- HANDLE hErr = GetStdHandle(STD_ERROR_HANDLE);
- int fdErr = _open_osfhandle((intptr_t) hErr, _O_TEXT);
- if (fdErr != -1) {
- FILE *fpErr = _fdopen(fdErr, "w");
- *stderr = *fpErr;
- } else {
- // Workaround for Cygwin when not redirecting to a pipe
- // Note: there's is no such 'CONERR$'. See MSDN for GetStdHandle().
- freopen("CONOUT$", "w", stderr);
- }
- return true;
-}
-
-static void testFindJava() {
- sendStdoutToConsole();
-
- CPath javaPath("<not found>");
- bool ok = findJavaInEnvPath(&javaPath);
- printf("findJavaInEnvPath: [%s] %s\n", ok ? "OK" : "FAIL", javaPath.cstr());
-
- javaPath.set("<not found>");
- ok = findJavaInRegistry(&javaPath);
- printf("findJavaInRegistry [%s] %s\n", ok ? "OK" : "FAIL", javaPath.cstr());
-
- javaPath.set("<not found>");
- ok = findJavaInProgramFiles(&javaPath);
- printf("findJavaInProgramFiles [%s] %s\n", ok ? "OK" : "FAIL", javaPath.cstr());
-}
-
-int APIENTRY WinMain(HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- LPTSTR lpCmdLine,
- int nCmdShow) {
-
- gDebug = (getenv("ANDROID_SDKMAN_DEBUG") != NULL);
-
- if (strcmp(lpCmdLine, "/test") == 0) {
- testFindJava();
- return 0;
- }
-
- CPath javaPath;
- if (!findJavaInEnvPath(&javaPath) &&
- !findJavaInRegistry(&javaPath) &&
- !findJavaInProgramFiles(&javaPath)) {
- msgBox("Failed to find Java on your system. Please reinstall it.");
- return 2;
- }
- _ASSERT(!javaPath.isEmpty());
-
- // For debugging it's convenient to override the tools directory location
- CPath toolsDir(getenv("ANDROID_SDKMAN_TOOLS_DIR"));
- if (toolsDir.isEmpty()) {
- if (!getModuleDir(&toolsDir)) {
- displayLastError("Failed to get program's filename: ");
- return 1;
- }
- }
- _ASSERT(!toolsDir.isEmpty());
-
- CPath tmpDir;
- if (!mkTempDir("temp-android-tool", &tmpDir)) {
- return 1;
- }
- _ASSERT(!tmpDir.isEmpty());
-
- if (!mkDirs(tmpDir.cstr(), sMkDirList)) {
- return 1;
- }
-
- if (!copyFiles(toolsDir.cstr(), tmpDir.cstr(), sFilesToCopy)) {
- return 1;
- }
-
- if (!execSdkManager(javaPath.cstr(), toolsDir.cstr(), tmpDir.cstr(), lpCmdLine)) {
- displayLastError("Failed to start SDK Manager: ");
- return 1;
- }
-
- return 0;
-}
-#endif /* _WIN32 */
|