mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-14 14:01:06 +00:00
359 lines
11 KiB
C++
359 lines
11 KiB
C++
/******************************************************************************
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2018-2019 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
|
|
{
|
|
static bool adbKillServer = false;
|
|
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<PathEntry> 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<std::string, std::string> 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 we try 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
|
|
// 3. Finally 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.
|
|
//
|
|
// The main reason we check our bundled folder last is because adb requires a *precise* match in
|
|
// its client-server setup, so if we run our bundled adb that might be newer than the user's, they
|
|
// will then get fighting back and forth when trying to run their own.
|
|
|
|
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;
|
|
|
|
// 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 : "";
|
|
}
|
|
|
|
if(sdk.empty() || !FileIO::exists(sdk.c_str()))
|
|
{
|
|
env = Process::GetEnvVariable("ANDROID_SDK_HOME");
|
|
sdk = env ? env : "";
|
|
}
|
|
|
|
#if ENABLED(RDOC_APPLE)
|
|
// on macOS it's common not to have the environment variable globally available, so try the home
|
|
// Library folder first, then the global folder
|
|
if(sdk.empty() || !FileIO::exists(sdk.c_str()))
|
|
{
|
|
std::string librarySDK = FileIO::GetHomeFolderFilename() + "/Library/Android/sdk";
|
|
sdk = FileIO::exists(librarySDK.c_str()) ? librarySDK : "";
|
|
}
|
|
|
|
if(sdk.empty() || !FileIO::exists(sdk.c_str()))
|
|
{
|
|
std::string librarySDK = "/Library/Android/sdk";
|
|
sdk = FileIO::exists(librarySDK.c_str()) ? librarySDK : "";
|
|
}
|
|
#endif
|
|
|
|
// maybe in future we can try to search in common install locations.
|
|
|
|
toolpath = getToolInSDK(subdir, jdk, sdk, toolname);
|
|
|
|
if(toolExists(toolpath))
|
|
return toolpath;
|
|
|
|
// finally try to locate it in our own distributed android subfolder
|
|
{
|
|
std::string libpath;
|
|
FileIO::GetLibraryFilename(libpath);
|
|
std::string libdir = get_dirname(FileIO::GetFullPathname(libpath));
|
|
|
|
toolpath = libdir + "/plugins/android/" + toolname;
|
|
if(toolExists(toolpath))
|
|
{
|
|
if(toolname == "adb")
|
|
{
|
|
// if we're using our own adb, we should kill the server upon shutdown
|
|
adbKillServer = true;
|
|
}
|
|
|
|
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, bool silent)
|
|
{
|
|
if(!silent)
|
|
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, bool silent)
|
|
{
|
|
if(!silent)
|
|
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, bool silent)
|
|
{
|
|
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, silent);
|
|
}
|
|
void initAdb()
|
|
{
|
|
// we don't use adbExecCommand because we need to be sure we don't wait for it to exit
|
|
std::string adb = getToolPath(ToolDir::PlatformTools, "adb", false);
|
|
std::string workdir = ".";
|
|
if(adb.find('/') != std::string::npos || adb.find('\\') != std::string::npos)
|
|
workdir = get_dirname(adb);
|
|
|
|
RDCLOG("Initialising adb using '%s'", adb.c_str());
|
|
|
|
if(adb.empty() || (!FileIO::exists(adb.c_str()) && !FileIO::exists((adb + ".exe").c_str())))
|
|
{
|
|
if(FileIO::FindFileInPath(adb.c_str()) == "")
|
|
RDCWARN(
|
|
"Couldn't locate adb. Ensure adb is in PATH, ANDROID_SDK or ANDROID_HOME is set, or you "
|
|
"configure your SDK location");
|
|
}
|
|
|
|
Process::ProcessResult res = {};
|
|
Process::LaunchProcess(adb.c_str(), workdir.c_str(), "start-server", true, &res);
|
|
|
|
if(res.strStdout.find("daemon") != std::string::npos ||
|
|
res.strStderror.find("daemon") != std::string::npos)
|
|
{
|
|
RDCLOG("Started adb server");
|
|
}
|
|
}
|
|
void shutdownAdb()
|
|
{
|
|
if(adbKillServer)
|
|
adbExecCommand("", "kill-server", ".", false);
|
|
}
|
|
};
|