packages feed

crux-llvm-0.8: src/CruxLLVMMain.hs

{-# Language DeriveAnyClass #-}
{-# Language DeriveGeneric #-}
{-# Language ImplicitParams #-}
{-# Language LambdaCase #-}
{-# Language OverloadedStrings #-}
{-# Language RankNTypes #-}

module CruxLLVMMain
  ( CruxLLVMLogging
  , cruxLLVMLoggingToSayWhat
  , defaultOutputConfig
  , mainWithOptions
  , mainWithOutputTo
  , mainWithOutputConfig
  , Crux.mkOutputConfig
  , processLLVMOptions
  , withCruxLLVMLogging
  )
  where

import           Data.Aeson ( ToJSON )
import           GHC.Generics ( Generic )
import           System.Directory ( createDirectoryIfMissing )
import           System.Exit ( ExitCode )
import           System.FilePath ( dropExtension, takeFileName, (</>) )
import           System.IO ( Handle )

-- crux
import qualified Crux
import qualified Crux.Log as Log
import           Crux.Config.Common (CruxOptions(..), OutputOptions)

-- local
import           Crux.LLVM.Config
import           Crux.LLVM.Compile
import qualified Crux.LLVM.Log as Log
import           Crux.LLVM.Simulate
import           Paths_crux_llvm (version)


mainWithOutputTo :: Handle -> IO ExitCode
mainWithOutputTo h = mainWithOutputConfig $
  Crux.mkOutputConfig (h, True) (h, True) cruxLLVMLoggingToSayWhat

data CruxLLVMLogging
  = LoggingCrux Crux.CruxLogMessage
  | LoggingCruxLLVM Log.CruxLLVMLogMessage
  deriving ( Generic, ToJSON )

cruxLLVMLoggingToSayWhat :: CruxLLVMLogging -> Crux.SayWhat
cruxLLVMLoggingToSayWhat (LoggingCrux msg) = Log.cruxLogMessageToSayWhat msg
cruxLLVMLoggingToSayWhat (LoggingCruxLLVM msg) = Log.cruxLLVMLogMessageToSayWhat msg

defaultOutputConfig :: IO (Maybe OutputOptions -> Log.OutputConfig CruxLLVMLogging)
defaultOutputConfig = Crux.defaultOutputConfig cruxLLVMLoggingToSayWhat

withCruxLLVMLogging ::
  (
    Log.SupportsCruxLogMessage CruxLLVMLogging =>
    Log.SupportsCruxLLVMLogMessage CruxLLVMLogging =>
    a
  ) -> a
withCruxLLVMLogging a =
  let
    ?injectCruxLogMessage = LoggingCrux
    ?injectCruxLLVMLogMessage = LoggingCruxLLVM
  in a

mainWithOutputConfig ::
  (Maybe OutputOptions -> Log.OutputConfig CruxLLVMLogging) ->
  IO ExitCode
mainWithOutputConfig mkOutCfg = withCruxLLVMLogging $ do
  cfg <- llvmCruxConfig
  Crux.loadOptions mkOutCfg "crux-llvm" version cfg mainWithOptions

mainWithOptions ::
  Log.Logs msgs =>
  Log.SupportsCruxLogMessage msgs =>
  Log.SupportsCruxLLVMLogMessage msgs =>
  (CruxOptions, LLVMOptions) -> IO ExitCode
mainWithOptions initOpts =
  do
    (cruxOpts, llvmOpts) <- processLLVMOptions initOpts
    bcFile <- genBitCode cruxOpts llvmOpts
    res <- Crux.runSimulator cruxOpts $ simulateLLVMFile bcFile llvmOpts
    makeCounterExamplesLLVM cruxOpts llvmOpts res
    Crux.postprocessSimResult True cruxOpts res

processLLVMOptions :: (CruxOptions,LLVMOptions) -> IO (CruxOptions,LLVMOptions)
processLLVMOptions (cruxOpts,llvmOpts) =
  do -- keep looking for clangBin if it is unset
     clangFilePath <- if clangBin llvmOpts == ""
                        then getClang
                        else return (clangBin llvmOpts)

     let llvmOpts2 = llvmOpts { clangBin = clangFilePath }

     -- update outDir if unset
     name <- case Crux.inputFiles cruxOpts of
               x : _ -> pure (dropExtension (takeFileName x))
                          -- use the first file as output directory
               [] -> throwCError NoFiles

     let cruxOpts2 = if Crux.outDir cruxOpts == ""
                       then cruxOpts { Crux.outDir = "results" </> name }
                       else cruxOpts

         odir      = Crux.outDir cruxOpts2

     createDirectoryIfMissing True odir

     return (cruxOpts2, llvmOpts2)