packages feed

cake3 0.5.0.0 → 0.5.1.0

raw patch · 7 files changed

+284/−137 lines, 7 filesdep +parsec

Dependencies added: parsec

Files

CHANGELOG view
@@ -1,3 +1,7 @@+Version 0.5.1.0+---------------+Fix urembed tool, change UrWeb.bin API, converts url's from the CSS to Ur/Web format+ Version 0.4 ----------- Autobuild 'clean' rule. genFile and genTmpFile functions allowing to embed text
cake3.cabal view
@@ -2,7 +2,7 @@ -- see http://haskell.org/cabal/users-guide/  name:                cake3-version:             0.5.0.0+version:             0.5.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.@@ -75,7 +75,7 @@                      filepath, containers, text, monadloc, mtl,                      bytestring, deepseq, system-filepath, text-format,                      directory, attoparsec, mime-types,-                     language-javascript, syb+                     language-javascript, syb, parsec    hs-source-dirs:    src @@ -92,6 +92,6 @@                      filepath, syb, optparse-applicative, directory,                      text, bytestring, containers, language-javascript,                      haskell-src-meta, template-haskell, attoparsec, monadloc,-                     mime-types+                     mime-types, parsec   other-modules:     Paths_cake3 
src/Development/Cake3.hs view
@@ -43,6 +43,7 @@   , readFileForMake   , genFile   , genTmpFile+  , genTmpFileWithPrefix    -- Make parts   , prerequisites@@ -186,6 +187,8 @@     quote_dollar (c:cs) = c : (quote_dollar cs)  genTmpFile :: (MonadMake m) => String -> m File-genTmpFile cnt = tmpFile >>= \f -> genFile f cnt+genTmpFile cnt = tmpFile [] >>= \f -> genFile f cnt +genTmpFileWithPrefix :: (MonadMake m) => String -> String -> m File+genTmpFileWithPrefix pfx cnt = tmpFile pfx >>= \f -> genFile f cnt 
src/Development/Cake3/Ext/UrWeb.hs view
@@ -5,6 +5,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE LambdaCase #-} module Development.Cake3.Ext.UrWeb where  import Data.Data@@ -23,21 +24,26 @@ 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 Language.JavaScript.Parser as JS import Network.Mime (defaultMimeLookup)-import System.Directory import Text.Printf++import Text.Parsec as P hiding (string)+import Text.Parsec.Token as P hiding(lexeme, symbol)+import qualified Text.Parsec.Token as P+import Text.Parsec.ByteString as P+ import qualified System.FilePath as F+import System.Directory import System.IO as IO  import System.FilePath.Wrapper import Development.Cake3.Monad-import Development.Cake3+import Development.Cake3 hiding (many, (<|>))  data UrpAllow = UrpMime | UrpUrl | UrpResponseHeader | UrpEnvVar | UrpHeader   deriving(Show,Data,Typeable)@@ -144,9 +150,10 @@  data UrpState = UrpState {     urpst :: Urp+  , urautogen :: File   } deriving (Show) -defState urp = UrpState (Urp urp Nothing [] [])+defState urp = UrpState (Urp urp Nothing [] []) (fromFilePath "autogen")  class ToUrpWord a where   toUrpWord :: a -> String@@ -195,9 +202,12 @@ newtype UrpGen m a = UrpGen { unUrpGen :: StateT UrpState m a }   deriving(Functor, Applicative, Monad, MonadState UrpState, MonadMake, MonadIO) -toFile f wr = liftIO $ writeFile (toFilePath f) $ execWriter $ wr+toFile f' wr = liftIO $ do+  let f = toFilePath f'+  createDirectoryIfMissing True (takeDirectory f)+  writeFile f $ execWriter $ wr -toTmpFile wr = genTmpFile $ execWriter $ wr+toTmpFile pfx wr = genTmpFileWithPrefix pfx $ execWriter $ wr  line :: (MonadWriter String m) => String -> m () line s = tell (s++"\n")@@ -220,7 +230,7 @@         ".c" -> shell [cmd| $cc -c $i $incfl $(string flags) -o @(c .= "o") $(c) |]         e -> error ("Unknown C-source extension " ++ e) -  inp_in <- toTmpFile $ do+  inp_in <- toTmpFile (takeFileName (urpfile .= "in")) $ do       forM hdr (line . toUrpLine (urpUp urpfile))       line ""       forM mod (line . toUrpLine (urpUp urpfile))@@ -251,7 +261,7 @@     unsafeShell [cmd|urweb $(string opts) $((takeDirectory urpfile)</>(takeBaseName urpfile))|]   return $ UWExe u -liftUrp m = m+setAutogenDir d = modify $ \s -> s { urautogen = d }  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] } }@@ -285,6 +295,16 @@   return [urp u]  -- | Build a file using external Makefile facility.+externalMake3 ::+     File -- ^ External Makefile+  -> File -- ^ External file to refer to+  -> String -- ^ The name of the target to run+  -> Make [File]+externalMake3 mk f tgt = do+  prebuild [cmd|$(make) -C $(string $ toFilePath $ takeDirectory mk) -f $(string $ takeFileName mk) $(string tgt) |]+  return [f]++-- | Build a file using external Makefile facility. externalMake' ::      File -- ^ External Makefile   -> File -- ^ External file to refer to@@ -294,13 +314,21 @@   return [f]  -- | Build a file from external project. It is expected, that this project has a--- 'Makwfile' in it's root directory+-- 'Makwfile' in it's root directory. Call Makefile with the default target externalMake ::      File -- ^ File from the external project to build   -> Make [File]-externalMake f = externalMake' (takeDirectory f </> "Makefile") f+externalMake f = externalMake3 (takeDirectory f </> "Makefile") f ""  -- | Build a file from external project. It is expected, that this project has a+-- 'Makwfile' in it's root directory+externalMakeTarget ::+     File -- ^ File from the external project to build+  -> String+  -> Make [File]+externalMakeTarget f tgt = externalMake3 (takeDirectory f </> "Makefile") f tgt++-- | Build a file from external project. It is expected, that this project has a -- fiel.mk (a Makefile with an unusual name) in it's root directory externalMake2 :: File -> Make [File] externalMake2 f = externalMake' ((takeDirectory f </> takeFileName f) .= "mk") f@@ -362,6 +390,8 @@  hdr = UrpHeader +requestHeader = UrpHeader+ responseHeader = UrpResponseHeader  script :: (MonadMake m) => String -> UrpGen m ()@@ -374,104 +404,53 @@ pkgconfig :: (MonadMake m) => String -> UrpGen m () pkgconfig l = addHdr $ UrpPkgConfig l -data JSFunc = JSFunc {-    urdecl :: String -- ^ URS declaration for this function-  , urname :: String -- ^ UrWeb name of this function-  , jsname :: String -- ^ JavaScript name of this function-  } deriving(Show) -data JSType = JSType {-    urtdecl :: String-  } deriving(Show)+type BinOptions = [ BinOption ]+data BinOption = NoScan | UseUrembed deriving(Show, Eq) --- | 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 -> BinOptions -> UrpGen m ()+bin src bo = do+  let ds = if NoScan `elem` bo then "--dont-scan" else ""+  case UseUrembed `elem` bo of+    False -> do+      c <- readFileForMake src+      bin' (toFilePath src) c bo+    True -> do+      a <- urautogen `liftM` get+      library' $ do+        rule $ shell [cmd|urembed -o @(a </> (takeFileName src .="urp")) $(string ds) $src|] -bin' :: (MonadIO m, MonadMake m) => File -> FilePath -> BS.ByteString -> UrpGen m ()-bin' dir src_name src_contents = do+bin' :: (MonadIO m, MonadMake m) => FilePath -> BS.ByteString -> BinOptions -> UrpGen m ()+bin' src_name src_contents' bo = do -  liftIO $ createDirectoryIfMissing True (toFilePath dir)+  dir <- urautogen `liftM` get -  let mime = guessMime src_name+  let mm = 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 +  (src_contents, nurls) <-+    if not (NoScan `elem` bo) then+    if ((takeExtension src_name) == ".css") then do+        (e,urls) <- return $ runWriter $ parse_css src_contents' $ \x -> do+          let (url, query) = span (\c -> not $ elem c "?#") x+          let mn = modname (const (fromFilePath $ mkname url))+          tell [ mn ]+          return $ "/" ++ mn ++ "/blobpage" ++ query+        case e of+          Left e -> do+            fail $ printf "Error while parsing css %s: %s" src_name (show e)+          Right b -> do+            return (b, L.nub urls)+    else+      return (src_contents', [])+    else+      return (src_contents', [])+   -- Binary module   let binfunc = printf "uw_%s_binary" (modname binmod)   let textfunc = printf "uw_%s_text" (modname binmod)@@ -498,7 +477,7 @@     line $ "  for (i = 0; i < size; i++) {"     line $ "    *write =  data[i];"     line $ "    if (*write == '\\0')"-    line $ "    *write = '\\n';"+    line $ "      *write = '\\n';"     line $ "    *write++;"     line $ "  }"     line $ "  *write=0;"@@ -530,19 +509,26 @@   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 ([],[])+  (jstypes,jsdecls) <- +    if not (NoScan `elem` bo) then+    if ((takeExtension src_name) == ".js") then do+      e <- liftMake $ parse_js src_contents+      case e of+        Left e -> do+          fail $ printf "Error while parsing javascript %s: %s" src_name e+        Right decls -> do+          return decls+    else+      return ([],[])+    else+      return ([],[]) +  forM_ jsdecls $ \decl -> do+    addHdr $ UrpJSFunc (modname jsmod) (urname decl) (jsname decl)   toFile (jsmod ".urs") $ do     forM_ jstypes $ \decl -> line (urtdecl decl)     forM_ jsdecls $ \decl -> line (urdecl decl)+  ffi (jsmod ".urs")      -- Wrapper module   toFile (wrapmod ".urs") $ do@@ -552,21 +538,24 @@     line $ "val geturl : url"     forM_ jstypes $ \decl -> line (urtdecl decl)     forM_ jsdecls $ \d -> line (urdecl d)+    line $ "val propagated_urls : list url"    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 $ printf "fun blobpage {} = b <- binary () ; returnBlob b (blessMime \"%s\")" mm     line $ "val geturl = url(blobpage {})"--  forM_ jsdecls $ \decl -> do-    addHdr $ UrpJSFunc (modname jsmod) (urname decl) (jsname decl)-  ffi (jsmod ".urs")+    line $ "val propagated_urls = "+    forM_ nurls $ \u -> do+      line $ "    " ++ u ++ ".geturl ::"+    line $ "    []" +  allow mime mm   safeGet (wrapmod ".ur") "blobpage"   safeGet (wrapmod ".ur") "blob"+   module_ (pair $ wrapmod ".ur")    where@@ -584,4 +573,157 @@     modname f = upper1 . takeBaseName $ f ".urs" where       upper1 [] = []       upper1 (x:xs) = (toUpper x) : xs++{-+ - Content parsing helpers+ -}++data JSFunc = JSFunc {+    urdecl :: String -- ^ URS declaration for this function+  , urname :: String -- ^ UrWeb name of this function+  , jsname :: String -- ^ JavaScript name of this function+  } 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 (JS.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+++transform_css :: (Stream s m Char) => ParsecT s u m [Either ByteString [Char]]+transform_css = do+  l1 <- map Left <$> blabla+  l2 <- map Right <$> funs+  e <- try (eof >> return True) <|> (return False)+  case e of+    True -> return (l1++l2)+    False -> do+      l <- transform_css+      return (l1 ++ l2 ++ l)++  where++    symbol = P.symbol l+    lexeme = P.lexeme l++    string  = lexeme (+      between (char '\'') (char '\'') (strchars '\'') <|>+      between (char '"') (char '"') (strchars '"')) <|>+      manyTill anyChar (try (char ')'))+      where+        strchars e = many $ satisfy (/=e)++    fun1 = lexeme $ do+      symbol "url"+      symbol "("+      s <- string+      symbol ")"+      return s++    blabla = do+      l <- manyTill anyChar (eof <|> (try (lookAhead fun1) >> return ()))+      case null l of+        True -> return []+        False -> return [BS.pack l]++    funs = many (try fun1)++    l =  P.makeTokenParser $ P.LanguageDef+        { P.commentStart	 = "/*"+        , P.commentEnd	 = "*/"+        , P.commentLine	 = "//"+        , P.nestedComments = True+        , P.identStart	 = P.letter+        , P.identLetter	 = P.alphaNum <|> oneOf "_@-"+        , P.reservedNames   = []+        , P.reservedOpNames = []+        , P.caseSensitive  = False+        , P.opStart        = l+        , P.opLetter       = l+        }+        where l = oneOf ":!#$%&*+./<=>?@\\^|-~"++parse_css :: (Monad m) => BS.ByteString -> (String -> m String) -> m (Either P.ParseError BS.ByteString)+parse_css inp f = do+  case P.runParser transform_css () "-" inp of+    Left e -> return $ Left e+    Right pr -> do+      b <- forM pr $ \i -> do+        case i of+          Left bs -> return bs+          Right u -> do+            u' <- f u+            return (BS.pack $ "url('" ++ u' ++ "')")+      return $ Right $ BS.concat b 
src/Development/Cake3/Ext/UrWeb/UrEmbed.hs view
@@ -49,6 +49,7 @@   { tgtdir :: FilePath   , fversion :: Bool   , dontrunmake :: Bool+  , dontscan :: Bool   , files :: [FilePath]   } @@ -62,6 +63,7 @@       <> value "")   <*> flag False True ( long "version" <> help "Show version information" )   <*> flag False True ( long "dont-run-make" <> help "Do not run the makefile generated" )+  <*> flag False True ( long "dont-scan" <> help "Do not generate JS/CSS FFI declarations" )   <*> arguments str ( metavar "FILE" <> help "File to embed" )  main :: IO ()@@ -74,10 +76,10 @@       <> header "UrEmebed is the Ur/Web module generator" ))  -main_ (A tgturp True drm ins) = do+main_ (A tgturp True drm ds ins) = do   hPutStrLn stderr $ "urembed version " ++ (show version) -main_ (A tgturp False drm ins) = do+main_ (A tgturp False drm ds ins) = do   let tgtdir = takeDirectory tgturp    when (null tgtdir) $ do@@ -86,30 +88,28 @@   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-  liftIO $ createDirectoryIfMissing True 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" -  cntnts <- mapM BS.readFile ins -  setCurrentDirectory tgtdir+  -- Create target directory earlier+  createDirectoryIfMissing True tgtdir+  -- setCurrentDirectory tgtdir+   loc <- currentDirLocation   let file = file' loc-  let bin = bin' (file ".") -  let mk = (("." </> takeFileName tgturp) .= "mk")+  let mk = (tgturp .= "mk")   writeMake (file mk) $ do-    let urp = file (takeFileName tgturp)+    let urp = file tgturp     u <- uwlib urp $ do-      forM_ (ins`zip`cntnts) $ \(i,c) -> do-        bin i c+      -- setAutogenDir (file "autogen2")+      forM_ ins $ \i -> do+        c <- liftIO $ BS.readFile i+        bin' i c (if ds then [NoScan] else [])     rule $ do       phony "all"       depend u
src/Development/Cake3/Monad.hs view
@@ -86,11 +86,11 @@ addMakeDep :: File -> Make () addMakeDep f = modify (\ms -> ms { makeDeps = S.insert f (makeDeps ms) }) -tmpFile :: (MonadMake m) => m File-tmpFile = liftMake $ do+tmpFile :: (MonadMake m) => String -> m File+tmpFile pfx = liftMake $ do   s <- get   put s{tmpIndex = (tmpIndex s)+1}-  return (fromFilePath (".cake3" </> ("tmp"++(show (tmpIndex s)))))+  return (fromFilePath (".cake3" </> ("tmp"++ pfx ++ (show (tmpIndex s)))))  -- | Add prebuild command prebuild, postbuild :: (MonadMake m) => CommandGen -> m ()
src/Development/Cake3/Writer.hs view
@@ -240,14 +240,12 @@       produce (queryTargets (recipes ms))       unsafeShell [cmd|-mkdir .cake3|]       commands (rcmd $ prebuilds ms)-      unsafeShell [cmd|$(make) -f $(outputFile ms) $(makecmdgoals)|]+      unsafeShell [cmd|MAIN=1 $(make) -f $(outputFile ms) $(makecmdgoals)|]       commands (rcmd $ postbuilds ms)       markPhony      line ""     line "# Prebuild/postbuild section"-    line ""-    line "export MAIN=1"      line ""      case hasClean (recipes ms) of