From e2442fc7158ffb7058559ee917b6665207747171 Mon Sep 17 00:00:00 2001 From: Ryan Gibb Date: Wed, 1 May 2024 14:39:38 +0100 Subject: [PATCH] provide Nix depexts with shell environment variables --- src/client/opamSolution.ml | 6 ++-- src/state/opamEnv.ml | 9 +++++- src/state/opamSysInteract.ml | 56 ++++++++++++++++++++++++++++++----- src/state/opamSysInteract.mli | 6 ++-- 4 files changed, 63 insertions(+), 14 deletions(-) diff --git a/src/client/opamSolution.ml b/src/client/opamSolution.ml index 4b7576483af..8b37e0c652f 100644 --- a/src/client/opamSolution.ml +++ b/src/client/opamSolution.ml @@ -1181,7 +1181,7 @@ let install_depexts ?(force_depext=false) ?(confirm=true) t packages = let answer = let pkgman = OpamConsole.colorise `yellow - (OpamSysInteract.package_manager_name ~env config) + (OpamSysInteract.package_manager_name ~env t.switch config) in OpamConsole.menu ~unsafe_yes:`Yes ~default:`Yes ~no:`Quit "opam believes some required external dependencies are missing. opam \ @@ -1213,7 +1213,7 @@ let install_depexts ?(force_depext=false) ?(confirm=true) t packages = | `Quit -> give_up_msg (); OpamStd.Sys.exit_because `Aborted and print_command sys_packages = let commands = - OpamSysInteract.install_packages_commands ~env config sys_packages + OpamSysInteract.install_packages_commands ~env t.switch config sys_packages |> List.map (fun ((`AsAdmin c | `AsUser c), a) -> c::a) in OpamConsole.formatted_msg @@ -1242,7 +1242,7 @@ let install_depexts ?(force_depext=false) ?(confirm=true) t packages = | `Quit -> give_up () and auto_install t sys_packages = try - OpamSysInteract.install ~env config sys_packages; (* handles dry_run *) + OpamSysInteract.install ~env t.switch config sys_packages; (* handles dry_run *) map_sysmap (fun _ -> OpamSysPkg.Set.empty) t with Failure msg -> OpamConsole.error "%s" msg; diff --git a/src/state/opamEnv.ml b/src/state/opamEnv.ml index 8c783f1b5cf..da70a4938ac 100644 --- a/src/state/opamEnv.ml +++ b/src/state/opamEnv.ml @@ -540,7 +540,14 @@ let compute_updates ?(force_path=false) st = in List.map resolve_separator_and_format updates in - switch_env @ pkg_env @ man_path @ [path] + let nix_env = + let open OpamFilename in + create (OpamPath.Switch.meta OpamStateConfig.(!r.root_dir) st.switch) (basename (raw "nix.env")) + |> OpamFile.make + |> OpamFile.Environment.read_opt + |> Option.fold ~none:[] ~some:(List.map resolve_separator_and_format) + in + switch_env @ pkg_env @ man_path @ [path] @ nix_env let updates_common ~set_opamroot ~set_opamswitch root switch = let root = diff --git a/src/state/opamSysInteract.ml b/src/state/opamSysInteract.ml index 535bd2f73b7..d936ceca053 100644 --- a/src/state/opamSysInteract.ml +++ b/src/state/opamSysInteract.ml @@ -134,6 +134,7 @@ type families = | Macports | Msys2 | Netbsd + | Nix | Openbsd | Suse @@ -198,6 +199,7 @@ let family ~env () = | "gentoo" -> Gentoo | "homebrew" -> Homebrew | "macports" -> Macports + | "nixos" -> Nix | "macos" -> failwith "External dependency handling for macOS requires either \ @@ -896,6 +898,16 @@ let packages_status ?(env=OpamVariable.Map.empty) config packages = |> package_set_of_pkgpath in compute_sets sys_installed + | Nix -> + (* We say all requested packages are available but uninstalled. + We could check that these packages are available in Nixpkgs, + but that would involve an expensive Nixpkgs evaluation. + Saying no packages are installed results in a warning that + conf packages depend on a 'system package that can no longer + be found.' But omitting them will mean that they won't be + added to the Nix derivation. + *) + packages, OpamSysPkg.Set.empty; | Openbsd -> let sys_installed = run_query_command "pkg_info" ["-mqP"] @@ -931,7 +943,7 @@ let packages_status ?(env=OpamVariable.Map.empty) config packages = (* Install *) -let install_packages_commands_t ?(env=OpamVariable.Map.empty) config sys_packages = +let install_packages_commands_t ?(env=OpamVariable.Map.empty) switch config sys_packages = let unsafe_yes = OpamCoreConfig.answer_is `unsafe_yes in let yes ?(no=[]) yes r = if unsafe_yes then @@ -1009,14 +1021,43 @@ let install_packages_commands_t ?(env=OpamVariable.Map.empty) config sys_package [`AsUser (Commands.msys2 config), "-Su"::"--noconfirm"::packages], None | Netbsd -> [`AsAdmin "pkgin", yes ["-y"] ("install" :: packages)], None + | Nix -> + let open OpamFilename in + let dir = OpamPath.Switch.meta OpamStateConfig.(!r.root_dir) switch in + let drvFile = create dir (basename (raw "env.nix")) in + let packages = String.concat " " (List.rev (OpamSysPkg.Set.fold (fun p l -> OpamSysPkg.to_string p :: l) sys_packages [])) in + let contents = +{|{ pkgs ? import {} }: +with pkgs; +stdenv.mkDerivation { + name = "opam-nix-env"; + nativeBuildInputs = with buildPackages; [ |} ^ packages ^ {| ]; + + phases = [ "buildPhase" ]; + + buildPhase = '' +vars=("NIX_CC" "NIX_CC_FLAGS" "NIX_CFLAGS_COMPILE" "NIX_CC_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu" "NIX_LDFLAGS" "PKG_CONFIG_PATH") +for var in "''${vars[@]}"; do + escaped="$(echo "''${!var}" | sed -e 's/^$/@/' -e 's/ /\\ /g')" + echo "$var = $escaped Nix" >> $out +done +echo "PATH += $PATH Nix" >> $out + ''; + + preferLocalBuild = true; +} +|} in + write drvFile contents; + let envFile = create dir (basename (raw "nix.env")) |> OpamFilename.to_string in + [`AsUser "nix-build", [ OpamFilename.to_string drvFile; "--out-link"; envFile ] ], None | Openbsd -> [`AsAdmin "pkg_add", yes ~no:["-i"] ["-I"] packages], None | Suse -> [`AsAdmin "zypper", yes ["--non-interactive"] ("install"::packages)], None -let install_packages_commands ?env config sys_packages = - fst (install_packages_commands_t ?env config sys_packages) +let install_packages_commands ?env switch config sys_packages = + fst (install_packages_commands_t ?env switch config sys_packages) -let package_manager_name ?env config = - match install_packages_commands ?env config OpamSysPkg.Set.empty with +let package_manager_name ?env switch config = + match install_packages_commands ?env switch config OpamSysPkg.Set.empty with | ((`AsAdmin pkgman | `AsUser pkgman), _) :: _ -> pkgman | [] -> assert false @@ -1041,11 +1082,11 @@ let sudo_run_command ?(env=OpamVariable.Map.empty) ?vars cmd args = "failed with exit code %d at command:\n %s" code (String.concat " " (cmd::args)) -let install ?env config packages = +let install ?env switch config packages = if OpamSysPkg.Set.is_empty packages then log "Nothing to install" else - let commands, vars = install_packages_commands_t ?env config packages in + let commands, vars = install_packages_commands_t ?env switch config packages in let vars = OpamStd.Option.map (List.map (fun x -> `add, x)) vars in List.iter (fun (cmd, args) -> @@ -1069,6 +1110,7 @@ let update ?(env=OpamVariable.Map.empty) config = | Macports -> Some (`AsAdmin "port", ["sync"]) | Msys2 -> Some (`AsUser (Commands.msys2 config), ["-Sy"]) | Netbsd -> None + | Nix -> None | Openbsd -> None | Suse -> Some (`AsAdmin "zypper", ["--non-interactive"; "refresh"]) in diff --git a/src/state/opamSysInteract.mli b/src/state/opamSysInteract.mli index 754e975a49e..1dba02687f1 100644 --- a/src/state/opamSysInteract.mli +++ b/src/state/opamSysInteract.mli @@ -23,16 +23,16 @@ val packages_status: (* Return the commands to run to install given system packages. [env] is used to determine host specification. *) val install_packages_commands: - ?env:gt_variables -> OpamFile.Config.t -> OpamSysPkg.Set.t -> + ?env:gt_variables -> OpamSwitch.t -> OpamFile.Config.t -> OpamSysPkg.Set.t -> ([`AsAdmin of string | `AsUser of string] * string list) list (* Install given system packages, by calling local system package manager. [env] is used to determine host specification. *) -val install: ?env:gt_variables -> OpamFile.Config.t -> OpamSysPkg.Set.t -> unit +val install: ?env:gt_variables -> OpamSwitch.t -> OpamFile.Config.t -> OpamSysPkg.Set.t -> unit val update: ?env:gt_variables -> OpamFile.Config.t -> unit -val package_manager_name: ?env:gt_variables -> OpamFile.Config.t -> string +val package_manager_name: ?env:gt_variables -> OpamSwitch.t -> OpamFile.Config.t -> string (* Determine if special packages may need installing to enable other repositories.