diff --git a/doc/user-changes.md b/doc/user-changes.md index 41fae67aa..8fbb0c257 100644 --- a/doc/user-changes.md +++ b/doc/user-changes.md @@ -25,6 +25,38 @@ file found in the user's home directory). The default behavior is unchanged. +### Abbreviated `--tree` output for repeating dependencies + +PR [#1814](https://github.com/alire-project/alire/pull/1814) + +By default, repeated dependencies are now omitted by `--tree` output, e.g.: + +``` +$ alr show --tree libgpr2 +... +Dependencies (tree): + gnat=14.1.3 (gnat_native) (>=14) + gnatcoll=25.0.0 (~25.0.0) + ├── gnat=14.1.3 (gnat_native) (>=13) + └── libgpr=25.0.0 (~25.0.0) + ├── gnat=14.1.3 (gnat_native) (/=2020) + └── xmlada=25.0.0 (~25.0.0) + └── gnat=14.1.3 (gnat_native) (>=11) + gnatcoll_gmp=25.0.0 (~25.0.0) + ├── gnatcoll=25.0.0 (~25.0.0) + │ └── ... + └── libgmp=6.3.0 (*) + gnatcoll_iconv=25.0.0 (~25.0.0) + └── gnatcoll=25.0.0 (~25.0.0) + └── ... +``` + +Whenever '...' appears, it means that the preceding release has its +dependencies already printed somewhere in the preceding tree lines. + +The old behavior can be obtained by increasing verbosity with the global `-v` +switch. + ### Faster `alr search` without resolving dependencies PR [#1799](https://github.com/alire-project/alire/pull/1799) diff --git a/src/alire/alire-solutions.adb b/src/alire/alire-solutions.adb index 81c7d84a4..0ccba3448 100644 --- a/src/alire/alire-solutions.adb +++ b/src/alire/alire-solutions.adb @@ -916,7 +916,8 @@ package body Alire.Solutions is procedure Print_Tree (This : Solution; Root : Alire.Releases.Release; Prefix : String := ""; - Print_Root : Boolean := True) + Print_Root : Boolean := True; + Concise : Boolean := not Detailed) is Mid_Node : constant String := @@ -926,6 +927,30 @@ package body Alire.Solutions is Branch : constant String := (if TTY.Color_Enabled then U ("│ ") else "| "); No_Branch : constant String := " "; + More_Deps : constant String := (Last_Node & "..."); + + Printed : AAA.Strings.Sets.Set; + -- Dependencies already printed, to avoid reprinting in Concise mode + + ----------- + -- Label -- + ----------- + -- The dependency Milestone or State + function Label (Dep : Dependencies.Dependency) return String + is (if This.State (Dep.Crate).Has_Release + then This.State (Dep.Crate).Milestone_Image + else This.State (Dep.Crate).TTY_Image); + + --------------------- + -- Already_Printed -- + --------------------- + + function Already_Printed (Dep : Dependencies.Dependency) return Boolean + is (Printed.Contains (Label (Dep))); + + ----------- + -- Print -- + ----------- procedure Print (Deps : Dependencies.Containers.Set; Prefix : String := ""; @@ -933,9 +958,31 @@ package body Alire.Solutions is -- Omit is used to remove the top-level connectors, for when the tree -- is printed without the root release. is + + ---------------------- + -- Has_Dependencies -- + ---------------------- + + function Has_Dependencies (Dep : Dependencies.Dependency) + return Boolean + is (This.State (Dep.Crate).Has_Release + and then not Conditional.Enumerate + (This.State (Dep.Crate).Release.Dependencies).Is_Empty); + Last : UString; -- Used to store the last dependency name in a subtree, to be able to -- use the proper ASCII connector. See just below. + + ---------------------- + -- Parent_Connector -- + ---------------------- + + function Parent_Connector (Dep : Dependencies.Dependency) + return String + is (if +Dep.Crate = +Last + then No_Branch -- End of this connector + else Branch); -- Continuation + begin -- Find last printable dependency. This is related to OR trees, that @@ -966,27 +1013,37 @@ package body Alire.Solutions is -- For a dependency solved by a release, print exact -- version. Otherwise print the state of the dependency. - & (if This.State (Dep.Crate).Has_Release - then This.State (Dep.Crate).Milestone_Image - else This.State (Dep.Crate).TTY_Image) + & Label (Dep) -- And dependency that introduces the crate in the solution & " (" & TTY.Emph (Dep.Versions.Image) & ")"); - -- Recurse for further releases - - if This.State (Dep.Crate).Has_Release then - Print (Conditional.Enumerate - (This.State (Dep.Crate).Release.Dependencies).To_Set, - Prefix => - Prefix - -- Indent adding the proper running connector - & (if Omit - then "" - else (if +Dep.Crate = +Last - then No_Branch -- End of this connector - else Branch))); -- "│" over the subtree + -- Recurse for further releases, or print the concise marker + + if + Concise + and then Already_Printed (Dep) + and then Has_Dependencies (Dep) + then + Trace.Always (Prefix + & Parent_Connector (Dep) + & More_Deps); + elsif + (not Concise or else not Printed.Contains (Label (Dep))) + and then Has_Dependencies (Dep) + then + Print + (Conditional.Enumerate + (This.State (Dep.Crate).Release.Dependencies).To_Set, + Prefix => + Prefix + -- Indent adding the proper running connector + & (if Omit + then "" + else Parent_Connector (Dep))); end if; + + Printed.Include (Label (Dep)); end if; end loop; end Print; diff --git a/src/alire/alire-solutions.ads b/src/alire/alire-solutions.ads index 43cad5c12..76c7ac9ba 100644 --- a/src/alire/alire-solutions.ads +++ b/src/alire/alire-solutions.ads @@ -412,9 +412,11 @@ package Alire.Solutions is procedure Print_Tree (This : Solution; Root : Alire.Releases.Release; Prefix : String := ""; - Print_Root : Boolean := True); + Print_Root : Boolean := True; + Concise : Boolean := not Detailed); -- Print the solution in tree form. If Print_Root, Root is printed too; -- otherwise the tree is a forest starting at Root direct dependencies. + -- If Concise, print each unique dependency only once. procedure Print_Versions (This : Solution; Root : Roots.Root); diff --git a/testsuite/tests/with/tree-concise/test.py b/testsuite/tests/with/tree-concise/test.py new file mode 100644 index 000000000..c08a3fb6e --- /dev/null +++ b/testsuite/tests/with/tree-concise/test.py @@ -0,0 +1,43 @@ +""" +Test the concise mode of the --tree switch. Repeating dependencies are +substituted by a "..." ellipsis. +""" + +import os +import re +from drivers.alr import run_alr, alr_with, init_local_crate +from drivers.asserts import assert_eq, assert_match + +# Prepare a crate in which dependencies appear twice, so their dependencies in +# turn can be elided. + +init_local_crate("yyy") +alr_with("hello") +os.chdir("..") +init_local_crate("xxx") +alr_with("hello") +alr_with("yyy", path="../yyy") + +# Check the concise tree +assert_eq("""\ +xxx=0.1.0-dev ++-- hello=1.0.1 (*) +| +-- libhello=1.0.0 (^1.0) ++-- yyy=0.1.0-dev (*) + +-- hello=1.0.1 (*) + +-- ...\ +""", + run_alr("with", "--tree").out.strip()) + +# Check the regular tree +assert_match(".*" + re.escape("""\ +xxx=0.1.0-dev ++-- hello=1.0.1 (*) +| +-- libhello=1.0.0 (^1.0) ++-- yyy=0.1.0-dev (*) + +-- hello=1.0.1 (*) + +-- libhello=1.0.0 (^1.0)\ +"""), + run_alr("-v", "with", "--tree", quiet=False).out.strip()) + +print("SUCCESS") diff --git a/testsuite/tests/with/tree-concise/test.yaml b/testsuite/tests/with/tree-concise/test.yaml new file mode 100644 index 000000000..c34d695d0 --- /dev/null +++ b/testsuite/tests/with/tree-concise/test.yaml @@ -0,0 +1,5 @@ +driver: python-script +build_mode: both +indexes: + basic_index: + in_fixtures: true