如何从Nix上的Haskell调用不在我的路径

时间:2017-07-11 05:29:49

标签: haskell executable nix nixos

我希望编写一些调用可执行文件的Haskell作为其工作的一部分;并在nixOS主机上安装它。我不希望可执行文件在我的PATH中(依赖于它会破坏nix的漂亮依赖模型)。

如果这是一个Perl脚本,我会有一个简单的构建器,它查找某种格式的字符串,并根据.nix文件中声明的依赖项将它们替换为可执行文件名。但是,对于哈斯克尔来说,基于阴谋的建筑看起来有点困难。

在nix上的Haskell代码中,是否有一个标准习惯用于在构建时(包括开发期间和安装时)编译可执行文件的路径?

为了一个具体的例子,这里有一个简单的"脚本":

import System.Process ( readProcess )

main = do
  stdout <- readProcess "hostname" [] ""
  putStrLn $ "Hostname: " ++ stdout

我希望能够编译运行它(原则上)而不依赖于PATH中的主机名,而是用完整的/ nix / store / -inetutils- / bin / hostname路径替换主机名,因此也在nix下获得依赖管理的好处。

这可以通过使用shell(或类似)脚本来管理,该脚本使用上面定义的替换方案构建,该脚本设置haskell可执行文件期望的环境;但是仍然需要通过cabal.mkDerivation进行一些自举,因为我是OptParse-Applicative的bash完成的爱好者,我不愿意用另一个脚本来减慢它的速度我点击了Tab键。但如果这需要什么,那就足够了。

我确实通过cabal.mkDerivation查看了某些预制步骤,但是如果它在那里我没有看到它。

谢谢,

3 个答案:

答案 0 :(得分:3)

假设您正在Nix中构建Haskell应用程序,您可以通过Nix表达式修补配置文件。有关如何执行此操作的示例,请查看this small project

关键是你可以定义一个postConfigure钩子,如this

pkgs.haskell.lib.overrideCabal yourProject (old: {
  postConfigure = ''
    substituteInPlace src/Configuration.hs --replace 'helloPrefix = Nothing' 'helloPrefix = Just "${pkgs.hello}"'
  '';
})

答案 1 :(得分:2)

我在nix 1 中使用xmonad构建的操作是将可执行路径称为@@compton@@/bin/compton之类的东西。然后我使用这样的脚本生成我的default.nix文件:

#!/usr/bin/env bash

set -eu

packages=($(grep '@@[^@]*@@' src/Main.hs | sed -e 's/.*@@\(.*\)@@.*/\1/' | sort -u))

extra_args=()
for p in "${packages[@]}"; do
    extra_args+=(--extra-arguments "$p")
done

cabal2nix . "${extra_args[@]}" \
    | head -n-1

echo "  patchPhase = ''";
echo "    substituteInPlace src/Main.hs \\"
for p in "${packages[@]}"; do
    echo "      --replace '@@$p@@' '\${$p}' \\"
done
echo "  '';"

echo "}"

它的作用是通过src/Main.hs进行grep(可以轻松更改以查找所有haskell文件或某些特定配置模块)并选择@@所包围的所有标记,如{{1} }。然后用它做两件事:

  1. 将它们传递给cabal2nix作为其生成的nix表达式的额外参数
  2. 对来自cabal2nix的nix表达式输出进行后处理以添加补丁阶段,该阶段将Haskell源文件中的@@some-package-name@@标记替换为派生的实际路径。 2
  3. 这会生成如下的nix表达式:

    @@some-package-name@@

    最终结果是我可以编写Haskell代码和cabal包文件;我也不必担心维护nix包文件,只有在我的依赖项发生变化时才重新运行我的generate-nix脚本。

    在我的Haskell代码中,我只是编写可执行文件的路径,好像{ mkDerivation, base, compton, networkmanagerapplet, notify-osd , powerline, setxkbmap, stdenv, synapse, system-config-printer , taffybar, udiskie, unix, X11, xmonad, xmonad-contrib }: mkDerivation { pname = "xmonad-custom"; version = "0.0.0.0"; src = ./.; isLibrary = false; isExecutable = true; executableHaskellDepends = [ base taffybar unix X11 xmonad xmonad-contrib ]; description = "My XMonad build"; license = stdenv.lib.licenses.bsd3; patchPhase = '' substituteInPlace src/Main.hs \ --replace '@@compton@@' '${compton}' \ --replace '@@networkmanagerapplet@@' '${networkmanagerapplet}' \ --replace '@@notify-osd@@' '${notify-osd}' \ --replace '@@powerline@@' '${powerline}' \ --replace '@@setxkbmap@@' '${setxkbmap}' \ --replace '@@synapse@@' '${synapse}' \ --replace '@@system-config-printer@@' '${system-config-printer}' \ --replace '@@udiskie@@' '${udiskie}' \ ''; } 是安装该软件包的文件夹的绝对路径,而且一切都神奇地起作用。

    安装的xmonad二进制文件最终包含对我调用的可执行文件的绝对路径的硬编码引用,这就是nix喜欢的工作方式(例如,这意味着它会自动了解垃圾回收期间的依赖关系)。而且我不必担心在我的交互式环境@@the-nix-package-name@@中保留我调用的内容,或者维护一个仅为此可执行文件设置PATH的包装器。

    1 我把它设置为一个cabal项目,它可以构建并安装到nix商店中,而不是让它从PATH动态重新编译

    2 第2步是一个小元,因为我正在使用bash脚本生成带有嵌入式bash脚本的nix代码

答案 2 :(得分:0)

这不是缩进的答案,但如果我在评论部分发布,那么结果会变得难看。

此外,我不确定这种黑客是否是正确的工作方式。

我注意到如果我使用nix-shell,我可以获得nix商店的完整路径

假设散列总是相同的,AFAIK我相信它是,您可以使用它在构建配方中进行硬编码。

$ which bash
/run/current-system/sw/bin/bash
[wizzup@ ~] 
$ nix-shell -p bash

[nix-shell:~]$ which bash
/nix/store/wb34dgkpmnssjkq7yj4qbjqxpnapq0lw-bash-4.4-p12/bin/bash

最后,如果您使用buildInput,我怀疑您是否需要这样做,它应该是相同的路径。