packages feed

ghcup-0.2.1.0: lib/GHCup/PlanJson.hs

module GHCup.PlanJson where

import GHCup.System.Directory

import Control.Monad    ( unless )
import System.FilePath

findPlanJson
    :: FilePath
    -> IO FilePath
findPlanJson fp = do
    planJsonFn <- do
            mRoot <- findProjectRoot fp
            case mRoot of
                Nothing  -> fail ("missing project root relative to: " ++ fp)
                Just dir -> fromBuilddir $ dir </> "dist-newstyle"

    havePlanJson <- doesFileExist planJsonFn

    unless havePlanJson $
        fail "missing 'plan.json' file; do you need to run 'cabal new-build'?"

    return planJsonFn
  where
    fromBuilddir distFolder = do
        haveDistFolder <- doesDirectoryExist distFolder

        unless haveDistFolder $
            fail ("missing " ++ show distFolder ++ " folder; do you need to run 'cabal new-build'?")

        return $ distFolder </> "cache" </> "plan.json"


-- | Find project root relative to a directory, this emulates cabal's current
-- heuristic, but is slightly more liberal. If no cabal.project is found,
-- cabal-install looks for *.cabal files in the specified directory only. This
-- function also considers *.cabal files in directories higher up in the
-- hierarchy.
findProjectRoot :: FilePath -> IO (Maybe FilePath)
findProjectRoot dir = do
    normalisedPath <- canonicalizePath dir
    let checkCabalProject d = do
            ex <- doesFileExist fn
            return $ if ex then Just d else Nothing
          where
            fn = d </> "cabal.project"

        checkCabal d = do
            files <- listDirectory' d
            return $ if any (isExtensionOf' ".cabal") files
                        then Just d
                        else Nothing

    result <- walkUpFolders checkCabalProject normalisedPath
    case result of
        Just rootDir -> pure $ Just rootDir
        Nothing      -> walkUpFolders checkCabal normalisedPath
  where
    isExtensionOf' :: String -> FilePath -> Bool
    isExtensionOf' ext fp = ext == takeExtension fp

    listDirectory' :: FilePath -> IO [FilePath]
    listDirectory' fp = filter isSpecialDir <$> getDirectoryContents fp
      where
        isSpecialDir f = f /= "." && f /= ".."

walkUpFolders :: (FilePath -> IO (Maybe a)) -> FilePath -> IO (Maybe a)
walkUpFolders dtest d0 = do
    home <- getHomeDirectory

    let go d | d == home  = pure Nothing
             | isDrive d  = pure Nothing
             | otherwise  = do
                   t <- dtest d
                   case t of
                     Nothing  -> go $ takeDirectory d
                     x@Just{} -> pure x

    go d0