-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
{-# LANGUAGE RecordWildCards #-}

-- |
-- Module      :  Distribution.Client.Exec
-- Maintainer  :  cabal-devel@haskell.org
-- Portability :  portable
--
-- Implementation of the 'v2-exec' command for running an arbitrary executable
-- in an environment suited to the part of the store built for a project.
module Distribution.Client.CmdExec
  ( execAction
  , execCommand
  ) where

import Distribution.Client.DistDirLayout
  ( DistDirLayout (..)
  )
import Distribution.Client.InstallPlan
  ( GenericPlanPackage (..)
  , toGraph
  )
import Distribution.Client.NixStyleOptions
  ( NixStyleFlags (..)
  , defaultNixStyleFlags
  , nixStyleOptions
  )
import Distribution.Client.ProjectConfig.Types
  ( ProjectConfig (projectConfigShared)
  , ProjectConfigShared (projectConfigProgPathExtra)
  )
import Distribution.Client.ProjectFlags
  ( removeIgnoreProjectOption
  )
import Distribution.Client.ProjectOrchestration
  ( BuildTimeSettings (..)
  , CurrentCommand (..)
  , ProjectBaseContext (..)
  , ProjectBuildContext (..)
  , commandLineFlagsToProjectConfig
  , distDirLayout
  , establishProjectBaseContext
  , runProjectPreBuildPhase
  )
import Distribution.Client.ProjectPlanOutput
  ( PostBuildProjectStatus
  , argsEquivalentOfGhcEnvironmentFile
  , createPackageEnvironment
  , updatePostBuildProjectStatus
  )
import Distribution.Client.ProjectPlanning
  ( ElaboratedInstallPlan
  , ElaboratedSharedConfig (..)
  )
import qualified Distribution.Client.ProjectPlanning as Planning
import Distribution.Client.ProjectPlanning.Types
  ( dataDirsEnvironmentForPlan
  )
import Distribution.Client.Setup
  ( ConfigFlags (configCommonFlags)
  , GlobalFlags
  )
import Distribution.Simple.Command
  ( CommandUI (..)
  )
import Distribution.Simple.Flag
  ( fromFlagOrDefault
  )
import Distribution.Simple.GHC
  ( GhcImplInfo (supportsPkgEnvFiles)
  , getImplInfo
  )
import Distribution.Simple.Program
  ( ConfiguredProgram
  , programDefaultArgs
  , programOverrideEnv
  , programPath
  , simpleProgram
  )
import Distribution.Simple.Program.Db
  ( configuredPrograms
  , prependProgramSearchPath
  , requireProgram
  )
import Distribution.Simple.Program.Run
  ( programInvocation
  , runProgramInvocation
  )
import Distribution.Simple.Setup (CommonSetupFlags (..))
import Distribution.Simple.Utils
  ( createDirectoryIfMissingVerbose
  , dieWithException
  , notice
  , withTempDirectory
  , wrapText
  )
import Distribution.Utils.NubList
  ( fromNubList
  )
import Distribution.Verbosity
  ( normal
  )

import Distribution.Client.Compat.Prelude
import Prelude ()

import qualified Data.Map as M
import qualified Data.Set as S
import Distribution.Client.Errors

execCommand :: CommandUI (NixStyleFlags ())
execCommand :: CommandUI (NixStyleFlags ())
execCommand =
  CommandUI
    { commandName :: FilePath
commandName = FilePath
"v2-exec"
    , commandSynopsis :: FilePath
commandSynopsis = FilePath
"Give a command access to the store."
    , commandUsage :: FilePath -> FilePath
commandUsage = \FilePath
pname ->
        FilePath
"Usage: " FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
pname FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
" v2-exec [FLAGS] [--] COMMAND [--] [ARGS]\n"
    , commandDescription :: Maybe (FilePath -> FilePath)
commandDescription = (FilePath -> FilePath) -> Maybe (FilePath -> FilePath)
forall a. a -> Maybe a
Just ((FilePath -> FilePath) -> Maybe (FilePath -> FilePath))
-> (FilePath -> FilePath) -> Maybe (FilePath -> FilePath)
forall a b. (a -> b) -> a -> b
$ \FilePath
pname ->
        FilePath -> FilePath
wrapText (FilePath -> FilePath) -> FilePath -> FilePath
forall a b. (a -> b) -> a -> b
$
          FilePath
"During development it is often useful to run build tasks and perform"
            FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
" one-off program executions to experiment with the behavior of build"
            FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
" tools. It is convenient to run these tools in the same way "
            FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
pname
            FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
" itself would. The `"
            FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
pname
            FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
" v2-exec` command provides a way to"
            FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
" do so.\n"
            FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
"\n"
            FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
"Compiler tools will be configured to see the same subset of the store"
            FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
" that builds would see. The PATH is modified to make all executables in"
            FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
" the dependency tree available (provided they have been built already)."
            FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
" Commands are also rewritten in the way cabal itself would. For"
            FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
" example, `"
            FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
pname
            FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
" v2-exec ghc` will consult the configuration"
            FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
" to choose an appropriate version of ghc and to include any"
            FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
" ghc-specific flags requested."
    , commandNotes :: Maybe (FilePath -> FilePath)
commandNotes = Maybe (FilePath -> FilePath)
forall a. Maybe a
Nothing
    , commandOptions :: ShowOrParseArgs -> [OptionField (NixStyleFlags ())]
commandOptions =
        [OptionField (NixStyleFlags ())]
-> [OptionField (NixStyleFlags ())]
forall a. [OptionField a] -> [OptionField a]
removeIgnoreProjectOption
          ([OptionField (NixStyleFlags ())]
 -> [OptionField (NixStyleFlags ())])
-> (ShowOrParseArgs -> [OptionField (NixStyleFlags ())])
-> ShowOrParseArgs
-> [OptionField (NixStyleFlags ())]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ShowOrParseArgs -> [OptionField ()])
-> ShowOrParseArgs -> [OptionField (NixStyleFlags ())]
forall a.
(ShowOrParseArgs -> [OptionField a])
-> ShowOrParseArgs -> [OptionField (NixStyleFlags a)]
nixStyleOptions ([OptionField ()] -> ShowOrParseArgs -> [OptionField ()]
forall a b. a -> b -> a
const [])
    , commandDefaultFlags :: NixStyleFlags ()
commandDefaultFlags = () -> NixStyleFlags ()
forall a. a -> NixStyleFlags a
defaultNixStyleFlags ()
    }

