#include "../include/PkgConfig.hpp" #include "../include/Exec.hpp" #include "../include/Dependency.hpp" using namespace std; static sibs::FileString pkgConfigPath = TINYDIR_STRING("pkg-config"); // TODO: Do not use pkg-config program. The same functionality can easily be achieved // by reading files in /usr/share/pkgconfig // Or is there no downside to calling pkg-config program? namespace sibs { static string trimRight(const string &input) { for(int i = input.size() - 1; i >= 0; --i) { if(!isspace(input[i])) return input.substr(0, i + 1); } return ""; } void PkgConfig::setPkgConfigPath(const FileString &path) { pkgConfigPath = path; } Result PkgConfig::validatePkgConfigPackageVersionExists(PackageListDependency *dependency) { Result dependencyValidationResult = PkgConfig::validatePackageExists(dependency->name); if(dependencyValidationResult.isErr()) return Result::Err(dependencyValidationResult.getErrMsg()); Result dependencyVersionValidationResult = PkgConfig::validatePackageVersionAtLeast(dependency->name, dependency->version); if(dependencyVersionValidationResult.isErr()) return Result::Err(dependencyVersionValidationResult.getErrMsg()); return Result::Ok(true); } Result PkgConfig::validatePackageExists(const string &name) { FileString command = pkgConfigPath + TINYDIR_STRING(" --exists '"); command += toFileString(name); command += TINYDIR_STRING("'"); Result execResult = exec(command.c_str()); if(execResult.isErr()) return Result::Err(execResult.getErrMsg()); if(execResult.unwrap().exitCode == 1) { string errMsg = "pkg-config dependency not found: "; errMsg += name; return Result::Err(errMsg); } else if(execResult.unwrap().exitCode == 127) { return Result::Err("pkg-config is not installed"); } else if(execResult.unwrap().exitCode != 0) { string errMsg = "Failed to check if dependency exists, Unknown error, exit code: "; errMsg += to_string(execResult.unwrap().exitCode); return Result::Err(errMsg); } return Result::Ok(true); } Result PkgConfig::validatePackageVersionAtLeast(const string &name, const string &version) { // TODO: Instead of checking if package version is same or newer, check if package is same major version // and same or newer minor version // Use --modversion instead and check if the version returned is newer or equal to dependency version. // This way we can output installed version vs expected dependency version FileString command = pkgConfigPath + TINYDIR_STRING(" '--atleast-version="); command += toFileString(version); command += TINYDIR_STRING("' '"); command += toFileString(name); command += TINYDIR_STRING("'"); Result execResult = exec(command.c_str()); if(execResult.isErr()) return Result::Err(execResult.getErrMsg()); if(execResult.unwrap().exitCode == 1) { string errMsg = "Dependency "; errMsg += name; errMsg += " is installed but the version older than "; errMsg += version; return Result::Err(errMsg); } else if(execResult.unwrap().exitCode == 127) { return Result::Err("pkg-config is not installed"); } else if(execResult.unwrap().exitCode != 0) { string errMsg = "Failed to check dependency version, Unknown error, exit code: "; errMsg += to_string(execResult.unwrap().exitCode); return Result::Err(errMsg); } return Result::Ok(true); } Result PkgConfig::getDynamicLibsLinkerFlags(const vector &libs) { if(libs.empty()) return Result::Ok(""); string args; for(PackageListDependency *lib : libs) { args += " '"; args += lib->name; args += "'"; } FileString command = pkgConfigPath + TINYDIR_STRING(" --libs"); command += toFileString(args); Result execResult = exec(command.c_str()); if(execResult.isErr()) return Result::Err(execResult.getErrMsg()); if(execResult.unwrap().exitCode == 0) { execResult.unwrap().execStdout = trimRight(execResult.unwrap().execStdout); return Result::Ok(execResult.unwrap().execStdout); } else if(execResult.unwrap().exitCode == 1) { // TODO: This shouldn't happen because we check if each dependency is installed before this, // but maybe the package is uninstalled somewhere between here... // Would be better to recheck if each package is installed here again // to know which package was uninstalled return Result::Err("Packages not found"); } else if(execResult.unwrap().exitCode == 127) { return Result::Err("pkg-config is not installed"); } else { string errMsg = "Failed to get package dynamic lib linking flags, Unknown error, exit code: "; errMsg += to_string(execResult.unwrap().exitCode); return Result::Err(errMsg); } } Result PkgConfig::getDynamicLibsCflags(const vector &libs) { if(libs.empty()) return Result::Ok(""); string args; for(PackageListDependency *lib : libs) { args += " '"; args += lib->name; args += "'"; } FileString command = pkgConfigPath + TINYDIR_STRING(" --cflags"); command += toFileString(args); Result execResult = exec(command.c_str()); if(execResult.isErr()) return Result::Err(execResult.getErrMsg()); if(execResult.unwrap().exitCode == 0) { execResult.unwrap().execStdout = trimRight(execResult.unwrap().execStdout); return Result::Ok(execResult.unwrap().execStdout); } else if(execResult.unwrap().exitCode == 1) { // TODO: This shouldn't happen because we check if each dependency is installed before this, // but maybe the package is uninstalled somewhere between here... // Would be better to recheck if each package is installed here again // to know which package was uninstalled return Result::Err("Packages not found"); } else if(execResult.unwrap().exitCode == 127) { return Result::Err("pkg-config is not installed"); } else { string errMsg = "Failed to get package dynamic lib cflags, Unknown error, exit code: "; errMsg += to_string(execResult.unwrap().exitCode); return Result::Err(errMsg); } } Result PkgConfig::getDynamicLibsFlags(const vector &libs) { PkgConfigFlags flags; Result linkerFlagsResult = getDynamicLibsLinkerFlags(libs); if(!linkerFlagsResult) return Result::Err(linkerFlagsResult); Result cflagsResult = getDynamicLibsCflags(libs); if(!cflagsResult) return Result::Err(cflagsResult); flags.linkerFlags = move(linkerFlagsResult.unwrap()); flags.cflags = move(cflagsResult.unwrap()); return Result::Ok(flags); } }