#include "../include/CmakeModule.hpp" #include "../include/Exec.hpp" #include "../include/GlobalLib.hpp" #include "../include/PkgConfig.hpp" #include "../backend/BackendUtils.hpp" #include #if OS_FAMILY == OS_FAMILY_POSIX #define nprintf printf #else #define nprintf(fmt, ...) wprintf(TINYDIR_STRING(fmt), __VA_ARGS__) #endif using namespace std; using namespace backend; namespace sibs { static FileString cmakePath = TINYDIR_STRING("cmake"); static FileString getIncludeOptionFlag(Compiler compiler, const FileString &filepath) { FileString result; switch (compiler) { case Compiler::MINGW_W64: case Compiler::GCC: { result = FileString("'-I"); result += filepath; result += FileString("'"); break; } case Compiler::MSVC: { result = FileString("/I \""); result += filepath; result += FileString("\""); break; } default: assert(false); break; } return result; } void CmakeModule::setCmakePath(const FileString &path) { cmakePath = path; } Result CmakeModule::compile(const SibsConfig &config, const FileString &buildPath, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback) { // TODO: Make packaging work with cmake projects if(config.packaging) return Result::Err("Project " + config.getPackageName() + " is a cmake project, such projects are currently not supported when building a package"); Result globalLibDirResult = getHomeDir(); if (!globalLibDirResult) return Result::Err(globalLibDirResult); FileString globalLibDir = globalLibDirResult.unwrap(); globalLibDir += TINYDIR_STRING("/.cache/sibs/lib/"); globalLibDir += toFileString(asString(config.platform)); 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 vector linkerFlags; auto parentProjStaticLinkerFlagCallbackFunc = staticLinkerFlagCallbackFunc; if (!staticLinkerFlagCallbackFunc || (config.getPackageType() == PackageType::DYNAMIC || config.getPackageType() == PackageType::LIBRARY)) { staticLinkerFlagCallbackFunc = [&linkerFlags](const string &linkerFlag) { linkerFlags.push_back(linkerFlag); }; } auto parentProjDynamicLinkerFlagCallbackFunc = dynamicLinkerFlagCallbackFunc; // TODO: If project contains no source files, then we shouldn't override this function... why? if(!dynamicLinkerFlagCallbackFunc || config.getPackageType() != PackageType::STATIC) { dynamicLinkerFlagCallbackFunc = [&linkerFlags, &parentProjDynamicLinkerFlagCallbackFunc](const string &linkerFlag) { if(parentProjDynamicLinkerFlagCallbackFunc) parentProjDynamicLinkerFlagCallbackFunc(linkerFlag); linkerFlags.push_back(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 (PackageListDependency *dependency : config.getPackageListDependencies()) { globalLibDependencies.push_back(dependency); } #endif Result globalLibResult = GlobalLib::getLibs(globalLibDependencies, config, globalLibDir, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); if(!globalLibResult) return globalLibResult; for(vector::reverse_iterator it = linkerFlags.rbegin(), end = linkerFlags.rend(); it != end; ++it) { allLinkerFlags += " "; allLinkerFlags += *it; } Result createBuildDirResult = createDirectoryRecursive(buildPath.c_str()); if (createBuildDirResult.isErr()) return createBuildDirResult; #if 0 #if OS_FAMILY == OS_FAMILY_POSIX setenv("CFLAGS", "-fPIC", 1); setenv("CXXFLAGS", "-fPIC", 1); #else _putenv("CFLAGS=-fPIC"); _putenv("CXXFLAGS=-fPIC"); #endif #endif FileString cmd = cmakePath; cmd += TINYDIR_STRING(" "); FileString cflags = TINYDIR_STRING("-fPIC"); FileString cxxflags; if(config.getCompiler() == Compiler::GCC && config.getSanitize()) { cflags += TINYDIR_STRING(" -fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined -fsanitize=leak -lasan -lubsan -llsan"); } #if OS_FAMILY == OS_FAMILY_POSIX cflags += TINYDIR_STRING(" -I/usr/local/include"); #endif for(const auto &includeDir : config.getIncludeDirs()) { FileString includeDirRelative = FileString("../../../"); includeDirRelative += toFileString(includeDir); cflags += TINYDIR_STRING(" "); cflags += getIncludeOptionFlag(config.getCompiler(), includeDirRelative); } cxxflags = cflags; cmd += TINYDIR_STRING(" \"-DCMAKE_C_FLAGS=") + cflags + TINYDIR_STRING("\""); cmd += TINYDIR_STRING(" \"-DCMAKE_CXX_FLAGS=") + cxxflags + TINYDIR_STRING("\" "); 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(" -DCMAKE_SKIP_RPATH=\"1\""); cmd += TINYDIR_STRING(" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON"); 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) { const _tinydir_char_t *dynamicLibExtension = CONFIG_DYNAMIC_LIB_FILE_EXTENSION; if(config.getCompiler() == Compiler::MINGW_W64) dynamicLibExtension = TINYDIR_STRING("dll"); string buildPathUtf8 = toUtf8(buildPath); nprintf("Searching for libraries generate by cmake in build path: %s\n", buildPathUtf8.c_str()); walkDirFilesRecursiveSortTimestamp(buildPath.c_str(), [&config, &parentProjStaticLinkerFlagCallbackFunc, &parentProjDynamicLinkerFlagCallbackFunc, dynamicLibExtension](const FileWithTimestamp &filedata) { bool isStatic = config.getPackageType() == PackageType::STATIC; if((!isStatic && _tinydir_strcmp(filedata.extension.c_str(), dynamicLibExtension) == 0) || (isStatic && _tinydir_strcmp(filedata.extension.c_str(), CONFIG_STATIC_LIB_FILE_EXTENSION) == 0)) { string libFileUtf8 = toUtf8(filedata.filepath); nprintf("Library generated by cmake: %s\n", libFileUtf8.c_str()); switch(config.getPackageType()) { case PackageType::STATIC: { string libFileCmd = "\""; libFileCmd += libFileUtf8; libFileCmd += "\""; if(parentProjStaticLinkerFlagCallbackFunc) parentProjStaticLinkerFlagCallbackFunc(libFileCmd); break; } case PackageType::DYNAMIC: case PackageType::LIBRARY: { string libFileCmd = "\""; libFileCmd += libFileUtf8; libFileCmd += "\""; if(parentProjDynamicLinkerFlagCallbackFunc) parentProjDynamicLinkerFlagCallbackFunc(libFileCmd); break; } } } return true; }); // 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); } }