#include "../include/GlobalLib.hpp" #include "../include/FileUtil.hpp" #include "../backend/BackendUtils.hpp" #include "../backend/ninja/Ninja.hpp" #include "../include/Conf.hpp" #include "../include/curl.hpp" #include "../include/Archive.hpp" #include "../include/CmakeModule.hpp" #include "../include/Dependency.hpp" #include "../include/VersionParser.hpp" using namespace std; namespace sibs { Result GlobalLib::validatePackageExists(const FileString &globalLibRootDir, const std::string &name) { Path packageDir = Path(globalLibRootDir).join(toFileString(name)); FileType packageDirFileType = getFileType(packageDir.data.c_str()); switch(packageDirFileType) { case FileType::FILE_NOT_FOUND: { string errMsg = "Global lib dependency not found: "; errMsg += toUtf8(packageDir.data); return Result::Err(errMsg, DependencyError::DEPENDENCY_NOT_FOUND); } case FileType::REGULAR: { string errMsg = "Corrupt library directory. "; errMsg += toUtf8(packageDir.data); errMsg += " is a file, expected it to be a directory"; return Result::Err(errMsg); } case FileType::DIRECTORY: { break; } default: { return Result::Err("Unexpected error!"); } } // We also need to verify that the archive was removed after extracting it, otherwise the extracted // package could be corrupted. The archive is only removed after extracting is done. Result libPathResult = getHomeDir(); if (!libPathResult) return Result::Err(libPathResult); Path libArchivedFilePath = Path(libPathResult.unwrap()).join(".cache/sibs/archive").join(toFileString(name)); FileType archive_path_file_type = getFileType(libArchivedFilePath.data.c_str()); if(archive_path_file_type == FileType::FILE_NOT_FOUND) return Result::Ok(true); if(archive_path_file_type != FileType::DIRECTORY) return Result::Err("A previous download of package is corrupt, attempting to redownload..."); bool isEmpty = true; walkDir(libArchivedFilePath.data.c_str(), [&isEmpty](tinydir_file *file) { isEmpty = false; return false; }); if(!isEmpty) return Result::Err("A previous download of package is corrupt, attempting to redownload..."); return Result::Ok(true); } Result GlobalLib::getLibs(const std::vector &libs, const SibsConfig &parentConfig, const FileString &globalLibRootDir, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback) { for(const PackageListDependency &globalLibDependency : libs) { if(!parentConfig.packaging) printf("Dependency %s in version range %s is missing from pkg-config, trying global lib\n", globalLibDependency.name.c_str(), globalLibDependency.version.toString().c_str()); Result globalLibLinkerFlagsResult = GlobalLib::getLibsLinkerFlags(parentConfig, globalLibRootDir, globalLibDependency.name, globalLibDependency.version, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); if(!globalLibLinkerFlagsResult) { if(globalLibLinkerFlagsResult.getErrorCode() == GlobalLib::DependencyError::DEPENDENCY_NOT_FOUND || globalLibLinkerFlagsResult.getErrorCode() == GlobalLib::DependencyError::DEPENDENCY_VERSION_NO_MATCH) { printf("Dependency not found in global lib, trying to download from package repository\n"); // TODO: Download several dependencies at the same time by adding them to a list // and then iterate them and download them all using several threads. // All dependecies should be downloaded at the same time, this includes dependencies of dependencies. // If a dependency is missing, fail build BEFORE downloading dependencies and before compiling anything. // You do not want to possibly wait several minutes only for build to fail when there is no compilation error. // TODO: If return error is invalid url, then the message should be converted to // invalid package name/version. A check should be done if it is the name or version // that is invalid. Result downloadDependencyResult = GlobalLib::downloadDependency(globalLibDependency, parentConfig.platform); if(!downloadDependencyResult) return downloadDependencyResult; globalLibLinkerFlagsResult = GlobalLib::getLibsLinkerFlags(parentConfig, globalLibRootDir, globalLibDependency.name, globalLibDependency.version, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); if(!globalLibLinkerFlagsResult) return globalLibLinkerFlagsResult; } else { return globalLibLinkerFlagsResult; } } } return Result::Ok(true); } Result GlobalLib::getLibsLinkerFlags(const SibsConfig &parentConfig, const FileString &globalLibRootDir, const std::string &name, const PackageVersionRange &versionRange, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback) { Result packageExistsResult = validatePackageExists(globalLibRootDir, name); if (packageExistsResult.isErr()) return packageExistsResult; Path packageDir = Path(globalLibRootDir).join(toFileString(name)); FileString foundVersion; walkDir(packageDir.data.c_str(), [&foundVersion, &versionRange](tinydir_file *file) { if(file->is_dir) { string versionUtf8 = toUtf8(file->name); Result versionResult = parsePackageVersion({ versionUtf8.data(), versionUtf8.size() }, nullptr); if(versionResult && versionRange.isInRange(versionResult.unwrap())) { foundVersion = file->name; return false; } } return true; }); if(foundVersion.empty()) return Result::Err("Global lib dependency found, but version isn't in range of version", DependencyError::DEPENDENCY_VERSION_NO_MATCH); packageDir.join(foundVersion); return GlobalLib::getLibsLinkerFlagsCommon(parentConfig, packageDir.data, name, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); } Result GlobalLib::getLibsLinkerFlagsCommon(const SibsConfig &parentConfig, const FileString &packageDir, const string &dependencyName, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback) { FileString projectConfFilePath = packageDir; projectConfFilePath += TINYDIR_STRING("/project.conf"); FileType projectConfFileType = getFileType(projectConfFilePath.c_str()); if(projectConfFileType != FileType::REGULAR) { string errMsg = "Dependency not found: "; errMsg += toUtf8(packageDir); return Result::Err(errMsg, DependencyError::DEPENDENCY_NOT_FOUND); } SibsConfig sibsConfig(parentConfig.getCompiler(), packageDir, parentConfig.getOptimizationLevel(), false); sibsConfig.platform = parentConfig.platform; sibsConfig.packaging = parentConfig.packaging; sibsConfig.bundling = parentConfig.bundling; Result result = Config::readFromFile(projectConfFilePath.c_str(), sibsConfig); if (result.isErr()) return result; if(sibsConfig.getPackageType() == PackageType::EXECUTABLE) { string errMsg = "The dependency "; errMsg += dependencyName; errMsg += " is an executable. Only libraries can be dependencies"; return Result::Err(errMsg); } FileString buildPath = packageDir + TINYDIR_STRING("/sibs-build/") + toFileString(asString(sibsConfig.platform)) + TINYDIR_STRING("/"); switch (sibsConfig.getOptimizationLevel()) { case OPT_LEV_DEBUG: { buildPath += TINYDIR_STRING("debug"); // TODO: Check if this dependency is static or dynamic and decide which lib path to use from that for(const string &staticLib : sibsConfig.getDebugStaticLibs()) { string staticLibCmd = "\""; staticLibCmd += staticLib; staticLibCmd += "\""; staticLinkerFlagCallbackFunc(staticLibCmd); } break; } case OPT_LEV_RELEASE: { buildPath += TINYDIR_STRING("release"); // TODO: Check if this dependency is static or dynamic and decide which lib path to use from that for (const string &staticLib : sibsConfig.getReleaseStaticLibs()) { string staticLibCmd = "\""; staticLibCmd += staticLib; staticLibCmd += "\""; staticLinkerFlagCallbackFunc(staticLibCmd); } break; } } for(const std::string &lib : sibsConfig.getLibs()) { string staticLibCmd = "\""; staticLibCmd += lib; staticLibCmd += "\""; staticLinkerFlagCallbackFunc(staticLibCmd); } if(sibsConfig.shouldUseCmake()) { CmakeModule cmakeModule; return cmakeModule.compile(sibsConfig, buildPath, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); } else { backend::Ninja ninja; backend::BackendUtils::collectSourceFiles(packageDir.c_str(), &ninja, sibsConfig); return ninja.build(sibsConfig, buildPath.c_str(), staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); } } Result GlobalLib::downloadDependency(const PackageListDependency &dependency, Platform platform) { Result packageResult = Package::getPackage(dependency.name.c_str(), dependency.version, platform); if(!packageResult) return Result::Err(packageResult); const PackageMetadata &package = packageResult.unwrap(); Result libPathResult = getHomeDir(); if (!libPathResult) return Result::Err(libPathResult); Path libPath = Path(libPathResult.unwrap()) .join(TINYDIR_STRING(".cache/sibs/lib")) .join(toFileString(asString(platform))) .join(toFileString(dependency.name)) .join(toFileString(package.version.toString())); Path libArchivedFilePath = Path(libPathResult.unwrap()).join(TINYDIR_STRING("/.cache/sibs/archive/")).join(toFileString(dependency.name)); Result createArchiveDirResult = createDirectoryRecursive(libArchivedFilePath.data.c_str()); if(!createArchiveDirResult) return createArchiveDirResult; Path libArchivedDir = Path(libArchivedFilePath).join(toFileString(package.version.toString())); Result downloadResult = curl::downloadFile(package.urls[0].c_str(), libArchivedFilePath.data.c_str()); if(!downloadResult) return downloadResult; // Create build path. This is done here because we dont want to create it if download fails Result createLibDirResult = createDirectoryRecursive(libPath.data.c_str()); if(!createLibDirResult) return createLibDirResult; Result archiveExtractResult = Archive::extract(libArchivedFilePath.data.c_str(), libPath.data.c_str()); // We have extracted the archive, we dont need to cache it. If remove fails, it doesn't really matter, user can remove it himself #if OS_FAMILY == OS_FAMILY_POSIX remove(libArchivedFilePath.data.c_str()); remove(libArchivedDir.data.c_str()); #else _wremove(libArchivedFilePath.data.c_str()); _wremove(libArchivedDir.data.c_str()); #endif return archiveExtractResult; } }