packages feed

hs-bindgen-1.0.0.0: app/HsBindgen/Cli/Info/Libclang.hs

-- | @hs-bindgen-cli info libclang@ command
--
-- Intended for qualified import.
--
-- > import HsBindgen.Cli.Info.Libclang qualified as Libclang
module HsBindgen.Cli.Info.Libclang (
    -- * CLI help
    info
    -- * Options
  , Opts(..)
  , parseOpts
    -- * Execution
  , exec
  ) where

import Options.Applicative hiding (info)
import Prettyprinter.Util qualified as PP
import System.Exit (exitFailure)

import Clang.Enum.Simple (SimpleEnum (..), simpleFromC)
import Clang.LowLevel.Core (CXErrorCode (..))

import HsBindgen.App
import HsBindgen.Boot
import HsBindgen.Clang
import HsBindgen.Config.ClangArgs
import HsBindgen.Imports
import HsBindgen.TraceMsg
import HsBindgen.Util.Tracer

{-------------------------------------------------------------------------------
  CLI help
-------------------------------------------------------------------------------}

info :: InfoMod a
info = mconcat [
      progDesc "Run libclang with empty input"
    , footerDoc . Just . PP.reflow $ mconcat infoHelpMessage
    ]

{-------------------------------------------------------------------------------
  Options
-------------------------------------------------------------------------------}

newtype Opts = Opts {
      clangArgsConfig :: ClangArgsConfig FilePath
    }

parseOpts :: Parser Opts
parseOpts = Opts <$> parseClangArgsConfig

{-------------------------------------------------------------------------------
  Execution
-------------------------------------------------------------------------------}

exec :: GlobalOpts -> Opts -> IO ()
exec global opts = do
    eRes <- withTracer tracerConfigWithoutASTReadError $ \tracer -> do
      clangArgs <- (.clangArgs) <$>
        getClangArtefacts (contramap TraceBoot tracer) opts.clangArgsConfig
      let hasNoUserOptions = hasNoUserClangOptions opts.clangArgsConfig
          setup = defaultClangSetup clangArgs $
            ClangInputMemory "hs-bindgen-nop.h" ""

      -- Emit informational message if no user options provided
      when (   hasNoUserOptions
            && global.unsafe.verbosity.level >= Info) $ do
        traceWith (contramap (TraceFrontend . FrontendClang) tracer) $
                  withCallStack ClangInvokedWithoutOptions

      withClang
        (contramap (TraceFrontend . FrontendClang) tracer)
        setup
        (const (pure ()))
    case eRes of
      Left e  -> do
        print $ prettyForTrace e
        exitFailure
      Right _ -> pure ()
  where
    -- Check if user provided any Clang options via command line
    --
    hasNoUserClangOptions :: ClangArgsConfig FilePath -> Bool
    hasNoUserClangOptions clangArgs = and [
          null clangArgs.argsBefore
        , null clangArgs.argsInner
        , null clangArgs.argsAfter
        ]

    -- Check if a trace is CXError_ASTReadError.
    -- This error is benign in the context of 'info libclang' when users
    -- provide diagnostic flags, as they cause Clang to exit without building an AST.
    --
    -- If trace is CXError_ASTReadError make it debug level which should
    -- supress it but still make it available if needed with Debug level
    -- logging
    --
    tracerConfigWithoutASTReadError =
      global.unsafe {
          customLogLevel = CustomLogLevel $ \trace actualLevel ->
            case trace of
              TraceFrontend (FrontendClang (ClangErrorCode (SimpleEnum x)))
                | Just CXError_ASTReadError <- simpleFromC x ->
                  Debug
              _ ->
                applyCustomLogLevel
                  global.unsafe.customLogLevel
                  trace
                  actualLevel
        }