aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2017-12-10 01:10:48 +0100
committerdec05eba <dec05eba@protonmail.com>2017-12-10 01:12:08 +0100
commit1d3e221a7a20bfd03517e3ae1e35e4a309a69b6a (patch)
treefdb38039d12cf38e9ac6102118727b78437cf3db /src
parent2ed7d0b09caa872e44e2eb09b09b2387e93f9b34 (diff)
Add support for dependencies in global lib dir
Global lib dir is located at ~/.sibs/lib TODO: If global lib dir doesn't exist, download it from github/server
Diffstat (limited to 'src')
-rw-r--r--src/Conf.cpp50
-rw-r--r--src/Exec.cpp28
-rw-r--r--src/FileUtil.cpp56
-rw-r--r--src/GlobalLib.cpp143
-rw-r--r--src/PkgConfig.cpp131
-rw-r--r--src/main.cpp101
6 files changed, 422 insertions, 87 deletions
diff --git a/src/Conf.cpp b/src/Conf.cpp
index 6383c30..dc1bbd0 100644
--- a/src/Conf.cpp
+++ b/src/Conf.cpp
@@ -331,4 +331,54 @@ namespace sibs
return Parser::parse(code, callback);
}
+
+ void SibsConfig::processObject(StringView name)
+ {
+ currentObject = name;
+ printf("Process object: %.*s\n", name.size, name.data);
+ }
+
+ void SibsConfig::processField(StringView name, const ConfigValue &value)
+ {
+ printf("Process field: %.*s, value: ", name.size, name.data);
+ if(value.isSingle())
+ {
+ printf("\"%.*s\"", value.asSingle().size, value.asSingle().data);
+ }
+ else
+ {
+ printf("[");
+ int i = 0;
+ for(auto listElement : value.asList())
+ {
+ if(i > 0)
+ printf(", ");
+ printf("\"%.*s\"", listElement.size, listElement.data);
+ ++i;
+ }
+ printf("]");
+ }
+ printf("\n");
+
+ if(currentObject.equals("package") && name.equals("name"))
+ {
+ if(value.isSingle())
+ packageName = string(value.asSingle().data, value.asSingle().size);
+ 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");
+ }
+ }
} \ No newline at end of file
diff --git a/src/Exec.cpp b/src/Exec.cpp
new file mode 100644
index 0000000..b245fd4
--- /dev/null
+++ b/src/Exec.cpp
@@ -0,0 +1,28 @@
+#include "../include/Exec.hpp"
+
+namespace sibs
+{
+ Result<ExecResult> exec(const char *cmd, bool print)
+ {
+ char buffer[128];
+ std::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;
+ if(print)
+ printf("%s", buffer);
+ }
+ }
+
+ ExecResult execResult;
+ execResult.execStdout = result;
+ execResult.exitCode = WEXITSTATUS(pclose(pipe));
+ return Result<ExecResult>::Ok(execResult);
+ }
+} \ No newline at end of file
diff --git a/src/FileUtil.cpp b/src/FileUtil.cpp
index 59132dc..400b439 100644
--- a/src/FileUtil.cpp
+++ b/src/FileUtil.cpp
@@ -1,6 +1,13 @@
#include "../include/FileUtil.hpp"
+#include "../include/env.hpp"
#include <cstdio>
+#if OS_FAMILY == OS_FAMILY_POSIX
+#include <unistd.h>
+#include <sys/types.h>
+#include <pwd.h>
+#endif
+
using namespace std;
namespace sibs
@@ -19,6 +26,24 @@ namespace sibs
}
// TODO: Handle failure (directory doesn't exist, no permission etc)
+ void walkDir(const char *directory, FileWalkCallbackFunc callbackFunc)
+ {
+ tinydir_dir dir;
+ tinydir_open(&dir, directory);
+
+ while (dir.has_next)
+ {
+ tinydir_file file;
+ tinydir_readfile(&dir, &file);
+ if(_tinydir_strcmp(file.name, ".") != 0 && _tinydir_strcmp(file.name, "..") != 0)
+ callbackFunc(&file);
+ tinydir_next(&dir);
+ }
+
+ tinydir_close(&dir);
+ }
+
+ // TODO: Handle failure (directory doesn't exist, no permission etc)
void walkDirFiles(const char *directory, FileWalkCallbackFunc callbackFunc)
{
tinydir_dir dir;
@@ -30,8 +55,26 @@ namespace sibs
tinydir_readfile(&dir, &file);
if(file.is_reg)
callbackFunc(&file);
+ tinydir_next(&dir);
+ }
+
+ tinydir_close(&dir);
+ }
+
+ // TODO: Handle failure (directory doesn't exist, no permission etc)
+ void walkDirFilesRecursive(const char *directory, FileWalkCallbackFunc callbackFunc)
+ {
+ tinydir_dir dir;
+ tinydir_open(&dir, directory);
+
+ while (dir.has_next)
+ {
+ tinydir_file file;
+ tinydir_readfile(&dir, &file);
+ if(file.is_reg)
+ callbackFunc(&file);
else if(_tinydir_strcmp(file.name, ".") != 0 && _tinydir_strcmp(file.name, "..") != 0)
- walkDirFiles(file.path, callbackFunc);
+ walkDirFilesRecursive(file.path, callbackFunc);
tinydir_next(&dir);
}
@@ -77,4 +120,15 @@ namespace sibs
fclose(file);
return true;
}
+
+ const char* getHomeDir()
+ {
+ const char *homeDir = getenv("HOME");
+ if(!homeDir)
+ {
+ passwd *pw = getpwuid(getuid());
+ homeDir = pw->pw_dir;
+ }
+ return homeDir;
+ }
} \ No newline at end of file
diff --git a/src/GlobalLib.cpp b/src/GlobalLib.cpp
new file mode 100644
index 0000000..0ed34c7
--- /dev/null
+++ b/src/GlobalLib.cpp
@@ -0,0 +1,143 @@
+#include "../include/GlobalLib.hpp"
+#include "../include/FileUtil.hpp"
+#include "../backend/ninja/Ninja.hpp"
+#include "../include/Conf.hpp"
+
+using namespace std;
+
+namespace sibs
+{
+ Result<bool> GlobalLib::validatePackageExists(const string &globalLibRootDir, const string &name)
+ {
+ string packageDir = globalLibRootDir + "/";
+ packageDir += name;
+ FileType packageDirFileType = getFileType(packageDir.c_str());
+ switch(packageDirFileType)
+ {
+ case FileType::FILE_NOT_FOUND:
+ {
+ string errMsg = "Global lib dependency not found: ";
+ errMsg += name;
+ return Result<bool>::Err(errMsg);
+ }
+ case FileType::REGULAR:
+ {
+ string errMsg = "Corrupt library directory. ";
+ errMsg += packageDir;
+ errMsg += " is a file, expected it to be a directory";
+ return Result<bool>::Err(errMsg);
+ }
+ case FileType::DIRECTORY:
+ {
+ return Result<bool>::Ok(true);
+ }
+ default:
+ {
+ return Result<bool>::Err("Unexpected error!");
+ }
+ }
+ }
+
+ const char *sourceFileExtensions[] = { "c", "cc", "cpp", "cxx" };
+ bool isSourceFile(tinydir_file *file)
+ {
+ if(!file->is_reg)
+ return false;
+
+ for(const char *sourceFileExtension : sourceFileExtensions)
+ {
+ if(_tinydir_strcmp(sourceFileExtension, file->extension) == 0)
+ return true;
+ }
+
+ return false;
+ }
+
+ Result<string> GlobalLib::getDynamicLibsLinkerFlags(const string &globalLibRootDir, const string &name, const string &version)
+ {
+ Result<bool> packageExistsResult = validatePackageExists(globalLibRootDir, name);
+ if(packageExistsResult.isErr())
+ return Result<string>::Err(packageExistsResult.getErrMsg());
+
+ string packageDir = globalLibRootDir + "/";
+ packageDir += name;
+
+ // TODO: Instead of checking if version is exact match, check if package has same major version
+ // and same or newer minor version
+ string foundVersion;
+ walkDir(packageDir.c_str(), [&foundVersion, &version](tinydir_file *file)
+ {
+ if(file->is_dir)
+ {
+ printf("version: %s\n", file->name);
+ if(_tinydir_strcmp(version.c_str(), file->name) == 0)
+ foundVersion = file->name;
+ }
+ });
+
+ if(foundVersion.empty())
+ return Result<string>::Err("Global lib dependency found, but version doesn't match dependency version");
+
+ packageDir += "/";
+ packageDir += version;
+
+ string projectConfFilePath = packageDir;
+ projectConfFilePath += "/project.conf";
+
+ FileType projectConfFileType = getFileType(projectConfFilePath.c_str());
+ switch(projectConfFileType)
+ {
+ case FileType::FILE_NOT_FOUND:
+ {
+ string errMsg = "Global lib dependency found: ";
+ errMsg += packageDir;
+ errMsg += ", but it's missing a project.conf file";
+ return Result<string>::Err(errMsg);
+ }
+ case FileType::DIRECTORY:
+ {
+ string errMsg = "Global lib dependency found: ";
+ errMsg += packageDir;
+ errMsg += ", but it's corrupt (Found directory instead of file)";
+ return Result<string>::Err(errMsg);
+ }
+ }
+
+ SibsConfig sibsConfig;
+ Result<bool> result = Config::readFromFile(projectConfFilePath.c_str(), sibsConfig);
+ if(result.isErr())
+ return Result<string>::Err(result.getErrMsg());
+
+ if(sibsConfig.getPackageName().empty())
+ return Result<string>::Err("project.conf is missing required field package.name");
+
+ backend::Ninja ninja(backend::Ninja::LibraryType::STATIC);
+ walkDirFilesRecursive(packageDir.c_str(), [&ninja, &packageDir](tinydir_file *file)
+ {
+ if (isSourceFile(file))
+ {
+ printf("Adding source file: %s\n", file->path + packageDir.size() + 1);
+ ninja.addSourceFile(file->path + packageDir.size() + 1);
+ } else
+ {
+ //printf("Ignoring non-source file: %s\n", file->path + packageDir.size() + 1);
+ }
+ });
+
+ // TODO: Create build path if it doesn't exist
+ string debugBuildPath = packageDir + "/build/debug";
+ Result<bool> buildFileResult = ninja.createBuildFile(sibsConfig.getPackageName(), sibsConfig.getDependencies(), debugBuildPath.c_str());
+ if(buildFileResult.isErr())
+ return Result<string>::Err(buildFileResult.getErrMsg());
+
+ Result<bool> buildResult = ninja.build(debugBuildPath.c_str());
+ if(buildResult.isErr())
+ return Result<string>::Err(buildResult.getErrMsg());
+
+ string staticLibPath = debugBuildPath;
+ staticLibPath += "/lib";
+ staticLibPath += name;
+ staticLibPath += ".a";
+ return Result<string>::Ok(staticLibPath);
+ }
+} \ No newline at end of file
diff --git a/src/PkgConfig.cpp b/src/PkgConfig.cpp
new file mode 100644
index 0000000..11e1cf0
--- /dev/null
+++ b/src/PkgConfig.cpp
@@ -0,0 +1,131 @@
+#include "../include/PkgConfig.hpp"
+#include "../include/Exec.hpp"
+
+using namespace std;
+
+namespace sibs
+{
+ string trimRight(const string &input)
+ {
+ for(int i = input.size() - 1; i >= 0; --i)
+ {
+ if(!isspace(input[i]))
+ return input.substr(0, i + 1);
+ }
+ return input;
+ }
+
+ Result<bool> PkgConfig::validatePackageExists(const string &name)
+ {
+ 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 = "pkg-config 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> PkgConfig::validatePackageVersionAtLeast(const string &name, const string &version)
+ {
+ // TODO: Instead of checking if package version is same or newer, check if package is same major version
+ // and same or newer minor version
+
+ // 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);
+ }
+
+ Result<string> PkgConfig::getDynamicLibsLinkerFlags(const vector<string> &libs)
+ {
+ if(libs.empty()) return Result<string>::Ok("");
+
+ string args;
+ for(const string &lib : libs)
+ {
+ args += " '";
+ args += lib;
+ 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)
+ {
+ execResult.unwrap().execStdout = trimRight(execResult.unwrap().execStdout);
+ return Result<string>::Ok(execResult.unwrap().execStdout);
+ }
+ 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("Packages not found");
+ }
+ else if(execResult.unwrap().exitCode == 127)
+ {
+ return Result<string>::Err("pkg-config is not installed");
+ }
+ else
+ {
+ string errMsg = "Failed to get package dynamic lib linking flags, Unknown error, exit code: ";
+ errMsg += to_string(execResult.unwrap().exitCode);
+ return Result<string>::Err(errMsg);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index 05a6a03..f9fbd82 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -51,83 +51,6 @@ void validateFilePath(const char *projectConfPath)
}
}
-class SibsConfig : public ConfigCallback
-{
-public:
- SibsConfig() : finishedProcessing(false) {}
-
- const string& getPackageName() const
- {
- assert(finishedProcessing);
- return packageName;
- }
-
- const std::vector<Dependency>& getDependencies() const
- {
- return dependencies;
- }
-protected:
- void processObject(StringView name) override
- {
- currentObject = name;
- printf("Process object: %.*s\n", name.size, name.data);
- }
-
- void processField(StringView name, const ConfigValue &value) override
- {
- printf("Process field: %.*s, value: ", name.size, name.data);
- if(value.isSingle())
- {
- printf("\"%.*s\"", value.asSingle().size, value.asSingle().data);
- }
- else
- {
- printf("[");
- int i = 0;
- for(auto listElement : value.asList())
- {
- if(i > 0)
- printf(", ");
- printf("\"%.*s\"", listElement.size, listElement.data);
- ++i;
- }
- printf("]");
- }
- printf("\n");
-
- if(currentObject.equals("package") && name.equals("name"))
- {
- if(value.isSingle())
- packageName = string(value.asSingle().data, value.asSingle().size);
- 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
- {
- finishedProcessing = true;
- }
-private:
- StringView currentObject;
- string packageName;
- std::vector<Dependency> dependencies;
- bool finishedProcessing;
-};
-
const char *sourceFileExtensions[] = { "cc", "cpp", "cxx" };
bool isSourceFile(tinydir_file *file)
{
@@ -150,6 +73,8 @@ int main(int argc, const char **argv)
string projectPath = argv[1];
validateDirectoryPath(projectPath.c_str());
+ if(projectPath.back() != '/')
+ projectPath += "/";
string projectConfFilePath = projectPath;
projectConfFilePath += "/project.conf";
@@ -163,27 +88,31 @@ int main(int argc, const char **argv)
exit(6);
}
+ if(sibsConfig.getPackageName().empty())
+ {
+ printf("project.conf is missing required field package.name\n");
+ exit(10);
+ }
+
//string projectSrcPath = projectPath + "/src";
//validateDirectoryPath(projectSrcPath.c_str());
backend::Ninja ninja;
- walkDirFiles(projectPath.c_str(), [&ninja, &projectPath](tinydir_file *file)
+ walkDirFilesRecursive(projectPath.c_str(), [&ninja, &projectPath](tinydir_file *file)
{
if (isSourceFile(file))
{
- printf("Adding source file: %s\n", file->path + projectPath.size() + 1);
- ninja.addSourceFile(file->path + projectPath.size() + 1);
- }
- else
+ printf("Adding source file: %s\n", file->path + projectPath.size());
+ ninja.addSourceFile(file->path + projectPath.size());
+ } else
{
- printf("Ignoring non-source file: %s\n", file->path + projectPath.size() + 1);
+ //printf("Ignoring non-source file: %s\n", file->path + projectPath.size());
}
});
// TODO: Create build path if it doesn't exist
string debugBuildPath = projectPath + "/build/debug";
- string buildFilePath = debugBuildPath + "/build.ninja";
- Result<bool> buildFileResult = ninja.createBuildFile(sibsConfig.getPackageName(), sibsConfig.getDependencies(), buildFilePath.c_str());
+ Result<bool> buildFileResult = ninja.createBuildFile(sibsConfig.getPackageName(), sibsConfig.getDependencies(), debugBuildPath.c_str());
if(buildFileResult.isErr())
{
printf("Failed to build ninja file: %s\n", buildFileResult.getErrMsg().c_str());
@@ -197,4 +126,4 @@ int main(int argc, const char **argv)
}
return 0;
-} \ No newline at end of file
+}