{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE LambdaCase #-}

module Stack.Setup.Installed
    ( getCompilerVersion
    , markInstalled
    , unmarkInstalled
    , listInstalled
    , Tool (..)
    , toolString
    , toolNameString
    , parseToolText
    , extraDirs
    , installDir
    , tempInstallDir
    ) where

import           Stack.Prelude
import qualified Data.ByteString.Char8 as S8
import qualified Data.ByteString.Lazy as BL
import           Data.List hiding (concat, elem, maximumBy)
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import           Distribution.System (Platform (..))
import qualified Distribution.System as Cabal
import           Path
import           Path.IO
import           Stack.Constants
import           Stack.Types.Compiler
import           Stack.Types.Config
import           RIO.Process

data Tool
    = Tool PackageIdentifier -- ^ e.g. ghc-7.8.4, msys2-20150512
    | ToolGhcGit !Text !Text   -- ^ e.g. ghc-git-COMMIT_ID-FLAVOUR
    deriving (Tool -> Tool -> Bool
(Tool -> Tool -> Bool) -> (Tool -> Tool -> Bool) -> Eq Tool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Tool -> Tool -> Bool
$c/= :: Tool -> Tool -> Bool
== :: Tool -> Tool -> Bool
$c== :: Tool -> Tool -> Bool
Eq)

toolString :: Tool -> String
toolString :: Tool -> String
toolString (Tool PackageIdentifier
ident) = PackageIdentifier -> String
packageIdentifierString PackageIdentifier
ident
toolString (ToolGhcGit Text
commit Text
flavour) = String
"ghc-git-" String -> String -> String
forall a. [a] -> [a] -> [a]
++ Text -> String
T.unpack Text
commit String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"-" String -> String -> String
forall a. [a] -> [a] -> [a]
++ Text -> String
T.unpack Text
flavour

toolNameString :: Tool -> String
toolNameString :: Tool -> String
toolNameString (Tool PackageIdentifier
ident) = PackageName -> String
packageNameString (PackageName -> String) -> PackageName -> String
forall a b. (a -> b) -> a -> b
$ PackageIdentifier -> PackageName
pkgName PackageIdentifier
ident
toolNameString ToolGhcGit{} = String
"ghc-git"

parseToolText :: Text -> Maybe Tool
parseToolText :: Text -> Maybe Tool
parseToolText (Text -> Either PantryException WantedCompiler
parseWantedCompiler -> Right WCGhcjs{}) = Maybe Tool
forall a. Maybe a
Nothing
parseToolText (Text -> Either PantryException WantedCompiler
parseWantedCompiler -> Right (WCGhcGit Text
c Text
f)) = Tool -> Maybe Tool
forall a. a -> Maybe a
Just (Text -> Text -> Tool
ToolGhcGit Text
c Text
f)
parseToolText (String -> Maybe PackageIdentifier
parsePackageIdentifier (String -> Maybe PackageIdentifier)
-> (Text -> String) -> Text -> Maybe PackageIdentifier
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
T.unpack -> Just PackageIdentifier
pkgId) = Tool -> Maybe Tool
forall a. a -> Maybe a
Just (PackageIdentifier -> Tool
Tool PackageIdentifier
pkgId)
parseToolText Text
_ = Maybe Tool
forall a. Maybe a
Nothing

markInstalled :: (MonadIO m, MonadThrow m)
              => Path Abs Dir
              -> Tool
              -> m ()
markInstalled :: Path Abs Dir -> Tool -> m ()
markInstalled Path Abs Dir
programsPath Tool
tool = do
    Path Rel File
fpRel <- String -> m (Path Rel File)
forall (m :: * -> *). MonadThrow m => String -> m (Path Rel File)
parseRelFile (String -> m (Path Rel File)) -> String -> m (Path Rel File)
forall a b. (a -> b) -> a -> b
$ Tool -> String
toolString Tool
tool String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".installed"
    Path Abs File -> Builder -> m ()
forall (m :: * -> *) absrel.
MonadIO m =>
Path absrel File -> Builder -> m ()
writeBinaryFileAtomic (Path Abs Dir
programsPath Path Abs Dir -> Path Rel File -> Path Abs File
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel File
fpRel) Builder
"installed"

unmarkInstalled :: MonadIO m
                => Path Abs Dir
                -> Tool
                -> m ()
unmarkInstalled :: Path Abs Dir -> Tool -> m ()
unmarkInstalled Path Abs Dir
programsPath Tool
tool = IO () -> m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ do
    Path Rel File
fpRel <- String -> IO (Path Rel File)
forall (m :: * -> *). MonadThrow m => String -> m (Path Rel File)
parseRelFile (String -> IO (Path Rel File)) -> String -> IO (Path Rel File)
forall a b. (a -> b) -> a -> b
$ Tool -> String
toolString Tool
tool String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".installed"
    IO () -> IO ()
forall (m :: * -> *) a. (MonadIO m, MonadCatch m) => m a -> m ()
ignoringAbsence (Path Abs File -> IO ()
forall (m :: * -> *) b. MonadIO m => Path b File -> m ()
removeFile (Path Abs File -> IO ()) -> Path Abs File -> IO ()
forall a b. (a -> b) -> a -> b
$ Path Abs Dir
programsPath Path Abs Dir -> Path Rel File -> Path Abs File
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel File
fpRel)

listInstalled :: (MonadIO m, MonadThrow m)
              => Path Abs Dir
              -> m [Tool]
listInstalled :: Path Abs Dir -> m [Tool]
listInstalled Path Abs Dir
programsPath = do
    Path Abs Dir -> m Bool
forall (m :: * -> *) b. MonadIO m => Path b Dir -> m Bool
doesDirExist Path Abs Dir
programsPath m Bool -> (Bool -> m [Tool]) -> m [Tool]
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
        Bool
False -> [Tool] -> m [Tool]
forall (m :: * -> *) a. Monad m => a -> m a
return []
        Bool
True -> do ([Path Abs Dir]
_, [Path Abs File]
files) <- Path Abs Dir -> m ([Path Abs Dir], [Path Abs File])
forall (m :: * -> *) b.
MonadIO m =>
Path b Dir -> m ([Path Abs Dir], [Path Abs File])
listDir Path Abs Dir
programsPath
                   [Tool] -> m [Tool]
forall (m :: * -> *) a. Monad m => a -> m a
return ([Tool] -> m [Tool]) -> [Tool] -> m [Tool]
forall a b. (a -> b) -> a -> b
$ (Path Abs File -> Maybe Tool) -> [Path Abs File] -> [Tool]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe Path Abs File -> Maybe Tool
forall b. Path b File -> Maybe Tool
toTool [Path Abs File]
files
  where
    toTool :: Path b File -> Maybe Tool
toTool Path b File
fp = do
        Text
x <- Text -> Text -> Maybe Text
T.stripSuffix Text
".installed" (Text -> Maybe Text) -> Text -> Maybe Text
forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ Path Rel File -> String
forall b t. Path b t -> String
toFilePath (Path Rel File -> String) -> Path Rel File -> String
forall a b. (a -> b) -> a -> b
$ Path b File -> Path Rel File
forall b. Path b File -> Path Rel File
filename Path b File
fp
        Text -> Maybe Tool
parseToolText Text
x

getCompilerVersion
  :: (HasProcessContext env, HasLogFunc env)
  => WhichCompiler
  -> Path Abs File -- ^ executable
  -> RIO env ActualCompiler
getCompilerVersion :: WhichCompiler -> Path Abs File -> RIO env ActualCompiler
getCompilerVersion WhichCompiler
wc Path Abs File
exe = do
    case WhichCompiler
wc of
        WhichCompiler
Ghc -> do
            Utf8Builder -> RIO env ()
forall (m :: * -> *) env.
(MonadIO m, MonadReader env m, HasLogFunc env, HasCallStack) =>
Utf8Builder -> m ()
logDebug Utf8Builder
"Asking GHC for its version"
            ByteString
bs <- (ByteString, ByteString) -> ByteString
forall a b. (a, b) -> a
fst ((ByteString, ByteString) -> ByteString)
-> RIO env (ByteString, ByteString) -> RIO env ByteString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String
-> [String]
-> (ProcessConfig () () () -> RIO env (ByteString, ByteString))
-> RIO env (ByteString, ByteString)
forall env (m :: * -> *) a.
(HasProcessContext env, HasLogFunc env, MonadReader env m,
 MonadIO m, HasCallStack) =>
String -> [String] -> (ProcessConfig () () () -> m a) -> m a
proc (Path Abs File -> String
forall b t. Path b t -> String
toFilePath Path Abs File
exe) [String
"--numeric-version"] ProcessConfig () () () -> RIO env (ByteString, ByteString)
forall (m :: * -> *) stdin stdoutIgnored stderrIgnored.
MonadIO m =>
ProcessConfig stdin stdoutIgnored stderrIgnored
-> m (ByteString, ByteString)
readProcess_
            let (ByteString
_, ByteString
ghcVersion) = ByteString -> (ByteString, ByteString)
versionFromEnd (ByteString -> (ByteString, ByteString))
-> ByteString -> (ByteString, ByteString)
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString
BL.toStrict ByteString
bs
            ActualCompiler
x <- Version -> ActualCompiler
ACGhc (Version -> ActualCompiler)
-> RIO env Version -> RIO env ActualCompiler
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> RIO env Version
forall (m :: * -> *). MonadThrow m => String -> m Version
parseVersionThrowing (Text -> String
T.unpack (Text -> String) -> Text -> String
forall a b. (a -> b) -> a -> b
$ ByteString -> Text
T.decodeUtf8 ByteString
ghcVersion)
            Utf8Builder -> RIO env ()
forall (m :: * -> *) env.
(MonadIO m, MonadReader env m, HasLogFunc env, HasCallStack) =>
Utf8Builder -> m ()
logDebug (Utf8Builder -> RIO env ()) -> Utf8Builder -> RIO env ()
forall a b. (a -> b) -> a -> b
$ Utf8Builder
"GHC version is: " Utf8Builder -> Utf8Builder -> Utf8Builder
forall a. Semigroup a => a -> a -> a
<> ActualCompiler -> Utf8Builder
forall a. Display a => a -> Utf8Builder
display ActualCompiler
x
            ActualCompiler -> RIO env ActualCompiler
forall (m :: * -> *) a. Monad m => a -> m a
return ActualCompiler
x
  where
    versionFromEnd :: ByteString -> (ByteString, ByteString)
versionFromEnd = (Char -> Bool) -> ByteString -> (ByteString, ByteString)
S8.spanEnd Char -> Bool
isValid (ByteString -> (ByteString, ByteString))
-> (ByteString -> ByteString)
-> ByteString
-> (ByteString, ByteString)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ByteString, ByteString) -> ByteString
forall a b. (a, b) -> a
fst ((ByteString, ByteString) -> ByteString)
-> (ByteString -> (ByteString, ByteString))
-> ByteString
-> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> ByteString -> (ByteString, ByteString)
S8.breakEnd Char -> Bool
isValid
    isValid :: Char -> Bool
