#include "../include/PkgConfig.hpp" #include "../include/Exec.hpp" #include "../include/Dependency.hpp" #include "../include/VersionParser.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(const PackageListDependency &dependency) { Result dependencyValidationResult = PkgConfig::validatePackageExists(dependency.name); if(dependencyValidationResult.isErr()) return Result::Err(dependencyValidationResult.getErrMsg()); Result dependencyVersionResult = PkgConfig::getPackageVersion(dependency.name); if(!dependencyVersionResult) return Result::Err(dependencyVersionResult); if(!dependency.version.isInRange(dependencyVersionResult.unwrap())) return Result::Err("pkg-config package " + dependency.name + " exists but the version does not match our expected version range"); return Result::Ok(true); } Result PkgConfig::validatePackageExists(const string &name) { Result execResult = exec({ pkgConfigPath, TINYDIR_STRING("--exists"), TINYDIR_STRING("--"), toFileString(name) }); 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::getPackageVersion(const std::string &name) { Result execResult = exec({ pkgConfigPath, TINYDIR_STRING("--modversion"), TINYDIR_STRING("--"), toFileString(name) }); if(!execResult) return Result::Err(execResult.getErrMsg()); if(execResult.unwrap().exitCode == 1) { string errMsg = "Dependency "; errMsg += name; errMsg += " not found in pkg-config"; 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 get pkg-config package version, Unknown error, exit code: "; errMsg += to_string(execResult.unwrap().exitCode); return Result::Err(errMsg); } // Intentionally allow packages which have a version that contains more data after patch number, since some pkg-config packages are not in semver format return parsePackageVersion({ execResult.unwrap().execStdout.data(), execResult.unwrap().execStdout.size() }, nullptr); } Result PkgConfig::getDynamicLibsLinkerFlags(const vector &libs) { if(libs.empty()) return Result::Ok(""); std::vector args = { pkgConfigPath, TINYDIR_STRING("--libs"), TINYDIR_STRING("--") }; for(const PackageListDependency &lib : libs) { args.push_back(toFileString(lib.name)); } Result execResult = exec(args); 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(""); std::vector args = { pkgConfigPath, TINYDIR_STRING("--cflags"), TINYDIR_STRING("--") }; for(const PackageListDependency &lib : libs) { args.push_back(toFileString(lib.name)); } Result execResult = exec(args); 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); } }