#include "../include/CmakeModule.hpp" #include "../include/Exec.hpp" #include "../include/GlobalLib.hpp" #include "../include/PkgConfig.hpp" #if OS_FAMILY == OS_FAMILY_POSIX #define nprintf printf #else #define nprintf wprintf #endif using namespace std; namespace sibs { Result CmakeModule::compile(const SibsConfig &config, const FileString &buildPath, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback) { Result globalLibDirResult = getHomeDir(); if (!globalLibDirResult) return Result::Err(globalLibDirResult); FileString globalLibDir = globalLibDirResult.unwrap(); globalLibDir += TINYDIR_STRING("/.sibs/lib"); Result createGlobalLibDirResult = createDirectoryRecursive(globalLibDir.c_str()); if(createGlobalLibDirResult.isErr()) return createGlobalLibDirResult; // TODO: This code was copied from Ninja.cpp, convert it to work for CMake #if OS_TYPE == OS_TYPE_LINUX // TODO: Allow configuring default linking flags. Maybe have `package.useThreads = false` to disable this flag string allLinkerFlags = "-pthread"; #else string allLinkerFlags = ""; #endif #if 0 // 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) for (const string &binaryDependency : binaryDependencies) { allLinkerFlags += " "; allLinkerFlags += binaryDependency; } #endif string staticLinkerFlags; if (!staticLinkerFlagCallbackFunc || (config.getPackageType() == PackageType::DYNAMIC || config.getPackageType() == PackageType::LIBRARY)) { staticLinkerFlagCallbackFunc = [&staticLinkerFlags](const string &linkerFlag) { staticLinkerFlags += " "; staticLinkerFlags += linkerFlag; }; } string dynamicLinkerFlags; // TODO: If project contains no source files, then we shouldn't override this function... why? if(!dynamicLinkerFlagCallbackFunc || config.getPackageType() != PackageType::STATIC) { dynamicLinkerFlagCallbackFunc = [&dynamicLinkerFlags](const string &linkerFlag) { dynamicLinkerFlags += " "; dynamicLinkerFlags += linkerFlag; }; } // TODO: Create a cmake module that contains library/include path for the dependencies (https://cmake.org/Wiki/CMake:How_To_Find_Libraries). // Modify the project CMakeLists.txt and add: list(APPEND CMAKE_MODULE_PATH "PathToDependenciesCmakeModulesGoesHere"). // CMakeLists.txt may contain: set(CMAKE_MODULE_PATH "PathToModules"). This needs to be replaced with list append, // otherwise our added module path is replaced. // It may work to do like vcpkg instead - to use -DCMAKE_TOOLCHAIN_FILE program argument to specify path to script (https://github.com/Microsoft/vcpkg/blob/master/docs/examples/using-sqlite.md) vector globalLibDependencies; #if OS_FAMILY == OS_FAMILY_POSIX vector pkgConfigDependencies; for(PackageListDependency *dependency : config.getPackageListDependencies()) { Result pkgConfigDependencyValidation = PkgConfig::validatePkgConfigPackageVersionExists(dependency); if(pkgConfigDependencyValidation.isOk()) { pkgConfigDependencies.push_back(dependency); } else { globalLibDependencies.push_back(dependency); } } // TODO: Use getDynamicLibsFlags instead which includes cflags? Result pkgConfigLinkerFlagsResult = PkgConfig::getDynamicLibsLinkerFlags(pkgConfigDependencies); if (pkgConfigLinkerFlagsResult.isErr()) { printf("%s, using global lib...\n", pkgConfigLinkerFlagsResult.getErrMsg().c_str()); globalLibDependencies.reserve(globalLibDependencies.size() + pkgConfigDependencies.size()); for (PackageListDependency *pkgConfigDependency : pkgConfigDependencies) { globalLibDependencies.push_back(pkgConfigDependency); } pkgConfigDependencies.clear(); } else { if (!pkgConfigLinkerFlagsResult.unwrap().empty()) dynamicLinkerFlagCallbackFunc(pkgConfigLinkerFlagsResult.unwrap()); } #else for (const Dependency &dependency : dependencies) { globalLibDependencies.push_back(dependency); } #endif Result globalLibResult = GlobalLib::getLibs(globalLibDependencies, config, globalLibDir, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); if(!globalLibResult) return globalLibResult; allLinkerFlags += staticLinkerFlags + dynamicLinkerFlags; Result createBuildDirResult = createDirectoryRecursive(buildPath.c_str()); if (createBuildDirResult.isErr()) return createBuildDirResult; FileString cmd = TINYDIR_STRING("cmake "); switch(config.getPackageType()) { case PackageType::EXECUTABLE: { cmd += config.getCmakeArgs(); break; } case PackageType::STATIC: { cmd += config.getCmakeArgsStatic(); break; } case PackageType::DYNAMIC: case PackageType::LIBRARY: { cmd += config.getCmakeArgsDynamic(); break; } } cmd += TINYDIR_STRING(" \"-B"); cmd += buildPath; cmd += TINYDIR_STRING("\" \"-H"); switch(config.getPackageType()) { case PackageType::EXECUTABLE: { cmd += config.getCmakeDir(); break; } case PackageType::STATIC: { cmd += config.getCmakeDirStatic(); break; } case PackageType::DYNAMIC: case PackageType::LIBRARY: { cmd += config.getCmakeDirDynamic(); break; } } cmd += TINYDIR_STRING("\""); nprintf("Compiling with cmake with arguments: %s\n", cmd.c_str()); Result execResult = exec(cmd.c_str(), true); if(execResult.isOk()) { if(execResult.unwrap().exitCode != 0) return Result::Err(execResult.unwrap().execStdout); } else return Result::Err(execResult); FileString ninjaCommand = TINYDIR_STRING("ninja -C \""); ninjaCommand += buildPath; ninjaCommand += TINYDIR_STRING("\""); nprintf("Compiling cmake generated ninja file: %s\n", ninjaCommand.c_str()); execResult = exec(ninjaCommand.c_str(), true); if(execResult.isOk()) { if(execResult.unwrap().exitCode != 0) return Result::Err(execResult.unwrap().execStdout); } else return Result::Err(execResult); if(config.getPackageType() != PackageType::EXECUTABLE) { string buildPathUtf8 = toUtf8(buildPath); nprintf("Searching for libraries generate by cmake in build path: %s\n", buildPathUtf8.c_str()); walkDirFilesRecursive(buildPath.c_str(), [&config, &staticLinkerFlagCallbackFunc, &dynamicLinkerFlagCallbackFunc](tinydir_file *file) { if(_tinydir_strcmp(file->extension, CONFIG_DYNAMIC_LIB_FILE_EXTENSION) == 0 || _tinydir_strcmp(file->extension, CONFIG_STATIC_LIB_FILE_EXTENSION) == 0) { string libFileUtf8 = toUtf8(file->path); nprintf("Library generated by cmake: %s\n", libFileUtf8.c_str()); switch(config.getPackageType()) { case PackageType::STATIC: { string libFileCmd = "\""; libFileCmd += libFileUtf8; libFileCmd += "\""; staticLinkerFlagCallbackFunc(libFileCmd); break; } case PackageType::DYNAMIC: case PackageType::LIBRARY: { string libFileCmd = "\""; libFileCmd += libFileUtf8; libFileCmd += "\""; dynamicLinkerFlagCallbackFunc(libFileCmd); break; } } } }); // TODO: Clean this up. The below code is indentical to code in Ninja.cpp....... string projectPathUtf8 = toUtf8(config.getProjectPath()); for (const string &globalIncludeDir : config.getGlobalIncludeDirs()) { string globalIncludeDirFull = projectPathUtf8; globalIncludeDirFull += "/"; globalIncludeDirFull += globalIncludeDir; if(globalIncludeDirCallback) globalIncludeDirCallback(globalIncludeDirFull); } } return Result::Ok(true); } }