isValid Char
c = Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'.' Bool -> Bool -> Bool
|| (Char
'0' Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
c Bool -> Bool -> Bool
&& Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
'9')

-- | Binary directories for the given installed package
extraDirs :: HasConfig env => Tool -> RIO env ExtraDirs
extraDirs :: Tool -> RIO env ExtraDirs
extraDirs Tool
tool = do
    Config
config <- Getting Config env Config -> RIO env Config
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting Config env Config
forall env. HasConfig env => Lens' env Config
configL
    Path Abs Dir
dir <- Path Abs Dir -> Tool -> RIO env (Path Abs Dir)
forall env (m :: * -> *).
(MonadReader env m, MonadThrow m) =>
Path Abs Dir -> Tool -> m (Path Abs Dir)
installDir (Config -> Path Abs Dir
configLocalPrograms Config
config) Tool
tool
    case (Config -> Platform
configPlatform Config
config, Tool -> String
toolNameString Tool
tool) of
        (Platform Arch
_ OS
Cabal.Windows, String -> Bool
isGHC -> Bool
True) -> ExtraDirs -> RIO env ExtraDirs
forall (m :: * -> *) a. Monad m => a -> m a
return ExtraDirs
forall a. Monoid a => a
mempty
            { edBins :: [Path Abs Dir]
edBins =
                [ Path Abs Dir
dir Path Abs Dir -> Path Rel Dir -> Path Abs Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirBin
                , Path Abs Dir
dir Path Abs Dir -> Path Rel Dir -> Path Abs Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirMingw Path Rel Dir -> Path Rel Dir -> Path Rel Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirBin
                ]
            }
        (Platform Arch
Cabal.I386 OS
Cabal.Windows, String
"msys2") -> ExtraDirs -> RIO env ExtraDirs
forall (m :: * -> *) a. Monad m => a -> m a
return ExtraDirs
forall a. Monoid a => a
mempty
            { edBins :: [Path Abs Dir]
edBins =
                [ Path Abs Dir
dir Path Abs Dir -> Path Rel Dir -> Path Abs Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirMingw32 Path Rel Dir -> Path Rel Dir -> Path Rel Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirBin
                , Path Abs Dir
dir Path Abs Dir -> Path Rel Dir -> Path Abs Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirUsr Path Rel Dir -> Path Rel Dir -> Path Rel Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirBin
                , Path Abs Dir
dir Path Abs Dir -> Path Rel Dir -> Path Abs Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirUsr Path Rel Dir -> Path Rel Dir -> Path Rel Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirLocal Path Rel Dir -> Path Rel Dir -> Path Rel Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirBin
                ]
            , edInclude :: [Path Abs Dir]
edInclude =
                [ Path Abs Dir
dir Path Abs Dir -> Path Rel Dir -> Path Abs Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirMingw32 Path Rel Dir -> Path Rel Dir -> Path Rel Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirInclude
                ]
            , edLib :: [Path Abs Dir]
edLib =
                [ Path Abs Dir
dir Path Abs Dir -> Path Rel Dir -> Path Abs Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirMingw32 Path Rel Dir -> Path Rel Dir -> Path Rel Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirLib
                , Path Abs Dir
dir Path Abs Dir -> Path Rel Dir -> Path Abs Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirMingw32 Path Rel Dir -> Path Rel Dir -> Path Rel Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirBin
                ]
            }
        (Platform Arch
Cabal.X86_64 OS
Cabal.Windows, String
"msys2") -> ExtraDirs -> RIO env ExtraDirs
forall (m :: * -> *) a. Monad m => a -> m a
return ExtraDirs
forall a. Monoid a => a
mempty
            { edBins :: [Path Abs Dir]
edBins =
                [ Path Abs Dir
dir Path Abs Dir -> Path Rel Dir -> Path Abs Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirMingw64 Path Rel Dir -> Path Rel Dir -> Path Rel Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirBin
                , Path Abs Dir
dir Path Abs Dir -> Path Rel Dir -> Path Abs Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirUsr Path Rel Dir -> Path Rel Dir -> Path Rel Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirBin
                , Path Abs Dir
dir Path Abs Dir -> Path Rel Dir -> Path Abs Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirUsr Path Rel Dir -> Path Rel Dir -> Path Rel Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirLocal Path Rel Dir -> Path Rel Dir -> Path Rel Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirBin
                ]
            , edInclude :: [Path Abs Dir]
edInclude =
                [ Path Abs Dir
dir Path Abs Dir -> Path Rel Dir -> Path Abs Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirMingw64 Path Rel Dir -> Path Rel Dir -> Path Rel Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirInclude
                ]
            , edLib :: [Path Abs Dir]
edLib =
                [ Path Abs Dir
dir Path Abs Dir -> Path Rel Dir -> Path Abs Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirMingw64 Path Rel Dir -> Path Rel Dir -> Path Rel Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirLib
                , Path Abs Dir
dir Path Abs Dir -> Path Rel Dir -> Path Abs Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirMingw64 Path Rel Dir -> Path Rel Dir -> Path Rel Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirBin
                ]
            }
        (Platform
_, String -> Bool
isGHC -> Bool
True) -> ExtraDirs -> RIO env ExtraDirs
forall (m :: * -> *) a. Monad m => a -> m a
return ExtraDirs
forall a. Monoid a => a
mempty
            { edBins :: [Path Abs Dir]
edBins =
                [ Path Abs Dir
dir Path Abs Dir -> Path Rel Dir -> Path Abs Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirBin
                ]
            }
        (Platform Arch
_ OS
x, String
toolName) -> do
            Utf8Builder -> RIO env ()
