377 lines
13 KiB
C++
377 lines
13 KiB
C++
#include "XNServiceGen.h"
|
|
#include <sqlite3.h>
|
|
#include <stdexcept>
|
|
#include <filesystem>
|
|
#include <cstdlib>
|
|
#include <fstream>
|
|
#include <algorithm>
|
|
#include <nlohmann/json.hpp>
|
|
#include <sstream>
|
|
|
|
using json = nlohmann::json;
|
|
namespace fs = std::filesystem;
|
|
|
|
XNServiceGen::XNServiceGen()
|
|
{
|
|
}
|
|
|
|
XNServiceGen::~XNServiceGen()
|
|
{
|
|
}
|
|
|
|
const std::string &XNServiceGen::GetCodePath() const
|
|
{
|
|
return m_codePath;
|
|
}
|
|
|
|
int XNServiceGen::Initialize(const std::string &className, const std::string &version,
|
|
std::string &errorMsg)
|
|
{
|
|
std::string dbPath = GetXNCorePath() + "/database/XNSim.db";
|
|
sqlite3 *db;
|
|
int rc = sqlite3_open(dbPath.c_str(), &db);
|
|
if (rc != SQLITE_OK) {
|
|
errorMsg = "无法打开数据库: " + std::string(sqlite3_errmsg(db));
|
|
return -1;
|
|
}
|
|
|
|
std::string sql = "SELECT * FROM XNServiceVersion WHERE ClassName = ? AND Version = ?";
|
|
sqlite3_stmt *stmt;
|
|
rc = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, nullptr);
|
|
if (rc != SQLITE_OK) {
|
|
errorMsg = "准备XNServiceVersion表SQL语句失败: " + std::string(sqlite3_errmsg(db));
|
|
sqlite3_close(db);
|
|
return -1;
|
|
}
|
|
|
|
// 绑定参数
|
|
sqlite3_bind_text(stmt, 1, className.c_str(), -1, SQLITE_STATIC);
|
|
sqlite3_bind_text(stmt, 2, version.c_str(), -1, SQLITE_STATIC);
|
|
|
|
// 执行查询
|
|
rc = sqlite3_step(stmt);
|
|
if (rc == SQLITE_ROW) {
|
|
// 读取数据到成员变量 - 按照CREATE TABLE语句的字段顺序
|
|
m_className = (const char *)sqlite3_column_text(stmt, 0); // ClassName
|
|
m_version = (const char *)sqlite3_column_text(stmt, 2); // Version
|
|
m_name = (const char *)sqlite3_column_text(stmt, 1); // Name
|
|
m_author = (const char *)sqlite3_column_text(stmt, 3); // Author
|
|
m_description = (const char *)sqlite3_column_text(stmt, 4); // Description
|
|
m_createTime = (const char *)sqlite3_column_text(stmt, 5); // CreatTime
|
|
m_changeTime = (const char *)sqlite3_column_text(stmt, 6); // ChangeTime
|
|
// TODO cmdList暂不读取
|
|
} else if (rc == SQLITE_DONE) {
|
|
// 没有找到数据
|
|
errorMsg = "未找到匹配的XNServiceVersion记录";
|
|
sqlite3_finalize(stmt);
|
|
sqlite3_close(db);
|
|
return -2;
|
|
} else {
|
|
// 查询出错
|
|
errorMsg = "查询XNServiceVersion表执行失败: " + std::string(sqlite3_errmsg(db));
|
|
sqlite3_finalize(stmt);
|
|
sqlite3_close(db);
|
|
return -3;
|
|
}
|
|
|
|
sqlite3_finalize(stmt);
|
|
sqlite3_close(db);
|
|
|
|
//目录组装
|
|
m_workPath = GetXNCorePath() + "/Service";
|
|
m_codePath = GetXNCorePath() + "/ServiceProjects/" + className + "_" + version;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int XNServiceGen::GenerateCode(std::string &errorMsg)
|
|
{
|
|
// 检查代码目录是否存在,不存在则创建
|
|
if (!fs::exists(m_codePath)) {
|
|
try {
|
|
fs::create_directories(m_codePath);
|
|
} catch (const fs::filesystem_error &e) {
|
|
errorMsg = "创建代码目录失败: " + std::string(e.what());
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
bool ret = GenerateHeaderFile();
|
|
|
|
if (!ret) {
|
|
errorMsg = "生成头文件失败";
|
|
return -2;
|
|
}
|
|
|
|
ret = GenerateSourceFile();
|
|
|
|
if (!ret) {
|
|
errorMsg = "生成源文件失败";
|
|
return -3;
|
|
}
|
|
|
|
ret = GenerateConfigFile();
|
|
if (!ret) {
|
|
errorMsg = "生成配置文件失败";
|
|
return -4;
|
|
}
|
|
|
|
ret = GenerateCMakeLists();
|
|
if (!ret) {
|
|
errorMsg = "生成CMakeLists.txt失败";
|
|
return -5;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool XNServiceGen::GenerateHeaderFile()
|
|
{
|
|
std::string globalHeaderPath = m_codePath + "/" + m_className + "_global.h";
|
|
std::ofstream globalHeaderFile(globalHeaderPath);
|
|
if (!globalHeaderFile.is_open()) {
|
|
return false;
|
|
}
|
|
std::string upperClassName = UpperCase(m_className);
|
|
|
|
// 导入导出定义文件
|
|
globalHeaderFile << "#ifndef " << upperClassName << "_GLOBAL_H" << std::endl;
|
|
globalHeaderFile << "#define " << upperClassName << "_GLOBAL_H" << std::endl;
|
|
globalHeaderFile << std::endl;
|
|
globalHeaderFile << "#if defined(" << upperClassName << "_LIBRARY)" << std::endl;
|
|
globalHeaderFile << "#define " << upperClassName
|
|
<< "_EXPORT __attribute__((visibility(\"default\")))" << std::endl;
|
|
globalHeaderFile << "#else" << std::endl;
|
|
globalHeaderFile << "#define " << upperClassName
|
|
<< "_EXPORT __attribute__((visibility(\"default\")))" << std::endl;
|
|
globalHeaderFile << "#endif" << std::endl;
|
|
globalHeaderFile << std::endl;
|
|
globalHeaderFile << "#endif // " << upperClassName << "_GLOBAL_H" << std::endl;
|
|
globalHeaderFile << std::endl;
|
|
globalHeaderFile.close();
|
|
|
|
// 模型头文件
|
|
std::string headerPath = m_codePath + "/" + m_className + ".h";
|
|
std::ofstream headerFile(headerPath);
|
|
if (!headerFile.is_open()) {
|
|
return false;
|
|
}
|
|
headerFile << "#pragma once" << std::endl;
|
|
headerFile << std::endl;
|
|
headerFile << "#include \"" << m_className << "_global.h\"" << std::endl;
|
|
headerFile << "#include <XNCore/XNServiceObject.h>" << std::endl;
|
|
headerFile << std::endl;
|
|
headerFile << "struct " << m_className << "Private;" << std::endl;
|
|
headerFile << std::endl;
|
|
headerFile << "class " << upperClassName << "_EXPORT " << m_className
|
|
<< " : public XNServiceObject" << std::endl;
|
|
headerFile << "{" << std::endl;
|
|
headerFile << "XN_METATYPE(" << m_className << ", XNServiceObject)" << std::endl;
|
|
headerFile << "XN_DECLARE_PRIVATE(" << m_className << ")" << std::endl;
|
|
headerFile << "public:" << std::endl;
|
|
headerFile << " " << m_className << "();" << std::endl;
|
|
headerFile << " virtual ~" << m_className << "();" << std::endl;
|
|
headerFile << std::endl;
|
|
headerFile << "protected:" << std::endl;
|
|
headerFile << " " << m_className << "(PrivateType *p);" << std::endl;
|
|
headerFile << std::endl;
|
|
headerFile << "public:" << std::endl;
|
|
headerFile << " virtual void Initialize() override;" << std::endl;
|
|
headerFile << " virtual void PrepareForExecute() override;" << std::endl;
|
|
headerFile << std::endl;
|
|
headerFile << "};" << std::endl;
|
|
headerFile << std::endl;
|
|
headerFile << "XNCLASS_PTR_DECLARE(" << m_className << ")" << std::endl;
|
|
headerFile << std::endl;
|
|
headerFile.close();
|
|
|
|
// 私有结构体头文件
|
|
std::string pHeaderPath = m_codePath + "/" + m_className + "_p.h";
|
|
std::ofstream pHeaderFile(pHeaderPath);
|
|
if (!pHeaderFile.is_open()) {
|
|
return false;
|
|
}
|
|
|
|
pHeaderFile << "#pragma once" << std::endl;
|
|
pHeaderFile << std::endl;
|
|
pHeaderFile << "#include <XNCore/XNServiceObject_p.h>" << std::endl;
|
|
pHeaderFile << std::endl;
|
|
pHeaderFile << "struct " << m_className << "Private : public XNServiceObjectPrivate{"
|
|
<< std::endl;
|
|
pHeaderFile << "};" << std::endl;
|
|
pHeaderFile << std::endl;
|
|
pHeaderFile.close();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool XNServiceGen::GenerateSourceFile()
|
|
{
|
|
// 源文件
|
|
std::string sourcePath = m_codePath + "/" + m_className + ".cpp";
|
|
std::ofstream sourceFile(sourcePath);
|
|
if (!sourceFile.is_open()) {
|
|
return false;
|
|
}
|
|
sourceFile << "#include \"" << m_className << ".h\"" << std::endl;
|
|
sourceFile << "#include \"" << m_className << "_p.h\"" << std::endl;
|
|
sourceFile << "#include <XNCore/XNServiceManager.h>" << std::endl;
|
|
sourceFile << std::endl;
|
|
sourceFile << "XN_SERVICE_INITIALIZE(" << m_className << ")" << std::endl;
|
|
sourceFile << std::endl;
|
|
sourceFile << m_className << "::" << m_className << "() : XNServiceObject("
|
|
<< "new " << m_className << "Private())" << std::endl;
|
|
sourceFile << "{" << std::endl;
|
|
sourceFile << "}" << std::endl;
|
|
sourceFile << std::endl;
|
|
sourceFile << m_className << "::~" << m_className << "() {" << std::endl;
|
|
sourceFile << "}" << std::endl;
|
|
sourceFile << std::endl;
|
|
sourceFile << m_className << "::" << m_className << "(PrivateType *p) : XNServiceObject(p)"
|
|
<< std::endl;
|
|
sourceFile << "{" << std::endl;
|
|
sourceFile << "}" << std::endl;
|
|
sourceFile << std::endl;
|
|
sourceFile << "void " << m_className << "::Initialize() {" << std::endl;
|
|
sourceFile << " T_D();" << std::endl;
|
|
sourceFile << " SuperType::Initialize();" << std::endl;
|
|
sourceFile << " /* 在这里进行其它初始化 */" << std::endl;
|
|
sourceFile << "}" << std::endl;
|
|
sourceFile << std::endl;
|
|
sourceFile << "void " << m_className << "::PrepareForExecute() {" << std::endl;
|
|
sourceFile << " T_D();" << std::endl;
|
|
sourceFile << " SuperType::PrepareForExecute();" << std::endl;
|
|
sourceFile << " /* 在这里进行其它运行前准备工作 */" << std::endl;
|
|
sourceFile << "}" << std::endl;
|
|
sourceFile << std::endl;
|
|
sourceFile.close();
|
|
return true;
|
|
}
|
|
|
|
bool XNServiceGen::GenerateConfigFile()
|
|
{
|
|
std::string configPath = m_codePath + "/" + m_className + ".scfg";
|
|
std::ofstream configFile(configPath);
|
|
if (!configFile.is_open()) {
|
|
return false;
|
|
}
|
|
configFile << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
|
|
configFile << "<Service>" << std::endl;
|
|
configFile << " <Name>" << m_className << "</Name>" << std::endl;
|
|
configFile << " <Description>" << m_description << "</Description>" << std::endl;
|
|
configFile << " <Author>" << m_author << "</Author>" << std::endl;
|
|
configFile << " <Version>" << m_version << "</Version>" << std::endl;
|
|
configFile << " <CreateTime>" << m_createTime << "</CreateTime>" << std::endl;
|
|
configFile << " <ChangeTime>" << m_changeTime << "</ChangeTime>" << std::endl;
|
|
// TODO: 添加命令列表
|
|
configFile << " <CommandList/>" << std::endl;
|
|
configFile << "</Service>" << std::endl;
|
|
configFile.close();
|
|
return true;
|
|
}
|
|
|
|
bool XNServiceGen::GenerateCMakeLists()
|
|
{
|
|
std::string cmakeListsPath = m_codePath + "/CMakeLists.txt";
|
|
std::ofstream cmakeListsFile(cmakeListsPath);
|
|
if (!cmakeListsFile.is_open()) {
|
|
return false;
|
|
}
|
|
cmakeListsFile << "cmake_minimum_required(VERSION 3.16)" << std::endl;
|
|
cmakeListsFile << std::endl;
|
|
cmakeListsFile << "project(" << m_className << " LANGUAGES CXX)" << std::endl;
|
|
cmakeListsFile << std::endl;
|
|
cmakeListsFile << "set(SERVICE_VERSION \"" << m_version << "\")" << std::endl;
|
|
cmakeListsFile << std::endl;
|
|
cmakeListsFile << "set(CMAKE_CXX_STANDARD 17)" << std::endl;
|
|
cmakeListsFile << "set(CMAKE_CXX_STANDARD_REQUIRED ON)" << std::endl;
|
|
cmakeListsFile << "set(CMAKE_POSITION_INDEPENDENT_CODE ON)" << std::endl;
|
|
cmakeListsFile << std::endl;
|
|
|
|
// 设置默认构建类型为Release
|
|
cmakeListsFile << "if(NOT CMAKE_BUILD_TYPE)" << std::endl;
|
|
cmakeListsFile << "\tset(CMAKE_BUILD_TYPE Release CACHE STRING "
|
|
<< "\"Choose the type of build (Debug, Release, RelWithDebInfo, MinSizeRel)\""
|
|
<< " FORCE)" << std::endl;
|
|
cmakeListsFile << "endif()" << std::endl;
|
|
cmakeListsFile << std::endl;
|
|
|
|
// 获取环境变量
|
|
cmakeListsFile << "if(DEFINED ENV{XNCore})" << std::endl;
|
|
cmakeListsFile << " set(XNCore_PATH $ENV{XNCore})" << std::endl;
|
|
cmakeListsFile << "else()" << std::endl;
|
|
cmakeListsFile << " message(FATAL_ERROR \"Environment variable XNCore is not set.\")"
|
|
<< std::endl;
|
|
cmakeListsFile << "endif()" << std::endl;
|
|
cmakeListsFile << std::endl;
|
|
// 添加 XNCore_PATH 下的 include 目录为包含目录
|
|
cmakeListsFile << "include_directories(${XNCore_PATH}/include)" << std::endl;
|
|
cmakeListsFile << std::endl;
|
|
cmakeListsFile << "add_library(" << m_className << " SHARED" << std::endl;
|
|
cmakeListsFile << " " << m_className << "_global.h" << std::endl;
|
|
cmakeListsFile << " " << m_className << ".cpp" << std::endl;
|
|
cmakeListsFile << " " << m_className << ".h" << std::endl;
|
|
cmakeListsFile << " " << m_className << "_p.h" << std::endl;
|
|
cmakeListsFile << ")" << std::endl;
|
|
cmakeListsFile << std::endl;
|
|
cmakeListsFile << "set_target_properties(" << m_className << " PROPERTIES" << std::endl;
|
|
cmakeListsFile << " LIBRARY_OUTPUT_NAME \"lib" << m_className << ".so." << m_version << "\""
|
|
<< std::endl;
|
|
cmakeListsFile << " PREFIX \"\"" << std::endl;
|
|
cmakeListsFile << " SUFFIX \"\"" << std::endl;
|
|
cmakeListsFile << " SKIP_BUILD_RPATH TRUE" << std::endl;
|
|
cmakeListsFile << " BUILD_WITH_INSTALL_RPATH TRUE" << std::endl;
|
|
cmakeListsFile << ")" << std::endl;
|
|
cmakeListsFile << std::endl;
|
|
cmakeListsFile << "target_link_libraries(" << m_className << " PRIVATE" << std::endl;
|
|
cmakeListsFile << " ${XNCore_PATH}/lib/libXNCore.so" << std::endl;
|
|
cmakeListsFile << ")" << std::endl;
|
|
cmakeListsFile << std::endl;
|
|
cmakeListsFile << "target_compile_definitions(" << m_className << " PRIVATE "
|
|
<< UpperCase(m_className) << "_LIBRARY)" << std::endl;
|
|
cmakeListsFile << std::endl;
|
|
cmakeListsFile << "if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)" << std::endl;
|
|
cmakeListsFile << " set(CMAKE_INSTALL_PREFIX \"${XNCore_PATH}/Services\" CACHE PATH "
|
|
"\"Install path prefix\" FORCE)"
|
|
<< std::endl;
|
|
cmakeListsFile << "endif()" << std::endl;
|
|
cmakeListsFile << std::endl;
|
|
cmakeListsFile << "install(TARGETS " << m_className
|
|
<< " BUNDLE DESTINATION . LIBRARY DESTINATION . RUNTIME DESTINATION .)"
|
|
<< std::endl;
|
|
cmakeListsFile << std::endl;
|
|
cmakeListsFile << "file(GLOB CONFIG_FILE \"*.scfg\")" << std::endl;
|
|
cmakeListsFile << std::endl;
|
|
cmakeListsFile << "install(FILES ${CONFIG_FILE} DESTINATION ${CMAKE_INSTALL_PREFIX} RENAME "
|
|
"\"${CMAKE_PROJECT_NAME}_V${SERVICE_VERSION}.scfg\")"
|
|
<< std::endl;
|
|
cmakeListsFile << std::endl;
|
|
cmakeListsFile.close();
|
|
return true;
|
|
}
|
|
|
|
std::string XNServiceGen::GetXNCorePath()
|
|
{
|
|
const char *env_value = std::getenv("XNCore");
|
|
return env_value ? env_value : "";
|
|
}
|
|
|
|
std::string XNServiceGen::UpperCase(const std::string &str)
|
|
{
|
|
std::string result = str;
|
|
std::transform(result.begin(), result.end(), result.begin(), ::toupper);
|
|
return result;
|
|
}
|
|
|
|
std::vector<std::string> XNServiceGen::SplitString(const std::string &str)
|
|
{
|
|
std::vector<std::string> result;
|
|
std::stringstream ss(str);
|
|
std::string token;
|
|
|
|
while (ss >> token) {
|
|
result.push_back(token);
|
|
}
|
|
|
|
return result;
|
|
} |