@@ -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 ())));
0 commit comments