Configuration
Maintaining a project involves configuring or reconfiguring inputs and then using make to generate the projects. What do we want to configure? We don’t need much:
- A stackage resolver1.
- A list of constraints2.
- A list of source repository packages, for unpublished3 packages or for unpublished versions of packages.
- A list of packages.
All configuration goes into ./project-dhall (where . is the root folder for your Haskell project) except for the cabal.config that we’ll likely need to download from stackage and modify. Save this file as project-stackage/lts-m.n.config using the exact resolver name.
project-stackage
└── lts-m.n.config
Any == constraint that gives the cabal solver impossible constraints can then be commented out by prepending with --. If there are no impossible constraints then the stackage resolver can be imported directly from https://stackage.org.
< StackageWeb | StackageLocal >
The dhall2cabal and dhall2config text templates import stackage constraints from one of the following locations by invoking updo/text-templates/import-stackage.dhall:
StackageWeb: “import: https://stackage.org/resolver/cabal.config”StackageLocal: “import: ./project-stackage/resolver.config”
For each ghc-x.y.z compiler version, create this set of inputs and templates:
project-dhall
└── ghc-x.y.z
├── constraints.dhall ▨ List { dep : Text, ver : Text }
├── deps-external.dhall ▨ List { loc : Text, tag : Text, sub : List Text }
├── deps-internal.dhall ▨ List { loc : Text, tag : Text, sub : List Text }
├── forks-external.dhall ▨ List { loc : Text, tag : Text, sub : List Text }
├── forks-internal.dhall ▨ List { loc : Text, tag : Text, sub : List Text }
└── text-templates
├── dhall2cabal.dhall ▨ template for ghc-x.y.z.dhall2cabal.project
├── dhall2config.dhall ▨ template for ghc-x.y.z.dhall2config.project
├── dhall2stack.dhall ▨ template for ghc-x.y.z.dhall2stack.yaml
└── stacksnippet.dhall ▨ anything for ghc-x.y.z.dhall2stack.yaml
All of the inputs can be omitted, any of:
project-dhall
└── ghc-x.y.z
├── constraints.dhall
├── deps-external.dhall
├── deps-internal.dhall
├── forks-external.dhall
└── forks-internal.dhall
Anything in stacksnippet.dhall gets added to the top of the generated stack project4. The rest of the files are inputs.
1. Stackage Constraints
The stackage resolver is able to provide us with a list of packages that work together for both stack and cabal. Any other dependencies as constraints or source packages are either not found on stackage or are different versions.
By specifying a resolver with stack we get a list of exact == versions for packages published to stackage. Stackage also publishes a cabal.config file that has these package versions as cabal constraints. As cabal constraints are additive, in practice importing directly from stackage often leads to conflicts between stackage’s constraints and the project’s own constraints and for that reason we’ll usually have to download the cabal.config from stackage5 and comment out lines that are conflicted.
We don’t expect any packages to impose constraints on their dependencies at the package level, in their .cabal files, but if they do then these constraints must fit with constraints at the project level.
2. Project Constraints
In constraints6 put published packages that you want to use that are not on stackage or if they are on stackage where you want to use a different version.
[ { dep = "diagrams-postscript", ver = "1.5" }
, { dep = "diagrams-svg", ver = "1.4.3" }
]
The type of constraints is a list of records with dependency and version fields:
List { dep : Text, ver : Text }
The version can include a @rev:... or @sha256:... revision suffix7.
3. Source Repository Packages
There are various reasons to depend on source packages and forks of source packages; to keep a mirror within your organization, to add fixes that haven’t been upstreamed yet, or to pick up a fix someone else has made but hasn’t yet been merged upstream, released and published.
project-dhall
└── ghc-x.y.z
├── deps-external.dhall ▨ List { loc : Text, tag : Text, sub : List Text }
├── deps-internal.dhall ▨ List { loc : Text, tag : Text, sub : List Text }
├── forks-external.dhall ▨ List { loc : Text, tag : Text, sub : List Text }
└── forks-internal.dhall ▨ List { loc : Text, tag : Text, sub : List Text }
The type of these files are all the same. They contain a list of records with fields for location, tag (commit SHA) and subdirectories:
List { loc : Text, tag : Text, sub : List Text }
Any empty sub field must be type annotated like this [] : List Text. Each source repository package record will bring in one dependency or as many as there are sub fields.
These source repository *.dhall files are for original work from your organisation (internal) and from third parties (external) and for internal and external forks. If using the empty list in any one of these files then use an explicit type annotation:
[] : List { loc : Text, tag : Text, sub : List Text }
4. Package Groups
In the pkgs folder, create one or more groups for related packages.
project-dhall
├── ghc-x.y.z
├── pkgs
│ ├── db.dhall ▨ List Text
│ ├── server.dhall ▨ List Text
│ └── tools.dhall ▨ List Text
└── pkg-groups.dhall ▨ List Text
The contents of each group is a List Text of relative paths to folders containing package .cabal files.
-- ./project-dhall/pkgs/tools.dhall
[ "tool/linter/"
, "tool/formatter/"
]
List the package groups in pkg-groups.dhall8. A good order to use is the order of cabal constraint solving that you may witness when upgrading a project and seeing solving failures.
-- ./project-dhall/pkg-groups.dhall
[ "tools"
, "db"
, "server"
]
This gives you control of the imports in the generated ./project-cabal/pkgs.config, itself imported into ghc-x.y.z.dhall2config.project:
Use
CABAL_RELATIVITY=ImportRelativeto be import-compatible withcabal-3.10.-- ./project-cabal/pkgs.config import: ./pkgs/tools.config import: ./pkgs/db.config import: ./pkgs/server.configThe default of
CABAL_RELATIVITY=ProjectRelativeis import-compatible withcabal-3.8.-- ./project-cabal/pkgs.config import: ./project-cabal/pkgs/tools.config import: ./project-cabal/pkgs/db.config import: ./project-cabal/pkgs/server.config
-- ./ghc-x.y.z.dhall2config.project
import: ./project-stackage/lts-m.n.config
import: ./project-cabal/pkgs.config
import: ./project-cabal/ghc-x.y.z/constraints.config
import: ./project-cabal/ghc-x.y.z/deps-external.config
import: ./project-cabal/ghc-x.y.z/deps-internal.config
import: ./project-cabal/ghc-x.y.z/forks-external.config
import: ./project-cabal/ghc-x.y.z/forks-internal.config
Footnotes
For cabal, the cabal.config will have to be downloaded to
project-stackage/resolver.configand then edited to comment out constraints that would otherwise lead to impossible to solve conflicting constraints because cabal constraints are additive and cannot be overridden.↩︎To stack, constraints and source repository packages are both
extra-deps. We use cabal-like nomenclature but, be warned, cabal constraints cover more than justpackage ==version, such as flags:
↩︎constraints: bson -_old-network , HaXml +splitbaseA package not published to hackage or stackage.↩︎
Anything in
stacksnippet.dhallwill be used by dhall2yaml2stack too and is put into abase.yaml, the topmost fragment when stitching together fragments.↩︎The URL path to download this file from is
/resolver/cabal.config. The latest nightly is always at https://www.stackage.org/nightly/cabal.config.↩︎To stack, constraints and source repository packages are both
extra-deps. We use cabal-like nomenclature but, be warned, cabal constraints cover more than justpackage ==version, such as flags:
↩︎constraints: bson -_old-network , HaXml +splitbaseCabal’s revision feature is not yet implemented so these suffixes are stripped for Cabal projects.↩︎
Dhall can’t do arbitrary IO like reading files from a folder.↩︎