避免使用 oracle,或者分离相同类型的 oracle

Avoiding oracle, OR separating oracles of the same type

我有以下情况:

所以想法是添加一个运行s find-deps生成deps的Shake规则,将其解析为文件列表srcs,然后编译规则将 need srcs 确保 compile 仅在 find-deps 发现的任何来源已更改时重新 运行。

棘手的部分是 find-deps 需要 alwaysRerun 来发现新依赖的源文件。所以现在如果 compile 规则依赖 deps 来获取文件列表,它也会 alwaysRerun。标准的解决方案是使用 oracle:我们可以添加一个 needs deps 的 oracle 并将其解析为文件列表,然后 compile 规则将首先要求它源文件列表,只有 need 个。所以compile.

need链上没有alwaysRerun

但是,就我而言,我并不是在编写特定的 Shakefile。相反,我正在编写一个可重用的 Rules 库,用户可以使用它来制作自己的主 Shakefile。所以我需要把它打包成类似

的东西
myRules :: FilePath -> Rules ()
myRules dir = do
    dir </> "deps" %> \depFile -> do
        alwaysRerun
        cmd_ (Cwd dir) "find-deps" ["-o", depFile]

    dir </> "exe" %> \exeFile -> do
        srcs <- askOracle $ Sources dir
        need srcs
        cmd_ (Cwd dir) "compile" ["-o", exeFile]

但是我应该把 addOracle $ \Sources dir -> ... 部分 need [dir </> "deps"] 并解析它和 return 源文件列表放在哪里?我不能将它放在 rules 中,因为这样,对不同目录的 rules 的两次调用将尝试为同一类型安装 oracle 处理程序两次。而且我不能使 dir 成为 oracle 问题类型的一部分,因为它是一个词级变量,所以我不能将它提升到查询的 Symbol 索引中。

这给我留下了一些超级蹩脚的东西,比如有一个 includeThisOnlyOnce :: Rules () 用户必须记住在他们的 Shakefile 中只包含一次。

所以我的问题是:

Is there a way to track dependencies without involving an oracle?

是 - 如果 find-deps 的输出根本没有改变,那么它不会重建 compile。您可以通过指定 Change 值(例如 ChangeModtimeAndDigest)来实现,但这是一个全局设置。或者,您可以将 find-deps 的输出放在某个地方,例如 foo.deps.out,然后调用 copyFileChanged "foo.deps.out" "foo.deps",如果文件未更改,则不会更新时间戳。

Is there a way to separate oracles of the same type?

虽然我知道它为什么有用,但并不容易和立即。我可以想到两条可能的途径来解决它:

  1. 可以添加 addOracleIdempotent,它会忽略关于重复添加同一个 oracle 的任何错误。这是对 Shake 的一个相当简单的更改(本质上是在 Rules 中设置一个标志以忽略重复项)。
  2. 或者,您可以尝试将 dir 提升为 type-level 并确保每个 oracle 具有不同的类型。它可能会使您的 API 变得更加复杂并且需要类型魔法。

在所有这些解决方案中,我会使用 copyFileChanged,因为它简单且本地化。