blob: 64170512114a959d4b6ea9fbcd6cbbd6ffc74b67 [file] [log] [blame]
/*
* 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"
// 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);
CPath javawPath(javaPath);
javawPath.replaceName("java.exe", "javaw.exe");
// Only accept it if we can actually find the exec
PVOID oldWow64Value = disableWow64FsRedirection();
if (!javawPath.fileExists()) {
javawPath.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.
// displayLastError("Unknown Processor Architecture: %d", sysInfo.wProcessorArchitecture);
// return false;
}
// Now build the command line.
// Note that we pass the absolute javawPath to execNoWait
// and the first parameter is 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.
// 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"
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
javawPath.baseName(), toolsDir, tmpDir, arch.cstr(), lpCmdLine);
if (gDebug) msgBox("Executing: %s", cmdLine.cstr());
if (!execNoWait(javawPath.cstr(), cmdLine.cstr(), tmpDir)) {
displayLastError("Failed to run %s", cmdLine.cstr());
return false;
}
return true;
}
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 more convenient to be able 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 */