diff --git a/cake3.cabal b/cake3.cabal
--- a/cake3.cabal
+++ b/cake3.cabal
@@ -2,33 +2,88 @@
 -- see http://haskell.org/cabal/users-guide/
 
 name:                cake3
-version:             0.1.0.0
-synopsis:            Third cake - Makefile DSL
-description:         Thirdcake is a Makefile DSL written in Haskell. Write your build logic in
+version:             0.2.1.0
+synopsis:            Third cake the Makefile EDSL
+description:         Cake3 is a Makefile EDSL written in Haskell. Write your build logic in
                      Haskell, obtain clean and safe Makefile, distribute it to the end-users.
                      GNU Make is required.
 license:             BSD3
 license-file:        LICENSE
 author:              Sergey Mironov
 maintainer:          grrwlf@gmail.com
--- copyright:           
+homepage:            https://github.com/grwlf/cake3
+description: 
+    Cake3 is a EDSL for building Makefiles, written in Haskell. With cake3,
+    developer can write their build logic in Haskell, obtain clean and safe Makefile
+    and distribute it among the non-Haskell-aware users. Currenly, GNU Make is
+    the only backend supported.
+    .
+    /Example program/
+    .
+    > module Cakefile where
+    >
+    > import Development.Cake3
+    > import Cakefile_P
+    >
+    > cs = map file ["main.c", "second.c"]
+    >
+    > main = writeMake (file "Makefile") $ do
+    >   selfUpdate
+    >   d <- rule $ do
+    >     shell [cmd|gcc -M $cs -MF @(file "depend.mk")|]
+    >   os <- forM cs $ \c -> do
+    >     rule $ do
+    >       shell [cmd| gcc -c $(extvar "CFLAGS") -o @(c.="o") $c |]
+    >   elf <- rule $ do
+    >     shell [cmd| gcc -o @(file "main.elf") $os |]
+    >   rule $ do
+    >     phony "clean"
+    >     unsafeShell [cmd|rm $elf $os $d|]
+    >   rule $ do
+    >     phony "all"
+    >     depend elf
+    >   includeMakefile d
+    .
+    /Basic workflow/
+    .
+      * Install the cake3
+    .
+      * Create Cakefile.hs in the project root
+    .
+      * Build the application using cake3 script provided
+    . 
+      * Execute the application to obtain the Makefile
+    .
+    See the README on the GitHub <https://github.com/grwlf/cake3> for more information.
+    .
+    /Changes/
+    .
+    * 0.1 - Initial release
+    .
+    * 0.2 - Redesign (simplify) monadic interface, add support for prebuild/postbuild actions.
+
+
 category:            Development
 build-type:          Simple
 cabal-version:       >=1.8
 data-dir:            src
-data-files:          CakeScript.sh
+data-files:          CakeScript.sh, UrEmbedHelp.txt
 
 library
-  exposed-modules:   Development.Cake3, Development.Cake3.Types,
-                     Development.Cake3.Writer, System.FilePath.Wrapper,
-                     Development.Cake3.Monad,
-                     Development.Cake3.Rules.UrWeb
+  exposed-modules:   Development.Cake3
+                     Development.Cake3.Types
+                     Development.Cake3.Writer
+                     System.FilePath.Wrapper
+                     Development.Cake3.Monad
+                     Development.Cake3.Ext.UrWeb
                      Development.Cake3.Utils.Find
+                     Text.QuasiMake
 
   build-depends:     base ==4.6.*, haskell-src-meta, template-haskell,
-                     QuasiText, filepath, containers, text, monadloc, mtl,
-                     bytestring,deepseq, system-filepath, text-format,
-                     directory
+                     filepath, containers, text, monadloc, mtl,
+                     bytestring, deepseq, system-filepath, text-format,
+                     directory, attoparsec, mime-types,
+                     language-javascript ==0.5.*, syb
 
   hs-source-dirs:    src
 
@@ -36,5 +91,15 @@
   hs-source-dirs:    src
   main-is:           CakeScript.hs
   build-depends:     base ==4.6.*, process
+  other-modules:     Paths_cake3
+
+executable urembed
+  hs-source-dirs:    src
+  main-is:           Development/Cake3/Ext/UrWeb/UrEmbed.hs
+  build-depends:     base == 4.6.*, mtl, language-javascript ==0.5.*, process,
+                     filepath, syb, optparse-applicative, directory,
+                     text, bytestring, containers, language-javascript,
+                     haskell-src-meta, template-haskell, attoparsec, monadloc,
+                     mime-types
   other-modules:     Paths_cake3
 
diff --git a/src/CakeScript.sh b/src/CakeScript.sh
--- a/src/CakeScript.sh
+++ b/src/CakeScript.sh
@@ -10,53 +10,105 @@
 -- This file was autogenerated by cake3.
 -- $CAKEURL
 
-module ${1}_P(file,cakefiles,projectroot,moduleroot) where
+{-# LANGUAGE TypeSynonymInstances #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE QuasiQuotes #-}
+module ${1}_P(file, cakefiles, selfUpdate,filterDirectoryContentsRecursive) where
 
+import Control.Monad.Trans
+import Control.Monad.State
 import Development.Cake3
+import Development.Cake3.Monad
+import Development.Cake3.Utils.Find
+import GHC.Exts (IsString(..))
 
+pl = ProjectLocation projectroot moduleroot
+
 file :: String -> File
-file x = file' "$TOP" "$2" x
+file x = file' pl x
 
-projectroot :: File
-projectroot = file "$TOP"
+-- instance IsString File where
+--   fromString = file
 
-moduleroot :: File
-moduleroot = file "$2"
+projectroot :: FilePath
+projectroot = "$TOP"
 
+moduleroot :: FilePath
+moduleroot = "$2"
+
 cakefiles :: [File]
-cakefiles = case "$2" of
-              "$TOP" -> map (file' "$TOP" "$TOP") \$ $3
-              _ -> error "cakefiles are defined for top-level cake only"
+cakefiles = 
+  let rl = ProjectLocation projectroot projectroot in
+  case "$2" of
+    "$TOP" -> map (file' rl) ($3)
+    _ -> error "cakefiles are defined for top-level cake only"
 
+selfUpdate :: Make [File]
+selfUpdate = do
+  makefile <- outputFile <$> get
+  (_,cg) <- rule2 $ do
+    depend cakefiles
+    produce (file "Cakegen")
+    shell [cmd|cake3|]
+  (_,f) <- rule2 $ do
+    produce makefile
+    shell [cmd|\$cg|]
+  return f
+
+filterDirectoryContentsRecursive :: (MonadIO m) => [String] -> m [File]
+filterDirectoryContentsRecursive exts = liftM (filterExts exts) (getDirectoryContentsRecursive (file "."))
+
 EOF
 }
 
 caketemplate() {
 cat <<"EOF"
-{-# LANGUAGE RecursiveDo, QuasiQuotes #-}
+{-# LANGUAGE QuasiQuotes, OverloadedStrings #-}
 module Cakefile where
 
 import Development.Cake3
-import Cakefile_P (file, cakefiles)
+import Cakefile_P (file,projectroot)
 
-elf = rule (file "main.elf") $ do
-    shell [cmd| echo "Your commands go here" ; exit 1 ; |]
 
-main = runMake (place (phony "all" (depend elf)) >> place defaultSelfUpdate)
+main = writeMake "Makefile" $ do
 
+  cs <- filterDirectoryContentsRecursive [".c"]
+
+  d <- rule $ do
+    shell [cmd|gcc -M @cfiles -MF %(file "depend.mk")|]
+
+  os <- forM cs $ \c -> do
+    rule $ do
+      shell [cmd| gcc -c $(extvar "CFLAGS") -o %(c.="o") @c |]
+
+  elf <- rule $ do
+    shell [cmd| gcc -o %(file "main.elf") @os |]
+
+  rule $ do
+    phony "all"
+    depend elf
+
+  includeMakefile d
+
 EOF
 }
 
+ARGS_PASS=""
+GHCI=n
+GHC_EXTS="-XFlexibleInstances -XTypeSynonymInstances -XOverloadedStrings -XQuasiQuotes"
+
 while test -n "$1" ; do
   case "$1" in
     --help|-h|help) 
-      err "Cake3 the Makefile generator help"
+      err "Cake3 the Makefile generator"
       err "$CAKEURL"
-      err "Usage: cake3 [--help|-h] [init]"
+      err "Usage: cake3 [--help|-h] [init] args"
       err "cake3 init"
-      err "    - Create default Cakefile.hs"
-      err "cake3"
-      err "    - Build the Makefile"
+      err "    - Create the default Cakefile.hs"
+      err "cake3 <args>"
+      err "    - Compile the ./Cakegen and run it. Args are passed as is."
+      err ""
+      err "Other arguments are passed to the ./Cakgegen as is"
       exit 1;
       ;;
     init)
@@ -66,6 +118,12 @@
       echo "Cakefile.hs has been created"
       exit 0;
       ;;
+    ghci)
+      GHCI=y
+      ;;
+    *)
+      ARGS_PASS="$ARGS_PASS $1"
+      ;;
   esac
   shift
 done
@@ -77,9 +135,11 @@
   find -type f '(' -name 'Cake*\.hs' -or -name 'Cake*\.lhs' \
                -or -name '*Cake\.hs' -or -name '*Cake\.lhs' ')' \
                -and -not -name '*_P.hs' \
-    | grep -v '^\.[a-zA-Z].*'
+    | grep -v '^\.[a-zA-Z].*' \
+    | sort
 }
 
+OIFS=$IFS
 IFS=$'\n'
 CAKES=`cakes`
 CAKELIST="[]"
@@ -111,20 +171,21 @@
   fi
 
   if test -f "$tgt" ; then
-    die "More than one file named '${fname}.hs' in the filetree"
+    err "Warning: duplicate file name, ignoring $f."
+    continue
   fi
 
   cp "$f" "$tgt" ||
-    die "cp $f $tgt failed. Duplicate names?"
+    die "Couldn't cp $f $tgt"
 
   if cat "$f" | grep -q "import.*${fname}_P" ; then
-    echo "Creating $fdir/${pname}" >&2
+    err "Creating $fdir/${pname}"
     cakepath "$fname" "$fdir_abs" "$CAKELIST" > "$fdir/${pname}"
 
     cp "$fdir/${pname}" "$T/${pname}" ||
       die -n "cp $fdir/${pname} $T/${pname} failed"
   else
-    echo "Skipping creating $fdir/${pname}" >&2
+    err "Warning: ${pname} is not required by $f"
   fi
 done
 
@@ -132,12 +193,17 @@
   die "No Cake* file exist in the current directory. Consider running \`cake3 --help'."
 fi
 
+IFS=$OIFS
+
 (
 set -e
 cd $T
-ghc --make "$MAIN_" -main-is "$MAIN" -o Cakegen
+case $GHCI in
+  n) ghc --make "$MAIN_" $GHC_EXTS -main-is "$MAIN" -o Cakegen ;;
+  y) exec ghci -main-is "$MAIN" $GHC_EXTS ;;
+esac
+
 cp -t "$CWD" Cakegen
 ) &&
-
-./Cakegen > Makefile  && echo "Makefile created" >&2
+./Cakegen $ARGS_PASS
 
