module Development.Cake3.Utils.Slice
where

import Control.Applicative
import Control.Monad.Trans
import qualified Data.Set as S
import Data.Set (Set)

import System.Directory
import System.Environment
import System.Process
import System.Exit
import System.IO
import Text.Printf

import System.FilePath.Wrapper
import Development.Cake3
import Development.Cake3.Types
import Development.Cake3.Monad
import Development.Cake3.Writer

-- | Build the full Makefile named @fo@ and a set of 'sliced' versions.
-- 'Slicing' here means filtering out all rules depending on certain tools
-- (second element of every @sls@ pairs) and all the upstream rules.
writeSliced :: File -> [(File,[Tool])] -> Make a -> IO ()
writeSliced fo sls mk = do
  cwd <- currentDirLocation
  ms <- evalMake fo mk
  runMakeH ms (writeFile (topRel fo))

  ecs <- forM sls $ \(fs,ts) -> do

    -- FIXME: We re-evaluate all the @mk@ monad for every sliced version just
    -- because of target Makefile's name change. We clould have moved @ts@ to
    -- the MonadReader' layer or similar
    ms <- evalMake fs mk
    let rs = filterRecipesByToolsDeep ts (recipes ms)
    let ts = S.toList $ queryTargets rs

    putStrLn  $ printf "Writing %s" (escapeFile fs)
    runMakeH ms {
        recipes = (recipes ms) `S.difference` rs
      } (writeFile (topRel fs))

    putStrLn  $ printf "Executing: make -f %s %s" (escapeFile fo) (unwords $ map escapeFile ts)
    ec <- system $ printf "make -f %s %s" (escapeFile fo) (unwords $ map escapeFile ts)

    return (ec,fs)

  forM_ ecs $ \(ec,sln) ->
    case ec of
      ExitFailure i -> fail $ printf "Non-zero exit code (%d) while building %s\n" i (escapeFile sln)
      _ -> return ()