From a548abb5a6a83c9318e9db3cf71170a7610e2758 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Thu, 4 Jan 2018 01:01:35 +0100 Subject: Use packages list to find packages --- src/CmakeModule.cpp | 97 ++++++++++++++++++++++++++++++++------------ src/GlobalLib.cpp | 47 +++++++++++++++++++--- src/Package.cpp | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/PkgConfig.cpp | 15 +++++++ 4 files changed, 242 insertions(+), 31 deletions(-) create mode 100644 src/Package.cpp (limited to 'src') diff --git a/src/CmakeModule.cpp b/src/CmakeModule.cpp index 7d5509c..d82eaf6 100644 --- a/src/CmakeModule.cpp +++ b/src/CmakeModule.cpp @@ -1,6 +1,7 @@ #include "../include/CmakeModule.hpp" #include "../include/Exec.hpp" #include "../include/GlobalLib.hpp" +#include "../include/PkgConfig.hpp" #if OS_FAMILY == OS_FAMILY_POSIX #define nprintf printf @@ -23,42 +24,86 @@ namespace sibs if(createGlobalLibDirResult.isErr()) return createGlobalLibDirResult; +// TODO: This code was copied from Ninja.cpp, convert it to work for CMake +#if OS_TYPE == OS_TYPE_LINUX + // TODO: Allow configuring default linking flags. Maybe have `package.useThreads = false` to disable this flag + string allLinkerFlags = "-pthread"; +#else + string allLinkerFlags = ""; +#endif + +#if 0 + // TODO: Somehow check loading order, because it has to be correct to work.. Or does it for dynamic libraries? + // Anyways it's required for static libraries (especially on Windows) + for (const string &binaryDependency : binaryDependencies) + { + allLinkerFlags += " "; + allLinkerFlags += binaryDependency; + } +#endif + if (!staticLinkerFlagCallbackFunc || (config.getPackageType() == PackageType::DYNAMIC || config.getPackageType() == PackageType::LIBRARY)) + { + staticLinkerFlagCallbackFunc = [&allLinkerFlags](const string &linkerFlag) + { + allLinkerFlags += " "; + allLinkerFlags += linkerFlag; + }; + } + + // TODO: If project contains no source files, then we shouldn't override this function + dynamicLinkerFlagCallbackFunc = [&allLinkerFlags](const string &linkerFlag) + { + allLinkerFlags += " "; + allLinkerFlags += linkerFlag; + }; + // TODO: Create a cmake module that contains library/include path for the dependencies (https://cmake.org/Wiki/CMake:How_To_Find_Libraries). // Modify the project CMakeLists.txt and add: list(APPEND CMAKE_MODULE_PATH "PathToDependenciesCmakeModulesGoesHere"). // 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) - for(const Dependency &globalLibDependency : config.getDependencies()) + vector globalLibDependencies; +#if OS_FAMILY == OS_FAMILY_POSIX + vector pkgConfigDependencies; + for(const Dependency &dependency : config.getDependencies()) { - Result globalLibLinkerFlagsResult = GlobalLib::getLibsLinkerFlags(config, globalLibDir, globalLibDependency.name, globalLibDependency.version, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); - if(globalLibLinkerFlagsResult.isErr()) + Result pkgConfigDependencyValidation = PkgConfig::validatePkgConfigPackageVersionExists(dependency); + if(pkgConfigDependencyValidation.isOk()) { - if(globalLibLinkerFlagsResult.getErrorCode() == GlobalLib::DependencyError::DEPENDENCY_NOT_FOUND || globalLibLinkerFlagsResult.getErrorCode() == GlobalLib::DependencyError::DEPENDENCY_VERSION_NO_MATCH) - { - printf("Dependency not found in global lib, trying to download from github\n"); - // 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 downloadDependencyResult = GlobalLib::downloadDependency(globalLibDependency); - if(downloadDependencyResult.isErr()) - return downloadDependencyResult; + pkgConfigDependencies.push_back(dependency); + } + else + { + globalLibDependencies.push_back(dependency); + } + } - globalLibLinkerFlagsResult = GlobalLib::getLibsLinkerFlags(config, globalLibDir, globalLibDependency.name, globalLibDependency.version, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); - if(globalLibLinkerFlagsResult.isErr()) - return Result::Err(globalLibLinkerFlagsResult); - } - else - { - return Result::Err(globalLibLinkerFlagsResult); - } + Result pkgConfigLinkerFlagsResult = PkgConfig::getDynamicLibsLinkerFlags(pkgConfigDependencies); + if (pkgConfigLinkerFlagsResult.isErr()) + { + printf("%s, using global lib...\n", pkgConfigLinkerFlagsResult.getErrMsg().c_str()); + globalLibDependencies.reserve(globalLibDependencies.size() + pkgConfigDependencies.size()); + for (const Dependency &pkgConfigDependency : pkgConfigDependencies) + { + globalLibDependencies.push_back(pkgConfigDependency); } + pkgConfigDependencies.clear(); } + else + { + if (!pkgConfigLinkerFlagsResult.unwrap().empty()) + dynamicLinkerFlagCallbackFunc(pkgConfigLinkerFlagsResult.unwrap()); + } +#else + for (const Dependency &dependency : dependencies) + { + globalLibDependencies.push_back(dependency); + } +#endif + + Result globalLibResult = GlobalLib::getLibs(globalLibDependencies, config, globalLibDir, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); + if(!globalLibResult) + return globalLibResult; Result createBuildDirResult = createDirectoryRecursive(buildPath.c_str()); if (createBuildDirResult.isErr()) diff --git a/src/GlobalLib.cpp b/src/GlobalLib.cpp index d3131a1..0256685 100644 --- a/src/GlobalLib.cpp +++ b/src/GlobalLib.cpp @@ -64,6 +64,43 @@ namespace sibs { return _tinydir_strncmp(path, subPathOf.c_str(), subPathOf.size()) == 0; } + + Result GlobalLib::getLibs(const std::vector &libs, const SibsConfig &parentConfig, const FileString &globalLibRootDir, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback) + { + for(const Dependency &globalLibDependency : libs) + { + printf("Dependency %s is missing from pkg-config, trying global lib\n", globalLibDependency.name.c_str()); + Result globalLibLinkerFlagsResult = GlobalLib::getLibsLinkerFlags(parentConfig, globalLibRootDir, globalLibDependency.name, globalLibDependency.version, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); + if(globalLibLinkerFlagsResult.isErr()) + { + if(globalLibLinkerFlagsResult.getErrorCode() == GlobalLib::DependencyError::DEPENDENCY_NOT_FOUND || globalLibLinkerFlagsResult.getErrorCode() == GlobalLib::DependencyError::DEPENDENCY_VERSION_NO_MATCH) + { + printf("Dependency not found in global lib, trying to download from github\n"); + // 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 downloadDependencyResult = GlobalLib::downloadDependency(globalLibDependency); + if(downloadDependencyResult.isErr()) + return downloadDependencyResult; + + globalLibLinkerFlagsResult = GlobalLib::getLibsLinkerFlags(parentConfig, globalLibRootDir, globalLibDependency.name, globalLibDependency.version, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); + if(globalLibLinkerFlagsResult.isErr()) + return Result::Err(globalLibLinkerFlagsResult); + } + else + { + return Result::Err(globalLibLinkerFlagsResult); + } + } + } + return Result::Ok(true); + } Result GlobalLib::getLibsLinkerFlags(const SibsConfig &parentConfig, const FileString &globalLibRootDir, const std::string &name, const std::string &version, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback) { @@ -281,11 +318,11 @@ namespace sibs Result GlobalLib::downloadDependency(const Dependency &dependency) { - string url = "https://github.com/DEC05EBA/"; - url += dependency.name; - url += "/archive/"; - url += dependency.version; - url += ".tar.gz"; + Result packageUrlResult = Package::getPackageUrl(dependency.name.c_str(), dependency.version.c_str()); + if(!packageUrlResult) + return Result::Err(packageUrlResult); + + const string &url = packageUrlResult.unwrap(); Result libPathResult = getHomeDir(); if (!libPathResult) diff --git a/src/Package.cpp b/src/Package.cpp new file mode 100644 index 0000000..c1c00be --- /dev/null +++ b/src/Package.cpp @@ -0,0 +1,114 @@ +#include "../include/Package.hpp" +#include "../include/curl.hpp" +#include "../external/rapidjson/error/en.h" + +using namespace std; +using namespace rapidjson; + +static Document *packageList = nullptr; + +namespace sibs +{ + // TODO: Always downloading is fine right now because the package list is small. This should later be modified to use local cache. + // The package file should be stored locally (at ~/.sibs/packages.json) and the version file should also be stored. + // First we check if the version is incorrect and if it, we download the new packages.json file. + // We should only check if the package list is up to date once every 10 minute or so (make it configurable in a config file?) + // to improve build performance and reduce server load. + // Or maybe we dont have to do that at all.... + Result Package::getPackageList(const char *url) + { + if(packageList) + return Result::Ok(packageList); + + HttpResult httpResult = curl::get(url); + if(!httpResult.success) + return Result::Err(httpResult.str, httpResult.httpCode); + + Document *doc = new Document(); + ParseResult parseResult = doc->Parse(httpResult.str.c_str()); + if(!parseResult) + { + string errMsg = "Failed to parse package list json file: "; + errMsg += GetParseError_En(parseResult.Code()); + errMsg += " ("; + errMsg += to_string(parseResult.Offset()); + errMsg += ")"; + return Result::Err(errMsg); + } + + packageList = doc; + return Result::Ok(packageList); + } + + Result Package::getPackageUrl(const char *packageName, const char *packageVersion) + { + Result packageList = Package::getPackageList("https://raw.githubusercontent.com/DEC05EBA/sibs_packages/master/packages.json"); + if(!packageList) + return Result::Err(packageList); + + const Document &packageDoc = *packageList.unwrap(); + const Value &packageMetaData = packageDoc[packageName]; + if(!packageMetaData.IsObject()) + { + string errMsg = "No package with the name \""; + errMsg += packageName; + errMsg += "\" was found"; + return Result::Err(errMsg); + } + + const Value &packageVersions = packageMetaData["versions"]; + if(!packageVersions.IsObject()) + { + string errMsg = "Package file is corrupt. "; + errMsg += packageName; + errMsg += ".versions is not a json object"; + return Result::Err("errMsg"); + } + + const Value &package = packageVersions[packageVersion]; + if(!package.IsObject()) + { + string errMsg = "Package file is corrupt. "; + errMsg += packageName; + errMsg += ".versions."; + errMsg += packageVersion; + errMsg += " is not a json object"; + return Result::Err(errMsg); + } + + const Value &packageUrlsValue = package["urls"]; + if(!packageUrlsValue.IsArray()) + { + string errMsg = "Package file is corrupt. "; + errMsg += packageName; + errMsg += ".versions."; + errMsg += packageVersion; + errMsg += ".urls is not a json array"; + return Result::Err(errMsg); + } + + auto packageUrls = packageUrlsValue.GetArray(); + if(packageUrls.Empty()) + { + string errMsg = "Package file is corrupt. "; + errMsg += packageName; + errMsg += ".versions."; + errMsg += packageVersion; + errMsg += ".urls is an empty json array"; + return Result::Err(errMsg); + } + + const Value &packageUrlValue = packageUrls[0]; + if(!packageUrlValue.IsString()) + { + string errMsg = "Package file is corrupt. "; + errMsg += packageName; + errMsg += ".versions."; + errMsg += packageVersion; + errMsg += ".urls[0] is not a string"; + return Result::Err(errMsg); + } + + return Result::Ok(string(packageUrlValue.GetString(), packageUrlValue.GetStringLength())); + } +} diff --git a/src/PkgConfig.cpp b/src/PkgConfig.cpp index a66d5b3..f9f8f9b 100644 --- a/src/PkgConfig.cpp +++ b/src/PkgConfig.cpp @@ -18,6 +18,21 @@ namespace sibs } return input; } + +#if OS_FAMILY == OS_FAMILY_POSIX + Result PkgConfig::validatePkgConfigPackageVersionExists(const Dependency &dependency) + { + Result dependencyValidationResult = PkgConfig::validatePackageExists(dependency.name); + if(dependencyValidationResult.isErr()) + return Result::Err(dependencyValidationResult.getErrMsg()); + + Result dependencyVersionValidationResult = PkgConfig::validatePackageVersionAtLeast(dependency.name, dependency.version); + if(dependencyVersionValidationResult.isErr()) + return Result::Err(dependencyVersionValidationResult.getErrMsg()); + + return Result::Ok(true); + } +#endif Result PkgConfig::validatePackageExists(const string &name) { -- cgit v1.2.3