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
35 , logExtraProgramSearchPath
36 , logExtraProgramOverrideEnv
42 import Distribution
.Compat
.Prelude
45 import Distribution
.Compat
.Environment
46 import Distribution
.Simple
.Program
.Types
47 import Distribution
.Simple
.Utils
48 import Distribution
.System
49 import Distribution
.Verbosity
51 import qualified System
.Directory
as Directory
54 import System
.FilePath as FilePath
62 #if defined
(mingw32_HOST_OS
)
63 import qualified System
.Win32
as Win32
66 defaultProgramSearchPath
:: ProgramSearchPath
67 defaultProgramSearchPath
= [ProgramSearchPathDefault
]
69 logExtraProgramSearchPath
73 logExtraProgramSearchPath verbosity extraPaths
=
74 info verbosity
. unlines $
75 "Including the following directories in PATH:"
76 : map ("- " ++) extraPaths
78 logExtraProgramOverrideEnv
80 -> [(String, Maybe String)]
82 logExtraProgramOverrideEnv verbosity extraEnv
=
83 info verbosity
. unlines $
84 "Including the following environment variable overrides:"
85 : [ "- " ++ case mbVal
of
86 Nothing
-> "unset " ++ var
87 Just val
-> var
++ "=" ++ val
88 |
(var
, mbVal
) <- extraEnv
91 findProgramOnSearchPath
95 -> IO (Maybe (FilePath, [FilePath]))
96 findProgramOnSearchPath verbosity searchpath prog
= do
97 debug verbosity
$ "Searching for " ++ prog
++ " in path."
98 res
<- tryPathElems
[] searchpath
100 Nothing
-> debug verbosity
("Cannot find " ++ prog
++ " on the path")
101 Just
(path
, _
) -> debug verbosity
("Found " ++ prog
++ " at " ++ path
)
106 -> [ProgramSearchPathEntry
]
107 -> IO (Maybe (FilePath, [FilePath]))
108 tryPathElems _
[] = return Nothing
109 tryPathElems tried
(pe
: pes
) = do
110 res
<- tryPathElem pe
112 (Nothing
, notfoundat
) -> tryPathElems
(notfoundat
: tried
) pes
113 (Just foundat
, notfoundat
) -> return (Just
(foundat
, alltried
))
115 alltried
= concat (reverse (notfoundat
: tried
))
117 tryPathElem
:: ProgramSearchPathEntry
-> IO (Maybe FilePath, [FilePath])
118 tryPathElem
(ProgramSearchPathDir dir
) =
119 findFirstExe
[dir
</> prog
<.> ext | ext
<- exeExtensions
]
120 -- On windows, getSystemSearchPath is not guaranteed 100% correct so we
121 -- use findExecutable and then approximate the not-found-at locations.
122 tryPathElem ProgramSearchPathDefault | buildOS
== Windows
= do
123 mExe
<- firstJustM
[findExecutable
(prog
<.> ext
) | ext
<- exeExtensions
]
124 syspath
<- getSystemSearchPath
127 let notfoundat
= [dir
</> prog | dir
<- syspath
]
128 in return (Nothing
, notfoundat
)
130 let founddir
= takeDirectory foundat
133 | dir
<- takeWhile (/= founddir
) syspath
135 return (Just foundat
, notfoundat
)
137 -- On other OSs we can just do the simple thing
138 tryPathElem ProgramSearchPathDefault
= do
139 dirs
<- getSystemSearchPath
140 findFirstExe
[dir
</> prog
<.> ext | dir
<- dirs
, ext
<- exeExtensions
]
142 findFirstExe
:: [FilePath] -> IO (Maybe FilePath, [FilePath])
145 go fs
' [] = return (Nothing
, reverse fs
')
147 isExe
<- doesExecutableExist f
149 then return (Just f
, reverse fs
')
152 -- Helper for evaluating actions until the first one returns 'Just'
153 firstJustM
:: Monad m
=> [m
(Maybe a
)] -> m
(Maybe a
)
154 firstJustM
[] = return Nothing
155 firstJustM
(ma
: mas
) = do
159 Nothing
-> firstJustM mas
161 -- | Adds some paths to the "PATH" entry in the key-value environment provided
162 -- or if there is none, looks up @$PATH@ in the real environment.
165 -> [(String, Maybe String)]
167 -> IO [(String, Maybe String)]
168 getExtraPathEnv _ _
[] = return []
169 getExtraPathEnv verbosity env extras
= do
170 mb_path
<- case lookup "PATH" env
of
172 Nothing
-> lookupEnv
"PATH"
173 logExtraProgramSearchPath verbosity extras
174 let extra
= intercalate
[searchPathSeparator
] extras
175 path
' = case mb_path
of
177 Just path
-> extra
++ searchPathSeparator
: path
178 return [("PATH", Just path
')]
180 -- | Interpret a 'ProgramSearchPath' to construct a new @$PATH@ env var.
181 -- Note that this is close but not perfect because on Windows the search
182 -- algorithm looks at more than just the @%PATH%@.
183 programSearchPathAsPATHVar
:: ProgramSearchPath
-> IO String
184 programSearchPathAsPATHVar searchpath
= do
185 ess
<- traverse getEntries searchpath
186 return (intercalate
[searchPathSeparator
] (concat ess
))
188 getEntries
(ProgramSearchPathDir dir
) = return [dir
]
189 getEntries ProgramSearchPathDefault
= do
190 env
<- getEnvironment
191 return (maybe [] splitSearchPath
(lookup "PATH" env
))
193 -- | Get the system search path. On Unix systems this is just the @$PATH@ env
194 -- var, but on windows it's a bit more complicated.
195 getSystemSearchPath
:: IO [FilePath]
196 getSystemSearchPath
= fmap nub $ do
197 #if defined
(mingw32_HOST_OS
)
198 processdir
<- takeDirectory `
fmap` Win32
.getModuleFileName Win32
.nullHANDLE
199 currentdir
<- Win32
.getCurrentDirectory
200 systemdir
<- Win32
.getSystemDirectory
201 windowsdir
<- Win32
.getWindowsDirectory
202 pathdirs
<- FilePath.getSearchPath
203 let path
= processdir
: currentdir
204 : systemdir
: windowsdir
208 FilePath.getSearchPath
211 #ifdef MIN_VERSION_directory
212 #if MIN_VERSION_directory
(1,2,1)
213 #define HAVE_directory_121
217 findExecutable
:: FilePath -> IO (Maybe FilePath)
218 #ifdef HAVE_directory_121
219 findExecutable
= Directory
.findExecutable
221 findExecutable prog
= do
222 -- With directory < 1.2.1 'findExecutable' doesn't check that the path
223 -- really refers to an executable.
224 mExe
<- Directory
.findExecutable prog
227 exeExists
<- doesExecutableExist exe
234 -- | Make a simple named program.
236 -- By default we'll just search for it in the path and not try to find the
237 -- version name. You can override these behaviours if necessary, eg:
239 -- > (simpleProgram "foo") { programFindLocation = ... , programFindVersion ... }
240 simpleProgram
:: String -> Program
244 , programFindLocation
= \v p
-> findProgramOnSearchPath v p name
245 , programFindVersion
= \_ _
-> return Nothing
246 , programPostConf
= \_ p
-> return p
247 , programNormaliseArgs
= \_ _
-> id