execAction :: NixStyleFlags () -> [String] -> GlobalFlags -> IO ()
execAction :: NixStyleFlags () -> [FilePath] -> GlobalFlags -> IO ()
execAction flags :: NixStyleFlags ()
flags@NixStyleFlags{()
TestFlags
HaddockFlags
ConfigFlags
BenchmarkFlags
ProjectFlags
InstallFlags
ConfigExFlags
configFlags :: ConfigFlags
configExFlags :: ConfigExFlags
installFlags :: InstallFlags
haddockFlags :: HaddockFlags
testFlags :: TestFlags
benchmarkFlags :: BenchmarkFlags
projectFlags :: ProjectFlags
extraFlags :: ()
configFlags :: forall a. NixStyleFlags a -> ConfigFlags
configExFlags :: forall a. NixStyleFlags a -> ConfigExFlags
installFlags :: forall a. NixStyleFlags a -> InstallFlags
haddockFlags :: forall a. NixStyleFlags a -> HaddockFlags
testFlags :: forall a. NixStyleFlags a -> TestFlags
benchmarkFlags :: forall a. NixStyleFlags a -> BenchmarkFlags
projectFlags :: forall a. NixStyleFlags a -> ProjectFlags
extraFlags :: forall a. NixStyleFlags a -> a
..} [FilePath]
extraArgs GlobalFlags
globalFlags = do
  ProjectBaseContext
