Skip to content

s5k6/versionInfo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

title author
Print version information in Haskell programs

Note: This is much more general than only git hashes and version information, they are just one use case.

The task

The compiled binary must provide the following information:

  • The version number it was assigned in the cabal file.

  • The git hash that represents the latest commit before compilation, including “dirtiness”. Also, its date.

  • The time and date the binary was compiled.

The struggle

Compilation time

The easiest one is compilation time. Using Template Haskell, the current time can easily be calculated at compile time, and then spliced into the program.

The version number from the cabal file

This can be accessed by the magic (and largely undocumented) API provided in an autogenerated module named Paths_…

Building my application around RIO, I was hoping to get along without depending on base at all. Well, o.k., I'd accept Setup.hs to depend on base, assuming this is code running in a pre-compilation situation and has little to do with the inner workings of my program.

But the Paths_… module thwarts that plan

module Paths_versionInfo ( … ) where

import qualified Control.Exception as Exception
import Data.Version (Version(..))
import System.Environment (getEnv)
import Prelude

and that is code compiled into the resulting binary. I can live with it, but it's not extra cool, or is it?

The git hash, oh my

Wait, there's a package for that: gitrev — but that just don't cut the mustard.

Obviously, if the git repository is not available at compile time, then there's a problem. This is a legit scenario though, I might want to publish the source without revealing my history of fallacies and transgressions.

Asking for a git hash introduces a dependency on the SCM. And consequently, Cabal makes this problem obvious when installing a program that uses gitrev: The build happens in a temporary directory (not containing the .git subdirectory that was available during building), and thus reaching out to git fails. gitrev just puts UNKNOWN where the hash should be.

But still, I think it is legit to ask what commit a binary (or source distribution) was created from. Anyways, the customer asks for it!

Ultimately, after much transgression, I've resorted to auto-generating a module Generated.Early to convey the hash into the source distribution used for cabal install and also for shipping.

This revealed the following insight:

The Insight

I see two phases at work here. The first is the step from the cloned git repository to the source distribution, and the second is the actual compilation.

Hence, there can be two sorts of generated files:

  1. I'll call early generated the files that are not stored in your source repository, but have to be present in a source distribution.

    The information about the current git hash would fall in that category, but also any modules or data used by the product, that should be shipped to the customer without shipping its means of creation. Be it out of embarassment, practicality or legal necessity.

  2. I'll call late generated the files that are not in the source distribution, but created during compilation

    This category is the “classical” compile-time generated stuff, e.g. date of compilation, but also the Paths_… module, and other precomputed data used by the product whose computation cannot be done at source distribution.

Obviously, there may be generated files that would fit into either category, trading source distribution size for compile time duration.

From this perspective, the generation of Paths_… is only a special case of the late generated files. Allowing the developer to provide a template for Paths_…, one could avoid the base dependency during compilation.

I do not see Cabal's user interface providing a clear line between these two sorts of auto-generated code. It might be there, somewhere, but I can't put my finger on it. Maybe I just did not find it in the documentation.

Currently remaining pain

  • Just as there is a directory where (late) auto-generated code ends up, it would be nice to have such a location for early stuff too. But I'm entirely unsure what that could be.

  • It is not entirely clear how Cabal could detect a pre-sdist situation. My best guess is to leave this to the developer of the package, since a simple test may be sufficient.

  • Cabal does not know about early generated code. Thus, the package is reported to be broken

    $ cabal check
    Warning: These warnings may cause trouble when distributing the package:
    Warning: In 'extra-source-files': the pattern 'src/Generated/Early.hs' does
    not match any files.
    

    although cabal build will fix this.

  • I had to write my own Setup.hs. A more sane approach would be Cabal providing early and late generation hooks as outlined above.

  • After cloning from git, it is now mandatory to cabal build before cabal install, otherwise

    cabal: filepath wildcard 'src/Generated/Early.hs' does not match any files.
    

    But installation from a source distribution tarball is fine, because that will contain gitinfo.

  • I don't like Paths_… pulling in base. If that could be built from a template, the developer might have more control over the dependencies. Such a templating mechanism would also be useful for early generated code.

About

get git hash into Haskell binary compiled with Cabal

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published