packages feed

exherbo-cabal 0.1.1.0 → 0.2.0.0

raw patch · 8 files changed

+436/−272 lines, 8 filesdep +ansi-wl-pprintdep +data-defaultdep +optparse-applicativedep ~exherbo-cabalPVP ok

version bump matches the API change (PVP)

Dependencies added: ansi-wl-pprint, data-default, optparse-applicative

Dependency ranges changed: exherbo-cabal

API changes (from Hackage documentation)

- ExRender: exRender :: ExRender a => a -> String
- ExRender: instance ExRender.ExRender Distribution.License.License
- ExRender: instance ExRender.ExRender Distribution.Package.Dependency
- ExRender: instance ExRender.ExRender Distribution.PackageDescription.GenericPackageDescription
- ExRender: instance ExRender.ExRender Distribution.Version.LowerBound
- ExRender: instance ExRender.ExRender Distribution.Version.UpperBound
- ExRender: instance ExRender.ExRender Distribution.Version.VersionInterval
- ExRender: instance ExRender.ExRender Distribution.Version.VersionRange
- ExRender: instance ExRender.ExRender Documentation.Haddock.Parser.Identifier
- ExRender: instance ExRender.ExRenderQ Documentation.Haddock.Parser.Identifier
- ExRender: instance ExRender.ExRenderQ GHC.Base.String
- ExRender: instance ExRender.ExRenderQ id => ExRender.ExRenderQ (Documentation.Haddock.Types.DocH mod id)
+ ExRender: ExCabalEnv :: Version -> Doc -> String -> ExCabalEnv
+ ExRender: [exBugsTo] :: ExCabalEnv -> String
+ ExRender: [exCopyright] :: ExCabalEnv -> Doc
+ ExRender: [exGHCVersion] :: ExCabalEnv -> Version
+ ExRender: class ExRenderPackage a
+ ExRender: data ExCabalEnv
+ ExRender: exRenderPkg :: ExRenderPackage a => ExPackageEnv a -> a -> String
+ ExRender: instance Data.Default.Class.Default ExRender.ExCabalEnv
+ ExRender: instance ExRender.Base.ExRender Distribution.PackageDescription.GenericPackageDescription
+ ExRender: instance ExRender.ExRenderPackage Distribution.PackageDescription.GenericPackageDescription
+ ExRender: instance GHC.Show.Show ExRender.ExCabalEnv

Files