diff --git a/src/Development/Cake3.hs b/src/Development/Cake3.hs
--- a/src/Development/Cake3.hs
+++ b/src/Development/Cake3.hs
@@ -1,37 +1,36 @@
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE QuasiQuotes #-}
-{-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE FunctionalDependencies #-}
 
 module Development.Cake3 (
 
-    Alias
-  , Variable
+    Variable
   , Recipe
-  , Referal(..)
-  , Placable(..)
-  , Reference
-  , ReferenceLike(..)
+  , RefInput(..)
+  , RefOutput(..)
+  , CakeString
+  , string
 
   -- Monads
   , A
   , Make
-  , toMake
+  , buildMake
   , runMake
-  , runMake_
+  , writeMake
+  , includeMakefile
+  , MonadMake(..)
 
   -- Rules
-  , Rule
-  , Rules
   , rule
-  , ruleM
+  , rule2
+  , rule'
   , phony
-  , phonyM
   , depend
-  , unsafe
-  , defaultSelfUpdate
+  , produce
+  , ignoreDepends
+  , prebuild
+  , postbuild
   
   -- Files
   , FileLike(..)
@@ -40,33 +39,32 @@
   , (.=)
   , (</>)
   , toFilePath
-  , fromFilePath
+  , readFileForMake
 
   -- Make parts
   , prerequisites
   , shell
+  , unsafeShell
   , cmd
   , makevar
   , extvar
-  , dst
-  , makefile
-  , CommandGen(..)
-  , unCommand
+  , CommandGen'(..)
+  , make
+  , ProjectLocation(..)
+  , currentDirLocation
 
-  -- More
+  -- Import several essential modules
+  , module Data.String
   , module Control.Monad
   , module Control.Applicative
   ) where
 
-import Prelude (id, Char(..), Bool(..), Maybe(..), Either(..), flip, ($), (+), (.), (/=), undefined, error,not)
-
 import Control.Applicative
 import Control.Monad
 import Control.Monad.Trans
 import Control.Monad.Writer
 import Control.Monad.State
 import Control.Monad.Loc
-import qualified Data.Text as T
 import qualified Data.List as L
 import Data.List (concat,map, (++), reverse,elem,intercalate,delete)
 import Data.Foldable (Foldable(..), foldr)
@@ -74,225 +72,107 @@
 import Data.Map (Map)
 import qualified Data.Set as S
 import Data.Set (Set,member,insert)
-import Data.String as S
+import Data.String
 import Data.Tuple
 import System.IO
+import System.Directory
 import qualified System.FilePath as F
-import Text.QuasiText
 import Text.Printf
 
-import Language.Haskell.TH.Quote
-import Language.Haskell.TH hiding(unsafe)
-import Language.Haskell.Meta (parseExp)
-
 import Development.Cake3.Types
 import Development.Cake3.Writer
 import Development.Cake3.Monad
-import System.FilePath.Wrapper
-
-makefile :: File
-makefile = makefileT
-
-file' :: String -> String -> String -> File
-file' root cwd f' =
-  let f = F.dropTrailingPathSeparator f' in
-  (fromFilePath ".") </> makeRelative (fromFilePath root)
-                                      ((fromFilePath cwd) </> (fromFilePath f))
-
-defaultSelfUpdate = rule makefile $ do
-  -- shell [cmd|./Cakegen > Makefile |]
-  shell (CommandGen (concat <$> sequence [
-      ref $ (fromFilePath ".") </> (fromFilePath "Cakegen" :: File)
-    , ref $ string " > "
-    , ref makefile]))
-
-runMake_ :: Make () -> IO ()
-runMake_ mk = evalMake mk >>= output where
-  output (Left err) = hPutStrLn stderr err
-  output (Right a) = hPutStrLn stdout (toMake a)
-
-runMake :: Make () -> IO String
-runMake mk = evalMake mk >>= output where
-  output (Left err) = fail err
-  output (Right a) = return (toMake a)
-
--- | CommandGen is a recipe packed in the newtype to prevent partial expantion
-newtype CommandGen = CommandGen (A Command)
-unCommand (CommandGen a) = a
-
-type Rule = Alias
-type Rules = [Alias]
+import System.FilePath.Wrapper as W
 
--- | Means that data structure f (containing Files) may be used to create data
--- structure a (containing Aliases).
-class Rulable f a | f -> a where
-  rule :: f -> A () -> a
+data ProjectLocation = ProjectLocation {
+    root :: FilePath
+  , off :: FilePath
+  } deriving (Show, Eq, Ord)
 
-ruleM :: (Monad m, Rulable f a) => f -> A () -> m a
-ruleM a b = return (rule a b)
+currentDirLocation :: (MonadIO m) => m ProjectLocation
+currentDirLocation = do
+  cwd <- liftIO $ getCurrentDirectory
+  return $ ProjectLocation cwd cwd
 
-list1 a = [a]
-fmap1 f a = f a
+file' :: ProjectLocation -> String -> File
+file' pl f' = fromFilePath (addpoint (F.normalise rel)) where
+  rel = makeRelative (root pl) ((off pl) </> f)
+  f = F.dropTrailingPathSeparator f'
+  addpoint "." = "."
+  addpoint p = "."</>p
 
-list2 (a1,a2) = [a1,a2]
-fmap2 f (a1,a2) = (f a1,f a2)
+runMake'
+  :: File -- ^ Output file
+  -> Make a  -- ^ Make builder
+  -> (String -> IO b) -- ^ Handler to output the file
+  -> IO b
+runMake' makefile mk output = do
+  ms <- evalMake makefile mk
+  when (not $ L.null (warnings ms)) $ do
+    hPutStr stderr (warnings ms)
+  when (not $ L.null (errors ms)) $ do
+    fail (errors ms)
+  case buildMake ms of
+    Left e -> fail e
+    Right s -> output s
 
-list3 (a1,a2,a3) = [a1,a2,a3]
-fmap3 f (a1,a2,a3) = (f a1,f a2,f a3)
+-- | Execute the Make monad, build the Makefile, write it to the output file. Also
+-- note, that errors (if any) go to the stderr. fail will be executed in such
+-- cases
+writeMake
+  :: File -- ^ Output file
+  -> Make a -- ^ Makefile builder
+  -> IO ()
+writeMake f mk = runMake' f mk (writeFile (toFilePath f))
 
-list4 (a1,a2,a3,a4) = [a1,a2,a3,a4]
-fmap4 f (a1,a2,a3,a4) = (f a1,f a2,f a3,f a4)
+-- | A General Make runner. Executes the monad, returns the Makefile as a
+-- String. Errors go to stdout. fail is possible.
+runMake :: Make a -> IO String
+runMake mk = runMake' defaultMakefile mk return
 
-phony name = rule' fmap1 list1 True (fromFilePath name)
+-- | Execute Make action, place the recipe above all other recipes (it will be
+-- higher in the final Makefile)
+withPlacement :: (MonadMake m) => m (Recipe,a) -> m (Recipe,a)
+withPlacement mk = do
+  (r,a) <- mk
+  liftMake $ do
+    addPlacement 0 (S.findMin (rtgt r))
+    return (r,a)
 
-phonyM :: (Monad m) => String -> A () -> m Alias
-phonyM n a = return $ phony n a
+-- | Adds the phony target for a rule. Typical usage:
+-- 
+-- > rule $ do
+-- >  phony "clean"
+-- >  unsafeShell [cmd|rm $elf $os $d|]
+-- >
+phony :: String -> A ()
+phony name = do
+  produce (W.fromFilePath name :: File)
+  markPhony
 
-rule' fmapX listX isPhony dst act = flip fmapX dst $ \x -> Alias (x, listX dst, do
+-- | Build a Recipe using recipe builder @act. Don't change recipe's priority.
+rule2 :: (MonadMake m) => A a -> m (Recipe,a)
+rule2 act = liftMake $ do
   loc <- getLoc
-  runA (Recipe (S.fromList (listX dst)) mempty [] M.empty loc isPhony) act)
-
-instance Rulable File Alias where
-  rule = rule' fmap1 list1 False
-
-instance Rulable (File,File) (Alias,Alias) where
-  rule = rule' fmap2 list2 False
-
-instance Rulable (File,File,File) (Alias,Alias,Alias) where
-  rule = rule' fmap3 list3 False
-
-instance Rulable (File,File,File,File) (Alias,Alias,Alias,Alias) where
-  rule = rule' fmap4 list4 False
-
-instance Rulable [File] [Alias] where
-  rule = rule' map id False
-
--- FIXME: depend can be used under unsafe but it doesn't work
-unsafe :: A () -> A ()
-unsafe action = do
-  r <- get
-  action
-  modify $ \r' -> r' { rsrc = rsrc r, rvars = rvars r }
-
-shell :: CommandGen -> A ()
-shell cmd = do
-  line <- unCommand cmd
-  modify (\r -> r { rcmd = (rcmd r) ++ [line] })
-
-depend :: (Referal x) => x -> A ()
-depend x = ref x >> return ()
-
-var :: String -> Maybe String -> Variable
-var n v = Variable n v
-
-makevar :: String -> String -> Variable
-makevar n v = var n (Just v)
-
-extvar :: String -> Variable
-extvar n = var n Nothing
-
-newtype Reference = Reference String
-
-class ReferenceLike a where
-  string :: a -> Reference
-
-instance ReferenceLike String where
-  string s = Reference s
-
-instance ReferenceLike File where
-  string (FileT x) = string x
-
-instance ReferenceLike Alias where
-  string (Alias (x,_,_)) = string x
-
-dst :: A (Set File)
-dst = rtgt <$> get
-
--- | Data structure x may be referenced from within the command. Referal
--- class specifies side effects of such referencing. For example, referencig the
--- file leads to adding it to the prerequisites list.
-class Referal x where
-  ref :: x -> A Command
-
-instance Referal Command where
-  ref = return
-
-instance Referal Reference where
-  ref v@(Reference s) = do
-    return_text s
-
-instance Referal Variable where
-  ref v@(Variable n _) = do
-    addVariable v
-    return_text $ printf "$(%s)" n
-
--- Alias may be referenced from the recipe of itself, so we have to prevent
--- the recursion
-not_myself :: File -> A a -> A ()
-not_myself f act = targets >>= \ts -> do
-  when (not (f `member` ts)) (act >> return ())
-
-instance Referal File where
-  ref f = do
-    not_myself f $ do
-      modify $ \r -> r { rsrc = f `insert` (rsrc r)}
-    return_file f
-
-instance Referal Alias where
-  ref (Alias (f,_,mr)) = do
-    not_myself f (A (lift mr))
-    ref f
-
-instance Referal [Alias] where
-  ref as = concat <$> (mapM ref as)
-
-instance Referal (Set File) where
-  ref as = ref (S.toList as)
-
-instance Referal [File] where
-  ref fs = concat <$> mapM ref fs
-
-instance Referal x => Referal (A x) where
-  ref mx = mx >>= ref
-
-instance Referal x => Referal (Make x) where
-  ref mx = (A $ lift mx) >>= ref
-
-instance Referal x => Referal (IO x) where
-  ref mx = liftIO mx >>= ref
-
-instance Referal CommandGen where
-  ref (CommandGen acmd) = ref acmd
+  (r,a) <- runA loc act
+  addRecipe r
+  return (r,a)
 
--- | Has effect of a function :: QQ -> CommandGen where QQ is a string supporting
--- $VARs. Each $VAR will be dereferenced using Ref typeclass. Result will
--- be equivalent to
+-- | Version of rule2 which places it's recipe above all other recipies.
 --
--- return Command $ do
---   s1 <- ref "gcc "
---   s2 <- ref (flags :: Variable)
---   s3 <- ref " "
---   s4 <- ref (file :: File)
---   return (s1 ++ s2 ++ s3)
+-- > let c = file "main.c"
 --
--- Later, this command may be examined or passed to the shell function to apply
--- it to the recepi
+-- Declare a rule to build "main.o" out of "main.c" and "CFLAGS" variable
 --
