cartel 0.16.0.0 → 0.18.0.0
raw patch · 10 files changed
+1119/−182 lines, 10 filesdep +optparse-applicativedep +pretty-showdep +processnew-component:exe:cartel-init
Dependencies added: optparse-applicative, pretty-show, process, split
Files
- LICENSE +1/−1
- README.md +10/−6
- cartel.cabal +46/−9
- exe/cartel-init.hs +6/−0
- lib/Cartel.hs +90/−26
- lib/Cartel/Ast.hs +54/−2
- lib/Cartel/Betsy/Internal.hs +1/−2
- lib/Cartel/GenCartelCabal.hs +0/−124
- lib/Cartel/Init.hs +911/−0
- lib/Cartel/Version.hs +0/−12
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2014 - 2015, Omari Norman+Copyright (c) 2014 - 2016, Omari Norman All rights reserved.
README.md view
@@ -11,11 +11,15 @@ http://www.github.com/massysett/cartel -## Test results+## On Stackage -[](https://travis-ci.org/massysett/cartel)+https://www.stackage.org/package/cartel -If you have trouble getting Cartel to build, you might try looking at-the test results by clicking on the button above; archived test-results will show you what package dependencies have been used in the-past to build Cartel successfully.+## Building Cartel++Cartel's Cabal file is generated using Cartel. You will need to+have the `stack` program installed. Stack is available at++http://www.haskellstack.org++To generate the Cabal file, simply run `sh buildprep`.
cartel.cabal view
@@ -2,12 +2,12 @@ -- Cartel is available at: -- http://www.github.com/massysett/cartel ----- Script name used to generate: GenCartelCabal.hs--- Generated on: 2016-01-30 07:32:28.40375 EST+-- Script name used to generate: gen-cartel-cabal+-- Generated on: 2016-06-29 13:31:46.413979 EDT -- Cartel library version: 0.16.0.0 name: cartel-version: 0.16.0.0+version: 0.18.0.0 cabal-version: >= 1.10 license: BSD3 license-file: LICENSE@@ -36,10 +36,9 @@ Cartel.Ast Cartel.Betsy Cartel.Betsy.Internal- Cartel.GenCartelCabal+ Cartel.Init Cartel.Render Cartel.Types- Cartel.Version default-language: Haskell2010 ghc-options: -Wall@@ -51,6 +50,10 @@ , filepath >= 1.3.0.0 , time >= 1.4 , transformers >= 0.3.0.0+ , optparse-applicative >= 0.12.1.0+ , pretty-show >= 1.6.10+ , process >= 1.2.3.0+ , split >= 0.2.3 source-repository head type: git@@ -65,10 +68,9 @@ Cartel.Ast Cartel.Betsy Cartel.Betsy.Internal- Cartel.GenCartelCabal+ Cartel.Init Cartel.Render Cartel.Types- Cartel.Version hs-source-dirs: tests build-depends:@@ -88,11 +90,43 @@ , filepath >= 1.3.0.0 , time >= 1.4 , transformers >= 0.3.0.0+ , optparse-applicative >= 0.12.1.0+ , pretty-show >= 1.6.10+ , process >= 1.2.3.0+ , split >= 0.2.3 else buildable: False default-language: Haskell2010 main-is: cartel-visual-test.hs +Executable cartel-init+ default-language: Haskell2010+ main-is: cartel-init.hs+ hs-source-dirs:+ exe+ other-modules:+ Cartel+ Cartel.Ast+ Cartel.Betsy+ Cartel.Betsy.Internal+ Cartel.Init+ Cartel.Render+ Cartel.Types+ ghc-options:+ -Wall+ hs-source-dirs:+ lib+ build-depends:+ base >= 4.8.0.0 && < 5+ , directory >= 1.1.0.2+ , filepath >= 1.3.0.0+ , time >= 1.4+ , transformers >= 0.3.0.0+ , optparse-applicative >= 0.12.1.0+ , pretty-show >= 1.6.10+ , process >= 1.2.3.0+ , split >= 0.2.3+ Test-Suite cartel-properties default-language: Haskell2010 other-modules:@@ -101,10 +135,9 @@ Cartel.Ast Cartel.Betsy Cartel.Betsy.Internal- Cartel.GenCartelCabal+ Cartel.Init Cartel.Render Cartel.Types- Cartel.Version other-extensions: TemplateHaskell hs-source-dirs:@@ -126,6 +159,10 @@ , filepath >= 1.3.0.0 , time >= 1.4 , transformers >= 0.3.0.0+ , optparse-applicative >= 0.12.1.0+ , pretty-show >= 1.6.10+ , process >= 1.2.3.0+ , split >= 0.2.3 type: exitcode-stdio-1.0 main-is: cartel-properties.hs
+ exe/cartel-init.hs view
@@ -0,0 +1,6 @@+module Main where++import Cartel.Init++main :: IO ()+main = cartelInit
lib/Cartel.hs view
@@ -21,7 +21,7 @@ -- new Cabal file whenever you change the script that generates your -- Cabal file. ----- To the extent possible, Cartel uses the Haskell type system to+-- To some extent, Cartel uses the Haskell type system to -- prevent you from making mistakes in your Cabal file. For example, -- the 'Betsy' type prevents you from using 'flag's that you have not -- declared, and you can't put an 'exposedModules' field in anything@@ -37,35 +37,106 @@ -- I highly recommend that you use Cartel with @stack@, a Haskell -- build tool. Stack is available at ----- <https://github.com/commercialhaskell/stack>------ By using @stack@ as a script interpreter, you can help ensure that--- your Cartel script runs well into the future. This is described at+-- <https://www.haskellstack.org> ----- <http://docs.haskellstack.org/en/stable/GUIDE.html#ghc-runghc>+-- Using @stack@ means you can easily specify the exact package set+-- with which to build your Cabal file, which helps ensure that it+-- builds well into the future regardless of what compiler version+-- someone happens to have installed. I recommend setting up a+-- different Cabal package whose sole job is to build your Cabal file.+-- If you are on a UNIX-like system, use the @cartel-init@ program,+-- which is included in the Cartel package. @cartel-init@ establishes+-- a skeleton file tree for a new package. Run @cartel-init@ first to+-- create an empty tree, and then create your package modules (or copy+-- them into the tree, if you are converting an existing package to+-- Cartel.) ----- In particular, to have a Cartel script that uses @stack@, you do--- something like this in your Cartel script:+-- For example, to create a new package named @hello@, I would run the+-- following: -- -- @--- #!\/usr/\bin\/env\/stack--- -- stack --resolver lts-5.0 --install-ghc runghc --package cartel--- module Main where--- import qualified Cartel as C--- -- etc...+-- $ cartel-init github --author 'Omari Norman' \\+-- --maintainer 'omari@smileystation.com' \\+-- --bsd3 --username massysett hello -- @ ----- However, if you have a @stack.yaml@ file in the same directory as--- your Cabal file, @stack@ will get confused. To work around this,--- temporarily rename your @stack.yaml@ file while you use your Cartel--- script (you can easily write a script to automate this).+-- The @--author@ and @--maintainer@ options specify the author and+-- maintainer fields for the Cabal file. The @--bsd3@ option makes+-- your new package have the BSD3 license; otherwise, you will get an+-- \"All Rights Reserved\" license. The @--username@ option gives+-- your Github username, and @hello@ is the name of the package+-- itself. --+-- This command creates a directory tree that looks like this:+--+-- [@hello\/@] Main project directory. Contains two packages: one to+-- build the Cabal file, and one main package+--+-- [@hello\/LICENSE@] BSD3 license file+--+-- [@hello\/README.md@] This is NOT distributed with the main Cabal+-- package. It describes how to build this project.+--+-- [@hello\/buildprep@] With \"hello\" as the current directory, run+-- \"sh buildprep\" to generate the Cabal file.+--+-- [@hello\/gen-hello-cabal\/@] Directory containing a package that+-- builds the Cabal file.+--+-- [@hello\/gen-hello-cabal\/Setup.hs@] For the package that builds the+-- Cabal file.+--+-- [@hello\/gen-hello-cabal\/buildprep@] Run from this directory to+-- generate the Cabal file.+--+-- [@hello\/gen-hello-cabal\/gen-hello-cabal.cabal@] Cabal file for the+-- pacakge that generates the Cabal file.+--+-- [@hello\/gen-hello-cabal\/gen-hello-cabal.hs@] Module with program+-- that generates the Cabal file. Edit this to change the generated+-- Cabal file.+--+-- [@hello\/gen-hello-cabal\/stack.yaml@] Specifies stack resolver for+-- package that builds the Cabal file.+--+-- [@hello\/hello\/@] Directory with the Cabal package that you would+-- distribute on Hackage.+--+-- [@hello\/hello\/LICENSE@] BSD3 license.+--+-- [@hello\/hello\/README.md@] This is distributed with the project's+-- Cabal package.+--+-- [@hello\/hello\/Setup.hs@] For the main Cabal package.+--+-- [@hello\/hello\/buildprep@] From this directory, run \"sh buildprep\"+-- to generate the Cabal file.+--+-- [@hello\/hello\/lib\/@] Empty directory. Place your library modules+-- in here.+--+-- [@hello\/hello\/stack.yaml@] Has the resolver to use for the main+-- package.+--+-- After running @cartel-init@, you will need to edit the+-- @gen-yourpackage-cabal.hs@ file to add (at a minimum) a @synopsis@,+-- @description@, and any additional @build-depends@.+--+-- For more information on @cartel-init@, run:+--+-- @+-- $ cartel-init --help+-- $ cartel-init cabal --help+-- $ cartel-init tree --help+-- $ cartel-init git --help+-- $ cartel-init github --help+-- @+-- -- Everything you usually need is in this module. Other Cartel -- modules contain implementation details. /See first/ the -- 'NonEmptyString' type synonym, which has important details on how -- to regard 'String's and 'NonEmptyString's as you read the--- documentation. Also, examine "Cartel.GenCartelCabal", which--- generates Cartel's own Cabal file using Cartel.+-- documentation. -- -- Hopefully this module's documentation is organized so that -- top-to-bottom reading will make sense.@@ -266,8 +337,6 @@ import Cartel.Betsy import Cartel.Render import Cartel.Types-import qualified Cartel.Version-import Data.List (intersperse) import Data.Time import System.Environment (getProgName) import System.Exit (exitFailure, exitSuccess)@@ -294,12 +363,7 @@ _ -> ["-- Script name used to generate: " ++ nm] ++ [ "-- Generated on: " ++ show zt- , "-- Cartel library version: " ++ showVer ]- showVer = concat- . intersperse "."- . map show- $ Cartel.Version.version -- | Generic header to prepend to Cabal files. genericHeader :: IO String
lib/Cartel/Ast.hs view
@@ -1196,10 +1196,19 @@ -- -- etc. Fields you don't supply will be blank. -- } -- @+--+-- Many of these fields hold a 'Maybe' type. Values that are+-- 'Nothing' will generate no output in the resulting Cabal file.+-- Other fields are a 'String' type. Empty strings will generate no+-- output in the resulting Cabal file. Other values are lists; empty+-- lists generate no output in the resulting Cabal file. data Properties = Properties { name :: String+ -- ^ The unique name for the package, without the version number. , version :: Version+ -- ^ The package version number, which is always a list of natural+ -- numbers. , cabalVersion :: Maybe (Word, Word) -- ^ The version of the Cabal specification to use. As of -- 2016-01-30 I can find no documentation on what these different@@ -1207,28 +1216,71 @@ -- version 1.22.6.0 is using @1.10@ as the default value here. , buildType :: Maybe BuildType , license :: Maybe License+ -- ^ The type of license under which the package is distributed , licenseFile :: String+ -- ^ The name of a file or files containing the precise copyright+ -- license. The license file will be installed with the package. If+ -- you have mulitple license files, use the 'licenseFiles' field+ -- instead of, or in addition to, this field. , licenseFiles :: [NonEmptyString]+ , copyright :: String+ -- ^ A freeform copyright string, for instance, @Copyright: (c)+ -- 2006-2007 Joe Bloggs@ , author :: String+ -- ^ The original author of the package. , maintainer :: String+ -- ^ According to the \"Developing Cabal Packages\" document, this+ -- should simply be an email address. , stability :: String+ -- ^ The stability level of the package, e.g. @alpha",+ -- @experimental@, @provisional@, @stable@, etc. , homepage :: String+ -- ^ URL for package homepage. , bugReports :: String+ -- ^ URL where user should send bug reports. This can be a+ -- @mailto:@ for mailed bug reports or an @http@ or @https@ URL for+ -- an online bug tracking system. , packageUrl :: String+ -- ^ Location of a source bundle for the package. The distribution+ -- should be a Cabal package. , synopsis :: String+ -- ^ Very short description of the package, for use in a table of packages. , description :: [String]+ -- ^ Description of the package. This can be several paragraphs. , category :: String- -- ^ According to the \"Developing Cabal Packages\" document, this- -- should simply be an email address.+ -- ^ Classification category for Hackage. There is not much of an+ -- organizational system to these categories. , testedWith :: [(Compiler, Constraint)]+ -- ^ LIst of compilers and versions against which the package has+ -- been tested (or built). , dataFiles :: [NonEmptyString]+ -- ^ List of files to be installed for run-time use by the package.+ -- This is useful for packages that have a large amount of static+ -- data. , dataDir :: String+ -- ^ The directory where Cabal looks for data files to install,+ -- relative to the source directory. By default, Cabal will look in+ -- the source directory itself. , extraSourceFiles :: [NonEmptyString]+ -- ^ A list of additional files to be included in source+ -- distributions built with @setup sdist@. As with 'dataFiles' it+ -- can include a limited form of @*@ wildcards in file names. , extraDocFiles :: [NonEmptyString]+ -- ^ A 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. , extraTmpFiles :: [NonEmptyString]+ -- ^ A 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) +-- | The 'mempty' value will generate no output in the resulting Cabal+-- file. 'mappend' takes the last non-'Nothing' value (for 'Maybe'+-- fields) or otherwise simply uses 'mappend' (for 'String' and list+-- values). instance Monoid Properties where mempty = Properties { name = ""
lib/Cartel/Betsy/Internal.hs view
@@ -115,8 +115,7 @@ Just _ -> Left (DuplicateFlag nm) runBetsy- :: Functor m- => Betsy m a+ :: Betsy m a -> m (Either Error (a, [Flag])) -- ^ Returns 'Left' if the making of a flag failed. Otherwise, -- returns the result of the computation, along with a list of all
− lib/Cartel/GenCartelCabal.hs
@@ -1,124 +0,0 @@--- | This program generates Cartel's own Cabal file. It's included--- here in the library to provide a live, type-checked, compilable--- example. You will want to look at the source code itself, as the--- Haddocks won't show you much.--module Cartel.GenCartelCabal where--import Cartel-import qualified Cartel.Version---- Dependencies--base :: Package-base = closedOpen "base" [4,8,0,0] [5]--directory :: Package-directory = atLeast "directory" [1,1,0,2]--filepath :: Package-filepath = atLeast "filepath" [1,3,0,0]--time :: Package-time = atLeast "time" [1,4]--quickcheck :: Package-quickcheck = atLeast "QuickCheck" [2,7]--tasty :: Package-tasty = atLeast "tasty" [0,10]--tastyQuickcheck :: Package-tastyQuickcheck = atLeast "tasty-quickcheck" [0,8]--tastyTh :: Package-tastyTh = atLeast "tasty-th" [0,1]--random :: Package-random = atLeast "random" [1,0,1,1]--transformers :: Package-transformers = atLeast "transformers" [0,3,0,0]--multiarg :: Package-multiarg = atLeast "multiarg" [0,30,0,0]--libDepends :: [Package]-libDepends =- [ base, directory, filepath, time, transformers ]--testDepends :: [Package]-testDepends- = [ multiarg, quickcheck, random, tasty, tastyTh, tastyQuickcheck ]--commonOptions :: HasBuildInfo a => [a]-commonOptions =- [ ghcOptions ["-Wall"]- , hsSourceDirs ["lib"]- , buildDepends libDepends- ]--testOptions :: HasBuildInfo a => [a]-testOptions- = hsSourceDirs ["tests"]- : buildDepends testDepends- : commonOptions--main :: IO ()-main = defaultMain $ do- libModules <- modules "lib"- flagVisual <- makeFlag "visual"- (FlagOpts { flagDescription = "Build cartel-visual-test executable."- , flagDefault = False- , flagManual = True- })- testMods <- modules "tests"- return- ( mempty- { name = "cartel"- , version = Cartel.Version.version- , buildType = Just simple- , category = "Distribution"- , maintainer = "omari@smileystation.com"- , synopsis = "Specify Cabal files in Haskell"- , description =- [ "By specifying your Cabal files in Haskell, you have the power"- , "of Haskell at your disposal to reduce redundancy. You can"- , "also read in trees of module names dynamically, which saves"- , "you from manually maintaining lists of module names."- , ""- , "See the documentation in the \"Cartel\" module for details."- ]- , license = Just bsd3- , licenseFile = "LICENSE"- , copyright = "Copyright 2014-2016 Omari Norman"- , author = "Omari Norman"- , stability = "Experimental"- , cabalVersion = Just (1,10)- , homepage = "http://www.github.com/massysett/cartel"- , bugReports = "http://www.github.com/massysett/cartel/issues"- , extraSourceFiles = ["README.md"]- }- , exposedModules libModules : haskell2010 : commonOptions-- , [ githubHead "massysett" "cartel"- , executable "cartel-visual-test"- [ condBlock (flag flagVisual)- ( buildable True- , otherModules (testMods ++ libModules)- : testOptions- )- [ buildable False- ]- , haskell2010- , mainIs "cartel-visual-test.hs"- ]- , testSuite "cartel-properties"- $ haskell2010 - : otherModules (testMods ++ libModules)- : otherExtensions ["TemplateHaskell"]- : testOptions- ++ exitcodeFields "cartel-properties.hs"- - ]- )
+ lib/Cartel/Init.hs view
@@ -0,0 +1,911 @@+-- | Generates a skeletal file structure to start a new Cartel-based+-- project. Similar to @cabal init@ or @stack new@.+module Cartel.Init+ ( cartelInit+ , Github(..)+ , GitInit(..)+ , License(..)+ , TreeInitMode(..)+ , TreeInit(..)+ , InitMode(..)+ , Init(..)+ , cartelInitWithSettings+ ) where++import Cartel.Types (NonEmptyString)+import Control.Applicative (many, (<|>))+import qualified Control.Exception as Exception+import Data.List (intersperse)+import Data.List.Split (splitOn)+import Data.Maybe (fromMaybe, catMaybes)+import Data.Monoid ((<>))+import qualified Data.Time as Time+import qualified Options.Applicative as O+import qualified Options.Applicative.Types as OT+import qualified System.Directory as Directory+import System.FilePath ((</>))+import qualified System.Process as Process+import Text.Read (readMaybe)+import Text.Show.Pretty (ppShow)++-- # Data types++-- | Configuration information for Github.+data Github = Github+ { githubUsername :: NonEmptyString+ , noGithubIssues :: Bool+ -- ^ If you want to use the Github issue tracker, use 'True' here,+ -- and then the 'C.bugReports' field will give the Github issues+ -- URL.+ , createRemote :: String+ -- ^ If non-empty, creates a Git remote with the given name using+ -- the @git remote@ command. If empty, does not create a Git+ -- remote.+ } deriving Show++data GitInit = GitInit+ { gitIgnorePatterns :: [NonEmptyString]+ -- ^ If there are any values here, a @.gitignore@ file will be+ -- created in your project's root directory, with one pattern for+ -- each element of this list.++ , trackCabal :: Bool+ -- ^ In addition to 'gitIgnorePatterns', if 'False' a pattern is+ -- added to ignore the project's generated .cabal file.++ , useGithub :: Maybe Github+ -- ^ If you want to use Github, pass an appropriate 'Just' value here.+ -- This will:+ --+ -- * use the main Github project page for the 'C.homepage' field+ --+ -- * create an appropriate @source-repository@ entry+ --+ -- * 'noGithubIssues' if you wish+ } deriving Show++-- | What kind of license you want to use. Currently the only choices+-- are BSD3 or AllRightsReserved.+data License+ = BSD3+ | AllRightsReserved+ deriving (Read, Show)++data TreeInitMode+ = MakeTreeOnly+ -- ^ Create a cabal file and write it with a skeleton of files.+ | MakeGit GitInit+ -- ^ Create a cabal file, write it with a skeleton of files, and+ -- initialize a Git repository.+ deriving Show+++data TreeInit = TreeInit+ { cartelResolver :: NonEmptyString+ -- ^ Use this resolver for the @stack.yaml@ for the Cabal file+ -- generator.+ , packageResolver :: NonEmptyString+ -- ^ Use this resolver for the @stack.yaml@ for the package.+ , treeInitMode :: TreeInitMode+ } deriving Show++-- | What to do when running @cabal-init@.+data InitMode+ = ShowCabalOnly+ -- ^ Only show a cabal file.+ | MakeTree TreeInit+ -- ^ Create a cabal file and write it with a skeleton of files.+ deriving Show++-- | Configuration options. Some of these can be overridden on the+-- command line.+data Init = Init+ { author :: String+ -- ^ Used for original package author name.+ , maintainer :: String+ -- ^ Used for the current maintainer. This should be an email address.+ , initMode :: InitMode+ , ghcOptions :: [NonEmptyString]+ -- ^ Put any default GHC warnings you want here, e.g. @["-Wall", "-O0"]@.+ , license :: License+ -- ^ Default license type+ , baseLowerBound :: [Word]+ -- ^ The lower bounds for the dependency for the @base@ package.+ -- For example use @[4,8]@ for the lower bound to be @base-4.8@.+ , baseUpperBound :: [Word]+ -- ^ The upper bounds for the dependency for the @base@ package.+ -- For example use @[5]@ for the lower bound to be @base-5@.+ } deriving Show++data ProjectInit = ProjectInit+ { piInit :: Init+ , projectName :: String+ -- ^ Name for the project. Will be used as the directory name,+ -- Github project name, and Cabal project name.+ } deriving Show++-- # Data type manipulation++gitInit :: Init -> Maybe GitInit+gitInit ini = case initMode ini of+ MakeTree treeInit -> case treeInitMode treeInit of+ MakeTreeOnly -> Nothing+ MakeGit gi -> Just gi+ _ -> Nothing++-- # File and directory utilities++-- | Change to the given directory, carry out IO action, and return to+-- the old directory after the IO action is complete. Exception safe.+withDirectory :: FilePath -> IO a -> IO a+withDirectory fp act = Exception.bracket get+ Directory.setCurrentDirectory (const act)+ where+ get = do+ current <- Directory.getCurrentDirectory+ Directory.setCurrentDirectory fp+ return current++-- | Give the given file executable permission by the user. Uses the+-- chmod program.+makeFileExecutable :: FilePath -> IO ()+makeFileExecutable fp = Process.callProcess "chmod" ["u+x", fp]++-- | Writes a particular file and then makes it executable.+writeExecutableFile :: FilePath -> String -> IO ()+writeExecutableFile fp txt = do+ writeFile fp txt+ makeFileExecutable fp++-- # Creating the text for result files++-- | The text for the package home page, without any quotes. Only+-- returns text if Github is being used.+homepageText+ :: NonEmptyString+ -- ^ Package name+ -> Init+ -> String+homepageText pkgName ini = fromMaybe "" $ do+ gi <- gitInit ini+ gh <- useGithub gi+ return $ "http://www.github.com/" ++ githubUsername gh ++ "/" ++ pkgName++-- | The text for the Github issues page, without any quotes, if there+-- is a Github issues page.+bugReportsText+ :: NonEmptyString+ -- ^ Package name+ -> Init+ -> String+bugReportsText pkgName ini = fromMaybe "" $ do+ gi <- gitInit ini+ gh <- useGithub gi+ return $ if not (noGithubIssues gh)+ then "http://www.github.com/" ++ githubUsername gh ++ "/"+ ++ pkgName ++ "/issues"+ else ""++licenseName+ :: Init+ -> String+licenseName ini = "Cartel." ++ case license ini of+ BSD3 -> "bsd3"+ AllRightsReserved -> "allRightsReserved"++licenseFileName :: Init -> Maybe String+licenseFileName ini = case license ini of+ BSD3 -> Just "LICENSE"+ AllRightsReserved -> Nothing++propertiesText+ :: NonEmptyString+ -- ^ Package name+ -> String+ -- ^ Year+ -> Init+ -> String+propertiesText packageName year ini = unlines+ [ "properties :: Cartel.Properties"+ , "properties = Cartel.Properties"+ , " { Cartel.name = " ++ quotes packageName+ , " , Cartel.version = version"+ , " , Cartel.cabalVersion = Just (1,10)"+ , " , Cartel.buildType = Just Cartel.simple"+ , " , Cartel.license = Just " ++ licenseName ini+ , " , Cartel.licenseFile = " ++ quotes (fromMaybe "" (licenseFileName ini))+ , " , Cartel.licenseFiles = []"+ , " , Cartel.copyright = " ++ (quotes $ "Copyright (c) "+ ++ year ++ " " ++ author ini)+ , " , Cartel.author = " ++ quotes (author ini)+ , " , Cartel.maintainer = " ++ quotes (maintainer ini)+ , " , Cartel.stability = \"Experimental\""+ , " , Cartel.homepage = " ++ quotes (homepageText packageName ini)+ , " , Cartel.bugReports = " ++ quotes (bugReportsText packageName ini)+ , " , Cartel.packageUrl = " ++ quotes ""+ , " , Cartel.synopsis = " ++ quotes ""+ , " , Cartel.description = []"+ , " , Cartel.category = " ++ quotes ""+ , " , Cartel.testedWith = []"+ , " , Cartel.dataFiles = []"+ , " , Cartel.dataDir = " ++ quotes ""+ , " , Cartel.extraSourceFiles = []"+ , " , Cartel.extraDocFiles = []"+ , " , Cartel.extraTmpFiles = []"+ , " }"+ ]++ghcOptionsText :: [NonEmptyString] -> String+ghcOptionsText options = unlines $+ [ "ghcOptions :: Cartel.HasBuildInfo a => a"+ , "ghcOptions = Cartel.ghcOptions"+ ] ++ end+ where+ end = case options of+ [] -> [" [", " ]"]+ x:xs -> optLines ++ [" ]"]+ where+ optLines = (" [ " ++ quotes x) : fmap ((" , " ++) . quotes) xs++commonOptionsText :: String+commonOptionsText = unlines+ [ "commonOptions :: Cartel.HasBuildInfo a => [a]"+ , "commonOptions"+ , " = ghcOptions"+ , " : Cartel.haskell2010"+ , " : Cartel.hsSourceDirs [\"lib\"]"+ , " : []"+ ]++libDependsText+ :: [Word]+ -- ^ Base lower bound+ -> [Word]+ -- ^ Base upper bound+ -> String+libDependsText lower upper = unlines+ [ "libraryDepends :: [Cartel.Package]"+ , "libraryDepends ="+ , " [ Cartel.closedOpen \"base\" " ++ show lower ++ " " ++ show upper+ , " ]"+ ]++libraryText :: String+libraryText = unlines+ [ "library"+ , " :: [Cartel.NonEmptyString]"+ , " -- ^ List of library modules"+ , " -> [Cartel.LibraryField]"+ , "library libModules"+ , " = Cartel.buildDepends libraryDepends"+ , " : Cartel.exposedModules libModules"+ , " : commonOptions"+ ]++githubHeadText+ :: NonEmptyString+ -- ^ Package name+ -> Init+ -> Maybe String+githubHeadText pkgName ini = do+ gi <- gitInit ini+ gh <- useGithub gi+ return . unlines $+ [ "github :: Cartel.Section"+ , "github = Cartel.githubHead " ++ quotes (githubUsername gh)+ ++ " " ++ quotes pkgName+ ]++sectionsText+ :: Init+ -> String+sectionsText ini = unlines+ [ "sections :: [Cartel.Section]"+ , "sections ="+ , " [" ++ section1+ , " ]"+ ]+ where+ section1 = fromMaybe "" $ do+ gi <- gitInit ini+ _ <- useGithub gi+ return " github"++mainText+ :: NonEmptyString+ -- ^ Package name+ -> String+mainText pkgName = unlines+ [ "main :: IO ()"+ , "main = Cartel.defaultMain $ do"+ , " libModules <- Cartel.modules " ++ quotes ("../" ++ pkgName ++ "/lib")+ , " return (properties, library libModules, sections)"+ ]++genCabalText+ :: NonEmptyString+ -- ^ Package name+ -> String+ -- ^ Year+ -> Init+ -> String+genCabalText pkgName year ini = concat . intersperse "\n" . catMaybes $+ [ Just heading+ , Just $ propertiesText pkgName year ini+ , Just $ ghcOptionsText (ghcOptions ini)+ , Just $ commonOptionsText+ , Just $ libDependsText (baseLowerBound ini) (baseUpperBound ini)+ , Just $ libraryText+ , githubHeadText pkgName ini+ , Just $ sectionsText ini+ , Just $ mainText pkgName+ ]+ where+ heading = unlines+ [ "module Main (main) where"+ , ""+ , "import qualified Cartel"+ , ""+ , "version :: [Word]"+ , "version = [0,1,0,0]"+ ]+++quotes :: String -> String+quotes s = "\"" ++ s ++ "\""++cartelCabalText :: ProjectInit -> String+cartelCabalText pinit = unlines+ [ "-- This Cabal file generated by the cartel-init program."+ , ""+ , "name: gen-" ++ projectName pinit ++ "-cabal"+ , "version: 0.1"+ , "build-type: Simple"+ , "synopsis: Generates a Cabal file for the "+ ++ projectName pinit ++ " package"+ , "cabal-version: >=1.10"+ , ""+ , "Executable " ++ exeName+ , " main-is: " ++ exeName ++ ".hs"+ , " default-language: Haskell2010"+ , " ghc-options: -W"+ , " build-depends: base, cartel"+ ]+ where+ exeName = "gen-" ++ projectName pinit ++ "-cabal"++setupText :: String+setupText = unlines+ [ "import Distribution.Simple"+ , "main = defaultMain"+ ]++stackYamlText :: NonEmptyString -> String+stackYamlText resolver = unlines+ [ "# This stack.yaml generated by the cartel-init program."+ , ""+ , "resolver: " ++ resolver+ ]++buildprepHeader :: [String]+buildprepHeader =+ [ "#!/bin/sh"+ , "# Generates the project's .cabal file."+ , "# Must be run with the containing directory as the current directory."+ ]++genCabalBuildprepText+ :: ProjectInit+ -> String+genCabalBuildprepText pini = unlines $ buildprepHeader +++ [ "stack build"+ , "stack exec gen-" ++ projectName pini ++ "-cabal > ../"+ ++ projectName pini ++ "/" ++ projectName pini ++ ".cabal"+ ]++genRootBuildprepText+ :: ProjectInit+ -> String+genRootBuildprepText pini = unlines $ buildprepHeader +++ [ "cd gen-" ++ projectName pini ++ "-cabal"+ , "sh buildprep"+ ]++genPackageBuildprepText+ :: ProjectInit+ -> String+genPackageBuildprepText pini = unlines $ buildprepHeader +++ [ "cd ../gen-" ++ projectName pini ++ "-cabal"+ , "sh buildprep"+ ]++gitignoreText+ :: String+ -- ^ Project name+ -> GitInit+ -> String+gitignoreText pname gi = unlines (headerLines ++ allLines)+ where+ headerLines =+ [ "# This .gitignore file generated by the cartel-init program."+ , ""+ , ".stack-work/"+ , "dist/"+ ]+ allLines = projectLines ++ extraLines+ projectLines+ | trackCabal gi = []+ | otherwise =+ [ "# Do not track project's cabal file"+ , pname ++ ".cabal"+ ]+ extraLines+ | null . gitIgnorePatterns $ gi = []+ | otherwise =+ "# Other specified ignored patterns" : gitIgnorePatterns gi++writeGitignoreText :: ProjectInit -> IO ()+writeGitignoreText pini = case initMode . piInit $ pini of+ ShowCabalOnly -> return ()+ MakeTree ti -> case treeInitMode ti of+ MakeTreeOnly -> return ()+ MakeGit gi -> let txt = gitignoreText (projectName pini) gi+ in writeFile (projectName pini </> ".gitignore") txt+ ++bsd3LicenseText+ :: String+ -- ^ Year+ -> String+ -- ^ Owner+ -> String+ -- ^ License text+bsd3LicenseText year owner = unlines+ [ "Copyright (c) " ++ year ++ " " ++ owner+ , "All rights reserved."+ , ""+ , "Redistribution and use in source and binary forms, with or without"+ , "modification, are permitted provided that the following conditions are"+ , "met:"+ , ""+ , "1. Redistributions of source code must retain the above copyright"+ , "notice, this list of conditions and the following disclaimer."+ , ""+ , "2. 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."+ , ""+ , "3. Neither the name of the copyright holder nor the names of its"+ , "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 HOLDER 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"+ , "HOLDER 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."+ ]++rootReadme :: ProjectInit -> String+rootReadme pini = unlines $+ [ "# " ++ projectName pini+ , ""+ ] ++ licenseLines +++ [ "## Building this project"+ , ""+ , "The Cabal file for this project is generated using the Cartel package."+ , "To generate the Cabal file, simply run `sh buildprep`."+ , "You must run this command from the project's main directory."+ , "You will need to have the `stack` program installed."+ , ""+ , "Stack is available at:"+ , "http://www.haskellstack.org"+ ]+ where+ licenseLines = case license . piInit $ pini of+ AllRightsReserved -> []+ BSD3 ->+ [ "## License"+ , ""+ , "This package is released under the BSD3 license."+ ++ " Please see the LICENSE file."+ , ""+ ]++packageReadme :: ProjectInit -> String+packageReadme pini = unlines $+ [ "# " ++ projectName pini+ , ""+ ] ++ licenseLines +++ [ "## Building this project"+ , ""+ , "If you are viewing the source code repository, a `.cabal` file might"+ , "not be included. Please see the README.md in the main project directory"+ , "for details on how to generate the `.cabal` file."+ ]+ where+ licenseLines = case license . piInit $ pini of+ AllRightsReserved -> []+ BSD3 ->+ [ "## License"+ , ""+ , "This package is released under the BSD3 license."+ ++ " Please see the LICENSE file."+ , ""+ ]++-- # Option parsing++pAuthor :: O.Parser String+pAuthor = O.strOption+ ( O.long "author"+ <> O.short 'a'+ <> O.metavar "NAME"+ <> O.help "Name of the package author (typically your full name)")++pMaintainer :: O.Parser String+pMaintainer = O.strOption+ ( O.long "maintainer"+ <> O.short 'm'+ <> O.metavar "EMAIL"+ <> O.help "Package maintainer's email address")++pGhcOption :: O.Parser [String]+pGhcOption = many $ O.strOption+ ( O.long "ghc-option"+ <> O.short 'g'+ <> O.metavar "OPTION"+ <> O.help ("A single GHC option, with the leading hyphen, e.g. \"-Wall\"."+ ++ " To get multiple GHC options, use this flag more than once."))++pCartelResolver :: O.Parser String+pCartelResolver = O.strOption+ ( O.long "cartel-resolver"+ <> O.metavar "RESOLVER"+ <> O.help "Resolver to use for package that generates Cabal file"+ <> O.value "lts-6.4"+ <> O.showDefault )++pPackageResolver :: O.Parser String+pPackageResolver = O.strOption+ ( O.long "package-resolver"+ <> O.metavar "RESOLVER"+ <> O.help "Resolver to use for the new package"+ <> O.value "lts-6.4"+ <> O.showDefault )++pLicense :: O.Parser License+pLicense = flagBSD <|> pure AllRightsReserved+ where+ flagBSD = O.flag' BSD3+ ( O.long "bsd3"+ <> O.help ("Use BSD3 license. (If this option is not used, license "+ ++ "will be \"All Rights Reserved\".)"))++parseVersionString :: O.ReadM [Word]+parseVersionString = do+ str <- OT.readerAsk+ let digitStrings = splitOn "." str+ case mapM readMaybe digitStrings of+ Nothing -> OT.readerError $ "invalid version number: " ++ str+ Just r -> return r+++pLowerBound :: O.Parser [Word]+pLowerBound = O.option parseVersionString+ ( O.long "base-lower-bound"+ <> O.metavar "VERSION"+ <> O.help ("Lower bound for the 'base' package, e.g. \"4.8\""+ ++ " (default: 4.8)")+ <> O.value [4,8] )++pUpperBound :: O.Parser [Word]+pUpperBound = O.option parseVersionString+ ( O.long "base-upper-bound"+ <> O.metavar "VERSION"+ <> O.help ("Lower bound for the 'base' package, e.g. \"5\""+ ++ " (default: 5)")+ <> O.value [5] )++pInit :: O.Parser (InitMode -> Init)+pInit = f <$> pAuthor+ <*> pMaintainer <*> pGhcOption <*> pLicense <*> pLowerBound+ <*> pUpperBound+ where+ f a m g l low up gi = Init a m gi g l low up++pTreeInit :: O.Parser (TreeInitMode -> TreeInit)+pTreeInit = TreeInit <$> pCartelResolver <*> pPackageResolver++pInitTreeOnly :: O.Parser Init+pInitTreeOnly = f <$> pInit <*> pTreeInit+ where+ f mkIni mkTreeIni = mkIni (MakeTree (mkTreeIni MakeTreeOnly))++pInitNoGithub :: O.Parser Init+pInitNoGithub = f <$> pInit <*> pTreeInit <*> pGitInit+ where+ f getInit getTreeInit getGitInit = getInit+ (MakeTree (getTreeInit (MakeGit (getGitInit Nothing))))++pInitWithGithub :: O.Parser Init+pInitWithGithub = f <$> pInit <*> pTreeInit <*> pGitInit+ <*> pGithub+ where+ f getInit getTreeInit getGitInit gh = getInit+ (MakeTree (getTreeInit (MakeGit (getGitInit (Just gh)))))++pInitCabalOnly :: O.Parser Init+pInitCabalOnly = ($ ShowCabalOnly) <$> pInit+++pGitInit :: O.Parser (Maybe Github -> GitInit)+pGitInit = GitInit <$> pGitIgnore <*> pTrackCabal+ where+ pGitIgnore = many $ O.strOption+ (O.long "git-ignore"+ <> O.short 'g'+ <> O.metavar "PATTERN"+ <> O.help ("A single pattern to add to the project-wide \".gitignore\""+ ++ " file. To add multiple patterns, use this flag multiple times."))+ pTrackCabal = O.switch+ (O.long "track-cabal"+ <> O.short 't'+ <> O.help ("By default an entry is added to \".gitignore\" so that the"+ ++ " generated \".cabal\" file is NOT tracked in git. If you DO want"+ ++ " to track the \".cabal\" file in git, use this option."))++pGithub :: O.Parser Github+pGithub = Github <$> pUser <*> pIssues <*> pRemote+ where+ pUser = O.strOption+ (O.long "username"+ <> O.short 'u'+ <> O.metavar "USERNAME"+ <> O.help "Github username")+ pIssues = O.flag False True+ (O.long "no-issues"+ <> O.help "Whether to use Github issue tracker"+ <> O.help ("By default the Github issues tracker is used. If you do"+ ++ " NOT want to use it, use this flag."))+ pRemote = O.strOption+ (O.long "remote"+ <> O.short 'r'+ <> O.metavar "REMOTE"+ <> O.value "github"+ <> O.showDefault+ <> O.help ("Name for local Git remote to interact with Github "+ ++ "(use empty string to create no remote)"))++pProjectName :: O.Parser String+pProjectName = O.strArgument+ (O.metavar "NAME"+ <> O.help+ ( "Name for project. Used for directory name, Github project name,"+ ++ " and Cabal project name."))++pTreeCommand :: O.Parser ProjectInit+pTreeCommand = O.helper <*>+ (ProjectInit <$> pInitTreeOnly <*> pProjectName)++pCabalCommand :: O.Parser ProjectInit+pCabalCommand = O.helper <*>+ (ProjectInit <$> pInitCabalOnly <*> pProjectName)++modTreeCommand :: O.Mod O.CommandFields ProjectInit+modTreeCommand = O.command "tree" infTreeOnly+ where+ infTreeOnly = O.info pTreeCommand+ (O.progDesc "create file tree only"+ <> O.footer ("Creates a skeleton file tree."))++pGitCommand :: O.Parser ProjectInit+pGitCommand = O.helper <*>+ (f <$> pInitNoGithub <*> pProjectName)+ where+ f i pName = ProjectInit i pName++modGitCommand :: O.Mod O.CommandFields ProjectInit+modGitCommand = O.command "git" infGit+ where+ infGit = O.info pGitCommand+ (O.progDesc "create file tree and Git repository"+ <> O.footer ("Creates a skeleton file tree and initializes a Git"+ ++ " repository."))++pGithubCommand :: O.Parser ProjectInit+pGithubCommand = O.helper <*>+ (f <$> pInitWithGithub <*> pProjectName)+ where+ f i pName = ProjectInit i pName++modGithubCommand :: O.Mod O.CommandFields ProjectInit+modGithubCommand = O.command "github" infGithub+ where+ infGithub = O.info pGithubCommand+ (O.progDesc "create file tree, Git repository, and Github remotes"+ <> O.footer ("Creates a skeleton file tree and initializes a Git"+ ++ " repository with a Github remote."))++modCabalCommand :: O.Mod O.CommandFields ProjectInit+modCabalCommand = O.command "cabal" infCabal+ where+ infCabal = O.info (O.helper <*> pCabalCommand)+ ( O.progDesc "only show Cabal file program"+ <> O.footer ("Creates a program that generates a Cabal file and "+ ++ "prints this program to standard output."))++pCommands :: O.ParserInfo ProjectInit+pCommands = O.info (O.helper <*> O.subparser+ (modCabalCommand+ <> modTreeCommand+ <> modGitCommand+ <> modGithubCommand))+ (O.footer ("Cartel skeleton generator. Sets up a skeleton file tree "+ ++ "to start a new package based on Cartel. Optionally, also sets up "+ ++ "Git repository and Github information. Some command line flags are "+ ++ "required; in the help, these are shown in parentheses. Optional flags "+ ++ "are shown in square brackets."))++-- | Retrieves the 'ProjectInit' by parsing the command line arguments.+parseCommands :: IO ProjectInit+parseCommands = O.execParser pCommands++pSettings :: Init -> O.Parser (a -> a)+pSettings ini = O.infoOption+ (ppShow ini)+ ( O.short 's'+ <> O.long "settings"+ <> O.help "show compiled-in settings and exit")++pWithSettingsName :: Init -> O.Parser String+pWithSettingsName ini = O.helper <*> pSettings ini <*> pProjectName++parseCartelInitWithSettings :: Init -> IO ProjectInit+parseCartelInitWithSettings ini = O.execParser parseInfo+ where+ parseInfo = O.info (O.helper <*> parser)+ (O.footer ("Cartel skeleton generator, with custom"+ ++ " compiled-in settings. To see the settings you compiled in,"+ ++ " use the \"--settings\" option."))+ parser = ProjectInit <$> pure ini <*> pWithSettingsName ini++-- | Runs @cartel-init@, but with the settings you compile in.+-- Retrieves the project name from the command line. Has a+-- command-line option, @--settings@, that displays the built-in+-- settings and then exits.+cartelInitWithSettings :: Init -> IO ()+cartelInitWithSettings ini = parseCartelInitWithSettings ini >>= runCartelInit++printCabalText :: ProjectInit -> String -> IO ()+printCabalText pinit cabalTxt = case initMode . piInit $ pinit of+ ShowCabalOnly -> putStrLn cabalTxt+ _ -> return ()++-- # Writing text to disk++writeLicenseText+ :: String+ -- ^ Year+ -> ProjectInit+ -> IO ()+writeLicenseText yr pini = case license . piInit $ pini of+ AllRightsReserved -> return ()+ BSD3 -> do+ writeFile (projectName pini </> "LICENSE") txt+ writeFile (projectName pini </> projectName pini </> "LICENSE") txt+ where+ txt = bsd3LicenseText yr (author . piInit $ pini)++writeReadmes :: ProjectInit -> IO ()+writeReadmes pini = do+ writeFile (name </> "README.md") (rootReadme pini)+ writeFile (name </> name </> "README.md") (packageReadme pini)+ where+ name = projectName pini++writeSetupFiles :: ProjectInit -> IO ()+writeSetupFiles pini = do+ writeFile (name </> ("gen-" ++ name ++ "-cabal") </> "Setup.hs")+ setupText+ writeFile (name </> name </> "Setup.hs") setupText+ where+ name = projectName pini++writeStackYamls :: ProjectInit -> IO ()+writeStackYamls pini = case initMode . piInit $ pini of+ ShowCabalOnly -> return ()+ MakeTree ti -> do+ writeFile (name </> ("gen-" ++ name ++ "-cabal") </> "stack.yaml")+ (stackYamlText (cartelResolver ti))+ writeFile (name </> name </> "stack.yaml")+ (stackYamlText (packageResolver ti))+ where+ name = projectName pini++writeBuildpreps :: ProjectInit -> IO ()+writeBuildpreps pini = do+ let fnRoot = (name </> ("gen-" ++ name ++ "-cabal") </> bp)+ writeExecutableFile fnRoot (genCabalBuildprepText pini)+ let fnPackage = (name </> bp)+ writeExecutableFile fnPackage (genRootBuildprepText pini)+ let fnLib = name </> name </> bp+ writeExecutableFile fnLib (genPackageBuildprepText pini)+ where+ name = projectName pini+ bp = "buildprep"++++createSkeleton+ :: String+ -- ^ Year+ -> ProjectInit+ -> String+ -- ^ Cabal text+ -> IO ()+createSkeleton yr pini cabalText = do+ -- Make directories+ Directory.createDirectory name+ Directory.createDirectory (name </> cartelProject)+ Directory.createDirectory (name </> name)+ Directory.createDirectory (name </> name </> "lib")++ -- Write files+ writeLicenseText yr pini+ writeFile (name </> cartelProject </> (cartelProject ++ ".hs")) cabalText+ writeFile (name </> cartelProject </> (cartelProject ++ ".cabal"))+ (cartelCabalText pini)+ writeReadmes pini+ writeGitignoreText pini+ writeSetupFiles pini+ writeStackYamls pini+ writeBuildpreps pini+ where+ name = projectName pini+ cartelProject = "gen-" ++ name ++ "-cabal"++-- # Git++addGitRemote :: ProjectInit -> IO ()+addGitRemote pini = case initMode . piInit $ pini of+ ShowCabalOnly -> return ()+ MakeTree ti -> case treeInitMode ti of+ MakeTreeOnly -> return ()+ MakeGit gi -> case useGithub gi of+ Nothing -> return ()+ Just gh+ | null (createRemote gh) -> return ()+ | otherwise -> withDirectory (projectName pini) $+ Process.callProcess "git" ["remote", "add", createRemote gh, url]+ where+ url = "git@github.com:" ++ githubUsername gh ++ "/"+ ++ projectName pini ++ ".git"++runGitInit :: ProjectInit -> IO ()+runGitInit pini = withDirectory (projectName pini) $+ Process.callProcess "git" ["init"]++initializeGit :: ProjectInit -> IO ()+initializeGit pini = runGitInit pini >> addGitRemote pini++runCartelInit :: ProjectInit -> IO ()+runCartelInit ini = do+ zt <- Time.getZonedTime+ let (yr, _, _) = Time.toGregorian . Time.localDay+ . Time.zonedTimeToLocalTime $ zt+ cabalText = genCabalText (projectName ini) (show yr) (piInit ini)+ printCabalText ini cabalText+ createSkeleton (show yr) ini cabalText+ initializeGit ini++-- | Runs @cartel-init@, retrieving all configuration information from+-- the command line.+cartelInit :: IO ()+cartelInit = parseCommands >>= runCartelInit
− lib/Cartel/Version.hs
@@ -1,12 +0,0 @@--- | The current version for the Cartel library. Having this in a--- separate module allows for the "Cartel.GenCartelCabal" module to--- exist. Without this module, I'd have to rely on the @Paths_cartel@--- module to exist, which introduces some nasty bootstrapping issues--- for "Cartel.GenCartelCabal" or, alternatively, some nasty circular--- dependency issues if this value were included directly into--- "Cartel.GenCartelCabal".--module Cartel.Version where--version :: [Word]-version = [0,16,0,0]