exherbo-cabal.cabal view
@@ -10,7 +10,7 @@ -- PVP summary:      +-+------- breaking API changes --                   | | +----- non-breaking API additions --                   | | | +--- code changes with no API change-version:             0.1.1.0+version:             0.2.0.0  -- A short (one-line) description of the package. synopsis:            Exheres generator for cabal packages@@ -54,10 +54,17 @@   Exposed-modules:     ExRender +  Other-modules:+    ExRender.Base+    ExRender.Dependency+    ExRender.Haddock+    ExRender.License+   build-depends:     Cabal >=1.20 && <1.23,     base >=4.7 && <4.9,     containers,+    data-default >=0.2 && <1,     haddock-library >=1.0 && <1.3,     pretty >=1.1 && <1.2 @@ -92,11 +99,14 @@   -- Other library packages from which modules are imported.   build-depends:     Cabal >=1.20 && <1.23,+    ansi-wl-pprint >=0.6 && <0.7,     base >=4.7 && <4.9,     bytestring >=0.10 && <0.11,-    exherbo-cabal >=0.1.1 && <0.2,+    data-default >=0.2 && <1,+    exherbo-cabal >=0.2 && <0.3,     http-client >=0.4 && <0.5,     http-types <1,+    optparse-applicative >=0.12 && <0.13,     pcre-light <0.5      -- Base language which the package is written in.
src/ExRender.hs view
@@ -1,214 +1,65 @@ -- Copyright © 2015 Mykola Orliuk <virkony@gmail.com> -- Distributed under the terms of the GNU General Public License v2 +{-# OPTIONS_GHC -fno-warn-orphans #-}++{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UnicodeSyntax, ViewPatterns, LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleInstances #-} -module ExRender (exDisp, exDispQ, exRender) where+module ExRender (ExCabalEnv(..), ExRenderPackage, exDisp, exDispQ, exRenderPkg) where  import Data.Maybe import Data.List import Data.Function+import Data.Default import qualified Data.Map as M-import Text.PrettyPrint  import Control.Arrow ((&&&))  import Distribution.Text import Distribution.Package import Distribution.Version-import Distribution.License import Distribution.Compiler import Distribution.System import Distribution.PackageDescription import Documentation.Haddock.Parser-import Documentation.Haddock.Types hiding (Version) -exWrapWidth ∷ Int-exWrapWidth = 80---- TODO: make GHC version configurable-exGHCVersion ∷ Version-exGHCVersion = case buildCompilerId of-                (CompilerId GHC ver) → ver-                x → error $ "Unsupported compiler " ++ show x--exKnownLicenses ∷ [String]-exKnownLicenses = ["CC0", "AGPL-3"]---- | Double-quoted string for bash-dquoted ∷ String → String-dquoted [] = []-dquoted ('\\':xs) = "\\\\" ++ dquoted xs-dquoted ('"':xs) = "\\\"" ++ dquoted xs-dquoted ('`':xs) = "\\`" ++ dquoted xs-dquoted ('$':xs) = "\\$" ++ dquoted xs-dquoted (x:xs) = x : dquoted xs--softWidth ∷ Int → [String] → [[String]]-softWidth width = build 0 [] where-    build _ ys [] = [reverse ys]-    build 0 [] (w:ws) = build (length w) [w] ws-    build n ys (w:ws) | n' > width = reverse ys : build 0 [] (w:ws)-                      | otherwise = build n' (w : ys) ws-        where-            n' = length w + n--reflow ∷ Int → String → Doc-reflow width = vcat . map (text . unwords) . softWidth width . words---- |Wrap doc with spaces around-spaces ∷ Doc → Doc-spaces doc | isEmpty doc = empty-           | otherwise = space <> doc <> space---- |Wrap with brackets non-empty doc-nbrackets ∷ Doc → Doc-nbrackets doc | isEmpty doc = empty-              | otherwise = brackets doc--class ExRender a where-    -- |Renders 'a' into a 'Doc' representing some part of exheres-    exDisp :: a → Doc--class ExRenderQ a where-    -- |Renders 'a' into a 'Doc' to be placed within double-quotes inside of exheres-    exDispQ :: a → Doc--instance ExRenderQ String where-    -- TODO: exDispQ = sep . map text . words . dquoted-    exDispQ = text . dquoted--instance ExRender Identifier where exDisp (_, s, _) = text s-instance ExRenderQ Identifier where exDispQ = exDisp--instance ExRenderQ id => ExRenderQ (DocH mod id) where-    exDispQ x = case x of-        DocEmpty → empty-        DocAppend a b → exDispQ a <> exDispQ b-        DocString s → exDispQ s-        DocParagraph a → exDispQ a-        DocIdentifier s → exDispQ s-        DocModule s → exDispQ s-        DocWarning a → exDispQ a-        DocEmphasis a → exDispQ a-        DocMonospaced a → exDispQ a-        DocBold a → exDispQ a-        DocHyperlink (Hyperlink _ (Just s)) → exDispQ s-        DocHyperlink (Hyperlink s Nothing) → exDispQ s-        DocPic _ → empty -- XXX: no images in description-        DocAName s → exDispQ s-        DocProperty s → exDispQ s-        DocExamples _ → empty -- XXX: examples are filtered out-        _ -> error $ "Unsupported haddock node"--instance ExRender LowerBound where-    exDisp (LowerBound v InclusiveBound) = ">=" <> disp v-    exDisp (LowerBound v ExclusiveBound) = ">" <> disp v--instance ExRender UpperBound where-    exDisp (UpperBound v InclusiveBound) = "<=" <> disp v-    exDisp (UpperBound v ExclusiveBound) = "<" <> disp v-    exDisp x = error $ "Unsupported UpperBound: " ++ show x---- | Render some of VersionInterval's that can be represented with a single condition and thus suitable for using in disjunction list.------ >>> map maybeExVersion $ asVersionIntervals (fromJust $ simpleParse ">=1.0 || ==0.1.*" :: VersionRange)--- [Just =0.1*,Just >=1.0]-maybeExVersion ∷ VersionInterval → Maybe Doc-maybeExVersion = \case-    -- >=x && <=x-    (LowerBound a InclusiveBound, UpperBound b InclusiveBound)-        | a == b → Just $ char '=' <> disp a--    -- <x, <=x-    (LowerBound (Version [0] []) InclusiveBound, ub) → Just $ exDisp ub--    -- >=x, >x-    (lb, NoUpperBound) → Just $ exDisp lb--    (LowerBound (Version [] _) _, _) → Nothing-    (_, UpperBound (Version [] _) _) → Nothing-    -- >=x.y && <x.y'-    (LowerBound v@(Version a []) InclusiveBound, UpperBound (Version b []) ExclusiveBound)-        | init a == init b && succ (last a) == last b →-            Just $ char '=' <> disp v <> char '*'--    (LowerBound (Version [_] _) _, _) → Nothing-    -- >=x.y.z && <x.y'-    (LowerBound v@(Version a []) InclusiveBound, UpperBound (Version b []) ExclusiveBound)-        | init a' == init b && succ (last a') == last b →-                Just $ char '~' <> disp v-            where a' = init a--    _ → Nothing---- | Transform VersionInterval in a sequence of disjunctions------ >>> map exVersions $ asVersionIntervals (fromJust $ simpleParse ">=1.0" :: VersionRange)--- [[>=1.0]]--- >>> map exVersions $ asVersionIntervals (fromJust $ simpleParse ">=1.0 && <1.3" :: VersionRange)--- [[=1.0*,=1.1*,=1.2*]]--- >>> map exVersions $ asVersionIntervals (fromJust $ simpleParse ">=1.0 && <=1.3" :: VersionRange)--- [[=1.0*,=1.1*,=1.2*,=1.3]]--- >>> map exVersions $ asVersionIntervals (fromJust $ simpleParse ">=1 && <=1.3" :: VersionRange)--- [[=1,=1.0*,=1.1*,=1.2*,=1.3]]--- >>> map exVersions $ asVersionIntervals (fromJust $ simpleParse ">=1 && <=1.0.3" :: VersionRange)--- [[=1,=1.0,=1.0.0*,=1.0.1*,=1.0.2*,=1.0.3]]-exVersions ∷ VersionInterval → [Doc]-exVersions = \case-    (maybeExVersion → Just x) → [x]--    -- ... && <=x.b-    (lb, UpperBound v InclusiveBound) →-        exVersions (lb, UpperBound v ExclusiveBound) ++ [char '=' <> disp v]--    -- >=x.a && <x.b-    (LowerBound va@(Version a _) InclusiveBound, ub@(UpperBound (Version b _) ExclusiveBound))-        | init a == init b → do-            c ← [init a ++ [i] | i ← [last a .. last b - 1]]-            return $ char '=' <> disp (Version c []) <> char '*'-        | length a < length b →-            char '=' <> disp va : exVersions (LowerBound (Version (a ++ [0]) []) InclusiveBound, ub)-    _ → []--instance ExRender VersionInterval where-    exDisp (LowerBound (Version [0] []) InclusiveBound, NoUpperBound) = empty-    exDisp (maybeExVersion → Just exVi) = exVi-    exDisp (lb, ub) = exDisp lb <> char '&' <> exDisp ub+import ExRender.Base+import ExRender.Haddock ()+import ExRender.License ()+import ExRender.Dependency () -instance ExRender VersionRange where-    exDisp vr = case asVersionIntervals vr of-        [vi] → nbrackets $ exDisp vi-        (concatMap exVersions → exVis) | not $ null exVis → nbrackets . hcat $ punctuate (char '|') exVis-        _ → error $ "Unsupported version range: " ++ display vr+data ExCabalEnv = ExCabalEnv+    { exGHCVersion ∷ Version+    , exCopyright ∷ Doc+    , exBugsTo ∷ String+    }+    deriving (Show) -instance ExRender Dependency where-    exDisp (Dependency n vr) = "dev-haskell/" <> disp n <> exDisp vr+instance Default ExCabalEnv where+    def = ExCabalEnv+        { exGHCVersion = case buildCompilerId of+            (CompilerId GHC ver) → ver+            x → error $ "Unsupported compiler " ++ show x+        , exCopyright = vcat+            [ "# Copyright 2015 Mykola Orliuk <virkony@gmail.com>"+            , "# Distributed under the terms of the GNU General Public License v2"+            ]+        , exBugsTo = "virkony@gmail.com"+        } -instance ExRender License where-    exDisp (GPL Nothing) = "Unspecified-GPL"-    exDisp (GPL (Just v)) = "GPL-" <> disp v-    exDisp (AGPL (Just v)) = "AGPL-" <> disp v-    exDisp (LGPL Nothing) = "Unspecified-LGPL"-    exDisp (LGPL (Just v)) = "LGPL-" <> disp v-    exDisp (Apache Nothing) = "Unspecified-Apache"-    exDisp (Apache (Just v)) = "Apache-" <> disp v-    exDisp (MPL v) = "MPL-" <> disp v-    exDisp BSD2 = "BSD-2"-    exDisp BSD3 = "BSD-3"-    exDisp BSD4 = "BSD-4"-    exDisp ISC = "ISC"-    exDisp MIT = "MIT"-    exDisp PublicDomain = "public-domain"-    exDisp (UnknownLicense "BSD2") = "BSD-2"-    exDisp (UnknownLicense "MPL-2") = "MPL-2.0"-    exDisp (UnknownLicense x) | x `elem` exKnownLicenses = text x-    exDisp x = error $ "Unsupported license: " ++ display x+class ExRenderPackage a where+    type ExPackageEnv a+    exDispPkg ∷ ExPackageEnv a → a → Doc  instance ExRender GenericPackageDescription where-    exDisp descr = exheres where+    exDisp = exDispPkg def++instance ExRenderPackage GenericPackageDescription where+    type ExPackageEnv GenericPackageDescription = ExCabalEnv+    exDispPkg env descr = exheres where         nameSelf = pkgName . package $ packageDescription descr         ignoredPkgIds = map (fromJust . simpleParse) ["base", "ghc", "ghc-prim"] @@ -227,17 +78,17 @@         exLibDeps | null libDeps = empty                   | otherwise = exDepFn "haskell_lib_dependencies" libDeps             where-                libDeps = filter (not . ignoredDep) (collectLibDeps descr)+                libDeps = filter (not . ignoredDep) (collectLibDeps env descr)          exBinDeps | null binDeps = empty                   | otherwise = exDepFn "haskell_bin_dependencies" binDeps             where-                binDeps = filter (not . ignoredBinDep) (collectBinDeps descr)+                binDeps = filter (not . ignoredBinDep) (collectBinDeps env descr)          exTestDeps = case condTestSuites descr of             [] → empty             _ → exDepFn "haskell_test_dependencies" testDeps where-                testDeps = filter (not . ignoredTestDep) (collectTestDeps descr)+                testDeps = filter (not . ignoredTestDep) (collectTestDeps env descr)          exDependencies = vcat [             "DEPENDENCIES=\"",@@ -263,10 +114,9 @@                                  ]                 exParams = spaces $ exHasLib <+> exHasBin <+> exHasOptions -        exSlot = if hasLib then empty else exField "SLOT" "0"+        exSlot = if not hasBin && hasLib then empty else exField "SLOT" "0"         exheres = vcat [-            "# Copyright 2015 Mykola Orliuk <virkony@gmail.com>",-            "# Distributed under the terms of the GNU General Public License v2",+            exCopyright env,             "# Generated for " <> disp (package pkgDescr),             "",             exRequire,@@ -275,33 +125,33 @@             exFieldDoc "DESCRIPTION" (exDispQ . toRegular . parseString $ description pkgDescr),             exField "HOMEPAGE" (homepage pkgDescr),             "",-            exField "LICENCES" (exRender $ license pkgDescr),+            exField "LICENCES" (render . exDisp $ license pkgDescr),             exSlot,             exField "PLATFORMS" "~amd64",             "",             exDependencies,             "",-            exField "BUGS_TO" "virkony@gmail.com",+            exField "BUGS_TO" (exBugsTo env),             ""             ]  -- |Collect dependencies from all 'CondTree' nodes of -- 'GenericPackageDescription' using provided view-collectDeps ∷ (GenericPackageDescription → [CondTree ConfVar [Dependency] a])+collectDeps ∷ ExCabalEnv → (GenericPackageDescription → [CondTree ConfVar [Dependency] a])               → GenericPackageDescription → [Dependency]-collectDeps view descr = concatMap build (view descr) where+collectDeps env view descr = concatMap build (view descr) where     flags = M.fromList . map (flagName &&& id) $ genPackageFlags descr     eval (Var (Flag k)) = flagDefault . fromJust $ M.lookup k flags     eval (Var (OS Linux)) = True -- TODO: solve this hard-coded OS assumption     eval (Var (OS _)) = False     eval (Var (Arch X86_64)) = True -- TODO: support other platforms besides amd64     eval (Var (Arch _)) = False-    eval (Var (Impl GHC vr)) = exGHCVersion `withinRange` vr+    eval (Var (Impl GHC vr)) = exGHCVersion env `withinRange` vr     eval (Var (Impl _ _)) = False -- XXX: no support for non-GHC compilers     eval (Lit f) = f     eval (CNot e) = not (eval e)     eval (COr a b) = eval a || eval b-    eval (CAnd a b) = eval a || eval b+    eval (CAnd a b) = eval a && eval b     -- eval e = error $ "Unsupported expr " ++ show e      build t = condTreeConstraints t ++ concatMap buildOptional (condTreeComponents t)@@ -310,27 +160,14 @@     buildOptional (_, _, Just t) = build t     buildOptional (_, _, Nothing) = [] -collectLibDeps, collectBinDeps, collectTestDeps ∷ GenericPackageDescription → [Dependency]-collectLibDeps = collectDeps (maybeToList . condLibrary)-collectBinDeps = collectDeps (map snd . condExecutables)-collectTestDeps = collectDeps (map snd . condTestSuites)---- |Render 'a' to a final part of Exheres-exRender ∷ ExRender a ⇒ a → String-exRender = render . exDisp---- |Render a multi-line meta-field of Exheres if non-empty value-exFieldDoc ∷ String → Doc → Doc-exFieldDoc name value | isEmpty value = empty-                      | otherwise = vcat [text name <> "=\"", value, char '"']+collectLibDeps, collectBinDeps, collectTestDeps ∷ ExCabalEnv → GenericPackageDescription → [Dependency]+collectLibDeps env = collectDeps env (maybeToList . condLibrary)+collectBinDeps env = collectDeps env (map snd . condExecutables)+collectTestDeps env = collectDeps env (map snd . condTestSuites) --- |Render a single-line with potential wrap meta-field of Exheres if non-empty value-exField ∷ String → String → Doc-exField _ "" = empty-exField name x | length singleLine < exWrapWidth = text singleLine-               | otherwise = exFieldDoc name (reflow exWrapWidth (dquoted x))-    where-        singleLine = name ++ "=\"" ++ dquoted x ++ "\""+-- | Render 'a' to a final Exheres+exRenderPkg ∷ ExRenderPackage a ⇒ ExPackageEnv a → a → String+exRenderPkg env = render . exDispPkg env  -- |Sort dependencies according to Exherbo order sortDeps ∷ [Dependency] → [Dependency]@@ -347,16 +184,3 @@  -- TODO: drop test deps that already in build -- TODO: use renderStyle instead of manual wrapping---- $setup------ doctest examples:------ >>> exRender (fromJust $ simpleParse ">=1.0 && <1.3" :: VersionRange)--- "[>=1.0&<1.3]"--- >>> exRender (fromJust $ simpleParse ">=1.1 && <2" :: VersionRange)--- "[~1.1]"--- >>> exRender (fromJust $ simpleParse "==1.* || ==3.*" :: VersionRange)--- "[=1*|=3*]"--- >>> exRender (fromJust $ simpleParse "==1.1.* || ==1.0.* || ==0.11.*" :: VersionRange)--- "[=0.11*|=1.0*|=1.1*]"
+ src/ExRender/Base.hs view
@@ -0,0 +1,74 @@+-- Copyright © 2015 Mykola Orliuk <virkony@gmail.com>+-- Distributed under the terms of the GNU General Public License v2++{-# LANGUAGE UnicodeSyntax #-}+{-# LANGUAGE FlexibleInstances #-}++module ExRender.Base+    ( module ExRender.Base+    , module Text.PrettyPrint+    ) where++import Text.PrettyPrint++-- | Wrap width soft limitt+exWrapWidth ∷ Int+exWrapWidth = 80++class ExRender a where+    -- | Renders 'a' into a 'Doc' representing some part of exheres+    exDisp ∷ a → Doc++class ExRenderQ a where+    -- | Renders 'a' into a 'Doc' to be placed within double-quotes inside of exheres+    exDispQ ∷ a → Doc++-- | Double-quoted string for bash+dquoted ∷ String → String+dquoted [] = []+dquoted ('\\':xs) = "\\\\" ++ dquoted xs+dquoted ('"':xs) = "\\\"" ++ dquoted xs+dquoted ('`':xs) = "\\`" ++ dquoted xs+dquoted ('$':xs) = "\\$" ++ dquoted xs+dquoted (x:xs) = x : dquoted xs++-- | Split String in lines according to soft limit+softWidth ∷ Int → [String] → [[String]]+softWidth width = build 0 [] where+    build _ ys [] = [reverse ys]+    build 0 [] (w:ws) = build (length w) [w] ws+    build n ys (w:ws) | n' > width = reverse ys : build 0 [] (w:ws)+                      | otherwise = build n' (w : ys) ws+        where+            n' = length w + n++-- | Build a Doc out of String respecting soft width limit+reflow ∷ Int → String → Doc+reflow width = vcat . map (text . unwords) . softWidth width . words++-- | Wrap doc with spaces around if non-empty+spaces ∷ Doc → Doc+spaces doc | isEmpty doc = empty+           | otherwise = space <> doc <> space++-- | Wrap with brackets non-empty doc+nbrackets ∷ Doc → Doc+nbrackets doc | isEmpty doc = empty+              | otherwise = brackets doc++-- | Render a multi-line meta-field of Exheres if non-empty value+exFieldDoc ∷ String → Doc → Doc+exFieldDoc name value | isEmpty value = empty+                      | otherwise = vcat [text name <> text "=\"", value, char '"']++-- |Render a single-line with potential wrap meta-field of Exheres if non-empty value+exField ∷ String → String → Doc+exField _ "" = empty+exField name x | length singleLine < exWrapWidth = text singleLine+               | otherwise = exFieldDoc name (reflow exWrapWidth (dquoted x))+    where+        singleLine = name ++ "=\"" ++ dquoted x ++ "\""++instance ExRenderQ String where+    -- TODO: exDispQ = sep . map text . words . dquoted+    exDispQ = text . dquoted
+ src/ExRender/Dependency.hs view
@@ -0,0 +1,115 @@+-- Copyright © 2015 Mykola Orliuk <virkony@gmail.com>+-- Distributed under the terms of the GNU General Public License v2++{-# OPTIONS_GHC -fno-warn-orphans #-}++{-# LANGUAGE UnicodeSyntax, ViewPatterns, LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE FlexibleInstances #-}++module ExRender.Dependency () where++import Distribution.Text+import Distribution.Package+import Distribution.Version++import ExRender.Base++instance ExRender LowerBound where+    exDisp (LowerBound v InclusiveBound) = ">=" <> disp v+    exDisp (LowerBound v ExclusiveBound) = ">" <> disp v++instance ExRender UpperBound where+    exDisp (UpperBound v InclusiveBound) = "<=" <> disp v+    exDisp (UpperBound v ExclusiveBound) = "<" <> disp v+    exDisp x = error $ "Unsupported UpperBound: " ++ show x++-- | Render some of VersionInterval's that can be represented with a single condition and thus suitable for using in disjunction list.+--+-- >>> map maybeExVersion $ asVersionIntervals (fromJust $ simpleParse ">=1.0 || ==0.1.*" :: VersionRange)+-- [Just =0.1*,Just >=1.0]+maybeExVersion ∷ VersionInterval → Maybe Doc+maybeExVersion = \case+    -- >=x && <=x+    (LowerBound a InclusiveBound, UpperBound b InclusiveBound)+        | a == b → Just $ char '=' <> disp a++    -- <x, <=x+    (LowerBound (Version [0] []) InclusiveBound, ub) → Just $ exDisp ub++    -- >=x, >x+    (lb, NoUpperBound) → Just $ exDisp lb++    (LowerBound (Version [] _) _, _) → Nothing+    (_, UpperBound (Version [] _) _) → Nothing+    -- >=x.y && <x.y'+    (LowerBound v@(Version a []) InclusiveBound, UpperBound (Version b []) ExclusiveBound)+        | init a == init b && succ (last a) == last b →+            Just $ char '=' <> disp v <> char '*'++    (LowerBound (Version [_] _) _, _) → Nothing+    -- >=x.y.z && <x.y'+    (LowerBound v@(Version a []) InclusiveBound, UpperBound (Version b []) ExclusiveBound)+        | init a' == init b && succ (last a') == last b →+                Just $ text "~>" <> disp v+            where a' = init a++    _ → Nothing++-- | Transform VersionInterval in a sequence of disjunctions+--+-- >>> map exVersions $ asVersionIntervals (fromJust $ simpleParse ">=1.0" :: VersionRange)+-- [[>=1.0]]+-- >>> map exVersions $ asVersionIntervals (fromJust $ simpleParse ">=1.0 && <1.3" :: VersionRange)+-- [[=1.0*,=1.1*,=1.2*]]+-- >>> map exVersions $ asVersionIntervals (fromJust $ simpleParse ">=1.0 && <=1.3" :: VersionRange)+-- [[=1.0*,=1.1*,=1.2*,=1.3]]+-- >>> map exVersions $ asVersionIntervals (fromJust $ simpleParse ">=1 && <=1.3" :: VersionRange)+-- [[=1,=1.0*,=1.1*,=1.2*,=1.3]]+-- >>> map exVersions $ asVersionIntervals (fromJust $ simpleParse ">=1 && <=1.0.3" :: VersionRange)+-- [[=1,=1.0,=1.0.0*,=1.0.1*,=1.0.2*,=1.0.3]]+exVersions ∷ VersionInterval → [Doc]+exVersions = \case+    (maybeExVersion → Just x) → [x]++    -- ... && <=x.b+    (lb, UpperBound v InclusiveBound) →+        exVersions (lb, UpperBound v ExclusiveBound) ++ [char '=' <> disp v]++    -- >=x.a && <x.b+    (LowerBound va@(Version a _) InclusiveBound, ub@(UpperBound (Version b _) ExclusiveBound))+        | init a == init b → do+            c ← [init a ++ [i] | i ← [last a .. last b - 1]]+            return $ char '=' <> disp (Version c []) <> char '*'+        | length a < length b →+            char '=' <> disp va : exVersions (LowerBound (Version (a ++ [0]) []) InclusiveBound, ub)+    _ → []++instance ExRender VersionInterval where+    exDisp (LowerBound (Version [0] []) InclusiveBound, NoUpperBound) = empty+    exDisp (maybeExVersion → Just exVi) = exVi+    exDisp (lb, ub) = exDisp lb <> char '&' <> exDisp ub++instance ExRender VersionRange where+    exDisp vr = case asVersionIntervals vr of+        [vi] → nbrackets $ exDisp vi+        (concatMap exVersions → exVis) | not $ null exVis → nbrackets . hcat $ punctuate (char '|') exVis+        _ → error $ "Unsupported version range: " ++ display vr++instance ExRender Dependency where+    exDisp (Dependency n vr) = "dev-haskell/" <> disp n <> exDisp vr++-- $setup+--+-- >>> import Data.Maybe+--+-- doctest examples:+--+-- >>> exDisp (fromJust $ simpleParse ">=1.0 && <1.3" :: VersionRange)+-- [>=1.0&<1.3]+-- >>> exDisp (fromJust $ simpleParse ">=1.1 && <2" :: VersionRange)+-- [~>1.1]+-- >>> exDisp (fromJust $ simpleParse "==1.* || ==3.*" :: VersionRange)+-- [=1*|=3*]+-- >>> exDisp (fromJust $ simpleParse "==1.1.* || ==1.0.* || ==0.11.*" :: VersionRange)+-- [=0.11*|=1.0*|=1.1*]
+ src/ExRender/Haddock.hs view
@@ -0,0 +1,37 @@+-- Copyright © 2015 Mykola Orliuk <virkony@gmail.com>+-- Distributed under the terms of the GNU General Public License v2++{-# OPTIONS_GHC -fno-warn-orphans #-}++{-# LANGUAGE UnicodeSyntax #-}+{-# LANGUAGE FlexibleInstances #-}++module ExRender.Haddock () where++import ExRender.Base++import Documentation.Haddock.Parser+import Documentation.Haddock.Types++instance ExRender Identifier where exDisp (_, s, _) = text s+instance ExRenderQ Identifier where exDispQ = exDisp++instance ExRenderQ id => ExRenderQ (DocH mod id) where+    exDispQ x = case x of+        DocEmpty → empty+        DocAppend a b → exDispQ a <> exDispQ b+        DocString s → exDispQ s+        DocParagraph a → exDispQ a+        DocIdentifier s → exDispQ s+        DocModule s → exDispQ s+        DocWarning a → exDispQ a+        DocEmphasis a → exDispQ a+        DocMonospaced a → exDispQ a+        DocBold a → exDispQ a+        DocHyperlink (Hyperlink _ (Just s)) → exDispQ s+        DocHyperlink (Hyperlink s Nothing) → exDispQ s+        DocPic _ → empty -- XXX: no images in description+        DocAName s → exDispQ s+        DocProperty s → exDispQ s+        DocExamples _ → empty -- XXX: examples are filtered out+        _ → error $ "Unsupported haddock node"
+ src/ExRender/License.hs view
@@ -0,0 +1,37 @@+-- Copyright © 2015 Mykola Orliuk <virkony@gmail.com>+-- Distributed under the terms of the GNU General Public License v2++{-# OPTIONS_GHC -fno-warn-orphans #-}++{-# LANGUAGE UnicodeSyntax #-}+{-# LANGUAGE OverloadedStrings #-}++module ExRender.License () where++import Distribution.Text+import Distribution.License++import ExRender.Base++exKnownLicenses ∷ [String]+exKnownLicenses = ["CC0", "AGPL-3"]++instance ExRender License where+    exDisp (GPL Nothing) = "Unspecified-GPL"+    exDisp (GPL (Just v)) = "GPL-" <> disp v+    exDisp (AGPL (Just v)) = "AGPL-" <> disp v+    exDisp (LGPL Nothing) = "Unspecified-LGPL"+    exDisp (LGPL (Just v)) = "LGPL-" <> disp v+    exDisp (Apache Nothing) = "Unspecified-Apache"+    exDisp (Apache (Just v)) = "Apache-" <> disp v+    exDisp (MPL v) = "MPL-" <> disp v+    exDisp BSD2 = "BSD-2"+    exDisp BSD3 = "BSD-3"+    exDisp BSD4 = "BSD-4"+    exDisp ISC = "ISC"+    exDisp MIT = "MIT"+    exDisp PublicDomain = "public-domain"+    exDisp (UnknownLicense "BSD2") = "BSD-2"+    exDisp (UnknownLicense "MPL-2") = "MPL-2.0"+    exDisp (UnknownLicense x) | x `elem` exKnownLicenses = text x+    exDisp x = error $ "Unsupported license: " ++ display x
src/Main.hs view
@@ -1,7 +1,9 @@ -- Copyright © 2015 Mykola Orliuk <virkony@gmail.com> -- Distributed under the terms of the GNU General Public License v2 -{-# LANGUAGE ViewPatterns, UnicodeSyntax #-}+{-# LANGUAGE ViewPatterns, LambdaCase, UnicodeSyntax #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE OverloadedStrings #-}  module Main where @@ -9,9 +11,11 @@ import Control.Concurrent (threadDelay) import Control.Exception import Data.Maybe+import Data.Default import Data.ByteString.Lazy.Char8 (unpack) -import System.Environment+import Options.Applicative+ import System.IO  import Distribution.Text@@ -24,10 +28,19 @@ import Network.HTTP.Client import Network.HTTP.Types +import Text.PrettyPrint.ANSI.Leijen hiding ((<>), (<$>), empty, text) import qualified Text.Regex.PCRE.Light.Char8 as R  import ExRender +maybeReader ∷ (String → Maybe a) → ReadM a+maybeReader f = eitherReader $ \case+    (f → Just x) → return x+    arg → Left $ "cannot parse value `" ++ arg ++ "'"++textAuto ∷ Text a ⇒ ReadM a+textAuto = maybeReader simpleParse+ -- |Fetch content by provided URI simpleFetch ∷ String → IO String simpleFetch url = do@@ -98,46 +111,86 @@     ParseOk _ descr ← liftM parsePackageDescription $ simpleFetch url     fixLicense descr +data TargetCabal = TargetCabalFile FilePath+                 | TargetPackage PackageIdentifier+                 | TargetInvalid String+    deriving (Show)++targetParse ∷ String → Either String TargetCabal+targetParse arg = case arg of+    ('.':_) → return $ TargetCabalFile arg+    ('/':_) → return $ TargetCabalFile arg+    (simpleParse → Just pkgId) → return $ TargetPackage pkgId+    _ → Left $ "Specified target " ++ show arg+        ++ " neither starts with '.' or '/' (local cabal file)"+        ++ " nor a valid packageIdentifier (to fetch from hackage)"++targetArguments ∷ Parser [TargetCabal]+targetArguments = some (argument targetReader (metavar "TARGETS..."))+    where targetReader = eitherReader targetParse++data ExCabal =+    ExCabal+        { ghcVersion ∷ Version+        , targets ∷ Maybe [TargetCabal]+        }+    deriving (Show)++exCabalParser ∷ Parser ExCabal+exCabalParser = ExCabal+    <$> option textAuto+        ( long "ghc" <> short 'V'+        <> metavar "VERSION"+        <> help "Target VERSION of GHC"+        <> showDefaultWith display+        <> value (exGHCVersion def)+        )+    <*> optional targetArguments+ main ∷ IO () main = do-    args ← getArgs-    case args of-        "-h":_ → putStr helpString-        "--help":_ → putStr helpString-        _ → do-          sources ← case args of-              [] → liftM lines getContents-              _ → return args-          forM_ sources $ \source → do-              hPutStrLn stderr $ "# Processing " ++ show source-              descr ← case source of-                  ('.':_) → readPackageDescription verbose source >>= fixLicense-                  ('/':_) → readPackageDescription verbose source >>= fixLicense-                  (simpleParse → Just pkgId) -> fetchPackageDescription pkgId-                  _ -> error $ "Specified source " ++ show source-                      ++ " neither starts with '.' or '/' (local file)"-                      ++ " nor a valid packageIdentifier (to fetch from hackage)"-              let handler ∷ SomeException → IO ()-                  handler e = hPutStrLn stderr $ "# Failed fetch/generate for " ++ show source ++ ": " ++ show e-              catch (evaluate (exRender descr) >>= putStrLn) handler+    let opts = info (helper <*> exCabalParser)+            ( fullDesc+            <> headerDoc (Just helpHeader)+            <> progDescDoc (Just helpDesc)+            <> footerDoc (Just helpFooter)+            )+    params ← execParser opts+    let env = def { exGHCVersion = ghcVersion params }+    targets' ← case targets params of+        Just xs → return xs+        Nothing →+            liftM (map (either TargetInvalid id . targetParse) . lines) getContents +    let generate source getDescr = do+            let handler ∷ SomeException → IO ()+                handler e = hPutStrLn stderr $ "# Failed fetch/generate for " ++ show source ++ ": " ++ show e+            hPutStrLn stderr $ "# Processing " ++ show source+            catch (liftM (exRenderPkg env) getDescr >>= putStrLn) handler -helpString :: String-helpString =-  "Generate package description from .cabal files in format of exheres-0 for\n" ++-  "Exherbo Linux.\n" ++-  "\n" ++-  "See https://github.com/ony/exherbo-cabal\n" ++-  "\n" ++-  "Usage: exherbo-cabal [ -h | --help | <ref-to-package> ... ]\n"++-  "  -h | --help        Print this help and exit\n" ++-  "  <ref-to-package> either a package name (mtl) at Hackage with optional\n" ++-  "  version (mtl-2.2.1) or path to local cabal file (./exherbo-cabal.cabal)\n" ++-  "  If no <ref-to-package> provided in args read them from standart input\n" ++-  "\n" ++-  "Examples:\n" ++-  "  > exherbo-cabal mtl-2.2.1\n" ++-  "  > exherbo-cabal mtl transformers\n" ++-  "  > echo yesod-core | exherbo-cabal\n" ++-  "  > exherbo-cabal ./exherbo-cabal.cabal\n" ++-  "  > find /tmp/index -name \\*.cabal | exherbo-cabal\n"+    forM_ targets' $ \case+        TargetInvalid err → hPutStrLn stderr $ "# Invalid target: " ++ err+        TargetCabalFile filepath → generate filepath (readPackageDescription verbose filepath >>= fixLicense)+        TargetPackage pkgId → generate (display pkgId) (fetchPackageDescription pkgId)+++helpHeader ∷ Doc+helpHeader = fillSep [+  "Generate package description", "from .cabal files",+  "in format of exheres-0", "for Exherbo Linux"]++helpDesc ∷ Doc+helpDesc = fillSep [+  "Each of TARGETS specify either", "a package name (mtl) at Hackage",+  "with optional", "version (mtl-2.2.1)",+  "or path to local cabal file (./exherbo-cabal.cabal).",+  "If no TARGETS provided in args read them from standart input."]++helpFooter ∷ Doc+helpFooter = vcat ["Examples:", indent 2 $ vcat [+  "> exherbo-cabal mtl-2.2.1",+  "> exherbo-cabal mtl transformers",+  "> echo yesod-core | exherbo-cabal",+  "> exherbo-cabal ./exherbo-cabal.cabal",+  "> find /tmp/index -name \\*.cabal | exherbo-cabal"+  ], mempty, "See https://github.com/ony/exherbo-cabal"]
src/doctests.hs view
@@ -1,2 +1,16 @@+import Data.List import Test.DocTest-main = doctest ["-isrc", "src/ExRender.hs"]++sources =+    [ "src/ExRender.hs"+    , "src/ExRender/Base.hs"+    , "src/ExRender/Dependency.hs"+    , "src/ExRender/Haddock.hs"+    , "src/ExRender/License.hs"+    , "src/Main.hs"+    ]++optarg _ [] = []+optarg x ys = x : intersperse x ys++main = doctest $ optarg "-isrc" sources