Utils: Simplify ProcessArgs

Instead of trying to find a generic abstraction for the arguments
and splitting and combininig code paths several times, split into
the Windows and non-Windows case early once.

This also means that the ProcessArgs class as such is not really
used as value type anymore and could be converted to a namespace
in the future.

Change-Id: I79562b1571794d60cc97e5d7f877744f3bc7d57c
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
hjk
2025-03-07 13:03:10 +01:00
parent daea61bfa3
commit 51b044b8df
6 changed files with 42 additions and 84 deletions

View File

@@ -77,10 +77,10 @@ static void envExpandWin(QString &args, const Environment *env, const QString &p
}
}
static ProcessArgs prepareArgsWin(const QString &_args, ProcessArgs::SplitError *err,
const Environment *env, const QString &pwd)
static QString prepareArgsWin(const QString &_args, ProcessArgs::SplitError *err,
const Environment *env, const QString &pwd)
{
QString args(_args);
QString args = _args;
if (env) {
envExpandWin(args, env, pwd);
@@ -88,7 +88,7 @@ static ProcessArgs prepareArgsWin(const QString &_args, ProcessArgs::SplitError
if (args.indexOf(QLatin1Char('%')) >= 0) {
if (err)
*err = ProcessArgs::FoundMeta;
return ProcessArgs::createWindowsArgs(QString());
return {};
}
}
@@ -107,13 +107,13 @@ static ProcessArgs prepareArgsWin(const QString &_args, ProcessArgs::SplitError
} else if (isMetaCharWin(c)) {
if (err)
*err = ProcessArgs::FoundMeta;
return ProcessArgs::createWindowsArgs(QString());
return {};
}
}
if (err)
*err = ProcessArgs::SplitOk;
return ProcessArgs::createWindowsArgs(args);
return args;
}
inline static bool isWhiteSpaceWin(ushort c)
@@ -262,7 +262,7 @@ static QStringList splitArgsWin(const QString &_args, bool abortOnMeta,
ProcessArgs::SplitError perr;
if (!err)
err = &perr;
QString args = prepareArgsWin(_args, &perr, env, pwd).toWindowsArgs();
QString args = prepareArgsWin(_args, &perr, env, pwd);
if (*err != ProcessArgs::SplitOk)
return {};
return doSplitArgsWin(args, err);
@@ -574,15 +574,15 @@ static QString quoteArgWin(const QString &arg)
return ret;
}
ProcessArgs ProcessArgs::prepareArgs(const QString &args, SplitError *err, OsType osType,
const Environment *env, const FilePath &pwd, bool abortOnMeta)
QString ProcessArgs::prepareShellArgs(const QString &args, SplitError *err, OsType osType,
const Environment *env, const FilePath &pwd)
{
const QString wd = pwd.path();
ProcessArgs res;
QString res;
if (osType == OsTypeWindows)
res = prepareArgsWin(args, err, env, wd);
else
res = createUnixArgs(splitArgs(args, osType, abortOnMeta, err, env, wd));
res = joinArgs(splitArgsUnix(args, true, err, env, wd), OsTypeLinux);
return res;
}
@@ -642,27 +642,32 @@ CommandLine &CommandLine::operator<<(const QStringList &args)
return *this;
}
bool ProcessArgs::prepareCommand(const CommandLine &cmdLine, QString *outCmd, ProcessArgs *outArgs,
bool ProcessArgs::prepareCommand(const CommandLine &cmdLine, QString *outCmd, QString *outArgs,
const Environment *env, const FilePath &pwd)
{
const FilePath executable = cmdLine.executable();
if (executable.isEmpty())
return false;
const QString arguments = cmdLine.arguments();
const OsType osType = executable.osType();
ProcessArgs::SplitError err;
*outArgs = ProcessArgs::prepareArgs(arguments, &err, executable.osType(), env, pwd);
*outArgs = ProcessArgs::prepareShellArgs(arguments, &err, osType, env, pwd);
if (err == ProcessArgs::SplitOk) {
*outCmd = executable.path();
} else {
if (executable.osType() == OsTypeWindows) {
if (osType == OsTypeWindows) {
*outCmd = qtcEnvironmentVariable("COMSPEC");
*outArgs = ProcessArgs::createWindowsArgs(QLatin1String("/v:off /s /c \"")
+ quoteArgWin(executable.nativePath()) + ' ' + arguments + '"');
*outArgs = "/v:off /s /c \""
+ quoteArgWin(executable.nativePath()) + ' ' + arguments + '"';
} else {
if (err != ProcessArgs::FoundMeta)
return false;
*outCmd = qtcEnvironmentVariable("SHELL", "/bin/sh");
*outArgs = ProcessArgs::createUnixArgs({"-c", quoteArg(executable.path()) + ' ' + arguments});
ProcessArgs::addArg(outArgs, "-c", osType);
ProcessArgs::addArg(outArgs, quoteArg(executable.path()) + ' ' + arguments, osType);
}
}
return true;
@@ -1386,42 +1391,6 @@ void ProcessArgs::ArgIterator::appendArg(const QString &str)
m_pos += qstr.length() + 1;
}
ProcessArgs ProcessArgs::createWindowsArgs(const QString &args)
{
ProcessArgs result;
result.m_windowsArgs = args;
result.m_isWindows = true;
return result;
}
ProcessArgs ProcessArgs::createUnixArgs(const QStringList &args)
{
ProcessArgs result;
result.m_unixArgs = args;
result.m_isWindows = false;
return result;
}
QString ProcessArgs::toWindowsArgs() const
{
QTC_CHECK(m_isWindows);
return m_windowsArgs;
}
QStringList ProcessArgs::toUnixArgs() const
{
QTC_CHECK(!m_isWindows);
return m_unixArgs;
}
QString ProcessArgs::toString() const
{
if (m_isWindows)
return m_windowsArgs;
else
return ProcessArgs::joinArgs(m_unixArgs, OsTypeLinux);
}
/*!
\class Utils::CommandLine
\inmodule QtCreator

View File

@@ -23,13 +23,6 @@ class MacroExpander;
class QTCREATOR_UTILS_EXPORT ProcessArgs
{
public:
static ProcessArgs createWindowsArgs(const QString &args);
static ProcessArgs createUnixArgs(const QStringList &args);
QString toWindowsArgs() const;
QStringList toUnixArgs() const;
QString toString() const;
enum SplitError {
SplitOk = 0, //! All went just fine
BadQuoting, //! Command contains quoting errors
@@ -44,12 +37,11 @@ public:
static void addArg(QString *args, const QString &arg, OsType osType = HostOsInfo::hostOs());
//! Join an argument list into a shell command
static QString joinArgs(const QStringList &args, OsType osType = HostOsInfo::hostOs());
//! Prepare argument of a shell command for feeding into QProcess
static ProcessArgs prepareArgs(const QString &args, SplitError *err, OsType osType,
const Environment *env = nullptr, const FilePath &pwd = {},
bool abortOnMeta = true);
//! Prepare argument of a shell command for feeding into QProcess.
static QString prepareShellArgs(const QString &args, SplitError *err, OsType osType,
const Environment *env = nullptr, const FilePath &pwd = {});
//! Prepare a shell command for feeding into QProcess
static bool prepareCommand(const CommandLine &cmdLine, QString *outCmd, ProcessArgs *outArgs,
static bool prepareCommand(const CommandLine &cmdLine, QString *outCmd, QString *outArgs,
const Environment *env = nullptr, const FilePath &pwd = {});
//! Quote and append each argument to a shell command
static void addArgs(QString *args, const QStringList &inArgs);
@@ -111,11 +103,6 @@ public:
QString m_str;
ArgIterator m_ait;
};
private:
QString m_windowsArgs;
QStringList m_unixArgs;
bool m_isWindows;
};
class QTCREATOR_UTILS_EXPORT RunResult

View File

@@ -244,13 +244,15 @@ void DefaultImpl::start()
bool DefaultImpl::dissolveCommand(QString *program, QStringList *arguments)
{
const CommandLine &commandLine = m_setup.m_commandLine;
const OsType osType = commandLine.executable().osType();
QString commandString;
ProcessArgs processArgs;
QString processArgs;
const bool success = ProcessArgs::prepareCommand(commandLine, &commandString, &processArgs,
&m_setup.m_environment,
m_setup.m_workingDirectory);
if (commandLine.executable().osType() == OsTypeWindows) {
if (osType == OsTypeWindows) {
QString args;
if (m_setup.m_useCtrlCStub) {
if (m_setup.m_lowPriority)
@@ -261,7 +263,7 @@ bool DefaultImpl::dissolveCommand(QString *program, QStringList *arguments)
} else if (m_setup.m_lowPriority) {
m_setup.m_belowNormalPriority = true;
}
ProcessArgs::addArgs(&args, processArgs.toWindowsArgs());
ProcessArgs::addArgs(&args, processArgs);
m_setup.m_nativeArguments = args;
// Note: Arguments set with setNativeArgs will be appended to the ones
// passed with start() below.
@@ -275,7 +277,7 @@ bool DefaultImpl::dissolveCommand(QString *program, QStringList *arguments)
emit done(result);
return false;
}
*arguments = processArgs.toUnixArgs();
*arguments = ProcessArgs::splitArgs(processArgs, osType);
}
*program = commandString;
return true;

View File

@@ -285,9 +285,9 @@ Result DebuggerRunParameters::fixupParameters(ProjectExplorer::RunControl *runCo
if (HostOsInfo::isWindowsHost()) {
// Otherwise command lines with '> tmp.log' hang.
ProcessArgs::SplitError perr;
ProcessArgs::prepareArgs(m_inferior.command.arguments(), &perr,
ProcessArgs::prepareShellArgs(m_inferior.command.arguments(), &perr,
HostOsInfo::hostOs(), nullptr,
m_inferior.workingDirectory).toWindowsArgs();
m_inferior.workingDirectory);
if (perr != ProcessArgs::SplitOk) {
// perr == BadQuoting is never returned on Windows
// FIXME? QTCREATORBUG-2809

View File

@@ -145,12 +145,15 @@ QString ProcessParameters::prettyArguments() const
{
const QString margs = effectiveArguments();
const FilePath workDir = effectiveWorkingDirectory();
const OsType osType = workDir.osType();
ProcessArgs::SplitError err;
const ProcessArgs args = ProcessArgs::prepareArgs(margs, &err, HostOsInfo::hostOs(),
&m_runData.environment, workDir);
const QString args = ProcessArgs::prepareShellArgs(margs, &err, osType,
&m_runData.environment, workDir);
if (err != ProcessArgs::SplitOk)
return margs; // Sorry, too complex - just fall back.
return args.toString();
if (osType == OsTypeWindows)
return args.front();
return args;
}
static QString invalidCommandMessage(const QString &displayName)

View File

@@ -386,8 +386,7 @@ void tst_Process::prepareArgs()
QFETCH(OsType, os);
ProcessArgs::SplitError outerr;
ProcessArgs args = ProcessArgs::prepareArgs(in, &outerr, os);
QString outstr = args.toString();
QString outstr = ProcessArgs::prepareShellArgs(in, &outerr, os);
QCOMPARE(outerr, err);
if (err == ProcessArgs::SplitOk)
@@ -476,8 +475,7 @@ void tst_Process::prepareArgsEnv()
QFETCH(OsType, os);
ProcessArgs::SplitError outerr;
ProcessArgs args = ProcessArgs::prepareArgs(in, &outerr, os, os == OsTypeLinux ? &envLinux : &envWindows);
QString outstr = args.toString();
QString outstr = ProcessArgs::prepareShellArgs(in, &outerr, os, os == OsTypeLinux ? &envLinux : &envWindows);
QCOMPARE(outerr, err);
if (err == ProcessArgs::SplitOk)
@@ -485,7 +483,6 @@ void tst_Process::prepareArgsEnv()
}
void tst_Process::iterations_data()
{
QTest::addColumn<QString>("in");