haskell - Dynamically generating Rules from the content of an (Action a) -


i'm testing porting our build system make shake , have hit roadblock:

given following project structure:

static/a.js static/b.coffee  build/a.js build/b.js 

that is, various input extensions map identical output extensions, straightforward "build//*.js" %> rule isn't going work.

i wanted avoid using priority if possible, , writing ad-hoc build rule checks existence of either possible input felt clunky (especially since situation occurs other filetypes well), wrote following:

data staticfilemapping = staticfilemapping string string (filepath -> filepath -> action a)  staticinputs :: filepath -> staticfilemapping -> action [filepath] staticinputs dir (staticfilemapping iext _ _) = (findfiles (dir </> "static") [iext])  staticinputtooutput :: staticfilemapping -> filepath -> filepath staticinputtooutput (staticfilemapping _ oext _) = (remapdir ["build"]) . (-<.> oext)  statictargets :: filepath -> staticfilemapping -> action [filepath] statictargets dir sfm = (map $ staticinputtooutput sfm) <$> staticinputs dir sfm  rules :: filepath -> staticfilemapping -> rules () rules dir sfm@(staticfilemapping _ _ process) = join $ mconcat . (map buildinputrule) <$> staticinputs dir sfm     buildinputrule :: filepath -> rules ()           buildinputrule input = (staticinputtooutput sfm input) %> (process input) 

that way can define mapping each input type (.coffee -> .js, .svg -> .png) , on, tiny amount of code implementing transformation each. , works.

but seems impossible go (action a) rules _ without throwing value inside action away first, far can tell.

is there function type (action a) -> (a -> rules ()) -> rules () or (action a) -> (rules a)? can implement either 1 myself, or need modify library's code?

or entire approach hare-brained , should take other route?

first off, using priority not work, picks rule statically runs - doesn't backtrack. it's important shake doesn't run action operations produce rules (as per 2 functions propose) since action might call need on rule defines, or defined action rule, making ordering of action calls visible. add io (rules ()) -> rules (), might enough thinking of (directory listing), isn't exposed (i have internal function that).

to give few example approaches it's useful define plausible commands convert .js/.coffee files:

cmdcoffee :: filepath -> filepath -> action () cmdcoffee src out =     need [src]     cmd "coffee-script-convertor" [src] [out]  cmdjavascript :: filepath -> filepath -> action () cmdjavascript = copyfile' 

approach 1: use doesfileexist

this standard approach, writing like:

"build/*.js" %> \out ->     let srcjs = "static" </> dropdirectory1 out     let srccf = srcjs -<.> "coffee"     b <- doesfileexist srccf     if b cmdcoffee srccf out else cmdjavascript srcjs out 

this accurately captures dependency if user adds .coffee file in directory rule should rerun. imagine sugaring doesfileexist if common pattern you. drive list of staticfilemapping structures (do group on oext field add 1 rule per oext calls doesfileexists on each iext in turn). advantage of approach if shake build/out.js doesn't need directory listing, although cost negligible.

approach 2: list files before calling shake

instead of writing main = shakeargs ... do:

import system.directory.extra(listfilesrecursive) -- "extra" package  main =     files <- listfilesrecursive "static"     shakeargs shakeoptions $         form_ files $ \src -> case takeextension src of             ".js" ->                  let out = "build" </> takedirectory1 src                  want [out]                  out %> \_ -> cmdjavascript src out             -- rules other types care             _ -> return () 

here operate in io list of files, can add rules referring captured value. adding rulesio :: io (rules ()) -> rules () allow list files inside shakeargs.

approach 3: list files inside rules

you can define mapping between file names , outputs, driven directory listing:

buildjs :: action (map filepath (action ())) buildjs =     js <- getdirectoryfiles "static" ["*.js"]     cf <- getdirectoryfiles "static" ["*.coffee"]     return $ map.fromlist $         [("build" </> j, cmdjavascript ("static" </> j) ("build" </> j)) | j <- js] ++         [("build" </> c, cmdcoffee ("static" </> c) ("")) | c <- cf] 

then lift set of rules:

action $     mpjs <- buildjs     need $ map.keys mpjs "//*.js" %> \out ->     mpjs <- buildjs     mpjs map.! out 

however, recomputes directory listing every file build, should cache , make sure it's computed once:

mpjs <- newcache $ \() -> buildjs action $     mpjs <- mpjs ()     need $ map.keys mpjs "//*.js" %> \out ->     mpjs <- mpjs ()     mpjs map.! out 

this solution closest original approach, find complex.


Comments

Popular posts from this blog

jquery - How do you format the date used in the popover widget title of FullCalendar? -

asp.net mvc - SSO between MVCForum and Umbraco7 -

Python Tkinter keyboard using bind -