module Development.Shake.Internal.Demo(demo) where
import Development.Shake.Internal.Paths
import Development.Shake.Command
import Control.Exception.Extra
import Control.Monad
import Data.List.Extra
import Data.Maybe
import System.Directory
import System.Exit
import System.FilePath
import General.Extra
import Development.Shake.FilePath(exe)
import System.IO
import System.Info.Extra
demo :: Bool -> IO ()
demo :: Bool -> IO ()
demo Bool
auto = do
Handle -> BufferMode -> IO ()
hSetBuffering Handle
stdout BufferMode
NoBuffering
String -> IO ()
putStrLn (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"% Welcome to the Shake v" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
shakeVersionString String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" demo mode!"
String -> IO ()
putStr String
"% Detecting machine configuration... "
hasManual <- IO Bool
hasManualData
ghc <- isJust <$> findExecutable "ghc"
(gcc, gccPath) <- findGcc
shakeLib <- wrap $ fmap (not . null . words . fromStdout) (cmd ("ghc-pkg list --simple-output shake" :: String))
ninja <- findExecutable "ninja"
putStrLn "done\n"
let path = if Bool
isWindows then String
"%PATH%" else String
"$PATH"
require ghc $ "% You don't have 'ghc' on your " ++ path ++ ", which is required to run the demo."
require gcc $ "% You don't have 'gcc' on your " ++ path ++ ", which is required to run the demo."
require shakeLib "% You don't have the 'shake' library installed with GHC, which is required to run the demo."
require hasManual "% You don't have the Shake data files installed, which are required to run the demo."
empty <- all (all (== '.')) <$> getDirectoryContents "."
dir <- if empty then getCurrentDirectory else do
home <- getHomeDirectory
dir <- getDirectoryContents home
pure $ home </> headErr (map ("shake-demo" ++) ("":map show [2..]) \\ dir)
putStrLn "% The Shake demo uses an empty directory, OK to use:"
putStrLn $ "% " ++ dir
b <- yesNo auto
require b "% Please create an empty directory to run the demo from, then run 'shake --demo' again."
putStr "% Copying files... "
copyManualData dir
unless isWindows $ do
p <- getPermissions $ dir </> "build.sh"
setPermissions (dir </> "build.sh") p{executable=True}
putStrLn "done"
let pause = do
String -> IO ()
putStr String
"% Press ENTER to continue: "
if Bool
auto then String -> IO String
putLine String
"" else IO String
getLine
let execute String
x = do
String -> IO ()
putStrLn (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"% RUNNING: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
x
CmdOption -> CmdOption -> CmdOption -> String -> IO ()
forall args r. (Partial, CmdArguments args) => args
cmd (String -> CmdOption
Cwd String
dir) ([String] -> [String] -> CmdOption
AddPath [] (Maybe String -> [String]
forall a. Maybe a -> [a]
maybeToList Maybe String
gccPath)) CmdOption
Shell String
x :: IO ()
let build = if Bool
isWindows then String
"build" else String
"./build.sh"
putStrLn "\n% [1/5] Building an example project with Shake."
pause
putStrLn $ "% RUNNING: cd " ++ dir
execute build
putStrLn "\n% [2/5] Running the produced example."
pause
execute $ "_build" </> "run" <.> exe
putStrLn "\n% [3/5] Rebuilding an example project with Shake (nothing should change)."
pause
execute build
putStrLn "\n% [4/5] Cleaning the build."
pause
execute $ build ++ " clean"
putStrLn "\n% [5/5] Rebuilding with 2 threads and profiling."
pause
execute $ build ++ " -j2 --report --report=-"
putStrLn "\n% See the profiling summary above, or look at the HTML profile report in"
putStrLn $ "% " ++ dir </> "report.html"
putStrLn "\n% Demo complete - all the examples can be run from:"
putStrLn $ "% " ++ dir
putStrLn "% For more info see https://shakebuild.com"
when (isJust ninja) $ do
putStrLn "\n% PS. Shake can also execute Ninja build files"
putStrLn "% For more info see https://shakebuild.com/ninja"
yesNo :: Bool -> IO Bool
yesNo :: Bool -> IO Bool
yesNo Bool
auto = do
String -> IO ()
putStr String
"% [Y/N] (then ENTER): "
x <- if Bool
auto then String -> IO String
putLine String
"y" else String -> String
lower (String -> String) -> IO String -> IO String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO String
getLine
if "y" `isPrefixOf` x then
pure True
else if "n" `isPrefixOf` x then
pure False
else
yesNo auto
putLine :: String -> IO String
putLine :: String -> IO String
putLine String
x = String -> IO ()
putStrLn String
x IO () -> IO String -> IO String
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> String -> IO String
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
x
wrap :: IO Bool -> IO Bool
wrap :: IO Bool -> IO Bool
wrap IO Bool
act = IO Bool
act IO Bool -> (SomeException -> IO Bool) -> IO Bool
forall a. IO a -> (SomeException -> IO a) -> IO a
`catch_` IO Bool -> SomeException -> IO Bool
forall a b. a -> b -> a
const (Bool -> IO Bool
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Bool
False)
require :: Bool -> String -> IO ()
require :: Bool -> String -> IO ()
require Bool
b String
msg = Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
b (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ String -> IO ()
putStrLn String
msg IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> IO ()
forall a. IO a
exitFailure