cabal icon indicating copy to clipboard operation
cabal copied to clipboard

[nix-local-build] Support `--prefix`, `--datadir`, etc

Open acfoltzer opened this issue 8 years ago • 29 comments

Both new-configure and new-build seem to silently swallow these options at the moment.

acfoltzer avatar Jul 21 '16 22:07 acfoltzer

Do these flags effect building or just installing? I vaguely recall with --enable-relocatable its all argv[0]-relative, but I'm not sure otherwise.

edit answer seems to be https://github.com/haskell/cabal/issues/3473

Ericson2314 avatar Oct 27 '16 20:10 Ericson2314

They affect building because prefix paths will affect what RPATHs we bake into the libraries we produce, as well as any hardcoded paths via a Paths module.

ezyang avatar Oct 27 '16 22:10 ezyang

I tagged this to high priority, as e.g. haddock uses data

phadej avatar Oct 31 '16 12:10 phadej

Surely less high priority than getting new-install working, though?

ezyang avatar Oct 31 '16 18:10 ezyang

I would like to take a stab at this if this is still wanted. Are there any pointers as to where to start? I did a bit of digging and found this module choosing some default InstallDirs. Is this a good place to tie it up with ConfigFlags? https://github.com/haskell/cabal/blob/6a97912efb816ee73ca495cfcd957409237b76a6/cabal-install/Distribution/Client/ProjectPlanning.hs#L1550-L1573

lukel97 avatar Jan 01 '19 20:01 lukel97

@acfoltzer @bubba @phadej @typedrat @23Skidoo At MacPorts we’ve automated stack builds and this has been successful for packages that don’t use datadir.

However, cabal’s lack of a GNU-standard capability for DESTDIR and PREFIX to produce relocatable binaries and installs is causing major problems for package managers that need to build with Haskell development tools. E.g. see:

  • https://github.com/macports/macports-ports/pull/4706
  • https://github.com/ndmitchell/hlint/issues/699
  • https://github.com/macports/macports-ports/pull/5050#issuecomment-527435346
  • https://github.com/macports/macports-ports/pull/5180

There’s obviously a lot of interest in the capability and the lack of it is holding back deployment of Haskell-based tools.

Are there any plans to tackle this soon?

Related:

  • https://github.com/haskell/cabal/issues/462
  • https://github.com/haskell/cabal/issues/3586
  • https://github.com/commercialhaskell/stack/issues/848
  • https://github.com/commercialhaskell/stack/issues/4857

essandess avatar Sep 03 '19 20:09 essandess

@essandess I have a dusty branch that sets the paths correctly here, if anyone is interested in picking this up. I think the last thing I was trying to do was to get v2-install to copy the binary into the right directory

lukel97 avatar Sep 03 '19 20:09 lukel97

@bubba Thanks. I think the is something that a person or group within the cabal development community who are familiar with the code base will necessarily have to pick up.

essandess avatar Sep 04 '19 12:09 essandess

The approach described by hlint's author @ndmitchell solves the issue of hardcoded cabal data-files in the binary.

All that's required is to specify the path within a file called Paths_packagename.hs, which is normally automatically generated by cabal with its own paths. Here is an automated stack build that solves the issue along with related files:

  • https://github.com/macports/macports-ports/blob/e18fe88466477b20da595346ebce8eab839b9fbc/www/adblock2privoxy/Portfile
  • https://github.com/macports/macports-ports/blob/e18fe88466477b20da595346ebce8eab839b9fbc/www/adblock2privoxy/files/Paths_adblock2privoxy.hs

Also, setting the environment variable packagename_datadir at runtime overrides the binary's hardcoded packagename_datadir path.

Also see https://github.com/commercialhaskell/stack/issues/5026.

essandess avatar Sep 06 '19 01:09 essandess

Not sure about this, and a bit off topic, but shouldn't distributions use the build system (Cabal+Setup.hs / cabal --act-as-setup, which should support --prefix) directly like nix does instead of calling another package manager (cabal/stack), except maybe to generate a build plan?

Is nix the exception rather than the rule? Is it done like this because of the high number of dependencies?

fgaz avatar Sep 06 '19 08:09 fgaz

