cabal2nix icon indicating copy to clipboard operation
cabal2nix copied to clipboard

Using data-files in cabal file results in undesirable `enableSeparateDataOutput = true` setting in package derivation

Open charukiewicz opened this issue 6 years ago • 1 comments

Today I was working on deploying a yesod application and needed to deploy some of the static assets alongside of my compiled Haskell executable. After searching for what approaches others have used for this, I settled on defining my project's static/ directory as a data-files entry in my cabal file.

Unfortunately, when there is a non-null data-files option in a cabal file, cabal2nix will automatically produce a derivation that includes enableSeparateDataOutput = true, which leads to undesirable behavior. In short, it took me several hours to figure out that this setting resulted in the list of data-files not getting included with the output package, but instead getting placed elsewhere, in a separate <pkg>-data package that was not getting included with my Nix deployment.

Detailed description of both expected and unexpected/undesirable behaviors below.

Expected Behavior (when enableSeparateDataOutput = false or null)

1. The data-files option is defined in the cabal file to include static/*

2. The package is compiled via nix-build

3. The package output directory (<hash>-<pkgname>-<version> ) includes both a bin/ directory for the executable, and a share/ directory for the extra data files.

4. In a Nix expression, the data files can be referenced via:

${myPkg}/share/x86_64-linux-${ghc.name}/${myPkg.pname}-${myPkg.version}/<data-files>

Undesirable Behavior (when enableSeparateDataOutput = true)

1. The data-files option is defined in the cabal file to include static/*

2. The package is compiled via nix-build but cabal2nix has added enableSeparateDataOutput = true to the derivation

3. The package output directory (<hash1>-<pkgname>-<version> ) includes only a bin/ directory for the executable. The data files are registered as a completely different output (<hash2>-<pkgname>-<version>-data), which contains the share/ directory.

4. The data files can no longer be referenced in relation to the original package (at least not in the way I described in step 4 above).

Why This Happens (based off of the code)

The place where this behavior is defined in the code is here:

https://github.com/NixOS/cabal2nix/blob/d19721cf8b5c0e712638e41a3d187fef31a21680/src/Distribution/Nixpkgs/Haskell/FromCabal.hs#L99-L101

We can see that enableSeparateDataOutput is set to True anytime data-files is not empty.

Suggestion / Question

I am posting this issue based off of a suggestion to do so from someone in the #haskell channel of the Functional Programming slack. It's not clear to me why this behavior is the default or what situations you would want enableSeparateDataOutput to be set to True, as it seems to complicate things when using Nix to package static files alongside of a Haskell binary in a package. If someone could explain the enableSeparateDataOutput option and why it exists, that would be helpful.

My suggestion would be to make it easier to override this setting based off of an options flag (e.g. --no-separate-data-output).

My workaround for this issue at the moment is not at all ideal. I use a Makefile for my project, so I include the following target:

my-pkg.nix: my-pkg.cabal ## Creates a .nix derivation file from the .cabal file
	@cabal2nix --no-haddock --no-check . > $@
	@sed -i 's/enableSeparateDataOutput = true;/enableSeparateDataOutput = false;/' $@

charukiewicz avatar Oct 28 '19 04:10 charukiewicz

Multiple outputs are an optimization offered by the nix package manager which is used in nixpkgs to try to reduce the amount of data needing to be downloaded (which is a big problem with nix). By installing data files into a separate output we for example can prevent downloading extra files if they aren't actually used in the binary (i. e. the path is not referenced).

The data files still can be referenced, it just needs to be made output-aware in a portable way:

"${lib.getOutput "data" myPkg}/share/x86_64-linux-${ghc.name}/${myPkg.pname}-${myPkg.version}/<data-files>"

This will work regardless of what enableSeparateDataOutput is set to.

If you want to disable the separate output regardless, you can disable it via overriding:

with pkgs.haskellPackages;
with haskell.lib;

overrideCabal (callPackage ./generated.nix { }) {
  enableSeparateDataOutput = false;
}

sternenseemann avatar Jun 16 '21 17:06 sternenseemann