mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-04 17:10:47 +00:00
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.
This commit is contained in:
@@ -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 <https://source.android.com/>`_ - 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
|
||||
------
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -974,17 +974,24 @@ Only happens if the capture is not in the recent files list.</string>
|
||||
<string>Android</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_8">
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Java JDK path</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_24">
|
||||
<property name="toolTip">
|
||||
<string>The location of adb.exe, used to control Android devices.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Android ADB executable path</string>
|
||||
<string>Android SDK root path</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<item row="6" column="0">
|
||||
<spacer name="verticalSpacer_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
@@ -997,7 +1004,7 @@ Only happens if the capture is not in the recent files list.</string>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_26">
|
||||
<property name="toolTip">
|
||||
<string>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.</
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<item row="5" column="1">
|
||||
<widget class="QCheckBox" name="Android_AutoPushLayerToApp">
|
||||
<property name="toolTip">
|
||||
<string>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.</
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_25">
|
||||
<property name="toolTip">
|
||||
<string>Maximum time to try connecting to the target app.</string>
|
||||
@@ -1026,7 +1033,7 @@ This can enable debugging of Vulkan apps that don't already contain the layer.</
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<item row="4" column="1">
|
||||
<widget class="QDoubleSpinBox" name="Android_MaxConnectTimeout">
|
||||
<property name="toolTip">
|
||||
<string>Maximum time to try connecting to the target app.</string>
|
||||
@@ -1040,14 +1047,14 @@ This can enable debugging of Vulkan apps that don't already contain the layer.</
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLineEdit" name="Android_AdbExecutablePath">
|
||||
<widget class="QLineEdit" name="Android_SDKPath">
|
||||
<property name="toolTip">
|
||||
<string>The location of adb.exe, used to control Android devices.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="browseAdbPath">
|
||||
<widget class="QPushButton" name="browseAndroidSDKPath">
|
||||
<property name="toolTip">
|
||||
<string>The location of adb.exe, used to control Android devices.</string>
|
||||
</property>
|
||||
@@ -1056,6 +1063,16 @@ This can enable debugging of Vulkan apps that don't already contain the layer.</
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLineEdit" name="Android_JDKPath"/>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QPushButton" name="browseJDKPath">
|
||||
<property name="text">
|
||||
<string>Browse</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
+335
-51
@@ -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<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 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<string> requirements;
|
||||
vector<string> 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<std::pair<ToolDir, std::string>> requirements;
|
||||
std::vector<std::string> 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;
|
||||
|
||||
+39
-8
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
| ||||