-----------------------------------------------------------------------------
--
-- GHC Driver
--
-- (c) The University of Glasgow 2002
--
-----------------------------------------------------------------------------

module GHC.Driver.Phases (
   Phase(..),
   happensBefore, eqPhase, isStopLn,
   startPhase,
   phaseInputExt,

   StopPhase(..),
   stopPhaseToPhase,

   isHaskellishSuffix,
   isHaskellSrcSuffix,
   isBackpackishSuffix,
   isObjectSuffix,
   isCishSuffix,
   isDynLibSuffix,
   isHaskellUserSrcSuffix,
   isHaskellSigSuffix,
   isSourceSuffix,

   isHaskellishTarget,

   isHaskellishFilename,
   isHaskellSrcFilename,
   isHaskellSigFilename,
   isObjectFilename,
   isCishFilename,
   isDynLibFilename,
   isHaskellUserSrcFilename,
   isSourceFilename,

   phaseForeignLanguage
 ) where

import GHC.Prelude

import GHC.Platform

import GHC.ForeignSrcLang

import GHC.Types.SourceFile

import GHC.Utils.Outputable
import GHC.Utils.Panic
import GHC.Utils.Misc

import System.FilePath

-----------------------------------------------------------------------------
-- Phases

{-
   Phase of the           | Suffix saying | Flag saying   | (suffix of)
   compilation system     | ``start here''| ``stop after''| output file

   literate pre-processor | .lhs          | -             | -
   C pre-processor (opt.) | -             | -E            | -
   Haskell compiler       | .hs           | -C, -S        | .hc, .s
   C compiler (opt.)      | .hc or .c     | -S            | .s
   assembler              | .s  or .S     | -c            | .o
   linker                 | other         | -             | a.out
   linker (merge objects) | other         | -             | .o
-}

-- Phases we can actually stop after
data StopPhase = StopPreprocess -- ^ @-E@
               | StopC          -- ^ @-C@
               | StopAs         -- ^ @-S@
               | NoStop         -- ^ @-c@

stopPhaseToPhase :: StopPhase -> Phase
stopPhaseToPhase :: StopPhase -> Phase
stopPhaseToPhase StopPhase
StopPreprocess = Phase
anyHsc
stopPhaseToPhase StopPhase
StopC          = Phase
HCc
stopPhaseToPhase StopPhase
StopAs         = Bool -> Phase
As Bool
False
stopPhaseToPhase StopPhase
NoStop         = Phase
StopLn

-- | Untyped Phase description
data Phase
        = Unlit HscSource
        | Cpp   HscSource
        | HsPp  HscSource
        | Hsc   HscSource
        | Ccxx          -- Compile C++
        | Cc            -- Compile C
        | Cobjc         -- Compile Objective-C
        | Cobjcxx       -- Compile Objective-C++
        | HCc           -- Haskellised C (as opposed to vanilla C) compilation
        | As Bool       -- Assembler for regular assembly files (Bool: with-cpp)
        | LlvmOpt       -- Run LLVM opt tool over llvm assembly
        | LlvmLlc       -- LLVM bitcode to native assembly
        | LlvmMangle    -- Fix up TNTC by processing assembly produced by LLVM
        | CmmCpp        -- pre-process Cmm source
        | Cmm           -- parse & compile Cmm code
        | MergeForeign  -- merge in the foreign object files
        | Js            -- pre-process Js source

        -- The final phase is a pseudo-phase that tells the pipeline to stop.
        | StopLn        -- Stop, but linking will follow, so generate .o file
  deriving (Phase -> Phase -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Phase -> Phase -> Bool
$c/= :: Phase -> Phase -> Bool
== :: Phase -> Phase -> Bool
$c== :: Phase -> Phase -> Bool
Eq, Int -> Phase -> ShowS
[Phase] -> ShowS
Phase -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Phase] -> ShowS
$cshowList :: [Phase] -> ShowS
show :: Phase -> String
$cshow :: Phase -> String
showsPrec :: Int -> Phase -> ShowS
$cshowsPrec :: Int -> Phase -> ShowS
Show)

instance Outputable Phase where
    ppr :: Phase -> SDoc
ppr Phase
p = forall doc. IsLine doc => String -> doc
text (forall a. Show a => a -> String
show Phase
p)

anyHsc :: Phase
anyHsc :: Phase
anyHsc = HscSource -> Phase
Hsc (forall a. HasCallStack => String -> a
panic String
"anyHsc")

