blob: 32d090cf7c845f9202fdbf57de778931d3e0a120 [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
Raphael628920b2011-11-22 10:40:13 -080030#include "utils.h"
31#include "find_java.h"
Raphael97f3e042011-11-11 18:08:58 -080032
Raphael97f3e042011-11-11 18:08:58 -080033
34// A NULL-terminated list of directory to create in the temp folder.
35static const char * sMkDirList[] = {
36 "lib",
37 "lib\\x86",
38 "lib\\x86_64",
39 NULL,
40};
41
42// A NULL-terminated list of file patterns to copy in the temp folder.
43// The folders must be listed in sMkDirList
44static const char * sFilesToCopy[] = {
45 "lib\\x86\\swt.jar",
46 "lib\\x86_64\\swt.jar",
47 "lib\\androidprefs.jar",
48 "lib\\org.eclipse.*",
49 "lib\\sdk*",
50 "lib\\common.jar",
51 "lib\\commons-compress*",
52 "lib\\swtmenubar.jar",
53 "lib\\commons-logging*",
54 "lib\\commons-codec*",
55 "lib\\httpclient*",
56 "lib\\httpcore*",
57 "lib\\httpmime*",
58 NULL,
59};
60
Raphael97f3e042011-11-11 18:08:58 -080061
62// Creates a directory named dirLeafName in the TEMP directory.
63// Returns the path in outDir on success.
64static bool mkTempDir(const char *dirLeafName, CPath *outDir) {
65 SetLastError(0);
66 char tempPath[MAX_PATH + 1] = "";
67 DWORD len = GetTempPathA(MAX_PATH, tempPath);
68 if (len > 0 && len <= MAX_PATH) {
69 _ASSERT(tempPath[len-1] == '\\');
70 _ASSERT(len + strlen(dirLeafName) < MAX_PATH);
71 if (len + strlen(dirLeafName) >= MAX_PATH) {
72 displayLastError("TEMP path too long to create a temporary directory: %s", tempPath);
73 return false;
74 }
75 strcat(tempPath, dirLeafName);
76 outDir->set(tempPath);
77
78 if (outDir->dirExists() ||
79 CreateDirectoryA(tempPath, NULL /*lpSecurityAttributes*/) != 0) {
80 return true;
81 }
82 }
83 displayLastError("Failed to create a temporary directory: %s", tempPath);
84 return false;
85}
86
87// Creates all the directories from sMkDirList in the specified base tmpDir.
88static bool mkDirs(const char *tmpDir, const char * dirList[]) {
89 SetLastError(0);
90 for (const char **dir = dirList; *dir != NULL; dir++) {
91 CPath path(tmpDir);
92 path.addPath(*dir);
93 if (!path.dirExists()) {
Raphael09439872011-11-17 15:50:48 -080094 if (!CreateDirectoryA(path.cstr(), NULL /*lpSecurityAttributes*/)) {
95 displayLastError("Failed to create directory: %s", path.cstr());
Raphael97f3e042011-11-11 18:08:58 -080096 return false;
97 }
98 }
99 }
100 return true;
101}
102
103static bool copyFiles(const char *toolsDir, const char *tmpDir, const char *globList[]) {
104 SetLastError(0);
105 WIN32_FIND_DATAA srcFindData;
106 WIN32_FIND_DATAA destFindData;
107 for (const char **glob = globList; *glob != NULL; glob++) {
108 CPath globDir = CPath(*glob).dirName();
109
110 CPath fullGlob(toolsDir);
111 fullGlob.addPath(*glob);
112
Raphael09439872011-11-17 15:50:48 -0800113 HANDLE srcH = FindFirstFileA(fullGlob.cstr(), &srcFindData);
Raphael97f3e042011-11-11 18:08:58 -0800114 if (srcH == INVALID_HANDLE_VALUE) {
115 displayLastError("Failed to list files: %s", *glob);
116 return false;
117 }
118 do {
119 // Skip directories
120 if ((srcFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
121 continue;
122 }
123 CPath srcPath(toolsDir);
124 srcPath.addPath(globDir).addPath(srcFindData.cFileName);
125
126 CPath destPath(tmpDir);
127 destPath.addPath(globDir).addPath(srcFindData.cFileName);
128
129 // Skip copy if files are likely to not have changed.
Raphael09439872011-11-17 15:50:48 -0800130 HANDLE destH = FindFirstFileA(destPath.cstr(), &destFindData);
Raphael97f3e042011-11-11 18:08:58 -0800131 if (destH != INVALID_HANDLE_VALUE) {
132 // Size must be same for us to skip it.
133 if (srcFindData.nFileSizeHigh == destFindData.nFileSizeHigh &&
134 srcFindData.nFileSizeLow == destFindData.nFileSizeLow) {
135 // Creation & access times can differ. However if the dest write time
136 // is >= than the source write time, it should be the same file.
137 LARGE_INTEGER srcWriteTime;
138 LARGE_INTEGER dstWriteTime;
139 srcWriteTime.HighPart = srcFindData.ftLastWriteTime.dwHighDateTime;
140 srcWriteTime.LowPart = srcFindData.ftLastWriteTime.dwLowDateTime;
141 dstWriteTime.HighPart = destFindData.ftLastWriteTime.dwHighDateTime;
142 dstWriteTime.LowPart = destFindData.ftLastWriteTime.dwLowDateTime;
143 if (dstWriteTime.QuadPart >= srcWriteTime.QuadPart) {
144 FindClose(destH);
145 continue;
146 }
147 }
148
149 FindClose(destH);
150
151 // CopyFile copies some attributes. It's common for tools to be unzipped
152 // as read-only so we need to remove any r-o attribute on existing
153 // files if we want a recopy to succeed.
Raphael1bb74402011-11-29 14:33:26 -0800154 if ((destFindData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0) {
Raphael09439872011-11-17 15:50:48 -0800155 SetFileAttributes(destPath.cstr(),
Raphael97f3e042011-11-11 18:08:58 -0800156 destFindData.dwFileAttributes ^ FILE_ATTRIBUTE_READONLY);
157 }
158 }
159
Raphael09439872011-11-17 15:50:48 -0800160 if (!CopyFileA(srcPath.cstr(), destPath.cstr(), false /*bFailIfExists*/)) {
Raphael97f3e042011-11-11 18:08:58 -0800161 FindClose(srcH);
Raphael09439872011-11-17 15:50:48 -0800162 displayLastError("Failed to copy file: %s", destPath.cstr());
Raphael97f3e042011-11-11 18:08:58 -0800163 return false;
164 }
165 } while (FindNextFileA(srcH, &srcFindData) != 0);
166 FindClose(srcH);
167 }
168 return true;
169}
170
171static bool execSdkManager(const char *javaPath,
172 const char *toolsDir,
173 const char *tmpDir,
174 const char *lpCmdLine) {
175 SetLastError(0);
176
Raphael1bb74402011-11-29 14:33:26 -0800177 // Which java binary to call.
178 // The default is to use java.exe to automatically dump stdout in
179 // the parent console.
180 CPath javaExecPath(javaPath);
181
182 // Attach to the parent console, if there's one.
183 if (AttachConsole(-1) == 0) {
184 // This can fail with ERROR_ACCESS_DENIED if the process is already
185 // attached to the parent console. That means there's a console so
186 // we want to keep invoking java.exe to get stdout into it.
187 //
188 // This also fails if there is no parent console, in which
189 // it means this was invoked not from a shell. It's a good
190 // signal we don't want a new console to show up so we'll
191 // switch to javaw.exe instead, if available.
192
193 if (GetLastError() != ERROR_ACCESS_DENIED) {
194 SetLastError(0);
195
196 javaExecPath.replaceName("java.exe", "javaw.exe");
197 // Only accept it if we can actually find the exec
198 PVOID oldWow64Value = disableWow64FsRedirection();
199 if (!javaExecPath.fileExists()) {
200 javaExecPath.set(javaPath);
201 }
202 revertWow64FsRedirection(&oldWow64Value);
203 }
Raphael97f3e042011-11-11 18:08:58 -0800204 }
Raphael97f3e042011-11-11 18:08:58 -0800205
206 // Check whether the underlying system is x86 or x86_64.
207 // We use GetSystemInfo which will see the one masqueraded by Wow64.
208 // (to get the real info, we would use GetNativeSystemInfo instead.)
209 SYSTEM_INFO sysInfo;
210 GetSystemInfo(&sysInfo);
211
212 CString arch("x86");
213 if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
214 arch.set("x86_64");
215 } else if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) {
216 // Skip this. We'll just assume x86 and let it fail later.
Raphael1bb74402011-11-29 14:33:26 -0800217 // Keep this line for debugging purposes:
Raphael97f3e042011-11-11 18:08:58 -0800218 // displayLastError("Unknown Processor Architecture: %d", sysInfo.wProcessorArchitecture);
Raphael97f3e042011-11-11 18:08:58 -0800219 }
220
221 // Now build the command line.
Raphael1bb74402011-11-29 14:33:26 -0800222 // Note that we pass the absolute javaExecPath both to CreateProcess (via execNoWait)
223 // and we set it as argv[0] in the command line just for the show.
Raphael97f3e042011-11-11 18:08:58 -0800224 // Important: for the classpath to be able to contain "lib\\sdkmanager.jar", etc.,
225 // we need to set the toolsDir as the *temp* directory in execNoWait.
226 // It's important to not use toolsDir otherwise it would lock that diretory.
227
Raphael97f3e042011-11-11 18:08:58 -0800228 CString cmdLine;
229 cmdLine.setf("\"%s\" " // javaPath
230 "-Dcom.android.sdkmanager.toolsdir=\"%s\" " // toolsDir
231 "-Dcom.android.sdkmanager.workdir=\"%s\" " // workDir==toolsdir
232 "-classpath \"lib\\sdkmanager.jar;lib\\swtmenubar.jar;lib\\%s\\swt.jar\" " // arch
233 "com.android.sdkmanager.Main "
234 "%s", // extra parameters
Raphael1bb74402011-11-29 14:33:26 -0800235 javaExecPath.baseName(), toolsDir, tmpDir, arch.cstr(), lpCmdLine);
236
237 // Tip: to connect the Java debugging to a running process, add this to the Java command line:
238 // "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000"
Raphael97f3e042011-11-11 18:08:58 -0800239
Raphael09439872011-11-17 15:50:48 -0800240 if (gDebug) msgBox("Executing: %s", cmdLine.cstr());
Raphael97f3e042011-11-11 18:08:58 -0800241
Raphael1bb74402011-11-29 14:33:26 -0800242 if (!execNoWait(javaExecPath.cstr(), cmdLine.cstr(), tmpDir)) {
Raphael09439872011-11-17 15:50:48 -0800243 displayLastError("Failed to run %s", cmdLine.cstr());
Raphael97f3e042011-11-11 18:08:58 -0800244 return false;
245 }
246
247 return true;
248}
249
250int APIENTRY WinMain(HINSTANCE hInstance,
251 HINSTANCE hPrevInstance,
252 LPTSTR lpCmdLine,
253 int nCmdShow) {
254
255 gDebug = (getenv("ANDROID_SDKMAN_DEBUG") != NULL);
256
257 PVOID oldWow64Value = disableWow64FsRedirection();
258
259 CPath javaPath;
260 if (!findJavaInEnvPath(&javaPath) &&
261 !findJavaInRegistry(&javaPath) &&
262 !findJavaInProgramFiles(&javaPath)) {
263 msgBox("Failed to find Java on your system. Please reinstall it.");
264 return 2;
265 }
266 _ASSERT(!javaPath.isEmpty());
267
268 revertWow64FsRedirection(oldWow64Value);
269
Raphael1bb74402011-11-29 14:33:26 -0800270 // For debugging it's convenient to override the tools directory location
Raphael97f3e042011-11-11 18:08:58 -0800271 CPath toolsDir(getenv("ANDROID_SDKMAN_TOOLS_DIR"));
272 if (toolsDir.isEmpty()) {
273 if (!getModuleDir(&toolsDir)) {
274 displayLastError("Failed to get program's filename: ");
275 return 1;
276 }
277 }
278 _ASSERT(!toolsDir.isEmpty());
279
280 CPath tmpDir;
281 if (!mkTempDir("temp-android-tool", &tmpDir)) {
282 return 1;
283 }
284 _ASSERT(!tmpDir.isEmpty());
285
Raphael09439872011-11-17 15:50:48 -0800286 if (!mkDirs(tmpDir.cstr(), sMkDirList)) {
Raphael97f3e042011-11-11 18:08:58 -0800287 return 1;
288 }
289
Raphael09439872011-11-17 15:50:48 -0800290 if (!copyFiles(toolsDir.cstr(), tmpDir.cstr(), sFilesToCopy)) {
Raphael97f3e042011-11-11 18:08:58 -0800291 return 1;
292 }
293
Raphael09439872011-11-17 15:50:48 -0800294 if (!execSdkManager(javaPath.cstr(), toolsDir.cstr(), tmpDir.cstr(), lpCmdLine)) {
Raphael97f3e042011-11-11 18:08:58 -0800295 displayLastError("Failed to start SDK Manager: ");
296 return 1;
297 }
298
299 return 0;
300}
301#endif /* _WIN32 */