From e31eec24fef828980e4649c8f5f0a9ceb16e9189 Mon Sep 17 00:00:00 2001 From: aoyama Date: Thu, 20 Mar 2025 17:57:30 +0900 Subject: [PATCH 01/15] update --- src/tglobal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tglobal.h b/src/tglobal.h index 1acbff848..ddbea826f 100644 --- a/src/tglobal.h +++ b/src/tglobal.h @@ -1,7 +1,7 @@ #pragma once constexpr auto TF_VERSION_STR = "2.10.1"; constexpr auto TF_VERSION_NUMBER = 0x020a01; -constexpr auto TF_SRC_REVISION = 2999; +constexpr auto TF_SRC_REVISION = 3001; #include #include From cdabc71547e6962a9287aed81a4b1016a3270dab Mon Sep 17 00:00:00 2001 From: aoyama Date: Sat, 19 Apr 2025 09:00:35 +0900 Subject: [PATCH 02/15] temp update --- defaults/development.ini | 4 +- src/tabstractcontroller.h | 4 + src/tactioncontext.cpp | 6 +- src/tactioncontroller.cpp | 40 ++- src/tactioncontroller.h | 14 +- src/tactionview.cpp | 9 + src/tactionview.h | 1 + src/tviewhelper.cpp | 8 + src/tviewhelper.h | 2 + tools/tfmanager/tfmanager.pro | 2 + tools/tfserver/tfserver.pro | 2 + tools/tmake/erbparser.cpp | 6 +- tools/tmake/main.cpp | 2 +- tools/tmake/tmake.pro | 2 + tools/tspawn/erbgenerator.h | 5 +- tools/tspawn/generator.h | 9 + tools/tspawn/main.cpp | 420 ++++++++++++++--------- tools/tspawn/otamagenerator.h | 6 +- tools/tspawn/servicegenerator.h | 5 +- tools/tspawn/tspawn.pro | 7 + tools/tspawn/validatorgenerator.cpp | 42 +-- tools/tspawn/vitevuegenerator.cpp | 382 +++++++++++++++++++++ tools/tspawn/vitevuegenerator.h | 22 ++ tools/tspawn/vitevueservicegenerator.cpp | 148 ++++++++ tools/tspawn/vitevueservicegenerator.h | 17 + tools/tspawn/vueservicegenerator.cpp | 182 +++++----- 26 files changed, 1054 insertions(+), 293 deletions(-) create mode 100644 tools/tspawn/generator.h create mode 100644 tools/tspawn/vitevuegenerator.cpp create mode 100644 tools/tspawn/vitevuegenerator.h create mode 100644 tools/tspawn/vitevueservicegenerator.cpp create mode 100644 tools/tspawn/vitevueservicegenerator.h diff --git a/defaults/development.ini b/defaults/development.ini index ddb5f0939..a90d60322 100644 --- a/defaults/development.ini +++ b/defaults/development.ini @@ -7,8 +7,8 @@ ## Template system section ## -# Specify the template system of view, ERB or Otama. -TemplateSystem=ERB +# Template system of view. +TemplateSystem=$TemplateSystem$ ## diff --git a/src/tabstractcontroller.h b/src/tabstractcontroller.h index 1107df6ef..19749bc88 100644 --- a/src/tabstractcontroller.h +++ b/src/tabstractcontroller.h @@ -23,6 +23,10 @@ class T_CORE_EXPORT TAbstractController : public QObject { virtual const TSession &session() const; virtual QString getRenderingData(const QString &templateName, const QVariantMap &vars = QVariantMap()); virtual QByteArray authenticityToken() const { return QByteArray(); } + virtual QVariantMap flashVariants() const { return QVariantMap(); } + virtual QVariant flashVariant(const QString &) const { return QVariant(); } + virtual QJsonObject flashVariantsJson() const { return QJsonObject(); } + virtual QJsonObject flashVariantJson(const QString &) const { return QJsonObject(); } virtual void setFlash(const QString &name, const QVariant &value); void exportVariant(const QString &name, const QVariant &value, bool overwrite = true); virtual bool isUserLoggedIn() const; diff --git a/src/tactioncontext.cpp b/src/tactioncontext.cpp index 642c13299..6d2268b4c 100644 --- a/src/tactioncontext.cpp +++ b/src/tactioncontext.cpp @@ -155,10 +155,8 @@ void TActionContext::execute(THttpRequest &request) tSystemDebug("Re-generate session ID: {}", (const char*)_currController->session().sessionId.data()); } - if (EnableCsrfProtectionModuleFlag && _currController->csrfProtectionEnabled()) { - // Sets CSRF protection information - TActionController::setCsrfProtectionInto(_currController->session()); - } + // Sets CSRF protection information + TActionController::setCsrfProtectionInto(_currController->session()); } // Database Transaction diff --git a/src/tactioncontroller.cpp b/src/tactioncontroller.cpp index bb963273f..2d082eb8a 100644 --- a/src/tactioncontroller.cpp +++ b/src/tactioncontroller.cpp @@ -31,7 +31,8 @@ #include #include -const QString FLASH_VARS_SESSION_KEY("_flashVariants"); +const QString QUEUED_FLASH_SESSION_KEY("_flashVariants"); +const QString FLASH_VARS_SESSION_KEY("_activeFlash"); const QString LOGIN_USER_NAME_KEY("_loginUserName"); const QByteArray DEFAULT_CONTENT_TYPE("text/html"); @@ -207,7 +208,11 @@ void TActionController::setCsrfProtectionInto(TSession &session) { if (TSessionManager::instance().storeType() == QLatin1String("cookie")) { QString key = TSessionManager::instance().csrfProtectionKey(); - session.insert(key, TSessionManager::instance().generateId()); // it's just a random value + QByteArray val = session.value(key).toByteArray(); + + if (val.isEmpty()) { + session.insert(key, TSessionManager::instance().generateId()); // it's just a random value + } } } @@ -595,7 +600,7 @@ void TActionController::redirect(const QUrl &url, int statusCode) // Enable flash-variants QVariant var; var.setValue(_flashVars); - _sessionStore.insert(FLASH_VARS_SESSION_KEY, var); + _sessionStore.insert(QUEUED_FLASH_SESSION_KEY, var); } /*! @@ -655,12 +660,39 @@ bool TActionController::sendData(const QByteArray &data, const QByteArray &conte */ void TActionController::exportAllFlashVariants() { - QVariant var = _sessionStore.take(FLASH_VARS_SESSION_KEY); + _sessionStore.remove(FLASH_VARS_SESSION_KEY); + + QVariant var = _sessionStore.take(QUEUED_FLASH_SESSION_KEY); if (!var.isNull()) { exportVariants(var.toMap()); + _sessionStore.insert(FLASH_VARS_SESSION_KEY, var); } } + +QVariantMap TActionController::flashVariants() const +{ + return _sessionStore.value(FLASH_VARS_SESSION_KEY).toMap(); +} + + +QVariant TActionController::flashVariant(const QString &key) const +{ + return _sessionStore.value(FLASH_VARS_SESSION_KEY).toMap().value(key); +} + + +QJsonObject TActionController::flashVariantsJson() const +{ + return QJsonObject::fromVariantMap(flashVariants()); +} + + +QJsonObject TActionController::flashVariantJson(const QString &key) const +{ + return QJsonObject::fromVariantMap(flashVariant(key).toMap()); +} + /*! Validates the access of the user \a user. Returns true if the user access is allowed by rule; otherwise returns false. diff --git a/src/tactioncontroller.h b/src/tactioncontroller.h index 90c096cda..226409cf2 100644 --- a/src/tactioncontroller.h +++ b/src/tactioncontroller.h @@ -37,7 +37,11 @@ class T_CORE_EXPORT TActionController : public TAbstractController, public TActi virtual QStringList exceptionActionsOfCsrfProtection() const { return QStringList(); } virtual bool transactionEnabled() const { return true; } QByteArray authenticityToken() const override; - QString flash(const QString &name) const; + QVariantMap flashVariants() const override; + QVariant flashVariant(const QString &key) const override; + QJsonObject flashVariantsJson() const override; + QJsonObject flashVariantJson(const QString &key) const override; + //QString flash(const QString &name) const; QHostAddress clientAddress() const; virtual bool isUserLoggedIn() const override; virtual QString identityKeyOfLoginUser() const; @@ -199,10 +203,10 @@ inline void TActionController::setStatusCode(int code) _statCode = code; } -inline QString TActionController::flash(const QString &name) const -{ - return _flashVars.value(name).toString(); -} +// inline QString TActionController::flash(const QString &name) const +// { +// return _flashVars.value(name).toString(); +// } inline QByteArray TActionController::contentType() const { diff --git a/src/tactionview.cpp b/src/tactionview.cpp index 8430b1d5a..55cd6fbff 100644 --- a/src/tactionview.cpp +++ b/src/tactionview.cpp @@ -71,6 +71,15 @@ QString TActionView::authenticityToken() const return (actionController) ? QString::fromLatin1(actionController->authenticityToken().data()) : QString(); } +/*! + Returns flash variants; +*/ +QVariantMap TActionView::flashVariants() const +{ + static QVariantMap dummy; + return (actionController) ? actionController->flashVariants() : dummy; +} + /*! Outputs the string of the HTML attribute \a attr to a view template. diff --git a/src/tactionview.h b/src/tactionview.h index 7886f53b5..396b7fb8f 100644 --- a/src/tactionview.h +++ b/src/tactionview.h @@ -23,6 +23,7 @@ class T_CORE_EXPORT TActionView : public QObject, public TActionHelper, public T QVariant variant(const QString &name) const; bool hasVariant(const QString &name) const; const QVariantMap &allVariants() const; + QVariantMap flashVariants() const; const TAbstractController *controller() const override; const THttpRequest &httpRequest() const; void reset(); diff --git a/src/tviewhelper.cpp b/src/tviewhelper.cpp index 647311f2f..61423246f 100644 --- a/src/tviewhelper.cpp +++ b/src/tviewhelper.cpp @@ -574,6 +574,14 @@ QString TViewHelper::scriptTag(const QString &src, bool withTimestamp, const THt return tag("script", attr, QString()); } + +QString TViewHelper::viteScriptTag(const QString &name, const THtmlAttribute &attributes) const +{ + // manifest.json をもとにパスを見つける必要あり。 + QString src = name; + return scriptTag(src, true, attributes); +} + /*! Creates and returns a THtmlAttribute object with \a key =\a "value". */ diff --git a/src/tviewhelper.h b/src/tviewhelper.h index de5dc0743..f64d07ec5 100644 --- a/src/tviewhelper.h +++ b/src/tviewhelper.h @@ -172,6 +172,8 @@ class T_CORE_EXPORT TViewHelper { QString scriptTag(const QString &src, bool withTimestamp = true, const THtmlAttribute &attributes = THtmlAttribute()) const; + QString viteScriptTag(const QString &name, const THtmlAttribute &attributes) const; + QString tag(const QString &name, const THtmlAttribute &attributes); QString tag(const QString &name, const THtmlAttribute &attributes, bool selfClose); diff --git a/tools/tfmanager/tfmanager.pro b/tools/tfmanager/tfmanager.pro index 431f8d0d1..8b806b447 100644 --- a/tools/tfmanager/tfmanager.pro +++ b/tools/tfmanager/tfmanager.pro @@ -5,6 +5,8 @@ CONFIG += console CONFIG -= app_bundle QT += network QT -= gui +MOC_DIR = .obj/ +OBJECTS_DIR = .obj/ # C++ Standards Support CONFIG += c++20 diff --git a/tools/tfserver/tfserver.pro b/tools/tfserver/tfserver.pro index 696af8b91..2121ac22b 100644 --- a/tools/tfserver/tfserver.pro +++ b/tools/tfserver/tfserver.pro @@ -5,6 +5,8 @@ CONFIG += console CONFIG -= app_bundle QT += network sql xml qml QT -= gui +MOC_DIR = .obj/ +OBJECTS_DIR = .obj/ # C++ Standards Support CONFIG += c++20 diff --git a/tools/tmake/erbparser.cpp b/tools/tmake/erbparser.cpp index 00e0b142f..7a0fa89e7 100644 --- a/tools/tmake/erbparser.cpp +++ b/tools/tmake/erbparser.cpp @@ -161,11 +161,11 @@ void ErbParser::parsePercentTag() QPair p = parseEndPercentTag(); if (!p.first.isEmpty()) { if (p.second.isEmpty()) { - srcCode += QLatin1String("responsebody += QVariant("); + srcCode += QLatin1String("echo("); srcCode += semicolonTrim(p.first); - srcCode += QLatin1String(").toString();\n"); + srcCode += QLatin1String(");\n"); } else { - srcCode += QLatin1String("{ QString ___s = QVariant("); + srcCode += QLatin1String("{ QString ___s = QVariant("); // TODO 修正!!! srcCode += semicolonTrim(p.first); srcCode += QLatin1String(").toString(); responsebody += (___s.isEmpty()) ? QVariant("); srcCode += semicolonTrim(p.second); diff --git a/tools/tmake/main.cpp b/tools/tmake/main.cpp index 3e00bd68a..b60bc73ef 100644 --- a/tools/tmake/main.cpp +++ b/tools/tmake/main.cpp @@ -99,7 +99,7 @@ int main(int argc, char *argv[]) ViewConverter conv(viewDir, outputDir, createProFile); QString templateSystem = devSetting.value("TemplateSystem").toString(); if (templateSystem.isEmpty()) { - templateSystem = appSetting.value("TemplateSystem", "Erb").toString(); + templateSystem = appSetting.value("TemplateSystem", "erb").toString(); } res = conv.convertView(templateSystem); diff --git a/tools/tmake/tmake.pro b/tools/tmake/tmake.pro index f7a2a8984..2da9cce0b 100644 --- a/tools/tmake/tmake.pro +++ b/tools/tmake/tmake.pro @@ -4,6 +4,8 @@ VERSION = 2.0.0 CONFIG += console CONFIG -= app_bundle QT -= gui +MOC_DIR = .obj/ +OBJECTS_DIR = .obj/ # C++ Standards Support CONFIG += c++20 diff --git a/tools/tspawn/erbgenerator.h b/tools/tspawn/erbgenerator.h index 168cf8a4c..ad05e3385 100644 --- a/tools/tspawn/erbgenerator.h +++ b/tools/tspawn/erbgenerator.h @@ -1,16 +1,17 @@ #pragma once #include "global.h" +#include "generator.h" #include #include #include #include -class ErbGenerator { +class ErbGenerator : public Generator { public: ErbGenerator(const QString &view, const QList> &fields, int pkIdx, int autoValIdx); virtual ~ErbGenerator() {} - bool generate(const QString &dstDir) const; + bool generate(const QString &dstDir) const override; protected: virtual QString indexTemplate() const; diff --git a/tools/tspawn/generator.h b/tools/tspawn/generator.h new file mode 100644 index 000000000..c18f9aac3 --- /dev/null +++ b/tools/tspawn/generator.h @@ -0,0 +1,9 @@ +#pragma once +#include + + +class Generator { +public: + virtual ~Generator() = default; + virtual bool generate(const QString &dstDir) const = 0; +}; diff --git a/tools/tspawn/main.cpp b/tools/tspawn/main.cpp index a03bac667..cc6637b06 100644 --- a/tools/tspawn/main.cpp +++ b/tools/tspawn/main.cpp @@ -17,6 +17,8 @@ #include "otamagenerator.h" #include "vueservicegenerator.h" #include "vueerbgenerator.h" +#include "vitevuegenerator.h" +#include "vitevueservicegenerator.h" #include "projectfilegenerator.h" #include "sqlobjgenerator.h" #include "tableschema.h" @@ -26,6 +28,7 @@ #include "apicontrollergenerator.h" #include "apiservicegenerator.h" #include +#include #include #ifndef Q_CC_MSVC #include @@ -38,8 +41,8 @@ #define D_VIEWS QLatin1String("views/") #define D_HELPERS QLatin1String("helpers/") -enum SubCommand { - Invalid = 0, +enum class SubCommand { + Invalid, Help, New, Controller, @@ -62,45 +65,60 @@ enum SubCommand { ShowCollections, }; -const QMap subCommands = { - {"-h", Help}, - {"--help", Help}, - {"new", New}, - {"n", New}, - {"controller", Controller}, - {"c", Controller}, - {"model", Model}, - {"m", Model}, - {"helper", Helper}, - {"h", Helper}, - {"usermodel", UserModel}, - {"u", UserModel}, - {"sqlobject", SqlObject}, - {"o", SqlObject}, - {"mongoscaffold", MongoScaffold}, - {"ms", MongoScaffold}, - //{"updatemodel", UpdateModel}, - //{"um", UpdateModel}, - {"mongomodel", MongoModel}, - {"mm", MongoModel}, - {"websocket", WebSocketEndpoint}, - {"w", WebSocketEndpoint}, - {"api", Api}, - {"a", Api}, - {"validator", Validator}, - {"v", Validator}, - {"mailer", Mailer}, - {"l", Mailer}, - {"scaffold", Scaffold}, - {"s", Scaffold}, - {"delete", Delete}, - {"d", Delete}, - {"remove", Delete}, - {"r", Delete}, - {"--show-drivers", ShowDrivers}, - {"--show-driver-path", ShowDriverPath}, - {"--show-tables", ShowTables}, - {"--show-collections", ShowCollections}, +enum class TemplateSystem { + Invalid, + Erb, + Otama, + Vue, + Vite_Vue, +}; + +const QMap subCommands = { + {"-h", SubCommand::Help}, + {"--help", SubCommand::Help}, + {"new", SubCommand::New}, + {"n", SubCommand::New}, + {"controller", SubCommand::Controller}, + {"c", SubCommand::Controller}, + {"model", SubCommand::Model}, + {"m", SubCommand::Model}, + {"helper", SubCommand::Helper}, + {"h", SubCommand::Helper}, + {"usermodel", SubCommand::UserModel}, + {"u", SubCommand::UserModel}, + {"sqlobject", SubCommand::SqlObject}, + {"o", SubCommand::SqlObject}, + {"mongoscaffold", SubCommand::MongoScaffold}, + {"ms", SubCommand::MongoScaffold}, + //{"updatemodel", SubCommand::UpdateModel}, + //{"um", SubCommand::UpdateModel}, + {"mongomodel", SubCommand::MongoModel}, + {"mm", SubCommand::MongoModel}, + {"websocket", SubCommand::WebSocketEndpoint}, + {"w", SubCommand::WebSocketEndpoint}, + {"api", SubCommand::Api}, + {"a", SubCommand::Api}, + {"validator", SubCommand::Validator}, + {"v", SubCommand::Validator}, + {"mailer", SubCommand::Mailer}, + {"l", SubCommand::Mailer}, + {"scaffold", SubCommand::Scaffold}, + {"s", SubCommand::Scaffold}, + {"delete", SubCommand::Delete}, + {"d", SubCommand::Delete}, + {"remove", SubCommand::Delete}, + {"r", SubCommand::Delete}, + {"--show-drivers", SubCommand::ShowDrivers}, + {"--show-driver-path", SubCommand::ShowDriverPath}, + {"--show-tables", SubCommand::ShowTables}, + {"--show-collections", SubCommand::ShowCollections}, +}; + +const QMap templateSystemMap = { + {"erb", TemplateSystem::Erb}, + {"otama", TemplateSystem::Otama}, + {"vue", TemplateSystem::Vue}, + {"vite+vue", TemplateSystem::Vite_Vue}, }; const QStringList subDirs = { @@ -178,14 +196,16 @@ const QStringList filePaths = { L("helpers/CMakeLists.txt"), }; +namespace { const QString appIni = QLatin1String("config/application.ini"); const QString devIni = QLatin1String("config/development.ini"); -static QSettings appSettings(appIni, QSettings::IniFormat); -static QSettings devSettings(devIni, QSettings::IniFormat); -static QString templateSystem; +QSettings appSettings(appIni, QSettings::IniFormat); +QSettings devSettings(devIni, QSettings::IniFormat); +TemplateSystem templateSystem = TemplateSystem::Invalid; -static void usage() + +void usage() { std::printf("usage: tspawn [args]\n\n" "Type 'tspawn --show-drivers' to show all the available database drivers for Qt.\n" @@ -193,7 +213,7 @@ static void usage() "Type 'tspawn --show-tables' to show all tables to user in the setting of 'dev'.\n" "Type 'tspawn --show-collections' to show all collections in the MongoDB.\n\n" "Available subcommands:\n" - " new (n) \n" + " new (n) [--template [erb | otama | vue | vite+vue]]\n" " scaffold (s) [model-name]\n" " controller (c) action [action ...]\n" " model (m) [model-name]\n" @@ -206,11 +226,12 @@ static void usage() " api (a) \n" " validator (v) \n" " mailer (l) action [action ...]\n" - " delete (d) \n"); + " delete (d) \n" + ); } -static QStringList rmfiles(const QStringList &files, bool &allRemove, bool &quit, const QString &baseDir, const QString &proj = QString()) +QStringList rmfiles(const QStringList &files, bool &allRemove, bool &quit, const QString &baseDir, const QString &proj = QString()) { QStringList rmd; @@ -282,7 +303,7 @@ static QStringList rmfiles(const QStringList &files, bool &allRemove, bool &quit } -static QStringList rmfiles(const QStringList &files, const QString &baseDir, const QString &proj) +QStringList rmfiles(const QStringList &files, const QString &baseDir, const QString &proj) { bool allRemove = false; bool quit = false; @@ -290,7 +311,7 @@ static QStringList rmfiles(const QStringList &files, const QString &baseDir, con } -static uint random(uint max) +uint random(uint max) { static std::random_device randev; static std::default_random_engine eng(randev()); @@ -299,7 +320,7 @@ static uint random(uint max) } -static QByteArray randomString(int length) +QByteArray randomString(int length) { constexpr auto ch = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; QByteArray ret; @@ -312,7 +333,7 @@ static QByteArray randomString(int length) } -static bool createNewApplication(const QString &name) +bool createNewApplication(const QString &name, const QByteArray &templateSystem) { if (name.isEmpty()) { qCritical("invalid argument"); @@ -355,6 +376,11 @@ static bool createNewApplication(const QString &name) if (filename == "application.ini") { replaceString(dst, "$SessionSecret$", randomString(30)); } + + // Replaces a string in development.ini file + if (filename == "development.ini") { + replaceString(dst, "$TemplateSystem$", templateSystem); + } } #ifdef Q_OS_WIN @@ -368,7 +394,7 @@ static bool createNewApplication(const QString &name) } -static int deleteScaffold(const QString &name) +int deleteScaffold(const QString &name) { // Removes files QString str = name; @@ -405,7 +431,7 @@ static int deleteScaffold(const QString &name) << "api" + str + "service.cpp"; // Template system - if (templateSystem == "otama") { + if (templateSystem == TemplateSystem::Otama) { views << str + "/index.html" << str + "/index.otm" << str + "/show.html" @@ -414,13 +440,14 @@ static int deleteScaffold(const QString &name) << str + "/create.otm" << str + "/save.html" << str + "/save.otm"; - } else if (templateSystem == "erb") { + } else if (templateSystem == TemplateSystem::Erb || templateSystem == TemplateSystem::Vue + || templateSystem == TemplateSystem::Vite_Vue) { views << str + "/index.erb" << str + "/show.erb" << str + "/create.erb" << str + "/save.erb"; } else { - qCritical("Invalid template system specified: %s", qUtf8Printable(templateSystem)); + qCritical("Invalid template system specified"); return 2; } @@ -458,7 +485,7 @@ static int deleteScaffold(const QString &name) } -static bool checkIniFile() +bool checkIniFile() { // Checking INI file if (!QFile::exists(appIni)) { @@ -470,7 +497,7 @@ static bool checkIniFile() } -static void printSuccessMessage(const QString &model) +void printSuccessMessage(const QString &model) { QString msg; @@ -498,28 +525,39 @@ static void printSuccessMessage(const QString &model) } -static bool isVueEnabled() +std::unique_ptr createServiceGenerator(TemplateSystem templateSystem, const QString &service, const QList> &fields, int pkIdx, int lockRevIdx) { - static int vueEnable = -1; + if (templateSystem == TemplateSystem::Erb) { + return std::make_unique(service, fields, pkIdx, lockRevIdx); + } else if (templateSystem == TemplateSystem::Vue) { + return std::make_unique(service, fields, pkIdx, lockRevIdx); + } else if (templateSystem == TemplateSystem::Vite_Vue) { + return std::make_unique(service, fields, pkIdx, lockRevIdx); + } else if (templateSystem == TemplateSystem::Otama) { + return std::make_unique(service, fields, pkIdx, lockRevIdx); + } else { + qCritical("Invalid template system specified"); + return nullptr; + } +} - if (vueEnable < 0) { - std::printf("\n"); - QTextStream stream(stdin); - for (;;) { - std::printf(" Create sources for vue.js? [y/n] "); - QString line = stream.readLine().trimmed(); - const QChar c = line[0]; - if (c == 'Y' || c == 'y') { - vueEnable = 1; - break; - } else if (c == 'N' || c == 'n') { - vueEnable = 0; - break; - } - } +std::unique_ptr createViewGenerator(TemplateSystem templateSystem, const QString &view, const QList> &fields, int pkIdx, int autoValIdx) +{ + if (templateSystem == TemplateSystem::Erb) { + return std::make_unique(view, fields, pkIdx, autoValIdx); + } else if (templateSystem == TemplateSystem::Vue) { + return std::make_unique(view, fields, pkIdx, autoValIdx); + } else if (templateSystem == TemplateSystem::Vite_Vue) { + return std::make_unique(view, fields, pkIdx, autoValIdx); + } else if (templateSystem == TemplateSystem::Otama) { + return std::make_unique(view, fields, pkIdx, autoValIdx); + } else { + qCritical("Invalid template system specified"); + return nullptr; } - return (bool)vueEnable; +} + } @@ -527,33 +565,42 @@ int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); QStringList args = QCoreApplication::arguments(); - int subcmd = subCommands.value(args.value(1), Invalid); + SubCommand subcmd = subCommands.value(args.value(1), SubCommand::Invalid); switch (subcmd) { - case Invalid: + case SubCommand::Invalid: qCritical("invalid argument"); return 1; break; - case Help: + case SubCommand::Help: usage(); break; - case New: + case SubCommand::New: { // Creates new project - if (!createNewApplication(args.value(2))) { + QByteArray ts = "erb"; + if (args.count() > 4 && args.value(3) == "--template") { + const auto name = args.value(4).toLower(); + if (templateSystemMap.contains(name)) { + ts = name.toLatin1(); + } + } + + if (!createNewApplication(args.value(2), ts)) { return 1; } break; + } - case ShowDrivers: + case SubCommand::ShowDrivers: std::printf("Available database drivers for Qt:\n"); for (QStringListIterator i(TableSchema::databaseDrivers()); i.hasNext();) { std::printf(" %s\n", qUtf8Printable(i.next())); } break; - case ShowDriverPath: { + case SubCommand::ShowDriverPath: { QString path = QLibraryInfo::path(QLibraryInfo::PluginsPath) + "/sqldrivers"; QFileInfo fi(path); if (!fi.exists() || !fi.isDir()) { @@ -564,7 +611,7 @@ int main(int argc, char *argv[]) break; } - case ShowTables: + case SubCommand::ShowTables: if (checkIniFile()) { QStringList tables = TableSchema::tables(); if (!tables.isEmpty()) { @@ -579,7 +626,7 @@ int main(int argc, char *argv[]) } break; - case ShowCollections: + case SubCommand::ShowCollections: if (checkIniFile()) { // MongoDB settings QString mongoini = appSettings.value("MongoDbSettingsFile").toString().trimmed(); @@ -614,14 +661,21 @@ int main(int argc, char *argv[]) return 2; } - // ERB or Otama - templateSystem = devSettings.value("TemplateSystem").toString().toLower(); - if (templateSystem.isEmpty()) { - templateSystem = appSettings.value("TemplateSystem", "Erb").toString().toLower(); + // Template system + QString ts = devSettings.value("TemplateSystem").toString().toLower(); + if (ts.isEmpty()) { + ts = appSettings.value("TemplateSystem", "Erb").toString().toLower(); + } + + if (!templateSystemMap.contains(ts)) { + qCritical("Invalid template system specified: %s", qUtf8Printable(ts)); + return 2; + } else { + templateSystem = templateSystemMap.value(ts); } switch (subcmd) { - case Controller: { + case SubCommand::Controller: { QString ctrl = args.value(2); ControllerGenerator crtlgen(ctrl, args.mid(3)); crtlgen.generate(D_CTRLS); @@ -633,7 +687,7 @@ int main(int argc, char *argv[]) break; } - case Model: { + case SubCommand::Model: { ModelGenerator modelgen(ModelGenerator::Sql, args.value(3), args.value(2)); modelgen.generate(D_MODELS); @@ -643,23 +697,29 @@ int main(int argc, char *argv[]) return 2; } - if (isVueEnabled()) { - VueServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); - svrgen.generate(D_MODELS); - } else { - ServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); - svrgen.generate(D_MODELS); + // Generates servie file of the specified template system + std::unique_ptr svrgen = createServiceGenerator(templateSystem, modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); + if (svrgen) { + svrgen->generate(D_MODELS); } + + // if (templateSystem == TemplateSystem::Vue) { + // VueServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); + // svrgen.generate(D_MODELS); + // } else { + // ServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); + // svrgen.generate(D_MODELS); + // } break; } - case Helper: { + case SubCommand::Helper: { HelperGenerator helpergen(args.value(2)); helpergen.generate(D_HELPERS); break; } - case UserModel: { + case SubCommand::UserModel: { ModelGenerator modelgen(ModelGenerator::Sql, args.value(5), args.value(2), args.mid(3, 2)); modelgen.generate(D_MODELS, true); @@ -669,17 +729,23 @@ int main(int argc, char *argv[]) return 2; } - if (isVueEnabled()) { - VueServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); - svrgen.generate(D_MODELS); - } else { - ServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); - svrgen.generate(D_MODELS); + // Generates servie file of the specified template system + std::unique_ptr svrgen = createServiceGenerator(templateSystem, modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); + if (svrgen) { + svrgen->generate(D_MODELS); } + + // if (templateSystem == TemplateSystem::Vue) { + // VueServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); + // svrgen.generate(D_MODELS); + // } else { + // ServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); + // svrgen.generate(D_MODELS); + // } break; } - case SqlObject: { + case SubCommand::SqlObject: { SqlObjGenerator sqlgen(args.value(3), args.value(2)); QString path = sqlgen.generate(D_MODELS); @@ -689,7 +755,7 @@ int main(int argc, char *argv[]) break; } - case MongoScaffold: { + case SubCommand::MongoScaffold: { ModelGenerator modelgen(ModelGenerator::Mongo, args.value(2)); bool success = modelgen.generate(D_MODELS); @@ -699,36 +765,52 @@ int main(int argc, char *argv[]) return 2; } - if (isVueEnabled()) { - VueServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); - success &= svrgen.generate(D_MODELS); + // Generates view files of the specified template system + std::unique_ptr svrgen = createServiceGenerator(templateSystem, modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); + if (svrgen) { + svrgen->generate(D_MODELS); } else { - ServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); - success &= svrgen.generate(D_MODELS); + qCritical("Invalid template system specified"); + return 2; } + // if (templateSystem == TemplateSystem::Vue) { + // VueServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); + // success &= svrgen.generate(D_MODELS); + // } else { + // ServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); + // success &= svrgen.generate(D_MODELS); + // } + ControllerGenerator crtlgen(modelgen.model(), modelgen.fieldList(), modelgen.primaryKeyIndex(), modelgen.lockRevisionIndex()); success &= crtlgen.generate(D_CTRLS); // Generates view files of the specified template system - if (templateSystem == "otama") { - OtamaGenerator viewgen(modelgen.model(), modelgen.fieldList(), modelgen.primaryKeyIndex(), modelgen.autoValueIndex()); - viewgen.generate(D_VIEWS); - } else if (templateSystem == "erb") { - ErbGenerator viewgen(modelgen.model(), modelgen.fieldList(), modelgen.primaryKeyIndex(), modelgen.autoValueIndex()); - viewgen.generate(D_VIEWS); + std::unique_ptr viewgen = createViewGenerator(templateSystem, modelgen.model(), modelgen.fieldList(), modelgen.primaryKeyIndex(), modelgen.autoValueIndex()); + if (viewgen) { + viewgen->generate(D_VIEWS); } else { - qCritical("Invalid template system specified: %s", qUtf8Printable(templateSystem)); return 2; } + // if (templateSystem == "otama") { + // OtamaGenerator viewgen(modelgen.model(), modelgen.fieldList(), modelgen.primaryKeyIndex(), modelgen.autoValueIndex()); + // viewgen.generate(D_VIEWS); + // } else if (templateSystem == "erb") { + // ErbGenerator viewgen(modelgen.model(), modelgen.fieldList(), modelgen.primaryKeyIndex(), modelgen.autoValueIndex()); + // viewgen.generate(D_VIEWS); + // } else { + // qCritical("Invalid template system specified: %s", qUtf8Printable(templateSystem)); + // return 2; + // } + if (success) { printSuccessMessage(modelgen.model()); } break; } - case MongoModel: { + case SubCommand::MongoModel: { ModelGenerator modelgen(ModelGenerator::Mongo, args.value(2)); modelgen.generate(D_MODELS); @@ -738,17 +820,23 @@ int main(int argc, char *argv[]) return 2; } - if (isVueEnabled()) { - VueServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); - svrgen.generate(D_MODELS); - } else { - ServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); - svrgen.generate(D_MODELS); + // Generates servie file of the specified template system + std::unique_ptr svrgen = createServiceGenerator(templateSystem, modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); + if (svrgen) { + svrgen->generate(D_MODELS); } + + // if (templateSystem == TemplateSystem::Vue) { + // VueServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); + // svrgen.generate(D_MODELS); + // } else { + // ServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); + // svrgen.generate(D_MODELS); + // } break; } - case WebSocketEndpoint: { + case SubCommand::WebSocketEndpoint: { const QString appendpointfiles[] = {L("controllers/applicationendpoint.h"), L("controllers/applicationendpoint.cpp")}; @@ -767,7 +855,7 @@ int main(int argc, char *argv[]) break; } - case Api: { + case SubCommand::Api: { ModelGenerator modelgen(ModelGenerator::Sql, args.value(3), args.value(2)); modelgen.generate(D_MODELS); @@ -785,20 +873,20 @@ int main(int argc, char *argv[]) break; } - case Validator: { + case SubCommand::Validator: { ValidatorGenerator validgen(args.value(2)); validgen.generate(D_HELPERS); break; } - case Mailer: { + case SubCommand::Mailer: { MailerGenerator mailgen(args.value(2), args.mid(3)); mailgen.generate(D_CTRLS); copy(dataDirPath + "mail.erb", D_VIEWS + "mailer/mail.erb"); break; } - case Scaffold: { + case SubCommand::Scaffold: { ModelGenerator modelgen(ModelGenerator::Sql, args.value(3), args.value(2)); bool success = modelgen.generate(D_MODELS); @@ -815,42 +903,58 @@ int main(int argc, char *argv[]) ControllerGenerator crtlgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); success &= crtlgen.generate(D_CTRLS); - if (isVueEnabled()) { - VueServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); - svrgen.generate(D_MODELS); - - // Generates view files of the specified template system - if (templateSystem == "erb") { - VueErbGenerator viewgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.autoValueIndex()); - viewgen.generate(D_VIEWS); - } else { - qCritical("Invalid template system specified: %s", qUtf8Printable(templateSystem)); - return 2; - } + // Generates view files of the specified template system + std::unique_ptr svrgen = createServiceGenerator(templateSystem, modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); + if (svrgen) { + svrgen->generate(D_MODELS); } else { - ServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); - svrgen.generate(D_MODELS); - - // Generates view files of the specified template system - if (templateSystem == "otama") { - OtamaGenerator viewgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.autoValueIndex()); - viewgen.generate(D_VIEWS); - } else if (templateSystem == "erb") { - ErbGenerator viewgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.autoValueIndex()); - viewgen.generate(D_VIEWS); - } else { - qCritical("Invalid template system specified: %s", qUtf8Printable(templateSystem)); - return 2; - } + qCritical("Invalid template system specified"); + return 2; + } + + // Generates view files of the specified template system + std::unique_ptr viewgen = createViewGenerator(templateSystem, modelgen.model(), modelgen.fieldList(), modelgen.primaryKeyIndex(), modelgen.autoValueIndex()); + if (viewgen) { + viewgen->generate(D_VIEWS); + } else { + qCritical("Invalid template system specified"); + return 2; } + // // Generates view files of the specified template system + // if (templateSystem == TemplateSystem::Vue) { + // VueServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); + // svrgen.generate(D_MODELS); + + // VueErbGenerator viewgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.autoValueIndex()); + // viewgen.generate(D_VIEWS); + + // } else if (templateSystem == TemplateSystem::Vite_Vue) { + + // } else if (templateSystem == TemplateSystem::Otama) { + // ServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); + // svrgen.generate(D_MODELS); + + // OtamaGenerator viewgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.autoValueIndex()); + // viewgen.generate(D_VIEWS); + // } else if (templateSystem == TemplateSystem::Erb) { + // ServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); + // svrgen.generate(D_MODELS); + + // ErbGenerator viewgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.autoValueIndex()); + // viewgen.generate(D_VIEWS); + // } else { + // qCritical("Invalid template system specified"); + // return 2; + // } + if (success) { printSuccessMessage(modelgen.model()); } break; } - case Delete: { + case SubCommand::Delete: { // Removes files int ret = deleteScaffold(args.value(2)); if (ret) { diff --git a/tools/tspawn/otamagenerator.h b/tools/tspawn/otamagenerator.h index d23ee3974..473c03d64 100644 --- a/tools/tspawn/otamagenerator.h +++ b/tools/tspawn/otamagenerator.h @@ -1,14 +1,16 @@ #pragma once +#include "generator.h" #include #include #include #include -class OtamaGenerator { +class OtamaGenerator : public Generator { public: OtamaGenerator(const QString &view, const QList> &fields, int pkIdx, int autoValIdx); - bool generate(const QString &dstDir) const; + virtual ~OtamaGenerator() {} + bool generate(const QString &dstDir) const override; protected: QStringList generateViews(const QString &dstDir) const; diff --git a/tools/tspawn/servicegenerator.h b/tools/tspawn/servicegenerator.h index fab926329..1d4269319 100644 --- a/tools/tspawn/servicegenerator.h +++ b/tools/tspawn/servicegenerator.h @@ -1,15 +1,16 @@ #pragma once +#include "generator.h" #include #include #include #include -class ServiceGenerator { +class ServiceGenerator : public Generator { public: ServiceGenerator(const QString &service, const QList> &fields, int pkIdx, int lockRevIdx); ~ServiceGenerator() { } - bool generate(const QString &dstDir) const; + bool generate(const QString &dstDir) const override; private: virtual QString headerFileTemplate() const; diff --git a/tools/tspawn/tspawn.pro b/tools/tspawn/tspawn.pro index 3cded0403..ea1e86107 100644 --- a/tools/tspawn/tspawn.pro +++ b/tools/tspawn/tspawn.pro @@ -5,6 +5,8 @@ CONFIG += console CONFIG -= app_bundle QT += sql QT -= gui +MOC_DIR = .obj/ +OBJECTS_DIR = .obj/ # C++ Standards Support CONFIG += c++20 @@ -184,6 +186,10 @@ HEADERS += vueservicegenerator.h SOURCES += vueservicegenerator.cpp HEADERS += vueerbgenerator.h SOURCES += vueerbgenerator.cpp +HEADERS += vitevuegenerator.h +SOURCES += vitevuegenerator.cpp +HEADERS += vitevueservicegenerator.h +SOURCES += vitevueservicegenerator.cpp HEADERS += abstractobjgenerator.h SOURCES += abstractobjgenerator.cpp HEADERS += sqlobjgenerator.h @@ -210,3 +216,4 @@ HEADERS += apiservicegenerator.h SOURCES += apiservicegenerator.cpp HEADERS += util.h SOURCES += util.cpp +HEADERS += generator.h diff --git a/tools/tspawn/validatorgenerator.cpp b/tools/tspawn/validatorgenerator.cpp index 3e447948e..bd4ea0e96 100644 --- a/tools/tspawn/validatorgenerator.cpp +++ b/tools/tspawn/validatorgenerator.cpp @@ -10,26 +10,28 @@ #include "global.h" #include "projectfilegenerator.h" -constexpr auto VALIDATOR_HEADER_TEMPLATE = "#pragma once\n" - "#include \n" - "#include \n" - "\n\n" - "class T_HELPER_EXPORT %1Validator : public TFormValidator {\n" - "public:\n" - " %1Validator();\n" - "};\n" - "\n" - "Q_DECLARE_METATYPE(%1Validator)\n" - "\n"; - -constexpr auto VALIDATOR_IMPL_TEMPLATE = "#include \"%1validator.h\"\n" - "\n\n" - "%2Validator::%2Validator() : TFormValidator()\n" - "{\n" - " //Set the rules below\n" - " //setRule(\"xxxx\", Tf::MaxLength, 20);\n" - " // :\n" - "}\n"; +constexpr auto VALIDATOR_HEADER_TEMPLATE = + "#pragma once\n" + "#include \n" + "#include \n" + "\n\n" + "class T_HELPER_EXPORT %1Validator : public TFormValidator {\n" + "public:\n" + " %1Validator();\n" + "};\n" + "\n" + "Q_DECLARE_METATYPE(%1Validator)\n" + "\n"; + +constexpr auto VALIDATOR_IMPL_TEMPLATE = + "#include \"%1validator.h\"\n" + "\n\n" + "%2Validator::%2Validator() : TFormValidator()\n" + "{\n" + " //Set the rules below\n" + " //setRule(\"xxxx\", Tf::MaxLength, 20);\n" + " // :\n" + "}\n"; ValidatorGenerator::ValidatorGenerator(const QString &validator) diff --git a/tools/tspawn/vitevuegenerator.cpp b/tools/tspawn/vitevuegenerator.cpp new file mode 100644 index 000000000..adf041971 --- /dev/null +++ b/tools/tspawn/vitevuegenerator.cpp @@ -0,0 +1,382 @@ +/* Copyright (c) 2025, AOYAMA Kazuharu + * All rights reserved. + * + * This software may be used and distributed according to the terms of + * the New BSD License, which is incorporated herein by reference. + */ + +#include "vitevuegenerator.h" +#include "filewriter.h" +#include "util.h" +#include + + +constexpr auto INDEX_TEMPLATE = + "\n" + "\n" + "\n" + " \n" + " %ClassName%: index\n" + "<% if (true) { %>\n" + " \n" + "<% } else { %>\n" + " <%== viteScriptTag(\"main\", a(\"type\", \"module\")) %>\n" + "<% } %>\n" + " \">\n" + "\n" + "\n" + "
\n" + "\n" + "\n" + "\n"; + +constexpr auto CREATE_TEMPLATE = + "\n" + "\n" + "\n" + " \n" + " %ClassName%: create\n" + " \n" + " \">\n" + "\n" + "\n" + "
\n" + "\n" + "\n" + "\n"; + +constexpr auto SHOW_TEMPLATE = + "\n" + "\n" + "\n" + " \n" + " %ClassName%: show\n" + " \n" + "\n" + "\n" + "
\n" + "\n" + "\n" + "\n"; + +constexpr auto SAVE_TEMPLATE = + "\n" + "\n" + "\n" + " \n" + " %ClassName%: save\n" + " \n" + " \">\n" + "\n" + "\n" + "
\n" + "\n" + "\n" + "\n"; + +constexpr auto INDEX_VUE_TEMPLATE = + "\n" + "\n" + "\n" + "\n" + "\n"; + +constexpr auto SHOW_VUE_TEMPLATE = + "\n" + "\n" + "\n" + "\n" + "\n"; + +constexpr auto CREATE_VUE_TEMPLATE = + "\n" + "\n" + "\n" + "\n" + "\n"; + +constexpr auto SAVE_VUE_TEMPLATE = + "\n" + "\n" + "\n" + "\n" + "\n"; + + +const QStringList excludedColumn = { + "created_at", + "updated_at", + "modified_at", + "lock_revision", + "createdAt", + "updatedAt", + "modifiedAt", + "lockRevision", +}; + +namespace { + +const QStringList excludedDirName = { + "layouts", + "partial", + "direct", + "_src", + "mailer", +}; + +} + + + ViteVueGenerator::ViteVueGenerator(const QString &view, const QList> &fields, int pkIdx, int autoValIdx) : + _viewName(view), _fieldList(fields), _primaryKeyIndex(pkIdx), _autoValueIndex(autoValIdx) +{ +} + + +bool ViteVueGenerator::generate(const QString &dstDir) const +{ + QDir dir(dstDir + _viewName.toLower()); + QString varName = enumNameToVariableName(_viewName); + const QPair &pkFld = _fieldList[_primaryKeyIndex]; + QString pkVarName = fieldNameToVariableName(pkFld.first); + QString th, td, showitems, entryitems, edititems; + + // Generates view files + for (int i = 0; i < _fieldList.count(); ++i) { + const QPair &p = _fieldList[i]; + + QString icap = fieldNameToCaption(p.first); + QString ivar = fieldNameToVariableName(p.first); + + showitems += "
"; + showitems += icap; + showitems += "
{{ item."; + showitems += ivar; + showitems += " }}
\n"; + + if (!excludedColumn.contains(ivar, Qt::CaseInsensitive)) { + th += " "; + th += icap; + th += "\n"; + td += " {{ item."; + td += ivar; + td += " }}\n"; + + if (i != _autoValueIndex) { // case of not auto-value field + entryitems += "

