diff options
Diffstat (limited to 'src/lib/corelib/jsextensions/process.cpp')
-rw-r--r-- | src/lib/corelib/jsextensions/process.cpp | 306 |
1 files changed, 135 insertions, 171 deletions
diff --git a/src/lib/corelib/jsextensions/process.cpp b/src/lib/corelib/jsextensions/process.cpp index f0c3c4705..f0febb364 100644 --- a/src/lib/corelib/jsextensions/process.cpp +++ b/src/lib/corelib/jsextensions/process.cpp @@ -37,6 +37,8 @@ ** ****************************************************************************/ +#include "jsextension.h" + #include <language/scriptengine.h> #include <logging/translator.h> #include <tools/executablefinder.h> @@ -44,7 +46,6 @@ #include <tools/shellutils.h> #include <tools/stringconstants.h> -#include <QtCore/qobject.h> #include <QtCore/qprocess.h> #include <QtCore/qtextstream.h> #include <QtCore/qvariant.h> @@ -53,133 +54,161 @@ #else #include <QtCore/qtextcodec.h> #endif -#include <QtScript/qscriptable.h> -#include <QtScript/qscriptengine.h> -#include <QtScript/qscriptvalue.h> namespace qbs { namespace Internal { -class Process : public QObject, public QScriptable, public ResourceAcquiringScriptObject +class Process : public JsExtension<Process> { - Q_OBJECT + friend class JsExtension<Process>; public: - static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine); - Process(QScriptContext *context); - - Q_INVOKABLE QString getEnv(const QString &name); - Q_INVOKABLE void setEnv(const QString &name, const QString &value); - Q_INVOKABLE void setCodec(const QString &codec); + static const char *name() { return "Process"; } + static JSValue ctor(JSContext *ctx, JSValueConst, JSValueConst, int, JSValueConst *, int); + static void setupStaticMethods(JSContext *ctx, JSValue classObj); + static void setupMethods(JSContext *ctx, JSValue obj); + Process(JSContext *context); + + DEFINE_JS_FORWARDER(jsGetEnv, &Process::getEnv, "Process.getEnv") + DEFINE_JS_FORWARDER(jsSetEnv, &Process::setEnv, "Process.setEnv") + DEFINE_JS_FORWARDER(jsSetCodec, &Process::setCodec, "Process.setCodec") + DEFINE_JS_FORWARDER(jsWorkingDir, &Process::workingDirectory, "Process.workingDirectory") + DEFINE_JS_FORWARDER(jsSetWorkingDir, &Process::setWorkingDirectory, + "Process.setWorkingDirectory") + DEFINE_JS_FORWARDER(jsStart, &Process::start, "Process.start") + DEFINE_JS_FORWARDER(jsClose, &Process::close, "Process.close") + DEFINE_JS_FORWARDER(jsTerminate, &Process::terminate, "Process.terminate") + DEFINE_JS_FORWARDER(jsKill, &Process::kill, "Process.kill") + DEFINE_JS_FORWARDER(jsReadLine, &Process::readLine, "Process.readLine") + DEFINE_JS_FORWARDER(jsAtEnd, &Process::atEnd, "Process.atEnd") + DEFINE_JS_FORWARDER(jsReadStdOut, &Process::readStdOut, "Process.readStdOut") + DEFINE_JS_FORWARDER(jsReadStdErr, &Process::readStdErr, "Process.readStdErr") + DEFINE_JS_FORWARDER(jsCloseWriteChannel, &Process::closeWriteChannel, + "Process.closeWriteChannel") + DEFINE_JS_FORWARDER(jsWrite, &Process::write, "Process.write") + DEFINE_JS_FORWARDER(jsWriteLine, &Process::writeLine, "Process.writeLine") + DEFINE_JS_FORWARDER(jsExitCode, &Process::exitCode, "Process.exitCode") + + static JSValue jsExec(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) + { + try { + const auto args = getArguments<QString, QStringList>(ctx, "Process.exec", argc, argv); + bool throwOnError = false; + if (argc > 2) + throwOnError = fromArg<bool>(ctx, "Process.exec", 3, argv[2]); + return JS_NewInt32(ctx, fromJsObject(ctx, this_val) + ->exec(std::get<0>(args), std::get<1>(args), throwOnError)); + } catch (const QString &error) { return throwError(ctx, error); } + } + static JSValue jsWaitForFinished(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) + { + try { + int msecs = 30000; + if (argc > 0) + msecs = getArgument<int>(ctx, "Process.waitForFinished", argc, argv); + return JS_NewBool(ctx, fromJsObject(ctx, this_val)->waitForFinished(msecs)); + } catch (const QString &error) { return throwError(ctx, error); } + } + QString getEnv(const QString &name) const { return m_environment.value(name); } + void setEnv(const QString &name, const QString &value) { m_environment.insert(name, value); } + void setCodec(const QString &codec); - Q_INVOKABLE QString workingDirectory(); - Q_INVOKABLE void setWorkingDirectory(const QString &dir); + QString workingDirectory() const { return m_workingDirectory; } + void setWorkingDirectory(const QString &dir) { m_workingDirectory = dir; } - Q_INVOKABLE bool start(const QString &program, const QStringList &arguments); - Q_INVOKABLE int exec(const QString &program, const QStringList &arguments, - bool throwOnError = false); - Q_INVOKABLE void close(); - Q_INVOKABLE bool waitForFinished(int msecs = 30000); - Q_INVOKABLE void terminate(); - Q_INVOKABLE void kill(); + bool start(const QString &program, const QStringList &arguments); + int exec(const QString &program, const QStringList &arguments, bool throwOnError); + void close(); + bool waitForFinished(int msecs); + void terminate() { m_qProcess->terminate(); } + void kill() { m_qProcess->kill(); } - Q_INVOKABLE QString readLine(); - Q_INVOKABLE bool atEnd() const; - Q_INVOKABLE QString readStdOut(); - Q_INVOKABLE QString readStdErr(); + QString readLine(); + bool atEnd() const { return m_qProcess->atEnd(); } + QString readStdOut() { return m_codec->toUnicode(m_qProcess->readAllStandardOutput()); } + QString readStdErr() { return m_codec->toUnicode(m_qProcess->readAllStandardError()); } - Q_INVOKABLE void closeWriteChannel(); + void closeWriteChannel() { m_qProcess->closeWriteChannel(); } - Q_INVOKABLE void write(const QString &str); - Q_INVOKABLE void writeLine(const QString &str); + void write(const QString &str) { m_qProcess->write(m_codec->fromUnicode(str)); } + void writeLine(const QString &str); - Q_INVOKABLE int exitCode() const; + int exitCode() const { return m_qProcess->exitCode(); } - static QScriptValue js_shellQuote(QScriptContext *context, QScriptEngine *engine); + static JSValue jsShellQuote(JSContext *ctx, JSValue, int argc, JSValue *argv); private: QString findExecutable(const QString &filePath) const; - // ResourceAcquiringScriptObject implementation - void releaseResources() override; - std::unique_ptr<QProcess> m_qProcess; QProcessEnvironment m_environment; QString m_workingDirectory; QTextCodec *m_codec = nullptr; }; -QScriptValue Process::ctor(QScriptContext *context, QScriptEngine *engine) +JSValue Process::ctor(JSContext *ctx, JSValueConst, JSValueConst, int, JSValueConst *, int) { - Process *t; - switch (context->argumentCount()) { - case 0: - t = new Process(context); - break; - default: - return context->throwError(QStringLiteral("Process()")); - } + try { + JSValue obj = createObject(ctx); - const auto se = static_cast<ScriptEngine *>(engine); - se->addResourceAcquiringScriptObject(t); - const DubiousContextList dubiousContexts ({ + Process * const process = fromJsObject(ctx, obj); + const auto se = ScriptEngine::engineForContext(ctx); + const DubiousContextList dubiousContexts{ DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving) - }); - se->checkContext(QStringLiteral("qbs.Process"), dubiousContexts); - - QScriptValue obj = engine->newQObject(t, QScriptEngine::QtOwnership); - - // Get environment - QVariant v = engine->property(StringConstants::qbsProcEnvVarInternal()); - if (v.isNull()) { - // The build environment is not initialized yet. - // This can happen if one uses Process on the RHS of a binding like Group.name. - t->m_environment = static_cast<ScriptEngine *>(engine)->environment(); - } else { - t->m_environment - = QProcessEnvironment(*reinterpret_cast<QProcessEnvironment*>(v.value<void*>())); - } - se->setUsesIo(); - - return obj; -} - -Process::Process(QScriptContext *context) -{ - Q_UNUSED(context); - Q_ASSERT(thisObject().engine() == engine()); - - m_qProcess = std::make_unique<QProcess>(); - m_codec = QTextCodec::codecForName("UTF-8"); -} - -QString Process::getEnv(const QString &name) -{ - Q_ASSERT(thisObject().engine() == engine()); - return m_environment.value(name); + }; + se->checkContext(QStringLiteral("qbs.Process"), dubiousContexts); + + // Get environment + QVariant v = se->property(StringConstants::qbsProcEnvVarInternal()); + if (v.isNull()) { + // The build environment is not initialized yet. + // This can happen if one uses Process on the RHS of a binding like Group.name. + process->m_environment = se->environment(); + } else { + process->m_environment + = QProcessEnvironment(*reinterpret_cast<QProcessEnvironment*>(v.value<void*>())); + } + se->setUsesIo(); + return obj; + } catch (const QString &error) { return throwError(ctx, error); } } -void Process::setEnv(const QString &name, const QString &value) +void Process::setupStaticMethods(JSContext *ctx, JSValue classObj) { - Q_ASSERT(thisObject().engine() == engine()); - m_environment.insert(name, value); + setupMethod(ctx, classObj, "shellQuote", &Process::jsShellQuote, 3); } -QString Process::workingDirectory() +void Process::setupMethods(JSContext *ctx, JSValue obj) { - Q_ASSERT(thisObject().engine() == engine()); - return m_workingDirectory; + setupMethod(ctx, obj, "getEnv", &jsGetEnv, 1); + setupMethod(ctx, obj, "setEnv", &jsSetEnv, 2); + setupMethod(ctx, obj, "setCodec", &jsSetCodec, 1); + setupMethod(ctx, obj, "workingDirectory", &jsWorkingDir, 0); + setupMethod(ctx, obj, "setWorkingDirectory", &jsSetWorkingDir, 1); + setupMethod(ctx, obj, "start", &jsStart, 2); + setupMethod(ctx, obj, "exec", &jsExec, 3); + setupMethod(ctx, obj, "close", &jsClose, 0); + setupMethod(ctx, obj, "waitForFinished", &jsWaitForFinished, 1); + setupMethod(ctx, obj, "terminate", &jsTerminate, 0); + setupMethod(ctx, obj, "kill", &jsKill, 0); + setupMethod(ctx, obj, "readLine", &jsReadLine, 0); + setupMethod(ctx, obj, "atEnd", &jsAtEnd, 0); + setupMethod(ctx, obj, "readStdOut", &jsReadStdOut, 0); + setupMethod(ctx, obj, "readStdErr", &jsReadStdErr, 0); + setupMethod(ctx, obj, "closeWriteChannel", &jsCloseWriteChannel, 0); + setupMethod(ctx, obj, "write", &jsWrite, 1); + setupMethod(ctx, obj, "writeLine", &jsWriteLine, 1); + setupMethod(ctx, obj, "exitCode", &jsExitCode, 0); } -void Process::setWorkingDirectory(const QString &dir) +Process::Process(JSContext *) { - Q_ASSERT(thisObject().engine() == engine()); - m_workingDirectory = dir; + m_qProcess = std::make_unique<QProcess>(); + m_codec = QTextCodec::codecForName("UTF-8"); } bool Process::start(const QString &program, const QStringList &arguments) { - Q_ASSERT(thisObject().engine() == engine()); - if (!m_workingDirectory.isEmpty()) m_qProcess->setWorkingDirectory(m_workingDirectory); @@ -190,13 +219,9 @@ bool Process::start(const QString &program, const QStringList &arguments) int Process::exec(const QString &program, const QStringList &arguments, bool throwOnError) { - Q_ASSERT(thisObject().engine() == engine()); - if (!start(findExecutable(program), arguments)) { - if (throwOnError) { - context()->throwError(Tr::tr("Error running '%1': %2") - .arg(program, m_qProcess->errorString())); - } + if (throwOnError) + throw Tr::tr("Error running '%1': %2").arg(program, m_qProcess->errorString()); return -1; } m_qProcess->closeWriteChannel(); @@ -204,8 +229,7 @@ int Process::exec(const QString &program, const QStringList &arguments, bool thr if (throwOnError) { if (m_qProcess->error() != QProcess::UnknownError && m_qProcess->error() != QProcess::Crashed) { - context()->throwError(Tr::tr("Error running '%1': %2") - .arg(program, m_qProcess->errorString())); + throw Tr::tr("Error running '%1': %2").arg(program, m_qProcess->errorString()); } else if (m_qProcess->exitStatus() == QProcess::CrashExit || m_qProcess->exitCode() != 0) { QString errorMessage = m_qProcess->error() == QProcess::Crashed ? Tr::tr("Error running '%1': %2").arg(program, m_qProcess->errorString()) @@ -218,7 +242,7 @@ int Process::exec(const QString &program, const QStringList &arguments, bool thr const QString stdErr = readStdErr(); if (!stdErr.isEmpty()) errorMessage.append(Tr::tr(" The standard error output was:\n")).append(stdErr); - context()->throwError(errorMessage); + throw errorMessage; } } if (m_qProcess->error() != QProcess::UnknownError) @@ -230,32 +254,18 @@ void Process::close() { if (!m_qProcess) return; - Q_ASSERT(thisObject().engine() == engine()); m_qProcess.reset(); } bool Process::waitForFinished(int msecs) { - Q_ASSERT(thisObject().engine() == engine()); - if (m_qProcess->state() == QProcess::NotRunning) return true; return m_qProcess->waitForFinished(msecs); } -void Process::terminate() -{ - m_qProcess->terminate(); -} - -void Process::kill() -{ - m_qProcess->kill(); -} - void Process::setCodec(const QString &codec) { - Q_ASSERT(thisObject().engine() == engine()); const auto newCodec = QTextCodec::codecForName(qPrintable(codec)); if (newCodec) m_codec = newCodec; @@ -269,82 +279,36 @@ QString Process::readLine() return result; } -bool Process::atEnd() const -{ - return m_qProcess->atEnd(); -} - -QString Process::readStdOut() -{ - return m_codec->toUnicode(m_qProcess->readAllStandardOutput()); -} - -QString Process::readStdErr() -{ - return m_codec->toUnicode(m_qProcess->readAllStandardError()); -} - -void Process::closeWriteChannel() -{ - m_qProcess->closeWriteChannel(); -} - -int Process::exitCode() const -{ - return m_qProcess->exitCode(); -} - QString Process::findExecutable(const QString &filePath) const { ExecutableFinder exeFinder(ResolvedProductPtr(), m_environment); return exeFinder.findExecutable(filePath, m_workingDirectory); } -void Process::releaseResources() -{ - close(); - deleteLater(); -} - -void Process::write(const QString &str) -{ - m_qProcess->write(m_codec->fromUnicode(str)); -} - void Process::writeLine(const QString &str) { m_qProcess->write(m_codec->fromUnicode(str)); m_qProcess->putChar('\n'); } -QScriptValue Process::js_shellQuote(QScriptContext *context, QScriptEngine *engine) +JSValue Process::jsShellQuote(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv) { - if (Q_UNLIKELY(context->argumentCount() < 2)) { - return context->throwError(QScriptContext::SyntaxError, - QStringLiteral("shellQuote expects at least 2 arguments")); - } - const QString program = context->argument(0).toString(); - const QStringList args = context->argument(1).toVariant().toStringList(); - HostOsInfo::HostOs hostOs = HostOsInfo::hostOs(); - if (context->argumentCount() > 2) { - hostOs = context->argument(2).toVariant().toStringList().contains(QLatin1String("windows")) - ? HostOsInfo::HostOsWindows : HostOsInfo::HostOsOtherUnix; - } - return engine->toScriptValue(shellQuote(program, args, hostOs)); + try { + const auto args = getArguments<QString, QStringList>(ctx, "Process.shellQuote", argc, argv); + HostOsInfo::HostOs hostOs = HostOsInfo::hostOs(); + if (argc > 2) { + const auto osList = fromArg<QStringList>(ctx, "Process.shellQuote", 3, argv[2]); + hostOs = osList.contains(QLatin1String("windows")) + ? HostOsInfo::HostOsWindows : HostOsInfo::HostOsOtherUnix; + } + return makeJsString(ctx, shellQuote(std::get<0>(args), std::get<1>(args), hostOs)); + } catch (const QString &error) { return throwError(ctx, error); } } } // namespace Internal } // namespace qbs -void initializeJsExtensionProcess(QScriptValue extensionObject) +void initializeJsExtensionProcess(qbs::Internal::ScriptEngine *engine, JSValue extensionObject) { - using namespace qbs::Internal; - QScriptEngine *engine = extensionObject.engine(); - QScriptValue obj = engine->newQMetaObject(&Process::staticMetaObject, engine->newFunction(&Process::ctor)); - extensionObject.setProperty(QStringLiteral("Process"), obj); - obj.setProperty(QStringLiteral("shellQuote"), engine->newFunction(Process::js_shellQuote, 3)); + qbs::Internal::Process::registerClass(engine, extensionObject); } - -Q_DECLARE_METATYPE(qbs::Internal::Process *) - -#include "process.moc" |