never executed always true always false
    1 {-# LANGUAGE ScopedTypeVariables #-}
    2 module Reanimate.Driver.Check
    3   ( checkEnvironment
    4   , hasRSvg
    5   , hasInkscape
    6   , hasMagick
    7   , hasFFmpegRSvg
    8   ) where
    9 
   10 import           Control.Exception            (SomeException, handle)
   11 import           Control.Monad
   12 import           Data.Maybe
   13 import           Data.Version
   14 import           Reanimate.Misc               (runCmd_)
   15 import           Reanimate.Driver.Magick      (magickCmd)
   16 import           System.Console.ANSI.Codes
   17 import           System.Directory             (findExecutable)
   18 import           System.IO
   19 import           System.IO.Temp
   20 import           Text.ParserCombinators.ReadP
   21 import           Text.Printf
   22 
   23 --------------------------------------------------------------------------
   24 -- Check environment
   25 
   26 checkEnvironment :: IO ()
   27 checkEnvironment = do
   28     putStrLn "reanimate checks:"
   29     runCheck "Has ffmpeg" hasFFmpeg
   30     runCheck "Has ffmpeg(rsvg)" hasFFmpegRSvg
   31     runCheck "Has dvisvgm" hasDvisvgm
   32     runCheck "Has povray" hasPovray
   33     runCheck "Has blender" hasBlender
   34     runCheck "Has rsvg-convert" hasRSvg
   35     runCheck "Has inkscape" hasInkscape
   36     runCheck "Has imagemagick" hasMagick
   37     runCheck "Has LaTeX" hasLaTeX
   38     runCheck ("Has LaTeX package '"++ "babel" ++ "'") $ hasTeXPackage "latex"
   39       "[english]{babel}"
   40     forM_ latexPackages $ \pkg ->
   41       runCheck ("Has LaTeX package '"++ pkg ++ "'") $ hasTeXPackage "latex" $
   42         "{"++pkg++"}"
   43     runCheck "Has XeLaTeX" hasXeLaTeX
   44     forM_ xelatexPackages $ \pkg ->
   45       runCheck ("Has XeLaTeX package '"++ pkg ++ "'") $ hasTeXPackage "xelatex" $
   46         "{"++pkg++"}"
   47   where
   48     latexPackages =
   49       ["preview"
   50       ,"amsmath"
   51       --,"amssymb"
   52       --,"dsfont"
   53       --,"setspace"
   54       --,"relsize"
   55       --,"textcomp"
   56       --,"mathrsfs"
   57       --,"calligra"
   58       --,"wasysym"
   59       --,"ragged2e"
   60       --,"physics"
   61       --,"xcolor"
   62       --,"textcomp"
   63       --,"xfrac"
   64       --,"microtype"
   65       ]
   66     xelatexPackages =
   67       ["ctex"]
   68     runCheck msg fn = do
   69       printf "  %-35s" (msg ++ ":")
   70       val <- fn
   71       case val of
   72         Left err -> putStrLnColor Red err
   73         Right ok -> putStrLnColor Green ok
   74 
   75 putStrLnColor :: Color -> String -> IO ()
   76 putStrLnColor color msg =
   77   putStrLn $ setSGRCode [SetColor Foreground Vivid color] ++ msg ++ setSGRCode [Reset]
   78 
   79 -- latex, dvisvgm, xelatex
   80 
   81 hasLaTeX :: IO (Either String String)
   82 hasLaTeX = hasProgram "latex"
   83 
   84 hasXeLaTeX :: IO (Either String String)
   85 hasXeLaTeX = hasProgram "xelatex"
   86 
   87 hasDvisvgm :: IO (Either String String)
   88 hasDvisvgm = hasProgram "dvisvgm"
   89 
   90 hasPovray :: IO (Either String String)
   91 hasPovray = hasProgram "povray"
   92 
   93 hasFFmpeg :: IO (Either String String)
   94 hasFFmpeg = checkMinVersion minVersion <$> ffmpegVersion
   95   where
   96     minVersion = Version [4,1,3] []
   97 
   98 hasFFmpegRSvg :: IO (Either String String)
   99 hasFFmpegRSvg = do
  100   mbPath <- findExecutable "ffmpeg"
  101   case mbPath of
  102     Nothing -> return $ Left "n/a"
  103     Just path -> do
  104       ret <- runCmd_ path ["-version"]
  105       pure $ case ret of
  106         Right out | "--enable-librsvg" `elem` words out
  107           -> Right "yes"
  108         _ -> Left "no"
  109 
  110 hasBlender :: IO (Either String String)
  111 hasBlender = checkMinVersion minVersion <$> blenderVersion
  112   where
  113     minVersion = Version [2,80] []
  114 
  115 hasRSvg :: IO (Either String String)
  116 hasRSvg = checkMinVersion minVersion <$> rsvgVersion
  117   where
  118     minVersion = Version [2,44,0] []
  119 
  120 hasInkscape :: IO (Either String String)
  121 hasInkscape = checkMinVersion minVersion <$> inkscapeVersion
  122   where
  123     minVersion = Version [0,92] []
  124 
  125 hasMagick :: IO (Either String String)
  126 hasMagick = checkMinVersion minVersion <$> magickVersion
  127   where
  128     minVersion = Version [6,0,0] []
  129 
  130 ffmpegVersion :: IO (Maybe Version)
  131 ffmpegVersion = extractVersion "ffmpeg" ["-version"] $ \line ->
  132       case take 3 $ words line of
  133         ["ffmpeg", "version", vs] -> vs
  134         _                         -> ""
  135 
  136 blenderVersion :: IO (Maybe Version)
  137 blenderVersion = extractVersion "blender" ["--version"] $ \line ->
  138     case take 2 (words line) of
  139       ["Blender", vs] -> vs
  140       _               -> ""
  141 
  142 rsvgVersion :: IO (Maybe Version)
  143 rsvgVersion = extractVersion "rsvg-convert" ["--version"] $ \line ->
  144   case words line of
  145     ["rsvg-convert", "version", vs] -> vs
  146     _                               -> ""
  147 
  148 inkscapeVersion :: IO (Maybe Version)
  149 inkscapeVersion = extractVersion "inkscape" ["--version"] $ \line ->
  150     case take 2 $ words line of
  151       ["Inkscape", vs] -> vs
  152       _                -> ""
  153 
  154 magickVersion :: IO (Maybe Version)
  155 magickVersion = extractVersion magickCmd ["-version"] $ \line ->
  156     case take 3 $ words line of
  157       ["Version:", "ImageMagick", vs] -> vs
  158       _                               -> ""
  159 
  160 checkMinVersion :: Version -> Maybe Version -> Either String String
  161 checkMinVersion _minVersion Nothing = Left "no"
  162 checkMinVersion minVersion (Just vs)
  163   | vs < minVersion = Left $ "too old: " ++ showVersion vs ++ " < " ++ showVersion minVersion
  164   | otherwise       = Right (showVersion vs)
  165 
  166 extractVersion :: FilePath -> [String] -> (String -> String) -> IO (Maybe Version)
  167 extractVersion execPath args outputFilter = do
  168   mbPath <- findExecutable execPath
  169   case mbPath of
  170     Nothing -> return Nothing
  171     Just path -> do
  172       ret <- runCmd_ path args
  173       case ret of
  174         Left{} -> return $ Just noVersion
  175         Right out ->
  176           pure $ Just $ fromMaybe noVersion $ parseVS $ outputFilter out
  177   where
  178     noVersion = Version [] []
  179     parseVS vs = listToMaybe $ reverse
  180       [ v | (v, _) <- readP_to_S parseVersion vs ]
  181 
  182 hasTeXPackage :: FilePath -> String -> IO (Either String String)
  183 hasTeXPackage exec pkg = handle (\(_::SomeException) -> return $ Left "n/a") $
  184     withSystemTempDirectory "reanimate" $ \tmp_dir -> withTempFile tmp_dir "test.tex" $ \tex_file tex_handle -> do
  185       hPutStr tex_handle tex_document
  186       hPutStr tex_handle $ "\\usepackage" ++ pkg ++ "\n"
  187       hPutStr tex_handle "\\begin{document}\n"
  188       hPutStr tex_handle "blah\n"
  189       hPutStr tex_handle tex_epilogue
  190       hClose tex_handle
  191       ret <- runCmd_ exec ["-interaction=batchmode", "-halt-on-error", "-output-directory="++tmp_dir, tex_file]
  192       return $ case ret of
  193         Right{} -> Right "OK"
  194         Left{}  -> Left "missing"
  195   where
  196     tex_document = "\\documentclass[preview]{standalone}\n"
  197     tex_epilogue =
  198       "\n\
  199       \\\end{document}"
  200 
  201 hasProgram :: String -> IO (Either String String)
  202 hasProgram exec = do
  203   mbPath <- findExecutable exec
  204   return $ case mbPath of
  205     Nothing   -> Left $ "'" ++ exec ++ "' not found"
  206     Just path -> Right path