nix-shell: how to specify a custom environment variable? - nixos

I'm learning about nixos and nix expressions. In a project folder I created a shell.nix and I when I run nix-shell I want it to preset an environment variable for me.
For example to set the PGDATA env var.
I know there are several ways to write nix expression files (I'm not yet used to most of them). Here is my sample:
shell.nix
let
pkgs = import <nixpkgs> {};
name = "test";
in pkgs.myEnvFun {
buildInputs = [
pkgs.python
pkgs.libxml2
];
inherit name;
extraCmds = ''
export TEST="ABC"
'';
}

Use buildPythonPackage function (that uses mkDerivation). Passing anything to it will set env variables in bash shell:
with import <nixpkgs> {};
buildPythonPackage {
name = "test";
buildInputs = [ pkgs.python pkgs.libxml2 ];
src = null;
PGDATA = "...";
}

You may also use pkgs.stdenv.mkDerivation.shellHook.
let
pkgs = import <nixpkgs> {};
name = "test";
in pkgs.stdenv.mkDerivation {
buildInputs = [
pkgs.python
pkgs.libxml2
];
inherit name;
shellHook = ''
export TEST="ABC"
'';
}

To set environment variable for a nix-shell without creating a new package, shellHook option can be used. As shown in the example from the manual:
shellHook =
''
echo "Hello shell"
export SOME_API_TOKEN="$(cat ~/.config/some-app/api-token)"
'';
A full shell.nix example based on my use-case - with go of version 1.18 from unstable channel:
let
pkgs = import <nixpkgs> {};
unstable = import <nixos-unstable> { config = { allowUnfree = true; }; };
in pkgs.mkShell rec {
name = "go-1.18";
buildInputs = with pkgs; [
unstable.go_1_18
];
shellHook = ''
export PATH="$HOME/go/bin:$PATH"
'';
}
The script also sets name option, which is then shown in the shell prompt (works nicely with Starship shell prompt).

Related

nixpkgs.overlays and nixpkgs.config.packageOverrides not being reflected in environment.systemPackages

It seems that when I override a python3 package in nixpkgs.config.packageOverrides or nixpkgs.overlays, a python application in environment.systemPackages is not using those overrides. How can I get that overridden python3 package to be used in a python3 application?
Example darwin-configuration.nix that uses nixpkgs.overlays:
{ config, pkgs, lib, ... }:
{
environment.systemPackages =
[
pkgs.myawscli2
];
nixpkgs.overlays = let overlayRemovePyopenssl = self: super:
let removePyopenssl = pythonpkgs:
lib.filter
(pythonpkg: !(pythonpkg != null && pythonpkg ? pname && pythonpkg.pname == "pyopenssl"))
pythonpkgs;
in {
python3 = super.python3.override {
packageOverrides = python-self: python-super: rec {
# Delete pyopenssl; workaround for broken package on darwin-aarch64
# “Package ‘python3.10-pyopenssl-22.0.0’ in /nix/store/<hash>-nixpkgs/nixpkgs/pkgs/development/python-modules/pyopenssl/default.nix:73 is marked as broken, refusing to evaluate”
# https://github.com/NixOS/nixpkgs/issues/174457
urllib3 = python-super.urllib3.overridePythonAttrs (origattrs: rec {
propagatedBuildInputs = removePyopenssl origattrs.propagatedBuildInputs;
});
twisted = python-super.twisted.overridePythonAttrs (origattrs: {
checkInputs = removePyopenssl origattrs.checkInputs;
});
};
};
myawscli2 = (self.awscli2.override {
# override the python3 arg of awscli2
# https://github.com/NixOS/nixpkgs/blob/f72be3af76fb7dc45e2088d8cb9aba1e6767a930/pkgs/tools/admin/awscli2/default.nix#L2
python3 = self.python3;
});
}; in
[
overlayRemovePyopenssl
];
system.stateVersion = 4;
}
The overlay is applied properly in the individual python package urllib3:
nix-repl> lib = import <nixpkgs>.lib
nix-repl> :l <darwin>
nix-repl> lib.forEach pkgs.python3.pkgs.urllib3.propagatedBuildInputs (x: x.pname)
[ "brotli" "certifi" "cryptography" "idna" "python3" ]
However, the overlay was not applied to the python application that uses urllib3. Note that pyopenssl is in the dependencies of urllib3 when used by awscli2:
nix-repl> lib.forEach (lib.findFirst (x: x.pname == "urllib3") null pkgs.awscli2.propagatedBuildInputs).propagatedBuildInputs (x: x.pname)
[ "brotli" "certifi" "cryptography" "idna" "pyopenssl" "python3" ]
I also tried the same thing with nixpkgs.config.packageOverrides with the same effect:
{ config, pkgs, lib, ... }:
{
environment.systemPackages =
[
pkgs.myawscli2
];
nixpkgs.config.packageOverrides = super:
let removePyopenssl = pythonpkgs:
lib.filter
(pythonpkg: !(pythonpkg != null && lib.hasAttr "pname" pythonpkg && pythonpkg.pname == "pyopenssl"))
pythonpkgs;
in {
python3 = super.python3.override {
packageOverrides = python-self: python-super: rec {
# workaround for
# “Package ‘python3.10-pyopenssl-22.0.0’ in /nix/store/<hash>-nixpkgs/nixpkgs/pkgs/development/python-modules/pyopenssl/default.nix:73 is marked as broken, refusing to evaluate”
# https://github.com/NixOS/nixpkgs/issues/174457
urllib3 = python-super.urllib3.overridePythonAttrs (origattrs: rec {
propagatedBuildInputs = removePyopenssl origattrs.propagatedBuildInputs;
});
twisted = python-super.twisted.overridePythonAttrs (origattrs: {
checkInputs = removePyopenssl origattrs.checkInputs;
});
};
};
myawscli2 = (pkgs.awscli2.override {
python3 = pkgs.python3;
});
};
system.stateVersion = 4;
}
This seems to contradict the nixpkgs manual, which says: “pythonPackages.twisted is now globally overridden. All packages and also all NixOS services that reference twisted (such as services.buildbot-worker) now use the new definition”
I also asked this on Element. The problem is that awscli2/default.nix calls python3.override {packageOverrides = …;}:
py = python3.override {
packageOverrides = self: super: {
…
};
};
This unfortunately overwrites my own .override {packageOverrides = …;}. Unfortunately you can’t compose (python.override {packageOverrides = …;}).override {packageOverrides = …;}
I worked around this by checking out nixpkgs and editing the awscli2 nix to combine the python application’s packageOverrides with packageOverrides from the arguments:
py = python3.override (oldargs: {
packageOverrides = self: super: {
…
} // (if oldargs?packageOverrides then (oldargs.packageOverrides self super) else super);
});

What language this file is written?

I am trying to understand in what language this file is written in? I would like to know but here authors did not specify the language.
https://github.com/status-im/status-react/blob/develop/nix/status-go/default.nix
{ lib, callPackage, mkShell, openjdk, androidPkgs }:
let
inherit (lib)
catAttrs concatStrings concatStringsSep fileContents makeBinPath
getConfig optional attrValues mapAttrs attrByPath;
# Metadata common to all builds of status-go
meta = {
description = "The Status Go module that consumes go-ethereum.";
license = lib.licenses.mpl20;
platforms = with lib.platforms; linux ++ darwin;
};
# Source can be changed with a local override from config
source = callPackage ./source.nix { };
# Params to be set at build time, important for About section and metrics
goBuildParams = {
GitCommit = source.rev;
Version = source.cleanVersion;
};
# These are necessary for status-go to show correct version
paramsLdFlags = attrValues (mapAttrs (name: value:
"-X github.com/status-im/status-go/params.${name}=${value}"
) goBuildParams);
goBuildLdFlags = paramsLdFlags ++ [
"-s" # -s disabled symbol table
"-w" # -w disables DWARF debugging information
];
goBuildFlags = [ "-v" ];
in rec {
mobile = callPackage ./mobile {
inherit meta source goBuildFlags goBuildLdFlags;
};
shell = mkShell {
inputsFrom = [ mobile.android mobile.ios ];
};
}
It the Nix expression language.

How to get use `callCabal2nix` to supply package list to 'ghcWithPackages'?

I have a list of ~46 dependencies.
I produce a shell using among other things a call to:
server = pkgs.haskellPackages.callCabal2nix "server" ./server.cabal { };
within an override of pkgs.haskellPackages that goes into pkgs.myHaskellPackages.
The shell works fine. However, I have another nix for building a docker and this requires a build input that comes from:
ghc = pkgs.myHaskellPackages.ghcWithPackages (p: [ longlistofdependencies ]);
How do I specify the long list of dependencies without having to write them out manually, defeating the purpose of using callCabal2nix in the first place.
let
bootstrap = import <nixpkgs> { };
nixpkgs = builtins.fromJSON (builtins.readFile ./nixpkgs-unstable.json);
src = bootstrap.fetchFromGitHub {
owner = "NixOS";
repo = "nixpkgs";
inherit (nixpkgs) rev sha256;
};
config = {
allowBroken = true;
packageOverrides = pkgs: rec {
servantSrc = pkgs.fetchFromGitHub {
owner = "haskell-servant";
repo = "servant";
rev = "73c87bc2bc0685649f2337b06ab4fdc66c4ce1dd";
sha256 = "0sw4mrncmfna30cyxrvinc1krqhfxn5dcc1ggzqfy39s0yl9q98r";
#sha256 = "0000000000000000000000000000000000000000000000000000";
} + "/servant";
myHaskellPackages = pkgs.haskellPackages.override {
overrides = haskellPackagesNew: haskellPackagesOld: rec {
server =
haskellPackagesNew.callCabal2nix "server" ./server.cabal { };
servant =
haskellPackagesNew.callCabal2nix "servant" servantSrc {};
# several other overridden packages
};
};
};
};
pkgs = import src { inherit config; };
devShell = pkgs.myHaskellPackages.shellFor {
packages = p: [
p.server
];
buildInputs = with pkgs.haskellPackages; [
hlint
brittany
haskell-language-server
];
};
# Every time a dependency changes in the cabal file, this has to be edited as well.
ghc = pkgs.myHaskellPackages.ghcWithPackages (p: [
p.aeson p.aeson-qq p.base p.blaze-html p.bytestring p.cache p.containers
p.contravariant-extras p.criterion p.either p.hashable p.hasql
p.hasql-migration p.hasql-pool p.hasql-th p.hasql-transaction p.hspec
p.http-client p.http-conduit p.http-types p.immortal-queue p.lens p.linear
p.monad-logger p.mtl p.pqueue p.pretty-simple p.QuickCheck p.raw-strings-qq
p.servant-blaze p.servant-docs p.servant-server
p.string-interpolate p.text p.text-format p.time p.tuple p.unordered-containers
p.utf8-string p.vector p.vector-split p.wai p.wai-cors p.warp p.xlsx p.yaml
]);
in {
inherit devShell;
inherit ghc;
inherit pkgs;
}
The ghc output is used in docker.nix as a build input:
let
inherit (import ./pinned-nixpkgs.nix) ghc pkgs;
pricing-server =
pkgs.stdenv.mkDerivation {
name = "my-server";
pname = "my-server";
version = "1.1.0";
src = ./.;
buildPhase = ''
ghc -O2 --make -outputdir ./tmp2 Main.hs
'';
installPhase = ''
mkdir -p $out/bin
cp Main $out/bin
cp -r ./sql $out/bin
'';
buildInputs = [ ghc ];
};
in
dockerTools.buildImage {
# irrelevant to the question.
}
A possible solution is pkgs.myHaskellPackages.server.getBuildInputs.haskellBuildInputs, or pkgs.myHaskellPackages.server.getCabalDeps.libraryHaskellDepends.
You can explore these attributes, or any expression, with nix repl. You may have to expose some values from your let bindings though. In this case I just browsed through haskellPackages.warp in nix repl <nixpkgs>.
I also noticed you use rec in an overlay. This will work for you until it doesn't. I'd recommend to remove rec to avoid accessing attributes in a third way and use the more standard haskellPackagesNew.servant instead.

How to write a shell.nix file which combines other nix-shell environments?

I want to write a shell.nix file that will provide me a development environment which includes developer tools + haskell dependencies. I would like to keep my nix expressions separate - for now that means one file containing information about my editor, and one file containing information about my haskell project.
I have tried to set up a really basic environment, but can't get it to work. I have two files which I can create a nix-shell from already:
# my-haskell-shell.nix - generated by cabal2nix --shell
{ nixpkgs ? import <nixpkgs> {}, compiler ? "default", doBenchmark ? false }:
let
inherit (nixpkgs) pkgs;
f = { mkDerivation, base, cabal-install, hpack, stdenv }:
mkDerivation {
pname = "hello-world";
version = "0.1.0.0";
src = ./.;
isLibrary = false;
isExecutable = true;
libraryToolDepends = [ hpack ];
executableHaskellDepends = [ base ];
executableToolDepends = [ cabal-install hpack ];
preConfigure = "hpack";
license = stdenv.lib.licenses.mit;
};
haskellPackages = if compiler == "default"
then pkgs.haskellPackages
else pkgs.haskell.packages.${compiler};
variant = if doBenchmark then pkgs.haskell.lib.doBenchmark else pkgs.lib.id;
drv = variant (haskellPackages.callPackage f {});
in
if pkgs.lib.inNixShell then drv.env else drv
# my-neovim-shell.nix
with import <nixpkgs> {};
let
my-neovim = neovim.override { vimAlias = false; };
in
stdenv.mkDerivation {
name = "neovim-dev-env";
buildInputs = [
my-neovim
];
}
Both of these produce useful shell environments! (That is, nix-shell my-haskell-env.nix and nix-shell my-neovim-env.nix give me cabal and neovim respectively, but not both).
My attempt at producing a shell.nix which will provide an environment with both neovim and cabal available is this:
# shell.nix
with import <nixpkgs> {};
let
my-neovim-shell-env = import ./my-neovim-shell.nix;
my-haskell-shell-env = import ./my-haskell-shell.nix {};
in
stdenv.mkDerivation {
name = "my-new-env";
buildInputs = [
my-neovim-shell-env
my-haskell-shell-env
];
}
This does not work however. It seems to try to build the neovim environment:
$ nix-shell
these derivations will be built:
/nix/store/k9ygid1wl75vf2nq7jzfh32mv5f8i956-neovim-dev-env.drv
building '/nix/store/k9ygid1wl75vf2nq7jzfh32mv5f8i956-neovim-dev-env.drv'...
unpacking sources
variable $src or $srcs should point to the source
builder for '/nix/store/k9ygid1wl75vf2nq7jzfh32mv5f8i956-neovim-dev-env.drv' failed with exit code 1
error: build of '/nix/store/k9ygid1wl75vf2nq7jzfh32mv5f8i956-neovim-dev-env.drv' failed
I don't know how to fix this; what am I doing wrong? I suspect maybe I should not be using mkDerivation?
Managed to find a solution. Maybe there is a better way, but this works:
Suppose you have a package.yaml, then
$ cabal2nix . > app.nix
# shell.nix
let
p = import <nixpkgs> {};
app = p.haskellPackages.callPackage (./app.nix) {};
lib = p.haskell.lib;
in
lib.overrideCabal app (old: { buildTools = [p.my-nvim] ++ (old.buildTools or []); })
(where I've added my-nvim to my ~/.config/nixpkgs/overlays/)
Now I have access to the my-nvim package and ghc, etc:
$ nix-shell
$ nvim # works
$ ghc # works
Inspiration from https://github.com/p-implies-q/nix-util

Nix & Haskell - using default.nix from cabal2nix and a generic shell.nix

From a pretty basic cabal file
cabal2nix ./. > default.nix
and then a shell.nix of
let
pkgs = import <nixpkgs> {};
haskellPackages = pkgs.haskellPackages_ghc784.override {
extension = self: super: {
thispackage = self.callPackage ./default.nix {};
};
};
in pkgs.myEnvFun {
name = haskellPackages.thispackage.name;
buildInputs = [
(haskellPackages.ghcWithPackages (hs: ([
hs.cabalInstall
] ++ hs.thispackage.propagatedNativeBuildInputs)))
];
}
When in the nix-shell and running cabal configure it complains of missing packages such as text.
If I put the text package explicitly into the shell.nix such as
let
pkgs = import <nixpkgs> {};
haskellPackages = pkgs.haskellPackages_ghc784.override {
extension = self: super: {
thispackage = self.callPackage ./default.nix {};
};
};
in pkgs.myEnvFun {
name = haskellPackages.thispackage.name;
buildInputs = [
(haskellPackages.ghcWithPackages (hs: ([
hs.cabalInstall
hs.text
] ++ hs.thispackage.propagatedNativeBuildInputs)))
];
}
The cabal configure is fine, but I would expect hs.thispackage.propagatedNativeBuildInputs to be supplying these packages.
The very basic haskell project can be seen at
https://github.com/fatlazycat/haskell-nix-helloworld
Am I wrong in assuming you can work in this way ?
Thanks
The propagatedNativeBuildInputs attribute is used by Haskell libraries to
propagate their build inputs down to other builds that depend on them. Your
package, however, is not a library --- it's an executable ---, so there's no
need to propagate the build inputs and thus propagatedNativeBuildInputs is
empty. Instead, you'll find the information you need in
hs.thispackage.extraBuildInputs.
Generally speaking, the definition of these kind of nix-shell environments
has become a lot easier in the release-15.09 branch (or nixos-unstable),
though. Simply run cabal2nix --shell . >shell.nix and you get a shell.nix
file that you can use for building with nix-build shell.nix as well as for
entering an interactive development environment with nix-shell.
http://nixos.org/nixpkgs/manual/#users-guide-to-the-haskell-infrastructure has
a lot more information about the subject.

Resources