cabal-install-3.16.0.0: src/Distribution/Client/Run.hs
-----------------------------------------------------------------------------
{-# LANGUAGE DataKinds #-}
-----------------------------------------------------------------------------
-- |
-- Module : Distribution.Client.Run
-- Maintainer : cabal-devel@haskell.org
-- Portability : portable
--
-- Implementation of the 'run' command.
module Distribution.Client.Run (run, splitRunArgs)
where
import Distribution.Client.Compat.Prelude
import Prelude ()
import Distribution.Types.LocalBuildInfo (componentNameTargets')
import Distribution.Types.TargetInfo (targetCLBI)
import Distribution.Client.Utils (tryCanonicalizePath)
import Distribution.PackageDescription
( Benchmark (..)
, BuildInfo (buildable)
, Executable (..)
, PackageDescription (..)
, TestSuite (..)
)
import Distribution.Simple (PackageDBX (..))
import Distribution.Simple.Build (addInternalBuildTools)
import Distribution.Simple.BuildPaths (exeExtension)
import Distribution.Simple.Compiler (CompilerFlavor (..), compilerFlavor)
import Distribution.Simple.Flag (fromFlag)
import Distribution.Simple.LocalBuildInfo
( ComponentName (..)
, LocalBuildInfo (..)
, absoluteWorkingDirLBI
, buildDir
, depLibraryPaths
, interpretSymbolicPathLBI
, mbWorkDirLBI
)
import Distribution.Simple.Program.Db
import Distribution.Simple.Program.Find
import Distribution.Simple.Program.Run
import Distribution.Simple.Register (internalPackageDBPath)
import Distribution.Simple.Setup (ConfigFlags (..))
import Distribution.Simple.Utils
( addLibraryPath
, dieWithException
, notice
, rawSystemExitWithEnvCwd
, warn
)
import Distribution.System (Platform (..))
import Distribution.Types.UnqualComponentName
import qualified Distribution.Simple.GHCJS as GHCJS
import Distribution.Client.Errors
import Distribution.Utils.Path
-- | Return the executable to run and any extra arguments that should be
-- forwarded to it. Die in case of error.
splitRunArgs
:: Verbosity
-> LocalBuildInfo
-> [String]
-> IO (Executable, [String])
splitRunArgs verbosity lbi args =
case whichExecutable of -- Either err (wasManuallyChosen, exe, paramsRest)
Left err -> do
warn verbosity `traverse_` maybeWarning -- If there is a warning, print it.
dieWithException verbosity $ SplitRunArgs err
Right (True, exe, xs) -> return (exe, xs)
Right (False, exe, xs) -> do
let addition =
" Interpreting all parameters to `run` as a parameter to"
++ " the default executable."
-- If there is a warning, print it together with the addition.
warn verbosity `traverse_` fmap (++ addition) maybeWarning
return (exe, xs)
where
pkg_descr = localPkgDescr lbi
whichExecutable
:: Either
String -- Error string.
( Bool -- If it was manually chosen.
, Executable -- The executable.
, [String] -- The remaining parameters.
)
whichExecutable = case (enabledExes, args) of
([], _) -> Left "Couldn't find any enabled executables."
([exe], []) -> return (False, exe, [])
([exe], (x : xs))
| x == unUnqualComponentName (exeName exe) -> return (True, exe, xs)
| otherwise -> return (False, exe, args)
(_, []) ->
Left $
"This package contains multiple executables. "
++ "You must pass the executable name as the first argument "
++ "to 'cabal run'."
(_, (x : xs)) ->
case find (\exe -> unUnqualComponentName (exeName exe) == x) enabledExes of
Nothing -> Left $ "No executable named '" ++ x ++ "'."
Just exe -> return (True, exe, xs)
where
enabledExes = filter (buildable . buildInfo) (executables pkg_descr)
maybeWarning :: Maybe String
maybeWarning = case args of
[] -> Nothing
(x : _) -> lookup (mkUnqualComponentName x) components
where
components :: [(UnqualComponentName, String)] -- Component name, message.
components =
[ (name, "The executable '" ++ prettyShow name ++ "' is disabled.")
| e <- executables pkg_descr
, not . buildable . buildInfo $ e
, let name = exeName e
]
++ [ ( name
, "There is a test-suite '"
++ prettyShow name
++ "',"
++ " but the `run` command is only for executables."
)
| t <- testSuites pkg_descr
, let name = testName t
]
++ [ ( name
, "There is a benchmark '"
++ prettyShow name
++ "',"
++ " but the `run` command is only for executables."
)
| b <- benchmarks pkg_descr
, let name = benchmarkName b
]
-- | Run a given executable.
run :: Verbosity -> LocalBuildInfo -> Executable -> [String] -> IO ()
run verbosity lbi exe exeArgs = do
curDir <- absoluteWorkingDirLBI lbi
let distPref = fromFlag $ configDistPref $ configFlags lbi
buildPref = buildDir lbi
pkg_descr = localPkgDescr lbi
i = interpretSymbolicPathLBI lbi -- See Note [Symbolic paths] in Distribution.Utils.Path
mbWorkDir = mbWorkDirLBI lbi
internalPkgDb = internalPackageDBPath lbi distPref
lbiForExe =
lbi
{ withPackageDB = withPackageDB lbi ++ [SpecificPackageDB internalPkgDb]
, -- Include any build-tool-depends on build tools internal to the current package.
withPrograms =
addInternalBuildTools
curDir
pkg_descr
lbi
(buildInfo exe)
(withPrograms lbi)
}
(path, runArgs) <-
let exeName' = prettyShow $ exeName exe
in case compilerFlavor (compiler lbiForExe) of
GHCJS -> do
let (script, cmd, cmdArgs) =
GHCJS.runCmd
(withPrograms lbiForExe)
(i buildPref </> exeName' </> exeName')
script' <- tryCanonicalizePath script
return (cmd, cmdArgs ++ [script'])
_ -> do
p <-
tryCanonicalizePath $
i buildPref </> exeName' </> (exeName' <.> exeExtension (hostPlatform lbiForExe))
return (p, [])
-- Compute the appropriate environment for running the executable
let progDb = withPrograms lbiForExe
pathVar = progSearchPath progDb
envOverrides = progOverrideEnv progDb
newPath <- programSearchPathAsPATHVar pathVar
env <- getFullEnvironment ([("PATH", Just newPath)] ++ envOverrides)
-- Add (DY)LD_LIBRARY_PATH if needed
env' <-
if withDynExe lbiForExe
then do
let (Platform _ os) = hostPlatform lbiForExe
clbi <- case componentNameTargets' pkg_descr lbiForExe (CExeName (exeName exe)) of
[target] -> return (targetCLBI target)
[] -> dieWithException verbosity CouldNotFindExecutable
_ -> dieWithException verbosity FoundMultipleMatchingExes
paths <- depLibraryPaths True False lbiForExe clbi
return (addLibraryPath os paths env)
else return env
notice verbosity $ "Running " ++ prettyShow (exeName exe) ++ "..."
rawSystemExitWithEnvCwd verbosity mbWorkDir path (runArgs ++ exeArgs) env'