I was recently asked on IRC to write town my use case for prefix installations, to provide insight what a potential solution to this issue would have to look like.

I'm writing a GTK application. Let's call it frobnicate-gtk. It is targeted at a non-technical audience. Potential users are unlikely to have ever heard of Haskell, let alone be able to use cabal to compile anything by themselves. Many of the users will also be using Windows and it is unlikely that they will have a system package manager that would be able to install frobnicate-gtk for them.

To help my users to run frobnicate-gtk, I intend to provide them with a pre-compiled version of frobnicate-gtk, that they can obtain in form of a compressed archive that contains the executable and data files of frobnicate-gtk, the data files of Haskell library dependencies of frobnicate-gtk (I'm assumimg frobnicate-gtk is statically linked to its Haskell library dependencies, if it were dynamically linked, the archive would also have to contain the shared libraries) and any other required library and data files of foreign dependencies (I don't expect cabal to handle the installation of foreign dependencies, so just ignore this last part).

Among the Haskell library dependencies of frobnicate-gtk are some packages that are licensed under a GPL license. Thus a compiled version of frobnicate-gtk will have to include license texts for these dependencies, as is mandated in their GPL license. So the archive will contain at least all license texts of dependencies that require it, but ideally all license texts of included packages will be contained in the archive.

Users should be able to unpack this archive anywhere and the contained executables should run correctly and find their data files as usual, given that --enable-relocatable was used during compilation. I would assume that code compiled without --enable-relocatable should be able to find its data files as long as all files involved are placed in the expected prefix given at compile time, but this isn't strictly part of my use case.

The way I hope cabal would be able to help me with this, is that I run cabal somewhat like this from inside the source directory of frobnicate-gtk:

cabal build --prefix=path/to/frobnicate-gtk-x.y.z

Then cabal would build frobnicate-gtk and all required dependencies and install the results to path/to/frobnicate-gtk-x.y.z. The prefix directory would then serve as the basis to cŕeate the desired archive.

I hope this helps to clarify what this feature could look like. If not, feel free to ask me for any clarifications.

Kritzefitz avatar Nov 11 '21 17:11 Kritzefitz

Hi! Thank you for the report. I know that datafiles in general are quite broken. I myself have switched to data file embedded in the executable (but presumably it doesn't work well for very large files). If you'd like to work on it, I'm sure there are many tickets and probably some brainstorming is needed first.

Licence files can be thrown into the same directory as the exe and the shared libraries (e.g., GTK libs) and this should not cause any problem. What work exactly is --prefix saving you? One cp -a ./licences/ pretty-name/; cp -a ~/shared-libs/ pretty-name/ or something more?

Mikolaj avatar Nov 12 '21 08:11 Mikolaj

--prefix would collect all the dependencies (and their data file) and conveniently into one named directory. At the moment there is no ./licenses/ or ~/shared-libs to conveniently cp -a from. The only place I could currently get the licenses from would be cabal's store directory, which AIUI isn't really a place to be dug around in. And at that point we're already talking about far more work than doing a simple cp -a.

Personally, I'm not a fan of embedded data files. Yes, they work for many common cases, but there are also many cases where they don't work for various reasons.

You say “datafiles in general are quite broken”. That really confuses me. From my viewpoint it looks like they worked pretty okayish with v1 builds. Sure, there were problems with relocatable libraries, but if anything, I feel like v2 builds provide more tools to deal with this problem than before. Can you maybe be more specific what the fundamental problem with datafiles seems to be?

Kritzefitz avatar Nov 12 '21 08:11 Kritzefitz

--prefix would collect all the dependencies (and their data file)

What exactly would the dependency files be? Regarding data files, I agree collecting them from the packages you depend on would be useful. I wonder how people cope with that when publishing Haskell applications, especially for multiple OSes.

At the moment there is no ./licenses/

I think there are some third-party tools to extract licences from all your Haskell dependencies. However, that doesn't cover licences of non-haskell libraries and licenses of data files. What I do is I copy all the licences by hand once to ./licences (actually, to a Debian-style COPYLEFT file with all licences embedded inside, but that's some extra work, see https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/) and mention them in README and copy as needed when publishing.

