初始可用。

This commit is contained in:
2026-03-07 23:01:32 +08:00
commit 5d0c57b858
9 changed files with 779 additions and 0 deletions

334
src/main.cpp Normal file
View 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;
// }