aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2017-12-09 16:36:23 +0100
committerdec05eba <dec05eba@protonmail.com>2017-12-09 16:38:31 +0100
commite7384a7672e4449bc194ca3ec66cdd4fcc63801e (patch)
treec79bc4dfbb8b3a752ae33f26f5e1b2992a484ace
parent6cc190828160586abc6961354a7c05e99537d7e2 (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.cpp174
-rw-r--r--backend/ninja/Ninja.hpp8
-rw-r--r--include/Dependency.hpp16
-rw-r--r--src/Conf.cpp16
-rw-r--r--src/FileUtil.cpp1
-rw-r--r--src/main.cpp33
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