From c462ebdec96098df0d9c258d491fd7f5a93ac607 Mon Sep 17 00:00:00 2001 From: Alejandro R Mosteo Date: Tue, 19 May 2020 12:05:36 +0200 Subject: [PATCH] `alr update` improvements (#415) * Reimplement `alr update --online` * On demand update of selected dependencies * Test: on demand update of selected crate * Review fixes Explicitly check that requested crate is not pinned, since it would never result in an update. --- src/alire/alire-containers.ads | 9 +- src/alire/alire-solutions.adb | 19 +++ src/alire/alire-solutions.ads | 4 + src/alire/alire-utils-tty.ads | 5 + src/alire/alire-workspaces.adb | 42 ++++++ src/alire/alire-workspaces.ads | 19 +++ src/alr/alr-commands-index.adb | 2 - src/alr/alr-commands-index.ads | 3 + src/alr/alr-commands-update.adb | 121 +++++++++++++----- src/alr/alr-commands-update.ads | 9 +- .../selective/my_index/index/he/hello1.toml | 8 ++ .../selective/my_index/index/he/hello2.toml | 8 ++ .../selective/my_index/index/index.toml | 1 + .../my_index/updated/index/he/hello1.toml | 11 ++ .../my_index/updated/index/he/hello2.toml | 11 ++ .../my_index/updated/index/index.toml | 1 + testsuite/tests/update/selective/test.py | 37 ++++++ testsuite/tests/update/selective/test.yaml | 4 + 18 files changed, 277 insertions(+), 37 deletions(-) create mode 100644 src/alire/alire-workspaces.adb create mode 100644 src/alire/alire-workspaces.ads create mode 100644 testsuite/tests/update/selective/my_index/index/he/hello1.toml create mode 100644 testsuite/tests/update/selective/my_index/index/he/hello2.toml create mode 100644 testsuite/tests/update/selective/my_index/index/index.toml create mode 100644 testsuite/tests/update/selective/my_index/updated/index/he/hello1.toml create mode 100644 testsuite/tests/update/selective/my_index/updated/index/he/hello2.toml create mode 100644 testsuite/tests/update/selective/my_index/updated/index/index.toml create mode 100644 testsuite/tests/update/selective/test.py create mode 100644 testsuite/tests/update/selective/test.yaml diff --git a/src/alire/alire-containers.ads b/src/alire/alire-containers.ads index 6ae57cce..72cacf83 100644 --- a/src/alire/alire-containers.ads +++ b/src/alire/alire-containers.ads @@ -12,7 +12,7 @@ with Alire.Releases; package Alire.Containers with Preelaborate is package Crate_Name_Sets is - new Ada.Containers.Indefinite_Ordered_Sets (Crate_Name); + new Ada.Containers.Indefinite_Ordered_Sets (Crate_Name); package Dependency_Lists is new Ada.Containers.Indefinite_Doubly_Linked_Lists @@ -51,6 +51,8 @@ package Alire.Containers with Preelaborate is (Crate_Name, Releases.Release, "<", Releases."="); type Release_Map is new Crate_Release_Maps.Map with null record; + Empty_Release_Map : constant Release_Map; + function Excluding (Map : Release_Map; Name : Crate_Name) return Release_Map; @@ -91,4 +93,9 @@ package Alire.Containers with Preelaborate is function To_Release_H (R : Releases.Release) return Release_H renames Release_Holders.To_Holder; +private + + Empty_Release_Map : constant Release_Map := + (Crate_Release_Maps.Empty_Map with null record); + end Alire.Containers; diff --git a/src/alire/alire-solutions.adb b/src/alire/alire-solutions.adb index e983cafb..e122e625 100644 --- a/src/alire/alire-solutions.adb +++ b/src/alire/alire-solutions.adb @@ -69,6 +69,25 @@ package body Alire.Solutions is end return; end Pins; + ---------- + -- Pins -- + ---------- + + function Pins (This : Solution) return Release_Map is + begin + if not This.Valid then + return Containers.Empty_Release_Map; + end if; + + return Map : Release_Map do + for Release of This.Releases loop + if Release.Is_Pinned then + Map.Insert (Release); + end if; + end loop; + end return; + end Pins; + ----------- -- Print -- ----------- diff --git a/src/alire/alire-solutions.ads b/src/alire/alire-solutions.ads index 8acdd4c9..b17268d3 100644 --- a/src/alire/alire-solutions.ads +++ b/src/alire/alire-solutions.ads @@ -57,6 +57,10 @@ package Alire.Solutions is -- Return all pinned releases as exact version dependencies. Will return an -- empty list for invalid solutions. + function Pins (This : Solution) return Release_Map; + -- Return all pinned release. Will return an empty map for invalid + -- solutions. + function With_Pins (This, Src : Solution) return Solution; -- Copy pins from Src to This diff --git a/src/alire/alire-utils-tty.ads b/src/alire/alire-utils-tty.ads index 3dd1f8b7..971c86af 100644 --- a/src/alire/alire-utils-tty.ads +++ b/src/alire/alire-utils-tty.ads @@ -44,6 +44,8 @@ package Alire.Utils.TTY with Preelaborate is function Bold (Text : String) return String; + function Name (Crate : Crate_Name) return String; + function Name (Text : String) return String; -- Bold Default for crate names @@ -79,6 +81,9 @@ private (Format (Text, Style => ANSI.Bright)); + function Name (Crate : Crate_Name) return String is + (Name (+Crate)); + function Name (Text : String) return String is (Bold (Text)); diff --git a/src/alire/alire-workspaces.adb b/src/alire/alire-workspaces.adb new file mode 100644 index 00000000..ca44a809 --- /dev/null +++ b/src/alire/alire-workspaces.adb @@ -0,0 +1,42 @@ +with Alire.Conditional; + +package body Alire.Workspaces is + + use type Conditional.Dependencies; + + ------------ + -- Update -- + ------------ + + function Update (Environment : Properties.Vector; + Allowed : Containers.Crate_Name_Sets.Set := + Containers.Crate_Name_Sets.Empty_Set; + Options : Solver.Query_Options := + Solver.Default_Options) + return Solutions.Solution + is + Old : constant Solutions.Solution := Root.Current.Solution; + Deps : Conditional.Dependencies := + Root.Current.Release.Dependencies (Environment); + begin + + -- Identify crates that must be held back + + if not Allowed.Is_Empty then + for Release of Old.Releases loop + if not Allowed.Contains (Release.Name) then + Trace.Debug ("Forcing release in solution: " + & Release.Version.Image); + Deps := Release.To_Dependency and Deps; + end if; + end loop; + end if; + + return Solver.Resolve + (Deps => Deps, + Props => Environment, + Current => Old, + Options => Options); + end Update; + +end Alire.Workspaces; diff --git a/src/alire/alire-workspaces.ads b/src/alire/alire-workspaces.ads new file mode 100644 index 00000000..a7bef573 --- /dev/null +++ b/src/alire/alire-workspaces.ads @@ -0,0 +1,19 @@ +with Alire.Containers; +with Alire.Properties; +with Alire.Root; +with Alire.Solver; +with Alire.Solutions; + +package Alire.Workspaces is + + function Update (Environment : Properties.Vector; + Allowed : Containers.Crate_Name_Sets.Set := + Containers.Crate_Name_Sets.Empty_Set; + Options : Solver.Query_Options := + Solver.Default_Options) + return Solutions.Solution + with Pre => Root.Current.Is_Valid; + -- Compute a new solution for the workspace. If Allowed is not empty, + -- crates not appearing in Allowed are held back at their current version. + +end Alire.Workspaces; diff --git a/src/alr/alr-commands-index.adb b/src/alr/alr-commands-index.adb index 05546ae9..59b9a2e5 100644 --- a/src/alr/alr-commands-index.adb +++ b/src/alr/alr-commands-index.adb @@ -15,8 +15,6 @@ package body Alr.Commands.Index is procedure Reset_Community; - procedure Update_All; - --------- -- Add -- --------- diff --git a/src/alr/alr-commands-index.ads b/src/alr/alr-commands-index.ads index f3e963fe..681b7d85 100644 --- a/src/alr/alr-commands-index.ads +++ b/src/alr/alr-commands-index.ads @@ -25,6 +25,9 @@ package Alr.Commands.Index is ("--add --name [--before ] | --del | --list" & " | --update-all"); + procedure Update_All; + -- Request update of configured indexes + private type Command is new Commands.Command with record diff --git a/src/alr/alr-commands-update.adb b/src/alr/alr-commands-update.adb index c5abd8fe..3e434309 100644 --- a/src/alr/alr-commands-update.adb +++ b/src/alr/alr-commands-update.adb @@ -1,19 +1,18 @@ -with Alire.Paths; +with Alire.Containers; +with Alire.Errors; with Alire.Solutions.Diffs; with Alire.Solver; +with Alire.Utils.TTY; +with Alire.Workspaces; with Alr.Checkout; +with Alr.Commands.Index; with Alr.Commands.User_Input; with Alr.Platform; with Alr.Root; -with GNAT.OS_Lib; -with Alr.Bootstrap; - package body Alr.Commands.Update is - use all type Bootstrap.Session_States; - package Query renames Alire.Solver; ------------- @@ -21,22 +20,36 @@ package body Alr.Commands.Update is ------------- procedure Upgrade (Interactive : Boolean; - Force : Boolean := False) is - -- The part concerning only to the working release + Force : Boolean := False; + Allowed : Alire.Containers.Crate_Name_Sets.Set := + Alire.Containers.Crate_Name_Sets.Empty_Set) + is + Old : constant Query.Solution := + Root.Current.Solution; begin - Requires_Full_Index; - Requires_Valid_Session; + -- Ensure requested crates are in solution first + + for Crate of Allowed loop + if not Old.Releases.Contains (Crate) then + Reportaise_Wrong_Arguments ("Requested crate is not a dependency: " + & Alire.Utils.TTY.Name (Crate)); + end if; + + if Old.Pins.Contains (Crate) then + Reportaise_Wrong_Arguments + ("Requested crate is pinned and cannot be updated: " + & Alire.Utils.TTY.Name (Crate)); + end if; + end loop; + + Requires_Full_Index; declare - Old : constant Query.Solution := - Root.Current.Solution; Needed : constant Query.Solution := - Query.Resolve - (Root.Current.Release.Dependencies.Evaluate - (Platform.Properties), - Platform.Properties, - Old, + Alire.Workspaces.Update + (Platform.Properties, + Allowed, Options => (Age => Query_Policy, Detecting => <>, Hinting => <>)); @@ -85,9 +98,37 @@ package body Alr.Commands.Update is ------------- overriding procedure Execute (Cmd : in out Command) is - pragma Unreferenced (Cmd); + + ------------------- + -- Parse_Allowed -- + ------------------- + + function Parse_Allowed return Alire.Containers.Crate_Name_Sets.Set is + begin + return Set : Alire.Containers.Crate_Name_Sets.Set do + for I in 1 .. Num_Arguments loop + Set.Include (Alire.Crate_Name (Argument (I))); + end loop; + end return; + exception + when E : Alire.Checked_Error => + -- Bad crate names in the command line is an expected error, so + -- re-raise it under the proper exception to avoid the 'unexpected + -- error' message. + Reportaise_Wrong_Arguments (Alire.Errors.Get (E)); + return Alire.Containers.Crate_Name_Sets.Empty_Set; + end Parse_Allowed; + begin - Execute (Interactive => True); + Requires_Valid_Session; + + if Cmd.Online then + Index.Update_All; + end if; + + Upgrade (Interactive => True, + Force => False, + Allowed => Parse_Allowed); end Execute; ------------- @@ -95,14 +136,13 @@ package body Alr.Commands.Update is ------------- procedure Execute (Interactive : Boolean; - Force : Boolean := False) is + Force : Boolean := False) + is begin - if Session_State > Outside then - Upgrade (Interactive => Interactive, - Force => Force); - else - Trace.Detail ("No working release to update"); - end if; + Requires_Valid_Session; + + Upgrade (Interactive => Interactive, + Force => Force); end Execute; ---------------------- @@ -113,9 +153,30 @@ package body Alr.Commands.Update is function Long_Description (Cmd : Command) return Alire.Utils.String_Vector is (Alire.Utils.Empty_Vector - .Append ("Resolves dependencies using the loaded indexes, and" - & " regenerates the aggregate project building file found in" - & " " & GNAT.OS_Lib.Directory_Separator - & Alire.Paths.Working_Folder_Inside_Root)); + .Append ("Resolves unpinned dependencies using available indexes.") + .New_Line + .Append ("Invoked without arguments will consider all unpinned crates" + & " for updating.") + .New_Line + .Append ("One or more crates can be given as argument, in which case" + & " only these crates will be candidates for updating." + & " Requesting the update of a pinned crate is not allowed.")); + + -------------------- + -- Setup_Switches -- + -------------------- + + overriding procedure Setup_Switches + (Cmd : in out Command; + Config : in out GNAT.Command_Line.Command_Line_Configuration) + is + use GNAT.Command_Line; + begin + Define_Switch + (Config, + Cmd.Online'Access, + Long_Switch => "--online", + Help => "Fetch index updates before attempting crate updates"); + end Setup_Switches; end Alr.Commands.Update; diff --git a/src/alr/alr-commands-update.ads b/src/alr/alr-commands-update.ads index 594c6fa1..eabce87c 100644 --- a/src/alr/alr-commands-update.ads +++ b/src/alr/alr-commands-update.ads @@ -12,8 +12,7 @@ package Alr.Commands.Update is overriding procedure Setup_Switches (Cmd : in out Command; - Config : in out GNAT.Command_Line.Command_Line_Configuration) - is null; + Config : in out GNAT.Command_Line.Command_Line_Configuration); overriding function Short_Description (Cmd : Command) return String @@ -21,7 +20,7 @@ package Alr.Commands.Update is overriding function Usage_Custom_Parameters (Cmd : Command) return String - is (""); + is ("[crate]..."); procedure Execute (Interactive : Boolean; Force : Boolean := False); @@ -31,6 +30,8 @@ package Alr.Commands.Update is private - type Command is new Commands.Command with null record; + type Command is new Commands.Command with record + Online : aliased Boolean := False; + end record; end Alr.Commands.Update; diff --git a/testsuite/tests/update/selective/my_index/index/he/hello1.toml b/testsuite/tests/update/selective/my_index/index/he/hello1.toml new file mode 100644 index 00000000..a537eb3a --- /dev/null +++ b/testsuite/tests/update/selective/my_index/index/he/hello1.toml @@ -0,0 +1,8 @@ +[general] +description = """"Hello, world!" demonstration project""" +licenses = [] +maintainers = ["example@example.com"] +maintainers-logins = ["mylogin"] + +['0.1'] +origin = "file://." diff --git a/testsuite/tests/update/selective/my_index/index/he/hello2.toml b/testsuite/tests/update/selective/my_index/index/he/hello2.toml new file mode 100644 index 00000000..a537eb3a --- /dev/null +++ b/testsuite/tests/update/selective/my_index/index/he/hello2.toml @@ -0,0 +1,8 @@ +[general] +description = """"Hello, world!" demonstration project""" +licenses = [] +maintainers = ["example@example.com"] +maintainers-logins = ["mylogin"] + +['0.1'] +origin = "file://." diff --git a/testsuite/tests/update/selective/my_index/index/index.toml b/testsuite/tests/update/selective/my_index/index/index.toml new file mode 100644 index 00000000..7c969026 --- /dev/null +++ b/testsuite/tests/update/selective/my_index/index/index.toml @@ -0,0 +1 @@ +version = "0.2" diff --git a/testsuite/tests/update/selective/my_index/updated/index/he/hello1.toml b/testsuite/tests/update/selective/my_index/updated/index/he/hello1.toml new file mode 100644 index 00000000..b084e4c9 --- /dev/null +++ b/testsuite/tests/update/selective/my_index/updated/index/he/hello1.toml @@ -0,0 +1,11 @@ +[general] +description = """"Hello, world!" demonstration project""" +licenses = [] +maintainers = ["example@example.com"] +maintainers-logins = ["mylogin"] + +['0.2'] +origin = "file://." + +['0.1'] +origin = "file://." diff --git a/testsuite/tests/update/selective/my_index/updated/index/he/hello2.toml b/testsuite/tests/update/selective/my_index/updated/index/he/hello2.toml new file mode 100644 index 00000000..b084e4c9 --- /dev/null +++ b/testsuite/tests/update/selective/my_index/updated/index/he/hello2.toml @@ -0,0 +1,11 @@ +[general] +description = """"Hello, world!" demonstration project""" +licenses = [] +maintainers = ["example@example.com"] +maintainers-logins = ["mylogin"] + +['0.2'] +origin = "file://." + +['0.1'] +origin = "file://." diff --git a/testsuite/tests/update/selective/my_index/updated/index/index.toml b/testsuite/tests/update/selective/my_index/updated/index/index.toml new file mode 100644 index 00000000..7c969026 --- /dev/null +++ b/testsuite/tests/update/selective/my_index/updated/index/index.toml @@ -0,0 +1 @@ +version = "0.2" diff --git a/testsuite/tests/update/selective/test.py b/testsuite/tests/update/selective/test.py new file mode 100644 index 00000000..275580e5 --- /dev/null +++ b/testsuite/tests/update/selective/test.py @@ -0,0 +1,37 @@ +""" +Test selective crate update +""" + +import os +import re + +from drivers.alr import run_alr +from drivers.asserts import assert_match + + +# Create a new "xxx" program project +run_alr('init', '--bin', 'xxx') +os.chdir('xxx') + +# Make it depend on hello1 and hello2 +run_alr('with', 'hello1', 'hello2') + +# Verify solution depends on 0.1 versions +p = run_alr('with', '--solve') +assert_match('.*Dependencies \(solution\):\n' + ' hello1=0\.1.*\n' + ' hello2=0\.1.*\n', + p.out, flags=re.S) + +# Add a new index that contains updates +run_alr('index', '--name', 'updates', '--add', '../my_index/updated') + +# Do selective update and check than only the requested crate has been updated +run_alr('update', 'hello2') +p = run_alr('with', '--solve') +assert_match('.*Dependencies \(solution\):\n' + ' hello1=0\.1.*\n' + ' hello2=0\.2.*\n', + p.out, flags=re.S) + +print('SUCCESS') diff --git a/testsuite/tests/update/selective/test.yaml b/testsuite/tests/update/selective/test.yaml new file mode 100644 index 00000000..0a859639 --- /dev/null +++ b/testsuite/tests/update/selective/test.yaml @@ -0,0 +1,4 @@ +driver: python-script +indexes: + my_index: + in_fixtures: false -- 2.39.5