From d9090882cae78695765204a3e1b60c6a9bf27977 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sat, 9 Dec 2017 02:43:02 +0100 Subject: Added ninja backend, very simple project works --- CMakeLists.txt | 2 +- backend/ninja/Ninja.cpp | 95 +++++++++++++++++++++++++++++++++++++++++++++++++ backend/ninja/Ninja.hpp | 21 +++++++++++ include/Conf.hpp | 10 ++++++ include/FileUtil.hpp | 1 + src/Conf.cpp | 10 ------ src/FileUtil.cpp | 14 ++++++++ src/main.cpp | 49 ++++++++++++++++++------- 8 files changed, 178 insertions(+), 24 deletions(-) create mode 100644 backend/ninja/Ninja.cpp create mode 100644 backend/ninja/Ninja.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ed5c57..784e6da 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) + external/xxhash.c backend/ninja/Ninja.cpp) add_executable(sibs ${SOURCE_FILES}) \ No newline at end of file diff --git a/backend/ninja/Ninja.cpp b/backend/ninja/Ninja.cpp new file mode 100644 index 0000000..7006d30 --- /dev/null +++ b/backend/ninja/Ninja.cpp @@ -0,0 +1,95 @@ +#include +#include "Ninja.hpp" +#include "../../include/FileUtil.hpp" + +using namespace std; + +namespace backend +{ + string join(const vector &list, const char *joinStr) + { + if(list.empty()) return ""; + string result; + long stringSize = 0; + long joinStrLen = strlen(joinStr); + int i = 0; + for(const string &str : list) + { + stringSize += str.size(); + if(!str.empty() && i > 0) + stringSize += joinStrLen; + ++i; + } + + result.reserve(stringSize); + + i = 0; + for(const string &str : list) + { + if(i > 0); + result += joinStr; + result += str; + ++i; + } + + return move(result); + } + + void Ninja::addSourceFile(const char *filepath) + { + if(filepath && !containsSourceFile(filepath)) + sourceFiles.emplace_back(filepath); + } + + bool Ninja::containsSourceFile(const char *filepath) const + { + for(const string &sourceFile : sourceFiles) + { + if(sourceFile == filepath) + return true; + } + return false; + } + + void Ninja::build(const std::string &packageName, const char *savePath) + { + if(sourceFiles.empty()) return; + printf("Package name: %s\n", packageName.c_str()); + + string result; + result.reserve(16384); + + result += "cflags = -Wall -Werror\n\n"; + + 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"; + + vector objectNames; + for(const string &sourceFile : sourceFiles) + { + //string sourceFileEncoded = sourceFile; + //replace(sourceFileEncoded, '/', '@'); + string objectName = packageName + "@exe/" + sourceFile + ".o"; + result += "build "; + result += objectName; + 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"; + objectNames.emplace_back(objectName); + } + + result += "build "; + result += packageName; + result += ": cpp_LINKER "; + result += join(objectNames, " "); + result += "\n"; + result += " LINK_ARGS = '-Wl,--no-undefined' '-Wl,--as-needed'\n\n"; + + sibs::fileOverwrite(savePath, sibs::StringView(result.data(), result.size())); + printf("Created ninja build file: %s\n", savePath); + } +} \ No newline at end of file diff --git a/backend/ninja/Ninja.hpp b/backend/ninja/Ninja.hpp new file mode 100644 index 0000000..ad71c80 --- /dev/null +++ b/backend/ninja/Ninja.hpp @@ -0,0 +1,21 @@ +#ifndef BACKEND_NINJA_HPP +#define BACKEND_NINJA_HPP + +#include +#include + +namespace backend +{ + class Ninja + { + public: + void addSourceFile(const char *filepath); + void build(const std::string &packageName, const char *savePath); + private: + bool containsSourceFile(const char *filepath) const; + private: + std::vector sourceFiles; + }; +} + +#endif //BACKEND_NINJA_HPP diff --git a/include/Conf.hpp b/include/Conf.hpp index c27046b..8b98189 100644 --- a/include/Conf.hpp +++ b/include/Conf.hpp @@ -6,6 +6,7 @@ #include "utils.hpp" #include #include +#include namespace sibs { @@ -55,6 +56,15 @@ namespace sibs class Parser; + class ParserException : public std::runtime_error + { + public: + ParserException(const std::string &errMsg) : runtime_error(errMsg) + { + + } + }; + class ConfigCallback { friend class Parser; diff --git a/include/FileUtil.hpp b/include/FileUtil.hpp index 345c72a..4083cb1 100644 --- a/include/FileUtil.hpp +++ b/include/FileUtil.hpp @@ -20,6 +20,7 @@ namespace sibs FileType getFileType(const char *path); void walkDirFiles(const char *directory, FileWalkCallbackFunc callbackFunc); Result getFileContent(const char *filepath); + bool fileOverwrite(const char *filepath, StringView data); } #endif //SIBS_FILEUTIL_HPP diff --git a/src/Conf.cpp b/src/Conf.cpp index cea1b01..56b1e2a 100644 --- a/src/Conf.cpp +++ b/src/Conf.cpp @@ -1,7 +1,6 @@ #include "../include/Conf.hpp" #include "../include/FileUtil.hpp" #include "../external/utf8/unchecked.h" -#include using namespace std; using u8string = utf8::unchecked::iterator; @@ -156,15 +155,6 @@ namespace sibs }; }; - class ParserException : public std::runtime_error - { - public: - ParserException(const string &errMsg) : runtime_error(errMsg) - { - - } - }; - class Parser { public: diff --git a/src/FileUtil.cpp b/src/FileUtil.cpp index 30fe03d..8502e84 100644 --- a/src/FileUtil.cpp +++ b/src/FileUtil.cpp @@ -18,6 +18,7 @@ namespace sibs } } + // TODO: Handle failure (directory doesn't exist, no permission etc) void walkDirFiles(const char *directory, FileWalkCallbackFunc callbackFunc) { tinydir_dir dir; @@ -50,6 +51,7 @@ namespace sibs size_t fileSize = ftell(file); fseek(file, 0, SEEK_SET); + // TODO: Change this to string so it can be deallocated and use std::move to prevent copies char *result = (char*)malloc(fileSize + 1); if(!result) { @@ -62,4 +64,16 @@ namespace sibs fclose(file); return Result::Ok(StringView(result, fileSize)); } + + bool fileOverwrite(const char *filepath, StringView data) + { + FILE *file = fopen(filepath, "wb"); + if(!file || errno != 0) + { + perror(filepath); + return false; + } + fwrite(data.data, 1, data.size, file); + fclose(file); + } } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index ce914b7..866d691 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,9 @@ #include #include "../include/FileUtil.hpp" #include "../include/Conf.hpp" +#include "../backend/ninja/Ninja.hpp" #include +#include using namespace std; using namespace sibs; @@ -50,6 +52,14 @@ void validateFilePath(const char *projectConfPath) class SibsConfig : public ConfigCallback { +public: + SibsConfig() : finishedProcessing(false) {} + + const string& getPackageName() const + { + assert(finishedProcessing); + return packageName; + } protected: void processObject(StringView name) override { @@ -78,15 +88,24 @@ protected: 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"); + } } void finished() override { - + finishedProcessing = true; } - private: StringView currentObject; + string packageName; + bool finishedProcessing; }; const char *sourceFileExtensions[] = { "cc", "cpp", "cxx" }; @@ -109,8 +128,8 @@ int main(int argc, const char **argv) if(argc != 2) usage(); - const char *projectPath = argv[1]; - validateDirectoryPath(projectPath); + string projectPath = argv[1]; + validateDirectoryPath(projectPath.c_str()); string projectConfFilePath = projectPath; projectConfFilePath += "/project.conf"; @@ -118,29 +137,33 @@ int main(int argc, const char **argv) SibsConfig sibsConfig; Result result = Config::readFromFile(projectConfFilePath.c_str(), sibsConfig); - if(result.isOk()) - { - - } - else + if(result.isErr()) { printf("Failed to read config: %s\n", result.getErrMsg().c_str()); exit(6); } - string projectSrcPath = string(projectPath) + "/src"; + string projectSrcPath = projectPath + "/src"; validateDirectoryPath(projectSrcPath.c_str()); - walkDirFiles(projectSrcPath.c_str(), [](tinydir_file *file) + + backend::Ninja ninja; + walkDirFiles(projectSrcPath.c_str(), [&ninja, &projectPath](tinydir_file *file) { if (isSourceFile(file)) { - printf("source file: %s\n", file->path); + printf("Adding source file: %s\n", file->path + projectPath.size() + 1); + ninja.addSourceFile(file->path + projectPath.size() + 1); } else { - printf("non source file: %s\n", file->path); + printf("Ignoring non-source file: %s\n", file->path + projectPath.size() + 1); } }); + // 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()); + return 0; } \ No newline at end of file -- cgit v1.2.3