| Raphael | 97f3e04 | 2011-11-11 18:08:58 -0800 | [diff] [blame] | 1 | /*
|
| 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 |
|
| Raphael | 628920b | 2011-11-22 10:40:13 -0800 | [diff] [blame] | 30 | #include "utils.h"
|
| 31 | #include "find_java.h"
|
| Raphael | 97f3e04 | 2011-11-11 18:08:58 -0800 | [diff] [blame] | 32 |
|
| Raphael | 97f3e04 | 2011-11-11 18:08:58 -0800 | [diff] [blame] | 33 |
|
| 34 | // A NULL-terminated list of directory to create in the temp folder.
|
| 35 | static 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
|
| 44 | static 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 |
|
| Raphael | 97f3e04 | 2011-11-11 18:08:58 -0800 | [diff] [blame] | 61 |
|
| 62 | // Creates a directory named dirLeafName in the TEMP directory.
|
| 63 | // Returns the path in outDir on success.
|
| 64 | static 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.
|
| 88 | static 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()) {
|
| Raphael | 0943987 | 2011-11-17 15:50:48 -0800 | [diff] [blame] | 94 | if (!CreateDirectoryA(path.cstr(), NULL /*lpSecurityAttributes*/)) {
|
| 95 | displayLastError("Failed to create directory: %s", path.cstr());
|
| Raphael | 97f3e04 | 2011-11-11 18:08:58 -0800 | [diff] [blame] | 96 | return false;
|
| 97 | }
|
| 98 | }
|
| 99 | }
|
| 100 | return true;
|
| 101 | }
|
| 102 |
|
| 103 | static 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 |
|
| Raphael | 0943987 | 2011-11-17 15:50:48 -0800 | [diff] [blame] | 113 | HANDLE srcH = FindFirstFileA(fullGlob.cstr(), &srcFindData);
|
| Raphael | 97f3e04 | 2011-11-11 18:08:58 -0800 | [diff] [blame] | 114 | 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.
|
| Raphael | 0943987 | 2011-11-17 15:50:48 -0800 | [diff] [blame] | 130 | HANDLE destH = FindFirstFileA(destPath.cstr(), &destFindData);
|
| Raphael | 97f3e04 | 2011-11-11 18:08:58 -0800 | [diff] [blame] | 131 | 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.
|
| Raphael | 1bb7440 | 2011-11-29 14:33:26 -0800 | [diff] [blame^] | 154 | if ((destFindData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0) {
|
| Raphael | 0943987 | 2011-11-17 15:50:48 -0800 | [diff] [blame] | 155 | SetFileAttributes(destPath.cstr(),
|
| Raphael | 97f3e04 | 2011-11-11 18:08:58 -0800 | [diff] [blame] | 156 | destFindData.dwFileAttributes ^ FILE_ATTRIBUTE_READONLY);
|
| 157 | }
|
| 158 | }
|
| 159 |
|
| Raphael | 0943987 | 2011-11-17 15:50:48 -0800 | [diff] [blame] | 160 | if (!CopyFileA(srcPath.cstr(), destPath.cstr(), false /*bFailIfExists*/)) {
|
| Raphael | 97f3e04 | 2011-11-11 18:08:58 -0800 | [diff] [blame] | 161 | FindClose(srcH);
|
| Raphael | 0943987 | 2011-11-17 15:50:48 -0800 | [diff] [blame] | 162 | displayLastError("Failed to copy file: %s", destPath.cstr());
|
| Raphael | 97f3e04 | 2011-11-11 18:08:58 -0800 | [diff] [blame] | 163 | return false;
|
| 164 | }
|
| 165 | } while (FindNextFileA(srcH, &srcFindData) != 0);
|
| 166 | FindClose(srcH);
|
| 167 | }
|
| 168 | return true;
|
| 169 | }
|
| 170 |
|
| 171 | static bool execSdkManager(const char *javaPath,
|
| 172 | const char *toolsDir,
|
| 173 | const char *tmpDir,
|
| 174 | const char *lpCmdLine) {
|
| 175 | SetLastError(0);
|
| 176 |
|
| Raphael | 1bb7440 | 2011-11-29 14:33:26 -0800 | [diff] [blame^] | 177 | // 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 | }
|
| Raphael | 97f3e04 | 2011-11-11 18:08:58 -0800 | [diff] [blame] | 204 | }
|
| Raphael | 97f3e04 | 2011-11-11 18:08:58 -0800 | [diff] [blame] | 205 |
|
| 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.
|
| Raphael | 1bb7440 | 2011-11-29 14:33:26 -0800 | [diff] [blame^] | 217 | // Keep this line for debugging purposes:
|
| Raphael | 97f3e04 | 2011-11-11 18:08:58 -0800 | [diff] [blame] | 218 | // displayLastError("Unknown Processor Architecture: %d", sysInfo.wProcessorArchitecture);
|
| Raphael | 97f3e04 | 2011-11-11 18:08:58 -0800 | [diff] [blame] | 219 | }
|
| 220 |
|
| 221 | // Now build the command line.
|
| Raphael | 1bb7440 | 2011-11-29 14:33:26 -0800 | [diff] [blame^] | 222 | // 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.
|
| Raphael | 97f3e04 | 2011-11-11 18:08:58 -0800 | [diff] [blame] | 224 | // 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 |
|
| Raphael | 97f3e04 | 2011-11-11 18:08:58 -0800 | [diff] [blame] | 228 | 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
|
| Raphael | 1bb7440 | 2011-11-29 14:33:26 -0800 | [diff] [blame^] | 235 | 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"
|
| Raphael | 97f3e04 | 2011-11-11 18:08:58 -0800 | [diff] [blame] | 239 |
|
| Raphael | 0943987 | 2011-11-17 15:50:48 -0800 | [diff] [blame] | 240 | if (gDebug) msgBox("Executing: %s", cmdLine.cstr());
|
| Raphael | 97f3e04 | 2011-11-11 18:08:58 -0800 | [diff] [blame] | 241 |
|
| Raphael | 1bb7440 | 2011-11-29 14:33:26 -0800 | [diff] [blame^] | 242 | if (!execNoWait(javaExecPath.cstr(), cmdLine.cstr(), tmpDir)) {
|
| Raphael | 0943987 | 2011-11-17 15:50:48 -0800 | [diff] [blame] | 243 | displayLastError("Failed to run %s", cmdLine.cstr());
|
| Raphael | 97f3e04 | 2011-11-11 18:08:58 -0800 | [diff] [blame] | 244 | return false;
|
| 245 | }
|
| 246 |
|
| 247 | return true;
|
| 248 | }
|
| 249 |
|
| 250 | int 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 |
|
| Raphael | 1bb7440 | 2011-11-29 14:33:26 -0800 | [diff] [blame^] | 270 | // For debugging it's convenient to override the tools directory location
|
| Raphael | 97f3e04 | 2011-11-11 18:08:58 -0800 | [diff] [blame] | 271 | 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 |
|
| Raphael | 0943987 | 2011-11-17 15:50:48 -0800 | [diff] [blame] | 286 | if (!mkDirs(tmpDir.cstr(), sMkDirList)) {
|
| Raphael | 97f3e04 | 2011-11-11 18:08:58 -0800 | [diff] [blame] | 287 | return 1;
|
| 288 | }
|
| 289 |
|
| Raphael | 0943987 | 2011-11-17 15:50:48 -0800 | [diff] [blame] | 290 | if (!copyFiles(toolsDir.cstr(), tmpDir.cstr(), sFilesToCopy)) {
|
| Raphael | 97f3e04 | 2011-11-11 18:08:58 -0800 | [diff] [blame] | 291 | return 1;
|
| 292 | }
|
| 293 |
|
| Raphael | 0943987 | 2011-11-17 15:50:48 -0800 | [diff] [blame] | 294 | if (!execSdkManager(javaPath.cstr(), toolsDir.cstr(), tmpDir.cstr(), lpCmdLine)) {
|
| Raphael | 97f3e04 | 2011-11-11 18:08:58 -0800 | [diff] [blame] | 295 | displayLastError("Failed to start SDK Manager: ");
|
| 296 | return 1;
|
| 297 | }
|
| 298 |
|
| 299 | return 0;
|
| 300 | }
|
| 301 | #endif /* _WIN32 */
|