int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { gDebug = (getenv("ANDROID_SDKMAN_DEBUG") != NULL); PVOID oldWow64Value = disableWow64FsRedirection(); 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()); revertWow64FsRedirection(oldWow64Value); // 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; }
void createTestFile(const string& testFilePath, const char* testFileText, int size /*0*/) // write a test file to the test directory // the size option is for 16 and 32 bit UTF files with NULs in the text { // verify test directory string testDir = getTestDirectory(); if (testFilePath.compare(0, testDir.length(), testDir) != 0 || !(testFilePath[testDir.length()] == '/' || testFilePath[testDir.length()] == '\\')) ASTYLE_ABORT("File not written to test directory: " + testFilePath); // write the output file ofstream fout(testFilePath.c_str(), ios::binary | ios::trunc); if (!fout) { #ifdef _WIN32 displayLastError(); #endif ASTYLE_ABORT("Cannot open output file: " + testFilePath); } if (size == 0) fout << testFileText; else fout.write(testFileText, size); fout.close(); }
void cleanTestDirectory(const wstring& directory) // Windows remove files and sub directories from the test directory { WIN32_FIND_DATAW FindFileData; // Find the first file in the directory // Find will get at least "." and "..". wstring firstFile = directory + L"\\*"; HANDLE hFind = ::FindFirstFileW(firstFile.c_str(), &FindFileData); if (hFind == INVALID_HANDLE_VALUE) { displayLastError(); systemAbort(L"Cannot open directory for clean: " + directory); } // remove files and sub directories do { // skip these if (wcscmp(FindFileData.cFileName, L".") == 0 || wcscmp(FindFileData.cFileName, L"..") == 0) continue; // clean and remove sub directories if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { wstring subDirectoryPath = directory + L"\\" + FindFileData.cFileName; cleanTestDirectory(subDirectoryPath); BOOL isRemoved = ::RemoveDirectoryW(subDirectoryPath.c_str()); if (!isRemoved) retryRemoveDirectory(subDirectoryPath); continue; } // remove the file wstring filePathName = directory + L"\\" + FindFileData.cFileName; BOOL isRemoved = ::DeleteFileW(filePathName.c_str()); if (!isRemoved) { displayLastError(); systemAbort(L"Cannot remove file for clean: " + filePathName); } } while (::FindNextFileW(hFind, &FindFileData) != 0); // check for processing error FindClose(hFind); DWORD dwError = GetLastError(); if (dwError != ERROR_NO_MORE_FILES) systemAbort(L"Error processing directory for clean: " + directory); }
// 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; }
void retryRemoveDirectory(const wstring& directory) // WINDOWS wait for files and sub-directories to be removed { // sleep a max of 20 seconds for the remove for (size_t seconds = 1; seconds <= 20; seconds++) { sleep(1); BOOL isRemoved = ::RemoveDirectoryW(directory.c_str()); if (isRemoved) { // cout << "remove retry: " << seconds << " seconds" << endl; return; } } displayLastError(); systemAbort(L"Cannot remove file for clean: " + directory); }
void retryCreateDirectory(const string& directory) // WINDOWS wait for directories to be removed { // sleep a max of 20 seconds for the remove for (size_t seconds = 1; seconds <= 20; seconds++) { sleep(1); BOOL ok = ::CreateDirectory(directory.c_str(), NULL); if (ok || GetLastError() == ERROR_ALREADY_EXISTS) { // cout << "create retry: " << seconds << " seconds" << endl; return; } } displayLastError(); systemAbort("Cannot create directory: " + directory); }
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); // 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); } revertWow64FsRedirection(&oldWow64Value); } } // 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\" " // javaPath "-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; }
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; }
// Tries to invoke the java.exe at the given path and extract it's // version number. // - outVersionStr: if not null, will capture version as a string (e.g. "1.6") // - outVersionInt: if not null, will capture version as an int (major * 1000 + minor, e.g. 1006). bool getJavaVersion(CPath &javaPath, CString *outVersionStr, int *outVersionInt) { bool result = false; // 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; STARTUPINFO startup; PROCESS_INFORMATION pinfo; // Want to inherit pipe handle ZeroMemory(&saAttr, sizeof(saAttr)); saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; // Create pipe for stdout HANDLE stdoutPipeRd, stdoutPipeWt; if (!CreatePipe( &stdoutPipeRd, // hReadPipe, &stdoutPipeWt, // hWritePipe, &saAttr, // lpPipeAttributes, 0)) { // nSize (0=default buffer size) if (gIsConsole || gIsDebug) displayLastError("CreatePipe failed: "); return false; } if (!SetHandleInformation(stdoutPipeRd, HANDLE_FLAG_INHERIT, 0)) { if (gIsConsole || gIsDebug) displayLastError("SetHandleInformation failed: "); return false; } ZeroMemory(&pinfo, sizeof(pinfo)); ZeroMemory(&startup, sizeof(startup)); startup.cb = sizeof(startup); 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); 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 ((gIsConsole || gIsDebug) && !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; if (GetExitCodeProcess(pinfo.hProcess, &exitCode)) { // this should not return STILL_ACTIVE (259) result = exitCode == 0; } CloseHandle(pinfo.hProcess); CloseHandle(pinfo.hThread); } CloseHandle(stdoutPipeRd); if (result && 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 ((gIsConsole || gIsDebug) && (!j || !v)) { fprintf(stderr, "Error: keywords 'java version' not found in '%s'\n", first32); } if (j != NULL && v != NULL) { result = extractJavaVersion(first32, index, outVersionStr, outVersionInt); } } return result; }