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 +30/−0
- README.md +17/−0
- Setup.hs +2/−0
- cartel.cabal +55/−0
- current-versions.txt +38/−0
- genCabal.hs +123/−0
- lib/Cartel.hs +57/−0
- lib/Cartel/Ast.hs +461/−0
- lib/Cartel/Defaults.hs +47/−0
- lib/Cartel/Render.hs +337/−0
- lib/Cartel/Tools.hs +321/−0
- minimum-versions.txt +38/−0
+ 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+