haskell-ci 0.1.0.0 → 0.2
raw patch · 48 files changed
+4216/−2357 lines, 48 filesdep +HsYAMLdep +base-compatdep +generic-lensdep ~Cabaldep ~Diffdep ~ShellChecknew-component:exe:haskell-cinew-uploaderPVP ok
version bump matches the API change (PVP)
Dependencies added: HsYAML, base-compat, generic-lens, microlens, optparse-applicative, parsec, pretty, text
Dependency ranges changed: Cabal, Diff, ShellCheck, ansi-terminal, base, bytestring, containers, deepseq, directory, filepath, tasty-golden, transformers
API changes (from Hackage documentation)
- MakeTravisYml: Error :: String -> Diagnostic
- MakeTravisYml: Failure :: [e] -> Result e a
- MakeTravisYml: Info :: String -> Diagnostic
- MakeTravisYml: Options :: [String] -> Maybe FilePath -> [String] -> Maybe FilePath -> Config -> Config -> Options
- MakeTravisYml: Success :: [e] -> a -> Result e a
- MakeTravisYml: Warn :: String -> Diagnostic
- MakeTravisYml: [optCollections] :: Options -> [String]
- MakeTravisYml: [optConfigMorphism] :: Options -> Config -> Config
- MakeTravisYml: [optConfig] :: Options -> Maybe FilePath
- MakeTravisYml: [optOsx] :: Options -> [String]
- MakeTravisYml: [optOutput] :: Options -> Maybe FilePath
- MakeTravisYml: data Diagnostic
- MakeTravisYml: data Options
- MakeTravisYml: data Result e a
- MakeTravisYml: defOptions :: Options
- MakeTravisYml: formatDiagnostic :: Diagnostic -> String
- MakeTravisYml: formatDiagnostics :: [Diagnostic] -> String
- MakeTravisYml: instance (GHC.Classes.Eq e, GHC.Classes.Eq a) => GHC.Classes.Eq (MakeTravisYml.Result e a)
- MakeTravisYml: instance (GHC.Show.Show e, GHC.Show.Show a) => GHC.Show.Show (MakeTravisYml.Result e a)
- MakeTravisYml: instance Data.Foldable.Foldable MakeTravisYml.Project
- MakeTravisYml: instance Data.Traversable.Traversable MakeTravisYml.Project
- MakeTravisYml: instance GHC.Base.Functor (MakeTravisYml.Result e)
- MakeTravisYml: instance GHC.Base.Functor MakeTravisYml.Project
- MakeTravisYml: instance GHC.Base.Monoid a => GHC.Base.Monoid (MakeTravisYml.Result e a)
- MakeTravisYml: instance GHC.Base.Monoid a => GHC.Base.Semigroup (MakeTravisYml.Result e a)
- MakeTravisYml: instance GHC.Classes.Eq MakeTravisYml.Diagnostic
- MakeTravisYml: instance GHC.Classes.Eq MakeTravisYml.FilePathGlob
- MakeTravisYml: instance GHC.Classes.Eq MakeTravisYml.FilePathGlobRel
- MakeTravisYml: instance GHC.Classes.Eq MakeTravisYml.FilePathRoot
- MakeTravisYml: instance GHC.Classes.Eq MakeTravisYml.Fold
- MakeTravisYml: instance GHC.Classes.Eq MakeTravisYml.GlobPiece
- MakeTravisYml: instance GHC.Classes.Eq MakeTravisYml.Package
- MakeTravisYml: instance GHC.Classes.Ord MakeTravisYml.Fold
- MakeTravisYml: instance GHC.Enum.Bounded MakeTravisYml.Fold
- MakeTravisYml: instance GHC.Enum.Enum MakeTravisYml.Fold
- MakeTravisYml: instance GHC.Show.Show MakeTravisYml.Config
- MakeTravisYml: instance GHC.Show.Show MakeTravisYml.ConstraintSet
- MakeTravisYml: instance GHC.Show.Show MakeTravisYml.Diagnostic
- MakeTravisYml: instance GHC.Show.Show MakeTravisYml.FilePathGlob
- MakeTravisYml: instance GHC.Show.Show MakeTravisYml.FilePathGlobRel
- MakeTravisYml: instance GHC.Show.Show MakeTravisYml.FilePathRoot
- MakeTravisYml: instance GHC.Show.Show MakeTravisYml.Fold
- MakeTravisYml: instance GHC.Show.Show MakeTravisYml.GlobPiece
- MakeTravisYml: instance GHC.Show.Show MakeTravisYml.Package
- MakeTravisYml: instance GHC.Show.Show a => GHC.Show.Show (MakeTravisYml.Project a)
- MakeTravisYml: main :: IO ()
- MakeTravisYml: options :: [OptDescr (Result Diagnostic (Options -> Options))]
- MakeTravisYml: parseOptsNoCommands :: [String] -> IO (Options, [String], FilePath, [String])
- MakeTravisYml: travisFromConfigFile :: MonadIO m => ([String], Options) -> FilePath -> [String] -> YamlWriter m ()
- MakeTravisYml: type MakeTravisOutput = Result Diagnostic [String]
+ HaskellCI: Error :: String -> Diagnostic
+ HaskellCI: Failure :: [e] -> Result e a
+ HaskellCI: Info :: String -> Diagnostic
+ HaskellCI: Options :: [String] -> Maybe FilePath -> Maybe FilePath -> (Config -> Config) -> Options
+ HaskellCI: Success :: [e] -> a -> Result e a
+ HaskellCI: Warn :: String -> Diagnostic
+ HaskellCI: [optCollections] :: Options -> [String]
+ HaskellCI: [optConfigMorphism] :: Options -> Config -> Config
+ HaskellCI: [optConfig] :: Options -> Maybe FilePath
+ HaskellCI: [optOutput] :: Options -> Maybe FilePath
+ HaskellCI: data Diagnostic
+ HaskellCI: data Options
+ HaskellCI: data Result e a
+ HaskellCI: defaultOptions :: Options
+ HaskellCI: formatDiagnostic :: Diagnostic -> String
+ HaskellCI: formatDiagnostics :: [Diagnostic] -> String
+ HaskellCI: main :: IO ()
+ HaskellCI: parseTravis :: [String] -> IO (FilePath, Options)
+ HaskellCI: travisFromConfigFile :: MonadIO m => [String] -> Options -> FilePath -> YamlWriter m ()
+ HaskellCI: type MakeTravisOutput = Result Diagnostic [String]
+ HaskellCI.Cli: CommandDumpConfig :: Command
+ HaskellCI.Cli: CommandListGHC :: Command
+ HaskellCI.Cli: CommandRegenerate :: Command
+ HaskellCI.Cli: CommandTravis :: FilePath -> Command
+ HaskellCI.Cli: Options :: [String] -> Maybe FilePath -> Maybe FilePath -> (Config -> Config) -> Options
+ HaskellCI.Cli: [optCollections] :: Options -> [String]
+ HaskellCI.Cli: [optConfigMorphism] :: Options -> Config -> Config
+ HaskellCI.Cli: [optConfig] :: Options -> Maybe FilePath
+ HaskellCI.Cli: [optOutput] :: Options -> Maybe FilePath
+ HaskellCI.Cli: cliParserInfo :: ParserInfo (Command, Options)
+ HaskellCI.Cli: data Command
+ HaskellCI.Cli: data Options
+ HaskellCI.Cli: defaultOptions :: Options
+ HaskellCI.Cli: instance GHC.Base.Semigroup HaskellCI.Cli.Options
+ HaskellCI.Cli: instance GHC.Show.Show HaskellCI.Cli.Command
+ HaskellCI.Cli: optionsP :: Parser Options
+ HaskellCI.Cli: parseTravis :: [String] -> IO (FilePath, Options)
+ HaskellCI.Config: Config :: Maybe Version -> Maybe Jobs -> !TestedWithJobs -> !CopyFields -> [String] -> !Bool -> !Bool -> !Bool -> [Installed] -> !VersionRange -> !VersionRange -> !VersionRange -> !VersionRange -> !VersionRange -> !Bool -> [String] -> [String] -> Maybe String -> Set Fold -> !Bool -> !Bool -> Map Version String -> !VersionRange -> !Bool -> Set Version -> Set String -> !DoctestConfig -> !HLintConfig -> [ConstraintSet] -> [PrettyField] -> Config
+ HaskellCI.Config: Env :: Map Version String -> Env
+ HaskellCI.Config: [cfgAllowFailures] :: Config -> !VersionRange
+ HaskellCI.Config: [cfgApt] :: Config -> Set String
+ HaskellCI.Config: [cfgBenchmarks] :: Config -> !VersionRange
+ HaskellCI.Config: [cfgCabalInstallVersion] :: Config -> Maybe Version
+ HaskellCI.Config: [cfgCache] :: Config -> !Bool
+ HaskellCI.Config: [cfgCheck] :: Config -> !Bool
+ HaskellCI.Config: [cfgConstraintSets] :: Config -> [ConstraintSet]
+ HaskellCI.Config: [cfgCopyFields] :: Config -> !CopyFields
+ HaskellCI.Config: [cfgDoctest] :: Config -> !DoctestConfig
+ HaskellCI.Config: [cfgEnv] :: Config -> Map Version String
+ HaskellCI.Config: [cfgFolds] :: Config -> Set Fold
+ HaskellCI.Config: [cfgGhcHead] :: Config -> !Bool
+ HaskellCI.Config: [cfgHLint] :: Config -> !HLintConfig
+ HaskellCI.Config: [cfgHaddock] :: Config -> !VersionRange
+ HaskellCI.Config: [cfgInstallDeps] :: Config -> !Bool
+ HaskellCI.Config: [cfgInstalled] :: Config -> [Installed]
+ HaskellCI.Config: [cfgIrcChannels] :: Config -> [String]
+ HaskellCI.Config: [cfgJobs] :: Config -> Maybe Jobs
+ HaskellCI.Config: [cfgLastInSeries] :: Config -> !Bool
+ HaskellCI.Config: [cfgLocalGhcOptions] :: Config -> [String]
+ HaskellCI.Config: [cfgNoTestsNoBench] :: Config -> !VersionRange
+ HaskellCI.Config: [cfgNoise] :: Config -> !Bool
+ HaskellCI.Config: [cfgOnlyBranches] :: Config -> [String]
+ HaskellCI.Config: [cfgOsx] :: Config -> Set Version
+ HaskellCI.Config: [cfgPostgres] :: Config -> !Bool
+ HaskellCI.Config: [cfgProjectName] :: Config -> Maybe String
+ HaskellCI.Config: [cfgRawProject] :: Config -> [PrettyField]
+ HaskellCI.Config: [cfgTestedWith] :: Config -> !TestedWithJobs
+ HaskellCI.Config: [cfgTests] :: Config -> !VersionRange
+ HaskellCI.Config: [cfgUnconstrainted] :: Config -> !VersionRange
+ HaskellCI.Config: configGrammar :: (OptionsGrammar g, Applicative (g Config), Applicative (g DoctestConfig), Applicative (g HLintConfig)) => g Config Config
+ HaskellCI.Config: data Config
+ HaskellCI.Config: defaultCabalInstallVersion :: Maybe Version
+ HaskellCI.Config: emptyConfig :: Config
+ HaskellCI.Config: instance Distribution.Compat.Newtype.Newtype HaskellCI.Config.Env (Data.Map.Internal.Map Distribution.Types.Version.Version GHC.Base.String)
+ HaskellCI.Config: instance Distribution.Parsec.Class.Parsec HaskellCI.Config.Env
+ HaskellCI.Config: instance Distribution.Pretty.Pretty HaskellCI.Config.Env
+ HaskellCI.Config: instance GHC.Generics.Generic HaskellCI.Config.Config
+ HaskellCI.Config: instance GHC.Show.Show HaskellCI.Config.Config
+ HaskellCI.Config: newtype Env
+ HaskellCI.Config: parseConfigFile :: [Field Position] -> ParseResult Config
+ HaskellCI.Config: parseName :: Position -> [SectionArg Position] -> ParseResult String
+ HaskellCI.Config: parseNameBS :: Position -> [SectionArg Position] -> ParseResult ByteString
+ HaskellCI.Config: readConfigFile :: MonadIO m => FilePath -> m Config
+ HaskellCI.Config.ConstraintSet: ConstraintSet :: String -> VersionRange -> [String] -> Bool -> ConstraintSet
+ HaskellCI.Config.ConstraintSet: [csConstraints] :: ConstraintSet -> [String]
+ HaskellCI.Config.ConstraintSet: [csGhcVersions] :: ConstraintSet -> VersionRange
+ HaskellCI.Config.ConstraintSet: [csName] :: ConstraintSet -> String
+ HaskellCI.Config.ConstraintSet: [csRunTests] :: ConstraintSet -> Bool
+ HaskellCI.Config.ConstraintSet: constraintSetGrammar :: (FieldGrammar g, Applicative (g ConstraintSet)) => String -> g ConstraintSet ConstraintSet
+ HaskellCI.Config.ConstraintSet: data ConstraintSet
+ HaskellCI.Config.ConstraintSet: emptyConstraintSet :: String -> ConstraintSet
+ HaskellCI.Config.ConstraintSet: instance GHC.Generics.Generic HaskellCI.Config.ConstraintSet.ConstraintSet
+ HaskellCI.Config.ConstraintSet: instance GHC.Show.Show HaskellCI.Config.ConstraintSet.ConstraintSet
+ HaskellCI.Config.CopyFields: CopyFieldsAll :: CopyFields
+ HaskellCI.Config.CopyFields: CopyFieldsNone :: CopyFields
+ HaskellCI.Config.CopyFields: CopyFieldsSome :: CopyFields
+ HaskellCI.Config.CopyFields: data CopyFields
+ HaskellCI.Config.CopyFields: instance Distribution.Parsec.Class.Parsec HaskellCI.Config.CopyFields.CopyFields
+ HaskellCI.Config.CopyFields: instance Distribution.Pretty.Pretty HaskellCI.Config.CopyFields.CopyFields
+ HaskellCI.Config.CopyFields: instance GHC.Classes.Eq HaskellCI.Config.CopyFields.CopyFields
+ HaskellCI.Config.CopyFields: instance GHC.Classes.Ord HaskellCI.Config.CopyFields.CopyFields
+ HaskellCI.Config.CopyFields: instance GHC.Enum.Bounded HaskellCI.Config.CopyFields.CopyFields
+ HaskellCI.Config.CopyFields: instance GHC.Enum.Enum HaskellCI.Config.CopyFields.CopyFields
+ HaskellCI.Config.CopyFields: instance GHC.Show.Show HaskellCI.Config.CopyFields.CopyFields
+ HaskellCI.Config.CopyFields: showCopyFields :: CopyFields -> String
+ HaskellCI.Config.Doctest: DoctestConfig :: !Bool -> [String] -> !VersionRange -> DoctestConfig
+ HaskellCI.Config.Doctest: [cfgDoctestEnabled] :: DoctestConfig -> !Bool
+ HaskellCI.Config.Doctest: [cfgDoctestOptions] :: DoctestConfig -> [String]
+ HaskellCI.Config.Doctest: [cfgDoctestVersion] :: DoctestConfig -> !VersionRange
+ HaskellCI.Config.Doctest: data DoctestConfig
+ HaskellCI.Config.Doctest: defaultDoctestVersion :: VersionRange
+ HaskellCI.Config.Doctest: doctestConfigGrammar :: (OptionsGrammar g, Applicative (g DoctestConfig)) => g DoctestConfig DoctestConfig
+ HaskellCI.Config.Doctest: instance GHC.Generics.Generic HaskellCI.Config.Doctest.DoctestConfig
+ HaskellCI.Config.Doctest: instance GHC.Show.Show HaskellCI.Config.Doctest.DoctestConfig
+ HaskellCI.Config.Dump: DG :: [String] -> DumpGrammar s a
+ HaskellCI.Config.Dump: [runDG] :: DumpGrammar s a -> [String]
+ HaskellCI.Config.Dump: instance Distribution.FieldGrammar.Class.FieldGrammar HaskellCI.Config.Dump.DumpGrammar
+ HaskellCI.Config.Dump: instance GHC.Base.Applicative (HaskellCI.Config.Dump.DumpGrammar s)
+ HaskellCI.Config.Dump: instance GHC.Base.Functor (HaskellCI.Config.Dump.DumpGrammar s)
+ HaskellCI.Config.Dump: instance HaskellCI.OptionsGrammar.OptionsGrammar HaskellCI.Config.Dump.DumpGrammar
+ HaskellCI.Config.Dump: newtype DumpGrammar s a
+ HaskellCI.Config.Folds: FoldBuild :: Fold
+ HaskellCI.Config.Folds: FoldBuildEverything :: Fold
+ HaskellCI.Config.Folds: FoldBuildInstalled :: Fold
+ HaskellCI.Config.Folds: FoldCheck :: Fold
+ HaskellCI.Config.Folds: FoldConstraintSets :: Fold
+ HaskellCI.Config.Folds: FoldDoctest :: Fold
+ HaskellCI.Config.Folds: FoldHLint :: Fold
+ HaskellCI.Config.Folds: FoldHaddock :: Fold
+ HaskellCI.Config.Folds: FoldSDist :: Fold
+ HaskellCI.Config.Folds: FoldStackage :: Fold
+ HaskellCI.Config.Folds: FoldTest :: Fold
+ HaskellCI.Config.Folds: FoldUnpack :: Fold
+ HaskellCI.Config.Folds: Folds :: Set Fold -> Folds
+ HaskellCI.Config.Folds: [getFolds] :: Folds -> Set Fold
+ HaskellCI.Config.Folds: data Fold
+ HaskellCI.Config.Folds: instance Distribution.Compat.Newtype.Newtype HaskellCI.Config.Folds.Folds (Data.Set.Internal.Set HaskellCI.Config.Folds.Fold)
+ HaskellCI.Config.Folds: instance Distribution.Parsec.Class.Parsec HaskellCI.Config.Folds.Folds
+ HaskellCI.Config.Folds: instance Distribution.Pretty.Pretty HaskellCI.Config.Folds.Folds
+ HaskellCI.Config.Folds: instance GHC.Classes.Eq HaskellCI.Config.Folds.Fold
+ HaskellCI.Config.Folds: instance GHC.Classes.Ord HaskellCI.Config.Folds.Fold
+ HaskellCI.Config.Folds: instance GHC.Enum.Bounded HaskellCI.Config.Folds.Fold
+ HaskellCI.Config.Folds: instance GHC.Enum.Enum HaskellCI.Config.Folds.Fold
+ HaskellCI.Config.Folds: instance GHC.Show.Show HaskellCI.Config.Folds.Fold
+ HaskellCI.Config.Folds: newtype Folds
+ HaskellCI.Config.Folds: possibleFolds :: [Fold]
+ HaskellCI.Config.Folds: showFold :: Fold -> String
+ HaskellCI.Config.HLint: HLintConfig :: !Bool -> !HLintJob -> !Maybe FilePath -> [String] -> !VersionRange -> HLintConfig
+ HaskellCI.Config.HLint: HLintJob :: Version -> HLintJob
+ HaskellCI.Config.HLint: HLintJobLatest :: HLintJob
+ HaskellCI.Config.HLint: [cfgHLintEnabled] :: HLintConfig -> !Bool
+ HaskellCI.Config.HLint: [cfgHLintJob] :: HLintConfig -> !HLintJob
+ HaskellCI.Config.HLint: [cfgHLintOptions] :: HLintConfig -> [String]
+ HaskellCI.Config.HLint: [cfgHLintVersion] :: HLintConfig -> !VersionRange
+ HaskellCI.Config.HLint: [cfgHLintYaml] :: HLintConfig -> !Maybe FilePath
+ HaskellCI.Config.HLint: data HLintConfig
+ HaskellCI.Config.HLint: data HLintJob
+ HaskellCI.Config.HLint: defaultHLintVersion :: VersionRange
+ HaskellCI.Config.HLint: hlintConfigGrammar :: (OptionsGrammar g, Applicative (g HLintConfig)) => g HLintConfig HLintConfig
+ HaskellCI.Config.HLint: instance Distribution.Parsec.Class.Parsec HaskellCI.Config.HLint.HLintJob
+ HaskellCI.Config.HLint: instance Distribution.Pretty.Pretty HaskellCI.Config.HLint.HLintJob
+ HaskellCI.Config.HLint: instance GHC.Classes.Eq HaskellCI.Config.HLint.HLintJob
+ HaskellCI.Config.HLint: instance GHC.Generics.Generic HaskellCI.Config.HLint.HLintConfig
+ HaskellCI.Config.HLint: instance GHC.Show.Show HaskellCI.Config.HLint.HLintConfig
+ HaskellCI.Config.HLint: instance GHC.Show.Show HaskellCI.Config.HLint.HLintJob
+ HaskellCI.Config.Installed: Add :: PackageName -> Installed
+ HaskellCI.Config.Installed: InstalledAll :: Installed
+ HaskellCI.Config.Installed: InstalledDiff :: Set PackageName -> InstalledNormalised
+ HaskellCI.Config.Installed: InstalledNone :: Installed
+ HaskellCI.Config.Installed: InstalledOnly :: Set PackageName -> InstalledNormalised
+ HaskellCI.Config.Installed: Remove :: PackageName -> Installed
+ HaskellCI.Config.Installed: data Installed
+ HaskellCI.Config.Installed: data InstalledNormalised
+ HaskellCI.Config.Installed: instance Distribution.Parsec.Class.Parsec HaskellCI.Config.Installed.Installed
+ HaskellCI.Config.Installed: instance Distribution.Pretty.Pretty HaskellCI.Config.Installed.Installed
+ HaskellCI.Config.Installed: instance GHC.Classes.Eq HaskellCI.Config.Installed.Installed
+ HaskellCI.Config.Installed: instance GHC.Show.Show HaskellCI.Config.Installed.Installed
+ HaskellCI.Config.Installed: instance GHC.Show.Show HaskellCI.Config.Installed.InstalledNormalised
+ HaskellCI.Config.Installed: normaliseInstalled :: [Installed] -> InstalledNormalised
+ HaskellCI.Config.Jobs: BothJobs :: Int -> Int -> Jobs
+ HaskellCI.Config.Jobs: CabalJobs :: Int -> Jobs
+ HaskellCI.Config.Jobs: GhcJobs :: Int -> Jobs
+ HaskellCI.Config.Jobs: cabalJobs :: Jobs -> Maybe Int
+ HaskellCI.Config.Jobs: data Jobs
+ HaskellCI.Config.Jobs: ghcJobs :: Jobs -> Maybe Int
+ HaskellCI.Config.Jobs: instance Distribution.Parsec.Class.Parsec HaskellCI.Config.Jobs.Jobs
+ HaskellCI.Config.Jobs: instance Distribution.Pretty.Pretty HaskellCI.Config.Jobs.Jobs
+ HaskellCI.Config.Jobs: instance GHC.Show.Show HaskellCI.Config.Jobs.Jobs
+ HaskellCI.Extras: afterInfix :: Eq a => [a] -> [a] -> Maybe [a]
+ HaskellCI.Extras: afterPrefix :: Eq a => [a] -> [a] -> Maybe [a]
+ HaskellCI.Extras: findMaybe :: (a -> Maybe b) -> [a] -> Maybe b
+ HaskellCI.GHC: dispGhcVersion :: Maybe Version -> String
+ HaskellCI.GHC: ghcAlpha :: Maybe Version
+ HaskellCI.GHC: ghcMajVer :: Version -> (Int, Int)
+ HaskellCI.GHC: knownGhcVersions :: [Version]
+ HaskellCI.GHC: previewGHC :: Maybe Version -> Bool
+ HaskellCI.Glob: FilePathGlob :: FilePathRoot -> FilePathGlobRel -> FilePathGlob
+ HaskellCI.Glob: FilePathHomeDir :: FilePathRoot
+ HaskellCI.Glob: FilePathRelative :: FilePathRoot
+ HaskellCI.Glob: FilePathRoot :: FilePath -> FilePathRoot
+ HaskellCI.Glob: GlobDir :: Glob -> FilePathGlobRel -> FilePathGlobRel
+ HaskellCI.Glob: GlobDirTrailing :: FilePathGlobRel
+ HaskellCI.Glob: GlobFile :: Glob -> FilePathGlobRel
+ HaskellCI.Glob: Literal :: String -> GlobPiece
+ HaskellCI.Glob: Union :: [Glob] -> GlobPiece
+ HaskellCI.Glob: WildCard :: GlobPiece
+ HaskellCI.Glob: data FilePathGlob
+ HaskellCI.Glob: data FilePathGlobRel
+ HaskellCI.Glob: data FilePathRoot
+ HaskellCI.Glob: data GlobPiece
+ HaskellCI.Glob: expandRelGlob :: MonadIO m => FilePath -> FilePathGlobRel -> m [FilePath]
+ HaskellCI.Glob: instance GHC.Classes.Eq HaskellCI.Glob.FilePathGlob
+ HaskellCI.Glob: instance GHC.Classes.Eq HaskellCI.Glob.FilePathGlobRel
+ HaskellCI.Glob: instance GHC.Classes.Eq HaskellCI.Glob.FilePathRoot
+ HaskellCI.Glob: instance GHC.Classes.Eq HaskellCI.Glob.GlobPiece
+ HaskellCI.Glob: instance GHC.Show.Show HaskellCI.Glob.FilePathGlob
+ HaskellCI.Glob: instance GHC.Show.Show HaskellCI.Glob.FilePathGlobRel
+ HaskellCI.Glob: instance GHC.Show.Show HaskellCI.Glob.FilePathRoot
+ HaskellCI.Glob: instance GHC.Show.Show HaskellCI.Glob.GlobPiece
+ HaskellCI.Glob: isGlobEscapedChar :: Char -> Bool
+ HaskellCI.Glob: matchGlob :: Glob -> FilePath -> Bool
+ HaskellCI.Glob: parseFilePathGlobRel :: ReadP r FilePathGlobRel
+ HaskellCI.Glob: parseGlob :: ReadP r Glob
+ HaskellCI.Glob: type Glob = [GlobPiece]
+ HaskellCI.MakeTravisOutput: Error :: String -> Diagnostic
+ HaskellCI.MakeTravisOutput: Failure :: [e] -> Result e a
+ HaskellCI.MakeTravisOutput: Info :: String -> Diagnostic
+ HaskellCI.MakeTravisOutput: Row :: String -> Row
+ HaskellCI.MakeTravisOutput: RowErr :: String -> Row
+ HaskellCI.MakeTravisOutput: RowSkip :: Row
+ HaskellCI.MakeTravisOutput: Success :: [e] -> a -> Result e a
+ HaskellCI.MakeTravisOutput: Warn :: String -> Diagnostic
+ HaskellCI.MakeTravisOutput: blank :: Row
+ HaskellCI.MakeTravisOutput: comment :: String -> Row
+ HaskellCI.MakeTravisOutput: data Diagnostic
+ HaskellCI.MakeTravisOutput: data Result e a
+ HaskellCI.MakeTravisOutput: data Row
+ HaskellCI.MakeTravisOutput: foldedTellStrLns :: Monad m => Fold -> String -> Set Fold -> YamlWriter m () -> YamlWriter m ()
+ HaskellCI.MakeTravisOutput: foldedTellStrLns' :: Monad m => Fold -> String -> String -> Set Fold -> YamlWriter m () -> YamlWriter m ()
+ HaskellCI.MakeTravisOutput: formatDiagnostic :: Diagnostic -> String
+ HaskellCI.MakeTravisOutput: formatDiagnostics :: [Diagnostic] -> String
+ HaskellCI.MakeTravisOutput: instance (GHC.Classes.Eq e, GHC.Classes.Eq a) => GHC.Classes.Eq (HaskellCI.MakeTravisOutput.Result e a)
+ HaskellCI.MakeTravisOutput: instance (GHC.Show.Show e, GHC.Show.Show a) => GHC.Show.Show (HaskellCI.MakeTravisOutput.Result e a)
+ HaskellCI.MakeTravisOutput: instance Data.String.IsString HaskellCI.MakeTravisOutput.Row
+ HaskellCI.MakeTravisOutput: instance GHC.Base.Functor (HaskellCI.MakeTravisOutput.Result e)
+ HaskellCI.MakeTravisOutput: instance GHC.Base.Monoid a => GHC.Base.Monoid (HaskellCI.MakeTravisOutput.Result e a)
+ HaskellCI.MakeTravisOutput: instance GHC.Base.Monoid a => GHC.Base.Semigroup (HaskellCI.MakeTravisOutput.Result e a)
+ HaskellCI.MakeTravisOutput: instance GHC.Classes.Eq HaskellCI.MakeTravisOutput.Diagnostic
+ HaskellCI.MakeTravisOutput: instance GHC.Show.Show HaskellCI.MakeTravisOutput.Diagnostic
+ HaskellCI.MakeTravisOutput: putStrLnErr :: Monad m => String -> YamlWriter m a
+ HaskellCI.MakeTravisOutput: putStrLnErrs :: Monad m => [String] -> YamlWriter m ()
+ HaskellCI.MakeTravisOutput: putStrLnInfo :: Monad m => String -> YamlWriter m ()
+ HaskellCI.MakeTravisOutput: putStrLnWarn :: Monad m => String -> YamlWriter m ()
+ HaskellCI.MakeTravisOutput: rawRow :: String -> Row
+ HaskellCI.MakeTravisOutput: sequenceRows :: [Row] -> Either String [String]
+ HaskellCI.MakeTravisOutput: sh :: String -> Row
+ HaskellCI.MakeTravisOutput: sh' :: [Integer] -> String -> Row
+ HaskellCI.MakeTravisOutput: shForJob :: Set Version -> VersionRange -> String -> Row
+ HaskellCI.MakeTravisOutput: shImpl :: String -> String
+ HaskellCI.MakeTravisOutput: success :: a -> Result e a
+ HaskellCI.MakeTravisOutput: tellStrLn :: Monad m => String -> YamlWriter m ()
+ HaskellCI.MakeTravisOutput: tellStrLns :: Monad m => [Row] -> YamlWriter m ()
+ HaskellCI.MakeTravisOutput: tellStrLnsRaw :: Monad m => [String] -> YamlWriter m ()
+ HaskellCI.MakeTravisOutput: type MakeTravisOutput = Result Diagnostic [String]
+ HaskellCI.MakeTravisOutput: type YamlWriter m a = MaybeT (WriterT MakeTravisOutput m) a
+ HaskellCI.Newtypes: AlaSet :: Set a -> AlaSet sep b a
+ HaskellCI.Newtypes: HeadVersion :: Maybe Version -> HeadVersion
+ HaskellCI.Newtypes: Int' :: Int -> Int'
+ HaskellCI.Newtypes: NoCommas :: String -> NoCommas
+ HaskellCI.Newtypes: PackageLocation :: String -> PackageLocation
+ HaskellCI.Newtypes: Range :: VersionRange -> Range
+ HaskellCI.Newtypes: [getAlaSet] :: AlaSet sep b a -> Set a
+ HaskellCI.Newtypes: [getHeadVersion] :: HeadVersion -> Maybe Version
+ HaskellCI.Newtypes: alaSet :: sep -> Set a -> AlaSet sep (Identity a) a
+ HaskellCI.Newtypes: alaSet' :: sep -> (a -> b) -> Set a -> AlaSet sep b a
+ HaskellCI.Newtypes: class Sep sep
+ HaskellCI.Newtypes: instance (Distribution.Compat.Newtype.Newtype b a, GHC.Classes.Ord a, HaskellCI.Newtypes.Sep sep, Distribution.Parsec.Class.Parsec b) => Distribution.Parsec.Class.Parsec (HaskellCI.Newtypes.AlaSet sep b a)
+ HaskellCI.Newtypes: instance (Distribution.Compat.Newtype.Newtype b a, HaskellCI.Newtypes.Sep sep, Distribution.Pretty.Pretty b) => Distribution.Pretty.Pretty (HaskellCI.Newtypes.AlaSet sep b a)
+ HaskellCI.Newtypes: instance Distribution.Compat.Newtype.Newtype (HaskellCI.Newtypes.AlaSet sep wrapper a) (Data.Set.Internal.Set a)
+ HaskellCI.Newtypes: instance Distribution.Compat.Newtype.Newtype HaskellCI.Newtypes.HeadVersion (GHC.Maybe.Maybe Distribution.Types.Version.Version)
+ HaskellCI.Newtypes: instance Distribution.Compat.Newtype.Newtype HaskellCI.Newtypes.Int' GHC.Types.Int
+ HaskellCI.Newtypes: instance Distribution.Compat.Newtype.Newtype HaskellCI.Newtypes.NoCommas [GHC.Types.Char]
+ HaskellCI.Newtypes: instance Distribution.Compat.Newtype.Newtype HaskellCI.Newtypes.PackageLocation [GHC.Types.Char]
+ HaskellCI.Newtypes: instance Distribution.Compat.Newtype.Newtype HaskellCI.Newtypes.Range Distribution.Types.VersionRange.VersionRange
+ HaskellCI.Newtypes: instance Distribution.Parsec.Class.Parsec HaskellCI.Newtypes.HeadVersion
+ HaskellCI.Newtypes: instance Distribution.Parsec.Class.Parsec HaskellCI.Newtypes.Int'
+ HaskellCI.Newtypes: instance Distribution.Parsec.Class.Parsec HaskellCI.Newtypes.NoCommas
+ HaskellCI.Newtypes: instance Distribution.Parsec.Class.Parsec HaskellCI.Newtypes.PackageLocation
+ HaskellCI.Newtypes: instance Distribution.Parsec.Class.Parsec HaskellCI.Newtypes.Range
+ HaskellCI.Newtypes: instance Distribution.Pretty.Pretty HaskellCI.Newtypes.HeadVersion
+ HaskellCI.Newtypes: instance Distribution.Pretty.Pretty HaskellCI.Newtypes.Int'
+ HaskellCI.Newtypes: instance Distribution.Pretty.Pretty HaskellCI.Newtypes.NoCommas
+ HaskellCI.Newtypes: instance Distribution.Pretty.Pretty HaskellCI.Newtypes.PackageLocation
+ HaskellCI.Newtypes: instance Distribution.Pretty.Pretty HaskellCI.Newtypes.Range
+ HaskellCI.Newtypes: instance HaskellCI.Newtypes.Sep Distribution.Parsec.Newtypes.CommaFSep
+ HaskellCI.Newtypes: instance HaskellCI.Newtypes.Sep Distribution.Parsec.Newtypes.CommaVCat
+ HaskellCI.Newtypes: instance HaskellCI.Newtypes.Sep Distribution.Parsec.Newtypes.NoCommaFSep
+ HaskellCI.Newtypes: newtype AlaSet sep b a
+ HaskellCI.Newtypes: newtype HeadVersion
+ HaskellCI.Newtypes: newtype Int'
+ HaskellCI.Newtypes: newtype NoCommas
+ HaskellCI.Newtypes: newtype PackageLocation
+ HaskellCI.Newtypes: newtype Range
+ HaskellCI.Newtypes: parseSep :: (Sep sep, CabalParsing m) => Proxy sep -> m a -> m [a]
+ HaskellCI.Newtypes: prettySep :: Sep sep => Proxy sep -> [Doc] -> Doc
+ HaskellCI.Optimization: OptimizationLevel :: Int -> Optimization
+ HaskellCI.Optimization: OptimizationOff :: Optimization
+ HaskellCI.Optimization: OptimizationOn :: Optimization
+ HaskellCI.Optimization: data Optimization
+ HaskellCI.Optimization: instance Distribution.Parsec.Class.Parsec HaskellCI.Optimization.Optimization
+ HaskellCI.Optimization: instance Distribution.Pretty.Pretty HaskellCI.Optimization.Optimization
+ HaskellCI.Optimization: instance GHC.Classes.Eq HaskellCI.Optimization.Optimization
+ HaskellCI.Optimization: instance GHC.Show.Show HaskellCI.Optimization.Optimization
+ HaskellCI.OptionsGrammar: (^^^) :: () => a -> (a -> b) -> b
+ HaskellCI.OptionsGrammar: class FieldGrammar p => OptionsGrammar p
+ HaskellCI.OptionsGrammar: help :: OptionsGrammar p => String -> p s a -> p s a
+ HaskellCI.OptionsGrammar: infixl 5 ^^^
+ HaskellCI.OptionsGrammar: instance HaskellCI.OptionsGrammar.OptionsGrammar Distribution.FieldGrammar.Parsec.ParsecFieldGrammar
+ HaskellCI.OptionsGrammar: metahelp :: OptionsGrammar p => String -> String -> p s a -> p s a
+ HaskellCI.OptionsGrammar: rangeField :: OptionsGrammar p => FieldName -> ALens' s VersionRange -> VersionRange -> p s VersionRange
+ HaskellCI.OptparseGrammar: data OptparseGrammar s a
+ HaskellCI.OptparseGrammar: instance Distribution.FieldGrammar.Class.FieldGrammar HaskellCI.OptparseGrammar.OptparseGrammar
+ HaskellCI.OptparseGrammar: instance GHC.Base.Applicative (HaskellCI.OptparseGrammar.OptparseGrammar s)
+ HaskellCI.OptparseGrammar: instance GHC.Base.Functor (HaskellCI.OptparseGrammar.OptparseGrammar s)
+ HaskellCI.OptparseGrammar: instance HaskellCI.OptionsGrammar.OptionsGrammar HaskellCI.OptparseGrammar.OptparseGrammar
+ HaskellCI.OptparseGrammar: runOptparseGrammar :: OptparseGrammar s a -> Parser (s -> s)
+ HaskellCI.Package: Pkg :: String -> VersionRange -> FilePath -> GenericPackageDescription -> Package
+ HaskellCI.Package: [pkgDir] :: Package -> FilePath
+ HaskellCI.Package: [pkgGpd] :: Package -> GenericPackageDescription
+ HaskellCI.Package: [pkgJobs] :: Package -> VersionRange
+ HaskellCI.Package: [pkgName] :: Package -> String
+ HaskellCI.Package: data Package
+ HaskellCI.Package: instance GHC.Classes.Eq HaskellCI.Package.Package
+ HaskellCI.Package: instance GHC.Show.Show HaskellCI.Package.Package
+ HaskellCI.ParsecError: renderParseError :: FilePath -> ByteString -> [PError] -> [PWarning] -> String
+ HaskellCI.ParsecUtils: readAndParseFile :: ([Field Position] -> ParseResult a) -> FilePath -> IO a
+ HaskellCI.Project: Project :: [a] -> [String] -> [String] -> Bool -> Maybe Int -> Optimization -> [SourceRepo] -> [PrettyField] -> Project a
+ HaskellCI.Project: [prjAllowNewer] :: Project a -> [String]
+ HaskellCI.Project: [prjConstraints] :: Project a -> [String]
+ HaskellCI.Project: [prjMaxBackjumps] :: Project a -> Maybe Int
+ HaskellCI.Project: [prjOptimization] :: Project a -> Optimization
+ HaskellCI.Project: [prjOrigFields] :: Project a -> [PrettyField]
+ HaskellCI.Project: [prjPackages] :: Project a -> [a]
+ HaskellCI.Project: [prjReorderGoals] :: Project a -> Bool
+ HaskellCI.Project: [prjSourceRepos] :: Project a -> [SourceRepo]
+ HaskellCI.Project: data Project a
+ HaskellCI.Project: emptyProject :: Project [a]
+ HaskellCI.Project: instance Data.Foldable.Foldable HaskellCI.Project.Project
+ HaskellCI.Project: instance Data.Traversable.Traversable HaskellCI.Project.Project
+ HaskellCI.Project: instance GHC.Base.Functor HaskellCI.Project.Project
+ HaskellCI.Project: instance GHC.Generics.Generic (HaskellCI.Project.Project a)
+ HaskellCI.Project: instance GHC.Show.Show a => GHC.Show.Show (HaskellCI.Project.Project a)
+ HaskellCI.Project: parseProjectFile :: FilePath -> ByteString -> Either String (Project String)
+ HaskellCI.TestedWith: TestedWithAny :: TestedWithJobs
+ HaskellCI.TestedWith: TestedWithUniform :: TestedWithJobs
+ HaskellCI.TestedWith: checkVersions :: TestedWithJobs -> Project (Package, Set Version) -> Either [String] (Set Version, Project Package)
+ HaskellCI.TestedWith: data TestedWithJobs
+ HaskellCI.TestedWith: instance Distribution.Parsec.Class.Parsec HaskellCI.TestedWith.TestedWithJobs
+ HaskellCI.TestedWith: instance Distribution.Pretty.Pretty HaskellCI.TestedWith.TestedWithJobs
+ HaskellCI.TestedWith: instance GHC.Classes.Eq HaskellCI.TestedWith.TestedWithJobs
+ HaskellCI.TestedWith: instance GHC.Show.Show HaskellCI.TestedWith.TestedWithJobs
+ HaskellCI.Version: equivVersionRanges :: VersionRange -> VersionRange -> Bool
+ HaskellCI.Version: ghcVersionPredicate :: VersionRange -> String
+ HaskellCI.Version: ghcVersionToString :: Version -> String
Files
- Main.hs +0/−2
- cli/Main.hs +2/−0
- fixtures/cabal.haskell-ci +15/−4
- fixtures/cabal.project.copy-fields.all +15/−0
- fixtures/cabal.project.copy-fields.all.stderr +1/−0
- fixtures/cabal.project.copy-fields.all.travis.yml +164/−0
- fixtures/cabal.project.copy-fields.none +15/−0
- fixtures/cabal.project.copy-fields.none.stderr +1/−0
- fixtures/cabal.project.copy-fields.none.travis.yml +154/−0
- fixtures/cabal.project.copy-fields.some +15/−0
- fixtures/cabal.project.copy-fields.some.stderr +1/−0
- fixtures/cabal.project.copy-fields.some.travis.yml +160/−0
- fixtures/cabal.project.empty-line.stderr +1/−1
- fixtures/cabal.project.empty-line.travis.yml +94/−99
- fixtures/cabal.project.messy.stderr +1/−1
- fixtures/cabal.project.messy.travis.yml +91/−98
- fixtures/haskell-ci.cabal +132/−80
- fixtures/haskell-ci.cabal.stderr +2/−1
- fixtures/haskell-ci.cabal.travis.yml +86/−77
- haskell-ci.cabal +132/−80
- src/Distribution/Fields/Pretty.hs +134/−0
- src/HaskellCI.hs +1029/−0
- src/HaskellCI/Cli.hs +91/−0
- src/HaskellCI/Config.hs +263/−0
- src/HaskellCI/Config/ConstraintSet.hs +35/−0
- src/HaskellCI/Config/CopyFields.hs +32/−0
- src/HaskellCI/Config/Doctest.hs +37/−0
- src/HaskellCI/Config/Dump.hs +58/−0
- src/HaskellCI/Config/Folds.hs +74/−0
- src/HaskellCI/Config/HLint.hs +65/−0
- src/HaskellCI/Config/Installed.hs +71/−0
- src/HaskellCI/Config/Jobs.hs +56/−0
- src/HaskellCI/Extras.hs +34/−0
- src/HaskellCI/GHC.hs +36/−0
- src/HaskellCI/Glob.hs +153/−0
- src/HaskellCI/MakeTravisOutput.hs +212/−0
- src/HaskellCI/Newtypes.hs +178/−0
- src/HaskellCI/Optimization.hs +27/−0
- src/HaskellCI/OptionsGrammar.hs +24/−0
- src/HaskellCI/OptparseGrammar.hs +124/−0
- src/HaskellCI/Package.hs +12/−0
- src/HaskellCI/ParsecError.hs +108/−0
- src/HaskellCI/ParsecUtils.hs +42/−0
- src/HaskellCI/Project.hs +91/−0
- src/HaskellCI/TestedWith.hs +91/−0
- src/HaskellCI/Version.hs +42/−0
- src/MakeTravisYml.hs +0/−1905
- test/Tests.hs +15/−9
− Main.hs
@@ -1,2 +0,0 @@-module Main (main) where-import MakeTravisYml (main)
+ cli/Main.hs view
@@ -0,0 +1,2 @@+module Main (main) where+import HaskellCI (main)
fixtures/cabal.haskell-ci view
@@ -1,8 +1,11 @@ -- Most settings can be given as command line arguments--- Config file is convinient if you maintain large amount of packages+-- Config file is convenient if you maintain large amount of packages jobs: 2:2 +-- Add GHC head job+ghc-head: True+ -- travis Caching -- cache: True @@ -21,9 +24,8 @@ -- no-tests-no-bench: True -- By default `installed` constraints are used for packages--- in global db. Unconstrained step is a build without them.--- Note: solver might or might not pick newer versions.--- unconstrained-step: True+-- in global db. We can modify which constraints are used.+installed: +all -Cabal -- Options for local packages local-ghc-options: -Werror@@ -48,6 +50,9 @@ doctest-options: --fast -- doctest-version: ==0.13.* +-- macOS job+osx: 8.4.4+ -- Constraint sets -- Package will be build with different constraints. -- This is useful to check compatibility with older versions of dependencies.@@ -55,3 +60,9 @@ constraint-set deepseq-1.4 ghc: (>= 7.8 && <7.10) || == 8.2.2 constraints: deepseq ==1.4.*++-- Include these fields "as is" in generated cabal.project+raw-project+ keep-going: False+ package bytestring+ tests: False
+ fixtures/cabal.project.copy-fields.all view
@@ -0,0 +1,15 @@+packages:+ servant/+ servant-client/+ servant-docs/+ servant-server/++package servant+ tests: False++constraints: foundation >= 0.14++allow-newer:+ servant-js:servant+allow-newer:+ servant-js:servant-foreign
+ fixtures/cabal.project.copy-fields.all.stderr view
@@ -0,0 +1,1 @@+*INFO* Generating Travis-CI config for testing for GHC versions: 7.8.1 7.8.2 7.8.3 7.8.4 7.10.1 7.10.2 7.10.3 8.0.1 8.0.2 8.2.1 8.2.2 8.4.1 8.4.2 8.4.3 8.4.4 8.6.1 8.6.2 8.6.3
+ fixtures/cabal.project.copy-fields.all.travis.yml view
@@ -0,0 +1,164 @@+# This Travis job script has been generated by a script via+#+# haskell-ci '-o' 'cabal.project.empty-line.travis.yml' '--copy-fields=all' 'cabal.project.empty-line'+#+# For more information, see https://github.com/haskell-CI/haskell-ci+#+# version: 0.2+#+language: c+dist: xenial++git:+ submodules: false # whether to recursively clone submodules++cache:+ directories:+ - $HOME/.cabal/packages+ - $HOME/.cabal/store++before_cache:+ - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log+ # remove files that are regenerated by 'cabal update'+ - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.*+ - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx++ - rm -rfv $CABALHOME/packages/head.hackage++matrix:+ include:+ - compiler: "ghc-8.6.3"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.3], sources: [hvr-ghc]}}+ - compiler: "ghc-8.6.2"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.2], sources: [hvr-ghc]}}+ - compiler: "ghc-8.6.1"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.1], sources: [hvr-ghc]}}+ - compiler: "ghc-8.4.4"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.4], sources: [hvr-ghc]}}+ - compiler: "ghc-8.4.3"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.3], sources: [hvr-ghc]}}+ - compiler: "ghc-8.4.2"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.2], sources: [hvr-ghc]}}+ - compiler: "ghc-8.4.1"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.1], sources: [hvr-ghc]}}+ - compiler: "ghc-8.2.2"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.2.2], sources: [hvr-ghc]}}+ - compiler: "ghc-8.2.1"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.2.1], sources: [hvr-ghc]}}+ - compiler: "ghc-8.0.2"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.0.2], sources: [hvr-ghc]}}+ - compiler: "ghc-8.0.1"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.0.1], sources: [hvr-ghc]}}+ - compiler: "ghc-7.10.3"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.10.3], sources: [hvr-ghc]}}+ - compiler: "ghc-7.10.2"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.10.2], sources: [hvr-ghc]}}+ - compiler: "ghc-7.10.1"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.10.1], sources: [hvr-ghc]}}+ - compiler: "ghc-7.8.4"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.4], sources: [hvr-ghc]}}+ - compiler: "ghc-7.8.3"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.3], sources: [hvr-ghc]}}+ - compiler: "ghc-7.8.2"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.2], sources: [hvr-ghc]}}+ - compiler: "ghc-7.8.1"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.1], sources: [hvr-ghc]}}++before_install:+ - HC=/opt/ghc/bin/${CC}+ - HCPKG=${HC/ghc/ghc-pkg}+ - unset CC+ - CABAL=/opt/ghc/bin/cabal+ - CABALHOME=$HOME/.cabal+ - export PATH="$CABALHOME/bin:$PATH"+ - ROOTDIR=$(pwd)+ - HCNUMVER=$(( $(${HC} --numeric-version|sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+).*/\1 * 10000 + \2 * 100 + \3/') ))+ - echo $HCNUMVER++install:+ - ${CABAL} --version+ - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"+ - TEST=--enable-tests+ - BENCH=--enable-benchmarks+ - GHCHEAD=${GHCHEAD-false}+ - travis_retry ${CABAL} update -v+ - sed -i.bak 's/^jobs:/-- jobs:/' $CABALHOME/config+ - rm -fv cabal.project cabal.project.local+ - grep -Ev -- '^\s*--' $CABALHOME/config | grep -Ev '^\s*$'+ - rm -f cabal.project+ - touch cabal.project+ - "printf 'packages: \"servant\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-client\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-docs\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-server\"\\n' >> cabal.project"+ - echo 'package servant' >> cabal.project+ - "echo ' tests: False' >> cabal.project"+ - "echo 'constraints: foundation >= 0.14' >> cabal.project"+ - "echo 'allow-newer: servant-js:servant' >> cabal.project"+ - "echo 'allow-newer: servant-js:servant-foreign' >> cabal.project"+ - "printf 'write-ghc-environment-files: always\\n' >> cabal.project"+ - touch cabal.project.local+ - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | grep -vE -- '^(servant|servant-client|servant-docs|servant-server)$' | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"+ - cat cabal.project || true+ - cat cabal.project.local || true+ - if [ -f "servant/configure.ac" ]; then (cd "servant" && autoreconf -i); fi+ - if [ -f "servant-client/configure.ac" ]; then (cd "servant-client" && autoreconf -i); fi+ - if [ -f "servant-docs/configure.ac" ]; then (cd "servant-docs" && autoreconf -i); fi+ - if [ -f "servant-server/configure.ac" ]; then (cd "servant-server" && autoreconf -i); fi+ - rm -f cabal.project.freeze+ - ${CABAL} new-freeze -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project.copy-fields.all" --dry+ - "cat \"cabal.project.copy-fields.all.freeze\" | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'"+ - rm "cabal.project.copy-fields.all.freeze"+ - ${CABAL} new-build -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project.copy-fields.all" --dep -j2 all+ - ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks --project-file="cabal.project.copy-fields.all" --dep -j2 all+ - rm -rf .ghc.environment.* "servant"/dist "servant-client"/dist "servant-docs"/dist "servant-server"/dist+ - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)++# Here starts the actual work to be performed for the package under test;+# any command which exits with a non-zero exit code causes the build to fail.+script:+ # test that source-distributions can be generated+ - ${CABAL} new-sdist all+ - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/+ - cd ${DISTDIR} || false+ - find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \;+ - rm -f cabal.project+ - touch cabal.project+ - "printf 'packages: \"servant-*/*.cabal\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-client-*/*.cabal\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-docs-*/*.cabal\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-server-*/*.cabal\"\\n' >> cabal.project"+ - echo 'package servant' >> cabal.project+ - "echo ' tests: False' >> cabal.project"+ - "echo 'constraints: foundation >= 0.14' >> cabal.project"+ - "echo 'allow-newer: servant-js:servant' >> cabal.project"+ - "echo 'allow-newer: servant-js:servant-foreign' >> cabal.project"+ - "printf 'write-ghc-environment-files: always\\n' >> cabal.project"+ - touch cabal.project.local+ - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | grep -vE -- '^(servant|servant-client|servant-docs|servant-server)$' | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"+ - cat cabal.project || true+ - cat cabal.project.local || true+ # this builds all libraries and executables (without tests/benchmarks)+ - ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks all++ # build & run tests, build benchmarks+ - ${CABAL} new-build -w ${HC} ${TEST} ${BENCH} all+ - if [ "x$TEST" = "x--enable-tests" ]; then ${CABAL} new-test -w ${HC} ${TEST} ${BENCH} all; fi++ # cabal check+ - (cd servant-* && ${CABAL} check)+ - (cd servant-client-* && ${CABAL} check)+ - (cd servant-docs-* && ${CABAL} check)+ - (cd servant-server-* && ${CABAL} check)++ # haddock+ - ${CABAL} new-haddock -w ${HC} ${TEST} ${BENCH} all++ # Build without installed constraints for packages in global-db+ - rm -f cabal.project.local; ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks all;++# REGENDATA ["-o","cabal.project.empty-line.travis.yml","--copy-fields=all","cabal.project.empty-line"]+# EOF
+ fixtures/cabal.project.copy-fields.none view
@@ -0,0 +1,15 @@+packages:+ servant/+ servant-client/+ servant-docs/+ servant-server/++package servant+ tests: False++constraints: foundation >= 0.14++allow-newer:+ servant-js:servant+allow-newer:+ servant-js:servant-foreign
+ fixtures/cabal.project.copy-fields.none.stderr view
@@ -0,0 +1,1 @@+*INFO* Generating Travis-CI config for testing for GHC versions: 7.8.1 7.8.2 7.8.3 7.8.4 7.10.1 7.10.2 7.10.3 8.0.1 8.0.2 8.2.1 8.2.2 8.4.1 8.4.2 8.4.3 8.4.4 8.6.1 8.6.2 8.6.3
+ fixtures/cabal.project.copy-fields.none.travis.yml view
@@ -0,0 +1,154 @@+# This Travis job script has been generated by a script via+#+# haskell-ci '-o' 'cabal.project.empty-line.travis.yml' '--copy-fields=none' 'cabal.project.empty-line'+#+# For more information, see https://github.com/haskell-CI/haskell-ci+#+# version: 0.2+#+language: c+dist: xenial++git:+ submodules: false # whether to recursively clone submodules++cache:+ directories:+ - $HOME/.cabal/packages+ - $HOME/.cabal/store++before_cache:+ - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log+ # remove files that are regenerated by 'cabal update'+ - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.*+ - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx++ - rm -rfv $CABALHOME/packages/head.hackage++matrix:+ include:+ - compiler: "ghc-8.6.3"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.3], sources: [hvr-ghc]}}+ - compiler: "ghc-8.6.2"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.2], sources: [hvr-ghc]}}+ - compiler: "ghc-8.6.1"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.1], sources: [hvr-ghc]}}+ - compiler: "ghc-8.4.4"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.4], sources: [hvr-ghc]}}+ - compiler: "ghc-8.4.3"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.3], sources: [hvr-ghc]}}+ - compiler: "ghc-8.4.2"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.2], sources: [hvr-ghc]}}+ - compiler: "ghc-8.4.1"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.1], sources: [hvr-ghc]}}+ - compiler: "ghc-8.2.2"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.2.2], sources: [hvr-ghc]}}+ - compiler: "ghc-8.2.1"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.2.1], sources: [hvr-ghc]}}+ - compiler: "ghc-8.0.2"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.0.2], sources: [hvr-ghc]}}+ - compiler: "ghc-8.0.1"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.0.1], sources: [hvr-ghc]}}+ - compiler: "ghc-7.10.3"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.10.3], sources: [hvr-ghc]}}+ - compiler: "ghc-7.10.2"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.10.2], sources: [hvr-ghc]}}+ - compiler: "ghc-7.10.1"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.10.1], sources: [hvr-ghc]}}+ - compiler: "ghc-7.8.4"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.4], sources: [hvr-ghc]}}+ - compiler: "ghc-7.8.3"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.3], sources: [hvr-ghc]}}+ - compiler: "ghc-7.8.2"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.2], sources: [hvr-ghc]}}+ - compiler: "ghc-7.8.1"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.1], sources: [hvr-ghc]}}++before_install:+ - HC=/opt/ghc/bin/${CC}+ - HCPKG=${HC/ghc/ghc-pkg}+ - unset CC+ - CABAL=/opt/ghc/bin/cabal+ - CABALHOME=$HOME/.cabal+ - export PATH="$CABALHOME/bin:$PATH"+ - ROOTDIR=$(pwd)+ - HCNUMVER=$(( $(${HC} --numeric-version|sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+).*/\1 * 10000 + \2 * 100 + \3/') ))+ - echo $HCNUMVER++install:+ - ${CABAL} --version+ - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"+ - TEST=--enable-tests+ - BENCH=--enable-benchmarks+ - GHCHEAD=${GHCHEAD-false}+ - travis_retry ${CABAL} update -v+ - sed -i.bak 's/^jobs:/-- jobs:/' $CABALHOME/config+ - rm -fv cabal.project cabal.project.local+ - grep -Ev -- '^\s*--' $CABALHOME/config | grep -Ev '^\s*$'+ - rm -f cabal.project+ - touch cabal.project+ - "printf 'packages: \"servant\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-client\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-docs\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-server\"\\n' >> cabal.project"+ - "printf 'write-ghc-environment-files: always\\n' >> cabal.project"+ - touch cabal.project.local+ - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | grep -vE -- '^(servant|servant-client|servant-docs|servant-server)$' | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"+ - cat cabal.project || true+ - cat cabal.project.local || true+ - if [ -f "servant/configure.ac" ]; then (cd "servant" && autoreconf -i); fi+ - if [ -f "servant-client/configure.ac" ]; then (cd "servant-client" && autoreconf -i); fi+ - if [ -f "servant-docs/configure.ac" ]; then (cd "servant-docs" && autoreconf -i); fi+ - if [ -f "servant-server/configure.ac" ]; then (cd "servant-server" && autoreconf -i); fi+ - rm -f cabal.project.freeze+ - ${CABAL} new-freeze -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project.copy-fields.none" --dry+ - "cat \"cabal.project.copy-fields.none.freeze\" | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'"+ - rm "cabal.project.copy-fields.none.freeze"+ - ${CABAL} new-build -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project.copy-fields.none" --dep -j2 all+ - ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks --project-file="cabal.project.copy-fields.none" --dep -j2 all+ - rm -rf .ghc.environment.* "servant"/dist "servant-client"/dist "servant-docs"/dist "servant-server"/dist+ - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)++# Here starts the actual work to be performed for the package under test;+# any command which exits with a non-zero exit code causes the build to fail.+script:+ # test that source-distributions can be generated+ - ${CABAL} new-sdist all+ - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/+ - cd ${DISTDIR} || false+ - find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \;+ - rm -f cabal.project+ - touch cabal.project+ - "printf 'packages: \"servant-*/*.cabal\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-client-*/*.cabal\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-docs-*/*.cabal\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-server-*/*.cabal\"\\n' >> cabal.project"+ - "printf 'write-ghc-environment-files: always\\n' >> cabal.project"+ - touch cabal.project.local+ - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | grep -vE -- '^(servant|servant-client|servant-docs|servant-server)$' | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"+ - cat cabal.project || true+ - cat cabal.project.local || true+ # this builds all libraries and executables (without tests/benchmarks)+ - ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks all++ # build & run tests, build benchmarks+ - ${CABAL} new-build -w ${HC} ${TEST} ${BENCH} all+ - if [ "x$TEST" = "x--enable-tests" ]; then ${CABAL} new-test -w ${HC} ${TEST} ${BENCH} all; fi++ # cabal check+ - (cd servant-* && ${CABAL} check)+ - (cd servant-client-* && ${CABAL} check)+ - (cd servant-docs-* && ${CABAL} check)+ - (cd servant-server-* && ${CABAL} check)++ # haddock+ - ${CABAL} new-haddock -w ${HC} ${TEST} ${BENCH} all++ # Build without installed constraints for packages in global-db+ - rm -f cabal.project.local; ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks all;++# REGENDATA ["-o","cabal.project.empty-line.travis.yml","--copy-fields=none","cabal.project.empty-line"]+# EOF
+ fixtures/cabal.project.copy-fields.some view
@@ -0,0 +1,15 @@+packages:+ servant/+ servant-client/+ servant-docs/+ servant-server/++package servant+ tests: False++constraints: foundation >= 0.14++allow-newer:+ servant-js:servant+allow-newer:+ servant-js:servant-foreign
+ fixtures/cabal.project.copy-fields.some.stderr view
@@ -0,0 +1,1 @@+*INFO* Generating Travis-CI config for testing for GHC versions: 7.8.1 7.8.2 7.8.3 7.8.4 7.10.1 7.10.2 7.10.3 8.0.1 8.0.2 8.2.1 8.2.2 8.4.1 8.4.2 8.4.3 8.4.4 8.6.1 8.6.2 8.6.3
+ fixtures/cabal.project.copy-fields.some.travis.yml view
@@ -0,0 +1,160 @@+# This Travis job script has been generated by a script via+#+# haskell-ci '-o' 'cabal.project.empty-line.travis.yml' '--copy-fields=some' 'cabal.project.empty-line'+#+# For more information, see https://github.com/haskell-CI/haskell-ci+#+# version: 0.2+#+language: c+dist: xenial++git:+ submodules: false # whether to recursively clone submodules++cache:+ directories:+ - $HOME/.cabal/packages+ - $HOME/.cabal/store++before_cache:+ - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log+ # remove files that are regenerated by 'cabal update'+ - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.*+ - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx++ - rm -rfv $CABALHOME/packages/head.hackage++matrix:+ include:+ - compiler: "ghc-8.6.3"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.3], sources: [hvr-ghc]}}+ - compiler: "ghc-8.6.2"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.2], sources: [hvr-ghc]}}+ - compiler: "ghc-8.6.1"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.1], sources: [hvr-ghc]}}+ - compiler: "ghc-8.4.4"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.4], sources: [hvr-ghc]}}+ - compiler: "ghc-8.4.3"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.3], sources: [hvr-ghc]}}+ - compiler: "ghc-8.4.2"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.2], sources: [hvr-ghc]}}+ - compiler: "ghc-8.4.1"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.1], sources: [hvr-ghc]}}+ - compiler: "ghc-8.2.2"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.2.2], sources: [hvr-ghc]}}+ - compiler: "ghc-8.2.1"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.2.1], sources: [hvr-ghc]}}+ - compiler: "ghc-8.0.2"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.0.2], sources: [hvr-ghc]}}+ - compiler: "ghc-8.0.1"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.0.1], sources: [hvr-ghc]}}+ - compiler: "ghc-7.10.3"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.10.3], sources: [hvr-ghc]}}+ - compiler: "ghc-7.10.2"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.10.2], sources: [hvr-ghc]}}+ - compiler: "ghc-7.10.1"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.10.1], sources: [hvr-ghc]}}+ - compiler: "ghc-7.8.4"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.4], sources: [hvr-ghc]}}+ - compiler: "ghc-7.8.3"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.3], sources: [hvr-ghc]}}+ - compiler: "ghc-7.8.2"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.2], sources: [hvr-ghc]}}+ - compiler: "ghc-7.8.1"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.1], sources: [hvr-ghc]}}++before_install:+ - HC=/opt/ghc/bin/${CC}+ - HCPKG=${HC/ghc/ghc-pkg}+ - unset CC+ - CABAL=/opt/ghc/bin/cabal+ - CABALHOME=$HOME/.cabal+ - export PATH="$CABALHOME/bin:$PATH"+ - ROOTDIR=$(pwd)+ - HCNUMVER=$(( $(${HC} --numeric-version|sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+).*/\1 * 10000 + \2 * 100 + \3/') ))+ - echo $HCNUMVER++install:+ - ${CABAL} --version+ - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"+ - TEST=--enable-tests+ - BENCH=--enable-benchmarks+ - GHCHEAD=${GHCHEAD-false}+ - travis_retry ${CABAL} update -v+ - sed -i.bak 's/^jobs:/-- jobs:/' $CABALHOME/config+ - rm -fv cabal.project cabal.project.local+ - grep -Ev -- '^\s*--' $CABALHOME/config | grep -Ev '^\s*$'+ - rm -f cabal.project+ - touch cabal.project+ - "printf 'packages: \"servant\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-client\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-docs\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-server\"\\n' >> cabal.project"+ - "echo 'constraints: foundation >= 0.14' >> cabal.project"+ - "echo 'allow-newer: servant-js:servant' >> cabal.project"+ - "echo 'allow-newer: servant-js:servant-foreign' >> cabal.project"+ - "printf 'write-ghc-environment-files: always\\n' >> cabal.project"+ - touch cabal.project.local+ - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | grep -vE -- '^(servant|servant-client|servant-docs|servant-server)$' | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"+ - cat cabal.project || true+ - cat cabal.project.local || true+ - if [ -f "servant/configure.ac" ]; then (cd "servant" && autoreconf -i); fi+ - if [ -f "servant-client/configure.ac" ]; then (cd "servant-client" && autoreconf -i); fi+ - if [ -f "servant-docs/configure.ac" ]; then (cd "servant-docs" && autoreconf -i); fi+ - if [ -f "servant-server/configure.ac" ]; then (cd "servant-server" && autoreconf -i); fi+ - rm -f cabal.project.freeze+ - ${CABAL} new-freeze -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project.copy-fields.some" --dry+ - "cat \"cabal.project.copy-fields.some.freeze\" | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'"+ - rm "cabal.project.copy-fields.some.freeze"+ - ${CABAL} new-build -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project.copy-fields.some" --dep -j2 all+ - ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks --project-file="cabal.project.copy-fields.some" --dep -j2 all+ - rm -rf .ghc.environment.* "servant"/dist "servant-client"/dist "servant-docs"/dist "servant-server"/dist+ - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)++# Here starts the actual work to be performed for the package under test;+# any command which exits with a non-zero exit code causes the build to fail.+script:+ # test that source-distributions can be generated+ - ${CABAL} new-sdist all+ - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/+ - cd ${DISTDIR} || false+ - find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \;+ - rm -f cabal.project+ - touch cabal.project+ - "printf 'packages: \"servant-*/*.cabal\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-client-*/*.cabal\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-docs-*/*.cabal\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-server-*/*.cabal\"\\n' >> cabal.project"+ - "echo 'constraints: foundation >= 0.14' >> cabal.project"+ - "echo 'allow-newer: servant-js:servant' >> cabal.project"+ - "echo 'allow-newer: servant-js:servant-foreign' >> cabal.project"+ - "printf 'write-ghc-environment-files: always\\n' >> cabal.project"+ - touch cabal.project.local+ - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | grep -vE -- '^(servant|servant-client|servant-docs|servant-server)$' | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"+ - cat cabal.project || true+ - cat cabal.project.local || true+ # this builds all libraries and executables (without tests/benchmarks)+ - ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks all++ # build & run tests, build benchmarks+ - ${CABAL} new-build -w ${HC} ${TEST} ${BENCH} all+ - if [ "x$TEST" = "x--enable-tests" ]; then ${CABAL} new-test -w ${HC} ${TEST} ${BENCH} all; fi++ # cabal check+ - (cd servant-* && ${CABAL} check)+ - (cd servant-client-* && ${CABAL} check)+ - (cd servant-docs-* && ${CABAL} check)+ - (cd servant-server-* && ${CABAL} check)++ # haddock+ - ${CABAL} new-haddock -w ${HC} ${TEST} ${BENCH} all++ # Build without installed constraints for packages in global-db+ - rm -f cabal.project.local; ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks all;++# REGENDATA ["-o","cabal.project.empty-line.travis.yml","--copy-fields=some","cabal.project.empty-line"]+# EOF
fixtures/cabal.project.empty-line.stderr view
@@ -1,1 +1,1 @@-*INFO* Generating Travis-CI config for testing for GHC versions: head 7.8.1 7.8.2 7.8.3 7.8.4 7.10.1 7.10.2 7.10.3 8.0.1 8.0.2 8.2.1 8.2.2 8.4.1 8.4.2 8.4.3 8.6.1+*INFO* Generating Travis-CI config for testing for GHC versions: head 7.8.1 7.8.2 7.8.3 7.8.4 7.10.1 7.10.2 7.10.3 8.0.1 8.0.2 8.2.1 8.2.2 8.4.1 8.4.2 8.4.3 8.4.4 8.6.1 8.6.2 8.6.3
fixtures/cabal.project.empty-line.travis.yml view
@@ -1,11 +1,13 @@ # This Travis job script has been generated by a script via #-# runghc make_travis_yml_2.hs '--ghc-head' 'cabal.project.empty-line'+# haskell-ci '-o' 'cabal.project.empty-line.travis.yml' '--ghc-head' 'cabal.project.empty-line' # # For more information, see https://github.com/haskell-CI/haskell-ci #+# version: 0.2+# language: c-sudo: false+dist: xenial git: submodules: false # whether to recursively clone submodules@@ -16,134 +18,124 @@ - $HOME/.cabal/store before_cache:- - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log+ - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log # remove files that are regenerated by 'cabal update'- - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.*- - rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json- - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache- - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar- - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx+ - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.*+ - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx - - rm -rfv $HOME/.cabal/packages/head.hackage+ - rm -rfv $CABALHOME/packages/head.hackage matrix: include:+ - compiler: "ghc-8.6.3"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.3], sources: [hvr-ghc]}}+ - compiler: "ghc-8.6.2"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.2], sources: [hvr-ghc]}} - compiler: "ghc-8.6.1"- env: GHCHEAD=true- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.6.1], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.1], sources: [hvr-ghc]}}+ - compiler: "ghc-8.4.4"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.4], sources: [hvr-ghc]}} - compiler: "ghc-8.4.3"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.4.3], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.3], sources: [hvr-ghc]}} - compiler: "ghc-8.4.2"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.4.2], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.2], sources: [hvr-ghc]}} - compiler: "ghc-8.4.1"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.4.1], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.1], sources: [hvr-ghc]}} - compiler: "ghc-8.2.2"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.2.2], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.2.2], sources: [hvr-ghc]}} - compiler: "ghc-8.2.1"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.2.1], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.2.1], sources: [hvr-ghc]}} - compiler: "ghc-8.0.2"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.0.2], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.0.2], sources: [hvr-ghc]}} - compiler: "ghc-8.0.1"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.0.1], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.0.1], sources: [hvr-ghc]}} - compiler: "ghc-7.10.3"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.10.3], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.10.3], sources: [hvr-ghc]}} - compiler: "ghc-7.10.2"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.10.2], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.10.2], sources: [hvr-ghc]}} - compiler: "ghc-7.10.1"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.10.1], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.10.1], sources: [hvr-ghc]}} - compiler: "ghc-7.8.4"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.8.4], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.4], sources: [hvr-ghc]}} - compiler: "ghc-7.8.3"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.8.3], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.3], sources: [hvr-ghc]}} - compiler: "ghc-7.8.2"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.8.2], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.2], sources: [hvr-ghc]}} - compiler: "ghc-7.8.1"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.8.1], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.1], sources: [hvr-ghc]}} - compiler: "ghc-head" env: GHCHEAD=true addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-head], sources: [hvr-ghc]}} allow_failures: - compiler: "ghc-head"- - compiler: "ghc-8.6.1" before_install:- - HC=${CC}+ - HC=/opt/ghc/bin/${CC} - HCPKG=${HC/ghc/ghc-pkg} - unset CC+ - CABAL=/opt/ghc/bin/cabal+ - CABALHOME=$HOME/.cabal+ - export PATH="$CABALHOME/bin:$PATH" - ROOTDIR=$(pwd)- - mkdir -p $HOME/.local/bin- - "PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$HOME/local/bin:$PATH" - HCNUMVER=$(( $(${HC} --numeric-version|sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+).*/\1 * 10000 + \2 * 100 + \3/') )) - echo $HCNUMVER install:- - cabal --version+ - ${CABAL} --version - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"- - BENCH=${BENCH---enable-benchmarks}- - TEST=${TEST---enable-tests}- - HADDOCK=${HADDOCK-true}- - UNCONSTRAINED=${UNCONSTRAINED-true}- - NOINSTALLEDCONSTRAINTS=${NOINSTALLEDCONSTRAINTS-false}+ - TEST=--enable-tests+ - BENCH=--enable-benchmarks - GHCHEAD=${GHCHEAD-false}- - travis_retry cabal update -v- - "sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config"+ - travis_retry ${CABAL} update -v+ - sed -i.bak 's/^jobs:/-- jobs:/' $CABALHOME/config - rm -fv cabal.project cabal.project.local # Overlay Hackage Package Index for GHC HEAD: https://github.com/hvr/head.hackage - | if $GHCHEAD; then- sed -i 's/-- allow-newer: .*/allow-newer: *:base/' ${HOME}/.cabal/config- for pkg in $($HCPKG list --simple-output); do pkg=$(echo $pkg | sed 's/-[^-]*$//'); sed -i "s/allow-newer: /allow-newer: *:$pkg, /" ${HOME}/.cabal/config; done+ sed -i 's/-- allow-newer: .*/allow-newer: *:base/' $CABALHOME/config+ for pkg in $($HCPKG list --simple-output); do pkg=$(echo $pkg | sed 's/-[^-]*$//'); sed -i "s/allow-newer: /allow-newer: *:$pkg, /" $CABALHOME/config; done - echo 'repository head.hackage' >> ${HOME}/.cabal/config- echo ' url: http://head.hackage.haskell.org/' >> ${HOME}/.cabal/config- echo ' secure: True' >> ${HOME}/.cabal/config- echo ' root-keys: 07c59cb65787dedfaef5bd5f987ceb5f7e5ebf88b904bbd4c5cbdeb2ff71b740' >> ${HOME}/.cabal/config- echo ' 2e8555dde16ebd8df076f1a8ef13b8f14c66bad8eafefd7d9e37d0ed711821fb' >> ${HOME}/.cabal/config- echo ' 8f79fd2389ab2967354407ec852cbe73f2e8635793ac446d09461ffb99527f6e' >> ${HOME}/.cabal/config- echo ' key-threshold: 3' >> ${HOME}/.cabal.config+ echo 'repository head.hackage' >> $CABALHOME/config+ echo ' url: http://head.hackage.haskell.org/' >> $CABALHOME/config+ echo ' secure: True' >> $CABALHOME/config+ echo ' root-keys: 07c59cb65787dedfaef5bd5f987ceb5f7e5ebf88b904bbd4c5cbdeb2ff71b740' >> $CABALHOME/config+ echo ' 2e8555dde16ebd8df076f1a8ef13b8f14c66bad8eafefd7d9e37d0ed711821fb' >> $CABALHOME/config+ echo ' 8f79fd2389ab2967354407ec852cbe73f2e8635793ac446d09461ffb99527f6e' >> $CABALHOME/config+ echo ' key-threshold: 3' >> $CABALHOME.config - grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$'+ grep -Ev -- '^\s*--' $CABALHOME/config | grep -Ev '^\s*$' - cabal new-update head.hackage -v+ ${CABAL} new-update head.hackage -v fi- - grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$'- - "printf 'packages: \"servant\" \"servant-client\" \"servant-docs\" \"servant-server\"\\n' > cabal.project"+ - grep -Ev -- '^\s*--' $CABALHOME/config | grep -Ev '^\s*$'+ - rm -f cabal.project+ - touch cabal.project+ - "printf 'packages: \"servant\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-client\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-docs\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-server\"\\n' >> cabal.project" - "echo 'constraints: foundatiion >= 0.14' >> cabal.project"- - "echo 'allow-newer: servant-js:servant, servant-js:servant-foreign' >> cabal.project"+ - "echo 'allow-newer: servant-js:servant' >> cabal.project"+ - "echo 'allow-newer: servant-js:servant-foreign' >> cabal.project"+ - "printf 'write-ghc-environment-files: always\\n' >> cabal.project" - touch cabal.project.local- - "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | grep -vw -- servant | grep -vw -- servant-client | grep -vw -- servant-docs | grep -vw -- servant-server | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi"+ - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | grep -vE -- '^(servant|servant-client|servant-docs|servant-server)$' | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done" - cat cabal.project || true - cat cabal.project.local || true- - if [ -f "servant/configure.ac" ]; then- (cd "servant" && autoreconf -i);- fi- - if [ -f "servant-client/configure.ac" ]; then- (cd "servant-client" && autoreconf -i);- fi- - if [ -f "servant-docs/configure.ac" ]; then- (cd "servant-docs" && autoreconf -i);- fi- - if [ -f "servant-server/configure.ac" ]; then- (cd "servant-server" && autoreconf -i);- fi+ - if [ -f "servant/configure.ac" ]; then (cd "servant" && autoreconf -i); fi+ - if [ -f "servant-client/configure.ac" ]; then (cd "servant-client" && autoreconf -i); fi+ - if [ -f "servant-docs/configure.ac" ]; then (cd "servant-docs" && autoreconf -i); fi+ - if [ -f "servant-server/configure.ac" ]; then (cd "servant-server" && autoreconf -i); fi - rm -f cabal.project.freeze- - cabal new-build -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project.empty-line" --dep -j2 all- - cabal new-build -w ${HC} --disable-tests --disable-benchmarks --project-file="cabal.project.empty-line" --dep -j2 all+ - ${CABAL} new-freeze -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project.empty-line" --dry+ - "cat \"cabal.project.empty-line.freeze\" | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'"+ - rm "cabal.project.empty-line.freeze"+ - ${CABAL} new-build -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project.empty-line" --dep -j2 all+ - ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks --project-file="cabal.project.empty-line" --dep -j2 all - rm -rf .ghc.environment.* "servant"/dist "servant-client"/dist "servant-docs"/dist "servant-server"/dist - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX) @@ -151,39 +143,42 @@ # any command which exits with a non-zero exit code causes the build to fail. script: # test that source-distributions can be generated- - (cd "servant" && cabal sdist)- - (cd "servant-client" && cabal sdist)- - (cd "servant-docs" && cabal sdist)- - (cd "servant-server" && cabal sdist)- - mv "servant"/dist/servant-*.tar.gz "servant-client"/dist/servant-client-*.tar.gz "servant-docs"/dist/servant-docs-*.tar.gz "servant-server"/dist/servant-server-*.tar.gz ${DISTDIR}/+ - ${CABAL} new-sdist all+ - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/ - cd ${DISTDIR} || false - find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \;- - "printf 'packages: servant-*/*.cabal servant-client-*/*.cabal servant-docs-*/*.cabal servant-server-*/*.cabal\\n' > cabal.project"+ - rm -f cabal.project+ - touch cabal.project+ - "printf 'packages: \"servant-*/*.cabal\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-client-*/*.cabal\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-docs-*/*.cabal\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-server-*/*.cabal\"\\n' >> cabal.project" - "echo 'constraints: foundatiion >= 0.14' >> cabal.project"- - "echo 'allow-newer: servant-js:servant, servant-js:servant-foreign' >> cabal.project"+ - "echo 'allow-newer: servant-js:servant' >> cabal.project"+ - "echo 'allow-newer: servant-js:servant-foreign' >> cabal.project"+ - "printf 'write-ghc-environment-files: always\\n' >> cabal.project" - touch cabal.project.local- - "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | grep -vw -- servant | grep -vw -- servant-client | grep -vw -- servant-docs | grep -vw -- servant-server | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi"+ - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | grep -vE -- '^(servant|servant-client|servant-docs|servant-server)$' | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done" - cat cabal.project || true - cat cabal.project.local || true # this builds all libraries and executables (without tests/benchmarks)- - cabal new-build -w ${HC} --disable-tests --disable-benchmarks all+ - ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks all # build & run tests, build benchmarks- - cabal new-build -w ${HC} ${TEST} ${BENCH} all- - if [ "x$TEST" = "x--enable-tests" ]; then cabal new-test -w ${HC} ${TEST} ${BENCH} all; fi+ - ${CABAL} new-build -w ${HC} ${TEST} ${BENCH} all+ - if [ "x$TEST" = "x--enable-tests" ]; then ${CABAL} new-test -w ${HC} ${TEST} ${BENCH} all; fi # cabal check- - (cd servant-* && cabal check)- - (cd servant-client-* && cabal check)- - (cd servant-docs-* && cabal check)- - (cd servant-server-* && cabal check)+ - (cd servant-* && ${CABAL} check)+ - (cd servant-client-* && ${CABAL} check)+ - (cd servant-docs-* && ${CABAL} check)+ - (cd servant-server-* && ${CABAL} check) # haddock- - rm -rf ./dist-newstyle- - if $HADDOCK; then cabal new-haddock -w ${HC} ${TEST} ${BENCH} all; else echo "Skipping haddock generation";fi+ - ${CABAL} new-haddock -w ${HC} ${TEST} ${BENCH} all # Build without installed constraints for packages in global-db- - if $UNCONSTRAINED; then rm -f cabal.project.local; echo cabal new-build -w ${HC} --disable-tests --disable-benchmarks all; else echo "Not building without installed constraints"; fi+ - rm -f cabal.project.local; ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks all; -# REGENDATA ["--ghc-head","cabal.project.empty-line"]+# REGENDATA ["-o","cabal.project.empty-line.travis.yml","--ghc-head","cabal.project.empty-line"] # EOF
fixtures/cabal.project.messy.stderr view
@@ -1,1 +1,1 @@-*INFO* Generating Travis-CI config for testing for GHC versions: head 7.8.1 7.8.2 7.8.3 7.8.4 7.10.1 7.10.2 7.10.3 8.0.1 8.0.2 8.2.1 8.2.2 8.4.1 8.4.2 8.4.3 8.6.1+*INFO* Generating Travis-CI config for testing for GHC versions: head 7.8.1 7.8.2 7.8.3 7.8.4 7.10.1 7.10.2 7.10.3 8.0.1 8.0.2 8.2.1 8.2.2 8.4.1 8.4.2 8.4.3 8.4.4 8.6.1 8.6.2 8.6.3
fixtures/cabal.project.messy.travis.yml view
@@ -1,11 +1,13 @@ # This Travis job script has been generated by a script via #-# runghc make_travis_yml_2.hs '--ghc-head' 'cabal.project.messy'+# haskell-ci '-o' 'cabal.project.messy.travis.yml' '--ghc-head' '--apt=fftw3-dev' '--installed=-all +deepseq' 'cabal.project.messy' # # For more information, see https://github.com/haskell-CI/haskell-ci #+# version: 0.2+# language: c-sudo: false+dist: xenial git: submodules: false # whether to recursively clone submodules@@ -16,132 +18,121 @@ - $HOME/.cabal/store before_cache:- - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log+ - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log # remove files that are regenerated by 'cabal update'- - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.*- - rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json- - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache- - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar- - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx+ - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.*+ - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx - - rm -rfv $HOME/.cabal/packages/head.hackage+ - rm -rfv $CABALHOME/packages/head.hackage matrix: include:+ - compiler: "ghc-8.6.3"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.3,fftw3-dev], sources: [hvr-ghc]}}+ - compiler: "ghc-8.6.2"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.2,fftw3-dev], sources: [hvr-ghc]}} - compiler: "ghc-8.6.1"- env: GHCHEAD=true- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.6.1], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.1,fftw3-dev], sources: [hvr-ghc]}}+ - compiler: "ghc-8.4.4"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.4,fftw3-dev], sources: [hvr-ghc]}} - compiler: "ghc-8.4.3"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.4.3], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.3,fftw3-dev], sources: [hvr-ghc]}} - compiler: "ghc-8.4.2"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.4.2], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.2,fftw3-dev], sources: [hvr-ghc]}} - compiler: "ghc-8.4.1"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.4.1], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.1,fftw3-dev], sources: [hvr-ghc]}} - compiler: "ghc-8.2.2"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.2.2], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.2.2,fftw3-dev], sources: [hvr-ghc]}} - compiler: "ghc-8.2.1"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.2.1], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.2.1,fftw3-dev], sources: [hvr-ghc]}} - compiler: "ghc-8.0.2"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.0.2], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.0.2,fftw3-dev], sources: [hvr-ghc]}} - compiler: "ghc-8.0.1"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.0.1], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.0.1,fftw3-dev], sources: [hvr-ghc]}} - compiler: "ghc-7.10.3"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.10.3], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.10.3,fftw3-dev], sources: [hvr-ghc]}} - compiler: "ghc-7.10.2"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.10.2], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.10.2,fftw3-dev], sources: [hvr-ghc]}} - compiler: "ghc-7.10.1"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.10.1], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.10.1,fftw3-dev], sources: [hvr-ghc]}} - compiler: "ghc-7.8.4"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.8.4], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.4,fftw3-dev], sources: [hvr-ghc]}} - compiler: "ghc-7.8.3"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.8.3], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.3,fftw3-dev], sources: [hvr-ghc]}} - compiler: "ghc-7.8.2"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.8.2], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.2,fftw3-dev], sources: [hvr-ghc]}} - compiler: "ghc-7.8.1"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.8.1], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.1,fftw3-dev], sources: [hvr-ghc]}} - compiler: "ghc-head" env: GHCHEAD=true- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-head], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-head,fftw3-dev], sources: [hvr-ghc]}} allow_failures: - compiler: "ghc-head"- - compiler: "ghc-8.6.1" before_install:- - HC=${CC}+ - HC=/opt/ghc/bin/${CC} - HCPKG=${HC/ghc/ghc-pkg} - unset CC+ - CABAL=/opt/ghc/bin/cabal+ - CABALHOME=$HOME/.cabal+ - export PATH="$CABALHOME/bin:$PATH" - ROOTDIR=$(pwd)- - mkdir -p $HOME/.local/bin- - "PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$HOME/local/bin:$PATH" - HCNUMVER=$(( $(${HC} --numeric-version|sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+).*/\1 * 10000 + \2 * 100 + \3/') )) - echo $HCNUMVER install:- - cabal --version+ - ${CABAL} --version - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"- - BENCH=${BENCH---enable-benchmarks}- - TEST=${TEST---enable-tests}- - HADDOCK=${HADDOCK-true}- - UNCONSTRAINED=${UNCONSTRAINED-true}- - NOINSTALLEDCONSTRAINTS=${NOINSTALLEDCONSTRAINTS-false}+ - TEST=--enable-tests+ - BENCH=--enable-benchmarks - GHCHEAD=${GHCHEAD-false}- - travis_retry cabal update -v- - "sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config"+ - travis_retry ${CABAL} update -v+ - sed -i.bak 's/^jobs:/-- jobs:/' $CABALHOME/config - rm -fv cabal.project cabal.project.local # Overlay Hackage Package Index for GHC HEAD: https://github.com/hvr/head.hackage - | if $GHCHEAD; then- sed -i 's/-- allow-newer: .*/allow-newer: *:base/' ${HOME}/.cabal/config- for pkg in $($HCPKG list --simple-output); do pkg=$(echo $pkg | sed 's/-[^-]*$//'); sed -i "s/allow-newer: /allow-newer: *:$pkg, /" ${HOME}/.cabal/config; done+ sed -i 's/-- allow-newer: .*/allow-newer: *:base/' $CABALHOME/config+ for pkg in $($HCPKG list --simple-output); do pkg=$(echo $pkg | sed 's/-[^-]*$//'); sed -i "s/allow-newer: /allow-newer: *:$pkg, /" $CABALHOME/config; done - echo 'repository head.hackage' >> ${HOME}/.cabal/config- echo ' url: http://head.hackage.haskell.org/' >> ${HOME}/.cabal/config- echo ' secure: True' >> ${HOME}/.cabal/config- echo ' root-keys: 07c59cb65787dedfaef5bd5f987ceb5f7e5ebf88b904bbd4c5cbdeb2ff71b740' >> ${HOME}/.cabal/config- echo ' 2e8555dde16ebd8df076f1a8ef13b8f14c66bad8eafefd7d9e37d0ed711821fb' >> ${HOME}/.cabal/config- echo ' 8f79fd2389ab2967354407ec852cbe73f2e8635793ac446d09461ffb99527f6e' >> ${HOME}/.cabal/config- echo ' key-threshold: 3' >> ${HOME}/.cabal.config+ echo 'repository head.hackage' >> $CABALHOME/config+ echo ' url: http://head.hackage.haskell.org/' >> $CABALHOME/config+ echo ' secure: True' >> $CABALHOME/config+ echo ' root-keys: 07c59cb65787dedfaef5bd5f987ceb5f7e5ebf88b904bbd4c5cbdeb2ff71b740' >> $CABALHOME/config+ echo ' 2e8555dde16ebd8df076f1a8ef13b8f14c66bad8eafefd7d9e37d0ed711821fb' >> $CABALHOME/config+ echo ' 8f79fd2389ab2967354407ec852cbe73f2e8635793ac446d09461ffb99527f6e' >> $CABALHOME/config+ echo ' key-threshold: 3' >> $CABALHOME.config - grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$'+ grep -Ev -- '^\s*--' $CABALHOME/config | grep -Ev '^\s*$' - cabal new-update head.hackage -v+ ${CABAL} new-update head.hackage -v fi- - grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$'- - "printf 'packages: \"servant\" \"servant-client\" \"servant-docs\" \"servant-server\"\\n' > cabal.project"+ - grep -Ev -- '^\s*--' $CABALHOME/config | grep -Ev '^\s*$'+ - rm -f cabal.project+ - touch cabal.project+ - "printf 'packages: \"servant\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-client\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-docs\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-server\"\\n' >> cabal.project"+ - "printf 'write-ghc-environment-files: always\\n' >> cabal.project" - touch cabal.project.local- - "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | grep -vw -- servant | grep -vw -- servant-client | grep -vw -- servant-docs | grep -vw -- servant-server | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi"+ - "for pkg in deepseq; do echo \"constraints: $pkg installed\" >> cabal.project >> cabal.project.local; done" - cat cabal.project || true - cat cabal.project.local || true- - if [ -f "servant/configure.ac" ]; then- (cd "servant" && autoreconf -i);- fi- - if [ -f "servant-client/configure.ac" ]; then- (cd "servant-client" && autoreconf -i);- fi- - if [ -f "servant-docs/configure.ac" ]; then- (cd "servant-docs" && autoreconf -i);- fi- - if [ -f "servant-server/configure.ac" ]; then- (cd "servant-server" && autoreconf -i);- fi+ - if [ -f "servant/configure.ac" ]; then (cd "servant" && autoreconf -i); fi+ - if [ -f "servant-client/configure.ac" ]; then (cd "servant-client" && autoreconf -i); fi+ - if [ -f "servant-docs/configure.ac" ]; then (cd "servant-docs" && autoreconf -i); fi+ - if [ -f "servant-server/configure.ac" ]; then (cd "servant-server" && autoreconf -i); fi - rm -f cabal.project.freeze- - cabal new-build -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project.messy" --dep -j2 all- - cabal new-build -w ${HC} --disable-tests --disable-benchmarks --project-file="cabal.project.messy" --dep -j2 all+ - ${CABAL} new-freeze -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project.messy" --dry+ - "cat \"cabal.project.messy.freeze\" | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'"+ - rm "cabal.project.messy.freeze"+ - ${CABAL} new-build -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project.messy" --dep -j2 all+ - ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks --project-file="cabal.project.messy" --dep -j2 all - rm -rf .ghc.environment.* "servant"/dist "servant-client"/dist "servant-docs"/dist "servant-server"/dist - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX) @@ -149,37 +140,39 @@ # any command which exits with a non-zero exit code causes the build to fail. script: # test that source-distributions can be generated- - (cd "servant" && cabal sdist)- - (cd "servant-client" && cabal sdist)- - (cd "servant-docs" && cabal sdist)- - (cd "servant-server" && cabal sdist)- - mv "servant"/dist/servant-*.tar.gz "servant-client"/dist/servant-client-*.tar.gz "servant-docs"/dist/servant-docs-*.tar.gz "servant-server"/dist/servant-server-*.tar.gz ${DISTDIR}/+ - ${CABAL} new-sdist all+ - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/ - cd ${DISTDIR} || false - find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \;- - "printf 'packages: servant-*/*.cabal servant-client-*/*.cabal servant-docs-*/*.cabal servant-server-*/*.cabal\\n' > cabal.project"+ - rm -f cabal.project+ - touch cabal.project+ - "printf 'packages: \"servant-*/*.cabal\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-client-*/*.cabal\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-docs-*/*.cabal\"\\n' >> cabal.project"+ - "printf 'packages: \"servant-server-*/*.cabal\"\\n' >> cabal.project"+ - "printf 'write-ghc-environment-files: always\\n' >> cabal.project" - touch cabal.project.local- - "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | grep -vw -- servant | grep -vw -- servant-client | grep -vw -- servant-docs | grep -vw -- servant-server | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi"+ - "for pkg in deepseq; do echo \"constraints: $pkg installed\" >> cabal.project >> cabal.project.local; done" - cat cabal.project || true - cat cabal.project.local || true # this builds all libraries and executables (without tests/benchmarks)- - cabal new-build -w ${HC} --disable-tests --disable-benchmarks all+ - ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks all # build & run tests, build benchmarks- - cabal new-build -w ${HC} ${TEST} ${BENCH} all- - if [ "x$TEST" = "x--enable-tests" ]; then cabal new-test -w ${HC} ${TEST} ${BENCH} all; fi+ - ${CABAL} new-build -w ${HC} ${TEST} ${BENCH} all+ - if [ "x$TEST" = "x--enable-tests" ]; then ${CABAL} new-test -w ${HC} ${TEST} ${BENCH} all; fi # cabal check- - (cd servant-* && cabal check)- - (cd servant-client-* && cabal check)- - (cd servant-docs-* && cabal check)- - (cd servant-server-* && cabal check)+ - (cd servant-* && ${CABAL} check)+ - (cd servant-client-* && ${CABAL} check)+ - (cd servant-docs-* && ${CABAL} check)+ - (cd servant-server-* && ${CABAL} check) # haddock- - rm -rf ./dist-newstyle- - if $HADDOCK; then cabal new-haddock -w ${HC} ${TEST} ${BENCH} all; else echo "Skipping haddock generation";fi+ - ${CABAL} new-haddock -w ${HC} ${TEST} ${BENCH} all # Build without installed constraints for packages in global-db- - if $UNCONSTRAINED; then rm -f cabal.project.local; echo cabal new-build -w ${HC} --disable-tests --disable-benchmarks all; else echo "Not building without installed constraints"; fi+ - rm -f cabal.project.local; ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks all; -# REGENDATA ["--ghc-head","cabal.project.messy"]+# REGENDATA ["-o","cabal.project.messy.travis.yml","--ghc-head","--apt=fftw3-dev","--installed=-all +deepseq","cabal.project.messy"] # EOF
fixtures/haskell-ci.cabal view
@@ -1,16 +1,15 @@-cabal-version: 1.18-name: haskell-ci-version: 0.1.0.0--synopsis: Cabal package script generator for Travis-CI+cabal-version: 2.2+name: haskell-ci+version: 0.2+synopsis: Cabal package script generator for Travis-CI description:- Script generator (@make-travis-yml@) for [Travis-CI](https://travis-ci.org/) for continuous-integration testing of Haskell Cabal packages.+ Script generator (@haskell-ci@) for [Travis-CI](https://travis-ci.org/) for continuous-integration testing of Haskell Cabal packages. . Included features (not limited to): . * Multiple GHC support * Dependency caching- * cabal.project support (see [Nix-style local builds documentation](http://cabal.readthedocs.io/en/latest/nix-local-build-overview.html)+ * cabal.project support (see [Nix-style local builds documentation](https://cabal.readthedocs.io/en/latest/nix-local-build-overview.html)) * Runs tests and builds benchmarks * Generates Haddocks * macOS (OSX) support@@ -18,104 +17,157 @@ . === Quick Start Guide .- Add a @tested-with@ line to your @.cabal@ file (e.g. @tested-with: GHC == 8.0.2 || == 8.2.2@) and then run @make-travis-yml yourpackage.cabal -o .travis.yml@ to generate the Travis-CI job script.+ Add a @tested-with@ line to your @.cabal@ file (e.g. @tested-with: GHC == 8.0.2 || == 8.2.2@) and then run @haskell-ci yourpackage.cabal -o .travis.yml@ to generate the Travis-CI job script. .- See @make-travis-yml --help@ for more information.+ See @haskell-ci --help@ for more information. - -homepage: http://haskell-ci.rtfd.org/-bug-reports: https://github.com/haskell-CI/haskell-ci/issues-license: BSD3-license-file: LICENSE-author: Herbert Valerio Riedel, Oleg Grenrus-maintainer: hvr@gnu.org-category: Development-build-type: Simple-tested-with:- GHC == 7.8.4,- GHC == 7.10.3,- GHC == 8.0.2,- GHC == 8.2.2,- GHC == 8.4.3,- GHC == 8.6.1+homepage: https://haskell-ci.rtfd.org/+bug-reports: https://github.com/haskell-CI/haskell-ci/issues+license: BSD-3-Clause+license-file: LICENSE+author: Herbert Valerio Riedel, Oleg Grenrus+maintainer: hvr@gnu.org+category: Development+build-type: Simple+tested-with: GHC ==8.6.3 || ==8.4.4 || ==8.2.2 || ==8.0.2 extra-source-files: fixtures/cabal.haskell-ci- fixtures/haskell-ci.cabal- fixtures/haskell-ci.cabal.stderr- fixtures/haskell-ci.cabal.travis.yml- fixtures/cabal.project.empty-line- fixtures/cabal.project.empty-line.stderr- fixtures/cabal.project.empty-line.travis.yml fixtures/cabal.project.fail-versions fixtures/cabal.project.fail-versions.stderr+ fixtures/haskell-ci.cabal+ fixtures/cabal.project.empty-line fixtures/cabal.project.messy+ fixtures/cabal.project.copy-fields.all+ fixtures/cabal.project.copy-fields.some+ fixtures/cabal.project.copy-fields.none+ fixtures/haskell-ci.cabal.stderr+ fixtures/cabal.project.empty-line.stderr fixtures/cabal.project.messy.stderr+ fixtures/cabal.project.copy-fields.all.stderr+ fixtures/cabal.project.copy-fields.some.stderr+ fixtures/cabal.project.copy-fields.none.stderr+ fixtures/haskell-ci.cabal.travis.yml+ fixtures/cabal.project.empty-line.travis.yml fixtures/cabal.project.messy.travis.yml- fixtures/doc/tutorial/tutorial.cabal+ fixtures/cabal.project.copy-fields.all.travis.yml+ fixtures/cabal.project.copy-fields.some.travis.yml+ fixtures/cabal.project.copy-fields.none.travis.yml fixtures/servant-client-core/*.cabal fixtures/servant-client/*.cabal fixtures/servant-docs/*.cabal fixtures/servant-foreign/*.cabal fixtures/servant-server/*.cabal fixtures/servant/*.cabal+ fixtures/doc/tutorial/tutorial.cabal source-repository head- type: git+ type: git location: https://github.com/haskell-CI/haskell-ci.git flag ShellCheck default: True- manual: True+ manual: True -library- exposed-modules: MakeTravisYml- ghc-options: -Wall- if impl(ghc >= 8.0)- ghc-options:- -Wcompat- -Wnoncanonical-monad-instances- -Wnoncanonical-monadfail-instances- hs-source-dirs: src- other-extensions: CPP, ViewPatterns, NamedFieldPuns- build-depends: base >= 4.7 && < 4.12- , Cabal == 1.18.*- || == 1.20.*- || == 1.22.*- || == 1.24.*- || == 2.0.*- || == 2.2.*- , containers >= 0.4 && < 0.6- , directory >= 1.1 && < 1.4- , filepath >= 1.2 && < 1.5- , transformers >= 0.3 && < 0.6- , deepseq >= 1.3 && < 1.5- default-language: Haskell2010+library haskell-ci-internal+ exposed-modules:+ HaskellCI+ HaskellCI.Cli+ HaskellCI.Config+ HaskellCI.Config.ConstraintSet+ HaskellCI.Config.CopyFields+ HaskellCI.Config.Doctest+ HaskellCI.Config.Dump+ HaskellCI.Config.Folds+ HaskellCI.Config.HLint+ HaskellCI.Config.Installed+ HaskellCI.Config.Jobs+ HaskellCI.Extras+ HaskellCI.GHC+ HaskellCI.Glob+ HaskellCI.MakeTravisOutput+ HaskellCI.Newtypes+ HaskellCI.Optimization+ HaskellCI.OptionsGrammar+ HaskellCI.OptparseGrammar+ HaskellCI.Package+ HaskellCI.ParsecError+ HaskellCI.ParsecUtils+ HaskellCI.Project+ HaskellCI.TestedWith+ HaskellCI.Version + -- vendored from Cabal development version+ other-modules: Distribution.Fields.Pretty+ ghc-options:+ -Wall -Wcompat -Wnoncanonical-monad-instances+ -Wnoncanonical-monadfail-instances++ hs-source-dirs: src+ default-extensions:+ DeriveFoldable+ DeriveFunctor+ DeriveGeneric+ DeriveTraversable++ other-extensions:+ CPP+ NamedFieldPuns+ OverloadedLabels+ ViewPatterns++ build-depends:+ , base >=4.9 && <4.13+ , bytestring ^>=0.10.8.1+ , Cabal ^>=2.4+ , containers ^>=0.5.7.1 || ^>=0.6.0.1+ , deepseq ^>=1.4.2.0+ , directory ^>=1.3.0.0+ , filepath ^>=1.4.1.1+ , parsec ^>=3.1.13.0+ , pretty ^>=1.1.3.3+ , text ^>=1.2.3.0+ , transformers ^>=0.5.2.0++ -- other dependencies+ build-depends:+ , base-compat ^>=0.10.5+ , generic-lens ^>=1.1.0.0+ , HsYAML ^>=0.1.1.3+ , microlens ^>=0.4.10+ , optparse-applicative ^>=0.14.3.0++ default-language: Haskell2010+ -- ShellCheck. Would need newer transformers for older GHC- if flag(ShellCheck) && impl(ghc >= 7.10 && <8.5)- build-depends: ShellCheck == 0.5.0+ if (flag(shellcheck) && impl(ghc >=7.10 && <8.7))+ build-depends: ShellCheck ==0.6.0 -executable make-travis-yml- main-is: Main.hs- ghc-options: -Wall- build-depends: base, haskell-ci- default-language: Haskell2010+executable haskell-ci+ main-is: Main.hs+ ghc-options: -Wall+ hs-source-dirs: cli+ build-depends:+ , base+ , haskell-ci-internal + default-language: Haskell2010+ test-suite golden- type: exitcode-stdio-1.0- main-is: Tests.hs- hs-source-dirs: test- build-depends: haskell-ci- -- inherited constraints via lib:haskell-ci- , base- , bytestring- , directory- , filepath- , transformers- -- dependencies needing explicit constraints- , tasty >= 1.0 && < 1.2- , tasty-golden >= 2.3.1.1 && < 2.4- , ansi-terminal >= 0.8.0.2 && <0.9- , Diff >= 0.3.4 && < 0.4+ type: exitcode-stdio-1.0+ main-is: Tests.hs+ hs-source-dirs: test+ build-depends:+ , ansi-terminal ^>=0.8.0.2+ , base+ , bytestring+ , Diff ^>=0.3.4+ , directory+ , filepath+ , haskell-ci-internal+ , tasty >=1.0 && <1.2+ , tasty-golden ^>=2.3.1.1+ , transformers - default-language: Haskell2010+ -- inherited constraints via lib:haskell-ci+ -- dependencies needing explicit constraints+ default-language: Haskell2010
fixtures/haskell-ci.cabal.stderr view
@@ -1,1 +1,2 @@-*INFO* Generating Travis-CI config for testing for GHC versions: 7.8.4 7.10.3 8.0.2 8.2.2 8.4.3 8.6.1+*INFO* Generating Travis-CI config for testing for GHC versions: head 8.0.2 8.2.2 8.4.4 8.6.3+*INFO* Also OSX jobs for: 8.4.4
fixtures/haskell-ci.cabal.travis.yml view
@@ -1,11 +1,13 @@ # This Travis job script has been generated by a script via #-# runghc make_travis_yml_2.hs '-o' '.travis.yml' '--config=cabal.haskell-ci' 'haskell-ci.cabal'+# haskell-ci '--output=.travis.yml' '--config=cabal.haskell-ci' 'haskell-ci.cabal' # # For more information, see https://github.com/haskell-CI/haskell-ci #+# version: 0.2+# language: c-sudo: false+dist: xenial git: submodules: false # whether to recursively clone submodules@@ -18,100 +20,104 @@ directories: - $HOME/.cabal/packages - $HOME/.cabal/store+ - $HOME/.ghc-install before_cache:- - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log+ - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log # remove files that are regenerated by 'cabal update'- - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.*- - rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json- - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache- - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar- - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx+ - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.*+ - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx - - rm -rfv $HOME/.cabal/packages/head.hackage+ - rm -rfv $CABALHOME/packages/head.hackage matrix: include:- - compiler: "ghc-8.6.1"- env: GHCHEAD=true- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.6.1], sources: [hvr-ghc]}}- - compiler: "ghc-8.4.3"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.4.3], sources: [hvr-ghc]}}+ - compiler: "ghc-8.6.3"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.3], sources: [hvr-ghc]}}+ - compiler: "ghc-8.4.4"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.4], sources: [hvr-ghc]}} - compiler: "ghc-8.2.2"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.2.2], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.2.2], sources: [hvr-ghc]}} - compiler: "ghc-8.0.2"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.0.2], sources: [hvr-ghc]}}- - compiler: "ghc-7.10.3"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.10.3], sources: [hvr-ghc]}}- - compiler: "ghc-7.8.4"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.8.4], sources: [hvr-ghc]}}+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.0.2], sources: [hvr-ghc]}}+ - compiler: "ghc-head"+ env: GHCHEAD=true+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-head], sources: [hvr-ghc]}}+ - compiler: "ghc-8.4.4"+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.4], sources: [hvr-ghc]}}+ os: osx allow_failures:- - compiler: "ghc-8.6.1"+ - compiler: "ghc-head" before_install:- - HC=${CC}+ - HC=/opt/ghc/bin/${CC} - HCPKG=${HC/ghc/ghc-pkg} - unset CC+ - CABAL=/opt/ghc/bin/cabal+ - CABALHOME=$HOME/.cabal+ - export PATH="$CABALHOME/bin:$PATH" - ROOTDIR=$(pwd)- - mkdir -p $HOME/.local/bin- - "PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$HOME/local/bin:$PATH"+ - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update; brew upgrade python@3; curl https://haskell.futurice.com/haskell-on-macos.py | python3 - --make-dirs --install-dir=$HOME/.ghc-install --cabal-alias=head install cabal-install-head ${TRAVIS_COMPILER}; fi+ - if [ "$TRAVIS_OS_NAME" = "osx" ]; then HC=$HOME/.ghc-install/ghc/bin/$TRAVIS_COMPILER; HCPKG=${HC/ghc/ghc-pkg}; CABAL=$HOME/.ghc-install/ghc/bin/cabal; fi - HCNUMVER=$(( $(${HC} --numeric-version|sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+).*/\1 * 10000 + \2 * 100 + \3/') )) - echo $HCNUMVER install:- - cabal --version+ - ${CABAL} --version - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"- - BENCH=${BENCH---enable-benchmarks}- - TEST=${TEST---enable-tests}- - HADDOCK=${HADDOCK-true}- - UNCONSTRAINED=${UNCONSTRAINED-true}- - NOINSTALLEDCONSTRAINTS=${NOINSTALLEDCONSTRAINTS-false}+ - TEST=--enable-tests+ - BENCH=--enable-benchmarks - GHCHEAD=${GHCHEAD-false}- - travis_retry cabal update -v- - "sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config"+ - travis_retry ${CABAL} update -v+ - sed -i.bak 's/^jobs:/-- jobs:/' $CABALHOME/config - rm -fv cabal.project cabal.project.local- - "sed -i.bak 's/^-- jobs:.*/jobs: 2/' ${HOME}/.cabal/config"- - "if [ $HCNUMVER -ge 70800 ]; then sed -i.bak 's/-- ghc-options:.*/ghc-options: -j2/' ${HOME}/.cabal/config; fi"+ - "sed -i.bak 's/^-- jobs:.*/jobs: 2/' $CABALHOME/config"+ - "sed -i.bak 's/-- ghc-options:.*/ghc-options: -j2/' $CABALHOME/config" # Overlay Hackage Package Index for GHC HEAD: https://github.com/hvr/head.hackage - | if $GHCHEAD; then- sed -i 's/-- allow-newer: .*/allow-newer: *:base/' ${HOME}/.cabal/config- for pkg in $($HCPKG list --simple-output); do pkg=$(echo $pkg | sed 's/-[^-]*$//'); sed -i "s/allow-newer: /allow-newer: *:$pkg, /" ${HOME}/.cabal/config; done+ sed -i 's/-- allow-newer: .*/allow-newer: *:base/' $CABALHOME/config+ for pkg in $($HCPKG list --simple-output); do pkg=$(echo $pkg | sed 's/-[^-]*$//'); sed -i "s/allow-newer: /allow-newer: *:$pkg, /" $CABALHOME/config; done - echo 'repository head.hackage' >> ${HOME}/.cabal/config- echo ' url: http://head.hackage.haskell.org/' >> ${HOME}/.cabal/config- echo ' secure: True' >> ${HOME}/.cabal/config- echo ' root-keys: 07c59cb65787dedfaef5bd5f987ceb5f7e5ebf88b904bbd4c5cbdeb2ff71b740' >> ${HOME}/.cabal/config- echo ' 2e8555dde16ebd8df076f1a8ef13b8f14c66bad8eafefd7d9e37d0ed711821fb' >> ${HOME}/.cabal/config- echo ' 8f79fd2389ab2967354407ec852cbe73f2e8635793ac446d09461ffb99527f6e' >> ${HOME}/.cabal/config- echo ' key-threshold: 3' >> ${HOME}/.cabal.config+ echo 'repository head.hackage' >> $CABALHOME/config+ echo ' url: http://head.hackage.haskell.org/' >> $CABALHOME/config+ echo ' secure: True' >> $CABALHOME/config+ echo ' root-keys: 07c59cb65787dedfaef5bd5f987ceb5f7e5ebf88b904bbd4c5cbdeb2ff71b740' >> $CABALHOME/config+ echo ' 2e8555dde16ebd8df076f1a8ef13b8f14c66bad8eafefd7d9e37d0ed711821fb' >> $CABALHOME/config+ echo ' 8f79fd2389ab2967354407ec852cbe73f2e8635793ac446d09461ffb99527f6e' >> $CABALHOME/config+ echo ' key-threshold: 3' >> $CABALHOME.config - grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$'+ grep -Ev -- '^\s*--' $CABALHOME/config | grep -Ev '^\s*$' - cabal new-update head.hackage -v+ ${CABAL} new-update head.hackage -v fi- - grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$'- - if [ $HCNUMVER -ge 80000 ]; then cabal new-install -w ${HC} -j2 --symlink-bindir=$HOME/.local/bin doctest --constraint='doctest ==0.16.*'; fi- - if [ $HCNUMVER -eq 80403 ]; then cabal new-install -w ${HC} -j2 --symlink-bindir=$HOME/.local/bin hlint --constraint='hlint ==2.1.*'; fi- - "printf 'packages: \".\"\\n' > cabal.project"+ - grep -Ev -- '^\s*--' $CABALHOME/config | grep -Ev '^\s*$'+ - ${CABAL} new-install -w ${HC} -j2 doctest --constraint='doctest ==0.16.*'+ - if [ $HCNUMVER -eq 80603 ] ; then ${CABAL} new-install -w ${HC} -j2 hlint --constraint='hlint ==2.1.*' ; fi+ - rm -f cabal.project+ - touch cabal.project+ - "printf 'packages: \".\"\\n' >> cabal.project" - echo 'package haskell-ci' >> cabal.project - "echo ' ghc-options: -Werror' >> cabal.project"+ - "printf 'write-ghc-environment-files: always\\n' >> cabal.project"+ - "echo 'keep-going: False' >> cabal.project"+ - echo 'package bytestring' >> cabal.project+ - "echo ' tests: False' >> cabal.project" - touch cabal.project.local- - "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | grep -vw -- haskell-ci | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi"+ - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | grep -vE -- '^(Cabal|haskell-ci)$' | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done" - cat cabal.project || true - cat cabal.project.local || true- - if [ -f "./configure.ac" ]; then- (cd "." && autoreconf -i);- fi+ - if [ -f "./configure.ac" ]; then (cd "." && autoreconf -i); fi - rm -f cabal.project.freeze- - cabal new-build -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project" --dep -j2 all- - cabal new-build -w ${HC} --disable-tests --disable-benchmarks --project-file="cabal.project" --dep -j2 all+ - ${CABAL} new-freeze -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project" --dry+ - "cat \"cabal.project.freeze\" | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'"+ - rm "cabal.project.freeze"+ - ${CABAL} new-build -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project" --dep -j2 all+ - ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks --project-file="cabal.project" --dep -j2 all - rm -rf .ghc.environment.* "."/dist - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX) @@ -119,48 +125,51 @@ # any command which exits with a non-zero exit code causes the build to fail. script: # test that source-distributions can be generated- - (cd "." && cabal sdist)- - mv "."/dist/haskell-ci-*.tar.gz ${DISTDIR}/+ - ${CABAL} new-sdist all+ - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/ - cd ${DISTDIR} || false - find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \;- - "printf 'packages: haskell-ci-*/*.cabal\\n' > cabal.project"+ - rm -f cabal.project+ - touch cabal.project+ - "printf 'packages: \"haskell-ci-*/*.cabal\"\\n' >> cabal.project" - echo 'package haskell-ci' >> cabal.project - "echo ' ghc-options: -Werror' >> cabal.project"+ - "printf 'write-ghc-environment-files: always\\n' >> cabal.project"+ - "echo 'keep-going: False' >> cabal.project"+ - echo 'package bytestring' >> cabal.project+ - "echo ' tests: False' >> cabal.project" - touch cabal.project.local- - "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | grep -vw -- haskell-ci | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi"+ - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | grep -vE -- '^(Cabal|haskell-ci)$' | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done" - cat cabal.project || true - cat cabal.project.local || true # this builds all libraries and executables (without tests/benchmarks)- - cabal new-build -w ${HC} --disable-tests --disable-benchmarks all+ - ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks all # build & run tests, build benchmarks- - cabal new-build -w ${HC} ${TEST} ${BENCH} all- - if [ "x$TEST" = "x--enable-tests" ]; then cabal new-test -w ${HC} ${TEST} ${BENCH} all; fi+ - ${CABAL} new-build -w ${HC} ${TEST} ${BENCH} all+ - if [ "x$TEST" = "x--enable-tests" ]; then ${CABAL} new-test -w ${HC} ${TEST} ${BENCH} all; fi # doctest- - if [ $HCNUMVER -ge 80000 ]; then (cd haskell-ci-* && doctest --fast src); fi+ - (cd haskell-ci-* && doctest --fast -XDeriveFoldable -XDeriveFunctor -XDeriveGeneric -XDeriveTraversable src) # hlint- - if [ $HCNUMVER -eq 80403 ]; then (cd haskell-ci-* && hlint -h ${ROOTDIR}/.hlint.yaml src); fi+ - if [ $HCNUMVER -eq 80603 ] ; then (cd haskell-ci-* && hlint -h ${ROOTDIR}/.hlint.yaml -XDeriveFoldable -XDeriveFunctor -XDeriveGeneric -XDeriveTraversable src) ; fi+ - if [ $HCNUMVER -eq 80603 ] ; then (cd haskell-ci-* && hlint -h ${ROOTDIR}/.hlint.yaml cli) ; fi # cabal check- - (cd haskell-ci-* && cabal check)-- # haddock- - rm -rf ./dist-newstyle- - if $HADDOCK; then cabal new-haddock -w ${HC} ${TEST} ${BENCH} all; else echo "Skipping haddock generation";fi+ - (cd haskell-ci-* && ${CABAL} check) # Build without installed constraints for packages in global-db- - if $UNCONSTRAINED; then rm -f cabal.project.local; echo cabal new-build -w ${HC} --disable-tests --disable-benchmarks all; else echo "Not building without installed constraints"; fi+ - rm -f cabal.project.local; ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks all; # Constraint sets - rm -rf cabal.project.local # Constraint set deepseq-1.4 - echo Constraint set deepseq-1.4 && echo -en 'travis_fold:start:constraint-sets-deepseq-1.4\\r'- - if [ $HCNUMVER -ge 70800 ] && [ $HCNUMVER -lt 71000 ] || [ $HCNUMVER -eq 80202 ]; then cabal new-build -w ${HC} --disable-tests --disable-benchmarks --constraint='deepseq ==1.4.*' all; else echo skipping...; fi+ - if [ $HCNUMVER -ge 70800 ] && [ $HCNUMVER -lt 71000 ] || [ $HCNUMVER -eq 80202 ] ; then ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks --constraint='deepseq ==1.4.*' all ; fi - echo -en 'travis_fold:end:constraint-sets-deepseq-1.4\\r' -# REGENDATA ["-o",".travis.yml","--config=cabal.haskell-ci","haskell-ci.cabal"]+# REGENDATA ["--output=.travis.yml","--config=cabal.haskell-ci","haskell-ci.cabal"] # EOF
haskell-ci.cabal view
@@ -1,16 +1,15 @@-cabal-version: 1.18-name: haskell-ci-version: 0.1.0.0--synopsis: Cabal package script generator for Travis-CI+cabal-version: 2.2+name: haskell-ci+version: 0.2+synopsis: Cabal package script generator for Travis-CI description:- Script generator (@make-travis-yml@) for [Travis-CI](https://travis-ci.org/) for continuous-integration testing of Haskell Cabal packages.+ Script generator (@haskell-ci@) for [Travis-CI](https://travis-ci.org/) for continuous-integration testing of Haskell Cabal packages. . Included features (not limited to): . * Multiple GHC support * Dependency caching- * cabal.project support (see [Nix-style local builds documentation](http://cabal.readthedocs.io/en/latest/nix-local-build-overview.html)+ * cabal.project support (see [Nix-style local builds documentation](https://cabal.readthedocs.io/en/latest/nix-local-build-overview.html)) * Runs tests and builds benchmarks * Generates Haddocks * macOS (OSX) support@@ -18,104 +17,157 @@ . === Quick Start Guide .- Add a @tested-with@ line to your @.cabal@ file (e.g. @tested-with: GHC == 8.0.2 || == 8.2.2@) and then run @make-travis-yml yourpackage.cabal -o .travis.yml@ to generate the Travis-CI job script.+ Add a @tested-with@ line to your @.cabal@ file (e.g. @tested-with: GHC == 8.0.2 || == 8.2.2@) and then run @haskell-ci yourpackage.cabal -o .travis.yml@ to generate the Travis-CI job script. .- See @make-travis-yml --help@ for more information.+ See @haskell-ci --help@ for more information. - -homepage: http://haskell-ci.rtfd.org/-bug-reports: https://github.com/haskell-CI/haskell-ci/issues-license: BSD3-license-file: LICENSE-author: Herbert Valerio Riedel, Oleg Grenrus-maintainer: hvr@gnu.org-category: Development-build-type: Simple-tested-with:- GHC == 7.8.4,- GHC == 7.10.3,- GHC == 8.0.2,- GHC == 8.2.2,- GHC == 8.4.3,- GHC == 8.6.1+homepage: https://haskell-ci.rtfd.org/+bug-reports: https://github.com/haskell-CI/haskell-ci/issues+license: BSD-3-Clause+license-file: LICENSE+author: Herbert Valerio Riedel, Oleg Grenrus+maintainer: hvr@gnu.org+category: Development+build-type: Simple+tested-with: GHC ==8.6.3 || ==8.4.4 || ==8.2.2 || ==8.0.2 extra-source-files: fixtures/cabal.haskell-ci- fixtures/haskell-ci.cabal- fixtures/haskell-ci.cabal.stderr- fixtures/haskell-ci.cabal.travis.yml- fixtures/cabal.project.empty-line- fixtures/cabal.project.empty-line.stderr- fixtures/cabal.project.empty-line.travis.yml fixtures/cabal.project.fail-versions fixtures/cabal.project.fail-versions.stderr+ fixtures/haskell-ci.cabal+ fixtures/cabal.project.empty-line fixtures/cabal.project.messy+ fixtures/cabal.project.copy-fields.all+ fixtures/cabal.project.copy-fields.some+ fixtures/cabal.project.copy-fields.none+ fixtures/haskell-ci.cabal.stderr+ fixtures/cabal.project.empty-line.stderr fixtures/cabal.project.messy.stderr+ fixtures/cabal.project.copy-fields.all.stderr+ fixtures/cabal.project.copy-fields.some.stderr+ fixtures/cabal.project.copy-fields.none.stderr+ fixtures/haskell-ci.cabal.travis.yml+ fixtures/cabal.project.empty-line.travis.yml fixtures/cabal.project.messy.travis.yml- fixtures/doc/tutorial/tutorial.cabal+ fixtures/cabal.project.copy-fields.all.travis.yml+ fixtures/cabal.project.copy-fields.some.travis.yml+ fixtures/cabal.project.copy-fields.none.travis.yml fixtures/servant-client-core/*.cabal fixtures/servant-client/*.cabal fixtures/servant-docs/*.cabal fixtures/servant-foreign/*.cabal fixtures/servant-server/*.cabal fixtures/servant/*.cabal+ fixtures/doc/tutorial/tutorial.cabal source-repository head- type: git+ type: git location: https://github.com/haskell-CI/haskell-ci.git flag ShellCheck default: True- manual: True+ manual: True -library- exposed-modules: MakeTravisYml- ghc-options: -Wall- if impl(ghc >= 8.0)- ghc-options:- -Wcompat- -Wnoncanonical-monad-instances- -Wnoncanonical-monadfail-instances- hs-source-dirs: src- other-extensions: CPP, ViewPatterns, NamedFieldPuns- build-depends: base >= 4.7 && < 4.12- , Cabal == 1.18.*- || == 1.20.*- || == 1.22.*- || == 1.24.*- || == 2.0.*- || == 2.2.*- , containers >= 0.4 && < 0.6- , directory >= 1.1 && < 1.4- , filepath >= 1.2 && < 1.5- , transformers >= 0.3 && < 0.6- , deepseq >= 1.3 && < 1.5- default-language: Haskell2010+library haskell-ci-internal+ exposed-modules:+ HaskellCI+ HaskellCI.Cli+ HaskellCI.Config+ HaskellCI.Config.ConstraintSet+ HaskellCI.Config.CopyFields+ HaskellCI.Config.Doctest+ HaskellCI.Config.Dump+ HaskellCI.Config.Folds+ HaskellCI.Config.HLint+ HaskellCI.Config.Installed+ HaskellCI.Config.Jobs+ HaskellCI.Extras+ HaskellCI.GHC+ HaskellCI.Glob+ HaskellCI.MakeTravisOutput+ HaskellCI.Newtypes+ HaskellCI.Optimization+ HaskellCI.OptionsGrammar+ HaskellCI.OptparseGrammar+ HaskellCI.Package+ HaskellCI.ParsecError+ HaskellCI.ParsecUtils+ HaskellCI.Project+ HaskellCI.TestedWith+ HaskellCI.Version + -- vendored from Cabal development version+ other-modules: Distribution.Fields.Pretty+ ghc-options:+ -Wall -Wcompat -Wnoncanonical-monad-instances+ -Wnoncanonical-monadfail-instances++ hs-source-dirs: src+ default-extensions:+ DeriveFoldable+ DeriveFunctor+ DeriveGeneric+ DeriveTraversable++ other-extensions:+ CPP+ NamedFieldPuns+ OverloadedLabels+ ViewPatterns++ build-depends:+ , base >=4.9 && <4.13+ , bytestring ^>=0.10.8.1+ , Cabal ^>=2.4+ , containers ^>=0.5.7.1 || ^>=0.6.0.1+ , deepseq ^>=1.4.2.0+ , directory ^>=1.3.0.0+ , filepath ^>=1.4.1.1+ , parsec ^>=3.1.13.0+ , pretty ^>=1.1.3.3+ , text ^>=1.2.3.0+ , transformers ^>=0.5.2.0++ -- other dependencies+ build-depends:+ , base-compat ^>=0.10.5+ , generic-lens ^>=1.1.0.0+ , HsYAML ^>=0.1.1.3+ , microlens ^>=0.4.10+ , optparse-applicative ^>=0.14.3.0++ default-language: Haskell2010+ -- ShellCheck. Would need newer transformers for older GHC- if flag(ShellCheck) && impl(ghc >= 7.10 && <8.5)- build-depends: ShellCheck == 0.5.0+ if (flag(shellcheck) && impl(ghc >=7.10 && <8.7))+ build-depends: ShellCheck ==0.6.0 -executable make-travis-yml- main-is: Main.hs- ghc-options: -Wall- build-depends: base, haskell-ci- default-language: Haskell2010+executable haskell-ci+ main-is: Main.hs+ ghc-options: -Wall+ hs-source-dirs: cli+ build-depends:+ , base+ , haskell-ci-internal + default-language: Haskell2010+ test-suite golden- type: exitcode-stdio-1.0- main-is: Tests.hs- hs-source-dirs: test- build-depends: haskell-ci- -- inherited constraints via lib:haskell-ci- , base- , bytestring- , directory- , filepath- , transformers- -- dependencies needing explicit constraints- , tasty >= 1.0 && < 1.2- , tasty-golden >= 2.3.1.1 && < 2.4- , ansi-terminal >= 0.8.0.2 && <0.9- , Diff >= 0.3.4 && < 0.4+ type: exitcode-stdio-1.0+ main-is: Tests.hs+ hs-source-dirs: test+ build-depends:+ , ansi-terminal ^>=0.8.0.2+ , base+ , bytestring+ , Diff ^>=0.3.4+ , directory+ , filepath+ , haskell-ci-internal+ , tasty >=1.0 && <1.2+ , tasty-golden ^>=2.3.1.1+ , transformers - default-language: Haskell2010+ -- inherited constraints via lib:haskell-ci+ -- dependencies needing explicit constraints+ default-language: Haskell2010
+ src/Distribution/Fields/Pretty.hs view
@@ -0,0 +1,134 @@+{-# LANGUAGE BangPatterns #-}+-- | Cabal-like file AST types: 'Field', 'Section' etc,+--+-- This (intermediate) data type is used for pretty-printing.+--+-- @since 3.0.0.0+--+module Distribution.Fields.Pretty (+ -- * Fields+ PrettyField (..),+ showFields,+ showFields',+ -- * Transformation from 'P.Field'+ fromParsecFields,+ genericFromParsecFields,+ prettyFieldLines,+ prettySectionArgs,+ ) where++import Data.Functor.Identity (Identity (..))+import Distribution.Pretty (showToken)++import Distribution.Parsec.Field (FieldName)+import Distribution.Simple.Utils (fromUTF8BS)++import qualified Distribution.Parsec.Parser as P++import qualified Data.ByteString as BS+import qualified Text.PrettyPrint as PP++data PrettyField+ = PrettyField FieldName PP.Doc+ | PrettySection FieldName [PP.Doc] [PrettyField]+ deriving Show++-- | Prettyprint a list of fields.+showFields :: [PrettyField] -> String+showFields = showFields' 4++-- | 'showFields' with user specified indentation.+showFields' :: Int -> [PrettyField] -> String+showFields' n = unlines . renderFields indent where+ -- few hardcoded, "unrolled" variants.+ indent | n == 4 = indent4+ | n == 2 = indent2+ | otherwise = (replicate (max n 1) ' ' ++)++ indent4 :: String -> String+ indent4 [] = []+ indent4 xs = ' ' : ' ' : ' ' : ' ' : xs++ indent2 :: String -> String+ indent2 [] = []+ indent2 xs = ' ' : ' ' : xs++renderFields :: (String -> String) -> [PrettyField] -> [String]+renderFields indent fields = flattenBlocks $ map (renderField indent len) fields+ where+ len = maxNameLength 0 fields++ maxNameLength !acc [] = acc+ maxNameLength !acc (PrettyField name _ : rest) = maxNameLength (max acc (BS.length name)) rest+ maxNameLength !acc (PrettySection {} : rest) = maxNameLength acc rest++-- | Block of lines,+-- Boolean parameter tells whether block should be surrounded by empty lines+data Block = Block Bool [String]++flattenBlocks :: [Block] -> [String]+flattenBlocks = go0 where+ go0 [] = []+ go0 (Block surr strs : blocks) = strs ++ go surr blocks++ go _surr' [] = []+ go surr' (Block surr strs : blocks) = ins $ strs ++ go surr blocks where+ ins | surr' || surr = ("" :)+ | otherwise = id++renderField :: (String -> String) -> Int -> PrettyField -> Block+renderField indent fw (PrettyField name doc) = Block False $ case lines narrow of+ [] -> [ name' ++ ":" ]+ [singleLine] | length singleLine < 60+ -> [ name' ++ ": " ++ replicate (fw - length name') ' ' ++ narrow ]+ _ -> (name' ++ ":") : map indent (lines (PP.render doc))+ where+ name' = fromUTF8BS name+ narrow = PP.renderStyle narrowStyle doc++ narrowStyle :: PP.Style+ narrowStyle = PP.style { PP.lineLength = PP.lineLength PP.style - fw }++renderField indent _ (PrettySection name args fields) = Block True $+ PP.render (PP.hsep $ PP.text (fromUTF8BS name) : args)+ :+ map indent (renderFields indent fields)++-------------------------------------------------------------------------------+-- Transform from Parsec.Field+-------------------------------------------------------------------------------++genericFromParsecFields+ :: Applicative f+ => (FieldName -> [P.FieldLine ann] -> f PP.Doc) -- ^ transform field contents+ -> (FieldName -> [P.SectionArg ann] -> f [PP.Doc]) -- ^ transform section arguments+ -> [P.Field ann]+ -> f [PrettyField]+genericFromParsecFields f g = goMany where+ goMany = traverse go++ go (P.Field (P.Name _ann name) fls) = PrettyField name <$> f name fls+ go (P.Section (P.Name _ann name) secargs fs) = PrettySection name <$> g name secargs <*> goMany fs++-- | Used in 'fromParsecFields'.+prettyFieldLines :: FieldName -> [P.FieldLine ann] -> PP.Doc+prettyFieldLines _ fls = PP.vcat+ [ PP.text $ fromUTF8BS bs+ | P.FieldLine _ bs <- fls+ ]++-- | Used in 'fromParsecFields'.+prettySectionArgs :: FieldName -> [P.SectionArg ann] -> [PP.Doc]+prettySectionArgs _ = map $ \sa -> case sa of+ P.SecArgName _ bs -> showToken $ fromUTF8BS bs+ P.SecArgStr _ bs -> showToken $ fromUTF8BS bs+ P.SecArgOther _ bs -> PP.text $ fromUTF8BS bs++-- | Simple variant of 'genericFromParsecField'+fromParsecFields :: [P.Field ann] -> [PrettyField]+fromParsecFields = runIdentity . genericFromParsecFields+ (Identity .: prettyFieldLines)+ (Identity .: prettySectionArgs)+ where+ (.:) :: (a -> b) -> (c -> d -> a) -> (c -> d -> b)+ (f .: g) x y = f (g x y)
+ src/HaskellCI.hs view
@@ -0,0 +1,1029 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE OverloadedLabels #-}++-- | New-style @.travis.yml@ script generator using cabal 1.24's nix-style+-- tech-preview facilities.+--+-- See also <https://github.com/haskell-CI/haskell-ci>+--+-- NB: This code deliberately avoids relying on non-standard packages and+-- is expected to compile/work with at least GHC 7.0 through GHC 8.0+module HaskellCI (+ main,+ -- * for tests+ Result (..),+ Diagnostic (..),+ parseTravis,+ formatDiagnostic, formatDiagnostics,+ travisFromConfigFile, MakeTravisOutput, Options (..), defaultOptions,+ ) where++import Prelude ()+import Prelude.Compat++import Control.DeepSeq (force)+import Control.Exception (evaluate)+import Control.Monad (when, unless, liftM, forM_, mzero)+import qualified Data.ByteString as BS+import qualified Data.Foldable as F+import qualified Data.Traversable as T+import Data.Function+import Data.List+import Data.Maybe+import Data.Set (Set)+import qualified Data.Set as S+import qualified Data.Map as M+import System.Directory (doesDirectoryExist, doesFileExist)+import System.Exit+import System.FilePath.Posix ((</>), takeDirectory, takeFileName, takeExtension)+import System.IO+import Control.Monad.IO.Class+import Control.Monad.Trans.Maybe+import System.Environment (getArgs)+import Control.Monad.Trans.Writer+import Text.Read (readMaybe)+import Distribution.Compat.ReadP (readP_to_S)+++import qualified Options.Applicative as O++import Distribution.Compiler (CompilerFlavor(..))+import Distribution.Package hiding (Package, pkgName)+import qualified Distribution.Package as Pkg+import Distribution.PackageDescription (GenericPackageDescription,packageDescription, testedWith, package, condLibrary, condTestSuites)+import Distribution.PackageDescription.Configuration (flattenPackageDescription)+import qualified Distribution.PackageDescription as PD+import qualified Distribution.ParseUtils as PU+import Distribution.Text+import Distribution.Version+#if MIN_VERSION_Cabal(2,2,0)+import Distribution.PackageDescription.Parsec (readGenericPackageDescription)+#elif MIN_VERSION_Cabal(2,0,0)+import Distribution.PackageDescription.Parse (readGenericPackageDescription)+#else+import Distribution.PackageDescription.Parse (readPackageDescription)+import Distribution.Verbosity (Verbosity)+#endif++import qualified Distribution.FieldGrammar as C+import qualified Distribution.Fields.Pretty as C+import qualified Distribution.PackageDescription.FieldGrammar as C+import qualified Distribution.Types.SourceRepo as C+import qualified Distribution.Types.VersionRange as C+import qualified Text.PrettyPrint as PP++++#if MIN_VERSION_base(4,9,0)+import Data.Semigroup (Semigroup (..))+#else+import Data.Monoid ((<>))+#endif++-- lens+import Lens.Micro+import Data.Generics.Labels () -- IsLabel (->) ...++import qualified Distribution.Types.BuildInfo.Lens as L+import qualified Distribution.Types.PackageDescription.Lens as L++import HaskellCI.Cli+import HaskellCI.Config+import HaskellCI.Config.CopyFields+import HaskellCI.Config.ConstraintSet+import HaskellCI.Config.Doctest+import HaskellCI.Config.Dump+import HaskellCI.Config.Folds+import HaskellCI.Config.HLint+import HaskellCI.Config.Installed+import HaskellCI.Config.Jobs+import HaskellCI.Extras+import HaskellCI.GHC+import HaskellCI.Glob+import HaskellCI.MakeTravisOutput+import HaskellCI.Optimization+import HaskellCI.Package+import HaskellCI.Project+import HaskellCI.TestedWith+import HaskellCI.Version++#ifndef CURRENT_PACKAGE_VERSION+#define CURRENT_PACKAGE_VERSION "???"+#endif++-------------------------------------------------------------------------------+-- Main+-------------------------------------------------------------------------------++main :: IO ()+main = do+ argv0 <- getArgs+ (cmd, opts) <- O.execParser cliParserInfo+ case cmd of+ CommandListGHC -> do+ putStrLn $ "Supported GHC versions:"+ forM_ groupedVersions $ \(v, vs) -> do+ putStr $ prettyMajVersion v ++ ": "+ putStrLn $ intercalate ", " (map display vs)+ CommandDumpConfig -> do+ putStr $ unlines $ runDG configGrammar++ CommandRegenerate -> do+ let fp = ".travis.yml" -- make configurable?+ contents <- readFile fp+ case findArgv (lines contents) of+ Nothing -> do+ hPutStrLn stderr $ "Error: expected REGENDATA line in " ++ fp+ exitFailure+ Just argv -> do+ (f, opts') <- parseTravis argv+ doTravis argv f (opts' <> opts)+ CommandTravis f -> doTravis argv0 f opts+ where+ findArgv :: [String] -> Maybe [String]+ findArgv ls = do+ l <- findMaybe (afterInfix "REGENDATA") ls+ readMaybe l++ groupedVersions :: [(Version, [Version])]+ groupedVersions = map ((\vs -> (head vs, vs)) . sortBy (flip compare))+ . groupBy ((==) `on` ghcMajVer)+ $ sort knownGhcVersions++ prettyMajVersion :: Version -> String+ prettyMajVersion v+ | Just v == ghcAlpha = "alpha"+ | otherwise = case ghcMajVer v of (x,y) -> show x ++ "." ++ show y++doTravis :: [String] -> FilePath -> Options -> IO ()+doTravis args path opts = do+ runYamlWriter (optOutput opts) $ travisFromConfigFile args opts path++runYamlWriter :: Maybe FilePath -> YamlWriter IO () -> IO ()+runYamlWriter mfp m = do+ result <- execWriterT (runMaybeT m)+ case result of+ Failure (formatDiagnostics -> errors) -> hPutStr stderr errors >> exitFailure+ Success (formatDiagnostics -> warnings) (unlines -> contents) -> do+ contents' <- evaluate (force contents)+ hPutStr stderr warnings+ case mfp of+ Nothing -> putStr contents'+ Just fp -> writeFile fp contents'++travisFromConfigFile+ :: MonadIO m+ => [String]+ -> Options+ -> FilePath+ -> YamlWriter m ()+travisFromConfigFile args opts path = do+ cabalFiles <- getCabalFiles+ config' <- maybe (return emptyConfig) readConfigFile (optConfig opts)+ let config = optConfigMorphism opts config'+ pkgs <- T.mapM (configFromCabalFile config opts) cabalFiles+ (ghcs, prj) <- case checkVersions (cfgTestedWith config) pkgs of+ Right x -> return x+ Left errors -> putStrLnErrs errors >> mzero+ genTravisFromConfigs args opts isCabalProject config prj ghcs+ where+ isCabalProject :: Maybe FilePath+ isCabalProject+ | "cabal.project" `isPrefixOf` takeFileName path = Just path+ | otherwise = Nothing++ getCabalFiles :: MonadIO m => YamlWriter m (Project FilePath)+ getCabalFiles+ | isNothing isCabalProject = return $ emptyProject & #prjPackages .~ [path]+ | otherwise = do+ contents <- liftIO $ BS.readFile path+ pkgs <- either putStrLnErr return $ parseProjectFile path contents+ over #prjPackages concat `liftM` T.mapM findProjectPackage pkgs++ rootdir = takeDirectory path++ -- See findProjectPackages in cabal-install codebase+ -- this is simple variant.+ findProjectPackage :: MonadIO m => String -> YamlWriter m [FilePath]+ findProjectPackage pkglocstr = do+ mfp <- checkisFileGlobPackage pkglocstr `mplusMaybeT`+ checkIsSingleFilePackage pkglocstr+ maybe (putStrLnErr $ "bad package location: " ++ pkglocstr) return mfp++ checkIsSingleFilePackage pkglocstr = do+ let abspath = rootdir </> pkglocstr+ isFile <- liftIO $ doesFileExist abspath+ isDir <- liftIO $ doesDirectoryExist abspath+ if | isFile && takeExtension pkglocstr == ".cabal" -> return (Just [abspath])+ | isDir -> checkisFileGlobPackage (pkglocstr </> "*.cabal")+ | otherwise -> return Nothing++ -- if it looks like glob, glob+ checkisFileGlobPackage pkglocstr =+ case filter (null . snd) $ readP_to_S parseFilePathGlobRel pkglocstr of+ [(g, "")] -> do+ files <- liftIO $ expandRelGlob rootdir g+ let files' = filter ((== ".cabal") . takeExtension) files+ -- if nothing is matched, skip.+ if null files' then return Nothing else return (Just files')+ _ -> return Nothing++ mplusMaybeT :: Monad m => m (Maybe a) -> m (Maybe a) -> m (Maybe a)+ mplusMaybeT ma mb = do+ mx <- ma+ case mx of+ Nothing -> mb+ Just x -> return (Just x)++configFromCabalFile+ :: MonadIO m => Config -> Options -> FilePath -> YamlWriter m (Package, Set Version)+configFromCabalFile cfg opts cabalFile = do+ gpd <- liftIO $ readGenericPackageDescription maxBound cabalFile++ let compilers = testedWith $ packageDescription gpd+ pkgNameStr = display $ Pkg.pkgName $ package $ packageDescription gpd++ let unknownComps = nub [ c | (c,_) <- compilers, c /= GHC ]+ ghcVerConstrs = [ vc | (GHC,vc) <- compilers ]+ ghcVerConstrs' = simplifyVersionRange $ foldr unionVersionRanges noVersion ghcVerConstrs+ twoDigitGhcVerConstrs = mapMaybe isTwoDigitGhcVersion ghcVerConstrs :: [Version]+ specificGhcVers = nub $ mapMaybe isSpecificVersion ghcVerConstrs++ unless (null twoDigitGhcVerConstrs) $ do+ putStrLnWarn $ "'tested-with:' uses two digit GHC versions (which don't match any existing GHC version): " ++ intercalate ", " (map display twoDigitGhcVerConstrs)+ putStrLnInfo $ "Either use wild-card format, for example 'tested-with: GHC ==7.10.*' or a specific existing version 'tested-with: GHC ==7.10.3'"++ when (null compilers) $ do+ putStrLnErr (unlines $+ [ "empty or missing top-level 'tested-with:' definition in " ++ cabalFile ++ " file; example definition:"+ , ""+ , "tested-with: " ++ intercalate ", " [ "GHC==" ++ display v | v <- lastStableGhcVers ]+ ])++ unless (null unknownComps) $ do+ putStrLnWarn $ "ignoring unsupported compilers mentioned in tested-with: " ++ show unknownComps++ when (null ghcVerConstrs) $ do+ putStrLnErr "'tested-with:' doesn't mention any 'GHC' version"++ when (isNoVersion ghcVerConstrs') $ do+ putStrLnErr "'tested-with:' describes an empty version range for 'GHC'"++ when (isAnyVersion ghcVerConstrs') $ do+ putStrLnErr "'tested-with:' allows /any/ 'GHC' version"++ let unknownGhcVers = sort $ specificGhcVers \\ knownGhcVersions++ unless (null unknownGhcVers) $ do+ putStrLnErr ("'tested-with:' specifically refers to unknown 'GHC' versions: "+ ++ intercalate ", " (map display unknownGhcVers) ++ "\n"+ ++ "Known GHC versions: " ++ intercalate ", " (map display knownGhcVersions))++ let knownGhcVersions'+ | cfgLastInSeries cfg = filterLastMajor knownGhcVersions+ | otherwise = knownGhcVersions++ let testedGhcVersions = filter (`withinRange` ghcVerConstrs') knownGhcVersions'++ when (null testedGhcVersions) $ do+ putStrLnErr "no known GHC version is allowed by the 'tested-with' specification"++ forM_ (optCollections opts) $ \c -> do+ let v = collToGhcVer c+ unless (v `elem` testedGhcVersions) $+ putStrLnErr $ unlines+ [ "collection " ++ c ++ " requires GHC " ++ display v+ , "add 'tested-width: GHC == " ++ display v ++ "' to your .cabal file"+ ]++ let pkg = Pkg pkgNameStr anyVersion (takeDirectory cabalFile) gpd++ return (pkg, S.fromList testedGhcVersions)+ where+ lastStableGhcVers+ = nubBy ((==) `on` ghcMajVer)+ $ sortBy (flip compare)+ $ filter (not . previewGHC . Just)+ $ knownGhcVersions++ isTwoDigitGhcVersion :: VersionRange -> Maybe Version+ isTwoDigitGhcVersion vr = isSpecificVersion vr >>= t+ where+ t v | [_,_] <- versionNumbers v = Just v+ t _ = Nothing++ filterLastMajor = map maximum . groupBy ((==) `on` ghcMajVer)++genTravisFromConfigs+ :: Monad m+ => [String]+ -> Options+ -> Maybe FilePath+ -> Config+ -> Project Package+ -> Set Version+ -> YamlWriter m ()+genTravisFromConfigs argv opts isCabalProject config prj@Project { prjPackages = pkgs } versions' = do+ let folds = cfgFolds config++ putStrLnInfo $+ "Generating Travis-CI config for testing for GHC versions: " ++ ghcVersions++ unless (null $ cfgOsx config) $ do+ putStrLnInfo $ "Also OSX jobs for: " ++ ghcOsxVersions+ unless (S.null omittedOsxVersions) $+ putStrLnWarn $ "Not all GHC versions specified with --osx are generated: " ++ ghcOmittedOsxVersions++ ---------------------------------------------------------------------------+ -- travis.yml generation starts here++ tellStrLns+ [ "# This Travis job script has been generated by a script via"+ , "#"+ , rawRow $ "# haskell-ci " ++ unwords [ "'" ++ a ++ "'" | a <- argv ]+ , "#"+ , "# For more information, see https://github.com/haskell-CI/haskell-ci"+ , "#"+ , rawRow $ "# version: " ++ CURRENT_PACKAGE_VERSION+ , "#"+ , "language: c"+ , "dist: xenial"+ , ""+ , "git:"+ , " submodules: false # whether to recursively clone submodules"+ , ""+ ]++ let projectName = fromMaybe (pkgName $ head pkgs) (cfgProjectName config)+ unless (null $ cfgIrcChannels config) $ tellStrLnsRaw $+ [ "notifications:"+ , " irc:"+ , " channels:"+ ] +++ [ " - \"" ++ chan ++ "\"" | chan <- cfgIrcChannels config ] +++ [ " skip_join: true"+ , " template:"+ , " - \"\\x0313" ++ projectName ++ "\\x03/\\x0306%{branch}\\x03 \\x0314%{commit}\\x03 %{build_url} %{message}\""+ , ""+ ]++ unless (null $ cfgOnlyBranches config) $ tellStrLnsRaw $+ [ "branches:"+ , " only:"+ ] +++ [ " - " ++ branch+ | branch <- cfgOnlyBranches config+ ] +++ [ ""+ ]++ -- cache directories+ when (cfgCache config) $ tellStrLns+ [ "cache:"+ , " directories:"+ , " - $HOME/.cabal/packages"+ , " - $HOME/.cabal/store"+ ]+++ -- on OSX ghc is installed in $HOME so we can cache it+ -- independently of linux+ when (cfgCache config && not (null (cfgOsx config))) $ tellStrLns+ [ " - $HOME/.ghc-install"+ ]++ tellStrLn ""++ -- postgresql+ when (cfgPostgres config) $ tellStrLns+ [ "services:"+ , "- postgresql"+ , "addons:"+ , " postgresql: '10'"+ , ""+ ]++ -- before caching: clear some redundant stuff+ when (cfgCache config) $ tellStrLns+ [ "before_cache:"+ , " - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log"+ , " # remove files that are regenerated by 'cabal update'"+ , " - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.*" -- legacy+ , " - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json" -- TUF meta-data+ , " - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache"+ , " - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar"+ , " - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx"+ , ""+ , " - rm -rfv $CABALHOME/packages/head.hackage" -- if we cache, it will break builds.+ , ""+ ]++ tellStrLn "matrix:"+ tellStrLn " include:"++ let colls = [ (collToGhcVer cid,cid) | cid <- reverse $ optCollections opts ]++ let tellJob :: Monad m => Bool -> Maybe Version -> YamlWriter m ()+ tellJob osx gv = do+ let cvs = dispGhcVersion $ gv >> cfgCabalInstallVersion config+ gvs = dispGhcVersion gv++ xpkgs' = concatMap (',':) (S.toList $ cfgApt config)++ colls' = [ cid | (v,cid) <- colls, Just v == gv ]++ tellStrLnsRaw $ catMaybes+ [ Just $ " - compiler: \"ghc-" <> gvs <> "\""+ , if | Just e <- gv >>= \v -> M.lookup v (cfgEnv config)+ -> Just $ " env: " ++ e+ | previewGHC gv -> Just $ " env: GHCHEAD=true"+ | null colls' -> Nothing+ | otherwise -> Just $ " env: 'COLLECTIONS=" ++ intercalate "," colls' ++ "'"+ , Just $ " addons: {apt: {packages: [ghc-ppa-tools,cabal-install-" <> cvs <> ",ghc-" <> gvs <> xpkgs' <> "], sources: [hvr-ghc]}}"+ ]++ when osx $ tellStrLnsRaw+ [ " os: osx"+ ]++ -- newer GHC first, -head last (which is great).+ -- Alpha release would go first though.+ F.forM_ (reverse $ S.toList versions) $ tellJob False+ F.forM_ (reverse $ S.toList osxVersions) $ tellJob True . Just++ let allowFailures = headGhcVers `S.union` S.map Just (S.filter (`C.withinRange` cfgAllowFailures config) versions')+ unless (S.null allowFailures) $ do+ tellStrLn ""+ tellStrLn " allow_failures:"++ F.forM_ allowFailures $ \gv -> do+ let gvs = dispGhcVersion gv+ tellStrLn $ concat [ " - compiler: \"ghc-", gvs, "\"" ]++ tellStrLns+ [ ""+ , "before_install:"+ , sh "HC=/opt/ghc/bin/${CC}"+ , sh' [2034,2039] "HCPKG=${HC/ghc/ghc-pkg}" -- SC2039. In POSIX sh, string replacement is undefined.+ , sh "unset CC"+ -- cabal+ , sh "CABAL=/opt/ghc/bin/cabal"+ , sh "CABALHOME=$HOME/.cabal"+ -- PATH+ , sh "export PATH=\"$CABALHOME/bin:$PATH\""+ -- rootdir is useful for manual script additions+ , sh "ROOTDIR=$(pwd)"+ ]++ -- macOS installing+ let haskellOnMacos = "https://haskell.futurice.com/haskell-on-macos.py"+ unless (null (cfgOsx config)) $ tellStrLns+ [ sh $ "if [ \"$TRAVIS_OS_NAME\" = \"osx\" ]; then brew update; brew upgrade python@3; curl " ++ haskellOnMacos ++ " | python3 - --make-dirs --install-dir=$HOME/.ghc-install --cabal-alias=head install cabal-install-head ${TRAVIS_COMPILER}; fi"+ , sh' [2034,2039] "if [ \"$TRAVIS_OS_NAME\" = \"osx\" ]; then HC=$HOME/.ghc-install/ghc/bin/$TRAVIS_COMPILER; HCPKG=${HC/ghc/ghc-pkg}; CABAL=$HOME/.ghc-install/ghc/bin/cabal; fi"+ ]++ -- HCNUMVER, numeric HC version, e.g. ghc 7.8.4 is 70804 and 7.10.3 is 71003+ tellStrLns+ [ sh $ "HCNUMVER=$(( $(${HC} --numeric-version|sed -E 's/([0-9]+)\\.([0-9]+)\\.([0-9]+).*/\\1 * 10000 + \\2 * 100 + \\3/') ))"+ , sh "echo $HCNUMVER"+ ]++ unless (null colls) $+ tellStrLn " - IFS=', ' read -a COLLS <<< \"$COLLECTIONS\""++ tellStrLns+ [ ""+ , "install:"+ , sh "${CABAL} --version"+ , sh "echo \"$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]\""+ , sh "TEST=--enable-tests"+ , shForJob versions' (invertVersionRange $ cfgTests config) "TEST=--disable-tests"+ , sh "BENCH=--enable-benchmarks"+ , shForJob versions' (invertVersionRange $ cfgBenchmarks config) "BENCH=--disable-benchmarks"+ , sh "GHCHEAD=${GHCHEAD-false}"+ ]++ -- Update hackage index. Side-effect: ~/.cabal.config is created.+ tellStrLns+ [ sh "travis_retry ${CABAL} update -v"+ , sh "sed -i.bak 's/^jobs:/-- jobs:/' $CABALHOME/config"+ , sh "rm -fv cabal.project cabal.project.local"+ ]++ -- Cabal jobs+ case cfgJobs config >>= cabalJobs of+ Just n -> tellStrLns+ [ sh $ "sed -i.bak 's/^-- jobs:.*/jobs: " ++ show n ++ "/' $CABALHOME/config"+ ]+ _ -> return ()++ -- GHC jobs+ case cfgJobs config >>= ghcJobs of+ Just m -> tellStrLns+ [ shForJob versions' (orLaterVersion (mkVersion [7,8])) $+ "sed -i.bak 's/-- ghc-options:.*/ghc-options: -j" ++ show m ++ "/' $CABALHOME/config"+ ]+ _ -> return ()++ -- Add head.hackage repository to ~/.cabal/config+ -- (locally you want to add it to cabal.project)+ unless (S.null headGhcVers) $ tellStrLns+ [ " # Overlay Hackage Package Index for GHC HEAD: https://github.com/hvr/head.hackage"+ , " - |"+ , " if $GHCHEAD; then"+ , " sed -i 's/-- allow-newer: .*/allow-newer: *:base/' $CABALHOME/config"+ , " for pkg in $($HCPKG list --simple-output); do pkg=$(echo $pkg | sed 's/-[^-]*$//'); sed -i \"s/allow-newer: /allow-newer: *:$pkg, /\" $CABALHOME/config; done"+ , ""+ , " echo 'repository head.hackage' >> $CABALHOME/config"+ , " echo ' url: http://head.hackage.haskell.org/' >> $CABALHOME/config"+ , " echo ' secure: True' >> $CABALHOME/config"+ , " echo ' root-keys: 07c59cb65787dedfaef5bd5f987ceb5f7e5ebf88b904bbd4c5cbdeb2ff71b740' >> $CABALHOME/config"+ , " echo ' 2e8555dde16ebd8df076f1a8ef13b8f14c66bad8eafefd7d9e37d0ed711821fb' >> $CABALHOME/config"+ , " echo ' 8f79fd2389ab2967354407ec852cbe73f2e8635793ac446d09461ffb99527f6e' >> $CABALHOME/config"+ , " echo ' key-threshold: 3' >> $CABALHOME.config"+ , ""+ , " grep -Ev -- '^\\s*--' $CABALHOME/config | grep -Ev '^\\s*$'"+ , ""+ , " ${CABAL} new-update head.hackage -v"+ , " fi"+ ]++ -- Output cabal.config+ tellStrLns+ [ sh "grep -Ev -- '^\\s*--' $CABALHOME/config | grep -Ev '^\\s*$'"+ ]++ -- Install doctest+ let doctestVersionConstraint+ | isAnyVersion (cfgDoctestVersion doctestConfig) = ""+ | otherwise = " --constraint='doctest " ++ display (cfgDoctestVersion doctestConfig) ++ "'"+ when (cfgDoctestEnabled doctestConfig) $ tellStrLns+ [ shForJob versions' doctestJobVersionRange $ "${CABAL} new-install -w ${HC} -j2 doctest" ++ doctestVersionConstraint+ ]++ -- Install hlint+ let hlintVersionConstraint+ | isAnyVersion (cfgHLintVersion hlintConfig) = ""+ | otherwise = " --constraint='hlint " ++ display (cfgHLintVersion hlintConfig) ++ "'"+ when (cfgHLintEnabled hlintConfig) $ tellStrLns+ [ shForJob versions' (hlintJobVersionRange versions (cfgHLintJob hlintConfig)) $+ "${CABAL} new-install -w ${HC} -j2 hlint" ++ hlintVersionConstraint+ ]++ -- create cabal.project file+ generateCabalProject False++ let pkgFilter = intercalate " | " $ map (wrap.pkgName) pkgs+ wrap s = "grep -Fv \"" ++ s ++ " ==\""+ unless (null colls) $ tellStrLnsRaw+ [ " - for COLL in \"${COLLS[@]}\"; do"+ , " echo \"== collection $COLL ==\";"+ , " ghc-travis collection ${COLL} > /dev/null || break;"+ , " ghc-travis collection ${COLL} | " ++ pkgFilter ++ " > cabal.project.freeze;"+ , " grep ' collection-id' cabal.project.freeze;"+ , " rm -rf dist-newstyle/;"+ , " $(CABAL} new-build -w ${HC} ${TEST} ${BENCH} --project-file=\"" ++ projectFile ++ "\" --dep -j2 all;"+ , " done"+ , ""+ ]++ forM_ pkgs $ \Pkg{pkgDir} -> tellStrLns+ [ sh $ "if [ -f \"" ++ pkgDir ++ "/configure.ac\" ]; then (cd \"" ++ pkgDir ++ "\" && autoreconf -i); fi"+ ]++ let quotedRmPaths =+ ".ghc.environment.*"+ ++ " " +++ unwords (quotedPaths (\Pkg{pkgDir} -> pkgDir ++ "/dist"))++ tellStrLns+ [ sh $ "rm -f cabal.project.freeze"+ ]++ -- Install dependencies+ when (cfgInstallDeps config) $ do+ tellStrLns+ -- dump install plan+ [ sh $ "${CABAL} new-freeze -w ${HC} ${TEST} ${BENCH} --project-file=\"" ++ projectFile ++"\" --dry"+ , sh $ "cat \"" ++ projectFile ++ ".freeze\" | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'"+ , sh $ "rm \"" ++ projectFile ++ ".freeze\""++ -- install dependencies+ , sh $ "${CABAL} new-build -w ${HC} ${TEST} ${BENCH} --project-file=\"" ++ projectFile ++"\" --dep -j2 all"+ ]+ tellStrLns+ [ shForJob versions' (cfgNoTestsNoBench config) $ "${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks --project-file=\"" ++ projectFile ++ "\" --dep -j2 all"+ ]++ tellStrLns+ [ sh $ "rm -rf " ++ quotedRmPaths+ , sh $ "DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)"+ ]++ tellStrLns+ [ ""+ , "# Here starts the actual work to be performed for the package under test;"+ , "# any command which exits with a non-zero exit code causes the build to fail."+ , "script:"+ , " # test that source-distributions can be generated"+ ]++ foldedTellStrLns FoldSDist "Packaging..." folds $ do+ tellStrLns+ [ sh $ "${CABAL} new-sdist all"+ ]++ let tarFiles = "dist-newstyle" </> "sdist" </> "*.tar.gz"+++ foldedTellStrLns FoldUnpack "Unpacking..." folds $ do+ tellStrLns+ [ sh $ "mv " ++ tarFiles ++ " ${DISTDIR}/"+ , sh $ "cd ${DISTDIR} || false" -- fail explicitly, makes SC happier+ , sh $ "find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \\;"+ ]+ generateCabalProject True++ unless (equivVersionRanges noVersion $ cfgNoTestsNoBench config) $ foldedTellStrLns FoldBuild "Building..." folds $ tellStrLns+ [ comment "this builds all libraries and executables (without tests/benchmarks)"+ , shForJob versions' (cfgNoTestsNoBench config) $ "${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks all"+ ]++ tellStrLns [""]+++ foldedTellStrLns FoldBuildEverything+ "Building with tests and benchmarks..." folds $ tellStrLns+ [ comment "build & run tests, build benchmarks"+ , sh "${CABAL} new-build -w ${HC} ${TEST} ${BENCH} all"+ ]++ -- cabal new-test fails if there are no test-suites.+ when hasTests $+ foldedTellStrLns FoldTest "Testing..." folds $ tellStrLns+ [ sh $ mconcat+ [ "if [ \"x$TEST\" = \"x--enable-tests\" ]; then "+ , if cfgNoise config+ then "${CABAL} "+ else "(set -o pipefail; ${CABAL} -vnormal+nowrap+markoutput "+ , "new-test -w ${HC} ${TEST} ${BENCH} all"+ , if cfgNoise config+ then ""+ else " 2>&1 | sed '/^-----BEGIN CABAL OUTPUT-----$/,/^-----END CABAL OUTPUT-----$/d' )"+ , "; fi"+ ]+ ]++ tellStrLns [""]++ when (cfgDoctestEnabled doctestConfig) $ do+ let doctestOptions = unwords $ cfgDoctestOptions doctestConfig+ tellStrLns [ comment "doctest" ]+ foldedTellStrLns FoldDoctest "Doctest..." folds $ do+ forM_ pkgs $ \Pkg{pkgName,pkgGpd,pkgJobs} -> do+ forM_ (doctestArgs pkgGpd) $ \args -> do+ let args' = unwords args+ unless (null args) $ tellStrLns+ [ shForJob versions' (doctestJobVersionRange `intersectVersionRanges` pkgJobs) $+ "(cd " ++ pkgName ++ "-* && doctest " ++ doctestOptions ++ " " ++ args' ++ ")"+ ]+ tellStrLns [ "" ]++ when (cfgHLintEnabled hlintConfig) $ do+ let "" <+> ys = ys+ xs <+> "" = xs+ xs <+> ys = xs ++ " " ++ ys++ prependSpace "" = ""+ prependSpace xs = " " ++ xs++ let hlintOptions = prependSpace $ maybe "" ("-h ${ROOTDIR}/" ++) (cfgHLintYaml hlintConfig) <+> unwords (cfgHLintOptions hlintConfig)++ tellStrLns [ comment "hlint" ]+ foldedTellStrLns FoldHLint "HLint.." folds $ do+ forM_ pkgs $ \Pkg{pkgName,pkgGpd,pkgJobs} -> do+ -- note: similar arguments work so far for doctest and hlint+ forM_ (hlintArgs pkgGpd) $ \args -> do+ let args' = unwords args+ unless (null args) $ tellStrLns+ [ shForJob versions' (hlintJobVersionRange versions (cfgHLintJob hlintConfig) `intersectVersionRanges` pkgJobs) $+ "(cd " ++ pkgName ++ "-* && hlint" ++ hlintOptions ++ " " ++ args' ++ ")"+ ]+ tellStrLns [ "" ]++ when (cfgCheck config) $+ foldedTellStrLns FoldCheck "cabal check..." folds $ do+ tellStrLns [ comment "cabal check" ]+ forM_ pkgs $ \Pkg{pkgName,pkgJobs} -> tellStrLns+ [ shForJob versions' pkgJobs $+ "(cd " ++ pkgName ++ "-* && ${CABAL} check)"+ ]+ tellStrLns [ "" ]++ when (hasLibrary && not (equivVersionRanges noVersion $ cfgHaddock config)) $+ foldedTellStrLns FoldHaddock "Haddock..." folds $ tellStrLns+ [ comment "haddock"+ , shForJob versions' (cfgHaddock config) "${CABAL} new-haddock -w ${HC} ${TEST} ${BENCH} all"+ , ""+ ]++ unless (null colls) $+ foldedTellStrLns FoldStackage "Stackage builds..." folds $ tellStrLnsRaw+ [ " # try building & testing for package collections"+ , " - for COLL in \"${COLLS[@]}\"; do"+ , " echo \"== collection $COLL ==\";"+ , " ghc-travis collection ${COLL} > /dev/null || break;"+ , " ghc-travis collection ${COLL} | " ++ pkgFilter ++ " > cabal.project.freeze;"+ , " grep ' collection-id' cabal.project.freeze;"+ , " rm -rf dist-newstyle/;"+ , " ${CABAL} new-build -w ${HC} ${TEST} ${BENCH} all || break;"+ , " if [ \"x$TEST\" = \"x--enable-tests\" ]; then ${CABAL} new-test -w ${HC} ${TEST} ${BENCH} all || break; fi;"+ , " done"+ , ""+ ]++ -- Have to build last, as we remove cabal.project.local+ unless (equivVersionRanges noVersion $ cfgUnconstrainted config) $ foldedTellStrLns FoldBuildInstalled+ "Building without installed constraints for packages in global-db..." folds $ tellStrLns+ [ comment "Build without installed constraints for packages in global-db"+ , shForJob versions' (cfgUnconstrainted config) "rm -f cabal.project.local; ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks all;"+ , ""+ ]++ -- and now, as we don't have cabal.project.local;+ -- we can test with other constraint sets+ let constraintSets = cfgConstraintSets config+ unless (null constraintSets) $ do+ tellStrLns+ [ comment "Constraint sets"+ , sh "rm -rf cabal.project.local"+ , ""+ ]+ forM_ constraintSets $ \cs -> do+ let name = csName cs+ let constraintFlags = concatMap (\x -> " --constraint='" ++ x ++ "'") (csConstraints cs)+ let cmd | csRunTests cs = "${CABAL} new-test -w ${HC} --enable-tests --enable-benchmarks"+ | otherwise = "${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks"+ tellStrLns [ comment $ "Constraint set " ++ name ]+ foldedTellStrLns' FoldConstraintSets name ("Constraint set " ++ name) folds $ tellStrLns+ [ shForJob versions' (csGhcVersions cs) $+ cmd ++ " " ++ constraintFlags ++ " all"+ , ""+ ]+ tellStrLns [""]++ tellStrLnsRaw+ [ "# REGENDATA " ++ show argv+ , "# EOF"+ ]++ return ()+ where+ doctestConfig = cfgDoctest config+ hlintConfig = cfgHLint config++ hasTests = F.any (\Pkg{pkgGpd} -> not . null $ condTestSuites pkgGpd) pkgs+ hasLibrary = F.any (\Pkg{pkgGpd} -> isJust $ condLibrary pkgGpd) pkgs++ -- GHC versions which need head.hackage+ headGhcVers = S.filter previewGHC versions++ generateCabalProject dist = do+ tellStrLns+ [ sh "rm -f cabal.project"+ , sh "touch cabal.project"+ ]+ F.forM_ pkgs $ \pkg -> do+ let p | dist = pkgName pkg ++ "-*/*.cabal"+ | otherwise = pkgDir pkg+ tellStrLns $+ [ shForJob versions' (pkgJobs pkg) $ "printf 'packages: \"" ++ p ++ "\"\\n' >> cabal.project"+ ]++ case cfgCopyFields config of+ CopyFieldsNone -> return ()+ CopyFieldsAll -> unless (null (prjOrigFields prj)) $ tellStrLns+ [ sh $ "echo '" ++ l ++ "' >> cabal.project"+ | l <- lines $ C.showFields' 2 $ prjOrigFields prj+ , not (null l)+ ]+ CopyFieldsSome -> do+ F.forM_ (prjConstraints prj) $ \xs -> do+ let s = concat (lines xs)+ tellStrLns+ [ sh $ "echo 'constraints: " ++ s ++ "' >> cabal.project"+ ]+ F.forM_ (prjAllowNewer prj) $ \xs -> do+ let s = concat (lines xs)+ tellStrLns+ [ sh $ "echo 'allow-newer: " ++ s ++ "' >> cabal.project"+ ]+ unless (null (cfgLocalGhcOptions config)) $ forM_ pkgs $ \Pkg{pkgName} -> do+ let s = unwords $ map (show . PU.showToken) $ cfgLocalGhcOptions config+ tellStrLns+ [ sh $ "echo 'package " ++ pkgName ++ "' >> cabal.project"+ , sh $ "echo ' ghc-options: " ++ s ++ "' >> cabal.project"+ ]++ when (prjReorderGoals prj) $+ tellStrLns+ [ sh $ "echo 'reorder-goals: True' >> cabal.project"+ ]++ F.forM_ (prjMaxBackjumps prj) $ \bj ->+ tellStrLns+ [ sh $ "echo 'max-backjumps: " ++ show bj ++ "' >> cabal.project"+ ]++ case prjOptimization prj of+ OptimizationOn -> return ()+ OptimizationOff -> tellStrLns [ sh $ "echo 'optimization: False' >> cabal.project " ]+ OptimizationLevel l -> tellStrLns [ sh $ "echo 'optimization: " ++ show l ++ "' >> cabal.project " ]++ F.forM_ (prjSourceRepos prj) $ \repo -> do+ let repo' = PP.render $ C.prettyFieldGrammar (C.sourceRepoFieldGrammar $ C.RepoKindUnknown "unused") repo+ tellStrLns [ sh $ "echo 'source-repository-package' >> cabal.project" ]+ tellStrLns [ sh $ "echo ' " ++ l ++ "' >> cabal.project" | l <- lines repo' ]++ -- mandatory cabal.project setup+ tellStrLns+ [ sh $ "printf 'write-ghc-environment-files: always\\n' >> cabal.project"+ ]++ unless (null (cfgRawProject config)) $ tellStrLns+ [ sh $ "echo '" ++ l ++ "' >> cabal.project"+ | l <- lines $ C.showFields' 2 $ cfgRawProject config+ , not (null l)+ ]++ -- also write cabal.project.local file with+ -- @+ -- constraints: base installed+ -- constraints: array installed+ -- ...+ --+ -- omitting any local package names+ case normaliseInstalled (cfgInstalled config) of+ InstalledDiff pns -> tellStrLns+ [ sh $ "touch cabal.project.local"+ , sh $ unwords+ [ "for pkg in $($HCPKG list --simple-output); do"+ , "echo $pkg"+ , "| sed 's/-[^-]*$//'"+ , "| grep -vE -- " ++ re+ , "| sed 's/^/constraints: /'"+ , "| sed 's/$/ installed/'"+ , ">> cabal.project.local; done"+ ]+ ]+ where+ pns' = S.map unPackageName pns `S.union` foldMap (S.singleton . pkgName) pkgs+ re = "'^(" ++ intercalate "|" (S.toList pns') ++ ")$'"++ InstalledOnly pns | not (null pns') -> tellStrLns+ [ sh $ "touch cabal.project.local"+ , sh' [2043] $ unwords+ [ "for pkg in " ++ unwords (S.toList pns') ++ "; do"+ , "echo \"constraints: $pkg installed\" >> cabal.project"+ , ">> cabal.project.local; done"+ ]+ ]+ where+ pns' = S.map unPackageName pns `S.difference` foldMap (S.singleton . pkgName) pkgs++ -- otherwise: nothing+ _ -> pure ()++ tellStrLns+ [ sh $ "cat cabal.project || true"+ , sh $ "cat cabal.project.local || true"+ ]++ projectFile :: FilePath+ projectFile = fromMaybe "cabal.project" isCabalProject++ quotedPaths :: (Package -> FilePath) -> [String]+ quotedPaths f = map (f . quote) pkgs+ where+ quote pkg = pkg{ pkgDir = "\"" ++ pkgDir pkg ++ "\"" }++ showVersions :: Set (Maybe Version) -> String+ showVersions = unwords . map dispGhcVersion . S.toList++ -- specified ersions+ osxVersions' :: Set Version+ osxVersions' = cfgOsx config++ versions :: Set (Maybe Version)+ versions+ | cfgGhcHead config = S.insert Nothing $ S.map Just versions'+ | otherwise = S.map Just versions'++ ghcVersions :: String+ ghcVersions = showVersions versions++ osxVersions, omittedOsxVersions :: Set Version+ (osxVersions, omittedOsxVersions) = S.partition (`S.member` versions') osxVersions'++ ghcOsxVersions :: String+ ghcOsxVersions = showVersions $ S.map Just osxVersions++ ghcOmittedOsxVersions :: String+ ghcOmittedOsxVersions = showVersions $ S.map Just omittedOsxVersions+++collToGhcVer :: String -> Version+collToGhcVer cid = case simpleParse cid of+ Nothing -> error ("invalid collection-id syntax " ++ show cid)+ Just (PackageIdentifier n (versionNumbers -> v))+ | display n /= "lts" -> error ("unknown collection " ++ show cid)+ | isPrefixOf [0] v -> mkVersion [7,8,3]+ | isPrefixOf [1] v -> mkVersion [7,8,4]+ | isPrefixOf [2] v -> mkVersion [7,8,4]+ | isPrefixOf [3] v -> mkVersion [7,10,2]+ | isPrefixOf [4] v -> mkVersion [7,10,3]+ | isPrefixOf [5] v -> mkVersion [7,10,3]+ | isPrefixOf [6] v -> mkVersion [7,10,3]+ | isPrefixOf [7] v -> mkVersion [8,0,1]+ | otherwise -> error ("unknown collection " ++ show cid)++-------------------------------------------------------------------------------+-- Doctest+-------------------------------------------------------------------------------++doctestJobVersionRange :: VersionRange+doctestJobVersionRange = orLaterVersion $ mkVersion [8,0]++-- | Modules arguments to the library+--+-- * We check the library component+--+-- * If there are hs-source-dirs, use them+--+-- * otherwise use exposed + other modules+--+-- * Also add default-extensions+--+-- /Note:/ same argument work for hlint too, but not exactly+--+doctestArgs :: GenericPackageDescription -> [[String]]+doctestArgs gpd =+ [ libraryModuleArgs c+ | c <- flattenPackageDescription gpd ^.. L.library . traverse+ ] +++ [ libraryModuleArgs c+ | c <- flattenPackageDescription gpd ^.. L.subLibraries . traverse+ ]++libraryModuleArgs :: PD.Library -> [String]+libraryModuleArgs l+ | null dirsOrMods = []+ | otherwise = exts ++ dirsOrMods+ where+ bi = l ^. L.buildInfo++ dirsOrMods+ | null (PD.hsSourceDirs bi) = map display (PD.exposedModules l)+ | otherwise = PD.hsSourceDirs bi++ exts = map (("-X" ++) . display) (PD.defaultExtensions bi)++executableModuleArgs :: PD.Executable -> [String]+executableModuleArgs e+ | null dirsOrMods = []+ | otherwise = exts ++ dirsOrMods+ where+ bi = e ^. L.buildInfo++ dirsOrMods+ -- note: we don't try to find main_is location, if hsSourceDirs is empty.+ | null (PD.hsSourceDirs bi) = map display (PD.otherModules bi)+ | otherwise = PD.hsSourceDirs bi++ exts = map (("-X" ++) . display) (PD.defaultExtensions bi)++-------------------------------------------------------------------------------+-- HLint+-------------------------------------------------------------------------------++hlintJobVersionRange :: Set (Maybe Version) -> HLintJob -> VersionRange+hlintJobVersionRange vs HLintJobLatest = case S.maxView vs of+ Just (Just v, _) -> thisVersion v+ _ -> thisVersion $ mkVersion [8,6,3]+hlintJobVersionRange _ (HLintJob v) = thisVersion v++hlintArgs :: GenericPackageDescription -> [[String]]+hlintArgs gpd =+ [ libraryModuleArgs c+ | c <- flattenPackageDescription gpd ^.. L.library . traverse+ ] +++ [ libraryModuleArgs c+ | c <- flattenPackageDescription gpd ^.. L.subLibraries . traverse+ ] +++ [ executableModuleArgs c+ | c <- flattenPackageDescription gpd ^.. L.executables . traverse+ ]
+ src/HaskellCI/Cli.hs view
@@ -0,0 +1,91 @@+-- | Most of client interface.+module HaskellCI.Cli where++import Prelude ()+import Prelude.Compat++import Control.Applicative ((<|>))+import System.Exit (exitFailure)+import System.IO (hPutStrLn, stderr)++import qualified Options.Applicative as O++import HaskellCI.Config+import HaskellCI.OptparseGrammar++-------------------------------------------------------------------------------+-- Command+-------------------------------------------------------------------------------++data Command+ = CommandTravis FilePath+ | CommandRegenerate+ | CommandListGHC+ | CommandDumpConfig+ deriving Show++-------------------------------------------------------------------------------+-- Options+-------------------------------------------------------------------------------++data Options = Options+ { optCollections :: [String]+ , optOutput :: Maybe FilePath+ , optConfig :: Maybe FilePath+ , optConfigMorphism :: Config -> Config+ }++instance Semigroup Options where+ Options a b d e <> Options a' b' d' e' =+ Options (a <> a') (b <|> b') (d <|> d') (e' . e)++defaultOptions :: Options+defaultOptions = Options+ { optCollections = []+ , optOutput = Nothing+ , optConfig = Nothing+ , optConfigMorphism = id+ }++-------------------------------------------------------------------------------+-- Parsers+-------------------------------------------------------------------------------++optionsP :: O.Parser Options+optionsP = Options+ <$> pure []+ <*> O.optional (O.strOption (O.long "output" <> O.short 'o' <> O.metavar "FILE" <> O.help "Optput file (stdout if omitted)"))+ <*> O.optional (O.strOption (O.long "config" <> O.metavar "CONFIGFILE" <> O.help "Configuration file"))+ <*> runOptparseGrammar configGrammar++cliParserInfo :: O.ParserInfo (Command, Options)+cliParserInfo = O.info ((,) <$> cmdP <*> optionsP O.<**> O.helper) $ mconcat+ [ O.fullDesc+ , O.header "haskell-ci - generate CI scripts for Haskell projects"+ ]+ where+ cmdP = O.subparser (mconcat+ [ O.command "regenerate" $ O.info (pure CommandRegenerate) $ O.progDesc "Regenerate .travis.yml"+ , O.command "travis" $ O.info travisP $ O.progDesc "Generate travis-ci config"+ , O.command "list-ghc" $ O.info (pure CommandListGHC) $ O.progDesc "List known GHC versions"+ , O.command "dump-config" $ O.info (pure CommandDumpConfig) $ O.progDesc "Dump cabal.haskell-ci config with default values" + ]) <|> travisP++ travisP = CommandTravis+ <$> O.strArgument (O.metavar "CABAL.FILE" <> O.help "Either <pkg.cabal> or cabal.project")++-------------------------------------------------------------------------------+-- Parsing helpers+-------------------------------------------------------------------------------++parseTravis :: [String] -> IO (FilePath, Options)+parseTravis argv = case res of+ O.Success x -> return x+ O.Failure f -> case O.renderFailure f "haskell-ci" of+ (help, _) -> hPutStrLn stderr help >> exitFailure+ O.CompletionInvoked _ -> exitFailure -- unexpected+ where+ res = O.execParserPure (O.prefs mempty) (O.info ((,) <$> cmdP <*> optionsP) mempty) argv+ cmdP = O.strArgument (O.metavar "CABAL.FILE" <> O.help "Either <pkg.cabal> or cabal.project")++
+ src/HaskellCI/Config.hs view
@@ -0,0 +1,263 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE OverloadedStrings #-}+module HaskellCI.Config where++import Control.Monad.IO.Class (MonadIO (..))+import Data.Coerce (coerce)+import Data.Generics.Labels ()+import Distribution.Simple.Utils (fromUTF8BS)+import Distribution.Types.Version (Version)+import Distribution.Types.VersionRange (VersionRange, anyVersion,+ noVersion)+import GHC.Generics (Generic)+import Lens.Micro (over)++import qualified Data.ByteString as BS+import qualified Data.Map as M+import qualified Data.Set as S+import qualified Distribution.CabalSpecVersion as C+import qualified Distribution.Compat.CharParsing as C+import qualified Distribution.Compat.Newtype as C+import qualified Distribution.FieldGrammar as C+import qualified Distribution.Fields.Pretty as C+import qualified Distribution.Parsec.Class as C+import qualified Distribution.Parsec.Common as C+import qualified Distribution.Parsec.Newtypes as C+import qualified Distribution.Parsec.Parser as C+import qualified Distribution.Parsec.ParseResult as C+import qualified Distribution.Pretty as C+import qualified Distribution.Types.Version as C+import qualified Text.PrettyPrint as PP++import HaskellCI.Config.ConstraintSet+import HaskellCI.Config.CopyFields+import HaskellCI.Config.Doctest+import HaskellCI.Config.Folds+import HaskellCI.Config.HLint+import HaskellCI.Config.Installed+import HaskellCI.Config.Jobs+import HaskellCI.Newtypes+import HaskellCI.OptionsGrammar+import HaskellCI.ParsecUtils+import HaskellCI.TestedWith++-- TODO: split other blocks like DoctestConfig+data Config = Config+ { cfgCabalInstallVersion :: Maybe Version+ , cfgJobs :: Maybe Jobs+ , cfgTestedWith :: !TestedWithJobs+ , cfgCopyFields :: !CopyFields+ , cfgLocalGhcOptions :: [String]+ , cfgCache :: !Bool+ , cfgNoise :: !Bool+ , cfgInstallDeps :: !Bool+ , cfgInstalled :: [Installed]+ , cfgTests :: !VersionRange+ , cfgBenchmarks :: !VersionRange+ , cfgHaddock :: !VersionRange+ , cfgNoTestsNoBench :: !VersionRange+ , cfgUnconstrainted :: !VersionRange+ , cfgCheck :: !Bool+ , cfgOnlyBranches :: [String]+ , cfgIrcChannels :: [String]+ , cfgProjectName :: Maybe String+ , cfgFolds :: S.Set Fold+ , cfgGhcHead :: !Bool+ , cfgPostgres :: !Bool+ , cfgEnv :: M.Map Version String+ , cfgAllowFailures :: !VersionRange+ , cfgLastInSeries :: !Bool+ , cfgOsx :: S.Set Version+ , cfgApt :: S.Set String+ , cfgDoctest :: !DoctestConfig+ , cfgHLint :: !HLintConfig+ , cfgConstraintSets :: [ConstraintSet]+ , cfgRawProject :: [C.PrettyField]+ }+ deriving (Show, Generic)++defaultCabalInstallVersion :: Maybe Version+defaultCabalInstallVersion = Just (C.mkVersion [2,4])++emptyConfig :: Config+emptyConfig = Config+ { cfgCabalInstallVersion = defaultCabalInstallVersion+ , cfgJobs = Nothing+ , cfgTestedWith = TestedWithUniform+ , cfgCopyFields = CopyFieldsSome+ , cfgDoctest = DoctestConfig+ { cfgDoctestEnabled = False+ , cfgDoctestOptions = []+ , cfgDoctestVersion = defaultDoctestVersion+ }+ , cfgHLint = HLintConfig+ { cfgHLintEnabled = False+ , cfgHLintJob = HLintJobLatest+ , cfgHLintYaml = Nothing+ , cfgHLintVersion = defaultHLintVersion+ , cfgHLintOptions = []+ }+ , cfgLocalGhcOptions = []+ , cfgConstraintSets = []+ , cfgCache = True+ , cfgNoise = True+ , cfgInstalled = []+ , cfgInstallDeps = True+ , cfgTests = anyVersion+ , cfgBenchmarks = anyVersion+ , cfgHaddock = anyVersion+ , cfgNoTestsNoBench = anyVersion+ , cfgUnconstrainted = anyVersion+ , cfgCheck = True+ , cfgOnlyBranches = []+ , cfgIrcChannels = []+ , cfgProjectName = Nothing+ , cfgFolds = S.empty+ , cfgGhcHead = False+ , cfgPostgres = False+ , cfgEnv = M.empty+ , cfgAllowFailures = noVersion+ , cfgLastInSeries = False+ , cfgOsx = S.empty+ , cfgApt = S.empty+ , cfgRawProject = []+ }++-------------------------------------------------------------------------------+-- Grammar+-------------------------------------------------------------------------------++configGrammar+ :: (OptionsGrammar g, Applicative (g Config), Applicative (g DoctestConfig), Applicative (g HLintConfig))+ => g Config Config+configGrammar = Config+ <$> C.optionalFieldDefAla "cabal-install-version" HeadVersion #cfgCabalInstallVersion defaultCabalInstallVersion+ ^^^ metahelp "VERSION" "cabal-install version for all jobs"+ <*> C.optionalField "jobs" #cfgJobs+ ^^^ metahelp "JOBS" "jobs (N:M - cabal:ghc)"+ <*> C.optionalFieldDef "jobs-selection" #cfgTestedWith TestedWithUniform+ ^^^ metahelp "uniform|any" "Jobs selection across packages"+ <*> C.optionalFieldDef "copy-fields" #cfgCopyFields CopyFieldsSome+ ^^^ metahelp "none|some|all" "Copy ? fields from cabal.project fields"+ <*> C.monoidalFieldAla "local-ghc-options" (C.alaList' C.NoCommaFSep C.Token') #cfgLocalGhcOptions+ ^^^ metahelp "OPTS" "--ghc-options for local packages"+ <*> C.booleanFieldDef "cache" #cfgCache True+ ^^^ help "Disable caching"+ <*> C.booleanFieldDef "cabal-noise" #cfgNoise True+ ^^^ help "Make cabal less noisy"+ <*> C.booleanFieldDef "install-dependencies" #cfgInstallDeps True+ ^^^ help "Skip separate dependency installation step"+ <*> C.monoidalFieldAla "installed" (C.alaList C.FSep) #cfgInstalled+ ^^^ metahelp "+/-PKG" "Specify 'constraint: ... installed' packages"+ <*> rangeField "tests" #cfgTests anyVersion+ ^^^ metahelp "RANGE" "Build and run tests with"+ <*> rangeField "benchmarks" #cfgBenchmarks anyVersion+ ^^^ metahelp "RANGE" "Build benchmarks"+ <*> rangeField "haddock" #cfgHaddock anyVersion+ ^^^ metahelp "RANGE" "Haddock step"+ <*> rangeField "no-tests-no-benchmarks" #cfgNoTestsNoBench anyVersion+ ^^^ metahelp "RANGE" "Build without tests and benchmarks"+ <*> rangeField "unconstrained" #cfgUnconstrainted anyVersion+ ^^^ metahelp "RANGE" "Make unconstrained build"+ <*> C.booleanFieldDef "cabal-check" #cfgCheck True+ ^^^ help "Disable cabal check run"+ <*> C.monoidalFieldAla "branches" (C.alaList' C.FSep C.Token') #cfgOnlyBranches+ ^^^ metahelp "BRANCH" "Enable builds only for specific branches"+ <*> C.monoidalFieldAla "irc-channels" (C.alaList' C.FSep C.Token') #cfgIrcChannels+ ^^^ metahelp "IRC" "Enable IRC notifications to given channel (e.g. 'irc.freenode.org#haskell-lens')"+ <*> C.optionalFieldAla "project-name" C.Token' #cfgProjectName+ ^^^ metahelp "NAME" "Project name (used for IRC notifications), defaults to package name or name of first package listed in cabal.project file"+ <*> C.monoidalFieldAla "folds" Folds #cfgFolds+ ^^^ metahelp "FOLD" "Build steps to fold"+ <*> C.booleanFieldDef "ghc-head" #cfgGhcHead False+ ^^^ help "Add ghc-head job"+ <*> C.booleanFieldDef "postgresql" #cfgPostgres False+ ^^^ help "Add postgresql service"+ <*> C.monoidalFieldAla "env" Env #cfgEnv+ ^^^ metahelp "ENV" "Environment variables per job (e.g. `8.0.2:HADDOCK=false`)"+ <*> C.optionalFieldDefAla "allow-failures" Range #cfgAllowFailures noVersion+ ^^^ metahelp "JOB" "Allow failures of particular GHC version"+ <*> C.booleanFieldDef "last-in-series" #cfgLastInSeries False+ ^^^ help "[Discouraged] Assume there are only GHCs last in major series: 8.2.* will match only 8.2.2"+ <*> C.monoidalFieldAla "osx" (alaSet C.NoCommaFSep) #cfgOsx+ ^^^ metahelp "JOB" "Jobs to additionally build with OSX"+ <*> C.monoidalFieldAla "apt" (alaSet' C.NoCommaFSep C.Token') #cfgApt+ ^^^ metahelp "PKG" "Additional apt packages to install"+ <*> C.blurFieldGrammar #cfgDoctest doctestConfigGrammar+ <*> C.blurFieldGrammar #cfgHLint hlintConfigGrammar+ <*> pure [] -- constraint sets+ <*> pure [] -- raw project fields++-------------------------------------------------------------------------------+-- Reading+-------------------------------------------------------------------------------++readConfigFile :: MonadIO m => FilePath -> m Config+readConfigFile = liftIO . readAndParseFile parseConfigFile++parseConfigFile :: [C.Field C.Position] -> C.ParseResult Config+parseConfigFile fields0 = do+ config <- C.parseFieldGrammar C.cabalSpecLatest fields configGrammar+ config' <- traverse parseSection $ concat sections+ return (foldr (.) id config' config)+ where+ (fields, sections) = C.partitionFields fields0++ parseSection :: C.Section C.Position -> C.ParseResult (Config -> Config)+ parseSection (C.MkSection (C.Name pos name) args cfields)+ | name == "constraint-set" = do+ name' <- parseName pos args+ let (fs, _sections) = C.partitionFields cfields+ cs <- C.parseFieldGrammar C.cabalSpecLatest fs (constraintSetGrammar name')+ return $ over #cfgConstraintSets (cs :)+ | name == "raw-project" = do+ let fs = C.fromParsecFields cfields+ return $ over #cfgRawProject (++ fs)+ | otherwise = do+ C.parseWarning pos C.PWTUnknownSection $ "Unknown section " ++ fromUTF8BS name+ return id++-------------------------------------------------------------------------------+-- Env+-------------------------------------------------------------------------------++newtype Env = Env (M.Map Version String)++instance C.Newtype Env (M.Map Version String) where+ pack = coerce+ unpack = coerce++instance C.Parsec Env where+ parsec = Env . M.fromList <$> C.parsecLeadingCommaList p where+ p = do+ v <- C.parsec+ _ <- C.char ':'+ s <- C.munch1 $ \c -> c /= ','+ return (v, s)++instance C.Pretty Env where+ pretty (Env m) = PP.fsep . PP.punctuate PP.comma . map p . M.toList $ m where+ p (v, s) = C.pretty v PP.<> PP.colon PP.<> PP.text s++-------------------------------------------------------------------------------+-- From Cabal+-------------------------------------------------------------------------------++parseName :: C.Position -> [C.SectionArg C.Position] -> C.ParseResult String+parseName pos args = fromUTF8BS <$> parseNameBS pos args++parseNameBS :: C.Position -> [C.SectionArg C.Position] -> C.ParseResult BS.ByteString+parseNameBS pos args = case args of+ [C.SecArgName _pos secName] ->+ pure secName+ [C.SecArgStr _pos secName] ->+ pure secName+ [] -> do+ C.parseFailure pos "name required"+ pure ""+ _ -> do+ -- TODO: pretty print args+ C.parseFailure pos $ "Invalid name " ++ show args+ pure ""
+ src/HaskellCI/Config/ConstraintSet.hs view
@@ -0,0 +1,35 @@+{-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE OverloadedStrings #-}+module HaskellCI.Config.ConstraintSet where++import Data.Generics.Labels ()+import Distribution.Version+import GHC.Generics (Generic)++import qualified Distribution.FieldGrammar as C+import qualified Distribution.Parsec.Newtypes as C++import HaskellCI.Newtypes++data ConstraintSet = ConstraintSet+ { csName :: String+ , csGhcVersions :: VersionRange+ , csConstraints :: [String] -- we parse these simply as strings+ , csRunTests :: Bool+ }+ deriving (Show, Generic)++emptyConstraintSet :: String -> ConstraintSet+emptyConstraintSet n = ConstraintSet n anyVersion [] False++-------------------------------------------------------------------------------+-- Grammar+-------------------------------------------------------------------------------++constraintSetGrammar+ :: (C.FieldGrammar g, Applicative (g ConstraintSet))+ => String -> g ConstraintSet ConstraintSet+constraintSetGrammar name = ConstraintSet name+ <$> C.optionalFieldDef "ghc" #csGhcVersions anyVersion+ <*> C.monoidalFieldAla "constraints" (C.alaList' C.CommaVCat NoCommas) #csConstraints+ <*> C.booleanFieldDef "run-tests" #csRunTests False
+ src/HaskellCI/Config/CopyFields.hs view
@@ -0,0 +1,32 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+module HaskellCI.Config.CopyFields where++import qualified Distribution.Compat.CharParsing as C+import qualified Distribution.Parsec.Class as C+import qualified Distribution.Pretty as C+import qualified Text.PrettyPrint as PP++data CopyFields+ = CopyFieldsNone+ | CopyFieldsSome+ | CopyFieldsAll+ deriving (Eq, Ord, Show, Enum, Bounded)++-------------------------------------------------------------------------------+-- Functions+-------------------------------------------------------------------------------++showCopyFields :: CopyFields -> String+showCopyFields CopyFieldsNone = "none"+showCopyFields CopyFieldsSome = "some"+showCopyFields CopyFieldsAll = "all"++instance C.Pretty CopyFields where+ pretty = PP.text . showCopyFields++instance C.Parsec CopyFields where+ parsec = C.choice+ [ f <$ C.string (showCopyFields f)+ | f <- [ minBound .. maxBound ]+ ]
+ src/HaskellCI/Config/Doctest.hs view
@@ -0,0 +1,37 @@+{-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE OverloadedStrings #-}+module HaskellCI.Config.Doctest where++import Data.Generics.Labels ()+import Distribution.Version+import GHC.Generics (Generic)++import qualified Distribution.FieldGrammar as C+import qualified Distribution.Parsec.Newtypes as C++import HaskellCI.OptionsGrammar++data DoctestConfig = DoctestConfig+ { cfgDoctestEnabled :: !Bool+ , cfgDoctestOptions :: [String]+ , cfgDoctestVersion :: !VersionRange+ }+ deriving (Show, Generic)++defaultDoctestVersion :: VersionRange+defaultDoctestVersion = withinVersion (mkVersion [0,16])++-------------------------------------------------------------------------------+-- Grammar+-------------------------------------------------------------------------------++doctestConfigGrammar+ :: (OptionsGrammar g, Applicative (g DoctestConfig))+ => g DoctestConfig DoctestConfig+doctestConfigGrammar = DoctestConfig+ <$> C.booleanFieldDef "doctest" #cfgDoctestEnabled False+ ^^^ help "Enable Doctest job"+ <*> C.monoidalFieldAla "doctest-options" (C.alaList' C.NoCommaFSep C.Token') #cfgDoctestOptions+ ^^^ metahelp "OPTS" "Additional Doctest options"+ <*> C.optionalFieldDef "doctest-version" #cfgDoctestVersion defaultDoctestVersion+ ^^^ metahelp "RANGE" "Doctest version"
+ src/HaskellCI/Config/Dump.hs view
@@ -0,0 +1,58 @@+module HaskellCI.Config.Dump where++import Prelude ()+import Prelude.Compat++import Data.Coerce (coerce)+import Distribution.Simple.Utils (fromUTF8BS)++import qualified Distribution.Compat.Newtype as C+import qualified Distribution.FieldGrammar as C+import qualified Distribution.Pretty as C++import HaskellCI.OptionsGrammar++-- TODO: with Cabal-2.6 this can be prettier, using Pretty.Field+newtype DumpGrammar s a = DG { runDG :: [String] }+ deriving Functor++instance Applicative (DumpGrammar s) where+ pure _ = DG []+ DG f <*> DG x = DG (f ++ x)++instance C.FieldGrammar DumpGrammar where+ blurFieldGrammar _ = coerce++ uniqueFieldAla _ _ _ = DG []++ booleanFieldDef fn _ def = DG+ [ fromUTF8BS fn ++ ": " ++ C.prettyShow def+ , ""+ ]++ optionalFieldAla fn _ _ = DG+ [ fromUTF8BS fn ++ ":"+ , ""+ ]++ optionalFieldDefAla fn c _ def = DG+ [ fromUTF8BS fn ++ ": " ++ C.prettyShow (C.pack' c def)+ , ""+ ]++ monoidalFieldAla fn _ _ = DG+ [ fromUTF8BS fn ++ ":"+ , ""+ ]++ prefixedFields _ _ = pure []+ knownField _ = pure ()+ deprecatedSince _ _ = id+ availableSince _ _ = id+ hiddenField = id++instance OptionsGrammar DumpGrammar where+ metahelp _ = help++ help h (DG xs) = DG $+ ("-- " ++ h) : xs
+ src/HaskellCI/Config/Folds.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+module HaskellCI.Config.Folds where++import Data.Char (isUpper, toLower)+import Data.Coerce (coerce)+import Data.List (intercalate)++import qualified Data.Map.Strict as M+import qualified Data.Set as S+import qualified Distribution.Compat.CharParsing as C+import qualified Distribution.Compat.Newtype as C+import qualified Distribution.Parsec.Class as C+import qualified Distribution.Pretty as C+import qualified Text.PrettyPrint as PP++data Fold+ = FoldSDist+ | FoldUnpack+ | FoldBuild+ | FoldBuildInstalled+ | FoldBuildEverything+ | FoldTest+ | FoldHaddock+ | FoldStackage+ | FoldCheck+ | FoldDoctest+ | FoldHLint+ | FoldConstraintSets+ deriving (Eq, Ord, Show, Enum, Bounded)++-------------------------------------------------------------------------------+-- Functions+-------------------------------------------------------------------------------++showFold :: Fold -> String+showFold = dashise . drop 4 . show+ where+ dashise = intercalate "-" . map (map toLower) . split++ split [] = []+ split xs0 =+ let (ys, xs1) = span isUpper xs0+ (zs, xs2) = break isUpper xs1+ in (ys ++ zs) : split xs2++possibleFolds :: [Fold]+possibleFolds = [minBound .. maxBound]++-------------------------------------------------------------------------------+-- Folds+-------------------------------------------------------------------------------++newtype Folds = Folds { getFolds :: S.Set Fold }++instance C.Newtype Folds (S.Set Fold) where+ pack = coerce+ unpack = coerce++instance C.Parsec Folds where+ parsec = fmap (Folds . S.unions) $ manySpaces $ do+ t <- C.parsecToken'+ case t of+ "all" -> return $ S.fromList possibleFolds+ "all-but-test" -> return $ S.delete FoldTest $ S.fromList possibleFolds+ n -> case M.lookup n ps of+ Just n' -> return $ S.singleton n'+ Nothing -> fail $ "Illegal fold name: " ++ n+ where+ ps = M.fromList $ map (\x -> (showFold x, x)) possibleFolds+ manySpaces p = C.many (p <* C.spaces)++instance C.Pretty Folds where+ pretty = PP.fsep . map (PP.text . showFold) . S.toList . getFolds
+ src/HaskellCI/Config/HLint.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE OverloadedStrings #-}+module HaskellCI.Config.HLint where++import Control.Applicative ((<|>))+import Data.Generics.Labels ()+import Distribution.Version+import GHC.Generics (Generic)++import qualified Distribution.Compat.CharParsing as C+import qualified Distribution.FieldGrammar as C+import qualified Distribution.Parsec.Class as C+import qualified Distribution.Parsec.Newtypes as C+import qualified Distribution.Pretty as C+import qualified Text.PrettyPrint as PP++import HaskellCI.OptionsGrammar++data HLintConfig = HLintConfig+ { cfgHLintEnabled :: !Bool+ , cfgHLintJob :: !HLintJob+ , cfgHLintYaml :: !(Maybe FilePath)+ , cfgHLintOptions :: [String]+ , cfgHLintVersion :: !VersionRange+ }+ deriving (Show, Generic)++defaultHLintVersion :: VersionRange+defaultHLintVersion = withinVersion (mkVersion [2,1])++-------------------------------------------------------------------------------+-- HLintJob+-------------------------------------------------------------------------------++data HLintJob+ = HLintJobLatest -- ^ run with latest GHC+ | HLintJob Version -- ^ run with specified GHC version+ deriving (Eq, Show)++instance C.Parsec HLintJob where+ parsec = HLintJobLatest <$ C.string "latest"+ <|> HLintJob <$> C.parsec++instance C.Pretty HLintJob where+ pretty HLintJobLatest = PP.text "latest"+ pretty (HLintJob v) = C.pretty v++-------------------------------------------------------------------------------+-- Grammar+-------------------------------------------------------------------------------++hlintConfigGrammar+ :: (OptionsGrammar g, Applicative (g HLintConfig))+ => g HLintConfig HLintConfig+hlintConfigGrammar = HLintConfig+ <$> C.booleanFieldDef "hlint" #cfgHLintEnabled False+ ^^^ help "Enable HLint job"+ <*> C.optionalFieldDef "hlint-job" #cfgHLintJob HLintJobLatest+ ^^^ metahelp "JOB" "Specify HLint job"+ <*> C.optionalFieldAla "hlint-yaml" C.FilePathNT #cfgHLintYaml+ ^^^ metahelp "PATH" "Use specific .hlint.yaml"+ <*> C.monoidalFieldAla "hlint-options" (C.alaList' C.NoCommaFSep C.Token') #cfgHLintOptions+ ^^^ metahelp "OPTS" "Additional HLint options"+ <*> C.optionalFieldDef "hlint-version" #cfgHLintVersion defaultHLintVersion+ ^^^ metahelp "RANGE" "HLint version"
+ src/HaskellCI/Config/Installed.hs view
@@ -0,0 +1,71 @@+module HaskellCI.Config.Installed where++import Control.Applicative ((<|>))+import Data.List (foldl')++import qualified Distribution.Types.PackageName as C+import qualified Distribution.Compat.CharParsing as C+import qualified Distribution.Parsec.Class as C+import qualified Distribution.Pretty as C+import qualified Text.PrettyPrint as PP+import qualified Data.Set as S++-------------------------------------------------------------------------------+-- Single action+-------------------------------------------------------------------------------++data Installed+ = InstalledAll+ | InstalledNone+ | Add C.PackageName+ | Remove C.PackageName+ deriving (Eq, Show)++instance C.Pretty Installed where+ pretty InstalledAll = PP.text "+all"+ pretty InstalledNone = PP.text "-all"+ pretty (Add pn) = PP.char '+' PP.<> C.pretty pn+ pretty (Remove pn) = PP.char '-' PP.<> C.pretty pn++instance C.Parsec Installed where+ parsec = do+ s <- True <$ C.char '+' <|> False <$ C.char '-'+ pn <- C.parsec+ return $ case (s, pn == C.mkPackageName "all") of+ (True, True) -> InstalledAll+ (True, False) -> Add pn+ (False, True) -> InstalledNone+ (False, False) -> Remove pn++-------------------------------------------------------------------------------+-- Normalised+-------------------------------------------------------------------------------++data InstalledNormalised+ = InstalledDiff (S.Set C.PackageName)+ | InstalledOnly (S.Set C.PackageName)+ deriving Show++-- | Normalised+--+-- >>> parseI = maybe (error "foo") id . traverse C.simpleParsec+-- >>> normaliseInstalled $ parseI ["-Cabal"]+-- InstalledDiff (fromList [PackageName "Cabal"])+--+-- >>> normaliseInstalled $ parseI ["-all"]+-- InstalledOnly (fromList [])+--+-- >>> normaliseInstalled $ parseI ["-all", "+transformers"]+-- InstalledOnly (fromList [PackageName "transformers"])+--+normaliseInstalled :: [Installed] -> InstalledNormalised+normaliseInstalled = foldl' f (InstalledDiff S.empty) where+ f :: InstalledNormalised -> Installed -> InstalledNormalised+ f _ InstalledNone = InstalledOnly S.empty+ f _ InstalledAll = InstalledDiff S.empty++ f (InstalledDiff s) (Remove p) = InstalledDiff (S.insert p s)+ f (InstalledDiff s) (Add p) = InstalledDiff (S.delete p s)++ f (InstalledOnly s) (Remove p) = InstalledOnly (S.delete p s)+ f (InstalledOnly s) (Add p) = InstalledOnly (S.insert p s)
+ src/HaskellCI/Config/Jobs.hs view
@@ -0,0 +1,56 @@+module HaskellCI.Config.Jobs where++import Control.Applicative ((<|>))++import qualified Distribution.Pretty as C+import qualified Text.PrettyPrint as PP+import qualified Distribution.Compat.CharParsing as C+import qualified Distribution.Parsec.Class as C++-- | Jobs+--+-- * @N:M@ - @N@ ghcs (cabal -j), @M@ threads (ghc -j)+--+-- >>> let parseJobs = C.simpleParsec :: String -> Maybe Jobs+-- >>> parseJobs "2:2"+-- Just (BothJobs 2 2)+--+-- >>> parseJobs ":2"+-- Just (GhcJobs 2)+--+-- >>> parseJobs "2"+-- Just (CabalJobs 2)+--+-- >>> parseJobs "garbage"+-- Nothing+--+data Jobs+ = CabalJobs Int+ | GhcJobs Int+ | BothJobs Int Int+ deriving (Show)++cabalJobs :: Jobs -> Maybe Int+cabalJobs (CabalJobs n) = Just n+cabalJobs (GhcJobs _) = Nothing+cabalJobs (BothJobs n _) = Just n++ghcJobs :: Jobs -> Maybe Int+ghcJobs (CabalJobs _) = Nothing+ghcJobs (GhcJobs m) = Just m+ghcJobs (BothJobs _ m) = Just m++instance C.Parsec Jobs where+ parsec = ghc <|> rest where+ ghc = C.char ':' *> (GhcJobs <$> C.integral)+ rest = do+ n <- C.integral+ m' <- C.optional (C.char ':' *> C.integral)+ return $ case m' of+ Nothing -> CabalJobs n+ Just m -> BothJobs n m++instance C.Pretty Jobs where+ pretty (BothJobs n m) = PP.int n PP.<> PP.colon PP.<> PP.int m+ pretty (CabalJobs n) = PP.int n+ pretty (GhcJobs m) = PP.colon PP.<> PP.int m
+ src/HaskellCI/Extras.hs view
@@ -0,0 +1,34 @@+-- | Various extras, are this in @base@ ?+module HaskellCI.Extras where++import Control.Applicative ((<|>))+import Data.List (isPrefixOf, tails)++-- $setup+-- >>> import Text.Read (readMaybe)++-- | Return the part after the first argument+--+-- >>> afterInfix "BAR" "FOOBAR XYZZY"+-- Just " XYZZY"+--+afterInfix :: Eq a => [a] -> [a] -> Maybe [a]+afterInfix needle haystack = findMaybe (afterPrefix needle) (tails haystack)++-- |+--+-- >>> afterPrefix "FOO" "FOOBAR"+-- Just "BAR"+--+afterPrefix :: Eq a => [a] -> [a] -> Maybe [a]+afterPrefix needle haystack+ | needle `isPrefixOf` haystack = Just (drop (length needle) haystack)+ | otherwise = Nothing++-- |+--+-- >>> findMaybe readMaybe ["foo", "1", "bar"] :: Maybe Int+-- Just 1+--+findMaybe :: (a -> Maybe b) -> [a] -> Maybe b+findMaybe f = foldr (\a b -> f a <|> b) Nothing
+ src/HaskellCI/GHC.hs view
@@ -0,0 +1,36 @@+-- | This module encodes what we know about GHC, including existing/supported versions.+module HaskellCI.GHC where++import Distribution.Version (Version, mkVersion, versionNumbers)++import qualified Distribution.Pretty as C++knownGhcVersions :: [Version]+knownGhcVersions = fmap mkVersion+ [ [7,0,1], [7,0,2], [7,0,3], [7,0,4]+ , [7,2,1], [7,2,2]+ , [7,4,1], [7,4,2]+ , [7,6,1], [7,6,2], [7,6,3]+ , [7,8,1], [7,8,2], [7,8,3], [7,8,4]+ , [7,10,1], [7,10,2], [7,10,3]+ , [8,0,1], [8,0,2]+ , [8,2,1], [8,2,2]+ , [8,4,1], [8,4,2], [8,4,3], [8,4,4]+ , [8,6,1], [8,6,2], [8,6,3]+ ]++ghcAlpha :: Maybe Version+ghcAlpha = Nothing+-- ghcAlpha = Just (mkVersion [8,8,1])+--+dispGhcVersion :: Maybe Version -> String+dispGhcVersion = maybe "head" C.prettyShow++-- | Alphas, RCs and HEAD.+previewGHC :: Maybe Version -> Bool+previewGHC = maybe True $ \v -> Just v == ghcAlpha || odd (snd (ghcMajVer v))++ghcMajVer :: Version -> (Int,Int)+ghcMajVer v+ | x:y:_ <- versionNumbers v = (x,y)+ | otherwise = error $ "panic: ghcMajVer called with " ++ show v
+ src/HaskellCI/Glob.hs view
@@ -0,0 +1,153 @@+module HaskellCI.Glob where++import Control.Applicative as App ((<$>))+import Control.Monad (void, filterM, liftM2)+import Control.Monad.IO.Class+import Data.List (stripPrefix)+import Distribution.Compat.ReadP+import System.Directory (doesDirectoryExist, getDirectoryContents)+import System.FilePath.Posix ((</>))++-------------------------------------------------------------------------------+-- Glob+-------------------------------------------------------------------------------++{-++Globbing code and grammar judiciously stolen from cabal-install:++FilePathGlob ::= FilePathRoot FilePathGlobRel+FilePathRoot ::= {- empty -} # relative to cabal.project+ | "/" # Unix root+ | [a-zA-Z] ":" [/\\] # Windows root+ | "~" # home directory++FilePathGlobRel ::= Glob "/" FilePathGlobRel # Unix directory+ | Glob "\\" FilePathGlobRel # Windows directory+ | Glob # file+ | {- empty -} # trailing slash++Glob ::= GlobPiece *+GlobPiece ::= "*" # wildcard+ | [^*{},/\\] * # literal string+ | "\\" [*{},] # escaped reserved character+ | "{" Glob "," ... "," Glob "}" # union (match any of these)+-}++data FilePathGlob = FilePathGlob FilePathRoot FilePathGlobRel+ deriving (Eq, Show)++data FilePathGlobRel+ = GlobDir Glob FilePathGlobRel+ | GlobFile Glob+ | GlobDirTrailing -- trailing dir, a glob ending in '/'+ deriving (Eq, Show)++-- | A single directory or file component of a globbed path+type Glob = [GlobPiece]++-- | A piece of a globbing pattern+data GlobPiece = WildCard+ | Literal String+ | Union [Glob]+ deriving (Eq, Show)++data FilePathRoot+ = FilePathRelative+ | FilePathRoot FilePath -- e.g. '/', 'c:\' or result of 'takeDrive'+ | FilePathHomeDir+ deriving (Eq, Show)++parseFilePathGlobRel :: ReadP r FilePathGlobRel+parseFilePathGlobRel =+ parseGlob >>= \globpieces ->+ asDir globpieces+ <++ asTDir globpieces+ <++ asFile globpieces+ where+ asDir glob = do dirSep+ GlobDir glob <$> parseFilePathGlobRel+ asTDir glob = do dirSep+ return (GlobDir glob GlobDirTrailing)+ asFile glob = return (GlobFile glob)++ dirSep = void (char '/')+ +++ (do _ <- char '\\'+ -- check this isn't an escape code+ following <- look+ case following of+ (c:_) | isGlobEscapedChar c -> pfail+ _ -> return ())++parseGlob :: ReadP r Glob+parseGlob = many1 parsePiece+ where+ parsePiece = literal +++ wildcard +++ union'++ wildcard = char '*' >> return WildCard++ union' = between (char '{') (char '}') $+ fmap Union (sepBy1 parseGlob (char ','))++ literal = Literal `fmap` litchars1++ litchar = normal +++ escape++ normal = satisfy (\c -> not (isGlobEscapedChar c)+ && c /= '/' && c /= '\\')+ escape = char '\\' >> satisfy isGlobEscapedChar++ litchars1 :: ReadP r [Char]+ litchars1 = liftM2 (:) litchar litchars++ litchars :: ReadP r [Char]+ litchars = litchars1 <++ return []++isGlobEscapedChar :: Char -> Bool+isGlobEscapedChar '*' = True+isGlobEscapedChar '{' = True+isGlobEscapedChar '}' = True+isGlobEscapedChar ',' = True+isGlobEscapedChar _ = False++expandRelGlob :: MonadIO m => FilePath -> FilePathGlobRel -> m [FilePath]+expandRelGlob root glob0 = liftIO $ go glob0 ""+ where+ go (GlobFile glob) dir = do+ entries <- getDirectoryContents (root </> dir)+ let files = filter (matchGlob glob) entries+ return (map (dir </>) files)++ go (GlobDir glob globPath) dir = do+ entries <- getDirectoryContents (root </> dir)+ subdirs <- filterM (\subdir -> doesDirectoryExist+ (root </> dir </> subdir))+ $ filter (matchGlob glob) entries+ concat App.<$> mapM (\subdir -> go globPath (dir </> subdir)) subdirs++ go GlobDirTrailing dir = return [dir]++matchGlob :: Glob -> FilePath -> Bool+matchGlob = goStart+ where+ -- From the man page, glob(7):+ -- "If a filename starts with a '.', this character must be+ -- matched explicitly."++ go, goStart :: [GlobPiece] -> String -> Bool++ goStart (WildCard:_) ('.':_) = False+ goStart (Union globs:rest) cs = any (\glob -> goStart (glob ++ rest) cs)+ globs+ goStart rest cs = go rest cs++ go [] "" = True+ go (Literal lit:rest) cs+ | Just cs' <- stripPrefix lit cs+ = go rest cs'+ | otherwise = False+ go [WildCard] "" = True+ go (WildCard:rest) (c:cs) = go rest (c:cs) || go (WildCard:rest) cs+ go (Union globs:rest) cs = any (\glob -> go (glob ++ rest) cs) globs+ go [] (_:_) = False+ go (_:_) "" = False
+ src/HaskellCI/MakeTravisOutput.hs view
@@ -0,0 +1,212 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}+module HaskellCI.MakeTravisOutput where++import Prelude ()+import Prelude.Compat++import Control.Monad (mzero)+import Control.Monad.Trans.Class (lift)+import Control.Monad.Trans.Maybe (MaybeT)+import Control.Monad.Trans.Writer (WriterT, tell)+import Data.Functor.Identity (Identity (..))+import Data.Maybe (mapMaybe)+import Data.Set (Set)+import Data.String (IsString (..))+import Distribution.Version++#ifdef MIN_VERSION_ShellCheck+import ShellCheck.Checker (checkScript)+import qualified ShellCheck.Interface as SC+#endif++import qualified Data.Set as S+import qualified Data.Text as T+import qualified Data.Text.Encoding as TE+import qualified Data.YAML as YAML++import HaskellCI.Config.Folds+import HaskellCI.Version++-- | Encode shell command to be YAML safe and (optionally) ShellCheck it.+sh :: String -> Row+sh = sh'+ [ 2034 -- VAR appears unused. Verify it or export it.+ , 2086 -- SC2086: Double quote to prevent globbing and word splitting.+ , 2002 -- SC2002: Useless cat. Consider 'cmd < file | ..' or 'cmd file | ..' instead.+ ]++shForJob :: Set Version -> VersionRange -> String -> Row+shForJob versions vr cmd+ | all (`withinRange` vr) versions = sh cmd+ | not $ any (`withinRange` vr) versions = RowSkip+ | otherwise = sh $ unwords+ [ "if"+ , ghcVersionPredicate vr+ , "; then"+ , cmd+ , "; fi"+ ]++-- | Like 'sh' but with explicit SC exclude codes.+sh' :: [Integer] -> String -> Row+#ifndef MIN_VERSION_ShellCheck+sh' _ cmd = rawRow (shImpl cmd)+#else+sh' excl cmd =+ if null (SC.crComments res)+ then rawRow $ shImpl cmd+ else RowErr $ unlines $+ ("ShellCheck! " ++ cmd) :+ [ "SC" ++ show (SC.cCode c) ++ ": " ++ SC.cMessage c+ | pc <- SC.crComments res+ , let c = SC.pcComment pc+ ]++ where+ res = runIdentity $ checkScript iface spec+ iface = SC.SystemInterface $ \n -> return $ Left $ "cannot read file: " ++ n+ spec = SC.emptyCheckSpec { SC.csFilename = "stdin"+ , SC.csScript = cmd+ , SC.csExcludedWarnings = excl+ , SC.csShellTypeOverride = Just SC.Sh+ }+#endif++-- Non-ShellCheck version of sh'+shImpl :: String -> String+shImpl cmd+ | needsEncoding = " - " ++ show cmd+ | otherwise = " - " ++ cmd+ where+ -- we try to decode optimisticly serialised "- cmd..."+ -- if it's ok, we use it :)+ -- otherwise escape with show+ cmdT = T.pack cmd+ cmdBS = "- " <> TE.encodeUtf8 cmdT+ needsEncoding = case YAML.decodeStrict cmdBS of+ Right [[t]] | t == cmdT -> False+ _ -> True++comment :: String -> Row+comment c = rawRow $ " # " ++ c++blank :: Row+blank = rawRow ""++rawRow :: String -> Row+rawRow = Row++type MakeTravisOutput = Result Diagnostic [String]++data Diagnostic+ = Info String+ | Warn String+ | Error String+ deriving (Eq, Show)++formatDiagnostics :: [Diagnostic] -> String+formatDiagnostics = unlines . map formatDiagnostic++formatDiagnostic :: Diagnostic -> String+formatDiagnostic (Error s) = "*ERROR* " ++ s+formatDiagnostic (Warn s) = "*WARNING* " ++ s+formatDiagnostic (Info s) = "*INFO* " ++ s++-- MaybeT is used to preserve the short-circuiting semantics of 'putStrLnErr'.+type YamlWriter m a = MaybeT (WriterT MakeTravisOutput m) a++putStrLnErr :: Monad m => String -> YamlWriter m a+putStrLnErr m = do+ lift . tell $ Failure [Error m]+ mzero++putStrLnErrs :: Monad m => [String] -> YamlWriter m ()+putStrLnErrs [] = return ()+putStrLnErrs ms = do+ lift (tell (Failure (map Error ms)))+ mzero++putStrLnWarn, putStrLnInfo :: Monad m => String -> YamlWriter m ()+putStrLnWarn m = lift . tell $ Success [Warn m] []+putStrLnInfo m = lift . tell $ Success [Info m] []++tellStrLn :: Monad m => String -> YamlWriter m ()+tellStrLn str = lift . tell $ success [str]++data Row+ = Row String+ | RowErr String+ | RowSkip++instance IsString Row where+ fromString = rawRow++tellStrLns :: Monad m => [Row] -> YamlWriter m ()+tellStrLns rows = case sequenceRows rows of+ Left err -> lift $ tell $ Failure [Error err]+ Right rows' -> lift $ tell $ success rows'++tellStrLnsRaw :: Monad m => [String] -> YamlWriter m ()+tellStrLnsRaw rows = lift $ tell $ success rows++sequenceRows :: [Row] -> Either String [String]+sequenceRows = sequenceA . mapMaybe f where+ f (Row s) = Just (Right s)+ f (RowErr err) = Just (Left err)+ f RowSkip = Nothing++-------------------------------------------------------------------------------+-- Folded+-------------------------------------------------------------------------------++foldedTellStrLns+ :: Monad m+ => Fold+ -> String+ -> Set Fold+ -> YamlWriter m ()+ -> YamlWriter m ()+foldedTellStrLns label = foldedTellStrLns' label ""++foldedTellStrLns'+ :: Monad m+ => Fold+ -> String+ -> String+ -> Set Fold+ -> YamlWriter m ()+ -> YamlWriter m ()+foldedTellStrLns' label pfx prettyLabel labels output+ | label `S.notMember` labels = output+ | otherwise = tellStrLns prologue >> output >> tellStrLns epilogue+ where+ prologue = [ sh' [2039] $ concat+ [ "echo ", prettyLabel+ , " && echo -en 'travis_fold:start:", showFold' label, "\\\\r'"+ ]]+ epilogue = [ sh' [2039] $ "echo -en 'travis_fold:end:" ++ showFold' label ++ "\\\\r'" ]++ showFold' l = showFold l ++ if null pfx then "" else "-" ++ pfx++-------------------------------------------------------------------------------+-- Result+-------------------------------------------------------------------------------++data Result e a+ = Success [e] a+ | Failure [e]+ deriving (Eq, Show, Functor)++success :: a -> Result e a+success = Success []++instance Monoid a => Monoid (Result e a) where+ mempty = success mempty+ mappend = (<>)++instance Monoid a => Semigroup (Result e a) where+ Failure err1 <> Failure err2 = Failure $ err1 <> err2+ Failure err1 <> Success err2 _ = Failure $ err1 <> err2+ Success err1 _ <> Failure err2 = Failure $ err1 <> err2+ Success l1 o1 <> Success l2 o2 = Success (mappend l1 l2) (mappend o1 o2)
+ src/HaskellCI/Newtypes.hs view
@@ -0,0 +1,178 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables #-}+module HaskellCI.Newtypes where++import Control.Applicative (liftA2, (<|>))+import Data.Char (isSpace)+import Data.Coerce (coerce)+import Data.Functor.Identity (Identity (..))+import Data.Proxy (Proxy (..))++import qualified Data.Set as S+import qualified Distribution.Compat.CharParsing as C+import qualified Distribution.Compat.Newtype as C+import qualified Distribution.Parsec.Class as C+import qualified Distribution.Parsec.Newtypes as C+import qualified Distribution.Pretty as C+import qualified Distribution.Types.Version as C+import qualified Distribution.Types.VersionRange as C+import qualified Text.PrettyPrint as PP++import HaskellCI.Version++-------------------------------------------------------------------------------+-- PackageLocation+-------------------------------------------------------------------------------++newtype PackageLocation = PackageLocation String++instance C.Newtype PackageLocation [Char] where+ pack = coerce+ unpack = coerce++-- | This is a bit tricky since it has to cover globs which have embedded @,@+-- chars. But we don't just want to parse strictly as a glob since we want to+-- allow http urls which don't parse as globs, and possibly some+-- system-dependent file paths. So we parse fairly liberally as a token, but+-- we allow @,@ inside matched @{}@ braces.+instance C.Parsec PackageLocation where+ parsec = PackageLocation <$> outerTerm+ where+ outerTerm = ($ "") <$> outerChars++ outerChars, outerChar, innerChars, innerChar :: C.CabalParsing m => m ShowS+ outerChars = foldr (.) id <$> C.some outerChar+ innerChars = foldr (.) id <$> C.many innerChar++ outerChar = do+ c <- C.satisfy $ \c -> not (isSpace c || c == '}' || c == ',')+ kont c++ innerChar = do+ c <- C.satisfy $ \c -> not (isSpace c || c == '}')+ kont c++ kont :: C.CabalParsing m => Char -> m ShowS+ kont c = case c of+ '{' -> do+ cs <- innerChars+ c' <- C.char '}'+ return (showChar c . cs . showChar c')+ _ -> return $ showChar c+++instance C.Pretty PackageLocation where+ pretty (PackageLocation p) = PP.text p++-------------------------------------------------------------------------------+-- NoCommas: something which can be comma separated+-------------------------------------------------------------------------------++newtype NoCommas = NoCommas String++instance C.Newtype NoCommas [Char] where+ pack = coerce+ unpack = coerce++instance C.Parsec NoCommas where+ parsec = NoCommas <$> liftA2 (:) (C.satisfy (not . isSpace)) (C.munch (/= ','))++instance C.Pretty NoCommas where+ pretty (NoCommas p) = PP.text p++-------------------------------------------------------------------------------+-- Head version+-------------------------------------------------------------------------------++newtype HeadVersion = HeadVersion { getHeadVersion :: Maybe C.Version }++instance C.Newtype HeadVersion (Maybe C.Version) where+ pack = coerce+ unpack = coerce++instance C.Parsec HeadVersion where+ parsec = HeadVersion Nothing <$ C.string "head" <|>+ HeadVersion . Just <$> C.parsec++instance C.Pretty HeadVersion where+ pretty (HeadVersion Nothing) = PP.text "head"+ pretty (HeadVersion (Just v)) = C.pretty v++-------------------------------------------------------------------------------+-- Newtype+-------------------------------------------------------------------------------++newtype Int' = Int' Int++instance C.Newtype Int' Int where+ pack = coerce+ unpack = coerce++instance C.Parsec Int' where+ parsec = Int' <$> C.integral++instance C.Pretty Int' where+ pretty (Int' i) = PP.int i++-------------------------------------------------------------------------------+-- Range+-------------------------------------------------------------------------------++newtype Range = Range C.VersionRange++instance C.Newtype Range C.VersionRange where+ pack = coerce+ unpack = coerce++instance C.Parsec Range where+ parsec = fmap Range $ C.parsec <|> fromBool <$> C.parsec where+ fromBool True = C.anyVersion+ fromBool False = C.noVersion++instance C.Pretty Range where+ pretty (Range r)+ | equivVersionRanges r C.anyVersion = C.pretty True+ | equivVersionRanges r C.noVersion = C.pretty False+ | otherwise = C.pretty r++-------------------------------------------------------------------------------+-- AlaSet+-------------------------------------------------------------------------------++newtype AlaSet sep b a = AlaSet { getAlaSet :: S.Set a }++alaSet :: sep -> S.Set a -> AlaSet sep (Identity a) a+alaSet _ = AlaSet++-- | More general version of 'alaSet'.+alaSet' :: sep -> (a -> b) -> S.Set a -> AlaSet sep b a+alaSet' _ _ = AlaSet++instance C.Newtype (AlaSet sep wrapper a) (S.Set a) where+ pack = AlaSet+ unpack = getAlaSet++instance (C.Newtype b a, Ord a, Sep sep, C.Parsec b) => C.Parsec (AlaSet sep b a) where+ parsec = C.pack . S.fromList . map (C.unpack :: b -> a) <$> parseSep (Proxy :: Proxy sep) C.parsec++instance (C.Newtype b a, Sep sep, C.Pretty b) => C.Pretty (AlaSet sep b a) where+ pretty = prettySep (Proxy :: Proxy sep) . map (C.pretty . (C.pack :: a -> b)) . S.toList . C.unpack++-------------------------------------------------------------------------------+-- From Cabal+-------------------------------------------------------------------------------++class Sep sep where+ prettySep :: Proxy sep -> [PP.Doc] -> PP.Doc+ parseSep :: C.CabalParsing m => Proxy sep -> m a -> m [a]++instance Sep C.CommaVCat where+ prettySep _ = PP.vcat . PP.punctuate PP.comma+ parseSep _ = C.parsecLeadingCommaList+instance Sep C.CommaFSep where+ prettySep _ = PP.fsep . PP.punctuate PP.comma+ parseSep _ = C.parsecLeadingCommaList+instance Sep C.NoCommaFSep where+ prettySep _ = PP.fsep+ parseSep _ p = C.many (p <* C.spaces)
+ src/HaskellCI/Optimization.hs view
@@ -0,0 +1,27 @@+module HaskellCI.Optimization where++import Control.Applicative ((<|>))++import qualified Distribution.Compat.CharParsing as C+import qualified Distribution.Parsec.Class as C+import qualified Distribution.Pretty as C+import qualified Text.PrettyPrint as PP++data Optimization+ = OptimizationOn+ | OptimizationOff+ | OptimizationLevel Int+ deriving (Eq, Show)++instance C.Parsec Optimization where+ parsec = boolean <|> numeric where+ boolean = ite OptimizationOn OptimizationOff <$> C.parsec+ numeric = OptimizationLevel <$> C.integral++ ite t _ True = t+ ite _ f False = f+ +instance C.Pretty Optimization where+ pretty OptimizationOn = C.pretty True+ pretty OptimizationOff = C.pretty False+ pretty (OptimizationLevel l) = PP.int l
+ src/HaskellCI/OptionsGrammar.hs view
@@ -0,0 +1,24 @@+module HaskellCI.OptionsGrammar (+ OptionsGrammar (..),+ (C.^^^),+ ) where++import qualified Distribution.Compat.Lens as C+import qualified Distribution.FieldGrammar as C+import qualified Distribution.Parsec.Field as C+import qualified Distribution.Types.VersionRange as C++import HaskellCI.Newtypes++class C.FieldGrammar p => OptionsGrammar p where+ metahelp :: String -> String -> p s a -> p s a+ metahelp _ _ = id++ help :: String -> p s a -> p s a+ help _ = id++ -- we treat range fields specially in options+ rangeField :: C.FieldName -> C.ALens' s C.VersionRange -> C.VersionRange -> p s C.VersionRange+ rangeField fn = C.optionalFieldDefAla fn Range++instance OptionsGrammar C.ParsecFieldGrammar
+ src/HaskellCI/OptparseGrammar.hs view
@@ -0,0 +1,124 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE OverloadedStrings #-}+module HaskellCI.OptparseGrammar (+ OptparseGrammar,+ runOptparseGrammar,+ ) where++import Prelude ()+import Prelude.Compat++import Control.Applicative (many)+import Data.Foldable (asum)+import Distribution.Simple.Utils (fromUTF8BS)++import qualified Distribution.Compat.Lens as C+import qualified Distribution.Compat.Newtype as C+import qualified Distribution.FieldGrammar as C+import qualified Distribution.Parsec.Class as C+import qualified Distribution.Parsec.Field as C+import qualified Distribution.Pretty as C+import qualified Distribution.Version as C+import qualified Options.Applicative as O++import HaskellCI.OptionsGrammar+import HaskellCI.Version++data SomeParser s where+ SP :: (Maybe String -> Maybe String -> O.Parser (s -> s)) -> SomeParser s++newtype OptparseGrammar s a = OG [SomeParser s]+ deriving Functor++runOptparseGrammar :: OptparseGrammar s a -> O.Parser (s -> s)+runOptparseGrammar (OG ps) = fmap (foldr (flip (.)) id) $ many $ asum+ [ p Nothing Nothing+ | SP p <- ps+ ]++instance Applicative (OptparseGrammar s) where+ pure _ = OG []+ OG f <*> OG x = OG (f ++ x)++instance C.FieldGrammar OptparseGrammar where+ blurFieldGrammar l (OG ps) = OG+ [ SP $ \v h -> fmap (l C.#%~) (p v h)+ | SP p <- ps+ ]++ -- we don't support unique fields atm+ uniqueFieldAla _ _ _ = OG []++ -- the non default flag has help entry+ booleanFieldDef fn l def = OG+ [ SP $ \_m h -> setOG l $ O.flag' True $ flagMods fn (th h)+ , SP $ \_m h -> setOG l $ O.flag' False $ flagMods ("no-" <> fn) (fh h)+ ]+ where+ th h = if def then Nothing else h+ fh h = if def then h else Nothing++ optionalFieldAla fn c l = OG+ [ SP $ \m h -> setOptionalOG l $ O.option (C.unpack' c <$> readMParsec) $ optionMods fn m h ]++ optionalFieldDefAla fn c l def = OG+ [ SP $ \m h -> setOG l $ O.option (C.unpack' c <$> readMParsec) $ optionMods fn m (fmap hdef h) ]+ where+ hdef h = h ++ " (Default: " ++ C.prettyShow (C.pack' c def) ++ ")"++ monoidalFieldAla fn c l = OG+ [ SP $ \m h -> monoidOG l $ O.option (C.unpack' c <$> readMParsec) $ optionMods fn m h ]++ prefixedFields _ _ = pure []+ knownField _ = pure ()+ deprecatedSince _ _ = id+ availableSince _ _ = id+ hiddenField = id++instance OptionsGrammar OptparseGrammar where+ help h (OG ps) = OG+ [ SP $ \m _h -> p m (Just h)+ | SP p <- ps+ ]++ metahelp m h (OG ps) = OG+ [ SP $ \_m _h -> p (Just m) (Just h)+ | SP p <- ps+ ]++ -- example: @rangeField tests #cfgTests anyVersion@, generates options:+ --+ -- --tests+ -- --no-tests+ -- --tests-jobs RANGE+ --+ -- where the --no-tests has help, because it's not default.+ --+ rangeField fn l def = OG+ [ SP $ \_m h -> setOG l $ O.flag' C.anyVersion $ flagMods fn (th h)+ , SP $ \_m h -> setOG l $ O.flag' C.noVersion $ flagMods ("no-" <> fn) (fh h)+ , SP $ \_m _h -> setOG l $ O.option readMParsec $ O.long (fromUTF8BS $ fn <> "-jobs") <> O.metavar "RANGE"+ ]+ where+ th h = if equivVersionRanges def C.anyVersion then Nothing else h+ fh h = if equivVersionRanges def C.anyVersion then h else Nothing++optionMods :: (O.HasName mods, O.HasMetavar mods) => C.FieldName -> Maybe String -> Maybe String -> O.Mod mods a+optionMods fn mmetavar mhelp = flagMods fn mhelp+ <> maybe mempty O.metavar mmetavar++flagMods :: O.HasName mods => C.FieldName -> Maybe String -> O.Mod mods a+flagMods fn mhelp = O.long (fromUTF8BS fn)+ <> maybe mempty O.help mhelp++readMParsec :: C.Parsec a => O.ReadM a+readMParsec = O.eitherReader C.eitherParsec++setOG :: C.ALens' s a -> O.Parser a -> O.Parser (s -> s)+setOG l = fmap (l C.#~)++setOptionalOG :: C.ALens' s (Maybe a) -> O.Parser a -> O.Parser (s -> s)+setOptionalOG l = fmap $ \x -> l C.#~ Just x++monoidOG :: Monoid a => C.ALens' s a -> O.Parser a -> O.Parser (s -> s)+monoidOG l = fmap $ \x -> l C.#%~ \y -> mappend y x
+ src/HaskellCI/Package.hs view
@@ -0,0 +1,12 @@+module HaskellCI.Package where++import qualified Distribution.Types.GenericPackageDescription as C+import qualified Distribution.Types.VersionRange as C++data Package = Pkg+ { pkgName :: String+ , pkgJobs :: C.VersionRange+ , pkgDir :: FilePath+ , pkgGpd :: C.GenericPackageDescription+ }+ deriving (Eq, Show)
+ src/HaskellCI/ParsecError.hs view
@@ -0,0 +1,108 @@+module HaskellCI.ParsecError (+ renderParseError,+ ) where++import Prelude ()+import Prelude.Compat++import Distribution.Parsec.Common (PError (..), PWarning (..),+ Position (..), showPos, zeroPos)+import Distribution.Simple.Utils (fromUTF8BS)+import System.FilePath (normalise)++import qualified Data.ByteString as BS+import qualified Data.ByteString.Char8 as BS8++-- | Render parse error highlighting the part of the input file.+renderParseError+ :: FilePath+ -> BS.ByteString+ -> [PError]+ -> [PWarning]+ -> String+renderParseError filepath contents errors warnings+ | null errors && null warnings = ""+ | null errors = unlines $+ ("Warnings encountered when parsing file " <> filepath <> ":")+ : renderedWarnings+ | otherwise = unlines $+ [ "Errors encountered when parsing file " <> filepath <> ":"+ ]+ ++ renderedErrors+ ++ renderedWarnings+ where+ filepath' = normalise filepath++ -- lines of the input file. 'lines' is taken, so they are called rows+ -- contents, line number, whether it's empty line+ rows :: [(String, Int, Bool)]+ rows = zipWith f (BS8.lines contents) [1..] where+ f bs i = let s = fromUTF8BS bs in (s, i, isEmptyOrComment s)++ rowsZipper = listToZipper rows++ isEmptyOrComment :: String -> Bool+ isEmptyOrComment s = case dropWhile (== ' ') s of+ "" -> True -- empty+ ('-':'-':_) -> True -- comment+ _ -> False++ renderedErrors = concatMap renderError errors+ renderedWarnings = concatMap renderWarning warnings++ renderError :: PError -> [String]+ renderError (PError pos@(Position row col) msg)+ -- if position is 0:0, then it doesn't make sense to show input+ -- looks like, Parsec errors have line-feed in them+ | pos == zeroPos = msgs+ | otherwise = msgs ++ formatInput row col+ where+ msgs = [ "", filepath' ++ ":" ++ showPos pos ++ ": error:", trimLF msg, "" ]++ renderWarning :: PWarning -> [String]+ renderWarning (PWarning _ pos@(Position row col) msg)+ | pos == zeroPos = msgs+ | otherwise = msgs ++ formatInput row col+ where+ msgs = [ "", filepath' ++ ":" ++ showPos pos ++ ": warning:", trimLF msg, "" ]++ -- sometimes there are (especially trailing) newlines.+ trimLF :: String -> String+ trimLF = dropWhile (== '\n') . reverse . dropWhile (== '\n') . reverse++ -- format line: prepend the given line number+ formatInput :: Int -> Int -> [String]+ formatInput row col = case advance (row - 1) rowsZipper of+ Zipper xs ys -> before ++ after where+ before = case span (\(_, _, b) -> b) xs of+ (_, []) -> []+ (zs, z : _) -> map formatInputLine $ z : reverse zs++ after = case ys of+ [] -> []+ (z : _zs) ->+ [ formatInputLine z -- error line+ , " | " ++ replicate (col - 1) ' ' ++ "^" -- pointer: ^+ ]+ -- do we need rows after?+ -- ++ map formatInputLine (take 1 zs) -- one row after++ formatInputLine :: (String, Int, Bool) -> String+ formatInputLine (str, row, _) = leftPadShow row ++ " | " ++ str++ -- hopefully we don't need to work with over 99999 lines .cabal files+ -- at that point small glitches in error messages are hopefully fine.+ leftPadShow :: Int -> String+ leftPadShow n = let s = show n in replicate (5 - length s) ' ' ++ s++data Zipper a = Zipper [a] [a]++listToZipper :: [a] -> Zipper a+listToZipper = Zipper []++advance :: Int -> Zipper a -> Zipper a+advance n z@(Zipper xs ys)+ | n <= 0 = z+ | otherwise = case ys of+ [] -> z+ (y:ys') -> advance (n - 1) $ Zipper (y:xs) ys'
+ src/HaskellCI/ParsecUtils.hs view
@@ -0,0 +1,42 @@+module HaskellCI.ParsecUtils where++import Control.Monad (unless)+import System.Directory (doesFileExist)+import System.Exit (exitFailure)+import System.IO (hPutStr, stderr)++import qualified Data.ByteString as BS+import qualified Distribution.Parsec.Common as C+import qualified Distribution.Parsec.LexerMonad as C+import qualified Distribution.Parsec.Parser as C+import qualified Distribution.Parsec.ParseResult as C+import qualified Text.Parsec as P++import HaskellCI.ParsecError++readAndParseFile+ :: ([C.Field C.Position] -> C.ParseResult a) -- ^ File fields to final value parser+ -> FilePath -- ^ File to read+ -> IO a+readAndParseFile parser fpath = do+ exists <- doesFileExist fpath+ unless exists $ do+ putStrLn $ "Error Parsing: file \"" ++ fpath ++ "\" doesn't exist. Cannot continue."+ exitFailure+ bs <- BS.readFile fpath+ run bs $ case C.readFields' bs of+ Right (fs, lexWarnings) -> do+ C.parseWarnings (C.toPWarnings lexWarnings)+ parser fs+ Left perr -> C.parseFatalFailure pos (show perr) where+ ppos = P.errorPos perr+ pos = C.Position (P.sourceLine ppos) (P.sourceColumn ppos)+ where+ run :: BS.ByteString -> C.ParseResult a -> IO a+ run bs r = case C.runParseResult r of+ (ws, Right x) -> do+ hPutStr stderr $ renderParseError fpath bs [] ws+ return x+ (ws, Left (_, es)) -> do+ hPutStr stderr $ renderParseError fpath bs es ws+ exitFailure
+ src/HaskellCI/Project.hs view
@@ -0,0 +1,91 @@+{-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE OverloadedStrings #-}+-- | Handling of @cabal.project@ file+module HaskellCI.Project (+ Project (..),+ emptyProject,+ parseProjectFile,+ ) where++import Data.ByteString (ByteString)+import Data.Generics.Labels ()+import GHC.Generics (Generic)+import Lens.Micro (over)++import qualified Data.Map.Strict as M+import qualified Distribution.CabalSpecVersion as C+import qualified Distribution.FieldGrammar as C+import qualified Distribution.PackageDescription.FieldGrammar as C+import qualified Distribution.Parsec.Common as C+import qualified Distribution.Parsec.Newtypes as C+import qualified Distribution.Parsec.Parser as C+import qualified Distribution.Parsec.ParseResult as C+import qualified Distribution.Types.SourceRepo as C+import qualified Distribution.Fields.Pretty as C++import HaskellCI.Newtypes+import HaskellCI.Optimization+import HaskellCI.ParsecError++-- $setup+-- >>> :seti -XOverloadedStrings++data Project a = Project+ { prjPackages :: [a]+ , prjConstraints :: [String]+ , prjAllowNewer :: [String]+ , prjReorderGoals :: Bool+ , prjMaxBackjumps :: Maybe Int+ , prjOptimization :: Optimization+ , prjSourceRepos :: [C.SourceRepo]+ , prjOrigFields :: [C.PrettyField]+ }+ deriving (Show, Functor, Foldable, Traversable, Generic)++emptyProject :: Project [a]+emptyProject = Project [] [] [] False Nothing OptimizationOn [] []++-- | Parse project file. Extracts only few fields.+--+-- >>> fmap prjPackages $ parseProjectFile "cabal.project" "packages: foo bar/*.cabal"+-- Right ["foo","bar/*.cabal"]+--+parseProjectFile :: FilePath -> ByteString -> Either String (Project String)+parseProjectFile fp bs = do+ fields0 <- either (Left . show) Right $ C.readFields bs+ let (fields1, sections) = C.partitionFields fields0+ let fields2 = M.filterWithKey (\k _ -> k `elem` knownFields) fields1+ case C.runParseResult $ parse fields0 fields2 sections of+ (_, Right x) -> return x+ (ws, Left (_, es)) -> Left $ renderParseError fp bs es ws+ where+ knownFields = C.fieldGrammarKnownFieldList $ grammar []++ parse origFields fields sections = do+ let prettyOrigFields = C.fromParsecFields $ filter notPackages origFields+ prj <- C.parseFieldGrammar C.cabalSpecLatest fields $ grammar prettyOrigFields+ foldr ($) prj <$> traverse parseSec (concat sections)++ parseSec :: C.Section C.Position -> C.ParseResult (Project String -> Project String)+ parseSec (C.MkSection (C.Name _pos name) [] fields) | name == "source-repository-package" = do+ let fields' = fst $ C.partitionFields fields+ repo <- C.parseFieldGrammar C.cabalSpecLatest fields' (C.sourceRepoFieldGrammar $ C.RepoKindUnknown "unused")+ return $ over #prjSourceRepos (repo :)++ parseSec _ = return id++notPackages :: C.Field ann -> Bool+notPackages (C.Field (C.Name _ "packages") _) = False+notPackages _ = True+++grammar :: [C.PrettyField] -> C.ParsecFieldGrammar (Project String) (Project String)+grammar origFields = Project+ <$> C.monoidalFieldAla "packages" (C.alaList' C.FSep PackageLocation) #prjPackages+ <*> C.monoidalFieldAla "constraints" (C.alaList' C.CommaVCat NoCommas) #prjConstraints+ <*> C.monoidalFieldAla "allow-newer" (C.alaList' C.CommaVCat NoCommas) #prjAllowNewer+ <*> C.booleanFieldDef "reorder-goals" #prjReorderGoals False+ <*> C.optionalFieldAla "max-backjumps" Int' #prjMaxBackjumps+ <*> C.optionalFieldDef "optimization" #prjOptimization OptimizationOn+ <*> pure []+ <*> pure origFields
+ src/HaskellCI/TestedWith.hs view
@@ -0,0 +1,91 @@+module HaskellCI.TestedWith (+ TestedWithJobs (..),+ checkVersions,+ ) where++import Prelude ()+import Prelude.Compat++import Control.Applicative ((<|>))+import Data.Generics.Labels ()+import Data.List (intercalate)++import qualified Data.Foldable as F+import qualified Data.Set as S+import qualified Distribution.Compat.CharParsing as C+import qualified Distribution.Parsec.Class as C+import qualified Distribution.Pretty as C+import qualified Distribution.Types.Version as C+import qualified Distribution.Types.VersionRange as C+import qualified Text.PrettyPrint as PP++import HaskellCI.Package+import HaskellCI.Project++data TestedWithJobs+ = TestedWithUniform+ | TestedWithAny+ deriving (Eq, Show)++instance C.Parsec TestedWithJobs where+ parsec = TestedWithUniform <$ C.string "uniform"+ <|> TestedWithAny <$ C.string "any"++instance C.Pretty TestedWithJobs where+ pretty TestedWithUniform = PP.text "uniform"+ pretty TestedWithAny = PP.text "any"++-------------------------------------------------------------------------------+-- Selection+-------------------------------------------------------------------------------++checkVersions+ :: TestedWithJobs+ -> Project (Package, S.Set C.Version)+ -> Either [String] (S.Set C.Version, Project Package)+checkVersions TestedWithUniform = checkVersionsUniform+checkVersions TestedWithAny = checkVersionsAny++checkVersionsUniform+ :: Project (Package, S.Set C.Version)+ -> Either [String] (S.Set C.Version, Project Package)+checkVersionsUniform prj | null (prjPackages prj) = Left ["Error reading cabal file(s)!"]+checkVersionsUniform prj = do+ let (errors, names) = F.foldl' collectConfig mempty prj+ if not (null errors)+ then Left errors+ else Right (allVersions, prj { prjPackages = names })+ where+ allVersions = F.foldMap snd prj++ collectConfig+ :: ([String], [Package])+ -> (Package, S.Set C.Version)+ -> ([String], [Package])+ collectConfig aggregate (pkg, testWith) =+ aggregate <> (errors, [pkg])+ where+ symDiff a b = S.union a b `S.difference` S.intersection a b+ diff = symDiff testWith allVersions+ missingVersions = map C.prettyShow $ S.toList diff+ errors | S.null diff = []+ | otherwise = pure $ mconcat+ [ pkgName pkg+ , " is missing tested-with annotations for: "+ ] ++ intercalate "," missingVersions++checkVersionsAny+ :: Project (Package, S.Set C.Version)+ -> Either [String] (S.Set C.Version, Project Package)+checkVersionsAny prj | null (prjPackages prj) = Left ["Error reading cabal file(s)!"]+checkVersionsAny prj =+ Right (allVersions, uncurry f <$> prj)+ where+ allVersions = F.foldMap snd prj++ f :: Package -> S.Set C.Version -> Package+ f pkg vs = case S.toList vs of+ [] -> pkg { pkgJobs = C.noVersion }+ (v:vs') -> pkg { pkgJobs = foldr g (C.thisVersion v) vs' }++ g = C.unionVersionRanges . C.thisVersion
+ src/HaskellCI/Version.hs view
@@ -0,0 +1,42 @@+module HaskellCI.Version where++import Data.Function (on)+import Data.List (intercalate)++import qualified Distribution.Version as C++ghcVersionPredicate :: C.VersionRange -> String+ghcVersionPredicate = conj . C.asVersionIntervals+ where+ conj = intercalate " || " . map disj++ disj :: C.VersionInterval -> String+ disj (C.LowerBound v C.InclusiveBound, C.UpperBound u C.InclusiveBound)+ | v == u = "[ $HCNUMVER -eq " ++ f v ++ " ]"+ disj (lb, C.NoUpperBound)+ | isInclZero lb = "true"+ | otherwise = lower lb+ disj (lb, C.UpperBound v b)+ | isInclZero lb = upper v b+ | otherwise = lower lb ++ " && " ++ upper v b++ isInclZero (C.LowerBound v C.InclusiveBound) = v == C.mkVersion [0]+ isInclZero (C.LowerBound _ C.ExclusiveBound) = False++ lower (C.LowerBound v C.InclusiveBound) = "[ $HCNUMVER -ge " ++ f v ++ " ]"+ lower (C.LowerBound v C.ExclusiveBound) = "[ $HCNUMVER -gt " ++ f v ++ " ]"++ upper v C.InclusiveBound = "[ $HCNUMVER -le " ++ f v ++ " ]"+ upper v C.ExclusiveBound = "[ $HCNUMVER -lt " ++ f v ++ " ]"++ f = ghcVersionToString++ghcVersionToString :: C.Version -> String+ghcVersionToString v = case C.versionNumbers v of+ [] -> "0"+ [x] -> show (x * 10000)+ [x,y] -> show (x * 10000 + y * 100)+ (x:y:z:_) -> show (x * 10000 + y * 100 + z)++equivVersionRanges :: C.VersionRange -> C.VersionRange -> Bool+equivVersionRanges = on (==) C.asVersionIntervals
− src/MakeTravisYml.hs
@@ -1,1905 +0,0 @@-{-# LANGUAGE Haskell2010 #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE ViewPatterns #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE MultiWayIf #-}-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE DeriveFoldable #-}-{-# LANGUAGE DeriveTraversable #-}--#if !defined(MIN_VERSION_Cabal)--- As a heuristic, if the macro isn't defined, be pessimistic and--- assume an "old" Cabal version-# define MIN_VERSION_Cabal(x,y,z) 0-#endif---- | New-style @.travis.yml@ script generator using cabal 1.24's nix-style--- tech-preview facilities.------ See also <https://github.com/haskell-CI/haskell-ci>------ NB: This code deliberately avoids relying on non-standard packages and--- is expected to compile/work with at least GHC 7.0 through GHC 8.0-module MakeTravisYml (- main,- -- * for tests- Result (..),- Diagnostic (..),- parseOptsNoCommands,- formatDiagnostic, formatDiagnostics,- travisFromConfigFile, MakeTravisOutput, Options (..), defOptions, options,- ) where--import Control.Applicative as App ((<$>),(<|>), pure)-import Control.DeepSeq (force)-import Control.Exception (evaluate)-import Control.Monad (void, when, unless, filterM, liftM, liftM2, forM_, mzero, foldM, join)-import Data.Char (isSpace, isUpper, toLower)-import qualified Data.Foldable as F-import qualified Data.Traversable as T-import Data.Function-import Data.List-import Data.Maybe-import Data.Monoid as Mon (Monoid (..), Endo (..))-import Data.Either (partitionEithers)-import Data.Set (Set)-import qualified Data.Set as S-import qualified Data.Map as M-import System.Console.GetOpt-import System.Directory (doesDirectoryExist, getDirectoryContents, doesFileExist)-import System.Environment-import System.Exit-import System.FilePath.Posix ((</>), takeDirectory, takeFileName, takeExtension)-import System.IO-import Control.Monad.IO.Class-import Control.Monad.Trans.Class (lift)-import Control.Monad.Trans.Maybe-import Control.Monad.Trans.Writer-import Text.Read (readMaybe)--import Distribution.Compiler (CompilerFlavor(..))-import Distribution.Package hiding (Package, pkgName)-import qualified Distribution.Package as Pkg-import Distribution.PackageDescription (GenericPackageDescription,packageDescription, testedWith, package, condLibrary, condTestSuites)-import Distribution.PackageDescription.Configuration (flattenPackageDescription)-import qualified Distribution.PackageDescription as PD-import qualified Distribution.ParseUtils as PU-import Distribution.Text-import Distribution.Version-#if MIN_VERSION_Cabal(2,2,0)-import Distribution.PackageDescription.Parsec (readGenericPackageDescription)-#elif MIN_VERSION_Cabal(2,0,0)-import Distribution.PackageDescription.Parse (readGenericPackageDescription)-#else-import Distribution.PackageDescription.Parse (readPackageDescription)-import Distribution.Verbosity (Verbosity)-#endif-import Distribution.Compat.ReadP- ( ReadP, (<++), (+++), between, char, many1, munch1- , pfail, readP_to_S, readS_to_P, look- , satisfy, sepBy, sepBy1, gather, munch, skipSpaces)--#ifdef MIN_VERSION_ShellCheck-import ShellCheck.Checker (checkScript)-import qualified ShellCheck.Interface as SC-import qualified ShellCheck.Formatter.Format as SC-import qualified ShellCheck.Formatter.TTY as SC.TTY-import System.IO.Unsafe (unsafePerformIO)-#endif--#if MIN_VERSION_base(4,9,0)-import Data.Semigroup (Semigroup (..))-#else-import Data.Monoid ((<>))-#endif--#if !(MIN_VERSION_Cabal(2,0,0))--- compat helpers for pre-2.0--readGenericPackageDescription :: Verbosity -> FilePath -> IO GenericPackageDescription-readGenericPackageDescription = readPackageDescription--mkVersion :: [Int] -> Version-mkVersion vn = Version vn []--versionNumbers :: Version -> [Int]-versionNumbers (Version vn _) = vn--#endif------------------------------------------------------------------------------------ Hardcoded values----------------------------------------------------------------------------------knownGhcVersions :: [Version]-knownGhcVersions = fmap mkVersion- [ [7,0,1], [7,0,2], [7,0,3], [7,0,4]- , [7,2,1], [7,2,2]- , [7,4,1], [7,4,2]- , [7,6,1], [7,6,2], [7,6,3]- , [7,8,1], [7,8,2], [7,8,3], [7,8,4]- , [7,10,1], [7,10,2], [7,10,3]- , [8,0,1], [8,0,2]- , [8,2,1], [8,2,2]- , [8,4,1], [8,4,2], [8,4,3]- , [8,6,1]- ]--ghcAlpha :: Maybe Version--- ghcAlpha = Nothing-ghcAlpha = Just (mkVersion [8,6,1])--cabalVerMap :: [((Int, Int), Maybe Version)]-cabalVerMap = fmap (fmap (fmap mkVersion))- [ ((7, 0), Just [2,2]) -- Use 2.2 for everything.- , ((7, 2), Just [2,2])- , ((7, 4), Just [2,2])- , ((7, 6), Just [2,2])- , ((7, 8), Just [2,2])- , ((7,10), Just [2,2])- , ((8, 0), Just [2,2])- , ((8, 2), Just [2,2])- , ((8, 4), Just [2,2])- ]--defaultHLintVersion :: VersionRange-defaultHLintVersion = withinVersion (mkVersion [2,1])--defaultDoctestVersion :: VersionRange-defaultDoctestVersion = withinVersion (mkVersion [0,16])------------------------------------------------------------------------------------ Script------------------------------------------------------------------------------------ | Encode shell command to be YAML safe and (optionally) ShellCheck it.-sh :: String -> String-sh = sh'- [ 2034 -- VAR appears unused. Verify it or export it.- , 2086 -- SC2086: Double quote to prevent globbing and word splitting.- ]---- | Like 'sh' but with explicit SC exclude codes.-sh' :: [Integer] -> String -> String-#ifndef MIN_VERSION_ShellCheck-sh' _ = shImpl-#else-sh' excl cmd = unsafePerformIO $ do- res <- checkScript iface spec- case res of- (SC.CheckResult _ []) -> return (shImpl cmd)- _ -> SC.onResult scFormatter res iface >> fail "ShellCheck!"- where- iface = SC.SystemInterface $ \n -> return $ Left $ "cannot read file: " ++ n- spec = SC.emptyCheckSpec { SC.csFilename = "stdin"- , SC.csScript = cmd- , SC.csExcludedWarnings = excl- , SC.csShellTypeOverride = Just SC.Sh- }--scFormatter :: SC.Formatter-scFormatter = unsafePerformIO (SC.TTY.format (SC.FormatterOptions SC.ColorAlways))-#endif---- Non-ShellCheck version of sh'-shImpl :: String -> String-shImpl cmd- | ':' `elem` cmd = " - " ++ show cmd- | otherwise = " - " ++ cmd--comment :: String -> String-comment = (" # " ++)--type MakeTravisOutput = Result Diagnostic [String]--data Diagnostic- = Info String- | Warn String- | Error String- deriving (Eq, Show)--formatDiagnostics :: [Diagnostic] -> String-formatDiagnostics = unlines . map formatDiagnostic--formatDiagnostic :: Diagnostic -> String-formatDiagnostic (Error s) = "*ERROR* " ++ s-formatDiagnostic (Warn s) = "*WARNING* " ++ s-formatDiagnostic (Info s) = "*INFO* " ++ s---- MaybeT is used to preserve the short-circuiting semantics of 'putStrLnErr'.-type YamlWriter m a = MaybeT (WriterT MakeTravisOutput m) a--putStrLnErr :: Monad m => String -> YamlWriter m a-putStrLnErr m = do- lift . tell $ Failure [Error m]- mzero--putStrLnErrs :: Monad m => [String] -> YamlWriter m ()-putStrLnErrs [] = return ()-putStrLnErrs ms = do- lift (tell (Failure (map Error ms)))- mzero--putStrLnWarn, putStrLnInfo :: Monad m => String -> YamlWriter m ()-putStrLnWarn m = lift . tell $ Success [Warn m] []-putStrLnInfo m = lift . tell $ Success [Info m] []--tellStrLn :: Monad m => String -> YamlWriter m ()-tellStrLn str = lift . tell $ success [str]--tellStrLns :: Monad m => [String] -> YamlWriter m ()-tellStrLns = lift . tell . success--foldedTellStrLns- :: Monad m- => Fold- -> String- -> Set Fold- -> YamlWriter m ()- -> YamlWriter m ()-foldedTellStrLns label = foldedTellStrLns' label ""--foldedTellStrLns'- :: Monad m- => Fold- -> String- -> String- -> Set Fold- -> YamlWriter m ()- -> YamlWriter m ()-foldedTellStrLns' label pfx prettyLabel labels output- | label `S.notMember` labels = output- | otherwise = tellStrLns [prologue] >> output >> tellStrLns epilogue- where- prologue = mconcat- [ " - echo ", prettyLabel- , " && echo -en 'travis_fold:start:", showFold' label, "\\\\r'" ]- epilogue = [" - echo -en 'travis_fold:end:" ++ showFold' label ++ "\\\\r'" ]-- showFold' l = showFold l ++ if null pfx then "" else "-" ++ pfx---- | Return the part after the first argument------ >>> afterInfix "BAR" "FOOBAR XYZZY"--- Just " XYZZY"----afterInfix :: Eq a => [a] -> [a] -> Maybe [a]-afterInfix needle haystack = findMaybe (afterPrefix needle) (tails haystack)---- |------ >>> afterPrefix "FOO" "FOOBAR"--- Just "BAR"----afterPrefix :: Eq a => [a] -> [a] -> Maybe [a]-afterPrefix needle haystack- | needle `isPrefixOf` haystack = Just (drop (length needle) haystack)- | otherwise = Nothing---- |------ >>> findMaybe readMaybe ["foo", "1", "bar"] :: Maybe Int--- Just 1----findMaybe :: (a -> Maybe b) -> [a] -> Maybe b-findMaybe f = foldr (\a b -> f a App.<|> b) Nothing---- | >>> maybeReadP PU.parseTokenQ' "foo"--- Just "foo"-maybeReadP :: ReadP a a -> String -> Maybe a-maybeReadP p s = listToMaybe $- [ x- | (x, rest) <- readP_to_S p s- , all isSpace rest- ]--main :: IO ()-main = do- argv <- getArgs- (opts,argv',configFile,xpkgs) <- parseOpts argv- genTravisFromConfigFile (argv',opts) configFile xpkgs--parseOpts :: [String] -> IO (Options, [String], FilePath, [String])-parseOpts argv = case argv of- (cmd : argv') | cmd `isPrefixOf` "regenerate" -> do- let fp = fromMaybe ".travis.yml" $ listToMaybe argv'- ls <- fmap lines (readFile fp >>= evaluate . force) -- strict IO- case findArgv ls of- Nothing -> dieCli [Error $ "expected REGENDATA line in " ++ fp ++ "\n"]- Just argv'' -> parseOpts argv''- [cmd] | cmd `isPrefixOf` "list-ghc" -> do- putStrLn $ "Supported GHC versions:"- forM_ groupedVersions $ \(v, vs) -> do- putStr $ prettyMajVersion v ++ ": "- putStrLn $ intercalate ", " (map display vs)- exitSuccess- _ -> parseOptsNoCommands argv- where- groupedVersions :: [(Version, [Version])]- groupedVersions = map ((\vs -> (head vs, vs)) . sortBy (flip compare))- . groupBy ((==) `on` ghcMajVer)- $ sort knownGhcVersions-- prettyMajVersion :: Version -> String- prettyMajVersion v- | Just v == ghcAlpha = "alpha"- | otherwise = case ghcMajVer v of (x,y) -> show x ++ "." ++ show y-- findArgv :: [String] -> Maybe [String]- findArgv ls = do- l <- findMaybe (afterInfix "REGENDATA") ls- readMaybe l---- Returns options, used argv, cabal file, xpkgs-parseOptsNoCommands :: [String] -> IO (Options, [String], FilePath, [String])-parseOptsNoCommands argv = case getOpt Permute options argv of- (opts',configFile:xpkgs,[]) -> do- opts <- foldOptions defOptions opts'- return (opts,argv,configFile,xpkgs)- (_,_,[]) -> dieCli [Error "expected .cabal or cabal.project file as first non-option argument\n"]- (_,_,errs) -> dieCli (map Error errs)- where- foldOptions :: Options -> [Result Diagnostic (Options -> Options)] -> IO Options- foldOptions def opts = case foldOptions' def opts of- Success ws x -> do- hPutStr stderr (formatDiagnostics ws)- return x- Failure errs -> dieCli errs-- foldOptions' :: Options -> [Result e (Options -> Options)] -> Result e Options- foldOptions' opts = fmap (`appEndo` opts) . F.foldMap (fmap Endo)--dieCli :: [Diagnostic] -> IO a-dieCli errs = hPutStrLn stderr (usageMsg errs) >> exitFailure- where- usageMsg errs' = formatDiagnostics errs' ++ usageInfo h options ++ ex-- h = intercalate "\n"- [ "Usage: runghc make_travis_yml_2.hs [OPTIONS] <cabal-file | cabal.project> <extra-apt-packages...>"- , ""- , "Available commands:"- , " regenerate [TRAVIS.YAML] Regenerate the file using the magic command in it. Default .travis.yml"- , " list-ghc List GHC versions supported by this version of make-travis-yml"- , ""- , "Available options:"- ]-- ex = unlines- [ ""- , "Example:"- , " runghc make_travis_yml_2.hs -o .travis.yml someProject.cabal liblzma-dev"- ]--runYamlWriter :: Maybe FilePath -> YamlWriter IO () -> IO ()-runYamlWriter mfp m = do- result <- execWriterT (runMaybeT m)- case result of- Failure (formatDiagnostics -> errors) -> hPutStr stderr errors >> exitFailure- Success (formatDiagnostics -> warnings) (unlines -> contents) -> do- contents' <- evaluate (force contents)- hPutStr stderr warnings- case mfp of- Nothing -> putStr contents'- Just fp -> writeFile fp contents'--ghcMajVer :: Version -> (Int,Int)-ghcMajVer v- | x:y:_ <- versionNumbers v = (x,y)- | otherwise = error $ "panic: ghcMajVer called with " ++ show v---- | Alphas, RCs and HEAD.-previewGHC :: Maybe Version -> Bool-previewGHC = maybe True $ \v -> Just v == ghcAlpha || odd (snd (ghcMajVer v))--dispGhcVersion :: Maybe Version -> String-dispGhcVersion = maybe "head" display--data Package = Pkg- { pkgName :: String- , pkgDir :: FilePath- , pkgGpd :: GenericPackageDescription- } deriving (Eq, Show)--genTravisFromConfigFile :: ([String],Options) -> FilePath -> [String] -> IO ()-genTravisFromConfigFile args@(_, opts) path xpkgs =- runYamlWriter (optOutput opts) $ travisFromConfigFile args path xpkgs--travisFromConfigFile- :: MonadIO m- => ([String],Options)- -> FilePath- -> [String]- -> YamlWriter m ()-travisFromConfigFile args@(_, opts) path xpkgs = do- cabalFiles <- getCabalFiles- config' <- maybe (return emptyConfig) readConfigFile (optConfig opts)- let config = optConfigMorphism opts config'- pkgs <- T.mapM (configFromCabalFile config opts) cabalFiles- (ghcs, prj) <- checkVersions pkgs- genTravisFromConfigs args xpkgs isCabalProject config prj ghcs- where- checkVersions- :: MonadIO m- => Project (Package, Set Version)- -> YamlWriter m (Set Version, Project Package)- checkVersions prj | null (prjPackages prj) = putStrLnErr "Error reading cabal file(s)!"- checkVersions prj = do- let (errors, names) = F.foldl' collectConfig mempty prj- putStrLnErrs errors- return (allVersions, prj { prjPackages = names })- where- allVersions = F.foldMap snd prj-- collectConfig- :: ([String], [Package])- -> (Package, Set Version)- -> ([String], [Package])- collectConfig aggregate (pkg, testWith) =- aggregate <> (errors, [pkg])- where- symDiff a b = S.union a b `S.difference` S.intersection a b- diff = symDiff testWith allVersions- missingVersions = map display $ S.toList diff- errors | S.null diff = []- | otherwise = App.pure $ mconcat- [ pkgName pkg- , " is missing tested-with annotations for: "- ] ++ intercalate "," missingVersions-- isCabalProject :: Maybe FilePath- isCabalProject- | "cabal.project" `isPrefixOf` takeFileName path = Just path- | otherwise = Nothing-- getCabalFiles :: MonadIO m => YamlWriter m (Project FilePath)- getCabalFiles- | isNothing isCabalProject = return (Project [path] Nothing Nothing)- | otherwise = do- contents <- liftIO $ readFile path- pkgs <- either putStrLnErr return $ parseProjectFile path contents- overPrjPackages concat `liftM` T.mapM findProjectPackage pkgs-- rootdir = takeDirectory path-- -- See findProjectPackages in cabal-install codebase- -- this is simple variant.- findProjectPackage :: MonadIO m => String -> YamlWriter m [FilePath]- findProjectPackage pkglocstr = do- mfp <- checkisFileGlobPackage pkglocstr `mplusMaybeT`- checkIsSingleFilePackage pkglocstr- maybe (putStrLnErr $ "bad package location: " ++ pkglocstr) return mfp-- checkIsSingleFilePackage pkglocstr = do- let abspath = rootdir </> pkglocstr- isFile <- liftIO $ doesFileExist abspath- isDir <- liftIO $ doesDirectoryExist abspath- if | isFile && takeExtension pkglocstr == ".cabal" -> return (Just [abspath])- | isDir -> checkisFileGlobPackage (pkglocstr </> "*.cabal")- | otherwise -> return Nothing-- -- if it looks like glob, glob- checkisFileGlobPackage pkglocstr =- case filter (null . snd) $ readP_to_S parseFilePathGlobRel pkglocstr of- [(g, "")] -> do- files <- liftIO $ expandRelGlob rootdir g- let files' = filter ((== ".cabal") . takeExtension) files- -- if nothing is matched, skip.- if null files' then return Nothing else return (Just files')- _ -> return Nothing-- mplusMaybeT :: Monad m => m (Maybe a) -> m (Maybe a) -> m (Maybe a)- mplusMaybeT ma mb = do- mx <- ma- case mx of- Nothing -> mb- Just x -> return (Just x)--configFromCabalFile- :: MonadIO m => Config -> Options -> FilePath -> YamlWriter m (Package, Set Version)-configFromCabalFile cfg opts cabalFile = do- gpd <- liftIO $ readGenericPackageDescription maxBound cabalFile-- let compilers = testedWith $ packageDescription gpd- pkgNameStr = display $ Pkg.pkgName $ package $ packageDescription gpd-- let unknownComps = nub [ c | (c,_) <- compilers, c /= GHC ]- ghcVerConstrs = [ vc | (GHC,vc) <- compilers ]- ghcVerConstrs' = simplifyVersionRange $ foldr unionVersionRanges noVersion ghcVerConstrs- twoDigitGhcVerConstrs = mapMaybe isTwoDigitGhcVersion ghcVerConstrs :: [Version]- specificGhcVers = nub $ mapMaybe isSpecificVersion ghcVerConstrs-- unless (null twoDigitGhcVerConstrs) $ do- putStrLnWarn $ "'tested-with:' uses two digit GHC versions (which don't match any existing GHC version): " ++ intercalate ", " (map display twoDigitGhcVerConstrs)- putStrLnInfo $ "Either use wild-card format, for example 'tested-with: GHC ==7.10.*' or a specific existing version 'tested-with: GHC ==7.10.3'"-- when (null compilers) $ do- putStrLnErr (unlines $- [ "empty or missing top-level 'tested-with:' definition in " ++ cabalFile ++ " file; example definition:"- , ""- , "tested-with: " ++ intercalate ", " [ "GHC==" ++ display v | v <- lastStableGhcVers ]- ])-- unless (null unknownComps) $ do- putStrLnWarn $ "ignoring unsupported compilers mentioned in tested-with: " ++ show unknownComps-- when (null ghcVerConstrs) $ do- putStrLnErr "'tested-with:' doesn't mention any 'GHC' version"-- when (isNoVersion ghcVerConstrs') $ do- putStrLnErr "'tested-with:' describes an empty version range for 'GHC'"-- when (isAnyVersion ghcVerConstrs') $ do- putStrLnErr "'tested-with:' allows /any/ 'GHC' version"-- let unknownGhcVers = sort $ specificGhcVers \\ knownGhcVersions-- unless (null unknownGhcVers) $ do- putStrLnErr ("'tested-with:' specifically refers to unknown 'GHC' versions: "- ++ intercalate ", " (map display unknownGhcVers) ++ "\n"- ++ "Known GHC versions: " ++ intercalate ", " (map display knownGhcVersions))-- let knownGhcVersions'- | cfgLastInSeries cfg = filterLastMajor knownGhcVersions- | otherwise = knownGhcVersions-- let testedGhcVersions = filter (`withinRange` ghcVerConstrs') knownGhcVersions'-- when (null testedGhcVersions) $ do- putStrLnErr "no known GHC version is allowed by the 'tested-with' specification"-- forM_ (optCollections opts) $ \c -> do- let v = collToGhcVer c- unless (v `elem` testedGhcVersions) $- putStrLnErr $ unlines- [ "collection " ++ c ++ " requires GHC " ++ display v- , "add 'tested-width: GHC == " ++ display v ++ "' to your .cabal file"- ]-- let pkg = Pkg pkgNameStr (takeDirectory cabalFile) gpd-- return (pkg, S.fromList testedGhcVersions)- where- lastStableGhcVers- = nubBy ((==) `on` ghcMajVer)- $ sortBy (flip compare)- $ filter (not . previewGHC . Just)- $ knownGhcVersions-- isTwoDigitGhcVersion :: VersionRange -> Maybe Version- isTwoDigitGhcVersion vr = isSpecificVersion vr >>= t- where- t v | [_,_] <- versionNumbers v = Just v- t _ = Nothing-- filterLastMajor = map maximum . groupBy ((==) `on` ghcMajVer)--genTravisFromConfigs- :: Monad m- => ([String], Options)- -> [String]- -> Maybe FilePath- -> Config- -> Project Package- -> Set Version- -> YamlWriter m ()-genTravisFromConfigs (argv,opts) xpkgs isCabalProject config prj@Project { prjPackages = pkgs } versions' = do- let folds = cfgFolds config-- putStrLnInfo $- "Generating Travis-CI config for testing for GHC versions: " ++ ghcVersions-- unless (null $ optOsx opts) $ do- putStrLnInfo $ "Also OSX jobs for: " ++ ghcOsxVersions- unless (S.null omittedOsxVersions) $- putStrLnWarn $ "Not all GHC versions specified with --osx are generated: " ++ ghcOmittedOsxVersions-- ---------------------------------------------------------------------------- -- travis.yml generation starts here-- tellStrLns- [ "# This Travis job script has been generated by a script via"- , "#"- , "# runghc make_travis_yml_2.hs " ++ unwords [ "'" ++ a ++ "'" | a <- argv ]- , "#"- , "# For more information, see https://github.com/haskell-CI/haskell-ci"- , "#"- , "language: c"- , "sudo: false"- , ""- , "git:"- , " submodules: false # whether to recursively clone submodules"- , ""- ]-- let projectName = fromMaybe (pkgName $ head pkgs) (cfgProjectName config)- unless (null $ cfgIrcChannels config) $ tellStrLns $- [ "notifications:"- , " irc:"- , " channels:"- ] ++- [ " - \"" ++ chan ++ "\"" | chan <- cfgIrcChannels config ] ++- [ " skip_join: true"- , " template:"- , " - \"\\x0313" ++ projectName ++ "\\x03/\\x0306%{branch}\\x03 \\x0314%{commit}\\x03 %{build_url} %{message}\""- , ""- ]-- unless (null $ cfgOnlyBranches config) $ tellStrLns $- [ "branches:"- , " only:"- ] ++- [ " - " ++ branch- | branch <- cfgOnlyBranches config- ] ++- [ ""- ]-- when (cfgCache config) $ tellStrLns- [ "cache:"- , " directories:"- , " - $HOME/.cabal/packages"- , " - $HOME/.cabal/store"- ]-- -- on OSX ghc is installed in $HOME so we can cache it- -- independently of linux- when (cfgCache config && not (null (optOsx opts))) $ tellStrLns- [ " - $HOME/.ghc-install"- ]-- when (cfgCache config) $ tellStrLns- [ ""- , "before_cache:"- , " - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log"- , " # remove files that are regenerated by 'cabal update'"- , " - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.*" -- legacy- , " - rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json" -- TUF meta-data- , " - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache"- , " - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar"- , " - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx"- , ""- , " - rm -rfv $HOME/.cabal/packages/head.hackage" -- if we cache, it will break builds.- , ""- ]-- tellStrLn "matrix:"- tellStrLn " include:"-- let colls = [ (collToGhcVer cid,cid) | cid <- reverse $ optCollections opts ]-- let tellJob :: Monad m => Bool -> Maybe Version -> YamlWriter m ()- tellJob osx gv = do- let cvs = dispGhcVersion $ cfgCabalInstallVersion config <|> (lookupCabVer =<< gv)- gvs = dispGhcVersion gv-- xpkgs' = concatMap (',':) xpkgs-- colls' = [ cid | (v,cid) <- colls, Just v == gv ]-- tellStrLns- [ " - compiler: \"ghc-" <> gvs <> "\""- , if | Just e <- gv >>= \v -> M.lookup v (cfgEnv config)- -> " env: " ++ e- | previewGHC gv -> " env: GHCHEAD=true"- | null colls' -> " # env: TEST=--disable-tests BENCH=--disable-benchmarks"- | otherwise -> " env: 'COLLECTIONS=" ++ intercalate "," colls' ++ "'"- , " addons: {apt: {packages: [ghc-ppa-tools,cabal-install-" <> cvs <> ",ghc-" <> gvs <> xpkgs' <> "], sources: [hvr-ghc]}}"- ]-- when osx $ tellStrLns- [ " os: osx"- ]-- -- newer GHC first, -head last (which is great).- -- Alpha release would go first though.- F.forM_ (reverse $ S.toList versions) $ tellJob False- F.forM_ (reverse $ S.toList osxVersions) $ tellJob True . Just-- let allowFailures = headGhcVers `S.union` S.map Just (cfgAllowFailures config)- unless (S.null allowFailures) $ do- tellStrLn ""- tellStrLn " allow_failures:"-- F.forM_ allowFailures $ \gv -> do- let gvs = dispGhcVersion gv- tellStrLn $ concat [ " - compiler: \"ghc-", gvs, "\"" ]-- tellStrLns- [ ""- , "before_install:"- , sh "HC=${CC}"- , sh' [2034,2039] "HCPKG=${HC/ghc/ghc-pkg}" -- SC2039. In POSIX sh, string replacement is undefined.- , sh "unset CC"- -- rootdir is useful for manual script additions- , sh "ROOTDIR=$(pwd)"- , sh "mkdir -p $HOME/.local/bin"- ]-- let haskellOnMacos = "https://haskell.futurice.com/haskell-on-macos.py"-- if null (optOsx opts)- then tellStrLns- [ sh "PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$HOME/local/bin:$PATH"- ]- else tellStrLns- [ sh $ "if [ \"$(uname)\" = \"Darwin\" ]; then brew update; brew upgrade python@3; curl " ++ haskellOnMacos ++ " | python3 - --make-dirs --install-dir=$HOME/.ghc-install --cabal-alias=head install cabal-install-head ${HC}; fi"- , sh $ "if [ \"$(uname)\" = \"Darwin\" ]; then PATH=$HOME/.ghc-install/ghc/bin:$HOME/local/bin:$PATH; else PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$HOME/local/bin:$PATH; fi"- ]-- -- HCNUMVER, numeric HC version, e.g. ghc 7.8.4 is 70804 and 7.10.3 is 71003- tellStrLns- [ sh $ "HCNUMVER=$(( $(${HC} --numeric-version|sed -E 's/([0-9]+)\\.([0-9]+)\\.([0-9]+).*/\\1 * 10000 + \\2 * 100 + \\3/') ))"- , sh "echo $HCNUMVER"- ]-- unless (null colls) $- tellStrLn " - IFS=', ' read -a COLLS <<< \"$COLLECTIONS\""-- tellStrLns- [ ""- , "install:"- , sh "cabal --version"- , sh "echo \"$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]\""- , sh "BENCH=${BENCH---enable-benchmarks}"- , sh "TEST=${TEST---enable-tests}"- , sh "HADDOCK=${HADDOCK-true}"- , sh "UNCONSTRAINED=${UNCONSTRAINED-true}"- , sh "NOINSTALLEDCONSTRAINTS=${NOINSTALLEDCONSTRAINTS-false}"- , sh "GHCHEAD=${GHCHEAD-false}"- ]-- -- Update hackage index. Side-effect: ~/.cabal.config is created.- tellStrLns- [ sh "travis_retry cabal update -v"- , sh "sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config"- , sh "rm -fv cabal.project cabal.project.local"- ]-- -- Cabal jobs- case cfgJobs config of- (Just n, _) -> tellStrLns- [ sh $ "sed -i.bak 's/^-- jobs:.*/jobs: " ++ show n ++ "/' ${HOME}/.cabal/config"- ]- _ -> return ()-- -- GHC jobs- case cfgJobs config of- (_, Just m) -> tellStrLns- [ sh $ "if [ $HCNUMVER -ge 70800 ]; then sed -i.bak 's/-- ghc-options:.*/ghc-options: -j" ++ show m ++ "/' ${HOME}/.cabal/config; fi"- ]- _ -> return ()-- -- Add head.hackage repository to ~/.cabal/config- -- (locally you want to add it to cabal.project)- unless (S.null headGhcVers) $ tellStrLns- [ " # Overlay Hackage Package Index for GHC HEAD: https://github.com/hvr/head.hackage"- , " - |"- , " if $GHCHEAD; then"- , " sed -i 's/-- allow-newer: .*/allow-newer: *:base/' ${HOME}/.cabal/config"- , " for pkg in $($HCPKG list --simple-output); do pkg=$(echo $pkg | sed 's/-[^-]*$//'); sed -i \"s/allow-newer: /allow-newer: *:$pkg, /\" ${HOME}/.cabal/config; done"- , ""- , " echo 'repository head.hackage' >> ${HOME}/.cabal/config"- , " echo ' url: http://head.hackage.haskell.org/' >> ${HOME}/.cabal/config"- , " echo ' secure: True' >> ${HOME}/.cabal/config"- , " echo ' root-keys: 07c59cb65787dedfaef5bd5f987ceb5f7e5ebf88b904bbd4c5cbdeb2ff71b740' >> ${HOME}/.cabal/config"- , " echo ' 2e8555dde16ebd8df076f1a8ef13b8f14c66bad8eafefd7d9e37d0ed711821fb' >> ${HOME}/.cabal/config"- , " echo ' 8f79fd2389ab2967354407ec852cbe73f2e8635793ac446d09461ffb99527f6e' >> ${HOME}/.cabal/config"- , " echo ' key-threshold: 3' >> ${HOME}/.cabal.config"- , ""- , " grep -Ev -- '^\\s*--' ${HOME}/.cabal/config | grep -Ev '^\\s*$'"- , ""- , " cabal new-update head.hackage -v"- , " fi"- ]-- -- Output cabal.config- tellStrLns- [ sh "grep -Ev -- '^\\s*--' ${HOME}/.cabal/config | grep -Ev '^\\s*$'"- ]-- -- Install doctest- let doctestVersionConstraint- | isAnyVersion (cfgDoctestVersion config) = ""- | otherwise = " --constraint='doctest " ++ display (cfgDoctestVersion config) ++ "'"- when (cfgDoctest config) $ tellStrLns- [ sh $ "if [ $HCNUMVER -ge 80000 ]; then cabal new-install -w ${HC} -j2 --symlink-bindir=$HOME/.local/bin doctest" ++ doctestVersionConstraint ++ "; fi"- ]-- -- Install hlint- let hlintVersionConstraint- | isAnyVersion (cfgHLintVersion config) = ""- | otherwise = " --constraint='hlint " ++ display (cfgHLintVersion config) ++ "'"- when (cfgHLint config) $ tellStrLns- [ sh $ "if [ $HCNUMVER -eq 80403 ]; then cabal new-install -w ${HC} -j2 --symlink-bindir=$HOME/.local/bin hlint" ++ hlintVersionConstraint ++ "; fi"- ]-- -- create cabal.project file- generateCabalProject False-- let pkgFilter = intercalate " | " $ map (wrap.pkgName) pkgs- wrap s = "grep -Fv \"" ++ s ++ " ==\""- unless (null colls) $ tellStrLns- [ " - for COLL in \"${COLLS[@]}\"; do"- , " echo \"== collection $COLL ==\";"- , " ghc-travis collection ${COLL} > /dev/null || break;"- , " ghc-travis collection ${COLL} | " ++ pkgFilter ++ " > cabal.project.freeze;"- , " grep ' collection-id' cabal.project.freeze;"- , " rm -rf dist-newstyle/;"- , " cabal new-build -w ${HC} ${TEST} ${BENCH} --project-file=\"" ++ projectFile ++ "\" --dep -j2 all;"- , " done"- , ""- ]-- forM_ pkgs $ \Pkg{pkgDir} -> tellStrLns- [ " - if [ -f \"" ++ pkgDir ++ "/configure.ac\" ]; then"- , " (cd \"" ++ pkgDir ++ "\" && autoreconf -i);"- , " fi"- ]-- let quotedRmPaths =- ".ghc.environment.*"- ++ " " ++- quotedPaths (\Pkg{pkgDir} -> pkgDir ++ "/dist")-- tellStrLns- [ sh $ "rm -f cabal.project.freeze"- ]-- -- Install dependencies- when (cfgInstallDeps config) $ do- tellStrLns- [ sh $ "cabal new-build -w ${HC} ${TEST} ${BENCH} --project-file=\"" ++ projectFile ++"\" --dep -j2 all"- ]- when (cfgNoTestsNoBench config) $ tellStrLns- [ sh $ "cabal new-build -w ${HC} --disable-tests --disable-benchmarks --project-file=\"" ++ projectFile ++ "\" --dep -j2 all"- ]-- tellStrLns- [ sh $ "rm -rf " ++ quotedRmPaths- , sh $ "DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)"- ]-- tellStrLns- [ ""- , "# Here starts the actual work to be performed for the package under test;"- , "# any command which exits with a non-zero exit code causes the build to fail."- , "script:"- , " # test that source-distributions can be generated"- ]-- foldedTellStrLns FoldSDist "Packaging..." folds $ do- forM_ pkgs $ \Pkg{pkgDir} -> tellStrLns- [ sh $ "(cd \"" ++ pkgDir ++ "\" && cabal sdist)"- ]-- let tarFiles = quotedPaths $ \Pkg{pkgDir,pkgName} ->- pkgDir </> "dist" </> pkgName ++ "-*.tar.gz"--- foldedTellStrLns FoldUnpack "Unpacking..." folds $ do- tellStrLns- [ sh $ "mv " ++ tarFiles ++ " ${DISTDIR}/"- , sh $ "cd ${DISTDIR} || false" -- fail explicitly, makes SC happier- , sh $ "find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \\;"- ]- generateCabalProject True-- when (cfgNoTestsNoBench config) $ foldedTellStrLns FoldBuild "Building..." folds $ tellStrLns- [ comment "this builds all libraries and executables (without tests/benchmarks)"- , sh "cabal new-build -w ${HC} --disable-tests --disable-benchmarks all"- ]-- tellStrLns [""]--- foldedTellStrLns FoldBuildEverything- "Building with tests and benchmarks..." folds $ tellStrLns- [ comment "build & run tests, build benchmarks"- , sh "cabal new-build -w ${HC} ${TEST} ${BENCH} all"- ]-- -- cabal new-test fails if there are no test-suites.- when hasTests $- foldedTellStrLns FoldTest "Testing..." folds $ tellStrLns- [ sh $ mconcat- [ "if [ \"x$TEST\" = \"x--enable-tests\" ]; then "- , if cfgNoise config- then "cabal "- else "(set -o pipefail; cabal -vnormal+nowrap+markoutput "- , "new-test -w ${HC} ${TEST} ${BENCH} all"- , if cfgNoise config- then ""- else " 2>&1 | sed '/^-----BEGIN CABAL OUTPUT-----$/,/^-----END CABAL OUTPUT-----$/d' )"- , "; fi"- ]- ]-- tellStrLns [""]-- when (cfgDoctest config) $ do- let doctestOptions = unwords $ cfgDoctestOptions config- tellStrLns [ comment "doctest" ]- foldedTellStrLns FoldDoctest "Doctest..." folds $ do- forM_ pkgs $ \Pkg{pkgName,pkgGpd} -> do- let args = doctestArgs pkgGpd- args' = unwords args- unless (null args) $ tellStrLns- [ sh $ "if [ $HCNUMVER -ge 80000 ]; then (cd " ++ pkgName ++ "-* && doctest " ++ doctestOptions ++ " " ++ args' ++ "); fi"- ]- tellStrLns [ "" ]-- when (cfgHLint config) $ do- let "" <+> ys = ys- xs <+> "" = xs- xs <+> ys = xs ++ " " ++ ys-- prependSpace "" = ""- prependSpace xs = " " ++ xs-- let hlintOptions = prependSpace $ maybe "" ("-h ${ROOTDIR}/" ++) (cfgHLintYaml config) <+> unwords (cfgHLintOptions config)-- tellStrLns [ comment "hlint" ]- foldedTellStrLns FoldHLint "HLint.." folds $ do- forM_ pkgs $ \Pkg{pkgName,pkgGpd} -> do- -- note: same arguments work so far for doctest and hlint- let args = doctestArgs pkgGpd- args' = unwords args- unless (null args) $ tellStrLns- [ sh $ "if [ $HCNUMVER -eq 80403 ]; then (cd " ++ pkgName ++ "-* && hlint" ++ hlintOptions ++ " " ++ args' ++ "); fi"- ]- tellStrLns [ "" ]-- when (cfgCheck config) $- foldedTellStrLns FoldCheck "cabal check..." folds $ do- tellStrLns [ comment "cabal check" ]- forM_ pkgs $ \Pkg{pkgName} -> tellStrLns- [ sh $ "(cd " ++ pkgName ++ "-* && cabal check)"-- ]- tellStrLns [ "" ]-- when hasLibrary $- foldedTellStrLns FoldHaddock "Haddock..." folds $ tellStrLns- [ comment "haddock"- , sh "rm -rf ./dist-newstyle"- , sh "if $HADDOCK; then cabal new-haddock -w ${HC} ${TEST} ${BENCH} all; else echo \"Skipping haddock generation\";fi"- , ""- ]-- unless (null colls) $- foldedTellStrLns FoldStackage "Stackage builds..." folds $ tellStrLns- [ " # try building & testing for package collections"- , " - for COLL in \"${COLLS[@]}\"; do"- , " echo \"== collection $COLL ==\";"- , " ghc-travis collection ${COLL} > /dev/null || break;"- , " ghc-travis collection ${COLL} | " ++ pkgFilter ++ " > cabal.project.freeze;"- , " grep ' collection-id' cabal.project.freeze;"- , " rm -rf dist-newstyle/;"- , " cabal new-build -w ${HC} ${TEST} ${BENCH} all || break;"- , " if [ \"x$TEST\" = \"x--enable-tests\" ]; then cabal new-test -w ${HC} ${TEST} ${BENCH} all || break; fi;"- , " done"- , ""- ]-- -- Have to build last, as we remove cabal.project.local- when (cfgUnconstrainted config) $ foldedTellStrLns FoldBuildInstalled- "Building without installed constraints for packages in global-db..." folds $ tellStrLns- [ comment "Build without installed constraints for packages in global-db"- -- SC2046: Quote this to prevent word splitting.- -- here we split on purpose!- , sh' [2046, 2086] $ unwords- [ "if $UNCONSTRAINED;"- , "then rm -f cabal.project.local; echo cabal new-build -w ${HC} --disable-tests --disable-benchmarks all;"- , "else echo \"Not building without installed constraints\"; fi"- ]- , ""- ]-- -- and now, as we don't have cabal.project.local;- -- we can test with other constraint sets- let constraintSets = cfgConstraintSets config- unless (null constraintSets) $ do- tellStrLns- [ comment "Constraint sets"- , sh "rm -rf cabal.project.local"- , ""- ]- forM_ constraintSets $ \cs -> do- let name = csName cs- let constraintFlags = concatMap (\x -> " --constraint='" ++ x ++ "'") (csConstraints cs)- tellStrLns [ comment "Constraint set " ++ name ]- foldedTellStrLns' FoldConstraintSets name ("Constraint set " ++ name) folds $ tellStrLns- [ sh' [2086] $ "if " ++ ghcVersionPredicate (csGhcVersions cs) ++ "; then cabal new-build -w ${HC} --disable-tests --disable-benchmarks" ++ constraintFlags ++ " all; else echo skipping...; fi"- , ""- ]- tellStrLns [""]-- tellStrLns- [ "# REGENDATA " ++ show argv- , "# EOF"- ]-- return ()- where- hasTests = F.any (\Pkg{pkgGpd} -> not . null $ condTestSuites pkgGpd) pkgs- hasLibrary = F.any (\Pkg{pkgGpd} -> isJust $ condLibrary pkgGpd) pkgs-- -- GHC versions which need head.hackage- headGhcVers = S.filter previewGHC versions-- generateCabalProject dist = do- tellStrLns- [ sh $ "printf 'packages: " ++ cabalPaths ++ "\\n' > cabal.project"- ]- F.forM_ (prjConstraints prj) $ \xs -> do- let s = concat (lines xs)- tellStrLns- [ sh $ "echo 'constraints: " ++ s ++ "' >> cabal.project"- ]- F.forM_ (prjAllowNewer prj) $ \xs -> do- let s = concat (lines xs)- tellStrLns- [ sh $ "echo 'allow-newer: " ++ s ++ "' >> cabal.project"- ]- unless (null (cfgLocalGhcOptions config)) $ forM_ pkgs $ \Pkg{pkgName} -> do- let s = unwords $ map (show . PU.showToken) $ cfgLocalGhcOptions config- tellStrLns- [ sh $ "echo 'package " ++ pkgName ++ "' >> cabal.project"- , sh $ "echo ' ghc-options: " ++ s ++ "' >> cabal.project"- ]-- -- also write cabal.project.local file with- -- @- -- constraints: base installed- -- constraints: array installed- -- ...- --- -- omitting any local package names- tellStrLns- [ sh $ "touch cabal.project.local"- , sh $ unwords- [ "if ! $NOINSTALLEDCONSTRAINTS; then"- , "for pkg in $($HCPKG list --simple-output); do"- , "echo $pkg"- , concatMap (\Pkg{pkgName} -> " | grep -vw -- " ++ pkgName) pkgs- , "| sed 's/^/constraints: /'"- , "| sed 's/-[^-]*$/ installed/'"- , ">> cabal.project.local; done; fi"- ]- ]-- tellStrLns- [ sh $ "cat cabal.project || true"- , sh $ "cat cabal.project.local || true"- ]- where- cabalPaths- | dist = quotedPaths $ \Pkg{pkgName} -> pkgName ++ "-*/*.cabal"- | otherwise = quotedPaths $ \Pkg{pkgDir} -> pkgDir-- projectFile :: FilePath- projectFile = fromMaybe "cabal.project" isCabalProject-- quotedPaths :: (Package -> FilePath) -> String- quotedPaths f = unwords $ map (f . quote) pkgs- where- quote pkg = pkg{ pkgDir = "\"" ++ pkgDir pkg ++ "\"" }-- showVersions :: Set (Maybe Version) -> String- showVersions = unwords . map dispGhcVersion . S.toList-- -- specified ersions- osxVersions' :: Set Version- osxVersions' = S.fromList $ mapMaybe simpleParse $ optOsx opts-- versions :: Set (Maybe Version)- versions- | cfgGhcHead config = S.insert Nothing $ S.map Just versions'- | otherwise = S.map Just versions'-- ghcVersions :: String- ghcVersions = showVersions versions-- osxVersions, omittedOsxVersions :: Set Version- (osxVersions, omittedOsxVersions) = S.partition (`S.member` versions') osxVersions'-- ghcOsxVersions :: String- ghcOsxVersions = showVersions $ S.map Just osxVersions-- ghcOmittedOsxVersions :: String- ghcOmittedOsxVersions = showVersions $ S.map Just omittedOsxVersions-- lookupCabVer :: Version -> Maybe Version- lookupCabVer v = join $ lookup (ghcMajVer v) cabalVerMap---- | Modules arguments to the library------ * We check the library component------ * If there are hs-source-dirs, use them------ * otherwise use exposed + other modules------ * Also add default-extensions------ /Note:/ same argument work for hlint too!----doctestArgs :: GenericPackageDescription -> [String]-doctestArgs gpd = case PD.library $ flattenPackageDescription gpd of- Nothing -> []- Just l -> exts ++ dirsOrMods- where- bi = PD.libBuildInfo l-- dirsOrMods- | null (PD.hsSourceDirs bi) = map display (PD.exposedModules l)- | otherwise = PD.hsSourceDirs bi-- exts = map (("-X" ++) . display) (PD.defaultExtensions bi)--collToGhcVer :: String -> Version-collToGhcVer cid = case simpleParse cid of- Nothing -> error ("invalid collection-id syntax " ++ show cid)- Just (PackageIdentifier n (versionNumbers -> v))- | display n /= "lts" -> error ("unknown collection " ++ show cid)- | isPrefixOf [0] v -> mkVersion [7,8,3]- | isPrefixOf [1] v -> mkVersion [7,8,4]- | isPrefixOf [2] v -> mkVersion [7,8,4]- | isPrefixOf [3] v -> mkVersion [7,10,2]- | isPrefixOf [4] v -> mkVersion [7,10,3]- | isPrefixOf [5] v -> mkVersion [7,10,3]- | isPrefixOf [6] v -> mkVersion [7,10,3]- | isPrefixOf [7] v -> mkVersion [8,0,1]- | otherwise -> error ("unknown collection " ++ show cid)------------------------------------------------------------------------------------ Jobs------------------------------------------------------------------------------------ | parse jobs defintion------ * N:M - N ghcs (cabal -j), M threads (ghc -j)------ >>> let parseJobs = maybeReadP parseJobsQ--- >>> parseJobs "2:2"--- Just (Just 2,Just 2)------ >>> parseJobs ":2"--- Just (Nothing,Just 2)------ >>> parseJobs "2"--- Just (Just 2,Nothing)------ >>> parseJobs "garbage"--- Nothing----parseJobsQ :: ReadP r (Maybe Int, Maybe Int)-parseJobsQ = nm <++ m <++ n <++ return (Nothing, Nothing)- where- nm = do- x <- parseInt- _ <- char ':'- y <- parseInt- return (Just x, Just y)-- m = do- _ <- char ':'- y <- parseInt- return (Nothing, Just y)-- n = do- x <- parseInt- return (Just x, Nothing)------------------------------------------------------------------------------------ Project file----------------------------------------------------------------------------------data Project a = Project- { prjPackages :: [a]- , prjConstraints :: Maybe String- , prjAllowNewer :: Maybe String- }- deriving (Show, Functor, F.Foldable, T.Traversable)--overPrjPackages :: ([a] -> [b]) -> Project a -> Project b-overPrjPackages f prj = prj { prjPackages = f (prjPackages prj) }--emptyProject :: Project [a]-emptyProject = Project [] Nothing Nothing---- | Parse project file. Extracts only @packages@ field.------ >>> fmap prjPackages $ parseProjectFile "cabal.project" "packages: foo bar/*.cabal"--- Right ["foo","bar/*.cabal"]----parseProjectFile :: FilePath -> String -> Either String (Project String)-parseProjectFile path contents =- case PU.parseFields legacyProjectConfigFieldDescrs emptyProject contents of- PU.ParseOk _ x -> Right x- PU.ParseFailed err -> Left $ case PU.locatedErrorMsg err of- (l, msg) -> "ERROR " ++ path ++ ":" ++ show l ++ ": " ++ msg--legacyProjectConfigFieldDescrs :: [PU.FieldDescr (Project String)]-legacyProjectConfigFieldDescrs =- [ PU.listField "packages"- (error "we don't pretty print") -- pretty- parsePackageLocationTokenQ -- parse- prjPackages- (\x prj -> prj { prjPackages = x })- , PU.simpleField "constraints"- (error "we don't pretty print") -- pretty- (fmap Just PU.parseFreeText)- prjConstraints- (\x prj -> prj { prjConstraints = maybeAlt2 commaConcat (prjConstraints prj) x })- , PU.simpleField "allow-newer"- (error "we don't pretty print") -- pretty- (fmap Just PU.parseFreeText)- prjAllowNewer- (\x prj -> prj { prjAllowNewer = maybeAlt2 commaConcat (prjAllowNewer prj) x })- ]- where- maybeAlt2 _ Nothing x = x- maybeAlt2 _ x Nothing = x- maybeAlt2 f (Just x) (Just y) = Just (f x y)-- commaConcat x y- | all isSpace x = y- | all isSpace y = x- | otherwise = x ++ ", " ++ y------------------------------------------------------------------------------------ Options----------------------------------------------------------------------------------data Options = Options- { optCollections :: [String]- , optOutput :: Maybe FilePath- , optOsx :: [String]- , optConfig :: Maybe FilePath- , optConfigMorphism :: Config -> Config- }--defOptions :: Options-defOptions = Options- { optCollections = []- , optOutput = Nothing- , optOsx = []- , optConfig = Nothing- , optConfigMorphism = id- }--options :: [OptDescr (Result Diagnostic (Options -> Options))]-options =- [ Option [] ["no-cache"]- (NoArg $ successCM $ \cfg -> cfg { cfgCache = False })- "disable Travis caching"- , Option [] ["no-cabal-noise"]- (NoArg $ successCM $ \cfg -> cfg { cfgNoise = False })- "remove cabal noise from test output"- , Option [] ["no-cabal-check"]- (NoArg $ successCM $ \cfg -> cfg { cfgCheck = False })- "Disable cabal check"- , Option [] ["no-install-dependencies"]- (NoArg $ successCM $ \cfg -> cfg { cfgInstallDeps = False })- "Disable installing dependencies in a seperate step"- , Option [] ["no-no-tests-no-bench"]- (NoArg $ successCM $ \cfg -> cfg { cfgNoTestsNoBench = False })- "Don't build with --no-tests --no-benchmarks"- , Option [] ["no-unconstrained"]- (NoArg $ successCM $ \cfg -> cfg { cfgUnconstrainted = False })- "Build also without 'installed' constraints"- , Option [] ["ghc-head"]- (NoArg $ successCM $ \cfg -> cfg { cfgGhcHead = True })- "Build also with ghc-head"- , Option ['c'] ["collection"]- (ReqArg (success' $ \arg opts -> opts { optCollections = arg : optCollections opts }) "CID")- "enable package collection(s) (e.g. 'lts-7'), use multiple times for multiple collections"- , Option ['f'] ["fold"]- (flip OptArg "FOLDS" $ \arg -> case arg of- Nothing -> successCM $ \cfg -> cfg { cfgFolds = S.fromList possibleFolds }- Just arg' -> case maybeReadP parseFoldQ arg' of- Nothing -> Failure [Error $ "cannot parse --fold argument: " ++ arg' ++ "\n"]- Just f -> successCM $ \cfg -> cfg { cfgFolds = f (cfgFolds cfg) })- ("build output(s) to fold, use multiple times for multiple folds. No argument defaults to 'all'. Possible values: all, all-but-test, " ++ intercalate ", " (map showFold possibleFolds))- , Option [] ["irc-channel"]- (ReqArg (successCM' $ \arg cfg -> cfg { cfgIrcChannels = arg : cfgIrcChannels cfg }) "HOST#CHANNEL")- "enable IRC notifcations to given channel (e.g. 'irc.freenode.org#haskell-lens'), use multiple times for multiple channels"- , Option ['n'] ["name"]- (ReqArg (successCM' $ \arg cfg -> cfg { cfgProjectName = Just arg }) "NAME")- "project name (used for IRC notifications), defaults to package name or name of first package listed in cabal.project file"- , Option ['b'] ["branch"]- (ReqArg (successCM' $ \arg cfg -> cfg { cfgOnlyBranches = arg : cfgOnlyBranches cfg }) "BRANCH")- "enable builds only for specific brances, use multiple times for multiple branches"- , Option ['o'] ["output"]- (ReqArg (success' $ \arg opts -> opts { optOutput = Just arg }) "OUTPUT")- "output file (stdout if omitted)"- , Option [] ["config"]- (OptArg (success' $ \arg opts -> opts { optConfig = Just $ fromMaybe "cabal.make-travis-yml" arg }) "CONFIG")- "config file, currently used only to specify constraint sets"- , Option [] ["osx"]- (ReqArg (success' $ \arg opts -> opts { optOsx = arg : optOsx opts }) "GHC")- "generate osx build job with ghc version"- , Option ['j'] ["jobs"]- (reqArgReadP parseJobsQ (\jobs cfg -> cfg { cfgJobs = jobs }) "JOBS")- "jobs (N:M - cabal:ghc)"- , Option [] ["local-ghc-options"]- (reqArgReadP parseOptsQ (\xs cfg -> cfg { cfgLocalGhcOptions = xs }) "OPTIONS")- "--ghc-options for local packages"- , Option ['d'] ["doctest"]- (NoArg $ successCM $ \cfg -> cfg { cfgDoctest = True })- "Run doctest using .ghc.environment files."- , Option [] ["doctest-options"]- (reqArgReadP parseOptsQ (\xs cfg -> cfg { cfgDoctestOptions = xs }) "OPTIONS")- "Additional doctest options."- , Option [] ["doctest-version"]- (reqArgReadP parse (\arg cfg -> cfg { cfgDoctestVersion = arg }) "VERSION")- "Doctest version range"- , Option ['l'] ["hlint"]- (NoArg $ successCM $ \cfg -> cfg { cfgHLint = True })- "Run hlint (only on GHC-8.4.3 target)"- , Option [] ["hlint-yaml"]- (ReqArg (successCM' $ \arg cfg -> cfg { cfgHLintYaml = Just arg }) "HLINT.YAML")- "Relative path to .hlint.yaml."- , Option [] ["hlint-options"]- (reqArgReadP parseOptsQ (\xs cfg -> cfg { cfgHLintOptions = xs }) "OPTIONS")- "Additional hlint options."- , Option [] ["hlint-version"]- (reqArgReadP parse (\arg cfg -> cfg { cfgHLintVersion = arg }) "VERSION")- "HLint version range"- , Option [] ["cabal-install-version"]- (reqArgReadP parse (\arg cfg -> cfg { cfgCabalInstallVersion = Just arg }) "VERSION")- "cabal-install version for all jobs, overrides default"- , Option [] ["cabal-install-head"]- (NoArg $ successCM $ \cfg -> cfg { cfgCabalInstallVersion = Nothing })- "Use cabal-install-head for all jobs, overrides default"- , Option [] ["env"]- (reqArgReadP envP (\(k, v) cfg -> cfg { cfgEnv = M.insert k v (cfgEnv cfg) }) "ENVDECL")- "Environment (e.g. `8.0.2:HADDOCK=false`)"- , Option [] ["allow-failure"]- (reqArgReadP parse (\arg cfg -> cfg { cfgAllowFailures = S.insert arg (cfgAllowFailures cfg) }) "GHCVERSION")- "Allow failures of particular GHC version"- , Option [] ["last-in-series"]- (NoArg $ successCM $ \cfg -> cfg { cfgLastInSeries = True })- "[Discouraged] Assume there are only GHCs last in major series: 8.0.* will match only 8.2.2"- ]- where- overCM f opts = opts- { optConfigMorphism = f . optConfigMorphism opts- }-- success' f arg = success (f arg)-- successCM = success . overCM- successCM' f arg = successCM (f arg)-- reqArgReadP :: ReadP a a -> (a -> Config -> Config) -> String -> ArgDescr (Result Diagnostic (Options -> Options))- reqArgReadP p f n = flip ReqArg n $ \arg -> case maybeReadP p arg of- Nothing -> Failure [Error $ "cannot parse: " ++ arg ]- Just x -> successCM' f x-- envP :: ReadP r (Version, String)- envP = do- ghc <- parse- skipSpaces- _ <- char ':'- skipSpaces- v <- munch (const True)- return (ghc, v)------------------------------------------------------------------------------------ Result----------------------------------------------------------------------------------data Result e a- = Success [e] a- | Failure [e]- deriving (Eq, Show, Functor)--success :: a -> Result e a-success = Success []--instance Monoid a => Mon.Monoid (Result e a) where- mempty = success mempty-#if MIN_VERSION_base(4,9,0)- mappend = (<>)--instance Monoid a => Semigroup (Result e a) where- Failure err1 <> Failure err2 = Failure $ err1 <> err2- Failure err1 <> Success err2 _ = Failure $ err1 <> err2- Success err1 _ <> Failure err2 = Failure $ err1 <> err2- Success l1 o1 <> Success l2 o2 = Success (mappend l1 l2) (mappend o1 o2)-#else- Failure err1 `mappend` Failure err2 = Failure $ err1 `mappend` err2- Failure err1 `mappend` Success err2 _ = Failure $ err1 `mappend` err2- Success err1 _ `mappend` Failure err2 = Failure $ err1 `mappend` err2- Success l1 o1 `mappend` Success l2 o2 = Success (mappend l1 l2) (mappend o1 o2)-#endif------------------------------------------------------------------------------------ Fold----------------------------------------------------------------------------------data Fold- = FoldSDist- | FoldUnpack- | FoldBuild- | FoldBuildInstalled- | FoldBuildEverything- | FoldTest- | FoldHaddock- | FoldStackage- | FoldCheck- | FoldDoctest- | FoldHLint- | FoldConstraintSets- deriving (Eq, Ord, Show, Enum, Bounded)--showFold :: Fold -> String-showFold = dashise . drop 4 . show- where- dashise = intercalate "-" . map (map toLower) . split-- split [] = []- split xs0 =- let (ys, xs1) = span isUpper xs0- (zs, xs2) = break isUpper xs1- in (ys ++ zs) : split xs2--possibleFolds :: [Fold]-possibleFolds = [minBound .. maxBound]--parseFoldQ :: ReadP r (Set Fold -> Set Fold)-parseFoldQ = do- t <- PU.parseTokenQ- case t of- "all" -> return $ const $ S.fromList possibleFolds- "all-but-test" -> return $ const $ S.delete FoldTest $ S.fromList possibleFolds- n -> case M.lookup n ps of- Just n' -> return (S.insert n')- Nothing -> fail $ "Illegal fold name: " ++ n- where- ps = M.fromList $ map (\x -> (showFold x, x)) possibleFolds------------------------------------------------------------------------------------ Config file----------------------------------------------------------------------------------data Config = Config- { cfgCabalInstallVersion :: Maybe Version- , cfgHLint :: !Bool- , cfgHLintYaml :: !(Maybe FilePath)- , cfgHLintVersion :: !VersionRange- , cfgHLintOptions :: [String]- , cfgJobs :: (Maybe Int, Maybe Int)- , cfgDoctest :: !Bool- , cfgDoctestOptions :: [String]- , cfgDoctestVersion :: !VersionRange- , cfgLocalGhcOptions :: [String]- , cfgConstraintSets :: [ConstraintSet]- , cfgCache :: !Bool- , cfgCheck :: !Bool- , cfgNoise :: !Bool- , cfgNoTestsNoBench :: !Bool- , cfgUnconstrainted :: !Bool- , cfgInstallDeps :: !Bool- , cfgOnlyBranches :: [String]- , cfgIrcChannels :: [String]- , cfgProjectName :: Maybe String- , cfgFolds :: Set Fold- , cfgGhcHead :: !Bool- , cfgEnv :: M.Map Version String- , cfgAllowFailures :: S.Set Version- , cfgLastInSeries :: !Bool- }- deriving (Show)--emptyConfig :: Config-emptyConfig = Config- { cfgCabalInstallVersion = Nothing- , cfgHLint = False- , cfgHLintYaml = Nothing- , cfgHLintVersion = defaultHLintVersion- , cfgHLintOptions = []- , cfgJobs = (Nothing, Nothing)- , cfgDoctest = False- , cfgDoctestOptions = []- , cfgDoctestVersion = defaultDoctestVersion- , cfgLocalGhcOptions = []- , cfgConstraintSets = []- , cfgCache = True- , cfgCheck = True- , cfgNoise = True- , cfgNoTestsNoBench = True- , cfgUnconstrainted = True- , cfgInstallDeps = True- , cfgOnlyBranches = []- , cfgIrcChannels = []- , cfgProjectName = Nothing- , cfgFolds = S.empty- , cfgGhcHead = False- , cfgEnv = M.empty- , cfgAllowFailures = S.empty- , cfgLastInSeries = False- }--configFieldDescrs :: [PU.FieldDescr Config]-configFieldDescrs =- [ PU.simpleField "jobs"- (error "we don't pretty print")- parseJobsQ- cfgJobs- (\x cfg -> cfg { cfgJobs = x })- , PU.boolField "hlint"- cfgHLint- (\b cfg -> cfg { cfgHLint = b })- , PU.simpleField "hlint-yaml"- (error "we don't pretty print")- (fmap Just PU.parseFilePathQ)- cfgHLintYaml- (\x cfg -> cfg { cfgHLintYaml = x })- , PU.simpleField "hlint-version"- (error "we don't pretty print")- parse- cfgHLintVersion- (\x cfg -> cfg { cfgHLintVersion = x })- -- TODO: hlint-options- , PU.boolField "doctest"- cfgDoctest- (\b cfg -> cfg { cfgDoctest = b })- , PU.simpleField "doctest-options"- (error "we don't pretty print")- parseOptsQ- cfgDoctestOptions- (\x cfg -> cfg { cfgDoctestOptions = cfgDoctestOptions cfg ++ x })- , PU.simpleField "doctest-version"- (error "we don't pretty print")- parse- cfgDoctestVersion- (\x cfg -> cfg { cfgDoctestVersion = x })- , PU.simpleField "cabal-install-version"- (error "we don't pretty print")- (fmap Just parse)- cfgCabalInstallVersion- (\x cfg -> cfg { cfgCabalInstallVersion = x })- , PU.simpleField "local-ghc-options"- (error "we don't pretty print")- parseOptsQ- cfgLocalGhcOptions- (\x cfg -> cfg { cfgLocalGhcOptions = cfgLocalGhcOptions cfg ++ x })- , PU.boolField "cache"- cfgCache- (\b cfg -> cfg { cfgCache = b })- , PU.boolField "cabal-noise"- cfgNoise- (\b cfg -> cfg { cfgNoise = b })- , PU.boolField "cabal-check"- cfgCheck- (\b cfg -> cfg { cfgCheck = b })- , PU.boolField "install-dependencies-step"- cfgInstallDeps- (\b cfg -> cfg { cfgInstallDeps = b })- , PU.boolField "no-tests-no-benchmarks"- cfgNoTestsNoBench- (\b cfg -> cfg { cfgNoTestsNoBench = b })- , PU.boolField "unconstrained-step"- cfgUnconstrainted- (\b cfg -> cfg { cfgUnconstrainted = b })- , PU.listField "irc-channels"- (error "we don't pretty print")- PU.parseTokenQ- cfgIrcChannels- (\x cfg -> cfg { cfgIrcChannels = x })- , PU.simpleField "name"- (error "we don't pretty print")- (fmap Just PU.parseTokenQ)- cfgProjectName- (\x cfg -> cfg { cfgProjectName = x })- , PU.listField "branches"- (error "we don't pretty print")- PU.parseTokenQ- cfgOnlyBranches- (\x cfg -> cfg { cfgOnlyBranches = x })- , PU.simpleField "folds"- (error "we don't pretty print")- (sepBy parseFoldQ (munch1 isSpace))- (\cfg -> [\_ -> cfgFolds cfg])- (\x cfg -> cfg { cfgFolds = foldl' (flip id) (cfgFolds cfg) x })- , PU.boolField "ghc-head"- cfgGhcHead- (\b cfg -> cfg { cfgGhcHead = b })- -- , PU.simpleField "env" -- TODO- ]--parseOptsQ :: ReadP r [String]-parseOptsQ = sepBy PU.parseTokenQ' (munch1 isSpace)--readConfigFile :: MonadIO m => FilePath -> YamlWriter m Config-readConfigFile path = do- contents <- liftIO $ readFile path- parseConfigFile path contents--parseConfigFile :: Monad m => FilePath -> String -> YamlWriter m Config-parseConfigFile path contents = toWriter $ do- fields' <- PU.readFields contents- let (fields, sections) = partitionEithers (map classify fields')- config <- accumFields configFieldDescrs emptyConfig fields- go config sections- where- toWriter r = case r of- PU.ParseOk ws x -> do- forM_ ws $ \w -> putStrLnWarn (PU.showPWarning path w)- return x- PU.ParseFailed err -> case PU.locatedErrorMsg err of- (l, msg) -> putStrLnErr $ path ++ ":" ++ show l ++ ": " ++ msg-- classify x@PU.IfBlock {} = Right x- classify x@PU.Section {} = Right x- classify x@PU.F {} = Left x-- go :: Config -> [PU.Field] -> PU.ParseResult Config- go cfg [] = return cfg- go _cfg (PU.IfBlock {} : _fields) = fail "if conditional found"- go cfg (PU.F {} : fields) = go cfg fields- go cfg (PU.Section line name arg subfields : fields)- | name == "constraint-set" = do- cs <- accumFields constraintSetFieldDescrs (emptyConstraintSet arg) subfields- let cfg' = cfg { cfgConstraintSets = cfgConstraintSets cfg ++ [cs] }- go cfg' fields- | otherwise = do- PU.warning $ "Unknown section " ++ name ++ " on line " ++ show line- go cfg fields------------------------------------------------------------------------------------ ConstraintSet----------------------------------------------------------------------------------data ConstraintSet = ConstraintSet- { csName :: String- , csGhcVersions :: VersionRange- , csConstraints :: [String] -- we parse these simply as strings- }- deriving (Show)--emptyConstraintSet :: String -> ConstraintSet-emptyConstraintSet n = ConstraintSet n anyVersion []--constraintSetFieldDescrs :: [PU.FieldDescr ConstraintSet]-constraintSetFieldDescrs =- [ PU.listField "constraints"- (error "we don't pretty print") -- pretty- (parseHaskellString <++ munch1 (`notElem` [',', '"']))- csConstraints- (\c cs -> cs { csConstraints = csConstraints cs ++ c })- , PU.simpleField "ghc"- (error "we don't pretty print") -- pretty- Distribution.Text.parse- csGhcVersions- (\c cs -> cs { csGhcVersions = c })- ]--ghcVersionPredicate :: VersionRange -> String-ghcVersionPredicate = conj . asVersionIntervals- where- conj = intercalate " || " . map disj-- disj :: VersionInterval -> String- disj (LowerBound v InclusiveBound, UpperBound u InclusiveBound)- | v == u = "[ $HCNUMVER -eq " ++ f v ++ " ]"- disj (lb, NoUpperBound) = lower lb- disj (lb, UpperBound v b) = lower lb ++ " && " ++ upper v b-- lower (LowerBound v InclusiveBound) = "[ $HCNUMVER -ge " ++ f v ++ " ]"- lower (LowerBound v ExclusiveBound) = "[ $HCNUMVER -gt " ++ f v ++ " ]"-- upper v InclusiveBound = "[ $HCNUMVER -le " ++ f v ++ " ]"- upper v ExclusiveBound = "[ $HCNUMVER -lt " ++ f v ++ " ]"-- f v = case versionNumbers v of- [] -> "0"- [x] -> show (x * 10000)- [x,y] -> show (x * 10000 + y * 100)- (x:y:z:_) -> show (x * 10000 + y * 100 + z)------------------------------------------------------------------------------------ From Cabal----------------------------------------------------------------------------------accumFields :: [PU.FieldDescr a] -> a -> [PU.Field] -> PU.ParseResult a-accumFields fields = foldM setField- where- fieldMap = M.fromList- [ (name, f) | f@(PU.FieldDescr name _ _) <- fields ]- setField accum (PU.F line name value) = case M.lookup name fieldMap of- Just (PU.FieldDescr _ _ set) -> set line value accum- Nothing -> do- PU.warning $ "Unrecognized field " ++ name ++ " on line " ++ show line- return accum- setField accum f = do- PU.warning ("Unrecognized stanza on line " ++ show (PU.lineNo f))- return accum------------------------------------------------------------------------------------ From cabal-install------------------------------------------------------------------------------------ | This is a bit tricky since it has to cover globs which have embedded @,@--- chars. But we don't just want to parse strictly as a glob since we want to--- allow http urls which don't parse as globs, and possibly some--- system-dependent file paths. So we parse fairly liberally as a token, but--- we allow @,@ inside matched @{}@ braces.----parsePackageLocationTokenQ :: ReadP r String-parsePackageLocationTokenQ = parseHaskellString <++ parsePackageLocationToken- where- parsePackageLocationToken :: ReadP r String- parsePackageLocationToken = fmap fst (gather outerTerm)- where- outerTerm = alternateEither1 outerToken (braces innerTerm)- innerTerm = alternateEither innerToken (braces innerTerm)- outerToken = void $ munch1 outerChar- innerToken = void $ munch1 innerChar- outerChar c = not (isSpace c || c == '{' || c == '}' || c == ',')- innerChar c = not (isSpace c || c == '{' || c == '}')- braces = between (char '{') (char '}')-- alternateEither, alternateEither1,- alternatePQs, alternate1PQs, alternateQsP, alternate1QsP- :: ReadP r () -> ReadP r () -> ReadP r ()-- alternateEither1 p q = alternate1PQs p q +++ alternate1QsP q p- alternateEither p q = alternateEither1 p q +++ return ()- alternate1PQs p q = p >> alternateQsP q p- alternatePQs p q = alternate1PQs p q +++ return ()- alternate1QsP q p = many1 q >> alternatePQs p q- alternateQsP q p = alternate1QsP q p +++ return ()--parseHaskellString :: ReadP r String-parseHaskellString = readS_to_P reads--parseInt :: ReadP r Int-parseInt = readS_to_P reads------------------------------------------------------------------------------------ Glob----------------------------------------------------------------------------------{---Globbing code and grammar judiciously stolen from cabal-install:--FilePathGlob ::= FilePathRoot FilePathGlobRel-FilePathRoot ::= {- empty -} # relative to cabal.project- | "/" # Unix root- | [a-zA-Z] ":" [/\\] # Windows root- | "~" # home directory--FilePathGlobRel ::= Glob "/" FilePathGlobRel # Unix directory- | Glob "\\" FilePathGlobRel # Windows directory- | Glob # file- | {- empty -} # trailing slash--Glob ::= GlobPiece *-GlobPiece ::= "*" # wildcard- | [^*{},/\\] * # literal string- | "\\" [*{},] # escaped reserved character- | "{" Glob "," ... "," Glob "}" # union (match any of these)--}--data FilePathGlob = FilePathGlob FilePathRoot FilePathGlobRel- deriving (Eq, Show)--data FilePathGlobRel- = GlobDir Glob FilePathGlobRel- | GlobFile Glob- | GlobDirTrailing -- trailing dir, a glob ending in '/'- deriving (Eq, Show)---- | A single directory or file component of a globbed path-type Glob = [GlobPiece]---- | A piece of a globbing pattern-data GlobPiece = WildCard- | Literal String- | Union [Glob]- deriving (Eq, Show)--data FilePathRoot- = FilePathRelative- | FilePathRoot FilePath -- e.g. '/', 'c:\' or result of 'takeDrive'- | FilePathHomeDir- deriving (Eq, Show)--parseFilePathGlobRel :: ReadP r FilePathGlobRel-parseFilePathGlobRel =- parseGlob >>= \globpieces ->- asDir globpieces- <++ asTDir globpieces- <++ asFile globpieces- where- asDir glob = do dirSep- GlobDir glob <$> parseFilePathGlobRel- asTDir glob = do dirSep- return (GlobDir glob GlobDirTrailing)- asFile glob = return (GlobFile glob)-- dirSep = void (char '/')- +++ (do _ <- char '\\'- -- check this isn't an escape code- following <- look- case following of- (c:_) | isGlobEscapedChar c -> pfail- _ -> return ())--parseGlob :: ReadP r Glob-parseGlob = many1 parsePiece- where- parsePiece = literal +++ wildcard +++ union'-- wildcard = char '*' >> return WildCard-- union' = between (char '{') (char '}') $- fmap Union (sepBy1 parseGlob (char ','))-- literal = Literal `fmap` litchars1-- litchar = normal +++ escape-- normal = satisfy (\c -> not (isGlobEscapedChar c)- && c /= '/' && c /= '\\')- escape = char '\\' >> satisfy isGlobEscapedChar-- litchars1 :: ReadP r [Char]- litchars1 = liftM2 (:) litchar litchars-- litchars :: ReadP r [Char]- litchars = litchars1 <++ return []--isGlobEscapedChar :: Char -> Bool-isGlobEscapedChar '*' = True-isGlobEscapedChar '{' = True-isGlobEscapedChar '}' = True-isGlobEscapedChar ',' = True-isGlobEscapedChar _ = False--expandRelGlob :: MonadIO m => FilePath -> FilePathGlobRel -> m [FilePath]-expandRelGlob root glob0 = liftIO $ go glob0 ""- where- go (GlobFile glob) dir = do- entries <- getDirectoryContents (root </> dir)- let files = filter (matchGlob glob) entries- return (map (dir </>) files)-- go (GlobDir glob globPath) dir = do- entries <- getDirectoryContents (root </> dir)- subdirs <- filterM (\subdir -> doesDirectoryExist- (root </> dir </> subdir))- $ filter (matchGlob glob) entries- concat App.<$> mapM (\subdir -> go globPath (dir </> subdir)) subdirs-- go GlobDirTrailing dir = return [dir]--matchGlob :: Glob -> FilePath -> Bool-matchGlob = goStart- where- -- From the man page, glob(7):- -- "If a filename starts with a '.', this character must be- -- matched explicitly."-- go, goStart :: [GlobPiece] -> String -> Bool-- goStart (WildCard:_) ('.':_) = False- goStart (Union globs:rest) cs = any (\glob -> goStart (glob ++ rest) cs)- globs- goStart rest cs = go rest cs-- go [] "" = True- go (Literal lit:rest) cs- | Just cs' <- stripPrefix lit cs- = go rest cs'- | otherwise = False- go [WildCard] "" = True- go (WildCard:rest) (c:cs) = go rest (c:cs) || go (WildCard:rest) cs- go (Union globs:rest) cs = any (\glob -> go (glob ++ rest) cs) globs- go [] (_:_) = False- go (_:_) "" = False
test/Tests.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE ViewPatterns #-} module Main (main) where -import MakeTravisYml hiding (main)+import HaskellCI hiding (main) import Control.Applicative ((<$>), (<*>)) import Control.Exception (ErrorCall(..), throwIO, try)@@ -12,7 +12,6 @@ import Data.List (isPrefixOf, stripPrefix) import Data.Maybe (mapMaybe) import Data.Monoid (mconcat)-import System.Console.GetOpt (getOpt, ArgOrder(Permute)) import System.Directory (doesFileExist, removeFile, setCurrentDirectory) import System.Exit (ExitCode(..), exitFailure) import System.FilePath (addExtension, (</>))@@ -33,6 +32,11 @@ , fixtureGoldenTest "cabal.project.empty-line" , fixtureGoldenTest "cabal.project.fail-versions" , fixtureGoldenTest "cabal.project.messy"+ , testGroup "copy-fields"+ [ fixtureGoldenTest "cabal.project.copy-fields.all"+ , fixtureGoldenTest "cabal.project.copy-fields.some"+ , fixtureGoldenTest "cabal.project.copy-fields.none"+ ] ] linesToArgv :: String -> Maybe [String]@@ -51,8 +55,8 @@ -- @ fixtureGoldenTest :: FilePath -> TestTree fixtureGoldenTest fp = cabalGoldenTest fp outputRef errorRef $ do- (argv, opts, xpkgs) <- makeTravisFlags- let genConfig = travisFromConfigFile (argv, opts) fp xpkgs+ (argv, opts) <- makeTravisFlags+ let genConfig = travisFromConfigFile argv opts fp execWriterT (runMaybeT genConfig) where outputRef = addExtension fp "travis.yml"@@ -63,17 +67,19 @@ | refExists = (linesToArgv . BS8.unpack) `fmap` BS.readFile outputRef | otherwise = return $ Just [fp] - makeTravisFlags :: IO ([String], Options, [String])+ makeTravisFlags :: IO ([String], Options) makeTravisFlags = do result <- doesFileExist outputRef >>= referenceArgv case result of Nothing -> throwIO (ErrorCall "No REGENDATA in result file.") Just argv -> do- (opts, _argv, _fp, xpkgs) <- parseOpts argv- return (argv, opts, xpkgs)+ (opts, _fp) <- parseOpts argv+ return (argv, opts) -parseOpts :: [String] -> IO (Options, [String], FilePath, [String])-parseOpts = parseOptsNoCommands+parseOpts :: [String] -> IO (Options, FilePath)+parseOpts argv = do+ (path, opts) <- parseTravis argv+ return (opts, path) cabalGoldenTest :: TestName