[Edit: and I actually should distribute (outside COPYLEFT) the extra licences for the binary libraries I ship with on WIndows, as opposed to Linux where I depend on libraries provided by the distribution. I have to check if I do that and if I need to do that.]

[Edit2: IANAL, but I think actually the following suffices for my case for the linked/bundled non-Haskell libraries, in addition to bundling a single copy of anonymous BSD3 license:

Binary distributions of this package may be linked or bundled with libraries such as SDL2, SDL_ttf, FreeType and many others. These libraries are copyright of their respective owners, with all rights reserved. In particular, portions of this software are copyright © 2021 The FreeType Project (www.freetype.org). All rights reserved.

]

or ~/shared-libs to conveniently cp -a from.

There can't be any automatic ~/shared-libs. This is OS-dependent and also changes whenever, say, GTK distribution changes. Your scripts [edit; presumably the CI scripts that build your binary packages on particular OSes?] need to copy the GTK libs and their dependencies as needed. I don't think we can provide any automation for that. Again, I do this once and then copy (and update whenever needed).

You say “datafiles in general are quite broken”. That really confuses me. From my viewpoint it looks like they worked pretty okayish with v1 builds. Sure, there were problems with relocatable libraries, but if anything, I feel like v2 builds provide more tools to deal with this problem than before. Can you maybe be more specific what the fundamental problem with datafiles seems to be?

I happily forgot once I switched to embedded files. Have you searched the tickets?

Mikolaj avatar Nov 12 '21 09:11 Mikolaj

Another way people uses sometimes is ship all files next to the executable and use getExecutablePath as one source of posible locations of files It has its own caveats of course

jneira avatar Nov 12 '21 10:11 jneira

Thanks for the suggested workarounds. I will revisit which of them might actually be helpful or my concrete use case. However, I still think a proper fix for this (or #1801 which seems related if not the same issue at core) would be desirable in Cabal.

There was some discussion in #haskell:matrix.org about this. @Mikolaj suggested I try to codify my imagined solution in a script to demonstrate what it would look like. This could then serve as a basis to get this feature into cabal proper. I agree that this is a reasonable way forward. I will attempt to implement this and report back once I have any significant findings.

It was also mentioned that it would be useful to know if stack supports this future and how it works there. Stack apparently does not implement any comparable feature as well. There is an issue about this: https://github.com/commercialhaskell/stack/issues/848

Kritzefitz avatar Nov 12 '21 16:11 Kritzefitz

@Kritzefitz: thank you so much for persisting on that.

While you workaround/prototype/find standalone tools (that use Cabal lib or not), I think it would also be great to see which parts of the "cabal helps building binary packages" functionality are in a tightly connected blob with --prefix and which are mostly independent, so that we can talk about smaller and well defined obstacles and also offer tools to users that need only a part of the convenience. That's even if we end up with a cabal bindist that does it all, from licenses and data files to building with proper hardcoded paths, statically linking (against musl when on Linux?), stripping and copying binaries.

I'm also hoping for advice from more experienced hackers (@gbaz? @fgaz?) and from users that managed to create a similar workflow (on CI perhaps?) without too much duct tape (surely Nix guys did, but that isn't necessarily directly applicable until the world converts).

Mikolaj avatar Nov 12 '21 17:11 Mikolaj

@Mikolaj

I know that datafiles in general are quite broken

They are not. Sure, in the early days of v2- there were a few bugs, but they are all fixed as far as I know. Relocatable data files in a separate path are still not supported though, and that's part of this ticket and #462.


Two other notable features that can be used to work around this issue:

  • --store-dir to build everything in a specific place. The data file paths remain absolute, so this bundle has to be installed in the same location where it was built (not too much of a problem on distro packages), but this can be overcome with the second feature
  • Data file paths can be overridden with (apparently undocumented?!) environment variables in a wrapper script. $pkg_datadir will override the data directory for package pkg

But yeah, it'd be really nice to have proper relocatable packages / cabal bindist / --prefix, including data files. It's just that nobody worked on it yet.

I will attempt to implement this and report back once I have any significant findings.

I also have a few use cases for moving executables around, so thanks for doing this!

fgaz avatar Nov 12 '21 20:11 fgaz

@Kritzefitz, any news? Could we assist?

