#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; 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); } Ninja::Ninja(LibraryType _libraryType) : libraryType(_libraryType) { } 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; } Result validatePkgConfigPackageVersionExists(const Dependency &dependency) { Result dependencyValidationResult = PkgConfig::validatePackageExists(dependency.name); if(dependencyValidationResult.isErr()) return Result::Err(dependencyValidationResult.getErrMsg()); Result dependencyVersionValidationResult = PkgConfig::validatePackageVersionAtLeast(dependency.name, dependency.version); if(dependencyVersionValidationResult.isErr()) return Result::Err(dependencyVersionValidationResult.getErrMsg()); return Result::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 Ninja::getLinkerFlags(const vector &dependencies, LinkerFlagCallbackFunc linkerFlagCallbackFunc) const { if(dependencies.empty()) return Result::Ok(""); // TODO: Global library dir should be created during sibs installation string globalLibDir = getHomeDir(); globalLibDir += "/.sibs/lib"; string globalLibLinkerFlags; vector pkgConfigDependencies; for(const sibs::Dependency &dependency : dependencies) { 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::getStaticLibsLinkerFlags(globalLibDir, dependency.name, dependency.version, linkerFlagCallbackFunc); if(globalLibLinkerFlagsResult.isErr()) return globalLibLinkerFlagsResult; globalLibLinkerFlags += " "; globalLibLinkerFlags += globalLibLinkerFlagsResult.unwrap(); // TODO: If package doesn't exist, download it from github/server } } Result pkgConfigLinkerFlagsResult = PkgConfig::getDynamicLibsLinkerFlags(pkgConfigDependencies); if(pkgConfigLinkerFlagsResult.isErr()) return pkgConfigLinkerFlagsResult; linkerFlagCallbackFunc(pkgConfigLinkerFlagsResult.unwrap()); string allLinkerFlags = pkgConfigLinkerFlagsResult.unwrap(); allLinkerFlags += globalLibLinkerFlags; return Result::Ok(allLinkerFlags); } Result Ninja::createBuildFile(const std::string &packageName, const vector &dependencies, const char *savePath, LinkerFlagCallbackFunc linkerFlagCallbackFunc) { if(sourceFiles.empty()) return Result::Err("No source files provided"); string ninjaBuildFilePath = savePath; ninjaBuildFilePath += "/build.ninja"; string result; result.reserve(16384); string globalLibDir = getHomeDir(); globalLibDir += "/.sibs/lib"; result += "globalLibDir = '-I"; result += globalLibDir; result += "'\n\n"; result += "rule cpp_COMPILER\n"; result += " command = ccache c++ $ARGS -c $in -o $out\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) { // TODO: Handle tests differently. // Maybe test directory should have project file and sub directories with project files // should be their own project? if(_tinydir_strncmp(sourceFile.c_str(), "tests", 5) == 0) continue; //string sourceFileEncoded = sourceFile; //replace(sourceFileEncoded, '/', '@'); string objectName = packageName + "@exe/" + sourceFile + ".o"; result += "build "; result += objectName; result += ": cpp_COMPILER ../../"; result += sourceFile; result += "\n"; result += " ARGS = $globalLibDir '-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); } switch(libraryType) { case LibraryType::EXECUTABLE: { string allLinkerFlags; Result linkerFlags = getLinkerFlags(dependencies, [&allLinkerFlags](const string &linkerFlag) { allLinkerFlags += " "; allLinkerFlags += linkerFlag; }); if(linkerFlags.isErr()) return Result::Err(linkerFlags.getErrMsg()); result += "build "; result += packageName; result += ": " + linkerJob + " "; result += join(objectNames, " "); result += "\n"; result += " LINK_ARGS = '-Wl,--no-undefined' '-Wl,--as-needed' "; result += allLinkerFlags; result += "\n\n"; break; } case LibraryType::STATIC: { string allLinkerFlags; Result linkerFlags = getLinkerFlags(dependencies, linkerFlagCallbackFunc); if(linkerFlags.isErr()) return Result::Err(linkerFlags.getErrMsg()); result += "build "; result += packageName; result += ": " + linkerJob + " "; result += join(objectNames, " "); result += "\n\n"; break; } default: assert(false); return Result::Err("NOT IMPLEMENTED YET!"); } bool fileOverwritten = sibs::fileOverwrite(ninjaBuildFilePath.c_str(), sibs::StringView(result.data(), result.size())); if(!fileOverwritten) { string errMsg = "Failed to overwrite ninja build file: "; errMsg += ninjaBuildFilePath; return Result::Err(errMsg); } printf("Created ninja build file: %s\n", ninjaBuildFilePath.c_str()); return Result::Ok(true); } Result Ninja::build(const char *buildFilePath) { string command = "ninja -C '"; command += buildFilePath; command += "'"; Result execResult = exec(command.c_str(), true); if(execResult.isOk()) return Result::Ok(true); else return Result::Err(execResult.getErrMsg()); } }