#include #include #include #include #include #include #include #include #include #include #include #include #include namespace fs = std::filesystem; const std::string CONFIG_PATH = "/home/unknownobject/UNSWebServerCore/build/core_distribute.json"; // 编译阶段枚举 enum class BuildPhase { Configure, Compile, Link }; // 辅助函数:显示标准文件复制进度条 void DisplayProgressBar(size_t current, size_t total) { if (total == 0) return; float progress = static_cast(current) / total; int bar_width = 30; std::string bar = ""; int pos = static_cast(bar_width * progress); for (int i = 0; i < bar_width; ++i) { if (i < pos) bar += "="; else if (i == pos) bar += ">"; else bar += " "; } std::cout << std::format("\t[{}] {:.0f}%\r", bar, progress * 100.0); std::cout.flush(); if (current == total) std::cout << "\n"; } // 辅助函数:显示带有阶段状态的编译进度条 void DisplayBuildStatus(BuildPhase phase, int percentage) { std::string phase_str = ""; if (phase == BuildPhase::Configure) phase_str = "\033[33m[1/3] Configuring\033[0m"; else if (phase == BuildPhase::Compile) phase_str = "\033[33m[2/3] Compiling \033[0m"; else if (phase == BuildPhase::Link) phase_str = "\033[33m[3/3] Linking \033[0m"; if (phase == BuildPhase::Compile) { int bar_width = 20; std::string bar = ""; int pos = static_cast(bar_width * (percentage / 100.0)); for (int i = 0; i < bar_width; ++i) { if (i < pos) bar += "="; else if (i == pos) bar += ">"; else bar += " "; } std::cout << std::format("\t{} [{}] {}%\r", phase_str, bar, percentage); } else std::cout << std::format("\t{} \r", phase_str); std::cout.flush(); } // 辅助函数:计算单个文件的 SHA256 哈希值 std::string CalculateSHA256(const fs::path& file_path) { std::ifstream file_in(file_path, std::ios::binary); if (!file_in.is_open()) return ""; EVP_MD_CTX* context = EVP_MD_CTX_new(); if (context == nullptr) return ""; if (EVP_DigestInit_ex(context, EVP_sha256(), nullptr) != 1) { EVP_MD_CTX_free(context); return ""; } char buffer[4096]; while ((file_in.read(buffer, sizeof(buffer))) || (file_in.gcount() > 0)) { if (EVP_DigestUpdate(context, buffer, file_in.gcount()) != 1) { EVP_MD_CTX_free(context); return ""; } } unsigned char hash[EVP_MAX_MD_SIZE]; unsigned int length = 0; if (EVP_DigestFinal_ex(context, hash, &length) != 1) { EVP_MD_CTX_free(context); return ""; } EVP_MD_CTX_free(context); std::stringstream ss; for (unsigned int i = 0; i < length; ++i) ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i]; return ss.str(); } bool ClearDirectory(const fs::path& dir) { std::error_code ec; if (!fs::exists(dir, ec)) { fs::create_directories(dir, ec); if (ec) return false; } else if (!fs::is_directory(dir, ec)) return false; for (const auto& entry : fs::directory_iterator(dir, ec)) { if (ec) return false; fs::remove_all(entry.path(), ec); if (ec) return false; } return true; } bool CheckSOAndCopy(const fs::path& so_src, const fs::path& so_dst) { std::error_code ec; if (!fs::exists(so_src, ec)) return false; if (!fs::is_directory(so_src, ec)) return false; if (!fs::exists(so_dst, ec)) { fs::create_directories(so_dst, ec); if (ec) return false; } size_t total_so = 0; for (const auto& entry : fs::directory_iterator(so_src, ec)) { if (ec) return false; bool is_reg = entry.is_regular_file(ec); bool is_so = entry.path().extension() == ".so"; if ((is_reg) && (is_so)) total_so++; } size_t processed_so = 0; DisplayProgressBar(processed_so, total_so); for (const auto& entry : fs::directory_iterator(so_src, ec)) { if (ec) return false; bool is_reg = entry.is_regular_file(ec); bool is_so = entry.path().extension() == ".so"; if ((is_reg) && (is_so)) { fs::path dst_file = so_dst / entry.path().filename(); if (!fs::exists(dst_file, ec)) { fs::copy_file(entry.path(), dst_file, fs::copy_options::overwrite_existing, ec); if (ec) return false; } else { std::string src_hash = CalculateSHA256(entry.path()); std::string dst_hash = CalculateSHA256(dst_file); if (src_hash.empty()) return false; if (dst_hash.empty()) return false; if (src_hash != dst_hash) { fs::copy_file(entry.path(), dst_file, fs::copy_options::overwrite_existing, ec); if (ec) return false; } } processed_so++; DisplayProgressBar(processed_so, total_so); } } return true; } bool CopyAll(const fs::path& dst) { static const fs::path SRC_DIR = "/home/unknownobject/UNSWebServerCore/build/unswsc"; std::error_code ec; if (!fs::exists(SRC_DIR, ec)) return false; if (!fs::is_directory(SRC_DIR, ec)) return false; size_t total_files = 0; for (const auto& entry : fs::recursive_directory_iterator(SRC_DIR, ec)) { if (ec) return false; if (entry.is_regular_file(ec)) total_files++; } size_t copied_files = 0; DisplayProgressBar(copied_files, total_files); for (const auto& entry : fs::recursive_directory_iterator(SRC_DIR, ec)) { if (ec) return false; if (entry.is_regular_file(ec)) { fs::path relative_path = fs::relative(entry.path(), SRC_DIR, ec); if (ec) return false; fs::path target_path = dst / relative_path; fs::create_directories(target_path.parent_path(), ec); if (ec) return false; fs::copy_file(entry.path(), target_path, fs::copy_options::overwrite_existing, ec); if (ec) return false; copied_files++; DisplayProgressBar(copied_files, total_files); } } return true; } std::map DecodeJson() { Json::Value root; Json::Reader reader; try { std::fstream file_in(CONFIG_PATH, std::ios::in); if (!file_in.is_open()) return {}; if (!reader.parse(file_in, root, false)) return {}; if (!root.isArray()) return {}; std::map result; for (const auto& data : root) result.insert({ data["Program"].asString(), data["Path"].asString() }); return result; } catch (...) { return {}; } } bool CallRebuild(const fs::path& path) { fs::path script_dir = path / "build"; fs::path sh_path = script_dir / "rebuild.sh"; std::error_code ec; bool is_exist = fs::exists(sh_path, ec); if (!is_exist) return false; bool is_file = fs::is_regular_file(sh_path, ec); if (!is_file) return false; std::string cmd_str = "cd \"" + script_dir.string() + "\" && bash ./rebuild.sh 2>&1"; FILE* pipe = popen(cmd_str.c_str(), "r"); if (pipe == nullptr) return false; char line_buffer[512]; int last_pct = -1; BuildPhase current_phase = BuildPhase::Configure; std::vector output_logs; // 初始拉起,显示第一阶段 DisplayBuildStatus(current_phase, 0); while (fgets(line_buffer, sizeof(line_buffer), pipe) != nullptr) { std::string line(line_buffer); output_logs.push_back(line); // 核心状态机切相逻辑 BuildPhase old_phase = current_phase; if (line.find("Building CXX object") != std::string::npos) current_phase = BuildPhase::Compile; else if (line.find("Linking CXX") != std::string::npos) current_phase = BuildPhase::Link; if (current_phase != old_phase) DisplayBuildStatus(current_phase, 0); size_t start_pos = line.find('['); size_t percent_pos = line.find('%'); if ((start_pos != std::string::npos) && (percent_pos != std::string::npos)) { if (start_pos < percent_pos) { std::string pct_str = line.substr(start_pos + 1, percent_pos - start_pos - 1); try { int pct = std::stoi(pct_str); if ((pct >= 0) && (pct <= 100)) { if (pct != last_pct) { DisplayBuildStatus(current_phase, pct); last_pct = pct; } } } catch (...) { } } } } int status = pclose(pipe); int exit_code = WEXITSTATUS(status); // 清理进度条行尾 std::cout << " \r"; if (exit_code != 0) { std::cout << std::format("\033[31m\t[ERROR] Rebuild Failed! Dump Log Below:\033[0m\n"); std::cout << std::format("\033[31m\t----------------------------------------\033[0m\n"); // 匹配实际的转义控制字节 (\x1b) 以及字面量字符串 (\\e 或 \\033) static const std::regex ANSI_REGEX(R"((\x1b|\\e|\\033)\[[0-9;]*m)"); for (const auto& log_line : output_logs) { // 过滤掉所有残留的颜色控制字符,避免与外层高亮红冲突 std::string clean_line = std::regex_replace(log_line, ANSI_REGEX, ""); std::cout << std::format("\t\033[31m {}\033[0m", clean_line); } std::cout << std::format("\033[31m\t----------------------------------------\033[0m\n"); return false; } return true; } int main() { auto programs = DecodeJson(); if (programs.empty()) { std::cout << "\033[31mConfig Decode Failed\033[0m\n"; return 0; } static const fs::path SRC_SO_DIR = "/home/unknownobject/UNSWebServerCore/build/so"; int deploy_success = 0; int deploy_failure = 0; int build_success = 0; int build_failure = 0; for (const auto& [prog_name, folder_path] : programs) { fs::path header = folder_path / "unswsc"; fs::path dynamic = folder_path / "so"; std::cout << std::format("Processing [\033[35m{}\033[0m]\n", prog_name); bool deploy_ok = true; if (!ClearDirectory(header)) { std::cout << std::format("\t\033[31mFailed to Clear Directory [{}]\033[0m\n\n", header.string()); deploy_ok = false; } else std::cout << std::format("\tHeader Directory Cleared.\n"); if (deploy_ok) { if (!CopyAll(header)) { std::cout << std::format("\t\033[31mFailed to Copy Header to [{}]\033[0m\n\n", header.string()); deploy_ok = false; } else std::cout << std::format("\tHeader Copy Finished.\n"); } if (deploy_ok) { if (!CheckSOAndCopy(SRC_SO_DIR, dynamic)) { std::cout << std::format("\t\033[31mFailed to Copy SO Library to [{}]\033[0m\n\n", dynamic.string()); deploy_ok = false; } else std::cout << std::format("\tSO Library Copy Finished.\n"); } if (deploy_ok) deploy_success++; else { deploy_failure++; continue; } std::cout << std::format("\tTrigger Auto Rebuild...\n"); if (CallRebuild(folder_path)) { std::cout << std::format("\t\033[32mRebuild Success.\033[0m\n"); build_success++; } else { std::cout << std::format("\t\033[31mRebuild Failed.\033[0m\n"); build_failure++; } std::cout << std::format("Done.\n\n"); } std::cout << std::format("\033[36m==================== Statistics Summary ====================\033[0m\n"); std::cout << std::format("Deployment (Clear+Headers+SO): \033[32m{} Success\033[0m | \033[31m{} Failure\033[0m\n", deploy_success, deploy_failure); std::cout << std::format("Compilation (CallRebuild): \033[32m{} Success\033[0m | \033[31m{} Failure\033[0m\n", build_success, build_failure); std::cout << std::format("\033[36m============================================================\033[0m\n"); return 0; }