mietek/halcyon

Halcyon checks twice for cabal constraints

Closed this issue · 4 comments

Halcyon runs cabal freeze twice. The first time is in Determining constraints (with my project it takes 0.86 seconds), and The second time is after "Using existing sandbox directory" (takes the same amount of time).

On a related note, I'd like it to have a cache so that it doesn't have to check each time; only when the .cabal file has been changed perhaps? When run on a project that has nothing changed, and is already built, this would take a 2.7 second build down to 0.95 seconds.

You should declare version constraints for your project, which will avoid the need for Halcyon to determine constraints, i.e., the first cabal freeze. This should be clear from the Halcyon tutorial, starting with the “Install the app” section. Halcyon warns you if version constraints aren’t declared:

-----> Determining constraints
   *** WARNING: Using newest versions of all packages
       MonadRandom-0.3.0.1
       StateVar-1.1.0.0
       aeson-0.8.0.2
       ...

In order to declare version constraints, you can either take the list of constraints printed by Halcyon and add them to your project as .halcyon/constraints, or run cabal freeze yourself, and keep the resulting cabal.config.

These two methods are currently equivalent, which isn’t clear from the documentation (mietek/halcyon-website#1), but this is likely to change soon, as the use of cabal freeze is due to be replaced (#52).

The second cabal freeze is an attempt at working around the deficiencies of Cabal (#1). Sometimes, the second run gives a different result than the first run (or than the declared version constraints). When this happens, Halcyon prints a warning which includes diff output, such as:

   *** WARNING: Unexpected constraints difference
       @@ -56,7 +56,6 @@
        nats-0.2
        network-2.5.0.0
        old-locale-1.0.0.5
       -old-time-1.1.0.1
        parallel-3.2.0.4
        parsec-3.1.5
        prelude-extras-0.4

There are two possible reasons:

  1. Cabal loses track of installed packages which only contain executables (haskell/cabal#779). This corresponds to diff lines starting with -. As explained by @benarmston (haskell/cabal#1896 (comment)):

When cabal installs a package, say foo, it registers the package's library (if any) with ghc-pkg. But the information which is registered doesn't contain any details about foo's executables. When cabal's resolver selects the installed version of foo, it queries ghc-pkg to find out what build-depends foo was compiled against. That list contains the build-depends for foo's library but not any build-depends for any of foo's executables.
In the test program provided by @mietek the only dependency on old-time is from the snap package's executable snap. When the snap package has been registered with ghc-pkg and the installed version of snap is selected, any build-depends in the snap package's executables are not available.

  1. The version constraints declared in .halcyon/constraints or cabal.config are incomplete. It’s not possible to determine this without installing dependencies (or restoring a previously-built sandbox directory), because cabal freeze ignores existing version constraints (haskell/cabal#2265). This corresponds to diff lines starting with +, and is explained in the “Declare a constraint” section of the Halcyon tutorial.

If you have any suggestions regarding the documentation, please open a mietek/halcyon-website issue.

Both cabal freeze runs are likely to be replaced by a single cabal install --dependencies-only --dry-run in the future (#52).

Thanks for that information. My builds runs just that bit faster.