2015-03-03 20:12:43 +00:00
|
|
|
-- ghc-mod: Making Haskell development *more* fun
|
|
|
|
-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>
|
|
|
|
--
|
|
|
|
-- This program is free software: you can redistribute it and/or modify
|
|
|
|
-- it under the terms of the GNU Affero General Public License as published by
|
|
|
|
-- the Free Software Foundation, either version 3 of the License, or
|
|
|
|
-- (at your option) any later version.
|
|
|
|
--
|
|
|
|
-- This program is distributed in the hope that it will be useful,
|
|
|
|
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
-- GNU Affero General Public License for more details.
|
|
|
|
--
|
|
|
|
-- You should have received a copy of the GNU Affero General Public License
|
|
|
|
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
2015-03-28 01:33:42 +00:00
|
|
|
{-# LANGUAGE CPP #-}
|
2015-03-03 20:12:43 +00:00
|
|
|
module Language.Haskell.GhcMod.CabalHelper (
|
2015-03-15 19:48:55 +00:00
|
|
|
getComponents
|
2015-03-03 20:12:43 +00:00
|
|
|
, getGhcPkgOptions
|
|
|
|
) where
|
|
|
|
|
|
|
|
import Control.Applicative
|
|
|
|
import Control.Monad
|
|
|
|
import Data.Monoid
|
2015-03-28 01:30:51 +00:00
|
|
|
import Data.Version
|
2015-03-15 19:48:55 +00:00
|
|
|
import Distribution.Helper
|
|
|
|
import qualified Language.Haskell.GhcMod.Types as T
|
|
|
|
import Language.Haskell.GhcMod.Types hiding (ghcProgram, ghcPkgProgram,
|
|
|
|
cabalProgram)
|
2015-03-03 20:12:43 +00:00
|
|
|
import Language.Haskell.GhcMod.Monad.Types
|
|
|
|
import Language.Haskell.GhcMod.Utils
|
|
|
|
import Language.Haskell.GhcMod.World
|
|
|
|
import Language.Haskell.GhcMod.PathsAndFiles
|
|
|
|
import System.FilePath
|
|
|
|
|
2015-03-28 01:33:42 +00:00
|
|
|
import Paths_ghc_mod as GhcMod
|
|
|
|
|
2015-03-03 20:12:43 +00:00
|
|
|
-- | Only package related GHC options, sufficient for things that don't need to
|
|
|
|
-- access home modules
|
2015-03-15 19:48:55 +00:00
|
|
|
getGhcPkgOptions :: (MonadIO m, GmEnv m) => m [(ChComponentName, [GHCOption])]
|
|
|
|
getGhcPkgOptions = do
|
|
|
|
Cradle {..} <- cradle
|
|
|
|
let distdir = cradleRootDir </> "dist"
|
|
|
|
runQuery distdir ghcPkgOptions
|
2015-03-03 20:12:43 +00:00
|
|
|
|
2015-03-15 19:48:55 +00:00
|
|
|
helperProgs :: Options -> Programs
|
|
|
|
helperProgs opts = Programs {
|
|
|
|
cabalProgram = T.cabalProgram opts,
|
|
|
|
ghcProgram = T.ghcProgram opts,
|
|
|
|
ghcPkgProgram = T.ghcPkgProgram opts
|
|
|
|
}
|
2015-03-03 20:12:43 +00:00
|
|
|
|
|
|
|
-- | Primary interface to cabal-helper and intended single entrypoint to
|
|
|
|
-- constructing 'GmComponent's
|
|
|
|
--
|
|
|
|
-- The Component\'s 'gmcHomeModuleGraph' will be empty and has to be resolved by
|
|
|
|
-- 'resolveGmComponents'.
|
2015-03-28 01:30:51 +00:00
|
|
|
getComponents :: (MonadIO m, GmEnv m, GmLog m)
|
|
|
|
=> m [GmComponent GMCRaw ChEntrypoint]
|
|
|
|
getComponents = do
|
|
|
|
opt <- options
|
|
|
|
Cradle {..} <- cradle
|
|
|
|
let gmVer = GhcMod.version
|
|
|
|
chVer = VERSION_cabal_helper
|
|
|
|
d = (helperProgs opt
|
|
|
|
, cradleRootDir </> "dist"
|
|
|
|
, (gmVer, chVer)
|
|
|
|
)
|
|
|
|
withCabal $ cached cradleRootDir cabalHelperCache d
|
2015-03-15 19:48:55 +00:00
|
|
|
|
2015-03-28 01:30:51 +00:00
|
|
|
cabalHelperCache :: MonadIO m => Cached m
|
|
|
|
(Programs, FilePath, (Version, String))
|
|
|
|
[GmComponent GMCRaw ChEntrypoint]
|
|
|
|
cabalHelperCache = Cached {
|
|
|
|
cacheFile = cabalHelperCacheFile,
|
2015-04-12 00:39:18 +00:00
|
|
|
cachedAction = \ _ (progs, root, _) _ ->
|
2015-03-28 01:30:51 +00:00
|
|
|
runQuery' progs root $ do
|
2015-04-12 00:36:17 +00:00
|
|
|
q <- liftM5 join5
|
|
|
|
ghcOptions
|
|
|
|
ghcSrcOptions
|
|
|
|
ghcLangOptions
|
|
|
|
entrypoints
|
|
|
|
sourceDirs
|
|
|
|
let cs = flip map q $ \(cn, (opts, (srcOpts, (langOpts, (ep, srcDirs))))) ->
|
|
|
|
GmComponent cn opts srcOpts langOpts ep ep srcDirs mempty
|
2015-03-28 01:30:51 +00:00
|
|
|
return ([setupConfigPath], cs)
|
|
|
|
}
|
2015-03-15 19:48:55 +00:00
|
|
|
where
|
2015-04-12 00:36:17 +00:00
|
|
|
join5 a b c d = join' a . join' b . join' c . join' d
|
2015-03-15 19:48:55 +00:00
|
|
|
join' :: Eq a => [(a,b)] -> [(a,c)] -> [(a,(b,c))]
|
|
|
|
join' lb lc = [ (a, (b, c))
|
|
|
|
| (a, b) <- lb
|
|
|
|
, (a', c) <- lc
|
|
|
|
, a == a'
|
|
|
|
]
|
2015-03-03 20:12:43 +00:00
|
|
|
|
|
|
|
withCabal :: (MonadIO m, GmEnv m) => m a -> m a
|
|
|
|
withCabal action = do
|
|
|
|
crdl <- cradle
|
2015-03-07 18:23:55 +00:00
|
|
|
opts <- options
|
2015-03-03 20:12:43 +00:00
|
|
|
liftIO $ whenM (isSetupConfigOutOfDate <$> getCurrentWorld crdl) $
|
2015-03-07 18:23:55 +00:00
|
|
|
withDirectory_ (cradleRootDir crdl) $ do
|
2015-05-19 13:25:22 +00:00
|
|
|
let pkgDbStack = cradlePkgDbStack crdl
|
|
|
|
pkgDbArgs = if pkgDbStack == defaultPkgDbStack
|
|
|
|
then []
|
|
|
|
else "--package-db=clear" : map pkgDbArg pkgDbStack
|
2015-03-03 11:18:54 +00:00
|
|
|
progOpts =
|
2015-03-15 19:48:55 +00:00
|
|
|
[ "--with-ghc=" ++ T.ghcProgram opts ]
|
2015-03-07 18:23:55 +00:00
|
|
|
-- Only pass ghc-pkg if it was actually set otherwise we
|
|
|
|
-- might break cabal's guessing logic
|
2015-03-15 19:48:55 +00:00
|
|
|
++ if T.ghcPkgProgram opts /= T.ghcPkgProgram defaultOptions
|
|
|
|
then [ "--with-ghc-pkg=" ++ T.ghcPkgProgram opts ]
|
2015-03-07 18:23:55 +00:00
|
|
|
else []
|
2015-03-03 11:18:54 +00:00
|
|
|
++ pkgDbArgs
|
2015-03-15 19:48:55 +00:00
|
|
|
void $ readProcess (T.cabalProgram opts) ("configure":progOpts) ""
|
2015-03-28 01:31:29 +00:00
|
|
|
writeAutogenFiles $ cradleRootDir crdl </> "dist"
|
2015-03-03 20:12:43 +00:00
|
|
|
action
|
2015-03-03 11:18:54 +00:00
|
|
|
|
|
|
|
pkgDbArg :: GhcPkgDb -> String
|
|
|
|
pkgDbArg GlobalDb = "--package-db=global"
|
|
|
|
pkgDbArg UserDb = "--package-db=user"
|
|
|
|
pkgDbArg (PackageDb p) = "--package-db=" ++ p
|
2015-05-19 13:25:22 +00:00
|
|
|
|
|
|
|
defaultPkgDbStack :: [GhcPkgDb]
|
|
|
|
defaultPkgDbStack = [GlobalDb, UserDb]
|