From 1d3e221a7a20bfd03517e3ae1e35e4a309a69b6a Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sun, 10 Dec 2017 01:10:48 +0100 Subject: 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 --- CMakeLists.txt | 2 +- backend/ninja/Ninja.cpp | 208 +++++++++++++++++------------------------------- backend/ninja/Ninja.hpp | 12 ++- include/Conf.hpp | 31 ++++++++ include/Exec.hpp | 18 +++++ include/FileUtil.hpp | 3 + include/GlobalLib.hpp | 16 ++++ include/PkgConfig.hpp | 19 +++++ include/Result.hpp | 2 +- include/env.hpp | 12 +++ project.conf | 4 +- src/Conf.cpp | 50 ++++++++++++ src/Exec.cpp | 28 +++++++ src/FileUtil.cpp | 56 ++++++++++++- src/GlobalLib.cpp | 143 +++++++++++++++++++++++++++++++++ src/PkgConfig.cpp | 131 ++++++++++++++++++++++++++++++ src/main.cpp | 101 ++++------------------- 17 files changed, 607 insertions(+), 229 deletions(-) create mode 100644 include/Exec.hpp create mode 100644 include/GlobalLib.hpp create mode 100644 include/PkgConfig.hpp create mode 100644 src/Exec.cpp create mode 100644 src/GlobalLib.cpp create mode 100644 src/PkgConfig.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 784e6da..96c4185 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,6 @@ set(SOURCE_FILES src/main.cpp src/FileUtil.cpp src/Conf.cpp - external/xxhash.c backend/ninja/Ninja.cpp) + external/xxhash.c backend/ninja/Ninja.cpp src/PkgConfig.cpp src/Exec.cpp src/GlobalLib.cpp) add_executable(sibs ${SOURCE_FILES}) \ No newline at end of file diff --git a/backend/ninja/Ninja.cpp b/backend/ninja/Ninja.cpp index 73a6dc3..3df4e41 100644 --- a/backend/ninja/Ninja.cpp +++ b/backend/ninja/Ninja.cpp @@ -1,6 +1,9 @@ #include #include "Ninja.hpp" #include "../../include/FileUtil.hpp" +#include "../../include/Exec.hpp" +#include "../../include/PkgConfig.hpp" +#include "../../include/GlobalLib.hpp" using namespace std; using namespace sibs; @@ -36,34 +39,10 @@ namespace backend return move(result); } - struct ExecResult + Ninja::Ninja(LibraryType _libraryType) : + libraryType(_libraryType) { - string stdout; - int exitCode; - }; - Result exec(const char *cmd, bool print = false) - { - 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; - if(print) - printf("%s", buffer); - } - } - - ExecResult execResult; - execResult.stdout = result; - execResult.exitCode = WEXITSTATUS(pclose(pipe)); - return Result::Ok(execResult); } void Ninja::addSourceFile(const char *filepath) @@ -82,70 +61,15 @@ namespace backend return false; } - Result Ninja::validatePkgConfigPackageExists(const string &name) const + Result validatePkgConfigPackageVersionExists(const Dependency &dependency) { - string command = "pkg-config --exists '"; - command += name; - command += "'"; - Result execResult = exec(command.c_str()); - if(execResult.isErr()) - { - return Result::Err(execResult.getErrMsg()); - } + Result dependencyValidationResult = PkgConfig::validatePackageExists(dependency.name); + if(dependencyValidationResult.isErr()) + return Result::Err(dependencyValidationResult.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); - } + Result dependencyVersionValidationResult = PkgConfig::validatePackageVersionAtLeast(dependency.name, dependency.version); + if(dependencyVersionValidationResult.isErr()) + return Result::Err(dependencyVersionValidationResult.getErrMsg()); return Result::Ok(true); } @@ -156,53 +80,39 @@ namespace backend { 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()); - } + // TODO: Global library dir should be created during sibs installation + string globalLibDir = getHomeDir(); + globalLibDir += "/.sibs/lib"; - string args; + string globalLibLinkerFlags; + vector pkgConfigDependencies; for(const sibs::Dependency &dependency : dependencies) { - args += " '"; - args += dependency.name; - args += "'"; + Result pkgConfigDependencyValidation = validatePkgConfigPackageVersionExists(dependency); + if(pkgConfigDependencyValidation.isOk()) + { + pkgConfigDependencies.push_back(dependency.name); + } + else + { + printf("%s, trying global lib\n", pkgConfigDependencyValidation.getErrMsg().c_str()); + Result globalLibLinkerFlagsResult = GlobalLib::getDynamicLibsLinkerFlags(globalLibDir, dependency.name, dependency.version); + if(globalLibLinkerFlagsResult.isErr()) + return globalLibLinkerFlagsResult; + + globalLibLinkerFlags += " "; + globalLibLinkerFlags += globalLibLinkerFlagsResult.unwrap(); + // TODO: If package doesn't exist, download it from github/server + } } - string command = "pkg-config --libs"; - command += args; - Result execResult = exec(command.c_str()); - if(execResult.isErr()) - return Result::Err(execResult.getErrMsg()); + Result pkgConfigLinkerFlagsResult = PkgConfig::getDynamicLibsLinkerFlags(pkgConfigDependencies); + if(pkgConfigLinkerFlagsResult.isErr()) + return pkgConfigLinkerFlagsResult; - 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); - } + string allLinkerFlags = pkgConfigLinkerFlagsResult.unwrap(); + allLinkerFlags += globalLibLinkerFlags; + return Result::Ok(allLinkerFlags); } Result Ninja::createBuildFile(const std::string &packageName, const vector &dependencies, const char *savePath) @@ -210,7 +120,8 @@ namespace backend if(sourceFiles.empty()) return Result::Err("No source files provided"); - printf("Package name: %s\n", packageName.c_str()); + string ninjaBuildFilePath = savePath; + ninjaBuildFilePath += "/build.ninja"; string result; result.reserve(16384); @@ -220,8 +131,30 @@ namespace backend result += "rule cpp_COMPILER\n"; result += " command = ccache c++ $ARGS -c $in -o $out\n\n"; - result += "rule cpp_LINKER\n"; - result += " command = ccache c++ $ARGS -o $out $in $LINK_ARGS $aliasing\n\n"; + string linkerJob; + switch(libraryType) + { + case LibraryType::EXECUTABLE: + { + result += "rule cpp_EXEC_LINKER\n"; + result += " command = ccache c++ $ARGS -o $out $in $LINK_ARGS $aliasing\n\n"; + linkerJob = "cpp_EXEC_LINKER"; + break; + } + case LibraryType::STATIC: + { + result += "rule cpp_STATIC_LINKER\n"; + result += " command = ar rcs lib"; + result += packageName; + result += ".a"; + result += " $in\n\n"; + linkerJob = "cpp_STATIC_LINKER"; + break; + } + default: + assert(false); + return Result::Err("NOT IMPLEMENTED YET!"); + } vector objectNames; for(const string &sourceFile : sourceFiles) @@ -234,7 +167,8 @@ namespace backend result += ": cpp_COMPILER ../../"; result += sourceFile; result += "\n"; - result += " ARGS = '-I" + packageName + "@exe' '-I.' '-I..' '-fdiagnostics-color=always' '-pipe' '-D_FILE_OFFSET_BITS=64' '-Wall' '-Winvalid-pch' '-Wnon-virtual-dtor' '-O0' '-g'\n\n"; + // TODO: Create .deps directory if it doesn't exist. Should be a symlink to homedir/.sibs/lib + result += " ARGS = '-I../../.deps' '-I" + packageName + "@exe' '-I.' '-I..' '-fdiagnostics-color=always' '-pipe' '-D_FILE_OFFSET_BITS=64' '-Wall' '-Winvalid-pch' '-Wnon-virtual-dtor' '-O0' '-g'\n\n"; objectNames.emplace_back(objectName); } @@ -244,22 +178,22 @@ namespace backend result += "build "; result += packageName; - result += ": cpp_LINKER "; + result += ": " + linkerJob + " "; result += join(objectNames, " "); result += "\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())); + bool fileOverwritten = sibs::fileOverwrite(ninjaBuildFilePath.c_str(), sibs::StringView(result.data(), result.size())); if(!fileOverwritten) { string errMsg = "Failed to overwrite ninja build file: "; - errMsg += savePath; + errMsg += ninjaBuildFilePath; return Result::Err(errMsg); } - printf("Created ninja build file: %s\n", savePath); + printf("Created ninja build file: %s\n", ninjaBuildFilePath.c_str()); return Result::Ok(true); } diff --git a/backend/ninja/Ninja.hpp b/backend/ninja/Ninja.hpp index fdaa890..9ca6ace 100644 --- a/backend/ninja/Ninja.hpp +++ b/backend/ninja/Ninja.hpp @@ -12,16 +12,24 @@ namespace backend class Ninja { public: + enum class LibraryType + { + EXECUTABLE, + DYNAMIC, + STATIC + }; + + Ninja(LibraryType libraryType = LibraryType::EXECUTABLE); + void addSourceFile(const char *filepath); sibs::Result createBuildFile(const std::string &packageName, const std::vector &dependencies, const char *savePath); sibs::Result build(const char *buildFilePath); 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; + LibraryType libraryType; }; } diff --git a/include/Conf.hpp b/include/Conf.hpp index 8b98189..3634f4c 100644 --- a/include/Conf.hpp +++ b/include/Conf.hpp @@ -4,6 +4,7 @@ #include "Result.hpp" #include "StringView.hpp" #include "utils.hpp" +#include "Dependency.hpp" #include #include #include @@ -79,6 +80,36 @@ namespace sibs public: static Result readFromFile(const char *filepath, const ConfigCallback &callback); }; + + class SibsConfig : public ConfigCallback + { + public: + SibsConfig() : finishedProcessing(false) {} + + const std::string& getPackageName() const + { + assert(finishedProcessing); + return packageName; + } + + const std::vector& getDependencies() const + { + return dependencies; + } + protected: + void processObject(StringView name) override; + void processField(StringView name, const ConfigValue &value) override; + + void finished() override + { + finishedProcessing = true; + } + private: + StringView currentObject; + std::string packageName; + std::vector dependencies; + bool finishedProcessing; + }; } #endif //SIBS_CONF_HPP diff --git a/include/Exec.hpp b/include/Exec.hpp new file mode 100644 index 0000000..42b6905 --- /dev/null +++ b/include/Exec.hpp @@ -0,0 +1,18 @@ +#ifndef SIBS_EXEC_HPP +#define SIBS_EXEC_HPP + +#include "Result.hpp" +#include + +namespace sibs +{ + struct ExecResult + { + std::string execStdout; + int exitCode; + }; + + Result exec(const char *cmd, bool print = false); +} + +#endif //SIBS_EXEC_HPP diff --git a/include/FileUtil.hpp b/include/FileUtil.hpp index 4083cb1..5a0bbc3 100644 --- a/include/FileUtil.hpp +++ b/include/FileUtil.hpp @@ -18,9 +18,12 @@ namespace sibs }; FileType getFileType(const char *path); + void walkDir(const char *directory, FileWalkCallbackFunc callbackFunc); void walkDirFiles(const char *directory, FileWalkCallbackFunc callbackFunc); + void walkDirFilesRecursive(const char *directory, FileWalkCallbackFunc callbackFunc); Result getFileContent(const char *filepath); bool fileOverwrite(const char *filepath, StringView data); + const char* getHomeDir(); } #endif //SIBS_FILEUTIL_HPP diff --git a/include/GlobalLib.hpp b/include/GlobalLib.hpp new file mode 100644 index 0000000..e5a9374 --- /dev/null +++ b/include/GlobalLib.hpp @@ -0,0 +1,16 @@ +#ifndef SIBS_GLOBALLIB_HPP +#define SIBS_GLOBALLIB_HPP + +#include "Result.hpp" + +namespace sibs +{ + class GlobalLib + { + public: + static Result validatePackageExists(const std::string &globalLibRootDir, const std::string &name); + static Result getDynamicLibsLinkerFlags(const std::string &globalLibRootDir, const std::string &name, const std::string &version); + }; +} + +#endif //SIBS_GLOBALLIB_HPP diff --git a/include/PkgConfig.hpp b/include/PkgConfig.hpp new file mode 100644 index 0000000..4bafa18 --- /dev/null +++ b/include/PkgConfig.hpp @@ -0,0 +1,19 @@ +#ifndef SIBS_PKGCONFIG_HPP +#define SIBS_PKGCONFIG_HPP + +#include "Result.hpp" +#include +#include + +namespace sibs +{ + class PkgConfig + { + public: + static Result validatePackageExists(const std::string &name); + static Result validatePackageVersionAtLeast(const std::string &name, const std::string &version); + static Result getDynamicLibsLinkerFlags(const std::vector &libs); + }; +} + +#endif //SIBS_PKGCONFIG_HPP diff --git a/include/Result.hpp b/include/Result.hpp index 3ee60e5..f755b15 100644 --- a/include/Result.hpp +++ b/include/Result.hpp @@ -28,7 +28,7 @@ namespace sibs bool isOk() const { return !error; } bool isErr() const { return error; } - const T& unwrap() const + T& unwrap() { assert(isOk()); return value; diff --git a/include/env.hpp b/include/env.hpp index 5d0a163..325db6e 100644 --- a/include/env.hpp +++ b/include/env.hpp @@ -1,12 +1,20 @@ #ifndef SIBS_ENV_HPP #define SIBS_ENV_HPP +#define OS_FAMILY_WINDOWS 0 +#define OS_FAMILY_POSIX 1 + #if defined(_WIN32) || defined(_WIN64) #if defined(_WIN64) #define CISB_ENV_64BIT #else #define CISB_ENV_32BIT #endif + #define OS_FAMILY OS_FAMILY_WINDOWS +#endif + +#if defined(__linux__) || defined(__unix__) || defined(__APPLE__) || defined(_POSIX_VERSION) + #define OS_FAMILY OS_FAMILY_POSIX #endif #if defined(__GNUC__) @@ -21,4 +29,8 @@ #error "System is not detected as either 32-bit or 64-bit" #endif +#if !defined(OS_FAMILY) + #error "System not support. Only Windows and Posix systems support" +#endif + #endif // SIBS_ENV_HPP diff --git a/project.conf b/project.conf index 0f65e5c..96bb9b9 100644 --- a/project.conf +++ b/project.conf @@ -3,4 +3,6 @@ name = "sibs" version = "0.1.0" authors = ["Aleksi Lindeman "] -[dependencies] \ No newline at end of file +[dependencies] +sfml-all = "2.4" +xxhash = "0.1.0" \ No newline at end of file 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 exec(const char *cmd, bool print) + { + char buffer[128]; + std::string result; + FILE *pipe = popen(cmd, "r"); + if(!pipe) + return Result::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::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 +#if OS_FAMILY == OS_FAMILY_POSIX +#include +#include +#include +#endif + using namespace std; namespace sibs @@ -18,12 +25,48 @@ 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; tinydir_open(&dir, directory); + while (dir.has_next) + { + tinydir_file file; + 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; @@ -31,7 +74,7 @@ namespace sibs 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 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::Err(errMsg); + } + case FileType::REGULAR: + { + string errMsg = "Corrupt library directory. "; + errMsg += packageDir; + errMsg += " is a file, expected it to be a directory"; + return Result::Err(errMsg); + } + case FileType::DIRECTORY: + { + return Result::Ok(true); + } + default: + { + return Result::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 GlobalLib::getDynamicLibsLinkerFlags(const string &globalLibRootDir, const string &name, const string &version) + { + Result packageExistsResult = validatePackageExists(globalLibRootDir, name); + if(packageExistsResult.isErr()) + return Result::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::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::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::Err(errMsg); + } + } + + SibsConfig sibsConfig; + Result result = Config::readFromFile(projectConfFilePath.c_str(), sibsConfig); + if(result.isErr()) + return Result::Err(result.getErrMsg()); + + if(sibsConfig.getPackageName().empty()) + return Result::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 buildFileResult = ninja.createBuildFile(sibsConfig.getPackageName(), sibsConfig.getDependencies(), debugBuildPath.c_str()); + if(buildFileResult.isErr()) + return Result::Err(buildFileResult.getErrMsg()); + + Result buildResult = ninja.build(debugBuildPath.c_str()); + if(buildResult.isErr()) + return Result::Err(buildResult.getErrMsg()); + + string staticLibPath = debugBuildPath; + staticLibPath += "/lib"; + staticLibPath += name; + staticLibPath += ".a"; + return Result::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 PkgConfig::validatePackageExists(const string &name) + { + 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 = "pkg-config 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 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 = 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); + } + + Result PkgConfig::getDynamicLibsLinkerFlags(const vector &libs) + { + if(libs.empty()) return Result::Ok(""); + + string args; + for(const string &lib : libs) + { + args += " '"; + args += lib; + 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) + { + execResult.unwrap().execStdout = trimRight(execResult.unwrap().execStdout); + return Result::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::Err("Packages not found"); + } + else if(execResult.unwrap().exitCode == 127) + { + return Result::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::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& 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 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 buildFileResult = ninja.createBuildFile(sibsConfig.getPackageName(), sibsConfig.getDependencies(), buildFilePath.c_str()); + Result 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 +} -- cgit v1.2.3