3

我试图了解如何使用Shake以及如何建立新规则。作为练习,我决定实施我所说的backup规则。

这个想法是如果文件不存在或者文件太旧(让我们超过 24 小时),则生成一个文件。我喜欢将长命令存储在生成文件中并按需运行它们。一个例子是 mysql 备份。唯一的问题是当备份已经存在时,make什么都不做。为了解决这个问题,我可以

  • 在重做新备份之前删除以前的备份,
  • 制作备份目标phony
  • 添加一个虚构的force依赖项,我可以手动或在 cron 中触摸它。

我想要的是重做备份,如果它超过 24 小时(我可以用一个touch forcein cron 来做)。无论如何,这只是一个可以玩的例子Shake。我想要的是这样的:

expirable "my_backup" 24 \out -> do
    cmd "mysqldump" backup_parameter out

我阅读了文档,但我不知道如何执行此操作或定义规则以及 anAction是什么。我知道我需要实例化一个Rule类,但我不知道是什么。

澄清

我不希望备份自动运行,而是仅按需运行,但每 24 小时最多运行一次。

一个示例场景是我在远程机器上有一个生产数据库,本地复制并在本地运行一些耗时的报告。正常的工作流程是

  • 下载生产备份
  • 用它刷新本地数据库
  • 在本地仓库数据库上创建一些非规范化表
  • 生成一些报告。

我不是每天都运行报告,而是仅在需要时运行。所以我不想每 24 小时运行一次报告。除了时间位之外,使用 makefile 很容易,它们可以解决,但它再次是一个人为的示例,可以深入了解 Shake 的工作原理。

所以,当我第一次make report备份数据库时,运行所有内容并生成报告。现在,我想修改报告(因为我正在测试它)。我不需要重新生成备份(也不需要刷新本地数据库)(我们是晚上,我知道直到第二天生产都没有任何变化)

然后第二天,或者下个月,我重新运行报告。这次我需要再次完成备份,并且所有依赖项也需要重新运行。

基本上我需要的规则是

重做时间戳=时间戳<旧

重做时间戳 = 时间戳 < 旧 || 现在 > 时间戳 + 24*36000

但我不知道把这条规则放在哪里。

问题更多的是把它放在哪里,而不是如何写它(它在上面)。如果它更容易(解释)我可以有一个规则询问用户(getLine)“你想重做这个目标(是/否)吗?”。

稍后我还将需要一个规则,具体取决于数据库(或特定表)的最后更新。我知道如何从数据库中获取信息,但不知道如何将其集成到 Shake 中。

我可能对 aRule是什么感到困惑。制定规则是关于如何制定目标(所以它更像是一个食谱)或者我认为是摇动中的行动。在哪里,当我说规则时,我指的是决定是否重新制作目标的规则,而不是如何去做。在 make 中,您没有选择权(它是时间戳),因此没有这样的概念。

4

2 回答 2

1

这是一个部分有效的解决方案:

import Development.Shake
import Control.Monad
import System.Directory as IO
import Data.Time

buildBackupAt :: FilePath -> Action ()
buildBackupAt out = cmd "mysqldump" "-backup" out {- Or whatever -}

-- Argument order chosen for partial application
buildEvery :: NominalDiffTime -> (FilePath -> Action ()) -> FilePath -> Action ()
buildEvery secs act file = do
    alwaysRerun
    exists <- liftIO $ IO.doesFileExist file
    rebuild <- if not exists then return True else do
        mtime <- liftIO $ getModificationTime file
        now <- liftIO $ getCurrentTime
        return $ diffUTCTime now mtime > secs
    when rebuild $ act file

myRules :: Rules ()
myRules = "my_backup" *> buildEvery (24*60*60) buildBackupAt
-- File name is a FilePattern that shake turns into a FilePath; no wildcard here,
-- so it's simple, but you can wildcard, too as long as you action pays attention
-- to the FilePath passed in.