-cmd :: QuasiQuoter
-cmd = QuasiQuoter
-  { quotePat  = undefined
-  , quoteType = undefined
-  , quoteDec  = undefined
-  , quoteExp = \s -> appE [| \x -> CommandGen x |] (qqact s)
-  } where
-    qqact s = 
-      let chunks = flip map (getChunks (S.fromString s)) $ \c ->
-                     case c of
-                       T t -> [| return_text t |]
-                       E t -> case parseExp (T.unpack t) of
-                                Left  e -> error e
-                                Right e -> appE [| ref |] (return e)
-                       V t -> appE [| ref |] (global (mkName (T.unpack t)))
-      in appE [| \l -> L.concat <$> (sequence l) |] (listE chunks)
+-- > rule $ shell [cmd| gcc -c $(extvar "CFLAGS") -o @(c.="o") $c |]
+--
+rule
+  :: A a    -- ^ Rule builder
+  -> Make a
+rule act = snd <$> withPlacement (rule2 act)
+
+-- | Version of rule2, without Make monad set explicitly
+rule' :: (MonadMake m) => A a -> m a
+rule' act = liftMake $ snd <$> withPlacement (rule2 act)
 
 
diff --git a/src/Development/Cake3/Ext/UrWeb.hs b/src/Development/Cake3/Ext/UrWeb.hs
new file mode 100644
--- /dev/null
+++ b/src/Development/Cake3/Ext/UrWeb.hs
@@ -0,0 +1,492 @@
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+module Development.Cake3.Ext.UrWeb where
+
+import Data.Data
+import Data.Char
+import Data.Typeable
+import Data.Generics
+import Data.Maybe
+import Data.Monoid
+import Data.List ()
+import qualified Data.List as L
+import Data.Set (Set)
+import qualified Data.Set as S
+import Data.Foldable (Foldable(..), foldl')
+import qualified Data.Foldable as F
+import Data.ByteString.Char8 (ByteString(..))
+import qualified Data.ByteString.Char8 as BS
+import qualified Data.Text as T
+import Data.String
+import Control.Applicative
+import Control.Monad.Trans
+import Control.Monad.State
+import Control.Monad.Writer
+import Control.Monad.Error
+import Language.JavaScript.Parser
+import Network.Mime (defaultMimeLookup)
+import System.Directory
+import Text.Printf
+import qualified System.FilePath as F
+import System.IO as IO
+
+import System.FilePath.Wrapper
+import Development.Cake3.Monad
+import Development.Cake3
+
+data UrpAllow = UrpMime | UrpUrl
+  deriving(Show,Data,Typeable)
+
+data UrpRewrite = UrpStyle
+  deriving(Show,Data,Typeable)
+
+data UrpHdrToken = UrpDatabase String
+                 | UrpSql File
+                 | UrpAllow UrpAllow String
+                 | UrpRewrite UrpRewrite String
+                 | UrpLibrary File
+                 | UrpDebug
+                 | UrpInclude File
+                 | UrpLink File
+                 | UrpFFI File
+                 | UrpJSFunc String String
+                 | UrpSafeGet String
+  deriving(Show,Data,Typeable)
+
+data UrpModToken
+  = UrpModule1 File
+  | UrpModule2 File File
+  | UrpModuleSys String
+  deriving(Show,Data,Typeable)
+
+data Urp = Urp {
+    urp :: File
+  , uexe :: Maybe File
+  , uhdr :: [UrpHdrToken]
+  , umod :: [UrpModToken]
+  } deriving(Show,Data,Typeable)
+
+newtype UWLib = UWLib Urp
+  deriving (Show,Data,Typeable)
+
+newtype UWExe = UWExe Urp
+  deriving (Show,Data,Typeable)
+
+instance (MonadAction a m) => RefInput a m UWLib where
+  refInput (UWLib u) = refInput (urp u)
+ 
+instance (MonadAction a m) => RefInput a m UWExe where
+  refInput (UWExe u) = refInput (urpExe u)
+ 
+
+urpDeps :: Urp -> [File]
+urpDeps (Urp _ _ hdr mod) = foldl' scan2 (foldl' scan1 mempty hdr) mod where
+  scan1 a (UrpLink f) = f:a
+  scan1 a (UrpInclude f) = f:a
+  scan1 a _ = a
+  scan2 a (UrpModule1 f) = f:a
+  scan2 a (UrpModule2 f1 f2) = f1:f2:a
+  scan2 a _ = a
+
+urpSql' :: Urp -> Maybe File
+urpSql' (Urp _ _ hdr _) = find hdr where
+  find [] = Nothing
+  find ((UrpSql f):hs) = Just f
+  find (h:hs) = find hs
+
+urpSql :: Urp -> File
+urpSql u = case urpSql' u of
+  Nothing -> error "ur project defines no SQL file"
+  Just sql -> sql
+
+urpObjs (Urp _ _ hdr _) = foldl' scan [] hdr where
+  scan a (UrpLink f) = f:a
+  scan a _ = a
+
+urpLibs (Urp _ _ hdr _) = foldl' scan [] hdr where
+  scan a (UrpLibrary f) = f:a
+  scan a _ = a
+
+urpExe u = case uexe u of
+  Nothing -> error "ur project defines no EXE file"
+  Just exe -> exe
+
+data UrpState = UrpState {
+    urpst :: Urp
+  } deriving (Show)
+
+defState urp = UrpState (Urp urp Nothing [] [])
+
+class ToUrpWord a where
+  toUrpWord :: a -> String
+
+instance ToUrpWord UrpAllow where
+  toUrpWord (UrpMime) = "mime"
+  toUrpWord (UrpUrl) = "url"
+
+instance ToUrpWord UrpRewrite where
+  toUrpWord (UrpStyle) = "style"
+
+class ToUrpLine a where
+  toUrpLine :: FilePath -> a -> String
+
+instance ToUrpLine UrpHdrToken where
+  toUrpLine up (UrpDatabase dbs) = printf "database %s" dbs
+  toUrpLine up (UrpSql f) = printf "sql %s" (up </> toFilePath f)
+  toUrpLine up (UrpAllow a s) = printf "allow %s %s" (toUrpWord a) s
+  toUrpLine up (UrpRewrite a s) = printf "rewrite %s %s" (toUrpWord a) s
+  toUrpLine up (UrpLibrary f) = printf "library %s" (up </> toFilePath (dropExtensions f))
+  toUrpLine up (UrpDebug) = printf "debug"
+  toUrpLine up (UrpInclude f) = printf "include %s" (up </> toFilePath f)
+  toUrpLine up (UrpLink f) = printf "link %s" (up </> toFilePath f)
+  toUrpLine up (UrpFFI s) = printf "ffi %s" (up </> toFilePath (dropExtensions s))
+  toUrpLine up (UrpSafeGet s) = printf "safeGet %s" (dropExtensions s)
+
+instance ToUrpLine UrpModToken where
+  toUrpLine up (UrpModule1 f) = up </> toFilePath (dropExtensions f)
+  toUrpLine up (UrpModule2 f _) = up </> toFilePath (dropExtensions f)
+  toUrpLine up (UrpModuleSys s) = printf "$/%s" s
+
+newtype UrpGen m a = UrpGen { unUrpGen :: StateT UrpState m a }
+  deriving(Functor, Applicative, Monad, MonadState UrpState, MonadMake, MonadIO)
+
+instance (Monad m) => MonadAction (UrpGen (A' m)) m where
+  liftAction a = UrpGen (lift a)
+
+toFile f wr = liftIO $ writeFile (toFilePath f) $ execWriter $ wr
+
+line :: (MonadWriter String m) => String -> m ()
+line s = tell (s++"\n")
+
+uwlib :: File -> UrpGen (A' (Make' IO)) () -> Make UWLib
+uwlib urpfile m = do
+  (_,u) <- rule2 $ do
+    ((),s) <- runStateT (unUrpGen m) (defState urpfile)
+    let u@(Urp _ _ hdr mod) = urpst s
+    let up = urpUp urpfile
+
+    toFile urpfile $ do
+      forM hdr (line . toUrpLine up)
+      line ""
+      forM mod (line . toUrpLine up)
+
+    forM_ (urpObjs u) $ \o -> do
+      let incl = makevar "URINCL" "$(shell urweb -print-cinclude)"
+      let cc = makevar "URCC" "$(shell $(shell urweb -print-ccompiler) -print-prog-name=gcc)"
+      rule2 $ do
+        shell [cmd| $cc -c -I $incl -o @o $(o .= "c") |]
+
+    depend (urpDeps u)
+    depend (urpLibs u)
+    shell [cmd|touch @urpfile|]
+    return u
+
+  return $ UWLib u
+
+uwapp :: String -> File -> UrpGen (A' (Make' IO)) () -> Make UWExe
+uwapp opts urpfile m = do
+  (UWLib u') <- uwlib urpfile m
+  let u = u' { uexe = Just (urpfile .= "exe") }
+  rule $ do
+    depend urpfile
+    produce (urpExe u)
+    case urpSql' u of
+      Nothing -> return ()
+      Just sql -> produce sql
+    unsafeShell [cmd|urweb $(string opts) $((takeDirectory urpfile)</>(takeBaseName urpfile))|]
+  return $ UWExe u
+
+liftUrp m = m
+
+addHdr h = modify $ \s -> let u = urpst s in s { urpst = u { uhdr = (uhdr u) ++ [h] } }
+addMod m = modify $ \s -> let u = urpst s in s { urpst = u { umod = (umod u) ++ [m] } }
+
+database :: (MonadMake m) => String -> UrpGen m ()
+database dbs = addHdr $ UrpDatabase dbs
+  
+allow :: (MonadMake m) => UrpAllow -> String -> UrpGen m ()
+allow a s = addHdr $ UrpAllow a s
+
+rewrite :: (MonadMake m) => UrpRewrite -> String -> UrpGen m ()
+rewrite a s = addHdr $ UrpRewrite a s
+
+urpUp :: File -> FilePath
+urpUp f = F.joinPath $ map (const "..") $ filter (/= ".") $ F.splitDirectories $ F.takeDirectory $ toFilePath f
+
+newtype UrEmbed = Urembed File
+  deriving (Show)
+
+data UrpLibReference
+  = UrpLibStandalone File 
+  | UrpLibInternal UWLib
+  | UrpLibEmbed UrEmbed
+  deriving(Show)
+
+library' :: (MonadMake m) => File -> UrpGen m ()
+library' l = do
+  when ((takeExtension l) /= ".urp") $ do
+    fail "library declaration for %s should ends with '.urp'" (toFilePath l)
+  addHdr $ UrpLibrary l
+
+library :: (MonadMake m) => UrpLibReference -> UrpGen m ()
+library (UrpLibStandalone l) = do
+  library' l
+  when ((toFilePath $ takeDirectory l) /= ".") $ do
+    prebuild [cmd| $(make) -C $(takeDirectory l) |]
+library (UrpLibInternal (UWLib u)) = library' (urp u)
+library (UrpLibEmbed ue) = error "urembed is not defined"
+
+standalone f = UrpLibStandalone f
+internal u = UrpLibInternal u
+embed e = UrpLibEmbed e
+
+module_ :: (MonadMake m) => UrpModToken -> UrpGen m ()
+module_ = addMod
+
+pair f = UrpModule2 (f.="ur") (f.="urs")
+single f = UrpModule1 f
+sys s = UrpModuleSys s
+
+debug :: (MonadMake m) => UrpGen m ()
+debug = addHdr $ UrpDebug
+
+include :: (MonadMake m) => File -> UrpGen m ()
+include f = addHdr $ UrpInclude f
+
+link :: (MonadMake m) => File -> UrpGen m ()
+link f = addHdr $ UrpLink f
+
+ffi :: (MonadMake m) => File -> UrpGen m ()
+ffi s = addHdr $ UrpFFI s
+
+sql :: (MonadMake m) => File -> UrpGen m ()
+sql f = addHdr $ UrpSql f
+  
+jsFunc n s = addHdr $ UrpJSFunc n s
+
+safeGet s = addHdr $ UrpSafeGet s
+
+url = UrpUrl
+
+mime = UrpMime
+
+style = UrpStyle
+
+guessMime inf = fixup $ BS.unpack (defaultMimeLookup (fromString inf)) where
+  fixup "application/javascript" = "text/javascript"
+  fixup m = m
+
+data JSFunc = JSFunc {
+    urdecl :: String
+  , urname :: String
+  , jsname :: String
+  } deriving(Show)
+
+data JSType = JSType {
+    urtdecl :: String
+  } deriving(Show)
+
+-- | Parse the JavaScript file, extract top-level functions, convert their
+-- signatures into Ur/Web format, return them as the list of strings
+parse_js :: BS.ByteString -> Make (Either String ([JSType],[JSFunc]))
+parse_js contents = do
+  runErrorT $ do
+    c <- either fail return (parse (BS.unpack contents) "<urembed_input>")
+    f <- concat <$> (forM (findTopLevelFunctions c) $ \f@(fn:_) -> (do
+      ts <- mapM extractEmbeddedType (f`zip`(False:repeat True))
+      let urdecl_ = urs_line ts
+      let urname_ = (fst (head ts))
+      let jsname_ = fn
+      return [JSFunc urdecl_ urname_ jsname_]
+      ) `catchError` (\(e::String) -> do
+        err $ printf "ignoring function %s, reason:\n\t%s" fn e
+        return []))
+    t <- concat <$> (forM (findTopLevelVars c) $ \vn -> (do
+      (n,t) <- extractEmbeddedType (vn,False)
+      return [JSType $ printf "type %s" t]
+      )`catchError`  (\(e::String) -> do
+        err $ printf "ignoring variable %s, reason:\n\t%s" vn e
+        return []))
+    return (t,f)
+
+  where
+    urs_line :: [(String,String)] -> String
+    urs_line [] = error "wrong function signature"
+    urs_line ((n,nt):args) = printf "val %s : %s" n (fmtargs args) where
+      fmtargs :: [(String,String)] -> String
+      fmtargs ((an,at):as) = printf "%s -> %s" at (fmtargs as)
+      fmtargs [] = let pf = L.stripPrefix "pure_" nt in
+                   case pf of
+                     Just p -> p
+                     Nothing -> printf "transaction %s" nt
+
+    extractEmbeddedType :: (Monad m) => (String,Bool) -> m (String,String)
+    extractEmbeddedType ([],_) = error "BUG: empty identifier"
+    extractEmbeddedType (name,fallback) = check (msum [span2  "__" name , span2 "_as_" name]) where
+      check (Just (n,t)) = return (n,t)
+      check _ | fallback == True = return (name,name)
+              | fallback == False = fail $ printf "Can't extract the type from the identifier '%s'" name
+
+    findTopLevelFunctions :: JSNode -> [[String]]
+    findTopLevelFunctions top = map decls $ listify is_func top where
+      is_func n@(JSFunction a b c d e f) = True
+      is_func _ = False
+      decls (JSFunction a b c d e f) = (identifiers b) ++ (identifiers d)
+
+    findTopLevelVars :: JSNode -> [String]
+    findTopLevelVars top = map decls $ listify is_var top where
+      is_var n@(JSVarDecl a []) = True
+      is_var _ = False
+      decls (JSVarDecl a _) = (head $ identifiers a);
+      
+    identifiers x = map name $ listify ids x where
+      ids i@(JSIdentifier s) = True
+      ids _ = False
+      name (JSIdentifier n) = n
+
+    err,out :: (MonadIO m) => String -> m ()
+    err = hio stderr
+    out = hio stdout
+
+    span2 :: String -> String -> Maybe (String,String)
+    span2 inf s = span' [] s where
+      span' _ [] = Nothing
+      span' acc (c:cs)
+        | L.isPrefixOf inf (c:cs) = Just (acc, drop (length inf) (c:cs))
+        | otherwise = span' (acc++[c]) cs
+
+    hio :: (MonadIO m) => Handle -> String -> m ()
+    hio h = liftIO . hPutStrLn h
+
+bin :: (MonadIO m, MonadMake m) => File -> File -> UrpGen m ()
+bin dir src = do
+  c <- readFileForMake src
+  bin' dir (toFilePath src) c
+
+bin' :: (MonadIO m, MonadMake m) => File -> FilePath -> BS.ByteString -> UrpGen m ()
+bin' dir src_name src_contents = do
+
+  let mime = guessMime src_name
+  let mn = (mkname src_name)
+  let wrapmod ext = (dir </> mn) .= ext
+  let binmod ext = (dir </> (mn ++ "_c")) .= ext
+  let jsmod ext = (dir </> (mn ++ "_js")) .= ext
+
+  -- Binary module
+  let binfunc = printf "uw_%s_binary" (modname binmod)
+  let textfunc = printf "uw_%s_text" (modname binmod)
+  toFile (binmod ".c") $ do
+    line $ "/* Thanks, http://stupefydeveloper.blogspot.ru/2008/08/cc-embed-binary-data-into-elf.html */"
+    line $ "#include <urweb.h>"
+    line $ "#include <stdio.h>"
+    line $ printf "#define BLOBSZ %d" (BS.length src_contents)
+    line $ "static char blob[BLOBSZ];"
+    line $ "uw_Basis_blob " ++ binfunc ++ " (uw_context ctx, uw_unit unit)"
+    line $ "{"
+    line $ "  uw_Basis_blob uwblob;"
+    line $ "  uwblob.data = &blob[0];"
+    line $ "  uwblob.size = BLOBSZ;"
+    line $ "  return uwblob;"
+    line $ "}"
+    line $ ""
+    line $ "uw_Basis_string " ++ textfunc ++ " (uw_context ctx, uw_unit unit) {"
+    line $ "  char* data = &blob[0];"
+    line $ "  size_t size = sizeof(blob);"
+    line $ "  char * c = uw_malloc(ctx, size+1);"
+    line $ "  char * write = c;"
+    line $ "  int i;"
+    line $ "  for (i = 0; i < size; i++) {"
+    line $ "    *write =  data[i];"
+    line $ "    if (*write == '\\0')"
+    line $ "    *write = '\\n';"
+    line $ "    *write++;"
+    line $ "  }"
+    line $ "  *write=0;"
+    line $ "  return c;"
+    line $ "  }"
+    line $ ""
+
+  let append f wr = liftIO $ BS.appendFile f $ execWriter $ wr
+  append (toFilePath (binmod ".c")) $ do
+    let line s = tell ((BS.pack s)`mappend`(BS.pack "\n"))
+    line $ ""
+    line $ "static char blob[BLOBSZ] = {"
+    let buf = reverse $ BS.foldl (\a c -> (BS.pack (printf "0x%02X ," c)) : a) [] src_contents
+    tell (BS.concat buf)
+    line $ "};"
+    line $ ""
+
+  toFile (binmod ".h") $ do
+    line $ "#include <urweb.h>"
+    line $ "uw_Basis_blob " ++ binfunc ++ " (uw_context ctx, uw_unit unit);"
+    line $ "uw_Basis_string " ++ textfunc ++ " (uw_context ctx, uw_unit unit);"
+
+  toFile (binmod ".urs") $ do
+    line $ "val binary : unit -> transaction blob"
+    line $ "val text : unit -> transaction string"
+
+  include (binmod ".h")
+  link (binmod ".o")
+  ffi (binmod ".urs")
+
+  -- JavaScript FFI Module
+  (jstypes,jsdecls) <- if ((takeExtension src_name) == ".js") then do
+                          e <- liftMake $ parse_js src_contents
+                          case e of
+                            Left e -> do
+                              fail $ printf "Error while parsing %s" src_name
+                            Right decls -> do
+                              return decls
+                       else
+                          return ([],[])
+
+  toFile (jsmod ".urs") $ do
+    forM_ jstypes $ \decl -> line (urtdecl decl)
+    forM_ jsdecls $ \decl -> line (urdecl decl)
+  
+  -- Wrapper module
+  toFile (wrapmod ".urs") $ do
+    line $ "val binary : unit -> transaction blob"
+    line $ "val text : unit -> transaction string"
+    line $ "val blobpage : unit -> transaction page"
+    line $ "val geturl : url"
+    forM_ jstypes $ \decl -> line (urtdecl decl)
+    forM_ jsdecls $ \d -> line (urdecl d)
+
+  toFile (wrapmod ".ur") $ do
+    line $ "val binary = " ++ modname binmod ++ ".binary"
+    line $ "val text = " ++ modname binmod ++ ".text"
+    forM_ jsdecls $ \d ->
+      line $ printf "val %s = %s.%s" (urname d) (modname jsmod) (urname d)
+    line $ printf "fun blobpage {} = b <- binary () ; returnBlob b (blessMime \"%s\")" mime
+    line $ "val geturl = url(blobpage {})"
+
+  forM_ jsdecls $ \decl -> do
+    jsFunc (printf "%s.%s = %s" (modname jsmod) (urname decl)) (jsname decl)
+  ffi (jsmod ".urs")
+
+  safeGet $ printf "%s/blobpage" (modname wrapmod)
+  safeGet $ printf "%s/blob" (modname wrapmod)
+  module_ (pair $ wrapmod ".ur")
+
+  where
+
+    mkname :: FilePath -> String
+    mkname = upper1 . notnum . map under . takeFileName where
+      under c | c`elem`"_-. /" = '_'
+              | otherwise = c
+      upper1 [] = []
+      upper1 (x:xs) = (toUpper x) : xs
+      notnum n@(x:xs) | isDigit x = "f" ++ n
+                      | otherwise = n
+
+    modname :: (String -> File) -> String
+    modname f = upper1 . takeBaseName $ f ".urs" where
+      upper1 [] = []
+      upper1 (x:xs) = (toUpper x) : xs
+
diff --git a/src/Development/Cake3/Ext/UrWeb/UrEmbed.hs b/src/Development/Cake3/Ext/UrWeb/UrEmbed.hs
new file mode 100644
--- /dev/null
+++ b/src/Development/Cake3/Ext/UrWeb/UrEmbed.hs
@@ -0,0 +1,125 @@
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE RecursiveDo #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE ImpredicativeTypes #-}
+module Main where
+
+import Control.Monad.Error
+import Control.Monad.State
+import Control.Monad.Writer
+import Data.Either
+import Data.Generics
+import Data.Char
+import Data.List
+import Data.Data
+import Data.Typeable
+import Data.Maybe
+import Data.String
+-- import qualified Data.Text as T
+import Data.ByteString.Char8 (ByteString)
+import qualified Data.ByteString.Char8 as BS
+import Language.JavaScript.Parser
+import Options.Applicative
+import System.Environment
+import System.Process
+import System.Exit
+import System.IO
+-- import System.FilePath
+import System.Directory
+import System.Info
+import Text.Printf
+
+import Development.Cake3
+import Development.Cake3.Ext.UrWeb
+
+
+import Paths_cake3
+
+io :: (MonadIO m) => IO a -> m a
+io = liftIO
+
+hio :: (MonadIO m) => Handle -> String -> m ()
+hio h = io . hPutStrLn h
+
+err,out :: (MonadIO m) => String -> m ()
+err = hio stderr
+out = hio stdout
+
+data Args = A
+  { tgtdir :: FilePath
+  , fversion :: Bool
+  , dontrunmake :: Bool
+  , files :: [FilePath]
+  }
+
+pargs :: Parser Args
+pargs = A
+  <$> strOption
+      (  long "output"
+      <> short 'o'
+      <> metavar "FILE.urp"
+      <> help "Name of the Ur/Web project being generated"
+      <> value "")
+  <*> flag False True ( long "version" <> help "Show version information" )
+  <*> flag False True ( long "dont-run-make" <> help "Do not run the makefile generated" )
+  <*> arguments str ( metavar "FILE" <> help "File to embed" )
+
+main :: IO ()
+main = do
+  h <- (getDataFileName >=> readFile) "UrEmbedHelp.txt" 
+  main_ =<< execParser (
+    info (helper <*> pargs)
+      (  fullDesc
+      <> progDesc h
+      <> header "UrEmebed is the Ur/Web module generator" ))
+
+
+main_ (A tgturp True drm ins) = do
+  hPutStrLn stderr $ "urembed version " ++ (show version)
+
+main_ (A tgturp False drm ins) = do
+  let tgtdir = takeDirectory tgturp
+
+  when (null tgtdir) $ do
+    fail "An output directory should be specified, use -o"
+
+  when (null ins) $ do
+    fail "At least one file should be specified, see --help"
+
+  exists <- doesDirectoryExist tgtdir
+  when (not exists) $ do
+    fail $ "Couldn't create output file, no such directory " ++ show tgtdir
+
+  when (null tgtdir) $ do
+    fail "An output directory should be specified, use -o"
+
+  when (null ins) $ do
+    fail "At least one file should be specified, see --help"
+
+  exists <- doesDirectoryExist tgtdir
+  when (not exists) $ do
+    fail "Output is not a directory"
+
+  cntnts <- mapM BS.readFile ins
+
+  setCurrentDirectory tgtdir
+  loc <- currentDirLocation
+  let file = file' loc
+  let bin = bin' (file ".")
+
+  s <- runMake $ do
+    let urp = file (takeFileName tgturp)
+    u <- uwlib urp $ do
+      forM_ (ins`zip`cntnts) $ \(i,c) -> do
+        bin i c
+    rule $ do
+      phony "all"
+      depend u
+  
+  let mk = (("." </> takeFileName tgturp) .= "mk")
+  writeFile mk s
+
+  when (drm == False) $ do
+    system $ printf "make -f %s" mk
+    return ()
+
diff --git a/src/Development/Cake3/Monad.hs b/src/Development/Cake3/Monad.hs
--- a/src/Development/Cake3/Monad.hs
+++ b/src/Development/Cake3/Monad.hs
@@ -1,186 +1,409 @@
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE FunctionalDependencies #-}
+{-# LANGUAGE OverlappingInstances #-}
+{-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE DeriveDataTypeable #-}
 module Development.Cake3.Monad where
 
 import Control.Applicative
 import Control.Monad
 import Control.Monad.State
 import Control.Monad.Reader
+import Control.Monad.Trans
 import Control.Monad.Loc
+import Data.Data
+import Data.Typeable
 import Data.Monoid
 import Data.Maybe
 import qualified Data.Map as M
 import Data.Map(Map)
 import qualified Data.Set as S
 import Data.Set(Set)
-import Data.List as L
+import qualified Data.String as STR
+import Data.List as L hiding (foldl')
 import Data.Either
+import Data.Foldable (Foldable(..), foldl')
+import qualified Data.ByteString.Char8 as BS
 import qualified Data.Foldable as F
 import qualified Data.Traversable as F
+import qualified Data.Text as T
 import Development.Cake3.Types
 import qualified System.IO as IO
 import Text.Printf
-
-import System.FilePath.Wrapper
-
-type Recipe = Recipe1
+import Text.QuasiMake
 
-type Recipe1 = RecipeT (Map String (Set Variable))
+import Language.Haskell.TH.Quote
+import Language.Haskell.TH hiding(unsafe)
+import Language.Haskell.Meta (parseExp)
 
-type Recipe2 = RecipeT (Map String Variable)
+import System.FilePath.Wrapper
 
 
 type Location = String
 
-type Target = Set File
-
+-- | MakeState describes the state of the EDSL synthesizers during the
+-- the program execution.
 data MakeState = MS {
-    srecipes :: Map Target (Set Recipe1)
+    prebuilds :: Recipe
+    -- ^ Prebuild commands. targets/prerequsites of the recipe are ignored,
+    -- commands are executed before any target
+  , postbuilds :: Recipe
+    -- ^ Postbuild commands.
+  , recipes :: Set Recipe
+    -- ^ The set of recipes
   , sloc :: Location
+    -- ^ Current location. FIXME: fix or remove
   , makeDeps :: Set File
-  , placement :: [Target]
-  } deriving(Show)
+    -- ^ Set of files which the Makefile depends on
+  , placement :: [File]
+    -- ^ Placement list is the order of targets to be placed in the output file
+  , includes :: Set File
+    -- ^ Set of files to include in the output file (Makefile specific thing)
+  , errors :: String
+    -- ^ Errors found so far
+  , warnings :: String
+    -- ^ Warnings found so far
+  , outputFile :: File
+  }
 
-addPlacement :: RecipeT x -> Make ()
-addPlacement r = modify $ \ms -> ms { placement = (placement ms) ++ [rtgt r] }
+-- Oh, such a boilerplate
+initialMakeState mf = MS defr defr mempty mempty mempty mempty mempty mempty mempty mf where
+  defr = emptyRecipe "<internal>"
 
-modifyRecipes f = modify $ \ms -> ms { srecipes = f (srecipes ms) }
+getPlacementPos :: Make Int
+getPlacementPos = L.length <$> placement <$> get
 
-applyPlacement :: (Eq x) => Map Target (RecipeT x) -> [Target] -> [RecipeT x]
-applyPlacement rs p = nub $ (mapMaybe id $ map (flip M.lookup rs) p) ++ (map snd $ M.toList rs)
+addPlacement :: Int -> File -> Make ()
+addPlacement pos r = modify $ \ms -> ms { placement = r`insertInto`(placement ms) } where
+  insertInto x xs = let (h,t) = splitAt pos xs in h ++ (x:t)
 
 addMakeDep :: File -> Make ()
 addMakeDep f = modify (\ms -> ms { makeDeps = S.insert f (makeDeps ms) })
 
-defMS = MS mempty mempty mempty mempty
+-- | Add prebuild command
+prebuild, postbuild :: (MonadMake m) => CommandGen -> m ()
+prebuild cmdg = liftMake $ do
+  s <- get
+  pb <- fst <$> runA' (prebuilds s) (shell cmdg)
+  put s { prebuilds = pb }
+postbuild cmdg = liftMake $ do
+  s <- get
+  pb <- fst <$> runA' (postbuilds s) (shell cmdg)
+  put s { postbuilds = pb }
 
--- | The File Alias records the file which may be referenced from other rules,
--- it's "Brothers", and the recipes required to build this file.
-newtype Alias = Alias (File, [File], Make Recipe)
+-- | Find recipes without targets
+checkForEmptyTarget :: (Foldable f) => f Recipe -> String
+checkForEmptyTarget rs = foldl' checker mempty rs where
+  checker es r | S.null (rtgt r) = es++e
+               | otherwise = es where
+    e = printf "Error: Recipe without targets\n\t%s\n" (show r)
 
--- unalias :: [Alias] -> Make ()
--- unalias as = F.sequence_ $ map (\(Alias (_,_,x)) -> x) as
+-- | Find recipes sharing a target
+checkForTargetConflicts :: (Foldable f) => f Recipe -> String
+checkForTargetConflicts rs = foldl' checker mempty (groupRecipes rs) where
+  checker es rs | S.size rs > 1 = es++e
+                | otherwise = es where
+    e = printf "Error: Recipes share one or more targets\n\t%s\n" (show rs)
 
-newtype Make a = Make { unMake :: (StateT MakeState IO) a }
-  deriving(Monad, Functor, Applicative, MonadState MakeState, MonadIO, MonadFix)
 
-makefileT :: (FileLike x) => (FileT x)
-makefileT= fromFilePath "Makefile"
+-- | A Monad providing access to MakeState. TODO: not mention IO here.
+class (Monad m) => MonadMake m where
+  liftMake :: (Make' IO) a -> m a
 
-addRebuildDeps :: Set File -> Map Target Recipe2 -> Map Target Recipe2
-addRebuildDeps md rs = M.map mkd rs where
-  mkd r | makefileT `S.member` (rtgt r) = r{ rsrc = ((rsrc r) `mappend` md) }
-        | otherwise = r
+newtype Make' m a = Make { unMake :: (StateT MakeState m) a }
+  deriving(Monad, Functor, Applicative, MonadState MakeState, MonadIO, MonadFix)
 
-addMakeDeps :: Map Target Recipe2 -> Map Target Recipe2
-addMakeDeps rs
-  | M.null makeRules = rs
-  | otherwise = M.map addMakeDeps' rs
-  where
-    makeRules = M.filter (\r -> makefileT `S.member` (rtgt r)) rs
-    addMakeDeps' r | not (makefileT `dependsOn` r) = r{ rsrc = (S.insert makefileT (rsrc r)) }
-                   | otherwise = r
-    dependsOn :: File -> Recipe2 -> Bool
-    dependsOn f r = if f`S.member`(rtgt r) then True else godeeper where
-      godeeper = or $ map (\tgt -> or $ map (dependsOn f) (selectBySrc tgt)) (S.toList $ rtgt r)
+type Make a = Make' IO a
 
-    selectBySrc f = map snd . M.toList . fst $ M.partition (\r -> f`S.member`(rsrc r)) rs
+instance MonadMake (Make' IO) where
+  liftMake = id
 
-flattern :: (Ord x, Ord y, Show y) => Map x (Set y) -> Either String (Map x y)
-flattern m = mapM check1 (M.toList m) >>= \m -> return (M.fromList m) where
-  check1 (k,s) = do
-    case S.size s of
-      1 -> return (k, S.findMin s)
-      _ -> fail $ printf "More than 1 value describes single entity: %s" (show s)
+instance (MonadMake m) => MonadMake (A' m) where
+  liftMake m = A' (lift (liftMake m))
 
-flattern' :: (Ord x, Ord y, Show y) => Map x (Set y) -> Map x y
-flattern' m = M.map check1 m where
-  check1 s = do
-    case S.size s of
-      1 -> S.findMin s
-      _ -> error $ printf "More than 1 value describes single entity: %s" (show s)
+instance (MonadMake m) => MonadMake (StateT s m) where
+  liftMake = lift . liftMake
 
-check :: Map Target (Set Recipe1) -> Either String (Map String Variable, Map Target Recipe2)
-check rs1 = do
-  rs1' <- flattern rs1
-  let vs = F.foldr (\b a -> M.unionWith mappend a (rvars b)) mempty rs1'
-  vs' <- flattern vs
-  let rs2 = M.map (\(Recipe a b c d e f) -> let d' = flattern' d in (Recipe a b c d' e f)) rs1'
-  return (vs',rs2)
 
--- forMapM :: (Monad m, Ord x, Ord a) => Map x a -> (a -> m b) -> m (Map x b)
--- forMapM s f = F.foldrM (\a b -> f a >>= \a -> return $ M.insert a b) S.empty s
-
-evalMake :: Make () -> IO (Either String (Map String Variable, Map Target Recipe2, [Target]))
-evalMake mk = do
-  flip evalStateT defMS $ unMake $ mk >> do
-    md <- makeDeps <$> get
-    rs <- srecipes <$> get
-    p <- placement <$> get
-    return $ case check rs of
-      Left err -> Left err
-      Right (v,r) -> Right (v, addMakeDeps $ addRebuildDeps md $ r, p)
+-- | Returns a MakeState
+evalMake :: (Monad m) => File -> Make' m a -> m MakeState
+evalMake mf mk = do
+  ms <- flip execStateT (initialMakeState mf) (unMake mk)
+  return ms {
+    errors = checkForEmptyTarget (recipes ms) ++ checkForTargetConflicts (recipes ms)
+  }
 
 modifyLoc f = modify $ \ms -> ms { sloc = f (sloc ms) }
 
-addRecipe :: Recipe1 -> Make ()
+addRecipe :: Recipe -> Make ()
 addRecipe r = modify $ \ms -> 
-  let rs = srecipes ms ; k = rtgt r
-  in ms { srecipes = (M.unionWith mappend (M.singleton k (S.singleton r)) rs) }
+  let rs = recipes ms ; k = rtgt r
+  in ms { recipes = (S.insert r (recipes ms)) }
 
 getLoc :: Make String
 getLoc = sloc <$> get
 
-instance MonadLoc Make where
+-- | Add 'include ...' directive to the final Makefile for each input file.
+includeMakefile :: (Foldable t) => t File -> Make ()
+includeMakefile fs = foldl' scan (return ()) fs where
+  scan a f = do
+    modify $ \ms -> ms {includes = S.insert f (includes ms)}
+    return ()
+
+instance (Monad m) => MonadLoc (Make' m) where
   withLoc l' (Make um) = Make $ do
     modifyLoc (\l -> l') >> um
 
-newtype A a = A { unA :: StateT Recipe1 Make a }
-  deriving(Monad, Functor, Applicative, MonadState Recipe1, MonadIO,MonadFix)
+-- | 'A' here stands for Action. It is a State monad carrying a Recipe as its
+-- state.  Various monadic actions add targets, prerequisites and shell commands
+-- to this recipe. After that, @rule@ function records it to the @MakeState@.
+-- After the recording, no modification is allowed for this recipe.
+newtype A' m a = A' { unA' :: StateT Recipe m a }
+  deriving(Monad, Functor, Applicative, MonadState Recipe, MonadIO,MonadFix)
 
-addVariable :: Variable -> A ()
-addVariable v = modify $ \r -> r { rvars = M.insertWith mappend (vname v) (S.singleton v) (rvars r) }
+-- | Verison of Action monad with fixed parents
+type A a = A' (Make' IO) a
 
-targets :: A (Set File)
+-- | A class of monads providing access to the underlying A monad
+class (Monad m, Monad t) => MonadAction t m | t -> m where
+  liftAction :: A' m x -> t x
+
+instance (Monad m) => MonadAction (A' m) m where
+  liftAction = id
+
+-- | Run the Action monad, using already existing Recipe as input.
+runA' :: (Monad m) => Recipe -> A' m a -> m (Recipe, a)
+runA' r act = do
+  (a,r) <- runStateT (unA' act) r
+  return (r,a)
+
+-- | Create new empty recipe and run action on it.
+runA :: (Monad m)
+  => String -- ^ Location string (in the Cakefile.hs)
+  -> A' m a -- ^ Recipe builder
+  -> m (Recipe, a)
+runA loc act = runA' (emptyRecipe loc) act
+
+-- | Version of runA discarding the result of A's computation
+runA_ :: (Monad m) => String -> A' m a -> m Recipe
+runA_ loc act = runA loc act >>= return .fst
+
+-- | Get a list of targets added so far
+targets :: (Applicative m, Monad m) => A' m (Set File)
 targets = rtgt <$> get
 
-prerequisites :: A (Set File)
+-- | Get a list of prerequisites added so far
+prerequisites :: (Applicative m, Monad m) => A' m (Set File)
 prerequisites = rsrc <$> get
 
-runA :: Recipe1 -> A a -> Make Recipe1
-runA r a = do
-  r' <- snd <$> runStateT (unA a) r
-  addRecipe r'
-  return r'
+-- | Mark the recipe as 'PHONY' i.e. claim that all it's targets are not real
+-- files. Makefile-specific.
+markPhony :: (Monad m) => A' m ()
+markPhony = modify $ \r -> r { rflags = S.insert Phony (rflags r) }
 
-readFile :: File -> A String
-readFile f = do
-  A (lift $ addMakeDep f)
-  liftIO (IO.readFile (unpack f))
+-- | Mark the recipe as 'INTERMEDIATE' i.e. claim that all it's targets may be
+-- removed after the build process. Makefile-specific.
+markIntermediate :: (Monad m) => A' m ()
+markIntermediate = modify $ \r -> r { rflags = S.insert Intermediate (rflags r) }
 
-class Placable a where
-  place :: a -> Make ()
+-- | Obtain the contents of a File. Note, that this generally means, that
+-- Makefile should be regenerated each time the File is changed.
+readFileForMake :: (MonadMake m)
+  => File -- ^ File to read contents of
+  -> m BS.ByteString
+readFileForMake f = liftMake (addMakeDep f >> liftIO (BS.readFile (toFilePath f)))
 
--- instance Placable (Make ()) where
---   place = id
+-- | CommandGen is a recipe-builder packed in the newtype to prevent partial
+-- expantion of it's commands
+newtype CommandGen' m = CommandGen' { unCommand :: A' m Command }
+type CommandGen = CommandGen' (Make' IO)
 
-instance Placable Alias where
-  place (Alias (_,_,x)) = do
-    x >>= addPlacement
+-- | Pack the command builder into a CommandGen
+commandGen :: A Command -> CommandGen
+commandGen mcmd = CommandGen' mcmd
 
-instance Placable (Alias,Alias) where
-  place (a,b) = place a >> place b
+-- | Modifie the recipe builder: ignore all the dependencies
+ignoreDepends :: (Monad m) => A' m a -> A' m a
+ignoreDepends action = do
+  r <- get
+  a <- action
+  modify $ \r' -> r' { rsrc = rsrc r, rvars = rvars r }
+  return a
 
-instance Placable (Alias,Alias,Alias) where
-  place (a,b,c) = place a >> place b >> place c
+-- | Apply the recipe builder to the current recipe state. Return the list of
+-- targets of the current @Recipe@ under construction
+shell :: (Monad m)
+  => CommandGen' m -- ^ Command builder as returned by cmd quasi-quoter
+  -> A' m [File]
+shell cmdg = do
+  line <- unCommand cmdg
+  commands [line]
+  r <- get
+  return (S.toList (rtgt r))
 
-instance Placable (Alias,Alias,Alias,Alias) where
-  place (a,b,c,d) = place a >> place b >> place c >> place d
+-- | Version of @shell@ which doesn't track it's dependencies
+unsafeShell :: (Monad m) => CommandGen' m -> A' m [File]
+unsafeShell cmdg = ignoreDepends (shell cmdg)
 
-instance Placable x => Placable (Make x) where
-  place mk = mk >>= place
+-- | Simple wrapper for strings, a target for various typeclass instances.
+newtype CakeString = CakeString String
+  deriving(Show,Eq,Ord)
 
-instance Placable x => Placable [x] where
-  place xs = sequence_ (map place xs)
+-- | An alias to CakeString constructor
+string :: String -> CakeString
+string = CakeString
+
+-- | Class of things which may be referenced using '\@(expr)' syntax of the
+-- quasi-quoted shell expressions.
+class (Monad m) => RefOutput m x where
+  -- | Register the output item, return it's shell-command representation. Files
+  -- are rendered using space protection quotation, variables are wrapped into
+  -- $(VAR) syntax, item lists are converted into space-separated lists.
+  refOutput :: x -> A' m Command
+
+instance (Monad m) => RefOutput m File where
+  refOutput f = do
+    modify $ \r -> r { rtgt = f `S.insert` (rtgt r)}
+    return_file f
+
+-- FIXME: inbetween will not notice if spaces are already exists
+inbetween x mx = (concat`liftM`mx) >>= \l -> return (inbetween' x l) where
+  inbetween' x [] = []
+  inbetween' x [a] = [a]
+  inbetween' x (a:as) = a:x:(inbetween' x as)
+
+spacify l = (CmdStr " ") `inbetween` l
+
+instance (Monad m) => RefOutput m [File] where
+  refOutput xs = spacify $ mapM refOutput (xs)
+
+instance (Monad m) => RefOutput m (Set File) where
+  refOutput xs = refOutput (S.toList xs)
+
+instance (RefOutput m x) => RefOutput m (Maybe x) where
+  refOutput mx = case mx of
+    Nothing -> return mempty
+    Just x -> refOutput x
+
+-- | Class of things which may be referenced using '\$(expr)' from inside
+-- of quasy-quoted shell expressions
+class (MonadAction a m) => RefInput a m x where
+  -- | Register the input item, return it's shell-script representation
+  refInput :: x -> a Command
+
+instance (MonadAction a m) => RefInput a m File where
+  refInput f = liftAction $ do
+    modify $ \r -> r { rsrc = f `S.insert` (rsrc r)}
+    return_file f
+
+instance (MonadAction a m) => RefInput a m Recipe where
+  refInput r = refInput (rtgt r)
+
+instance (RefInput a m x) => RefInput a m [x] where
+  refInput xs = spacify $ mapM refInput xs
+
+instance (MonadAction a m) => RefInput a m (Set File) where
+  refInput xs = refInput (S.toList xs)
+
+instance (MonadIO a, RefInput a m x) => RefInput a m (IO x) where
+  refInput mx = liftIO mx >>= refInput
+
+instance (MonadAction a m, MonadMake a) => RefInput a m (Make Recipe) where
+  refInput mr = liftMake mr >>= refInput
+
+instance (RefInput a m x, MonadMake a) => RefInput a m (Make x) where
+  refInput mx = liftMake mx >>= refInput
+
+instance (RefInput a m x) => RefInput a m (Maybe x) where
+  refInput mx = case mx of
+    Nothing -> return mempty
+    Just x -> refInput x
+
+instance (MonadAction a m) => RefInput a m Variable where
+  refInput v@(Variable n _) = liftAction $ do
+    variables [v]
+    return_text $ printf "$(%s)" n
+
+instance (MonadAction a m) => RefInput a m CakeString where
+  refInput v@(CakeString s) = do
+    return_text s
+
+-- | Add it's argument to the list of dependencies (prerequsites) of a current
+-- recipe under construction
+depend :: (RefInput a m x)
+  => x -- ^ File or [File] or (Set File) or other form of dependency.
+  -> a ()
+depend x = refInput x >> return ()
+
+-- | Declare that current recipe produces some producable item.
+produce :: (RefOutput m x)
+  => x -- ^ File or [File] or other form of target.
+  -> A' m ()
+produce x = refOutput x >> return ()
+
+-- | Add variables to the list of variables referenced by the current recipe
+variables :: (Foldable t, Monad m)
+  => (t Variable) -- ^ A set of variables to depend the recipe on
+  -> A' m ()
+variables vs = modify (\r -> r { rvars = foldl' (\a v -> S.insert v a) (rvars r) vs } )
+
+-- | Add commands to the list of commands of a current recipe under
+-- construction. Warning: this function behaves like unsafeShell i.e. it doesn't
+-- analyze the command text
+commands :: (Monad m) => [Command] -> A' m ()
+commands cmds = modify (\r -> r { rcmd = (rcmd r) ++ cmds } )
+
+-- | Set the recipe's location in the Cakefile.hs
+location :: (Monad m) => String -> A' m ()
+location l  = modify (\r -> r { rloc = l } )
+
+-- | Set additional flags
+flags :: (Monad m) => Set Flag -> A' m ()
+flags f = modify (\r -> r { rflags = (rflags r) `mappend` f } )
+
+-- | Has effect of a function @QQ -> CommandGen@ where QQ is a string supporting
+-- the following syntax:
+--
+-- * $(expr) evaluates to expr and adds it to the list of dependencies (prerequsites)
+--
+-- * \@(expr) evaluates to expr and adds it to the list of targets
+--
+-- * $$ and \@\@ evaluates to $ and \@
+--
+-- /Example/
+--
+-- > [cmd|gcc $flags -o @file|]
+--
+-- is equivalent to
+--
+-- >   return $ CommandGen $ do
+-- >     s1 <- refInput "gcc "
+-- >     s2 <- refInput (flags :: Variable)
+-- >     s3 <- refInput " -o "
+-- >     s4 <- refOutput (file :: File)
+-- >     return (s1 ++ s2 ++ s3 ++ s4)
+--
+-- Later, this command may be examined or passed to the shell function to apply
+-- it to the recipe
+--
+cmd :: QuasiQuoter
+cmd = QuasiQuoter
+  { quotePat  = undefined
+  , quoteType = undefined
+  , quoteDec  = undefined
+  , quoteExp = \s -> appE [| \x -> CommandGen' x |] (qqact s)
+  } where
+    qqact s = 
+      let chunks = flip map (getChunks (STR.fromString s)) $ \c ->
+                     case c of
+                       T t -> let t' = T.unpack t in [| return_text t' |]
+                       E c t -> case parseExp (T.unpack t) of
+                                  Left  e -> error e
+                                  Right e -> case c of
+                                    '$' -> appE [| refInput |] (return e)
+                                    '@' -> appE [| refOutput |] (return e)
+      in appE [| \l -> L.concat <$> (sequence l) |] (listE chunks)
 
diff --git a/src/Development/Cake3/Rules/UrWeb.hs b/src/Development/Cake3/Rules/UrWeb.hs
deleted file mode 100644
--- a/src/Development/Cake3/Rules/UrWeb.hs
+++ /dev/null
@@ -1,146 +0,0 @@
-{-# LANGUAGE QuasiQuotes #-}
-{-# LANGUAGE FlexibleContexts #-}
-module Development.Cake3.Rules.UrWeb(
-    urweb
-  , Config(..)
-  , defaultConfig
-  , urdeps
-  ) where
-
-import Data.Monoid
-import Data.List
-import Data.Set (member)
-import Control.Applicative
-import Control.Monad.Trans
-import Control.Monad.State
-import System.Directory
-import System.IO as IO
-
-import System.FilePath.Wrapper
-import Development.Cake3
-import Development.Cake3.Types
-import Development.Cake3.Monad as C3
-
-splitWhen c l = (h,if null t then [] else tail t) where
-  (h,t) = span (/=c) l
-
--- | URP file parser. Takes file name and 2 callbacks: one for header lines and
--- one for source lines
-urpparse :: (Monad m) => String -> ((String,String) -> m ()) -> (FilePath -> m ()) -> m ()
-urpparse inp hact sact = do
-  parseline False (lines inp) where
-    parseline _ [] = return ()
-    parseline False (l:ls)
-      | (unwords (words l)) == [] = parseline True ls
-      | otherwise = hact (splitWhen ' ' l) >> parseline False ls
-    parseline True (l:ls) = sact l >> parseline True ls
-
-data Config = Config {
-    urObjRule :: File -> Rule
-  , urInclude :: Variable
-  , urEmbed :: [(String, [File])]
-  }
-
-defaultConfig = Config {
-    urObjRule = \f -> rule f (fail "urobj: not set")
-  , urInclude = makevar "UR_INCLUDE_DIR" "/usr/local/include/urweb"
-  , urEmbed = []
-  }
-
--- | Helper function, parses dependencies of an *urp
-urdeps :: Config -> File -> A ()
-urdeps cfg f = do
-  ps <- prerequisites
-  let check = msum [ lookup (unpack (takeBaseName f)) (urEmbed cfg)
-                   , lookup (unpack (takeFileName f)) (urEmbed cfg)
-                   ]
-  case check of
-    Just embeddable -> do
-      depend (urembed cfg f embeddable)
-    Nothing -> do
-      depend f
-      inp <- C3.readFile f
-      urpparse inp lib src
-  where
-    relative x = takeDirectory f </> (fromFilePath x)
-
-    lib (h,x)
-      | (h=="library") = do
-        let nested = relative x
-        isdir <- liftIO $ doesDirectoryExist (unpack nested)
-        case isdir of
-          True -> urdeps cfg (nested </> (fromFilePath "lib.urp"))
-          False -> urdeps cfg (nested .= "urp")
-      | (h=="ffi") = do
-        depend ((relative x) .= "urs")
-      | (h=="include") = do
-        depend (relative x)
-      | (h=="link") = do
-        depend (urObjRule cfg (relative x))
-      | otherwise = return ()
-
-    src d@(c:_)
-      | (c/='$') = do
-        depend ((relative d) .= "ur" :: File)
-        let urs = (relative d) .= "urs"
-        e <- liftIO $ doesFileExist (toFilePath urs)
-        when e $ do
-          depend ((relative d) .= "urs" :: File)
-      | otherwise = return ()
-    src _ = return ()
-
--- | Search for @sect@ in the urp file's header.
-urpline :: String -> File -> String -> IO File
-urpline sect f c = flip execStateT mempty $ urpparse c lib (const $ return ()) where
-  relative x = takeDirectory f </> (fromFilePath x)
-  lib (n,x) | n == sect = put (relative x)
-            | otherwise = return ()
-
--- | Search for section @sect@ in the urp file's database line
--- FIXME: actually supports only 'databse dbname=XXX' format
-urpdb :: String -> File -> String -> IO File
-urpdb dbsect f c = flip execStateT mempty $ urpparse c lib (const $ return ()) where
-  relative x = takeDirectory f </> (fromFilePath x)
-  lib (n,x) | n == "database" = put (relative $ snd (splitWhen '=' x))
-            | otherwise = return ()
-
--- | Get executable name of an URP project
-urpexe :: File -> File
-urpexe f = f .= "exe"
-
--- | Take the URP file and the build action. Provide three aliases: one for
--- executable, one for SQL-file and one for database file
---
--- FIXME: Rewrite in urembed style: fill urweb_cmd and pass it back to the user
-urweb :: Config -> File -> A() -> IO (Alias, Alias, Alias)
-urweb cfg f act = do
-  c <- liftIO (IO.readFile $ unpack f)
-  exe <- return (urpexe f)
-  sql <- urpline "sql" f c
-  db <- urpdb "name" f c
-  ruleM (exe,sql,db) (act >> urdeps cfg f)
-
--- | Generate Ur/Web project file @urp@ providing embedded files @files@
--- FIXME: Generate unique variable name instead of URGCC
--- FIXME: implement variable dependency
-urembed :: Config -> File -> [File] -> Alias
-urembed cfg urp files =
-  let
-    dir = takeDirectory urp
-    makefile = (dir </> takeBaseName urp) .= "mk"
-    dir_ = string dir
-    urp_ = string urp
-    mf_ = string (takeFileName makefile)
-  in rule urp $ do
-    let urcc = makevar "URCC" "$(shell urweb -print-ccompiler)"
-    let gcc = makevar "CC" "$(shell $(URCC) -print-prog-name=gcc)"
-    let ld = makevar "LD" "$(shell $(URCC) -print-prog-name=ld)"
-    let mf = rule makefile $ do
-              -- FIXME: strange recursion occures if uncomment
-              -- depend urp
-              shell [cmd|mkdir -pv $(dir_)|]
-              shell [cmd|urembed -o $(urp_) $files|]
-    depend urcc
-    depend mf
-    shell [cmd|$(extvar "MAKE") -C $(dir_) -f $(mf_) CC=$gcc LD=$ld UR_INCLUDE_DIR=$(urInclude cfg) urp|]
-
diff --git a/src/Development/Cake3/Types.hs b/src/Development/Cake3/Types.hs
--- a/src/Development/Cake3/Types.hs
+++ b/src/Development/Cake3/Types.hs
@@ -1,8 +1,14 @@
+{-# LANGUAGE DeriveDataTypeable #-}
 module Development.Cake3.Types where
 
+import Control.Applicative
+import Data.Maybe
 import Data.Monoid
+import Data.Data
+import Data.Typeable
+import Data.Foldable (Foldable(..), foldl')
 import qualified Data.List as L
-import Data.List hiding(foldr)
+import Data.List hiding(foldr, foldl')
 import qualified Data.Map as M
 import Data.Map (Map)
 import qualified Data.Set as S
@@ -10,56 +16,117 @@
 
 import System.FilePath.Wrapper
 
--- | Item wich have it's position in the Makefile. Positioned adds the metric to
--- the contained datatype. Note, that the metric is not the subject of Eq or
--- Ord. mappend-ing two metrics results in taking the minimal one.
--- data Pos a = Pos { ppos :: Int, pwhat :: a }
---   deriving(Show, Eq)
-
--- instance Ord a => Ord (Pos a) where
---   compare (Pos p w) (Pos p2 w2) =
---     case p`compare`p2 of
---       EQ -> w`compare`w2
---       x -> x
-
--- instance Monoid a => Monoid (Pos a) where
---   mempty = Pos 0 mempty
---   mappend (Pos a ad) (Pos b bd) = Pos (min a b) (mappend ad bd)
-
--- -- higher positions go first
--- cmpPos (Pos a _) (Pos b _) = a`compare`b
--- unposition (Pos _ x) = x
-
-
--- | Makefile variable
+-- | The representation of Makefile variable
 data Variable = Variable {
     vname :: String
+  -- ^ The name of a variable
   , vval :: Maybe String
   -- ^ Nothing means that variable is defined elsewhere (eg. borrowed from the
   -- environment)
-  } deriving(Show, Eq, Ord)
+  } deriving(Show, Eq, Ord, Data, Typeable)
 
-type Vars = Map String (Set Variable)
+-- type Vars = Map String (Set Variable)
 
 -- | Command represents OS command line and consists of a list of fragments.
 -- Each fragment is either text (may contain spaces) or FilePath (spaces should
 -- be escaped)
-type Command = [Either String File]
+type Command = [CommandPiece]
 
-return_text x = return [Left x]
-return_file x = return [Right x]
+data CommandPiece = CmdStr String | CmdFile File
+  deriving (Show, Eq, Ord, Data, Typeable)
 
--- | Recipe answers to the question 'How to build the targets'
-data RecipeT v = Recipe {
+return_text x = return [CmdStr x]
+return_file f = return [CmdFile f]
+
+data Flag = Phony | Intermediate
+  deriving(Show,Eq,Ord, Data, Typeable)
+
+-- | Recipe answers to the question 'How to build the targets'. Internally, it
+-- contains sets of targets and prerequisites, as well as shell commands
+-- required to build former from latter
+data Recipe = Recipe {
     rtgt :: Set File
   -- ^ Targets 
   , rsrc :: Set File
   -- ^ Prerequisites
   , rcmd :: [Command]
   -- ^ A list of shell commands
-  , rvars :: v
+  , rvars :: Set Variable
   -- ^ Container of variables
   , rloc :: String
-  -- FIXME: actually, PHONY is a file's attribute, not recipe's
-  , rphony :: Bool
-  } deriving(Show, Eq, Ord)
+  , rflags :: Set Flag
+  } deriving(Show, Eq, Ord, Data, Typeable)
+
+emptyRecipe :: String -> Recipe
+emptyRecipe loc = Recipe mempty mempty mempty mempty loc mempty
+
+addPrerequisites :: Set File -> Recipe -> Recipe
+addPrerequisites p r = r { rsrc = p`mappend`(rsrc r)}
+
+addPrerequisite :: File -> Recipe -> Recipe
+addPrerequisite f = addPrerequisites (S.singleton f)
+
+type Target = Set File
+
+groupSet :: (Ord k, Ord x, Foldable t) => (x -> Set k) -> t x -> Map k (Set x)
+groupSet keys s = foldl' f' mempty s where
+  f' m x = foldl' ins m (keys x) where
+    ins m k = M.insertWith mappend k (S.singleton x) m
+
+groupRecipes ::  (Foldable t) => t Recipe -> Map File (Set Recipe)
+groupRecipes = groupSet rtgt
+
+flattern :: [Set x] -> [x]
+flattern = concat . map S.toList
+
+applyPlacement' :: (Eq x) => [File] -> Map File x  -> [x]
+applyPlacement' pl m = 
+  let placed = nub $ catMaybes $ L.map (\k -> M.lookup k m) pl
+      all = L.map snd $ M.toList m
+  in placed ++ (all \\ placed)
+
+applyPlacement :: (Foldable t) => [File] -> t Recipe  -> [Recipe]
+applyPlacement pl rs = flattern $ applyPlacement' pl (groupRecipes rs)
+
+-- applyPlacement2 :: (Foldable t) => [File] -> t File  -> [File]
+-- applyPlacement2 pl fs = flattern $ applyPlacement' pl (groupSet S.singleton fs)
+    
+
+transformRecipes :: (Applicative m) => (Recipe -> m (Set Recipe)) -> Set Recipe -> m (Set Recipe)
+transformRecipes f m = S.foldl' f' (pure mempty) m where
+  f' a r = mappend <$> (f r) <*> a
+
+transformRecipesM_ :: (Monad m, Foldable t) => (Recipe -> m ()) -> t Recipe -> m ()
+transformRecipesM_ f rs = foldl' (\a r -> a >> f r) (return mempty) rs
+
+queryVariables :: (Foldable t) => t Recipe -> Set Variable
+queryVariables rs = foldl' (\a r -> a`mappend`(rvars r)) mempty rs
+
+queryVariablesE :: (Foldable t) => t Recipe -> Either String (Set Variable)
+queryVariablesE rs = check where
+  vs = queryVariables rs
+  bads = M.filter (\s -> (S.size s) /= 1) (groupSet (\v -> S.singleton (vname v)) vs)
+  check | (M.size bads) > 0 = Left "Some variables share same name"
+        | otherwise = Right vs
+
+queryTargets :: (Foldable t) => t Recipe -> Set File
+queryTargets rs = foldl' (\a r -> a`mappend`(rtgt r)) mempty rs
+
+var :: String -> Maybe String -> Variable
+var n v = Variable n v
+
+-- | Declare the variable which is defined in the current Makefile and has it's
+-- default value
+makevar
+  :: String -- ^ Variable name
+  -> String -- ^ Default value
+  -> Variable
+makevar n v = var n (Just v)
+
+-- | Declare the variable which is not defined in the target Makefile
+extvar :: String -> Variable
+extvar n = var n Nothing
+
+-- | Special variable @$(MAKE)@
+make = extvar "MAKE"
+
diff --git a/src/Development/Cake3/Utils/Find.hs b/src/Development/Cake3/Utils/Find.hs
--- a/src/Development/Cake3/Utils/Find.hs
+++ b/src/Development/Cake3/Utils/Find.hs
@@ -22,7 +22,7 @@
 -- FIXME: Figure out how to add ./relative notation (./file instead of file)
 -- Makefile : contents_of(directory)
 getDirectoryContentsRecursive :: (MonadIO m) => File -> m [File]
-getDirectoryContentsRecursive (FileT topdir) = map (fromFilePath . (topdir</>)) `liftM` (liftIO $ recurseDirectories [""])
+getDirectoryContentsRecursive td@(FileT topdir) = map (td</>) `liftM` (liftIO $ recurseDirectories [""])
   where
     recurseDirectories :: [FilePath] -> IO [FilePath]
     recurseDirectories []         = return []
@@ -45,3 +45,4 @@
         ignore ['.']      = True
         ignore ['.', '.'] = True
         ignore _          = False
+ 
diff --git a/src/Development/Cake3/Writer.hs b/src/Development/Cake3/Writer.hs
--- a/src/Development/Cake3/Writer.hs
+++ b/src/Development/Cake3/Writer.hs
@@ -1,12 +1,20 @@
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE FlexibleInstances #-}
-module Development.Cake3.Writer (toMake) where
+module Development.Cake3.Writer (defaultMakefile,buildMake) where
 
-import Control.Monad (when)
 import Control.Applicative
-import Control.Monad.State (State(..), execState, runState, modify, get, put)
+import Control.Monad (when)
+import Control.Monad.Identity (runIdentity)
+import Control.Monad.State (MonadState, StateT(..), runStateT, State(..), execState, evalState, runState, modify, get, put)
+import Control.Monad.Trans
 import Data.List as L
 import Data.Char
+import Data.Maybe (catMaybes)
+import Data.Monoid
 import Data.String
+import Data.Foldable (Foldable(..), foldl')
 import Data.Foldable (forM_)
 import Data.Traversable (forM)
 import Data.Set (Set)
@@ -19,65 +27,257 @@
 import Development.Cake3.Types
 import Development.Cake3.Monad
 
-gen :: MakeWriter Int
-gen = do
-  x <- head <$> cnt <$> get
-  modify (\ws -> ws { cnt = tail (cnt ws) })
-  return x
-
--- | Writer state
-data WS = WS { cnt :: [Int] , ls :: String }
-  deriving(Show)
-
-type MakeWriter a = State WS a
-
 class ToMakeText x where
   toMakeText :: x -> String
   
 instance ToMakeText [Char] where
   toMakeText = id
 
-instance ToMakeText [[Char]] where
-  toMakeText = concat . map toMakeText
-
--- instance ToMakeText (Set File) where
---   toMakeText = concat . map toMakeText . S.toList
+escapeFile f = escapeFile' (toFilePath f)
+escapeFile' [] = []
+escapeFile' (' ':xs) = "\\ " ++ escapeFile' xs
+escapeFile' (x:xs) = (x:(escapeFile' xs))
 
 instance ToMakeText File where
-  toMakeText (FileT f) = escape f where
-    escape [] = []
-    escape (' ':xs) = "\\ " ++ escape xs
-    escape (x:xs) = (x:(escape xs))
-
-trimE = dropWhileEnd isSpace
-trimB = dropWhile isSpace
-
-cs a b = a ++ (' ':b)
+  toMakeText = escapeFile
 
 instance ToMakeText Command where
-  toMakeText [x] = either toMakeText toMakeText x
-  toMakeText ((Left str):(Right f):cmd) = toMakeText ((Left ((trimE str)`cs` (toMakeText f))):cmd)
-  toMakeText ((Right f):(Left str):cmd) = toMakeText ((Left ((toMakeText f)`cs`(trimB str))):cmd)
-  toMakeText ((Right a):(Right b):cmd) = toMakeText ((Left ((toMakeText a)`cs`(toMakeText b))):cmd)
-  toMakeText ((Left s1):(Left s2):cmd) = toMakeText ((Left (s1++s2)):cmd)
+  toMakeText cmd = concat $ map toMakeText cmd
 
-line :: String -> MakeWriter ()
-line s = modify $ \ws -> ws { ls = concat [ls ws, s, "\n"] }
+instance ToMakeText CommandPiece where
+  toMakeText (CmdStr s) = s
+  toMakeText (CmdFile f) = toMakeText f
 
-mmap f = map (f . snd) . M.toList
+instance ToMakeText (Set File) where
+  toMakeText s = intercalate " " (map toMakeText (S.toList s))
+ 
 smap f = map f . S.toList
 
-toMake :: (Map String Variable, Map Target Recipe2, [Target]) -> String
-toMake (vs_, rs_, p) = 
-  let (vs,rs) = (map snd $ M.toList vs_, applyPlacement rs_ p)
-  in ls $ flip execState (WS [1..] "") $ do
-  line "# This Makefile was generated by the ThirdCake"
-  line "# https://github.com/grwlf/cake3"
-  line ""
+line :: (MonadState String m) => String -> m ()
+line s = modify $ \ws -> concat [ws, s, "\n"]
 
-  when (not (null vs)) $ do
+text :: (MonadState String m) => String -> m ()
+text s = modify $ \ws -> concat [ws, s]
+
+newtype MakeLL a = MakeLL { unMakeLL :: State ([File], Set Recipe) a }
+  deriving(Functor, Monad, MonadState ([File], Set Recipe), Applicative)
+
+fresh :: MakeLL File
+fresh = do
+  (f:fs,rs) <- get
+  put (fs,rs)
+  return f
+
+runMakeLL :: String -> MakeLL () -> Set Recipe
+runMakeLL templ m = snd $ execState (unMakeLL m) (names, S.empty) where
+  names = map fromFilePath $ map (\x -> printf ".%s%d" templ x) $ ([1..] :: [Int])
+
+produceLL :: Recipe -> MakeLL ()
+produceLL r = modify (\(a,b) -> (a,S.insert r b))
+
+ruleLL :: A' MakeLL a -> MakeLL Recipe
+ruleLL act = do
+  (r,_) <- runA "<internal>" act
+  produceLL r
+  return r
+
+applySubprojects :: Map File [Command] -> Set Recipe -> Set Recipe
+applySubprojects sp rs = runMakeLL "subproject" (transformRecipesM_ f rs) where
+  f r | L.null scmds = produceLL r
+      | otherwise = do
+          n1 <- fresh
+          n2 <- fresh
+          r1 <- ruleLL $ do
+            produce n2
+            commands scmds
+            markPhony
+          r2 <- ruleLL $ do
+            produce n1
+            depend (rsrc r)
+            commands (rcmd r)
+          ruleLL $ do
+            produce (rtgt r)
+            depend r1
+            depend r2
+          return ()
+      where
+        scmds :: [Command]
+        scmds = concat $ catMaybes $ map (flip M.lookup sp) (S.toList $ rsrc r)
+
+
+-- | Turns multi-target rules of the form
+--
+--    a b c : d e f
+--      cmd1
+--
+-- into the pair:
+--
+--    a : stampX
+--    b : stampX
+--    c : stampX
+--
+--    .INTERMEDIATE:stampX
+--    stampX : d e f
+--      cmd1
+--
+fixMultiTarget :: (Foldable t) => t Recipe -> Set Recipe
+fixMultiTarget rs = runMakeLL "fix-multy" (transformRecipesM_ f rs) where
+  f r | (S.size (rtgt r)) > 1 = do
+          s <- fresh
+          forM_ (S.toList $ rtgt r) $ \t -> do
+            ruleLL $ do
+              produce t
+              location (rloc r)
+              depend s
+              flags (rflags r)
+          ruleLL $ do
+            produce s
+            depend (rsrc r)
+            variables (rvars r)
+            commands (rcmd r)
+            location (rloc r)
+            markIntermediate
+          return ()
+      | otherwise = do
+          produceLL r
+
+-- | Operate on a prerequisites which themselfs are targets of a multitarget rule. Make
+-- the conversion from:
+--
+--    a b c : x
+--    x : a
+--    y : b
+--
+-- to
+--
+--    a b c : x
+--    x : a b c
+--    y : a b c
+--
+--
+completeMultiTarget :: Set Recipe -> Set Recipe
+completeMultiTarget rs = 
+  let
+    badlist = S.foldl' (\ts r -> do
+      if (S.size (rtgt r)) > 1 then (rtgt r):ts else ts) [] rs
+  in
+    flip S.map rs $ \r ->
+      L.foldl' (\r mulpack ->
+        case (not . S.null) ((rsrc r)`S.intersection` mulpack) of
+          True -> r { rsrc = (rsrc r) `S.union` mulpack }
+          False -> r) r badlist
+
+-- | Rule referring to the 
+defaultMakefile :: File
+defaultMakefile = fromFilePath ("." </> "Makefile")
+
+addRebuildDeps :: File -> Set File -> Set Recipe -> Set Recipe
+addRebuildDeps makefile deps rs = S.map mkd rs where
+  mkd r | makefile `S.member` (rtgt r) = addPrerequisites deps r
+        | otherwise = r
+
+isRequiredFor :: Set Recipe -> Recipe -> File -> Bool
+isRequiredFor rs r f = if f`S.member`(rtgt r) then True else godeeper where
+  godeeper = or $ map (\tgt -> or $ map (\r -> isRequiredFor rs r f) (selectBySrc tgt)) (S.toList $ rtgt r)
+  selectBySrc f = S.toList . fst $ S.partition (\r -> f`S.member`(rsrc r)) rs
+
+-- | There are only 2 kind of rules: 1) ones that depend on a Makefile, and 2) ones
+-- that Makefile depends on. Case-2 is known in advance (for example, when the
+-- the contents of a file is required to build a Makefile then Makefile depends
+-- on this file). This function adds the case-1 dependencies.
+addMakeDeps :: File -> Set Recipe -> Set Recipe
+addMakeDeps makefile rs
+  | S.null (S.filter (\r -> makefile `S.member` (rtgt r)) rs) = rs
+  | otherwise = S.map addMakeDeps_ rs
+  where
+    addMakeDeps_ r | not (isRequiredFor rs r makefile) = addPrerequisite makefile r
+                   | otherwise = r
+
+
+
+-- | Render the Makefile. Return either the content (Right), or error messages
+-- (Left).
+buildMake :: MakeState -> Either String String
+buildMake ms = do
+
+  mr <- region "MAIN" $ do
+    line ""
+    line "# Main section"
+    line ""
+    writeRules rs'
+    forM_ (includes ms) $ \i -> do
+      line (printf "include %s" (toMakeText i))
+    line ""
+
+  sr <- region "SERVICE" $ do
+    line ""
+    line "# Prebuild/postbuild section"
+    line ""
+    r <- runA_ "<internal>" $ do
+      produce (queryTargets (recipes ms))
+      commands (rcmd $ prebuilds ms)
+      commands [[CmdStr "$(MAKE) MAIN=1 $(MAKECMDGOALS)"]]
+      commands (rcmd $ postbuilds ms)
+      variables (rvars $ prebuilds ms)
+      variables (rvars $ postbuilds ms)
+      markPhony
+    writeRules $ applyPlacement (placement ms) $ fixMultiTarget [r]
+    line ""
+
+  hdr <- runLines $ do
+    line "# This Makefile was generated by the Cake3"
+    line "# https://github.com/grwlf/cake3"
+    line ""
     line "GUARD = .GUARD_$(1)_$(shell echo $($(1)) | md5sum | cut -d ' ' -f 1)"
+    line ""
+    
+  writeRegions hdr [mr,sr]
 
+  where
+    make = extvar "MAKE"
+    makecmdgoals = extvar "MAKECMDGOALS"
+
+    rs' = applyPlacement (placement ms)
+        $ fixMultiTarget
+        $ completeMultiTarget
+        $ addMakeDeps (outputFile ms)
+        $ addRebuildDeps (outputFile ms) (makeDeps ms)
+        $ recipes ms
+
+data MakeRegion = MR {
+    mrname :: String
+  , mrtext :: String
+}
+
+type Lines = StateT String (Either String) ()
+
+runLines :: Lines -> Either String String
+runLines s = let e = runStateT s "" in
+  case e of
+    Left e -> Left e
+    Right ((),st) -> Right st
+
+writeRegions :: String -> [MakeRegion] -> Either String String
+writeRegions hdr rs = mappend <$> (pure hdr) <*> (writeRegions' rs) where
+  writeRegions' [] = fail "No regions are defined"
+  writeRegions' (r:rs)
+    | L.null rs = return (mrtext r)
+    | otherwise = do
+      inner <- writeRegions' rs
+      runLines $ do
+        line (printf "ifdef %s" (map toUpper $ mrname r))
+        text (mrtext r)
+        line "else"
+        text inner
+        line "endif"
+
+region name mlines = do
+  lines <- runLines mlines
+  return $ MR { mrname = name , mrtext = lines}
+
+writeRules rs = do
+  vs <- lift $ queryVariablesE rs
+
   -- Variables
   forM_ vs $ \v -> case v of
     (Variable n (Just v)) -> line (printf "%s = %s" n v)
@@ -86,30 +286,19 @@
   -- Rules
   forM_ rs $ \r -> do
     let varguard v = printf "$(call GUARD,%s)" (vname v)
-    let deps = intercalate " " $ (smap toMakeText (rsrc r)) ++ (mmap varguard (rvars r))
+    let deps = intercalate " " $ (smap toMakeText (rsrc r)) ++ (smap varguard (rvars r))
     let tgts = intercalate " " $ (smap toMakeText (rtgt r))
 
-    when (rphony r) $ do
+    when (Phony `S.member` (rflags r)) $ do
       line (printf ".PHONY: %s" tgts)
+    when (Intermediate `S.member` (rflags r)) $ do
+      line (printf ".INTERMEDIATE: %s" tgts)
 
-    case (S.size (rtgt r)) of
-      0 -> do
-        return ()
-      1 -> do
-        let s = (S.findMin (rtgt r))
-        line $ printf "%s: %s" (toMakeText s) deps
-        forM_ (rcmd r) $ \c -> do
-          line (printf "\t%s" (toMakeText c))
-      _ -> do
-        i <- gen
-        let s = (printf "stamp%d" i :: String)
-        line (printf "%s: %s" tgts s)
-        line (printf ".INTERMEDIATE: %s" s)
-        line (printf "%s: %s" s deps)
-        forM_ (rcmd r) $ \c -> do
-          line (printf "\t%s" (toMakeText c))
+    line (printf "%s: %s" tgts deps)
+    forM_ (rcmd r) $ \c -> do
+      line (printf "\t%s" (toMakeText c))
 
-  -- Rules for variable's guards
+  -- Rules for variable guards
   -- FIXME: add those on the higher level
   forM_ vs $ \v -> do
     line (printf "$(call GUARD,%s):" (vname v))
diff --git a/src/System/FilePath/Wrapper.hs b/src/System/FilePath/Wrapper.hs
--- a/src/System/FilePath/Wrapper.hs
+++ b/src/System/FilePath/Wrapper.hs
@@ -1,8 +1,11 @@
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE TypeSynonymInstances #-}
 {-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE DeriveDataTypeable #-}
 module System.FilePath.Wrapper where
 
+import Data.Data
+import Data.Typeable
 import Control.Applicative
 import Control.Monad.State
 import Control.Monad.Trans
@@ -12,37 +15,47 @@
 import Text.Printf
 
 newtype FileT a = FileT a
-  deriving(Show,Eq,Ord)
+  deriving(Show,Eq,Ord,Data,Typeable)
 
+-- | Simple wrapper for FilePath.
+type File = FileT FilePath
+
+-- | Convert File back to FilePath
+toFilePath :: (FileT FilePath) -> FilePath
+toFilePath (FileT f) = f
+
+fromFilePath :: FilePath -> FileT FilePath
+fromFilePath f = FileT f
+
 instance (Monoid a) => Monoid (FileT a) where
   mempty = FileT mempty
   mappend (FileT a) (FileT b) = FileT (a`mappend`b)
 
-type File = FileT FilePath
-
 class FileLike a where
-  fromFilePath :: FilePath -> a
-  combine :: a -> a -> a
-  takeBaseName :: a -> a
-  takeFileName :: a -> a
+  -- fromFilePath :: FilePath -> a
+  combine :: a -> String -> a
+  takeDirectory :: a -> a
+  takeBaseName :: a -> String
+  takeFileName :: a -> String
   makeRelative :: a -> a -> a
   replaceExtension :: a -> String -> a
   takeExtension :: a -> String
   takeExtensions :: a -> String
-  takeDirectory :: a -> a
   dropExtensions :: a -> a
 
-(</>) :: (FileLike a) => a -> a -> a
+-- | Redefine standard @</>@ operator to work with Files
+(</>) :: (FileLike a) => a -> String -> a
 (</>) = combine
 
+-- | Alias for replaceExtension
 (.=) :: (FileLike a) => a -> String -> a
 (.=) = replaceExtension
 
 instance FileLike a => FileLike (FileT a) where
-  fromFilePath fp = FileT (fromFilePath fp)
-  combine (FileT a) (FileT b) = FileT (combine a b)
-  takeBaseName (FileT a) = FileT (takeBaseName a)
-  takeFileName (FileT a) = FileT (takeFileName a)
+  -- fromFilePath fp = FileT (fromFilePath fp)
+  combine (FileT a) b = FileT (combine a b)
+  takeBaseName (FileT a) = takeBaseName a
+  takeFileName (FileT a) = takeFileName a
   takeExtension (FileT a) = takeExtension a
   takeExtensions (FileT a) = takeExtensions a
   makeRelative (FileT a) (FileT b) = FileT (makeRelative a b)
@@ -51,7 +64,7 @@
   dropExtensions (FileT a) = FileT (dropExtensions a)
 
 instance FileLike FilePath where
-  fromFilePath = id
+  -- fromFilePath = id
   combine = F.combine
   takeBaseName = F.takeBaseName
   takeFileName = F.takeFileName
@@ -61,9 +74,4 @@
   takeExtension = F.takeExtension
   takeExtensions = F.takeExtensions
   dropExtensions = F.dropExtensions
-
-unpack :: (FileT FilePath) -> FilePath
-unpack (FileT f) = f
-
-toFilePath = unpack
 
diff --git a/src/Text/QuasiMake.hs b/src/Text/QuasiMake.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/QuasiMake.hs
@@ -0,0 +1,100 @@
+{-# LANGUAGE ExistentialQuantification, TemplateHaskell, QuasiQuotes, OverloadedStrings, FlexibleInstances, UndecidableInstances, IncoherentInstances #-}
+
+-- | QuasyString-like module. Tweaked for the cake3
+module Text.QuasiMake (Chunk (..), getChunks) where
+import Language.Haskell.TH.Quote
+import Language.Haskell.TH.Syntax
+import Language.Haskell.TH
+import Language.Haskell.Meta (parseExp)
+
+import Data.Attoparsec.Text
+import qualified Data.Text as T 
+import Data.Text (Text)
+import Data.Char
+
+import Data.Monoid
+import Control.Applicative
+                        
+instance Lift Text where
+    lift = litE . stringL . T.unpack
+
+-- | Chunk is a part of quasy-quotation
+data Chunk 
+    = T Text
+    -- ^ the text
+    | E Char Text
+    -- ^ \$(expr) or \@(expr)
+  deriving (Show, Eq)
+
+class Textish a where
+    toText :: a -> Text
+
+instance Textish Text where
+    {-# INLINE toText #-}
+    toText x = x
+
+instance Textish [Char] where
+    {-# INLINE toText #-}
+    toText x = T.pack x
+
+instance Show a => Textish a where 
+    {-# INLINE toText #-}
+    toText x = T.pack (show x)
+
+
+-- | A simple 'QuasiQuoter' to interpolate 'Text' into other pieces of 'Text'.
+-- Expressions can be embedded using \$(expr) or \@(expr), and values can be
+-- interpolated with $name. Inside \$( )s, if you have a string of ambiguous
+-- type, it will default to the Show instance for toText, which will escape
+-- unicode characters in the string, and add quotes around them.
+getChunks :: Text -> [Chunk]
+getChunks i = case parseOnly parser (T.strip i) of
+        Right m -> m
+        _       -> error "Unclosed parenthesis."
+
+  where
+    parenthesis '(' = True
+    parenthesis ')' = True
+    parenthesis _   = False
+
+    parseExpression :: Int -> Parser [Text]
+    parseExpression level = do
+        expr  <- takeTill parenthesis
+        paren <- anyChar
+        case paren of
+            ')' | level <= 0 -> return [expr]
+                | otherwise  -> do
+                    next <- parseExpression (level - 1)
+                    return ([expr, ")"] ++ next)
+
+            '(' -> do
+                next <- parseExpression (level + 1)
+                return ([expr, "("] ++ next)
+
+            _ -> return [expr, T.singleton paren]
+
+    parser :: Parser [Chunk]
+    parser = fmap concat $ flip manyTill endOfInput $ do
+        text <- takeTill (\c -> elem c "@$")
+        end  <- atEnd
+        if end
+            then return [T text]
+            else do
+                delim <- anyChar
+                next <- anyChar
+                case next of
+                    -- opening an experssion
+                    '(' -> do
+                        expr <- T.concat <$> parseExpression 0
+                        return [T text, E delim expr]
+                    c | c == delim -> do
+                        -- escaped '$', '@' or '%' 
+                        return [T (text <> T.singleton delim)]
+
+                      | otherwise -> do
+                        -- value
+                        name <- takeTill (\c -> not (isAlphaNum c || c == '_') )
+                        return [T text, E delim (T.cons next name)]
+                        
+
+
diff --git a/src/UrEmbedHelp.txt b/src/UrEmbedHelp.txt
new file mode 100644
--- /dev/null
+++ b/src/UrEmbedHelp.txt
@@ -0,0 +1,28 @@
+
+  Converts a set of FILEs into the Ur/Web modules. Each Module will contain
+  following functions:
+
+    val binary : unit -> transaction blob
+    val blobpage : unit -> transaction page
+    val text : unit -> transaction string
+
+    Additionally, FFI signatures will be provided for JavaScript files. In order
+    to enable this, you have to name your JS functions using the name__type
+    scheme. See README for details. Also, uru project uses this a lot.
+
+    (NOTE: the interface is not stable. Pleas, fork the Urembed sources
+    before using)
+
+  The master project (specified with -o FILE.urp) will contain a dictionary
+  version of those functions taking the datatype key as an argument, instead of
+  unit. In order to actually compile the binaries, you have to call
+
+    make -f FILE.mk CC=.. LD=.. UR_INCLUDE_DIR=.. urp
+
+  Where FILE.mk is the Makefile, generated by urembed.
+
+    Example: urembed -o static/Static.urp Style.css Script.js
+             make -C static -f Static.mk CC=gcc LD=ld UR_INCLUDE_DIR=/usr/local/include/urweb urp
+
+  Note: output directory should exist
+