Mikolaj avatar Sep 08 '22 12:09 Mikolaj

@Mikolaj, no news. While I would love to have some time to work on this, I didn't get around to working on this since my last messages and I don't have much hope there will be considerable time in the future. Sorry for making a big fuss about this and then not following up.

Kritzefitz avatar Sep 08 '22 14:09 Kritzefitz

Life. :) Take care and let us know.

Mikolaj avatar Sep 12 '22 12:09 Mikolaj

I observe that this issue breaks alex and happy builds that require these files in their datadir: /opt/local/share/alex, happy. These are critical path binaries that a cabal build breaks.

Because these tools are necessary to bootstrap and use Haskell world, I’ve thrown in the towel and just used an ugly hack of replacing this incorrect path in the binary with the correct path padded with extraneous /‘s.

port contents alex
Port alex contains:
  /opt/local/bin/alex
  /opt/local/share/alex/AlexTemplate.hs
  /opt/local/share/alex/AlexWrappers.hs

port contents happy
Port happy contains:
  /opt/local/bin/happy
  /opt/local/share/happy/GLR_Base
  /opt/local/share/happy/GLR_Lib
  /opt/local/share/happy/GLR_Lib-ghc
  /opt/local/share/happy/GLR_Lib-ghc-debug
  /opt/local/share/happy/HappyTemplate
  /opt/local/share/happy/HappyTemplate-arrays
  /opt/local/share/happy/HappyTemplate-arrays-coerce
  /opt/local/share/happy/HappyTemplate-arrays-coerce-debug
  /opt/local/share/happy/HappyTemplate-arrays-debug
  /opt/local/share/happy/HappyTemplate-arrays-ghc
  /opt/local/share/happy/HappyTemplate-arrays-ghc-debug
  /opt/local/share/happy/HappyTemplate-coerce
  /opt/local/share/happy/HappyTemplate-ghc

essandess avatar Sep 18 '22 20:09 essandess

@essandess: That's unfortunate. Does the workaround in https://github.com/haskell/cabal/issues/3586#issuecomment-967541907 work for you? Optionally with chroot or whatever other utility lets you fool cabal and all its child processes that it's in another directory that it really is.

Mikolaj avatar Sep 19 '22 08:09 Mikolaj

@essandess: That's unfortunate. Does the workaround in #3586 (comment) work for you? Optionally with chroot or whatever other utility lets you fool cabal and all its child processes that it's in another directory that it really is.

We’re using store-dir, but I observed this issue whether or not this global option is set.

It’s ancient practice with Unix build tools to use PREFIX and DESTDIR so that the deployed target is independent of the build location, so I admit to being confused why this is so broken in cabal.

The cabal flag --datadir or config option or Paths_NAME.hs provides the path, so just compile that path into the binary. Why should this be a difficult and longstanding issue?

Setting up a chroot jail environment for a package manger install is something that almost certainly will never happen.

essandess avatar Sep 19 '22 13:09 essandess

I observed this issue whether or not this global option is set.

The $pkg_datadir global option?

I agree this should be just fixed and I don't remember any particular difficulty apart of the sprawling code base and testsuite. Right --- and @fgaz confirms above we don't expect any traps or conflicts, so the "longstanding" is not an indication of technical problems, just social ones.

Mikolaj avatar Sep 19 '22 14:09 Mikolaj

The $pkg_datadir global option?

How does that one work? I don't see anything in the cabal man page, help string, or User's guide:

$ man cabal | egrep -e pkg[-_]datadir
$ cabal build --help | egrep -e pkg[-_]datadir

https://cabal.readthedocs.io/en/stable/search.html?q=pkg_datadir&check_keywords=yes&area=default

Search Results

Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.

essandess avatar Sep 19 '22 14:09 essandess

I'm looking at this now and whew its a complicated mess. Honestly, I'm not sure if datafiles ever really did work at all with v2. @fgaz can you show an example where they work and in particular show where the copying is supposed to take place? It looks to me like neither v2-install nor v2-build actually copies them -- they just bake in a Paths module that says where they would be?

In any case, I think I'm on track to get --datadir only working properly. My approach would be that build itself should copy the files to where the Paths module thinks they should be, otherwise you get a nonworking executable during local development. Likewise install should also copy them. And in both cases they should respect --datadir.

