Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically narrow down dependencies given as "any" #675

Merged
merged 6 commits into from
Feb 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions doc/user-changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ This document is a development diary summarizing changes in `alr` that notably
affect the user experience. It is intended as a one-stop point for users to
stay on top of `alr` new features.

### Narrow down versions for dependencies given without restrictions

PR [#675](https://github.com/alire-project/alire/pull/675).

When a user requests a dependency without narrowing down its version set (e.g.,
`alr with foo`), the solved version will be used to instead add an
"update-safe" dependency (e.g., `foo^1.x`, `foo~0.x`). To truly request any
version, this can be explicitly entered as `alr with 'foo>=0'`.

This behavior can be disabled by setting the `solver.autonarrow` configuration
option to false.

### The command `alr list` has been renamed to `alr search --crates`

PR [#671](https://github.com/alire-project/alire/pull/671).
Expand Down
5 changes: 5 additions & 0 deletions src/alire/alire-conditional.ads
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ package Alire.Conditional with Preelaborate is
return Dependencies;
-- Dependency on a version set

function New_Dependency (Dep : Alire.Dependencies.Dependency)
return Conditional.Dependencies
renames For_Dependencies.New_Value;
-- Convert a plain dependency into a tree containing a single leaf

function Enumerate is new Conditional.For_Dependencies.Enumerate
(Alire.Dependencies.Containers.List,
Alire.Dependencies.Containers.Append);
Expand Down
5 changes: 5 additions & 0 deletions src/alire/alire-config-edit.ads
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ private
+("If true, Alire will report an unknown distribution and will not"
& " attempt to use the system package manager.")),

(+Keys.Solver_Autonarrow,
Cfg_Bool,
+("If true, `alr with` will replace 'any' dependencies with the"
& " appropriate caret/tilde dependency.")),

(+Keys.Warning_Caret,
Cfg_Bool,
+("If true, Alire will warn about the use of caret (^) "
Expand Down
18 changes: 11 additions & 7 deletions src/alire/alire-config.ads
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,17 @@ package Alire.Config with Preelaborate is
-- A few predefined keys that are used in several places. This list is
-- not exhaustive.

Editor_Cmd : constant Config_Key := "editor.cmd";

Distribution_Disable_Detection : constant Config_Key :=
"distribution.disable_detection";
-- When set to True, distro will be reported as unknown, and in turn no
-- native package manager will be used.

Solver_Autonarrow : constant Config_Key := "solver.autonarrow";
-- When true, `alr with` will substitute "any" dependencies by the
-- appropriate caret/tilde.

Update_Manually : constant Config_Key := "update-manually-only";
-- Used by `get --only` to flag a workspace to not autoupdate itself
-- despite having no solution in the lockfile.
Expand All @@ -88,13 +99,6 @@ package Alire.Config with Preelaborate is
User_Name : constant Config_Key := "user.name";
User_Github_Login : constant Config_Key := "user.github_login";

Editor_Cmd : constant Config_Key := "editor.cmd";

Distribution_Disable_Detection : constant Config_Key :=
"distribution.disable_detection";
-- When set to True, distro will be reported as unknown, and in turn no
-- native package manager will be used.

Warning_Caret : constant Config_Key := "warning.caret";
-- Set to false to disable warnings about caret/tilde use for ^0 deps.

Expand Down
2 changes: 1 addition & 1 deletion src/alire/alire-milestones.adb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ package body Alire.Milestones is
use Ada.Strings.Fixed;
use Ada.Strings.Maps;

Op_Pos : constant Natural := Index (Spec, To_Set ("=^~<>/("), Inside);
Op_Pos : constant Natural := Index (Spec, To_Set ("*=^~<>/("), Inside);
Name : constant String := (if Op_Pos > Spec'First
then Spec (Spec'First .. Op_Pos - 1)
else Spec);
Expand Down
63 changes: 61 additions & 2 deletions src/alire/alire-solutions.adb
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
with Ada.Containers;

with Alire.Config;
with Alire.Crates;
with Alire.Dependencies.Containers;
with Alire.Dependencies.Diffs;
with Alire.Dependencies.Graphs;
with Alire.Index;
with Alire.Root;
Expand All @@ -10,10 +12,9 @@ with Alire.Utils.Tables;
with Alire.Utils.Tools;
with Alire.Utils.TTY;

with Semantic_Versioning;

package body Alire.Solutions is

package Semver renames Semantic_Versioning;
package TTY renames Utils.TTY;

use type Ada.Containers.Count_Type;
Expand Down Expand Up @@ -806,4 +807,62 @@ package body Alire.Solutions is
end return;
end To_TOML;

-------------------------------
-- Restrict_New_Dependencies --
-------------------------------

function Restrict_New_Dependencies (Old_Deps,
New_Deps : Conditional.Dependencies;
New_Sol : Solution)
return Conditional.Dependencies
is
Releases : constant Release_Map := New_Sol.Releases;

use type Conditional.Dependencies;
use type Semver.Extended.Version_Set;
Diff : constant Dependencies.Diffs.Diff :=
Dependencies.Diffs.Between (Old_Deps, New_Deps);
begin

-- Do nothing when deps are being removed.

if not Config.Get (Config.Keys.Solver_Autonarrow, True) or else
not Diff.Removed.Is_Empty
then
return New_Deps;
end if;

return Fixed_Deps : Conditional.Dependencies := Old_Deps do
for Added of Diff.Added loop

-- Keep as-is any version that is not "*", or is not solved

if Added.Versions /= Semver.Extended.Any or else
not Releases.Contains (Added.Crate)
then
Fixed_Deps := Fixed_Deps and Added;
else

-- Use either caret or tilde to narrow down the version

declare
Fixed : constant Dependencies.Dependency :=
Dependencies.New_Dependency
(Added.Crate,
Semver.Extended.Value
((if Releases (Added.Crate).Version.Major in 0
then "~"
else "^")
& Releases (Added.Crate).Version.Image));
begin
Trace.Detail ("Narrowing down dependency "
& Added.TTY_Image & " as " & Fixed.TTY_Image);
Fixed_Deps := Fixed_Deps and Fixed;
end;

end if;
end loop;
end return;
end Restrict_New_Dependencies;

end Alire.Solutions;
13 changes: 13 additions & 0 deletions src/alire/alire-solutions.ads
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,19 @@ package Alire.Solutions is
-- Requires releases not to have dynamic expressions. This is currently
-- guaranteed by the states storing static versions of releases.

---------------
-- Utilities --
---------------

function Restrict_New_Dependencies (Old_Deps,
New_Deps : Conditional.Dependencies;
New_Sol : Solution)
return Conditional.Dependencies;
-- Take new dependencies in a tree, see how they've been solved, and
-- replace "any" dependencies with the proper tilde or caret, depending on
-- what was found in the solution. E.g., if the user provided lib=*, and it
-- is solved as lib=2.0, replace lib=* with lib^2.0 in the result.

private

type Solution is new Interfaces.Tomifiable with record
Expand Down
7 changes: 6 additions & 1 deletion src/alr/alr-commands-withing.adb
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,12 @@ package body Alr.Commands.Withing is
others => <>));

Deps_Diff : constant Alire.Dependencies.Diffs.Diff :=
Alire.Dependencies.Diffs.Between (Old_Deps, New_Deps);
Alire.Dependencies.Diffs.Between
(Old_Deps,
Alire.Solutions.Restrict_New_Dependencies
(Old_Deps,
New_Deps,
New_Solution));

use Alire.Utils.User_Input;
begin
Expand Down
4 changes: 2 additions & 2 deletions testsuite/tests/pin/change-type/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def check_version_pin():
# Initialize a workspace, enter, and add a regular dependency
run_alr('init', '--bin', 'xxx')
os.chdir('xxx')
run_alr('with', 'libhello')
run_alr('with', 'libhello^1')

# Pin to a version
p = run_alr('pin', 'libhello=1.0')
Expand All @@ -35,7 +35,7 @@ def check_version_pin():
p = run_alr('show', '--solve')
s = re.escape(dir_separator()) # platform-dependent
assert_match('.*Dependencies \(external\):.*'
'libhello\* \(direct,linked'
'libhello\^1 \(direct,linked'
',pin=.*' + s + 'pin__change-type' + s +
'crates' + s + 'libhello_1.0.0\).*',
p.out, flags=re.S)
Expand Down
2 changes: 1 addition & 1 deletion testsuite/tests/pin/downgrade/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def check_child(version, output, pinned):
os.chdir('xxx')

# Make it depend on child (there are 0.1 and 0.2, so 0.2 used initially)
run_alr('with', 'libchild')
run_alr('with', 'libchild>0')
p = run_alr('show', '--solve')
check_child('0.2.0', p.out, pinned=False)

Expand Down
4 changes: 2 additions & 2 deletions testsuite/tests/pin/missing-version/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# xxx=0.0.0 -> hello=1.0.1 --> libhello=1.1.0
run_alr('init', '--bin', 'xxx')
os.chdir('xxx')
run_alr('with', 'hello')
run_alr('with', 'hello>0')

# 1st test: pin to an existing version that brings in missing dependencies.
# Pinning hello=3 brings in a libhello^3 dependency that is unavailable, so:
Expand All @@ -37,7 +37,7 @@
# Check solution is as expected
p = run_alr('with', '--solve')
assert_match('.*Dependencies \(external\):\n'
' hello=5\.0\.0.*',
' hello\(=5\.0\.0\).*',
p.out, flags=re.S)


Expand Down
2 changes: 1 addition & 1 deletion testsuite/tests/pin/pin-dir/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
# over some parts of the output
s = re.escape(dir_separator()) # platform-dependent
assert_match('.*Dependencies \(external\):.*'
'libhello\* \(direct,linked'
'libhello\^1.0.0 \(direct,linked'
',pin=.*' + s + 'pin__pin-dir' + s +
'crates' + s + 'libhello_1.0.0\).*',
p.out, flags=re.S)
Expand Down
2 changes: 1 addition & 1 deletion testsuite/tests/solver/one-dep-two-constraints/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
p = run_alr('with', '--solve')
assert_match('.*' # skip solution
'Dependencies \(graph\):\n'
' hello=1.0.1 --> libhello=1.0.1 \(\^1.0\)\n'
' hello=1.0.1 --> libhello=1.0.1 \(\^1.0\)\s*\n'
' superhello=1.0.0 --> libhello=1.0.1 \(~1.0\).*',
p.out, flags=re.S)

Expand Down
11 changes: 7 additions & 4 deletions testsuite/tests/update/missing-deps/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@
# See that updating succeeds
run_alr('update')

# Check that the solution is still the expected one
# Check that the solution is still the expected one, and also that the original
# dependency is included in the restrictions
p = run_alr('with', '--solve')
assert_match('.*Dependencies \(external\):\n'
' libhello=3.0.0.*',
p.out, flags=re.S)
assert_match(
'.*Dependencies \(external\):\n'
' ' +
re.escape('libhello(=3.0.0) & (^2.0.0) (direct,missed,pin=3.0.0)') + '.*',
p.out, flags=re.S)


print('SUCCESS')
2 changes: 1 addition & 1 deletion testsuite/tests/update/pinned/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# Add a dependency and force it missing by pinning it to non-existing version
run_alr('init', '--bin', 'xxx')
os.chdir('xxx')
run_alr('with', 'libhello') # This causes libhello=1.1
run_alr('with', 'libhello^1') # This causes libhello=1.1
run_alr('pin', 'libhello=1') # Downgrade to 1.0

# Check that updating without specific crate does not err
Expand Down
2 changes: 1 addition & 1 deletion testsuite/tests/update/selective/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
os.chdir('xxx')

# Make it depend on hello1 and hello2
run_alr('with', 'hello1', 'hello2')
run_alr('with', 'hello1>0', 'hello2>0')

# Verify solution depends on 0.1 versions
p = run_alr('with', '--solve')
Expand Down
8 changes: 4 additions & 4 deletions testsuite/tests/with/add-several/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@

p = run_alr('init', '--bin', 'xxx')
os.chdir('xxx')
p = run_alr('with', 'libhello')
p = run_alr('with', 'hello')
p = run_alr('with', 'libhello^1')
p = run_alr('with', 'hello^1')
p = run_alr('show')

assert_match('.*\n'
'Dependencies \(direct\):\n'
' hello\*\n'
' libhello\*\n',
' hello\^1\n'
' libhello\^1\n',
p.out, flags=re.S)

print('SUCCESS')
10 changes: 5 additions & 5 deletions testsuite/tests/with/changes-info/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
p = run_alr('with', 'libhello', quiet=False)
assert_match(re.escape("""Requested changes:

+ libhello * (add)
+ libhello ^2.0.0 (add)

Changes to dependency solution:

Expand Down Expand Up @@ -76,7 +76,7 @@
assert_match(".*" +
re.escape("""Requested changes:

- libhello * (remove)
- libhello ^2.0.0 (remove)

Changes to dependency solution:

Expand All @@ -89,7 +89,7 @@
assert_match(".*" +
re.escape("""Requested changes:

+ hello * (add)
+ hello ^1.0.1 (add)

Changes to dependency solution:

Expand All @@ -104,7 +104,7 @@
assert_match(".*" +
re.escape("""Requested changes:

+ libhello * (add)
+ libhello ^1.1.0 (add)

Changes to dependency solution:

Expand All @@ -118,7 +118,7 @@
assert_match(".*" +
re.escape("""Requested changes:

- libhello * (remove)
- libhello ^1.1.0 (remove)

Changes to dependency solution:

Expand Down
6 changes: 3 additions & 3 deletions testsuite/tests/with/dynamic-dependencies/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@
'superhello = "*"')

# Check adding a dependency
run_alr('with', 'hello')
assert 'hello = "*" # This line was added by `alr with`' \
run_alr('with', 'hello^1')
assert 'hello = "^1" # This line was added by `alr with`' \
in content_of(manifest)

# Check removal
run_alr('with', '--del', 'hello')
assert 'hello = "*" # This line was added by `alr with`' \
assert 'hello = "^1" # This line was added by `alr with`' \
not in content_of(manifest)

# Check that the dependency that precedes the dynamic expression is removable
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
description = "\"Hello, world!\" demonstration project"
name = "hello1"
version = "0.1.2"
licenses = "GPL-3.0-only"
maintainers = ["[email protected]"]
maintainers-logins = ["mylogin"]

[origin]
url = "file:."
Loading