PkgC: Correctly handle various pkg-config edge cases

Fixes many bugs in PkgC by incorporating the 0.0.7 changes from llpkgc.
These include:
  * Variable substitution fixes
  * Better whitespace handling
  * Correct handling of single and double quotes
  * Dedicated parsers for fragment and dependency lists

Also adds support for pcfiledir, which was blocked on incorrect
whitespace handling in the old parser.

Fixes: #27652, #27683
This commit is contained in:
Vito Gamberini
2026-06-24 12:06:21 -04:00
parent 391b44f37e
commit 38fa645e73
10 changed files with 1270 additions and 980 deletions

View File

@@ -451,27 +451,64 @@ struct ImportEnv
bool required;
bool quiet;
bool exact;
bool err;
CommonArguments::StrictnessType strictness;
cmExecutionStatus& status;
};
void warn_or_error(std::string const& err, ImportEnv& imEnv)
struct ImportState
{
cmExecutionStatus& status;
bool err;
};
void warn_or_error(std::string const& err, ImportEnv imEnv, ImportState& state)
{
if (imEnv.required) {
imEnv.status.SetError(err);
state.status.SetError(err);
cmSystemTools::SetFatalErrorOccurred();
} else if (!imEnv.quiet) {
imEnv.status.GetMakefile().IssueMessage(MessageType::WARNING, err);
state.status.GetMakefile().IssueMessage(MessageType::WARNING, err);
}
imEnv.err = true;
state.err = true;
}
cm::optional<cmPkgConfigResult> ReadPackage(std::string const& package,
ImportEnv& imEnv,
cmPkgConfigEnv& pcEnv)
cm::optional<cmPkgConfigDependencySpec> MakeVersionRequirement(
std::string const& version)
{
cm::optional<cmPkgConfigResult> result;
std::string requirement = cmTrimWhitespace(version);
std::string expression = "cmake_pkg_config_version";
expression += ' ';
if (requirement.empty()) {
expression += '=';
} else {
char const first = requirement.front();
if (first != '<' && first != '>' && first != '=' && first != '!') {
expression += "= ";
}
expression += requirement;
}
cmPkgConfigDependencyParser parser;
auto err = parser.Parse(expression.data(), expression.size());
if (err != PDEP_OK || parser.Finish() != PDEP_OK) {
return {};
}
auto const& dependencies = parser.Dependencies();
if (dependencies.size() != 1 ||
dependencies.front().Name != "cmake_pkg_config_version") {
return {};
}
return dependencies.front();
}
cm::optional<cmPkgConfigResolver> ReadPackage(std::string const& package,
ImportEnv imEnv,
cmPkgConfigEnv const& pcEnv,
ImportState& state)
{
cm::optional<cmPkgConfigResolver> result;
cm::filesystem::path path{ package };
if (path.extension() == ".pc") {
@@ -510,7 +547,7 @@ cm::optional<cmPkgConfigResult> ReadPackage(std::string const& package,
if (!ifs) {
warn_or_error(cmStrCat("Could not open file '", path.string(), '\''),
imEnv);
imEnv, state);
return result;
}
@@ -520,7 +557,7 @@ cm::optional<cmPkgConfigResult> ReadPackage(std::string const& package,
// Shouldn't have hit eof on previous read, should hit eof now
if (ifs.fail() || ifs.eof() || ifs.get() != EOF) {
warn_or_error(cmStrCat("Error while reading file '", path.string(), '\''),
imEnv);
imEnv, state);
return result;
}
@@ -530,38 +567,45 @@ cm::optional<cmPkgConfigResult> ReadPackage(std::string const& package,
auto err = parser.Finish(buf.get(), len);
if (imEnv.strictness != StrictnessType::STRICTNESS_BEST_EFFORT &&
err != PCE_OK) {
err != PFCE_OK) {
warn_or_error(cmStrCat("Parsing failed for file '", path.string(), '\''),
imEnv);
imEnv, state);
return result;
}
std::string const pcFileDir = cmSystemTools::GetFilenamePath(
cmSystemTools::CollapseFullPath(path.string()));
if (imEnv.strictness == StrictnessType::STRICTNESS_STRICT) {
result = cmPkgConfigResolver::ResolveStrict(parser.Data(), pcEnv);
result =
cmPkgConfigResolver::ResolveStrict(std::move(parser), pcEnv, pcFileDir);
} else if (imEnv.strictness == StrictnessType::STRICTNESS_PERMISSIVE) {
result = cmPkgConfigResolver::ResolvePermissive(parser.Data(), pcEnv);
result = cmPkgConfigResolver::ResolvePermissive(std::move(parser), pcEnv,
pcFileDir);
} else {
result = cmPkgConfigResolver::ResolveBestEffort(parser.Data(), pcEnv);
result = cmPkgConfigResolver::ResolveBestEffort(std::move(parser), pcEnv,
pcFileDir);
}
if (!result) {
warn_or_error(
cmStrCat("Resolution failed for file '", path.string(), '\''), imEnv);
cmStrCat("Resolution failed for file '", path.string(), '\''), imEnv,
state);
}
return result;
}
cm::optional<cmPkgConfigResult> ImportPackage(
cm::optional<cmPkgConfigResolver> ImportPackage(
std::string const& package, cm::optional<std::string> version,
ImportEnv& imEnv, cmPkgConfigEnv& pcEnv)
ImportEnv imEnv, cmPkgConfigEnv const& pcEnv, ImportState& state)
{
auto result = ReadPackage(package, imEnv, pcEnv);
auto result = ReadPackage(package, imEnv, pcEnv, state);
if (!result) {
if (!imEnv.err) {
if (!state.err) {
warn_or_error(cmStrCat("Could not find pkg-config: '", package, '\''),
imEnv);
imEnv, state);
}
return result;
}
@@ -570,53 +614,64 @@ cm::optional<cmPkgConfigResult> ImportPackage(
std::string ver;
if (version) {
ver = cmPkgConfigResolver::ParseVersion(*version).Version;
auto req = MakeVersionRequirement(*version);
if (!req) {
warn_or_error(
cmStrCat("Invalid version requirement '", *version, '\''), imEnv,
state);
return {};
}
ver = req->Version;
}
if (ver != result->Version()) {
warn_or_error(
cmStrCat("Package '", package, "' version '", result->Version(),
"' does not meet exact version requirement '", ver, '\''),
imEnv);
imEnv, state);
return {};
}
} else if (version) {
auto rv = cmPkgConfigResolver::ParseVersion(*version);
if (!cmPkgConfigResolver::CheckVersion(rv, result->Version())) {
auto req = MakeVersionRequirement(*version);
if (!req) {
warn_or_error(cmStrCat("Invalid version requirement '", *version, '\''),
imEnv, state);
return {};
}
if (!cmPkgConfigResolver::CheckVersion(*req, result->Version())) {
warn_or_error(
cmStrCat("Package '", package, "' version '", result->Version(),
"' does not meet version requirement '", *version, '\''),
imEnv);
imEnv, state);
return {};
}
}
result->env = &pcEnv;
return result;
}
struct pkgStackEntry
{
cmPkgConfigVersionReq ver;
cmPkgConfigDependencySpec Dependency;
std::string parent;
};
cm::optional<cmPkgConfigResult> ImportPackage(
cm::optional<cmPkgConfigResolver> ImportPackage(
std::string const& package, std::vector<pkgStackEntry> const& reqs,
ImportEnv& imEnv, cmPkgConfigEnv& pcEnv)
ImportEnv imEnv, cmPkgConfigEnv const& pcEnv, ImportState& state)
{
auto result = ReadPackage(package, imEnv, pcEnv);
auto result = ReadPackage(package, imEnv, pcEnv, state);
if (!result) {
if (!imEnv.err) {
if (!state.err) {
std::string req_str = cmStrCat('\'', reqs.begin()->parent, '\'');
for (auto it = reqs.begin() + 1; it != reqs.end(); ++it) {
req_str = cmStrCat(req_str, ", '", it->parent, '\'');
}
warn_or_error(cmStrCat("Could not find pkg-config: '", package,
"' required by: ", req_str),
imEnv);
imEnv, state);
}
return result;
}
@@ -624,16 +679,17 @@ cm::optional<cmPkgConfigResult> ImportPackage(
auto ver = result->Version();
for (auto const& req : reqs) {
if (!cmPkgConfigResolver::CheckVersion(req.ver, ver)) {
warn_or_error(cmStrCat("Package '", package, "' version '", ver,
"' does not meet version requirement '",
req.ver.string(), "' of '", req.parent, '\''),
imEnv);
if (!cmPkgConfigResolver::CheckVersion(req.Dependency, ver)) {
warn_or_error(
cmStrCat("Package '", package, "' version '", ver,
"' does not meet version requirement '",
cmPkgConfigResolver::VersionReqString(req.Dependency),
"' of '", req.parent, '\''),
imEnv, state);
return {};
}
}
result->env = &pcEnv;
return result;
}
@@ -679,8 +735,7 @@ cm::optional<std::pair<cmPkgConfigEnv, ImportEnv>> HandleCommon(
}
return std::pair<cmPkgConfigEnv, ImportEnv>{
pcEnv,
{ args.Required, args.Quiet, args.Exact, false, args.Strictness, status }
pcEnv, { args.Required, args.Quiet, args.Exact, args.Strictness }
};
}
@@ -714,10 +769,11 @@ bool HandleExtractCommand(std::vector<std::string> const& args,
return !parsedArgs.Required;
}
auto& pcEnv = maybeEnv->first;
auto& imEnv = maybeEnv->second;
auto const& imEnv = maybeEnv->second;
ImportState state{ status, false };
auto maybePackage =
ImportPackage(*parsedArgs.Package, parsedArgs.Version, imEnv, pcEnv);
auto maybePackage = ImportPackage(*parsedArgs.Package, parsedArgs.Version,
imEnv, pcEnv, state);
if (!maybePackage) {
return !parsedArgs.Required;
}
@@ -745,7 +801,7 @@ bool HandleExtractCommand(std::vector<std::string> const& args,
mf.AddDefinition("CMAKE_PKG_CONFIG_VERSION", package.Version());
auto make_list = [&](char const* def,
std::vector<cmPkgConfigDependency> const& deps) {
std::vector<cmPkgConfigDependencySpec> const& deps) {
std::vector<cm::string_view> vec;
vec.reserve(deps.size());
@@ -800,8 +856,8 @@ using pkgStack = std::unordered_map<std::string, std::vector<pkgStackEntry>>;
using pkgProviders = std::unordered_map<std::string, std::string>;
cmTarget* CreateCMakeTarget(std::string const& name, std::string const& prefix,
cmPkgConfigResult& pkg, pkgProviders& providers,
cmMakefile& mf)
cmPkgConfigResolver const& pkg,
pkgProviders const& providers, cmMakefile& mf)
{
auto* tgt = mf.AddForeignTarget("pkgcfg", cmStrCat(prefix, name));
@@ -824,7 +880,7 @@ cmTarget* CreateCMakeTarget(std::string const& name, std::string const& prefix,
tgt->AppendProperty("INTERFACE_COMPILE_OPTIONS",
cmList::to_string(cflags.CompileOptions));
for (auto& dep : pkg.Requires()) {
for (auto const& dep : pkg.Requires()) {
auto it = providers.find(dep.Name);
if (it != providers.end()) {
tgt->AppendProperty("INTERFACE_LINK_LIBRARIES", it->second);
@@ -838,27 +894,28 @@ cmTarget* CreateCMakeTarget(std::string const& name, std::string const& prefix,
}
bool CheckPackageDependencies(
std::string const& name, std::string const& prefix, cmPkgConfigResult& pkg,
pkgStack& inStack,
std::unordered_map<std::string, cmPkgConfigResult>& outStack,
pkgProviders& providers, ImportEnv& imEnv)
std::string const& name, std::string const& prefix,
cmPkgConfigResolver const& pkg, pkgStack& inStack,
std::unordered_map<std::string, cmPkgConfigResolver> const& outStack,
pkgProviders const& providers, ImportEnv imEnv, ImportState& state)
{
for (auto& dep : pkg.Requires()) {
for (auto const& dep : pkg.Requires()) {
auto prov_it = providers.find(dep.Name);
if (prov_it != providers.end()) {
continue;
}
auto* tgt = imEnv.status.GetMakefile().FindTargetToUse(
auto* tgt = state.status.GetMakefile().FindTargetToUse(
cmStrCat("@foreign_pkgcfg::", prefix, dep.Name),
cm::TargetDomain::FOREIGN);
if (tgt) {
auto ver = tgt->GetProperty("VERSION");
if (!cmPkgConfigResolver::CheckVersion(dep.VerReq, *ver)) {
if (!cmPkgConfigResolver::CheckVersion(dep, *ver)) {
warn_or_error(cmStrCat("Package '", dep.Name, "' version '", *ver,
"' does not meet version requirement '",
dep.VerReq.string(), "' of '", name, '\''),
imEnv);
cmPkgConfigResolver::VersionReqString(dep),
"' of '", name, '\''),
imEnv, state);
return false;
}
continue;
@@ -867,18 +924,18 @@ bool CheckPackageDependencies(
auto it = outStack.find(dep.Name);
if (it != outStack.end()) {
auto ver = it->second.Version();
if (!cmPkgConfigResolver::CheckVersion(dep.VerReq, ver)) {
if (!cmPkgConfigResolver::CheckVersion(dep, ver)) {
warn_or_error(cmStrCat("Package '", dep.Name, "' version '", ver,
"' does not meet version requirement '",
dep.VerReq.string(), "' of '", name, '\''),
imEnv);
cmPkgConfigResolver::VersionReqString(dep),
"' of '", name, '\''),
imEnv, state);
return false;
}
continue;
}
inStack[dep.Name].emplace_back(
pkgStackEntry{ std::move(dep.VerReq), name });
inStack[dep.Name].emplace_back(pkgStackEntry{ dep, name });
}
return true;
@@ -910,11 +967,13 @@ std::pair<bool, bool> PopulatePCTarget(PopulateArguments& args,
return { !args.Required, false };
}
auto& pcEnv = maybeEnv->first;
auto& imEnv = maybeEnv->second;
pcEnv.AllowSysCflags = true;
pcEnv.AllowSysLibs = true;
auto const& imEnv = maybeEnv->second;
ImportState state{ status, false };
pkgProviders providers;
if (args.Providers) {
for (auto const& provider_str : *args.Providers) {
@@ -923,7 +982,7 @@ std::pair<bool, bool> PopulatePCTarget(PopulateArguments& args,
providers.emplace(provider_str.substr(0, assignment),
provider_str.substr(assignment + 1));
} else {
imEnv.status.SetError(cmStrCat(
state.status.SetError(cmStrCat(
"No '=' found in BIND_PC_REQUIRES argument '", provider_str, '\''));
cmSystemTools::SetFatalErrorOccurred();
return { false, false };
@@ -932,33 +991,35 @@ std::pair<bool, bool> PopulatePCTarget(PopulateArguments& args,
}
pkgStack inStack;
std::unordered_map<std::string, cmPkgConfigResult> outStack;
std::unordered_map<std::string, cmPkgConfigResolver> outStack;
auto maybePackage = ImportPackage(*args.Package, args.Version, imEnv, pcEnv);
auto maybePackage =
ImportPackage(*args.Package, args.Version, imEnv, pcEnv, state);
if (!maybePackage) {
return { !args.Required, false };
}
imEnv.exact = false;
ImportEnv depEnv = imEnv;
depEnv.exact = false;
if (!CheckPackageDependencies(*args.Package, prefix, *maybePackage, inStack,
outStack, providers, imEnv)) {
outStack, providers, depEnv, state)) {
return { !args.Required, false };
}
outStack[*args.Package] = std::move(*maybePackage);
outStack.emplace(*args.Package, std::move(*maybePackage));
while (!inStack.empty()) {
auto name = inStack.begin()->first;
auto reqs = inStack.begin()->second;
maybePackage = ImportPackage(name, reqs, imEnv, pcEnv);
maybePackage = ImportPackage(name, reqs, depEnv, pcEnv, state);
if (!maybePackage) {
return { !args.Required, false };
}
if (!CheckPackageDependencies(name, prefix, *maybePackage, inStack,
outStack, providers, imEnv)) {
outStack, providers, depEnv, state)) {
return { !args.Required, false };
}
inStack.erase(name);
outStack[std::move(name)] = std::move(*maybePackage);
outStack.emplace(std::move(name), std::move(*maybePackage));
}
for (auto& entry : outStack) {

View File

@@ -3,98 +3,248 @@
#include "cmPkgConfigParser.h"
#include <algorithm>
#include <cctype>
#include <cstddef>
#include <set>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include <cm/string_view>
#include <cmllpkgc/llpkgc.h>
cmPkgConfigValueElement::cmPkgConfigValueElement(bool isVariable,
cm::string_view data)
: IsVariable{ isVariable }
, Data{ data }
namespace {
cmPkgConfigDependencyOperation LookupDependencyOperation(cm::string_view op)
{
if (op == "<") {
return cmPkgConfigDependencyOperation::LessThan;
}
if (op == "<=") {
return cmPkgConfigDependencyOperation::LessThanEqual;
}
if (op == "=") {
return cmPkgConfigDependencyOperation::Equal;
}
if (op == "!=") {
return cmPkgConfigDependencyOperation::NotEqual;
}
if (op == ">") {
return cmPkgConfigDependencyOperation::GreaterThan;
}
if (op == ">=") {
return cmPkgConfigDependencyOperation::GreaterThanEqual;
}
return cmPkgConfigDependencyOperation::None;
}
} // namespace
cmPkgConfigValueItem::cmPkgConfigValueItem(std::string text,
bool isSubstitution)
: Text{ std::move(text) }
, IsSubstitution{ isSubstitution }
{
}
cmPkgConfigEntry::cmPkgConfigEntry(bool isVariable, cm::string_view key)
: IsVariable{ isVariable }
, Key{ key }
cmPkgConfigEntry::cmPkgConfigEntry(std::vector<cmPkgConfigValueItem> value,
std::string ident,
cmPkgConfigEntryKind kind)
: Value{ std::move(value) }
, Ident{ std::move(ident) }
, Kind{ kind }
{
}
cmPkgConfigParser::cmPkgConfigParser()
cmPkgConfigFileParser::cmPkgConfigFileParser()
{
llpkgc_init(static_cast<llpkgc_t*>(this), &Settings_);
llpkgc_file_init(static_cast<llpkgc_file_t*>(this), &Settings_);
}
llpkgc_errno_t cmPkgConfigParser::Parse(char* buf, std::size_t len)
cmPkgConfigFileParser::cmPkgConfigFileParser(
cmPkgConfigFileParser const& other)
: cmPkgConfigFileParser()
{
return llpkgc_execute(static_cast<llpkgc_t*>(this), buf, len);
*this = other;
}
llpkgc_errno_t cmPkgConfigParser::Finish()
cmPkgConfigFileParser::cmPkgConfigFileParser(
cmPkgConfigFileParser&& other) noexcept
: cmPkgConfigFileParser()
{
return llpkgc_finish(static_cast<llpkgc_t*>(this));
*this = std::move(other);
}
llpkgc_errno_t cmPkgConfigParser::Finish(char* buf, std::size_t len)
cmPkgConfigFileParser& cmPkgConfigFileParser::operator=(
cmPkgConfigFileParser const& other)
{
Parse(buf, len);
return llpkgc_finish(static_cast<llpkgc_t*>(this));
if (this != &other) {
this->ResetCallbacks();
llpkgc_file_init(static_cast<llpkgc_file_t*>(this), &Settings_);
Ptr_ = nullptr;
Len_ = 0;
Ident_ = other.Ident_;
Data_ = other.Data_;
}
return *this;
}
int cmPkgConfigParser::OnSpanNext(char const*, std::size_t len)
cmPkgConfigFileParser& cmPkgConfigFileParser::operator=(
cmPkgConfigFileParser&& other) noexcept
{
if (this != &other) {
this->ResetCallbacks();
llpkgc_file_init(static_cast<llpkgc_file_t*>(this), &Settings_);
Ptr_ = nullptr;
Len_ = 0;
Ident_ = std::move(other.Ident_);
Data_ = std::move(other.Data_);
}
return *this;
}
void cmPkgConfigFileParser::ResetCallbacks()
{
Settings_.on_ident = OnIdentTr;
Settings_.on_value_literal = OnValueLiteralTr;
Settings_.on_value_substitution = OnValueSubstitutionTr;
Settings_.on_line_begin = nullptr;
Settings_.on_property_complete = OnPropertyCompleteTr;
Settings_.on_variable_complete = OnVariableCompleteTr;
Settings_.on_barewords_complete = OnBarewordsCompleteTr;
Settings_.on_value_literal_complete = OnValueLiteralCompleteTr;
Settings_.on_value_substitution_complete = OnValueSubstitutionCompleteTr;
Settings_.on_value_complete = OnValueCompleteTr;
Settings_.on_pkgc_complete = nullptr;
}
llpkgc_file_errno_t cmPkgConfigFileParser::Parse(char const* input,
std::size_t len)
{
return llpkgc_file_execute(static_cast<llpkgc_file_t*>(this), input, len);
}
llpkgc_file_errno_t cmPkgConfigFileParser::Finish()
{
return llpkgc_file_finish(static_cast<llpkgc_file_t*>(this));
}
void cmPkgConfigFileParser::Resume()
{
llpkgc_file_resume(static_cast<llpkgc_file_t*>(this));
}
llpkgc_file_errno_t cmPkgConfigFileParser::GetErrno() const
{
return llpkgc_file_get_errno(static_cast<llpkgc_file_t const*>(this));
}
char const* cmPkgConfigFileParser::ErrorReason() const
{
return llpkgc_file_get_error_reason(static_cast<llpkgc_file_t const*>(this));
}
std::ptrdiff_t cmPkgConfigFileParser::ErrorOffset(char const* base) const
{
char const* pos =
llpkgc_file_get_error_pos(static_cast<llpkgc_file_t const*>(this));
if (pos == nullptr) {
return -1;
}
return pos - base;
}
int cmPkgConfigFileParser::OnIdentNext(char const* at, std::size_t len)
{
Ident_.append(at, len);
return 0;
}
int cmPkgConfigFileParser::OnIdentNextTr(llpkgc_file_t* parser, char const* at,
std::size_t len)
{
return static_cast<cmPkgConfigFileParser*>(parser)->OnIdentNext(at, len);
}
void cmPkgConfigFileParser::AppendValue(std::string text, bool isSubstitution)
{
if (!isSubstitution && !Data_.empty() && !Data_.back().Value.empty() &&
!Data_.back().Value.back().IsSubstitution) {
Data_.back().Value.back().Text += text;
return;
}
Data_.back().Value.emplace_back(std::move(text), isSubstitution);
}
int cmPkgConfigFileParser::OnSpanNext(char const*, std::size_t len)
{
Len_ += len;
return 0;
}
int cmPkgConfigParser::OnSpanNextTr(llpkgc_t* parser, char const* at,
std::size_t len)
int cmPkgConfigFileParser::OnSpanNextTr(llpkgc_file_t* parser, char const* at,
std::size_t len)
{
return static_cast<cmPkgConfigParser*>(parser)->OnSpanNext(at, len);
return static_cast<cmPkgConfigFileParser*>(parser)->OnSpanNext(at, len);
}
int cmPkgConfigParser::OnKey(char const* at, std::size_t len)
int cmPkgConfigFileParser::OnIdent(char const* at, std::size_t len)
{
Ptr_ = at;
Len_ = len;
Settings_.on_key = OnSpanNextTr;
Ident_.assign(at, len);
Settings_.on_ident = OnIdentNextTr;
return 0;
}
int cmPkgConfigParser::OnKeyTr(llpkgc_t* parser, char const* at,
std::size_t len)
int cmPkgConfigFileParser::OnIdentTr(llpkgc_file_t* parser, char const* at,
std::size_t len)
{
return static_cast<cmPkgConfigParser*>(parser)->OnKey(at, len);
return static_cast<cmPkgConfigFileParser*>(parser)->OnIdent(at, len);
}
int cmPkgConfigParser::OnKeywordComplete()
int cmPkgConfigFileParser::OnPropertyComplete()
{
Data_.emplace_back(false, cm::string_view{ Ptr_, Len_ });
Settings_.on_key = OnKeyTr;
Data_.emplace_back(std::vector<cmPkgConfigValueItem>{}, Ident_,
cmPkgConfigEntryKind::Property);
Settings_.on_ident = OnIdentTr;
return 0;
}
int cmPkgConfigParser::OnKeywordCompleteTr(llpkgc_t* parser)
int cmPkgConfigFileParser::OnPropertyCompleteTr(llpkgc_file_t* parser)
{
return static_cast<cmPkgConfigParser*>(parser)->OnKeywordComplete();
return static_cast<cmPkgConfigFileParser*>(parser)->OnPropertyComplete();
}
int cmPkgConfigParser::OnVariableComplete()
int cmPkgConfigFileParser::OnVariableComplete()
{
Data_.emplace_back(true, cm::string_view{ Ptr_, Len_ });
Settings_.on_key = OnKeyTr;
Data_.emplace_back(std::vector<cmPkgConfigValueItem>{}, Ident_,
cmPkgConfigEntryKind::Variable);
Settings_.on_ident = OnIdentTr;
return 0;
}
int cmPkgConfigParser::OnVariableCompleteTr(llpkgc_t* parser)
int cmPkgConfigFileParser::OnVariableCompleteTr(llpkgc_file_t* parser)
{
return static_cast<cmPkgConfigParser*>(parser)->OnVariableComplete();
return static_cast<cmPkgConfigFileParser*>(parser)->OnVariableComplete();
}
int cmPkgConfigParser::OnValueLiteral(char const* at, std::size_t len)
int cmPkgConfigFileParser::OnBarewordsComplete()
{
Data_.emplace_back(std::vector<cmPkgConfigValueItem>{}, Ident_,
cmPkgConfigEntryKind::Barewords);
Settings_.on_ident = OnIdentTr;
return 0;
}
int cmPkgConfigFileParser::OnBarewordsCompleteTr(llpkgc_file_t* parser)
{
return static_cast<cmPkgConfigFileParser*>(parser)->OnBarewordsComplete();
}
int cmPkgConfigFileParser::OnValueLiteral(char const* at, std::size_t len)
{
Ptr_ = at;
Len_ = len;
@@ -102,50 +252,408 @@ int cmPkgConfigParser::OnValueLiteral(char const* at, std::size_t len)
return 0;
}
int cmPkgConfigParser::OnValueLiteralTr(llpkgc_t* parser, char const* at,
std::size_t len)
int cmPkgConfigFileParser::OnValueLiteralTr(llpkgc_file_t* parser,
char const* at, std::size_t len)
{
return static_cast<cmPkgConfigParser*>(parser)->OnValueLiteral(at, len);
return static_cast<cmPkgConfigFileParser*>(parser)->OnValueLiteral(at, len);
}
int cmPkgConfigParser::OnValueLiteralComplete()
int cmPkgConfigFileParser::OnValueLiteralComplete()
{
Settings_.on_value_literal = OnValueLiteralTr;
if (Len_) {
Data_.back().Val.emplace_back(false, cm::string_view{ Ptr_, Len_ });
}
AppendValue(std::string{ Ptr_, Len_ }, false);
return 0;
}
int cmPkgConfigParser::OnValueLiteralCompleteTr(llpkgc_t* parser)
int cmPkgConfigFileParser::OnValueLiteralCompleteTr(llpkgc_file_t* parser)
{
return static_cast<cmPkgConfigParser*>(parser)->OnValueLiteralComplete();
return static_cast<cmPkgConfigFileParser*>(parser)->OnValueLiteralComplete();
}
int cmPkgConfigParser::OnValueVariable(char const* at, std::size_t len)
int cmPkgConfigFileParser::OnValueSubstitution(char const* at, std::size_t len)
{
Ptr_ = at;
Len_ = len;
Settings_.on_value_variable = OnSpanNextTr;
Settings_.on_value_substitution = OnSpanNextTr;
return 0;
}
int cmPkgConfigParser::OnValueVariableTr(llpkgc_t* parser, char const* at,
std::size_t len)
int cmPkgConfigFileParser::OnValueSubstitutionTr(llpkgc_file_t* parser,
char const* at,
std::size_t len)
{
return static_cast<cmPkgConfigParser*>(parser)->OnValueVariable(at, len);
return static_cast<cmPkgConfigFileParser*>(parser)->OnValueSubstitution(at,
len);
}
int cmPkgConfigParser::OnValueVariableComplete()
int cmPkgConfigFileParser::OnValueSubstitutionComplete()
{
Settings_.on_value_variable = OnValueVariableTr;
Data_.back().Val.emplace_back(true, cm::string_view{ Ptr_, Len_ });
Settings_.on_value_substitution = OnValueSubstitutionTr;
AppendValue(std::string{ Ptr_, Len_ }, true);
return 0;
}
int cmPkgConfigParser::OnValueVariableCompleteTr(llpkgc_t* parser)
int cmPkgConfigFileParser::OnValueSubstitutionCompleteTr(llpkgc_file_t* parser)
{
return static_cast<cmPkgConfigParser*>(parser)->OnValueVariableComplete();
return static_cast<cmPkgConfigFileParser*>(parser)
->OnValueSubstitutionComplete();
}
int cmPkgConfigFileParser::OnValueComplete()
{
return 0;
}
int cmPkgConfigFileParser::OnValueCompleteTr(llpkgc_file_t* parser)
{
return static_cast<cmPkgConfigFileParser*>(parser)->OnValueComplete();
}
cmPkgConfigFragmentParser::cmPkgConfigFragmentParser()
{
llpkgc_frag_init(static_cast<llpkgc_frag_t*>(this), &Settings_);
}
llpkgc_frag_errno_t cmPkgConfigFragmentParser::Parse(char const* input,
std::size_t len)
{
return llpkgc_frag_execute(static_cast<llpkgc_frag_t*>(this), input, len);
}
llpkgc_frag_errno_t cmPkgConfigFragmentParser::Finish()
{
return llpkgc_frag_finish(static_cast<llpkgc_frag_t*>(this));
}
llpkgc_frag_errno_t cmPkgConfigFragmentParser::GetErrno() const
{
return llpkgc_frag_get_errno(static_cast<llpkgc_frag_t const*>(this));
}
char const* cmPkgConfigFragmentParser::ErrorReason() const
{
return llpkgc_frag_get_error_reason(static_cast<llpkgc_frag_t const*>(this));
}
int cmPkgConfigFragmentParser::OnFragmentText(char const* at, std::size_t len)
{
CurrentFragment_.append(at, len);
return 0;
}
int cmPkgConfigFragmentParser::OnFragmentTextTr(llpkgc_frag_t* parser,
char const* at,
std::size_t len)
{
return static_cast<cmPkgConfigFragmentParser*>(parser)->OnFragmentText(at,
len);
}
int cmPkgConfigFragmentParser::OnFragmentComplete()
{
Fragments_.push_back(std::move(CurrentFragment_));
CurrentFragment_.clear();
return 0;
}
int cmPkgConfigFragmentParser::OnFragmentCompleteTr(llpkgc_frag_t* parser)
{
return static_cast<cmPkgConfigFragmentParser*>(parser)->OnFragmentComplete();
}
cmPkgConfigDependencyParser::cmPkgConfigDependencyParser()
{
llpkgc_dep_init(static_cast<llpkgc_dep_t*>(this), &Settings_);
}
llpkgc_dep_errno_t cmPkgConfigDependencyParser::Parse(char const* input,
std::size_t len)
{
return llpkgc_dep_execute(static_cast<llpkgc_dep_t*>(this), input, len);
}
llpkgc_dep_errno_t cmPkgConfigDependencyParser::Finish()
{
return llpkgc_dep_finish(static_cast<llpkgc_dep_t*>(this));
}
llpkgc_dep_errno_t cmPkgConfigDependencyParser::GetErrno() const
{
return llpkgc_dep_get_errno(static_cast<llpkgc_dep_t const*>(this));
}
char const* cmPkgConfigDependencyParser::ErrorReason() const
{
return llpkgc_dep_get_error_reason(static_cast<llpkgc_dep_t const*>(this));
}
int cmPkgConfigDependencyParser::OnDependencyName(char const* at,
std::size_t len)
{
Current_.Name.assign(at, len);
return 0;
}
int cmPkgConfigDependencyParser::OnDependencyNameTr(llpkgc_dep_t* parser,
char const* at,
std::size_t len)
{
return static_cast<cmPkgConfigDependencyParser*>(parser)->OnDependencyName(
at, len);
}
int cmPkgConfigDependencyParser::OnDependencyOperator(char const* at,
std::size_t len)
{
Current_.Operation = LookupDependencyOperation(cm::string_view{ at, len });
return 0;
}
int cmPkgConfigDependencyParser::OnDependencyOperatorTr(llpkgc_dep_t* parser,
char const* at,
std::size_t len)
{
return static_cast<cmPkgConfigDependencyParser*>(parser)
->OnDependencyOperator(at, len);
}
int cmPkgConfigDependencyParser::OnDependencyVersion(char const* at,
std::size_t len)
{
Current_.Version.assign(at, len);
return 0;
}
int cmPkgConfigDependencyParser::OnDependencyVersionTr(llpkgc_dep_t* parser,
char const* at,
std::size_t len)
{
return static_cast<cmPkgConfigDependencyParser*>(parser)
->OnDependencyVersion(at, len);
}
int cmPkgConfigDependencyParser::OnDependencyComplete()
{
Dependencies_.push_back(std::move(Current_));
Current_ = cmPkgConfigDependencySpec{};
return 0;
}
int cmPkgConfigDependencyParser::OnDependencyCompleteTr(llpkgc_dep_t* parser)
{
return static_cast<cmPkgConfigDependencyParser*>(parser)
->OnDependencyComplete();
}
llpkgc_file_errno_t cmPkgConfigParser::Parse(char const* input,
std::size_t len)
{
return File_.Parse(input, len);
}
llpkgc_file_errno_t cmPkgConfigParser::Finish()
{
return File_.Finish();
}
llpkgc_file_errno_t cmPkgConfigParser::Finish(char const* input,
std::size_t len)
{
auto err = Parse(input, len);
if (err != PFCE_OK) {
return err;
}
return Finish();
}
llpkgc_file_errno_t cmPkgConfigParser::GetErrno() const
{
return File_.GetErrno();
}
char const* cmPkgConfigParser::ErrorReason() const
{
return File_.ErrorReason();
}
std::ptrdiff_t cmPkgConfigParser::ErrorOffset(char const* base) const
{
return File_.ErrorOffset(base);
}
void cmPkgConfigParser::SetVariable(std::string name, std::string value)
{
ValueItems variable;
variable.emplace_back(std::move(value), false);
StoredVariables_[std::move(name)] = std::move(variable);
Cache_.clear();
}
std::string cmPkgConfigParser::GetVariable(std::string const& name)
{
return ResolveValue(StoredVariables_[name]);
}
bool cmPkgConfigParser::HasProperty(std::string const& name) const
{
return std::any_of(StoredProperties_.begin(), StoredProperties_.end(),
[&name](cmPkgConfigEntry const& entry) -> bool {
return entry.Ident == name;
});
}
std::vector<std::string> cmPkgConfigParser::GetFragments(
std::string const& name)
{
auto it = Cache_.find(name);
if (it != Cache_.end()) {
return it->second.Fragments;
}
cmPkgConfigResolvedValue cache;
if (!ResolveAndParse(name, cache)) {
return {};
}
return cache.Fragments;
}
std::vector<cmPkgConfigDependencySpec> cmPkgConfigParser::GetDependencies(
std::string const& name)
{
auto it = Cache_.find(name);
if (it != Cache_.end()) {
return it->second.Dependencies;
}
cmPkgConfigResolvedValue cache;
if (!ResolveAndParse(name, cache)) {
return {};
}
return cache.Dependencies;
}
std::string cmPkgConfigParser::GetLiteral(std::string const& name)
{
auto it = Cache_.find(name);
if (it != Cache_.end()) {
return it->second.Literal;
}
cmPkgConfigResolvedValue cache;
if (!ResolveAndParse(name, cache)) {
return {};
}
return cache.Literal;
}
void cmPkgConfigParser::ParseComplete()
{
StoredVariables_.clear();
StoredProperties_.clear();
Cache_.clear();
for (auto const& entry : File_.Data_) {
switch (entry.Kind) {
case cmPkgConfigEntryKind::Variable:
StoredVariables_[entry.Ident] = entry.Value;
break;
case cmPkgConfigEntryKind::Property:
StoredProperties_.push_back(entry);
break;
default:
break;
}
}
}
std::string cmPkgConfigParser::ResolveValue(ValueItems const& items)
{
return ResolveValueImpl(items, {});
}
std::string cmPkgConfigParser::ResolveValueImpl(
ValueItems const& items, std::set<std::string> resolving)
{
std::string result;
if (items.empty()) {
return result;
}
for (auto const& item : items) {
if (item.IsSubstitution) {
if (resolving.count(item.Text)) {
result += "${";
result += item.Text;
result += "}";
continue;
}
auto it = StoredVariables_.find(item.Text);
if (it != StoredVariables_.end()) {
resolving.insert(item.Text);
result += ResolveValueImpl(it->second, resolving);
}
} else {
result += item.Text;
}
}
return result;
}
bool cmPkgConfigParser::ResolveAndParse(std::string const& name,
cmPkgConfigResolvedValue& cache)
{
cmPkgConfigEntry const* entry = nullptr;
for (auto const& candidate : StoredProperties_) {
if (candidate.Ident == name) {
entry = &candidate;
break;
}
}
if (entry == nullptr) {
return false;
}
std::string resolved = ResolveValue(entry->Value);
auto start = resolved.find_first_not_of(" \t");
if (start != std::string::npos) {
resolved.erase(0, start);
}
cache.Literal = resolved;
std::string lower = name;
for (auto& c : lower) {
c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
}
if (IsFragmentProperty(lower)) {
cmPkgConfigFragmentParser fragmentParser;
auto err = fragmentParser.Parse(resolved.data(), resolved.size());
if (err == PFFE_OK && fragmentParser.Finish() == PFFE_OK) {
cache.Fragments = fragmentParser.Fragments();
}
} else if (IsDependencyProperty(lower)) {
cmPkgConfigDependencyParser dependencyParser;
auto err = dependencyParser.Parse(resolved.data(), resolved.size());
if (err == PDEP_OK && dependencyParser.Finish() == PDEP_OK) {
cache.Dependencies = dependencyParser.Dependencies();
}
}
Cache_[name] = cache;
return true;
}
bool cmPkgConfigParser::IsFragmentProperty(std::string const& lower)
{
return lower == "cflags" || lower == "cflags.private" ||
lower == "cflags.shared" || lower == "libs" || lower == "libs.private" ||
lower == "libs.shared";
}
bool cmPkgConfigParser::IsDependencyProperty(std::string const& lower)
{
return lower == "conflicts" || lower == "provides" || lower == "requires" ||
lower == "requires.private" || lower == "requires.internal" ||
lower == "requires.shared";
}

View File

@@ -6,88 +6,259 @@
#include "cmConfigure.h" // IWYU pragma: keep
#include <cstddef>
#include <set>
#include <string>
#include <unordered_map>
#include <vector>
#include <cm/string_view>
#include <cmllpkgc/llpkgc.h>
struct cmPkgConfigValueElement
struct cmPkgConfigValueItem
{
cmPkgConfigValueItem() = default;
cmPkgConfigValueItem(std::string text, bool isSubstitution);
cmPkgConfigValueElement() = default;
std::string Text;
bool IsSubstitution = false;
};
cmPkgConfigValueElement(bool isVariable, cm::string_view data);
bool IsVariable;
cm::string_view Data;
enum class cmPkgConfigEntryKind
{
Property,
Variable,
Barewords,
};
struct cmPkgConfigEntry
{
cmPkgConfigEntry() = default;
cmPkgConfigEntry(std::vector<cmPkgConfigValueItem> value, std::string ident,
cmPkgConfigEntryKind kind);
cmPkgConfigEntry(bool isVariable, cm::string_view key);
bool IsVariable;
cm::string_view Key;
std::vector<cmPkgConfigValueElement> Val;
std::vector<cmPkgConfigValueItem> Value;
std::string Ident;
cmPkgConfigEntryKind Kind = cmPkgConfigEntryKind::Barewords;
};
class cmPkgConfigParser : llpkgc_t
class cmPkgConfigFileParser : private llpkgc_file_t
{
friend class cmPkgConfigParser;
public:
cmPkgConfigParser();
cmPkgConfigFileParser();
cmPkgConfigFileParser(cmPkgConfigFileParser const& other);
cmPkgConfigFileParser(cmPkgConfigFileParser&& other) noexcept;
cmPkgConfigFileParser& operator=(cmPkgConfigFileParser const& other);
cmPkgConfigFileParser& operator=(cmPkgConfigFileParser&& other) noexcept;
llpkgc_errno_t Parse(char* buf, std::size_t len);
llpkgc_file_errno_t Parse(char const* input, std::size_t len);
llpkgc_file_errno_t Finish();
void Resume();
llpkgc_errno_t Finish();
llpkgc_errno_t Finish(char* buf, std::size_t len);
std::vector<cmPkgConfigEntry>& Data() { return Data_; }
llpkgc_file_errno_t GetErrno() const;
char const* ErrorReason() const;
std::ptrdiff_t ErrorOffset(char const* base) const;
private:
void ResetCallbacks();
int OnIdentNext(char const* at, std::size_t len);
static int OnIdentNextTr(llpkgc_file_t* parser, char const* at,
std::size_t len);
void AppendValue(std::string text, bool isSubstitution);
int OnSpanNext(char const*, std::size_t len);
static int OnSpanNextTr(llpkgc_t* parser, char const* at, std::size_t len);
static int OnSpanNextTr(llpkgc_file_t* parser, char const* at,
std::size_t len);
int OnKey(char const* at, std::size_t len);
static int OnKeyTr(llpkgc_t* parser, char const* at, std::size_t len);
int OnIdent(char const* at, std::size_t len);
static int OnIdentTr(llpkgc_file_t* parser, char const* at, std::size_t len);
int OnKeywordComplete();
static int OnKeywordCompleteTr(llpkgc_t* parser);
int OnPropertyComplete();
static int OnPropertyCompleteTr(llpkgc_file_t* parser);
int OnVariableComplete();
static int OnVariableCompleteTr(llpkgc_t* parser);
static int OnVariableCompleteTr(llpkgc_file_t* parser);
int OnBarewordsComplete();
static int OnBarewordsCompleteTr(llpkgc_file_t* parser);
int OnValueLiteral(char const* at, std::size_t len);
static int OnValueLiteralTr(llpkgc_t* parser, char const* at,
static int OnValueLiteralTr(llpkgc_file_t* parser, char const* at,
std::size_t len);
int OnValueLiteralComplete();
static int OnValueLiteralCompleteTr(llpkgc_t* parser);
static int OnValueLiteralCompleteTr(llpkgc_file_t* parser);
int OnValueVariable(char const* at, std::size_t len);
static int OnValueVariableTr(llpkgc_t* parser, char const* at,
std::size_t len);
int OnValueSubstitution(char const* at, std::size_t len);
static int OnValueSubstitutionTr(llpkgc_file_t* parser, char const* at,
std::size_t len);
int OnValueVariableComplete();
static int OnValueVariableCompleteTr(llpkgc_t* parser);
int OnValueSubstitutionComplete();
static int OnValueSubstitutionCompleteTr(llpkgc_file_t* parser);
llpkgc_settings_t Settings_{
OnKeyTr,
int OnValueComplete();
static int OnValueCompleteTr(llpkgc_file_t* parser);
llpkgc_file_settings_t Settings_{
OnIdentTr,
OnValueLiteralTr,
OnValueVariableTr,
OnValueSubstitutionTr,
nullptr, // on_line_begin
OnKeywordCompleteTr,
OnPropertyCompleteTr,
OnVariableCompleteTr,
OnBarewordsCompleteTr,
OnValueLiteralCompleteTr,
OnValueVariableCompleteTr,
nullptr, // on_value_complete
OnValueSubstitutionCompleteTr,
OnValueCompleteTr,
nullptr, // on_pkgc_complete
};
char const* Ptr_;
std::size_t Len_;
char const* Ptr_ = nullptr;
std::size_t Len_ = 0;
std::string Ident_;
std::vector<cmPkgConfigEntry> Data_;
};
class cmPkgConfigFragmentParser : private llpkgc_frag_t
{
public:
cmPkgConfigFragmentParser();
llpkgc_frag_errno_t Parse(char const* input, std::size_t len);
llpkgc_frag_errno_t Finish();
llpkgc_frag_errno_t GetErrno() const;
char const* ErrorReason() const;
std::vector<std::string> const& Fragments() const { return Fragments_; }
private:
int OnFragmentText(char const* at, std::size_t len);
static int OnFragmentTextTr(llpkgc_frag_t* parser, char const* at,
std::size_t len);
int OnFragmentComplete();
static int OnFragmentCompleteTr(llpkgc_frag_t* parser);
llpkgc_frag_settings_t Settings_{
OnFragmentTextTr, OnFragmentCompleteTr,
nullptr, // on_fraglist_complete
};
std::string CurrentFragment_;
std::vector<std::string> Fragments_;
};
enum class cmPkgConfigDependencyOperation
{
None,
LessThan,
LessThanEqual,
Equal,
NotEqual,
GreaterThan,
GreaterThanEqual,
};
struct cmPkgConfigDependencySpec
{
std::string Name;
cmPkgConfigDependencyOperation Operation =
cmPkgConfigDependencyOperation::None;
std::string Version;
};
class cmPkgConfigDependencyParser : private llpkgc_dep_t
{
public:
cmPkgConfigDependencyParser();
llpkgc_dep_errno_t Parse(char const* input, std::size_t len);
llpkgc_dep_errno_t Finish();
llpkgc_dep_errno_t GetErrno() const;
char const* ErrorReason() const;
std::vector<cmPkgConfigDependencySpec> const& Dependencies() const
{
return Dependencies_;
}
private:
int OnDependencyName(char const* at, std::size_t len);
static int OnDependencyNameTr(llpkgc_dep_t* parser, char const* at,
std::size_t len);
int OnDependencyOperator(char const* at, std::size_t len);
static int OnDependencyOperatorTr(llpkgc_dep_t* parser, char const* at,
std::size_t len);
int OnDependencyVersion(char const* at, std::size_t len);
static int OnDependencyVersionTr(llpkgc_dep_t* parser, char const* at,
std::size_t len);
int OnDependencyComplete();
static int OnDependencyCompleteTr(llpkgc_dep_t* parser);
llpkgc_dep_settings_t Settings_{
OnDependencyNameTr,
OnDependencyOperatorTr,
OnDependencyVersionTr,
OnDependencyCompleteTr,
nullptr, // on_dep_list_complete
};
cmPkgConfigDependencySpec Current_;
std::vector<cmPkgConfigDependencySpec> Dependencies_;
};
struct cmPkgConfigResolvedValue
{
std::vector<std::string> Fragments;
std::vector<cmPkgConfigDependencySpec> Dependencies;
std::string Literal;
};
class cmPkgConfigParser
{
public:
cmPkgConfigParser() = default;
llpkgc_file_errno_t Parse(char const* input, std::size_t len);
llpkgc_file_errno_t Finish();
llpkgc_file_errno_t Finish(char const* data, std::size_t len);
llpkgc_file_errno_t GetErrno() const;
char const* ErrorReason() const;
std::ptrdiff_t ErrorOffset(char const* base) const;
void SetVariable(std::string name, std::string value);
std::string GetVariable(std::string const& name);
bool HasProperty(std::string const& name) const;
std::vector<std::string> GetFragments(std::string const& name);
std::vector<cmPkgConfigDependencySpec> GetDependencies(
std::string const& name);
std::string GetLiteral(std::string const& name);
void ParseComplete();
private:
using ValueItems = std::vector<cmPkgConfigValueItem>;
std::string ResolveValue(ValueItems const& items);
std::string ResolveValueImpl(ValueItems const& items,
std::set<std::string> resolving);
bool ResolveAndParse(std::string const& name,
cmPkgConfigResolvedValue& cache);
static bool IsFragmentProperty(std::string const& lower);
static bool IsDependencyProperty(std::string const& lower);
cmPkgConfigFileParser File_;
std::unordered_map<std::string, ValueItems> StoredVariables_;
std::vector<cmPkgConfigEntry> StoredProperties_;
std::unordered_map<std::string, cmPkgConfigResolvedValue> Cache_;
};

File diff suppressed because it is too large Load Diff

View File

@@ -6,14 +6,11 @@
#include "cmConfigure.h" // IWYU pragma: keep
#include <string>
#include <unordered_map>
#include <vector>
#include <cm/optional>
#include <cm/string_view>
// From cmPkgConfigParser.h, IWYU doesn't like including the header
struct cmPkgConfigEntry;
#include "cmPkgConfigParser.h"
struct cmPkgConfigCflagsResult
{
@@ -30,29 +27,6 @@ struct cmPkgConfigLibsResult
std::vector<std::string> LinkOptions;
};
struct cmPkgConfigVersionReq
{
enum
{
ANY = 0,
LT,
LT_EQ,
EQ,
NEQ,
GT_EQ,
GT,
} Operation = ANY;
std::string Version;
std::string string() const;
};
struct cmPkgConfigDependency
{
std::string Name;
cmPkgConfigVersionReq VerReq;
};
struct cmPkgConfigEnv
{
cm::optional<std::vector<std::string>> Path;
@@ -70,46 +44,51 @@ struct cmPkgConfigEnv
bool AllowSysLibs = true;
};
class cmPkgConfigResult
{
public:
std::unordered_map<std::string, std::string> Keywords;
std::unordered_map<std::string, std::string> Variables;
std::string Name();
std::string Description();
std::string Version();
std::vector<cmPkgConfigDependency> Conflicts();
std::vector<cmPkgConfigDependency> Provides();
std::vector<cmPkgConfigDependency> Requires(bool priv = false);
cmPkgConfigCflagsResult Cflags(bool priv = false);
cmPkgConfigLibsResult Libs(bool priv = false);
cmPkgConfigEnv* env;
private:
std::string StrOrDefault(std::string const& key, cm::string_view def = "");
};
class cmPkgConfigResolver
{
friend class cmPkgConfigResult;
public:
static cm::optional<cmPkgConfigResult> ResolveStrict(
std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv& env);
enum class Strictness
{
Strict,
Permissive,
BestEffort,
};
static cm::optional<cmPkgConfigResult> ResolvePermissive(
std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv& env);
cmPkgConfigResolver(cmPkgConfigParser parser, cmPkgConfigEnv const& env,
std::string const& pcFileDir);
static cmPkgConfigResult ResolveBestEffort(
std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv& env);
static cm::optional<cmPkgConfigResolver> Resolve(
cmPkgConfigParser parser, cmPkgConfigEnv const& env, Strictness strictness,
std::string const& pcFileDir);
static cmPkgConfigVersionReq ParseVersion(std::string const& version);
static cm::optional<cmPkgConfigResolver> ResolveStrict(
cmPkgConfigParser parser, cmPkgConfigEnv const& env,
std::string const& pcFileDir);
static bool CheckVersion(cmPkgConfigVersionReq const& desired,
static cm::optional<cmPkgConfigResolver> ResolvePermissive(
cmPkgConfigParser parser, cmPkgConfigEnv const& env,
std::string const& pcFileDir);
static cmPkgConfigResolver ResolveBestEffort(cmPkgConfigParser parser,
cmPkgConfigEnv const& env,
std::string const& pcFileDir);
std::string Literal(std::string const& key) const;
std::string Name() const;
std::string Description() const;
std::string Version() const;
std::vector<cmPkgConfigDependencySpec> Conflicts() const;
std::vector<cmPkgConfigDependencySpec> Provides() const;
std::vector<cmPkgConfigDependencySpec> Requires(bool priv = false) const;
cmPkgConfigCflagsResult Cflags(bool priv = false) const;
cmPkgConfigLibsResult Libs(bool priv = false) const;
static std::string VersionReqString(cmPkgConfigDependencySpec const& spec);
static bool CheckVersion(cmPkgConfigDependencySpec const& desired,
std::string const& provided);
static void ReplaceSep(std::string& list);
@@ -121,55 +100,23 @@ public:
#endif
private:
static std::string HandleVariablePermissive(
cmPkgConfigEntry const& entry,
std::unordered_map<std::string, std::string> const& variables);
void ConfigureParser(std::string const& pcFileDir);
bool HasRequiredFields() const;
bool HasStrictConflicts() const;
static cm::optional<std::string> HandleVariableStrict(
cmPkgConfigEntry const& entry,
std::unordered_map<std::string, std::string> const& variables);
static std::string HandleKeyword(
cmPkgConfigEntry const& entry,
std::unordered_map<std::string, std::string> const& variables);
static std::vector<cm::string_view> TokenizeFlags(
std::string const& flagline);
std::vector<std::string> Fragments(std::string const& key) const;
std::vector<cmPkgConfigDependencySpec> Dependencies(
std::string const& key) const;
static cmPkgConfigCflagsResult MangleCflags(
std::vector<cm::string_view> const& flags);
static cmPkgConfigCflagsResult MangleCflags(
std::vector<cm::string_view> const& flags, std::string const& sysroot);
static cmPkgConfigCflagsResult MangleCflags(
std::vector<cm::string_view> const& flags,
std::vector<std::string> const& syspaths);
static cmPkgConfigCflagsResult MangleCflags(
std::vector<cm::string_view> const& flags, std::string const& sysroot,
std::vector<std::string> const& syspaths);
std::vector<std::string> const& flags, cmPkgConfigEnv const& env);
static cmPkgConfigLibsResult MangleLibs(
std::vector<cm::string_view> const& flags);
std::vector<std::string> const& flags, cmPkgConfigEnv const& env);
static cmPkgConfigLibsResult MangleLibs(
std::vector<cm::string_view> const& flags, std::string const& sysroot);
static cmPkgConfigLibsResult MangleLibs(
std::vector<cm::string_view> const& flags,
std::vector<std::string> const& syspaths);
static cmPkgConfigLibsResult MangleLibs(
std::vector<cm::string_view> const& flags, std::string const& sysroot,
std::vector<std::string> const& syspaths);
static std::string Reroot(cm::string_view flag, cm::string_view prefix,
static std::string Reroot(std::string const& flag, char const* prefix,
std::string const& sysroot);
static cmPkgConfigVersionReq ParseVersion(std::string::const_iterator& cur,
std::string::const_iterator end);
static std::vector<cmPkgConfigDependency> ParseDependencies(
std::string const& deps);
mutable cmPkgConfigParser Parser;
cmPkgConfigEnv const* Env;
};

View File

@@ -44,16 +44,21 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
result = parser.Finish();
(void)result;
// Access parsed data to exercise accessors
auto& entries = parser.Data();
for (auto const& entry : entries) {
(void)entry.IsVariable;
(void)entry.Key;
for (auto const& elem : entry.Val) {
(void)elem.IsVariable;
(void)elem.Data;
}
}
parser.ParseComplete();
(void)parser.GetLiteral("Name");
(void)parser.GetLiteral("Description");
(void)parser.GetLiteral("Version");
(void)parser.GetFragments("Cflags");
(void)parser.GetFragments("Cflags.private");
(void)parser.GetFragments("CFlags");
(void)parser.GetFragments("CFlags.private");
(void)parser.GetFragments("Libs");
(void)parser.GetFragments("Libs.private");
(void)parser.GetDependencies("Requires");
(void)parser.GetDependencies("Requires.private");
(void)parser.GetDependencies("Conflicts");
(void)parser.GetDependencies("Provides");
return 0;
}

View File

@@ -0,0 +1,26 @@
cmake_pkg_config(
EXTRACT builtindirs
PC_SYSROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/Sys Root"
TOP_BUILD_DIR "${CMAKE_CURRENT_LIST_DIR}/Top Build"
)
set(expected_dir "${CMAKE_CURRENT_LIST_DIR}/PackageRoot")
set(expected_sysroot "${CMAKE_CURRENT_LIST_DIR}/Sys Root")
set(expected_top_build "${CMAKE_CURRENT_LIST_DIR}/Top Build")
set(expected_options
"-DROOT=${expected_sysroot}/include"
"-DTOP=${expected_top_build}/generated"
)
if(NOT CMAKE_PKG_CONFIG_INCLUDES STREQUAL "-I${expected_sysroot}${expected_dir}/include")
message(FATAL_ERROR "Unexpected built-in directory include: ${CMAKE_PKG_CONFIG_INCLUDES}")
endif()
if(NOT CMAKE_PKG_CONFIG_COMPILE_OPTIONS STREQUAL "${expected_options}")
message(FATAL_ERROR
"Unexpected built-in directory options: ${CMAKE_PKG_CONFIG_COMPILE_OPTIONS}")
endif()
if(NOT CMAKE_PKG_CONFIG_LIBDIRS STREQUAL "-L${expected_sysroot}${expected_dir}/lib")
message(FATAL_ERROR "Unexpected built-in directory libdir: ${CMAKE_PKG_CONFIG_LIBDIRS}")
endif()

View File

@@ -0,0 +1,9 @@
Name: BuiltinDirs
Description: Uses built-in directory variables
Version: 1.0.0
includedir=${pcfiledir}/include
libdir=${pcfiledir}/lib
Cflags: -I${includedir} -DROOT=${pc_sysrootdir}/include -DTOP=${pc_top_builddir}/generated
Libs: -L${libdir} -lbuiltindirs

View File

@@ -1,4 +1,4 @@
Name: Invalid
Description: Will cause a parse error
Version: 1.0.0
BrokenKey
BrokenKey: ${unterminated

View File

@@ -8,6 +8,7 @@ foreach(strictness IN ITEMS STRICT PERMISSIVE BEST_EFFORT)
)
endforeach()
run_cmake(ExtractBuiltinDirs)
run_cmake(ExtractEnv)
run_cmake(ExtractFields)
run_cmake(ExtractMangle)