Skip to content

Commit

Permalink
Introduce PkgconfigVersion and PkgconfigVersionRange
Browse files Browse the repository at this point in the history
- Don't accept leading zeros in Version or VersionRange
- Fixes #163
  (pkg-config uses a more general version scheme)
- Also resolves #5138
- `PkgconfigVersion` is compared with `rpmvercmp`
- `PkgconfigVersionRange` is subset of `VersionRange`
    - with `cabal-version` before 3.0 it's parsed like `VersionRange`,
      where version digits are arbitrary integral (leading spaces
      allowed)
    - starting from cabal spec 3.0 `== x.y.*` and `^>=`
      (and set `{ ..  }`) are disallowed.
      Yet, the version literals syntax is relaxed to accept
      alphanumerical + `-.` strings. E.g. openssl's `1.1.0h` is
      accepted.
- Lax `PkgconfigVersion` parser is also used to parse
  `pkg-config --modversion` output.
  • Loading branch information
phadej committed Mar 3, 2019
1 parent 6191970 commit 01df85e
Show file tree
Hide file tree
Showing 39 changed files with 2,233 additions and 157 deletions.
18 changes: 16 additions & 2 deletions Cabal/Cabal.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ extra-source-files:
-- Generated with 'make gen-extra-source-files'
-- Do NOT edit this section manually; instead, run the script.
-- BEGIN gen-extra-source-files
tests/ParserTests/errors/MiniAgda.cabal
tests/ParserTests/errors/MiniAgda.errors
tests/ParserTests/errors/common1.cabal
tests/ParserTests/errors/common1.errors
tests/ParserTests/errors/common2.cabal
Expand All @@ -57,6 +59,10 @@ extra-source-files:
tests/ParserTests/errors/leading-comma-2c.errors
tests/ParserTests/errors/leading-comma.cabal
tests/ParserTests/errors/leading-comma.errors
tests/ParserTests/errors/libpq1.cabal
tests/ParserTests/errors/libpq1.errors
tests/ParserTests/errors/libpq2.cabal
tests/ParserTests/errors/libpq2.errors
tests/ParserTests/errors/mixin-1.cabal
tests/ParserTests/errors/mixin-1.errors
tests/ParserTests/errors/mixin-2.cabal
Expand Down Expand Up @@ -99,8 +105,6 @@ extra-source-files:
tests/ParserTests/ipi/transformers.cabal
tests/ParserTests/ipi/transformers.expr
tests/ParserTests/ipi/transformers.format
tests/ParserTests/regressions/MiniAgda.cabal
tests/ParserTests/regressions/MiniAgda.check
tests/ParserTests/regressions/Octree-0.5.cabal
tests/ParserTests/regressions/Octree-0.5.expr
tests/ParserTests/regressions/Octree-0.5.format
Expand Down Expand Up @@ -159,6 +163,12 @@ extra-source-files:
tests/ParserTests/regressions/leading-comma.cabal
tests/ParserTests/regressions/leading-comma.expr
tests/ParserTests/regressions/leading-comma.format
tests/ParserTests/regressions/libpq1.cabal
tests/ParserTests/regressions/libpq1.expr
tests/ParserTests/regressions/libpq1.format
tests/ParserTests/regressions/libpq2.cabal
tests/ParserTests/regressions/libpq2.expr
tests/ParserTests/regressions/libpq2.format
tests/ParserTests/regressions/mixin-1.cabal
tests/ParserTests/regressions/mixin-1.expr
tests/ParserTests/regressions/mixin-1.format
Expand Down Expand Up @@ -221,6 +231,7 @@ extra-source-files:
tests/ParserTests/warnings/unknownsection.cabal
tests/ParserTests/warnings/utf8.cabal
tests/ParserTests/warnings/versiontag.cabal
tests/cbits/rpmvercmp.c
tests/hackage/check.sh
tests/hackage/download.sh
tests/hackage/unpack.sh
Expand Down Expand Up @@ -405,6 +416,8 @@ library
Distribution.Types.PackageName
Distribution.Types.PackageName.Magic
Distribution.Types.PkgconfigName
Distribution.Types.PkgconfigVersion
Distribution.Types.PkgconfigVersionRange
Distribution.Types.UnqualComponentName
Distribution.Types.IncludeRenaming
Distribution.Types.Mixin
Expand Down Expand Up @@ -566,6 +579,7 @@ test-suite unit-tests
UnitTests.Distribution.Utils.NubList
UnitTests.Distribution.Utils.ShortText
UnitTests.Distribution.Version
UnitTests.Distribution.PkgconfigVersion
main-is: UnitTests.hs
build-depends:
array,
Expand Down
40 changes: 40 additions & 0 deletions Cabal/Distribution/PackageDescription/Quirks.hs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,46 @@ patches = Map.fromList
(Fingerprint 6870520675313101180 14553457351296240636)
(Fingerprint 12481021059537696455 14711088786769892762)
(bsReplace "flag(sse42)" "False")
-- leading zeros in version digits
-- https://github.com/haskell-infra/hackage-trustees/issues/128
-- https://github.com/haskell/cabal/issues/5092
-- https://github.com/haskell/cabal/issues/5138
, mk "name: Sit\nversion: 0.2017.02.26\nbuild-type: Simple\ncabal-version: >= 1.8\nlicense: OtherLicense\nlicense-file: LICENSE\nauthor: Anonymous\nmaintainer: Anonymous\nhomepage: NONE\ncategory: Dependent"
(Fingerprint 8458530898096910998 3228538743646501413)
(Fingerprint 14470502514907936793 17514354054641875371)
(bsReplace "0.2017.02.26" "0.2017.2.26")
, mk "name: Sit\nversion: 0.2017.05.01\nbuild-type: Simple\ncabal-version: >= 1.8\nlicense: OtherLicense\nlicense-file: LICENSE\nauthor: Andreas Abel <[email protected]>\nmaintainer: Andreas Abel <[email protected]>\n"
(Fingerprint 1450130849535097473 11742099607098860444)
(Fingerprint 16679762943850814021 4253724355613883542)
(bsReplace "0.2017.05.01" "0.2017.5.1")
, mk "name: Sit\nversion: 0.2017.05.02\nbuild-type: Simple\ncabal-version: >= 1.8\nlicense: OtherLicense\nlicense-file: LICENSE\nauthor: Andreas Abel <[email protected]>\nmaintainer: Andreas Abel <[email protected]>\n"
(Fingerprint 297248532398492441 17322625167861324800)
(Fingerprint 634812045126693280 1755581866539318862)
(bsReplace "0.2017.05.02" "0.2017.5.2")
, mk "name: Sit\nversion: 0.2017.5.02\nx-revision: 1\n-- x-revision:1 see https://github.com/haskell-infra/hackage-trustees/issues/128\nbuild-type: Simple\ncabal-version: >= 1.8\nlicense: OtherLicense\nlicense-file: LICENSE\nauthor: "
(Fingerprint 3697869560530373941 3942982281026987312)
(Fingerprint 14344526114710295386 16386400353475114712)
(bsReplace "0.2017.5.02" "0.2017.5.2")
, mk "name: MiniAgda\nversion: 0.2017.02.18\nbuild-type: Simple\ncabal-version: >= 1.22\nlicense: OtherLicense\nlicense-file: LICENSE\nauthor: Andreas Abel and Karl Mehltretter\nmaintainer: Andreas Abel <andreas.abel@i"
(Fingerprint 17167128953451088679 4300350537748753465)
(Fingerprint 12402236925293025673 7715084875284020606)
(bsReplace "0.2017.02.18" "0.2017.2.18")
, mk "cabal-version:\n 2.0\nname:\n fast-downward\nversion:\n 0.1.0.0\nbuild-type:\n Simple\nsynopsis:\n Solve classical planning problems (STRIPS/SAS+) using Haskell & Fast Downward.\ndescription:\n @fast-downward@ is a library for modelling classical planning probl"
(Fingerprint 11256076039027887363 6867903407496243216)
(Fingerprint 12159816716813155434 5278015399212299853)
(bsReplace "1.2.03.0" "1.2.3.0")
, mk "cabal-version:\r\n 2.0\r\nname:\r\n fast-downward\r\nversion:\r\n 0.1.0.0\r\nx-revision: \r\n 1\r\nbuild-type:\r\n Simple\r\nsynopsis:\r\n Solve classical planning problems (STRIPS/SAS+) using Haskell & Fast Downward.\r\ndescription:\r\n @fast-downward@ is a library for mode"
(Fingerprint 9216193973149680231 893446343655828508)
(Fingerprint 10020169545407746427 1828336750379510675)
(bsReplace "1.2.03.0" "1.2.3.0")
, mk "cabal-version:\n 2.0\nname:\n fast-downward\nversion:\n 0.1.0.1\nbuild-type:\n Simple\nsynopsis:\n Solve classical planning problems (STRIPS/SAS+) using Haskell & Fast Downward.\ndescription:\n @fast-downward@ is a library for modelling classical planning probl"
(Fingerprint 9899886602574848632 5980433644983783334)
(Fingerprint 12007469255857289958 8321466548645225439)
(bsReplace "1.2.03.0" "1.2.3.0")
, mk "cabal-version:\n 2.0\nname:\n fast-downward\nversion:\n 0.1.1.0\nbuild-type:\n Simple\nsynopsis:\n Solve classical planning problems (STRIPS/SAS+) using Haskell & Fast Downward.\ndescription:\n @fast-downward@ is a library for modelling classical planning probl"
(Fingerprint 12694656661460787751 1902242956706735615)
(Fingerprint 15433152131513403849 2284712791516353264)
(bsReplace "1.2.03.0" "1.2.3.0")
]
where
mk a b c d = ((a, b), (c, d))
Expand Down
1 change: 0 additions & 1 deletion Cabal/Distribution/Parsec/Warning.hs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ data PWarnType
| PWTDoubleDash -- ^ Double dash token, most likely it's a mistake - it's not a comment
| PWTMultipleSingularField -- ^ e.g. name or version should be specified only once.
| PWTBuildTypeDefault -- ^ Workaround for derive-package having build-type: Default. See <https://github.com/haskell/cabal/issues/5020>.
| PWTVersionLeadingZeros -- ^ See https://github.com/haskell-infra/hackage-trustees/issues/128
deriving (Eq, Ord, Show, Enum, Bounded, Generic)

