Haskell 从操作(a)的内容动态生成规则

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

我目前正在测试将构建系统从make移植到shake,遇到了一个障碍:

鉴于以下项目结构:

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