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

View File

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

View File

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

View File

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

View File

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

View File

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