content: -- | etlas CLI command: build
  --
  module Distribution.Client.CmdBuild (
      -- * The @build@ CLI and action
      buildCommand,
      buildAction,
  
      -- * Internals exposed for testing
      TargetProblem(..),
      selectPackageTargets,
      selectComponentTarget
    ) where
  
  import Distribution.Client.ProjectOrchestration
  import Distribution.Client.CmdErrorMessages
  
  import Distribution.Client.Setup
           ( GlobalFlags, ConfigFlags(..), ConfigExFlags, InstallFlags )
  import qualified Distribution.Client.Setup as Client
  import Distribution.Simple.Setup
           ( HaddockFlags, fromFlagOrDefault )
  import Distribution.Simple.Command
           ( CommandUI(..), usageAlternatives )
  import Distribution.Verbosity
           ( Verbosity, normal )
  import Distribution.Simple.Utils
           ( wrapText, die' )
  
  import qualified Data.Map as Map
  
  
  buildCommand :: CommandUI (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags)
  buildCommand = Client.installCommand {
    commandName         = "build",
    commandSynopsis     = "Compile targets within the project.",
    commandUsage        = usageAlternatives "build" [ "[TARGETS] [FLAGS]" ],
    commandDescription  = Just $ \_ -> wrapText $
          "Build one or more targets from within the project. The available "
       ++ "targets are the packages in the project as well as individual "
       ++ "components within those packages, including libraries, executables, "
       ++ "test-suites or benchmarks. Targets can be specified by name or "
       ++ "location. If no target is specified then the default is to build "
       ++ "the package in the current directory.\n\n"
  
       ++ "Dependencies are built or rebuilt as necessary. Additional "
       ++ "configuration flags can be specified on the command line and these "
       ++ "extend the project configuration from the 'cabal.project', "
       ++ "'cabal.project.local' and other files.",
    commandNotes        = Just $ \pname ->
          "Examples:\n"
       ++ "  " ++ pname ++ " build\n"
       ++ "    Build the package in the current directory or all packages in the project\n"
       ++ "  " ++ pname ++ " build pkgname\n"
       ++ "    Build the package named pkgname in the project\n"
       ++ "  " ++ pname ++ " build ./pkgfoo\n"
       ++ "    Build the package in the ./pkgfoo directory\n"
       ++ "  " ++ pname ++ " build cname\n"
       ++ "    Build the component named cname in the project\n"
       ++ "  " ++ pname ++ " build cname --enable-profiling\n"
       ++ "    Build the component in profiling mode (including dependencies as needed)\n\n"
  
       ++ cmdCommonHelpTextNewBuildBeta
     }
  
  
  -- | The @build@ command does a lot. It brings the install plan up to date,
  -- selects that part of the plan needed by the given or implicit targets and
  -- then executes the plan.
  --
  -- For more details on how this works, see the module
  -- "Distribution.Client.ProjectOrchestration"
  --
  buildAction :: (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags)
              -> [String] -> GlobalFlags -> IO ()
  buildAction (configFlags, configExFlags, installFlags, haddockFlags)
              targetStrings globalFlags = do
  
      baseCtx <- establishProjectBaseContext verbosity cliConfig
  
      targetSelectors <- either (reportTargetSelectorProblems verbosity) return
                     =<< readTargetSelectors (localPackages baseCtx) Nothing targetStrings
  
      (buildCtx, _) <-
        runProjectPreBuildPhase verbosity baseCtx $ \elaboratedPlan -> do
  
              -- Interpret the targets on the command line as build targets
              -- (as opposed to say repl or haddock targets).
              targets <- either (reportTargetProblems verbosity) return
                       $ resolveTargets
                           selectPackageTargets
                           selectComponentTarget
                           TargetProblemCommon
                           elaboratedPlan
                           Nothing
                           targetSelectors
  
              let elaboratedPlan' = pruneInstallPlanToTargets
                                      TargetActionBuild
                                      targets
                                      elaboratedPlan
              elaboratedPlan'' <-
                if buildSettingOnlyDeps (buildSettings baseCtx)
                  then either (reportCannotPruneDependencies verbosity) return $
                       pruneInstallPlanToDependencies (Map.keysSet targets)
                                                      elaboratedPlan'
                  else return elaboratedPlan'
  
              return (elaboratedPlan'', targets, ())
  
      printPlan verbosity baseCtx buildCtx
  
      buildOutcomes <- runProjectBuildPhase verbosity baseCtx buildCtx
      runProjectPostBuildPhase verbosity baseCtx buildCtx buildOutcomes
    where
      verbosity = fromFlagOrDefault normal (configVerbosity configFlags)
      cliConfig = commandLineFlagsToProjectConfig
                    globalFlags configFlags configExFlags
                    installFlags haddockFlags
  
  -- | This defines what a 'TargetSelector' means for the @bench@ command.
  -- It selects the 'AvailableTarget's that the 'TargetSelector' refers to,
  -- or otherwise classifies the problem.
  --
  -- For the @build@ command select all components except non-buildable and disabled
  -- tests\/benchmarks, fail if there are no such components
  --
  selectPackageTargets :: TargetSelector
                       -> [AvailableTarget k] -> Either TargetProblem [k]
  selectPackageTargets targetSelector targets
  
      -- If there are any buildable targets then we select those
    | not (null targetsBuildable)
    = Right targetsBuildable
  
      -- If there are targets but none are buildable then we report those
    | not (null targets)
    = Left (TargetProblemNoneEnabled targetSelector targets')
  
      -- If there are no targets at all then we report that
    | otherwise
    = Left (TargetProblemNoTargets targetSelector)
    where
      targets'         = forgetTargetsDetail targets
      targetsBuildable = selectBuildableTargetsWith
                           (buildable targetSelector)
                           targets
  
      -- When there's a target filter like "pkg:tests" then we do select tests,
      -- but if it's just a target like "pkg" then we don't build tests unless
      -- they are requested by default (i.e. by using --enable-tests)
      buildable (TargetPackage _ _  Nothing) TargetNotRequestedByDefault = False
      buildable (TargetAllPackages  Nothing) TargetNotRequestedByDefault = False
      buildable _ _ = True
  
  -- | For a 'TargetComponent' 'TargetSelector', check if the component can be
  -- selected.
  --
  -- For the @build@ command we just need the basic checks on being buildable etc.
  --
  selectComponentTarget :: SubComponentTarget
                        -> AvailableTarget k -> Either TargetProblem k
  selectComponentTarget subtarget =
      either (Left . TargetProblemCommon) Right
    . selectComponentTargetBasic subtarget
  
  
  -- | The various error conditions that can occur when matching a
  -- 'TargetSelector' against 'AvailableTarget's for the @build@ command.
  --
  data TargetProblem =
       TargetProblemCommon       TargetProblemCommon
  
       -- | The 'TargetSelector' matches targets but none are buildable
     | TargetProblemNoneEnabled TargetSelector [AvailableTarget ()]
  
       -- | There are no targets at all
     | TargetProblemNoTargets   TargetSelector
    deriving (Eq, Show)
  
  reportTargetProblems :: Verbosity -> [TargetProblem] -> IO a
  reportTargetProblems verbosity =
      die' verbosity . unlines . map renderTargetProblem
  
  renderTargetProblem :: TargetProblem -> String
  renderTargetProblem (TargetProblemCommon problem) =
      renderTargetProblemCommon "build" problem
  renderTargetProblem (TargetProblemNoneEnabled targetSelector targets) =
      renderTargetProblemNoneEnabled "build" targetSelector targets
  renderTargetProblem(TargetProblemNoTargets targetSelector) =
      renderTargetProblemNoTargets "build" targetSelector
  
  reportCannotPruneDependencies :: Verbosity -> CannotPruneDependencies -> IO a
  reportCannotPruneDependencies verbosity =
      die' verbosity . renderCannotPruneDependencies
  