diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -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.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -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.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/cartel.cabal b/cartel.cabal
new file mode 100644
--- /dev/null
+++ b/cartel.cabal
@@ -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
diff --git a/current-versions.txt b/current-versions.txt
new file mode 100644
--- /dev/null
+++ b/current-versions.txt
@@ -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
+
diff --git a/genCabal.hs b/genCabal.hs
new file mode 100644
--- /dev/null
+++ b/genCabal.hs
@@ -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
diff --git a/lib/Cartel.hs b/lib/Cartel.hs
new file mode 100644
--- /dev/null
+++ b/lib/Cartel.hs
@@ -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
diff --git a/lib/Cartel/Ast.hs b/lib/Cartel/Ast.hs
new file mode 100644
--- /dev/null
+++ b/lib/Cartel/Ast.hs
@@ -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)
diff --git a/lib/Cartel/Defaults.hs b/lib/Cartel/Defaults.hs
new file mode 100644
--- /dev/null
+++ b/lib/Cartel/Defaults.hs
@@ -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 = []
+  }
+
diff --git a/lib/Cartel/Render.hs b/lib/Cartel/Render.hs
new file mode 100644
--- /dev/null
+++ b/lib/Cartel/Render.hs
@@ -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
+  ]
+
diff --git a/lib/Cartel/Tools.hs b/lib/Cartel/Tools.hs
new file mode 100644
--- /dev/null
+++ b/lib/Cartel/Tools.hs
@@ -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
+
diff --git a/minimum-versions.txt b/minimum-versions.txt
new file mode 100644
--- /dev/null
+++ b/minimum-versions.txt
@@ -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
+
