{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE NoFieldSelectors #-} {-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-} -- | Types and functions related to Stack's @path@ command. module Stack.Path ( EnvConfigPathInfo , path , pathsFromRunner , pathsFromConfig , pathsFromEnvConfig ) where import Data.List ( intercalate ) import qualified Data.Text as T import qualified Data.Text.IO as T import Path ( (), parent ) import Path.Extra ( toFilePathNoTrailingSep ) import RIO.Process ( HasProcessContext (..), exeSearchPathL ) import Stack.Config ( determineStackRootAndOwnership ) import Stack.Constants ( docDirSuffix, stackGlobalConfigOptionName , stackRootOptionName ) import Stack.Constants.Config ( distRelativeDir ) import Stack.GhcPkg as GhcPkg import Stack.Prelude hiding ( pi ) import Stack.Runners ( ShouldReexec (..), withConfig, withDefaultEnvConfig ) import Stack.Types.BuildConfig ( BuildConfig (..), HasBuildConfig (..), projectRootL , stackYamlL ) import Stack.Types.BuildOptsMonoid ( buildOptsMonoidHaddockL ) import Stack.Types.CompilerPaths ( CompilerPaths (..), HasCompiler (..), getCompilerPath ) import Stack.Types.Config ( Config (..), HasConfig (..), stackGlobalConfigL ) import Stack.Types.EnvConfig ( EnvConfig, HasEnvConfig (..), bindirCompilerTools , hpcReportDir, installationRootDeps, installationRootLocal , packageDatabaseDeps, packageDatabaseExtra , packageDatabaseLocal ) import qualified Stack.Types.EnvConfig as EnvConfig import Stack.Types.GHCVariant ( HasGHCVariant (..) ) import Stack.Types.GlobalOpts ( GlobalOpts (..), globalOptsBuildOptsMonoidL ) import Stack.Types.Platform ( HasPlatform (..) ) import Stack.Types.Runner ( HasRunner (..), Runner, globalOptsL ) import qualified System.FilePath as FP -- | Print out useful path information in a human-readable format (and support -- others later). path :: [Text] -> RIO Runner () path keys = do let -- filter the chosen paths in flags (keys), or show all of them if no -- specific paths chosen. filterKeys (_, key, _) = null keys || elem key keys goodPathsFromRunner = null keys || elem stackRootOptionName' keys goodPathsFromConfig = filter filterKeys pathsFromConfig goodPathsFromEnvConfig = filter filterKeys pathsFromEnvConfig toKeyPath (_, key, p) = (key, p) goodPathsFromConfig' = map toKeyPath goodPathsFromConfig singlePath = (if goodPathsFromRunner then 1 else 0) + length goodPathsFromConfig + length goodPathsFromEnvConfig == 1 toEither (_, k, UseHaddocks a) = Left (k, a) toEither (_, k, WithoutHaddocks a) = Right (k, a) (with, without) = partitionEithers $ map toEither goodPathsFromEnvConfig when goodPathsFromRunner $ printKeysWithRunner singlePath unless (null goodPathsFromConfig') $ runHaddockWithConfig $ printKeysWithConfig goodPathsFromConfig' singlePath unless (null without) $ runHaddockWithEnvConfig False $ printKeysWithEnvConfig without singlePath unless (null with) $ runHaddockWithEnvConfig True $ printKeysWithEnvConfig with singlePath printKeysWithRunner :: Bool -> RIO Runner () printKeysWithRunner single = do clArgs <- view $ globalOptsL . to (.configMonoid) liftIO $ do (_, stackRoot, _) <- determineStackRootAndOwnership clArgs let prefix = if single then "" else stackRootOptionName' <> ": " T.putStrLn $ prefix <> T.pack (toFilePathNoTrailingSep stackRoot) printKeysWithConfig :: HasConfig env => [(Text, Config -> Text)] -> Bool -> RIO env () printKeysWithConfig extractors single = view configL >>= printKeys extractors single printKeysWithEnvConfig :: HasEnvConfig env => [(Text, EnvConfigPathInfo -> Text)] -> Bool -> RIO env () printKeysWithEnvConfig extractors single = fillEnvConfigPathInfo >>= printKeys extractors single printKeys :: [(Text, info -> Text)] -> Bool -> info -> RIO env () printKeys extractors single info = do liftIO $ forM_ extractors $ \(key, extractPath) -> do let prefix = if single then "" else key <> ": " T.putStrLn $ prefix <> extractPath info runHaddockWithEnvConfig :: Bool -> RIO EnvConfig () -> RIO Runner () runHaddockWithEnvConfig x action = runHaddock x (withDefaultEnvConfig action) runHaddockWithConfig :: RIO Config () -> RIO Runner () runHaddockWithConfig = runHaddock False runHaddock :: Bool -> RIO Config () -> RIO Runner () runHaddock x action = local modifyConfig $ withConfig YesReexec action where modifyConfig = set (globalOptsL . globalOptsBuildOptsMonoidL . buildOptsMonoidHaddockL) (Just x) fillEnvConfigPathInfo :: HasEnvConfig env => RIO env EnvConfigPathInfo fillEnvConfigPathInfo = do -- We must use a BuildConfig from an EnvConfig to ensure that it contains the -- full environment info including GHC paths etc. buildConfig <- view $ envConfigL . buildConfigL -- This is the modified 'bin-path', -- including the local GHC or MSYS if not configured to operate on -- global GHC. -- It was set up in 'withBuildConfigAndLock -> withBuildConfigExt -> setupEnv'. -- So it's not the *minimal* override path. snapDb <- packageDatabaseDeps localDb <- packageDatabaseLocal extraDbs <- packageDatabaseExtra globalDb <- view $ compilerPathsL . to (.globalDB) snapRoot <- installationRootDeps localRoot <- installationRootLocal toolsDir <- bindirCompilerTools hoogleRoot <- EnvConfig.hoogleRoot distDir <- distRelativeDir hpcDir <- hpcReportDir compiler <- getCompilerPath pure EnvConfigPathInfo { buildConfig , snapDb , localDb , globalDb , snapRoot , localRoot , toolsDir , hoogleRoot , distDir , hpcDir , extraDbs , compiler } data EnvConfigPathInfo = EnvConfigPathInfo { buildConfig :: !BuildConfig , snapDb :: !(Path Abs Dir) , localDb :: !(Path Abs Dir) , globalDb :: !(Path Abs Dir) , snapRoot :: !(Path Abs Dir) , localRoot :: !(Path Abs Dir) , toolsDir :: !(Path Abs Dir) , hoogleRoot :: !(Path Abs Dir) , distDir :: Path Rel Dir , hpcDir :: !(Path Abs Dir) , extraDbs :: ![Path Abs Dir] , compiler :: !(Path Abs File) } instance HasPlatform EnvConfigPathInfo where platformL = configL . platformL {-# INLINE platformL #-} platformVariantL = configL . platformVariantL {-# INLINE platformVariantL #-} instance HasLogFunc EnvConfigPathInfo where logFuncL = configL . logFuncL instance HasRunner EnvConfigPathInfo where runnerL = configL . runnerL instance HasStylesUpdate EnvConfigPathInfo where stylesUpdateL = runnerL . stylesUpdateL instance HasTerm EnvConfigPathInfo where useColorL = runnerL . useColorL termWidthL = runnerL . termWidthL instance HasGHCVariant EnvConfigPathInfo where ghcVariantL = configL . ghcVariantL {-# INLINE ghcVariantL #-} instance HasConfig EnvConfigPathInfo where configL = buildConfigL . lens (.config) (\x y -> x { config = y }) {-# INLINE configL #-} instance HasPantryConfig EnvConfigPathInfo where pantryConfigL = configL . pantryConfigL instance HasProcessContext EnvConfigPathInfo where processContextL = configL . processContextL instance HasBuildConfig EnvConfigPathInfo where buildConfigL = lens (.buildConfig) (\x y -> x { buildConfig = y }) . buildConfigL data UseHaddocks a = UseHaddocks a | WithoutHaddocks a -- | The paths of interest to a user which do require a 'Config' or 'EnvConfig'. -- The first tuple string is used for a description that the optparse flag uses, -- and the second string as a machine-readable key and also for @--foo@ flags. -- The user can choose a specific path to list like @--stack-root@. But really -- it's mainly for the documentation aspect. pathsFromRunner :: (String, Text) pathsFromRunner = ("Global Stack root directory", stackRootOptionName') -- | The paths of interest to a user which do require an 'EnvConfig'. The first -- tuple string is used for a description that the optparse flag uses, and the -- second string as a machine-readable key and also for @--foo@ flags. The user -- can choose a specific path to list like @--stack-root@. But really it's -- mainly for the documentation aspect. -- -- When printing output we generate @Config@ and pass it to the function -- to generate an appropriate string. Trailing slashes are removed, see #506. pathsFromConfig :: [(String, Text, Config -> Text)] pathsFromConfig = [ ( "Global Stack configuration file" , T.pack stackGlobalConfigOptionName , view (stackGlobalConfigL . to toFilePath . to T.pack) ) , ( "Install location for GHC and other core tools (see 'stack ls tools' command)" , "programs" , view (configL . to (.localPrograms) . to toFilePathNoTrailingSep . to T.pack) ) , ( "Directory where Stack installs executables (e.g. ~/.local/bin (Unix-like OSs) or %APPDATA%\\local\\bin (Windows))" , "local-bin" , view $ configL . to (.localBin) . to toFilePathNoTrailingSep . to T.pack ) ] -- | The paths of interest to a user which require a 'EnvConfig'. The first -- tuple string is used for a description that the optparse flag uses, and the -- second string as a machine-readable key and also for @--foo@ flags. The user -- can choose a specific path to list like @--project-root@. But really it's -- mainly for the documentation aspect. -- -- When printing output we generate @EnvConfigPathInfo@ and pass it to the -- function to generate an appropriate string. Trailing slashes are removed, see -- #506. pathsFromEnvConfig :: [(String, Text, UseHaddocks (EnvConfigPathInfo -> Text))] pathsFromEnvConfig = [ ( "Project root (derived from stack.yaml file)" , "project-root" , WithoutHaddocks $ view (projectRootL . to toFilePathNoTrailingSep . to T.pack) ) , ( "Configuration location (where the stack.yaml file is)" , "config-location" , WithoutHaddocks $ view (stackYamlL . to toFilePath . to T.pack) ) , ( "PATH environment variable" , "bin-path" , WithoutHaddocks $ T.pack . intercalate [FP.searchPathSeparator] . view exeSearchPathL ) , ( "Compiler binary (e.g. ghc)" , "compiler-exe" , WithoutHaddocks $ T.pack . toFilePath . (.compiler) ) , ( "Directory containing the compiler binary (e.g. ghc)" , "compiler-bin" , WithoutHaddocks $ T.pack . toFilePathNoTrailingSep . parent . (.compiler) ) , ( "Directory containing binaries specific to a particular compiler" , "compiler-tools-bin" , WithoutHaddocks $ T.pack . toFilePathNoTrailingSep . (.toolsDir) ) , ( "Extra include directories" , "extra-include-dirs" , WithoutHaddocks $ T.intercalate ", " . map T.pack . (.extraIncludeDirs) . view configL ) , ( "Extra library directories" , "extra-library-dirs" , WithoutHaddocks $ T.intercalate ", " . map T.pack . (.extraLibDirs) . view configL ) , ( "Snapshot package database" , "snapshot-pkg-db" , WithoutHaddocks $ T.pack . toFilePathNoTrailingSep . (.snapDb) ) , ( "Local project package database" , "local-pkg-db" , WithoutHaddocks $ T.pack . toFilePathNoTrailingSep . (.localDb) ) , ( "Global package database" , "global-pkg-db" , WithoutHaddocks $ T.pack . toFilePathNoTrailingSep . (.globalDb) ) , ( "GHC_PACKAGE_PATH environment variable" , "ghc-package-path" , WithoutHaddocks $ \pi -> mkGhcPackagePath True pi.localDb pi.snapDb pi.extraDbs pi.globalDb ) , ( "Snapshot installation root" , "snapshot-install-root" , WithoutHaddocks $ T.pack . toFilePathNoTrailingSep . (.snapRoot) ) , ( "Local project installation root" , "local-install-root" , WithoutHaddocks $ T.pack . toFilePathNoTrailingSep . (.localRoot) ) , ( "Snapshot documentation root" , "snapshot-doc-root" , UseHaddocks $ \pi -> T.pack (toFilePathNoTrailingSep (pi.snapRoot docDirSuffix)) ) , ( "Local project documentation root" , "local-doc-root" , UseHaddocks $ \pi -> T.pack (toFilePathNoTrailingSep (pi.localRoot docDirSuffix)) ) , ( "Local project documentation root" , "local-hoogle-root" , UseHaddocks $ T.pack . toFilePathNoTrailingSep . (.hoogleRoot) ) , ( "Dist work directory, relative to package directory" , "dist-dir" , WithoutHaddocks $ T.pack . toFilePathNoTrailingSep . (.distDir) ) , ( "Where HPC reports and tix files are stored" , "local-hpc-root" , WithoutHaddocks $ T.pack . toFilePathNoTrailingSep . (.hpcDir) ) ] -- | 'Text' equivalent of 'stackRootOptionName'. stackRootOptionName' :: Text stackRootOptionName' = T.pack stackRootOptionName