#ifndef SIBS_CONF_HPP #define SIBS_CONF_HPP #include "FileUtil.hpp" #include "Result.hpp" #include "StringView.hpp" #include "utils.hpp" #include "Dependency.hpp" #include "Package.hpp" #include #include #include #include namespace sibs { class ConfigValue { public: enum class Type { NONE, SINGLE, LIST, OBJECT }; ConfigValue() : type(Type::NONE) {} ConfigValue(StringView value) : type(Type::SINGLE) { values.push_back(value); } ConfigValue(const std::vector &_values) : type(Type::LIST), values(_values) { } ConfigValue(const std::unordered_map &_map) : type(Type::OBJECT), map(_map) { } bool isSingle() const { return type == Type::SINGLE; } bool isList() const { return type == Type::LIST; } bool isObject() const { return type == Type::OBJECT; } StringView asSingle() const { assert(isSingle()); return values[0]; } const std::vector asList() const { assert(isList()); return values; } const std::unordered_map& asObject() const { assert(isObject()); return map; } private: Type type; std::vector values; std::unordered_map map; }; class Parser; class ParserException : public std::runtime_error { public: ParserException(const std::string &errMsg) : runtime_error(errMsg) { } }; class ConfigCallback { friend class Parser; public: virtual ~ConfigCallback(){} protected: virtual void processObject(StringView name) = 0; virtual void processField(StringView name, const ConfigValue &value) = 0; virtual void finished() = 0; }; class Config { public: static Result readFromFile(const _tinydir_char_t *filepath, const ConfigCallback &callback); }; enum OptimizationLevel { OPT_LEV_NONE, OPT_LEV_DEBUG, OPT_LEV_RELEASE }; enum class Compiler { GCC, MSVC }; enum Platform { PLATFORM_ANY, PLATFORM_LINUX32, PLATFORM_LINUX64, PLATFORM_WIN32, PLATFORM_WIN64 }; enum class CVersion { C89, // aka ansi C99, C11 }; enum class CPPVersion { CPP11, CPP14, CPP17 }; const StringView CONFIGS[] = { "config.win32", "config.win32.static.debug", "config.win32.static.release", "config.win64", "config.win64.static.debug", "config.win64.static.release", "config.linux32", "config.linux32.static.debug", "config.linux32.static.release", "config.linux64", "config.linux64.static.debug", "config.linux64.static.release" }; const int NUM_CONFIGS = 12; // TODO: Detect this at runtime? #if OS_TYPE == OS_TYPE_WINDOWS #ifdef SIBS_ENV_32BIT #define SYSTEM_PLATFORM Platform::PLATFORM_WIN32 #define SYSTEM_PLATFORM_NAME "win32" #define CONFIG_SYSTEM_PLATFORM 0 #define CONFIG_STATIC_DEBUG_PLATFORM 1 #define CONFIG_STATIC_RELEASE_PLATFORM 2 #else #define SYSTEM_PLATFORM Platform::PLATFORM_WIN64 #define SYSTEM_PLATFORM_NAME "win64" #define CONFIG_SYSTEM_PLATFORM 3 #define CONFIG_STATIC_DEBUG_PLATFORM 4 #define CONFIG_STATIC_RELEASE_PLATFORM 5 #endif #define CONFIG_STATIC_LIB_FILE_EXTENSION "lib" #define CONFIG_DYNAMIC_LIB_FILE_EXTENSION "dll" #elif OS_TYPE == OS_TYPE_LINUX #ifdef SIBS_ENV_32BIT #define SYSTEM_PLATFORM Platform::PLATFORM_LINUX32 #define SYSTEM_PLATFORM_NAME "linux32" #define CONFIG_SYSTEM_PLATFORM 6 #define CONFIG_STATIC_DEBUG_PLATFORM 7 #define CONFIG_STATIC_RELEASE_PLATFORM 8 #else #define SYSTEM_PLATFORM Platform::PLATFORM_LINUX64 #define SYSTEM_PLATFORM_NAME "linux64" #define CONFIG_SYSTEM_PLATFORM 9 #define CONFIG_STATIC_DEBUG_PLATFORM 10 #define CONFIG_STATIC_RELEASE_PLATFORM 11 #endif #define CONFIG_STATIC_LIB_FILE_EXTENSION "a" #define CONFIG_DYNAMIC_LIB_FILE_EXTENSION "so" #endif bool containsPlatform(const std::vector &platforms, Platform platform); const char* asString(Platform platform); const char* asString(OptimizationLevel optLevel); bool directoryToIgnore(const FileString &dir, const std::vector &ignoreDirList); bool isProjectNameValid(const std::string &projectName); class SibsConfig : public ConfigCallback { public: SibsConfig(Compiler _compiler, const FileString &_projectPath, OptimizationLevel _optimizationLevel, bool _buildTests) : compiler(_compiler), projectPath(_projectPath), packageType((PackageType)-1), optimizationLevel(_optimizationLevel), finishedProcessing(false), useCmake(false), buildTests(_buildTests), cVersion(CVersion::C11), cppVersion(CPPVersion::CPP14) { cmakeDirGlobal = projectPath; cmakeDirStatic = cmakeDirGlobal; cmakeDirDynamic = cmakeDirGlobal; switch(optimizationLevel) { case OPT_LEV_DEBUG: cmakeArgsGlobal = "-G Ninja \"-DCMAKE_BUILD_TYPE=Debug\""; break; case OPT_LEV_RELEASE: cmakeArgsGlobal = "-G Ninja \"-DCMAKE_BUILD_TYPE=Release\""; break; } } SibsConfig operator=(SibsConfig &other) = delete; virtual ~SibsConfig(); Compiler getCompiler() const { return compiler; } virtual const std::string& getPackageName() const { assert(finishedProcessing); return packageName; } virtual PackageType getPackageType() const { assert(finishedProcessing); return packageType; } virtual const FileString& getTestPath() const { return testPath; } void setTestPath(const FileString &testPath) { this->testPath = testPath; } virtual const std::vector& getPackageListDependencies() const { return packageListDependencies; } virtual const std::vector& getGitDependencies() const { return gitDependencies; } virtual const FileString& getProjectPath() const { return projectPath; } virtual const std::vector& getIncludeDirs() const { return includeDirs; } virtual const std::vector& getGlobalIncludeDirs() const { return exposeIncludeDirs; } virtual const std::vector& getPlatforms() const { return platforms; } virtual OptimizationLevel getOptimizationLevel() const { return optimizationLevel; } const std::vector& getDebugStaticLibs() const { return debugStaticLibs; } const std::vector& getReleaseStaticLibs() const { return releaseStaticLibs; } const std::vector& getIgnoreDirs() const { return ignoreDirs; } const FileString& getCmakeDir() const { return cmakeDirGlobal; } const FileString& getCmakeDirStatic() const { return !cmakeDirStatic.empty() ? cmakeDirStatic : cmakeDirGlobal; } const FileString& getCmakeDirDynamic() const { return !cmakeDirDynamic.empty() ? cmakeDirDynamic : cmakeDirGlobal; } // Get cmake args for all builds. This is args under [cmake] only const std::string& getCmakeArgs() const { return cmakeArgsGlobal; } // Get cmake args for static build. This is a combination of args under [cmake] and under [cmake.static] std::string getCmakeArgsStatic() const { std::string result; result.reserve(cmakeArgsGlobal.size() + 1 + cmakeArgsStatic.size()); result += cmakeArgsGlobal; result += " "; result += cmakeArgsStatic; return result; } // Get cmake args for dynamic build. This is a combination of args under [cmake] and under [cmake.dynamic] std::string getCmakeArgsDynamic() const { std::string result; result.reserve(cmakeArgsGlobal.size() + 1 + cmakeArgsDynamic.size()); result += cmakeArgsGlobal; result += " "; result += cmakeArgsDynamic; return result; } CVersion getCversion() const { return cVersion; } CPPVersion getCppVersion() const { return cppVersion; } bool shouldUseCmake() const { return useCmake; } bool shouldBuildTests() const { return buildTests; } void setPackageType(PackageType packageType) { this->packageType = packageType; } virtual bool isDefined(const std::string &name) const; virtual bool define(const std::string &name, const std::string &value); virtual const std::unordered_map& getDefines() const; // Get define value by name. // Return empty string if the value is empty or if the defined value doesn't exist const std::string& getDefinedValue(const std::string &name) const; protected: virtual void processObject(StringView name) override; virtual void processField(StringView name, const ConfigValue &value) override; void parseDependencies(const StringView &name, const ConfigValue &value); virtual void finished() override; void failInvalidFieldUnderObject(const StringView &fieldName) const; void validatePackageTypeDefined() const; private: void parseCLang(const StringView &fieldName, const ConfigValue &fieldValue); void parseCppLang(const StringView &fieldName, const ConfigValue &fieldValue); void parsePlatformConfigs(const StringView &fieldName, const ConfigValue &fieldValue); void parsePlatformConfig(const StringView &fieldName, const ConfigValue &fieldValue); void parsePlatformConfigStaticDebug(const StringView &fieldName, const ConfigValue &fieldValue); void parsePlatformConfigStaticRelease(const StringView &fieldName, const ConfigValue &fieldValue); void parseCmake(const StringView &fieldName, const ConfigValue &fieldValue, std::string &cmakeDir, std::string &cmakeArgs); void validatePackageName() const; protected: StringView currentObject; Compiler compiler; FileString projectPath; std::string packageName; FileString testPath; PackageType packageType; std::vector packageListDependencies; std::vector gitDependencies; std::vector includeDirs; std::vector exposeIncludeDirs; std::vector platforms; std::vector ignoreDirs; std::unordered_map defines; OptimizationLevel optimizationLevel; std::vector debugStaticLibs; std::vector releaseStaticLibs; FileString cmakeDirGlobal; FileString cmakeDirStatic; FileString cmakeDirDynamic; std::string cmakeArgsGlobal; std::string cmakeArgsStatic; std::string cmakeArgsDynamic; CVersion cVersion; CPPVersion cppVersion; bool useCmake; bool buildTests; bool finishedProcessing; }; class SibsTestConfig : public SibsConfig { public: SibsTestConfig(Compiler _compiler, const FileString &_projectPath, OptimizationLevel _optimizationLevel) : SibsConfig(_compiler, _projectPath, _optimizationLevel, true) { packageName = "test"; } virtual ~SibsTestConfig(){} PackageType getPackageType() const override { return PackageType::EXECUTABLE; } protected: void processObject(StringView name) override; void processField(StringView name, const ConfigValue &value) override; void finished() override; }; } #endif //SIBS_CONF_HPP