forall (m :: * -> *) env.
(MonadIO m, MonadReader env m, HasLogFunc env, HasCallStack) =>
Utf8Builder -> m ()
logWarn (Utf8Builder -> RIO env ()) -> Utf8Builder -> RIO env ()
forall a b. (a -> b) -> a -> b
$ Utf8Builder
"binDirs: unexpected OS/tool combo: " Utf8Builder -> Utf8Builder -> Utf8Builder
forall a. Semigroup a => a -> a -> a
<> (OS, String) -> Utf8Builder
forall a. Show a => a -> Utf8Builder
displayShow (OS
x, String
toolName)
            ExtraDirs -> RIO env ExtraDirs
forall (m :: * -> *) a. Monad m => a -> m a
return ExtraDirs
forall a. Monoid a => a
mempty
  where
    isGHC :: String -> Bool
isGHC String
n = String
"ghc" String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
n Bool -> Bool -> Bool
|| String
"ghc-" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
n

installDir :: (MonadReader env m, MonadThrow m)
           => Path Abs Dir
           -> Tool
           -> m (Path Abs Dir)
installDir :: Path Abs Dir -> Tool -> m (Path Abs Dir)
installDir Path Abs Dir
programsDir Tool
tool = do
    Path Rel Dir
relativeDir <- String -> m (Path Rel Dir)
forall (m :: * -> *). MonadThrow m => String -> m (Path Rel Dir)
parseRelDir (String -> m (Path Rel Dir)) -> String -> m (Path Rel Dir)
forall a b. (a -> b) -> a -> b
$ Tool -> String
toolString Tool
tool
    Path Abs Dir -> m (Path Abs Dir)
