Skip to content

Commit ea804f9

Browse files
committed
Parse 'installed' constraints
Cabal constraints, like those returned from stackage's cabal.config endpoint, can specify packages in a number of ways, including e.g. 'mtl installed'. Previously we were ignoring such constraints. We now parse these too.
1 parent 7779e97 commit ea804f9

File tree

21 files changed

+289
-168
lines changed

21 files changed

+289
-168
lines changed

clc-stackage.cabal

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,18 @@ library utils
3535
CLC.Stackage.Utils.JSON
3636
CLC.Stackage.Utils.Logging
3737
CLC.Stackage.Utils.OS
38+
CLC.Stackage.Utils.Package
3839
CLC.Stackage.Utils.Paths
3940

4041
build-depends:
4142
, aeson >=2.0 && <2.3
4243
, aeson-pretty ^>=0.8.9
4344
, bytestring >=0.10.12.0 && <0.13
45+
, deepseq >=1.4.6.0 && <1.6
4446
, directory ^>=1.3.5.0
4547
, file-io ^>=0.1.0.0
4648
, filepath >=1.4.100.0 && <1.6
49+
, megaparsec >=9.5.0 && <9.8
4750
, pretty-terminal ^>=0.1.0.0
4851
, text >=1.2.3.2 && <2.2
4952
, time >=1.9.3 && <1.15
@@ -63,11 +66,12 @@ library parser
6366
, aeson
6467
, bytestring
6568
, containers >=0.6.3.1 && <0.9
66-
, deepseq >=1.4.6.0 && <1.6
69+
, deepseq
6770
, filepath
6871
, http-client >=0.5.9 && <0.8
6972
, http-client-tls ^>=0.3
7073
, http-types ^>=0.12.3
74+
, megaparsec
7175
, text
7276
, utils
7377

@@ -79,12 +83,10 @@ library builder
7983
CLC.Stackage.Builder
8084
CLC.Stackage.Builder.Batch
8185
CLC.Stackage.Builder.Env
82-
CLC.Stackage.Builder.Package
8386
CLC.Stackage.Builder.Process
8487
CLC.Stackage.Builder.Writer
8588

8689
build-depends:
87-
, aeson
8890
, containers
8991
, directory
9092
, filepath
@@ -146,6 +148,7 @@ test-suite unit
146148
Unit.CLC.Stackage.Parser.API
147149
Unit.CLC.Stackage.Runner.Env
148150
Unit.CLC.Stackage.Runner.Report
151+
Unit.CLC.Stackage.Utils.Package
149152
Unit.Prelude
150153

151154
build-depends:

src/builder/CLC/Stackage/Builder/Batch.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import CLC.Stackage.Builder.Env
1010
packagesToBuild
1111
),
1212
)
13-
import CLC.Stackage.Builder.Package (Package)
13+
import CLC.Stackage.Utils.Package (Package)
1414
import Data.Bifunctor (Bifunctor (first))
1515
import Data.List qualified as L
1616
import Data.List.NonEmpty (NonEmpty ((:|)), (<|))

src/builder/CLC/Stackage/Builder/Env.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ module CLC.Stackage.Builder.Env
66
)
77
where
88

9-
import CLC.Stackage.Builder.Package (Package)
109
import CLC.Stackage.Utils.Logging qualified as Logging
10+
import CLC.Stackage.Utils.Package (Package)
1111
import Data.IORef (IORef)
1212
import Data.List.NonEmpty (NonEmpty)
1313
import Data.Set (Set)

src/builder/CLC/Stackage/Builder/Package.hs

Lines changed: 0 additions & 65 deletions
This file was deleted.

src/builder/CLC/Stackage/Builder/Process.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ import CLC.Stackage.Builder.Env
1818
WriteLogs (WriteLogsCurrent, WriteLogsNone, WriteLogsSaveFailures),
1919
)
2020
import CLC.Stackage.Builder.Env qualified as Env
21-
import CLC.Stackage.Builder.Package qualified as Package
2221
import CLC.Stackage.Builder.Writer qualified as Writer
2322
import CLC.Stackage.Utils.IO qualified as IO
2423
import CLC.Stackage.Utils.Logging qualified as Logging
24+
import CLC.Stackage.Utils.Package qualified as Package
2525
import CLC.Stackage.Utils.Paths qualified as Paths
2626
import Control.Exception (throwIO)
2727
import Control.Monad (when)
@@ -115,7 +115,7 @@ buildProject env idx pkgs = do
115115
mconcat
116116
[ T.pack $ show idx,
117117
": ",
118-
T.intercalate ", " (Package.toText <$> pkgsList)
118+
T.intercalate ", " (Package.toTextInstalled <$> pkgsList)
119119
]
120120
pkgsList = NE.toList pkgs.unPackageGroup
121121
pkgsSet = Set.fromList pkgsList

src/builder/CLC/Stackage/Builder/Writer.hs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ module CLC.Stackage.Builder.Writer
55
where
66

77
import CLC.Stackage.Builder.Batch (PackageGroup (unPackageGroup))
8-
import CLC.Stackage.Builder.Package (Package)
9-
import CLC.Stackage.Builder.Package qualified as Package
108
import CLC.Stackage.Utils.IO qualified as IO
9+
import CLC.Stackage.Utils.Package (Package)
10+
import CLC.Stackage.Utils.Package qualified as Package
1111
import CLC.Stackage.Utils.Paths qualified as Paths
1212
import Data.List.NonEmpty qualified as NE
1313
import Data.Text (Text)
@@ -33,7 +33,7 @@ writeCabalProjectLocal pkgs = IO.writeBinaryFile path constraintsSrc
3333
path = Paths.generatedCabalProjectLocalPath
3434
constraintsSrc = TEnc.encodeUtf8 constraintsTxt
3535
constraintsTxt = T.unlines $ "constraints:" : constraints
36-
constraints = (\p -> " " <> Package.toText p <> ",") <$> pkgs
36+
constraints = (\p -> " " <> Package.toCabalConstraintsText p) <$> pkgs
3737

3838
-- | Writes the package set to a cabal file for building. This will be called
3939
-- for each group we want to build.
@@ -60,7 +60,7 @@ mkCabalFile pkgs =
6060
" default-language: Haskell2010"
6161
]
6262
where
63-
pkgsTxt = (\p -> pkgsIndent <> Package.toDepText p) <$> NE.toList pkgs.unPackageGroup
63+
pkgsTxt = (\p -> pkgsIndent <> Package.toCabalDepText p) <$> NE.toList pkgs.unPackageGroup
6464

6565
-- build-depends is indented 4, then 2 for the package itself.
6666
pkgsIndent :: Text

src/parser/CLC/Stackage/Parser.hs

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ module CLC.Stackage.Parser
1111
where
1212

