Skip to content

Commit 66ce221

Browse files
authored
Merge pull request #4900 from hvr/pr/new-style-spec-2.0
Implement preliminary support for new forward-compat scheme
2 parents 0d34d92 + 54baa24 commit 66ce221

File tree

1 file changed

+97
-14
lines changed

1 file changed

+97
-14
lines changed

cabal-install/Distribution/Client/IndexUtils.hs

Lines changed: 97 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{-# LANGUAGE DeriveGeneric #-}
33
{-# LANGUAGE RecordWildCards #-}
44
{-# LANGUAGE BangPatterns #-}
5+
{-# LANGUAGE OverloadedStrings #-}
56
{-# LANGUAGE GADTs #-}
67

78
-----------------------------------------------------------------------------
@@ -53,15 +54,16 @@ import Distribution.Package
5354
import Distribution.Types.Dependency
5455
import Distribution.Simple.PackageIndex (InstalledPackageIndex)
5556
import Distribution.PackageDescription
56-
( GenericPackageDescription )
57+
( GenericPackageDescription(..)
58+
, PackageDescription(..), emptyPackageDescription )
5759
import Distribution.Simple.Compiler
5860
( Compiler, PackageDBStack )
5961
import Distribution.Simple.Program
6062
( ProgramDb )
6163
import qualified Distribution.Simple.Configure as Configure
6264
( getInstalledPackages, getInstalledPackagesMonitorFiles )
6365
import Distribution.Version
64-
( mkVersion, intersectVersionRanges )
66+
( Version, mkVersion, intersectVersionRanges, versionNumbers )
6567
import Distribution.Text
6668
( display, simpleParse )
6769
import Distribution.Simple.Utils
@@ -94,6 +96,7 @@ import Control.Exception
9496
import qualified Data.ByteString.Lazy as BS
9597
import qualified Data.ByteString.Lazy.Char8 as BS.Char8
9698
import qualified Data.ByteString.Char8 as BSS
99+
import qualified Data.ByteString as BSW8
97100
import Data.ByteString.Lazy (ByteString)
98101
import Distribution.Client.GZipUtils (maybeDecompress)
99102
import Distribution.Client.Utils ( byteStringToFilePath
@@ -678,7 +681,7 @@ packageListFromCache verbosity mkPkg hnd Cache{..} mode = accum mempty [] mempty
678681
-- Most of the time we only need the package id.
679682
~(pkg, pkgtxt) <- unsafeInterleaveIO $ do
680683
pkgtxt <- getEntryContent blockno
681-
pkg <- readPackageDescription pkgtxt
684+
pkg <- readPackageDescription pkgid pkgtxt
682685
return (pkg, pkgtxt)
683686
case mode of
684687
ReadPackageIndexLazyIO -> pure ()
@@ -710,22 +713,102 @@ packageListFromCache verbosity mkPkg hnd Cache{..} mode = accum mempty [] mempty
710713
-> return content
711714
_ -> interror "unexpected tar entry type"
712715

713-
readPackageDescription :: ByteString -> IO GenericPackageDescription
714-
readPackageDescription content =
716+
interror :: String -> IO a
717+
interror msg = die' verbosity $ "internal error when reading package index: " ++ msg
718+
++ "The package index or index cache is probably "
719+
++ "corrupt. Running cabal update might fix it."
720+
721+
readPackageDescription pkgid content = do
722+
case parseGenericPackageDescription' content of
723+
ParseGPDOk gpd -> return gpd
724+
ParseError (Just specVer) | specVer >= mkVersion [2,0]
725+
-> return (dummyPackageDescription specVer)
726+
ParseError _ -> interror "failed to parse .cabal file"
727+
where
728+
dummyPackageDescription :: Version -> GenericPackageDescription
729+
dummyPackageDescription specVer = GenericPackageDescription
730+
{ packageDescription = emptyPackageDescription
731+
{ specVersionRaw = Left specVer
732+
, package = pkgid
733+
, synopsis = dummySynopsis
734+
}
735+
, genPackageFlags = []
736+
, condLibrary = Nothing
737+
, condSubLibraries = []
738+
, condForeignLibs = []
739+
, condExecutables = []
740+
, condTestSuites = []
741+
, condBenchmarks = []
742+
}
743+
744+
dummySynopsis = "<could not be parsed due to unsupported CABAL spec-version>"
745+
746+
-- | Result of 'parseGenericPackageDescription''
747+
data ParseGPDResult = ParseGPDOk GenericPackageDescription
748+
-- ^ parsing succeeded (may still have unsupported spec-version)
749+
| ParseError !(Maybe Version)
750+
-- ^ parsing failed, but we were able to recover
751+
-- a new-style spec-version declaration
752+
753+
-- | Variant of 'parseGenericPackageDescription' which is able to
754+
-- extract new-style spec-version in case of parser errors
755+
parseGenericPackageDescription' :: ByteString -> ParseGPDResult
756+
parseGenericPackageDescription' content =
757+
case parseGPDMaybe of
758+
Just gpd -> ParseGPDOk gpd
759+
Nothing -> ParseError (scanSpecVersion content)
760+
where
761+
parseGPDMaybe =
715762
#ifdef CABAL_PARSEC
716-
case parseGenericPackageDescriptionMaybe (BS.toStrict content) of
717-
Just gpd -> return gpd
718-
Nothing -> interror "failed to parse .cabal file"
763+
parseGenericPackageDescriptionMaybe (BS.toStrict content)
719764
#else
720765
case parseGenericPackageDescription . ignoreBOM . fromUTF8 . BS.Char8.unpack $ content of
721-
ParseOk _ d -> return d
722-
_ -> interror "failed to parse .cabal file"
766+
ParseOk _ d -> Just d
767+
_ -> Nothing
723768
#endif
724769

725-
interror :: String -> IO a
726-
interror msg = die' verbosity $ "internal error when reading package index: " ++ msg
727-
++ "The package index or index cache is probably "
728-
++ "corrupt. Running cabal update might fix it."
770+
-- | Quickly scan new-style spec-version
771+
--
772+
-- A new-style spec-version declaration begins the .cabal file and
773+
-- follow the following case-insensitive grammar (expressed in
774+
-- RFC5234 ABNF):
775+
--
776+
-- newstyle-spec-version-decl = "cabal-version" *WS ":" *WS newstyle-pec-version *WS
777+
--
778+
-- spec-version = NUM "." NUM [ "." NUM ]
779+
--
780+
-- NUM = DIGIT0 / DIGITP 1*DIGIT0
781+
-- DIGIT0 = %x30-39
782+
-- DIGITP = %x31-39
783+
-- WS = %20
784+
--
785+
-- TODO: move into lib:Cabal
786+
scanSpecVersion :: ByteString -> Maybe Version
787+
scanSpecVersion bs = do
788+
fstline':_ <- pure (BS.Char8.lines bs)
789+
790+
-- parse <newstyle-spec-version-decl>
791+
-- normalise: remove all whitespace, convert to lower-case
792+
let fstline = BSW8.map toLowerW8 $ BSW8.filter (/= 0x20) $ BS.toStrict fstline'
793+
["cabal-version",vers] <- pure (BSS.split ':' fstline)
794+
795+
-- parse <spec-version>
796+
--
797+
-- This is currently more tolerant regarding leading 0 digits.
798+
--
799+
ver <- simpleParse (BSS.unpack vers)
800+
guard $ case versionNumbers ver of
801+
[_,_] -> True
802+
[_,_,_] -> True
803+
_ -> False
804+
805+
pure ver
806+
where
807+
-- | Translate ['A'..'Z'] to ['a'..'z']
808+
toLowerW8 :: Word8 -> Word8
809+
toLowerW8 w | 0x40 < w && w < 0x5b = w+0x20
810+
| otherwise = w
811+
729812

730813
------------------------------------------------------------------------
731814
-- Index cache data structure

0 commit comments

Comments
 (0)