Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Binaries using hlint have hlint_datadir hardcoded to build directory, not destroot #699

Open
essandess opened this issue Jul 3, 2019 · 11 comments

Comments

@essandess
Copy link

essandess commented Jul 3, 2019

I’m trying to port IHaskell to MacPorts. IHaskell uses hlint.

The issue is that both stack and cabal builds create binaries that point to the build directory in .stack or .cabal-sandbox, not the install directory with prefix, say, /opt/local/bin. After the build, these temporary build directories are discarded, and neither ihaskell nor hlint binaries work.

I see that there is an hlint --datadir option:

hlint/cc/Engine.hs

Lines 65 to 70 in c62b616

callProcess "hlint" $
[ "lint"
, "--cc"
, "--datadir", "/opt/hlint"
, "--no-exit-code"
]

It seems like it should be straightforward, but I’m not sure how to build projects that use hlint to set its data directory to have prefix /opt/local so that destrooting the build will work.

Any pointers would be appreciated.

Related:

@ndmitchell
Copy link
Owner

I think this is a cabal/stack thing. When configuring cabal, you pass --prefix or some other flag and it will wire that into HLint. The --data-dir flag is mostly there as a backup, in case you get into a state where you can't do that.

@vaibhavsagar
Copy link
Contributor

It looks like Cabal has a --datadir flag. I wasn't able to find the equivalent for Stack.

@essandess
Copy link
Author

I see that there’s a (four year old!) upstream request for a stack --prefix option at commercialhaskell/stack#848.

I don’t see a cabal --prefix option, but there is a runhaskell --prefix.

Any suggestions on the correct build calls for hlint or ihaskell that use --prefix?

@vaibhavsagar
Copy link
Contributor

I think you want --data-dir: cabal configure --data-dir=<path> or cabal install --data-dir=<path>.

@essandess
Copy link
Author

Thanks again! It's all working now. See macports/macports-ports#4706

I'll defer to you if you want to close this issue. I was able to work around it with a lot of hackery, but both upstream cabal and stack need destroy capability.

@ndmitchell
Copy link
Owner

Can you share your hackery? Is this something HLint should have made easier, or could make easier? Or is this just Cabal and Stack being hard to wrangle?

@essandess
Copy link
Author

The hackery is in the Portfile in the PR linked above: https://github.com/macports/macports-ports/pull/4706/files#diff-039a3b878c0a62a118725172573632f4

It’s basically: create symlinks in PREFIX to DESTDIR, install to PREFIX symlinks, which indirectly copies stuff into DESTDIR, delete the symlinks in PREFIX, then the package manager’s destroot procedure copies everything back from DESTDIR to PREFIX, but keeping track of what gets installed for upgrades or uninstalls.

This won’t necessarily work for packages that want to do things like mkdir ${PREFIX}/bin with a specific mode and ownership.

To the best of my knowledge (which isn’t too much), this is a compiler-level issue (ghc or cabal or stack, in that order) in Haskell world, and not necessarily an application-level issue.

@essandess
Copy link
Author

An added benefit for ihaskell is that we get a working standalone hlint binary too!

hlint is really nicely integrated into ihaskell and displays well within a notebook—thanks @ndmitchell and @vaibhavsagar for providing these nice tools.

@essandess
Copy link
Author

@ndmitchell

Can you share your hackery? Is this something HLint should have made easier, or could make easier? Or is this just Cabal and Stack being hard to wrangle?

This has come full circle back to you from an older blog post. The bottom line is that Cabal is being hard to wrangle.

Before I get to the issue, please let me recap the background:

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 figuring out an hlint (within ihaskell) build recipe that produces a relocatable, properly prefix-ed binary?

Your approach described by 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.

This works for the ihaskell binary, but not for hlint_datadir in either the ihaskell or hlint binaries. I’ve tried sever 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.

Nothing is working. ghc always finds the Paths_hlint.hs that Cabal generates, not the one I provided.

Would you be able to give this a try and let me know what you think the issue is? I've also unsuccessfully 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.

Current build recipe:

rm -fr ~/.stack ./.stack-work stack.yaml.lock
stack setup
stack build

Here's aPaths_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)

@ndmitchell
Copy link
Owner

I think Cabal deliberately defeats you here, by picking its autogen over yours. I suspect you'll have to go to the Cabal people for help - I'm afraid I'm out of my depth.

@ndmitchell
Copy link
Owner

Thanks to #824 this is now fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants