hpack: A modern format for Haskell packages
Hpack is a format for Haskell packages. It is a modern alternative to the Cabal package format and follows different design principles.
The guiding design principles for Hpack are:
Hpack packages are described in a file named
package.yaml. Both
cabal2nixand
stacksupport
package.yamlnatively. For other build tools the
hpackexecutable can be used to generate a
.cabalfile from
package.yaml.
There is reference documentation below, but introductory documentation is still lacking. For the time being, take a look at the slides from my talk about Hpack at the Singapore Haskell meetup: http://typeful.net/talks/hpack
hpackwill generate hpack.cabal
hpackwill generate getopt-generics.cabal
hpackwill generate sensei.cabal
hpackwill generate base-orphans.cabal
Paths_modules
Cabal generates a
Paths_module for every package. By default Hpack adds that module to
other-moduleswhen generating a
.cabalfile. This is sometimes useful and most of the time not harmful.
However, there are situations when this can lead to compilation errors (e.g when using a custom
Prelude).
To prevent Hpack from adding the
Paths_module to
other-modulesadd the following to
package.yaml:
library: when: - condition: false other-modules: Paths_name # substitute name with the package name
| Hpack | Cabal | Default | Notes | Example | Since | | --- | --- | --- | --- | --- | --- | |
spec-version| | | The minimum version of
hpackthat is required to parse this package description. |
spec-version: 0.30.0|
0.30.0| |
name| · | | | | | |
version| · |
0.0.0| | | | |
synopsis| · | | | | | |
description| · | | | | | |
category| · | | | | | |
stability| · | | | | | |
homepage| · | If
githubgiven,
#readme| | | | |
bug-reports| · | If
githubgiven,
/issues| | | | |
author| · | | May be a list | | | |
maintainer| · |
author| May be a list | | | |
copyright| · | | May be a list | | |
license| · | Inferred from
license-file| Both SPDX license expressions and traditional Cabal license identifiers are accepted. |
license: MIT| SPDX:
0.29.0| |
license-file|
license-fileor
license-files|
LICENSEif file exists | May be a list | | | |
tested-with| · | | May be a list (since
0.34.3) | | | |
build-type| · |
Simple, or
Customif
custom-setupexists | Must be
Simple,
Configure,
Make, or
Custom| | | |
extra-source-files| · | | Accepts glob patterns | | | |
extra-doc-files| · | | Accepts glob patterns | |
0.21.2| |
data-files| · | | Accepts glob patterns | | | |
data-dir| · | | | | | |
github|
source-repository head| | Accepts
owner/repoor
owner/repo/subdir|
github: foo/bar| |
git|
source-repository head| | No effect if
githubgiven |
git: https://my.repo.com/foo| | |
custom-setup| · | | See Custom setup | | | |
flags|
flag| | Map from flag name to flag (see Flags) | | | |
library| · | | See Library fields | | | |
internal-libraries|
library| | Map from internal library name to a dict of library fields and global top-level fields. | |
0.21.0| |
executables|
executable| | Map from executable name to executable (see Executable fields) | | | |
executable|
executable| | Shortcut for
executables: { package-name: ... }| |
0.18.0| |
tests|
test-suite| | Map from test name to test (see Test fields) | | | |
benchmarks|
benchmark| | Map from benchmark name to benchmark (see Benchmark fields) | | | |
defaults| | | See Defaults, may be a list | | |
Hpack does not require you to specify a
cabal-versionmanually. When generating a
.cabalfile, Hpack sets the
cabal-versionautomatically based on the features that are used.
If you want to override this behavior you can use
verbatimto set
cabal-versionmanually, e.g.:
verbatim: cabal-version: 2.2
Hpack allows the inclusion of common fields from a file on GitHub or a local file.
To use this feature a user must specify a GitHub repository, Git reference and a path to a file within that repository; alternatively, a path to the local file must be given.
Example:
defaults: github: sol/hpack-template ref: 2017 path: defaults.yaml
This will include all common fields from https://github.com/sol/hpack-template/blob/2017/defaults.yaml into the package specification.
| Field | Default | Notes | Example | | ----- | ------- | ----- | ------- | |
github| For github defaults. | Accepts
/|
github: sol/hpack-template| |
ref| | For github defaults. |
ref: 2017| |
path|
.hpack/defaults.yaml| For github defaults. A relative path to a file within the repository, path segments are separated by
/and must not contain
:and
\. |
path: defaults.yaml| |
local| | For local defaults. New in
0.26.0. | |
Exactly one of
githuband
localmust be given in a
defaultssection.
Hpack supports shorthand syntax for specifying
githuband
refas a string:
defaults: sol/[email protected]
This is equivalent to:
defaults: github: sol/hpack-template ref: 2017
Note: Hpack caches downloaded files under
~/.hpack/defaults///. Once downloaded, a file is reused from the cache. If the content on GitHub changes the file is not updated. For this reason it is recommended to only use tags as Git references.
If a defaults file has changed on GitHub and you want to use the latest version, then you have to delete that file from the cache manually.
If you want to prevent Hpack from accessing the network to download a defaults file, then you can achieve this by adding that file to the cache manually.
| Hpack | Cabal | Default | Notes | Example | | --- | --- | --- | --- | --- | |
dependencies|
setup-depends| | Implies
build-type: Custom| |
These fields can be specified top-level or on a per section basis; top-level values are merged with per section values.
| Hpack | Cabal | Default | Notes | | --- | --- | --- | --- | |
buildable| · | | Per section takes precedence over top-level | |
source-dirs|
hs-source-dirs| | | |
default-extensions| · | | | |
other-extensions| · | | | |
ghc-options| · | | | |
ghc-prof-options| · | | | |
ghcjs-options| · | | | |
cpp-options| · | | | |
cc-options| · | | | |
c-sources| · | | Accepts glob patterns | |
cxx-options| · | | | |
cxx-sources| · | | Accepts glob patterns | |
js-sources| · | | Accepts glob patterns | |
extra-lib-dirs| · | | | |
extra-libraries| · | | | |
include-dirs| · | | | |
install-includes| · | | | |
frameworks| · | | | |
extra-frameworks-dirs| · | | | |
ld-options| · | | | |
dependencies|
build-depends| | See Dependencies | |
pkg-config-dependencies|
pkgconfig-depends| | | |
build-tools|
build-toolsand/or
build-tool-depends| | | |
system-build-tools|
build-tools| | A set of system executables that have to be on the
PATHto build this component | |
when| | | Accepts a list of conditionals (see Conditionals) |
build-tools: A set of Haskell executables that are needed to build this component
Each element consists of a name and an optional version constraint.
The name can be specified in two ways:
:
A qualified name refers to an executable named
from a package named .An unqualified name either refers to an executables in the same package, or if no such executable exists it is desugared to
:.
build-toolscan be specified as a list or a mapping.
Examples:
yaml build-tools: - alex - happy:happy - hspec-discover == 2.*
build-tools: alex: 3.2.* happy:happy: 1.19.* hspec-discover: 2.*
When generating a
.cabalfile each element of
build-toolsis either added to
build-toolsor
build-tool-depends.
If the name refers to one of
alex,
c2hs,
cpphs,
greencard,
haddock,
happy,
hsc2hsor
hscolourthen the element is added to
build-tools, otherwise it is added to
build-tool-depends.
This is done to allow compatibility with a wider range of
Cabalversions.
Note: Unlike
Cabal, Hpack does not accept system executables as
build-tools. Use
system-build-toolsif you need this.
| Hpack | Cabal | Default | Notes | | --- | --- | --- | --- | |
exposed| · | | | |
visibility| · | | | |
exposed-modules| · | All modules in
source-dirsless
other-modulesless any modules mentioned in
when| | |
generated-exposed-modules| | | Added to
exposed-modulesand
autogen-modules. Since
0.23.0. |
other-modules| · | Outside conditionals: All modules in
source-dirsless
exposed-modulesless any modules mentioned in
when. Inside conditionals, and only if
exposed-modulesis not specified inside the conditional: All modules in
source-dirsof the conditional less any modules mentioned in
whenof the conditional | | |
generated-other-modules| | | Added to
other-modulesand
autogen-modules. Since
0.23.0. |
reexported-modules| · | | | |
signatures| · | | | | |
default-language|
Haskell2010| |
| Hpack | Cabal | Default | Notes | | --- | --- | --- | --- | |
main|
main-is| | | |
other-modules| · | All modules in
source-dirsless
mainless any modules mentioned in
when| | |
generated-other-modules| | | Added to
other-modulesand
autogen-modules. Since
0.23.0. | |
default-language|
Haskell2010| |
| Hpack | Cabal | Default | Notes | | --- | --- | --- | --- | | |
type|
exitcode-stdio-1.0| | |
main|
main-is| | | |
other-modules| · | All modules in
source-dirsless
mainless any modules mentioned in
when| | |
generated-other-modules| | | Added to
other-modulesand
autogen-modules. Since
0.23.0. | |
default-language|
Haskell2010| |
| Hpack | Cabal | Default | Notes | | --- | --- | --- | --- | | |
type|
exitcode-stdio-1.0| | |
main|
main-is| | | |
other-modules| · | All modules in
source-dirsless
mainless any modules mentioned in
when| | |
generated-other-modules| | | Added to
other-modulesand
autogen-modules. Since
0.23.0. | |
default-language|
Haskell2010| |
| Hpack | Cabal | Default | Notes | | --- | --- | --- | --- | |
description| · | | Optional | |
manual| · | | Required (unlike Cabal) | |
default| · | | Required (unlike Cabal) |
Dependencies can be specified as either a list or an object. These are equivalent:
dependencies: - base >= 4.10.1.0 - containers >= 5.10
dependencies: base: ">= 4.10.1.0" containers: ">= 5.10"
The individual dependencies can also be specified as an object:
dependencies: - name: base version: ">= 4.10.1.0" - name: containers
You can use objects at both levels, or have a mix of valid ways to specify the individual dependencies:
dependencies: base: version: ">= 4.10.1.0" # If you don't give a version, it defaults to 'any version'. containers: {} transformers: ">= 0.5.5.0 && < 5.6"
Individual dependencies as objects are only supported from version
0.31.0.
When a dependency is specified as an object, you can use the
mixinfield to control what modules from the dependency your program will see and how its signatures are filled in:
dependencies: # This gives you a shorter name to import from, and hides the other modules. - name: containers mixin: - (Data.Map.Lazy as Map) # This hides the System.IO.Unsafe module, and leaves the other modules unchanged. - name: base mixin: - hiding (System.IO.Unsafe) # This exposes only the listed modules - you won't be able to import the others! - name: lens mixin: - (Control.Lens, Data.Set.Lens, Data.Map.Lens as MapL) # This will rename the module, and expose the others. - name: transformers mixin: - hiding (Control.Monad.Trans.State.Lazy) - (Control.Monad.Trans.State.Lazy as State)
For more information, see the Cabal documentation.
Hint: you can hide the
Preludemodule from
base, and then rename an alternative prelude to
Preludeso that it doesn't need to be imported!
mixinwas added in version
0.31.0.
Conditionals with no else branch:
conditionfield
For example,
when: - condition: os(darwin) extra-lib-dirs: lib/darwin
becomes
if os(darwin) extra-lib-dirs: lib/darwin
Conditionals with an else branch:
conditionfield
thenfield, itself an object containing any number of other fields
elsefield, itself an object containing any number of other fields
For example,
when: - condition: flag(fast) then: ghc-options: -O2 else: ghc-options: -O0
becomes
if flag(fast) ghc-options: -O2 else ghc-options: -O0
Note: Conditionals with
condition: falseare omitted from the generated
.cabalfile.
At place where you can specify a list of files you can also use glob patterns. Glob patterns and ordinary file names can be freely mixed, e.g.:
extra-source-files: - static/*.js - static/site.css
Glob patterns are expanded according to the following rules:
?and
*are expanded according to POSIX (they match arbitrary characters, except for directory separators)
**is expanded in a
zsh-like fashion (matching across directory separators)
?,
*and
**do not match a
.at the beginning of a file/directory
(since
hpack-0.24.0)
In cases where Hpack does not (yet!) support what you want to do, you can use the
verbatimfield to pass things to Cabal verbatim. It is recognized top-level, in sections, and in conditionals.
verbatimaccepts an object or a string (or a list of objects and strings).
Disclaimer: The content of
verbatimfields are merged into the generated
.cabalfile as a final step, after Hpack is done with most of its work. Before that final step Hpack does not look at any
verbatimfields. Consequently, the content of a
verbatimfield does not affect any other fields that are populated by Hpack. As an example, if you use
verbatimto override
hs-source-dirs, the overridden information will not be used when Hpack infers
exposed-modulesor
other-modules.
When an object is used:
null
.cabalfields can be overridden
.cabalfields can be removed by overriding with
null
.cabalfields can be added
Example:
tests: spec: main: Spec.hs source-dirs: test verbatim: type: detailed-0.9 # change type from exitcode-stdio-1.0 default-language: null # remove default-language
When a string is used:
.cabalfields are left untouched
Example:
verbatim: | build-tool-depends: hspec-discover:hspec-discover == 2.*
You can combine the use of objects and strings to gain more fine-grained control, e.g. you can remove an existing field with an object and then include it with a string so that you have 100% control over the layout.
verbatim: - build-depends: null - | -- let's use Cabal 5.0 dependency syntax build-depends: hspec: [2-3[
It is possible to use YAML anchors (
&), aliases (
*) and merge keys (
<<) to define fields and reference them later.
executables: my-exe-1: &my-exe main: my-exe-1.hs dependencies: [base, my-lib] ghc-options: [-threaded] my-exe-2: <<: main: my-exe-2.hs>Fields that start with an underscore are ignored by
hpack, so they can be used to declare aliases:_exe-ghc-options: &exe-ghc-options - -threaded - -rtsoptsexecutables: my-exe-1: ghc-options: *exe-ghc-options
It is also possible to use the
!includedirective:# ...tests: hlint: !include "../common/hlint.yaml"
hlint.yaml:source-dirs: test main: hlint.hs dependencies: [base, hlint]This can also be used to provide entire libraries of snippets:
_common/lib: !include "../common/lib.yaml"name: example1 version: '0.1.0.0' synopsis: Example <<: library: source-dirs: src tests: hlint:>
lib.yaml:
- &legal maintainer: Some One copyright: (c) 2017 Some One license: BSD3
&defaults dependencies:
&test_hlint source-dirs: test main: hlint.hs dependencies: [hlint]
To run
hpackautomatically on modifications to
package.yamladd the following to your
~/.vimrc:
autocmd BufWritePost package.yaml call Hpack()function Hpack() let err = system('hpack ' . expand('%')) if v:shell_error echo err endif endfunction
Stack has built-in support for Hpack. If you are using Stack you can use
package.yamlinstead of a
.cabalfile. No additional steps are required.
You can get binaries for use on Travis CI with:
curl -sSL https://github.com/sol/hpack/raw/master/get-hpack.sh | bash
(both Linux and OS X are supported)