1313
import CLC.Stackage.Parser.API
14-
( PackageResponse (name, version),
15-
StackageResponse (packages),
14+
( StackageResponse (packages),
1615
)
1716
import CLC.Stackage.Parser.API qualified as API
1817
import CLC.Stackage.Parser.API.CabalConfig qualified as CabalConfig
@@ -21,6 +20,8 @@ import CLC.Stackage.Utils.JSON qualified as JSON
2120
import CLC.Stackage.Utils.Logging qualified as Logging
2221
import CLC.Stackage.Utils.OS (Os (Linux, Osx, Windows))
2322
import CLC.Stackage.Utils.OS qualified as OS
23+
import CLC.Stackage.Utils.Package (Package)
24+
import CLC.Stackage.Utils.Package qualified as Package
2425
import Control.Monad (when)
2526
import Data.Aeson (FromJSON, ToJSON)
2627
import Data.Foldable (for_)
@@ -33,13 +34,13 @@ import System.OsPath (OsPath, osp)
3334

3435
-- | Retrieves the list of packages, based on
3536
-- 'CLC.Stackage.Parser.API.stackageUrl'.
36-
getPackageList :: Logging.Handle -> Maybe OsPath -> IO [PackageResponse]
37+
getPackageList :: Logging.Handle -> Maybe OsPath -> IO [Package]
3738
getPackageList hLogger msnapshotPath =
3839
getPackageListByOs hLogger msnapshotPath OS.currentOs
3940

4041
-- | Prints the package list to a file.
41-
printPackageList :: Bool -> Maybe Os -> IO ()
42-
printPackageList incVers mOs = do
42+
printPackageList :: Maybe Os -> IO ()
43+
printPackageList mOs = do
4344
case mOs of
4445
Just os -> printOsList os
4546
Nothing -> for_ [minBound .. maxBound] printOsList
@@ -49,23 +50,18 @@ printPackageList incVers mOs = do
4950
file Windows = [osp|pkgs_windows.txt|]
5051

5152
printOsList os = do
52-
pkgs <- getPackageListByOsFmt incVers os
53+
pkgs <- getPackageListByOsFmt os
5354
let txt = T.unlines pkgs
5455
IO.writeFileUtf8 (file os) txt
5556

5657
-- | Retrieves the package list formatted to text.
57-
getPackageListByOsFmt :: Bool -> Os -> IO [Text]
58-
getPackageListByOsFmt incVers =
59-
(fmap . fmap) toText
58+
getPackageListByOsFmt :: Os -> IO [Text]
59+
getPackageListByOsFmt =
60+
(fmap . fmap) Package.toTextInstalled
6061
. getPackageListByOs Logging.mkDefaultLogger Nothing
61-
where
62-
toText r =
63-
if incVers
64-
then r.name <> "-" <> r.version
65-
else r.name
6662

6763
-- | Helper in case we want to see what the package set for a given OS is.
68-
getPackageListByOs :: Logging.Handle -> Maybe OsPath -> Os -> IO [PackageResponse]
64+
getPackageListByOs :: Logging.Handle -> Maybe OsPath -> Os -> IO [Package]
6965
getPackageListByOs hLogger msnapshotPath os = do
7066
excludedPkgs <- getExcludedPkgs os
7167
let filterExcluded = flip Set.notMember excludedPkgs . (.name)

src/parser/CLC/Stackage/Parser/API.hs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
module CLC.Stackage.Parser.API
33
( -- * Querying stackage
44
StackageResponse (..),
5-
PackageResponse (..),
65
getStackage,
76

87
-- ** Exceptions
@@ -22,7 +21,6 @@ import CLC.Stackage.Parser.API.Common
2221
ReasonReadBody,
2322
ReasonStatus
2423
),
25-
PackageResponse (name, version),
2624
StackageException (MkStackageException),
2725
StackageResponse (MkStackageResponse, packages),
2826
)

src/parser/CLC/Stackage/Parser/API/CabalConfig.hs

Lines changed: 12 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,22 @@ import CLC.Stackage.Parser.API.Common
1313
ReasonReadBody,
1414
ReasonStatus
1515
),
16-
PackageResponse
17-
( MkPackageResponse,
18-
name,
19-
version
20-
),
2116
StackageException (MkStackageException),
2217
StackageResponse (MkStackageResponse),
2318
getStatusCode,
2419
)
2520
import CLC.Stackage.Utils.Exception qualified as Ex
21+
import CLC.Stackage.Utils.Package qualified as Package
2622
import Control.Exception (throwIO)
2723
import Control.Monad (when)
28-
import Data.List qualified as L
2924
import Data.Maybe (catMaybes)
3025
import Data.Text (Text)
3126
import Data.Text qualified as T
3227
import Data.Text.Encoding qualified as TEnc
3328
import Network.HTTP.Client (BodyReader, Manager, Request, Response)
3429
import Network.HTTP.Client qualified as HttpClient
30+
import Text.Megaparsec qualified as MP
31+
import Text.Megaparsec.Char qualified as MPC
3532

