@@ -7,7 +7,9 @@ Functions and types related to interpreting and manipulating an ebuild,
77as understood by the Portage package manager.
88-}
99{-# LANGUAGE CPP #-}
10+ {-# LANGUAGE GeneralizedNewtypeDeriving #-}
1011{-# LANGUAGE OverloadedStrings #-}
12+ {-# LANGUAGE TypeFamilies #-} -- Needed to get OverloadedStrings to work
1113module Portage.EBuild
1214 ( EBuild (.. )
1315 , ebuildTemplate
@@ -29,8 +31,12 @@ import qualified Data.Function as F
2931import qualified Data.List as L
3032import qualified Data.List.Split as LS
3133import Data.Version (Version (.. ))
32- import Data.Maybe (catMaybes , maybeToList )
34+ import Control.Monad.Trans.Writer.CPS
35+ import Data.Foldable (toList )
36+ import Data.Maybe (catMaybes )
37+ import Data.Monoid (Endo (.. )) -- more efficient list concat
3338import Hackport.Pretty
39+ import GHC.Exts (IsString (.. ))
3440
3541import Network.URI
3642import qualified Paths_hackport (version )
@@ -100,66 +106,65 @@ ebuildTemplate = EBuild {
100106
101107-- | Pretty-print an 'EBuild' as a 'String'.
102108showEBuild :: TC. UTCTime -> EBuild -> String
103- showEBuild now ebuild = renderDoc $ vsep $
104- [ " # Copyright 1999-" <> this_year <> " Gentoo Authors"
105- , " # Distributed under the terms of the GNU General Public License v2"
106- , emptyDoc
107- , " EAPI=8"
108- , emptyDoc
109- , " # ebuild generated by hackport" <+> pretty (hackportVersion ebuild)
110- , vsep $ map
111- (\ (k,v) -> " #hackport: " <> pretty k <> " : " <> pretty v)
112- (used_options ebuild)
113- , emptyDoc
114- , beforeInherit
115- , " CABAL_FEATURES=" <> dquotes (hsep (map (pretty . render) (features ebuild)))
116- , " inherit haskell-cabal"
117- , emptyDoc
118- , " DESCRIPTION=" <> dquotes (pretty (drop_tdot (description ebuild)))
119- , " HOMEPAGE=" <> dquotes (pretty (toHttps $ expandVars (homepage ebuild)))
120- , emptyDoc
121- , " LICENSE=" <> (either (\ err -> dquotes emptyDoc <> " \t # FIXME: " <> pretty err)
122- (dquotes . pretty)
123- (license ebuild))
124- , " SLOT=" <> dquotes (pretty (slot ebuild))
125- , " KEYWORDS=" <> dquotes (hsep (map pretty (keywords ebuild)))
126- , let iuseList = hsep $ map pretty $ sort_iuse $ L. nub $ iuse ebuild
127- x = if null (iuse ebuild)
128- then Nothing
129- else Just $ " IUSE=" <> dquotes iuseList
130- -- IUSE line if needed, then an empty line
131- in vsep $ maybeToList x ++ [emptyDoc]
132- , dep_str " RDEPEND" (rdepend_extra ebuild) (rdepend ebuild)
133- , dep_str " DEPEND" ( depend_extra ebuild) ( depend ebuild)
134- ] ++ catMaybes
135- [ verbatim
109+ showEBuild now ebuild = replaceLeadingSpaces $ renderDoc $ execLinesBuilder $ do
110+ fromDoc $ " # Copyright 1999-" <> this_year <> " Gentoo Authors"
111+ " # Distributed under the terms of the GNU General Public License v2"
112+ emptyLine
113+ " EAPI=8"
114+ emptyLine
115+ fromDoc $ " # ebuild generated by hackport" <+> pretty (hackportVersion ebuild)
116+ let optLine (k,v) = " #hackport: " <> pretty k <> " : " <> pretty v
117+ fromDocs $ map optLine (used_options ebuild)
118+ emptyLine
119+ preFeatures
120+ fromDoc $ " CABAL_FEATURES=" <> dquotes (hsep (map (pretty . render) (features ebuild)))
121+ " inherit haskell-cabal"
122+ emptyLine
123+ fromDoc $ " DESCRIPTION=" <> dquotes (pretty (drop_tdot (description ebuild)))
124+ fromDoc $ " HOMEPAGE=" <> dquotes (pretty (toHttps (expandVars (homepage ebuild))))
125+ emptyLine
126+ fromDoc $ " LICENSE=" <> (either (\ err -> dquotes emptyDoc <> " \t # FIXME: " <> pretty err)
127+ (dquotes . pretty)
128+ (license ebuild) )
129+ fromDoc $ " SLOT=" <> dquotes (pretty (slot ebuild))
130+ fromDoc $ " KEYWORDS=" <> dquotes (hsep (map pretty (keywords ebuild)))
131+ fromDocs $
132+ if null (iuse ebuild)
133+ then Nothing
134+ else
135+ let iuseList = hsep $ map pretty $ sort_iuse $ L. nub $ iuse ebuild
136+ in Just $ " IUSE=" <> dquotes iuseList
137+ emptyLine
138+ dep_str " RDEPEND" (rdepend_extra ebuild) (rdepend ebuild)
139+ dep_str " DEPEND" ( depend_extra ebuild) ( depend ebuild)
140+ verbatim
136141 (vsep [emptyDoc, " src_prepare() {" ])
137- (src_prepare ebuild)
142+ (map pretty ( src_prepare ebuild) )
138143 " }"
139- , verbatim
144+ verbatim
140145 (vsep [emptyDoc, " src_configure() {" ])
141- (src_configure ebuild)
146+ (map pretty ( src_configure ebuild) )
142147 " }"
143- ]
144148 where
145149 expandVars = replaceMultiVars [ ( name ebuild, " ${PN}" )
146150 , (hackage_name ebuild, " ${HACKAGE_N}" )
147151 ]
148152
149-
150153 this_year :: Doc ann
151154 this_year = pretty $ TC. formatTime TC. defaultTimeLocale " %Y" now
152155
153- beforeInherit =
154- let rev = revision ebuild
155- mpn = cabal_pn ebuild
156- -- Any needed lines then an empty line
157- in vsep $ catMaybes [revLine rev, pnLine <$> mpn] ++ [emptyDoc]
156+ preFeatures =
157+ let rev = revLine (revision ebuild)
158+ pn = pnLine <$> cabal_pn ebuild
159+ preList = catMaybes [rev, pn]
160+ in if null preList
161+ then pure ()
162+ else fromDocs $ preList ++ [emptyDoc]
158163
159164 revLine " 0" = Nothing
160- revLine r = Just $ hcat [ " CABAL_HACKAGE_REVISION=" , pretty r]
165+ revLine r = Just $ " CABAL_HACKAGE_REVISION=" <> pretty r
161166
162- pnLine pn = hcat [ " CABAL_PN=" , dquotes (pretty pn)]
167+ pnLine pn = " CABAL_PN=" <> dquotes (pretty pn)
163168
164169-- | Convert http urls into https urls, unless whitelisted as http-only.
165170--
@@ -203,14 +208,21 @@ sort_iuse = L.sortBy (compare `F.on` dropWhile ( `elem` ("+" :: String)))
203208drop_tdot :: String -> String
204209drop_tdot = reverse . dropWhile (== ' .' ) . reverse
205210
206- verbatim :: Doc ann -> [String ] -> Doc ann -> Maybe (Doc ann )
211+ -- | Print the @pre@ line, each line in the list and the @post@ line, _only_
212+ -- if the list is not empty.
213+ verbatim :: Doc ann -> [Doc ann ] -> Doc ann -> LinesBuilder ann ()
207214verbatim pre s post
208- | null s = Nothing
209- | otherwise = Just $ vsep $ [pre] ++ map (hang 1 . pretty) s ++ [post]
215+ | null s = pure ()
216+ | otherwise = fromDocs $ [pre] ++ s ++ [post]
210217
211- dep_str :: Doc ann -> [String ] -> Dependency -> Doc ann
212- dep_str var extra dep = var <> " =" <> dquotes (hang 1 inner)
213- where inner = vsep $ map pretty extra ++ [showDepend (PN. normalize_depend dep)]
218+ dep_str :: Doc ann -> [String ] -> Dependency -> LinesBuilder ann ()
219+ dep_str var extra dep = fromDoc $ vsep
220+ [ var <> " =\" "
221+ , indent 1 (
222+ vsep (map pretty extra ++ [showDepend (PN. normalize_depend dep)])
223+ )
224+ , " \" "
225+ ]
214226
215227getRestIfPrefix :: String -- ^ the prefix
216228 -> String -- ^ the string
@@ -236,3 +248,30 @@ replaceMultiVars [] str = str
236248replaceMultiVars whole@ ((pname,cont): rest) str = case subStr cont str of
237249 Nothing -> replaceMultiVars rest str
238250 Just (pre,post) -> (replaceMultiVars rest pre)++ pname++ (replaceMultiVars whole post)
251+
252+
253+ emptyLine :: LinesBuilder ann ()
254+ emptyLine = fromDoc emptyDoc
255+
256+ fromDoc :: Doc ann -> LinesBuilder ann ()
257+ fromDoc = fromDocs . (: [] )
258+
259+ fromDocs :: Foldable t => t (Doc ann ) -> LinesBuilder ann ()
260+ fromDocs = LinesBuilder . tell . Endo . (\ x -> (toList x ++ ))
261+
262+ newtype LinesBuilder ann a = LinesBuilder (Writer (Endo [Doc ann ]) a )
263+ deriving (Functor , Applicative , Monad )
264+
265+ instance (a ~ () ) => IsString (LinesBuilder ann a ) where
266+ fromString = fromDoc . fromString
267+
268+ execLinesBuilder :: LinesBuilder ann a -> Doc ann
269+ execLinesBuilder (LinesBuilder w) = vsep (appEndo (execWriter w) [] )
270+
271+ -- | Replace any leading spaces on each line with tabs
272+ replaceLeadingSpaces :: String -> String
273+ replaceLeadingSpaces = unlines . map go . lines
274+ where
275+ go (' ' : rest) = ' \t ' : go rest
276+ go rest = rest
277+
0 commit comments