\n \n

\n"; + } + edititems += "

\n

\n" @@ -65,7 +73,11 @@ constexpr auto SAVE_TEMPLATE = "\n" " \n" " %ClassName%: save\n" + "<% if (databaseEnvironment() == \"dev\") { %>\n" " \n" + "<% } else { %>\n" + " <%== viteScriptTag(\"src/main.js\", a(\"type\", \"module\")) %>\n" + "<% } %>\n" " \">\n" "\n" "\n" From 70a29cfb7f1e27d302f6d4531af654f0f34101c2 Mon Sep 17 00:00:00 2001 From: aoyama Date: Sat, 19 Apr 2025 22:10:39 +0900 Subject: [PATCH 04/15] fix compilation errors. --- src/test/mailmessage/main.cpp | 5 +++++ tools/tmake/test/tmaketest.cpp | 36 +++++++++++++++++----------------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/test/mailmessage/main.cpp b/src/test/mailmessage/main.cpp index 142deecf4..b6ec8a304 100644 --- a/src/test/mailmessage/main.cpp +++ b/src/test/mailmessage/main.cpp @@ -162,8 +162,13 @@ void TestMailMessage::dateTime_data() .arg(qAbs(offset) / 60, 2, 10, QLatin1Char('0')) .arg(qAbs(offset) % 60, 2, 10, QLatin1Char('0')); +#if QT_VERSION >= 0x060700 QTest::newRow("1") << QDateTime(QDate(2011,3,28), QTime(12,11,04), QTimeZone::LocalTime) << "Mon, 28 Mar 2011 12:11:04 " + offsetStr; QTest::newRow("2") << QDateTime(QDate(2014,3,31), QTime( 1, 0, 0), QTimeZone::LocalTime) << "Mon, 31 Mar 2014 01:00:00 " + offsetStr; +#else + QTest::newRow("1") << QDateTime(QDate(2011,3,28), QTime(12,11,04), Qt::LocalTime) << "Mon, 28 Mar 2011 12:11:04 " + offsetStr; + QTest::newRow("2") << QDateTime(QDate(2014,3,31), QTime( 1, 0, 0), Qt::LocalTime) << "Mon, 31 Mar 2014 01:00:00 " + offsetStr; +#endif QTest::newRow("3") << QDateTime(QDate(2011,3,28), QTime(12,11,04), QTimeZone::UTC) << "Mon, 28 Mar 2011 12:11:04 +0000"; QTest::newRow("4") << QDateTime(QDate(2014,3,31), QTime( 1, 0, 0), QTimeZone::UTC) << "Mon, 31 Mar 2014 01:00:00 +0000"; } diff --git a/tools/tmake/test/tmaketest.cpp b/tools/tmake/test/tmaketest.cpp index 2a6668c24..fb2918528 100644 --- a/tools/tmake/test/tmaketest.cpp +++ b/tools/tmake/test/tmaketest.cpp @@ -210,11 +210,11 @@ void TestTfpconverter::erbparse_data() QTest::newRow("7") << "Hello <% QString s(\"%>\"); %>" << " responsebody += QStringLiteral(\"Hello \");\n QString s(\"%>\");\n responsebody += QStringLiteral(\"\");\n"; QTest::newRow("8") << "Hello <%== vvv %>" - << " responsebody += QStringLiteral(\"Hello \");\n responsebody += QVariant(vvv).toString();\n responsebody += QStringLiteral(\"\");\n"; + << " responsebody += QStringLiteral(\"Hello \");\n echo(vvv);\n responsebody += QStringLiteral(\"\");\n"; QTest::newRow("9") << "Hello <%= vvv %> \n" - << " responsebody += QStringLiteral(\"Hello \");\n responsebody += THttpUtility::htmlEscape(vvv);\n responsebody += QStringLiteral(\" \\n\");\n"; + << " responsebody += QStringLiteral(\"Hello \");\n eh(vvv);\n responsebody += QStringLiteral(\" \\n\");\n"; QTest::newRow("10") << "Hello <%= vvv; -%> \n" - << " responsebody += QStringLiteral(\"Hello \");\n responsebody += THttpUtility::htmlEscape(vvv);\n responsebody += QStringLiteral(\"\");\n"; + << " responsebody += QStringLiteral(\"Hello \");\n eh(vvv);\n responsebody += QStringLiteral(\"\");\n"; QTest::newRow("11") << "Hello <% int i; -%> \r\n " << " responsebody += QStringLiteral(\"Hello \");\n int i;\n responsebody += QStringLiteral(\" \");\n"; QTest::newRow("12") << "Hello <% int i; %> \r\n" @@ -222,9 +222,9 @@ void TestTfpconverter::erbparse_data() QTest::newRow("13") << "Hello ... \r\n" << " responsebody += QStringLiteral(\"Hello ... \\r\\n\");\n"; QTest::newRow("14") << "Hello <%= vvv; +%> \n" - << " responsebody += QStringLiteral(\"Hello \");\n responsebody += THttpUtility::htmlEscape(vvv);\n responsebody += QStringLiteral(\" \\n\");\n"; + << " responsebody += QStringLiteral(\"Hello \");\n eh(vvv);\n responsebody += QStringLiteral(\" \\n\");\n"; QTest::newRow("15") << "Hello <%= vvv; +%>\r\n" - << " responsebody += QStringLiteral(\"Hello \");\n responsebody += THttpUtility::htmlEscape(vvv);\n responsebody += QStringLiteral(\"\\r\\n\");\n"; + << " responsebody += QStringLiteral(\"Hello \");\n eh(vvv);\n responsebody += QStringLiteral(\"\\r\\n\");\n"; QTest::newRow("16") << "Hello <% int i; +%> \r\n " << " responsebody += QStringLiteral(\"Hello \");\n int i;\n responsebody += QStringLiteral(\" \\r\\n \");\n"; @@ -238,16 +238,16 @@ void TestTfpconverter::erbparse_data() QTest::newRow("19") << "<%# comment. %|% 33 %>" << " responsebody += QStringLiteral(\"\");\n /* comment. */\n responsebody += QStringLiteral(\"\");\n"; QTest::newRow("20") << "<%= number %|% 33 %>" - << " responsebody += QStringLiteral(\"\");\n { QString ___s = QVariant(number).toString(); responsebody += (___s.isEmpty()) ? THttpUtility::htmlEscape(33) : THttpUtility::htmlEscape(___s); }\n responsebody += QStringLiteral(\"\");\n"; + << " responsebody += QStringLiteral(\"\");\n { QString ___s(fromValue(number)); if (___s.isEmpty()) { eh(33); } else { eh(number); }}\n responsebody += QStringLiteral(\"\");\n"; QTest::newRow("21") << "<%== number %|% 33 %>" - << " responsebody += QStringLiteral(\"\");\n { QString ___s = QVariant(number).toString(); responsebody += (___s.isEmpty()) ? QVariant(33).toString() : ___s; }\n responsebody += QStringLiteral(\"\");\n"; + << " responsebody += QStringLiteral(\"\");\n { QString ___s(fromValue(number)); if (___s.isEmpty()) { echo(33); } else { echo(number); }}\n responsebody += QStringLiteral(\"\");\n"; QTest::newRow("22") << "<%=$number %|% 33 %>" << " responsebody += QStringLiteral(\"\");\n tehex2(number, (33));\n responsebody += QStringLiteral(\"\");\n"; // Irregular pattern QTest::newRow("23") << "<%==$number %|% 33 -%>\t\n" << " responsebody += QStringLiteral(\"\");\n techoex2(number, (33));\n responsebody += QStringLiteral(\"\");\n"; QTest::newRow("24") << "<%== \" %|%\" %|% \"%|%\" -%> \t \n" - << " responsebody += QStringLiteral(\"\");\n { QString ___s = QVariant(\" %|%\").toString(); responsebody += (___s.isEmpty()) ? QVariant(\"%|%\").toString() : ___s; }\n responsebody += QStringLiteral(\"\");\n"; + << " responsebody += QStringLiteral(\"\");\n { QString ___s(fromValue(\" %|%\")); if (___s.isEmpty()) { echo(\"%|%\"); } else { echo(\" %|%\"); }}\n responsebody += QStringLiteral(\"\");\n"; QTest::newRow("25") << "" << " responsebody += QStringLiteral(\"\");\n"; @@ -292,13 +292,13 @@ void TestTfpconverter::erbparseStrong_data() QTest::newRow("7") << "Hello <% QString s(\"%>\"); %>" << " responsebody += QStringLiteral(\"Hello \");\n QString s(\"%>\");\n responsebody += QStringLiteral(\"\");\n"; QTest::newRow("8") << "Hello <%== vvv %>" - << " responsebody += QStringLiteral(\"Hello \");\n responsebody += QVariant(vvv).toString();\n responsebody += QStringLiteral(\"\");\n"; - QTest::newRow("9") << "Hello <%= vvv %> \n" - << " responsebody += QStringLiteral(\"Hello \");\n responsebody += THttpUtility::htmlEscape(vvv);\n responsebody += QStringLiteral(\"\\n\");\n"; + << " responsebody += QStringLiteral(\"Hello \");\n echo(vvv);\n responsebody += QStringLiteral(\"\");\n"; + QTest::newRow("9-1") << "Hello <%= vvv %> \n" + << " responsebody += QStringLiteral(\"Hello \");\n eh(vvv);\n responsebody += QStringLiteral(\"\\n\");\n"; QTest::newRow("9-2") << "Hello <%= vvv %> \n" - << " responsebody += QStringLiteral(\"Hello \");\n responsebody += THttpUtility::htmlEscape(vvv);\n responsebody += tr(\" \\n\");\n"; + << " responsebody += QStringLiteral(\"Hello \");\n eh(vvv);\n responsebody += tr(\" \\n\");\n"; QTest::newRow("10") << "Hello <%= vvv; -%> \n" - << " responsebody += QStringLiteral(\"Hello \");\n responsebody += THttpUtility::htmlEscape(vvv);\n responsebody += QStringLiteral(\"\");\n"; + << " responsebody += QStringLiteral(\"Hello \");\n eh(vvv);\n responsebody += QStringLiteral(\"\");\n"; QTest::newRow("11") << " Hello <% int i; -%> \r\n " << " responsebody += QStringLiteral(\"Hello \");\n int i;\n responsebody += QStringLiteral(\"\");\n"; QTest::newRow("12") << "Hello <% int i; %> \r\n" @@ -306,9 +306,9 @@ void TestTfpconverter::erbparseStrong_data() QTest::newRow("13") << "Hello ... \t\r\n\t" << " responsebody += QStringLiteral(\"Hello ...\\n\");\n"; QTest::newRow("14") << "Hello <%= vvv; +%> \n" - << " responsebody += QStringLiteral(\"Hello \");\n responsebody += THttpUtility::htmlEscape(vvv);\n responsebody += QStringLiteral(\"\\n\");\n"; + << " responsebody += QStringLiteral(\"Hello \");\n eh(vvv);\n responsebody += QStringLiteral(\"\\n\");\n"; QTest::newRow("15") << "Hello <%= vvv; +%>\t\r\n" - << " responsebody += QStringLiteral(\"Hello \");\n responsebody += THttpUtility::htmlEscape(vvv);\n responsebody += QStringLiteral(\"\");\n"; + << " responsebody += QStringLiteral(\"Hello \");\n eh(vvv);\n responsebody += QStringLiteral(\"\");\n"; QTest::newRow("16") << " \tHello <% int i; +%> \r\n " << " responsebody += QStringLiteral(\"Hello \");\n int i;\n responsebody += QStringLiteral(\"\\n\");\n"; @@ -322,16 +322,16 @@ void TestTfpconverter::erbparseStrong_data() QTest::newRow("19") << "<%# comment. %|% 33 %>" << " responsebody += QStringLiteral(\"\");\n /* comment. */\n responsebody += QStringLiteral(\"\");\n"; QTest::newRow("20") << "<%= number %|% 33 %>" - << " responsebody += QStringLiteral(\"\");\n { QString ___s = QVariant(number).toString(); responsebody += (___s.isEmpty()) ? THttpUtility::htmlEscape(33) : THttpUtility::htmlEscape(___s); }\n responsebody += QStringLiteral(\"\");\n"; + << " responsebody += QStringLiteral(\"\");\n { QString ___s(fromValue(number)); if (___s.isEmpty()) { eh(33); } else { eh(number); }}\n responsebody += QStringLiteral(\"\");\n"; QTest::newRow("21") << "<%== number %|% 33 %>" - << " responsebody += QStringLiteral(\"\");\n { QString ___s = QVariant(number).toString(); responsebody += (___s.isEmpty()) ? QVariant(33).toString() : ___s; }\n responsebody += QStringLiteral(\"\");\n"; + << " responsebody += QStringLiteral(\"\");\n { QString ___s(fromValue(number)); if (___s.isEmpty()) { echo(33); } else { echo(number); }}\n responsebody += QStringLiteral(\"\");\n"; QTest::newRow("22") << "<%=$number %|% 33 %>" << " responsebody += QStringLiteral(\"\");\n tehex2(number, (33));\n responsebody += QStringLiteral(\"\");\n"; // Irregular pattern QTest::newRow("23") << "<%==$number %|% 33 -%>\t\n" << " responsebody += QStringLiteral(\"\");\n techoex2(number, (33));\n responsebody += QStringLiteral(\"\");\n"; QTest::newRow("24") << "<%== \" %|%\" %|% \"%|%\" -%> \t \n" - << " responsebody += QStringLiteral(\"\");\n { QString ___s = QVariant(\" %|%\").toString(); responsebody += (___s.isEmpty()) ? QVariant(\"%|%\").toString() : ___s; }\n responsebody += QStringLiteral(\"\");\n"; + << " responsebody += QStringLiteral(\"\");\n { QString ___s(fromValue(\" %|%\")); if (___s.isEmpty()) { echo(\"%|%\"); } else { echo(\" %|%\"); }}\n responsebody += QStringLiteral(\"\");\n"; QTest::newRow("25") << "" << " responsebody += QStringLiteral(\"\");\n"; From e15a0b94fe96d8c88044898e1b8dabbcfde293b2 Mon Sep 17 00:00:00 2001 From: aoyama Date: Sat, 19 Apr 2025 22:27:30 +0900 Subject: [PATCH 05/15] fix compilation errors. --- src/test/mailmessage/main.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/test/mailmessage/main.cpp b/src/test/mailmessage/main.cpp index b6ec8a304..ce621b34b 100644 --- a/src/test/mailmessage/main.cpp +++ b/src/test/mailmessage/main.cpp @@ -154,8 +154,13 @@ void TestMailMessage::dateTime_data() QTest::addColumn("result"); // Timezone +#if QT_VERSION >= 0x060700 uint utc = QDateTime(QDate(2000,1,1), QTime(0,0,0), QTimeZone::UTC).toSecsSinceEpoch(); uint local = QDateTime(QDate(2000,1,1), QTime(0,0,0), QTimeZone::LocalTime).toSecsSinceEpoch(); +#else + uint utc = QDateTime(QDate(2000,1,1), QTime(0,0,0), Qt::UTC).toSecsSinceEpoch(); + uint local = QDateTime(QDate(2000,1,1), QTime(0,0,0), Qt::LocalTime).toSecsSinceEpoch(); +#endif int offset = (utc - local) / 60; QString offsetStr = QString("%1%2%3") .arg(offset > 0 ? '+' : '-') @@ -169,8 +174,8 @@ void TestMailMessage::dateTime_data() QTest::newRow("1") << QDateTime(QDate(2011,3,28), QTime(12,11,04), Qt::LocalTime) << "Mon, 28 Mar 2011 12:11:04 " + offsetStr; QTest::newRow("2") << QDateTime(QDate(2014,3,31), QTime( 1, 0, 0), Qt::LocalTime) << "Mon, 31 Mar 2014 01:00:00 " + offsetStr; #endif - QTest::newRow("3") << QDateTime(QDate(2011,3,28), QTime(12,11,04), QTimeZone::UTC) << "Mon, 28 Mar 2011 12:11:04 +0000"; - QTest::newRow("4") << QDateTime(QDate(2014,3,31), QTime( 1, 0, 0), QTimeZone::UTC) << "Mon, 31 Mar 2014 01:00:00 +0000"; + QTest::newRow("3") << QDateTime(QDate(2011,3,28), QTime(12,11,04), QTimeZone::UTC) << "Mon, 28 Mar 2011 12:11:04 +0000"; + QTest::newRow("4") << QDateTime(QDate(2014,3,31), QTime( 1, 0, 0), QTimeZone::UTC) << "Mon, 31 Mar 2014 01:00:00 +0000"; } void TestMailMessage::dateTime() From 657b9bd5385595c421a3369c72e371abb6f86236 Mon Sep 17 00:00:00 2001 From: aoyama Date: Sat, 19 Apr 2025 22:44:52 +0900 Subject: [PATCH 06/15] fix compilation errors. --- src/test/mailmessage/main.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/test/mailmessage/main.cpp b/src/test/mailmessage/main.cpp index ce621b34b..742727c66 100644 --- a/src/test/mailmessage/main.cpp +++ b/src/test/mailmessage/main.cpp @@ -170,12 +170,15 @@ void TestMailMessage::dateTime_data() #if QT_VERSION >= 0x060700 QTest::newRow("1") << QDateTime(QDate(2011,3,28), QTime(12,11,04), QTimeZone::LocalTime) << "Mon, 28 Mar 2011 12:11:04 " + offsetStr; QTest::newRow("2") << QDateTime(QDate(2014,3,31), QTime( 1, 0, 0), QTimeZone::LocalTime) << "Mon, 31 Mar 2014 01:00:00 " + offsetStr; + QTest::newRow("3") << QDateTime(QDate(2011,3,28), QTime(12,11,04), QTimeZone::UTC) << "Mon, 28 Mar 2011 12:11:04 +0000"; + QTest::newRow("4") << QDateTime(QDate(2014,3,31), QTime( 1, 0, 0), QTimeZone::UTC) << "Mon, 31 Mar 2014 01:00:00 +0000"; #else QTest::newRow("1") << QDateTime(QDate(2011,3,28), QTime(12,11,04), Qt::LocalTime) << "Mon, 28 Mar 2011 12:11:04 " + offsetStr; QTest::newRow("2") << QDateTime(QDate(2014,3,31), QTime( 1, 0, 0), Qt::LocalTime) << "Mon, 31 Mar 2014 01:00:00 " + offsetStr; + QTest::newRow("3") << QDateTime(QDate(2011,3,28), QTime(12,11,04), Qt::UTC) << "Mon, 28 Mar 2011 12:11:04 +0000"; + QTest::newRow("4") << QDateTime(QDate(2014,3,31), QTime( 1, 0, 0), Qt::UTC) << "Mon, 31 Mar 2014 01:00:00 +0000"; + #endif - QTest::newRow("3") << QDateTime(QDate(2011,3,28), QTime(12,11,04), QTimeZone::UTC) << "Mon, 28 Mar 2011 12:11:04 +0000"; - QTest::newRow("4") << QDateTime(QDate(2014,3,31), QTime( 1, 0, 0), QTimeZone::UTC) << "Mon, 31 Mar 2014 01:00:00 +0000"; } void TestMailMessage::dateTime() From 8741c0ee0eaa8b4b179a6d09ff47df7c8d4bae81 Mon Sep 17 00:00:00 2001 From: aoyama Date: Sat, 19 Apr 2025 23:25:01 +0900 Subject: [PATCH 07/15] updated version strings. --- configure.bat | 2 +- installer/create_installer.bat | 2 +- installer/treefrog-setup/treefrog-setup/AssemblyInfo.cpp | 2 +- src/test/viewhelper/viewhelper.cpp | 2 +- src/tglobal.h | 6 +++--- tfbase.pri | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/configure.bat b/configure.bat index 61503ad6c..f8abd05d2 100644 --- a/configure.bat +++ b/configure.bat @@ -1,7 +1,7 @@ @echo off @setlocal -set VERSION=2.10.1 +set VERSION=2.11.0 set TFDIR=C:\TreeFrog\%VERSION% set MONBOC_VERSION=1.26.2 set LZ4_VERSION=1.9.4 diff --git a/installer/create_installer.bat b/installer/create_installer.bat index 801f1fe20..a06592ebd 100644 --- a/installer/create_installer.bat +++ b/installer/create_installer.bat @@ -7,7 +7,7 @@ :: 10sځA28sځA39sڂҏW -set VERSION=2.10.1 +set VERSION=2.11.0 set QTBASE=C:\Qt set TFDIR=C:\TreeFrog\%VERSION% diff --git a/installer/treefrog-setup/treefrog-setup/AssemblyInfo.cpp b/installer/treefrog-setup/treefrog-setup/AssemblyInfo.cpp index 5e9021252..1010c389c 100644 --- a/installer/treefrog-setup/treefrog-setup/AssemblyInfo.cpp +++ b/installer/treefrog-setup/treefrog-setup/AssemblyInfo.cpp @@ -31,7 +31,7 @@ using namespace System::Security::Permissions; // ׂĂ̒lw肷邩Â悤 '*' gărWуrhԍ // lɂ邱Ƃł܂: -[assembly:AssemblyVersionAttribute("2.10.1")]; +[assembly:AssemblyVersionAttribute("2.11.0")]; [assembly:ComVisible(false)]; diff --git a/src/test/viewhelper/viewhelper.cpp b/src/test/viewhelper/viewhelper.cpp index 9c034a0e9..e615a37f7 100644 --- a/src/test/viewhelper/viewhelper.cpp +++ b/src/test/viewhelper/viewhelper.cpp @@ -153,7 +153,7 @@ void TestViewHelper::stylesheetTag() attr.append("onclick", "return 0;"); attr.append("style", "none"); QString actual = view.styleSheetTag("hoge.png", attr); - QString result = ""; + QString result = ""; QCOMPARE(actual, result); } diff --git a/src/tglobal.h b/src/tglobal.h index ddbea826f..1dc8c010d 100644 --- a/src/tglobal.h +++ b/src/tglobal.h @@ -1,7 +1,7 @@ #pragma once -constexpr auto TF_VERSION_STR = "2.10.1"; -constexpr auto TF_VERSION_NUMBER = 0x020a01; -constexpr auto TF_SRC_REVISION = 3001; +constexpr auto TF_VERSION_STR = "2.11.0"; +constexpr auto TF_VERSION_NUMBER = 0x020b00; +constexpr auto TF_SRC_REVISION = 3013; #include #include diff --git a/tfbase.pri b/tfbase.pri index 67384b496..52b55c8fb 100644 --- a/tfbase.pri +++ b/tfbase.pri @@ -1,4 +1,4 @@ TF_VER_MAJ=2 -TF_VER_MIN=10 -TF_VER_PAT=1 +TF_VER_MIN=11 +TF_VER_PAT=0 TF_VERSION=$${TF_VER_MAJ}.$${TF_VER_MIN}.$${TF_VER_PAT} From b085ee8bb040b7d2098938d6d3755033378ac2c4 Mon Sep 17 00:00:00 2001 From: aoyama Date: Sun, 20 Apr 2025 00:22:01 +0900 Subject: [PATCH 08/15] fix testcase errors on macOS. --- src/test/sharedmemoryhash/sharedmemoryhash.cpp | 2 +- src/tsharedmemorykvs.cpp | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/test/sharedmemoryhash/sharedmemoryhash.cpp b/src/test/sharedmemoryhash/sharedmemoryhash.cpp index 1fc146df3..c8b5837cd 100644 --- a/src/test/sharedmemoryhash/sharedmemoryhash.cpp +++ b/src/test/sharedmemoryhash/sharedmemoryhash.cpp @@ -196,7 +196,7 @@ void TestSharedMemoryHash::testAlloc4() smhash.set(key, "hoge", seconds); QVERIFY(!smhash.get(key).isEmpty()); // not empty qDebug() << "smhash.get(" << key << ") =" << smhash.get(key); - Tf::msleep(seconds * 1000 + 1); + Tf::msleep(seconds * 1000 + 20); auto val = smhash.get(key); QCOMPARE(val, QByteArray()); // timeout, empty } diff --git a/src/tsharedmemorykvs.cpp b/src/tsharedmemorykvs.cpp index f7106e637..c9d22249b 100644 --- a/src/tsharedmemorykvs.cpp +++ b/src/tsharedmemorykvs.cpp @@ -83,16 +83,13 @@ TSharedMemoryKvs::~TSharedMemoryKvs() */ bool TSharedMemoryKvs::initialize(const QString &name, const QString &options) { - hash_header_t hashheader; - TSharedMemoryKvsDriver::initialize(name, options); TSharedMemoryKvsDriver driver; - driver.open(name, QString(), QString(), QString(), 0, options); - hash_header_t *header = (hash_header_t *)driver.origin(); - void *ptr = driver.malloc(sizeof(hashheader)); - Q_ASSERT(ptr == header); - std::memcpy(header, &hashheader, sizeof(hashheader)); + driver.open(name, QString(), QString(), QString(), 0, options); + void *ptr = driver.malloc(sizeof(hash_header_t)); + hash_header_t *header = new (ptr) hash_header_t{}; // Initialize with the default constructor + Q_ASSERT(header == (hash_header_t *)driver.origin()); ptr = driver.calloc(header->tableSize, sizeof(uintptr_t)); header->setHashg(ptr); Q_ASSERT(ptr); From 5bd906107fafa7a41fd85de9dcbeea2596ed07c9 Mon Sep 17 00:00:00 2001 From: aoyama Date: Sun, 20 Apr 2025 22:38:15 +0900 Subject: [PATCH 09/15] add a testcase. --- src/test/sharedmemory/sharedmemory | Bin 0 -> 47064 bytes src/test/sharedmemory/sharedmemory.cpp | 110 +++++++++++++++++++++++++ src/test/sharedmemory/sharedmemory.pro | 3 + src/test/test.pro | 2 +- 4 files changed, 114 insertions(+), 1 deletion(-) create mode 100755 src/test/sharedmemory/sharedmemory create mode 100644 src/test/sharedmemory/sharedmemory.cpp create mode 100644 src/test/sharedmemory/sharedmemory.pro diff --git a/src/test/sharedmemory/sharedmemory b/src/test/sharedmemory/sharedmemory new file mode 100755 index 0000000000000000000000000000000000000000..335fec121a455cb90b336f28b8a6e5f19a3d1589 GIT binary patch literal 47064 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 u0=%T% +#include "tsharedmemory.h" +#include "tglobal.h" +#include + + +namespace { +TSharedMemory sharedMomory("testshared.shm"); +} + + +class TestSharedMemory : public QObject +{ + Q_OBJECT +private slots: + void initTestCase(); + void cleanupTestCase(); + + void attach(); + void test1(); + void test2_data(); + void test2(); +}; + + +static QByteArray randomString(int length) +{ + constexpr auto ch = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-+/^=_[]@:;!#$%()~? \t\n"; + QByteArray ret; + int max = (int)strlen(ch) - 1; + + for (int i = 0; i < length; ++i) { + ret += ch[Tf::random(max)]; + } + return ret; +} + +void TestSharedMemory::initTestCase() +{ + sharedMomory.create(100 * 1024 * 1024); +} + +void TestSharedMemory::cleanupTestCase() +{ + sharedMomory.unlink(); +} + +void TestSharedMemory::attach() +{ + bool res = sharedMomory.attach(); + Q_ASSERT(res); + Tf::msleep(1000); + res = sharedMomory.detach(); + Q_ASSERT(res); + Tf::msleep(1000); + res = sharedMomory.attach(); + Q_ASSERT(res); + res = sharedMomory.detach(); + Q_ASSERT(res); +} + +void TestSharedMemory::test1() +{ + sharedMomory.attach(); + QVERIFY(sharedMomory.size() == 100 * 1024 * 1024); + + // Lock and unlock + auto ptr = sharedMomory.data(); + QVERIFY(ptr != nullptr); + bool res = sharedMomory.lockForRead(); + Q_ASSERT(res); + res = sharedMomory.lockForWrite(); + Q_ASSERT(!res); + res = sharedMomory.unlock(); + Q_ASSERT(res); + res = sharedMomory.lockForWrite(); + Q_ASSERT(res); + res = sharedMomory.unlock(); + Q_ASSERT(res); + QVERIFY(ptr == sharedMomory.data()); +} + +void TestSharedMemory::test2_data() +{ + QTest::addColumn("string"); + + QTest::newRow("1") << QUuid::createUuid().toByteArray(); + QTest::newRow("2") << QUuid::createUuid().toByteArray(); + QTest::newRow("3") << randomString(128); + QTest::newRow("4") << randomString(256); + QTest::newRow("5") << randomString(1024); +} + +void TestSharedMemory::test2() +{ + QFETCH(QByteArray, string); + + bool res = sharedMomory.attach(); + Q_ASSERT(res); + auto ptr = sharedMomory.data(); + res = sharedMomory.lockForWrite(); + Q_ASSERT(res); + std::memcpy(ptr, string.data(), string.length()); + sharedMomory.unlock(); + res = sharedMomory.detach(); + Q_ASSERT(res); +} + +TF_TEST_MAIN(TestSharedMemory) +#include "sharedmemory.moc" diff --git a/src/test/sharedmemory/sharedmemory.pro b/src/test/sharedmemory/sharedmemory.pro new file mode 100644 index 000000000..b644dfd48 --- /dev/null +++ b/src/test/sharedmemory/sharedmemory.pro @@ -0,0 +1,3 @@ +include(../test.pri) +TARGET = sharedmemory +SOURCES = sharedmemory.cpp diff --git a/src/test/test.pro b/src/test/test.pro index 992efc95a..a481a0f37 100644 --- a/src/test/test.pro +++ b/src/test/test.pro @@ -5,7 +5,7 @@ SUBDIRS += mailmessage multipartformdata smtpmailer viewhelper paginator SUBDIRS += fieldnametovariablename rand urlrouter urlrouter2 SUBDIRS += buildtest stack queue forlist SUBDIRS += jscontext compression sqlitedb url malloc -SUBDIRS += sharedmemoryhash sharedmemorymutex +SUBDIRS += sharedmemory sharedmemoryhash sharedmemorymutex unix { SUBDIRS += redis memcached } From 271c756007393a121fcef0de08366ecd7ed3aff1 Mon Sep 17 00:00:00 2001 From: aoyama Date: Mon, 21 Apr 2025 17:10:03 +0900 Subject: [PATCH 10/15] updated --- src/corelib.pro | 2 + src/tsharedmemory.h | 13 +++++ src/tsharedmemory_linux.cpp | 82 +++++++++++++++++++++++++++++ src/tsharedmemory_macx.cpp | 81 +++++++++++++++++++++++++++++ src/tsharedmemory_qt.cpp | 4 ++ src/tsharedmemory_unix.cpp | 100 ++---------------------------------- 6 files changed, 186 insertions(+), 96 deletions(-) create mode 100644 src/tsharedmemory_linux.cpp create mode 100644 src/tsharedmemory_macx.cpp diff --git a/src/corelib.pro b/src/corelib.pro index 076b4dc32..a0b93c2fc 100644 --- a/src/corelib.pro +++ b/src/corelib.pro @@ -412,6 +412,7 @@ linux-* { SOURCES += tthreadapplicationserver_linux.cpp SOURCES += tredisdriver_linux.cpp SOURCES += tmemcacheddriver_linux.cpp + SOURCES += tsharedmemory_linux.cpp } # For Mac @@ -420,6 +421,7 @@ macx { SOURCES += tthreadapplicationserver_qt.cpp SOURCES += tredisdriver_qt.cpp SOURCES += tmemcacheddriver_qt.cpp + SOURCES += tsharedmemory_macx.cpp } # For UNIX diff --git a/src/tsharedmemory.h b/src/tsharedmemory.h index c5545dab4..9b9c3383a 100644 --- a/src/tsharedmemory.h +++ b/src/tsharedmemory.h @@ -3,6 +3,10 @@ #ifdef Q_OS_WIN #include #endif +#ifdef Q_OS_LINUX +#include +#endif + class T_CORE_EXPORT TSharedMemory #ifdef Q_OS_WIN @@ -27,6 +31,15 @@ class T_CORE_EXPORT TSharedMemory bool unlock(); private: + struct header_t { +#ifdef Q_OS_LINUX + pthread_rwlock_t rwlock; + uint lockcounter {0}; +#endif + }; + + void initRwlock(header_t *header) const; + #ifndef Q_OS_WIN QString _name; size_t _size {0}; diff --git a/src/tsharedmemory_linux.cpp b/src/tsharedmemory_linux.cpp new file mode 100644 index 000000000..c4dcce0b5 --- /dev/null +++ b/src/tsharedmemory_linux.cpp @@ -0,0 +1,82 @@ +/* Copyright (c) 2022, AOYAMA Kazuharu + * All rights reserved. + * + * This software may be used and distributed according to the terms of + * the New BSD License, which is incorporated herein by reference. + */ + +#include "tsharedmemory.h" +#include + + +void TSharedMemory::initRwlock(header_t *header) const +{ + pthread_rwlockattr_t attr; + + int res = pthread_rwlockattr_init(&attr); + Q_ASSERT(!res); + res = pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); // Linux only + Q_ASSERT(!res); + res = pthread_rwlock_init(&header->rwlock, &attr); + Q_ASSERT(!res); +} + + +bool TSharedMemory::lockForRead() +{ + struct timespec timeout; + header_t *header = (header_t *)_ptr; + + while (pthread_rwlock_tryrdlock(&header->rwlock) == EBUSY) { + uint cnt = header->lockcounter; + timespec_get(&timeout, TIME_UTC); + timeout.tv_sec += 1; // 1sec + + int res = pthread_rwlock_timedrdlock(&header->rwlock, &timeout); + if (!res) { + // success + break; + } else { + if (res == ETIMEDOUT && header->lockcounter == cnt) { + // resets rwlock object + initRwlock(header); + } + } + } + header->lockcounter++; + return true; +} + + +bool TSharedMemory::lockForWrite() +{ + struct timespec timeout; + header_t *header = (header_t *)_ptr; + + while (pthread_rwlock_trywrlock(&header->rwlock) == EBUSY) { + uint cnt = header->lockcounter; + timespec_get(&timeout, TIME_UTC); + timeout.tv_sec += 1; // 1sec + + int res = pthread_rwlock_timedwrlock(&header->rwlock, &timeout); + if (!res) { + // success + break; + } else { + if (res == ETIMEDOUT && header->lockcounter == cnt) { + // resets rwlock object + initRwlock(header); + } + } + } + header->lockcounter++; + return true; +} + + +bool TSharedMemory::unlock() +{ + header_t *header = (header_t *)_ptr; + pthread_rwlock_unlock(&header->rwlock); + return true; +} diff --git a/src/tsharedmemory_macx.cpp b/src/tsharedmemory_macx.cpp new file mode 100644 index 000000000..8266678c2 --- /dev/null +++ b/src/tsharedmemory_macx.cpp @@ -0,0 +1,81 @@ +/* Copyright (c) 2025, AOYAMA Kazuharu + * All rights reserved. + * + * This software may be used and distributed according to the terms of + * the New BSD License, which is incorporated herein by reference. + */ + +#include "tsharedmemory.h" +#include +#include // O_CREAT, O_EXCL +#include // mode constants +#include + + +void TSharedMemory::initRwlock(header_t *) const +{ + const QByteArray SEM_NAME = _name + "_global_lock"; + + sem_t* sem = sem_open(SEM_NAME.data(), O_CREAT | O_EXCL, 0644, 1); + if (sem == SEM_FAILED) { + if (errno == EEXIST) { + return true; + } else { + tSystemError("sem_open (init) failed: {}", strerror(errno)); + return false; + } + } + + tSystemDebug("Semaphore initialized"); + sem_close(sem); + return true; +} + + +bool TSharedMemory::lockForRead() +{ + const QByteArray SEM_NAME = _name + "_global_lock"; + + sem_t* sem = sem_open(SEM_NAME.data(), 0); + if (sem == SEM_FAILED) { + tSystemError("sem_open (lock) failed: {}", strerror(errno)); + return false; + } + + if (sem_wait(sem) < 0) { + tSystemError("sem_wait failed: {}", strerror(errno)); + sem_close(sem); + return false; + } + + sem_close(sem); + return true; +} + + +bool TSharedMemory::lockForWrite() +{ + // Same as lockForRead() + return lockForRead(); +} + + +bool TSharedMemory::unlock() +{ + const QByteArray SEM_NAME = _name + "_global_lock"; + + sem_t* sem = sem_open(SEM_NAME.data(), 0); // 既存を開く + if (sem == SEM_FAILED) { + tSystemError("sem_open (unlock) failed: {}", strerror(errno)); + return false; + } + + if (sem_post(sem) < 0) { + tSystemError("sem_post failed: {}", strerror(errno)); + sem_close(sem); + return false; + } + + sem_close(sem); + return true; +} diff --git a/src/tsharedmemory_qt.cpp b/src/tsharedmemory_qt.cpp index 81f21bbaa..7e5c728c6 100644 --- a/src/tsharedmemory_qt.cpp +++ b/src/tsharedmemory_qt.cpp @@ -103,3 +103,7 @@ bool TSharedMemory::unlock() { return QSharedMemory::unlock(); } + + +void TSharedMemory::initRwlock(header_t *) const +{} diff --git a/src/tsharedmemory_unix.cpp b/src/tsharedmemory_unix.cpp index 55c0a4a54..d0f0237ba 100644 --- a/src/tsharedmemory_unix.cpp +++ b/src/tsharedmemory_unix.cpp @@ -12,27 +12,8 @@ #include #include #include -#include #include -struct header_t { - pthread_rwlock_t rwlock; - uint lockcounter {0}; -}; - - -static void rwlock_init(pthread_rwlock_t *rwlock) -{ - pthread_rwlockattr_t attr; - - int res = pthread_rwlockattr_init(&attr); - Q_ASSERT(!res); - res = pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); - Q_ASSERT(!res); - res = pthread_rwlock_init(rwlock, &attr); - Q_ASSERT(!res); -} - TSharedMemory::TSharedMemory(const QString &name) : _name(name) @@ -52,17 +33,12 @@ TSharedMemory::~TSharedMemory() bool TSharedMemory::create(size_t size) { - static const header_t INIT_HEADER = []() { - static header_t header; - rwlock_init(&header.rwlock); - return header; - }(); - if (_ptr || size == 0 || _name.isEmpty()) { return false; } struct stat st; + header_t *header = nullptr; // Creates shared memory _fd = shm_open(qUtf8Printable(_name), O_CREAT | O_RDWR | O_CLOEXEC, S_IRUSR | S_IWUSR); @@ -90,7 +66,9 @@ bool TSharedMemory::create(size_t size) goto error; } - std::memcpy(_ptr, &INIT_HEADER, sizeof(INIT_HEADER)); + header = new (_ptr) header_t{}; + initRwlock(header); + _size = size; tSystemDebug("SharedMemory created. name:{} size:{}", qUtf8Printable(_name), (qulonglong)_size); return true; @@ -202,73 +180,3 @@ size_t TSharedMemory::size() const { return _size; } - - -bool TSharedMemory::lockForRead() -{ -#ifdef Q_OS_LINUX - struct timespec timeout; - header_t *header = (header_t *)_ptr; - - while (pthread_rwlock_tryrdlock(&header->rwlock) == EBUSY) { - uint cnt = header->lockcounter; - timespec_get(&timeout, TIME_UTC); - timeout.tv_sec += 1; // 1sec - - int res = pthread_rwlock_timedrdlock(&header->rwlock, &timeout); - if (!res) { - // success - break; - } else { - if (res == ETIMEDOUT && header->lockcounter == cnt) { - // resets rwlock object - rwlock_init(&header->rwlock); - } - } - } - header->lockcounter++; - return true; -#else - header_t *header = (header_t *)_ptr; - return pthread_rwlock_rdlock(&header->rwlock) == 0; -#endif -} - - -bool TSharedMemory::lockForWrite() -{ -#ifdef Q_OS_LINUX - struct timespec timeout; - header_t *header = (header_t *)_ptr; - - while (pthread_rwlock_trywrlock(&header->rwlock) == EBUSY) { - uint cnt = header->lockcounter; - timespec_get(&timeout, TIME_UTC); - timeout.tv_sec += 1; // 1sec - - int res = pthread_rwlock_timedwrlock(&header->rwlock, &timeout); - if (!res) { - // success - break; - } else { - if (res == ETIMEDOUT && header->lockcounter == cnt) { - // resets rwlock object - rwlock_init(&header->rwlock); - } - } - } - header->lockcounter++; - return true; -#else - header_t *header = (header_t *)_ptr; - return pthread_rwlock_wrlock(&header->rwlock) == 0; -#endif -} - - -bool TSharedMemory::unlock() -{ - header_t *header = (header_t *)_ptr; - pthread_rwlock_unlock(&header->rwlock); - return true; -} From 0e81f5b486e8221bb2d33815af3909f48241510b Mon Sep 17 00:00:00 2001 From: AOYAMA Kazuharu Date: Tue, 22 Apr 2025 10:26:11 +0900 Subject: [PATCH 11/15] bugfixes of TSharedMemory on macos. --- src/test/sharedmemory/sharedmemory | Bin 47064 -> 79824 bytes src/test/sharedmemory/sharedmemory.cpp | 9 +++ src/tsharedmemory.h | 6 +- src/tsharedmemory_linux.cpp | 9 +++ src/tsharedmemory_macx.cpp | 81 ++++++++++++++++++------- src/tsharedmemory_qt.cpp | 8 ++- src/tsharedmemory_unix.cpp | 2 + 7 files changed, 91 insertions(+), 24 deletions(-) diff --git a/src/test/sharedmemory/sharedmemory b/src/test/sharedmemory/sharedmemory index 335fec121a455cb90b336f28b8a6e5f19a3d1589..a1bc2e4fc9ca613d059e477595f9e165805e37e3 100755 GIT binary patch literal 79824 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 literal 47064 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 u0=%T%rwlock); + } +} + + bool TSharedMemory::lockForRead() { struct timespec timeout; @@ -65,6 +73,7 @@ bool TSharedMemory::lockForWrite() } else { if (res == ETIMEDOUT && header->lockcounter == cnt) { // resets rwlock object + releaseRwlock(header); initRwlock(header); } } diff --git a/src/tsharedmemory_macx.cpp b/src/tsharedmemory_macx.cpp index 8266678c2..1564b8b12 100644 --- a/src/tsharedmemory_macx.cpp +++ b/src/tsharedmemory_macx.cpp @@ -6,22 +6,29 @@ */ #include "tsharedmemory.h" +#include #include #include // O_CREAT, O_EXCL -#include // mode constants +#include +#include #include -void TSharedMemory::initRwlock(header_t *) const +static inline QByteArray semaphoreName(const QString &name) { - const QByteArray SEM_NAME = _name + "_global_lock"; + return (name.startsWith("/") ? "" : "/") + name.toLatin1() + "_global_lock"; +} + - sem_t* sem = sem_open(SEM_NAME.data(), O_CREAT | O_EXCL, 0644, 1); +bool TSharedMemory::initRwlock(header_t *) const +{ + sem_t *sem = sem_open(semaphoreName(_name).data(), O_CREAT | O_EXCL, 0644, 1); if (sem == SEM_FAILED) { if (errno == EEXIST) { + tSystemError("sem_open semaphore already exists: {}", semaphoreName(_name)); return true; } else { - tSystemError("sem_open (init) failed: {}", strerror(errno)); + tSystemError("sem_open (init) failed: {}", (const char*)strerror(errno)); return false; } } @@ -32,24 +39,58 @@ void TSharedMemory::initRwlock(header_t *) const } +void TSharedMemory::releaseRwlock(header_t *) const +{ + sem_unlink(semaphoreName(_name).data()); +} + + bool TSharedMemory::lockForRead() { - const QByteArray SEM_NAME = _name + "_global_lock"; + // Use semaphore as a lock mechanism between processes. + // PTHREAD_PROCESS_SHARED attribute is not supported on macos. + + sem_t *sem = SEM_FAILED; + header_t *header = (header_t *)_ptr; + uint cnt = header->lockcounter; + + auto sem_timedwait = [&](int msecs) { + sem = sem_open(semaphoreName(_name).data(), 0); + if (sem == SEM_FAILED) { + tSystemError("sem_open (lock) failed: {}", (const char*)strerror(errno)); + return -1; + } - sem_t* sem = sem_open(SEM_NAME.data(), 0); - if (sem == SEM_FAILED) { - tSystemError("sem_open (lock) failed: {}", strerror(errno)); - return false; + auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(msecs); + while (std::chrono::steady_clock::now() < deadline) { + if (sem_trywait(sem) < 0) { + if (errno == EAGAIN) { + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + continue; + } else { + return -1; // error + } + } else { + header->lockcounter++; + return 0; // lock success + } + } + tSystemError("sem_wait (lock) timed out: {}", semaphoreName(_name)); + return 1; // timeout + }; + + int res; + while ((res = sem_timedwait(1000)) == 1) { + if (header->lockcounter == cnt) { // timeout and same counter + releaseRwlock(header); + initRwlock(header); + } } - if (sem_wait(sem) < 0) { - tSystemError("sem_wait failed: {}", strerror(errno)); + if (sem != SEM_FAILED) { sem_close(sem); - return false; } - - sem_close(sem); - return true; + return !res; } @@ -62,16 +103,14 @@ bool TSharedMemory::lockForWrite() bool TSharedMemory::unlock() { - const QByteArray SEM_NAME = _name + "_global_lock"; - - sem_t* sem = sem_open(SEM_NAME.data(), 0); // 既存を開く + sem_t *sem = sem_open(semaphoreName(_name).data(), 0); if (sem == SEM_FAILED) { - tSystemError("sem_open (unlock) failed: {}", strerror(errno)); + tSystemError("sem_open (unlock) failed: {}", (const char*)strerror(errno)); return false; } if (sem_post(sem) < 0) { - tSystemError("sem_post failed: {}", strerror(errno)); + tSystemError("sem_post failed: {}", (const char*)strerror(errno)); sem_close(sem); return false; } diff --git a/src/tsharedmemory_qt.cpp b/src/tsharedmemory_qt.cpp index 7e5c728c6..ca990f55c 100644 --- a/src/tsharedmemory_qt.cpp +++ b/src/tsharedmemory_qt.cpp @@ -105,5 +105,11 @@ bool TSharedMemory::unlock() } -void TSharedMemory::initRwlock(header_t *) const +bool TSharedMemory::initRwlock(header_t *) const +{ + return true; +} + + +void TSharedMemory::releaseRwlock(header_t *) const {} diff --git a/src/tsharedmemory_unix.cpp b/src/tsharedmemory_unix.cpp index d0f0237ba..4d43e92be 100644 --- a/src/tsharedmemory_unix.cpp +++ b/src/tsharedmemory_unix.cpp @@ -89,6 +89,7 @@ bool TSharedMemory::create(size_t size) void TSharedMemory::unlink() { + releaseRwlock((header_t *)_ptr); shm_unlink(qUtf8Printable(_name)); tSystemDebug("SharedMemory unlinked. name:{}", qUtf8Printable(_name)); } @@ -154,6 +155,7 @@ bool TSharedMemory::detach() _ptr = nullptr; _size = 0; + tSystemDebug("SharedMemory detached. name:{}", qUtf8Printable(_name)); return true; } From 1eba67b21d780436bb99384d522bad9b2c3143db Mon Sep 17 00:00:00 2001 From: aoyama Date: Tue, 22 Apr 2025 16:40:26 +0900 Subject: [PATCH 12/15] fix compilarion errors. --- .github/workflows/actions.yml | 6 +++--- src/tsharedmemory_linux.cpp | 21 ++++++++++++++------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 64cc295e4..bf8d52c5f 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -364,7 +364,7 @@ jobs: vs-version: '[17,18)' # 17.xx msbuild-architecture: x64 - name: Install Qt - uses: jurplel/install-qt-action@v4 + uses: jurplel/install-qt-action@v4.1 with: version: 6.8.0 host: windows @@ -414,7 +414,7 @@ jobs: steps: - uses: actions/checkout@main - name: Install Qt - uses: jurplel/install-qt-action@v4 + uses: jurplel/install-qt-action@v4.1 with: version: 6.8.0 host: windows @@ -466,7 +466,7 @@ jobs: steps: - uses: actions/checkout@main - name: Install Qt - uses: jurplel/install-qt-action@v4 + uses: jurplel/install-qt-action@v4.1 with: version: 6.5.2 host: windows diff --git a/src/tsharedmemory_linux.cpp b/src/tsharedmemory_linux.cpp index 7818f820d..43f8d2229 100644 --- a/src/tsharedmemory_linux.cpp +++ b/src/tsharedmemory_linux.cpp @@ -6,19 +6,26 @@ */ #include "tsharedmemory.h" +#include #include +#include -void TSharedMemory::initRwlock(header_t *header) const +bool TSharedMemory::initRwlock(header_t *header) const { + int res = -1; pthread_rwlockattr_t attr; - int res = pthread_rwlockattr_init(&attr); - Q_ASSERT(!res); - res = pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); // Linux only - Q_ASSERT(!res); - res = pthread_rwlock_init(&header->rwlock, &attr); - Q_ASSERT(!res); + if (header) { + res = pthread_rwlockattr_init(&attr); + Q_ASSERT(!res); + res = pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); // Linux only + Q_ASSERT(!res); + if ((res = pthread_rwlock_init(&header->rwlock, &attr)) < 0) { + tSystemError("pthread_rwlock_init failed: {}", (const char*)strerror(errno)); + } + } + return !res; } From 098aa6db05b77d2ed2e8c5f3bd9a255f856a59ad Mon Sep 17 00:00:00 2001 From: aoyama Date: Tue, 22 Apr 2025 16:45:57 +0900 Subject: [PATCH 13/15] updated --- .github/workflows/actions.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index bf8d52c5f..1f88a4c02 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -364,7 +364,7 @@ jobs: vs-version: '[17,18)' # 17.xx msbuild-architecture: x64 - name: Install Qt - uses: jurplel/install-qt-action@v4.1 + uses: jurplel/install-qt-action@v4.1.1 with: version: 6.8.0 host: windows @@ -414,7 +414,7 @@ jobs: steps: - uses: actions/checkout@main - name: Install Qt - uses: jurplel/install-qt-action@v4.1 + uses: jurplel/install-qt-action@v4.1.1 with: version: 6.8.0 host: windows @@ -466,7 +466,7 @@ jobs: steps: - uses: actions/checkout@main - name: Install Qt - uses: jurplel/install-qt-action@v4.1 + uses: jurplel/install-qt-action@v4.1.1 with: version: 6.5.2 host: windows From 3686a7976841d2ba382db7abed7622ce854236f8 Mon Sep 17 00:00:00 2001 From: aoyama Date: Tue, 22 Apr 2025 20:33:54 +0900 Subject: [PATCH 14/15] add debug print. --- src/tactioncontroller.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/tactioncontroller.cpp b/src/tactioncontroller.cpp index 2d082eb8a..a837c07d2 100644 --- a/src/tactioncontroller.cpp +++ b/src/tactioncontroller.cpp @@ -253,6 +253,7 @@ QString TActionController::loginUserNameKey() bool TActionController::verifyRequest(const THttpRequest &request) const { if (!csrfProtectionEnabled()) { + tSystemWarn("Skipped verifying authenticity token : {}", request.header().path().data()); return true; } @@ -268,7 +269,11 @@ bool TActionController::verifyRequest(const THttpRequest &request) const } tSystemDebug("postAuthToken: {}", (const char*)postAuthToken.data()); - return Tf::strcmp(postAuthToken, authenticityToken()); + bool res = Tf::strcmp(postAuthToken, authenticityToken()); + if (res) { + tSystemDebug("Verified authenticity token : {}", request.header().path().data()); + } + return res; } /*! From bc12a4f323119daf5663eac643d7f1f9e890e68f Mon Sep 17 00:00:00 2001 From: aoyama Date: Tue, 22 Apr 2025 20:41:16 +0900 Subject: [PATCH 15/15] commented out --- tools/tspawn/main.cpp | 72 +------------------------------------------ 1 file changed, 1 insertion(+), 71 deletions(-) diff --git a/tools/tspawn/main.cpp b/tools/tspawn/main.cpp index cc6637b06..74908b3a3 100644 --- a/tools/tspawn/main.cpp +++ b/tools/tspawn/main.cpp @@ -702,14 +702,6 @@ int main(int argc, char *argv[]) if (svrgen) { svrgen->generate(D_MODELS); } - - // if (templateSystem == TemplateSystem::Vue) { - // VueServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); - // svrgen.generate(D_MODELS); - // } else { - // ServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); - // svrgen.generate(D_MODELS); - // } break; } @@ -734,14 +726,6 @@ int main(int argc, char *argv[]) if (svrgen) { svrgen->generate(D_MODELS); } - - // if (templateSystem == TemplateSystem::Vue) { - // VueServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); - // svrgen.generate(D_MODELS); - // } else { - // ServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); - // svrgen.generate(D_MODELS); - // } break; } @@ -774,14 +758,6 @@ int main(int argc, char *argv[]) return 2; } - // if (templateSystem == TemplateSystem::Vue) { - // VueServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); - // success &= svrgen.generate(D_MODELS); - // } else { - // ServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); - // success &= svrgen.generate(D_MODELS); - // } - ControllerGenerator crtlgen(modelgen.model(), modelgen.fieldList(), modelgen.primaryKeyIndex(), modelgen.lockRevisionIndex()); success &= crtlgen.generate(D_CTRLS); @@ -793,17 +769,6 @@ int main(int argc, char *argv[]) return 2; } - // if (templateSystem == "otama") { - // OtamaGenerator viewgen(modelgen.model(), modelgen.fieldList(), modelgen.primaryKeyIndex(), modelgen.autoValueIndex()); - // viewgen.generate(D_VIEWS); - // } else if (templateSystem == "erb") { - // ErbGenerator viewgen(modelgen.model(), modelgen.fieldList(), modelgen.primaryKeyIndex(), modelgen.autoValueIndex()); - // viewgen.generate(D_VIEWS); - // } else { - // qCritical("Invalid template system specified: %s", qUtf8Printable(templateSystem)); - // return 2; - // } - if (success) { printSuccessMessage(modelgen.model()); } @@ -825,14 +790,6 @@ int main(int argc, char *argv[]) if (svrgen) { svrgen->generate(D_MODELS); } - - // if (templateSystem == TemplateSystem::Vue) { - // VueServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); - // svrgen.generate(D_MODELS); - // } else { - // ServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); - // svrgen.generate(D_MODELS); - // } break; } @@ -903,7 +860,7 @@ int main(int argc, char *argv[]) ControllerGenerator crtlgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); success &= crtlgen.generate(D_CTRLS); - // Generates view files of the specified template system + // Generates service file of the specified template system std::unique_ptr svrgen = createServiceGenerator(templateSystem, modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); if (svrgen) { svrgen->generate(D_MODELS); @@ -921,33 +878,6 @@ int main(int argc, char *argv[]) return 2; } - // // Generates view files of the specified template system - // if (templateSystem == TemplateSystem::Vue) { - // VueServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); - // svrgen.generate(D_MODELS); - - // VueErbGenerator viewgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.autoValueIndex()); - // viewgen.generate(D_VIEWS); - - // } else if (templateSystem == TemplateSystem::Vite_Vue) { - - // } else if (templateSystem == TemplateSystem::Otama) { - // ServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); - // svrgen.generate(D_MODELS); - - // OtamaGenerator viewgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.autoValueIndex()); - // viewgen.generate(D_VIEWS); - // } else if (templateSystem == TemplateSystem::Erb) { - // ServiceGenerator svrgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.lockRevisionIndex()); - // svrgen.generate(D_MODELS); - - // ErbGenerator viewgen(modelgen.model(), modelgen.fieldList(), pkidx, modelgen.autoValueIndex()); - // viewgen.generate(D_VIEWS); - // } else { - // qCritical("Invalid template system specified"); - // return 2; - // } - if (success) { printSuccessMessage(modelgen.model()); }