From 23117906c571714b0b55caf35cf9f876d1f9fa2e Mon Sep 17 00:00:00 2001 From: dec05eba Date: Wed, 21 Mar 2018 14:56:51 +0100 Subject: Add sub projects (should be used with git submodules) Fix issue where static lib dependencies are not built correctly because their dynamic lib dependencies are not propagated to dependant project --- backend/BackendUtils.cpp | 85 ++++++++++++++++++++++++++++++++++++++++++++++ backend/BackendUtils.hpp | 15 +++++++++ backend/ninja/Ninja.cpp | 88 ++++++++++++++++++++++++++++++++---------------- backend/ninja/Ninja.hpp | 23 ++++++++++++- 4 files changed, 181 insertions(+), 30 deletions(-) create mode 100644 backend/BackendUtils.cpp create mode 100644 backend/BackendUtils.hpp (limited to 'backend') diff --git a/backend/BackendUtils.cpp b/backend/BackendUtils.cpp new file mode 100644 index 0000000..68794f5 --- /dev/null +++ b/backend/BackendUtils.cpp @@ -0,0 +1,85 @@ +#include "BackendUtils.hpp" +#include "../include/FileUtil.hpp" +#include "ninja/Ninja.hpp" + +using namespace std; +using namespace sibs; + +namespace backend +{ + bool isPathSubPathOf(const FileString &path, const FileString &subPathOf) + { + return _tinydir_strncmp(path.c_str(), subPathOf.c_str(), subPathOf.size()) == 0; + } + + const _tinydir_char_t *sourceFileExtensions[] = { TINYDIR_STRING("c"), TINYDIR_STRING("cc"), TINYDIR_STRING("cpp"), TINYDIR_STRING("cxx"), TINYDIR_STRING("c++") }; + bool BackendUtils::isSourceFile(tinydir_file *file) + { + if(!file->is_reg) + return false; + + for(const _tinydir_char_t *sourceFileExtension : sourceFileExtensions) + { + if(_tinydir_strcmp(sourceFileExtension, file->extension) == 0) + return true; + } + + return false; + } + + void BackendUtils::collectSourceFiles(const _tinydir_char_t *projectPath, Ninja *ninjaProject, const SibsConfig &sibsConfig) + { + walkDir(projectPath, [ninjaProject, &sibsConfig](tinydir_file *file) + { + FileString pathNative = file->path; + #if OS_FAMILY == OS_FAMILY_WINDOWS + replaceChar(pathNative, L'/', L'\\'); + #endif + if(file->is_reg) + { + if (isSourceFile(file)) + { + string filePathUtf8 = toUtf8(pathNative.c_str() + sibsConfig.getProjectPath().size()); + ninjaProject->addSourceFile(filePathUtf8.c_str()); + } + else + { + //printf("Ignoring non-source file: %s\n", file->path + projectPath.size()); + } + } + else + { + if (!sibsConfig.getTestPath().empty() && isPathSubPathOf(pathNative.c_str(), sibsConfig.getTestPath())) + { + string filePathUtf8 = toUtf8(pathNative.c_str()); + ninjaProject->addTestSourceDir(filePathUtf8.c_str()); + } + else if(!directoryToIgnore(pathNative, sibsConfig.getIgnoreDirs())) + { + FileString projectConfPath = file->path; + #if OS_FAMILY == OS_FAMILY_WINDOWS + projectConfPath += L'\\'; + #else + projectConfPath += '/'; + #endif + projectConfPath += TINYDIR_STRING("project.conf"); + + auto projectConfFileType = getFileType(projectConfPath.c_str()); + if(projectConfFileType == FileType::REGULAR) + { + backend::Ninja *subProject = new backend::Ninja(); + + SibsConfig *subProjectConfig = new SibsConfig(sibsConfig.getCompiler(), file->path, sibsConfig.getOptimizationLevel(), false); + FileString subProjectBuildPath; + readSibsConfig(file->path, projectConfPath, *subProjectConfig, subProjectBuildPath); + + collectSourceFiles(file->path, subProject, *subProjectConfig); + ninjaProject->addSubProject(subProject, subProjectConfig, move(subProjectBuildPath)); + } + else + collectSourceFiles(file->path, ninjaProject, sibsConfig); + } + } + }); + } +} diff --git a/backend/BackendUtils.hpp b/backend/BackendUtils.hpp new file mode 100644 index 0000000..b2fe280 --- /dev/null +++ b/backend/BackendUtils.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include "../include/Conf.hpp" + +namespace backend +{ + class Ninja; + + class BackendUtils + { + public: + static bool isSourceFile(tinydir_file *file); + static void collectSourceFiles(const _tinydir_char_t *projectPath, Ninja *ninjaProject, const sibs::SibsConfig &sibsConfig); + }; +} diff --git a/backend/ninja/Ninja.cpp b/backend/ninja/Ninja.cpp index fb081de..614d08c 100644 --- a/backend/ninja/Ninja.cpp +++ b/backend/ninja/Ninja.cpp @@ -1,4 +1,5 @@ #include +#include "../BackendUtils.hpp" #include "Ninja.hpp" #include "../../include/FileUtil.hpp" #include "../../include/Exec.hpp" @@ -237,6 +238,11 @@ namespace backend if(!containsDependency(binaryFile)) binaryDependencies.emplace_back(binaryFile); } + + void Ninja::addSubProject(Ninja *subProject, SibsConfig *config, sibs::FileString &&buildPath) + { + subProjects.emplace_back(NinjaSubProject{ subProject, config, move(buildPath) }); + } const std::vector& Ninja::getSourceFiles() const { @@ -318,7 +324,7 @@ namespace backend else { const PkgConfigFlags &pkgConfigFlag = pkgConfigFlagsResult.unwrap(); - if (dynamicLinkerFlagCallback && !pkgConfigFlag.linkerFlags.empty()) + if (!pkgConfigFlag.linkerFlags.empty()) dynamicLinkerFlagCallback(pkgConfigFlag.linkerFlags); if(!pkgConfigFlag.cflags.empty()) cflagsCallbackFunc(pkgConfigFlag.cflags); @@ -338,6 +344,26 @@ namespace backend C, CPP }; + + Result Ninja::buildSubProjects(LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallback, GlobalIncludeDirCallbackFunc globalIncludeDirCallback) + { + for(auto &subProject : subProjects) + { + if(subProject.config->getPackageType() == PackageType::EXECUTABLE) + { + string errMsg = "The sub project "; + errMsg += toUtf8(subProject.buildPath); + errMsg += " is an executable. Only libraries can be sub projects"; + return Result::Err(errMsg); + } + + Result buildResult = subProject.subProject->build(*subProject.config, subProject.buildPath.c_str(), staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallback, globalIncludeDirCallback); + if(!buildResult) + return buildResult; + } + + return Result::Ok(true); + } Result Ninja::build(const SibsConfig &config, const _tinydir_char_t *savePath, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallback, GlobalIncludeDirCallbackFunc globalIncludeDirCallback) { @@ -405,7 +431,7 @@ namespace backend #endif // TODO: Somehow check loading order, because it has to be correct to work.. Or does it for dynamic libraries? - // Anyways it's required for static libraries (especially on Windows) + // Anyways it's required for static libraries for (const string &binaryDependency : binaryDependencies) { allLinkerFlags += " "; @@ -413,6 +439,7 @@ namespace backend } string staticLinkerFlags; + auto parentProjStaticLinkerFlagCallbackFunc = staticLinkerFlagCallbackFunc; if (!staticLinkerFlagCallbackFunc || libraryType == LibraryType::DYNAMIC) { staticLinkerFlagCallbackFunc = [&staticLinkerFlags](const string &linkerFlag) @@ -423,8 +450,8 @@ namespace backend } string dynamicLinkerFlags; - // TODO: Do same for cmake - if (!sourceFiles.empty()) + auto parentProjDynamicLinkerFlagCallbackFunc = dynamicLinkerFlagCallback; + if(!dynamicLinkerFlagCallback || libraryType != LibraryType::STATIC) { dynamicLinkerFlagCallback = [&dynamicLinkerFlags](const string &linkerFlag) { @@ -439,6 +466,10 @@ namespace backend cflags += " "; cflags += dependencyCflags; }; + + Result buildSubProjectResult = buildSubProjects(staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallback, globalIncludeDirCallback); + if(!buildSubProjectResult) + return buildSubProjectResult; Result linkerFlags = getLinkerFlags(config, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallback, globalIncludeDirCallback, cflagsCallbackFunc); if (linkerFlags.isErr()) @@ -739,13 +770,13 @@ namespace backend objectNames.emplace_back(objectName); } - string projectGeneratedBinary; + string projectGeneratedBinaryFlags; if (!sourceFiles.empty()) { - projectGeneratedBinary = allLinkerFlags; - projectGeneratedBinary += " \""; + string projectGeneratedBinary = "\""; projectGeneratedBinary += savePathUtf8; projectGeneratedBinary += "/"; + switch (libraryType) { case LibraryType::EXECUTABLE: @@ -776,7 +807,11 @@ namespace backend result += allLinkerFlags; } result += "\n\n"; + projectGeneratedBinary += config.getPackageName(); + #if OS_FAMILY == OS_FAMILY_WINDOWS + projectGeneratedBinary += ".exe"; + #endif break; } case LibraryType::STATIC: @@ -804,6 +839,11 @@ namespace backend break; } } + + projectGeneratedBinary += "\""; + if(parentProjStaticLinkerFlagCallbackFunc) + parentProjStaticLinkerFlagCallbackFunc(projectGeneratedBinary); + break; } case LibraryType::DYNAMIC: @@ -841,13 +881,19 @@ namespace backend //result += " '-Wl,--no-whole-archive'"; } result += "\n\n"; + + projectGeneratedBinary += "\""; + if(parentProjDynamicLinkerFlagCallbackFunc) + parentProjDynamicLinkerFlagCallbackFunc(projectGeneratedBinary); + break; } default: assert(false); return Result::Err("Unexpected error"); } - projectGeneratedBinary += "\""; + + projectGeneratedBinaryFlags = allLinkerFlags + " " + projectGeneratedBinary; Result fileOverwriteResult = sibs::fileOverwrite(ninjaBuildFilePath.c_str(), sibs::StringView(result.data(), result.size())); if (fileOverwriteResult.isErr()) @@ -863,30 +909,14 @@ namespace backend // TODO: If tests are being run (sibs test) and root project is an executable, do not run compile (above code) as executable. // Sibs test will compile root project as dynamic library so you end up compiling the project twice, first as an executable and then as a dynamic library. // Even if the root project has been built before and there is cached object, it will take a few seconds to run compile - Result buildTestResult = buildTests(projectGeneratedBinary, config, savePath, dependencyExportIncludeDirs); + Result buildTestResult = buildTests(projectGeneratedBinaryFlags, config, savePath, dependencyExportIncludeDirs); if(!buildTestResult) return buildTestResult; return Result::Ok(true); } - // TODO: Add "c++" file extension, seems to be used in some places? - const _tinydir_char_t *sourceFileExtensions[] = { TINYDIR_STRING("c"), TINYDIR_STRING("cc"), TINYDIR_STRING("cpp"), TINYDIR_STRING("cxx") }; - bool isSourceFile(tinydir_file *file) - { - if(!file->is_reg) - return false; - - for(const _tinydir_char_t *sourceFileExtension : sourceFileExtensions) - { - if(_tinydir_strcmp(sourceFileExtension, file->extension) == 0) - return true; - } - - return false; - } - - Result Ninja::buildTests(const std::string &projectGeneratedBinary, const SibsConfig &config, const _tinydir_char_t *savePath, const string &parentDependencyExportIncludeDirs) + Result Ninja::buildTests(const std::string &projectGeneratedBinaryFlags, const SibsConfig &config, const _tinydir_char_t *savePath, const string &parentDependencyExportIncludeDirs) { if(testSourceDirs.empty() || !config.shouldBuildTests()) return Result::Ok(true); @@ -940,12 +970,12 @@ namespace backend backend::Ninja ninja; ninja.addGlobalIncludeDirs(parentExportIncludeDirs); - if(!projectGeneratedBinary.empty()) - ninja.addDependency(projectGeneratedBinary); + if(!projectGeneratedBinaryFlags.empty()) + ninja.addDependency(projectGeneratedBinaryFlags); // TODO: Use same source file finder as in main.cpp walkDirFilesRecursive(testSourceDirNative.c_str(), [&ninja, &sibsTestConfig](tinydir_file *file) { - if (isSourceFile(file)) + if (backend::BackendUtils::isSourceFile(file)) { string filePathUtf8 = toUtf8(file->path + sibsTestConfig.getProjectPath().size() + 1); ninja.addSourceFile(filePathUtf8.c_str()); diff --git a/backend/ninja/Ninja.hpp b/backend/ninja/Ninja.hpp index 8411137..7bbff51 100644 --- a/backend/ninja/Ninja.hpp +++ b/backend/ninja/Ninja.hpp @@ -11,6 +11,24 @@ namespace backend { + class Ninja; + + struct NinjaSubProject + { + Ninja *subProject; + sibs::SibsConfig *config; + sibs::FileString buildPath; + + NinjaSubProject() : subProject(nullptr), config(nullptr) {} + NinjaSubProject(Ninja *_subProject, sibs::SibsConfig *_config, sibs::FileString &&_buildPath) : + subProject(_subProject), + config(_config), + buildPath(move(_buildPath)) + { + + } + }; + class Ninja { public: @@ -27,10 +45,12 @@ namespace backend void addSourceFile(const char *filepath); void addTestSourceDir(const char *dir); void addDependency(const std::string &binaryFile); + void addSubProject(Ninja *subProject, sibs::SibsConfig *config, sibs::FileString &&buildPath); const std::vector& getSourceFiles() const; sibs::Result build(const sibs::SibsConfig &config, const _tinydir_char_t *savePath, sibs::LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc = nullptr, sibs::LinkerFlagCallbackFunc dynamicLinkerFlagCallback = nullptr, sibs::GlobalIncludeDirCallbackFunc globalIncludeDirCallback = nullptr); private: - sibs::Result buildTests(const std::string &projectGeneratedBinary, const sibs::SibsConfig &config, const _tinydir_char_t *savePath, const std::string &parentDependencyExportIncludeDirs); + sibs::Result buildSubProjects(sibs::LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, sibs::LinkerFlagCallbackFunc dynamicLinkerFlagCallback, sibs::GlobalIncludeDirCallbackFunc globalIncludeDirCallback); + sibs::Result buildTests(const std::string &projectGeneratedBinaryFlags, const sibs::SibsConfig &config, const _tinydir_char_t *savePath, const std::string &parentDependencyExportIncludeDirs); bool containsSourceFile(const std::string &filepath) const; bool containsTestSourceDir(const std::string &dir) const; bool containsDependency(const std::string &dependency) const; @@ -41,6 +61,7 @@ namespace backend std::vector sourceFiles; std::vector testSourceDirs; std::vector binaryDependencies; + std::vector subProjects; }; } -- cgit v1.2.3