packages feed

rehoo-0.1.1: Main.hs

{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ExtendedDefaultRules #-}
{-# OPTIONS_GHC -fno-warn-type-defaults #-}

module Main where

import           Control.Applicative
import           Control.Concurrent
import qualified Control.Concurrent.MSem as MSem
import           Control.Exception
import           Control.Monad hiding (sequence)
import           Data.Foldable
import qualified Data.List as L
import           Data.List.Split
import           Data.Text.Lazy as T hiding (filter, map, chunksOf)
import           Data.Traversable
import           Filesystem.Path
import           GHC.Conc
import           Prelude hiding (FilePath, sequence)
import           Shelly
import           System.Console.CmdArgs
import           System.Environment (getArgs, withArgs)
import           System.IO hiding (FilePath)

default (Text)

version :: String
version       = "0.1.0"

copyright :: String
copyright     = "2012"

reHooSummary :: String
reHooSummary = "rehoo v" ++ version ++ ", (C) John Wiegley " ++ copyright

data Rehoo = Rehoo
    { chunks  :: Int
    , jobs    :: Int
    , outfile :: String
    , dir     :: String }
    deriving (Data, Typeable, Show, Eq)

reHoo :: Rehoo
reHoo = Rehoo
    { chunks  = def &= typ "INT"
                &= help "Process INT .hoo's at a time (def: 16)"
    , jobs    = def &= name "j" &= typ "INT"
                &= help "Run INT hoogle combine's (def: # of capabilities)"
    , outfile = def &= typFile   &= help "Output file (defaults to default.hoo)"
    , dir     = def &= args &= typDir } &=
    summary reHooSummary &=
    program "rehoo" &=
    help "Rebuild default.hoo from many .hoo files in the given directory"

main :: IO ()
main = do
  -- process command-line options
  mainArgs <- getArgs
  opts     <- withArgs (if L.null mainArgs then ["--help"] else mainArgs)
                       (cmdArgs reHoo)

  caps <- GHC.Conc.getNumCapabilities

  let jobs'   = max (jobs opts) caps
      chunks' = max (chunks opts) 16

  _ <- GHC.Conc.setNumCapabilities jobs'

  hoos <- shelly $ filter <$> pure (`hasExtension` "hoo")
                          <*> (ls . fromText . T.pack . dir $ opts)

  putStrLn $ "Running with " ++ show jobs'
          ++ " workers and " ++ show chunks'
          ++ " sized chunks per worker"

  pool     <- MSem.new jobs'
  tempPath <- processHoos pool chunks' hoos

  shelly $ verbosely $ do
    let outputPath = fromText $ case outfile opts of
                                  "" -> "default.hoo"
                                  x  -> T.pack x
    rm_f outputPath
    mv tempPath outputPath

processHoos :: MSem.MSem Int -> Int -> [FilePath] -> IO FilePath
processHoos pool size hoos
  | L.length hoos > size =
    -- Split the list into 'size' sized chunks, then fork off a thread to
    -- recursively process each chunk.  The results are collected in series
    -- from MVars that each contain the final pathname of the subjob.
    bracket (traverse forkProcessHoos (chunksOf size hoos) >>=
             traverse takeMVar)
            (shelly . verbosely . traverse_ rm)
            (processHoos pool size)

  | otherwise = do
    -- Now that we have a list of files < size elements long, and we are
    -- already in our own thread, we can start the expensive process of
    -- running hoogle combine with the output going to a temp file.
    (tempPath, hndl) <- openTempFile "." "rehoo.hoo"
    hClose hndl

    MSem.with pool $
      shelly $ verbosely $
        run_ "hoogle" ( "combine" : "--outfile" : T.pack tempPath
                      : map toTextIgnore hoos)

    return . fromText . T.pack $ tempPath

  where
    forkProcessHoos :: [FilePath] -> IO (MVar FilePath)
    forkProcessHoos xs = do
      mVar <- newEmptyMVar
      _    <- forkIO $ processHoos pool size xs >>= putMVar mVar
      return mVar

-- Main.hs (rehoo) ends here