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
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 @@
 # 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
similarity index 71%
rename from sdkmanager/win_android/Android.mk
rename to 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 @@
 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 @@
 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 @@
 # 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
similarity index 100%
rename from sdkmanager/win_android/NOTICE
rename to find_java/NOTICE
diff --git a/sdkmanager/win_android/win_android.exe.manifest b/find_java/find_java.exe.manifest
similarity index 85%
rename from sdkmanager/win_android/win_android.exe.manifest
rename to 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
similarity index 93%
rename from sdkmanager/win_android/find_java.h
rename to 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
similarity index 69%
rename from sdkmanager/win_android/find_java.cpp
rename to 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 @@
     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 @@
     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 @@
 

 // --------------

 

-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 @@
         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;

+    // Capture both stderr and stdout

+    startup.hStdError   = stdoutPipeWt;

+    startup.hStdOutput  = stdoutPipeWt;

+    startup.hStdInput   = GetStdHandle(STD_INPUT_HANDLE);

 

-    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 */

+    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 @@
             // 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
similarity index 100%
rename from sdkmanager/win_android/images/android_icon.ico
rename to find_java/images/android_icon.ico
Binary files 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/sdkmanager/win_android/utils.cpp b/find_java/utils.cpp
similarity index 93%
rename from sdkmanager/win_android/utils.cpp
rename to 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 @@
     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
similarity index 87%
rename from sdkmanager/win_android/utils.h
rename to 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 @@
         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 @@
             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 */