From 715b13580c9374649007fce56a0f50b272c3b787 Mon Sep 17 00:00:00 2001 From: Alejandro R Mosteo Date: Tue, 14 Feb 2023 17:09:39 +0100 Subject: [PATCH] Install a local crate (#1322) * Install a local crate to a prefix * Make --this the default, list info with --info --- doc/user-changes.md | 27 +- src/alire/alire-errors.ads | 9 +- src/alire/alire-install.adb | 92 ++++-- src/alire/alire-install.ads | 34 ++- src/alire/alire-platforms-folders.ads | 6 +- src/alire/alire-releases.ads | 4 +- src/alire/alire-roots.adb | 285 +++++++++++++++++- src/alire/alire-roots.ads | 23 +- src/alire/alire-solutions.adb | 31 +- src/alire/alire-solutions.ads | 7 +- src/alire/alire-spawn.adb | 33 ++ src/alire/alire-spawn.ads | 18 +- .../alire-platforms-folders__windows.adb | 2 +- src/alr/alr-commands-install.adb | 58 +++- src/alr/alr-commands-install.ads | 9 +- src/alr/alr-commands.adb | 14 +- src/alr/alr-commands.ads | 7 +- testsuite/drivers/asserts.py | 15 + testsuite/drivers/helpers.py | 11 +- .../tests/install/binary-release/test.py | 29 +- .../tests/install/default-location/test.py | 39 +++ .../tests/install/default-location/test.yaml | 1 + .../tests/install/dynamic-linking/test.py | 53 ++++ .../tests/install/dynamic-linking/test.yaml | 1 + .../install/executable-dependency/test.py | 50 +++ .../install/executable-dependency/test.yaml | 1 + .../tests/install/static-linking/test.py | 24 ++ .../tests/install/static-linking/test.yaml | 1 + 28 files changed, 770 insertions(+), 114 deletions(-) create mode 100644 testsuite/tests/install/default-location/test.py create mode 100644 testsuite/tests/install/default-location/test.yaml create mode 100644 testsuite/tests/install/dynamic-linking/test.py create mode 100644 testsuite/tests/install/dynamic-linking/test.yaml create mode 100644 testsuite/tests/install/executable-dependency/test.py create mode 100644 testsuite/tests/install/executable-dependency/test.yaml create mode 100644 testsuite/tests/install/static-linking/test.py create mode 100644 testsuite/tests/install/static-linking/test.yaml diff --git a/doc/user-changes.md b/doc/user-changes.md index 5240b931..9347ac88 100644 --- a/doc/user-changes.md +++ b/doc/user-changes.md @@ -6,7 +6,30 @@ stay on top of `alr` new features. ## Release 1.3-dev -### New subcommand `alr install +### Installation of local crates + +PR [#1322](https://github.com/alire-project/alire/pull/1322) + +`alr install` without arguments performs the installation of the current crate. +With `--info`, it shows the contents of an installation prefix. For example: + +``` +$ alr -n init --bin mycrate && cd mycrate +$ alr install +$ alr install --info +Installation prefix found at /home/user/.alire +Contents: + mycrate=0.1.0-dev +``` + +Or, to install the hangman game: + +``` +$ alr get hangman && cd hangman* +$ alr install +``` + +### New subcommand `alr install` PR [#1302](https://github.com/alire-project/alire/pull/1302) @@ -18,7 +41,7 @@ This is a experimental feature that will see improvements and tweaks in further PRs and as we gather feedback on its usage. At present, only binary releases can be installed (e.g., compilers, `gprbuild`, -`gnatprove`, `gnatstudio`). There is no ability to uninstall releases either +`gnatprove`). There is no ability to uninstall releases either (but reinstallation can be forced). Only one version per executable can be installed, meaning that, for example, diff --git a/src/alire/alire-errors.ads b/src/alire/alire-errors.ads index 5bea9ed2..762366c5 100644 --- a/src/alire/alire-errors.ads +++ b/src/alire/alire-errors.ads @@ -84,7 +84,8 @@ package Alire.Errors with Preelaborate is function Wrap (This : Wrapper; Text : String) return Wrapper; -- Add an indented detail error msg to the current wrapping chain, unless - -- the wrapper is empty in which case the message will be top level. + -- the wrapper is empty in which case the message will be top level. If + -- Text is "", silently do nothing. function Wrap (This : Wrapper; Ex : Ada.Exceptions.Exception_Occurrence) return Wrapper; @@ -93,6 +94,9 @@ package Alire.Errors with Preelaborate is procedure Print (This : Wrapper); -- Complete the chain of errors and log it at error level + function Get (This : Wrapper) return String; + -- Return the entire error sequence + function Set (This : Wrapper) return String; -- Store the msgs in This and return an Id for use as exception message -- (see Set above). @@ -166,6 +170,9 @@ private is (This.Wrap (Get (Ex))); -- Start a chain with an exception message instead of a given text + function Get (This : Wrapper) return String + is (This.Text); + --------- -- Set -- --------- diff --git a/src/alire/alire-install.adb b/src/alire/alire-install.adb index eedb831c..6a92594f 100644 --- a/src/alire/alire-install.adb +++ b/src/alire/alire-install.adb @@ -4,15 +4,27 @@ with Alire.Dependencies.Containers; with Alire.Errors; with Alire.Origins; with Alire.Platforms.Current; -with Alire.Releases; with Alire.Solver; -with Semantic_Versioning; - package body Alire.Install is package Adirs renames Ada.Directories; + --------------------- + -- To_Image_Vector -- + --------------------- + + function To_Image_Vector (This : Installed_Milestones) + return AAA.Strings.Vector + is + begin + return Result : AAA.Strings.Vector do + for M of This loop + Result.Append (M.TTY_Image); + end loop; + end return; + end To_Image_Vector; + --------- -- Add -- --------- @@ -66,28 +78,6 @@ package body Alire.Install is Directories.Force_Delete (Prefix / Rel.Base_Folder); end Install_Binary; - -------------------- - -- Check_Conflict -- - -------------------- - - procedure Check_Conflict (Rel : Releases.Release) is - use type Semantic_Versioning.Version; - Installed : constant Installed_Milestones := Find_Installed (Prefix); - begin - if Installed.Contains (Rel.Name) then - if Installed (Rel.Name).Version = Rel.Version then - Recoverable_Error - ("Requested release " & Rel.Milestone.TTY_Image - & " is already installed"); - else - Recoverable_Error - ("Requested release " & Rel.Milestone.TTY_Image - & " has another version already installed: " - & Installed (Rel.Name).TTY_Image); - end if; - end if; - end Check_Conflict; - ----------------- -- Add_Targets -- ----------------- @@ -104,7 +94,7 @@ package body Alire.Install is Origins => (Binary_Archive => True, others => False)); begin - Check_Conflict (Rel); + Check_Conflict (Prefix, Rel); Install_Binary (Rel); end; end loop; @@ -138,18 +128,56 @@ package body Alire.Install is -- Ensure destination exists Add_Targets; - - Put_Info ("Installation complete."); end Add; + -------------------- + -- Check_Conflict -- + -------------------- + + procedure Check_Conflict (Prefix : Any_Path; Rel : Releases.Release) is + Installed : constant Installed_Milestones := Find_Installed (Prefix); + begin + if (for some M of Installed => M.Crate = Rel.Name) then + if Installed.Contains (Rel.Milestone) then + Recoverable_Error + ("Requested release " & Rel.Milestone.TTY_Image + & " is already installed"); + else + Recoverable_Error + (Errors.Wrap + ("Requested release " & Rel.Milestone.TTY_Image + & " has another version already installed: ", + To_Image_Vector (Find_Installed + (Prefix, Rel.Name)).Flatten (ASCII.LF))); + end if; + end if; + end Check_Conflict; + + -------------------- + -- Find_Installed -- + -------------------- + + function Find_Installed (Prefix : Any_Path; + Crate : Crate_Name) + return Installed_Milestones is + begin + return Result : Installed_Milestones do + for M of Find_Installed (Prefix) loop + if M.Crate = Crate then + Result.Include (M); + end if; + end loop; + end return; + end Find_Installed; + -------------------- -- Find_Installed -- -------------------- function Find_Installed (Prefix : Any_Path) - return Milestones.Containers.Maps.Map + return Installed_Milestones is - Result : Milestones.Containers.Maps.Map; + Result : Installed_Milestones; procedure Find (Item : Ada.Directories.Directory_Entry_Type; @@ -164,7 +192,7 @@ package body Alire.Install is Milestone : constant Milestones.Milestone := Milestones.New_Milestone (Name); begin - Result.Insert (Milestone.Crate, Milestone); + Result.Insert (Milestone); end; end if; end Find; @@ -219,6 +247,8 @@ package body Alire.Install is begin for Mil of Find_Installed (Prefix) loop if Mil.Crate = Crate then + Trace.Detail ("Deleting installation metadata file: " + & (Prefix / Metadata_Dir_In_Prefix / Mil.Image)); Adirs.Delete_File (Prefix / Metadata_Dir_In_Prefix / Mil.Image); end if; end loop; diff --git a/src/alire/alire-install.ads b/src/alire/alire-install.ads index 8357bc60..cc97c1b4 100644 --- a/src/alire/alire-install.ads +++ b/src/alire/alire-install.ads @@ -1,7 +1,8 @@ limited with Alire.Dependencies.Containers; with Alire.Directories; use Alire.Directories.Operators; -private with Alire.Milestones.Containers; +with Alire.Milestones.Containers; with Alire.Platforms.Folders; +with Alire.Releases; package Alire.Install is @@ -15,25 +16,38 @@ package Alire.Install is -- Resolve the dependencies and install the resulting releases. If a -- crate is given twice it will raise. + procedure Check_Conflict (Prefix : Any_Path; Rel : Releases.Release); + -- Will cause a recoverable error if Rel is already installed, or another + -- release from the same crate is. This is regardless Rel containing + -- executables or not. + procedure Info (Prefix : Any_Path); -- Display information about the given prefix -private - - Metadata_Dir_In_Prefix : constant Relative_Path - := "share" / "gpr" / "manifests"; - -- This is used by gprinstall and we will reuse it for our "fake" binary - -- installs. - - subtype Installed_Milestones is Milestones.Containers.Maps.Map; + subtype Installed_Milestones is Milestones.Containers.Sets.Set; function Find_Installed (Prefix : Any_Path) return Installed_Milestones; -- Identify installed releases in the prefix + function Find_Installed (Prefix : Any_Path; + Crate : Crate_Name) + return Installed_Milestones; + -- Return milestones for only the given crate + procedure Set_Installed (Prefix : Any_Path; Mil : Milestones.Milestone); + -- Stores an empty file in share/gpr/manifests/crate=version procedure Set_Not_Installed (Prefix : Any_Path; Crate : Crate_Name); - -- Any and all versions will be marked as not installed + -- Any and all versions will be marked as not installed. Intended for when + -- reinstalling a different executable crate version, as only one can be + -- installed. + +private + + Metadata_Dir_In_Prefix : constant Relative_Path + := "share" / "gpr" / "manifests"; + -- This is used by gprinstall and we will reuse it for our "fake" binary + -- installs. end Alire.Install; diff --git a/src/alire/alire-platforms-folders.ads b/src/alire/alire-platforms-folders.ads index 1cd1a3f1..690f425c 100644 --- a/src/alire/alire-platforms-folders.ads +++ b/src/alire/alire-platforms-folders.ads @@ -7,15 +7,15 @@ package Alire.Platforms.Folders is -- any other global data. Deleting it is akin to running alr afresh for -- the first time. -- On Linux/macOS it is ${XDG_CONFIG_HOME:-$HOME/.config}/alire - -- On Windows it is $Homedrive:$Homepath\.config\alire + -- On Windows it is $UserProfile\.config\alire function Cache return String; -- Folder for dependencies, global toolchains, and any other info that is -- not critical to lose. Can be deleted freely, it's repopulated on-demand. -- On Linux/macOS it is ${XDG_CACHE_HOME:-$HOME/.cache}/alire - -- On Windows it is $Homedrive:$Homepath\.cache\alire + -- On Windows it is $UserProfile\.cache\alire function Home return Absolute_Path; - -- $HOME (Linux/macOS) or $Homedrive:$Homepath (Windows) + -- $HOME (Linux/macOS) or $UserProfile (Windows) end Alire.Platforms.Folders; diff --git a/src/alire/alire-releases.ads b/src/alire/alire-releases.ads index dd578c42..ce341049 100644 --- a/src/alire/alire-releases.ads +++ b/src/alire/alire-releases.ads @@ -221,7 +221,9 @@ package Alire.Releases is P : Alire.Properties.Vector; With_Path : Boolean) return AAA.Strings.Vector; - -- with relative path on demand + -- With relative path on demand. Will always return at least the default + -- project file when nothing is declared in the manifest for regular + -- crates, but nothing for system/binary/external. function Deployment_Folder (R : Release) return Folder_String; -- The folder under which the release origin will be deployed diff --git a/src/alire/alire-roots.adb b/src/alire/alire-roots.adb index 8eb3d267..5b7c309e 100644 --- a/src/alire/alire-roots.adb +++ b/src/alire/alire-roots.adb @@ -5,9 +5,11 @@ with Alire.Dependencies.Containers; with Alire.Directories; with Alire.Environment; with Alire.Errors; +with Alire.Install; with Alire.Manifest; with Alire.Origins; with Alire.OS_Lib; +with Alire.Platforms.Current; with Alire.Properties.Actions.Executor; with Alire.Roots.Optional; with Alire.Shared; @@ -36,6 +38,7 @@ package body Alire.Roots is function Build (This : in out Root; Cmd_Args : AAA.Strings.Vector; Export_Build_Env : Boolean; + Build_All_Deps : Boolean := False; Saved_Profiles : Boolean := True) return Boolean is @@ -98,6 +101,7 @@ package body Alire.Roots is ------------------- procedure Call_Gprbuild (Release : Releases.Release) is + use AAA.Strings; use Directories.Operators; Count : constant Natural := Natural @@ -110,16 +114,17 @@ package body Alire.Roots is if not Is_Root and then not Release.Auto_GPR_With then Put_Info (TTY.Bold ("Not") & " pre-building " - & Utils.TTY.Name (Release.Name) + & Release.Milestone.TTY_Image & " (auto with disabled)", Trace.Detail); elsif not Is_Root and then Release.Executables (This.Environment).Is_Empty + and then not Build_All_Deps then Put_Info (TTY.Bold ("Not") & " pre-building " - & Utils.TTY.Name (Release.Name) + & Release.Milestone.TTY_Image & " (no executables declared)", Trace.Detail); @@ -130,7 +135,7 @@ package body Alire.Roots is (This.Environment, With_Path => True) loop Put_Info ("Building " - & Utils.TTY.Name (Release.Name) & "/" + & Release.Milestone.TTY_Image & "/" & TTY.URL (Gpr_File) & (if Count > 1 then " (" & AAA.Strings.Trim (Current'Image) @@ -221,6 +226,275 @@ package body Alire.Roots is end return; end Build_Context; + ------------- + -- Install -- + ------------- + + procedure Install + (This : in out Root; + Prefix : Absolute_Path; + Build : Boolean := True; + Export_Env : Boolean := True) + is + use AAA.Strings; + use Directories.Operators; + + type Actions is (Doinstall, -- no conflict + Reinstall, -- overwrite + Skip -- skip install + ); + + --------------------- + -- Check_Conflicts -- + --------------------- + + procedure Check_Conflicts (Rel : Releases.Release; + Action : out Actions) + is + begin + Action := Doinstall; -- unless we find some problem + + -- Crates declaring executables can only be installed once + + if not Rel.Executables (This.Environment).Is_Empty then + declare + Installed : constant Alire.Install.Installed_Milestones := + Alire.Install.Find_Installed + (Prefix, Rel.Name); + begin + + -- No problem if the version installed is the same one + + if Installed.Contains (Rel.Milestone) then + Action := (if Force then Reinstall else Skip); + Trace.Debug ("Already installed: " & Rel.Milestone.TTY_Image + & "; action: " & Action'Image); + return; + + elsif not Installed.Is_Empty then + + -- A different version exists, here we fail unless forced + + Recoverable_Error + (Errors.New_Wrapper + ("Release " & Rel.Milestone.TTY_Image & " conflicts " + & "with already installed " + & Alire.Install.Find_Installed + (Prefix, Rel.Name).First_Element.TTY_Image) + .Wrap ("Releases installing executables can be " + & "installed only once") + .Wrap ("Forcing this install will overwrite the " + & "release already installed") + .Get); + + Action := Reinstall; + + -- If forced to continue, we mark as uninstalled the + -- currently installed version. We are not doing cleanup + -- (yet?) so anything not overwritten will remain. + + Alire.Install.Set_Not_Installed (Prefix, Rel.Name); + end if; + end; + else + + -- This is a library, several versions are OK but we can skip one + -- already available. + + if Alire.Install.Find_Installed (Prefix).Contains (Rel.Milestone) + then + Action := (if Force then Reinstall else Skip); + Trace.Debug ("Already installed: " & Rel.Milestone.TTY_Image + & "; action: " & Action'Image); + + elsif Platforms.Current.Operating_System in Platforms.Windows + and then + not Alire.Install.Find_Installed (Prefix, Rel.Name).Is_Empty + then + + -- Several versions of the same library on Windows are a no-no. + -- Note that forcing through this will likely + + Recoverable_Error + (Errors.New_Wrapper + ("Release " & Rel.Milestone.TTY_Image & " conflicts " + & "with already installed " + & Alire.Install.Find_Installed + (Prefix, Rel.Name).First_Element.TTY_Image) + .Wrap ("Windows does not support installing multiple " + & "versions") + .Wrap ("Forcing will cause dependents on the other " + & "versions to break") + .Get); + + Alire.Install.Set_Not_Installed (Prefix, Rel.Name); + + end if; + + end if; + + end Check_Conflicts; + + ------------------- + -- Install_Inner -- + ------------------- + + procedure Install_Inner (This : in out Root; + Solution : Solutions.Solution; + State : Dependencies.States.State) is + pragma Unreferenced (Solution); + begin + if not State.Has_Release then + -- This may happen if there's a link to a raw project, or it's a + -- missing dependency that somehow didn't make the build fail. + Put_Warning ("Skipping " & State.As_Dependency.TTY_Image + & " without release in solution"); + return; + end if; + + -- Safe to get the release at this point + + declare + use all type Origins.Kinds; + Rel : constant Releases.Release := State.Release; + begin + + -- Binary crates may not include a GPR file, that we + -- would need to install its artifacts. This may be + -- common for compiler releases, so no need to be + -- exceedingly alarmist about it. + + if Rel.Project_Files (This.Environment, + With_Path => False).Is_Empty + then + declare + Text : constant String := + "Skipping " & Rel.Milestone.TTY_Image + & " without project files..."; + begin + if Rel.Provides (GNAT_Crate) + -- A compiler, we don't install those as dependency as it + -- doesn't make sense. + or else + Rel.Origin.Kind in External | System + -- A system or external, nothing for us to install + then + Put_Info (TTY.Dim (Text)); + else + -- A binary archive; Those are installed when given + -- as the explicit crate to install, but skipped as a + -- dependency. For binaries that want to be installed + -- even as dependencies, they should pack a project + -- file with Artifacts clauses. + Put_Warning (Text); + end if; + end; + end if; + + -- Install project files. Gprinstall doesn't mind installing + -- several times to the same manifest, which is handy for the rare + -- crate with more than one project file. Also, for uninstallable + -- crates such as system ones, this skips the step entirely. + + for Gpr_File of Rel.Project_Files (This.Environment, + With_Path => True) + loop + declare + Gpr_Path : constant Any_Path := + This.Release_Base (Rel.Name) / Gpr_File; + Action : Actions := Doinstall; + TTY_Target : constant String + := Rel.Milestone.TTY_Image & "/" & TTY.URL (Gpr_File); + begin + Check_Conflicts (Rel, Action); + -- Libraries with same version already installed, + -- binaries already installed. + + case Action is + when Doinstall => + Put_Info ("Installing " & TTY_Target & "..."); + when Reinstall => + Put_Warning ("Reinstalling " & TTY_Target & "..."); + when Skip => + Put_Info ("Skipping already installed " + & TTY_Target & "..."); + end case; + + case Action is + when Doinstall | Reinstall => + Spawn.Gprinstall + (Release => Rel, + Project_File => Ada.Directories + .Full_Name (Gpr_Path), + Prefix => Prefix, + Recursive => False, + Quiet => True, + Force => (Force or Action = Reinstall)); + + -- Say something if after installing a crate it + -- leaves no trace in the prefix. This is the + -- usual for statically linked libraries. + + if not Alire.Install + .Find_Installed (Prefix, Rel.Name) + .Contains (Rel.Milestone) + then + Trace.Detail ("Installation of " + & TTY_Target + & " had no effect"); + end if; + + when Skip => + null; + end case; + end; + end loop; + end; + end Install_Inner; + + begin + + -- Show some preliminary info + + Put_Info ("Starting installation of " + & This.Release.Element.Milestone.TTY_Image & " with " + & (if This.Solution.All_Dependencies.Is_Empty + then "no dependencies." + else "solution:")); + if not This.Solution.All_Dependencies.Is_Empty then + This.Solution.Print (Root => This.Release.Element, + Env => This.Environment, + Detailed => False, + Level => Info, + Prefix => " ", + Graph => False); + end if; + + -- Do a build to ensure same scenario seen by gprbuild and gprinstall + + if Build then + Assert (This.Build (Cmd_Args => AAA.Strings.Empty_Vector, + Export_Build_Env => Export_Env, + Build_All_Deps => True), + Or_Else => "Build failed, cannot perform installation"); + end if; + + if Export_Env then + This.Export_Build_Environment; + end if; + + -- Traverse dependencies in proper order just in case this has some + -- relevance to installation. + + -- We need to go over all projects in the solution because gprinstall + -- only installs binaries generated by the root project, even + -- when told to install recursively. So, instead we gprinstall + -- non-recursively each individual project in the solution. + -- Config projects, being abstract, need no installation. + + This.Traverse (Doing => Install_Inner'Access); + end Install; + ------------------ -- Direct_Withs -- ------------------ @@ -337,6 +611,11 @@ package body Alire.Roots is ---------------------------- procedure Generate_Configuration (This : in out Root) is + Guard : Directories.Guard (Directories.Enter (Path (This))) + with Unreferenced; + -- At some point inside the configuration generation process the config + -- is loaded and Config.Edit.Filepath requires being inside the root, + -- which can't be directly used because of circularities. begin This.Load_Configuration; This.Configuration.Generate_Config_Files (This); diff --git a/src/alire/alire-roots.ads b/src/alire/alire-roots.ads index 8f2ae24d..707ebc26 100644 --- a/src/alire/alire-roots.ads +++ b/src/alire/alire-roots.ads @@ -216,15 +216,28 @@ package Alire.Roots is function Build (This : in out Root; Cmd_Args : AAA.Strings.Vector; Export_Build_Env : Boolean; + Build_All_Deps : Boolean := False; Saved_Profiles : Boolean := True) return Boolean; -- Recursively build all dependencies that declare executables, and finally -- the root release. Also executes all pre-build/post-build actions for - -- all releases in the solution (even those not built). Returns True on - -- successful build. By default, profiles stored in the persistent crate - -- configuration are used (i.e. last explicit build); otherwise the ones - -- given in This.Configuration are used. These come in order of increasing - -- priority from: defaults -> manifests -> explicit set via API. + -- all releases in the solution (even those not built). Returns True + -- on successful build. When Build_All_Deps, all dependencies are built + -- explicitly; otherwise only those declaring executables are built. + -- This is useful when we are going to gprinstall dependencies + -- containing undeclared executables, which otherwise wouldn't be built. + -- Unfortunately, it's not mandatory to declare the default executable. + -- Saved_Profiles determines whether profiles stored in the persistent + -- crate configuration are used (i.e. last explicit build); otherwise + -- the ones given in This.Configuration are used. These come in order of + -- increasing priority from: defaults -> manifests -> explicit set via API. + + procedure Install + (This : in out Root; + Prefix : Absolute_Path; + Build : Boolean := True; + Export_Env : Boolean := True); + -- Call gprinstall on the releases in solution using --prefix=Prefix function Configuration (This : in out Root) return Crate_Configuration.Global_Config; diff --git a/src/alire/alire-solutions.adb b/src/alire/alire-solutions.adb index af527649..dd2ef6cd 100644 --- a/src/alire/alire-solutions.adb +++ b/src/alire/alire-solutions.adb @@ -697,14 +697,16 @@ package body Alire.Solutions is Root : Alire.Releases.Release; Env : Properties.Vector; Detailed : Boolean; - Level : Trace.Levels) is + Level : Trace.Levels; + Prefix : String := ""; + Graph : Boolean := True) is begin -- Outta here if nothing to print if not This.Solved then - Trace.Log ("Dependencies (solution):", Level); - Trace.Log (" No solving attempted", Level); + Trace.Log (Prefix & "Dependencies (solution):", Level); + Trace.Log (Prefix & " No solving attempted", Level); return; elsif This.Dependencies.Is_Empty then return; @@ -713,12 +715,12 @@ package body Alire.Solutions is -- Print all releases first, followed by the rest of dependencies if not This.Releases.Is_Empty then - Trace.Log ("Dependencies (solution):", Level); + Trace.Log (Prefix & "Dependencies (solution):", Level); for Dep of This.Dependencies loop if Dep.Has_Release then Trace.Log - (" " + (Prefix & " " & Utils.TTY.Name (Dep.Crate) & "=" & TTY.Version (Dep.Release.Version.Image) & (if Dep.Crate /= Dep.Release.Name -- provided by @@ -756,12 +758,12 @@ package body Alire.Solutions is -- although they're properly resolved. if (for some Dep of This.Dependencies => not Dep.Has_Release) then - Trace.Log ("Dependencies (external):", Level); + Trace.Log (Prefix & "Dependencies (external):", Level); for Dep of This.Dependencies loop if not This.State (Dep.Crate).Has_Release then Trace.Log - (" " + (Prefix & " " & Dep.TTY_Image & (if Dep.Is_Pinned or else Dep.Is_Linked then TTY.Emph (" (pinned)") @@ -788,7 +790,8 @@ package body Alire.Solutions is (Name => Dep.Crate, Env => Env) loop - Trace.Log (TTY.Emph (" Hint: ") & Hint, Level); + Trace.Log (Prefix & TTY.Emph (" Hint: ") & Hint, + Level); end loop; end if; end if; @@ -798,16 +801,16 @@ package body Alire.Solutions is -- Show forbidden, if any if not This.Forbidden (Env).Is_Empty then - Trace.Log ("Dependencies (forbidden):", Level); + Trace.Log (Prefix & "Dependencies (forbidden):", Level); for Dep of This.Forbidden (Env) loop - Trace.Log (" " & Dep.TTY_Image, Level); + Trace.Log (Prefix & " " & Dep.TTY_Image, Level); end loop; end if; - -- Textual and graphical dependency graph + -- Textual graph - if not This.Dependencies.Is_Empty then - Trace.Log ("Dependencies (graph):", Level); + if Graph and then not This.Dependencies.Is_Empty then + Trace.Log (Prefix & "Dependencies (graph):", Level); declare With_Root : constant Solution := This.Including (Root, Env, Add_Dependency => True); @@ -815,7 +818,7 @@ package body Alire.Solutions is Alire.Dependencies.Graphs .From_Solution (With_Root, Env); begin - Graph.Print (With_Root, Prefix => " "); + Graph.Print (With_Root, Prefix => Prefix & " "); end; end if; end Print; diff --git a/src/alire/alire-solutions.ads b/src/alire/alire-solutions.ads index bd8b81ef..6846a3ce 100644 --- a/src/alire/alire-solutions.ads +++ b/src/alire/alire-solutions.ads @@ -344,10 +344,13 @@ package Alire.Solutions is Root : Alire.Releases.Release; Env : Properties.Vector; Detailed : Boolean; - Level : Trace.Levels); + Level : Trace.Levels; + Prefix : String := ""; + Graph : Boolean := True); -- 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. + -- Detailed, extra information about origins is shown. When Prefix, prepend + -- to each line. When Graph, print a textual dependency graph at the end. procedure Print_Graph (This : Solution; Root : Alire.Releases.Release; diff --git a/src/alire/alire-spawn.adb b/src/alire/alire-spawn.adb index e393d20f..fb5369e2 100644 --- a/src/alire/alire-spawn.adb +++ b/src/alire/alire-spawn.adb @@ -48,4 +48,37 @@ package body Alire.Spawn is Understands_Verbose => True); end Gprbuild; + ---------------- + -- Gprinstall -- + ---------------- + + procedure Gprinstall + (Release : Releases.Release; + Project_File : Absolute_File; + Prefix : Absolute_Path; + Recursive : Boolean; + Quiet : Boolean; + Force : Boolean := Alire.Force; + Extra_Args : AAA.Strings.Vector := AAA.Strings.Empty_Vector) + is + use AAA.Strings; + begin + Spawn.Command + ("gprinstall", + AAA.Strings.Empty_Vector + & (if Recursive then To_Vector ("-r") else Empty_Vector) + & (if Quiet then To_Vector ("-q") else Empty_Vector) + & (if Force then To_Vector ("-f") else Empty_Vector) + & String'("--install-name=" & Release.Milestone.Image) + & "-m" -- minimal install (only needed sources) + & "-p" -- create missing dirs + & "--link-lib-subdir=bin" + -- Softlinks in same dir as executables, saves a path on Windows + & "--mode=usage" -- omit unwanted devel files + & String'("--prefix=" & Prefix) + & "-P" & Project_File + & Extra_Args + ); + end Gprinstall; + end Alire.Spawn; diff --git a/src/alire/alire-spawn.ads b/src/alire/alire-spawn.ads index f5902506..994d39b2 100644 --- a/src/alire/alire-spawn.ads +++ b/src/alire/alire-spawn.ads @@ -1,5 +1,7 @@ with AAA.Strings; +with Alire.Releases; + package Alire.Spawn is -- Encapsulates all external spawns @@ -17,7 +19,19 @@ package Alire.Spawn is Extra_Args : AAA.Strings.Vector); -- Launches gprbuild for the building of a crate. -- Extra args can be -Xblah detected from command-line. - -- Out-of-tree build takes place in - -- $crate / Alire.Paths.Build_Folder ($crate/alire/build). + + procedure Gprinstall + (Release : Releases.Release; + Project_File : Absolute_File; + Prefix : Absolute_Path; + Recursive : Boolean; + Quiet : Boolean; + Force : Boolean := Alire.Force; + Extra_Args : AAA.Strings.Vector := AAA.Strings.Empty_Vector); + -- Launches + -- gprinstall [-f] -m -p [-r] \ + -- --mode=usage -P Project_File --prefix=Prefix -- Extra_Args \ + -- --install-name=Release.Milestone.Image \ + -- --link-lib-dir=Prefix/bin end Alire.Spawn; diff --git a/src/alire/os_windows/alire-platforms-folders__windows.adb b/src/alire/os_windows/alire-platforms-folders__windows.adb index f7b66a29..24a733c8 100644 --- a/src/alire/os_windows/alire-platforms-folders__windows.adb +++ b/src/alire/os_windows/alire-platforms-folders__windows.adb @@ -9,7 +9,7 @@ package body Alire.Platforms.Folders is ---------- function Home return Absolute_Path - is (OS_Lib.Getenv ("HOMEDRIVE") & OS_Lib.Getenv ("HOMEPATH")); + is (OS_Lib.Getenv ("USERPROFILE")); ----------- -- Cache -- diff --git a/src/alr/alr-commands-install.adb b/src/alr/alr-commands-install.adb index a5fbf7c5..6bc82d89 100644 --- a/src/alr/alr-commands-install.adb +++ b/src/alr/alr-commands-install.adb @@ -3,6 +3,8 @@ with Ada.Directories; with Alire.Dependencies.Containers; with Alire.Install; +with Stopwatch; + package body Alr.Commands.Install is package Adirs renames Ada.Directories; @@ -11,8 +13,20 @@ package body Alr.Commands.Install is -- Validate -- -------------- - procedure Validate (Cmd : Command) is null; - -- Nothing to validate for now + procedure Validate (Cmd : in out Command; Args : AAA.Strings.Vector) is + begin + -- If nothing given, we must be in workspace + if not Cmd.Info and then Args.Is_Empty then + Cmd.Requires_Valid_Session + (Error => "Give a crate name to install or enter a local crate"); + end if; + + if Cmd.Info and then not Args.Is_Empty then + Reportaise_Wrong_Arguments + ("You cannot request information and " + & "install crates simultaenously"); + end if; + end Validate; ------------- -- Execute -- @@ -22,20 +36,29 @@ package body Alr.Commands.Install is procedure Execute (Cmd : in out Command; Args : AAA.Strings.Vector) is - Global_Prefix : constant Alire.Absolute_Path := - Adirs.Full_Name - (if Cmd.Prefix.all /= "" - then Cmd.Prefix.all - else Alire.Install.Default_Prefix); - begin + Prefix : constant Alire.Absolute_Path := + Adirs.Full_Name + (if Cmd.Prefix.all /= "" + then Cmd.Prefix.all + else Alire.Install.Default_Prefix); - Cmd.Validate; + Timer : Stopwatch.Instance; + begin + Cmd.Validate (Args); - if Args.Is_Empty then + if Cmd.Info then -- Display info on default/given prefix. - Alire.Install.Info (Global_Prefix); + Alire.Install.Info (Prefix); + + elsif Args.Is_Empty then + + -- Install local crate first if requested + + Alire.Install.Check_Conflict (Prefix, Cmd.Root.Release); + Cmd.Root.Install (Prefix => Prefix, + Export_Env => True); else @@ -48,9 +71,13 @@ package body Alr.Commands.Install is Deps.Append (Alire.Dependencies.From_String (Img)); end loop; - Alire.Install.Add (Global_Prefix, Deps); + Alire.Install.Add (Prefix, Deps); end; + Alire.Put_Success ("Install to " & TTY.URL (Prefix) + & " finished successfully in " + & TTY.Bold (Timer.Image) & " seconds."); + end if; end Execute; @@ -58,7 +85,7 @@ package body Alr.Commands.Install is -- Long_Description -- ---------------------- - Binaries : constant String := "gnat, gnatprove, gprbuild, gnatstudio"; + Binaries : constant String := "gnat, gnatprove, gprbuild"; overriding function Long_Description (Cmd : Command) @@ -101,6 +128,11 @@ package body Alr.Commands.Install is "", "--prefix=", "Override installation prefix (default is " & TTY.URL ("${CRATE_ROOT}/alire/prefix)") & ")"); + + Define_Switch (Config, + Cmd.Info'Access, + "", "--info", + "Show info about a installation prefix"); end Setup_Switches; end Alr.Commands.Install; diff --git a/src/alr/alr-commands-install.ads b/src/alr/alr-commands-install.ads index 29c4041f..65a738f5 100644 --- a/src/alr/alr-commands-install.ads +++ b/src/alr/alr-commands-install.ads @@ -14,11 +14,15 @@ package Alr.Commands.Install is function Switch_Parsing (This : Command) return CLIC.Subcommand.Switch_Parsing_Kind is (CLIC.Subcommand.Parse_All); - -- For this command we want the args after -- to pass them to gprinstall + -- To keep things simple we don't forward switches to neither gprbuild nor + -- gprinstall, and any scenarios have to be set up via environment e.g., + -- $ LIBRARY_TYPE=relocatable alr install . We could improve on + -- this down the line. overriding procedure Execute (Cmd : in out Command; Args : AAA.Strings.Vector); + -- Will both build + gprinstall, to ensure both see the same environment overriding function Long_Description (Cmd : Command) @@ -35,11 +39,12 @@ package Alr.Commands.Install is overriding function Usage_Custom_Parameters (Cmd : Command) return String - is ("[switches] [crate[versions]]..."); + is ("[[--info] | [crate[versions]]...]"); private type Command is new Commands.Command with record Target : aliased GNAT.Strings.String_Access; -- Crate[version] to install Prefix : aliased GNAT.Strings.String_Access; -- Prefix for gprinstall + Info : aliased Boolean := False; -- Show prefix info end record; end Alr.Commands.Install; diff --git a/src/alr/alr-commands.adb b/src/alr/alr-commands.adb index da4b0dd0..ac1eb1bb 100644 --- a/src/alr/alr-commands.adb +++ b/src/alr/alr-commands.adb @@ -279,8 +279,9 @@ package body Alr.Commands is -- Requires_Valid_Session -- ---------------------------- - procedure Requires_Valid_Session (Cmd : in out Command'Class; - Sync : Boolean := True) is + procedure Requires_Valid_Session (Cmd : in out Command'Class; + Sync : Boolean := True; + Error : String := "") is use Alire; Unchecked : Alire.Roots.Optional.Root renames Cmd.Optional_Root; @@ -315,8 +316,13 @@ package body Alr.Commands is if not Unchecked.Is_Valid then Raise_Checked_Error - (Alire.Errors.Wrap - ("Cannot continue with invalid session", Unchecked.Message)); + (Alire.Errors.New_Wrapper + .Wrap + (if Error /= "" + then Error + else "Cannot continue with invalid session") + .Wrap (Unchecked.Message) + .Get); end if; Unchecked.Value.Check_Stored; diff --git a/src/alr/alr-commands.ads b/src/alr/alr-commands.ads index 19dab9c2..2c54046f 100644 --- a/src/alr/alr-commands.ads +++ b/src/alr/alr-commands.ads @@ -59,13 +59,14 @@ package Alr.Commands is -- Unless Force_Reload, if the index is not empty we no nothing. When -- strict, don't allow unknown values in enums. - procedure Requires_Valid_Session (Cmd : in out Command'Class; - Sync : Boolean := True); + procedure Requires_Valid_Session (Cmd : in out Command'Class; + Sync : Boolean := True; + Error : String := ""); -- Verifies that a valid working dir is in scope. After calling it, -- Cmd.Root will be usable if alr was run inside a Root. If Sync, enforce -- that the manifest, lockfile and dependencies on disk are in sync, by -- performing a silent update. If not Sync, only a minimal empty lockfile - -- is created. + -- is created. If Error, replace the first generic error message with it. procedure Load (Cmd : Command'Class; Crate : Alire.Crate_Name; diff --git a/testsuite/drivers/asserts.py b/testsuite/drivers/asserts.py index c866b874..bb95002e 100644 --- a/testsuite/drivers/asserts.py +++ b/testsuite/drivers/asserts.py @@ -10,6 +10,7 @@ import difflib from drivers.alr import run_alr from drivers.helpers import contents, lines_of +from typing import List def indent(text, prefix=' '): """ @@ -81,3 +82,17 @@ def match_solution(regex, escape=False, whole=False): (regex if not escape else re.escape(regex)) + wrap, p.out) + +def assert_installed(prefix : str, milestones : List[str]): + """ + Check that installed releases match those given in milestones + """ + + p = run_alr("install", "--info", f"--prefix={prefix}", quiet=False) + + milestones.sort() + + assert_eq(f"Installation prefix found at {prefix}\n" + "Contents:\n" + " " + "\n ".join(milestones) + "\n", + p.out) \ No newline at end of file diff --git a/testsuite/drivers/helpers.py b/testsuite/drivers/helpers.py index c82bc5a3..d3e6a651 100644 --- a/testsuite/drivers/helpers.py +++ b/testsuite/drivers/helpers.py @@ -14,7 +14,7 @@ import stat # Return the entries (sorted) under a given folder, both folders and files -# Optionally, return only those matching regex +# Optionally, return only those matching regex. Uses '/' always as separator. def contents(dir, regex=""): assert os.path.exists(dir), "Bad path for enumeration: {}".format(dir) if regex != "": @@ -231,3 +231,12 @@ def md5sum(file): file_hash.update(chunk) return file_hash.hexdigest() + + +def replace_in_file(filename : str, old : str, new : str): + """ + Replace all occurrences of a string in a file + """ + old_contents = content_of(filename) + with open(filename, "wt") as file: + file.write(old_contents.replace(old, new)) diff --git a/testsuite/tests/install/binary-release/test.py b/testsuite/tests/install/binary-release/test.py index 33a20916..27999557 100644 --- a/testsuite/tests/install/binary-release/test.py +++ b/testsuite/tests/install/binary-release/test.py @@ -5,7 +5,7 @@ Test deployment of a binary release and basic `alr install` use # NOTE: this test only runs on Linux from drivers.alr import run_alr, init_local_crate -from drivers.asserts import assert_eq, assert_match +from drivers.asserts import assert_eq, assert_match, assert_installed from subprocess import run import platform @@ -15,18 +15,19 @@ if platform.system() != "Linux": print('SUCCESS') exit(0) -PREFIX=f"--prefix={os.getcwd()}/install" +PREFIX=os.path.join(os.getcwd(), "install") +PREFIX_ARG=f"--prefix={PREFIX}" # Check that the prefix is empty -p = run_alr("install", PREFIX, quiet=False) +p = run_alr("install", "--info", PREFIX_ARG, quiet=False) assert_match("There is no installation at prefix .*", p.out) # Install the binary crate -p = run_alr("install", PREFIX, "crate", quiet=False) -assert_eq("""Note: Deploying crate=1.0.0... +p = run_alr("install", PREFIX_ARG, "crate", quiet=False) +assert_match("""Note: Deploying crate=1.0.0... Note: Installing crate=1.0.0... -Note: Installation complete. +Success: Install to .* finished successfully in .* seconds. """, p.out) @@ -38,27 +39,23 @@ assert_eq("Bin crate OK\n", p.stdout.decode()) # Verify release cannot be reinstalled assert_match(".*Requested release crate=1.0.0 is already installed.*", - run_alr("install", PREFIX, "crate", + run_alr("install", PREFIX_ARG, "crate", quiet=False, complain_on_error=False).out) # Verify another version cannot be installed -assert_match(".*Requested release crate=0.1.0 has another version already installed: crate=1.0.0.*", - run_alr("install", PREFIX, "crate=0.1.0", +assert_match(".*Requested release crate=0.1.0 has another version already installed:\n" + ".* crate=1.0.0.*", + run_alr("install", PREFIX_ARG, "crate=0.1.0", quiet=False, complain_on_error=False).out) # Force install of the same crate and see no failure -run_alr("install", PREFIX, "crate=0.1.0", force=True) +run_alr("install", PREFIX_ARG, "crate=0.1.0", force=True) # Recheck output p = run(f"{os.getcwd()}/install/bin/crate", capture_output=True) assert_eq("Bin crate OK\n", p.stdout.decode()) # Check contents of the prefix -p = run_alr("install", PREFIX, quiet=False) -assert_match("""Installation prefix found at .* -Contents: - crate=0.1.0 -""", - p.out) +assert_installed(PREFIX, ["crate=0.1.0"]) print('SUCCESS') \ No newline at end of file diff --git a/testsuite/tests/install/default-location/test.py b/testsuite/tests/install/default-location/test.py new file mode 100644 index 00000000..f5b0350b --- /dev/null +++ b/testsuite/tests/install/default-location/test.py @@ -0,0 +1,39 @@ +""" +Test installation to default location +""" + +from drivers.alr import run_alr, init_local_crate +from drivers.asserts import assert_eq +from drivers.helpers import on_windows + +import os + +# Override HOME to our test location so it is pristine + +if on_windows(): + os.environ["USERPROFILE"] = os.getcwd() +else: + os.environ["HOME"] = os.getcwd() + +# Disable msys2 as the home change doesn't sit well with it and we don't need it here. +# This is a workaround because trying to disable it via config doesn't work on 1st run: +# run_alr("config", "--global", "--set", "msys2.do_not_install", "true") +if on_windows(): + os.makedirs(os.path.join(os.environ["USERPROFILE"], ".cache", "alire", "msys64")) + +p = run_alr("install", "--info", quiet=False) +assert_eq(f"There is no installation at prefix {os.path.join(os.getcwd(), '.alire')}\n", + p.out) + +# Install a local crate to check prefix creation and contents at expected place +init_local_crate() +run_alr("install") +os.chdir("..") +p = run_alr("install", "--info", quiet=False) +assert_eq(f"Installation prefix found at {os.path.join(os.getcwd(), '.alire')}\n" + "Contents:\n" + " xxx=0.1.0-dev\n", + p.out) + + +print('SUCCESS') diff --git a/testsuite/tests/install/default-location/test.yaml b/testsuite/tests/install/default-location/test.yaml new file mode 100644 index 00000000..32c747b3 --- /dev/null +++ b/testsuite/tests/install/default-location/test.yaml @@ -0,0 +1 @@ +driver: python-script diff --git a/testsuite/tests/install/dynamic-linking/test.py b/testsuite/tests/install/dynamic-linking/test.py new file mode 100644 index 00000000..5966f085 --- /dev/null +++ b/testsuite/tests/install/dynamic-linking/test.py @@ -0,0 +1,53 @@ +""" +Test installation of dynamically linked executables +""" + +from drivers.alr import run_alr, init_local_crate, alr_with, alr_manifest +from drivers.asserts import assert_installed +from drivers.helpers import replace_in_file, on_windows + +import os + +PREFIX=os.path.join(os.getcwd(), "install") +PREFIX_ARG=f"--prefix={PREFIX}" + +# Init a binary crate, add a dependency, and with dynamic linking both should +# appear as installed + +os.environ["LIBRARY_TYPE"] = "relocatable" + +init_local_crate() +init_local_crate(name="dep", binary=False, enter=False) +alr_with("dep", path="dep") +run_alr("install", PREFIX_ARG) +assert_installed(PREFIX, ["dep=0.1.0-dev", "xxx=0.1.0-dev"]) + +# Installing a second binary with the same dependency should be OK + +init_local_crate(name="yyy") +alr_with("dep", path="../dep") # portable paths required for pins +run_alr("install", PREFIX_ARG) +assert_installed(PREFIX, ["dep=0.1.0-dev", + "xxx=0.1.0-dev", + "yyy=0.1.0-dev"]) + +# Installing a second library version should be OK (not on Windows) +# gprinstall for Windows do not uses Library_Version to create e.g. +# libBlah.so.1 -> libBlah.dll.1. All libraries are simply libBlah.dll + +if not on_windows(): + os.chdir("..") + replace_in_file(os.path.join("dep", alr_manifest()), + "0.1.0-dev", "0.2.0") + init_local_crate(name="zzz") + alr_with("dep", path="../dep") + run_alr("install", PREFIX_ARG) + assert_installed(PREFIX, ["dep=0.1.0-dev", + "dep=0.2.0", + "xxx=0.1.0-dev", + "yyy=0.1.0-dev", + "zzz=0.1.0-dev" + ]) + +print('SUCCESS') + diff --git a/testsuite/tests/install/dynamic-linking/test.yaml b/testsuite/tests/install/dynamic-linking/test.yaml new file mode 100644 index 00000000..32c747b3 --- /dev/null +++ b/testsuite/tests/install/dynamic-linking/test.yaml @@ -0,0 +1 @@ +driver: python-script diff --git a/testsuite/tests/install/executable-dependency/test.py b/testsuite/tests/install/executable-dependency/test.py new file mode 100644 index 00000000..7fe338a7 --- /dev/null +++ b/testsuite/tests/install/executable-dependency/test.py @@ -0,0 +1,50 @@ +""" +Test installation of executables in dependencies +""" + +from drivers.alr import run_alr, init_local_crate, alr_with, alr_manifest +from drivers.asserts import assert_contents, assert_installed, assert_match +from drivers.helpers import on_windows, replace_in_file + +import os + +START=os.getcwd() +PREFIX=os.path.join(os.getcwd(), "install") +PREFIX_ARG=f"--prefix={PREFIX}" + +# Init a binary crate, add an executable dependency, and both should +# end being installed. + +os.environ["LIBRARY_TYPE"] = "static" # It's the default, but just in case + +init_local_crate() +init_local_crate(name="dep", binary=True, enter=False) +alr_with("dep", path="dep") +run_alr("install", PREFIX_ARG) + +# Check gprbuild manifests +assert_installed(PREFIX, ["dep=0.1.0-dev", + "xxx=0.1.0-dev"]) + +ext = (".exe" if on_windows() else "") + +# Check actual executables in place +os.chdir(os.path.join("..", "install", "bin")) # simplifies paths in check +assert_contents(".", + [f"./dep{ext}", + f"./xxx{ext}"]) + +# Attempting to install a different version of the dependency should fail, +# since only a single version of an executable can live in a prefix + +os.chdir(START) + +replace_in_file(os.path.join("xxx", "dep", alr_manifest()), + "0.1.0-dev", "0.2.0") +init_local_crate(name="yyy") +alr_with("dep", path="../xxx/dep") +p = run_alr("install", PREFIX_ARG, complain_on_error=False) +assert_match(".*Release dep=0.2.0 conflicts with already installed dep=0.1.0-dev.*", + p.out) + +print("SUCCESS") \ No newline at end of file diff --git a/testsuite/tests/install/executable-dependency/test.yaml b/testsuite/tests/install/executable-dependency/test.yaml new file mode 100644 index 00000000..32c747b3 --- /dev/null +++ b/testsuite/tests/install/executable-dependency/test.yaml @@ -0,0 +1 @@ +driver: python-script diff --git a/testsuite/tests/install/static-linking/test.py b/testsuite/tests/install/static-linking/test.py new file mode 100644 index 00000000..ab913791 --- /dev/null +++ b/testsuite/tests/install/static-linking/test.py @@ -0,0 +1,24 @@ +""" +Test installation of statically linked executables +""" + +from drivers.alr import run_alr, init_local_crate, alr_with +from drivers.asserts import assert_installed + +import os + +PREFIX=os.path.join(os.getcwd(), "install") +PREFIX_ARG=f"--prefix={PREFIX}" + +# Init a binary crate, add a dependency, and by default with static linking +# only the root dependency should show as installed + +os.environ["LIBRARY_TYPE"] = "static" # It's the default, but just in case + +init_local_crate() +init_local_crate(name="dep", binary=False, enter=False) +alr_with("dep", path="dep") +run_alr("install", PREFIX_ARG) +assert_installed(PREFIX, ["xxx=0.1.0-dev"]) + +print('SUCCESS') diff --git a/testsuite/tests/install/static-linking/test.yaml b/testsuite/tests/install/static-linking/test.yaml new file mode 100644 index 00000000..32c747b3 --- /dev/null +++ b/testsuite/tests/install/static-linking/test.yaml @@ -0,0 +1 @@ +driver: python-script -- 2.39.5