diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2012 Gergo Erdi
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. The names of the authors may not be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,1 @@
+Shake rules for building Kansas Lava projects
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/ise.template/ut.mustache b/ise.template/ut.mustache
new file mode 100644
--- /dev/null
+++ b/ise.template/ut.mustache
@@ -0,0 +1,21 @@
+-w
+-g DebugBitstream:No
+-g Binary:no
+-g CRC:Enable
+-g ConfigRate:1
+-g ProgPin:PullUp
+-g DonePin:PullUp
+-g TckPin:PullUp
+-g TdiPin:PullUp
+-g TdoPin:PullUp
+-g TmsPin:PullUp
+-g UnusedPin:PullDown
+-g UserID:0xFFFFFFFF
+-g StartUpClk:CClk
+-g DONE_cycle:4
+-g GTS_cycle:5
+-g GWE_cycle:6
+-g LCK_cycle:NoWait
+-g Security:None
+-g DonePipe:Yes
+-g DriveDone:No
diff --git a/ise.template/xise.mustache b/ise.template/xise.mustache
new file mode 100644
--- /dev/null
+++ b/ise.template/xise.mustache
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
+<project xmlns="http://www.xilinx.com/XMLSchema" xmlns:xil_pn="http://www.xilinx.com/XMLSchema">
+
+  <header/>
+
+  <version xil_pn:ise_version="14.2" xil_pn:schema_version="2"/>
+
+  <files>
+{{#components}}
+    <file xil_pn:name="{{fileName}}" xil_pn:type="{{type}}">
+      <association xil_pn:name="Implementation"/>
+      {{#behavior}}<association xil_pn:name="BehavioralSimulation"/>{{/behavior}}
+    </file>
+{{/components}}
+  </files>
+
+  <properties>
+    <property xil_pn:name="Device" xil_pn:value="xc3s500e" xil_pn:valueState="non-default"/>
+    <property xil_pn:name="Device Family" xil_pn:value="Spartan3E" xil_pn:valueState="non-default"/>
+  </properties>
+
+  <bindings/>
+
+  <libraries/>
+
+  <autoManagedFiles/>
+
+</project>
diff --git a/ise.template/xst.mustache b/ise.template/xst.mustache
new file mode 100644
--- /dev/null
+++ b/ise.template/xst.mustache
@@ -0,0 +1,56 @@
+set -tmpdir "xst/projnav.tmp"
+set -xsthdpdir "xst"
+run
+-ifn {{MAIN}}.prj
+-ifmt mixed
+-ofn {{MAIN}}
+-ofmt NGC
+-p xc3s500e-5-vq100
+-top {{TOP}}
+-opt_mode Speed
+-opt_level 1
+-iuc NO
+-keep_hierarchy No
+-netlist_hierarchy As_Optimized
+-rtlview Yes
+-glob_opt AllClockNets
+-read_cores YES
+-write_timing_constraints NO
+-cross_clock_analysis NO
+-hierarchy_separator /
+-bus_delimiter <>
+-case Maintain
+-slice_utilization_ratio 100
+-bram_utilization_ratio 100
+-verilog2001 YES
+-fsm_extract YES -fsm_encoding Auto
+-safe_implementation No
+-fsm_style LUT
+-ram_extract Yes
+-ram_style Auto
+-rom_extract Yes
+-mux_style Auto
+-decoder_extract YES
+-priority_extract Yes
+-shreg_extract YES
+-shift_extract YES
+-xor_collapse YES
+-rom_style Auto
+-auto_bram_packing NO
+-mux_extract Yes
+-resource_sharing YES
+-async_to_sync NO
+-mult_style Auto
+-iobuf YES
+-max_fanout 100000
+-bufg 24
+-register_duplication YES
+-register_balancing No
+-slice_packing YES
+-optimize_primitives NO
+-use_clock_enable Yes
+-use_sync_set Yes
+-use_sync_reset Yes
+-iob Auto
+-equivalent_register_removal YES
+-slice_utilization_ratio_maxmargin 5
diff --git a/kansas-lava-shake.cabal b/kansas-lava-shake.cabal
new file mode 100644
--- /dev/null
+++ b/kansas-lava-shake.cabal
@@ -0,0 +1,38 @@
+name:                kansas-lava-shake
+version:             0.1.0
+synopsis:            Shake rules for building Kansas Lava projects
+-- description:
+license:             BSD3
+license-file:        LICENSE
+copyright:           (C) 2014 Gergo Erdi
+author:              Gergo Erdi
+maintainer:          Gergo Erdi <gergo@erdi.hu>
+category:            Development
+build-type:          Simple
+cabal-version:       >=1.10
+extra-source-files:  README.md
+data-files:
+        ise.template/*.mustache
+
+Library
+  hs-source-dirs:      src
+  Exposed-modules:
+        Development.KansasLava.Shake.Xilinx
+  Other-modules:
+        Paths_kansas_lava_shake
+  build-depends:
+        base >=4.7 && < 5,
+        filepath,
+        directory,
+        shake,
+        containers,
+        text,
+        hastache
+  default-language:    Haskell2010
+  other-extensions:    RecordWildCards
+  Ghc-Options:         -Werror -fwarn-unused-imports -fwarn-unused-matches
+                       -fwarn-unused-binds -fwarn-missing-signatures
+
+source-repository head
+  type:     git
+  location: git://github.com/gergoerdi/kansas-lava-shake
diff --git a/src/Development/KansasLava/Shake/Xilinx.hs b/src/Development/KansasLava/Shake/Xilinx.hs
new file mode 100644
--- /dev/null
+++ b/src/Development/KansasLava/Shake/Xilinx.hs
@@ -0,0 +1,212 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+module Development.KansasLava.Shake.Xilinx
+       ( XilinxConfig(..)
+       , xilinxRules
+       ) where
+
+import Development.Shake
+import Development.Shake.FilePath
+import System.Directory
+
+import Data.Monoid
+import Data.List (stripPrefix)
+import Data.Maybe (fromJust)
+import Data.String (fromString)
+
+import qualified Data.Text.Lazy as TL
+import qualified Data.Text as TS
+import Text.Hastache
+
+import Paths_kansas_lava_shake
+
+data XilinxConfig = XilinxConfig{ xilinxRoot :: FilePath
+                                , xilinxPlatform :: String
+                                }
+
+xilinxRules :: XilinxConfig
+            -> String
+            -> [String]
+            -> Rules ()
+xilinxRules XilinxConfig{..} mod xaws = do
+    "*.ut" *>
+        textTemplate []
+
+    "*.xst" *>
+        textTemplate [("MAIN", fromString mod), ("TOP", fromString mod)]
+
+    "*.xise" *>
+        listTemplate "components" xiseFiles
+
+    "*.prj" *> \target -> do
+        let vhdlWork baseName = mconcat ["vhdl work \"", baseName <.> "vhdl", "\""]
+        liftIO $ writeFile target . unlines $
+          map (vhdlWork . gensrc) vhdls ++ map (vhdlWork . xawsrc) xaws
+
+    xawsrc "*.vhdl" *> \target -> do
+        let xaw = ".." </> "xaw" </> takeFileName target -<.> "xaw"
+        need [xaw]
+        removeFilesAfter "." ["xaw2vhdl.log"]
+        xilinx "xaw2vhdl" [xaw, "-st", "XST", target]
+    xawsrc "*.ucf" *> \target -> need [target -<.> "vhdl"]
+
+    "xst/projnav.tmp" *> liftIO . createDirectoryIfMissing True
+
+    "*.ngc" *> \target -> do
+        need $
+          (target -<.> "prj"):
+          (target -<.> "xst"):
+          ("xst" </> "projnav.tmp"):
+          [gensrc $ f <.> "vhdl" | f <- vhdls] ++
+          [xawsrc $ f <.> "vhdl" | f <- xaws]
+
+        removeFilesAfter "."
+          [ "xst//*"
+          , "_xmsgs//*"
+          , target -<.> "lso"
+          , target -<.> "ngr"
+          , target -<.> "syr"
+          , "*.xrpt"
+          ]
+        xilinx "xst" [ "-ifn", target -<.> "xst"
+                     , "-ofn", target -<.> "syr"
+                     ]
+
+    "*.ngd" *> \target -> do
+        let ucf = gensrc $ target -<.> "ucf"
+        need [ target -<.> "ngc"
+             , ucf
+             , "xst/projnav.tmp"
+             ]
+        removeFilesAfter "."
+          [ target -<.> "bld"
+          , "ngo//*"
+          , "xlnx_auto_0_xdb//*"
+          , "*.xrpt"
+          ]
+        xilinx "ngdbuild" [ "-dd", "ngo"
+                          , "-nt", "timestamp"
+                          , "-uc", ucf
+                          , "-p", xilinxPlatform
+                          , target -<.> "ngc"
+                          , target
+                          ]
+
+    "*.pcf" *> \target -> do
+        need [ target -<.> "ngc"
+             , target -<.> "ngd"
+             , "xst/projnav.tmp"
+             ]
+        removeFilesAfter "."
+          [ "*_summary.xml"
+          , "*_usage.xml"
+          , target -<.> "ngm"
+          , target -<.> "mrp"
+          , target -<.> "map"
+          ]
+        xilinx "map" [ "-p", xilinxPlatform
+                     , "-ir", "off"
+                     , "-pr", "off"
+                     , "-c", "100"
+                     , "-o", mapFileName (<> "_map") target -<.> "ncd"
+                     , target -<.> "ngd"
+                     , target -<.> "pcf"
+                     ]
+
+    alternatives $ do
+        "*_map.ncd" *> \target -> need [mapFileName (fromJust . stripSuffix "_map") target -<.> "pcf"]
+
+        "*.ncd" *> \target -> do
+            need [ "xst" </> "projnav.tmp"
+                 , target -<.> "pcf"
+                 ]
+            removeFilesAfter "."
+              [ "*_pad.txt"
+              , "*_pad.xrpt"
+              , "*_pad.csv"
+              , "_xmsgs//*"
+              , target -<.> "pad"
+              , target -<.> "par"
+              , target -<.> "xpi"
+              , target -<.> "unroutes"
+              , target -<.> "ptwx"
+              ]
+
+            xilinx "par" [ "-w"
+                         , "-ol", "high"
+                         , "-mt", "off"
+                         , mapFileName (<> "_map") target -<.> "ncd"
+                         , target -<.> "ncd"
+                         , target -<.> "pcf"
+                         ]
+
+    "*.bit" *> \target -> do
+        need [ "xst/projnav.tmp"
+             , target -<.> "ut"
+             , target -<.> "ncd"
+             ]
+        removeFilesAfter "."
+          [ "*_bitgen.xwbt"
+          , "usage_statistics_webtalk.html"
+          , "webtalk.log"
+          , target -<.> "bgn"
+          , target -<.> "drc"
+          ]
+        xilinx "bitgen" [ "-f", target -<.> "ut"
+                        , target -<.> "ncd"
+                        ]
+  where
+    xilinx tool args = cmd (xilinxRoot </> tool) args
+
+    gensrc f = "gensrc" </> f
+    xawsrc f = gensrc $ "xaw" </> f
+
+    vhdls = [mod, "lava-prelude"]
+
+    xiseFiles = map (return .) $ ucf : map vhdl vhdls ++ map xaw xaws
+      where
+        vhdl componentName = \key -> case key of
+            "type" -> MuVariable ("FILE_VHDL" :: String)
+            "fileName" -> MuVariable $ componentName <.> "vhdl"
+            "behavior" -> MuBool True
+        ucf = \key -> case key of
+            "type" -> MuVariable ("FILE_UCF" :: String)
+            "fileName" -> MuVariable $ mod <.> "ucf"
+            "behavior" -> MuBool False
+        xaw componentName = \key -> case key of
+            "type" -> MuVariable ("FILE_XAW" :: String)
+            "fileName" -> MuVariable $ ".." </> "xaw" </> componentName <.> "xaw"
+            "behavior" -> MuBool True
+
+hastache :: MuContext IO -> FilePath -> Action ()
+hastache ctxt target = do
+    templateFile <- liftIO $ getDataFileName ("ise.template" </> templateName)
+    t <- liftIO $ hastacheFile hastacheConfig templateFile ctxt
+    writeFileChanged target $ TL.unpack t
+  where
+    hastacheConfig = MuConfig{ muEscapeFunc = emptyEscape
+                             , muTemplateFileDir = Nothing
+                             , muTemplateFileExt = Just "mustache"
+                             , muTemplateRead = const $ return Nothing
+                             }
+
+    ext = drop 1 . takeExtension $ target
+    templateName = ext <.> "mustache"
+
+listTemplate :: TS.Text -> [MuContext IO] -> FilePath -> Action ()
+listTemplate key0 entities = hastache ctxt
+  where
+    ctxt key = return $ if key == key0 then MuList entities else MuNothing
+
+textTemplate :: [(TS.Text, TL.Text)] -> FilePath -> Action ()
+textTemplate replacements = hastache ctxt
+  where
+    ctxt key = return $ case lookup key replacements of
+        Just value -> MuVariable value
+        Nothing -> MuNothing
+
+mapFileName :: (String -> String) -> FilePath -> FilePath
+mapFileName f fp = replaceFileName fp (f (takeFileName fp))
+
+stripSuffix :: (Eq a) => [a] -> [a] -> Maybe [a]
+stripSuffix suffix = fmap reverse . stripPrefix (reverse suffix) . reverse
