haskell / haskell/cabal

Binaries have package_datadir hardcoded to build directory, not path in Paths_hlint.hs

Open
#6,234 0 comments 0 reactions 0 assignees View on GitHub
type: bug type: enhancement
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.