diff --git a/.gitignore b/.gitignore index 7ae8fcd..80855a9 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ builds/ Builds/ Build/ +build/ v0.*/ *.bak *.runcpp2/ @@ -59,5 +60,5 @@ compile_commands.json # Generated file Src/runcpp2/DefaultYAMLs.c -Include/cfgpath.h +Src/cfgpath.h .aider* diff --git a/.gitmodules b/.gitmodules index a963546..68637c3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -34,3 +34,6 @@ [submodule "DSResult"] path = External/DSResult url = https://github.com/Neko-Box-Coder/DSResult.git +[submodule "System2.cpp"] + path = External/System2.cpp + url = https://github.com/Neko-Box-Coder/System2.cpp.git diff --git a/CMakeLists.txt b/CMakeLists.txt index d2d7213..20a050f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,7 +112,9 @@ set(DS_USE_DEBUG_BREAK ON) add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/External/DSResult") set(BUILD_TESTING ON) -add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/External/libyaml") +# add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/External/libyaml") +add_library(libyaml INTERFACE) +target_include_directories(libyaml INTERFACE "${CMAKE_CURRENT_LIST_DIR}/External/libyaml/include") set(BUILD_TESTING OFF) # NOTE: Just include the file. Can't add subdirectory since it install stuff @@ -123,7 +125,7 @@ target_include_directories( string-view-lite INTERFACE # Copy cfgpath.h configure_file( "${CMAKE_CURRENT_LIST_DIR}/External/cfgpath/cfgpath.h" - "${CMAKE_CURRENT_LIST_DIR}/Include/cfgpath.h" COPYONLY) + "${CMAKE_CURRENT_LIST_DIR}/Src/cfgpath.h" COPYONLY) add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/External/CppOverride") @@ -177,49 +179,21 @@ endif() # runcpp2 library # ========================================================================= -set(RUNCPP2_SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/Data/Profile.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/Data/DependencyInfo.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/Data/DependencyLinkProperty.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/Data/DependencySource.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/Data/GitSource.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/Data/LocalSource.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/Data/FileProperties.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/Data/FlagsOverrideInfo.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/Data/ProfilesCommands.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/Data/ProfilesFlagsOverride.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/Data/ScriptInfo.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/Data/ProfilesProcessPaths.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/Data/FilesTypesInfo.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/Data/StageInfo.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/Data/ProfilesDefines.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/Data/FilesToCopyInfo.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/Data/BuildTypeHelper.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/ProfileHelper.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/CompilingLinking.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/ConfigParsing.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/DefaultYAMLs.c" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/DependenciesHelper.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/ParseUtil.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/PlatformUtil.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/StringUtil.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/runcpp2.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/BuildsManager.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/PipelineSteps.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/IncludeManager.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/LibYAML_Wrapper.cpp") - -add_library(runcpp2Lib STATIC ${RUNCPP2_SOURCE_FILES}) +set(RUNCPP2_SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/runcpp2.cpp") + +# add_library(runcpp2Lib STATIC ${RUNCPP2_SOURCE_FILES}) +add_library(runcpp2Lib INTERFACE) set(RUNCPP2_PRIVATE_LINK_LIBS ssLogger System2 CppOverride dylib) -set(RUNCPP2_PUBLIC_LINK_LIBS ghc_filesystem mpark_variant MacroPowerToys string-view-lite yaml DSResult) +set(RUNCPP2_PUBLIC_LINK_LIBS ghc_filesystem mpark_variant MacroPowerToys string-view-lite libyaml DSResult) +target_include_directories(runcpp2Lib INTERFACE "${CMAKE_CURRENT_LIST_DIR}/Src") +target_link_libraries(runcpp2Lib INTERFACE ${RUNCPP2_PRIVATE_LINK_LIBS}) +target_link_libraries(runcpp2Lib INTERFACE ${RUNCPP2_PUBLIC_LINK_LIBS}) -target_include_directories(runcpp2Lib PUBLIC "${CMAKE_CURRENT_LIST_DIR}/Include") -target_link_libraries(runcpp2Lib PRIVATE ${RUNCPP2_PRIVATE_LINK_LIBS}) -target_link_libraries(runcpp2Lib PUBLIC ${RUNCPP2_PUBLIC_LINK_LIBS}) if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") # TODO: Try to change to /Wall - set(RUNCPP2_STANDARD_COMPILE_FLAGS "/utf-8;/W1;/DGHC_WIN_DISABLE_WSTRING_STORAGE_TYPE=1") + set(RUNCPP2_STANDARD_COMPILE_FLAGS "/utf-8;/W1;/DGHC_WIN_DISABLE_WSTRING_STORAGE_TYPE=1;/bigobj") if (RUNCPP2_WARNINGS_AS_ERRORS) list(APPEND RUNCPP2_STANDARD_COMPILE_FLAGS "/WX") endif() @@ -233,7 +207,7 @@ else() #"-Wno-unused-but-set-variable" "-Wno-unused-parameter" "-Wno-switch" - "-Wno-gnu-zero-variadic-macro-arguments" + # "-Wno-gnu-zero-variadic-macro-arguments" "-Wextra" "-Wpedantic") if (RUNCPP2_WARNINGS_AS_ERRORS) @@ -241,34 +215,25 @@ else() endif() endif() -target_compile_options(runcpp2Lib PRIVATE "${RUNCPP2_STANDARD_COMPILE_FLAGS}") +target_compile_options(runcpp2Lib INTERFACE "${RUNCPP2_STANDARD_COMPILE_FLAGS}") # Define the runcpp2 and default config version macro -target_compile_definitions(runcpp2Lib PUBLIC RUNCPP2_VERSION="${RUNCPP2_PROJECT_VERSION}" - RUNCPP2_CONFIG_VERSION=${RUNCPP2_CONFIG_VERSION}) +target_compile_definitions(runcpp2Lib INTERFACE RUNCPP2_VERSION="${RUNCPP2_PROJECT_VERSION}" + RUNCPP2_CONFIG_VERSION=${RUNCPP2_CONFIG_VERSION} + YAML_DECLARE_STATIC=1) # ========================================================================= # runcpp2 executable # ========================================================================= add_executable(runcpp2 "${CMAKE_CURRENT_LIST_DIR}/Src/runcpp2/main.cpp") -target_compile_options(runcpp2 PRIVATE "${RUNCPP2_STANDARD_COMPILE_FLAGS}") -target_link_libraries(runcpp2 PUBLIC runcpp2Lib ssLogger) +target_link_libraries(runcpp2 PUBLIC runcpp2Lib) # ========================================================================= # runcpp2 library override # ========================================================================= if(RUNCPP2_BUILD_TESTS) - add_library(runcpp2Override STATIC ${RUNCPP2_SOURCE_FILES}) - target_include_directories(runcpp2Override PUBLIC "${CMAKE_CURRENT_LIST_DIR}/Include") - target_link_libraries(runcpp2Override PRIVATE ${RUNCPP2_PRIVATE_LINK_LIBS}) - target_link_libraries(runcpp2Override PUBLIC ${RUNCPP2_PUBLIC_LINK_LIBS}) - target_compile_options(runcpp2Override PRIVATE "${RUNCPP2_STANDARD_COMPILE_FLAGS}") - target_compile_definitions( runcpp2Override PUBLIC - RUNCPP2_VERSION="${RUNCPP2_PROJECT_VERSION}" - RUNCPP2_CONFIG_VERSION=${RUNCPP2_CONFIG_VERSION} - INTERNAL_RUNCPP2_UNIT_TESTS=1) add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Src/Tests") endif() diff --git a/DefaultYAMLs/DefaultScriptInfo.yaml b/DefaultYAMLs/DefaultScriptInfo.yaml index 0d6c98d..864484e 100644 --- a/DefaultYAMLs/DefaultScriptInfo.yaml +++ b/DefaultYAMLs/DefaultScriptInfo.yaml @@ -1,251 +1,239 @@ -# This is the template for specifying build settings. -# Many of the settings are passed directly to the shell. -# Be cautious when using user-provided input in your build commands to avoid potential security risks. -# Output from commands such as Setup or Cleanup won't be shown unless log level is set to info. -# If the default is not mentioned for a setting, it will be empty. - -# Each of the platform dependent settings can be listed under -# - DefaultPlatform -# - Windows -# - Linux -# - MacOS -# - Unix - -# You can find all the profiles in your config folder. -# This can be found by running `runcpp2 --show-config-path`. -# Specifying "DefaultProfile" in the profile name will allow any profiles -# and use the user's preferred one. - -# (Optional) Whether to pass the script path as the second parameter when running. Default is false -PassScriptPath: false - -# (Optional) Language of the script. Default is determined by file extension -Language: "c++" - -# (Optional) The type of output to build. Default is Executable -# Supported types: -# - Executable: Build as executable that can be run -# - Static: Build as static library (.lib/.a) -# - Shared: Build as shared library (.dll/.so) -# - Objects: Only compile to object files without linking -BuildType: Executable - -# TODO: Rename this -# (Optional) Allowed profiles for the script for each platform. -# Any profiles will be used if none is specified for the platform. -RequiredProfiles: - Windows: ["g++"] - Linux: ["g++"] - MacOS: ["g++"] - -# (Optional) Override the default compile flags for each platform. -OverrideCompileFlags: - # Target Platform - DefaultPlatform: - # Profile with the respective flags to override - "g++": - # (Optional) Flags to be removed from the default compile flags, separated by space - Remove: "" - - # (Optional) Additional flags to be appended to the default compile flags, separated by space - Append: "" - +# # This is an example script info. Comments have 2 leading `#`, while a valid config has only 1 leading `#` + +# # This is the template for specifying build settings. +# # Many of the settings are passed directly to the shell. +# # Be cautious when using user-provided input in your build commands to avoid potential security risks. +# # Output from commands such as Setup or Cleanup won't be shown unless log level is set to info. +# # If the default is not mentioned for a setting, it will be empty. + +# # Each of the platform dependent settings can be listed under +# # - DefaultPlatform +# # - Windows +# # - Linux +# # - MacOS +# # - Unix + +# # You can find all the profiles in your config folder. +# # This can be found by running `runcpp2 --show-config-path`. +# # Specifying "DefaultProfile" in the profile name will allow any profiles +# # and use the user's preferred one. + +# # (Optional) Whether to pass the script path as the second parameter when running. Default is false +# PassScriptPath: false + +# # (Optional) Language of the script. Default is determined by file extension +# Language: "c++" + +# # (Optional) The type of output to build. Default is Executable +# # Supported types: +# # - Executable: Build as executable that can be run +# # - Static: Build as static library (.lib/.a) +# # - Shared: Build as shared library (.dll/.so) +# # - Objects: Only compile to object files without linking +# BuildType: Executable + +# # TODO: Rename this +# # (Optional) Allowed profiles for the script for each platform. +# # Any profiles will be used if none is specified for the platform. Default to none +# RequiredProfiles: +# Windows: ["g++"] +# Linux: ["g++"] +# MacOS: ["g++"] + +# # (Optional) Override the default compile flags for each platform. +# OverrideCompileFlags: +# # Target Platform +# DefaultPlatform: +# # Profile with the respective flags to override +# "g++": +# # (Optional) Flags to be removed from the default compile flags, separated by space +# Remove: "" +# +# # (Optional) Additional flags to be appended to the default compile flags, separated by space +# Append: "" + # (Optional) Override the default link flags for each platform. -OverrideLinkFlags: - # Target Platform - DefaultPlatform: - # Profile with the respective flags to override - "g++": - # (Optional) Flags to be removed from the default link flags, separated by space - Remove: "" - - # (Optional) Additional flags to be appended to the default link flags, - # separated by space - Append: "" - -# (Optional) Other source files (relative to script file path) to be compiled. -SourceFiles: - # Target Platform - DefaultPlatform: - # Target Profile - DefaultProfile: - - "./AnotherSourceFile.cpp" - -# (Optional) Include paths (relative to script file path) for each platform and profile -IncludePaths: - # Target Platform - DefaultPlatform: - # Target Profile - DefaultProfile: - - "./include" - - "./src/include" - -# (Optional) Define cross-compiler defines for each platform and profile. -# Defines can be specified as just a name or as a name-value pair. -Defines: - # Target Platform - DefaultPlatform: - # Profile name - DefaultProfile: - - "EXAMPLE_DEFINE" # Define without a value - - "VERSION_MAJOR=1" # Define with a value - -# (Optional) Setup commands are run once before the script is first built. -# These commands are run at the script's location when no build directory exists. -Setup: - # Target Platform - DefaultPlatform: - # Profile name - DefaultProfile: - # List of setup commands - - "echo Setting up script..." - -# (Optional) PreBuild commands are run before each build. -# These commands are run in the build directory before compilation starts. -PreBuild: - # Target Platform - DefaultPlatform: - # Profile name - DefaultProfile: - - "echo Starting build..." - -# (Optional) PostBuild commands are run after each successful build. -# These commands are run in the output directory where binaries are located. -PostBuild: - # Target Platform - DefaultPlatform: - # Profile name - DefaultProfile: - - "echo Build completed..." - -# (Optional) Cleanup commands are run when using the --cleanup option. -# These commands are run at the script's location before the build directory is removed. -Cleanup: - # Target Platform - DefaultPlatform: - # Profile name - DefaultProfile: - - "echo Cleaning up script..." - -# (Optional) The list of dependencies needed by the script -Dependencies: - # Dependency name -- Name: MyLibrary - - # Supported platforms of the dependency - Platforms: [Windows, Linux, MacOS] - - # Where to get and copy the dependency (Git, Local) - # Either Git or Local can exist, not both - Source: - # (Optional) Import dependency configuration from a YAML file if this field exists - # All other fields (Name, Platforms, etc...) are not needed if this field exists - # For Git source: Path is relative to the git repository root - # For Local source: Path is relative to the path specified under `Local` - # If neither source exists, local source with root script directory is assumed. - ImportPath: "config/dependency.yaml" - - # Dependency or import YAML file exists in a git server, and needs to be cloned to build directory - Git: - # Git repository URL - URL: "https://github.com/MyUser/MyLibrary.git" - - # (Optional) Branch name or tag name - # Defaults to default branch on specified git repo if this is not specified - # Branch: "" - - # (Optional) Checkout full git history or just the target commit. Defaults to false - # FullHistory: false - - # (Optional) Initialization type for all the submodules recursively - # - "None": Don't initialize any submodules - # - "Shallow": Only checkout the target commit of all the submodules (default) - # - "Full": Checkout the full git history of all the submodules - # SubmoduleInitType: "Shallow" - - # Dependency or import YAML file exists in local filesystem directory, - # and needs to be copied to build directory - Local: - # Path to the library directory - Path: "./libs/LocalLibrary" - - # (Optional) How to handle copying files to build directory - # Values: - # - "Auto" (default): Try symlink first, then hardlink, then copy as fallback - # - "Symlink": Create symbolic links only, fail if not possible - # - "Hardlink": Create hard links only, fail if not possible - # - "Copy": Copy files to build directory - CopyMode: "Auto" - - # Library Type (Static, Object, Shared, Header) - LibraryType: Static - - # (Optional) Paths to be added to the include paths, relative to the dependency folder - IncludePaths: - - "src/include" - - # (Optional if LibraryType is Header) Link properties of the dependency - LinkProperties: - # Properties for searching the library binary for each platform - DefaultPlatform: - # Profile-specific properties - "g++": - # The library names to be searched for when linking against the script. - # Binaries with linkable extension that contains one of the names will be linked - SearchLibraryNames: ["MyLibrary"] - - # (Optional) The library names to be excluded from being searched. - # Works the same as SearchLibraryNames but will NOT be linked instead - ExcludeLibraryNames: [] - - # The path (relative to the dependency folder) to be searched for the dependency binaries - SearchDirectories: ["./build"] - - # (Optional) Additional link flags for this dependency - AdditionalLinkOptions: [] - - # (Optional) Setup commands are run once when the dependency is populated - Setup: - # Target Platform - DefaultPlatform: - # Setup shell commands for the specified profile. - # Default commands are run in the dependency folder - # You can also use "DefaultProfile" if all the compilers run the same setup commands - "g++": - - "mkdir build" - - - # (Optional) Build commands are run every time before the script is being built - Build: - # Target Platform - DefaultPlatform: - # Target Profile - "g++": - - "cd build && cmake .." - - "cd build && cmake --build ." - - # (Optional) Cleanup commands are run when the reset option is present. Normally nothing needs - # to be done since the dependency folder will be removed automatically. - Cleanup: - # Target Platform - Linux: - # Target Profile - "g++": - - "sudo apt purge MyLibrary" - - # (Optional) Files to be copied to next to output binary for each platform and profile - FilesToCopy: - # Target Platform - DefaultPlatform: - # Profile name - DefaultProfile: - # List of files to copy (relative to the dependency folder) - - "assets/textures/sprite.png" - Windows: - "msvc": - - "assets/textures/sprite.png" - - "assets/fonts/windows_specific_font.ttf" - Linux: - "g++": - - "assets/textures/sprite.png" - - "assets/shaders/linux_optimized_shader.glsl" +# OverrideLinkFlags: +# # Target Platform +# DefaultPlatform: +# # Profile with the respective flags to override +# "g++": +# # (Optional) Flags to be removed from the default link flags, separated by space +# Remove: "" +# +# # (Optional) Additional flags to be appended to the default link flags, +# # separated by space +# Append: "" + +# # (Optional) Other source files (relative to script file path) to be compiled. +# SourceFiles: +# DefaultPlatform: # Target Platform +# DefaultProfile: # Target Profile +# - "./AnotherSourceFile.cpp" + +# # (Optional) Include paths (relative to script file path) for each platform and profile +# IncludePaths: +# DefaultPlatform: # Target Platform +# DefaultProfile: # Target Profile +# - "./include" +# - "./src/include" + +# # (Optional) Define cross-compiler defines for each platform and profile. +# # Defines can be specified as just a name or as a name-value pair. +# Defines: +# DefaultPlatform: # Target Platform +# DefaultProfile: # Profile name +# - "EXAMPLE_DEFINE" # Define without a value +# - "VERSION_MAJOR=1" # Define with a value + +# # (Optional) Setup commands are run once before the script is first built. +# # These commands are run at the script's location when no build directory exists. +# Setup: +# DefaultPlatform: # Target Platform +# DefaultProfile: # Profile name +# # List of setup commands +# - "echo Setting up script..." + +# # (Optional) PreBuild commands are run before each build. +# # These commands are run in the build directory before compilation starts. +# PreBuild: +# DefaultPlatform: # Target Platform +# DefaultProfile: # Profile name +# - "echo Starting build..." + +# # (Optional) PostBuild commands are run after each successful build. +# # These commands are run in the output directory where binaries are located. +# PostBuild: +# DefaultPlatform: # Target Platform +# DefaultProfile: # Profile name +# - "echo Build completed..." + +# # (Optional) Cleanup commands are run when using the --cleanup option. +# # These commands are run at the script's location before the build directory is removed. +# Cleanup: +# DefaultPlatform: # Target Platform +# DefaultProfile: # Profile name +# - "echo Cleaning up script..." + +# # (Optional) The list of dependencies needed by the script +# Dependencies: +# # Dependency name +# - Name: MyLibrary +# +# # Supported platforms of the dependency +# Platforms: [Windows, Linux, MacOS] +# +# # Where to get and copy the dependency (Git, Local) +# # Either Git or Local can exist, not both +# Source: +# # (Optional) Import dependency configuration from a YAML file if this field exists +# # All other fields (Name, Platforms, etc...) are not needed if this field exists +# # For Git source: Path is relative to the git repository root +# # For Local source: Path is relative to the path specified under `Local` +# # If neither source exists, local source with root script directory is assumed. +# ImportPath: "config/dependency.yaml" +# +# # Dependency or import YAML file exists in a git server, and needs to be cloned to build directory +# Git: +# # Git repository URL +# URL: "https://github.com/MyUser/MyLibrary.git" +# +# # (Optional) Branch name or tag name +# # Defaults to default branch on specified git repo if this is not specified +# # Branch: "" +# +# # (Optional) Checkout full git history or just the target commit. Defaults to false +# # FullHistory: false +# +# # (Optional) Initialization type for all the submodules recursively +# # - "None": Don't initialize any submodules +# # - "Shallow": Only checkout the target commit of all the submodules (default) +# # - "Full": Checkout the full git history of all the submodules +# # SubmoduleInitType: "Shallow" +# +# # Dependency or import YAML file exists in local filesystem directory, +# # and needs to be copied to build directory +# Local: +# # Path to the library directory +# Path: "./libs/LocalLibrary" +# +# # (Optional) How to handle copying files to build directory +# # Values: +# # - "Auto" (default): Try symlink first, then hardlink, then copy as fallback +# # - "Symlink": Create symbolic links only, fail if not possible +# # - "Hardlink": Create hard links only, fail if not possible +# # - "Copy": Copy files to build directory +# CopyMode: "Auto" +# +# # Library Type (Static, Object, Shared, Header) +# LibraryType: Static +# +# # (Optional) Paths to be added to the include paths, relative to the dependency folder +# IncludePaths: +# - "src/include" +# +# # (Optional if LibraryType is Header) Link properties of the dependency +# LinkProperties: +# # Properties for searching the library binary for each platform +# DefaultPlatform: +# # Profile-specific properties +# "g++": +# # The library names to be searched for when linking against the script. +# # Binaries with linkable extension that contains one of the names will be linked +# SearchLibraryNames: ["MyLibrary"] +# +# # (Optional) The library names to be excluded from being searched. +# # Works the same as SearchLibraryNames but will NOT be linked instead +# ExcludeLibraryNames: [] +# +# # The path (relative to the dependency folder) to be searched for the dependency binaries +# SearchDirectories: ["./build"] +# +# # (Optional) Additional link flags for this dependency +# AdditionalLinkOptions: [] +# +# # (Optional) Setup commands are run once when the dependency is populated +# Setup: +# # Target Platform +# DefaultPlatform: +# # Setup shell commands for the specified profile. +# # Default commands are run in the dependency folder +# # You can also use "DefaultProfile" if all the compilers run the same setup commands +# "g++": +# - "mkdir build" +# +# +# # (Optional) Build commands are run every time before the script is being built +# Build: +# # Target Platform +# DefaultPlatform: +# # Target Profile +# "g++": +# - "cd build && cmake .." +# - "cd build && cmake --build ." +# +# # (Optional) Cleanup commands are run when the reset option is present. Normally nothing needs +# # to be done since the dependency folder will be removed automatically. +# Cleanup: +# # Target Platform +# Linux: +# # Target Profile +# "g++": +# - "sudo apt purge MyLibrary" +# +# # (Optional) Files to be copied to next to output binary for each platform and profile +# FilesToCopy: +# # Target Platform +# DefaultPlatform: +# # Profile name +# DefaultProfile: +# # List of files to copy (relative to the dependency folder) +# - "assets/textures/sprite.png" +# Windows: +# "msvc": +# - "assets/textures/sprite.png" +# - "assets/fonts/windows_specific_font.ttf" +# Linux: +# "g++": +# - "assets/textures/sprite.png" +# - "assets/shaders/linux_optimized_shader.glsl" diff --git a/External/CppOverride b/External/CppOverride index 2cef79b..b3ddf34 160000 --- a/External/CppOverride +++ b/External/CppOverride @@ -1 +1 @@ -Subproject commit 2cef79be3130cff8daace92aaa2c0ea283e56474 +Subproject commit b3ddf34f27763c5617fd1479b51d48d2b9fa765a diff --git a/External/System2.cpp b/External/System2.cpp new file mode 160000 index 0000000..ffe62bc --- /dev/null +++ b/External/System2.cpp @@ -0,0 +1 @@ +Subproject commit ffe62bcabceb158efa015df0feba280e8c92579e diff --git a/External/libyaml b/External/libyaml index 453b582..a90df8f 160000 --- a/External/libyaml +++ b/External/libyaml @@ -1 +1 @@ -Subproject commit 453b5826b7597aa605955d5dc73260d2b3f1d0a1 +Subproject commit a90df8f6b0aa162bc5de726f355826fbd978df83 diff --git a/Include/runcpp2/BuildsManager.hpp b/Include/runcpp2/BuildsManager.hpp deleted file mode 100644 index 3f7dba7..0000000 --- a/Include/runcpp2/BuildsManager.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef RUNCPP2_BUILDS_MANAGER_HPP -#define RUNCPP2_BUILDS_MANAGER_HPP - -#if !defined(NOMINMAX) - #define NOMINMAX 1 -#endif -#include "ghc/filesystem.hpp" - -#include - -#if INTERNAL_RUNCPP2_UNIT_TESTS - class BuildsManagerAccessor; -#endif - -namespace runcpp2 -{ - class BuildsManager - { - #if INTERNAL_RUNCPP2_UNIT_TESTS - friend class ::BuildsManagerAccessor; - #endif - - private: - ghc::filesystem::path ConfigDirectory; - - std::unordered_map Mappings; - std::unordered_map ReverseMappings; - - - ghc::filesystem::path BuildDirectory; - ghc::filesystem::path MappingsFile; - bool Initialized; - - bool ParseMappings(const std::string& mappingsContent); - std::string ProcessPath(const std::string& path); - - public: - BuildsManager(const ghc::filesystem::path& configDirectory); - BuildsManager(const BuildsManager& other); - BuildsManager& operator=(const BuildsManager& other); - ~BuildsManager(); - - bool Initialize(); - bool CreateBuildMapping(const ghc::filesystem::path& scriptPath); - bool RemoveBuildMapping(const ghc::filesystem::path& scriptPath); - bool HasBuildMapping(const ghc::filesystem::path& scriptPath); - bool GetBuildMapping( const ghc::filesystem::path& scriptPath, - ghc::filesystem::path& outPath); - bool RemoveAllBuildsMappings(); - bool SaveBuildsMappings(); - }; -} - -#endif diff --git a/Include/runcpp2/CompilingLinking.hpp b/Include/runcpp2/CompilingLinking.hpp deleted file mode 100644 index ee591a5..0000000 --- a/Include/runcpp2/CompilingLinking.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef RUNCPP2_COMPILING_LINKING_HPP -#define RUNCPP2_COMPILING_LINKING_HPP - -#include "runcpp2/Data/Profile.hpp" -#include "runcpp2/Data/ScriptInfo.hpp" - -#if !defined(NOMINMAX) - #define NOMINMAX 1 -#endif - -#include "ghc/filesystem.hpp" - -#include - -namespace runcpp2 -{ - DS::Result - CompileScriptOnly( const ghc::filesystem::path& buildDir, - const ghc::filesystem::path& scriptPath, - const std::vector& sourceFiles, - const std::vector& sourceHasCache, - const std::vector& includePaths, - const Data::ScriptInfo& scriptInfo, - const std::vector& availableDependencies, - const Data::Profile& profile, - const int maxThreads); - - //TODO: Convert string paths to filesystem paths - DS::Result - CompileAndLinkScript( const ghc::filesystem::path& buildDir, - const ghc::filesystem::path& scriptPath, - const std::string& outputName, - const std::vector& sourceFiles, - const std::vector& sourceHasCache, - const std::vector& includePaths, - const Data::ScriptInfo& scriptInfo, - const std::vector& availableDependencies, - const Data::Profile& profile, - const std::vector& compiledObjectsPaths, - const int maxThreads); -} - -#endif diff --git a/Include/runcpp2/ConfigParsing.hpp b/Include/runcpp2/ConfigParsing.hpp deleted file mode 100644 index a47e8af..0000000 --- a/Include/runcpp2/ConfigParsing.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef RUNCPP2_CONFIG_PARSING_HPP -#define RUNCPP2_CONFIG_PARSING_HPP - -#include "runcpp2/Data/Profile.hpp" -#include "runcpp2/Data/ScriptInfo.hpp" - -#include "ghc/filesystem.hpp" - -#include -#include -namespace runcpp2 -{ - DS::Result GetConfigFilePath(); - - DS::Result WriteDefaultConfigs( const ghc::filesystem::path& userConfigPath, - const bool writeUserConfig, - const bool writeDefaultConfigs); - - DS::Result ReadUserConfig(std::vector& outProfiles, - std::string& outPreferredProfile, - const std::string& customConfigPath = ""); - - DS::Result ParseScriptInfo(const std::string& scriptInfo, Data::ScriptInfo& outScriptInfo); -} - -#endif diff --git a/Include/runcpp2/Data/BuildTypeHelper.hpp b/Include/runcpp2/Data/BuildTypeHelper.hpp deleted file mode 100644 index 9d0b67a..0000000 --- a/Include/runcpp2/Data/BuildTypeHelper.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef RUNCPP2_DATA_BUILD_TYPE_HELPER_HPP -#define RUNCPP2_DATA_BUILD_TYPE_HELPER_HPP - -#include "runcpp2/Data/BuildType.hpp" -#include "runcpp2/Data/FilesTypesInfo.hpp" -#include "runcpp2/Data/Profile.hpp" -#include "ghc/filesystem.hpp" - -namespace runcpp2 -{ - namespace Data - { - namespace BuildTypeHelper - { - bool NeedsLinking(BuildType buildType); - - bool GetPossibleOutputPaths(const ghc::filesystem::path& buildDir, - const std::string& scriptName, - const Profile& profile, - const BuildType buildType, - std::vector& outPaths, - std::vector& outIsRunnable); - } - } -} - -#endif diff --git a/Include/runcpp2/Data/DependencyInfo.hpp b/Include/runcpp2/Data/DependencyInfo.hpp deleted file mode 100644 index 113a175..0000000 --- a/Include/runcpp2/Data/DependencyInfo.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef RUNCPP2_DATA_DEPENDENCY_INFO_HPP -#define RUNCPP2_DATA_DEPENDENCY_INFO_HPP - -#include "runcpp2/Data/DependencyLibraryType.hpp" -#include "runcpp2/Data/DependencySource.hpp" -#include "runcpp2/Data/DependencyLinkProperty.hpp" -#include "runcpp2/Data/ProfilesCommands.hpp" -#include "runcpp2/Data/ParseCommon.hpp" -#include "runcpp2/Data/FilesToCopyInfo.hpp" -#include "runcpp2/YamlLib.hpp" - -#include -#include - -namespace runcpp2 -{ - namespace Data - { - struct DependencyInfo - { - std::string Name; - std::unordered_set Platforms; - DependencySource Source; - DependencyLibraryType LibraryType; - std::vector IncludePaths; - std::vector AbsoluteIncludePaths; - std::unordered_map LinkProperties; - std::unordered_map Setup; - std::unordered_map Cleanup; - std::unordered_map Build; - std::unordered_map FilesToCopy; - - bool ParseYAML_Node(YAML::ConstNodePtr node); - - std::string ToString(std::string indentation) const; - bool Equals(const DependencyInfo& other) const; - }; - } -} - -#endif diff --git a/Include/runcpp2/Data/DependencyLinkProperty.hpp b/Include/runcpp2/Data/DependencyLinkProperty.hpp deleted file mode 100644 index 4b83a8a..0000000 --- a/Include/runcpp2/Data/DependencyLinkProperty.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef RUNCPP2_DATA_DEPENDENCY_LINK_PROPERTY_HPP -#define RUNCPP2_DATA_DEPENDENCY_LINK_PROPERTY_HPP - -#include "runcpp2/Data/ParseCommon.hpp" -#include "runcpp2/YamlLib.hpp" - -#include -#include -#include - -namespace runcpp2 -{ - namespace Data - { - struct ProfileLinkProperty - { - std::vector SearchLibraryNames; - std::vector SearchDirectories; - std::vector ExcludeLibraryNames; - std::vector AdditionalLinkOptions; - }; - - class DependencyLinkProperty - { - public: - std::unordered_map ProfileProperties; - - bool ParseYAML_Node(YAML::ConstNodePtr node); - - bool ParseYAML_NodeWithProfile(YAML::ConstNodePtr node, ProfileName profile); - bool IsYAML_NodeParsableAsDefault(YAML::ConstNodePtr node) const; - - std::string ToString(std::string indentation) const; - bool Equals(const DependencyLinkProperty& other) const; - }; - } -} - -#endif diff --git a/Include/runcpp2/Data/DependencySource.hpp b/Include/runcpp2/Data/DependencySource.hpp deleted file mode 100644 index d984784..0000000 --- a/Include/runcpp2/Data/DependencySource.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef RUNCPP2_DATA_DEPENDENCY_SOURCE_HPP -#define RUNCPP2_DATA_DEPENDENCY_SOURCE_HPP - -#include "runcpp2/Data/GitSource.hpp" -#include "runcpp2/Data/LocalSource.hpp" -#include "runcpp2/YamlLib.hpp" -#include "ghc/filesystem.hpp" -#include "mpark/variant.hpp" - -#include -#include - -namespace runcpp2 -{ - namespace Data - { - class DependencySource - { - public: - mpark::variant Source; - ghc::filesystem::path ImportPath; - std::vector> ImportedSources; - - bool ParseYAML_Node(YAML::ConstNodePtr node); - std::string ToString(std::string indentation) const; - bool Equals(const DependencySource& other) const; - }; - } -} - -#endif diff --git a/Include/runcpp2/Data/FileProperties.hpp b/Include/runcpp2/Data/FileProperties.hpp deleted file mode 100644 index 798dd3e..0000000 --- a/Include/runcpp2/Data/FileProperties.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef RUNCPP2_DATA_FILE_PROPERTIES_HPP -#define RUNCPP2_DATA_FILE_PROPERTIES_HPP - -#include "runcpp2/Data/ParseCommon.hpp" -#include "runcpp2/YamlLib.hpp" - -#include - -namespace runcpp2 -{ - namespace Data - { - struct FileProperties - { - std::unordered_map Prefix; - std::unordered_map Extension; - - bool ParseYAML_Node(YAML::ConstNodePtr node); - - std::string ToString(std::string indentation) const; - bool Equals(const FileProperties& other) const; - }; - - } -} - -#endif diff --git a/Include/runcpp2/Data/FilesToCopyInfo.hpp b/Include/runcpp2/Data/FilesToCopyInfo.hpp deleted file mode 100644 index 41c95f5..0000000 --- a/Include/runcpp2/Data/FilesToCopyInfo.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef RUNCPP2_DATA_FILES_TO_COPY_INFO_HPP -#define RUNCPP2_DATA_FILES_TO_COPY_INFO_HPP - -#include "runcpp2/Data/ParseCommon.hpp" -#include "runcpp2/YamlLib.hpp" - -#include -#include -#include - -namespace runcpp2 -{ - namespace Data - { - struct FilesToCopyInfo - { - std::unordered_map> ProfileFiles; - - bool ParseYAML_Node(YAML::ConstNodePtr node); - - bool ParseYAML_NodeWithProfile(YAML::ConstNodePtr node, ProfileName profile); - bool IsYAML_NodeParsableAsDefault(YAML::ConstNodePtr node) const; - - std::string ToString(std::string indentation) const; - bool Equals(const FilesToCopyInfo& other) const; - }; - } -} - -#endif diff --git a/Include/runcpp2/Data/FilesTypesInfo.hpp b/Include/runcpp2/Data/FilesTypesInfo.hpp deleted file mode 100644 index 4346984..0000000 --- a/Include/runcpp2/Data/FilesTypesInfo.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef RUNCPP2_DATA_FILES_TYPES_HPP -#define RUNCPP2_DATA_FILES_TYPES_HPP - -#include "runcpp2/Data/FileProperties.hpp" -#include "runcpp2/Data/ParseCommon.hpp" -#include "runcpp2/YamlLib.hpp" - -namespace runcpp2 -{ - namespace Data - { - struct FilesTypesInfo - { - FileProperties ObjectLinkFile; - FileProperties SharedLinkFile; - FileProperties SharedLibraryFile; - FileProperties StaticLinkFile; - FileProperties DebugSymbolFile; - - bool ParseYAML_Node(YAML::ConstNodePtr node); - - std::string ToString(std::string indentation) const; - bool Equals(const FilesTypesInfo& other) const; - }; - } -} - -#endif diff --git a/Include/runcpp2/Data/FlagsOverrideInfo.hpp b/Include/runcpp2/Data/FlagsOverrideInfo.hpp deleted file mode 100644 index b3197c8..0000000 --- a/Include/runcpp2/Data/FlagsOverrideInfo.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef RUNCPP2_DATA_FLAGS_OVERRIDE_INFO_HPP -#define RUNCPP2_DATA_FLAGS_OVERRIDE_INFO_HPP - -#include "runcpp2/YamlLib.hpp" -#include - -namespace runcpp2 -{ - namespace Data - { - class FlagsOverrideInfo - { - public: - std::string Remove; - std::string Append; - - bool ParseYAML_Node(YAML::ConstNodePtr node); - - bool IsYAML_NodeParsableAsDefault(YAML::ConstNodePtr node) const; - - std::string ToString(std::string indentation) const; - bool Equals(const FlagsOverrideInfo& other) const; - }; - } -} - - -#endif diff --git a/Include/runcpp2/Data/GitSource.hpp b/Include/runcpp2/Data/GitSource.hpp deleted file mode 100644 index bf46d7d..0000000 --- a/Include/runcpp2/Data/GitSource.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef RUNCPP2_DATA_GIT_SOURCE_HPP -#define RUNCPP2_DATA_GIT_SOURCE_HPP - -#include "runcpp2/Data/SubmoduleInitType.hpp" - -#include "runcpp2/YamlLib.hpp" -#include - -namespace runcpp2 -{ - namespace Data - { - class GitSource - { - public: - std::string URL; - std::string Branch; - bool FullHistory = false; - SubmoduleInitType CurrentSubmoduleInitType = SubmoduleInitType::SHALLOW; - - bool ParseYAML_Node(YAML::ConstNodePtr node); - - std::string ToString(std::string indentation) const; - bool Equals(const GitSource& other) const; - }; - } -} - -#endif diff --git a/Include/runcpp2/Data/LocalSource.hpp b/Include/runcpp2/Data/LocalSource.hpp deleted file mode 100644 index 31a14c7..0000000 --- a/Include/runcpp2/Data/LocalSource.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef RUNCPP2_DATA_LOCAL_SOURCE_HPP -#define RUNCPP2_DATA_LOCAL_SOURCE_HPP - -#include "runcpp2/YamlLib.hpp" -#include - -namespace runcpp2 -{ - namespace Data - { - enum class LocalCopyMode - { - Auto, - Symlink, - Hardlink, - Copy, - Count - }; - - class LocalSource - { - public: - std::string Path; - LocalCopyMode CopyMode = LocalCopyMode::Auto; - - bool ParseYAML_Node(YAML::ConstNodePtr node); - - std::string ToString(std::string indentation) const; - bool Equals(const LocalSource& other) const; - }; - } -} - -#endif diff --git a/Include/runcpp2/Data/Profile.hpp b/Include/runcpp2/Data/Profile.hpp deleted file mode 100644 index 2c2e001..0000000 --- a/Include/runcpp2/Data/Profile.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef RUNCPP2_DATA_PROFILE_HPP -#define RUNCPP2_DATA_PROFILE_HPP - -#include "runcpp2/Data/ParseCommon.hpp" -#include "runcpp2/Data/FilesTypesInfo.hpp" -#include "runcpp2/Data/StageInfo.hpp" -#include "runcpp2/YamlLib.hpp" - -#include -#include -#include -#include - -namespace runcpp2 -{ - namespace Data - { - class Profile - { - public: - std::string Name; - - std::unordered_set NameAliases; - std::unordered_set FileExtensions; - std::unordered_set Languages; - std::unordered_map> Setup; - std::unordered_map> Cleanup; - FilesTypesInfo FilesTypes; - - StageInfo Compiler; - StageInfo Linker; - - void GetNames(std::vector& outNames) const; - bool ParseYAML_Node(YAML::ConstNodePtr profileNode); - - std::string ToString(std::string indentation) const; - bool Equals(const Profile& other) const; - }; - } -} - -#endif diff --git a/Include/runcpp2/Data/ProfilesCommands.hpp b/Include/runcpp2/Data/ProfilesCommands.hpp deleted file mode 100644 index 5cad2ee..0000000 --- a/Include/runcpp2/Data/ProfilesCommands.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef RUNCPP2_DATA_PROFILES_COMMANDS_HPP -#define RUNCPP2_DATA_PROFILES_COMMANDS_HPP - -#include "runcpp2/Data/ParseCommon.hpp" -#include "runcpp2/YamlLib.hpp" - -#include -#include -#include - -namespace runcpp2 -{ - namespace Data - { - class ProfilesCommands - { - public: - //TODO: Allow specifying command can fail - std::unordered_map> CommandSteps; - - bool ParseYAML_Node(YAML::ConstNodePtr node); - bool ParseYAML_NodeWithProfile(YAML::ConstNodePtr node, ProfileName profile); - bool IsYAML_NodeParsableAsDefault(YAML::ConstNodePtr node) const; - - std::string ToString(std::string indentation) const; - bool Equals(const ProfilesCommands& other) const; - }; - } -} - -#endif diff --git a/Include/runcpp2/Data/ProfilesDefines.hpp b/Include/runcpp2/Data/ProfilesDefines.hpp deleted file mode 100644 index 219f514..0000000 --- a/Include/runcpp2/Data/ProfilesDefines.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef RUNCPP2_DATA_PROFILES_DEFINES_HPP -#define RUNCPP2_DATA_PROFILES_DEFINES_HPP - -#include "runcpp2/Data/ParseCommon.hpp" -#include "runcpp2/YamlLib.hpp" - -#include -#include -#include - -namespace runcpp2 -{ - namespace Data - { - struct Define - { - std::string Name; - std::string Value; - bool HasValue; - }; - - class ProfilesDefines - { - public: - std::unordered_map> Defines; - - bool ParseYAML_Node(YAML::ConstNodePtr node); - - bool ParseYAML_NodeWithProfile(YAML::ConstNodePtr node, ProfileName profile); - bool IsYAML_NodeParsableAsDefault(YAML::ConstNodePtr node) const; - std::string ToString(std::string indentation) const; - bool Equals(const ProfilesDefines& other) const; - }; - } -} - -#endif diff --git a/Include/runcpp2/Data/ProfilesFlagsOverride.hpp b/Include/runcpp2/Data/ProfilesFlagsOverride.hpp deleted file mode 100644 index f7e5a21..0000000 --- a/Include/runcpp2/Data/ProfilesFlagsOverride.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef RUNCPP2_DATA_PROFILES_FLAGS_OVERRIDE_HPP -#define RUNCPP2_DATA_PROFILES_FLAGS_OVERRIDE_HPP - -#include "FlagsOverrideInfo.hpp" -#include "runcpp2/Data/ParseCommon.hpp" - -#include - -namespace runcpp2 -{ - namespace Data - { - class ProfilesFlagsOverride - { - public: - std::unordered_map FlagsOverrides; - - bool ParseYAML_Node(YAML::ConstNodePtr node); - - bool ParseYAML_NodeWithProfile(YAML::ConstNodePtr node, ProfileName profile); - bool IsYAML_NodeParsableAsDefault(YAML::ConstNodePtr node) const; - - std::string ToString(std::string indentation) const; - bool Equals(const ProfilesFlagsOverride& other) const; - }; - } -} - -#endif diff --git a/Include/runcpp2/Data/ProfilesProcessPaths.hpp b/Include/runcpp2/Data/ProfilesProcessPaths.hpp deleted file mode 100644 index 1cc454e..0000000 --- a/Include/runcpp2/Data/ProfilesProcessPaths.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef RUNCPP2_DATA_PROFILES_PROCESS_PATHS_HPP -#define RUNCPP2_DATA_PROFILES_PROCESS_PATHS_HPP - -#include "runcpp2/Data/ParseCommon.hpp" - -#include "runcpp2/YamlLib.hpp" - -#if !defined(NOMINMAX) - #define NOMINMAX 1 -#endif -#include "ghc/filesystem.hpp" - -#include - -namespace runcpp2 -{ - namespace Data - { - class ProfilesProcessPaths - { - public: - std::unordered_map> Paths; - - bool ParseYAML_Node(YAML::ConstNodePtr node); - - bool ParseYAML_NodeWithProfile(YAML::ConstNodePtr node, ProfileName profile); - bool IsYAML_NodeParsableAsDefault(YAML::ConstNodePtr node) const; - - std::string ToString(std::string indentation) const; - bool Equals(const ProfilesProcessPaths& other) const; - }; - } -} - -#endif diff --git a/Include/runcpp2/Data/ScriptInfo.hpp b/Include/runcpp2/Data/ScriptInfo.hpp deleted file mode 100644 index 0753cb9..0000000 --- a/Include/runcpp2/Data/ScriptInfo.hpp +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef RUNCPP2_DATA_SCRIPT_INFO_HPP -#define RUNCPP2_DATA_SCRIPT_INFO_HPP - -#include "runcpp2/Data/DependencyInfo.hpp" -#include "runcpp2/Data/ProfilesFlagsOverride.hpp" -#include "runcpp2/Data/ProfilesProcessPaths.hpp" -#include "runcpp2/Data/ProfilesDefines.hpp" -#include "runcpp2/Data/ProfilesCommands.hpp" -#include "runcpp2/Data/BuildType.hpp" - -#define RUNCPP2_CURRENT_CLASS_NAME ScriptInfo -#include "runcpp2/MacroUtil.hpp" - -#if !defined(NOMINMAX) - #define NOMINMAX 1 -#endif - -#include "ghc/filesystem.hpp" - -#include -#include -#include - -namespace runcpp2 -{ - namespace Data - { - class ScriptInfo - { - public: - RUNCPP2_FIELD_BEGIN(); - - RUNCPP2_FIELD std::string Language; - RUNCPP2_FIELD bool PassScriptPath = false; - RUNCPP2_FIELD BuildType CurrentBuildType = BuildType::EXECUTABLE; - RUNCPP2_FIELD std::unordered_map< PlatformName, - std::vector> RequiredProfiles; - RUNCPP2_FIELD std::unordered_map< PlatformName, - ProfilesFlagsOverride> OverrideCompileFlags; - RUNCPP2_FIELD std::unordered_map< PlatformName, - ProfilesFlagsOverride> OverrideLinkFlags; - RUNCPP2_FIELD std::unordered_map< PlatformName, - ProfilesProcessPaths> OtherFilesToBeCompiled; - RUNCPP2_FIELD std::unordered_map< PlatformName, - ProfilesProcessPaths> IncludePaths; - RUNCPP2_FIELD std::vector Dependencies; - RUNCPP2_FIELD std::unordered_map Defines; - RUNCPP2_FIELD std::unordered_map Setup; - RUNCPP2_FIELD std::unordered_map PreBuild; - RUNCPP2_FIELD std::unordered_map PostBuild; - RUNCPP2_FIELD std::unordered_map Cleanup; - - static constexpr int FieldsCount = RUNCPP2_FIELD_COUNT; - - //Internal tracking - bool Populated = false; - - ghc::filesystem::file_time_type LastWriteTime = - ghc::filesystem::file_time_type::min(); - - bool ParseYAML_Node(YAML::ConstNodePtr node); - - std::string ToString(std::string indentation) const; - bool IsAllCompiledCacheInvalidated(const ScriptInfo& other) const; - bool Equals(const ScriptInfo& other) const; - }; - } -} - -#endif diff --git a/Include/runcpp2/Data/StageInfo.hpp b/Include/runcpp2/Data/StageInfo.hpp deleted file mode 100644 index 7a03361..0000000 --- a/Include/runcpp2/Data/StageInfo.hpp +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef RUNCPP2_DATA_STAGE_INFO_HPP -#define RUNCPP2_DATA_STAGE_INFO_HPP - -#include "runcpp2/Data/ParseCommon.hpp" -#include "runcpp2/Data/BuildType.hpp" -#include "runcpp2/YamlLib.hpp" - -#include -#include -#include - -namespace runcpp2 -{ - namespace Data - { - class StageInfo - { - public: - std::unordered_map PreRun; - std::unordered_map CheckExistence; - - struct RunPart - { - enum class RunType - { - ONCE, - REPEATS, - COUNT - }; - - RunType Type; - std::string CommandPart; - }; - - struct OutputTypeInfo - { - std::string Flags; - std::string Executable; - - std::vector Setup; - std::vector Cleanup; - std::vector RunParts; - std::vector ExpectedOutputFiles; - }; - - struct - { - std::unordered_map Executable; - std::unordered_map ExecutableShared; - std::unordered_map Static; - std::unordered_map Shared; - } OutputTypes; - - using SubstitutionMap = std::unordered_map>; - - //TODO: Make this static function? - bool PerformSubstituions( const SubstitutionMap& substitutionMap, - std::string& inOutSubstitutedString) const; - - bool ConstructCommand( const SubstitutionMap& substitutionMap, - const BuildType buildType, - std::string& outCommand) const; - - bool ParseYAML_Node(YAML::ConstNodePtr node, std::string outputTypeKeyName); - - std::string ToString( std::string indentation, - std::string outputTypeKeyName) const; - bool Equals(const StageInfo& other) const; - }; - } -} - -#endif diff --git a/Include/runcpp2/DependenciesHelper.hpp b/Include/runcpp2/DependenciesHelper.hpp deleted file mode 100644 index eb37506..0000000 --- a/Include/runcpp2/DependenciesHelper.hpp +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef RUNCPP2_DEPENDENCIES_SETUP_HELPER_HPP -#define RUNCPP2_DEPENDENCIES_SETUP_HELPER_HPP - -#include "runcpp2/Data/DependencyInfo.hpp" -#include "runcpp2/Data/ScriptInfo.hpp" -#include "runcpp2/Data/Profile.hpp" - -#if !defined(NOMINMAX) - #define NOMINMAX 1 -#endif - -#include "DSResult/DSResult.hpp" -#include "ghc/filesystem.hpp" -#include - -namespace runcpp2 -{ - DS::Result - GetDependenciesPaths( const std::vector& availableDependencies, - std::vector& outCopiesPaths, - std::vector& outSourcesPaths, - const ghc::filesystem::path& scriptPath, - const ghc::filesystem::path& buildDir); - - bool IsDependencyAvailableForThisPlatform(const Data::DependencyInfo& dependency); - - DS::Result - CleanupDependencies(const runcpp2::Data::Profile& profile, - const Data::ScriptInfo& scriptInfo, - const std::vector& availableDependencies, - const std::vector& dependenciesLocalCopiesPaths, - const std::string& dependenciesToReset); - - DS::Result - SetupDependenciesIfNeeded( const runcpp2::Data::Profile& profile, - const ghc::filesystem::path& buildDir, - const Data::ScriptInfo& scriptInfo, - std::vector& availableDependencies, - const std::vector& dependenciesLocalCopiesPaths, - const std::vector& dependenciesSourcePaths, - const int maxThreads); - - DS::Result - BuildDependencies( const runcpp2::Data::Profile& profile, - const Data::ScriptInfo& scriptInfo, - const std::vector& availableDependencies, - const std::vector& dependenciesLocalCopiesPaths, - const int maxThreads); - - DS::Result - GatherDependenciesBinaries( const std::vector& availableDependencies, - const std::vector& dependenciesCopiesPaths, - const Data::Profile& profile, - std::vector& outBinariesPaths); - - DS::Result HandleImport( Data::DependencyInfo& dependency, - const ghc::filesystem::path& basePath); - - DS::Result ResolveImports(Data::ScriptInfo& scriptInfo, - const ghc::filesystem::path& scriptPath, - const ghc::filesystem::path& buildDir); - - DS::Result SyncLocalDependency( const Data::DependencyInfo& dependency, - const ghc::filesystem::path& sourcePath, - const ghc::filesystem::path& copyPath); - - DS::Result SyncLocalDependencies(const std::vector& dependencies, - const std::vector& dependenciesSourcePaths, - const std::vector& dependenciesCopiesPaths); -} - -#endif diff --git a/Include/runcpp2/IncludeManager.hpp b/Include/runcpp2/IncludeManager.hpp deleted file mode 100644 index 642659c..0000000 --- a/Include/runcpp2/IncludeManager.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef RUNCPP2_INCLUDE_MANAGER_HPP -#define RUNCPP2_INCLUDE_MANAGER_HPP - -#if !defined(NOMINMAX) - #define NOMINMAX 1 -#endif - -#include "ghc/filesystem.hpp" -#include -#include - -#if INTERNAL_RUNCPP2_UNIT_TESTS - struct IncludeManagerAccessor; -#endif - -namespace runcpp2 -{ - class IncludeManager - { - public: - IncludeManager() = default; - ~IncludeManager() = default; - - bool Initialize(const ghc::filesystem::path& buildDir); - - bool WriteIncludeRecord(const ghc::filesystem::path& sourceFile, - const std::vector& includes); - - bool ReadIncludeRecord( const ghc::filesystem::path& sourceFile, - std::vector& outIncludes, - ghc::filesystem::file_time_type& outRecordTime); - - bool NeedsUpdate( const ghc::filesystem::path& sourceFile, - const std::vector& includes, - const ghc::filesystem::file_time_type& recordTime) const; - - private: - #if INTERNAL_RUNCPP2_UNIT_TESTS - friend struct ::IncludeManagerAccessor; - #endif - - ghc::filesystem::path GetRecordPath(const ghc::filesystem::path& sourceFile) const; - - ghc::filesystem::path IncludeRecordDir; - }; -} - -#endif diff --git a/Include/runcpp2/LibYAML_Wrapper.hpp b/Include/runcpp2/LibYAML_Wrapper.hpp deleted file mode 100644 index 843f8d5..0000000 --- a/Include/runcpp2/LibYAML_Wrapper.hpp +++ /dev/null @@ -1,505 +0,0 @@ -#ifndef RUNCPP2_LIB_YAML_WRAPPER_HPP -#define RUNCPP2_LIB_YAML_WRAPPER_HPP - -#include "DSResult/DSResult.hpp" -#include "mpark/variant.hpp" -#include "nonstd/string_view.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include - -struct yaml_event_s; -typedef yaml_event_s yaml_event_t; - -struct yaml_parser_s; -typedef yaml_parser_s yaml_parser_t; - -namespace runcpp2 -{ - using StringView = nonstd::string_view; - - namespace YAML - { - enum class NodeType - { - Scalar, - Alias, - Sequence, - Map - }; - - inline StringView NodeTypeToString(NodeType type); - - struct Node; - using NodePtr = std::shared_ptr; - using ConstNodePtr = std::shared_ptr; - - struct ReadWriteBuffer - { - std::vector> ReadData = {}; - //NOTE: Needs to be unique_ptr due to small string optimizations - std::vector> WriteData = {}; - - inline std::string& CreateString() - { - WriteData.emplace_back(std::unique_ptr(new std::string())); - return *WriteData.back().get(); - } - - inline void StoreString(const std::string& str) - { - CreateString() = str; - } - }; - - using ResourceHandle = std::pair; - - inline NodePtr CreateNodePtr() { return std::make_shared(); } - - //NOTE: all parameters must be alive while using the node - DS::Result> ParseYAML(StringView yamlString, ResourceHandle& outResource); - - DS::Result ResolveAnchors(NodePtr rootNode); - - DS::Result FreeYAMLResource(ResourceHandle& resourceHandleToFree); - - struct Alias - { - StringView Value; - }; - - using Sequence = std::vector; - using ScalarValue = StringView; - - struct OrderedMap - { - std::vector InsertedKeys; - std::unordered_map Map; - std::unordered_map StringMap; - }; - - using NodeValue = mpark::variant; - - struct Node - { - NodeValue Value = ""; - StringView Anchor = ""; - int LineNumber = -1; - Node* Parent = nullptr; - - //Reading - inline NodeType GetType() const { return (NodeType)Value.index(); } - - inline bool IsAlias() const { return mpark::holds_alternative(Value); }; - inline bool IsScalar() const { return mpark::holds_alternative(Value); }; - inline bool IsSequence() const { return mpark::holds_alternative(Value); }; - inline bool IsMap() const { return mpark::holds_alternative(Value); }; - - template - inline DS::Result GetAlias() const; - - template - inline DS::Result GetScalar() const; - - inline NodePtr GetSequenceChildNode(uint32_t index); - inline ConstNodePtr GetSequenceChildNode(uint32_t index) const; - - template - inline DS::Result GetSequenceChildAlias(uint32_t index) const; - - template - inline DS::Result GetSequenceChildScalar(uint32_t index) const; - - inline bool HasMapKey(StringView key) const; - inline NodePtr GetMapValueNode(StringView key); - inline ConstNodePtr GetMapValueNode(StringView key) const; - - template - inline DS::Result GetMapValueAlias(StringView key) const; - - template - inline DS::Result GetMapValueScalar(StringView key) const; - - inline NodePtr GetMapKeyNodeAt(uint32_t index); - inline ConstNodePtr GetMapKeyNodeAt(uint32_t index) const; - - template - inline DS::Result GetMapKeyAliasAt(uint32_t index) const; - - template - inline DS::Result GetMapKeyScalarAt(uint32_t index) const; - - inline NodePtr GetMapValueNodeAt(uint32_t index); - inline ConstNodePtr GetMapValueNodeAt(uint32_t index) const; - - template - inline DS::Result GetMapValueAliasAt(uint32_t index) const; - - template - inline DS::Result GetMapValueScalarAt(uint32_t index) const; - - inline uint32_t GetChildrenCount() const; - - //Writing - DS::Result CloneToSequenceChild( NodePtr parentNode, - ResourceHandle& yamlResouce) const; - - //TODO: Support ordering sequence child - - //TODO: Support erasing sequence child - - //NOTE: `key` will be copied - DS::Result CloneToMapChild(StringView key, - NodePtr parentNode, - ResourceHandle& yamlResouce) const; - - //TODO: Support ordering map child - - //TODO: Support erasing map child - inline DS::Result RemoveMapChild(StringView key); - - - //TODO: Proper Writing - - //TODO: Support null (null tag and ~)? - - DS::Result ToString(std::string& outString) const; - - DS::Result Clone(bool shallow, ResourceHandle& yamlResouce) const; - }; - - //================================================== - //Node functions inline implementations - //================================================== - - inline StringView NodeTypeToString(NodeType type) - { - switch(type) - { - case NodeType::Scalar: - return "Scalar"; - case NodeType::Alias: - return "Alias"; - case NodeType::Sequence: - return "Sequence"; - case NodeType::Map: - return "Map"; - default: - return ""; - } - } - - template<> - inline DS::Result Node::GetAlias() const - { - DS_ASSERT_TRUE(IsAlias()); - return mpark::get_if(&Value)->Value; - } - - template<> - inline DS::Result Node::GetAlias() const - { - DS_ASSERT_TRUE(IsAlias()); - return (std::string)mpark::get_if(&Value)->Value; - } - - #define INTERNAL_RUNCPP2_TRY_CONVERT_SCALAR(convertFunc) \ - do \ - { \ - try \ - { \ - return convertFunc((std::string)*mpark::get_if(&Value)); \ - } \ - catch(std::exception& ex) \ - { \ - return DS_ERROR_MSG(ex.what()); \ - } \ - } while(false) - - template<> - inline DS::Result Node::GetScalar() const - { - DS_ASSERT_TRUE(IsScalar()); - INTERNAL_RUNCPP2_TRY_CONVERT_SCALAR(std::stoi); - } - - template<> - inline DS::Result Node::GetScalar() const - { - DS_ASSERT_TRUE(IsScalar()); - INTERNAL_RUNCPP2_TRY_CONVERT_SCALAR(std::stoul); - } - - template<> - inline DS::Result Node::GetScalar() const - { - DS_ASSERT_TRUE(IsScalar()); - INTERNAL_RUNCPP2_TRY_CONVERT_SCALAR(std::stol); - } - - template<> - inline DS::Result Node::GetScalar() const - { - DS_ASSERT_TRUE(IsScalar()); - INTERNAL_RUNCPP2_TRY_CONVERT_SCALAR(std::stoull); - } - - template<> - inline DS::Result Node::GetScalar() const - { - DS_ASSERT_TRUE(IsScalar()); - INTERNAL_RUNCPP2_TRY_CONVERT_SCALAR(std::stoll); - } - - template<> - inline DS::Result Node::GetScalar() const - { - DS_UNWRAP_DECL(unsigned long long ull, GetScalar()); - DS_ASSERT_LT_EQ(ull, std::numeric_limits::max()); - return (unsigned int)ull; - } - - template<> - inline DS::Result Node::GetScalar() const - { - DS_ASSERT_TRUE(IsScalar()); - INTERNAL_RUNCPP2_TRY_CONVERT_SCALAR(std::stof); - } - - template<> - inline DS::Result Node::GetScalar() const - { - DS_ASSERT_TRUE(IsScalar()); - INTERNAL_RUNCPP2_TRY_CONVERT_SCALAR(std::stod); - } - - #undef INTERNAL_RUNCPP2_TRY_CONVERT_SCALAR - - template<> - inline DS::Result Node::GetScalar() const - { - DS_ASSERT_TRUE(IsScalar()); - - std::string v = (std::string)*mpark::get_if(&Value); - for(int i = 0; i < v.size(); ++i) - v[i] = std::tolower(v[i]); - - //TODO: Strict mode which only allows lowercase true and false? - bool isTrue = v == "true" || v == "yes" || v == "on" || v == "1"; - if(isTrue) - return true; - else if(v == "false" || v == "no" || v == "off" || v == "0") - return false; - else - return DS_ERROR_MSG("Invalid scalar value \"" + v + "\" to be converted to bool"); - } - - template<> - inline DS::Result Node::GetScalar() const - { - DS_ASSERT_TRUE(IsScalar()); - return *mpark::get_if(&Value); - } - - template<> - inline DS::Result Node::GetScalar() const - { - DS_ASSERT_TRUE(IsScalar()); - return (std::string)*mpark::get_if(&Value); - } - - inline NodePtr Node::GetSequenceChildNode(uint32_t index) - { - if(!IsSequence() || index >= mpark::get_if(&Value)->size()) - return nullptr; - return (*mpark::get_if(&Value))[index]; - } - - inline ConstNodePtr Node::GetSequenceChildNode(uint32_t index) const - { - if(!IsSequence() || index >= mpark::get_if(&Value)->size()) - return nullptr; - return (*mpark::get_if(&Value))[index]; - } - - template - inline DS::Result Node::GetSequenceChildAlias(uint32_t index) const - { - DS_ASSERT_TRUE(IsSequence()); - DS_ASSERT_LT(index, mpark::get_if(&Value)->size()); - return (*mpark::get_if(&Value))[index]->GetAlias(); - } - - template - inline DS::Result Node::GetSequenceChildScalar(uint32_t index) const - { - DS_ASSERT_TRUE(IsSequence()); - DS_ASSERT_LT(index, mpark::get_if(&Value)->size()); - return (*mpark::get_if(&Value))[index]->GetScalar(); - } - - inline bool Node::HasMapKey(StringView key) const - { - if(!IsMap()) - return false; - return mpark::get_if(&Value)->StringMap.count(key) > 0; - } - - inline NodePtr Node::GetMapValueNode(StringView key) - { - if(!IsMap() || mpark::get_if(&Value)->StringMap.count(key) == 0) - return nullptr; - return mpark::get_if(&Value)->StringMap.at(key); - } - - inline ConstNodePtr Node::GetMapValueNode(StringView key) const - { - if(!IsMap() || mpark::get_if(&Value)->StringMap.count(key) == 0) - return nullptr; - return mpark::get_if(&Value)->StringMap.at(key); - } - - template - inline DS::Result Node::GetMapValueAlias(StringView key) const - { - DS_ASSERT_TRUE(IsMap()); - DS_ASSERT_TRUE(HasMapKey(key)); - return mpark::get_if(&Value)->StringMap.at(key)->GetAlias(); - } - - template - inline DS::Result Node::GetMapValueScalar(StringView key) const - { - DS_ASSERT_TRUE(IsMap()); - DS_ASSERT_TRUE(HasMapKey(key)); - return mpark::get_if(&Value)->StringMap.at(key)->GetScalar(); - } - - inline NodePtr Node::GetMapKeyNodeAt(uint32_t index) - { - if(!IsMap() || index >= mpark::get_if(&Value)->InsertedKeys.size()) - return nullptr; - return mpark::get_if(&Value)->InsertedKeys[index]; - } - - inline ConstNodePtr Node::GetMapKeyNodeAt(uint32_t index) const - { - if(!IsMap() || index >= mpark::get_if(&Value)->InsertedKeys.size()) - return nullptr; - return mpark::get_if(&Value)->InsertedKeys[index]; - } - - template - inline DS::Result Node::GetMapKeyAliasAt(uint32_t index) const - { - DS_ASSERT_TRUE(IsMap()); - DS_ASSERT_LT(index, mpark::get_if(&Value)->InsertedKeys.size()); - return mpark::get_if(&Value)->InsertedKeys[index]->GetAlias(); - } - - template - inline DS::Result Node::GetMapKeyScalarAt(uint32_t index) const - { - DS_ASSERT_TRUE(IsMap()); - DS_ASSERT_LT(index, mpark::get_if(&Value)->InsertedKeys.size()); - return mpark::get_if(&Value)->InsertedKeys[index]->GetScalar(); - } - - inline NodePtr Node::GetMapValueNodeAt(uint32_t index) - { - if(!IsMap() || index >= mpark::get_if(&Value)->InsertedKeys.size()) - return nullptr; - - NodePtr keyNode = mpark::get_if(&Value)->InsertedKeys[index]; - if(mpark::get_if(&Value)->Map.count(keyNode) == 0) - return nullptr; - - return mpark::get_if(&Value)->Map.at(keyNode); - } - - inline ConstNodePtr Node::GetMapValueNodeAt(uint32_t index) const - { - if(!IsMap() || index >= mpark::get_if(&Value)->InsertedKeys.size()) - return nullptr; - - NodePtr keyNode = mpark::get_if(&Value)->InsertedKeys[index]; - if(mpark::get_if(&Value)->Map.count(keyNode) == 0) - return nullptr; - - return mpark::get_if(&Value)->Map.at(keyNode); - } - - template - inline DS::Result Node::GetMapValueAliasAt(uint32_t index) const - { - DS_ASSERT_TRUE(IsMap()); - DS_ASSERT_LT(index, mpark::get_if(&Value)->InsertedKeys.size()); - - NodePtr keyNode = mpark::get_if(&Value)->InsertedKeys[index]; - DS_ASSERT_NOT_EQ(mpark::get_if(&Value)->Map.count(keyNode), 0); - - return mpark::get_if(&Value)->Map.at(keyNode)->GetAlias(); - } - - template - inline DS::Result Node::GetMapValueScalarAt(uint32_t index) const - { - DS_ASSERT_TRUE(IsMap()); - DS_ASSERT_LT(index, mpark::get_if(&Value)->InsertedKeys.size()); - - NodePtr keyNode = mpark::get_if(&Value)->InsertedKeys[index]; - DS_ASSERT_NOT_EQ(mpark::get_if(&Value)->Map.count(keyNode), 0); - - return mpark::get_if(&Value)->Map.at(keyNode)->GetScalar(); - } - - inline uint32_t Node::GetChildrenCount() const - { - if(IsAlias() || IsScalar()) - return 0; - else if(IsMap()) - return mpark::get_if(&Value)->InsertedKeys.size(); - else if(IsSequence()) - return mpark::get_if(&Value)->size(); - else - return 0; - } - - inline DS::Result Node::RemoveMapChild(StringView key) - { - DS_ASSERT_TRUE(IsMap()); - OrderedMap& orderedMap = *mpark::get_if(&Value); - if(orderedMap.StringMap.count(key) == 0) - return {}; - - //NodePtr valueNode = orderedMap.StringMap[key]; - NodePtr keyNode = nullptr; - int keyNodeIndex = -1; - for(int i = 0; i < orderedMap.InsertedKeys.size(); ++i) - { - if( orderedMap.InsertedKeys[i]->IsScalar() && - orderedMap.InsertedKeys.at(i)->GetScalar().Value() == key) - { - keyNode = orderedMap.InsertedKeys[i]; - keyNodeIndex = i; - break; - } - } - - DS_ASSERT_TRUE(keyNodeIndex != -1); - orderedMap.InsertedKeys.erase(orderedMap.InsertedKeys.begin() + keyNodeIndex); - orderedMap.Map.erase(keyNode); - orderedMap.StringMap.erase(key); - return {}; - } - } -} - - -#endif diff --git a/Include/runcpp2/ParseUtil.hpp b/Include/runcpp2/ParseUtil.hpp deleted file mode 100644 index 8effd50..0000000 --- a/Include/runcpp2/ParseUtil.hpp +++ /dev/null @@ -1,96 +0,0 @@ -#ifndef RUNCPP2_PARSE_UTIL_HPP -#define RUNCPP2_PARSE_UTIL_HPP - -#include "runcpp2/YamlLib.hpp" -#include "runcpp2/Data/ParseCommon.hpp" -#include "ssLogger/ssLog.hpp" - -#include -#include - -namespace runcpp2 -{ - struct NodeRequirement - { - std::string Name; - YAML::NodeType NodeType; - bool Required; - bool Nullable; - - NodeRequirement(); - NodeRequirement(const std::string& name, - YAML::NodeType nodeType, - bool required, - bool nullable); - }; - - bool CheckNodeRequirement( YAML::ConstNodePtr parentNode, - const std::string childName, - YAML::NodeType childType, - bool required, - bool nullable); - - bool CheckNodeRequirements( YAML::ConstNodePtr node, - const std::vector& requirements); - - - bool GetParsableInfo(const std::string& contentToParse, std::string& outParsableInfo); - - bool MergeYAML_NodeChildren(YAML::NodePtr nodeToMergeFrom, - YAML::NodePtr nodeToMergeTo, - YAML::ResourceHandle& yamlResouce); - - bool ExistAndHasChild( YAML::ConstNodePtr node, - const std::string& childName, - bool nullable = false); - - - std::string GetEscapedYAMLString(const std::string& input); - - bool ParseIncludes(const std::string& line, std::string& outIncludePath); - - template - bool ParsePlatformProfileMap( YAML::ConstNodePtr node, - const std::string& key, - std::unordered_map& result, - const std::string& errorMessage) - { - if(ExistAndHasChild(node, key)) - { - YAML::ConstNodePtr mapNode = node->GetMapValueNode(key); - - //If we skip platform profile - T defaultValue; - if(defaultValue.IsYAML_NodeParsableAsDefault(mapNode)) - { - if(defaultValue.ParseYAML_NodeWithProfile(mapNode, "DefaultProfile")) - result["DefaultPlatform"] = defaultValue; - else - return false; - } - else - { - if(!CheckNodeRequirement(node, key, YAML::NodeType::Map, false, true)) - return false; - - for(int i = 0; i < mapNode->GetChildrenCount(); ++i) - { - PlatformName platform = - mapNode ->GetMapKeyScalarAt(i) - .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); return false); - T value; - YAML::ConstNodePtr currentNode = mapNode->GetMapValueNodeAt(i); - if(!value.ParseYAML_Node(currentNode)) - { - ssLOG_ERROR("ScriptInfo: Failed to parse " << errorMessage); - return false; - } - result[platform] = value; - } - } - } - return true; - } -} - -#endif diff --git a/Include/runcpp2/PipelineSteps.hpp b/Include/runcpp2/PipelineSteps.hpp deleted file mode 100644 index a40e40e..0000000 --- a/Include/runcpp2/PipelineSteps.hpp +++ /dev/null @@ -1,133 +0,0 @@ -#ifndef RUNCPP2_PIPELINE_STEPS_HPP -#define RUNCPP2_PIPELINE_STEPS_HPP - -#include "runcpp2/Data/Profile.hpp" -#include "runcpp2/Data/PipelineResult.hpp" -#include "runcpp2/Data/ProfilesCommands.hpp" -#include "runcpp2/Data/ScriptInfo.hpp" -#include "runcpp2/Data/CmdOptions.hpp" - -#include "runcpp2/BuildsManager.hpp" -#include "runcpp2/IncludeManager.hpp" - -#if !defined(NOMINMAX) - #define NOMINMAX 1 -#endif -#include "ghc/filesystem.hpp" - -#include -#include -#include - -namespace runcpp2 -{ - DS::Result CopyFiles( const ghc::filesystem::path& destDir, - const std::vector& filePaths, - std::vector& outCopiedPaths); - - DS::Result RunProfileCommands(const Data::ProfilesCommands* commands, - const Data::Profile& profile, - const std::string& workingDir, - const std::string& commandType); - - DS::Result ValidateInputs(const std::string& scriptPath, - const std::vector& profiles, - ghc::filesystem::path& outAbsoluteScriptPath, - ghc::filesystem::path& outScriptDirectory, - std::string& outScriptName); - - DS::Result ParseAndValidateScriptInfo(const ghc::filesystem::path& absoluteScriptPath, - const ghc::filesystem::path& scriptDirectory, - const std::string& scriptName, - const bool buildExecutable, - Data::ScriptInfo& outScriptInfo); - - DS::Result HandleCleanup( const Data::ScriptInfo& scriptInfo, - const Data::Profile& profile, - const ghc::filesystem::path& scriptDirectory, - const ghc::filesystem::path& buildDir, - const ghc::filesystem::path& absoluteScriptPath, - BuildsManager& buildsManager); - - DS::Result InitializeBuildDirectory( const ghc::filesystem::path& configDir, - const ghc::filesystem::path& absoluteScriptPath, - bool useLocalBuildDir, - BuildsManager& outBuildsManager, - ghc::filesystem::path& outBuildDir, - IncludeManager& outIncludeManager); - - DS::Result ResolveScriptImports( Data::ScriptInfo& scriptInfo, - const ghc::filesystem::path& scriptPath, - const ghc::filesystem::path& buildDir); - - DS::Result CheckScriptInfoChanges(const ghc::filesystem::path& buildDir, - const Data::ScriptInfo& scriptInfo, - const Data::Profile& profile, - const ghc::filesystem::path& absoluteScriptPath, - const Data::ScriptInfo* lastScriptInfo, - const int maxThreads, - bool& outAllRecompileNeeded, - bool& outRelinkNeeded, - std::vector& outChangedDependencies); - - DS::Result - ProcessDependencies(Data::ScriptInfo& scriptInfo, - const Data::Profile& profile, - const ghc::filesystem::path& absoluteScriptPath, - const ghc::filesystem::path& buildDir, - const std::unordered_map& currentOptions, - const std::vector& changedDependencies, - const int maxThreads, - std::vector& outAvailableDependencies, - std::vector& outGatheredBinariesPaths); - - void SeparateDependencyFiles( const Data::FilesTypesInfo& filesTypes, - const std::vector& gatheredBinariesPaths, - std::vector& outLinkFilesPaths, - std::vector& outFilesToCopyPaths); - - DS::Result HandlePreBuild(const Data::ScriptInfo& scriptInfo, - const Data::Profile& profile, - const ghc::filesystem::path& buildDir); - - DS::Result HandlePostBuild( const Data::ScriptInfo& scriptInfo, - const Data::Profile& profile, - const ghc::filesystem::path& buildDir); - - DS::Result - RunCompiledOutput( const ghc::filesystem::path& target, - const ghc::filesystem::path& absoluteScriptPath, - const Data::ScriptInfo& scriptInfo, - const std::vector& runArgs, - const std::unordered_map& currentOptions, - int& returnStatus); - - DS::Result GetBuiltTargetPaths( const ghc::filesystem::path& buildDir, - const std::string& scriptName, - const Data::Profile& profile, - const std::unordered_map< CmdOptions, - std::string>& currentOptions, - const Data::ScriptInfo& scriptInfo, - std::vector& outTargets, - ghc::filesystem::path* outRunnableTarget); - - DS::Result GatherSourceFiles( const ghc::filesystem::path& absoluteScriptPath, - const Data::ScriptInfo& scriptInfo, - const Data::Profile& currentProfile, - std::vector& outSourcePaths); - - DS::Result GatherIncludePaths(const ghc::filesystem::path& scriptDirectory, - const Data::ScriptInfo& scriptInfo, - const Data::Profile& currentProfile, - const std::vector& dependencies, - std::vector& outIncludePaths); - - using SourceIncludeMap = std::unordered_map>; - - DS::Result GatherFilesIncludes( const std::vector& sourceFiles, - const std::vector& sourceHasCache, - const std::vector& includePaths, - SourceIncludeMap& outSourceIncludes); -} - -#endif diff --git a/Include/runcpp2/PlatformUtil.hpp b/Include/runcpp2/PlatformUtil.hpp deleted file mode 100644 index ff928dc..0000000 --- a/Include/runcpp2/PlatformUtil.hpp +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef RUNCPP2_PLATFORM_UTIL_HPP -#define RUNCPP2_PLATFORM_UTIL_HPP - -#include "runcpp2/Data/ParseCommon.hpp" -#include "runcpp2/Data/Profile.hpp" - -#if !defined(NOMINMAX) - #define NOMINMAX 1 -#endif - -#include "System2.h" - -#include -#include -#include -#include - -namespace ghc -{ - namespace filesystem - { - class path; - } -} - -namespace runcpp2 -{ - std::string ProcessPath(const std::string& path); - - std::vector GetPlatformNames(); - - bool RunCommand(const std::string& command, - const bool& captureOutput, - const std::string& runDirectory, - std::string& outOutput, - int& outReturnCode); - - #if defined(_WIN32) - std::string GetWindowsError(); - #endif - - template - inline bool HasValueFromPlatformMap(const std::unordered_map& map) - { - std::vector platformNames = runcpp2::GetPlatformNames(); - - for(int i = 0; i < platformNames.size(); ++i) - { - if(map.find(platformNames.at(i)) != map.end()) - return true; - } - - return false; - } - - template - inline const T* GetValueFromPlatformMap(const std::unordered_map& map) - { - std::vector platformNames = runcpp2::GetPlatformNames(); - - for(int i = 0; i < platformNames.size(); ++i) - { - if(map.find(platformNames.at(i)) != map.end()) - return &map.at(platformNames[i]); - } - - return nullptr; - } - - std::string GetFileExtensionWithoutVersion(const ghc::filesystem::path& path); - - template - inline bool HasValueFromProfileMap( const Data::Profile& profile, - const std::unordered_map& map) - { - std::vector profileNames; - profile.GetNames(profileNames); - - for(int i = 0; i < profileNames.size(); ++i) - { - if(map.find(profileNames.at(i)) != map.end()) - return true; - } - return false; - } - - template - inline const T* GetValueFromProfileMap( const Data::Profile& profile, - const std::unordered_map& map) - { - std::vector profileNames; - profile.GetNames(profileNames); - - for(int i = 0; i < profileNames.size(); ++i) - { - auto it = map.find(profileNames.at(i)); - if(it != map.end()) - return &it->second; - } - return nullptr; - } -} - -#endif diff --git a/Include/runcpp2/ProfileHelper.hpp b/Include/runcpp2/ProfileHelper.hpp deleted file mode 100644 index e462d54..0000000 --- a/Include/runcpp2/ProfileHelper.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef RUNCPP2_PROFILE_HELPER_HPP -#define RUNCPP2_PROFILE_HELPER_HPP - -#include "runcpp2/Data/Profile.hpp" -#include "runcpp2/Data/ScriptInfo.hpp" - -namespace runcpp2 -{ - int GetPreferredProfileIndex( const std::string& scriptPath, - const Data::ScriptInfo& scriptInfo, - const std::vector& profiles, - const std::string& configPreferredProfile); -} - -#endif \ No newline at end of file diff --git a/Include/runcpp2/StringUtil.hpp b/Include/runcpp2/StringUtil.hpp deleted file mode 100644 index c0791d1..0000000 --- a/Include/runcpp2/StringUtil.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef RUNCPP2_STRING_UTIL_HPP -#define RUNCPP2_STRING_UTIL_HPP - -#include -#include - -namespace runcpp2 -{ - void TrimLeft(std::string& str); - void TrimRight(std::string& str); - void Trim(std::string& str); - - void SplitString( const std::string& stringToSplit, - const std::string& splitter, - std::vector& outStrings); -} - -#endif \ No newline at end of file diff --git a/Include/runcpp2/YamlLib.hpp b/Include/runcpp2/YamlLib.hpp deleted file mode 100644 index 18fd0b8..0000000 --- a/Include/runcpp2/YamlLib.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef RUNCPP2_YAML_LIB_HPP -#define RUNCPP2_YAML_LIB_HPP - -#if !defined(NOMINMAX) - #define NOMINMAX 1 -#endif - -//#include "c4/std/string.hpp" -//#include "ryml.hpp" - -#include "LibYAML_Wrapper.hpp" - -#endif diff --git a/Include/runcpp2/runcpp2.hpp b/Include/runcpp2/runcpp2.hpp deleted file mode 100644 index ae3b46f..0000000 --- a/Include/runcpp2/runcpp2.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef RUNCPP2_RUNCPP2_HPP -#define RUNCPP2_RUNCPP2_HPP - -#include "runcpp2/Data/CmdOptions.hpp" -#include "runcpp2/Data/Profile.hpp" -#include "runcpp2/Data/ScriptInfo.hpp" -#include "runcpp2/Data/PipelineResult.hpp" - -#include -#include - -namespace runcpp2 -{ - struct OptionInfo - { - CmdOptions Option; - bool ValueExists; - std::string Value; - - inline OptionInfo( CmdOptions option, - bool valueExists = false, - const std::string& value = "") : Option(option), - ValueExists(valueExists), - Value(value) - {} - }; - - void GetDefaultScriptInfo(std::string& scriptInfo); - - void SetLogLevel(const std::string& logLevel); - - //NOTE: This should be run after running StartPipeline first - PipelineResult - CheckSourcesNeedUpdate( const std::string& scriptPath, - const std::vector& profiles, - const std::string& configPreferredProfile, - const Data::ScriptInfo& scriptInfo, - const std::unordered_map< CmdOptions, - std::string>& currentOptions, - const ghc::filesystem::file_time_type& prevFinalSourceWriteTime, - const ghc::filesystem::file_time_type& prevFinalIncludeWriteTime, - bool& outNeedsUpdate); - - PipelineResult StartPipeline( const std::string& scriptPath, - const std::vector& profiles, - const std::string& configPreferredProfile, - const std::unordered_map< CmdOptions, - std::string>& currentOptions, - const std::vector& runArgs, - const Data::ScriptInfo* lastScriptInfo, - const std::string& buildOutputDir, - Data::ScriptInfo& outScriptInfo, - ghc::filesystem::file_time_type& outFinalSourceWriteTime, - ghc::filesystem::file_time_type& outFinalIncludeWriteTime, - int& returnStatus); - - std::string PipelineResultToString(PipelineResult result); - - bool DownloadTutorial(char* runcppPath); -} - -#endif diff --git a/Jenkinsfile b/Jenkinsfile index f5f1e4a..41ad2f1 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -347,6 +347,7 @@ pipeline } post { failure { script { FAILED_STAGE = env.STAGE_NAME } } } } + //TODO: Move this to shell or cpp stage('Linux Integration Test') { agent { label 'linux' } @@ -359,10 +360,10 @@ pipeline bash "ls -lah ./Build/Src/Tests" bash "cd ./Build && ./runcpp2 -l " + "-c ../DefaultYAMLs/DefaultUserConfig.yaml " + - "--log-level info ../Tests/test.cpp" + "--log-level info ../Tests/Test.cpp" bash "cd ./Build && ./runcpp2 -l -b -o . " + "-c ../DefaultYAMLs/DefaultUserConfig.yaml " + - "--log-level info ../Tests/test.cpp" + "--log-level info ../Tests/Test.cpp" bash "ls -lah ./Build" @@ -373,7 +374,7 @@ pipeline bash "ls -lah ./Build/Src/Tests" bash "cd ./Build && ./runcpp2 -l -b -o . " + "-c ../DefaultYAMLs/DefaultUserConfig.yaml " + - "--log-level info ../Tests/test_static.cpp" + "--log-level info ../Tests/TestStatic.cpp" bash "ls -lah ./Build" @@ -384,8 +385,40 @@ pipeline bash "ls -lah ./Build/Src/Tests" bash "cd ./Build && ./runcpp2 -l " + "-c ../DefaultYAMLs/DefaultUserConfig.yaml " + - "--log-level info ../Tests/testLocalDependency.cpp" + "--log-level info ../Tests/TestLocalDependency.cpp" + cleanWs() + bash "ls -lah" + unstash 'linux_build' + bash "ls -lah" + bash "ls -lah ./Build/Src/Tests" + bash "cd ./Build && ./runcpp2 -l " + + "-c ../DefaultYAMLs/DefaultUserConfig.yaml " + + "--log-level info ../Tests/TestSeparateYaml.cpp" + + cleanWs() + bash "ls -lah" + unstash 'linux_build' + bash "ls -lah" + bash "ls -lah ./Build/Src/Tests" + script + { + def retResult = + sh( script: "cd ./Build && ./runcpp2 -l " + + "-c ../DefaultYAMLs/DefaultUserConfig.yaml " + + "--log-level info ../Tests/TestMissingSource.yaml", + returnStatus: true) + echo("Return result: ${retResult}") + } + + cleanWs() + bash "ls -lah" + unstash 'linux_build' + bash "ls -lah" + bash "ls -lah ./Build/Src/Tests" + bash "cd ./Build && ./runcpp2 -l " + + "-c ../DefaultYAMLs/DefaultUserConfig.yaml " + + "--log-level info ../Tests/YamlOnlyTest.yaml" cleanWs() bash "ls -lah" @@ -415,6 +448,7 @@ pipeline } post { failure { script { FAILED_STAGE = env.STAGE_NAME } } } } + //TODO: Move this to shell or cpp stage('Windows Integration Test') { agent { label 'windows' } @@ -426,10 +460,10 @@ pipeline bat 'dir' bat "cd .\\Build\\Debug && .\\runcpp2.exe -l " + "-c ..\\..\\DefaultYAMLs\\DefaultUserConfig.yaml " + - "--log-level info ..\\..\\Tests\\test.cpp" + "--log-level info ..\\..\\Tests\\Test.cpp" bat "cd .\\Build\\Debug && .\\runcpp2.exe -l -b -o . " + "-c ..\\..\\DefaultYAMLs\\DefaultUserConfig.yaml " + - "--log-level info ..\\..\\Tests\\test.cpp" + "--log-level info ..\\..\\Tests\\Test.cpp" bat "dir .\\Build\\Debug" @@ -439,7 +473,7 @@ pipeline bat 'dir' bat "cd .\\Build\\Debug && .\\runcpp2.exe -l -b -o . " + "-c ..\\..\\DefaultYAMLs\\DefaultUserConfig.yaml " + - "--log-level info ..\\..\\Tests\\test_static.cpp" + "--log-level info ..\\..\\Tests\\TestStatic.cpp" bat "dir .\\Build\\Debug" @@ -449,8 +483,37 @@ pipeline bat 'dir' bat "cd .\\Build\\Debug && .\\runcpp2.exe -l " + "-c ..\\..\\DefaultYAMLs\\DefaultUserConfig.yaml " + - "--log-level info ..\\..\\Tests\\testLocalDependency.cpp" + "--log-level info ..\\..\\Tests\\TestLocalDependency.cpp" + cleanWs() + bat 'dir' + unstash 'windows_build' + bat 'dir' + bat "cd .\\Build\\Debug && .\\runcpp2.exe -l " + + "-c ..\\..\\DefaultYAMLs\\DefaultUserConfig.yaml " + + "--log-level info ..\\..\\Tests\\TestSeparateYaml.cpp" + + cleanWs() + bat 'dir' + unstash 'windows_build' + bat 'dir' + script + { + def retResult = + bat(script: "cd .\\Build\\Debug && .\\runcpp2.exe -l " + + "-c ..\\..\\DefaultYAMLs\\DefaultUserConfig.yaml " + + "--log-level info ..\\..\\Tests\\TestMissingSource.yaml", + returnStatus: true) + echo("Return result: ${retResult}") + } + + cleanWs() + bat 'dir' + unstash 'windows_build' + bat 'dir' + bat "cd .\\Build\\Debug && .\\runcpp2.exe -l " + + "-c ..\\..\\DefaultYAMLs\\DefaultUserConfig.yaml " + + "--log-level info ..\\..\\Tests\\YamlOnlyTest.yaml" cleanWs() bat 'dir' diff --git a/Include/Tests/BuildsManager/MockComponents.hpp b/Src/Tests/BuildsManager/MockComponents.hpp similarity index 96% rename from Include/Tests/BuildsManager/MockComponents.hpp rename to Src/Tests/BuildsManager/MockComponents.hpp index 3fd980f..2da4ae6 100644 --- a/Include/Tests/BuildsManager/MockComponents.hpp +++ b/Src/Tests/BuildsManager/MockComponents.hpp @@ -135,13 +135,7 @@ namespace Mock_std #define std Mock_std #if INTERNAL_RUNCPP2_UNDEF_MOCKS - #undef exists - #undef create_directories - #undef remove_all - #undef ifstream - #undef ofstream - #undef hash - #undef std + #include "./UndefMocks.hpp" #endif #endif diff --git a/Src/Tests/BuildsManager/UndefMocks.hpp b/Src/Tests/BuildsManager/UndefMocks.hpp new file mode 100644 index 0000000..5101a6d --- /dev/null +++ b/Src/Tests/BuildsManager/UndefMocks.hpp @@ -0,0 +1,7 @@ +#undef exists +#undef create_directories +#undef remove_all +#undef ifstream +#undef ofstream +#undef hash +#undef std diff --git a/Src/Tests/CMakeLists.txt b/Src/Tests/CMakeLists.txt index c4f3cb2..633ae13 100644 --- a/Src/Tests/CMakeLists.txt +++ b/Src/Tests/CMakeLists.txt @@ -1,32 +1,50 @@ -add_executable(BuildsManagerTest "${CMAKE_CURRENT_LIST_DIR}/../runcpp2/BuildsManager.cpp" - "${CMAKE_CURRENT_LIST_DIR}/BuildsManagerTest.cpp") +# set(INTERNAL_RUNCPP2_UNIT_TESTS_GENERIC 1) +set(INTERNAL_RUNCPP2_UNIT_TESTS_CONFIG_PARSING 2) +set(INTERNAL_RUNCPP2_UNIT_TESTS_INCLUDE_MANAGER 3) +set(INTERNAL_RUNCPP2_UNIT_TESTS_BUILDS_MANAGER 4) + +function(add_unit_test_defines TEST_NAME) + target_compile_definitions( "${TEST_NAME}" + # PUBLIC INTERNAL_RUNCPP2_UNIT_TESTS_GENERIC=${INTERNAL_RUNCPP2_UNIT_TESTS_GENERIC} + PUBLIC INTERNAL_RUNCPP2_UNIT_TESTS_CONFIG_PARSING=${INTERNAL_RUNCPP2_UNIT_TESTS_CONFIG_PARSING} + PUBLIC INTERNAL_RUNCPP2_UNIT_TESTS_INCLUDE_MANAGER=${INTERNAL_RUNCPP2_UNIT_TESTS_INCLUDE_MANAGER} + PUBLIC INTERNAL_RUNCPP2_UNIT_TESTS_BUILDS_MANAGER=${INTERNAL_RUNCPP2_UNIT_TESTS_BUILDS_MANAGER}) +endfunction() + + + +add_executable(BuildsManagerTest "${CMAKE_CURRENT_LIST_DIR}/BuildsManagerTest.cpp") +target_link_libraries(BuildsManagerTest PRIVATE runcpp2Lib) +target_compile_definitions(BuildsManagerTest PRIVATE INTERNAL_RUNCPP2_UNIT_TESTS=${INTERNAL_RUNCPP2_UNIT_TESTS_BUILDS_MANAGER}) +add_unit_test_defines(BuildsManagerTest) + + + + +add_executable(IncludeManagerTest "${CMAKE_CURRENT_LIST_DIR}/IncludeManagerTest.cpp") +target_link_libraries(IncludeManagerTest PRIVATE runcpp2Lib) +target_compile_definitions(IncludeManagerTest PRIVATE INTERNAL_RUNCPP2_UNIT_TESTS=${INTERNAL_RUNCPP2_UNIT_TESTS_INCLUDE_MANAGER}) +add_unit_test_defines(IncludeManagerTest) -target_include_directories(BuildsManagerTest PRIVATE "${CMAKE_CURRENT_LIST_DIR}/../../Include") -target_compile_options(BuildsManagerTest PRIVATE "${RUNCPP2_STANDARD_COMPILE_FLAGS}") -target_link_libraries(BuildsManagerTest PRIVATE ghc_filesystem CppOverride ssLogger DSResult) -target_compile_definitions(BuildsManagerTest PRIVATE INTERNAL_RUNCPP2_UNIT_TESTS=1) -add_executable(IncludeManagerTest "${CMAKE_CURRENT_LIST_DIR}/../runcpp2/IncludeManager.cpp" - "${CMAKE_CURRENT_LIST_DIR}/IncludeManagerTest.cpp") -target_include_directories(IncludeManagerTest PRIVATE "${CMAKE_CURRENT_LIST_DIR}/../../Include") -target_compile_options(IncludeManagerTest PRIVATE "${RUNCPP2_STANDARD_COMPILE_FLAGS}") -target_link_libraries(IncludeManagerTest PRIVATE ghc_filesystem CppOverride ssLogger DSResult) -target_compile_definitions(IncludeManagerTest PRIVATE INTERNAL_RUNCPP2_UNIT_TESTS=1) add_executable(ConfigParsingTest "${CMAKE_CURRENT_LIST_DIR}/ConfigParsingTest.cpp") -target_include_directories(ConfigParsingTest PRIVATE "${CMAKE_CURRENT_LIST_DIR}/../../Include") -target_compile_options(ConfigParsingTest PRIVATE "${RUNCPP2_STANDARD_COMPILE_FLAGS}") -target_link_libraries(ConfigParsingTest PRIVATE ghc_filesystem CppOverride ssLogger runcpp2Override DSResult) -target_compile_definitions(ConfigParsingTest PRIVATE INTERNAL_RUNCPP2_UNIT_TESTS=1) +target_link_libraries(ConfigParsingTest PRIVATE runcpp2Lib) +target_compile_definitions(ConfigParsingTest PRIVATE INTERNAL_RUNCPP2_UNIT_TESTS=${INTERNAL_RUNCPP2_UNIT_TESTS_CONFIG_PARSING}) +add_unit_test_defines(ConfigParsingTest) + + + function(create_data_test TEST_NAME) add_executable("${TEST_NAME}" "${CMAKE_CURRENT_LIST_DIR}/Data/${TEST_NAME}.cpp") target_compile_options("${TEST_NAME}" PRIVATE "${RUNCPP2_STANDARD_COMPILE_FLAGS}") - target_compile_definitions("${TEST_NAME}" PRIVATE INTERNAL_RUNCPP2_UNIT_TESTS=1 TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES=1) - target_link_libraries("${TEST_NAME}" PUBLIC runcpp2Lib ssLogger) + target_compile_definitions("${TEST_NAME}" PRIVATE # INTERNAL_RUNCPP2_UNIT_TESTS=${INTERNAL_RUNCPP2_UNIT_TESTS_GENERIC} + TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES=1) + target_link_libraries("${TEST_NAME}" PUBLIC runcpp2Lib) endfunction() create_data_test(BuildTypeTest) diff --git a/Include/Tests/ConfigParsing/MockComponents.hpp b/Src/Tests/ConfigParsing/MockComponents.hpp similarity index 97% rename from Include/Tests/ConfigParsing/MockComponents.hpp rename to Src/Tests/ConfigParsing/MockComponents.hpp index fe9e609..1d10d92 100644 --- a/Include/Tests/ConfigParsing/MockComponents.hpp +++ b/Src/Tests/ConfigParsing/MockComponents.hpp @@ -97,10 +97,7 @@ namespace Mock_std #define std Mock_std #if INTERNAL_RUNCPP2_UNDEF_MOCKS - #undef exists - #undef is_directory - #undef ifstream - #undef std + #include "./UndefMocks.hpp" #endif #endif diff --git a/Src/Tests/ConfigParsing/UndefMocks.hpp b/Src/Tests/ConfigParsing/UndefMocks.hpp new file mode 100644 index 0000000..e648923 --- /dev/null +++ b/Src/Tests/ConfigParsing/UndefMocks.hpp @@ -0,0 +1,4 @@ +#undef exists +#undef is_directory +#undef ifstream +#undef std diff --git a/Src/Tests/ConfigParsingTest.cpp b/Src/Tests/ConfigParsingTest.cpp index 3f31a07..998f18c 100644 --- a/Src/Tests/ConfigParsingTest.cpp +++ b/Src/Tests/ConfigParsingTest.cpp @@ -1,10 +1,12 @@ #include "runcpp2/ConfigParsing.hpp" - #include "DSResult/DSResult.hpp" #include "CppOverride.hpp" #include "ssLogger/ssLog.hpp" #include "MacroPowerToys.h" +//NOTE: #include "runcpp2/DefaultYAMLs.c" at the end +//NOTE: #include "runcpp2/LibYamlImpl.cpp" at the end + CO_DECLARE_INSTANCE(OverrideInstance); #define INTERNAL_RUNCPP2_UNDEF_MOCKS 1 @@ -17,17 +19,6 @@ CO_DECLARE_INSTANCE(OverrideInstance); #endif -//Mock DefaultUserConfig for testing -extern "C" const uint8_t DefaultUserConfig[] = {0}; -extern "C" const size_t DefaultUserConfig_size = 0; -extern "C" const uint8_t CommonFileTypes[] = {0}; -extern "C" const size_t CommonFileTypes_size = 0; -extern "C" const uint8_t G_PlusPlus[] = {0}; -extern "C" const size_t G_PlusPlus_size = 0; -extern "C" const uint8_t Vs2022_v17Plus[] = {0}; -extern "C" const size_t Vs2022_v17Plus_size = 0; - - DS::Result TestMain() { using namespace CppOverride; @@ -618,3 +609,5 @@ int main(int argc, char** argv) return 1; } +#include "runcpp2/DefaultYAMLs.c" +#include "runcpp2/LibYamlImpl.cpp" diff --git a/Src/Tests/Data/DependencyInfoTest.cpp b/Src/Tests/Data/DependencyInfoTest.cpp index 459121c..961ad4a 100644 --- a/Src/Tests/Data/DependencyInfoTest.cpp +++ b/Src/Tests/Data/DependencyInfoTest.cpp @@ -1,5 +1,5 @@ #include "runcpp2/Data/DependencyInfo.hpp" -#include "runcpp2/YamlLib.hpp" +#include "runcpp2/LibYAML_Wrapper.hpp" #include "runcpp2/runcpp2.hpp" #include "runcpp2/DeferUtil.hpp" #include "ssLogger/ssLog.hpp" diff --git a/Src/Tests/Data/DependencySourceTest.cpp b/Src/Tests/Data/DependencySourceTest.cpp index 4d905fd..085c7d5 100644 --- a/Src/Tests/Data/DependencySourceTest.cpp +++ b/Src/Tests/Data/DependencySourceTest.cpp @@ -1,5 +1,5 @@ #include "runcpp2/Data/DependencySource.hpp" -#include "runcpp2/YamlLib.hpp" +#include "runcpp2/LibYAML_Wrapper.hpp" #include "runcpp2/runcpp2.hpp" #include "runcpp2/DeferUtil.hpp" #include "ssLogger/ssLog.hpp" diff --git a/Src/Tests/Data/ProfileTest.cpp b/Src/Tests/Data/ProfileTest.cpp index efec89e..aadb003 100644 --- a/Src/Tests/Data/ProfileTest.cpp +++ b/Src/Tests/Data/ProfileTest.cpp @@ -1,6 +1,6 @@ #include "runcpp2/Data/Profile.hpp" #include "runcpp2/runcpp2.hpp" -#include "runcpp2/YamlLib.hpp" +#include "runcpp2/LibYAML_Wrapper.hpp" #include "runcpp2/DeferUtil.hpp" #include "ssLogger/ssLog.hpp" diff --git a/Src/Tests/Data/ScriptInfoTest.cpp b/Src/Tests/Data/ScriptInfoTest.cpp index 6e3c8a3..b163b5f 100644 --- a/Src/Tests/Data/ScriptInfoTest.cpp +++ b/Src/Tests/Data/ScriptInfoTest.cpp @@ -1,5 +1,5 @@ #include "runcpp2/Data/ScriptInfo.hpp" -#include "runcpp2/YamlLib.hpp" +#include "runcpp2/LibYAML_Wrapper.hpp" #include "runcpp2/runcpp2.hpp" #include "runcpp2/DeferUtil.hpp" #include "ssLogger/ssLog.hpp" diff --git a/Include/Tests/IncludeManager/MockComponents.hpp b/Src/Tests/IncludeManager/MockComponents.hpp similarity index 95% rename from Include/Tests/IncludeManager/MockComponents.hpp rename to Src/Tests/IncludeManager/MockComponents.hpp index f09b3b8..5082e1d 100644 --- a/Include/Tests/IncludeManager/MockComponents.hpp +++ b/Src/Tests/IncludeManager/MockComponents.hpp @@ -123,14 +123,7 @@ namespace Mock_std #define std Mock_std #if INTERNAL_RUNCPP2_UNDEF_MOCKS - #undef exists - #undef create_directories - #undef last_write_time - #undef ifstream - #undef ofstream - #undef hash - #undef getline - #undef std + #include "./UndefMocks.hpp" #endif #endif diff --git a/Src/Tests/IncludeManager/UndefMocks.hpp b/Src/Tests/IncludeManager/UndefMocks.hpp new file mode 100644 index 0000000..008f855 --- /dev/null +++ b/Src/Tests/IncludeManager/UndefMocks.hpp @@ -0,0 +1,8 @@ +#undef exists +#undef create_directories +#undef last_write_time +#undef ifstream +#undef ofstream +#undef hash +#undef getline +#undef std diff --git a/Src/runcpp2/BuildsManager.cpp b/Src/runcpp2/BuildsManager.cpp deleted file mode 100644 index 9d617b0..0000000 --- a/Src/runcpp2/BuildsManager.cpp +++ /dev/null @@ -1,337 +0,0 @@ -#include "runcpp2/BuildsManager.hpp" -#include "ssLogger/ssLog.hpp" - -#include -#include - -#if INTERNAL_RUNCPP2_UNIT_TESTS - #include "Tests/BuildsManager/MockComponents.hpp" -#else - #define CO_NO_OVERRIDE 1 - #include "CppOverride.hpp" -#endif - -namespace runcpp2 -{ - bool BuildsManager::ParseMappings(const std::string& mappingsContent) - { - ssLOG_FUNC_DEBUG(); - - std::error_code e; - std::istringstream iss(mappingsContent); - std::string line; - - //Read each line in the mappings file, and extract the mappings - while(std::getline(iss, line)) - { - if(line.empty()) - continue; - - std::size_t foundComma = line.find(","); - - if(foundComma == std::string::npos) - { - ssLOG_ERROR("Failed to parse line: " << line); - return false; - } - - std::string scriptPath = line.substr(0, foundComma); - - if(!ghc::filesystem::path(scriptPath).is_absolute()) - { - ssLOG_ERROR("Script path " << scriptPath << " is not absolute"); - return false; - } - - if(foundComma == line.size() - 1) - { - ssLOG_ERROR("End of line reached, mapped path not found"); - ssLOG_ERROR("Failed to parse line" << line); - return false; - } - - std::string mappedPath = line.substr(foundComma + 1, line.size() - foundComma - 1); - - if(!ghc::filesystem::path(mappedPath).is_relative()) - { - ssLOG_ERROR("Mapped path " << mappedPath << " is not relative"); - return false; - } - - ssLOG_DEBUG("BuildDirectory / mappedPath: " << - ghc::filesystem::path(BuildDirectory / mappedPath).string()); - - //We don't add the mapped path to record if it doesn't exist - if(!ghc::filesystem::exists(BuildDirectory / mappedPath, e)) - continue; - - Mappings[scriptPath] = mappedPath; - ReverseMappings[mappedPath] = scriptPath; - } - - return true; - } - - std::string BuildsManager::ProcessPath(const std::string& path) - { - std::string processedPath; - for(int i = 0; i < path.size(); ++i) - { - if(path[i] == '\\') - processedPath += '/'; - else - processedPath += path[i]; - } - - return processedPath; - }; - - BuildsManager::BuildsManager(const ghc::filesystem::path& configDirectory) : - ConfigDirectory(configDirectory), - Mappings(), - BuildDirectory(configDirectory / "CachedBuilds"), - MappingsFile(BuildDirectory / "Mappings.csv"), - Initialized(false) - {} - - BuildsManager::BuildsManager(const BuildsManager& other) - { - *this = other; - } - - BuildsManager& BuildsManager::operator=(const BuildsManager& other) - { - ConfigDirectory = other.ConfigDirectory; - Mappings = other.Mappings; - BuildDirectory = other.BuildDirectory; - MappingsFile = other.MappingsFile; - Initialized = other.Initialized; - return *this; - } - - BuildsManager::~BuildsManager() - {} - - bool BuildsManager::Initialize() - { - ssLOG_FUNC_DEBUG(); - - std::error_code e; - - ssLOG_INFO("MappingsFile: " << MappingsFile.string()); - ssLOG_INFO("BuildDirectory: " << BuildDirectory.string()); - - //Read the build mappings if exist, if not create it. - if(ghc::filesystem::exists(MappingsFile, e)) - { - std::ifstream inputFile(MappingsFile); - if (!inputFile.is_open()) - { - ssLOG_ERROR("Failed to open file: " << MappingsFile.string()); - return false; - } - std::stringstream buffer; - buffer << inputFile.rdbuf(); - std::string source(buffer.str()); - inputFile.close(); - - if(!ParseMappings(source)) - return false; - } - else - { - //Create the Builds directory - if(!ghc::filesystem::exists(BuildDirectory, e)) - { - if(!ghc::filesystem::create_directories(BuildDirectory, e)) - { - ssLOG_ERROR("Failed to create directory " << BuildDirectory.string()); - return false; - } - } - - std::ofstream newFile(MappingsFile); - - if(!newFile.is_open()) - { - ssLOG_ERROR("Failed to open file: " << MappingsFile.string()); - return false; - } - - newFile.close(); - } - - Initialized = true; - return true; - } - - bool BuildsManager::CreateBuildMapping(const ghc::filesystem::path& scriptPath) - { - CO_INSERT_MEMBER_IMPL(OverrideInstance, bool, (scriptPath)); - - ssLOG_FUNC_DEBUG(); - - if(!Initialized) - return false; - - if(scriptPath.is_relative()) - { - ssLOG_ERROR("Cannot create build mapping with relative path"); - return false; - } - - ghc::filesystem::path cleanScriptPath = scriptPath.lexically_normal(); - std::string processScriptPathStr = ProcessPath(cleanScriptPath.string()); - - if(Mappings.count(processScriptPathStr) > 0) - return true; - - //Create build folder for script - constexpr int MAX_TRIES = 1000; - std::size_t scriptPathHash; - std::string scriptBuildPathStr; - int counter = 0; - do - { - scriptPathHash = std::hash{}(processScriptPathStr + std::to_string(counter)); - scriptBuildPathStr = "./" + std::to_string(scriptPathHash); - - if(ReverseMappings.count(scriptBuildPathStr) == 0) - break; - - counter++; - } - while(counter < MAX_TRIES); - - if(counter == MAX_TRIES) - { - ssLOG_ERROR("Failed to get unique hash for " << cleanScriptPath.string() << - " after " << MAX_TRIES << " attempts"); - return false; - } - - ghc::filesystem::path scriptBuildPath = BuildDirectory / scriptBuildPathStr; - std::error_code e; - - //Removing existing build folder if exists - if(ghc::filesystem::exists(scriptBuildPath, e)) - { - if(!ghc::filesystem::remove_all(scriptBuildPath, e)) - { - ssLOG_ERROR("Failed to remove " << scriptBuildPath); - return false; - } - } - - //Create the build folder we need - if(!ghc::filesystem::create_directories(scriptBuildPath, e)) - { - ssLOG_ERROR("Failed to create directory " << scriptBuildPath.string()); - return false; - } - - ssLOG_INFO("Build path " << scriptBuildPath << " for " << cleanScriptPath); - Mappings[processScriptPathStr] = scriptBuildPathStr; - ReverseMappings[scriptBuildPathStr] = processScriptPathStr; - return true; - } - - bool BuildsManager::RemoveBuildMapping(const ghc::filesystem::path& scriptPath) - { - if(!Initialized) - return false; - - if(scriptPath.is_relative()) - { - ssLOG_ERROR("Cannot have build mapping with relative path"); - return false; - } - - ghc::filesystem::path cleanScriptPath = scriptPath.lexically_normal(); - std::string processScriptPathStr = ProcessPath(cleanScriptPath.string()); - if(Mappings.count(processScriptPathStr) == 0) - return true; - - ReverseMappings.erase(Mappings.at(processScriptPathStr)); - Mappings.erase(processScriptPathStr); - assert(ReverseMappings.size() == Mappings.size()); - return true; - } - - bool BuildsManager::HasBuildMapping(const ghc::filesystem::path& scriptPath) - { - if(!Initialized) - return false; - - if(scriptPath.is_relative()) - { - ssLOG_ERROR("Cannot have build mapping with relative path"); - return false; - } - - ghc::filesystem::path cleanScriptPath = scriptPath.lexically_normal(); - return Mappings.count(ProcessPath(cleanScriptPath.string())); - } - - bool BuildsManager::GetBuildMapping(const ghc::filesystem::path& scriptPath, - ghc::filesystem::path& outPath) - { - if(!Initialized) - return false; - - if(scriptPath.is_relative()) - { - ssLOG_ERROR("Cannot have build mapping with relative path"); - return false; - } - - ghc::filesystem::path cleanScriptPath = scriptPath.lexically_normal(); - - //If it doesn't exist, create the mapping - if(!Mappings.count(ProcessPath(cleanScriptPath.string()))) - { - if(!CreateBuildMapping(cleanScriptPath)) - return false; - } - - outPath = BuildDirectory.string() + "/" + Mappings.at(ProcessPath(cleanScriptPath.string())); - return true; - } - - bool BuildsManager::RemoveAllBuildsMappings() - { - if(!Initialized) - return false; - - Mappings.clear(); - ReverseMappings.clear(); - return true; - } - - bool BuildsManager::SaveBuildsMappings() - { - if(!Initialized) - return false; - - std::ofstream buildsMappings(MappingsFile); - - if(!buildsMappings.is_open()) - { - ssLOG_ERROR("Failed to open file: " << MappingsFile.string()); - return false; - } - - for(std::unordered_map::iterator it = Mappings.begin(); - it != Mappings.end(); ++it) - { - ssLOG_DEBUG("Writing mapping: " << it->first << "," << it->second); - buildsMappings << it->first << "," << it->second << std::endl; - } - - buildsMappings.close(); - return true; - } -} - - - diff --git a/Src/runcpp2/BuildsManager.hpp b/Src/runcpp2/BuildsManager.hpp new file mode 100644 index 0000000..13965e1 --- /dev/null +++ b/Src/runcpp2/BuildsManager.hpp @@ -0,0 +1,375 @@ +#ifndef RUNCPP2_BUILDS_MANAGER_HPP +#define RUNCPP2_BUILDS_MANAGER_HPP + +#if !defined(NOMINMAX) + #define NOMINMAX 1 +#endif +#include "ghc/filesystem.hpp" +#include "ssLogger/ssLog.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(INTERNAL_RUNCPP2_UNIT_TESTS) && \ + INTERNAL_RUNCPP2_UNIT_TESTS == INTERNAL_RUNCPP2_UNIT_TESTS_BUILDS_MANAGER + + #include "Tests/BuildsManager/MockComponents.hpp" + class BuildsManagerAccessor; +#else + #define CO_NO_OVERRIDE 1 + #include "CppOverride.hpp" +#endif + +namespace runcpp2 +{ + class BuildsManager + { + #if defined(INTERNAL_RUNCPP2_UNIT_TESTS) && \ + INTERNAL_RUNCPP2_UNIT_TESTS == INTERNAL_RUNCPP2_UNIT_TESTS_BUILDS_MANAGER + + friend class ::BuildsManagerAccessor; + #endif + + private: + ghc::filesystem::path ConfigDirectory; + + std::unordered_map Mappings; + std::unordered_map ReverseMappings; + + + ghc::filesystem::path BuildDirectory; + ghc::filesystem::path MappingsFile; + bool Initialized; + + inline bool ParseMappings(const std::string& mappingsContent) + { + ssLOG_FUNC_DEBUG(); + + std::error_code e; + std::istringstream iss(mappingsContent); + std::string line; + + //Read each line in the mappings file, and extract the mappings + while(std::getline(iss, line)) + { + if(line.empty()) + continue; + + std::size_t foundComma = line.find(","); + + if(foundComma == std::string::npos) + { + ssLOG_ERROR("Failed to parse line: " << line); + return false; + } + + std::string scriptPath = line.substr(0, foundComma); + + if(!ghc::filesystem::path(scriptPath).is_absolute()) + { + ssLOG_ERROR("Script path " << scriptPath << " is not absolute"); + return false; + } + + if(foundComma == line.size() - 1) + { + ssLOG_ERROR("End of line reached, mapped path not found"); + ssLOG_ERROR("Failed to parse line" << line); + return false; + } + + std::string mappedPath = line.substr(foundComma + 1, line.size() - foundComma - 1); + + if(!ghc::filesystem::path(mappedPath).is_relative()) + { + ssLOG_ERROR("Mapped path " << mappedPath << " is not relative"); + return false; + } + + ssLOG_DEBUG("BuildDirectory / mappedPath: " << + ghc::filesystem::path(BuildDirectory / mappedPath).string()); + + //We don't add the mapped path to record if it doesn't exist + if(!ghc::filesystem::exists(BuildDirectory / mappedPath, e)) + continue; + + Mappings[scriptPath] = mappedPath; + ReverseMappings[mappedPath] = scriptPath; + } + + return true; + } + + inline std::string ProcessPath(const std::string& path) + { + std::string processedPath; + for(int i = 0; i < path.size(); ++i) + { + if(path[i] == '\\') + processedPath += '/'; + else + processedPath += path[i]; + } + + return processedPath; + } + + public: + inline BuildsManager(const ghc::filesystem::path& configDirectory) : + ConfigDirectory(configDirectory), + Mappings(), + BuildDirectory(configDirectory / "CachedBuilds"), + MappingsFile(BuildDirectory / "Mappings.csv"), + Initialized(false) + {} + + inline BuildsManager(const BuildsManager& other) + { + *this = other; + } + + inline BuildsManager& operator=(const BuildsManager& other) + { + ConfigDirectory = other.ConfigDirectory; + Mappings = other.Mappings; + BuildDirectory = other.BuildDirectory; + MappingsFile = other.MappingsFile; + Initialized = other.Initialized; + return *this; + } + + inline ~BuildsManager(){} + + inline bool Initialize() + { + ssLOG_FUNC_DEBUG(); + + std::error_code e; + + ssLOG_INFO("MappingsFile: " << MappingsFile.string()); + ssLOG_INFO("BuildDirectory: " << BuildDirectory.string()); + + //Read the build mappings if exist, if not create it. + if(ghc::filesystem::exists(MappingsFile, e)) + { + std::ifstream inputFile(MappingsFile); + if (!inputFile.is_open()) + { + ssLOG_ERROR("Failed to open file: " << MappingsFile.string()); + return false; + } + std::stringstream buffer; + buffer << inputFile.rdbuf(); + std::string source(buffer.str()); + inputFile.close(); + + if(!ParseMappings(source)) + return false; + } + else + { + //Create the Builds directory + if(!ghc::filesystem::exists(BuildDirectory, e)) + { + if(!ghc::filesystem::create_directories(BuildDirectory, e)) + { + ssLOG_ERROR("Failed to create directory " << BuildDirectory.string()); + return false; + } + } + + std::ofstream newFile(MappingsFile); + + if(!newFile.is_open()) + { + ssLOG_ERROR("Failed to open file: " << MappingsFile.string()); + return false; + } + + newFile.close(); + } + + Initialized = true; + return true; + } + inline bool CreateBuildMapping(const ghc::filesystem::path& scriptPath) + { + CO_INSERT_MEMBER_IMPL(OverrideInstance, bool, (scriptPath)); + + ssLOG_FUNC_DEBUG(); + + if(!Initialized) + return false; + + if(scriptPath.is_relative()) + { + ssLOG_ERROR("Cannot create build mapping with relative path"); + return false; + } + + ghc::filesystem::path cleanScriptPath = scriptPath.lexically_normal(); + std::string processScriptPathStr = ProcessPath(cleanScriptPath.string()); + + if(Mappings.count(processScriptPathStr) > 0) + return true; + + //Create build folder for script + constexpr int MAX_TRIES = 1000; + std::size_t scriptPathHash; + std::string scriptBuildPathStr; + int counter = 0; + do + { + scriptPathHash = std::hash{}(processScriptPathStr + std::to_string(counter)); + scriptBuildPathStr = "./" + std::to_string(scriptPathHash); + + if(ReverseMappings.count(scriptBuildPathStr) == 0) + break; + + counter++; + } + while(counter < MAX_TRIES); + + if(counter == MAX_TRIES) + { + ssLOG_ERROR("Failed to get unique hash for " << cleanScriptPath.string() << + " after " << MAX_TRIES << " attempts"); + return false; + } + + ghc::filesystem::path scriptBuildPath = BuildDirectory / scriptBuildPathStr; + std::error_code e; + + //Removing existing build folder if exists + if(ghc::filesystem::exists(scriptBuildPath, e)) + { + if(!ghc::filesystem::remove_all(scriptBuildPath, e)) + { + ssLOG_ERROR("Failed to remove " << scriptBuildPath); + return false; + } + } + + //Create the build folder we need + if(!ghc::filesystem::create_directories(scriptBuildPath, e)) + { + ssLOG_ERROR("Failed to create directory " << scriptBuildPath.string()); + return false; + } + + ssLOG_INFO("Build path " << scriptBuildPath << " for " << cleanScriptPath); + Mappings[processScriptPathStr] = scriptBuildPathStr; + ReverseMappings[scriptBuildPathStr] = processScriptPathStr; + return true; + } + + inline bool RemoveBuildMapping(const ghc::filesystem::path& scriptPath) + { + if(!Initialized) + return false; + + if(scriptPath.is_relative()) + { + ssLOG_ERROR("Cannot have build mapping with relative path"); + return false; + } + + ghc::filesystem::path cleanScriptPath = scriptPath.lexically_normal(); + std::string processScriptPathStr = ProcessPath(cleanScriptPath.string()); + if(Mappings.count(processScriptPathStr) == 0) + return true; + + ReverseMappings.erase(Mappings.at(processScriptPathStr)); + Mappings.erase(processScriptPathStr); + assert(ReverseMappings.size() == Mappings.size()); + return true; + } + + bool HasBuildMapping(const ghc::filesystem::path& scriptPath) + { + if(!Initialized) + return false; + + if(scriptPath.is_relative()) + { + ssLOG_ERROR("Cannot have build mapping with relative path"); + return false; + } + + ghc::filesystem::path cleanScriptPath = scriptPath.lexically_normal(); + return Mappings.count(ProcessPath(cleanScriptPath.string())); + } + + inline bool GetBuildMapping(const ghc::filesystem::path& scriptPath, + ghc::filesystem::path& outPath) + { + if(!Initialized) + return false; + + if(scriptPath.is_relative()) + { + ssLOG_ERROR("Cannot have build mapping with relative path"); + return false; + } + + ghc::filesystem::path cleanScriptPath = scriptPath.lexically_normal(); + + //If it doesn't exist, create the mapping + if(!Mappings.count(ProcessPath(cleanScriptPath.string()))) + { + if(!CreateBuildMapping(cleanScriptPath)) + return false; + } + + outPath = BuildDirectory.string() + "/" + Mappings.at(ProcessPath(cleanScriptPath.string())); + return true; + } + + inline bool RemoveAllBuildsMappings() + { + if(!Initialized) + return false; + + Mappings.clear(); + ReverseMappings.clear(); + return true; + } + + inline bool SaveBuildsMappings() + { + if(!Initialized) + return false; + + std::ofstream buildsMappings(MappingsFile); + + if(!buildsMappings.is_open()) + { + ssLOG_ERROR("Failed to open file: " << MappingsFile.string()); + return false; + } + + for(std::unordered_map::iterator it = Mappings.begin(); + it != Mappings.end(); ++it) + { + ssLOG_DEBUG("Writing mapping: " << it->first << "," << it->second); + buildsMappings << it->first << "," << it->second << std::endl; + } + + buildsMappings.close(); + return true; + } + }; +} + +#if defined(INTERNAL_RUNCPP2_UNIT_TESTS) && \ + INTERNAL_RUNCPP2_UNIT_TESTS == INTERNAL_RUNCPP2_UNIT_TESTS_BUILDS_MANAGER + + #include "Tests/BuildsManager/UndefMocks.hpp" +#endif + +#endif diff --git a/Src/runcpp2/CompilingLinking.cpp b/Src/runcpp2/CompilingLinking.hpp similarity index 86% rename from Src/runcpp2/CompilingLinking.cpp rename to Src/runcpp2/CompilingLinking.hpp index 5523083..5049c0e 100644 --- a/Src/runcpp2/CompilingLinking.cpp +++ b/Src/runcpp2/CompilingLinking.hpp @@ -1,17 +1,39 @@ -#include "runcpp2/CompilingLinking.hpp" -#include "runcpp2/DependenciesHelper.hpp" -#include "runcpp2/PlatformUtil.hpp" -#include "runcpp2/StringUtil.hpp" +#ifndef RUNCPP2_COMPILING_LINKING_HPP +#define RUNCPP2_COMPILING_LINKING_HPP + +#include "runcpp2/Data/Profile.hpp" #include "runcpp2/Data/ScriptInfo.hpp" #include "runcpp2/Data/DependencyLibraryType.hpp" #include "runcpp2/Data/BuildTypeHelper.hpp" +#include "runcpp2/Data/BuildType.hpp" +#include "runcpp2/Data/DependencyInfo.hpp" +#include "runcpp2/Data/DependencyLinkProperty.hpp" +#include "runcpp2/Data/FileProperties.hpp" +#include "runcpp2/Data/FilesTypesInfo.hpp" +#include "runcpp2/Data/FlagsOverrideInfo.hpp" +#include "runcpp2/Data/ParseCommon.hpp" +#include "runcpp2/Data/ProfilesDefines.hpp" +#include "runcpp2/Data/ProfilesFlagsOverride.hpp" +#include "runcpp2/Data/StageInfo.hpp" + +#include "runcpp2/PlatformUtil.hpp" +#include "runcpp2/StringUtil.hpp" +#if !defined(NOMINMAX) + #define NOMINMAX 1 +#endif + +#include "DSResult/DSResult.hpp" #include "ssLogger/ssLog.hpp" -#include "System2.h" #include "ghc/filesystem.hpp" #include #include +#include +#include +#include +#include +#include namespace { @@ -115,7 +137,7 @@ namespace outObjectsFilesPaths.clear(); - using OutputTypeInfo = runcpp2::Data::StageInfo::OutputTypeInfo; + using OutputTypeInfo = runcpp2::Data::OutputTypeInfo; OutputTypeInfo* tempOutputInfo = nullptr; static_assert( static_cast(runcpp2::Data::BuildType::COUNT) == 6, "Add new type to be processed"); @@ -499,7 +521,7 @@ namespace const std::vector& objectsFilesPaths) { ssLOG_FUNC_INFO(); - const runcpp2::Data::StageInfo::OutputTypeInfo* currentOutputTypeInfo = nullptr; + const runcpp2::Data::OutputTypeInfo* currentOutputTypeInfo = nullptr; //Only use BuildType for non-executable builds static_assert(static_cast(runcpp2::Data::BuildType::COUNT) == 6, @@ -848,142 +870,147 @@ namespace } } -DS::Result -runcpp2::CompileScriptOnly( const ghc::filesystem::path& buildDir, +namespace runcpp2 +{ + inline DS::Result + CompileScriptOnly( const ghc::filesystem::path& buildDir, + const ghc::filesystem::path& scriptPath, + const std::vector& sourceFiles, + const std::vector& sourceHasCache, + const std::vector& includePaths, + const Data::ScriptInfo& scriptInfo, + const std::vector& availableDependencies, + const Data::Profile& profile, + const int maxThreads) + { + if(!RunGlobalSteps(buildDir, profile.Setup)) + return DS_ERROR_MSG("Failed to run profile global setup steps"); + + std::vector sourceFilesNeededToCompile; + + for(int i = 0; i < sourceFiles.size(); ++i) + { + if(!sourceHasCache.at(i)) + sourceFilesNeededToCompile.push_back(sourceFiles.at(i)); + } + + std::vector objectsFilesPaths; + + if(!CompileScript( buildDir, + scriptPath, + sourceFilesNeededToCompile, + includePaths, + scriptInfo, + availableDependencies, + profile, + objectsFilesPaths, + maxThreads)) + { + if(!RunGlobalSteps(buildDir, profile.Cleanup)) + return DS_ERROR_MSG("CompileScript failed. Failed to run profile global cleanup steps"); + + return DS_ERROR_MSG("CompileScript failed"); + } + + if(!RunGlobalSteps(buildDir, profile.Cleanup)) + return DS_ERROR_MSG("Failed to run profile global cleanup steps"); + + return {}; + } + + //TODO: Convert string paths to filesystem paths + inline DS::Result + CompileAndLinkScript( const ghc::filesystem::path& buildDir, const ghc::filesystem::path& scriptPath, + const std::string& outputName, const std::vector& sourceFiles, const std::vector& sourceHasCache, const std::vector& includePaths, const Data::ScriptInfo& scriptInfo, const std::vector& availableDependencies, const Data::Profile& profile, + const std::vector& compiledObjectsPaths, const int maxThreads) -{ - if(!RunGlobalSteps(buildDir, profile.Setup)) - return DS_ERROR_MSG("Failed to run profile global setup steps"); - - std::vector sourceFilesNeededToCompile; - - for(int i = 0; i < sourceFiles.size(); ++i) { - if(!sourceHasCache.at(i)) - sourceFilesNeededToCompile.push_back(sourceFiles.at(i)); - } - - std::vector objectsFilesPaths; - - if(!CompileScript( buildDir, - scriptPath, - sourceFilesNeededToCompile, - includePaths, - scriptInfo, - availableDependencies, - profile, - objectsFilesPaths, - maxThreads)) - { - if(!RunGlobalSteps(buildDir, profile.Cleanup)) - return DS_ERROR_MSG("CompileScript failed. Failed to run profile global cleanup steps"); + if(!RunGlobalSteps(buildDir, profile.Setup)) + return DS_ERROR_MSG("Failed to run profile global setup steps"); - return DS_ERROR_MSG("CompileScript failed"); - } - - if(!RunGlobalSteps(buildDir, profile.Cleanup)) - return DS_ERROR_MSG("Failed to run profile global cleanup steps"); - - return {}; -} - -DS::Result -runcpp2::CompileAndLinkScript( const ghc::filesystem::path& buildDir, - const ghc::filesystem::path& scriptPath, - const std::string& outputName, - const std::vector& sourceFiles, - const std::vector& sourceHasCache, - const std::vector& includePaths, - const Data::ScriptInfo& scriptInfo, - const std::vector& availableDependencies, - const Data::Profile& profile, - const std::vector& compiledObjectsPaths, - const int maxThreads) -{ - if(!RunGlobalSteps(buildDir, profile.Setup)) - return DS_ERROR_MSG("Failed to run profile global setup steps"); - - std::vector sourceFilesNeededToCompile; - for(int i = 0; i < sourceFiles.size(); ++i) - { - if(!sourceHasCache.at(i)) - sourceFilesNeededToCompile.push_back(sourceFiles.at(i)); - } + std::vector sourceFilesNeededToCompile; + for(int i = 0; i < sourceFiles.size(); ++i) + { + if(!sourceHasCache.at(i)) + sourceFilesNeededToCompile.push_back(sourceFiles.at(i)); + } - std::vector objectsFilesPaths; + std::vector objectsFilesPaths; - //Compile source files that don't have cache - if(!CompileScript( buildDir, - scriptPath, - sourceFilesNeededToCompile, - includePaths, - scriptInfo, - availableDependencies, - profile, - objectsFilesPaths, - maxThreads)) - { - if(!RunGlobalSteps(buildDir, profile.Cleanup)) - return DS_ERROR_MSG("CompileScript failed. Failed to run profile global cleanup steps"); + //Compile source files that don't have cache + if(!CompileScript( buildDir, + scriptPath, + sourceFilesNeededToCompile, + includePaths, + scriptInfo, + availableDependencies, + profile, + objectsFilesPaths, + maxThreads)) + { + if(!RunGlobalSteps(buildDir, profile.Cleanup)) + return DS_ERROR_MSG("CompileScript failed. Failed to run profile global cleanup steps"); + + return DS_ERROR_MSG("CompileScript failed"); + } - return DS_ERROR_MSG("CompileScript failed"); - } - - //Add compiled object files - for(int i = 0; i < compiledObjectsPaths.size(); ++i) - objectsFilesPaths.push_back(compiledObjectsPaths.at(i)); + //Add compiled object files + for(int i = 0; i < compiledObjectsPaths.size(); ++i) + objectsFilesPaths.push_back(compiledObjectsPaths.at(i)); - //Skip linking if build type doesn't need it - if(!Data::BuildTypeHelper::NeedsLinking(scriptInfo.CurrentBuildType)) - { - ssLOG_INFO( "Skipping linking - build type is " << - Data::BuildTypeToString(scriptInfo.CurrentBuildType)); - return {}; - } - - std::string dependenciesLinkFlags; - - //Add link flags for the dependencies - for(int i = 0; i < availableDependencies.size(); ++i) - { - if(!runcpp2::HasValueFromPlatformMap(availableDependencies.at(i)->LinkProperties)) - continue; + //Skip linking if build type doesn't need it + if(!Data::BuildTypeHelper::NeedsLinking(scriptInfo.CurrentBuildType)) + { + ssLOG_INFO( "Skipping linking - build type is " << + Data::BuildTypeToString(scriptInfo.CurrentBuildType)); + return {}; + } - const runcpp2::Data::DependencyLinkProperty& linkProperty = - *runcpp2::GetValueFromPlatformMap(availableDependencies.at(i)->LinkProperties); + std::string dependenciesLinkFlags; - const runcpp2::Data::ProfileLinkProperty* profileLinkProperty = - runcpp2::GetValueFromProfileMap(profile, linkProperty.ProfileProperties); + //Add link flags for the dependencies + for(int i = 0; i < availableDependencies.size(); ++i) + { + if(!runcpp2::HasValueFromPlatformMap(availableDependencies.at(i)->LinkProperties)) + continue; - if(!profileLinkProperty) - continue; + const runcpp2::Data::DependencyLinkProperty& linkProperty = + *runcpp2::GetValueFromPlatformMap(availableDependencies.at(i)->LinkProperties); + + const runcpp2::Data::ProfileLinkProperty* profileLinkProperty = + runcpp2::GetValueFromProfileMap(profile, linkProperty.ProfileProperties); + + if(!profileLinkProperty) + continue; + + for(const std::string& option : profileLinkProperty->AdditionalLinkOptions) + dependenciesLinkFlags += option + " "; + } - for(const std::string& option : profileLinkProperty->AdditionalLinkOptions) - dependenciesLinkFlags += option + " "; - } - - runcpp2::TrimRight(dependenciesLinkFlags); - - if(!LinkScript( buildDir, - outputName, - scriptInfo, - dependenciesLinkFlags, - profile, - objectsFilesPaths)) - { - return DS_ERROR_MSG("LinkScript failed"); + runcpp2::TrimRight(dependenciesLinkFlags); + + if(!LinkScript( buildDir, + outputName, + scriptInfo, + dependenciesLinkFlags, + profile, + objectsFilesPaths)) + { + return DS_ERROR_MSG("LinkScript failed"); + } + + if(!RunGlobalSteps(buildDir, profile.Cleanup)) + return DS_ERROR_MSG("Failed to run profile global cleanup steps"); + + return {}; } - - if(!RunGlobalSteps(buildDir, profile.Cleanup)) - return DS_ERROR_MSG("Failed to run profile global cleanup steps"); - - return {}; } +#endif diff --git a/Src/runcpp2/ConfigParsing.cpp b/Src/runcpp2/ConfigParsing.hpp similarity index 55% rename from Src/runcpp2/ConfigParsing.cpp rename to Src/runcpp2/ConfigParsing.hpp index 939d72d..8cfc73c 100644 --- a/Src/runcpp2/ConfigParsing.cpp +++ b/Src/runcpp2/ConfigParsing.hpp @@ -1,16 +1,37 @@ -#include "runcpp2/ConfigParsing.hpp" -#include "runcpp2/ParseUtil.hpp" +#ifndef RUNCPP2_CONFIG_PARSING_HPP +#define RUNCPP2_CONFIG_PARSING_HPP + +#include "runcpp2/Data/Profile.hpp" +#include "runcpp2/Data/ScriptInfo.hpp" +#include "runcpp2/Data/ParseCommon.hpp" +#include "runcpp2/Data/StageInfo.hpp" #include "runcpp2/StringUtil.hpp" +#include "runcpp2/ParseUtil.hpp" #include "runcpp2/PlatformUtil.hpp" -#include "runcpp2/YamlLib.hpp" - -#include "runcpp2/LibYAML_Wrapper.hpp" #include "runcpp2/DeferUtil.hpp" +#include "runcpp2/LibYAML_Wrapper.hpp" + +#include "DSResult/DSResult.hpp" #include "cfgpath.h" #include "ssLogger/ssLog.hpp" +#include "ghc/filesystem.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -#if INTERNAL_RUNCPP2_UNIT_TESTS +#if defined(INTERNAL_RUNCPP2_UNIT_TESTS) && \ + INTERNAL_RUNCPP2_UNIT_TESTS == INTERNAL_RUNCPP2_UNIT_TESTS_CONFIG_PARSING + #include "Tests/ConfigParsing/MockComponents.hpp" #else #define CO_NO_OVERRIDE 1 @@ -26,7 +47,6 @@ extern "C" const size_t G_PlusPlus_size; extern "C" const uint8_t Vs2022_v17Plus[]; extern "C" const size_t Vs2022_v17Plus_size; - namespace { DS::Result ResovleProfileImport( runcpp2::YAML::NodePtr currentProfileNode, @@ -242,240 +262,250 @@ namespace } } -DS::Result runcpp2::GetConfigFilePath() +namespace runcpp2 { - CO_INSERT_IMPL(OverrideInstance, DS::Result, ()); - - //Check if user config exists - char configDirC_Str[MAX_PATH] = {0}; - - get_user_config_folder(configDirC_Str, MAX_PATH, "runcpp2"); - - DS_ASSERT_GT(strlen(configDirC_Str), 0); - std::string configDir = std::string(configDirC_Str); - - ssLOG_INFO("configDir: " << configDir); - std::string compilerConfigFilePaths[2] = + inline DS::Result GetConfigFilePath() { - configDir + "UserConfig.yaml", - configDir + "UserConfig.yml" - }; - - //config directory is created by get_user_config_folder if it doesn't exist - { - std::error_code ec; - for(int i = 0; i < sizeof(compilerConfigFilePaths) / sizeof(std::string); ++i) + CO_INSERT_IMPL(OverrideInstance, DS::Result, ()); + + //Check if user config exists + char configDirC_Str[MAX_PATH] = {0}; + + get_user_config_folder(configDirC_Str, MAX_PATH, "runcpp2"); + + DS_ASSERT_GT(strlen(configDirC_Str), 0); + std::string configDir = std::string(configDirC_Str); + + ssLOG_INFO("configDir: " << configDir); + std::string compilerConfigFilePaths[2] = { - //Check if the config file exists - if(ghc::filesystem::exists(compilerConfigFilePaths[i], ec)) - return compilerConfigFilePaths[i]; + configDir + "UserConfig.yaml", + configDir + "UserConfig.yml" + }; + + //config directory is created by get_user_config_folder if it doesn't exist + { + std::error_code ec; + for(int i = 0; i < sizeof(compilerConfigFilePaths) / sizeof(std::string); ++i) + { + //Check if the config file exists + if(ghc::filesystem::exists(compilerConfigFilePaths[i], ec)) + return compilerConfigFilePaths[i]; + } } + + return compilerConfigFilePaths[0]; } - return compilerConfigFilePaths[0]; -} - -DS::Result runcpp2::WriteDefaultConfigs( const ghc::filesystem::path& userConfigPath, + inline DS::Result WriteDefaultConfigs(const ghc::filesystem::path& userConfigPath, const bool writeUserConfig, const bool writeDefaultConfigs) -{ - CO_INSERT_IMPL( OverrideInstance, - DS::Result, - (userConfigPath, writeUserConfig, writeDefaultConfigs)); - - //Backup existing user config - std::error_code _; - if(writeUserConfig && ghc::filesystem::exists(userConfigPath, _)) { - int backupCount = 0; - do + CO_INSERT_IMPL( OverrideInstance, + DS::Result, + (userConfigPath, writeUserConfig, writeDefaultConfigs)); + + //Backup existing user config + std::error_code _; + if(writeUserConfig && ghc::filesystem::exists(userConfigPath, _)) { - if(backupCount > 10) + int backupCount = 0; + do { - return DS_ERROR_MSG("Failed to backup existing user config: " + - userConfigPath.string()); - } - - std::string backupPath = userConfigPath.string(); - - if(backupCount > 0) - backupPath += "." + std::to_string(backupCount); - - backupPath += ".bak"; - - if(ghc::filesystem::exists(backupPath, _)) - { - ssLOG_WARNING("Backup path exists: " << backupPath); - ++backupCount; - continue; - } - - std::error_code copyErrorCode; - ghc::filesystem::copy(userConfigPath, backupPath, copyErrorCode); - if(copyErrorCode) - { - return DS_ERROR_MSG("Failed to backup existing user config: " + - userConfigPath.string() + " with error: " + _.message()); + if(backupCount > 10) + { + return DS_ERROR_MSG("Failed to backup existing user config: " + + userConfigPath.string()); + } + + std::string backupPath = userConfigPath.string(); + + if(backupCount > 0) + backupPath += "." + std::to_string(backupCount); + + backupPath += ".bak"; + + if(ghc::filesystem::exists(backupPath, _)) + { + ssLOG_WARNING("Backup path exists: " << backupPath); + ++backupCount; + continue; + } + + std::error_code copyErrorCode; + ghc::filesystem::copy(userConfigPath, backupPath, copyErrorCode); + if(copyErrorCode) + { + return DS_ERROR_MSG("Failed to backup existing user config: " + + userConfigPath.string() + " with error: " + _.message()); + } + + ssLOG_INFO("Backed up existing user config: " << backupPath); + if(!ghc::filesystem::remove(userConfigPath, _)) + { + return DS_ERROR_MSG("Failed to delete existing user config: " + + userConfigPath.string()); + } + + break; } + while(true); + } + + //Create user config + if(writeUserConfig) + { + std::ofstream configFile(userConfigPath, std::ios::binary); + if(!configFile) + return DS_ERROR_MSG("Failed to create default config file: " + userConfigPath.string()); - ssLOG_INFO("Backed up existing user config: " << backupPath); - if(!ghc::filesystem::remove(userConfigPath, _)) + configFile.write((const char*)DefaultUserConfig, DefaultUserConfig_size); + } + + if(!writeDefaultConfigs) + return {}; + + ghc::filesystem::path userConfigDirectory = userConfigPath; + userConfigDirectory = userConfigDirectory.parent_path(); + ghc::filesystem::path defaultYamlDirectory = userConfigDirectory / "Default"; + + //Default configs + if(!ghc::filesystem::exists(defaultYamlDirectory , _)) + DS_ASSERT_TRUE(ghc::filesystem::create_directories(defaultYamlDirectory, _)); + + //Writing default profiles + auto writeDefaultConfig = + [&defaultYamlDirectory](ghc::filesystem::path outputPath, + const uint8_t* outputContent, + size_t outputSize) -> DS::Result { - return DS_ERROR_MSG("Failed to delete existing user config: " + - userConfigPath.string()); - } - - break; + const ghc::filesystem::path currentOutputPath = defaultYamlDirectory / outputPath; + std::ofstream defaultFile( currentOutputPath.string(), + std::ios::binary | std::ios_base::trunc); + if(!defaultFile) + { + return DS_ERROR_MSG("Failed to create default config file: " + + currentOutputPath.string()); + } + defaultFile.write((const char*)outputContent, outputSize); + return {}; + }; + + writeDefaultConfig("CommonFileTypes.yaml", CommonFileTypes, CommonFileTypes_size).DS_TRY(); + writeDefaultConfig("g++.yaml", G_PlusPlus, G_PlusPlus_size).DS_TRY(); + writeDefaultConfig("vs2022_v17+.yaml", Vs2022_v17Plus, Vs2022_v17Plus_size).DS_TRY(); + + //Writing .version to indicate everything is up-to-date + std::ofstream configVersionFile(userConfigDirectory / ".version", + std::ios::binary | std::ios_base::trunc); + if(!configVersionFile) + { + return DS_ERROR_MSG("Failed to open version file: " + + ghc::filesystem::path(userConfigDirectory / ".version").string()); } - while(true); - } - - //Create user config - if(writeUserConfig) - { - std::ofstream configFile(userConfigPath, std::ios::binary); - if(!configFile) - return DS_ERROR_MSG("Failed to create default config file: " + userConfigPath.string()); - configFile.write((const char*)DefaultUserConfig, DefaultUserConfig_size); - } - - if(!writeDefaultConfigs) + configVersionFile << std::to_string(RUNCPP2_CONFIG_VERSION); + return {}; - - ghc::filesystem::path userConfigDirectory = userConfigPath; - userConfigDirectory = userConfigDirectory.parent_path(); - ghc::filesystem::path defaultYamlDirectory = userConfigDirectory / "Default"; - - //Default configs - if(!ghc::filesystem::exists(defaultYamlDirectory , _)) - DS_ASSERT_TRUE(ghc::filesystem::create_directories(defaultYamlDirectory, _)); - - //Writing default profiles - auto writeDefaultConfig = - [&defaultYamlDirectory](ghc::filesystem::path outputPath, - const uint8_t* outputContent, - size_t outputSize) -> DS::Result - { - const ghc::filesystem::path currentOutputPath = defaultYamlDirectory / outputPath; - std::ofstream defaultFile( currentOutputPath.string(), - std::ios::binary | std::ios_base::trunc); - if(!defaultFile) - { - return DS_ERROR_MSG("Failed to create default config file: " + - currentOutputPath.string()); - } - defaultFile.write((const char*)outputContent, outputSize); - return {}; - }; - - writeDefaultConfig("CommonFileTypes.yaml", CommonFileTypes, CommonFileTypes_size).DS_TRY(); - writeDefaultConfig("g++.yaml", G_PlusPlus, G_PlusPlus_size).DS_TRY(); - writeDefaultConfig("vs2022_v17+.yaml", Vs2022_v17Plus, Vs2022_v17Plus_size).DS_TRY(); - - //Writing .version to indicate everything is up-to-date - std::ofstream configVersionFile(userConfigDirectory / ".version", - std::ios::binary | std::ios_base::trunc); - if(!configVersionFile) - { - return DS_ERROR_MSG("Failed to open version file: " + - ghc::filesystem::path(userConfigDirectory / ".version").string()); } - configVersionFile << std::to_string(RUNCPP2_CONFIG_VERSION); - - return {}; -} - -DS::Result runcpp2::ReadUserConfig( std::vector& outProfiles, + inline DS::Result ReadUserConfig( std::vector& outProfiles, std::string& outPreferredProfile, - const std::string& customConfigPath) -{ - ssLOG_FUNC_INFO(); - - ghc::filesystem::path configPath = !customConfigPath.empty() ? - customConfigPath : - GetConfigFilePath().DS_VALUE_OR(); - DS_CHECK_PREV(); - ghc::filesystem::path configVersionPath = configPath.parent_path() / ".version"; - - DS_ASSERT_FALSE(configPath.empty()); - - std::error_code e; - - bool writeUserConfig = false; - bool writeDefaultConfigs = false; - - //Create default config files if it doesn't exist - if(!ghc::filesystem::exists(configPath, e)) + const std::string& customConfigPath = "") { - if(!customConfigPath.empty()) - return DS_ERROR_MSG("Config file doesn't exist: " + configPath.string()); + ssLOG_FUNC_INFO(); - ssLOG_INFO("Config file doesn't exist. Creating one at: " << configPath.string()); - writeUserConfig = true; - writeDefaultConfigs = true; - } - //Overwrite default config files if it is using old version - else if(ghc::filesystem::exists(configVersionPath, e)) - { - std::ifstream configVersionFile(configVersionPath); - if(!configVersionFile) - return DS_ERROR_MSG("Failed to open version file: " + configVersionPath.string()); + ghc::filesystem::path configPath = !customConfigPath.empty() ? + customConfigPath : + GetConfigFilePath().DS_VALUE_OR(); + DS_CHECK_PREV(); + ghc::filesystem::path configVersionPath = configPath.parent_path() / ".version"; + + DS_ASSERT_FALSE(configPath.empty()); - std::string configVersionStr; - std::stringstream buffer; - buffer << configVersionFile.rdbuf(); - configVersionStr = buffer.str(); + std::error_code e; - Trim(configVersionStr); - int configVersion = std::stoi(configVersionStr); - if(configVersion < RUNCPP2_CONFIG_VERSION) + bool writeUserConfig = false; + bool writeDefaultConfigs = false; + + //Create default config files if it doesn't exist + if(!ghc::filesystem::exists(configPath, e)) + { + if(!customConfigPath.empty()) + return DS_ERROR_MSG("Config file doesn't exist: " + configPath.string()); + + ssLOG_INFO("Config file doesn't exist. Creating one at: " << configPath.string()); + writeUserConfig = true; writeDefaultConfigs = true; + } + //Overwrite default config files if it is using old version + else if(ghc::filesystem::exists(configVersionPath, e)) + { + std::ifstream configVersionFile(configVersionPath); + if(!configVersionFile) + return DS_ERROR_MSG("Failed to open version file: " + configVersionPath.string()); + + std::string configVersionStr; + std::stringstream buffer; + buffer << configVersionFile.rdbuf(); + configVersionStr = buffer.str(); + + Trim(configVersionStr); + int configVersion = std::stoi(configVersionStr); + if(configVersion < RUNCPP2_CONFIG_VERSION) + writeDefaultConfigs = true; + } + //Overwrite default config files if missing version file + else + writeDefaultConfigs = true; + + if(writeUserConfig || writeDefaultConfigs) + { + WriteDefaultConfigs(configPath, writeUserConfig, writeDefaultConfigs).DS_TRY(); + } + + if(ghc::filesystem::is_directory(configPath, e)) + return DS_ERROR_MSG("Config file path is a directory: " + configPath.string()); + + //Read compiler profiles + std::string userConfigContent; + { + std::ifstream userConfigFile(configPath); + if(!userConfigFile) + return DS_ERROR_MSG("Failed to open config file: " + configPath.string()); + std::stringstream buffer; + buffer << userConfigFile.rdbuf(); + userConfigContent = buffer.str(); + } + + ParseUserConfig(userConfigContent, configPath, outProfiles, outPreferredProfile).DS_TRY(); + return {}; } - //Overwrite default config files if missing version file - else - writeDefaultConfigs = true; - - if(writeUserConfig || writeDefaultConfigs) - { - WriteDefaultConfigs(configPath, writeUserConfig, writeDefaultConfigs).DS_TRY(); - } - - if(ghc::filesystem::is_directory(configPath, e)) - return DS_ERROR_MSG("Config file path is a directory: " + configPath.string()); - //Read compiler profiles - std::string userConfigContent; + inline DS::Result ParseScriptInfo(const std::string& scriptInfo, + Data::ScriptInfo& outScriptInfo) { - std::ifstream userConfigFile(configPath); - if(!userConfigFile) - return DS_ERROR_MSG("Failed to open config file: " + configPath.string()); - std::stringstream buffer; - buffer << userConfigFile.rdbuf(); - userConfigContent = buffer.str(); - } - - ParseUserConfig(userConfigContent, configPath, outProfiles, outPreferredProfile).DS_TRY(); - return {}; -} + if(scriptInfo.empty()) + return {}; -DS::Result runcpp2::ParseScriptInfo( const std::string& scriptInfo, - Data::ScriptInfo& outScriptInfo) -{ - if(scriptInfo.empty()) + YAML::ResourceHandle resourceHandle; + std::vector scriptNodes = YAML::ParseYAML(scriptInfo, resourceHandle).DS_TRY(); + DEFER { YAML::FreeYAMLResource(resourceHandle); }; + DS_ASSERT_FALSE(scriptNodes.empty()); + + //NOTE: Use the first one + YAML::ResolveAnchors(scriptNodes.front()).DS_TRY(); + YAML::NodePtr rootScriptNode = scriptNodes.front(); + DS_ASSERT_TRUE(outScriptInfo.ParseYAML_Node(rootScriptNode)); + outScriptInfo.Populated = true; return {}; + } +} - YAML::ResourceHandle resourceHandle; - std::vector scriptNodes = YAML::ParseYAML(scriptInfo, resourceHandle).DS_TRY(); - DEFER { YAML::FreeYAMLResource(resourceHandle); }; - DS_ASSERT_FALSE(scriptNodes.empty()); +#if defined(INTERNAL_RUNCPP2_UNIT_TESTS) && \ + INTERNAL_RUNCPP2_UNIT_TESTS == INTERNAL_RUNCPP2_UNIT_TESTS_CONFIG_PARSING - //NOTE: Use the first one - YAML::ResolveAnchors(scriptNodes.front()).DS_TRY(); - YAML::NodePtr rootScriptNode = scriptNodes.front(); - DS_ASSERT_TRUE(outScriptInfo.ParseYAML_Node(rootScriptNode)); - outScriptInfo.Populated = true; - return {}; -} + #include "Tests/ConfigParsing/UndefMocks.hpp" +#endif +#endif diff --git a/Include/runcpp2/Data/BuildType.hpp b/Src/runcpp2/Data/BuildType.hpp similarity index 100% rename from Include/runcpp2/Data/BuildType.hpp rename to Src/runcpp2/Data/BuildType.hpp diff --git a/Src/runcpp2/Data/BuildTypeHelper.cpp b/Src/runcpp2/Data/BuildTypeHelper.hpp similarity index 77% rename from Src/runcpp2/Data/BuildTypeHelper.cpp rename to Src/runcpp2/Data/BuildTypeHelper.hpp index 3c17cd8..73f4adb 100644 --- a/Src/runcpp2/Data/BuildTypeHelper.cpp +++ b/Src/runcpp2/Data/BuildTypeHelper.hpp @@ -1,8 +1,18 @@ -#include "runcpp2/Data/BuildTypeHelper.hpp" +#ifndef RUNCPP2_DATA_BUILD_TYPE_HELPER_HPP +#define RUNCPP2_DATA_BUILD_TYPE_HELPER_HPP + +#include "runcpp2/Data/BuildType.hpp" +#include "runcpp2/Data/FilesTypesInfo.hpp" +#include "runcpp2/Data/Profile.hpp" +#include "runcpp2/Data/FileProperties.hpp" #include "runcpp2/PlatformUtil.hpp" +#include "ghc/filesystem.hpp" #include "ssLogger/ssLog.hpp" +#include +#include + namespace { using namespace runcpp2::Data; @@ -39,17 +49,21 @@ namespace namespace runcpp2 { - bool Data::BuildTypeHelper::NeedsLinking(BuildType buildType) +namespace Data +{ +namespace BuildTypeHelper +{ + inline bool NeedsLinking(BuildType buildType) { return buildType != BuildType::OBJECTS; } - bool Data::BuildTypeHelper::GetPossibleOutputPaths( const ghc::filesystem::path& buildDir, - const std::string& scriptName, - const Profile& profile, - const BuildType buildType, - std::vector& outPaths, - std::vector& outIsRunnable) + inline bool GetPossibleOutputPaths( const ghc::filesystem::path& buildDir, + const std::string& scriptName, + const Profile& profile, + const BuildType buildType, + std::vector& outPaths, + std::vector& outIsRunnable) { outPaths.clear(); outIsRunnable.clear(); @@ -95,4 +109,8 @@ namespace runcpp2 return !outPaths.empty(); } -} +} +} +} + +#endif diff --git a/Include/runcpp2/Data/CmdOptions.hpp b/Src/runcpp2/Data/CmdOptions.hpp similarity index 100% rename from Include/runcpp2/Data/CmdOptions.hpp rename to Src/runcpp2/Data/CmdOptions.hpp diff --git a/Src/runcpp2/Data/DependencyInfo.cpp b/Src/runcpp2/Data/DependencyInfo.cpp deleted file mode 100644 index 031208e..0000000 --- a/Src/runcpp2/Data/DependencyInfo.cpp +++ /dev/null @@ -1,277 +0,0 @@ -#include "runcpp2/Data/DependencyInfo.hpp" -#include "runcpp2/ParseUtil.hpp" -#include "runcpp2/Data/ParseCommon.hpp" -#include "ssLogger/ssLog.hpp" - -bool runcpp2::Data::DependencyInfo::ParseYAML_Node(YAML::ConstNodePtr node) -{ - //If import is needed, we only need to parse the Source section - do - { - if(!ExistAndHasChild(node, "Source")) - { - ssLOG_ERROR("DependencyInfo: Missing Source"); - return false; - } - - YAML::ConstNodePtr sourceNode = node->GetMapValueNode("Source"); - if(!ExistAndHasChild(sourceNode, "ImportPath")) - break; - - if(!Source.ParseYAML_Node(sourceNode)) - { - ssLOG_ERROR("DependencyInfo: Failed to parse Source"); - return false; - } - - ssLOG_DEBUG("DependencyInfo: Importing from " << Source.ImportPath.string()); - ssLOG_DEBUG("Skipping the rest of the DependencyInfo"); - return true; - } - while(false); - - std::vector requirements = - { - NodeRequirement("Name", YAML::NodeType::Scalar, true, false), - NodeRequirement("Platforms", YAML::NodeType::Sequence, true, false), - NodeRequirement("Source", YAML::NodeType::Map, true, false), - NodeRequirement("LibraryType", YAML::NodeType::Scalar, true, false), - NodeRequirement("IncludePaths", YAML::NodeType::Sequence, false, true), - - //Expecting either platform profile map or ProfileLinkProperty map - NodeRequirement("LinkProperties", YAML::NodeType::Map, false, false), - - //Setup can be platform profile map or sequence of commands, handle later - //Cleanup can be platform profile map or sequence of commands, handle later - //Build can be platform profile map or sequence of commands, handle later - //FilesToCopy can be platform profile map or sequence of paths, handle later - }; - - if(!CheckNodeRequirements(node, requirements)) - { - ssLOG_ERROR("DependencyInfo: Failed to meet requirements"); - return false; - } - - Name = node->GetMapValueScalar("Name").DS_TRY_ACT(return false); - - YAML::ConstNodePtr platformsNode = node->GetMapValueNode("Platforms"); - for(int i = 0; i < platformsNode->GetChildrenCount(); ++i) - { - std::string platform = platformsNode->GetSequenceChildScalar(i) - .DS_TRY_ACT(return false); - Platforms.insert(platform); - } - - YAML::ConstNodePtr sourceNode = node->GetMapValueNode("Source"); - if(!Source.ParseYAML_Node(sourceNode)) - { - ssLOG_ERROR("DependencyInfo: Failed to parse Source"); - return false; - } - - static_assert((int)DependencyLibraryType::COUNT == 4, ""); - - std::string libType = node->GetMapValueScalar("LibraryType").DS_TRY_ACT(return false); - if(libType == "Static") - LibraryType = DependencyLibraryType::STATIC; - else if(libType == "Shared") - LibraryType = DependencyLibraryType::SHARED; - else if(libType == "Object") - LibraryType = DependencyLibraryType::OBJECT; - else if(libType == "Header") - LibraryType = DependencyLibraryType::HEADER; - else - { - ssLOG_ERROR("DependencyInfo: LibraryType is invalid"); - return false; - } - - if(ExistAndHasChild(node, "IncludePaths")) - { - YAML::ConstNodePtr includePathsNode = node->GetMapValueNode("IncludePaths"); - - for(int i = 0; i < includePathsNode->GetChildrenCount(); ++i) - { - std::string includePath = includePathsNode ->GetSequenceChildScalar(i) - .DS_TRY_ACT(return false); - IncludePaths.push_back(includePath); - } - } - - if(ExistAndHasChild(node, "LinkProperties")) - { - if(!ParsePlatformProfileMap(node, - "LinkProperties", - LinkProperties, - "LinkProperties")) - { - return false; - } - } - else if(LibraryType != DependencyLibraryType::HEADER) - { - ssLOG_ERROR("DependencyInfo: Missing LinkProperties with library type " << - Data::DependencyLibraryTypeToString(LibraryType)); - return false; - } - - if(!ParsePlatformProfileMap(node, "Setup", Setup, "Setup")) - return false; - - if(!ParsePlatformProfileMap(node, "Cleanup", Cleanup, "Cleanup")) - return false; - - if(!ParsePlatformProfileMap(node, "Build", Build, "Build")) - return false; - - if(!ParsePlatformProfileMap(node, "FilesToCopy", FilesToCopy, "FilesToCopy")) - return false; - - return true; -} - -std::string runcpp2::Data::DependencyInfo::ToString(std::string indentation) const -{ - std::string out; - out += indentation + "Name: " + GetEscapedYAMLString(Name) + "\n"; - - out += indentation + "Platforms:\n"; - for(auto it = Platforms.begin(); it != Platforms.end(); ++it) - out += indentation + "- " + GetEscapedYAMLString(*it) + "\n"; - - out += indentation + "Source:\n"; - out += Source.ToString(indentation + " "); - - static_assert((int)DependencyLibraryType::COUNT == 4, ""); - - if(LibraryType == DependencyLibraryType::STATIC) - out += indentation + "LibraryType: Static\n"; - else if(LibraryType == DependencyLibraryType::SHARED) - out += indentation + "LibraryType: Shared\n"; - else if(LibraryType == DependencyLibraryType::OBJECT) - out += indentation + "LibraryType: Object\n"; - else if(LibraryType == DependencyLibraryType::HEADER) - out += indentation + "LibraryType: Header\n"; - - if(!IncludePaths.empty()) - { - out += indentation + "IncludePaths:\n"; - for(auto it = IncludePaths.begin(); it != IncludePaths.end(); ++it) - out += indentation + "- " + GetEscapedYAMLString(*it) + "\n"; - } - - if(!LinkProperties.empty()) - { - out += indentation + "LinkProperties:\n"; - for(auto it = LinkProperties.begin(); it != LinkProperties.end(); ++it) - { - out += indentation + " " + it->first + ":\n"; - out += it->second.ToString(indentation + " "); - } - } - - if(!Setup.empty()) - { - out += indentation + "Setup:\n"; - for(auto it = Setup.begin(); it != Setup.end(); ++it) - { - out += indentation + " " + it->first + ":\n"; - out += it->second.ToString(indentation + " "); - } - } - - if(!Cleanup.empty()) - { - out += indentation + "Cleanup:\n"; - for(auto it = Cleanup.begin(); it != Cleanup.end(); ++it) - { - out += indentation + " " + it->first + ":\n"; - out += it->second.ToString(indentation + " "); - } - } - - if(!Build.empty()) - { - out += indentation + "Build:\n"; - for(auto it = Build.begin(); it != Build.end(); ++it) - { - out += indentation + " " + it->first + ":\n"; - out += it->second.ToString(indentation + " "); - } - } - - if(!FilesToCopy.empty()) - { - out += indentation + "FilesToCopy:\n"; - for(auto it = FilesToCopy.begin(); it != FilesToCopy.end(); ++it) - { - out += indentation + " " + it->first + ":\n"; - out += it->second.ToString(indentation + " "); - } - } - - return out; -} - -bool runcpp2::Data::DependencyInfo::Equals(const DependencyInfo& other) const -{ - if( Name != other.Name || - Platforms.size() != other.Platforms.size() || - !Source.Equals(other.Source) || - LibraryType != other.LibraryType || - IncludePaths != other.IncludePaths || - AbsoluteIncludePaths != other.AbsoluteIncludePaths || - LinkProperties.size() != other.LinkProperties.size() || - Setup.size() != other.Setup.size() || - Cleanup.size() != other.Cleanup.size() || - Build.size() != other.Build.size() || - FilesToCopy.size() != other.FilesToCopy.size()) - { - return false; - } - - for(const std::string& it : Platforms) - { - if(other.Platforms.count(it) == 0) - return false; - } - - for(const auto& it : LinkProperties) - { - if( other.LinkProperties.count(it.first) == 0 || - !other.LinkProperties.at(it.first).Equals(it.second)) - { - return false; - } - } - - for(const auto& it : Setup) - { - if(other.Setup.count(it.first) == 0 || !other.Setup.at(it.first).Equals(it.second)) - return false; - } - - for(const auto& it : Cleanup) - { - if(other.Cleanup.count(it.first) == 0 || !other.Cleanup.at(it.first).Equals(it.second)) - return false; - } - - for(const auto& it : Build) - { - if(other.Build.count(it.first) == 0 || !other.Build.at(it.first).Equals(it.second)) - return false; - } - - for(const auto& it : FilesToCopy) - { - if( other.FilesToCopy.count(it.first) == 0 || - !other.FilesToCopy.at(it.first).Equals(it.second)) - { - return false; - } - } - - return true; -} diff --git a/Src/runcpp2/Data/DependencyInfo.hpp b/Src/runcpp2/Data/DependencyInfo.hpp new file mode 100644 index 0000000..d7f6008 --- /dev/null +++ b/Src/runcpp2/Data/DependencyInfo.hpp @@ -0,0 +1,323 @@ +#ifndef RUNCPP2_DATA_DEPENDENCY_INFO_HPP +#define RUNCPP2_DATA_DEPENDENCY_INFO_HPP + +#include "runcpp2/Data/DependencyLibraryType.hpp" +#include "runcpp2/Data/DependencySource.hpp" +#include "runcpp2/Data/DependencyLinkProperty.hpp" +#include "runcpp2/Data/ProfilesCommands.hpp" +#include "runcpp2/Data/ParseCommon.hpp" +#include "runcpp2/Data/FilesToCopyInfo.hpp" +#include "runcpp2/LibYAML_Wrapper.hpp" +#include "runcpp2/ParseUtil.hpp" + +#include "ssLogger/ssLog.hpp" +#include "DSResult/DSResult.hpp" + +#include +#include +#include +#include +#include + +namespace runcpp2 +{ +namespace Data +{ + struct DependencyInfo + { + std::string Name; + std::unordered_set Platforms; + DependencySource Source; + DependencyLibraryType LibraryType; + std::vector IncludePaths; + std::vector AbsoluteIncludePaths; + std::unordered_map LinkProperties; + std::unordered_map Setup; + std::unordered_map Cleanup; + std::unordered_map Build; + std::unordered_map FilesToCopy; + + inline bool ParseYAML_Node(YAML::ConstNodePtr node) + { + //If import is needed, we only need to parse the Source section + do + { + if(!ExistAndHasChild(node, "Source")) + { + ssLOG_ERROR("DependencyInfo: Missing Source"); + return false; + } + + YAML::ConstNodePtr sourceNode = node->GetMapValueNode("Source"); + if(!ExistAndHasChild(sourceNode, "ImportPath")) + break; + + if(!Source.ParseYAML_Node(sourceNode)) + { + ssLOG_ERROR("DependencyInfo: Failed to parse Source"); + return false; + } + + ssLOG_DEBUG("DependencyInfo: Importing from " << Source.ImportPath.string()); + ssLOG_DEBUG("Skipping the rest of the DependencyInfo"); + return true; + } + while(false); + + std::vector requirements = + { + NodeRequirement("Name", YAML::NodeType::Scalar, true, false), + NodeRequirement("Platforms", YAML::NodeType::Sequence, true, false), + NodeRequirement("Source", YAML::NodeType::Map, true, false), + NodeRequirement("LibraryType", YAML::NodeType::Scalar, true, false), + NodeRequirement("IncludePaths", YAML::NodeType::Sequence, false, true), + + //Expecting either platform profile map or ProfileLinkProperty map + NodeRequirement("LinkProperties", YAML::NodeType::Map, false, false), + + //Setup can be platform profile map or sequence of commands, handle later + //Cleanup can be platform profile map or sequence of commands, handle later + //Build can be platform profile map or sequence of commands, handle later + //FilesToCopy can be platform profile map or sequence of paths, handle later + }; + + if(!CheckNodeRequirements(node, requirements)) + { + ssLOG_ERROR("DependencyInfo: Failed to meet requirements"); + return false; + } + + Name = node->GetMapValueScalar("Name").DS_TRY_ACT(return false); + + YAML::ConstNodePtr platformsNode = node->GetMapValueNode("Platforms"); + for(int i = 0; i < platformsNode->GetChildrenCount(); ++i) + { + std::string platform = platformsNode->GetSequenceChildScalar(i) + .DS_TRY_ACT(return false); + Platforms.insert(platform); + } + + YAML::ConstNodePtr sourceNode = node->GetMapValueNode("Source"); + if(!Source.ParseYAML_Node(sourceNode)) + { + ssLOG_ERROR("DependencyInfo: Failed to parse Source"); + return false; + } + + static_assert((int)DependencyLibraryType::COUNT == 4, ""); + + std::string libType = node ->GetMapValueScalar("LibraryType") + .DS_TRY_ACT(return false); + if(libType == "Static") + LibraryType = DependencyLibraryType::STATIC; + else if(libType == "Shared") + LibraryType = DependencyLibraryType::SHARED; + else if(libType == "Object") + LibraryType = DependencyLibraryType::OBJECT; + else if(libType == "Header") + LibraryType = DependencyLibraryType::HEADER; + else + { + ssLOG_ERROR("DependencyInfo: LibraryType is invalid"); + return false; + } + + if(ExistAndHasChild(node, "IncludePaths")) + { + YAML::ConstNodePtr includePathsNode = node->GetMapValueNode("IncludePaths"); + + for(int i = 0; i < includePathsNode->GetChildrenCount(); ++i) + { + std::string includePath = + includePathsNode->GetSequenceChildScalar(i) + .DS_TRY_ACT(return false); + IncludePaths.push_back(includePath); + } + } + + if(ExistAndHasChild(node, "LinkProperties")) + { + if(!ParsePlatformProfileMap(node, + "LinkProperties", + LinkProperties, + "LinkProperties")) + { + return false; + } + } + else if(LibraryType != DependencyLibraryType::HEADER) + { + ssLOG_ERROR("DependencyInfo: Missing LinkProperties with library type " << + Data::DependencyLibraryTypeToString(LibraryType)); + return false; + } + + if(!ParsePlatformProfileMap(node, "Setup", Setup, "Setup")) + return false; + + if(!ParsePlatformProfileMap(node, "Cleanup", Cleanup, "Cleanup")) + return false; + + if(!ParsePlatformProfileMap(node, "Build", Build, "Build")) + return false; + + if(!ParsePlatformProfileMap( node, + "FilesToCopy", + FilesToCopy, + "FilesToCopy")) + { + return false; + } + + return true; + } + + inline std::string ToString(std::string indentation) const + { + std::string out; + out += indentation + "Name: " + GetEscapedYAMLString(Name) + "\n"; + + out += indentation + "Platforms:\n"; + for(auto it = Platforms.begin(); it != Platforms.end(); ++it) + out += indentation + "- " + GetEscapedYAMLString(*it) + "\n"; + + out += indentation + "Source:\n"; + out += Source.ToString(indentation + " "); + + static_assert((int)DependencyLibraryType::COUNT == 4, ""); + + if(LibraryType == DependencyLibraryType::STATIC) + out += indentation + "LibraryType: Static\n"; + else if(LibraryType == DependencyLibraryType::SHARED) + out += indentation + "LibraryType: Shared\n"; + else if(LibraryType == DependencyLibraryType::OBJECT) + out += indentation + "LibraryType: Object\n"; + else if(LibraryType == DependencyLibraryType::HEADER) + out += indentation + "LibraryType: Header\n"; + + if(!IncludePaths.empty()) + { + out += indentation + "IncludePaths:\n"; + for(auto it = IncludePaths.begin(); it != IncludePaths.end(); ++it) + out += indentation + "- " + GetEscapedYAMLString(*it) + "\n"; + } + + if(!LinkProperties.empty()) + { + out += indentation + "LinkProperties:\n"; + for(auto it = LinkProperties.begin(); it != LinkProperties.end(); ++it) + { + out += indentation + " " + it->first + ":\n"; + out += it->second.ToString(indentation + " "); + } + } + + if(!Setup.empty()) + { + out += indentation + "Setup:\n"; + for(auto it = Setup.begin(); it != Setup.end(); ++it) + { + out += indentation + " " + it->first + ":\n"; + out += it->second.ToString(indentation + " "); + } + } + + if(!Cleanup.empty()) + { + out += indentation + "Cleanup:\n"; + for(auto it = Cleanup.begin(); it != Cleanup.end(); ++it) + { + out += indentation + " " + it->first + ":\n"; + out += it->second.ToString(indentation + " "); + } + } + + if(!Build.empty()) + { + out += indentation + "Build:\n"; + for(auto it = Build.begin(); it != Build.end(); ++it) + { + out += indentation + " " + it->first + ":\n"; + out += it->second.ToString(indentation + " "); + } + } + + if(!FilesToCopy.empty()) + { + out += indentation + "FilesToCopy:\n"; + for(auto it = FilesToCopy.begin(); it != FilesToCopy.end(); ++it) + { + out += indentation + " " + it->first + ":\n"; + out += it->second.ToString(indentation + " "); + } + } + + return out; + } + + inline bool Equals(const DependencyInfo& other) const + { + if( Name != other.Name || + Platforms.size() != other.Platforms.size() || + !Source.Equals(other.Source) || + LibraryType != other.LibraryType || + IncludePaths != other.IncludePaths || + AbsoluteIncludePaths != other.AbsoluteIncludePaths || + LinkProperties.size() != other.LinkProperties.size() || + Setup.size() != other.Setup.size() || + Cleanup.size() != other.Cleanup.size() || + Build.size() != other.Build.size() || + FilesToCopy.size() != other.FilesToCopy.size()) + { + return false; + } + + for(const std::string& it : Platforms) + { + if(other.Platforms.count(it) == 0) + return false; + } + + for(const auto& it : LinkProperties) + { + if( other.LinkProperties.count(it.first) == 0 || + !other.LinkProperties.at(it.first).Equals(it.second)) + { + return false; + } + } + + for(const auto& it : Setup) + { + if(other.Setup.count(it.first) == 0 || !other.Setup.at(it.first).Equals(it.second)) + return false; + } + + for(const auto& it : Cleanup) + { + if(other.Cleanup.count(it.first) == 0 || !other.Cleanup.at(it.first).Equals(it.second)) + return false; + } + + for(const auto& it : Build) + { + if(other.Build.count(it.first) == 0 || !other.Build.at(it.first).Equals(it.second)) + return false; + } + + for(const auto& it : FilesToCopy) + { + if( other.FilesToCopy.count(it.first) == 0 || + !other.FilesToCopy.at(it.first).Equals(it.second)) + { + return false; + } + } + + return true; + } + }; +} +} + +#endif diff --git a/Include/runcpp2/Data/DependencyLibraryType.hpp b/Src/runcpp2/Data/DependencyLibraryType.hpp similarity index 100% rename from Include/runcpp2/Data/DependencyLibraryType.hpp rename to Src/runcpp2/Data/DependencyLibraryType.hpp diff --git a/Src/runcpp2/Data/DependencyLinkProperty.cpp b/Src/runcpp2/Data/DependencyLinkProperty.cpp deleted file mode 100644 index 0b7c202..0000000 --- a/Src/runcpp2/Data/DependencyLinkProperty.cpp +++ /dev/null @@ -1,257 +0,0 @@ -#include "runcpp2/Data/DependencyLinkProperty.hpp" -#include "runcpp2/Data/ParseCommon.hpp" -#include "runcpp2/ParseUtil.hpp" -#include "ssLogger/ssLog.hpp" - -bool runcpp2::Data::DependencyLinkProperty::ParseYAML_Node(YAML::ConstNodePtr node) -{ - ssLOG_FUNC_DEBUG(); - - if(!node->IsMap()) - { - ssLOG_ERROR("DependencyLinkProperty: Node is not a Map"); - return false; - } - - for(int i = 0; i < node->GetChildrenCount(); ++i) - { - ProfileName profile = node->GetMapKeyScalarAt(i).DS_TRY_ACT(return false); - YAML::ConstNodePtr profileNode = node->GetMapValueNodeAt(i); - - //TODO: Maybe use `ParseYAML_NodeWithProfile()`? - - ProfileLinkProperty& property = ProfileProperties[profile]; - - std::vector requirements = - { - NodeRequirement("SearchLibraryNames", YAML::NodeType::Sequence, true, false), - NodeRequirement("SearchDirectories", YAML::NodeType::Sequence, true, false), - NodeRequirement("ExcludeLibraryNames", YAML::NodeType::Sequence, false, true), - NodeRequirement("AdditionalLinkOptions", YAML::NodeType::Sequence, false, true) - }; - - if(!CheckNodeRequirements(profileNode, requirements)) - { - ssLOG_ERROR("DependencyLinkProperty: Failed to meet requirements for profile " << - profile); - return false; - } - - for(int j = 0; - j < profileNode->GetMapValueNode("SearchLibraryNames")->GetChildrenCount(); - ++j) - { - std::string searchLibraryName = - profileNode ->GetMapValueNode("SearchLibraryNames") - ->GetSequenceChildScalar(j) - .DS_TRY_ACT(return false); - - property.SearchLibraryNames.push_back(searchLibraryName); - } - - for(int j = 0; - j < profileNode->GetMapValueNode("SearchDirectories")->GetChildrenCount(); - ++j) - { - std::string searchPath = - profileNode ->GetMapValueNode("SearchDirectories") - ->GetSequenceChildScalar(j) - .DS_TRY_ACT(return false); - property.SearchDirectories.push_back(searchPath); - } - - if(ExistAndHasChild(profileNode, "ExcludeLibraryNames")) - { - for(int j = 0; - j < profileNode->GetMapValueNode("ExcludeLibraryNames")->GetChildrenCount(); - ++j) - { - std::string excludeName = - profileNode ->GetMapValueNode("ExcludeLibraryNames") - ->GetSequenceChildScalar(j) - .DS_TRY_ACT(return false); - property.ExcludeLibraryNames.push_back(excludeName); - } - } - - if(ExistAndHasChild(profileNode, "AdditionalLinkOptions")) - { - for(int j = 0; - j < profileNode->GetMapValueNode("AdditionalLinkOptions")->GetChildrenCount(); - ++j) - { - std::string linkOption = - profileNode ->GetMapValueNode("AdditionalLinkOptions") - ->GetSequenceChildScalar(j) - .DS_TRY_ACT(return false); - property.AdditionalLinkOptions.push_back(linkOption); - } - } - } - - return true; -} - -bool runcpp2::Data::DependencyLinkProperty::ParseYAML_NodeWithProfile( YAML::ConstNodePtr node, - ProfileName profile) -{ - ssLOG_FUNC_DEBUG(); - - ProfileLinkProperty& property = ProfileProperties[profile]; - - std::vector requirements = - { - NodeRequirement("SearchLibraryNames", YAML::NodeType::Sequence, true, false), - NodeRequirement("SearchDirectories", YAML::NodeType::Sequence, true, false), - NodeRequirement("ExcludeLibraryNames", YAML::NodeType::Sequence, false, true), - NodeRequirement("AdditionalLinkOptions", YAML::NodeType::Sequence, false, true) - }; - - if(!CheckNodeRequirements(node, requirements)) - { - ssLOG_ERROR("DependencyLinkProperty: Failed to meet requirements for profile " << - profile); - return false; - } - - for(int j = 0; j < node->GetMapValueNode("SearchLibraryNames")->GetChildrenCount(); ++j) - { - std::string searchLibraryName = - node->GetMapValueNode("SearchLibraryNames") - ->GetSequenceChildScalar(j) - .DS_TRY_ACT(return false); - - property.SearchLibraryNames.push_back(searchLibraryName); - } - - for(int j = 0; j < node->GetMapValueNode("SearchDirectories")->GetChildrenCount(); ++j) - { - std::string searchPath = - node->GetMapValueNode("SearchDirectories") - ->GetSequenceChildScalar(j) - .DS_TRY_ACT(return false); - property.SearchDirectories.push_back(searchPath); - } - - if(ExistAndHasChild(node, "ExcludeLibraryNames")) - { - for(int j = 0; j < node->GetMapValueNode("ExcludeLibraryNames")->GetChildrenCount(); ++j) - { - std::string excludeName = - node->GetMapValueNode("ExcludeLibraryNames") - ->GetSequenceChildScalar(j) - .DS_TRY_ACT(return false); - property.ExcludeLibraryNames.push_back(excludeName); - } - } - - if(ExistAndHasChild(node, "AdditionalLinkOptions")) - { - for(int j = 0; j < node->GetMapValueNode("AdditionalLinkOptions")->GetChildrenCount(); ++j) - { - std::string linkOption = - node->GetMapValueNode("AdditionalLinkOptions") - ->GetSequenceChildScalar(j) - .DS_TRY_ACT(return false); - property.AdditionalLinkOptions.push_back(linkOption); - } - } - - ProfileProperties[profile] = property; - - return true; -} - -bool -runcpp2::Data::DependencyLinkProperty::IsYAML_NodeParsableAsDefault(YAML::ConstNodePtr node) const -{ - ssLOG_FUNC_DEBUG(); - - if(!node->IsMap()) - { - ssLOG_ERROR("DependencyLinkProperty type requires a map"); - return false; - } - - if(ExistAndHasChild(node, "SearchLibraryNames") && ExistAndHasChild(node, "SearchDirectories")) - { - std::vector requirements = - { - NodeRequirement("SearchLibraryNames", YAML::NodeType::Sequence, true, false), - NodeRequirement("SearchDirectories", YAML::NodeType::Sequence, true, false) - }; - - return CheckNodeRequirements(node, requirements); - } - - return false; -} - -std::string runcpp2::Data::DependencyLinkProperty::ToString(std::string indentation) const -{ - std::string out; - for(const std::pair& profilePair : ProfileProperties) - { - out += indentation + profilePair.first + ":\n"; - const ProfileLinkProperty& property = profilePair.second; - - if(property.SearchLibraryNames.empty()) - out += indentation + " SearchLibraryNames: []\n"; - else - { - out += indentation + " SearchLibraryNames:\n"; - for(const std::string& name : property.SearchLibraryNames) - out += indentation + " - " + GetEscapedYAMLString(name) + "\n"; - } - - if(property.SearchDirectories.empty()) - out += indentation + " SearchDirectories: []\n"; - else - { - out += indentation + " SearchDirectories:\n"; - for(const std::string& dir : property.SearchDirectories) - out += indentation + " - " + GetEscapedYAMLString(dir) + "\n"; - } - - if(!property.ExcludeLibraryNames.empty()) - { - out += indentation + " ExcludeLibraryNames:\n"; - for(const std::string& name : property.ExcludeLibraryNames) - out += indentation + " - " + GetEscapedYAMLString(name) + "\n"; - } - - if(!property.AdditionalLinkOptions.empty()) - { - out += indentation + " AdditionalLinkOptions:\n"; - for(const std::string& option : property.AdditionalLinkOptions) - out += indentation + " - " + GetEscapedYAMLString(option) + "\n"; - } - } - - return out; -} - -bool runcpp2::Data::DependencyLinkProperty::Equals(const DependencyLinkProperty& other) const -{ - if(ProfileProperties.size() != other.ProfileProperties.size()) - return false; - - for(const auto& it : ProfileProperties) - { - if(other.ProfileProperties.count(it.first) == 0) - return false; - - const ProfileLinkProperty& otherProperty = other.ProfileProperties.at(it.first); - const ProfileLinkProperty& property = it.second; - - if( property.SearchLibraryNames != otherProperty.SearchLibraryNames || - property.SearchDirectories != otherProperty.SearchDirectories || - property.ExcludeLibraryNames != otherProperty.ExcludeLibraryNames || - property.AdditionalLinkOptions != otherProperty.AdditionalLinkOptions) - { - return false; - } - } - - return true; -} diff --git a/Src/runcpp2/Data/DependencyLinkProperty.hpp b/Src/runcpp2/Data/DependencyLinkProperty.hpp new file mode 100644 index 0000000..70f7278 --- /dev/null +++ b/Src/runcpp2/Data/DependencyLinkProperty.hpp @@ -0,0 +1,293 @@ +#ifndef RUNCPP2_DATA_DEPENDENCY_LINK_PROPERTY_HPP +#define RUNCPP2_DATA_DEPENDENCY_LINK_PROPERTY_HPP + +#include "runcpp2/Data/ParseCommon.hpp" + +#include "runcpp2/LibYAML_Wrapper.hpp" +#include "runcpp2/ParseUtil.hpp" + +#include "DSResult/DSResult.hpp" +#include "ssLogger/ssLog.hpp" + +#include +#include +#include +#include + + +namespace runcpp2 +{ +namespace Data +{ + struct ProfileLinkProperty + { + std::vector SearchLibraryNames; + std::vector SearchDirectories; + std::vector ExcludeLibraryNames; + std::vector AdditionalLinkOptions; + }; + + struct DependencyLinkProperty + { + std::unordered_map ProfileProperties; + + inline bool ParseYAML_Node(YAML::ConstNodePtr node) + { + ssLOG_FUNC_DEBUG(); + + if(!node->IsMap()) + { + ssLOG_ERROR("DependencyLinkProperty: Node is not a Map"); + return false; + } + + for(int i = 0; i < node->GetChildrenCount(); ++i) + { + ProfileName profile = node ->GetMapKeyScalarAt(i) + .DS_TRY_ACT(return false); + YAML::ConstNodePtr profileNode = node->GetMapValueNodeAt(i); + + //TODO: Maybe use `ParseYAML_NodeWithProfile()`? + + ProfileLinkProperty& property = ProfileProperties[profile]; + + std::vector requirements = + { + NodeRequirement("SearchLibraryNames", YAML::NodeType::Sequence, true, false), + NodeRequirement("SearchDirectories", YAML::NodeType::Sequence, true, false), + NodeRequirement("ExcludeLibraryNames", YAML::NodeType::Sequence, false, true), + NodeRequirement("AdditionalLinkOptions", YAML::NodeType::Sequence, false, true) + }; + + if(!CheckNodeRequirements(profileNode, requirements)) + { + ssLOG_ERROR("DependencyLinkProperty: Failed to meet requirements for profile " << + profile); + return false; + } + + for(int j = 0; + j < profileNode->GetMapValueNode("SearchLibraryNames")->GetChildrenCount(); + ++j) + { + std::string searchLibraryName = + profileNode ->GetMapValueNode("SearchLibraryNames") + ->GetSequenceChildScalar(j) + .DS_TRY_ACT(return false); + + property.SearchLibraryNames.push_back(searchLibraryName); + } + + for(int j = 0; + j < profileNode->GetMapValueNode("SearchDirectories")->GetChildrenCount(); + ++j) + { + std::string searchPath = + profileNode ->GetMapValueNode("SearchDirectories") + ->GetSequenceChildScalar(j) + .DS_TRY_ACT(return false); + property.SearchDirectories.push_back(searchPath); + } + + if(ExistAndHasChild(profileNode, "ExcludeLibraryNames")) + { + for(int j = 0; + j < profileNode->GetMapValueNode("ExcludeLibraryNames")->GetChildrenCount(); + ++j) + { + std::string excludeName = + profileNode ->GetMapValueNode("ExcludeLibraryNames") + ->GetSequenceChildScalar(j) + .DS_TRY_ACT(return false); + property.ExcludeLibraryNames.push_back(excludeName); + } + } + + if(ExistAndHasChild(profileNode, "AdditionalLinkOptions")) + { + for(int j = 0; + j < profileNode->GetMapValueNode("AdditionalLinkOptions")->GetChildrenCount(); + ++j) + { + std::string linkOption = + profileNode ->GetMapValueNode("AdditionalLinkOptions") + ->GetSequenceChildScalar(j) + .DS_TRY_ACT(return false); + property.AdditionalLinkOptions.push_back(linkOption); + } + } + } + + return true; + } + + inline bool ParseYAML_NodeWithProfile(YAML::ConstNodePtr node, ProfileName profile) + { + ssLOG_FUNC_DEBUG(); + + ProfileLinkProperty& property = ProfileProperties[profile]; + + std::vector requirements = + { + NodeRequirement("SearchLibraryNames", YAML::NodeType::Sequence, true, false), + NodeRequirement("SearchDirectories", YAML::NodeType::Sequence, true, false), + NodeRequirement("ExcludeLibraryNames", YAML::NodeType::Sequence, false, true), + NodeRequirement("AdditionalLinkOptions", YAML::NodeType::Sequence, false, true) + }; + + if(!CheckNodeRequirements(node, requirements)) + { + ssLOG_ERROR("DependencyLinkProperty: Failed to meet requirements for profile " << + profile); + return false; + } + + for(int j = 0; j < node->GetMapValueNode("SearchLibraryNames")->GetChildrenCount(); ++j) + { + std::string searchLibraryName = + node->GetMapValueNode("SearchLibraryNames") + ->GetSequenceChildScalar(j) + .DS_TRY_ACT(return false); + + property.SearchLibraryNames.push_back(searchLibraryName); + } + + for(int j = 0; j < node->GetMapValueNode("SearchDirectories")->GetChildrenCount(); ++j) + { + std::string searchPath = + node->GetMapValueNode("SearchDirectories") + ->GetSequenceChildScalar(j) + .DS_TRY_ACT(return false); + property.SearchDirectories.push_back(searchPath); + } + + if(ExistAndHasChild(node, "ExcludeLibraryNames")) + { + for(int j = 0; + j < node->GetMapValueNode("ExcludeLibraryNames")->GetChildrenCount(); + ++j) + { + std::string excludeName = + node->GetMapValueNode("ExcludeLibraryNames") + ->GetSequenceChildScalar(j) + .DS_TRY_ACT(return false); + property.ExcludeLibraryNames.push_back(excludeName); + } + } + + if(ExistAndHasChild(node, "AdditionalLinkOptions")) + { + for(int j = 0; j < node->GetMapValueNode("AdditionalLinkOptions")->GetChildrenCount(); ++j) + { + std::string linkOption = + node->GetMapValueNode("AdditionalLinkOptions") + ->GetSequenceChildScalar(j) + .DS_TRY_ACT(return false); + property.AdditionalLinkOptions.push_back(linkOption); + } + } + + ProfileProperties[profile] = property; + + return true; + } + + inline bool IsYAML_NodeParsableAsDefault(YAML::ConstNodePtr node) const + { + ssLOG_FUNC_DEBUG(); + + if(!node->IsMap()) + { + ssLOG_ERROR("DependencyLinkProperty type requires a map"); + return false; + } + + if( ExistAndHasChild(node, "SearchLibraryNames") && + ExistAndHasChild(node, "SearchDirectories")) + { + std::vector requirements = + { + NodeRequirement("SearchLibraryNames", YAML::NodeType::Sequence, true, false), + NodeRequirement("SearchDirectories", YAML::NodeType::Sequence, true, false) + }; + + return CheckNodeRequirements(node, requirements); + } + + return false; + } + + inline std::string ToString(std::string indentation) const + { + std::string out; + for(const std::pair& profilePair : + ProfileProperties) + { + out += indentation + profilePair.first + ":\n"; + const ProfileLinkProperty& property = profilePair.second; + + if(property.SearchLibraryNames.empty()) + out += indentation + " SearchLibraryNames: []\n"; + else + { + out += indentation + " SearchLibraryNames:\n"; + for(const std::string& name : property.SearchLibraryNames) + out += indentation + " - " + GetEscapedYAMLString(name) + "\n"; + } + + if(property.SearchDirectories.empty()) + out += indentation + " SearchDirectories: []\n"; + else + { + out += indentation + " SearchDirectories:\n"; + for(const std::string& dir : property.SearchDirectories) + out += indentation + " - " + GetEscapedYAMLString(dir) + "\n"; + } + + if(!property.ExcludeLibraryNames.empty()) + { + out += indentation + " ExcludeLibraryNames:\n"; + for(const std::string& name : property.ExcludeLibraryNames) + out += indentation + " - " + GetEscapedYAMLString(name) + "\n"; + } + + if(!property.AdditionalLinkOptions.empty()) + { + out += indentation + " AdditionalLinkOptions:\n"; + for(const std::string& option : property.AdditionalLinkOptions) + out += indentation + " - " + GetEscapedYAMLString(option) + "\n"; + } + } + + return out; + } + + inline bool Equals(const DependencyLinkProperty& other) const + { + if(ProfileProperties.size() != other.ProfileProperties.size()) + return false; + + for(const auto& it : ProfileProperties) + { + if(other.ProfileProperties.count(it.first) == 0) + return false; + + const ProfileLinkProperty& otherProperty = other.ProfileProperties.at(it.first); + const ProfileLinkProperty& property = it.second; + + if( property.SearchLibraryNames != otherProperty.SearchLibraryNames || + property.SearchDirectories != otherProperty.SearchDirectories || + property.ExcludeLibraryNames != otherProperty.ExcludeLibraryNames || + property.AdditionalLinkOptions != otherProperty.AdditionalLinkOptions) + { + return false; + } + } + + return true; + } + }; +} +} + +#endif diff --git a/Src/runcpp2/Data/DependencySource.cpp b/Src/runcpp2/Data/DependencySource.cpp deleted file mode 100644 index 5a27813..0000000 --- a/Src/runcpp2/Data/DependencySource.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "runcpp2/Data/DependencySource.hpp" -#include "runcpp2/Data/ParseCommon.hpp" -#include "runcpp2/ParseUtil.hpp" -#include "ssLogger/ssLog.hpp" - -bool runcpp2::Data::DependencySource::ParseYAML_Node(YAML::ConstNodePtr node) -{ - if(ExistAndHasChild(node, "ImportPath")) - { - DS_UNWRAP_ASSIGN_ACT( ImportPath, - node->GetMapValueScalar("ImportPath"), - ssLOG_ERROR(DS_TMP_ERROR.ToString()); return false); - - if(ImportPath.is_absolute()) - { - ssLOG_ERROR("DependencySource: ImportPath must be relative: " << ImportPath.string()); - return false; - } - } - - if(ExistAndHasChild(node, "Git")) - { - if(ExistAndHasChild(node, "Local")) - { - ssLOG_ERROR("DependencySource: Both Git and Local sources found"); - return false; - } - - GitSource gitSource; - YAML::ConstNodePtr gitNode = node->GetMapValueNode("Git"); - if(!gitSource.ParseYAML_Node(gitNode)) - return false; - Source = gitSource; - return true; - } - else if(ExistAndHasChild(node, "Local")) - { - if(ExistAndHasChild(node, "Git")) - { - ssLOG_ERROR("DependencySource: Both Git and Local sources found"); - return false; - } - - LocalSource localSource; - YAML::ConstNodePtr localNode = node->GetMapValueNode("Local"); - if(!localSource.ParseYAML_Node(localNode)) - return false; - Source = localSource; - return true; - } - //If no source is found, we need to check if it's an imported source. - //If so, we assume it's a local source with path "./" - else if(!ImportPath.empty()) - { - LocalSource localSource; - localSource.Path = "./"; - Source = localSource; - return true; - } - - ssLOG_ERROR("DependencySource: Neither Git nor Local source found"); - return false; -} - -std::string runcpp2::Data::DependencySource::ToString(std::string indentation) const -{ - std::string out; - if(!ImportPath.empty()) - out += indentation + "ImportPath: " + GetEscapedYAMLString(ImportPath.string()) + "\n"; - - if(mpark::get_if(&Source)) - { - const GitSource* git = mpark::get_if(&Source); - out += git->ToString(indentation); - } - else if(mpark::get_if(&Source)) - { - const LocalSource* local = mpark::get_if(&Source); - out += local->ToString(indentation); - } - else - { - ssLOG_ERROR("Invalid DependencySource type"); - return ""; - } - return out; -} - -bool runcpp2::Data::DependencySource::Equals(const DependencySource& other) const -{ - if(ImportPath != other.ImportPath) - return false; - - if(mpark::get_if(&Source)) - { - if(mpark::get_if(&other.Source)) - { - const GitSource* git = mpark::get_if(&Source); - const GitSource* otherGit = mpark::get_if(&other.Source); - return git->Equals(*otherGit); - } - else - return false; - } - else if(mpark::get_if(&Source)) - { - if(mpark::get_if(&other.Source)) - { - const LocalSource* local = mpark::get_if(&Source); - const LocalSource* otherLocal = mpark::get_if(&other.Source); - return local->Equals(*otherLocal); - } - else - return false; - } - - ssLOG_ERROR("Invalid DependencySource type"); - return false; -} diff --git a/Src/runcpp2/Data/DependencySource.hpp b/Src/runcpp2/Data/DependencySource.hpp new file mode 100644 index 0000000..56a1e0b --- /dev/null +++ b/Src/runcpp2/Data/DependencySource.hpp @@ -0,0 +1,147 @@ +#ifndef RUNCPP2_DATA_DEPENDENCY_SOURCE_HPP +#define RUNCPP2_DATA_DEPENDENCY_SOURCE_HPP + +#include "runcpp2/Data/GitSource.hpp" +#include "runcpp2/Data/LocalSource.hpp" +#include "runcpp2/ParseUtil.hpp" +#include "runcpp2/LibYAML_Wrapper.hpp" + +#include "ssLogger/ssLog.hpp" +#include "ghc/filesystem.hpp" +#include "mpark/variant.hpp" +#include "DSResult/DSResult.hpp" + +#include +#include +#include + +namespace runcpp2 +{ +namespace Data +{ + struct DependencySource + { + mpark::variant Source; + ghc::filesystem::path ImportPath; + std::vector> ImportedSources; + + inline bool ParseYAML_Node(YAML::ConstNodePtr node) + { + if(ExistAndHasChild(node, "ImportPath")) + { + DS_UNWRAP_ASSIGN_ACT( ImportPath, + node->GetMapValueScalar("ImportPath"), + ssLOG_ERROR(DS_TMP_ERROR.ToString()); return false); + + if(ImportPath.is_absolute()) + { + ssLOG_ERROR("DependencySource: ImportPath must be relative: " << + ImportPath.string()); + return false; + } + } + + if(ExistAndHasChild(node, "Git")) + { + if(ExistAndHasChild(node, "Local")) + { + ssLOG_ERROR("DependencySource: Both Git and Local sources found"); + return false; + } + + GitSource gitSource; + YAML::ConstNodePtr gitNode = node->GetMapValueNode("Git"); + if(!gitSource.ParseYAML_Node(gitNode)) + return false; + Source = gitSource; + return true; + } + else if(ExistAndHasChild(node, "Local")) + { + if(ExistAndHasChild(node, "Git")) + { + ssLOG_ERROR("DependencySource: Both Git and Local sources found"); + return false; + } + + LocalSource localSource; + YAML::ConstNodePtr localNode = node->GetMapValueNode("Local"); + if(!localSource.ParseYAML_Node(localNode)) + return false; + Source = localSource; + return true; + } + //If no source is found, we need to check if it's an imported source. + //If so, we assume it's a local source with path "./" + else if(!ImportPath.empty()) + { + LocalSource localSource; + localSource.Path = "./"; + Source = localSource; + return true; + } + + ssLOG_ERROR("DependencySource: Neither Git nor Local source found"); + return false; + } + + inline std::string ToString(std::string indentation) const + { + std::string out; + if(!ImportPath.empty()) + out += indentation + "ImportPath: " + GetEscapedYAMLString(ImportPath.string()) + "\n"; + + if(mpark::get_if(&Source)) + { + const GitSource* git = mpark::get_if(&Source); + out += git->ToString(indentation); + } + else if(mpark::get_if(&Source)) + { + const LocalSource* local = mpark::get_if(&Source); + out += local->ToString(indentation); + } + else + { + ssLOG_ERROR("Invalid DependencySource type"); + return ""; + } + return out; + } + + inline bool Equals(const DependencySource& other) const + { + if(ImportPath != other.ImportPath) + return false; + + if(mpark::get_if(&Source)) + { + if(mpark::get_if(&other.Source)) + { + const GitSource* git = mpark::get_if(&Source); + const GitSource* otherGit = mpark::get_if(&other.Source); + return git->Equals(*otherGit); + } + else + return false; + } + else if(mpark::get_if(&Source)) + { + if(mpark::get_if(&other.Source)) + { + const LocalSource* local = mpark::get_if(&Source); + const LocalSource* otherLocal = mpark::get_if(&other.Source); + return local->Equals(*otherLocal); + } + else + return false; + } + + ssLOG_ERROR("Invalid DependencySource type"); + return false; + } + }; +} +} + +#endif diff --git a/Src/runcpp2/Data/FileProperties.cpp b/Src/runcpp2/Data/FileProperties.cpp deleted file mode 100644 index 5737310..0000000 --- a/Src/runcpp2/Data/FileProperties.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "runcpp2/Data/FileProperties.hpp" -#include "runcpp2/Data/ParseCommon.hpp" -#include "runcpp2/ParseUtil.hpp" -#include "ssLogger/ssLog.hpp" - -bool runcpp2::Data::FileProperties::ParseYAML_Node(YAML::ConstNodePtr node) -{ - std::vector requirements = - { - NodeRequirement("Prefix", YAML::NodeType::Map, true, true), - NodeRequirement("Extension", YAML::NodeType::Map, true, false) - }; - - if(!CheckNodeRequirements(node, requirements)) - { - ssLOG_ERROR("File Properties: Failed to meet requirements"); - return false; - } - - if(ExistAndHasChild(node, "Prefix")) - { - YAML::ConstNodePtr prefixNode = node->GetMapValueNode("Prefix"); - for(int i = 0; i < prefixNode->GetChildrenCount(); ++i) - { - std::string key = prefixNode->GetMapKeyScalarAt(i).DS_TRY_ACT(return false); - std::string value = prefixNode ->GetMapValueScalarAt(i) - .DS_TRY_ACT(return false); - Prefix[key] = value; - } - } - - if(ExistAndHasChild(node, "Extension")) - { - YAML::ConstNodePtr extensionNode = node->GetMapValueNode("Extension"); - for(int i = 0; i < extensionNode->GetChildrenCount(); ++i) - { - std::string key = extensionNode ->GetMapKeyScalarAt(i) - .DS_TRY_ACT(return false); - std::string value = extensionNode ->GetMapValueScalarAt(i) - .DS_TRY_ACT(return false); - Extension[key] = value; - } - } - - return true; -} - -std::string runcpp2::Data::FileProperties::ToString(std::string indentation) const -{ - std::string out; - - if(!Prefix.empty()) - { - out += indentation + "Prefix:\n"; - for(const auto& it : Prefix) - out += indentation + " " + it.first + ": " + GetEscapedYAMLString(it.second) + "\n"; - } - - if(!Extension.empty()) - { - out += indentation + "Extension:\n"; - for(const auto& it : Extension) - out += indentation + " " + it.first + ": " + GetEscapedYAMLString(it.second) + "\n"; - } - - out += "\n"; - return out; -} - -bool runcpp2::Data::FileProperties::Equals(const FileProperties& other) const -{ - if(Prefix.size() != other.Prefix.size() || Extension.size() != other.Extension.size()) - return false; - - for(const auto& it : Prefix) - { - if(other.Prefix.count(it.first) == 0 || other.Prefix.at(it.first) != it.second) - return false; - } - - for(const auto& it : Extension) - { - if(other.Extension.count(it.first) == 0 || other.Extension.at(it.first) != it.second) - return false; - } - - return true; -} diff --git a/Src/runcpp2/Data/FileProperties.hpp b/Src/runcpp2/Data/FileProperties.hpp new file mode 100644 index 0000000..fe19714 --- /dev/null +++ b/Src/runcpp2/Data/FileProperties.hpp @@ -0,0 +1,119 @@ +#ifndef RUNCPP2_DATA_FILE_PROPERTIES_HPP +#define RUNCPP2_DATA_FILE_PROPERTIES_HPP + +#include "runcpp2/Data/ParseCommon.hpp" +#include "runcpp2/LibYAML_Wrapper.hpp" +#include "runcpp2/ParseUtil.hpp" + +#include "ssLogger/ssLog.hpp" +#include "DSResult/DSResult.hpp" + +#include +#include +#include +#include + +namespace runcpp2 +{ +namespace Data +{ + struct FileProperties + { + std::unordered_map Prefix; + std::unordered_map Extension; + + inline bool ParseYAML_Node(YAML::ConstNodePtr node) + { + std::vector requirements = + { + NodeRequirement("Prefix", YAML::NodeType::Map, true, true), + NodeRequirement("Extension", YAML::NodeType::Map, true, false) + }; + + if(!CheckNodeRequirements(node, requirements)) + { + ssLOG_ERROR("File Properties: Failed to meet requirements"); + return false; + } + + if(ExistAndHasChild(node, "Prefix")) + { + YAML::ConstNodePtr prefixNode = node->GetMapValueNode("Prefix"); + for(int i = 0; i < prefixNode->GetChildrenCount(); ++i) + { + std::string key = prefixNode->GetMapKeyScalarAt(i) + .DS_TRY_ACT(return false); + std::string value = prefixNode ->GetMapValueScalarAt(i) + .DS_TRY_ACT(return false); + Prefix[key] = value; + } + } + + if(ExistAndHasChild(node, "Extension")) + { + YAML::ConstNodePtr extensionNode = node->GetMapValueNode("Extension"); + for(int i = 0; i < extensionNode->GetChildrenCount(); ++i) + { + std::string key = extensionNode ->GetMapKeyScalarAt(i) + .DS_TRY_ACT(return false); + std::string value = extensionNode ->GetMapValueScalarAt(i) + .DS_TRY_ACT(return false); + Extension[key] = value; + } + } + + return true; + } + + inline std::string ToString(std::string indentation) const + { + std::string out; + + if(!Prefix.empty()) + { + out += indentation + "Prefix:\n"; + for(const auto& it : Prefix) + { + out += indentation + " " + it.first + ": " + + GetEscapedYAMLString(it.second) + "\n"; + } + } + + if(!Extension.empty()) + { + out += indentation + "Extension:\n"; + for(const auto& it : Extension) + { + out += indentation + " " + it.first + ": " + + GetEscapedYAMLString(it.second) + "\n"; + } + } + + out += "\n"; + return out; + } + + inline bool Equals(const FileProperties& other) const + { + if(Prefix.size() != other.Prefix.size() || Extension.size() != other.Extension.size()) + return false; + + for(const auto& it : Prefix) + { + if(other.Prefix.count(it.first) == 0 || other.Prefix.at(it.first) != it.second) + return false; + } + + for(const auto& it : Extension) + { + if(other.Extension.count(it.first) == 0 || other.Extension.at(it.first) != it.second) + return false; + } + + return true; + } + }; +} +} + +#endif diff --git a/Src/runcpp2/Data/FilesToCopyInfo.cpp b/Src/runcpp2/Data/FilesToCopyInfo.cpp deleted file mode 100644 index 4444493..0000000 --- a/Src/runcpp2/Data/FilesToCopyInfo.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "runcpp2/Data/FilesToCopyInfo.hpp" -#include "runcpp2/ParseUtil.hpp" -#include "ssLogger/ssLog.hpp" - -bool runcpp2::Data::FilesToCopyInfo::ParseYAML_Node(YAML::ConstNodePtr node) -{ - if(!node->IsMap()) - { - ssLOG_ERROR("FilesToCopyInfo: Node is not a Map"); - return false; - } - - //If we skip platform profile - if(IsYAML_NodeParsableAsDefault(node)) - { - if(!ParseYAML_NodeWithProfile(node, "DefaultProfile")) - return false; - } - else - { - for(int i = 0; i < node->GetChildrenCount(); ++i) - { - DS_UNWRAP_DECL_ACT( ProfileName profile, - node->GetMapKeyScalarAt(i), - ssLOG_ERROR(DS_TMP_ERROR.ToString()); return false); - - if(!ParseYAML_NodeWithProfile(node->GetMapValueNodeAt(i), profile)) - return false; - } - } - - return true; -} - -bool runcpp2::Data::FilesToCopyInfo::ParseYAML_NodeWithProfile( YAML::ConstNodePtr node, - ProfileName profile) -{ - ssLOG_FUNC_DEBUG(); - if(!node->IsSequence()) - { - ssLOG_ERROR("FilesToCopyInfo: Node is not a sequence"); - return false; - } - - for(int i = 0; i < node->GetChildrenCount(); i++) - ProfileFiles[profile].push_back(node->GetSequenceChildScalar(i).value()); - - return true; -} - -bool -runcpp2::Data::FilesToCopyInfo::IsYAML_NodeParsableAsDefault(YAML::ConstNodePtr node) const -{ - ssLOG_FUNC_DEBUG(); - return node->IsSequence(); -} - -std::string runcpp2::Data::FilesToCopyInfo::ToString(std::string indentation) const -{ - std::string out; - - if(ProfileFiles.empty()) - return out; - - for(auto it = ProfileFiles.begin(); it != ProfileFiles.end(); it++) - { - if(it->second.empty()) - out += indentation + it->first + ": []\n"; - else - { - out += indentation + it->first + ":\n"; - for(int i = 0; i < it->second.size(); i++) - out += indentation + "- " + GetEscapedYAMLString(it->second[i]) + "\n"; - } - } - - return out; -} - -bool runcpp2::Data::FilesToCopyInfo::Equals(const FilesToCopyInfo& other) const -{ - if(ProfileFiles.size() != other.ProfileFiles.size()) - return false; - - for(const auto& it : ProfileFiles) - { - if(other.ProfileFiles.count(it.first) == 0 || other.ProfileFiles.at(it.first) != it.second) - return false; - } - - return true; -} diff --git a/Src/runcpp2/Data/FilesToCopyInfo.hpp b/Src/runcpp2/Data/FilesToCopyInfo.hpp new file mode 100644 index 0000000..a5cad8c --- /dev/null +++ b/Src/runcpp2/Data/FilesToCopyInfo.hpp @@ -0,0 +1,117 @@ +#ifndef RUNCPP2_DATA_FILES_TO_COPY_INFO_HPP +#define RUNCPP2_DATA_FILES_TO_COPY_INFO_HPP + +#include "runcpp2/Data/ParseCommon.hpp" +#include "runcpp2/LibYAML_Wrapper.hpp" +#include "runcpp2/ParseUtil.hpp" + +#include "DSResult/DSResult.hpp" +#include "ssLogger/ssLog.hpp" + +#include +#include +#include +#include + +namespace runcpp2 +{ +namespace Data +{ + struct FilesToCopyInfo + { + std::unordered_map> ProfileFiles; + + inline bool ParseYAML_Node(YAML::ConstNodePtr node) + { + if(!node->IsMap()) + { + ssLOG_ERROR("FilesToCopyInfo: Node is not a Map"); + return false; + } + + //If we skip platform profile + if(IsYAML_NodeParsableAsDefault(node)) + { + if(!ParseYAML_NodeWithProfile(node, "DefaultProfile")) + return false; + } + else + { + for(int i = 0; i < node->GetChildrenCount(); ++i) + { + DS_UNWRAP_DECL_ACT( ProfileName profile, + node->GetMapKeyScalarAt(i), + ssLOG_ERROR(DS_TMP_ERROR.ToString()); return false); + + if(!ParseYAML_NodeWithProfile(node->GetMapValueNodeAt(i), profile)) + return false; + } + } + + return true; + } + + inline bool ParseYAML_NodeWithProfile(YAML::ConstNodePtr node, ProfileName profile) + { + ssLOG_FUNC_DEBUG(); + if(!node->IsSequence()) + { + ssLOG_ERROR("FilesToCopyInfo: Node is not a sequence"); + return false; + } + + for(int i = 0; i < node->GetChildrenCount(); i++) + ProfileFiles[profile].push_back(node->GetSequenceChildScalar(i).value()); + + return true; + } + + inline bool IsYAML_NodeParsableAsDefault(YAML::ConstNodePtr node) const + { + ssLOG_FUNC_DEBUG(); + return node->IsSequence(); + } + + inline std::string ToString(std::string indentation) const + { + std::string out; + + if(ProfileFiles.empty()) + return out; + + for(auto it = ProfileFiles.begin(); it != ProfileFiles.end(); it++) + { + if(it->second.empty()) + out += indentation + it->first + ": []\n"; + else + { + out += indentation + it->first + ":\n"; + for(int i = 0; i < it->second.size(); i++) + out += indentation + "- " + GetEscapedYAMLString(it->second[i]) + "\n"; + } + } + + return out; + } + + inline bool Equals(const FilesToCopyInfo& other) const + { + if(ProfileFiles.size() != other.ProfileFiles.size()) + return false; + + for(const auto& it : ProfileFiles) + { + if( other.ProfileFiles.count(it.first) == 0 || + other.ProfileFiles.at(it.first) != it.second) + { + return false; + } + } + + return true; + } + }; +} +} + +#endif diff --git a/Src/runcpp2/Data/FilesTypesInfo.cpp b/Src/runcpp2/Data/FilesTypesInfo.cpp deleted file mode 100644 index 21e2353..0000000 --- a/Src/runcpp2/Data/FilesTypesInfo.cpp +++ /dev/null @@ -1,99 +0,0 @@ -#include "runcpp2/Data/FilesTypesInfo.hpp" -#include "runcpp2/ParseUtil.hpp" -#include "ssLogger/ssLog.hpp" - -bool runcpp2::Data::FilesTypesInfo::ParseYAML_Node(YAML::ConstNodePtr node) -{ - ssLOG_FUNC_DEBUG(); - - std::vector requirements = - { - NodeRequirement("ObjectLinkFile", YAML::NodeType::Map, true, false), - NodeRequirement("SharedLinkFile", YAML::NodeType::Map, true, false), - NodeRequirement("SharedLibraryFile", YAML::NodeType::Map, true, false), - NodeRequirement("StaticLinkFile", YAML::NodeType::Map, true, false), - NodeRequirement("DebugSymbolFile", YAML::NodeType::Map, false, false), - }; - - if(!CheckNodeRequirements(node, requirements)) - { - ssLOG_ERROR("FilesTypesInfo: Failed to meet requirements"); - return false; - } - - YAML::ConstNodePtr objectLinkFileNode = node->GetMapValueNode("ObjectLinkFile"); - if(!ObjectLinkFile.ParseYAML_Node(objectLinkFileNode)) - { - ssLOG_ERROR("Compiler profile: ObjectLinkFile is invalid"); - return false; - } - - YAML::ConstNodePtr sharedLinkFileNode = node->GetMapValueNode("SharedLinkFile"); - if(!SharedLinkFile.ParseYAML_Node(sharedLinkFileNode)) - { - ssLOG_ERROR("Compiler profile: SharedLinkFile is invalid"); - return false; - } - - YAML::ConstNodePtr sharedLibraryFileNode = node->GetMapValueNode("SharedLibraryFile"); - if(!SharedLibraryFile.ParseYAML_Node(sharedLibraryFileNode)) - { - ssLOG_ERROR("Compiler profile: SharedLibraryFile is invalid"); - return false; - } - - YAML::ConstNodePtr staticLinkFileNode = node->GetMapValueNode("StaticLinkFile"); - if(!StaticLinkFile.ParseYAML_Node(staticLinkFileNode)) - { - ssLOG_ERROR("Compiler profile: StaticLinkFile is invalid"); - return false; - } - - if(ExistAndHasChild(node, "DebugSymbolFile")) - { - YAML::ConstNodePtr debugSymbolFileNode = node->GetMapValueNode("DebugSymbolFile"); - if(!DebugSymbolFile.ParseYAML_Node(debugSymbolFileNode)) - { - ssLOG_ERROR("Compiler profile: DebugSymbolFile is invalid"); - return false; - } - } - - return true; -} - -std::string runcpp2::Data::FilesTypesInfo::ToString(std::string indentation) const -{ - ssLOG_FUNC_DEBUG(); - - std::string out; - - out += indentation + "ObjectLinkFile:\n"; - out += ObjectLinkFile.ToString(indentation + " "); - - out += indentation + "SharedLinkFile:\n"; - out += SharedLinkFile.ToString(indentation + " "); - - out += indentation + "SharedLibraryFile:\n"; - out += SharedLibraryFile.ToString(indentation + " "); - - out += indentation + "StaticLinkFile:\n"; - out += StaticLinkFile.ToString(indentation + " "); - - if(!DebugSymbolFile.Prefix.empty() || !DebugSymbolFile.Extension.empty()) - { - out += indentation + "DebugSymbolFile:\n"; - out += DebugSymbolFile.ToString(indentation + " "); - } - - return out; -} - -bool runcpp2::Data::FilesTypesInfo::Equals(const FilesTypesInfo& other) const -{ - return ObjectLinkFile.Equals(other.ObjectLinkFile) && - SharedLinkFile.Equals(other.SharedLinkFile) && - SharedLibraryFile.Equals(other.SharedLibraryFile) && - StaticLinkFile.Equals(other.StaticLinkFile) && - DebugSymbolFile.Equals(other.DebugSymbolFile); -} diff --git a/Src/runcpp2/Data/FilesTypesInfo.hpp b/Src/runcpp2/Data/FilesTypesInfo.hpp new file mode 100644 index 0000000..248b005 --- /dev/null +++ b/Src/runcpp2/Data/FilesTypesInfo.hpp @@ -0,0 +1,125 @@ +#ifndef RUNCPP2_DATA_FILES_TYPES_HPP +#define RUNCPP2_DATA_FILES_TYPES_HPP + +#include "runcpp2/Data/FileProperties.hpp" +#include "runcpp2/LibYAML_Wrapper.hpp" +#include "runcpp2/ParseUtil.hpp" + +#include "ssLogger/ssLog.hpp" + +#include +#include +#include + +namespace runcpp2 +{ +namespace Data +{ + struct FilesTypesInfo + { + FileProperties ObjectLinkFile; + FileProperties SharedLinkFile; + FileProperties SharedLibraryFile; + FileProperties StaticLinkFile; + FileProperties DebugSymbolFile; + + inline bool ParseYAML_Node(YAML::ConstNodePtr node) + { + ssLOG_FUNC_DEBUG(); + + std::vector requirements = + { + NodeRequirement("ObjectLinkFile", YAML::NodeType::Map, true, false), + NodeRequirement("SharedLinkFile", YAML::NodeType::Map, true, false), + NodeRequirement("SharedLibraryFile", YAML::NodeType::Map, true, false), + NodeRequirement("StaticLinkFile", YAML::NodeType::Map, true, false), + NodeRequirement("DebugSymbolFile", YAML::NodeType::Map, false, false), + }; + + if(!CheckNodeRequirements(node, requirements)) + { + ssLOG_ERROR("FilesTypesInfo: Failed to meet requirements"); + return false; + } + + YAML::ConstNodePtr objectLinkFileNode = node->GetMapValueNode("ObjectLinkFile"); + if(!ObjectLinkFile.ParseYAML_Node(objectLinkFileNode)) + { + ssLOG_ERROR("Compiler profile: ObjectLinkFile is invalid"); + return false; + } + + YAML::ConstNodePtr sharedLinkFileNode = node->GetMapValueNode("SharedLinkFile"); + if(!SharedLinkFile.ParseYAML_Node(sharedLinkFileNode)) + { + ssLOG_ERROR("Compiler profile: SharedLinkFile is invalid"); + return false; + } + + YAML::ConstNodePtr sharedLibraryFileNode = node->GetMapValueNode("SharedLibraryFile"); + if(!SharedLibraryFile.ParseYAML_Node(sharedLibraryFileNode)) + { + ssLOG_ERROR("Compiler profile: SharedLibraryFile is invalid"); + return false; + } + + YAML::ConstNodePtr staticLinkFileNode = node->GetMapValueNode("StaticLinkFile"); + if(!StaticLinkFile.ParseYAML_Node(staticLinkFileNode)) + { + ssLOG_ERROR("Compiler profile: StaticLinkFile is invalid"); + return false; + } + + if(ExistAndHasChild(node, "DebugSymbolFile")) + { + YAML::ConstNodePtr debugSymbolFileNode = node->GetMapValueNode("DebugSymbolFile"); + if(!DebugSymbolFile.ParseYAML_Node(debugSymbolFileNode)) + { + ssLOG_ERROR("Compiler profile: DebugSymbolFile is invalid"); + return false; + } + } + + return true; + } + + inline std::string ToString(std::string indentation) const + { + ssLOG_FUNC_DEBUG(); + + std::string out; + + out += indentation + "ObjectLinkFile:\n"; + out += ObjectLinkFile.ToString(indentation + " "); + + out += indentation + "SharedLinkFile:\n"; + out += SharedLinkFile.ToString(indentation + " "); + + out += indentation + "SharedLibraryFile:\n"; + out += SharedLibraryFile.ToString(indentation + " "); + + out += indentation + "StaticLinkFile:\n"; + out += StaticLinkFile.ToString(indentation + " "); + + if(!DebugSymbolFile.Prefix.empty() || !DebugSymbolFile.Extension.empty()) + { + out += indentation + "DebugSymbolFile:\n"; + out += DebugSymbolFile.ToString(indentation + " "); + } + + return out; + } + + inline bool Equals(const FilesTypesInfo& other) const + { + return ObjectLinkFile.Equals(other.ObjectLinkFile) && + SharedLinkFile.Equals(other.SharedLinkFile) && + SharedLibraryFile.Equals(other.SharedLibraryFile) && + StaticLinkFile.Equals(other.StaticLinkFile) && + DebugSymbolFile.Equals(other.DebugSymbolFile); + } + }; +} +} + +#endif diff --git a/Src/runcpp2/Data/FlagsOverrideInfo.cpp b/Src/runcpp2/Data/FlagsOverrideInfo.cpp deleted file mode 100644 index 3a2eb83..0000000 --- a/Src/runcpp2/Data/FlagsOverrideInfo.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "runcpp2/Data/FlagsOverrideInfo.hpp" -#include "runcpp2/Data/ParseCommon.hpp" -#include "runcpp2/ParseUtil.hpp" -#include "ssLogger/ssLog.hpp" - -bool runcpp2::Data::FlagsOverrideInfo::ParseYAML_Node(YAML::ConstNodePtr node) -{ - ssLOG_FUNC_DEBUG(); - - std::vector requirements = - { - NodeRequirement("Remove", YAML::NodeType::Scalar, false, true), - NodeRequirement("Append", YAML::NodeType::Scalar, false, true) - }; - - if(!CheckNodeRequirements(node, requirements)) - { - ssLOG_ERROR("FlagsOverrideInfo: Failed to meet requirements"); - return false; - } - - if(ExistAndHasChild(node, "Remove")) - { - Remove = node->GetMapValueScalar("Remove").DS_TRY_ACT(return false); - } - - if(ExistAndHasChild(node, "Append")) - { - Append = node->GetMapValueScalar("Append").DS_TRY_ACT(return false); - } - - return true; -} - -bool runcpp2::Data::FlagsOverrideInfo::IsYAML_NodeParsableAsDefault(YAML::ConstNodePtr node) const -{ - ssLOG_FUNC_DEBUG(); - - if(!node->IsMap()) - { - ssLOG_ERROR("FlagsOverrideInfo type requires a map"); - return false; - } - - if(ExistAndHasChild(node, "Remove") || ExistAndHasChild(node, "Append")) - { - std::vector requirements = - { - NodeRequirement("Remove", YAML::NodeType::Scalar, false, true), - NodeRequirement("Append", YAML::NodeType::Scalar, false, true) - }; - - if(!CheckNodeRequirements(node, requirements)) - return false; - - return true; - } - - return false; -} - -std::string runcpp2::Data::FlagsOverrideInfo::ToString(std::string indentation) const -{ - std::string out; - - if(!Remove.empty()) - out += indentation + "Remove: " + GetEscapedYAMLString(Remove) + "\n"; - - if(!Append.empty()) - out += indentation + "Append: " + GetEscapedYAMLString(Append) + "\n"; - - return out; -} - -bool runcpp2::Data::FlagsOverrideInfo::Equals(const FlagsOverrideInfo& other) const -{ - return Remove == other.Remove && Append == other.Append; -} diff --git a/Src/runcpp2/Data/FlagsOverrideInfo.hpp b/Src/runcpp2/Data/FlagsOverrideInfo.hpp new file mode 100644 index 0000000..e8cb74b --- /dev/null +++ b/Src/runcpp2/Data/FlagsOverrideInfo.hpp @@ -0,0 +1,100 @@ +#ifndef RUNCPP2_DATA_FLAGS_OVERRIDE_INFO_HPP +#define RUNCPP2_DATA_FLAGS_OVERRIDE_INFO_HPP + +#include "runcpp2/LibYAML_Wrapper.hpp" +#include "runcpp2/ParseUtil.hpp" + +#include "ssLogger/ssLog.hpp" +#include "DSResult/DSResult.hpp" + +#include +#include + +namespace runcpp2 +{ +namespace Data +{ + struct FlagsOverrideInfo + { + std::string Remove; + std::string Append; + + inline bool ParseYAML_Node(YAML::ConstNodePtr node) + { + ssLOG_FUNC_DEBUG(); + + std::vector requirements = + { + NodeRequirement("Remove", YAML::NodeType::Scalar, false, true), + NodeRequirement("Append", YAML::NodeType::Scalar, false, true) + }; + + if(!CheckNodeRequirements(node, requirements)) + { + ssLOG_ERROR("FlagsOverrideInfo: Failed to meet requirements"); + return false; + } + + if(ExistAndHasChild(node, "Remove")) + { + Remove = node->GetMapValueScalar("Remove").DS_TRY_ACT(return false); + } + + if(ExistAndHasChild(node, "Append")) + { + Append = node->GetMapValueScalar("Append").DS_TRY_ACT(return false); + } + + return true; + } + + inline bool IsYAML_NodeParsableAsDefault(YAML::ConstNodePtr node) const + { + ssLOG_FUNC_DEBUG(); + + if(!node->IsMap()) + { + ssLOG_ERROR("FlagsOverrideInfo type requires a map"); + return false; + } + + if(ExistAndHasChild(node, "Remove") || ExistAndHasChild(node, "Append")) + { + std::vector requirements = + { + NodeRequirement("Remove", YAML::NodeType::Scalar, false, true), + NodeRequirement("Append", YAML::NodeType::Scalar, false, true) + }; + + if(!CheckNodeRequirements(node, requirements)) + return false; + + return true; + } + + return false; + } + + inline std::string ToString(std::string indentation) const + { + std::string out; + + if(!Remove.empty()) + out += indentation + "Remove: " + GetEscapedYAMLString(Remove) + "\n"; + + if(!Append.empty()) + out += indentation + "Append: " + GetEscapedYAMLString(Append) + "\n"; + + return out; + } + + inline bool Equals(const FlagsOverrideInfo& other) const + { + return Remove == other.Remove && Append == other.Append; + } + }; +} +} + + +#endif diff --git a/Src/runcpp2/Data/GitSource.cpp b/Src/runcpp2/Data/GitSource.cpp deleted file mode 100644 index ee3ac43..0000000 --- a/Src/runcpp2/Data/GitSource.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "runcpp2/Data/GitSource.hpp" -#include "runcpp2/Data/ParseCommon.hpp" -#include "runcpp2/ParseUtil.hpp" -#include "ssLogger/ssLog.hpp" - -bool runcpp2::Data::GitSource::ParseYAML_Node(YAML::ConstNodePtr node) -{ - std::vector requirements = - { - NodeRequirement("URL", YAML::NodeType::Scalar, true, false), - NodeRequirement("Branch", YAML::NodeType::Scalar, false, false), - NodeRequirement("FullHistory", YAML::NodeType::Scalar, false, false), - NodeRequirement("SubmoduleInitType", YAML::NodeType::Scalar, false, false) - }; - - if(!CheckNodeRequirements(node, requirements)) - { - ssLOG_ERROR("GitSource: Failed to meet requirements"); - return false; - } - - URL = node->GetMapValueScalar("URL").value(); - - if(ExistAndHasChild(node, "Branch")) - { - Branch = node->GetMapValueScalar("Branch").DS_TRY_ACT(return false); - } - if(ExistAndHasChild(node, "FullHistory")) - { - FullHistory = node->GetMapValueScalar("FullHistory").DS_TRY_ACT(return false); - } - if(ExistAndHasChild(node, "SubmoduleInitType")) - { - std::string submoduleTypeString = - node->GetMapValueScalar("SubmoduleInitType").value(); - - CurrentSubmoduleInitType = StringToSubmoduleInitType(submoduleTypeString); - if(CurrentSubmoduleInitType == SubmoduleInitType::COUNT) - { - ssLOG_ERROR("GitSource: Invalid submodule init type " << submoduleTypeString); - return false; - } - } - - return true; -} - -std::string runcpp2::Data::GitSource::ToString(std::string indentation) const -{ - std::string out; - out += indentation + "Git:\n"; - out += indentation + " URL: " + GetEscapedYAMLString(URL) + "\n"; - if(!Branch.empty()) - out += indentation + " Branch: " + GetEscapedYAMLString(Branch) + "\n"; - out += indentation + " FullHistory: " + (FullHistory ? "true" : "false") + "\n"; - out += indentation + - " SubmoduleInitType: " + - SubmoduleInitTypeToString(CurrentSubmoduleInitType) + - "\n"; - return out; -} - -bool runcpp2::Data::GitSource::Equals(const GitSource& other) const -{ - return URL == other.URL && - Branch == other.Branch && - FullHistory == other.FullHistory && - CurrentSubmoduleInitType == other.CurrentSubmoduleInitType; -} diff --git a/Src/runcpp2/Data/GitSource.hpp b/Src/runcpp2/Data/GitSource.hpp new file mode 100644 index 0000000..aa50e0b --- /dev/null +++ b/Src/runcpp2/Data/GitSource.hpp @@ -0,0 +1,93 @@ +#ifndef RUNCPP2_DATA_GIT_SOURCE_HPP +#define RUNCPP2_DATA_GIT_SOURCE_HPP + +#include "runcpp2/Data/SubmoduleInitType.hpp" +#include "runcpp2/ParseUtil.hpp" +#include "runcpp2/LibYAML_Wrapper.hpp" + +#include "DSResult/DSResult.hpp" +#include "ssLogger/ssLog.hpp" + +#include +#include + +namespace runcpp2 +{ +namespace Data +{ + struct GitSource + { + std::string URL; + std::string Branch; + bool FullHistory = false; + SubmoduleInitType CurrentSubmoduleInitType = SubmoduleInitType::SHALLOW; + + inline bool ParseYAML_Node(YAML::ConstNodePtr node) + { + std::vector requirements = + { + NodeRequirement("URL", YAML::NodeType::Scalar, true, false), + NodeRequirement("Branch", YAML::NodeType::Scalar, false, false), + NodeRequirement("FullHistory", YAML::NodeType::Scalar, false, false), + NodeRequirement("SubmoduleInitType", YAML::NodeType::Scalar, false, false) + }; + + if(!CheckNodeRequirements(node, requirements)) + { + ssLOG_ERROR("GitSource: Failed to meet requirements"); + return false; + } + + URL = node->GetMapValueScalar("URL").value(); + + if(ExistAndHasChild(node, "Branch")) + { + Branch = node->GetMapValueScalar("Branch").DS_TRY_ACT(return false); + } + if(ExistAndHasChild(node, "FullHistory")) + { + FullHistory = node->GetMapValueScalar("FullHistory").DS_TRY_ACT(return false); + } + if(ExistAndHasChild(node, "SubmoduleInitType")) + { + std::string submoduleTypeString = + node->GetMapValueScalar("SubmoduleInitType").value(); + + CurrentSubmoduleInitType = StringToSubmoduleInitType(submoduleTypeString); + if(CurrentSubmoduleInitType == SubmoduleInitType::COUNT) + { + ssLOG_ERROR("GitSource: Invalid submodule init type " << submoduleTypeString); + return false; + } + } + + return true; + } + + inline std::string ToString(std::string indentation) const + { + std::string out; + out += indentation + "Git:\n"; + out += indentation + " URL: " + GetEscapedYAMLString(URL) + "\n"; + if(!Branch.empty()) + out += indentation + " Branch: " + GetEscapedYAMLString(Branch) + "\n"; + out += indentation + " FullHistory: " + (FullHistory ? "true" : "false") + "\n"; + out += indentation + + " SubmoduleInitType: " + + SubmoduleInitTypeToString(CurrentSubmoduleInitType) + + "\n"; + return out; + } + + inline bool Equals(const GitSource& other) const + { + return URL == other.URL && + Branch == other.Branch && + FullHistory == other.FullHistory && + CurrentSubmoduleInitType == other.CurrentSubmoduleInitType; + } + }; +} +} + +#endif diff --git a/Src/runcpp2/Data/LocalSource.cpp b/Src/runcpp2/Data/LocalSource.cpp deleted file mode 100644 index d1ae1f3..0000000 --- a/Src/runcpp2/Data/LocalSource.cpp +++ /dev/null @@ -1,99 +0,0 @@ -#include "runcpp2/Data/LocalSource.hpp" -#include "runcpp2/Data/ParseCommon.hpp" -#include "runcpp2/ParseUtil.hpp" -#include "ssLogger/ssLog.hpp" - -namespace -{ - static_assert( static_cast(runcpp2::Data::LocalCopyMode::Count) == 4, - "Update CopyModeToString when adding new LocalCopyMode values"); - - std::string CopyModeToString(runcpp2::Data::LocalCopyMode mode) - { - switch(mode) - { - case runcpp2::Data::LocalCopyMode::Auto: - return "Auto"; - case runcpp2::Data::LocalCopyMode::Symlink: - return "Symlink"; - case runcpp2::Data::LocalCopyMode::Hardlink: - return "Hardlink"; - case runcpp2::Data::LocalCopyMode::Copy: - return "Copy"; - default: - ssLOG_ERROR("Unknown LocalCopyMode value"); - return "Auto"; - } - } - - static_assert( static_cast(runcpp2::Data::LocalCopyMode::Count) == 4, - "Update StringToCopyMode when adding new LocalCopyMode values"); - - runcpp2::Data::LocalCopyMode StringToCopyMode(const std::string& str, bool& outSuccess) - { - if(str == "Auto") - { - outSuccess = true; - return runcpp2::Data::LocalCopyMode::Auto; - } - else if(str == "Symlink") - { - outSuccess = true; - return runcpp2::Data::LocalCopyMode::Symlink; - } - else if(str == "Hardlink") - { - outSuccess = true; - return runcpp2::Data::LocalCopyMode::Hardlink; - } - else if(str == "Copy") - { - outSuccess = true; - return runcpp2::Data::LocalCopyMode::Copy; - } - - ssLOG_ERROR("Invalid LocalCopyMode value: " << str); - outSuccess = false; - return runcpp2::Data::LocalCopyMode::Auto; - } -} - -bool runcpp2::Data::LocalSource::ParseYAML_Node(YAML::ConstNodePtr node) -{ - std::vector requirements = - { - NodeRequirement("Path", YAML::NodeType::Scalar, true, false) - }; - - if(!CheckNodeRequirements(node, requirements)) - { - ssLOG_ERROR("LocalSource: Failed to meet requirements"); - return false; - } - - Path = node->GetMapValueScalar("Path").DS_TRY_ACT(return false); - if(ExistAndHasChild(node, "CopyMode")) - { - bool success = false; - CopyMode = StringToCopyMode(node->GetMapValueScalar("CopyMode").DefaultOr(), - success); - if(!success) - return false; - } - - return true; -} - -std::string runcpp2::Data::LocalSource::ToString(std::string indentation) const -{ - std::string out; - out += indentation + "Local:\n"; - out += indentation + " Path: " + GetEscapedYAMLString(Path) + "\n"; - out += indentation + " CopyMode: " + CopyModeToString(CopyMode) + "\n"; - return out; -} - -bool runcpp2::Data::LocalSource::Equals(const LocalSource& other) const -{ - return Path == other.Path && CopyMode == other.CopyMode; -} diff --git a/Src/runcpp2/Data/LocalSource.hpp b/Src/runcpp2/Data/LocalSource.hpp new file mode 100644 index 0000000..595c7a5 --- /dev/null +++ b/Src/runcpp2/Data/LocalSource.hpp @@ -0,0 +1,144 @@ +#ifndef RUNCPP2_DATA_LOCAL_SOURCE_HPP +#define RUNCPP2_DATA_LOCAL_SOURCE_HPP + +#include "runcpp2/LibYAML_Wrapper.hpp" +#include "runcpp2/ParseUtil.hpp" + +#include "DSResult/DSResult.hpp" +#include "ssLogger/ssLog.hpp" + +#include +#include + +namespace runcpp2 +{ +namespace Data +{ + enum class LocalCopyMode; +} +} + +namespace +{ + std::string CopyModeToString(runcpp2::Data::LocalCopyMode mode); + runcpp2::Data::LocalCopyMode StringToCopyMode(const std::string& str, bool& outSuccess); +} + +namespace runcpp2 +{ +namespace Data +{ + enum class LocalCopyMode + { + Auto, + Symlink, + Hardlink, + Copy, + Count + }; + + struct LocalSource + { + std::string Path; + LocalCopyMode CopyMode = LocalCopyMode::Auto; + + inline bool ParseYAML_Node(YAML::ConstNodePtr node) + { + std::vector requirements = + { + NodeRequirement("Path", YAML::NodeType::Scalar, true, false) + }; + + if(!CheckNodeRequirements(node, requirements)) + { + ssLOG_ERROR("LocalSource: Failed to meet requirements"); + return false; + } + + Path = node->GetMapValueScalar("Path").DS_TRY_ACT(return false); + if(ExistAndHasChild(node, "CopyMode")) + { + bool success = false; + CopyMode = StringToCopyMode(node->GetMapValueScalar("CopyMode") + .DefaultOr(), + success); + if(!success) + return false; + } + + return true; + } + + inline std::string ToString(std::string indentation) const + { + std::string out; + out += indentation + "Local:\n"; + out += indentation + " Path: " + GetEscapedYAMLString(Path) + "\n"; + out += indentation + " CopyMode: " + CopyModeToString(CopyMode) + "\n"; + return out; + } + + inline bool Equals(const LocalSource& other) const + { + return Path == other.Path && CopyMode == other.CopyMode; + } + }; +} +} + +namespace +{ + static_assert( static_cast(runcpp2::Data::LocalCopyMode::Count) == 4, + "Update CopyModeToString when adding new LocalCopyMode values"); + + std::string CopyModeToString(runcpp2::Data::LocalCopyMode mode) + { + switch(mode) + { + case runcpp2::Data::LocalCopyMode::Auto: + return "Auto"; + case runcpp2::Data::LocalCopyMode::Symlink: + return "Symlink"; + case runcpp2::Data::LocalCopyMode::Hardlink: + return "Hardlink"; + case runcpp2::Data::LocalCopyMode::Copy: + return "Copy"; + default: + ssLOG_ERROR("Unknown LocalCopyMode value"); + return "Auto"; + } + } + + static_assert( static_cast(runcpp2::Data::LocalCopyMode::Count) == 4, + "Update StringToCopyMode when adding new LocalCopyMode values"); + + runcpp2::Data::LocalCopyMode StringToCopyMode(const std::string& str, bool& outSuccess) + { + if(str == "Auto") + { + outSuccess = true; + return runcpp2::Data::LocalCopyMode::Auto; + } + else if(str == "Symlink") + { + outSuccess = true; + return runcpp2::Data::LocalCopyMode::Symlink; + } + else if(str == "Hardlink") + { + outSuccess = true; + return runcpp2::Data::LocalCopyMode::Hardlink; + } + else if(str == "Copy") + { + outSuccess = true; + return runcpp2::Data::LocalCopyMode::Copy; + } + + ssLOG_ERROR("Invalid LocalCopyMode value: " << str); + outSuccess = false; + return runcpp2::Data::LocalCopyMode::Auto; + } +} + +#endif diff --git a/Include/runcpp2/Data/ParseCommon.hpp b/Src/runcpp2/Data/ParseCommon.hpp similarity index 93% rename from Include/runcpp2/Data/ParseCommon.hpp rename to Src/runcpp2/Data/ParseCommon.hpp index 98cba19..2db3472 100644 --- a/Include/runcpp2/Data/ParseCommon.hpp +++ b/Src/runcpp2/Data/ParseCommon.hpp @@ -1,6 +1,9 @@ #ifndef RUNCPP2_DATA_PARSE_COMMON_HPP #define RUNCPP2_DATA_PARSE_COMMON_HPP +#include "ssLogger/ssLog.hpp" + +#include #include using PlatformName = std::string;; diff --git a/Include/runcpp2/Data/PipelineResult.hpp b/Src/runcpp2/Data/PipelineResult.hpp similarity index 100% rename from Include/runcpp2/Data/PipelineResult.hpp rename to Src/runcpp2/Data/PipelineResult.hpp diff --git a/Src/runcpp2/Data/Profile.cpp b/Src/runcpp2/Data/Profile.cpp deleted file mode 100644 index ca222ff..0000000 --- a/Src/runcpp2/Data/Profile.cpp +++ /dev/null @@ -1,248 +0,0 @@ -#include "runcpp2/Data/Profile.hpp" -#include "runcpp2/Data/ParseCommon.hpp" -#include "runcpp2/ParseUtil.hpp" -#include "ssLogger/ssLog.hpp" - -void runcpp2::Data::Profile::GetNames(std::vector& outNames) const -{ - outNames.clear(); - outNames.push_back(Name); - for(const auto& alias : NameAliases) - outNames.push_back(alias); - - //Special name all that applies to all profile - outNames.push_back("DefaultProfile"); -} - -bool runcpp2::Data::Profile::ParseYAML_Node(YAML::ConstNodePtr profileNode) -{ - ssLOG_FUNC_DEBUG(); - - std::vector requirements = - { - NodeRequirement("Name", YAML::NodeType::Scalar, true, false), - NodeRequirement("NameAliases", YAML::NodeType::Sequence, false, true), - NodeRequirement("FileExtensions", YAML::NodeType::Sequence, true, false), - NodeRequirement("Languages", YAML::NodeType::Sequence, false, true), - NodeRequirement("Setup", YAML::NodeType::Map, false, true), - NodeRequirement("Cleanup", YAML::NodeType::Map, false, true), - NodeRequirement("FilesTypes", YAML::NodeType::Map, true, false), - NodeRequirement("Compiler", YAML::NodeType::Map, true, false), - NodeRequirement("Linker", YAML::NodeType::Map, true, false) - }; - - if(!CheckNodeRequirements(profileNode, requirements)) - { - ssLOG_ERROR("Compiler profile: Failed to meet requirements"); - return false; - } - - Name = profileNode->GetMapValueScalar("Name").DS_TRY_ACT(return false); - - if(ExistAndHasChild(profileNode, "NameAliases")) - { - YAML::ConstNodePtr nameAliasesNode = profileNode->GetMapValueNode("NameAliases"); - for(int i = 0; i < nameAliasesNode->GetChildrenCount(); ++i) - { - std::string nameAlias = nameAliasesNode ->GetSequenceChildScalar(i) - .DS_TRY_ACT(return false); - NameAliases.insert(nameAlias); - } - } - - { - YAML::ConstNodePtr fileExtensionsNode = profileNode->GetMapValueNode("FileExtensions"); - for(int i = 0; i < fileExtensionsNode->GetChildrenCount(); ++i) - { - std::string extension = fileExtensionsNode ->GetSequenceChildScalar(i) - .DS_TRY_ACT(return false); - FileExtensions.insert(extension); - } - } - - if(ExistAndHasChild(profileNode, "Languages")) - { - YAML::ConstNodePtr languagesNode = profileNode->GetMapValueNode("Languages"); - for(int i = 0; i < languagesNode->GetChildrenCount(); ++i) - { - std::string language = languagesNode->GetSequenceChildScalar(i) - .DS_TRY_ACT(return false); - Languages.insert(language); - } - } - - if(ExistAndHasChild(profileNode, "Setup")) - { - YAML::ConstNodePtr setupNode = profileNode->GetMapValueNode("Setup"); - for(int i = 0; i < setupNode->GetChildrenCount(); ++i) - { - YAML::ConstNodePtr currentPlatformNode = setupNode->GetMapValueNodeAt(i); - - std::string key = setupNode->GetMapKeyScalarAt(i).DS_TRY_ACT(return false); - std::vector setupSteps; - - for(int j = 0; j < currentPlatformNode->GetChildrenCount(); ++j) - { - std::string step = currentPlatformNode ->GetSequenceChildScalar(j) - .DS_TRY_ACT(return false); - setupSteps.push_back(step); - } - - Setup[key] = setupSteps; - } - } - - if(ExistAndHasChild(profileNode, "Cleanup")) - { - YAML::ConstNodePtr cleanupNode = profileNode->GetMapValueNode("Cleanup"); - for(int i = 0; i < cleanupNode->GetChildrenCount(); ++i) - { - YAML::ConstNodePtr currentPlatformNode = cleanupNode->GetMapValueNodeAt(i); - - std::string key = cleanupNode->GetMapKeyScalarAt(i).DS_TRY_ACT(return false); - std::vector cleanupSteps; - - for(int j = 0; j < currentPlatformNode->GetChildrenCount(); ++j) - { - std::string step = currentPlatformNode ->GetSequenceChildScalar(j) - .DS_TRY_ACT(return false); - cleanupSteps.push_back(step); - } - - Cleanup[key] = cleanupSteps; - } - } - - if(!FilesTypes.ParseYAML_Node(profileNode->GetMapValueNode("FilesTypes"))) - { - ssLOG_ERROR("Profile: FilesTypes is invalid"); - return false; - } - - ssLOG_DEBUG("Parsing Compiler"); - if(!Compiler.ParseYAML_Node(profileNode->GetMapValueNode("Compiler"), "CompileTypes")) - { - ssLOG_ERROR("Profile: Compiler is invalid"); - return false; - } - - ssLOG_DEBUG("Parsing Linker"); - if(!Linker.ParseYAML_Node(profileNode->GetMapValueNode("Linker"), "LinkTypes")) - { - ssLOG_ERROR("Profile: Linker is invalid"); - return false; - } - - return true; -} - -std::string runcpp2::Data::Profile::ToString(std::string indentation) const -{ - std::string out; - - out += indentation + "Name: " + GetEscapedYAMLString(Name) + "\n"; - - if(!NameAliases.empty()) - { - out += indentation + "NameAliases:\n"; - for(auto it = NameAliases.begin(); it != NameAliases.end(); ++it) - out += indentation + "- " + GetEscapedYAMLString(*it) + "\n"; - } - - if(FileExtensions.empty()) - out += indentation + "FileExtensions: []\n"; - else - { - out += indentation + "FileExtensions:\n"; - for(auto it = FileExtensions.begin(); it != FileExtensions.end(); ++it) - out += indentation + "- " + GetEscapedYAMLString(*it) + "\n"; - } - - if(!Languages.empty()) - { - out += indentation + "Languages:\n"; - for(auto it = Languages.begin(); it != Languages.end(); ++it) - out += indentation + "- " + GetEscapedYAMLString(*it) + "\n"; - } - - if(!Setup.empty()) - { - out += indentation + "Setup:\n"; - for(auto it = Setup.begin(); it != Setup.end(); ++it) - { - out += indentation + " " + it->first + ":\n"; - for(int i = 0; i < it->second.size(); ++i) - out += indentation + " - " + GetEscapedYAMLString(it->second[i]) + "\n"; - } - } - - if(!Cleanup.empty()) - { - out += indentation + "Cleanup:\n"; - for(auto it = Cleanup.begin(); it != Cleanup.end(); ++it) - { - out += indentation + " " + it->first + ":\n"; - for(int i = 0; i < it->second.size(); ++i) - out += indentation + " - " + GetEscapedYAMLString(it->second[i]) + "\n"; - } - } - - out += indentation + "FilesTypes:\n"; - out += FilesTypes.ToString(indentation + " "); - - out += indentation + "Compiler:\n"; - out += Compiler.ToString(indentation + " ", "CompileTypes"); - - out += indentation + "Linker:\n"; - out += Linker.ToString(indentation + " ", "LinkTypes"); - - return out; -} - -bool runcpp2::Data::Profile::Equals(const Profile& other) const -{ - if( Name != other.Name || - NameAliases.size() != other.NameAliases.size() || - FileExtensions.size() != other.FileExtensions.size() || - Languages.size() != other.Languages.size() || - Setup.size() != other.Setup.size() || - Cleanup.size() != other.Cleanup.size() || - !FilesTypes.Equals(other.FilesTypes) || - !Compiler.Equals(other.Compiler) || - !Linker.Equals(other.Linker)) - { - return false; - } - - for(const std::string& it : NameAliases) - { - if(other.NameAliases.count(it) == 0) - return false; - } - - for(const std::string& it : FileExtensions) - { - if(other.FileExtensions.count(it) == 0) - return false; - } - - for(const std::string& it : Languages) - { - if(other.Languages.count(it) == 0) - return false; - } - - for(const auto& it : Setup) - { - if(other.Setup.count(it.first) == 0 || other.Setup.at(it.first) != it.second) - return false; - } - - for(const auto& it : Cleanup) - { - if(other.Cleanup.count(it.first) == 0 || other.Cleanup.at(it.first) != it.second) - return false; - } - - return true; -} diff --git a/Src/runcpp2/Data/Profile.hpp b/Src/runcpp2/Data/Profile.hpp new file mode 100644 index 0000000..32539e6 --- /dev/null +++ b/Src/runcpp2/Data/Profile.hpp @@ -0,0 +1,325 @@ +#ifndef RUNCPP2_DATA_PROFILE_HPP +#define RUNCPP2_DATA_PROFILE_HPP + +#include "runcpp2/Data/ParseCommon.hpp" +#include "runcpp2/Data/FilesTypesInfo.hpp" +#include "runcpp2/Data/StageInfo.hpp" +#include "runcpp2/ParseUtil.hpp" +#include "runcpp2/LibYAML_Wrapper.hpp" + +#include "DSResult/DSResult.hpp" + +#include "ssLogger/ssLog.hpp" + +#include +#include +#include +#include +#include + +namespace runcpp2 +{ +namespace Data +{ + struct Profile + { + std::string Name; + + std::unordered_set NameAliases; + std::unordered_set FileExtensions; + std::unordered_set Languages; + std::unordered_map> Setup; + std::unordered_map> Cleanup; + FilesTypesInfo FilesTypes; + + StageInfo Compiler; + StageInfo Linker; + + inline void GetNames(std::vector& outNames) const + { + outNames.clear(); + outNames.push_back(Name); + for(const auto& alias : NameAliases) + outNames.push_back(alias); + + //Special name all that applies to all profile + outNames.push_back("DefaultProfile"); + } + + inline bool ParseYAML_Node(YAML::ConstNodePtr profileNode) + { + ssLOG_FUNC_DEBUG(); + + std::vector requirements = + { + NodeRequirement("Name", YAML::NodeType::Scalar, true, false), + NodeRequirement("NameAliases", YAML::NodeType::Sequence, false, true), + NodeRequirement("FileExtensions", YAML::NodeType::Sequence, true, false), + NodeRequirement("Languages", YAML::NodeType::Sequence, false, true), + NodeRequirement("Setup", YAML::NodeType::Map, false, true), + NodeRequirement("Cleanup", YAML::NodeType::Map, false, true), + NodeRequirement("FilesTypes", YAML::NodeType::Map, true, false), + NodeRequirement("Compiler", YAML::NodeType::Map, true, false), + NodeRequirement("Linker", YAML::NodeType::Map, true, false) + }; + + if(!CheckNodeRequirements(profileNode, requirements)) + { + ssLOG_ERROR("Compiler profile: Failed to meet requirements"); + return false; + } + + Name = profileNode->GetMapValueScalar("Name").DS_TRY_ACT(return false); + + if(ExistAndHasChild(profileNode, "NameAliases")) + { + YAML::ConstNodePtr nameAliasesNode = profileNode->GetMapValueNode("NameAliases"); + for(int i = 0; i < nameAliasesNode->GetChildrenCount(); ++i) + { + std::string nameAlias = nameAliasesNode ->GetSequenceChildScalar(i) + .DS_TRY_ACT(return false); + NameAliases.insert(nameAlias); + } + } + + { + YAML::ConstNodePtr fileExtensionsNode = profileNode->GetMapValueNode("FileExtensions"); + for(int i = 0; i < fileExtensionsNode->GetChildrenCount(); ++i) + { + std::string extension = + fileExtensionsNode ->GetSequenceChildScalar(i) + .DS_TRY_ACT(return false); + FileExtensions.insert(extension); + } + } + + if(ExistAndHasChild(profileNode, "Languages")) + { + YAML::ConstNodePtr languagesNode = profileNode->GetMapValueNode("Languages"); + for(int i = 0; i < languagesNode->GetChildrenCount(); ++i) + { + std::string language = languagesNode->GetSequenceChildScalar(i) + .DS_TRY_ACT(return false); + Languages.insert(language); + } + } + + if(ExistAndHasChild(profileNode, "Setup")) + { + YAML::ConstNodePtr setupNode = profileNode->GetMapValueNode("Setup"); + for(int i = 0; i < setupNode->GetChildrenCount(); ++i) + { + YAML::ConstNodePtr currentPlatformNode = setupNode->GetMapValueNodeAt(i); + + std::string key = setupNode ->GetMapKeyScalarAt(i) + .DS_TRY_ACT(return false); + std::vector setupSteps; + + for(int j = 0; j < currentPlatformNode->GetChildrenCount(); ++j) + { + std::string step = + currentPlatformNode ->GetSequenceChildScalar(j) + .DS_TRY_ACT(return false); + setupSteps.push_back(step); + } + + Setup[key] = setupSteps; + } + } + + if(ExistAndHasChild(profileNode, "Cleanup")) + { + YAML::ConstNodePtr cleanupNode = profileNode->GetMapValueNode("Cleanup"); + for(int i = 0; i < cleanupNode->GetChildrenCount(); ++i) + { + YAML::ConstNodePtr currentPlatformNode = cleanupNode->GetMapValueNodeAt(i); + + std::string key = cleanupNode ->GetMapKeyScalarAt(i) + .DS_TRY_ACT(return false); + std::vector cleanupSteps; + + for(int j = 0; j < currentPlatformNode->GetChildrenCount(); ++j) + { + std::string step = + currentPlatformNode ->GetSequenceChildScalar(j) + .DS_TRY_ACT(return false); + cleanupSteps.push_back(step); + } + + Cleanup[key] = cleanupSteps; + } + } + + if(!FilesTypes.ParseYAML_Node(profileNode->GetMapValueNode("FilesTypes"))) + { + ssLOG_ERROR("Profile: FilesTypes is invalid"); + return false; + } + + ssLOG_DEBUG("Parsing Compiler"); + if(!Compiler.ParseYAML_Node(profileNode->GetMapValueNode("Compiler"), "CompileTypes")) + { + ssLOG_ERROR("Profile: Compiler is invalid"); + return false; + } + + ssLOG_DEBUG("Parsing Linker"); + if(!Linker.ParseYAML_Node(profileNode->GetMapValueNode("Linker"), "LinkTypes")) + { + ssLOG_ERROR("Profile: Linker is invalid"); + return false; + } + + return true; + } + + inline std::string ToString(std::string indentation) const + { + std::string out; + + out += indentation + "Name: " + GetEscapedYAMLString(Name) + "\n"; + + if(!NameAliases.empty()) + { + out += indentation + "NameAliases:\n"; + for(auto it = NameAliases.begin(); it != NameAliases.end(); ++it) + out += indentation + "- " + GetEscapedYAMLString(*it) + "\n"; + } + + if(FileExtensions.empty()) + out += indentation + "FileExtensions: []\n"; + else + { + out += indentation + "FileExtensions:\n"; + for(auto it = FileExtensions.begin(); it != FileExtensions.end(); ++it) + out += indentation + "- " + GetEscapedYAMLString(*it) + "\n"; + } + + if(!Languages.empty()) + { + out += indentation + "Languages:\n"; + for(auto it = Languages.begin(); it != Languages.end(); ++it) + out += indentation + "- " + GetEscapedYAMLString(*it) + "\n"; + } + + if(!Setup.empty()) + { + out += indentation + "Setup:\n"; + for(auto it = Setup.begin(); it != Setup.end(); ++it) + { + out += indentation + " " + it->first + ":\n"; + for(int i = 0; i < it->second.size(); ++i) + out += indentation + " - " + GetEscapedYAMLString(it->second[i]) + "\n"; + } + } + + if(!Cleanup.empty()) + { + out += indentation + "Cleanup:\n"; + for(auto it = Cleanup.begin(); it != Cleanup.end(); ++it) + { + out += indentation + " " + it->first + ":\n"; + for(int i = 0; i < it->second.size(); ++i) + out += indentation + " - " + GetEscapedYAMLString(it->second[i]) + "\n"; + } + } + + out += indentation + "FilesTypes:\n"; + out += FilesTypes.ToString(indentation + " "); + + out += indentation + "Compiler:\n"; + out += Compiler.ToString(indentation + " ", "CompileTypes"); + + out += indentation + "Linker:\n"; + out += Linker.ToString(indentation + " ", "LinkTypes"); + + return out; + } + + inline bool Equals(const Profile& other) const + { + if( Name != other.Name || + NameAliases.size() != other.NameAliases.size() || + FileExtensions.size() != other.FileExtensions.size() || + Languages.size() != other.Languages.size() || + Setup.size() != other.Setup.size() || + Cleanup.size() != other.Cleanup.size() || + !FilesTypes.Equals(other.FilesTypes) || + !Compiler.Equals(other.Compiler) || + !Linker.Equals(other.Linker)) + { + return false; + } + + for(const std::string& it : NameAliases) + { + if(other.NameAliases.count(it) == 0) + return false; + } + + for(const std::string& it : FileExtensions) + { + if(other.FileExtensions.count(it) == 0) + return false; + } + + for(const std::string& it : Languages) + { + if(other.Languages.count(it) == 0) + return false; + } + + for(const auto& it : Setup) + { + if(other.Setup.count(it.first) == 0 || other.Setup.at(it.first) != it.second) + return false; + } + + for(const auto& it : Cleanup) + { + if(other.Cleanup.count(it.first) == 0 || other.Cleanup.at(it.first) != it.second) + return false; + } + + return true; + } + }; +} +} + +namespace runcpp2 +{ + template + inline bool HasValueFromProfileMap( const Data::Profile& profile, + const std::unordered_map& map) + { + std::vector profileNames; + profile.GetNames(profileNames); + + for(int i = 0; i < profileNames.size(); ++i) + { + if(map.find(profileNames.at(i)) != map.end()) + return true; + } + return false; + } + + template + inline const T* GetValueFromProfileMap( const Data::Profile& profile, + const std::unordered_map& map) + { + std::vector profileNames; + profile.GetNames(profileNames); + + for(int i = 0; i < profileNames.size(); ++i) + { + auto it = map.find(profileNames.at(i)); + if(it != map.end()) + return &it->second; + } + return nullptr; + } +} + + +#endif diff --git a/Src/runcpp2/Data/ProfilesCommands.cpp b/Src/runcpp2/Data/ProfilesCommands.cpp deleted file mode 100644 index d5957b6..0000000 --- a/Src/runcpp2/Data/ProfilesCommands.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "runcpp2/Data/ProfilesCommands.hpp" -#include "runcpp2/ParseUtil.hpp" -#include "runcpp2/Data/ParseCommon.hpp" -#include "ssLogger/ssLog.hpp" - -bool runcpp2::Data::ProfilesCommands::ParseYAML_Node(YAML::ConstNodePtr node) -{ - ssLOG_FUNC_DEBUG(); - - if(!node->IsMap()) - { - ssLOG_ERROR("ProfilesCommands: Node is not a Map"); - return false; - } - - for(int i = 0; i < node->GetChildrenCount(); ++i) - { - ProfileName profile = node->GetMapKeyScalarAt(i).DS_TRY_ACT(return false); - if(!ParseYAML_NodeWithProfile(node->GetMapValueNodeAt(i), profile)) - return false; - } - - return true; -} - -bool runcpp2::Data::ProfilesCommands::ParseYAML_NodeWithProfile(YAML::ConstNodePtr node, - ProfileName profile) -{ - ssLOG_FUNC_DEBUG(); - - if(!node->IsSequence()) - { - ssLOG_ERROR("ProfilesDefines: Paths type requires a list"); - return false; - } - - for(int i = 0; i < node->GetChildrenCount(); ++i) - { - std::string command = node->GetSequenceChildScalar(i).DS_TRY_ACT(return false); - CommandSteps[profile].push_back(command); - } - - return true; -} - -bool -runcpp2::Data::ProfilesCommands::IsYAML_NodeParsableAsDefault(YAML::ConstNodePtr node) const -{ - return node->IsSequence(); -} - -std::string runcpp2::Data::ProfilesCommands::ToString(std::string indentation) const -{ - std::string out; - - if(CommandSteps.empty()) - return out; - - for(auto it = CommandSteps.begin(); it != CommandSteps.end(); it++) - { - if(it->second.empty()) - out += indentation + it->first + ": []\n"; - else - { - out += indentation + it->first + ":\n"; - for(int i = 0; i < it->second.size(); i++) - out += indentation + "- " + GetEscapedYAMLString(it->second[i]) + "\n"; - } - } - - return out; -} - -bool runcpp2::Data::ProfilesCommands::Equals(const ProfilesCommands& other) const -{ - if(CommandSteps.size() != other.CommandSteps.size()) - return false; - - for(const auto& it : CommandSteps) - { - if(other.CommandSteps.count(it.first) == 0 || other.CommandSteps.at(it.first) != it.second) - return false; - } - - return true; -} diff --git a/Src/runcpp2/Data/ProfilesCommands.hpp b/Src/runcpp2/Data/ProfilesCommands.hpp new file mode 100644 index 0000000..27886f2 --- /dev/null +++ b/Src/runcpp2/Data/ProfilesCommands.hpp @@ -0,0 +1,112 @@ +#ifndef RUNCPP2_DATA_PROFILES_COMMANDS_HPP +#define RUNCPP2_DATA_PROFILES_COMMANDS_HPP + +#include "runcpp2/Data/ParseCommon.hpp" +#include "runcpp2/LibYAML_Wrapper.hpp" +#include "runcpp2/ParseUtil.hpp" + +#include "ssLogger/ssLog.hpp" +#include "DSResult/DSResult.hpp" + +#include +#include +#include +#include + +namespace runcpp2 +{ +namespace Data +{ + struct ProfilesCommands + { + //TODO: Allow specifying command can fail + std::unordered_map> CommandSteps; + + inline bool ParseYAML_Node(YAML::ConstNodePtr node) + { + ssLOG_FUNC_DEBUG(); + + if(!node->IsMap()) + { + ssLOG_ERROR("ProfilesCommands: Node is not a Map"); + return false; + } + + for(int i = 0; i < node->GetChildrenCount(); ++i) + { + ProfileName profile = node->GetMapKeyScalarAt(i).DS_TRY_ACT(return false); + if(!ParseYAML_NodeWithProfile(node->GetMapValueNodeAt(i), profile)) + return false; + } + + return true; + } + + inline bool ParseYAML_NodeWithProfile(YAML::ConstNodePtr node, ProfileName profile) + { + ssLOG_FUNC_DEBUG(); + + if(!node->IsSequence()) + { + ssLOG_ERROR("ProfilesDefines: Paths type requires a list"); + return false; + } + + for(int i = 0; i < node->GetChildrenCount(); ++i) + { + std::string command = node ->GetSequenceChildScalar(i) + .DS_TRY_ACT(return false); + CommandSteps[profile].push_back(command); + } + + return true; + } + + inline bool IsYAML_NodeParsableAsDefault(YAML::ConstNodePtr node) const + { + return node->IsSequence(); + } + + inline std::string ToString(std::string indentation) const + { + std::string out; + + if(CommandSteps.empty()) + return out; + + for(auto it = CommandSteps.begin(); it != CommandSteps.end(); it++) + { + if(it->second.empty()) + out += indentation + it->first + ": []\n"; + else + { + out += indentation + it->first + ":\n"; + for(int i = 0; i < it->second.size(); i++) + out += indentation + "- " + GetEscapedYAMLString(it->second[i]) + "\n"; + } + } + + return out; + } + + inline bool Equals(const ProfilesCommands& other) const + { + if(CommandSteps.size() != other.CommandSteps.size()) + return false; + + for(const auto& it : CommandSteps) + { + if( other.CommandSteps.count(it.first) == 0 || + other.CommandSteps.at(it.first) != it.second) + { + return false; + } + } + + return true; + } + }; +} +} + +#endif diff --git a/Src/runcpp2/Data/ProfilesDefines.cpp b/Src/runcpp2/Data/ProfilesDefines.cpp deleted file mode 100644 index 81f6b37..0000000 --- a/Src/runcpp2/Data/ProfilesDefines.cpp +++ /dev/null @@ -1,129 +0,0 @@ -#include "runcpp2/Data/ProfilesDefines.hpp" -#include "runcpp2/ParseUtil.hpp" -#include "ssLogger/ssLog.hpp" -#include - -bool runcpp2::Data::ProfilesDefines::ParseYAML_Node(YAML::ConstNodePtr node) -{ - ssLOG_FUNC_DEBUG(); - if(!node->IsMap()) - { - ssLOG_ERROR("ProfilesDefines: Not a map type"); - return false; - } - - for(int i = 0; i < node->GetChildrenCount(); ++i) - { - DS_UNWRAP_DECL_ACT( ProfileName profile, - node->GetMapKeyScalarAt(i), - ssLOG_ERROR(DS_TMP_ERROR.ToString()); return false); - if(!ParseYAML_NodeWithProfile(node->GetMapValueNodeAt(i), profile)) - return false; - } - - return true; -} - -bool runcpp2::Data::ProfilesDefines::ParseYAML_NodeWithProfile( YAML::ConstNodePtr node, - ProfileName profile) -{ - ssLOG_FUNC_DEBUG(); - - if(!node->IsSequence()) - { - ssLOG_ERROR("ProfilesDefines: Paths type requires a list"); - return false; - } - - for(int i = 0; i < node->GetChildrenCount(); ++i) - { - DS_UNWRAP_DECL_ACT( std::string defineStr, - node->GetSequenceChildScalar(i), - ssLOG_ERROR(DS_TMP_ERROR.ToString()); return false); - Define define; - size_t equalPos = defineStr.find('='); - if(equalPos != std::string::npos) - { - define.Name = defineStr.substr(0, equalPos); - define.Value = defineStr.substr(equalPos + 1); - define.HasValue = true; - } - else - { - define.Name = defineStr; - define.Value = ""; - define.HasValue = false; - } - - Defines[profile].push_back(define); - } - - return true; -} - -bool -runcpp2::Data::ProfilesDefines::IsYAML_NodeParsableAsDefault(YAML::ConstNodePtr node) const -{ - ssLOG_FUNC_DEBUG(); - return node->IsSequence(); -} - -std::string runcpp2::Data::ProfilesDefines::ToString(std::string indentation) const -{ - std::string result; - - if(Defines.empty()) - return result; - - for(auto it = Defines.begin(); it != Defines.end(); ++it) - { - if(it->second.empty()) - result += indentation + it->first + ": []\n"; - else - { - result += indentation + it->first + ":\n"; - for(int j = 0; j < it->second.size(); ++j) - { - const Define& define = it->second.at(j); - result += indentation + "- "; - if(define.Value.empty()) - result += GetEscapedYAMLString(define.Name) + "\n"; - else - result += GetEscapedYAMLString(define.Name + "=" + define.Value) + "\n"; - } - } - } - - return result; -} - -bool runcpp2::Data::ProfilesDefines::Equals(const ProfilesDefines& other) const -{ - if(Defines.size() != other.Defines.size()) - return false; - - for(const auto& it : Defines) - { - if(other.Defines.count(it.first) == 0) - return false; - - const std::vector& otherDefines = other.Defines.at(it.first); - if(it.second.size() != otherDefines.size()) - return false; - - for(size_t i = 0; i < it.second.size(); ++i) - { - const Define& define = it.second[i]; - const Define& otherDefine = otherDefines[i]; - - if( define.Name != otherDefine.Name || - define.Value != otherDefine.Value || - define.HasValue != otherDefine.HasValue) - { - return false; - } - } - } - - return true; -} diff --git a/Src/runcpp2/Data/ProfilesDefines.hpp b/Src/runcpp2/Data/ProfilesDefines.hpp new file mode 100644 index 0000000..92cc510 --- /dev/null +++ b/Src/runcpp2/Data/ProfilesDefines.hpp @@ -0,0 +1,158 @@ +#ifndef RUNCPP2_DATA_PROFILES_DEFINES_HPP +#define RUNCPP2_DATA_PROFILES_DEFINES_HPP + +#include "runcpp2/Data/ParseCommon.hpp" +#include "runcpp2/LibYAML_Wrapper.hpp" +#include "runcpp2/ParseUtil.hpp" + +#include "ssLogger/ssLog.hpp" +#include "DSResult/DSResult.hpp" + +#include +#include +#include +#include +#include + +namespace runcpp2 +{ +namespace Data +{ + struct Define + { + std::string Name; + std::string Value; + bool HasValue; + }; + + struct ProfilesDefines + { + std::unordered_map> Defines; + + inline bool ParseYAML_Node(YAML::ConstNodePtr node) + { + ssLOG_FUNC_DEBUG(); + if(!node->IsMap()) + { + ssLOG_ERROR("ProfilesDefines: Not a map type"); + return false; + } + + for(int i = 0; i < node->GetChildrenCount(); ++i) + { + DS_UNWRAP_DECL_ACT( ProfileName profile, + node->GetMapKeyScalarAt(i), + ssLOG_ERROR(DS_TMP_ERROR.ToString()); return false); + if(!ParseYAML_NodeWithProfile(node->GetMapValueNodeAt(i), profile)) + return false; + } + + return true; + } + + inline bool ParseYAML_NodeWithProfile(YAML::ConstNodePtr node, ProfileName profile) + { + ssLOG_FUNC_DEBUG(); + + if(!node->IsSequence()) + { + ssLOG_ERROR("ProfilesDefines: Paths type requires a list"); + return false; + } + + for(int i = 0; i < node->GetChildrenCount(); ++i) + { + DS_UNWRAP_DECL_ACT( std::string defineStr, + node->GetSequenceChildScalar(i), + ssLOG_ERROR(DS_TMP_ERROR.ToString()); return false); + Define define; + size_t equalPos = defineStr.find('='); + if(equalPos != std::string::npos) + { + define.Name = defineStr.substr(0, equalPos); + define.Value = defineStr.substr(equalPos + 1); + define.HasValue = true; + } + else + { + define.Name = defineStr; + define.Value = ""; + define.HasValue = false; + } + + Defines[profile].push_back(define); + } + + return true; + } + + inline bool IsYAML_NodeParsableAsDefault(YAML::ConstNodePtr node) const + { + ssLOG_FUNC_DEBUG(); + return node->IsSequence(); + } + + inline std::string ToString(std::string indentation) const + { + std::string result; + + if(Defines.empty()) + return result; + + for(auto it = Defines.begin(); it != Defines.end(); ++it) + { + if(it->second.empty()) + result += indentation + it->first + ": []\n"; + else + { + result += indentation + it->first + ":\n"; + for(int j = 0; j < it->second.size(); ++j) + { + const Define& define = it->second.at(j); + result += indentation + "- "; + if(define.Value.empty()) + result += GetEscapedYAMLString(define.Name) + "\n"; + else + result += GetEscapedYAMLString(define.Name + "=" + define.Value) + "\n"; + } + } + } + + return result; + } + + inline bool Equals(const ProfilesDefines& other) const + { + if(Defines.size() != other.Defines.size()) + return false; + + for(const auto& it : Defines) + { + if(other.Defines.count(it.first) == 0) + return false; + + const std::vector& otherDefines = other.Defines.at(it.first); + if(it.second.size() != otherDefines.size()) + return false; + + for(size_t i = 0; i < it.second.size(); ++i) + { + const Define& define = it.second[i]; + const Define& otherDefine = otherDefines[i]; + + if( define.Name != otherDefine.Name || + define.Value != otherDefine.Value || + define.HasValue != otherDefine.HasValue) + { + return false; + } + } + } + + return true; + } + }; +} +} + +#endif diff --git a/Src/runcpp2/Data/ProfilesFlagsOverride.cpp b/Src/runcpp2/Data/ProfilesFlagsOverride.cpp deleted file mode 100644 index 698881b..0000000 --- a/Src/runcpp2/Data/ProfilesFlagsOverride.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include "runcpp2/Data/ProfilesFlagsOverride.hpp" -#include "runcpp2/ParseUtil.hpp" -#include "ssLogger/ssLog.hpp" - -bool runcpp2::Data::ProfilesFlagsOverride::ParseYAML_Node(YAML::ConstNodePtr node) -{ - ssLOG_FUNC_DEBUG(); - - if(!node->IsMap()) - { - ssLOG_ERROR("ProfilesFlagsOverrides: Not a map type"); - return false; - } - - for(int i = 0; i < node->GetChildrenCount(); ++i) - { - ProfileName profile = node->GetMapKeyScalarAt(i).DS_TRY_ACT(return false); - if(!ParseYAML_NodeWithProfile(node->GetMapValueNodeAt(i), profile)) - return false; - } - - return true; -} - -bool -runcpp2::Data::ProfilesFlagsOverride::ParseYAML_NodeWithProfile(YAML::ConstNodePtr node, - ProfileName profile) -{ - ssLOG_FUNC_DEBUG(); - - if(!node->IsMap()) - { - ssLOG_ERROR("ProfilesFlagsOverrides: FlagsOverrideInfo type requires a map"); - return false; - } - - FlagsOverrideInfo flags; - YAML::ConstNodePtr flagsOverrideNode = node; - - if(!flags.ParseYAML_Node(flagsOverrideNode)) - { - ssLOG_ERROR("ProfilesFlagsOverrides: Unable to parse FlagsOverride."); - return false; - } - - FlagsOverrides[profile] = flags; - - return true; -} - -bool -runcpp2::Data::ProfilesFlagsOverride::IsYAML_NodeParsableAsDefault(YAML::ConstNodePtr node) const -{ - ssLOG_FUNC_DEBUG(); - FlagsOverrideInfo flags; - return flags.IsYAML_NodeParsableAsDefault(node); -} - -std::string runcpp2::Data::ProfilesFlagsOverride::ToString(std::string indentation) const -{ - std::string out; - - if(FlagsOverrides.empty()) - return out; - - for(auto it = FlagsOverrides.begin(); it != FlagsOverrides.end(); ++it) - { - out += indentation + it->first + ":\n"; - out += it->second.ToString(indentation + " "); - } - - return out; -} - -bool runcpp2::Data::ProfilesFlagsOverride::Equals(const ProfilesFlagsOverride& other) const -{ - if(FlagsOverrides.size() != other.FlagsOverrides.size()) - return false; - - for(const auto& it : FlagsOverrides) - { - if( other.FlagsOverrides.count(it.first) == 0 || - !other.FlagsOverrides.at(it.first).Equals(it.second)) - { - return false; - } - } - - return true; -} diff --git a/Src/runcpp2/Data/ProfilesFlagsOverride.hpp b/Src/runcpp2/Data/ProfilesFlagsOverride.hpp new file mode 100644 index 0000000..1ba8878 --- /dev/null +++ b/Src/runcpp2/Data/ProfilesFlagsOverride.hpp @@ -0,0 +1,110 @@ +#ifndef RUNCPP2_DATA_PROFILES_FLAGS_OVERRIDE_HPP +#define RUNCPP2_DATA_PROFILES_FLAGS_OVERRIDE_HPP + +#include "FlagsOverrideInfo.hpp" +#include "runcpp2/Data/ParseCommon.hpp" +#include "runcpp2/LibYAML_Wrapper.hpp" + +#include "ssLogger/ssLog.hpp" +#include "DSResult/DSResult.hpp" + +#include +#include +#include + +namespace runcpp2 +{ +namespace Data +{ + struct ProfilesFlagsOverride + { + std::unordered_map FlagsOverrides; + + inline bool ParseYAML_Node(YAML::ConstNodePtr node) + { + ssLOG_FUNC_DEBUG(); + + if(!node->IsMap()) + { + ssLOG_ERROR("ProfilesFlagsOverrides: Not a map type"); + return false; + } + + for(int i = 0; i < node->GetChildrenCount(); ++i) + { + ProfileName profile = node->GetMapKeyScalarAt(i).DS_TRY_ACT(return false); + if(!ParseYAML_NodeWithProfile(node->GetMapValueNodeAt(i), profile)) + return false; + } + + return true; + } + + inline bool ParseYAML_NodeWithProfile(YAML::ConstNodePtr node, ProfileName profile) + { + ssLOG_FUNC_DEBUG(); + + if(!node->IsMap()) + { + ssLOG_ERROR("ProfilesFlagsOverrides: FlagsOverrideInfo type requires a map"); + return false; + } + + FlagsOverrideInfo flags; + YAML::ConstNodePtr flagsOverrideNode = node; + + if(!flags.ParseYAML_Node(flagsOverrideNode)) + { + ssLOG_ERROR("ProfilesFlagsOverrides: Unable to parse FlagsOverride."); + return false; + } + + FlagsOverrides[profile] = flags; + + return true; + } + + inline bool IsYAML_NodeParsableAsDefault(YAML::ConstNodePtr node) const + { + ssLOG_FUNC_DEBUG(); + FlagsOverrideInfo flags; + return flags.IsYAML_NodeParsableAsDefault(node); + } + + inline std::string ToString(std::string indentation) const + { + std::string out; + + if(FlagsOverrides.empty()) + return out; + + for(auto it = FlagsOverrides.begin(); it != FlagsOverrides.end(); ++it) + { + out += indentation + it->first + ":\n"; + out += it->second.ToString(indentation + " "); + } + + return out; + } + + inline bool Equals(const ProfilesFlagsOverride& other) const + { + if(FlagsOverrides.size() != other.FlagsOverrides.size()) + return false; + + for(const auto& it : FlagsOverrides) + { + if( other.FlagsOverrides.count(it.first) == 0 || + !other.FlagsOverrides.at(it.first).Equals(it.second)) + { + return false; + } + } + + return true; + } + }; +} +} + +#endif diff --git a/Src/runcpp2/Data/ProfilesProcessPaths.cpp b/Src/runcpp2/Data/ProfilesProcessPaths.cpp deleted file mode 100644 index dfca0e2..0000000 --- a/Src/runcpp2/Data/ProfilesProcessPaths.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "runcpp2/Data/ProfilesProcessPaths.hpp" -#include "runcpp2/ParseUtil.hpp" -#include "ssLogger/ssLog.hpp" - -bool runcpp2::Data::ProfilesProcessPaths::ParseYAML_Node(YAML::ConstNodePtr node) -{ - ssLOG_FUNC_DEBUG(); - - if(!node->IsMap()) - { - ssLOG_ERROR("ProfilesProcessPaths: Not a map type"); - return false; - } - - for(int i = 0; i < node->GetChildrenCount(); ++i) - { - YAML::ConstNodePtr currentProfilePathsNode = node->GetMapValueNodeAt(i); - ProfileName profile = node->GetMapKeyScalarAt(i).DS_TRY_ACT(return false); - if(!ParseYAML_NodeWithProfile(currentProfilePathsNode, profile)) - return false; - } - - return true; -} - -bool runcpp2::Data::ProfilesProcessPaths::ParseYAML_NodeWithProfile(YAML::ConstNodePtr node, - ProfileName profile) -{ - ssLOG_FUNC_DEBUG(); - - if(!node->IsSequence()) - { - ssLOG_ERROR("ProfilesProcessPaths: Paths type requires a list"); - return false; - } - - for(int i = 0; i < node->GetChildrenCount(); ++i) - { - std::string path = node->GetSequenceChildScalar(i).DS_TRY_ACT(return false); - Paths[profile].push_back(path); - } - - return true; -} - -bool -runcpp2::Data::ProfilesProcessPaths::IsYAML_NodeParsableAsDefault(YAML::ConstNodePtr node) const -{ - ssLOG_FUNC_DEBUG(); - return node->IsSequence(); -} - -std::string runcpp2::Data::ProfilesProcessPaths::ToString(std::string indentation) const -{ - std::string out; - - if(Paths.empty()) - return out; - - for(auto it = Paths.begin(); it != Paths.end(); ++it) - { - if(it->second.empty()) - out += indentation + it->first + ": []\n"; - else - { - out += indentation + it->first + ":\n"; - for(int i = 0; i < it->second.size(); ++i) - out += indentation + "- " + GetEscapedYAMLString(it->second.at(i).string()) + "\n"; - } - } - - return out; -} - -bool runcpp2::Data::ProfilesProcessPaths::Equals(const ProfilesProcessPaths& other) const -{ - if(Paths.size() != other.Paths.size()) - return false; - - for(const auto& it : Paths) - { - if(other.Paths.count(it.first) == 0 || other.Paths.at(it.first) != it.second) - return false; - } - - return true; -} diff --git a/Src/runcpp2/Data/ProfilesProcessPaths.hpp b/Src/runcpp2/Data/ProfilesProcessPaths.hpp new file mode 100644 index 0000000..3e776fe --- /dev/null +++ b/Src/runcpp2/Data/ProfilesProcessPaths.hpp @@ -0,0 +1,118 @@ +#ifndef RUNCPP2_DATA_PROFILES_PROCESS_PATHS_HPP +#define RUNCPP2_DATA_PROFILES_PROCESS_PATHS_HPP + +#include "runcpp2/Data/ParseCommon.hpp" +#include "runcpp2/ParseUtil.hpp" +#include "runcpp2/LibYAML_Wrapper.hpp" + +#include "ssLogger/ssLog.hpp" + +#if !defined(NOMINMAX) + #define NOMINMAX 1 +#endif +#include "ghc/filesystem.hpp" +#include "DSResult/DSResult.hpp" + +#include +#include +#include +#include + +namespace runcpp2 +{ +namespace Data +{ + struct ProfilesProcessPaths + { + std::unordered_map> Paths; + + inline bool ParseYAML_Node(YAML::ConstNodePtr node) + { + ssLOG_FUNC_DEBUG(); + + if(!node->IsMap()) + { + ssLOG_ERROR("ProfilesProcessPaths: Not a map type"); + return false; + } + + for(int i = 0; i < node->GetChildrenCount(); ++i) + { + YAML::ConstNodePtr currentProfilePathsNode = node->GetMapValueNodeAt(i); + ProfileName profile = node->GetMapKeyScalarAt(i).DS_TRY_ACT(return false); + if(!ParseYAML_NodeWithProfile(currentProfilePathsNode, profile)) + return false; + } + + return true; + } + + inline bool ParseYAML_NodeWithProfile(YAML::ConstNodePtr node, ProfileName profile) + { + ssLOG_FUNC_DEBUG(); + + if(!node->IsSequence()) + { + ssLOG_ERROR("ProfilesProcessPaths: Paths type requires a list"); + return false; + } + + for(int i = 0; i < node->GetChildrenCount(); ++i) + { + std::string path = node ->GetSequenceChildScalar(i) + .DS_TRY_ACT(return false); + Paths[profile].push_back(path); + } + + return true; + } + + inline bool IsYAML_NodeParsableAsDefault(YAML::ConstNodePtr node) const + { + ssLOG_FUNC_DEBUG(); + return node->IsSequence(); + } + + inline std::string ToString(std::string indentation) const + { + std::string out; + + if(Paths.empty()) + return out; + + for(auto it = Paths.begin(); it != Paths.end(); ++it) + { + if(it->second.empty()) + out += indentation + it->first + ": []\n"; + else + { + out += indentation + it->first + ":\n"; + for(int i = 0; i < it->second.size(); ++i) + { + out += indentation + "- " + + GetEscapedYAMLString(it->second.at(i).string()) + "\n"; + } + } + } + + return out; + } + + inline bool Equals(const ProfilesProcessPaths& other) const + { + if(Paths.size() != other.Paths.size()) + return false; + + for(const auto& it : Paths) + { + if(other.Paths.count(it.first) == 0 || other.Paths.at(it.first) != it.second) + return false; + } + + return true; + } + }; +} +} + +#endif diff --git a/Src/runcpp2/Data/ScriptInfo.cpp b/Src/runcpp2/Data/ScriptInfo.cpp deleted file mode 100644 index 50d687e..0000000 --- a/Src/runcpp2/Data/ScriptInfo.cpp +++ /dev/null @@ -1,479 +0,0 @@ -#include "runcpp2/Data/ScriptInfo.hpp" -#include "runcpp2/ParseUtil.hpp" -#include "runcpp2/Data/ParseCommon.hpp" -#include "ssLogger/ssLog.hpp" - -bool runcpp2::Data::ScriptInfo::ParseYAML_Node(YAML::ConstNodePtr node) -{ - ssLOG_FUNC_DEBUG(); - - static_assert(FieldsCount == 14, "Update this function when adding new fields"); - std::vector requirements = - { - NodeRequirement("PassScriptPath", YAML::NodeType::Scalar, false, true), - NodeRequirement("Language", YAML::NodeType::Scalar, false, true), - NodeRequirement("BuildType", YAML::NodeType::Scalar, false, true), - NodeRequirement("RequiredProfiles", YAML::NodeType::Map, false, true), - - //Expecting either platform profile map or remove append map - NodeRequirement("OverrideCompileFlags", YAML::NodeType::Map, false, true), - - //Expecting either platform profile map or remove append map - NodeRequirement("OverrideLinkFlags", YAML::NodeType::Map, false, true), - - //OtherFilesToBeCompiled can be platform profile map or sequence of paths, handle later - //IncludePaths can be platform profile map or sequence of paths, handle later - - NodeRequirement("Dependencies", YAML::NodeType::Sequence, false, true) - - //Defines can be platform profile map or sequence of defines, handle later - //Setup can be platform profile map or sequence of commands, handle later - //PreBuild can be platform profile map or sequence of commands, handle later - //PostBuild can be platform profile map or sequence of commands, handle later - //Cleanup can be platform profile map or sequence of commands, handle later - }; - - if(!CheckNodeRequirements(node, requirements)) - { - ssLOG_ERROR("ScriptInfo: Failed to meet requirements"); - return false; - } - - if(ExistAndHasChild(node, "PassScriptPath")) - { - std::string passScriptPathStr = node->GetMapValueScalar("PassScriptPath") - .DS_TRY_ACT(return false); - for(size_t i = 0; i < passScriptPathStr.length(); ++i) - passScriptPathStr[i] = std::tolower(passScriptPathStr[i]); - - if(passScriptPathStr == "true" || passScriptPathStr == "1") - PassScriptPath = true; - else if(passScriptPathStr == "false" || passScriptPathStr == "0") - PassScriptPath = false; - else - { - ssLOG_ERROR("ScriptInfo: Invalid value for PassScriptPath: " << passScriptPathStr); - ssLOG_ERROR("Expected true/false or 1/0"); - return false; - } - } - - if(ExistAndHasChild(node, "Language")) - { - Language = node->GetMapValueScalar("Language").DS_TRY_ACT(return false); - } - - if(ExistAndHasChild(node, "BuildType")) - { - std::string typeStr = node ->GetMapValueScalar("BuildType") - .DS_TRY_ACT(return false); - BuildType buildType = StringToBuildType(typeStr); - if(buildType == BuildType::COUNT) - { - ssLOG_ERROR("ScriptInfo: Invalid build type: " << typeStr); - return false; - } - CurrentBuildType = buildType; - } - - if(ExistAndHasChild(node, "RequiredProfiles")) - { - YAML::ConstNodePtr requiredProfilesNode = node->GetMapValueNode("RequiredProfiles"); - for(int i = 0; i < requiredProfilesNode->GetChildrenCount(); ++i) - { - PlatformName platform = requiredProfilesNode->GetMapKeyScalarAt(i) - .DS_TRY_ACT(return false); - std::vector profiles; - YAML::ConstNodePtr platformNode = requiredProfilesNode->GetMapValueNodeAt(i); - for(int j = 0; j < platformNode->GetChildrenCount(); ++j) - { - std::string profile = platformNode ->GetSequenceChildScalar(j) - .DS_TRY_ACT(return false); - profiles.push_back(profile); - } - - RequiredProfiles[platform] = profiles; - } - } - - if(!ParsePlatformProfileMap( node, - "OverrideCompileFlags", - OverrideCompileFlags, - "OverrideCompileFlags")) - { - return false; - } - - if(!ParsePlatformProfileMap( node, - "OverrideLinkFlags", - OverrideLinkFlags, - "OverrideLinkFlags")) - { - return false; - } - - if(!ParsePlatformProfileMap( node, - "OtherFilesToBeCompiled", - OtherFilesToBeCompiled, - "OtherFilesToBeCompiled")) - { - return false; - } - - if(!ParsePlatformProfileMap( node, - "SourceFiles", - OtherFilesToBeCompiled, - "SourceFiles")) - { - return false; - } - - if(!ParsePlatformProfileMap( node, - "IncludePaths", - IncludePaths, - "IncludePaths")) - { - return false; - } - - if(ExistAndHasChild(node, "Dependencies")) - { - YAML::ConstNodePtr dependenciesNode = node->GetMapValueNode("Dependencies"); - for(int i = 0; i < dependenciesNode->GetChildrenCount(); ++i) - { - DependencyInfo info; - YAML::ConstNodePtr dependencyNode = dependenciesNode->GetSequenceChildNode(i); - if(!info.ParseYAML_Node(dependencyNode)) - { - ssLOG_ERROR("ScriptInfo: Failed to parse DependencyInfo at index " << i); - return false; - } - - Dependencies.push_back(info); - } - } - - if(!ParsePlatformProfileMap(node, "Defines", Defines, "Defines")) - return false; - - if(!ParsePlatformProfileMap(node, "Setup", Setup, "Setup")) - return false; - - if(!ParsePlatformProfileMap(node, "PreBuild", PreBuild, "PreBuild")) - return false; - - if(!ParsePlatformProfileMap(node, "PostBuild", PostBuild, "PostBuild")) - return false; - - if(!ParsePlatformProfileMap(node, "Cleanup", Cleanup, "Cleanup")) - return false; - - return true; -} - -std::string runcpp2::Data::ScriptInfo::ToString(std::string indentation) const -{ - static_assert(FieldsCount == 14, "Update this function when adding new fields"); - std::string out; - - out += indentation + "PassScriptPath: " + (PassScriptPath ? "true" : "false") + "\n"; - - if(!Language.empty()) - out += indentation + "Language: " + GetEscapedYAMLString(Language) + "\n"; - - out += indentation + "BuildType: " + BuildTypeToString(CurrentBuildType) + "\n"; - - if(!RequiredProfiles.empty()) - { - out += indentation + "RequiredProfiles:\n"; - for(auto it = RequiredProfiles.begin(); it != RequiredProfiles.end(); ++it) - { - if(it->second.empty()) - out += indentation + " " + it->first + ": []\n"; - else - { - out += indentation + " " + it->first + ":\n"; - for(int i = 0; i < it->second.size(); ++i) - out += indentation + " - " + GetEscapedYAMLString(it->second[i]) + "\n"; - } - } - } - - if(!OverrideCompileFlags.empty()) - { - out += indentation + "OverrideCompileFlags:\n"; - for(auto it = OverrideCompileFlags.begin(); it != OverrideCompileFlags.end(); ++it) - { - out += indentation + " " + it->first + ":\n"; - out += it->second.ToString(indentation + " "); - } - } - - if(!OverrideLinkFlags.empty()) - { - out += indentation + "OverrideLinkFlags:\n"; - for(auto it = OverrideLinkFlags.begin(); it != OverrideLinkFlags.end(); ++it) - { - out += indentation + " " + it->first + ":\n"; - out += it->second.ToString(indentation + " "); - } - } - - if(!OtherFilesToBeCompiled.empty()) - { - out += indentation + "SourceFiles:\n"; - for(auto it = OtherFilesToBeCompiled.begin(); it != OtherFilesToBeCompiled.end(); ++it) - { - out += indentation + " " + it->first + ":\n"; - out += it->second.ToString(indentation + " "); - } - } - - if(!IncludePaths.empty()) - { - out += indentation + "IncludePaths:\n"; - for(auto it = IncludePaths.begin(); it != IncludePaths.end(); ++it) - { - out += indentation + " " + it->first + ":\n"; - out += it->second.ToString(indentation + " "); - } - } - - if(!Dependencies.empty()) - { - out += indentation + "Dependencies:\n"; - for(int i = 0; i < Dependencies.size(); ++i) - { - int currentOutSize = out.size(); - out += Dependencies[i].ToString(indentation + " "); - - //Change character to yaml list - out.at(currentOutSize + indentation.size()) = '-'; - } - } - - if(!Defines.empty()) - { - out += indentation + "Defines:\n"; - for(auto it = Defines.begin(); it != Defines.end(); ++it) - { - out += indentation + " " + it->first + ":\n"; - out += it->second.ToString(indentation + " "); - } - } - - if(!Setup.empty()) - { - out += indentation + "Setup:\n"; - for(auto it = Setup.begin(); it != Setup.end(); ++it) - { - out += indentation + " " + it->first + ":\n"; - out += it->second.ToString(indentation + " "); - } - } - - if(!PreBuild.empty()) - { - out += indentation + "PreBuild:\n"; - for(auto it = PreBuild.begin(); it != PreBuild.end(); ++it) - { - out += indentation + " " + it->first + ":\n"; - out += it->second.ToString(indentation + " "); - } - } - - if(!PostBuild.empty()) - { - out += indentation + "PostBuild:\n"; - for(auto it = PostBuild.begin(); it != PostBuild.end(); ++it) - { - out += indentation + " " + it->first + ":\n"; - out += it->second.ToString(indentation + " "); - } - } - - if(!Cleanup.empty()) - { - out += indentation + "Cleanup:\n"; - for(auto it = Cleanup.begin(); it != Cleanup.end(); ++it) - { - out += indentation + " " + it->first + ":\n"; - out += it->second.ToString(indentation + " "); - } - } - - return out; -} - -bool runcpp2::Data::ScriptInfo::IsAllCompiledCacheInvalidated(const ScriptInfo& other) const -{ - static_assert(FieldsCount == 14, "Update this function when adding new fields"); - if( Language != other.Language || - CurrentBuildType != other.CurrentBuildType || - RequiredProfiles.size() != other.RequiredProfiles.size() || - OverrideCompileFlags.size() != other.OverrideCompileFlags.size() || - OtherFilesToBeCompiled.size() != other.OtherFilesToBeCompiled.size() || - IncludePaths.size() != other.IncludePaths.size() || - Dependencies.size() != other.Dependencies.size() || - Defines.size() != other.Defines.size() || - Populated != other.Populated) - { - return true; - } - - for(const auto& it : RequiredProfiles) - { - if( other.RequiredProfiles.count(it.first) == 0 || - other.RequiredProfiles.at(it.first) != it.second) - { - return true; - } - } - - for(const auto& it : OverrideCompileFlags) - { - if( other.OverrideCompileFlags.count(it.first) == 0 || - !other.OverrideCompileFlags.at(it.first).Equals(it.second)) - { - return true; - } - } - - for(const auto& it : OtherFilesToBeCompiled) - { - if( other.OtherFilesToBeCompiled.count(it.first) == 0 || - !other.OtherFilesToBeCompiled.at(it.first).Equals(it.second)) - { - return true; - } - } - - for(const auto& it : IncludePaths) - { - if( other.IncludePaths.count(it.first) == 0 || - !other.IncludePaths.at(it.first).Equals(it.second)) - { - return true; - } - } - - for(size_t i = 0; i < Dependencies.size(); ++i) - { - if(!Dependencies[i].Equals(other.Dependencies[i])) - return true; - } - - for(const auto& it : Defines) - { - if(other.Defines.count(it.first) == 0 || !other.Defines.at(it.first).Equals(it.second)) - return true; - } - - return false; -} - -bool runcpp2::Data::ScriptInfo::Equals(const ScriptInfo& other) const -{ - static_assert(FieldsCount == 14, "Update this function when adding new fields"); - if( Language != other.Language || - PassScriptPath != other.PassScriptPath || - CurrentBuildType != other.CurrentBuildType || - RequiredProfiles.size() != other.RequiredProfiles.size() || - OverrideCompileFlags.size() != other.OverrideCompileFlags.size() || - OverrideLinkFlags.size() != other.OverrideLinkFlags.size() || - OtherFilesToBeCompiled.size() != other.OtherFilesToBeCompiled.size() || - IncludePaths.size() != other.IncludePaths.size() || - Dependencies.size() != other.Dependencies.size() || - Defines.size() != other.Defines.size() || - Setup.size() != other.Setup.size() || - PreBuild.size() != other.PreBuild.size() || - PostBuild.size() != other.PostBuild.size() || - Cleanup.size() != other.Cleanup.size() || - Populated != other.Populated) - { - return false; - } - - for(const auto& it : RequiredProfiles) - { - if( other.RequiredProfiles.count(it.first) == 0 || - other.RequiredProfiles.at(it.first) != it.second) - { - return false; - } - } - - for(const auto& it : OverrideCompileFlags) - { - if( other.OverrideCompileFlags.count(it.first) == 0 || - !other.OverrideCompileFlags.at(it.first).Equals(it.second)) - { - return false; - } - } - - for(const auto& it : OverrideLinkFlags) - { - if( other.OverrideLinkFlags.count(it.first) == 0 || - !other.OverrideLinkFlags.at(it.first).Equals(it.second)) - { - return false; - } - } - - for(const auto& it : OtherFilesToBeCompiled) - { - if( other.OtherFilesToBeCompiled.count(it.first) == 0 || - !other.OtherFilesToBeCompiled.at(it.first).Equals(it.second)) - { - return false; - } - } - - for(const auto& it : IncludePaths) - { - if( other.IncludePaths.count(it.first) == 0 || - !other.IncludePaths.at(it.first).Equals(it.second)) - { - return false; - } - } - - for(size_t i = 0; i < Dependencies.size(); ++i) - { - if(!Dependencies[i].Equals(other.Dependencies[i])) - return false; - } - - for(const auto& it : Defines) - { - if(other.Defines.count(it.first) == 0 || !other.Defines.at(it.first).Equals(it.second)) - return false; - } - - for(const auto& it : Setup) - { - if(other.Setup.count(it.first) == 0 || !other.Setup.at(it.first).Equals(it.second)) - return false; - } - - for(const auto& it : PreBuild) - { - if(other.PreBuild.count(it.first) == 0 || !other.PreBuild.at(it.first).Equals(it.second)) - return false; - } - - for(const auto& it : PostBuild) - { - if(other.PostBuild.count(it.first) == 0 || !other.PostBuild.at(it.first).Equals(it.second)) - return false; - } - - for(const auto& it : Cleanup) - { - if(other.Cleanup.count(it.first) == 0 || !other.Cleanup.at(it.first).Equals(it.second)) - return false; - } - - return true; -} diff --git a/Src/runcpp2/Data/ScriptInfo.hpp b/Src/runcpp2/Data/ScriptInfo.hpp new file mode 100644 index 0000000..f96dddc --- /dev/null +++ b/Src/runcpp2/Data/ScriptInfo.hpp @@ -0,0 +1,557 @@ +#ifndef RUNCPP2_DATA_SCRIPT_INFO_HPP +#define RUNCPP2_DATA_SCRIPT_INFO_HPP + +#include "runcpp2/Data/DependencyInfo.hpp" +#include "runcpp2/Data/ProfilesFlagsOverride.hpp" +#include "runcpp2/Data/ProfilesProcessPaths.hpp" +#include "runcpp2/Data/ProfilesDefines.hpp" +#include "runcpp2/Data/ProfilesCommands.hpp" +#include "runcpp2/Data/BuildType.hpp" +#include "runcpp2/ParseUtil.hpp" +#include "runcpp2/Data/ParseCommon.hpp" +#include "runcpp2/LibYAML_Wrapper.hpp" + +#undef RUNCPP2_CURRENT_CLASS_NAME +#define RUNCPP2_CURRENT_CLASS_NAME ScriptInfo +#include "runcpp2/MacroUtil.hpp" + +#if !defined(NOMINMAX) + #define NOMINMAX 1 +#endif + +#include "DSResult/DSResult.hpp" +#include "ghc/filesystem.hpp" +#include "ssLogger/ssLog.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace runcpp2 +{ +namespace Data +{ + struct ScriptInfo + { + RUNCPP2_FIELD_BEGIN(); + + RUNCPP2_FIELD std::string Language; + RUNCPP2_FIELD bool PassScriptPath = false; + RUNCPP2_FIELD BuildType CurrentBuildType = BuildType::EXECUTABLE; + RUNCPP2_FIELD std::unordered_map< PlatformName, + std::vector> RequiredProfiles; + RUNCPP2_FIELD std::unordered_map< PlatformName, + ProfilesFlagsOverride> OverrideCompileFlags; + RUNCPP2_FIELD std::unordered_map< PlatformName, + ProfilesFlagsOverride> OverrideLinkFlags; + RUNCPP2_FIELD std::unordered_map< PlatformName, + ProfilesProcessPaths> OtherFilesToBeCompiled; + RUNCPP2_FIELD std::unordered_map< PlatformName, + ProfilesProcessPaths> IncludePaths; + RUNCPP2_FIELD std::vector Dependencies; + RUNCPP2_FIELD std::unordered_map Defines; + RUNCPP2_FIELD std::unordered_map Setup; + RUNCPP2_FIELD std::unordered_map PreBuild; + RUNCPP2_FIELD std::unordered_map PostBuild; + RUNCPP2_FIELD std::unordered_map Cleanup; + + static constexpr int FieldsCount = RUNCPP2_FIELD_COUNT; + + //Internal tracking + bool Populated = false; + ghc::filesystem::file_time_type LastWriteTime = ghc::filesystem::file_time_type::min(); + + inline bool ParseYAML_Node(YAML::ConstNodePtr node) + { + ssLOG_FUNC_DEBUG(); + + static_assert(FieldsCount == 14, "Update this function when adding new fields"); + std::vector requirements = + { + NodeRequirement("PassScriptPath", YAML::NodeType::Scalar, false, true), + NodeRequirement("Language", YAML::NodeType::Scalar, false, true), + NodeRequirement("BuildType", YAML::NodeType::Scalar, false, true), + NodeRequirement("RequiredProfiles", YAML::NodeType::Map, false, true), + + //Expecting either platform profile map or remove append map + NodeRequirement("OverrideCompileFlags", YAML::NodeType::Map, false, true), + + //Expecting either platform profile map or remove append map + NodeRequirement("OverrideLinkFlags", YAML::NodeType::Map, false, true), + + //OtherFilesToBeCompiled can be platform profile map or sequence of paths, handle later + //IncludePaths can be platform profile map or sequence of paths, handle later + + NodeRequirement("Dependencies", YAML::NodeType::Sequence, false, true) + + //Defines can be platform profile map or sequence of defines, handle later + //Setup can be platform profile map or sequence of commands, handle later + //PreBuild can be platform profile map or sequence of commands, handle later + //PostBuild can be platform profile map or sequence of commands, handle later + //Cleanup can be platform profile map or sequence of commands, handle later + }; + + if(!CheckNodeRequirements(node, requirements)) + { + ssLOG_ERROR("ScriptInfo: Failed to meet requirements"); + return false; + } + + if(ExistAndHasChild(node, "PassScriptPath")) + { + std::string passScriptPathStr = node->GetMapValueScalar("PassScriptPath") + .DS_TRY_ACT(return false); + for(size_t i = 0; i < passScriptPathStr.length(); ++i) + passScriptPathStr[i] = std::tolower(passScriptPathStr[i]); + + if(passScriptPathStr == "true" || passScriptPathStr == "1") + PassScriptPath = true; + else if(passScriptPathStr == "false" || passScriptPathStr == "0") + PassScriptPath = false; + else + { + ssLOG_ERROR("ScriptInfo: Invalid value for PassScriptPath: " << passScriptPathStr); + ssLOG_ERROR("Expected true/false or 1/0"); + return false; + } + } + + if(ExistAndHasChild(node, "Language")) + { + Language = node->GetMapValueScalar("Language").DS_TRY_ACT(return false); + } + + if(ExistAndHasChild(node, "BuildType")) + { + std::string typeStr = node ->GetMapValueScalar("BuildType") + .DS_TRY_ACT(return false); + BuildType buildType = StringToBuildType(typeStr); + if(buildType == BuildType::COUNT) + { + ssLOG_ERROR("ScriptInfo: Invalid build type: " << typeStr); + return false; + } + CurrentBuildType = buildType; + } + + if(ExistAndHasChild(node, "RequiredProfiles")) + { + YAML::ConstNodePtr requiredProfilesNode = node->GetMapValueNode("RequiredProfiles"); + for(int i = 0; i < requiredProfilesNode->GetChildrenCount(); ++i) + { + PlatformName platform = requiredProfilesNode->GetMapKeyScalarAt(i) + .DS_TRY_ACT(return false); + std::vector profiles; + YAML::ConstNodePtr platformNode = requiredProfilesNode->GetMapValueNodeAt(i); + for(int j = 0; j < platformNode->GetChildrenCount(); ++j) + { + std::string profile = platformNode ->GetSequenceChildScalar(j) + .DS_TRY_ACT(return false); + profiles.push_back(profile); + } + + RequiredProfiles[platform] = profiles; + } + } + + if(!ParsePlatformProfileMap( node, + "OverrideCompileFlags", + OverrideCompileFlags, + "OverrideCompileFlags")) + { + return false; + } + + if(!ParsePlatformProfileMap( node, + "OverrideLinkFlags", + OverrideLinkFlags, + "OverrideLinkFlags")) + { + return false; + } + + if(!ParsePlatformProfileMap( node, + "OtherFilesToBeCompiled", + OtherFilesToBeCompiled, + "OtherFilesToBeCompiled")) + { + return false; + } + + if(!ParsePlatformProfileMap( node, + "SourceFiles", + OtherFilesToBeCompiled, + "SourceFiles")) + { + return false; + } + + if(!ParsePlatformProfileMap( node, + "IncludePaths", + IncludePaths, + "IncludePaths")) + { + return false; + } + + if(ExistAndHasChild(node, "Dependencies")) + { + YAML::ConstNodePtr dependenciesNode = node->GetMapValueNode("Dependencies"); + for(int i = 0; i < dependenciesNode->GetChildrenCount(); ++i) + { + DependencyInfo info; + YAML::ConstNodePtr dependencyNode = dependenciesNode->GetSequenceChildNode(i); + if(!info.ParseYAML_Node(dependencyNode)) + { + ssLOG_ERROR("ScriptInfo: Failed to parse DependencyInfo at index " << i); + return false; + } + + Dependencies.push_back(info); + } + } + + if(!ParsePlatformProfileMap(node, "Defines", Defines, "Defines")) + return false; + + if(!ParsePlatformProfileMap(node, "Setup", Setup, "Setup")) + return false; + + if(!ParsePlatformProfileMap(node, "PreBuild", PreBuild, "PreBuild")) + return false; + + if(!ParsePlatformProfileMap(node, "PostBuild", PostBuild, "PostBuild")) + return false; + + if(!ParsePlatformProfileMap(node, "Cleanup", Cleanup, "Cleanup")) + return false; + + return true; + } + + inline std::string ToString(std::string indentation) const + { + static_assert(FieldsCount == 14, "Update this function when adding new fields"); + std::string out; + + out += indentation + "PassScriptPath: " + (PassScriptPath ? "true" : "false") + "\n"; + + if(!Language.empty()) + out += indentation + "Language: " + GetEscapedYAMLString(Language) + "\n"; + + out += indentation + "BuildType: " + BuildTypeToString(CurrentBuildType) + "\n"; + + if(!RequiredProfiles.empty()) + { + out += indentation + "RequiredProfiles:\n"; + for(auto it = RequiredProfiles.begin(); it != RequiredProfiles.end(); ++it) + { + if(it->second.empty()) + out += indentation + " " + it->first + ": []\n"; + else + { + out += indentation + " " + it->first + ":\n"; + for(int i = 0; i < it->second.size(); ++i) + { + out += indentation + " - " + + GetEscapedYAMLString(it->second[i]) + "\n"; + } + } + } + } + + if(!OverrideCompileFlags.empty()) + { + out += indentation + "OverrideCompileFlags:\n"; + for(auto it = OverrideCompileFlags.begin(); it != OverrideCompileFlags.end(); ++it) + { + out += indentation + " " + it->first + ":\n"; + out += it->second.ToString(indentation + " "); + } + } + + if(!OverrideLinkFlags.empty()) + { + out += indentation + "OverrideLinkFlags:\n"; + for(auto it = OverrideLinkFlags.begin(); it != OverrideLinkFlags.end(); ++it) + { + out += indentation + " " + it->first + ":\n"; + out += it->second.ToString(indentation + " "); + } + } + + if(!OtherFilesToBeCompiled.empty()) + { + out += indentation + "SourceFiles:\n"; + for(auto it = OtherFilesToBeCompiled.begin(); + it != OtherFilesToBeCompiled.end(); + ++it) + { + out += indentation + " " + it->first + ":\n"; + out += it->second.ToString(indentation + " "); + } + } + + if(!IncludePaths.empty()) + { + out += indentation + "IncludePaths:\n"; + for(auto it = IncludePaths.begin(); it != IncludePaths.end(); ++it) + { + out += indentation + " " + it->first + ":\n"; + out += it->second.ToString(indentation + " "); + } + } + + if(!Dependencies.empty()) + { + out += indentation + "Dependencies:\n"; + for(int i = 0; i < Dependencies.size(); ++i) + { + int currentOutSize = out.size(); + out += Dependencies[i].ToString(indentation + " "); + + //Change character to yaml list + out.at(currentOutSize + indentation.size()) = '-'; + } + } + + if(!Defines.empty()) + { + out += indentation + "Defines:\n"; + for(auto it = Defines.begin(); it != Defines.end(); ++it) + { + out += indentation + " " + it->first + ":\n"; + out += it->second.ToString(indentation + " "); + } + } + + if(!Setup.empty()) + { + out += indentation + "Setup:\n"; + for(auto it = Setup.begin(); it != Setup.end(); ++it) + { + out += indentation + " " + it->first + ":\n"; + out += it->second.ToString(indentation + " "); + } + } + + if(!PreBuild.empty()) + { + out += indentation + "PreBuild:\n"; + for(auto it = PreBuild.begin(); it != PreBuild.end(); ++it) + { + out += indentation + " " + it->first + ":\n"; + out += it->second.ToString(indentation + " "); + } + } + + if(!PostBuild.empty()) + { + out += indentation + "PostBuild:\n"; + for(auto it = PostBuild.begin(); it != PostBuild.end(); ++it) + { + out += indentation + " " + it->first + ":\n"; + out += it->second.ToString(indentation + " "); + } + } + + if(!Cleanup.empty()) + { + out += indentation + "Cleanup:\n"; + for(auto it = Cleanup.begin(); it != Cleanup.end(); ++it) + { + out += indentation + " " + it->first + ":\n"; + out += it->second.ToString(indentation + " "); + } + } + + return out; + } + + inline bool IsAllCompiledCacheInvalidated(const ScriptInfo& other) const + { + static_assert(FieldsCount == 14, "Update this function when adding new fields"); + if( Language != other.Language || + CurrentBuildType != other.CurrentBuildType || + RequiredProfiles.size() != other.RequiredProfiles.size() || + OverrideCompileFlags.size() != other.OverrideCompileFlags.size() || + OtherFilesToBeCompiled.size() != other.OtherFilesToBeCompiled.size() || + IncludePaths.size() != other.IncludePaths.size() || + Dependencies.size() != other.Dependencies.size() || + Defines.size() != other.Defines.size() || + Populated != other.Populated) + { + return true; + } + + for(const auto& it : RequiredProfiles) + { + if( other.RequiredProfiles.count(it.first) == 0 || + other.RequiredProfiles.at(it.first) != it.second) + { + return true; + } + } + + for(const auto& it : OverrideCompileFlags) + { + if( other.OverrideCompileFlags.count(it.first) == 0 || + !other.OverrideCompileFlags.at(it.first).Equals(it.second)) + { + return true; + } + } + + for(const auto& it : OtherFilesToBeCompiled) + { + if( other.OtherFilesToBeCompiled.count(it.first) == 0 || + !other.OtherFilesToBeCompiled.at(it.first).Equals(it.second)) + { + return true; + } + } + + for(const auto& it : IncludePaths) + { + if( other.IncludePaths.count(it.first) == 0 || + !other.IncludePaths.at(it.first).Equals(it.second)) + { + return true; + } + } + + for(size_t i = 0; i < Dependencies.size(); ++i) + { + if(!Dependencies[i].Equals(other.Dependencies[i])) + return true; + } + + for(const auto& it : Defines) + { + if(other.Defines.count(it.first) == 0 || !other.Defines.at(it.first).Equals(it.second)) + return true; + } + + return false; + } + + inline bool Equals(const ScriptInfo& other) const + { + static_assert(FieldsCount == 14, "Update this function when adding new fields"); + if( Language != other.Language || + PassScriptPath != other.PassScriptPath || + CurrentBuildType != other.CurrentBuildType || + RequiredProfiles.size() != other.RequiredProfiles.size() || + OverrideCompileFlags.size() != other.OverrideCompileFlags.size() || + OverrideLinkFlags.size() != other.OverrideLinkFlags.size() || + OtherFilesToBeCompiled.size() != other.OtherFilesToBeCompiled.size() || + IncludePaths.size() != other.IncludePaths.size() || + Dependencies.size() != other.Dependencies.size() || + Defines.size() != other.Defines.size() || + Setup.size() != other.Setup.size() || + PreBuild.size() != other.PreBuild.size() || + PostBuild.size() != other.PostBuild.size() || + Cleanup.size() != other.Cleanup.size() || + Populated != other.Populated) + { + return false; + } + + for(const auto& it : RequiredProfiles) + { + if( other.RequiredProfiles.count(it.first) == 0 || + other.RequiredProfiles.at(it.first) != it.second) + { + return false; + } + } + + for(const auto& it : OverrideCompileFlags) + { + if( other.OverrideCompileFlags.count(it.first) == 0 || + !other.OverrideCompileFlags.at(it.first).Equals(it.second)) + { + return false; + } + } + + for(const auto& it : OverrideLinkFlags) + { + if( other.OverrideLinkFlags.count(it.first) == 0 || + !other.OverrideLinkFlags.at(it.first).Equals(it.second)) + { + return false; + } + } + + for(const auto& it : OtherFilesToBeCompiled) + { + if( other.OtherFilesToBeCompiled.count(it.first) == 0 || + !other.OtherFilesToBeCompiled.at(it.first).Equals(it.second)) + { + return false; + } + } + + for(const auto& it : IncludePaths) + { + if( other.IncludePaths.count(it.first) == 0 || + !other.IncludePaths.at(it.first).Equals(it.second)) + { + return false; + } + } + + for(size_t i = 0; i < Dependencies.size(); ++i) + { + if(!Dependencies[i].Equals(other.Dependencies[i])) + return false; + } + + for(const auto& it : Defines) + { + if(other.Defines.count(it.first) == 0 || !other.Defines.at(it.first).Equals(it.second)) + return false; + } + + for(const auto& it : Setup) + { + if(other.Setup.count(it.first) == 0 || !other.Setup.at(it.first).Equals(it.second)) + return false; + } + + for(const auto& it : PreBuild) + { + if( other.PreBuild.count(it.first) == 0 || + !other.PreBuild.at(it.first).Equals(it.second)) + { + return false; + } + } + + for(const auto& it : PostBuild) + { + if( other.PostBuild.count(it.first) == 0 || + !other.PostBuild.at(it.first).Equals(it.second)) + { + return false; + } + } + + for(const auto& it : Cleanup) + { + if(other.Cleanup.count(it.first) == 0 || !other.Cleanup.at(it.first).Equals(it.second)) + return false; + } + + return true; + } + }; +} +} + +#endif diff --git a/Src/runcpp2/Data/StageInfo.cpp b/Src/runcpp2/Data/StageInfo.cpp deleted file mode 100644 index adf16df..0000000 --- a/Src/runcpp2/Data/StageInfo.cpp +++ /dev/null @@ -1,763 +0,0 @@ -#include "runcpp2/Data/StageInfo.hpp" -#include "runcpp2/ParseUtil.hpp" -#include "runcpp2/PlatformUtil.hpp" - -#include "ssLogger/ssLog.hpp" - -namespace -{ - using Runcpp2OutputTypeInfo = runcpp2::Data::StageInfo::OutputTypeInfo; - - bool ParseOutputTypes( const std::string& subNodeKey, - runcpp2::YAML::ConstNodePtr outputTypesSubNode, - std::vector requirements, - std::unordered_map< PlatformName, - Runcpp2OutputTypeInfo>& outInfos) - { - ssLOG_FUNC_DEBUG(); - using namespace runcpp2; - using namespace runcpp2::Data; - - const std::string& subNodeName = subNodeKey; - - for(int i = 0; i < outputTypesSubNode->GetChildrenCount(); ++i) - { - YAML::ConstNodePtr currentPlatformNode = outputTypesSubNode->GetMapValueNodeAt(i); - std::string platformName = outputTypesSubNode ->GetMapKeyScalarAt(i) - .DS_TRY_ACT(return false); - if(!CheckNodeRequirements(currentPlatformNode, requirements)) - { - ssLOG_ERROR("Failed to parse outputTypesSubNode in " << subNodeName << - " for platform " << platformName); - return false; - } - - outInfos[platformName].Flags = - currentPlatformNode->GetMapValueScalar("Flags").DS_TRY_ACT(return false); - - outInfos[platformName].Executable = - currentPlatformNode ->GetMapValueScalar("Executable") - .DS_TRY_ACT(return false); - - //RunParts - if(!ExistAndHasChild(currentPlatformNode, "RunParts")) - { - ssLOG_ERROR("Failed to parse RunParts"); - return false; - } - - YAML::ConstNodePtr runPartsNode = currentPlatformNode->GetMapValueNode("RunParts"); - for(int j = 0; j < runPartsNode->GetChildrenCount(); ++j) - { - YAML::ConstNodePtr currentPartNode = runPartsNode->GetSequenceChildNode(j); - - std::vector currentRunPartRequirements = - { - NodeRequirement("Type", YAML::NodeType::Scalar, true, false), - NodeRequirement("CommandPart", YAML::NodeType::Scalar, true, false) - }; - - if(!CheckNodeRequirements(currentPartNode, currentRunPartRequirements)) - { - ssLOG_ERROR("Failed to parse RunPart at index " << j << " for " << platformName); - return false; - } - - outInfos[platformName].RunParts.push_back({}); - std::string currentType = currentPartNode ->GetMapValueScalar("Type") - .DS_TRY_ACT(return false); - static_assert( static_cast(StageInfo::RunPart::RunType::COUNT) == 2, - "Add new RunType"); - - if(currentType == "Once") - outInfos[platformName].RunParts.back().Type = StageInfo::RunPart::RunType::ONCE; - else if(currentType == "Repeats") - outInfos[platformName].RunParts.back().Type = StageInfo::RunPart::RunType::REPEATS; - else - { - ssLOG_WARNING( "Invalid RunPart type " << currentType << " at index " << j << - " for " << platformName); - ssLOG_DEBUG("Defaulting to Once"); - outInfos[platformName].RunParts.back().Type = StageInfo::RunPart::RunType::ONCE; - } - - outInfos[platformName].RunParts.back().CommandPart = - currentPartNode ->GetMapValueScalar("CommandPart") - .DS_TRY_ACT(return false); - } - - //Setup - if(ExistAndHasChild(currentPlatformNode, "Setup")) - { - YAML::ConstNodePtr setupNode = currentPlatformNode->GetMapValueNode("Setup"); - for(int j = 0; j < setupNode->GetChildrenCount(); ++j) - { - std::string setupVal = setupNode->GetSequenceChildScalar(j) - .DS_TRY_ACT(return false); - outInfos[platformName].Setup.push_back(setupVal); - } - } - - //Cleanup - if(ExistAndHasChild(currentPlatformNode, "Cleanup")) - { - YAML::ConstNodePtr cleanupNode = currentPlatformNode->GetMapValueNode("Cleanup"); - for(int j = 0; j < cleanupNode->GetChildrenCount(); ++j) - { - std::string cleanupVal = cleanupNode->GetSequenceChildScalar(j) - .DS_TRY_ACT(return false); - outInfos[platformName].Cleanup.push_back(cleanupVal); - } - } - - //ExpectedOutputFiles - YAML::ConstNodePtr expectedOutputFilesNode = - currentPlatformNode->GetMapValueNode("ExpectedOutputFiles"); - for(int j = 0; j < expectedOutputFilesNode->GetChildrenCount(); ++j) - { - std::string outputFileVal = - expectedOutputFilesNode ->GetSequenceChildScalar(j) - .DS_TRY_ACT(return false); - outInfos[platformName].ExpectedOutputFiles.push_back(outputFileVal); - } - } - - return true; - } - - using OutputTypeInfo = runcpp2::Data::StageInfo::OutputTypeInfo; - - void OutputTypeInfoMapToString( const std::string& indentation, - const std::unordered_map< PlatformName, - OutputTypeInfo>& toStringMap, - std::string& outString) - { - using namespace runcpp2; - using namespace runcpp2::Data; - - for(auto it = toStringMap.begin(); it != toStringMap.end(); ++it) - { - outString += indentation + " " + it->first + ": \n"; - outString += indentation + " Flags: " + - GetEscapedYAMLString(it->second.Flags) + "\n"; - outString += indentation + " Executable: " + - GetEscapedYAMLString(it->second.Executable) + "\n"; - outString += indentation + " RunParts: \n"; - for(int i = 0; i < it->second.RunParts.size(); ++i) - { - static_assert( static_cast(StageInfo::RunPart::RunType::COUNT) == 2, - "Add new RunType"); - - StageInfo::RunPart::RunType currentType = it->second.RunParts.at(i).Type; - outString += indentation + - " - Type: " + - (currentType == StageInfo::RunPart::RunType::ONCE ? - "Once" : - "Repeats") + - "\n"; - - outString += indentation + " CommandPart: " + - GetEscapedYAMLString(it->second.RunParts.at(i).CommandPart) + "\n"; - } - - outString += indentation + " ExpectedOutputFiles: \n"; - for(int i = 0; i < it->second.ExpectedOutputFiles.size(); ++i) - { - outString += indentation + " - " + - GetEscapedYAMLString(it->second.ExpectedOutputFiles.at(i)) + "\n"; - } - - if(!it->second.Setup.empty()) - { - outString += indentation + " Setup: \n"; - for(int i = 0; i < it->second.Setup.size(); ++i) - { - outString += indentation + " - " + - GetEscapedYAMLString(it->second.Setup.at(i)) + "\n"; - } - } - - if(!it->second.Cleanup.empty()) - { - outString += indentation + " Cleanup: \n"; - for(int i = 0; i < it->second.Cleanup.size(); ++i) - { - outString += indentation + " - " + - GetEscapedYAMLString(it->second.Cleanup.at(i)) + "\n"; - } - } - } - } - - //NOTE: This extracts substitutions and also allow escapes to happen for substitution characters. - // To escape a substitution character, just repeat it. (i.e. {{text}} will be escaped as {text}) - void GetEscapedStringAndExtractSubstitutions( const std::string& processString, - std::string& outEscapedString, - std::vector& outFoundSubstitutions, - std::vector& outFoundLocations, - std::vector& outFoundLength) - { - ssLOG_FUNC_DEBUG(); - - outEscapedString.clear(); - std::string currentSubstitution; - - int lastOpenBracketIndex = -1; - for(int i = 0; i < processString.size(); ++i) - { - if(processString[i] == '{') - { - if(i == processString.size() - 1) - { - if(lastOpenBracketIndex == -1 || lastOpenBracketIndex != i - 1) - ssLOG_WARNING("Unescaped { at the end: " << processString); - - outEscapedString += '{'; - continue; - } - - //If we have opening bracket for the next character, - //this is an escaping character - if(processString[i + 1] == '{') - { - outEscapedString += '{'; - if(lastOpenBracketIndex != -1) - currentSubstitution += '{'; - - ++i; - continue; - } - - if(lastOpenBracketIndex != -1) - { - ssLOG_WARNING( "Unescaped { at index " << lastOpenBracketIndex << - ": " << processString); - } - - lastOpenBracketIndex = i; - currentSubstitution = "{"; - outEscapedString += '{'; - } - else if(processString[i] == '}') - { - //If we have closing bracket for the next character, - //this is an escaping character - if(i < processString.size() - 1 && processString[i + 1] == '}') - { - outEscapedString += '}'; - if(lastOpenBracketIndex != -1) - currentSubstitution += '}'; - - ++i; - continue; - } - - //If there's no open bracket, give warning - if(lastOpenBracketIndex == -1) - { - ssLOG_WARNING("Unescaped } at index " << i << ": " << processString); - continue; - } - - //Add substitution - outEscapedString += '}'; - currentSubstitution += '}'; - ssLOG_DEBUG("Substitution " << currentSubstitution << " found"); - outFoundSubstitutions.push_back(currentSubstitution); - outFoundLocations.push_back(outEscapedString.size() - currentSubstitution.size()); - outFoundLength.push_back(currentSubstitution.size()); - - //Reset - lastOpenBracketIndex = -1; - currentSubstitution.clear(); - } - //Normal characters - else - { - outEscapedString += processString[i]; - if(lastOpenBracketIndex != -1) - currentSubstitution += processString[i]; - } - } - } - - bool PerformSubstituionsWithInfo( const runcpp2::Data::StageInfo::SubstitutionMap& substitutionMap, - const std::string& escapedString, - const std::vector& foundSubstitutions, - const std::vector& substitutionsLocations, - const std::vector& substitutionsLengths, - std::string& inOutSubstitutedString, - int substituteValueIndex = 0) - { - ssLOG_FUNC_DEBUG(); - - inOutSubstitutedString = escapedString; - - for(int i = foundSubstitutions.size() - 1; i >= 0; --i) - { - const std::string& substitution = foundSubstitutions.at(i); - if(substitutionMap.count(substitution) == 0) - { - ssLOG_ERROR("INTERNAL ERROR, missing substitution value for \"" << substitution << "\""); - return false; - } - - std::string currentValue = substitutionMap.at(substitution).at(substituteValueIndex); - - //Escape escapes character at the end if any - { - std::vector escapeChars = {'\\'}; - #ifdef _WIN32 - escapeChars.emplace_back('^'); - #endif - if(!currentValue.empty()) - { - int currentValIndex = currentValue.size(); - while(currentValIndex > 0) - { - --currentValIndex; - bool found = false; - for(int j = 0; j < escapeChars.size(); ++j) - { - if(currentValue[currentValIndex] == escapeChars[j]) - { - found = true; - break; - } - } - - if(!found) - { - ++currentValIndex; - break; - } - } - - if(currentValIndex < currentValue.size()) - { - const std::string foundEscapes = currentValue.substr(currentValIndex); - std::string newEndEscapes; - //Just repeat the escape characters - for(int j = 0; j < foundEscapes.size(); ++j) - { - newEndEscapes.push_back(foundEscapes[j]); - newEndEscapes.push_back(foundEscapes[j]); - } - - currentValue = currentValue.substr(0, currentValIndex) + newEndEscapes; - } - } - } - - ssLOG_DEBUG("Replacing \"" << substitution << "\" with \"" << currentValue << - "\" in \"" << escapedString << "\""); - - inOutSubstitutedString.replace( substitutionsLocations.at(i), - substitutionsLengths.at(i), - currentValue); - } - - return true; - } -} - -bool runcpp2::Data::StageInfo::PerformSubstituions( const SubstitutionMap& substitutionMap, - std::string& inOutSubstitutedString) const -{ - std::string escapedString; - std::vector foundSubstitutions; - std::vector substitutionsLocations; - std::vector substitutionsLengths; - - GetEscapedStringAndExtractSubstitutions(inOutSubstitutedString, - escapedString, - foundSubstitutions, - substitutionsLocations, - substitutionsLengths); - - if( foundSubstitutions.size() != substitutionsLocations.size() || - foundSubstitutions.size() != substitutionsLengths.size()) - { - ssLOG_ERROR("Substitution size mismatch"); - ssLOG_ERROR("foundSubstitutions.size(): " << foundSubstitutions.size()); - ssLOG_ERROR("substitutionsLocations.size(): " << substitutionsLocations.size()); - ssLOG_ERROR("substitutionsLengths.size(): " << substitutionsLengths.size()); - return false; - } - - return PerformSubstituionsWithInfo( substitutionMap, - escapedString, - foundSubstitutions, - substitutionsLocations, - substitutionsLengths, - inOutSubstitutedString); -} - -bool runcpp2::Data::StageInfo::ConstructCommand(const SubstitutionMap& substitutionMap, - const BuildType buildType, - std::string& outCommand) const -{ - ssLOG_FUNC_DEBUG(); - - - static_assert(static_cast(BuildType::COUNT) == 6, "Add new type to be processed"); - const std::unordered_map& currentOutputTypeMap = - buildType == BuildType::INTERNAL_EXECUTABLE_EXECUTABLE ? - OutputTypes.Executable : - ( - buildType == BuildType::STATIC ? - OutputTypes.Static : - ( - buildType == BuildType::INTERNAL_EXECUTABLE_SHARED ? - OutputTypes.ExecutableShared : - OutputTypes.Shared - ) - ); - - if(!runcpp2::HasValueFromPlatformMap(currentOutputTypeMap)) - { - ssLOG_ERROR("Failed to find RunParts for current platform"); - return false; - } - - const OutputTypeInfo* rawOutputTypeInfo = - runcpp2::GetValueFromPlatformMap(currentOutputTypeMap); - - if(rawOutputTypeInfo == nullptr) - { - ssLOG_ERROR("Failed to retrieve OutputTypeInfo"); - return false; - } - - const std::vector& currentRunParts = (*rawOutputTypeInfo).RunParts; - outCommand.clear(); - - for(int i = 0; i < currentRunParts.size(); ++i) - { - ssLOG_DEBUG("Parsing run part at index: " << i); - ssLOG_DEBUG("Which is: \"" << currentRunParts.at(i).CommandPart << "\""); - - std::string currentEscapedPart; - std::vector substitutionsInCurrentPart; - std::vector substitutionsLocations; - std::vector substitutionsLengths; - - GetEscapedStringAndExtractSubstitutions(currentRunParts.at(i).CommandPart, - currentEscapedPart, - substitutionsInCurrentPart, - substitutionsLocations, - substitutionsLengths); - - if( substitutionsInCurrentPart.size() != substitutionsLocations.size() || - substitutionsInCurrentPart.size() != substitutionsLengths.size()) - { - ssLOG_ERROR("Substitution size mismatch"); - ssLOG_ERROR("substitutionsInCurrentPart.size(): " << substitutionsInCurrentPart.size()); - ssLOG_ERROR("substitutionsLocations.size(): " << substitutionsLocations.size()); - ssLOG_ERROR("substitutionsLengths.size(): " << substitutionsLengths.size()); - return false; - } - - static_assert( static_cast(RunPart::RunType::COUNT) == 2, - "Update parsing for new runtype"); - - //Check RunType::ONCE if all the substitutions are present - if(currentRunParts.at(i).Type == RunPart::RunType::ONCE) - { - std::string substitutedPart; - - if(!PerformSubstituionsWithInfo(substitutionMap, - currentEscapedPart, - substitutionsInCurrentPart, - substitutionsLocations, - substitutionsLengths, - substitutedPart)) - { - return false; - } - - outCommand += substitutedPart; - } - //Check RunType::REPEATS, all the substitutions must have the same number of values. - //Otherwise, bail - else - { - int firstCount = -1; - - //If there are no substitution, report this - if(substitutionsInCurrentPart.empty()) - { - ssLOG_ERROR("There are no substitutions found in " << currentEscapedPart << - " but it is set to be of RunType::REPEATS."); - - ssLOG_ERROR("Substitutions are needed to determine " << - "how many times to repeat this part"); - - return false; - } - - //Check all the substitution found in the run part - for(int j = 0; j < substitutionsInCurrentPart.size(); ++j) - { - if(substitutionMap.count(substitutionsInCurrentPart.at(j)) == 0) - { - ssLOG_DEBUG("No substitution found for " << substitutionsInCurrentPart.at(j) << - " in " << currentRunParts.at(i).CommandPart); - - ssLOG_DEBUG("Current run part is type repeat, skipping to next"); - continue; - } - - int substitutionValuesCount = - substitutionMap.at(substitutionsInCurrentPart.at(j)).size(); - - if(firstCount == -1) - firstCount = substitutionValuesCount; - else if(substitutionValuesCount != firstCount) - { - ssLOG_ERROR("The number of substitution values found for " << - substitutionsInCurrentPart.at(j) << " which is " << - substitutionValuesCount << " does not match " << - "the number of other substitution values which is " << firstCount); - - return false; - } - } - - //Once we agreed on how many repeats we need to do based on - //the substitution values count, we can then do substitution. - for(int j = 0; j < firstCount; ++j) - { - std::string substitutedPart; - - if(!PerformSubstituionsWithInfo(substitutionMap, - currentEscapedPart, - substitutionsInCurrentPart, - substitutionsLocations, - substitutionsLengths, - substitutedPart, - j)) - { - return false; - } - - outCommand += substitutedPart; - } - } - } - - return true; -} - -bool runcpp2::Data::StageInfo::ParseYAML_Node( YAML::ConstNodePtr node, - std::string outputTypeKeyName) -{ - ssLOG_FUNC_DEBUG(); - - std::vector requirements = - { - NodeRequirement("PreRun", YAML::NodeType::Map, false, true), - NodeRequirement("CheckExistence", YAML::NodeType::Map, true, false), - NodeRequirement(outputTypeKeyName, YAML::NodeType::Map, true, false) - }; - - if(!CheckNodeRequirements(node, requirements)) - { - ssLOG_ERROR("StageInfo: Failed to meet requirements"); - return false; - } - - if(ExistAndHasChild(node, "PreRun")) - { - YAML::ConstNodePtr preRunNode = node->GetMapValueNode("PreRun"); - for(int i = 0; i < preRunNode->GetChildrenCount(); ++i) - { - std::string key = preRunNode->GetMapKeyScalarAt(i).DS_TRY_ACT(return false); - std::string value = preRunNode ->GetMapValueScalarAt(i) - .DS_TRY_ACT(return false); - PreRun[key] = value; - } - } - - //CheckExistence - { - YAML::ConstNodePtr checkExistenceNode = node->GetMapValueNode("CheckExistence"); - for(int i = 0; i < checkExistenceNode->GetChildrenCount(); ++i) - { - std::string key = checkExistenceNode->GetMapKeyScalarAt(i) - .DS_TRY_ACT(return false); - std::string value = checkExistenceNode ->GetMapValueScalarAt(i) - .DS_TRY_ACT(return false); - CheckExistence[key] = value; - } - } - - //OutputTypes - { - if(!ExistAndHasChild(node, outputTypeKeyName)) - { - ssLOG_ERROR("Failed to parse " << outputTypeKeyName); - return false; - } - - YAML::ConstNodePtr outputTypeNode = node->GetMapValueNode(outputTypeKeyName); - std::vector outputTypeRequirements = - { - NodeRequirement("Executable", YAML::NodeType::Map, true, false), - NodeRequirement("ExecutableShared", YAML::NodeType::Map, true, false), - NodeRequirement("Static", YAML::NodeType::Map, true, false), - NodeRequirement("Shared", YAML::NodeType::Map, true, false) - }; - - if(!CheckNodeRequirements(outputTypeNode, outputTypeRequirements)) - { - ssLOG_ERROR("Failed to parse " << outputTypeKeyName); - return false; - } - - std::vector outputTypeInfoRequirements = - { - NodeRequirement("Flags", YAML::NodeType::Scalar, true, true), - NodeRequirement("Executable", YAML::NodeType::Scalar, true, false), - NodeRequirement("RunParts", YAML::NodeType::Sequence, true, false), - NodeRequirement("Setup", YAML::NodeType::Sequence, false, false), - NodeRequirement("Cleanup", YAML::NodeType::Sequence, false, false), - NodeRequirement("ExpectedOutputFiles", YAML::NodeType::Sequence, true, false) - }; - - YAML::ConstNodePtr executableNode = outputTypeNode->GetMapValueNode("Executable"); - if(!ParseOutputTypes( "Executable", - executableNode, - outputTypeInfoRequirements, - OutputTypes.Executable)) - { - return false; - } - - YAML::ConstNodePtr executableSharedNode = outputTypeNode->GetMapValueNode("ExecutableShared"); - if(!ParseOutputTypes( "ExecutableShared", - executableSharedNode, - outputTypeInfoRequirements, - OutputTypes.ExecutableShared)) - { - return false; - } - - YAML::ConstNodePtr staticNode = outputTypeNode->GetMapValueNode("Static"); - if(!ParseOutputTypes("Static", staticNode, outputTypeInfoRequirements, OutputTypes.Static)) - return false; - - YAML::ConstNodePtr sharedNode = outputTypeNode->GetMapValueNode("Shared"); - if(!ParseOutputTypes("Shared", sharedNode, outputTypeInfoRequirements, OutputTypes.Shared)) - return false; - } - - return true; -} - -std::string runcpp2::Data::StageInfo::ToString( std::string indentation, - std::string outputTypeKeyName) const -{ - ssLOG_FUNC_DEBUG(); - - std::string out; - - if(!PreRun.empty()) - { - out += indentation + "PreRun:\n"; - for(auto it = PreRun.begin(); it != PreRun.end(); ++it) - out += indentation + " " + it->first + ": " + GetEscapedYAMLString(it->second) + "\n"; - } - - out += indentation + "CheckExistence:\n"; - for(auto it = CheckExistence.begin(); it != CheckExistence.end(); ++it) - out += indentation + " " + it->first + ": " + GetEscapedYAMLString(it->second) + "\n"; - - out += indentation + outputTypeKeyName + ":\n"; - - out += indentation + " Executable: \n"; - OutputTypeInfoMapToString(indentation + " ", OutputTypes.Executable, out); - - out += indentation + " ExecutableShared: \n"; - OutputTypeInfoMapToString(indentation + " ", OutputTypes.ExecutableShared, out); - - out += indentation + " Static: \n"; - OutputTypeInfoMapToString(indentation + " ", OutputTypes.Static, out); - - out += indentation + " Shared: \n"; - OutputTypeInfoMapToString(indentation + " ", OutputTypes.Shared, out); - - return out; -} - -bool runcpp2::Data::StageInfo::Equals(const StageInfo& other) const -{ - if( PreRun.size() != other.PreRun.size() || - CheckExistence.size() != other.CheckExistence.size()) - { - return false; - } - - for(const auto& it : PreRun) - { - if(other.PreRun.count(it.first) == 0 || other.PreRun.at(it.first) != it.second) - return false; - } - - for(const auto& it : CheckExistence) - { - if( other.CheckExistence.count(it.first) == 0 || - other.CheckExistence.at(it.first) != it.second) - { - return false; - } - } - - auto compareOutputTypeInfoMaps = - []( const std::unordered_map& a, - const std::unordered_map& b) -> bool - { - if(a.size() != b.size()) - return false; - - for(const auto& it : a) - { - if(b.count(it.first) == 0) - return false; - - const OutputTypeInfo& otherInfo = b.at(it.first); - const OutputTypeInfo& info = it.second; - - if( info.Flags != otherInfo.Flags || - info.Executable != otherInfo.Executable || - info.Setup != otherInfo.Setup || - info.Cleanup != otherInfo.Cleanup) - { - return false; - } - - if(info.RunParts.size() != otherInfo.RunParts.size()) - return false; - - for(size_t i = 0; i < info.RunParts.size(); ++i) - { - if( info.RunParts[i].Type != otherInfo.RunParts[i].Type || - info.RunParts[i].CommandPart != otherInfo.RunParts[i].CommandPart) - { - return false; - } - } - } - return true; - }; - - if( !compareOutputTypeInfoMaps(OutputTypes.Executable, other.OutputTypes.Executable) || - !compareOutputTypeInfoMaps(OutputTypes.ExecutableShared, other.OutputTypes.ExecutableShared) || - !compareOutputTypeInfoMaps(OutputTypes.Static, other.OutputTypes.Static) || - !compareOutputTypeInfoMaps(OutputTypes.Shared, other.OutputTypes.Shared)) - { - return false; - } - - return true; -} diff --git a/Src/runcpp2/Data/StageInfo.hpp b/Src/runcpp2/Data/StageInfo.hpp new file mode 100644 index 0000000..1535f5b --- /dev/null +++ b/Src/runcpp2/Data/StageInfo.hpp @@ -0,0 +1,870 @@ +#ifndef RUNCPP2_DATA_STAGE_INFO_HPP +#define RUNCPP2_DATA_STAGE_INFO_HPP + +#include "runcpp2/Data/ParseCommon.hpp" +#include "runcpp2/Data/BuildType.hpp" +#include "runcpp2/LibYAML_Wrapper.hpp" +#include "runcpp2/ParseUtil.hpp" +#include "runcpp2/PlatformUtil.hpp" + +#include "ssLogger/ssLog.hpp" +#include "DSResult/DSResult.hpp" + +#include +#include +#include +#include +#include +#include + +namespace runcpp2 +{ +namespace Data +{ + struct OutputTypeInfo; + using SubstitutionMap = std::unordered_map>; +} +} + +namespace +{ + using OutputTypeInfo = runcpp2::Data::OutputTypeInfo; + bool ParseOutputTypes( const std::string& subNodeKey, + runcpp2::YAML::ConstNodePtr outputTypesSubNode, + std::vector requirements, + std::unordered_map& outInfos); + + void OutputTypeInfoMapToString( const std::string& indentation, + const std::unordered_map< PlatformName, + OutputTypeInfo>& toStringMap, + std::string& outString); + + using SubMap = runcpp2::Data::SubstitutionMap; + bool PerformSubstituionsWithInfo( const SubMap& substitutionMap, + const std::string& escapedString, + const std::vector& foundSubstitutions, + const std::vector& substitutionsLocations, + const std::vector& substitutionsLengths, + std::string& inOutSubstitutedString, + int substituteValueIndex = 0); + + //NOTE: This extracts substitutions and also allow escapes to happen for substitution characters. + // To escape a substitution character, just repeat it. (i.e. {{text}} will be escaped as {text}) + void GetEscapedStringAndExtractSubstitutions( const std::string& processString, + std::string& outEscapedString, + std::vector& outFoundSubstitutions, + std::vector& outFoundLocations, + std::vector& outFoundLength) + { + ssLOG_FUNC_DEBUG(); + + outEscapedString.clear(); + std::string currentSubstitution; + + int lastOpenBracketIndex = -1; + for(int i = 0; i < processString.size(); ++i) + { + if(processString[i] == '{') + { + if(i == processString.size() - 1) + { + if(lastOpenBracketIndex == -1 || lastOpenBracketIndex != i - 1) + ssLOG_WARNING("Unescaped { at the end: " << processString); + + outEscapedString += '{'; + continue; + } + + //If we have opening bracket for the next character, + //this is an escaping character + if(processString[i + 1] == '{') + { + outEscapedString += '{'; + if(lastOpenBracketIndex != -1) + currentSubstitution += '{'; + + ++i; + continue; + } + + if(lastOpenBracketIndex != -1) + { + ssLOG_WARNING( "Unescaped { at index " << lastOpenBracketIndex << + ": " << processString); + } + + lastOpenBracketIndex = i; + currentSubstitution = "{"; + outEscapedString += '{'; + } + else if(processString[i] == '}') + { + //If we have closing bracket for the next character, + //this is an escaping character + if(i < processString.size() - 1 && processString[i + 1] == '}') + { + outEscapedString += '}'; + if(lastOpenBracketIndex != -1) + currentSubstitution += '}'; + + ++i; + continue; + } + + //If there's no open bracket, give warning + if(lastOpenBracketIndex == -1) + { + ssLOG_WARNING("Unescaped } at index " << i << ": " << processString); + continue; + } + + //Add substitution + outEscapedString += '}'; + currentSubstitution += '}'; + ssLOG_DEBUG("Substitution " << currentSubstitution << " found"); + outFoundSubstitutions.push_back(currentSubstitution); + outFoundLocations.push_back(outEscapedString.size() - currentSubstitution.size()); + outFoundLength.push_back(currentSubstitution.size()); + + //Reset + lastOpenBracketIndex = -1; + currentSubstitution.clear(); + } + //Normal characters + else + { + outEscapedString += processString[i]; + if(lastOpenBracketIndex != -1) + currentSubstitution += processString[i]; + } + } + } +} + +namespace runcpp2 +{ +namespace Data +{ + struct RunPart + { + enum class RunType + { + ONCE, + REPEATS, + COUNT + }; + + RunType Type; + std::string CommandPart; + }; + + struct OutputTypeInfo + { + std::string Flags; + std::string Executable; + + std::vector Setup; + std::vector Cleanup; + std::vector RunParts; + std::vector ExpectedOutputFiles; + }; + + struct StageInfo + { + std::unordered_map PreRun; + std::unordered_map CheckExistence; + + struct + { + std::unordered_map Executable; + std::unordered_map ExecutableShared; + std::unordered_map Static; + std::unordered_map Shared; + } OutputTypes; + + using SubstitutionMap = std::unordered_map>; + + //TODO: Make this static function? + inline bool PerformSubstituions(const SubstitutionMap& substitutionMap, + std::string& inOutSubstitutedString) const + { + std::string escapedString; + std::vector foundSubstitutions; + std::vector substitutionsLocations; + std::vector substitutionsLengths; + + GetEscapedStringAndExtractSubstitutions(inOutSubstitutedString, + escapedString, + foundSubstitutions, + substitutionsLocations, + substitutionsLengths); + + if( foundSubstitutions.size() != substitutionsLocations.size() || + foundSubstitutions.size() != substitutionsLengths.size()) + { + ssLOG_ERROR("Substitution size mismatch"); + ssLOG_ERROR("foundSubstitutions.size(): " << foundSubstitutions.size()); + ssLOG_ERROR("substitutionsLocations.size(): " << substitutionsLocations.size()); + ssLOG_ERROR("substitutionsLengths.size(): " << substitutionsLengths.size()); + return false; + } + + return PerformSubstituionsWithInfo( substitutionMap, + escapedString, + foundSubstitutions, + substitutionsLocations, + substitutionsLengths, + inOutSubstitutedString); + } + + inline bool ConstructCommand( const SubstitutionMap& substitutionMap, + const BuildType buildType, + std::string& outCommand) const + { + ssLOG_FUNC_DEBUG(); + + static_assert(static_cast(BuildType::COUNT) == 6, "Add new type to be processed"); + const std::unordered_map& currentOutputTypeMap = + buildType == BuildType::INTERNAL_EXECUTABLE_EXECUTABLE ? + OutputTypes.Executable : + ( + buildType == BuildType::STATIC ? + OutputTypes.Static : + ( + buildType == BuildType::INTERNAL_EXECUTABLE_SHARED ? + OutputTypes.ExecutableShared : + OutputTypes.Shared + ) + ); + + if(!runcpp2::HasValueFromPlatformMap(currentOutputTypeMap)) + { + ssLOG_ERROR("Failed to find RunParts for current platform"); + return false; + } + + const OutputTypeInfo* rawOutputTypeInfo = + runcpp2::GetValueFromPlatformMap(currentOutputTypeMap); + + if(rawOutputTypeInfo == nullptr) + { + ssLOG_ERROR("Failed to retrieve OutputTypeInfo"); + return false; + } + + const std::vector& currentRunParts = (*rawOutputTypeInfo).RunParts; + outCommand.clear(); + + for(int i = 0; i < currentRunParts.size(); ++i) + { + ssLOG_DEBUG("Parsing run part at index: " << i); + ssLOG_DEBUG("Which is: \"" << currentRunParts.at(i).CommandPart << "\""); + + std::string currentEscapedPart; + std::vector substitutionsInCurrentPart; + std::vector substitutionsLocations; + std::vector substitutionsLengths; + + GetEscapedStringAndExtractSubstitutions(currentRunParts.at(i).CommandPart, + currentEscapedPart, + substitutionsInCurrentPart, + substitutionsLocations, + substitutionsLengths); + + if( substitutionsInCurrentPart.size() != substitutionsLocations.size() || + substitutionsInCurrentPart.size() != substitutionsLengths.size()) + { + ssLOG_ERROR("Substitution size mismatch"); + ssLOG_ERROR("substitutionsInCurrentPart.size(): " << substitutionsInCurrentPart.size()); + ssLOG_ERROR("substitutionsLocations.size(): " << substitutionsLocations.size()); + ssLOG_ERROR("substitutionsLengths.size(): " << substitutionsLengths.size()); + return false; + } + + static_assert( static_cast(RunPart::RunType::COUNT) == 2, + "Update parsing for new runtype"); + + //Check RunType::ONCE if all the substitutions are present + if(currentRunParts.at(i).Type == RunPart::RunType::ONCE) + { + std::string substitutedPart; + + if(!PerformSubstituionsWithInfo(substitutionMap, + currentEscapedPart, + substitutionsInCurrentPart, + substitutionsLocations, + substitutionsLengths, + substitutedPart)) + { + return false; + } + + outCommand += substitutedPart; + } + //Check RunType::REPEATS, all the substitutions must have the same number of values. + //Otherwise, bail + else + { + int firstCount = -1; + + //If there are no substitution, report this + if(substitutionsInCurrentPart.empty()) + { + ssLOG_ERROR("There are no substitutions found in " << currentEscapedPart << + " but it is set to be of RunType::REPEATS."); + + ssLOG_ERROR("Substitutions are needed to determine " << + "how many times to repeat this part"); + + return false; + } + + //Check all the substitution found in the run part + for(int j = 0; j < substitutionsInCurrentPart.size(); ++j) + { + if(substitutionMap.count(substitutionsInCurrentPart.at(j)) == 0) + { + ssLOG_DEBUG("No substitution found for " << substitutionsInCurrentPart.at(j) << + " in " << currentRunParts.at(i).CommandPart); + + ssLOG_DEBUG("Current run part is type repeat, skipping to next"); + continue; + } + + int substitutionValuesCount = + substitutionMap.at(substitutionsInCurrentPart.at(j)).size(); + + if(firstCount == -1) + firstCount = substitutionValuesCount; + else if(substitutionValuesCount != firstCount) + { + ssLOG_ERROR("The number of substitution values found for " << + substitutionsInCurrentPart.at(j) << " which is " << + substitutionValuesCount << " does not match " << + "the number of other substitution values which is " << + firstCount); + + return false; + } + } + + //Once we agreed on how many repeats we need to do based on + //the substitution values count, we can then do substitution. + for(int j = 0; j < firstCount; ++j) + { + std::string substitutedPart; + + if(!PerformSubstituionsWithInfo(substitutionMap, + currentEscapedPart, + substitutionsInCurrentPart, + substitutionsLocations, + substitutionsLengths, + substitutedPart, + j)) + { + return false; + } + + outCommand += substitutedPart; + } + } + } + + return true; + } + + inline bool ParseYAML_Node(YAML::ConstNodePtr node, std::string outputTypeKeyName) + { + ssLOG_FUNC_DEBUG(); + + std::vector requirements = + { + NodeRequirement("PreRun", YAML::NodeType::Map, false, true), + NodeRequirement("CheckExistence", YAML::NodeType::Map, true, false), + NodeRequirement(outputTypeKeyName, YAML::NodeType::Map, true, false) + }; + + if(!CheckNodeRequirements(node, requirements)) + { + ssLOG_ERROR("StageInfo: Failed to meet requirements"); + return false; + } + + if(ExistAndHasChild(node, "PreRun")) + { + YAML::ConstNodePtr preRunNode = node->GetMapValueNode("PreRun"); + for(int i = 0; i < preRunNode->GetChildrenCount(); ++i) + { + std::string key = preRunNode->GetMapKeyScalarAt(i) + .DS_TRY_ACT(return false); + std::string value = preRunNode ->GetMapValueScalarAt(i) + .DS_TRY_ACT(return false); + PreRun[key] = value; + } + } + + //CheckExistence + { + YAML::ConstNodePtr checkExistenceNode = node->GetMapValueNode("CheckExistence"); + for(int i = 0; i < checkExistenceNode->GetChildrenCount(); ++i) + { + std::string key = checkExistenceNode->GetMapKeyScalarAt(i) + .DS_TRY_ACT(return false); + std::string value = checkExistenceNode ->GetMapValueScalarAt(i) + .DS_TRY_ACT(return false); + CheckExistence[key] = value; + } + } + + //OutputTypes + { + if(!ExistAndHasChild(node, outputTypeKeyName)) + { + ssLOG_ERROR("Failed to parse " << outputTypeKeyName); + return false; + } + + YAML::ConstNodePtr outputTypeNode = node->GetMapValueNode(outputTypeKeyName); + std::vector outputTypeRequirements = + { + NodeRequirement("Executable", YAML::NodeType::Map, true, false), + NodeRequirement("ExecutableShared", YAML::NodeType::Map, true, false), + NodeRequirement("Static", YAML::NodeType::Map, true, false), + NodeRequirement("Shared", YAML::NodeType::Map, true, false) + }; + + if(!CheckNodeRequirements(outputTypeNode, outputTypeRequirements)) + { + ssLOG_ERROR("Failed to parse " << outputTypeKeyName); + return false; + } + + std::vector outputTypeInfoRequirements = + { + NodeRequirement("Flags", YAML::NodeType::Scalar, true, true), + NodeRequirement("Executable", YAML::NodeType::Scalar, true, false), + NodeRequirement("RunParts", YAML::NodeType::Sequence, true, false), + NodeRequirement("Setup", YAML::NodeType::Sequence, false, false), + NodeRequirement("Cleanup", YAML::NodeType::Sequence, false, false), + NodeRequirement("ExpectedOutputFiles", YAML::NodeType::Sequence, true, false) + }; + + YAML::ConstNodePtr executableNode = outputTypeNode->GetMapValueNode("Executable"); + if(!ParseOutputTypes( "Executable", + executableNode, + outputTypeInfoRequirements, + OutputTypes.Executable)) + { + return false; + } + + YAML::ConstNodePtr executableSharedNode = + outputTypeNode->GetMapValueNode("ExecutableShared"); + if(!ParseOutputTypes( "ExecutableShared", + executableSharedNode, + outputTypeInfoRequirements, + OutputTypes.ExecutableShared)) + { + return false; + } + + YAML::ConstNodePtr staticNode = outputTypeNode->GetMapValueNode("Static"); + if(!ParseOutputTypes( "Static", + staticNode, + outputTypeInfoRequirements, + OutputTypes.Static)) + { + return false; + } + + YAML::ConstNodePtr sharedNode = outputTypeNode->GetMapValueNode("Shared"); + if(!ParseOutputTypes( "Shared", + sharedNode, + outputTypeInfoRequirements, + OutputTypes.Shared)) + { + return false; + } + } + + return true; + } + + inline std::string ToString(std::string indentation, std::string outputTypeKeyName) const + { + ssLOG_FUNC_DEBUG(); + + std::string out; + + if(!PreRun.empty()) + { + out += indentation + "PreRun:\n"; + for(auto it = PreRun.begin(); it != PreRun.end(); ++it) + { + out += indentation + " " + it->first + ": " + + GetEscapedYAMLString(it->second) + "\n"; + } + } + + out += indentation + "CheckExistence:\n"; + for(auto it = CheckExistence.begin(); it != CheckExistence.end(); ++it) + { + out += indentation + " " + it->first + ": " + + GetEscapedYAMLString(it->second) + "\n"; + } + + out += indentation + outputTypeKeyName + ":\n"; + + out += indentation + " Executable: \n"; + OutputTypeInfoMapToString(indentation + " ", OutputTypes.Executable, out); + + out += indentation + " ExecutableShared: \n"; + OutputTypeInfoMapToString(indentation + " ", OutputTypes.ExecutableShared, out); + + out += indentation + " Static: \n"; + OutputTypeInfoMapToString(indentation + " ", OutputTypes.Static, out); + + out += indentation + " Shared: \n"; + OutputTypeInfoMapToString(indentation + " ", OutputTypes.Shared, out); + + return out; + } + + inline bool Equals(const StageInfo& other) const + { + if( PreRun.size() != other.PreRun.size() || + CheckExistence.size() != other.CheckExistence.size()) + { + return false; + } + + for(const auto& it : PreRun) + { + if(other.PreRun.count(it.first) == 0 || other.PreRun.at(it.first) != it.second) + return false; + } + + for(const auto& it : CheckExistence) + { + if( other.CheckExistence.count(it.first) == 0 || + other.CheckExistence.at(it.first) != it.second) + { + return false; + } + } + + auto compareOutputTypeInfoMaps = + []( const std::unordered_map& a, + const std::unordered_map& b) -> bool + { + if(a.size() != b.size()) + return false; + + for(const auto& it : a) + { + if(b.count(it.first) == 0) + return false; + + const OutputTypeInfo& otherInfo = b.at(it.first); + const OutputTypeInfo& info = it.second; + + if( info.Flags != otherInfo.Flags || + info.Executable != otherInfo.Executable || + info.Setup != otherInfo.Setup || + info.Cleanup != otherInfo.Cleanup) + { + return false; + } + + if(info.RunParts.size() != otherInfo.RunParts.size()) + return false; + + for(size_t i = 0; i < info.RunParts.size(); ++i) + { + if( info.RunParts[i].Type != otherInfo.RunParts[i].Type || + info.RunParts[i].CommandPart != otherInfo.RunParts[i].CommandPart) + { + return false; + } + } + } + return true; + }; + + if( !compareOutputTypeInfoMaps(OutputTypes.Executable, other.OutputTypes.Executable) || + !compareOutputTypeInfoMaps( OutputTypes.ExecutableShared, + other.OutputTypes.ExecutableShared) || + !compareOutputTypeInfoMaps(OutputTypes.Static, other.OutputTypes.Static) || + !compareOutputTypeInfoMaps(OutputTypes.Shared, other.OutputTypes.Shared)) + { + return false; + } + + return true; + } + }; +} +} + +namespace +{ + using OutputTypeInfo = runcpp2::Data::OutputTypeInfo; + bool ParseOutputTypes( const std::string& subNodeKey, + runcpp2::YAML::ConstNodePtr outputTypesSubNode, + std::vector requirements, + std::unordered_map& outInfos) + { + ssLOG_FUNC_DEBUG(); + using namespace runcpp2; + using namespace runcpp2::Data; + + const std::string& subNodeName = subNodeKey; + + for(int i = 0; i < outputTypesSubNode->GetChildrenCount(); ++i) + { + YAML::ConstNodePtr currentPlatformNode = outputTypesSubNode->GetMapValueNodeAt(i); + std::string platformName = outputTypesSubNode ->GetMapKeyScalarAt(i) + .DS_TRY_ACT(return false); + if(!CheckNodeRequirements(currentPlatformNode, requirements)) + { + ssLOG_ERROR("Failed to parse outputTypesSubNode in " << subNodeName << + " for platform " << platformName); + return false; + } + + outInfos[platformName].Flags = + currentPlatformNode->GetMapValueScalar("Flags").DS_TRY_ACT(return false); + + outInfos[platformName].Executable = + currentPlatformNode ->GetMapValueScalar("Executable") + .DS_TRY_ACT(return false); + + //RunParts + if(!ExistAndHasChild(currentPlatformNode, "RunParts")) + { + ssLOG_ERROR("Failed to parse RunParts"); + return false; + } + + YAML::ConstNodePtr runPartsNode = currentPlatformNode->GetMapValueNode("RunParts"); + for(int j = 0; j < runPartsNode->GetChildrenCount(); ++j) + { + YAML::ConstNodePtr currentPartNode = runPartsNode->GetSequenceChildNode(j); + + std::vector currentRunPartRequirements = + { + NodeRequirement("Type", YAML::NodeType::Scalar, true, false), + NodeRequirement("CommandPart", YAML::NodeType::Scalar, true, false) + }; + + if(!CheckNodeRequirements(currentPartNode, currentRunPartRequirements)) + { + ssLOG_ERROR("Failed to parse RunPart at index " << j << " for " << platformName); + return false; + } + + outInfos[platformName].RunParts.push_back({}); + std::string currentType = currentPartNode ->GetMapValueScalar("Type") + .DS_TRY_ACT(return false); + static_assert( static_cast(RunPart::RunType::COUNT) == 2, + "Add new RunType"); + + if(currentType == "Once") + outInfos[platformName].RunParts.back().Type = RunPart::RunType::ONCE; + else if(currentType == "Repeats") + outInfos[platformName].RunParts.back().Type = RunPart::RunType::REPEATS; + else + { + ssLOG_WARNING( "Invalid RunPart type " << currentType << " at index " << j << + " for " << platformName); + ssLOG_DEBUG("Defaulting to Once"); + outInfos[platformName].RunParts.back().Type = RunPart::RunType::ONCE; + } + + outInfos[platformName].RunParts.back().CommandPart = + currentPartNode ->GetMapValueScalar("CommandPart") + .DS_TRY_ACT(return false); + } + + //Setup + if(ExistAndHasChild(currentPlatformNode, "Setup")) + { + YAML::ConstNodePtr setupNode = currentPlatformNode->GetMapValueNode("Setup"); + for(int j = 0; j < setupNode->GetChildrenCount(); ++j) + { + std::string setupVal = setupNode->GetSequenceChildScalar(j) + .DS_TRY_ACT(return false); + outInfos[platformName].Setup.push_back(setupVal); + } + } + + //Cleanup + if(ExistAndHasChild(currentPlatformNode, "Cleanup")) + { + YAML::ConstNodePtr cleanupNode = currentPlatformNode->GetMapValueNode("Cleanup"); + for(int j = 0; j < cleanupNode->GetChildrenCount(); ++j) + { + std::string cleanupVal = cleanupNode->GetSequenceChildScalar(j) + .DS_TRY_ACT(return false); + outInfos[platformName].Cleanup.push_back(cleanupVal); + } + } + + //ExpectedOutputFiles + YAML::ConstNodePtr expectedOutputFilesNode = + currentPlatformNode->GetMapValueNode("ExpectedOutputFiles"); + for(int j = 0; j < expectedOutputFilesNode->GetChildrenCount(); ++j) + { + std::string outputFileVal = + expectedOutputFilesNode ->GetSequenceChildScalar(j) + .DS_TRY_ACT(return false); + outInfos[platformName].ExpectedOutputFiles.push_back(outputFileVal); + } + } + + return true; + } + + void OutputTypeInfoMapToString( const std::string& indentation, + const std::unordered_map< PlatformName, + OutputTypeInfo>& toStringMap, + std::string& outString) + { + using namespace runcpp2; + using namespace runcpp2::Data; + + for(auto it = toStringMap.begin(); it != toStringMap.end(); ++it) + { + outString += indentation + " " + it->first + ": \n"; + outString += indentation + " Flags: " + + GetEscapedYAMLString(it->second.Flags) + "\n"; + outString += indentation + " Executable: " + + GetEscapedYAMLString(it->second.Executable) + "\n"; + outString += indentation + " RunParts: \n"; + for(int i = 0; i < it->second.RunParts.size(); ++i) + { + static_assert(static_cast(RunPart::RunType::COUNT) == 2, "Add new RunType"); + + RunPart::RunType currentType = it->second.RunParts.at(i).Type; + outString += indentation + + " - Type: " + + (currentType == RunPart::RunType::ONCE ? + "Once" : + "Repeats") + + "\n"; + + outString += indentation + " CommandPart: " + + GetEscapedYAMLString(it->second.RunParts.at(i).CommandPart) + "\n"; + } + + outString += indentation + " ExpectedOutputFiles: \n"; + for(int i = 0; i < it->second.ExpectedOutputFiles.size(); ++i) + { + outString += indentation + " - " + + GetEscapedYAMLString(it->second.ExpectedOutputFiles.at(i)) + "\n"; + } + + if(!it->second.Setup.empty()) + { + outString += indentation + " Setup: \n"; + for(int i = 0; i < it->second.Setup.size(); ++i) + { + outString += indentation + " - " + + GetEscapedYAMLString(it->second.Setup.at(i)) + "\n"; + } + } + + if(!it->second.Cleanup.empty()) + { + outString += indentation + " Cleanup: \n"; + for(int i = 0; i < it->second.Cleanup.size(); ++i) + { + outString += indentation + " - " + + GetEscapedYAMLString(it->second.Cleanup.at(i)) + "\n"; + } + } + } + } + + using SubMap = runcpp2::Data::SubstitutionMap; + bool PerformSubstituionsWithInfo( const SubMap& substitutionMap, + const std::string& escapedString, + const std::vector& foundSubstitutions, + const std::vector& substitutionsLocations, + const std::vector& substitutionsLengths, + std::string& inOutSubstitutedString, + int substituteValueIndex) + { + ssLOG_FUNC_DEBUG(); + + inOutSubstitutedString = escapedString; + + for(int i = foundSubstitutions.size() - 1; i >= 0; --i) + { + const std::string& substitution = foundSubstitutions.at(i); + if(substitutionMap.count(substitution) == 0) + { + ssLOG_ERROR("INTERNAL ERROR, missing substitution value for \"" << substitution << "\""); + return false; + } + + std::string currentValue = substitutionMap.at(substitution).at(substituteValueIndex); + + //Escape escapes character at the end if any + { + std::vector escapeChars = {'\\'}; + #ifdef _WIN32 + escapeChars.emplace_back('^'); + #endif + if(!currentValue.empty()) + { + int currentValIndex = currentValue.size(); + while(currentValIndex > 0) + { + --currentValIndex; + bool found = false; + for(int j = 0; j < escapeChars.size(); ++j) + { + if(currentValue[currentValIndex] == escapeChars[j]) + { + found = true; + break; + } + } + + if(!found) + { + ++currentValIndex; + break; + } + } + + if(currentValIndex < currentValue.size()) + { + const std::string foundEscapes = currentValue.substr(currentValIndex); + std::string newEndEscapes; + //Just repeat the escape characters + for(int j = 0; j < foundEscapes.size(); ++j) + { + newEndEscapes.push_back(foundEscapes[j]); + newEndEscapes.push_back(foundEscapes[j]); + } + + currentValue = currentValue.substr(0, currentValIndex) + newEndEscapes; + } + } + } + + ssLOG_DEBUG("Replacing \"" << substitution << "\" with \"" << currentValue << + "\" in \"" << escapedString << "\""); + + inOutSubstitutedString.replace( substitutionsLocations.at(i), + substitutionsLengths.at(i), + currentValue); + } + + return true; + } +} + +#endif diff --git a/Include/runcpp2/Data/SubmoduleInitType.hpp b/Src/runcpp2/Data/SubmoduleInitType.hpp similarity index 100% rename from Include/runcpp2/Data/SubmoduleInitType.hpp rename to Src/runcpp2/Data/SubmoduleInitType.hpp diff --git a/Include/runcpp2/DeferUtil.hpp b/Src/runcpp2/DeferUtil.hpp similarity index 100% rename from Include/runcpp2/DeferUtil.hpp rename to Src/runcpp2/DeferUtil.hpp diff --git a/Src/runcpp2/DependenciesHelper.cpp b/Src/runcpp2/DependenciesHelper.cpp deleted file mode 100644 index f4d291e..0000000 --- a/Src/runcpp2/DependenciesHelper.cpp +++ /dev/null @@ -1,1285 +0,0 @@ -#include "runcpp2/DependenciesHelper.hpp" -#include "runcpp2/PlatformUtil.hpp" -#include "runcpp2/StringUtil.hpp" -#include "runcpp2/ParseUtil.hpp" -#include "runcpp2/DeferUtil.hpp" - -#include "ghc/filesystem.hpp" -#include "ssLogger/ssLog.hpp" - -#include -#include -#include - -namespace -{ - DS::Result GetDependencyPath( const runcpp2::Data::DependencyInfo& dependency, - const ghc::filesystem::path& scriptPath, - const ghc::filesystem::path& buildDir, - ghc::filesystem::path& outCopyPath, - ghc::filesystem::path& outSourcePath) - { - ssLOG_FUNC_INFO(); - - ghc::filesystem::path scriptDirectory = scriptPath.parent_path(); - - const runcpp2::Data::DependencySource& currentSource = dependency.Source; - - if(mpark::get_if(¤tSource.Source)) - { - const runcpp2::Data::GitSource* git = - mpark::get_if(¤tSource.Source); - - size_t lastSlashFoundIndex = git->URL.find_last_of("/"); - size_t lastDotGitFoundIndex = git->URL.find_last_of(".git"); - - if( lastSlashFoundIndex == std::string::npos || - lastDotGitFoundIndex == std::string::npos || - lastDotGitFoundIndex < lastSlashFoundIndex) - { - return DS_ERROR_MSG(DS_STR("Invalid git url: ") + git->URL); - } - else - { - std::string gitRepoName = - //+1 for / to not include it - git->URL.substr(lastSlashFoundIndex + 1, - //-1 for slash - lastDotGitFoundIndex - 1 - - //-(size - 1) for .git - (std::string(".git").size() - 1) - - lastSlashFoundIndex); - - outCopyPath = (buildDir / gitRepoName); - outSourcePath.clear(); - } - } - else if(mpark::get_if(¤tSource.Source)) - { - const runcpp2::Data::LocalSource* local = - mpark::get_if(¤tSource.Source); - - std::string localDepDirectoryName; - std::string curPath = local->Path; - - if(curPath.back() == '/') - curPath.pop_back(); - - localDepDirectoryName = ghc::filesystem::path(curPath).filename().string(); - - if(ghc::filesystem::path(curPath).is_relative()) - outSourcePath = (scriptDirectory / local->Path); - else - outSourcePath = (local->Path); - - if(!ghc::filesystem::is_directory(outSourcePath)) - { - return DS_ERROR_MSG(DS_STR( "Local dependency path is not a directory: ") + - DS_STR(outSourcePath)); - } - - if(currentSource.ImportPath.empty()) - outCopyPath = (buildDir / localDepDirectoryName); - else - outCopyPath = outSourcePath; - } - - return {}; - } - - DS::Result PopulateLocalDependency( const runcpp2::Data::DependencyInfo& dependency, - const ghc::filesystem::path& copyPath, - const ghc::filesystem::path& sourcePath, - const ghc::filesystem::path& buildDir, - bool& outPrePopulated) - { - ssLOG_FUNC_DEBUG(); - - std::error_code e; - - if(ghc::filesystem::exists(copyPath, e)) - { - if(!ghc::filesystem::is_directory(copyPath, e)) - { - return DS_ERROR_MSG("Dependency path is a file: " + copyPath.string() + "\n" + - "It should be a folder instead"); - } - outPrePopulated = true; - return {}; - } - else - { - if(mpark::get_if(&(dependency.Source.Source))) - { - const runcpp2::Data::GitSource* git = - mpark::get_if(&(dependency.Source.Source)); - - std::string submoduleString; - static_assert( static_cast(runcpp2::Data::SubmoduleInitType::COUNT) == 3, - "Add new type to be processed"); - switch(git->CurrentSubmoduleInitType) - { - case runcpp2::Data::SubmoduleInitType::NONE: - break; - case runcpp2::Data::SubmoduleInitType::SHALLOW: - submoduleString = "--recursive --shallow-submodules "; - break; - case runcpp2::Data::SubmoduleInitType::FULL: - submoduleString = "--recursive "; - break; - default: - { - return DS_ERROR_MSG("Invalid git submodule init type: " + - DS_STR(static_cast(git->CurrentSubmoduleInitType))); - } - } - - std::string gitCloneCommand = - std::string("git clone ") + - submoduleString + - (git->FullHistory ? "" : "--depth 1 ") + - ( - git->Branch.empty() ? - std::string("") : - std::string("--branch ") + git->Branch + " " - ) + - git->URL; - - ssLOG_INFO("Running git clone command: " << gitCloneCommand << " in " << - buildDir.string()); - - int returnCode = 0; - std::string output; - if(!runcpp2::RunCommand(gitCloneCommand, - false, - buildDir.string(), - output, - returnCode)) - { - return DS_ERROR_MSG("Failed to run git clone with result: " + DS_STR(returnCode) + - "\nWas trying to run: " + gitCloneCommand); - } - //else - // ssLOG_INFO("Output: \n" << output); - } - else if(mpark::get_if(&(dependency.Source.Source))) - { - //Copy/link local directory if it doesn't have any import path - if(dependency.Source.ImportPath.empty()) - { - runcpp2::SyncLocalDependency(dependency, sourcePath, copyPath).DS_TRY(); - } - } - - outPrePopulated = false; - return {}; - } - } - - DS::Result - PopulateLocalDependencies( const std::vector& dependencies, - const std::vector& dependenciesCopiesPaths, - const std::vector& dependenciesSourcesPaths, - const ghc::filesystem::path& buildDir, - std::vector& outPrePopulated) - { - ssLOG_FUNC_INFO(); - - outPrePopulated.resize(dependencies.size()); - - for(int i = 0; i < dependencies.size(); ++i) - { - if(!dependencies.at(i)->Source.ImportPath.empty()) - return DS_ERROR_MSG("Dependency import not resolved before populating."); - - bool prePopulated = false; - PopulateLocalDependency(*dependencies.at(i), - ghc::filesystem::path(dependenciesCopiesPaths.at(i)), - ghc::filesystem::path(dependenciesSourcesPaths.at(i)), - buildDir, - prePopulated).DS_TRY(); - outPrePopulated.at(i) = prePopulated; - } - - return {}; - } - - DS::Result - PopulateAbsoluteIncludePaths( std::vector& dependencies, - const std::vector& dependenciesCopiesPaths) - { - //Append absolute include paths from relative include paths - for(int i = 0; i < dependencies.size(); ++i) - { - dependencies.at(i)->AbsoluteIncludePaths.clear(); - for(int j = 0; j < dependencies.at(i)->IncludePaths.size(); ++j) - { - if(ghc::filesystem::path(dependencies.at(i)->IncludePaths.at(j)).is_absolute()) - { - return DS_ERROR_MSG("Dependency include path cannot be absolute: " + - dependencies.at(i)->IncludePaths.at(j)); - } - - dependencies.at(i) - ->AbsoluteIncludePaths - .push_back - ( - ghc::filesystem::absolute - ( - dependenciesCopiesPaths.at(i) + - "/" + - dependencies.at(i)->IncludePaths.at(j) - ) - .string() - ); - - ssLOG_DEBUG("Include path added: " << dependencies.at(i)->AbsoluteIncludePaths.back()); - } - } - - return {}; - } - - DS::Result - RunDependenciesSteps( const runcpp2::Data::Profile& profile, - const std::unordered_map< PlatformName, - runcpp2::Data::ProfilesCommands> steps, - const std::string& dependenciesCopiedDirectory, - bool required, - bool redirectIO) - { - ssLOG_FUNC_INFO(); - - if(steps.empty()) - return {}; - - //Find the platform name we use for setup - if(!runcpp2::HasValueFromPlatformMap(steps)) - { - if(required) - return DS_ERROR_MSG("Failed to find steps for current platform"); - else - return {}; - } - - const runcpp2::Data::ProfilesCommands& dependencySteps = - *runcpp2::GetValueFromPlatformMap(steps); - - const std::vector* commands = - runcpp2::GetValueFromProfileMap(profile, dependencySteps.CommandSteps); - - if(!commands) - { - return DS_ERROR_MSG(DS_STR( "Failed to find steps for profile ") + profile.Name + - " for current platform"); - } - - //Run the commands - for(int k = 0; k < commands->size(); ++k) - { - std::string processedDependencyPath = runcpp2::ProcessPath(dependenciesCopiedDirectory); - ssLOG_INFO("Running command: " << commands->at(k) << " in " << processedDependencyPath); - - int returnCode = 0; - std::string output; - - //TODO: Change to ds result - if(!runcpp2::RunCommand(commands->at(k), - redirectIO, - processedDependencyPath, - output, - returnCode)) - { - std::string errorMsg = - DS_STR("Failed to run command with result: ") + DS_STR(returnCode) + "\n" - "Was trying to run: " + commands->at(k); - if(redirectIO) - errorMsg += DS_STR("\nOutput: \n") + output; - return DS_ERROR_MSG(errorMsg); - } - else - { - if(redirectIO) - ssLOG_INFO("Output: \n" << output); - } - } - - return {}; - } - - bool GetDependencyBinariesExtensionsToLink( const runcpp2::Data::DependencyInfo& dependencyInfo, - const runcpp2::Data::Profile& profile, - std::vector& outExtensionsToLink) - { - static_assert((int)runcpp2::Data::DependencyLibraryType::COUNT == 4, ""); - switch(dependencyInfo.LibraryType) - { - case runcpp2::Data::DependencyLibraryType::STATIC: - { - if(!runcpp2::HasValueFromPlatformMap(profile.FilesTypes.StaticLinkFile.Extension)) - { - ssLOG_ERROR("Failed to find static library extensions for dependency " << - dependencyInfo.Name); - - return false; - } - - outExtensionsToLink.push_back( - *runcpp2::GetValueFromPlatformMap(profile .FilesTypes - .StaticLinkFile - .Extension)); - - break; - } - case runcpp2::Data::DependencyLibraryType::SHARED: - { - if( !runcpp2::HasValueFromPlatformMap(profile.FilesTypes.SharedLinkFile.Extension) || - !runcpp2::HasValueFromPlatformMap(profile.FilesTypes.SharedLibraryFile.Extension)) - { - ssLOG_ERROR("Failed to find shared library extensions for dependency " << - dependencyInfo.Name); - - return false; - } - - outExtensionsToLink.push_back( - *runcpp2::GetValueFromPlatformMap(profile.FilesTypes.SharedLinkFile.Extension)); - - if( *runcpp2::GetValueFromPlatformMap(profile.FilesTypes.SharedLinkFile.Extension) != - *runcpp2::GetValueFromPlatformMap(profile.FilesTypes.SharedLibraryFile.Extension)) - { - outExtensionsToLink.push_back( - *runcpp2::GetValueFromPlatformMap(profile .FilesTypes - .SharedLibraryFile - .Extension)); - } - - break; - } - case runcpp2::Data::DependencyLibraryType::OBJECT: - { - if(!runcpp2::HasValueFromPlatformMap(profile.FilesTypes.ObjectLinkFile.Extension)) - { - ssLOG_ERROR("Failed to find object file extensions for dependency " << - dependencyInfo.Name); - - return false; - } - - outExtensionsToLink.push_back( - *runcpp2::GetValueFromPlatformMap(profile.FilesTypes.ObjectLinkFile.Extension)); - - break; - } - case runcpp2::Data::DependencyLibraryType::HEADER: - break; - default: - ssLOG_ERROR("Invalid library type: " << (int)dependencyInfo.LibraryType); - return false; - } - - return true; - } - - ghc::filesystem::path ResolveSymlink(const ghc::filesystem::path& path, std::error_code& ec) - { - ghc::filesystem::path resolvedPath = ghc::filesystem::canonical(path, ec); - if(ec) - return path; // Return original path if canonical fails - - return resolvedPath; - } -} - -DS::Result -runcpp2::GetDependenciesPaths( const std::vector& availableDependencies, - std::vector& outCopiesPaths, - std::vector& outSourcesPaths, - const ghc::filesystem::path& scriptPath, - const ghc::filesystem::path& buildDir) -{ - ssLOG_FUNC_INFO(); - - for(int i = 0; i < availableDependencies.size(); ++i) - { - ghc::filesystem::path currentCopyPath; - ghc::filesystem::path currentSourcePath; - GetDependencyPath( *availableDependencies.at(i), - scriptPath, - buildDir, - currentCopyPath, - currentSourcePath).DS_TRY(); - - outCopiesPaths.push_back(currentCopyPath.string()); - outSourcesPaths.push_back(currentSourcePath.string()); - } - - return {}; -} - -bool runcpp2::IsDependencyAvailableForThisPlatform(const Data::DependencyInfo& dependency) -{ - std::vector platformNames = GetPlatformNames(); - - for(int i = 0; i < platformNames.size(); ++i) - { - if(dependency.Platforms.find(platformNames.at(i)) != dependency.Platforms.end()) - return true; - } - - return false; -} - -DS::Result -runcpp2::CleanupDependencies( const runcpp2::Data::Profile& profile, - const Data::ScriptInfo& scriptInfo, - const std::vector& availableDependencies, - const std::vector& dependenciesLocalCopiesPaths, - const std::string& dependenciesToReset) -{ - ssLOG_FUNC_DEBUG(); - - //If the script info is not populated (i.e. empty script info), don't do anything - if(!scriptInfo.Populated) - return {}; - - DS_ASSERT_EQ(availableDependencies.size(), dependenciesLocalCopiesPaths.size()); - - //Split dependency names if not "all" - std::unordered_set dependencyNames; - if(dependenciesToReset != "all") - { - std::string currentName; - for(int i = 0; i < dependenciesToReset.size(); ++i) - { - if(dependenciesToReset[i] == ',' || i == dependenciesToReset.size() - 1) - { - if(dependenciesToReset[i] != ',') - currentName += dependenciesToReset[i]; - - if(!currentName.empty()) - { - runcpp2::Trim(currentName); - //Convert to lowercase for case-insensitive comparison - for(int j = 0; j < currentName.size(); ++j) - currentName[j] = std::tolower(currentName[j]); - - dependencyNames.insert(currentName); - currentName.clear(); - } - } - else - currentName += dependenciesToReset[i]; - } - } - - for(int i = 0; i < availableDependencies.size(); ++i) - { - //Skip if not in the list of dependencies to reset - if(!dependencyNames.empty()) - { - //Convert dependency name to lowercase for comparison - std::string depName = availableDependencies.at(i)->Name; - for(int j = 0; j < depName.size(); ++j) - depName[j] = std::tolower(depName[j]); - - if(dependencyNames.count(depName) == 0) - { - ssLOG_DEBUG(availableDependencies.at(i)->Name << " not in list to remove"); - continue; - } - } - - std::error_code e; - ssLOG_INFO("Running cleanup commands for " << availableDependencies.at(i)->Name); - - DS::Result depResult = RunDependenciesSteps( profile, - availableDependencies.at(i)->Cleanup, - dependenciesLocalCopiesPaths.at(i), - false, - false); - if(!depResult.HasValue()) - { - depResult.Error().Message += "\nFailed to cleanup dependency " + - availableDependencies.at(i)->Name; - DS_APPEND_TRACE(depResult.Error()); - return depResult; - } - - //Remove the directory - if( ghc::filesystem::exists(dependenciesLocalCopiesPaths.at(i), e) && - !ghc::filesystem::remove_all(dependenciesLocalCopiesPaths.at(i), e)) - { - return DS_ERROR_MSG("Failed to reset dependency directory: " + - dependenciesLocalCopiesPaths.at(i)); - } - - ssLOG_DEBUG(availableDependencies.at(i)->Name << " removed"); - } - - return {}; -} - -//#define RUNCPP2_USE_PARALLEL_FOR_DEP 0 - -DS::Result -runcpp2::SetupDependenciesIfNeeded( const runcpp2::Data::Profile& profile, - const ghc::filesystem::path& buildDir, - const Data::ScriptInfo& scriptInfo, - std::vector& availableDependencies, - const std::vector& dependenciesLocalCopiesPaths, - const std::vector& dependenciesSourcePaths, - const int maxThreads) -{ - ssLOG_FUNC_INFO(); - - //If the script info is not populated (i.e. empty script info), don't do anything - if(!scriptInfo.Populated) - return {}; - - std::vector prePolulatedDependencies; - - //Clone/copy the dependencies if needed - PopulateLocalDependencies( availableDependencies, - dependenciesLocalCopiesPaths, - dependenciesSourcePaths, - buildDir, - prePolulatedDependencies).DS_TRY(); - - PopulateAbsoluteIncludePaths(availableDependencies, dependenciesLocalCopiesPaths).DS_TRY(); - - #if RUNCPP2_USE_PARALLEL_FOR_DEP - std::vector>> actions; - std::vector finished; - - //Cache logs for worker threads - ssLOG_ENABLE_CACHE_OUTPUT_FOR_NEW_THREADS(); - int logLevel = ssLOG_GET_CURRENT_THREAD_TARGET_LEVEL(); - #endif - - //Run setup steps - for(int i = 0; i < availableDependencies.size(); ++i) - { - //Don't run setup if the dependency is already setup in previous runs - if(prePolulatedDependencies.at(i)) - { - ssLOG_INFO("Skip running setup commands for " << availableDependencies.at(i)->Name); - continue; - } - - #if RUNCPP2_USE_PARALLEL_FOR_DEP - actions.emplace_back - ( - std::async - ( - std::launch::async, - [ - i, - &profile, - &availableDependencies, - &dependenciesLocalCopiesPaths, - logLevel - ]() -> DS::Result - { - ssLOG_SET_CURRENT_THREAD_TARGET_LEVEL(logLevel); - #endif - ssLOG_INFO("Running setup commands for " << availableDependencies.at(i)->Name); - DS::Result depResult = - RunDependenciesSteps( profile, - availableDependencies.at(i)->Setup, - dependenciesLocalCopiesPaths.at(i), - true, - false); - if(!depResult.HasValue()) - { - depResult.Error().Message += "\nFailed to setup dependency " + - availableDependencies.at(i)->Name; - DS_APPEND_TRACE(depResult.Error()); - return depResult; - } - - #if RUNCPP2_USE_PARALLEL_FOR_DEP - return {}; - } - ) - ); - - finished.emplace_back(false); - - //Evaluate the setup results for each batch - if(actions.size() >= maxThreads || i == availableDependencies.size() - 1) - { - bool needsWaiting = false; - do - { - std::chrono::system_clock::time_point deadline = - std::chrono::system_clock::now() + std::chrono::seconds(30); - needsWaiting = false; - for(int j = 0; j < actions.size(); ++j) - { - if(finished.at(j)) - continue; - - if(!actions.at(j).valid()) - { - ssLOG_OUTPUT_ALL_CACHE_GROUPED(); - return DS_ERROR_MSG("Failed to construct actions for setup"); - } - - std::future_status actionStatus = actions.at(j).wait_until(deadline); - if(actionStatus == std::future_status::ready) - { - DS::Result actionResult = actions.at(j).get(); - if(!actionResult.HasValue()) - { - ssLOG_OUTPUT_ALL_CACHE_GROUPED(); - actionResult.Error().Message += "\nSetup failed for dependencies"; - DS_APPEND_TRACE(actionResult.Error()); - return actionResult; - } - finished.at(j) = true; - } - else - { - ssLOG_WARNING("Manual interrupt might be needed"); - ssLOG_WARNING("Waited 30 seconds, dependencies setup still going..."); - needsWaiting = true; - } - } - } - while(needsWaiting); - - actions.clear(); - finished.clear(); - } - - #endif //#if RUNCPP2_USE_PARALLEL_FOR_DEP - } - - ssLOG_OUTPUT_ALL_CACHE_GROUPED(); - return {}; -} - -DS::Result -runcpp2::BuildDependencies( const runcpp2::Data::Profile& profile, - const Data::ScriptInfo& scriptInfo, - const std::vector& availableDependencies, - const std::vector& dependenciesLocalCopiesPaths, - const int maxThreads) -{ - ssLOG_FUNC_INFO(); - - //If the script info is not populated (i.e. empty script info), don't do anything - if(!scriptInfo.Populated) - return {}; - - #if RUNCPP2_USE_PARALLEL_FOR_DEP - std::vector> actions; - std::vector finished; - - //Cache logs for worker threads - ssLOG_ENABLE_CACHE_OUTPUT_FOR_NEW_THREADS(); - int logLevel = ssLOG_GET_CURRENT_THREAD_TARGET_LEVEL(); - #endif - - //Run build steps - for(int i = 0; i < availableDependencies.size(); ++i) - { - ssLOG_INFO("Running build commands for " << availableDependencies.at(i)->Name); - - #if RUNCPP2_USE_PARALLEL_FOR_DEP - actions.emplace_back - ( - std::async - ( - std::launch::async, - [ - i, - &profile, - &availableDependencies, - &dependenciesLocalCopiesPaths, - logLevel - ]() -> DS::Result - { - ssLOG_SET_CURRENT_THREAD_TARGET_LEVEL(logLevel); - #endif - DS::Result depResult = - RunDependenciesSteps( profile, - availableDependencies.at(i)->Build, - dependenciesLocalCopiesPaths.at(i), - true, - false); - if(!depResult.HasValue()) - { - depResult.Error().Message += "\nFailed to build dependency " + - availableDependencies.at(i)->Name; - DS_APPEND_TRACE(depResult.Error()); - return depResult; - } - #if RUNCPP2_USE_PARALLEL_FOR_DEP - return {}; - } - ) - ); - - finished.emplace_back(false); - - //Evaluate the build results for each batch - if(actions.size() >= maxThreads || i == availableDependencies.size() - 1) - { - bool needsWaiting = false; - do - { - std::chrono::system_clock::time_point deadline = - std::chrono::system_clock::now() + std::chrono::seconds(30); - needsWaiting = false; - for(int j = 0; j < actions.size(); ++j) - { - if(finished.at(j)) - continue; - - if(!actions.at(j).valid()) - { - ssLOG_OUTPUT_ALL_CACHE_GROUPED(); - return DS_ERROR_MSG("Failed to construct actions for building dependencies"); - } - - std::future_status actionStatus = actions.at(j).wait_until(deadline); - if(actionStatus == std::future_status::ready) - { - DS::Result actionResult = actions.at(j).get(); - if(!actionResult.HasValue()) - { - ssLOG_OUTPUT_ALL_CACHE_GROUPED(); - actionResult.Error().Message += "\nBuild failed for dependencies"; - DS_APPEND_TRACE(actionResult.Error()); - return actionResult; - } - finished.at(j) = true; - } - else - { - ssLOG_WARNING("Manual interrupt might be needed"); - ssLOG_WARNING("Waited 30 seconds, dependencies build still going..."); - needsWaiting = true; - } - } - } - while(needsWaiting); - - actions.clear(); - finished.clear(); - } - #endif ////#if RUNCPP2_USE_PARALLEL_FOR_DEP - } - - ssLOG_OUTPUT_ALL_CACHE_GROUPED(); - return {}; -} - -DS::Result -runcpp2::GatherDependenciesBinaries(const std::vector& availableDependencies, - const std::vector& dependenciesCopiesPaths, - const Data::Profile& profile, - std::vector& outBinariesPaths) -{ - ssLOG_FUNC_DEBUG(); - - std::unordered_set binariesPathsSet; - for(int i = 0; i < outBinariesPaths.size(); ++i) - binariesPathsSet.insert(outBinariesPaths[i]); - - int minimumDependenciesCopiesCount = 0; - for(int i = 0; i < availableDependencies.size(); ++i) - { - if(availableDependencies.at(i)->LibraryType != runcpp2::Data::DependencyLibraryType::HEADER) - ++minimumDependenciesCopiesCount; - } - - if(minimumDependenciesCopiesCount > dependenciesCopiesPaths.size()) - { - return DS_ERROR_MSG("The amount of available dependencies do not match" - " the amount of dependencies copies paths"); - } - - int nonLinkFilesCount = 0; - for(int i = 0; i < availableDependencies.size(); ++i) - { - ssLOG_INFO("Evaluating dependency " << availableDependencies.at(i)->Name); - - if(runcpp2::HasValueFromPlatformMap(availableDependencies.at(i)->FilesToCopy)) - { - const runcpp2::Data::FilesToCopyInfo& filesToCopy = - *runcpp2::GetValueFromPlatformMap(availableDependencies.at(i)->FilesToCopy); - - const std::vector* filesToGatherForProfile = - runcpp2::GetValueFromProfileMap(profile, filesToCopy.ProfileFiles); - - if(filesToGatherForProfile) - { - for(int j = 0; j < filesToGatherForProfile->size(); ++j) - { - ghc::filesystem::path srcPath = - ghc::filesystem::path(dependenciesCopiesPaths.at(i)) / - filesToGatherForProfile->at(j); - - std::error_code e; - if(ghc::filesystem::exists(srcPath, e)) - { - const std::string processedSrcPath = runcpp2::ProcessPath(srcPath); - outBinariesPaths.push_back(processedSrcPath); - binariesPathsSet.insert(processedSrcPath); - ++nonLinkFilesCount; - ssLOG_INFO("Added binary path: " << srcPath.string()); - } - else - ssLOG_WARNING("File not found: " << srcPath.string()); - } - } - } - - std::vector extensionsToLink; - - //Get all the file extensions to gather - { - DS_ASSERT_TRUE(GetDependencyBinariesExtensionsToLink( *availableDependencies.at(i), - profile, - extensionsToLink)); - const std::string* debugSymbolExt = - runcpp2::GetValueFromPlatformMap(profile.FilesTypes.DebugSymbolFile.Extension); - - if(debugSymbolExt) - extensionsToLink.push_back(*debugSymbolExt); - } - - if(availableDependencies.at(i)->LibraryType == Data::DependencyLibraryType::HEADER) - continue; - - //Get the Search path and search library name - using PropertyMap = std::unordered_map; - const PropertyMap& linkProperties = availableDependencies.at(i)->LinkProperties; - - if(!runcpp2::HasValueFromPlatformMap(linkProperties)) - { - return DS_ERROR_MSG("Link properties for dependency " + availableDependencies.at(i)->Name + - " is missing for the current platform"); - } - - const Data::DependencyLinkProperty& linkProperty = - *runcpp2::GetValueFromPlatformMap(linkProperties); - - const Data::ProfileLinkProperty* profileLinkProperty = - runcpp2::GetValueFromProfileMap(profile, linkProperty.ProfileProperties); - - if(!profileLinkProperty) - continue; - - for(int searchLibIndex = 0; - searchLibIndex < profileLinkProperty->SearchLibraryNames.size(); - ++searchLibIndex) - { - for(int searchDirIndex = 0; - searchDirIndex < profileLinkProperty->SearchDirectories.size(); - ++searchDirIndex) - { - std::string currentSearchLibraryName = - profileLinkProperty->SearchLibraryNames.at(searchLibIndex); - std::string currentSearchDirectory = - profileLinkProperty->SearchDirectories.at(searchDirIndex); - - if(!ghc::filesystem::path(currentSearchDirectory).is_absolute()) - { - currentSearchDirectory = dependenciesCopiesPaths.at(i) + "/" + - currentSearchDirectory; - } - - ssLOG_DEBUG("currentSearchDirectory: " << currentSearchDirectory); - ssLOG_DEBUG("currentSearchLibraryName: " << currentSearchLibraryName); - - std::error_code e; - if( !ghc::filesystem::exists(currentSearchDirectory, e) || - !ghc::filesystem::is_directory(currentSearchDirectory, e)) - { - ssLOG_INFO("Invalid search path: " << currentSearchDirectory); - continue; - } - - //Iterate each files in the directory we are searching - for(auto it : ghc::filesystem::directory_iterator(currentSearchDirectory, e)) - { - if(it.is_directory()) - continue; - - std::string currentFileName = it.path().filename().string(); - std::string currentExtension = runcpp2::GetFileExtensionWithoutVersion(it.path()); - - ssLOG_DEBUG("currentFileName: " << currentFileName); - ssLOG_DEBUG("currentExtension: " << currentExtension); - - //TODO: Make it not case sensitive? - bool nameMatched = false; - if(currentFileName.find(currentSearchLibraryName) != std::string::npos) - nameMatched = true; - - for(int excludeIndex = 0; - excludeIndex < profileLinkProperty->ExcludeLibraryNames.size(); - ++excludeIndex) - { - std::string currentExcludeLibraryName = - profileLinkProperty->ExcludeLibraryNames.at(excludeIndex); - - if(currentFileName.find(currentExcludeLibraryName) != std::string::npos) - { - nameMatched = false; - break; - } - } - - if(!nameMatched) - continue; - - bool extensionMatched = false; - for(int extIndex = 0; extIndex < extensionsToLink.size(); ++extIndex) - { - if(currentExtension == extensionsToLink.at(extIndex)) - { - extensionMatched = true; - break; - } - } - - if(!extensionMatched) - continue; - - //Handle symlink - ghc::filesystem::path resolvedPath = it.path(); - { - std::error_code symlink_ec; - resolvedPath = ResolveSymlink(resolvedPath, symlink_ec); - if(symlink_ec) - return DS_ERROR_MSG("Failed to resolve symlink: " + symlink_ec.message()); - } - - const std::string processedPath = runcpp2::ProcessPath(it.path().string()); - const std::string processedResolvedPath = - runcpp2::ProcessPath(resolvedPath.string()); - - if(binariesPathsSet.count(processedResolvedPath) == 0) - { - ssLOG_INFO("Linking " << processedPath); - outBinariesPaths.push_back(processedPath); - binariesPathsSet.insert(processedResolvedPath); - } - } - } - } - } - - //Do a check to see if any dependencies are copied - if(outBinariesPaths.size() - nonLinkFilesCount < minimumDependenciesCopiesCount) - { - ssLOG_WARNING("We could be missing some link files for dependencies"); - - for(int i = 0; i < outBinariesPaths.size(); ++i) - ssLOG_WARNING("outBinariesPaths[" << i << "]: " << outBinariesPaths.at(i)); - } - - return {}; -} - -DS::Result runcpp2::HandleImport( Data::DependencyInfo& dependency, - const ghc::filesystem::path& basePath) -{ - ssLOG_FUNC_DEBUG(); - - if(dependency.Source.ImportPath.empty()) - return {}; - - const std::string fullPath = (basePath / dependency.Source.ImportPath).string(); - std::error_code ec; - if(!ghc::filesystem::exists(fullPath, ec)) - return DS_ERROR_MSG("Import file not found: " + fullPath); - - if(ghc::filesystem::is_directory(fullPath)) - return DS_ERROR_MSG("Import path is a directory: " + fullPath); - - //Parse the YAML file - YAML::ResourceHandle resource; - std::vector rootNodes; - - std::string content; - { - std::ifstream file(fullPath); - if(!file.is_open()) - return DS_ERROR_MSG("Failed to open import file: " + fullPath); - - std::stringstream buffer; - buffer << file.rdbuf(); - content = buffer.str(); - } - - rootNodes = YAML::ParseYAML(content, resource).DS_TRY(); - DEFER { YAML::FreeYAMLResource(resource); }; - DS_ASSERT_FALSE(rootNodes.empty()); - - //Store the imported sources as copies for traciblity if needed - std::vector> previouslyImportedSources; - { - std::shared_ptr currentImportSource = - std::make_shared(dependency.Source); - - previouslyImportedSources = dependency.Source.ImportedSources; - - dependency.Source.ImportedSources.clear(); - currentImportSource->ImportedSources.clear(); - - previouslyImportedSources.push_back(currentImportSource); - - //Reset the current dependency before we parse the import dependency - dependency = Data::DependencyInfo(); - } - - for(int i = 0; i < rootNodes.size(); ++i) - { - YAML::ResolveAnchors(rootNodes[i]).DS_TRY(); - - //Parse the imported dependency - if(!dependency.ParseYAML_Node(rootNodes[i])) - { - //If failed to parse document, fail only if we reach the last one - if(i != rootNodes.size() - 1) - continue; - - std::string errMsg = "Failed to parse imported dependency: " + fullPath; - - //Print the list of imported sources - for(int j = 0; j < previouslyImportedSources.size(); ++j) - { - errMsg += "Imported source[" + DS_STR(j) + "]: " = - previouslyImportedSources.at(j)->ImportPath.string(); - } - - return DS_ERROR_MSG(errMsg); - } - - dependency.Source.ImportedSources = previouslyImportedSources; - return {}; - } - - return DS_ERROR_MSG("This should never be reached"); -} - -DS::Result runcpp2::ResolveImports( Data::ScriptInfo& scriptInfo, - const ghc::filesystem::path& scriptPath, - const ghc::filesystem::path& buildDir) -{ - ssLOG_FUNC_INFO(); - - //For each dependency, check if import path exists - for(int i = 0; i < scriptInfo.Dependencies.size(); ++i) - { - Data::DependencyInfo& dependency = scriptInfo.Dependencies.at(i); - - //Check if import path exists - Data::DependencySource& source = dependency.Source; - if(source.ImportPath.empty()) - continue; - - if(!source.ImportPath.is_relative()) - return DS_ERROR_MSG("Import path is not relative: " + source.ImportPath.string()); - - ghc::filesystem::path copyPath; - ghc::filesystem::path sourcePath; - GetDependencyPath(dependency, scriptPath, buildDir, copyPath, sourcePath).DS_TRY(); - - bool prePopulated = false; - PopulateLocalDependency(dependency, copyPath, sourcePath, buildDir, prePopulated).DS_TRY(); - - //Parse the import file - HandleImport(dependency, copyPath).DS_TRY(); - - //Check do we still have import path in the dependency. If so, we need to parse it again - if(!dependency.Source.ImportPath.empty()) - --i; - } - - return {}; -} - -DS::Result runcpp2::SyncLocalDependency( const Data::DependencyInfo& dependency, - const ghc::filesystem::path& sourcePath, - const ghc::filesystem::path& copyPath) -{ - ssLOG_FUNC_DEBUG(); - std::error_code ec; - - //Only sync if it's a local dependency - const Data::LocalSource* local = mpark::get_if(&dependency.Source.Source); - if(!local) - { - ssLOG_DEBUG("Not a local dependency, skipping sync"); - return {}; - } - - //Fail if source path doesn't exist - if(!ghc::filesystem::exists(sourcePath, ec)) - return DS_ERROR_MSG("Source path does not exist: " + sourcePath.string()); - - //Create target directory if it doesn't exist - if(!ghc::filesystem::exists(copyPath, ec)) - { - if(!ghc::filesystem::create_directory(copyPath, ec)) - return DS_ERROR_MSG("Failed to create directory " + copyPath.string() + ": " + ec.message()); - } - - //Get list of files in source - std::unordered_set sourceFiles; - for(const ghc::filesystem::directory_entry& entry : - ghc::filesystem::directory_iterator(sourcePath, ec)) - { - sourceFiles.insert(entry.path().filename().string()); - } - - //First pass: Check existing files in target - for(const ghc::filesystem::directory_entry& entry : - ghc::filesystem::directory_iterator(copyPath, ec)) - { - const ghc::filesystem::path& targetPath = entry.path(); - const ghc::filesystem::path& srcPath = sourcePath / targetPath.filename(); - bool needsUpdate = false; - - //Check if this is a symlink - if(ghc::filesystem::is_symlink(targetPath, ec)) - { - //Verify if symlink is valid - if(!ghc::filesystem::exists(targetPath, ec)) - { - ssLOG_DEBUG("Found invalid symlink, removing: " << targetPath.string()); - ghc::filesystem::remove(targetPath, ec); - needsUpdate = true; - } - } - - //If file exists in source, check if it needs update - if(ghc::filesystem::exists(srcPath, ec)) - { - if(!needsUpdate) - { - ghc::filesystem::file_time_type srcTime = - ghc::filesystem::last_write_time(srcPath, ec); - ghc::filesystem::file_time_type dstTime = - ghc::filesystem::last_write_time(targetPath, ec); - needsUpdate = (srcTime > dstTime); - } - - if(needsUpdate) - { - ssLOG_DEBUG("Updating: " << targetPath.string()); - ghc::filesystem::remove(targetPath, ec); - - switch(local->CopyMode) - { - case Data::LocalCopyMode::Auto: - ghc::filesystem::create_symlink(srcPath, targetPath, ec); - if(ec) - { - ssLOG_DEBUG("Symlink failed: " << ec.message()); - ec.clear(); - ghc::filesystem::create_hard_link(srcPath, targetPath, ec); - if(ec) - { - ssLOG_DEBUG("Hardlink failed: " << ec.message()); - ec.clear(); - ghc::filesystem::copy(srcPath, targetPath, ec); - } - } - break; - - case Data::LocalCopyMode::Symlink: - ghc::filesystem::create_symlink(srcPath, targetPath, ec); - break; - - case Data::LocalCopyMode::Hardlink: - ghc::filesystem::create_hard_link(srcPath, targetPath, ec); - break; - - case Data::LocalCopyMode::Copy: - ghc::filesystem::copy(srcPath, targetPath, ec); - break; - } - - if(ec) - return DS_ERROR_MSG("Failed to update target: " + ec.message()); - } - sourceFiles.erase(targetPath.filename().string()); - } - else - { - //File no longer exists in source, remove it - ssLOG_DEBUG("Removing file that no longer exists in source: " << targetPath.string()); - ghc::filesystem::remove(targetPath, ec); - } - } - - //Second pass: Add any new files from source - for(const std::string& filename : sourceFiles) - { - const ghc::filesystem::path& srcPath = sourcePath / filename; - const ghc::filesystem::path& targetPath = copyPath / filename; - - ssLOG_DEBUG("Adding new file: " << targetPath.string()); - - switch(local->CopyMode) - { - case Data::LocalCopyMode::Auto: - ghc::filesystem::create_symlink(srcPath, targetPath, ec); - if(ec) - { - ssLOG_DEBUG("Symlink failed: " << ec.message()); - ec.clear(); - ghc::filesystem::create_hard_link(srcPath, targetPath, ec); - if(ec) - { - ssLOG_DEBUG("Hardlink failed: " << ec.message()); - ec.clear(); - ghc::filesystem::copy(srcPath, targetPath, ec); - } - } - break; - - case Data::LocalCopyMode::Symlink: - ghc::filesystem::create_symlink(srcPath, targetPath, ec); - break; - - case Data::LocalCopyMode::Hardlink: - ghc::filesystem::create_hard_link(srcPath, targetPath, ec); - break; - - case Data::LocalCopyMode::Copy: - ghc::filesystem::copy(srcPath, targetPath, ec); - break; - } - - if(ec) - return DS_ERROR_MSG("Failed to add new file: " + ec.message()); - } - - return {}; -} - -DS::Result -runcpp2::SyncLocalDependencies( const std::vector& dependencies, - const std::vector& dependenciesSourcePaths, - const std::vector& dependenciesCopiesPaths) -{ - ssLOG_FUNC_DEBUG(); - for(size_t i = 0; i < dependencies.size(); ++i) - { - SyncLocalDependency(*dependencies.at(i), - ghc::filesystem::path(dependenciesSourcePaths.at(i)), - ghc::filesystem::path(dependenciesCopiesPaths.at(i))).DS_TRY(); - } - - return {}; -} - diff --git a/Src/runcpp2/DependenciesHelper.hpp b/Src/runcpp2/DependenciesHelper.hpp new file mode 100644 index 0000000..f65f398 --- /dev/null +++ b/Src/runcpp2/DependenciesHelper.hpp @@ -0,0 +1,1360 @@ +#ifndef RUNCPP2_DEPENDENCIES_SETUP_HELPER_HPP +#define RUNCPP2_DEPENDENCIES_SETUP_HELPER_HPP + +#include "runcpp2/Data/DependencyInfo.hpp" +#include "runcpp2/Data/ScriptInfo.hpp" +#include "runcpp2/Data/Profile.hpp" +#include "runcpp2/Data/DependencyLibraryType.hpp" +#include "runcpp2/Data/DependencyLinkProperty.hpp" +#include "runcpp2/Data/DependencySource.hpp" +#include "runcpp2/Data/FileProperties.hpp" +#include "runcpp2/Data/FilesToCopyInfo.hpp" +#include "runcpp2/Data/FilesTypesInfo.hpp" +#include "runcpp2/Data/GitSource.hpp" +#include "runcpp2/Data/LocalSource.hpp" +#include "runcpp2/Data/ParseCommon.hpp" +#include "runcpp2/Data/ProfilesCommands.hpp" +#include "runcpp2/Data/SubmoduleInitType.hpp" + +#include "runcpp2/LibYAML_Wrapper.hpp" +#include "runcpp2/PlatformUtil.hpp" +#include "runcpp2/StringUtil.hpp" +#include "runcpp2/DeferUtil.hpp" + +#if !defined(NOMINMAX) + #define NOMINMAX 1 +#endif + +#include "ssLogger/ssLog.hpp" +#include "DSResult/DSResult.hpp" +#include "ghc/filesystem.hpp" +#include "mpark/variant.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + DS::Result GetDependencyPath( const runcpp2::Data::DependencyInfo& dependency, + const ghc::filesystem::path& scriptPath, + const ghc::filesystem::path& buildDir, + ghc::filesystem::path& outCopyPath, + ghc::filesystem::path& outSourcePath); + + DS::Result PopulateLocalDependency( const runcpp2::Data::DependencyInfo& dependency, + const ghc::filesystem::path& copyPath, + const ghc::filesystem::path& sourcePath, + const ghc::filesystem::path& buildDir, + bool& outPrePopulated); + + DS::Result + PopulateLocalDependencies( const std::vector& dependencies, + const std::vector& dependenciesCopiesPaths, + const std::vector& dependenciesSourcesPaths, + const ghc::filesystem::path& buildDir, + std::vector& outPrePopulated); + + DS::Result + PopulateAbsoluteIncludePaths( std::vector& dependencies, + const std::vector& dependenciesCopiesPaths); + + DS::Result + RunDependenciesSteps( const runcpp2::Data::Profile& profile, + const std::unordered_map< PlatformName, + runcpp2::Data::ProfilesCommands> steps, + const std::string& dependenciesCopiedDirectory, + bool required, + bool redirectIO); + + bool GetDependencyBinariesExtensionsToLink( const runcpp2::Data::DependencyInfo& dependencyInfo, + const runcpp2::Data::Profile& profile, + std::vector& outExtensionsToLink); + + ghc::filesystem::path ResolveSymlink(const ghc::filesystem::path& path, std::error_code& ec); +} + +namespace runcpp2 +{ + inline DS::Result + GetDependenciesPaths( const std::vector& availableDependencies, + std::vector& outCopiesPaths, + std::vector& outSourcesPaths, + const ghc::filesystem::path& scriptPath, + const ghc::filesystem::path& buildDir) + { + ssLOG_FUNC_INFO(); + + for(int i = 0; i < availableDependencies.size(); ++i) + { + ghc::filesystem::path currentCopyPath; + ghc::filesystem::path currentSourcePath; + GetDependencyPath( *availableDependencies.at(i), + scriptPath, + buildDir, + currentCopyPath, + currentSourcePath).DS_TRY(); + + outCopiesPaths.push_back(currentCopyPath.string()); + outSourcesPaths.push_back(currentSourcePath.string()); + } + + return {}; + } + + inline bool IsDependencyAvailableForThisPlatform(const Data::DependencyInfo& dependency) + { + std::vector platformNames = GetPlatformNames(); + + for(int i = 0; i < platformNames.size(); ++i) + { + if(dependency.Platforms.find(platformNames.at(i)) != dependency.Platforms.end()) + return true; + } + + return false; + } + + inline DS::Result + CleanupDependencies(const runcpp2::Data::Profile& profile, + const Data::ScriptInfo& scriptInfo, + const std::vector& availableDependencies, + const std::vector& dependenciesLocalCopiesPaths, + const std::string& dependenciesToReset) + { + ssLOG_FUNC_DEBUG(); + + //If the script info is not populated (i.e. empty script info), don't do anything + if(!scriptInfo.Populated) + return {}; + + DS_ASSERT_EQ(availableDependencies.size(), dependenciesLocalCopiesPaths.size()); + + //Split dependency names if not "all" + std::unordered_set dependencyNames; + if(dependenciesToReset != "all") + { + std::string currentName; + for(int i = 0; i < dependenciesToReset.size(); ++i) + { + if(dependenciesToReset[i] == ',' || i == dependenciesToReset.size() - 1) + { + if(dependenciesToReset[i] != ',') + currentName += dependenciesToReset[i]; + + if(!currentName.empty()) + { + runcpp2::Trim(currentName); + //Convert to lowercase for case-insensitive comparison + for(int j = 0; j < currentName.size(); ++j) + currentName[j] = std::tolower(currentName[j]); + + dependencyNames.insert(currentName); + currentName.clear(); + } + } + else + currentName += dependenciesToReset[i]; + } + } + + for(int i = 0; i < availableDependencies.size(); ++i) + { + //Skip if not in the list of dependencies to reset + if(!dependencyNames.empty()) + { + //Convert dependency name to lowercase for comparison + std::string depName = availableDependencies.at(i)->Name; + for(int j = 0; j < depName.size(); ++j) + depName[j] = std::tolower(depName[j]); + + if(dependencyNames.count(depName) == 0) + { + ssLOG_DEBUG(availableDependencies.at(i)->Name << " not in list to remove"); + continue; + } + } + + std::error_code e; + ssLOG_INFO("Running cleanup commands for " << availableDependencies.at(i)->Name); + + DS::Result depResult = RunDependenciesSteps( profile, + availableDependencies.at(i)->Cleanup, + dependenciesLocalCopiesPaths.at(i), + false, + false); + if(!depResult.HasValue()) + { + depResult.Error().Message += "\nFailed to cleanup dependency " + + availableDependencies.at(i)->Name; + DS_APPEND_TRACE(depResult.Error()); + return depResult; + } + + //Remove the directory + if( ghc::filesystem::exists(dependenciesLocalCopiesPaths.at(i), e) && + !ghc::filesystem::remove_all(dependenciesLocalCopiesPaths.at(i), e)) + { + return DS_ERROR_MSG("Failed to reset dependency directory: " + + dependenciesLocalCopiesPaths.at(i)); + } + + ssLOG_DEBUG(availableDependencies.at(i)->Name << " removed"); + } + + return {}; + } + + inline DS::Result + SetupDependenciesIfNeeded( const runcpp2::Data::Profile& profile, + const ghc::filesystem::path& buildDir, + const Data::ScriptInfo& scriptInfo, + std::vector& availableDependencies, + const std::vector& dependenciesLocalCopiesPaths, + const std::vector& dependenciesSourcePaths, + const int maxThreads) + { + ssLOG_FUNC_INFO(); + + //If the script info is not populated (i.e. empty script info), don't do anything + if(!scriptInfo.Populated) + return {}; + + std::vector prePolulatedDependencies; + + //Clone/copy the dependencies if needed + PopulateLocalDependencies( availableDependencies, + dependenciesLocalCopiesPaths, + dependenciesSourcePaths, + buildDir, + prePolulatedDependencies).DS_TRY(); + + PopulateAbsoluteIncludePaths(availableDependencies, dependenciesLocalCopiesPaths).DS_TRY(); + + #if RUNCPP2_USE_PARALLEL_FOR_DEP + std::vector>> actions; + std::vector finished; + + //Cache logs for worker threads + ssLOG_ENABLE_CACHE_OUTPUT_FOR_NEW_THREADS(); + int logLevel = ssLOG_GET_CURRENT_THREAD_TARGET_LEVEL(); + #endif + + //Run setup steps + for(int i = 0; i < availableDependencies.size(); ++i) + { + //Don't run setup if the dependency is already setup in previous runs + if(prePolulatedDependencies.at(i)) + { + ssLOG_INFO("Skip running setup commands for " << availableDependencies.at(i)->Name); + continue; + } + + #if RUNCPP2_USE_PARALLEL_FOR_DEP + actions.emplace_back + ( + std::async + ( + std::launch::async, + [ + i, + &profile, + &availableDependencies, + &dependenciesLocalCopiesPaths, + logLevel + ]() -> DS::Result + { + ssLOG_SET_CURRENT_THREAD_TARGET_LEVEL(logLevel); + #endif + ssLOG_INFO("Running setup commands for " << availableDependencies.at(i)->Name); + DS::Result depResult = + RunDependenciesSteps( profile, + availableDependencies.at(i)->Setup, + dependenciesLocalCopiesPaths.at(i), + true, + false); + if(!depResult.HasValue()) + { + depResult.Error().Message += "\nFailed to setup dependency " + + availableDependencies.at(i)->Name; + DS_APPEND_TRACE(depResult.Error()); + return depResult; + } + + #if RUNCPP2_USE_PARALLEL_FOR_DEP + return {}; + } + ) + ); + + finished.emplace_back(false); + + //Evaluate the setup results for each batch + if(actions.size() >= maxThreads || i == availableDependencies.size() - 1) + { + bool needsWaiting = false; + do + { + std::chrono::system_clock::time_point deadline = + std::chrono::system_clock::now() + std::chrono::seconds(30); + needsWaiting = false; + for(int j = 0; j < actions.size(); ++j) + { + if(finished.at(j)) + continue; + + if(!actions.at(j).valid()) + { + ssLOG_OUTPUT_ALL_CACHE_GROUPED(); + return DS_ERROR_MSG("Failed to construct actions for setup"); + } + + std::future_status actionStatus = actions.at(j).wait_until(deadline); + if(actionStatus == std::future_status::ready) + { + DS::Result actionResult = actions.at(j).get(); + if(!actionResult.HasValue()) + { + ssLOG_OUTPUT_ALL_CACHE_GROUPED(); + actionResult.Error().Message += "\nSetup failed for dependencies"; + DS_APPEND_TRACE(actionResult.Error()); + return actionResult; + } + finished.at(j) = true; + } + else + { + ssLOG_WARNING("Manual interrupt might be needed"); + ssLOG_WARNING("Waited 30 seconds, dependencies setup still going..."); + needsWaiting = true; + } + } + } + while(needsWaiting); + + actions.clear(); + finished.clear(); + } + + #endif //#if RUNCPP2_USE_PARALLEL_FOR_DEP + } + + ssLOG_OUTPUT_ALL_CACHE_GROUPED(); + return {}; + } + + + inline DS::Result + BuildDependencies( const runcpp2::Data::Profile& profile, + const Data::ScriptInfo& scriptInfo, + const std::vector& availableDependencies, + const std::vector& dependenciesLocalCopiesPaths, + const int maxThreads) + { + ssLOG_FUNC_INFO(); + + //If the script info is not populated (i.e. empty script info), don't do anything + if(!scriptInfo.Populated) + return {}; + + #if RUNCPP2_USE_PARALLEL_FOR_DEP + std::vector> actions; + std::vector finished; + + //Cache logs for worker threads + ssLOG_ENABLE_CACHE_OUTPUT_FOR_NEW_THREADS(); + int logLevel = ssLOG_GET_CURRENT_THREAD_TARGET_LEVEL(); + #endif + + //Run build steps + for(int i = 0; i < availableDependencies.size(); ++i) + { + ssLOG_INFO("Running build commands for " << availableDependencies.at(i)->Name); + + #if RUNCPP2_USE_PARALLEL_FOR_DEP + actions.emplace_back + ( + std::async + ( + std::launch::async, + [ + i, + &profile, + &availableDependencies, + &dependenciesLocalCopiesPaths, + logLevel + ]() -> DS::Result + { + ssLOG_SET_CURRENT_THREAD_TARGET_LEVEL(logLevel); + #endif + DS::Result depResult = + RunDependenciesSteps( profile, + availableDependencies.at(i)->Build, + dependenciesLocalCopiesPaths.at(i), + true, + false); + if(!depResult.HasValue()) + { + depResult.Error().Message += "\nFailed to build dependency " + + availableDependencies.at(i)->Name; + DS_APPEND_TRACE(depResult.Error()); + return depResult; + } + #if RUNCPP2_USE_PARALLEL_FOR_DEP + return {}; + } + ) + ); + + finished.emplace_back(false); + + //Evaluate the build results for each batch + if(actions.size() >= maxThreads || i == availableDependencies.size() - 1) + { + bool needsWaiting = false; + do + { + std::chrono::system_clock::time_point deadline = + std::chrono::system_clock::now() + std::chrono::seconds(30); + needsWaiting = false; + for(int j = 0; j < actions.size(); ++j) + { + if(finished.at(j)) + continue; + + if(!actions.at(j).valid()) + { + ssLOG_OUTPUT_ALL_CACHE_GROUPED(); + return DS_ERROR_MSG("Failed to construct actions for building dependencies"); + } + + std::future_status actionStatus = actions.at(j).wait_until(deadline); + if(actionStatus == std::future_status::ready) + { + DS::Result actionResult = actions.at(j).get(); + if(!actionResult.HasValue()) + { + ssLOG_OUTPUT_ALL_CACHE_GROUPED(); + actionResult.Error().Message += "\nBuild failed for dependencies"; + DS_APPEND_TRACE(actionResult.Error()); + return actionResult; + } + finished.at(j) = true; + } + else + { + ssLOG_WARNING("Manual interrupt might be needed"); + ssLOG_WARNING("Waited 30 seconds, dependencies build still going..."); + needsWaiting = true; + } + } + } + while(needsWaiting); + + actions.clear(); + finished.clear(); + } + #endif ////#if RUNCPP2_USE_PARALLEL_FOR_DEP + } + + ssLOG_OUTPUT_ALL_CACHE_GROUPED(); + return {}; + } + + inline DS::Result + GatherDependenciesBinaries( const std::vector& availableDependencies, + const std::vector& dependenciesCopiesPaths, + const Data::Profile& profile, + std::vector& outBinariesPaths) + { + ssLOG_FUNC_DEBUG(); + + std::unordered_set binariesPathsSet; + for(int i = 0; i < outBinariesPaths.size(); ++i) + binariesPathsSet.insert(outBinariesPaths[i]); + + int minimumDependenciesCopiesCount = 0; + for(int i = 0; i < availableDependencies.size(); ++i) + { + if(availableDependencies.at(i)->LibraryType != runcpp2::Data::DependencyLibraryType::HEADER) + ++minimumDependenciesCopiesCount; + } + + if(minimumDependenciesCopiesCount > dependenciesCopiesPaths.size()) + { + return DS_ERROR_MSG("The amount of available dependencies do not match" + " the amount of dependencies copies paths"); + } + + int nonLinkFilesCount = 0; + for(int i = 0; i < availableDependencies.size(); ++i) + { + ssLOG_INFO("Evaluating dependency " << availableDependencies.at(i)->Name); + + if(runcpp2::HasValueFromPlatformMap(availableDependencies.at(i)->FilesToCopy)) + { + const runcpp2::Data::FilesToCopyInfo& filesToCopy = + *runcpp2::GetValueFromPlatformMap(availableDependencies.at(i)->FilesToCopy); + + const std::vector* filesToGatherForProfile = + runcpp2::GetValueFromProfileMap(profile, filesToCopy.ProfileFiles); + + if(filesToGatherForProfile) + { + for(int j = 0; j < filesToGatherForProfile->size(); ++j) + { + ghc::filesystem::path srcPath = + ghc::filesystem::path(dependenciesCopiesPaths.at(i)) / + filesToGatherForProfile->at(j); + + std::error_code e; + if(ghc::filesystem::exists(srcPath, e)) + { + const std::string processedSrcPath = runcpp2::ProcessPath(srcPath); + outBinariesPaths.push_back(processedSrcPath); + binariesPathsSet.insert(processedSrcPath); + ++nonLinkFilesCount; + ssLOG_INFO("Added binary path: " << srcPath.string()); + } + else + ssLOG_WARNING("File not found: " << srcPath.string()); + } + } + } + + std::vector extensionsToLink; + + //Get all the file extensions to gather + { + DS_ASSERT_TRUE(GetDependencyBinariesExtensionsToLink( *availableDependencies.at(i), + profile, + extensionsToLink)); + const std::string* debugSymbolExt = + runcpp2::GetValueFromPlatformMap(profile.FilesTypes.DebugSymbolFile.Extension); + + if(debugSymbolExt) + extensionsToLink.push_back(*debugSymbolExt); + } + + if(availableDependencies.at(i)->LibraryType == Data::DependencyLibraryType::HEADER) + continue; + + //Get the Search path and search library name + using PropertyMap = std::unordered_map; + const PropertyMap& linkProperties = availableDependencies.at(i)->LinkProperties; + + if(!runcpp2::HasValueFromPlatformMap(linkProperties)) + { + return DS_ERROR_MSG("Link properties for dependency " + availableDependencies.at(i)->Name + + " is missing for the current platform"); + } + + const Data::DependencyLinkProperty& linkProperty = + *runcpp2::GetValueFromPlatformMap(linkProperties); + + const Data::ProfileLinkProperty* profileLinkProperty = + runcpp2::GetValueFromProfileMap(profile, linkProperty.ProfileProperties); + + if(!profileLinkProperty) + continue; + + for(int searchLibIndex = 0; + searchLibIndex < profileLinkProperty->SearchLibraryNames.size(); + ++searchLibIndex) + { + for(int searchDirIndex = 0; + searchDirIndex < profileLinkProperty->SearchDirectories.size(); + ++searchDirIndex) + { + std::string currentSearchLibraryName = + profileLinkProperty->SearchLibraryNames.at(searchLibIndex); + std::string currentSearchDirectory = + profileLinkProperty->SearchDirectories.at(searchDirIndex); + + if(!ghc::filesystem::path(currentSearchDirectory).is_absolute()) + { + currentSearchDirectory = dependenciesCopiesPaths.at(i) + "/" + + currentSearchDirectory; + } + + ssLOG_DEBUG("currentSearchDirectory: " << currentSearchDirectory); + ssLOG_DEBUG("currentSearchLibraryName: " << currentSearchLibraryName); + + std::error_code e; + if( !ghc::filesystem::exists(currentSearchDirectory, e) || + !ghc::filesystem::is_directory(currentSearchDirectory, e)) + { + ssLOG_INFO("Invalid search path: " << currentSearchDirectory); + continue; + } + + //Iterate each files in the directory we are searching + for(auto it : ghc::filesystem::directory_iterator(currentSearchDirectory, e)) + { + if(it.is_directory()) + continue; + + std::string currentFileName = it.path().filename().string(); + std::string currentExtension = runcpp2::GetFileExtensionWithoutVersion(it.path()); + + ssLOG_DEBUG("currentFileName: " << currentFileName); + ssLOG_DEBUG("currentExtension: " << currentExtension); + + //TODO: Make it not case sensitive? + bool nameMatched = false; + if(currentFileName.find(currentSearchLibraryName) != std::string::npos) + nameMatched = true; + + for(int excludeIndex = 0; + excludeIndex < profileLinkProperty->ExcludeLibraryNames.size(); + ++excludeIndex) + { + std::string currentExcludeLibraryName = + profileLinkProperty->ExcludeLibraryNames.at(excludeIndex); + + if(currentFileName.find(currentExcludeLibraryName) != std::string::npos) + { + nameMatched = false; + break; + } + } + + if(!nameMatched) + continue; + + bool extensionMatched = false; + for(int extIndex = 0; extIndex < extensionsToLink.size(); ++extIndex) + { + if(currentExtension == extensionsToLink.at(extIndex)) + { + extensionMatched = true; + break; + } + } + + if(!extensionMatched) + continue; + + //Handle symlink + ghc::filesystem::path resolvedPath = it.path(); + { + std::error_code symlink_ec; + resolvedPath = ResolveSymlink(resolvedPath, symlink_ec); + if(symlink_ec) + return DS_ERROR_MSG("Failed to resolve symlink: " + symlink_ec.message()); + } + + const std::string processedPath = runcpp2::ProcessPath(it.path().string()); + const std::string processedResolvedPath = + runcpp2::ProcessPath(resolvedPath.string()); + + if(binariesPathsSet.count(processedResolvedPath) == 0) + { + ssLOG_INFO("Linking " << processedPath); + outBinariesPaths.push_back(processedPath); + binariesPathsSet.insert(processedResolvedPath); + } + } + } + } + } + + //Do a check to see if any dependencies are copied + if(outBinariesPaths.size() - nonLinkFilesCount < minimumDependenciesCopiesCount) + { + ssLOG_WARNING("We could be missing some link files for dependencies"); + + for(int i = 0; i < outBinariesPaths.size(); ++i) + ssLOG_WARNING("outBinariesPaths[" << i << "]: " << outBinariesPaths.at(i)); + } + + return {}; + } + + inline DS::Result HandleImport( Data::DependencyInfo& dependency, + const ghc::filesystem::path& basePath) + { + ssLOG_FUNC_DEBUG(); + + if(dependency.Source.ImportPath.empty()) + return {}; + + const std::string fullPath = (basePath / dependency.Source.ImportPath).string(); + std::error_code ec; + if(!ghc::filesystem::exists(fullPath, ec)) + return DS_ERROR_MSG("Import file not found: " + fullPath); + + if(ghc::filesystem::is_directory(fullPath)) + return DS_ERROR_MSG("Import path is a directory: " + fullPath); + + //Parse the YAML file + YAML::ResourceHandle resource; + std::vector rootNodes; + + std::string content; + { + std::ifstream file(fullPath); + if(!file.is_open()) + return DS_ERROR_MSG("Failed to open import file: " + fullPath); + + std::stringstream buffer; + buffer << file.rdbuf(); + content = buffer.str(); + } + + rootNodes = YAML::ParseYAML(content, resource).DS_TRY(); + DEFER { YAML::FreeYAMLResource(resource); }; + DS_ASSERT_FALSE(rootNodes.empty()); + + //Store the imported sources as copies for traciblity if needed + std::vector> previouslyImportedSources; + { + std::shared_ptr currentImportSource = + std::make_shared(dependency.Source); + + previouslyImportedSources = dependency.Source.ImportedSources; + + dependency.Source.ImportedSources.clear(); + currentImportSource->ImportedSources.clear(); + + previouslyImportedSources.push_back(currentImportSource); + + //Reset the current dependency before we parse the import dependency + dependency = Data::DependencyInfo(); + } + + for(int i = 0; i < rootNodes.size(); ++i) + { + YAML::ResolveAnchors(rootNodes[i]).DS_TRY(); + + //Parse the imported dependency + if(!dependency.ParseYAML_Node(rootNodes[i])) + { + //If failed to parse document, fail only if we reach the last one + if(i != rootNodes.size() - 1) + continue; + + std::string errMsg = "Failed to parse imported dependency: " + fullPath; + + //Print the list of imported sources + for(int j = 0; j < previouslyImportedSources.size(); ++j) + { + errMsg += "Imported source[" + DS_STR(j) + "]: " = + previouslyImportedSources.at(j)->ImportPath.string(); + } + + return DS_ERROR_MSG(errMsg); + } + + dependency.Source.ImportedSources = previouslyImportedSources; + return {}; + } + + return DS_ERROR_MSG("This should never be reached"); + } + + inline DS::Result ResolveImports( Data::ScriptInfo& scriptInfo, + const ghc::filesystem::path& scriptPath, + const ghc::filesystem::path& buildDir) + { + ssLOG_FUNC_INFO(); + + //For each dependency, check if import path exists + for(int i = 0; i < scriptInfo.Dependencies.size(); ++i) + { + Data::DependencyInfo& dependency = scriptInfo.Dependencies.at(i); + + //Check if import path exists + Data::DependencySource& source = dependency.Source; + if(source.ImportPath.empty()) + continue; + + if(!source.ImportPath.is_relative()) + return DS_ERROR_MSG("Import path is not relative: " + source.ImportPath.string()); + + ghc::filesystem::path copyPath; + ghc::filesystem::path sourcePath; + GetDependencyPath(dependency, scriptPath, buildDir, copyPath, sourcePath).DS_TRY(); + + bool prePopulated = false; + PopulateLocalDependency(dependency, copyPath, sourcePath, buildDir, prePopulated).DS_TRY(); + + //Parse the import file + HandleImport(dependency, copyPath).DS_TRY(); + + //Check do we still have import path in the dependency. If so, we need to parse it again + if(!dependency.Source.ImportPath.empty()) + --i; + } + + return {}; + } + + inline DS::Result SyncLocalDependency(const Data::DependencyInfo& dependency, + const ghc::filesystem::path& sourcePath, + const ghc::filesystem::path& copyPath) + { + ssLOG_FUNC_DEBUG(); + std::error_code ec; + + //Only sync if it's a local dependency + const Data::LocalSource* local = mpark::get_if(&dependency.Source.Source); + if(!local) + { + ssLOG_DEBUG("Not a local dependency, skipping sync"); + return {}; + } + + //Fail if source path doesn't exist + if(!ghc::filesystem::exists(sourcePath, ec)) + return DS_ERROR_MSG("Source path does not exist: " + sourcePath.string()); + + //Create target directory if it doesn't exist + if(!ghc::filesystem::exists(copyPath, ec)) + { + if(!ghc::filesystem::create_directory(copyPath, ec)) + return DS_ERROR_MSG("Failed to create directory " + copyPath.string() + ": " + ec.message()); + } + + //Get list of files in source + std::unordered_set sourceFiles; + for(const ghc::filesystem::directory_entry& entry : + ghc::filesystem::directory_iterator(sourcePath, ec)) + { + sourceFiles.insert(entry.path().filename().string()); + } + + //First pass: Check existing files in target + for(const ghc::filesystem::directory_entry& entry : + ghc::filesystem::directory_iterator(copyPath, ec)) + { + const ghc::filesystem::path& targetPath = entry.path(); + const ghc::filesystem::path& srcPath = sourcePath / targetPath.filename(); + bool needsUpdate = false; + + //Check if this is a symlink + if(ghc::filesystem::is_symlink(targetPath, ec)) + { + //Verify if symlink is valid + if(!ghc::filesystem::exists(targetPath, ec)) + { + ssLOG_DEBUG("Found invalid symlink, removing: " << targetPath.string()); + ghc::filesystem::remove(targetPath, ec); + needsUpdate = true; + } + } + + //If file exists in source, check if it needs update + if(ghc::filesystem::exists(srcPath, ec)) + { + if(!needsUpdate) + { + ghc::filesystem::file_time_type srcTime = + ghc::filesystem::last_write_time(srcPath, ec); + ghc::filesystem::file_time_type dstTime = + ghc::filesystem::last_write_time(targetPath, ec); + needsUpdate = (srcTime > dstTime); + } + + if(needsUpdate) + { + ssLOG_DEBUG("Updating: " << targetPath.string()); + ghc::filesystem::remove(targetPath, ec); + + switch(local->CopyMode) + { + case Data::LocalCopyMode::Auto: + ghc::filesystem::create_symlink(srcPath, targetPath, ec); + if(ec) + { + ssLOG_DEBUG("Symlink failed: " << ec.message()); + ec.clear(); + ghc::filesystem::create_hard_link(srcPath, targetPath, ec); + if(ec) + { + ssLOG_DEBUG("Hardlink failed: " << ec.message()); + ec.clear(); + ghc::filesystem::copy(srcPath, targetPath, ec); + } + } + break; + + case Data::LocalCopyMode::Symlink: + ghc::filesystem::create_symlink(srcPath, targetPath, ec); + break; + + case Data::LocalCopyMode::Hardlink: + ghc::filesystem::create_hard_link(srcPath, targetPath, ec); + break; + + case Data::LocalCopyMode::Copy: + ghc::filesystem::copy(srcPath, targetPath, ec); + break; + } + + if(ec) + return DS_ERROR_MSG("Failed to update target: " + ec.message()); + } + sourceFiles.erase(targetPath.filename().string()); + } + else + { + //File no longer exists in source, remove it + ssLOG_DEBUG("Removing file that no longer exists in source: " << targetPath.string()); + ghc::filesystem::remove(targetPath, ec); + } + } + + //Second pass: Add any new files from source + for(const std::string& filename : sourceFiles) + { + const ghc::filesystem::path& srcPath = sourcePath / filename; + const ghc::filesystem::path& targetPath = copyPath / filename; + + ssLOG_DEBUG("Adding new file: " << targetPath.string()); + + switch(local->CopyMode) + { + case Data::LocalCopyMode::Auto: + ghc::filesystem::create_symlink(srcPath, targetPath, ec); + if(ec) + { + ssLOG_DEBUG("Symlink failed: " << ec.message()); + ec.clear(); + ghc::filesystem::create_hard_link(srcPath, targetPath, ec); + if(ec) + { + ssLOG_DEBUG("Hardlink failed: " << ec.message()); + ec.clear(); + ghc::filesystem::copy(srcPath, targetPath, ec); + } + } + break; + + case Data::LocalCopyMode::Symlink: + ghc::filesystem::create_symlink(srcPath, targetPath, ec); + break; + + case Data::LocalCopyMode::Hardlink: + ghc::filesystem::create_hard_link(srcPath, targetPath, ec); + break; + + case Data::LocalCopyMode::Copy: + ghc::filesystem::copy(srcPath, targetPath, ec); + break; + } + + if(ec) + return DS_ERROR_MSG("Failed to add new file: " + ec.message()); + } + + return {}; + } + + inline DS::Result + SyncLocalDependencies( const std::vector& dependencies, + const std::vector& dependenciesSourcePaths, + const std::vector& dependenciesCopiesPaths) + { + ssLOG_FUNC_DEBUG(); + for(size_t i = 0; i < dependencies.size(); ++i) + { + SyncLocalDependency(*dependencies.at(i), + ghc::filesystem::path(dependenciesSourcePaths.at(i)), + ghc::filesystem::path(dependenciesCopiesPaths.at(i))).DS_TRY(); + } + + return {}; + } +} + +namespace +{ + DS::Result GetDependencyPath( const runcpp2::Data::DependencyInfo& dependency, + const ghc::filesystem::path& scriptPath, + const ghc::filesystem::path& buildDir, + ghc::filesystem::path& outCopyPath, + ghc::filesystem::path& outSourcePath) + { + ssLOG_FUNC_INFO(); + + ghc::filesystem::path scriptDirectory = scriptPath.parent_path(); + + const runcpp2::Data::DependencySource& currentSource = dependency.Source; + + if(mpark::get_if(¤tSource.Source)) + { + const runcpp2::Data::GitSource* git = + mpark::get_if(¤tSource.Source); + + size_t lastSlashFoundIndex = git->URL.find_last_of("/"); + size_t lastDotGitFoundIndex = git->URL.find_last_of(".git"); + + if( lastSlashFoundIndex == std::string::npos || + lastDotGitFoundIndex == std::string::npos || + lastDotGitFoundIndex < lastSlashFoundIndex) + { + return DS_ERROR_MSG(DS_STR("Invalid git url: ") + git->URL); + } + else + { + std::string gitRepoName = + //+1 for / to not include it + git->URL.substr(lastSlashFoundIndex + 1, + //-1 for slash + lastDotGitFoundIndex - 1 - + //-(size - 1) for .git + (std::string(".git").size() - 1) - + lastSlashFoundIndex); + + outCopyPath = (buildDir / gitRepoName); + outSourcePath.clear(); + } + } + else if(mpark::get_if(¤tSource.Source)) + { + const runcpp2::Data::LocalSource* local = + mpark::get_if(¤tSource.Source); + + std::string localDepDirectoryName; + std::string curPath = local->Path; + + if(curPath.back() == '/') + curPath.pop_back(); + + localDepDirectoryName = ghc::filesystem::path(curPath).filename().string(); + + if(ghc::filesystem::path(curPath).is_relative()) + outSourcePath = (scriptDirectory / local->Path); + else + outSourcePath = (local->Path); + + if(!ghc::filesystem::is_directory(outSourcePath)) + { + return DS_ERROR_MSG(DS_STR( "Local dependency path is not a directory: ") + + DS_STR(outSourcePath)); + } + + if(currentSource.ImportPath.empty()) + outCopyPath = (buildDir / localDepDirectoryName); + else + outCopyPath = outSourcePath; + } + + return {}; + } + + DS::Result PopulateLocalDependency( const runcpp2::Data::DependencyInfo& dependency, + const ghc::filesystem::path& copyPath, + const ghc::filesystem::path& sourcePath, + const ghc::filesystem::path& buildDir, + bool& outPrePopulated) + { + ssLOG_FUNC_DEBUG(); + + std::error_code e; + + if(ghc::filesystem::exists(copyPath, e)) + { + if(!ghc::filesystem::is_directory(copyPath, e)) + { + return DS_ERROR_MSG("Dependency path is a file: " + copyPath.string() + "\n" + + "It should be a folder instead"); + } + outPrePopulated = true; + return {}; + } + else + { + if(mpark::get_if(&(dependency.Source.Source))) + { + const runcpp2::Data::GitSource* git = + mpark::get_if(&(dependency.Source.Source)); + + std::string submoduleString; + static_assert( static_cast(runcpp2::Data::SubmoduleInitType::COUNT) == 3, + "Add new type to be processed"); + switch(git->CurrentSubmoduleInitType) + { + case runcpp2::Data::SubmoduleInitType::NONE: + break; + case runcpp2::Data::SubmoduleInitType::SHALLOW: + submoduleString = "--recursive --shallow-submodules "; + break; + case runcpp2::Data::SubmoduleInitType::FULL: + submoduleString = "--recursive "; + break; + default: + { + return DS_ERROR_MSG("Invalid git submodule init type: " + + DS_STR(static_cast(git->CurrentSubmoduleInitType))); + } + } + + std::string gitCloneCommand = + std::string("git clone ") + + submoduleString + + (git->FullHistory ? "" : "--depth 1 ") + + ( + git->Branch.empty() ? + std::string("") : + std::string("--branch ") + git->Branch + " " + ) + + git->URL; + + ssLOG_INFO("Running git clone command: " << gitCloneCommand << " in " << + buildDir.string()); + + int returnCode = 0; + std::string output; + if(!runcpp2::RunCommand(gitCloneCommand, + false, + buildDir.string(), + output, + returnCode)) + { + return DS_ERROR_MSG("Failed to run git clone with result: " + DS_STR(returnCode) + + "\nWas trying to run: " + gitCloneCommand); + } + //else + // ssLOG_INFO("Output: \n" << output); + } + else if(mpark::get_if(&(dependency.Source.Source))) + { + //Copy/link local directory if it doesn't have any import path + if(dependency.Source.ImportPath.empty()) + { + runcpp2::SyncLocalDependency(dependency, sourcePath, copyPath).DS_TRY(); + } + } + + outPrePopulated = false; + return {}; + } + } + + DS::Result + PopulateLocalDependencies( const std::vector& dependencies, + const std::vector& dependenciesCopiesPaths, + const std::vector& dependenciesSourcesPaths, + const ghc::filesystem::path& buildDir, + std::vector& outPrePopulated) + { + ssLOG_FUNC_INFO(); + + outPrePopulated.resize(dependencies.size()); + + for(int i = 0; i < dependencies.size(); ++i) + { + if(!dependencies.at(i)->Source.ImportPath.empty()) + return DS_ERROR_MSG("Dependency import not resolved before populating."); + + bool prePopulated = false; + PopulateLocalDependency(*dependencies.at(i), + ghc::filesystem::path(dependenciesCopiesPaths.at(i)), + ghc::filesystem::path(dependenciesSourcesPaths.at(i)), + buildDir, + prePopulated).DS_TRY(); + outPrePopulated.at(i) = prePopulated; + } + + return {}; + } + + DS::Result + PopulateAbsoluteIncludePaths( std::vector& dependencies, + const std::vector& dependenciesCopiesPaths) + { + //Append absolute include paths from relative include paths + for(int i = 0; i < dependencies.size(); ++i) + { + dependencies.at(i)->AbsoluteIncludePaths.clear(); + for(int j = 0; j < dependencies.at(i)->IncludePaths.size(); ++j) + { + if(ghc::filesystem::path(dependencies.at(i)->IncludePaths.at(j)).is_absolute()) + { + return DS_ERROR_MSG("Dependency include path cannot be absolute: " + + dependencies.at(i)->IncludePaths.at(j)); + } + + dependencies.at(i) + ->AbsoluteIncludePaths + .push_back + ( + ghc::filesystem::absolute + ( + dependenciesCopiesPaths.at(i) + + "/" + + dependencies.at(i)->IncludePaths.at(j) + ) + .string() + ); + + ssLOG_DEBUG("Include path added: " << dependencies.at(i)->AbsoluteIncludePaths.back()); + } + } + + return {}; + } + + DS::Result + RunDependenciesSteps( const runcpp2::Data::Profile& profile, + const std::unordered_map< PlatformName, + runcpp2::Data::ProfilesCommands> steps, + const std::string& dependenciesCopiedDirectory, + bool required, + bool redirectIO) + { + ssLOG_FUNC_INFO(); + + if(steps.empty()) + return {}; + + //Find the platform name we use for setup + if(!runcpp2::HasValueFromPlatformMap(steps)) + { + if(required) + return DS_ERROR_MSG("Failed to find steps for current platform"); + else + return {}; + } + + const runcpp2::Data::ProfilesCommands& dependencySteps = + *runcpp2::GetValueFromPlatformMap(steps); + + const std::vector* commands = + runcpp2::GetValueFromProfileMap(profile, dependencySteps.CommandSteps); + + if(!commands) + { + return DS_ERROR_MSG(DS_STR( "Failed to find steps for profile ") + profile.Name + + " for current platform"); + } + + //Run the commands + for(int k = 0; k < commands->size(); ++k) + { + std::string processedDependencyPath = runcpp2::ProcessPath(dependenciesCopiedDirectory); + ssLOG_INFO("Running command: " << commands->at(k) << " in " << processedDependencyPath); + + int returnCode = 0; + std::string output; + + //TODO: Change to ds result + if(!runcpp2::RunCommand(commands->at(k), + redirectIO, + processedDependencyPath, + output, + returnCode)) + { + std::string errorMsg = + DS_STR("Failed to run command with result: ") + DS_STR(returnCode) + "\n" + "Was trying to run: " + commands->at(k); + if(redirectIO) + errorMsg += DS_STR("\nOutput: \n") + output; + return DS_ERROR_MSG(errorMsg); + } + else + { + if(redirectIO) + ssLOG_INFO("Output: \n" << output); + } + } + + return {}; + } + + bool GetDependencyBinariesExtensionsToLink( const runcpp2::Data::DependencyInfo& dependencyInfo, + const runcpp2::Data::Profile& profile, + std::vector& outExtensionsToLink) + { + static_assert((int)runcpp2::Data::DependencyLibraryType::COUNT == 4, ""); + switch(dependencyInfo.LibraryType) + { + case runcpp2::Data::DependencyLibraryType::STATIC: + { + if(!runcpp2::HasValueFromPlatformMap(profile.FilesTypes.StaticLinkFile.Extension)) + { + ssLOG_ERROR("Failed to find static library extensions for dependency " << + dependencyInfo.Name); + + return false; + } + + outExtensionsToLink.push_back( + *runcpp2::GetValueFromPlatformMap(profile .FilesTypes + .StaticLinkFile + .Extension)); + + break; + } + case runcpp2::Data::DependencyLibraryType::SHARED: + { + if( !runcpp2::HasValueFromPlatformMap(profile.FilesTypes.SharedLinkFile.Extension) || + !runcpp2::HasValueFromPlatformMap(profile.FilesTypes.SharedLibraryFile.Extension)) + { + ssLOG_ERROR("Failed to find shared library extensions for dependency " << + dependencyInfo.Name); + + return false; + } + + outExtensionsToLink.push_back( + *runcpp2::GetValueFromPlatformMap(profile.FilesTypes.SharedLinkFile.Extension)); + + if( *runcpp2::GetValueFromPlatformMap(profile.FilesTypes.SharedLinkFile.Extension) != + *runcpp2::GetValueFromPlatformMap(profile.FilesTypes.SharedLibraryFile.Extension)) + { + outExtensionsToLink.push_back( + *runcpp2::GetValueFromPlatformMap(profile .FilesTypes + .SharedLibraryFile + .Extension)); + } + + break; + } + case runcpp2::Data::DependencyLibraryType::OBJECT: + { + if(!runcpp2::HasValueFromPlatformMap(profile.FilesTypes.ObjectLinkFile.Extension)) + { + ssLOG_ERROR("Failed to find object file extensions for dependency " << + dependencyInfo.Name); + + return false; + } + + outExtensionsToLink.push_back( + *runcpp2::GetValueFromPlatformMap(profile.FilesTypes.ObjectLinkFile.Extension)); + + break; + } + case runcpp2::Data::DependencyLibraryType::HEADER: + break; + default: + ssLOG_ERROR("Invalid library type: " << (int)dependencyInfo.LibraryType); + return false; + } + + return true; + } + + ghc::filesystem::path ResolveSymlink(const ghc::filesystem::path& path, std::error_code& ec) + { + ghc::filesystem::path resolvedPath = ghc::filesystem::canonical(path, ec); + if(ec) + return path; // Return original path if canonical fails + + return resolvedPath; + } +} + +#endif diff --git a/Src/runcpp2/IncludeManager.cpp b/Src/runcpp2/IncludeManager.cpp deleted file mode 100644 index 2b032e0..0000000 --- a/Src/runcpp2/IncludeManager.cpp +++ /dev/null @@ -1,160 +0,0 @@ -#include "runcpp2/IncludeManager.hpp" -#include "runcpp2/Data/ParseCommon.hpp" -#include "ssLogger/ssLog.hpp" - -#include - -#if INTERNAL_RUNCPP2_UNIT_TESTS - #include "Tests/IncludeManager/MockComponents.hpp" -#else - #define CO_NO_OVERRIDE 1 - #include "CppOverride.hpp" -#endif - -namespace runcpp2 -{ - bool IncludeManager::Initialize(const ghc::filesystem::path& buildDir) - { - INTERNAL_RUNCPP2_SAFE_START(); - ssLOG_FUNC_DEBUG(); - - IncludeRecordDir = buildDir / "IncludeMaps"; - - std::error_code e; - if(!ghc::filesystem::exists(IncludeRecordDir, e)) - { - if(!ghc::filesystem::create_directories(IncludeRecordDir, e)) - { - ssLOG_ERROR("Failed to create IncludeMaps directory: " << IncludeRecordDir); - return false; - } - } - - return true; - INTERNAL_RUNCPP2_SAFE_CATCH_RETURN(false); - } - - bool IncludeManager::WriteIncludeRecord(const ghc::filesystem::path& sourceFile, - const std::vector& includes) - { - INTERNAL_RUNCPP2_SAFE_START(); - ssLOG_FUNC_DEBUG(); - - if(!sourceFile.is_absolute()) - { - ssLOG_ERROR("Source file is not absolute: " << sourceFile); - return false; - } - - ghc::filesystem::path recordPath = GetRecordPath(sourceFile); - - std::ofstream recordFile(recordPath); - if(!recordFile.is_open()) - { - ssLOG_ERROR("Failed to open include record file: " << recordPath); - return false; - } - - for(const ghc::filesystem::path& include : includes) - recordFile << include.string() << "\n"; - - recordFile.close(); - return true; - INTERNAL_RUNCPP2_SAFE_CATCH_RETURN(false); - } - - bool IncludeManager::ReadIncludeRecord( const ghc::filesystem::path& sourceFile, - std::vector& outIncludes, - ghc::filesystem::file_time_type& outRecordTime) - { - INTERNAL_RUNCPP2_SAFE_START(); - ssLOG_FUNC_DEBUG(); - - if(!sourceFile.is_absolute()) - { - ssLOG_ERROR("Source file is not absolute: " << sourceFile); - return false; - } - - outIncludes.clear(); - ghc::filesystem::path recordPath = GetRecordPath(sourceFile); - - std::error_code e; - if(!ghc::filesystem::exists(recordPath, e)) - return false; - - outRecordTime = ghc::filesystem::last_write_time(recordPath, e); - - std::ifstream recordFile(recordPath); - if(!recordFile.is_open()) - { - ssLOG_ERROR("Failed to open include record file: " << recordPath); - return false; - } - - std::string line; - while(std::getline(recordFile, line)) - { - if(!line.empty()) - outIncludes.push_back(ghc::filesystem::path(line)); - } - - return true; - INTERNAL_RUNCPP2_SAFE_CATCH_RETURN(false); - } - - bool IncludeManager::NeedsUpdate( const ghc::filesystem::path& sourceFile, - const std::vector& includes, - const ghc::filesystem::file_time_type& recordTime) const - { - INTERNAL_RUNCPP2_SAFE_START(); - ssLOG_FUNC_DEBUG(); - - ssLOG_DEBUG("Checking includes for " << sourceFile.string()); - - std::error_code e; - ghc::filesystem::file_time_type sourceTime = ghc::filesystem::last_write_time(sourceFile, e); - - ssLOG_DEBUG("sourceTime: " << sourceTime.time_since_epoch().count()); - ssLOG_DEBUG("recordTime: " << recordTime.time_since_epoch().count()); - - if(sourceTime > recordTime) - { - ssLOG_DEBUG("Source file newer than include record"); - return true; - } - - for(const ghc::filesystem::path& include : includes) - { - if(!ghc::filesystem::exists(include, e)) - { - ssLOG_DEBUG("Include file does not exist: " << include.string()); - return true; - } - - ghc::filesystem::file_time_type includeTime = - ghc::filesystem::last_write_time(include, e); - - if(includeTime > recordTime) - { - ssLOG_DEBUG("Include time for " << include.string() << - " is newer than record time"); - return true; - } - } - - ssLOG_DEBUG("No update needed for " << sourceFile.string()); - return false; - - INTERNAL_RUNCPP2_SAFE_CATCH_RETURN(true); - } - - ghc::filesystem::path IncludeManager::GetRecordPath(const ghc::filesystem::path& sourceFile) const - { - CO_INSERT_MEMBER_IMPL(OverrideInstance, ghc::filesystem::path, (sourceFile)); - - ghc::filesystem::path cleanSourceFile = sourceFile.lexically_normal(); - std::size_t pathHash = std::hash{}(cleanSourceFile.string()); - return IncludeRecordDir / (std::to_string(pathHash) + ".Includes"); - } -} diff --git a/Src/runcpp2/IncludeManager.hpp b/Src/runcpp2/IncludeManager.hpp new file mode 100644 index 0000000..08bc782 --- /dev/null +++ b/Src/runcpp2/IncludeManager.hpp @@ -0,0 +1,200 @@ +#ifndef RUNCPP2_INCLUDE_MANAGER_HPP +#define RUNCPP2_INCLUDE_MANAGER_HPP + +#include "runcpp2/Data/ParseCommon.hpp" + +#if !defined(NOMINMAX) + #define NOMINMAX 1 +#endif + +#include "ssLogger/ssLog.hpp" +#include "ghc/filesystem.hpp" + +#include +#include +#include +#include +#include +#include + +#if defined(INTERNAL_RUNCPP2_UNIT_TESTS) && \ + INTERNAL_RUNCPP2_UNIT_TESTS == INTERNAL_RUNCPP2_UNIT_TESTS_INCLUDE_MANAGER + + struct IncludeManagerAccessor; + #include "Tests/IncludeManager/MockComponents.hpp" +#else + #define CO_NO_OVERRIDE 1 + #include "CppOverride.hpp" +#endif + +namespace runcpp2 +{ + class IncludeManager + { + public: + IncludeManager() = default; + ~IncludeManager() = default; + + inline bool Initialize(const ghc::filesystem::path& buildDir) + { + INTERNAL_RUNCPP2_SAFE_START(); + ssLOG_FUNC_DEBUG(); + + IncludeRecordDir = buildDir / "IncludeMaps"; + + std::error_code e; + if(!ghc::filesystem::exists(IncludeRecordDir, e)) + { + if(!ghc::filesystem::create_directories(IncludeRecordDir, e)) + { + ssLOG_ERROR("Failed to create IncludeMaps directory: " << IncludeRecordDir); + return false; + } + } + + return true; + INTERNAL_RUNCPP2_SAFE_CATCH_RETURN(false); + } + + inline bool WriteIncludeRecord( const ghc::filesystem::path& sourceFile, + const std::vector& includes) + { + INTERNAL_RUNCPP2_SAFE_START(); + ssLOG_FUNC_DEBUG(); + + if(!sourceFile.is_absolute()) + { + ssLOG_ERROR("Source file is not absolute: " << sourceFile); + return false; + } + + ghc::filesystem::path recordPath = GetRecordPath(sourceFile); + + std::ofstream recordFile(recordPath); + if(!recordFile.is_open()) + { + ssLOG_ERROR("Failed to open include record file: " << recordPath); + return false; + } + + for(const ghc::filesystem::path& include : includes) + recordFile << include.string() << "\n"; + + recordFile.close(); + return true; + INTERNAL_RUNCPP2_SAFE_CATCH_RETURN(false); + } + + inline bool ReadIncludeRecord( const ghc::filesystem::path& sourceFile, + std::vector& outIncludes, + ghc::filesystem::file_time_type& outRecordTime) + { + INTERNAL_RUNCPP2_SAFE_START(); + ssLOG_FUNC_DEBUG(); + + if(!sourceFile.is_absolute()) + { + ssLOG_ERROR("Source file is not absolute: " << sourceFile); + return false; + } + + outIncludes.clear(); + ghc::filesystem::path recordPath = GetRecordPath(sourceFile); + + std::error_code e; + if(!ghc::filesystem::exists(recordPath, e)) + return false; + + outRecordTime = ghc::filesystem::last_write_time(recordPath, e); + + std::ifstream recordFile(recordPath); + if(!recordFile.is_open()) + { + ssLOG_ERROR("Failed to open include record file: " << recordPath); + return false; + } + + std::string line; + while(std::getline(recordFile, line)) + { + if(!line.empty()) + outIncludes.push_back(ghc::filesystem::path(line)); + } + + return true; + INTERNAL_RUNCPP2_SAFE_CATCH_RETURN(false); + } + + inline bool NeedsUpdate(const ghc::filesystem::path& sourceFile, + const std::vector& includes, + const ghc::filesystem::file_time_type& recordTime) const + { + INTERNAL_RUNCPP2_SAFE_START(); + ssLOG_FUNC_DEBUG(); + + ssLOG_DEBUG("Checking includes for " << sourceFile.string()); + + std::error_code e; + ghc::filesystem::file_time_type sourceTime = ghc::filesystem::last_write_time(sourceFile, e); + + ssLOG_DEBUG("sourceTime: " << sourceTime.time_since_epoch().count()); + ssLOG_DEBUG("recordTime: " << recordTime.time_since_epoch().count()); + + if(sourceTime > recordTime) + { + ssLOG_DEBUG("Source file newer than include record"); + return true; + } + + for(const ghc::filesystem::path& include : includes) + { + if(!ghc::filesystem::exists(include, e)) + { + ssLOG_DEBUG("Include file does not exist: " << include.string()); + return true; + } + + ghc::filesystem::file_time_type includeTime = + ghc::filesystem::last_write_time(include, e); + + if(includeTime > recordTime) + { + ssLOG_DEBUG("Include time for " << include.string() << + " is newer than record time"); + return true; + } + } + + ssLOG_DEBUG("No update needed for " << sourceFile.string()); + return false; + + INTERNAL_RUNCPP2_SAFE_CATCH_RETURN(true); + } + + private: + #if defined(INTERNAL_RUNCPP2_UNIT_TESTS) && \ + INTERNAL_RUNCPP2_UNIT_TESTS == INTERNAL_RUNCPP2_UNIT_TESTS_INCLUDE_MANAGER + + friend struct ::IncludeManagerAccessor; + #endif + + inline ghc::filesystem::path GetRecordPath(const ghc::filesystem::path& sourceFile) const + { + CO_INSERT_MEMBER_IMPL(OverrideInstance, ghc::filesystem::path, (sourceFile)); + + ghc::filesystem::path cleanSourceFile = sourceFile.lexically_normal(); + std::size_t pathHash = std::hash{}(cleanSourceFile.string()); + return IncludeRecordDir / (std::to_string(pathHash) + ".Includes"); + } + + ghc::filesystem::path IncludeRecordDir; + }; +} + +#if defined(INTERNAL_RUNCPP2_UNIT_TESTS) && \ + INTERNAL_RUNCPP2_UNIT_TESTS == INTERNAL_RUNCPP2_UNIT_TESTS_INCLUDE_MANAGER + + #include "Tests/IncludeManager/UndefMocks.hpp" +#endif + +#endif diff --git a/Src/runcpp2/LibYAML_Wrapper.cpp b/Src/runcpp2/LibYAML_Wrapper.cpp deleted file mode 100644 index bb2a6f4..0000000 --- a/Src/runcpp2/LibYAML_Wrapper.cpp +++ /dev/null @@ -1,1237 +0,0 @@ -#include "runcpp2/LibYAML_Wrapper.hpp" - -#include "yaml.h" -#include "ssLogger/ssLog.hpp" - -#include -#include - -#if RUNCPP2_YAML_PRINT_PARSE - #include - #define RUNCPP2_YAML_PRINT(...) printf(__VA_ARGS__) -#else - #define RUNCPP2_YAML_PRINT(...) do{} while(false) -#endif - -namespace -{ - std::string EscapeString(const runcpp2::StringView string) - { - std::string outString = ""; - for(int i = 0; i < string.size(); ++i) - { - switch(string[i]) - { - case '\\': - outString += "\\\\"; - break; - case '\0': - outString += "\\0"; - break; - case '\b': - outString += "\\b"; - break; - case '\n': - outString += "\\n"; - break; - case '\r': - outString += "\\r"; - break; - case '\t': - outString += "\\t"; - break; - default: - outString += string[i]; - break; - } - } - return outString; - } - - std::string ScalarToString(const runcpp2::YAML::ScalarValue& scalar) - { - return std::string("\"") + EscapeString(scalar) + "\""; - } - - std::string AliasToString(const runcpp2::YAML::Alias& alias) - { - return std::string("*") + std::string(alias.Value.data(), alias.Value.size()); - } - - template - DS::Result AddValueToMap( std::stack>& nodeValCountStack, - const YamlValueType& newVal, - const yaml_event_t& event) - { - nodeValCountStack.top().second = 0; - DS_ASSERT_TRUE(nodeValCountStack.top().first->IsMap()); - runcpp2::YAML::OrderedMap* currentMap = - mpark::get_if(&nodeValCountStack.top().first->Value); - DS_ASSERT_NOT_EQ(currentMap, nullptr); - DS_ASSERT_FALSE(currentMap->InsertedKeys.empty()); - const runcpp2::YAML::NodePtr& lastKey = currentMap->InsertedKeys.back(); - - DS_ASSERT_GT(currentMap->Map.count(lastKey), 0); - DS_ASSERT_TRUE(currentMap->Map[lastKey] == nullptr); - currentMap->Map[lastKey] = std::make_shared(); - currentMap->Map[lastKey]->Value = newVal; - currentMap->Map[lastKey]->LineNumber = event.start_mark.line + 1; - currentMap->Map[lastKey]->Parent = nodeValCountStack.top().first; - - //Add to KeyMap if key is scalar - if(mpark::holds_alternative(lastKey->Value)) - { - auto* scalarVal = mpark::get_if(&lastKey->Value); - currentMap->StringMap[*scalarVal] = currentMap->Map[lastKey]; - } - - if(pushToStack) - { - nodeValCountStack.push(std::pair< runcpp2::YAML::Node*, - int>(currentMap->Map[lastKey].get(), 0)); - } - return {}; - } - - DS::Result ParseHandleMap(yaml_event_t& event, - std::stack>& nodeValCountStack) - { - //ssLOG_FUNC_DEBUG(); - switch(nodeValCountStack.top().second) - { - //Starting node - case -1: - nodeValCountStack.top().first->Value = runcpp2::YAML::OrderedMap(); - ++(nodeValCountStack.top().second); - break; - - //Fill key: - case 0: - { - runcpp2::YAML::NodeValue& currentParentValue = nodeValCountStack.top().first->Value; - - //We can only start a map as key in sequence node, error out - if(!mpark::holds_alternative(currentParentValue)) - { - //If we are in a map node, that means it is a complex key. - //We don't support complex key. - if(mpark::holds_alternative(currentParentValue)) - return DS_ERROR_MSG("Complex key is not supported"); - //If we are in scalar node, what? - else if(mpark::holds_alternative(currentParentValue)) - { - return DS_ERROR_MSG("Trying to create map in scalar node. " - "Missed node creation?"); - } - //If we are in alias, what? - else if(mpark::holds_alternative(currentParentValue)) - { - return DS_ERROR_MSG("Trying to create map in alias node. " - "Missed node creation?"); - } - //Invalid type - else - { - return DS_ERROR_MSG("Invalid node value type: " + - DS_STR(currentParentValue.index())); - } - } - - runcpp2::YAML::NodePtr newNode = std::make_shared(); - newNode->Value = runcpp2::YAML::OrderedMap(); - newNode->LineNumber = event.start_mark.line + 1; - newNode->Parent = nodeValCountStack.top().first; - mpark::get_if(¤tParentValue)->push_back(newNode); - nodeValCountStack.push(std::pair(newNode.get(), 0)); - break; - } - - //Key is filled, value is map - case 1: - { - runcpp2::YAML::NodeValue& currentParentValue = nodeValCountStack.top().first->Value; - - //We can only fill a value after key in a map node, error out - if(!mpark::holds_alternative(currentParentValue)) - { - if(mpark::holds_alternative(currentParentValue)) - { - return DS_ERROR_MSG("Trying to fill value in sequnce node. " - "Missed node creation?"); - } - else if(mpark::holds_alternative(currentParentValue)) - { - return DS_ERROR_MSG("Trying to fill value in scalar node. " - "Missed node creation?"); - } - else if(mpark::holds_alternative(currentParentValue)) - { - return DS_ERROR_MSG("Trying to fill value in alias node. " - "Missed node creation?"); - } - //Invalid type - else - { - return DS_ERROR_MSG("Invalid node value type: " + - DS_STR(currentParentValue.index())); - } - } - runcpp2::YAML::OrderedMap newMap = runcpp2::YAML::OrderedMap(); - DS_UNWRAP_VOID(AddValueToMap(nodeValCountStack, newMap, event)); - break; - } //case 1: - - //What? - default: - return DS_ERROR_MSG("Invalid node counter: " + DS_STR(nodeValCountStack.top().second)); - } //switch(nodeValCountStack.top().second) - - if(event.data.mapping_start.anchor) - { - nodeValCountStack.top().first->Anchor = - runcpp2::StringView((const char*)event.data.mapping_start.anchor); - } - - ssLOG_DEBUG("+MAP {}" << - ( - event.data.mapping_start.anchor ? - std::string(" &") + std::string((char*)event.data.mapping_start.anchor) : - std::string("") - ) << - ( - event.data.mapping_start.tag ? - std::string(" <") + std::string((char*)event.data.mapping_start.tag) + ">" : - std::string("") - )); - - return {}; - } - - DS::Result ParseHandleSequence( yaml_event_t& event, - std::stack>& nodeValCountStack) - { - //ssLOG_FUNC_DEBUG(); - switch(nodeValCountStack.top().second) - { - //Starting node - case -1: - nodeValCountStack.top().first->Value = runcpp2::YAML::Sequence(); - ++(nodeValCountStack.top().second); - break; - - //Fill key: - case 0: - { - runcpp2::YAML::NodeValue& currentParentValue = nodeValCountStack.top().first->Value; - //If we are in a sequence node, this means it is a nested sequence. - // fill it and append to stack - if(mpark::holds_alternative(currentParentValue)) - { - runcpp2::YAML::NodePtr newNode = runcpp2::YAML::CreateNodePtr(); - newNode->Value = runcpp2::YAML::Sequence(); - newNode->LineNumber = event.start_mark.line + 1; - newNode->Parent = nodeValCountStack.top().first; - mpark::get_if(¤tParentValue)->push_back(newNode); - nodeValCountStack.push(std::pair(newNode.get(), 0)); - } - //If we are in a map node, that means it is a complex key. - //We don't support complex key. - else if(mpark::holds_alternative(currentParentValue)) - return DS_ERROR_MSG("Complex key is not supported"); - //If we are in scalar node, what? - else if(mpark::holds_alternative(currentParentValue)) - return DS_ERROR_MSG("Trying to create map in scalar node. Missed node creation?"); - //If we are in alias node, what? - else if(mpark::holds_alternative(currentParentValue)) - return DS_ERROR_MSG("Trying to create map in alias node. Missed node creation?"); - //Invalid type - else - return DS_ERROR_MSG("Invalid node value type: " + DS_STR(currentParentValue.index())); - break; - } - - //Key is filled, value is sequence - case 1: - { - runcpp2::YAML::NodeValue& currentParentValue = nodeValCountStack.top().first->Value; - runcpp2::YAML::Sequence newSeq = runcpp2::YAML::Sequence(); - - //If we are in a map node, that means the value is a sequence - // fill it and append to stack - if(mpark::holds_alternative(currentParentValue)) - DS_UNWRAP_VOID(AddValueToMap(nodeValCountStack, newSeq, event)); - else if(mpark::holds_alternative(currentParentValue)) - return DS_ERROR_MSG("Trying to fill value in sequnce node. Missed node creation?"); - else if(mpark::holds_alternative(currentParentValue)) - return DS_ERROR_MSG("Trying to fill value in scalar node. Missed node creation?"); - else if(mpark::holds_alternative(currentParentValue)) - return DS_ERROR_MSG("Trying to fill value in alias node. Missed node creation?"); - //Invalid type - else - return DS_ERROR_MSG("Invalid node value type: " + DS_STR(currentParentValue.index())); - break; - } - - //What? - default: - return DS_ERROR_MSG("Invalid node counter: " + DS_STR(nodeValCountStack.top().second)); - } //switch(nodeValCountStack.top().second) - - if(event.data.sequence_start.anchor) - { - nodeValCountStack.top().first->Anchor = - runcpp2::StringView((const char*)event.data.sequence_start.anchor); - } - ssLOG_DEBUG("+SEQ []" << - ( - event.data.sequence_start.anchor ? - std::string(" &") + std::string((char*)event.data.sequence_start.anchor) : - std::string("") - ) << - ( - event.data.sequence_start.tag ? - std::string(" <") + std::string((char*)event.data.sequence_start.tag) + ">" : - std::string("") - )); - - return {}; - } - - DS::Result ParseHandleScalar( yaml_event_t& event, - std::stack>& nodeValCountStack) - { - //ssLOG_FUNC_DEBUG(); - std::string scalarStartStyle = ""; - std::string scalarEndStyle = ""; - - switch(event.data.scalar.style) - { - case YAML_PLAIN_SCALAR_STYLE: - scalarStartStyle = " :"; - break; - case YAML_SINGLE_QUOTED_SCALAR_STYLE: - scalarStartStyle = " '"; - scalarEndStyle = "'"; - break; - case YAML_DOUBLE_QUOTED_SCALAR_STYLE: - scalarStartStyle = " \""; - scalarEndStyle = "\""; - break; - case YAML_LITERAL_SCALAR_STYLE: - scalarStartStyle = " |"; - break; - case YAML_FOLDED_SCALAR_STYLE: - scalarStartStyle = " >"; - break; - case YAML_ANY_SCALAR_STYLE: - ssLOG_ERROR("Unrecognized scalar style"); - break; - } - - ssLOG_DEBUG("=VAL" << - ( - event.data.scalar.anchor ? - std::string(" &") + std::string((char*)event.data.scalar.anchor) : - std::string("") - ) << - ( - event.data.scalar.tag ? - std::string(" <") + std::string((char*)event.data.scalar.tag) + ">" : - std::string("") - ) << - scalarStartStyle << - EscapeString(runcpp2::StringView( (char*)event.data.scalar.value, - event.data.scalar.length)) << - scalarEndStyle); - - runcpp2::StringView scalarView = runcpp2::StringView( (const char*)event.data.scalar.value, - event.data.scalar.length); - switch(nodeValCountStack.top().second) - { - //Starting node - case -1: - return DS_ERROR_MSG("Starting node must be map or sequence"); - - //Fill key: - case 0: - { - if(event.data.scalar.anchor) - { - return DS_ERROR_MSG("Parsing error where key " + (std::string)scalarView + - " has anchor " + (char*)event.data.scalar.anchor); - } - - runcpp2::YAML::NodeValue& nodeValue = nodeValCountStack.top().first->Value; - runcpp2::YAML::NodePtr newNode = runcpp2::YAML::CreateNodePtr(); - newNode->LineNumber = event.start_mark.line + 1; - newNode->Parent = nodeValCountStack.top().first; - bool mergeKey = scalarView == "<<" && - event.data.scalar.style == YAML_PLAIN_SCALAR_STYLE && - mpark::holds_alternative(nodeValue); - if(!mergeKey) - newNode->Value = scalarView; - else - newNode->Value = runcpp2::YAML::Alias{scalarView}; - - //If we are in a sequence node, we just need to append the value - if(mpark::holds_alternative(nodeValue)) - mpark::get_if(&nodeValue)->emplace_back(newNode); - //If we are in a map node, we need to set the key - else if(mpark::holds_alternative(nodeValue)) - { - mpark::get_if(&nodeValue)->InsertedKeys - .push_back(newNode); - mpark::get_if(&nodeValue)->Map[newNode] = nullptr; - ++(nodeValCountStack.top().second); - } - //We should never in a scalar node - else if(mpark::holds_alternative(nodeValue)) - return DS_ERROR_MSG("Should not be in a scalar node"); - //We should never in a alias node - else if(mpark::holds_alternative(nodeValue)) - return DS_ERROR_MSG("Should not be in a alias node"); - //Invalid type - else - return DS_ERROR_MSG("Invalid node value type: " + DS_STR(nodeValue.index())); - break; - } - - //Key is filled, value is scalar - case 1: - { - runcpp2::YAML::NodeValue& nodeValue = nodeValCountStack.top().first->Value; - - //If we are in a map node, that means the value is scalar - if(mpark::holds_alternative(nodeValue)) - DS_UNWRAP_VOID(AddValueToMap(nodeValCountStack, scalarView, event)); - else if(mpark::holds_alternative(nodeValue)) - return DS_ERROR_MSG("Trying to fill value in sequnce node. Missed node creation?"); - //We should never in a scalar node - else if(mpark::holds_alternative(nodeValue)) - return DS_ERROR_MSG("Should not be in a scalar node"); - //We should never in a alias node - else if(mpark::holds_alternative(nodeValue)) - return DS_ERROR_MSG("Should not be in a alias node"); - //Invalid type - else - return DS_ERROR_MSG("Invalid node value type: " + DS_STR(nodeValue.index())); - - if(event.data.scalar.anchor) - { - auto& mapVal = *mpark::get_if(&nodeValue); - mapVal.Map[mapVal.InsertedKeys.back()]->Anchor = - runcpp2::StringView((const char*)event.data.scalar.anchor); - } - break; - } - - //What? - default: - return DS_ERROR_MSG("Invalid node counter: " + DS_STR(nodeValCountStack.top().second)); - } - - return {}; - } - - DS::Result ParseHandleAlias( yaml_event_t& event, - std::stack>& nodeValCountStack) - { - //ssLOG_FUNC_DEBUG(); - ssLOG_DEBUG("=ALI *" << (char*)event.data.alias.anchor); - - switch(nodeValCountStack.top().second) - { - //Starting node - case -1: - return DS_ERROR_MSG("Starting node must be map or sequence"); - - //Fill key: - case 0: - { - runcpp2::YAML::NodeValue& currentParentValue = nodeValCountStack.top().first->Value; - runcpp2::YAML::NodePtr newNode = runcpp2::YAML::CreateNodePtr(); - newNode->Value = - runcpp2::YAML::Alias { runcpp2::StringView((const char*)event.data.alias.anchor) }; - - newNode->LineNumber = event.start_mark.line + 1; - newNode->Parent = nodeValCountStack.top().first; - - //If we are in a sequence node, we just need to append the alias - if(mpark::holds_alternative(currentParentValue)) - mpark::get_if(¤tParentValue)->emplace_back(newNode); - //Alias should never be map key - //TODO: Actually, it can. But I guess we can forget about it for now? - else if(mpark::holds_alternative(currentParentValue)) - return DS_ERROR_MSG("Alias cannot be map key"); - //We should never in a scalar node - else if(mpark::holds_alternative(currentParentValue)) - return DS_ERROR_MSG("Should not be in a scalar node"); - //We should never in a alias node - else if(mpark::holds_alternative(currentParentValue)) - return DS_ERROR_MSG("Should not be in a alias node"); - //Invalid type - else - return DS_ERROR_MSG("Invalid node value type: " + DS_STR(currentParentValue.index())); - break; - } - - //Key is filled, value is alias - case 1: - { - runcpp2::YAML::NodeValue& currentParentValue = nodeValCountStack.top().first->Value; - - //If we are in a map node, that means the value is alias - if(mpark::holds_alternative(currentParentValue)) - { - runcpp2::YAML::Alias alias = - { runcpp2::StringView((const char*)event.data.alias.anchor) }; - - DS_UNWRAP_VOID(AddValueToMap(nodeValCountStack, alias, event)); - } - else if(mpark::holds_alternative(currentParentValue)) - return DS_ERROR_MSG("Trying to fill value in sequnce node. Missed node creation?"); - //We should never in a scalar node - else if(mpark::holds_alternative(currentParentValue)) - return DS_ERROR_MSG("Should not be in a scalar node"); - //We should never in a alias node - else if(mpark::holds_alternative(currentParentValue)) - return DS_ERROR_MSG("Should not be in a alias node"); - //Invalid type - else - return DS_ERROR_MSG("Invalid node value type: " + DS_STR(currentParentValue.index())); - break; - } - - //What? - default: - return DS_ERROR_MSG("Invalid node counter: " + DS_STR(nodeValCountStack.top().second)); - } //switch(nodeValCountStack.top().second) - - return {}; - } - - DS::Result CopyVariant(runcpp2::YAML::NodeValue& dest, const runcpp2::YAML::NodeValue& src) - { - if(mpark::holds_alternative(src)) - dest = *mpark::get_if(&src); - else if(mpark::holds_alternative(src)) - dest = *mpark::get_if(&src); - else if(mpark::holds_alternative(src)) - dest = *mpark::get_if(&src); - else if(mpark::holds_alternative(src)) - dest = *mpark::get_if(&src); - else - return DS_ERROR_MSG("Invalid type"); - - return {}; - } - - DS::Result UpdateParentsRecursively(runcpp2::YAML::Node& currentNode) - { - //NOTE: Parent of currentNode is correct, but not its children - std::stack nodesToProcess; - nodesToProcess.push(¤tNode); - - while(!nodesToProcess.empty()) - { - runcpp2::YAML::Node& node = *nodesToProcess.top(); - nodesToProcess.pop(); - - if(mpark::holds_alternative(node.Value)) - continue; - else if(mpark::holds_alternative(node.Value)) - continue; - else if(mpark::holds_alternative(node.Value)) - { - auto& seq = *mpark::get_if(&node.Value); - for(int i = 0; i < seq.size(); ++i) - { - if(seq[i]->Parent != &node) - { - runcpp2::YAML::NodePtr newNode = runcpp2::YAML::CreateNodePtr(); - newNode->Anchor = seq[i]->Anchor; - newNode->LineNumber = seq[i]->LineNumber; - newNode->Parent = &node; - DS_UNWRAP_VOID(CopyVariant(newNode->Value, seq[i]->Value)); - seq[i] = newNode; - } - - nodesToProcess.push(seq[i].get()); - } - } - else if(mpark::holds_alternative(node.Value)) - { - auto& map = *mpark::get_if(&node.Value); - for(int i = 0; i < map.InsertedKeys.size(); ++i) - { - runcpp2::YAML::NodePtr currentKey = map.InsertedKeys[i]; - runcpp2::YAML::NodePtr currentVal = map.Map[currentKey]; - - if(currentKey->Parent != &node || currentVal->Parent != &node) - { - runcpp2::YAML::NodePtr newKey = runcpp2::YAML::CreateNodePtr(); - newKey->Anchor = currentKey->Anchor; - newKey->LineNumber = currentKey->LineNumber; - newKey->Parent = &node; - DS_UNWRAP_VOID(CopyVariant(newKey->Value, currentKey->Value)); - - runcpp2::YAML::NodePtr newVal = runcpp2::YAML::CreateNodePtr(); - newVal->Anchor = currentKey->Anchor; - newVal->LineNumber = currentKey->LineNumber; - newVal->Parent = &node; - DS_UNWRAP_VOID(CopyVariant(newVal->Value, currentVal->Value)); - - //Replace entries with new key and values - { - map.InsertedKeys[i] = newKey; - - map.Map.erase(currentKey); - map.Map[newKey] = newVal; - - if(mpark::holds_alternative(currentKey->Value)) - { - auto& keyScalar = - *mpark::get_if(¤tKey->Value); - - map.StringMap[keyScalar] = newVal; - } - } - - nodesToProcess.push(newKey.get()); - nodesToProcess.push(newVal.get()); - } - else - { - nodesToProcess.push(currentKey.get()); - nodesToProcess.push(currentVal.get()); - } - } - } - else - return DS_ERROR_MSG("Invalid type"); - } - - return {}; - } - - //NOTE: This doesn't update OrderedMap.StringMap - DS::Result ResolveMergeKey( runcpp2::YAML::OrderedMap& currentMap, - int mergeKeyIndex, - const std::unordered_map< runcpp2::StringView, - runcpp2::YAML::Node*>& anchors) - { - //ssLOG_FUNC_DEBUG(); - runcpp2::YAML::NodePtr currentKey = currentMap.InsertedKeys[mergeKeyIndex]; - - //We trust that the key at `mergeKeyIndex` is a merge key - runcpp2::YAML::NodePtr currentValueNode = currentMap.Map[currentKey]; - std::string defaultErrorMessage = "We are expecting either an alias or sequence of alias"; - - std::vector aliasNodes; - std::vector mapsForMerging; - - if(mpark::holds_alternative(currentValueNode->Value)) - aliasNodes.push_back(currentValueNode); - else if(mpark::holds_alternative(currentValueNode->Value)) - { - auto& sequence = *mpark::get_if(¤tValueNode->Value); - for(int i = 0; i < sequence.size(); ++i) - aliasNodes.push_back(sequence[i]); - } - else - return DS_ERROR_MSG(defaultErrorMessage); - - for(int i = 0; i < aliasNodes.size(); ++i) - { - runcpp2::YAML::NodeValue& currentAliasValue = aliasNodes[i]->Value; - - if(!mpark::holds_alternative(currentAliasValue)) - return DS_ERROR_MSG(defaultErrorMessage); - - auto& alias = *mpark::get_if(¤tAliasValue); - if(anchors.count(alias.Value) == 0) - { - return DS_ERROR_MSG("Cannot find anchor value: " + DS_STR(alias.Value) + - " on line " + DS_STR(aliasNodes[i]->LineNumber)); - } - - runcpp2::YAML::Node* anchorNode = anchors.at(alias.Value); - if(!mpark::holds_alternative(anchorNode->Value)) - { - return DS_ERROR_MSG("Target anchor must be a map. Line " + - DS_STR(aliasNodes[i]->LineNumber)); - } - - mapsForMerging.emplace_back(mpark::get_if(&anchorNode->Value)); - } - - //For each map that needs to be merged, iterating their children and add it to our map - for(int i = 0; i < mapsForMerging.size(); ++i) - { - for(int j = mapsForMerging[i]->InsertedKeys.size() - 1; j >= 0; --j) - { - runcpp2::YAML::NodePtr keyToInsert = mapsForMerging[i]->InsertedKeys[j]; - runcpp2::YAML::NodePtr valueToInsert = mapsForMerging[i]->Map[keyToInsert]; - - runcpp2::YAML::ScalarValue keyView = - keyToInsert->GetScalar().DefaultOr(); - - //If duplicate key exist, that's override key, don't merge it - if(!keyView.empty() && currentMap.StringMap.count(keyView) > 0) - continue; - - //Add key and value - currentMap.InsertedKeys.insert( currentMap.InsertedKeys.begin() + mergeKeyIndex + 1, - keyToInsert); - currentMap.Map[keyToInsert] = valueToInsert; - } - } - - //Finally remove the merge key entry - currentMap.Map.erase(currentKey); - currentMap.InsertedKeys.erase(currentMap.InsertedKeys.begin() + mergeKeyIndex); - - return {}; - } -} //namespace - -namespace runcpp2 -{ - DS::Result> YAML::ParseYAML( StringView yamlString, - ResourceHandle& outResource) - { - ssLOG_FUNC_DEBUG(); - std::vector rootNodes; - - outResource.first = nullptr; - outResource.first = new yaml_parser_t(); - DS_ASSERT_TRUE(outResource.first != nullptr); - yaml_parser_t& parser = *outResource.first; - ReadWriteBuffer& readBuffer = outResource.second; - - DS_ASSERT_EQ(yaml_parser_initialize(&parser), 1); - - yaml_parser_set_input_string( &parser, - (const unsigned char*)yamlString.data(), - yamlString.size()); - - std::stack> nodeValCountStack; - - while(true) - { - readBuffer.ReadData.emplace_back(std::shared_ptr(new yaml_event_t())); - yaml_event_t& event = *readBuffer.ReadData.back().get(); - - if(!yaml_parser_parse(&parser, &event)) - { - if(parser.problem_mark.line || parser.problem_mark.column) - { - return DS_ERROR_MSG(DS_STR("Parsing failed: ") + parser.problem + " on line " + - DS_STR(parser.problem_mark.line + 1) + ", column" + - DS_STR(parser.problem_mark.column + 1)); - } - else - return DS_ERROR_MSG(DS_STR("Parsing failed: ") + parser.problem); - } - - switch(event.type) - { - case YAML_NO_EVENT: - ssLOG_DEBUG("???\n"); - break; - case YAML_STREAM_START_EVENT: - ssLOG_DEBUG("+STR\n"); - break; - case YAML_STREAM_END_EVENT: - ssLOG_DEBUG("-STR\n"); - break; - case YAML_DOCUMENT_START_EVENT: - if(!event.data.document_start.implicit) - ssLOG_DEBUG("+DOC ---"); - else - ssLOG_DEBUG("+DOC"); - rootNodes.push_back(CreateNodePtr()); - nodeValCountStack.push(std::pair(rootNodes.back().get(), -1)); - break; - case YAML_DOCUMENT_END_EVENT: - if(!event.data.document_end.implicit) - ssLOG_DEBUG("-DOC ..."); - else - ssLOG_DEBUG("-DOC"); - - //NOTE: Node is popped either by map end or sequence end event, - // hence it should be empty. - DS_ASSERT_TRUE(nodeValCountStack.empty()); - break; - case YAML_MAPPING_START_EVENT: - DS_UNWRAP_VOID(ParseHandleMap(event, nodeValCountStack)); - break; - case YAML_MAPPING_END_EVENT: - nodeValCountStack.pop(); - ssLOG_DEBUG("-MAP"); - break; - case YAML_SEQUENCE_START_EVENT: - DS_UNWRAP_VOID(ParseHandleSequence(event, nodeValCountStack)); - break; - case YAML_SEQUENCE_END_EVENT: - ssLOG_DEBUG("-SEQ"); - nodeValCountStack.pop(); - break; - case YAML_SCALAR_EVENT: - DS_UNWRAP_VOID(ParseHandleScalar(event, nodeValCountStack)); - break; - case YAML_ALIAS_EVENT: - DS_UNWRAP_VOID(ParseHandleAlias(event, nodeValCountStack)); - break; - default: - return DS_ERROR_MSG(DS_STR("Unrecognize event type: ") + DS_STR((int)event.type)); - } //switch(event.type) - - if(event.type == YAML_STREAM_END_EVENT) - break; - } //while(true) - - return rootNodes; - } - - DS::Result YAML::ResolveAnchors(NodePtr rootNode) - { - DS_ASSERT_FALSE(mpark::holds_alternative(rootNode->Value) || - mpark::holds_alternative(rootNode->Value)); - - std::stack> nodeValCountStack; - nodeValCountStack.push(std::pair(rootNode.get(), 0)); - - std::unordered_map anchors; - std::vector mapsToUpdateStringMapKeys; - - while(!nodeValCountStack.empty()) - { - Node& currentNode = *nodeValCountStack.top().first; - int visitCount = nodeValCountStack.top().second++; - - //We visited once already, all the children are processed. - if(visitCount >= 1) - { - nodeValCountStack.pop(); - continue; - } - - if(mpark::holds_alternative(currentNode.Value)) - { - StringView aliasValue = mpark::get_if(¤tNode.Value)->Value; - DS_ASSERT_FALSE(aliasValue.empty()); - if(anchors.count(aliasValue) == 0) - { - return DS_ERROR_MSG("Cannot find anchor value: " + DS_STR(aliasValue) + - " on line " + DS_STR(currentNode.LineNumber)); - } - - //Check parents don't have the anchor we want - Node* parent = ¤tNode; - while(parent != nullptr) - { - if(parent->Anchor == aliasValue) - { - return DS_ERROR_MSG("Cannot alias parent anchor: " + - DS_STR(aliasValue) + - " on line " + DS_STR(currentNode.LineNumber) + - " with parent on line " + - DS_STR(parent->LineNumber)); - } - parent = parent->Parent; - } - - //Resolve alias with the anchor we found - DS_UNWRAP_VOID(CopyVariant(currentNode.Value, anchors[aliasValue]->Value)); - - //Check if parent is map, remember to update the string map - if( currentNode.Parent != nullptr && - mpark::holds_alternative(currentNode.Parent->Value)) - { - mapsToUpdateStringMapKeys.push_back(currentNode.Parent); - } - - //The parent entry for this node is correct, but not the children, update them. - DS_UNWRAP_VOID(UpdateParentsRecursively(currentNode)); - } - else if(mpark::holds_alternative(currentNode.Value)) - ++(nodeValCountStack.top().second); - else if(mpark::holds_alternative(currentNode.Value)) - { - Sequence& sequence = *mpark::get_if(¤tNode.Value); - for(int i = sequence.size() - 1; i >= 0; --i) - nodeValCountStack.push(std::pair(sequence[i].get(), 0)); - } - else if(mpark::holds_alternative(currentNode.Value)) - { - OrderedMap& orderedMap = *mpark::get_if(¤tNode.Value); - bool hasMergeKeys = false; - - //Resolve merge keys first if any - for(int i = 0; i < orderedMap.InsertedKeys.size(); ++i) - { - NodePtr currentKey = orderedMap.InsertedKeys[i]; - if(!mpark::holds_alternative(currentKey->Value)) - continue; - - //TODO: Add support for alias map key - if(mpark::get_if(¤tKey->Value)->Value != "<<") - return DS_ERROR_MSG("Alias key must be a merge key"); - - //Move all the alias children to current map - DS_UNWRAP_VOID(ResolveMergeKey(orderedMap, i, anchors)); - - hasMergeKeys = true; - } - - //Need to udpate the parent entry for the children coming from merge keys, if any. - //Then we need to update the string map. - if(hasMergeKeys) - { - DS_UNWRAP_VOID(UpdateParentsRecursively(currentNode)); - mapsToUpdateStringMapKeys.push_back(¤tNode); - } - - //Push all the children to the stack to be resolved - for(int i = orderedMap.InsertedKeys.size() - 1; i >= 0; --i) - { - NodePtr currentKey = orderedMap.InsertedKeys[i]; - nodeValCountStack.push(std::pair(orderedMap.Map[currentKey].get(), 0)); - nodeValCountStack.push(std::pair(currentKey.get(), 0)); - } - } - else - return DS_ERROR_MSG("Invalid type"); - - if(!currentNode.Anchor.empty()) - anchors[currentNode.Anchor] = ¤tNode; - } //while(!nodeValCountStack.empty()) - - //Update string map for maps in mapsToUpdateStringMapKeys for any missing scalar keys - for(int i = 0; i < mapsToUpdateStringMapKeys.size(); ++i) - { - DS_ASSERT_TRUE(mpark::holds_alternative(mapsToUpdateStringMapKeys[i]->Value)); - OrderedMap& currentMap = *mpark::get_if(&mapsToUpdateStringMapKeys[i]->Value); - - for(int j = 0; j < currentMap.InsertedKeys.size(); ++j) - { - if(mpark::holds_alternative(currentMap.InsertedKeys[j]->Value)) - { - ScalarValue& curKey = *mpark::get_if(¤tMap .InsertedKeys[j] - ->Value); - DS_ASSERT_TRUE(currentMap.Map.count(currentMap.InsertedKeys[j]) > 0); - if(currentMap.StringMap.count(curKey) == 0) - currentMap.StringMap[curKey] = currentMap.Map[currentMap.InsertedKeys[j]]; - } - } - } - - return {}; - } - - DS::Result YAML::FreeYAMLResource(ResourceHandle& resourceHandleToFree) - { - DS_ASSERT_TRUE(resourceHandleToFree.first != nullptr); - - for(int i = 0; i < resourceHandleToFree.second.ReadData.size(); ++i) - yaml_event_delete(resourceHandleToFree.second.ReadData[i].get()); - - yaml_parser_delete(resourceHandleToFree.first); - - delete resourceHandleToFree.first; - resourceHandleToFree.first = nullptr; - resourceHandleToFree.second = ReadWriteBuffer(); - return {}; - } - - DS::Result YAML::Node::CloneToSequenceChild( NodePtr parentNode, - ResourceHandle& yamlResouce) const - { - DS_ASSERT_TRUE(parentNode->IsSequence()); - NodePtr clonedThis = Clone(false, yamlResouce).DS_TRY(); - clonedThis->Parent = parentNode.get(); - mpark::get_if(&parentNode->Value)->push_back(clonedThis); - return clonedThis; - } - - DS::Result YAML::Node::CloneToMapChild( StringView key, - NodePtr parentNode, - ResourceHandle& yamlResouce) const - { - DS_ASSERT_TRUE(parentNode->IsMap()); - NodePtr clonedThis = Clone(false, yamlResouce).DS_TRY(); - clonedThis->Parent = parentNode.get(); - yamlResouce.second.StoreString(std::string(key)); - StringView keyView = StringView(*yamlResouce.second.WriteData.back()); - NodePtr keyNode = CreateNodePtr(); - keyNode->Value = keyView; - keyNode->LineNumber = LineNumber; - - mpark::get_if(&parentNode->Value)->InsertedKeys.push_back(keyNode); - mpark::get_if(&parentNode->Value)->Map[keyNode] = clonedThis; - mpark::get_if(&parentNode->Value)->StringMap[keyView] = clonedThis; - return clonedThis; - } - - DS::Result YAML::Node::ToString(std::string& outString) const - { - std::stack nodeStack; - std::stack nodePrefixStack; - std::stack nodeIndentLevelStack; - - //if(!mpark::holds_alternative(Value) && !mpark::holds_alternative(Value)) - // return DS_ERROR_MSG("Starting value must be a map or sequence"); - - nodeStack.push(this); - nodePrefixStack.push(""); - nodeIndentLevelStack.push(0); - - while(!nodeStack.empty()) - { - outString += nodePrefixStack.top(); - - /* Scalar Output Format: - [anchor] - */ - if(mpark::holds_alternative(nodeStack.top()->Value)) - { - if(!nodeStack.top()->Anchor.empty()) - outString += " &" + std::string(nodeStack.top()->Anchor) + " "; - - outString += ScalarToString(*mpark::get_if(&(nodeStack.top()->Value))); - outString += "\n"; - nodeStack.pop(); - nodePrefixStack.pop(); - nodeIndentLevelStack.pop(); - } - /* Alias Output Format: - * - */ - else if(mpark::holds_alternative(nodeStack.top()->Value)) - { - if(!nodeStack.top()->Anchor.empty()) - return DS_ERROR_MSG("Alias node cannot have anchor"); - - outString += AliasToString(*mpark::get_if(&(nodeStack.top()->Value))); - outString += "\n"; - nodeStack.pop(); - nodePrefixStack.pop(); - nodeIndentLevelStack.pop(); - } - /* Sequence Output Format: - - - - - */ - else if(mpark::holds_alternative(nodeStack.top()->Value)) - { - const Sequence* seq = mpark::get_if(&nodeStack.top()->Value); - int indentLevel = nodeIndentLevelStack.top(); - std::string prefix = nodePrefixStack.top(); - nodeStack.pop(); - nodePrefixStack.pop(); - nodeIndentLevelStack.pop(); - if(seq->empty()) - continue; - //We iterate from back to front since we are pushing to a stack - for(int i = seq->size() - 1; i >= 0; --i) - { - nodeStack.push((*seq)[i].get()); - nodePrefixStack.push(std::string(indentLevel, ' ') + "- "); - nodeIndentLevelStack.push(indentLevel + 4); - } - nodePrefixStack.top() = "- "; //No need to indent for the first one - } - /* Map Output Format: - : - : [child sequqence anchor] - - : [child map anchor] - - */ - else if(mpark::holds_alternative(nodeStack.top()->Value)) - { - const OrderedMap& map = *mpark::get_if(&(nodeStack.top()->Value)); - int indentLevel = nodeIndentLevelStack.top(); - nodeStack.pop(); - nodePrefixStack.pop(); - nodeIndentLevelStack.pop(); - - if(map.InsertedKeys.empty()) - continue; - - //We iterate from back to front since we are pushing to a stack - for(int i = map.InsertedKeys.size() - 1; i >= 0; --i) - { - std::string prefix; - if(mpark::holds_alternative(map.InsertedKeys[i]->Value)) - prefix = (std::string)*mpark::get_if(&map.InsertedKeys[i]->Value); - else if(mpark::holds_alternative(map.InsertedKeys[i]->Value)) - { - //TODO: Add support for alias map key - const Alias& aliasVal = *mpark::get_if(&map.InsertedKeys[i]->Value); - if(aliasVal.Value != "<<") - { - return DS_ERROR_MSG(DS_STR("Merge key must be <<, \"") + - DS_STR(aliasVal.Value) + "\" is found instead"); - } - else - prefix = (std::string)aliasVal.Value; - } - else - return DS_ERROR_MSG("Complex key is not supported"); - - prefix += ": "; - - //We don't add indent for the first key/value - if(i != 0) - prefix = std::string(indentLevel, ' ') + prefix; - - NodePtr childNode = map.Map.at(map.InsertedKeys[i]); - std::string anchor = childNode->Anchor.empty() ? - "" : - "&" + std::string(childNode->Anchor); - if(mpark::holds_alternative(childNode->Value)) - { - nodeStack.push(childNode.get()); - nodePrefixStack.push( prefix + anchor + "\n" + - std::string(indentLevel, ' ')); - nodeIndentLevelStack.push(indentLevel); - } - else if(mpark::holds_alternative(childNode->Value)) - { - nodeStack.push(childNode.get()); - nodePrefixStack.push( prefix + anchor + "\n" + - std::string(indentLevel + 4, ' ')); - nodeIndentLevelStack.push(indentLevel + 4); - } - else if(mpark::holds_alternative(childNode->Value) || - mpark::holds_alternative(childNode->Value)) - { - nodeStack.push(childNode.get()); - nodePrefixStack.push(prefix); - nodeIndentLevelStack.push(indentLevel); - } - else - return DS_ERROR_MSG("Invalid type"); - } //for(int i = map.InsertedKeys.size() - 1; i >= 0; --i) - } //else if(mpark::holds_alternative(nodeStack.top()->Value)) - else - return DS_ERROR_MSG("Invalid type"); - } //while(!nodeStack.empty()) - - return {}; - } - - DS::Result YAML::Node::Clone(bool shallow, ResourceHandle& yamlResouce) const - { - std::stack> nodesToCloneStack; //Dst, src - - { - NodePtr clonedNode = CreateNodePtr(); - clonedNode->Parent = nullptr; - nodesToCloneStack.push(std::make_pair(clonedNode, this)); - } - - NodePtr returnNode = nodesToCloneStack.top().first; - - while(!nodesToCloneStack.empty()) - { - NodePtr dst = nodesToCloneStack.top().first; - const Node* src = nodesToCloneStack.top().second; - nodesToCloneStack.pop(); - - CopyVariant(dst->Value, src->Value).DS_TRY(); - dst->Anchor = src->Anchor; - dst->LineNumber = src->LineNumber; - - if(shallow) - break; - - yamlResouce.second.StoreString(std::string(src->Anchor)); - dst->Anchor = StringView(*yamlResouce.second.WriteData.back()); - - switch(src->GetType()) - { - case NodeType::Scalar: - { - std::string val = src->GetScalar().DS_TRY(); - yamlResouce.second.StoreString(val); - dst->Value = ScalarValue(*yamlResouce.second.WriteData.back()); - break; - } - case NodeType::Alias: - { - std::string val = src->GetAlias().DS_TRY(); - yamlResouce.second.StoreString(val); - dst->Value = Alias{ StringView(*yamlResouce.second.WriteData.back()) }; - break; - } - case NodeType::Sequence: - { - DS_ASSERT_TRUE(dst->IsSequence()); - mpark::get_if(&dst->Value)->clear(); - for(int i = 0; i < src->GetChildrenCount(); ++i) - { - NodePtr clonedNode = CreateNodePtr(); - clonedNode->Parent = dst.get(); - nodesToCloneStack.push(std::make_pair( clonedNode, - src->GetSequenceChildNode(i).get())); - mpark::get_if(&dst->Value)->push_back(clonedNode); - } - break; - } - case NodeType::Map: - { - DS_ASSERT_TRUE(dst->IsMap()); - OrderedMap& dstMap = *mpark::get_if(&dst->Value); - - dstMap.InsertedKeys.clear(); - dstMap.Map.clear(); - dstMap.StringMap.clear(); - - for(int i = 0; i < src->GetChildrenCount(); ++i) - { - NodePtr clonedKeyNode = CreateNodePtr(); - NodePtr clonedValNode = CreateNodePtr(); - clonedKeyNode->Parent = dst.get(); - clonedValNode->Parent = dst.get(); - - ConstNodePtr keyNode = src->GetMapKeyNodeAt(i); - DS_ASSERT_TRUE(keyNode->IsScalar() || keyNode->IsAlias()); - ConstNodePtr valueNode = src->GetMapValueNodeAt(i); - - nodesToCloneStack.push(std::make_pair(clonedKeyNode, keyNode.get())); - nodesToCloneStack.push(std::make_pair(clonedValNode, valueNode.get())); - - dstMap.InsertedKeys.push_back(clonedKeyNode); - dstMap.Map[clonedKeyNode] = clonedValNode; - - if(keyNode->IsScalar()) - { - std::string key = keyNode->GetScalar().DS_TRY(); - yamlResouce.second.StoreString(key); - StringView keyView = StringView(*yamlResouce.second.WriteData.back()); - dstMap.StringMap[keyView] = clonedValNode; - } - } - break; - } - default: - return DS_ERROR_MSG("Invalid node type: " + DS_STR((int)src->GetType())); - } - } //while(!nodesToCloneStack.empty()) - - return returnNode; - } - -} //namespace runcpp2 - - - - diff --git a/Src/runcpp2/LibYAML_Wrapper.hpp b/Src/runcpp2/LibYAML_Wrapper.hpp new file mode 100644 index 0000000..df94db0 --- /dev/null +++ b/Src/runcpp2/LibYAML_Wrapper.hpp @@ -0,0 +1,1782 @@ +#ifndef RUNCPP2_LIB_YAML_WRAPPER_HPP +#define RUNCPP2_LIB_YAML_WRAPPER_HPP + +#include "DSResult/DSResult.hpp" +#include "mpark/variant.hpp" +#include "nonstd/string_view.hpp" +#include "yaml.h" +#include "ssLogger/ssLog.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef yaml_event_s yaml_event_t; +typedef yaml_parser_s yaml_parser_t; + +#if RUNCPP2_YAML_PRINT_PARSE + #include + #define RUNCPP2_YAML_PRINT(...) printf(__VA_ARGS__) +#else + #define RUNCPP2_YAML_PRINT(...) do{} while(false) +#endif + +namespace runcpp2 +{ + using StringView = nonstd::string_view; + namespace YAML + { + struct Node; + using ScalarValue = StringView; + struct Alias; + using NodePtr = std::shared_ptr; + using Sequence = std::vector; + struct OrderedMap; + using NodeValue = mpark::variant; + } +} + +namespace +{ + std::string EscapeString(const runcpp2::StringView string); + std::string ScalarToString(const runcpp2::YAML::ScalarValue& scalar); + std::string AliasToString(const runcpp2::YAML::Alias& alias); + + template + DS::Result AddValueToMap( std::stack>& nodeValCountStack, + const YamlValueType& newVal, + const yaml_event_t& event); + + DS::Result ParseHandleMap(yaml_event_t& event, + std::stack>& nodeValCountStack); + + DS::Result ParseHandleSequence( yaml_event_t& event, + std::stack>& nodeValCountStack); + + DS::Result ParseHandleScalar( yaml_event_t& event, + std::stack>& nodeValCountStack); + + DS::Result ParseHandleAlias( yaml_event_t& event, + std::stack>& nodeValCountStack); + + DS::Result CopyVariant(runcpp2::YAML::NodeValue& dest, const runcpp2::YAML::NodeValue& src); + + DS::Result UpdateParentsRecursively(runcpp2::YAML::Node& currentNode); + + //NOTE: This doesn't update OrderedMap.StringMap + DS::Result ResolveMergeKey( runcpp2::YAML::OrderedMap& currentMap, + int mergeKeyIndex, + const std::unordered_map< runcpp2::StringView, + runcpp2::YAML::Node*>& anchors); +} + +namespace runcpp2 +{ + namespace YAML + { + enum class NodeType + { + Scalar, + Alias, + Sequence, + Map + }; + + inline StringView NodeTypeToString(NodeType type); + + using NodePtr = std::shared_ptr; + using ConstNodePtr = std::shared_ptr; + + struct ReadWriteBuffer + { + std::vector> ReadData = {}; + //NOTE: Needs to be unique_ptr due to small string optimizations + std::vector> WriteData = {}; + + inline std::string& CreateString() + { + WriteData.emplace_back(std::unique_ptr(new std::string())); + return *WriteData.back().get(); + } + + inline void StoreString(const std::string& str) + { + CreateString() = str; + } + }; + + using ResourceHandle = std::pair; + + inline NodePtr CreateNodePtr() { return std::make_shared(); } + + //NOTE: all parameters must be alive while using the node + inline DS::Result> ParseYAML( StringView yamlString, + ResourceHandle& outResource); + + inline DS::Result ResolveAnchors(NodePtr rootNode); + + inline DS::Result FreeYAMLResource(ResourceHandle& resourceHandleToFree); + + struct Alias + { + StringView Value; + }; + + using Sequence = std::vector; + using ScalarValue = StringView; + + struct OrderedMap + { + std::vector InsertedKeys; + std::unordered_map Map; + std::unordered_map StringMap; + }; + + using NodeValue = mpark::variant; + + struct Node + { + NodeValue Value = ""; + StringView Anchor = ""; + int LineNumber = -1; + Node* Parent = nullptr; + + //Reading + inline NodeType GetType() const { return (NodeType)Value.index(); } + + inline bool IsAlias() const { return mpark::holds_alternative(Value); }; + inline bool IsScalar() const { return mpark::holds_alternative(Value); }; + inline bool IsSequence() const { return mpark::holds_alternative(Value); }; + inline bool IsMap() const { return mpark::holds_alternative(Value); }; + + template + inline DS::Result GetAlias() const; + + template + inline DS::Result GetScalar() const; + + inline NodePtr GetSequenceChildNode(uint32_t index); + inline ConstNodePtr GetSequenceChildNode(uint32_t index) const; + + template + inline DS::Result GetSequenceChildAlias(uint32_t index) const; + + template + inline DS::Result GetSequenceChildScalar(uint32_t index) const; + + inline bool HasMapKey(StringView key) const; + inline NodePtr GetMapValueNode(StringView key); + inline ConstNodePtr GetMapValueNode(StringView key) const; + + template + inline DS::Result GetMapValueAlias(StringView key) const; + + template + inline DS::Result GetMapValueScalar(StringView key) const; + + inline NodePtr GetMapKeyNodeAt(uint32_t index); + inline ConstNodePtr GetMapKeyNodeAt(uint32_t index) const; + + template + inline DS::Result GetMapKeyAliasAt(uint32_t index) const; + + template + inline DS::Result GetMapKeyScalarAt(uint32_t index) const; + + inline NodePtr GetMapValueNodeAt(uint32_t index); + inline ConstNodePtr GetMapValueNodeAt(uint32_t index) const; + + template + inline DS::Result GetMapValueAliasAt(uint32_t index) const; + + template + inline DS::Result GetMapValueScalarAt(uint32_t index) const; + + inline uint32_t GetChildrenCount() const; + + //Writing + DS::Result CloneToSequenceChild( NodePtr parentNode, + ResourceHandle& yamlResouce) const; + + //TODO: Support ordering sequence child + + //TODO: Support erasing sequence child + + //NOTE: `key` will be copied + DS::Result CloneToMapChild(StringView key, + NodePtr parentNode, + ResourceHandle& yamlResouce) const; + + //TODO: Support ordering map child + + //TODO: Support erasing map child + inline DS::Result RemoveMapChild(StringView key); + + + //TODO: Proper Writing + + //TODO: Support null (null tag and ~)? + + DS::Result ToString(std::string& outString) const; + + DS::Result Clone(bool shallow, ResourceHandle& yamlResouce) const; + }; + + //================================================== + //Node functions inline implementations + //================================================== + + inline StringView NodeTypeToString(NodeType type) + { + switch(type) + { + case NodeType::Scalar: + return "Scalar"; + case NodeType::Alias: + return "Alias"; + case NodeType::Sequence: + return "Sequence"; + case NodeType::Map: + return "Map"; + default: + return ""; + } + } + + inline DS::Result> ParseYAML( StringView yamlString, + ResourceHandle& outResource) + { + ssLOG_FUNC_DEBUG(); + std::vector rootNodes; + + outResource.first = nullptr; + outResource.first = new yaml_parser_t(); + DS_ASSERT_TRUE(outResource.first != nullptr); + yaml_parser_t& parser = *outResource.first; + ReadWriteBuffer& readBuffer = outResource.second; + + DS_ASSERT_EQ(yaml_parser_initialize(&parser), 1); + + yaml_parser_set_input_string( &parser, + (const unsigned char*)yamlString.data(), + yamlString.size()); + + std::stack> nodeValCountStack; + + while(true) + { + readBuffer.ReadData.emplace_back(std::shared_ptr(new yaml_event_t())); + yaml_event_t& event = *readBuffer.ReadData.back().get(); + + if(!yaml_parser_parse(&parser, &event)) + { + if(parser.problem_mark.line || parser.problem_mark.column) + { + return DS_ERROR_MSG(DS_STR("Parsing failed: ") + parser.problem + " on line " + + DS_STR(parser.problem_mark.line + 1) + ", column" + + DS_STR(parser.problem_mark.column + 1)); + } + else + return DS_ERROR_MSG(DS_STR("Parsing failed: ") + parser.problem); + } + + switch(event.type) + { + case YAML_NO_EVENT: + ssLOG_DEBUG("???\n"); + break; + case YAML_STREAM_START_EVENT: + ssLOG_DEBUG("+STR\n"); + break; + case YAML_STREAM_END_EVENT: + ssLOG_DEBUG("-STR\n"); + break; + case YAML_DOCUMENT_START_EVENT: + if(!event.data.document_start.implicit) + ssLOG_DEBUG("+DOC ---"); + else + ssLOG_DEBUG("+DOC"); + rootNodes.push_back(CreateNodePtr()); + nodeValCountStack.push(std::pair(rootNodes.back().get(), -1)); + break; + case YAML_DOCUMENT_END_EVENT: + if(!event.data.document_end.implicit) + ssLOG_DEBUG("-DOC ..."); + else + ssLOG_DEBUG("-DOC"); + + //NOTE: Node is popped either by map end or sequence end event, + // hence it should be empty. + DS_ASSERT_TRUE(nodeValCountStack.empty()); + break; + case YAML_MAPPING_START_EVENT: + DS_UNWRAP_VOID(ParseHandleMap(event, nodeValCountStack)); + break; + case YAML_MAPPING_END_EVENT: + nodeValCountStack.pop(); + ssLOG_DEBUG("-MAP"); + break; + case YAML_SEQUENCE_START_EVENT: + DS_UNWRAP_VOID(ParseHandleSequence(event, nodeValCountStack)); + break; + case YAML_SEQUENCE_END_EVENT: + ssLOG_DEBUG("-SEQ"); + nodeValCountStack.pop(); + break; + case YAML_SCALAR_EVENT: + DS_UNWRAP_VOID(ParseHandleScalar(event, nodeValCountStack)); + break; + case YAML_ALIAS_EVENT: + DS_UNWRAP_VOID(ParseHandleAlias(event, nodeValCountStack)); + break; + default: + return DS_ERROR_MSG(DS_STR("Unrecognize event type: ") + + DS_STR((int)event.type)); + } //switch(event.type) + + if(event.type == YAML_STREAM_END_EVENT) + break; + } //while(true) + + return rootNodes; + } + + inline DS::Result ResolveAnchors(NodePtr rootNode) + { + DS_ASSERT_FALSE(mpark::holds_alternative(rootNode->Value) || + mpark::holds_alternative(rootNode->Value)); + + std::stack> nodeValCountStack; + nodeValCountStack.push(std::pair(rootNode.get(), 0)); + + std::unordered_map anchors; + std::vector mapsToUpdateStringMapKeys; + + while(!nodeValCountStack.empty()) + { + Node& currentNode = *nodeValCountStack.top().first; + int visitCount = nodeValCountStack.top().second++; + + //We visited once already, all the children are processed. + if(visitCount >= 1) + { + nodeValCountStack.pop(); + continue; + } + + if(mpark::holds_alternative(currentNode.Value)) + { + StringView aliasValue = mpark::get_if(¤tNode.Value)->Value; + DS_ASSERT_FALSE(aliasValue.empty()); + if(anchors.count(aliasValue) == 0) + { + return DS_ERROR_MSG("Cannot find anchor value: " + DS_STR(aliasValue) + + " on line " + DS_STR(currentNode.LineNumber)); + } + + //Check parents don't have the anchor we want + Node* parent = ¤tNode; + while(parent != nullptr) + { + if(parent->Anchor == aliasValue) + { + return DS_ERROR_MSG("Cannot alias parent anchor: " + + DS_STR(aliasValue) + + " on line " + DS_STR(currentNode.LineNumber) + + " with parent on line " + + DS_STR(parent->LineNumber)); + } + parent = parent->Parent; + } + + //Resolve alias with the anchor we found + DS_UNWRAP_VOID(CopyVariant(currentNode.Value, anchors[aliasValue]->Value)); + + //Check if parent is map, remember to update the string map + if( currentNode.Parent != nullptr && + mpark::holds_alternative(currentNode.Parent->Value)) + { + mapsToUpdateStringMapKeys.push_back(currentNode.Parent); + } + + //The parent entry for this node is correct, but not the children, update them. + DS_UNWRAP_VOID(UpdateParentsRecursively(currentNode)); + } + else if(mpark::holds_alternative(currentNode.Value)) + ++(nodeValCountStack.top().second); + else if(mpark::holds_alternative(currentNode.Value)) + { + Sequence& sequence = *mpark::get_if(¤tNode.Value); + for(int i = sequence.size() - 1; i >= 0; --i) + nodeValCountStack.push(std::pair(sequence[i].get(), 0)); + } + else if(mpark::holds_alternative(currentNode.Value)) + { + OrderedMap& orderedMap = *mpark::get_if(¤tNode.Value); + bool hasMergeKeys = false; + + //Resolve merge keys first if any + for(int i = 0; i < orderedMap.InsertedKeys.size(); ++i) + { + NodePtr currentKey = orderedMap.InsertedKeys[i]; + if(!mpark::holds_alternative(currentKey->Value)) + continue; + + //TODO: Add support for alias map key + if(mpark::get_if(¤tKey->Value)->Value != "<<") + return DS_ERROR_MSG("Alias key must be a merge key"); + + //Move all the alias children to current map + DS_UNWRAP_VOID(ResolveMergeKey(orderedMap, i, anchors)); + + hasMergeKeys = true; + } + + //Need to udpate the parent entry for the children coming from merge keys, if any. + //Then we need to update the string map. + if(hasMergeKeys) + { + DS_UNWRAP_VOID(UpdateParentsRecursively(currentNode)); + mapsToUpdateStringMapKeys.push_back(¤tNode); + } + + //Push all the children to the stack to be resolved + for(int i = orderedMap.InsertedKeys.size() - 1; i >= 0; --i) + { + NodePtr currentKey = orderedMap.InsertedKeys[i]; + nodeValCountStack.push(std::pair(orderedMap.Map[currentKey].get(), 0)); + nodeValCountStack.push(std::pair(currentKey.get(), 0)); + } + } + else + return DS_ERROR_MSG("Invalid type"); + + if(!currentNode.Anchor.empty()) + anchors[currentNode.Anchor] = ¤tNode; + } //while(!nodeValCountStack.empty()) + + //Update string map for maps in mapsToUpdateStringMapKeys for any missing scalar keys + for(int i = 0; i < mapsToUpdateStringMapKeys.size(); ++i) + { + DS_ASSERT_TRUE(mpark::holds_alternative(mapsToUpdateStringMapKeys[i]->Value)); + OrderedMap& currentMap = *mpark::get_if(&mapsToUpdateStringMapKeys[i]->Value); + + for(int j = 0; j < currentMap.InsertedKeys.size(); ++j) + { + if(mpark::holds_alternative(currentMap.InsertedKeys[j]->Value)) + { + ScalarValue& curKey = *mpark::get_if(¤tMap .InsertedKeys[j] + ->Value); + DS_ASSERT_TRUE(currentMap.Map.count(currentMap.InsertedKeys[j]) > 0); + if(currentMap.StringMap.count(curKey) == 0) + currentMap.StringMap[curKey] = currentMap.Map[currentMap.InsertedKeys[j]]; + } + } + } + + return {}; + } + + inline DS::Result FreeYAMLResource(ResourceHandle& resourceHandleToFree) + { + DS_ASSERT_TRUE(resourceHandleToFree.first != nullptr); + + for(int i = 0; i < resourceHandleToFree.second.ReadData.size(); ++i) + yaml_event_delete(resourceHandleToFree.second.ReadData[i].get()); + + yaml_parser_delete(resourceHandleToFree.first); + + delete resourceHandleToFree.first; + resourceHandleToFree.first = nullptr; + resourceHandleToFree.second = ReadWriteBuffer(); + return {}; + } + + template<> + inline DS::Result Node::GetAlias() const + { + DS_ASSERT_TRUE(IsAlias()); + return mpark::get_if(&Value)->Value; + } + + template<> + inline DS::Result Node::GetAlias() const + { + DS_ASSERT_TRUE(IsAlias()); + return (std::string)mpark::get_if(&Value)->Value; + } + + #define INTERNAL_RUNCPP2_TRY_CONVERT_SCALAR(convertFunc) \ + do \ + { \ + try \ + { \ + return convertFunc((std::string)*mpark::get_if(&Value)); \ + } \ + catch(std::exception& ex) \ + { \ + return DS_ERROR_MSG(ex.what()); \ + } \ + } while(false) + + template<> + inline DS::Result Node::GetScalar() const + { + DS_ASSERT_TRUE(IsScalar()); + INTERNAL_RUNCPP2_TRY_CONVERT_SCALAR(std::stoi); + } + + template<> + inline DS::Result Node::GetScalar() const + { + DS_ASSERT_TRUE(IsScalar()); + INTERNAL_RUNCPP2_TRY_CONVERT_SCALAR(std::stoul); + } + + template<> + inline DS::Result Node::GetScalar() const + { + DS_ASSERT_TRUE(IsScalar()); + INTERNAL_RUNCPP2_TRY_CONVERT_SCALAR(std::stol); + } + + template<> + inline DS::Result Node::GetScalar() const + { + DS_ASSERT_TRUE(IsScalar()); + INTERNAL_RUNCPP2_TRY_CONVERT_SCALAR(std::stoull); + } + + template<> + inline DS::Result Node::GetScalar() const + { + DS_ASSERT_TRUE(IsScalar()); + INTERNAL_RUNCPP2_TRY_CONVERT_SCALAR(std::stoll); + } + + template<> + inline DS::Result Node::GetScalar() const + { + DS_UNWRAP_DECL(unsigned long long ull, GetScalar()); + DS_ASSERT_LT_EQ(ull, std::numeric_limits::max()); + return (unsigned int)ull; + } + + template<> + inline DS::Result Node::GetScalar() const + { + DS_ASSERT_TRUE(IsScalar()); + INTERNAL_RUNCPP2_TRY_CONVERT_SCALAR(std::stof); + } + + template<> + inline DS::Result Node::GetScalar() const + { + DS_ASSERT_TRUE(IsScalar()); + INTERNAL_RUNCPP2_TRY_CONVERT_SCALAR(std::stod); + } + + #undef INTERNAL_RUNCPP2_TRY_CONVERT_SCALAR + + template<> + inline DS::Result Node::GetScalar() const + { + DS_ASSERT_TRUE(IsScalar()); + + std::string v = (std::string)*mpark::get_if(&Value); + for(int i = 0; i < v.size(); ++i) + v[i] = std::tolower(v[i]); + + //TODO: Strict mode which only allows lowercase true and false? + bool isTrue = v == "true" || v == "yes" || v == "on" || v == "1"; + if(isTrue) + return true; + else if(v == "false" || v == "no" || v == "off" || v == "0") + return false; + else + return DS_ERROR_MSG("Invalid scalar value \"" + v + "\" to be converted to bool"); + } + + template<> + inline DS::Result Node::GetScalar() const + { + DS_ASSERT_TRUE(IsScalar()); + return *mpark::get_if(&Value); + } + + template<> + inline DS::Result Node::GetScalar() const + { + DS_ASSERT_TRUE(IsScalar()); + return (std::string)*mpark::get_if(&Value); + } + + inline NodePtr Node::GetSequenceChildNode(uint32_t index) + { + if(!IsSequence() || index >= mpark::get_if(&Value)->size()) + return nullptr; + return (*mpark::get_if(&Value))[index]; + } + + inline ConstNodePtr Node::GetSequenceChildNode(uint32_t index) const + { + if(!IsSequence() || index >= mpark::get_if(&Value)->size()) + return nullptr; + return (*mpark::get_if(&Value))[index]; + } + + template + inline DS::Result Node::GetSequenceChildAlias(uint32_t index) const + { + DS_ASSERT_TRUE(IsSequence()); + DS_ASSERT_LT(index, mpark::get_if(&Value)->size()); + return (*mpark::get_if(&Value))[index]->GetAlias(); + } + + template + inline DS::Result Node::GetSequenceChildScalar(uint32_t index) const + { + DS_ASSERT_TRUE(IsSequence()); + DS_ASSERT_LT(index, mpark::get_if(&Value)->size()); + return (*mpark::get_if(&Value))[index]->GetScalar(); + } + + inline bool Node::HasMapKey(StringView key) const + { + if(!IsMap()) + return false; + return mpark::get_if(&Value)->StringMap.count(key) > 0; + } + + inline NodePtr Node::GetMapValueNode(StringView key) + { + if(!IsMap() || mpark::get_if(&Value)->StringMap.count(key) == 0) + return nullptr; + return mpark::get_if(&Value)->StringMap.at(key); + } + + inline ConstNodePtr Node::GetMapValueNode(StringView key) const + { + if(!IsMap() || mpark::get_if(&Value)->StringMap.count(key) == 0) + return nullptr; + return mpark::get_if(&Value)->StringMap.at(key); + } + + template + inline DS::Result Node::GetMapValueAlias(StringView key) const + { + DS_ASSERT_TRUE(IsMap()); + DS_ASSERT_TRUE(HasMapKey(key)); + return mpark::get_if(&Value)->StringMap.at(key)->GetAlias(); + } + + template + inline DS::Result Node::GetMapValueScalar(StringView key) const + { + DS_ASSERT_TRUE(IsMap()); + DS_ASSERT_TRUE(HasMapKey(key)); + return mpark::get_if(&Value)->StringMap.at(key)->GetScalar(); + } + + inline NodePtr Node::GetMapKeyNodeAt(uint32_t index) + { + if(!IsMap() || index >= mpark::get_if(&Value)->InsertedKeys.size()) + return nullptr; + return mpark::get_if(&Value)->InsertedKeys[index]; + } + + inline ConstNodePtr Node::GetMapKeyNodeAt(uint32_t index) const + { + if(!IsMap() || index >= mpark::get_if(&Value)->InsertedKeys.size()) + return nullptr; + return mpark::get_if(&Value)->InsertedKeys[index]; + } + + template + inline DS::Result Node::GetMapKeyAliasAt(uint32_t index) const + { + DS_ASSERT_TRUE(IsMap()); + DS_ASSERT_LT(index, mpark::get_if(&Value)->InsertedKeys.size()); + return mpark::get_if(&Value)->InsertedKeys[index]->GetAlias(); + } + + template + inline DS::Result Node::GetMapKeyScalarAt(uint32_t index) const + { + DS_ASSERT_TRUE(IsMap()); + DS_ASSERT_LT(index, mpark::get_if(&Value)->InsertedKeys.size()); + return mpark::get_if(&Value)->InsertedKeys[index]->GetScalar(); + } + + inline NodePtr Node::GetMapValueNodeAt(uint32_t index) + { + if(!IsMap() || index >= mpark::get_if(&Value)->InsertedKeys.size()) + return nullptr; + + NodePtr keyNode = mpark::get_if(&Value)->InsertedKeys[index]; + if(mpark::get_if(&Value)->Map.count(keyNode) == 0) + return nullptr; + + return mpark::get_if(&Value)->Map.at(keyNode); + } + + inline ConstNodePtr Node::GetMapValueNodeAt(uint32_t index) const + { + if(!IsMap() || index >= mpark::get_if(&Value)->InsertedKeys.size()) + return nullptr; + + NodePtr keyNode = mpark::get_if(&Value)->InsertedKeys[index]; + if(mpark::get_if(&Value)->Map.count(keyNode) == 0) + return nullptr; + + return mpark::get_if(&Value)->Map.at(keyNode); + } + + template + inline DS::Result Node::GetMapValueAliasAt(uint32_t index) const + { + DS_ASSERT_TRUE(IsMap()); + DS_ASSERT_LT(index, mpark::get_if(&Value)->InsertedKeys.size()); + + NodePtr keyNode = mpark::get_if(&Value)->InsertedKeys[index]; + DS_ASSERT_NOT_EQ(mpark::get_if(&Value)->Map.count(keyNode), 0); + + return mpark::get_if(&Value)->Map.at(keyNode)->GetAlias(); + } + + template + inline DS::Result Node::GetMapValueScalarAt(uint32_t index) const + { + DS_ASSERT_TRUE(IsMap()); + DS_ASSERT_LT(index, mpark::get_if(&Value)->InsertedKeys.size()); + + NodePtr keyNode = mpark::get_if(&Value)->InsertedKeys[index]; + DS_ASSERT_NOT_EQ(mpark::get_if(&Value)->Map.count(keyNode), 0); + + return mpark::get_if(&Value)->Map.at(keyNode)->GetScalar(); + } + + inline uint32_t Node::GetChildrenCount() const + { + if(IsAlias() || IsScalar()) + return 0; + else if(IsMap()) + return mpark::get_if(&Value)->InsertedKeys.size(); + else if(IsSequence()) + return mpark::get_if(&Value)->size(); + else + return 0; + } + + //TODO: Reorder the whole thing to match declaration order + inline DS::Result Node::CloneToSequenceChild( NodePtr parentNode, + ResourceHandle& yamlResouce) const + { + DS_ASSERT_TRUE(parentNode->IsSequence()); + NodePtr clonedThis = Clone(false, yamlResouce).DS_TRY(); + clonedThis->Parent = parentNode.get(); + mpark::get_if(&parentNode->Value)->push_back(clonedThis); + return clonedThis; + } + + inline DS::Result Node::CloneToMapChild( StringView key, + NodePtr parentNode, + ResourceHandle& yamlResouce) const + { + DS_ASSERT_TRUE(parentNode->IsMap()); + NodePtr clonedThis = Clone(false, yamlResouce).DS_TRY(); + clonedThis->Parent = parentNode.get(); + yamlResouce.second.StoreString(std::string(key)); + StringView keyView = StringView(*yamlResouce.second.WriteData.back()); + NodePtr keyNode = CreateNodePtr(); + keyNode->Value = keyView; + keyNode->LineNumber = LineNumber; + + mpark::get_if(&parentNode->Value)->InsertedKeys.push_back(keyNode); + mpark::get_if(&parentNode->Value)->Map[keyNode] = clonedThis; + mpark::get_if(&parentNode->Value)->StringMap[keyView] = clonedThis; + return clonedThis; + } + + inline DS::Result Node::RemoveMapChild(StringView key) + { + DS_ASSERT_TRUE(IsMap()); + OrderedMap& orderedMap = *mpark::get_if(&Value); + if(orderedMap.StringMap.count(key) == 0) + return {}; + + //NodePtr valueNode = orderedMap.StringMap[key]; + NodePtr keyNode = nullptr; + int keyNodeIndex = -1; + for(int i = 0; i < orderedMap.InsertedKeys.size(); ++i) + { + if( orderedMap.InsertedKeys[i]->IsScalar() && + orderedMap.InsertedKeys.at(i)->GetScalar().Value() == key) + { + keyNode = orderedMap.InsertedKeys[i]; + keyNodeIndex = i; + break; + } + } + + DS_ASSERT_TRUE(keyNodeIndex != -1); + orderedMap.InsertedKeys.erase(orderedMap.InsertedKeys.begin() + keyNodeIndex); + orderedMap.Map.erase(keyNode); + orderedMap.StringMap.erase(key); + return {}; + } + + inline DS::Result Node::ToString(std::string& outString) const + { + std::stack nodeStack; + std::stack nodePrefixStack; + std::stack nodeIndentLevelStack; + + //if(!mpark::holds_alternative(Value) && !mpark::holds_alternative(Value)) + // return DS_ERROR_MSG("Starting value must be a map or sequence"); + + nodeStack.push(this); + nodePrefixStack.push(""); + nodeIndentLevelStack.push(0); + + while(!nodeStack.empty()) + { + outString += nodePrefixStack.top(); + + /* Scalar Output Format: + [anchor] + */ + if(mpark::holds_alternative(nodeStack.top()->Value)) + { + if(!nodeStack.top()->Anchor.empty()) + outString += " &" + std::string(nodeStack.top()->Anchor) + " "; + + outString += ScalarToString(*mpark::get_if(&(nodeStack.top()->Value))); + outString += "\n"; + nodeStack.pop(); + nodePrefixStack.pop(); + nodeIndentLevelStack.pop(); + } + /* Alias Output Format: + * + */ + else if(mpark::holds_alternative(nodeStack.top()->Value)) + { + if(!nodeStack.top()->Anchor.empty()) + return DS_ERROR_MSG("Alias node cannot have anchor"); + + outString += AliasToString(*mpark::get_if(&(nodeStack.top()->Value))); + outString += "\n"; + nodeStack.pop(); + nodePrefixStack.pop(); + nodeIndentLevelStack.pop(); + } + /* Sequence Output Format: + - + - + */ + else if(mpark::holds_alternative(nodeStack.top()->Value)) + { + const Sequence* seq = mpark::get_if(&nodeStack.top()->Value); + int indentLevel = nodeIndentLevelStack.top(); + std::string prefix = nodePrefixStack.top(); + nodeStack.pop(); + nodePrefixStack.pop(); + nodeIndentLevelStack.pop(); + if(seq->empty()) + continue; + //We iterate from back to front since we are pushing to a stack + for(int i = seq->size() - 1; i >= 0; --i) + { + nodeStack.push((*seq)[i].get()); + nodePrefixStack.push(std::string(indentLevel, ' ') + "- "); + nodeIndentLevelStack.push(indentLevel + 4); + } + nodePrefixStack.top() = "- "; //No need to indent for the first one + } + /* Map Output Format: + : + : [child sequqence anchor] + + : [child map anchor] + + */ + else if(mpark::holds_alternative(nodeStack.top()->Value)) + { + const OrderedMap& map = *mpark::get_if(&(nodeStack.top()->Value)); + int indentLevel = nodeIndentLevelStack.top(); + nodeStack.pop(); + nodePrefixStack.pop(); + nodeIndentLevelStack.pop(); + + if(map.InsertedKeys.empty()) + continue; + + //We iterate from back to front since we are pushing to a stack + for(int i = map.InsertedKeys.size() - 1; i >= 0; --i) + { + std::string prefix; + if(mpark::holds_alternative(map.InsertedKeys[i]->Value)) + prefix = (std::string)*mpark::get_if(&map.InsertedKeys[i]->Value); + else if(mpark::holds_alternative(map.InsertedKeys[i]->Value)) + { + //TODO: Add support for alias map key + const Alias& aliasVal = *mpark::get_if(&map.InsertedKeys[i]->Value); + if(aliasVal.Value != "<<") + { + return DS_ERROR_MSG(DS_STR("Merge key must be <<, \"") + + DS_STR(aliasVal.Value) + "\" is found instead"); + } + else + prefix = (std::string)aliasVal.Value; + } + else + return DS_ERROR_MSG("Complex key is not supported"); + + prefix += ": "; + + //We don't add indent for the first key/value + if(i != 0) + prefix = std::string(indentLevel, ' ') + prefix; + + NodePtr childNode = map.Map.at(map.InsertedKeys[i]); + std::string anchor = childNode->Anchor.empty() ? + "" : + "&" + std::string(childNode->Anchor); + if(mpark::holds_alternative(childNode->Value)) + { + nodeStack.push(childNode.get()); + nodePrefixStack.push( prefix + anchor + "\n" + + std::string(indentLevel, ' ')); + nodeIndentLevelStack.push(indentLevel); + } + else if(mpark::holds_alternative(childNode->Value)) + { + nodeStack.push(childNode.get()); + nodePrefixStack.push( prefix + anchor + "\n" + + std::string(indentLevel + 4, ' ')); + nodeIndentLevelStack.push(indentLevel + 4); + } + else if(mpark::holds_alternative(childNode->Value) || + mpark::holds_alternative(childNode->Value)) + { + nodeStack.push(childNode.get()); + nodePrefixStack.push(prefix); + nodeIndentLevelStack.push(indentLevel); + } + else + return DS_ERROR_MSG("Invalid type"); + } //for(int i = map.InsertedKeys.size() - 1; i >= 0; --i) + } //else if(mpark::holds_alternative(nodeStack.top()->Value)) + else + return DS_ERROR_MSG("Invalid type"); + } //while(!nodeStack.empty()) + + return {}; + } + + inline DS::Result Node::Clone(bool shallow, ResourceHandle& yamlResouce) const + { + std::stack> nodesToCloneStack; //Dst, src + + { + NodePtr clonedNode = CreateNodePtr(); + clonedNode->Parent = nullptr; + nodesToCloneStack.push(std::make_pair(clonedNode, this)); + } + + NodePtr returnNode = nodesToCloneStack.top().first; + + while(!nodesToCloneStack.empty()) + { + NodePtr dst = nodesToCloneStack.top().first; + const Node* src = nodesToCloneStack.top().second; + nodesToCloneStack.pop(); + + CopyVariant(dst->Value, src->Value).DS_TRY(); + dst->Anchor = src->Anchor; + dst->LineNumber = src->LineNumber; + + if(shallow) + break; + + yamlResouce.second.StoreString(std::string(src->Anchor)); + dst->Anchor = StringView(*yamlResouce.second.WriteData.back()); + + switch(src->GetType()) + { + case NodeType::Scalar: + { + std::string val = src->GetScalar().DS_TRY(); + yamlResouce.second.StoreString(val); + dst->Value = ScalarValue(*yamlResouce.second.WriteData.back()); + break; + } + case NodeType::Alias: + { + std::string val = src->GetAlias().DS_TRY(); + yamlResouce.second.StoreString(val); + dst->Value = Alias{ StringView(*yamlResouce.second.WriteData.back()) }; + break; + } + case NodeType::Sequence: + { + DS_ASSERT_TRUE(dst->IsSequence()); + mpark::get_if(&dst->Value)->clear(); + for(int i = 0; i < src->GetChildrenCount(); ++i) + { + NodePtr clonedNode = CreateNodePtr(); + clonedNode->Parent = dst.get(); + nodesToCloneStack.push(std::make_pair( clonedNode, + src->GetSequenceChildNode(i).get())); + mpark::get_if(&dst->Value)->push_back(clonedNode); + } + break; + } + case NodeType::Map: + { + DS_ASSERT_TRUE(dst->IsMap()); + OrderedMap& dstMap = *mpark::get_if(&dst->Value); + + dstMap.InsertedKeys.clear(); + dstMap.Map.clear(); + dstMap.StringMap.clear(); + + for(int i = 0; i < src->GetChildrenCount(); ++i) + { + NodePtr clonedKeyNode = CreateNodePtr(); + NodePtr clonedValNode = CreateNodePtr(); + clonedKeyNode->Parent = dst.get(); + clonedValNode->Parent = dst.get(); + + ConstNodePtr keyNode = src->GetMapKeyNodeAt(i); + DS_ASSERT_TRUE(keyNode->IsScalar() || keyNode->IsAlias()); + ConstNodePtr valueNode = src->GetMapValueNodeAt(i); + + nodesToCloneStack.push(std::make_pair(clonedKeyNode, keyNode.get())); + nodesToCloneStack.push(std::make_pair(clonedValNode, valueNode.get())); + + dstMap.InsertedKeys.push_back(clonedKeyNode); + dstMap.Map[clonedKeyNode] = clonedValNode; + + if(keyNode->IsScalar()) + { + std::string key = keyNode->GetScalar().DS_TRY(); + yamlResouce.second.StoreString(key); + StringView keyView = StringView(*yamlResouce.second.WriteData.back()); + dstMap.StringMap[keyView] = clonedValNode; + } + } + break; + } + default: + return DS_ERROR_MSG("Invalid node type: " + DS_STR((int)src->GetType())); + } + } //while(!nodesToCloneStack.empty()) + + return returnNode; + } + } +} + +namespace +{ + std::string EscapeString(const runcpp2::StringView string) + { + std::string outString = ""; + for(int i = 0; i < string.size(); ++i) + { + switch(string[i]) + { + case '\\': + outString += "\\\\"; + break; + case '\0': + outString += "\\0"; + break; + case '\b': + outString += "\\b"; + break; + case '\n': + outString += "\\n"; + break; + case '\r': + outString += "\\r"; + break; + case '\t': + outString += "\\t"; + break; + default: + outString += string[i]; + break; + } + } + return outString; + } + + std::string ScalarToString(const runcpp2::YAML::ScalarValue& scalar) + { + return std::string("\"") + EscapeString(scalar) + "\""; + } + + std::string AliasToString(const runcpp2::YAML::Alias& alias) + { + return std::string("*") + std::string(alias.Value.data(), alias.Value.size()); + } + + template + DS::Result AddValueToMap( std::stack>& nodeValCountStack, + const YamlValueType& newVal, + const yaml_event_t& event) + { + nodeValCountStack.top().second = 0; + DS_ASSERT_TRUE(nodeValCountStack.top().first->IsMap()); + runcpp2::YAML::OrderedMap* currentMap = + mpark::get_if(&nodeValCountStack.top().first->Value); + DS_ASSERT_NOT_EQ(currentMap, nullptr); + DS_ASSERT_FALSE(currentMap->InsertedKeys.empty()); + const runcpp2::YAML::NodePtr& lastKey = currentMap->InsertedKeys.back(); + + DS_ASSERT_GT(currentMap->Map.count(lastKey), 0); + DS_ASSERT_TRUE(currentMap->Map[lastKey] == nullptr); + currentMap->Map[lastKey] = std::make_shared(); + currentMap->Map[lastKey]->Value = newVal; + currentMap->Map[lastKey]->LineNumber = event.start_mark.line + 1; + currentMap->Map[lastKey]->Parent = nodeValCountStack.top().first; + + //Add to KeyMap if key is scalar + if(mpark::holds_alternative(lastKey->Value)) + { + auto* scalarVal = mpark::get_if(&lastKey->Value); + currentMap->StringMap[*scalarVal] = currentMap->Map[lastKey]; + } + + if(pushToStack) + { + nodeValCountStack.push(std::pair< runcpp2::YAML::Node*, + int>(currentMap->Map[lastKey].get(), 0)); + } + return {}; + } + + DS::Result ParseHandleMap(yaml_event_t& event, + std::stack>& nodeValCountStack) + { + //ssLOG_FUNC_DEBUG(); + switch(nodeValCountStack.top().second) + { + //Starting node + case -1: + nodeValCountStack.top().first->Value = runcpp2::YAML::OrderedMap(); + ++(nodeValCountStack.top().second); + break; + + //Fill key: + case 0: + { + runcpp2::YAML::NodeValue& currentParentValue = nodeValCountStack.top().first->Value; + + //We can only start a map as key in sequence node, error out + if(!mpark::holds_alternative(currentParentValue)) + { + //If we are in a map node, that means it is a complex key. + //We don't support complex key. + if(mpark::holds_alternative(currentParentValue)) + return DS_ERROR_MSG("Complex key is not supported"); + //If we are in scalar node, what? + else if(mpark::holds_alternative(currentParentValue)) + { + return DS_ERROR_MSG("Trying to create map in scalar node. " + "Missed node creation?"); + } + //If we are in alias, what? + else if(mpark::holds_alternative(currentParentValue)) + { + return DS_ERROR_MSG("Trying to create map in alias node. " + "Missed node creation?"); + } + //Invalid type + else + { + return DS_ERROR_MSG("Invalid node value type: " + + DS_STR(currentParentValue.index())); + } + } + + runcpp2::YAML::NodePtr newNode = std::make_shared(); + newNode->Value = runcpp2::YAML::OrderedMap(); + newNode->LineNumber = event.start_mark.line + 1; + newNode->Parent = nodeValCountStack.top().first; + mpark::get_if(¤tParentValue)->push_back(newNode); + nodeValCountStack.push(std::pair(newNode.get(), 0)); + break; + } + + //Key is filled, value is map + case 1: + { + runcpp2::YAML::NodeValue& currentParentValue = nodeValCountStack.top().first->Value; + + //We can only fill a value after key in a map node, error out + if(!mpark::holds_alternative(currentParentValue)) + { + if(mpark::holds_alternative(currentParentValue)) + { + return DS_ERROR_MSG("Trying to fill value in sequnce node. " + "Missed node creation?"); + } + else if(mpark::holds_alternative(currentParentValue)) + { + return DS_ERROR_MSG("Trying to fill value in scalar node. " + "Missed node creation?"); + } + else if(mpark::holds_alternative(currentParentValue)) + { + return DS_ERROR_MSG("Trying to fill value in alias node. " + "Missed node creation?"); + } + //Invalid type + else + { + return DS_ERROR_MSG("Invalid node value type: " + + DS_STR(currentParentValue.index())); + } + } + runcpp2::YAML::OrderedMap newMap = runcpp2::YAML::OrderedMap(); + DS_UNWRAP_VOID(AddValueToMap(nodeValCountStack, newMap, event)); + break; + } //case 1: + + //What? + default: + return DS_ERROR_MSG("Invalid node counter: " + DS_STR(nodeValCountStack.top().second)); + } //switch(nodeValCountStack.top().second) + + if(event.data.mapping_start.anchor) + { + nodeValCountStack.top().first->Anchor = + runcpp2::StringView((const char*)event.data.mapping_start.anchor); + } + + ssLOG_DEBUG("+MAP {}" << + ( + event.data.mapping_start.anchor ? + std::string(" &") + std::string((char*)event.data.mapping_start.anchor) : + std::string("") + ) << + ( + event.data.mapping_start.tag ? + std::string(" <") + std::string((char*)event.data.mapping_start.tag) + ">" : + std::string("") + )); + + return {}; + } + + DS::Result ParseHandleSequence( yaml_event_t& event, + std::stack>& nodeValCountStack) + { + //ssLOG_FUNC_DEBUG(); + switch(nodeValCountStack.top().second) + { + //Starting node + case -1: + nodeValCountStack.top().first->Value = runcpp2::YAML::Sequence(); + ++(nodeValCountStack.top().second); + break; + + //Fill key: + case 0: + { + runcpp2::YAML::NodeValue& currentParentValue = nodeValCountStack.top().first->Value; + //If we are in a sequence node, this means it is a nested sequence. + // fill it and append to stack + if(mpark::holds_alternative(currentParentValue)) + { + runcpp2::YAML::NodePtr newNode = runcpp2::YAML::CreateNodePtr(); + newNode->Value = runcpp2::YAML::Sequence(); + newNode->LineNumber = event.start_mark.line + 1; + newNode->Parent = nodeValCountStack.top().first; + mpark::get_if(¤tParentValue)->push_back(newNode); + nodeValCountStack.push(std::pair(newNode.get(), 0)); + } + //If we are in a map node, that means it is a complex key. + //We don't support complex key. + else if(mpark::holds_alternative(currentParentValue)) + return DS_ERROR_MSG("Complex key is not supported"); + //If we are in scalar node, what? + else if(mpark::holds_alternative(currentParentValue)) + return DS_ERROR_MSG("Trying to create map in scalar node. Missed node creation?"); + //If we are in alias node, what? + else if(mpark::holds_alternative(currentParentValue)) + return DS_ERROR_MSG("Trying to create map in alias node. Missed node creation?"); + //Invalid type + else + return DS_ERROR_MSG("Invalid node value type: " + DS_STR(currentParentValue.index())); + break; + } + + //Key is filled, value is sequence + case 1: + { + runcpp2::YAML::NodeValue& currentParentValue = nodeValCountStack.top().first->Value; + runcpp2::YAML::Sequence newSeq = runcpp2::YAML::Sequence(); + + //If we are in a map node, that means the value is a sequence + // fill it and append to stack + if(mpark::holds_alternative(currentParentValue)) + DS_UNWRAP_VOID(AddValueToMap(nodeValCountStack, newSeq, event)); + else if(mpark::holds_alternative(currentParentValue)) + return DS_ERROR_MSG("Trying to fill value in sequnce node. Missed node creation?"); + else if(mpark::holds_alternative(currentParentValue)) + return DS_ERROR_MSG("Trying to fill value in scalar node. Missed node creation?"); + else if(mpark::holds_alternative(currentParentValue)) + return DS_ERROR_MSG("Trying to fill value in alias node. Missed node creation?"); + //Invalid type + else + return DS_ERROR_MSG("Invalid node value type: " + DS_STR(currentParentValue.index())); + break; + } + + //What? + default: + return DS_ERROR_MSG("Invalid node counter: " + DS_STR(nodeValCountStack.top().second)); + } //switch(nodeValCountStack.top().second) + + if(event.data.sequence_start.anchor) + { + nodeValCountStack.top().first->Anchor = + runcpp2::StringView((const char*)event.data.sequence_start.anchor); + } + ssLOG_DEBUG("+SEQ []" << + ( + event.data.sequence_start.anchor ? + std::string(" &") + std::string((char*)event.data.sequence_start.anchor) : + std::string("") + ) << + ( + event.data.sequence_start.tag ? + std::string(" <") + std::string((char*)event.data.sequence_start.tag) + ">" : + std::string("") + )); + + return {}; + } + + DS::Result ParseHandleScalar( yaml_event_t& event, + std::stack>& nodeValCountStack) + { + //ssLOG_FUNC_DEBUG(); + std::string scalarStartStyle = ""; + std::string scalarEndStyle = ""; + + switch(event.data.scalar.style) + { + case YAML_PLAIN_SCALAR_STYLE: + scalarStartStyle = " :"; + break; + case YAML_SINGLE_QUOTED_SCALAR_STYLE: + scalarStartStyle = " '"; + scalarEndStyle = "'"; + break; + case YAML_DOUBLE_QUOTED_SCALAR_STYLE: + scalarStartStyle = " \""; + scalarEndStyle = "\""; + break; + case YAML_LITERAL_SCALAR_STYLE: + scalarStartStyle = " |"; + break; + case YAML_FOLDED_SCALAR_STYLE: + scalarStartStyle = " >"; + break; + case YAML_ANY_SCALAR_STYLE: + ssLOG_ERROR("Unrecognized scalar style"); + break; + } + + ssLOG_DEBUG("=VAL" << + ( + event.data.scalar.anchor ? + std::string(" &") + std::string((char*)event.data.scalar.anchor) : + std::string("") + ) << + ( + event.data.scalar.tag ? + std::string(" <") + std::string((char*)event.data.scalar.tag) + ">" : + std::string("") + ) << + scalarStartStyle << + EscapeString(runcpp2::StringView( (char*)event.data.scalar.value, + event.data.scalar.length)) << + scalarEndStyle); + + runcpp2::StringView scalarView = runcpp2::StringView( (const char*)event.data.scalar.value, + event.data.scalar.length); + switch(nodeValCountStack.top().second) + { + //Starting node + case -1: + return DS_ERROR_MSG("Starting node must be map or sequence"); + + //Fill key: + case 0: + { + if(event.data.scalar.anchor) + { + return DS_ERROR_MSG("Parsing error where key " + (std::string)scalarView + + " has anchor " + (char*)event.data.scalar.anchor); + } + + runcpp2::YAML::NodeValue& nodeValue = nodeValCountStack.top().first->Value; + runcpp2::YAML::NodePtr newNode = runcpp2::YAML::CreateNodePtr(); + newNode->LineNumber = event.start_mark.line + 1; + newNode->Parent = nodeValCountStack.top().first; + bool mergeKey = scalarView == "<<" && + event.data.scalar.style == YAML_PLAIN_SCALAR_STYLE && + mpark::holds_alternative(nodeValue); + if(!mergeKey) + newNode->Value = scalarView; + else + newNode->Value = runcpp2::YAML::Alias{scalarView}; + + //If we are in a sequence node, we just need to append the value + if(mpark::holds_alternative(nodeValue)) + mpark::get_if(&nodeValue)->emplace_back(newNode); + //If we are in a map node, we need to set the key + else if(mpark::holds_alternative(nodeValue)) + { + mpark::get_if(&nodeValue)->InsertedKeys + .push_back(newNode); + mpark::get_if(&nodeValue)->Map[newNode] = nullptr; + ++(nodeValCountStack.top().second); + } + //We should never in a scalar node + else if(mpark::holds_alternative(nodeValue)) + return DS_ERROR_MSG("Should not be in a scalar node"); + //We should never in a alias node + else if(mpark::holds_alternative(nodeValue)) + return DS_ERROR_MSG("Should not be in a alias node"); + //Invalid type + else + return DS_ERROR_MSG("Invalid node value type: " + DS_STR(nodeValue.index())); + break; + } + + //Key is filled, value is scalar + case 1: + { + runcpp2::YAML::NodeValue& nodeValue = nodeValCountStack.top().first->Value; + + //If we are in a map node, that means the value is scalar + if(mpark::holds_alternative(nodeValue)) + DS_UNWRAP_VOID(AddValueToMap(nodeValCountStack, scalarView, event)); + else if(mpark::holds_alternative(nodeValue)) + return DS_ERROR_MSG("Trying to fill value in sequnce node. Missed node creation?"); + //We should never in a scalar node + else if(mpark::holds_alternative(nodeValue)) + return DS_ERROR_MSG("Should not be in a scalar node"); + //We should never in a alias node + else if(mpark::holds_alternative(nodeValue)) + return DS_ERROR_MSG("Should not be in a alias node"); + //Invalid type + else + return DS_ERROR_MSG("Invalid node value type: " + DS_STR(nodeValue.index())); + + if(event.data.scalar.anchor) + { + auto& mapVal = *mpark::get_if(&nodeValue); + mapVal.Map[mapVal.InsertedKeys.back()]->Anchor = + runcpp2::StringView((const char*)event.data.scalar.anchor); + } + break; + } + + //What? + default: + return DS_ERROR_MSG("Invalid node counter: " + DS_STR(nodeValCountStack.top().second)); + } + + return {}; + } + + DS::Result ParseHandleAlias( yaml_event_t& event, + std::stack>& nodeValCountStack) + { + //ssLOG_FUNC_DEBUG(); + ssLOG_DEBUG("=ALI *" << (char*)event.data.alias.anchor); + + switch(nodeValCountStack.top().second) + { + //Starting node + case -1: + return DS_ERROR_MSG("Starting node must be map or sequence"); + + //Fill key: + case 0: + { + runcpp2::YAML::NodeValue& currentParentValue = nodeValCountStack.top().first->Value; + runcpp2::YAML::NodePtr newNode = runcpp2::YAML::CreateNodePtr(); + newNode->Value = + runcpp2::YAML::Alias { runcpp2::StringView((const char*)event.data.alias.anchor) }; + + newNode->LineNumber = event.start_mark.line + 1; + newNode->Parent = nodeValCountStack.top().first; + + //If we are in a sequence node, we just need to append the alias + if(mpark::holds_alternative(currentParentValue)) + mpark::get_if(¤tParentValue)->emplace_back(newNode); + //Alias should never be map key + //TODO: Actually, it can. But I guess we can forget about it for now? + else if(mpark::holds_alternative(currentParentValue)) + return DS_ERROR_MSG("Alias cannot be map key"); + //We should never in a scalar node + else if(mpark::holds_alternative(currentParentValue)) + return DS_ERROR_MSG("Should not be in a scalar node"); + //We should never in a alias node + else if(mpark::holds_alternative(currentParentValue)) + return DS_ERROR_MSG("Should not be in a alias node"); + //Invalid type + else + return DS_ERROR_MSG("Invalid node value type: " + DS_STR(currentParentValue.index())); + break; + } + + //Key is filled, value is alias + case 1: + { + runcpp2::YAML::NodeValue& currentParentValue = nodeValCountStack.top().first->Value; + + //If we are in a map node, that means the value is alias + if(mpark::holds_alternative(currentParentValue)) + { + runcpp2::YAML::Alias alias = + { runcpp2::StringView((const char*)event.data.alias.anchor) }; + + DS_UNWRAP_VOID(AddValueToMap(nodeValCountStack, alias, event)); + } + else if(mpark::holds_alternative(currentParentValue)) + return DS_ERROR_MSG("Trying to fill value in sequnce node. Missed node creation?"); + //We should never in a scalar node + else if(mpark::holds_alternative(currentParentValue)) + return DS_ERROR_MSG("Should not be in a scalar node"); + //We should never in a alias node + else if(mpark::holds_alternative(currentParentValue)) + return DS_ERROR_MSG("Should not be in a alias node"); + //Invalid type + else + return DS_ERROR_MSG("Invalid node value type: " + DS_STR(currentParentValue.index())); + break; + } + + //What? + default: + return DS_ERROR_MSG("Invalid node counter: " + DS_STR(nodeValCountStack.top().second)); + } //switch(nodeValCountStack.top().second) + + return {}; + } + + DS::Result CopyVariant(runcpp2::YAML::NodeValue& dest, const runcpp2::YAML::NodeValue& src) + { + if(mpark::holds_alternative(src)) + dest = *mpark::get_if(&src); + else if(mpark::holds_alternative(src)) + dest = *mpark::get_if(&src); + else if(mpark::holds_alternative(src)) + dest = *mpark::get_if(&src); + else if(mpark::holds_alternative(src)) + dest = *mpark::get_if(&src); + else + return DS_ERROR_MSG("Invalid type"); + + return {}; + } + + DS::Result UpdateParentsRecursively(runcpp2::YAML::Node& currentNode) + { + //NOTE: Parent of currentNode is correct, but not its children + std::stack nodesToProcess; + nodesToProcess.push(¤tNode); + + while(!nodesToProcess.empty()) + { + runcpp2::YAML::Node& node = *nodesToProcess.top(); + nodesToProcess.pop(); + + if(mpark::holds_alternative(node.Value)) + continue; + else if(mpark::holds_alternative(node.Value)) + continue; + else if(mpark::holds_alternative(node.Value)) + { + auto& seq = *mpark::get_if(&node.Value); + for(int i = 0; i < seq.size(); ++i) + { + if(seq[i]->Parent != &node) + { + runcpp2::YAML::NodePtr newNode = runcpp2::YAML::CreateNodePtr(); + newNode->Anchor = seq[i]->Anchor; + newNode->LineNumber = seq[i]->LineNumber; + newNode->Parent = &node; + DS_UNWRAP_VOID(CopyVariant(newNode->Value, seq[i]->Value)); + seq[i] = newNode; + } + + nodesToProcess.push(seq[i].get()); + } + } + else if(mpark::holds_alternative(node.Value)) + { + auto& map = *mpark::get_if(&node.Value); + for(int i = 0; i < map.InsertedKeys.size(); ++i) + { + runcpp2::YAML::NodePtr currentKey = map.InsertedKeys[i]; + runcpp2::YAML::NodePtr currentVal = map.Map[currentKey]; + + if(currentKey->Parent != &node || currentVal->Parent != &node) + { + runcpp2::YAML::NodePtr newKey = runcpp2::YAML::CreateNodePtr(); + newKey->Anchor = currentKey->Anchor; + newKey->LineNumber = currentKey->LineNumber; + newKey->Parent = &node; + DS_UNWRAP_VOID(CopyVariant(newKey->Value, currentKey->Value)); + + runcpp2::YAML::NodePtr newVal = runcpp2::YAML::CreateNodePtr(); + newVal->Anchor = currentKey->Anchor; + newVal->LineNumber = currentKey->LineNumber; + newVal->Parent = &node; + DS_UNWRAP_VOID(CopyVariant(newVal->Value, currentVal->Value)); + + //Replace entries with new key and values + { + map.InsertedKeys[i] = newKey; + + map.Map.erase(currentKey); + map.Map[newKey] = newVal; + + if(mpark::holds_alternative(currentKey->Value)) + { + auto& keyScalar = + *mpark::get_if(¤tKey->Value); + + map.StringMap[keyScalar] = newVal; + } + } + + nodesToProcess.push(newKey.get()); + nodesToProcess.push(newVal.get()); + } + else + { + nodesToProcess.push(currentKey.get()); + nodesToProcess.push(currentVal.get()); + } + } + } + else + return DS_ERROR_MSG("Invalid type"); + } + + return {}; + } + + //NOTE: This doesn't update OrderedMap.StringMap + DS::Result ResolveMergeKey( runcpp2::YAML::OrderedMap& currentMap, + int mergeKeyIndex, + const std::unordered_map< runcpp2::StringView, + runcpp2::YAML::Node*>& anchors) + { + //ssLOG_FUNC_DEBUG(); + runcpp2::YAML::NodePtr currentKey = currentMap.InsertedKeys[mergeKeyIndex]; + + //We trust that the key at `mergeKeyIndex` is a merge key + runcpp2::YAML::NodePtr currentValueNode = currentMap.Map[currentKey]; + std::string defaultErrorMessage = "We are expecting either an alias or sequence of alias"; + + std::vector aliasNodes; + std::vector mapsForMerging; + + if(mpark::holds_alternative(currentValueNode->Value)) + aliasNodes.push_back(currentValueNode); + else if(mpark::holds_alternative(currentValueNode->Value)) + { + auto& sequence = *mpark::get_if(¤tValueNode->Value); + for(int i = 0; i < sequence.size(); ++i) + aliasNodes.push_back(sequence[i]); + } + else + return DS_ERROR_MSG(defaultErrorMessage); + + for(int i = 0; i < aliasNodes.size(); ++i) + { + runcpp2::YAML::NodeValue& currentAliasValue = aliasNodes[i]->Value; + + if(!mpark::holds_alternative(currentAliasValue)) + return DS_ERROR_MSG(defaultErrorMessage); + + auto& alias = *mpark::get_if(¤tAliasValue); + if(anchors.count(alias.Value) == 0) + { + return DS_ERROR_MSG("Cannot find anchor value: " + DS_STR(alias.Value) + + " on line " + DS_STR(aliasNodes[i]->LineNumber)); + } + + runcpp2::YAML::Node* anchorNode = anchors.at(alias.Value); + if(!mpark::holds_alternative(anchorNode->Value)) + { + return DS_ERROR_MSG("Target anchor must be a map. Line " + + DS_STR(aliasNodes[i]->LineNumber)); + } + + mapsForMerging.emplace_back(mpark::get_if(&anchorNode->Value)); + } + + //For each map that needs to be merged, iterating their children and add it to our map + for(int i = 0; i < mapsForMerging.size(); ++i) + { + for(int j = mapsForMerging[i]->InsertedKeys.size() - 1; j >= 0; --j) + { + runcpp2::YAML::NodePtr keyToInsert = mapsForMerging[i]->InsertedKeys[j]; + runcpp2::YAML::NodePtr valueToInsert = mapsForMerging[i]->Map[keyToInsert]; + + runcpp2::YAML::ScalarValue keyView = + keyToInsert->GetScalar().DefaultOr(); + + //If duplicate key exist, that's override key, don't merge it + if(!keyView.empty() && currentMap.StringMap.count(keyView) > 0) + continue; + + //Add key and value + currentMap.InsertedKeys.insert( currentMap.InsertedKeys.begin() + mergeKeyIndex + 1, + keyToInsert); + currentMap.Map[keyToInsert] = valueToInsert; + } + } + + //Finally remove the merge key entry + currentMap.Map.erase(currentKey); + currentMap.InsertedKeys.erase(currentMap.InsertedKeys.begin() + mergeKeyIndex); + + return {}; + } +} //namespace + + +#endif diff --git a/Src/runcpp2/LibYamlImpl.cpp b/Src/runcpp2/LibYamlImpl.cpp new file mode 100644 index 0000000..4b8a5ec --- /dev/null +++ b/Src/runcpp2/LibYamlImpl.cpp @@ -0,0 +1,17 @@ +//libyaml implementations +extern "C" { + #define YAML_STR(x) #x + #define YAML_VERSION_MAJOR 0 + #define YAML_VERSION_MINOR 2 + #define YAML_VERSION_PATCH 5 + #define YAML_VERSION_STRING YAML_STR(YAML_VERSION_MAJOR) "." YAML_STR(YAML_VERSION_MINOR) "." YAML_STR(YAML_VERSION_PATCH) + + #include "../src/api.c" + #include "../src/dumper.c" + #include "../src/emitter.c" + #include "../src/loader.c" + #include "../src/parser.c" + #include "../src/reader.c" + #include "../src/scanner.c" + #include "../src/writer.c" +} diff --git a/Include/runcpp2/MacroUtil.hpp b/Src/runcpp2/MacroUtil.hpp similarity index 100% rename from Include/runcpp2/MacroUtil.hpp rename to Src/runcpp2/MacroUtil.hpp diff --git a/Src/runcpp2/ParseUtil.cpp b/Src/runcpp2/ParseUtil.cpp deleted file mode 100644 index 0fc290d..0000000 --- a/Src/runcpp2/ParseUtil.cpp +++ /dev/null @@ -1,383 +0,0 @@ -#include "runcpp2/ParseUtil.hpp" -#include "runcpp2/Data/ParseCommon.hpp" -#include "runcpp2/StringUtil.hpp" - -#include - -runcpp2::NodeRequirement::NodeRequirement() : Name(""), - NodeType(YAML::NodeType::Scalar), - Required(false), - Nullable(true) -{ -} - -runcpp2::NodeRequirement::NodeRequirement( const std::string& name, - YAML::NodeType nodeType, - bool required, - bool nullable) : Name(name), - NodeType(nodeType), - Required(required), - Nullable(nullable) -{} - -bool runcpp2::CheckNodeRequirement( YAML::ConstNodePtr parentNode, - const std::string childName, - YAML::NodeType childType, - bool required, - bool nullable) -{ - ssLOG_FUNC_DEBUG(); - - if(!parentNode->IsMap()) - { - ssLOG_ERROR("Node is not a map: " << parentNode->Value.index()); - return false; - } - - ssLOG_DEBUG("Checking: " << childName << " exists"); - if(!ExistAndHasChild(parentNode, childName, nullable)) - { - if(required) - { - if(false) - { - ssLOG_DEBUG("node.num_children(): " << parentNode->GetChildrenCount()); - for(int j = 0; j < parentNode->GetChildrenCount(); ++j) - ssLOG_DEBUG(parentNode->GetMapKeyScalarAt(j).value_or("")); - } - - ssLOG_ERROR("Required field not found: " << childName); - return false; - } - return true; - } - - //If type is nullable, we cannot verify it's type, so just continue - YAML::ConstNodePtr childNode = parentNode->GetMapValueNode(childName); - if(nullable && childNode->IsScalar() && childNode->GetScalar().value_or("").empty()) - return true; - - if(childNode->GetType() != childType) - { - ssLOG_ERROR("Field type is invalid: " << childName); - ssLOG_ERROR("Expected: " << YAML::NodeTypeToString(childType)); - ssLOG_ERROR("Found: " << YAML::NodeTypeToString(childNode->GetType())); - return false; - } - - return true; -} - -bool runcpp2::CheckNodeRequirements(YAML::ConstNodePtr node, - const std::vector& requirements) -{ - ssLOG_FUNC_DEBUG(); - - if(!node->IsMap()) - { - ssLOG_ERROR("Node is not a map: " << YAML::NodeTypeToString(node->GetType())); - ssLOG_ERROR("Node is not a map"); - return false; - } - - //All keys must be unique - { - std::unordered_set childKeys; - for(int i = 0; i < node->GetChildrenCount(); ++i) - { - YAML::ConstNodePtr keyNode = node->GetMapKeyNodeAt(i); - if(!keyNode->IsScalar()) - { - ssLOG_ERROR("Key must be scalar"); - return false; - } - - std::string currentKey = (std::string)keyNode->GetScalar().value_or(""); - if(childKeys.count(currentKey) != 0) - { - ssLOG_ERROR("Duplicate key found for: " << currentKey); - return false; - } - else - childKeys.insert(currentKey); - } - } - - for(int i = 0; i < requirements.size(); ++i) - { - if(!CheckNodeRequirement( node, - requirements[i].Name, - requirements[i].NodeType, - requirements[i].Required, - requirements[i].Nullable)) - { - return false; - } - } - - return true; -} - -bool runcpp2::GetParsableInfo(const std::string& contentToParse, std::string& outParsableInfo) -{ - ssLOG_FUNC_DEBUG(); - - std::string source = contentToParse; - - //Remove all \r characters - for(int i = 0; i < source.size(); i++) - { - if(source[i] == '\r') - { - source.erase(i, 1); - --i; - } - } - - const std::string prefix = "runcpp2"; - - std::string currentLine; - - bool insideCommentToParse = false; - bool singleLineComments = false; - bool preceedingSpace = false; - - bool contentReadyToParse = false; - - auto resetPrefixState = [&]() - { - //Clear values - insideCommentToParse = false; - singleLineComments = false; - preceedingSpace = false; - outParsableInfo.clear(); - contentReadyToParse = false; - }; - - auto checkFinishedGettingParsableContent = [&]() -> bool - { - if(singleLineComments) - { - //Check for end of continuous section - if( currentLine.size() < 2 || - currentLine.at(0) != '/' || - currentLine.at(1) != '/') - { - contentReadyToParse = true; - return true; - } - - if(preceedingSpace && currentLine.size() < 3) - { - ssLOG_ERROR("Inconsistent spacing for single line comment"); - resetPrefixState(); - return false; - } - } - else - { - //Check for closing */ - if( currentLine.size() >= 2 && - currentLine.at(currentLine.size() - 1) == '/' && - currentLine.at(currentLine.size() - 2) == '*') - { - currentLine.erase(currentLine.size() - 2, 2); - TrimRight(currentLine); - outParsableInfo += currentLine; - contentReadyToParse = true; - return true; - } - } - - return false; - }; - - for(int i = 0; i < source.size(); i++) - { - //Just append character if not newline - if(source[i] != '\n') - currentLine.push_back(source[i]); - //Newline is reached, parse the current line - else - { - //Try to find prefix that indicates the content inside the comment for parsing - if(!insideCommentToParse) - { - Trim(currentLine); - - if(currentLine == "// " + prefix || currentLine == "//" + prefix) - { - insideCommentToParse = true; - singleLineComments = true; - preceedingSpace = (currentLine + prefix).at(2) == ' '; - currentLine.clear(); - continue; - } - - if(currentLine == "/* " + prefix || currentLine == "/*" + prefix) - { - insideCommentToParse = true; - singleLineComments = false; - preceedingSpace = false; - currentLine.clear(); - continue; - } - - currentLine.clear(); - } - //Parse the content - else - { - if(singleLineComments) - { - Trim(currentLine); - - if(checkFinishedGettingParsableContent()) - break; - - currentLine.erase(0, (preceedingSpace ? 3 : 2)); - currentLine += '\n'; - outParsableInfo += currentLine; - } - //If inside block comment - else - { - TrimRight(currentLine); - - if(checkFinishedGettingParsableContent()) - break; - - currentLine += '\n'; - outParsableInfo += currentLine; - } - - currentLine.clear(); - } - } - - //Special case for last character/line - if(i == source.size() - 1 && insideCommentToParse) - { - TrimRight(currentLine); - - if(singleLineComments) - { - if(checkFinishedGettingParsableContent()) - break; - - currentLine.erase(0, (preceedingSpace ? 3 : 2)); - outParsableInfo += currentLine; - contentReadyToParse = true; - } - else - { - if(checkFinishedGettingParsableContent()) - break; - - ssLOG_ERROR("Missing closing */ in block comment"); - resetPrefixState(); - break; - } - } - } - - if(!contentReadyToParse) - outParsableInfo.clear(); - - return true; -} - -bool runcpp2::MergeYAML_NodeChildren( YAML::NodePtr nodeToMergeFrom, - YAML::NodePtr nodeToMergeTo, - YAML::ResourceHandle& yamlResouce) -{ - ssLOG_FUNC_DEBUG(); - - if(!nodeToMergeFrom->IsMap() || !nodeToMergeTo->IsMap()) - { - ssLOG_ERROR("Merge node is not map"); - return false; - } - - for(int i = 0; i < nodeToMergeFrom->GetChildrenCount(); ++i) - { - std::string key = nodeToMergeFrom->GetMapKeyScalarAt(i).DS_TRY_ACT(return false); - - if(!ExistAndHasChild(nodeToMergeTo, key, true)) - { - YAML::NodePtr fromNode = nodeToMergeFrom->GetMapValueNodeAt(i); - fromNode->CloneToMapChild(key, nodeToMergeTo, yamlResouce).DS_TRY_ACT(return false); - } - } - - return true; -} - -bool runcpp2::ExistAndHasChild( runcpp2::YAML::ConstNodePtr node, - const std::string& childName, - bool nullable) -{ - ssLOG_FUNC_DEBUG(); - - if(node->GetChildrenCount() > 0 && node->IsMap() && node->HasMapKey(childName)) - { - YAML::ConstNodePtr mapVal = node->GetMapValueNode(childName); - if(!mapVal) - return false; - - if(!nullable && mapVal->IsScalar() && mapVal->GetScalar().value_or("").empty()) - return false; - - return true; - } - - return false; -} - -//TODO: Replace with string escape in libyaml wrapper -std::string runcpp2::GetEscapedYAMLString(const std::string& input) -{ - std::string output = "\""; - - for(char c : input) - { - switch(c) - { - case '\\': output += "\\\\"; break; - case '\"': output += "\\\""; break; - case '\'': output += "\\\'"; break; - case '\n': output += "\\n"; break; - case '\t': output += "\\t"; break; - default: output += c; - } - } - - output += "\""; - return output; -} - -bool runcpp2::ParseIncludes(const std::string& line, std::string& outIncludePath) -{ - //Skip if not an include line - if(line.find("#include") == std::string::npos) - return false; - - size_t firstQuote = line.find('\"'); - size_t firstBracket = line.find('<'); - - //Skip if no valid include format found - if(firstQuote == std::string::npos && firstBracket == std::string::npos) - return false; - - bool isQuoted = firstQuote != std::string::npos && - (firstBracket == std::string::npos || firstQuote < firstBracket); - - size_t start = isQuoted ? firstQuote + 1 : firstBracket + 1; - size_t end = line.find(isQuoted ? '\"' : '>', start); - - if(end == std::string::npos) - return false; - - outIncludePath = line.substr(start, end - start); - return true; -} diff --git a/Src/runcpp2/ParseUtil.hpp b/Src/runcpp2/ParseUtil.hpp new file mode 100644 index 0000000..dfbf6a6 --- /dev/null +++ b/Src/runcpp2/ParseUtil.hpp @@ -0,0 +1,453 @@ +#ifndef RUNCPP2_PARSE_UTIL_HPP +#define RUNCPP2_PARSE_UTIL_HPP + +#include "runcpp2/LibYAML_Wrapper.hpp" +#include "runcpp2/Data/ParseCommon.hpp" +#include "runcpp2/StringUtil.hpp" + +#include "ssLogger/ssLog.hpp" +#include "DSResult/DSResult.hpp" + +#include +#include +#include +#include +#include +#include +#include + + +namespace runcpp2 +{ + struct NodeRequirement + { + std::string Name; + YAML::NodeType NodeType; + bool Required; + bool Nullable; + + inline NodeRequirement() : Name(""), + NodeType(YAML::NodeType::Scalar), + Required(false), + Nullable(true) + {} + + inline NodeRequirement( const std::string& name, + YAML::NodeType nodeType, + bool required, + bool nullable) : Name(name), + NodeType(nodeType), + Required(required), + Nullable(nullable) + {} + }; + + inline bool ExistAndHasChild( YAML::ConstNodePtr node, + const std::string& childName, + bool nullable = false) + { + ssLOG_FUNC_DEBUG(); + + if(node->GetChildrenCount() > 0 && node->IsMap() && node->HasMapKey(childName)) + { + YAML::ConstNodePtr mapVal = node->GetMapValueNode(childName); + if(!mapVal) + return false; + + if(!nullable && mapVal->IsScalar() && mapVal->GetScalar().value_or("").empty()) + return false; + + return true; + } + + return false; + } + + inline bool CheckNodeRequirement( YAML::ConstNodePtr parentNode, + const std::string childName, + YAML::NodeType childType, + bool required, + bool nullable) + { + ssLOG_FUNC_DEBUG(); + + if(!parentNode->IsMap()) + { + ssLOG_ERROR("Node is not a map: " << parentNode->Value.index()); + return false; + } + + ssLOG_DEBUG("Checking: " << childName << " exists"); + if(!ExistAndHasChild(parentNode, childName, nullable)) + { + if(required) + { + if(false) + { + ssLOG_DEBUG("node.num_children(): " << parentNode->GetChildrenCount()); + for(int j = 0; j < parentNode->GetChildrenCount(); ++j) + ssLOG_DEBUG(parentNode->GetMapKeyScalarAt(j).value_or("")); + } + + ssLOG_ERROR("Required field not found: " << childName); + return false; + } + return true; + } + + //If type is nullable, we cannot verify it's type, so just continue + YAML::ConstNodePtr childNode = parentNode->GetMapValueNode(childName); + if(nullable && childNode->IsScalar() && childNode->GetScalar().value_or("").empty()) + return true; + + if(childNode->GetType() != childType) + { + ssLOG_ERROR("Field type is invalid: " << childName); + ssLOG_ERROR("Expected: " << YAML::NodeTypeToString(childType)); + ssLOG_ERROR("Found: " << YAML::NodeTypeToString(childNode->GetType())); + return false; + } + + return true; + } + + inline bool CheckNodeRequirements( YAML::ConstNodePtr node, + const std::vector& requirements) + { + ssLOG_FUNC_DEBUG(); + + if(!node->IsMap()) + { + ssLOG_ERROR("Node is not a map: " << YAML::NodeTypeToString(node->GetType())); + ssLOG_ERROR("Node is not a map"); + return false; + } + + //All keys must be unique + { + std::unordered_set childKeys; + for(int i = 0; i < node->GetChildrenCount(); ++i) + { + YAML::ConstNodePtr keyNode = node->GetMapKeyNodeAt(i); + if(!keyNode->IsScalar()) + { + ssLOG_ERROR("Key must be scalar"); + return false; + } + + std::string currentKey = (std::string)keyNode->GetScalar().value_or(""); + if(childKeys.count(currentKey) != 0) + { + ssLOG_ERROR("Duplicate key found for: " << currentKey); + return false; + } + else + childKeys.insert(currentKey); + } + } + + for(int i = 0; i < requirements.size(); ++i) + { + if(!CheckNodeRequirement( node, + requirements[i].Name, + requirements[i].NodeType, + requirements[i].Required, + requirements[i].Nullable)) + { + return false; + } + } + + return true; + } + + inline DS::Result GetParsableInfo(const std::string& contentToParse, + std::string& outParsableInfo) + { + ssLOG_FUNC_DEBUG(); + + std::string source = contentToParse; + + //Remove all \r characters + for(int i = 0; i < source.size(); i++) + { + if(source[i] == '\r') + { + source.erase(i, 1); + --i; + } + } + + const std::string prefix = "runcpp2"; + + std::string currentLine; + + bool insideCommentToParse = false; + bool singleLineComments = false; + bool preceedingSpace = false; + + bool contentReadyToParse = false; + + auto resetPrefixState = [&]() + { + //Clear values + insideCommentToParse = false; + singleLineComments = false; + preceedingSpace = false; + outParsableInfo.clear(); + contentReadyToParse = false; + }; + + auto checkFinishedGettingParsableContent = [&]() -> DS::Result + { + if(singleLineComments) + { + //Check for end of continuous section + if(currentLine.size() < 2 || currentLine[0] != '/' || currentLine[1] != '/') + { + contentReadyToParse = true; + return true; + } + + if(preceedingSpace && currentLine.size() < 3) + { + resetPrefixState(); + return DS_ERROR_MSG("Inconsistent spacing for single line comment"); + } + } + else + { + //Check for closing */ + if( currentLine.size() >= 2 && + currentLine[currentLine.size() - 1] == '/' && + currentLine[currentLine.size() - 2] == '*') + { + currentLine.erase(currentLine.size() - 2, 2); + TrimRight(currentLine); + outParsableInfo += currentLine; + contentReadyToParse = true; + return true; + } + } + + return false; + }; + + for(int i = 0; i < source.size(); i++) + { + //Just append character if not newline + if(source[i] != '\n') + currentLine.push_back(source[i]); + //Newline is reached, parse the current line + else + { + //Try to find prefix that indicates the content inside the comment for parsing + if(!insideCommentToParse) + { + Trim(currentLine); + + if(currentLine == "// " + prefix || currentLine == "//" + prefix) + { + insideCommentToParse = true; + singleLineComments = true; + preceedingSpace = (currentLine + prefix).at(2) == ' '; + currentLine.clear(); + continue; + } + + if(currentLine == "/* " + prefix || currentLine == "/*" + prefix) + { + insideCommentToParse = true; + singleLineComments = false; + preceedingSpace = false; + currentLine.clear(); + continue; + } + + currentLine.clear(); + } + //Parse the content + else + { + if(singleLineComments) + { + Trim(currentLine); + + bool check = checkFinishedGettingParsableContent().DS_TRY(); + if(check) + break; + + currentLine.erase(0, (preceedingSpace ? 3 : 2)); + currentLine += '\n'; + outParsableInfo += currentLine; + } + //If inside block comment + else + { + TrimRight(currentLine); + + bool check = checkFinishedGettingParsableContent().DS_TRY(); + if(check) + break; + + currentLine += '\n'; + outParsableInfo += currentLine; + } + + currentLine.clear(); + } + } //else + + //Special case for last character/line + if(i == source.size() - 1 && insideCommentToParse) + { + TrimRight(currentLine); + + if(singleLineComments) + { + bool check = checkFinishedGettingParsableContent().DS_TRY(); + if(check) + break; + + currentLine.erase(0, (preceedingSpace ? 3 : 2)); + outParsableInfo += currentLine; + contentReadyToParse = true; + } + else + { + bool check = checkFinishedGettingParsableContent().DS_TRY(); + if(check) + break; + + //resetPrefixState(); + return DS_ERROR_MSG("Missing closing */ in block comment"); + } + } + } //for(int i = 0; i < source.size(); i++) + + if(!contentReadyToParse) + outParsableInfo.clear(); + + return {}; + } + + inline bool MergeYAML_NodeChildren( YAML::NodePtr nodeToMergeFrom, + YAML::NodePtr nodeToMergeTo, + YAML::ResourceHandle& yamlResouce) + { + ssLOG_FUNC_DEBUG(); + + if(!nodeToMergeFrom->IsMap() || !nodeToMergeTo->IsMap()) + { + ssLOG_ERROR("Merge node is not map"); + return false; + } + + for(int i = 0; i < nodeToMergeFrom->GetChildrenCount(); ++i) + { + std::string key = nodeToMergeFrom ->GetMapKeyScalarAt(i) + .DS_TRY_ACT(return false); + + if(!ExistAndHasChild(nodeToMergeTo, key, true)) + { + YAML::NodePtr fromNode = nodeToMergeFrom->GetMapValueNodeAt(i); + fromNode->CloneToMapChild(key, nodeToMergeTo, yamlResouce).DS_TRY_ACT(return false); + } + } + + return true; + } + + //TODO: Replace with string escape in libyaml wrapper + inline std::string GetEscapedYAMLString(const std::string& input) + { + std::string output = "\""; + + for(char c : input) + { + switch(c) + { + case '\\': output += "\\\\"; break; + case '\"': output += "\\\""; break; + case '\'': output += "\\\'"; break; + case '\n': output += "\\n"; break; + case '\t': output += "\\t"; break; + default: output += c; + } + } + + output += "\""; + return output; + } + + inline bool ParseIncludes(const std::string& line, std::string& outIncludePath) + { + //Skip if not an include line + if(line.find("#include") == std::string::npos) + return false; + + size_t firstQuote = line.find('\"'); + size_t firstBracket = line.find('<'); + + //Skip if no valid include format found + if(firstQuote == std::string::npos && firstBracket == std::string::npos) + return false; + + bool isQuoted = firstQuote != std::string::npos && + (firstBracket == std::string::npos || firstQuote < firstBracket); + + size_t start = isQuoted ? firstQuote + 1 : firstBracket + 1; + size_t end = line.find(isQuoted ? '\"' : '>', start); + + if(end == std::string::npos) + return false; + + outIncludePath = line.substr(start, end - start); + return true; + } + + template + bool ParsePlatformProfileMap( YAML::ConstNodePtr node, + const std::string& key, + std::unordered_map& result, + const std::string& errorMessage) + { + if(ExistAndHasChild(node, key)) + { + YAML::ConstNodePtr mapNode = node->GetMapValueNode(key); + + //If we skip platform profile + T defaultValue; + if(defaultValue.IsYAML_NodeParsableAsDefault(mapNode)) + { + if(defaultValue.ParseYAML_NodeWithProfile(mapNode, "DefaultProfile")) + result["DefaultPlatform"] = defaultValue; + else + return false; + } + else + { + if(!CheckNodeRequirement(node, key, YAML::NodeType::Map, false, true)) + return false; + + for(int i = 0; i < mapNode->GetChildrenCount(); ++i) + { + PlatformName platform = + mapNode ->GetMapKeyScalarAt(i) + .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); return false); + T value; + YAML::ConstNodePtr currentNode = mapNode->GetMapValueNodeAt(i); + if(!value.ParseYAML_Node(currentNode)) + { + ssLOG_ERROR("ScriptInfo: Failed to parse " << errorMessage); + return false; + } + result[platform] = value; + } + } + } + return true; + } +} + +#endif diff --git a/Src/runcpp2/PipelineSteps.cpp b/Src/runcpp2/PipelineSteps.cpp deleted file mode 100644 index d8958d8..0000000 --- a/Src/runcpp2/PipelineSteps.cpp +++ /dev/null @@ -1,1188 +0,0 @@ -#include "runcpp2/PipelineSteps.hpp" - -#include "runcpp2/ProfileHelper.hpp" -#include "runcpp2/ConfigParsing.hpp" -#include "runcpp2/DependenciesHelper.hpp" -#include "runcpp2/ParseUtil.hpp" -#include "runcpp2/PlatformUtil.hpp" -#include "runcpp2/Data/BuildTypeHelper.hpp" - -#include "System2.h" -#include "ssLogger/ssLog.hpp" -#include "ghc/filesystem.hpp" -#include "dylib.hpp" - -#include - -namespace -{ - bool RunCompiledScript( const ghc::filesystem::path& executable, - const std::string& scriptPath, - const std::vector& runArgs, - int& returnStatus) - { - INTERNAL_RUNCPP2_SAFE_START(); - ssLOG_FUNC_INFO(); - - std::vector args; - for(size_t i = 0; i < runArgs.size(); ++i) - args.push_back(runArgs[i].c_str()); - - System2CommandInfo runCommandInfo = {}; - SYSTEM2_RESULT result = System2RunSubprocess( executable.c_str(), - args.data(), - args.size(), - &runCommandInfo); - - ssLOG_INFO("Running: " << executable.string()); - for(size_t i = 0; i < runArgs.size(); ++i) - ssLOG_INFO("- " << runArgs[i]); - - if(result != SYSTEM2_RESULT_SUCCESS) - { - ssLOG_ERROR("System2Run failed with result: " << result); - return false; - } - - result = System2GetCommandReturnValueSync(&runCommandInfo, &returnStatus, false); - if(result != SYSTEM2_RESULT_SUCCESS) - { - ssLOG_ERROR("System2GetCommandReturnValueSync failed with result: " << result); - return false; - } - - return true; - INTERNAL_RUNCPP2_SAFE_CATCH_RETURN(false); - } - - bool RunCompiledSharedLib( const std::string& scriptPath, - const ghc::filesystem::path& compiledSharedLibPath, - const std::vector& runArgs, - int& returnStatus) - { - INTERNAL_RUNCPP2_SAFE_START(); - ssLOG_FUNC_INFO(); - - std::error_code _; - if(!ghc::filesystem::exists(compiledSharedLibPath, _)) - { - ssLOG_ERROR("Failed to find shared library: " << compiledSharedLibPath.string()); - return false; - } - - //Load it - std::unique_ptr sharedLib; - - try - { - ssLOG_INFO("Trying to run shared library: " << compiledSharedLibPath.string()); - - //TODO: We might want to use unicode instead for the path - #if defined(_WIN32) - std::string sharedLibDir = compiledSharedLibPath.parent_path().string(); - if(SetDllDirectoryA(sharedLibDir.c_str()) == FALSE) - { - std::string lastError = runcpp2::GetWindowsError(); - ssLOG_ERROR("Failed to set DLL directory: " << lastError); - return false; - } - #endif - - sharedLib = std::unique_ptr(new dylib( compiledSharedLibPath.string(), - dylib::no_filename_decorations)); - } - catch(std::exception& e) - { - ssLOG_ERROR("Failed to load shared library " << compiledSharedLibPath.string() << - " with exception: "); - - ssLOG_ERROR(e.what()); - return false; - } - - //Get main as entry point - if(sharedLib->has_symbol("main") == false) - { - ssLOG_ERROR("The shared library does not have a main function"); - return false; - } - - int (*scriptFullMain)(int, const char**) = nullptr; - int (*scriptMain)() = nullptr; - - try - { - scriptFullMain = sharedLib->get_function("main"); - } - catch(const dylib::exception& ex) - { - ssLOG_DEBUG("Failed to get full main function from shared library: " << ex.what()); - } - catch(...) - { - ssLOG_ERROR("Failed to get entry point function"); - return false; - } - - if(scriptFullMain == nullptr) - { - try - { - scriptMain = sharedLib->get_function("_main"); - } - catch(const dylib::exception& ex) - { - ssLOG_DEBUG("Failed to get main function from shared library: " << ex.what()); - } - catch(...) - { - ssLOG_ERROR("Failed to get entry point function"); - return false; - } - } - - if(scriptMain == nullptr && scriptFullMain == nullptr) - { - ssLOG_ERROR("Failed to load function"); - return false; - } - - //Run the entry point - try - { - if(scriptFullMain != nullptr) - { - std::vector runArgsCStr(runArgs.size()); - for(size_t i = 0; i < runArgs.size(); ++i) - runArgsCStr.at(i) = &runArgs.at(i).at(0); - - returnStatus = scriptFullMain(runArgsCStr.size(), runArgsCStr.data()); - } - else if(scriptMain != nullptr) - returnStatus = scriptMain(); - } - catch(std::exception& e) - { - ssLOG_ERROR("Failed to run script main with exception: " << e.what()); - return true; - } - catch(...) - { - ssLOG_ERROR("Unknown exception caught"); - return true; - } - - return true; - - INTERNAL_RUNCPP2_SAFE_CATCH_RETURN(false); - } -} - -DS::Result runcpp2::CopyFiles(const ghc::filesystem::path& destDir, - const std::vector& filePaths, - std::vector& outCopiedPaths) -{ - ssLOG_FUNC_INFO(); - - std::error_code e; - for (const std::string& srcPath : filePaths) - { - ghc::filesystem::path destPath = destDir / ghc::filesystem::path(srcPath).filename(); - - if(ghc::filesystem::exists(srcPath, e)) - { - ghc::filesystem::copy(srcPath, - destPath, - ghc::filesystem::copy_options::update_existing, - e); - - if(e) - { - std::string errorMsg = DS_STR("Failed to copy file from ") + srcPath + " to " + - destPath.string() + "\nError: " + e.message(); - return DS_ERROR_MSG(errorMsg); - } - - ssLOG_INFO("Copied from " << srcPath << " to " << destPath.string()); - outCopiedPaths.push_back(ProcessPath(destPath)); - } - else - return DS_ERROR_MSG("File to copy not found: " + srcPath); - } - - return {}; -} - - -DS::Result runcpp2::RunProfileCommands( const Data::ProfilesCommands* commands, - const Data::Profile& profile, - const std::string& workingDir, - const std::string& commandType) -{ - ssLOG_FUNC_INFO(); - - if(commands != nullptr) - { - const std::vector* commandSteps = - runcpp2::GetValueFromProfileMap(profile, commands->CommandSteps); - - if(commandSteps != nullptr) - { - for(const std::string& cmd : *commandSteps) - { - std::string output; - int returnCode = 0; - - if(!runcpp2::RunCommand(cmd, true, workingDir, output, returnCode)) - { - std::string errorMsg = commandType + " command failed: " + cmd + - " with return code " + DS_STR(returnCode) + "\n"; - errorMsg += "Was trying to run: " + cmd + "\n"; - errorMsg += "Output: \n" + output; - return DS_ERROR_MSG_EC(errorMsg, (int)PipelineResult::UNEXPECTED_FAILURE); - } - - ssLOG_INFO(commandType << " command ran: \n" << cmd); - ssLOG_INFO(commandType << " command output: \n" << output); - } - } - } - - return {}; -} - -DS::Result runcpp2::ValidateInputs( const std::string& scriptPath, - const std::vector& profiles, - ghc::filesystem::path& outAbsoluteScriptPath, - ghc::filesystem::path& outScriptDirectory, - std::string& outScriptName) -{ - ssLOG_FUNC_INFO(); - - if(profiles.empty()) - return DS_ERROR_MSG_EC("No compiler profiles found", (int)PipelineResult::EMPTY_PROFILES); - - //Check if input file exists - std::error_code _; - if(!ghc::filesystem::exists(scriptPath, _)) - { - return DS_ERROR_MSG_EC( "File does not exist: " + scriptPath, - (int)PipelineResult::INVALID_SCRIPT_PATH); - } - - if(ghc::filesystem::is_directory(scriptPath, _)) - { - return DS_ERROR_MSG_EC( "The input file must not be a directory: " + scriptPath, - (int)PipelineResult::INVALID_SCRIPT_PATH); - } - - outAbsoluteScriptPath = ghc::filesystem::absolute(ghc::filesystem::canonical(scriptPath, _)); - outScriptDirectory = outAbsoluteScriptPath.parent_path(); - outScriptName = outAbsoluteScriptPath.stem().string(); - - ssLOG_DEBUG("scriptPath: " << scriptPath); - ssLOG_DEBUG("absoluteScriptPath: " << outAbsoluteScriptPath.string()); - ssLOG_DEBUG("scriptDirectory: " << outScriptDirectory.string()); - ssLOG_DEBUG("scriptName: " << outScriptName); - ssLOG_DEBUG("is_directory: " << ghc::filesystem::is_directory(outScriptDirectory)); - - return {}; -} - -DS::Result runcpp2::ParseAndValidateScriptInfo( const ghc::filesystem::path& absoluteScriptPath, - const ghc::filesystem::path& scriptDirectory, - const std::string& scriptName, - const bool buildExecutable, - Data::ScriptInfo& outScriptInfo) -{ - ssLOG_FUNC_INFO(); - - //Check if there's script info as yaml file instead - std::error_code e; - std::string parsableInfo; - std::ifstream inputFile; - ghc::filesystem::path dedicatedYamlLoc = - scriptDirectory / ghc::filesystem::path(scriptName + ".yaml"); - - if(ghc::filesystem::exists(dedicatedYamlLoc, e)) - { - //Record write time for yaml file for watch option - outScriptInfo.LastWriteTime = ghc::filesystem::last_write_time(dedicatedYamlLoc, e); - if(e) - { - std::string errorMsg = e.message(); - errorMsg += "\nFailed to get last write time for: " + dedicatedYamlLoc.string(); - return DS_ERROR_MSG_EC(errorMsg, (int)PipelineResult::INVALID_SCRIPT_INFO); - } - - inputFile.open(dedicatedYamlLoc); - std::stringstream buffer; - buffer << inputFile.rdbuf(); - parsableInfo = buffer.str(); - } - else - { - //Record write time for script file for watch option - outScriptInfo.LastWriteTime = ghc::filesystem::last_write_time(absoluteScriptPath, e); - if(e) - { - std::string errorMsg = e.message(); - errorMsg += "\nFailed to get last write time for: " + absoluteScriptPath.string(); - return DS_ERROR_MSG_EC(errorMsg, (int)PipelineResult::INVALID_SCRIPT_INFO); - } - - inputFile.open(absoluteScriptPath); - - if(!inputFile) - { - return DS_ERROR_MSG_EC( "Failed to open file: " + absoluteScriptPath.string(), - (int)PipelineResult::INVALID_SCRIPT_PATH); - } - - std::stringstream buffer; - buffer << inputFile.rdbuf(); - std::string source(buffer.str()); - - if(!GetParsableInfo(source, parsableInfo)) - { - return DS_ERROR_MSG_EC( "An error has been encountered when parsing info: " + - absoluteScriptPath.string(), - (int)PipelineResult::INVALID_SCRIPT_INFO); - } - } - - //Try to parse the runcpp2 info - ParseScriptInfo(parsableInfo, outScriptInfo) - .DS_TRY_ACT(DS_APPEND_TRACE(DS_TMP_ERROR); - DS_TMP_ERROR.Message += "\nContent trying to parse: \n" + parsableInfo; - DS_TMP_ERROR.ErrorCode = (int)PipelineResult::INVALID_SCRIPT_INFO; - return DS::Error(DS_TMP_ERROR)); - - if(!parsableInfo.empty()) - { - ssLOG_DEBUG("Parsed script info YAML:"); - ssLOG_DEBUG("\n" << outScriptInfo.ToString("")); - } - - //Replace build type with internal executable type to trigger recompiling when switching to - //have or not have "--executable" option - if(outScriptInfo.CurrentBuildType == Data::BuildType::EXECUTABLE) - { - outScriptInfo.CurrentBuildType = buildExecutable ? - Data::BuildType::INTERNAL_EXECUTABLE_EXECUTABLE : - Data::BuildType::INTERNAL_EXECUTABLE_SHARED; - } - - return {}; -} - -DS::Result runcpp2::HandleCleanup(const Data::ScriptInfo& scriptInfo, - const Data::Profile& profile, - const ghc::filesystem::path& scriptDirectory, - const ghc::filesystem::path& buildDir, - const ghc::filesystem::path& absoluteScriptPath, - BuildsManager& buildsManager) -{ - ssLOG_FUNC_INFO(); - - const Data::ProfilesCommands* cleanupCommands = - runcpp2::GetValueFromPlatformMap(scriptInfo.Cleanup); - - if(cleanupCommands != nullptr) - { - const std::vector* commands = - runcpp2::GetValueFromProfileMap(profile, cleanupCommands->CommandSteps); - if(commands != nullptr) - { - for(const std::string& cmd : *commands) - { - std::string output; - int returnCode = 0; - - if(!runcpp2::RunCommand(cmd, true, scriptDirectory, output, returnCode)) - { - return DS_ERROR_MSG_EC( "Cleanup command failed: " + cmd + " with return code " + - DS_STR(returnCode) + "\nOutput: \n" + output, - (int)PipelineResult::UNEXPECTED_FAILURE); - } - - ssLOG_INFO("Cleanup command ran: \n" << cmd); - ssLOG_INFO("Cleanup command output: \n" << output); - } - } - } - - //Remove build directory - std::error_code e; - if(!ghc::filesystem::remove_all(buildDir, e)) - { - return DS_ERROR_MSG_EC( "Failed to remove build directory: " + buildDir.string(), - (int)PipelineResult::UNEXPECTED_FAILURE); - } - - if(!buildsManager.RemoveBuildMapping(absoluteScriptPath)) - { - return DS_ERROR_MSG_EC( "Failed to remove build mapping", - (int)PipelineResult::UNEXPECTED_FAILURE); - } - - if(!buildsManager.SaveBuildsMappings()) - { - return DS_ERROR_MSG_EC( "Failed to save build mappings", - (int)PipelineResult::UNEXPECTED_FAILURE); - } - - return {}; -} - -DS::Result runcpp2::InitializeBuildDirectory( const ghc::filesystem::path& configDir, - const ghc::filesystem::path& absoluteScriptPath, - bool useLocalBuildDir, - BuildsManager& outBuildsManager, - ghc::filesystem::path& outBuildDir, - IncludeManager& outIncludeManager) -{ - ssLOG_FUNC_INFO(); - - //Create build directory - ghc::filesystem::path buildDirPath = useLocalBuildDir ? - ghc::filesystem::current_path() / ".runcpp2" : - configDir; - - //Create a class that manages build folder - outBuildsManager = BuildsManager(buildDirPath); - - if(!outBuildsManager.Initialize()) - { - return DS_ERROR_MSG_EC( "Failed to initialize builds manager", - (int)PipelineResult::INVALID_BUILD_DIR); - } - - bool createdBuildDir = false; - bool writeMapping = false; - if(!outBuildsManager.HasBuildMapping(absoluteScriptPath)) - writeMapping = true; - - if(outBuildsManager.GetBuildMapping(absoluteScriptPath, outBuildDir)) - { - if(writeMapping && !outBuildsManager.SaveBuildsMappings()) - ssLOG_FATAL("Failed to save builds mappings"); - else - createdBuildDir = true; - } - - if(!createdBuildDir) - { - return DS_ERROR_MSG_EC( "Failed to create local build directory for: " + - DS_STR(absoluteScriptPath), - (int)PipelineResult::INVALID_BUILD_DIR); - } - - outIncludeManager = IncludeManager(); - if(!outIncludeManager.Initialize(outBuildDir)) - { - return DS_ERROR_MSG_EC( "Failed to initialize include manager", - (int)PipelineResult::INVALID_BUILD_DIR); - } - - return {}; -} - -DS::Result runcpp2::ResolveScriptImports( Data::ScriptInfo& scriptInfo, - const ghc::filesystem::path& scriptPath, - const ghc::filesystem::path& buildDir) -{ - ssLOG_FUNC_INFO(); - - //Resolve all the script info imports first before evaluating it - ResolveImports(scriptInfo, scriptPath, buildDir) - .DS_TRY_ACT(DS_TMP_ERROR.ErrorCode = (int)PipelineResult::UNEXPECTED_FAILURE; - DS_APPEND_TRACE(DS_TMP_ERROR); - return DS::Error(DS_TMP_ERROR)); - - return {}; -} - -DS::Result runcpp2::CheckScriptInfoChanges( const ghc::filesystem::path& buildDir, - const Data::ScriptInfo& scriptInfo, - const Data::Profile& profile, - const ghc::filesystem::path& absoluteScriptPath, - const Data::ScriptInfo* lastScriptInfo, - const int maxThreads, - bool& outAllRecompileNeeded, - bool& outRelinkNeeded, - std::vector& outChangedDependencies) -{ - ssLOG_FUNC_INFO(); - - const ghc::filesystem::path scriptDirectory = absoluteScriptPath.parent_path(); - ghc::filesystem::path lastScriptInfoFilePath = buildDir / "LastScriptInfo.yaml"; - Data::ScriptInfo lastScriptInfoFromDisk; - - std::error_code e; - - //Run Setup commands if we don't have previous build - if(!ghc::filesystem::exists(lastScriptInfoFilePath, e)) - { - const Data::ProfilesCommands* setupCommands = - runcpp2::GetValueFromPlatformMap(scriptInfo.Setup); - - if(setupCommands != nullptr) - { - RunProfileCommands(setupCommands, profile, scriptDirectory.string(), "Setup").DS_TRY(); - } - } - - //Compare script info in memory or from disk - const Data::ScriptInfo* lastInfo = lastScriptInfo; - if(lastInfo == nullptr && ghc::filesystem::exists(lastScriptInfoFilePath, e)) - { - ssLOG_DEBUG("Last script info file exists: " << lastScriptInfoFilePath); - std::ifstream lastScriptInfoFile; - lastScriptInfoFile.open(lastScriptInfoFilePath); - std::stringstream lastScriptInfoBuffer; - lastScriptInfoBuffer << lastScriptInfoFile.rdbuf(); - - int currentThreadTargetLevel = ssLOG_GET_CURRENT_THREAD_TARGET_LEVEL(); - ssLOG_SET_CURRENT_THREAD_TARGET_LEVEL(ssLOG_LEVEL_NONE); - - do - { - if(!ParseScriptInfo(lastScriptInfoBuffer.str(), lastScriptInfoFromDisk).HasValue()) - break; - - //Resolve imports for last script info - ResolveScriptImports(lastScriptInfoFromDisk, absoluteScriptPath, buildDir).DS_TRY(); - lastInfo = &lastScriptInfoFromDisk; - } - while(false); - - ssLOG_SET_CURRENT_THREAD_TARGET_LEVEL(currentThreadTargetLevel); - - if(lastInfo != nullptr) - ssLOG_INFO("Last script info parsed"); - else - ssLOG_INFO("Failed to parse last script info"); - } - - //Check if the cached script info has changed - if(lastInfo != nullptr) - { - //Relink if there are any changes to the link flags - { - const Data::ProfilesFlagsOverride* lastLinkFlags = - runcpp2::GetValueFromPlatformMap(lastInfo->OverrideLinkFlags); - const Data::ProfilesFlagsOverride* currentLinkFlags = - runcpp2::GetValueFromPlatformMap(scriptInfo.OverrideLinkFlags); - - outRelinkNeeded = (lastLinkFlags == nullptr) != (currentLinkFlags == nullptr) || - ( - lastLinkFlags != nullptr && - currentLinkFlags != nullptr && - !lastLinkFlags->Equals(*currentLinkFlags) - ); - } - - outAllRecompileNeeded = scriptInfo.IsAllCompiledCacheInvalidated(*lastInfo); - - //Check dependencies - for(int i = 0; i < scriptInfo.Dependencies.size(); ++i) - { - if( lastInfo->Dependencies.size() <= i || - !scriptInfo.Dependencies[i].Equals(lastInfo->Dependencies[i])) - { - outChangedDependencies.push_back(scriptInfo.Dependencies[i].Name); - } - } - - if(outAllRecompileNeeded || outRelinkNeeded) - { - ssLOG_INFO( "Last script info is out of date, " << - (outAllRecompileNeeded ? "recompiling..." : "relinking...")); - } - } - else - outAllRecompileNeeded = true; - - ssLOG_DEBUG("recompileNeeded: " << outAllRecompileNeeded << - ", changedDependencies.size(): " << outChangedDependencies.size() << - ", relinkNeeded: " << outRelinkNeeded); - - //Write to file if there's any changes to the current script info - if( !lastInfo || - outAllRecompileNeeded || - !outChangedDependencies.empty() || - outRelinkNeeded || - !scriptInfo.Equals(*lastInfo)) - { - std::ofstream writeOutputFile(lastScriptInfoFilePath); - if(!writeOutputFile) - { - return DS_ERROR_MSG_EC( "Failed to open file: " + DS_STR(lastScriptInfoFilePath), - (int)PipelineResult::INVALID_BUILD_DIR); - } - - writeOutputFile << scriptInfo.ToString(""); - ssLOG_DEBUG("Wrote current script info to " << lastScriptInfoFilePath.string()); - } - - return {}; -} - -DS::Result -runcpp2::ProcessDependencies( Data::ScriptInfo& scriptInfo, - const Data::Profile& profile, - const ghc::filesystem::path& absoluteScriptPath, - const ghc::filesystem::path& buildDir, - const std::unordered_map& currentOptions, - const std::vector& changedDependencies, - const int maxThreads, - std::vector& outAvailableDependencies, - std::vector& outGatheredBinariesPaths) -{ - ssLOG_FUNC_INFO(); - - for(int i = 0; i < scriptInfo.Dependencies.size(); ++i) - { - if(IsDependencyAvailableForThisPlatform(scriptInfo.Dependencies.at(i))) - outAvailableDependencies.push_back(&scriptInfo.Dependencies.at(i)); - } - - std::vector dependenciesLocalCopiesPaths; - std::vector dependenciesSourcePaths; - GetDependenciesPaths( outAvailableDependencies, - dependenciesLocalCopiesPaths, - dependenciesSourcePaths, - absoluteScriptPath, - buildDir) - .DS_TRY_ACT(DS_TMP_ERROR.ErrorCode = (int)PipelineResult::DEPENDENCIES_FAILED; - DS_APPEND_TRACE(DS_TMP_ERROR); - return DS::Error(DS_TMP_ERROR)); - - if(currentOptions.count(CmdOptions::RESET_DEPENDENCIES) > 0 || !changedDependencies.empty()) - { - if(currentOptions.count(CmdOptions::BUILD_SOURCE_ONLY) > 0) - { - std::string errorMsg = - "Dependencies settings have changed or being reset explicitly.\n" - "Cannot just build source files only without building dependencies"; - return DS_ERROR_MSG_EC(errorMsg, (int)PipelineResult::INVALID_OPTION); - } - - std::string depsToReset = "all"; - if(!changedDependencies.empty()) - { - depsToReset = changedDependencies[0]; - for(int i = 1; i < changedDependencies.size(); ++i) - depsToReset += "," + changedDependencies[i]; - } - - CleanupDependencies(profile, - scriptInfo, - outAvailableDependencies, - dependenciesLocalCopiesPaths, - currentOptions.count(CmdOptions::RESET_DEPENDENCIES) > 0 ? - currentOptions.at(CmdOptions::RESET_DEPENDENCIES) : - depsToReset) - .DS_TRY_ACT(DS_TMP_ERROR.ErrorCode = (int)PipelineResult::DEPENDENCIES_FAILED; - DS_APPEND_TRACE(DS_TMP_ERROR); - return DS::Error(DS_TMP_ERROR)); - } - - if(currentOptions.count(CmdOptions::RESET_DEPENDENCIES) > 0) - return {}; - - SetupDependenciesIfNeeded( profile, - buildDir, - scriptInfo, - outAvailableDependencies, - dependenciesLocalCopiesPaths, - dependenciesSourcePaths, - maxThreads) - .DS_TRY_ACT(DS_TMP_ERROR.ErrorCode = (int)PipelineResult::DEPENDENCIES_FAILED; - DS_APPEND_TRACE(DS_TMP_ERROR); - return DS::Error(DS_TMP_ERROR)); - - //Sync local dependencies before building - SyncLocalDependencies( outAvailableDependencies, - dependenciesSourcePaths, - dependenciesLocalCopiesPaths) - .DS_TRY_ACT(DS_TMP_ERROR.ErrorCode = (int)PipelineResult::DEPENDENCIES_FAILED; - DS_APPEND_TRACE(DS_TMP_ERROR); - return DS::Error(DS_TMP_ERROR)); - - if(currentOptions.count(CmdOptions::BUILD_SOURCE_ONLY) == 0) - { - BuildDependencies( profile, - scriptInfo, - outAvailableDependencies, - dependenciesLocalCopiesPaths, - maxThreads) - .DS_TRY_ACT - ( - DS_TMP_ERROR.Message += - "\nFailed to build script dependencies. Maybe try resetting dependencies " - "with \"-rd all\" and run again?"; - DS_TMP_ERROR.ErrorCode = (int)PipelineResult::DEPENDENCIES_FAILED; - DS_APPEND_TRACE(DS_TMP_ERROR); - return DS::Error(DS_TMP_ERROR); - ); - } - - GatherDependenciesBinaries( outAvailableDependencies, - dependenciesLocalCopiesPaths, - profile, - outGatheredBinariesPaths) - .DS_TRY_ACT(DS_TMP_ERROR.ErrorCode = (int)PipelineResult::DEPENDENCIES_FAILED; - DS_APPEND_TRACE(DS_TMP_ERROR); - return DS::Error(DS_TMP_ERROR)); - - return {}; -} - -void runcpp2::SeparateDependencyFiles( const Data::FilesTypesInfo& filesTypes, - const std::vector& gatheredBinariesPaths, - std::vector& outLinkFilesPaths, - std::vector& outFilesToCopyPaths) -{ - ssLOG_FUNC_INFO(); - INTERNAL_RUNCPP2_SAFE_START(); - - std::unordered_set linkExtensions; - - //Populate the set of link extensions - if(runcpp2::HasValueFromPlatformMap(filesTypes.StaticLinkFile.Extension)) - linkExtensions.insert(*runcpp2::GetValueFromPlatformMap(filesTypes.StaticLinkFile.Extension)); - if(runcpp2::HasValueFromPlatformMap(filesTypes.SharedLinkFile.Extension)) - linkExtensions.insert(*runcpp2::GetValueFromPlatformMap(filesTypes.SharedLinkFile.Extension)); - if(runcpp2::HasValueFromPlatformMap(filesTypes.ObjectLinkFile.Extension)) - linkExtensions.insert(*runcpp2::GetValueFromPlatformMap(filesTypes.ObjectLinkFile.Extension)); - - //Separate the gathered files from dependencies into files to link and files to copy - for(int i = 0; i < gatheredBinariesPaths.size(); ++i) - { - ghc::filesystem::path filePath(gatheredBinariesPaths.at(i)); - std::string extension = runcpp2::GetFileExtensionWithoutVersion(filePath); - - //Check if the file is a link file based on its extension - if(linkExtensions.find(extension) != linkExtensions.end()) - { - outLinkFilesPaths.push_back(gatheredBinariesPaths.at(i)); - - //Special case when SharedLinkFile and SharedLibraryFile share the same extension - if( runcpp2::HasValueFromPlatformMap(filesTypes.SharedLibraryFile.Extension) && - *runcpp2::GetValueFromPlatformMap(filesTypes.SharedLibraryFile - .Extension) == extension) - { - outFilesToCopyPaths.push_back(gatheredBinariesPaths.at(i)); - } - } - else - outFilesToCopyPaths.push_back(gatheredBinariesPaths.at(i)); - } - - ssLOG_INFO("Files to link:"); - for(int i = 0; i < outLinkFilesPaths.size(); ++i) - ssLOG_INFO(" " << outLinkFilesPaths[i]); - - ssLOG_INFO("Files to copy:"); - for(int i = 0; i < outFilesToCopyPaths.size(); ++i) - ssLOG_INFO(" " << outFilesToCopyPaths[i]); - - INTERNAL_RUNCPP2_SAFE_CATCH_RETURN(void()); -} - -DS::Result runcpp2::HandlePreBuild( const Data::ScriptInfo& scriptInfo, - const Data::Profile& profile, - const ghc::filesystem::path& buildDir) -{ - ssLOG_FUNC_INFO(); - - const Data::ProfilesCommands* preBuildCommands = - runcpp2::GetValueFromPlatformMap(scriptInfo.PreBuild); - - RunProfileCommands(preBuildCommands, profile, buildDir.string(), "PreBuild").DS_TRY(); - return {}; -} - -DS::Result runcpp2::HandlePostBuild( const Data::ScriptInfo& scriptInfo, - const Data::Profile& profile, - const ghc::filesystem::path& buildDir) -{ - ssLOG_FUNC_INFO(); - - const Data::ProfilesCommands* postBuildCommands = - GetValueFromPlatformMap(scriptInfo.PostBuild); - - RunProfileCommands(postBuildCommands, profile, buildDir.string(), "PostBuild").DS_TRY(); - return {}; -} - -DS::Result -runcpp2::RunCompiledOutput( const ghc::filesystem::path& target, - const ghc::filesystem::path& absoluteScriptPath, - const Data::ScriptInfo& scriptInfo, - const std::vector& runArgs, - const std::unordered_map& currentOptions, - int& returnStatus) -{ - ssLOG_FUNC_INFO(); - - //Skip running if not executable - if( scriptInfo.CurrentBuildType != Data::BuildType::INTERNAL_EXECUTABLE_EXECUTABLE && - scriptInfo.CurrentBuildType != Data::BuildType::INTERNAL_EXECUTABLE_SHARED) - { - ssLOG_INFO("Skipping run - output is not executable"); - return {}; - } - - std::error_code e; - if(target.empty() || !ghc::filesystem::exists(target, e)) - { - return DS_ERROR_MSG_EC( "Failed to find the compiled file to run", - (int)PipelineResult::COMPILE_LINK_FAILED); - } - - //Prepare run arguments - std::vector finalRunArgs; - finalRunArgs.push_back(target.string()); - if(scriptInfo.PassScriptPath) - finalRunArgs.push_back(absoluteScriptPath); - - //Add user provided arguments - for(size_t i = 0; i < runArgs.size(); ++i) - finalRunArgs.push_back(runArgs[i]); - - if(scriptInfo.CurrentBuildType == Data::BuildType::INTERNAL_EXECUTABLE_EXECUTABLE) - { - //Running the script with modified args - if(!RunCompiledScript(target, absoluteScriptPath, finalRunArgs, returnStatus)) - return DS_ERROR_MSG_EC("Failed to run script", (int)PipelineResult::RUN_SCRIPT_FAILED); - } - else - { - //Load the shared library and run it with modified args - if(!RunCompiledSharedLib(absoluteScriptPath, target, finalRunArgs, returnStatus)) - return DS_ERROR_MSG_EC("Failed to run script", (int)PipelineResult::RUN_SCRIPT_FAILED); - } - - return {}; -} - -DS::Result -runcpp2::GetBuiltTargetPaths( const ghc::filesystem::path& buildDir, - const std::string& scriptName, - const Data::Profile& profile, - const std::unordered_map& currentOptions, - const Data::ScriptInfo& scriptInfo, - std::vector& outTargets, - ghc::filesystem::path* outRunnableTarget) -{ - ssLOG_FUNC_INFO(); - - std::error_code _; - outTargets.clear(); - - //Validate executable option against build type - if( currentOptions.count(CmdOptions::EXECUTABLE) > 0 && - scriptInfo.CurrentBuildType != Data::BuildType::INTERNAL_EXECUTABLE_SHARED && - scriptInfo.CurrentBuildType != Data::BuildType::INTERNAL_EXECUTABLE_EXECUTABLE) - { - std::string errMsg = - DS_STR("Cannot run as executable - script is configured for ") + - Data::BuildTypeToString(scriptInfo.CurrentBuildType) + - " output. Please remove --executable flag or change build type to Executable"; - - return DS_ERROR_MSG_EC(errMsg, (int)PipelineResult::INVALID_OPTION); - } - - //Get all target paths - std::vector isRunnable; - if(!Data::BuildTypeHelper::GetPossibleOutputPaths( buildDir, - scriptName, - profile, - scriptInfo.CurrentBuildType, - outTargets, - isRunnable)) - { - return DS_ERROR_MSG_EC( "Extension or prefix not found in compiler profile for build type: " + - runcpp2::Data::BuildTypeToString(scriptInfo.CurrentBuildType), - (int)PipelineResult::INVALID_SCRIPT_INFO); - } - - //Verify all targets exist - for(const ghc::filesystem::path& target : outTargets) - { - if(!ghc::filesystem::exists(target, _)) - { - ssLOG_WARNING("Failed to find the compiled file: " << target.string()); - continue; - //return PipelineResult::COMPILE_LINK_FAILED; - } - } - - //If requested, find the runnable target - if(outRunnableTarget != nullptr) - { - for(size_t i = 0; i < outTargets.size(); ++i) - { - if(isRunnable.at(i)) - { - *outRunnableTarget = outTargets.at(i); - break; - } - } - } - - return {}; -} - -DS::Result runcpp2::GatherSourceFiles(const ghc::filesystem::path& absoluteScriptPath, - const Data::ScriptInfo& scriptInfo, - const Data::Profile& currentProfile, - std::vector& outSourcePaths) -{ - ssLOG_FUNC_INFO(); - - if(!currentProfile.FileExtensions.count(absoluteScriptPath.extension())) - return DS_ERROR_MSG("File extension of script doesn't match profile"); - - if(!absoluteScriptPath.is_absolute()) - return DS_ERROR_MSG("Script path is not absolute: " + DS_STR(absoluteScriptPath)); - - outSourcePaths.clear(); - outSourcePaths.push_back(absoluteScriptPath); - - const Data::ProfilesProcessPaths* compileFiles = - GetValueFromPlatformMap(scriptInfo.OtherFilesToBeCompiled); - - if(compileFiles == nullptr) - { - ssLOG_INFO("No other files to be compiled files current platform"); - - if(!scriptInfo.OtherFilesToBeCompiled.empty()) - { - ssLOG_WARNING( "Other source files are present, " - "but none are included for current configuration. Is this intended?"); - } - return {}; - } - - const std::vector* profileCompileFiles = - GetValueFromProfileMap(currentProfile, compileFiles->Paths); - - if(!profileCompileFiles) - { - ssLOG_INFO("No other files to be compiled for current profile"); - return {}; - } - - //TODO: Allow filepaths to contain wildcards as follows - //* as directory or filename wildcard - //i.e. "./*/test/*.cpp" will match "./moduleA/test/a.cpp" and "./moduleB/test/b.cpp" - - //** as recursive directory wildcard - //i.e. "./**/*.cpp" will match any .cpp files - //i.e. "./**/test.cpp" will match any files named "test.cpp" - - //For the time being, each entry will represent a path - { - const ghc::filesystem::path scriptDirectory = - ghc::filesystem::path(absoluteScriptPath).parent_path(); - - for(int i = 0; i < profileCompileFiles->size(); ++i) - { - ghc::filesystem::path currentPath = profileCompileFiles->at(i); - if(currentPath.is_relative()) - currentPath = scriptDirectory / currentPath; - - if(currentPath.is_relative()) - { - std::string errMsg = - DS_STR("Failed to process compile path: ") + DS_STR(profileCompileFiles->at(i)) + - "\nTry to append path to script directory but failed" + - "\nFinal appended path: " + DS_STR(currentPath); - return DS_ERROR_MSG(errMsg); - } - - std::error_code e; - if(ghc::filesystem::is_directory(currentPath, e)) - { - return DS_ERROR_MSG("Directory is found instead of file: " + - DS_STR(profileCompileFiles->at(i))); - } - - if(!ghc::filesystem::exists(currentPath, e)) - { - return DS_ERROR_MSG("File doesn't exist: " + - DS_STR(profileCompileFiles->at(i))); - } - - outSourcePaths.push_back(currentPath); - } - } - - return {}; -} - -DS::Result -runcpp2::GatherIncludePaths(const ghc::filesystem::path& scriptDirectory, - const Data::ScriptInfo& scriptInfo, - const Data::Profile& currentProfile, - const std::vector& dependencies, - std::vector& outIncludePaths) -{ - ssLOG_FUNC_INFO(); - - outIncludePaths.clear(); - - if(!scriptDirectory.is_absolute()) - return DS_ERROR_MSG("Script directory is not absolute: " + DS_STR(scriptDirectory)); - - //Get include paths from script - const Data::ProfilesProcessPaths* includePaths = - GetValueFromPlatformMap(scriptInfo.IncludePaths); - - outIncludePaths.push_back(scriptDirectory); - - if(includePaths != nullptr) - { - const std::vector* profileIncludePaths = - GetValueFromProfileMap(currentProfile, includePaths->Paths); - - if(profileIncludePaths != nullptr) - { - for(const auto& currentPath : *profileIncludePaths) - { - ghc::filesystem::path resolvedPath = currentPath; - if(currentPath.is_relative()) - resolvedPath = scriptDirectory / currentPath; - - if(resolvedPath.is_relative()) - { - std::string errMsg = - DS_STR("Failed to process include path: ") + DS_STR(currentPath) + - "\nTry to append path to script directory but failed" + - "\nFinal appended path: " + DS_STR(resolvedPath); - return DS_ERROR_MSG(errMsg); - } - - std::error_code e; - if(!ghc::filesystem::exists(resolvedPath, e)) - { - std::string errMsg = - DS_STR("Include path doesn't exist: ") + DS_STR(currentPath) + - "\nFullpath: " + DS_STR(resolvedPath); - return DS_ERROR_MSG(errMsg); - } - - if(!ghc::filesystem::is_directory(resolvedPath, e)) - { - std::string errMsg = - DS_STR("Include path is not a directory: ") + DS_STR(currentPath) + - "\nFullpath: " + DS_STR(resolvedPath); - return DS_ERROR_MSG(errMsg); - } - - outIncludePaths.push_back(resolvedPath); - } - } - } - - //Get include paths from dependencies - for(const Data::DependencyInfo* dependency : dependencies) - { - for(const std::string& includePath : dependency->AbsoluteIncludePaths) - outIncludePaths.push_back(ghc::filesystem::path(includePath)); - } - - return {}; -} - -DS::Result -runcpp2::GatherFilesIncludes( const std::vector& sourceFiles, - const std::vector& sourceHasCache, - const std::vector& includePaths, - SourceIncludeMap& outSourceIncludes) -{ - ssLOG_FUNC_INFO(); - - if(sourceFiles.size() != sourceHasCache.size()) - return DS_ERROR_MSG("Size of sourceFiles and sourceHasCache not matching"); - - outSourceIncludes.clear(); - - for(int i = 0; i < sourceFiles.size(); ++i) - { - if(sourceHasCache.at(i)) - continue; - - const ghc::filesystem::path& source = sourceFiles.at(i); - - std::unordered_set visitedFiles; - ssLOG_INFO("Gathering includes for " << source.string()); - - std::vector& currentIncludes = outSourceIncludes[source.string()]; - std::queue filesToProcess; - filesToProcess.push(source); - - while(!filesToProcess.empty()) - { - ghc::filesystem::path currentFile = filesToProcess.front(); - filesToProcess.pop(); - - if(visitedFiles.count(currentFile.string()) > 0) - continue; - - visitedFiles.insert(currentFile.string()); - - std::ifstream fileStream(currentFile); - if(!fileStream.is_open()) - return DS_ERROR_MSG("Failed to open file: " + DS_STR(currentFile)); - - std::string line; - while(std::getline(fileStream, line)) - { - std::string includePath; - if(!ParseIncludes(line, includePath)) - continue; - - ghc::filesystem::path resolvedInclude; - bool found = false; - - //For quoted includes, first check relative to source file - if(line.find('\"') != std::string::npos) - { - resolvedInclude = currentFile.parent_path() / includePath; - std::error_code ec; - if(ghc::filesystem::exists(resolvedInclude, ec)) - found = true; - } - - //Search in include paths if not found - if(!found) - { - for(const ghc::filesystem::path& searchPath : includePaths) - { - resolvedInclude = searchPath / includePath; - std::error_code ec; - if(ghc::filesystem::exists(resolvedInclude, ec)) - { - found = true; - break; - } - } - } - - if(found) - { - ssLOG_DEBUG("Found include file: " << resolvedInclude.string()); - currentIncludes.push_back(resolvedInclude); - filesToProcess.push(resolvedInclude); - } - } - } - } - - return {}; -} diff --git a/Src/runcpp2/PipelineSteps.hpp b/Src/runcpp2/PipelineSteps.hpp new file mode 100644 index 0000000..ef8cbc5 --- /dev/null +++ b/Src/runcpp2/PipelineSteps.hpp @@ -0,0 +1,1262 @@ +#ifndef RUNCPP2_PIPELINE_STEPS_HPP +#define RUNCPP2_PIPELINE_STEPS_HPP + +#include "runcpp2/Data/Profile.hpp" +#include "runcpp2/Data/PipelineResult.hpp" +#include "runcpp2/Data/ProfilesCommands.hpp" +#include "runcpp2/Data/ScriptInfo.hpp" +#include "runcpp2/Data/CmdOptions.hpp" +#include "runcpp2/Data/BuildTypeHelper.hpp" +#include "runcpp2/Data/BuildType.hpp" +#include "runcpp2/Data/DependencyInfo.hpp" +#include "runcpp2/Data/FileProperties.hpp" +#include "runcpp2/Data/FilesTypesInfo.hpp" +#include "runcpp2/Data/ParseCommon.hpp" +#include "runcpp2/Data/ProfilesFlagsOverride.hpp" +#include "runcpp2/Data/ProfilesProcessPaths.hpp" + +#include "runcpp2/BuildsManager.hpp" +#include "runcpp2/IncludeManager.hpp" + +#include "runcpp2/ConfigParsing.hpp" +#include "runcpp2/DependenciesHelper.hpp" +#include "runcpp2/ParseUtil.hpp" +#include "runcpp2/PlatformUtil.hpp" + +#if !defined(NOMINMAX) + #define NOMINMAX 1 +#endif + +#include "ghc/filesystem.hpp" +#include "System2.h" +#include "ssLogger/ssLog.hpp" +#include "dylib.hpp" +#include "DSResult/DSResult.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +namespace +{ + bool RunCompiledScript( const ghc::filesystem::path& executable, + const std::string& scriptPath, + const std::vector& runArgs, + int& returnStatus) + { + INTERNAL_RUNCPP2_SAFE_START(); + ssLOG_FUNC_INFO(); + + std::vector args; + for(size_t i = 0; i < runArgs.size(); ++i) + args.push_back(runArgs[i].c_str()); + + System2CommandInfo runCommandInfo = {}; + SYSTEM2_RESULT result = System2RunSubprocess( executable.c_str(), + args.data(), + args.size(), + &runCommandInfo); + + ssLOG_INFO("Running: " << executable.string()); + for(size_t i = 0; i < runArgs.size(); ++i) + ssLOG_INFO("- " << runArgs[i]); + + if(result != SYSTEM2_RESULT_SUCCESS) + { + ssLOG_ERROR("System2Run failed with result: " << result); + return false; + } + + result = System2GetCommandReturnValueSync(&runCommandInfo, &returnStatus, false); + if(result != SYSTEM2_RESULT_SUCCESS) + { + ssLOG_ERROR("System2GetCommandReturnValueSync failed with result: " << result); + return false; + } + + return true; + INTERNAL_RUNCPP2_SAFE_CATCH_RETURN(false); + } + + bool RunCompiledSharedLib( const std::string& scriptPath, + const ghc::filesystem::path& compiledSharedLibPath, + const std::vector& runArgs, + int& returnStatus) + { + INTERNAL_RUNCPP2_SAFE_START(); + ssLOG_FUNC_INFO(); + + std::error_code _; + if(!ghc::filesystem::exists(compiledSharedLibPath, _)) + { + ssLOG_ERROR("Failed to find shared library: " << compiledSharedLibPath.string()); + return false; + } + + //Load it + std::unique_ptr sharedLib; + + try + { + ssLOG_INFO("Trying to run shared library: " << compiledSharedLibPath.string()); + + //TODO: We might want to use unicode instead for the path + #if defined(_WIN32) + std::string sharedLibDir = compiledSharedLibPath.parent_path().string(); + if(SetDllDirectoryA(sharedLibDir.c_str()) == FALSE) + { + std::string lastError = runcpp2::GetWindowsError(); + ssLOG_ERROR("Failed to set DLL directory: " << lastError); + return false; + } + #endif + + sharedLib = std::unique_ptr(new dylib( compiledSharedLibPath.string(), + dylib::no_filename_decorations)); + } + catch(std::exception& e) + { + ssLOG_ERROR("Failed to load shared library " << compiledSharedLibPath.string() << + " with exception: "); + + ssLOG_ERROR(e.what()); + return false; + } + + //Get main as entry point + if(sharedLib->has_symbol("main") == false) + { + ssLOG_ERROR("The shared library does not have a main function"); + return false; + } + + int (*scriptFullMain)(int, const char**) = nullptr; + int (*scriptMain)() = nullptr; + + try + { + scriptFullMain = sharedLib->get_function("main"); + } + catch(const dylib::exception& ex) + { + ssLOG_DEBUG("Failed to get full main function from shared library: " << ex.what()); + } + catch(...) + { + ssLOG_ERROR("Failed to get entry point function"); + return false; + } + + if(scriptFullMain == nullptr) + { + try + { + scriptMain = sharedLib->get_function("_main"); + } + catch(const dylib::exception& ex) + { + ssLOG_DEBUG("Failed to get main function from shared library: " << ex.what()); + } + catch(...) + { + ssLOG_ERROR("Failed to get entry point function"); + return false; + } + } + + if(scriptMain == nullptr && scriptFullMain == nullptr) + { + ssLOG_ERROR("Failed to load function"); + return false; + } + + //Run the entry point + try + { + if(scriptFullMain != nullptr) + { + std::vector runArgsCStr(runArgs.size()); + for(size_t i = 0; i < runArgs.size(); ++i) + runArgsCStr.at(i) = &runArgs.at(i).at(0); + + returnStatus = scriptFullMain(runArgsCStr.size(), runArgsCStr.data()); + } + else if(scriptMain != nullptr) + returnStatus = scriptMain(); + } + catch(std::exception& e) + { + ssLOG_ERROR("Failed to run script main with exception: " << e.what()); + return true; + } + catch(...) + { + ssLOG_ERROR("Unknown exception caught"); + return true; + } + + return true; + + INTERNAL_RUNCPP2_SAFE_CATCH_RETURN(false); + } +} + +namespace runcpp2 +{ + inline DS::Result CopyFiles( const ghc::filesystem::path& destDir, + const std::vector& filePaths, + std::vector& outCopiedPaths) + { + ssLOG_FUNC_INFO(); + + std::error_code e; + for (const std::string& srcPath : filePaths) + { + ghc::filesystem::path destPath = destDir / ghc::filesystem::path(srcPath).filename(); + + if(ghc::filesystem::exists(srcPath, e)) + { + ghc::filesystem::copy(srcPath, + destPath, + ghc::filesystem::copy_options::update_existing, + e); + if(e) + { + std::string errorMsg = DS_STR("Failed to copy file from ") + srcPath + " to " + + destPath.string() + "\nError: " + e.message(); + return DS_ERROR_MSG(errorMsg); + } + + ssLOG_INFO("Copied from " << srcPath << " to " << destPath.string()); + outCopiedPaths.push_back(ProcessPath(destPath)); + } + else + return DS_ERROR_MSG("File to copy not found: " + srcPath); + } + + return {}; + } + + inline DS::Result RunProfileCommands( const Data::ProfilesCommands* commands, + const Data::Profile& profile, + const std::string& workingDir, + const std::string& commandType) + { + ssLOG_FUNC_INFO(); + + if(commands != nullptr) + { + const std::vector* commandSteps = + runcpp2::GetValueFromProfileMap(profile, commands->CommandSteps); + + if(commandSteps != nullptr) + { + for(const std::string& cmd : *commandSteps) + { + std::string output; + int returnCode = 0; + + if(!runcpp2::RunCommand(cmd, true, workingDir, output, returnCode)) + { + std::string errorMsg = commandType + " command failed: " + cmd + + " with return code " + DS_STR(returnCode) + "\n"; + errorMsg += "Was trying to run: " + cmd + "\n"; + errorMsg += "Output: \n" + output; + return DS_ERROR_MSG_EC(errorMsg, (int)PipelineResult::UNEXPECTED_FAILURE); + } + + ssLOG_INFO(commandType << " command ran: \n" << cmd); + ssLOG_INFO(commandType << " command output: \n" << output); + } + } + } + + return {}; + } + + inline DS::Result ValidateInputs( const std::string& scriptPath, + const std::vector& profiles, + ghc::filesystem::path& outAbsoluteScriptPath, + ghc::filesystem::path& outScriptDirectory, + std::string& outScriptName) + { + ssLOG_FUNC_INFO(); + + if(profiles.empty()) + return DS_ERROR_MSG_EC("No compiler profiles found", (int)PipelineResult::EMPTY_PROFILES); + + //Check if input file exists + std::error_code _; + if(!ghc::filesystem::exists(scriptPath, _)) + { + return DS_ERROR_MSG_EC( "File does not exist: " + scriptPath, + (int)PipelineResult::INVALID_SCRIPT_PATH); + } + + if(ghc::filesystem::is_directory(scriptPath, _)) + { + return DS_ERROR_MSG_EC( "The input file must not be a directory: " + scriptPath, + (int)PipelineResult::INVALID_SCRIPT_PATH); + } + + outAbsoluteScriptPath = ghc::filesystem::absolute(ghc::filesystem::canonical(scriptPath, _)); + outScriptDirectory = outAbsoluteScriptPath.parent_path(); + outScriptName = outAbsoluteScriptPath.stem().string(); + + ssLOG_DEBUG("scriptPath: " << scriptPath); + ssLOG_DEBUG("absoluteScriptPath: " << outAbsoluteScriptPath.string()); + ssLOG_DEBUG("scriptDirectory: " << outScriptDirectory.string()); + ssLOG_DEBUG("scriptName: " << outScriptName); + ssLOG_DEBUG("is_directory: " << ghc::filesystem::is_directory(outScriptDirectory)); + + return {}; + } + + inline DS::Result + ParseAndValidateScriptInfo( const ghc::filesystem::path& absoluteScriptPath, + const ghc::filesystem::path& scriptDirectory, + const std::string& scriptName, + const bool buildExecutable, + Data::ScriptInfo& outScriptInfo) + { + ssLOG_FUNC_INFO(); + + //Check if there's script info as yaml file instead + std::error_code e; + std::string parsableInfo; + std::ifstream inputFile; + + ghc::filesystem::path scriptInfoFile; + bool dedicatedYaml = false; + + //If we are having yaml as input + if(absoluteScriptPath.extension() == ".yaml" || absoluteScriptPath.extension() == ".yml") + { + scriptInfoFile = absoluteScriptPath; + dedicatedYaml = true; + } + //Otherwise we are having source file as input + else + { + //Try to see if there's a corresponding yaml file + ghc::filesystem::path dedicatedYamlLoc = + scriptDirectory / ghc::filesystem::path(scriptName + ".yaml"); + + if(ghc::filesystem::exists(dedicatedYamlLoc, e)) + { + dedicatedYaml = true; + scriptInfoFile = dedicatedYamlLoc; + } + else + scriptInfoFile = absoluteScriptPath; + } + + //Get parsable info + { + //Record write time for script file for watch option + outScriptInfo.LastWriteTime = ghc::filesystem::last_write_time(scriptInfoFile, e); + if(e) + { + std::string errorMsg = e.message(); + errorMsg += "\nFailed to get last write time for: " + scriptInfoFile.string(); + return DS_ERROR_MSG_EC(errorMsg, (int)PipelineResult::INVALID_SCRIPT_INFO); + } + + inputFile.open(scriptInfoFile); + + if(!inputFile) + { + return DS_ERROR_MSG_EC( "Failed to open file: " + scriptInfoFile.string(), + (int)PipelineResult::INVALID_SCRIPT_PATH); + } + + std::stringstream buffer; + buffer << inputFile.rdbuf(); + + if(dedicatedYaml) + parsableInfo = buffer.str(); + else + { + GetParsableInfo(buffer.str(), parsableInfo) + .DS_TRY_ACT(DS_TMP_ERROR.Message += + "\nAn error has been encountered when parsing info: " + + scriptInfoFile.string(); + DS_TMP_ERROR.ErrorCode = (int)PipelineResult::INVALID_SCRIPT_INFO; + DS_APPEND_TRACE(DS_TMP_ERROR); + return DS::Error(DS_TMP_ERROR)); + } + } + + //Try to parse the runcpp2 info + ParseScriptInfo(parsableInfo, outScriptInfo) + .DS_TRY_ACT(DS_APPEND_TRACE(DS_TMP_ERROR); + DS_TMP_ERROR.Message += "\nContent trying to parse: \n" + parsableInfo; + DS_TMP_ERROR.ErrorCode = (int)PipelineResult::INVALID_SCRIPT_INFO; + return DS::Error(DS_TMP_ERROR)); + + if(!parsableInfo.empty()) + { + ssLOG_DEBUG("Parsed script info YAML:"); + ssLOG_DEBUG("\n" << outScriptInfo.ToString("")); + } + + //Replace build type with internal executable type to trigger recompiling when switching to + //have or not have "--executable" option + if(outScriptInfo.CurrentBuildType == Data::BuildType::EXECUTABLE) + { + outScriptInfo.CurrentBuildType = buildExecutable ? + Data::BuildType::INTERNAL_EXECUTABLE_EXECUTABLE : + Data::BuildType::INTERNAL_EXECUTABLE_SHARED; + } + + return {}; + } + + inline DS::Result HandleCleanup( const Data::ScriptInfo& scriptInfo, + const Data::Profile& profile, + const ghc::filesystem::path& scriptDirectory, + const ghc::filesystem::path& buildDir, + const ghc::filesystem::path& absoluteScriptPath, + BuildsManager& buildsManager) + { + ssLOG_FUNC_INFO(); + + const Data::ProfilesCommands* cleanupCommands = + runcpp2::GetValueFromPlatformMap(scriptInfo.Cleanup); + + if(cleanupCommands != nullptr) + { + const std::vector* commands = + runcpp2::GetValueFromProfileMap(profile, cleanupCommands->CommandSteps); + if(commands != nullptr) + { + for(const std::string& cmd : *commands) + { + std::string output; + int returnCode = 0; + + if(!runcpp2::RunCommand(cmd, true, scriptDirectory, output, returnCode)) + { + return DS_ERROR_MSG_EC( "Cleanup command failed: " + cmd + " with return code " + + DS_STR(returnCode) + "\nOutput: \n" + output, + (int)PipelineResult::UNEXPECTED_FAILURE); + } + + ssLOG_INFO("Cleanup command ran: \n" << cmd); + ssLOG_INFO("Cleanup command output: \n" << output); + } + } + } + + //Remove build directory + std::error_code e; + if(!ghc::filesystem::remove_all(buildDir, e)) + { + return DS_ERROR_MSG_EC( "Failed to remove build directory: " + buildDir.string(), + (int)PipelineResult::UNEXPECTED_FAILURE); + } + + if(!buildsManager.RemoveBuildMapping(absoluteScriptPath)) + { + return DS_ERROR_MSG_EC( "Failed to remove build mapping", + (int)PipelineResult::UNEXPECTED_FAILURE); + } + + if(!buildsManager.SaveBuildsMappings()) + { + return DS_ERROR_MSG_EC( "Failed to save build mappings", + (int)PipelineResult::UNEXPECTED_FAILURE); + } + + return {}; + } + + inline DS::Result + InitializeBuildDirectory( const ghc::filesystem::path& configDir, + const ghc::filesystem::path& absoluteScriptPath, + bool useLocalBuildDir, + BuildsManager& outBuildsManager, + ghc::filesystem::path& outBuildDir, + IncludeManager& outIncludeManager) + { + ssLOG_FUNC_INFO(); + + //Create build directory + ghc::filesystem::path buildDirPath = useLocalBuildDir ? + ghc::filesystem::current_path() / ".runcpp2" : + configDir; + + //Create a class that manages build folder + outBuildsManager = BuildsManager(buildDirPath); + + if(!outBuildsManager.Initialize()) + { + return DS_ERROR_MSG_EC( "Failed to initialize builds manager", + (int)PipelineResult::INVALID_BUILD_DIR); + } + + bool createdBuildDir = false; + bool writeMapping = false; + if(!outBuildsManager.HasBuildMapping(absoluteScriptPath)) + writeMapping = true; + + if(outBuildsManager.GetBuildMapping(absoluteScriptPath, outBuildDir)) + { + if(writeMapping && !outBuildsManager.SaveBuildsMappings()) + ssLOG_FATAL("Failed to save builds mappings"); + else + createdBuildDir = true; + } + + if(!createdBuildDir) + { + return DS_ERROR_MSG_EC( "Failed to create local build directory for: " + + DS_STR(absoluteScriptPath), + (int)PipelineResult::INVALID_BUILD_DIR); + } + + outIncludeManager = IncludeManager(); + if(!outIncludeManager.Initialize(outBuildDir)) + { + return DS_ERROR_MSG_EC( "Failed to initialize include manager", + (int)PipelineResult::INVALID_BUILD_DIR); + } + + return {}; + } + + inline DS::Result ResolveScriptImports( Data::ScriptInfo& scriptInfo, + const ghc::filesystem::path& scriptPath, + const ghc::filesystem::path& buildDir) + { + ssLOG_FUNC_INFO(); + + //Resolve all the script info imports first before evaluating it + ResolveImports(scriptInfo, scriptPath, buildDir) + .DS_TRY_ACT(DS_TMP_ERROR.ErrorCode = (int)PipelineResult::UNEXPECTED_FAILURE; + DS_APPEND_TRACE(DS_TMP_ERROR); + return DS::Error(DS_TMP_ERROR)); + + return {}; + } + + inline DS::Result CheckScriptInfoChanges( const ghc::filesystem::path& buildDir, + const Data::ScriptInfo& scriptInfo, + const Data::Profile& profile, + const ghc::filesystem::path& absoluteScriptPath, + const Data::ScriptInfo* lastScriptInfo, + const int maxThreads, + bool& outAllRecompileNeeded, + bool& outRelinkNeeded, + std::vector& outChangedDependencies) + { + ssLOG_FUNC_INFO(); + + const ghc::filesystem::path scriptDirectory = absoluteScriptPath.parent_path(); + ghc::filesystem::path lastScriptInfoFilePath = buildDir / "LastScriptInfo.yaml"; + Data::ScriptInfo lastScriptInfoFromDisk; + + std::error_code e; + + //Run Setup commands if we don't have previous build + if(!ghc::filesystem::exists(lastScriptInfoFilePath, e)) + { + const Data::ProfilesCommands* setupCommands = + runcpp2::GetValueFromPlatformMap(scriptInfo.Setup); + + if(setupCommands != nullptr) + { + RunProfileCommands(setupCommands, profile, scriptDirectory.string(), "Setup").DS_TRY(); + } + } + + //Compare script info in memory or from disk + const Data::ScriptInfo* lastInfo = lastScriptInfo; + if(lastInfo == nullptr && ghc::filesystem::exists(lastScriptInfoFilePath, e)) + { + ssLOG_DEBUG("Last script info file exists: " << lastScriptInfoFilePath); + std::ifstream lastScriptInfoFile; + lastScriptInfoFile.open(lastScriptInfoFilePath); + std::stringstream lastScriptInfoBuffer; + lastScriptInfoBuffer << lastScriptInfoFile.rdbuf(); + + int currentThreadTargetLevel = ssLOG_GET_CURRENT_THREAD_TARGET_LEVEL(); + ssLOG_SET_CURRENT_THREAD_TARGET_LEVEL(ssLOG_LEVEL_NONE); + + do + { + if(!ParseScriptInfo(lastScriptInfoBuffer.str(), lastScriptInfoFromDisk).HasValue()) + break; + + //Resolve imports for last script info + ResolveScriptImports(lastScriptInfoFromDisk, absoluteScriptPath, buildDir).DS_TRY(); + lastInfo = &lastScriptInfoFromDisk; + } + while(false); + + ssLOG_SET_CURRENT_THREAD_TARGET_LEVEL(currentThreadTargetLevel); + + if(lastInfo != nullptr) + ssLOG_INFO("Last script info parsed"); + else + ssLOG_INFO("Failed to parse last script info"); + } + + //Check if the cached script info has changed + if(lastInfo != nullptr) + { + //Relink if there are any changes to the link flags + { + const Data::ProfilesFlagsOverride* lastLinkFlags = + runcpp2::GetValueFromPlatformMap(lastInfo->OverrideLinkFlags); + const Data::ProfilesFlagsOverride* currentLinkFlags = + runcpp2::GetValueFromPlatformMap(scriptInfo.OverrideLinkFlags); + + outRelinkNeeded = (lastLinkFlags == nullptr) != (currentLinkFlags == nullptr) || + ( + lastLinkFlags != nullptr && + currentLinkFlags != nullptr && + !lastLinkFlags->Equals(*currentLinkFlags) + ); + } + + outAllRecompileNeeded = scriptInfo.IsAllCompiledCacheInvalidated(*lastInfo); + + //Check dependencies + for(int i = 0; i < scriptInfo.Dependencies.size(); ++i) + { + if( lastInfo->Dependencies.size() <= i || + !scriptInfo.Dependencies[i].Equals(lastInfo->Dependencies[i])) + { + outChangedDependencies.push_back(scriptInfo.Dependencies[i].Name); + } + } + + if(outAllRecompileNeeded || outRelinkNeeded) + { + ssLOG_INFO( "Last script info is out of date, " << + (outAllRecompileNeeded ? "recompiling..." : "relinking...")); + } + } + else + outAllRecompileNeeded = true; + + ssLOG_DEBUG("recompileNeeded: " << outAllRecompileNeeded << + ", changedDependencies.size(): " << outChangedDependencies.size() << + ", relinkNeeded: " << outRelinkNeeded); + + //Write to file if there's any changes to the current script info + if( !lastInfo || + outAllRecompileNeeded || + !outChangedDependencies.empty() || + outRelinkNeeded || + !scriptInfo.Equals(*lastInfo)) + { + std::ofstream writeOutputFile(lastScriptInfoFilePath); + if(!writeOutputFile) + { + return DS_ERROR_MSG_EC( "Failed to open file: " + DS_STR(lastScriptInfoFilePath), + (int)PipelineResult::INVALID_BUILD_DIR); + } + + writeOutputFile << scriptInfo.ToString(""); + ssLOG_DEBUG("Wrote current script info to " << lastScriptInfoFilePath.string()); + } + + return {}; + } + + + inline DS::Result + ProcessDependencies(Data::ScriptInfo& scriptInfo, + const Data::Profile& profile, + const ghc::filesystem::path& absoluteScriptPath, + const ghc::filesystem::path& buildDir, + const std::unordered_map& currentOptions, + const std::vector& changedDependencies, + const int maxThreads, + std::vector& outAvailableDependencies, + std::vector& outGatheredBinariesPaths) + { + ssLOG_FUNC_INFO(); + + for(int i = 0; i < scriptInfo.Dependencies.size(); ++i) + { + if(IsDependencyAvailableForThisPlatform(scriptInfo.Dependencies.at(i))) + outAvailableDependencies.push_back(&scriptInfo.Dependencies.at(i)); + } + + std::vector dependenciesLocalCopiesPaths; + std::vector dependenciesSourcePaths; + GetDependenciesPaths( outAvailableDependencies, + dependenciesLocalCopiesPaths, + dependenciesSourcePaths, + absoluteScriptPath, + buildDir) + .DS_TRY_ACT(DS_TMP_ERROR.ErrorCode = (int)PipelineResult::DEPENDENCIES_FAILED; + DS_APPEND_TRACE(DS_TMP_ERROR); + return DS::Error(DS_TMP_ERROR)); + + if(currentOptions.count(CmdOptions::RESET_DEPENDENCIES) > 0 || !changedDependencies.empty()) + { + if(currentOptions.count(CmdOptions::BUILD_SOURCE_ONLY) > 0) + { + std::string errorMsg = + "Dependencies settings have changed or being reset explicitly.\n" + "Cannot just build source files only without building dependencies"; + return DS_ERROR_MSG_EC(errorMsg, (int)PipelineResult::INVALID_OPTION); + } + + std::string depsToReset = "all"; + if(!changedDependencies.empty()) + { + depsToReset = changedDependencies[0]; + for(int i = 1; i < changedDependencies.size(); ++i) + depsToReset += "," + changedDependencies[i]; + } + + CleanupDependencies(profile, + scriptInfo, + outAvailableDependencies, + dependenciesLocalCopiesPaths, + currentOptions.count(CmdOptions::RESET_DEPENDENCIES) > 0 ? + currentOptions.at(CmdOptions::RESET_DEPENDENCIES) : + depsToReset) + .DS_TRY_ACT(DS_TMP_ERROR.ErrorCode = (int)PipelineResult::DEPENDENCIES_FAILED; + DS_APPEND_TRACE(DS_TMP_ERROR); + return DS::Error(DS_TMP_ERROR)); + } + + if(currentOptions.count(CmdOptions::RESET_DEPENDENCIES) > 0) + return {}; + + SetupDependenciesIfNeeded( profile, + buildDir, + scriptInfo, + outAvailableDependencies, + dependenciesLocalCopiesPaths, + dependenciesSourcePaths, + maxThreads) + .DS_TRY_ACT(DS_TMP_ERROR.ErrorCode = (int)PipelineResult::DEPENDENCIES_FAILED; + DS_APPEND_TRACE(DS_TMP_ERROR); + return DS::Error(DS_TMP_ERROR)); + + //Sync local dependencies before building + SyncLocalDependencies( outAvailableDependencies, + dependenciesSourcePaths, + dependenciesLocalCopiesPaths) + .DS_TRY_ACT(DS_TMP_ERROR.ErrorCode = (int)PipelineResult::DEPENDENCIES_FAILED; + DS_APPEND_TRACE(DS_TMP_ERROR); + return DS::Error(DS_TMP_ERROR)); + + if(currentOptions.count(CmdOptions::BUILD_SOURCE_ONLY) == 0) + { + BuildDependencies( profile, + scriptInfo, + outAvailableDependencies, + dependenciesLocalCopiesPaths, + maxThreads) + .DS_TRY_ACT + ( + DS_TMP_ERROR.Message += + "\nFailed to build script dependencies. Maybe try resetting dependencies " + "with \"-rd all\" and run again?"; + DS_TMP_ERROR.ErrorCode = (int)PipelineResult::DEPENDENCIES_FAILED; + DS_APPEND_TRACE(DS_TMP_ERROR); + return DS::Error(DS_TMP_ERROR); + ); + } + + GatherDependenciesBinaries( outAvailableDependencies, + dependenciesLocalCopiesPaths, + profile, + outGatheredBinariesPaths) + .DS_TRY_ACT(DS_TMP_ERROR.ErrorCode = (int)PipelineResult::DEPENDENCIES_FAILED; + DS_APPEND_TRACE(DS_TMP_ERROR); + return DS::Error(DS_TMP_ERROR)); + + return {}; + } + + inline void SeparateDependencyFiles(const Data::FilesTypesInfo& filesTypes, + const std::vector& gatheredBinariesPaths, + std::vector& outLinkFilesPaths, + std::vector& outFilesToCopyPaths) + { + ssLOG_FUNC_INFO(); + INTERNAL_RUNCPP2_SAFE_START(); + + std::unordered_set linkExtensions; + + //Populate the set of link extensions + if(runcpp2::HasValueFromPlatformMap(filesTypes.StaticLinkFile.Extension)) + { + linkExtensions.insert(*runcpp2::GetValueFromPlatformMap(filesTypes .StaticLinkFile + .Extension)); + } + if(runcpp2::HasValueFromPlatformMap(filesTypes.SharedLinkFile.Extension)) + { + linkExtensions.insert(*runcpp2::GetValueFromPlatformMap(filesTypes .SharedLinkFile + .Extension)); + } + if(runcpp2::HasValueFromPlatformMap(filesTypes.ObjectLinkFile.Extension)) + { + linkExtensions.insert(*runcpp2::GetValueFromPlatformMap(filesTypes .ObjectLinkFile + .Extension)); + } + + //Separate the gathered files from dependencies into files to link and files to copy + for(int i = 0; i < gatheredBinariesPaths.size(); ++i) + { + ghc::filesystem::path filePath(gatheredBinariesPaths.at(i)); + std::string extension = runcpp2::GetFileExtensionWithoutVersion(filePath); + + //Check if the file is a link file based on its extension + if(linkExtensions.find(extension) != linkExtensions.end()) + { + outLinkFilesPaths.push_back(gatheredBinariesPaths.at(i)); + + //Special case when SharedLinkFile and SharedLibraryFile share the same extension + if( runcpp2::HasValueFromPlatformMap(filesTypes.SharedLibraryFile.Extension) && + *runcpp2::GetValueFromPlatformMap(filesTypes.SharedLibraryFile + .Extension) == extension) + { + outFilesToCopyPaths.push_back(gatheredBinariesPaths.at(i)); + } + } + else + outFilesToCopyPaths.push_back(gatheredBinariesPaths.at(i)); + } + + ssLOG_INFO("Files to link:"); + for(int i = 0; i < outLinkFilesPaths.size(); ++i) + ssLOG_INFO(" " << outLinkFilesPaths[i]); + + ssLOG_INFO("Files to copy:"); + for(int i = 0; i < outFilesToCopyPaths.size(); ++i) + ssLOG_INFO(" " << outFilesToCopyPaths[i]); + + INTERNAL_RUNCPP2_SAFE_CATCH_RETURN(void()); + } + + inline DS::Result HandlePreBuild( const Data::ScriptInfo& scriptInfo, + const Data::Profile& profile, + const ghc::filesystem::path& buildDir) + { + ssLOG_FUNC_INFO(); + + const Data::ProfilesCommands* preBuildCommands = + runcpp2::GetValueFromPlatformMap(scriptInfo.PreBuild); + + RunProfileCommands(preBuildCommands, profile, buildDir.string(), "PreBuild").DS_TRY(); + return {}; + } + + inline DS::Result HandlePostBuild(const Data::ScriptInfo& scriptInfo, + const Data::Profile& profile, + const ghc::filesystem::path& buildDir) + { + ssLOG_FUNC_INFO(); + + const Data::ProfilesCommands* postBuildCommands = + GetValueFromPlatformMap(scriptInfo.PostBuild); + + RunProfileCommands(postBuildCommands, profile, buildDir.string(), "PostBuild").DS_TRY(); + return {}; + } + + inline DS::Result + RunCompiledOutput( const ghc::filesystem::path& target, + const ghc::filesystem::path& absoluteScriptPath, + const Data::ScriptInfo& scriptInfo, + const std::vector& runArgs, + const std::unordered_map& currentOptions, + int& returnStatus) + { + ssLOG_FUNC_INFO(); + + //Skip running if not executable + if( scriptInfo.CurrentBuildType != Data::BuildType::INTERNAL_EXECUTABLE_EXECUTABLE && + scriptInfo.CurrentBuildType != Data::BuildType::INTERNAL_EXECUTABLE_SHARED) + { + ssLOG_INFO("Skipping run - output is not executable"); + return {}; + } + + std::error_code e; + if(target.empty() || !ghc::filesystem::exists(target, e)) + { + return DS_ERROR_MSG_EC( "Failed to find the compiled file to run", + (int)PipelineResult::COMPILE_LINK_FAILED); + } + + //Prepare run arguments + std::vector finalRunArgs; + finalRunArgs.push_back(target.string()); + if(scriptInfo.PassScriptPath) + finalRunArgs.push_back(absoluteScriptPath); + + //Add user provided arguments + for(size_t i = 0; i < runArgs.size(); ++i) + finalRunArgs.push_back(runArgs[i]); + + if(scriptInfo.CurrentBuildType == Data::BuildType::INTERNAL_EXECUTABLE_EXECUTABLE) + { + //Running the script with modified args + if(!RunCompiledScript(target, absoluteScriptPath, finalRunArgs, returnStatus)) + return DS_ERROR_MSG_EC("Failed to run script", (int)PipelineResult::RUN_SCRIPT_FAILED); + } + else + { + //Load the shared library and run it with modified args + if(!RunCompiledSharedLib(absoluteScriptPath, target, finalRunArgs, returnStatus)) + return DS_ERROR_MSG_EC("Failed to run script", (int)PipelineResult::RUN_SCRIPT_FAILED); + } + + return {}; + } + + inline DS::Result + GetBuiltTargetPaths(const ghc::filesystem::path& buildDir, + const std::string& scriptName, + const Data::Profile& profile, + const std::unordered_map< CmdOptions, + std::string>& currentOptions, + const Data::ScriptInfo& scriptInfo, + std::vector& outTargets, + ghc::filesystem::path* outRunnableTarget) + { + ssLOG_FUNC_INFO(); + + std::error_code _; + outTargets.clear(); + + //Validate executable option against build type + if( currentOptions.count(CmdOptions::EXECUTABLE) > 0 && + scriptInfo.CurrentBuildType != Data::BuildType::INTERNAL_EXECUTABLE_SHARED && + scriptInfo.CurrentBuildType != Data::BuildType::INTERNAL_EXECUTABLE_EXECUTABLE) + { + std::string errMsg = + DS_STR("Cannot run as executable - script is configured for ") + + Data::BuildTypeToString(scriptInfo.CurrentBuildType) + + " output. Please remove --executable flag or change build type to Executable"; + + return DS_ERROR_MSG_EC(errMsg, (int)PipelineResult::INVALID_OPTION); + } + + //Get all target paths + std::vector isRunnable; + if(!Data::BuildTypeHelper::GetPossibleOutputPaths( buildDir, + scriptName, + profile, + scriptInfo.CurrentBuildType, + outTargets, + isRunnable)) + { + return DS_ERROR_MSG_EC( "Extension or prefix not found in compiler profile for build type: " + + runcpp2::Data::BuildTypeToString(scriptInfo.CurrentBuildType), + (int)PipelineResult::INVALID_SCRIPT_INFO); + } + + //Verify all targets exist + for(const ghc::filesystem::path& target : outTargets) + { + if(!ghc::filesystem::exists(target, _)) + { + ssLOG_WARNING("Failed to find the compiled file: " << target.string()); + continue; + //return PipelineResult::COMPILE_LINK_FAILED; + } + } + + //If requested, find the runnable target + if(outRunnableTarget != nullptr) + { + for(size_t i = 0; i < outTargets.size(); ++i) + { + if(isRunnable.at(i)) + { + *outRunnableTarget = outTargets.at(i); + break; + } + } + } + + return {}; + } + + inline DS::Result GatherSourceFiles( const ghc::filesystem::path& absoluteScriptPath, + const Data::ScriptInfo& scriptInfo, + const Data::Profile& currentProfile, + std::vector& outSourcePaths) + { + ssLOG_FUNC_INFO(); + + if(!absoluteScriptPath.is_absolute()) + return DS_ERROR_MSG("Script path is not absolute: " + DS_STR(absoluteScriptPath)); + + outSourcePaths.clear(); + if(absoluteScriptPath.extension() != ".yaml" && absoluteScriptPath.extension() != ".yml") + { + if(currentProfile.FileExtensions.count(absoluteScriptPath.extension().string()) == 0) + return DS_ERROR_MSG("Input file cannot be used for profile " + currentProfile.Name); + else + outSourcePaths.push_back(absoluteScriptPath); + } + + do + { + const Data::ProfilesProcessPaths* compileFiles = + GetValueFromPlatformMap(scriptInfo.OtherFilesToBeCompiled); + + if(compileFiles == nullptr) + { + ssLOG_INFO("No other files to be compiled files current platform"); + + if(!scriptInfo.OtherFilesToBeCompiled.empty()) + { + ssLOG_WARNING( "Other source files are present, " + "but none are included for current configuration. " + "Is this intended?"); + } + break; + } + + const std::vector* profileCompileFiles = + GetValueFromProfileMap(currentProfile, compileFiles->Paths); + + if(!profileCompileFiles) + { + ssLOG_INFO("No other files to be compiled for current profile"); + break; + } + + //TODO: Allow filepaths to contain wildcards as follows + //* as directory or filename wildcard + //i.e. "./*/test/*.cpp" will match "./moduleA/test/a.cpp" and "./moduleB/test/b.cpp" + + //** as recursive directory wildcard + //i.e. "./**/*.cpp" will match any .cpp files + //i.e. "./**/test.cpp" will match any files named "test.cpp" + + //For the time being, each entry will represent a path + { + const ghc::filesystem::path scriptDirectory = + ghc::filesystem::path(absoluteScriptPath).parent_path(); + + for(int i = 0; i < profileCompileFiles->size(); ++i) + { + ghc::filesystem::path currentPath = profileCompileFiles->at(i); + if(currentPath.is_relative()) + currentPath = scriptDirectory / currentPath; + + if(currentPath.is_relative()) + { + std::string errMsg = DS_STR("Failed to process compile path: ") + + DS_STR(profileCompileFiles->at(i)) + + "\nTry to append path to script directory but failed" + + "\nFinal appended path: " + + DS_STR(currentPath); + return DS_ERROR_MSG(errMsg); + } + + std::error_code e; + if(ghc::filesystem::is_directory(currentPath, e)) + { + return DS_ERROR_MSG("Directory is found instead of file: " + + DS_STR(profileCompileFiles->at(i))); + } + + if(!ghc::filesystem::exists(currentPath, e)) + { + return DS_ERROR_MSG("File doesn't exist: " + + DS_STR(profileCompileFiles->at(i))); + } + + outSourcePaths.push_back(currentPath); + } + } + } + while(0); + + if(outSourcePaths.empty()) + return DS_ERROR_MSG("No source files found for compiling."); + + return {}; + } + + inline DS::Result + GatherIncludePaths( const ghc::filesystem::path& scriptDirectory, + const Data::ScriptInfo& scriptInfo, + const Data::Profile& currentProfile, + const std::vector& dependencies, + std::vector& outIncludePaths) + { + ssLOG_FUNC_INFO(); + + outIncludePaths.clear(); + + if(!scriptDirectory.is_absolute()) + return DS_ERROR_MSG("Script directory is not absolute: " + DS_STR(scriptDirectory)); + + //Get include paths from script + const Data::ProfilesProcessPaths* includePaths = + GetValueFromPlatformMap(scriptInfo.IncludePaths); + + outIncludePaths.push_back(scriptDirectory); + + if(includePaths != nullptr) + { + const std::vector* profileIncludePaths = + GetValueFromProfileMap(currentProfile, includePaths->Paths); + + if(profileIncludePaths != nullptr) + { + for(const auto& currentPath : *profileIncludePaths) + { + ghc::filesystem::path resolvedPath = currentPath; + if(currentPath.is_relative()) + resolvedPath = scriptDirectory / currentPath; + + if(resolvedPath.is_relative()) + { + std::string errMsg = + DS_STR("Failed to process include path: ") + DS_STR(currentPath) + + "\nTry to append path to script directory but failed" + + "\nFinal appended path: " + DS_STR(resolvedPath); + return DS_ERROR_MSG(errMsg); + } + + std::error_code e; + if(!ghc::filesystem::exists(resolvedPath, e)) + { + std::string errMsg = + DS_STR("Include path doesn't exist: ") + DS_STR(currentPath) + + "\nFullpath: " + DS_STR(resolvedPath); + return DS_ERROR_MSG(errMsg); + } + + if(!ghc::filesystem::is_directory(resolvedPath, e)) + { + std::string errMsg = + DS_STR("Include path is not a directory: ") + DS_STR(currentPath) + + "\nFullpath: " + DS_STR(resolvedPath); + return DS_ERROR_MSG(errMsg); + } + + outIncludePaths.push_back(resolvedPath); + } + } + } + + //Get include paths from dependencies + for(const Data::DependencyInfo* dependency : dependencies) + { + for(const std::string& includePath : dependency->AbsoluteIncludePaths) + outIncludePaths.push_back(ghc::filesystem::path(includePath)); + } + + return {}; + } + + using SourceIncludeMap = std::unordered_map>; + inline DS::Result + GatherFilesIncludes(const std::vector& sourceFiles, + const std::vector& sourceHasCache, + const std::vector& includePaths, + SourceIncludeMap& outSourceIncludes) + { + ssLOG_FUNC_INFO(); + + if(sourceFiles.size() != sourceHasCache.size()) + return DS_ERROR_MSG("Size of sourceFiles and sourceHasCache not matching"); + + outSourceIncludes.clear(); + + for(int i = 0; i < sourceFiles.size(); ++i) + { + if(sourceHasCache.at(i)) + continue; + + const ghc::filesystem::path& source = sourceFiles.at(i); + + std::unordered_set visitedFiles; + ssLOG_INFO("Gathering includes for " << source.string()); + + std::vector& currentIncludes = outSourceIncludes[source.string()]; + std::queue filesToProcess; + filesToProcess.push(source); + + while(!filesToProcess.empty()) + { + ghc::filesystem::path currentFile = filesToProcess.front(); + filesToProcess.pop(); + + if(visitedFiles.count(currentFile.string()) > 0) + continue; + + visitedFiles.insert(currentFile.string()); + + std::ifstream fileStream(currentFile); + if(!fileStream.is_open()) + return DS_ERROR_MSG("Failed to open file: " + DS_STR(currentFile)); + + std::string line; + while(std::getline(fileStream, line)) + { + std::string includePath; + if(!ParseIncludes(line, includePath)) + continue; + + ghc::filesystem::path resolvedInclude; + bool found = false; + + //For quoted includes, first check relative to source file + if(line.find('\"') != std::string::npos) + { + resolvedInclude = currentFile.parent_path() / includePath; + std::error_code ec; + if(ghc::filesystem::exists(resolvedInclude, ec)) + found = true; + } + + //Search in include paths if not found + if(!found) + { + for(const ghc::filesystem::path& searchPath : includePaths) + { + resolvedInclude = searchPath / includePath; + std::error_code ec; + if(ghc::filesystem::exists(resolvedInclude, ec)) + { + found = true; + break; + } + } + } + + if(found) + { + ssLOG_DEBUG("Found include file: " << resolvedInclude.string()); + currentIncludes.push_back(resolvedInclude); + filesToProcess.push(resolvedInclude); + } + } + } + } + + return {}; + } +} + +#endif diff --git a/Src/runcpp2/PlatformUtil.cpp b/Src/runcpp2/PlatformUtil.cpp deleted file mode 100644 index 59986ae..0000000 --- a/Src/runcpp2/PlatformUtil.cpp +++ /dev/null @@ -1,191 +0,0 @@ -#include "runcpp2/PlatformUtil.hpp" - -#include "ssLogger/ssLog.hpp" -#include "ghc/filesystem.hpp" -#include -#include - -#if !INTERNAL_RUNCPP2_UNIT_TESTS - #define CO_NO_OVERRIDE 1 - #include "CppOverride.hpp" -#else - #include "CppOverride.hpp" - extern CO_DECLARE_INSTANCE(OverrideInstance); -#endif - - -namespace -{ - char GetAltFileSystemSeparator() - { - #ifdef _WIN32 - return '/'; - #elif __unix__ - return '\0'; - #else - return '\0'; - #endif - } - - char GetFileSystemSeparator() - { - #ifdef _WIN32 - return '\\'; - #elif __unix__ - return '/'; - #else - return '/'; - #endif - } -} - -std::string runcpp2::ProcessPath(const std::string& path) -{ - std::string processedScriptPath; - - char altSeparator = GetAltFileSystemSeparator(); - - //Convert alternative slashes to native slashes - if(altSeparator != '\0') - { - char separator = GetFileSystemSeparator(); - - processedScriptPath = path; - for(int i = 0; i < processedScriptPath.size(); ++i) - { - if(processedScriptPath[i] == altSeparator) - processedScriptPath[i] = separator; - } - - return processedScriptPath; - } - else - return path; -} - -std::vector runcpp2::GetPlatformNames() -{ - CO_INSERT_IMPL(OverrideInstance, std::vector, ()); - - #ifdef _WIN32 - return {"Windows", "DefaultPlatform"}; - #elif __linux__ - return {"Linux", "Unix", "DefaultPlatform"}; - #elif __APPLE__ - return {"MacOS", "Unix", "DefaultPlatform"}; - #elif __unix__ - return {"Unix", "DefaultPlatform"}; - #else - return {"Unknown", "DefaultPlatform"}; - #endif -} - -bool runcpp2::RunCommand( const std::string& command, - const bool& captureOutput, - const std::string& runDirectory, - std::string& outOutput, - int& outReturnCode) -{ - ssLOG_FUNC_DEBUG(); - ssLOG_DEBUG("Running: " << command); - System2CommandInfo commandInfo = {}; - if(!runDirectory.empty()) - commandInfo.RunDirectory = runDirectory.c_str(); - - commandInfo.RedirectOutput = captureOutput; - SYSTEM2_RESULT sys2Result = System2Run(command.c_str(), &commandInfo); - - if(sys2Result != SYSTEM2_RESULT_SUCCESS) - { - ssLOG_ERROR("System2Run failed with result: " << sys2Result); - return false; - } - outOutput.clear(); - - if(captureOutput) - { - do - { - uint32_t byteRead = 0; - outOutput.resize(outOutput.size() + 4096); - - sys2Result = - System2ReadFromOutput( &commandInfo, - const_cast(outOutput.data()) + outOutput.size() - 4096, - 4096, - &byteRead); - - outOutput.resize(outOutput.size() + byteRead); - } - while(sys2Result == SYSTEM2_RESULT_READ_NOT_FINISHED); - } - - sys2Result = System2GetCommandReturnValueSync(&commandInfo, &outReturnCode, false); - if(sys2Result != SYSTEM2_RESULT_SUCCESS) - { - ssLOG_ERROR("System2GetCommandReturnValueSync failed with result: " << sys2Result); - return false; - } - - if(captureOutput) - ssLOG_DEBUG("outOutput: \n" << outOutput.c_str()); - - if(outReturnCode != 0) - { - ssLOG_DEBUG("Failed when running command with return code: " << outReturnCode); - return false; - } - - return true; -} - - -#if defined(_WIN32) - //Credit: https://stackoverflow.com/a/17387176 - std::string runcpp2::GetWindowsError() - { - //Get the error message ID, if any. - DWORD errorMessageID = ::GetLastError(); - if(errorMessageID == 0) { - return std::string(); //No error message has been recorded - } - - LPSTR messageBuffer = nullptr; - - //Ask Win32 to give us the string version of that message ID. - //The parameters we pass in, tell Win32 to create the buffer that holds the message for us (because we don't yet know how long the message string will be). - size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); - - //Copy the error message into a std::string. - std::string message(messageBuffer, size); - - //Free the Win32's string's buffer. - LocalFree(messageBuffer); - - return message; - } -#endif - -std::string runcpp2::GetFileExtensionWithoutVersion(const ghc::filesystem::path& path) -{ - std::string filename = path.filename().string(); - bool inNumericPart = true; - int lastDotPos = filename.length(); - - for(int i = filename.length() - 1; i >= 0; --i) - { - if(filename[i] == '.') - { - if(!inNumericPart) - return filename.substr(i, lastDotPos - i); - - inNumericPart = true; - lastDotPos = i; - } - else if(!std::isdigit(filename[i])) - inNumericPart = false; - } - - return ""; -} diff --git a/Src/runcpp2/PlatformUtil.hpp b/Src/runcpp2/PlatformUtil.hpp new file mode 100644 index 0000000..494e6c8 --- /dev/null +++ b/Src/runcpp2/PlatformUtil.hpp @@ -0,0 +1,241 @@ +#ifndef RUNCPP2_PLATFORM_UTIL_HPP +#define RUNCPP2_PLATFORM_UTIL_HPP + +#include "runcpp2/Data/ParseCommon.hpp" + +#if !defined(NOMINMAX) + #define NOMINMAX 1 +#endif + +#include "System2.h" +#include "ssLogger/ssLog.hpp" +#include "ghc/filesystem.hpp" + +#include +#include +#include +#include +#include + +#if !defined(INTERNAL_RUNCPP2_UNIT_TESTS) || !INTERNAL_RUNCPP2_UNIT_TESTS + #define CO_NO_OVERRIDE 1 + #include "CppOverride.hpp" +#else + #include "CppOverride.hpp" + extern CO_DECLARE_INSTANCE(OverrideInstance); +#endif + +namespace +{ + char GetAltFileSystemSeparator() + { + #ifdef _WIN32 + return '/'; + #elif __unix__ + return '\0'; + #else + return '\0'; + #endif + } + + char GetFileSystemSeparator() + { + #ifdef _WIN32 + return '\\'; + #elif __unix__ + return '/'; + #else + return '/'; + #endif + } +} + +namespace runcpp2 +{ + inline std::string ProcessPath(const std::string& path) + { + std::string processedScriptPath; + + char altSeparator = GetAltFileSystemSeparator(); + + //Convert alternative slashes to native slashes + if(altSeparator != '\0') + { + char separator = GetFileSystemSeparator(); + + processedScriptPath = path; + for(int i = 0; i < processedScriptPath.size(); ++i) + { + if(processedScriptPath[i] == altSeparator) + processedScriptPath[i] = separator; + } + + return processedScriptPath; + } + else + return path; + } + + inline std::vector GetPlatformNames() + { + CO_INSERT_IMPL(OverrideInstance, std::vector, ()); + + #ifdef _WIN32 + return {"Windows", "DefaultPlatform"}; + #elif __linux__ + return {"Linux", "Unix", "DefaultPlatform"}; + #elif __APPLE__ + return {"MacOS", "Unix", "DefaultPlatform"}; + #elif __unix__ + return {"Unix", "DefaultPlatform"}; + #else + return {"Unknown", "DefaultPlatform"}; + #endif + } + + inline bool RunCommand( const std::string& command, + const bool& captureOutput, + const std::string& runDirectory, + std::string& outOutput, + int& outReturnCode) + { + ssLOG_FUNC_DEBUG(); + ssLOG_DEBUG("Running: " << command); + System2CommandInfo commandInfo = {}; + if(!runDirectory.empty()) + commandInfo.RunDirectory = runDirectory.c_str(); + + commandInfo.RedirectOutput = captureOutput; + SYSTEM2_RESULT sys2Result = System2Run(command.c_str(), &commandInfo); + + if(sys2Result != SYSTEM2_RESULT_SUCCESS) + { + ssLOG_ERROR("System2Run failed with result: " << sys2Result); + return false; + } + outOutput.clear(); + + if(captureOutput) + { + do + { + uint32_t byteRead = 0; + outOutput.resize(outOutput.size() + 4096); + + sys2Result = + System2ReadFromOutput( &commandInfo, + const_cast(outOutput.data()) + outOutput.size() - 4096, + 4096, + &byteRead); + + outOutput.resize(outOutput.size() + byteRead); + } + while(sys2Result == SYSTEM2_RESULT_READ_NOT_FINISHED); + } + + sys2Result = System2GetCommandReturnValueSync(&commandInfo, &outReturnCode, false); + if(sys2Result != SYSTEM2_RESULT_SUCCESS) + { + ssLOG_ERROR("System2GetCommandReturnValueSync failed with result: " << sys2Result); + return false; + } + + if(captureOutput) + ssLOG_DEBUG("outOutput: \n" << outOutput.c_str()); + + if(outReturnCode != 0) + { + ssLOG_DEBUG("Failed when running command with return code: " << outReturnCode); + return false; + } + + return true; + } + + #if defined(_WIN32) + inline std::string GetWindowsError() + { + //Get the error message ID, if any. + DWORD errorMessageID = ::GetLastError(); + if(errorMessageID == 0) { + return std::string(); //No error message has been recorded + } + + LPSTR messageBuffer = nullptr; + + //Ask Win32 to give us the string version of that message ID. + //The parameters we pass in, tell Win32 to create the buffer that holds the message for us + //(because we don't yet know how long the message string will be). + size_t size = FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + errorMessageID, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&messageBuffer, + 0, + NULL); + + //Copy the error message into a std::string. + std::string message(messageBuffer, size); + + //Free the Win32's string's buffer. + LocalFree(messageBuffer); + + return message; + } + #endif + + template + inline bool HasValueFromPlatformMap(const std::unordered_map& map) + { + std::vector platformNames = runcpp2::GetPlatformNames(); + + for(int i = 0; i < platformNames.size(); ++i) + { + if(map.find(platformNames.at(i)) != map.end()) + return true; + } + + return false; + } + + template + inline const T* GetValueFromPlatformMap(const std::unordered_map& map) + { + std::vector platformNames = runcpp2::GetPlatformNames(); + + for(int i = 0; i < platformNames.size(); ++i) + { + if(map.find(platformNames.at(i)) != map.end()) + return &map.at(platformNames[i]); + } + + return nullptr; + } + + inline std::string GetFileExtensionWithoutVersion(const ghc::filesystem::path& path) + { + std::string filename = path.filename().string(); + bool inNumericPart = true; + int lastDotPos = filename.length(); + + for(int i = filename.length() - 1; i >= 0; --i) + { + if(filename[i] == '.') + { + if(!inNumericPart) + return filename.substr(i, lastDotPos - i); + + inNumericPart = true; + lastDotPos = i; + } + else if(!std::isdigit(filename[i])) + inNumericPart = false; + } + + return ""; + } +} + +#endif diff --git a/Src/runcpp2/ProfileHelper.cpp b/Src/runcpp2/ProfileHelper.hpp similarity index 67% rename from Src/runcpp2/ProfileHelper.cpp rename to Src/runcpp2/ProfileHelper.hpp index bc67889..e46c9f2 100644 --- a/Src/runcpp2/ProfileHelper.cpp +++ b/Src/runcpp2/ProfileHelper.hpp @@ -1,9 +1,22 @@ -#include "runcpp2/ProfileHelper.hpp" +#ifndef RUNCPP2_PROFILE_HELPER_HPP +#define RUNCPP2_PROFILE_HELPER_HPP + +#include "runcpp2/Data/Profile.hpp" +#include "runcpp2/Data/ScriptInfo.hpp" +#include "runcpp2/Data/ParseCommon.hpp" +#include "runcpp2/Data/StageInfo.hpp" + #include "runcpp2/PlatformUtil.hpp" #include "ghc/filesystem.hpp" #include "ssLogger/ssLog.hpp" +#include +#include +#include +#include + + namespace { bool IsProfileAvailableOnSystem(const runcpp2::Data::Profile& profile) @@ -170,21 +183,45 @@ namespace return true; } - std::vector GetAvailableProfiles( const std::vector& profiles, - const runcpp2::Data::ScriptInfo& scriptInfo, - const std::string& scriptPath) + DS::Result> + GetAvailableProfiles( const std::vector& profiles, + const runcpp2::Data::ScriptInfo& scriptInfo, + const std::string& scriptPath) { + std::string scriptExtension = ghc::filesystem::path(scriptPath).extension().string(); + bool isYaml = scriptExtension == ".yaml" || scriptExtension == ".yml"; + const ProfilesProcessPaths* sources = + isYaml ? runcpp2::GetValueFromPlatformMap(scriptInfo.OtherFilesToBeCompiled) : nullptr; + + if(isYaml && !sources) + return DS_ERROR_MSG("No valid source files found when using yaml as input"); + //Check which profile is available std::vector availableProfiles; for(int i = 0; i < profiles.size(); ++i) { - ssLOG_DEBUG("Checking profile: " << profiles.at(i).Name); + ssLOG_DEBUG("Checking profile: " << profiles[i].Name); - if( IsProfileAvailableOnSystem(profiles.at(i)) && - IsProfileValidForScript(profiles.at(i), scriptInfo, scriptPath)) + if(IsProfileAvailableOnSystem(profiles[i])) { - availableProfiles.push_back(i); + std::string checkPath; + if(isYaml) + { + //TODO: Maybe need to check all the paths to be explicit + if( sources->Paths.count(profiles[i].Name) > 0 && + !sources->Paths.at(profiles[i].Name).empty()) + { + checkPath = sources->Paths.at(profiles[i].Name)[0].string(); + } + else + continue; + } + else + checkPath = scriptPath; + + if(IsProfileValidForScript(profiles[i], scriptInfo, scriptPath)) + availableProfiles.push_back(i); } } @@ -192,36 +229,34 @@ namespace } } - -int runcpp2::GetPreferredProfileIndex( const std::string& scriptPath, - const Data::ScriptInfo& scriptInfo, - const std::vector& profiles, - const std::string& configPreferredProfile) +namespace runcpp2 { - std::vector availableProfiles = GetAvailableProfiles(profiles, scriptInfo, scriptPath); - - if(availableProfiles.empty()) + inline DS::Result GetPreferredProfileIndex(const std::string& scriptPath, + const Data::ScriptInfo& scriptInfo, + const std::vector& profiles, + const std::string& configPreferredProfile) { - ssLOG_ERROR("No compilers/linkers found that can be used for " << scriptPath); - return -1; - } - - int firstAvailableProfileIndex = -1; - - if(!configPreferredProfile.empty()) - { - for(int i = 0; i < availableProfiles.size(); ++i) + std::vector availableProfiles = GetAvailableProfiles( profiles, + scriptInfo, + scriptPath).DS_TRY(); + if(availableProfiles.empty()) + return DS_ERROR_MSG("No compilers/linkers found that can be used for " + scriptPath); + + int firstAvailableProfileIndex = availableProfiles[0]; + if(!configPreferredProfile.empty()) { - if(firstAvailableProfileIndex == -1) - firstAvailableProfileIndex = availableProfiles.at(i); - - if( profiles.at(availableProfiles.at(i)).Name == configPreferredProfile || - profiles.at(availableProfiles.at(i)).NameAliases.count(configPreferredProfile) > 0) + for(int i = 1; i < availableProfiles.size(); ++i) { - return availableProfiles.at(i); + if( profiles.at(availableProfiles[i]).Name == configPreferredProfile || + profiles.at(availableProfiles[i]).NameAliases.count(configPreferredProfile) > 0) + { + return availableProfiles.at(i); + } } } + + return firstAvailableProfileIndex; } - - return firstAvailableProfileIndex; } + +#endif diff --git a/Src/runcpp2/StringUtil.cpp b/Src/runcpp2/StringUtil.hpp similarity index 87% rename from Src/runcpp2/StringUtil.cpp rename to Src/runcpp2/StringUtil.hpp index 3f647a3..3921c64 100644 --- a/Src/runcpp2/StringUtil.cpp +++ b/Src/runcpp2/StringUtil.hpp @@ -1,8 +1,12 @@ -#include "runcpp2/StringUtil.hpp" +#ifndef RUNCPP2_STRING_UTIL_HPP +#define RUNCPP2_STRING_UTIL_HPP + +#include +#include namespace runcpp2 { - void TrimLeft(std::string& str) + inline void TrimLeft(std::string& str) { //Remove spaces from left while(!str.empty() && str.at(0) == ' ') @@ -12,8 +16,8 @@ namespace runcpp2 while(!str.empty() && str.at(0) == '\t') str.erase(0, 1); } - - void TrimRight(std::string& str) + + inline void TrimRight(std::string& str) { //Remove spaces from right while(!str.empty() && str.at(str.size() - 1) == ' ') @@ -23,9 +27,8 @@ namespace runcpp2 while(!str.empty() && str.at(str.size() - 1) == '\t') str.erase(str.size() - 1, 1); } - - //Trim string from both sides - void Trim(std::string& str) + + inline void Trim(std::string& str) { //Remove spaces from left TrimLeft(str); @@ -34,9 +37,9 @@ namespace runcpp2 TrimRight(str); } - void SplitString( const std::string& stringToSplit, - const std::string& splitter, - std::vector& outStrings) + inline void SplitString(const std::string& stringToSplit, + const std::string& splitter, + std::vector& outStrings) { if(splitter.empty()) { @@ -102,3 +105,5 @@ namespace runcpp2 outStrings.push_back(curOutString); } } + +#endif diff --git a/Src/runcpp2/main.cpp b/Src/runcpp2/main.cpp index 73ca4a5..c14ea33 100644 --- a/Src/runcpp2/main.cpp +++ b/Src/runcpp2/main.cpp @@ -1,10 +1,30 @@ +#include "runcpp2/Data/CmdOptions.hpp" +#include "runcpp2/Data/ParseCommon.hpp" +#include "runcpp2/Data/PipelineResult.hpp" +#include "runcpp2/Data/Profile.hpp" +#include "runcpp2/Data/ScriptInfo.hpp" + #include "runcpp2/ConfigParsing.hpp" #include "runcpp2/StringUtil.hpp" #include "runcpp2/runcpp2.hpp" #include "ssLogger/ssLog.hpp" - #include "ghc/filesystem.hpp" +#include "DSResult/DSResult.hpp" + +//NOTE: #include "runcpp2/DefaultYAMLs.c" at the end + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #define OUTPUT_ERROR() ssLOG_ERROR(DS_TMP_ERROR.ToString()); @@ -590,3 +610,4 @@ int main(int argc, char* argv[]) INTERNAL_RUNCPP2_SAFE_CATCH_RETURN(-1); } +#include "runcpp2/DefaultYAMLs.c" diff --git a/Src/runcpp2/runcpp2.cpp b/Src/runcpp2/runcpp2.cpp deleted file mode 100644 index c961daa..0000000 --- a/Src/runcpp2/runcpp2.cpp +++ /dev/null @@ -1,869 +0,0 @@ -#include "runcpp2/runcpp2.hpp" -#include "runcpp2/PipelineSteps.hpp" - -#include "runcpp2/ProfileHelper.hpp" -#include "runcpp2/CompilingLinking.hpp" -#include "runcpp2/ConfigParsing.hpp" -#include "runcpp2/PlatformUtil.hpp" -#include "runcpp2/Data/BuildTypeHelper.hpp" - -#include "ssLogger/ssLog.hpp" -#include "ghc/filesystem.hpp" - -#include -#include - -extern "C" const uint8_t DefaultScriptInfo[]; -extern "C" const size_t DefaultScriptInfo_size; - -//Use for SetDllDirectory -#if defined(_WIN32) - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif - #ifndef NOMINMAX - #define NOMINMAX - #endif - #include -#endif - -namespace -{ - DS::Result HasCompiledCache( const ghc::filesystem::path& scriptPath, - const std::vector& sourceFiles, - const ghc::filesystem::path& buildDir, - const runcpp2::Data::Profile& currentProfile, - runcpp2::IncludeManager& includeManager, - std::vector& outHasCache, - std::vector& outCachedObjectsFiles, - ghc::filesystem::file_time_type& outFinalObjectWriteTime, - ghc::filesystem::file_time_type& outFinalSourceWriteTime, - ghc::filesystem::file_time_type& outFinalIncludeWriteTime) - { - ssLOG_FUNC_INFO(); - - outHasCache.clear(); - outHasCache = std::vector(sourceFiles.size(), false); - - //TODO: Check compile flags - - const std::string* rawObjectExt = - runcpp2::GetValueFromPlatformMap(currentProfile.FilesTypes.ObjectLinkFile.Extension); - - DS_ASSERT_FALSE(rawObjectExt == nullptr); - - const std::string& objectExt = *rawObjectExt; - outFinalObjectWriteTime = ghc::filesystem::file_time_type(); - - std::error_code e; - for(int i = 0; i < sourceFiles.size(); ++i) - { - ghc::filesystem::path relativeSourcePath = - ghc::filesystem::relative(sourceFiles.at(i), scriptPath.parent_path(), e); - - if(e) - { - std::string retMsg = DS_STR("Failed to get relative path for ") + - sourceFiles.at(i).string() + "\n"; - retMsg += DS_STR("Failed with error: ") + e.message(); - return DS_ERROR_MSG(retMsg); - } - - ghc::filesystem::path currentObjectFilePath = buildDir / - relativeSourcePath.parent_path() / - relativeSourcePath.stem(); - currentObjectFilePath.concat(objectExt); - - ssLOG_DEBUG("Trying to use cache: " << sourceFiles.at(i).string()); - - //Check source file timestamp - ghc::filesystem::file_time_type currentSourceWriteTime = - ghc::filesystem::last_write_time(sourceFiles.at(i), e); - if(currentSourceWriteTime > outFinalSourceWriteTime) - outFinalSourceWriteTime = currentSourceWriteTime; - - //Check include record - bool outdatedIncludeRecord = false; - ghc::filesystem::file_time_type currentIncludeWriteTime; - { - std::vector cachedIncludes; - ghc::filesystem::file_time_type recordTime; - - if(includeManager.ReadIncludeRecord(sourceFiles.at(i), cachedIncludes, recordTime)) - { - if(includeManager.NeedsUpdate(sourceFiles.at(i), cachedIncludes, recordTime)) - outdatedIncludeRecord = true; - } - - if(outdatedIncludeRecord) - ssLOG_DEBUG("Needs to update include record for " << sourceFiles.at(i).string()); - - for(int j = 0; j < cachedIncludes.size(); ++j) - { - ghc::filesystem::file_time_type includeWriteTime = - ghc::filesystem::last_write_time(cachedIncludes.at(j), e); - - if(includeWriteTime > currentIncludeWriteTime) - currentIncludeWriteTime = includeWriteTime; - } - - if(currentIncludeWriteTime > outFinalIncludeWriteTime) - outFinalIncludeWriteTime = currentIncludeWriteTime; - } - - //Check object file timestamp - if(ghc::filesystem::exists(currentObjectFilePath, e)) - { - ghc::filesystem::file_time_type currentObjectWriteTime = - ghc::filesystem::last_write_time(currentObjectFilePath, e); - - bool useCache = currentObjectWriteTime > currentSourceWriteTime && - currentObjectWriteTime > currentIncludeWriteTime && - !outdatedIncludeRecord; - - ssLOG_DEBUG("currentObjectWriteTime: " << - currentObjectWriteTime.time_since_epoch().count()); - ssLOG_DEBUG("currentSourceWriteTime: " << - currentSourceWriteTime.time_since_epoch().count()); - ssLOG_DEBUG("currentIncludeWriteTime: " << - currentIncludeWriteTime.time_since_epoch().count()); - ssLOG_DEBUG("outdatedIncludeRecord: " << outdatedIncludeRecord); - - if(useCache) - { - ssLOG_INFO("Using cache for " << sourceFiles.at(i).string()); - outHasCache.at(i) = true; - outCachedObjectsFiles.push_back(currentObjectFilePath); - } - else - ssLOG_INFO("Cache invalidated for " << sourceFiles.at(i).string()); - - if(currentObjectWriteTime > outFinalObjectWriteTime) - outFinalObjectWriteTime = currentObjectWriteTime; - } - } - - return {}; - } - - bool HasOutputCache( const std::vector& sourceHasCache, - const ghc::filesystem::path& buildDir, - const runcpp2::Data::Profile& currentProfile, - const runcpp2::Data::ScriptInfo& scriptInfo, - const std::string& scriptName, - const std::vector& copiedBinariesPaths, - const ghc::filesystem::file_time_type& finalBinaryWriteTime, - bool& outOutputCache) - { - for(int i = 0; i < sourceHasCache.size(); ++i) - { - if(!sourceHasCache.at(i)) - { - outOutputCache = false; - return true; - } - } - - ghc::filesystem::file_time_type currentFinalBinaryWriteTime = finalBinaryWriteTime; - std::error_code e; - - for(int i = 0; i < copiedBinariesPaths.size(); ++i) - { - if(ghc::filesystem::exists(copiedBinariesPaths.at(i), e)) - { - ghc::filesystem::file_time_type lastBinaryWriteTime = - ghc::filesystem::last_write_time(copiedBinariesPaths.at(i), e); - - if(lastBinaryWriteTime > currentFinalBinaryWriteTime) - currentFinalBinaryWriteTime = lastBinaryWriteTime; - } - else - { - ssLOG_ERROR("Somehow copied binary path " << copiedBinariesPaths.at(i) << - " doesn't exist"); - outOutputCache = false; - return false; - } - } - - //Check if output is cached - std::vector outputPaths; - std::vector runnable; - - if(!runcpp2::Data::BuildTypeHelper::GetPossibleOutputPaths( buildDir, - scriptName, - currentProfile, - scriptInfo.CurrentBuildType, - outputPaths, - runnable)) - { - return false; - } - - int existCount = 0; - for(const ghc::filesystem::path& outputPath : outputPaths) - { - ssLOG_INFO("Trying to use output cache: " << outputPath.string()); - - if( ghc::filesystem::exists(outputPath, e) && - ghc::filesystem::file_size(outputPath, e) > 0) - { - ++existCount; - ghc::filesystem::file_time_type lastOutputBinary = - ghc::filesystem::last_write_time(outputPath, e); - - if(lastOutputBinary >= currentFinalBinaryWriteTime) - { - ssLOG_INFO("Using output cache for " << outputPath.string()); - continue; - } - else - { - ssLOG_INFO("Object files have more recent write time"); - ssLOG_DEBUG("lastOutputBinary: " << - lastOutputBinary.time_since_epoch().count()); - ssLOG_DEBUG("currentFinalBinaryWriteTime: " << - currentFinalBinaryWriteTime.time_since_epoch().count()); - outOutputCache = false; - return true; - } - } - else - ssLOG_INFO(outputPath.string() << " doesn't exist"); - } - - //TODO: Parsing ExpectedOutputFiles in the profile to see cache is valid or not - //NOTE: We don't know which ones are optionals, at least for now. - // If there's nothing, there's no cache for sure. - // If there's something, it's very likely we have it cached. - // Dumb logic, I know, but it works for now. - if(existCount == 0) - { - outOutputCache = false; - return true; - } - - ssLOG_INFO("Using output cache"); - outOutputCache = true; - return true; - } -} - -void runcpp2::GetDefaultScriptInfo(std::string& scriptInfo) -{ - scriptInfo = std::string( reinterpret_cast(DefaultScriptInfo), - DefaultScriptInfo_size); -} - -//NOTE: Mainly used for test to reduce spamminig -void runcpp2::SetLogLevel(const std::string& logLevel) -{ - if(logLevel == "Debug") - ssLOG_SET_CURRENT_THREAD_TARGET_LEVEL(ssLOG_LEVEL_DEBUG); - else if(logLevel == "Warning") - ssLOG_SET_CURRENT_THREAD_TARGET_LEVEL(ssLOG_LEVEL_WARNING); - else if(logLevel == "Error") - ssLOG_SET_CURRENT_THREAD_TARGET_LEVEL(ssLOG_LEVEL_ERROR); - else - ssLOG_ERROR("Invalid log level: " << logLevel); -} - -runcpp2::PipelineResult -runcpp2::CheckSourcesNeedUpdate( const std::string& scriptPath, - const std::vector& profiles, - const std::string& configPreferredProfile, - const Data::ScriptInfo& scriptInfo, - const std::unordered_map< CmdOptions, - std::string>& currentOptions, - const ghc::filesystem::file_time_type& prevFinalSourceWriteTime, - const ghc::filesystem::file_time_type& prevFinalIncludeWriteTime, - bool& outNeedsUpdate) -{ - INTERNAL_RUNCPP2_SAFE_START(); - ssLOG_FUNC_INFO(); - - //Validate inputs and get paths - ghc::filesystem::path absoluteScriptPath; - ghc::filesystem::path scriptDirectory; - std::string scriptName; - - ValidateInputs(scriptPath, profiles, absoluteScriptPath, scriptDirectory, scriptName) - .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); - return (PipelineResult)DS_TMP_ERROR.ErrorCode); - - //First check if script info file has changed - std::error_code e; - ghc::filesystem::path dedicatedYamlLoc = - scriptDirectory / ghc::filesystem::path(scriptName + ".yaml"); - - ghc::filesystem::file_time_type currentWriteTime; - if(ghc::filesystem::exists(dedicatedYamlLoc, e)) - currentWriteTime = ghc::filesystem::last_write_time(dedicatedYamlLoc, e); - else - currentWriteTime = ghc::filesystem::last_write_time(absoluteScriptPath, e); - - if(e) - { - ssLOG_ERROR("Failed to get write time for script info"); - return PipelineResult::UNEXPECTED_FAILURE; - } - - //If script info file is newer than last check, we need to update - if(currentWriteTime > scriptInfo.LastWriteTime) - { - outNeedsUpdate = true; - return PipelineResult::SUCCESS; - } - - //Initialize BuildsManager and IncludeManager - ghc::filesystem::path configDir = - GetConfigFilePath().DS_TRY_ACT( ssLOG_ERROR(DS_TMP_ERROR.ToString()); - return PipelineResult::INVALID_CONFIG_PATH); - - configDir = configDir.parent_path(); - if(!ghc::filesystem::is_directory(configDir, e)) - { - ssLOG_FATAL("Unexpected path for config directory: " << configDir.string()); - return PipelineResult::INVALID_CONFIG_PATH; - } - - ghc::filesystem::path buildDir; - BuildsManager buildsManager("/tmp"); - IncludeManager includeManager; - - const bool useLocalBuildDir = currentOptions.count(CmdOptions::LOCAL) > 0; - InitializeBuildDirectory( configDir, - absoluteScriptPath, - useLocalBuildDir, - buildsManager, - buildDir, - includeManager) - .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); - return (PipelineResult)DS_TMP_ERROR.ErrorCode); - - //Get profile and gather source files - const int profileIndex = GetPreferredProfileIndex( scriptPath, - scriptInfo, - profiles, - configPreferredProfile); - const Data::Profile& currentProfile = profiles.at(profileIndex); - - std::vector sourceFiles; - GatherSourceFiles(absoluteScriptPath, scriptInfo, currentProfile, sourceFiles) - .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); - return PipelineResult::UNEXPECTED_FAILURE); - - for(int i = 0; i < sourceFiles.size(); ++i) - ssLOG_DEBUG("sourceFiles.at(i).string(): " << sourceFiles.at(i).string()); - - std::vector sourceHasCache; - std::vector cachedObjectsFiles; - ghc::filesystem::file_time_type finalObjectWriteTime; - ghc::filesystem::file_time_type finalSourceWriteTime; - ghc::filesystem::file_time_type finalIncludeWriteTime; - - HasCompiledCache( absoluteScriptPath, - sourceFiles, - buildDir, - currentProfile, - includeManager, - sourceHasCache, - cachedObjectsFiles, - finalObjectWriteTime, - finalSourceWriteTime, - finalIncludeWriteTime).DS_TRY_ACT( ssLOG_ERROR(DS_TMP_ERROR.ToString()); - //TODO: Maybe add a pipeline result for this? - return PipelineResult::UNEXPECTED_FAILURE); - - if( finalSourceWriteTime > prevFinalSourceWriteTime || - finalIncludeWriteTime > prevFinalIncludeWriteTime) - { - outNeedsUpdate = true; - } - else - outNeedsUpdate = false; - - return PipelineResult::SUCCESS; - - INTERNAL_RUNCPP2_SAFE_CATCH_RETURN(PipelineResult::UNEXPECTED_FAILURE); -} - -runcpp2::PipelineResult -runcpp2::StartPipeline( const std::string& scriptPath, - const std::vector& profiles, - const std::string& configPreferredProfile, - const std::unordered_map& currentOptions, - const std::vector& runArgs, - const Data::ScriptInfo* lastScriptInfo, - const std::string& buildOutputDir, - Data::ScriptInfo& outScriptInfo, - ghc::filesystem::file_time_type& outFinalSourceWriteTime, - ghc::filesystem::file_time_type& outFinalIncludeWriteTime, - int& returnStatus) -{ - INTERNAL_RUNCPP2_SAFE_START(); - ssLOG_FUNC_INFO(); - - //Validate inputs and get paths - ghc::filesystem::path absoluteScriptPath; - ghc::filesystem::path scriptDirectory; - std::string scriptName; - ValidateInputs(scriptPath, profiles, absoluteScriptPath, scriptDirectory, scriptName) - .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); - return (PipelineResult)DS_TMP_ERROR.ErrorCode); - - ghc::filesystem::path configDir = - GetConfigFilePath().DS_TRY_ACT( ssLOG_ERROR(DS_TMP_ERROR.ToString()); - return PipelineResult::INVALID_CONFIG_PATH); - ghc::filesystem::path buildDir; - - //Parse script info - Data::ScriptInfo scriptInfo; - ParseAndValidateScriptInfo( absoluteScriptPath, - scriptDirectory, - scriptName, - currentOptions.count(CmdOptions::EXECUTABLE) > 0, - scriptInfo) - .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); - return (PipelineResult)DS_TMP_ERROR.ErrorCode); - - //Parse and get the config directory - { - std::error_code e; - if(ghc::filesystem::is_directory(configDir, e)) - { - ssLOG_FATAL("Unexpected path for config file: " << configDir.string()); - return PipelineResult::INVALID_CONFIG_PATH; - } - - configDir = configDir.parent_path(); - if(!ghc::filesystem::is_directory(configDir, e)) - { - ssLOG_FATAL("Unexpected path for config directory: " << configDir.string()); - return PipelineResult::INVALID_CONFIG_PATH; - } - } - - int profileIndex = GetPreferredProfileIndex(absoluteScriptPath, - scriptInfo, - profiles, - configPreferredProfile); - - if(profileIndex == -1) - return PipelineResult::NO_AVAILABLE_PROFILE; - - //Parsing the script, setting up dependencies, compiling and linking - std::vector filesToCopyPaths; - { - const int maxThreads = - currentOptions.count(CmdOptions::THREADS) ? - strtol(currentOptions.at(CmdOptions::THREADS).c_str(), nullptr, 10) : - 8; - if(maxThreads == 0) - { - ssLOG_ERROR("Invalid number of threads passed in"); - return PipelineResult::INVALID_OPTION; - } - - BuildsManager buildsManager("/tmp"); - IncludeManager includeManager; - InitializeBuildDirectory( configDir, - absoluteScriptPath, - currentOptions.count(CmdOptions::LOCAL) > 0, - buildsManager, - buildDir, - includeManager) - .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); - return (PipelineResult)DS_TMP_ERROR.ErrorCode); - - //Handle cleanup command if present - if(currentOptions.count(CmdOptions::CLEANUP) > 0) - { - HandleCleanup( scriptInfo, - profiles.at(profileIndex), - scriptDirectory, - buildDir, - absoluteScriptPath, - buildsManager) - .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); - return (PipelineResult)DS_TMP_ERROR.ErrorCode); - } - - //Resolve imports - ResolveScriptImports(scriptInfo, absoluteScriptPath, buildDir) - .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); - return (PipelineResult)DS_TMP_ERROR.ErrorCode); - - //Check if script info has changed if provided and run setup if needed - bool recompileNeeded = false; - bool relinkNeeded = false; - std::vector changedDependencies; - - CheckScriptInfoChanges( buildDir, - scriptInfo, - profiles.at(profileIndex), - absoluteScriptPath, - lastScriptInfo, - maxThreads, - recompileNeeded, - relinkNeeded, - changedDependencies) - .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); - return (PipelineResult)DS_TMP_ERROR.ErrorCode); - - //if(!lastScriptInfo || recompileNeeded || !changedDependencies.empty() || relinkNeeded) - outScriptInfo = scriptInfo; - - std::vector gatheredBinariesPaths; - - //Process Dependencies - std::vector availableDependencies; - ProcessDependencies(scriptInfo, - profiles.at(profileIndex), - absoluteScriptPath, - buildDir, - currentOptions, - changedDependencies, - maxThreads, - availableDependencies, - gatheredBinariesPaths) - .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); - return (PipelineResult)DS_TMP_ERROR.ErrorCode); - - if(currentOptions.count(CmdOptions::RESET_DEPENDENCIES) > 0) - return PipelineResult::SUCCESS; - - //Get all the files we are trying to compile - std::vector sourceFiles; - GatherSourceFiles( absoluteScriptPath, - scriptInfo, - profiles.at(profileIndex), - sourceFiles) - .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); - return PipelineResult::INVALID_SCRIPT_INFO); - - //Get all include paths - std::vector includePaths; - GatherIncludePaths( scriptDirectory, - scriptInfo, - profiles.at(profileIndex), - availableDependencies, - includePaths) - .DS_TRY_ACT(DS_TMP_ERROR.Message += "Failed to gather include paths"; - ssLOG_ERROR(DS_TMP_ERROR.ToString()); - return PipelineResult::INVALID_SCRIPT_INFO); - - //Check if we have already compiled before. - std::vector sourceHasCache; - std::vector cachedObjectsFiles; - ghc::filesystem::file_time_type finalObjectWriteTime; - - if(currentOptions.count(CmdOptions::RESET_CACHE) > 0 || recompileNeeded) - sourceHasCache = std::vector(sourceFiles.size(), false); - else - { - HasCompiledCache( absoluteScriptPath, - sourceFiles, - buildDir, - profiles.at(profileIndex), - includeManager, - sourceHasCache, - cachedObjectsFiles, - finalObjectWriteTime, - outFinalSourceWriteTime, - outFinalIncludeWriteTime) - .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); - //TODO: Maybe add a pipeline result for this? - return PipelineResult::UNEXPECTED_FAILURE); - } - - runcpp2::SourceIncludeMap sourcesIncludes; - runcpp2::GatherFilesIncludes(sourceFiles, sourceHasCache, includePaths, sourcesIncludes) - .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); - return PipelineResult::UNEXPECTED_FAILURE); - - for(int i = 0; i < sourceFiles.size(); ++i) - { - if(!sourceHasCache.at(i)) - { - ssLOG_DEBUG("Updating include record for " << sourceFiles.at(i).string()); - if(sourcesIncludes.count(sourceFiles.at(i)) == 0) - { - ssLOG_WARNING("Includes not gathered for " << sourceFiles.at(i).string()); - continue; - } - - bool writeResult = includeManager.WriteIncludeRecord - ( - sourceFiles.at(i), - sourcesIncludes.at(sourceFiles.at(i)) - ); - if(!writeResult) - { - ssLOG_ERROR("Failed to write include record for " << sourceFiles.at(i).string()); - return PipelineResult::UNEXPECTED_FAILURE; - } - } - else - ssLOG_DEBUG("Include record for " << sourceFiles.at(i).string() << " is up to date"); - } - - std::vector linkFilesPaths; - SeparateDependencyFiles(profiles.at(profileIndex).FilesTypes, - gatheredBinariesPaths, - linkFilesPaths, - filesToCopyPaths); - - std::error_code e; - - //Get finalBinaryWriteTime by combining final object and dependencies write times - ghc::filesystem::file_time_type finalBinaryWriteTime = finalObjectWriteTime; - for(int i = 0; i < linkFilesPaths.size(); ++i) - { - if(!ghc::filesystem::exists(linkFilesPaths.at(i), e)) - { - ssLOG_ERROR(linkFilesPaths.at(i) << " reported as cached but doesn't exist"); - return PipelineResult::UNEXPECTED_FAILURE; - } - - ghc::filesystem::file_time_type lastWriteTime = - ghc::filesystem::last_write_time(linkFilesPaths.at(i), e); - - if(lastWriteTime > finalBinaryWriteTime) - finalBinaryWriteTime = lastWriteTime; - } - - //Run PreBuild commands before compilation - HandlePreBuild(scriptInfo, profiles.at(profileIndex), buildDir) - .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); - return (PipelineResult)DS_TMP_ERROR.ErrorCode); - - //Compiling/Linking - bool outputCache = false; - if(!HasOutputCache( sourceHasCache, - buildDir, - profiles.at(profileIndex), - scriptInfo, - scriptName, - linkFilesPaths, - finalBinaryWriteTime, - outputCache)) - { - ssLOG_WARNING("Error detected when trying to use output cache. A cleanup is recommended"); - //return PipelineResult::UNEXPECTED_FAILURE; - } - - if(!outputCache || relinkNeeded) - { - for(int i = 0; i < cachedObjectsFiles.size(); ++i) - linkFilesPaths.push_back(cachedObjectsFiles.at(i)); - - //TODO: Compile and link for watch as well. Load library as well - if(currentOptions.count(CmdOptions::WATCH) > 0) - { - CompileScriptOnly( buildDir, - absoluteScriptPath, - sourceFiles, - sourceHasCache, - includePaths, - scriptInfo, - availableDependencies, - profiles.at(profileIndex), - maxThreads) - .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); - return PipelineResult::COMPILE_LINK_FAILED); - - return PipelineResult::SUCCESS; - } - else - { - CompileAndLinkScript( buildDir, - absoluteScriptPath, - ghc::filesystem::path(absoluteScriptPath).stem(), - sourceFiles, - sourceHasCache, - includePaths, - scriptInfo, - availableDependencies, - profiles.at(profileIndex), - linkFilesPaths, - maxThreads) - .DS_TRY_ACT(DS_TMP_ERROR.Message += "\nFailed to compile or link script."; - ssLOG_ERROR(DS_TMP_ERROR.ToString()); - return PipelineResult::COMPILE_LINK_FAILED); - } - } - } - - //Trigger post build and run the script if needed - { - std::vector targets; - ghc::filesystem::path runnableTarget; - GetBuiltTargetPaths(buildDir, - scriptName, - profiles.at(profileIndex), - currentOptions, - scriptInfo, - targets, - &runnableTarget) - .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); - return (PipelineResult)DS_TMP_ERROR.ErrorCode); - - if(targets.empty()) - { - ssLOG_WARNING("No target files found"); - return PipelineResult::SUCCESS; - } - - //Copy files to build directory - std::vector copiedPaths; - if(!buildOutputDir.empty()) - { - buildDir = buildOutputDir; - //filesToCopyPaths.push_back(runnableTarget.string()); - for(const ghc::filesystem::path& target : targets) - filesToCopyPaths.push_back(target.string()); - } - - CopyFiles(buildDir, filesToCopyPaths, copiedPaths) - .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); - ssLOG_ERROR("Failed to copy binaries before running the script"); - return PipelineResult::UNEXPECTED_FAILURE); - - //Run PostBuild commands after successful compilation - HandlePostBuild(scriptInfo, profiles.at(profileIndex), buildDir.string()) - .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); - return (PipelineResult)DS_TMP_ERROR.ErrorCode); - - //Don't run if we are just watching, reseting source cache or building - if( currentOptions.count(CmdOptions::WATCH) > 0 || - currentOptions.count(CmdOptions::RESET_CACHE) > 0 || - currentOptions.count(CmdOptions::BUILD) > 0) - { - return PipelineResult::SUCCESS; - } - - //Run otherwise - ssLOG_INFO("Running script..."); - RunCompiledOutput( runnableTarget, - absoluteScriptPath, - scriptInfo, - runArgs, - currentOptions, - returnStatus) - .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); - return (PipelineResult)DS_TMP_ERROR.ErrorCode); - } - - return PipelineResult::SUCCESS; - - INTERNAL_RUNCPP2_SAFE_CATCH_RETURN(PipelineResult::UNEXPECTED_FAILURE); -} - -std::string runcpp2::PipelineResultToString(PipelineResult result) -{ - static_assert(static_cast(PipelineResult::COUNT) == 13, "PipelineResult enum has changed"); - - switch(result) - { - case PipelineResult::UNEXPECTED_FAILURE: - return "UNEXPECTED_FAILURE"; - case PipelineResult::SUCCESS: - return "SUCCESS"; - case PipelineResult::EMPTY_PROFILES: - return "EMPTY_PROFILES"; - case PipelineResult::INVALID_SCRIPT_PATH: - return "INVALID_SCRIPT_PATH"; - case PipelineResult::INVALID_CONFIG_PATH: - return "INVALID_CONFIG_PATH"; - case PipelineResult::INVALID_BUILD_DIR: - return "INVALID_BUILD_DIR"; - case PipelineResult::INVALID_SCRIPT_INFO: - return "INVALID_SCRIPT_INFO"; - case PipelineResult::NO_AVAILABLE_PROFILE: - return "NO_AVAILABLE_PROFILE"; - case PipelineResult::DEPENDENCIES_FAILED: - return "DEPENDENCIES_FAILED"; - case PipelineResult::COMPILE_LINK_FAILED: - return "COMPILE_LINK_FAILED"; - case PipelineResult::INVALID_PROFILE: - return "INVALID_PROFILE"; - case PipelineResult::RUN_SCRIPT_FAILED: - return "RUN_SCRIPT_FAILED"; - case PipelineResult::INVALID_OPTION: - return "INVALID_OPTION"; - default: - return "UNKNOWN_PIPELINE_RESULT"; - } -} - -bool runcpp2::DownloadTutorial(char* runcppPath) -{ - std::string dummy; - int returnCode = 0; - - std::string input; - while(true) - { - input.clear(); - ssLOG_BASE( "This will download InteractiveTutorial.cpp from github to current directory. " - "Continue? [Y/n]"); - - if(!std::getline(std::cin, input)) - { - ssLOG_ERROR("IO Error when trying to get cin"); - return false; - } - - if(!input.empty()) - { - if(input == "y" || input == "Y") - break; - else if(input == "n" || input == "N") - { - ssLOG_BASE("Not continuing"); - return 0; - } - else - ssLOG_BASE("Please only answer with y or n"); - } - else - break; - } - - std::string targetBranch = RUNCPP2_VERSION; - size_t dashPos = targetBranch.find("-"); - if(dashPos != std::string::npos) - targetBranch = targetBranch.substr(0, dashPos); - - #ifdef _WIN32 - if(!RunCommand( "powershell -Command \"" - "Invoke-WebRequest https://github.com/Neko-Box-Coder/runcpp2/raw/" - "refs/tags/" + targetBranch + "/Examples/InteractiveTutorial.cpp " - "-OutFile InteractiveTutorial.cpp\"", - false, - "./", - dummy, - returnCode)) - { - return false; - } - #else - if(!RunCommand( "curl -L -o InteractiveTutorial.cpp " - "https://github.com/Neko-Box-Coder/runcpp2/raw/refs/tags/" + - targetBranch + "/Examples/InteractiveTutorial.cpp", - false, - "./", - dummy, - returnCode)) - { - return false; - } - #endif - - ssLOG_INFO("targetBranch: " << targetBranch); - ssLOG_BASE("Downloaded InteractiveTutorial.cpp from github."); - ssLOG_BASE("Do `" << runcppPath << " InteractiveTutorial.cpp to start the tutorial."); - - return true; -} diff --git a/Src/runcpp2/runcpp2.hpp b/Src/runcpp2/runcpp2.hpp new file mode 100644 index 0000000..8decea0 --- /dev/null +++ b/Src/runcpp2/runcpp2.hpp @@ -0,0 +1,919 @@ +#ifndef RUNCPP2_RUNCPP2_HPP +#define RUNCPP2_RUNCPP2_HPP + +#include "runcpp2/Data/BuildTypeHelper.hpp" +#include "runcpp2/Data/CmdOptions.hpp" +#include "runcpp2/Data/Profile.hpp" +#include "runcpp2/Data/ScriptInfo.hpp" +#include "runcpp2/Data/PipelineResult.hpp" +#include "runcpp2/Data/FileProperties.hpp" +#include "runcpp2/Data/FilesTypesInfo.hpp" +#include "runcpp2/Data/ParseCommon.hpp" + +#include "runcpp2/PipelineSteps.hpp" +#include "runcpp2/ProfileHelper.hpp" +#include "runcpp2/CompilingLinking.hpp" +#include "runcpp2/ConfigParsing.hpp" +#include "runcpp2/PlatformUtil.hpp" +#include "runcpp2/BuildsManager.hpp" +#include "runcpp2/IncludeManager.hpp" + +#include "ssLogger/ssLog.hpp" +#include "ghc/filesystem.hpp" +#include "DSResult/DSResult.hpp" + +#include +#include +#include +#include +#include +#include +#include + +//NOTE: #include "runcpp2/LibYamlImpl.cpp" at the end + +//Use for SetDllDirectory +#if defined(_WIN32) + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include +#endif + +extern const uint8_t DefaultScriptInfo[]; +extern const size_t DefaultScriptInfo_size; + +namespace runcpp2 { namespace Data { struct DependencyInfo; } } + +namespace +{ + DS::Result HasCompiledCache( const ghc::filesystem::path& scriptPath, + const std::vector& sourceFiles, + const ghc::filesystem::path& buildDir, + const runcpp2::Data::Profile& currentProfile, + runcpp2::IncludeManager& includeManager, + std::vector& outHasCache, + std::vector& outCachedObjectsFiles, + ghc::filesystem::file_time_type& outFinalObjectWriteTime, + ghc::filesystem::file_time_type& outFinalSourceWriteTime, + ghc::filesystem::file_time_type& outFinalIncludeWriteTime) + { + ssLOG_FUNC_INFO(); + + outHasCache.clear(); + outHasCache = std::vector(sourceFiles.size(), false); + + //TODO: Check compile flags + + const std::string* rawObjectExt = + runcpp2::GetValueFromPlatformMap(currentProfile.FilesTypes.ObjectLinkFile.Extension); + + DS_ASSERT_FALSE(rawObjectExt == nullptr); + + const std::string& objectExt = *rawObjectExt; + outFinalObjectWriteTime = ghc::filesystem::file_time_type(); + + std::error_code e; + for(int i = 0; i < sourceFiles.size(); ++i) + { + ghc::filesystem::path relativeSourcePath = + ghc::filesystem::relative(sourceFiles.at(i), scriptPath.parent_path(), e); + + if(e) + { + std::string retMsg = DS_STR("Failed to get relative path for ") + + sourceFiles.at(i).string() + "\n"; + retMsg += DS_STR("Failed with error: ") + e.message(); + return DS_ERROR_MSG(retMsg); + } + + ghc::filesystem::path currentObjectFilePath = buildDir / + relativeSourcePath.parent_path() / + relativeSourcePath.stem(); + currentObjectFilePath.concat(objectExt); + + ssLOG_DEBUG("Trying to use cache: " << sourceFiles.at(i).string()); + + //Check source file timestamp + ghc::filesystem::file_time_type currentSourceWriteTime = + ghc::filesystem::last_write_time(sourceFiles.at(i), e); + if(currentSourceWriteTime > outFinalSourceWriteTime) + outFinalSourceWriteTime = currentSourceWriteTime; + + //Check include record + bool outdatedIncludeRecord = false; + ghc::filesystem::file_time_type currentIncludeWriteTime; + { + std::vector cachedIncludes; + ghc::filesystem::file_time_type recordTime; + + if(includeManager.ReadIncludeRecord(sourceFiles.at(i), cachedIncludes, recordTime)) + { + if(includeManager.NeedsUpdate(sourceFiles.at(i), cachedIncludes, recordTime)) + outdatedIncludeRecord = true; + } + + if(outdatedIncludeRecord) + ssLOG_DEBUG("Needs to update include record for " << sourceFiles.at(i).string()); + + for(int j = 0; j < cachedIncludes.size(); ++j) + { + ghc::filesystem::file_time_type includeWriteTime = + ghc::filesystem::last_write_time(cachedIncludes.at(j), e); + + if(includeWriteTime > currentIncludeWriteTime) + currentIncludeWriteTime = includeWriteTime; + } + + if(currentIncludeWriteTime > outFinalIncludeWriteTime) + outFinalIncludeWriteTime = currentIncludeWriteTime; + } + + //Check object file timestamp + if(ghc::filesystem::exists(currentObjectFilePath, e)) + { + ghc::filesystem::file_time_type currentObjectWriteTime = + ghc::filesystem::last_write_time(currentObjectFilePath, e); + + bool useCache = currentObjectWriteTime > currentSourceWriteTime && + currentObjectWriteTime > currentIncludeWriteTime && + !outdatedIncludeRecord; + + ssLOG_DEBUG("currentObjectWriteTime: " << + currentObjectWriteTime.time_since_epoch().count()); + ssLOG_DEBUG("currentSourceWriteTime: " << + currentSourceWriteTime.time_since_epoch().count()); + ssLOG_DEBUG("currentIncludeWriteTime: " << + currentIncludeWriteTime.time_since_epoch().count()); + ssLOG_DEBUG("outdatedIncludeRecord: " << outdatedIncludeRecord); + + if(useCache) + { + ssLOG_INFO("Using cache for " << sourceFiles.at(i).string()); + outHasCache.at(i) = true; + outCachedObjectsFiles.push_back(currentObjectFilePath); + } + else + ssLOG_INFO("Cache invalidated for " << sourceFiles.at(i).string()); + + if(currentObjectWriteTime > outFinalObjectWriteTime) + outFinalObjectWriteTime = currentObjectWriteTime; + } + } + + return {}; + } + + bool HasOutputCache( const std::vector& sourceHasCache, + const ghc::filesystem::path& buildDir, + const runcpp2::Data::Profile& currentProfile, + const runcpp2::Data::ScriptInfo& scriptInfo, + const std::string& scriptName, + const std::vector& copiedBinariesPaths, + const ghc::filesystem::file_time_type& finalBinaryWriteTime, + bool& outOutputCache) + { + for(int i = 0; i < sourceHasCache.size(); ++i) + { + if(!sourceHasCache.at(i)) + { + outOutputCache = false; + return true; + } + } + + ghc::filesystem::file_time_type currentFinalBinaryWriteTime = finalBinaryWriteTime; + std::error_code e; + + for(int i = 0; i < copiedBinariesPaths.size(); ++i) + { + if(ghc::filesystem::exists(copiedBinariesPaths.at(i), e)) + { + ghc::filesystem::file_time_type lastBinaryWriteTime = + ghc::filesystem::last_write_time(copiedBinariesPaths.at(i), e); + + if(lastBinaryWriteTime > currentFinalBinaryWriteTime) + currentFinalBinaryWriteTime = lastBinaryWriteTime; + } + else + { + ssLOG_ERROR("Somehow copied binary path " << copiedBinariesPaths.at(i) << + " doesn't exist"); + outOutputCache = false; + return false; + } + } + + //Check if output is cached + std::vector outputPaths; + std::vector runnable; + + if(!runcpp2::Data::BuildTypeHelper::GetPossibleOutputPaths( buildDir, + scriptName, + currentProfile, + scriptInfo.CurrentBuildType, + outputPaths, + runnable)) + { + return false; + } + + int existCount = 0; + for(const ghc::filesystem::path& outputPath : outputPaths) + { + ssLOG_INFO("Trying to use output cache: " << outputPath.string()); + + if( ghc::filesystem::exists(outputPath, e) && + ghc::filesystem::file_size(outputPath, e) > 0) + { + ++existCount; + ghc::filesystem::file_time_type lastOutputBinary = + ghc::filesystem::last_write_time(outputPath, e); + + if(lastOutputBinary >= currentFinalBinaryWriteTime) + { + ssLOG_INFO("Using output cache for " << outputPath.string()); + continue; + } + else + { + ssLOG_INFO("Object files have more recent write time"); + ssLOG_DEBUG("lastOutputBinary: " << + lastOutputBinary.time_since_epoch().count()); + ssLOG_DEBUG("currentFinalBinaryWriteTime: " << + currentFinalBinaryWriteTime.time_since_epoch().count()); + outOutputCache = false; + return true; + } + } + else + ssLOG_INFO(outputPath.string() << " doesn't exist"); + } + + //TODO: Parsing ExpectedOutputFiles in the profile to see cache is valid or not + //NOTE: We don't know which ones are optionals, at least for now. + // If there's nothing, there's no cache for sure. + // If there's something, it's very likely we have it cached. + // Dumb logic, I know, but it works for now. + if(existCount == 0) + { + outOutputCache = false; + return true; + } + + ssLOG_INFO("Using output cache"); + outOutputCache = true; + return true; + } +} + +namespace runcpp2 +{ + struct OptionInfo + { + CmdOptions Option; + bool ValueExists; + std::string Value; + + inline OptionInfo( CmdOptions option, + bool valueExists = false, + const std::string& value = "") : Option(option), + ValueExists(valueExists), + Value(value) + {} + }; + + inline void GetDefaultScriptInfo(std::string& scriptInfo) + { + scriptInfo = std::string( reinterpret_cast(DefaultScriptInfo), + DefaultScriptInfo_size); + } + + //NOTE: Mainly used for test to reduce spamminig + inline void SetLogLevel(const std::string& logLevel) + { + if(logLevel == "Debug") + ssLOG_SET_CURRENT_THREAD_TARGET_LEVEL(ssLOG_LEVEL_DEBUG); + else if(logLevel == "Warning") + ssLOG_SET_CURRENT_THREAD_TARGET_LEVEL(ssLOG_LEVEL_WARNING); + else if(logLevel == "Error") + ssLOG_SET_CURRENT_THREAD_TARGET_LEVEL(ssLOG_LEVEL_ERROR); + else + ssLOG_ERROR("Invalid log level: " << logLevel); + } + + //NOTE: This should be run after running StartPipeline first + inline PipelineResult + CheckSourcesNeedUpdate( const std::string& scriptPath, + const std::vector& profiles, + const std::string& configPreferredProfile, + const Data::ScriptInfo& scriptInfo, + const std::unordered_map< CmdOptions, + std::string>& currentOptions, + const ghc::filesystem::file_time_type& prevFinalSourceWriteTime, + const ghc::filesystem::file_time_type& prevFinalIncludeWriteTime, + bool& outNeedsUpdate) + { + INTERNAL_RUNCPP2_SAFE_START(); + ssLOG_FUNC_INFO(); + + //Validate inputs and get paths + ghc::filesystem::path absoluteScriptPath; + ghc::filesystem::path scriptDirectory; + std::string scriptName; + + ValidateInputs(scriptPath, profiles, absoluteScriptPath, scriptDirectory, scriptName) + .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); + return (PipelineResult)DS_TMP_ERROR.ErrorCode); + + //First check if script info file has changed + std::error_code e; + ghc::filesystem::path dedicatedYamlLoc = + scriptDirectory / ghc::filesystem::path(scriptName + ".yaml"); + + ghc::filesystem::file_time_type currentWriteTime; + if(ghc::filesystem::exists(dedicatedYamlLoc, e)) + currentWriteTime = ghc::filesystem::last_write_time(dedicatedYamlLoc, e); + else + currentWriteTime = ghc::filesystem::last_write_time(absoluteScriptPath, e); + + if(e) + { + ssLOG_ERROR("Failed to get write time for script info"); + return PipelineResult::UNEXPECTED_FAILURE; + } + + //If script info file is newer than last check, we need to update + if(currentWriteTime > scriptInfo.LastWriteTime) + { + outNeedsUpdate = true; + return PipelineResult::SUCCESS; + } + + //Initialize BuildsManager and IncludeManager + ghc::filesystem::path configDir = + GetConfigFilePath().DS_TRY_ACT( ssLOG_ERROR(DS_TMP_ERROR.ToString()); + return PipelineResult::INVALID_CONFIG_PATH); + + configDir = configDir.parent_path(); + if(!ghc::filesystem::is_directory(configDir, e)) + { + ssLOG_FATAL("Unexpected path for config directory: " << configDir.string()); + return PipelineResult::INVALID_CONFIG_PATH; + } + + ghc::filesystem::path buildDir; + BuildsManager buildsManager("/tmp"); + IncludeManager includeManager; + + const bool useLocalBuildDir = currentOptions.count(CmdOptions::LOCAL) > 0; + InitializeBuildDirectory( configDir, + absoluteScriptPath, + useLocalBuildDir, + buildsManager, + buildDir, + includeManager) + .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); + return (PipelineResult)DS_TMP_ERROR.ErrorCode); + + //Get profile and gather source files + const int profileIndex = GetPreferredProfileIndex( scriptPath, + scriptInfo, + profiles, + configPreferredProfile) + .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); + return PipelineResult::NO_AVAILABLE_PROFILE); + + const Data::Profile& currentProfile = profiles.at(profileIndex); + + std::vector sourceFiles; + GatherSourceFiles(absoluteScriptPath, scriptInfo, currentProfile, sourceFiles) + .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); + return PipelineResult::UNEXPECTED_FAILURE); + + for(int i = 0; i < sourceFiles.size(); ++i) + ssLOG_DEBUG("sourceFiles.at(i).string(): " << sourceFiles.at(i).string()); + + std::vector sourceHasCache; + std::vector cachedObjectsFiles; + ghc::filesystem::file_time_type finalObjectWriteTime; + ghc::filesystem::file_time_type finalSourceWriteTime; + ghc::filesystem::file_time_type finalIncludeWriteTime; + + HasCompiledCache( absoluteScriptPath, + sourceFiles, + buildDir, + currentProfile, + includeManager, + sourceHasCache, + cachedObjectsFiles, + finalObjectWriteTime, + finalSourceWriteTime, + finalIncludeWriteTime) + .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); + //TODO: Maybe add a pipeline result for this? + return PipelineResult::UNEXPECTED_FAILURE); + + if( finalSourceWriteTime > prevFinalSourceWriteTime || + finalIncludeWriteTime > prevFinalIncludeWriteTime) + { + outNeedsUpdate = true; + } + else + outNeedsUpdate = false; + + return PipelineResult::SUCCESS; + + INTERNAL_RUNCPP2_SAFE_CATCH_RETURN(PipelineResult::UNEXPECTED_FAILURE); + } + + inline PipelineResult StartPipeline(const std::string& scriptPath, + const std::vector& profiles, + const std::string& configPreferredProfile, + const std::unordered_map< CmdOptions, + std::string>& currentOptions, + const std::vector& runArgs, + const Data::ScriptInfo* lastScriptInfo, + const std::string& buildOutputDir, + Data::ScriptInfo& outScriptInfo, + ghc::filesystem::file_time_type& outFinalSourceWriteTime, + ghc::filesystem::file_time_type& outFinalIncludeWriteTime, + int& returnStatus) + { + INTERNAL_RUNCPP2_SAFE_START(); + ssLOG_FUNC_INFO(); + + //Validate inputs and get paths + ghc::filesystem::path absoluteScriptPath; + ghc::filesystem::path scriptDirectory; + std::string scriptName; + ValidateInputs(scriptPath, profiles, absoluteScriptPath, scriptDirectory, scriptName) + .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); + return (PipelineResult)DS_TMP_ERROR.ErrorCode); + + ghc::filesystem::path configDir = + GetConfigFilePath().DS_TRY_ACT( ssLOG_ERROR(DS_TMP_ERROR.ToString()); + return PipelineResult::INVALID_CONFIG_PATH); + ghc::filesystem::path buildDir; + + //Parse script info + Data::ScriptInfo scriptInfo; + ParseAndValidateScriptInfo( absoluteScriptPath, + scriptDirectory, + scriptName, + currentOptions.count(CmdOptions::EXECUTABLE) > 0, + scriptInfo) + .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); + return (PipelineResult)DS_TMP_ERROR.ErrorCode); + + //Parse and get the config directory + { + std::error_code e; + if(ghc::filesystem::is_directory(configDir, e)) + { + ssLOG_FATAL("Unexpected path for config file: " << configDir.string()); + return PipelineResult::INVALID_CONFIG_PATH; + } + + configDir = configDir.parent_path(); + if(!ghc::filesystem::is_directory(configDir, e)) + { + ssLOG_FATAL("Unexpected path for config directory: " << configDir.string()); + return PipelineResult::INVALID_CONFIG_PATH; + } + } + + int profileIndex = GetPreferredProfileIndex( absoluteScriptPath, + scriptInfo, + profiles, + configPreferredProfile) + .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); + return PipelineResult::NO_AVAILABLE_PROFILE);; + + //Parsing the script, setting up dependencies, compiling and linking + std::vector filesToCopyPaths; + { + const int maxThreads = + currentOptions.count(CmdOptions::THREADS) ? + strtol(currentOptions.at(CmdOptions::THREADS).c_str(), nullptr, 10) : + 8; + if(maxThreads == 0) + { + ssLOG_ERROR("Invalid number of threads passed in"); + return PipelineResult::INVALID_OPTION; + } + + BuildsManager buildsManager("/tmp"); + IncludeManager includeManager; + InitializeBuildDirectory( configDir, + absoluteScriptPath, + currentOptions.count(CmdOptions::LOCAL) > 0, + buildsManager, + buildDir, + includeManager) + .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); + return (PipelineResult)DS_TMP_ERROR.ErrorCode); + + //Handle cleanup command if present + if(currentOptions.count(CmdOptions::CLEANUP) > 0) + { + HandleCleanup( scriptInfo, + profiles.at(profileIndex), + scriptDirectory, + buildDir, + absoluteScriptPath, + buildsManager) + .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); + return (PipelineResult)DS_TMP_ERROR.ErrorCode); + } + + //Resolve imports + ResolveScriptImports(scriptInfo, absoluteScriptPath, buildDir) + .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); + return (PipelineResult)DS_TMP_ERROR.ErrorCode); + + //Check if script info has changed if provided and run setup if needed + bool recompileNeeded = false; + bool relinkNeeded = false; + std::vector changedDependencies; + + CheckScriptInfoChanges( buildDir, + scriptInfo, + profiles.at(profileIndex), + absoluteScriptPath, + lastScriptInfo, + maxThreads, + recompileNeeded, + relinkNeeded, + changedDependencies) + .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); + return (PipelineResult)DS_TMP_ERROR.ErrorCode); + + //if(!lastScriptInfo || recompileNeeded || !changedDependencies.empty() || relinkNeeded) + outScriptInfo = scriptInfo; + + std::vector gatheredBinariesPaths; + + //Process Dependencies + std::vector availableDependencies; + ProcessDependencies(scriptInfo, + profiles.at(profileIndex), + absoluteScriptPath, + buildDir, + currentOptions, + changedDependencies, + maxThreads, + availableDependencies, + gatheredBinariesPaths) + .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); + return (PipelineResult)DS_TMP_ERROR.ErrorCode); + + if(currentOptions.count(CmdOptions::RESET_DEPENDENCIES) > 0) + return PipelineResult::SUCCESS; + + //Get all the files we are trying to compile + std::vector sourceFiles; + GatherSourceFiles( absoluteScriptPath, + scriptInfo, + profiles.at(profileIndex), + sourceFiles) + .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); + return PipelineResult::INVALID_SCRIPT_INFO); + + //Get all include paths + std::vector includePaths; + GatherIncludePaths( scriptDirectory, + scriptInfo, + profiles.at(profileIndex), + availableDependencies, + includePaths) + .DS_TRY_ACT(DS_TMP_ERROR.Message += "Failed to gather include paths"; + ssLOG_ERROR(DS_TMP_ERROR.ToString()); + return PipelineResult::INVALID_SCRIPT_INFO); + + //Check if we have already compiled before. + std::vector sourceHasCache; + std::vector cachedObjectsFiles; + ghc::filesystem::file_time_type finalObjectWriteTime; + + if(currentOptions.count(CmdOptions::RESET_CACHE) > 0 || recompileNeeded) + sourceHasCache = std::vector(sourceFiles.size(), false); + else + { + HasCompiledCache( absoluteScriptPath, + sourceFiles, + buildDir, + profiles.at(profileIndex), + includeManager, + sourceHasCache, + cachedObjectsFiles, + finalObjectWriteTime, + outFinalSourceWriteTime, + outFinalIncludeWriteTime) + .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); + //TODO: Maybe add a pipeline result for this? + return PipelineResult::UNEXPECTED_FAILURE); + } + + runcpp2::SourceIncludeMap sourcesIncludes; + runcpp2::GatherFilesIncludes(sourceFiles, sourceHasCache, includePaths, sourcesIncludes) + .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); + return PipelineResult::UNEXPECTED_FAILURE); + + for(int i = 0; i < sourceFiles.size(); ++i) + { + if(!sourceHasCache.at(i)) + { + ssLOG_DEBUG("Updating include record for " << sourceFiles.at(i).string()); + if(sourcesIncludes.count(sourceFiles.at(i)) == 0) + { + ssLOG_WARNING("Includes not gathered for " << sourceFiles.at(i).string()); + continue; + } + + bool writeResult = includeManager.WriteIncludeRecord + ( + sourceFiles.at(i), + sourcesIncludes.at(sourceFiles.at(i)) + ); + if(!writeResult) + { + ssLOG_ERROR("Failed to write include record for " << sourceFiles.at(i).string()); + return PipelineResult::UNEXPECTED_FAILURE; + } + } + else + { + ssLOG_DEBUG("Include record for " << sourceFiles.at(i).string() << + " is up to date"); + } + } + + std::vector linkFilesPaths; + SeparateDependencyFiles(profiles.at(profileIndex).FilesTypes, + gatheredBinariesPaths, + linkFilesPaths, + filesToCopyPaths); + + std::error_code e; + + //Get finalBinaryWriteTime by combining final object and dependencies write times + ghc::filesystem::file_time_type finalBinaryWriteTime = finalObjectWriteTime; + for(int i = 0; i < linkFilesPaths.size(); ++i) + { + if(!ghc::filesystem::exists(linkFilesPaths.at(i), e)) + { + ssLOG_ERROR(linkFilesPaths.at(i) << " reported as cached but doesn't exist"); + return PipelineResult::UNEXPECTED_FAILURE; + } + + ghc::filesystem::file_time_type lastWriteTime = + ghc::filesystem::last_write_time(linkFilesPaths.at(i), e); + + if(lastWriteTime > finalBinaryWriteTime) + finalBinaryWriteTime = lastWriteTime; + } + + //Run PreBuild commands before compilation + HandlePreBuild(scriptInfo, profiles.at(profileIndex), buildDir) + .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); + return (PipelineResult)DS_TMP_ERROR.ErrorCode); + + //Compiling/Linking + bool outputCache = false; + if(!HasOutputCache( sourceHasCache, + buildDir, + profiles.at(profileIndex), + scriptInfo, + scriptName, + linkFilesPaths, + finalBinaryWriteTime, + outputCache)) + { + ssLOG_WARNING( "Error detected when trying to use output cache. " + "A cleanup is recommended"); + //return PipelineResult::UNEXPECTED_FAILURE; + } + + if(!outputCache || relinkNeeded) + { + for(int i = 0; i < cachedObjectsFiles.size(); ++i) + linkFilesPaths.push_back(cachedObjectsFiles.at(i)); + + //TODO: Compile and link for watch as well. Load library as well + if(currentOptions.count(CmdOptions::WATCH) > 0) + { + CompileScriptOnly( buildDir, + absoluteScriptPath, + sourceFiles, + sourceHasCache, + includePaths, + scriptInfo, + availableDependencies, + profiles.at(profileIndex), + maxThreads) + .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); + return PipelineResult::COMPILE_LINK_FAILED); + + return PipelineResult::SUCCESS; + } + else + { + CompileAndLinkScript( buildDir, + absoluteScriptPath, + ghc::filesystem::path(absoluteScriptPath).stem(), + sourceFiles, + sourceHasCache, + includePaths, + scriptInfo, + availableDependencies, + profiles.at(profileIndex), + linkFilesPaths, + maxThreads) + .DS_TRY_ACT(DS_TMP_ERROR.Message += "\nFailed to compile or link script."; + ssLOG_ERROR(DS_TMP_ERROR.ToString()); + return PipelineResult::COMPILE_LINK_FAILED); + } + } + } + + //Trigger post build and run the script if needed + { + std::vector targets; + ghc::filesystem::path runnableTarget; + GetBuiltTargetPaths(buildDir, + scriptName, + profiles.at(profileIndex), + currentOptions, + scriptInfo, + targets, + &runnableTarget) + .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); + return (PipelineResult)DS_TMP_ERROR.ErrorCode); + + if(targets.empty()) + { + ssLOG_WARNING("No target files found"); + return PipelineResult::SUCCESS; + } + + //Copy files to build directory + std::vector copiedPaths; + if(!buildOutputDir.empty()) + { + buildDir = buildOutputDir; + //filesToCopyPaths.push_back(runnableTarget.string()); + for(const ghc::filesystem::path& target : targets) + filesToCopyPaths.push_back(target.string()); + } + + CopyFiles(buildDir, filesToCopyPaths, copiedPaths) + .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); + ssLOG_ERROR("Failed to copy binaries before running the script"); + return PipelineResult::UNEXPECTED_FAILURE); + + //Run PostBuild commands after successful compilation + HandlePostBuild(scriptInfo, profiles.at(profileIndex), buildDir.string()) + .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); + return (PipelineResult)DS_TMP_ERROR.ErrorCode); + + //Don't run if we are just watching, reseting source cache or building + if( currentOptions.count(CmdOptions::WATCH) > 0 || + currentOptions.count(CmdOptions::RESET_CACHE) > 0 || + currentOptions.count(CmdOptions::BUILD) > 0) + { + return PipelineResult::SUCCESS; + } + + //Run otherwise + ssLOG_INFO("Running script..."); + RunCompiledOutput( runnableTarget, + absoluteScriptPath, + scriptInfo, + runArgs, + currentOptions, + returnStatus) + .DS_TRY_ACT(ssLOG_ERROR(DS_TMP_ERROR.ToString()); + return (PipelineResult)DS_TMP_ERROR.ErrorCode); + } + + return PipelineResult::SUCCESS; + + INTERNAL_RUNCPP2_SAFE_CATCH_RETURN(PipelineResult::UNEXPECTED_FAILURE); + } + + inline std::string PipelineResultToString(PipelineResult result) + { + static_assert(static_cast(PipelineResult::COUNT) == 13, "PipelineResult enum has changed"); + + switch(result) + { + case PipelineResult::UNEXPECTED_FAILURE: + return "UNEXPECTED_FAILURE"; + case PipelineResult::SUCCESS: + return "SUCCESS"; + case PipelineResult::EMPTY_PROFILES: + return "EMPTY_PROFILES"; + case PipelineResult::INVALID_SCRIPT_PATH: + return "INVALID_SCRIPT_PATH"; + case PipelineResult::INVALID_CONFIG_PATH: + return "INVALID_CONFIG_PATH"; + case PipelineResult::INVALID_BUILD_DIR: + return "INVALID_BUILD_DIR"; + case PipelineResult::INVALID_SCRIPT_INFO: + return "INVALID_SCRIPT_INFO"; + case PipelineResult::NO_AVAILABLE_PROFILE: + return "NO_AVAILABLE_PROFILE"; + case PipelineResult::DEPENDENCIES_FAILED: + return "DEPENDENCIES_FAILED"; + case PipelineResult::COMPILE_LINK_FAILED: + return "COMPILE_LINK_FAILED"; + case PipelineResult::INVALID_PROFILE: + return "INVALID_PROFILE"; + case PipelineResult::RUN_SCRIPT_FAILED: + return "RUN_SCRIPT_FAILED"; + case PipelineResult::INVALID_OPTION: + return "INVALID_OPTION"; + default: + return "UNKNOWN_PIPELINE_RESULT"; + } + } + + inline bool DownloadTutorial(char* runcppPath) + { + std::string dummy; + int returnCode = 0; + + std::string input; + while(true) + { + input.clear(); + ssLOG_BASE( "This will download InteractiveTutorial.cpp from github to current directory. " + "Continue? [Y/n]"); + + if(!std::getline(std::cin, input)) + { + ssLOG_ERROR("IO Error when trying to get cin"); + return false; + } + + if(!input.empty()) + { + if(input == "y" || input == "Y") + break; + else if(input == "n" || input == "N") + { + ssLOG_BASE("Not continuing"); + return 0; + } + else + ssLOG_BASE("Please only answer with y or n"); + } + else + break; + } + + std::string targetBranch = RUNCPP2_VERSION; + size_t dashPos = targetBranch.find("-"); + if(dashPos != std::string::npos) + targetBranch = targetBranch.substr(0, dashPos); + + #ifdef _WIN32 + if(!RunCommand( "powershell -Command \"" + "Invoke-WebRequest https://github.com/Neko-Box-Coder/runcpp2/raw/" + "refs/tags/" + targetBranch + "/Examples/InteractiveTutorial.cpp " + "-OutFile InteractiveTutorial.cpp\"", + false, + "./", + dummy, + returnCode)) + { + return false; + } + #else + if(!RunCommand( "curl -L -o InteractiveTutorial.cpp " + "https://github.com/Neko-Box-Coder/runcpp2/raw/refs/tags/" + + targetBranch + "/Examples/InteractiveTutorial.cpp", + false, + "./", + dummy, + returnCode)) + { + return false; + } + #endif + + ssLOG_INFO("targetBranch: " << targetBranch); + ssLOG_BASE("Downloaded InteractiveTutorial.cpp from github."); + ssLOG_BASE("Do `" << runcppPath << " InteractiveTutorial.cpp to start the tutorial."); + + return true; + } +} + +#include "runcpp2/LibYamlImpl.cpp" + +#endif diff --git a/Tests/Simple.cpp b/Tests/Simple.cpp new file mode 100644 index 0000000..78d0206 --- /dev/null +++ b/Tests/Simple.cpp @@ -0,0 +1,2 @@ +#include +int main(){ std::cout<<"hello"< +#include + +int main(int argc, char* argv[]) +{ + std::cout << "Hello World" << std::endl; + std::cout << TEST_DEF << std::endl; + + System2CommandInfo commandInfo = {}; + int returnCode = 0; + + #if defined(_WIN32) + System2CppRun("dir", commandInfo); + #else + auto re = System2CppRun("ls -lah", commandInfo); + #endif + System2CppGetCommandReturnValueSync(commandInfo, returnCode, false); + + for(int i = 0; i < argc; ++i) + std::cout << "Arg" << i << ": " << argv[i] << std::endl; + + #if ssLOG_USE_SOURCE + ssLOG_LINE("Source dependency is working!!!"); + #else + ssLOG_LINE("Header only dependency is working!!!"); + #endif + + ssLOG_LINE("Multi source file is working: " << TestFunc()); + + int CheckCounter = 5; + ssLOG_FATAL("Test fatal: " << CheckCounter); + ssLOG_ERROR("Test error: " << CheckCounter); + ssLOG_WARNING("Test warning: " << CheckCounter); + ssLOG_INFO("Test info: " << CheckCounter); + ssLOG_DEBUG("Test debug: " << CheckCounter); + + for(int i = 0; i < 100; ++i) + { + ssLOG_LINE("Logging test: " << i); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + return 0; +} diff --git a/Tests/TestSeparateYaml.yaml b/Tests/TestSeparateYaml.yaml new file mode 100644 index 0000000..e9f70dd --- /dev/null +++ b/Tests/TestSeparateYaml.yaml @@ -0,0 +1,101 @@ +PassScriptPath: true + +RequiredProfiles: + Windows: ["msvc"] + Unix: ["g++"] + +OtherFilesToBeCompiled: + # Target Platform + DefaultPlatform: + # Target Profile + "g++": + - "./OtherSources/AnotherSourceFileGcc.cpp" + "msvc": + - "./OtherSources/AnotherSourceFileMSVC.cpp" + +IncludePaths: + DefaultPlatform: + DefaultProfile: + - "./OtherSources" + +Defines: + DefaultPlatform: + # Turns into `TEST_DEF=\"Test Define Working\"` in shell + DefaultProfile: ["TEST_DEF=\\\"Test Define Working\\\""] + +Setup: + Unix: + DefaultProfile: + - "echo Setting up script... in $PWD" + Windows: + DefaultProfile: + - "echo Setting up script... in %cd%" + +PreBuild: + Unix: + DefaultProfile: + - "echo Starting build... in $PWD" + Windows: + DefaultProfile: + - "echo Starting build... in %cd%" + +PostBuild: + Unix: + DefaultProfile: + - "echo Build completed... in $PWD" + Windows: + DefaultProfile: + - "echo Build completed... in %cd%" + +Cleanup: + Unix: + DefaultProfile: + - "echo Cleaning up script... in $PWD" + Windows: + DefaultProfile: + - "echo Cleaning up script... in %cd%" + +Dependencies: +- Name: ssLogger + Platforms: [Windows, Linux, MacOS] + Source: + Git: + URL: "https://github.com/Neko-Box-Coder/ssLogger.git" + LibraryType: Shared + IncludePaths: ["Include"] + LinkProperties: + DefaultPlatform: + DefaultProfile: + SearchLibraryNames: ["ssLogger"] + ExcludeLibraryNames: ["ssLogger_SRC"] + SearchDirectories: ["./build", "./build/Debug", "./build/Release"] + Setup: + DefaultPlatform: + DefaultProfile: + - "mkdir build" + Build: + DefaultPlatform: + DefaultProfile: + - "cd build && cmake .. -DssLOG_BUILD_TYPE=SHARED" + - "cd build && cmake --build . -j 16" + FilesToCopy: + # Target Platform (Default, Windows, Linux, MacOS, or Unix) + DefaultPlatform: + DefaultProfile: + - "./Include/ssLogger/ssLog.hpp" + +- Name: System2.cpp + Source: + ImportPath: "./TestImport.yaml" + +# - Name: System2.cpp +# Platforms: [DefaultPlatform] +# Source: +# Git: +# URL: "https://github.com/Neko-Box-Coder/System2.cpp.git" +# LibraryType: Header +# IncludePaths: [".", "./External/System2"] +# Setup: +# DefaultPlatform: +# DefaultProfile: +# - "git submodule update --init --recursive" diff --git a/Tests/test_static.cpp b/Tests/TestStatic.cpp similarity index 100% rename from Tests/test_static.cpp rename to Tests/TestStatic.cpp diff --git a/Tests/YamlMissingSource.yaml b/Tests/YamlMissingSource.yaml new file mode 100644 index 0000000..858f24f --- /dev/null +++ b/Tests/YamlMissingSource.yaml @@ -0,0 +1,6 @@ +BuildType: Executable +PassScriptPath: false +Language: "c++" +RequiredProfiles: + DefaultPlatform: + - "g++" diff --git a/Tests/YamlOnlyTest.yaml b/Tests/YamlOnlyTest.yaml new file mode 100644 index 0000000..8a680d1 --- /dev/null +++ b/Tests/YamlOnlyTest.yaml @@ -0,0 +1,7 @@ +BuildType: Executable +PassScriptPath: false +Language: "c++" +SourceFiles: + DefaultPlatform: + DefaultProfile: + - "./Simple.cpp" diff --git a/iwyu.sh b/iwyu.sh new file mode 100755 index 0000000..b440842 --- /dev/null +++ b/iwyu.sh @@ -0,0 +1 @@ +include-what-you-use -Xiwyu --check_also="*Include/runcpp2/*.hpp" "-DRUNCPP2_VERSION=\"0.0.0\"" -DRUNCPP2_CONFIG_VERSION=1 --std=c++11 -Wno-switch -I ./Include -I ./External -I ./External/DSResult/Include -I ./External/ssLogger/Include -I ./External/filesystem/include -I ./External/DSResult/External/expected/include -I ./External/variant/include -I ./External/string-view-lite/include -I ./External/System2 -I ./External/CppOverride/Include_SingleHeader -I ./External/MacroPowerToys -I ./External/dylib/include -I ./External/libyaml/include -I ./Src ./Src/runcpp2/main.cpp > iwyu.txt 2>&1 diff --git a/mkdocs/docs/TODO.md b/mkdocs/docs/TODO.md index ee70093..62bd6ca 100644 --- a/mkdocs/docs/TODO.md +++ b/mkdocs/docs/TODO.md @@ -7,6 +7,7 @@ - Add version for default user config and prompt for update - Rename "OtherFilesToBeCompiled" to "SourceFiles" - Migrate to libyaml +- Migrate to DSResult and remove ssTest ### v0.3.1 - Check last run is shared lib or executable. Reset cache when necessary if different type @@ -31,9 +32,11 @@ ### v0.4.0 - Allow runcpp2 to be library for scriptable pipeline +- Parameters for build config/script +- Parameters for profiles - Add more default profiles -- Ability to compile runcpp2 as single cpp, to ditch cmake -- Migrate to DSResult and remove ssTest +- Ability to compile runcpp2 as single cpp, ditch cmake +- Move to variant-lite, mpark-variant is unreadable ## High Priority @@ -41,7 +44,6 @@ - This ties to the warning in `CompilingLinking.cpp:619` - Update `FileProperties.hpp` to use list of string for prefix and extension - Merge `SharedLinkFile` and `SharedLibraryFile` -- Add the ability for user to specify custom substitution options which applies to all fields - Add the ability to append defines coming from the dependencies - Add wildcard support for filenames and extensions (Files Globbing) - Add the ability to query script build directory diff --git a/mkdocs/docs/guides/basics.md b/mkdocs/docs/guides/basics.md index c41e925..2418170 100644 --- a/mkdocs/docs/guides/basics.md +++ b/mkdocs/docs/guides/basics.md @@ -21,7 +21,7 @@ with the config specified either: script.yaml # Your build settings ``` -The name of the final output will be the name of the script file, therefore a script file will +The name of the output binary will be the name of the script file therefore a script file will always have a 1 to 1 relationship with the linker output, even if multiple sources are specified in the script file build settings. @@ -60,6 +60,12 @@ runcpp2 ./script.cpp int main(int, char**) { std::cout << "Hello World" << std::endl; } ``` +### YAML File As Input + +!!! info inline end "This requires `v0.4.0` version" +You can also use a YAML file as input. If this is the case, the name of the output +binary will be the name of the YAML file. + --- ## Error Feedback @@ -79,6 +85,8 @@ can be spcified inlined inside a source file or as a separate yaml file in the f - To specify build config in a dedicated yaml file: - The yaml file in the same directory and share the same as the source file being run will be used + !!! info inline end "This requires `v0.4.0` version" + - The yaml file must specify at least one source if fed as input - To specify inline build config inside a source file: - Put them inside a comment with `runcpp2` at the beginning of the build config - The inline build config can exist in anywhere of the source file