Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Forward compat scheme #4899

Closed
hvr opened this issue Nov 20, 2017 · 9 comments
Closed

Forward compat scheme #4899

hvr opened this issue Nov 20, 2017 · 9 comments

Comments

@hvr
Copy link
Member

hvr commented Nov 20, 2017

Generally, a given cabal-install release is only expected to understand (cabal-)spec(ification)-versions that were known at the time of release.

The Cabal-1.12 release, and more recently the Cabal-2.0 release have made it apparent that we don't have a good forward-compatibility story in place, thereby limiting our ability to evolve the .cabal format. Concretely, cabal-install-1.10 is not expected to understand package meta-data declared using the cabal-version:1.12 spec(ification)-version; also, upon encountering such a future .cabal file in the package index, cabal-install-1.10 ought to gracefully recover (instead of failing fatally to parse the package index). A similar situation occurred with the Cabal-2.0 release, which also introduced new syntax which previous parsers would fail to recognise, again resulting in fatal parsing failures.

To prevent this from happening again, the following forward compatibility has been devised, which is intended to make it possible to evolve the lexical and syntactical structure of the .cabal format in future, while not breaking legacy clients.


New-style Cabal-Specification Version Declaration

A new-style spec-version declaration at the beginning of a the .cabal file (without any preceding whitespace) and follow the following case-insensitive grammar (expressed in RFC5234 ABNF):

newstyle-spec-version-decl = "cabal-version" *WS ":" *WS newstyle-spec-version *WS

newstyle-spec-version      = NUM "." NUM [ "." NUM ]

NUM    = DIGIT0 / DIGITP 1*DIGIT0
DIGIT0 = %x30-39
DIGITP = %x31-39
WS = %20

The use of a new-style spec-version is

  • invalid before spec-version 1.12,
  • optional starting with spec-version 1.12,
  • recommended starting with spec-version 2.0, and
  • mandatory starting with spec-version 2.1.

It's also assumed that the following invariant holds:

  • If a new-style spec-version declaration is present, its value must match the spec-version as determined by a full .cabal parser.

Scanning the Cabal-Specification Version

  1. If a .cabal file contains a valid new-style spec-version declaration, it is authoritative;
  2. otherwise, the spec-version must be below 2.1 (and either a full .cabal parser or a heuristic cabal-version-scanner (tbd) must be used to determine the exact spec-version).

The new-style spec-version declaration is designed to be simple to parse by means of common string operations. A simple implementation is shown below

scanSpecVersion :: ByteString -> Maybe Version
scanSpecVersion bs = do
    fstline':_ <- pure (BS.Char8.lines bs)

    -- parse <newstyle-spec-version-decl>
    -- normalise: remove all whitespace, convert to lower-case
    let fstline = BSW8.map toLowerW8 $ BSW8.filter (/= 0x20) $ BS.toStrict fstline'
    ["cabal-version",vers] <- pure (BSS.split ':' fstline)

    -- parse <spec-version> tolerantly
    ver <- simpleParse (BSS.unpack vers)
    guard $ case versionNumbers ver of
              [_,_]   -> True
              [_,_,_] -> True
              _       -> False

    pure ver
  where
    -- | Translate ['A'..'Z'] to ['a'..'z']
    toLowerW8 :: Word8 -> Word8
    toLowerW8 w | 0x40 < w && w < 0x5b = w+0x20
                | otherwise            = w

Appendix: Compatiblity with clients prior to cabal 2.0

Since this new scheme is only understood properly starting with cabal 2.0, older clients need to avoid being exposed to spec-versions 2.0 and newer.

To this end we exploit that cabal 2.0 started using the incremental secure 01-index.tar package index by default, while cabal versions prior to cabal 2.0 use the (non-incremental/non-secure) 00-index.tar package index by default (NB: cabal 1.24 was the first release that added experimental non-default/opt-in support for the secure 01-index.tar), by establishing the following hackage-side invariant:

  • the legacy index 00-index.tar.gz contains only .cabal files with a spec version below 2

This way, the huge install-base of legacy cabal clients prior to cabal 2.0 keep working without requiring modifications, as they won't be exposed to incompatible .cabal files; with the unfortunate exception that the (hopefully uncommon) cabal clients prior to cabal 1.12 may still be exposed to incompatible cabal versions using the >=-less spec-version declarations.

hvr added a commit to hvr/cabal that referenced this issue Nov 20, 2017
This provides a provisional (i.e. hacky) retrofitted implementation of
the forward-compat scheme described in haskell#4899 for the cabal-2.0 branch.

This hack works by constructing a dummy package description in case
the package description fails to be parsed via the standard parser,
and we detect a new-style cabal-spec declaration.
hvr added a commit to hvr/cabal that referenced this issue Nov 24, 2017
This compares the request spec-version to the lib:Cabal's version in order to
determine whether cabal is able to properly understand the package. If it's
newer than the currently linked lib:Cabal version it's turned into a global
`FailResult` which the solver treats as desired in terms of backtracking and
error reporting.

This is related to the new spec-version forward-compat scheme (see haskell#4899).

This is complements haskell#4900 for the 2.0 branch.
hvr added a commit to hvr/cabal that referenced this issue Nov 24, 2017
This compares the request spec-version to the lib:Cabal's version in order to
determine whether cabal is able to properly understand the package. If it's
newer than the currently linked lib:Cabal version it's turned into a global
`FailResult` which the solver treats as desired in terms of backtracking and
error reporting.

This is related to the new spec-version forward-compat scheme (see haskell#4899).

This is complements haskell#4900 for the 2.0 branch.
hvr added a commit to hvr/cabal that referenced this issue Nov 25, 2017
This compares the request spec-version to the lib:Cabal's version in order to
determine whether cabal is able to properly understand the package. If it's
newer than the currently linked lib:Cabal version it's turned into a global
`FailResult` which the solver treats as desired in terms of backtracking and
error reporting.

This is related to the new spec-version forward-compat scheme (see haskell#4899).

This is a forward-port of haskell#4907 to the `master` branch
@23Skidoo
Copy link
Member

23Skidoo commented Dec 8, 2017

Related: #4448.

@gbaz
Copy link
Collaborator

gbaz commented Dec 11, 2017

This seems like a good way forward.

@phadej phadej added this to the 2.2 milestone Jan 14, 2018
phadej pushed a commit to phadej/cabal that referenced this issue Jan 14, 2018
This provides a provisional (i.e. hacky) retrofitted implementation of
the forward-compat scheme described in haskell#4899 for the cabal-2.2 branch

This hack works by constructing a dummy package description in case
the package description fails to be parsed via the standard parser,
and we detect a new-style cabal-spec declaration.
@phadej
Copy link
Collaborator

phadej commented Jan 14, 2018

@hvr, should the ABNF contain end-of-line character? '\n' ?

phadej pushed a commit to phadej/cabal that referenced this issue Jan 14, 2018
This provides a provisional (i.e. hacky) retrofitted implementation of
the forward-compat scheme described in haskell#4899 for the cabal-2.2 branch

This hack works by constructing a dummy package description in case
the package description fails to be parsed via the standard parser,
and we detect a new-style cabal-spec declaration.
phadej pushed a commit to phadej/cabal that referenced this issue Jan 14, 2018
This provides a provisional (i.e. hacky) retrofitted implementation of
the forward-compat scheme described in haskell#4899 for the cabal-2.2 branch

This hack works by constructing a dummy package description in case
the package description fails to be parsed via the standard parser,
and we detect a new-style cabal-spec declaration.
phadej pushed a commit to phadej/cabal that referenced this issue Jan 15, 2018
This provides a provisional (i.e. hacky) retrofitted implementation of
the forward-compat scheme described in haskell#4899 for the cabal-2.2 branch

This hack works by constructing a dummy package description in case
the package description fails to be parsed via the standard parser,
and we detect a new-style cabal-spec declaration.
phadej pushed a commit to phadej/cabal that referenced this issue Jan 16, 2018
This provides a provisional (i.e. hacky) retrofitted implementation of
the forward-compat scheme described in haskell#4899 for the cabal-2.2 branch

This hack works by constructing a dummy package description in case
the package description fails to be parsed via the standard parser,
and we detect a new-style cabal-spec declaration.
@phadej
Copy link
Collaborator

phadej commented Jan 17, 2018

merged in #5046

@typesanitizer
Copy link

typesanitizer commented May 21, 2018

Can the docs be updated to reflect this change? I just encountered this issue and am having a hard time understanding what I should do if I want to express >= 2.2 (I'm using common stanzas). Nevermind, the docs have been updated in #5131.

vu3rdd added a commit to LeastAuthority/wormhole-client that referenced this issue Aug 16, 2018
…d as a range

CI reports a failure:
  unexpected cabal-version higher than 2.2 cannot be specified as a range. See
  haskell/cabal#4899
  expecting ".", "-", white space, "&&" or "||" >=2.2
  setup-Simple-Cabal-2.2.0.1-x86_64-linux-ghc-8.0.2: Failed parsing "./hwormhole.cabal".

This change also relaxes the lower bound to 2.0 instead of the very new 2.2 and see
how that goes.
vu3rdd added a commit to LeastAuthority/wormhole-client that referenced this issue Aug 16, 2018
…d as a range

CI reports a failure:
  unexpected cabal-version higher than 2.2 cannot be specified as a range. See
  haskell/cabal#4899
  expecting ".", "-", white space, "&&" or "||" >=2.2
  setup-Simple-Cabal-2.2.0.1-x86_64-linux-ghc-8.0.2: Failed parsing "./hwormhole.cabal".

This change also relaxes the lower bound to 2.0 instead of the very new 2.2 and see
how that goes.
vu3rdd added a commit to LeastAuthority/wormhole-client that referenced this issue Aug 20, 2018
…d as a range

This change also relaxes the lower bound to 1.24 instead of the very new 2.2 and see
how that goes.

CI reports a failure:
  unexpected cabal-version higher than 2.2 cannot be specified as a range. See
  haskell/cabal#4899
  expecting ".", "-", white space, "&&" or "||" >=2.2
  setup-Simple-Cabal-2.2.0.1-x86_64-linux-ghc-8.0.2: Failed parsing "./hwormhole.cabal".
sboosali added a commit to sboosali/fltkhs that referenced this issue Apr 11, 2019
also move `cabal-version` field (for this following warning: `"cabal-version should be at the beginning of the file starting with spec version 2.2. See haskell/cabal#4899).
@ntc2
Copy link

ntc2 commented May 30, 2019

If you were brought here by a Cabal error message and just want to fix your Cabal file, replace

cabal-version: >= 2.2

with

cabal-version: 2.2

and move the cabal-version: declaration to the first line of your .cabal file.

@zdleaf
Copy link

zdleaf commented Nov 17, 2019

I've been able to fix this issue on Debian by running stack upgrade

@recursion-ninja
Copy link
Contributor

hackage.org claims cabal-version: 3.0 is invalid. The output directs me here.

I ran the following (anonymized) command:

$ cabal upload -u user_name dist-newstyle/sdist/package-name-0.0.0.0.tar.gz
hackage.haskell.org password: 

Uploading dist-newstyle/sdist/package-name-0.0.0.0.tar.gz...
Error uploading dist-newstyle/sdist/package-name-0.0.0.0.tar.gz: http code 400
Error: Invalid package

package-name-0.0.0.0/package-name.cabal:0:0: Unsupported cabal-version. See
https://github.com/haskell/cabal/issues/4899.

With the following package-name.cabal file header:

cabal-version:       3.0
name:                package-name
version:             0.0.0.0
<suffix omitted>

@phadej
Copy link
Collaborator

phadej commented Mar 3, 2020

@phadej phadej closed this as completed Mar 3, 2020
l29ah added a commit to l29ah/hsendxmpp that referenced this issue Apr 18, 2021
akuhlens pushed a commit to Gradual-Typing/Dynamizer that referenced this issue Sep 30, 2021
I encountered the following error message while using `stack build`:
Unsupported cabal-version 2.0.1.1. See
haskell/cabal#4899.
zzantares added a commit to zzantares/fourmolu that referenced this issue Mar 12, 2023
When fourmolu fails to parse the Cabal file the user doesn't know what is the
actual error as only the cabal filepath is reported. This makes the necessary
changes to actually show what were the errors that the cabal file parser
encountered.

Before:
  Parsing this .cabal file failed:
    /path/to/file.cabal

After:
  Parsing this .cabal file failed:
  /path/to/file.cabal:0:0: Unsupported cabal-version 3.8. See haskell/cabal#4899.

Note: Removing the `Eq` instance on `OrmoluException` was the easy way to not
  having to implement an orphan `Eq` instance on `PError`. This does not have
  any impact elsewhere as we're actually not making use of the `Eq` instance
  on this exception type.
zzantares added a commit to zzantares/fourmolu that referenced this issue Mar 12, 2023
When fourmolu fails to parse the Cabal file the user doesn't know what is the
actual error as only the cabal filepath is reported. This makes the necessary
changes to actually show what were the errors that the cabal file parser
encountered.

Before:
  Parsing this .cabal file failed:
    /path/to/file.cabal

After:
  Parsing this .cabal file failed:
  /path/to/file.cabal:0:0: Unsupported cabal-version 3.8. See haskell/cabal#4899.

Note: Removing the `Eq` instance on `OrmoluException` was the easy way to not
  having to implement an orphan `Eq` instance on `PError`. This does not have
  any impact elsewhere as we're actually not making use of the `Eq` instance
  on this exception type.
zzantares added a commit to zzantares/ormolu that referenced this issue Mar 13, 2023
When ormolu fails to parse the Cabal file the user doesn't know what is the
actual error as only the cabal filepath is reported. This makes the necessary
changes to actually show what errors the cabal file parser encountered.

Before:
  Parsing this .cabal file failed:
    /path/to/file.cabal

After:
  Parsing this .cabal file failed:
    /path/to/file.cabal:0:0: Unsupported cabal-version 3.8. See haskell/cabal#4899.

Note: Removing the `Eq` instance on `OrmoluException` was the easy way to not
  having to implement an orphan `Eq` instance on `PError`. This does not have
  any impact elsewhere as we're actually not making use of the `Eq` instance
  on this exception type.
zzantares added a commit to zzantares/ormolu that referenced this issue Mar 20, 2023
When ormolu fails to parse the Cabal file the user doesn't know what is the
actual error as only the cabal filepath is reported. This makes the necessary
changes to actually show what errors the cabal file parser encountered.

Before:
  Parsing this .cabal file failed:
    /path/to/file.cabal

After:
  Parsing this .cabal file failed:
    /path/to/file.cabal:0:0: Unsupported cabal-version 3.8. See haskell/cabal#4899.

Note: Removing the `Eq` instance on `OrmoluException` was the easy way to not
  having to implement an orphan `Eq` instance on `PError`. This does not have
  any impact elsewhere as we're actually not making use of the `Eq` instance
  on this exception type.
mrkkrp pushed a commit to zzantares/ormolu that referenced this issue Mar 31, 2023
When ormolu fails to parse the Cabal file the user doesn't know what is the
actual error as only the cabal filepath is reported. This makes the necessary
changes to actually show what errors the cabal file parser encountered.

Before:
  Parsing this .cabal file failed:
    /path/to/file.cabal

After:
  Parsing this .cabal file failed:
    /path/to/file.cabal:0:0: Unsupported cabal-version 3.8. See haskell/cabal#4899.

Note: Removing the `Eq` instance on `OrmoluException` was the easy way to not
  having to implement an orphan `Eq` instance on `PError`. This does not have
  any impact elsewhere as we're actually not making use of the `Eq` instance
  on this exception type.
mrkkrp pushed a commit to tweag/ormolu that referenced this issue Mar 31, 2023
When ormolu fails to parse the Cabal file the user doesn't know what is the
actual error as only the cabal filepath is reported. This makes the necessary
changes to actually show what errors the cabal file parser encountered.

Before:
  Parsing this .cabal file failed:
    /path/to/file.cabal

After:
  Parsing this .cabal file failed:
    /path/to/file.cabal:0:0: Unsupported cabal-version 3.8. See haskell/cabal#4899.

Note: Removing the `Eq` instance on `OrmoluException` was the easy way to not
  having to implement an orphan `Eq` instance on `PError`. This does not have
  any impact elsewhere as we're actually not making use of the `Eq` instance
  on this exception type.
tych0 added a commit to tych0/xcffib that referenced this issue Aug 19, 2023
xcffib.cabal:11:1: error:
cabal-version should be at the beginning of the file starting with spec version 2.2. See haskell/cabal#4899

   10 | build-type:          Simple
   11 | cabal-version:       2.4
      | ^
Error: cabal: parse error

Warning: These warnings may cause trouble when distributing the package:
Warning: In 'extra-source-files': the pattern 'test/generator/*.py' does not
match the file 'test/generator/render_1.7.py' because the extensions do not
exactly match (e.g., foo.en.html does not exactly match *.html). To enable
looser suffix-only matching, set 'cabal-version: 2.4' or higher.

Signed-off-by: Tycho Andersen <tycho@tycho.pizza>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants