packages feed

shaker 0.1 → 0.2

raw patch · 14 files changed

+160/−236 lines, 14 files

Files

+ README view
@@ -0,0 +1,7 @@+Shaker is a build tool which allow to compile and launch test on haskell project with some features like continuous action similar to SBT.++The package is uploaded in hackage - http://hackage.haskell.org/package/shaker++To install shaker, either use cabal install - cabal update && cabal install shaker +Or build shaker from sources - runhaskell Setup.hs configure && runhaskell Setup.hs install+
− README.textile
@@ -1,78 +0,0 @@-Shaker is a build tool which allow to compile and launch test on haskell project with some features like continuous action similar to SBT.-For now, all configuration are made via cabal. Shaker will read cabal configuration to discover source directories, compilation options and targets to compile.--h2. Installation --You can either install shaker using cabal-install or by building it yourself.--1. Using cabal-install--<pre>-cabal install shaker -</pre>--2. Building from sources--<pre>-runhaskell Setup.hs configure && runhaskell Setup.hs build && runhaskell Setup.hs install-</pre>--h2. Usage--The cabal configuration file should be generated beforehand. -<pre>-runhaskell Setup.hs configure-</pre>-If you change your cabal configuration, you need to recreate the configuration file. --h3. Execution-In the root of your project, launch-<pre>-shaker-</pre>-An interactive prompt will allow you to execute differents action.--h3. Action execution--h4. Simple Execution-An action can be launched normally, by entering the action name -<pre>-Compile-</pre>-It will compile the projet and give the control back.--h4. Multiple action execution-You can specify multiple action to execute.-<pre>-Clean Compile-</pre>-It will clean all .o and .hi files before Compiling the project.--h4. Continuous action-A continuous action will execute the action when a changement on files has been detected.-Every action are elligible to continuous action, you simply need to prefix them with '~'--<pre>-~Compile-</pre>-It will compile the project every time a change in the code has been detected.-To get control back, simply enter a character.--h3. Actions available--h4. Compile--Compile the project. Targets of the compilation are main file (in case of executable) or exposed modules (in case of library).--h4. FullCompile--Compile all hs files found in source directory. It is usefull to compile test sources.--h4. Clean--Clean the directory containing .o and .hi files.--h4. Quit --Exit the application.-
TODO view
@@ -10,7 +10,6 @@   Correct import module (only import necessary functions)   Create action command   Use a monad writer for cabal information extraction-  Extract extra-source-dirs  Done :    bug : ~Compile launche two fork the first time@@ -28,4 +27,5 @@     Get config through cabal api   Exclude main methods from fullCompile    If no source dir is specified, an error is throwed+  correct fullcompile. Concat source dir from all artifacts (currently only use the first) 
shaker.cabal view
@@ -1,5 +1,5 @@ name: shaker-version: 0.1+version: 0.2 cabal-version: >= 1.8 build-type: Custom license: BSD3@@ -54,7 +54,7 @@   [@% ~Compile@] Switch to continuous mode and will compile the project when sources are modified.   .   [@% ~Clean Compile@] Switch to continuous mode and will clean and compile the project when sources are modified.-extra-source-files: README.textile TODO testsuite/tests/resources/invalidMain/dist/setup-config+extra-source-files: README TODO testsuite/tests/resources/invalidMain/dist/setup-config     testsuite/tests/resources/invalidMain/src/Main.hs     testsuite/tests/resources/invalidMain/tests/Dummy.hs     testsuite/tests/resources/invalidMain/invalidMain.cabal@@ -73,11 +73,11 @@ source-repository this   type:     git   location: git://github.com/bonnefoa/Shaker.git -  tag:      0.1+  tag:      0.2  Executable shaker   Main-Is: Shaker.hs-  ghc-options: -Wall+  ghc-options: -Wall -fno-warn-orphans   hs-source-dirs: src    other-modules:      Shaker.Parser@@ -116,6 +116,7 @@ Executable test   hs-source-dirs:  src, testsuite/tests    main-is:         RunTest.hs+  ghc-options: -Wall -fno-warn-orphans   other-modules:      Shaker.RegexTest     Shaker.Properties
src/Shaker/Action/Compile.hs view
@@ -1,62 +1,35 @@ module Shaker.Action.Compile(     runCompile     ,runFullCompile+    ,ghcCompile   )  where -import Data.List +import Shaker.SourceHelper import GHC import DynFlags  import GHC.Paths-import Shaker.Io import Shaker.Type-import Control.Monad.Trans  import Control.Monad.Reader --- |Run haskell compilation on given file input +-- | Run haskell compilation on given CompileInput list runCompile :: Plugin-runCompile = asks compileInputs >>=  mapM runSingleCompileInput >> return ()--runSingleCompileInput :: CompileInput -> Shaker IO()-runSingleCompileInput (CompileInput sourceDir desc targetInput procFlags strflags inputTargetFiles) = do-        lift $ putStrLn "-------------------------------------"-        lift $ putStrLn $ concat ["   --------- ", desc," ---------"]-        targetFiles <- checkTargetFiles inputTargetFiles -        lift $ putStrLn $ concat ["   --------- ", "Compiling target : "++ show targetFiles," ---------"]-        lift $ defaultErrorHandler defaultDynFlags $ -                       runGhc (Just libdir) $ do-                       dflags <- getSessionDynFlags-                       (newFlags,_,_) <- parseDynamicFlags dflags (map noLoc strflags)-	               _ <- setSessionDynFlags $ procFlags $ setSourceAndTarget sourceDir targetInput newFlags-                       target <- mapM (`guessTarget` Nothing) targetFiles-                       setTargets target-        	       _ <- load LoadAllTargets-                       return ()+runCompile = asks compileInputs >>= mapM (lift . runSingleCompileInput )  >> return ()  +-- | Run haskell compilation on all haskell files runFullCompile :: Plugin-runFullCompile = setCompileInputForAllHsSources >>= \a -> -  runSingleCompileInput a >> -  return () ---- | Fill the target files to all files in listenerInput if empty-checkTargetFiles :: [String] -> Shaker IO([String])-checkTargetFiles [] = do -        (ListenerInput fli _) <- asks listenerInput -        files <- lift $ recurseMultipleListFiles fli-        lift $ filterM (\a -> not `liftM` isFileContainingMain a) files-checkTargetFiles l = return l--setSourceAndTarget :: [String] -> String ->DynFlags -> DynFlags-setSourceAndTarget sources target dflags = dflags{-    importPaths = sources-    ,objectDir = Just target-    ,hiDir = Just target-  }+runFullCompile = do+  cpList <- asks compileInputs +  let cpIn = mergeCompileInputsSources cpList+  cfFlList <- lift $ constructCompileFileList cpIn+  let newInp = runReader (setAllHsFilesAsTargets cpIn >>= removeFileWithMain)  cfFlList+  lift $ runSingleCompileInput newInp -setCompileInputForAllHsSources :: Shaker IO CompileInput-setCompileInputForAllHsSources = do -  (cpIn:_) <- asks compileInputs-  filePaths <- lift $ recurseMultipleListFiles $ map (\a -> FileListenInfo a defaultExclude defaultHaskellPatterns ) (cfSourceDirs cpIn)-  toExcludeFiles <- lift $ filterM isFileContainingMain filePaths-  return  $ cpIn {cfTargetFiles = filePaths \\ toExcludeFiles , cfDescription ="Full compilation"  }+runSingleCompileInput :: CompileInput -> IO()+runSingleCompileInput cplInp = do+        putStrLn $ concat ["   --------- ", cfDescription cplInp," ---------"]+        putStrLn $ concat ["   --------- ", "Compiling target : "++ show (cfTargetFiles cplInp) ," ---------"]+        _ <- defaultErrorHandler defaultDynFlags $ +                    runGhc (Just libdir) $ ghcCompile cplInp +        return () 
src/Shaker/Action/Quickcheck.hs view
@@ -1,6 +1,13 @@ module Shaker.Action.Quickcheck  where +{-+import OccName (occNameString)+import Name (nameOccName)+import Var (varName)+import Data.List+import Data.Maybe+import           Data.Dynamic                           (fromDynamic) import GHC import DynFlags  import GHC.Paths@@ -8,10 +15,12 @@ import Shaker.Type import Control.Monad.Trans  import Control.Monad.Reader-{--runQuickcheck :: Shaker IO String+import Unsafe.Coerce+++runQuickcheck :: Plugin runQuickcheck = do-        (CompileInput sourceDir targetInput procFlags strflags) <-  asks compileInput +        (CompileInput sourceDir targetInput procFlags strflags) <-  asks compileInputs         (ListenerInput fli _) <- asks listenerInput          targetFiles <-  lift $ recurseMultipleListFiles fli         lift $ defaultErrorHandler defaultDynFlags $ @@ -22,39 +31,62 @@                        target <- mapM (`guessTarget` Nothing) targetFiles                        setTargets target         	       _ <- load LoadAllTargets-                       modSum <- getModSummary $ mkModuleName "B"-                       p <- parseModule modSum-                       t <- typecheckModule p-                       d <- desugarModule t-                       l <- loadModule d-                       n <- getNamesInScope-                       c <- return $ coreModule d-                       g <- getModuleGraph-                       mapM showModule g     -                       print $ showSDoc ( ppr (parsedSource d,"/n-----/n",  typecheckedSource d)        res )--}+                       return()+runTest :: IO()+runTest = do +  _ <- getTestFunc+  return() ++getTestFunc :: IO()+getTestFunc = +            defaultErrorHandler defaultDynFlags $ do+            runGhc (Just libdir) $ do+            dflags <- getSessionDynFlags+            (newFlags,_,_) <- parseDynamicFlags dflags (map noLoc ["-itestsuite/tests","-isrc/","-package ghc"])+            _ <- setSessionDynFlags newFlags+            target <- guessTarget "Shaker.RunTest" Nothing+            setTargets [target]+            _ <- load LoadAllTargets+            m <- findModule (mkModuleName "Shaker.RunTest") Nothing+            setContext [] [m]+            dynCompileExpr("runAll")++            --compileExpr ("runAll")+            --(unsafeCoerce value) ++example :: IO[String] example = defaultErrorHandler defaultDynFlags $ do     runGhc (Just libdir) $ do     dflags <- getSessionDynFlags     (newFlags,_,_) <- parseDynamicFlags dflags (map noLoc ["-itestsuite/tests","-isrc/","-package ghc"])-    setSessionDynFlags newFlags+    _ <- setSessionDynFlags newFlags     target <- guessTarget "Shaker.CliTest" Nothing     setTargets [target]-    load LoadAllTargets-    modSum <- getModSummary $ mkModuleName "Shaker.CliTest"-    p <- parseModule modSum- --   liftIO $ putStrLn $ show p -    t <- typecheckModule p---    liftIO $ putStrLn $ show t -    d <- desugarModule t-    l <- loadModule d-    n <- getNamesInScope-    c <- return $ coreModule d-    g <- getModuleGraph-    mapM showModule g     -    return $ (parsedSource d,"/n-----/n",  typecheckedSource d)+    _ <- load LoadAllTargets+    modulesList <- getModuleGraph+    tyThingsList <- getTyThingsFromModuleSummary modulesList+--    return $ showPpr gra+    return $ getQuickcheckFunction tyThingsList+ +getTyThingsFromModuleSummary :: (GhcMonad  m) => [ModSummary] -> m [TyThing]+getTyThingsFromModuleSummary modSummaries = do+        modulesInfo <- mapM getModuleInfo modules+        return $ concat $ map modInfoTyThings $ catMaybes modulesInfo+   where modules = map ms_mod modSummaries+          +getQuickcheckFunction :: [TyThing] -> [String]+getQuickcheckFunction tyMap = filter ("prop_" `isPrefixOf`) nameList+   where idList = catMaybes $ map tyThingToId tyMap+         varList = map varName idList+         occList = map nameOccName varList+         nameList = map occNameString occList++tyThingToId :: TyThing -> Maybe Id+tyThingToId (AnId tyId) = Just tyId+tyThingToId _ = Nothing+ setSourceAndTarget :: [String] -> String ->DynFlags -> DynFlags setSourceAndTarget sources target dflags = dflags{     importPaths = sources@@ -62,3 +94,5 @@     ,hiDir = Just target   } ++-} 
src/Shaker/Action/Standard.hs view
@@ -13,7 +13,7 @@   putStrLn "Following actions are available : "   print $ M.keys commands   putStrLn "use ~[actionName] for continuous launch"-+  runExit :: Plugin runExit = lift $ putStrLn "Exiting" 
src/Shaker/Cabal/CabalInfo.hs view
@@ -1,9 +1,8 @@ -- | Allow to use cabal configuration (generated via the configure action of cabal). -- Source directories and compilation options will be reused by Shaker. module Shaker.Cabal.CabalInfo- where+   where -import Shaker.Io(FileListenInfo(..),defaultHaskellPatterns,defaultExclude) import Shaker.Type import Shaker.Config import Distribution.Simple.Configure (getPersistBuildConfig)@@ -136,6 +135,4 @@     return cplInput {cfTargetFiles = newTargets}  | otherwise = return cplInput   where oldTargets = cfTargetFiles cplInput-- 
src/Shaker/Config.hs view
@@ -4,12 +4,9 @@  import Shaker.Type import Shaker.PluginConfig-import Shaker.Io(FileListenInfo(..),defaultHaskellPatterns) import Shaker.Cli(InputState(..))-import DynFlags import Control.Concurrent - defaultInput ::ShakerInput   defaultInput = ShakerInput {   compileInputs = [defaultCompileInput],@@ -17,37 +14,6 @@   pluginMap = defaultPluginMap,   commandMap = defaultCommandMap   }---- | Default compilation argument.--- Wall is activated by default-defaultCompileInput :: CompileInput-defaultCompileInput = CompileInput {-  cfSourceDirs= ["src/","testsuite/tests/"]-  ,cfDescription = "Default Compilation"-  ,cfCompileTarget =  "target"  -  ,cfDynFlags = defaultCompileFlags  -  ,cfCommandLineFlags = ["-Wall"]-  ,cfTargetFiles = []-}---- | default dynamics flags--- the sources are expected to be in src as described in <http://www.haskell.org/haskellwiki/structure_of_a_haskell_project>--- the result of compilation (.o and .hi) are placed in the target/ directory--- there is no main linkage by default to allow faster compilation feedback-defaultCompileFlags :: (DynFlags -> DynFlags)-defaultCompileFlags = \a-> a  {-    verbosity = 1-    ,ghcLink = NoLink-} ---- | The default Listener configuration--- Listened sources are all haskell sources in src/ and testsuite/--- The default delay is 2 sec-defaultListenerInput :: ListenerInput                                   -defaultListenerInput = ListenerInput {-    fileListenInfo= [FileListenInfo "src/" [] defaultHaskellPatterns, FileListenInfo "testsuite/" [] defaultHaskellPatterns ]-    ,delay = 2000000-    }  defaultInputState :: IO InputState defaultInputState = do
src/Shaker/Io.hs view
@@ -8,29 +8,18 @@   ,FileListenInfo(..)   ,isFileContaining   ,isFileContainingMain+  ,isFileContainingTH   ,defaultHaskellPatterns   ,defaultExclude )  where  -import qualified Data.ByteString.Lazy.Char8 as L +import qualified Data.ByteString.Char8 as L  import Control.Monad import System.Directory import Data.List import Shaker.Regex-import System.Time---- | Represents directory to listen -data FileListenInfo = FileListenInfo{-  dir :: FilePath     -- ^ location of the listened directory-  ,ignore :: [String] -- ^ ignore patterns-  ,include :: [String] -- ^include patterns-  }-  deriving (Show,Eq)---- |Agregate a FilePath with its modification time-data FileInfo = FileInfo FilePath ClockTime -  deriving (Show,Eq)+import Shaker.Type  -- |Get the tuples of (newFiles,modifiedFiles) from given list of directory listModifiedAndCreatedFiles :: [FileListenInfo] -> [FileInfo] -> IO ([FileInfo],[FileInfo])@@ -64,6 +53,7 @@ recurseMultipleListFiles flis = liftM concat $ mapM recurseListFiles flis  -- | Recursively list all files+-- All non matching files are excluded recurseListFiles :: FileListenInfo -> IO [FilePath] recurseListFiles fli@(FileListenInfo inputDir _ _) = do   curDir <- canonicalizePath inputDir@@ -73,6 +63,9 @@   curListFiles <-  listFiles fli   return $ curListFiles ++ concat sub +isFileContainingTH :: FilePath -> IO Bool+isFileContainingTH fp = isFileContaining fp (L.pack "$(" `L.isInfixOf`)+ isFileContainingMain :: FilePath -> IO Bool isFileContainingMain fp = isFileContaining fp (L.pack "main" `L.isPrefixOf`) @@ -81,16 +74,9 @@    byStr <- L.readFile fp    return $ any pat $ L.lines byStr - convertToFullPath :: FilePath -> [FilePath] -> [FilePath] convertToFullPath absDir = map (\a-> concat [absDir, "/",a])   removeDotDirectory :: [String] -> [String] removeDotDirectory = filter (not . isSuffixOf "."  ) --defaultHaskellPatterns :: [String]-defaultHaskellPatterns = [".*\\.hs$"]--defaultExclude :: [String]-defaultExclude =  [".*Setup\\.hs$"] 
src/Shaker/Type.hs view
@@ -1,12 +1,13 @@ module Shaker.Type  where -import DynFlags(DynFlags)-import Shaker.Io(FileListenInfo)+import DynFlags import qualified Data.Map as M import Control.Monad.Reader+import System.Time(ClockTime)  type Shaker  = ReaderT ShakerInput +type CompileM = Reader CompileInput  -- | Duration define the life span of an action data Duration = @@ -55,12 +56,62 @@   fileListenInfo :: [FileListenInfo] -- ^ The files to listen   ,delay :: Int  -- ^ Delay beetween 2 check in microsecond }++-- | Represents directory to listen +data FileListenInfo = FileListenInfo{+  dir :: FilePath     -- ^ location of the listened directory+  ,ignore :: [String] -- ^ ignore patterns+  ,include :: [String] -- ^include patterns+  }+  deriving (Show,Eq)++-- |Agregate a FilePath with its modification time+data FileInfo = FileInfo FilePath ClockTime +  deriving (Show,Eq)    -- | Represents the mapping beetween an action and the function to execute type PluginMap = M.Map Action Plugin -- | Represents the mapping between the command-line input and the action type CommandMap = M.Map String Action  -- | Represents an action of shaker-type Plugin = Shaker  IO()+type Plugin = Shaker IO()  +-- * Default data++-- | Default compilation argument.+-- Wall is activated by default+defaultCompileInput :: CompileInput+defaultCompileInput = CompileInput {+  cfSourceDirs= ["src/","testsuite/tests/"]+  ,cfDescription = "Default Compilation"+  ,cfCompileTarget =  "target"  +  ,cfDynFlags = defaultCompileFlags  +  ,cfCommandLineFlags = ["-Wall"]+  ,cfTargetFiles = []+}++-- | default dynamics flags+-- the sources are expected to be in src as described in <http://www.haskell.org/haskellwiki/structure_of_a_haskell_project>+-- the result of compilation (.o and .hi) are placed in the target/ directory+-- there is no main linkage by default to allow faster compilation feedback+defaultCompileFlags :: (DynFlags -> DynFlags)+defaultCompileFlags = \a-> a  {+    verbosity = 1+    ,ghcLink = NoLink+} ++-- | The default Listener configuration+-- Listened sources are all haskell sources in src/ and testsuite/+-- The default delay is 2 sec+defaultListenerInput :: ListenerInput                                   +defaultListenerInput = ListenerInput {+    fileListenInfo= [FileListenInfo "src/" [] defaultHaskellPatterns, FileListenInfo "testsuite/" [] defaultHaskellPatterns ]+    ,delay = 2000000+    }++defaultHaskellPatterns :: [String]+defaultHaskellPatterns = [".*\\.hs$"]++defaultExclude :: [String]+defaultExclude =  [".*Setup\\.hs$"]
testsuite/tests/Shaker/Cabal/CabalInfoTest.hs view
@@ -6,16 +6,11 @@ import Shaker.Action.Compile import Shaker.Action.Clean import Shaker.Type-import Shaker.Io+import Shaker.CommonTest import Test.HUnit-import Control.Exception import Shaker.Cabal.CabalInfo-import GHC(DynFlags(DynFlags))-import DynFlags(-    DynFlags, verbosity, ghcLink, packageFlags, outputFile, hiDir, objectDir ,importPaths-    ,PackageFlag (ExposePackage)-    ,GhcLink (NoLink)-  )+import DynFlags( DynFlags, packageFlags, importPaths ,PackageFlag (ExposePackage) , defaultDynFlags+        )  testParseCabalConfig :: Test testParseCabalConfig = TestCase $ runTestOnDirectory "testsuite/tests/resources/cabalTest" $ do  @@ -26,7 +21,7 @@   cfCommandLineFlags cplLib == ["-Wall"] @? "command line flags should be -Wall, got " ++ show ( cfCommandLineFlags cplLib)   cfTargetFiles cplLib == ["CabalTest"]  @? "targetFiles should be CabalTest, got "++ show ( cfTargetFiles cplLib)   cfTargetFiles cplExe == ["src/Main.hs"]  @? "targetFiles should be src/Main.hs, got "++ show ( cfTargetFiles cplExe)-  let dFlags = cfDynFlags cplExe DynFlags{}+  let dFlags = cfDynFlags cplExe defaultDynFlags   importPaths dFlags == ["src"] @? "importPaths should be src, got "++ show (importPaths dFlags)   packageFlags dFlags == [ExposePackage "ghc"] @? "Expected : ExposePackage ghc. No show instance so figure it yourself... (/me being lazy)"    let (ListenerInput (flLib:[]) _) = listenerInput shIn@@ -56,10 +51,4 @@     length cplInps == 1 @? "Should have one compile input, got "++ show (length cplInps)     let targs = cfTargetFiles cplInp     targs == ["./noHsSource.hs"] @? "Expected [\"./noHsSource.hs\"] got " ++ show cplInp--runTestOnDirectory :: FilePath -> Assertion -> Assertion-runTestOnDirectory fp fun = do-  oldDir <- getCurrentDirectory -  setCurrentDirectory fp-  finally fun (setCurrentDirectory oldDir) 
testsuite/tests/Shaker/ConductorTest.hs view
@@ -1,5 +1,4 @@ module Shaker.ConductorTest  where -import Shaker.Conductor 
testsuite/tests/Shaker/Properties.hs view
@@ -4,7 +4,6 @@ import Control.Monad import Test.QuickCheck  import System.Time-import Shaker.Type import Shaker.Io  instance Arbitrary TimeDiff where