module Development.IDE.Core.Preprocessor
( preprocessor
) where
import Development.IDE.GHC.CPP
import Development.IDE.GHC.Orphans()
import Development.IDE.GHC.Compat
import GhcMonad
import StringBuffer as SB
import Data.List.Extra
import System.FilePath
import System.IO.Extra
import Data.Char
import qualified HeaderInfo as Hdr
import Development.IDE.Types.Diagnostics
import Development.IDE.Types.Location
import Development.IDE.GHC.Error
import SysTools (Option (..), runUnlit, runPp)
import Control.Monad.Trans.Except
import qualified GHC.LanguageExtensions as LangExt
import Data.Maybe
import Control.Exception.Safe (catch, throw)
import Data.IORef (IORef, modifyIORef, newIORef, readIORef)
import Data.Text (Text)
import qualified Data.Text as T
import Outputable (showSDoc)
import Control.DeepSeq (NFData(rnf))
import Control.Exception (evaluate)
import HscTypes (HscEnv(hsc_dflags))
preprocessor :: HscEnv -> FilePath -> Maybe StringBuffer -> ExceptT [FileDiagnostic] IO (StringBuffer, DynFlags)
preprocessor :: HscEnv
-> FilePath
-> Maybe StringBuffer
-> ExceptT [FileDiagnostic] IO (StringBuffer, DynFlags)
preprocessor HscEnv
env FilePath
filename Maybe StringBuffer
mbContents = do
(Bool
isOnDisk, StringBuffer
contents) <-
if FilePath -> Bool
isLiterate FilePath
filename then do
let dflags :: DynFlags
dflags = HscEnv -> DynFlags
hsc_dflags HscEnv
env
StringBuffer
newcontent <- IO StringBuffer -> ExceptT [FileDiagnostic] IO StringBuffer
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO StringBuffer -> ExceptT [FileDiagnostic] IO StringBuffer)
-> IO StringBuffer -> ExceptT [FileDiagnostic] IO StringBuffer
forall a b. (a -> b) -> a -> b
$ DynFlags -> FilePath -> Maybe StringBuffer -> IO StringBuffer
runLhs DynFlags
dflags FilePath
filename Maybe StringBuffer
mbContents
(Bool, StringBuffer)
-> ExceptT [FileDiagnostic] IO (Bool, StringBuffer)
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool
False, StringBuffer
newcontent)
else do
StringBuffer
contents <- IO StringBuffer -> ExceptT [FileDiagnostic] IO StringBuffer
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO StringBuffer -> ExceptT [FileDiagnostic] IO StringBuffer)
-> IO StringBuffer -> ExceptT [FileDiagnostic] IO StringBuffer
forall a b. (a -> b) -> a -> b
$ IO StringBuffer
-> (StringBuffer -> IO StringBuffer)
-> Maybe StringBuffer
-> IO StringBuffer
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (FilePath -> IO StringBuffer
hGetStringBuffer FilePath
filename) StringBuffer -> IO StringBuffer
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe StringBuffer
mbContents
let isOnDisk :: Bool
isOnDisk = Maybe StringBuffer -> Bool
forall a. Maybe a -> Bool
isNothing Maybe StringBuffer
mbContents
(Bool, StringBuffer)
-> ExceptT [FileDiagnostic] IO (Bool, StringBuffer)
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool
isOnDisk, StringBuffer
contents)
DynFlags
dflags <- IO (Either [FileDiagnostic] DynFlags)
-> ExceptT [FileDiagnostic] IO DynFlags
forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
ExceptT (IO (Either [FileDiagnostic] DynFlags)
-> ExceptT [FileDiagnostic] IO DynFlags)
-> IO (Either [FileDiagnostic] DynFlags)
-> ExceptT [FileDiagnostic] IO DynFlags
forall a b. (a -> b) -> a -> b
$ HscEnv
-> FilePath
-> StringBuffer
-> IO (Either [FileDiagnostic] DynFlags)
parsePragmasIntoDynFlags HscEnv
env FilePath
filename StringBuffer
contents
(Bool
isOnDisk, StringBuffer
contents, DynFlags
dflags) <-
if Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ Extension -> DynFlags -> Bool
xopt Extension
LangExt.Cpp DynFlags
dflags then
(Bool, StringBuffer, DynFlags)
-> ExceptT [FileDiagnostic] IO (Bool, StringBuffer, DynFlags)
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool
isOnDisk, StringBuffer
contents, DynFlags
dflags)
else do
IORef [CPPLog]
cppLogs <- IO (IORef [CPPLog]) -> ExceptT [FileDiagnostic] IO (IORef [CPPLog])
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (IORef [CPPLog])
-> ExceptT [FileDiagnostic] IO (IORef [CPPLog]))
-> IO (IORef [CPPLog])
-> ExceptT [FileDiagnostic] IO (IORef [CPPLog])
forall a b. (a -> b) -> a -> b
$ [CPPLog] -> IO (IORef [CPPLog])
forall a. a -> IO (IORef a)
newIORef []
StringBuffer
contents <- IO (Either [FileDiagnostic] StringBuffer)
-> ExceptT [FileDiagnostic] IO StringBuffer
forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
ExceptT
(IO (Either [FileDiagnostic] StringBuffer)
-> ExceptT [FileDiagnostic] IO StringBuffer)
-> IO (Either [FileDiagnostic] StringBuffer)
-> ExceptT [FileDiagnostic] IO StringBuffer
forall a b. (a -> b) -> a -> b
$ (StringBuffer -> Either [FileDiagnostic] StringBuffer
forall a b. b -> Either a b
Right (StringBuffer -> Either [FileDiagnostic] StringBuffer)
-> IO StringBuffer -> IO (Either [FileDiagnostic] StringBuffer)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (DynFlags -> FilePath -> Maybe StringBuffer -> IO StringBuffer
runCpp DynFlags
dflags {log_action :: LogAction
log_action = IORef [CPPLog] -> LogAction
logAction IORef [CPPLog]
cppLogs} FilePath
filename
(Maybe StringBuffer -> IO StringBuffer)
-> Maybe StringBuffer -> IO StringBuffer
forall a b. (a -> b) -> a -> b
$ if Bool
isOnDisk then Maybe StringBuffer
forall a. Maybe a
Nothing else StringBuffer -> Maybe StringBuffer
forall a. a -> Maybe a
Just StringBuffer
contents))
IO (Either [FileDiagnostic] StringBuffer)
-> (GhcException -> IO (Either [FileDiagnostic] StringBuffer))
-> IO (Either [FileDiagnostic] StringBuffer)
forall (m :: * -> *) e a.
(MonadCatch m, Exception e) =>
m a -> (e -> m a) -> m a
`catch`
( \(GhcException
e :: GhcException) -> do
[CPPLog]
logs <- IORef [CPPLog] -> IO [CPPLog]
forall a. IORef a -> IO a
readIORef IORef [CPPLog]
cppLogs
case FilePath -> [CPPLog] -> [FileDiagnostic]
diagsFromCPPLogs FilePath
filename ([CPPLog] -> [CPPLog]
forall a. [a] -> [a]
reverse [CPPLog]
logs) of
[] -> GhcException -> IO (Either [FileDiagnostic] StringBuffer)
forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throw GhcException
e
[FileDiagnostic]
diags -> Either [FileDiagnostic] StringBuffer
-> IO (Either [FileDiagnostic] StringBuffer)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either [FileDiagnostic] StringBuffer
-> IO (Either [FileDiagnostic] StringBuffer))
-> Either [FileDiagnostic] StringBuffer
-> IO (Either [FileDiagnostic] StringBuffer)
forall a b. (a -> b) -> a -> b
$ [FileDiagnostic] -> Either [FileDiagnostic] StringBuffer
forall a b. a -> Either a b
Left [FileDiagnostic]
diags
)
DynFlags
dflags <- IO (Either [FileDiagnostic] DynFlags)
-> ExceptT [FileDiagnostic] IO DynFlags
forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
ExceptT (IO (Either [FileDiagnostic] DynFlags)
-> ExceptT [FileDiagnostic] IO DynFlags)
-> IO (Either [FileDiagnostic] DynFlags)
-> ExceptT [FileDiagnostic] IO DynFlags
forall a b. (a -> b) -> a -> b
$ HscEnv
-> FilePath
-> StringBuffer
-> IO (Either [FileDiagnostic] DynFlags)
parsePragmasIntoDynFlags HscEnv
env FilePath
filename StringBuffer
contents
(Bool, StringBuffer, DynFlags)
-> ExceptT [FileDiagnostic] IO (Bool, StringBuffer, DynFlags)
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool
False, StringBuffer
contents, DynFlags
dflags)
if Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ GeneralFlag -> DynFlags -> Bool
gopt GeneralFlag
Opt_Pp DynFlags
dflags then
(StringBuffer, DynFlags)
-> ExceptT [FileDiagnostic] IO (StringBuffer, DynFlags)
forall (m :: * -> *) a. Monad m => a -> m a
return (StringBuffer
contents, DynFlags
dflags)
else do
StringBuffer
contents <- IO StringBuffer -> ExceptT [FileDiagnostic] IO StringBuffer
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO StringBuffer -> ExceptT [FileDiagnostic] IO StringBuffer)
-> IO StringBuffer -> ExceptT [FileDiagnostic] IO StringBuffer
forall a b. (a -> b) -> a -> b
$ DynFlags -> FilePath -> Maybe StringBuffer -> IO StringBuffer
runPreprocessor DynFlags
dflags FilePath
filename (Maybe StringBuffer -> IO StringBuffer)
-> Maybe StringBuffer -> IO StringBuffer
forall a b. (a -> b) -> a -> b
$ if Bool
isOnDisk then Maybe StringBuffer
forall a. Maybe a
Nothing else StringBuffer -> Maybe StringBuffer
forall a. a -> Maybe a
Just StringBuffer
contents
DynFlags
dflags <- IO (Either [FileDiagnostic] DynFlags)
-> ExceptT [FileDiagnostic] IO DynFlags
forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
ExceptT (IO (Either [FileDiagnostic] DynFlags)
-> ExceptT [FileDiagnostic] IO DynFlags)
-> IO (Either [FileDiagnostic] DynFlags)
-> ExceptT [FileDiagnostic] IO DynFlags
forall a b. (a -> b) -> a -> b
$ HscEnv
-> FilePath
-> StringBuffer
-> IO (Either [FileDiagnostic] DynFlags)
parsePragmasIntoDynFlags HscEnv
env FilePath
filename StringBuffer
contents
(StringBuffer, DynFlags)
-> ExceptT [FileDiagnostic] IO (StringBuffer, DynFlags)
forall (m :: * -> *) a. Monad m => a -> m a
return (StringBuffer
contents, DynFlags
dflags)
where
logAction :: IORef [CPPLog] -> LogAction
logAction :: IORef [CPPLog] -> LogAction
logAction IORef [CPPLog]
cppLogs DynFlags
dflags WarnReason
_reason Severity
severity SrcSpan
srcSpan PprStyle
_style MsgDoc
msg = do
let log :: CPPLog
log = Severity -> SrcSpan -> Text -> CPPLog
CPPLog Severity
severity SrcSpan
srcSpan (Text -> CPPLog) -> Text -> CPPLog
forall a b. (a -> b) -> a -> b
$ FilePath -> Text
T.pack (FilePath -> Text) -> FilePath -> Text
forall a b. (a -> b) -> a -> b
$ DynFlags -> MsgDoc -> FilePath
showSDoc DynFlags
dflags MsgDoc
msg
IORef [CPPLog] -> ([CPPLog] -> [CPPLog]) -> IO ()
forall a. IORef a -> (a -> a) -> IO ()
modifyIORef IORef [CPPLog]
cppLogs (CPPLog
log CPPLog -> [CPPLog] -> [CPPLog]
forall a. a -> [a] -> [a]
:)
data CPPLog = CPPLog Severity SrcSpan Text
deriving (Int -> CPPLog -> ShowS
[CPPLog] -> ShowS
CPPLog -> FilePath
(Int -> CPPLog -> ShowS)
-> (CPPLog -> FilePath) -> ([CPPLog] -> ShowS) -> Show CPPLog
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
showList :: [CPPLog] -> ShowS
$cshowList :: [CPPLog] -> ShowS
show :: CPPLog -> FilePath
$cshow :: CPPLog -> FilePath
showsPrec :: Int -> CPPLog -> ShowS
$cshowsPrec :: Int -> CPPLog -> ShowS
Show)
data CPPDiag
= CPPDiag
{ CPPDiag -> Range
cdRange :: Range,
CPPDiag -> Maybe DiagnosticSeverity
cdSeverity :: Maybe DiagnosticSeverity,
CPPDiag -> [Text]
cdMessage :: [Text]
}
deriving (Int -> CPPDiag -> ShowS
[CPPDiag] -> ShowS
CPPDiag -> FilePath
(Int -> CPPDiag -> ShowS)
-> (CPPDiag -> FilePath) -> ([CPPDiag] -> ShowS) -> Show CPPDiag
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
showList :: [CPPDiag] -> ShowS
$cshowList :: [CPPDiag] -> ShowS
show :: CPPDiag -> FilePath
$cshow :: CPPDiag -> FilePath
showsPrec :: Int -> CPPDiag -> ShowS
$cshowsPrec :: Int -> CPPDiag -> ShowS
Show)
diagsFromCPPLogs :: FilePath -> [CPPLog] -> [FileDiagnostic]
diagsFromCPPLogs :: FilePath -> [CPPLog] -> [FileDiagnostic]
diagsFromCPPLogs FilePath
filename [CPPLog]
logs =
(CPPDiag -> FileDiagnostic) -> [CPPDiag] -> [FileDiagnostic]
forall a b. (a -> b) -> [a] -> [b]
map (\CPPDiag
d -> (FilePath -> NormalizedFilePath
toNormalizedFilePath' FilePath
filename, ShowDiagnostic
ShowDiag, CPPDiag -> Diagnostic
cppDiagToDiagnostic CPPDiag
d)) ([CPPDiag] -> [FileDiagnostic]) -> [CPPDiag] -> [FileDiagnostic]
forall a b. (a -> b) -> a -> b
$
[CPPDiag] -> [CPPLog] -> [CPPDiag]
go [] [CPPLog]
logs
where
go :: [CPPDiag] -> [CPPLog] -> [CPPDiag]
go :: [CPPDiag] -> [CPPLog] -> [CPPDiag]
go [CPPDiag]
acc [] = [CPPDiag] -> [CPPDiag]
forall a. [a] -> [a]
reverse ([CPPDiag] -> [CPPDiag]) -> [CPPDiag] -> [CPPDiag]
forall a b. (a -> b) -> a -> b
$ (CPPDiag -> CPPDiag) -> [CPPDiag] -> [CPPDiag]
forall a b. (a -> b) -> [a] -> [b]
map (\CPPDiag
d -> CPPDiag
d {cdMessage :: [Text]
cdMessage = [Text] -> [Text]
forall a. [a] -> [a]
reverse ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$ CPPDiag -> [Text]
cdMessage CPPDiag
d}) [CPPDiag]
acc
go [CPPDiag]
acc (CPPLog Severity
sev (RealSrcSpan RealSrcSpan
span) Text
msg : [CPPLog]
logs) =
let diag :: CPPDiag
diag = Range -> Maybe DiagnosticSeverity -> [Text] -> CPPDiag
CPPDiag (RealSrcSpan -> Range
realSrcSpanToRange RealSrcSpan
span) (Severity -> Maybe DiagnosticSeverity
toDSeverity Severity
sev) [Text
msg]
in [CPPDiag] -> [CPPLog] -> [CPPDiag]
go (CPPDiag
diag CPPDiag -> [CPPDiag] -> [CPPDiag]
forall a. a -> [a] -> [a]
: [CPPDiag]
acc) [CPPLog]
logs
go (CPPDiag
diag : [CPPDiag]
diags) (CPPLog Severity
_sev (UnhelpfulSpan FastString
_) Text
msg : [CPPLog]
logs) =
[CPPDiag] -> [CPPLog] -> [CPPDiag]
go (CPPDiag
diag {cdMessage :: [Text]
cdMessage = Text
msg Text -> [Text] -> [Text]
forall a. a -> [a] -> [a]
: CPPDiag -> [Text]
cdMessage CPPDiag
diag} CPPDiag -> [CPPDiag] -> [CPPDiag]
forall a. a -> [a] -> [a]
: [CPPDiag]
diags) [CPPLog]
logs
go [] (CPPLog Severity
_sev (UnhelpfulSpan FastString
_) Text
_msg : [CPPLog]
logs) = [CPPDiag] -> [CPPLog] -> [CPPDiag]
go [] [CPPLog]
logs
cppDiagToDiagnostic :: CPPDiag -> Diagnostic
cppDiagToDiagnostic :: CPPDiag -> Diagnostic
cppDiagToDiagnostic CPPDiag
d =
Diagnostic :: Range
-> Maybe DiagnosticSeverity
-> Maybe NumberOrString
-> Maybe Text
-> Text
-> Maybe (List DiagnosticTag)
-> Maybe (List DiagnosticRelatedInformation)
-> Diagnostic
Diagnostic
{ $sel:_range:Diagnostic :: Range
_range = CPPDiag -> Range
cdRange CPPDiag
d,
$sel:_severity:Diagnostic :: Maybe DiagnosticSeverity
_severity = CPPDiag -> Maybe DiagnosticSeverity
cdSeverity CPPDiag
d,
$sel:_code:Diagnostic :: Maybe NumberOrString
_code = Maybe NumberOrString
forall a. Maybe a
Nothing,
$sel:_source:Diagnostic :: Maybe Text
_source = Text -> Maybe Text
forall a. a -> Maybe a
Just Text
"CPP",
$sel:_message:Diagnostic :: Text
_message = [Text] -> Text
T.unlines ([Text] -> Text) -> [Text] -> Text
forall a b. (a -> b) -> a -> b
$ CPPDiag -> [Text]
cdMessage CPPDiag
d,
$sel:_relatedInformation:Diagnostic :: Maybe (List DiagnosticRelatedInformation)
_relatedInformation = Maybe (List DiagnosticRelatedInformation)
forall a. Maybe a
Nothing,
$sel:_tags:Diagnostic :: Maybe (List DiagnosticTag)
_tags = Maybe (List DiagnosticTag)
forall a. Maybe a
Nothing
}
isLiterate :: FilePath -> Bool
isLiterate :: FilePath -> Bool
isLiterate FilePath
x = ShowS
takeExtension FilePath
x FilePath -> [FilePath] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [FilePath
".lhs",FilePath
".lhs-boot"]
parsePragmasIntoDynFlags
:: HscEnv
-> FilePath
-> SB.StringBuffer
-> IO (Either [FileDiagnostic] DynFlags)
parsePragmasIntoDynFlags :: HscEnv
-> FilePath
-> StringBuffer
-> IO (Either [FileDiagnostic] DynFlags)
parsePragmasIntoDynFlags HscEnv
env FilePath
fp StringBuffer
contents = DynFlags
-> Text -> IO DynFlags -> IO (Either [FileDiagnostic] DynFlags)
forall a.
DynFlags -> Text -> IO a -> IO (Either [FileDiagnostic] a)
catchSrcErrors DynFlags
dflags0 Text
"pragmas" (IO DynFlags -> IO (Either [FileDiagnostic] DynFlags))
-> IO DynFlags -> IO (Either [FileDiagnostic] DynFlags)
forall a b. (a -> b) -> a -> b
$ do
let opts :: [Located FilePath]
opts = DynFlags -> StringBuffer -> FilePath -> [Located FilePath]
Hdr.getOptions DynFlags
dflags0 StringBuffer
contents FilePath
fp
() -> IO ()
forall a. a -> IO a
evaluate (() -> IO ()) -> () -> IO ()
forall a b. (a -> b) -> a -> b
$ [Located FilePath] -> ()
forall a. NFData a => a -> ()
rnf [Located FilePath]
opts
(DynFlags
dflags, [Located FilePath]
_, [Warn]
_) <- DynFlags
-> [Located FilePath] -> IO (DynFlags, [Located FilePath], [Warn])
forall (m :: * -> *).
MonadIO m =>
DynFlags
-> [Located FilePath] -> m (DynFlags, [Located FilePath], [Warn])
parseDynamicFilePragma DynFlags
dflags0 [Located FilePath]
opts
DynFlags
dflags' <- HscEnv -> DynFlags -> IO DynFlags
initializePlugins HscEnv
env DynFlags
dflags
DynFlags -> IO DynFlags
forall (m :: * -> *) a. Monad m => a -> m a
return (DynFlags -> IO DynFlags) -> DynFlags -> IO DynFlags
forall a b. (a -> b) -> a -> b
$ DynFlags -> DynFlags
disableWarningsAsErrors DynFlags
dflags'
where dflags0 :: DynFlags
dflags0 = HscEnv -> DynFlags
hsc_dflags HscEnv
env
runLhs :: DynFlags -> FilePath -> Maybe SB.StringBuffer -> IO SB.StringBuffer
runLhs :: DynFlags -> FilePath -> Maybe StringBuffer -> IO StringBuffer
runLhs DynFlags
dflags FilePath
filename Maybe StringBuffer
contents = (FilePath -> IO StringBuffer) -> IO StringBuffer
forall a. (FilePath -> IO a) -> IO a
withTempDir ((FilePath -> IO StringBuffer) -> IO StringBuffer)
-> (FilePath -> IO StringBuffer) -> IO StringBuffer
forall a b. (a -> b) -> a -> b
$ \FilePath
dir -> do
let fout :: FilePath
fout = FilePath
dir FilePath -> ShowS
</> ShowS
takeFileName FilePath
filename FilePath -> ShowS
<.> FilePath
"unlit"
FilePath
filesrc <- case Maybe StringBuffer
contents of
Maybe StringBuffer
Nothing -> FilePath -> IO FilePath
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
filename
Just StringBuffer
cnts -> do
let fsrc :: FilePath
fsrc = FilePath
dir FilePath -> ShowS
</> ShowS
takeFileName FilePath
filename FilePath -> ShowS
<.> FilePath
"literate"
FilePath -> IOMode -> (Handle -> IO ()) -> IO ()
forall r. FilePath -> IOMode -> (Handle -> IO r) -> IO r
withBinaryFile FilePath
fsrc IOMode
WriteMode ((Handle -> IO ()) -> IO ()) -> (Handle -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \Handle
h ->
Handle -> StringBuffer -> IO ()
hPutStringBuffer Handle
h StringBuffer
cnts
FilePath -> IO FilePath
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
fsrc
FilePath -> FilePath -> IO ()
unlit FilePath
filesrc FilePath
fout
FilePath -> IO StringBuffer
SB.hGetStringBuffer FilePath
fout
where
unlit :: FilePath -> FilePath -> IO ()
unlit FilePath
filein FilePath
fileout = DynFlags -> [Option] -> IO ()
SysTools.runUnlit DynFlags
dflags (FilePath -> FilePath -> [Option]
args FilePath
filein FilePath
fileout)
args :: FilePath -> FilePath -> [Option]
args FilePath
filein FilePath
fileout = [
FilePath -> Option
SysTools.Option FilePath
"-h"
, FilePath -> Option
SysTools.Option (ShowS
escape FilePath
filename)
, FilePath -> FilePath -> Option
SysTools.FileOption FilePath
"" FilePath
filein
, FilePath -> FilePath -> Option
SysTools.FileOption FilePath
"" FilePath
fileout ]
escape :: ShowS
escape (Char
'\\':FilePath
cs) = Char
'\\'Char -> ShowS
forall a. a -> [a] -> [a]
:Char
'\\'Char -> ShowS
forall a. a -> [a] -> [a]
: ShowS
escape FilePath
cs
escape (Char
'\"':FilePath
cs) = Char
'\\'Char -> ShowS
forall a. a -> [a] -> [a]
:Char
'\"'Char -> ShowS
forall a. a -> [a] -> [a]
: ShowS
escape FilePath
cs
escape (Char
'\'':FilePath
cs) = Char
'\\'Char -> ShowS
forall a. a -> [a] -> [a]
:Char
'\''Char -> ShowS
forall a. a -> [a] -> [a]
: ShowS
escape FilePath
cs
escape (Char
c:FilePath
cs) = Char
c Char -> ShowS
forall a. a -> [a] -> [a]
: ShowS
escape FilePath
cs
escape [] = []
runCpp :: DynFlags -> FilePath -> Maybe SB.StringBuffer -> IO SB.StringBuffer
runCpp :: DynFlags -> FilePath -> Maybe StringBuffer -> IO StringBuffer
runCpp DynFlags
dflags FilePath
filename Maybe StringBuffer
contents = (FilePath -> IO StringBuffer) -> IO StringBuffer
forall a. (FilePath -> IO a) -> IO a
withTempDir ((FilePath -> IO StringBuffer) -> IO StringBuffer)
-> (FilePath -> IO StringBuffer) -> IO StringBuffer
forall a b. (a -> b) -> a -> b
$ \FilePath
dir -> do
let out :: FilePath
out = FilePath
dir FilePath -> ShowS
</> ShowS
takeFileName FilePath
filename FilePath -> ShowS
<.> FilePath
"out"
DynFlags
dflags <- DynFlags -> IO DynFlags
forall (f :: * -> *) a. Applicative f => a -> f a
pure (DynFlags -> IO DynFlags) -> DynFlags -> IO DynFlags
forall a b. (a -> b) -> a -> b
$ FilePath -> DynFlags -> DynFlags
addOptP FilePath
"-D__GHCIDE__" DynFlags
dflags
case Maybe StringBuffer
contents of
Maybe StringBuffer
Nothing -> do
DynFlags -> Bool -> FilePath -> FilePath -> IO ()
doCpp DynFlags
dflags Bool
True FilePath
filename FilePath
out
IO StringBuffer -> IO StringBuffer
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO StringBuffer -> IO StringBuffer)
-> IO StringBuffer -> IO StringBuffer
forall a b. (a -> b) -> a -> b
$ FilePath -> IO StringBuffer
SB.hGetStringBuffer FilePath
out
Just StringBuffer
contents -> do
DynFlags
dflags <- DynFlags -> IO DynFlags
forall (m :: * -> *) a. Monad m => a -> m a
return (DynFlags -> IO DynFlags) -> DynFlags -> IO DynFlags
forall a b. (a -> b) -> a -> b
$ FilePath -> DynFlags -> DynFlags
addIncludePathsQuote (ShowS
takeDirectory FilePath
filename) DynFlags
dflags
let inp :: FilePath
inp = FilePath
dir FilePath -> ShowS
</> FilePath
"___GHCIDE_MAGIC___"
FilePath -> IOMode -> (Handle -> IO ()) -> IO ()
forall r. FilePath -> IOMode -> (Handle -> IO r) -> IO r
withBinaryFile FilePath
inp IOMode
WriteMode ((Handle -> IO ()) -> IO ()) -> (Handle -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \Handle
h ->
Handle -> StringBuffer -> IO ()
hPutStringBuffer Handle
h StringBuffer
contents
DynFlags -> Bool -> FilePath -> FilePath -> IO ()
doCpp DynFlags
dflags Bool
True FilePath
inp FilePath
out
let tweak :: ShowS
tweak FilePath
x
| Just FilePath
x <- FilePath -> FilePath -> Maybe FilePath
forall a. Eq a => [a] -> [a] -> Maybe [a]
stripPrefix FilePath
"# " FilePath
x
, FilePath
"___GHCIDE_MAGIC___" FilePath -> FilePath -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` FilePath
x
, let num :: FilePath
num = (Char -> Bool) -> ShowS
forall a. (a -> Bool) -> [a] -> [a]
takeWhile (Bool -> Bool
not (Bool -> Bool) -> (Char -> Bool) -> Char -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
isSpace) FilePath
x
= FilePath
"# " FilePath -> ShowS
forall a. Semigroup a => a -> a -> a
<> FilePath
num FilePath -> ShowS
forall a. Semigroup a => a -> a -> a
<> FilePath
" \"" FilePath -> ShowS
forall a. Semigroup a => a -> a -> a
<> (Char -> Char) -> ShowS
forall a b. (a -> b) -> [a] -> [b]
map (\Char
x -> if Char -> Bool
isPathSeparator Char
x then Char
'/' else Char
x) FilePath
filename FilePath -> ShowS
forall a. Semigroup a => a -> a -> a
<> FilePath
"\""
| Bool
otherwise = FilePath
x
FilePath -> StringBuffer
stringToStringBuffer (FilePath -> StringBuffer) -> ShowS -> FilePath -> StringBuffer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [FilePath] -> FilePath
unlines ([FilePath] -> FilePath) -> (FilePath -> [FilePath]) -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ShowS -> [FilePath] -> [FilePath]
forall a b. (a -> b) -> [a] -> [b]
map ShowS
tweak ([FilePath] -> [FilePath])
-> (FilePath -> [FilePath]) -> FilePath -> [FilePath]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> [FilePath]
lines (FilePath -> StringBuffer) -> IO FilePath -> IO StringBuffer
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FilePath -> IO FilePath
readFileUTF8' FilePath
out
runPreprocessor :: DynFlags -> FilePath -> Maybe SB.StringBuffer -> IO SB.StringBuffer
runPreprocessor :: DynFlags -> FilePath -> Maybe StringBuffer -> IO StringBuffer
runPreprocessor DynFlags
dflags FilePath
filename Maybe StringBuffer
contents = (FilePath -> IO StringBuffer) -> IO StringBuffer
forall a. (FilePath -> IO a) -> IO a
withTempDir ((FilePath -> IO StringBuffer) -> IO StringBuffer)
-> (FilePath -> IO StringBuffer) -> IO StringBuffer
forall a b. (a -> b) -> a -> b
$ \FilePath
dir -> do
let out :: FilePath
out = FilePath
dir FilePath -> ShowS
</> ShowS
takeFileName FilePath
filename FilePath -> ShowS
<.> FilePath
"out"
FilePath
inp <- case Maybe StringBuffer
contents of
Maybe StringBuffer
Nothing -> FilePath -> IO FilePath
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
filename
Just StringBuffer
contents -> do
let inp :: FilePath
inp = FilePath
dir FilePath -> ShowS
</> ShowS
takeFileName FilePath
filename FilePath -> ShowS
<.> FilePath
"hs"
FilePath -> IOMode -> (Handle -> IO ()) -> IO ()
forall r. FilePath -> IOMode -> (Handle -> IO r) -> IO r
withBinaryFile FilePath
inp IOMode
WriteMode ((Handle -> IO ()) -> IO ()) -> (Handle -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \Handle
h ->
Handle -> StringBuffer -> IO ()
hPutStringBuffer Handle
h StringBuffer
contents
FilePath -> IO FilePath
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
inp
DynFlags -> [Option] -> IO ()
runPp DynFlags
dflags [FilePath -> Option
SysTools.Option FilePath
filename, FilePath -> Option
SysTools.Option FilePath
inp, FilePath -> FilePath -> Option
SysTools.FileOption FilePath
"" FilePath
out]
FilePath -> IO StringBuffer
SB.hGetStringBuffer FilePath
out