cartel 0.10.0.2 → 0.12.0.0
raw patch · 21 files changed
+2787/−1685 lines, 21 filesdep +QuickCheckdep +multiargdep +quickpulldep ~timenew-component:exe:cartel-visual-testPVP ok
version bump matches the API change (PVP)
Dependencies added: QuickCheck, multiarg, quickpull, random, transformers
Dependency ranges changed: time
API changes (from Hackage documentation)
- Cartel.Ast: Constraint :: Ordering -> Version -> Constraint
- Cartel.Ast: Flag :: String -> String -> Bool -> Bool -> Flag
- Cartel.Ast: LibExposed :: Bool -> LibraryField
- Cartel.Ast: LibExposedModules :: [String] -> LibraryField
- Cartel.Ast: Library :: [LibraryField] -> Library
- Cartel.Ast: TestType :: TestSuiteType -> TestSuiteField
- Cartel.Ast: Version :: [Int] -> Version
- Cartel.Ast: bmFields :: Benchmark -> [BenchmarkField]
- Cartel.Ast: bmMainIs :: Benchmark -> String
- Cartel.Ast: bmName :: Benchmark -> String
- Cartel.Ast: cBenchmarks :: Cabal -> [Benchmark]
- Cartel.Ast: cExecutables :: Cabal -> [Executable]
- Cartel.Ast: cFlags :: Cabal -> [Flag]
- Cartel.Ast: cLibrary :: Cabal -> Maybe Library
- Cartel.Ast: cProperties :: Cabal -> Properties
- Cartel.Ast: cRepositories :: Cabal -> [Repository]
- Cartel.Ast: cTestSuites :: Cabal -> [TestSuite]
- Cartel.Ast: csComparison :: Constraint -> Ordering
- Cartel.Ast: csVersion :: Constraint -> Version
- Cartel.Ast: data CondTree
- Cartel.Ast: data Flag
- Cartel.Ast: data Language
- Cartel.Ast: flDefault :: Flag -> Bool
- Cartel.Ast: flDescription :: Flag -> String
- Cartel.Ast: flManual :: Flag -> Bool
- Cartel.Ast: flName :: Flag -> String
- Cartel.Ast: ifElse :: CondBlock a -> [a]
- Cartel.Ast: instance Eq CondTree
- Cartel.Ast: instance Eq Flag
- Cartel.Ast: instance Eq Language
- Cartel.Ast: instance Eq Library
- Cartel.Ast: instance Eq Version
- Cartel.Ast: instance Ord CondTree
- Cartel.Ast: instance Ord Flag
- Cartel.Ast: instance Ord Language
- Cartel.Ast: instance Ord Library
- Cartel.Ast: instance Ord Version
- Cartel.Ast: instance Show CondTree
- Cartel.Ast: instance Show Flag
- Cartel.Ast: instance Show Language
- Cartel.Ast: instance Show Library
- Cartel.Ast: instance Show Version
- Cartel.Ast: libFields :: Library -> [LibraryField]
- Cartel.Ast: newtype Library
- Cartel.Ast: newtype Version
- Cartel.Ast: packConstraints :: Package -> Maybe ConstrTree
- Cartel.Ast: packName :: Package -> String
- Cartel.Ast: prAuthor :: Properties -> String
- Cartel.Ast: prBugReports :: Properties -> String
- Cartel.Ast: prBuildType :: Properties -> BuildType
- Cartel.Ast: prCabalVersion :: Properties -> (Int, Int)
- Cartel.Ast: prCategory :: Properties -> String
- Cartel.Ast: prCopyright :: Properties -> String
- Cartel.Ast: prDataDir :: Properties -> String
- Cartel.Ast: prDataFiles :: Properties -> [String]
- Cartel.Ast: prDescription :: Properties -> [String]
- Cartel.Ast: prExtraDocFiles :: Properties -> [String]
- Cartel.Ast: prExtraSourceFiles :: Properties -> [String]
- Cartel.Ast: prExtraTmpFiles :: Properties -> [String]
- Cartel.Ast: prHomepage :: Properties -> String
- Cartel.Ast: prLicense :: Properties -> License
- Cartel.Ast: prLicenseFile :: Properties -> String
- Cartel.Ast: prLicenseFiles :: Properties -> [String]
- Cartel.Ast: prMaintainer :: Properties -> String
- Cartel.Ast: prName :: Properties -> String
- Cartel.Ast: prPackageUrl :: Properties -> String
- Cartel.Ast: prStability :: Properties -> String
- Cartel.Ast: prSynopsis :: Properties -> String
- Cartel.Ast: prTestedWith :: Properties -> [(Compiler, ConstrTree)]
- Cartel.Ast: prVersion :: Properties -> Version
- Cartel.Ast: tsFields :: TestSuite -> [TestSuiteField]
- Cartel.Ast: tsName :: TestSuite -> String
- Cartel.Ast: unVersion :: Version -> [Int]
- Cartel.Empty: class Empty a
- Cartel.Empty: empty :: Empty a => a
- Cartel.Empty: instance Empty Cabal
- Cartel.Empty: instance Empty Flag
- Cartel.Empty: instance Empty Properties
- Cartel.Empty: instance Empty Repository
- Cartel.Render: benchmark :: Benchmark -> String
- Cartel.Render: buildType :: BuildType -> String
- Cartel.Render: cabal :: Cabal -> String
- Cartel.Render: cabalVersion :: (Int, Int) -> String
- Cartel.Render: class Field a
- Cartel.Render: comparison :: Ordering -> String
- Cartel.Render: compiler :: Compiler -> String
- Cartel.Render: condBlock :: Field a => Int -> CondBlock a -> String
- Cartel.Render: condTree :: CondTree -> String
- Cartel.Render: condition :: Condition -> String
- Cartel.Render: constrTree :: ConstrTree -> String
- Cartel.Render: constraint :: Constraint -> String
- Cartel.Render: description :: [String] -> String
- Cartel.Render: executable :: Executable -> String
- Cartel.Render: field :: Field a => Int -> a -> String
- Cartel.Render: flag :: Flag -> String
- Cartel.Render: instance Field BenchmarkField
- Cartel.Render: instance Field BuildInfoField
- Cartel.Render: instance Field ExecutableField
- Cartel.Render: instance Field LibraryField
- Cartel.Render: instance Field TestSuiteField
- Cartel.Render: language :: Language -> String
- Cartel.Render: library :: Library -> String
- Cartel.Render: license :: License -> String
- Cartel.Render: logical :: Logical -> String
- Cartel.Render: package :: Package -> String
- Cartel.Render: properties :: Properties -> String
- Cartel.Render: repoKind :: RepoKind -> String
- Cartel.Render: repository :: Repository -> String
- Cartel.Render: testSuite :: TestSuite -> String
- Cartel.Render: testSuiteType :: TestSuiteType -> String
- Cartel.Render: testedWith :: [(Compiler, ConstrTree)] -> String
- Cartel.Render: vcs :: Vcs -> String
- Cartel.Render: version :: Version -> String
- Cartel.Tools: (&&&) :: LogicTree a => a -> a -> a
- Cartel.Tools: (|||) :: LogicTree a => a -> a -> a
- Cartel.Tools: apiVersion :: String -> [Int] -> Package
- Cartel.Tools: arch :: String -> CondTree
- Cartel.Tools: buildDepends :: Field a => [Package] -> a
- Cartel.Tools: buildInfo :: Field a => BuildInfoField -> a
- Cartel.Tools: buildTools :: Field a => [Package] -> a
- Cartel.Tools: buildable :: Field a => Bool -> a
- Cartel.Tools: cSources :: Field a => [String] -> a
- Cartel.Tools: ccOptions :: Field a => [String] -> a
- Cartel.Tools: cif :: Field a => CondTree -> [a] -> [a] -> a
- Cartel.Tools: class Field a
- Cartel.Tools: class LogicTree a
- Cartel.Tools: closedOpen :: String -> [Int] -> [Int] -> Package
- Cartel.Tools: conditional :: Field a => CondBlock a -> a
- Cartel.Tools: cppOptions :: Field a => [String] -> a
- Cartel.Tools: defaultLanguage :: Field a => Language -> a
- Cartel.Tools: eq :: [Int] -> ConstrTree
- Cartel.Tools: exactly :: String -> [Int] -> Package
- Cartel.Tools: extensions :: Field a => [String] -> a
- Cartel.Tools: extraLibDirs :: Field a => [String] -> a
- Cartel.Tools: extraLibraries :: Field a => [String] -> a
- Cartel.Tools: false :: CondTree
- Cartel.Tools: fileExtensions :: [String]
- Cartel.Tools: flag :: String -> CondTree
- Cartel.Tools: frameworks :: Field a => [String] -> a
- Cartel.Tools: ghcOptions :: Field a => [String] -> a
- Cartel.Tools: ghcProfOptions :: Field a => [String] -> a
- Cartel.Tools: ghcSharedOptions :: Field a => [String] -> a
- Cartel.Tools: gt :: [Int] -> ConstrTree
- Cartel.Tools: gtEq :: [Int] -> ConstrTree
- Cartel.Tools: hsSourceDirs :: Field a => [String] -> a
- Cartel.Tools: hugsOptions :: Field a => [String] -> a
- Cartel.Tools: impl :: Compiler -> Maybe ConstrTree -> CondTree
- Cartel.Tools: includeDirs :: Field a => [String] -> a
- Cartel.Tools: includes :: Field a => [String] -> a
- Cartel.Tools: installIncludes :: Field a => [String] -> a
- Cartel.Tools: instance Field BenchmarkField
- Cartel.Tools: instance Field ExecutableField
- Cartel.Tools: instance Field LibraryField
- Cartel.Tools: instance Field TestSuiteField
- Cartel.Tools: instance LogicTree CondTree
- Cartel.Tools: instance LogicTree ConstrTree
- Cartel.Tools: ldOptions :: Field a => [String] -> a
- Cartel.Tools: lt :: [Int] -> ConstrTree
- Cartel.Tools: ltEq :: [Int] -> ConstrTree
- Cartel.Tools: modules :: FilePath -> IO [String]
- Cartel.Tools: modulesWithExtensions :: [String] -> FilePath -> IO [String]
- Cartel.Tools: nextBreaking :: String -> [Int] -> Package
- Cartel.Tools: nextMajor :: String -> [Int] -> Package
- Cartel.Tools: nhc98Options :: Field a => [String] -> a
- Cartel.Tools: otherModules :: Field a => [String] -> a
- Cartel.Tools: pkgConfigDepends :: Field a => [Package] -> a
- Cartel.Tools: render :: String -> Cabal -> IO ()
- Cartel.Tools: renderString :: String -> ZonedTime -> Version -> Cabal -> String
- Cartel.Tools: system :: String -> CondTree
- Cartel.Tools: true :: CondTree
+ Cartel: (&&&) :: LogicTree a => a -> a -> a
+ Cartel: (|||) :: LogicTree a => a -> a -> a
+ Cartel: DuplicateFlag :: FlagName -> Error
+ Cartel: EmptyFlagName :: Error
+ Cartel: Failed :: String -> Error
+ Cartel: FlagOpts :: String -> Bool -> Bool -> FlagOpts
+ Cartel: Properties :: String -> Version -> Maybe (Word, Word) -> Maybe BuildType -> Maybe License -> String -> [NonEmptyString] -> String -> String -> String -> String -> String -> String -> String -> String -> [String] -> String -> [(Compiler, Constraint)] -> [NonEmptyString] -> String -> [NonEmptyString] -> [NonEmptyString] -> [NonEmptyString] -> Properties
+ Cartel: Repository :: Maybe Vcs -> Maybe RepoKind -> String -> NonEmptyString -> String -> String -> Repository
+ Cartel: agpl :: License
+ Cartel: allRightsReserved :: License
+ Cartel: anyVersion :: Constraint
+ Cartel: apache :: License
+ Cartel: apiVersion :: NonEmptyString -> Version -> Package
+ Cartel: arch :: NonEmptyString -> Condition
+ Cartel: archVcs :: Vcs
+ Cartel: author :: Properties -> String
+ Cartel: bazaar :: Vcs
+ Cartel: benchmark :: NonEmptyString -> [BenchmarkField] -> Section
+ Cartel: betsyToCabalStringIO :: (MonadIO m, Functor m) => Betsy m Cabal -> m (Either Error String)
+ Cartel: blank :: Blank a => a
+ Cartel: bsd2 :: License
+ Cartel: bsd3 :: License
+ Cartel: bsd4 :: License
+ Cartel: bugReports :: Properties -> String
+ Cartel: buildDepends :: HasBuildInfo a => [Package] -> a
+ Cartel: buildTools :: HasBuildInfo a => [Package] -> a
+ Cartel: buildType :: Properties -> Maybe BuildType
+ Cartel: buildable :: HasBuildInfo a => Bool -> a
+ Cartel: cSources :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel: cabal :: Properties -> [LibraryField] -> [Section] -> Cabal
+ Cartel: cabalVersion :: Properties -> Maybe (Word, Word)
+ Cartel: category :: Properties -> String
+ Cartel: ccOptions :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel: class Blank a
+ Cartel: class BuildsExe a
+ Cartel: class BuildsExitcode a
+ Cartel: class HasBuildInfo a
+ Cartel: class LogicTree a
+ Cartel: class Renderable a
+ Cartel: class RenderableIndented a
+ Cartel: closedOpen :: NonEmptyString -> Version -> Version -> Package
+ Cartel: condBlock :: HasBuildInfo a => Condition -> (a, [a]) -> [a] -> a
+ Cartel: configure :: BuildType
+ Cartel: copyright :: Properties -> String
+ Cartel: cppOptions :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel: currentFlags :: Applicative f => Betsy f [(FlagName, FlagOpts)]
+ Cartel: custom :: BuildType
+ Cartel: cvs :: NonEmptyString -> Vcs
+ Cartel: darcs :: Vcs
+ Cartel: data BenchmarkField
+ Cartel: data Betsy m a
+ Cartel: data BuildInfoField
+ Cartel: data BuildType
+ Cartel: data Cabal
+ Cartel: data Compiler
+ Cartel: data Condition
+ Cartel: data Constraint
+ Cartel: data Error
+ Cartel: data ExecutableField
+ Cartel: data FlagName
+ Cartel: data FlagOpts
+ Cartel: data LibraryField
+ Cartel: data License
+ Cartel: data Package
+ Cartel: data Properties
+ Cartel: data RepoKind
+ Cartel: data Repository
+ Cartel: data Section
+ Cartel: data TestSuiteField
+ Cartel: data Vcs
+ Cartel: data Word :: *
+ Cartel: dataDir :: Properties -> String
+ Cartel: dataFiles :: Properties -> [NonEmptyString]
+ Cartel: defaultMain :: Betsy IO (Properties, [LibraryField], [Section]) -> IO ()
+ Cartel: description :: Properties -> [String]
+ Cartel: detailed :: TestSuiteField
+ Cartel: eq :: Version -> Constraint
+ Cartel: exactly :: NonEmptyString -> Version -> Package
+ Cartel: executable :: NonEmptyString -> [ExecutableField] -> Section
+ Cartel: exitcodeFields :: (BuildsExitcode a, BuildsExe a) => NonEmptyString -> [a]
+ Cartel: exitcodeStdio :: BuildsExitcode a => a
+ Cartel: exposed :: Bool -> LibraryField
+ Cartel: exposedModules :: [NonEmptyString] -> LibraryField
+ Cartel: extensions :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel: extraDocFiles :: Properties -> [NonEmptyString]
+ Cartel: extraLibDirs :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel: extraLibraries :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel: extraSourceFiles :: Properties -> [NonEmptyString]
+ Cartel: extraTmpFiles :: Properties -> [NonEmptyString]
+ Cartel: false :: Condition
+ Cartel: fileExtensions :: [String]
+ Cartel: flag :: FlagName -> Condition
+ Cartel: flagDefault :: FlagOpts -> Bool
+ Cartel: flagDescription :: FlagOpts -> String
+ Cartel: flagManual :: FlagOpts -> Bool
+ Cartel: frameworks :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel: ghc :: Compiler
+ Cartel: ghcOptions :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel: ghcProfOptions :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel: ghcSharedOptions :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel: git :: Vcs
+ Cartel: githubHead :: NonEmptyString -> NonEmptyString -> Section
+ Cartel: gpl :: License
+ Cartel: gt :: Version -> Constraint
+ Cartel: gtEq :: Version -> Constraint
+ Cartel: haskell2010 :: HasBuildInfo a => a
+ Cartel: haskell98 :: HasBuildInfo a => a
+ Cartel: helium :: Compiler
+ Cartel: homepage :: Properties -> String
+ Cartel: hsSourceDirs :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel: hugs :: Compiler
+ Cartel: hugsOptions :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel: impl :: Compiler -> Constraint -> Condition
+ Cartel: includeDirs :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel: includes :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel: installIncludes :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel: invert :: Condition -> Condition
+ Cartel: jhc :: Compiler
+ Cartel: ldOptions :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel: lgpl :: License
+ Cartel: lhc :: Compiler
+ Cartel: license :: Properties -> Maybe License
+ Cartel: licenseFile :: Properties -> String
+ Cartel: licenseFiles :: Properties -> [NonEmptyString]
+ Cartel: lt :: Version -> Constraint
+ Cartel: ltEq :: Version -> Constraint
+ Cartel: mainIs :: BuildsExe a => NonEmptyString -> a
+ Cartel: maintainer :: Properties -> String
+ Cartel: make :: BuildType
+ Cartel: makeFlag :: Applicative m => NonEmptyString -> FlagOpts -> Betsy m FlagName
+ Cartel: mercurial :: Vcs
+ Cartel: mit :: License
+ Cartel: modules :: MonadIO m => FilePath -> Betsy m [NonEmptyString]
+ Cartel: modulesWithExtensions :: MonadIO m => [NonEmptyString] -> FilePath -> Betsy m [NonEmptyString]
+ Cartel: monotone :: Vcs
+ Cartel: mpl :: License
+ Cartel: name :: Properties -> String
+ Cartel: nextBreaking :: NonEmptyString -> Version -> Package
+ Cartel: nextMajor :: NonEmptyString -> Version -> Package
+ Cartel: nhc :: Compiler
+ Cartel: nhc98Options :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel: otherLicense :: License
+ Cartel: otherModules :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel: package :: NonEmptyString -> Constraint -> Package
+ Cartel: packageUrl :: Properties -> String
+ Cartel: pkgConfigDepends :: HasBuildInfo a => [Package] -> a
+ Cartel: publicDomain :: License
+ Cartel: render :: Renderable a => a -> String
+ Cartel: renderBetsy :: Functor m => Betsy m Cabal -> m (Either Error String)
+ Cartel: renderIndented :: RenderableIndented a => Int -> a -> String
+ Cartel: repoBranch :: Repository -> String
+ Cartel: repoHead :: RepoKind
+ Cartel: repoKind :: Repository -> Maybe RepoKind
+ Cartel: repoLocation :: Repository -> NonEmptyString
+ Cartel: repoSubdir :: Repository -> String
+ Cartel: repoTag :: Repository -> String
+ Cartel: repoThis :: RepoKind
+ Cartel: repoVcs :: Repository -> Maybe Vcs
+ Cartel: repository :: Repository -> Section
+ Cartel: simple :: BuildType
+ Cartel: stability :: Properties -> String
+ Cartel: svn :: Vcs
+ Cartel: synopsis :: Properties -> String
+ Cartel: system :: NonEmptyString -> Condition
+ Cartel: testModule :: NonEmptyString -> TestSuiteField
+ Cartel: testSuite :: NonEmptyString -> [TestSuiteField] -> Section
+ Cartel: testedWith :: Properties -> [(Compiler, Constraint)]
+ Cartel: true :: Condition
+ Cartel: type NonEmptyString = String
+ Cartel: type Version = [Word]
+ Cartel: unconstrained :: NonEmptyString -> Package
+ Cartel: version :: Properties -> Version
+ Cartel: yhc :: Compiler
+ Cartel.Ast: (&&&) :: LogicTree a => a -> a -> a
+ Cartel.Ast: (|||) :: LogicTree a => a -> a -> a
+ Cartel.Ast: AnyVersion :: Constraint
+ Cartel.Ast: BenchExitCode :: BenchmarkType
+ Cartel.Ast: BenchmarkMainIs :: NonEmptyString -> BenchmarkField
+ Cartel.Ast: BenchmarkType :: BenchmarkType -> BenchmarkField
+ Cartel.Ast: Constrained :: ConstrTree -> Constraint
+ Cartel.Ast: Exposed :: Bool -> LibraryField
+ Cartel.Ast: ExposedModules :: [NonEmptyString] -> LibraryField
+ Cartel.Ast: SecBench :: Benchmark -> Section
+ Cartel.Ast: SecExe :: Executable -> Section
+ Cartel.Ast: SecRepo :: Repository -> Section
+ Cartel.Ast: SecTest :: TestSuite -> Section
+ Cartel.Ast: TestSuiteType :: TestSuiteType -> TestSuiteField
+ Cartel.Ast: agpl :: License
+ Cartel.Ast: allRightsReserved :: License
+ Cartel.Ast: anyVersion :: Constraint
+ Cartel.Ast: apache :: License
+ Cartel.Ast: apiVersion :: NonEmptyString -> Version -> Package
+ Cartel.Ast: arch :: NonEmptyString -> Condition
+ Cartel.Ast: archVcs :: Vcs
+ Cartel.Ast: author :: Properties -> String
+ Cartel.Ast: bazaar :: Vcs
+ Cartel.Ast: benchmark :: NonEmptyString -> [BenchmarkField] -> Section
+ Cartel.Ast: benchmarkFields :: Benchmark -> [BenchmarkField]
+ Cartel.Ast: benchmarkName :: Benchmark -> NonEmptyString
+ Cartel.Ast: blank :: Blank a => a
+ Cartel.Ast: bsd2 :: License
+ Cartel.Ast: bsd3 :: License
+ Cartel.Ast: bsd4 :: License
+ Cartel.Ast: bugReports :: Properties -> String
+ Cartel.Ast: buildDepends :: HasBuildInfo a => [Package] -> a
+ Cartel.Ast: buildInfo :: HasBuildInfo a => BuildInfoField -> a
+ Cartel.Ast: buildTools :: HasBuildInfo a => [Package] -> a
+ Cartel.Ast: buildType :: Properties -> Maybe BuildType
+ Cartel.Ast: buildable :: HasBuildInfo a => Bool -> a
+ Cartel.Ast: cSources :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel.Ast: cabal :: Properties -> [LibraryField] -> [Section] -> Cabal
+ Cartel.Ast: cabalVersion :: Properties -> Maybe (Word, Word)
+ Cartel.Ast: category :: Properties -> String
+ Cartel.Ast: ccOptions :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel.Ast: class Blank a
+ Cartel.Ast: class BuildsExe a
+ Cartel.Ast: class BuildsExitcode a
+ Cartel.Ast: class HasBuildInfo a
+ Cartel.Ast: class LogicTree a
+ Cartel.Ast: closedOpen :: NonEmptyString -> Version -> Version -> Package
+ Cartel.Ast: condBlock :: HasBuildInfo a => Condition -> (a, [a]) -> [a] -> a
+ Cartel.Ast: conditional :: HasBuildInfo a => CondBlock a -> a
+ Cartel.Ast: configure :: BuildType
+ Cartel.Ast: copyright :: Properties -> String
+ Cartel.Ast: cppOptions :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel.Ast: custom :: BuildType
+ Cartel.Ast: cvs :: NonEmptyString -> Vcs
+ Cartel.Ast: darcs :: Vcs
+ Cartel.Ast: data BenchmarkType
+ Cartel.Ast: data CondLeaf
+ Cartel.Ast: data DefaultLanguage
+ Cartel.Ast: data Section
+ Cartel.Ast: dataDir :: Properties -> String
+ Cartel.Ast: dataFiles :: Properties -> [NonEmptyString]
+ Cartel.Ast: description :: Properties -> [String]
+ Cartel.Ast: detailed :: TestSuiteField
+ Cartel.Ast: eq :: Version -> Constraint
+ Cartel.Ast: exactly :: NonEmptyString -> Version -> Package
+ Cartel.Ast: executable :: NonEmptyString -> [ExecutableField] -> Section
+ Cartel.Ast: exitcodeFields :: (BuildsExitcode a, BuildsExe a) => NonEmptyString -> [a]
+ Cartel.Ast: exitcodeStdio :: BuildsExitcode a => a
+ Cartel.Ast: exposed :: Bool -> LibraryField
+ Cartel.Ast: exposedModules :: [NonEmptyString] -> LibraryField
+ Cartel.Ast: extensions :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel.Ast: extraDocFiles :: Properties -> [NonEmptyString]
+ Cartel.Ast: extraLibDirs :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel.Ast: extraLibraries :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel.Ast: extraSourceFiles :: Properties -> [NonEmptyString]
+ Cartel.Ast: extraTmpFiles :: Properties -> [NonEmptyString]
+ Cartel.Ast: false :: Condition
+ Cartel.Ast: fileExtensions :: [String]
+ Cartel.Ast: flag :: FlagName -> Condition
+ Cartel.Ast: frameworks :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel.Ast: ghc :: Compiler
+ Cartel.Ast: ghcOptions :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel.Ast: ghcProfOptions :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel.Ast: ghcSharedOptions :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel.Ast: git :: Vcs
+ Cartel.Ast: githubHead :: NonEmptyString -> NonEmptyString -> Section
+ Cartel.Ast: gpl :: License
+ Cartel.Ast: gt :: Version -> Constraint
+ Cartel.Ast: gtEq :: Version -> Constraint
+ Cartel.Ast: haskell2010 :: HasBuildInfo a => a
+ Cartel.Ast: haskell98 :: HasBuildInfo a => a
+ Cartel.Ast: helium :: Compiler
+ Cartel.Ast: homepage :: Properties -> String
+ Cartel.Ast: hsSourceDirs :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel.Ast: hugs :: Compiler
+ Cartel.Ast: hugsOptions :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel.Ast: ifFalse :: CondBlock a -> [a]
+ Cartel.Ast: impl :: Compiler -> Constraint -> Condition
+ Cartel.Ast: includeDirs :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel.Ast: includes :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel.Ast: installIncludes :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel.Ast: instance Blank Cabal
+ Cartel.Ast: instance Blank Properties
+ Cartel.Ast: instance Blank Repository
+ Cartel.Ast: instance BuildsExe BenchmarkField
+ Cartel.Ast: instance BuildsExe ExecutableField
+ Cartel.Ast: instance BuildsExe TestSuiteField
+ Cartel.Ast: instance BuildsExitcode BenchmarkField
+ Cartel.Ast: instance BuildsExitcode TestSuiteField
+ Cartel.Ast: instance Eq BenchmarkType
+ Cartel.Ast: instance Eq CondLeaf
+ Cartel.Ast: instance Eq DefaultLanguage
+ Cartel.Ast: instance Eq Section
+ Cartel.Ast: instance HasBuildInfo BenchmarkField
+ Cartel.Ast: instance HasBuildInfo ExecutableField
+ Cartel.Ast: instance HasBuildInfo LibraryField
+ Cartel.Ast: instance HasBuildInfo TestSuiteField
+ Cartel.Ast: instance LogicTree Condition
+ Cartel.Ast: instance LogicTree ConstrTree
+ Cartel.Ast: instance LogicTree Constraint
+ Cartel.Ast: instance Ord BenchmarkType
+ Cartel.Ast: instance Ord CondLeaf
+ Cartel.Ast: instance Ord DefaultLanguage
+ Cartel.Ast: instance Ord Section
+ Cartel.Ast: instance Show BenchmarkType
+ Cartel.Ast: instance Show CondLeaf
+ Cartel.Ast: instance Show DefaultLanguage
+ Cartel.Ast: instance Show Section
+ Cartel.Ast: interestingDir :: FilePath -> Bool
+ Cartel.Ast: interestingFile :: [String] -> FilePath -> Bool
+ Cartel.Ast: invert :: Condition -> Condition
+ Cartel.Ast: jhc :: Compiler
+ Cartel.Ast: ldOptions :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel.Ast: lgpl :: License
+ Cartel.Ast: lhc :: Compiler
+ Cartel.Ast: library :: Cabal -> [LibraryField]
+ Cartel.Ast: license :: Properties -> Maybe License
+ Cartel.Ast: licenseFile :: Properties -> String
+ Cartel.Ast: licenseFiles :: Properties -> [NonEmptyString]
+ Cartel.Ast: lt :: Version -> Constraint
+ Cartel.Ast: ltEq :: Version -> Constraint
+ Cartel.Ast: mainIs :: BuildsExe a => NonEmptyString -> a
+ Cartel.Ast: maintainer :: Properties -> String
+ Cartel.Ast: make :: BuildType
+ Cartel.Ast: mercurial :: Vcs
+ Cartel.Ast: mit :: License
+ Cartel.Ast: modules :: MonadIO m => FilePath -> Betsy m [NonEmptyString]
+ Cartel.Ast: modulesIO :: FilePath -> IO [String]
+ Cartel.Ast: modulesInDir :: [String] -> FilePath -> [FilePath] -> IO [[String]]
+ Cartel.Ast: modulesWithExtensions :: MonadIO m => [NonEmptyString] -> FilePath -> Betsy m [NonEmptyString]
+ Cartel.Ast: modulesWithExtensionsIO :: [String] -> FilePath -> IO [String]
+ Cartel.Ast: monotone :: Vcs
+ Cartel.Ast: mpl :: License
+ Cartel.Ast: name :: Properties -> String
+ Cartel.Ast: nextBreaking :: NonEmptyString -> Version -> Package
+ Cartel.Ast: nextMajor :: NonEmptyString -> Version -> Package
+ Cartel.Ast: nhc :: Compiler
+ Cartel.Ast: nhc98Options :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel.Ast: otherLicense :: License
+ Cartel.Ast: otherModules :: HasBuildInfo a => [NonEmptyString] -> a
+ Cartel.Ast: package :: NonEmptyString -> Constraint -> Package
+ Cartel.Ast: packageUrl :: Properties -> String
+ Cartel.Ast: pkgConfigDepends :: HasBuildInfo a => [Package] -> a
+ Cartel.Ast: processFile :: [String] -> FilePath -> [FilePath] -> FilePath -> IO [[String]]
+ Cartel.Ast: properties :: Cabal -> Properties
+ Cartel.Ast: publicDomain :: License
+ Cartel.Ast: repoHead :: RepoKind
+ Cartel.Ast: repoThis :: RepoKind
+ Cartel.Ast: repository :: Repository -> Section
+ Cartel.Ast: sections :: Cabal -> [Section]
+ Cartel.Ast: simple :: BuildType
+ Cartel.Ast: sorter :: [String] -> [String] -> Ordering
+ Cartel.Ast: stability :: Properties -> String
+ Cartel.Ast: svn :: Vcs
+ Cartel.Ast: synopsis :: Properties -> String
+ Cartel.Ast: system :: NonEmptyString -> Condition
+ Cartel.Ast: testModule :: NonEmptyString -> TestSuiteField
+ Cartel.Ast: testSuite :: NonEmptyString -> [TestSuiteField] -> Section
+ Cartel.Ast: testSuiteFields :: TestSuite -> [TestSuiteField]
+ Cartel.Ast: testSuiteName :: TestSuite -> NonEmptyString
+ Cartel.Ast: testedWith :: Properties -> [(Compiler, Constraint)]
+ Cartel.Ast: true :: Condition
+ Cartel.Ast: type Version = [Word]
+ Cartel.Ast: unconstrained :: NonEmptyString -> Package
+ Cartel.Ast: version :: Properties -> Version
+ Cartel.Ast: yhc :: Compiler
+ Cartel.Betsy: DuplicateFlag :: FlagName -> Error
+ Cartel.Betsy: EmptyFlagName :: Error
+ Cartel.Betsy: Failed :: String -> Error
+ Cartel.Betsy: FlagOpts :: String -> Bool -> Bool -> FlagOpts
+ Cartel.Betsy: currentFlags :: Applicative f => Betsy f [(FlagName, FlagOpts)]
+ Cartel.Betsy: data Betsy m a
+ Cartel.Betsy: data Error
+ Cartel.Betsy: data FlagName
+ Cartel.Betsy: data FlagOpts
+ Cartel.Betsy: flagDefault :: FlagOpts -> Bool
+ Cartel.Betsy: flagDescription :: FlagOpts -> String
+ Cartel.Betsy: flagManual :: FlagOpts -> Bool
+ Cartel.Betsy: flagNameHead :: FlagName -> Char
+ Cartel.Betsy: flagNameTail :: FlagName -> String
+ Cartel.Betsy: makeFlag :: Applicative m => NonEmptyString -> FlagOpts -> Betsy m FlagName
+ Cartel.Betsy: runBetsy :: Functor m => Betsy m a -> m (Either Error (a, [(FlagName, FlagOpts)]))
+ Cartel.Betsy.Internal: Betsy :: (State -> m (Either Error (a, State))) -> Betsy m a
+ Cartel.Betsy.Internal: DuplicateFlag :: FlagName -> Error
+ Cartel.Betsy.Internal: EmptyFlagName :: Error
+ Cartel.Betsy.Internal: Failed :: String -> Error
+ Cartel.Betsy.Internal: FlagName :: Char -> String -> FlagName
+ Cartel.Betsy.Internal: FlagOpts :: String -> Bool -> Bool -> FlagOpts
+ Cartel.Betsy.Internal: State :: [(FlagName, FlagOpts)] -> State
+ Cartel.Betsy.Internal: currentFlags :: Applicative f => Betsy f [(FlagName, FlagOpts)]
+ Cartel.Betsy.Internal: data Error
+ Cartel.Betsy.Internal: data FlagName
+ Cartel.Betsy.Internal: data FlagOpts
+ Cartel.Betsy.Internal: data State
+ Cartel.Betsy.Internal: flagDefault :: FlagOpts -> Bool
+ Cartel.Betsy.Internal: flagDescription :: FlagOpts -> String
+ Cartel.Betsy.Internal: flagManual :: FlagOpts -> Bool
+ Cartel.Betsy.Internal: flagNameHead :: FlagName -> Char
+ Cartel.Betsy.Internal: flagNameTail :: FlagName -> String
+ Cartel.Betsy.Internal: instance (Monad m, Functor m) => Applicative (Betsy m)
+ Cartel.Betsy.Internal: instance Eq Error
+ Cartel.Betsy.Internal: instance Eq FlagName
+ Cartel.Betsy.Internal: instance Eq FlagOpts
+ Cartel.Betsy.Internal: instance Eq State
+ Cartel.Betsy.Internal: instance Functor m => Functor (Betsy m)
+ Cartel.Betsy.Internal: instance Monad m => Monad (Betsy m)
+ Cartel.Betsy.Internal: instance MonadIO m => MonadIO (Betsy m)
+ Cartel.Betsy.Internal: instance MonadTrans Betsy
+ Cartel.Betsy.Internal: instance Ord Error
+ Cartel.Betsy.Internal: instance Ord FlagName
+ Cartel.Betsy.Internal: instance Ord FlagOpts
+ Cartel.Betsy.Internal: instance Ord State
+ Cartel.Betsy.Internal: instance Show Error
+ Cartel.Betsy.Internal: instance Show FlagName
+ Cartel.Betsy.Internal: instance Show FlagOpts
+ Cartel.Betsy.Internal: instance Show State
+ Cartel.Betsy.Internal: makeFlag :: Applicative m => NonEmptyString -> FlagOpts -> Betsy m FlagName
+ Cartel.Betsy.Internal: newtype Betsy m a
+ Cartel.Betsy.Internal: renderError :: Error -> String
+ Cartel.Betsy.Internal: runBetsy :: Functor m => Betsy m a -> m (Either Error (a, [(FlagName, FlagOpts)]))
+ Cartel.GenCartelCabal: base :: Package
+ Cartel.GenCartelCabal: commonOptions :: HasBuildInfo a => [a]
+ Cartel.GenCartelCabal: directory :: Package
+ Cartel.GenCartelCabal: filepath :: Package
+ Cartel.GenCartelCabal: libDepends :: [Package]
+ Cartel.GenCartelCabal: main :: IO ()
+ Cartel.GenCartelCabal: multiarg :: Package
+ Cartel.GenCartelCabal: quickcheck :: Package
+ Cartel.GenCartelCabal: quickpull :: Package
+ Cartel.GenCartelCabal: random :: Package
+ Cartel.GenCartelCabal: testDepends :: [Package]
+ Cartel.GenCartelCabal: testOptions :: HasBuildInfo a => [a]
+ Cartel.GenCartelCabal: time :: Package
+ Cartel.GenCartelCabal: transformers :: Package
+ Cartel.Render: Description :: [String] -> Description
+ Cartel.Render: class Renderable a
+ Cartel.Render: class RenderableIndented a
+ Cartel.Render: instance Eq Description
+ Cartel.Render: instance Ord Description
+ Cartel.Render: instance Renderable (Compiler, Constraint)
+ Cartel.Render: instance Renderable (Word, Word)
+ Cartel.Render: instance Renderable BenchmarkType
+ Cartel.Render: instance Renderable Bool
+ Cartel.Render: instance Renderable BuildType
+ Cartel.Render: instance Renderable Compiler
+ Cartel.Render: instance Renderable CondLeaf
+ Cartel.Render: instance Renderable Condition
+ Cartel.Render: instance Renderable ConstrTree
+ Cartel.Render: instance Renderable Constraint
+ Cartel.Render: instance Renderable DefaultLanguage
+ Cartel.Render: instance Renderable FlagName
+ Cartel.Render: instance Renderable License
+ Cartel.Render: instance Renderable Logical
+ Cartel.Render: instance Renderable Ordering
+ Cartel.Render: instance Renderable Package
+ Cartel.Render: instance Renderable RepoKind
+ Cartel.Render: instance Renderable String
+ Cartel.Render: instance Renderable TestSuiteType
+ Cartel.Render: instance Renderable Vcs
+ Cartel.Render: instance Renderable Version
+ Cartel.Render: instance Renderable a => Renderable (Maybe a)
+ Cartel.Render: instance RenderableIndented (FlagName, FlagOpts)
+ Cartel.Render: instance RenderableIndented Benchmark
+ Cartel.Render: instance RenderableIndented BenchmarkField
+ Cartel.Render: instance RenderableIndented BuildInfoField
+ Cartel.Render: instance RenderableIndented Cabal
+ Cartel.Render: instance RenderableIndented Description
+ Cartel.Render: instance RenderableIndented Error
+ Cartel.Render: instance RenderableIndented Executable
+ Cartel.Render: instance RenderableIndented ExecutableField
+ Cartel.Render: instance RenderableIndented LibraryField
+ Cartel.Render: instance RenderableIndented Properties
+ Cartel.Render: instance RenderableIndented Repository
+ Cartel.Render: instance RenderableIndented Section
+ Cartel.Render: instance RenderableIndented TestSuite
+ Cartel.Render: instance RenderableIndented TestSuiteField
+ Cartel.Render: instance RenderableIndented [(FlagName, FlagOpts)]
+ Cartel.Render: instance RenderableIndented a => RenderableIndented (CondBlock a)
+ Cartel.Render: instance Show Description
+ Cartel.Render: newtype Description
+ Cartel.Render: renLibrary :: Int -> [LibraryField] -> String
+ Cartel.Render: render :: Renderable a => a -> String
+ Cartel.Render: renderIndented :: RenderableIndented a => Int -> a -> String
+ Cartel.Types: type NonEmptyString = String
+ Cartel.Version: version :: [Word]
- Cartel.Ast: Arch :: String -> Condition
+ Cartel.Ast: Arch :: NonEmptyString -> CondLeaf
- Cartel.Ast: Benchmark :: String -> String -> [BenchmarkField] -> Benchmark
+ Cartel.Ast: Benchmark :: NonEmptyString -> [BenchmarkField] -> Benchmark
- Cartel.Ast: CBranch :: Logical -> CondTree -> CondTree -> CondTree
+ Cartel.Ast: CBranch :: Logical -> Condition -> Condition -> Condition
- Cartel.Ast: CCOptions :: [String] -> BuildInfoField
+ Cartel.Ast: CCOptions :: [NonEmptyString] -> BuildInfoField
- Cartel.Ast: CFalse :: Condition
+ Cartel.Ast: CFalse :: CondLeaf
- Cartel.Ast: CFlag :: String -> Condition
+ Cartel.Ast: CFlag :: FlagName -> CondLeaf
- Cartel.Ast: CLeaf :: Condition -> CondTree
+ Cartel.Ast: CLeaf :: CondLeaf -> Condition
- Cartel.Ast: CNegate :: CondTree -> CondTree
+ Cartel.Ast: CNegate :: Condition -> Condition
- Cartel.Ast: CPPOptions :: [String] -> BuildInfoField
+ Cartel.Ast: CPPOptions :: [NonEmptyString] -> BuildInfoField
- Cartel.Ast: CSources :: [String] -> BuildInfoField
+ Cartel.Ast: CSources :: [NonEmptyString] -> BuildInfoField
- Cartel.Ast: CTrue :: Condition
+ Cartel.Ast: CTrue :: CondLeaf
- Cartel.Ast: Cabal :: Properties -> [Repository] -> [Flag] -> Maybe Library -> [Executable] -> [TestSuite] -> [Benchmark] -> Cabal
+ Cartel.Ast: Cabal :: Properties -> [LibraryField] -> [Section] -> Cabal
- Cartel.Ast: CondBlock :: CondTree -> [a] -> [a] -> CondBlock a
+ Cartel.Ast: CondBlock :: Condition -> (a, [a]) -> [a] -> CondBlock a
- Cartel.Ast: Cvs :: String -> Vcs
+ Cartel.Ast: Cvs :: NonEmptyString -> Vcs
- Cartel.Ast: DefaultLanguage :: Language -> BuildInfoField
+ Cartel.Ast: DefaultLanguage :: DefaultLanguage -> BuildInfoField
- Cartel.Ast: ExeMainIs :: String -> ExecutableField
+ Cartel.Ast: ExeMainIs :: NonEmptyString -> ExecutableField
- Cartel.Ast: Executable :: String -> [ExecutableField] -> Executable
+ Cartel.Ast: Executable :: NonEmptyString -> [ExecutableField] -> Executable
- Cartel.Ast: Extensions :: [String] -> BuildInfoField
+ Cartel.Ast: Extensions :: [NonEmptyString] -> BuildInfoField
- Cartel.Ast: ExtraLibDirs :: [String] -> BuildInfoField
+ Cartel.Ast: ExtraLibDirs :: [NonEmptyString] -> BuildInfoField
- Cartel.Ast: ExtraLibraries :: [String] -> BuildInfoField
+ Cartel.Ast: ExtraLibraries :: [NonEmptyString] -> BuildInfoField
- Cartel.Ast: Frameworks :: [String] -> BuildInfoField
+ Cartel.Ast: Frameworks :: [NonEmptyString] -> BuildInfoField
- Cartel.Ast: GHCOptions :: [String] -> BuildInfoField
+ Cartel.Ast: GHCOptions :: [NonEmptyString] -> BuildInfoField
- Cartel.Ast: GHCProfOptions :: [String] -> BuildInfoField
+ Cartel.Ast: GHCProfOptions :: [NonEmptyString] -> BuildInfoField
- Cartel.Ast: GHCSharedOptions :: [String] -> BuildInfoField
+ Cartel.Ast: GHCSharedOptions :: [NonEmptyString] -> BuildInfoField
- Cartel.Ast: Haskell2010 :: Language
+ Cartel.Ast: Haskell2010 :: DefaultLanguage
- Cartel.Ast: Haskell98 :: Language
+ Cartel.Ast: Haskell98 :: DefaultLanguage
- Cartel.Ast: HsSourceDirs :: [String] -> BuildInfoField
+ Cartel.Ast: HsSourceDirs :: [NonEmptyString] -> BuildInfoField
- Cartel.Ast: HugsOptions :: [String] -> BuildInfoField
+ Cartel.Ast: HugsOptions :: [NonEmptyString] -> BuildInfoField
- Cartel.Ast: Impl :: (Compiler, Maybe ConstrTree) -> Condition
+ Cartel.Ast: Impl :: Compiler -> Constraint -> CondLeaf
- Cartel.Ast: IncludeDirs :: [String] -> BuildInfoField
+ Cartel.Ast: IncludeDirs :: [NonEmptyString] -> BuildInfoField
- Cartel.Ast: Includes :: [String] -> BuildInfoField
+ Cartel.Ast: Includes :: [NonEmptyString] -> BuildInfoField
- Cartel.Ast: InstallIncludes :: [String] -> BuildInfoField
+ Cartel.Ast: InstallIncludes :: [NonEmptyString] -> BuildInfoField
- Cartel.Ast: LDOptions :: [String] -> BuildInfoField
+ Cartel.Ast: LDOptions :: [NonEmptyString] -> BuildInfoField
- Cartel.Ast: Leaf :: Constraint -> ConstrTree
+ Cartel.Ast: Leaf :: Ordering -> Version -> ConstrTree
- Cartel.Ast: Nhc98Options :: [String] -> BuildInfoField
+ Cartel.Ast: Nhc98Options :: [NonEmptyString] -> BuildInfoField
- Cartel.Ast: OS :: String -> Condition
+ Cartel.Ast: OS :: NonEmptyString -> CondLeaf
- Cartel.Ast: OtherModules :: [String] -> BuildInfoField
+ Cartel.Ast: OtherModules :: [NonEmptyString] -> BuildInfoField
- Cartel.Ast: Package :: String -> Maybe ConstrTree -> Package
+ Cartel.Ast: Package :: NonEmptyString -> Constraint -> Package
- Cartel.Ast: Properties :: String -> Version -> (Int, Int) -> BuildType -> License -> String -> [String] -> String -> String -> String -> String -> String -> String -> String -> String -> [String] -> String -> [(Compiler, ConstrTree)] -> [String] -> String -> [String] -> [String] -> [String] -> Properties
+ Cartel.Ast: Properties :: String -> Version -> Maybe (Word, Word) -> Maybe BuildType -> Maybe License -> String -> [NonEmptyString] -> String -> String -> String -> String -> String -> String -> String -> String -> [String] -> String -> [(Compiler, Constraint)] -> [NonEmptyString] -> String -> [NonEmptyString] -> [NonEmptyString] -> [NonEmptyString] -> Properties
- Cartel.Ast: Repository :: Vcs -> RepoKind -> String -> String -> String -> String -> Repository
+ Cartel.Ast: Repository :: Maybe Vcs -> Maybe RepoKind -> String -> NonEmptyString -> String -> String -> Repository
- Cartel.Ast: TestMainIs :: String -> TestSuiteField
+ Cartel.Ast: TestMainIs :: NonEmptyString -> TestSuiteField
- Cartel.Ast: TestModule :: String -> TestSuiteField
+ Cartel.Ast: TestModule :: NonEmptyString -> TestSuiteField
- Cartel.Ast: TestSuite :: String -> [TestSuiteField] -> TestSuite
+ Cartel.Ast: TestSuite :: NonEmptyString -> [TestSuiteField] -> TestSuite
- Cartel.Ast: condIf :: CondBlock a -> CondTree
+ Cartel.Ast: condIf :: CondBlock a -> Condition
- Cartel.Ast: exeName :: Executable -> String
+ Cartel.Ast: exeName :: Executable -> NonEmptyString
- Cartel.Ast: ifTrue :: CondBlock a -> [a]
+ Cartel.Ast: ifTrue :: CondBlock a -> (a, [a])
- Cartel.Ast: repoKind :: Repository -> RepoKind
+ Cartel.Ast: repoKind :: Repository -> Maybe RepoKind
- Cartel.Ast: repoLocation :: Repository -> String
+ Cartel.Ast: repoLocation :: Repository -> NonEmptyString
- Cartel.Ast: repoVcs :: Repository -> Vcs
+ Cartel.Ast: repoVcs :: Repository -> Maybe Vcs
- Cartel.Render: indentList :: Int -> [String] -> String
+ Cartel.Render: indentList :: Renderable a => Int -> [a] -> String
- Cartel.Render: labeled :: Int -> String -> String -> String
+ Cartel.Render: labeled :: Renderable a => Int -> String -> a -> String
- Cartel.Render: labeledList :: Int -> String -> [String] -> String
+ Cartel.Render: labeledList :: Renderable a => Int -> String -> [a] -> String
Files
- LICENSE +1/−1
- README.md +7/−3
- cartel.cabal +87/−23
- current-versions.txt +0/−38
- genCabal.hs +0/−123
- lib/Cartel.hs +321/−31
- lib/Cartel/Ast.hs +1209/−577
- lib/Cartel/Betsy.hs +15/−0
- lib/Cartel/Betsy/Internal.hs +132/−0
- lib/Cartel/Empty.hs +0/−79
- lib/Cartel/GenCartelCabal.hs +117/−0
- lib/Cartel/Render.hs +301/−253
- lib/Cartel/Tools.hs +0/−519
- lib/Cartel/Types.hs +13/−0
- lib/Cartel/Version.hs +14/−0
- minimum-versions.txt +0/−38
- tests/Cartel/Generators.hs +373/−0
- tests/Cartel/Properties.hs +83/−0
- tests/Decrees.hs +26/−0
- tests/cartel-properties.hs +7/−0
- tests/cartel-visual-test.hs +81/−0
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2014, Omari Norman+Copyright (c) 2014 - 2015, Omari Norman All rights reserved.
README.md view
@@ -11,7 +11,11 @@ http://www.github.com/massysett/cartel -## Testing+## Test results -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.+[](https://travis-ci.org/massysett/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.
cartel.cabal view
@@ -2,54 +2,118 @@ -- Cartel is available at: -- http://www.github.com/massysett/cartel ----- Script name used to generate: genCabal.hs--- Generated on: 2014-05-30 14:59:50.352284 EDT--- Cartel library version: 0.10.0.0+-- Script name used to generate: GenCartelCabal.hs+-- Generated on: 2015-01-17 18:01:46.377337 EST+-- Cartel library version: 0.12.0.0 name: cartel-version: 0.10.0.2-cabal-version: >= 1.14-build-type: Simple+version: 0.12.0.0+cabal-version: >= 1.16 license: BSD3 license-file: LICENSE-copyright: 2014 Omari Norman+build-type: Simple+copyright: Copyright 2014-2015 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+bug-reports: http://www.github.com/massysett/cartel/issues+synopsis: Specify 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.+ 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. category: Distribution-tested-with: GHC == 7.4.1, GHC == 7.6.3, GHC == 7.8.2+tested-with:+ 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.Empty+ , Cartel.Betsy+ , Cartel.Betsy.Internal+ , Cartel.GenCartelCabal , Cartel.Render- , Cartel.Tools- exposed: True+ , Cartel.Types+ , Cartel.Version+ default-language: Haskell2010+ ghc-options:+ -Wall+ hs-source-dirs:+ lib 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)+ , time ((> 1.4 || == 1.4) && < 1.6)+ , transformers ((> 0.4 || == 0.4) && < 0.5)++source-repository head+ type: git+ location: https://github.com/massysett/cartel.git++Executable cartel-visual-test+ if flag(visual)+ buildable: True+ other-modules:+ Cartel.Generators+ , Cartel.Properties+ , Decrees+ hs-source-dirs:+ tests+ build-depends:+ multiarg ((> 0.30.0.0 || == 0.30.0.0) && < 0.31)+ , QuickCheck ((> 2.7 || == 2.7) && < 2.8)+ , random ((> 1.1 || == 1.1) && < 1.2)+ , quickpull ((> 0.4.0.0 || == 0.4.0.0) && < 0.5)+ ghc-options:+ -Wall+ hs-source-dirs:+ lib+ 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.6)+ , transformers ((> 0.4 || == 0.4) && < 0.5)+ else+ buildable: False+ default-language: Haskell2010+ main-is: cartel-visual-test.hs++Test-Suite cartel-properties+ default-language: Haskell2010 other-modules:- Paths_cartel- buildable: True+ Cartel.Generators+ , Cartel.Properties+ , Decrees+ hs-source-dirs:+ tests+ build-depends:+ multiarg ((> 0.30.0.0 || == 0.30.0.0) && < 0.31)+ , QuickCheck ((> 2.7 || == 2.7) && < 2.8)+ , random ((> 1.1 || == 1.1) && < 1.2)+ , quickpull ((> 0.4.0.0 || == 0.4.0.0) && < 0.5) ghc-options: -Wall- default-language: Haskell2010 hs-source-dirs: lib+ 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.6)+ , transformers ((> 0.4 || == 0.4) && < 0.5)+ type: exitcode-stdio-1.0+ main-is: cartel-properties.hs++Flag visual+ description: Build cartel-visual-test executable.+ default: False+ manual: True
− current-versions.txt
@@ -1,38 +0,0 @@-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-30 16:15:53.772569 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-11214/db:- cartel-0.10.0.0-
− genCabal.hs
@@ -1,123 +0,0 @@--- | 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.10.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.empty- { A.prName = "cartel"- , A.prVersion = A.Version [0,10,0,2]- , 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.empty- { 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
@@ -16,42 +16,332 @@ -- 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.+-- A disadvantage to Cartel is that is 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 the extent possible, 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+-- but a library. However, Cartel does not prevent against all+-- errors. For example, Cartel does nothing to prevent you from+-- applying a function that calls for a 'NonEmptyString' to a string+-- that is, in fact, empty. Another example is that Cabal requires+-- executables to have a @main-is@ field, but Cartel does not force+-- you to include one. Ultimately your Cabal file might still have+-- errors that you have to fix by changing the program that generates+-- the file. ----- 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.+-- 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. ----- 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".+-- Hopefully this module's documentation is organized so that+-- top-to-bottom reading will make sense. module Cartel- ( module Cartel.Ast- , module Cartel.Empty- , module Cartel.Tools+ ( -- * Basic types and classes+ Word+ , NonEmptyString+ , Blank(..)+ , Version++ -- * Sections+ , Section++ -- * Repositories+ -- ** Version control systems+ , Vcs+ , darcs, git, svn, mercurial, bazaar, archVcs, monotone, cvs++ -- ** Repository kinds+ , RepoKind+ , repoHead, repoThis+ -- ** Building repositories+ , Repository(..)+ , githubHead+ , repository++ -- * Logicals+ , LogicTree(..)+ , invert++ -- ** Constraints+ , Constraint+ , lt+ , gt+ , eq+ , ltEq+ , gtEq+ , anyVersion++ -- ** Conditionals+ , Compiler+ , ghc, nhc, yhc, hugs, helium, jhc, lhc+ , Condition+ , system+ , arch+ , impl+ , flag+ , true+ , false+ , condBlock++ -- * Packages+ , Package+ , package++ -- ** Package helpers+ , closedOpen+ , apiVersion+ , nextBreaking+ , nextMajor+ , exactly+ , unconstrained++ -- * Build information++ -- | Libraries, executables, test suites, and benchmarks all share+ -- common fields for build information. 'BuildInfoField' represents+ -- these common fields, and 'HasBuildInfo' is a typeclass encompassing+ -- libraries, executables, test suites, and benchmarks. You can+ -- build these fields for any of these sections using the functions+ -- and values listed here.+ , BuildInfoField+ , HasBuildInfo+ , haskell98+ , haskell2010+ , buildDepends+ , otherModules+ , hsSourceDirs+ , extensions+ , buildTools+ , buildable+ , ghcOptions+ , ghcProfOptions+ , ghcSharedOptions+ , hugsOptions+ , nhc98Options+ , includes+ , installIncludes+ , includeDirs+ , cSources+ , extraLibraries+ , extraLibDirs+ , ccOptions+ , cppOptions+ , ldOptions+ , pkgConfigDepends+ , frameworks++ -- * BuildsExe+ --+ -- | Benchmarks, test suites, and executables have common fields;+ -- the 'BuildsExe' class captures these.+ , BuildsExe(..)++ -- * Exitcode executables+ --+ -- | Test suites and benchmarks are capable of building things of+ -- type @exitcode-stdio-1.0@; the 'BuildsExitcode' class reflects+ -- this.+ , BuildsExitcode(..)+ , exitcodeFields++ -- * Betsy+ , Betsy++ -- ** Flags+ , FlagName+ , FlagOpts(..)+ , makeFlag+ , currentFlags++ -- * Libraries+ --+ -- | A library consists of one or more 'LibraryField's. Typically+ -- you will return these fields inside of the 'Betsy' type through+ -- the 'defaultMain' function. To build a 'LibraryField', you will+ -- mostly use the bindings in the \"Build Information\" section of+ -- this module. You will also need 'exposedModules' and you might+ -- use 'exposed' and 'condBlock'.+ , LibraryField+ , exposed+ , exposedModules++ -- * Executables+ --+ -- | An executable consists of one more more 'ExecutableField's.+ -- You build an executable by passing one or more 'ExecutableField's+ -- to the 'executable' function. To get an 'ExecutableField', you+ -- will mostly use the bindings in the \"Build Information\" section+ -- of this module, as well as 'mainIs'. You might also need+ -- 'condBlock'.+ , ExecutableField+ , executable++ -- * Test suites+ --+ -- | A test suite consists of one more more 'TestSuiteField's. You+ -- build an test suite by passing one or more 'TestSuiteField's to+ -- the 'testSuite' function. To get a 'TestSuiteField', you will+ -- mostly use the bindings in the \"Build Information\" section of+ -- this module. You might also need the 'testModule',+ -- 'exitcodeStdio', 'mainIs', 'detailed', 'condBlock', and+ -- 'exitcodeFields' bindings.+ , detailed+ , TestSuiteField+ , testModule+ , testSuite++ -- * Benchmarks+ -- + -- | A benchmark consists of one more more 'BenchmarkField's. You+ -- build an benchmark by passing one or more 'BenchmarkField's to+ -- the 'benchmark' function. To get an 'BenchmarkField', you will+ -- mostly use the bindings in the \"Build Information\" section of+ -- this module. You might also need the 'exitcodeStdio',+ -- 'exitcodeFields', and 'condBlock' bindings.+ , BenchmarkField+ , benchmark++ -- * Getting module lists+ , fileExtensions+ , modulesWithExtensions+ , modules++ -- * Properties+ -- ** Build types+ , BuildType+ , simple, configure, make, custom+ -- ** Licenses+ , License+ , gpl, agpl, lgpl, bsd2, bsd3, bsd4, mit, mpl, apache, publicDomain+ , allRightsReserved, otherLicense+ , Properties(..)++ -- * Cabal file+ --+ -- | Usually you will not need this type, as 'defaultMain' produces+ -- a 'Cabal' value for you, but you will need it if you want to use+ -- 'bestyToCabalString'.+ , Cabal+ , cabal++ -- * Generating Cabal files+ -- ** defaultMain - usually all you need+ , defaultMain++ -- ** Other bindings - for more unusual uses+ , Error(..)+ , Renderable(..)+ , RenderableIndented(..)+ , renderBetsy+ , betsyToCabalStringIO+ ) where +-- # begin imports+ import Cartel.Ast-import Cartel.Empty-import Cartel.Tools+import Cartel.Render+import qualified System.IO as IO+import Data.Time+import qualified Cartel.Version+import Data.List (intersperse)+import System.Exit (exitFailure, exitSuccess)+import System.Environment (getProgName)+import Data.Word+import Control.Monad.IO.Class+import Cartel.Betsy+import Cartel.Types++-- # end imports++-- | Pure function to obtain Cabal text.+renderBetsy+ :: Functor m+ => Betsy m Cabal+ -> m (Either Error String)+ -- ^ Returns either an error message or the generated Cabal text.+renderBetsy = fmap k . runBetsy+ where+ k ei = case ei of+ Left e -> Left e+ Right (cbl, fls) -> Right+ . vsep+ $ [ renderIndented 0 cbl+ , renderIndented 0 fls ]++-- | Renders a header for the Cabal file.+header+ :: String+ -- ^ Name of program used+ -> ZonedTime+ -- ^ When this output is being created+ -> String+header nm zt = hdr+ 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+ $ Cartel.Version.version+++-- | Like 'renderBetsy' but also uses 'IO' to prepend a header+-- that has the name of the program that is running this function, and+-- the current date and time.+betsyToCabalStringIO+ :: (MonadIO m, Functor m)+ => Betsy m Cabal+ -> m (Either Error String)+betsyToCabalStringIO wg = do+ ei <- renderBetsy wg+ case ei of+ Left e -> return $ Left e+ Right cbl -> do+ pn <- liftIO getProgName+ zt <- liftIO getZonedTime+ return . Right $ (header pn zt) ++ cbl+++-- | Generates a Cabal file. If you have no library, just leave the+-- list of 'LibraryField' empty. Include any and all executables,+-- test suites, benchmarks, and repositories in the list of 'Section'.+-- Ensures that the generated Cabal file also includes any flags you+-- made with 'makeFlag'. If there is an error (such as a duplicate+-- flag) an error message is printed to standard error and the program+-- invokes 'exitFailure'; otherwise, the generated Cabal file is+-- printed to standard output and the program invokes 'exitSuccess'.+defaultMain+ :: Betsy IO (Properties, [LibraryField], [Section])+ -> IO ()+defaultMain wg = do+ let conv (p, ls, ss) = Cabal p ls ss+ ei <- betsyToCabalStringIO (fmap conv wg)+ case ei of+ Left e -> do+ IO.hPutStrLn IO.stderr . renderIndented 0 $ e+ exitFailure+ Right str -> do+ IO.hSetBinaryMode IO.stdout False+ IO.hSetEncoding IO.stdout IO.utf8+ putStr str+ exitSuccess
lib/Cartel/Ast.hs view
@@ -1,577 +1,1209 @@--- | 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- (- -- * Constraint trees- Logical(..)- , Version(..)- , Constraint(..)- , ConstrTree(..)-- -- * Global package description- , BuildType(..)- , License(..)- , Compiler(..)- , Properties(..)-- -- * Repositories- , RepoKind(..)- , Vcs(..)- , Repository(..)-- -- * Flags- , Flag(..)-- -- * Build information- , Package(..)- , Language(..)- , BuildInfoField(..)-- -- * Conditionals- , Condition(..)- , CondTree(..)- , CondBlock(..)-- -- * Libraries- , LibraryField(..)- , Library(..)-- -- * Executables- , ExecutableField(..)- , Executable(..)-- -- * Test suites- , TestSuiteType(..)- , TestSuiteField(..)- , TestSuite(..)-- -- * Benchmarks- , BenchmarkField(..)- , Benchmark(..)-- -- * Cabal- , Cabal(..)-- ) 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.Empty.empty', 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- -- ^ An @exitcode-stdio-1.0@ test. The @String@ is the name of- -- the file containing the executable code. In this case, the- -- 'TestMainIs' field is required.-- | Detailed- -- ^ The @detailed-1.0@ test. In this case, the 'TestModule'- -- field is required.- 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-- | TestMainIs String- -- ^ The name of the @.hs@ or @.lhs@ file containing the @Main@- -- module. The @.hs@ filename must be listed, even if that file- -- is generated with a preprocessor. The file must be relatie to- -- one of the directories listed in 'HsSourceDirs'. This field- -- is required when using 'ExitcodeStdio' and disallowed when- -- using 'Detailed'.-- | TestModule String- -- ^ The module exporting the @tests@ symbol. This field is- -- required when using 'Detailed' and disallowed when using- -- 'ExitcodeStdio'.-- | 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.-- | CPPOptions [String]- -- ^ C Preprocessor options. Undocumented, see- -- <https://github.com/haskell/cabal/issues/646>-- | 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)--instance Functor CondBlock where- fmap f (CondBlock t ts es) = CondBlock t (map f ts) (map f es)---- | What kind of VCS repository is this?-data RepoKind-- = Head- -- ^ The latest development branch of the repository-- | This- -- ^ The sources for this release of this package.- 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- -- ^ Specifies the version control system in use.-- , repoKind :: RepoKind- -- ^ Whether this is a @head@, which refers to the latest- -- development branch of the package, or a @this@, which contains- -- the sources for this release of the package.-- , repoLocation :: String- -- ^ A URL giving the location of the repository. Will vary by- -- repository type; for git, you may use a @git://@ URL, for- -- example. This field is required.-- , repoBranch :: String- -- ^ For example, CVS, SVN, and git can have multiple branches in- -- a single repository; darcs cannot. If you need to specify a- -- branch, do it here. This field is optional.-- , repoTag :: String- -- ^ A repository tag. It is required for the @this@ repo kind.- -- It is optional for the @head@ repo kind. The tag will often be- -- a version number of some kind and should point the user to the- -- sources in the repo that correspond to a particular package- -- version.-- , repoSubdir :: String- -- ^ Some projects put the sources for multiple packages under a- -- single source repository. This field lets you specify the- -- relative path from the root of the repository to the top- -- directory for the package, ie the directory containing the- -- package’s .cabal file.- --- -- This field is optional. It defaults to empty which corresponds- -- to the root directory of the repository.-- } deriving (Eq, Ord, Show)---- | A single flag.-data Flag = Flag- { flName :: String- -- ^ The name of the flag; other parts of the Cabal file refer to- -- the flag using this name.-- , flDescription :: String- -- ^ A description of the flag (typically one line long.)-- , flDefault :: Bool- -- ^ Default value of this flag.-- , flManual :: Bool- -- ^ If True, then Cabal will not change this flag's value as it- -- tries to build the package. If False, then Cabal will first- -- try to satisfy dependencies with the default flag value and- -- then, if that is not possible, with the negated value.-- } deriving (Eq, Ord, Show)---- | Data for the entire Cabal file.-data Cabal = Cabal- { cProperties :: Properties- , cRepositories :: [Repository]- , cFlags :: [Flag]- , cLibrary :: Maybe Library- , cExecutables :: [Executable]- , cTestSuites :: [TestSuite]- , cBenchmarks :: [Benchmark]- } deriving (Eq, Ord, Show)+-- | The Cartel abstract syntax tree+--+-- Use this module if you want access to the data constructors in the+-- AST; the functions and values exported through "Cartel" should be+-- enough for most uses. Use of this module will not violate any+-- invariants; this stuff is in a separate module merely for the sake+-- of tidiness of the documentation in "Cartel".+--+-- 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.+module Cartel.Ast where++import qualified Data.Char as C+import qualified System.FilePath as P+import Data.Word+import System.FilePath+import Data.List (sortBy, intersperse)+import Data.Monoid+import qualified System.Directory as D+import Control.Monad.IO.Class+import Cartel.Types+import Cartel.Betsy++-- * Basic types++-- * Blank++-- | Typeclass for things that can be blank. More specifically,+-- @blank a@ results in an item that, when rendered in a Cabal file,+-- is the null string. 'blank' can be useful to indicate that you+-- have no options, and you can also use it in combination with record+-- syntax when you want to specify just a few options.++class Blank a where+ blank :: a++-- * Repositories++-- | What kind of VCS repository is this?+data RepoKind++ = Head+ -- ^ The latest development branch of the repository++ | This+ -- ^ The sources for this release of this package.+ deriving (Eq, Ord, Show)++repoHead :: RepoKind+repoHead = Head++repoThis :: RepoKind+repoThis = This++-- | Version control systems.+data Vcs+ = Darcs+ | Git+ | Svn+ | Cvs NonEmptyString+ -- ^ The argument 'String' is the named module+ | Mercurial+ | Bazaar+ | ArchVcs+ | Monotone+ deriving (Eq, Ord, Show)++darcs, git, svn, mercurial, bazaar, archVcs, monotone :: Vcs+cvs+ :: NonEmptyString+ -- ^ The named module+ -> Vcs+darcs = Darcs+git = Git+svn = Svn+cvs = Cvs+mercurial = Mercurial+bazaar = Bazaar+archVcs = ArchVcs+monotone = Monotone++-- | A single @repository@ section.+data Repository = Repository+ { repoVcs :: Maybe Vcs+ -- ^ What kind of 'Vcs' this is. This is required.++ , repoKind :: Maybe RepoKind+ -- ^ The kind of repository ('repoHead' or 'repoThis'). Required.++ , repoTag :: String+ -- ^ Repository tag. This is required for the 'repoThis' repository+ -- kind and is optional for the 'repoHead' repository kind.++ , repoLocation :: NonEmptyString+ -- ^ URL for the location of the repository--for example, for a+ -- @darcs@ repo this might be @http:\/\/code.haskell.org\/foo\/@; for+ -- git, this might be @git:\/\/github.com\/foo\/bar.git@.++ , repoBranch :: String+ -- ^ The repository branch.++ , repoSubdir :: String+ -- ^ The repository subdirectory.+ } deriving (Eq, Ord, Show)++instance Blank Repository where+ blank = Repository Nothing Nothing "" "" "" ""++-- | Creates a 'Section' that is a 'Repository' for a Github head.+-- For example, for Cartel, use @githubHead \"massysett\" \"cartel\"@.+githubHead+ :: NonEmptyString+ -- ^ The Github account name+ -> NonEmptyString+ -- ^ The Github project name within the account+ -> Section+githubHead acct proj+ = SecRepo $ blank { repoVcs = Just Git+ , repoKind = Just Head+ , repoLocation = loc+ }+ where+ loc = "https://github.com/" ++ acct ++ "/" ++ proj ++ ".git"++-- | Creates a 'Section' for a repository.+repository :: Repository -> Section+repository = SecRepo++-- * Logicals++-- | Condition; for use in a 'CondTree' and ultimately in a+-- 'CondBlock', which implements Cabal's @if-then-else@ feature.+data CondLeaf+ = OS NonEmptyString+ -- ^ Tests if the current operating system is the given name,+ -- case-insensitive+ | Arch NonEmptyString+ -- ^ Tests if the current architecture is the given name,+ -- case-insensitive+ | Impl Compiler Constraint+ -- ^ Tests for the configured Haskell implementation+ | CFlag FlagName+ -- ^ Evalutes to the current assignment of the flag of the given+ -- name. To get a flag, use 'makeFlag'.+ | CTrue+ -- ^ Always True+ | CFalse+ -- ^ Always False+ deriving (Eq, Ord, Show)++-- | For use in a 'CondTree' or 'ConstrTree'.+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 (Leaf (Constraint LT one)) (Leaf (Constraint EQ one))+data ConstrTree+ = Leaf Ordering Version+ | Branch Logical ConstrTree ConstrTree+ deriving (Eq, Ord, Show)+++-- | Expresses any version constraint, including no version+-- constraint.+data Constraint+ = AnyVersion+ | Constrained ConstrTree+ deriving (Eq, Ord, Show)++-- | Conditions. Ultimately these are used in a 'CondBlock'.+data Condition+ = CLeaf CondLeaf+ | CBranch Logical Condition Condition+ | CNegate Condition+ deriving (Eq, Ord, Show)++-- | Like 'not', which is what I would have named it but for the+-- conflict. Only 'Condition's have this sort of operation; Cabal+-- does not have a (documented, at least) way to express this for+-- package constraints.+invert :: Condition -> Condition+invert = CNegate++-- | Conditional blocks. These implement the @if-then-else@ feature+-- of Cabal files. You must have at least one thing to do if the+-- condition is True; the if-false block is optional.+data CondBlock a = CondBlock+ { condIf :: Condition+ -- ^ If this condition is true . . . + , ifTrue :: (a, [a])+ -- ^ . . . then do this . . .+ , ifFalse :: [a]+ -- ^ . . . or if it's false, do this instead.+ } deriving (Eq, Ord, Show)++instance Functor CondBlock where+ fmap f (CondBlock i (t1, ts) flse)+ = CondBlock i (f t1, fmap f ts) (fmap f flse)++class LogicTree a where+ (&&&) :: a -> a -> a+ (|||) :: a -> a -> a++instance LogicTree Constraint where+ AnyVersion &&& x = x+ Constrained x &&& AnyVersion = Constrained x+ Constrained x &&& Constrained y = Constrained (x &&& y)++ AnyVersion ||| _ = AnyVersion+ Constrained _ ||| AnyVersion = AnyVersion+ Constrained x ||| Constrained y = Constrained (x ||| y)++instance LogicTree ConstrTree where+ l &&& r = Branch And l r+ l ||| r = Branch Or l r++instance LogicTree Condition where+ l &&& r = CBranch And l r+ l ||| r = CBranch Or l r++infixr 3 &&&+infixr 2 |||++-- | Less than+lt :: Version -> Constraint+lt = Constrained . Leaf LT++-- | Greater than+gt :: Version -> Constraint+gt = Constrained . Leaf GT++-- | Equal to+eq :: Version -> Constraint+eq = Constrained . Leaf EQ++-- | Less than or equal to+ltEq :: Version -> Constraint+ltEq v = lt v ||| eq v++-- | Greater than or equal to+gtEq :: Version -> Constraint+gtEq v = gt v ||| eq v++-- | Matches any version at all (in a Cabal file, this is represented+-- as an empty string).+anyVersion :: Constraint+anyVersion = AnyVersion+++-- | 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+ :: NonEmptyString+ -- ^ Package name+ -> Version+ -- ^ Version number for lower bound+ -> Version+ -- ^ Version number for upper bound+ -> Package+ -- ^ Resulting 'Package'+closedOpen n l u = Package n (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 :: NonEmptyString -> Version -> 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+ :: NonEmptyString+ -> Version+ -> 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+ :: NonEmptyString+ -> Version+ -> 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 :: NonEmptyString -> Version -> Package+exactly n v = Package n (eq v)++-- | Allows any version of a package.++unconstrained+ :: NonEmptyString+ -- ^ Name of package+ -> Package+unconstrained nm = Package nm anyVersion++-- | Builds @if@ statements. For example:+--+-- > condition (flag "buildExe") (buildable True, []) [buildable False]+--+-- A little more complicated:+--+-- > condition (flag "buildExe" &&& system "windows")+-- > (buildable True, []) [buildable False]+condBlock+ :: HasBuildInfo a+ => Condition+ -- ^ Condition to satisfy+ -> (a, [a])+ -- ^ Use these results if condition is true+ -> [a]+ -- ^ Use these results if condition if false+ -> a+condBlock tree ifT ifF =+ conditional $ CondBlock tree ifT ifF++-- | Operating system; tested against @System.Info.os@ on the+-- target system.+system :: NonEmptyString -> Condition+system = CLeaf . OS++-- | Argument is matched against @System.Info.arch@ on the target+-- system.+arch :: NonEmptyString -> Condition+arch = CLeaf . Arch++-- | Tests for the configured Haskell implementation.+impl :: Compiler -> Constraint -> Condition+impl cm cn = CLeaf $ Impl cm cn++-- | 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.+flag :: FlagName -> Condition+flag = CLeaf . CFlag++-- | Always true.+true :: Condition+true = CLeaf CTrue++-- | Always false.+false :: Condition+false = CLeaf CFalse++-- * Versions++-- | 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 'Word', as+-- each number cannot be negative. In addition, this list should+-- never be empty. However, this is just a type synonym for a list of+-- 'Word', so the type system does not enforce the requirement that+-- this list be non-empty.+type Version = [Word]++-- * Packages++-- | A single package, consisting of a package name and an optional+-- set of constraints. Used when specifying 'buildDepends',+-- 'buildTools', and 'pkgConfigDepends'.+--+-- Some functions exist to ease the creation of a 'Package'. For a+-- package with no version constrains, simply do something like+-- @'unconstrained' \"QuickCheck\"@. Common use cases are+-- covered in the functions in the \"Package Helpers\" section below.+-- For something more complicated, use the functions in the+-- \"Logicals\" sections above, along with the '&&&' and '|||'+-- combinators, to create your own 'Constraint' and then use it with+-- the 'package' function.++data Package = Package NonEmptyString Constraint+ deriving (Eq, Ord, Show)++-- | Builds a 'Package'.+package+ :: NonEmptyString+ -- ^ The name of the package+ -> Constraint+ -- ^ Version constraints.+ -> Package+package = Package++-- * Build information++-- | Default language. Currently not documented, see+--+-- <https://github.com/haskell/cabal/issues/1894>+data DefaultLanguage+ = Haskell98+ | Haskell2010+ deriving (Eq, Ord, Show)++-- | Sets Haskell 98 as the @default-language@.+--+-- Currently not documented in Cabal, see+--+-- <https://github.com/haskell/cabal/issues/1894>+haskell98 :: HasBuildInfo a => a+haskell98 = buildInfo $ DefaultLanguage Haskell98++-- | Sets Haskell 2010 as the @default-language@.+--+-- Currently not documented in Cabal, see+--+-- <https://github.com/haskell/cabal/issues/1894>+haskell2010 :: HasBuildInfo a => a+haskell2010 = buildInfo $ DefaultLanguage Haskell2010++-- | A single field of build information. This can appear in a+-- @Library@, 'Executable', 'TestSuite', or 'Benchmark'.+data BuildInfoField+ = BuildDepends [Package]+ -- ^ A list of packages needed to build this component++ | OtherModules [NonEmptyString]+ -- ^ 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 [NonEmptyString]+ -- ^ Root directories for the module hierarchy++ | Extensions [NonEmptyString]+ -- ^ Haskell extensions used by every module.++ | BuildTools [Package]+ -- ^ Programs needed to build this package, such as c2hs.++ | Buildable Bool+ -- ^ Is this component buildable?++ | GHCOptions [NonEmptyString]+ | GHCProfOptions [NonEmptyString]+ | GHCSharedOptions [NonEmptyString]+ | HugsOptions [NonEmptyString]+ | Nhc98Options [NonEmptyString]+ | Includes [NonEmptyString]+ -- ^ 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 [NonEmptyString]+ -- ^ 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 [NonEmptyString]+ -- ^ List of diretories to search for header files when dealing+ -- with C compilations.++ | CSources [NonEmptyString]+ -- ^ C sources to be compiled and lined with the Haskell files.++ | ExtraLibraries [NonEmptyString]+ -- ^ Extra libraries to link with.++ | ExtraLibDirs [NonEmptyString]+ -- ^ Directories to search for libraries.++ | CCOptions [NonEmptyString]+ -- ^ C Compiler options.++ | CPPOptions [NonEmptyString]+ -- ^ C Preprocessor options. Undocumented, see+ -- <https://github.com/haskell/cabal/issues/646>++ | LDOptions [NonEmptyString]+ -- ^ Linker options.++ | PkgConfigDepends [Package]+ -- ^ List of pkg-config packages needed to build this component.++ | Frameworks [NonEmptyString]+ -- ^ OS X frameworks.++ | DefaultLanguage DefaultLanguage++ deriving (Eq, Ord, Show)++-- * Libraries++-- | A field in the @Library@ section of the Cabal file. A+-- @Library@ section can have multiple fields.+data LibraryField+ = ExposedModules [NonEmptyString]+ -- ^ Exposed modules. 'modules' can help you generate this, without+ -- you having to manually list each module and keep the list up to+ -- date.++ | Exposed 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)++-- | Whether a library is exposed. GHC can hide libraries.+exposed :: Bool -> LibraryField+exposed = Exposed++-- | A library's exposed modules. 'modules' can help you generate+-- this, without you having to manually list each module and keep the+-- list up to date.+exposedModules :: [NonEmptyString] -> LibraryField+exposedModules = ExposedModules++-- * Executables++-- | A single field in an 'Executable' section. An 'Executable'+-- section may have multiple fields.+data ExecutableField+ = ExeConditional (CondBlock ExecutableField)+ -- ^ An 'Executable' section can contain conditional blocks.++ | ExeInfo BuildInfoField+ -- ^ An 'Executable' section can contain one or more build+ -- information fields.++ | ExeMainIs NonEmptyString+ -- ^ The name of the @.hs@ or @.lhs@ file containing the @Main@+ -- module. Note that it is the @.hs@ filename that must be listed,+ -- even if that file is generated using a preprocessor. The source+ -- file must be relative to one of the directories listed in+ -- 'hsSourceDirs'.++ deriving (Eq, Ord, Show)++-- | An entire @Executable@ section.+data Executable = Executable+ { exeName :: NonEmptyString+ -- ^ The name of the executable that Cabal will build.+ , exeFields :: [ExecutableField]+ -- ^ An executable can contain zero or more 'ExecutableField's.+ } deriving (Eq, Ord, Show)++-- | Builds a 'Section' for executable files.+executable+ :: NonEmptyString+ -- ^ The name of the executable that Cabal will build.+ -> [ExecutableField]+ -- ^ An executable can contain zero or more 'ExecutableField's.+ -> Section+executable nm fs = SecExe (Executable nm fs)++-- * Test suites++-- | What kind of test suite is this?+data TestSuiteType+ = ExitcodeStdio+ -- ^ An @exitcode-stdio-1.0@ test. The @String@ is the name of+ -- the file containing the executable code. In this case, the+ -- @test-main-is@ field is required.++ | Detailed+ -- ^ The @detailed-0.9@ test. In this case, the @test-module@+ -- field is required.+ deriving (Eq, Ord, Show)++detailed :: TestSuiteField+detailed = TestSuiteType Detailed++-- | A single field value in a 'TestSuite' section. A single test+-- suite section may contain mulitple fields.+data TestSuiteField+ = TestConditional (CondBlock TestSuiteField)+ -- ^ The 'TestSuite' may contain zero or more conditional blocks.+ | TestInfo BuildInfoField+ -- ^ The 'TestSuite' may contain zero or more build information+ -- fields.+ | TestMainIs NonEmptyString+ -- ^ The name of the @.hs@ or @.lhs@ file containing the @Main@+ -- module. Note that it is the @.hs@ filename that must be listed,+ -- even if that file is generated using a preprocessor. The source+ -- file must be relative to one of the directories listed in+ -- 'hsSourceDirs'.+ --+ -- This is required when using 'ExitcodeStdio' and disallowed when+ -- using 'Detailed'.++ | TestSuiteType TestSuiteType+ | TestModule NonEmptyString+ -- ^ The module exporting the @tests@ symbol. This is required when+ -- using 'Detailed' and disallowed when using 'ExitcodeStdio'.++ deriving (Eq, Ord, Show)++-- | The module exporting the @tests@ symbol. This is required when+-- using 'Detailed' and disallowed when using 'ExitcodeStdio'.+testModule :: NonEmptyString -> TestSuiteField+testModule = TestModule++-- | An entire @test-suite@ section.+data TestSuite = TestSuite+ { testSuiteName :: NonEmptyString+ -- ^ The executable name for the resulting test suite+ , testSuiteFields :: [TestSuiteField]+ -- ^ Zero or more 'TestSuiteField's.+ } deriving (Eq, Ord, Show)++-- | Builds a 'Section' for test suites.+testSuite+ :: NonEmptyString+ -- ^ The executable name for the resulting test suite+ -> [TestSuiteField]+ -- ^ Zero or more 'TestSuiteField's.+ -> Section+testSuite nm fs = SecTest (TestSuite nm fs)++-- * Benchmarks++data BenchmarkType+ = BenchExitCode+ -- ^ @exitcode-stdio-1.0@, currently the only supported benchmark interface.+ deriving (Eq, Ord, Show)++-- | A single field in a @Benchmark@ section.+data BenchmarkField+ = BenchmarkConditional (CondBlock BenchmarkField)+ | BenchmarkInfo BuildInfoField+ | BenchmarkType BenchmarkType+ | BenchmarkMainIs NonEmptyString+ -- ^ The name of the @.hs@ or @.lhs@ file containing the @Main@+ -- module. Note that it is the @.hs@ filename that must be listed,+ -- even if that file is generated using a preprocessor. The source+ -- file must be relative to one of the directories listed in+ -- 'hsSourceDirs'.+ deriving (Eq, Ord, Show)++-- | An entire @Benchmark@ section.+data Benchmark = Benchmark+ { benchmarkName :: NonEmptyString+ -- ^ The name of the executable file that will be the benchmark+ , benchmarkFields :: [BenchmarkField]+ -- ^ Zero or more benchmark fields.+ } deriving (Eq, Ord, Show)++-- | Builds a 'Section' for benchmarks.+benchmark+ :: NonEmptyString+ -- ^ The name of the executable file that will be the benchmark+ -> [BenchmarkField]+ -- ^ Zero or more benchmark fields.+ -> Section+benchmark nm fs = SecBench (Benchmark nm fs)++-- * Overloaded fields++-- | Things that can be an item in a build information field in a+-- Cabal file.+class HasBuildInfo a where++ -- | Takes a conditional block and wraps it in the field type.+ conditional :: CondBlock a -> a++ -- | Takes a build information field and wraps it in the field+ -- type.+ buildInfo :: BuildInfoField -> a++instance HasBuildInfo LibraryField where+ conditional = LibConditional+ buildInfo = LibInfo++instance HasBuildInfo ExecutableField where+ conditional = ExeConditional+ buildInfo = ExeInfo++instance HasBuildInfo TestSuiteField where+ conditional = TestConditional+ buildInfo = TestInfo++instance HasBuildInfo BenchmarkField where+ conditional = BenchmarkConditional+ buildInfo = BenchmarkInfo++-- # Build info helpers++-- ^ A list of packages needed to build this component+buildDepends :: HasBuildInfo a => [Package] -> a+buildDepends = buildInfo . BuildDepends++-- ^ 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.+--+-- 'modules' can help greatly with maintenance of+-- this field.++otherModules :: HasBuildInfo a => [NonEmptyString] -> a+otherModules = buildInfo . OtherModules++-- ^ Root directories for the module hierarchy+hsSourceDirs :: HasBuildInfo a => [NonEmptyString] -> a+hsSourceDirs = buildInfo . HsSourceDirs++-- ^ Haskell extensions used by every module.+extensions :: HasBuildInfo a => [NonEmptyString] -> a+extensions = buildInfo . Extensions++-- | Programs needed to build this package, such as c2hs.+buildTools :: HasBuildInfo a => [Package] -> a+buildTools = buildInfo . BuildTools++-- | Is this component buildable?+buildable :: HasBuildInfo a => Bool -> a+buildable = buildInfo . Buildable++ghcOptions :: HasBuildInfo a => [NonEmptyString] -> a+ghcOptions = buildInfo . GHCOptions++ghcProfOptions :: HasBuildInfo a => [NonEmptyString] -> a+ghcProfOptions = buildInfo . GHCProfOptions++ghcSharedOptions :: HasBuildInfo a => [NonEmptyString] -> a+ghcSharedOptions = buildInfo . GHCSharedOptions++hugsOptions :: HasBuildInfo a => [NonEmptyString] -> a+hugsOptions = buildInfo . HugsOptions++nhc98Options :: HasBuildInfo a => [NonEmptyString] -> a+nhc98Options = buildInfo . Nhc98Options++-- | 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.++includes :: HasBuildInfo a => [NonEmptyString] -> a+includes = buildInfo . Includes++-- | 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@.+installIncludes :: HasBuildInfo a => [NonEmptyString] -> a+installIncludes = buildInfo . InstallIncludes++-- | List of diretories to search for header files when dealing+-- with C compilations.+includeDirs :: HasBuildInfo a => [NonEmptyString] -> a+includeDirs = buildInfo . IncludeDirs++-- | C sources to be compiled and lined with the Haskell files.+cSources :: HasBuildInfo a => [NonEmptyString] -> a+cSources = buildInfo . CSources++-- | Extra libraries to link with.+extraLibraries :: HasBuildInfo a => [NonEmptyString] -> a+extraLibraries = buildInfo . ExtraLibraries++-- | Directories to search for libraries.+extraLibDirs :: HasBuildInfo a => [NonEmptyString] -> a+extraLibDirs = buildInfo . ExtraLibDirs++-- | C Compiler options.+ccOptions :: HasBuildInfo a => [NonEmptyString] -> a+ccOptions = buildInfo . CCOptions++-- | C Preprocessor options. Undocumented, see+-- <https://github.com/haskell/cabal/issues/646>+cppOptions :: HasBuildInfo a => [NonEmptyString] -> a+cppOptions = buildInfo . CPPOptions++-- | Linker options.+ldOptions :: HasBuildInfo a => [NonEmptyString] -> a+ldOptions = buildInfo . LDOptions++-- | List of pkg-config packages needed to build this component.+pkgConfigDepends :: HasBuildInfo a => [Package] -> a+pkgConfigDepends = buildInfo . PkgConfigDepends++-- | OS X frameworks.+frameworks :: HasBuildInfo a => [NonEmptyString] -> a+frameworks = buildInfo . Frameworks++-- | Sections that build executables. These are the 'Executable',+-- 'Benchmark', and 'TestSuite' sections.+class BuildsExe a where+ mainIs :: NonEmptyString -> a+ -- ^ Overloaded function allowing you to use 'mainIs' for an+ -- 'Executable', 'Benchmark', or 'TestSuite' section.++instance BuildsExe ExecutableField where+ mainIs = ExeMainIs++instance BuildsExe TestSuiteField where+ mainIs = TestMainIs++instance BuildsExe BenchmarkField where+ mainIs = BenchmarkMainIs++-- | Sections that build executables that can be @exitcode-stdio-1.0@.+-- These are the 'Benchmark' and 'TestSuite' sections.+class BuildsExitcode a where+ exitcodeStdio :: a+ -- ^ Returns a field that is @exitcode-stdio-1.0@++instance BuildsExitcode TestSuiteField where+ exitcodeStdio = TestSuiteType ExitcodeStdio++instance BuildsExitcode BenchmarkField where+ exitcodeStdio = BenchmarkType BenchExitCode++-- | Builds two fields. The first indicates that this is an+-- @exitcode-stdio-1.0@ executable; the second is the appropriate+-- @main-is@ field.+exitcodeFields+ :: (BuildsExitcode a, BuildsExe a)+ => NonEmptyString+ -- ^ Value for @main-is@ field+ -> [a]+exitcodeFields m = [exitcodeStdio, mainIs m]++-- * Getting module lists++-- | Gets all Haskell modules in a given directory tree. Allows you+-- to specify what extensions you are interested in. For this to work+-- best, you will want to keep all your library modules in their own+-- directory, such as @lib/@. You can also separate executables and+-- test suites this way. 'hsSourceDirs' will then tell Cabal to use+-- these directories.++modulesWithExtensions+ :: MonadIO m+ => [NonEmptyString]+ -- ^ Look for files that have 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.+ --+ -- Do not include the leading dot with the extension. For example,+ -- to look for Haskell and literate Haskell files only, use+ --+ -- > ["hs", "lhs"]++ -> FilePath+ -- ^ Start searching within this directory.++ -> Betsy m [NonEmptyString]+ -- ^ 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 fp+ = liftIO $ modulesWithExtensionsIO exts fp+++-- | Same as+--+-- @+-- 'modulesWithExtensions' 'fileExtensions'+-- @+modules+ :: MonadIO m+ => FilePath+ -> Betsy m [NonEmptyString]+modules = modulesWithExtensions fileExtensions++-- | 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.++modulesIO+ :: 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.+modulesIO = modulesWithExtensionsIO fileExtensions+++-- | Gets all Haskell modules in a given directory tree. Allows you+-- to specify what extensions you are interested in.++modulesWithExtensionsIO+ :: [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.+ --+ -- Do not include the leading dot with the extension. For+ -- example, to look for Haskell and literate Haskell files only, use+ --+ -- > ["hs", "lhs"]++ -> 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.++modulesWithExtensionsIO 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 []++-- * Section++-- | A single section in a Cabal file; this may be a source+-- repository, executable, test suite, or benchmark. You build a+-- 'Section' with the 'repository', 'executable', 'testSuite', and+-- 'benchmark' functions.+data Section+ = SecRepo Repository+ | SecExe Executable+ | SecTest TestSuite+ | SecBench Benchmark+ deriving (Eq, Ord, Show)++-- * Properties++data BuildType+ = Simple+ | Configure+ | Make+ | Custom+ deriving (Eq, Ord, Show)++simple, configure, make, custom :: BuildType+simple = Simple+configure = Configure+make = Make+custom = Custom++data License+ = GPL+ | AGPL+ | LGPL+ | BSD2+ | BSD3+ | BSD4+ | MIT+ | MPL+ | Apache+ | PublicDomain+ | AllRightsReserved+ | OtherLicense+ deriving (Eq, Ord, Show)++gpl, agpl, lgpl, bsd2, bsd3, bsd4, mit, mpl, apache, publicDomain,+ allRightsReserved, otherLicense :: License++gpl = GPL+agpl = AGPL+lgpl = LGPL+bsd2 = BSD2+bsd3 = BSD3+bsd4 = BSD4+mit = MIT+mpl = MPL+apache = Apache+publicDomain = PublicDomain+allRightsReserved = AllRightsReserved+otherLicense = OtherLicense++data Compiler+ = GHC+ | NHC+ | YHC+ | Hugs+ | Helium+ | JHC+ | LHC+ deriving (Eq, Ord, Show)++ghc, nhc, yhc, hugs, helium, jhc, lhc :: Compiler+ghc = GHC+nhc = NHC+yhc = YHC+hugs = Hugs+helium = Helium+jhc = JHC+lhc = LHC++-- | Global package properties.+data Properties = Properties+ { name :: String+ , version :: Version+ , cabalVersion :: Maybe (Word, Word)+ , buildType :: Maybe BuildType+ , license :: Maybe License+ , licenseFile :: String+ , licenseFiles :: [NonEmptyString]+ , copyright :: String+ , author :: String+ , maintainer :: String+ , stability :: String+ , homepage :: String+ , bugReports :: String+ , packageUrl :: String+ , synopsis :: String+ , description :: [String]+ , category :: String+ -- ^ According to the \"Developing Cabal Packages\" document, this+ -- should simply be an email address.+ , testedWith :: [(Compiler, Constraint)]+ , dataFiles :: [NonEmptyString]+ , dataDir :: String+ , extraSourceFiles :: [NonEmptyString]+ , extraDocFiles :: [NonEmptyString]+ , extraTmpFiles :: [NonEmptyString]+ } deriving (Eq, Ord, Show)++instance Blank Properties where+ blank = Properties+ { name = ""+ , version = []+ , cabalVersion = Nothing+ , buildType = Nothing+ , license = Nothing+ , licenseFile = ""+ , licenseFiles = []+ , copyright = ""+ , author = ""+ , maintainer = ""+ , stability = ""+ , homepage = ""+ , bugReports = ""+ , packageUrl = ""+ , synopsis = ""+ , description = []+ , category = ""+ , testedWith = []+ , dataFiles = []+ , dataDir = ""+ , extraSourceFiles = []+ , extraDocFiles = []+ , extraTmpFiles = []+ }++-- * Cabal++-- | Represents an entire Cabal file.+data Cabal = Cabal+ { properties :: Properties+ , library :: [LibraryField]+ , sections :: [Section]+ } deriving (Eq, Ord, Show)++-- | Creates a 'Cabal' value when applied to package properties, any+-- applicable library fields, and any sections.+cabal :: Properties -> [LibraryField] -> [Section] -> Cabal+cabal = Cabal++instance Blank Cabal where+ blank = Cabal blank [] []
+ lib/Cartel/Betsy.hs view
@@ -0,0 +1,15 @@+-- | Public interface for 'Betsy', the Cartel flag maker. Simply+-- re-exports safe bindings from "Cartel.Betsy.Internal".+module Cartel.Betsy+ ( FlagName+ , flagNameHead+ , flagNameTail+ , FlagOpts(..)+ , Betsy+ , Error(..)+ , runBetsy+ , makeFlag+ , currentFlags+ ) where++import Cartel.Betsy.Internal
+ lib/Cartel/Betsy/Internal.hs view
@@ -0,0 +1,132 @@+-- | Internal workings of 'Betsy', the Cartel flag maker. Use of this+-- module may break 'Betsy' invariants.+module Cartel.Betsy.Internal where++import Control.Monad+import Control.Monad.IO.Class+import Control.Monad.Trans.Class+import Cartel.Types+import Control.Applicative++-- | Errors that may result from running a 'Betsy' computation.+data Error+ = DuplicateFlag FlagName+ -- ^ The user requested creation of a duplicate flag.+ | Failed String+ -- ^ 'fail' was invoked.+ | EmptyFlagName+ -- ^ The user requested creation of a flag with an empty name.+ deriving (Eq, Ord, Show)++renderError :: Error -> String+renderError e = unlines $+ "Error while attempting to generate Cabal file from Cartel source."+ : case e of+ DuplicateFlag (FlagName c cs) ->+ ["Duplicated flag: " ++ (c:cs)]+ Failed s -> ["The \"fail\" function was invoked: " ++ s]+ EmptyFlagName -> ["Empty flag name"]++-- | Internal state maintained in the 'Betsy' type.+data State = State [(FlagName, FlagOpts)]+ deriving (Eq, Ord, Show)++-- | Computations that can create and use Cabal flags. Use of this+-- type, along with the 'Cartel.defaultMain' function ensures that any+-- 'FlagName' you use has been properly set up by using 'makeFlag'.+-- That way, you don't use flags in a 'Cartel.flag' without actually+-- declaring the flag. When 'Cartel.defaultMain' creates your Cabal+-- file, it will print the necessary @Flag@ sections.+--+-- 'Betsy' is parameterized on a type, @m@. When this type is a+-- monad, 'Betsy' is also a monad, allowing you to use use the usual+-- monad combinators and @do@ notation. 'Betsy' is also a monad transformer.++newtype Betsy m a = Betsy (State -> m (Either Error (a, State)))++instance Monad m => Monad (Betsy m) where+ return a = Betsy $ \s -> return (Right (a, s))+ (Betsy l) >>= f = Betsy $ \s -> do+ ei <- l s+ case ei of+ Left e -> return (Left e)+ Right (g, s') -> do+ let Betsy r = f g+ r s'+ fail s = Betsy $ \_ -> return (Left (Failed s))++instance Functor m => Functor (Betsy m) where+ fmap f (Betsy k) = Betsy $ fmap (fmap f') k+ where+ f' ei = fmap (\(a, s) -> (f a, s)) ei++instance (Monad m, Functor m) => Applicative (Betsy m) where+ pure = return+ (<*>) = ap++instance MonadTrans Betsy where+ lift k = Betsy $ \st -> do+ a <- k+ return $ Right (a, st)++instance MonadIO m => MonadIO (Betsy m) where+ liftIO = lift . liftIO++-- ** Flags++-- | Options for flags, except for the flag's name.+data FlagOpts = FlagOpts+ { flagDescription :: String+ -- ^ A one-line description of what the flag does; this is optional.+ , flagDefault :: Bool+ -- ^ Is this flag on or off by default?+ , flagManual :: Bool+ -- ^ If a flag is manual, Cabal will not change its value. If a+ -- flag is not manual, Cabal will change its value automatically to+ -- attempt to satisfy the package's dependencies.+ } deriving (Eq, Ord, Show)++-- | The name of a flag. Only 'makeFlag' creates flags; it will+-- return a 'FlagName' to you. You can then use that 'FlagName' in a+-- conditional using 'Cartel.flag'.+data FlagName = FlagName+ { flagNameHead :: Char+ , flagNameTail :: String+ } deriving (Eq, Ord, Show)++-- | Creates new flags.+makeFlag+ :: Applicative m+ => NonEmptyString+ -- ^ Name of flag+ -> FlagOpts+ -- ^ Options for the flag+ -> Betsy m FlagName+ -- ^ This operation will fail if there is already a flag with the+ -- name you gave.+makeFlag nes opts = Betsy $ \(State fls) -> pure $+ case nes of+ [] -> Left EmptyFlagName+ x:xs ->+ let nm = FlagName x xs+ in case lookup nm fls of+ Nothing ->+ let st' = State ((nm, opts) : fls)+ in Right (nm, st')+ Just _ -> Left (DuplicateFlag nm)++runBetsy+ :: Functor m+ => Betsy m a+ -> m (Either Error (a, [(FlagName, FlagOpts)]))+ -- ^ Returns 'Left' if the making of a flag failed. Otherwise,+ -- returns the result of the computation, along with a list of all+ -- flags made.+runBetsy (Betsy f) = fmap (fmap g) $ f (State [])+ where+ g (a, State ls) = (a, ls)+++-- | Returns a list of all flags made so far.+currentFlags :: Applicative f => Betsy f [(FlagName, FlagOpts)]+currentFlags = Betsy $ \(State ls) -> pure (Right (ls, State ls))
− lib/Cartel/Empty.hs
@@ -1,79 +0,0 @@--- | 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 empty value as a starting point and fill in--- the components of the record that you need.-module Cartel.Empty where--import qualified Cartel.Ast as A--class Empty a where- empty :: a---- | A default 'A.Properties'. 'A.prCabalVersion' is @(1, 14)@ to--- specify Cabal version 1.14; 'A.prBuildType' is 'A.Simple', and--- 'A.prLicense' is 'A.BSD3'. All other items are either the empty--- 'String' or the empty list, including the 'A.prVersion', which is--- left empty.-instance Empty A.Properties where- empty = A.Properties- { A.prName = ""- , A.prVersion = A.Version []- , A.prCabalVersion = (1, 14)- , A.prBuildType = A.Simple- , A.prLicense = A.BSD3- , A.prLicenseFile = ""- , 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 = []- }---- | A default 'A.Cabal'. 'empty' is used for 'A.cProperties',--- and 'A.cLibrary' is 'Nothing'; all other lists are empty.-instance Empty A.Cabal where- empty = A.Cabal- { A.cProperties = empty- , A.cRepositories = []- , A.cFlags = []- , A.cLibrary = Nothing- , A.cExecutables = []- , A.cTestSuites = []- , A.cBenchmarks = []- }---- | A default 'A.Repository'. The VCS type is 'A.Git' and the--- repository kind is 'A.Head'. All other values are the empty--- string.-instance Empty A.Repository where- empty = A.Repository- { A.repoVcs = A.Git- , A.repoKind = A.Head- , A.repoLocation = ""- , A.repoBranch = ""- , A.repoTag = ""- , A.repoSubdir = ""- }---- | A default 'A.Flag'. 'A.flName' and 'A.flDescription' are--- empty; 'A.flDefault' is 'True', and 'A.flManual' is 'False'.-instance Empty A.Flag where- empty = A.Flag- { A.flName = ""- , A.flDescription = ""- , A.flDefault = True- , A.flManual = False- }
+ lib/Cartel/GenCartelCabal.hs view
@@ -0,0 +1,117 @@+-- | 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,5,0,0] [4,8]++directory :: Package+directory = closedOpen "directory" [1,1,0,2] [1,3]++filepath :: Package+filepath = closedOpen "filepath" [1,3,0,0] [1,4]++time :: Package+time = closedOpen "time" [1,4] [1,6]++quickcheck :: Package+quickcheck = nextBreaking "QuickCheck" [2,7]++random :: Package+random = closedOpen "random" [1,1] [1,2]++transformers :: Package+transformers = nextBreaking "transformers" [0,4]++multiarg :: Package+multiarg = nextBreaking "multiarg" [0,30,0,0]++quickpull :: Package+quickpull = nextBreaking "quickpull" [0,4,0,0]++libDepends :: [Package]+libDepends =+ [ base, directory, filepath, time, transformers ]++testDepends :: [Package]+testDepends = [ multiarg, quickcheck, random, quickpull ]++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+ ( blank+ { 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-2015 Omari Norman"+ , author = "Omari Norman"+ , stability = "Experimental"+ , cabalVersion = Just (1,16)+ , homepage = "http://www.github.com/massysett/cartel"+ , bugReports = "http://www.github.com/massysett/cartel/issues"+ , testedWith = let f v = (ghc, eq v)+ in map f [[7,6,3], [7,8,2]]+ , extraSourceFiles = ["README.md"]+ }+ , exposedModules libModules : haskell2010 : commonOptions++ , [ githubHead "massysett" "cartel"+ , executable "cartel-visual-test"+ [ condBlock (flag flagVisual)+ ( buildable True+ , otherModules testMods : testOptions+ )+ [ buildable False+ ]+ , haskell2010+ , mainIs "cartel-visual-test.hs"+ ]+ , testSuite "cartel-properties"+ $ haskell2010 + : otherModules testMods + : testOptions+ ++ exitcodeFields "cartel-properties.hs"+ + ]+ )
lib/Cartel/Render.hs view
@@ -1,17 +1,23 @@--- | 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".+{-# LANGUAGE FlexibleInstances #-}+-- | Reducing a Cartel AST to flat Cabal text; essentially a+-- pretty-printer. module Cartel.Render where -import qualified Cartel.Ast as A+import Data.Word+import Cartel.Betsy+import Cartel.Ast import Data.List (intersperse) +-- | Separate two strings with a space, but only if both strings are+-- not empty. (<+>) :: String -> String -> String l <+> r | null l = r | null r = l | otherwise = l ++ " " ++ r +-- | Concatenate several vertically. Unlike 'unlines', does not add a+-- newline when an item is 'null' or when the accumulator is 'null'. vsep :: [String] -> String vsep = foldr f "" where@@ -23,38 +29,52 @@ indentAmt :: Int indentAmt = 2 +-- | Indents a line of text; adds a newline at the end. indent :: Int -> String -> String indent i s = replicate (i * indentAmt) ' ' ++ s ++ "\n" -indentList :: Int -> [String] -> String+-- | Creates a comma-separated indented representation for a list of+-- items, such as a list of filenames or modules. Each line ends with+-- a newline.+indentList+ :: Renderable a+ => Int+ -- ^ Indentation level.+ -> [a]+ -> 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"+ line1 s = replicate (i * indentAmt + 2) ' ' ++ render s ++ "\n"+ lineRest s = replicate (i * indentAmt) ' '+ ++ ", " ++ render s ++ "\n" labeled- :: Int+ :: Renderable a+ => Int -- ^ Indentation -> String -- ^ Label- -> String+ -> a -- ^ 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)+ | null txt = ""+ | otherwise = indent i (l ++ ": " ++ txt)+ where+ txt = render t labeledList- :: Int+ :: Renderable a+ => Int -- ^ Indentation -> String -- ^ Label- -> [String]+ -> [a] -- ^ List -> String -- ^ Result, with newlines. Empty if the list is empty.@@ -62,277 +82,305 @@ | null ts = "" | otherwise = indent i (l ++ ":") ++ indentList (i + 1) ts -version :: A.Version -> String-version = concat . intersperse "." . map show . A.unVersion+-- | Render an item. The rendered text shall contain no newlines.+class Renderable a where+ render :: a -> String -license :: A.License -> String-license = show+instance Renderable String where+ render = id -buildType :: A.BuildType -> String-buildType = show+instance Renderable a => Renderable (Maybe a) where+ render = maybe "" render -compiler :: A.Compiler -> String-compiler = show+-- | Render an item. The rendered text must contain a newline at the+-- end of each line and must end with a newline. The leftmost line of+-- the rendered text shall be indented by the given number of+-- indentation levels (the number of spaces in each level is set by+-- 'indentAmt').+--+-- If there are no lines to indent, return an empty string.+class RenderableIndented a where+ renderIndented :: Int -> a -> String -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)+instance RenderableIndented Error where+ renderIndented i e = concatMap (indent i) $+ "Error while attempting to generate Cabal file from Cartel source."+ : case e of+ DuplicateFlag nm ->+ ["Duplicated flag: " ++ (flagNameHead nm : flagNameTail nm)]+ Failed s -> ["The \"fail\" function was invoked: " ++ s]+ EmptyFlagName -> ["Empty flag name"] -logical :: A.Logical -> String-logical a = case a of- A.Or -> "||"- A.And -> "&&"+instance Renderable BuildType where+ render = show -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 ++ ")"+instance Renderable License where+ render = show -cabalVersion :: (Int, Int) -> String-cabalVersion (a, b) = ">= " ++ show a ++ "." ++ show b+instance Renderable Compiler where+ render = show -description :: [String] -> String-description ls- | null ls = ""- | otherwise = concatMap f ls- where- f s- | null s = " .\n"- | otherwise = " " ++ s ++ "\n"+instance Renderable Ordering where+ render LT = "<"+ render GT = ">"+ render EQ = "==" -testedWith :: [(A.Compiler, A.ConstrTree)] -> String-testedWith = concat . intersperse ", " . map f- where- f (cmp, cst) = compiler cmp <+> constrTree cst+instance Renderable Version where+ render = concat . intersperse "." . map show -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+instance Renderable Constraint where+ render AnyVersion = ""+ render (Constrained t) = render t -class Field a where- field :: Int -> a -> String+instance Renderable Logical where+ render Or = "||"+ render And = "&&" -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+instance Renderable ConstrTree where+ render (Leaf o v) = render o <+> render v+ render (Branch c l r) = "(" ++ render l+ <+> render c <+> render r ++ ")" -library :: A.Library -> String-library (A.Library l) = "Library\n"- ++ concatMap (field 1) l+instance Renderable (Word, Word) where+ render (a, b) = ">= " ++ show a ++ "." ++ show b -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+newtype Description = Description [String]+ deriving (Eq, Ord, Show) -executable :: A.Executable -> String-executable (A.Executable n fs) =- "Executable " ++ n ++ "\n"- ++ concatMap (field 1) fs+instance RenderableIndented Description where+ renderIndented i (Description ls)+ | null ls = ""+ | otherwise = concatMap f ls+ where+ f s+ | null s = indent i "."+ | otherwise = indent i s -testSuiteType :: A.TestSuiteType -> String-testSuiteType t = case t of- A.ExitcodeStdio -> "exitcode-stdio-1.0"- A.Detailed -> "detailed-1.0"+instance Renderable (Compiler, Constraint) where+ render (cmp, cst) = render cmp <+> render cst -instance Field A.TestSuiteField where- field i t = case t of- A.TestType a -> labeled i "type" (testSuiteType a)- A.TestMainIs s -> labeled i "main-is" s- A.TestModule s -> labeled i "test-module" s- A.TestConditional b -> condBlock i b- A.TestInfo b -> field i b+instance Renderable FlagName where+ render nm = flagNameHead nm : flagNameTail nm -testSuite :: A.TestSuite -> String-testSuite (A.TestSuite n fs) =- "Test-Suite " ++ n ++ "\n"- ++ concatMap (field 1) fs+instance RenderableIndented (FlagName, FlagOpts) where+ renderIndented i (nm, (FlagOpts desc df man)) =+ indent i ("Flag " ++ render nm)+ ++ labeled next "description" desc+ ++ labeled next "default" df+ ++ labeled next "manual" man+ where+ next = i + 1 -instance Field A.BenchmarkField where- field i b = case b of- A.BenchmarkConditional s -> condBlock i s- A.BenchmarkInfo a -> field i a+instance RenderableIndented [(FlagName, FlagOpts)] where+ renderIndented i = vsep . map (renderIndented i) -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+instance Renderable CondLeaf where+ render a = case a of+ OS s -> "os(" ++ s ++ ")"+ Arch s -> "arch(" ++ s ++ ")"+ Impl cmp constr -> "impl(" ++ s ++ ")"+ where+ s = render cmp <+> render constr+ CFlag f -> "flag(" ++ render f ++ ")"+ CTrue -> "true"+ CFalse -> "false" -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)+instance Renderable RepoKind where+ render Head = "head"+ render This = "this" -package :: A.Package -> String-package p =- A.packName p <+> maybe "" constrTree (A.packConstraints p)+instance Renderable Vcs where+ render x = case x of+ Darcs -> "darcs"+ Git -> "git"+ Svn -> "svn"+ Cvs _ -> "cvs"+ Mercurial -> "mercurial"+ Bazaar -> "bazaar"+ ArchVcs -> "arch"+ Monotone -> "monotone" -language :: A.Language -> String-language = show+instance RenderableIndented Repository where+ renderIndented i r =+ indent i ("source-repository " ++ render (repoKind r))+ ++ lbl "type" (repoVcs r)+ ++ lbl "location" (repoLocation r)+ ++ lbl "module" mdle+ ++ lbl "branch" (repoBranch r)+ ++ lbl "tag" (repoTag r)+ ++ lbl "subdir" (repoSubdir r)+ where+ mdle = case repoVcs r of+ Just (Cvs s) -> s+ _ -> ""+ lbl x = labeled (i + 1) x -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.CPPOptions ps ->- labeledList i "cpp-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+instance Renderable Package where+ render (Package nm cs)+ = nm <+> render cs+ +instance Renderable Bool where+ render = show -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 ++ ")"+instance Renderable DefaultLanguage where+ render = show++instance RenderableIndented BuildInfoField where+ renderIndented i fld = case fld of+ BuildDepends ls -> lst "build-depends" ls+ OtherModules ls -> lst "other-modules" ls+ HsSourceDirs ls -> lst "hs-source-dirs" ls+ Extensions ls -> lst "extensions" ls+ BuildTools ls -> lst "build-tools" ls+ Buildable b -> lbl "buildable" b+ GHCOptions ls -> lst "ghc-options" ls+ GHCProfOptions ls -> lst "ghc-prof-options" ls+ GHCSharedOptions ls -> lst "ghc-shared-options" ls+ HugsOptions ls -> lst "hugs-options" ls+ Nhc98Options ls -> lst "nhc98-options" ls+ Includes ls -> lst "includes" ls+ InstallIncludes ls -> lst "install-includes" ls+ IncludeDirs ls -> lst "include-dirs" ls+ CSources ls -> lst "c-sources" ls+ ExtraLibraries ls -> lst "extra-libraries" ls+ ExtraLibDirs ls -> lst "extra-lib-dirs" ls+ CCOptions ls -> lst "cc-options" ls+ CPPOptions ls -> lst "cpp-options" ls+ LDOptions ls -> lst "ld-options" ls+ PkgConfigDepends ls -> lst "pkgconfig-depends" ls+ Frameworks ls -> lst "frameworks" ls+ DefaultLanguage df -> lbl "default-language" df where- s = compiler cmp <+> cond- cond = maybe "" constrTree mayC- A.CFlag f -> "flag(" ++ f ++ ")"- A.CTrue -> "true"- A.CFalse -> "false"+ -- don't do @lst = labeledList i@ - monomorphism restriction+ lst l = labeledList i l+ lbl l = labeled i l -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 ++ ")"+instance RenderableIndented LibraryField where+ renderIndented i fld = case fld of+ ExposedModules ls -> lst "exposed-modules" ls+ Exposed b -> lbl "exposed" b+ LibConditional b -> renderIndented i b+ LibInfo b -> renderIndented i b+ where+ lst l = labeledList i l+ lbl l = labeled i l -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- ]+instance Renderable Condition where+ render tree = case tree of+ CLeaf c -> render c+ CBranch c l r -> ("(" ++ render l)+ <+> render c <+> (render r ++ ")")+ CNegate t -> "!(" ++ render t ++ ")" -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"+instance RenderableIndented a => RenderableIndented (CondBlock a) where+ renderIndented i (CondBlock cond (y1, ys) nos) =+ indent i ("if" <+> render cond)+ ++ renderIndented (i + 1) y1+ ++ concatMap (renderIndented (i + 1)) ys+ ++ elses+ where+ elses | null nos = ""+ | otherwise = indent i "else"+ ++ concatMap (renderIndented (i + 1)) nos -repoKind :: A.RepoKind -> String-repoKind a = case a of- A.Head -> "head"- A.This -> "this"+instance RenderableIndented ExecutableField where+ renderIndented i fld = case fld of+ ExeConditional b -> renderIndented i b+ ExeInfo b -> renderIndented i b+ ExeMainIs m -> labeled i "main-is" m -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" (A.repoTag r)- ++ lbl "subdir" (A.repoSubdir r)- where- mdle = case A.repoVcs r of- A.Cvs s -> s- _ -> ""- lbl = labeled 1+instance RenderableIndented Executable where+ renderIndented i (Executable nm flds) =+ indent i ("Executable " ++ nm)+ ++ concatMap (renderIndented next) flds+ where+ next = i + 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- ]+instance Renderable TestSuiteType where+ render ExitcodeStdio = "exitcode-stdio-1.0"+ render Detailed = "detailed-0.9"++instance RenderableIndented TestSuiteField where+ renderIndented i fld = case fld of+ TestConditional c -> renderIndented i c+ TestInfo b -> renderIndented i b+ TestMainIs m -> labeled i "main-is" m+ TestSuiteType t -> labeled i "type" t+ TestModule m -> labeled i "test-module" m++instance RenderableIndented TestSuite where+ renderIndented i (TestSuite n flds) =+ indent i ("Test-Suite " ++ n)+ ++ concatMap (renderIndented next) flds+ where+ next = i + 1++instance Renderable BenchmarkType where+ render BenchExitCode = "exitcode-stdio-1.0"++instance RenderableIndented BenchmarkField where+ renderIndented i (BenchmarkConditional b) = renderIndented i b+ renderIndented i (BenchmarkInfo b) = renderIndented i b+ renderIndented i (BenchmarkMainIs b) = labeled i "main-is" b+ renderIndented i (BenchmarkType b) = labeled i "type" b++instance RenderableIndented Benchmark where+ renderIndented i (Benchmark nm flds) =+ indent i ("Benchmark " ++ nm)+ ++ concatMap (renderIndented next) flds+ where+ next = i + 1++instance RenderableIndented Properties where+ renderIndented i c =+ lbl "name" (name c)+ ++ lbl "version" (version c)+ ++ lbl "cabal-version" (maybe "" render . cabalVersion $ c)+ ++ lbl "license" (maybe "" render . license $ c)+ ++ lbl "license-file" (licenseFile c)+ ++ lst "license-files" (licenseFiles c)+ ++ lbl "build-type" (maybe "" render . buildType $ c)+ ++ lbl "copyright" (copyright c)+ ++ lbl "author" (author c)+ ++ lbl "maintainer" (maintainer c)+ ++ lbl "stability" (stability c)+ ++ lbl "homepage" (homepage c)+ ++ lbl "bug-reports" (bugReports c)+ ++ lbl "package-url" (packageUrl c)+ ++ lbl "synopsis" (synopsis c)+ ++ indent i "description:"+ ++ renderIndented (i + 1) (Description . description $ c)+ ++ lbl "category" (category c)+ ++ labeledList i "tested-with" (testedWith c)+ ++ lst "data-files" (dataFiles c)+ ++ lbl "data-dir" (dataDir c)+ ++ lst "extra-source-files" (extraSourceFiles c)+ ++ lst "extra-doc-files" (extraDocFiles c)+ ++ lst "extra-tmp-files" (extraTmpFiles c)+ where+ lbl l = labeled i l+ lst l = labeledList i l++renLibrary :: Int -> [LibraryField] -> String+renLibrary _ [] = ""+renLibrary lvl xs = indent lvl "Library"+ ++ concatMap (renderIndented (lvl + 1)) xs++instance RenderableIndented Section where+ renderIndented i s = case s of+ SecRepo x -> ren x+ SecExe x -> ren x+ SecTest x -> ren x+ SecBench x -> ren x+ where+ ren x = renderIndented i x++instance RenderableIndented Cabal where+ renderIndented i (Cabal prop lib secs)+ = vsep $ ren prop+ : renLibrary 0 lib+ : map ren secs+ where+ ren x = renderIndented i x
− lib/Cartel/Tools.hs
@@ -1,519 +0,0 @@-module Cartel.Tools- ( -- * Logic trees- LogicTree(..)- - -- * 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-- -- * Field class- , Field(..)-- -- ** Conditionals- , cif- , system- , arch- , impl- , flag- , true- , false-- -- ** Build info builders- , buildDepends- , otherModules- , hsSourceDirs- , extensions- , buildTools- , buildable- , ghcOptions- , ghcProfOptions- , ghcSharedOptions- , hugsOptions- , nhc98Options- , includes- , installIncludes- , includeDirs- , cSources- , extraLibraries- , extraLibDirs- , ccOptions- , cppOptions- , ldOptions- , pkgConfigDepends- , frameworks- , defaultLanguage-- -- * 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--class LogicTree a where- (&&&) :: a -> a -> a- (|||) :: a -> a -> a--infixr 3 &&&-infixr 2 |||--instance LogicTree A.ConstrTree where- l &&& r = A.Branch A.And l r- l ||| r = A.Branch A.Or l r--instance LogicTree A.CondTree where- l &&& r = A.CBranch A.And l r- l ||| r = A.CBranch A.Or l r---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 []---- | Things that can be an item in a field in a Cabal file.-class Field a where-- -- | Takes a conditional block and wraps it in the field type.- conditional :: A.CondBlock a -> a-- -- | Takes a build information field and wraps it in the field- -- type.- buildInfo :: A.BuildInfoField -> a--instance Field A.LibraryField where- conditional = A.LibConditional- buildInfo = A.LibInfo--instance Field A.ExecutableField where- conditional = A.ExeConditional- buildInfo = A.ExeInfo--instance Field A.TestSuiteField where- conditional = A.TestConditional- buildInfo = A.TestInfo--instance Field A.BenchmarkField where- conditional = A.BenchmarkConditional- buildInfo = A.BenchmarkInfo---- # Build info helpers-buildDepends :: Field a => [A.Package] -> a-buildDepends = buildInfo . A.BuildDepends--otherModules :: Field a => [String] -> a-otherModules = buildInfo . A.OtherModules--hsSourceDirs :: Field a => [String] -> a-hsSourceDirs = buildInfo . A.HsSourceDirs--extensions :: Field a => [String] -> a-extensions = buildInfo . A.Extensions--buildTools :: Field a => [A.Package] -> a-buildTools = buildInfo . A.BuildTools--buildable :: Field a => Bool -> a-buildable = buildInfo . A.Buildable--ghcOptions :: Field a => [String] -> a-ghcOptions = buildInfo . A.GHCOptions--ghcProfOptions :: Field a => [String] -> a-ghcProfOptions = buildInfo . A.GHCProfOptions--ghcSharedOptions :: Field a => [String] -> a-ghcSharedOptions = buildInfo . A.GHCSharedOptions--hugsOptions :: Field a => [String] -> a-hugsOptions = buildInfo . A.HugsOptions--nhc98Options :: Field a => [String] -> a-nhc98Options = buildInfo . A.Nhc98Options--includes :: Field a => [String] -> a-includes = buildInfo . A.Includes--installIncludes :: Field a => [String] -> a-installIncludes = buildInfo . A.InstallIncludes--includeDirs :: Field a => [String] -> a-includeDirs = buildInfo . A.IncludeDirs--cSources :: Field a => [String] -> a-cSources = buildInfo . A.CSources--extraLibraries :: Field a => [String] -> a-extraLibraries = buildInfo . A.ExtraLibraries--extraLibDirs :: Field a => [String] -> a-extraLibDirs = buildInfo . A.ExtraLibDirs--ccOptions :: Field a => [String] -> a-ccOptions = buildInfo . A.CCOptions--cppOptions :: Field a => [String] -> a-cppOptions = buildInfo . A.CPPOptions--ldOptions :: Field a => [String] -> a-ldOptions = buildInfo . A.LDOptions--pkgConfigDepends :: Field a => [A.Package] -> a-pkgConfigDepends = buildInfo . A.PkgConfigDepends--frameworks :: Field a => [String] -> a-frameworks = buildInfo . A.Frameworks--defaultLanguage :: Field a => A.Language -> a-defaultLanguage = buildInfo . A.DefaultLanguage---- | Builds @if@ statements. Use with the following functions, such--- as 'flag', to make it easy to build conditional blocks. For--- example:------ > cif (flag "buildExe") [buildable True] [buildable False]------ A little more complicated:------ > cif (flag "buildExe" &&& system "windows")--- > [buildable True] [buildable False]-cif- :: Field a- => A.CondTree- -- ^ Condition to satisfy- -> [a]- -- ^ Use these results if condition is true- -> [a]- -- ^ Use these results if condition if false- -> a-cif tree ifTrue ifFalse =- conditional $ A.CondBlock tree ifTrue ifFalse---- | Operating system; tested against @System.Info.os@ on the--- target system.-system :: String -> A.CondTree-system = A.CLeaf . A.OS---- | Argument is matched against @System.Info.arch@ on the target--- system.-arch :: String -> A.CondTree-arch = A.CLeaf . A.Arch---- | Tests for the configured Haskell implementation.-impl :: A.Compiler -> Maybe A.ConstrTree -> A.CondTree-impl cm cn = A.CLeaf $ A.Impl (cm, cn)---- | 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.-flag :: String -> A.CondTree-flag = A.CLeaf . A.CFlag---- | Always true.-true :: A.CondTree-true = A.CLeaf A.CTrue---- | Always false.-false :: A.CondTree-false = A.CLeaf A.CFalse----- | 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-
+ lib/Cartel/Types.hs view
@@ -0,0 +1,13 @@+module Cartel.Types where++-- | A non-empty string. This string should never be empty. It is+-- used where, for example, a field in a Cabal file is required to+-- have a value and that value cannot be empty. In contrast, Cartel+-- uses an ordinary 'String' for values that can be empty.+--+-- This is only a type synonym, so nothing in the type system enforces+-- that these strings must be non-empty. Typically though, Cabal will+-- give you grief about the file that Cartel generates if you used an+-- empty value for a 'NonEmptyString'.+type NonEmptyString = String+
+ lib/Cartel/Version.hs view
@@ -0,0 +1,14 @@+-- | 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++import Data.Word++version :: [Word]+version = [0,12,0,0]
− minimum-versions.txt
@@ -1,38 +0,0 @@-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-30 16:15:53.772569 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-11214/db:- cartel-0.10.0.0-
+ tests/Cartel/Generators.hs view
@@ -0,0 +1,373 @@+{-# LANGUAGE TypeSynonymInstances #-}+module Cartel.Generators where++import Cartel+import Cartel.Betsy+import Control.Applicative+import Control.Monad.Trans.State+import Control.Monad.Trans.Class+import System.Random (StdGen, random, randomR, Random(..), mkStdGen)+import Control.Monad.Trans.Reader+import Control.Monad++type Gen = ReaderT Word (Betsy (State StdGen))++class Arbitrary a where+ arbitrary :: Gen a++getSize :: Gen Word+getSize = ask++resize :: Word -> Gen a -> Gen a+resize w = withReaderT (const w)++getStdGen :: Gen StdGen+getStdGen = lift . lift $ get++setStdGen :: StdGen -> Gen ()+setStdGen = lift . lift . put++choose :: Random a => (a, a) -> Gen a+choose pair = do+ g <- getStdGen+ let (r, g') = randomR pair g+ setStdGen g'+ return r++chooseDefault :: Random a => Gen a+chooseDefault = do+ g <- getStdGen+ let (r, g') = random g+ setStdGen g'+ return r++frequency :: [(Int, Gen a)] -> Gen a+frequency [] = error "frequency used with empty list"+frequency xs0 = choose (1, tot) >>= (`pick` xs0)+ where+ tot = sum (map fst xs0)+ pick n ((k, x):xs)+ | n <= k = x+ | otherwise = pick (n - k) xs+ pick _ _ = error "pick used with empty list"++elements :: [a] -> Gen a+elements [] = error "elements used with empty list"+elements xs = (xs !!) `fmap` choose (0, length xs - 1)++oneOf :: [Gen a] -> Gen a+oneOf = join . elements++instance Arbitrary Bool where+ arbitrary = chooseDefault++instance Arbitrary Word where+ arbitrary = choose (0, 255)++instance Arbitrary a => Arbitrary (Maybe a) where+ arbitrary = frequency [(3, fmap Just arbitrary), (1, return Nothing)]++instance Arbitrary a => Arbitrary [a] where+ arbitrary = do+ sz <- getSize+ c <- choose (0, sz)+ replicateM (fromIntegral c) arbitrary++instance Arbitrary Char where+ arbitrary = choose ('a', 'z')++genNonEmptyString :: Gen NonEmptyString+genNonEmptyString = liftM2 (:) arbitrary (resize 5 arbitrary)++instance Arbitrary Vcs where+ arbitrary = oneOf $ fmap cvs genNonEmptyString+ : map return [ darcs, git, svn, mercurial,+ bazaar, archVcs, monotone]++instance Arbitrary RepoKind where+ arbitrary = elements [repoHead, repoThis]++instance Arbitrary Repository where+ arbitrary = Repository <$> arbitrary <*> arbitrary <*> arbitrary+ <*> genNonEmptyString <*> arbitrary <*> arbitrary++listOf :: Gen a -> Gen [a]+listOf g = do+ sz <- getSize+ len <- choose (0, sz)+ replicateM (fromIntegral len) g++genNonEmptyList :: Gen a -> Gen [a]+genNonEmptyList g = liftM2 (:) g (listOf g)++genVersion :: Gen Version+genVersion = resize 3 (genNonEmptyList (choose (0, 99)))++instance Arbitrary Constraint where+ arbitrary = do+ sz <- getSize+ lvls <- choose (0, sz)+ go lvls+ where+ go lvl | lvl == 0 = oneOf+ [ fmap lt genVersion+ , fmap gt genVersion+ , fmap eq genVersion+ , return anyVersion+ ]+ | otherwise = do+ l <- next+ r <- next+ op <- elements [ (&&&), (|||) ]+ return $ l `op` r+ where+ next = choose (0, lvl - 1) >>= go++instance Arbitrary Compiler where+ arbitrary = elements [ ghc, nhc, yhc, hugs, helium, jhc, lhc ]++instance Arbitrary Condition where+ arbitrary = do+ sz <- getSize+ lvls <- choose (0, sz)+ goCondition lvls++goCondition :: Word -> Gen Condition+goCondition lvl+ | lvl == 0 = lift currentFlags >>= lowest+ | otherwise = upper+ where+ lowest [] = nonFlagLevel+ lowest xs = elements (fmap fst xs) >>= withFlagLevel+ nonFlagLevel = oneOf [ fmap system genNonEmptyString+ , fmap arch genNonEmptyString+ , liftM2 impl arbitrary arbitrary+ , return true+ , return false+ ]+ withFlagLevel fl = frequency+ [ (2, nonFlagLevel), (1, return $ flag fl)]+ upper = frequency [(2, binary), (1, inverted)]+ where+ next = choose (0, lvl - 1) >>= goCondition+ inverted = fmap invert next+ binary = do+ op <- elements [(&&&), (|||)]+ liftM2 op next next++genCondBlock :: (Arbitrary a, HasBuildInfo a) => Gen a+genCondBlock = liftM3 condBlock arbitrary+ (liftM2 (,) arbitrary arbitrary) arbitrary++instance Arbitrary Package where+ arbitrary = liftM2 package genNonEmptyString arbitrary++genBuildInfo :: HasBuildInfo a => Gen a+genBuildInfo = oneOf+ [ return haskell98+ , return haskell2010+ , fmap buildDepends arbitrary+ , fmap otherModules neslist+ , fmap hsSourceDirs neslist+ , fmap extensions neslist+ , fmap buildTools (genNonEmptyList arbitrary)+ , fmap buildable arbitrary+ , fmap ghcOptions neslist+ , fmap ghcProfOptions neslist+ , fmap ghcSharedOptions neslist+ , fmap hugsOptions neslist+ , fmap nhc98Options neslist+ , fmap includes neslist+ , fmap installIncludes neslist+ , fmap includeDirs neslist+ , fmap cSources neslist+ , fmap extraLibraries neslist+ , fmap ccOptions neslist+ , fmap cppOptions neslist+ , fmap ldOptions neslist+ , fmap pkgConfigDepends (genNonEmptyList arbitrary)+ , fmap frameworks neslist+ ]+ where+ neslist = genNonEmptyList genNonEmptyString++genMainIs :: BuildsExe a => Gen a+genMainIs = fmap mainIs genNonEmptyString++instance Arbitrary FlagOpts where+ arbitrary = liftM3 FlagOpts arbitrary arbitrary arbitrary++addFlagsToState+ :: Word+ -- ^ How many flags?+ -> Gen ()+addFlagsToState nFlags = do+ let mkf = do+ nm <- genNonEmptyString+ opts <- arbitrary+ _ <- lift (makeFlag nm opts)+ return ()+ _ <- replicateM (fromIntegral nFlags) mkf+ return ()++downsize :: Gen a -> Gen a+downsize g = do+ sz <- getSize+ resize (sz - 1) g++instance Arbitrary LibraryField where+ arbitrary = getSize >>= f+ where+ nonNestGens =+ [ (6, genBuildInfo)+ , (1, fmap exposed arbitrary)+ , (1, fmap exposedModules (genNonEmptyList genNonEmptyString))+ ]+ f sz | sz == 0 = nonNest+ | otherwise = nest+ where+ nonNest = frequency nonNestGens+ nest = downsize . frequency $+ (1, genCondBlock) : nonNestGens+++instance Arbitrary ExecutableField where+ arbitrary = getSize >>= f+ where+ nonNestGens =+ [ (6, genBuildInfo)+ , (1, genMainIs)+ ]+ nestGens = (1, genCondBlock) : nonNestGens+ f sz | sz == 0 = frequency nonNestGens+ | otherwise = downsize $ frequency nestGens++instance Arbitrary TestSuiteField where+ arbitrary = getSize >>= f+ where+ nonNestGens =+ [ (7, genBuildInfo)+ , (1, fmap testModule genNonEmptyString)+ , (1, pure exitcodeStdio)+ , (1, pure detailed)+ , (1, genMainIs)+ ]+ nestGens = (1, genCondBlock) : nonNestGens+ f sz | sz == 0 = frequency nonNestGens+ | otherwise = downsize $ frequency nestGens++instance Arbitrary BenchmarkField where+ arbitrary = getSize >>= f+ where+ nonNest =+ [ (6, genBuildInfo)+ , (1, return exitcodeStdio)+ ]+ nest = (1, genCondBlock) : nonNest+ f sz | sz == 0 = frequency nonNest+ | otherwise = downsize $ frequency nest++instance Arbitrary BuildType where+ arbitrary = elements [simple, configure, make, custom]++instance Arbitrary License where+ arbitrary = elements [ gpl, agpl, lgpl, bsd2, bsd3, bsd4, mit, mpl,+ apache, publicDomain, allRightsReserved,+ otherLicense ]++instance (Arbitrary a, Arbitrary b) => Arbitrary (a, b) where+ arbitrary = liftM2 (,) arbitrary arbitrary++instance Arbitrary Properties where+ arbitrary = Properties+ <$> genNonEmptyString -- name+ <*> genVersion -- version+ <*> arbitrary -- cabalVersion+ <*> arbitrary -- buildType+ <*> arbitrary -- license+ <*> arbitrary -- licenseFile+ <*> listOf genNonEmptyString -- licenseFiles+ <*> arbitrary -- copyright+ <*> arbitrary -- author+ <*> arbitrary+ <*> arbitrary+ <*> arbitrary+ <*> arbitrary -- bugReports+ <*> arbitrary+ <*> arbitrary+ <*> arbitrary -- description+ <*> arbitrary+ <*> arbitrary+ <*> listOf genNonEmptyString -- dataFiles+ <*> arbitrary+ <*> listOf genNonEmptyString+ <*> listOf genNonEmptyString+ <*> listOf genNonEmptyString -- extraTmpFiles++instance Arbitrary Section where+ arbitrary = frequency [(3, nonRepo), (1, repo)]+ where+ nonRepo = oneOf+ [ liftM2 executable genNonEmptyString arbitrary+ , liftM2 testSuite genNonEmptyString arbitrary+ , liftM2 benchmark genNonEmptyString arbitrary+ ]+ repo = fmap repository arbitrary++instance Arbitrary Cabal where+ arbitrary = do+ sz <- getSize+ nFlags <- choose (0, sz)+ addFlagsToState nFlags+ liftM3 cabal arbitrary arbitrary arbitrary++-- | Generates Cabal text given a size parameter and int to generate a+-- seed. Read the function from the bottom up:+--+-- * generates a random computation in ReaderT+--+-- * runs the ReaderT with the given size, resulting in a 'Betsy'+--+-- * renders the 'Betsy', which results in a State computation with+-- the 'StdGen' as the state+--+-- * runs the State computation with an initialized generator,+-- resulting in a pair (a, s), where @s@ is the resulting state and+-- @a@ is an Either+--+-- * gets the Either, discards the resulting state, resulting in an+-- @Either Error String@+--+-- * transforms the @Error@ to a string+genCabalText+ :: Word+ -- ^ Size+ -> Int+ -- ^ Random seed+ -> Either String String+ -- ^ Either an error message, or the Cabal text.+genCabalText sz sd+ = either (Left . renderIndented 0) (Right . id)+ . fst+ . flip runState (mkStdGen sd)+ . renderBetsy+ . flip runReaderT sz+ $ arbitrary++genResult+ :: Gen a+ -- ^ Generator+ -> Word+ -- ^ How many flags?+ -> Word+ -- ^ Size+ -> Int+ -- ^ Random seed+ -> Either String a+genResult gen nFlags sz sd+ = either (Left . renderIndented 0) (Right . fst)+ . fst+ . flip runState (mkStdGen sd)+ . runBetsy+ . flip runReaderT sz+ $ (addFlagsToState nFlags >> gen)
+ tests/Cartel/Properties.hs view
@@ -0,0 +1,83 @@+{-# OPTIONS_GHC -fno-warn-missing-signatures #-}++-- | Cartel properties. For the time being, most (maybe all) of these+-- properties test the generators, rather than Cartel itself. I had+-- to debug generators that were stuck in an infinite loop.+module Cartel.Properties where++import Cartel+import Control.Monad+import Test.QuickCheck+import qualified Cartel.Generators as G++seconds :: Int -> Int+seconds = (* 10 ^ (6 :: Int))++newtype Little = Little Word+ deriving (Eq, Ord, Show)++instance Arbitrary Little where+ arbitrary = sized f+ where+ f sz = fmap Little (choose (0, min (fromIntegral sz) 10))++completesInSecs+ :: Show a+ => Int+ -- ^ How many seconds+ -> G.Gen a+ -> Little+ -- ^ How many flags+ -> Little+ -- ^ Size+ -> Int+ -- ^ Seed+ -> Property+completesInSecs time gen (Little nFlags) (Little sz) sd+ = within (seconds time) $+ length (show (G.genResult gen nFlags sz sd)) >= 0++prop_vcsCompletesInSecs = completesInSecs 10 (G.arbitrary :: G.Gen Vcs)++prop_constraintCompletesInSecs = completesInSecs 10+ (G.arbitrary :: G.Gen Constraint)++mainIsCompletesInSecs a = completesInSecs 10 (G.genMainIs `asTypeOf` a)++prop_conditionCompletesInSecs = completesInSecs 10+ (G.arbitrary :: G.Gen Condition)++buildInfoCompletesInSecs a = completesInSecs 10 (G.genBuildInfo `asTypeOf` a)++-- ExecutableField contents++prop_buildInfoExeCompletesInSecs = buildInfoCompletesInSecs+ (undefined :: G.Gen ExecutableField)++prop_condBlockExeCompletesInSecs = completesInSecs 10+ (G.genCondBlock :: G.Gen ExecutableField)++prop_exeMainIsCompletesInSecs = mainIsCompletesInSecs+ (undefined :: G.Gen ExecutableField)++prop_cabalCompletesInSecs = completesInSecs 10+ (G.arbitrary :: G.Gen Cabal)++prop_propertiesCompletesInSecs = completesInSecs 10+ (G.arbitrary :: G.Gen Properties)++prop_repositoryCompletesInSecs = completesInSecs 10+ (G.arbitrary :: G.Gen Repository)++prop_sectionCompletesInSecs = completesInSecs 10+ (G.arbitrary :: G.Gen Section)++prop_executableCompletesInSecs = completesInSecs 10+ (liftM2 executable G.genNonEmptyString G.arbitrary)++prop_benchmarkCompletesInSecs = completesInSecs 10+ (liftM2 benchmark G.genNonEmptyString G.arbitrary)++prop_testSuiteCompletesInSecs = completesInSecs 10+ (liftM2 testSuite G.genNonEmptyString G.arbitrary)+
+ tests/Decrees.hs view
@@ -0,0 +1,26 @@+-- | This module generated by the Quickpull package.+-- Quickpull is available at:+-- <http://www.github.com/massysett/quickpull>++module Decrees where++import Quickpull+import qualified Cartel.Properties++decrees :: [Decree]+decrees =++ [ Decree ( Meta {modDesc = ModDesc {modPath = "tests/Cartel/Properties.hs", modName = ["Cartel","Properties"]}, linenum = 40, qName = "prop_vcsCompletesInSecs"} ) ( Single Cartel.Properties.prop_vcsCompletesInSecs )+ , Decree ( Meta {modDesc = ModDesc {modPath = "tests/Cartel/Properties.hs", modName = ["Cartel","Properties"]}, linenum = 42, qName = "prop_constraintCompletesInSecs"} ) ( Single Cartel.Properties.prop_constraintCompletesInSecs )+ , Decree ( Meta {modDesc = ModDesc {modPath = "tests/Cartel/Properties.hs", modName = ["Cartel","Properties"]}, linenum = 47, qName = "prop_conditionCompletesInSecs"} ) ( Single Cartel.Properties.prop_conditionCompletesInSecs )+ , Decree ( Meta {modDesc = ModDesc {modPath = "tests/Cartel/Properties.hs", modName = ["Cartel","Properties"]}, linenum = 54, qName = "prop_buildInfoExeCompletesInSecs"} ) ( Single Cartel.Properties.prop_buildInfoExeCompletesInSecs )+ , Decree ( Meta {modDesc = ModDesc {modPath = "tests/Cartel/Properties.hs", modName = ["Cartel","Properties"]}, linenum = 57, qName = "prop_condBlockExeCompletesInSecs"} ) ( Single Cartel.Properties.prop_condBlockExeCompletesInSecs )+ , Decree ( Meta {modDesc = ModDesc {modPath = "tests/Cartel/Properties.hs", modName = ["Cartel","Properties"]}, linenum = 60, qName = "prop_exeMainIsCompletesInSecs"} ) ( Single Cartel.Properties.prop_exeMainIsCompletesInSecs )+ , Decree ( Meta {modDesc = ModDesc {modPath = "tests/Cartel/Properties.hs", modName = ["Cartel","Properties"]}, linenum = 63, qName = "prop_cabalCompletesInSecs"} ) ( Single Cartel.Properties.prop_cabalCompletesInSecs )+ , Decree ( Meta {modDesc = ModDesc {modPath = "tests/Cartel/Properties.hs", modName = ["Cartel","Properties"]}, linenum = 66, qName = "prop_propertiesCompletesInSecs"} ) ( Single Cartel.Properties.prop_propertiesCompletesInSecs )+ , Decree ( Meta {modDesc = ModDesc {modPath = "tests/Cartel/Properties.hs", modName = ["Cartel","Properties"]}, linenum = 69, qName = "prop_repositoryCompletesInSecs"} ) ( Single Cartel.Properties.prop_repositoryCompletesInSecs )+ , Decree ( Meta {modDesc = ModDesc {modPath = "tests/Cartel/Properties.hs", modName = ["Cartel","Properties"]}, linenum = 72, qName = "prop_sectionCompletesInSecs"} ) ( Single Cartel.Properties.prop_sectionCompletesInSecs )+ , Decree ( Meta {modDesc = ModDesc {modPath = "tests/Cartel/Properties.hs", modName = ["Cartel","Properties"]}, linenum = 75, qName = "prop_executableCompletesInSecs"} ) ( Single Cartel.Properties.prop_executableCompletesInSecs )+ , Decree ( Meta {modDesc = ModDesc {modPath = "tests/Cartel/Properties.hs", modName = ["Cartel","Properties"]}, linenum = 78, qName = "prop_benchmarkCompletesInSecs"} ) ( Single Cartel.Properties.prop_benchmarkCompletesInSecs )+ , Decree ( Meta {modDesc = ModDesc {modPath = "tests/Cartel/Properties.hs", modName = ["Cartel","Properties"]}, linenum = 81, qName = "prop_testSuiteCompletesInSecs"} ) ( Single Cartel.Properties.prop_testSuiteCompletesInSecs )+ ]
+ tests/cartel-properties.hs view
@@ -0,0 +1,7 @@+module Main where++import Quickpull+import Decrees++main :: IO ()+main = defaultMain decrees
+ tests/cartel-visual-test.hs view
@@ -0,0 +1,81 @@+-- | Generates random Cabal text. The program takes two arguments.+-- The first is a size parameter. This should be a non-negative+-- integer. Bigger sizes will result in bigger results generated.+-- The second argument is the random seed. It should be any integer.+-- Different seeds should generate different results.+module Main where++import Data.Word+import Cartel.Generators+import System.Environment+import Data.Either+import Text.Read (readMaybe)+import System.Random+import System.Exit+import qualified System.IO as IO+import Multiarg++maxSize :: Word+maxSize = 10++help :: (String -> String)+help pn = unlines+ [ pn ++ " - generates random Cabal text."+ , "Options:"+ , " --size INT - runs generator with the given size."+ , " INT must be non-negative. Larger INT will"+ , " result in larger results."+ , " --seed INT - runs generator with the given seed."+ , " Without these options, a random seed is chosen, and"+ , " a random size between 0 and " ++ show maxSize ++ " is chosen."+ ]++opts :: [OptSpec (IO (Either Word Int))]+opts =+ [ optSpec "" ["size"] (OneArg (fmap Left . readVal))+ , optSpec "" ["seed"] (OneArg (fmap Right . readVal))+ ]++failOnPosArg :: IO a+failOnPosArg = do+ pn <- getProgName+ fail $ pn ++ " does not accept positional arguments."++finalOptions :: [Either Word Int] -> IO (Word, Int)+finalOptions ls = do+ let (ws, is) = partitionEithers ls+ sz <- case safeLast ws of+ Nothing -> randomRIO (0, maxSize)+ Just x -> return x+ sd <- case safeLast is of+ Nothing -> randomIO+ Just x -> return x+ return (sz, sd)++safeLast :: [a] -> Maybe a+safeLast xs+ | null xs = Nothing+ | otherwise = Just . last $ xs++readVal :: Read a => String -> IO a+readVal s = maybe (fail $ "could not read string: " ++ s) return+ . readMaybe $ s++main :: IO ()+main = do+ eithersActs <- parseCommandLine help opts (const failOnPosArg)+ eithers <- sequence eithersActs+ (sz, sd) <- finalOptions eithers+ case genCabalText sz sd of+ Left e -> do+ IO.hPutStrLn IO.stderr "could not generate Cabal text."+ IO.hPutStr IO.stderr e+ exitFailure+ Right g -> printHeader sz sd >> putStr g >> exitSuccess++printHeader :: Word -> Int -> IO ()+printHeader sz sd = putStr $ unlines+ [ replicate 50 '-'+ , "-- below - size: " ++ show sz ++ " seed: " ++ show sd+ , replicate 50 '-'+ ]