#include "../include/GlobalLib.hpp" #include "../include/FileUtil.hpp" #include "../backend/ninja/Ninja.hpp" #include "../include/Conf.hpp" #include "../include/curl.hpp" #include "../include/Archive.hpp" using namespace std; namespace sibs { Result GlobalLib::validatePackageExists(const string &globalLibRootDir, const string &name) { string packageDir = globalLibRootDir + "/"; packageDir += name; FileType packageDirFileType = getFileType(packageDir.c_str()); switch(packageDirFileType) { case FileType::FILE_NOT_FOUND: { string errMsg = "Global lib dependency not found: "; errMsg += name; return Result::Err(errMsg, DependencyError::DEPENDENCY_NOT_FOUND); } case FileType::REGULAR: { string errMsg = "Corrupt library directory. "; errMsg += packageDir; errMsg += " is a file, expected it to be a directory"; return Result::Err(errMsg); } case FileType::DIRECTORY: { return Result::Ok(true); } default: { return Result::Err("Unexpected error!"); } } } const char *sourceFileExtensions[] = { "c", "cc", "cpp", "cxx" }; bool isSourceFile(tinydir_file *file) { if(!file->is_reg) return false; for(const char *sourceFileExtension : sourceFileExtensions) { if(_tinydir_strcmp(sourceFileExtension, file->extension) == 0) return true; } return false; } bool isPathSubPathOf(const char *path, const string &subPathOf) { return _tinydir_strncmp(path, subPathOf.c_str(), subPathOf.size()) == 0; } Result GlobalLib::getLibsLinkerFlags(const SibsConfig &parentConfig, const string &globalLibRootDir, const string &name, const string &version, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc) { Result packageExistsResult = validatePackageExists(globalLibRootDir, name); if(packageExistsResult.isErr()) return Result::Err(packageExistsResult); string packageDir = globalLibRootDir + "/"; packageDir += name; // TODO: Instead of checking if version is exact match, check if package has same major version // and same or newer minor version string foundVersion; walkDir(packageDir.c_str(), [&foundVersion, &version](tinydir_file *file) { if(file->is_dir) { //printf("version: %s\n", file->name); if(_tinydir_strcmp(version.c_str(), file->name) == 0) foundVersion = file->name; } }); if(foundVersion.empty()) return Result::Err("Global lib dependency found, but version doesn't match dependency version", DependencyError::DEPENDENCY_VERSION_NO_MATCH); packageDir += "/"; packageDir += version; string projectConfFilePath = packageDir; projectConfFilePath += "/project.conf"; FileType projectConfFileType = getFileType(projectConfFilePath.c_str()); switch(projectConfFileType) { case FileType::FILE_NOT_FOUND: { string errMsg = "Global lib dependency found: "; errMsg += packageDir; errMsg += ", but it's missing a project.conf file"; return Result::Err(errMsg); } case FileType::DIRECTORY: { string errMsg = "Global lib dependency found: "; errMsg += packageDir; errMsg += ", but it's corrupt (Found directory instead of file)"; return Result::Err(errMsg); } } SibsConfig sibsConfig(packageDir, parentConfig.getOptimizationLevel()); Result result = Config::readFromFile(projectConfFilePath.c_str(), sibsConfig); if(result.isErr()) return Result::Err(result.getErrMsg()); if(sibsConfig.getPackageName().empty()) return Result::Err("project.conf is missing required field package.name"); if(sibsConfig.getPackageType() == PackageType::EXECUTABLE) { string errMsg = "The dependency "; errMsg += name; errMsg += " is an executable. Only libraries can be dependencies"; return Result::Err(errMsg); } backend::Ninja ninja; FileWalkCallbackFunc collectSourceFiles = [&ninja, &sibsConfig, &collectSourceFiles](tinydir_file *file) { if(file->is_reg) { if (isSourceFile(file)) { ninja.addSourceFile(file->path + sibsConfig.getProjectPath().size() + 1); } else { //printf("Ignoring non-source file: %s\n", file->path + projectPath.size()); } } else { // TODO: If compiling without "test" option, do not add test source dir to ninja. Ninja logic will then not build tests... // OR I believe there is no reason to run tests in dependencies. The main projects tests should cover that? // But you might want to know exactly which dependency is causing issue and which part of it... if(!sibsConfig.getTestPath().empty() && isPathSubPathOf(file->path, sibsConfig.getTestPath())) ninja.addTestSourceDir(file->path); else walkDir(file->path, collectSourceFiles); } }; walkDir(packageDir.c_str(), collectSourceFiles); // TODO: Dont do this. Unit tests wont be built. Need to call the below ninja.createBuildFile if(ninja.getSourceFiles().empty()) { return Result::Ok("No source files in dependency (header only library?)"); } else { string buildPath = packageDir + "/sibs-build/"; switch(sibsConfig.getOptimizationLevel()) { case OPT_LEV_DEBUG: buildPath += "debug"; break; case OPT_LEV_RELEASE: buildPath += "release"; break; } string libPath = buildPath; libPath += "/lib"; libPath += name; if(sibsConfig.getPackageType() == PackageType::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 Result createBuildDirResult = createDirectoryRecursive(buildPath.c_str()); if(createBuildDirResult.isErr()) return Result::Err(createBuildDirResult); Result buildFileResult = ninja.build(sibsConfig, buildPath.c_str(), staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc); if (buildFileResult.isErr()) return Result::Err(buildFileResult.getErrMsg()); return Result::Ok(libPath); } } Result GlobalLib::downloadDependency(const Dependency &dependency) { string url = "https://github.com/DEC05EBA/"; url += dependency.name; url += "/archive/"; url += dependency.version; url += ".tar.gz"; string libPath = getHomeDir(); libPath += "/.sibs/lib/"; libPath += dependency.name; libPath += "/"; libPath += dependency.version; string libArchivedFilePath = getHomeDir(); libArchivedFilePath += "/.sibs/archive/"; libArchivedFilePath += dependency.name; Result createArchiveDirResult = createDirectoryRecursive(libArchivedFilePath.c_str()); if(createArchiveDirResult.isErr()) return createArchiveDirResult; libArchivedFilePath += "/"; libArchivedFilePath += dependency.version; Result downloadResult = curl::downloadFile(url.c_str(), libArchivedFilePath.c_str()); if(downloadResult.isErr()) return downloadResult; // Create build path. This is done here because we dont want to create it if download fails Result createLibDirResult = createDirectoryRecursive(libPath.c_str()); if(createLibDirResult.isErr()) return createLibDirResult; return Archive::extract(libArchivedFilePath.c_str(), libPath.c_str()); } }