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 +4/−0
- cake3.cabal +3/−3
- src/Development/Cake3.hs +4/−1
- src/Development/Cake3/Ext/UrWeb.hs +255/−113
- src/Development/Cake3/Ext/UrWeb/UrEmbed.hs +14/−14
- src/Development/Cake3/Monad.hs +3/−3
- src/Development/Cake3/Writer.hs +1/−3
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