2017-03-06 23:19:57 +00:00
|
|
|
-- ghc-mod: Happy Haskell Hacking
|
2015-03-03 19:28:34 +00:00
|
|
|
-- 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/>.
|
|
|
|
|
2014-09-18 08:05:47 +00:00
|
|
|
{-# LANGUAGE CPP #-}
|
2015-06-01 14:54:50 +00:00
|
|
|
{-# LANGUAGE DoAndIfThenElse #-}
|
|
|
|
|
2017-05-28 02:22:56 +00:00
|
|
|
module GhcMod.Utils (
|
|
|
|
module GhcMod.Utils
|
2015-03-03 20:12:43 +00:00
|
|
|
, module Utils
|
|
|
|
, readProcess
|
|
|
|
) where
|
2014-04-19 06:20:16 +00:00
|
|
|
|
2015-03-03 20:12:43 +00:00
|
|
|
import Control.Applicative
|
2014-11-02 18:00:25 +00:00
|
|
|
import Data.Char
|
2015-07-04 14:49:48 +00:00
|
|
|
import qualified Data.Map as M
|
2015-08-16 20:20:00 +00:00
|
|
|
import Data.Maybe (fromMaybe)
|
2015-08-17 05:43:34 +00:00
|
|
|
import Data.Either (rights)
|
|
|
|
import Data.List (inits)
|
2015-06-01 14:54:50 +00:00
|
|
|
import Exception
|
2017-05-28 02:22:56 +00:00
|
|
|
import GhcMod.Error
|
|
|
|
import GhcMod.Types
|
|
|
|
import GhcMod.Monad.Types
|
2015-09-08 03:20:26 +00:00
|
|
|
import System.Directory
|
2015-06-01 14:54:50 +00:00
|
|
|
import System.Environment
|
2015-09-08 03:20:26 +00:00
|
|
|
import System.FilePath
|
2015-08-16 20:20:00 +00:00
|
|
|
import System.IO.Temp (createTempDirectory)
|
2015-06-01 14:54:50 +00:00
|
|
|
import System.Process (readProcess)
|
2015-03-03 20:12:43 +00:00
|
|
|
import Text.Printf
|
|
|
|
|
2016-08-03 11:14:31 +00:00
|
|
|
import Paths_ghc_mod (getLibexecDir, getBinDir)
|
2015-03-03 20:12:43 +00:00
|
|
|
import Utils
|
2015-08-03 01:09:56 +00:00
|
|
|
import Prelude
|
2014-04-30 23:48:03 +00:00
|
|
|
|
2014-04-19 06:20:16 +00:00
|
|
|
-- dropWhileEnd is not provided prior to base 4.5.0.0.
|
|
|
|
dropWhileEnd :: (a -> Bool) -> [a] -> [a]
|
|
|
|
dropWhileEnd p = foldr (\x xs -> if p x && null xs then [] else x : xs) []
|
2014-04-30 23:54:15 +00:00
|
|
|
|
2014-08-28 09:54:01 +00:00
|
|
|
withDirectory_ :: (MonadIO m, ExceptionMonad m) => FilePath -> m a -> m a
|
2014-05-01 00:10:42 +00:00
|
|
|
withDirectory_ dir action =
|
2015-06-01 14:54:50 +00:00
|
|
|
gbracket
|
|
|
|
(liftIO getCurrentDirectory)
|
|
|
|
(liftIO . setCurrentDirectory)
|
|
|
|
(\_ -> liftIO (setCurrentDirectory dir) >> action)
|
2014-09-18 08:05:47 +00:00
|
|
|
|
2014-11-02 18:00:25 +00:00
|
|
|
uniqTempDirName :: FilePath -> FilePath
|
2015-06-01 14:54:50 +00:00
|
|
|
uniqTempDirName dir =
|
|
|
|
"ghc-mod" ++ map escapeDriveChar drive ++ map escapePathChar path
|
|
|
|
where
|
|
|
|
(drive, path) = splitDrive dir
|
|
|
|
escapeDriveChar :: Char -> Char
|
2014-11-02 18:00:25 +00:00
|
|
|
escapeDriveChar c
|
2015-06-01 14:54:50 +00:00
|
|
|
| isAlphaNum c = c
|
|
|
|
| otherwise = '-'
|
|
|
|
escapePathChar :: Char -> Char
|
2014-11-02 18:00:25 +00:00
|
|
|
escapePathChar c
|
2015-06-01 14:54:50 +00:00
|
|
|
| c `elem` pathSeparators = '-'
|
|
|
|
| otherwise = c
|
2014-11-02 18:00:25 +00:00
|
|
|
|
|
|
|
newTempDir :: FilePath -> IO FilePath
|
2015-12-30 13:24:56 +00:00
|
|
|
newTempDir _dir =
|
|
|
|
flip createTempDirectory "ghc-mod" =<< getTemporaryDirectory
|
2014-11-02 18:00:25 +00:00
|
|
|
|
2015-08-03 03:20:14 +00:00
|
|
|
whenM :: Monad m => m Bool -> m () -> m ()
|
2015-03-03 20:12:43 +00:00
|
|
|
whenM mb ma = mb >>= flip when ma
|
2014-11-01 21:02:47 +00:00
|
|
|
|
2014-09-18 08:05:47 +00:00
|
|
|
-- | Returns the path to the currently running ghc-mod executable. With ghc<7.6
|
|
|
|
-- this is a guess but >=7.6 uses 'getExecutablePath'.
|
|
|
|
ghcModExecutable :: IO FilePath
|
2014-10-03 19:21:26 +00:00
|
|
|
ghcModExecutable = do
|
2017-01-12 16:03:31 +00:00
|
|
|
exe <- getExecutablePath'
|
|
|
|
stack <- lookupEnv "STACK_EXE"
|
|
|
|
case takeBaseName exe of
|
|
|
|
"spec" | Just _ <- stack ->
|
|
|
|
(</> "ghc-mod") <$> getBinDir
|
|
|
|
"spec" ->
|
|
|
|
(</> "dist/build/ghc-mod/ghc-mod") <$> getCurrentDirectory
|
|
|
|
"ghc-mod" ->
|
|
|
|
return exe
|
|
|
|
_ ->
|
|
|
|
return $ takeDirectory exe </> "ghc-mod"
|
2015-02-07 22:48:33 +00:00
|
|
|
|
2015-03-03 20:12:43 +00:00
|
|
|
getExecutablePath' :: IO FilePath
|
|
|
|
#if __GLASGOW_HASKELL__ >= 706
|
|
|
|
getExecutablePath' = getExecutablePath
|
2015-02-07 22:48:33 +00:00
|
|
|
#else
|
2015-03-03 20:12:43 +00:00
|
|
|
getExecutablePath' = getProgName
|
2014-10-06 06:29:05 +00:00
|
|
|
#endif
|
2015-04-13 22:51:03 +00:00
|
|
|
|
2015-04-14 19:39:11 +00:00
|
|
|
canonFilePath :: FilePath -> IO FilePath
|
2015-04-13 22:51:03 +00:00
|
|
|
canonFilePath f = do
|
|
|
|
p <- canonicalizePath f
|
|
|
|
e <- doesFileExist p
|
|
|
|
when (not e) $ error $ "canonFilePath: not a file: " ++ p
|
|
|
|
return p
|
2015-07-03 03:43:32 +00:00
|
|
|
|
|
|
|
withMappedFile :: (IOish m, GmState m, GmEnv m) =>
|
|
|
|
forall a. FilePath -> (FilePath -> m a) -> m a
|
2015-07-03 17:29:07 +00:00
|
|
|
withMappedFile file action = getCanonicalFileNameSafe file >>= lookupMMappedFile >>= runWithFile
|
2015-07-03 03:43:32 +00:00
|
|
|
where
|
2015-08-16 20:20:00 +00:00
|
|
|
runWithFile (Just to) = action $ fmPath to
|
2015-07-03 03:43:32 +00:00
|
|
|
runWithFile _ = action file
|
2015-07-03 17:25:54 +00:00
|
|
|
|
2015-07-03 17:29:07 +00:00
|
|
|
getCanonicalFileNameSafe :: (IOish m, GmEnv m) => FilePath -> m FilePath
|
2015-07-03 17:25:54 +00:00
|
|
|
getCanonicalFileNameSafe fn = do
|
2015-08-17 07:39:49 +00:00
|
|
|
let fn' = normalise fn
|
|
|
|
pl <- liftIO $ rights <$> (mapM ((try :: IO FilePath -> IO (Either SomeException FilePath)) . canonicalizePath . joinPath) $ reverse $ inits $ splitPath' fn')
|
2015-08-17 05:43:34 +00:00
|
|
|
return $
|
|
|
|
if (length pl > 0)
|
2015-08-17 07:39:49 +00:00
|
|
|
then joinPath $ (head pl):(drop (length pl - 1) (splitPath fn'))
|
2015-08-17 05:43:34 +00:00
|
|
|
else error "Current dir doesn't seem to exist?"
|
2015-08-17 07:39:49 +00:00
|
|
|
where
|
|
|
|
#if __GLASGOW_HASKELL__ < 710
|
|
|
|
splitPath' = (".":) . splitPath
|
|
|
|
#else
|
|
|
|
splitPath' = splitPath
|
|
|
|
#endif
|
2015-07-04 14:49:48 +00:00
|
|
|
|
|
|
|
mkRevRedirMapFunc :: (Functor m, GmState m, GmEnv m) => m (FilePath -> FilePath)
|
|
|
|
mkRevRedirMapFunc = do
|
2015-08-16 20:20:00 +00:00
|
|
|
rm <- M.fromList <$> map (uncurry mf) <$> M.toList <$> getMMappedFiles
|
2015-07-04 14:49:48 +00:00
|
|
|
crdl <- cradle
|
|
|
|
return $ \key ->
|
|
|
|
fromMaybe key
|
|
|
|
$ makeRelative (cradleRootDir crdl)
|
|
|
|
<$> M.lookup key rm
|
|
|
|
where
|
2015-08-16 20:20:00 +00:00
|
|
|
mf :: FilePath -> FileMapping -> (FilePath, FilePath)
|
|
|
|
mf from to = (fmPath to, from)
|
2015-08-28 07:44:20 +00:00
|
|
|
|
|
|
|
findFilesWith' :: (FilePath -> IO Bool) -> [FilePath] -> String -> IO [FilePath]
|
|
|
|
findFilesWith' _ [] _ = return []
|
|
|
|
findFilesWith' f (d:ds) fileName = do
|
|
|
|
let file = d </> fileName
|
|
|
|
exist <- doesFileExist file
|
|
|
|
b <- if exist then f file else return False
|
|
|
|
if b then do
|
|
|
|
files <- findFilesWith' f ds fileName
|
|
|
|
return $ file : files
|
|
|
|
else findFilesWith' f ds fileName
|
2015-09-08 03:20:26 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- Copyright : (c) The University of Glasgow 2001
|
|
|
|
-- | Make a path absolute by prepending the current directory (if it isn't
|
|
|
|
-- already absolute) and applying 'normalise' to the result.
|
|
|
|
--
|
|
|
|
-- If the path is already absolute, the operation never fails. Otherwise, the
|
|
|
|
-- operation may fail with the same exceptions as 'getCurrentDirectory'.
|
|
|
|
makeAbsolute' :: FilePath -> IO FilePath
|
|
|
|
makeAbsolute' = (normalise <$>) . absolutize
|
|
|
|
where absolutize path -- avoid the call to `getCurrentDirectory` if we can
|
2015-09-08 04:42:32 +00:00
|
|
|
| isRelative path = (</> path) <$> getCurrentDirectory
|
2015-09-08 03:20:26 +00:00
|
|
|
| otherwise = return path
|