Binaries have package_datadir hardcoded to build directory, not path in Paths_hlint.hs
- Dominant language
- Haskell
- Stars
- 1.7k
- Forks
- 750
- Avg merge
- 4d 3h
- Merged PRs (30d)
- 28
Description
I’m trying to port [IHaskell](https://github.com/gibiansky/IHaskell/) to MacPorts. IHaskell uses `hlint`. The issue is that both `stack` and `cabal` builds create binaries with an `hlint_datadir` that points to the build directory in `.stack` or `.cabal-sandbox`, not the install directory with prefix, say, `/opt/local/share/hlint`. After the build, these temporary build directories are discarded, and neither `ihaskell` nor `hlint` binaries work.
This issue has been opened at https://github.com/ndmitchell/hlint/issues/699 and https://github.com/gibiansky/IHaskell/issues/1061, and we all agree that it's an upstream issue with `cabal`.
Before I get to the issue, please let me recap the background:
* The `cabal` build of `hlint` hardcodes the build path into the binaries because of well-known issue with the way `cabal` handles `data-files`.
* This behavior breaks package managers like MacPorts and Brew because they cannot destroot properly.
* Brew has hacked around this by reluctantly modifying `prefix`. See https://github.com/Homebrew/homebrew-core/pull/1630#issuecomment-223244572.
* I hacked around this by adding a temporary symbolic link to `prefix` (https://github.com/gibiansky/IHaskell/issues/1061#issuecomment-508874776), and have been discussing this approach with the MacPorts maintainers for packages like this one that have the `data-files` issue. There's also been reasonable reluctance and push-back against this practice, which is one reason we haven't merged `ihaskell` yet.
* To address these reasonable concerns, I found `hlint` author's @ndmitchell's `Paths_packagename.hs` [solution](http://neilmitchell.blogspot.com/2008/02/adding-data-files-using-cabal.html) to the issue of hardcoded `cabal` `data-files` in the binary, for which no ugly `DESTDIR` hack is necessary. 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. See https://github.com/commercialhaskell/stack/issues/5026
* Side note: `pandoc`'s author addressed this issue by using [file-embed](http://hackage.haskell.org/package/file-embed). See: https://github.com/commercialhaskell/stack/issues/848#issuecomment-528440793
Now that we have a working fix to `stack` and `cabal`'s `data-files` issues, I'd like to use it for the MacPorts port. I haven't gotten this working with `ihaskell`'s more complicated build, including its dependencies like `hlint`.
Would you please help figure out an `hlint` (within `ihaskell`) build recipe that produces a relocatable, properly `prefix`-ed binary?
The `src/Paths_*.hs` approach works for the `ihaskell` binary, but not for `hlint_datadir` in either the `ihaskell` or `hlint` binaries. I’ve tried many things:
* Put a copy of `Paths_hlint.hs` both in `src` and `src/IHaskell`, without success.
* Tried setting Cabal’s global `datadir` setting in a variety ways.
* Adding the `ghc` option `-ipathtoPaths_hlint`, several different ways.
* Played around with `--enable-relocatable`, which I believe tickles that `reloc` guard in https://github.com/haskell/cabal/blob/master/Cabal/Distribution/Simple/Build/PathsModule.hs.
Nothing is working. `ghc` always finds the `Paths_hlint.hs` that Cabal generates, not the one I provided.
Would you all please indicate a Cabal build recipe that reads my version of `Paths_hlint.hs`—not Cabal's `autogen` version—so that we can get a properly `DESTDIR`-ed and `PREFIXED`-ed binary for a packaged install?
Cabal-based build recipe:
```
cabal update
cabal install cpphs happy
cabal sandbox init
cabal sandbox add-source . ./ghc-parser ./ipython-kernel
cabal install --only-dependencies
cabal configure --extra-lib-dir=/usr/lib
cabal install --extra-lib-dir=/usr/lib
```
Alternate `stack`-based build recipe:
```
rm -fr ~/.stack ./.stack-work stack.yaml.lock
stack setup
stack build
```
Here's a`Paths_hlint.hs` that should work. A successful build recipe will just stick this in a place where it will be found by `ghc` before the Cabal’s `autogen` Paths_*.hs file.
```
{- Cabal data-files hardcoded path in binary fix.
This file replaces the `Paths_hlint.hs` automatically created by Cabal.
See:
* https://github.com/commercialhaskell/stack/issues/848
* https://github.com/commercialhaskell/stack/issues/4857
* https://github.com/haskell/cabal/issues/462
* https://github.com/haskell/cabal/issues/3586
-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE NoRebindableSyntax #-}
{-# OPTIONS_GHC -fno-warn-missing-import-lists #-}
module Paths_hlint (
version,
getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir,
getDataFileName, getSysconfDir
) where
import qualified Control.Exception as Exception
import Data.Version (Version(..))
import System.Environment (getEnv)
import Prelude
#if defined(VERSION_base)
#if MIN_VERSION_base(4,0,0)
catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a
#else
catchIO :: IO a -> (Exception.Exception -> IO a) -> IO a
#endif
#else
catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a
#endif
catchIO = Exception.catch
version :: Version
version = Version [2,0,1] []
bindir, libdir, dynlibdir, datadir, libexecdir, sysconfdir :: FilePath
bindir = "/opt/local/bin"
libdir = "/opt/local/lib/ihaskell"
dynlibdir = "/opt/local/lib/ihaskell"
datadir = "/opt/local/share/ihaskell"
libexecdir = "/opt/local/lib/ihaskell"
sysconfdir = "/opt/local/etc"
getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir, getSysconfDir :: IO FilePath
getBinDir = catchIO (getEnv "hlint_bindir") (\_ -> return bindir)
getLibDir = catchIO (getEnv "hlint_libdir") (\_ -> return libdir)
getDynLibDir = catchIO (getEnv "hlint_dynlibdir") (\_ -> return dynlibdir)
getDataDir = catchIO (getEnv "hlint_datadir") (\_ -> return datadir)
getLibexecDir = catchIO (getEnv "hlint_libexecdir") (\_ -> return libexecdir)
getSysconfDir = catchIO (getEnv "hlint_sysconfdir") (\_ -> return sysconfdir)
getDataFileName :: FilePath -> IO FilePath
getDataFileName name = do
dir <- getDataDir
return (dir ++ "/" ++ name)
```
Contributor guide
Assessment
This issue has not been assessed yet.