packages feed

cartel (empty) → 0.2.0.0

raw patch · 12 files changed

+1526/−0 lines, 12 filesdep +basedep +directorydep +filepathsetup-changed

Dependencies added: base, directory, filepath, time

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2014, Omari Norman++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Omari Norman nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,17 @@+# Cartel - specify Cabal files in Haskell++Please see the documentation in the Cartel.hs module+in the lib directory for details.++## On Hackage++http://hackage.haskell.org/package/cartel++## On Github++http://www.github.com/massysett/cartel++## Testing++There are no tests for this library.  If you hit any bugs, please+report them in the Github issue tracker so I can fix them.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ cartel.cabal view
@@ -0,0 +1,55 @@+-- This Cabal file generated using the Cartel library.+-- Cartel is available at:+-- http://www.github.com/massysett/cartel+--+-- Script name used to generate: genCabal.hs+-- Generated on: 2014-05-27 21:31:49.996187 EDT+-- Cartel library version: 0.1.0.0+name: cartel+version: 0.2.0.0+cabal-version: >= 1.14+build-type: Simple+license: BSD3+license-file: LICENSE+copyright: 2014 Omari Norman+author: Omari Norman+maintainer: omari@smileystation.com+stability: Experimental+homepage: http://www.github.com/massysett/cartel+bug-reports: omari@smileystation.com+synopsis: Specify your Cabal files in Haskell+description:+  By specifying Cabal files in Haskell, you have the power+  of Haskell at your disposal to eliminate redundancies+  and to programatically populate various fields.+  .+  See the documentation in the "Cartel" module for details.+category: Distribution+tested-with: GHC == 7.4.1, GHC == 7.6.3, GHC == 7.8.2+extra-source-files:+    README.md+  , genCabal.hs+  , current-versions.txt+  , minimum-versions.txt++Library+  exposed-modules:+      Cartel+    , Cartel.Ast+    , Cartel.Defaults+    , Cartel.Render+    , Cartel.Tools+  exposed: True+  build-depends:+      base ((> 4.5.0.0 || == 4.5.0.0) && < 4.8)+    , directory ((> 1.1.0.2 || == 1.1.0.2) && < 1.3)+    , filepath ((> 1.3.0.0 || == 1.3.0.0) && < 1.4)+    , time ((> 1.4 || == 1.4) && < 1.5)+  other-modules:+      Paths_cartel+  buildable: True+  ghc-options:+      -Wall+  default-language: Haskell2010+  hs-source-dirs:+      lib
+ current-versions.txt view
@@ -0,0 +1,38 @@+This package was tested to work with these dependency+versions and compiler version.+These are the default versions fetched by cabal install.+Tested as of: 2014-05-28 01:31:54.008981 UTC+Path to compiler: ghc-7.8.2+Compiler description: 7.8.2++/opt/ghc/7.8.2/lib/ghc-7.8.2/package.conf.d:+    Cabal-1.18.1.3+    array-0.5.0.0+    base-4.7.0.0+    bin-package-db-0.0.0.0+    binary-0.7.1.0+    rts-1.0+    bytestring-0.10.4.0+    containers-0.5.5.1+    deepseq-1.3.0.2+    directory-1.2.1.0+    filepath-1.3.0.2+    (ghc-7.8.2)+    ghc-prim-0.3.1.0+    (haskell2010-1.1.2.0)+    (haskell98-2.0.0.3)+    hoopl-3.10.0.1+    hpc-0.6.0.1+    integer-gmp-0.5.1.0+    old-locale-1.0.0.6+    old-time-1.1.0.2+    pretty-1.1.1.1+    process-1.2.0.0+    template-haskell-2.9.0.0+    time-1.4.2+    transformers-0.3.0.0+    unix-2.7.0.1++/home/massysett/cartel/sunlight-28486/db:+    cartel-0.2.0.0+
+ genCabal.hs view
@@ -0,0 +1,123 @@+-- | This is a Cartel program used to generate the cabal file for+-- Cartel itself.+--+-- You should note in your comments which version of Cartel was used+-- when writing the Cartel program; in this case, I used version+-- 0.1.0.0.+--+-- There's no huge benefit to using Cartel for small programs or+-- libraries; indeed, Cartel itself is small so there's not much+-- benefit to using Cartel here other than for pedagogical and+-- testing purposes.  Cartel is most useful for big Cabal files with+-- lots of redundancy and dozens of modules.+module Main (main) where++-- Cartel exports a lot of names; by importing them qualified you+-- don't have to worry about clashing with your own names++import qualified Cartel as A++-- Package Dependencies+--+-- It can be useful to pull these out and define them in+-- top-level values, especially if your Cabal file has many+-- redundancies.  That way you can update version numbers in just+-- one place rather than throughout your Cabal file (forgetting to+-- update all these numbers is a major reason I wrote Cartel.)++base :: A.Package+base = A.closedOpen "base" [4,5,0,0] [4,8]++directory :: A.Package+directory = A.closedOpen "directory" [1,1,0,2] [1,3]++filepath :: A.Package+filepath = A.closedOpen "filepath" [1,3,0,0] [1,4]++time :: A.Package+time = A.closedOpen "time" [1,4] [1,5]++testedWith :: [(A.Compiler, A.ConstrTree)]+testedWith =+  let ghc v = (A.GHC, A.Leaf (A.Constraint EQ (A.Version v)))+  in map ghc [[7,4,1], [7,6,3], [7,8,2]]++-- The global package properties.  Build your properties from the+-- default 'properties' record rather than the 'Properties'+-- constructor to help future-proof your code.++properties :: A.Properties+properties = A.properties+  { A.prName = "cartel"+  , A.prVersion = A.Version [0,2,0,0]+  , A.prCabalVersion = (1, 14)+  , A.prBuildType = A.Simple+  , A.prLicense = A.BSD3+  , A.prLicenseFile = "LICENSE"+  , A.prLicenseFiles = []+  , A.prExtraSourceFiles =+    [ "README.md"+    , "genCabal.hs"+    , "current-versions.txt"+    , "minimum-versions.txt" ]+  , A.prTestedWith = testedWith+  , A.prCopyright = "2014 Omari Norman"+  , A.prAuthor = "Omari Norman"+  , A.prMaintainer = "omari@smileystation.com"+  , A.prStability = "Experimental"+  , A.prHomepage = "http://www.github.com/massysett/cartel"+  , A.prBugReports = "omari@smileystation.com"+  , A.prSynopsis = "Specify your Cabal files in Haskell"+  , A.prDescription =+      [ "By specifying Cabal files in Haskell, you have the power"+      , "of Haskell at your disposal to eliminate redundancies"+      , "and to programatically populate various fields."+      , ""+      , "See the documentation in the \"Cartel\" module for details."+      ]+  , A.prCategory = "Distribution"+  }++-- Definition for the library.  It takes a list of modules to export+-- as a parameter; this allows the Main function to use a function+-- to generate the list of modules rather than you having to specify+-- them in a duplicative, error-prone, and boring process.++library+  :: [String]+  -- ^ List of library modules to export+  -> A.Library+library ms = A.Library $+  [ A.LibExposedModules ms+  , A.LibExposed True+  ] +  +  ++ map A.LibInfo++  [ A.BuildDepends [base, directory, filepath, time]+  , A.OtherModules ["Paths_cartel"]+  , A.Buildable True+  , A.GHCOptions ["-Wall"]+  , A.DefaultLanguage A.Haskell2010+  , A.HsSourceDirs ["lib"]+  ]+++-- Generate the complete Cabal record++cabal+  :: [String]+  -- ^ List of library modules+  -> A.Cabal+cabal mods = A.cabal+  { A.cProperties = properties+  , A.cLibrary = Just $ library mods+  }++-- To see the result, just run "runhaskell genCabal.hs".  The result+-- is printed to standard output.++main :: IO ()+main = do+  modulesToExport <- A.modules "lib"+  A.render "genCabal.hs" $ cabal modulesToExport
+ lib/Cartel.hs view
@@ -0,0 +1,57 @@+-- | Cartel - a library to specify Cabal files in Haskell+--+-- The Cabal file format works very well for small projects.+-- However, in big projects with a library, many executables, and+-- test suites, some irritations emerge.  You need to specify+-- dependencies in multiple places, leading to redundancy.  You also+-- have to manually add in new modules, make sure you list all+-- modules (a real pain with executables, as problems may arise only+-- after you build a distribution tarball), and update your module+-- lists when you refactor.+--+-- Specifying your Cabal files in Haskell rather than in a+-- plain-text file format helps deal with a lot of these problems.+-- You have the full power of Haskell to make definitions in one+-- place and then reuse them.  You can also dynamically read a tree+-- of modules and use the result, thus avoiding the need to manually+-- update your module lists.+--+-- A disadvantage to Cartel is that it can be a bit more verbose+-- than a vanilla Cabal file.  In addition, you also have to+-- remember to generate the new Cabal file whenever you change the+-- script that generates your Cabal file.+--+-- Cabal already has an AST that it uses.  Cartel could, perhaps,+-- have re-used these structures.  Cartel does not do this for three+-- reasons.  First, the Cabal API is a bit untidy, partially because+-- it has to do things that Cartel doesn't have to worry about, but+-- also because the comments in the Cabal modules themselves+-- indicate that the whole thing could use a refactoring.  Second,+-- the Cabal developers make no commitment to keep that API stable.+-- Third, the Cartel API tries only to replicate format of the+-- plain-text Cabal file, which will be much more stable than the+-- Cabal API.+--+-- To get started, first study the "Cartel.Ast" module.  It contains+-- the data types that mirror the structure of a Cabal file.  You+-- will find that every Cabal feature, including test suites,+-- benchmarks, and conditionals, is represented.  Then look at+-- "Cartel.Tools", which has short functions to make Cartel easier+-- to use.  "Cartel.Render" has functions to transform "Cartel.Ast"+-- types to plain text; the necessary bits from here are exported+-- through "Cartel.Tools" so you shouldn't need to use this module+-- much if at all.+--+-- Also, examine the genCabal.hs file, which generates Cartel's own+-- Cabal file using Cartel.  To test it out, after installing Cartel+-- just run "runhaskell genCabal.hs".++module Cartel+  ( module Cartel.Ast+  , module Cartel.Defaults+  , module Cartel.Tools+  ) where++import Cartel.Ast+import Cartel.Defaults+import Cartel.Tools
+ lib/Cartel/Ast.hs view
@@ -0,0 +1,461 @@+-- | An abstract syntax tree for Cabal files.  There are thoughts in+-- the "Cartel" module documentation on why this wheel is reinvented+-- rather than reusing the types already available in the Cabal+-- library.+--+-- The 'Cabal' data type represents the root of the Cabal file; all+-- other types in this file are ultimately components of the 'Cabal'+-- type.+--+-- Much documentation in this module is copied from the+-- /Developing Cabal Packages/ guide at+--+-- <http://www.haskell.org/cabal/users-guide/developing-packages.html>++module Cartel.Ast where++-- | A version number.  The Cabal documentation says this+-- \"usually\" consists of a sequence of natural numbers separated+-- by dots.  Though this suggests that a version number could+-- contain something other than natural numbers, in fact the types+-- in the Cabal library do not allow anything other than numbers and+-- you will get a parse error if you try to use anything else.+--+-- Therefore Cartel's 'Version' type only allows a list of 'Int' in+-- a version number.+newtype Version = Version { unVersion :: [Int] }+  deriving (Eq, Ord, Show)++data BuildType+  = Simple+  | Configure+  | Make+  | Custom+  deriving (Eq, Ord, Show)++data License+  = GPL+  | AGPL+  | LGPL+  | BSD2+  | BSD3+  | BSD4+  | MIT+  | MPL+  | Apache+  | PublicDomain+  | AllRightsReserved+  | OtherLicense+  deriving (Eq, Ord, Show)++data Compiler+  = GHC+  | NHC+  | YHC+  | Hugs+  | Helium+  | JHC+  | LHC+  deriving (Eq, Ord, Show)++-- | A version constraint; used primarily when specifying the build+-- dependencies of a package.  For example, to specify+-- @less than version 1.0@, use @Constraint LT (Version [1,0])@.+--+-- There is no way to express @less than or equal to@ or @greater+-- than or equal to@; for that, use 'ConstrTree'.+data Constraint = Constraint+  { csComparison :: Ordering+  , csVersion :: Version+  } deriving (Eq, Ord, Show)++data Logical+  = Or+  | And+  deriving (Eq, Ord, Show)++-- | Expresses a tree of constraints.  This is how you represent+-- more complex dependency relationships.  For example, to represent+-- @less than or equal to version 1.0@, use+--+-- >> let one = Version [1,0]+-- >> in Branch Or (Constraint LT one) (Constraint EQ one)+--+-- Combinators in "Cartel.Tools" help you build the most common+-- cases.+data ConstrTree+  = Leaf Constraint+  | Branch Logical ConstrTree ConstrTree+  deriving (Eq, Ord, Show)++-- | Global package properties.  Most of the 'String' fields are+-- optional; to leave them blank, use the empty string.  To future+-- proof your code, use 'Cartel.Defaults.properties', which provides+-- this record already filled in with default values (which are+-- typically blank.)  Then you only alter the fields you wish to+-- fill in.++data Properties = Properties+  { prName :: String+  -- ^ Unique name of package, without the version number++  , prVersion :: Version+  -- ^ Package version number++  , prCabalVersion :: (Int, Int)+  -- ^ Version of Cabal that the package uses; for instance, for+  -- 1.20, use @(1, 20)++  , prBuildType :: BuildType++  , prLicense :: License++  , prLicenseFile :: String+  -- ^ The file containing the precise copyright license++  , prLicenseFiles :: [String]+  -- ^ Multiple license files; use in addition to, or instead of,+  -- 'prLicenseFile'++  , prCopyright :: String+  -- ^ Copyright notice; typically the name of the holder of the+  -- copyright and the year(s) for which copyright is claimed++  , prAuthor :: String+  -- ^ Original package author++  , prMaintainer :: String+  -- ^ Current package maintainer; this is an email address to which+  -- users should send bug reports, feature requests, and patches.++  , prStability :: String+  -- ^ Package stability level, e.g. @alpha@, @stable@, etc.++  , prHomepage :: String+  -- ^ URL of package homepage++  , prBugReports :: String+  -- ^ URL where users should direct bug reports.  Should be either+  -- a @mailto:@ URL for a person or mailing list, or an @http:@ or+  -- @https:@ URL for an online bug tracking system.++  , prPackageUrl :: String+  -- ^ The location of a source bundle for the package.++  , prSynopsis :: String+  -- ^ Short, one-line synopsis of what the package does++  , prDescription :: [String]+  -- ^ Each line of the long description.  Do not include the+  -- newlines themselves.  If you wish to include a blank line,+  -- include a list item that is the empty string; upon rendering, a+  -- single period will be inserted, as Cabal requires when+  -- inputting blank lines.++  , prCategory :: String+  -- ^ A comma-separated list of categories to use on Hackage.++  , prTestedWith :: [(Compiler, ConstrTree)]+  -- ^ Compilers and versions against which the package has been+  -- tested.++  , prDataFiles :: [String]+  -- ^ List of files to be installed for run-time use by the+  -- package.++  , prDataDir :: String+  -- ^ The directory where Cabal looks for data files to install,+  -- relative to the source directory.  By default Cabal looks in+  -- the source directory itself.++  , prExtraSourceFiles :: [String]+  -- ^ List of additional files to be included in source+  -- distributions built with @setup sdist@.  This can use a limited+  -- form of @*@ wildcards in file names.++  , prExtraDocFiles :: [String]+  -- ^ List of additional files to be included in source+  -- distributions, and also copied to the html directory when+  -- Haddock documentation is generated. As with data-files it can+  -- use a limited form of @*@ wildcards in file names.++  , prExtraTmpFiles :: [String]+  -- ^ List of additional files or directories to be removed by+  -- @setup clean@.  These would typically be additional files+  -- created by additional hooks.++  } deriving (Eq, Ord, Show)++-- | A field in the @Library@ section of the Cabal file.  A+-- @Library@ section can have multiple fields.+data LibraryField+  = LibExposedModules [String]+  -- ^ Exposed modules.  'Cartel.Tools.modules' can help you+  -- generate this.++  | LibExposed Bool+  -- ^ Is the library exposed?  GHC can hide libraries.++  | LibConditional (CondBlock LibraryField)+  -- ^ The @Library@ section can contain conditional blocks.++  | LibInfo BuildInfoField+  -- ^ The @Library@ section can contain build information.+  deriving (Eq, Ord, Show)++-- | The entire @Library@ section.+newtype Library = Library+  { libFields :: [LibraryField]+  } deriving (Eq, Ord, Show)++-- | A single field in an @Executable@ section.  An @Executable@+-- section may have multiple fields.+data ExecutableField++  = ExeMainIs String+  -- ^ @main-is@.  This field is required.++  | ExeConditional (CondBlock ExecutableField)+  -- ^ An @Executable@ section can contain conditional blocks.++  | ExeInfo BuildInfoField+  -- ^ An @Executable@ section can contain one or more build+  -- information fields.++  deriving (Eq, Ord, Show)++-- | An entire @Executable@ section.+data Executable = Executable+  { exeName :: String+  -- ^ The name of the executable that Cabal will build.+  , exeFields :: [ExecutableField]+  -- ^ Zero or more fields associated with this executable.+  } deriving (Eq, Ord, Show)++-- | What kind of test suite is this?+data TestSuiteType+  = ExitcodeStdio String+  -- ^ An @exitcode-stdio-1.0@ test.  The @String@ is the name of+  -- the file containing the executable code.  This @String@ becomes+  -- the @main-is@ is the resulting Cabal file.++  | Detailed String+  -- ^ The @detailed-1.0@ test.  The @String@ is the module+  -- exporting the @tests@ symbol.  The @String@ becomes the+  -- @test-module@ field in the resulting Cabal file.+  deriving (Eq, Ord, Show)++-- | A single field value in a test suite section.  A single test+-- suite section may contain mulitple fields.+data TestSuiteField+  = TestType TestSuiteType+  -- ^ What kind of test this is+  | TestConditional (CondBlock TestSuiteField)+  -- ^ A test may contain zero or more conditional blocks.+  | TestInfo BuildInfoField+  -- ^ A test suite can contain build information fields.+  deriving (Eq, Ord, Show)++-- | An entire test suite section.+data TestSuite = TestSuite+  { tsName :: String+  -- ^ The executable name of the resulting test suite+  , tsFields :: [TestSuiteField]+  -- ^ Zero or more fields configuring the test.+  } deriving (Eq, Ord, Show)++-- | A single field in a @Benchmark@ section.  Because Cabal+-- currently supports only one benchmark interface, which is+-- @exitcode-stdio-1.0@, all Cartel-generated files using benchmarks+-- will have benchmarks of this type.+data BenchmarkField+  = BenchmarkConditional (CondBlock BenchmarkField)+  -- ^ A benchmark can have conditional blocks.+  | BenchmarkInfo BuildInfoField+  -- ^ A benchmark can have build information fields.+  deriving (Eq, Ord, Show)++data Benchmark = Benchmark+  { bmName :: String+  -- ^ The name of the benchmark that will be built+  , bmMainIs :: String+  -- ^ The @.hs@ or @.lhs@ file containing the @Main@ module.+  , bmFields :: [BenchmarkField]+  -- ^ Zero or more benchmark fields.+  } deriving (Eq, Ord, Show)++-- | A single package, consisting of a package name and an optional+-- set of constraints.  Used when specifying 'BuildDepends',+-- 'BuildTools', and 'PkgConfigDepends'.+data Package = Package+  { packName :: String+  , packConstraints :: Maybe ConstrTree+  } deriving (Eq, Ord, Show)++-- | Default language.  Currently not documented in Cabal docs, see+--+-- <https://github.com/haskell/cabal/issues/1894>+data Language+  = Haskell98+  | Haskell2010+  deriving (Eq, Ord, Show)++-- | A single field of build information.  This can appear in a+-- Library, Executable, Test-Suite, or Benchmark.+data BuildInfoField+  = BuildDepends [Package]+  -- ^ A list of packages needed to build this component++  | OtherModules [String]+  -- ^ Modules used but not exposed.  For libraries, these are+  -- hidden modules; for executable, these are auxiliary modules to+  -- be linked with the file in the @main-is@ field.+  --+  -- 'Cartel.Tools.modules' can help greatly with maintenance of+  -- this field.++  | HsSourceDirs [String]+  -- ^ Root directories for the module hierarchy++  | Extensions [String]+  -- ^ Haskell extensions used by every module.++  | BuildTools [Package]+  -- ^ Programs needed to build this package, such as c2hs.++  | Buildable Bool+  -- ^ Is this component buildable?++  | GHCOptions [String]+  | GHCProfOptions [String]+  | GHCSharedOptions [String]+  | HugsOptions [String]+  | Nhc98Options [String]+  | Includes [String]+  -- ^ Header files to be included in any compilations via C.+  -- Applies to both header files that are already installed on the+  -- system and to those coming with the package to be installed.++  | InstallIncludes [String]+  -- ^ Header files to be installed into @$libdir/includes@ when the+  -- package is installed.  These files should be found in relative+  -- to the top of the source tree or relative to one of the+  -- directories listed in @include-dirs@.++  | IncludeDirs [String]+  -- ^ List of diretories to search for header files when dealing+  -- with C compilations.++  | CSources [String]+  -- ^ C sources to be compiled and lined with the Haskell files.++  | ExtraLibraries [String]+  -- ^ Extra libraries to link with.++  | ExtraLibDirs [String]+  -- ^ Directories to search for libraries.++  | CCOptions [String]+  -- ^ C Compiler options.++  | LDOptions [String]+  -- ^ Linker options.++  | PkgConfigDepends [Package]+  -- ^ List of pkg-config packages needed to build this component.++  | Frameworks [String]+  -- ^ OS X frameworks.++  | DefaultLanguage Language+  deriving (Eq, Ord, Show)++-- | Conditions to use in the @if@ statement when defining+-- conitional blocks.+data Condition+  = OS String+  -- ^ Operating system; tested against @System.Info.os@ on the+  -- target system.++  | Arch String+  -- ^ Argument is matched against @System.Info.arch@ on the target+  -- system.++  | Impl (Compiler, Maybe ConstrTree)+  -- ^ Tests for the configured Haskell implementation.++  | CFlag String+  -- ^ Evaluates to the current assignment of the flag of the given+  -- name.  Flag names are case insensitive.  Testing for flags that+  -- have not been introduced with a flag section is an error.++  | CTrue+  -- ^ Always true.++  | CFalse+  -- ^ Always false.++  deriving (Eq, Ord, Show)++-- | Tree of conditions to use in a 'CondBlock'.+data CondTree+  = CLeaf Condition+  | CBranch Logical CondTree CondTree+  | CNegate CondTree+  deriving (Eq, Ord, Show)++-- | An @if-then-else@ block.+data CondBlock a = CondBlock+  { condIf :: CondTree+  , ifTrue :: [a]+  , ifElse :: [a]+  } deriving (Eq, Ord, Show)++-- | What kind of VCS repository is this?+data RepoKind+  = Head (Maybe String)+  -- ^ The Maybe String is the repository tag.  It is optional; use+  -- Nothing if there is no tag.+  | This String+  -- ^ The String is the repository tag.  It is required for the+  -- @this@ repo kind.+  deriving (Eq, Ord, Show)++data Vcs+  = Darcs+  | Git+  | Svn+  | Cvs String+  -- ^ The argument 'String' is the named module+  | Mercurial+  | Bazaar+  | ArchVcs+  | Monotone+  deriving (Eq, Ord, Show)++data Repository = Repository+  { repoVcs :: Vcs+  , repoKind :: RepoKind+  , repoLocation :: String+  , repoBranch :: String+  , repoTag :: String+  , repoSubdir :: String+  } deriving (Eq, Ord, Show)++-- | A single flag.+data Flag = Flag+  { flName :: String+  , flDescription :: String+  , flDefault :: Bool+  , flManual :: Bool+  } deriving (Eq, Ord, Show)++data Cabal = Cabal+  { cProperties :: Properties+  , cRepositories :: [Repository]+  , cFlags :: [Flag]+  , cLibrary :: Maybe Library+  , cExecutables :: [Executable]+  , cTestSuites :: [TestSuite]+  , cBenchmarks :: [Benchmark]+  } deriving (Eq, Ord, Show)
+ lib/Cartel/Defaults.hs view
@@ -0,0 +1,47 @@+-- | Default values for the data types in "Cartel.Ast".  These+-- defaults are genrally filled in with blank values.  Use these+-- default values where you can; by doing so you help future-proof+-- your code.  Use the default as a starting point and fill in the+-- values that you need.+module Cartel.Defaults where++import qualified Cartel.Ast as A++properties :: A.Properties+properties = A.Properties+  { A.prName = ""+  , A.prVersion = A.Version []+  , A.prCabalVersion = (1, 20)+  , A.prBuildType = A.Simple+  , A.prLicense = A.BSD3+  , A.prLicenseFile = "LICENSE"+  , A.prLicenseFiles = []+  , A.prCopyright = ""+  , A.prAuthor = ""+  , A.prMaintainer = ""+  , A.prStability = ""+  , A.prHomepage = ""+  , A.prBugReports = ""+  , A.prPackageUrl = ""+  , A.prSynopsis = ""+  , A.prDescription = []+  , A.prCategory = ""+  , A.prTestedWith = []+  , A.prDataFiles = []+  , A.prDataDir = ""+  , A.prExtraSourceFiles = []+  , A.prExtraDocFiles = []+  , A.prExtraTmpFiles = []+  }++cabal :: A.Cabal+cabal = A.Cabal+  { A.cProperties = properties+  , A.cRepositories = []+  , A.cFlags = []+  , A.cLibrary = Nothing+  , A.cExecutables = []+  , A.cTestSuites = []+  , A.cBenchmarks = []+  }+
+ lib/Cartel/Render.hs view
@@ -0,0 +1,337 @@+-- | Functions to render the data types in "Cartel.Ast".  You+-- shouldn't need anything from this module; the main rendering+-- function is also exported from "Cartel".+module Cartel.Render where++import qualified Cartel.Ast as A+import Data.List (intersperse)++(<+>) :: String -> String -> String+l <+> r+  | null l = r+  | null r = l+  | otherwise = l ++ " " ++ r++vsep :: [String] -> String+vsep = foldr f ""+  where+    f s acc+      | null acc = s+      | null s = acc+      | otherwise = s ++ "\n" ++ acc++indentAmt :: Int+indentAmt = 2++indent :: Int -> String -> String+indent i s = replicate (i * indentAmt) ' ' ++ s ++ "\n"++indentList :: Int -> [String] -> String+indentList i ls = case ls of+  [] -> ""+  x:[] -> line1 x+  x:xs -> concat $ line1 x : map lineRest xs+  where+    line1 s = replicate (i * indentAmt + 2) ' ' ++ s ++ "\n"+    lineRest s = replicate (i * indentAmt) ' ' ++ ", " ++ s ++ "\n"++labeled+  :: Int+  -- ^ Indentation+  -> String+  -- ^ Label+  -> String+  -- ^ Text+  -> String+  -- ^ Result, with a newline.  Empty if the text to show is also+  -- empty.+labeled i l t+  | null t = ""+  | otherwise = indent i (l ++ ": " ++ t)++labeledList+  :: Int+  -- ^ Indentation+  -> String+  -- ^ Label+  -> [String]+  -- ^ List+  -> String+  -- ^ Result, with newlines.  Empty if the list is empty.+labeledList i l ts+  | null ts = ""+  | otherwise = indent i (l ++ ":") ++ indentList (i + 1) ts++version :: A.Version -> String+version = concat . intersperse "." . map show . A.unVersion++license :: A.License -> String+license = show++buildType :: A.BuildType -> String+buildType = show++compiler :: A.Compiler -> String+compiler = show++comparison :: Ordering -> String+comparison a = case a of+  LT -> "<"+  GT -> ">"+  EQ -> "=="++constraint :: A.Constraint -> String+constraint a = comparison (A.csComparison a) <+>+  version (A.csVersion a)++logical :: A.Logical -> String+logical a = case a of+  A.Or -> "||"+  A.And -> "&&"++constrTree :: A.ConstrTree -> String+constrTree a = case a of+  A.Leaf c -> constraint c+  A.Branch c l r -> "(" ++ constrTree l+    <+> logical c <+> constrTree r ++ ")"++cabalVersion :: (Int, Int) -> String+cabalVersion (a, b) = ">= " ++ show a ++ "." ++ show b++description :: [String] -> String+description ls+  | null ls = ""+  | otherwise = concatMap f ls+  where+    f s+      | null s = "  .\n"+      | otherwise = "  " ++ s ++ "\n"++testedWith :: [(A.Compiler, A.ConstrTree)] -> String+testedWith = concat . intersperse ", " . map f+  where+    f (cmp, cst) = compiler cmp <+> constrTree cst++properties :: A.Properties -> String+properties p+  = lbl "name" (A.prName p)+  ++ lbl "version" (version . A.prVersion $ p)+  ++ lbl "cabal-version" (cabalVersion . A.prCabalVersion $ p)+  ++ lbl "build-type" (buildType . A.prBuildType $ p)+  ++ lbl "license" (license . A.prLicense $ p)+  ++ lbl "license-file" (A.prLicenseFile p)+  ++ lst "license-files" (A.prLicenseFiles p)+  ++ lbl "copyright" (A.prCopyright p)+  ++ lbl "author" (A.prAuthor p)+  ++ lbl "maintainer" (A.prMaintainer p)+  ++ lbl "stability" (A.prStability p)+  ++ lbl "homepage" (A.prHomepage p)+  ++ lbl "bug-reports" (A.prBugReports p)+  ++ lbl "package-url" (A.prPackageUrl p)+  ++ lbl "synopsis" (A.prSynopsis p)+  ++ let desc = description . A.prDescription $ p+         lbld | null desc = ""+              | otherwise = "description:\n" ++ desc+     in lbld+  ++ lbl "category" (A.prCategory p)+  ++ lbl "tested-with" (testedWith . A.prTestedWith $ p)+  ++ lst "data-files" (A.prDataFiles p)+  ++ lbl "data-dir" (A.prDataDir p)+  ++ lst "extra-source-files" (A.prExtraSourceFiles p)+  ++ lst "extra-doc-files" (A.prExtraDocFiles p)+  ++ lst "extra-tmp-files" (A.prExtraTmpFiles p)+  where+    lbl = labeled 0+    lst = labeledList 0++class Field a where+  field :: Int -> a -> String++instance Field A.LibraryField where+  field i f = case f of+    A.LibExposedModules xs -> labeledList i "exposed-modules" xs+    A.LibExposed b -> labeled i "exposed" (show b)+    A.LibConditional b -> condBlock i b+    A.LibInfo b -> field i b++library :: A.Library -> String+library (A.Library l) = "Library\n"+  ++ concatMap (field 1) l++instance Field A.ExecutableField where+  field i f = case f of+    A.ExeMainIs s -> labeled i "main-is" s+    A.ExeConditional b -> condBlock i b+    A.ExeInfo b -> field i b++executable :: A.Executable -> String+executable (A.Executable n fs) =+  "Executable " ++ n ++ "\n"+  ++ concatMap (field 1) fs++testSuiteType :: A.TestSuiteType -> String+testSuiteType t = case t of+  A.ExitcodeStdio _ -> "exitcode-stdio-1.0"+  A.Detailed _ -> "detailed-1.0"++instance Field A.TestSuiteField where+  field i t = case t of+    A.TestType a -> labeled i "type" (testSuiteType a)+    A.TestConditional b -> condBlock i b+    A.TestInfo b -> field i b++testSuite :: A.TestSuite -> String+testSuite (A.TestSuite n fs) =+  "Test-Suite " ++ n ++ "\n"+  ++ concatMap (field 1) fs++instance Field A.BenchmarkField where+  field i b = case b of+    A.BenchmarkConditional s -> condBlock i s+    A.BenchmarkInfo a -> field i a++benchmark :: A.Benchmark -> String+benchmark (A.Benchmark n mi fs) =+  "Benchmark " ++ n ++ "\n"+  ++ labeled 1 "main-is" mi+  ++ labeled 1 "interface" "exitcode-stdio-1.0"+  ++ concatMap (field 1) fs++condBlock :: Field a => Int -> A.CondBlock a -> String+condBlock i b =+  indent i ("if " ++ condTree (A.condIf b))+  ++ concatMap (field (i + 1)) (A.ifTrue b)+  ++ elses+  where+    elses | null (A.ifElse b) = ""+          | otherwise = indent i "else"+              ++ concatMap (field (i + 1)) (A.ifElse b)++package :: A.Package -> String+package p =+  A.packName p <+> maybe "" constrTree (A.packConstraints p)++language :: A.Language -> String+language = show++instance Field A.BuildInfoField where+  field i fld = case fld of+    A.BuildDepends ps ->+      labeledList i "build-depends" . map package $ ps+    A.OtherModules ls ->+      labeledList i "other-modules" ls+    A.HsSourceDirs ls ->+      labeledList i "hs-source-dirs" ls+    A.Extensions ls ->+      labeledList i "extensions" ls+    A.BuildTools ps ->+      labeledList i "build-tools" . map package $ ps+    A.Buildable b ->+      labeled i "buildable" . show $ b+    A.GHCOptions ps ->+      labeledList i "ghc-options" ps+    A.GHCProfOptions ps ->+      labeledList i "ghc-prof-options" ps+    A.GHCSharedOptions ps ->+      labeledList i "ghc-shared-options" ps+    A.HugsOptions ps ->+      labeledList i "hugs-options" ps+    A.Nhc98Options ps ->+      labeledList i "nhc98-options" ps+    A.Includes ps ->+      labeledList i "includes" ps+    A.InstallIncludes ps ->+      labeledList i "install-includes" ps+    A.IncludeDirs ps ->+      labeledList i "include-dirs" ps+    A.CSources ps ->+      labeledList i "c-sources" ps+    A.ExtraLibraries ps ->+      labeledList i "extra-libraries" ps+    A.ExtraLibDirs ps ->+      labeledList i "extra-lib-dirs" ps+    A.CCOptions ps ->+      labeledList i "cc-options" ps+    A.LDOptions ps ->+      labeledList i "ld-options" ps+    A.PkgConfigDepends ps ->+      labeledList i "pkgconfig-depends" . map package $ ps+    A.Frameworks ps ->+      labeledList i "frameworks" ps+    A.DefaultLanguage l+      -> labeled i "default-language" . language $ l++condition :: A.Condition -> String+condition a = case a of+  A.OS s -> "os(" ++ s ++ ")"+  A.Arch s -> "arch(" ++ s ++ ")"+  A.Impl (cmp, mayC) -> "impl(" ++ s ++ ")"+    where+      s = compiler cmp <+> cond+      cond = maybe "" constrTree mayC+  A.CFlag f -> "flag(" ++ f ++ ")"+  A.CTrue -> "true"+  A.CFalse -> "false"++condTree :: A.CondTree -> String+condTree a = case a of+  A.CLeaf c -> condition c+  A.CBranch b l r -> "(" ++ condTree l <+> logical b <+> condTree r+    ++ ")"+  A.CNegate t -> "!(" ++ condTree t ++ ")"++flag :: A.Flag -> String+flag (A.Flag nm desc dflt man) = concat (line1:lineRest)+  where+    line1 = "Flag " ++ nm ++ "\n"+    lineRest = map (indent 1)+      [ "Description: " ++ desc+      , "Default: " ++ show dflt+      , "Manual: " ++ show man+      ]++vcs :: A.Vcs -> String+vcs a = case a of+  A.Darcs -> "darcs"+  A.Git -> "git"+  A.Svn -> "svn"+  A.Cvs _ -> "cvs"+  A.Mercurial -> "mercurial"+  A.Bazaar -> "bazaar"+  A.ArchVcs -> "arch"+  A.Monotone -> "monotone"++repoKind :: A.RepoKind -> String+repoKind a = case a of+  A.Head _ -> "head"+  A.This _ -> "this"++repository :: A.Repository -> String+repository r =+  "source-repository " ++ repoKind (A.repoKind r) ++ "\n"+  ++ lbl "type" (vcs . A.repoVcs $ r)+  ++ lbl "location" (A.repoLocation r)+  ++ lbl "module" mdle+  ++ lbl "branch" (A.repoBranch r)+  ++ lbl "tag" tag+  ++ lbl "subdir" (A.repoSubdir r)+  where+    mdle = case A.repoVcs r of+      A.Cvs s -> s+      _ -> ""+    tag = case A.repoKind r of+      A.Head m -> maybe "" id m+      A.This s -> s+    lbl = labeled 1++cabal :: A.Cabal -> String+cabal d = vsep+  [ properties . A.cProperties $ d+  , vsep . map repository . A.cRepositories $ d+  , vsep . map flag . A.cFlags $ d+  , maybe "" library . A.cLibrary $ d+  , vsep . map executable . A.cExecutables $ d+  , vsep . map testSuite . A.cTestSuites $ d+  , vsep . map benchmark . A.cBenchmarks $ d+  ]+
+ lib/Cartel/Tools.hs view
@@ -0,0 +1,321 @@+module Cartel.Tools+  ( -- * Building constraints++    -- | Helpers to build many common version constraints.++    -- ** Building constraint trees+    (&&&)+  , (|||)+  , lt+  , gt+  , eq+  , ltEq+  , gtEq++  -- ** Building package specifications+  , closedOpen+  , apiVersion+  , nextBreaking+  , nextMajor+  , exactly++  -- * Rendering a cabal file+  , render+  , renderString++  -- * Getting a list of all modules in a directory tree+  , modules+  , fileExtensions+  , modulesWithExtensions+  ) where++import qualified Cartel.Ast as A+import qualified Cartel.Render as R+import qualified System.Directory as D+import qualified System.FilePath as P+import System.FilePath ((</>))+import qualified Data.Char as C+import Data.List (intersperse, sortBy)+import Data.Monoid+import qualified Data.Version as V+import Data.Time+import qualified Paths_cartel+import qualified System.IO as IO++(&&&) :: A.ConstrTree -> A.ConstrTree -> A.ConstrTree+l &&& r = A.Branch A.And l r++infixr 3 &&&++(|||) :: A.ConstrTree -> A.ConstrTree -> A.ConstrTree+l ||| r = A.Branch A.Or l r++infixr 2 |||++lt :: [Int] -> A.ConstrTree+lt = A.Leaf . A.Constraint LT . A.Version++gt :: [Int] -> A.ConstrTree+gt = A.Leaf . A.Constraint GT . A.Version++eq :: [Int] -> A.ConstrTree+eq = A.Leaf . A.Constraint EQ . A.Version++ltEq :: [Int] -> A.ConstrTree+ltEq v = lt v ||| eq v++gtEq :: [Int] -> A.ConstrTree+gtEq v = gt v ||| eq v++-- | Creates a package interval that is closed on the left, open on+-- the right.  Useful for the common case under the PVP to specify+-- that you depend on a version that is at least a particular+-- version, but less than another version.+--+-- > closedOpen "bytestring" [0,17] [0,19] ==> bytestring >= 0.17 && < 0.19++closedOpen+  :: String+  -- ^ Package name+  -> [Int]+  -- ^ Version number for lower bound+  -> [Int]+  -- ^ Version number for upper bound+  -> A.Package+  -- ^ Resulting contraints+closedOpen n l u = A.Package n $ Just (gtEq l &&& lt u)++-- | Specifies a particular API version.  Useful to lock your+-- package dependencies down to a particular API version.+--+-- > apiVersion "base" [1] ==> base >= 1 && < 2+-- > apiVersion "base" [1,2] ==> base >= 1.2 && < 1.3+-- > apiVersion "base" [1,2,3] ==> base >= 1.2.3 && < 1.2.4++apiVersion :: String -> [Int] -> A.Package+apiVersion n v = closedOpen n v u+  where+    u = case v of+      [] -> error "apiVersion: requires a non-empty list argument"+      _ -> init v ++ [succ (last v)]++-- | Depends on the version given, up to the next breaking API+-- change.+--+-- > nextBreaking "base" [4] ==> base >= 4 && < 4.1+-- > nextBreaking "base" [4,1] ==> base >= 4.1 && < 4.2+-- > nextBreaking "base" [4,7,0,0] ==> base >= 4.7.0.0 && < 4.8++nextBreaking+  :: String+  -> [Int]+  -> A.Package+nextBreaking n v = closedOpen n v u+  where+    u = case v of+      [] -> error "nextBreaking: requires a non-empty list argument"+      x:[] -> [x, 1]+      x:y:_ -> x : succ y : []++-- | Depends on the version given, up to the next time the first+-- digit increments.  Useful for @base@.+--+-- > nextBreaking "base" [4] ==> base >= 4 && < 5++nextMajor+  :: String+  -> [Int]+  -> A.Package+nextMajor n v = closedOpen n v u+  where+    u = case v of+      [] -> error "nextMajor: requires a non-empty list argument"+      x:_ -> succ x : []++-- | Depends on exactly this version only.+--+-- > exactly "base" [4,5,0,0] ==> base ==4.5.0.0++exactly :: String -> [Int] -> A.Package+exactly n v = A.Package n (Just $ eq v)++-- | Common extensions of Haskell files and files that are+-- preprocessed into Haskell files.  Includes:+--+-- * hs (Haskell)+-- * lhs (literate Haskell)+-- * gc (greencard)+-- * chs (c2hs)+-- * hsc (hsc2hs)+-- * y and ly (happy)+-- * x (alex)+-- * cpphs++fileExtensions :: [String]+fileExtensions =+  [ "hs"+  , "lhs"+  , "gc"+  , "chs"+  , "hsc"+  , "y"+  , "ly"+  , "x"+  , "cpphs"+  ]++interestingFile+  :: [String]+  -- ^ Extensions of module files+  -> FilePath+  -> Bool++interestingFile xs s = case s of+  "" -> False+  x:_+    | not (C.isUpper x) -> False+    | otherwise -> let mayExt = P.takeExtension s+                   in case mayExt of+                       [] -> False+                       _ : ext -> ext `elem` xs++interestingDir :: FilePath -> Bool+interestingDir p = case p of+  [] -> False+  x:_+    | not (C.isUpper x) -> False+    | otherwise -> not $ '.' `elem` p++-- | Gets all Haskell modules in a given directory tree.  Only files+-- with one of the extensions listed in 'fileExtensions' are+-- returned.  Files and directories that do not begin with an+-- uppercase letter are ignored.  (This also ignores files that+-- start with a dot.)  Directories with a dot anywhere in the name+-- are ignored.++modules+  :: FilePath+  -- ^ Start searching within this directory.+  -> IO [String]+  -- ^ A list of Haskell modules in the given directory tree.  The+  -- file contents are not examined; only the file names matter.+  -- Returned as a list of dotted names.+modules = modulesWithExtensions fileExtensions+++-- | Gets all Haskell modules in a given directory tree.  Allows you+-- to specify what extensions you are interested in.++modulesWithExtensions+  :: [String]+  -- ^ Look for files that have one of these extensions.+  -- 'fileExtensions' covers the most common cases.  Files without+  -- one of these extensions are ignored.  Files and directories+  -- that do not begin with an uppercase letter are ignored.  (This+  -- also ignores files that start with a dot.)  Directories with a+  -- dot anywhere in the name are ignored.++  -> FilePath+  -- ^ Start searching within this directory.++  -> IO [String]+  -- ^ A list of Haskell modules in the given directory tree.  The+  -- file contents are not examined; only the file names matter.+  -- Returned as a list of dotted names.++modulesWithExtensions exts start+  = fmap (map modName . sortBy sorter . map reverse)+  $ modulesInDir exts start []+  where+    modName = concat . intersperse "."++sorter :: [String] -> [String] -> Ordering+sorter x y = mconcat (zipWith compare x y) <> compare lenX lenY+  where+    (lenX, lenY) = (length x, length y)++modulesInDir+  :: [String]+  -- ^ Extensions of module files+  -> FilePath+  -- Search is rooted in this directory+  -> [FilePath]+  -- ^ Stack of directories we're in+  -> IO [[String]]+  -- ^ Returns a list of modules in this directory.+modulesInDir exts start dirs = do+  cs <- D.getDirectoryContents (start </> P.joinPath (reverse dirs))+  fmap concat . mapM (processFile exts start dirs) $ cs++processFile+  :: [String]+  -- ^ Extensions of module files+  -> FilePath+  -- ^ Search is rooted in this directory+  -> [FilePath]+  -- Stack of directories we're in, including current directory+  -> FilePath+  -- ^ Interesting file under investigation+  -> IO [[String]]+processFile exts start dirs this = do+  isDir <- D.doesDirectoryExist+    (start </> (P.joinPath . reverse $ this : dirs))+  if isDir+    then if interestingDir this+          then modulesInDir exts start (this : dirs)+          else return []+    else return $ if interestingFile exts this+          then [(P.dropExtension this : dirs)]+          else []++-- | Renders a 'A.Cabal' data type as a string.+renderString+  :: String+  -- ^ Name of program used+  -> ZonedTime+  -- ^ When this output is being created+  -> V.Version+  -- ^ Cartel package version+  -> A.Cabal+  -> String+renderString nm zt ver cbl = hdr ++ R.cabal cbl+  where+    hdr = unlines $+      [ "-- This Cabal file generated using the Cartel library."+      , "-- Cartel is available at:"+      , "-- http://www.github.com/massysett/cartel"+      , "--"+      ] ++ case nm of+            [] -> []+            _ -> ["-- Script name used to generate: " ++ nm]+      +++      [ "-- Generated on: " ++ show zt+      , "-- Cartel library version: " ++ showVer+      ]+    showVer = concat+      . intersperse "."+      . map show+      . V.versionBranch+      $ ver++-- | Render a Cabal file to standard output.  The output will have+-- comments at the beginning indicating that it was built with+-- Cartel, and what version of the Cartel library was used, and when+-- the output was produced, along with (optionally) the filename of+-- the program used to produce the output.+--+-- Ensures that the output is UTF-8, as required by Cabal.+render+  :: String+  -- ^ The name of the program used to produce this output.  Put the+  -- name of your script here so it can appear in the output.  This+  -- is optional; to omit it, use the empty string here.++  -> A.Cabal+  -> IO ()+render nm cbl = do+  zt <- getZonedTime+  IO.hSetBinaryMode IO.stdout False+  IO.hSetEncoding IO.stdout IO.utf8+  putStr $ renderString nm zt Paths_cartel.version cbl+
+ minimum-versions.txt view
@@ -0,0 +1,38 @@+This package was tested to work with these dependency+versions and compiler version.+These are the minimum versions given in the .cabal file.+Tested as of: 2014-05-28 01:31:54.008981 UTC+Path to compiler: ghc-7.4.1+Compiler description: 7.4.1++/opt/ghc/7.4.1/lib/ghc-7.4.1/package.conf.d:+    Cabal-1.14.0+    array-0.4.0.0+    base-4.5.0.0+    bin-package-db-0.0.0.0+    binary-0.5.1.0+    bytestring-0.9.2.1+    containers-0.4.2.1+    deepseq-1.3.0.0+    directory-1.1.0.2+    extensible-exceptions-0.1.1.4+    filepath-1.3.0.0+    (ghc-7.4.1)+    ghc-prim-0.2.0.0+    (haskell2010-1.1.0.1)+    (haskell98-2.0.0.1)+    hoopl-3.8.7.3+    hpc-0.5.1.1+    integer-gmp-0.4.0.0+    old-locale-1.0.0.4+    old-time-1.1.0.0+    pretty-1.1.1.0+    process-1.1.0.1+    rts-1.0+    template-haskell-2.7.0.0+    time-1.4+    unix-2.5.1.0++/home/massysett/cartel/sunlight-28486/db:+    cartel-0.2.0.0+