commit 1671aca835cbe60d56116fa198b3e8cf347eb875 Author: Lyra Date: Sun Jun 28 05:50:35 2026 -0400 Add project diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..f2c8263 --- /dev/null +++ b/.clang-format @@ -0,0 +1,357 @@ +--- +Language: Cpp +AlignAfterOpenBracket: true +AccessModifierOffset: -1 +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: true + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveShortCaseStatements: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCaseArrows: false + AlignCaseColons: false +AlignConsecutiveTableGenBreakingDAGArgColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveTableGenCondOperatorColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveTableGenDefinitionColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowBreakBeforeNoexceptSpecifier: Never +AllowBreakBeforeQtProperty: false +AllowShortBlocksOnASingleLine: Never +AllowShortCaseExpressionOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: false +AllowShortCompoundRequirementOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AllowShortNamespacesOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AttributeMacros: + - __capability + - absl_nonnull + - absl_nullable + - absl_nullability_unknown +BinPackArguments: true +BinPackLongBracedList: true +BinPackParameters: BinPack +BitFieldColonSpacing: Both +BracedInitializerIndentWidth: -1 +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAdjacentStringLiterals: true +BreakAfterAttributes: Leave +BreakAfterJavaFieldAnnotations: false +BreakAfterOpenBracketBracedList: false +BreakAfterOpenBracketFunction: false +BreakAfterOpenBracketIf: false +BreakAfterOpenBracketLoop: false +BreakAfterOpenBracketSwitch: false +BreakAfterReturnType: None +BreakArrays: true +BreakBeforeBinaryOperators: None +BreakBeforeCloseBracketBracedList: false +BreakBeforeCloseBracketFunction: false +BreakBeforeCloseBracketIf: false +BreakBeforeCloseBracketLoop: false +BreakBeforeCloseBracketSwitch: false +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Attach +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTemplateCloser: false +BreakBeforeTernaryOperators: true +BreakBinaryOperations: Never +BreakConstructorInitializers: BeforeColon +BreakFunctionDefinitionParameters: false +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +BreakTemplateDeclarations: Yes +ColumnLimit: 80 +CommentPragmas: "^ IWYU pragma:" +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +#Cpp11BracedListStyle: AlignFirstComment +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +EnumTrailingComma: Leave +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^<.*\.h>' + Priority: 1 + SortPriority: 0 + CaseSensitive: false + - Regex: "^<.*" + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: ".*" + Priority: 3 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: "([-_](test|unittest))?$" +IncludeIsMainSourceRegex: "" +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: true +IndentExportBlock: true +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertNewlineAtEOF: false +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigitsInsert: 0 + BinaryMaxDigitsRemove: 0 + Decimal: 0 + DecimalMinDigitsInsert: 0 + DecimalMaxDigitsRemove: 0 + Hex: 0 + HexMinDigitsInsert: 0 + HexMaxDigitsRemove: 0 + BinaryMinDigits: 0 + DecimalMinDigits: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLines: + AtEndOfFile: false + AtStartOfBlock: false + AtStartOfFile: true +KeepFormFeed: false +LambdaBodyIndentation: Signature +LineEnding: DeriveLF +MacroBlockBegin: "" +MacroBlockEnd: "" +MainIncludeChar: Quote +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +NumericLiteralCase: + ExponentLetter: Leave + HexDigit: Leave + Prefix: Leave + Suffix: Leave +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 4 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +OneLineFormatOffRegex: "" +PackConstructorInitializers: NextLine +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakBeforeMemberAccess: 150 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakScopeResolution: 500 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +PPIndentWidth: -1 +QualifierAlignment: Leave +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - "c++" + - "C++" + CanonicalDelimiter: "" + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + - ParseTestProto + - ParsePartialTestProto + CanonicalDelimiter: pb + BasedOnStyle: google +ReferenceAlignment: Pointer +ReflowComments: Always +RemoveBracesLLVM: false +RemoveEmptyLinesInUnwrappedLines: false +RemoveParentheses: Leave +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SkipMacroDefinitionBody: false +SortIncludes: + Enabled: true + IgnoreCase: false + IgnoreExtension: false +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterOperatorKeyword: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeJsonColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterNot: false + AfterOverloadedOperator: false + AfterPlacementOperator: true + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBraces: Never +SpacesBeforeTrailingComments: 2 +SpacesInAngles: Never +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParens: Never +SpacesInParensOptions: + ExceptDoubleParentheses: false + InCStyleCasts: false + InConditionalStatements: false + InEmptyParentheses: false + Other: false +SpacesInSquareBrackets: false +Standard: Auto +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TableGenBreakInsideDAGArg: DontBreak +TabWidth: 8 +UseTab: Never +VerilogBreakBetweenInstancePorts: true +WhitespaceSensitiveMacros: + - BOOST_PP_STRINGIZE + - CF_SWIFT_NAME + - NS_SWIFT_NAME + - PP_STRINGIZE + - STRINGIZE +WrapNamespaceBodyWithEmptyLines: Leave +... + diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..adedb95 --- /dev/null +++ b/.clangd @@ -0,0 +1,3 @@ +CompileFlags: + Add: -Wno-unknown-warning-option + Remove: [-m*, -f*] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..12efd86 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +build/ +.idea +build_script +build_script.exe + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8c02301 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 4.2) +project(build_script) + +set(CMAKE_CXX_STANDARD 26) +set(CMAKE_POLICY_VERSION_MINIMUM 3.5) + +file(GLOB REQUIRED_CMAKE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/cmake/*.required.cmake") +foreach (cmake_file ${REQUIRED_CMAKE_FILES}) + include(${cmake_file}) +endforeach () + +file(GLOB CMAKE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/cmake/*.cmake") +foreach (cmake_file ${CMAKE_FILES}) + include(${cmake_file}) +endforeach () + +include_directories(src/) + +file(GLOB_RECURSE SRC_FILES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") +file(GLOB_RECURSE HEADER_FILES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.h") +add_executable(${PROJECT_NAME} ${SRC_FILES} ${HEADER_FILES}) + +target_include_directories(${PROJECT_NAME} PUBLIC + "${structops_SOURCE_DIR}/include" + "${spdlog_SOURCE_DIR}/include" +) + +target_link_libraries(${PROJECT_NAME} PUBLIC + structopt + spdlog +) diff --git a/cmake/CPM.cmake b/cmake/CPM.cmake new file mode 100644 index 0000000..9a66dd8 --- /dev/null +++ b/cmake/CPM.cmake @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: MIT +# +# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors + +set(CPM_DOWNLOAD_VERSION 0.42.1) +set(CPM_HASH_SUM "f3a6dcc6a04ce9e7f51a127307fa4f699fb2bade357a8eb4c5b45df76e1dc6a5") + +if(CPM_SOURCE_CACHE) + set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +elseif(DEFINED ENV{CPM_SOURCE_CACHE}) + set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +else() + set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +endif() + +# Expand relative path. This is important if the provided path contains a tilde (~) +get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE) + +file(DOWNLOAD + https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake + ${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM} +) + +include(${CPM_DOWNLOAD_LOCATION}) diff --git a/cmake/compileCommands.cmake b/cmake/compileCommands.cmake new file mode 100644 index 0000000..38fa25b --- /dev/null +++ b/cmake/compileCommands.cmake @@ -0,0 +1,14 @@ +##### SETUP COMPILE COMMANDS ##### + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CMAKE_EXPORT_COMPILE_COMMANDS on) +endif() + +if(EXISTS "${CMAKE_BINARY_DIR}/compile_commands.json") + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + file( + COPY "${CMAKE_BINARY_DIR}/compile_commands.json" + DESTINATION "${CMAKE_BINARY_DIR}/../" + ) + endif() +endif() diff --git a/cmake/platform.cmake b/cmake/platform.cmake new file mode 100644 index 0000000..0791c94 --- /dev/null +++ b/cmake/platform.cmake @@ -0,0 +1,10 @@ +message("System name: ${CMAKE_HOST_SYSTEM_NAME}") +if(${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows") + add_compile_definitions(PLATFORM_WINDOWS) +elseif(${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Linux") + add_compile_definitions(PLATFORM_LINUX) +elseif(${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Darwin") + add_compile_definitions(PLATFORM_MACOS) +else() + add_compile_definitions(PLATFORM_UNKNOWN) +endif() diff --git a/cmake/spdlog.cmake b/cmake/spdlog.cmake new file mode 100644 index 0000000..862fbd4 --- /dev/null +++ b/cmake/spdlog.cmake @@ -0,0 +1,3 @@ +CPMAddPackage( + URI "gh:gabime/spdlog@1.17.0" +) diff --git a/cmake/structopt.cmake b/cmake/structopt.cmake new file mode 100644 index 0000000..c0cec9d --- /dev/null +++ b/cmake/structopt.cmake @@ -0,0 +1,5 @@ +CPMAddPackage( + URI "gh:p-ranav/structopt@0.1.3" + OPTIONS "STRUCTOPT_TESTS off" + OPTIONS "STRUCTOPT_SAMPLES off" +) \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..96e4013 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,437 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef _WIN32 +#define popen _popen +#define pclose _pclose +#endif + +enum Command { + build, + configure, + clean, + del, + rebuild, +}; + +enum BuildType { + none, + debug, + release, + relwithdeb, +}; + +struct Options { + Command command; + std::optional buildType = BuildType::none; + + std::optional verbose = false; + std::optional dryrun = false; +}; +STRUCTOPT(Options, command, buildType, verbose, dryrun); + +bool g_DryRun = false; + +void initLogging() { + spdlog::init_thread_pool(8192, 1); + spdlog::flush_every(std::chrono::seconds(1)); +} + +void createLogger(const char* name, const char* filename, const bool verbose) { + std::vector sinks; + + const auto stdout_sink = + std::make_shared(); + stdout_sink->set_level(spdlog::level::debug); + stdout_sink->set_pattern("%^[%D %r %z] [%n] [%l] %v%$"); + sinks.push_back(stdout_sink); + + if (filename != nullptr) { + const auto file_sink = + std::make_shared(filename); + file_sink->set_level(spdlog::level::debug); + file_sink->set_pattern("[%D %r %z] [%n] [%l] %v"); + sinks.push_back(file_sink); + } + + const auto logger = std::make_shared( + name, sinks.begin(), sinks.end(), spdlog::thread_pool(), + spdlog::async_overflow_policy::block); + logger->flush_on(spdlog::level::warn); + +#ifdef DEBUG + logger->set_level(spdlog::level::debug); +#else + if (verbose) + logger->set_level(spdlog::level::debug); + else + logger->set_level(spdlog::level::info); +#endif + + spdlog::register_logger(logger); +} + +void deleteDir(const std::filesystem::path& path) { + if (g_DryRun) { + spdlog::get("filesystem") + ->warn("[DRYRUN] Deleting '{}'", path.string()); + return; + } + + spdlog::get("filesystem")->warn("DELETING '{}' directory.", path.string()); + spdlog::get("filesystem")->debug("Checking '{}'", path.string()); + if (!std::filesystem::exists(path) || + !std::filesystem::is_directory(path)) { + spdlog::get("filesystem") + ->info( + "The {} directory either doesn't exist or its a file. " + "Continuing...", + path.string()); + return; + } + + spdlog::get("filesystem")->debug("Deleting '{}'", path.string()); + std::filesystem::remove_all(path); +} + +void createDir(const std::filesystem::path& path) { + if (g_DryRun) { + spdlog::get("filesystem") + ->warn("[DRYRUN] Creating '{}'", path.string()); + return; + } + + spdlog::get("filesystem")->debug("Creating '{}'", path.string()); + if (const bool res = std::filesystem::create_directories(path); !res) { + spdlog::get("filesystem") + ->warn("Failed to create directory: '{}'", path.string()); + } +} + +void checkBuildDirs(const BuildType buildType) { + spdlog::get("filesystem")->debug("Checking 'build' directory."); + if (!std::filesystem::is_directory("build")) { + spdlog::get("filesystem") + ->warn("'build' is not a directory. Deleting."); + deleteDir("build"); + } + if (!std::filesystem::exists("build")) { + spdlog::get("filesystem")->info("Creating 'build' directory"); + createDir("build"); + } + + switch (buildType) { + case none: + spdlog::get("filesystem") + ->debug("Checking 'build/debug' directory."); + if (!std::filesystem::is_directory("build/debug")) { + spdlog::get("filesystem") + ->warn("'build/debug' is not a directory. Deleting."); + deleteDir("build/debug"); + } + if (!std::filesystem::exists("build/debug")) { + spdlog::get("filesystem") + ->info("Creating 'build/debug' directory."); + createDir("build/debug"); + } + + spdlog::get("filesystem") + ->debug("Checking 'build/release' directory."); + if (!std::filesystem::is_directory("build/release")) { + spdlog::get("filesystem") + ->warn("'build/release' is not a directory. Deleting."); + deleteDir("build/release"); + } + if (!std::filesystem::exists("build/release")) { + spdlog::get("filesystem") + ->info("Creating 'build/release' directory."); + createDir("build/release"); + } + + spdlog::get("filesystem") + ->debug("Checking 'build/relwithdeb' directory."); + if (!std::filesystem::is_directory("build/relwithdeb")) { + spdlog::get("filesystem") + ->warn("'build/relwithdeb' is not a directory. Deleting."); + deleteDir("build/relwithdeb"); + } + if (!std::filesystem::exists("build/relwithdeb")) { + spdlog::get("filesystem") + ->info("Creating 'build/relwithdeb' directory."); + createDir("build/relwithdeb"); + } + break; + case debug: + spdlog::get("filesystem") + ->debug("Checking 'build/debug' directory."); + if (!std::filesystem::is_directory("build/debug")) { + spdlog::get("filsystem") + ->warn("'build/debug' is not a directory. Deleting."); + deleteDir("build/debug"); + } + if (!std::filesystem::exists("build/debug")) { + spdlog::get("filesystem") + ->info("Creating 'build/debug' directory."); + createDir("build/debug"); + } + break; + case release: + spdlog::get("filesystem") + ->debug("Checking 'build/release' directory."); + if (!std::filesystem::is_directory("build/release")) { + spdlog::get("filesystem") + ->warn("'build/release' is not a directory. Deleting."); + deleteDir("build/release"); + } + if (!std::filesystem::exists("build/release")) { + spdlog::get("filesystem") + ->info("Creating 'build/release' directory."); + createDir("build/release"); + } + break; + case relwithdeb: + spdlog::get("filesystem") + ->debug("Checking 'build/relwithdeb' directory."); + if (!std::filesystem::is_directory("build/relwithdeb")) { + spdlog::get("filesystem") + ->warn("'build/relwithdeb' is not a directory. Deleting."); + deleteDir("build/relwithdeb"); + } + if (!std::filesystem::exists("build/relwithdeb")) { + spdlog::get("filesystem") + ->info("Creating 'build/relwithdeb' directory."); + createDir("build/relwithdeb"); + }; + } + + spdlog::get("filesystem")->info("All directories checked."); +} + +int runCmd(std::string cmd) { + if (g_DryRun) { + spdlog::get("cmd")->warn("[DRYRUN] Running '{}'", cmd); + return 0; + } + + spdlog::get("cmd")->debug("Running '{}'", cmd); + spdlog::get("cmd")->debug("Opening pipe"); + FILE* pipe = popen(cmd.c_str(), "r"); + if (!pipe) { + spdlog::get("cmd")->warn("Failed to start the process"); + return 1; + } + + spdlog::get("cmd")->debug("Buffering and offloading pipe"); + std::array buffer; + while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) { + std::cout << buffer.data() << std::flush; + } + + spdlog::get("filesystem")->debug("Closing pipe"); + int exitCode = pclose(pipe); + spdlog::get("cmd")->info("Process ended with exit code: {}", exitCode); + + return exitCode; +} + +void buildDir(const std::filesystem::path& path) { + if (g_DryRun) { + spdlog::get("build")->warn("[DRYRUN] Building '{}'", path.string()); + return; + } + + spdlog::get("build")->info("Building '{}'", path.string()); + if (int exitCode = runCmd(std::format("cd {} && ninja", path.string())); + exitCode != 0) { + spdlog::get("build")->critical("Command exited with exit code: {}", + exitCode); + exit(EXIT_FAILURE); + } +} + +void configureDir(const std::filesystem::path& path, std::string buildStr) { + if (g_DryRun) { + spdlog::get("configure") + ->warn("[DRYRUN] Configuring '{}'", path.string()); + return; + } + + spdlog::get("configure")->info("Configuring '{}'", path.string()); + if (int exitCode = runCmd( + std::format("cd {} && cmake ../../ -G Ninja -DCMAKE_BUILD_TYPE={}", + path.string(), buildStr)); + exitCode != 0) { + spdlog::get("configure") + ->critical("Command exited with exit code: {}", exitCode); + exit(EXIT_FAILURE); + } +} + +void cleanDir(const std::filesystem::path& path) { + if (g_DryRun) { + spdlog::get("clean")->warn("[DRYRUN] Cleaning '{}'", path.string()); + return; + } + + spdlog::get("clean")->info("Cleaning '{}'", path.string()); + if (int exitCode = + runCmd(std::format("cd {} && cmake clean", path.string())); + exitCode != 0) { + spdlog::get("clean")->critical("Command exited with exit code: {}", + exitCode); + exit(EXIT_FAILURE); + } +} + +void buildProject(const BuildType buildType) { + checkBuildDirs(buildType); + switch (buildType) { + case none: + configureDir("build/debug", "Debug"); + buildDir("build/debug"); + configureDir("build/release", "Release"); + buildDir("build/release"); + configureDir("build/relwithdeb", "RelWithDeb"); + buildDir("build/relwithdeb"); + break; + case debug: + configureDir("build/debug", "Debug"); + buildDir("build/debug"); + break; + case release: + configureDir("build/release", "Release"); + buildDir("build/release"); + break; + case relwithdeb: + configureDir("build/relwithdeb", "RelWithDeb"); + buildDir("build/relwithdeb"); + break; + } +} + +void configureProject(const BuildType buildType) { + checkBuildDirs(buildType); + switch (buildType) { + case none: + configureDir("build/debug", "Debug"); + configureDir("build/release", "Release"); + configureDir("build/relwithdeb", "RelWithDeb"); + break; + case debug: + configureDir("build/debug", "Debug"); + break; + case release: + configureDir("build/release", "Release"); + break; + case relwithdeb: + configureDir("build/RelWithDeb", "RelWithDeb"); + break; + } +} + +void cleanProject(const BuildType buildType) { + checkBuildDirs(buildType); + + switch (buildType) { + case none: + cleanDir("build/debug"); + cleanDir("build/release"); + cleanDir("build/relwithdeb"); + break; + case debug: + cleanDir("build/debug"); + break; + case release: + cleanDir("build/release"); + break; + case relwithdeb: + cleanDir("build/RelWithDeb"); + break; + } +} + +void deleteProject(const BuildType buildType) { + switch (buildType) { + case none: + deleteDir("build"); + break; + case debug: + deleteDir("build/debug"); + break; + case release: + deleteDir("build/release"); + break; + case relwithdeb: + deleteDir("build/relwithdeb"); + break; + } +} + +void rebuildProject(const BuildType buildType) { + deleteProject(buildType); + checkBuildDirs(buildType); + configureProject(buildType); + buildProject(buildType); +} + +int main(const int argc, char** argv) { + Options options; + try { + options = structopt::app(argv[0], "1.0.0").parse(argc, argv); + } catch (structopt::exception& e) { + std::cout << e.what() << std::endl; + std::cout << e.help(); + exit(EXIT_FAILURE); + } + + assert(options.verbose.has_value()); + + assert(options.dryrun.has_value()); + assert(options.buildType.has_value()); + + g_DryRun = options.dryrun.value(); + + initLogging(); + createLogger("app", nullptr, options.verbose.value()); + spdlog::get("app")->debug("Creating logger: 'build'."); + createLogger("build", nullptr, options.verbose.value()); + spdlog::get("app")->debug("Creating logger: 'configure'."); + createLogger("configure", nullptr, options.verbose.value()); + spdlog::get("app")->debug("Creating logger: 'clean'."); + createLogger("clean", nullptr, options.verbose.value()); + spdlog::get("app")->debug("Creating logger: 'filesystem'."); + createLogger("filesystem", nullptr, options.verbose.value()); + spdlog::get("app")->debug("Creating logger: 'cmd'."); + createLogger("cmd", nullptr, options.verbose.value()); + spdlog::get("app")->debug("Loggers created."); + + spdlog::get("app")->debug("Running command"); + switch (options.command) { + case build: + buildProject(options.buildType.value()); + break; + case configure: + configureProject(options.buildType.value()); + break; + case clean: + cleanProject(options.buildType.value()); + break; + case del: + deleteProject(options.buildType.value()); + break; + case rebuild: + rebuildProject(options.buildType.value()); + break; + } + + spdlog::get("app")->info("All work done!"); + + return 0; +}