Process launcher updates (#6183)

* Enable process and ap connection tests on linux
* Updated 'OpenProjectManager' to use new the ProcessLauncher argument type
* Add logic to double-escape escaped double quotes in arguments on windows platforms
* Updated argument for LaunchProjectManager to reflect new ProcessLauncher argument type
* Fixed unit test arguments for 'arg=value' condition
* Fix compile errors for BuilderManager and RHI.Edit\Utils.cpp
* PAL'ify the GetCommandLineParametersAsString() to handle windows specific behavior

Signed-off-by: Steve Pham <82231385+spham-amzn@users.noreply.github.com>

Co-authored-by: byrcolin <byrcolin@amazon.com>
This commit is contained in:
Steve Pham
2021-12-08 09:30:19 -08:00
committed by GitHub
parent 29da71e64e
commit 558532f094
17 changed files with 285 additions and 180 deletions
@@ -6,10 +6,10 @@
*
*/
#include <AzCore/StringFunc/StringFunc.h>
#include <AzFramework/Process/ProcessWatcher.h>
#include <AzFramework/Process/ProcessCommunicator.h>
namespace AzFramework
{
@@ -83,4 +83,23 @@ namespace AzFramework
{
}
AZStd::string ProcessLauncher::ProcessLaunchInfo::GetCommandLineParametersAsString() const
{
struct CommandLineParametersVisitor
{
AZStd::string operator()(const AZStd::string& commandLine) const
{
return commandLine;
}
AZStd::string operator()(const AZStd::vector<AZStd::string>& commandLineArray) const
{
AZStd::string commandLineResult;
AZ::StringFunc::Join(commandLineResult, commandLineArray.begin(), commandLineArray.end(), " ");
return commandLineResult;
}
};
return AZStd::visit(CommandLineParametersVisitor{}, m_commandlineParameters);
}
} //namespace AzFramework
@@ -10,12 +10,11 @@
#include <AzFramework/Process/ProcessWatcher.h>
#include <AzFramework/Process/ProcessCommunicator.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzCore/base.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/std/parallel/thread.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <iostream>
#include <errno.h>
@@ -220,36 +219,52 @@ namespace AzFramework
// this is so that the callers (which could be numerous) do not have to worry about this and sprinkle ifdefs
// all over their code.
// We'll convert this to UNIX style command line parameters by counting and eliminating quotes:
AZStd::vector<AZStd::string> commandTokens;
AZStd::string outputString;
bool inQuotes = false;
for (const char currentChar : processLaunchInfo.m_commandlineParameters)
{
if (currentChar == '"')
{
inQuotes = !inQuotes;
}
else if ((currentChar == ' ') && (!inQuotes))
{
// its a space outside of quotes, so it ends the current parameter
commandTokens.push_back(outputString);
outputString.clear();
}
else
{
// Its a normal character, or its a space inside quotes
outputString.push_back(currentChar);
}
}
if (!outputString.empty())
// Struct uses overloaded operator() to quote command line arguments based
// on whether a string or a vector<string> was supplied
struct EscapeCommandArguments
{
commandTokens.push_back(outputString);
outputString.clear();
}
void operator()(const AZStd::string& commandParameterString)
{
AZStd::string outputString;
bool inQuotes = false;
for (size_t pos = 0; pos < commandParameterString.size(); ++pos)
{
char currentChar = commandParameterString[pos];
if (currentChar == '"')
{
inQuotes = !inQuotes;
}
else if ((currentChar == ' ') && (!inQuotes))
{
// its a space outside of quotes, so it ends the current parameter
commandArray.push_back(outputString);
outputString.clear();
}
else
{
// Its a normal character, or its a space inside quotes
outputString.push_back(currentChar);
}
}
if (!outputString.empty())
{
commandArray.push_back(outputString);
outputString.clear();
}
}
void operator()(const AZStd::vector<AZStd::string>& commandParameterArray)
{
commandArray = commandParameterArray;
}
AZStd::vector<AZStd::string>& commandArray;
};
AZStd::vector<AZStd::string> commandTokens;
AZStd::visit(EscapeCommandArguments{ commandTokens }, processLaunchInfo.m_commandlineParameters);
if (!processLaunchInfo.m_processExecutableString.empty())
{
commandTokens.insert(commandTokens.begin(), processLaunchInfo.m_processExecutableString);
@@ -452,4 +467,23 @@ namespace AzFramework
kill(m_pWatcherData->m_childProcessId, SIGKILL);
}
AZStd::string ProcessLauncher::ProcessLaunchInfo::GetCommandLineParametersAsString() const
{
struct CommandLineParametersVisitor
{
AZStd::string operator()(const AZStd::string& commandLine) const
{
return commandLine;
}
AZStd::string operator()(const AZStd::vector<AZStd::string>& commandLineArray) const
{
AZStd::string commandLineResult;
AZ::StringFunc::Join(commandLineResult, commandLineArray.begin(), commandLineArray.end(), " ");
return commandLineResult;
}
};
return AZStd::visit(CommandLineParametersVisitor{}, m_commandlineParameters);
}
} //namespace AzFramework
@@ -11,13 +11,12 @@
#include <AzFramework/Process/ProcessWatcher.h>
#include <AzFramework/Process/ProcessCommunicator.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzCore/base.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/std/containers/fixed_vector.h>
#include <AzCore/std/parallel/thread.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <iostream>
#include <errno.h>
@@ -210,46 +209,51 @@ namespace AzFramework
// this is so that the callers (which could be numerous) do not have to worry about this and sprinkle ifdefs
// all over their code.
// We'll convert this to UNIX style command line parameters by counting and eliminating quotes:
// Struct uses overloaded operator() to quote command line arguments based
// on whether a string or a vector<string> was supplied
struct EscapeCommandArguments
{
void operator()(const AZStd::string& commandParameterString)
{
AZStd::string outputString;
bool inQuotes = false;
for (size_t pos = 0; pos < commandParameterString.size(); ++pos)
{
char currentChar = commandParameterString[pos];
if (currentChar == '"')
{
inQuotes = !inQuotes;
}
else if ((currentChar == ' ') && (!inQuotes))
{
// its a space outside of quotes, so it ends the current parameter
commandArray.push_back(outputString);
outputString.clear();
}
else
{
// Its a normal character, or its a space inside quotes
outputString.push_back(currentChar);
}
}
if (!outputString.empty())
{
commandArray.push_back(outputString);
outputString.clear();
}
}
void operator()(const AZStd::vector<AZStd::string>& commandParameterArray)
{
commandArray = commandParameterArray;
}
AZStd::vector<AZStd::string>& commandArray;
};
AZStd::vector<AZStd::string> commandTokens;
AZStd::string outputString;
bool inQuotes = false;
for (size_t pos = 0; pos < processLaunchInfo.m_commandlineParameters.size(); ++pos)
{
char currentChar = processLaunchInfo.m_commandlineParameters[pos];
if (currentChar == '"')
{
// Allow quote literals to go through as quotes which do NOT alter our "in quotes" bool below
// This is to conform with our PC parameter strings which will sometimes include path parameters which
// Can have spaces and commas and need to be output as paramname="\"Some pa,ram\"" in order to capture both correctly
if (outputString.length() && outputString.back() == '\\')
{
outputString.back() = currentChar;
}
else
{
inQuotes = !inQuotes;
}
}
else if ((currentChar == ' ') && (!inQuotes))
{
// its a space outside of quotes, so it ends the current parameter
commandTokens.push_back(outputString);
outputString.clear();
}
else
{
// Its a normal character, or its a space inside quotes
outputString.push_back(currentChar);
}
}
if (!outputString.empty())
{
commandTokens.push_back(outputString);
outputString.clear();
}
AZStd::visit(EscapeCommandArguments{ commandTokens }, processLaunchInfo.m_commandlineParameters);
if (!processLaunchInfo.m_processExecutableString.empty())
{
@@ -417,5 +421,24 @@ namespace AzFramework
kill(m_pWatcherData->m_childProcessId, SIGKILL);
waitpid(m_pWatcherData->m_childProcessId, NULL, 0);
}
AZStd::string ProcessLauncher::ProcessLaunchInfo::GetCommandLineParametersAsString() const
{
struct CommandLineParametersVisitor
{
AZStd::string operator()(const AZStd::string& commandLine) const
{
return commandLine;
}
AZStd::string operator()(const AZStd::vector<AZStd::string>& commandLineArray) const
{
AZStd::string commandLineResult;
AZ::StringFunc::Join(commandLineResult, commandLineArray.begin(), commandLineArray.end(), " ");
return commandLineResult;
}
};
return AZStd::visit(CommandLineParametersVisitor{}, m_commandlineParameters);
}
} //namespace AzFramework
@@ -10,6 +10,7 @@
#include <AzCore/std/string/conversions.h>
#include <AzCore/std/parallel/thread.h>
#include <AzCore/PlatformIncl.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzFramework/Process/ProcessWatcher.h>
#include <AzFramework/Process/ProcessCommunicator.h>
@@ -99,7 +100,7 @@ namespace AzFramework
AZStd::wstring editableCommandLine;
AZStd::wstring processExecutableString;
AZStd::wstring workingDirectory;
AZStd::to_wstring(editableCommandLine, processLaunchInfo.m_commandlineParameters);
AZStd::to_wstring(editableCommandLine, processLaunchInfo.GetCommandLineParametersAsString());
AZStd::to_wstring(processExecutableString, processLaunchInfo.m_processExecutableString);
AZStd::to_wstring(workingDirectory, processLaunchInfo.m_workingDirectory);
@@ -355,4 +356,41 @@ namespace AzFramework
::TerminateProcess(m_pWatcherData->processInformation.hProcess, exitCode);
}
}
AZStd::string ProcessLauncher::ProcessLaunchInfo::GetCommandLineParametersAsString() const
{
struct CommandLineParametersVisitor
{
AZStd::string operator()(const AZStd::string& commandLine) const
{
return commandLine;
}
AZStd::string operator()(const AZStd::vector<AZStd::string>& commandLineArray) const
{
AZStd::string commandLineResult;
// When re-constructing a command line from an argument list (on windows), if an argument
// is double-quoted, then the double-quotes must be escaped properly otherwise
// it will be absorbed by the native argument parser and possibly evaluated as
// multiple values for arguments
AZStd::string_view escapedDoubleQuote = R"("\")";
AZStd::vector<AZStd::string> preprocessedCommandArray;
for (const auto& commandArg : commandLineArray)
{
AZStd::string replacedArg = commandArg;
AZ::StringFunc::Replace(replacedArg, R"(")", R"("\")", false, true, true);
preprocessedCommandArray.emplace_back(replacedArg);
}
AZ::StringFunc::Join(commandLineResult, preprocessedCommandArray.begin(), preprocessedCommandArray.end(), " ");
return commandLineResult;
}
};
return AZStd::visit(CommandLineParametersVisitor{}, m_commandlineParameters);
}
} // namespace AzFramework
@@ -6,10 +6,10 @@
*
*/
#include <AzCore/StringFunc/StringFunc.h>
#include <AzFramework/Process/ProcessWatcher.h>
#include <AzFramework/Process/ProcessCommunicator.h>
namespace AzFramework
{
@@ -83,4 +83,23 @@ namespace AzFramework
{
}
AZStd::string ProcessLauncher::ProcessLaunchInfo::GetCommandLineParametersAsString() const
{
struct CommandLineParametersVisitor
{
AZStd::string operator()(const AZStd::string& commandLine) const
{
return commandLine;
}
AZStd::string operator()(const AZStd::vector<AZStd::string>& commandLineArray) const
{
AZStd::string commandLineResult;
Az::StringFunc::Join(commandLineResult, commandLineArray.begin(), commandLineArray.end(), " ");
return commandLineResult;
}
};
return AZStd::visit(CommandLineParametersVisitor{}, m_commandlineParameters);
}
} //namespace AzFramework