instance Binary PWarnType
Expand Down
9 changes: 5 additions & 4 deletions Cabal/Distribution/Simple/Configure.hs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ import Distribution.Simple.LocalBuildInfo
import Distribution.Types.ExeDependency
import Distribution.Types.LegacyExeDependency
import Distribution.Types.PkgconfigDependency
import Distribution.Types.PkgconfigVersionRange
import Distribution.Types.LocalBuildInfo
import Distribution.Types.LibraryName
import Distribution.Types.ComponentRequestedSpec
Expand Down Expand Up @@ -1559,8 +1560,8 @@ configurePkgconfigPackages verbosity pkg_descr progdb enabled
`catchExit` (\_ -> die' verbosity notFound)
case simpleParsec version of
Nothing -> die' verbosity "parsing output of pkg-config --modversion failed"
Just v | not (withinRange v range) -> die' verbosity (badVersion v)
| otherwise -> info verbosity (depSatisfied v)
Just v | not (withinPkgconfigVersionRange v range) -> die' verbosity (badVersion v)
| otherwise -> info verbosity (depSatisfied v)
where
notFound = "The pkg-config package '" ++ pkg ++ "'"
++ versionRequirement
Expand All @@ -1573,8 +1574,8 @@ configurePkgconfigPackages verbosity pkg_descr progdb enabled
++ ": using version " ++ prettyShow v

versionRequirement
| isAnyVersion range = ""
| otherwise = " version " ++ prettyShow range
| isAnyPkgconfigVersion range = ""
| otherwise = " version " ++ prettyShow range

pkg = unPkgconfigName pkgn

Expand Down
7 changes: 3 additions & 4 deletions Cabal/Distribution/Types/PkgconfigDependency.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ module Distribution.Types.PkgconfigDependency
import Distribution.Compat.Prelude
import Prelude ()

import Distribution.Version (VersionRange, anyVersion)

import Distribution.Types.PkgconfigName
import Distribution.Types.PkgconfigVersionRange

import Distribution.Parsec
import Distribution.Pretty
Expand All @@ -22,7 +21,7 @@ import Text.PrettyPrint ((<+>))
-- @since 2.0.0.2
data PkgconfigDependency = PkgconfigDependency
PkgconfigName
VersionRange
PkgconfigVersionRange
deriving (Generic, Read, Show, Eq, Typeable, Data)

instance Binary PkgconfigDependency
Expand All @@ -36,5 +35,5 @@ instance Parsec PkgconfigDependency where
parsec = do
name <- parsec
P.spaces
verRange <- parsec <|> pure anyVersion
verRange <- parsec <|> pure anyPkgconfigVersion
pure $ PkgconfigDependency name verRange
105 changes: 105 additions & 0 deletions Cabal/Distribution/Types/PkgconfigVersion.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
-- @since 3.0
module Distribution.Types.PkgconfigVersion (
PkgconfigVersion (..),
rpmvercmp,
) where

import Distribution.Compat.Prelude
import Prelude ()

import Distribution.Parsec
import Distribution.Pretty
import Distribution.Utils.Generic (isAsciiAlphaNum)

import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BS8
import qualified Distribution.Compat.CharParsing as P
import qualified Text.PrettyPrint as PP

-- | @pkg-config@ versions.
--
-- In fact, this can be arbitrary 'BS.ByteString',
-- but 'Parsec' instance is a little pickier.
--
-- @since 3.0
newtype PkgconfigVersion = PkgconfigVersion BS.ByteString
deriving (Generic, Read, Show, Typeable, Data)

instance Eq PkgconfigVersion where
PkgconfigVersion a == PkgconfigVersion b = rpmvercmp a b == EQ

instance Ord PkgconfigVersion where
PkgconfigVersion a `compare` PkgconfigVersion b = rpmvercmp a b

instance Binary PkgconfigVersion
instance NFData PkgconfigVersion where rnf = genericRnf

instance Pretty PkgconfigVersion where
pretty (PkgconfigVersion bs) = PP.text (BS8.unpack bs)

instance Parsec PkgconfigVersion where
parsec = PkgconfigVersion . BS8.pack <$> P.munch1 predicate where
predicate c = isAsciiAlphaNum c || c == '.' || c == '-'

-------------------------------------------------------------------------------
-- rmpvercmp - pure Haskell implementation
-------------------------------------------------------------------------------

-- | Compare two version strings as @pkg-config@ would compare them.
--
-- @since 3.0
rpmvercmp :: BS.ByteString -> BS.ByteString -> Ordering
rpmvercmp a b = go0 (BS.unpack a) (BS.unpack b)
where
go0 :: [Word8] -> [Word8] -> Ordering
go0 xs ys = go1 (dropNonAlnum8 xs) (dropNonAlnum8 ys)

go1 :: [Word8] -> [Word8] -> Ordering
go1 [] [] = EQ
go1 [] _ = LT
go1 _ [] = GT
go1 xs@(x:_) ys
| isDigit8 x =
let (xs1, xs2) = span isDigit8 xs
(ys1, ys2) = span isDigit8 ys
-- numeric segments are always newer than alpha segments
in if null ys1
then GT
else compareInt xs1 ys1 <> go0 xs2 ys2

-- isAlpha
| otherwise =
let (xs1, xs2) = span isAlpha8 xs
(ys1, ys2) = span isAlpha8 ys
in if null ys1
then LT
else compareStr xs1 ys1 <> go0 xs2 ys2

-- compare as numbers
compareInt :: [Word8] -> [Word8] -> Ordering
compareInt xs ys =
-- whichever number has more digits wins
compare (length xs') (length ys') <>
-- equal length: use per character compare, "strcmp"
compare xs' ys'
where
-- drop leading zeros
xs' = dropWhile (== 0x30) xs
ys' = dropWhile (== 0x30) ys

-- strcmp
compareStr :: [Word8] -> [Word8] -> Ordering
compareStr = compare

dropNonAlnum8 :: [Word8] -> [Word8]
dropNonAlnum8 = dropWhile (\w -> not (isDigit8 w || isAlpha8 w))

isDigit8 :: Word8 -> Bool
isDigit8 w = 0x30 <= w && w <= 0x39

isAlpha8 :: Word8 -> Bool
isAlpha8 w = (0x41 <= w && w <= 0x5A) || (0x61 <= w && w <= 0x7A)


Loading

0 comments on commit 01df85e

Please sign in to comment.