2025-10-13 00:20:53 +00:00
|
|
|
#include "workspace/CommandManager.h"
|
|
|
|
|
|
|
|
|
|
#include <QFileInfo>
|
|
|
|
|
|
|
|
|
|
#include "xml/tinyxml2.h"
|
|
|
|
|
#include "common/SpdLogger.h"
|
|
|
|
|
|
|
|
|
|
static QMap<QString, QString> parseEnvAttr(const QString& envAttr) {
|
|
|
|
|
QMap<QString, QString> env;
|
|
|
|
|
if (envAttr.isEmpty()) return env;
|
|
|
|
|
const auto pairs = envAttr.split(';', Qt::SkipEmptyParts);
|
|
|
|
|
for (const auto& p : pairs) {
|
|
|
|
|
const auto kv = p.split('=', Qt::KeepEmptyParts);
|
|
|
|
|
if (kv.size() >= 2) env.insert(kv[0].trimmed(), kv[1].trimmed());
|
|
|
|
|
}
|
|
|
|
|
return env;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CommandManager::Reload(WorkSpace* ws) {
|
|
|
|
|
onCreate_.clear();
|
|
|
|
|
onLoad_.clear();
|
|
|
|
|
if (!ws) return;
|
|
|
|
|
|
2025-10-14 16:41:12 +00:00
|
|
|
const QString cmdPath = QString("%1/%2").arg(ws->GetDir(), ws->GetCommondFilePath());
|
2025-10-13 00:20:53 +00:00
|
|
|
if (cmdPath.isEmpty()) {
|
|
|
|
|
LOG_INFO("no command xml configured");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
QFileInfo fi(cmdPath);
|
|
|
|
|
if (!fi.exists() || !fi.isFile()) {
|
|
|
|
|
LOG_WARN("command xml not found: {}", cmdPath.toLocal8Bit().constData());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tinyxml2::XMLDocument doc;
|
|
|
|
|
auto rc = doc.LoadFile(cmdPath.toLocal8Bit().constData());
|
|
|
|
|
if (rc != tinyxml2::XML_SUCCESS) {
|
2025-10-13 14:03:53 +00:00
|
|
|
LOG_WARN("load command xml failed: {} rc:{}", cmdPath.toLocal8Bit().constData(), static_cast<int>(rc));
|
2025-10-13 00:20:53 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
auto* root = doc.RootElement();
|
|
|
|
|
if (!root) {
|
|
|
|
|
LOG_WARN("command xml has no root: {}", cmdPath.toLocal8Bit().constData());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto* node = root->FirstChildElement(); node; node = node->NextSiblingElement()) {
|
|
|
|
|
const char* tag = node->Name();
|
|
|
|
|
if (!tag) continue;
|
|
|
|
|
QString tagQ = QString::fromUtf8(tag).toLower();
|
|
|
|
|
if (tagQ != QLatin1String("commond") && tagQ != QLatin1String("command")) continue;
|
|
|
|
|
|
|
|
|
|
Command cmd;
|
|
|
|
|
if (const char* nameAttr = node->Attribute("name")) cmd.name = QString::fromUtf8(nameAttr);
|
|
|
|
|
if (const char* exeAttr = node->Attribute("exe")) cmd.program = QString::fromUtf8(exeAttr);
|
|
|
|
|
if (cmd.program.isEmpty()) {
|
|
|
|
|
if (const char* programAttr = node->Attribute("program")) cmd.program = QString::fromUtf8(programAttr);
|
|
|
|
|
}
|
|
|
|
|
if (const char* pathAttr = node->Attribute("path")) cmd.path = QString::fromUtf8(pathAttr);
|
|
|
|
|
if (cmd.path.isEmpty()) {
|
|
|
|
|
if (const char* pathTypo = node->Attribute("paht")) cmd.path = QString::fromUtf8(pathTypo);
|
|
|
|
|
}
|
|
|
|
|
if (const char* argsAttr = node->Attribute("args")) cmd.rawArgs = QString::fromUtf8(argsAttr);
|
|
|
|
|
if (const char* cwdAttr = node->Attribute("workingDir")) cmd.workingDir = QString::fromUtf8(cwdAttr);
|
|
|
|
|
if (cmd.workingDir.isEmpty()) {
|
|
|
|
|
if (const char* cwdAttr2 = node->Attribute("cwd")) cmd.workingDir = QString::fromUtf8(cwdAttr2);
|
|
|
|
|
}
|
|
|
|
|
if (const char* enabledAttr = node->Attribute("enabled")) {
|
|
|
|
|
QString en = QString::fromUtf8(enabledAttr).toLower();
|
|
|
|
|
cmd.enabled = !(en == QLatin1String("false") || en == QLatin1String("0"));
|
|
|
|
|
}
|
|
|
|
|
if (const char* descAttr = node->Attribute("descript")) cmd.descript = QString::fromUtf8(descAttr);
|
|
|
|
|
if (cmd.descript.isEmpty()) {
|
|
|
|
|
if (const char* desc2 = node->Attribute("description")) cmd.descript = QString::fromUtf8(desc2);
|
|
|
|
|
}
|
|
|
|
|
if (const char* timeoutAttr = node->Attribute("timeoutSec")) {
|
|
|
|
|
bool ok = false; int v = QString::fromUtf8(timeoutAttr).toInt(&ok);
|
|
|
|
|
if (ok && v > 0) cmd.timeoutMs = v * 1000;
|
|
|
|
|
}
|
|
|
|
|
// env: either attribute env="KEY=VAL;K2=V2" or child elements <env key="" value=""/>
|
|
|
|
|
if (const char* envAttr = node->Attribute("env")) {
|
|
|
|
|
cmd.env = parseEnvAttr(QString::fromUtf8(envAttr));
|
|
|
|
|
}
|
|
|
|
|
for (auto* envNode = node->FirstChildElement("env"); envNode; envNode = envNode->NextSiblingElement("env")) {
|
|
|
|
|
const char* k = envNode->Attribute("key");
|
|
|
|
|
const char* v = envNode->Attribute("value");
|
|
|
|
|
if (k && v) cmd.env.insert(QString::fromUtf8(k), QString::fromUtf8(v));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Pre-build args list from rawArgs (actual insertion of path happens in executor)
|
|
|
|
|
if (!cmd.rawArgs.isEmpty()) {
|
|
|
|
|
for (const auto& part : cmd.rawArgs.split(' ', Qt::SkipEmptyParts)) {
|
|
|
|
|
cmd.args << part;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// when routing
|
|
|
|
|
WorkSpace::CommandWhen target = WorkSpace::CommandWhen::OnCreate; // default
|
|
|
|
|
if (const char* whenAttr = node->Attribute("when")) {
|
|
|
|
|
QString wa = QString::fromUtf8(whenAttr).toLower();
|
|
|
|
|
if (wa == QLatin1String("onload")) target = WorkSpace::CommandWhen::OnLoad;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto exec = std::make_unique<CommandExecutor>(cmd);
|
|
|
|
|
if (target == WorkSpace::CommandWhen::OnCreate) onCreate_.push_back(std::move(exec));
|
|
|
|
|
else onLoad_.push_back(std::move(exec));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CommandManager::Execute(WorkSpace* ws, WorkSpace::CommandWhen when) {
|
|
|
|
|
// Reload each time to reflect latest XML
|
|
|
|
|
Reload(ws);
|
|
|
|
|
auto& list = (when == WorkSpace::CommandWhen::OnCreate) ? onCreate_ : onLoad_;
|
|
|
|
|
for (auto& exec : list) {
|
|
|
|
|
exec->Execute(ws, when);
|
|
|
|
|
}
|
2025-10-13 16:15:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<Command> CommandManager::ListCommands(WorkSpace* ws) {
|
|
|
|
|
std::vector<Command> items;
|
|
|
|
|
Reload(ws);
|
|
|
|
|
for (auto& exec : onCreate_) {
|
|
|
|
|
items.push_back(exec->Get());
|
|
|
|
|
}
|
|
|
|
|
for (auto& exec : onLoad_) {
|
|
|
|
|
items.push_back(exec->Get());
|
|
|
|
|
}
|
|
|
|
|
return items;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CommandManager::ExecuteByName(WorkSpace* ws, const QString& name) {
|
|
|
|
|
Reload(ws);
|
|
|
|
|
auto matchAndRun = [&](std::vector<std::unique_ptr<CommandExecutor>>& list, WorkSpace::CommandWhen when) -> bool {
|
|
|
|
|
for (auto& exec : list) {
|
|
|
|
|
if (exec->Get().name == name) {
|
|
|
|
|
exec->Execute(ws, when);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
if (matchAndRun(onCreate_, WorkSpace::CommandWhen::OnCreate)) return true;
|
|
|
|
|
if (matchAndRun(onLoad_, WorkSpace::CommandWhen::OnLoad)) return true;
|
|
|
|
|
return false;
|
2025-10-13 00:20:53 +00:00
|
|
|
}
|