18

I thought there would already be a question about this, but I can't find one.

I want my program to print out the date it was compiled on. What's the easiest way to set that up?

I can think of several possibilities, but none of them are what you'd call "easy". Ideally I'd like to be able to just do ghc --make Foo and have Foo print out the compilation date each time I run it.

Various non-easy possibilities that spring to mind:

  • Learn Template Haskell. Figure out how to use Data.Time to fetch today's date. Find a way how to transform that into a string. (Now my program requires TH in order to work. I also need to convince it to recompile that module every time, otherwise I get the compilation date for that module [which never changes] rather than the whole program.)

  • Write a shell script that generates a tiny Haskell module containing the system date. (Now I have to use that shell script rather than compile my program directly. Also, shell scripting on Windows leaves much to be desired!)

  • Sit down and write some Haskell code which generates a tiny Haskell module containing the date. (More portable than previous idea - but still requires extra build steps or the date printed will be incorrect.)

  • There might be some way to do this through Cabal - but do I really want to package up this little program just to get a date facility?

Does anybody have any simpler suggestions?

MathematicalOrchid
  • 61,854
  • 19
  • 123
  • 220
  • 3
    Alias ghc to something equivalent to ``ghc -DNOW="\"`date`\""`` (no idea how that would look for a Windows shell), and in your module use `{-# LANGUAGE CPP #-}` and `now = NOW` for the date string. Ugly hack, might just be ugly enough to push you to a cleaner alternative that takes more work. – Daniel Fischer Jul 22 '13 at 20:30
  • @DanielFischer Apparently the Windows shell lacks any capability to set a variable from a command's output. Instead, you must use a hack involving `SET /P VAR=`, which reads text from stdin. If you pipe command output to a file, then pipe it back in, you can achieve the effect you want... but JESUS!! >_ – MathematicalOrchid Jul 22 '13 at 20:32
  • Oh, wow. Doesn't even PowerShell have that capability? – Daniel Fischer Jul 22 '13 at 20:33
  • 1
    @DanielFischer PowerShell may, yes. But I don't have that installed. It seems easier to write a tiny Haskell script to generate the necessary output. – MathematicalOrchid Jul 22 '13 at 20:35
  • Yep. That seems much easier then. – Daniel Fischer Jul 22 '13 at 20:36

2 Answers2

28

Using Template Haskell for this is relatively simple. You just need to:

  1. Run IO action within Template Haskell monad:

    runIO :: IO a -> Exp a
    
  2. Then create a string literal with:

    stringE :: String -> ExpQ
    
  3. Put a whole expression within a quasiquote.

    $( ... )
    

This program will print time of its compilation:

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

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

You may put the relevant fragment into a module that imports all other modules to make sure it is recompiled.

Or take current revision information from your versioning system. See: TemplateHaskell and IO

Community
  • 1
  • 1
Michal Gajda
  • 603
  • 5
  • 13
  • 1
    What a great first answer! Welcome to Stack Overflow! – AndrewC Jul 22 '13 at 22:25
  • 1
    This does seem to be just about the cleanest solution. The version string is used in several different places, so I'd put this code in a separate module. The only problem I foresee is getting the version module recompiled every time; perhaps I could use TH to "touch" the source file each time or something? – MathematicalOrchid Jul 23 '13 at 07:34
  • MathematicalOrchid: That's why I suggested that this module imports all other toplevel modules. Or one could put this code in `main` module. Then it is always recompiled. The other solution has the same problem, and trying to "touch" the source file could seem dirtier solution for some. – Michal Gajda Nov 13 '13 at 16:36
  • As of 2023, we have [`githash`](https://hackage.haskell.org/package/githash) package that does the thing. It is useful when you want details like Git commit hash and choose your own date format. If you really just want a date and time, you may prefer Nick's answer below. It is also worth noting that `stack` now recompiles Template Haskell modules each time it is called. – Michal Gajda Mar 13 '23 at 07:39
10

The preprocessor helpfully defines __DATE__ and __TIME__ macros (just like in C), so this works:

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

This is probably simpler than Michal's suggestion of Template Haskell, but doesn't let you choose the format of the date.

  • of course, once you get the date in the program as a literal, you can mangle that however you like. – muhmuhten Jul 28 '13 at 00:23
  • I was hoping this might be the case - but I couldn't find any mention of it in the documentation. Also, is there a reason why writing `"__DATE__"` doesn't work, but `__DATE__` on its own does? – MathematicalOrchid Jul 28 '13 at 08:18
  • GHC just calls the C preprocessor, so you'll find the predefined macros documented there. The only useful ones are `__DATE__`, `__TIME__`, `__FILE__` (the source file name) and `__LINE__` (the source file line number) - the last two are useful for defining an error macro that includes the source location. As for why `"__DATE__"` doesn't work, the preprocessor doesn't expand macros inside a string literal. Besides, `__DATE__` on its own already expands to a string literal. – Nick Smallbone Jul 28 '13 at 10:16