{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE LambdaCase #-} ----------------------------------------------------------------------------- -- | -- Module : Distribution.Client.Setup -- Copyright : (c) David Himmelstrup 2005 -- License : BSD-like -- -- Maintainer : lemmih@gmail.com -- Stability : provisional -- Portability : portable -- -- ----------------------------------------------------------------------------- module Distribution.Client.Setup ( globalCommand, GlobalFlags(..), defaultGlobalFlags , RepoContext(..), withRepoContext , configureCommand, ConfigFlags(..), configureOptions, filterConfigureFlags , configPackageDB', configCompilerAux' , configureExCommand, ConfigExFlags(..), defaultConfigExFlags , buildCommand, BuildFlags(..) , filterTestFlags , replCommand, testCommand, benchmarkCommand, testOptions, benchmarkOptions , configureExOptions, reconfigureCommand , installCommand, InstallFlags(..), installOptions, defaultInstallFlags , filterHaddockArgs, filterHaddockFlags, haddockOptions , defaultSolver, defaultMaxBackjumps , listCommand, ListFlags(..), listNeedsCompiler , updateCommand, UpdateFlags(..), defaultUpdateFlags , infoCommand, InfoFlags(..) , fetchCommand, FetchFlags(..) , freezeCommand, FreezeFlags(..) , genBoundsCommand , outdatedCommand, OutdatedFlags(..), IgnoreMajorVersionBumps(..) , getCommand, unpackCommand, GetFlags(..) , checkCommand , formatCommand , uploadCommand, UploadFlags(..), IsCandidate(..) , reportCommand, ReportFlags(..) , runCommand , initCommand, initOptions, IT.InitFlags(..) , actAsSetupCommand, ActAsSetupFlags(..) , execCommand, ExecFlags(..), defaultExecFlags , userConfigCommand, UserConfigFlags(..) , manpageCommand , haddockCommand , cleanCommand , doctestCommand , copyCommand , registerCommand , parsePackageArgs , liftOptions , yesNoOpt ) where import Prelude () import Distribution.Client.Compat.Prelude hiding (get) import Distribution.Client.Types.Credentials (Username (..), Password (..)) import Distribution.Client.Types.Repo (RemoteRepo(..), LocalRepo (..)) import Distribution.Client.Types.AllowNewer (AllowNewer(..), AllowOlder(..), RelaxDeps(..)) import Distribution.Client.Types.WriteGhcEnvironmentFilesPolicy import Distribution.Client.BuildReports.Types ( ReportLevel(..) ) import Distribution.Client.Dependency.Types ( PreSolver(..) ) import Distribution.Client.IndexUtils.ActiveRepos ( ActiveRepos ) import Distribution.Client.IndexUtils.IndexState ( TotalIndexState, headTotalIndexState ) import qualified Distribution.Client.Init.Types as IT ( InitFlags(..), PackageType(..), defaultInitFlags ) import Distribution.Client.Targets ( UserConstraint, readUserConstraint ) import Distribution.Utils.NubList ( NubList, toNubList, fromNubList) import Distribution.Solver.Types.ConstraintSource import Distribution.Solver.Types.Settings import Distribution.Simple.Compiler ( Compiler, PackageDB, PackageDBStack ) import Distribution.Simple.Program (ProgramDb, defaultProgramDb) import Distribution.Simple.Command hiding (boolOpt, boolOpt') import qualified Distribution.Simple.Command as Command import Distribution.Simple.Configure ( configCompilerAuxEx, interpretPackageDbFlags, computeEffectiveProfiling ) import qualified Distribution.Simple.Setup as Cabal import Distribution.Simple.Flag ( Flag(..), toFlag, flagToMaybe, flagToList, maybeToFlag , flagElim, fromFlagOrDefault ) import Distribution.Simple.Setup ( ConfigFlags(..), BuildFlags(..), ReplFlags , TestFlags, BenchmarkFlags , HaddockFlags(..) , CleanFlags(..), DoctestFlags(..) , CopyFlags(..), RegisterFlags(..) , readPackageDbList, showPackageDbList , BooleanFlag(..), optionVerbosity , boolOpt, boolOpt', trueArg, falseArg , optionNumJobs ) import Distribution.Simple.InstallDirs ( PathTemplate, InstallDirs(..) , toPathTemplate, fromPathTemplate, combinePathTemplate ) import Distribution.Version ( Version, mkVersion ) import Distribution.Package ( PackageName ) import Distribution.Types.GivenComponent ( GivenComponent(..) ) import Distribution.Types.PackageVersionConstraint ( PackageVersionConstraint(..) ) import Distribution.Types.UnqualComponentName ( unqualComponentNameToPackageName ) import Distribution.PackageDescription ( BuildType(..), RepoKind(..), LibraryName(..) ) import Distribution.System ( Platform ) import Distribution.ReadE ( ReadE(..), succeedReadE, parsecToReadE ) import qualified Distribution.Compat.CharParsing as P import Distribution.Verbosity ( lessVerbose, normal, verboseNoFlags, verboseNoTimestamp ) import Distribution.Simple.Utils ( wrapText ) import Distribution.Client.GlobalFlags ( GlobalFlags(..), defaultGlobalFlags , RepoContext(..), withRepoContext ) import Distribution.Client.ManpageFlags (ManpageFlags, defaultManpageFlags, manpageOptions) import Distribution.FieldGrammar.Newtypes (SpecVersion (..)) import Data.List ( deleteFirstsBy ) import System.FilePath ( () ) globalCommand :: [Command action] -> CommandUI GlobalFlags globalCommand commands = CommandUI { commandName = "", commandSynopsis = "Command line interface to the Haskell Cabal infrastructure.", commandUsage = \pname -> "See http://www.haskell.org/cabal/ for more information.\n" ++ "\n" ++ "Usage: " ++ pname ++ " [GLOBAL FLAGS] [COMMAND [FLAGS]]\n", commandDescription = Just $ \pname -> let commands' = commands ++ [commandAddAction helpCommandUI undefined] cmdDescs = getNormalCommandDescriptions commands' -- if new commands are added, we want them to appear even if they -- are not included in the custom listing below. Thus, we calculate -- the `otherCmds` list and append it under the `other` category. -- Alternatively, a new testcase could be added that ensures that -- the set of commands listed here is equal to the set of commands -- that are actually available. otherCmds = deleteFirstsBy (==) (map fst cmdDescs) [ "help" , "update" , "install" , "fetch" , "list" , "info" , "user-config" , "get" , "init" , "configure" , "build" , "clean" , "run" , "repl" , "test" , "bench" , "check" , "sdist" , "upload" , "report" , "freeze" , "gen-bounds" , "outdated" , "haddock" , "hscolour" , "exec" , "new-build" , "new-configure" , "new-repl" , "new-freeze" , "new-run" , "new-test" , "new-bench" , "new-haddock" , "new-exec" , "new-update" , "new-install" , "new-clean" , "new-sdist" , "list-bin" -- v1 commands, stateful style , "v1-build" , "v1-configure" , "v1-repl" , "v1-freeze" , "v1-run" , "v1-test" , "v1-bench" , "v1-haddock" , "v1-exec" , "v1-update" , "v1-install" , "v1-clean" , "v1-sdist" , "v1-doctest" , "v1-copy" , "v1-register" , "v1-reconfigure" -- v2 commands, nix-style , "v2-build" , "v2-configure" , "v2-repl" , "v2-freeze" , "v2-run" , "v2-test" , "v2-bench" , "v2-haddock" , "v2-exec" , "v2-update" , "v2-install" , "v2-clean" , "v2-sdist" ] maxlen = maximum $ [length name | (name, _) <- cmdDescs] align str = str ++ replicate (maxlen - length str) ' ' startGroup n = " ["++n++"]" par = "" addCmd n = case lookup n cmdDescs of Nothing -> "" Just d -> " " ++ align n ++ " " ++ d in "Commands:\n" ++ unlines ( [ startGroup "global" , addCmd "update" , addCmd "install" , par , addCmd "help" , addCmd "info" , addCmd "list" , addCmd "fetch" , addCmd "user-config" , par , startGroup "package" , addCmd "get" , addCmd "init" , par , addCmd "configure" , addCmd "build" , addCmd "clean" , par , addCmd "run" , addCmd "repl" , addCmd "test" , addCmd "bench" , par , addCmd "check" , addCmd "sdist" , addCmd "upload" , addCmd "report" , par , addCmd "freeze" , addCmd "gen-bounds" , addCmd "outdated" , addCmd "haddock" , addCmd "hscolour" , addCmd "exec" , addCmd "list-bin" , par , startGroup "new-style projects (forwards-compatible aliases)" , addCmd "v2-build" , addCmd "v2-configure" , addCmd "v2-repl" , addCmd "v2-run" , addCmd "v2-test" , addCmd "v2-bench" , addCmd "v2-freeze" , addCmd "v2-haddock" , addCmd "v2-exec" , addCmd "v2-update" , addCmd "v2-install" , addCmd "v2-clean" , addCmd "v2-sdist" , par , startGroup "legacy command aliases" , addCmd "v1-build" , addCmd "v1-configure" , addCmd "v1-repl" , addCmd "v1-run" , addCmd "v1-test" , addCmd "v1-bench" , addCmd "v1-freeze" , addCmd "v1-haddock" , addCmd "v1-exec" , addCmd "v1-update" , addCmd "v1-install" , addCmd "v1-clean" , addCmd "v1-sdist" , addCmd "v1-doctest" , addCmd "v1-copy" , addCmd "v1-register" , addCmd "v1-reconfigure" ] ++ if null otherCmds then [] else par :startGroup "other" :[addCmd n | n <- otherCmds]) ++ "\n" ++ "For more information about a command use:\n" ++ " " ++ pname ++ " COMMAND --help\n" ++ "or " ++ pname ++ " help COMMAND\n" ++ "\n" ++ "To install Cabal packages from hackage use:\n" ++ " " ++ pname ++ " install foo [--dry-run]\n" ++ "\n" ++ "Occasionally you need to update the list of available packages:\n" ++ " " ++ pname ++ " update\n", commandNotes = Nothing, commandDefaultFlags = mempty, commandOptions = args } where args :: ShowOrParseArgs -> [OptionField GlobalFlags] args ShowArgs = argsShown args ParseArgs = argsShown ++ argsNotShown -- arguments we want to show in the help argsShown :: [OptionField GlobalFlags] argsShown = [ option ['V'] ["version"] "Print version information" globalVersion (\v flags -> flags { globalVersion = v }) trueArg ,option [] ["numeric-version"] "Print just the version number" globalNumericVersion (\v flags -> flags { globalNumericVersion = v }) trueArg ,option [] ["config-file"] "Set an alternate location for the config file" globalConfigFile (\v flags -> flags { globalConfigFile = v }) (reqArgFlag "FILE") ,option [] ["default-user-config"] "Set a location for a cabal.config file for projects without their own cabal.config freeze file." globalConstraintsFile (\v flags -> flags {globalConstraintsFile = v}) (reqArgFlag "FILE") ,option [] ["ignore-expiry"] "Ignore expiry dates on signed metadata (use only in exceptional circumstances)" globalIgnoreExpiry (\v flags -> flags { globalIgnoreExpiry = v }) trueArg ,option [] ["http-transport"] "Set a transport for http(s) requests. Accepts 'curl', 'wget', 'powershell', and 'plain-http'. (default: 'curl')" globalHttpTransport (\v flags -> flags { globalHttpTransport = v }) (reqArgFlag "HttpTransport") ,option [] ["nix"] "Nix integration: run commands through nix-shell if a 'shell.nix' file exists" globalNix (\v flags -> flags { globalNix = v }) (boolOpt [] []) ] -- arguments we don't want shown in the help argsNotShown :: [OptionField GlobalFlags] argsNotShown = [ option [] ["remote-repo"] "The name and url for a remote repository" globalRemoteRepos (\v flags -> flags { globalRemoteRepos = v }) (reqArg' "NAME:URL" (toNubList . maybeToList . readRemoteRepo) (map showRemoteRepo . fromNubList)) ,option [] ["local-no-index-repo"] "The name and a path for a local no-index repository" globalLocalNoIndexRepos (\v flags -> flags { globalLocalNoIndexRepos = v }) (reqArg' "NAME:PATH" (toNubList . maybeToList . readLocalRepo) (map showLocalRepo . fromNubList)) ,option [] ["remote-repo-cache"] "The location where downloads from all remote repos are cached" globalCacheDir (\v flags -> flags { globalCacheDir = v }) (reqArgFlag "DIR") ,option [] ["logs-dir", "logsdir"] "The location to put log files" globalLogsDir (\v flags -> flags { globalLogsDir = v }) (reqArgFlag "DIR") ,option [] ["world-file"] "The location of the world file" globalWorldFile (\v flags -> flags { globalWorldFile = v }) (reqArgFlag "FILE") ,option [] ["store-dir", "storedir"] "The location of the nix-local-build store" globalStoreDir (\v flags -> flags { globalStoreDir = v }) (reqArgFlag "DIR") , option [] ["active-repositories"] "The active package repositories" globalActiveRepos (\v flags -> flags { globalActiveRepos = v }) (reqArg "REPOS" (parsecToReadE (\err -> "Error parsing active-repositories: " ++ err) (toFlag `fmap` parsec)) (map prettyShow . flagToList)) ] -- ------------------------------------------------------------ -- * Config flags -- ------------------------------------------------------------ configureCommand :: CommandUI ConfigFlags configureCommand = c { commandName = "configure" , commandDefaultFlags = mempty , commandDescription = Just $ \_ -> wrapText $ "Configure how the package is built by setting " ++ "package (and other) flags.\n" ++ "\n" ++ "The configuration affects several other commands, " ++ "including v1-build, v1-test, v1-bench, v1-run, v1-repl.\n" , commandUsage = \pname -> "Usage: " ++ pname ++ " v1-configure [FLAGS]\n" , commandNotes = Just $ \pname -> (Cabal.programFlagsDescription defaultProgramDb ++ "\n") ++ "Examples:\n" ++ " " ++ pname ++ " v1-configure\n" ++ " Configure with defaults;\n" ++ " " ++ pname ++ " v1-configure --enable-tests -fcustomflag\n" ++ " Configure building package including tests,\n" ++ " with some package-specific flag.\n" } where c = Cabal.configureCommand defaultProgramDb configureOptions :: ShowOrParseArgs -> [OptionField ConfigFlags] configureOptions = commandOptions configureCommand -- | Given some 'ConfigFlags' for the version of Cabal that -- cabal-install was built with, and a target older 'Version' of -- Cabal that we want to pass these flags to, convert the -- flags into a form that will be accepted by the older -- Setup script. Generally speaking, this just means filtering -- out flags that the old Cabal library doesn't understand, but -- in some cases it may also mean "emulating" a feature using -- some more legacy flags. filterConfigureFlags :: ConfigFlags -> Version -> ConfigFlags filterConfigureFlags flags cabalLibVersion -- NB: we expect the latest version to be the most common case, -- so test it first. | cabalLibVersion >= mkVersion [2,5,0] = flags_latest -- The naming convention is that flags_version gives flags with -- all flags *introduced* in version eliminated. -- It is NOT the latest version of Cabal library that -- these flags work for; version of introduction is a more -- natural metric. | cabalLibVersion < mkVersion [1,3,10] = flags_1_3_10 | cabalLibVersion < mkVersion [1,10,0] = flags_1_10_0 | cabalLibVersion < mkVersion [1,12,0] = flags_1_12_0 | cabalLibVersion < mkVersion [1,14,0] = flags_1_14_0 | cabalLibVersion < mkVersion [1,18,0] = flags_1_18_0 | cabalLibVersion < mkVersion [1,19,1] = flags_1_19_1 | cabalLibVersion < mkVersion [1,19,2] = flags_1_19_2 | cabalLibVersion < mkVersion [1,21,1] = flags_1_21_1 | cabalLibVersion < mkVersion [1,22,0] = flags_1_22_0 | cabalLibVersion < mkVersion [1,22,1] = flags_1_22_1 | cabalLibVersion < mkVersion [1,23,0] = flags_1_23_0 | cabalLibVersion < mkVersion [1,25,0] = flags_1_25_0 | cabalLibVersion < mkVersion [2,1,0] = flags_2_1_0 | cabalLibVersion < mkVersion [2,5,0] = flags_2_5_0 | otherwise = error "the impossible just happened" -- see first guard where flags_latest = flags { -- Cabal >= 1.19.1 uses '--dependency' and does not need '--constraint'. -- Note: this is not in the wrong place. configConstraints gets -- repopulated in flags_1_19_1 but it needs to be set to empty for -- newer versions first. configConstraints = [] } flags_2_5_0 = flags_latest { -- Cabal < 2.5 does not understand --dependency=pkg:component=cid -- (public sublibraries), so we convert it to the legacy -- --dependency=pkg_or_internal_compoent=cid configDependencies = let convertToLegacyInternalDep (GivenComponent _ (LSubLibName cn) cid) = Just $ GivenComponent (unqualComponentNameToPackageName cn) LMainLibName cid convertToLegacyInternalDep (GivenComponent pn LMainLibName cid) = Just $ GivenComponent pn LMainLibName cid in catMaybes $ convertToLegacyInternalDep <$> configDependencies flags -- Cabal < 2.5 doesn't know about '--allow-depending-on-private-libs'. , configAllowDependingOnPrivateLibs = NoFlag -- Cabal < 2.5 doesn't know about '--enable/disable-executable-static'. , configFullyStaticExe = NoFlag } flags_2_1_0 = flags_2_5_0 { -- Cabal < 2.1 doesn't know about -v +timestamp modifier configVerbosity = fmap verboseNoTimestamp (configVerbosity flags_latest) -- Cabal < 2.1 doesn't know about ---static , configStaticLib = NoFlag , configSplitSections = NoFlag } flags_1_25_0 = flags_2_1_0 { -- Cabal < 1.25.0 doesn't know about --dynlibdir. configInstallDirs = configInstallDirs_1_25_0, -- Cabal < 1.25 doesn't have extended verbosity syntax configVerbosity = fmap verboseNoFlags (configVerbosity flags_2_1_0), -- Cabal < 1.25 doesn't support --deterministic configDeterministic = mempty } configInstallDirs_1_25_0 = let dirs = configInstallDirs flags in dirs { dynlibdir = NoFlag , libexecsubdir = NoFlag , libexecdir = maybeToFlag $ combinePathTemplate <$> flagToMaybe (libexecdir dirs) <*> flagToMaybe (libexecsubdir dirs) } -- Cabal < 1.23 doesn't know about '--profiling-detail'. -- Cabal < 1.23 has a hacked up version of 'enable-profiling' -- which we shouldn't use. (tryLibProfiling, tryExeProfiling) = computeEffectiveProfiling flags flags_1_23_0 = flags_1_25_0 { configProfDetail = NoFlag , configProfLibDetail = NoFlag , configIPID = NoFlag , configProf = NoFlag , configProfExe = Flag tryExeProfiling , configProfLib = Flag tryLibProfiling } -- Cabal == 1.22.0.* had a discontinuity (see #5946 or e9a8d48a3adce34d) -- due to temporary amnesia of the --*-executable-profiling flags flags_1_22_1 = flags_1_23_0 { configDebugInfo = NoFlag , configProfExe = NoFlag } -- Cabal < 1.22 doesn't know about '--disable-debug-info'. flags_1_22_0 = flags_1_23_0 { configDebugInfo = NoFlag } -- Cabal < 1.21.1 doesn't know about 'disable-relocatable' -- Cabal < 1.21.1 doesn't know about 'enable-profiling' -- (but we already dealt with it in flags_1_23_0) flags_1_21_1 = flags_1_22_0 { configRelocatable = NoFlag , configCoverage = NoFlag , configLibCoverage = configCoverage flags } -- Cabal < 1.19.2 doesn't know about '--exact-configuration' and -- '--enable-library-stripping'. flags_1_19_2 = flags_1_21_1 { configExactConfiguration = NoFlag , configStripLibs = NoFlag } -- Cabal < 1.19.1 uses '--constraint' instead of '--dependency'. flags_1_19_1 = flags_1_19_2 { configDependencies = [] , configConstraints = configConstraints flags } -- Cabal < 1.18.0 doesn't know about --extra-prog-path and --sysconfdir. flags_1_18_0 = flags_1_19_1 { configProgramPathExtra = toNubList [] , configInstallDirs = configInstallDirs_1_18_0} configInstallDirs_1_18_0 = (configInstallDirs flags_1_19_1) { sysconfdir = NoFlag } -- Cabal < 1.14.0 doesn't know about '--disable-benchmarks'. flags_1_14_0 = flags_1_18_0 { configBenchmarks = NoFlag } -- Cabal < 1.12.0 doesn't know about '--enable/disable-executable-dynamic' -- and '--enable/disable-library-coverage'. flags_1_12_0 = flags_1_14_0 { configLibCoverage = NoFlag , configDynExe = NoFlag } -- Cabal < 1.10.0 doesn't know about '--disable-tests'. flags_1_10_0 = flags_1_12_0 { configTests = NoFlag } -- Cabal < 1.3.10 does not grok the '--constraints' flag. flags_1_3_10 = flags_1_10_0 { configConstraints = [] } -- | Get the package database settings from 'ConfigFlags', accounting for -- @--package-db@ and @--user@ flags. configPackageDB' :: ConfigFlags -> PackageDBStack configPackageDB' cfg = interpretPackageDbFlags userInstall (configPackageDBs cfg) where userInstall = Cabal.fromFlagOrDefault True (configUserInstall cfg) -- | Configure the compiler, but reduce verbosity during this step. configCompilerAux' :: ConfigFlags -> IO (Compiler, Platform, ProgramDb) configCompilerAux' configFlags = configCompilerAuxEx configFlags --FIXME: make configCompilerAux use a sensible verbosity { configVerbosity = fmap lessVerbose (configVerbosity configFlags) } -- ------------------------------------------------------------ -- * Config extra flags -- ------------------------------------------------------------ -- | cabal configure takes some extra flags beyond runghc Setup configure -- data ConfigExFlags = ConfigExFlags { configCabalVersion :: Flag Version, configExConstraints :: [(UserConstraint, ConstraintSource)], configPreferences :: [PackageVersionConstraint], configSolver :: Flag PreSolver, configAllowNewer :: Maybe AllowNewer, configAllowOlder :: Maybe AllowOlder, configWriteGhcEnvironmentFilesPolicy :: Flag WriteGhcEnvironmentFilesPolicy } deriving (Eq, Show, Generic) defaultConfigExFlags :: ConfigExFlags defaultConfigExFlags = mempty { configSolver = Flag defaultSolver } configureExCommand :: CommandUI (ConfigFlags, ConfigExFlags) configureExCommand = configureCommand { commandDefaultFlags = (mempty, defaultConfigExFlags), commandOptions = \showOrParseArgs -> liftOptions fst setFst (filter ((`notElem` ["constraint", "dependency", "exact-configuration"]) . optionName) $ configureOptions showOrParseArgs) ++ liftOptions snd setSnd (configureExOptions showOrParseArgs ConstraintSourceCommandlineFlag) } where setFst a (_,b) = (a,b) setSnd b (a,_) = (a,b) configureExOptions :: ShowOrParseArgs -> ConstraintSource -> [OptionField ConfigExFlags] configureExOptions _showOrParseArgs src = [ option [] ["cabal-lib-version"] ("Select which version of the Cabal lib to use to build packages " ++ "(useful for testing).") configCabalVersion (\v flags -> flags { configCabalVersion = v }) (reqArg "VERSION" (parsecToReadE ("Cannot parse cabal lib version: "++) (fmap toFlag parsec)) (map prettyShow. flagToList)) , option [] ["constraint"] "Specify constraints on a package (version, installed/source, flags)" configExConstraints (\v flags -> flags { configExConstraints = v }) (reqArg "CONSTRAINT" ((\x -> [(x, src)]) `fmap` ReadE readUserConstraint) (map $ prettyShow . fst)) , option [] ["preference"] "Specify preferences (soft constraints) on the version of a package" configPreferences (\v flags -> flags { configPreferences = v }) (reqArg "CONSTRAINT" (parsecToReadE (const "dependency expected") (fmap (\x -> [x]) parsec)) (map prettyShow)) , optionSolver configSolver (\v flags -> flags { configSolver = v }) , option [] ["allow-older"] ("Ignore lower bounds in all dependencies or DEPS") (fmap unAllowOlder . configAllowOlder) (\v flags -> flags { configAllowOlder = fmap AllowOlder v}) (optArg "DEPS" (parsecToReadE ("Cannot parse the list of packages: " ++) relaxDepsParser) (Just RelaxDepsAll) relaxDepsPrinter) , option [] ["allow-newer"] ("Ignore upper bounds in all dependencies or DEPS") (fmap unAllowNewer . configAllowNewer) (\v flags -> flags { configAllowNewer = fmap AllowNewer v}) (optArg "DEPS" (parsecToReadE ("Cannot parse the list of packages: " ++) relaxDepsParser) (Just RelaxDepsAll) relaxDepsPrinter) , option [] ["write-ghc-environment-files"] ("Whether to create a .ghc.environment file after a successful build" ++ " (v2-build only)") configWriteGhcEnvironmentFilesPolicy (\v flags -> flags { configWriteGhcEnvironmentFilesPolicy = v}) (reqArg "always|never|ghc8.4.4+" writeGhcEnvironmentFilesPolicyParser writeGhcEnvironmentFilesPolicyPrinter) ] writeGhcEnvironmentFilesPolicyParser :: ReadE (Flag WriteGhcEnvironmentFilesPolicy) writeGhcEnvironmentFilesPolicyParser = ReadE $ \case "always" -> Right $ Flag AlwaysWriteGhcEnvironmentFiles "never" -> Right $ Flag NeverWriteGhcEnvironmentFiles "ghc8.4.4+" -> Right $ Flag WriteGhcEnvironmentFilesOnlyForGhc844AndNewer policy -> Left $ "Cannot parse the GHC environment file write policy '" <> policy <> "'" writeGhcEnvironmentFilesPolicyPrinter :: Flag WriteGhcEnvironmentFilesPolicy -> [String] writeGhcEnvironmentFilesPolicyPrinter = \case (Flag AlwaysWriteGhcEnvironmentFiles) -> ["always"] (Flag NeverWriteGhcEnvironmentFiles) -> ["never"] (Flag WriteGhcEnvironmentFilesOnlyForGhc844AndNewer) -> ["ghc8.4.4+"] NoFlag -> [] relaxDepsParser :: CabalParsing m => m (Maybe RelaxDeps) relaxDepsParser = (Just . RelaxDepsSome . toList) `fmap` P.sepByNonEmpty parsec (P.char ',') relaxDepsPrinter :: (Maybe RelaxDeps) -> [Maybe String] relaxDepsPrinter Nothing = [] relaxDepsPrinter (Just RelaxDepsAll) = [Nothing] relaxDepsPrinter (Just (RelaxDepsSome pkgs)) = map (Just . prettyShow) $ pkgs instance Monoid ConfigExFlags where mempty = gmempty mappend = (<>) instance Semigroup ConfigExFlags where (<>) = gmappend reconfigureCommand :: CommandUI (ConfigFlags, ConfigExFlags) reconfigureCommand = configureExCommand { commandName = "reconfigure" , commandSynopsis = "Reconfigure the package if necessary." , commandDescription = Just $ \pname -> wrapText $ "Run `configure` with the most recently used flags, or append FLAGS " ++ "to the most recently used configuration. " ++ "Accepts the same flags as `" ++ pname ++ " v1-configure'. " ++ "If the package has never been configured, the default flags are " ++ "used." , commandNotes = Just $ \pname -> "Examples:\n" ++ " " ++ pname ++ " v1-reconfigure\n" ++ " Configure with the most recently used flags.\n" ++ " " ++ pname ++ " v1-reconfigure -w PATH\n" ++ " Reconfigure with the most recently used flags,\n" ++ " but use the compiler at PATH.\n\n" , commandUsage = usageAlternatives "v1-reconfigure" [ "[FLAGS]" ] , commandDefaultFlags = mempty } -- ------------------------------------------------------------ -- * Build flags -- ------------------------------------------------------------ buildCommand :: CommandUI BuildFlags buildCommand = parent { commandName = "build", commandDescription = Just $ \_ -> wrapText $ "Components encompass executables, tests, and benchmarks.\n" ++ "\n" ++ "Affected by configuration options, see `v1-configure`.\n", commandDefaultFlags = commandDefaultFlags parent, commandUsage = usageAlternatives "v1-build" $ [ "[FLAGS]", "COMPONENTS [FLAGS]" ], commandOptions = commandOptions parent , commandNotes = Just $ \pname -> "Examples:\n" ++ " " ++ pname ++ " v1-build " ++ " All the components in the package\n" ++ " " ++ pname ++ " v1-build foo " ++ " A component (i.e. lib, exe, test suite)\n\n" ++ Cabal.programFlagsDescription defaultProgramDb } where parent = Cabal.buildCommand defaultProgramDb -- ------------------------------------------------------------ -- * Test flags -- ------------------------------------------------------------ -- | Given some 'TestFlags' for the version of Cabal that -- cabal-install was built with, and a target older 'Version' of -- Cabal that we want to pass these flags to, convert the -- flags into a form that will be accepted by the older -- Setup script. Generally speaking, this just means filtering -- out flags that the old Cabal library doesn't understand, but -- in some cases it may also mean "emulating" a feature using -- some more legacy flags. filterTestFlags :: TestFlags -> Version -> TestFlags filterTestFlags flags cabalLibVersion -- NB: we expect the latest version to be the most common case, -- so test it first. | cabalLibVersion >= mkVersion [3,0,0] = flags_latest -- The naming convention is that flags_version gives flags with -- all flags *introduced* in version eliminated. -- It is NOT the latest version of Cabal library that -- these flags work for; version of introduction is a more -- natural metric. | cabalLibVersion < mkVersion [3,0,0] = flags_3_0_0 | otherwise = error "the impossible just happened" -- see first guard where flags_latest = flags flags_3_0_0 = flags_latest { -- Cabal < 3.0 doesn't know about --test-wrapper Cabal.testWrapper = NoFlag } -- ------------------------------------------------------------ -- * Repl command -- ------------------------------------------------------------ replCommand :: CommandUI ReplFlags replCommand = parent { commandName = "repl", commandDescription = Just $ \pname -> wrapText $ "If the current directory contains no package, ignores COMPONENT " ++ "parameters and opens an interactive interpreter session;\n" ++ "\n" ++ "Otherwise, (re)configures with the given or default flags, and " ++ "loads the interpreter with the relevant modules. For executables, " ++ "tests and benchmarks, loads the main module (and its " ++ "dependencies); for libraries all exposed/other modules.\n" ++ "\n" ++ "The default component is the library itself, or the executable " ++ "if that is the only component.\n" ++ "\n" ++ "Support for loading specific modules is planned but not " ++ "implemented yet. For certain scenarios, `" ++ pname ++ " v1-exec -- ghci :l Foo` may be used instead. Note that `v1-exec` will " ++ "not (re)configure and you will have to specify the location of " ++ "other modules, if required.\n", commandUsage = \pname -> "Usage: " ++ pname ++ " v1-repl [COMPONENT] [FLAGS]\n", commandDefaultFlags = commandDefaultFlags parent, commandOptions = commandOptions parent, commandNotes = Just $ \pname -> "Examples:\n" ++ " " ++ pname ++ " v1-repl " ++ " The first component in the package\n" ++ " " ++ pname ++ " v1-repl foo " ++ " A named component (i.e. lib, exe, test suite)\n" ++ " " ++ pname ++ " v1-repl --ghc-options=\"-lstdc++\"" ++ " Specifying flags for interpreter\n" } where parent = Cabal.replCommand defaultProgramDb -- ------------------------------------------------------------ -- * Test command -- ------------------------------------------------------------ testCommand :: CommandUI (BuildFlags, TestFlags) testCommand = parent { commandName = "test", commandDescription = Just $ \pname -> wrapText $ "If necessary (re)configures with `--enable-tests` flag and builds" ++ " the test suite.\n" ++ "\n" ++ "Remember that the tests' dependencies must be installed if there" ++ " are additional ones; e.g. with `" ++ pname ++ " v1-install --only-dependencies --enable-tests`.\n" ++ "\n" ++ "By defining UserHooks in a custom Setup.hs, the package can" ++ " define actions to be executed before and after running tests.\n", commandUsage = usageAlternatives "v1-test" [ "[FLAGS]", "TESTCOMPONENTS [FLAGS]" ], commandDefaultFlags = (Cabal.defaultBuildFlags, commandDefaultFlags parent), commandOptions = \showOrParseArgs -> liftOptions get1 set1 (Cabal.buildOptions progDb showOrParseArgs) ++ liftOptions get2 set2 (commandOptions parent showOrParseArgs) } where get1 (a,_) = a; set1 a (_,b) = (a,b) get2 (_,b) = b; set2 b (a,_) = (a,b) parent = Cabal.testCommand progDb = defaultProgramDb -- ------------------------------------------------------------ -- * Bench command -- ------------------------------------------------------------ benchmarkCommand :: CommandUI (BuildFlags, BenchmarkFlags) benchmarkCommand = parent { commandName = "bench", commandUsage = usageAlternatives "v1-bench" [ "[FLAGS]", "BENCHCOMPONENTS [FLAGS]" ], commandDescription = Just $ \pname -> wrapText $ "If necessary (re)configures with `--enable-benchmarks` flag and" ++ " builds the benchmarks.\n" ++ "\n" ++ "Remember that the benchmarks' dependencies must be installed if" ++ " there are additional ones; e.g. with `" ++ pname ++ " v1-install --only-dependencies --enable-benchmarks`.\n" ++ "\n" ++ "By defining UserHooks in a custom Setup.hs, the package can" ++ " define actions to be executed before and after running" ++ " benchmarks.\n", commandDefaultFlags = (Cabal.defaultBuildFlags, commandDefaultFlags parent), commandOptions = \showOrParseArgs -> liftOptions get1 set1 (Cabal.buildOptions progDb showOrParseArgs) ++ liftOptions get2 set2 (commandOptions parent showOrParseArgs) } where get1 (a,_) = a; set1 a (_,b) = (a,b) get2 (_,b) = b; set2 b (a,_) = (a,b) parent = Cabal.benchmarkCommand progDb = defaultProgramDb -- ------------------------------------------------------------ -- * Fetch command -- ------------------------------------------------------------ data FetchFlags = FetchFlags { -- fetchOutput :: Flag FilePath, fetchDeps :: Flag Bool, fetchDryRun :: Flag Bool, fetchSolver :: Flag PreSolver, fetchMaxBackjumps :: Flag Int, fetchReorderGoals :: Flag ReorderGoals, fetchCountConflicts :: Flag CountConflicts, fetchFineGrainedConflicts :: Flag FineGrainedConflicts, fetchMinimizeConflictSet :: Flag MinimizeConflictSet, fetchIndependentGoals :: Flag IndependentGoals, fetchShadowPkgs :: Flag ShadowPkgs, fetchStrongFlags :: Flag StrongFlags, fetchAllowBootLibInstalls :: Flag AllowBootLibInstalls, fetchOnlyConstrained :: Flag OnlyConstrained, fetchTests :: Flag Bool, fetchBenchmarks :: Flag Bool, fetchVerbosity :: Flag Verbosity } defaultFetchFlags :: FetchFlags defaultFetchFlags = FetchFlags { -- fetchOutput = mempty, fetchDeps = toFlag True, fetchDryRun = toFlag False, fetchSolver = Flag defaultSolver, fetchMaxBackjumps = Flag defaultMaxBackjumps, fetchReorderGoals = Flag (ReorderGoals False), fetchCountConflicts = Flag (CountConflicts True), fetchFineGrainedConflicts = Flag (FineGrainedConflicts True), fetchMinimizeConflictSet = Flag (MinimizeConflictSet False), fetchIndependentGoals = Flag (IndependentGoals False), fetchShadowPkgs = Flag (ShadowPkgs False), fetchStrongFlags = Flag (StrongFlags False), fetchAllowBootLibInstalls = Flag (AllowBootLibInstalls False), fetchOnlyConstrained = Flag OnlyConstrainedNone, fetchTests = toFlag False, fetchBenchmarks = toFlag False, fetchVerbosity = toFlag normal } fetchCommand :: CommandUI FetchFlags fetchCommand = CommandUI { commandName = "fetch", commandSynopsis = "Downloads packages for later installation.", commandUsage = usageAlternatives "fetch" [ "[FLAGS] PACKAGES" ], commandDescription = Just $ \_ -> "Note that it currently is not possible to fetch the dependencies for a\n" ++ "package in the current directory.\n", commandNotes = Nothing, commandDefaultFlags = defaultFetchFlags, commandOptions = \ showOrParseArgs -> [ optionVerbosity fetchVerbosity (\v flags -> flags { fetchVerbosity = v }) -- , option "o" ["output"] -- "Put the package(s) somewhere specific rather than the usual cache." -- fetchOutput (\v flags -> flags { fetchOutput = v }) -- (reqArgFlag "PATH") , option [] ["dependencies", "deps"] "Resolve and fetch dependencies (default)" fetchDeps (\v flags -> flags { fetchDeps = v }) trueArg , option [] ["no-dependencies", "no-deps"] "Ignore dependencies" fetchDeps (\v flags -> flags { fetchDeps = v }) falseArg , option [] ["dry-run"] "Do not install anything, only print what would be installed." fetchDryRun (\v flags -> flags { fetchDryRun = v }) trueArg , option "" ["tests"] "dependency checking and compilation for test suites listed in the package description file." fetchTests (\v flags -> flags { fetchTests = v }) (boolOpt [] []) , option "" ["benchmarks"] "dependency checking and compilation for benchmarks listed in the package description file." fetchBenchmarks (\v flags -> flags { fetchBenchmarks = v }) (boolOpt [] []) ] ++ optionSolver fetchSolver (\v flags -> flags { fetchSolver = v }) : optionSolverFlags showOrParseArgs fetchMaxBackjumps (\v flags -> flags { fetchMaxBackjumps = v }) fetchReorderGoals (\v flags -> flags { fetchReorderGoals = v }) fetchCountConflicts (\v flags -> flags { fetchCountConflicts = v }) fetchFineGrainedConflicts (\v flags -> flags { fetchFineGrainedConflicts = v }) fetchMinimizeConflictSet (\v flags -> flags { fetchMinimizeConflictSet = v }) fetchIndependentGoals (\v flags -> flags { fetchIndependentGoals = v }) fetchShadowPkgs (\v flags -> flags { fetchShadowPkgs = v }) fetchStrongFlags (\v flags -> flags { fetchStrongFlags = v }) fetchAllowBootLibInstalls (\v flags -> flags { fetchAllowBootLibInstalls = v }) fetchOnlyConstrained (\v flags -> flags { fetchOnlyConstrained = v }) } -- ------------------------------------------------------------ -- * Freeze command -- ------------------------------------------------------------ data FreezeFlags = FreezeFlags { freezeDryRun :: Flag Bool, freezeTests :: Flag Bool, freezeBenchmarks :: Flag Bool, freezeSolver :: Flag PreSolver, freezeMaxBackjumps :: Flag Int, freezeReorderGoals :: Flag ReorderGoals, freezeCountConflicts :: Flag CountConflicts, freezeFineGrainedConflicts :: Flag FineGrainedConflicts, freezeMinimizeConflictSet :: Flag MinimizeConflictSet, freezeIndependentGoals :: Flag IndependentGoals, freezeShadowPkgs :: Flag ShadowPkgs, freezeStrongFlags :: Flag StrongFlags, freezeAllowBootLibInstalls :: Flag AllowBootLibInstalls, freezeOnlyConstrained :: Flag OnlyConstrained, freezeVerbosity :: Flag Verbosity } defaultFreezeFlags :: FreezeFlags defaultFreezeFlags = FreezeFlags { freezeDryRun = toFlag False, freezeTests = toFlag False, freezeBenchmarks = toFlag False, freezeSolver = Flag defaultSolver, freezeMaxBackjumps = Flag defaultMaxBackjumps, freezeReorderGoals = Flag (ReorderGoals False), freezeCountConflicts = Flag (CountConflicts True), freezeFineGrainedConflicts = Flag (FineGrainedConflicts True), freezeMinimizeConflictSet = Flag (MinimizeConflictSet False), freezeIndependentGoals = Flag (IndependentGoals False), freezeShadowPkgs = Flag (ShadowPkgs False), freezeStrongFlags = Flag (StrongFlags False), freezeAllowBootLibInstalls = Flag (AllowBootLibInstalls False), freezeOnlyConstrained = Flag OnlyConstrainedNone, freezeVerbosity = toFlag normal } freezeCommand :: CommandUI FreezeFlags freezeCommand = CommandUI { commandName = "freeze", commandSynopsis = "Freeze dependencies.", commandDescription = Just $ \_ -> wrapText $ "Calculates a valid set of dependencies and their exact versions. " ++ "If successful, saves the result to the file `cabal.config`.\n" ++ "\n" ++ "The package versions specified in `cabal.config` will be used for " ++ "any future installs.\n" ++ "\n" ++ "An existing `cabal.config` is ignored and overwritten.\n", commandNotes = Nothing, commandUsage = usageFlags "freeze", commandDefaultFlags = defaultFreezeFlags, commandOptions = \ showOrParseArgs -> [ optionVerbosity freezeVerbosity (\v flags -> flags { freezeVerbosity = v }) , option [] ["dry-run"] "Do not freeze anything, only print what would be frozen" freezeDryRun (\v flags -> flags { freezeDryRun = v }) trueArg , option [] ["tests"] ("freezing of the dependencies of any tests suites " ++ "in the package description file.") freezeTests (\v flags -> flags { freezeTests = v }) (boolOpt [] []) , option [] ["benchmarks"] ("freezing of the dependencies of any benchmarks suites " ++ "in the package description file.") freezeBenchmarks (\v flags -> flags { freezeBenchmarks = v }) (boolOpt [] []) ] ++ optionSolver freezeSolver (\v flags -> flags { freezeSolver = v }): optionSolverFlags showOrParseArgs freezeMaxBackjumps (\v flags -> flags { freezeMaxBackjumps = v }) freezeReorderGoals (\v flags -> flags { freezeReorderGoals = v }) freezeCountConflicts (\v flags -> flags { freezeCountConflicts = v }) freezeFineGrainedConflicts (\v flags -> flags { freezeFineGrainedConflicts = v }) freezeMinimizeConflictSet (\v flags -> flags { freezeMinimizeConflictSet = v }) freezeIndependentGoals (\v flags -> flags { freezeIndependentGoals = v }) freezeShadowPkgs (\v flags -> flags { freezeShadowPkgs = v }) freezeStrongFlags (\v flags -> flags { freezeStrongFlags = v }) freezeAllowBootLibInstalls (\v flags -> flags { freezeAllowBootLibInstalls = v }) freezeOnlyConstrained (\v flags -> flags { freezeOnlyConstrained = v }) } -- ------------------------------------------------------------ -- * 'gen-bounds' command -- ------------------------------------------------------------ genBoundsCommand :: CommandUI FreezeFlags genBoundsCommand = CommandUI { commandName = "gen-bounds", commandSynopsis = "Generate dependency bounds.", commandDescription = Just $ \_ -> wrapText $ "Generates bounds for all dependencies that do not currently have them. " ++ "Generated bounds are printed to stdout. " ++ "You can then paste them into your .cabal file.\n" ++ "\n", commandNotes = Nothing, commandUsage = usageFlags "gen-bounds", commandDefaultFlags = defaultFreezeFlags, commandOptions = \ _ -> [ optionVerbosity freezeVerbosity (\v flags -> flags { freezeVerbosity = v }) ] } -- ------------------------------------------------------------ -- * 'outdated' command -- ------------------------------------------------------------ data IgnoreMajorVersionBumps = IgnoreMajorVersionBumpsNone | IgnoreMajorVersionBumpsAll | IgnoreMajorVersionBumpsSome [PackageName] instance Monoid IgnoreMajorVersionBumps where mempty = IgnoreMajorVersionBumpsNone mappend = (<>) instance Semigroup IgnoreMajorVersionBumps where IgnoreMajorVersionBumpsNone <> r = r l@IgnoreMajorVersionBumpsAll <> _ = l l@(IgnoreMajorVersionBumpsSome _) <> IgnoreMajorVersionBumpsNone = l (IgnoreMajorVersionBumpsSome _) <> r@IgnoreMajorVersionBumpsAll = r (IgnoreMajorVersionBumpsSome a) <> (IgnoreMajorVersionBumpsSome b) = IgnoreMajorVersionBumpsSome (a ++ b) data OutdatedFlags = OutdatedFlags { outdatedVerbosity :: Flag Verbosity, outdatedFreezeFile :: Flag Bool, outdatedNewFreezeFile :: Flag Bool, outdatedProjectFile :: Flag FilePath, outdatedSimpleOutput :: Flag Bool, outdatedExitCode :: Flag Bool, outdatedQuiet :: Flag Bool, outdatedIgnore :: [PackageName], outdatedMinor :: Maybe IgnoreMajorVersionBumps } defaultOutdatedFlags :: OutdatedFlags defaultOutdatedFlags = OutdatedFlags { outdatedVerbosity = toFlag normal, outdatedFreezeFile = mempty, outdatedNewFreezeFile = mempty, outdatedProjectFile = mempty, outdatedSimpleOutput = mempty, outdatedExitCode = mempty, outdatedQuiet = mempty, outdatedIgnore = mempty, outdatedMinor = mempty } outdatedCommand :: CommandUI OutdatedFlags outdatedCommand = CommandUI { commandName = "outdated", commandSynopsis = "Check for outdated dependencies", commandDescription = Just $ \_ -> wrapText $ "Checks for outdated dependencies in the package description file " ++ "or freeze file", commandNotes = Nothing, commandUsage = usageFlags "outdated", commandDefaultFlags = defaultOutdatedFlags, commandOptions = \ _ -> [ optionVerbosity outdatedVerbosity (\v flags -> flags { outdatedVerbosity = v }) ,option [] ["freeze-file", "v1-freeze-file"] "Act on the freeze file" outdatedFreezeFile (\v flags -> flags { outdatedFreezeFile = v }) trueArg ,option [] ["v2-freeze-file", "new-freeze-file"] "Act on the new-style freeze file (default: cabal.project.freeze)" outdatedNewFreezeFile (\v flags -> flags { outdatedNewFreezeFile = v }) trueArg ,option [] ["project-file"] "Act on the new-style freeze file named PROJECTFILE.freeze rather than the default cabal.project.freeze" outdatedProjectFile (\v flags -> flags { outdatedProjectFile = v }) (reqArgFlag "PROJECTFILE") ,option [] ["simple-output"] "Only print names of outdated dependencies, one per line" outdatedSimpleOutput (\v flags -> flags { outdatedSimpleOutput = v }) trueArg ,option [] ["exit-code"] "Exit with non-zero when there are outdated dependencies" outdatedExitCode (\v flags -> flags { outdatedExitCode = v }) trueArg ,option ['q'] ["quiet"] "Don't print any output. Implies '--exit-code' and '-v0'" outdatedQuiet (\v flags -> flags { outdatedQuiet = v }) trueArg ,option [] ["ignore"] "Packages to ignore" outdatedIgnore (\v flags -> flags { outdatedIgnore = v }) (reqArg "PKGS" pkgNameListParser (map prettyShow)) ,option [] ["minor"] "Ignore major version bumps for these packages" outdatedMinor (\v flags -> flags { outdatedMinor = v }) (optArg "PKGS" ignoreMajorVersionBumpsParser (Just IgnoreMajorVersionBumpsAll) ignoreMajorVersionBumpsPrinter) ] } where ignoreMajorVersionBumpsPrinter :: (Maybe IgnoreMajorVersionBumps) -> [Maybe String] ignoreMajorVersionBumpsPrinter Nothing = [] ignoreMajorVersionBumpsPrinter (Just IgnoreMajorVersionBumpsNone)= [] ignoreMajorVersionBumpsPrinter (Just IgnoreMajorVersionBumpsAll) = [Nothing] ignoreMajorVersionBumpsPrinter (Just (IgnoreMajorVersionBumpsSome pkgs)) = map (Just . prettyShow) $ pkgs ignoreMajorVersionBumpsParser = (Just . IgnoreMajorVersionBumpsSome) `fmap` pkgNameListParser pkgNameListParser = parsecToReadE ("Couldn't parse the list of package names: " ++) (fmap toList (P.sepByNonEmpty parsec (P.char ','))) -- ------------------------------------------------------------ -- * Update command -- ------------------------------------------------------------ data UpdateFlags = UpdateFlags { updateVerbosity :: Flag Verbosity, updateIndexState :: Flag TotalIndexState } deriving Generic defaultUpdateFlags :: UpdateFlags defaultUpdateFlags = UpdateFlags { updateVerbosity = toFlag normal, updateIndexState = toFlag headTotalIndexState } updateCommand :: CommandUI UpdateFlags updateCommand = CommandUI { commandName = "update", commandSynopsis = "Updates list of known packages.", commandDescription = Just $ \_ -> "For all known remote repositories, download the package list.\n", commandNotes = Just $ \_ -> relevantConfigValuesText ["remote-repo" ,"remote-repo-cache" ,"local-repo"], commandUsage = usageFlags "v1-update", commandDefaultFlags = defaultUpdateFlags, commandOptions = \_ -> [ optionVerbosity updateVerbosity (\v flags -> flags { updateVerbosity = v }), option [] ["index-state"] ("Update the source package index to its state as it existed at a previous time. " ++ "Accepts unix-timestamps (e.g. '@1474732068'), ISO8601 UTC timestamps " ++ "(e.g. '2016-09-24T17:47:48Z'), or 'HEAD' (default: 'HEAD').") updateIndexState (\v flags -> flags { updateIndexState = v }) (reqArg "STATE" (parsecToReadE (const $ "index-state must be a " ++ "unix-timestamps (e.g. '@1474732068'), " ++ "a ISO8601 UTC timestamp " ++ "(e.g. '2016-09-24T17:47:48Z'), or 'HEAD'") (toFlag `fmap` parsec)) (flagToList . fmap prettyShow)) ] } -- ------------------------------------------------------------ -- * Other commands -- ------------------------------------------------------------ cleanCommand :: CommandUI CleanFlags cleanCommand = Cabal.cleanCommand { commandUsage = \pname -> "Usage: " ++ pname ++ " v1-clean [FLAGS]\n" } checkCommand :: CommandUI (Flag Verbosity) checkCommand = CommandUI { commandName = "check", commandSynopsis = "Check the package for common mistakes.", commandDescription = Just $ \_ -> wrapText $ "Expects a .cabal package file in the current directory.\n" ++ "\n" ++ "The checks correspond to the requirements to packages on Hackage. " ++ "If no errors and warnings are reported, Hackage will accept this " ++ "package.\n", commandNotes = Nothing, commandUsage = usageFlags "check", commandDefaultFlags = toFlag normal, commandOptions = \_ -> [optionVerbosity id const] } formatCommand :: CommandUI (Flag Verbosity) formatCommand = CommandUI { commandName = "format", commandSynopsis = "Reformat the .cabal file using the standard style.", commandDescription = Nothing, commandNotes = Nothing, commandUsage = usageAlternatives "format" ["[FILE]"], commandDefaultFlags = toFlag normal, commandOptions = \_ -> [] } manpageCommand :: CommandUI ManpageFlags manpageCommand = CommandUI { commandName = "man", commandSynopsis = "Outputs manpage source.", commandDescription = Just $ \_ -> "Output manpage source to STDOUT.\n", commandNotes = Nothing, commandUsage = usageFlags "man", commandDefaultFlags = defaultManpageFlags, commandOptions = manpageOptions } runCommand :: CommandUI BuildFlags runCommand = CommandUI { commandName = "run", commandSynopsis = "Builds and runs an executable.", commandDescription = Just $ \pname -> wrapText $ "Builds and then runs the specified executable. If no executable is " ++ "specified, but the package contains just one executable, that one " ++ "is built and executed.\n" ++ "\n" ++ "Use `" ++ pname ++ " v1-test --show-details=streaming` to run a " ++ "test-suite and get its full output.\n", commandNotes = Just $ \pname -> "Examples:\n" ++ " " ++ pname ++ " v1-run\n" ++ " Run the only executable in the current package;\n" ++ " " ++ pname ++ " v1-run foo -- --fooflag\n" ++ " Works similar to `./foo --fooflag`.\n", commandUsage = usageAlternatives "v1-run" ["[FLAGS] [EXECUTABLE] [-- EXECUTABLE_FLAGS]"], commandDefaultFlags = mempty, commandOptions = commandOptions parent } where parent = Cabal.buildCommand defaultProgramDb -- ------------------------------------------------------------ -- * Report flags -- ------------------------------------------------------------ data ReportFlags = ReportFlags { reportUsername :: Flag Username, reportPassword :: Flag Password, reportVerbosity :: Flag Verbosity } deriving Generic defaultReportFlags :: ReportFlags defaultReportFlags = ReportFlags { reportUsername = mempty, reportPassword = mempty, reportVerbosity = toFlag normal } reportCommand :: CommandUI ReportFlags reportCommand = CommandUI { commandName = "report", commandSynopsis = "Upload build reports to a remote server.", commandDescription = Nothing, commandNotes = Just $ \_ -> "You can store your Hackage login in the ~/.cabal/config file\n", commandUsage = usageAlternatives "report" ["[FLAGS]"], commandDefaultFlags = defaultReportFlags, commandOptions = \_ -> [optionVerbosity reportVerbosity (\v flags -> flags { reportVerbosity = v }) ,option ['u'] ["username"] "Hackage username." reportUsername (\v flags -> flags { reportUsername = v }) (reqArg' "USERNAME" (toFlag . Username) (flagToList . fmap unUsername)) ,option ['p'] ["password"] "Hackage password." reportPassword (\v flags -> flags { reportPassword = v }) (reqArg' "PASSWORD" (toFlag . Password) (flagToList . fmap unPassword)) ] } instance Monoid ReportFlags where mempty = gmempty mappend = (<>) instance Semigroup ReportFlags where (<>) = gmappend -- ------------------------------------------------------------ -- * Get flags -- ------------------------------------------------------------ data GetFlags = GetFlags { getDestDir :: Flag FilePath, getPristine :: Flag Bool, getIndexState :: Flag TotalIndexState, getActiveRepos :: Flag ActiveRepos, getSourceRepository :: Flag (Maybe RepoKind), getVerbosity :: Flag Verbosity } deriving Generic defaultGetFlags :: GetFlags defaultGetFlags = GetFlags { getDestDir = mempty, getPristine = mempty, getIndexState = mempty, getActiveRepos = mempty, getSourceRepository = mempty, getVerbosity = toFlag normal } getCommand :: CommandUI GetFlags getCommand = CommandUI { commandName = "get", commandSynopsis = "Download/Extract a package's source code (repository).", commandDescription = Just $ \_ -> wrapText $ "Creates a local copy of a package's source code. By default it gets " ++ "the source\ntarball and unpacks it in a local subdirectory. " ++ "Alternatively, with -s it will\nget the code from the source " ++ "repository specified by the package.\n", commandNotes = Just $ \pname -> "Examples:\n" ++ " " ++ pname ++ " get hlint\n" ++ " Download the latest stable version of hlint;\n" ++ " " ++ pname ++ " get lens --source-repository=head\n" ++ " Download the source repository (i.e. git clone from github).\n", commandUsage = usagePackages "get", commandDefaultFlags = defaultGetFlags, commandOptions = \_ -> [ optionVerbosity getVerbosity (\v flags -> flags { getVerbosity = v }) ,option "d" ["destdir"] "Where to place the package source, defaults to the current directory." getDestDir (\v flags -> flags { getDestDir = v }) (reqArgFlag "PATH") ,option "s" ["source-repository"] "Copy the package's source repository (ie git clone, darcs get, etc as appropriate)." getSourceRepository (\v flags -> flags { getSourceRepository = v }) (optArg "[head|this|...]" (parsecToReadE (const "invalid source-repository") (fmap (toFlag . Just) parsec)) (Flag Nothing) (map (fmap show) . flagToList)) , option [] ["index-state"] ("Use source package index state as it existed at a previous time. " ++ "Accepts unix-timestamps (e.g. '@1474732068'), ISO8601 UTC timestamps " ++ "(e.g. '2016-09-24T17:47:48Z'), or 'HEAD' (default: 'HEAD'). " ++ "This determines which package versions are available as well as " ++ ".cabal file revision is selected (unless --pristine is used).") getIndexState (\v flags -> flags { getIndexState = v }) (reqArg "STATE" (parsecToReadE (const $ "index-state must be a " ++ "unix-timestamps (e.g. '@1474732068'), " ++ "a ISO8601 UTC timestamp " ++ "(e.g. '2016-09-24T17:47:48Z'), or 'HEAD'") (toFlag `fmap` parsec)) (flagToList . fmap prettyShow)) , option [] ["pristine"] ("Unpack the original pristine tarball, rather than updating the " ++ ".cabal file with the latest revision from the package archive.") getPristine (\v flags -> flags { getPristine = v }) trueArg ] } -- 'cabal unpack' is a deprecated alias for 'cabal get'. unpackCommand :: CommandUI GetFlags unpackCommand = getCommand { commandName = "unpack", commandUsage = usagePackages "unpack" } instance Monoid GetFlags where mempty = gmempty mappend = (<>) instance Semigroup GetFlags where (<>) = gmappend -- ------------------------------------------------------------ -- * List flags -- ------------------------------------------------------------ data ListFlags = ListFlags { listInstalled :: Flag Bool , listSimpleOutput :: Flag Bool , listCaseInsensitive :: Flag Bool , listVerbosity :: Flag Verbosity , listPackageDBs :: [Maybe PackageDB] , listHcPath :: Flag FilePath } deriving Generic defaultListFlags :: ListFlags defaultListFlags = ListFlags { listInstalled = Flag False , listSimpleOutput = Flag False , listCaseInsensitive = Flag True , listVerbosity = toFlag normal , listPackageDBs = [] , listHcPath = mempty } listCommand :: CommandUI ListFlags listCommand = CommandUI { commandName = "list", commandSynopsis = "List packages matching a search string.", commandDescription = Just $ \_ -> wrapText $ "List all packages, or all packages matching one of the search" ++ " strings.\n" ++ "\n" ++ "Use the package database specified with --package-db. " ++ "If not specified, use the user package database.\n", commandNotes = Just $ \pname -> "Examples:\n" ++ " " ++ pname ++ " list pandoc\n" ++ " Will find pandoc, pandoc-citeproc, pandoc-lens, ...\n", commandUsage = usageAlternatives "list" [ "[FLAGS]" , "[FLAGS] STRINGS"], commandDefaultFlags = defaultListFlags, commandOptions = const listOptions } listOptions :: [OptionField ListFlags] listOptions = [ optionVerbosity listVerbosity (\v flags -> flags { listVerbosity = v }) , option [] ["installed"] "Only print installed packages" listInstalled (\v flags -> flags { listInstalled = v }) trueArg , option [] ["simple-output"] "Print in a easy-to-parse format" listSimpleOutput (\v flags -> flags { listSimpleOutput = v }) trueArg , option ['i'] ["ignore-case"] "Ignore case destictions" listCaseInsensitive (\v flags -> flags { listCaseInsensitive = v }) (boolOpt' (['i'], ["ignore-case"]) (['I'], ["strict-case"])) , option "" ["package-db"] ( "Append the given package database to the list of package" ++ " databases used (to satisfy dependencies and register into)." ++ " May be a specific file, 'global' or 'user'. The initial list" ++ " is ['global'], ['global', 'user']," ++ " depending on context. Use 'clear' to reset the list to empty." ++ " See the user guide for details.") listPackageDBs (\v flags -> flags { listPackageDBs = v }) (reqArg' "DB" readPackageDbList showPackageDbList) , option "w" ["with-compiler"] "give the path to a particular compiler" listHcPath (\v flags -> flags { listHcPath = v }) (reqArgFlag "PATH") ] listNeedsCompiler :: ListFlags -> Bool listNeedsCompiler f = flagElim False (const True) (listHcPath f) || fromFlagOrDefault False (listInstalled f) instance Monoid ListFlags where mempty = gmempty mappend = (<>) instance Semigroup ListFlags where (<>) = gmappend -- ------------------------------------------------------------ -- * Info flags -- ------------------------------------------------------------ data InfoFlags = InfoFlags { infoVerbosity :: Flag Verbosity, infoPackageDBs :: [Maybe PackageDB] } deriving Generic defaultInfoFlags :: InfoFlags defaultInfoFlags = InfoFlags { infoVerbosity = toFlag normal, infoPackageDBs = [] } infoCommand :: CommandUI InfoFlags infoCommand = CommandUI { commandName = "info", commandSynopsis = "Display detailed information about a particular package.", commandDescription = Just $ \_ -> wrapText $ "Use the package database specified with --package-db. " ++ "If not specified, use the user package database.\n", commandNotes = Nothing, commandUsage = usageAlternatives "info" ["[FLAGS] PACKAGES"], commandDefaultFlags = defaultInfoFlags, commandOptions = \_ -> [ optionVerbosity infoVerbosity (\v flags -> flags { infoVerbosity = v }) , option "" ["package-db"] ( "Append the given package database to the list of package" ++ " databases used (to satisfy dependencies and register into)." ++ " May be a specific file, 'global' or 'user'. The initial list" ++ " is ['global'], ['global', 'user']," ++ " depending on context. Use 'clear' to reset the list to empty." ++ " See the user guide for details.") infoPackageDBs (\v flags -> flags { infoPackageDBs = v }) (reqArg' "DB" readPackageDbList showPackageDbList) ] } instance Monoid InfoFlags where mempty = gmempty mappend = (<>) instance Semigroup InfoFlags where (<>) = gmappend -- ------------------------------------------------------------ -- * Install flags -- ------------------------------------------------------------ -- | Install takes the same flags as configure along with a few extras. -- data InstallFlags = InstallFlags { installDocumentation :: Flag Bool, installHaddockIndex :: Flag PathTemplate, installDest :: Flag Cabal.CopyDest, installDryRun :: Flag Bool, installMaxBackjumps :: Flag Int, installReorderGoals :: Flag ReorderGoals, installCountConflicts :: Flag CountConflicts, installFineGrainedConflicts :: Flag FineGrainedConflicts, installMinimizeConflictSet :: Flag MinimizeConflictSet, installIndependentGoals :: Flag IndependentGoals, installShadowPkgs :: Flag ShadowPkgs, installStrongFlags :: Flag StrongFlags, installAllowBootLibInstalls :: Flag AllowBootLibInstalls, installOnlyConstrained :: Flag OnlyConstrained, installReinstall :: Flag Bool, installAvoidReinstalls :: Flag AvoidReinstalls, installOverrideReinstall :: Flag Bool, installUpgradeDeps :: Flag Bool, installOnly :: Flag Bool, installOnlyDeps :: Flag Bool, installIndexState :: Flag TotalIndexState, installRootCmd :: Flag String, installSummaryFile :: NubList PathTemplate, installLogFile :: Flag PathTemplate, installBuildReports :: Flag ReportLevel, installReportPlanningFailure :: Flag Bool, -- Note: symlink-bindir is no longer used by v2-install and can be removed -- when removing v1 commands installSymlinkBinDir :: Flag FilePath, installPerComponent :: Flag Bool, installOneShot :: Flag Bool, installNumJobs :: Flag (Maybe Int), installKeepGoing :: Flag Bool, installRunTests :: Flag Bool, installOfflineMode :: Flag Bool } deriving (Eq, Show, Generic) instance Binary InstallFlags defaultInstallFlags :: InstallFlags defaultInstallFlags = InstallFlags { installDocumentation = Flag False, installHaddockIndex = Flag docIndexFile, installDest = Flag Cabal.NoCopyDest, installDryRun = Flag False, installMaxBackjumps = Flag defaultMaxBackjumps, installReorderGoals = Flag (ReorderGoals False), installCountConflicts = Flag (CountConflicts True), installFineGrainedConflicts = Flag (FineGrainedConflicts True), installMinimizeConflictSet = Flag (MinimizeConflictSet False), installIndependentGoals= Flag (IndependentGoals False), installShadowPkgs = Flag (ShadowPkgs False), installStrongFlags = Flag (StrongFlags False), installAllowBootLibInstalls = Flag (AllowBootLibInstalls False), installOnlyConstrained = Flag OnlyConstrainedNone, installReinstall = Flag False, installAvoidReinstalls = Flag (AvoidReinstalls False), installOverrideReinstall = Flag False, installUpgradeDeps = Flag False, installOnly = Flag False, installOnlyDeps = Flag False, installIndexState = mempty, installRootCmd = mempty, installSummaryFile = mempty, installLogFile = mempty, installBuildReports = Flag NoReports, installReportPlanningFailure = Flag False, installSymlinkBinDir = mempty, installPerComponent = Flag True, installOneShot = Flag False, installNumJobs = mempty, installKeepGoing = Flag False, installRunTests = mempty, installOfflineMode = Flag False } where docIndexFile = toPathTemplate ("$datadir" "doc" "$arch-$os-$compiler" "index.html") defaultMaxBackjumps :: Int defaultMaxBackjumps = 4000 defaultSolver :: PreSolver defaultSolver = AlwaysModular allSolvers :: String allSolvers = intercalate ", " (map prettyShow ([minBound .. maxBound] :: [PreSolver])) installCommand :: CommandUI ( ConfigFlags, ConfigExFlags, InstallFlags , HaddockFlags, TestFlags, BenchmarkFlags ) installCommand = CommandUI { commandName = "install", commandSynopsis = "Install packages.", commandUsage = usageAlternatives "v1-install" [ "[FLAGS]" , "[FLAGS] PACKAGES" ], commandDescription = Just $ \_ -> wrapText $ "Installs one or more packages. By default, the installed package" ++ " will be registered in the user's package database." ++ "\n" ++ "If PACKAGES are specified, downloads and installs those packages." ++ " Otherwise, install the package in the current directory (and/or its" ++ " dependencies) (there must be exactly one .cabal file in the current" ++ " directory).\n" ++ "\n" ++ "The flags to `v1-install` are saved and" ++ " affect future commands such as `v1-build` and `v1-repl`. See the help for" ++ " `v1-configure` for a list of commands being affected.\n" ++ "\n" ++ "Installed executables will by default" ++ " be put into `~/.cabal/bin/`." ++ " If you want installed executable to be available globally, make" ++ " sure that the PATH environment variable contains that directory.\n" ++ "\n", commandNotes = Just $ \pname -> ( case commandNotes $ Cabal.configureCommand defaultProgramDb of Just desc -> desc pname ++ "\n" Nothing -> "" ) ++ "Examples:\n" ++ " " ++ pname ++ " v1-install " ++ " Package in the current directory\n" ++ " " ++ pname ++ " v1-install foo " ++ " Package from the hackage server\n" ++ " " ++ pname ++ " v1-install foo-1.0 " ++ " Specific version of a package\n" ++ " " ++ pname ++ " v1-install 'foo < 2' " ++ " Constrained package version\n" ++ " " ++ pname ++ " v1-install haddock --bindir=$HOME/hask-bin/ --datadir=$HOME/hask-data/\n" ++ " " ++ (map (const ' ') pname) ++ " " ++ " Change installation destination\n", commandDefaultFlags = (mempty, mempty, mempty, mempty, mempty, mempty), commandOptions = \showOrParseArgs -> liftOptions get1 set1 -- Note: [Hidden Flags] -- hide "constraint", "dependency", and -- "exact-configuration" from the configure options. (filter ((`notElem` ["constraint", "dependency" , "exact-configuration"]) . optionName) $ configureOptions showOrParseArgs) ++ liftOptions get2 set2 (configureExOptions showOrParseArgs ConstraintSourceCommandlineFlag) ++ liftOptions get3 set3 -- hide "target-package-db" flag from the -- install options. (filter ((`notElem` ["target-package-db"]) . optionName) $ installOptions showOrParseArgs) ++ liftOptions get4 set4 (haddockOptions showOrParseArgs) ++ liftOptions get5 set5 (testOptions showOrParseArgs) ++ liftOptions get6 set6 (benchmarkOptions showOrParseArgs) } where get1 (a,_,_,_,_,_) = a; set1 a (_,b,c,d,e,f) = (a,b,c,d,e,f) get2 (_,b,_,_,_,_) = b; set2 b (a,_,c,d,e,f) = (a,b,c,d,e,f) get3 (_,_,c,_,_,_) = c; set3 c (a,b,_,d,e,f) = (a,b,c,d,e,f) get4 (_,_,_,d,_,_) = d; set4 d (a,b,c,_,e,f) = (a,b,c,d,e,f) get5 (_,_,_,_,e,_) = e; set5 e (a,b,c,d,_,f) = (a,b,c,d,e,f) get6 (_,_,_,_,_,f) = f; set6 f (a,b,c,d,e,_) = (a,b,c,d,e,f) haddockCommand :: CommandUI HaddockFlags haddockCommand = Cabal.haddockCommand { commandUsage = usageAlternatives "v1-haddock" $ [ "[FLAGS]", "COMPONENTS [FLAGS]" ] } filterHaddockArgs :: [String] -> Version -> [String] filterHaddockArgs args cabalLibVersion | cabalLibVersion >= mkVersion [2,3,0] = args_latest | cabalLibVersion < mkVersion [2,3,0] = args_2_3_0 | otherwise = args_latest where args_latest = args -- Cabal < 2.3 doesn't know about per-component haddock args_2_3_0 = [] filterHaddockFlags :: HaddockFlags -> Version -> HaddockFlags filterHaddockFlags flags cabalLibVersion | cabalLibVersion >= mkVersion [2,3,0] = flags_latest | cabalLibVersion < mkVersion [2,3,0] = flags_2_3_0 | otherwise = flags_latest where flags_latest = flags flags_2_3_0 = flags_latest { -- Cabal < 2.3 doesn't know about per-component haddock haddockArgs = [] } haddockOptions :: ShowOrParseArgs -> [OptionField HaddockFlags] haddockOptions showOrParseArgs = [ opt { optionName = "haddock-" ++ name, optionDescr = [ fmapOptFlags (\(_, lflags) -> ([], map ("haddock-" ++) lflags)) descr | descr <- optionDescr opt] } | opt <- commandOptions Cabal.haddockCommand showOrParseArgs , let name = optionName opt , name `elem` ["hoogle", "html", "html-location" ,"executables", "tests", "benchmarks", "all", "internal", "css" ,"hyperlink-source", "quickjump", "hscolour-css" ,"contents-location", "for-hackage"] ] testOptions :: ShowOrParseArgs -> [OptionField TestFlags] testOptions showOrParseArgs = [ opt { optionName = prefixTest name, optionDescr = [ fmapOptFlags (\(_, lflags) -> ([], map prefixTest lflags)) descr | descr <- optionDescr opt] } | opt <- commandOptions Cabal.testCommand showOrParseArgs , let name = optionName opt , name `elem` ["log", "machine-log", "show-details", "keep-tix-files" ,"fail-when-no-test-suites", "test-options", "test-option" ,"test-wrapper"] ] where prefixTest name | "test-" `isPrefixOf` name = name | otherwise = "test-" ++ name benchmarkOptions :: ShowOrParseArgs -> [OptionField BenchmarkFlags] benchmarkOptions showOrParseArgs = [ opt { optionName = prefixBenchmark name, optionDescr = [ fmapOptFlags (\(_, lflags) -> ([], map prefixBenchmark lflags)) descr | descr <- optionDescr opt] } | opt <- commandOptions Cabal.benchmarkCommand showOrParseArgs , let name = optionName opt , name `elem` ["benchmark-options", "benchmark-option"] ] where prefixBenchmark name | "benchmark-" `isPrefixOf` name = name | otherwise = "benchmark-" ++ name fmapOptFlags :: (OptFlags -> OptFlags) -> OptDescr a -> OptDescr a fmapOptFlags modify (ReqArg d f p r w) = ReqArg d (modify f) p r w fmapOptFlags modify (OptArg d f p r i w) = OptArg d (modify f) p r i w fmapOptFlags modify (ChoiceOpt xs) = ChoiceOpt [(d, modify f, i, w) | (d, f, i, w) <- xs] fmapOptFlags modify (BoolOpt d f1 f2 r w) = BoolOpt d (modify f1) (modify f2) r w installOptions :: ShowOrParseArgs -> [OptionField InstallFlags] installOptions showOrParseArgs = [ option "" ["documentation"] "building of documentation" installDocumentation (\v flags -> flags { installDocumentation = v }) (boolOpt [] []) , option [] ["doc-index-file"] "A central index of haddock API documentation (template cannot use $pkgid)" installHaddockIndex (\v flags -> flags { installHaddockIndex = v }) (reqArg' "TEMPLATE" (toFlag.toPathTemplate) (flagToList . fmap fromPathTemplate)) , option [] ["dry-run"] "Do not install anything, only print what would be installed." installDryRun (\v flags -> flags { installDryRun = v }) trueArg , option "" ["target-package-db"] "package database to install into. Required when using ${pkgroot} prefix." installDest (\v flags -> flags { installDest = v }) (reqArg "DATABASE" (succeedReadE (Flag . Cabal.CopyToDb)) (\f -> case f of Flag (Cabal.CopyToDb p) -> [p]; _ -> [])) ] ++ optionSolverFlags showOrParseArgs installMaxBackjumps (\v flags -> flags { installMaxBackjumps = v }) installReorderGoals (\v flags -> flags { installReorderGoals = v }) installCountConflicts (\v flags -> flags { installCountConflicts = v }) installFineGrainedConflicts (\v flags -> flags { installFineGrainedConflicts = v }) installMinimizeConflictSet (\v flags -> flags { installMinimizeConflictSet = v }) installIndependentGoals (\v flags -> flags { installIndependentGoals = v }) installShadowPkgs (\v flags -> flags { installShadowPkgs = v }) installStrongFlags (\v flags -> flags { installStrongFlags = v }) installAllowBootLibInstalls (\v flags -> flags { installAllowBootLibInstalls = v }) installOnlyConstrained (\v flags -> flags { installOnlyConstrained = v }) ++ [ option [] ["reinstall"] "Install even if it means installing the same version again." installReinstall (\v flags -> flags { installReinstall = v }) (yesNoOpt showOrParseArgs) , option [] ["avoid-reinstalls"] "Do not select versions that would destructively overwrite installed packages." (fmap asBool . installAvoidReinstalls) (\v flags -> flags { installAvoidReinstalls = fmap AvoidReinstalls v }) (yesNoOpt showOrParseArgs) , option [] ["force-reinstalls"] "Reinstall packages even if they will most likely break other installed packages." installOverrideReinstall (\v flags -> flags { installOverrideReinstall = v }) (yesNoOpt showOrParseArgs) , option [] ["upgrade-dependencies"] "Pick the latest version for all dependencies, rather than trying to pick an installed version." installUpgradeDeps (\v flags -> flags { installUpgradeDeps = v }) (yesNoOpt showOrParseArgs) , option [] ["only-dependencies"] "Install only the dependencies necessary to build the given packages" installOnlyDeps (\v flags -> flags { installOnlyDeps = v }) (yesNoOpt showOrParseArgs) , option [] ["dependencies-only"] "A synonym for --only-dependencies" installOnlyDeps (\v flags -> flags { installOnlyDeps = v }) (yesNoOpt showOrParseArgs) , option [] ["index-state"] ("Use source package index state as it existed at a previous time. " ++ "Accepts unix-timestamps (e.g. '@1474732068'), ISO8601 UTC timestamps " ++ "(e.g. '2016-09-24T17:47:48Z'), or 'HEAD' (default: 'HEAD').") installIndexState (\v flags -> flags { installIndexState = v }) (reqArg "STATE" (parsecToReadE (const $ "index-state must be a " ++ "unix-timestamps (e.g. '@1474732068'), " ++ "a ISO8601 UTC timestamp " ++ "(e.g. '2016-09-24T17:47:48Z'), or 'HEAD'") (toFlag `fmap` parsec)) (flagToList . fmap prettyShow)) , option [] ["root-cmd"] "(No longer supported, do not use.)" installRootCmd (\v flags -> flags { installRootCmd = v }) (reqArg' "COMMAND" toFlag flagToList) , option [] ["symlink-bindir"] "Add symlinks to installed executables into this directory." installSymlinkBinDir (\v flags -> flags { installSymlinkBinDir = v }) (reqArgFlag "DIR") , option [] ["build-summary"] "Save build summaries to file (name template can use $pkgid, $compiler, $os, $arch)" installSummaryFile (\v flags -> flags { installSummaryFile = v }) (reqArg' "TEMPLATE" (\x -> toNubList [toPathTemplate x]) (map fromPathTemplate . fromNubList)) , option [] ["build-log"] "Log all builds to file (name template can use $pkgid, $compiler, $os, $arch)" installLogFile (\v flags -> flags { installLogFile = v }) (reqArg' "TEMPLATE" (toFlag.toPathTemplate) (flagToList . fmap fromPathTemplate)) , option [] ["remote-build-reporting"] "Generate build reports to send to a remote server (none, anonymous or detailed)." installBuildReports (\v flags -> flags { installBuildReports = v }) (reqArg "LEVEL" (parsecToReadE (const $ "report level must be 'none', " ++ "'anonymous' or 'detailed'") (toFlag `fmap` parsec)) (flagToList . fmap prettyShow)) , option [] ["report-planning-failure"] "Generate build reports when the dependency solver fails. This is used by the Hackage build bot." installReportPlanningFailure (\v flags -> flags { installReportPlanningFailure = v }) trueArg , option "" ["per-component"] "Per-component builds when possible" installPerComponent (\v flags -> flags { installPerComponent = v }) (boolOpt [] []) , option [] ["one-shot"] "Do not record the packages in the world file." installOneShot (\v flags -> flags { installOneShot = v }) (yesNoOpt showOrParseArgs) , option [] ["run-tests"] "Run package test suites during installation." installRunTests (\v flags -> flags { installRunTests = v }) trueArg , optionNumJobs installNumJobs (\v flags -> flags { installNumJobs = v }) , option [] ["keep-going"] "After a build failure, continue to build other unaffected packages." installKeepGoing (\v flags -> flags { installKeepGoing = v }) trueArg , option [] ["offline"] "Don't download packages from the Internet." installOfflineMode (\v flags -> flags { installOfflineMode = v }) (yesNoOpt showOrParseArgs) ] ++ case showOrParseArgs of -- TODO: remove when "cabal install" -- avoids ParseArgs -> [ option [] ["only"] "Only installs the package in the current directory." installOnly (\v flags -> flags { installOnly = v }) trueArg ] _ -> [] instance Monoid InstallFlags where mempty = gmempty mappend = (<>) instance Semigroup InstallFlags where (<>) = gmappend -- ------------------------------------------------------------ -- * Upload flags -- ------------------------------------------------------------ -- | Is this a candidate package or a package to be published? data IsCandidate = IsCandidate | IsPublished deriving Eq data UploadFlags = UploadFlags { uploadCandidate :: Flag IsCandidate, uploadDoc :: Flag Bool, uploadUsername :: Flag Username, uploadPassword :: Flag Password, uploadPasswordCmd :: Flag [String], uploadVerbosity :: Flag Verbosity } deriving Generic defaultUploadFlags :: UploadFlags defaultUploadFlags = UploadFlags { uploadCandidate = toFlag IsCandidate, uploadDoc = toFlag False, uploadUsername = mempty, uploadPassword = mempty, uploadPasswordCmd = mempty, uploadVerbosity = toFlag normal } uploadCommand :: CommandUI UploadFlags uploadCommand = CommandUI { commandName = "upload", commandSynopsis = "Uploads source packages or documentation to Hackage.", commandDescription = Nothing, commandNotes = Just $ \_ -> "You can store your Hackage login in the ~/.cabal/config file\n" ++ relevantConfigValuesText ["username", "password", "password-command"], commandUsage = \pname -> "Usage: " ++ pname ++ " upload [FLAGS] TARFILES\n", commandDefaultFlags = defaultUploadFlags, commandOptions = \_ -> [optionVerbosity uploadVerbosity (\v flags -> flags { uploadVerbosity = v }) ,option [] ["publish"] "Publish the package instead of uploading it as a candidate." uploadCandidate (\v flags -> flags { uploadCandidate = v }) (noArg (Flag IsPublished)) ,option ['d'] ["documentation"] ("Upload documentation instead of a source package. " ++ "By default, this uploads documentation for a package candidate. " ++ "To upload documentation for " ++ "a published package, combine with --publish.") uploadDoc (\v flags -> flags { uploadDoc = v }) trueArg ,option ['u'] ["username"] "Hackage username." uploadUsername (\v flags -> flags { uploadUsername = v }) (reqArg' "USERNAME" (toFlag . Username) (flagToList . fmap unUsername)) ,option ['p'] ["password"] "Hackage password." uploadPassword (\v flags -> flags { uploadPassword = v }) (reqArg' "PASSWORD" (toFlag . Password) (flagToList . fmap unPassword)) ,option ['P'] ["password-command"] "Command to get Hackage password." uploadPasswordCmd (\v flags -> flags { uploadPasswordCmd = v }) (reqArg' "PASSWORD" (Flag . words) (fromMaybe [] . flagToMaybe)) ] } instance Monoid UploadFlags where mempty = gmempty mappend = (<>) instance Semigroup UploadFlags where (<>) = gmappend -- ------------------------------------------------------------ -- * Init flags -- ------------------------------------------------------------ initCommand :: CommandUI IT.InitFlags initCommand = CommandUI { commandName = "init", commandSynopsis = "Create a new .cabal package file.", commandDescription = Just $ \_ -> wrapText $ "Create a .cabal, Setup.hs, and optionally a LICENSE file.\n" ++ "\n" ++ "Calling init with no arguments creates an executable, " ++ "guessing as many options as possible. The interactive " ++ "mode can be invoked by the -i/--interactive flag, which " ++ "will try to guess as much as possible and prompt you for " ++ "the rest. You can change init to always be interactive by " ++ "setting the interactive flag in your configuration file. " ++ "Command-line arguments are provided for scripting purposes.\n", commandNotes = Nothing, commandUsage = \pname -> "Usage: " ++ pname ++ " init [FLAGS]\n", commandDefaultFlags = IT.defaultInitFlags, commandOptions = initOptions } initOptions :: ShowOrParseArgs -> [OptionField IT.InitFlags] initOptions _ = [ option ['i'] ["interactive"] "interactive mode." IT.interactive (\v flags -> flags { IT.interactive = v }) (boolOpt' (['i'], ["interactive"]) (['n'], ["non-interactive"])) , option ['q'] ["quiet"] "Do not generate log messages to stdout." IT.quiet (\v flags -> flags { IT.quiet = v }) trueArg , option [] ["no-comments"] "Do not generate explanatory comments in the .cabal file." IT.noComments (\v flags -> flags { IT.noComments = v }) trueArg , option ['m'] ["minimal"] "Generate a minimal .cabal file, that is, do not include extra empty fields. Also implies --no-comments." IT.minimal (\v flags -> flags { IT.minimal = v }) trueArg , option [] ["overwrite"] "Overwrite any existing .cabal, LICENSE, or Setup.hs files without warning." IT.overwrite (\v flags -> flags { IT.overwrite = v }) trueArg , option [] ["package-dir", "packagedir"] "Root directory of the package (default = current directory)." IT.packageDir (\v flags -> flags { IT.packageDir = v }) (reqArgFlag "DIRECTORY") , option ['p'] ["package-name"] "Name of the Cabal package to create." IT.packageName (\v flags -> flags { IT.packageName = v }) (reqArg "PACKAGE" (parsecToReadE ("Cannot parse package name: "++) (toFlag `fmap` parsec)) (flagToList . fmap prettyShow)) , option [] ["version"] "Initial version of the package." IT.version (\v flags -> flags { IT.version = v }) (reqArg "VERSION" (parsecToReadE ("Cannot parse package version: "++) (toFlag `fmap` parsec)) (flagToList . fmap prettyShow)) , option [] ["cabal-version"] "Version of the Cabal specification." IT.cabalVersion (\v flags -> flags { IT.cabalVersion = v }) (reqArg "CABALSPECVERSION" (parsecToReadE ("Cannot parse Cabal specification version: "++) (fmap (toFlag . getSpecVersion) parsec)) (flagToList . fmap (prettyShow . SpecVersion))) , option ['l'] ["license"] "Project license." IT.license (\v flags -> flags { IT.license = v }) (reqArg "LICENSE" (parsecToReadE ("Cannot parse license: "++) (toFlag `fmap` parsec)) (flagToList . fmap prettyShow)) , option ['a'] ["author"] "Name of the project's author." IT.author (\v flags -> flags { IT.author = v }) (reqArgFlag "NAME") , option ['e'] ["email"] "Email address of the maintainer." IT.email (\v flags -> flags { IT.email = v }) (reqArgFlag "EMAIL") , option ['u'] ["homepage"] "Project homepage and/or repository." IT.homepage (\v flags -> flags { IT.homepage = v }) (reqArgFlag "URL") , option ['s'] ["synopsis"] "Short project synopsis." IT.synopsis (\v flags -> flags { IT.synopsis = v }) (reqArgFlag "TEXT") , option ['c'] ["category"] "Project category." IT.category (\v flags -> flags { IT.category = v }) (reqArg' "CATEGORY" (\s -> toFlag $ maybe (Left s) Right (readMaybe s)) (flagToList . fmap (either id show))) , option ['x'] ["extra-source-file"] "Extra source file to be distributed with tarball." IT.extraSrc (\v flags -> flags { IT.extraSrc = v }) (reqArg' "FILE" (Just . (:[])) (fromMaybe [])) , option [] ["lib", "is-library"] "Build a library." IT.packageType (\v flags -> flags { IT.packageType = v }) (noArg (Flag IT.Library)) , option [] ["exe", "is-executable"] "Build an executable." IT.packageType (\v flags -> flags { IT.packageType = v }) (noArg (Flag IT.Executable)) , option [] ["libandexe", "is-libandexe"] "Build a library and an executable." IT.packageType (\v flags -> flags { IT.packageType = v }) (noArg (Flag IT.LibraryAndExecutable)) , option [] ["tests"] "Generate a test suite for the library." IT.initializeTestSuite (\v flags -> flags { IT.initializeTestSuite = v }) trueArg , option [] ["test-dir"] "Directory containing tests." IT.testDirs (\v flags -> flags { IT.testDirs = v }) (reqArg' "DIR" (Just . (:[])) (fromMaybe [])) , option [] ["simple"] "Create a simple project with sensible defaults." IT.simpleProject (\v flags -> flags { IT.simpleProject = v }) trueArg , option [] ["main-is"] "Specify the main module." IT.mainIs (\v flags -> flags { IT.mainIs = v }) (reqArgFlag "FILE") , option [] ["language"] "Specify the default language." IT.language (\v flags -> flags { IT.language = v }) (reqArg "LANGUAGE" (parsecToReadE ("Cannot parse language: "++) (toFlag `fmap` parsec)) (flagToList . fmap prettyShow)) , option ['o'] ["expose-module"] "Export a module from the package." IT.exposedModules (\v flags -> flags { IT.exposedModules = v }) (reqArg "MODULE" (parsecToReadE ("Cannot parse module name: "++) ((Just . (:[])) `fmap` parsec)) (maybe [] (fmap prettyShow))) , option [] ["extension"] "Use a LANGUAGE extension (in the other-extensions field)." IT.otherExts (\v flags -> flags { IT.otherExts = v }) (reqArg "EXTENSION" (parsecToReadE ("Cannot parse extension: "++) ((Just . (:[])) `fmap` parsec)) (maybe [] (fmap prettyShow))) , option ['d'] ["dependency"] "Package dependency." IT.dependencies (\v flags -> flags { IT.dependencies = v }) (reqArg "PACKAGE" (parsecToReadE ("Cannot parse dependency: "++) ((Just . (:[])) `fmap` parsec)) (maybe [] (fmap prettyShow))) , option [] ["application-dir"] "Directory containing package application executable." IT.applicationDirs (\v flags -> flags { IT.applicationDirs = v}) (reqArg' "DIR" (Just . (:[])) (fromMaybe [])) , option [] ["source-dir", "sourcedir"] "Directory containing package library source." IT.sourceDirs (\v flags -> flags { IT.sourceDirs = v }) (reqArg' "DIR" (Just . (:[])) (fromMaybe [])) , option [] ["build-tool"] "Required external build tool." IT.buildTools (\v flags -> flags { IT.buildTools = v }) (reqArg' "TOOL" (Just . (:[])) (fromMaybe [])) -- NB: this is a bit of a transitional hack and will likely be -- removed again if `cabal init` is migrated to the v2-* command -- framework , option "w" ["with-compiler"] "give the path to a particular compiler" IT.initHcPath (\v flags -> flags { IT.initHcPath = v }) (reqArgFlag "PATH") , optionVerbosity IT.initVerbosity (\v flags -> flags { IT.initVerbosity = v }) ] -- ------------------------------------------------------------ -- * SDist flags -- ------------------------------------------------------------ doctestCommand :: CommandUI DoctestFlags doctestCommand = Cabal.doctestCommand { commandUsage = \pname -> "Usage: " ++ pname ++ " v1-doctest [FLAGS]\n" } copyCommand :: CommandUI CopyFlags copyCommand = Cabal.copyCommand { commandNotes = Just $ \pname -> "Examples:\n" ++ " " ++ pname ++ " v1-copy " ++ " All the components in the package\n" ++ " " ++ pname ++ " v1-copy foo " ++ " A component (i.e. lib, exe, test suite)" , commandUsage = usageAlternatives "v1-copy" $ [ "[FLAGS]" , "COMPONENTS [FLAGS]" ] } registerCommand :: CommandUI RegisterFlags registerCommand = Cabal.registerCommand { commandUsage = \pname -> "Usage: " ++ pname ++ " v1-register [FLAGS]\n" } -- ------------------------------------------------------------ -- * ActAsSetup flags -- ------------------------------------------------------------ data ActAsSetupFlags = ActAsSetupFlags { actAsSetupBuildType :: Flag BuildType } deriving Generic defaultActAsSetupFlags :: ActAsSetupFlags defaultActAsSetupFlags = ActAsSetupFlags { actAsSetupBuildType = toFlag Simple } actAsSetupCommand :: CommandUI ActAsSetupFlags actAsSetupCommand = CommandUI { commandName = "act-as-setup", commandSynopsis = "Run as-if this was a Setup.hs", commandDescription = Nothing, commandNotes = Nothing, commandUsage = \pname -> "Usage: " ++ pname ++ " act-as-setup\n", commandDefaultFlags = defaultActAsSetupFlags, commandOptions = \_ -> [option "" ["build-type"] "Use the given build type." actAsSetupBuildType (\v flags -> flags { actAsSetupBuildType = v }) (reqArg "BUILD-TYPE" (parsecToReadE ("Cannot parse build type: "++) (fmap toFlag parsec)) (map prettyShow . flagToList)) ] } instance Monoid ActAsSetupFlags where mempty = gmempty mappend = (<>) instance Semigroup ActAsSetupFlags where (<>) = gmappend -- ------------------------------------------------------------ -- * Exec Flags -- ------------------------------------------------------------ data ExecFlags = ExecFlags { execVerbosity :: Flag Verbosity, execDistPref :: Flag FilePath } deriving Generic defaultExecFlags :: ExecFlags defaultExecFlags = ExecFlags { execVerbosity = toFlag normal, execDistPref = NoFlag } execCommand :: CommandUI ExecFlags execCommand = CommandUI { commandName = "exec", commandSynopsis = "Give a command access to the sandbox package repository.", commandDescription = Just $ \pname -> wrapText $ -- TODO: this is too GHC-focused for my liking.. "A directly invoked GHC will not automatically be aware of any" ++ " sandboxes: the GHC_PACKAGE_PATH environment variable controls what" ++ " GHC uses. `" ++ pname ++ " v1-exec` can be used to modify this variable:" ++ " COMMAND will be executed in a modified environment and thereby uses" ++ " the sandbox package database.\n" ++ "\n" ++ "If there is no sandbox, behaves as identity (executing COMMAND).\n" ++ "\n" ++ "Note that other " ++ pname ++ " commands change the environment" ++ " variable appropriately already, so there is no need to wrap those" ++ " in `" ++ pname ++ " v1-exec`. But with `" ++ pname ++ " v1-exec`, the user" ++ " has more control and can, for example, execute custom scripts which" ++ " indirectly execute GHC.\n" ++ "\n" ++ "Note that `" ++ pname ++ " v1-repl` is different from `" ++ pname ++ " v1-exec -- ghci` as the latter will not forward any additional flags" ++ " being defined in the local package to ghci.\n" ++ "\n" ++ "See `" ++ pname ++ " sandbox`.\n", commandNotes = Just $ \pname -> "Examples:\n" ++ " " ++ pname ++ " v1-exec -- ghci -Wall\n" ++ " Start a repl session with sandbox packages and all warnings;\n" ++ " " ++ pname ++ " v1-exec gitit -- -f gitit.cnf\n" ++ " Give gitit access to the sandbox packages, and pass it a flag;\n" ++ " " ++ pname ++ " v1-exec runghc Foo.hs\n" ++ " Execute runghc on Foo.hs with runghc configured to use the\n" ++ " sandbox package database (if a sandbox is being used).\n", commandUsage = \pname -> "Usage: " ++ pname ++ " v1-exec [FLAGS] [--] COMMAND [--] [ARGS]\n", commandDefaultFlags = defaultExecFlags, commandOptions = \showOrParseArgs -> [ optionVerbosity execVerbosity (\v flags -> flags { execVerbosity = v }) , Cabal.optionDistPref execDistPref (\d flags -> flags { execDistPref = d }) showOrParseArgs ] } instance Monoid ExecFlags where mempty = gmempty mappend = (<>) instance Semigroup ExecFlags where (<>) = gmappend -- ------------------------------------------------------------ -- * UserConfig flags -- ------------------------------------------------------------ data UserConfigFlags = UserConfigFlags { userConfigVerbosity :: Flag Verbosity, userConfigForce :: Flag Bool, userConfigAppendLines :: Flag [String] } deriving Generic instance Monoid UserConfigFlags where mempty = UserConfigFlags { userConfigVerbosity = toFlag normal, userConfigForce = toFlag False, userConfigAppendLines = toFlag [] } mappend = (<>) instance Semigroup UserConfigFlags where (<>) = gmappend userConfigCommand :: CommandUI UserConfigFlags userConfigCommand = CommandUI { commandName = "user-config", commandSynopsis = "Display and update the user's global cabal configuration.", commandDescription = Just $ \_ -> wrapText $ "When upgrading cabal, the set of configuration keys and their default" ++ " values may change. This command provides means to merge the existing" ++ " config in ~/.cabal/config" ++ " (i.e. all bindings that are actually defined and not commented out)" ++ " and the default config of the new version.\n" ++ "\n" ++ "init: Creates a new config file at either ~/.cabal/config or as" ++ " specified by --config-file, if given. An existing file won't be " ++ " overwritten unless -f or --force is given.\n" ++ "diff: Shows a pseudo-diff of the user's ~/.cabal/config file and" ++ " the default configuration that would be created by cabal if the" ++ " config file did not exist.\n" ++ "update: Applies the pseudo-diff to the configuration that would be" ++ " created by default, and write the result back to ~/.cabal/config.", commandNotes = Nothing, commandUsage = usageAlternatives "user-config" ["init", "diff", "update"], commandDefaultFlags = mempty, commandOptions = \ _ -> [ optionVerbosity userConfigVerbosity (\v flags -> flags { userConfigVerbosity = v }) , option ['f'] ["force"] "Overwrite the config file if it already exists." userConfigForce (\v flags -> flags { userConfigForce = v }) trueArg , option ['a'] ["augment"] "Additional setting to augment the config file (replacing a previous setting if it existed)." userConfigAppendLines (\v flags -> flags {userConfigAppendLines = Flag $ concat (flagToList (userConfigAppendLines flags) ++ flagToList v)}) (reqArg' "CONFIGLINE" (Flag . (:[])) (fromMaybe [] . flagToMaybe)) ] } -- ------------------------------------------------------------ -- * GetOpt Utils -- ------------------------------------------------------------ reqArgFlag :: ArgPlaceHolder -> MkOptDescr (b -> Flag String) (Flag String -> b -> b) b reqArgFlag ad = reqArg ad (succeedReadE Flag) flagToList liftOptions :: (b -> a) -> (a -> b -> b) -> [OptionField a] -> [OptionField b] liftOptions get set = map (liftOption get set) yesNoOpt :: ShowOrParseArgs -> MkOptDescr (b -> Flag Bool) (Flag Bool -> b -> b) b yesNoOpt ShowArgs sf lf = trueArg sf lf yesNoOpt _ sf lf = Command.boolOpt' flagToMaybe Flag (sf, lf) ([], map ("no-" ++) lf) sf lf optionSolver :: (flags -> Flag PreSolver) -> (Flag PreSolver -> flags -> flags) -> OptionField flags optionSolver get set = option [] ["solver"] ("Select dependency solver to use (default: " ++ prettyShow defaultSolver ++ "). Choices: " ++ allSolvers ++ ".") get set (reqArg "SOLVER" (parsecToReadE (const $ "solver must be one of: " ++ allSolvers) (toFlag `fmap` parsec)) (flagToList . fmap prettyShow)) optionSolverFlags :: ShowOrParseArgs -> (flags -> Flag Int ) -> (Flag Int -> flags -> flags) -> (flags -> Flag ReorderGoals) -> (Flag ReorderGoals -> flags -> flags) -> (flags -> Flag CountConflicts) -> (Flag CountConflicts -> flags -> flags) -> (flags -> Flag FineGrainedConflicts) -> (Flag FineGrainedConflicts -> flags -> flags) -> (flags -> Flag MinimizeConflictSet) -> (Flag MinimizeConflictSet -> flags -> flags) -> (flags -> Flag IndependentGoals) -> (Flag IndependentGoals -> flags -> flags) -> (flags -> Flag ShadowPkgs) -> (Flag ShadowPkgs -> flags -> flags) -> (flags -> Flag StrongFlags) -> (Flag StrongFlags -> flags -> flags) -> (flags -> Flag AllowBootLibInstalls) -> (Flag AllowBootLibInstalls -> flags -> flags) -> (flags -> Flag OnlyConstrained) -> (Flag OnlyConstrained -> flags -> flags) -> [OptionField flags] optionSolverFlags showOrParseArgs getmbj setmbj getrg setrg getcc setcc getfgc setfgc getmc setmc getig setig getsip setsip getstrfl setstrfl getib setib getoc setoc = [ option [] ["max-backjumps"] ("Maximum number of backjumps allowed while solving (default: " ++ show defaultMaxBackjumps ++ "). Use a negative number to enable unlimited backtracking. Use 0 to disable backtracking completely.") getmbj setmbj (reqArg "NUM" (parsecToReadE ("Cannot parse number: "++) (fmap toFlag P.signedIntegral)) (map show . flagToList)) , option [] ["reorder-goals"] "Try to reorder goals according to certain heuristics. Slows things down on average, but may make backtracking faster for some packages." (fmap asBool . getrg) (setrg . fmap ReorderGoals) (yesNoOpt showOrParseArgs) , option [] ["count-conflicts"] "Try to speed up solving by preferring goals that are involved in a lot of conflicts (default)." (fmap asBool . getcc) (setcc . fmap CountConflicts) (yesNoOpt showOrParseArgs) , option [] ["fine-grained-conflicts"] "Skip a version of a package if it does not resolve the conflicts encountered in the last version, as a solver optimization (default)." (fmap asBool . getfgc) (setfgc . fmap FineGrainedConflicts) (yesNoOpt showOrParseArgs) , option [] ["minimize-conflict-set"] ("When there is no solution, try to improve the error message by finding " ++ "a minimal conflict set (default: false). May increase run time " ++ "significantly.") (fmap asBool . getmc) (setmc . fmap MinimizeConflictSet) (yesNoOpt showOrParseArgs) , option [] ["independent-goals"] "Treat several goals on the command line as independent. If several goals depend on the same package, different versions can be chosen." (fmap asBool . getig) (setig . fmap IndependentGoals) (yesNoOpt showOrParseArgs) , option [] ["shadow-installed-packages"] "If multiple package instances of the same version are installed, treat all but one as shadowed." (fmap asBool . getsip) (setsip . fmap ShadowPkgs) (yesNoOpt showOrParseArgs) , option [] ["strong-flags"] "Do not defer flag choices (this used to be the default in cabal-install <= 1.20)." (fmap asBool . getstrfl) (setstrfl . fmap StrongFlags) (yesNoOpt showOrParseArgs) , option [] ["allow-boot-library-installs"] "Allow cabal to install base, ghc-prim, integer-simple, integer-gmp, and template-haskell." (fmap asBool . getib) (setib . fmap AllowBootLibInstalls) (yesNoOpt showOrParseArgs) , option [] ["reject-unconstrained-dependencies"] "Require these packages to have constraints on them if they are to be selected (default: none)." getoc setoc (reqArg "none|all" (parsecToReadE (const "reject-unconstrained-dependencies must be 'none' or 'all'") (toFlag `fmap` parsec)) (flagToList . fmap prettyShow)) ] usagePackages :: String -> String -> String usagePackages name pname = "Usage: " ++ pname ++ " " ++ name ++ " [PACKAGES]\n" usageFlags :: String -> String -> String usageFlags name pname = "Usage: " ++ pname ++ " " ++ name ++ " [FLAGS]\n" --TODO: do we want to allow per-package flags? parsePackageArgs :: [String] -> Either String [PackageVersionConstraint] parsePackageArgs = traverse p where p arg = case eitherParsec arg of Right pvc -> Right pvc Left err -> Left $ show arg ++ " is not valid syntax for a package name or" ++ " package dependency. " ++ err showRemoteRepo :: RemoteRepo -> String showRemoteRepo = prettyShow readRemoteRepo :: String -> Maybe RemoteRepo readRemoteRepo = simpleParsec showLocalRepo :: LocalRepo -> String showLocalRepo = prettyShow readLocalRepo :: String -> Maybe LocalRepo readLocalRepo = simpleParsec -- ------------------------------------------------------------ -- * Helpers for Documentation -- ------------------------------------------------------------ relevantConfigValuesText :: [String] -> String relevantConfigValuesText vs = "Relevant global configuration keys:\n" ++ concat [" " ++ v ++ "\n" |v <- vs]