From 28d6b571139998915bce147abb58617884431192 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Thu, 14 Dec 2017 15:22:06 +0100 Subject: Add support for dynamic libraries (shared objects) --- backend/ninja/Ninja.cpp | 116 ++++++++++++++++++++++++++++++++++++------------ backend/ninja/Ninja.hpp | 6 +-- include/GlobalLib.hpp | 2 +- include/Result.hpp | 2 + src/GlobalLib.cpp | 52 ++++++++++++++++------ src/PkgConfig.cpp | 4 -- 6 files changed, 132 insertions(+), 50 deletions(-) diff --git a/backend/ninja/Ninja.cpp b/backend/ninja/Ninja.cpp index ede150c..d8345a4 100644 --- a/backend/ninja/Ninja.cpp +++ b/backend/ninja/Ninja.cpp @@ -81,7 +81,7 @@ namespace backend // 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 + Result Ninja::getLinkerFlags(const vector &dependencies, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallback) const { if(dependencies.empty()) return Result::Ok(true); @@ -120,13 +120,13 @@ namespace backend else { if(!pkgConfigLinkerFlagsResult.unwrap().empty()) - linkerFlagCallbackFunc(pkgConfigLinkerFlagsResult.unwrap()); + dynamicLinkerFlagCallback(pkgConfigLinkerFlagsResult.unwrap()); } for(const Dependency &globalLibDependency : globalLibDependencies) { printf("Dependency %s is missing from pkg-config, trying global lib\n", globalLibDependency.name.c_str()); - Result globalLibLinkerFlagsResult = GlobalLib::getStaticLibsLinkerFlags(globalLibDir, globalLibDependency.name, globalLibDependency.version, linkerFlagCallbackFunc); + Result globalLibLinkerFlagsResult = GlobalLib::getLibsLinkerFlags(globalLibDir, globalLibDependency.name, globalLibDependency.version, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallback); if(globalLibLinkerFlagsResult.isErr()) { if(globalLibLinkerFlagsResult.getErrorCode() == GlobalLib::DependencyError::DEPENDENCY_NOT_FOUND || globalLibLinkerFlagsResult.getErrorCode() == GlobalLib::DependencyError::DEPENDENCY_VERSION_NO_MATCH) @@ -142,7 +142,7 @@ namespace backend if(downloadDependencyResult.isErr()) return downloadDependencyResult; - globalLibLinkerFlagsResult = GlobalLib::getStaticLibsLinkerFlags(globalLibDir, globalLibDependency.name, globalLibDependency.version, linkerFlagCallbackFunc); + globalLibLinkerFlagsResult = GlobalLib::getLibsLinkerFlags(globalLibDir, globalLibDependency.name, globalLibDependency.version, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallback); if(globalLibLinkerFlagsResult.isErr()) return Result::Err(globalLibLinkerFlagsResult); } @@ -156,8 +156,9 @@ namespace backend return Result::Ok(true); } - Result Ninja::createBuildFile(const std::string &packageName, const vector &dependencies, const char *savePath, LinkerFlagCallbackFunc linkerFlagCallbackFunc) + Result Ninja::createBuildFile(const std::string &packageName, const vector &dependencies, const char *savePath, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallback) { + // TODO: Do not quit here if no source files are provided. The source-less project could have dependencies if(sourceFiles.empty()) return Result::Err("No source files provided"); @@ -178,35 +179,50 @@ namespace backend result += globalLibDir; result += "'\n\n"; - result += "rule cpp_COMPILER\n"; - result += " command = ccache c++ $ARGS -c $in -o $out\n\n"; - - string linkerJob; + string buildJob; switch(libraryType) { case LibraryType::EXECUTABLE: { - result += "rule cpp_EXEC_LINKER\n"; + result += "rule cpp_COMPILER\n"; + result += " command = ccache c++ $ARGS -c $in -o $out\n\n"; + + result += "rule cpp_BUILD_EXEC\n"; result += " command = ccache c++ $ARGS -o $out $in $LINK_ARGS $aliasing\n\n"; - linkerJob = "cpp_EXEC_LINKER"; + buildJob = "cpp_BUILD_EXEC"; break; } case LibraryType::STATIC: { - result += "rule cpp_STATIC_LINKER\n"; + result += "rule cpp_COMPILER\n"; + result += " command = ccache c++ $ARGS -c -fPIC $in -o $out\n\n"; + + result += "rule cpp_BUILD_STATIC\n"; result += " command = ar rcs lib"; result += packageName; result += ".a"; result += " $in\n\n"; - linkerJob = "cpp_STATIC_LINKER"; + buildJob = "cpp_BUILD_STATIC"; + break; + } + case LibraryType::DYNAMIC: + { + result += "rule cpp_COMPILER\n"; + result += " command = ccache c++ $ARGS -c -fPIC $in -o $out\n\n"; + + // --whole-archive + result += "rule cpp_BUILD_DYNAMIC\n"; + result += " command = ccache c++ $in -shared -o $out $LINK_ARGS $aliasing\n\n"; + buildJob = "cpp_BUILD_DYNAMIC"; break; } default: assert(false); - return Result::Err("Building a dynamic library is not supported yet"); + return Result::Err("Unexpected error"); } vector objectNames; + objectNames.reserve(sourceFiles.size()); for(const string &sourceFile : sourceFiles) { // TODO: Handle tests differently. @@ -227,46 +243,80 @@ namespace backend objectNames.emplace_back(objectName); } + string allLinkerFlags; + if(!staticLinkerFlagCallbackFunc || libraryType == LibraryType::DYNAMIC) + { + staticLinkerFlagCallbackFunc = [&allLinkerFlags](const string &linkerFlag) + { + allLinkerFlags += " "; + allLinkerFlags += linkerFlag; + }; + } + + // TODO: If project contains no source files, then we shouldn't override this function + dynamicLinkerFlagCallback = [&allLinkerFlags](const string &linkerFlag) + { + allLinkerFlags += " "; + allLinkerFlags += linkerFlag; + }; + switch(libraryType) { case LibraryType::EXECUTABLE: { - string allLinkerFlags; - Result linkerFlags = getLinkerFlags(dependencies, [&allLinkerFlags](const string &linkerFlag) - { - allLinkerFlags += " "; - allLinkerFlags += linkerFlag; - }); + Result linkerFlags = getLinkerFlags(dependencies, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallback); if(linkerFlags.isErr()) return Result::Err(linkerFlags.getErrMsg()); result += "build "; result += packageName; - result += ": " + linkerJob + " "; + result += ": " + buildJob + " "; result += join(objectNames, " "); result += "\n"; - result += " LINK_ARGS = '-Wl,--no-undefined' '-Wl,--as-needed' "; - result += allLinkerFlags; + result += " LINK_ARGS = '-Wl,--no-undefined,--as-needed' "; + if(!allLinkerFlags.empty()) + { + result += allLinkerFlags; + } result += "\n\n"; break; } case LibraryType::STATIC: { - string allLinkerFlags; - Result linkerFlags = getLinkerFlags(dependencies, linkerFlagCallbackFunc); + Result linkerFlags = getLinkerFlags(dependencies, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallback); if(linkerFlags.isErr()) return linkerFlags; result += "build "; result += packageName; - result += ": " + linkerJob + " "; + result += ": " + buildJob + " "; result += join(objectNames, " "); result += "\n\n"; break; } + case LibraryType::DYNAMIC: + { + Result linkerFlags = getLinkerFlags(dependencies, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallback); + if(linkerFlags.isErr()) + return Result::Err(linkerFlags.getErrMsg()); + + result += "build lib"; + result += packageName; + result += ".so: " + buildJob + " "; + result += join(objectNames, " "); + result += "\n"; + result += " LINK_ARGS = '-Wl,--no-undefined,--as-needed' "; + if(!allLinkerFlags.empty()) + { + result += allLinkerFlags; + //result += " '-Wl,--no-whole-archive'"; + } + result += "\n\n"; + break; + } default: assert(false); - return Result::Err("Building a dynamic library is not supported yet"); + return Result::Err("Unexpected error"); } Result fileOverwriteResult = sibs::fileOverwrite(ninjaBuildFilePath.c_str(), sibs::StringView(result.data(), result.size())); @@ -282,9 +332,17 @@ namespace backend string command = "ninja -C '"; command += buildFilePath; command += "'"; - Result execResult = exec(command.c_str(), true); + Result execResult = exec(command.c_str()); if(execResult.isOk()) - return Result::Ok(true); + { + if(execResult.unwrap().exitCode == 0) + { + printf("%s", execResult.unwrap().execStdout.c_str()); + return Result::Ok(true); + } + else + return Result::Err(execResult.unwrap().execStdout); + } else return Result::Err(execResult.getErrMsg()); } diff --git a/backend/ninja/Ninja.hpp b/backend/ninja/Ninja.hpp index b82139c..a0239d2 100644 --- a/backend/ninja/Ninja.hpp +++ b/backend/ninja/Ninja.hpp @@ -17,19 +17,19 @@ namespace backend enum class LibraryType { EXECUTABLE, + STATIC, DYNAMIC, - STATIC }; Ninja(LibraryType libraryType = LibraryType::EXECUTABLE); void addSourceFile(const char *filepath); const std::vector& getSourceFiles() const; - sibs::Result createBuildFile(const std::string &packageName, const std::vector &dependencies, const char *savePath, sibs::LinkerFlagCallbackFunc linkerFlagCallbackFunc = nullptr); + sibs::Result createBuildFile(const std::string &packageName, const std::vector &dependencies, const char *savePath, sibs::LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc = nullptr, sibs::LinkerFlagCallbackFunc dynamicLinkerFlagCallback = nullptr); sibs::Result build(const char *buildFilePath); private: bool containsSourceFile(const char *filepath) const; - sibs::Result getLinkerFlags(const std::vector &dependencies, sibs::LinkerFlagCallbackFunc linkerFlagCallbackFunc) const; + sibs::Result getLinkerFlags(const std::vector &dependencies, sibs::LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, sibs::LinkerFlagCallbackFunc dynamicLinkerFlagCallback) const; private: std::vector sourceFiles; LibraryType libraryType; diff --git a/include/GlobalLib.hpp b/include/GlobalLib.hpp index ca542f9..78511c3 100644 --- a/include/GlobalLib.hpp +++ b/include/GlobalLib.hpp @@ -17,7 +17,7 @@ namespace sibs }; static Result validatePackageExists(const std::string &globalLibRootDir, const std::string &name); - static Result getStaticLibsLinkerFlags(const std::string &globalLibRootDir, const std::string &name, const std::string &version, LinkerFlagCallbackFunc linkerFlagCallbackFunc); + static Result getLibsLinkerFlags(const std::string &globalLibRootDir, const std::string &name, const std::string &version, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc); static Result downloadDependency(const Dependency &dependency); }; } diff --git a/include/Result.hpp b/include/Result.hpp index eb0aa01..e8f4d12 100644 --- a/include/Result.hpp +++ b/include/Result.hpp @@ -53,6 +53,8 @@ namespace sibs { return errorCode; } + + operator bool () { return isOk(); } private: Result(const T &_value = T()) : value(_value) {} private: diff --git a/src/GlobalLib.cpp b/src/GlobalLib.cpp index 617f993..a07e23d 100644 --- a/src/GlobalLib.cpp +++ b/src/GlobalLib.cpp @@ -55,7 +55,7 @@ namespace sibs return false; } - Result GlobalLib::getStaticLibsLinkerFlags(const string &globalLibRootDir, const string &name, const string &version, LinkerFlagCallbackFunc linkerFlagCallbackFunc) + Result GlobalLib::getLibsLinkerFlags(const string &globalLibRootDir, const string &name, const string &version, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc) { Result packageExistsResult = validatePackageExists(globalLibRootDir, name); if(packageExistsResult.isErr()) @@ -121,7 +121,22 @@ namespace sibs return Result::Err(errMsg); } - backend::Ninja ninja(backend::Ninja::LibraryType::STATIC); + backend::Ninja::LibraryType libraryType; + switch(sibsConfig.getPackageType()) + { + case PackageType::STATIC: + libraryType = backend::Ninja::LibraryType::STATIC; + break; + case PackageType::DYNAMIC: + case PackageType::LIBRARY: + libraryType = backend::Ninja::LibraryType::DYNAMIC; + break; + default: + assert(false); + return Result::Err("Unexpected error"); + } + + backend::Ninja ninja(libraryType); walkDirFilesRecursive(packageDir.c_str(), [&ninja, &packageDir](tinydir_file *file) { if (isSourceFile(file)) @@ -141,15 +156,26 @@ namespace sibs else { string debugBuildPath = packageDir + "/sibs-build/debug"; - string staticLibPath = debugBuildPath; - staticLibPath += "/lib"; - staticLibPath += name; - staticLibPath += ".a"; - - string staticLibPathCmd = "'"; - staticLibPathCmd += staticLibPath; - staticLibPathCmd += "'"; - linkerFlagCallbackFunc(staticLibPathCmd); + string libPath = debugBuildPath; + libPath += "/lib"; + libPath += name; + if(libraryType == backend::Ninja::LibraryType::STATIC) + { + libPath += ".a"; + string libPathCmd = "'"; + libPathCmd += libPath; + libPathCmd += "'"; + staticLinkerFlagCallbackFunc(libPathCmd); + libPath += ".a"; + } + else + { + libPath += ".so"; + string libPathCmd = "'"; + libPathCmd += libPath; + libPathCmd += "'"; + dynamicLinkerFlagCallbackFunc(libPathCmd); + } // TODO: Use different directories depending on the project type, but .o build files should be in the same directory // no matter what project type, since they are used for executables, static/dynamic libraries @@ -157,7 +183,7 @@ namespace sibs if(createBuildDirResult.isErr()) return Result::Err(createBuildDirResult); - Result buildFileResult = ninja.createBuildFile(sibsConfig.getPackageName(), sibsConfig.getDependencies(), debugBuildPath.c_str(), linkerFlagCallbackFunc); + Result buildFileResult = ninja.createBuildFile(sibsConfig.getPackageName(), sibsConfig.getDependencies(), debugBuildPath.c_str(), staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc); if (buildFileResult.isErr()) return Result::Err(buildFileResult.getErrMsg()); @@ -165,7 +191,7 @@ namespace sibs if (buildResult.isErr()) return Result::Err(buildResult.getErrMsg()); - return Result::Ok(staticLibPath); + return Result::Ok(libPath); } } diff --git a/src/PkgConfig.cpp b/src/PkgConfig.cpp index 5f85549..620b1a7 100644 --- a/src/PkgConfig.cpp +++ b/src/PkgConfig.cpp @@ -25,9 +25,7 @@ namespace sibs command += "'"; Result execResult = exec(command.c_str()); if(execResult.isErr()) - { return Result::Err(execResult.getErrMsg()); - } if(execResult.unwrap().exitCode == 1) { @@ -63,9 +61,7 @@ namespace sibs command += "'"; Result execResult = exec(command.c_str()); if(execResult.isErr()) - { return Result::Err(execResult.getErrMsg()); - } if(execResult.unwrap().exitCode == 1) { -- cgit v1.2.3