From cf035eba122aad3560e8796ea77150af3a55e6eb Mon Sep 17 00:00:00 2001 From: "Alejandro R. Mosteo" Date: Mon, 18 Mar 2024 23:53:58 +0100 Subject: [PATCH] More detailed test of softlink installation --- .github/workflows/ci-toolchain.yml | 14 --- src/alire/alire-directories.adb | 107 ++++++++++-------- src/alire/alire-directories.ads | 4 + .../softlinks/my_index/crate-0.1.0.tgz | Bin 349 -> 20480 bytes .../my_index/index/cr/crate/crate-0.1.0.toml | 2 +- testsuite/tests/install/softlinks/test.py | 62 +++++++++- 6 files changed, 123 insertions(+), 66 deletions(-) diff --git a/.github/workflows/ci-toolchain.yml b/.github/workflows/ci-toolchain.yml index 8f9bd8749..7db742c4e 100644 --- a/.github/workflows/ci-toolchain.yml +++ b/.github/workflows/ci-toolchain.yml @@ -44,20 +44,6 @@ jobs: with: crates: gnat_native^${{matrix.gcc_version}} gprbuild - # Superstrange occurrence in which a softlink is missing at the installed - # destination. The link is there when using `alr get`, and also when using - # `alr install` in my machines. Being a softlink the one missing, it could - # have something with the order in which files are enumerated and GNATCOLL - # implementation. Will have to dig further but this should be a temporary - # fix. - - name: GNAT 10 fix - if: runner.os == 'Linux' && matrix.gcc_version == 10 - run: | - PREFIX=/home/runner/work/alire/alire/alire_prefix/libexec/gcc/x86_64-pc-linux-gnu/10.3.0 - ln -sf \ - $PREFIX/liblto_plugin.so.0.0.0 \ - $PREFIX/liblto_plugin.so - - name: Build alr with default toolchain shell: bash run: dev/build.sh diff --git a/src/alire/alire-directories.adb b/src/alire/alire-directories.adb index 14b0af76b..e5b53bbb1 100644 --- a/src/alire/alire-directories.adb +++ b/src/alire/alire-directories.adb @@ -136,6 +136,28 @@ package body Alire.Directories is End_Search (Search); end Copy; + --------------- + -- Copy_Link -- + --------------- + + procedure Copy_Link (Src, Dst : Any_Path) is + use AAA.Strings; + use all type Platforms.Operating_Systems; + Keep_Links : constant String + := (case Platforms.Current.Operating_System is + when Linux => "-d", + when Freebsd | MacOS => "-R", + when others => + raise Program_Error with "Unsupported operation"); + begin + -- Given that we are here because Src is indeed a link, we should be in + -- a Unix-like platform able to do this. + OS_Lib.Subprocess.Checked_Spawn + ("cp", + To_Vector (Keep_Links) + & Src & Dst); + end Copy_Link; + ----------------- -- Create_Tree -- ----------------- @@ -812,9 +834,6 @@ package body Alire.Directories is and then Base = Parent (Src) then Trace.Debug (" Merge: Not merging top-level file " & Src); - if Remove_From_Source then - Adirs.Delete_File (Src); - end if; return; end if; @@ -831,20 +850,19 @@ package body Alire.Directories is -- recursion we could more efficiently rename now into place. end if; - -- Copy/Move a file into place + -- Copy file into place - Trace.Debug (" Merge: " - & (if Remove_From_Source then " moving " else " copying ") + Trace.Debug (" Merge: copying " & Adirs.Full_Name (Item) & " into " & Dst); if Adirs.Exists (Dst) then if Fail_On_Existing_File then - Recoverable_User_Error ("Cannot move " & TTY.URL (Src) + Recoverable_User_Error ("Cannot copy " & TTY.URL (Src) & " into place, file already exists: " & TTY.URL (Dst)); elsif Adirs.Kind (Dst) /= Ordinary_File then - Raise_Checked_Error ("Cannot replace " & TTY.URL (Dst) + Raise_Checked_Error ("Cannot overwrite " & TTY.URL (Dst) & " as it is not a regular file"); else Trace.Debug (" Merge: Deleting in preparation to replace: " @@ -853,58 +871,51 @@ package body Alire.Directories is end if; end if; - -- We use GNATCOLL.VFS here as some binary packages contain softlinks + -- We use GNAT.OS_Lib here as some binary packages contain softlinks -- to .so libs that we must copy too, and these are troublesome -- with regular Ada.Directories (that has no concept of softlink). -- Also, some of these softlinks are broken and although they are -- presumably safe to discard, let's just go for an identical copy. - declare - VF : constant VFS.Virtual_File := - VFS.New_Virtual_File (VFS.From_FS (Src)); - OK : Boolean := False; - begin - if VF.Is_Symbolic_Link then - Trace.Debug (" Merge (softlink): " & Src); + if GNAT.OS_Lib.Is_Symbolic_Link (Src) then + Trace.Debug (" Merge (softlink): " & Src); - if Remove_From_Source then - VF.Rename (VFS.New_Virtual_File (Dst), OK); - else - VF.Copy (VFS.Filesystem_String (Dst), OK); - end if; - if not OK or else not GNAT.OS_Lib.Is_Symbolic_Link (Dst) then - Raise_Checked_Error ("Failed to copy/move softlink: " - & TTY.URL (Src)); - end if; - else - begin - if Remove_From_Source then - Adirs.Rename (Old_Name => Src, - New_Name => Dst); - else - Adirs.Copy_File (Source_Name => Src, - Target_Name => Dst, - Form => "preserve=all_attributes"); - end if; - exception - when E : others => - Trace.Error - ("When " & - (if Remove_From_Source - then "renaming " - else "copying ") - & Src & " --> " & Dst & ": "); - Log_Exception (E, Error); - raise; - end; + Copy_Link (Src, Dst); + if not GNAT.OS_Lib.Is_Symbolic_Link (Dst) then + Raise_Checked_Error ("Failed to copy softlink: " + & TTY.URL (Src) + & " to " & TTY.URL (Dst) + & " (dst not a link)"); end if; - end; + else + begin + Adirs.Copy_File (Source_Name => Src, + Target_Name => Dst, + Form => "preserve=all_attributes"); + exception + when E : others => + Trace.Error + ("When copying " & Src & " --> " & Dst & ": "); + Log_Exception (E, Error); + raise; + end; + end if; end Merge; begin Traverse_Tree (Start => Src, Doing => Merge'Access, Recurse => True); + + -- This is space-inefficient since we use 2x the actual size, but this + -- is the only way we have unless we want to go into platform-dependent + -- details and radical changes due to softlinks . + + -- TODO: remove this limitation on a non-patch release. + + if Remove_From_Source then + Force_Delete (Src); + end if; end Merge_Contents; ------------------- @@ -990,7 +1001,7 @@ package body Alire.Directories is if not Prune and then Recurse and then Kind (Item) = Directory then declare - Normal_Name : constant String + Normal_Name : constant Absolute_Path := String (GNATCOLL.VFS.Full_Name (VFS.New_Virtual_File (Full_Name (Item)), diff --git a/src/alire/alire-directories.ads b/src/alire/alire-directories.ads index 64af2cd39..bbf513751 100644 --- a/src/alire/alire-directories.ads +++ b/src/alire/alire-directories.ads @@ -32,6 +32,10 @@ package Alire.Directories is -- equivalent to "cp -r src/* dst/". Excluding may be a single name that -- will not be copied (if file) or recursed into (if folder). + procedure Copy_Link (Src, Dst : Any_Path) + with Pre => GNAT.OS_Lib.Is_Symbolic_Link (Src); + -- Copy a softlink into a new place preserving its relative path to target + function Current return String renames Ada.Directories.Current_Directory; function Parent (Path : Any_Path) return String diff --git a/testsuite/tests/install/softlinks/my_index/crate-0.1.0.tgz b/testsuite/tests/install/softlinks/my_index/crate-0.1.0.tgz index 11effa04cedfa0880e3d5fefb46130889b860442..233270e6a85bc1eda79e21b41a8f34944129c4f0 100644 GIT binary patch literal 20480 zcmeI24UXC{5QTFTF3{ldPkbK!TG3_6DzMUu(|0D`ZCP+gybP6Ey$A_#V#mDC&z><^ zb&KQXo2jWb^Xfr|;UmUS@f*cp&fn#f92I%*oHdqQQyC)bn`#yw)Spkyz8;Q?ZZ@la zF81xG6Bl2`&R<;quY6sd@$WxBU=#@8asDAT z7+wTyg#Yi=eM2Tw-$yI{_nrdyPvLj(H8=Uc-P?MqFZ`RvkIesl{71%< z@DG9GKcjQ|+pS>!&(Ht7=+1X-`*K^oNoSM)OVZQ32D-uzHkxv=lQ=Z zy3PLh1wpT?l=Bakc>QPo*NthHfPbFE%dUOesHTAB z`~wBg{(s&c4%__?rD2$F2K+Dhx7~WvX@&m1=86B<{$I-f8z=Ff<2(TIe}2H~mR`sI ziT_gZHo|{A{!jb^N&d(AKjMEy0>e!W@n0*>044qp^8WxiP5|KlVQPA3hWTqnf0g9@ z)BWG`_a^ZEFYwi}*heOu01A|G8Z~)rYn=wMGms{3quBo*w}a z|3^cJ|CLc^n;YSuty5Z9F#mh%*Z;=xV}QK>t2zwx-NyQVvHr{a|1Nx9vEtGa{?^L= zPeYt8f`75n%kKjZ(zUrXaM{u2KM z{sS2Nwe&|jPyh-*0Vn_kpa2wr0#E=7KmjNK1)u;FfC5ke3P1rU00p1`6o3Ly017|> MC;$bZ!2hklFS)5@x&QzG literal 349 zcmV-j0iymNiwFP!000001MQcAYJ)HkhVv-Cz-%ri=6P)Aid&~eE$s1ku`Nhjox-KC z?f(!A7`VePmrHhS(U&herBrWJBsAl_3vcpHkKb1mS({}>8_IH}iHyxv;$kH<=G65? zn_+~FafDT{(7z`l>vhKdO~#+${|Vgt-&VD7jI)2gr2a`;o=1)! zm6-0deXZIE8I6Gd8~>M|E*N7){kt&M+ysnA{Xc_A|6FuZYhcy>Z$kgKHq<}-$7k06 zmrRTxIM40E{hxw==lJ}mWx~$`|I_{tO>-28v9SM->fiGU;QpVc8-C6S3FPq*_ subdir/bin ├── broken -> missing +├── lib +│ ├── mock.so -> mock.so.0.0 +│ ├── mock.so.0 -> mock.so.0.0 +│ ├── mock.so.0.0 +│ ├── zzz.so -> mock.so +│ └── zzz.so.0 -> mock.so +├── order +│ ├── ab -> b +│ ├── af -> d/f +│ ├── b +│ ├── cb -> b +│ ├── d +│ │ └── f +│ └── zf -> d/f └── subdir ├── bin │ ├── loop -> ../../subdir │ └── x ├── parent -> .. └── self -> ../subdir + """ +import os +import shutil +import subprocess import sys -from drivers.alr import run_alr -from drivers.helpers import on_windows +from drivers.alr import crate_dirname, run_alr +from drivers.helpers import contents, on_windows + + +def kind(file): + return (os.path.isfile(file), os.path.islink(file), os.path.isdir(file)) + +def ls(path): + out = subprocess.run(["ls", "-alFR", path], capture_output=True, text=True) + return out.stdout # Does not apply to Windows as it does not support softlinks @@ -27,5 +53,35 @@ # This command should succeed normally run_alr("install", "--prefix=install", "crate") +# Contents should be identical. For that, we first untar the crate and then +# directly compare with the destination. + +run_alr("get", "crate") # This merely untars and moves the whole dir in one step, + # so contents are not modified. +cratedir = crate_dirname("crate") +os.chdir(cratedir) +shutil.rmtree("alire") # Created by get +os.remove("alire.toml") # Created by get +items = contents(".") # Contents of the original crate +os.chdir("..") +os.chdir("install") # Contents of the install prefix + +for item in items: + orig = f"../{cratedir}/{item}" + whatis = kind(orig) + if os.path.islink(orig) and os.path.isdir(orig): + continue # We aren't yet able to copy those + if not os.path.exists (orig): + continue # Broken links, we don't copy them at all + assert os.path.exists(item), \ + f"Missing expected entry {item}: {whatis}') in " + \ + f"contents (dst):\n{ls('.')}" + \ + f"contents (src):\n{ls('../' + cratedir)}" + assert kind(item) == kind(orig), \ + f"Unexpected kind for {item}: {kind(item)} != {kind(orig)}" + +# Cleanup +os.chdir("..") +shutil.rmtree(cratedir) print('SUCCESS')