diff options
-rw-r--r-- | CMakeLists.txt | 5 | ||||
-rw-r--r-- | backend/ninja/Ninja.cpp | 45 | ||||
-rw-r--r-- | backend/ninja/Ninja.hpp | 5 | ||||
-rw-r--r-- | include/Conf.hpp | 17 | ||||
-rw-r--r-- | include/Dependency.hpp | 51 | ||||
-rw-r--r-- | include/GitRepository.hpp | 16 | ||||
-rw-r--r-- | include/GlobalLib.hpp | 12 | ||||
-rw-r--r-- | include/PkgConfig.hpp | 11 | ||||
-rw-r--r-- | src/CmakeModule.cpp | 8 | ||||
-rw-r--r-- | src/Conf.cpp | 129 | ||||
-rw-r--r-- | src/GitRepository.cpp | 101 | ||||
-rw-r--r-- | src/GlobalLib.cpp | 158 | ||||
-rw-r--r-- | src/PkgConfig.cpp | 23 | ||||
-rw-r--r-- | src/main.cpp | 17 | ||||
-rw-r--r-- | tests/src/confTest/confTest.cpp | 43 | ||||
-rw-r--r-- | tests/src/confTest/gitDependency.conf | 9 | ||||
-rw-r--r-- | tests/src/confTest/validProject.conf | 1 |
17 files changed, 515 insertions, 136 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index d95355c..0e1af36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,8 @@ set(SOURCE_FILES src/curl.cpp src/Archive.cpp src/CmakeModule.cpp - src/Package.cpp) + src/Package.cpp + src/GitRepository.cpp) find_package(CURL REQUIRED) find_package(LibArchive REQUIRED) @@ -23,5 +24,5 @@ find_package(LibArchive REQUIRED) add_executable(sibs ${SOURCE_FILES}) include_directories(${CURL_INCLUDE_DIR} ${LibArchive_INCLUDE_DIR}) -target_link_libraries(sibs ${CURL_LIBRARIES} ${LibArchive_LIBRARIES}) +target_link_libraries(sibs ${CURL_LIBRARIES} ${LibArchive_LIBRARIES} -lgit2) target_compile_options(sibs PRIVATE -Wall -Wextra -Werror=return-type -fdiagnostics-show-option -fexceptions) diff --git a/backend/ninja/Ninja.cpp b/backend/ninja/Ninja.cpp index 530dffe..0ca7c7a 100644 --- a/backend/ninja/Ninja.cpp +++ b/backend/ninja/Ninja.cpp @@ -206,6 +206,11 @@ namespace backend { } + + void Ninja::addGlobalIncludeDirs(const string &globalIncludeDirs) + { + customGlobalIncludeDirs = globalIncludeDirs; + } void Ninja::addSourceFile(const char *filepath) { @@ -272,9 +277,8 @@ namespace backend // If pkg-config is installed and dependency is not installed, check in dependencies sub directory. Result<bool> Ninja::getLinkerFlags(const SibsConfig &config, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallback, GlobalIncludeDirCallbackFunc globalIncludeDirCallback, CflagsCallbackFunc cflagsCallbackFunc) const { - const vector<Dependency> &dependencies = config.getDependencies(); - if(dependencies.empty()) return Result<bool>::Ok(true); - + const vector<PackageListDependency*> &packageListDependencies = config.getPackageListDependencies(); + Result<FileString> globalLibDirResult = getHomeDir(); if (!globalLibDirResult) return Result<bool>::Err(globalLibDirResult); @@ -284,10 +288,10 @@ namespace backend if(createGlobalLibDirResult.isErr()) return createGlobalLibDirResult; - vector<Dependency> globalLibDependencies; + vector<PackageListDependency*> globalLibDependencies; #if OS_FAMILY == OS_FAMILY_POSIX - vector<Dependency> pkgConfigDependencies; - for(const Dependency &dependency : dependencies) + vector<PackageListDependency*> pkgConfigDependencies; + for(PackageListDependency *dependency : packageListDependencies) { Result<bool> pkgConfigDependencyValidation = PkgConfig::validatePkgConfigPackageVersionExists(dependency); if(pkgConfigDependencyValidation.isOk()) @@ -305,7 +309,7 @@ namespace backend { printf("%s, using global lib...\n", pkgConfigFlagsResult.getErrMsg().c_str()); globalLibDependencies.reserve(globalLibDependencies.size() + pkgConfigDependencies.size()); - for (const Dependency &pkgConfigDependency : pkgConfigDependencies) + for (PackageListDependency *pkgConfigDependency : pkgConfigDependencies) { globalLibDependencies.push_back(pkgConfigDependency); } @@ -353,7 +357,7 @@ namespace backend ninjaBuildFilePath += TINYDIR_STRING("/build.ninja"); string result; - result.reserve(16384); + result.reserve(2048); Result<FileString> globalIncDirResult = getHomeDir(); if (!globalIncDirResult) @@ -384,10 +388,11 @@ namespace backend parentGlobalIncludeDirCallback(globalIncludeDirFull); } - globalIncludeDirCallback = [&parentGlobalIncludeDirCallback, &globalIncludeDirCallback, &result, &config](const string &globalIncludeDir) + string dependencyExportIncludeDirs = customGlobalIncludeDirs; + globalIncludeDirCallback = [&parentGlobalIncludeDirCallback, &globalIncludeDirCallback, &dependencyExportIncludeDirs, &config](const string &globalIncludeDir) { - result += " "; - result += getIncludeOptionFlag(config.getCompiler(), globalIncludeDir); + dependencyExportIncludeDirs += " "; + dependencyExportIncludeDirs += getIncludeOptionFlag(config.getCompiler(), globalIncludeDir); if (parentGlobalIncludeDirCallback) parentGlobalIncludeDirCallback(globalIncludeDir); }; @@ -437,6 +442,7 @@ namespace backend if (linkerFlags.isErr()) return Result<bool>::Err(linkerFlags.getErrMsg()); + result += dependencyExportIncludeDirs; result += "\n\n"; string defines; @@ -780,7 +786,7 @@ namespace backend result += ".a: " + buildJob + " "; result += join(objectNames, " "); result += "\n\n"; - projectGeneratedBinary += config.getPackageName() + ".a"; + projectGeneratedBinary += "lib" + config.getPackageName() + ".a"; break; } case Compiler::MSVC: @@ -850,7 +856,7 @@ namespace backend return buildResult; } - Result<bool> buildTestResult = buildTests(projectGeneratedBinary, config, savePath); + Result<bool> buildTestResult = buildTests(projectGeneratedBinary, config, savePath, dependencyExportIncludeDirs); if(!buildTestResult) return buildTestResult; @@ -872,7 +878,7 @@ namespace backend return false; } - Result<bool> Ninja::buildTests(const std::string &projectGeneratedBinary, const SibsConfig &config, const _tinydir_char_t *savePath) + Result<bool> Ninja::buildTests(const std::string &projectGeneratedBinary, const SibsConfig &config, const _tinydir_char_t *savePath, const string &parentDependencyExportIncludeDirs) { if(testSourceDirs.empty() || !config.shouldBuildTests()) return Result<bool>::Ok(true); @@ -913,6 +919,7 @@ namespace backend } backend::Ninja ninja; + ninja.addGlobalIncludeDirs(parentDependencyExportIncludeDirs); if(!projectGeneratedBinary.empty()) ninja.addDependency(projectGeneratedBinary); // TODO: Use same source file finder as in main.cpp @@ -955,6 +962,9 @@ namespace backend Result<ExecResult> runTestResult = exec(testExecutableName.c_str(), true); if(!runTestResult) return Result<bool>::Err(runTestResult); + + if(runTestResult.unwrap().exitCode != 0) + return Result<bool>::Err("Tests failed", runTestResult.unwrap().exitCode); } } @@ -970,11 +980,14 @@ namespace backend if(execResult.isOk()) { if(execResult.unwrap().exitCode == 0) + { + //printf("%s\n", execResult.unwrap().execStdout.c_str()); return Result<bool>::Ok(true); + } else - return Result<bool>::Err(execResult.unwrap().execStdout); + return Result<bool>::Err(""); } else - return Result<bool>::Err(execResult.getErrMsg()); + return Result<bool>::Err(""); } } diff --git a/backend/ninja/Ninja.hpp b/backend/ninja/Ninja.hpp index fc6842f..8411137 100644 --- a/backend/ninja/Ninja.hpp +++ b/backend/ninja/Ninja.hpp @@ -9,7 +9,6 @@ #include <string> #include <functional> - namespace backend { class Ninja @@ -24,19 +23,21 @@ namespace backend Ninja(); + void addGlobalIncludeDirs(const std::string &globalIncludeDirs); void addSourceFile(const char *filepath); void addTestSourceDir(const char *dir); void addDependency(const std::string &binaryFile); const std::vector<std::string>& getSourceFiles() const; sibs::Result<bool> build(const sibs::SibsConfig &config, const _tinydir_char_t *savePath, sibs::LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc = nullptr, sibs::LinkerFlagCallbackFunc dynamicLinkerFlagCallback = nullptr, sibs::GlobalIncludeDirCallbackFunc globalIncludeDirCallback = nullptr); private: - sibs::Result<bool> buildTests(const std::string &projectGeneratedBinary, const sibs::SibsConfig &config, const _tinydir_char_t *savePath); + sibs::Result<bool> buildTests(const std::string &projectGeneratedBinary, const sibs::SibsConfig &config, const _tinydir_char_t *savePath, const std::string &parentDependencyExportIncludeDirs); bool containsSourceFile(const std::string &filepath) const; bool containsTestSourceDir(const std::string &dir) const; bool containsDependency(const std::string &dependency) const; sibs::Result<bool> getLinkerFlags(const sibs::SibsConfig &config, sibs::LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, sibs::LinkerFlagCallbackFunc dynamicLinkerFlagCallback, sibs::GlobalIncludeDirCallbackFunc globalIncludeDirCallback, sibs::CflagsCallbackFunc cflagsCallbackFunc) const; sibs::Result<bool> compile(const _tinydir_char_t *buildFilePath); private: + std::string customGlobalIncludeDirs; std::vector<std::string> sourceFiles; std::vector<std::string> testSourceDirs; std::vector<std::string> binaryDependencies; diff --git a/include/Conf.hpp b/include/Conf.hpp index 90f150a..13245c1 100644 --- a/include/Conf.hpp +++ b/include/Conf.hpp @@ -228,7 +228,9 @@ namespace sibs } } - virtual ~SibsConfig(){} + SibsConfig operator=(SibsConfig &other) = delete; + + virtual ~SibsConfig(); Compiler getCompiler() const { @@ -257,9 +259,14 @@ namespace sibs this->testPath = testPath; } - virtual const std::vector<Dependency>& getDependencies() const + virtual const std::vector<PackageListDependency*>& getPackageListDependencies() const + { + return packageListDependencies; + } + + virtual const std::vector<GitDependency*>& getGitDependencies() const { - return dependencies; + return gitDependencies; } virtual const FileString& getProjectPath() const @@ -380,6 +387,7 @@ namespace sibs 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; @@ -399,7 +407,8 @@ namespace sibs std::string packageName; FileString testPath; PackageType packageType; - std::vector<Dependency> dependencies; + std::vector<PackageListDependency*> packageListDependencies; + std::vector<GitDependency*> gitDependencies; std::vector<std::string> includeDirs; std::vector<std::string> exposeIncludeDirs; std::vector<Platform> platforms; diff --git a/include/Dependency.hpp b/include/Dependency.hpp index b97c362..7c8bbf1 100644 --- a/include/Dependency.hpp +++ b/include/Dependency.hpp @@ -2,15 +2,66 @@ #define SIBS_DEPENDENCY_HPP #include <string> +#include <cassert> namespace sibs { + class PackageListDependency; + class GitDependency; + class Dependency { public: + enum class Source + { + PACKAGE_LIST, + GIT + }; + + virtual ~Dependency(){} + + virtual Source getSource() const = 0; + + const PackageListDependency* asPackageListDependency() const + { + assert(getSource() == Source::PACKAGE_LIST); + return (PackageListDependency*)this; + } + + const GitDependency* asGitDependency() const + { + assert(getSource() == Source::GIT); + return (GitDependency*)this; + } + }; + + class PackageListDependency : public Dependency + { + public: + virtual ~PackageListDependency(){} + virtual Source getSource() const + { + return Source::PACKAGE_LIST; + } + std::string name; std::string version; }; + + class GitDependency : public Dependency + { + public: + virtual ~GitDependency(){} + virtual Source getSource() const + { + return Source::GIT; + } + + std::string name; + std::string url; + std::string branch; + std::string revision; + }; } #endif //SIBS_DEPENDENCY_HPP diff --git a/include/GitRepository.hpp b/include/GitRepository.hpp new file mode 100644 index 0000000..98399a0 --- /dev/null +++ b/include/GitRepository.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "Result.hpp" +#include "FileUtil.hpp" + +namespace sibs +{ + class GitDependency; + + class GitRepository + { + public: + static Result<bool> clone(GitDependency *gitDependency, const FileString &repoDirPath); + static Result<bool> pull(GitDependency *gitDependency, const FileString &repoDirPath); + }; +} diff --git a/include/GlobalLib.hpp b/include/GlobalLib.hpp index e0cc25c..8647a65 100644 --- a/include/GlobalLib.hpp +++ b/include/GlobalLib.hpp @@ -3,11 +3,13 @@ #include "Result.hpp" #include "Linker.hpp" -#include "Dependency.hpp" #include "Conf.hpp" namespace sibs { + class PackageListDependency; + class GitDependency; + class GlobalLib { public: @@ -17,10 +19,14 @@ namespace sibs DEPENDENCY_VERSION_NO_MATCH = 20 }; - static Result<bool> getLibs(const std::vector<Dependency> &libs, const SibsConfig &parentConfig, const FileString &globalLibRootDir, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback); + static Result<bool> getLibs(const std::vector<PackageListDependency*> &libs, const SibsConfig &parentConfig, const FileString &globalLibRootDir, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback); static Result<bool> validatePackageExists(const FileString &globalLibRootDir, const std::string &name); static Result<bool> getLibsLinkerFlags(const SibsConfig &parentConfig, const FileString &globalLibRootDir, const std::string &name, const std::string &version, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback); - static Result<bool> downloadDependency(const Dependency &dependency); + static Result<bool> getLibsLinkerFlags(const SibsConfig &parentConfig, const FileString &globalLibRootDir, GitDependency *gitDependency, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback); + static Result<bool> downloadDependency(PackageListDependency *dependency); + static Result<bool> downloadDependency(GitDependency *dependency); + private: + static Result<bool> getLibsLinkerFlagsCommon(const SibsConfig &parentConfig, const FileString &packageDir, const std::string &dependencyName, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback); }; } diff --git a/include/PkgConfig.hpp b/include/PkgConfig.hpp index e41b216..43f7d91 100644 --- a/include/PkgConfig.hpp +++ b/include/PkgConfig.hpp @@ -4,12 +4,13 @@ #include "env.hpp" #if OS_FAMILY == OS_FAMILY_POSIX #include "Result.hpp" -#include "Dependency.hpp" #include <string> #include <vector> namespace sibs { + class PackageListDependency; + struct PkgConfigFlags { std::string linkerFlags; @@ -19,12 +20,12 @@ namespace sibs class PkgConfig { public: - static Result<bool> validatePkgConfigPackageVersionExists(const Dependency &dependency); + static Result<bool> validatePkgConfigPackageVersionExists(PackageListDependency *dependency); static Result<bool> validatePackageExists(const std::string &name); static Result<bool> validatePackageVersionAtLeast(const std::string &name, const std::string &version); - static Result<std::string> getDynamicLibsLinkerFlags(const std::vector<Dependency> &libs); - static Result<std::string> getDynamicLibsCflags(const std::vector<Dependency> &libs); - static Result<PkgConfigFlags> getDynamicLibsFlags(const std::vector<Dependency> &libs); + static Result<std::string> getDynamicLibsLinkerFlags(const std::vector<PackageListDependency*> &libs); + static Result<std::string> getDynamicLibsCflags(const std::vector<PackageListDependency*> &libs); + static Result<PkgConfigFlags> getDynamicLibsFlags(const std::vector<PackageListDependency*> &libs); }; } #endif // OS_FAMILY_POSIX diff --git a/src/CmakeModule.cpp b/src/CmakeModule.cpp index 106f2c6..35e4884 100644 --- a/src/CmakeModule.cpp +++ b/src/CmakeModule.cpp @@ -62,10 +62,10 @@ namespace sibs // CMakeLists.txt may contain: set(CMAKE_MODULE_PATH "PathToModules"). This needs to be replaced with list append, // otherwise our added module path is replaced. // It may work to do like vcpkg instead - to use -DCMAKE_TOOLCHAIN_FILE program argument to specify path to script (https://github.com/Microsoft/vcpkg/blob/master/docs/examples/using-sqlite.md) - vector<Dependency> globalLibDependencies; + vector<PackageListDependency*> globalLibDependencies; #if OS_FAMILY == OS_FAMILY_POSIX - vector<Dependency> pkgConfigDependencies; - for(const Dependency &dependency : config.getDependencies()) + vector<PackageListDependency*> pkgConfigDependencies; + for(PackageListDependency *dependency : config.getPackageListDependencies()) { Result<bool> pkgConfigDependencyValidation = PkgConfig::validatePkgConfigPackageVersionExists(dependency); if(pkgConfigDependencyValidation.isOk()) @@ -84,7 +84,7 @@ namespace sibs { printf("%s, using global lib...\n", pkgConfigLinkerFlagsResult.getErrMsg().c_str()); globalLibDependencies.reserve(globalLibDependencies.size() + pkgConfigDependencies.size()); - for (const Dependency &pkgConfigDependency : pkgConfigDependencies) + for (PackageListDependency *pkgConfigDependency : pkgConfigDependencies) { globalLibDependencies.push_back(pkgConfigDependency); } diff --git a/src/Conf.cpp b/src/Conf.cpp index 93ec6e7..41cc157 100644 --- a/src/Conf.cpp +++ b/src/Conf.cpp @@ -528,6 +528,22 @@ namespace sibs return true; } + SibsConfig::~SibsConfig() + { + // TODO: Fix this shit.. why does this cause segfault? + /* + for(PackageListDependency *dependency : packageListDependencies) + { + //delete dependency; + } + + for(GitDependency *dependency : gitDependencies) + { + //delete dependency; + } + */ + } + bool SibsConfig::isDefined(const std::string &name) const { return defines.find(name) != defines.end(); @@ -794,16 +810,7 @@ namespace sibs } else if(currentObject.equals("dependencies")) { - if(value.isSingle()) - { - // TODO: Validate version is number in correct format - Dependency dependency; - dependency.name = string(name.data, name.size); - dependency.version = string(value.asSingle().data, value.asSingle().size); - dependencies.emplace_back(dependency); - } - else - throw ParserException("Expected field under dependencies to be a single value, was a list"); + parseDependencies(name, value); } else if(currentObject.equals("define")) { @@ -897,6 +904,97 @@ namespace sibs } } + void SibsConfig::parseDependencies(const StringView &name, const ConfigValue &value) + { + if(value.isSingle()) + { + // TODO: Validate version is number in correct format + PackageListDependency *dependency = new PackageListDependency(); + dependency->name = string(name.data, name.size); + dependency->version = string(value.asSingle().data, value.asSingle().size); + packageListDependencies.emplace_back(dependency); + } + else if(value.isObject()) + { + enum class DepType + { + NONE, + GIT + }; + + DepType depType = DepType::NONE; + for(auto it : value.asObject()) + { + DepType fieldDepType = DepType::NONE; + if(it.first == "git" || it.first == "branch" || it.first == "revision") + fieldDepType = DepType::GIT; + + if(fieldDepType == DepType::NONE) + { + string errMsg = "Invalid dependency object field \""; + errMsg += it.first; + errMsg += "\""; + throw ParserException(errMsg); + } + + if(depType != DepType::NONE && fieldDepType != depType) + { + switch(depType) + { + case DepType::GIT: + { + string errMsg = "Invalid dependency object field \""; + errMsg += it.first; + errMsg += "\" is invalid for a git dependency"; + throw ParserException(errMsg); + } + default: + throw ParserException("Invalid dependency object"); + } + } + depType = fieldDepType; + } + + switch(depType) + { + case DepType::GIT: + { + auto gitIt = value.asObject().find("git"); + if(gitIt == value.asObject().end()) + { + throw ParserException("Required field \"git\" is missing from git dependency. Expected an url to location of git repository"); + } + + auto branchIt = value.asObject().find("branch"); + string branch; + if(branchIt == value.asObject().end()) + branch = "master"; + else + branch = string(branchIt->second.data, branchIt->second.size); + + auto revisionIt = value.asObject().find("revision"); + string revision; + if(revisionIt == value.asObject().end()) + revision = "HEAD"; + else + revision = string(revisionIt->second.data, revisionIt->second.size); + + GitDependency *dependency = new GitDependency(); + dependency->name = string(name.data, name.size); + dependency->url = string(gitIt->second.data, gitIt->second.size); + dependency->branch = branch; + dependency->revision = revision; + gitDependencies.emplace_back(dependency); + break; + } + default: + throw ParserException("Invalid dependency object"); + } + } + else + throw ParserException("Expected field under dependencies to be a single value or an object, was a list"); + } + void SibsConfig::parseCLang(const StringView &fieldName, const ConfigValue &fieldValue) { if(fieldName.equals("version")) @@ -1183,16 +1281,7 @@ namespace sibs { if(currentObject.equals("dependencies")) { - if(value.isSingle()) - { - // TODO: Validate version is number in correct format - Dependency dependency; - dependency.name = string(name.data, name.size); - dependency.version = string(value.asSingle().data, value.asSingle().size); - dependencies.emplace_back(dependency); - } - else - throw ParserException("Expected field under dependencies to be a single value, was a list"); + parseDependencies(name, value); } else { diff --git a/src/GitRepository.cpp b/src/GitRepository.cpp new file mode 100644 index 0000000..0a3b0a7 --- /dev/null +++ b/src/GitRepository.cpp @@ -0,0 +1,101 @@ +#include "../include/GitRepository.hpp" +#include "../include/Dependency.hpp" +#include <git2.h> +#include <string> +#include <cassert> + +using namespace std; + +static bool libgitInitialized = false; + +namespace sibs +{ + void gitInit() + { + if(!libgitInitialized) + { + libgitInitialized = true; + // TODO: Call git_libgit2_shutdown in destructor? dont really need to do that though + git_libgit2_init(); + } + } + + Result<bool> buildGitError(int error, const string &errorPrefix) + { + const git_error *e = giterr_last(); + string errMsg = errorPrefix; + errMsg += ": "; + errMsg += to_string(error); + errMsg += "/"; + errMsg += to_string(e->klass); + errMsg += ": "; + errMsg += e->message; + return Result<bool>::Err(errMsg, error); + } + + Result<bool> GitRepository::clone(GitDependency *gitDependency, const FileString &repoDirPath) + { + gitInit(); + git_repository *repo; + int error = git_clone(&repo, gitDependency->url.c_str(), toUtf8(repoDirPath).c_str(), NULL); + if(error != 0) + return buildGitError(error, "Failed to clone git repository"); + + git_repository_free(repo); + return Result<bool>::Ok(true); + } + + Result<bool> GitRepository::pull(GitDependency *gitDependency, const FileString &repoDirPath) + { + gitInit(); + int error; + + git_repository *repo; + error = git_repository_open(&repo, toUtf8(repoDirPath).c_str()); + if(error != 0) + return buildGitError(error, "Failed to open git repository"); + + git_remote *remote; + error = git_remote_lookup(&remote, repo, "origin"); + if(error != 0) + return buildGitError(error, "Failed to do remote lookup for git repository"); + + // TODO: Setup option to be able to use callback for progress (for output in console) and handling credentials + error = git_remote_fetch(remote, NULL, NULL, "pull"); + if(error != 0) + { + Result<bool> err = buildGitError(error, "Failed to do remote fetch for git repository"); + git_remote_free(remote); + git_repository_free(repo); + return err; + } + + git_reference *ref; + error = git_reference_dwim(&ref, repo, gitDependency->branch.c_str()); + if(error != 0) + { + Result<bool> err = buildGitError(error, "Failed to do reference lookup for git repository"); + git_remote_free(remote); + git_repository_free(repo); + return err; + } + + git_annotated_commit *annotatedCommit; + error = git_annotated_commit_from_ref(&annotatedCommit, repo, ref); + if(error != 0) + { + Result<bool> err = buildGitError(error, "Failed to get commit from ref"); + git_annotated_commit_free(annotatedCommit); + git_remote_free(remote); + git_repository_free(repo); + return err; + } + + assert(false); + + git_annotated_commit_free(annotatedCommit); + git_remote_free(remote); + git_repository_free(repo); + return Result<bool>::Ok(true); + } +} diff --git a/src/GlobalLib.cpp b/src/GlobalLib.cpp index 3842388..eee37dd 100644 --- a/src/GlobalLib.cpp +++ b/src/GlobalLib.cpp @@ -5,6 +5,8 @@ #include "../include/curl.hpp" #include "../include/Archive.hpp" #include "../include/CmakeModule.hpp" +#include "../include/Dependency.hpp" +#include "../include/GitRepository.hpp" using namespace std; @@ -65,13 +67,13 @@ namespace sibs return _tinydir_strncmp(path, subPathOf.c_str(), subPathOf.size()) == 0; } - Result<bool> GlobalLib::getLibs(const std::vector<Dependency> &libs, const SibsConfig &parentConfig, const FileString &globalLibRootDir, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback) + Result<bool> GlobalLib::getLibs(const std::vector<PackageListDependency*> &libs, const SibsConfig &parentConfig, const FileString &globalLibRootDir, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback) { - for(const Dependency &globalLibDependency : libs) + for(PackageListDependency *globalLibDependency : libs) { - printf("Dependency %s is missing from pkg-config, trying global lib\n", globalLibDependency.name.c_str()); - Result<bool> globalLibLinkerFlagsResult = GlobalLib::getLibsLinkerFlags(parentConfig, globalLibRootDir, globalLibDependency.name, globalLibDependency.version, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); - if(globalLibLinkerFlagsResult.isErr()) + printf("Dependency %s is missing from pkg-config, trying global lib\n", globalLibDependency->name.c_str()); + Result<bool> globalLibLinkerFlagsResult = GlobalLib::getLibsLinkerFlags(parentConfig, globalLibRootDir, globalLibDependency->name, globalLibDependency->version, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); + if(!globalLibLinkerFlagsResult) { if(globalLibLinkerFlagsResult.getErrorCode() == GlobalLib::DependencyError::DEPENDENCY_NOT_FOUND || globalLibLinkerFlagsResult.getErrorCode() == GlobalLib::DependencyError::DEPENDENCY_VERSION_NO_MATCH) { @@ -86,16 +88,48 @@ namespace sibs // invalid package name/version. A check should be done if it is the name or version // that is invalid. Result<bool> downloadDependencyResult = GlobalLib::downloadDependency(globalLibDependency); - if(downloadDependencyResult.isErr()) + if(!downloadDependencyResult) return downloadDependencyResult; - globalLibLinkerFlagsResult = GlobalLib::getLibsLinkerFlags(parentConfig, globalLibRootDir, globalLibDependency.name, globalLibDependency.version, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); - if(globalLibLinkerFlagsResult.isErr()) - return Result<bool>::Err(globalLibLinkerFlagsResult); + globalLibLinkerFlagsResult = GlobalLib::getLibsLinkerFlags(parentConfig, globalLibRootDir, globalLibDependency->name, globalLibDependency->version, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); + if(!globalLibLinkerFlagsResult) + return globalLibLinkerFlagsResult; } else { - return Result<bool>::Err(globalLibLinkerFlagsResult); + return globalLibLinkerFlagsResult; + } + } + } + + for(GitDependency *gitDependency : parentConfig.getGitDependencies()) + { + Result<bool> gitLibLinkerFlagsResult = GlobalLib::getLibsLinkerFlags(parentConfig, globalLibRootDir, gitDependency, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); + if(!gitLibLinkerFlagsResult) + { + if(gitLibLinkerFlagsResult.getErrorCode() == GlobalLib::DependencyError::DEPENDENCY_NOT_FOUND) + { + printf("Dependency %s not found in global lib, trying to download from git\n", gitDependency->name.c_str()); + // TODO: Download several dependencies at the same time by adding them to a list + // and then iterate them and download them all using several threads. + // All dependecies should be downloaded at the same time, this includes dependencies of dependencies. + // If a dependency is missing, fail build BEFORE downloading dependencies and before compiling anything. + // You do not want to possibly wait several minutes only for build to fail when there is no compilation error. + + // TODO: If return error is invalid url, then the message should be converted to + // invalid package name/version. A check should be done if it is the name or version + // that is invalid. + Result<bool> downloadDependencyResult = GlobalLib::downloadDependency(gitDependency); + if(!downloadDependencyResult) + return downloadDependencyResult; + + gitLibLinkerFlagsResult = GlobalLib::getLibsLinkerFlags(parentConfig, globalLibRootDir, gitDependency, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); + if(!gitLibLinkerFlagsResult) + return gitLibLinkerFlagsResult; + } + else + { + return gitLibLinkerFlagsResult; } } } @@ -138,28 +172,53 @@ namespace sibs packageDir += TINYDIR_STRING("/"); packageDir += versionPlatformNative; + return GlobalLib::getLibsLinkerFlagsCommon(parentConfig, packageDir, name, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); + } + + Result<bool> GlobalLib::getLibsLinkerFlags(const SibsConfig &parentConfig, const FileString &globalLibRootDir, GitDependency *gitDependency, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback) + { + Result<bool> packageExistsResult = validatePackageExists(globalLibRootDir, gitDependency->name); + if (packageExistsResult.isErr()) + return packageExistsResult; + +#if OS_FAMILY == OS_FAMILY_POSIX + FileString namePlatformNative = gitDependency->name; + FileString versionPlatformNative = gitDependency->revision; +#else + FileString namePlatformNative = utf8To16(name); + FileString versionPlatformNative = utf8To16(version); +#endif + + FileString packageDir = globalLibRootDir + TINYDIR_STRING("/"); + packageDir += namePlatformNative; + packageDir += TINYDIR_STRING("/"); + packageDir += versionPlatformNative; + + // TODO: Check path is valid git repository by using git_repository_open_ext + + // TODO: Pull if revision == HEAD, fail build if there are conflicts. + // TODO: When building a sibs project, put a symlink in libs directory. + // This allows you to have dependency on a project and make changes to it without pushing + // to remote before the dependant project can see the changes. + //GitRepository gitRepo; + //gitRepo.pull(gitDependency, packageDir); + + return GlobalLib::getLibsLinkerFlagsCommon(parentConfig, packageDir, gitDependency->name, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); + } + + Result<bool> GlobalLib::getLibsLinkerFlagsCommon(const SibsConfig &parentConfig, const FileString &packageDir, const string &dependencyName, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback) + { FileString projectConfFilePath = packageDir; projectConfFilePath += TINYDIR_STRING("/project.conf"); FileType projectConfFileType = getFileType(projectConfFilePath.c_str()); - switch(projectConfFileType) + if(projectConfFileType != FileType::REGULAR) { - case FileType::FILE_NOT_FOUND: - { - string errMsg = "Global lib dependency found: "; - errMsg += toUtf8(packageDir); - errMsg += ", but it's missing a project.conf file"; - return Result<bool>::Err(errMsg); - } - case FileType::DIRECTORY: - { - string errMsg = "Global lib dependency found: "; - errMsg += toUtf8(packageDir); - errMsg += ", but it's corrupt (Found directory instead of file)"; - return Result<bool>::Err(errMsg); - } + string errMsg = "Dependency not found: "; + errMsg += toUtf8(packageDir); + return Result<bool>::Err(errMsg, DependencyError::DEPENDENCY_NOT_FOUND); } - + SibsConfig sibsConfig(parentConfig.getCompiler(), packageDir, parentConfig.getOptimizationLevel(), false); Result<bool> result = Config::readFromFile(projectConfFilePath.c_str(), sibsConfig); if (result.isErr()) @@ -171,7 +230,7 @@ namespace sibs if(sibsConfig.getPackageType() == PackageType::EXECUTABLE) { string errMsg = "The dependency "; - errMsg += name; + errMsg += dependencyName; errMsg += " is an executable. Only libraries can be dependencies"; return Result<bool>::Err(errMsg); } @@ -179,7 +238,7 @@ namespace sibs if (!containsPlatform(sibsConfig.getPlatforms(), SYSTEM_PLATFORM)) { string errMsg = "The dependency "; - errMsg += name; + errMsg += dependencyName; errMsg += " does not support your platform ("; errMsg += asString(SYSTEM_PLATFORM); errMsg += ")"; @@ -268,7 +327,7 @@ namespace sibs case Compiler::GCC: { libPath += "/lib"; - libPath += name; + libPath += dependencyName; if (sibsConfig.getPackageType() == PackageType::STATIC) { libPath += ".a"; @@ -290,7 +349,7 @@ namespace sibs case Compiler::MSVC: { libPath += "/"; - libPath += name; + libPath += dependencyName; if (sibsConfig.getPackageType() == PackageType::STATIC) { libPath += ".lib"; @@ -316,9 +375,9 @@ namespace sibs } } - Result<bool> GlobalLib::downloadDependency(const Dependency &dependency) + Result<bool> GlobalLib::downloadDependency(PackageListDependency *dependency) { - Result<string> packageUrlResult = Package::getPackageUrl(dependency.name.c_str(), dependency.version.c_str(), SYSTEM_PLATFORM_NAME); + Result<string> packageUrlResult = Package::getPackageUrl(dependency->name.c_str(), dependency->version.c_str(), SYSTEM_PLATFORM_NAME); if(!packageUrlResult) return Result<bool>::Err(packageUrlResult); @@ -329,28 +388,49 @@ namespace sibs return Result<bool>::Err(libPathResult); FileString libPath = libPathResult.unwrap(); libPath += TINYDIR_STRING("/.sibs/lib/"); - libPath += toFileString(dependency.name); + libPath += toFileString(dependency->name); libPath += TINYDIR_STRING("/"); - libPath += toFileString(dependency.version); + libPath += toFileString(dependency->version); FileString libArchivedFilePath = libPathResult.unwrap(); libArchivedFilePath += TINYDIR_STRING("/.sibs/archive/"); - libArchivedFilePath += toFileString(dependency.name); + libArchivedFilePath += toFileString(dependency->name); Result<bool> createArchiveDirResult = createDirectoryRecursive(libArchivedFilePath.c_str()); - if(createArchiveDirResult.isErr()) + if(!createArchiveDirResult) return createArchiveDirResult; libArchivedFilePath += TINYDIR_STRING("/"); - libArchivedFilePath += toFileString(dependency.version); + libArchivedFilePath += toFileString(dependency->version); Result<bool> downloadResult = curl::downloadFile(url.c_str(), libArchivedFilePath.c_str()); - if(downloadResult.isErr()) + if(!downloadResult) return downloadResult; // Create build path. This is done here because we dont want to create it if download fails Result<bool> createLibDirResult = createDirectoryRecursive(libPath.c_str()); - if(createLibDirResult.isErr()) + if(!createLibDirResult) return createLibDirResult; return Archive::extract(libArchivedFilePath.c_str(), libPath.c_str()); } + + Result<bool> GlobalLib::downloadDependency(GitDependency *dependency) + { + Result<FileString> libPathResult = getHomeDir(); + if (!libPathResult) + return Result<bool>::Err(libPathResult); + FileString libPath = libPathResult.unwrap(); + libPath += TINYDIR_STRING("/.sibs/lib/"); + libPath += toFileString(dependency->name); + + // We dont care if the directory already exists. Nothing will happen if it does + Result<bool> createLibDirResult = createDirectoryRecursive(libPath.c_str()); + if(!createLibDirResult) + return createLibDirResult; + + libPath += TINYDIR_STRING("/"); + libPath += toFileString(dependency->revision); + + GitRepository gitRepo; + return gitRepo.clone(dependency, libPath); + } } diff --git a/src/PkgConfig.cpp b/src/PkgConfig.cpp index 9a19387..42d0d13 100644 --- a/src/PkgConfig.cpp +++ b/src/PkgConfig.cpp @@ -1,6 +1,7 @@ #include "../include/PkgConfig.hpp" #if OS_FAMILY == OS_FAMILY_POSIX #include "../include/Exec.hpp" +#include "../include/Dependency.hpp" using namespace std; @@ -19,20 +20,18 @@ namespace sibs return ""; } -#if OS_FAMILY == OS_FAMILY_POSIX - Result<bool> PkgConfig::validatePkgConfigPackageVersionExists(const Dependency &dependency) + Result<bool> PkgConfig::validatePkgConfigPackageVersionExists(PackageListDependency *dependency) { - Result<bool> dependencyValidationResult = PkgConfig::validatePackageExists(dependency.name); + Result<bool> dependencyValidationResult = PkgConfig::validatePackageExists(dependency->name); if(dependencyValidationResult.isErr()) return Result<bool>::Err(dependencyValidationResult.getErrMsg()); - Result<bool> dependencyVersionValidationResult = PkgConfig::validatePackageVersionAtLeast(dependency.name, dependency.version); + Result<bool> dependencyVersionValidationResult = PkgConfig::validatePackageVersionAtLeast(dependency->name, dependency->version); if(dependencyVersionValidationResult.isErr()) return Result<bool>::Err(dependencyVersionValidationResult.getErrMsg()); return Result<bool>::Ok(true); } -#endif Result<bool> PkgConfig::validatePackageExists(const string &name) { @@ -101,15 +100,15 @@ namespace sibs return Result<bool>::Ok(true); } - Result<string> PkgConfig::getDynamicLibsLinkerFlags(const vector<Dependency> &libs) + Result<string> PkgConfig::getDynamicLibsLinkerFlags(const vector<PackageListDependency*> &libs) { if(libs.empty()) return Result<string>::Ok(""); string args; - for(const Dependency &lib : libs) + for(PackageListDependency *lib : libs) { args += " '"; - args += lib.name; + args += lib->name; args += "'"; } @@ -144,15 +143,15 @@ namespace sibs } } - Result<string> PkgConfig::getDynamicLibsCflags(const vector<Dependency> &libs) + Result<string> PkgConfig::getDynamicLibsCflags(const vector<PackageListDependency*> &libs) { if(libs.empty()) return Result<string>::Ok(""); string args; - for(const Dependency &lib : libs) + for(PackageListDependency *lib : libs) { args += " '"; - args += lib.name; + args += lib->name; args += "'"; } @@ -187,7 +186,7 @@ namespace sibs } } - Result<PkgConfigFlags> PkgConfig::getDynamicLibsFlags(const vector<Dependency> &libs) + Result<PkgConfigFlags> PkgConfig::getDynamicLibsFlags(const vector<PackageListDependency*> &libs) { PkgConfigFlags flags; diff --git a/src/main.cpp b/src/main.cpp index a2e6565..dcc38d1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -59,6 +59,20 @@ using namespace std::chrono; // TODO: Make dependency/project names case insensitive. This means we can't use pkgconfig +// TODO: Fail build if dependency requires newer language version than dependant package. +// To make it work properly, should language version be required in project.conf? + +// TODO: Remove duplicate compiler options (include flags, linker flags etc...) to improve compilation speed. +// The compiler ignores duplicate symbols but it's faster to just remove duplicate options because we only have +// to compare strings. Duplicate options can happen if for example a project has two dependencies and both dependencies +// have dependency on the same package (would be common for example with boost libraries or libraries that dpepend on boost) + +// TODO: Create symlink to dependencies, for example if we have dependency on xxhash which has xxhash.h in its root directory, +// then you should be able to include it from dependant project by typing #include "xxhash/xxhash.h" +// that means we create a symlink with the dependency name to the dependency version directory. +// This will make it easier to prevent clashes in header file names and it's easier to see from the include statement +// what exactly we are including + #if OS_FAMILY == OS_FAMILY_POSIX #define ferr std::cerr #else @@ -503,7 +517,8 @@ int newProject(int argc, const _tinydir_char_t **argv) newProjectCreateConf(projectName, projectTypeConf, projectPath); createProjectSubDir(projectPath + TINYDIR_STRING("/src")); createProjectSubDir(projectPath + TINYDIR_STRING("/include")); - createProjectFile(projectPath + TINYDIR_STRING("/src/main.cpp"), "#include <cstdio>\n\nint main()\n{\n printf(\"hello, world!\\n\");\n return 0;\n}\n"); + createProjectSubDir(projectPath + TINYDIR_STRING("/tests")); + createProjectFile(projectPath + TINYDIR_STRING("/src/main.cpp"), "#include <cstdio>\n\nint main(int argc, char **argv)\n{\n printf(\"hello, world!\\n\");\n return 0;\n}\n"); // We are ignoring git init result on purpose. If it fails, just ignore it; not important gitInitProject(projectPath); return 0; diff --git a/tests/src/confTest/confTest.cpp b/tests/src/confTest/confTest.cpp index d24c460..5b0fd0f 100644 --- a/tests/src/confTest/confTest.cpp +++ b/tests/src/confTest/confTest.cpp @@ -1,4 +1,4 @@ -#include "catch2/2.0.1/catch.hpp" +#include "catch.hpp" #include "../../../include/Conf.hpp" using namespace sibs; @@ -19,14 +19,30 @@ TEST_CASE("parse config") REQUIRE(containsPlatform(sibsConfig.getPlatforms(), PLATFORM_LINUX64)); REQUIRE(containsPlatform(sibsConfig.getPlatforms(), PLATFORM_WIN64)); - REQUIRE(sibsConfig.getDependencies().size() == 2); - const auto &xxhashDependency = sibsConfig.getDependencies()[0]; - REQUIRE(xxhashDependency.name == "xxhash"); - REQUIRE(xxhashDependency.version == "0.1.0"); + REQUIRE(sibsConfig.getPackageListDependencies().size() == 2); + for(auto *dep : sibsConfig.getPackageListDependencies()) + { + REQUIRE(dep->getSource() == Dependency::Source::PACKAGE_LIST); + } + auto *xxhashDependency = sibsConfig.getPackageListDependencies()[0]->asPackageListDependency(); + REQUIRE(xxhashDependency->name == "xxhash"); + REQUIRE(xxhashDependency->version == "0.1.0"); - const auto &catch2Dependency = sibsConfig.getDependencies()[1]; - REQUIRE(catch2Dependency.name == "catch2"); - REQUIRE(catch2Dependency.version == "1.0.0"); + const auto &catch2Dependency = sibsConfig.getPackageListDependencies()[1]->asPackageListDependency(); + REQUIRE(catch2Dependency->name == "catch2"); + REQUIRE(catch2Dependency->version == "1.0.0"); + + REQUIRE(sibsConfig.getGitDependencies().size() == 1); + for(auto *dep : sibsConfig.getGitDependencies()) + { + REQUIRE(dep->getSource() == Dependency::Source::GIT); + } + const Dependency *sfmlAllDependency = sibsConfig.getGitDependencies()[0]; + const GitDependency *sfmlAllDependencyGit = sfmlAllDependency->asGitDependency(); + REQUIRE(sfmlAllDependencyGit->name == "sfml-all"); + REQUIRE(sfmlAllDependencyGit->url == "https://github.com/DEC05EBA/sfml-all.git"); + REQUIRE(sfmlAllDependencyGit->branch == "master"); + REQUIRE(sfmlAllDependencyGit->revision == "HEAD"); REQUIRE(sibsConfig.shouldUseCmake()); @@ -104,14 +120,3 @@ TEST_CASE("parse config - define dynamic") REQUIRE(sibsConfig.getDefinedValue("BUILD_STATIC") == "0"); REQUIRE(sibsConfig.getDefinedValue("DEFINE_TYPE") == "DYNAMIC"); } - -TEST_CASE("parse config - git dependency") -{ - SibsConfig sibsConfig(Compiler::GCC, TINYDIR_STRING("tests/src/confTest"), OPT_LEV_DEBUG, false); - Result<bool> result = Config::readFromFile(TINYDIR_STRING("tests/src/confTest/gitDependency.conf"), sibsConfig); - if(result.isErr()) - { - fprintf(stderr, "%s", result.getErrMsg().c_str()); - exit(1); - } -} diff --git a/tests/src/confTest/gitDependency.conf b/tests/src/confTest/gitDependency.conf deleted file mode 100644 index d18b267..0000000 --- a/tests/src/confTest/gitDependency.conf +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "confTest" -version = "0.1.0" -type = "library" -platforms = ["linux64", "win64"] - -[dependencies] -xxhash = { git = "https://github.com/DEC05EBA/xxHash.git", branch = "master" } -catch2 = "1.0.0" diff --git a/tests/src/confTest/validProject.conf b/tests/src/confTest/validProject.conf index cd3f9cc..c5232c6 100644 --- a/tests/src/confTest/validProject.conf +++ b/tests/src/confTest/validProject.conf @@ -7,6 +7,7 @@ platforms = ["linux64", "win64"] [dependencies] xxhash = "0.1.0" # random comment at end of line catch2 = "1.0.0" +sfml-all = { git = "https://github.com/DEC05EBA/sfml-all.git", branch = "master" } # cmake building is currrently not implemented, but it is intended to work like this [cmake] |