From e7384a7672e4449bc194ca3ec66cdd4fcc63801e Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sat, 9 Dec 2017 16:36:23 +0100 Subject: Add support for dependencies (including version check) This currently only works using pkg-config and it only adds linking flags. Need to check with a library that also includes other types of flags. TODO: Fallback to dependencies sub directory and github/server if package not found in pkg-config. --- backend/ninja/Ninja.cpp | 174 ++++++++++++++++++++++++++++++++++++++++++++++-- backend/ninja/Ninja.hpp | 8 ++- 2 files changed, 177 insertions(+), 5 deletions(-) (limited to 'backend') diff --git a/backend/ninja/Ninja.cpp b/backend/ninja/Ninja.cpp index 7006d30..683fb67 100644 --- a/backend/ninja/Ninja.cpp +++ b/backend/ninja/Ninja.cpp @@ -3,6 +3,7 @@ #include "../../include/FileUtil.hpp" using namespace std; +using namespace sibs; namespace backend { @@ -35,6 +36,32 @@ namespace backend return move(result); } + struct ExecResult + { + string stdout; + int exitCode; + }; + + Result exec(const char *cmd) + { + char buffer[128]; + string result; + FILE *pipe = popen(cmd, "r"); + if(!pipe) + return Result::Err("popen() failed"); + + while(!feof(pipe)) + { + if(fgets(buffer, 128, pipe)) + result += buffer; + } + + ExecResult execResult; + execResult.stdout = result; + execResult.exitCode = WEXITSTATUS(pclose(pipe)); + return Result::Ok(execResult); + } + void Ninja::addSourceFile(const char *filepath) { if(filepath && !containsSourceFile(filepath)) @@ -51,9 +78,134 @@ namespace backend return false; } - void Ninja::build(const std::string &packageName, const char *savePath) + Result Ninja::validatePkgConfigPackageExists(const string &name) const { - if(sourceFiles.empty()) return; + string command = "pkg-config --exists '"; + command += name; + command += "'"; + Result execResult = exec(command.c_str()); + if(execResult.isErr()) + { + return Result::Err(execResult.getErrMsg()); + } + + if(execResult.unwrap().exitCode == 1) + { + string errMsg = "Dependency not found: "; + errMsg += name; + return Result::Err(errMsg); + } + else if(execResult.unwrap().exitCode == 127) + { + return Result::Err("pkg-config is not installed"); + } + else if(execResult.unwrap().exitCode != 0) + { + string errMsg = "Failed to check if dependency exists, Unknown error, exit code: "; + errMsg += to_string(execResult.unwrap().exitCode); + return Result::Err(errMsg); + } + + return Result::Ok(true); + } + + Result Ninja::validatePkgConfigPackageVersionAtLeast(const string &name, const string &version) const + { + // Use --modversion instead and check if the version returned is newer or equal to dependency version. + // This way we can output installed version vs expected dependency version + string command = "pkg-config '--atleast-version="; + command += version; + command += "' '"; + command += name; + command += "'"; + Result execResult = exec(command.c_str()); + if(execResult.isErr()) + { + return Result::Err(execResult.getErrMsg()); + } + + if(execResult.unwrap().exitCode == 1) + { + string errMsg = "Dependency "; + errMsg += name; + errMsg += " is installed but the version older than "; + errMsg += version; + return Result::Err(errMsg); + } + else if(execResult.unwrap().exitCode == 127) + { + return Result::Err("pkg-config is not installed"); + } + else if(execResult.unwrap().exitCode != 0) + { + string errMsg = "Failed to check dependency version, Unknown error, exit code: "; + errMsg += to_string(execResult.unwrap().exitCode); + return Result::Err(errMsg); + } + + return Result::Ok(true); + } + + // TODO: First check if pkg-config is installed. If it's not, only check dependencies that exists in the dependencies sub directory. + // If pkg-config is installed and dependency is not installed, check in dependencies sub directory. + Result Ninja::getLinkerFlags(const vector &dependencies) const + { + if(dependencies.empty()) return Result::Ok(""); + + for(const sibs::Dependency &dependency : dependencies) + { + Result dependencyValidationResult = validatePkgConfigPackageExists(dependency.name); + if(dependencyValidationResult.isErr()) + return Result::Err(dependencyValidationResult.getErrMsg()); + + Result dependencyVersionValidationResult = validatePkgConfigPackageVersionAtLeast(dependency.name, dependency.version); + if(dependencyVersionValidationResult.isErr()) + return Result::Err(dependencyVersionValidationResult.getErrMsg()); + } + + string args; + for(const sibs::Dependency &dependency : dependencies) + { + args += " '"; + args += dependency.name; + args += "'"; + } + + string command = "pkg-config --libs"; + command += args; + Result execResult = exec(command.c_str()); + if(execResult.isErr()) + return Result::Err(execResult.getErrMsg()); + + if(execResult.unwrap().exitCode == 0) + { + return Result::Ok(execResult.unwrap().stdout); + } + else if(execResult.unwrap().exitCode == 1) + { + // TODO: This shouldn't happen because we check if each dependency is installed before this, + // but maybe the package is uninstalled somewhere between here... + // Would be better to recheck if each package is installed here again + // to know which package was uninstalled + return Result::Err("Dependencies not found"); + } + else if(execResult.unwrap().exitCode == 127) + { + return Result::Err("pkg-config is not installed"); + } + else + { + string errMsg = "Failed to get dependencies linking flags, Unknown error, exit code: "; + errMsg += to_string(execResult.unwrap().exitCode); + return Result::Err(errMsg); + } + } + + Result Ninja::createBuildFile(const std::string &packageName, const vector &dependencies, const char *savePath) + { + if(sourceFiles.empty()) + return Result::Err("No source files provided"); + printf("Package name: %s\n", packageName.c_str()); string result; @@ -82,14 +234,28 @@ namespace backend objectNames.emplace_back(objectName); } + Result linkerFlags = getLinkerFlags(dependencies); + if(linkerFlags.isErr()) + return Result::Err(linkerFlags.getErrMsg()); + result += "build "; result += packageName; result += ": cpp_LINKER "; result += join(objectNames, " "); result += "\n"; - result += " LINK_ARGS = '-Wl,--no-undefined' '-Wl,--as-needed'\n\n"; + result += " LINK_ARGS = '-Wl,--no-undefined' '-Wl,--as-needed' "; + result += linkerFlags.unwrap(); + result += "\n\n"; + + bool fileOverwritten = sibs::fileOverwrite(savePath, sibs::StringView(result.data(), result.size())); + if(!fileOverwritten) + { + string errMsg = "Failed to overwrite ninja build file: "; + errMsg += savePath; + return Result::Err(errMsg); + } - sibs::fileOverwrite(savePath, sibs::StringView(result.data(), result.size())); printf("Created ninja build file: %s\n", savePath); + return Result::Ok(true); } } \ No newline at end of file diff --git a/backend/ninja/Ninja.hpp b/backend/ninja/Ninja.hpp index ad71c80..1ba20c7 100644 --- a/backend/ninja/Ninja.hpp +++ b/backend/ninja/Ninja.hpp @@ -1,18 +1,24 @@ #ifndef BACKEND_NINJA_HPP #define BACKEND_NINJA_HPP +#include "../../include/Dependency.hpp" +#include "../../include/Result.hpp" #include #include + namespace backend { class Ninja { public: void addSourceFile(const char *filepath); - void build(const std::string &packageName, const char *savePath); + sibs::Result createBuildFile(const std::string &packageName, const std::vector &dependencies, const char *savePath); private: bool containsSourceFile(const char *filepath) const; + sibs::Result getLinkerFlags(const std::vector &dependencies) const; + sibs::Result validatePkgConfigPackageExists(const std::string &name) const; + sibs::Result validatePkgConfigPackageVersionAtLeast(const std::string &name, const std::string &version) const; private: std::vector sourceFiles; }; -- cgit v1.2.3