#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 "Platform.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; }; enum OptimizationLevel { OPT_LEV_NONE, OPT_LEV_DEBUG, OPT_LEV_RELEASE }; enum class Compiler { GCC, MSVC }; enum class CVersion { C89, // aka ansi C99, C11 }; enum class CPPVersion { CPP11, CPP14, CPP17 }; enum class Language { NONE, C, CPP, ZIG }; struct SourceFile { Language language; std::string filepath; }; const StringView CONFIGS[] = { "config.win", "config.win.static.debug", "config.win.static.release", "config.linux", "config.linux.static.debug", "config.linux.static.release", "config.macos", "config.macos.static.debug", "config.macos.static.release", "config.bsd", "config.bsd.static.debug", "config.bsd.static.release", "config.openbsd", "config.openbsd.static.debug", "config.openbsd.static.release", "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", "config.macos32", "config.macos32.static.debug", "config.macos32.static.release" "config.macos64", "config.macos64.static.debug", "config.macos64.static.release", "config.openbsd32", "config.openbsd32.static.debug", "config.openbsd32.static.release", "config.openbsd64", "config.openbsd64.static.debug", "config.openbsd64.static.release" }; const int NUM_CONFIGS = 3 * 13; const int CONFIGS_GENERIC_OFFSET = 3 * 5; #if OS_TYPE == OS_TYPE_WINDOWS #ifdef SIBS_ENV_32BIT const Platform SYSTEM_PLATFORM = PLATFORM_WIN32; #define SYSTEM_PLATFORM_NAME "win32" #define CONFIG_SYSTEM_PLATFORM CONFIGS_GENERIC_OFFSET + 0 #define CONFIG_STATIC_DEBUG_PLATFORM CONFIGS_GENERIC_OFFSET + 1 #define CONFIG_STATIC_RELEASE_PLATFORM CONFIGS_GENERIC_OFFSET + 2 #else const Platform SYSTEM_PLATFORM = PLATFORM_WIN64; #define SYSTEM_PLATFORM_NAME "win64" #define CONFIG_SYSTEM_PLATFORM CONFIGS_GENERIC_OFFSET + 3 #define CONFIG_STATIC_DEBUG_PLATFORM CONFIGS_GENERIC_OFFSET + 4 #define CONFIG_STATIC_RELEASE_PLATFORM CONFIGS_GENERIC_OFFSET + 5 #endif #define CONFIG_STATIC_LIB_FILE_EXTENSION L"lib" #define CONFIG_DYNAMIC_LIB_FILE_EXTENSION L"dll" #define SYSTEM_GENERIC_PLATFORM_NAME "win" #define CONFIG_GENERIC_SYSTEM_PLATFORM 0 #define CONFIG_GENERIC_STATIC_DEBUG_PLATFORM 1 #define CONFIG_GENERIC_STATIC_RELEASE_PLATFORM 2 #elif OS_TYPE == OS_TYPE_LINUX #ifdef SIBS_ENV_32BIT const Platform SYSTEM_PLATFORM = PLATFORM_LINUX32; #define SYSTEM_PLATFORM_NAME "linux32" #define CONFIG_SYSTEM_PLATFORM CONFIGS_GENERIC_OFFSET + 6 #define CONFIG_STATIC_DEBUG_PLATFORM CONFIGS_GENERIC_OFFSET + 7 #define CONFIG_STATIC_RELEASE_PLATFORM CONFIGS_GENERIC_OFFSET + 8 #else const Platform SYSTEM_PLATFORM = PLATFORM_LINUX64; #define SYSTEM_PLATFORM_NAME "linux64" #define CONFIG_SYSTEM_PLATFORM CONFIGS_GENERIC_OFFSET + 9 #define CONFIG_STATIC_DEBUG_PLATFORM CONFIGS_GENERIC_OFFSET + 10 #define CONFIG_STATIC_RELEASE_PLATFORM CONFIGS_GENERIC_OFFSET + 11 #endif #define CONFIG_STATIC_LIB_FILE_EXTENSION "a" #define CONFIG_DYNAMIC_LIB_FILE_EXTENSION "so" #define SYSTEM_GENERIC_PLATFORM_NAME "linux" #define CONFIG_GENERIC_SYSTEM_PLATFORM 3 #define CONFIG_GENERIC_STATIC_DEBUG_PLATFORM 4 #define CONFIG_GENERIC_STATIC_RELEASE_PLATFORM 5 #elif OS_TYPE == OS_TYPE_APPLE #ifdef SIBS_ENV_32BIT const Platform SYSTEM_PLATFORM = PLATFORM_MACOS32; #define SYSTEM_PLATFORM_NAME "macos32" #define CONFIG_SYSTEM_PLATFORM CONFIGS_GENERIC_OFFSET + 12 #define CONFIG_STATIC_DEBUG_PLATFORM CONFIGS_GENERIC_OFFSET + 13 #define CONFIG_STATIC_RELEASE_PLATFORM CONFIGS_GENERIC_OFFSET + 14 #else const Platform SYSTEM_PLATFORM = PLATFORM_MACOS64; #define SYSTEM_PLATFORM_NAME "macos64" #define CONFIG_SYSTEM_PLATFORM CONFIGS_GENERIC_OFFSET + 15 #define CONFIG_STATIC_DEBUG_PLATFORM CONFIGS_GENERIC_OFFSET + 16 #define CONFIG_STATIC_RELEASE_PLATFORM CONFIGS_GENERIC_OFFSET + 17 #endif #define CONFIG_STATIC_LIB_FILE_EXTENSION "a" #define CONFIG_DYNAMIC_LIB_FILE_EXTENSION "dylib" #define SYSTEM_GENERIC_PLATFORM_NAME "macos" #define CONFIG_GENERIC_SYSTEM_PLATFORM 6 #define CONFIG_GENERIC_STATIC_DEBUG_PLATFORM 7 #define CONFIG_GENERIC_STATIC_RELEASE_PLATFORM 8 #elif OS_TYPE == OS_TYPE_OPENBSD #ifdef SIBS_ENV_32BIT const Platform SYSTEM_PLATFORM = PLATFORM_OPENBSD32; #define SYSTEM_PLATFORM_NAME "openbsd32" #define CONFIG_SYSTEM_PLATFORM CONFIGS_GENERIC_OFFSET + 18 #define CONFIG_STATIC_DEBUG_PLATFORM CONFIGS_GENERIC_OFFSET + 19 #define CONFIG_STATIC_RELEASE_PLATFORM CONFIGS_GENERIC_OFFSET + 20 #else const Platform SYSTEM_PLATFORM = PLATFORM_OPENBSD64; #define SYSTEM_PLATFORM_NAME "openbsd64" #define CONFIG_SYSTEM_PLATFORM CONFIGS_GENERIC_OFFSET + 21 #define CONFIG_STATIC_DEBUG_PLATFORM CONFIGS_GENERIC_OFFSET + 22 #define CONFIG_STATIC_RELEASE_PLATFORM CONFIGS_GENERIC_OFFSET + 23 #endif #define CONFIG_STATIC_LIB_FILE_EXTENSION "a" #define CONFIG_DYNAMIC_LIB_FILE_EXTENSION "so" // TODO: Also add "bsd" platform #define SYSTEM_GENERIC_PLATFORM_NAME "openbsd" #define CONFIG_GENERIC_SYSTEM_PLATFORM 9 #define CONFIG_GENERIC_STATIC_DEBUG_PLATFORM 10 #define CONFIG_GENERIC_STATIC_RELEASE_PLATFORM 11 #endif 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), mainProject(false), sanitize(false), showWarnings(false), zigTestAllFiles(false), packaging(false), bundling(false) { cmakeDirGlobal = projectPath; cmakeDirStatic = cmakeDirGlobal; cmakeDirDynamic = cmakeDirGlobal; switch(optimizationLevel) { case OPT_LEV_DEBUG: cmakeArgsGlobal = TINYDIR_STRING("-G Ninja \"-DCMAKE_BUILD_TYPE=Debug\""); break; case OPT_LEV_RELEASE: cmakeArgsGlobal = TINYDIR_STRING("-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 FileString& getCmakeArgs() const { return cmakeArgsGlobal; } // Get cmake args for static build. This is a combination of args under [cmake] and under [cmake.static] FileString getCmakeArgsStatic() const { FileString result; result.reserve(cmakeArgsGlobal.size() + 1 + cmakeArgsStatic.size()); result += cmakeArgsGlobal; result += TINYDIR_STRING(" "); result += cmakeArgsStatic; return result; } // Get cmake args for dynamic build. This is a combination of args under [cmake] and under [cmake.dynamic] FileString getCmakeArgsDynamic() const { FileString result; result.reserve(cmakeArgsGlobal.size() + 1 + cmakeArgsDynamic.size()); result += cmakeArgsGlobal; result += TINYDIR_STRING(" "); 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; } bool isMainProject() const { return mainProject; } void setMainProject(bool mainProject) { this->mainProject = mainProject; } bool getSanitize() const { return sanitize; } void setSanitize(bool sanitize) { this->sanitize = sanitize; } 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; virtual bool isTest() const { return false; } // 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; std::vector zigTestFiles; bool showWarnings; bool zigTestAllFiles; bool packaging; bool bundling; std::string version; 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 parsePlatformBuildTypeConfigs(const StringView &fieldName, const ConfigValue &fieldValue); std::string parsePlatformConfigStatic(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, FileString &cmakeDir, FileString &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; FileString cmakeArgsGlobal; FileString cmakeArgsStatic; FileString cmakeArgsDynamic; CVersion cVersion; CPPVersion cppVersion; bool useCmake; bool buildTests; bool finishedProcessing; bool mainProject; bool sanitize; }; class SibsTestConfig : public SibsConfig { public: SibsTestConfig(Compiler _compiler, const FileString &_projectPath, OptimizationLevel _optimizationLevel) : SibsConfig(_compiler, _projectPath, _optimizationLevel, false) { packageName = "test"; showWarnings = true; } virtual ~SibsTestConfig(){} bool isTest() const override { return true; } PackageType getPackageType() const override { return PackageType::EXECUTABLE; } protected: void processObject(StringView name) override; void processField(StringView name, const ConfigValue &value) override; void finished() override; }; class Config { public: static Result readFromFile(const _tinydir_char_t *filepath, SibsConfig &config); }; void readSibsConfig(const FileString &projectPath, const FileString &projectConfFilePath, SibsConfig &sibsConfig, FileString &buildPath); } #endif //SIBS_CONF_HPP