11use  std:: borrow:: Cow ; 
22use  std:: collections:: { BTreeSet ,  VecDeque } ; 
3+ use  std:: path:: Path ; 
34
45use  itertools:: Itertools ; 
6+ use  petgraph:: graph:: { EdgeIndex ,  NodeIndex } ; 
57use  petgraph:: prelude:: EdgeRef ; 
6- use  petgraph:: visit:: Dfs ; 
78use  petgraph:: Direction ; 
89use  rustc_hash:: { FxHashMap ,  FxHashSet } ; 
910
1011use  uv_configuration:: DevGroupsManifest ; 
1112use  uv_normalize:: { ExtraName ,  GroupName ,  PackageName } ; 
1213use  uv_pypi_types:: ResolverMarkerEnvironment ; 
1314
14- use  crate :: lock:: { Dependency ,  PackageId } ; 
15+ use  crate :: lock:: { Dependency ,  PackageId ,   Source } ; 
1516use  crate :: Lock ; 
1617
1718#[ derive( Debug ) ]  
@@ -24,6 +25,8 @@ pub struct TreeDisplay<'env> {
2425depth :  usize , 
2526    /// Whether to de-duplicate the displayed dependencies. 
2627no_dedupe :  bool , 
28+     /// The packages considered as roots of the dependency tree. 
29+ roots :  Vec < NodeIndex > , 
2730} 
2831
2932impl < ' env >  TreeDisplay < ' env >  { 
@@ -38,6 +41,38 @@ impl<'env> TreeDisplay<'env> {
3841        no_dedupe :  bool , 
3942        invert :  bool , 
4043    )  -> Self  { 
44+         // Identify the workspace members. 
45+         // 
46+         // The members are encoded directly in the lockfile, unless the workspace contains a 
47+         // single member at the root, in which case, we identify it by its source. 
48+         let  members:  FxHashSet < & PackageId >  = if  lock. members ( ) . is_empty ( )  { 
49+             lock. packages 
50+                 . iter ( ) 
51+                 . filter_map ( |package| { 
52+                     let  ( Source :: Editable ( path)  | Source :: Virtual ( path) )  = & package. id . source 
53+                     else  { 
54+                         return  None ; 
55+                     } ; 
56+                     if  path == Path :: new ( "" )  { 
57+                         Some ( & package. id ) 
58+                     }  else  { 
59+                         None 
60+                     } 
61+                 } ) 
62+                 . collect ( ) 
63+         }  else  { 
64+             lock. packages 
65+                 . iter ( ) 
66+                 . filter_map ( |package| { 
67+                     if  lock. members ( ) . contains ( & package. id . name )  { 
68+                         Some ( & package. id ) 
69+                     }  else  { 
70+                         None 
71+                     } 
72+                 } ) 
73+                 . collect ( ) 
74+         } ; 
75+ 
4176        // Create a graph. 
4277        let  mut  graph = petgraph:: graph:: Graph :: < & PackageId ,  Edge ,  petgraph:: Directed > :: new ( ) ; 
4378
@@ -136,29 +171,24 @@ impl<'env> TreeDisplay<'env> {
136171
137172        // Step 1: Filter out packages that aren't reachable on this platform. 
138173        if  let  Some ( environment_markers)  = markers { 
139-             let  mut  reachable = FxHashSet :: default ( ) ; 
140- 
141174            // Perform a DFS from the root nodes to find the reachable nodes, following only the 
142175            // production edges. 
143-             let  mut  stack  = graph
176+             let  mut  reachable  = graph
144177                . node_indices ( ) 
145-                 . filter ( |index| { 
146-                     graph
147-                         . edges_directed ( * index,  Direction :: Incoming ) 
148-                         . next ( ) 
149-                         . is_none ( ) 
150-                 } ) 
151-                 . collect :: < VecDeque < _ > > ( ) ; 
178+                 . filter ( |index| members. contains ( graph[ * index] ) ) 
179+                 . collect :: < FxHashSet < _ > > ( ) ; 
180+             let  mut  stack = reachable. iter ( ) . copied ( ) . collect :: < VecDeque < _ > > ( ) ; 
152181            while  let  Some ( node)  = stack. pop_front ( )  { 
153-                 reachable. insert ( node) ; 
154182                for  edge in  graph. edges_directed ( node,  Direction :: Outgoing )  { 
155183                    if  edge
156184                        . weight ( ) 
157185                        . dependency ( ) 
158186                        . complexified_marker 
159187                        . evaluate ( environment_markers,  & [ ] ) 
160188                    { 
161-                         stack. push_back ( edge. target ( ) ) ; 
189+                         if  reachable. insert ( edge. target ( ) )  { 
190+                             stack. push_back ( edge. target ( ) ) ; 
191+                         } 
162192                    } 
163193                } 
164194            } 
@@ -167,32 +197,26 @@ impl<'env> TreeDisplay<'env> {
167197            graph. retain_nodes ( |_,  index| reachable. contains ( & index) ) ; 
168198        } 
169199
170-         // Step 2: Filter the graph to those that are reachable in production or development, if 
171-         // `--no-dev` or `--only-dev` were specified, respectively. 
200+         // Step 2: Filter the graph to those that are reachable in production or development. 
172201        { 
173-             let  mut  reachable = FxHashSet :: default ( ) ; 
174- 
175202            // Perform a DFS from the root nodes to find the reachable nodes, following only the 
176203            // production edges. 
177-             let  mut  stack  = graph
204+             let  mut  reachable  = graph
178205                . node_indices ( ) 
179-                 . filter ( |index| { 
180-                     graph
181-                         . edges_directed ( * index,  Direction :: Incoming ) 
182-                         . next ( ) 
183-                         . is_none ( ) 
184-                 } ) 
185-                 . collect :: < VecDeque < _ > > ( ) ; 
206+                 . filter ( |index| members. contains ( graph[ * index] ) ) 
207+                 . collect :: < FxHashSet < _ > > ( ) ; 
208+             let  mut  stack = reachable. iter ( ) . copied ( ) . collect :: < VecDeque < _ > > ( ) ; 
186209            while  let  Some ( node)  = stack. pop_front ( )  { 
187-                 reachable. insert ( node) ; 
188210                for  edge in  graph. edges_directed ( node,  Direction :: Outgoing )  { 
189211                    let  include = match  edge. weight ( )  { 
190212                        Edge :: Prod ( _)  => dev. prod ( ) , 
191213                        Edge :: Optional ( _,  _)  => dev. prod ( ) , 
192214                        Edge :: Dev ( group,  _)  => dev. iter ( ) . contains ( * group) , 
193215                    } ; 
194216                    if  include { 
195-                         stack. push_back ( edge. target ( ) ) ; 
217+                         if  reachable. insert ( edge. target ( ) )  { 
218+                             stack. push_back ( edge. target ( ) ) ; 
219+                         } 
196220                    } 
197221                } 
198222            } 
@@ -208,24 +232,62 @@ impl<'env> TreeDisplay<'env> {
208232
209233        // Step 4: Filter the graph to those nodes reachable from the target packages. 
210234        if  !packages. is_empty ( )  { 
211-             let  mut  reachable = FxHashSet :: default ( ) ; 
212- 
213235            // Perform a DFS from the root nodes to find the reachable nodes. 
214-             let  mut  dfs = Dfs  { 
215-                 stack :  graph
216-                     . node_indices ( ) 
217-                     . filter ( |index| packages. contains ( & graph[ * index] . name ) ) 
218-                     . collect :: < Vec < _ > > ( ) , 
219-                 ..Dfs :: empty ( & graph) 
220-             } ; 
221-             while  let  Some ( node)  = dfs. next ( & graph)  { 
222-                 reachable. insert ( node) ; 
236+             let  mut  reachable = graph
237+                 . node_indices ( ) 
238+                 . filter ( |index| packages. contains ( & graph[ * index] . name ) ) 
239+                 . collect :: < FxHashSet < _ > > ( ) ; 
240+             let  mut  stack = reachable. iter ( ) . copied ( ) . collect :: < VecDeque < _ > > ( ) ; 
241+             while  let  Some ( node)  = stack. pop_front ( )  { 
242+                 for  edge in  graph. edges_directed ( node,  Direction :: Outgoing )  { 
243+                     if  reachable. insert ( edge. target ( ) )  { 
244+                         stack. push_back ( edge. target ( ) ) ; 
245+                     } 
246+                 } 
223247            } 
224248
225249            // Remove the unreachable nodes from the graph. 
226250            graph. retain_nodes ( |_,  index| reachable. contains ( & index) ) ; 
227251        } 
228252
253+         // Compute the list of roots. 
254+         let  roots = { 
255+             let  mut  edges = vec ! [ ] ; 
256+ 
257+             // Remove any cycles. 
258+             let  feedback_set:  Vec < EdgeIndex >  = petgraph:: algo:: greedy_feedback_arc_set ( & graph) 
259+                 . map ( |e| e. id ( ) ) 
260+                 . collect ( ) ; 
261+             for  edge_id in  feedback_set { 
262+                 if  let  Some ( ( source,  target) )  = graph. edge_endpoints ( edge_id)  { 
263+                     if  let  Some ( weight)  = graph. remove_edge ( edge_id)  { 
264+                         edges. push ( ( source,  target,  weight) ) ; 
265+                     } 
266+                 } 
267+             } 
268+ 
269+             // Find the root nodes. 
270+             let  mut  roots = graph
271+                 . node_indices ( ) 
272+                 . filter ( |index| { 
273+                     graph
274+                         . edges_directed ( * index,  Direction :: Incoming ) 
275+                         . next ( ) 
276+                         . is_none ( ) 
277+                 } ) 
278+                 . collect :: < Vec < _ > > ( ) ; 
279+ 
280+             // Sort the roots. 
281+             roots. sort_by_key ( |index| & graph[ * index] ) ; 
282+ 
283+             // Re-add the removed edges. 
284+             for  ( source,  target,  weight)  in  edges { 
285+                 graph. add_edge ( source,  target,  weight) ; 
286+             } 
287+ 
288+             roots
289+         } ; 
290+ 
229291        // Re-create the inverse map. 
230292        { 
231293            inverse. clear ( ) ; 
@@ -239,6 +301,7 @@ impl<'env> TreeDisplay<'env> {
239301            inverse, 
240302            depth, 
241303            no_dedupe, 
304+             roots, 
242305        } 
243306    } 
244307
@@ -355,24 +418,9 @@ impl<'env> TreeDisplay<'env> {
355418        let  mut  path = Vec :: new ( ) ; 
356419        let  mut  lines = Vec :: new ( ) ; 
357420
358-         let  roots = { 
359-             let  mut  roots = self 
360-                 . graph 
361-                 . node_indices ( ) 
362-                 . filter ( |index| { 
363-                     self . graph 
364-                         . edges_directed ( * index,  petgraph:: Direction :: Incoming ) 
365-                         . next ( ) 
366-                         . is_none ( ) 
367-                 } ) 
368-                 . collect :: < Vec < _ > > ( ) ; 
369-             roots. sort_by_key ( |index| & self . graph [ * index] ) ; 
370-             roots
371-         } ; 
372- 
373-         for  node in  roots { 
421+         for  node in  & self . roots  { 
374422            path. clear ( ) ; 
375-             lines. extend ( self . visit ( Node :: Root ( self . graph [ node] ) ,  & mut  visited,  & mut  path) ) ; 
423+             lines. extend ( self . visit ( Node :: Root ( self . graph [ * node] ) ,  & mut  visited,  & mut  path) ) ; 
376424        } 
377425
378426        lines
0 commit comments