diff --git a/ise.template/tcl.mustache b/ise.template/tcl.mustache
--- a/ise.template/tcl.mustache
+++ b/ise.template/tcl.mustache
@@ -161,6 +161,7 @@
 
 proc add_source_files {} {
 
+   global myProject
    global myScript
 
    if { ! [ open_project ] } {
@@ -187,7 +188,7 @@
    {{/ipcores}}
 
    # Set the Top Module as well...
-   project set top "rtl" "PET"
+   project set top "rtl" $myProject
 
    puts "$myScript: project sources reloaded."
 
diff --git a/kansas-lava-shake.cabal b/kansas-lava-shake.cabal
--- a/kansas-lava-shake.cabal
+++ b/kansas-lava-shake.cabal
@@ -1,5 +1,5 @@
 name:                kansas-lava-shake
-version:             0.2.0
+version:             0.3.0
 synopsis:            Shake rules for building Kansas Lava projects
 description:         Shake rules for building Kansas Lava projects. Currently supports the
                      Xilinx FPGA tooling only.
@@ -25,9 +25,11 @@
   build-depends:
         base >=4.7 && < 5,
         kansas-lava >=0.2.4 && < 0.2.5,
-        shake >= 0.14,
+        shake >= 0.16,
         text,
-        hastache >= 0.6 && < 0.7
+        mustache,
+        vector,
+        containers
   default-language:    Haskell2010
   other-extensions:    RecordWildCards
   Ghc-Options:         -fwarn-unused-imports -fwarn-unused-matches
@@ -40,4 +42,4 @@
 source-repository this
   type:     git
   location: git://github.com/gergoerdi/kansas-lava-shake
-  tag:      0.2.0
+  tag:      0.3.0
diff --git a/src/Development/KansasLava/Shake/Xilinx.hs b/src/Development/KansasLava/Shake/Xilinx.hs
--- a/src/Development/KansasLava/Shake/Xilinx.hs
+++ b/src/Development/KansasLava/Shake/Xilinx.hs
@@ -1,26 +1,41 @@
-{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards   #-}
 module Development.KansasLava.Shake.Xilinx
-       ( XilinxConfig(..), XilinxTarget(..)
+       ( XilinxTarget(..)
+       , papilioOne, papilioPro
        , xilinxRules
        ) where
 
-import Development.Shake
-import Development.Shake.FilePath
+import           Development.Shake          hiding ((~>))
+import           Development.Shake.FilePath
+import           Development.Shake.Config
 
-import qualified Data.Text.Lazy as TL
-import Text.Hastache
-import Text.Hastache.Context
+import qualified Data.Text                  as T
+import           Text.Mustache
+import           Text.Mustache.Types
+import qualified Data.Map as M
+import           Data.Maybe (fromMaybe)
 
-import Paths_kansas_lava_shake
+import           Paths_kansas_lava_shake
 
-data XilinxTarget = XilinxTarget{ targetFamily, targetDevice, targetSpeed, targetPackage :: String }
+data XilinxTarget = XilinxTarget{ targetFamily, targetDevice, targetPackage, targetSpeed :: String }
 
-data XilinxConfig = XilinxConfig{ xilinxRoot :: FilePath
-                                , xilinxTarget :: XilinxTarget
-                                }
+papilioPro :: XilinxTarget
+papilioPro = XilinxTarget "Spartan6" "xc6slx9" "tqg144" "-2"
 
-xilinxRules :: XilinxConfig -> FilePath -> String -> [FilePath] -> [FilePath] -> Rules ()
-xilinxRules XilinxConfig{..} outDir projName srcs ipcores = do
+papilioOne :: XilinxTarget
+papilioOne = XilinxTarget "Spartan3E" "xc3s500e" "vq100" "-5"
+
+boards :: M.Map String XilinxTarget
+boards = M.fromList
+    [ ("papilio-pro", papilioPro)
+    , ("papilio-one", papilioOne)
+    ]
+
+xilinxRules :: Maybe XilinxTarget -> FilePath -> String -> [FilePath] -> [FilePath] -> Rules ()
+xilinxRules mtarget outDir projName srcs ipcores = do
+    usingConfigFile "build.mk"
+
     outDir </> projName <.> "bit" %> \_out -> do
         need . concat $ [ [ outDir </> src | src <- srcs ]
                         , [ outDir </> "ipcore_dir" </> xco | xco <- ipcores ]
@@ -29,44 +44,46 @@
 
         xilinx "xtclsh" [projName <.> "tcl", "rebuild_project"]
 
-    "build" </> "*.tcl" %> do
-        hastache projCtxt
+    outDir </> "*.tcl" %> \out -> do
+        target <- case mtarget of
+            Just target -> return target
+            Nothing -> do
+                board <- fromMaybe (error "Please set BOARD in build.mk") <$> getConfig "BOARD"
+                return $ fromMaybe (error $ unwords ["Unknown board:", board]) $ M.lookup board boards
+        mustache (projCtxt target) out
   where
-    xilinx tool args = cmd (Cwd outDir) (xilinxRoot </> tool) args
+    xilinx tool args = do
+        root <- getConfig "XILINX_ROOT"
+        wrap <- getConfig "XILINX_WRAPPER"
+        let exe = case (wrap, root) of
+                (Just wrap, _) -> [wrap, tool]
+                (Nothing, Just root) -> [root </> tool]
+                (Nothing, Nothing) -> error "XILINX_ROOT or XILINX_WRAPPER must be set"
+        cmd (Cwd outDir) exe args
 
-    projCtxt = mkStrContext $ \key -> case key of
-        "project" -> MuVariable projName
-        "targetFamily" -> MuVariable targetFamily
-        "targetDevice" -> MuVariable targetDevice
-        "targetSpeed" -> MuVariable targetSpeed
-        "targetPackage" -> MuVariable targetPackage
-        "ipcores" -> MuList [ mkStrContext $ \key -> case key of
-                                   "name" -> MuVariable name
-                                   _ -> MuNothing
-                            | xco <- ipcores
-                            , let name = dropExtension xco
-                            ]
-        "srcs" -> MuList [ mkStrContext $ \key -> case key of
-                                "fileName" -> MuVariable src
-                                _ -> MuNothing
-                         | src <- srcs
-                         ]
-        _ -> MuNothing
-      where
-        XilinxTarget{..} = xilinxTarget
+    projCtxt XilinxTarget{..} = object $ [
+        "project" ~=  projName,
+        "targetFamily" ~= targetFamily,
+        "targetDevice" ~= targetDevice,
+        "targetSpeed" ~= targetSpeed,
+        "targetPackage" ~= targetPackage,
+        "ipcores" ~> (map (\n -> object ["name" ~> n]) $ map dropExtension ipcores),
+        "srcs" ~> (map (\s -> object ["fileName" ~> s]) srcs)
+        ]
 
-hastache :: MuContext IO -> FilePath -> Action ()
-hastache ctxt target = do
+mustache :: Value -> FilePath -> Action ()
+mustache ctxt target = do
     alwaysRerun
-    templateFile <- liftIO $ getDataFileName ("ise.template" </> templateName)
-    t <- liftIO $ hastacheFile hastacheConfig templateFile ctxt
-    writeFileChanged target $ TL.unpack t
+    rSrc <- liftIO $ getDataFileName ("ise.template" </> templateName)
+    withTempDir $ \tempDir -> do
+      copyFileChanged rSrc (tempDir </> templateName)
+      tE <- liftIO $ automaticCompile [tempDir] templateName
+      case tE of
+        Left err ->
+          liftIO $ error $ "KansasLava.Shake.Xilinx - mustache template problem: " ++ show err
+        Right template -> do
+          let st = substitute template ctxt
+          writeFileChanged target $ T.unpack st
   where
-    hastacheConfig = MuConfig{ muEscapeFunc = emptyEscape
-                             , muTemplateFileDir = Nothing
-                             , muTemplateFileExt = Just "mustache"
-                             , muTemplateRead = const $ return Nothing
-                             }
-
     ext = drop 1 . takeExtension $ target
     templateName = ext <.> "mustache"
