module Distribution.Simple.InstallDirs (
InstallDirs(..),
InstallDirTemplates,
defaultInstallDirs,
combineInstallDirs,
absoluteInstallDirs,
CopyDest(..),
prefixRelativeInstallDirs,
PathTemplate,
PathTemplateVariable(..),
toPathTemplate,
fromPathTemplate,
substPathTemplate,
initialPathTemplateEnv,
fullPathTemplateEnv,
) where
import Data.List (isPrefixOf)
import Data.Maybe (fromMaybe)
import Data.Monoid (Monoid(..))
import System.Directory (getAppUserDataDirectory)
import System.FilePath ((</>), isPathSeparator, pathSeparator)
#if __HUGS__ || __GLASGOW_HASKELL__ > 606
import System.FilePath (dropDrive)
#endif
import Distribution.Package
( PackageIdentifier, packageName, packageVersion )
import Distribution.System
( OS(..), buildOS, buildArch )
import Distribution.Compiler
( CompilerId, CompilerFlavor(..) )
import Distribution.Text
( display )
#if mingw32_HOST_OS || mingw32_TARGET_OS
import Foreign
import Foreign.C
#endif
data InstallDirs dir = InstallDirs {
prefix :: dir,
bindir :: dir,
libdir :: dir,
libsubdir :: dir,
dynlibdir :: dir,
libexecdir :: dir,
progdir :: dir,
includedir :: dir,
datadir :: dir,
datasubdir :: dir,
docdir :: dir,
mandir :: dir,
htmldir :: dir,
haddockdir :: dir
} deriving (Read, Show)
instance Functor InstallDirs where
fmap f dirs = InstallDirs {
prefix = f (prefix dirs),
bindir = f (bindir dirs),
libdir = f (libdir dirs),
libsubdir = f (libsubdir dirs),
dynlibdir = f (dynlibdir dirs),
libexecdir = f (libexecdir dirs),
progdir = f (progdir dirs),
includedir = f (includedir dirs),
datadir = f (datadir dirs),
datasubdir = f (datasubdir dirs),
docdir = f (docdir dirs),
mandir = f (mandir dirs),
htmldir = f (htmldir dirs),
haddockdir = f (haddockdir dirs)
}
instance Monoid dir => Monoid (InstallDirs dir) where
mempty = InstallDirs {
prefix = mempty,
bindir = mempty,
libdir = mempty,
libsubdir = mempty,
dynlibdir = mempty,
libexecdir = mempty,
progdir = mempty,
includedir = mempty,
datadir = mempty,
datasubdir = mempty,
docdir = mempty,
mandir = mempty,
htmldir = mempty,
haddockdir = mempty
}
mappend = combineInstallDirs mappend
combineInstallDirs :: (a -> b -> c)
-> InstallDirs a
-> InstallDirs b
-> InstallDirs c
combineInstallDirs combine a b = InstallDirs {
prefix = prefix a `combine` prefix b,
bindir = bindir a `combine` bindir b,
libdir = libdir a `combine` libdir b,
libsubdir = libsubdir a `combine` libsubdir b,
dynlibdir = dynlibdir a `combine` dynlibdir b,
libexecdir = libexecdir a `combine` libexecdir b,
progdir = progdir a `combine` progdir b,
includedir = includedir a `combine` includedir b,
datadir = datadir a `combine` datadir b,
datasubdir = datasubdir a `combine` datasubdir b,
docdir = docdir a `combine` docdir b,
mandir = mandir a `combine` mandir b,
htmldir = htmldir a `combine` htmldir b,
haddockdir = haddockdir a `combine` haddockdir b
}
appendSubdirs :: (a -> a -> a) -> InstallDirs a -> InstallDirs a
appendSubdirs append dirs = dirs {
libdir = libdir dirs `append` libsubdir dirs,
datadir = datadir dirs `append` datasubdir dirs,
libsubdir = error "internal error InstallDirs.libsubdir",
datasubdir = error "internal error InstallDirs.datasubdir"
}
type InstallDirTemplates = InstallDirs PathTemplate
defaultInstallDirs :: CompilerFlavor -> Bool -> Bool -> IO InstallDirTemplates
defaultInstallDirs comp userInstall hasLibs = do
windowsProgramFilesDir <- getWindowsProgramFilesDir
userInstallPrefix <- getAppUserDataDirectory "cabal"
return $ fmap toPathTemplate $ InstallDirs {
prefix = if userInstall
then userInstallPrefix
else case buildOS of
Windows -> windowsProgramFilesDir </> "Haskell"
_other -> "/usr/local",
bindir = "$prefix" </> "bin",
libdir = case buildOS of
Windows -> "$prefix"
_other -> "$prefix" </> "lib",
libsubdir = case comp of
Hugs -> "hugs" </> "packages" </> "$pkg"
JHC -> "$compiler"
_other -> "$pkgid" </> "$compiler",
dynlibdir = "$libdir",
libexecdir = case buildOS of
Windows -> "$prefix" </> "$pkgid"
_other -> "$prefix" </> "libexec",
progdir = "$libdir" </> "hugs" </> "programs",
includedir = "$libdir" </> "$libsubdir" </> "include",
datadir = case buildOS of
Windows | hasLibs -> windowsProgramFilesDir </> "Haskell"
| otherwise -> "$prefix"
_other -> "$prefix" </> "share",
datasubdir = "$pkgid",
docdir = case buildOS of
Windows -> "$prefix" </> "doc" </> "$pkgid"
_other -> "$datadir" </> "doc" </> "$pkgid",
mandir = "$datadir" </> "man",
htmldir = "$docdir" </> "html",
haddockdir = "$htmldir"
}
substituteTemplates :: PackageIdentifier -> CompilerId
-> InstallDirTemplates -> InstallDirTemplates
substituteTemplates pkgId compilerId dirs = dirs'
where
dirs' = InstallDirs {
prefix = subst prefix [],
bindir = subst bindir [prefixVar],
libdir = subst libdir [prefixVar, bindirVar],
libsubdir = subst libsubdir [],
dynlibdir = subst dynlibdir [prefixVar, bindirVar, libdirVar],
libexecdir = subst libexecdir prefixBinLibVars,
progdir = subst progdir prefixBinLibVars,
includedir = subst includedir prefixBinLibVars,
datadir = subst datadir prefixBinLibVars,
datasubdir = subst datasubdir [],
docdir = subst docdir prefixBinLibDataVars,
mandir = subst mandir (prefixBinLibDataVars ++ [docdirVar]),
htmldir = subst htmldir (prefixBinLibDataVars ++ [docdirVar]),
haddockdir = subst haddockdir (prefixBinLibDataVars ++
[docdirVar, htmldirVar])
}
env = initialPathTemplateEnv pkgId compilerId
subst dir env' = substPathTemplate (env'++env) (dir dirs)
prefixVar = (PrefixVar, prefix dirs')
bindirVar = (BindirVar, bindir dirs')
libdirVar = (LibdirVar, libdir dirs')
libsubdirVar = (LibsubdirVar, libsubdir dirs')
datadirVar = (DatadirVar, datadir dirs')
datasubdirVar = (DatasubdirVar, datasubdir dirs')
docdirVar = (DocdirVar, docdir dirs')
htmldirVar = (HtmldirVar, htmldir dirs')
prefixBinLibVars = [prefixVar, bindirVar, libdirVar, libsubdirVar]
prefixBinLibDataVars = prefixBinLibVars ++ [datadirVar, datasubdirVar]
absoluteInstallDirs :: PackageIdentifier -> CompilerId -> CopyDest
-> InstallDirTemplates -> InstallDirs FilePath
absoluteInstallDirs pkgId compilerId copydest dirs =
(case copydest of
CopyTo destdir -> fmap ((destdir </>) . dropDrive)
_ -> id)
. appendSubdirs (</>)
. fmap fromPathTemplate
$ substituteTemplates pkgId compilerId dirs {
prefix = case copydest of
CopyPrefix p -> toPathTemplate p
_ -> prefix dirs
}
data CopyDest
= NoCopyDest
| CopyTo FilePath
| CopyPrefix FilePath
deriving (Eq, Show)
prefixRelativeInstallDirs :: PackageIdentifier -> CompilerId
-> InstallDirTemplates
-> InstallDirs (Maybe FilePath)
prefixRelativeInstallDirs pkgId compilerId dirs =
fmap relative
. appendSubdirs combinePathTemplate
$
substituteTemplates pkgId compilerId dirs {
prefix = PathTemplate [Variable PrefixVar]
}
where
relative dir = case dir of
PathTemplate cs -> fmap (fromPathTemplate . PathTemplate) (relative' cs)
relative' (Variable PrefixVar : Ordinary (s:rest) : rest')
| isPathSeparator s = Just (Ordinary rest : rest')
relative' (Variable PrefixVar : rest) = Just rest
relative' _ = Nothing
newtype PathTemplate = PathTemplate [PathComponent]
data PathComponent =
Ordinary FilePath
| Variable PathTemplateVariable
data PathTemplateVariable =
PrefixVar
| BindirVar
| LibdirVar
| LibsubdirVar
| DatadirVar
| DatasubdirVar
| DocdirVar
| HtmldirVar
| PkgNameVar
| PkgVerVar
| PkgIdVar
| CompilerVar
| OSVar
| ArchVar
| ExecutableNameVar
deriving Eq
toPathTemplate :: FilePath -> PathTemplate
toPathTemplate = PathTemplate . read
fromPathTemplate :: PathTemplate -> FilePath
fromPathTemplate (PathTemplate template) = show template
combinePathTemplate :: PathTemplate -> PathTemplate -> PathTemplate
combinePathTemplate (PathTemplate t1) (PathTemplate t2) =
PathTemplate (t1 ++ [Ordinary [pathSeparator]] ++ t2)
substPathTemplate :: [(PathTemplateVariable, PathTemplate)]
-> PathTemplate -> PathTemplate
substPathTemplate environment (PathTemplate template) =
PathTemplate (concatMap subst template)
where subst component@(Ordinary _) = [component]
subst component@(Variable variable) =
case lookup variable environment of
Just (PathTemplate components) -> components
Nothing -> [component]
initialPathTemplateEnv :: PackageIdentifier -> CompilerId
-> [(PathTemplateVariable, PathTemplate)]
initialPathTemplateEnv pkgId compilerId =
map (\(v,s) -> (v, PathTemplate [Ordinary s]))
[(PkgNameVar, display (packageName pkgId))
,(PkgVerVar, display (packageVersion pkgId))
,(PkgIdVar, display pkgId)
,(CompilerVar, display compilerId)
,(OSVar, display buildOS)
,(ArchVar, display buildArch)
]
fullPathTemplateEnv :: PackageIdentifier -> CompilerId
-> InstallDirs FilePath
-> [(PathTemplateVariable, PathTemplate)]
fullPathTemplateEnv pkgId compilerId dirs = env ++ dirEnv
where
env = initialPathTemplateEnv pkgId compilerId
dirEnv = [(PrefixVar, toPathTemplate $ prefix dirs),
(BindirVar, toPathTemplate $ bindir dirs),
(LibdirVar, toPathTemplate $ libdir dirs),
(DatadirVar, toPathTemplate $ datadir dirs),
(DocdirVar, toPathTemplate $ docdir dirs),
(HtmldirVar, toPathTemplate $ htmldir dirs)]
instance Show PathTemplateVariable where
show PrefixVar = "prefix"
show BindirVar = "bindir"
show LibdirVar = "libdir"
show LibsubdirVar = "libsubdir"
show DatadirVar = "datadir"
show DatasubdirVar = "datasubdir"
show DocdirVar = "docdir"
show HtmldirVar = "htmldir"
show PkgNameVar = "pkg"
show PkgVerVar = "version"
show PkgIdVar = "pkgid"
show CompilerVar = "compiler"
show OSVar = "os"
show ArchVar = "arch"
show ExecutableNameVar = "executablename"
instance Read PathTemplateVariable where
readsPrec _ s =
take 1
[ (var, drop (length varStr) s)
| (varStr, var) <- vars
, varStr `isPrefixOf` s ]
where vars = [("prefix", PrefixVar)
,("bindir", BindirVar)
,("libdir", LibdirVar)
,("libsubdir", LibsubdirVar)
,("datadir", DatadirVar)
,("datasubdir", DatasubdirVar)
,("docdir", DocdirVar)
,("htmldir", HtmldirVar)
,("pkgid", PkgIdVar)
,("pkg", PkgNameVar)
,("version", PkgVerVar)
,("compiler", CompilerVar)
,("os", OSVar)
,("arch", ArchVar)
,("executablename", ExecutableNameVar)]
instance Show PathComponent where
show (Ordinary path) = path
show (Variable var) = '$':show var
showList = foldr (\x -> (shows x .)) id
instance Read PathComponent where
readsPrec _ = lex0
where lex0 [] = []
lex0 ('$':'$':s') = lex0 ('$':s')
lex0 ('$':s') = case [ (Variable var, s'')
| (var, s'') <- reads s' ] of
[] -> lex1 "$" s'
ok -> ok
lex0 s' = lex1 [] s'
lex1 "" "" = []
lex1 acc "" = [(Ordinary (reverse acc), "")]
lex1 acc ('$':'$':s) = lex1 acc ('$':s)
lex1 acc ('$':s) = [(Ordinary (reverse acc), '$':s)]
lex1 acc (c:s) = lex1 (c:acc) s
readList [] = [([],"")]
readList s = [ (component:components, s'')
| (component, s') <- reads s
, (components, s'') <- readList s' ]
instance Show PathTemplate where
show (PathTemplate template) = show (show template)
instance Read PathTemplate where
readsPrec p s = [ (PathTemplate template, s')
| (path, s') <- readsPrec p s
, (template, "") <- reads path ]
getWindowsProgramFilesDir :: IO FilePath
getWindowsProgramFilesDir = do
#if mingw32_HOST_OS || mingw32_TARGET_OS
m <- shGetFolderPath csidl_PROGRAM_FILES
#else
let m = Nothing
#endif
return (fromMaybe "C:\\Program Files" m)
#if mingw32_HOST_OS || mingw32_TARGET_OS
shGetFolderPath :: CInt -> IO (Maybe FilePath)
shGetFolderPath n =
# if __HUGS__
return Nothing
# else
allocaBytes long_path_size $ \pPath -> do
r <- c_SHGetFolderPath nullPtr n nullPtr 0 pPath
if (r /= 0)
then return Nothing
else do s <- peekCString pPath; return (Just s)
where
long_path_size = 1024
# endif
csidl_PROGRAM_FILES :: CInt
csidl_PROGRAM_FILES = 0x0026
foreign import stdcall unsafe "shlobj.h SHGetFolderPathA"
c_SHGetFolderPath :: Ptr ()
-> CInt
-> Ptr ()
-> CInt
-> CString
-> IO CInt
#endif
#if !(__HUGS__ || __GLASGOW_HASKELL__ > 606)
dropDrive :: FilePath -> FilePath
dropDrive (c:cs) | isPathSeparator c = cs
dropDrive (_:':':c:cs) | isWindows
&& isPathSeparator c = cs
dropDrive (_:':':cs) | isWindows = cs
dropDrive cs = cs
isWindows :: Bool
isWindows = case buildOS of
Windows -> True
_ -> False
#endif