/* Crafter® Build Copyright (C) 2025 Catcrafts® Catcrafts.net This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3.0 as published by the Free Software Foundation; This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ module; #if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32) #include #endif #include #include #include #ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu #include #endif module Crafter.Build:Command_impl; import :Command; import :Project; import std; namespace fs = std::filesystem; namespace Crafter { std::string RunCommand(const std::string_view cmd); std::filesystem::path GetPath(); void BuildWasmStdPcm(const Project& project, const Configuration& config) { fs::path exeDir = GetPath(); fs::create_directories(exeDir/config.target); const std::string stdPcm = std::format("{}/wasi-sysroot-28.0/share/libc++/v1/std.pcm", exeDir.string()); fs::path stdCc = fs::path(std::format("{}/wasi-sysroot-28.0/share/libc++/v1/std.cppm", exeDir.string())); if(!fs::exists(stdPcm) || fs::last_write_time(stdPcm) < fs::last_write_time(stdCc)) { std::string result = RunCommand(std::format("clang++ -fno-exceptions --target=wasm32-wasi -nodefaultlibs --sysroot={}/wasi-sysroot-28.0 -std=c++26 -Wno-reserved-identifier -Wno-reserved-module-identifier -fno-c++-static-destructors --precompile {} -o {}", exeDir.string(), stdCc.string(), stdPcm)); if(result != "") { throw std::runtime_error(result); } } const fs::path indexPath = std::format("{}/index.html", exeDir.string()); const fs::path indexDstPath = project.binDir/"index.html"; if(!fs::exists(indexDstPath)) { fs::copy(indexPath, indexDstPath); } else if(fs::last_write_time(indexDstPath) < fs::last_write_time(indexPath)) { fs::remove(indexDstPath); fs::copy(indexPath, indexDstPath); } const fs::path runtimePath = std::format("{}/runtime.js", exeDir.string()); const fs::path runtimeDstPath = project.binDir/"runtime.js"; if(!fs::exists(runtimeDstPath)) { fs::copy(runtimePath, runtimeDstPath); } else if(fs::last_write_time(runtimeDstPath) < fs::last_write_time(runtimePath)) { fs::remove(runtimeDstPath); fs::copy(runtimePath, runtimeDstPath); } } #if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32) std::string RunCommand(const std::string_view cmd) { std::array buffer; std::string result; // Use cmd.exe to interpret redirection std::string with = "cmd /C \"" + std::string(cmd) + " 2>&1\""; FILE* pipe = _popen(with.c_str(), "r"); if (!pipe) { throw std::runtime_error("_popen() failed!"); } while (fgets(buffer.data(), static_cast(buffer.size()), pipe) != nullptr) { result += buffer.data(); } _pclose(pipe); return result; } void RunCommandIgnore(const std::string_view cmd) { std::string with = "cmd /C \"" + std::string(cmd) + " > NUL 2>&1\""; FILE* pipe = _popen(with.c_str(), "r"); if (!pipe) { throw std::runtime_error("_popen() failed!"); } _pclose(pipe); } std::filesystem::path GetPath() { char path[MAX_PATH]; DWORD length = GetModuleFileNameA(NULL, path, MAX_PATH); std::cout << length << std::endl; if (length == 0) { std::cout << "failed" << std::endl; throw std::runtime_error("Failed to get executable path"); } path[length] = '\0'; return fs::path(path).parent_path().parent_path(); } void BuildGnuStdPcm(const Project& project, const Configuration& config) { throw std::runtime_error("target x86_64-pc-linux-gnu is not supported"); } void BuildMingwStdPcm(const Project& project, const Configuration& config) { throw std::runtime_error("target x86_64-w64-mingw32 is not supported"); } void BuildMsvcStdPcm(const Project& project, const Configuration& config) { std::cout << "bruh 1" << std::endl; fs::path exeDir = GetPath(); std::cout << "bruh 2" << std::endl; fs::create_directories(exeDir/config.target); std::cout << "bruh 3" << std::endl; std::string stdPcm = std::format("{}\\{}\\std.pcm", exeDir.string(), config.target); std::string stdcppm = std::format("{}\\{}\\std.cppm", exeDir.string(), config.target); std::cout << stdPcm << std::endl; std::cout << stdcppm << std::endl; std::string directoryPath = RunCommand("C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\vswhere.exe -latest -property installationPath") + "\\VC\\Tools"; std::cout << directoryPath << std::endl; std::vector folders; // Iterate through the directory and collect all subdirectories for (const auto& entry : fs::directory_iterator(directoryPath+"\\MSVC")) { if (entry.is_directory()) { folders.push_back(entry.path().filename().string()); } } std::cout << "bruh 4" << std::endl; // Sort the folders by version in descending order std::sort(folders.begin(), folders.end(), [](const std::string& a, const std::string& b) { return std::lexicographical_compare(b.begin(), b.end(), a.begin(), a.end()); }); std::cout << "bruh 5" << std::endl; std::string msvcVersion = folders.front(); std::cout << msvcVersion << std::endl; clDir = std::format("{}\\MSVC\\{}\\bin\\Hostx64\\x64", directoryPath, msvcVersion); clangClDir = std::format("{}\\Llvm\\x64\\bin", directoryPath); std::string sourceFilePath = directoryPath + "\\MSVC\\" + msvcVersion + "\\modules\\std.ixx"; std::println("Auto-Detected:\nCL: {}Clang: {}\nstd.ixx: {}", clDir, clangClDir, sourceFilePath); if(!fs::exists(stdPcm) || fs::last_write_time(stdPcm) < fs::last_write_time(sourceFilePath)) { fs::copy(sourceFilePath, stdcppm, fs::copy_options::overwrite_existing); std::string result = RunCommand(std::format("cd {}\\{} && {}\\clang-cl.exe /EHsc /MD /std:c++latest --target=x86_64-pc-windows-msvc -Wno-reserved-identifier -Wno-reserved-module-identifier --precompile std.cppm -o std.pcm", exeDir.string(), config.target, clangClDir, stdPcm)); if(result != "") { throw std::runtime_error(result); } result = RunCommand(std::format("cd {}\\{} && {}\\cl.exe /std:c++latest /EHsc /nologo /W4 /MD /c {}", exeDir.string(), config.target, clDir, sourceFilePath)); } } #else std::string RunCommand(const std::string_view cmd) { std::array buffer; std::string result; std::string with = std::string(cmd) + " 2>&1"; // Open pipe to file FILE* pipe = popen(with.c_str(), "r"); if (!pipe) throw std::runtime_error("popen() failed!"); // Read till end of process: while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) { result += buffer.data(); } // Close pipe pclose(pipe); return result; } void RunCommandIgnore(const std::string_view cmd) { std::string with = std::string(cmd) + " > /dev/null 2>&1"; FILE* pipe = popen(with.c_str(), "r"); if (!pipe) throw std::runtime_error("popen() failed!"); pclose(pipe); } std::filesystem::path GetPath() { char path[PATH_MAX]; ssize_t count = readlink("/proc/self/exe", path, PATH_MAX); if (count == -1) { throw std::runtime_error("Failed to get executable path"); } path[count] = '\0'; return fs::path(path).parent_path().parent_path(); } void BuildGnuStdPcm(const Project& project, const Configuration& config) { fs::path exeDir = GetPath(); fs::create_directories(exeDir/config.target); const std::string stdPcm = std::format("{}/{}/std.pcm", exeDir.string(), config.target); std::string gccVersion = RunCommand("g++ -dumpversion"); gccVersion.pop_back(); fs::path stdCc = fs::path(std::format("/usr/include/c++/{}/bits/std.cc", gccVersion)); if(!fs::exists(stdPcm) || fs::last_write_time(stdPcm) < fs::last_write_time(stdCc)) { std::string result = RunCommand(std::format("cp {} {}/{}/std.cppm\nclang++ --target={} -std=c++26 -Wno-reserved-identifier -Wno-reserved-module-identifier --precompile {}/{}/std.cppm -o {}", stdCc.string(), exeDir.string(), config.target, config.target, exeDir.string(), config.target, stdPcm)); if(result != "") { throw std::runtime_error(result); } } } void BuildMingwStdPcm(const Project& project, const Configuration& config) { fs::path exeDir = GetPath(); fs::create_directories(exeDir/config.target); const std::string stdPcm = std::format("{}/{}/std.pcm", exeDir.string(), config.target); std::vector folders; // Iterate through the directory and collect all subdirectories for (const auto& entry : fs::directory_iterator("/usr/x86_64-w64-mingw32/include/c++")) { if (entry.is_directory()) { folders.push_back(entry.path().filename().string()); } } // Sort the folders by version in descending order std::sort(folders.begin(), folders.end(), [](const std::string& a, const std::string& b) { return std::lexicographical_compare(b.begin(), b.end(), a.begin(), a.end()); }); std::string mingWversion = folders.front(); fs::path stdCc = fs::path(std::format("/usr/x86_64-w64-mingw32/include/c++/{}/bits/std.cc", mingWversion)); if(!fs::exists(stdPcm) || fs::last_write_time(stdPcm) < fs::last_write_time(stdCc)) { std::string result = RunCommand(std::format("cp {} {}/{}/std.cppm\nclang++ --target={} -std=c++26 -Wno-reserved-identifier -Wno-reserved-module-identifier --precompile {}/{}/std.cppm -o {}", stdCc.string(), exeDir.string(), config.target, config.target, exeDir.string(), config.target, stdPcm)); if(result != "") { throw std::runtime_error(result); } } try { // Iterate over the source directory for (const auto& entry : fs::directory_iterator("/usr/x86_64-w64-mingw32/bin/")) { // Check if the file is a regular file and ends with ".dll" if (fs::is_regular_file(entry) && entry.path().extension() == ".dll") { // Construct the destination file path fs::path dest_file = project.binDir / entry.path().filename(); // Check if the destination file exists and if it is older than the source file if (!fs::exists(dest_file) || fs::last_write_time(entry.path()) > fs::last_write_time(dest_file)) { // Copy the file if it doesn't exist or is older fs::copy(entry.path(), dest_file, fs::copy_options::overwrite_existing); } } } } catch (const fs::filesystem_error& e) { std::cerr << "Error: " << e.what() << std::endl; } } void BuildMsvcStdPcm(const Project& project, const Configuration& config) { throw std::runtime_error("target x86_64-pc-windows-msvc is not supported"); } #endif // CompileException::CompileException(std::vector&& errors) : errors(std::move(errors)) { // for(CompileError error : errors) { // message += std::format("File: {}:{}\nMessage: {}\nCode: {}", error.filename, error.line, error.message, error.code); // } // }; // const char* CompileException::what() const noexcept { // return message.c_str(); // } void BuildStdPcm(const Project& project, const Configuration& config) { std::cout << config.target << std::endl; if(config.target == "x86_64-pc-linux-gnu") { BuildGnuStdPcm(project, config); } else if(config.target == "x86_64-w64-mingw32") { BuildMingwStdPcm(project, config); } else if(config.target == "x86_64-pc-windows-msvc") { BuildMsvcStdPcm(project, config); } else if(config.target == "wasm32-wasi") { BuildWasmStdPcm(project, config); } else { throw std::runtime_error(std::format("Unkown target: {}", config.target)); } } std::string RunClang(const std::string_view cmd) { // std::string result = RunCommand(cmd); // // std::vector errors; // // std::regex error_regex(R"((/[^:]+\.cpp):(\d+):\d+: error: (.*)\n\s*[0-9| ]*\s*(.*))"); // // std::smatch match; // // while (std::regex_search(result, match, error_regex)) { // // CompileError error; // // error.filename = match[1].str(); // // error.line = std::stoi(match[2].str()); // // error.message = match[3].str(); // // error.code = match[4].str(); // // errors.push_back(error); // // result = match.suffix().str(); // // } // if(result != "") { // // if(errors.size() != 0) { // // throw CompileException(std::move(errors)); // // } else { // throw std::runtime_error(result); // //} // } std::cout << cmd << std::endl; return RunCommand(cmd); } }