baseCtx <- Verbosity
-> ProjectConfig -> CurrentCommand -> IO ProjectBaseContext
establishProjectBaseContext Verbosity
verbosity ProjectConfig
cliConfig CurrentCommand
OtherCommand

  -- To set up the environment, we'd like to select the libraries in our
  -- dependency tree that we've already built. So first we set up an install
  -- plan, but we walk the dependency tree without first executing the plan.
  ProjectBuildContext
buildCtx <-
    Verbosity
-> ProjectBaseContext
-> (ElaboratedInstallPlan
    -> IO (ElaboratedInstallPlan, TargetsMap))
-> IO ProjectBuildContext
runProjectPreBuildPhase
      Verbosity
verbosity
      ProjectBaseContext
baseCtx
      (\ElaboratedInstallPlan
plan -> (ElaboratedInstallPlan, TargetsMap)
-> IO (ElaboratedInstallPlan, TargetsMap)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (ElaboratedInstallPlan
plan, TargetsMap
forall k a. Map k a
M.empty))

  -- We use the build status below to decide what libraries to include in the
  -- compiler environment, but we don't want to actually build anything. So we
  -- pass mempty to indicate that nothing happened and we just want the current
  -- status.
  PostBuildProjectStatus
buildStatus <-
    Verbosity
-> DistDirLayout
-> ElaboratedInstallPlan
-> BuildStatusMap
-> BuildOutcomes
-> IO PostBuildProjectStatus
updatePostBuildProjectStatus
      Verbosity
verbosity
      (ProjectBaseContext -> DistDirLayout
distDirLayout ProjectBaseContext
baseCtx)
      (ProjectBuildContext -> ElaboratedInstallPlan
elaboratedPlanOriginal ProjectBuildContext
buildCtx)
      (ProjectBuildContext -> BuildStatusMap
pkgsBuildStatus ProjectBuildContext
buildCtx)
      BuildOutcomes
forall a. Monoid a => a
mempty

  -- Some dependencies may have executables. Let's put those on the PATH.
  let extraPaths :: [FilePath]
extraPaths = ProjectBaseContext -> ProjectBuildContext -> [FilePath]
pathAdditions ProjectBaseContext
baseCtx ProjectBuildContext
buildCtx
      pkgProgs :: ProgramDb
pkgProgs = ElaboratedSharedConfig -> ProgramDb
pkgConfigCompilerProgs (ProjectBuildContext -> ElaboratedSharedConfig
elaboratedShared ProjectBuildContext
buildCtx)
      extraEnvVars :: [(FilePath, Maybe FilePath)]
extraEnvVars =
        DistDirLayout
-> ElaboratedInstallPlan -> [(FilePath, Maybe FilePath)]
dataDirsEnvironmentForPlan
          (ProjectBaseContext -> DistDirLayout
distDirLayout ProjectBaseContext
baseCtx)
          (ProjectBuildContext -> ElaboratedInstallPlan
elaboratedPlanToExecute ProjectBuildContext
buildCtx)

  ProgramDb
programDb <-
    Verbosity
-> [FilePath]
-> [(FilePath, Maybe FilePath)]
-> ProgramDb
-> IO ProgramDb
prependProgramSearchPath Verbosity
verbosity [FilePath]
extraPaths [(FilePath, Maybe FilePath)]
extraEnvVars ProgramDb
pkgProgs

  -- Now that we have the packages, set up the environment. We accomplish this
  -- by creating an environment file that selects the databases and packages we
  -- computed in the previous step, and setting an environment variable to
  -- point at the file.
  -- In case ghc is too old to support environment files,
  -- we pass the same info as arguments
  let compiler :: Compiler
compiler = ElaboratedSharedConfig -> Compiler
pkgConfigCompiler (ElaboratedSharedConfig -> Compiler)
-> ElaboratedSharedConfig -> Compiler
forall a b. (a -> b) -> a -> b
$ ProjectBuildContext -> ElaboratedSharedConfig
elaboratedShared ProjectBuildContext
buildCtx
      envFilesSupported :: Bool
envFilesSupported = GhcImplInfo -> Bool
supportsPkgEnvFiles (Compiler -> GhcImplInfo
getImplInfo Compiler
compiler)
  case [FilePath]