3633
-- | Given http manager and snapshot string, queries the cabal config
3734
-- endpoint. This is intended as a backup, for when the primary endpoint fails.
@@ -80,45 +77,13 @@ parseCabalConfig =
8077
. fmap parseCabalConfigLine
8178
. T.lines
8279

83-
-- | Parses a line like '<pkg> ==<vers>'. This does not currently handle
84-
-- "installed" packages e.g. 'mtl installed'. This probably isn't a big deal,
85-
-- since all such libs will be built transitively anyway. That said, if
86-
-- we wanted to fix it, we would probably want to change PackageResponse's
87-
--
88-
-- version :: Text
89-
--
90-
-- field to
91-
--
92-
-- version :: Maybe Text
93-
--
94-
-- and parse "installed" to Nothing. Then, when we go to write the generated
95-
-- cabal file, Nothing will correspond to writing no version number.
96-
-- (CLC.Stackage.Builder.Package.toText).
97-
parseCabalConfigLine :: Text -> Maybe PackageResponse
98-
-- splitOn rather than breakOn since the former drops the delim, which is
99-
-- convenient.
100-
parseCabalConfigLine txt = case T.splitOn delim txt of
101-
[nameRaw, versRaw] -> do
102-
(v, c) <- T.unsnoc versRaw
103-
-- Strip trailing comma if it exists. Otherwise take everything.
104-
let version = if c == ',' then v else T.snoc v c
105-
106-
-- This line handles prefixes e.g. whitespace or a stanza e.g.
107-
--
108-
-- constraints: abstract-deque ==0.3,
109-
-- abstract-deque-tests ==0.3,
110-
-- ...
111-
--
112-
-- We split pre-delim on whitespace, and take the last word.
113-
(_, name) <- L.unsnoc $ T.words nameRaw
114-
115-
-- T.strip as trailing characters can cause problems e.g. windows can
116-
-- pick up \r.
117-
Just $
118-
MkPackageResponse
119-
{ name = T.strip name,
120-
version = T.strip version
121-
}
122-
_ -> Nothing
80+
-- | Parses a line like '<pkg> ==<vers>'.
81+
parseCabalConfigLine :: Text -> Maybe Package.Package
82+
parseCabalConfigLine txt = case MP.parse (MPC.space *> p) "package" txt of
83+
Right x -> Just x
84+
Left _ -> Nothing
12385
where
124-
delim = " =="
86+
-- Optional case for leading constraints section.
87+
p = do
88+
_ <- MP.optional (MPC.string "constraints:" *> MPC.space)
89+
Package.packageParser

src/parser/CLC/Stackage/Parser/API/Common.hs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
module CLC.Stackage.Parser.API.Common
33
( -- * Types
44
StackageResponse (..),
5-
PackageResponse (..),
65

76
-- * Exception
87
StackageException (..),
@@ -13,13 +12,13 @@ module CLC.Stackage.Parser.API.Common
1312
)
1413
where
1514

15+
import CLC.Stackage.Utils.Package (Package)
1616
import Control.DeepSeq (NFData)
1717
import Control.Exception
1818
( Exception (displayException),
1919
SomeException,
2020
)
2121
import Data.ByteString (ByteString)
22-
import Data.Text (Text)
2322
import Data.Text.Encoding.Error (UnicodeException)
2423
import GHC.Generics (Generic)
2524
import Network.HTTP.Client (Response)
@@ -29,15 +28,7 @@ import Network.HTTP.Types.Status qualified as Status
2928

3029
-- | Stackage response. This type unifies different stackage responses.
3130
newtype StackageResponse = MkStackageResponse
32-
{ packages :: [PackageResponse]
33-
}
34-
deriving stock (Eq, Generic, Show)
35-
deriving anyclass (NFData)
36-
37-
-- | Package in a stackage snapshot.
38-
data PackageResponse = MkPackageResponse
39-
{ name :: Text,
40-
version :: Text
31+
{ packages :: [Package]
4132
}
4233
deriving stock (Eq, Generic, Show)
4334
deriving anyclass (NFData)

0 commit comments

Comments
 (0)