From b3e48a75760bed45d372f7571a1df257d891eb8b Mon Sep 17 00:00:00 2001 From: baldurk Date: Thu, 14 Dec 2017 14:38:39 +0000 Subject: [PATCH] Add search logic to find android tools automagically, which we ship * We search first in specified folders by the user (they can browse to the android SDK and java JDK). * If the tools we want aren't found there, we look relative to the UI as we now distribute the required tools with windows builds. * If we still don't find them, we prefer to look in PATH since the user has 'opted in' to any tools found in there. If the tool isn't in PATH either then we look relative to known environment variables. --- docs/credits_acknowledgements.rst | 4 + .../Code/Interface/PersistantConfig.cpp | 10 +- qrenderdoc/Code/Interface/PersistantConfig.h | 16 +- qrenderdoc/Windows/Dialogs/SettingsDialog.cpp | 54 ++- qrenderdoc/Windows/Dialogs/SettingsDialog.h | 6 +- qrenderdoc/Windows/Dialogs/SettingsDialog.ui | 33 +- renderdoc/core/android.cpp | 386 +++++++++++++++--- scripts/dist.sh | 47 ++- scripts/installer/LICENSE.rtf | 1 + 9 files changed, 466 insertions(+), 91 deletions(-) diff --git a/docs/credits_acknowledgements.rst b/docs/credits_acknowledgements.rst index 264af4941..f3d3d3776 100644 --- a/docs/credits_acknowledgements.rst +++ b/docs/credits_acknowledgements.rst @@ -108,6 +108,10 @@ The following libraries and components are incorporated into RenderDoc, listed h Used for converting capture files to and from xml. +* `AOSP `_ - Copyright (c) 2006-2016, The Android Open Source Project, distributed under the Apache 2.0 License. + + Used to simplify Android workflows by distributing some tools from the android SDK. + Thanks ------ diff --git a/qrenderdoc/Code/Interface/PersistantConfig.cpp b/qrenderdoc/Code/Interface/PersistantConfig.cpp index 85189dbe3..75fe8416f 100644 --- a/qrenderdoc/Code/Interface/PersistantConfig.cpp +++ b/qrenderdoc/Code/Interface/PersistantConfig.cpp @@ -191,11 +191,13 @@ void PersistantConfig::AddAndroidHosts() } } - QString adbExePath = - QFile::exists(Android_AdbExecutablePath) ? QString(Android_AdbExecutablePath) : QString(); + QString androidSDKPath = QFile::exists(Android_SDKPath) ? QString(Android_SDKPath) : QString(); - // Set the config setting as it will be reused when we start the remoteserver etc. - SetConfigSetting("adbExePath", adbExePath); + SetConfigSetting("androidSDKPath", androidSDKPath); + + QString androidJDKPath = QFile::exists(Android_JDKPath) ? QString(Android_JDKPath) : QString(); + + SetConfigSetting("androidJDKPath", androidJDKPath); SetConfigSetting("MaxConnectTimeout", QString::number(Android_MaxConnectTimeout)); diff --git a/qrenderdoc/Code/Interface/PersistantConfig.h b/qrenderdoc/Code/Interface/PersistantConfig.h index e6134301e..3504ab120 100644 --- a/qrenderdoc/Code/Interface/PersistantConfig.h +++ b/qrenderdoc/Code/Interface/PersistantConfig.h @@ -122,7 +122,9 @@ DECLARE_REFLECTION_STRUCT(SPIRVDisassembler); \ CONFIG_SETTING_VAL(public, bool, bool, Font_PreferMonospaced, false) \ \ - CONFIG_SETTING_VAL(public, QString, rdcstr, Android_AdbExecutablePath, "") \ + CONFIG_SETTING_VAL(public, QString, rdcstr, Android_SDKPath, "") \ + \ + CONFIG_SETTING_VAL(public, QString, rdcstr, Android_JDKPath, "") \ \ CONFIG_SETTING_VAL(public, int, int, Android_MaxConnectTimeout, 30) \ \ @@ -374,9 +376,17 @@ For more information about some of these settings that are user-facing see Defaults to ``False``. -.. data:: Android_AdbExecutablePath +.. data:: Android_SDKPath - The path to the ``adb`` executable to use for Android interaction. + The path to the root of the android SDK, to locate android tools to use for android remote hosts. + + Defaults to using the tools distributed with RenderDoc. + +.. data:: Android_JDKPath + + The path to the root of the Java JDK, to locate java for running android java tools. + + Defaults to using the JAVA_HOME environment variable, if set. .. data:: Android_MaxConnectTimeout diff --git a/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp b/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp index 6db7ac587..982089c48 100644 --- a/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp +++ b/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp @@ -84,7 +84,8 @@ SettingsDialog::SettingsDialog(ICaptureContext &ctx, QWidget *parent) ui->externalDisassemblerArgs->setText(m_Ctx.Config().SPIRVDisassemblers[0].args); ui->externalDisassemblePath->setText(m_Ctx.Config().SPIRVDisassemblers[0].executable); } - ui->Android_AdbExecutablePath->setText(m_Ctx.Config().Android_AdbExecutablePath); + ui->Android_SDKPath->setText(m_Ctx.Config().Android_SDKPath); + ui->Android_JDKPath->setText(m_Ctx.Config().Android_JDKPath); ui->Android_MaxConnectTimeout->setValue(m_Ctx.Config().Android_MaxConnectTimeout); ui->Android_AutoPushLayerToApp->setChecked(m_Ctx.Config().Android_AutoPushLayerToApp); @@ -388,21 +389,52 @@ void SettingsDialog::on_browseTempCaptureDirectory_clicked() m_Ctx.Config().Save(); } -void SettingsDialog::on_browseAdbPath_clicked() +void SettingsDialog::on_browseAndroidSDKPath_clicked() { - QString adb = RDDialog::getExecutableFileName( - this, tr("Locate adb executable"), - QFileInfo(m_Ctx.Config().Android_AdbExecutablePath).absoluteDir().path()); + QString adb = RDDialog::getExistingDirectory( + this, tr("Locate SDK root folder (containing build-tools, platform-tools)"), + QFileInfo(m_Ctx.Config().Android_SDKPath).absoluteDir().path()); if(!adb.isEmpty()) { - ui->Android_AdbExecutablePath->setText(adb); - m_Ctx.Config().Android_AdbExecutablePath = adb; + ui->Android_SDKPath->setText(adb); + m_Ctx.Config().Android_SDKPath = adb; } m_Ctx.Config().Save(); } +void SettingsDialog::on_Android_SDKPath_textEdited(const QString &adb) +{ + if(QFileInfo::exists(adb) || adb.isEmpty()) + m_Ctx.Config().Android_SDKPath = adb; + + m_Ctx.Config().Save(); +} + +void SettingsDialog::on_browseAndroidJDKPath_clicked() +{ + QString adb = + RDDialog::getExistingDirectory(this, tr("Locate JDK root folder (containing bin, jre, lib)"), + QFileInfo(m_Ctx.Config().Android_JDKPath).absoluteDir().path()); + + if(!adb.isEmpty()) + { + ui->Android_JDKPath->setText(adb); + m_Ctx.Config().Android_JDKPath = adb; + } + + m_Ctx.Config().Save(); +} + +void SettingsDialog::on_Android_JDKPath_textEdited(const QString &adb) +{ + if(QFileInfo::exists(adb) || adb.isEmpty()) + m_Ctx.Config().Android_JDKPath = adb; + + m_Ctx.Config().Save(); +} + void SettingsDialog::on_Android_MaxConnectTimeout_valueChanged(double timeout) { m_Ctx.Config().Android_MaxConnectTimeout = ui->Android_MaxConnectTimeout->value(); @@ -410,14 +442,6 @@ void SettingsDialog::on_Android_MaxConnectTimeout_valueChanged(double timeout) m_Ctx.Config().Save(); } -void SettingsDialog::on_Android_AdbExecutablePath_textEdited(const QString &adb) -{ - if(QFileInfo::exists(adb) || adb.isEmpty()) - m_Ctx.Config().Android_AdbExecutablePath = adb; - - m_Ctx.Config().Save(); -} - void SettingsDialog::on_Android_AutoPushLayerToApp_toggled(bool checked) { m_Ctx.Config().Android_AutoPushLayerToApp = ui->Android_AutoPushLayerToApp->isChecked(); diff --git a/qrenderdoc/Windows/Dialogs/SettingsDialog.h b/qrenderdoc/Windows/Dialogs/SettingsDialog.h index 220eff2df..9e1591df7 100644 --- a/qrenderdoc/Windows/Dialogs/SettingsDialog.h +++ b/qrenderdoc/Windows/Dialogs/SettingsDialog.h @@ -87,9 +87,11 @@ private slots: // android void on_browseTempCaptureDirectory_clicked(); - void on_browseAdbPath_clicked(); + void on_browseAndroidSDKPath_clicked(); + void on_browseAndroidJDKPath_clicked(); void on_Android_MaxConnectTimeout_valueChanged(double timeout); - void on_Android_AdbExecutablePath_textEdited(const QString &path); + void on_Android_SDKPath_textEdited(const QString &path); + void on_Android_JDKPath_textEdited(const QString &path); void on_Android_AutoPushLayerToApp_toggled(bool checked); // manual slots diff --git a/qrenderdoc/Windows/Dialogs/SettingsDialog.ui b/qrenderdoc/Windows/Dialogs/SettingsDialog.ui index 402bd3e93..fb5d14a23 100644 --- a/qrenderdoc/Windows/Dialogs/SettingsDialog.ui +++ b/qrenderdoc/Windows/Dialogs/SettingsDialog.ui @@ -974,17 +974,24 @@ Only happens if the capture is not in the recent files list. Android + + + + Java JDK path + + + The location of adb.exe, used to control Android devices. - Android ADB executable path + Android SDK root path - + Qt::Vertical @@ -997,7 +1004,7 @@ Only happens if the capture is not in the recent files list. - + Automatically push the RenderDoc layer to applications that need it when running on a device with root access. @@ -1008,7 +1015,7 @@ This can enable debugging of Vulkan apps that don't already contain the layer. - + Automatically push the RenderDoc layer to applications that need it when running on a device with root access. @@ -1016,7 +1023,7 @@ This can enable debugging of Vulkan apps that don't already contain the layer. - + Maximum time to try connecting to the target app. @@ -1026,7 +1033,7 @@ This can enable debugging of Vulkan apps that don't already contain the layer. - + Maximum time to try connecting to the target app. @@ -1040,14 +1047,14 @@ This can enable debugging of Vulkan apps that don't already contain the layer. - + The location of adb.exe, used to control Android devices. - + The location of adb.exe, used to control Android devices. @@ -1056,6 +1063,16 @@ This can enable debugging of Vulkan apps that don't already contain the layer. + + + + + + + Browse + + + diff --git a/renderdoc/core/android.cpp b/renderdoc/core/android.cpp index 042cb209c..268aa2725 100644 --- a/renderdoc/core/android.cpp +++ b/renderdoc/core/android.cpp @@ -30,6 +30,245 @@ namespace Android { +enum class ToolDir +{ + None, + Java, + BuildTools, + BuildToolsLib, + PlatformTools, +}; +static const char keystoreName[] = "renderdoc.keystore"; +bool toolExists(const std::string &path) +{ + if(path.empty()) + return false; + return FileIO::exists(path.c_str()) || FileIO::exists((path + ".exe").c_str()); +} +std::string getToolInSDK(ToolDir subdir, const std::string &jdkroot, const std::string &sdkroot, + const std::string &toolname) +{ + std::string toolpath; + + switch(subdir) + { + case ToolDir::None: + { + // This indicates the file is not a standard tool and will not exist anywhere but our + // distributed folder. + break; + } + case ToolDir::Java: + { + // if no path is configured, abort + if(jdkroot.empty()) + break; + + toolpath = jdkroot + "/bin/" + toolname; + + if(toolExists(toolpath)) + return toolpath; + + break; + } + case ToolDir::BuildTools: + case ToolDir::BuildToolsLib: + case ToolDir::PlatformTools: + { + // if no path is configured, abort + if(sdkroot.empty()) + break; + + // if it's in platform tools it's easy, just concatenate the path + if(subdir == ToolDir::PlatformTools) + { + toolpath = sdkroot + "/platform-tools/" + toolname; + } + else + { + // otherwise we need to find the build-tools versioned folder + toolpath = sdkroot + "/build-tools/"; + + std::vector paths = FileIO::GetFilesInDirectory(toolpath.c_str()); + + if(paths.empty()) + break; + + uint32_t bestversion = 0; + std::string bestpath; + + for(const PathEntry &path : paths) + { + // skip non-directories + if(!(path.flags & PathProperty::Directory)) + continue; + + uint32_t version = 0; + bool valid = true; + for(char c : path.filename) + { + // add digits to the version + if(c >= '0' && c <= '9') + { + int digit = int(c) - int('0'); + version *= 10; + version += digit; + continue; + } + + // ignore .s + if(c == '.') + continue; + + // if any char is not in [.0-9] then this filename is invalid + valid = false; + break; + } + + // skip non-valid directories + if(!valid) + continue; + + // if this directory is a higher version, prefer it + if(version > bestversion) + { + bestversion = version; + bestpath = path.filename; + } + } + + // if we didn't find a version at all, abort + if(bestversion == 0) + break; + + toolpath += bestpath + "/"; + + if(subdir == ToolDir::BuildToolsLib) + toolpath += "lib/"; + + toolpath += toolname; + } + + if(toolExists(toolpath)) + return toolpath; + + break; + } + } + + return ""; +} +struct ToolPathCache +{ + std::string sdk, jdk; + std::map paths; +} cache; +std::string getToolPath(ToolDir subdir, const std::string &toolname, bool checkExist) +{ + // search path for tools: + // 1. First look relative to the configured paths, these come from the user manually setting them + // so they always have priority. + // 2. Next if those paths don't exist or the tool isn't found, we search relative to our + // executable looking for an android/ subfolder, and look for the tool in there. + // 3. If we still don't have that (most likely because it's a local build from a git clone and not + // a distributed build with the tools available) then we fall back to trying to auto-locate it. + // - First check if the tool is in the path, assuming the user configured it to their system. + // - Otherwise check environment variables or default locations + + std::string sdk = RenderDoc::Inst().GetConfigSetting("androidSDKPath"); + std::string jdk = RenderDoc::Inst().GetConfigSetting("androidJDKPath"); + + // invalidate the cache when these settings change + if(sdk != cache.sdk || jdk != cache.jdk) + { + cache.paths.clear(); + cache.sdk = sdk; + cache.jdk = jdk; + } + + // if we have the path cached and it's still valid, return it + if(toolExists(cache.paths[toolname])) + return cache.paths[toolname]; + + std::string &toolpath = cache.paths[toolname]; + + // first try according to the configured paths + toolpath = getToolInSDK(subdir, jdk, sdk, toolname); + + if(toolExists(toolpath)) + return toolpath; + + // next try to locate it in our own distributed android subfolder + { + std::string exepath; + FileIO::GetExecutableFilename(exepath); + std::string exedir = dirname(FileIO::GetFullPathname(exepath)); + + toolpath = exedir + "/android/" + toolname; + if(toolExists(toolpath)) + return toolpath; + } + + // need to try to auto-guess the tool's location + + // first try in PATH + if(subdir != ToolDir::None) + { + toolpath = FileIO::FindFileInPath(toolname); + + if(toolExists(toolpath)) + return toolpath; + + // if the tool name contains a .jar then try stripping that and look for the non-.jar version in + // the PATH. + if(toolname.find(".jar") != std::string::npos) + { + toolpath = toolname; + toolpath.erase(toolpath.rfind(".jar"), 4); + toolpath = FileIO::FindFileInPath(toolpath); + + if(toolExists(toolpath)) + return toolpath; + } + } + + // now try to find it based on heuristics/environment variables + const char *env = Process::GetEnvVariable("JAVA_HOME"); + + jdk = env ? env : ""; + + env = Process::GetEnvVariable("ANDROID_HOME"); + sdk = env ? env : ""; + + if(sdk.empty() || !FileIO::exists(sdk.c_str())) + { + env = Process::GetEnvVariable("ANDROID_SDK_ROOT"); + sdk = env ? env : ""; + } + + if(sdk.empty() || !FileIO::exists(sdk.c_str())) + { + env = Process::GetEnvVariable("ANDROID_SDK"); + sdk = env ? env : ""; + } + + // maybe in future we can try to search in common install locations. + + toolpath = getToolInSDK(subdir, jdk, sdk, toolname); + + if(toolExists(toolpath)) + return toolpath; + + toolpath = ""; + + // if we're checking for existence, we have failed so return empty string. + if(checkExist) + return toolpath; + + // otherwise we at least return the tool name so that there's something to try and run + return toolname; +} + bool IsHostADB(const char *hostname) { return !strncmp(hostname, "adb:", 4); @@ -76,35 +315,14 @@ Process::ProcessResult execCommand(const string &exe, const string &args, } Process::ProcessResult adbExecCommand(const string &device, const string &args, const string &workDir) { - string adbExePath = RenderDoc::Inst().GetConfigSetting("adbExePath"); - if(adbExePath.empty()) - { - string exepath; - FileIO::GetExecutableFilename(exepath); - string exedir = dirname(FileIO::GetFullPathname(exepath)); - - string adbpath = exedir + "/android/adb.exe"; - if(FileIO::exists(adbpath.c_str())) - adbExePath = adbpath; - - if(adbExePath.empty()) - { - static bool warnPath = true; - if(warnPath) - { - RDCWARN("adbExePath not set, attempting to call 'adb' in working env"); - warnPath = false; - } - adbExePath = "adb"; - } - } + std::string adb = getToolPath(ToolDir::PlatformTools, "adb", false); Process::ProcessResult result; string deviceArgs; if(device.empty()) deviceArgs = args; else deviceArgs = StringFormat::Fmt("-s %s %s", device.c_str(), args.c_str()); - return execCommand(adbExePath, deviceArgs, workDir); + return execCommand(adb, deviceArgs, workDir); } string adbGetDeviceList() { @@ -175,8 +393,10 @@ bool RemoveAPKSignature(const string &apk) { RDCLOG("Checking for existing signature"); + std::string aapt = getToolPath(ToolDir::BuildTools, "aapt", false); + // Get the list of files in META-INF - string fileList = execCommand("aapt", "list " + apk).strStdout; + string fileList = execCommand(aapt, "list \"" + apk + "\"").strStdout; if(fileList.empty()) return false; @@ -193,7 +413,7 @@ bool RemoveAPKSignature(const string &apk) if(line.compare(0, prefix.size(), prefix) == 0) { RDCDEBUG("Match found, removing %s", line.c_str()); - execCommand("aapt", "remove " + apk + " " + line); + execCommand(aapt, "remove \"" + apk + "\" " + line); matchCount++; } } @@ -201,7 +421,7 @@ bool RemoveAPKSignature(const string &apk) // Ensure no hits on second pass through RDCDEBUG("Walk through file list again, ensure signature removed"); - fileList = execCommand("aapt", "list " + apk).strStdout; + fileList = execCommand(aapt, "list \"" + apk + "\"").strStdout; std::istringstream recheck(fileList); while(std::getline(recheck, line)) { @@ -219,10 +439,12 @@ bool AddLayerToAPK(const string &apk, const string &layerPath, const string &lay { RDCLOG("Adding RenderDoc layer"); + std::string aapt = getToolPath(ToolDir::BuildTools, "aapt", false); + // Run aapt from the directory containing "lib" so the relative paths are good string relativeLayer("lib/" + abi + "/" + layerName); string workDir = removeFromEnd(layerPath, relativeLayer); - Process::ProcessResult result = execCommand("aapt", "add " + apk + " " + relativeLayer, workDir); + Process::ProcessResult result = execCommand(aapt, "add \"" + apk + "\" " + relativeLayer, workDir); if(result.strStdout.empty()) { @@ -235,9 +457,12 @@ bool AddLayerToAPK(const string &apk, const string &layerPath, const string &lay bool RealignAPK(const string &apk, string &alignedAPK, const string &tmpDir) { + std::string zipalign = getToolPath(ToolDir::BuildTools, "zipalign", false); + // Re-align the APK for performance RDCLOG("Realigning APK"); - string errOut = execCommand("zipalign", "-f 4 " + apk + " " + alignedAPK, tmpDir).strStderror; + string errOut = + execCommand(zipalign, "-f 4 \"" + apk + "\" \"" + alignedAPK + "\"", tmpDir).strStderror; if(!errOut.empty()) return false; @@ -263,14 +488,22 @@ bool RealignAPK(const string &apk, string &alignedAPK, const string &tmpDir) string GetAndroidDebugKey() { - string key = FileIO::GetTempFolderFilename() + "debug.keystore"; + std::string keystore = getToolPath(ToolDir::None, keystoreName, false); + + // if we found the keystore, use that. + if(FileIO::exists(keystore.c_str())) + return keystore; + + // otherwise, see if we generated a temporary one + string key = FileIO::GetTempFolderFilename() + keystoreName; if(FileIO::exists(key.c_str())) return key; + // locate keytool and use it to generate a keystore string create; create += " -genkey"; - create += " -keystore " + key; + create += " -keystore \"" + key + "\""; create += " -storepass android"; create += " -alias androiddebugkey"; create += " -keypass android"; @@ -279,7 +512,9 @@ string GetAndroidDebugKey() create += " -validity 10000"; create += " -dname \"CN=, OU=, O=, L=, S=, C=\""; - Process::ProcessResult result = execCommand("keytool", create); + std::string keytool = getToolPath(ToolDir::Java, "keytool", false); + + Process::ProcessResult result = execCommand(keytool, create); if(!result.strStderror.empty()) RDCERR("Failed to create debug key"); @@ -290,19 +525,42 @@ bool DebugSignAPK(const string &apk, const string &workDir) { RDCLOG("Signing with debug key"); + std::string aapt = getToolPath(ToolDir::BuildTools, "aapt", false); + std::string apksigner = getToolPath(ToolDir::BuildToolsLib, "apksigner.jar", false); + string debugKey = GetAndroidDebugKey(); string args; args += " sign "; - args += " --ks " + debugKey + " "; + args += " --ks \"" + debugKey + "\" "; args += " --ks-pass pass:android "; args += " --key-pass pass:android "; args += " --ks-key-alias androiddebugkey "; - args += apk; - execScript("apksigner", args.c_str(), workDir.c_str()); + args += "\"" + apk + "\""; + + if(apksigner.find(".jar") == std::string::npos) + { + // if we found the non-jar version, then the jar wasn't located and we found the wrapper script + // in PATH. Execute it as a script + execScript(apksigner, args, workDir); + } + else + { + // otherwise, find and invoke java on the .jar + std::string java = getToolPath(ToolDir::Java, "java", false); + + std::string signerdir = dirname(FileIO::GetFullPathname(apksigner)); + + std::string javaargs; + javaargs += " \"-Djava.ext.dirs=" + signerdir + "\""; + javaargs += " -jar \"" + apksigner + "\""; + javaargs += args; + + execCommand(java, javaargs, workDir); + } // Check for signature - string list = execCommand("aapt", "list " + apk).strStdout; + string list = execCommand(aapt, "list \"" + apk + "\"").strStdout; // Walk through the output. If it starts with META-INF, we're good std::istringstream contents(list); @@ -353,7 +611,7 @@ bool ReinstallPatchedAPK(const string &deviceID, const string &apk, const string { RDCLOG("Reinstalling APK"); - adbExecCommand(deviceID, "install --abi " + abi + " " + apk, workDir); + adbExecCommand(deviceID, "install --abi " + abi + " \"" + apk + "\"", workDir); // Wait until re-install completes string reinstallResult; @@ -378,19 +636,41 @@ bool ReinstallPatchedAPK(const string &deviceID, const string &apk, const string bool CheckPatchingRequirements() { - // check for aapt, zipalign, apksigner, debug key - vector requirements; - vector missingTools; - requirements.push_back("aapt"); - requirements.push_back("zipalign"); - requirements.push_back("keytool"); - requirements.push_back("apksigner"); - requirements.push_back("java"); + // check for required tools for patching + std::vector> requirements; + std::vector missingTools; + requirements.push_back(std::make_pair(ToolDir::BuildTools, "aapt")); + requirements.push_back(std::make_pair(ToolDir::BuildTools, "zipalign")); + requirements.push_back(std::make_pair(ToolDir::BuildToolsLib, "apksigner.jar")); + requirements.push_back(std::make_pair(ToolDir::Java, "java")); for(uint32_t i = 0; i < requirements.size(); i++) { - if(FileIO::FindFileInPath(requirements[i]).empty()) - missingTools.push_back(requirements[i]); + std::string tool = getToolPath(requirements[i].first, requirements[i].second, true); + + // if we located the tool, we're fine. + if(toolExists(tool)) + continue; + + // didn't find it. + missingTools.push_back(requirements[i].second); + } + + // keytool is special - we look for a debug key first + { + std::string key = getToolPath(ToolDir::None, keystoreName, true); + + if(key.empty()) + { + // if we don't have the debug key, check that we can find keytool. First in our normal search + std::string tool = getToolPath(ToolDir::Java, "keytool", true); + + if(tool.empty()) + { + // if not, it's missing too + missingTools.push_back("keytool"); + } + } } if(missingTools.size() > 0) @@ -407,7 +687,7 @@ bool PullAPK(const string &deviceID, const string &pkgPath, const string &apk) { RDCLOG("Pulling APK to patch"); - adbExecCommand(deviceID, "pull " + pkgPath + " " + apk); + adbExecCommand(deviceID, "pull " + pkgPath + " \"" + apk + "\""); // Wait until the apk lands uint32_t elapsed = 0; @@ -438,7 +718,9 @@ bool CheckAPKPermissions(const string &apk) { RDCLOG("Checking that APK can be can write to sdcard"); - string badging = execCommand("aapt", "dump badging " + apk).strStdout; + std::string aapt = getToolPath(ToolDir::BuildTools, "aapt", false); + + string badging = execCommand(aapt, "dump badging \"" + apk + "\"").strStdout; if(badging.empty()) { @@ -452,7 +734,9 @@ bool CheckDebuggable(const string &apk) { RDCLOG("Checking that APK s debuggable"); - string badging = execCommand("aapt", "dump badging " + apk).strStdout; + std::string aapt = getToolPath(ToolDir::BuildTools, "aapt", false); + + string badging = execCommand(aapt, "dump badging \"" + apk + "\"").strStdout; if(badging.find("application-debuggable") == string::npos) { @@ -556,7 +840,7 @@ bool installRenderDocServer(const string &deviceID) { case Android_armeabi_v7a: case Android_arm64_v8a: - adbInstall = adbExecCommand(deviceID, "install -r -g " + serverApk).strStdout; + adbInstall = adbExecCommand(deviceID, "install -r -g \"" + serverApk + "\"").strStdout; break; case Android_armeabi: case Android_x86: @@ -781,7 +1065,7 @@ string FindAndroidLayer(const string &abi, const string &layerName) "%s missing! RenderDoc for Android will not work without it. " "Build your Android ABI in build-android in the root to have it " "automatically found and installed.", - layer.c_str()); + layerName.c_str()); } return layer; diff --git a/scripts/dist.sh b/scripts/dist.sh index 523bc720f..307372ba7 100644 --- a/scripts/dist.sh +++ b/scripts/dist.sh @@ -51,22 +51,31 @@ cp -R plugins-win32/ dist/Release32/plugins find dist/Release{32,64}/ -iname '*.ipdb' -exec rm '{}' \; find dist/Release{32,64}/ -iname '*.iobj' -exec rm '{}' \; -if [ -f bin-android32/RenderDocCmd.apk ]; then +ANDROID32="bin-android32" +if [ -f build-android32/bin/RenderDocCmd.apk ]; then + ANDROID32="build-android32/bin"; +fi + +ANDROID64="bin-android64" +if [ -f build-android64/bin/RenderDocCmd.apk ]; then + ANDROID64="build-android64/bin"; +fi + +if [ -f $ANDROID32/RenderDocCmd.apk ]; then # Building for android, copy the apk and vulkan layer into folders mkdir -p dist/Release64/android/apk dist/Release64/android/lib/armeabi-v7a - cp bin-android32/RenderDocCmd.apk dist/Release64/android/apk - cp bin-android32/libVkLayer_GLES_RenderDoc.so dist/Release64/android/lib/armeabi-v7a/libVkLayer_GLES_RenderDoc.so + cp $ANDROID32/RenderDocCmd.apk dist/Release64/android/apk + cp $ANDROID32/libVkLayer_GLES_RenderDoc.so dist/Release64/android/lib/armeabi-v7a/libVkLayer_GLES_RenderDoc.so fi -if [ -f bin-android64/RenderDocCmd.apk ]; then - # We don't distribute the 64-bit apk, we use armeabi-v7a for both 32-bit and 64-bit - +if [ -f $ANDROID64/RenderDocCmd.apk ]; then # Building for android, copy the vulkan layer into folder mkdir -p dist/Release64/android/lib/arm64-v8a - #cp bin-android64/RenderDocCmd.apk dist/Release64/android/apk/64 - cp bin-android64/libVkLayer_GLES_RenderDoc.so dist/Release64/android/lib/arm64-v8a/libVkLayer_GLES_RenderDoc.so + # We don't distribute the 64-bit apk, we use armeabi-v7a for both 32-bit and 64-bit + #cp $ANDROID64/RenderDocCmd.apk dist/Release64/android/apk/64 + cp $ANDROID64/libVkLayer_GLES_RenderDoc.so dist/Release64/android/lib/arm64-v8a/libVkLayer_GLES_RenderDoc.so fi # try to copy adb.exe in as well, with its dll dependencies @@ -74,6 +83,28 @@ if [ -f $ANDROID_SDK/platform-tools/adb.exe ] && [ -d dist/Release64/android ]; cp $ANDROID_SDK/platform-tools/{adb.exe,AdbWinApi.dll,AdbWinUsbApi.dll} dist/Release64/android/ fi +# Copy tools needed for patching apks +if [ -d "$ANDROID_SDK/build-tools" ] && [ -d dist/Release64/android ]; then + BUILD_DIR=$(ls "$ANDROID_SDK/build-tools" | grep -E '^[0-9.]*$' | sort -n | tail -n 1) + + if [ -f "$ANDROID_SDK/build-tools/$BUILD_DIR/aapt.exe" ]; then + cp "$ANDROID_SDK/build-tools/$BUILD_DIR/aapt.exe" dist/Release64/android/ + fi + + if [ -f "$ANDROID_SDK/build-tools/$BUILD_DIR/zipalign.exe" ]; then + cp "$ANDROID_SDK/build-tools/$BUILD_DIR/zipalign.exe" dist/Release64/android/ + fi + + if [ -f "$ANDROID_SDK/build-tools/$BUILD_DIR/lib/apksigner.jar" ]; then + cp "$ANDROID_SDK/build-tools/$BUILD_DIR/lib/apksigner.jar" dist/Release64/android/ + fi +fi + +# Generate a debug key for signing purposes +if [ -f "$JAVA_HOME/bin/keytool.exe" ] && [ -d dist/Release64/android ]; then + keytool -genkey -keystore dist/Release64/android/renderdoc.keystore -storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 10000 -dname "CN=, OU=, O=, L=, S=, C=" +fi + if [ -d dist/Release64/android ]; then cp -R dist/Release64/android dist/Release32/ fi diff --git a/scripts/installer/LICENSE.rtf b/scripts/installer/LICENSE.rtf index 2ae2e1826..27b690bfe 100644 --- a/scripts/installer/LICENSE.rtf +++ b/scripts/installer/LICENSE.rtf @@ -36,5 +36,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI {\field{\*\fldinst{HYPERLINK "{\pntext\f1\'B7\tab}https://github.com/facebook/zstd/"}}{\fldrslt{\ul\cf1 https://github.com/facebook/zstd/}}}\f0\fs22\line zstd distributed under the BSD License. Copyright 2016-present, Facebook, Inc.\par {\field{\*\fldinst{HYPERLINK "{\pntext\f1\'B7\tab}https://github.com/philsquared/Catch"}}{\fldrslt{\ul\cf1 https://github.com/philsquared/Catch}}}\f0\fs22\line Catch distributed under the Boost Software License. Copyright (c) 2012 Two Blue Cubes Ltd.\par {\field{\*\fldinst{HYPERLINK "{\pntext\f1\'B7\tab}https://pugixml.org"}}{\fldrslt{\ul\cf1 https://pugixml.org}}}\f0\fs22\line pugixml distributed under the MIT License. Copyright (c) 2006-2017 Arseny Kapoulkine.\par +{\field{\*\fldinst{HYPERLINK "{\pntext\f1\'B7\tab}https://source.android.com"}}{\fldrslt{\ul\cf1 https://source.android.com}}}\f0\fs22\line components of AOSP distributed under the Apache 2.0 License. Copyright (c) 2006-2016 The Android Open Source Project.\par }