From 1be3e8bfac4692eac1fb804f013bcf0d7d459e6f Mon Sep 17 00:00:00 2001 From: baldurk Date: Fri, 26 Jan 2018 15:56:31 +0000 Subject: [PATCH] Split out handling of android tools: locating&precedence, executing, etc --- renderdoc/CMakeLists.txt | 2 + renderdoc/android/android.cpp | 271 +------------------------- renderdoc/android/android.h | 5 +- renderdoc/android/android_tools.cpp | 290 ++++++++++++++++++++++++++++ renderdoc/android/android_utils.h | 49 +++++ renderdoc/renderdoc.vcxproj | 2 + renderdoc/renderdoc.vcxproj.filters | 6 + 7 files changed, 356 insertions(+), 269 deletions(-) create mode 100644 renderdoc/android/android_tools.cpp create mode 100644 renderdoc/android/android_utils.h diff --git a/renderdoc/CMakeLists.txt b/renderdoc/CMakeLists.txt index b6e85dc99..4c0365c8f 100644 --- a/renderdoc/CMakeLists.txt +++ b/renderdoc/CMakeLists.txt @@ -90,7 +90,9 @@ set(sources core/replay_proxy.cpp core/replay_proxy.h android/android.cpp + android/android_tools.cpp android/android.h + android/android_utils.h core/plugins.cpp core/plugins.h core/resource_manager.cpp diff --git a/renderdoc/android/android.cpp b/renderdoc/android/android.cpp index e0fb0afaa..4bfee4323 100644 --- a/renderdoc/android/android.cpp +++ b/renderdoc/android/android.cpp @@ -27,6 +27,7 @@ #include "api/replay/version.h" #include "core/core.h" #include "strings/string_utils.h" +#include "android_utils.h" // we use GIT_COMMIT_HASH here instead of GitVersionHash since the value is actually only used on // android - where GIT_COMMIT_HASH is available globally. We need to have it available in a static @@ -38,247 +39,10 @@ extern "C" RENDERDOC_API const char RENDERDOC_Version_Tag_String[] = "RenderDoc_build_version: " FULL_VERSION_STRING " from git commit " GIT_COMMIT_HASH; +static const char keystoreName[] = "renderdoc.keystore"; + 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); @@ -305,35 +69,6 @@ void extractDeviceIDAndIndex(const string &hostname, int &index, string &deviceI deviceID = c; } -Process::ProcessResult execScript(const string &script, const string &args, - const string &workDir = ".") -{ - RDCLOG("SCRIPT: %s", script.c_str()); - - Process::ProcessResult result; - Process::LaunchScript(script.c_str(), workDir.c_str(), args.c_str(), true, &result); - return result; -} -Process::ProcessResult execCommand(const string &exe, const string &args, - const string &workDir = ".") -{ - RDCLOG("COMMAND: %s '%s'", exe.c_str(), args.c_str()); - - Process::ProcessResult result; - Process::LaunchProcess(exe.c_str(), workDir.c_str(), args.c_str(), true, &result); - return result; -} -Process::ProcessResult adbExecCommand(const string &device, const string &args, const string &workDir) -{ - 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(adb, deviceArgs, workDir); -} string adbGetDeviceList() { return adbExecCommand("", "devices").strStdout; diff --git a/renderdoc/android/android.h b/renderdoc/android/android.h index c9e63c5cb..099d7f63a 100644 --- a/renderdoc/android/android.h +++ b/renderdoc/android/android.h @@ -22,14 +22,17 @@ * THE SOFTWARE. ******************************************************************************/ +#pragma once + #include #include "os/os_specific.h" +// public interface, for other non-android parts of the code namespace Android { bool IsHostADB(const char *hostname); uint32_t StartAndroidPackageForCapture(const char *host, const char *package); +void extractDeviceIDAndIndex(const std::string &hostname, int &index, std::string &deviceID); Process::ProcessResult adbExecCommand(const std::string &deviceID, const std::string &args, const string &workDir = "."); -void extractDeviceIDAndIndex(const std::string &hostname, int &index, std::string &deviceID); }; diff --git a/renderdoc/android/android_tools.cpp b/renderdoc/android/android_tools.cpp new file mode 100644 index 000000000..40cbbea4d --- /dev/null +++ b/renderdoc/android/android_tools.cpp @@ -0,0 +1,290 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2018 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include "core/core.h" +#include "strings/string_utils.h" +#include "android_utils.h" + +namespace Android +{ +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; +} +Process::ProcessResult execScript(const std::string &script, const std::string &args, + const std::string &workDir) +{ + RDCLOG("SCRIPT: %s", script.c_str()); + + Process::ProcessResult result; + Process::LaunchScript(script.c_str(), workDir.c_str(), args.c_str(), true, &result); + return result; +} +Process::ProcessResult execCommand(const std::string &exe, const std::string &args, + const std::string &workDir) +{ + RDCLOG("COMMAND: %s '%s'", exe.c_str(), args.c_str()); + + Process::ProcessResult result; + Process::LaunchProcess(exe.c_str(), workDir.c_str(), args.c_str(), true, &result); + return result; +} +Process::ProcessResult adbExecCommand(const std::string &device, const std::string &args, + const std::string &workDir) +{ + std::string adb = getToolPath(ToolDir::PlatformTools, "adb", false); + Process::ProcessResult result; + std::string deviceArgs; + if(device.empty()) + deviceArgs = args; + else + deviceArgs = StringFormat::Fmt("-s %s %s", device.c_str(), args.c_str()); + return execCommand(adb, deviceArgs, workDir); +} +}; diff --git a/renderdoc/android/android_utils.h b/renderdoc/android/android_utils.h new file mode 100644 index 000000000..6fc565470 --- /dev/null +++ b/renderdoc/android/android_utils.h @@ -0,0 +1,49 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2017-2018 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#pragma once + +#include "android.h" + +// internal functions, shouldn't be used outside the android implementation - anything public goes +// in android.h + +namespace Android +{ +Process::ProcessResult execScript(const string &script, const string &args, + const string &workDir = "."); +Process::ProcessResult execCommand(const string &exe, const string &args, + const string &workDir = "."); + +enum class ToolDir +{ + None, + Java, + BuildTools, + BuildToolsLib, + PlatformTools, +}; +std::string getToolPath(ToolDir subdir, const std::string &toolname, bool checkExist); +bool toolExists(const std::string &path); +}; diff --git a/renderdoc/renderdoc.vcxproj b/renderdoc/renderdoc.vcxproj index 5712e9cb9..089b5c5cd 100644 --- a/renderdoc/renderdoc.vcxproj +++ b/renderdoc/renderdoc.vcxproj @@ -133,6 +133,7 @@ + @@ -306,6 +307,7 @@ + diff --git a/renderdoc/renderdoc.vcxproj.filters b/renderdoc/renderdoc.vcxproj.filters index 3fe391a24..fb8407d42 100644 --- a/renderdoc/renderdoc.vcxproj.filters +++ b/renderdoc/renderdoc.vcxproj.filters @@ -378,6 +378,9 @@ Android + + Android + @@ -653,6 +656,9 @@ Android + + Android +