3

Problem: I've got a command-line tool written in Haskell that reads a data file. Upon installing the program, I'd like for the program to be able to read that data file regardless of the directory I find myself in. That is, upon installing, the path that the program knows should be aligned with the path that the installer chooses.

Initial attempt: I was directed to the section 7.6. Accessing data files from package code in Cabal (the library) and learned that I can import getDataDir :: IO FilePath from Paths_myprog, as long as I add Paths_myprog to other-modules (although Hpack auto-includes this by default) and that after installing my program, I can run it with:

myprog_datadir=~/tmp ~/.local/bin/myprog

Then all I need to do is have the installer move the data file to this directory, since myprog dynamically accesses that path rather than some hardcoded value. But I don't want to have to specify myprog_datadir=~/tmp in my environment every time I run the program, I want it hardcoded into the binary! And it seems that setting myprog_datadir=~/tmp when building and installing doesn't set a compile-time default.

So: Is there a way I can hardcode this path upon installation?

(Preferrably, an answer that relates to Stack, but a cabal-install answer is also greatly appreciated.)

sshine
  • 15,635
  • 1
  • 41
  • 66
  • The file is listed as `data-files: relative/path/to/file.txt`, but as I install the executable and change directory, that relative path no longer exists. The default `datadir` is something like `/Users/simonshine/Projects/myprog/.stack-work/install/x86_64-osx/d6f6bad3f39945fab394a08b1bc4be948440a07b57dc3d9c354985b39ee09310/8.8.4/share/x86_64-osx-ghc-8.8.4/myprog-0.1.0`, but I'd like for that to be e.g. `/usr/local/share`. – sshine Feb 15 '21 at 22:00
  • 1
    Have you considered hardcoding _the data itself_ into the executable, rather than just a path to it? – leftaroundabout Feb 15 '21 at 22:11

1 Answers1

4

Files listed in data-files are already automatically installed in the right location so that Paths_pkg will find them. Here's a minimal example:

% cat Main.hs
import Paths_so_test
main :: IO ()
main = getDataDir >>= putStrLn
% cat so-test.cabal
cabal-version:       >=1.10
name:                so-test
version:             0.1.0.0
build-type:          Simple
data-files:          test

executable so-test
  main-is:             Main.hs
  other-modules:       Paths_so_test
  autogen-modules:     Paths_so_test
  build-depends:       base >=4.14 && <4.15
  default-language:    Haskell2010
% cat test
Hello, world!
% cabal install
Wrote tarball sdist to
/home/dmwit/projects/oneoff/tmp.dir/dist-newstyle/sdist/so-test-0.1.0.0.tar.gz
Resolving dependencies...
Up to date
Symlinking 'so-test'
% cat `so-test`/test
Hello, world!
Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
  • But how do I change that? If the installer were `configure`, I might write `./configure --prefix=/usr/local` and then `datarootdir` would be `${prefix}/share`. My scenario is that I imagine some packager (myself, really) would call `stack install` and specify paths that my packaging tool will then go to and bundle up whichever way it does (e.g. PPA, AUR). – sshine Feb 16 '21 at 05:42
  • I might like the equivalent of `--prefix=~/.local` so that not only the executable ends up in `~/.local/bin`, but also data files end up in `~/.local/share` with `myprog_datadir=~/.local/share` being the default instead of the really long name with a hash in it. – sshine Feb 16 '21 at 05:50
  • 1
    @SimonShine `~/.cabal/config` should have stanzas labeled `install-dirs user` and `install-dirs global`. – Daniel Wagner Feb 16 '21 at 14:54