DanielG / ghc-mod

Happy Haskell Hacking for editors. DEPRECATED
Other
677 stars 175 forks source link

ghc-mod fails in a multi-package stack project #787

Open simonmichael opened 8 years ago

simonmichael commented 8 years ago

ghc-mod check FILE in the root of a multi-package stack project doesn't detect the stack package db.

$ ghc-mod check hledger-lib/Hledger/Read/JournalReader.hs
hledger-lib/Hledger/Read/JournalReader.hs:101:8:Could not find module ‘Hledger.Data’Use -v to see a list of the files searched for.
hledger-lib/Hledger/Read/JournalReader.hs:102:8:Could not find module ‘Hledger.Utils’Use -v to see a list of the files searched for.
$ ghc-mod debug
Root directory:       /Users/simon/src/hledger
Current directory:    /Users/simon/src/hledger
GHC Package flags:
    -i/Users/simon/src/hledger -i/Users/simon/src/hledger
    -global-package-db -user-package-db -Wall
GHC System libraries: /usr/local/Cellar/ghc/7.10.3b/lib/ghc-7.10.3
GHC user options:
$ ghc-mod --version
ghc-mod version 5.5.0.0 compiled by GHC 7.10.3

It works if I cd into any of the package directories.

simonmichael commented 8 years ago

This hack makes it work for me.

my-ghc-mod.hs:

#!/usr/bin/env stack
-- stack runghc --verbosity info --package directory --package filepath --package process

-- A temporary workaround for https://github.com/DanielG/ghc-mod/issues/787
-- (make ghc-mod work in multi-package stack projects).
-- Ensures check commands are run inside the first directory of the path argument.
-- Setup: compile (stack ghc my-ghc-mod.hs) and install this in your path,
-- configure it as haskell-ghc-mod's Ghc Mod Path, uncheck Enable Ghc Modi.

import Data.List
import Control.Monad
import System.Directory
import System.Environment
import System.FilePath
import System.Process

main :: IO ()
main = do
  args <- getArgs
  when ("check" `elem` takeWhile (/="--") args) $ do
    wd <- getCurrentDirectory
    let wdparts = splitDirectories wd
        _:patharg:_ = dropWhile (/="check") args
        pathparts = splitDirectories patharg
    when (wdparts `isPrefixOf` pathparts) $ do
      case take 1 $ init $ drop (length wdparts) pathparts of
        [subdir] -> setCurrentDirectory $ wd </> subdir
        _        -> return ()
  callProcess "ghc-mod" args
mizunashi-mana commented 8 years ago

+1

rvion commented 8 years ago

thanks @simonmichael

@DanielG @lierdakil integrating that would be great !

simonmichael commented 8 years ago

PS the workaround above is not robust, ie does not handle all cases.

DanielG commented 8 years ago

This is more or less intended behavior, the project detection is based on the CWD, not the path you pass in for the simple reason that not all commands have a FILE argument but all of them still need the project detection to work. I think with some refactoring it should be possible to support this but I'm not sure how best to do it.

Essentially there's two internal run* functions that most commands are implemented in terms of :

that correspond to commands that have file or module arguments and ones that only need access to GHC's package databases.

Unfortunately both of these rely on the "Cradle" (i.e. project environment) to already have been resolved and just get it from the monadic environment. The resolution happens in the withGhcModEnv' wrapper used by runGhcModT which these run functions have to be run in.

The problem is that we have to support ghc-modi as well which does the "Cradle" resolution at startup based solely on the CWD so we can't easily delay it until we actually get a command that has a FILE argument.

ghc-mod was just never designed to handle superprojects to begin with :( I'm very much open to redesiging this core functionality though, so if anyone wants to work on this I'd be happy to help.

DanielG commented 8 years ago

As for a workaround, how about:

#!/bin/sh
cd "$(cd $(dirname "$1"); ghc-mod root)"
ghc-mod check "$@"

but that of course only works for check.