GHC: 插入编译日期

18

我想这方面应该已经有问题了,但我找不到一个。

我希望我的程序能够打印出编译日期。最简单的设置方式是什么?

我可以想到几种可能性,但它们都不算“容易”。理想情况下,我希望只需执行 ghc --make Foo 就可以每次运行时打印出编译日期。

一些不容易的可能性:

  • 学习模板 Haskell。弄清如何使用 Data.Time 获取今天的日期,找到一种方法将其转换为字符串。(现在我的程序需要 TH 才能工作。我还需要说服它每次重新编译该模块,否则我得到的是那个模块的编译日期 [永远不会变] 而不是整个程序的编译日期。)

  • 编写一个 shell 脚本,生成一个包含系统日期的小型 Haskell 模块。(现在我必须使用该 shell 脚本而不是直接编译我的程序。此外,在 Windows 上运行 shell 脚本留下了很多问题!)

  • 编写一些 Haskell 代码,生成一个包含日期的小型 Haskell 模块。(比上一个想法更具可移植性,但仍需要额外的构建步骤,否则打印的日期将不正确。)

  • 可能有一种方法可以通过 Cabal 来实现这一点——但我真的想打包这个小程序只是为了得到一个日期功能吗?

有没有更简单的建议?


3
将“ghc”的别名配置为等效于“ghc -DNOW=""date""”(不知道在Windows Shell中会是什么样子),并在您的模块中使用{-# LANGUAGE CPP #-}now = NOW来表示日期字符串。这是一个丑陋的 hack,但可能足够丑陋,以便让您考虑更加复杂但更干净的替代方案。 - Daniel Fischer
@DanielFischer 显然,Windows shell缺乏从命令输出中设置变量的能力。相反,您必须使用涉及SET / P VAR =的技巧,该技巧从stdin读取文本。如果您将命令输出导管到文件,然后将其重新导回,则可以实现所需的效果...但天啊!! >_< - MathematicalOrchid
哦,哇。即使是PowerShell也没有这个功能吗? - Daniel Fischer
1
@DanielFischer 可以使用PowerShell,但我没有安装它。编写一个小的Haskell脚本生成所需的输出似乎更容易。 - MathematicalOrchid
是的,那似乎比较容易。 - Daniel Fischer
2个回答

28

使用Template Haskell相对简单。您只需要:

  1. 在Template Haskell单子中运行IO操作:

  2. runIO :: IO a -> Exp a
    
  3. 然后使用字符串字面量创建:

  4. stringE :: String -> ExpQ
    
  5. 将整个表达式放在反引号中。

  6. $( ... )
    

    这个程序将会打印它被编译的时间:

{-# LANGUAGE TemplateHaskell #-}
import Language.Haskell.TH
import Data.Time

main = print $(stringE =<< runIO (show `fmap` Data.Time.getCurrentTime))

你可以将相关的代码片段放入一个模块中,该模块导入所有其他模块,以确保它被重新编译。

或者从您的版本控制系统中获取当前修订信息。请参见:TemplateHaskell and IO


1
太棒了,第一个答案就这么好!欢迎来到 Stack Overflow! - AndrewC
1
这似乎是最干净的解决方案。版本字符串在多个不同的位置使用,所以我会把这段代码放在一个单独的模块中。我预见到的唯一问题就是每次重新编译版本模块的麻烦;也许我可以使用TH来每次“touch”源文件呢? - MathematicalOrchid
MathematicalOrchid:这就是为什么我建议该模块导入所有其他顶级模块。或者可以将此代码放在“main”模块中。然后它总是重新编译。另一个解决方案有同样的问题,尝试“触摸”源文件可能会对一些人来说是更脏的解决方案。 - Michal Gajda
截至2023年,我们有githash包可以做这件事。当您想要获取Git提交哈希和选择自己的日期格式时,它很有用。如果您只需要日期和时间,您可能更喜欢下面Nick提供的答案。值得注意的是,每次调用stack时现在都会重新编译模板Haskell模块。 - Michal Gajda

10

预处理器有用地定义了__DATE____TIME__宏(就像在C语言中一样),因此这个代码可以工作:

{-# LANGUAGE CPP #-}
main = putStrLn (__DATE__ ++ " " ++ __TIME__)

这可能比Michal的Template Haskell建议更简单,但不允许您选择日期的格式。


当然,一旦您在程序中以文字形式获取日期,您可以随意修改它。 - muhmuhten
我曾经希望这是可能的 - 但我在文档中找不到任何提及它的地方。此外,写入"__DATE__"为什么不起作用,而单独使用__DATE__却可以呢? - MathematicalOrchid
GHC只是调用C预处理器,因此您会在那里找到预定义的宏文档。唯一有用的是__DATE____TIME____FILE__(源文件名)和__LINE__(源文件行号)-最后两个对于定义包含源位置的错误宏很有用。至于为什么"__DATE__"不起作用,预处理器不会展开字符串文字中的宏。此外,__DATE__本身已经扩展为字符串文字。 - Nick Smallbone

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接