isStopLn :: Phase -> Bool
isStopLn :: Phase -> Bool
isStopLn Phase
StopLn = Bool
True
isStopLn Phase
_      = Bool
False

eqPhase :: Phase -> Phase -> Bool
-- Equality of constructors, ignoring the HscSource field
-- NB: the HscSource field can be 'bot'; see anyHsc above
eqPhase :: Phase -> Phase -> Bool
eqPhase (Unlit HscSource
_)   (Unlit HscSource
_)  = Bool
True
eqPhase (Cpp   HscSource
_)   (Cpp   HscSource
_)  = Bool
True
eqPhase (HsPp  HscSource
_)   (HsPp  HscSource
_)  = Bool
True
eqPhase (Hsc   HscSource
_)   (Hsc   HscSource
_)  = Bool
True
eqPhase Phase
Cc          Phase
Cc         = Bool
True
eqPhase Phase
Cobjc       Phase
Cobjc      = Bool
True
eqPhase Phase
HCc         Phase
HCc        = Bool
True
eqPhase (As Bool
x)      (As Bool
y)     = Bool
x forall a. Eq a => a -> a -> Bool
== Bool
y
eqPhase Phase
LlvmOpt     Phase
LlvmOpt    = Bool
True
eqPhase Phase
LlvmLlc     Phase
LlvmLlc    = Bool
True
eqPhase Phase
LlvmMangle  Phase
LlvmMangle = Bool
True
eqPhase Phase
CmmCpp      Phase
CmmCpp     = Bool
True
eqPhase Phase
Cmm         Phase
Cmm        = Bool
True
eqPhase Phase
MergeForeign Phase
MergeForeign  = Bool
True
eqPhase Phase
StopLn      Phase
StopLn     = Bool
True
eqPhase Phase
Ccxx        Phase
Ccxx       = Bool
True
eqPhase Phase
Cobjcxx     Phase
Cobjcxx    = Bool
True
eqPhase Phase
Js          Phase
Js         = Bool
True
eqPhase Phase
_           Phase
_          = Bool
False

-- MP: happensBefore is only used in preprocessPipeline, that usage should
-- be refactored and this usage removed.
happensBefore :: Platform -> Phase -> Phase -> Bool
happensBefore :: Platform -> Phase -> Phase -> Bool
happensBefore Platform
platform Phase
p1 Phase
p2 = Phase
p1 Phase -> Phase -> Bool
`happensBefore'` Phase
p2
    where Phase
StopLn happensBefore' :: Phase -> Phase -> Bool
`happensBefore'` Phase
_ = Bool
False
          Phase
x      `happensBefore'` Phase
y = Phase
after_x Phase -> Phase -> Bool
`eqPhase` Phase
y
                                   Bool -> Bool -> Bool
|| Phase
after_x Phase -> Phase -> Bool
`happensBefore'` Phase
y
              where after_x :: Phase
after_x = Platform -> Phase -> Phase
nextPhase Platform
platform Phase
x

nextPhase :: Platform -> Phase -> Phase
nextPhase :: Platform -> Phase -> Phase
nextPhase Platform
platform Phase
p
    -- A conservative approximation to the next phase, used in happensBefore
    = case Phase
p of
      Unlit HscSource
sf   -> HscSource -> Phase
Cpp  HscSource
sf
      Cpp   HscSource
sf   -> HscSource -> Phase
HsPp HscSource
sf
      HsPp  HscSource
sf   -> HscSource -> Phase
Hsc  HscSource
sf
      Hsc   HscSource
_    -> Phase
maybeHCc
      Phase
LlvmOpt    -> Phase
LlvmLlc
      Phase
LlvmLlc    -> Phase
LlvmMangle
      Phase
LlvmMangle -> Bool -> Phase
As Bool
False
      As Bool
_       -> Phase
MergeForeign
      Phase
Ccxx       -> Phase
MergeForeign
      Phase
Cc         -> Phase
MergeForeign
      Phase
Cobjc      -> Phase
MergeForeign
      Phase
Cobjcxx    -> Phase
MergeForeign
      Phase
CmmCpp     -> Phase
Cmm
      Phase
Cmm        -> Phase
maybeHCc
      Phase
HCc        -> Phase
MergeForeign
      Phase
MergeForeign -> Phase
StopLn
      Phase
Js         -> Phase
StopLn
      Phase
StopLn     -> forall a. HasCallStack => String -> a
panic String
"nextPhase: nothing after StopLn"
    where maybeHCc :: Phase
maybeHCc = if Platform -> Bool
platformUnregisterised Platform
platform
                     then Phase
