shaker-0.5.0: src/Shaker/Reflexivite.hs
module Shaker.Reflexivite (
RunnableFunction(..)
-- * Collect module information functions
,runFunction
,searchInstalledPackageId
-- * Template haskell generator
,listAllTestFrameworkGroupList
,filterModulesWithPattern
,filterFunctionsWithPatterns
,listTestFrameworkGroupList
)
where
import Control.Arrow
import Control.Exception as C
import Control.Monad.Reader
import Data.List
import Data.Maybe
import Distribution.InstalledPackageInfo
import Distribution.Simple.PackageIndex
import DynFlags
import GHC
import GHC.Paths
import Language.Haskell.TH
import Shaker.Action.Compile
import Shaker.GhcInterface
import Shaker.ModuleData
import Shaker.Type
import Unsafe.Coerce
data RunnableFunction = RunnableFunction {
runnableFunctionModule :: [String]
,runnableLibrairies :: [String]
,runnableFunctionFunction :: String -- The function name. Should have IO() as signature
}
deriving Show
-- | Compile, load and run the given function
runFunction :: RunnableFunction -> Shaker IO()
runFunction (RunnableFunction importModuleList listLibs fun) = do
cpIn <- getNonMainCompileInput
listInstalledPkgId <- fmap catMaybes (mapM searchInstalledPackageId listLibs)
dynFun <- lift $ runGhc (Just libdir) $ do
dflags <- getSessionDynFlags
_ <- setSessionDynFlags (addShakerLibraryAsImport listInstalledPkgId (dopt_set dflags Opt_HideAllPackages))
_ <- ghcCompile cpIn
configureContext importModuleList
value <- compileExpr fun
do let value' = unsafeCoerce value :: a
return value'
_ <- lift $ handleActionInterrupt dynFun
return ()
where
configureContext [] = getModuleGraph >>= \mGraph -> setContext [] $ map ms_mod mGraph
configureContext imports = mapM (\a -> findModule (mkModuleName a) Nothing ) imports >>= \m -> setContext [] m
addShakerLibraryAsImport :: [String] -> DynFlags -> DynFlags
addShakerLibraryAsImport listInstalledPkgId dflags = dflags {
packageFlags = nub $ map ExposePackageId listInstalledPkgId ++ oldPackageFlags
}
where oldPackageFlags = packageFlags dflags
searchInstalledPackageId :: String -> Shaker IO (Maybe String)
searchInstalledPackageId pkgName = do
pkgIndex <- asks shakerPackageIndex
let srchRes = searchByName pkgIndex pkgName
return $ processSearchResult srchRes
where processSearchResult None = Nothing
processSearchResult (Unambiguous a) = Just $ installedPackageId >>> installedPackageIdString $ head a
processSearchResult (Ambiguous (a:_)) = Just $ installedPackageId >>> installedPackageIdString $ head a
processSearchResult _ = Nothing
handleActionInterrupt :: IO() -> IO()
handleActionInterrupt = C.handle catchAll
where catchAll :: C.SomeException -> IO ()
catchAll e = putStrLn ("Shaker caught " ++ show e ) >> return ()
-- | List all test group of the project.
-- see "Shaker.TestTH"
listAllTestFrameworkGroupList :: ShakerInput -> ExpQ
listAllTestFrameworkGroupList = shakerModuleData >>> removeNonTestModules >>> listTestFrameworkGroupList
-- | List all test group for test-framework from the list of modules
listTestFrameworkGroupList :: [ModuleData] -> ExpQ
listTestFrameworkGroupList = return . ListE . map getSingleTestFrameworkGroup
-- * Test framework integration
-- | Generate a test group for a given module
getSingleTestFrameworkGroup :: ModuleData -> Exp
getSingleTestFrameworkGroup moduleData = foldl1 AppE [process_to_group_exp, test_case_tuple_list, list_assertion, list_prop]
where process_to_group_exp = AppE (VarE .mkName $ "processToTestGroup") (LitE (StringL $ moduleDataName moduleData))
list_prop = ListE $ map getSingleFrameworkQuickCheck $ moduleDataProperties moduleData
list_assertion = ListE $ map getSingleFrameworkHunit $ moduleDataAssertions moduleData
test_case_tuple_list = convertHunitTestCaseToTuples (moduleDataTestCase moduleData)
convertHunitTestCaseToTuples :: [String] -> Exp
convertHunitTestCaseToTuples = ListE . map convertToTuple
where convertToTuple name = TupE [LitE (StringL name), VarE $ mkName name ]
-- | Generate an expression for a single hunit test
getSingleFrameworkHunit :: String -> Exp
getSingleFrameworkHunit hunitName = AppE testcase_with_name_exp assertion_exp
where testcase_with_name_exp = AppE ( VarE $ mkName "testCase") (LitE $ StringL hunitName)
assertion_exp = VarE . mkName $ hunitName
-- | Generate an expression for a single quickcheck property
getSingleFrameworkQuickCheck :: String -> Exp
getSingleFrameworkQuickCheck propName = AppE testproperty_with_name_exp property_exp
where canonical_name = tail . dropWhile (/= '_') $ propName
testproperty_with_name_exp = AppE ( VarE $ mkName "testProperty") (LitE $ StringL canonical_name)
property_exp = VarE . mkName $ propName