blob: 2c957e0f994c553540916aa3b1156f0cc62454da [file] [log] [blame]
Raphael97f3e042011-11-11 18:08:58 -08001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * "win_android.exe", for Windows only. Replaces the previous android.bat.
19 * In the final SDK, this is what becomes tools\android.exe and it runs
20 * the UI for the SDK Manager or the AVD Manager.
21 *
22 * Implementation details:
23 * - We don't have access to ATL or MFC.
24 * - We don't want to pull in things like STL.
25 * - No Unicode/MBCS support for now.
26 */
27
28#ifdef _WIN32
29
30#define _CRT_SECURE_NO_WARNINGS 1
31
32#include <direct.h>
33#include <stdio.h>
34#include <stdarg.h>
35#include <string.h>
36#include <windows.h>
37
38// VS vs MINGW specific includes
39#ifdef USE_MINGW
40 #define _ASSERT(x) // undef
41#else
42 #include <crtdbg.h> // for _ASSERT
43#endif
44
45// A NULL-terminated list of directory to create in the temp folder.
46static const char * sMkDirList[] = {
47 "lib",
48 "lib\\x86",
49 "lib\\x86_64",
50 NULL,
51};
52
53// A NULL-terminated list of file patterns to copy in the temp folder.
54// The folders must be listed in sMkDirList
55static const char * sFilesToCopy[] = {
56 "lib\\x86\\swt.jar",
57 "lib\\x86_64\\swt.jar",
58 "lib\\androidprefs.jar",
59 "lib\\org.eclipse.*",
60 "lib\\sdk*",
61 "lib\\common.jar",
62 "lib\\commons-compress*",
63 "lib\\swtmenubar.jar",
64 "lib\\commons-logging*",
65 "lib\\commons-codec*",
66 "lib\\httpclient*",
67 "lib\\httpcore*",
68 "lib\\httpmime*",
69 NULL,
70};
71
72static bool gDebug = false;
73
74
75// An array that knows its own size. Not dynamically resizable.
76template <class T> class CArray {
77 T* mPtr;
78 int mSize;
79public:
Raphael09439872011-11-17 15:50:48 -080080 explicit CArray(int size) {
Raphael97f3e042011-11-11 18:08:58 -080081 mSize = size;
82 mPtr = new T[size];
83 }
84
85 ~CArray() {
86 if (mPtr != NULL) {
87 delete[] mPtr;
88 mPtr = NULL;
89 }
90 mSize = 0;
91 }
92
93 T& operator[](int i) {
94 _ASSERT(i >= 0 && i < mSize);
95 return mPtr[i];
96 }
97
Raphael09439872011-11-17 15:50:48 -080098 int size() const {
Raphael97f3e042011-11-11 18:08:58 -080099 return mSize;
100 }
101};
102
103// A simple string class wrapper.
104class CString {
105protected:
106 char *mStr;
107public:
Raphael09439872011-11-17 15:50:48 -0800108 CString() { mStr = NULL; }
109 CString(const CString &str) { mStr = str.mStr == NULL ? NULL : _strdup(str.mStr); }
110 explicit CString(const char *str) { mStr = NULL; set(str); }
Raphael97f3e042011-11-11 18:08:58 -0800111 CString(const char *start, int length) { mStr = NULL; set(start, length); }
112
113 CString& set(const char *str) {
114 _free();
115 if (str != NULL) {
116 mStr = _strdup(str);
117 }
118 return *this;
119 }
120
121 CString& set(const char *start, int length) {
122 _free();
123 if (start != NULL) {
124 mStr = (char *)malloc(length + 1);
125 strncpy(mStr, start, length);
126 mStr[length] = 0;
127 }
128 return *this;
129 }
130
131 CString& setv(const char *str, va_list ap) {
132 _free();
133 // _vscprintf(str, ap) is only available with the MSVCRT, not MinGW.
134 // Instead we'll iterate till we have enough space to generate the string.
135 int len = strlen(str) + 1024;
136 mStr = (char *)malloc(len);
137 strcpy(mStr, str); // provide a default in case vsnprintf totally fails
138 for (int guard = 0; guard < 10; guard++) {
139 int ret = vsnprintf(mStr, len, str, ap);
140 if (ret == -1) {
141 // Some implementations don't give the proper size needed
142 // so double the space and try again.
143 len *= 2;
144 } else if (ret >= len) {
145 len = ret + 1;
146 } else {
147 // There was enough space to write.
148 break;
149 }
150 mStr = (char *)realloc((void *)mStr, len);
151 strcpy(mStr, str); // provide a default in case vsnprintf totally fails
152 }
153 return *this;
154 }
155
156 CString& setf(const char *str, ...) {
157 _free();
158 va_list ap;
159 va_start(ap, str);
160 setv(str, ap);
161 va_end(ap);
162 return *this;
163 }
164
165 virtual ~CString() { _free(); }
166
167 // Returns the C string owned by this CString. It will be
168 // invalid as soon as this CString is deleted or out of scope.
Raphael09439872011-11-17 15:50:48 -0800169 const char * cstr() const {
Raphael97f3e042011-11-11 18:08:58 -0800170 return mStr;
171 }
172
Raphael09439872011-11-17 15:50:48 -0800173 bool isEmpty() const {
Raphael97f3e042011-11-11 18:08:58 -0800174 return mStr == NULL || *mStr == 0;
175 }
176
Raphael09439872011-11-17 15:50:48 -0800177 int length() const {
Raphael97f3e042011-11-11 18:08:58 -0800178 return mStr == NULL ? 0 : strlen(mStr);
179 }
180
181 CString& add(const char *s) {
182 if (mStr == NULL) {
183 set(s);
184 } else {
185 mStr = (char *)realloc((void *)mStr, strlen(mStr) + strlen(s) + 1);
Raphael09439872011-11-17 15:50:48 -0800186 strcat(mStr, s);
Raphael97f3e042011-11-11 18:08:58 -0800187 }
188 return *this;
189 }
190
Raphael09439872011-11-17 15:50:48 -0800191 CArray<CString> * split(char sep) const {
Raphael97f3e042011-11-11 18:08:58 -0800192 if (mStr == NULL) {
193 return new CArray<CString>(0);
194 }
195 const char *last = NULL;
196 int n = 0;
197 for (const char *s = mStr; *s; s++) {
198 if (*s == sep && s != mStr && (last == NULL || s > last+1)) {
199 n++;
200 last = s;
201 }
202 }
203
204 CArray<CString> *result = new CArray<CString>(n);
205 last = NULL;
206 n = 0;
207 for (const char *s = mStr; *s; s++) {
208 if (*s == sep) {
209 if (s != mStr && (last == NULL || s > last+1)) {
210 const char *start = last ? last : mStr;
211 (*result)[n++].set(start, s-start);
212 }
213 last = s+1;
214 }
215 }
216
217 return result;
218 }
219
220private:
221 void _free() {
222 if (mStr != NULL) {
223 free((void *)mStr);
224 mStr = NULL;
225 }
226 }
227
228};
229
230// A simple path class wrapper.
231class CPath : public CString {
232public:
Raphael09439872011-11-17 15:50:48 -0800233 CPath() : CString() { }
234 CPath(const CPath &str) : CString(str) { }
235 explicit CPath(const char *str) : CString(str) { }
Raphael97f3e042011-11-11 18:08:58 -0800236 CPath(const char *start, int length) : CString(start, length) { }
237
238 // Appends a path segment, adding a \ as necessary.
Raphael09439872011-11-17 15:50:48 -0800239 CPath& addPath(const CString &s) {
240 return addPath(s.cstr());
241 }
242
243 // Appends a path segment, adding a \ as necessary.
Raphael97f3e042011-11-11 18:08:58 -0800244 CPath& addPath(const char *s) {
Raphael09439872011-11-17 15:50:48 -0800245 _ASSERT(s != NULL);
246 if (s != NULL && s[0] != 0) {
247 int n = length();
248 if (n > 0 && s[0] != '\\' && mStr[n-1] != '\\') add("\\");
249 add(s);
250 }
Raphael97f3e042011-11-11 18:08:58 -0800251 return *this;
252 }
253
254 // Returns true if file exist and is not a directory.
255 // There's no garantee we have rights to access it.
Raphael09439872011-11-17 15:50:48 -0800256 bool fileExists() const {
Raphael97f3e042011-11-11 18:08:58 -0800257 if (mStr == NULL) return false;
258 DWORD attribs = GetFileAttributesA(mStr);
259 return attribs != INVALID_FILE_ATTRIBUTES &&
260 !(attribs & FILE_ATTRIBUTE_DIRECTORY);
261 }
262
263 // Returns true if file exist and is a directory.
264 // There's no garantee we have rights to access it.
Raphael09439872011-11-17 15:50:48 -0800265 bool dirExists() const {
Raphael97f3e042011-11-11 18:08:58 -0800266 if (mStr == NULL) return false;
267 DWORD attribs = GetFileAttributesA(mStr);
268 return attribs != INVALID_FILE_ATTRIBUTES &&
269 (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
270 }
271
272 // Returns a copy of the directory portion of the path, if any
Raphael09439872011-11-17 15:50:48 -0800273 CPath dirName() const {
Raphael97f3e042011-11-11 18:08:58 -0800274 CPath result;
275 if (mStr != NULL) {
276 char *pos = strrchr(mStr, '\\');
277 if (pos != NULL) {
278 result.set(mStr, pos - mStr);
279 }
280 }
281 return result;
282 }
283
284 // Returns a pointer to the baseName part of the path.
285 // It becomes invalid if the path changes.
Raphael09439872011-11-17 15:50:48 -0800286 const char * baseName() const {
Raphael97f3e042011-11-11 18:08:58 -0800287 if (mStr != NULL) {
288 char *pos = strrchr(mStr, '\\');
289 if (pos != NULL) {
290 return pos + 1;
291 }
292 }
293 return NULL;
294 }
295
Raphael09439872011-11-17 15:50:48 -0800296 // If the path ends with the given searchName, replace in-place by the new name
Raphael97f3e042011-11-11 18:08:58 -0800297 void replaceName(const char *searchName, const char* newName) {
298 if (mStr == NULL) return;
299 int n = length();
300 int sn = strlen(searchName);
301 if (n < sn) return;
302 // if mStr ends with searchName
303 if (strcmp(mStr + n - sn, searchName) == 0) {
304 int sn2 = strlen(newName);
305 if (sn2 > sn) {
306 mStr = (char *)realloc((void *)mStr, n + sn2 - sn + 1);
307 }
308 strcpy(mStr + n - sn, newName);
309 mStr[n + sn2 - sn] = 0;
310 }
311 }
312};
313
314// ========= UTILITIES ==============
315
316// Displays a message in an ok+info dialog box.
317static void msgBox(const char* text, ...) {
318 CString formatted;
319 va_list ap;
320 va_start(ap, text);
321 formatted.setv(text, ap);
322 va_end(ap);
323
Raphael09439872011-11-17 15:50:48 -0800324 MessageBoxA(NULL, formatted.cstr(), "Android SDK Manager", MB_OK | MB_ICONINFORMATION);
Raphael97f3e042011-11-11 18:08:58 -0800325}
326
327static void displayLastError(const char *description, ...) {
328 CString formatted;
329 va_list ap;
330 va_start(ap, description);
331 formatted.setv(description, ap);
332 va_end(ap);
333
334 DWORD err = GetLastError();
335 LPSTR errStr;
336 if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | /* dwFlags */
337 FORMAT_MESSAGE_FROM_SYSTEM,
338 NULL, /* lpSource */
339 err, /* dwMessageId */
340 0, /* dwLanguageId */
341 (LPSTR)&errStr, /* lpBuffer */
342 0, /* nSize */
343 NULL) != 0) { /* va_list args */
344 formatted.add("\r\n");
345 formatted.add(errStr);
Raphael09439872011-11-17 15:50:48 -0800346 MessageBox(NULL, formatted.cstr(), "Android SDK Manager - Error", MB_OK | MB_ICONERROR);
Raphael97f3e042011-11-11 18:08:58 -0800347 LocalFree(errStr);
348 }
349}
350
351// Executes the command line. Does not wait for the program to finish.
352// The return code is from CreateProcess (0 means failure), not the running app.
353static int execNoWait(const char *app, const char *params, const char *workDir) {
354 STARTUPINFO startup;
355 PROCESS_INFORMATION pinfo;
356
357 ZeroMemory(&pinfo, sizeof(pinfo));
358
359 ZeroMemory(&startup, sizeof(startup));
360 startup.cb = sizeof(startup);
361 startup.dwFlags = STARTF_USESHOWWINDOW;
362 startup.wShowWindow = SW_HIDE|SW_MINIMIZE;
363
364 int ret = CreateProcessA(
365 (LPSTR) app, /* program path */
366 (LPSTR) params, /* command-line */
367 NULL, /* process handle is not inheritable */
368 NULL, /* thread handle is not inheritable */
369 TRUE, /* yes, inherit some handles */
370 CREATE_NO_WINDOW, /* we don't want a console */
371 NULL, /* use parent's environment block */
372 workDir, /* use parent's starting directory */
373 &startup, /* startup info, i.e. std handles */
374 &pinfo);
375
376 if (ret) {
377 CloseHandle(pinfo.hProcess);
378 CloseHandle(pinfo.hThread);
379 }
380
381 return ret;
382}
383
384
385// Executes command, waits for completion and returns exit code.
386// As indicated in MSDN for CreateProcess, callers should double-quote the program name
387// e.g. cmd="\"c:\program files\myapp.exe\" arg1 arg2";
388static int execWait(const char *cmd) {
389 STARTUPINFO startup;
390 PROCESS_INFORMATION pinfo;
391
392 ZeroMemory(&pinfo, sizeof(pinfo));
393
394 ZeroMemory(&startup, sizeof(startup));
395 startup.cb = sizeof(startup);
396 startup.dwFlags = STARTF_USESHOWWINDOW;
397 startup.wShowWindow = SW_HIDE|SW_MINIMIZE;
398
399 int ret = CreateProcessA(
400 NULL, /* program path */
401 (LPSTR) cmd, /* command-line */
402 NULL, /* process handle is not inheritable */
403 NULL, /* thread handle is not inheritable */
404 TRUE, /* yes, inherit some handles */
405 CREATE_NO_WINDOW, /* we don't want a console */
406 NULL, /* use parent's environment block */
407 NULL, /* use parent's starting directory */
408 &startup, /* startup info, i.e. std handles */
409 &pinfo);
410
411 int result = -1;
412 if (ret) {
413 WaitForSingleObject(pinfo.hProcess, INFINITE);
414
415 DWORD exitCode;
416 if (GetExitCodeProcess(pinfo.hProcess, &exitCode)) {
417 // this should not return STILL_ACTIVE (259)
418 result = exitCode;
419 }
420 CloseHandle(pinfo.hProcess);
421 CloseHandle(pinfo.hThread);
422 }
423
424 return result;
425}
426
427static bool getModuleDir(CPath *outDir) {
428 CHAR programDir[MAX_PATH];
429 int ret = GetModuleFileName(NULL, programDir, sizeof(programDir));
430 if (ret != 0) {
431 // Remove the last segment to keep only the directory.
432 int pos = ret - 1;
433 while (pos > 0 && programDir[pos] != '\\') {
434 --pos;
435 }
436 outDir->set(programDir, pos);
437 return true;
438 }
439 return false;
440}
441
442// Disable the FS redirection done by WOW64.
443// Because this runs as a 32-bit app, Windows automagically remaps some
444// folder under the hood (e.g. "Programs Files(x86)" is mapped as "Program Files").
445// This prevents the app from correctly searching for java.exe in these folders.
446// The registry is also remapped.
447static PVOID disableWow64FsRedirection() {
448
449 // The call we want to make is the following:
450 // PVOID oldWow64Value;
451 // Wow64DisableWow64FsRedirection(&oldWow64Value);
452 // However that method may not exist (e.g. on non-64 systems) so
453 // we must not call it directly.
454
455 PVOID oldWow64Value = 0;
456
457 HMODULE hmod = LoadLibrary("kernel32.dll");
458 if (hmod != NULL) {
459 FARPROC proc = GetProcAddress(hmod, "Wow64DisableWow64FsRedirection");
460 if (proc != NULL) {
461 typedef BOOL (WINAPI *disableWow64FuncType)(PVOID *);
462 disableWow64FuncType funcPtr = (disableWow64FuncType)proc;
463 funcPtr(&oldWow64Value);
464 }
465
466 FreeLibrary(hmod);
467 }
468
469 return oldWow64Value;
470}
471
472//Reverts the redirection disabled in disableWow64FsRedirection.
473static void revertWow64FsRedirection(PVOID oldWow64Value) {
474
475 // The call we want to make is the following:
476 // Wow64RevertWow64FsRedirection(oldWow64Value);
477 // However that method may not exist (e.g. on non-64 systems) so
478 // we must not call it directly.
479
480 HMODULE hmod = LoadLibrary("kernel32.dll");
481 if (hmod != NULL) {
482 FARPROC proc = GetProcAddress(hmod, "Wow64RevertWow64FsRedirection");
483 if (proc != NULL) {
484 typedef BOOL (WINAPI *revertWow64FuncType)(PVOID);
485 revertWow64FuncType funcPtr = (revertWow64FuncType)proc;
486 funcPtr(oldWow64Value);
487 }
488
489 FreeLibrary(hmod);
490 }
491}
492
493
494// =============================
495
496// Search java.exe in the path
497static bool findJavaInEnvPath(CPath *outJavaPath) {
498 SetLastError(0);
499 const char* envPath = getenv("PATH");
500 if (!envPath) return false;
501
502 CArray<CString> *paths = CString(envPath).split(';');
503 for(int i = 0; i < paths->size(); i++) {
Raphael09439872011-11-17 15:50:48 -0800504 CPath p((*paths)[i].cstr());
Raphael97f3e042011-11-11 18:08:58 -0800505 p.addPath("java.exe");
506 if (p.fileExists()) {
507 // Make sure we can actually run "java -version".
508 CString cmd;
Raphael09439872011-11-17 15:50:48 -0800509 cmd.setf("\"%s\" -version", p.cstr());
510 int code = execWait(cmd.cstr());
Raphael97f3e042011-11-11 18:08:58 -0800511 if (code == 0) {
Raphael09439872011-11-17 15:50:48 -0800512 if (gDebug) msgBox("Java found via env path: %s", p.cstr());
513 outJavaPath->set(p.cstr());
Raphael97f3e042011-11-11 18:08:58 -0800514 delete paths;
515 return true;
516 }
517 }
518 }
519
520 delete paths;
521 return false;
522}
523
524static bool findJavaInRegistry(CPath *outJavaPath) {
525 // TODO
526 return false;
527}
528
529static bool findJavaInProgramFiles(CPath *outJavaPath) {
530 // TODO
531 return false;
532}
533
534// Creates a directory named dirLeafName in the TEMP directory.
535// Returns the path in outDir on success.
536static bool mkTempDir(const char *dirLeafName, CPath *outDir) {
537 SetLastError(0);
538 char tempPath[MAX_PATH + 1] = "";
539 DWORD len = GetTempPathA(MAX_PATH, tempPath);
540 if (len > 0 && len <= MAX_PATH) {
541 _ASSERT(tempPath[len-1] == '\\');
542 _ASSERT(len + strlen(dirLeafName) < MAX_PATH);
543 if (len + strlen(dirLeafName) >= MAX_PATH) {
544 displayLastError("TEMP path too long to create a temporary directory: %s", tempPath);
545 return false;
546 }
547 strcat(tempPath, dirLeafName);
548 outDir->set(tempPath);
549
550 if (outDir->dirExists() ||
551 CreateDirectoryA(tempPath, NULL /*lpSecurityAttributes*/) != 0) {
552 return true;
553 }
554 }
555 displayLastError("Failed to create a temporary directory: %s", tempPath);
556 return false;
557}
558
559// Creates all the directories from sMkDirList in the specified base tmpDir.
560static bool mkDirs(const char *tmpDir, const char * dirList[]) {
561 SetLastError(0);
562 for (const char **dir = dirList; *dir != NULL; dir++) {
563 CPath path(tmpDir);
564 path.addPath(*dir);
565 if (!path.dirExists()) {
Raphael09439872011-11-17 15:50:48 -0800566 if (!CreateDirectoryA(path.cstr(), NULL /*lpSecurityAttributes*/)) {
567 displayLastError("Failed to create directory: %s", path.cstr());
Raphael97f3e042011-11-11 18:08:58 -0800568 return false;
569 }
570 }
571 }
572 return true;
573}
574
575static bool copyFiles(const char *toolsDir, const char *tmpDir, const char *globList[]) {
576 SetLastError(0);
577 WIN32_FIND_DATAA srcFindData;
578 WIN32_FIND_DATAA destFindData;
579 for (const char **glob = globList; *glob != NULL; glob++) {
580 CPath globDir = CPath(*glob).dirName();
581
582 CPath fullGlob(toolsDir);
583 fullGlob.addPath(*glob);
584
Raphael09439872011-11-17 15:50:48 -0800585 HANDLE srcH = FindFirstFileA(fullGlob.cstr(), &srcFindData);
Raphael97f3e042011-11-11 18:08:58 -0800586 if (srcH == INVALID_HANDLE_VALUE) {
587 displayLastError("Failed to list files: %s", *glob);
588 return false;
589 }
590 do {
591 // Skip directories
592 if ((srcFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
593 continue;
594 }
595 CPath srcPath(toolsDir);
596 srcPath.addPath(globDir).addPath(srcFindData.cFileName);
597
598 CPath destPath(tmpDir);
599 destPath.addPath(globDir).addPath(srcFindData.cFileName);
600
601 // Skip copy if files are likely to not have changed.
Raphael09439872011-11-17 15:50:48 -0800602 HANDLE destH = FindFirstFileA(destPath.cstr(), &destFindData);
Raphael97f3e042011-11-11 18:08:58 -0800603 if (destH != INVALID_HANDLE_VALUE) {
604 // Size must be same for us to skip it.
605 if (srcFindData.nFileSizeHigh == destFindData.nFileSizeHigh &&
606 srcFindData.nFileSizeLow == destFindData.nFileSizeLow) {
607 // Creation & access times can differ. However if the dest write time
608 // is >= than the source write time, it should be the same file.
609 LARGE_INTEGER srcWriteTime;
610 LARGE_INTEGER dstWriteTime;
611 srcWriteTime.HighPart = srcFindData.ftLastWriteTime.dwHighDateTime;
612 srcWriteTime.LowPart = srcFindData.ftLastWriteTime.dwLowDateTime;
613 dstWriteTime.HighPart = destFindData.ftLastWriteTime.dwHighDateTime;
614 dstWriteTime.LowPart = destFindData.ftLastWriteTime.dwLowDateTime;
615 if (dstWriteTime.QuadPart >= srcWriteTime.QuadPart) {
616 FindClose(destH);
617 continue;
618 }
619 }
620
621 FindClose(destH);
622
623 // CopyFile copies some attributes. It's common for tools to be unzipped
624 // as read-only so we need to remove any r-o attribute on existing
625 // files if we want a recopy to succeed.
626 if ((destFindData.dwFileAttributes && FILE_ATTRIBUTE_READONLY) != 0) {
Raphael09439872011-11-17 15:50:48 -0800627 SetFileAttributes(destPath.cstr(),
Raphael97f3e042011-11-11 18:08:58 -0800628 destFindData.dwFileAttributes ^ FILE_ATTRIBUTE_READONLY);
629 }
630 }
631
Raphael09439872011-11-17 15:50:48 -0800632 if (!CopyFileA(srcPath.cstr(), destPath.cstr(), false /*bFailIfExists*/)) {
Raphael97f3e042011-11-11 18:08:58 -0800633 FindClose(srcH);
Raphael09439872011-11-17 15:50:48 -0800634 displayLastError("Failed to copy file: %s", destPath.cstr());
Raphael97f3e042011-11-11 18:08:58 -0800635 return false;
636 }
637 } while (FindNextFileA(srcH, &srcFindData) != 0);
638 FindClose(srcH);
639 }
640 return true;
641}
642
643static bool execSdkManager(const char *javaPath,
644 const char *toolsDir,
645 const char *tmpDir,
646 const char *lpCmdLine) {
647 SetLastError(0);
648
649 CPath javawPath(javaPath);
650 javawPath.replaceName("java.exe", "javaw.exe");
651 // Only accept it if we can actually find the exec
652 PVOID oldWow64Value = disableWow64FsRedirection();
653 if (!javawPath.fileExists()) {
654 javawPath.set(javaPath);
655 }
656 revertWow64FsRedirection(&oldWow64Value);
657
658 // Check whether the underlying system is x86 or x86_64.
659 // We use GetSystemInfo which will see the one masqueraded by Wow64.
660 // (to get the real info, we would use GetNativeSystemInfo instead.)
661 SYSTEM_INFO sysInfo;
662 GetSystemInfo(&sysInfo);
663
664 CString arch("x86");
665 if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
666 arch.set("x86_64");
667 } else if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) {
668 // Skip this. We'll just assume x86 and let it fail later.
669 // displayLastError("Unknown Processor Architecture: %d", sysInfo.wProcessorArchitecture);
670 // return false;
671 }
672
673 // Now build the command line.
674 // Note that we pass the absolute javawPath to execNoWait
675 // and the first parameter is just for the show.
676 // Important: for the classpath to be able to contain "lib\\sdkmanager.jar", etc.,
677 // we need to set the toolsDir as the *temp* directory in execNoWait.
678 // It's important to not use toolsDir otherwise it would lock that diretory.
679
680 // Tip: to connect the Java debugging to a running process, add this to the Java command line:
681 // "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000"
682
683 CString cmdLine;
684 cmdLine.setf("\"%s\" " // javaPath
685 "-Dcom.android.sdkmanager.toolsdir=\"%s\" " // toolsDir
686 "-Dcom.android.sdkmanager.workdir=\"%s\" " // workDir==toolsdir
687 "-classpath \"lib\\sdkmanager.jar;lib\\swtmenubar.jar;lib\\%s\\swt.jar\" " // arch
688 "com.android.sdkmanager.Main "
689 "%s", // extra parameters
Raphael09439872011-11-17 15:50:48 -0800690 javawPath.baseName(), toolsDir, tmpDir, arch.cstr(), lpCmdLine);
Raphael97f3e042011-11-11 18:08:58 -0800691
Raphael09439872011-11-17 15:50:48 -0800692 if (gDebug) msgBox("Executing: %s", cmdLine.cstr());
Raphael97f3e042011-11-11 18:08:58 -0800693
Raphael09439872011-11-17 15:50:48 -0800694 if (!execNoWait(javawPath.cstr(), cmdLine.cstr(), tmpDir)) {
695 displayLastError("Failed to run %s", cmdLine.cstr());
Raphael97f3e042011-11-11 18:08:58 -0800696 return false;
697 }
698
699 return true;
700}
701
702int APIENTRY WinMain(HINSTANCE hInstance,
703 HINSTANCE hPrevInstance,
704 LPTSTR lpCmdLine,
705 int nCmdShow) {
706
707 gDebug = (getenv("ANDROID_SDKMAN_DEBUG") != NULL);
708
709 PVOID oldWow64Value = disableWow64FsRedirection();
710
711 CPath javaPath;
712 if (!findJavaInEnvPath(&javaPath) &&
713 !findJavaInRegistry(&javaPath) &&
714 !findJavaInProgramFiles(&javaPath)) {
715 msgBox("Failed to find Java on your system. Please reinstall it.");
716 return 2;
717 }
718 _ASSERT(!javaPath.isEmpty());
719
720 revertWow64FsRedirection(oldWow64Value);
721
722 // For debugging it's more convenient to be able to override the tools directory location
723 CPath toolsDir(getenv("ANDROID_SDKMAN_TOOLS_DIR"));
724 if (toolsDir.isEmpty()) {
725 if (!getModuleDir(&toolsDir)) {
726 displayLastError("Failed to get program's filename: ");
727 return 1;
728 }
729 }
730 _ASSERT(!toolsDir.isEmpty());
731
732 CPath tmpDir;
733 if (!mkTempDir("temp-android-tool", &tmpDir)) {
734 return 1;
735 }
736 _ASSERT(!tmpDir.isEmpty());
737
Raphael09439872011-11-17 15:50:48 -0800738 if (!mkDirs(tmpDir.cstr(), sMkDirList)) {
Raphael97f3e042011-11-11 18:08:58 -0800739 return 1;
740 }
741
Raphael09439872011-11-17 15:50:48 -0800742 if (!copyFiles(toolsDir.cstr(), tmpDir.cstr(), sFilesToCopy)) {
Raphael97f3e042011-11-11 18:08:58 -0800743 return 1;
744 }
745
Raphael09439872011-11-17 15:50:48 -0800746 if (!execSdkManager(javaPath.cstr(), toolsDir.cstr(), tmpDir.cstr(), lpCmdLine)) {
Raphael97f3e042011-11-11 18:08:58 -0800747 displayLastError("Failed to start SDK Manager: ");
748 return 1;
749 }
750
751 return 0;
752}
753#endif /* _WIN32 */