2 {-# LANGUAGE DeriveDataTypeable #-}
3 {-# LANGUAGE DeriveGeneric #-}
4 {-# LANGUAGE FlexibleContexts #-}
5 {-# LANGUAGE RankNTypes #-}
7 -----------------------------------------------------------------------------
10 -- Module : Distribution.Simple.Program.Find
11 -- Copyright : Duncan Coutts 2013
13 -- Maintainer : cabal-devel@haskell.org
14 -- Portability : portable
16 -- A somewhat extended notion of the normal program search path concept.
18 -- Usually when finding executables we just want to look in the usual places
19 -- using the OS's usual method for doing so. In Haskell the normal OS-specific
20 -- method is captured by 'findExecutable'. On all common OSs that makes use of
21 -- a @PATH@ environment variable, (though on Windows it is not just the @PATH@).
23 -- However it is sometimes useful to be able to look in additional locations
24 -- without having to change the process-global @PATH@ environment variable.
25 -- So we need an extension of the usual 'findExecutable' that can look in
26 -- additional locations, either before, after or instead of the normal OS
28 module Distribution
.Simple
.Program
.Find
29 ( -- * Program search path
31 , ProgramSearchPathEntry
(..)
32 , defaultProgramSearchPath
33 , findProgramOnSearchPath
34 , programSearchPathAsPATHVar
38 import Distribution
.Compat
.Prelude
41 import Distribution
.Compat
.Environment
42 import Distribution
.Simple
.Utils
43 import Distribution
.System
44 import Distribution
.Verbosity
46 import qualified System
.Directory
as Directory
49 import System
.FilePath as FilePath
57 #if defined
(mingw32_HOST_OS
)
58 import qualified System
.Win32
as Win32
61 -- | A search path to use when locating executables. This is analogous
62 -- to the unix @$PATH@ or win32 @%PATH%@ but with the ability to use
63 -- the system default method for finding executables ('findExecutable' which
64 -- on unix is simply looking on the @$PATH@ but on win32 is a bit more
67 -- The default to use is @[ProgSearchPathDefault]@ but you can add extra dirs
68 -- either before, after or instead of the default, e.g. here we add an extra
69 -- dir to search after the usual ones.
71 -- > ['ProgramSearchPathDefault', 'ProgramSearchPathDir' dir]
72 type ProgramSearchPath
= [ProgramSearchPathEntry
]
74 data ProgramSearchPathEntry
76 ProgramSearchPathDir
FilePath
77 |
-- | The system default
78 ProgramSearchPathDefault
79 deriving (Eq
, Generic
, Typeable
)
81 instance Binary ProgramSearchPathEntry
82 instance Structured ProgramSearchPathEntry
84 defaultProgramSearchPath
:: ProgramSearchPath
85 defaultProgramSearchPath
= [ProgramSearchPathDefault
]
87 findProgramOnSearchPath
91 -> IO (Maybe (FilePath, [FilePath]))
92 findProgramOnSearchPath verbosity searchpath prog
= do
93 debug verbosity
$ "Searching for " ++ prog
++ " in path."
94 res
<- tryPathElems
[] searchpath
96 Nothing
-> debug verbosity
("Cannot find " ++ prog
++ " on the path")
97 Just
(path
, _
) -> debug verbosity
("Found " ++ prog
++ " at " ++ path
)
102 -> [ProgramSearchPathEntry
]
103 -> IO (Maybe (FilePath, [FilePath]))
104 tryPathElems _
[] = return Nothing
105 tryPathElems tried
(pe
: pes
) = do
106 res
<- tryPathElem pe
108 (Nothing
, notfoundat
) -> tryPathElems
(notfoundat
: tried
) pes
109 (Just foundat
, notfoundat
) -> return (Just
(foundat
, alltried
))
111 alltried
= concat (reverse (notfoundat
: tried
))
113 tryPathElem
:: ProgramSearchPathEntry
-> IO (Maybe FilePath, [FilePath])
114 tryPathElem
(ProgramSearchPathDir dir
) =
115 findFirstExe
[dir
</> prog
<.> ext | ext
<- exeExtensions
]
116 -- On windows, getSystemSearchPath is not guaranteed 100% correct so we
117 -- use findExecutable and then approximate the not-found-at locations.
118 tryPathElem ProgramSearchPathDefault | buildOS
== Windows
= do
119 mExe
<- firstJustM
[findExecutable
(prog
<.> ext
) | ext
<- exeExtensions
]
120 syspath
<- getSystemSearchPath
123 let notfoundat
= [dir
</> prog | dir
<- syspath
]
124 in return (Nothing
, notfoundat
)
126 let founddir
= takeDirectory foundat
129 | dir
<- takeWhile (/= founddir
) syspath
131 return (Just foundat
, notfoundat
)
133 -- On other OSs we can just do the simple thing
134 tryPathElem ProgramSearchPathDefault
= do
135 dirs
<- getSystemSearchPath
136 findFirstExe
[dir
</> prog
<.> ext | dir
<- dirs
, ext
<- exeExtensions
]
138 findFirstExe
:: [FilePath] -> IO (Maybe FilePath, [FilePath])
141 go fs
' [] = return (Nothing
, reverse fs
')
143 isExe
<- doesExecutableExist f
145 then return (Just f
, reverse fs
')
148 -- Helper for evaluating actions until the first one returns 'Just'
149 firstJustM
:: Monad m
=> [m
(Maybe a
)] -> m
(Maybe a
)
150 firstJustM
[] = return Nothing
151 firstJustM
(ma
: mas
) = do
155 Nothing
-> firstJustM mas
157 -- | Interpret a 'ProgramSearchPath' to construct a new @$PATH@ env var.
158 -- Note that this is close but not perfect because on Windows the search
159 -- algorithm looks at more than just the @%PATH%@.
160 programSearchPathAsPATHVar
:: ProgramSearchPath
-> IO String
161 programSearchPathAsPATHVar searchpath
= do
162 ess
<- traverse getEntries searchpath
163 return (intercalate
[searchPathSeparator
] (concat ess
))
165 getEntries
(ProgramSearchPathDir dir
) = return [dir
]
166 getEntries ProgramSearchPathDefault
= do
167 env
<- getEnvironment
168 return (maybe [] splitSearchPath
(lookup "PATH" env
))
170 -- | Get the system search path. On Unix systems this is just the @$PATH@ env
171 -- var, but on windows it's a bit more complicated.
172 getSystemSearchPath
:: IO [FilePath]
173 getSystemSearchPath
= fmap nub $ do
174 #if defined
(mingw32_HOST_OS
)
175 processdir
<- takeDirectory `
fmap` Win32
.getModuleFileName Win32
.nullHANDLE
176 currentdir
<- Win32
.getCurrentDirectory
177 systemdir
<- Win32
.getSystemDirectory
178 windowsdir
<- Win32
.getWindowsDirectory
179 pathdirs
<- FilePath.getSearchPath
180 let path
= processdir
: currentdir
181 : systemdir
: windowsdir
185 FilePath.getSearchPath
188 #ifdef MIN_VERSION_directory
189 #if MIN_VERSION_directory
(1,2,1)
190 #define HAVE_directory_121
194 findExecutable
:: FilePath -> IO (Maybe FilePath)
195 #ifdef HAVE_directory_121
196 findExecutable
= Directory
.findExecutable
198 findExecutable prog
= do
199 -- With directory < 1.2.1 'findExecutable' doesn't check that the path
200 -- really refers to an executable.
201 mExe
<- Directory
.findExecutable prog
204 exeExists
<- doesExecutableExist exe