extraArgs of
    [] -> Verbosity -> CabalInstallException -> IO ()
forall a1 a.
(HasCallStack, Show a1, Typeable a1,
 Exception (VerboseException a1)) =>
Verbosity -> a1 -> IO a
dieWithException Verbosity
verbosity CabalInstallException
SpecifyAnExecutable
    FilePath
exe : [FilePath]
args -> do
      (ConfiguredProgram
program, ProgramDb
_) <- Verbosity
-> Program -> ProgramDb -> IO (ConfiguredProgram, ProgramDb)
requireProgram Verbosity
verbosity (FilePath -> Program
simpleProgram FilePath
exe) ProgramDb
programDb
      let argOverrides :: [FilePath]
argOverrides =
            Compiler
-> DistDirLayout
-> ElaboratedInstallPlan
-> PostBuildProjectStatus
-> [FilePath]
argsEquivalentOfGhcEnvironmentFile
              Compiler
compiler
              (ProjectBaseContext -> DistDirLayout
distDirLayout ProjectBaseContext
baseCtx)
              (ProjectBuildContext -> ElaboratedInstallPlan
elaboratedPlanOriginal ProjectBuildContext
buildCtx)
              PostBuildProjectStatus
buildStatus
          programIsConfiguredCompiler :: Bool
programIsConfiguredCompiler =
            ElaboratedSharedConfig -> ConfiguredProgram -> Bool
matchCompilerPath
              (ProjectBuildContext -> ElaboratedSharedConfig
elaboratedShared ProjectBuildContext
buildCtx)
              ConfiguredProgram
program
          argOverrides' :: [FilePath]
argOverrides' =
            if Bool
envFilesSupported
              Bool -> Bool -> Bool
|| Bool -> Bool
not Bool
programIsConfiguredCompiler
              then []
              else [FilePath]
argOverrides

      ( if Bool
envFilesSupported
          then Verbosity
-> ProjectBaseContext
-> ProjectBuildContext
-> PostBuildProjectStatus
-> ([(FilePath, Maybe FilePath)] -> IO ())
-> IO ()
forall a.
Verbosity
-> ProjectBaseContext
-> ProjectBuildContext
-> PostBuildProjectStatus
-> ([(FilePath, Maybe FilePath)] -> IO a)
-> IO a
withTempEnvFile Verbosity
verbosity ProjectBaseContext
baseCtx ProjectBuildContext
buildCtx PostBuildProjectStatus
buildStatus
          else \[(FilePath, Maybe FilePath)] -> IO ()
f -> [(FilePath, Maybe FilePath)] -> IO ()
f []
        )
        (([(FilePath, Maybe FilePath)] -> IO ()) -> IO ())
