初始可用。
This commit is contained in:
334
src/main.cpp
Normal file
334
src/main.cpp
Normal file
@@ -0,0 +1,334 @@
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <filesystem>
|
||||
#include <fmt/format.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <shlobj.h>
|
||||
#include <spdlog/sinks/rotating_file_sink.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <windows.h>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
static std::shared_ptr<spdlog::logger> logger;
|
||||
|
||||
// 全局常量
|
||||
const char* MENU_NAME = "quick-xmake";
|
||||
const char* SUBMENU_NAMES[] = {"create", "build", "set-debug", "set-release"};
|
||||
const int SUBMENU_COUNT = 4;
|
||||
|
||||
std::string WcharToChar(const wchar_t* wp, size_t m_encode = CP_ACP)
|
||||
{
|
||||
std::string str;
|
||||
int len = WideCharToMultiByte(m_encode, 0, wp, wcslen(wp), NULL, 0, NULL, NULL);
|
||||
char* m_char = new char[len + 1];
|
||||
WideCharToMultiByte(m_encode, 0, wp, wcslen(wp), m_char, len, NULL, NULL);
|
||||
m_char[len] = '\0';
|
||||
str = m_char;
|
||||
delete m_char;
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string u8_to_ansi(const std::string& str)
|
||||
{
|
||||
int wideCharLen = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0);
|
||||
if (wideCharLen <= 0) {
|
||||
return "";
|
||||
}
|
||||
std::wstring wideStr(wideCharLen, L'\0');
|
||||
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &wideStr[0], wideCharLen);
|
||||
int gbkLen = WideCharToMultiByte(CP_ACP, 0, wideStr.c_str(), -1, nullptr, 0, nullptr, nullptr);
|
||||
if (gbkLen <= 0) {
|
||||
return "";
|
||||
}
|
||||
std::string gbkStr(gbkLen, '\0');
|
||||
WideCharToMultiByte(CP_ACP, 0, wideStr.c_str(), -1, &gbkStr[0], gbkLen, nullptr, nullptr);
|
||||
|
||||
gbkStr.resize(gbkLen - 1);
|
||||
return gbkStr;
|
||||
}
|
||||
std::string ansi_to_u8(const std::string& str)
|
||||
{
|
||||
int wideCharLen = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, nullptr, 0);
|
||||
if (wideCharLen <= 0) {
|
||||
return "";
|
||||
}
|
||||
std::wstring wideStr(wideCharLen, L'\0');
|
||||
MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, &wideStr[0], wideCharLen);
|
||||
|
||||
int utf8Len = WideCharToMultiByte(CP_UTF8, 0, wideStr.c_str(), -1, nullptr, 0, nullptr, nullptr);
|
||||
if (utf8Len <= 0) {
|
||||
return "";
|
||||
}
|
||||
std::string utf8Str(utf8Len, '\0');
|
||||
WideCharToMultiByte(CP_UTF8, 0, wideStr.c_str(), -1, &utf8Str[0], utf8Len, nullptr, nullptr);
|
||||
|
||||
utf8Str.resize(utf8Len - 1);
|
||||
return utf8Str;
|
||||
}
|
||||
|
||||
std::string GetQuickXmakeLogPath()
|
||||
{
|
||||
try {
|
||||
fs::path userDir;
|
||||
char* userProfileEnv = std::getenv("USERPROFILE");
|
||||
if (userProfileEnv != nullptr) {
|
||||
userDir = fs::path(userProfileEnv);
|
||||
} else {
|
||||
char* homeEnv = std::getenv("HOME");
|
||||
if (homeEnv != nullptr) {
|
||||
userDir = fs::path(homeEnv);
|
||||
}
|
||||
}
|
||||
if (userDir.empty() || !fs::exists(userDir)) {
|
||||
userDir = fs::current_path();
|
||||
}
|
||||
|
||||
fs::path logPath = userDir / ".config" / "quick-xmake" / "quick-xmake.log";
|
||||
|
||||
try {
|
||||
fs::create_directories(logPath.parent_path());
|
||||
} catch (const std::exception& e) {
|
||||
logger->error("Failed to create log directory: {}", ansi_to_u8(e.what()));
|
||||
return "quick-xmake.log";
|
||||
}
|
||||
|
||||
return logPath.string();
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
logger->error("Failed to get user directory: {}", ansi_to_u8(e.what()));
|
||||
return "quick-xmake.log";
|
||||
}
|
||||
}
|
||||
|
||||
int ReplaceTemplate(const std::string& projectDir)
|
||||
{
|
||||
fs::path p(projectDir);
|
||||
p.append("xmake.lua");
|
||||
|
||||
if (!fs::exists(p)) {
|
||||
logger->error("xmake.lua not found in {}", projectDir);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::ifstream in(p.string());
|
||||
std::string content(std::istreambuf_iterator<char>(in), {});
|
||||
in.close();
|
||||
|
||||
std::string newLuaContent = R"(
|
||||
add_rules("mode.debug", "mode.release")
|
||||
add_rules("plugin.compile_commands.autoupdate", {outputdir = "build"})
|
||||
|
||||
if is_plat("windows") then
|
||||
add_cxxflags("/utf-8")
|
||||
-- add_defines("WIN32_LEAN_AND_MEAN")
|
||||
end
|
||||
|
||||
set_languages("c++17")
|
||||
)";
|
||||
|
||||
std::string lpre = R"(add_rules("mode.debug", "mode.release"))";
|
||||
boost::replace_all(content, lpre, newLuaContent);
|
||||
std::ofstream out(p.string());
|
||||
out << content;
|
||||
out.close();
|
||||
|
||||
fs::path pm(projectDir);
|
||||
pm.append("src");
|
||||
pm.append("main.cpp");
|
||||
if (!fs::exists(pm)) {
|
||||
logger->error("main.cpp not found in {}", projectDir);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::ifstream inCpp(pm.string());
|
||||
std::string contentCpp(std::istreambuf_iterator<char>(inCpp), {});
|
||||
inCpp.close();
|
||||
|
||||
std::string newCppContent =
|
||||
R"(#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
#ifdef _WIN32
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
#endif
|
||||
)";
|
||||
|
||||
std::string mpre = R"(int main(int argc, char **argv) {)";
|
||||
boost::replace_all(contentCpp, mpre, newCppContent);
|
||||
std::ofstream outCpp(pm.string());
|
||||
outCpp << contentCpp;
|
||||
outCpp.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ExecuteCmd(const std::string& exe, std::vector<std::string>& args, const std::string& workDir)
|
||||
{
|
||||
try {
|
||||
// 构建 xmake 命令
|
||||
std::string xmakeCmd = "\"" + exe + "\"";
|
||||
for (const auto& arg : args) {
|
||||
xmakeCmd += " \"" + arg + "\"";
|
||||
}
|
||||
|
||||
// 完整的 cmd 命令:先 cd 到目录,然后执行 xmake,使用 /k 保持窗口打开
|
||||
std::string fullCmd = "cd /d \"" + workDir + "\" && " + xmakeCmd;
|
||||
|
||||
logger->info("Command line: cmd /k {}", fullCmd);
|
||||
|
||||
STARTUPINFOA si = {sizeof(STARTUPINFOA)};
|
||||
PROCESS_INFORMATION pi = {0};
|
||||
|
||||
// 设置控制台窗口
|
||||
si.dwFlags = STARTF_USESHOWWINDOW;
|
||||
si.wShowWindow = SW_SHOW;
|
||||
|
||||
// 构建包含 /k 的命令行
|
||||
std::string cmdLine = "cmd.exe /k \"" + fullCmd + "\"";
|
||||
|
||||
BOOL success = CreateProcess(NULL, // 应用程序名称
|
||||
const_cast<char*>(cmdLine.c_str()), // 完整的命令行
|
||||
NULL, // 进程安全属性
|
||||
NULL, // 线程安全属性
|
||||
FALSE, // 不继承句柄
|
||||
CREATE_NEW_CONSOLE, // 创建新控制台
|
||||
NULL, // 环境块
|
||||
NULL, // 工作目录(已经在命令中指定)
|
||||
&si, // STARTUPINFO
|
||||
&pi // PROCESS_INFORMATION
|
||||
);
|
||||
|
||||
if (!success) {
|
||||
logger->error("CreateProcess failed: {}", GetLastError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 立即返回,不等待进程结束
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
|
||||
logger->info("Process started successfully, console will remain open");
|
||||
return 0; // 立即返回成功
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
logger->error("Exception: {}", ansi_to_u8(e.what()));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) HRESULT CALLBACK ContextMenuHandler(HWND hwnd, HINSTANCE hinst, LPCSTR lpszCmdLine, int nCmdShow)
|
||||
{
|
||||
std::shared_ptr<void> quit(nullptr, [](void*) {
|
||||
if (logger) {
|
||||
logger->flush();
|
||||
}
|
||||
spdlog::shutdown();
|
||||
});
|
||||
|
||||
auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(GetQuickXmakeLogPath(), 1024 * 1024 * 50, 3);
|
||||
file_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l]: %v");
|
||||
std::vector<spdlog::sink_ptr> sinks{file_sink};
|
||||
logger = std::make_shared<spdlog::logger>("quick-xmake", sinks.begin(), sinks.end());
|
||||
logger->set_level(spdlog::level::debug);
|
||||
spdlog::register_logger(logger);
|
||||
|
||||
// 记录开始
|
||||
logger->info("=== ContextMenuHandler called ===");
|
||||
|
||||
std::string path = ansi_to_u8(lpszCmdLine);
|
||||
logger->info("path:[{}], nCmdShow:[{}]", path, nCmdShow);
|
||||
|
||||
std::vector<std::string> vecArg;
|
||||
boost::split(vecArg, path, boost::is_any_of(" "));
|
||||
if (vecArg.size() < 2) {
|
||||
logger->error("invalid args: {}", path);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
boost::trim_if(vecArg[0], boost::is_any_of("\""));
|
||||
// 0 是创建
|
||||
if (vecArg[1] == "0") {
|
||||
std::vector<std::string> args = {"create", "-l", "c++", "-P", "."};
|
||||
int ec = ExecuteCmd("xmake", args, vecArg[0]);
|
||||
if (ec != 0) {
|
||||
logger->error("xmake create failed, exit_code: {}", ec);
|
||||
} else {
|
||||
logger->info("xmake create success");
|
||||
}
|
||||
}
|
||||
// 1 是 build
|
||||
else if (vecArg[1] == "1") {
|
||||
std::vector<std::string> args = {"build", "-P", "."};
|
||||
int ec = ExecuteCmd("xmake", args, vecArg[0]);
|
||||
if (ec != 0) {
|
||||
logger->error("xmake build failed, exit_code: {}", ec);
|
||||
} else {
|
||||
logger->info("xmake build success");
|
||||
}
|
||||
}
|
||||
// 2 是设置 debug
|
||||
else if (vecArg[1] == "2") {
|
||||
std::vector<std::string> args = {"f", "-m", "debug", "-P", "."};
|
||||
int ec = ExecuteCmd("xmake", args, vecArg[0]);
|
||||
if (ec != 0) {
|
||||
logger->error("xmake config debug failed, exit_code: {}", ec);
|
||||
} else {
|
||||
logger->info("xmake config debug success");
|
||||
}
|
||||
}
|
||||
// 3 是设置 release
|
||||
else if (vecArg[1] == "3") {
|
||||
std::vector<std::string> args = {"f", "-m", "release", "-P", "."};
|
||||
int ec = ExecuteCmd("xmake", args, vecArg[0]);
|
||||
if (ec != 0) {
|
||||
logger->error("xmake config release failed, exit_code: {}", ec);
|
||||
} else {
|
||||
logger->info("xmake config release success");
|
||||
}
|
||||
}
|
||||
// 4 是运行
|
||||
else if (vecArg[1] == "4") {
|
||||
std::vector<std::string> args = {"run", "-P", "."};
|
||||
int ec = ExecuteCmd("xmake", args, vecArg[0]);
|
||||
if (ec != 0) {
|
||||
logger->error("xmake run failed, exit_code: {}", ec);
|
||||
} else {
|
||||
logger->info("xmake run success");
|
||||
}
|
||||
}
|
||||
// 5 是替换
|
||||
else if (vecArg[1] == "5") {
|
||||
auto r = ReplaceTemplate(vecArg[0]);
|
||||
if (r) {
|
||||
logger->info("replace template success");
|
||||
} else {
|
||||
logger->error("replace template failed");
|
||||
}
|
||||
} else {
|
||||
logger->error("invalid vecArg[1]: {}", vecArg[1]);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
logger->info("=== ContextMenuHandler end ===");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// DLL入口点
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// int main()
|
||||
// {
|
||||
// std::cout << "Hello World!\n";
|
||||
// ContextMenuHandler(NULL, NULL, "D:\\XmakeDemo\\simple_demo\\000 0", 0);
|
||||
// return 0;
|
||||
// }
|
||||
Reference in New Issue
Block a user