forall (m :: * -> *) a. Monad m => a -> m a
return (Path Abs Dir -> m (Path Abs Dir))
-> Path Abs Dir -> m (Path Abs Dir)
forall a b. (a -> b) -> a -> b
$ Path Abs Dir
programsDir Path Abs Dir -> Path Rel Dir -> Path Abs Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relativeDir

tempInstallDir :: (MonadReader env m, MonadThrow m)
           => Path Abs Dir
           -> Tool
           -> m (Path Abs Dir)
tempInstallDir :: Path Abs Dir -> Tool -> m (Path Abs Dir)
tempInstallDir Path Abs Dir
programsDir Tool
tool = do
    Path Rel Dir
relativeDir <- String -> m (Path Rel Dir)
forall (m :: * -> *). MonadThrow m => String -> m (Path Rel Dir)
parseRelDir (String -> m (Path Rel Dir)) -> String -> m (Path Rel Dir)
forall a b. (a -> b) -> a -> b
$ Tool -> String
toolString Tool
tool String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".temp"
    Path Abs Dir -> m (Path Abs Dir)
forall (m :: * -> *) a. Monad m => a -> m a
return (Path Abs Dir -> m (Path Abs Dir))
-> Path Abs Dir -> m (Path Abs Dir)
forall a b. (a -> b) -> a -> b
$ Path Abs Dir
programsDir Path Abs Dir -> Path Rel Dir -> Path Abs Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relativeDir