-> ([(FilePath, Maybe FilePath)] -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \[(FilePath, Maybe FilePath)]
envOverrides -> do
          let program' :: ConfiguredProgram
program' =
                [(FilePath, Maybe FilePath)]
-> [FilePath] -> ConfiguredProgram -> ConfiguredProgram
withOverrides
                  [(FilePath, Maybe FilePath)]
envOverrides
                  [FilePath]
argOverrides'
                  ConfiguredProgram
program
              invocation :: ProgramInvocation
invocation = ConfiguredProgram -> [FilePath] -> ProgramInvocation
programInvocation ConfiguredProgram
program' [FilePath]
args
              dryRun :: Bool
dryRun =
                BuildTimeSettings -> Bool
buildSettingDryRun (ProjectBaseContext -> BuildTimeSettings
buildSettings ProjectBaseContext
baseCtx)
                  Bool -> Bool -> Bool
|| BuildTimeSettings -> Bool
buildSettingOnlyDownload (ProjectBaseContext -> BuildTimeSettings
buildSettings ProjectBaseContext
baseCtx)

          if Bool
dryRun
            then Verbosity -> FilePath -> IO ()
notice Verbosity
verbosity FilePath
"Running of executable suppressed by flag(s)"
            else Verbosity -> ProgramInvocation -> IO ()
runProgramInvocation Verbosity
verbosity ProgramInvocation
invocation
  where
    verbosity :: Verbosity
verbosity = Verbosity -> Flag Verbosity -> Verbosity
forall a. a -> Flag a -> a
fromFlagOrDefault Verbosity
normal (CommonSetupFlags -> Flag Verbosity
setupVerbosity (CommonSetupFlags -> Flag Verbosity)
-> CommonSetupFlags -> Flag Verbosity
forall a b. (a -> b) -> a -> b
$ ConfigFlags -> CommonSetupFlags
configCommonFlags ConfigFlags
configFlags)
    cliConfig :: ProjectConfig
cliConfig =
      GlobalFlags
-> NixStyleFlags () -> ClientInstallFlags -> ProjectConfig
forall a.
GlobalFlags
-> NixStyleFlags a -> ClientInstallFlags -> ProjectConfig
commandLineFlagsToProjectConfig
        GlobalFlags
globalFlags
        NixStyleFlags ()
flags
        ClientInstallFlags
forall a. Monoid a => a
mempty -- ClientInstallFlags, not needed here
    withOverrides :: [(FilePath, Maybe FilePath)]
-> [FilePath] -> ConfiguredProgram -> ConfiguredProgram
withOverrides [(FilePath, Maybe FilePath)]
env [FilePath]
args ConfiguredProgram
program =
      ConfiguredProgram
program
        { programOverrideEnv = programOverrideEnv program ++ env
        , programDefaultArgs = programDefaultArgs program ++ args
        }

matchCompilerPath :: ElaboratedSharedConfig -> ConfiguredProgram -> Bool
matchCompilerPath :: ElaboratedSharedConfig -> ConfiguredProgram -> Bool
matchCompilerPath ElaboratedSharedConfig
elaboratedShared ConfiguredProgram
program =
  ConfiguredProgram -> FilePath
programPath ConfiguredProgram
program
    FilePath -> [FilePath] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` (ConfiguredProgram -> FilePath
programPath (ConfiguredProgram -> FilePath)
-> [ConfiguredProgram] -> [FilePath]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [ConfiguredProgram]
configuredCompilers)
  where
    configuredCompilers :: [ConfiguredProgram]
configuredCompilers = ProgramDb -> [ConfiguredProgram]
configuredPrograms (ProgramDb -> [ConfiguredProgram])
-> ProgramDb -> [ConfiguredProgram]
forall a b. (a -> b) -> a -> b
$ ElaboratedSharedConfig -> ProgramDb
pkgConfigCompilerProgs ElaboratedSharedConfig
elaboratedShared

-- | Execute an action with a temporary .ghc.environment file reflecting the
-- current environment. The action takes an environment containing the env
-- variable which points ghc to the file.
withTempEnvFile
  :: Verbosity
  -> ProjectBaseContext
  -> ProjectBuildContext
  -> PostBuildProjectStatus
  -> ([(String, Maybe String)] -> IO a)
  -> IO a
withTempEnvFile :: forall a.
Verbosity
-> ProjectBaseContext
-> ProjectBuildContext
-> PostBuildProjectStatus
-> ([(FilePath, Maybe FilePath)] -> IO a)
-> IO a
withTempEnvFile Verbosity
verbosity ProjectBaseContext
baseCtx ProjectBuildContext
buildCtx PostBuildProjectStatus
buildStatus [(FilePath, Maybe FilePath)] -> IO a
action = do
  let tmpDirTemplate :: FilePath
tmpDirTemplate = DistDirLayout -> FilePath
distTempDirectory (ProjectBaseContext -> DistDirLayout
distDirLayout ProjectBaseContext
baseCtx)
  Verbosity -> Bool -> FilePath -> IO ()
createDirectoryIfMissingVerbose Verbosity
verbosity Bool
True FilePath
tmpDirTemplate
  Verbosity -> FilePath -> FilePath -> (FilePath -> IO a) -> IO a
forall a.
Verbosity -> FilePath -> FilePath -> (FilePath -> IO a) -> IO a
withTempDirectory
    Verbosity
verbosity
    FilePath
tmpDirTemplate
    FilePath
"environment."
    ( \FilePath
tmpDir -> do
        [(FilePath, Maybe FilePath)]
envOverrides <-
          Verbosity
-> FilePath
-> ElaboratedInstallPlan
-> ElaboratedSharedConfig
-> PostBuildProjectStatus
-> IO [(FilePath, Maybe FilePath)]
createPackageEnvironment
            Verbosity
verbosity
            FilePath
tmpDir
            (ProjectBuildContext -> ElaboratedInstallPlan
elaboratedPlanToExecute ProjectBuildContext
buildCtx)
            (ProjectBuildContext -> ElaboratedSharedConfig
elaboratedShared ProjectBuildContext
buildCtx)
            PostBuildProjectStatus
buildStatus
        [(FilePath, Maybe FilePath)] -> IO a
action [(FilePath, Maybe FilePath)]
envOverrides
    )

-- | Get paths to all dependency executables to be included in PATH.
pathAdditions :: ProjectBaseContext -> ProjectBuildContext -> [FilePath]
pathAdditions :: ProjectBaseContext -> ProjectBuildContext -> [FilePath]
pathAdditions ProjectBaseContext{[PackageSpecifier UnresolvedSourcePackage]
Maybe InstalledPackageIndex
BuildTimeSettings
ProjectConfig
CabalDirLayout
DistDirLayout
CurrentCommand
distDirLayout :: ProjectBaseContext -> DistDirLayout
buildSettings :: ProjectBaseContext -> BuildTimeSettings
distDirLayout :: DistDirLayout
cabalDirLayout :: CabalDirLayout
projectConfig :: ProjectConfig
localPackages :: [PackageSpecifier UnresolvedSourcePackage]
buildSettings :: BuildTimeSettings
currentCommand :: CurrentCommand
installedPackages :: Maybe InstalledPackageIndex
cabalDirLayout :: ProjectBaseContext -> CabalDirLayout
projectConfig :: ProjectBaseContext -> ProjectConfig
localPackages :: ProjectBaseContext -> [PackageSpecifier UnresolvedSourcePackage]
currentCommand :: ProjectBaseContext -> CurrentCommand
installedPackages :: ProjectBaseContext -> Maybe InstalledPackageIndex
..} ProjectBuildContext{TargetsMap
BuildStatusMap
ElaboratedInstallPlan
ElaboratedSharedConfig
elaboratedPlanOriginal :: ProjectBuildContext -> ElaboratedInstallPlan
pkgsBuildStatus :: ProjectBuildContext -> BuildStatusMap
elaboratedShared :: ProjectBuildContext -> ElaboratedSharedConfig
elaboratedPlanToExecute :: ProjectBuildContext -> ElaboratedInstallPlan
elaboratedPlanOriginal :: ElaboratedInstallPlan
elaboratedPlanToExecute :: ElaboratedInstallPlan
elaboratedShared :: ElaboratedSharedConfig
pkgsBuildStatus :: BuildStatusMap
targetsMap :: TargetsMap
targetsMap :: ProjectBuildContext -> TargetsMap
..} =
  [FilePath]
paths [FilePath] -> [FilePath] -> [FilePath]
forall a. [a] -> [a] -> [a]
++ [FilePath]
cabalConfigPaths
  where
    cabalConfigPaths :: [FilePath]
cabalConfigPaths =
      NubList FilePath -> [FilePath]
forall a. NubList a -> [a]
fromNubList
        (NubList FilePath -> [FilePath])
-> (ProjectConfig -> NubList FilePath)
-> ProjectConfig
-> [FilePath]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ProjectConfigShared -> NubList FilePath
projectConfigProgPathExtra
        (ProjectConfigShared -> NubList FilePath)
-> (ProjectConfig -> ProjectConfigShared)
-> ProjectConfig
-> NubList FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ProjectConfig -> ProjectConfigShared
projectConfigShared
        (ProjectConfig -> [FilePath]) -> ProjectConfig -> [FilePath]
forall a b. (a -> b) -> a -> b
$ ProjectConfig
projectConfig
    paths :: [FilePath]
paths =
      Set FilePath -> [FilePath]
forall a. Set a -> [a]
S.toList (Set FilePath -> [FilePath]) -> Set FilePath -> [FilePath]
forall a b. (a -> b) -> a -> b
$
        DistDirLayout
-> ElaboratedSharedConfig -> ElaboratedInstallPlan -> Set FilePath
binDirectories DistDirLayout
distDirLayout ElaboratedSharedConfig
elaboratedShared ElaboratedInstallPlan
elaboratedPlanToExecute

-- | Get paths to all dependency executables to be included in PATH.
binDirectories
  :: DistDirLayout
  -> ElaboratedSharedConfig
  -> ElaboratedInstallPlan
  -> Set FilePath
binDirectories :: DistDirLayout
-> ElaboratedSharedConfig -> ElaboratedInstallPlan -> Set FilePath
binDirectories DistDirLayout
layout ElaboratedSharedConfig
config = ElaboratedInstallPlan -> Set FilePath
forall {ipkg}.
GenericInstallPlan ipkg ElaboratedConfiguredPackage -> Set FilePath
fromElaboratedInstallPlan
  where
    fromElaboratedInstallPlan :: GenericInstallPlan ipkg ElaboratedConfiguredPackage -> Set FilePath
fromElaboratedInstallPlan = Graph (GenericPlanPackage ipkg ElaboratedConfiguredPackage)
-> Set FilePath
forall {ipkg}.
Graph (GenericPlanPackage ipkg ElaboratedConfiguredPackage)
-> Set FilePath
fromGraph (Graph (GenericPlanPackage ipkg ElaboratedConfiguredPackage)
 -> Set FilePath)
-> (GenericInstallPlan ipkg ElaboratedConfiguredPackage
    -> Graph (GenericPlanPackage ipkg ElaboratedConfiguredPackage))
-> GenericInstallPlan ipkg ElaboratedConfiguredPackage
-> Set FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. GenericInstallPlan ipkg ElaboratedConfiguredPackage
-> Graph (GenericPlanPackage ipkg ElaboratedConfiguredPackage)
forall ipkg srcpkg.
GenericInstallPlan ipkg srcpkg
-> Graph (GenericPlanPackage ipkg srcpkg)
toGraph
    fromGraph :: Graph (GenericPlanPackage ipkg ElaboratedConfiguredPackage)
-> Set FilePath
fromGraph = (GenericPlanPackage ipkg ElaboratedConfiguredPackage
 -> Set FilePath)
-> Graph (GenericPlanPackage ipkg ElaboratedConfiguredPackage)
-> Set FilePath
forall m a. Monoid m => (a -> m) -> Graph a -> m
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap GenericPlanPackage ipkg ElaboratedConfiguredPackage -> Set FilePath
forall {ipkg}.
GenericPlanPackage ipkg ElaboratedConfiguredPackage -> Set FilePath
fromPlan
    fromSrcPkg :: ElaboratedConfiguredPackage -> Set FilePath
fromSrcPkg = [FilePath] -> Set FilePath
forall a. Ord a => [a] -> Set a
S.fromList ([FilePath] -> Set FilePath)
-> (ElaboratedConfiguredPackage -> [FilePath])
-> ElaboratedConfiguredPackage
-> Set FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. DistDirLayout
-> ElaboratedSharedConfig
-> ElaboratedConfiguredPackage
-> [FilePath]
Planning.binDirectories DistDirLayout
layout ElaboratedSharedConfig
config

    fromPlan :: GenericPlanPackage ipkg ElaboratedConfiguredPackage -> Set FilePath
fromPlan (PreExisting ipkg
_) = Set FilePath
forall a. Monoid a => a
mempty
    fromPlan (Configured ElaboratedConfiguredPackage
pkg) = ElaboratedConfiguredPackage -> Set FilePath
fromSrcPkg ElaboratedConfiguredPackage
pkg
    fromPlan (Installed ElaboratedConfiguredPackage
pkg) = ElaboratedConfiguredPackage -> Set FilePath
fromSrcPkg ElaboratedConfiguredPackage
pkg