这将每天重建备份,但如果在buildBackupAt更改中声明的依赖项将不会重建。

于 2014-07-22T22:47:54.360 回答
1

在 Shake 中“编写规则”有两种含义: 1)使用*>或类似定义特定于您的构建系统的规则;2) 定义新的规则类型,例如定义像*>你自己这样的操作符。Shake 的大多数用户经常做 1,而从不做 2。您的问题似乎完全与 2 相关,这当然是可能的(所有规则都写在 Shake 的核心之外),但很少见。

要定义在检查构建时运行的东西,您需要使用Development.Shake.Rule模块,并定义类型 class 的实例Rule。您通常希望对apply1函数进行加糖,以便人们可以以类型安全的方式使用您的规则。如果您正在编写一个简单的规则(例如,查找修改日期,看看它是否已更改),那么它并不太难。如果您正在执行更复杂的规则(例如检查文件不超过 1 天),这有点棘手,但仍然可能 - 它需要更加小心地考虑存储在哪里。以您的“如果文件超过一定秒数则重建”示例,我们可以定义:

module MaximumAgeRule(maximumAge, includeMaximumAge) where

import Data.Maybe
import Development.Shake.Rule
import Development.Shake.Classes
import Development.Shake
import System.Directory as IO
import Data.Time

newtype MaxAgeQ = MaxAgeQ (FilePath, Double)
    deriving (Show,Binary,NFData,Hashable,Typeable,Eq)

instance Rule MaxAgeQ Double where
    storedValue _ (MaxAgeQ (file, secs)) = do
        exists <- IO.doesFileExist file
        if not exists then return Nothing else do
            mtime <- getModificationTime file
            now <- getCurrentTime
            return $ Just $ fromRational (toRational $ diffUTCTime now mtime)
    equalValue _ (MaxAgeQ (_, t)) old new = if new < t then EqualCheap else NotEqual

-- | Define that the file must be no more than N seconds old
maximumAge :: FilePath -> Double -> Action ()
maximumAge file secs = do
    apply1 $ MaxAgeQ (file, secs) :: Action Double
    return ()

includeMaximumAge :: Rules ()
includeMaximumAge = do
    rule $ \q@(MaxAgeQ (_, secs)) -> Just $ do
        opts <- getShakeOptions
        liftIO $ fmap (fromMaybe $ secs + 1) $ storedValue opts q

然后我们可以使用该规则:

import Development.Shake
import MaximumAgeRule

main = shakeArgs shakeOptions $ do
    includeMaximumAge
    want ["output.txt"]
    "output.txt" *> \out -> do
        maximumAge out (24*60*60)
        liftIO $ putStrLn "rerunning"
        copyFile' "input.txt" "output.txt"

现在文件input.txt将在output.txt每次更改时被复制到。此外,如果output.txt超过 1 天,则会重新复制。

用法是如何工作的因为我们使用的是自定义规则,所以我们必须声明它includeMaximumAge(这很丑陋,但不可避免)。maximumAge然后我们在生产时调用output.txt,说文件output.txt必须不超过 1 天。如果是,则重新运行规则。简单且可重复使用。

定义是如何工作的 定义有点复杂,但我不希望很多人定义规则,所以每个规则定义的 StackOverflow 问题似乎是合理的 :)。我们必须为规则定义一个键和一个值,键产生值。对于密钥,我们声明了一个新类型(正如您对密钥所做的那样),它存储文件名和允许的年龄。对于该值,我们存储文件的年龄。该storedValue函数通过查询文件从键中检索值。该equalValue函数查看该值并确定该值是EqualCheap(不重建)还是NotEqual(重建)。通常作为equalValueold == new的主要测试,但在这里我们不关心上次的值是什么(我们忽略old),但我们确实关心阈值MaxAgeQ是,我们将其与值进行比较。

maximumAge函数只是调用apply1以添加对 的依赖MaxAgeQ,并includeMaximumAge定义apply1调用的内容。

于 2014-07-24T16:20:33.453 回答