{-| Module : Util.System Description : Utilities for interacting with the system. License : BSD3 Maintainer : The Idris Community. -} {-# LANGUAGE CPP, ForeignFunctionInterface #-} module Util.System( tempfile , withTempdir , rmFile , catchIO , isDarwin , isWindows , writeSource , writeSourceText , readSource , readSourceStrict , setupBundledCC , isATTY ) where import Control.Exception as CE import Control.Monad (when) import qualified Data.Text as T import qualified Data.Text.IO as TIO import Foreign.C import System.Directory (createDirectoryIfMissing, getTemporaryDirectory, removeDirectoryRecursive, removeFile) import System.FilePath (normalise, ()) import System.Info import System.IO import System.IO.Error #ifdef FREESTANDING import Data.List (intercalate) import System.Directory (doesDirectoryExist) import System.Environment (getEnv, getExecutablePath, setEnv) import System.FilePath (dropFileName, isAbsolute, searchPathSeparator) import Tools_idris #endif #ifdef mingw32_HOST_OS import Graphics.Win32.Misc (getStdHandle, sTD_OUTPUT_HANDLE) import System.Console.MinTTY (isMinTTYHandle) #endif catchIO :: IO a -> (IOError -> IO a) -> IO a catchIO = CE.catch isWindows :: Bool isWindows = os `elem` ["win32", "mingw32", "cygwin32"] isDarwin :: Bool isDarwin = os == "darwin" -- | Create a temp file with the extensiom ext (in the format ".xxx") tempfile :: String -> IO (FilePath, Handle) tempfile ext = do dir <- getTemporaryDirectory openTempFile (normalise dir) $ "idris" ++ ext -- | Read a source file, same as readFile but make sure the encoding is utf-8. readSource :: FilePath -> IO String readSource f = do h <- openFile f ReadMode hSetEncoding h utf8 hGetContents h -- | Read a source file, make sure that the it all has been read before exiting the function. -- | This is useful when we want to write the file again and need it to be closed. readSourceStrict :: FilePath -> IO String readSourceStrict f = withFile f ReadMode $ \h -> do hSetEncoding h utf8 src <- hGetContents h length src `seq` return src -- | Write a source file, same as writeFile except the encoding is set to utf-8 writeSource :: FilePath -> String -> IO () writeSource f s = withFile f WriteMode (\h -> hSetEncoding h utf8 >> hPutStr h s) -- | Write a utf-8 source file from Text writeSourceText :: FilePath -> T.Text -> IO () writeSourceText f s = withFile f WriteMode (\h -> hSetEncoding h utf8 >> TIO.hPutStr h s) foreign import ccall "isatty" isATTYRaw :: CInt -> IO CInt isATTY :: IO Bool isATTY = do tty <- isATTYRaw 1 -- fd stdout mintty <- isMinTTY return $ (tty /= 0) || mintty -- | Return 'True' if the process's standard output is attached to a MinTTY -- console (e.g., Cygwin or MSYS) on Windows. Return 'False' otherwise. -- -- Unfortunately, we must check this separately since 'isATTY' always returns -- 'False' on MinTTY consoles. isMinTTY :: IO Bool #ifdef mingw32_HOST_OS isMinTTY = do h <- getStdHandle sTD_OUTPUT_HANDLE isMinTTYHandle h #else isMinTTY = return False #endif withTempdir :: String -> (FilePath -> IO a) -> IO a withTempdir subdir callback = do dir <- getTemporaryDirectory let tmpDir = normalise dir subdir removeLater <- catchIO (createDirectoryIfMissing True tmpDir >> return True) (\ ioError -> if isAlreadyExistsError ioError then return False else throw ioError ) result <- callback tmpDir when removeLater $ removeDirectoryRecursive tmpDir return result rmFile :: FilePath -> IO () rmFile f = do result <- try (removeFile f) case result of Right _ -> putStrLn $ "Removed: " ++ f Left err -> handleExists err where handleExists e | isDoesNotExistError e = return () | otherwise = putStrLn $ "WARNING: Cannot remove file " ++ f ++ ", Error msg:" ++ show e setupBundledCC :: IO() #ifdef FREESTANDING setupBundledCC = when hasBundledToolchain $ do exePath <- getExecutablePath path <- getEnv "PATH" tcDir <- return getToolchainDir absolute <- return $ isAbsolute tcDir target <- return $ if absolute then tcDir else dropFileName exePath ++ tcDir present <- doesDirectoryExist target when present $ do newPath <- return $ intercalate [searchPathSeparator] [target, path] setEnv "PATH" newPath #else setupBundledCC = return () #endif