1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
{-# LANGUAGE CPP #-}
{-# LANGUAGE TupleSections #-}
module SensibleDir where
import System.Directory
#ifdef VERSION_directory_xdg
import System.DirectoryXdg
#endif
import System.FilePath
import System.IO
import System.Info
import System.Environment
import Control.Arrow
import Control.Applicative
import Data.List
runningInBuildDir :: IO Bool
runningInBuildDir = any (=="build") . take 2 . reverse . splitDirectories . takeDirectory <$> getExecutablePath
runningInCabalDir :: IO Bool
runningInCabalDir = any (==".cabal") . take 2 . reverse . splitDirectories . takeDirectory <$> getExecutablePath
runningInSysBin = do
exedir <- splitDirectories . takeDirectory <$> getExecutablePath
return $
case take 3 (drop 1 exedir) of
["bin"] -> True
["sbin"] -> True
["usr","bin"] -> True
["usr","sbin"] -> True
_ -> False
runningInLocalBin = do
exedir <- splitDirectories . takeDirectory <$> getExecutablePath
return $
case take 3 (drop 1 exedir) of
["usr","local","bin"] -> True
["usr","local","sbin"] -> True
_ -> False
-- scenarios
data InstallScenario = PrefixInstall
-- ^ installed in /opt or some other prefix, or running inside source folder
| SystemInstall
-- ^ installed system wide
| LocalInstall
-- ^ installed in /usr/local/
| UserInstall
-- ^ installed for a given user
| NotInstalled
-- ^ not installed, built by cabal or make, running from dist folder
deriving (Show,Enum,Eq)
-- | careful, assumes unix-style FHS
-- windows aware contributions welcome
-- Detects the likely install scenario based on the path to the exectuable.
detectLikelyInstall :: IO InstallScenario
detectLikelyInstall = do
bBuildDir <- fmap (,NotInstalled) runningInBuildDir
bCabalDir <- fmap (,UserInstall) runningInCabalDir
bLocalDir <- fmap (,LocalInstall) runningInLocalBin
bSysDir <- fmap (,SystemInstall) runningInSysBin
case dropWhile (not . fst) [bBuildDir,bCabalDir,bLocalDir,bSysDir] of
(True,scenario):_ -> return scenario
_ -> return PrefixInstall
isUnix os = not $ "mingw" `isPrefixOf` os
-- | Try to get the cache directory appropriate to the install scenario.
-- On windows it just calls getAppUserDataDirectory and appends "cache".
-- (windows behavior subject to change, contributions welcome)
sensibleCacheDir suffix = do
let ifunix x = if isUnix os then x
else PrefixInstall
install <- ifunix <$> detectLikelyInstall
case install of
PrefixInstall -> do
if isUnix os
then do
dirs <- splitDirectories . takeDirectory <$> getExecutablePath
homedir <- getHomeDirectory
let rdir = reverse dirs
case take 1 rdir of
["bin"] -> do
let dir = foldl1 combine $ reverse (drop 1 rdir)
if homedir == dir then getXdgDirectory XdgCache suffix
else return (dir </> "cache" </> suffix)
_ -> do
let dir = foldl1 combine dirs
if homedir == dir then getXdgDirectory XdgCache suffix
else return (dir </> "cache" </> suffix)
else getAppUserDataDirectory ("cache" </> suffix)
SystemInstall -> return ("/var/cache" </> suffix)
LocalInstall -> return ("/var/local/cache" </> suffix)
UserInstall -> getXdgDirectory XdgCache suffix
NotInstalled -> do
dir <- splitDirectories . takeDirectory <$> getExecutablePath
let rdir = reverse dir
return . foldl1 combine $ reverse ("cache": drop 2 rdir)
-- | like 'sensibleCacheDir', but creates it if needed
sensibleCacheDirCreateIfMissing suffix = do
dir <- sensibleCacheDir suffix
createDirectoryIfMissing True dir
|