From 5a54894552803eec8b0b4b8fcafd7a2690f82a39 Mon Sep 17 00:00:00 2001 From: Alejandro R Mosteo Date: Fri, 15 May 2020 19:11:13 +0200 Subject: [PATCH] Show complete solution with `alr with --solve` (#408) * Show complete solution via `alr with --solve` * Test: use `with --solve` in test, output fixes Output fixes are due to minor extra information being added to `Solution.Print`: pinned releases are flagged as such. --- .../alire-dependencies-graphs.adb} | 32 +++---- .../alire-dependencies-graphs.ads} | 14 +-- src/alire/alire-paths.ads | 3 + src/alire/alire-solutions-diffs.adb | 2 +- src/alire/alire-solutions.adb | 88 ++++++++++++++++++- src/alire/alire-solutions.ads | 10 +++ src/alr/alr-checkout.adb | 7 +- src/alr/alr-commands-show.adb | 81 +---------------- src/alr/alr-commands-withing.adb | 57 +++++++++--- src/alr/alr-commands-withing.ads | 5 +- src/alr/alr-paths.ads | 3 - testsuite/tests/pin/downgrade/test.py | 9 +- testsuite/tests/pin/post-update/test.py | 10 +-- testsuite/tests/pin/unneeded-held/test.py | 7 +- 14 files changed, 196 insertions(+), 132 deletions(-) rename src/{alr/alr-dependency_graphs.adb => alire/alire-dependencies-graphs.adb} (84%) rename src/{alr/alr-dependency_graphs.ads => alire/alire-dependencies-graphs.ads} (84%) diff --git a/src/alr/alr-dependency_graphs.adb b/src/alire/alire-dependencies-graphs.adb similarity index 84% rename from src/alr/alr-dependency_graphs.adb rename to src/alire/alire-dependencies-graphs.adb index e82e7250..2c5cb061 100644 --- a/src/alr/alr-dependency_graphs.adb +++ b/src/alire/alire-dependencies-graphs.adb @@ -1,13 +1,10 @@ with Alire.Conditional; +with Alire.Directories; +with Alire.OS_Lib.Subprocess; +with Alire.Paths; with Alire.Utils.Tables; -with Alr.OS_Lib; -with Alr.Paths; -with Alr.Platform; -with Alr.Utils; -with Alr.Utils.Temp_File; - -package body Alr.Dependency_Graphs is +package body Alire.Dependencies.Graphs is ----------------- -- Empty_Graph -- @@ -20,11 +17,12 @@ package body Alr.Dependency_Graphs is -- From_Instance -- ------------------- - function From_Solution (Sol : Alire.Solver.Solution) return Graph is + function From_Solution (Sol : Solutions.Solution; + Env : Properties.Vector) return Graph is begin return Result : Graph do for Rel of Sol.Releases loop - Result := Result.Including (Rel); + Result := Result.Including (Rel, Env); end loop; Result := Result.Filtering_Unused (Sol.Releases); @@ -36,7 +34,8 @@ package body Alr.Dependency_Graphs is --------------- function Including (This : Graph; - R : Types.Release) + R : Releases.Release; + Env : Properties.Vector) return Graph is @@ -46,7 +45,7 @@ package body Alr.Dependency_Graphs is begin return Result : Graph := This do - for Dep of Enumerate (R.Dependencies.Evaluate (Platform.Properties)) + for Dep of Enumerate (R.Dependencies.Evaluate (Env)) loop Result.Include (New_Dependency (R.Name, Dep.Crate)); end loop; @@ -118,11 +117,12 @@ package body Alr.Dependency_Graphs is Alt.Append (" }"); declare - Tmp : constant Utils.Temp_File.File := Utils.Temp_File.New_File; + Tmp : Directories.Temp_File; begin - Source.Write (Tmp.Name, Separator => " "); - OS_Lib.Spawn_Raw (Paths.Scripts_Graph_Easy, "--as=boxart " & - Tmp.Name); + Source.Write (Tmp.Filename, Separator => " "); + OS_Lib.Subprocess.Checked_Spawn + (Paths.Scripts_Graph_Easy, + Utils.Empty_Vector.Append ("--as=boxart").Append (Tmp.Filename)); end; end Plot; @@ -164,4 +164,4 @@ package body Alr.Dependency_Graphs is end return; end Removing_Dependee; -end Alr.Dependency_Graphs; +end Alire.Dependencies.Graphs; diff --git a/src/alr/alr-dependency_graphs.ads b/src/alire/alire-dependencies-graphs.ads similarity index 84% rename from src/alr/alr-dependency_graphs.ads rename to src/alire/alire-dependencies-graphs.ads index fff65153..bb57b56f 100644 --- a/src/alr/alr-dependency_graphs.ads +++ b/src/alire/alire-dependencies-graphs.ads @@ -1,19 +1,23 @@ with Ada.Containers.Indefinite_Ordered_Sets; with Alire.Containers; -with Alire.Solver; +with Alire.Properties; +with Alire.Releases; +with Alire.Solutions; -package Alr.Dependency_Graphs is +package Alire.Dependencies.Graphs is type Graph is tagged private; function Empty_Graph return Graph; - function From_Solution (Sol : Alire.Solver.Solution) + function From_Solution (Sol : Solutions.Solution; + Env : Properties.Vector) return Graph; function Including (This : Graph; - R : Types.Release) return Graph; + R : Releases.Release; + Env : Properties.Vector) return Graph; -- Add a release and ALL its potential direct dependencies (even OR'ed) function Filtering_Unused (This : Graph; @@ -60,4 +64,4 @@ private type Graph is new Dep_Sets.Set with null record; -end Alr.Dependency_Graphs; +end Alire.Dependencies.Graphs; diff --git a/src/alire/alire-paths.ads b/src/alire/alire-paths.ads index 6a9895ba..a12af064 100644 --- a/src/alire/alire-paths.ads +++ b/src/alire/alire-paths.ads @@ -11,6 +11,9 @@ package Alire.Paths with Preelaborate is function Build_Folder return Relative_Path; -- The folder where the out-of-tree global build is performed + Scripts_Graph_Easy : constant String := "graph-easy"; + -- Script for ASCII graphs + private Crate_File_Extension_With_Dot : constant String := ".toml"; diff --git a/src/alire/alire-solutions-diffs.adb b/src/alire/alire-solutions-diffs.adb index 0134f0f3..089a530d 100644 --- a/src/alire/alire-solutions-diffs.adb +++ b/src/alire/alire-solutions-diffs.adb @@ -172,7 +172,7 @@ package body Alire.Solutions.Diffs is -- Early exit if no changes - if not This.Contains_Changes then + if Changed_Only and then not This.Contains_Changes then Trace.Log (Prefix & "No changes between former an new solution.", Level); return; diff --git a/src/alire/alire-solutions.adb b/src/alire/alire-solutions.adb index 266678eb..79406c7b 100644 --- a/src/alire/alire-solutions.adb +++ b/src/alire/alire-solutions.adb @@ -1,9 +1,13 @@ with Alire.Crates.With_Releases; -with Alire.Dependencies; -with Alire.Releases; +with Alire.Dependencies.Graphs; +with Alire.Index; +with Alire.OS_Lib.Subprocess; +with Alire.Paths; with Alire.Solutions.Diffs; with Alire.Utils.Tables; +with Semantic_Versioning; + package body Alire.Solutions is ------------- @@ -31,6 +35,14 @@ package body Alire.Solutions is end return; end Changing_Pin; + ---------------------------------- + -- Libgraph_Easy_Perl_Installed -- + ---------------------------------- + + function Libgraph_Easy_Perl_Installed return Boolean + is (OS_Lib.Subprocess.Locate_In_Path (Paths.Scripts_Graph_Easy) /= ""); + -- Return whether libgraph_easy_perl_install is in path + ---------- -- Pins -- ---------- @@ -54,6 +66,78 @@ package body Alire.Solutions is end return; end Pins; + ----------- + -- Print -- + ----------- + + procedure Print (This : Solution; + Root : Alire.Releases.Release; + Env : Properties.Vector; + Detailed : Boolean; + Level : Trace.Levels) is + begin + if not This.Releases.Is_Empty then + Trace.Log ("Dependencies (solution):", Level); + for Rel of This.Releases loop + Trace.Log (" " & Rel.Milestone.Image + & (if Rel.Is_Pinned + then " (pinned)" + else "") + & (if Detailed then + " (origin: " & + Utils.To_Lower_Case (Rel.Origin.Kind'Img) & ")" + else + ""), + Level); + end loop; + end if; + + -- Show unresolved hints, with their hinting message + + if not This.Hints.Is_Empty then + Trace.Log ("Dependencies (external):", Level); + for Dep of This.Hints loop + Trace.Log (" " & Dep.Image, Level); + + -- Look for hints. If we are relying on workspace + -- information the index may not be loaded, or have + -- changed, so we need to ensure the crate is indexed. + + if Index.Exists (Dep.Crate) then + for Hint of + Alire.Index.Crate (Dep.Crate) + .Externals.Hints + (Name => Dep.Crate, + Env => Alire.Properties.No_Properties) + loop + Trace.Log (" Hint: " & Hint, Level); + end loop; + end if; + end loop; + end if; + + if not (This.Releases.Is_Empty and then This.Hints.Is_Empty) + then + Trace.Log ("Dependencies (graph):", Level); + declare + Graph : constant Dependencies.Graphs.Graph := + Dependencies.Graphs.From_Solution (This, Env) + .Including (Root, Env); + begin + Graph.Print (This.Releases.Including (Root), Prefix => " "); + + if Libgraph_Easy_Perl_Installed then + Graph.Plot (This.Releases.Including (Root)); + else + Trace.Log ("Cannot display graphical graph: " & + Paths.Scripts_Graph_Easy & " not in path" & + " (usually packaged as libgraph_easy_perl).", + Level); + end if; + end; + end if; + end Print; + ---------------- -- Print_Pins -- ---------------- diff --git a/src/alire/alire-solutions.ads b/src/alire/alire-solutions.ads index ff0c1251..8acdd4c9 100644 --- a/src/alire/alire-solutions.ads +++ b/src/alire/alire-solutions.ads @@ -2,6 +2,7 @@ with Alire.Conditional; with Alire.Containers; with Alire.Interfaces; with Alire.Properties; +with Alire.Releases; with Alire.TOML_Adapters; limited with Alire.Solutions.Diffs; @@ -59,6 +60,15 @@ package Alire.Solutions is function With_Pins (This, Src : Solution) return Solution; -- Copy pins from Src to This + procedure Print (This : Solution; + Root : Alire.Releases.Release; + Env : Properties.Vector; + Detailed : Boolean; + Level : Trace.Levels); + -- Prints releases, and direct and transitive dependencies. Root is the + -- crate not in solution that introduces the direct dependencies. When + -- Detailed, extra information about origins is shown. + procedure Print_Pins (This : Solution); -- Dump a table with pins in this solution diff --git a/src/alr/alr-checkout.adb b/src/alr/alr-checkout.adb index af1223c1..0a0bc07d 100644 --- a/src/alr/alr-checkout.adb +++ b/src/alr/alr-checkout.adb @@ -3,6 +3,7 @@ with Ada.Directories; with Alire; with Alire.Actions; with Alire.Containers; +with Alire.Dependencies.Graphs; with Alire.Externals.Lists; with Alire.Lockfiles; with Alire.Origins.Deployers; @@ -10,13 +11,14 @@ with Alire.Solutions; with Alire.Roots; with Alr.Actions; -with Alr.Dependency_Graphs; with Alr.OS_Lib; with Alr.Platform; with Alr.Templates; package body Alr.Checkout is + package Dependency_Graphs renames Alire.Dependencies.Graphs; + -------------- -- Checkout -- -------------- @@ -71,7 +73,8 @@ package body Alr.Checkout is is Was_There : Boolean; Graph : Dependency_Graphs.Graph := - Dependency_Graphs.From_Solution (Solution); + Dependency_Graphs.From_Solution (Solution, + Platform.Properties); Pending : Alire.Solutions.Release_Map := Solution.Releases; Round : Natural := 0; begin diff --git a/src/alr/alr-commands-show.adb b/src/alr/alr-commands-show.adb index 9996661c..408aa362 100644 --- a/src/alr/alr-commands-show.adb +++ b/src/alr/alr-commands-show.adb @@ -1,7 +1,6 @@ with Alire.Index; with Alire.Milestones; with Alire.Origins.Deployers; -with Alire.OS_Lib.Subprocess; with Alire.Platform; with Alire.Platforms; with Alire.Properties; @@ -12,8 +11,6 @@ with Alire.Solver; with Alire.Utils.Tables; with Alr.Bootstrap; -with Alr.Dependency_Graphs; -with Alr.Paths; with Alr.Platform; with Alr.Root; @@ -24,15 +21,6 @@ package body Alr.Commands.Show is package Query renames Alire.Solver; package Semver renames Semantic_Versioning; - ---------------------------------- - -- Libgraph_Easy_Perl_Installed -- - ---------------------------------- - - function Libgraph_Easy_Perl_Installed return Boolean is - -- Return whether the rolling version of libgraph_easy_perl_install is - -- installed. - (Alire.OS_Lib.Subprocess.Locate_In_Path (Paths.Scripts_Graph_Easy) /= ""); - ------------ -- Report -- ------------ @@ -82,71 +70,10 @@ package body Alr.Commands.Show is Hinting => <>))); begin if Needed.Valid then - - -- Show regular dependencies in solution. When requested, - -- show also their origin kind. This is useful for crate - -- testing to let us know that we need to update system - -- repositories. It also raises awareness about the - -- provenance of sources. - - if not Needed.Releases.Is_Empty then - Put_Line ("Dependencies (solution):"); - for Rel of Needed.Releases loop - Put_Line (" " & Rel.Milestone.Image - & (if Cmd.Detail - then " (origin: " - & Utils.To_Lower_Case - (Rel.Origin.Kind'Img) & ")" - else "")); - end loop; - end if; - - -- Show unresolved hints, with their hinting message - - if not Needed.Hints.Is_Empty then - Put_Line ("Dependencies (external):"); - for Dep of Needed.Hints loop - Put_Line (" " & Dep.Image); - - -- Look for hints. If we are relying on workspace - -- information the index may not be loaded, or have - -- changed, so we need to ensure the crate is indexed. - if Alire.Index.Exists (Dep.Crate) then - for Hint of - Alire.Index.Crate (Dep.Crate) - .Externals.Hints - (Name => Dep.Crate, - Env => - (if Cmd.System - then Platform.Properties - else Alire.Properties.No_Properties)) - loop - Trace.Info (" Hint: " & Hint); - end loop; - end if; - end loop; - end if; - - if not (Needed.Releases.Is_Empty and then - Needed.Hints.Is_Empty) - then - Put_Line ("Dependencies (graph):"); - declare - Graph : constant Dependency_Graphs.Graph := - Dependency_Graphs - .From_Solution (Needed) - .Including (Rel); - begin - if Libgraph_Easy_Perl_Installed then -- plot - Graph.Plot - (Needed.Releases.Including (Rel)); - else -- textual - Graph.Print - (Needed.Releases.Including (Rel), - Prefix => " "); - end if; - end; - end if; + Needed.Print (Rel, + Platform.Properties, + Cmd.Detail, + Always); else Put_Line ("Dependencies cannot be met"); end if; diff --git a/src/alr/alr-commands-withing.adb b/src/alr/alr-commands-withing.adb index fa2e0c79..a9406b58 100644 --- a/src/alr/alr-commands-withing.adb +++ b/src/alr/alr-commands-withing.adb @@ -6,6 +6,7 @@ with Ada.Text_IO; with Alire.Conditional; with Alire.Dependencies.Diffs; with Alire.Milestones; +with Alire.Releases; with Alire.Roots; with Alire.Solutions; with Alire.Solver; @@ -308,9 +309,20 @@ package body Alr.Commands.Withing is -- List -- ---------- - procedure List is + procedure List (Cmd : Command) is + Root_Release : constant Alire.Releases.Release := Root.Current.Release; begin - Root.Current.Release.Dependencies (Platform.Properties).Print; + Put_Line ("Dependencies (direct):"); + Root_Release.Dependencies.Print (" ", + Root_Release.Dependencies.Contains_ORs); + + if Cmd.Solve then + Requires_Full_Index; -- Load possible hints + Root.Current.Solution.Print (Root_Release, + Platform.Properties, + Detailed => True, + Level => Always); + end if; end List; ------------- @@ -318,20 +330,33 @@ package body Alr.Commands.Withing is ------------- overriding procedure Execute (Cmd : in out Command) is + Flags : Natural := 0; + + procedure Check (Flag : Boolean) is + begin + if Flag then + Flags := Flags + 1; + end if; + + if Flags > 1 then + Reportaise_Wrong_Arguments + ("Only one simultaneous switch allowed."); + end if; + end Check; + begin Requires_Valid_Session; + Check (Cmd.Del); + Check (Cmd.From); + Check (Cmd.Solve); + -- No parameters: give current platform dependencies and BAIL OUT if not (Cmd.Del or else Cmd.From) and then Num_Arguments = 0 then - List; + List (Cmd); return; end if; - if Cmd.Del and Cmd.From then - Reportaise_Wrong_Arguments - ("Simultaneous --del, --from are incompatible"); - end if; - if Num_Arguments < 1 then if Cmd.Del then Reportaise_Wrong_Arguments ("At least one dependency required"); @@ -367,14 +392,19 @@ package body Alr.Commands.Withing is function Long_Description (Cmd : Command) return Alire.Utils.String_Vector is (Alire.Utils.Empty_Vector - .Append ("Manages dependencies of the current crate or sandbox.") + .Append ("Inspect and manage dependencies.") .New_Line - .Append ("* From the command line:") + .Append ("* Inspecting dependencies:") + .Append ("Run without arguments prints current dependencies. Use" + & " --solve to print the solution in use for these" + & " dependencies.") + .New_Line + .Append ("* Adding dependencies from the command line:") .Append ("Dependencies are added by giving their name, and removed" & " by using the --del flag. Dependencies cannot be" & " simultaneously added and removed in a single invocation.") .New_Line - .Append ("* From a GPR file:") + .Append ("* Adding dependencies from a GPR file:") .Append ("The project file given with --from will be scanned looking" & " for comments that contain the sequence 'alr with'. " & " These will be processed individually as if they had been" @@ -414,6 +444,11 @@ package body Alr.Commands.Withing is Cmd.From'Access, "", "--from", "Use dependencies declared within GPR project file"); + + Define_Switch (Config, + Cmd.Solve'Access, + "", "--solve", + "Show complete solution to dependencies"); end Setup_Switches; end Alr.Commands.Withing; diff --git a/src/alr/alr-commands-withing.ads b/src/alr/alr-commands-withing.ads index 1cf63119..3af64b0d 100644 --- a/src/alr/alr-commands-withing.ads +++ b/src/alr/alr-commands-withing.ads @@ -21,8 +21,9 @@ package Alr.Commands.Withing is private type Command is new Commands.Command with record - Del : aliased Boolean := False; - From : aliased Boolean := False; + Del : aliased Boolean := False; + From : aliased Boolean := False; + Solve : aliased Boolean := False; end record; end Alr.Commands.Withing; diff --git a/src/alr/alr-paths.ads b/src/alr/alr-paths.ads index 8aca870d..5ea553fb 100644 --- a/src/alr/alr-paths.ads +++ b/src/alr/alr-paths.ads @@ -66,9 +66,6 @@ package Alr.Paths is function Dependencies_Folder return Relative_Path; -- Location in the working dir where dependencies are deployed - -- Scripts paths/names - Scripts_Graph_Easy : constant String := "graph-easy"; - private function "/" (L, R : String) return String diff --git a/testsuite/tests/pin/downgrade/test.py b/testsuite/tests/pin/downgrade/test.py index 8ec6a266..2e7398b4 100644 --- a/testsuite/tests/pin/downgrade/test.py +++ b/testsuite/tests/pin/downgrade/test.py @@ -13,12 +13,11 @@ import re # Verify that proper version of libchild is in the printed and disk solution -def check_child(version, output): +def check_child(version, output, pinned): # Verify output assert_match('.*\n' 'Dependencies \(solution\):\n' - ' libchild=' + version + '\n' - '.*\n', + ' libchild=' + version + (" \(pinned\)" if pinned else "") + '.*\n', output, flags=re.S) # Verify lockfile @@ -37,14 +36,14 @@ os.chdir('xxx') # Make it depend on child (there are 0.1 and 0.2, so 0.2 used initially) run_alr('with', 'libchild') p = run_alr('show', '--solve') -check_child('0.2.0', p.out) +check_child('0.2.0', p.out, pinned=False) # Pin it to a downgrade run_alr('pin', 'libchild=0.1') # Verify new version p = run_alr('show', '--solve') -check_child('0.1.0', p.out) +check_child('0.1.0', p.out, pinned=True) print('SUCCESS') diff --git a/testsuite/tests/pin/post-update/test.py b/testsuite/tests/pin/post-update/test.py index cb594a88..7acff977 100644 --- a/testsuite/tests/pin/post-update/test.py +++ b/testsuite/tests/pin/post-update/test.py @@ -12,11 +12,11 @@ import re # Verify that proper version of libchild is in the printed and disk solution -def check_child(version, output): +def check_child(version, output, pinned): # Verify output assert_match('.*\n' 'Dependencies \(solution\):\n' - ' libchild=' + version + '\n' + ' libchild=' + version + (' \(pinned\)' if pinned else "") + '\n' ' libparent=1\.0\.0\n' '.*\n', output, flags=re.S) @@ -46,17 +46,17 @@ run_alr('with', '--del', 'libchild') # Verify pinned version is still in the solution, pre-update: p = run_alr('show', '--solve') -check_child('0.1.0', p.out) +check_child('0.1.0', p.out, pinned=True) # Run an update and verify solution is still the same run_alr('update') p = run_alr('show', '--solve') -check_child('0.1.0', p.out) +check_child('0.1.0', p.out, pinned=True) # Unpin and check upgraded solution run_alr('pin', '--unpin', 'libchild') p = run_alr('show', '--solve') -check_child('0.2.0', p.out) +check_child('0.2.0', p.out, pinned=False) print('SUCCESS') diff --git a/testsuite/tests/pin/unneeded-held/test.py b/testsuite/tests/pin/unneeded-held/test.py index 2bb475dc..8ff3c8b4 100644 --- a/testsuite/tests/pin/unneeded-held/test.py +++ b/testsuite/tests/pin/unneeded-held/test.py @@ -33,14 +33,15 @@ assert_eq('libchild 0.2.0\n', # Check that there are no dependencies p = run_alr('with') -assert_eq('(empty)\n', +assert_eq('Dependencies (direct):\n' + ' (empty)\n', p.out) # But the pinned release is still in the solution -p = run_alr('show', '--solve') +p = run_alr('with', '--solve') assert_match('.*' 'Dependencies \(solution\):\n' - ' libchild=0\.2\.0\n' + ' libchild=0\.2\.0 \(pinned\) \(origin: filesystem\)\n' 'Dependencies \(graph\):.*', p.out, flags=re.S) -- 2.39.5