cake3 0.3.0.1 → 0.4.0.0
raw patch · 8 files changed
+210/−131 lines, 8 files
Files
- CHANGELOG +5/−0
- README.md +42/−34
- cake3.cabal +1/−4
- src/Development/Cake3.hs +22/−17
- src/Development/Cake3/Ext/UrWeb.hs +48/−45
- src/Development/Cake3/Ext/UrWeb/UrEmbed.hs +4/−7
- src/Development/Cake3/Monad.hs +23/−1
- src/Development/Cake3/Writer.hs +65/−23
CHANGELOG view
@@ -1,3 +1,8 @@+Version 0.4+-----------+Autobuild 'clean' rule. genFile and genTmpFile functions allowing to embed text+files into the Makefile.+ Version 0.3 ----------- API changes, improve documentation, improve UrWeb extension
README.md view
@@ -72,8 +72,8 @@ ---------------- Cake3 allows user to write Cakefile.hs in plain Haskell to define rules, targets-and other stuff as usual. After that, `cake3` compiles it into ./Cakegen-application which builds your Makefile (ghc is required for that). GNU Make+and other things as usual. `cake3` executable compiles it into ./Cakegen+application which outputs your Makefile (ghc is required for that). GNU Make knows how to do the rest. ### Example@@ -102,32 +102,34 @@ 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 - * Cakefile\_P is an autogenerated module. It defines `file :: String -> File` function plus some others. * Main building blocks - `rule` functions - build Makefile rules one-to-one. The prerequisites are computed based on it's actions. * All actions live in Action monad (`A` Monad). `shell` is the most important operation of this monad.- * Quasy-quotation is used to simplify the shell code. `[cmd|..|]` takes a string- as an argument. It's syntax includes:- * $name untiquotes hasell expressions `name` of type File, Variable plus few- others. They define a prerequisites of a rule- * @name untiquotes hasell expressions `name` of type File. They define- rule's targets- * complex expressions are also supported with $(foo bar) and @(bar baz).- * $$ and @@ expands to $ and @+ * Quasy-quotation is used to simplify writing of the shell code. `[cmd|..|]`+ takes a string as an argument. The following antiquotations are supported:+ * $name antiquotes Hasell expressions `name` of type File, Variable, few+ others. The name will be placed to the set of prerequisites of the rule.+ * @name antiquotes Hasell expressions `name` of type File. The name will be+ placed to the set of rule's targets.+ * complex Haskell expressions inside antiquotations are supported with+ $(foo bar) and @(bar baz) syntax.+ * $$ and @@ expands to $ and @. * Rules appears in the Makefile in the reversed order. Normally, you want- 'all' rule to be at the bottom.+ 'all' rule to be defined at the bottom of Cakefile.hs.+ * Starting from 0.4, cake3 outputs rule named 'clean' automatically. This rule+ contains recipe which deletes all intermediate files with 'rm' command.+ * selfUpdate call includes the self-updating dependencies. That means, that+ Makefile will depend on Cakefile.hs and thus will require ghc to present in+ the system. Removing selfUpdate call will make the Makefile fully+ Haskell-independent. Features and limitations@@ -147,20 +149,23 @@ ### Features - * *Allows spaces inside the filenames.*+ * *Cake3 generates the 'clean' rule automatically.*+ But if you define your own 'clean', cake3 will take it as is.++ * *Cake3 takes care of spaces inside the filenames.* Everyone knows that Makefiles don't like spaces in filenames. Cake3 carefully inserts '\ ' to make make happy. - * *Rebuilds a rule when variable changes.*+ * *Cake3 rebuilds a rule's target when variable changes.* Consider following antipattern: # You often write rules like this, don't you?- out : in- foo $(FLAGS) -o $@ $^+ program : program.c+ gcc $(FLAGS) -o $@ $^ - Unfortunately, changes in FLAGS don't lead to rebuilding of out.+ Unfortunately, changes in FLAGS don't lead to rebuilding of the program. Hardly-trackable bugs may appear if one part of a project was built with one set of optimisation flags and another part was build with another set by mistake.@@ -170,20 +175,21 @@ detect changes in variables and rebuild targets when nessesary. rule $ do- shell [cmd|foo $(extvar "FLAGS") -o @out $in |]+ shell [cmd|gcc $(extvar "FLAGS") -o @program $program_c |] - will rebuild `out` on FLAGS change+ will rebuild `program` every time the FLAGS change - * *Supports rules with more than one target.*+ * *Rules may have more than one target.* - It is not that simple to write a rule which has more than one target. Really,+ It is not that simple to write a rule which has more than one target in+ Makefile. Indeed, out1 out2 : in1 in2 foo in1 in2 -o out1 -o out2 is not corret. Read this [Automake article](http://www.gnu.org/software/automake/manual/html_node/Multiple-Outputs.html#Multiple-Outputs)- if you are surprised. Thirdcake implements [.INTERMEDIATE+ if you are surprised. Cake3 implements [.INTERMEDIATE pattern](http://stackoverflow.com/a/10609434/1133157) to deal with this problem so `rule` like this @@ -192,13 +198,13 @@ will always notice inputs changes and rebuild both outputs - * *Supports global prebuild\postbuild actions*+ * *Cake3 supports global prebuild\postbuild actions* Common human-made Makefile with prebuild commands would support them for one rule, typically, "all". Other targets often stay uncovered. Cake3 makes sure that actions are executed for any target you call. - * *Lets user organize build hierarchy.*+ * *Cake3 lets user organize build hierarchy.* Say, we have a project A with subproject L. L has it's own Makefile and we want to re-use it from our global A/Makefile. Make provides only two ways of@@ -234,22 +240,24 @@ #### Make syntax - * Make is not able to detect directory content chages. For example, user has- to rerun the ./Cakegen if they add/remove a source file. I plan to automate- this kind of checks. so it is on my TODO list.- * ifdef/endif instructions is not supported generally. User has to move this- logic in Haskell for now. Also on my TODO list+As a summary - only a samll subset of Make syntax is supported for generation.+For complex algorithms Haskell looks more suitable so implement everything you+need inside the Cakefile.hs. In particular:++ * Cake3 offers no way of detecting directory content chages. For example, user+ has to rerun the ./Cakegen if they add/remove a source file. * Cake3 doesn't check the contents of Makefile variables. It is user's responsibility to keep them safe. * Variables as targets is not supported. Implement this logic in Haskell for now. + #### General * Resulting Makefile is actually a GNUMakefile. GNU extensions (shell function and others) are needed to make various tricks to work. Also, posix environment with coreututils package is required. So, Linux, Probably Mac,- Probably Windows+Cygwin.+ Probably Windows+Cygwin are the platforms which run cake3. * All Cakefiles across the project tree should have unique names in order to be copied. Duplicates are found, the first one is used
cake3.cabal view
@@ -2,7 +2,7 @@ -- see http://haskell.org/cabal/users-guide/ name: cake3-version: 0.3.0.1+version: 0.4.0.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.@@ -36,9 +36,6 @@ > 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
src/Development/Cake3.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}@@ -40,6 +41,8 @@ , (</>) , toFilePath , readFileForMake+ , genFile+ , genTmpFile -- Make parts , prerequisites@@ -138,23 +141,10 @@ addPlacement 0 (S.findMin (rtgt r)) return (r,a) --- | Adds the phony target for a rule. Typical usage:--- --- > rule $ do--- > phony "clean"--- > unsafeShell [cmd|rm $elf $os $d|]--- >-phony ::- String -- ^ A name of phony target- -> A ()-phony name = do- produce (W.fromFilePath name :: File)- markPhony---- | Build a Recipe using recipe builder provided, than record the Recipe to the--- MakeState. Return the copy of Recipe (which should not be changed in future)--- and the result of recipe builder. The typical recipe builder result is the--- list of it's targets.+-- | Build a Recipe using the builder provided and record it to the MakeState.+-- Return the copy of Recipe (which should not be changed in future) and the+-- result of recipe builder. The typical recipe builder result is the list of+-- it's targets. -- -- /Example/ -- Lets declare a rule which builds "main.o" out of "main.c" and "CFLAGS"@@ -182,5 +172,20 @@ -- | A version of rule, without monad set explicitly rule' :: (MonadMake m) => A a -> m a rule' act = liftMake $ snd <$> withPlacement (rule2 act)+++genFile :: (MonadMake m) => File -> String -> m File+genFile tgt cnt = rule' $do+ shell [cmd|-rm -rf @tgt |]+ forM_ (lines cnt) $ \l -> do+ shell [cmd|echo '$(string (quote_dollar l))' >> @tgt |]+ return tgt+ where+ quote_dollar [] = []+ quote_dollar ('$':cs) = '$':'$':(quote_dollar cs)+ quote_dollar (c:cs) = c : (quote_dollar cs)++genTmpFile :: (MonadMake m) => String -> m File+genTmpFile cnt = tmpFile >>= \f -> genFile f cnt
src/Development/Cake3/Ext/UrWeb.hs view
@@ -39,7 +39,7 @@ import Development.Cake3.Monad import Development.Cake3 -data UrpAllow = UrpMime | UrpUrl | UrpResponseHeader+data UrpAllow = UrpMime | UrpUrl | UrpResponseHeader | UrpEnvVar | UrpHeader deriving(Show,Data,Typeable) data UrpRewrite = UrpStyle | UrpAll@@ -52,7 +52,7 @@ | UrpLibrary File | UrpDebug | UrpInclude File- | UrpLink File String+ | UrpLink (Either File String) | UrpSrc File String String | UrpPkgConfig String | UrpFFI File@@ -101,7 +101,7 @@ urpDeps :: Urp -> [File] urpDeps (Urp _ _ hdr mod) = foldl' scan2 (foldl' scan1 mempty hdr) mod where- scan1 a (UrpLink f _) = f:a+ scan1 a (UrpLink (Left f)) = f:a scan1 a (UrpSrc f _ _) = (f.="o"):a scan1 a (UrpInclude f) = f:a scan1 a _ = a@@ -126,7 +126,7 @@ urpObjs (Urp _ _ hdr _) = foldl' scan [] hdr where scan a (UrpSrc f _ lfl) = (f.="o"):a- scan a (UrpLink f lfl) = (f):a+ scan a (UrpLink (Left f)) = (f):a scan a _ = a urpLibs (Urp _ _ hdr _) = foldl' scan [] hdr where@@ -153,7 +153,9 @@ instance ToUrpWord UrpAllow where toUrpWord (UrpMime) = "mime"+ toUrpWord (UrpHeader) = "requestHeader" toUrpWord (UrpUrl) = "url"+ toUrpWord (UrpEnvVar) = "env" toUrpWord (UrpResponseHeader) = "responseHeader" instance ToUrpWord UrpRewrite where@@ -175,8 +177,9 @@ | otherwise = printf "library %s" (up </> toFilePath (dropExtension f)) toUrpLine up (UrpDebug) = printf "debug" toUrpLine up (UrpInclude f) = printf "include %s" (up </> toFilePath f)- toUrpLine up (UrpLink f lfl) = printf "link %s %s" lfl (up </> toFilePath f)- toUrpLine up (UrpSrc f _ lfl) = printf "link %s %s" lfl (up </> toFilePath (f.="o"))+ toUrpLine up (UrpLink (Left f)) = printf "link %s" (up </> toFilePath f)+ toUrpLine up (UrpLink (Right lfl)) = printf "link %s" lfl+ toUrpLine up (UrpSrc f _ _) = printf "link %s" (up </> toFilePath (f.="o")) toUrpLine up (UrpPkgConfig s) = printf "link %s" (maskPkgCfg s) toUrpLine up (UrpFFI s) = printf "ffi %s" (up </> toFilePath (dropExtensions s)) toUrpLine up (UrpSafeGet s) = printf "safeGet %s" (dropExtensions s)@@ -192,11 +195,10 @@ 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 +toTmpFile wr = genTmpFile $ execWriter $ wr+ line :: (MonadWriter String m) => String -> m () line s = tell (s++"\n") @@ -206,35 +208,32 @@ let u@(Urp _ _ hdr mod) = urpst s let pkgcfg = (urpPkgCfg u) - inp <- rule' $ do- let inp = urpfile .= "urp.in"- toFile inp $ do+ forM_ (urpSrcs u) $ \(c,fl) -> do+ let flags = concat $ fl : map (\p -> printf "$(shell pkg-config --cflags %s) " p) (urpPkgCfg u)+ let i = makevar "URINCL" "-I$(shell urweb -print-cinclude) " + let cc = makevar "URCC" "$(shell $(shell urweb -print-ccompiler) -print-prog-name=gcc)"+ let cpp = makevar "URCPP" "$(shell $(shell urweb -print-ccompiler) -print-prog-name=g++)"+ let incfl = extvar "UR_CFLAGS"+ rule2 $ do+ case takeExtension c of+ ".cpp" -> shell [cmd| $cpp -c $incfl $i $(string flags) -o @(c .= "o") $(c) |]+ ".c" -> shell [cmd| $cc -c $i $incfl $(string flags) -o @(c .= "o") $(c) |]+ e -> error ("Unknown C-source extension " ++ e)++ inp_in <- toTmpFile $ do forM hdr (line . toUrpLine (urpUp urpfile)) line "" forM mod (line . toUrpLine (urpUp urpfile)) - forM_ (urpSrcs u) $ \(c,fl) -> do- let flags = concat $ fl : map (\p -> printf "$(shell pkg-config --cflags %s) " p) (urpPkgCfg u)- let i = makevar "URINCL" "-I$(shell urweb -print-cinclude) " - let cc = makevar "URCC" "$(shell $(shell urweb -print-ccompiler) -print-prog-name=gcc)"- let cpp = makevar "URCPP" "$(shell $(shell urweb -print-ccompiler) -print-prog-name=g++)"- rule2 $ do- case takeExtension c of- ".cpp" -> shell [cmd| $cpp -c $i $(string flags) -o @(c .= "o") $(c) |]- ".c" -> shell [cmd| $cc -c $i -o $(string flags) @(c .= "o") $(c) |]- e -> error ("Unknown C-source extension " ++ e)-- depend (urpDeps u)- depend (urpLibs u)- shell [cmd|touch @inp|]- rule' $ do- let cpy = [cmd|cat $inp|] :: CommandGen' (Make' IO)+ let cpy = [cmd|cat $inp_in|] :: CommandGen' (Make' IO) let l = foldl' (\a p -> do let l = makevar (map toUpper $ printf "lib%s" p) (printf "$(shell pkg-config --libs %s)" p) [cmd| $a | sed 's@@$(string $ maskPkgCfg p)@@$l@@' |] ) cpy pkgcfg shell [cmd| $l > @urpfile |]+ depend (urpDeps u)+ depend (urpLibs u) return $ UWLib u @@ -269,17 +268,6 @@ urpUp :: File -> FilePath urpUp f = F.joinPath $ map (const "..") $ filter (/= ".") $ F.splitDirectories $ F.takeDirectory $ toFilePath f --- | Dir name , file to embed--- data UrEmbed = Urembed File File--- deriving (Show)---- data UrpLibReference--- = UrpLibStandaloneMake File --- | UrpLibStandaloneMake2 File --- | UrpLibInternal UWLib--- | UrpLibEmbed File File--- deriving(Show)- -- | A general method of including a library into the UrWeb project. library' :: (MonadMake m) => Make [File] -- ^ A monadic action, returning a list of libraries to include@@ -332,13 +320,19 @@ include f = addHdr $ UrpInclude f link' :: (MonadMake m) => File -> String -> UrpGen m ()-link' f fl = addHdr $ UrpLink f fl+link' f fl = do+ addHdr $ UrpLink (Left f)+ when (fl /= "") $ do+ addHdr $ UrpLink (Right fl) link :: (MonadMake m) => File -> UrpGen m () link f = link' f [] csrc' :: (MonadMake m) => File -> String -> String -> UrpGen m ()-csrc' f cfl lfl = addHdr $ UrpSrc f cfl lfl+csrc' f cfl lfl = do+ addHdr $ UrpSrc f cfl lfl+ when (lfl /= "") $ do+ addHdr $ UrpLink (Right lfl) csrc :: (MonadMake m) => File -> UrpGen m () csrc f = csrc' f [] []@@ -351,7 +345,10 @@ jsFunc m u j = addHdr $ UrpJSFunc m u j -safeGet s = addHdr $ UrpSafeGet s+safeGet :: (MonadMake m) => File -> String -> UrpGen m ()+safeGet m fn+ | (takeExtension m) /= ".ur" = fail (printf "safeGet: not an Ur/Web module name specified (%s)" (toFilePath m))+ | otherwise = addHdr $ UrpSafeGet (printf "%s/%s" (takeBaseName m) fn) url = UrpUrl @@ -361,6 +358,10 @@ all = UrpAll +env = UrpEnvVar++hdr = UrpHeader+ responseHeader = UrpResponseHeader script :: (MonadMake m) => String -> UrpGen m ()@@ -463,6 +464,8 @@ bin' :: (MonadIO m, MonadMake m) => File -> FilePath -> BS.ByteString -> UrpGen m () bin' dir src_name src_contents = do + liftIO $ createDirectoryIfMissing True (toFilePath dir)+ let mime = guessMime src_name let mn = (mkname src_name) let wrapmod ext = (dir </> mn) .= ext@@ -523,7 +526,7 @@ line $ "val text : unit -> transaction string" include (binmod ".h")- link (binmod ".o")+ csrc (binmod ".c") ffi (binmod ".urs") -- JavaScript FFI Module@@ -562,8 +565,8 @@ addHdr $ UrpJSFunc (modname jsmod) (urname decl) (jsname decl) ffi (jsmod ".urs") - safeGet $ printf "%s/blobpage" (modname wrapmod)- safeGet $ printf "%s/blob" (modname wrapmod)+ safeGet (wrapmod ".ur") "blobpage"+ safeGet (wrapmod ".ur") "blob" module_ (pair $ wrapmod ".ur") where
src/Development/Cake3/Ext/UrWeb/UrEmbed.hs view
@@ -86,19 +86,16 @@ 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+ -- 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"-- exists <- doesDirectoryExist tgtdir- when (not exists) $ do- fail "Output is not a directory" cntnts <- mapM BS.readFile ins
src/Development/Cake3/Monad.hs view
@@ -67,10 +67,13 @@ , warnings :: String -- ^ Warnings found so far , outputFile :: File+ -- ^ Name of the Makefile being generated+ , tmpIndex :: Int+ -- ^ Index to build temp names } -- Oh, such a boilerplate-initialMakeState mf = MS defr defr mempty mempty mempty mempty mempty mempty mempty mf where+initialMakeState mf = MS defr defr mempty mempty mempty mempty mempty mempty mempty mf 0 where defr = emptyRecipe "<internal>" getPlacementPos :: Make Int@@ -83,6 +86,12 @@ addMakeDep :: File -> Make () addMakeDep f = modify (\ms -> ms { makeDeps = S.insert f (makeDeps ms) }) +tmpFile :: (MonadMake m) => m File+tmpFile = liftMake $ do+ s <- get+ put s{tmpIndex = (tmpIndex s)+1}+ return (fromFilePath (".cake3" </> ("tmp"++(show (tmpIndex s)))))+ -- | Add prebuild command prebuild, postbuild :: (MonadMake m) => CommandGen -> m () prebuild cmdg = liftMake $ do@@ -203,6 +212,19 @@ -- files. Makefile-specific. markPhony :: (Monad m) => A' m () markPhony = modify $ \r -> r { rflags = S.insert Phony (rflags r) }++-- | Adds the phony target for a rule. Typical usage:+-- +-- > rule $ do+-- > phony "clean"+-- > unsafeShell [cmd|rm $elf $os $d|]+-- >+phony :: (Monad m)+ => String -- ^ A name of phony target+ -> A' m ()+phony name = do+ produce (fromFilePath name :: File)+ markPhony -- | Mark the recipe as 'INTERMEDIATE' i.e. claim that all it's targets may be -- removed after the build process. Makefile-specific.
src/Development/Cake3/Writer.hs view
@@ -9,13 +9,14 @@ import Control.Monad.Identity (runIdentity) import Control.Monad.State (MonadState, StateT(..), runStateT, State(..), execState, evalState, runState, modify, get, put) import Control.Monad.Trans+import Control.Monad.Writer (MonadWriter, WriterT(..), execWriterT, execWriter, tell) 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.Foldable (Foldable(..), forM_)+import qualified Data.Foldable as F import Data.Traversable (forM) import Data.Set (Set) import qualified Data.Set as S@@ -72,18 +73,18 @@ 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))+copyRecipeLL :: Recipe -> MakeLL ()+copyRecipeLL r = modify (\(a,b) -> (a,S.insert r b)) ruleLL :: A' MakeLL a -> MakeLL Recipe ruleLL act = do (r,_) <- runA "<internal>" act- produceLL r+ copyRecipeLL 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+ f r | L.null scmds = copyRecipeLL r | otherwise = do n1 <- fresh n2 <- fresh@@ -139,7 +140,7 @@ markIntermediate return () | otherwise = do- produceLL r+ copyRecipeLL r -- | Operate on a prerequisites which themselfs are targets of a multitarget rule. Make -- the conversion from:@@ -167,6 +168,34 @@ True -> r { rsrc = (rsrc r) `S.union` mulpack } False -> r) r badlist +hasClean :: (Foldable t) => t Recipe -> Bool+hasClean rs = F.foldl' flt False rs where+ flt True _ = True+ flt False r = (Phony `S.member`(rflags r)) && ((fromFilePath "clean")`S.member`(rtgt r))++intermediateFiles :: (Foldable t) => t Recipe -> Set File+intermediateFiles rs = + execWriter $ do+ forM_ rs $ \r -> do+ when (not $ Phony `S.member` (rflags r)) $ do+ tell (rtgt r)++cleanRuleLL :: Set File -> MakeLL Recipe+cleanRuleLL fs =+ ruleLL $ do+ phony "clean"+ unsafeShell [cmd|-rm $(fs)|]+ unsafeShell [cmd|-rm -rf .cake3|]++-- | Define a 'clean' phony target. The rule removes all targets except phony+-- targets and the Makefile itself+defineClean :: File -> Set Recipe -> Set Recipe+defineClean mk rs =+ runMakeLL "defineClean" $ do+ let fs = intermediateFiles rs+ cleanRuleLL (fs `S.difference` (S.singleton mk))+ return ()+ -- | Rule referring to the defaultMakefile :: File defaultMakefile = fromFilePath ("." </> "Makefile")@@ -181,17 +210,14 @@ 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.+-- | There are only 2 kind of rules: 1) rules which depend on the Makefile, and+-- 2) rules which Makefile depends on. Case-2 is known in advance (for example,+-- when 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+addMakeDeps makefile rs = S.map addMakeDeps_ rs where+ addMakeDeps_ r | not (isRequiredFor rs r makefile) = addPrerequisite makefile r+ | otherwise = r @@ -210,19 +236,34 @@ line "" sr <- region "SERVICE" $ do- line ""- line "# Prebuild/postbuild section"- line "" r <- runA_ "<internal>" $ do produce (queryTargets (recipes ms)) unsafeShell [cmd|-mkdir .cake3|] commands (rcmd $ prebuilds ms)- unsafeShell [cmd|$(make) -f $(outputFile ms) MAIN=1 $(makecmdgoals)|]+ unsafeShell [cmd|$(make) -f $(outputFile ms) $(makecmdgoals)|] commands (rcmd $ postbuilds ms) markPhony- writeRules $ applyPlacement (placement ms) $ fixMultiTarget [r]+ line ""+ line "# Prebuild/postbuild section"+ line ""+ line "export MAIN=1" + line "" + case hasClean (recipes ms) of+ True -> do+ writeRules $ applyPlacement (placement ms)+ $ fixMultiTarget [r]+ False -> do+ line "ifneq ($(MAKECMDGOALS),clean)"+ line ""+ writeRules $ applyPlacement (placement ms)+ $ fixMultiTarget [r]+ line ""+ line "endif"+ writeRules $ defineClean (outputFile ms) (recipes ms)+ line ""+ hdr <- runLines $ do line "# This Makefile was generated by the Cake3" line "# https://github.com/grwlf/cake3"@@ -264,7 +305,8 @@ | otherwise = do inner <- writeRegions' rs runLines $ do- line (printf "ifdef %s" (map toUpper $ mrname r))+ line (printf "ifeq ($(%s),1)" (map toUpper $ mrname r))+ line (printf "unexport %s" (map toUpper $ mrname r)) text (mrtext r) line "else" text inner