Skip to content

Commit aba53cd

Browse files
committed
search: use a struct for tracking components
We'll need this for link only components
1 parent bd9cc04 commit aba53cd

5 files changed

Lines changed: 91 additions & 23 deletions

File tree

src/cps/search.cpp

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ namespace cps::search {
3131

3232
using version::to_string;
3333

34+
struct Component {
35+
std::string name;
36+
bool link_only;
37+
};
38+
3439
/// @brief A CPS file, along with the components in that CPS file to
3540
/// load
3641
class Dependency {
@@ -40,7 +45,7 @@ namespace cps::search {
4045
/// @brief The loaded CPS file
4146
loader::Package package;
4247
/// @brief the components from that CPS file to use
43-
std::vector<std::string> components;
48+
std::vector<Component> components;
4449
};
4550

4651
/// @brief A DAG node
@@ -377,64 +382,79 @@ namespace cps::search {
377382
/// @param node The node to process
378383
/// @param components the components required from this node
379384
void set_components(std::shared_ptr<Node> node, const std::vector<std::string> & components,
380-
bool default_components) {
381-
// If this package needs the default_components, then add to the list of used components
385+
bool default_components, bool link_only = false) {
386+
// Set the components that this package's dependees want
382387
if (default_components && node->data.package.default_components) {
383388
const std::vector<std::string> & defs = node->data.package.default_components.value();
384-
node->data.components.insert(node->data.components.end(), defs.begin(), defs.end());
389+
std::transform(defs.begin(), defs.end(), std::back_insert_iterator(node->data.components),
390+
[link_only](std::string name) {
391+
return Component{std::move(name), link_only};
392+
});
385393
}
386394
// Then add all of the explicitly listed components
387-
node->data.components.insert(node->data.components.end(), components.begin(), components.end());
395+
std::transform(components.begin(), components.end(), std::back_insert_iterator(node->data.components),
396+
[link_only](std::string name) {
397+
return Component{std::move(name), link_only};
398+
});
388399

389400
// Handle self requirements first
390401
// This takes the form `"requires": [":a", ":b"]`
391402
// These must be handled before child dependencies, as they may alter the requirements placed on the
392403
// children..
393-
std::vector<std::string> self_requires = node->data.components;
404+
std::vector<Component> self_requires = node->data.components;
394405
std::unordered_set<std::string> processed;
395406
bool self_defaults = default_components;
396407
while (!self_requires.empty()) {
397-
const std::string c_name = self_requires.back();
408+
const Component this_comp = self_requires.back();
398409
self_requires.pop_back();
399-
if (processed.find(c_name) != processed.end()) {
410+
if (processed.find(this_comp.name) != processed.end()) {
400411
continue;
401412
}
402-
processed.emplace(c_name);
413+
processed.emplace(this_comp.name);
403414

404-
const loader::Component & component = node->data.package.components.at(c_name);
415+
const loader::Component & component = node->data.package.components.at(this_comp.name);
405416
auto && required = process_requires(component.require);
406417
if (auto && self = required.find(""); self != required.end()) {
407418
// Don't insert these twice
408419
if (!self_defaults && self->second.defaults && node->data.package.default_components) {
409420
self_defaults = true;
410421
const std::vector<std::string> & defs = node->data.package.default_components.value();
411-
node->data.components.insert(node->data.components.end(), defs.begin(), defs.end());
422+
std::transform(defs.begin(), defs.end(), std::back_insert_iterator(node->data.components),
423+
[](std::string name) { return Component{std::move(name)}; });
412424
}
413425
for (auto && comp : self->second.components) {
414426
if (processed.find(comp) != processed.end()) {
415427
continue;
416428
}
417-
self_requires.emplace_back(comp);
418-
node->data.components.emplace_back(comp);
429+
self_requires.emplace_back(Component{comp});
430+
node->data.components.emplace_back(Component{comp});
419431
}
420432
}
421433
}
422434

423435
// Walk the list of components for this component, adding component
424436
// requirements recursively for external requirements.
425-
for (const std::string & c_name : node->data.components) {
437+
for (const Component & this_comp : node->data.components) {
426438
// It's possible that the Package::Requires section listed
427439
// dependencies we don't actually need. If we don't need them we
428440
// can trim the graph
429441
std::vector<std::shared_ptr<Node>> trimmed;
430442

431443
// This *should* be validated such that we won't have an exception
432-
const loader::Component & component = node->data.package.components.at(c_name);
444+
const loader::Component & component = node->data.package.components.at(this_comp.name);
433445
auto && required = process_requires(component.require);
434446
for (std::shared_ptr<Node> & child : node->depends) {
435447
if (auto && child_comps = required.find(child->data.package.name); child_comps != required.end()) {
436448
trimmed.emplace_back(child);
437-
set_components(child, child_comps->second.components, child_comps->second.defaults);
449+
set_components(child, child_comps->second.components, child_comps->second.defaults, link_only);
450+
}
451+
}
452+
auto && link_required = process_requires(component.link_requires);
453+
for (std::shared_ptr<Node> & child : node->depends) {
454+
if (auto && child_comps = link_required.find(child->data.package.name);
455+
child_comps != link_required.end()) {
456+
trimmed.emplace_back(child);
457+
set_components(child, child_comps->second.components, child_comps->second.defaults, true);
438458
}
439459
}
440460
node->depends = trimmed;
@@ -485,27 +505,30 @@ namespace cps::search {
485505
return s;
486506
};
487507

488-
for (const auto & c_name : node->data.components) {
508+
for (const Component & cps_comp : node->data.components) {
489509
// We should have already errored if this is not the case
490-
auto && f = node->data.package.components.find(c_name);
510+
auto && f = node->data.package.components.find(cps_comp.name);
491511
utils::assert_fn(
492512
f != node->data.package.components.end(),
493-
fmt::format("Could not find component {} of package {}", c_name, node->data.package.name));
513+
fmt::format("Could not find component {} of package {}", cps_comp.name, node->data.package.name));
494514
auto && comp = f->second;
495515

496516
// Convert prefix at this point because:
497517
// 1. we are about to lose which CPS file the information came
498518
// from
499519
// 2. if we do it at the search point we have to plumb overrides
500520
// deep into that
501-
merge_result<loader::KnownLanguages, std::string>(comp.includes, result.includes, prefix_replacer);
502-
merge_result(comp.definitions, result.definitions);
503-
merge_result(comp.compile_flags, result.compile_flags);
521+
if (!cps_comp.link_only) {
522+
merge_result<loader::KnownLanguages, std::string>(comp.includes, result.includes, prefix_replacer);
523+
merge_result(comp.definitions, result.definitions);
524+
merge_result(comp.compile_flags, result.compile_flags);
525+
}
504526
merge_result(comp.link_libraries, result.link_libraries);
505527
merge_result(comp.link_flags, result.link_flags);
506528
if (comp.type != loader::Type::interface) {
507529
if (!comp.location) {
508-
return tl::make_unexpected(fmt::format("Component `{}` requires 'location' attribute", c_name));
530+
return tl::make_unexpected(
531+
fmt::format("Component `{}` requires 'location' attribute", cps_comp.name));
509532
}
510533
result.link_location.emplace_back(
511534
prefix_replacer(comp.link_location.value_or(comp.location.value())));

tests/cases/cps-config.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,9 @@ name = "requires component from self nested"
133133
cps = "full"
134134
args = ["flags", "--component", "requires-self", "--cflags", "--print-errors"]
135135
expected = "-fvectorize -I/usr/local/include -I/opt/include -DBAR=2 -DFOO=1 -DOTHER"
136+
137+
[[case]]
138+
name = "link requires nested"
139+
cps = "link-requires"
140+
args = ["flags", "--component", "nested", "--cflags", "--libs", "--print-errors"]
141+
expected = "-lfake"

tests/cases/pkg-config-compat.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,9 @@ name = "parsing pc file"
5959
cps = "pc-variables"
6060
args = ["pkg-config", "--cflags"]
6161
expected = "-I/home/kaniini/pkg/include/libfoo"
62+
63+
[[case]]
64+
name = "link requires"
65+
cps = "link-requires"
66+
args = ["pkg-config", "--cflags", "--libs", "--print-errors"]
67+
expected = "-L/usr/lib/ -flto -l/something/lib/libfoo.so -lbar"
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "link-requires",
3+
"cps_version": "0.13.0",
4+
"version": "1.0.0",
5+
"requires": {
6+
"full": null,
7+
"multiple-components": null
8+
},
9+
"prefix": "/prefix",
10+
"components": {
11+
"default": {
12+
"type": "interface",
13+
"link_requires": [
14+
"full"
15+
]
16+
},
17+
"nested": {
18+
"type": "interface",
19+
"link_requires": [
20+
"multiple-components:link-requires"
21+
]
22+
}
23+
},
24+
"default_components": [
25+
"default"
26+
]
27+
}

tests/cps-files/lib/cps/multiple-components.cps

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@
6868
"requires": [
6969
"minimal:sample0"
7070
]
71+
},
72+
"link-requires": {
73+
"type": "interface",
74+
"link_requires": [
75+
"minimal:sample1"
76+
]
7177
}
7278
},
7379
"default_components": [

0 commit comments

Comments
 (0)