Haskell 从操作(a)的内容动态生成规则
我目前正在测试将构建系统从make移植到shake,遇到了一个障碍: 鉴于以下项目结构:Haskell 从操作(a)的内容动态生成规则,haskell,shake-build-system,Haskell,Shake Build System,我目前正在测试将构建系统从make移植到shake,遇到了一个障碍: 鉴于以下项目结构: static/a.js static/b.coffee build/a.js build/b.js 也就是说,不同的输入扩展映射到相同的输出扩展,因此一个简单的“build//*.js”%>规则将不起作用 如果可能的话,我希望避免使用优先级,并编写一个特别的构建规则来检查是否存在任何一种可能的输入(特别是在其他文件类型也出现这种情况时),因此我编写了以下内容: data StaticFileMappin
static/a.js
static/b.coffee
build/a.js
build/b.js
也就是说,不同的输入扩展映射到相同的输出扩展,因此一个简单的“build//*.js”%>
规则将不起作用
如果可能的话,我希望避免使用优先级,并编写一个特别的构建规则来检查是否存在任何一种可能的输入(特别是在其他文件类型也出现这种情况时),因此我编写了以下内容:
data StaticFileMapping a = StaticFileMapping String String (FilePath -> FilePath -> Action a)
staticInputs :: FilePath -> StaticFileMapping a -> Action [FilePath]
staticInputs dir (StaticFileMapping iExt _ _) = (findFiles (dir </> "static") [iExt])
staticInputToOutput :: StaticFileMapping a -> FilePath -> FilePath
staticInputToOutput (StaticFileMapping _ oExt _) = (remapDir ["build"]) . (-<.> oExt)
staticTargets :: FilePath -> StaticFileMapping a -> Action [FilePath]
staticTargets dir sfm = (map $ staticInputToOutput sfm) <$> staticInputs dir sfm
rules :: FilePath -> StaticFileMapping a -> Rules ()
rules dir sfm@(StaticFileMapping _ _ process) = join $ mconcat . (map buildInputRule) <$> staticInputs dir sfm
where buildInputRule :: FilePath -> Rules ()
buildInputRule input = (staticInputToOutput sfm input) %> (process input)
data StaticFileMapping a=StaticFileMapping字符串(FilePath->FilePath->Action a)
staticInputs::FilePath->StaticFileMapping a->Action[FilePath]
staticInputs目录(StaticFileMapping iExt _;)=(FindFile(目录“static”)[iExt])
StaticInputOutput::StaticFileMapping a->FilePath->FilePath
StaticInputOutput(StaticFileMapping oExt ux)=(remapDir[“build”])。(-oExt)
staticTargets::FilePath->StaticFileMapping a->Action[FilePath]
staticTargets目录sfm=(映射$StaticInputOutput sfm)staticInputs目录sfm
规则::文件路径->静态文件映射a->规则()
rules dir sfm@(StaticFileMapping_uuu进程)=加入$mconcat。(映射buildInputRule)静态输入目录sfm
其中buildInputRule::FilePath->Rules()
BuildInput程序输入=(静态输入输出sfm输入)%>(进程输入)
这样,我就可以为每种输入类型定义映射(.coffee->.js
,.svg->.png
)等等,只需少量代码即可实现每种类型的转换。而且它几乎起作用了
但据我所知,如果不先将操作中的值扔掉,似乎不可能从(操作a)
转到规则
是否有类型为(操作a)->(a->Rules())->Rules()
或(操作a)->(规则a)
的函数?我可以自己实现其中一个,还是需要修改库的代码
或者整个方法都是胡思乱想的,我应该采取其他方法吗?首先,使用优先级将不起作用,因为这样静态地选择规则,然后运行它-它不会回溯。同样重要的是,Shake不运行任何操作
操作来生成规则
(根据您建议的两个函数),因为操作
可能会调用它自己定义的规则
,或者由另一个操作规则定义的需要
,从而使那些操作
调用的顺序可见。您可以添加IO(Rules())->Rules()
,这可能足以满足您的想法(目录列表),但它目前还没有公开(我有一个内部函数正好可以这样做)
为了给出一些示例方法,定义合理的命令来转换.js
/.coffee
文件非常有用:
cmdCoffee :: FilePath -> FilePath -> Action ()
cmdCoffee src out = do
need [src]
cmd "coffee-script-convertor" [src] [out]
cmdJavascript :: FilePath -> FilePath -> Action ()
cmdJavascript = copyFile'
方法1:使用doesFileExist
这将是我的标准方法,写下如下内容:
"build/*.js" %> \out -> do
let srcJs = "static" </> dropDirectory1 out
let srcCf = srcJs -<.> "coffee"
b <- doesFileExist srcCf
if b then cmdCoffee srcCf out else cmdJavascript srcJs out
在这里,您可以在IO中操作以获取文件列表,然后可以通过引用先前捕获的值来添加规则。添加rulesIO::IO(Rules())->Rules()
将允许您列出shakeArgs
中的文件
方法3:列出规则中的文件
您可以定义文件名和输出之间的映射,由目录列表驱动:
buildJs :: Action (Map FilePath (Action ()))
buildJs = do
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]
buildJs::Action(映射文件路径(Action()))
buildJs=do
我很傻,我先尝试了方法1,觉得它太笨重了,所以我用另一种技术去推广,没有意识到我可以推广这个方法。我试试看。
buildJs :: Action (Map FilePath (Action ()))
buildJs = do
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]
action $ do
mpJs <- buildJs
need $ Map.keys mpJs
"//*.js" %> \out -> do
mpJs <- buildJs
mpJs Map.! out
mpJs <- newCache $ \() -> buildJs
action $ do
mpJs <- mpJs ()
need $ Map.keys mpJs
"//*.js" %> \out -> do
mpJs <- mpJs ()
mpJs Map.! out