{-# LANGUAGE ScopedTypeVariables #-}
module Hie.Implicit.Cradle
( loadImplicitHieCradle,
)
where
import Control.Applicative ((<|>))
import Control.Exception (handleJust)
import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.Trans.Maybe
import Data.List
import Data.Maybe
import Data.Ord (Down (..))
import qualified Data.Text as T
import Data.Void
import qualified Data.Yaml as Yaml
import GHC.Fingerprint (fingerprintString)
import HIE.Bios.Config hiding (cabalComponent, stackComponent)
import HIE.Bios.Cradle
import HIE.Bios.Environment (getCacheDir)
import HIE.Bios.Types hiding (ActionName (..))
import qualified HIE.Bios.Types as Types
import HIE.Bios.Wrappers
import Hie.Cabal.Parser
import Hie.Locate
import Hie.Yaml
import System.Directory hiding (findFile)
import System.Environment
import System.Exit
import System.FilePath
import System.IO
import System.IO.Error (isPermissionError)
import System.IO.Temp
import System.Info.Extra (isWindows)
import System.PosixCompat.Files
import System.Process
loadImplicitHieCradle :: FilePath -> IO (Cradle a)
loadImplicitHieCradle wfile = do
let wdir = takeDirectory wfile
cfg <- runMaybeT (implicitConfig wdir)
return $ case cfg of
Just bc -> getCradle absurd bc
Nothing -> defaultCradle wdir
implicitConfig :: FilePath -> MaybeT IO (CradleConfig a, FilePath)
implicitConfig fp = do
(crdType, wdir) <- implicitConfig' fp
return (CradleConfig [] crdType, wdir)
implicitConfig' :: FilePath -> MaybeT IO (CradleType a, FilePath)
implicitConfig' fp =
( \wdir ->
(Bios (Program $ wdir </> ".hie-bios") Nothing Nothing, wdir)
)
<$> biosWorkDir fp
<|> (cabalExecutable >> cabalProjectDir fp >> cabalDistDir fp >>= cabal)
<|> (stackExecutable >> stackYamlDir fp >> stackWorkDir fp >>= stack)
<|> (cabalExecutable >> cabalProjectDir fp >>= cabal)
<|> (stackExecutable >> stackYamlDir fp >>= stack)
<|> (cabalExecutable >> cabalFile fp >>= cabal)
where
readPkgs f gp p = do
cfs <- gp p
pkgs <- liftIO $ catMaybes <$> mapM (nestedPkg p) cfs
pure $ concatMap (components f) pkgs
build cn cc gp p = do
c <- cn <$> readPkgs cc gp p
pure (c, p)
cabal :: FilePath -> MaybeT IO (CradleType a, FilePath)
cabal = build (CabalMulti mempty) cabalComponent' cabalPkgs
stack :: FilePath -> MaybeT IO (CradleType a, FilePath)
stack = build (StackMulti mempty) stackComponent' stackYamlPkgs
components f (Package n cs) = map (f n) cs
cabalComponent' n c = CabalType . Just <$> cabalComponent n c
stackComponent' n c = flip StackType Nothing . Just <$> stackComponent n c
cabalCradleDependencies :: FilePath -> IO [FilePath]
cabalCradleDependencies rootDir = do
cabalFiles <- findCabalFiles rootDir
return $ cabalFiles ++ ["cabal.project", "cabal.project.local"]
findCabalFiles :: FilePath -> IO [FilePath]
findCabalFiles wdir = do
dirContent <- listDirectory wdir
return $ filter ((== ".cabal") . takeExtension) dirContent
type GhcProc = (FilePath, [String])
cabalExecutable :: MaybeT IO FilePath
cabalExecutable = MaybeT $ findExecutable "cabal"
cabalDistDir :: FilePath -> MaybeT IO FilePath
cabalDistDir = findSubdirUpwards isCabal
where
isCabal name = name == "dist-newstyle" || name == "dist"
cabalProjectDir :: FilePath -> MaybeT IO FilePath
cabalProjectDir = findFileUpwards isCabal
where
isCabal name = name == "cabal.project"
cabalFile :: FilePath -> MaybeT IO FilePath
cabalFile = findFileUpwards isCabal
where
isCabal = (".cabal" ==) . takeExtension
stackCradleDependencies :: FilePath -> IO [FilePath]
stackCradleDependencies wdir = do
cabalFiles <- findCabalFiles wdir
return $ cabalFiles ++ ["package.yaml", "stack.yaml"]
combineExitCodes :: [ExitCode] -> ExitCode
combineExitCodes = foldr go ExitSuccess
where
go ExitSuccess b = b
go a _ = a
stackExecutable :: MaybeT IO FilePath
stackExecutable = MaybeT $ findExecutable "stack"
stackWorkDir :: FilePath -> MaybeT IO FilePath
stackWorkDir = findSubdirUpwards isStack
where
isStack name = name == ".stack-work"
stackYamlDir :: FilePath -> MaybeT IO FilePath
stackYamlDir = findFileUpwards isStack
where
isStack name = name == "stack.yaml"
findSubdirUpwards :: (FilePath -> Bool) -> FilePath -> MaybeT IO FilePath
findSubdirUpwards p dir = findContentUpwards p' dir
where p' subdir = do
exists <- doesDirectoryExist $ dir </> subdir
return $ (p subdir) && exists
findFileUpwards :: (FilePath -> Bool) -> FilePath -> MaybeT IO FilePath
findFileUpwards p dir = findContentUpwards p' dir
where p' file = do
exists <- doesFileExist $ dir </> file
return $ (p file) && exists
findContentUpwards :: (FilePath -> IO Bool) -> FilePath -> MaybeT IO FilePath
findContentUpwards p dir = do
cnts <-
liftIO $
handleJust
(\(e :: IOError) -> if isPermissionError e then Just [] else Nothing)
pure
(findContent p dir)
case cnts of
[]
| dir' == dir -> fail "No cabal files"
| otherwise -> findContentUpwards p dir'
_ : _ -> return dir
where
dir' = takeDirectory dir
findContent :: (FilePath -> IO Bool) -> FilePath -> IO [FilePath]
findContent p dir = do
b <- doesDirectoryExist dir
if b then getFiles else pure []
where
getFiles = getDirectoryContents dir >>= filterM p
biosWorkDir :: FilePath -> MaybeT IO FilePath
biosWorkDir = findFileUpwards (".hie-bios" ==)