HCc
                     else Bool -> Phase
As Bool
False

-- the first compilation phase for a given file is determined
-- by its suffix.
startPhase :: String -> Phase
startPhase :: String -> Phase
startPhase String
"lhs"      = HscSource -> Phase
Unlit HscSource
HsSrcFile
startPhase String
"lhs-boot" = HscSource -> Phase
Unlit HscSource
HsBootFile
startPhase String
"lhsig"    = HscSource -> Phase
Unlit HscSource
HsigFile
startPhase String
"hs"       = HscSource -> Phase
Cpp   HscSource
HsSrcFile
startPhase String
"hs-boot"  = HscSource -> Phase
Cpp   HscSource
HsBootFile
startPhase String
"hsig"     = HscSource -> Phase
Cpp   HscSource
HsigFile
startPhase String
"hscpp"    = HscSource -> Phase
HsPp  HscSource
HsSrcFile
startPhase String
"hspp"     = HscSource -> Phase
Hsc   HscSource
HsSrcFile
startPhase String
"hc"       = Phase
HCc
startPhase String
"c"        = Phase
Cc
startPhase String
"cpp"      = Phase
Ccxx
startPhase String
"C"        = Phase
Cc
startPhase String
"m"        = Phase
Cobjc
startPhase String
"M"        = Phase
Cobjcxx
startPhase String
"mm"       = Phase
Cobjcxx
startPhase String
"cc"       = Phase
Ccxx
startPhase String
"cxx"      = Phase
Ccxx
startPhase String
"s"        = Bool -> Phase
As Bool
False
startPhase String
"S"        = Bool -> Phase
As Bool
True
startPhase String
"ll"       = Phase
LlvmOpt
startPhase String
"bc"       = Phase
LlvmLlc
startPhase String
"lm_s"     = Phase
LlvmMangle
startPhase String
"o"        = Phase
StopLn
startPhase String
"cmm"      = Phase
CmmCpp
startPhase String
"cmmcpp"   = Phase
Cmm
startPhase String
"js"       = Phase
Js
startPhase String
_          = Phase
StopLn     -- all unknown file types

-- This is used to determine the extension for the output from the
-- current phase (if it generates a new file).  The extension depends
-- on the next phase in the pipeline.
phaseInputExt :: Phase -> String
phaseInputExt :: Phase -> String
phaseInputExt (Unlit HscSource
HsSrcFile)   = String
"lhs"
phaseInputExt (Unlit HscSource
HsBootFile)  = String
"lhs-boot"
phaseInputExt (Unlit HscSource
HsigFile)    = String
"lhsig"
phaseInputExt (Cpp   HscSource
_)           = String
"lpp"       -- intermediate only
phaseInputExt (HsPp  HscSource
_)           = String
"hscpp"     -- intermediate only
phaseInputExt (Hsc   HscSource
_)           = String
"hspp"      -- intermediate only
        -- NB: as things stand, phaseInputExt (Hsc x) must not evaluate x
        --     because runPhase uses the StopBefore phase to pick the
        --     output filename.  That could be fixed, but watch out.
phaseInputExt Phase
HCc                 = String
"hc"
phaseInputExt Phase
Ccxx                = String
"cpp"
phaseInputExt Phase
Cobjc               = String
"m"
phaseInputExt Phase
Cobjcxx             = String
"mm"
phaseInputExt Phase
Cc                  = String
"c"
phaseInputExt (As Bool
True)           = String
"S"
phaseInputExt (As Bool
False)          = String
"s"
phaseInputExt Phase
LlvmOpt             = String
"ll"
phaseInputExt Phase
LlvmLlc             = String
"bc"
phaseInputExt Phase
LlvmMangle          = String
"lm_s"
phaseInputExt Phase
CmmCpp              = String
"cmmcpp"
phaseInputExt Phase
Cmm                 = String
"cmm"
phaseInputExt Phase
MergeForeign        = String
"o"
phaseInputExt Phase
Js                  = String
"js"
phaseInputExt Phase
StopLn              = String
"o"

haskellish_src_suffixes, backpackish_suffixes, haskellish_suffixes, cish_suffixes,
    js_suffixes, haskellish_user_src_suffixes, haskellish_sig_suffixes
 :: [String]
