From 2c053e445b6583fcade854678898148bb6a62626 Mon Sep 17 00:00:00 2001 From: Alejandro R Mosteo Date: Fri, 7 Feb 2025 11:51:54 +0100 Subject: [PATCH] feat: diagnose tool mismatch due to architecture change (#1837) * Warn on unexpected tool hash * Test * Fixes found by testsuite --- src/alire/alire-origins.adb | 13 +++++ src/alire/alire-origins.ads | 9 +++- src/alire/alire-toolchains-solutions.adb | 5 ++ src/alire/alire-toolchains.adb | 53 ++++++++++++++++++- src/alire/alire-toolchains.ads | 7 +++ .../tests/toolchain/arch-mismatch/test.py | 39 ++++++++++++++ .../tests/toolchain/arch-mismatch/test.yaml | 4 ++ 7 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 testsuite/tests/toolchain/arch-mismatch/test.py create mode 100644 testsuite/tests/toolchain/arch-mismatch/test.yaml diff --git a/src/alire/alire-origins.adb b/src/alire/alire-origins.adb index 276d340f..48790839 100644 --- a/src/alire/alire-origins.adb +++ b/src/alire/alire-origins.adb @@ -254,6 +254,19 @@ package body Alire.Origins is when Source_Archive => This.Data.Src_Archive.Hashes, when others => Hash_Vectors.Empty_Vector); + ---------------- + -- Unique_Ids -- + ---------------- + + function Unique_Ids (This : Origin) return AAA.Strings.Vector is + Result : AAA.Strings.Vector; + begin + for Hash of Hash_Vectors.Vector'(This.Get_Hashes) loop + Result.Append (String (Hash)); + end loop; + return Result; + end Unique_Ids; + ---------------- -- Add_Hashes -- ---------------- diff --git a/src/alire/alire-origins.ads b/src/alire/alire-origins.ads index 9f0a97f5..43a120fd 100644 --- a/src/alire/alire-origins.ads +++ b/src/alire/alire-origins.ads @@ -134,6 +134,11 @@ package Alire.Origins is when Binary_Archive | External | System => False, when Source_Archive | VCS_Kinds | Filesystem => True); + function Unique_Ids (This : Origin) return AAA.Strings.Vector; + -- This returns the hashes that apply to the current environment; it may be + -- an empty vector for origins without a hash (e.g. external or system or + -- unavailable). + function Short_Unique_Id (This : Origin) return String with Pre => This.Kind in Git | Hg | Archive_Kinds; @@ -218,7 +223,9 @@ private Ada.Containers.Indefinite_Vectors (Positive, Hashes.Any_Hash); function Get_Hashes (This : Origin) return Hash_Vectors.Vector; - -- Ugly Get_ but it avoids lots of ambiguities down the line + -- Ugly Get_ but it avoids lots of ambiguities down the line. This returns + -- the hashes that apply to the current environment; it may be an empty + -- vector for origins without a hash (e.g. external or system). function "+" (S : String) return Unbounded_String renames To_Unbounded_String; diff --git a/src/alire/alire-toolchains-solutions.adb b/src/alire/alire-toolchains-solutions.adb index 0000ded8..d0059417 100644 --- a/src/alire/alire-toolchains-solutions.adb +++ b/src/alire/alire-toolchains-solutions.adb @@ -35,6 +35,11 @@ package body Alire.Toolchains.Solutions is Result : Alire.Solutions.Solution := Solution; begin + -- Detect tool mismatch. This is a good place as this is the last moment + -- before a tool is really going to be used. + + Detect_Hash_Mismatch; + -- Last-minute redeployment of any missing toolchain element. This may -- happen if the user has manually deleted the cache of toolchains, or -- uninstalled a system package for the external compiler. diff --git a/src/alire/alire-toolchains.adb b/src/alire/alire-toolchains.adb index 636f0657..ebea6190 100644 --- a/src/alire/alire-toolchains.adb +++ b/src/alire/alire-toolchains.adb @@ -614,12 +614,63 @@ package body Alire.Toolchains is Trace.Debug ("Detected available tools:"); for Rel of Result loop - Trace.Debug (" Tool: " & Rel.Milestone.TTY_Image); + Trace.Debug (" Tool: " & Rel.Milestone.TTY_Image + & " hash: " & Rel.Origin.Unique_Ids.Flatten (",")); end loop; + -- When we have cached results, this is the earliest moment in which we + -- can check we don't have a tool mismatch. If Results were empty the + -- detection results in a recursive loop. + + if not Result.Is_Empty then + Detect_Hash_Mismatch; + end if; + return Result; end Available; + -------------------------- + -- Detect_Hash_Mismatch -- + -------------------------- + + procedure Detect_Hash_Mismatch is + use type AAA.Strings.Vector; + begin + for Tool of Tools loop + if Tool_Is_Configured (Tool) and then not Tool_Is_Missing (Tool) then + declare + Tool_Rel : constant Releases.Release := Tool_Release (Tool); + begin + if Index.Exists (Tool, Tool_Rel.Version) and then + Tool_Rel.Origin.Unique_Ids /= + Index.Find (Tool, Tool_Rel.Version).Origin.Unique_Ids + then + Trace.Debug ("Tool hash mismatch for " + & Tool_Rel.Milestone.TTY_Image); + Trace.Debug ("Configured: " + & Tool_Rel.Origin.Unique_Ids.Flatten); + Trace.Debug ("From index: " + & Index.Find (Tool, Tool_Rel.Version) + .Origin.Unique_Ids.Flatten); + + Raise_Checked_Error + ("Selected tool " & Tool_Rel.Milestone.TTY_Image + & " does not match its fingerprint from the index." + & New_Line + & "This may be caused by reusing the configuration " + & "of an alr built for a different architecture" + & New_Line + & "Please manually delete the folder " + & TTY.URL (Path / Tool_Rel.Deployment_Folder) + & New_Line + & "Afterwards, reconfigure your toolchain with " + & TTY.Terminal ("alr toolchain --select")); + end if; + end; + end if; + end loop; + end Detect_Hash_Mismatch; + ---------- -- Path -- ---------- diff --git a/src/alire/alire-toolchains.ads b/src/alire/alire-toolchains.ads index 1c941ba3..3ded2056 100644 --- a/src/alire/alire-toolchains.ads +++ b/src/alire/alire-toolchains.ads @@ -43,6 +43,13 @@ package Alire.Toolchains is -- plain `gnat`. This way we need not to litter the callers with similar -- transformations, as we always want whatever gnat_XXX is used for "gnat". + procedure Detect_Hash_Mismatch; + -- As reported in https://github.com/alire-project/alire/issues/1796, + -- mixing alrs with different architectures will confuse the toolchain. We + -- can detect this from the hashes of the installed toolchains, that will + -- not match once the architecture changes. We raise in that case with + -- advice for the user to reconfigure its toolchain. + procedure Set_Automatic_Assistant (Enabled : Boolean; Level : Settings.Level); -- Enable/Disable the automatic assistant on next run diff --git a/testsuite/tests/toolchain/arch-mismatch/test.py b/testsuite/tests/toolchain/arch-mismatch/test.py new file mode 100644 index 00000000..7c2f36c9 --- /dev/null +++ b/testsuite/tests/toolchain/arch-mismatch/test.py @@ -0,0 +1,39 @@ +""" +Verify that when there's a hash mismatch between an installed tool and the one +in our index for the same version, we are able to detect it and give advice on +how to fix the situation. +""" + +import os +from drivers.alr import run_alr, alr_settings_dir +from drivers.asserts import assert_substring +from drivers.helpers import content_of + +# We can trigger the situation by configuring a toolchain and tampering with +# the hash in the toolchain cache. + +run_alr("toolchain", "--select", "gprbuild", "gnat_native") + +target_file = os.path.join(alr_settings_dir(), + "cache", + "toolchains", + "gprbuild_1.0.0_e3d52b4a", + "alire.toml") + +OLD = "e3d52b4a441a56ab1c0175ee8ea407864b72879cf73184a9f7d68eef53a87451" +NEW = "00002b4a441a56ab1c0175ee8ea407864b72879cf73184a9f7d68eef53a87451" + +# Modify the hash in the toolchain cache +lines = content_of(target_file) +lines = lines.replace(OLD, NEW) +assert_substring(NEW, lines) + +# Replace contents of the target file +with open(target_file, "w") as f: + f.write(lines) + +# Verify the diagnostic message +assert_substring("Selected tool gprbuild=1.0.0 does not match its fingerprint from the index", + run_alr("toolchain", complain_on_error=False).out) + +print("SUCCESS") diff --git a/testsuite/tests/toolchain/arch-mismatch/test.yaml b/testsuite/tests/toolchain/arch-mismatch/test.yaml new file mode 100644 index 00000000..7d0e9fc6 --- /dev/null +++ b/testsuite/tests/toolchain/arch-mismatch/test.yaml @@ -0,0 +1,4 @@ +driver: python-script +build_mode: both +indexes: + gnat_toolchain_index: {} \ No newline at end of file -- 2.39.5