44
55use std:: cell:: { Cell , RefCell } ;
66use std:: collections:: { BTreeSet , HashMap , HashSet } ;
7+ use std:: str:: FromStr ;
78use std:: sync:: Arc ;
89
910use resolvo:: utils:: Pool ;
@@ -35,8 +36,9 @@ use spk_schema::ident_build::{Build, EmbeddedSource, EmbeddedSourcePackage};
3536use spk_schema:: ident_component:: Component ;
3637use spk_schema:: name:: { OptNameBuf , PkgNameBuf } ;
3738use spk_schema:: prelude:: { HasVersion , Named } ;
39+ use spk_schema:: version:: Version ;
3840use spk_schema:: version_range:: { DoubleEqualsVersion , VersionFilter } ;
39- use spk_schema:: { Opt , Package , Request , VersionIdent } ;
41+ use spk_schema:: { BuildIdent , Opt , Package , Request , VersionIdent } ;
4042use spk_storage:: RepositoryHandle ;
4143
4244use super :: pkg_request_version_set:: {
@@ -467,21 +469,31 @@ impl DependencyProvider for SpkProvider {
467469 let located_build_ident =
468470 LocatedBuildIdent :: new ( repo. name ( ) . to_owned ( ) , build. clone ( ) ) ;
469471 if let SyntheticComponent :: Actual ( pkg_name_component) = & pkg_name. component {
470- if let Build :: Embedded ( EmbeddedSource :: Package ( parent) ) = build. build ( ) {
471- // Embedded packages don't have components, but
472- // their parent has a component.
473- if parent. components . contains ( pkg_name_component) {
474- located_builds. push ( LocatedBuildIdentWithComponent {
475- ident : located_build_ident,
476- component : SyntheticComponent :: Actual (
477- pkg_name_component. clone ( ) ,
478- ) ,
479- } ) ;
480- }
481- continue ;
482- }
483- let components =
484- repo. list_build_components ( & build) . await . unwrap_or_default ( ) ;
472+ let components = if let Build :: Embedded ( EmbeddedSource :: Package ( _parent) ) =
473+ build. build ( )
474+ {
475+ // Does this embedded stub contain the component
476+ // being requested? For whatever reason,
477+ // list_build_components returns an empty list for
478+ // embedded stubs.
479+ itertools:: Either :: Right (
480+ if let Ok ( stub) = repo. read_embed_stub ( & build) . await {
481+ itertools:: Either :: Right (
482+ stub. components ( )
483+ . iter ( )
484+ . map ( |component_spec| component_spec. name . clone ( ) )
485+ . collect :: < Vec < _ > > ( )
486+ . into_iter ( ) ,
487+ )
488+ } else {
489+ itertools:: Either :: Left ( std:: iter:: empty ( ) )
490+ } ,
491+ )
492+ } else {
493+ itertools:: Either :: Left (
494+ repo. list_build_components ( & build) . await . unwrap_or_default ( ) ,
495+ )
496+ } ;
485497 for component in components. into_iter ( ) . chain (
486498 // A build representing the All component is included so
487499 // when a request for it is found it can act as a
@@ -615,12 +627,15 @@ impl DependencyProvider for SpkProvider {
615627 else {
616628 return Dependencies :: Known ( KnownDependencies :: default ( ) ) ;
617629 } ;
618- if let SyntheticComponent :: Base = located_build_ident_with_component. component {
619- // Base can't depend on anything because we don't know what
620- // components actually exist or if requests exist for whatever it
621- // was we picked if we were to pick a component to depend on.
622- return Dependencies :: Known ( KnownDependencies :: default ( ) ) ;
623- }
630+ let actual_component = match & located_build_ident_with_component. component {
631+ SyntheticComponent :: Base => {
632+ // Base can't depend on anything because we don't know what
633+ // components actually exist or if requests exist for whatever it
634+ // was we picked if we were to pick a component to depend on.
635+ return Dependencies :: Known ( KnownDependencies :: default ( ) ) ;
636+ }
637+ SyntheticComponent :: Actual ( component) => component,
638+ } ;
624639 // XXX: This find runtime will add up.
625640 let repo = self
626641 . repos
@@ -666,9 +681,7 @@ impl DependencyProvider for SpkProvider {
666681 ) ;
667682 }
668683 return Dependencies :: Known ( known_deps) ;
669- } else if let SyntheticComponent :: Actual ( ref solvable_component) =
670- located_build_ident_with_component. component
671- {
684+ } else {
672685 // For any non-All/non-Base component, add a dependency on
673686 // the base to ensure all components come from the same
674687 // base version.
@@ -692,7 +705,7 @@ impl DependencyProvider for SpkProvider {
692705 if let Some ( component_spec) = package
693706 . components ( )
694707 . iter ( )
695- . find ( |component_spec| component_spec. name == * solvable_component )
708+ . find ( |component_spec| component_spec. name == * actual_component )
696709 {
697710 component_spec. uses . iter ( ) . for_each ( |uses| {
698711 let dep_name = self . pool . intern_package_name ( PkgNameBufWithComponent {
@@ -718,8 +731,6 @@ impl DependencyProvider for SpkProvider {
718731 }
719732 // Also add dependencies on any packages embedded in this
720733 // component.
721- // XXX: This ignores the detail of embeds inside components for
722- // now.
723734 for embedded in package. embedded ( ) . iter ( ) {
724735 let dep_name = self . pool . intern_package_name ( PkgNameBufWithComponent {
725736 name : embedded. name ( ) . to_owned ( ) ,
@@ -746,12 +757,11 @@ impl DependencyProvider for SpkProvider {
746757 ) ,
747758 // This needs to match the build of
748759 // the stub for get_candidates to like
749- // it.
760+ // it. Stub parents are always the Run
761+ // component.
750762 build : Some ( Build :: Embedded ( EmbeddedSource :: Package (
751763 Box :: new ( EmbeddedSourcePackage {
752764 ident : package. ident ( ) . into ( ) ,
753- // XXX: Hard coded to Run for
754- // now.
755765 components : BTreeSet :: from_iter ( [ Component :: Run ] ) ,
756766 } ) ,
757767 ) ) ) ,
@@ -777,6 +787,102 @@ impl DependencyProvider for SpkProvider {
777787 todo ! ( "{embedded_component_requirement:?}" ) ;
778788 }
779789 }
790+ // If this solvable is an embedded stub and it is
791+ // representing that it provides a component that lives in a
792+ // component of the parent, then that parent component needs
793+ // to be included in the solution.
794+ if let Build :: Embedded ( EmbeddedSource :: Package ( parent) ) =
795+ located_build_ident_with_component. ident . build ( )
796+ {
797+ match actual_component {
798+ Component :: Run => {
799+ // The Run component is the default "home" of
800+ // embedded packages, no dependency needed in this
801+ // case.
802+ }
803+ component => ' invalid_parent: {
804+ // XXX: Do we not have a convenient way to read the
805+ // parent package from an embedded stub ident?
806+ let Ok ( pkg_name) = PkgNameBuf :: from_str ( & parent. ident . pkg_name ) else {
807+ break ' invalid_parent;
808+ } ;
809+ let Some ( version_str) = parent. ident . version_str . as_ref ( ) else {
810+ break ' invalid_parent;
811+ } ;
812+ let Ok ( version) = Version :: from_str ( & version_str) else {
813+ break ' invalid_parent;
814+ } ;
815+ let Some ( build_str) = parent. ident . build_str . as_ref ( ) else {
816+ break ' invalid_parent;
817+ } ;
818+ let Ok ( build) = Build :: from_str ( build_str) else {
819+ break ' invalid_parent;
820+ } ;
821+ let ident = BuildIdent :: new (
822+ VersionIdent :: new ( pkg_name, version) ,
823+ build. clone ( ) ,
824+ ) ;
825+ let Ok ( parent) = repo. read_package ( & ident) . await else {
826+ break ' invalid_parent;
827+ } ;
828+ // Look through the components of the parent to see
829+ // if one (or more?) of them embeds this component.
830+ for parent_component in parent. components ( ) . iter ( ) {
831+ parent_component
832+ . embedded
833+ . iter ( )
834+ . filter ( |embedded_package| {
835+ embedded_package. pkg . name ( )
836+ == located_build_ident_with_component. ident . name ( )
837+ && embedded_package. components ( ) . contains ( component)
838+ } )
839+ . for_each ( |_embedded_package| {
840+ let dep_name = self . pool . intern_package_name (
841+ PkgNameBufWithComponent {
842+ name : ident. name ( ) . to_owned ( ) ,
843+ component : SyntheticComponent :: Actual (
844+ parent_component. name . clone ( ) ,
845+ ) ,
846+ } ,
847+ ) ;
848+ known_deps. requirements . push (
849+ self . pool . intern_version_set (
850+ dep_name,
851+ RequestVS :: SpkRequest ( Request :: Pkg (
852+ PkgRequest :: new (
853+ RangeIdent {
854+ repository_name : Some (
855+ located_build_ident_with_component
856+ . ident
857+ . repository_name ( )
858+ . to_owned ( ) ,
859+ ) ,
860+ name : ident. name ( ) . to_owned ( ) ,
861+ components : BTreeSet :: from_iter ( [
862+ parent_component. name . clone ( ) ,
863+ ] ) ,
864+ version : VersionFilter :: single (
865+ DoubleEqualsVersion :: version_range (
866+ ident. version ( ) . clone ( ) ,
867+ ) ,
868+ ) ,
869+ build : Some ( build. clone ( ) ) ,
870+ } ,
871+ RequestedBy :: Embedded (
872+ located_build_ident_with_component
873+ . ident
874+ . target ( )
875+ . clone ( ) ,
876+ ) ,
877+ ) ,
878+ ) )
879+ ) . into ( ) ,
880+ ) ;
881+ } ) ;
882+ }
883+ }
884+ }
885+ }
780886 for option in package. get_build_options ( ) {
781887 let Opt :: Var ( var_opt) = option else {
782888 continue ;
0 commit comments