From b4c5a0e84b61265816216c9262721f52c587b6b6 Mon Sep 17 00:00:00 2001 From: xblackicex Date: Tue, 26 Nov 2024 17:35:31 +0100 Subject: [PATCH 1/4] =?UTF-8?q?=E2=9C=A8=20feat:=20add=20windows=20support?= =?UTF-8?q?=20=20=20=20=20-=20build=20and=20run=20poac=20cmd=20on=20window?= =?UTF-8?q?s=20=20=20=20=20-=20add=20cmake=20build=20system=20=20=20=20=20?= =?UTF-8?q?-=20add=20vcpkg=20package=20manager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitattributes | 14 ++++ .gitignore | 9 +++ CMakeLists.txt | 112 ++++++++++++++++++++++++++ CMakePresets.json | 58 ++++++++++++++ src/Algos.cc | 6 +- src/BuildConfig.cc | 39 ++++----- src/Cmd/Build.cc | 2 +- src/Cmd/Version.cc | 10 ++- src/Command.cc | 173 ++++++++++++++++++++++++++++++++++++++-- src/Command.hpp | 19 +++-- src/Manifest.cc | 10 ++- src/Rustify/Aliases.hpp | 4 + vcpkg.json | 10 +++ 13 files changed, 428 insertions(+), 38 deletions(-) create mode 100644 .gitattributes create mode 100644 CMakeLists.txt create mode 100644 CMakePresets.json create mode 100644 vcpkg.json diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..a94a188a2 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,14 @@ +# Set the default behavior for all files. +* text=auto eol=lf + +# Normalized and converts to native line endings on checkout. +*.c text +*.cc text +*.cxx text +*.cpp text +*.mpp text +*.cppm text +*.ixx text +*.h text +*.hxx text +*.hpp text diff --git a/.gitignore b/.gitignore index 9713abfc1..05dc41dc5 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,12 @@ poac-out/ # Doxygen html + +# CMake & XMake +.xmake +build +out + +# Visual Studio +*.sdf +*.vs \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..42979e100 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,112 @@ +cmake_minimum_required(VERSION 3.15) +file(READ "${CMAKE_CURRENT_SOURCE_DIR}/poac.toml" POAC_TOML_CONTENT) + +string(REGEX MATCH "([0-9]+\\.[0-9]+\\.[0-9]+)" version_match "${POAC_TOML_CONTENT}") + +if(NOT version_match) + message(FATAL_ERROR "Failed to extract version from poac.toml") +endif() +project(poac VERSION ${version_match} LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + + +# target +add_executable(poac "") +set_target_properties(poac PROPERTIES OUTPUT_NAME "poac") + + +# dependencies +find_package(fmt REQUIRED) +find_package(nlohmann_json REQUIRED) +find_package(TBB REQUIRED) +find_package(toml11 REQUIRED) +find_package(CURL REQUIRED) +find_package(unofficial-libgit2 CONFIG REQUIRED) + +find_package(Git REQUIRED) +if(GIT_FOUND) + execute_process( + COMMAND ${GIT_EXECUTABLE} rev-parse HEAD + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE GIT_COMMIT_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + execute_process( + COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE GIT_COMMIT_SHORT_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + execute_process( + COMMAND ${GIT_EXECUTABLE} log -1 --format=%cd --date=short + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE GIT_COMMIT_DATE + OUTPUT_STRIP_TRAILING_WHITESPACE + ) +else() + set(GIT_COMMIT_HASH "unknown") + set(GIT_COMMIT_SHORT_HASH "unknown") + set(GIT_COMMIT_DATE "unknown") +endif() + +target_compile_definitions(poac PRIVATE + POAC_POAC_PKG_VERSION="${PROJECT_VERSION}" + POAC_POAC_COMMIT_HASH="${GIT_COMMIT_HASH}" + POAC_POAC_COMMIT_SHORT_HASH="${GIT_COMMIT_SHORT_HASH}" + POAC_POAC_COMMIT_DATE="${GIT_COMMIT_DATE}" + NOMINMAX + PCRE2_CODE_UNIT_WIDTH=8 + PCRE2_STATIC + CURL_STATICLIB + __TBB_NO_IMPLICIT_LINKAGE +) + +target_sources(poac PUBLIC + src/Algos.cc + src/BuildConfig.cc + src/Cli.cc + src/Cmd/Add.cc + src/Cmd/Build.cc + src/Cmd/Clean.cc + src/Cmd/Fmt.cc + src/Cmd/Help.cc + src/Cmd/Init.cc + src/Cmd/Lint.cc + src/Cmd/New.cc + src/Cmd/Run.cc + src/Cmd/Search.cc + src/Cmd/Test.cc + src/Cmd/Tidy.cc + src/Cmd/Version.cc + src/Command.cc + src/CurlVersion.cc + src/Git2/Commit.cc + src/Git2/Config.cc + src/Git2/Describe.cc + src/Git2/Exception.cc + src/Git2/Global.cc + src/Git2/Object.cc + src/Git2/Oid.cc + src/Git2/Repository.cc + src/Git2/Revparse.cc + src/Git2/Revwalk.cc + src/Git2/Time.cc + src/Git2/Version.cc + src/main.cc + src/Manifest.cc + src/Parallelism.cc + src/Semver.cc + src/TermColor.cc + src/VersionReq.cc +) + +target_link_libraries(poac PRIVATE + fmt::fmt + nlohmann_json::nlohmann_json + TBB::tbb + toml11::toml11 + CURL::libcurl + unofficial::libgit2::libgit2 +) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 000000000..51acb3a61 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,58 @@ +{ + "version": 8, + "configurePresets": [ + { + "name": "VS-x64-msvc", + "displayName": "Visual Studio Community 2022 Release - amd64", + "generator": "Visual Studio 17 2022", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "cacheVariables": { + "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}", + "CMAKE_C_COMPILER": "cl.exe", + "CMAKE_CXX_COMPILER": "cl.exe", + "CMAKE_TOOLCHAIN_FILE": "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" + } + }, + { + "name": "Ninja-x64-msvc", + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "cacheVariables": { + "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}", + "CMAKE_C_COMPILER": "cl.exe", + "CMAKE_CXX_COMPILER": "cl.exe", + "CMAKE_TOOLCHAIN_FILE": "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" + } + } + ], + "buildPresets": [ + { + "name": "VS-x64-msvc-debug", + "displayName": "Visual Studio Community 2022 Release - amd64 - Debug", + "configurePreset": "VS-x64-msvc", + "configuration": "Debug", + "jobs": 16 + }, + { + "name": "VS-x64-msvc-release", + "displayName": "Visual Studio Community 2022 Release - amd64 - Release", + "configurePreset": "VS-x64-msvc", + "configuration": "Release", + "jobs": 16 + }, + { + "name": "Ninja-x64-msvc-debug", + "displayName": "Ninja Release - amd64 - Debug", + "configurePreset": "Ninja-x64-msvc", + "configuration": "Debug", + "jobs": 16 + }, + { + "name": "Ninja-x64-msvc-release", + "displayName": "Ninja Release - amd64 - Release", + "configurePreset": "Ninja-x64-msvc", + "configuration": "Release", + "jobs": 16 + } + ] +} \ No newline at end of file diff --git a/src/Algos.cc b/src/Algos.cc index 13d756df2..6aa5fa526 100644 --- a/src/Algos.cc +++ b/src/Algos.cc @@ -69,10 +69,10 @@ getCmdOutput(const Command& cmd, const size_t retry) { int exitCode = EXIT_SUCCESS; int waitTime = 1; for (size_t i = 0; i < retry; ++i) { - const auto [curExitCode, stdout, stderr] = cmd.output(); - static_cast(stderr); + const auto [curExitCode, out, err] = cmd.output(); + static_cast(err); if (curExitCode == EXIT_SUCCESS) { - return stdout; + return out; } exitCode = curExitCode; diff --git a/src/BuildConfig.cc b/src/BuildConfig.cc index 5ceffcd48..a8b1f3519 100644 --- a/src/BuildConfig.cc +++ b/src/BuildConfig.cc @@ -83,7 +83,7 @@ BuildConfig::BuildConfig(const std::string& packageName, const bool isDebug) .addArg("/dev/null") .setStderrConfig(Command::IOConfig::Null) .output() - .stdout; + .stdout_str; std::istringstream iss(output); std::string line; @@ -346,9 +346,9 @@ BuildConfig::emitCompdb(std::ostream& os) const { // We don't check the std::optional value because we know the first // dependency always exists for compile targets. const std::string file = - fs::relative(targetInfo.sourceFile.value(), directory); + fs::relative(targetInfo.sourceFile.value(), directory).string(); // The output is the target. - const std::string output = fs::relative(target, directory); + const std::string output = fs::relative(target, directory).string(); const Command cmd = Command(cxx) .addArgs(cxxflags) .addArgs(defines) @@ -532,7 +532,7 @@ BuildConfig::collectBinDepObjs( // NOLINT(misc-no-recursion) // file (e.g., unittests/path/to/file.o). continue; } - if (!HEADER_FILE_EXTS.contains(headerPath.extension())) { + if (!HEADER_FILE_EXTS.contains(headerPath.extension().string())) { // We only care about header files. continue; } @@ -674,7 +674,7 @@ BuildConfig::processSrc( ) { std::string objTarget; // source.o const std::unordered_set objTargetDeps = - parseMMOutput(runMM(sourceFilePath), objTarget); + parseMMOutput(runMM(sourceFilePath.string()), objTarget); const fs::path targetBaseDir = fs::relative(sourceFilePath.parent_path(), getProjectBasePath() / "src"); @@ -683,13 +683,13 @@ BuildConfig::processSrc( buildTargetBaseDir /= targetBaseDir; } - const std::string buildObjTarget = buildTargetBaseDir / objTarget; + const std::string buildObjTarget = (buildTargetBaseDir / objTarget).string(); if (mtx) { mtx->lock(); } buildObjTargets.insert(buildObjTarget); - defineCompileTarget(buildObjTarget, sourceFilePath, objTargetDeps); + defineCompileTarget(buildObjTarget, sourceFilePath.string(), objTargetDeps); if (mtx) { mtx->unlock(); } @@ -724,13 +724,13 @@ BuildConfig::processUnittestSrc( const std::unordered_set& buildObjTargets, std::unordered_set& testTargets, tbb::spin_mutex* mtx ) { - if (!containsTestCode(sourceFilePath)) { + if (!containsTestCode(sourceFilePath.string())) { return; } std::string objTarget; // source.o const std::unordered_set objTargetDeps = - parseMMOutput(runMM(sourceFilePath, /*isTest=*/true), objTarget); + parseMMOutput(runMM(sourceFilePath.string(), /*isTest=*/true), objTarget); const fs::path targetBaseDir = fs::relative( sourceFilePath.parent_path(), getProjectBasePath() / "src"_path @@ -740,7 +740,7 @@ BuildConfig::processUnittestSrc( testTargetBaseDir /= targetBaseDir; } - const std::string testObjTarget = testTargetBaseDir / objTarget; + const std::string testObjTarget = (testTargetBaseDir / objTarget).string(); const std::string testTarget = (testTargetBaseDir / sourceFilePath.filename()).string() + ".test"; @@ -756,7 +756,7 @@ BuildConfig::processUnittestSrc( } // Test object target. defineCompileTarget( - testObjTarget, sourceFilePath, objTargetDeps, /*isTest=*/true + testObjTarget, sourceFilePath.string(), objTargetDeps, /*isTest=*/true ); // Test binary target. @@ -772,7 +772,7 @@ static std::vector listSourceFilePaths(const fs::path& dir) { std::vector sourceFilePaths; for (const auto& entry : fs::recursive_directory_iterator(dir)) { - if (!SOURCE_FILE_EXTS.contains(entry.path().extension())) { + if (!SOURCE_FILE_EXTS.contains(entry.path().extension().string())) { continue; } sourceFilePaths.emplace_back(entry.path()); @@ -794,7 +794,7 @@ BuildConfig::configureBuild() { fs::path mainSource; for (const auto& entry : fs::directory_iterator(srcDir)) { const fs::path& path = entry.path(); - if (!SOURCE_FILE_EXTS.contains(path.extension())) { + if (!SOURCE_FILE_EXTS.contains(path.extension().string())) { continue; } if (!isMainSource(path)) { @@ -843,7 +843,7 @@ BuildConfig::configureBuild() { processSources(sourceFilePaths); // Project binary target. - const std::string mainObjTarget = buildOutPath / "main.o"; + const std::string mainObjTarget = (buildOutPath / "main.o").string(); std::unordered_set projTargetDeps = { mainObjTarget }; collectBinDepObjs( projTargetDeps, "", @@ -851,7 +851,7 @@ BuildConfig::configureBuild() { buildObjTargets ); - defineLinkTarget(outBasePath / packageName, projTargetDeps); + defineLinkTarget((outBasePath / packageName).string(), projTargetDeps); // Test Pass std::unordered_set testTargets; @@ -895,7 +895,7 @@ emitMakefile(const bool isDebug, const bool includeDevDeps) { // make sure the dependencies are installed. config.installDeps(includeDevDeps); - const std::string makefilePath = config.outBasePath / "Makefile"; + const std::string makefilePath = (config.outBasePath / "Makefile").string(); if (isUpToDate(makefilePath)) { logger::debug("Makefile is up to date"); return config; @@ -916,17 +916,18 @@ emitCompdb(const bool isDebug, const bool includeDevDeps) { // compile_commands.json also needs INCLUDES, but not LIBS. config.installDeps(includeDevDeps); - const std::string compdbPath = config.outBasePath / "compile_commands.json"; + const std::string compdbPath = + (config.outBasePath / "compile_commands.json").string(); if (isUpToDate(compdbPath)) { logger::debug("compile_commands.json is up to date"); - return config.outBasePath; + return config.outBasePath.string(); } logger::debug("compile_commands.json is NOT up to date"); config.configureBuild(); std::ofstream ofs(compdbPath); config.emitCompdb(ofs); - return config.outBasePath; + return config.outBasePath.string(); } std::string_view diff --git a/src/Cmd/Build.cc b/src/Cmd/Build.cc index 74d1b4ba3..591721847 100644 --- a/src/Cmd/Build.cc +++ b/src/Cmd/Build.cc @@ -37,7 +37,7 @@ buildImpl(std::string& outDir, const bool isDebug) { const auto start = std::chrono::steady_clock::now(); const BuildConfig config = emitMakefile(isDebug, /*includeDevDeps=*/false); - outDir = config.outBasePath; + outDir = config.outBasePath.string(); const std::string& packageName = getPackageName(); const Command makeCmd = getMakeCommand().addArg("-C").addArg(outDir).addArg( diff --git a/src/Cmd/Version.cc b/src/Cmd/Version.cc index a785f79e1..1c91ad0fb 100644 --- a/src/Cmd/Version.cc +++ b/src/Cmd/Version.cc @@ -96,7 +96,7 @@ secondMonthChar(const std::string_view month) noexcept { } // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays) -static constinit const char COMPILE_DATE[] = { +static constexpr const char COMPILE_DATE[] = { // Year __DATE__[7], __DATE__[8], __DATE__[9], __DATE__[10], @@ -143,7 +143,15 @@ versionMain(const std::span args) noexcept { std::cout << "release: " << POAC_POAC_PKG_VERSION << '\n' << "commit-hash: " << COMMIT_HASH << '\n' << "commit-date: " << COMMIT_DATE << '\n' + #if defined(__clang__) + << "compiler: " << __clang_version__ << '\n' + #elif defined(__GNUC__) || defined(__GNUG__) << "compiler: " << __VERSION__ << '\n' + #elif defined(_MSC_VER) + << "compiler: MSVC " << _MSC_FULL_VER << '\n' + #else + << "compiler: unknown\n" + #endif << "compile-date: " << COMPILE_DATE << '\n' << "libgit2: " << git2::Version() << '\n' << "libcurl: " << curl::Version() << '\n'; diff --git a/src/Command.cc b/src/Command.cc index b2a036e41..3e53ec86a 100644 --- a/src/Command.cc +++ b/src/Command.cc @@ -3,20 +3,58 @@ #include "Exception.hpp" #include "Rustify.hpp" -#include #include -#include -#include -#include -#include -#include -#include +#include #include +#ifdef _WIN32 +# include +#else +# include +# include +# include +#endif + constexpr std::size_t BUFFER_SIZE = 128; +#ifdef _WIN32 +namespace { +std::string +GetLastErrorAsString() { + DWORD error = GetLastError(); + char buffer[256]; + FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer), NULL + ); + return std::string(buffer); +} + +void +CloseHandleSafe(HANDLE& handle) { + if (handle != INVALID_HANDLE_VALUE && handle != NULL) { + CloseHandle(handle); + handle = INVALID_HANDLE_VALUE; + } +} +} // namespace +#endif + int Child::wait() const { +#ifdef _WIN32 + DWORD exitCode = 0; + WaitForSingleObject(process, INFINITE); + GetExitCodeProcess(process, &exitCode); + + CloseHandleSafe(const_cast(process)); + if (stdoutfd != -1) + CloseHandle((HANDLE)stdoutfd); + if (stderrfd != -1) + CloseHandle((HANDLE)stderrfd); + + return static_cast(exitCode); +#else int status{}; if (waitpid(pid, &status, 0) == -1) { if (stdoutfd != -1) { @@ -37,10 +75,49 @@ Child::wait() const { const int exitCode = WEXITSTATUS(status); return exitCode; +#endif } CommandOutput Child::waitWithOutput() const { +#ifdef _WIN32 + std::string stdoutOutput; + std::string stderrOutput; + DWORD bytesRead; + std::array buffer{}; + + if (stdoutfd != -1) { + while (ReadFile( + (HANDLE)stdoutfd, buffer.data(), buffer.size(), &bytesRead, NULL + ) + && bytesRead > 0) { + stdoutOutput.append(buffer.data(), bytesRead); + } + } + + if (stderrfd != -1) { + while (ReadFile( + (HANDLE)stderrfd, buffer.data(), buffer.size(), &bytesRead, NULL + ) + && bytesRead > 0) { + stderrOutput.append(buffer.data(), bytesRead); + } + } + + DWORD exitCode = EXIT_SUCCESS; + WaitForSingleObject(process, INFINITE); + GetExitCodeProcess(process, &exitCode); + + CloseHandleSafe(const_cast(process)); + if (stdoutfd != -1) + CloseHandle((HANDLE)stdoutfd); + if (stderrfd != -1) + CloseHandle((HANDLE)stderrfd); + + return { .exitCode = static_cast(exitCode), + .stdout_str = stdoutOutput, + .stderr_str = stderrOutput }; +#else std::string stdoutOutput; std::string stderrOutput; @@ -124,10 +201,91 @@ Child::waitWithOutput() const { return { .exitCode = exitCode, .stdout = stdoutOutput, .stderr = stderrOutput }; +#endif } Child Command::spawn() const { +#ifdef _WIN32 + SECURITY_ATTRIBUTES saAttr = {}; + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + + HANDLE stdoutRead = INVALID_HANDLE_VALUE; + HANDLE stdoutWrite = INVALID_HANDLE_VALUE; + HANDLE stderrRead = INVALID_HANDLE_VALUE; + HANDLE stderrWrite = INVALID_HANDLE_VALUE; + + if (stdoutConfig == IOConfig::Piped) { + if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0)) { + throw PoacError("CreatePipe failed for stdout: ", GetLastErrorAsString()); + } + SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0); + } + + if (stderrConfig == IOConfig::Piped) { + if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0)) { + CloseHandleSafe(stdoutRead); + CloseHandleSafe(stdoutWrite); + throw PoacError("CreatePipe failed for stderr: ", GetLastErrorAsString()); + } + SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0); + } + + // Prepare startup information + STARTUPINFOA si = {}; + si.cb = sizeof(si); + si.dwFlags |= STARTF_USESTDHANDLES; + si.hStdOutput = stdoutConfig == IOConfig::Piped ? stdoutWrite + : stdoutConfig == IOConfig::Null + ? NULL + : GetStdHandle(STD_OUTPUT_HANDLE); + si.hStdError = stderrConfig == IOConfig::Piped ? stderrWrite + : stderrConfig == IOConfig::Null + ? NULL + : GetStdHandle(STD_ERROR_HANDLE); + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + + // Prepare command line + std::string cmdLine = command; + for (const auto& arg : arguments) { + cmdLine += " " + arg; + } + + PROCESS_INFORMATION pi = {}; + + // Create process + BOOL success = CreateProcessA( + NULL, + cmdLine.data(), + NULL, + NULL, + TRUE, + CREATE_NO_WINDOW, + NULL, + workingDirectory.empty() ? NULL : workingDirectory.string().c_str(), + &si, + &pi + ); + + // Clean up unnecessary handles + if (stdoutWrite != INVALID_HANDLE_VALUE) + CloseHandle(stdoutWrite); + if (stderrWrite != INVALID_HANDLE_VALUE) + CloseHandle(stderrWrite); + + if (!success) { + CloseHandleSafe(stdoutRead); + CloseHandleSafe(stderrRead); + throw PoacError("CreateProcess failed: ", GetLastErrorAsString()); + } + + CloseHandle(pi.hThread); // Close thread handle + + return { pi.hProcess, + (int)(stdoutConfig == IOConfig::Piped ? (intptr_t)stdoutRead : -1), + (int)(stderrConfig == IOConfig::Piped ? (intptr_t)stderrRead : -1) }; +#else std::array stdoutPipe{}; std::array stderrPipe{}; @@ -219,6 +377,7 @@ Command::spawn() const { return { pid, stdoutConfig == IOConfig::Piped ? stdoutPipe[0] : -1, stderrConfig == IOConfig::Piped ? stderrPipe[0] : -1 }; } +#endif } CommandOutput diff --git a/src/Command.hpp b/src/Command.hpp index 9c85c8b96..c6c8cd33f 100644 --- a/src/Command.hpp +++ b/src/Command.hpp @@ -5,24 +5,31 @@ #include #include #include -#include #include #include +#ifdef _WIN32 +# include +using process_handle = HANDLE; +#else +# include +using process_handle = pid_t; +#endif + struct CommandOutput { int exitCode; - std::string stdout; - std::string stderr; + std::string stdout_str; + std::string stderr_str; }; class Child { private: - pid_t pid; + process_handle process; int stdoutfd; int stderrfd; - Child(pid_t pid, int stdoutfd, int stderrfd) noexcept - : pid(pid), stdoutfd(stdoutfd), stderrfd(stderrfd) {} + Child(process_handle process, int stdoutfd, int stderrfd) noexcept + : process(process), stdoutfd(stdoutfd), stderrfd(stderrfd) {} friend struct Command; diff --git a/src/Manifest.cc b/src/Manifest.cc index 400031b73..1d28ac619 100644 --- a/src/Manifest.cc +++ b/src/Manifest.cc @@ -434,7 +434,15 @@ getXdgCacheHome() { if (const char* envP = std::getenv("XDG_CACHE_HOME")) { return envP; } - const fs::path userDir = std::getenv("HOME"); +#ifdef _WIN32 + const char* homeDir = std::getenv("USERPROFILE"); +#else + const char* homeDir = std::getenv("HOME"); +#endif + if (homeDir == nullptr) { + throw PoacError("Environment variable HOME or USERPROFILE is not set"); + } + const fs::path userDir = homeDir; return userDir / ".cache"; } diff --git a/src/Rustify/Aliases.hpp b/src/Rustify/Aliases.hpp index 5f0f72167..0c9489255 100644 --- a/src/Rustify/Aliases.hpp +++ b/src/Rustify/Aliases.hpp @@ -36,7 +36,11 @@ unreachable( std::source_location::current() ) noexcept { #ifdef NDEBUG +# if defined(__GNUC__) || defined(__clang__) __builtin_unreachable(); +# else + __assume(false); +# endif #else panic("unreachable", loc); #endif diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 000000000..996ff5201 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,10 @@ +{ + "dependencies": [ + "fmt", + "libgit2", + "nlohmann-json", + "tbb", + "toml11", + "curl" + ] +} From 3d0e539d6b0343686562e89df0a3b5af58c98fc2 Mon Sep 17 00:00:00 2001 From: xblackicex Date: Tue, 26 Nov 2024 19:27:14 +0100 Subject: [PATCH 2/4] =?UTF-8?q?=E2=9C=A8=20feat(build):=20add`Xmake`=20as?= =?UTF-8?q?=20an=20alternative=20build=20system?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Refactored `buildImpl` to handle multiple build systems (`Makefile` and `Xmake`). - Introduced `emitXmakeLua` method to generate Xmake-specific build files. - Added `--build-system` option to `build` command for selecting the desired build system. - Fixed improper handling of file descriptors in `Command` class on Windows. --- CMakeLists.txt | 78 ++++++++++------------------- CMakePresets.json | 4 +- src/BuildConfig.cc | 63 +++++++++++++++++++++++ src/BuildConfig.hpp | 7 +++ src/Cmd/Build.cc | 118 ++++++++++++++++++++++++++++++-------------- src/Cmd/Build.hpp | 15 +++++- src/Cmd/Run.cc | 2 +- src/Command.cc | 25 +++++----- src/Command.hpp | 8 +-- src/main.cc | 1 + 10 files changed, 213 insertions(+), 108 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 42979e100..72b3abd2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,28 +4,26 @@ file(READ "${CMAKE_CURRENT_SOURCE_DIR}/poac.toml" POAC_TOML_CONTENT) string(REGEX MATCH "([0-9]+\\.[0-9]+\\.[0-9]+)" version_match "${POAC_TOML_CONTENT}") if(NOT version_match) - message(FATAL_ERROR "Failed to extract version from poac.toml") + message(FATAL_ERROR "Failed to extract version from poac.toml") endif() + project(poac VERSION ${version_match} LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) - -# target -add_executable(poac "") -set_target_properties(poac PROPERTIES OUTPUT_NAME "poac") - - -# dependencies -find_package(fmt REQUIRED) -find_package(nlohmann_json REQUIRED) -find_package(TBB REQUIRED) -find_package(toml11 REQUIRED) -find_package(CURL REQUIRED) -find_package(unofficial-libgit2 CONFIG REQUIRED) +if("${CMAKE_GENERATOR}" STREQUAL "Ninja") + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + add_custom_target(copy_compile_commands ALL + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_BINARY_DIR}/compile_commands.json + ${CMAKE_SOURCE_DIR}/.vscode/compile_commands.json + DEPENDS ${CMAKE_BINARY_DIR}/compile_commands.json + ) +endif() find_package(Git REQUIRED) + if(GIT_FOUND) execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse HEAD @@ -51,6 +49,13 @@ else() set(GIT_COMMIT_DATE "unknown") endif() +file(GLOB_RECURSE SOURCES "src/*.cc") + +# target +add_executable(poac) + +target_sources(poac PRIVATE ${SOURCES}) + target_compile_definitions(poac PRIVATE POAC_POAC_PKG_VERSION="${PROJECT_VERSION}" POAC_POAC_COMMIT_HASH="${GIT_COMMIT_HASH}" @@ -63,44 +68,13 @@ target_compile_definitions(poac PRIVATE __TBB_NO_IMPLICIT_LINKAGE ) -target_sources(poac PUBLIC - src/Algos.cc - src/BuildConfig.cc - src/Cli.cc - src/Cmd/Add.cc - src/Cmd/Build.cc - src/Cmd/Clean.cc - src/Cmd/Fmt.cc - src/Cmd/Help.cc - src/Cmd/Init.cc - src/Cmd/Lint.cc - src/Cmd/New.cc - src/Cmd/Run.cc - src/Cmd/Search.cc - src/Cmd/Test.cc - src/Cmd/Tidy.cc - src/Cmd/Version.cc - src/Command.cc - src/CurlVersion.cc - src/Git2/Commit.cc - src/Git2/Config.cc - src/Git2/Describe.cc - src/Git2/Exception.cc - src/Git2/Global.cc - src/Git2/Object.cc - src/Git2/Oid.cc - src/Git2/Repository.cc - src/Git2/Revparse.cc - src/Git2/Revwalk.cc - src/Git2/Time.cc - src/Git2/Version.cc - src/main.cc - src/Manifest.cc - src/Parallelism.cc - src/Semver.cc - src/TermColor.cc - src/VersionReq.cc -) +# dependencies +find_package(fmt REQUIRED) +find_package(nlohmann_json REQUIRED) +find_package(TBB REQUIRED) +find_package(toml11 REQUIRED) +find_package(CURL REQUIRED) +find_package(unofficial-libgit2 CONFIG REQUIRED) target_link_libraries(poac PRIVATE fmt::fmt diff --git a/CMakePresets.json b/CMakePresets.json index 51acb3a61..59ac48d2d 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -10,7 +10,7 @@ "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}", "CMAKE_C_COMPILER": "cl.exe", "CMAKE_CXX_COMPILER": "cl.exe", - "CMAKE_TOOLCHAIN_FILE": "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" + "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" } }, { @@ -21,7 +21,7 @@ "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}", "CMAKE_C_COMPILER": "cl.exe", "CMAKE_CXX_COMPILER": "cl.exe", - "CMAKE_TOOLCHAIN_FILE": "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" + "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" } } ], diff --git a/src/BuildConfig.cc b/src/BuildConfig.cc index a8b1f3519..3bb819903 100644 --- a/src/BuildConfig.cc +++ b/src/BuildConfig.cc @@ -315,6 +315,22 @@ BuildConfig::emitMakefile(std::ostream& os) const { } } +void +BuildConfig::emitXmakeLua(std::ostream& os) const { + os << R"(add_rules("mode.debug", "mode.release"))" << '\n'; + os << R"(set_config("buildir", ")" << buildOutPath.generic_string() << R"("))" + << '\n'; + os << R"(set_languages("c++20"))" << '\n'; + os << R"(target(")" << packageName << R"("))" << '\n'; + os << R"( set_kind("binary"))" << '\n'; + os << R"( set_targetdir(")" << outBasePath.generic_string() << R"("))" + << '\n'; + os << R"( set_objectdir(")" << buildOutPath.generic_string() << R"("))" + << '\n'; + os << R"( add_files("../../src/*.cc"))" << '\n'; + os << R"( add_includedirs("../../include"))" << '\n'; +} + void BuildConfig::emitCompdb(std::ostream& os) const { const fs::path directory = getProjectBasePath(); @@ -908,6 +924,20 @@ emitMakefile(const bool isDebug, const bool includeDevDeps) { return config; } +BuildConfig +emitXmakeLua(bool isDebug, bool includeDevDeps) { + BuildConfig config(getPackageName(), isDebug); + + if (!fs::exists(config.outBasePath)) { + fs::create_directories(config.outBasePath); + } + const std::string xmakefilePath = (config.outBasePath / "xmake.lua").string(); + + std::ofstream ofs(xmakefilePath); + config.emitXmakeLua(ofs); + return config; +} + /// @returns the directory where the compilation database is generated. std::string emitCompdb(const bool isDebug, const bool includeDevDeps) { @@ -958,6 +988,39 @@ getMakeCommand() { return makeCommand; } +Command +getBuildCommand(BuildSystem system) { + switch (system) { + case BuildSystem::Makefile: + return getMakeCommand(); + case BuildSystem::Xmake: + return Command("xmake"); + } + throw PoacError("unknown build system"); +} + +BuildConfig +emitBuildFiles(BuildSystem system, bool isDebug, bool includeDevDeps) { + switch (system) { + case BuildSystem::Makefile: + return emitMakefile(isDebug, includeDevDeps); + case BuildSystem::Xmake: + return emitXmakeLua(isDebug, includeDevDeps); + } + throw PoacError("unknown build system"); +} + +std::string_view +getBuildSystemName(BuildSystem system) { + switch (system) { + case BuildSystem::Makefile: + return "Makefile"; + case BuildSystem::Xmake: + return "XMake"; + } + throw PoacError("unknown build system"); +} + #ifdef POAC_TEST namespace tests { diff --git a/src/BuildConfig.hpp b/src/BuildConfig.hpp index e7e14389d..9ed35eaae 100644 --- a/src/BuildConfig.hpp +++ b/src/BuildConfig.hpp @@ -3,6 +3,7 @@ #include "Command.hpp" #include "Exception.hpp" #include "Rustify.hpp" +#include "Cmd/Build.hpp" #include #include @@ -122,6 +123,7 @@ struct BuildConfig { void emitVariable(std::ostream& os, const std::string& varName) const; void emitMakefile(std::ostream& os) const; + void emitXmakeLua(std::ostream& os) const; void emitCompdb(std::ostream& os) const; std::string runMM(const std::string& sourceFile, bool isTest = false) const; bool containsTestCode(const std::string& sourceFile) const; @@ -162,7 +164,12 @@ struct BuildConfig { void configureBuild(); }; +Command getBuildCommand(BuildSystem system); +BuildConfig emitBuildFiles(BuildSystem system, bool isDebug, bool includeDevDeps); +std::string_view getBuildSystemName(BuildSystem system); + BuildConfig emitMakefile(bool isDebug, bool includeDevDeps); +BuildConfig emitXmakeLua(bool isDebug, bool includeDevDeps); std::string emitCompdb(bool isDebug, bool includeDevDeps); std::string_view modeToString(bool isDebug); std::string_view modeToProfile(bool isDebug); diff --git a/src/Cmd/Build.cc b/src/Cmd/Build.cc index 591721847..808e449a1 100644 --- a/src/Cmd/Build.cc +++ b/src/Cmd/Build.cc @@ -30,54 +30,85 @@ const Subcmd BUILD_CMD = "Generate compilation database instead of building" )) .addOpt(OPT_JOBS) + .addOpt(Opt{ "--build-system" } + .setDesc("Specify build system (makefile/xmake)") + .setPlaceholder("SYSTEM")) .setMainFn(buildMain); int -buildImpl(std::string& outDir, const bool isDebug) { +buildImpl(std::string& outDir, const bool isDebug, BuildSystem buildSystem) { const auto start = std::chrono::steady_clock::now(); - const BuildConfig config = emitMakefile(isDebug, /*includeDevDeps=*/false); + const BuildConfig config = emitBuildFiles(buildSystem, isDebug, false); outDir = config.outBasePath.string(); const std::string& packageName = getPackageName(); - const Command makeCmd = getMakeCommand().addArg("-C").addArg(outDir).addArg( - (config.outBasePath / packageName).string() - ); - Command checkUpToDateCmd = makeCmd; - checkUpToDateCmd.addArg("--question"); - - int exitCode = execCmd(checkUpToDateCmd); - if (exitCode != EXIT_SUCCESS) { - // If packageName binary is not up-to-date, compile it. - logger::info( - "Compiling", "{} v{} ({})", packageName, getPackageVersion().toString(), - getProjectBasePath().string() - - ); - exitCode = execCmd(makeCmd); + Command buildCmd = getBuildCommand(buildSystem); + + auto logBuildCompletion = [&](int exitCode, + const std::chrono::duration& elapsed) { + if (exitCode == EXIT_SUCCESS) { + const Profile& profile = isDebug ? getDevProfile() : getReleaseProfile(); + std::vector profiles; + if (profile.optLevel.value() == 0) { + profiles.emplace_back("unoptimized"); + } else { + profiles.emplace_back("optimized"); + } + if (profile.debug.value()) { + profiles.emplace_back("debuginfo"); + } + logger::info( + "Finished", "`{}` profile [{}] target(s) in {:.2f}s", + modeToProfile(isDebug), fmt::join(profiles, " + "), elapsed.count() + ); + } + }; + + int exitCode = EXIT_FAILURE; + switch (buildSystem) { + case BuildSystem::Makefile: { + buildCmd.addArg("-C").addArg(outDir).addArg( + (config.outBasePath / packageName).string() + ); + Command checkUpToDateCmd = buildCmd; + checkUpToDateCmd.addArg("--question"); + + exitCode = execCmd(checkUpToDateCmd); + if (exitCode != EXIT_SUCCESS) { + logger::info( + "Compiling", "{} v{} ({})", packageName, + getPackageVersion().toString(), getProjectBasePath().string() + ); + exitCode = execCmd(buildCmd); + } + break; + } + case BuildSystem::Xmake: { + Command configCmd = buildCmd; + auto xmakePath = (config.outBasePath / "xmake.lua").string(); + configCmd.addArg("f") + .addArg("-m") + .addArg(isDebug ? "debug" : "release") + .addArg("-F") + .addArg(xmakePath); + exitCode = execCmd(configCmd); + if (exitCode == EXIT_SUCCESS) { + buildCmd.addArg("-F").addArg(xmakePath); + logger::info( + "Compiling", "{} v{} ({})", packageName, + getPackageVersion().toString(), getProjectBasePath().string() + ); + exitCode = execCmd(buildCmd); + } + break; + } } const auto end = std::chrono::steady_clock::now(); const std::chrono::duration elapsed = end - start; + logBuildCompletion(exitCode, elapsed); - if (exitCode == EXIT_SUCCESS) { - const Profile& profile = isDebug ? getDevProfile() : getReleaseProfile(); - - std::vector profiles; - if (profile.optLevel.value() == 0) { - profiles.emplace_back("unoptimized"); - } else { - profiles.emplace_back("optimized"); - } - if (profile.debug.value()) { - profiles.emplace_back("debuginfo"); - } - - logger::info( - "Finished", "`{}` profile [{}] target(s) in {:.2f}s", - modeToProfile(isDebug), fmt::join(profiles, " + "), elapsed.count() - ); - } return exitCode; } @@ -86,6 +117,8 @@ buildMain(const std::span args) { // Parse args bool isDebug = true; bool buildCompdb = false; + BuildSystem buildSystem = getDefaultBuildSystem(); + for (auto itr = args.begin(); itr != args.end(); ++itr) { if (const auto res = Cli::handleGlobalOpts(itr, args.end(), "build")) { if (res.value() == Cli::CONTINUE) { @@ -99,6 +132,19 @@ buildMain(const std::span args) { isDebug = false; } else if (*itr == "--compdb") { buildCompdb = true; + } else if (*itr == "--build-system") { + if (itr + 1 == args.end()) { + return Subcmd::missingArgumentForOpt(*itr); + } + ++itr; + if (*itr == "makefile") { + buildSystem = BuildSystem::Makefile; + } else if (*itr == "xmake") { + buildSystem = BuildSystem::Xmake; + } else { + logger::error("unsupported build system: ", *itr); + return EXIT_FAILURE; + } } else if (*itr == "-j" || *itr == "--jobs") { if (itr + 1 == args.end()) { return Subcmd::missingArgumentForOpt(*itr); @@ -121,7 +167,7 @@ buildMain(const std::span args) { if (!buildCompdb) { std::string outDir; - return buildImpl(outDir, isDebug); + return buildImpl(outDir, isDebug, buildSystem); } // Build compilation database diff --git a/src/Cmd/Build.hpp b/src/Cmd/Build.hpp index 00de5a6cb..58a36cad5 100644 --- a/src/Cmd/Build.hpp +++ b/src/Cmd/Build.hpp @@ -4,5 +4,18 @@ #include +enum class BuildSystem { + Makefile, + Xmake +}; + +static BuildSystem getDefaultBuildSystem() { +#ifdef _WIN32 + return BuildSystem::Xmake; +#else + return BuildSystem::Makefile; +#endif +} + extern const Subcmd BUILD_CMD; -int buildImpl(std::string& outDir, bool isDebug); +int buildImpl(std::string& outDir, bool isDebug, BuildSystem system); diff --git a/src/Cmd/Run.cc b/src/Cmd/Run.cc index 2e3cddc5c..262bfe2d4 100644 --- a/src/Cmd/Run.cc +++ b/src/Cmd/Run.cc @@ -75,7 +75,7 @@ runMain(const std::span args) { } std::string outDir; - if (buildImpl(outDir, isDebug) != EXIT_SUCCESS) { + if (buildImpl(outDir, isDebug, getDefaultBuildSystem()) != EXIT_SUCCESS) { return EXIT_FAILURE; } diff --git a/src/Command.cc b/src/Command.cc index 3e53ec86a..953e3756f 100644 --- a/src/Command.cc +++ b/src/Command.cc @@ -48,10 +48,8 @@ Child::wait() const { GetExitCodeProcess(process, &exitCode); CloseHandleSafe(const_cast(process)); - if (stdoutfd != -1) - CloseHandle((HANDLE)stdoutfd); - if (stderrfd != -1) - CloseHandle((HANDLE)stderrfd); + CloseHandleSafe(const_cast(stdoutfd)); + CloseHandleSafe(const_cast(stderrfd)); return static_cast(exitCode); #else @@ -86,7 +84,8 @@ Child::waitWithOutput() const { DWORD bytesRead; std::array buffer{}; - if (stdoutfd != -1) { + // Read from stdout + if (stdoutfd != INVALID_HANDLE_VALUE) { while (ReadFile( (HANDLE)stdoutfd, buffer.data(), buffer.size(), &bytesRead, NULL ) @@ -95,7 +94,8 @@ Child::waitWithOutput() const { } } - if (stderrfd != -1) { + // read from stderr + if (stderrfd != INVALID_HANDLE_VALUE) { while (ReadFile( (HANDLE)stderrfd, buffer.data(), buffer.size(), &bytesRead, NULL ) @@ -109,10 +109,8 @@ Child::waitWithOutput() const { GetExitCodeProcess(process, &exitCode); CloseHandleSafe(const_cast(process)); - if (stdoutfd != -1) - CloseHandle((HANDLE)stdoutfd); - if (stderrfd != -1) - CloseHandle((HANDLE)stderrfd); + CloseHandleSafe(const_cast(stdoutfd)); + CloseHandleSafe(const_cast(stderrfd)); return { .exitCode = static_cast(exitCode), .stdout_str = stdoutOutput, @@ -261,7 +259,7 @@ Command::spawn() const { NULL, NULL, TRUE, - CREATE_NO_WINDOW, + 0, NULL, workingDirectory.empty() ? NULL : workingDirectory.string().c_str(), &si, @@ -283,8 +281,9 @@ Command::spawn() const { CloseHandle(pi.hThread); // Close thread handle return { pi.hProcess, - (int)(stdoutConfig == IOConfig::Piped ? (intptr_t)stdoutRead : -1), - (int)(stderrConfig == IOConfig::Piped ? (intptr_t)stderrRead : -1) }; + stdoutConfig == IOConfig::Piped ? stdoutRead : INVALID_HANDLE_VALUE, + stderrConfig == IOConfig::Piped ? stderrRead + : INVALID_HANDLE_VALUE }; #else std::array stdoutPipe{}; std::array stderrPipe{}; diff --git a/src/Command.hpp b/src/Command.hpp index c6c8cd33f..df8753bcf 100644 --- a/src/Command.hpp +++ b/src/Command.hpp @@ -11,9 +11,11 @@ #ifdef _WIN32 # include using process_handle = HANDLE; +using fd_t = HANDLE; #else # include using process_handle = pid_t; +using fd_t = int; #endif struct CommandOutput { @@ -25,10 +27,10 @@ struct CommandOutput { class Child { private: process_handle process; - int stdoutfd; - int stderrfd; + fd_t stdoutfd; + fd_t stderrfd; - Child(process_handle process, int stdoutfd, int stderrfd) noexcept + Child(process_handle process, fd_t stdoutfd, fd_t stderrfd) noexcept : process(process), stdoutfd(stdoutfd), stderrfd(stderrfd) {} friend struct Command; diff --git a/src/main.cc b/src/main.cc index a9810efdd..b9226e3b9 100644 --- a/src/main.cc +++ b/src/main.cc @@ -67,6 +67,7 @@ main(int argc, char* argv[]) { // ^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // [global] [run] [help (under run)] const std::span args(argv + 1, argv + argc); + for (auto itr = args.begin(); itr != args.end(); ++itr) { if (const auto res = Cli::handleGlobalOpts(itr, args.end())) { if (res.value() == Cli::CONTINUE) { From f1a5b40afce02bb01a5d7562bf15036261c3056c Mon Sep 17 00:00:00 2001 From: XBLACKICEX Date: Wed, 27 Nov 2024 21:34:04 +0100 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=90=9E=20fix:=20resolve=20build=20fai?= =?UTF-8?q?l=20on=20linux?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - corrected the `waitpid` function in `Command` class to use the `process` member properly. - updated `Child::waitWithOutput` to align return fields with consistent naming (`stdout_str` and `stderr_str`). --- src/Cmd/Build.cc | 9 +++++++++ src/Cmd/Build.hpp | 8 +------- src/Command.cc | 8 ++++---- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/Cmd/Build.cc b/src/Cmd/Build.cc index 808e449a1..c4bd5f0d0 100644 --- a/src/Cmd/Build.cc +++ b/src/Cmd/Build.cc @@ -18,6 +18,15 @@ #include #include +BuildSystem +getDefaultBuildSystem() { +#ifdef _WIN32 + return BuildSystem::Xmake; +#else + return BuildSystem::Makefile; +#endif +} + static int buildMain(std::span args); const Subcmd BUILD_CMD = diff --git a/src/Cmd/Build.hpp b/src/Cmd/Build.hpp index 58a36cad5..d2d8888b9 100644 --- a/src/Cmd/Build.hpp +++ b/src/Cmd/Build.hpp @@ -9,13 +9,7 @@ enum class BuildSystem { Xmake }; -static BuildSystem getDefaultBuildSystem() { -#ifdef _WIN32 - return BuildSystem::Xmake; -#else - return BuildSystem::Makefile; -#endif -} +extern BuildSystem getDefaultBuildSystem(); extern const Subcmd BUILD_CMD; int buildImpl(std::string& outDir, bool isDebug, BuildSystem system); diff --git a/src/Command.cc b/src/Command.cc index 953e3756f..a290e1804 100644 --- a/src/Command.cc +++ b/src/Command.cc @@ -54,7 +54,7 @@ Child::wait() const { return static_cast(exitCode); #else int status{}; - if (waitpid(pid, &status, 0) == -1) { + if (waitpid(process, &status, 0) == -1) { if (stdoutfd != -1) { close(stdoutfd); } @@ -191,14 +191,14 @@ Child::waitWithOutput() const { } int status{}; - if (waitpid(pid, &status, 0) == -1) { + if (waitpid(process, &status, 0) == -1) { throw PoacError("waitpid() failed"); } const int exitCode = WEXITSTATUS(status); return { .exitCode = exitCode, - .stdout = stdoutOutput, - .stderr = stderrOutput }; + .stdout_str = stdoutOutput, + .stderr_str = stderrOutput }; #endif } From ad214acf8a94c2c0ecccd893ea221dde9f8bb364 Mon Sep 17 00:00:00 2001 From: XBLACKICEX Date: Wed, 27 Nov 2024 21:36:40 +0100 Subject: [PATCH 4/4] =?UTF-8?q?=E2=9C=A8=20feat(cmake):=20add=20test=20sup?= =?UTF-8?q?port=20and=20improve=20build=20configuration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - introduced a testing framework with test targets for core components. - separated `poac` into a library (`poac_lib`) and executable for modularity. - add support for stricter compiler diagnostics and warnings on supported platforms. - updated CMake presets to simplify and unify build configurations for Ninja and Visual Studio. - refined `CMakeLists.txt` to include `clang++` as the default compiler on Unix if available. --- CMakeLists.txt | 56 ++++++++++++++++++++++++++++++++++++++--------- CMakePresets.json | 45 ++++++++++++++++--------------------- 2 files changed, 65 insertions(+), 36 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 72b3abd2b..014016fff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,13 @@ cmake_minimum_required(VERSION 3.15) + +if (UNIX) + find_program(CLANGPP clang++) + if (CLANGPP) + set(CMAKE_CXX_COMPILER ${CLANGPP}) + endif() +endif() + + file(READ "${CMAKE_CURRENT_SOURCE_DIR}/poac.toml" POAC_TOML_CONTENT) string(REGEX MATCH "([0-9]+\\.[0-9]+\\.[0-9]+)" version_match "${POAC_TOML_CONTENT}") @@ -12,6 +21,12 @@ project(poac VERSION ${version_match} LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") + add_compile_options(-fdiagnostics-color -pedantic-errors -Wall -Wextra -Wpedantic) +elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + # add_compile_options(/W4 /permissive-) +endif() + if("${CMAKE_GENERATOR}" STREQUAL "Ninja") set(CMAKE_EXPORT_COMPILE_COMMANDS ON) add_custom_target(copy_compile_commands ALL @@ -50,22 +65,16 @@ else() endif() file(GLOB_RECURSE SOURCES "src/*.cc") +list(REMOVE_ITEM SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/main.cc") -# target -add_executable(poac) - -target_sources(poac PRIVATE ${SOURCES}) +add_library(poac_lib ${SOURCES}) -target_compile_definitions(poac PRIVATE +target_compile_definitions(poac_lib PRIVATE POAC_POAC_PKG_VERSION="${PROJECT_VERSION}" POAC_POAC_COMMIT_HASH="${GIT_COMMIT_HASH}" POAC_POAC_COMMIT_SHORT_HASH="${GIT_COMMIT_SHORT_HASH}" POAC_POAC_COMMIT_DATE="${GIT_COMMIT_DATE}" NOMINMAX - PCRE2_CODE_UNIT_WIDTH=8 - PCRE2_STATIC - CURL_STATICLIB - __TBB_NO_IMPLICIT_LINKAGE ) # dependencies @@ -76,7 +85,7 @@ find_package(toml11 REQUIRED) find_package(CURL REQUIRED) find_package(unofficial-libgit2 CONFIG REQUIRED) -target_link_libraries(poac PRIVATE +target_link_libraries(poac_lib PUBLIC fmt::fmt nlohmann_json::nlohmann_json TBB::tbb @@ -84,3 +93,30 @@ target_link_libraries(poac PRIVATE CURL::libcurl unofficial::libgit2::libgit2 ) + +add_executable(poac "src/main.cc") +target_link_libraries(poac PRIVATE poac_lib) + +enable_testing() + +set(UNITTEST_SRCS + src/BuildConfig.cc + src/Algos.cc + src/Semver.cc + src/VersionReq.cc + src/Manifest.cc +) + +foreach(TEST_SRC ${UNITTEST_SRCS}) + get_filename_component(TEST_NAME ${TEST_SRC} NAME_WE) + set(TEST_EXEC "test_${TEST_NAME}") + add_executable(${TEST_EXEC} ${TEST_SRC}) + target_compile_definitions(${TEST_EXEC} PRIVATE + POAC_TEST + NOMINMAX + ) + target_link_libraries(${TEST_EXEC} PRIVATE + poac_lib + ) + add_test(NAME ${TEST_EXEC} COMMAND ${TEST_EXEC}) +endforeach() \ No newline at end of file diff --git a/CMakePresets.json b/CMakePresets.json index 59ac48d2d..d022e10a8 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -8,51 +8,44 @@ "binaryDir": "${sourceDir}/out/build/${presetName}", "cacheVariables": { "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}", - "CMAKE_C_COMPILER": "cl.exe", - "CMAKE_CXX_COMPILER": "cl.exe", "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" } }, { - "name": "Ninja-x64-msvc", + "name": "Ninja", + "description": "Ninja build system", "generator": "Ninja", - "binaryDir": "${sourceDir}/out/build/${presetName}", - "cacheVariables": { - "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}", - "CMAKE_C_COMPILER": "cl.exe", - "CMAKE_CXX_COMPILER": "cl.exe", - "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" - } + "inherits": "VS-x64-msvc" } ], "buildPresets": [ { "name": "VS-x64-msvc-debug", - "displayName": "Visual Studio Community 2022 Release - amd64 - Debug", "configurePreset": "VS-x64-msvc", - "configuration": "Debug", - "jobs": 16 + "configuration": "Debug" }, { "name": "VS-x64-msvc-release", - "displayName": "Visual Studio Community 2022 Release - amd64 - Release", "configurePreset": "VS-x64-msvc", - "configuration": "Release", - "jobs": 16 + "configuration": "Release" }, { - "name": "Ninja-x64-msvc-debug", - "displayName": "Ninja Release - amd64 - Debug", - "configurePreset": "Ninja-x64-msvc", - "configuration": "Debug", - "jobs": 16 + "name": "Ninja-debug", + "configurePreset": "Ninja", + "configuration": "Debug" }, { - "name": "Ninja-x64-msvc-release", - "displayName": "Ninja Release - amd64 - Release", - "configurePreset": "Ninja-x64-msvc", - "configuration": "Release", - "jobs": 16 + "name": "Ninja-release", + "configurePreset": "Ninja", + "configuration": "Release" + } + ], + "testPresets": [ + { + "name": "poac-Unitests", + "description": "", + "displayName": "", + "configurePreset": "Ninja" } ] } \ No newline at end of file