From 1bdb1de1fac1c460e28926bc2f149c5c62283cf7 Mon Sep 17 00:00:00 2001 From: Martin Sosic Date: Fri, 18 Mar 2022 22:41:05 +0100 Subject: [PATCH 01/61] Improved docs on cabal freeze. --- doc/cabal-commands.rst | 80 +++++++++++++++++--------- doc/cabal-project-description-file.rst | 2 +- 2 files changed, 53 insertions(+), 29 deletions(-) diff --git a/doc/cabal-commands.rst b/doc/cabal-commands.rst index eb239a9285a..eec77aa919b 100644 --- a/doc/cabal-commands.rst +++ b/doc/cabal-commands.rst @@ -511,38 +511,62 @@ flag. cabal freeze ^^^^^^^^^^^^ -If a package is built in several different environments, such as a -development environment, a staging environment and a production -environment, it may be necessary or desirable to ensure that the same -dependency versions are selected in each environment. This can be done -with the ``freeze`` command: - -``cabal freeze`` writes out a **freeze file** which records all of -the versions and flags that are picked by the solver under the -current index and flags. Default name of this file is -``cabal.project.freeze`` but in combination with a -``--project-file=my.project`` flag (see :ref:`project-file -`) -the name will be ``my.project.freeze``. -A freeze file has the same syntax as ``cabal.project`` and looks -something like this: - -.. highlight:: cabal +.. code-block:: console -:: + $ cabal freeze + +generates ``cabal.project.freeze`` file, which describes the exact dependency tree as it was resolved at that moment by Cabal. +This means it captures an exact version of every dependency, including dependencies of dependencies, recursively all the way. + +Since ``cabal`` reads ``cabal.project.freeze`` when present, and takes into consideration the version constraints in it, +this means that by producing ``cabal.project.freeze`` you are guaranteed that every future ``cabal`` call will use the exact same set of dependencies, +regardless of any updates (even patches) that might get published for these dependencies in the meantime. +Therefore, we have effectively "frozen" the dependencies in place. + +``cabal.project.freeze`` is intended to be committed to the version control. + +Do you need this? +================= + +Why would you want this? Don't we want to get minor updates of our dependencies, or at least patches, as soon as we can? +Well, although they shouldn't, it is possible that any kind of update introduces new bugs, performance issues, or some other kind of unexpected behaviour. +This is where ``cabal.project.freeze`` comes in, as it ensures that dependencies don't unexpectedly change. +You can still update your dependencies, but you have to do it on purpose, by modifying or by deleting and regenerating ``cabal.project.freeze`` file, +and in the meantime you are guaranteed no surprises will happen. + +This consistency can be valuable as it ensures that all teammates, deployments, and continuous integration are installing the exactly same dependencies. +So if you are running and testing the code on your local machine, you are guaranteed that your teammate and your continuos integration will be running the exact same code, +and that at the end that exact same code will get deployed. + +Usual use-case for using ``cabal freeze`` is when developing end-user code, for example an executable that you will distribute. +On the other hand, if you are developing a library, you will not want to distribute it together with the ``cabal.project.freeze`` file, as it would make it very hard for cabal to resolve dependencies for users of the library. + +Common workflow +=============== + +Common workflow for using ``cabal freeze``, if you changed any dependencies in ``.cabal`` file or want to update their versions, is to delete ``cabal.project.freeze`` file (if it already exists) and run ``cabal freeze`` to generate fresh version of ``cabal.project.freeze``. +You might in some cases want to skip deletion of ``cabal.project.freeze``, but keep in mind that in that case ``cabal freeze`` will use existing ``cabal.project.freeze`` when resolving dependencies, therefore not updating any existing dependencies, only adding new ones. +If not sure, best to delete ``cabal.project.freeze`` first and then run ``cabal freeze``. +Finally, you will always want to committ the new ``cabal.project.freeze`` to the version control. + +Ensuring everything is frozen +============================= + +Since ``cabal`` reads both ``.cabal`` and ``cabal.project.freeze`` files and combines version constraints from them, you can get into a state where not all dependencies are frozen, i.e. if you add a dependency to ``.cabal`` but forget to regenerate ``cabal.project.freeze`` after it -> now this new dependency will not be frozen and might get updated unexpectedly. + +To check if you are in such state, you can just run ``cabal freeze`` and check if ``cabal.project.freeze`` changed its contents -> if so, somebody forgot to regenerate ``cabal.project.freeze`` previously. + +To automate this check, you can make it a part of your continuous integration, or a part of your pre-commit hook. - constraints: HTTP ==4000.3.3, - HTTP +warp-tests -warn-as-error -network23 +network-uri -mtl1 -conduit10, - QuickCheck ==2.9.1, - QuickCheck +templatehaskell, - -- etc... +Example of how this can be done via bash script: +.. code-block:: bash -For end-user executables, it is recommended that you distribute the -``cabal.project.freeze`` file in your source repository so that all -users see a consistent set of dependencies. For libraries, this is not -recommended: users often need to build against different versions of -libraries than what you developed against. + [[ -f cabal.project.freeze ]] || exit 1 + OLD_FREEZE_SUM=$(md5sum cabal.project.freeze) + cabal freeze || exit 1 + NEW_FREEZE_SUM=$(md5sum cabal.project.freeze) + exit [[ "$NEW_FREEZE_SUM" == "$OLD_FREEZE_SUM" ]] .. _cabal-gen-bounds: diff --git a/doc/cabal-project-description-file.rst b/doc/cabal-project-description-file.rst index 8f7639ee1b8..edcf28333a3 100644 --- a/doc/cabal-project-description-file.rst +++ b/doc/cabal-project-description-file.rst @@ -24,7 +24,7 @@ file with ``profiling: True``. The full configuration of a project is determined by combining the following sources (later entries override earlier ones, except for appendable -options): +options, like dependency version constraints): 1. :ref:`The user-wide global configuration ` (default: ``~/.config/cabal/config``) From 856d661b492e66af16ceb604cec80a3a30fda3aa Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Fri, 26 Apr 2024 08:21:14 -0400 Subject: [PATCH 02/61] Move freeze discussion to guide --- doc/cabal-commands.rst | 59 +++++------------------------ doc/how-to-freeze-versions.rst | 69 ++++++++++++++++++++++++++++++++++ doc/index.rst | 1 + 3 files changed, 80 insertions(+), 49 deletions(-) create mode 100644 doc/how-to-freeze-versions.rst diff --git a/doc/cabal-commands.rst b/doc/cabal-commands.rst index eec77aa919b..2d86d70bebc 100644 --- a/doc/cabal-commands.rst +++ b/doc/cabal-commands.rst @@ -515,59 +515,20 @@ cabal freeze $ cabal freeze -generates ``cabal.project.freeze`` file, which describes the exact dependency tree as it was resolved at that moment by Cabal. -This means it captures an exact version of every dependency, including dependencies of dependencies, recursively all the way. - -Since ``cabal`` reads ``cabal.project.freeze`` when present, and takes into consideration the version constraints in it, -this means that by producing ``cabal.project.freeze`` you are guaranteed that every future ``cabal`` call will use the exact same set of dependencies, -regardless of any updates (even patches) that might get published for these dependencies in the meantime. +generates ``cabal.project.freeze`` file, which describes the exact dependency +tree as it was resolved at that moment by Cabal. This means it captures an +exact version of every dependency, including dependencies of dependencies, +recursively all the way. + +Since ``cabal`` reads ``cabal.project.freeze`` when present, and takes into +consideration the version constraints in it, this means that by producing +``cabal.project.freeze`` you are guaranteed that every future ``cabal`` call +will use the exact same set of dependencies, regardless of any updates (even +patches) that might get published for these dependencies in the meantime. Therefore, we have effectively "frozen" the dependencies in place. ``cabal.project.freeze`` is intended to be committed to the version control. -Do you need this? -================= - -Why would you want this? Don't we want to get minor updates of our dependencies, or at least patches, as soon as we can? -Well, although they shouldn't, it is possible that any kind of update introduces new bugs, performance issues, or some other kind of unexpected behaviour. -This is where ``cabal.project.freeze`` comes in, as it ensures that dependencies don't unexpectedly change. -You can still update your dependencies, but you have to do it on purpose, by modifying or by deleting and regenerating ``cabal.project.freeze`` file, -and in the meantime you are guaranteed no surprises will happen. - -This consistency can be valuable as it ensures that all teammates, deployments, and continuous integration are installing the exactly same dependencies. -So if you are running and testing the code on your local machine, you are guaranteed that your teammate and your continuos integration will be running the exact same code, -and that at the end that exact same code will get deployed. - -Usual use-case for using ``cabal freeze`` is when developing end-user code, for example an executable that you will distribute. -On the other hand, if you are developing a library, you will not want to distribute it together with the ``cabal.project.freeze`` file, as it would make it very hard for cabal to resolve dependencies for users of the library. - -Common workflow -=============== - -Common workflow for using ``cabal freeze``, if you changed any dependencies in ``.cabal`` file or want to update their versions, is to delete ``cabal.project.freeze`` file (if it already exists) and run ``cabal freeze`` to generate fresh version of ``cabal.project.freeze``. -You might in some cases want to skip deletion of ``cabal.project.freeze``, but keep in mind that in that case ``cabal freeze`` will use existing ``cabal.project.freeze`` when resolving dependencies, therefore not updating any existing dependencies, only adding new ones. -If not sure, best to delete ``cabal.project.freeze`` first and then run ``cabal freeze``. -Finally, you will always want to committ the new ``cabal.project.freeze`` to the version control. - -Ensuring everything is frozen -============================= - -Since ``cabal`` reads both ``.cabal`` and ``cabal.project.freeze`` files and combines version constraints from them, you can get into a state where not all dependencies are frozen, i.e. if you add a dependency to ``.cabal`` but forget to regenerate ``cabal.project.freeze`` after it -> now this new dependency will not be frozen and might get updated unexpectedly. - -To check if you are in such state, you can just run ``cabal freeze`` and check if ``cabal.project.freeze`` changed its contents -> if so, somebody forgot to regenerate ``cabal.project.freeze`` previously. - -To automate this check, you can make it a part of your continuous integration, or a part of your pre-commit hook. - -Example of how this can be done via bash script: - -.. code-block:: bash - - [[ -f cabal.project.freeze ]] || exit 1 - OLD_FREEZE_SUM=$(md5sum cabal.project.freeze) - cabal freeze || exit 1 - NEW_FREEZE_SUM=$(md5sum cabal.project.freeze) - exit [[ "$NEW_FREEZE_SUM" == "$OLD_FREEZE_SUM" ]] - .. _cabal-gen-bounds: cabal gen-bounds diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst new file mode 100644 index 00000000000..18834a17e8d --- /dev/null +++ b/doc/how-to-freeze-versions.rst @@ -0,0 +1,69 @@ +.. _freeze-versions: + +How to freeze versions +====================== + +Do you need this? +^^^^^^^^^^^^^^^^^ + +Why would you want this? Don't we want to get minor updates of our dependencies, +or at least patches, as soon as we can? Well, although they shouldn't, it is +possible that any kind of update introduces new bugs, performance issues, or +some other kind of unexpected behaviour. This is where ``cabal.project.freeze`` +comes in, as it ensures that dependencies don't unexpectedly change. You can +still update your dependencies, but you have to do it on purpose, by modifying +or by deleting and regenerating ``cabal.project.freeze`` file, and in the +meantime you are guaranteed no surprises will happen. + +This consistency can be valuable as it ensures that all teammates, deployments, +and continuous integration are installing the exactly same dependencies. So if +you are running and testing the code on your local machine, you are guaranteed +that your teammate and your continuos integration will be running the exact same +code, and that at the end that exact same code will get deployed. + +Usual use-case for using ``cabal freeze`` is when developing end-user code, for +example an executable that you will distribute. On the other hand, if you are +developing a library, you will not want to distribute it together with the +``cabal.project.freeze`` file, as it would make it very hard for cabal to +resolve dependencies for users of the library. + +Common workflow +^^^^^^^^^^^^^^^ + +Common workflow for using ``cabal freeze``, if you changed any dependencies in +``.cabal`` file or want to update their versions, is to delete +``cabal.project.freeze`` file (if it already exists) and run ``cabal freeze`` to +generate fresh version of ``cabal.project.freeze``. You might in some cases +want to skip deletion of ``cabal.project.freeze``, but keep in mind that in that +case ``cabal freeze`` will use existing ``cabal.project.freeze`` when resolving +dependencies, therefore not updating any existing dependencies, only adding new +ones. If not sure, best to delete ``cabal.project.freeze`` first and then run +``cabal freeze``. Finally, you will always want to committ the new +``cabal.project.freeze`` to the version control. + +Ensuring everything is frozen +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Since ``cabal`` reads both ``.cabal`` and ``cabal.project.freeze`` +files and combines version constraints from them, you can get into a state where +not all dependencies are frozen, i.e. if you add a dependency to +``.cabal`` but forget to regenerate ``cabal.project.freeze`` after +it -> now this new dependency will not be frozen and might get updated +unexpectedly. + +To check if you are in such state, you can just run ``cabal freeze`` and check +if ``cabal.project.freeze`` changed its contents -> if so, somebody forgot to +regenerate ``cabal.project.freeze`` previously. + +To automate this check, you can make it a part of your continuous integration, +or a part of your pre-commit hook. + +Example of how this can be done via bash script: + +.. code-block:: bash + + [[ -f cabal.project.freeze ]] || exit 1 + OLD_FREEZE_SUM=$(md5sum cabal.project.freeze) + cabal freeze || exit 1 + NEW_FREEZE_SUM=$(md5sum cabal.project.freeze) + exit [[ "$NEW_FREEZE_SUM" == "$OLD_FREEZE_SUM" ]] diff --git a/doc/index.rst b/doc/index.rst index d14e29674a0..799342b7c4d 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -13,6 +13,7 @@ Welcome to the Cabal User Guide :numbered: :maxdepth: 2 + how-to-freeze-versions how-to-package-haskell-code how-to-source-packages how-to-enable-profiling From ea6f205dc96c4ced65b6c7089c2f028f9f6e545b Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 7 May 2024 19:41:07 -0400 Subject: [PATCH 03/61] Typo continuos --- doc/how-to-freeze-versions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 18834a17e8d..dd53923e655 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -18,7 +18,7 @@ meantime you are guaranteed no surprises will happen. This consistency can be valuable as it ensures that all teammates, deployments, and continuous integration are installing the exactly same dependencies. So if you are running and testing the code on your local machine, you are guaranteed -that your teammate and your continuos integration will be running the exact same +that your teammate and your continuous integration will be running the exact same code, and that at the end that exact same code will get deployed. Usual use-case for using ``cabal freeze`` is when developing end-user code, for From 890f6084dcf2a9884040f5242828c7e63c28ee96 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 7 May 2024 20:10:06 -0400 Subject: [PATCH 04/61] Use description list for common workflows --- doc/how-to-freeze-versions.rst | 51 +++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index dd53923e655..03a07dc69b8 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -27,19 +27,44 @@ developing a library, you will not want to distribute it together with the ``cabal.project.freeze`` file, as it would make it very hard for cabal to resolve dependencies for users of the library. -Common workflow -^^^^^^^^^^^^^^^ - -Common workflow for using ``cabal freeze``, if you changed any dependencies in -``.cabal`` file or want to update their versions, is to delete -``cabal.project.freeze`` file (if it already exists) and run ``cabal freeze`` to -generate fresh version of ``cabal.project.freeze``. You might in some cases -want to skip deletion of ``cabal.project.freeze``, but keep in mind that in that -case ``cabal freeze`` will use existing ``cabal.project.freeze`` when resolving -dependencies, therefore not updating any existing dependencies, only adding new -ones. If not sure, best to delete ``cabal.project.freeze`` first and then run -``cabal freeze``. Finally, you will always want to committ the new -``cabal.project.freeze`` to the version control. +Common workflows +^^^^^^^^^^^^^^^^ + +.. Warning:: + For each of these workflows, you may have to first delete the + ``index-state`` line from ``cabal.project`` (and from + ``cabal.project.freeze`` if it exists) and then run ``cabal update`` to + ensure that cabal will have newer versions to re-resolve the dependencies + with. Alternatively, you can run ``cabal update + --ignore-project``. + +Freeze + If the ``cabal.project.freeze`` file doesn't exist, generating one is a + great way to see what versions of dependencies are currently being used even + if you choose to discard the ``.freeze`` file after inspecting it. + +Thaw, Freeze + If you changed the version ranges of any of the dependencies in any of your + project's package descriptions, in any ``.cabal`` file, then delete the + ``cabal.project.freeze`` file if it already exists and run ``cabal freeze`` + to generate fresh version of ``cabal.project.freeze``. + +Freeze, Freeze + You might in some cases want to skip deletion of ``cabal.project.freeze``, + but keep in mind that in that case ``cabal freeze`` will use existing + ``cabal.project.freeze`` when resolving dependencies, therefore not updating + any existing dependencies, only adding new ones. + +Partial Thaw, Freeze + If you want to a pick up a different version of a single dependency, you can + delete its constraint from ``cabal.project.freeze`` and then run ``cabal + freeze`` again. + +.. Note:: + + If not sure, pick the "thaw, freeze" workflow, as it is the safest, the + simplest and the most common. Finally, you will always want to commit the + changed ``cabal.project.freeze`` to version control. Ensuring everything is frozen ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 999c4dc799e644e6924d7d246ef2023a083fb869 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Wed, 8 May 2024 12:27:29 -0400 Subject: [PATCH 05/61] Define what freezing is --- doc/how-to-freeze-versions.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 03a07dc69b8..f444839c423 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -3,6 +3,22 @@ How to freeze versions ====================== +Freezing + Pins the versions picked by the solver for all dependencies. + +Pinning adds a version equality constraint for each package in the set of +project dependencies, explicit and transitive. The ``cabal freeze`` command +saves these to a file named the same as the whole of the project file name but +with a ``.freeze`` extension, so the freeze file for ``cabal.project`` is +``cabal.project.freeze``. Effectively a ``.freeze`` file is an implicit project +import, same as the ``.local`` file for projects. + +.. Warning:: + + The order of imports of ``.local`` and ``.freeze`` files is important. The + ``.local`` file is imported last, after the ``.freeze`` file, giving the + user a final say in the setting of any fields that have override semantics. + Do you need this? ^^^^^^^^^^^^^^^^^ From 242ca1993e715978e1b3cc2f1ba2ab97d8bacb19 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Fri, 10 May 2024 20:34:49 -0400 Subject: [PATCH 06/61] Move freezing downwards in the guide --- doc/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/index.rst b/doc/index.rst index 799342b7c4d..fcf2ebe958c 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -13,9 +13,9 @@ Welcome to the Cabal User Guide :numbered: :maxdepth: 2 - how-to-freeze-versions how-to-package-haskell-code how-to-source-packages + how-to-freeze-versions how-to-enable-profiling how-to-build-like-nix how-to-run-in-windows From 99847873e26ead5600db7cda5bfe5aecafe986c2 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Fri, 10 May 2024 20:40:40 -0400 Subject: [PATCH 07/61] Extra .freeze extension --- doc/how-to-freeze-versions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index f444839c423..843a3c133e2 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -9,7 +9,7 @@ Freezing Pinning adds a version equality constraint for each package in the set of project dependencies, explicit and transitive. The ``cabal freeze`` command saves these to a file named the same as the whole of the project file name but -with a ``.freeze`` extension, so the freeze file for ``cabal.project`` is +with a extra ``.freeze`` extension, so the freeze file for ``cabal.project`` is ``cabal.project.freeze``. Effectively a ``.freeze`` file is an implicit project import, same as the ``.local`` file for projects. From 1dc90f22540f12eb7417ef116a8c6ae59cfff3ea Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Fri, 10 May 2024 21:13:20 -0400 Subject: [PATCH 08/61] Add a dependency then freeze harder --- doc/how-to-freeze-versions.rst | 35 +++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 843a3c133e2..95d8e584f6d 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -65,7 +65,7 @@ Thaw, Freeze ``cabal.project.freeze`` file if it already exists and run ``cabal freeze`` to generate fresh version of ``cabal.project.freeze``. -Freeze, Freeze +Freeze, Freeze (Freezing Harder) You might in some cases want to skip deletion of ``cabal.project.freeze``, but keep in mind that in that case ``cabal freeze`` will use existing ``cabal.project.freeze`` when resolving dependencies, therefore not updating @@ -85,21 +85,26 @@ Partial Thaw, Freeze Ensuring everything is frozen ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Since ``cabal`` reads both ``.cabal`` and ``cabal.project.freeze`` -files and combines version constraints from them, you can get into a state where -not all dependencies are frozen, i.e. if you add a dependency to -``.cabal`` but forget to regenerate ``cabal.project.freeze`` after -it -> now this new dependency will not be frozen and might get updated -unexpectedly. - -To check if you are in such state, you can just run ``cabal freeze`` and check -if ``cabal.project.freeze`` changed its contents -> if so, somebody forgot to -regenerate ``cabal.project.freeze`` previously. - -To automate this check, you can make it a part of your continuous integration, -or a part of your pre-commit hook. +.. Note:: -Example of how this can be done via bash script: + If the ``.freeze`` file already has version equality constraints for every + package that is a dependency of the project, then the solver will not be + able to find a different version for any of them, the ``.freeze`` file + cannot change and, at that point when every dependency is frozen, ``cabal + freeze`` becomes an idempotent operation. + +Adding a dependency to one of the packages in a project without freezing harder +leaves the newly added dependency susceptible to getting updated unexpectedly +when the solver can find a different version for it. Running ``cabal freeze`` +will show this vulnerability to a human or an automated check that notices a new +version equality constraint in the ``.freeze`` file, a constraint for a package +that wasn't in the ``.freeze`` file before. + +To automate this check, make it a part of continuous integration or make a +pre-commit hook for it. A simple check for this might be to compare the md5sum +of the ``.freeze`` file before and after running ``cabal freeze``. If the +checksums are the same, then the ``.freeze`` file didn't change, and all +versions are frozen. .. code-block:: bash From 314e5e29d467cc4b08de52d44e637b4983e78a57 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Fri, 10 May 2024 21:48:46 -0400 Subject: [PATCH 09/61] Do you need to freeze as new section title - Fix typo --- doc/how-to-freeze-versions.rst | 48 ++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 95d8e584f6d..69b71db740c 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -5,7 +5,7 @@ How to freeze versions Freezing Pins the versions picked by the solver for all dependencies. - + Pinning adds a version equality constraint for each package in the set of project dependencies, explicit and transitive. The ``cabal freeze`` command saves these to a file named the same as the whole of the project file name but @@ -19,29 +19,39 @@ import, same as the ``.local`` file for projects. ``.local`` file is imported last, after the ``.freeze`` file, giving the user a final say in the setting of any fields that have override semantics. -Do you need this? -^^^^^^^^^^^^^^^^^ +Do you need to freeze? +^^^^^^^^^^^^^^^^^^^^^^ -Why would you want this? Don't we want to get minor updates of our dependencies, -or at least patches, as soon as we can? Well, although they shouldn't, it is -possible that any kind of update introduces new bugs, performance issues, or -some other kind of unexpected behaviour. This is where ``cabal.project.freeze`` -comes in, as it ensures that dependencies don't unexpectedly change. You can -still update your dependencies, but you have to do it on purpose, by modifying -or by deleting and regenerating ``cabal.project.freeze`` file, and in the -meantime you are guaranteed no surprises will happen. +Why would you want to freeze? Don't we want to get minor updates of our +dependencies, or at least patches, as soon as we can? Well, although they +shouldn't, it is possible that any kind of update introduces new bugs, +performance issues, or some other kind of unexpected behaviour. This is where +``cabal.project.freeze`` comes in, as it ensures that dependencies don't +unexpectedly change. You can still update your dependencies, but you have to do +it on purpose, by modifying or by deleting and regenerating +``cabal.project.freeze`` file, and in the meantime you are guaranteed no +surprises will happen. This consistency can be valuable as it ensures that all teammates, deployments, and continuous integration are installing the exactly same dependencies. So if you are running and testing the code on your local machine, you are guaranteed -that your teammate and your continuous integration will be running the exact same -code, and that at the end that exact same code will get deployed. - -Usual use-case for using ``cabal freeze`` is when developing end-user code, for -example an executable that you will distribute. On the other hand, if you are -developing a library, you will not want to distribute it together with the -``cabal.project.freeze`` file, as it would make it very hard for cabal to -resolve dependencies for users of the library. +that your teammate and your continuous integration will be running the exact +same code, and that at the end that exact same code will get deployed. + +A ``.freeze`` file can be good to have when developing for yourself or within a +private team. If anyone using it can somehow have different inputs to +the solver then the ``.freeze`` file can be troublesome. It can prevent the +solver from finding a different version of a dependency that would satisfy a +different architecture or a different compiler version and boot libraries. + +.. Warning:: + + If publishing a package to Hackage, not matter what kind of component it + contains, don't include a ``.freeze`` file, don't add it to any field of the + package description that would have ``cabal sdist`` include it in the + ``.tar.gz``. In general, don't include anything in the package description + that relates to the project environment, like ``cabal.project`` or + ``cabal.project.local``. Common workflows ^^^^^^^^^^^^^^^^ From 7f2ddae9e912a73105226c09d7b2d29d595c22c6 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sat, 11 May 2024 06:16:23 -0400 Subject: [PATCH 10/61] How to control versions --- doc/how-to-freeze-versions.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 69b71db740c..cb3e5e30919 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -1,7 +1,7 @@ .. _freeze-versions: -How to freeze versions -====================== +How to control versions +======================= Freezing Pins the versions picked by the solver for all dependencies. From 41437584e49e5d6b0358325f2beacc79ce686bbf Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sat, 11 May 2024 06:58:47 -0400 Subject: [PATCH 11/61] Add other ways to control versions --- doc/how-to-freeze-versions.rst | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index cb3e5e30919..9741157c5d9 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -3,8 +3,32 @@ How to control versions ======================= +There are various ways and places to limit what versions the solver can pick for +dependencies. + +Version ranges + Within a package description, version ranges for dependencies can be; tight + or loose or missing altogether. + +Version constraints + Within a project, version constraints for dependencies limit the versions + that the solver can pick. + +Curated sets + A project can import curated sets of packages and versions that are known to + work together, such as those Stackage provides for Cabal's use. + +Pinning Hackage versions + Adding ``index-state`` to a project limits versions coming from Hackage to + include only those that were available at the cutoff time. + Freezing - Pins the versions picked by the solver for all dependencies. + Pins the versions picked by the solver for all dependencies. This is a way + to preserve a set of versions found by the solver, a solver-curated set, if + you will. + +Freezing versions +----------------- Pinning adds a version equality constraint for each package in the set of project dependencies, explicit and transitive. The ``cabal freeze`` command @@ -53,8 +77,8 @@ different architecture or a different compiler version and boot libraries. that relates to the project environment, like ``cabal.project`` or ``cabal.project.local``. -Common workflows -^^^^^^^^^^^^^^^^ +Freezing workflows +^^^^^^^^^^^^^^^^^^ .. Warning:: For each of these workflows, you may have to first delete the From e3c934fddfd44953cd9fb724af769f6ed4c98f41 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sat, 11 May 2024 16:06:48 -0400 Subject: [PATCH 12/61] Add section titles --- doc/how-to-freeze-versions.rst | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 9741157c5d9..28834a22123 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -1,5 +1,3 @@ -.. _freeze-versions: - How to control versions ======================= @@ -14,21 +12,35 @@ Version constraints Within a project, version constraints for dependencies limit the versions that the solver can pick. -Curated sets +Curated version sets A project can import curated sets of packages and versions that are known to work together, such as those Stackage provides for Cabal's use. -Pinning Hackage versions +Capped repository versions Adding ``index-state`` to a project limits versions coming from Hackage to include only those that were available at the cutoff time. -Freezing +Frozen versions Pins the versions picked by the solver for all dependencies. This is a way to preserve a set of versions found by the solver, a solver-curated set, if you will. -Freezing versions ------------------ +Version ranges +-------------- + +Version constraints +------------------- + +Curated version sets +-------------------- + +Capped repository versions +-------------------------- + +.. _freeze-versions: + +Frozen versions +--------------- Pinning adds a version equality constraint for each package in the set of project dependencies, explicit and transitive. The ``cabal freeze`` command From dafc08c6ea8bca1ba4198173bfcbe1c7a54d7444 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sat, 11 May 2024 16:12:50 -0400 Subject: [PATCH 13/61] Stackage as a curated package set --- doc/how-to-freeze-versions.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 28834a22123..5281b2fb7e0 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -34,6 +34,11 @@ Version constraints Curated version sets -------------------- +Stackage provides curated sets of packages and versions that are known to work +together and are updated regularly. The latest resolver is the nightly and this +typically lags a bit behind the latest available GHC version. The LTS resolvers +will each follow a specific GHC version and are updated less frequently. + Capped repository versions -------------------------- From 7d1ea84caf6e7074ffba7e50bc660c3c89eb240e Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sun, 9 Feb 2025 15:15:11 -0500 Subject: [PATCH 14/61] Constraints are additive --- doc/how-to-freeze-versions.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 5281b2fb7e0..47e31c99b6b 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -31,6 +31,11 @@ Version ranges Version constraints ------------------- +.. Warning:: + + Constraints are additive. If I add a constraint it doesn't remove or replace + prior constraints on versions. Constraints don't have override semantics. + Curated version sets -------------------- From c72bf118792b1ca8b28b77c963778c6d6ba9d017 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sun, 9 Feb 2025 15:29:39 -0500 Subject: [PATCH 15/61] Add version ranges --- doc/how-to-freeze-versions.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 47e31c99b6b..5f78cb9e113 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -28,6 +28,23 @@ Frozen versions Version ranges -------------- +A dependency can have a version range specified in the package description. +This is expected for published packages. The `cabal check` command will check +that version ranges conform to a set of rules: + +- that lower bounds are inclusive +- that upper bounds are exclusive +- that upper bounds don't have trailing zeros +- that upper bounds are not missing +- that upper bounds are not missing for ``base`` + +With large projects with many packages, it would be a lot of work to keep all +package dependency version ranges up to date. Ways of overcoming this are: + +- use a package generator like ``hpack-dhall-cabal`` to import sets of dependencies with version ranges +- use a ``cabal.project`` file to specify version constraints for all dependencies +- use a curated set of packages and versions and import these into the project + Version constraints ------------------- From f64ec0b2849f874f5f12fc38a30d070948c9e072 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sun, 9 Feb 2025 15:37:35 -0500 Subject: [PATCH 16/61] Add content to the version ranges section --- doc/how-to-freeze-versions.rst | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 5f78cb9e113..ae02c1bfb58 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -29,8 +29,9 @@ Version ranges -------------- A dependency can have a version range specified in the package description. -This is expected for published packages. The `cabal check` command will check -that version ranges conform to a set of rules: +Published packages are expected to attach version ranges to dependencies that +conform to a set of rules that ``cabal check`` command will check for and report +on: - that lower bounds are inclusive - that upper bounds are exclusive @@ -38,11 +39,12 @@ that version ranges conform to a set of rules: - that upper bounds are not missing - that upper bounds are not missing for ``base`` -With large projects with many packages, it would be a lot of work to keep all +For large projects with many packages, it would be a lot of work to keep all package dependency version ranges up to date. Ways of overcoming this are: +- use a bash script to replace or update version ranges in all package descriptions - use a package generator like ``hpack-dhall-cabal`` to import sets of dependencies with version ranges -- use a ``cabal.project`` file to specify version constraints for all dependencies +- use a ``cabal.project`` file to specify version constraints for some or all dependencies - use a curated set of packages and versions and import these into the project Version constraints From 7d0d68207f8e1f2a5cd364acd2661a0354a6e694 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sun, 9 Feb 2025 15:53:53 -0500 Subject: [PATCH 17/61] Add version constraints and exceptions --- doc/how-to-freeze-versions.rst | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index ae02c1bfb58..7cf92b5bcbc 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -25,6 +25,9 @@ Frozen versions to preserve a set of versions found by the solver, a solver-curated set, if you will. +Version exceptions + Allow newer or older dependencies. + Version ranges -------------- @@ -50,10 +53,18 @@ package dependency version ranges up to date. Ways of overcoming this are: Version constraints ------------------- +Version constraints can be applied to the project or command line. + +.. Note:: + + While they do not directly restrain dependency versions, flag constraints + can alter the set of dependencies of a package. + .. Warning:: - Constraints are additive. If I add a constraint it doesn't remove or replace - prior constraints on versions. Constraints don't have override semantics. + Version constraints are additive. If I add a constraint it doesn't remove or + replace prior constraints on versions. Constraints don't have override + semantics. Curated version sets -------------------- From c1e5eb58ac99fab13fdd032f0f89e742c8c20bc4 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sun, 9 Feb 2025 16:17:29 -0500 Subject: [PATCH 18/61] Demo of --with-compiler effect --- doc/how-to-freeze-versions.rst | 46 ++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 7cf92b5bcbc..db18ef67267 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -95,6 +95,52 @@ import, same as the ``.local`` file for projects. ``.local`` file is imported last, after the ``.freeze`` file, giving the user a final say in the setting of any fields that have override semantics. +.. Warning:: + + A project or package that may build with a variety of compiler versions will + be locked to a particular compiler version by freezing. For instance, if + ``cabal init`` is used to create an executable package, + ``unconstrained-base-exe.cabal``, after removing version constraints on base + it can be frozen with various compiler versions that will each bring in + their set of boot libraries as dependencies. + + .. code-block:: cabal + + executable unconstrained-base-exe + build-depends: base >0 + + .. code-block:: diff + + $ cabal freeze --with-compiler=ghc-8.10.7 + $ mv cabal.project.freeze ghc-8.10.7.freeze + + $ cabal freeze --with-compiler=ghc-9.10.1 + $ mv cabal.project.freeze ghc-9.10.1.freeze + + $ cabal freeze --with-compiler=ghc-9.12.1 + $ mv cabal.project.freeze ghc-9.12.1.freeze + + $ diff ghc-8.10.7.freeze ghc-9.12.1.freeze --unified + -constraints: any.base ==4.14.3.0, + - any.ghc-prim ==0.6.1, + - any.integer-gmp ==1.0.3.0, + - any.rts ==1.0.1 + +constraints: any.base ==4.21.0.0, + + any.ghc-bignum ==1.3, + + any.ghc-internal ==9.1201.0, + + any.ghc-prim ==0.13.0, + + any.rts ==1.0.2 + + $ diff ghc-9.10.1.freeze ghc-9.12.1.freeze --unified + -constraints: any.base ==4.20.0.0, + +constraints: any.base ==4.21.0.0, + any.ghc-bignum ==1.3, + - any.ghc-internal ==9.1001.0, + - any.ghc-prim ==0.11.0, + + any.ghc-internal ==9.1201.0, + + any.ghc-prim ==0.13.0, + any.rts ==1.0.2 + Do you need to freeze? ^^^^^^^^^^^^^^^^^^^^^^ From 8303f8a3c3545a8f2eed89b77ae87f20c6417883 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sun, 27 Apr 2025 05:56:05 -0400 Subject: [PATCH 19/61] Typo, not matter Co-Authored-By: Langston Barrett --- doc/how-to-freeze-versions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index db18ef67267..0d99fddee91 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -168,7 +168,7 @@ different architecture or a different compiler version and boot libraries. .. Warning:: - If publishing a package to Hackage, not matter what kind of component it + If publishing a package to Hackage, no matter what kind of component it contains, don't include a ``.freeze`` file, don't add it to any field of the package description that would have ``cabal sdist`` include it in the ``.tar.gz``. In general, don't include anything in the package description From acbe85cf17902a593dfb42b2fc0fa9897e75eb62 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sun, 27 Apr 2025 10:15:23 -0400 Subject: [PATCH 20/61] Redo the freezing workflows --- doc/how-to-freeze-versions.rst | 47 ++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 0d99fddee91..c48377c7596 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -186,33 +186,42 @@ Freezing workflows with. Alternatively, you can run ``cabal update --ignore-project``. -Freeze - If the ``cabal.project.freeze`` file doesn't exist, generating one is a - great way to see what versions of dependencies are currently being used even - if you choose to discard the ``.freeze`` file after inspecting it. +For the versions of all dependencies at once: + +- To check solver-chosen versions - cold snap + + With this workflow we freeze, inspect then discard ``cabal.project.freeze``, + always leaving the solver free to choose other versions. This is great way + to see what versions of dependencies are currently being used. + +- To update versions of all dependencies - thaw and freeze -Thaw, Freeze If you changed the version ranges of any of the dependencies in any of your project's package descriptions, in any ``.cabal`` file, then delete the ``cabal.project.freeze`` file if it already exists and run ``cabal freeze`` - to generate fresh version of ``cabal.project.freeze``. + to generate fresh version of ``cabal.project.freeze``. The steps of this + workflow are delete and freeze. The solver is let free to choose versions of + all dependencies but once it has decided, those versions are immediately + pinned. -Freeze, Freeze (Freezing Harder) - You might in some cases want to skip deletion of ``cabal.project.freeze``, - but keep in mind that in that case ``cabal freeze`` will use existing - ``cabal.project.freeze`` when resolving dependencies, therefore not updating - any existing dependencies, only adding new ones. + This is "thaw and freeze" workflow is the simplest way to work with a + ``cabal.project.freeze`` file committed to source control. -Partial Thaw, Freeze - If you want to a pick up a different version of a single dependency, you can - delete its constraint from ``cabal.project.freeze`` and then run ``cabal - freeze`` again. +For the version of a single dependency: + +- To pin the version of a new dependency -.. Note:: + The steps of this workflow are add the new dependency and freeze. The solver + is free to chose its version for the new dependency while retaining the + already pinned versions for the rest of the dependencies. + +- To update the version of one dependency - If not sure, pick the "thaw, freeze" workflow, as it is the safest, the - simplest and the most common. Finally, you will always want to commit the - changed ``cabal.project.freeze`` to version control. + If you want to a pick up a different version of a single dependency, you can + delete its constraint from ``cabal.project.freeze`` and then run ``cabal + freeze`` again. The steps of this workflow are delete one line and freeze. + It gives the solver the chance to choose another version for the unpinned + dependency. Ensuring everything is frozen ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From d88f6e4d93ff318ae8e78fd99b5386aabdf4420e Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Mon, 7 Jul 2025 08:21:47 -0400 Subject: [PATCH 21/61] Satisfy whitespace --- doc/how-to-freeze-versions.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index c48377c7596..b1d515f7405 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -208,14 +208,14 @@ For the versions of all dependencies at once: ``cabal.project.freeze`` file committed to source control. For the version of a single dependency: - + - To pin the version of a new dependency The steps of this workflow are add the new dependency and freeze. The solver is free to chose its version for the new dependency while retaining the already pinned versions for the rest of the dependencies. -- To update the version of one dependency +- To update the version of one dependency If you want to a pick up a different version of a single dependency, you can delete its constraint from ``cabal.project.freeze`` and then run ``cabal From e3c41db677b7b26195404e54394ebb3a0175894d Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 27 Jan 2026 10:50:29 -0500 Subject: [PATCH 22/61] Typo s/chose its/choose a/ Co-authored-by: Langston Barrett --- doc/how-to-freeze-versions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index b1d515f7405..aaace6d54da 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -212,7 +212,7 @@ For the version of a single dependency: - To pin the version of a new dependency The steps of this workflow are add the new dependency and freeze. The solver - is free to chose its version for the new dependency while retaining the + is free to choose a version for the new dependency while retaining the already pinned versions for the rest of the dependencies. - To update the version of one dependency From d29055ef9cad739d8b9816e5f2eaa73fa8a8f111 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 27 Jan 2026 10:51:12 -0500 Subject: [PATCH 23/61] Typo s/This is/This/ Co-authored-by: Langston Barrett --- doc/how-to-freeze-versions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index aaace6d54da..4c68738049c 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -204,7 +204,7 @@ For the versions of all dependencies at once: all dependencies but once it has decided, those versions are immediately pinned. - This is "thaw and freeze" workflow is the simplest way to work with a + This "thaw and freeze" workflow is the simplest way to work with a ``cabal.project.freeze`` file committed to source control. For the version of a single dependency: From eb6fb6f412eae2064ce8cf20fb51c4c6842e1b23 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 27 Jan 2026 12:01:56 -0500 Subject: [PATCH 24/61] Add mandatory version constraints --- doc/how-to-freeze-versions.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 4c68738049c..4624533112f 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -12,6 +12,10 @@ Version constraints Within a project, version constraints for dependencies limit the versions that the solver can pick. +Mandatory version constraints + With :cfg-field:`reject-unconstrained-dependencies` set to ``all`` + dependencies must have version constraints. + Curated version sets A project can import curated sets of packages and versions that are known to work together, such as those Stackage provides for Cabal's use. From 49608dc25ec426403fca0cf821f51c5b9545b3f8 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 27 Jan 2026 12:19:16 -0500 Subject: [PATCH 25/61] Warn about reject-unconstrained --- doc/how-to-freeze-versions.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 4624533112f..60b6890c330 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -238,6 +238,11 @@ Ensuring everything is frozen cannot change and, at that point when every dependency is frozen, ``cabal freeze`` becomes an idempotent operation. +.. Warning:: + Setting :cfg-field:`reject-unconstrained-dependencies` to ``all`` will + ensure that dependencies have version constraints but does not require these + be equality constraints. + Adding a dependency to one of the packages in a project without freezing harder leaves the newly added dependency susceptible to getting updated unexpectedly when the solver can find a different version for it. Running ``cabal freeze`` From 1f80dccefea3608e411a2e7e74cd9b876fa59d75 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Thu, 26 Mar 2026 11:28:55 -0400 Subject: [PATCH 26/61] Versions and flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: ˌbodʲɪˈɡrʲim --- doc/cabal-commands.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/cabal-commands.rst b/doc/cabal-commands.rst index 2d86d70bebc..9847f8006fb 100644 --- a/doc/cabal-commands.rst +++ b/doc/cabal-commands.rst @@ -517,7 +517,7 @@ cabal freeze generates ``cabal.project.freeze`` file, which describes the exact dependency tree as it was resolved at that moment by Cabal. This means it captures an -exact version of every dependency, including dependencies of dependencies, +exact versions and flags of every dependency, including dependencies of dependencies, recursively all the way. Since ``cabal`` reads ``cabal.project.freeze`` when present, and takes into From 0bfa00d50297f62851fc97b48312e89223c6d1d1 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Thu, 26 Mar 2026 12:02:45 -0400 Subject: [PATCH 27/61] Add LTS and nightly package set examples --- doc/how-to-freeze-versions.rst | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 60b6890c330..f9825659068 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -18,7 +18,7 @@ Mandatory version constraints Curated version sets A project can import curated sets of packages and versions that are known to - work together, such as those Stackage provides for Cabal's use. + work together. Capped repository versions Adding ``index-state`` to a project limits versions coming from Hackage to @@ -75,8 +75,25 @@ Curated version sets Stackage provides curated sets of packages and versions that are known to work together and are updated regularly. The latest resolver is the nightly and this -typically lags a bit behind the latest available GHC version. The LTS resolvers -will each follow a specific GHC version and are updated less frequently. +typically lags a bit behind the latest available GHC version. The long term +support (LTS) resolvers will each follow a specific GHC version and are updated +less frequently. + +- https://www.stackage.org/lts-24.34/cabal.config +- https://www.stackage.org/nightly-2026-03-25/cabal.config + +These ``*.config`` files set the compiler version and warn about cabal not +yet having revisions. + +.. code-block:: + + $ curl -s https://www.stackage.org/lts-24.34/cabal.config | grep "with-compiler" + with-compiler: ghc-9.10.3 + +If importing directly causes a version conflict, we recommend; downloading +the package set locally, committing to source control and then commenting +out conflicting package constraint lines. Each constraint is on its own line +and can be prefixed with ``--`` to comment it out. Capped repository versions -------------------------- From e3d46e7100be23a0e9d84961ac3bcb8d96d15dbf Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Thu, 26 Mar 2026 12:06:02 -0400 Subject: [PATCH 28/61] Add a ref to curated version sets --- doc/how-to-freeze-versions.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index f9825659068..0a40ba58a54 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -16,7 +16,7 @@ Mandatory version constraints With :cfg-field:`reject-unconstrained-dependencies` set to ``all`` dependencies must have version constraints. -Curated version sets +:ref:`Curated version sets ` A project can import curated sets of packages and versions that are known to work together. @@ -70,6 +70,8 @@ Version constraints can be applied to the project or command line. replace prior constraints on versions. Constraints don't have override semantics. +.. _curated_version_sets: + Curated version sets -------------------- From 6eda244dcd3790b98b12caba55e441d18aa64549 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Thu, 26 Mar 2026 12:18:48 -0400 Subject: [PATCH 29/61] Add ref links from definition list to sections --- doc/how-to-freeze-versions.rst | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 0a40ba58a54..4dac7f53273 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -4,11 +4,11 @@ How to control versions There are various ways and places to limit what versions the solver can pick for dependencies. -Version ranges +:ref:`Version ranges ` Within a package description, version ranges for dependencies can be; tight or loose or missing altogether. -Version constraints +:ref:`Version constraints ` Within a project, version constraints for dependencies limit the versions that the solver can pick. @@ -16,22 +16,24 @@ Mandatory version constraints With :cfg-field:`reject-unconstrained-dependencies` set to ``all`` dependencies must have version constraints. -:ref:`Curated version sets ` +:ref:`Curated version sets ` A project can import curated sets of packages and versions that are known to work together. -Capped repository versions +:ref:`Capped repository versions ` Adding ``index-state`` to a project limits versions coming from Hackage to include only those that were available at the cutoff time. -Frozen versions +:ref:`Frozen versions ` Pins the versions picked by the solver for all dependencies. This is a way to preserve a set of versions found by the solver, a solver-curated set, if you will. -Version exceptions +:ref:`Version exceptions ` Allow newer or older dependencies. +.. _version-ranges: + Version ranges -------------- @@ -54,6 +56,8 @@ package dependency version ranges up to date. Ways of overcoming this are: - use a ``cabal.project`` file to specify version constraints for some or all dependencies - use a curated set of packages and versions and import these into the project +.. _version-constraints: + Version constraints ------------------- @@ -70,7 +74,7 @@ Version constraints can be applied to the project or command line. replace prior constraints on versions. Constraints don't have override semantics. -.. _curated_version_sets: +.. _curated-versions: Curated version sets -------------------- @@ -97,10 +101,12 @@ the package set locally, committing to source control and then commenting out conflicting package constraint lines. Each constraint is on its own line and can be prefixed with ``--`` to comment it out. +.. _capped-versions: + Capped repository versions -------------------------- -.. _freeze-versions: +.. _frozen-versions: Frozen versions --------------- @@ -282,3 +288,10 @@ versions are frozen. cabal freeze || exit 1 NEW_FREEZE_SUM=$(md5sum cabal.project.freeze) exit [[ "$NEW_FREEZE_SUM" == "$OLD_FREEZE_SUM" ]] + +.. _version-exceptions: + +Version Exceptions +^^^^^^^^^^^^^^^^^^ + +Version constraints can be lifted with ``allow-newer`` and ``allow-older``. From c16cf9ade7c32efe18a78c08fc25cd2d18fd87f3 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Thu, 26 Mar 2026 12:34:43 -0400 Subject: [PATCH 30/61] Use directory as example of flags changing deps --- doc/how-to-freeze-versions.rst | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 4dac7f53273..d7964c1db59 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -66,7 +66,28 @@ Version constraints can be applied to the project or command line. .. Note:: While they do not directly restrain dependency versions, flag constraints - can alter the set of dependencies of a package. + can alter the set of dependencies of a package. This can happen when a + dependency is behind a flag. For example, the `directory + `_ + package description has a flag that brings in the `os-string + `_ as a dependency and + changes the version range for `filepath + `_: + + .. code-block:: cabal + + name: directory + + flag os-string + description: Use the new os-string package + default: False + manual: False + + library + if flag(os-string) + build-depends: filepath >= 1.5.0.0, os-string >= 2.0.0 + else + build-depends: filepath >= 1.4.100.0 && < 1.5.0.0 .. Warning:: From a752554b1ef9b6aa7889fc72e14d0257e709f14a Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Thu, 26 Mar 2026 12:54:00 -0400 Subject: [PATCH 31/61] Add example of missing upper bound on base --- doc/how-to-freeze-versions.rst | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index d7964c1db59..8f2cb4746f8 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -48,6 +48,38 @@ on: - that upper bounds are not missing - that upper bounds are not missing for ``base`` +.. Note:: + + Missing an upper bound on ``base`` is more serious than it is for other + packages: + + - when upper bounds are missing for ``base``: + + .. code-block:: text + + $ cabal check + The following errors will cause portability problems on other environments: + Error: [missing-bounds-important] The dependency 'build-depends: base' does + not specify an upper bound on the version number. Each major release of the + 'base' package changes the API in various ways and most packages will need + some changes to compile with it. The recommended practice is to specify an + upper bound on the version of the 'base' package. This ensures your package + will continue to build when a new major version of the 'base' package is + released. If you are not sure what upper bound to use then use the next major + version. For example if you have tested your package with 'base' version 4.5 + and 4.6 then use 'build-depends: base >= 4.5 && < 4.7'. + Error: Hackage would reject this package. + + - when upper bounds are missing on ``array``: + + .. code-block:: text + + $ cabal check + These warnings may cause trouble when distributing the package: + Warning: [missing-upper-bounds] On library, these packages miss upper bounds: + - array + Please add them. There is more information at https://pvp.haskell.org/ + For large projects with many packages, it would be a lot of work to keep all package dependency version ranges up to date. Ways of overcoming this are: From a2ddf0be651a7a1ea20b97da51a387027bb89977 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Thu, 26 Mar 2026 13:25:26 -0400 Subject: [PATCH 32/61] Say the order is worse to better - link to exe hpack-dhall-cabal --- doc/how-to-freeze-versions.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 8f2cb4746f8..43b42ceaf18 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -81,10 +81,11 @@ on: Please add them. There is more information at https://pvp.haskell.org/ For large projects with many packages, it would be a lot of work to keep all -package dependency version ranges up to date. Ways of overcoming this are: +package dependency version ranges up to date. Ways of overcoming, in worse to +better order, are: - use a bash script to replace or update version ranges in all package descriptions -- use a package generator like ``hpack-dhall-cabal`` to import sets of dependencies with version ranges +- use a package generator like `dhall-hpack-cabal `_ to import sets of dependencies with version ranges - use a ``cabal.project`` file to specify version constraints for some or all dependencies - use a curated set of packages and versions and import these into the project From b8d842d8e9e85359ae5f96e32b815b2e72bd496a Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Thu, 26 Mar 2026 13:39:15 -0400 Subject: [PATCH 33/61] Move mandated versions to its own section --- doc/how-to-freeze-versions.rst | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 43b42ceaf18..4ed7f04985d 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -10,11 +10,7 @@ dependencies. :ref:`Version constraints ` Within a project, version constraints for dependencies limit the versions - that the solver can pick. - -Mandatory version constraints - With :cfg-field:`reject-unconstrained-dependencies` set to ``all`` - dependencies must have version constraints. + that the solver can pick. These can be :ref:`mandated `. :ref:`Curated version sets ` A project can import curated sets of packages and versions that are known to @@ -128,6 +124,17 @@ Version constraints can be applied to the project or command line. replace prior constraints on versions. Constraints don't have override semantics. +.. _mandated-versions: + +Mandated version constraints +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Version constraints can be mandated with +:cfg-field:`reject-unconstrained-dependencies`. When this flag is set to +``all``, each dependency must have some version constraint, even if this is just +``>0``. This flag can be made stricter and set to ``eq``, requiring each +dependency to have an equality version constraint. + .. _curated-versions: Curated version sets From 6ce66fc5e041fb06bca9f58b687af084184548d3 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Fri, 27 Mar 2026 07:46:46 -0400 Subject: [PATCH 34/61] Add a section, places for versioning --- doc/how-to-freeze-versions.rst | 36 +++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 4ed7f04985d..e27c1cf1459 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -2,7 +2,7 @@ How to control versions ======================= There are various ways and places to limit what versions the solver can pick for -dependencies. +dependencies. Ways to control versions are: :ref:`Version ranges ` Within a package description, version ranges for dependencies can be; tight @@ -28,6 +28,23 @@ dependencies. :ref:`Version exceptions ` Allow newer or older dependencies. +Places to control versions are the files and command line options related to +versioning but how to use these also depend on what we're building. If we're +building a product for its own sake then we're free to manage versions wherever +and however we please. If we're publishing a package to Hackage then that +platform places restrictions on how loose we can be when specifying +dependencies. Before publishing to Hackage, ``cabal check`` will warn about +loose versioning that Hackage will reject. + +:ref:`Hackage versions ` + Not all dependency version ranges are accepted by Hackage. After publishing, + a package on Hackage can be revised to change version ranges of its + dependencies. + +:ref:`Project versions ` + Almost any way and any place imaginable for setting versions is available + when configuring a project. + .. _version-ranges: Version ranges @@ -356,3 +373,20 @@ Version Exceptions ^^^^^^^^^^^^^^^^^^ Version constraints can be lifted with ``allow-newer`` and ``allow-older``. + +Places for Versioning +--------------------- + +.. _hackage-versions: + +Hackage versions +^^^^^^^^^^^^^^^^^^ + +TODO + +.. _project-versions: + +Project versions +^^^^^^^^^^^^^^^^^^ + +TODO From 15036080b13742772c753731fa41a5d4b16fed1c Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Fri, 27 Mar 2026 07:54:07 -0400 Subject: [PATCH 35/61] Demote to note, order of .freeze and .local --- doc/how-to-freeze-versions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index e27c1cf1459..6c21ed54569 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -196,7 +196,7 @@ with a extra ``.freeze`` extension, so the freeze file for ``cabal.project`` is ``cabal.project.freeze``. Effectively a ``.freeze`` file is an implicit project import, same as the ``.local`` file for projects. -.. Warning:: +.. Note:: The order of imports of ``.local`` and ``.freeze`` files is important. The ``.local`` file is imported last, after the ``.freeze`` file, giving the From d4448f18328b44d750c3a46233b507ca867fba06 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Fri, 27 Mar 2026 08:03:28 -0400 Subject: [PATCH 36/61] Use .project and .config extensions in general --- doc/how-to-freeze-versions.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 6c21ed54569..b48247972c2 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -202,6 +202,12 @@ import, same as the ``.local`` file for projects. ``.local`` file is imported last, after the ``.freeze`` file, giving the user a final say in the setting of any fields that have override semantics. + The ``.local`` and ``.freeze`` extensions are specialized and quirky. In + general, it is better to use a ``.project`` extension for any project and a + ``.config`` extension for any project configuration that is imported. It is + of course possible to have multiple ``.project`` files and for these to have + import relationships. + .. Warning:: A project or package that may build with a variety of compiler versions will From c7cf83fe4066e702df10b9acec9afd2f608665a4 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Fri, 27 Mar 2026 08:22:02 -0400 Subject: [PATCH 37/61] Move lib versus exe .freeze distribution - Add project environment section --- doc/how-to-freeze-versions.rst | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index b48247972c2..fb27bb495aa 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -383,16 +383,30 @@ Version constraints can be lifted with ``allow-newer`` and ``allow-older``. Places for Versioning --------------------- +For end-user executables, it is recommended that you distribute the +``cabal.project.freeze`` file in your source repository so that all users see a +consistent set of dependencies. For libraries, this is not recommended: users +often need to build against different versions of libraries than what you +developed against. + .. _hackage-versions: Hackage versions -^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^ TODO .. _project-versions: Project versions -^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^ TODO + +Project environments +^^^^^^^^^^^^^^^^^^^^ + +If a package is built in several environments, such as a development +environment, a staging environment and a production environment, it may be +necessary or desirable to ensure that the same dependency versions are selected +in each environment. This can be done with a **freeze file**. From f9bea1cdd6a526c07f107e0453542b6af152aecb Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Fri, 27 Mar 2026 08:49:46 -0400 Subject: [PATCH 38/61] Revert cabal freeze command - Keep cabal freeze command simple - Move details to freeze guide and link to guide --- doc/cabal-commands.rst | 34 +++++++++++++++++++++++----------- doc/how-to-freeze-versions.rst | 31 ++++++++++++++++++++++++------- 2 files changed, 47 insertions(+), 18 deletions(-) diff --git a/doc/cabal-commands.rst b/doc/cabal-commands.rst index 9847f8006fb..046e497b807 100644 --- a/doc/cabal-commands.rst +++ b/doc/cabal-commands.rst @@ -508,6 +508,8 @@ is in place, which moves the old configuration to a ``cabal.project.local~`` file, this feature can also be disabled by using the ``--disable-backup`` flag. +.. _cabal-freeze: + cabal freeze ^^^^^^^^^^^^ @@ -515,19 +517,29 @@ cabal freeze $ cabal freeze -generates ``cabal.project.freeze`` file, which describes the exact dependency -tree as it was resolved at that moment by Cabal. This means it captures an -exact versions and flags of every dependency, including dependencies of dependencies, -recursively all the way. +``cabal freeze`` writes out a **freeze file** which records all of +the versions and flags that are picked by the solver under the +current index and flags. Default name of this file is +``cabal.project.freeze`` but in combination with a +``--project-file=my.project`` flag (see :ref:`project-file +`) +the name will be ``my.project.freeze``. +A freeze file has the same syntax as ``cabal.project`` and looks +something like this: + +.. highlight:: cabal + +:: -Since ``cabal`` reads ``cabal.project.freeze`` when present, and takes into -consideration the version constraints in it, this means that by producing -``cabal.project.freeze`` you are guaranteed that every future ``cabal`` call -will use the exact same set of dependencies, regardless of any updates (even -patches) that might get published for these dependencies in the meantime. -Therefore, we have effectively "frozen" the dependencies in place. + constraints: HTTP ==4000.3.3, + HTTP +warp-tests -warn-as-error -network23 +network-uri -mtl1 -conduit10, + QuickCheck ==2.9.1, + QuickCheck +templatehaskell, + -- etc... -``cabal.project.freeze`` is intended to be committed to the version control. +For more detail, please see the guide on how to :ref:`control versions +` with its section on :ref:`freezing +`. .. _cabal-gen-bounds: diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index fb27bb495aa..f9b0d92a36f 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -1,3 +1,5 @@ +.. _how-to-control-versions: + How to control versions ======================= @@ -189,12 +191,17 @@ Capped repository versions Frozen versions --------------- -Pinning adds a version equality constraint for each package in the set of -project dependencies, explicit and transitive. The ``cabal freeze`` command -saves these to a file named the same as the whole of the project file name but -with a extra ``.freeze`` extension, so the freeze file for ``cabal.project`` is -``cabal.project.freeze``. Effectively a ``.freeze`` file is an implicit project -import, same as the ``.local`` file for projects. +The :ref:`cabal freeze ` command generates **freeze file** that +describes the exact dependency tree as it was resolved at that moment by Cabal's +dependency solver. It captures exact versions and flags of every dependency, +including dependencies of dependencies, exhaustively and recursively. + +In practice, freezing adds a version equality constraint for each package in the +set of project dependencies, explicit and transitive. The ``cabal freeze`` +command saves these to a file named the same as the whole of the project file +name but with a extra ``.freeze`` extension, so the freeze file for +``cabal.project`` is ``cabal.project.freeze``. Effectively a ``.freeze`` file is +an implicit project import, same as the ``.local`` file for projects. .. Note:: @@ -401,7 +408,17 @@ TODO Project versions ^^^^^^^^^^^^^^^^ -TODO +The unit of distribution to Hackage is the package. Projects are shared by +*source code* repository. + +Since ``cabal`` reads ``cabal.project.freeze`` when present, and takes into +consideration the version constraints in it, this means that by producing +``cabal.project.freeze`` you are guaranteed that every future ``cabal`` call +will use the exact same set of dependencies, regardless of any updates (even +patches) that might get published for these dependencies in the meantime. +Therefore, we have effectively "frozen" the dependencies in place. + +``cabal.project.freeze`` is intended to be committed to the version control. Project environments ^^^^^^^^^^^^^^^^^^^^ From d2fdde34c286419b0d2991e3e926343a049e941b Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Fri, 27 Mar 2026 09:45:23 -0400 Subject: [PATCH 39/61] Add an example of hackage dependencies --- doc/how-to-freeze-versions.rst | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index f9b0d92a36f..72903928247 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -401,15 +401,28 @@ developed against. Hackage versions ^^^^^^^^^^^^^^^^ -TODO +The unit of distribution to Hackage is the package but each package component +has their own dependencies. The Hackage website shows dependencies in summary +form, like those for `hpack-dhall-0.5.7 +`_, a package with +multiple executables. Clicking on the "[details]" link at the end of the +summary of dependencies list will show the dependencies for each component: + +.. raw:: html + +
+ Dependencies
+
+ aeson, aeson-pretty, base (>=4.13 && <5), bytestring, dhall (>=1.41.1), dhall-json (>=1.7.10), filepath, hpack (>=0.35 && <0.36), megaparsec (>=9.2), microlens, optparse-applicative, prettyprinter, text, transformers, yaml [details] +
+
.. _project-versions: Project versions ^^^^^^^^^^^^^^^^ -The unit of distribution to Hackage is the package. Projects are shared by -*source code* repository. +Projects are shared by *source code* repository. Since ``cabal`` reads ``cabal.project.freeze`` when present, and takes into consideration the version constraints in it, this means that by producing From 285ffe94e9e8601c54403875d5f0696cf681d270 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Fri, 27 Mar 2026 10:33:58 -0400 Subject: [PATCH 40/61] Typo, commit to *the* version control Co-authored-by: brandon s allbery kf8nh --- doc/how-to-freeze-versions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 72903928247..ba76535c878 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -431,7 +431,7 @@ will use the exact same set of dependencies, regardless of any updates (even patches) that might get published for these dependencies in the meantime. Therefore, we have effectively "frozen" the dependencies in place. -``cabal.project.freeze`` is intended to be committed to the version control. +``cabal.project.freeze`` is intended to be committed to version control. Project environments ^^^^^^^^^^^^^^^^^^^^ From 2bee3b21ef0eb2ec0f37cbc1d04a3706402db50e Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Fri, 27 Mar 2026 10:34:33 -0400 Subject: [PATCH 41/61] Typo the solver is *let* free Co-authored-by: brandon s allbery kf8nh --- doc/how-to-freeze-versions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index ba76535c878..d0c980a055b 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -320,7 +320,7 @@ For the versions of all dependencies at once: project's package descriptions, in any ``.cabal`` file, then delete the ``cabal.project.freeze`` file if it already exists and run ``cabal freeze`` to generate fresh version of ``cabal.project.freeze``. The steps of this - workflow are delete and freeze. The solver is let free to choose versions of + workflow are delete and freeze. The solver is left free to choose versions of all dependencies but once it has decided, those versions are immediately pinned. From 28bac5d95b3c5fac03b0eb018da6cf5206b9fa88 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Wed, 3 Jun 2026 15:59:47 -0400 Subject: [PATCH 42/61] Bold short descriptions and ref to freeze-harder --- doc/how-to-freeze-versions.rst | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index d0c980a055b..2bbc27b9a6a 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -308,13 +308,13 @@ Freezing workflows For the versions of all dependencies at once: -- To check solver-chosen versions - cold snap +- To check solver-chosen versions - **cold snap** With this workflow we freeze, inspect then discard ``cabal.project.freeze``, always leaving the solver free to choose other versions. This is great way to see what versions of dependencies are currently being used. -- To update versions of all dependencies - thaw and freeze +- To update versions of all dependencies - **thaw and freeze** If you changed the version ranges of any of the dependencies in any of your project's package descriptions, in any ``.cabal`` file, then delete the @@ -329,13 +329,15 @@ For the versions of all dependencies at once: For the version of a single dependency: -- To pin the version of a new dependency +.. _freeze-harder: + +- To pin the version of a new dependency - **freeze harder** The steps of this workflow are add the new dependency and freeze. The solver is free to choose a version for the new dependency while retaining the already pinned versions for the rest of the dependencies. -- To update the version of one dependency +- To update the version of one dependency - **partial thaw and freeze** If you want to a pick up a different version of a single dependency, you can delete its constraint from ``cabal.project.freeze`` and then run ``cabal @@ -359,12 +361,12 @@ Ensuring everything is frozen ensure that dependencies have version constraints but does not require these be equality constraints. -Adding a dependency to one of the packages in a project without freezing harder -leaves the newly added dependency susceptible to getting updated unexpectedly -when the solver can find a different version for it. Running ``cabal freeze`` -will show this vulnerability to a human or an automated check that notices a new -version equality constraint in the ``.freeze`` file, a constraint for a package -that wasn't in the ``.freeze`` file before. +Adding a dependency to one of the packages in a project without :ref:`freezing +harder` leaves the newly added dependency susceptible to getting +updated unexpectedly when the solver can find a different version for it. +Running ``cabal freeze`` will show this vulnerability to a human or an automated +check that notices a new version equality constraint in the ``.freeze`` file, a +constraint for a package that wasn't in the ``.freeze`` file before. To automate this check, make it a part of continuous integration or make a pre-commit hook for it. A simple check for this might be to compare the md5sum From 752a8b0b88540f97b9815591384c812ddbc642ac Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Wed, 3 Jun 2026 16:10:50 -0400 Subject: [PATCH 43/61] New introductory sentence for adding a dep --- doc/how-to-freeze-versions.rst | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 2bbc27b9a6a..ff70f400245 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -361,12 +361,16 @@ Ensuring everything is frozen ensure that dependencies have version constraints but does not require these be equality constraints. -Adding a dependency to one of the packages in a project without :ref:`freezing -harder` leaves the newly added dependency susceptible to getting -updated unexpectedly when the solver can find a different version for it. -Running ``cabal freeze`` will show this vulnerability to a human or an automated -check that notices a new version equality constraint in the ``.freeze`` file, a -constraint for a package that wasn't in the ``.freeze`` file before. +When adding a new dependency to a project that utilizes a freeze file +[#add-new-dep]_, regenerating the freeze file using one of the methods described +above ensures that the freeze file contains a constraint for the new dependency. + +Not :ref:`freezing harder` leaves the newly added dependency +susceptible to getting updated unexpectedly when the solver can find a different +version for it. Running ``cabal freeze`` will show this vulnerability to a +human or an automated check that notices a new version equality constraint in +the ``.freeze`` file, a constraint for a package that wasn't in the ``.freeze`` +file before. To automate this check, make it a part of continuous integration or make a pre-commit hook for it. A simple check for this might be to compare the md5sum @@ -382,6 +386,8 @@ versions are frozen. NEW_FREEZE_SUM=$(md5sum cabal.project.freeze) exit [[ "$NEW_FREEZE_SUM" == "$OLD_FREEZE_SUM" ]] +.. [#add-new-dep] By adding a dependency to one of the packages in the project. + .. _version-exceptions: Version Exceptions From e39fcc27273b82764d597774ba65b716ec1e203d Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Wed, 3 Jun 2026 16:31:14 -0400 Subject: [PATCH 44/61] Add a tip for different compiler versions --- doc/how-to-freeze-versions.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index ff70f400245..b36c89eb3ff 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -441,6 +441,20 @@ Therefore, we have effectively "frozen" the dependencies in place. ``cabal.project.freeze`` is intended to be committed to version control. +.. Tip:: + + To freeze but also build your project with different versions of GHC; fix + the compiler version using ``with-compiler`` field and freeze each project + separately using the ``--project-file`` option. + + .. code-block:: bash + + $ cabal freeze --project-file=cabal.ghc-9.10.3.project + $ cabal freeze --project-file=cabal.ghc-9.12.4.project + + Common configuration can be shared between projects by using project + imports. + Project environments ^^^^^^^^^^^^^^^^^^^^ From 0f0694a8b158a9be949d29a66e59607d941f554a Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Wed, 3 Jun 2026 16:35:57 -0400 Subject: [PATCH 45/61] Link to "with-compiler" and "project-file" --- doc/how-to-freeze-versions.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index b36c89eb3ff..61043376e92 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -444,9 +444,10 @@ Therefore, we have effectively "frozen" the dependencies in place. .. Tip:: To freeze but also build your project with different versions of GHC; fix - the compiler version using ``with-compiler`` field and freeze each project - separately using the ``--project-file`` option. - + the compiler version using :cfg-field:`with-compiler` field and freeze each + project separately using the :ref:`project-file` + option. + .. code-block:: bash $ cabal freeze --project-file=cabal.ghc-9.10.3.project From 6bc64af97759bf8db46c21f44d1a14118e06f15d Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Wed, 3 Jun 2026 16:49:14 -0400 Subject: [PATCH 46/61] Satisfy fix-whitespace --- doc/how-to-freeze-versions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 61043376e92..6416c66198a 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -447,7 +447,7 @@ Therefore, we have effectively "frozen" the dependencies in place. the compiler version using :cfg-field:`with-compiler` field and freeze each project separately using the :ref:`project-file` option. - + .. code-block:: bash $ cabal freeze --project-file=cabal.ghc-9.10.3.project From 9454310e35989bf6f8fb525c480146457ed0e7a1 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sat, 6 Jun 2026 11:02:56 -0400 Subject: [PATCH 47/61] Misplaced semicolon --- doc/how-to-freeze-versions.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 6416c66198a..f46406066d3 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -7,8 +7,8 @@ There are various ways and places to limit what versions the solver can pick for dependencies. Ways to control versions are: :ref:`Version ranges ` - Within a package description, version ranges for dependencies can be; tight - or loose or missing altogether. + Within a package description, version ranges for dependencies can be tight, + loose, or missing altogether. :ref:`Version constraints ` Within a project, version constraints for dependencies limit the versions From 1533833f9d55a4e1e2ea0418de02cde990f30430 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sat, 6 Jun 2026 11:05:35 -0400 Subject: [PATCH 48/61] Remove unneeded semicolon --- doc/how-to-freeze-versions.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index f46406066d3..a8df7e48845 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -176,10 +176,10 @@ yet having revisions. $ curl -s https://www.stackage.org/lts-24.34/cabal.config | grep "with-compiler" with-compiler: ghc-9.10.3 -If importing directly causes a version conflict, we recommend; downloading -the package set locally, committing to source control and then commenting -out conflicting package constraint lines. Each constraint is on its own line -and can be prefixed with ``--`` to comment it out. +If importing directly causes a version conflict, we recommend downloading the +package set locally, committing it to source control, and then commenting out +conflicting package constraint lines. Each constraint is on its own line and can +be prefixed with ``--`` to comment it out. .. _capped-versions: From 00882dc49e7bdbd3a6cd4fa1cf82cf61e65c80b0 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sat, 6 Jun 2026 11:36:23 -0400 Subject: [PATCH 49/61] Say a lot more about accumulating constraints --- doc/how-to-freeze-versions.rst | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index a8df7e48845..a92c9171dad 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -139,9 +139,24 @@ Version constraints can be applied to the project or command line. .. Warning:: - Version constraints are additive. If I add a constraint it doesn't remove or - replace prior constraints on versions. Constraints don't have override - semantics. + Version constraints accumulate! + + These are often set in a ``.cabal`` file as a version range within the + ``build-depends`` stanza. + + There are other ways to supply version constraints on a dependency; in a + project file or one of its imports and on the command line. Constraints for + the same dependency can be given multiple times in the project or on the + command line. + + Adding a constraint via any of these methods does not remove or replace any + of the constraints on the same dependency set elsewhere; no method or order + of supply has priority and constraints given one way cannot and do not + override other constraints. + + Before dependency solving begins, all version constraints for each + dependency given by any method are combined with the ``&&`` operator into + one effective version range for that dependency. .. _mandated-versions: From 1e02e97e4332d22446baa9a9ac1bca6d59067e5c Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sat, 20 Jun 2026 15:26:52 -0400 Subject: [PATCH 50/61] Limitations of cabal freeze --- doc/how-to-freeze-versions.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index a92c9171dad..344140c3188 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -454,7 +454,13 @@ will use the exact same set of dependencies, regardless of any updates (even patches) that might get published for these dependencies in the meantime. Therefore, we have effectively "frozen" the dependencies in place. -``cabal.project.freeze`` is intended to be committed to version control. +There's a lot to consider before deciding to add a ``cabal.project.freeze`` file +to source control due to limitations of ``cabal freeze``. A freeze is specific +for a single Cabal dependency solver run within the current environment. A good +many packages have platform-specific dependencies and will vary their +dependencies based on the operating system. The index state of the local package +index will also change the list of possible dependencies available to the +solver. .. Tip:: From 0e67fd709f7bdd2d86e317c8f19c46dc3263423a Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sat, 20 Jun 2026 15:31:33 -0400 Subject: [PATCH 51/61] Not a true lock file --- doc/how-to-freeze-versions.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 344140c3188..5bc362cc940 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -462,6 +462,11 @@ dependencies based on the operating system. The index state of the local package index will also change the list of possible dependencies available to the solver. +The solver can only solve for the platform and environment that it is running on +and within so the generated ``cabal.project.freeze`` file has no ``os(name)``, +``arch(name)`` or ``impl(compiler)`` conditionals. This is why a ``.freeze`` +file is not a true lock file. + .. Tip:: To freeze but also build your project with different versions of GHC; fix From 3389359b6d7eddd1b0334be31e6508bc4e5a8597 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sat, 20 Jun 2026 15:36:21 -0400 Subject: [PATCH 52/61] When to commit a freeze file to source control --- doc/how-to-freeze-versions.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 5bc362cc940..dd4e033d342 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -467,6 +467,10 @@ and within so the generated ``cabal.project.freeze`` file has no ``os(name)``, ``arch(name)`` or ``impl(compiler)`` conditionals. This is why a ``.freeze`` file is not a true lock file. +For a software product that is developed and built for production on the same +platform and environment, committing the freeze file to source control may make +sense. + .. Tip:: To freeze but also build your project with different versions of GHC; fix From efdd9655bf3571ad58e0f8c7da33a8a81f3471be Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sat, 20 Jun 2026 15:40:20 -0400 Subject: [PATCH 53/61] Add a "Freezing limitations" section --- doc/how-to-freeze-versions.rst | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index dd4e033d342..72fbc4cf183 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -454,6 +454,18 @@ will use the exact same set of dependencies, regardless of any updates (even patches) that might get published for these dependencies in the meantime. Therefore, we have effectively "frozen" the dependencies in place. +Project environments +^^^^^^^^^^^^^^^^^^^^ + +If a package is built in several environments, such as a development +environment, a staging environment and a production environment, it may be +necessary or desirable to ensure that the same dependency versions are selected +in each environment. While this can be done with a **freeze file** there are +limitations. + +Freezing limitations +^^^^^^^^^^^^^^^^^^^^ + There's a lot to consider before deciding to add a ``cabal.project.freeze`` file to source control due to limitations of ``cabal freeze``. A freeze is specific for a single Cabal dependency solver run within the current environment. A good @@ -485,11 +497,3 @@ sense. Common configuration can be shared between projects by using project imports. - -Project environments -^^^^^^^^^^^^^^^^^^^^ - -If a package is built in several environments, such as a development -environment, a staging environment and a production environment, it may be -necessary or desirable to ensure that the same dependency versions are selected -in each environment. This can be done with a **freeze file**. From c62607242155e597df13a8828f18d12e6913ca19 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sat, 20 Jun 2026 16:04:36 -0400 Subject: [PATCH 54/61] Add a limitations section --- doc/how-to-freeze-versions.rst | 66 +++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 72fbc4cf183..bce06c8ea37 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -454,35 +454,6 @@ will use the exact same set of dependencies, regardless of any updates (even patches) that might get published for these dependencies in the meantime. Therefore, we have effectively "frozen" the dependencies in place. -Project environments -^^^^^^^^^^^^^^^^^^^^ - -If a package is built in several environments, such as a development -environment, a staging environment and a production environment, it may be -necessary or desirable to ensure that the same dependency versions are selected -in each environment. While this can be done with a **freeze file** there are -limitations. - -Freezing limitations -^^^^^^^^^^^^^^^^^^^^ - -There's a lot to consider before deciding to add a ``cabal.project.freeze`` file -to source control due to limitations of ``cabal freeze``. A freeze is specific -for a single Cabal dependency solver run within the current environment. A good -many packages have platform-specific dependencies and will vary their -dependencies based on the operating system. The index state of the local package -index will also change the list of possible dependencies available to the -solver. - -The solver can only solve for the platform and environment that it is running on -and within so the generated ``cabal.project.freeze`` file has no ``os(name)``, -``arch(name)`` or ``impl(compiler)`` conditionals. This is why a ``.freeze`` -file is not a true lock file. - -For a software product that is developed and built for production on the same -platform and environment, committing the freeze file to source control may make -sense. - .. Tip:: To freeze but also build your project with different versions of GHC; fix @@ -497,3 +468,40 @@ sense. Common configuration can be shared between projects by using project imports. + +Freezing limitations +^^^^^^^^^^^^^^^^^^^^ + +A freeze is specific for a single Cabal dependency solver run within the current +environment. There's a lot to consider before deciding to add a +``cabal.project.freeze`` file to source control due to limitations of ``cabal +freeze``. + +Environment Limitation + The solver can only solve for the environment that it is running within so + the generated ``cabal.project.freeze`` file has no ``os(name)``, + ``arch(name)`` or ``impl(compiler)`` conditionals. This is why a ``.freeze`` + file is not a true lock file. + + A good many packages have platform-specific dependencies and will vary their + dependencies based on the operating system. + +Index State Limitation + The index state of the local package index will also change the list of + possible dependencies available to the solver. + +Revision Limitation + A version constraint cannot include a package revision, neither by revision + number nor hash. These constraints do not have sufficient granularity to + guarantee picking a specific revision. To work around this limitation a + project file will often fix the index state and so hide future revisions + that a freeze file's equality constraints cannot specify. + +Index State Limitation + Aside from revisions, the index state of the local package index will also + change the list of possible dependency versions available to the solver. + +Despite all of those limitations, for a software product that is always +developed on the same platform and environment, committing the freeze file to +source control may make sense. Even then, a production build would likely need +to avoid such a freeze file. From fd26380335af83e636cb800a66d65267759ae2be Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Mon, 22 Jun 2026 11:24:21 -0400 Subject: [PATCH 55/61] Split into avoiding and setting lists --- doc/how-to-freeze-versions.rst | 23 +++++++++++++++++------ doc/uv.lock | 2 +- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index bce06c8ea37..c7010f61bec 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -96,13 +96,24 @@ on: Please add them. There is more information at https://pvp.haskell.org/ For large projects with many packages, it would be a lot of work to keep all -package dependency version ranges up to date. Ways of overcoming, in worse to -better order, are: +package dependency version ranges up to date if each package was independently +maintained, especially using tight version ranges for dependencies in the +packages. Here are some options for avoiding or setting the same version ranges +in each package of a project with many packages: -- use a bash script to replace or update version ranges in all package descriptions -- use a package generator like `dhall-hpack-cabal `_ to import sets of dependencies with version ranges -- use a ``cabal.project`` file to specify version constraints for some or all dependencies -- use a curated set of packages and versions and import these into the project +**Avoiding** + Package-level version ranges can be avoided; + + - by using a curated set of packages and versions and import these into the project, or + - by specifying version constraints only in the ``cabal.project``. + +**Setting** + Package-level version ranges can be specified in one place and then copied + to packages; + + - by using a package generator like `dhall-hpack-cabal `_ with imports, + - by using a package formatter with fragment pragmas, or + - by using a bash script to replace or update version ranges in all package descriptions. .. _version-constraints: diff --git a/doc/uv.lock b/doc/uv.lock index 46b5c0ca24b..df4b5cf190d 100644 --- a/doc/uv.lock +++ b/doc/uv.lock @@ -1,6 +1,6 @@ version = 1 revision = 3 -requires-python = ">=3.12" +requires-python = ">=3.14" [manifest] From e97feebf48b3a62eac5e472abfbcdfd7d623f755 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Mon, 22 Jun 2026 11:47:39 -0400 Subject: [PATCH 56/61] Variation on avoiding and setting --- doc/how-to-freeze-versions.rst | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index c7010f61bec..78f87f046b8 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -98,15 +98,19 @@ on: For large projects with many packages, it would be a lot of work to keep all package dependency version ranges up to date if each package was independently maintained, especially using tight version ranges for dependencies in the -packages. Here are some options for avoiding or setting the same version ranges -in each package of a project with many packages: +packages. For a dependency, we can avoid setting its version range in any +package, or we can set the same version range in each package of a project. **Avoiding** Package-level version ranges can be avoided; - - by using a curated set of packages and versions and import these into the project, or + - by using a curated set of packages and versions, or - by specifying version constraints only in the ``cabal.project``. + Curated sets of version constrained packages, such as the ones supplied by + Stackage, are project-level configuration, ready-made to be imported by a + project. + **Setting** Package-level version ranges can be specified in one place and then copied to packages; @@ -115,6 +119,17 @@ in each package of a project with many packages: - by using a package formatter with fragment pragmas, or - by using a bash script to replace or update version ranges in all package descriptions. +If a project has many packages and these are all in-house and not published, +then it will be easier to use the same version range for a dependency +everywhere. When only some packages of a project are published, and moreover +when their publication times are staggered, it may not be possible to use the +same version range for a dependency in all packages of a project. In this latter +case we'll need to adjust the avoidance and shared setting techniques. A +project-level version range for a dependency will still work as long as it lies +within (as long as it narrows) each package-level version range for the same +dependency. For package-level generators, formatters or scripts, you may need +to use more than one set of imports, fragment pragmas or scripts. + .. _version-constraints: Version constraints From 0b949d7c037d2d59f3f53abc7dd25574bf17956a Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Wed, 1 Jul 2026 14:40:49 -0400 Subject: [PATCH 57/61] Satisfy whitespace --- doc/how-to-freeze-versions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 78f87f046b8..edd37047b99 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -110,7 +110,7 @@ package, or we can set the same version range in each package of a project. Curated sets of version constrained packages, such as the ones supplied by Stackage, are project-level configuration, ready-made to be imported by a project. - + **Setting** Package-level version ranges can be specified in one place and then copied to packages; From 0e496e973998b4c115cba48be6cc559811f1e36d Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sun, 5 Jul 2026 10:08:22 -0400 Subject: [PATCH 58/61] Change warning to tip for reject-unconstrained-* --- doc/how-to-freeze-versions.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index edd37047b99..3acd6ff4453 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -397,10 +397,9 @@ Ensuring everything is frozen cannot change and, at that point when every dependency is frozen, ``cabal freeze`` becomes an idempotent operation. -.. Warning:: - Setting :cfg-field:`reject-unconstrained-dependencies` to ``all`` will - ensure that dependencies have version constraints but does not require these - be equality constraints. +.. Tip:: + Use :cfg-field:`reject-unconstrained-dependencies` to ensure each dependency + has version constraints. When adding a new dependency to a project that utilizes a freeze file [#add-new-dep]_, regenerating the freeze file using one of the methods described From 19b5befb017085f42d047442940eb42c0ef8975a Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sun, 5 Jul 2026 10:23:25 -0400 Subject: [PATCH 59/61] A freeze file does not block adding new deps --- doc/how-to-freeze-versions.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 3acd6ff4453..e7129252081 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -397,6 +397,10 @@ Ensuring everything is frozen cannot change and, at that point when every dependency is frozen, ``cabal freeze`` becomes an idempotent operation. +.. Warning:: + A freeze file snapshots a solver resolution. It does not block adding new + dependencies. + .. Tip:: Use :cfg-field:`reject-unconstrained-dependencies` to ensure each dependency has version constraints. From f991d8378e11e481c899b78f26280c8c8d9de592 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sun, 5 Jul 2026 10:41:41 -0400 Subject: [PATCH 60/61] Add TODOs for .local and .freeze loading --- doc/how-to-freeze-versions.rst | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index e7129252081..69d7a6c1422 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -242,14 +242,24 @@ set of project dependencies, explicit and transitive. The ``cabal freeze`` command saves these to a file named the same as the whole of the project file name but with a extra ``.freeze`` extension, so the freeze file for ``cabal.project`` is ``cabal.project.freeze``. Effectively a ``.freeze`` file is -an implicit project import, same as the ``.local`` file for projects. +an implicit project import, the same way the project ``.local`` file is. The +order of imports of ``.local`` and ``.freeze`` files is important. The +``.local`` file is imported last, after the ``.freeze`` file, giving the user a +final say in the setting of any fields that have override semantics. -.. Note:: +.. Todo:: + + The ``cabal.project.local`` is loaded even when ``cabal.project`` does not + exist. + + TODO: Find out if this is the case for the default project only? - The order of imports of ``.local`` and ``.freeze`` files is important. The - ``.local`` file is imported last, after the ``.freeze`` file, giving the - user a final say in the setting of any fields that have override semantics. + TODO: Find out if the behavior for ``cabal.project.freeze`` matches. + TODO: Confirm that the ``.local`` file has to be in the same directory as + its project file. + +.. Tip:: The ``.local`` and ``.freeze`` extensions are specialized and quirky. In general, it is better to use a ``.project`` extension for any project and a ``.config`` extension for any project configuration that is imported. It is From 79ce54bd5fa6b23c62e9e8f03dd541bb006e9315 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sun, 5 Jul 2026 10:44:01 -0400 Subject: [PATCH 61/61] Don't describe .local and .freeze as quirky --- doc/how-to-freeze-versions.rst | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/doc/how-to-freeze-versions.rst b/doc/how-to-freeze-versions.rst index 69d7a6c1422..da4139677db 100644 --- a/doc/how-to-freeze-versions.rst +++ b/doc/how-to-freeze-versions.rst @@ -260,11 +260,9 @@ final say in the setting of any fields that have override semantics. its project file. .. Tip:: - The ``.local`` and ``.freeze`` extensions are specialized and quirky. In - general, it is better to use a ``.project`` extension for any project and a - ``.config`` extension for any project configuration that is imported. It is - of course possible to have multiple ``.project`` files and for these to have - import relationships. + Use a ``.project`` extension for any project and a ``.config`` extension for + any project configuration that is imported. It is of course possible to have + multiple ``.project`` files and for these to have import relationships. .. Warning::