diff options
author | dec05eba <dec05eba@protonmail.com> | 2017-12-09 16:36:23 +0100 |
---|---|---|
committer | dec05eba <dec05eba@protonmail.com> | 2017-12-09 16:38:31 +0100 |
commit | e7384a7672e4449bc194ca3ec66cdd4fcc63801e (patch) | |
tree | c79bc4dfbb8b3a752ae33f26f5e1b2992a484ace | |
parent | 6cc190828160586abc6961354a7c05e99537d7e2 (diff) |
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.
-rw-r--r-- | backend/ninja/Ninja.cpp | 174 | ||||
-rw-r--r-- | backend/ninja/Ninja.hpp | 8 | ||||
-rw-r--r-- | include/Dependency.hpp | 16 | ||||
-rw-r--r-- | src/Conf.cpp | 16 | ||||
-rw-r--r-- | src/FileUtil.cpp | 1 | ||||
-rw-r--r-- | src/main.cpp | 33 |
6 files changed, 232 insertions, 16 deletions
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<ExecResult> exec(const char *cmd) + { + char buffer[128]; + string result; + FILE *pipe = popen(cmd, "r"); + if(!pipe) + return Result<ExecResult>::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<ExecResult>::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<bool> Ninja::validatePkgConfigPackageExists(const string &name) const { - if(sourceFiles.empty()) return; + string command = "pkg-config --exists '"; + command += name; + command += "'"; + Result<ExecResult> execResult = exec(command.c_str()); + if(execResult.isErr()) + { + return Result<bool>::Err(execResult.getErrMsg()); + } + + if(execResult.unwrap().exitCode == 1) + { + string errMsg = "Dependency not found: "; + errMsg += name; + return Result<bool>::Err(errMsg); + } + else if(execResult.unwrap().exitCode == 127) + { + return Result<bool>::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<bool>::Err(errMsg); + } + + return Result<bool>::Ok(true); + } + + Result<bool> 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> execResult = exec(command.c_str()); + if(execResult.isErr()) + { + return Result<bool>::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<bool>::Err(errMsg); + } + else if(execResult.unwrap().exitCode == 127) + { + return Result<bool>::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<bool>::Err(errMsg); + } + + return Result<bool>::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<string> Ninja::getLinkerFlags(const vector<Dependency> &dependencies) const + { + if(dependencies.empty()) return Result<string>::Ok(""); + + for(const sibs::Dependency &dependency : dependencies) + { + Result<bool> dependencyValidationResult = validatePkgConfigPackageExists(dependency.name); + if(dependencyValidationResult.isErr()) + return Result<string>::Err(dependencyValidationResult.getErrMsg()); + + Result<bool> dependencyVersionValidationResult = validatePkgConfigPackageVersionAtLeast(dependency.name, dependency.version); + if(dependencyVersionValidationResult.isErr()) + return Result<string>::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> execResult = exec(command.c_str()); + if(execResult.isErr()) + return Result<string>::Err(execResult.getErrMsg()); + + if(execResult.unwrap().exitCode == 0) + { + return Result<string>::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<string>::Err("Dependencies not found"); + } + else if(execResult.unwrap().exitCode == 127) + { + return Result<string>::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<string>::Err(errMsg); + } + } + + Result<bool> Ninja::createBuildFile(const std::string &packageName, const vector<Dependency> &dependencies, const char *savePath) + { + if(sourceFiles.empty()) + return Result<bool>::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<string> linkerFlags = getLinkerFlags(dependencies); + if(linkerFlags.isErr()) + return Result<bool>::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<bool>::Err(errMsg); + } - sibs::fileOverwrite(savePath, sibs::StringView(result.data(), result.size())); printf("Created ninja build file: %s\n", savePath); + return Result<bool>::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 <vector> #include <string> + namespace backend { class Ninja { public: void addSourceFile(const char *filepath); - void build(const std::string &packageName, const char *savePath); + sibs::Result<bool> createBuildFile(const std::string &packageName, const std::vector<sibs::Dependency> &dependencies, const char *savePath); private: bool containsSourceFile(const char *filepath) const; + sibs::Result<std::string> getLinkerFlags(const std::vector<sibs::Dependency> &dependencies) const; + sibs::Result<bool> validatePkgConfigPackageExists(const std::string &name) const; + sibs::Result<bool> validatePkgConfigPackageVersionAtLeast(const std::string &name, const std::string &version) const; private: std::vector<std::string> sourceFiles; }; diff --git a/include/Dependency.hpp b/include/Dependency.hpp new file mode 100644 index 0000000..b97c362 --- /dev/null +++ b/include/Dependency.hpp @@ -0,0 +1,16 @@ +#ifndef SIBS_DEPENDENCY_HPP +#define SIBS_DEPENDENCY_HPP + +#include <string> + +namespace sibs +{ + class Dependency + { + public: + std::string name; + std::string version; + }; +} + +#endif //SIBS_DEPENDENCY_HPP diff --git a/src/Conf.cpp b/src/Conf.cpp index 56b1e2a..6383c30 100644 --- a/src/Conf.cpp +++ b/src/Conf.cpp @@ -68,7 +68,7 @@ namespace sibs char *startOfIdentifier = code.base(); ++code; c = *code; - while(isAlpha(c) || c == '_' || isDigit(c)) + while(isAlpha(c) || isDigit(c) || c == '_' || c == '-') { ++code; c = *code; @@ -95,19 +95,21 @@ namespace sibs } else if(c == '"') { - u32 escapeCount = 0; + bool escapeQuote = false; ++code; char *startOfStr = code.base(); - while(escapeCount > 0 || *code != '"') + while(true) { c = *code; - if(c == '\0') - return Token::END_OF_FILE; + if(c == '"' && !escapeQuote) + break; else if(c == '\\') - ++escapeCount; + escapeQuote = !escapeQuote; + else if(c == '\0') + throw UnexpectedTokenException("Reached end of file before string end"); else - escapeCount = min(0, escapeCount - 1); + escapeQuote = false; ++code; } diff --git a/src/FileUtil.cpp b/src/FileUtil.cpp index 8502e84..59132dc 100644 --- a/src/FileUtil.cpp +++ b/src/FileUtil.cpp @@ -75,5 +75,6 @@ namespace sibs } fwrite(data.data, 1, data.size, file); fclose(file); + return true; } }
\ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 866d691..9f14c67 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,7 @@ #include <cstdio> #include "../include/FileUtil.hpp" #include "../include/Conf.hpp" +#include "../include/Dependency.hpp" #include "../backend/ninja/Ninja.hpp" #include <string> #include <cassert> @@ -60,6 +61,11 @@ public: assert(finishedProcessing); return packageName; } + + const std::vector<Dependency>& getDependencies() const + { + return dependencies; + } protected: void processObject(StringView name) override { @@ -96,6 +102,19 @@ protected: else throw ParserException("Expected package.name to be a single value, was a list"); } + 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"); + } } void finished() override @@ -105,6 +124,7 @@ protected: private: StringView currentObject; string packageName; + std::vector<Dependency> dependencies; bool finishedProcessing; }; @@ -143,11 +163,11 @@ int main(int argc, const char **argv) exit(6); } - string projectSrcPath = projectPath + "/src"; - validateDirectoryPath(projectSrcPath.c_str()); + //string projectSrcPath = projectPath + "/src"; + //validateDirectoryPath(projectSrcPath.c_str()); backend::Ninja ninja; - walkDirFiles(projectSrcPath.c_str(), [&ninja, &projectPath](tinydir_file *file) + walkDirFiles(projectPath.c_str(), [&ninja, &projectPath](tinydir_file *file) { if (isSourceFile(file)) { @@ -163,7 +183,12 @@ int main(int argc, const char **argv) // TODO: Create build path if it doesn't exist string debugBuildPath = projectPath + "/build/debug"; string buildFilePath = debugBuildPath + "/build.ninja"; - ninja.build(sibsConfig.getPackageName(), buildFilePath.c_str()); + Result<bool> buildFileResult = ninja.createBuildFile(sibsConfig.getPackageName(), sibsConfig.getDependencies(), buildFilePath.c_str()); + if(buildFileResult.isErr()) + { + printf("Failed to build ninja file: %s\n", buildFileResult.getErrMsg().c_str()); + exit(7); + } return 0; }
\ No newline at end of file |