diff --git a/shake-ext.cabal b/shake-ext.cabal
--- a/shake-ext.cabal
+++ b/shake-ext.cabal
@@ -1,5 +1,5 @@
 name:                shake-ext
-version:             2.1.0.2
+version:             2.2.0.0
 synopsis:            Helper functions for linting with shake 
 description:         This package provides several linters out of the box, for use with [shake](http://shakebuild.com/).
 homepage:            https://hub.darcs.net/vmchale/shake-ext
@@ -27,6 +27,8 @@
                      , Development.Shake.Check
                      , Development.Shake.Clean
                      , Development.Shake.Cabal
+                     , Development.Shake.Version
+  other-modules:       Development.Shake.TH
   build-depends:       base >= 4.10 && < 5
                      , shake
                      , language-ats >= 0.1.1.3
@@ -35,6 +37,7 @@
                      , directory
                      , text
                      , Cabal >= 2.0
+                     , template-haskell
   default-language:    Haskell2010
   if flag(development)
     ghc-options:       -Werror
diff --git a/src/Development/Shake/Check.hs b/src/Development/Shake/Check.hs
--- a/src/Development/Shake/Check.hs
+++ b/src/Development/Shake/Check.hs
@@ -1,35 +1,22 @@
+{-# LANGUAGE TemplateHaskell #-}
+
 -- | Functions in this module check for the presence of various build tools.
 module Development.Shake.Check ( checkExecutable
                                -- * Helper functions for specific programs
                                , pandoc
                                , autoconf
                                , patsFilter
-                               , valgrind
-                               , hs2ats
                                , ghc
+                               , compleat
+                               , cabal
+                               -- * Types
+                               , MBool
                                ) where
 
 import           Control.Monad.IO.Class
-import           Data.Maybe             (isJust)
-import           System.Directory       (findExecutable)
-
-checkExecutable :: (MonadIO m) => String -> m Bool
-checkExecutable = fmap isJust . liftIO . findExecutable
-
-pandoc :: (MonadIO m) => m Bool
-pandoc = checkExecutable "pandoc"
+import           Development.Shake.TH
 
-autoconf :: (MonadIO m) => m Bool
-autoconf = checkExecutable "autoconf"
+$(mkExecChecks ["compleat", "pandoc", "autoconf", "cabal", "ghc"])
 
 patsFilter :: (MonadIO m) => m Bool
 patsFilter = checkExecutable "pats-filter"
-
-valgrind :: (MonadIO m) => m Bool
-valgrind = checkExecutable "valgrind"
-
-hs2ats :: (MonadIO m) => m Bool
-hs2ats = checkExecutable "hs2ats"
-
-ghc :: (MonadIO m) => m Bool
-ghc = checkExecutable "ghc"
diff --git a/src/Development/Shake/TH.hs b/src/Development/Shake/TH.hs
new file mode 100644
--- /dev/null
+++ b/src/Development/Shake/TH.hs
@@ -0,0 +1,51 @@
+{-# LANGUAGE RankNTypes      #-}
+{-# LANGUAGE TemplateHaskell #-}
+
+module Development.Shake.TH ( checkExecutable
+                            , mkVersions
+                            , mkExecChecks
+                            , commonVersion
+                            , MBool
+                            , AVersion
+                            ) where
+
+import           Control.Monad.IO.Class
+import           Data.Maybe             (isJust)
+import           Development.Shake
+import           Language.Haskell.TH
+import           System.Directory       (findExecutable)
+
+type MBool = forall m. MonadIO m => m Bool
+
+type AVersion = Action String
+
+commonVersion :: String -> Action String
+commonVersion prog = do
+    (Stdout out) <- command mempty prog ["--version"]
+    pure . last . words . head . lines $ out
+
+mkSigVersion :: String -> Dec
+mkSigVersion s = SigD (mkName $ s ++ "Version") (ConT ''AVersion)
+
+mkVersion :: String -> Dec
+mkVersion s = FunD (mkName $ s ++ "Version") [Clause mempty (NormalB expr) mempty]
+    where expr = VarE 'commonVersion `AppE` (LitE $ StringL s)
+
+mkVersions :: [String] -> Q [Dec]
+mkVersions = pure . (=<<) g
+    where g s = [mkVersion s, mkSigVersion s]
+
+mkSig :: String -> Dec
+mkSig s = SigD (mkName s) (ConT ''MBool)
+
+-- | Check for the presence of some executable.
+checkExecutable :: (MonadIO m) => String -> m Bool
+checkExecutable = fmap isJust . liftIO . findExecutable
+
+mkExecCheck :: String -> Dec
+mkExecCheck s = FunD (mkName s) [Clause mempty (NormalB expr) mempty]
+    where expr = VarE 'checkExecutable `AppE` (LitE $ StringL s)
+
+mkExecChecks :: [String] -> Q [Dec]
+mkExecChecks = pure . (=<<) g
+    where g s = [mkExecCheck s, mkSig s]
diff --git a/src/Development/Shake/Version.hs b/src/Development/Shake/Version.hs
new file mode 100644
--- /dev/null
+++ b/src/Development/Shake/Version.hs
@@ -0,0 +1,11 @@
+{-# LANGUAGE TemplateHaskell #-}
+
+module Development.Shake.Version ( ghcVersion
+                                 , cabalVersion
+                                 , commonVersion
+                                 , pandocVersion
+                                 ) where
+
+import           Development.Shake.TH
+
+$(mkVersions ["pandoc", "ghc", "cabal"])
