#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(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) { 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) { 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 pkg-config package version, 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) { FileString command = pkgConfigPath + TINYDIR_STRING(" --modversion '"); command += toFileString(name); command += TINYDIR_STRING("'"); Result execResult = exec(command.c_str()); 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(""); 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); } }