I don't think my brain has the capacity to handle prefix or the other installDir flags in the same PR, nor am I sure how many of them make sense at all.

gbaz avatar Oct 04 '22 01:10 gbaz

First of all: nobody addressed https://github.com/haskell/cabal/issues/3586#issuecomment-528755017.

Cabal is like make, cabal-install is like apt. You wouldn't call apt while building a package for arch or fedora, right? The distinction is still there exactly for this reason. Nix, debian, arch... all use Cabal (with appropriate --prefix, --datadir, etc), and use their respective package manager to... do package manager things. Why does macports have to use cabal-install?

--prefix, --datadir etc should just be removed from v2-* commands. They do nothing, they ended up there by accident: #3549

if using cabal-install is actually needed for distro packaging (though haskell is not at npm-like crazy level of dependencies so I don't see why), try to use it just to generate a build plan to then execute with your distro package manager. haskell.nix does this. Our bootstrap script does something similar. Another example.

If that's still not enough, there's --store dir. Set that to the installation location (not a prefix, more like /opt) and you should be ok, though it's a bit hacky. If that's still not enough, then there are #462 and #3473


@gbaz

Honestly, I'm not sure if datafiles ever really did work at all with v2. @fgaz can you show an example where they work and in particular show where the copying is supposed to take place?

Agda uses them, they work. I can't point to the exact location of the copying right now, but it's when the package gets in the store.

It looks to me like neither v2-install nor v2-build actually copies them -- they just bake in a Paths module that says where they would be?

v2-build sets the paths to the values they would assume if the package was installed in the store. v2-run takes care of overriding that through $pkg_datadir variables. This is why one should not run local executables directly. This is actually pretty nice because it'd make it easier to fix #6919 (no need to change Paths_*.hs and rebuild). v2-install installs the package in the store as normal, then creates a symlink to the exe. the data files are in the expected paths in the store.

In any case, I think I'm on track to get --datadir only working properly.

I think we should make it clear that this is not like --datadir the low-level Cabal flag (or is it? are you splitting up the store?). Furthermore, data files are not the only problem when copying stuff around. if we want to do this, I think we should focus on a complete, higher level solution like #3473 (maybe erroring out on dynamic builds for now) or #462.

fgaz avatar Oct 04 '22 06:10 fgaz

Agda uses them, they work. I can't point to the exact location of the copying right now, but it's when the package gets in the store.

Ah I see. Thanks!

https://github.com/haskell/cabal/blob/ebfd8c7270dd603ddbf6ec5302a8145427f2763f/cabal-install/src/Distribution/Client/ProjectBuilding.hs#L1007

buildAndInstallUnpackedPackage calls cabal copy and cabal copy in fact runs install as per:

https://github.com/haskell/cabal/blob/0abbe37187f708e0a5daac8d388167f72ca0db7e/Cabal/src/Distribution/Simple.hs#L568

Which in turn is the One True place that datafiles are copied to where they're assigned.

That said, I don't understand the objection to just letting the --datadir flag set the underlying --datadir that eventually gets sent to cabal copy? Sure that means that the installed exe will be in the store (or whereever the storedir was set to) and its datadir will be wherever it was set to be, which is not there. But then at least most of the time we get an executable that is more relocatable than before.

I'm open to the argument that nobody actually needs this. But I also think requiring end users (not distros) to not use cabal install to build binaries that can be shipped around is sort of wild.

So I'm very much of two minds here.

gbaz avatar Oct 04 '22 20:10 gbaz

@fgaz: what's your opinion on fixing --datadir for other uses than distro packaging? Is it, in general, useful for packages that take advantage of data files? I can't remember the details right now, but don't some people stick to v1- due to datafiles problems?

@essandess: apologies for the confusion

How does that one work? I don't see anything in the cabal man page, help string, or User's guide

I meant <package_name>_datadir, as in

Cabal/src/Distribution/Simple/Build/PathsModule/Z.hs:    tell "_datadir\")    (\\_ -> getPrefixDirReloc $ "

I thought you were referring to that one. but apparently not. :)

Mikolaj avatar Oct 17 '22 11:10 Mikolaj