-- When a file with an extension in the haskellish_src_suffixes group is
-- loaded in --make mode, its imports will be loaded too.
haskellish_src_suffixes :: [String]
haskellish_src_suffixes      = [String]
haskellish_user_src_suffixes forall a. [a] -> [a] -> [a]
++
                               [ String
"hspp", String
"hscpp" ]
haskellish_suffixes :: [String]
haskellish_suffixes          = [String]
haskellish_src_suffixes forall a. [a] -> [a] -> [a]
++
                               [ String
"hc", String
"cmm", String
"cmmcpp" ]
cish_suffixes :: [String]
cish_suffixes                = [ String
"c", String
"cpp", String
"C", String
"cc", String
"cxx", String
"s", String
"S", String
"ll", String
"bc", String
"lm_s", String
"m", String
"M", String
"mm" ]
js_suffixes :: [String]
js_suffixes                  = [ String
"js" ]

-- Will not be deleted as temp files:
haskellish_user_src_suffixes :: [String]
haskellish_user_src_suffixes =
  [String]
haskellish_sig_suffixes forall a. [a] -> [a] -> [a]
++ [ String
"hs", String
"lhs", String
"hs-boot", String
"lhs-boot" ]
haskellish_sig_suffixes :: [String]
haskellish_sig_suffixes      = [ String
"hsig", String
"lhsig" ]
backpackish_suffixes :: [String]
backpackish_suffixes         = [ String
"bkp" ]

objish_suffixes :: Platform -> [String]
-- Use the appropriate suffix for the system on which
-- the GHC-compiled code will run
objish_suffixes :: Platform -> [String]
objish_suffixes Platform
platform = case Platform -> OS
platformOS Platform
platform of
  OS
OSMinGW32 -> [ String
"o", String
"O", String
"obj", String
"OBJ" ]
  OS
_         -> [ String
"o" ]

dynlib_suffixes :: Platform -> [String]
dynlib_suffixes :: Platform -> [String]
dynlib_suffixes Platform
platform = case Platform -> OS
platformOS Platform
platform of
  OS
OSMinGW32 -> [String
"dll", String
"DLL"]
  OS
OSDarwin  -> [String
"dylib", String
"so"]
  OS
_         -> [String
"so"]

isHaskellishSuffix, isBackpackishSuffix, isHaskellSrcSuffix, isCishSuffix,
    isHaskellUserSrcSuffix, isJsSuffix, isHaskellSigSuffix
 :: String -> Bool
isHaskellishSuffix :: String -> Bool
isHaskellishSuffix     String
s = String
s forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
haskellish_suffixes
isBackpackishSuffix :: String -> Bool
isBackpackishSuffix    String
s = String
s forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
backpackish_suffixes
isHaskellSigSuffix :: String -> Bool
isHaskellSigSuffix     String
s = String
s forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
haskellish_sig_suffixes
isHaskellSrcSuffix :: String -> Bool
isHaskellSrcSuffix     String
s = String
s forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
haskellish_src_suffixes
isCishSuffix :: String -> Bool
isCishSuffix           String
s = String
s forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
cish_suffixes
isJsSuffix :: String -> Bool
isJsSuffix             String
s = String
s forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
js_suffixes
isHaskellUserSrcSuffix :: String -> Bool
isHaskellUserSrcSuffix String
s = String
s forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
haskellish_user_src_suffixes

isObjectSuffix, isDynLibSuffix :: Platform -> String -> Bool
isObjectSuffix :: Platform -> String -> Bool
isObjectSuffix Platform
platform String
s = String
s forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` Platform -> [String]
objish_suffixes Platform
platform
isDynLibSuffix :: Platform -> String -> Bool
isDynLibSuffix Platform
platform String
s = String
s forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` Platform -> [String]
dynlib_suffixes Platform
platform

isSourceSuffix :: String -> Bool
isSourceSuffix :: String -> Bool
isSourceSuffix String
suff  = String -> Bool
isHaskellishSuffix String
suff
                    Bool -> Bool -> Bool
|| String -> Bool
isCishSuffix String
suff
                    Bool -> Bool -> Bool
|| String -> Bool
isJsSuffix String
suff
                    Bool -> Bool -> Bool
|| String -> Bool
isBackpackishSuffix String
suff

-- | When we are given files (modified by -x arguments) we need
-- to determine if they are Haskellish or not to figure out
-- how we should try to compile it.  The rules are:
--
--      1. If no -x flag was specified, we check to see if
--         the file looks like a module name, has no extension,
--         or has a Haskell source extension.
--
--      2. If an -x flag was specified, we just make sure the
--         specified suffix is a Haskell one.
isHaskellishTarget :: (String, Maybe Phase) -> Bool
isHaskellishTarget :: (String, Maybe Phase) -> Bool
isHaskellishTarget (String
f,Maybe Phase
Nothing) =
  String -> Bool
looksLikeModuleName String
f Bool -> Bool -> Bool
|| String -> Bool
isHaskellSrcFilename String
f Bool -> Bool -> Bool
|| Bool -> Bool
not (String -> Bool
hasExtension String
f)
isHaskellishTarget (String
_,Just Phase
phase) =
  Phase
phase forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [ Bool -> Phase
As Bool
True, Bool -> Phase
As Bool
False, Phase
Cc, Phase
Cobjc, Phase
Cobjcxx, Phase
CmmCpp, Phase
Cmm, Phase
Js
                  , Phase
StopLn]

isHaskellishFilename, isHaskellSrcFilename, isCishFilename,
    isHaskellUserSrcFilename, isSourceFilename, isHaskellSigFilename
 :: FilePath -> Bool
-- takeExtension return .foo, so we drop 1 to get rid of the .
isHaskellishFilename :: String -> Bool
isHaskellishFilename     String
f = String -> Bool
isHaskellishSuffix     (forall a. Int -> [a] -> [a]
drop Int
1 forall a b. (a -> b) -> a -> b
$ ShowS
takeExtension String
f)
isHaskellSrcFilename :: String -> Bool
isHaskellSrcFilename     String
f = String -> Bool
isHaskellSrcSuffix     (forall a. Int -> [a] -> [a]
drop Int
1 forall a b. (a -> b) -> a -> b
$ ShowS
takeExtension String
f)
isCishFilename :: String -> Bool
isCishFilename           String
f = String -> Bool
isCishSuffix           (forall a. Int -> [a] -> [a]
drop Int
1 forall a b. (a -> b) -> a -> b
$ ShowS
takeExtension String
f)
isHaskellUserSrcFilename :: String -> Bool
isHaskellUserSrcFilename String
f = String -> Bool
isHaskellUserSrcSuffix (forall a. Int -> [a] -> [a]
drop Int
1 forall a b. (a -> b) -> a -> b
$ ShowS
takeExtension String
f)
isSourceFilename :: String -> Bool
isSourceFilename         String
f = String -> Bool
isSourceSuffix         (forall a. Int -> [a] -> [a]
drop Int
1 forall a b. (a -> b) -> a -> b
$ ShowS
takeExtension String
f)
isHaskellSigFilename :: String -> Bool
isHaskellSigFilename     String
f = String -> Bool
isHaskellSigSuffix     (forall a. Int -> [a] -> [a]
drop Int
1 forall a b. (a -> b) -> a -> b
$ ShowS
takeExtension String
f)

isObjectFilename, isDynLibFilename :: Platform -> FilePath -> Bool
isObjectFilename :: Platform -> String -> Bool
isObjectFilename Platform
platform String
f = Platform -> String -> Bool
isObjectSuffix Platform
platform (forall a. Int -> [a] -> [a]
drop Int
1 forall a b. (a -> b) -> a -> b
$ ShowS
takeExtension String
f)
isDynLibFilename :: Platform -> String -> Bool
isDynLibFilename Platform
platform String
f = Platform -> String -> Bool
isDynLibSuffix Platform
platform (forall a. Int -> [a] -> [a]
drop Int
1 forall a b. (a -> b) -> a -> b
$ ShowS
takeExtension String
f)

-- | Foreign language of the phase if the phase deals with a foreign code
phaseForeignLanguage :: Phase -> Maybe ForeignSrcLang
phaseForeignLanguage :: Phase -> Maybe ForeignSrcLang
phaseForeignLanguage Phase
phase = case Phase
phase of
  Phase
Cc           -> forall a. a -> Maybe a
Just ForeignSrcLang
LangC
  Phase
Ccxx         -> forall a. a -> Maybe a
Just ForeignSrcLang
LangCxx
  Phase
Cobjc        -> forall a. a -> Maybe a
Just ForeignSrcLang
LangObjc
  Phase
Cobjcxx      -> forall a. a -> Maybe a
Just ForeignSrcLang
LangObjcxx
  Phase
HCc          -> forall a. a -> Maybe a
Just ForeignSrcLang
LangC
  As Bool
_         -> forall a. a -> Maybe a
Just ForeignSrcLang
LangAsm
  Phase
MergeForeign -> forall a. a -> Maybe a
Just ForeignSrcLang
RawObject
  Phase
Js           -> forall a. a -> Maybe a
Just ForeignSrcLang
LangJs
  Phase
_            -> forall a. Maybe a
Nothing