| ... |
... |
@@ -16,6 +16,7 @@ module GHC.Toolchain.Program |
|
16
|
16
|
, _poPath
|
|
17
|
17
|
, _poFlags
|
|
18
|
18
|
, findProgram
|
|
|
19
|
+ , findLlvmProgram
|
|
19
|
20
|
-- * Compiler programs
|
|
20
|
21
|
, compile
|
|
21
|
22
|
, supportsTarget
|
| ... |
... |
@@ -23,7 +24,8 @@ module GHC.Toolchain.Program |
|
23
|
24
|
|
|
24
|
25
|
import Control.Monad
|
|
25
|
26
|
import Control.Monad.IO.Class
|
|
26
|
|
-import Data.List (intercalate, isPrefixOf)
|
|
|
27
|
+import Data.Char (isDigit)
|
|
|
28
|
+import Data.List (find, intercalate, isPrefixOf, tails)
|
|
27
|
29
|
import Data.Maybe
|
|
28
|
30
|
import System.FilePath
|
|
29
|
31
|
import System.Directory
|
| ... |
... |
@@ -131,17 +133,13 @@ programFromOpt userSpec path flags = Program { prgPath = fromMaybe path (poPath |
|
131
|
133
|
-- in the given list of candidates.
|
|
132
|
134
|
--
|
|
133
|
135
|
-- If the 'ProgOpt' program flags are unspecified the program will have an empty list of flags.
|
|
134
|
|
-findProgram :: String
|
|
|
136
|
+findProgram :: String -- ^ The program description
|
|
135
|
137
|
-> ProgOpt -- ^ path provided by user
|
|
136
|
138
|
-> [FilePath] -- ^ candidate names
|
|
137
|
139
|
-> M Program
|
|
138
|
140
|
findProgram description userSpec candidates
|
|
139
|
|
- | Just path <- poPath userSpec = do
|
|
140
|
|
- let err =
|
|
141
|
|
- [ "Failed to find " ++ description ++ "."
|
|
142
|
|
- , "Looked for user-specified program '" ++ path ++ "' in the system search path."
|
|
143
|
|
- ]
|
|
144
|
|
- toProgram <$> find_it path <|> throwEs err
|
|
|
141
|
+ | Just findProgramFromProgOpts <- maybeFindProgramFromProgOpts description userSpec
|
|
|
142
|
+ = findProgramFromProgOpts
|
|
145
|
143
|
|
|
146
|
144
|
| otherwise = do
|
|
147
|
145
|
env <- getEnv
|
| ... |
... |
@@ -154,17 +152,91 @@ findProgram description userSpec candidates |
|
154
|
152
|
[ "Failed to find " ++ description ++ "."
|
|
155
|
153
|
, "Looked for one of " ++ show candidates' ++ " in the system search path."
|
|
156
|
154
|
]
|
|
157
|
|
- toProgram <$> oneOf' err (map find_it candidates') <|> throwEs err
|
|
|
155
|
+ path <- oneOf' err (map findExecutableErr candidates')
|
|
|
156
|
+ return Program { prgPath = path, prgFlags = fromMaybe [] (poFlags userSpec) }
|
|
|
157
|
+
|
|
|
158
|
+-- Note that @configure.ac@ checks these llvm version constants (using @sed@) to
|
|
|
159
|
+-- ensure they are the same as the @$LlvmMinVersion@ and @$LlvmMaxVersion@
|
|
|
160
|
+-- defined in @configure.ac@.
|
|
|
161
|
+
|
|
|
162
|
+-- Min llvm version (inclusive)
|
|
|
163
|
+minLlvmVersion :: Int
|
|
|
164
|
+minLlvmVersion = 13
|
|
|
165
|
+
|
|
|
166
|
+-- Max llvm version (exclusive)
|
|
|
167
|
+maxLlvmVersionExcl :: Int
|
|
|
168
|
+maxLlvmVersionExcl = 23
|
|
|
169
|
+
|
|
|
170
|
+-- Max llvm version (inclusive)
|
|
|
171
|
+maxLlvmVersion :: Int
|
|
|
172
|
+maxLlvmVersion = maxLlvmVersionExcl - 1
|
|
|
173
|
+
|
|
|
174
|
+-- | Tries to find an llvm program with the highest supported llvm versions.
|
|
|
175
|
+-- This searches for an explicitly versioned executable (postfixed with the llvm version).
|
|
|
176
|
+-- If an explicitly versioned executable is not found, then this searches for a non-explicitly
|
|
|
177
|
+-- versioned executable. If supported, the llvm version is checked by passing @--version@ to
|
|
|
178
|
+-- the executable.
|
|
|
179
|
+--
|
|
|
180
|
+-- If the 'ProgOpt' program flags are unspecified the program will have an empty list of flags.
|
|
|
181
|
+findLlvmProgram :: String -- ^ The llvm program description
|
|
|
182
|
+ -> ProgOpt -- ^ path provided by user
|
|
|
183
|
+ -> FilePath -- ^ Candidate name
|
|
|
184
|
+ -> Bool -- ^ True if the program supports the @--version@ flag and the output
|
|
|
185
|
+ -- contains the llvm version number in the form @version <LLVM_VERSION>@
|
|
|
186
|
+ -> M Program
|
|
|
187
|
+findLlvmProgram description userSpec candidate checkVersion
|
|
|
188
|
+ | Just findProgramFromProgOpts <- maybeFindProgramFromProgOpts description userSpec
|
|
|
189
|
+ = findProgramFromProgOpts
|
|
|
190
|
+
|
|
|
191
|
+ | otherwise = do
|
|
|
192
|
+ program <- findProgram description userSpec (versionedCandidates ++ [candidate])
|
|
|
193
|
+ when checkVersion $ do
|
|
|
194
|
+ -- Extract the version from the `--version` output
|
|
|
195
|
+ versionOutput <- readProgramStdout program ["--version"]
|
|
|
196
|
+ let versionStrPrefix = "version "
|
|
|
197
|
+
|
|
|
198
|
+ versionMay :: Maybe Int
|
|
|
199
|
+ versionMay = fmap (read . takeWhile isDigit . drop (length versionStrPrefix))
|
|
|
200
|
+ . find (versionStrPrefix `isPrefixOf`)
|
|
|
201
|
+ $ tails versionOutput
|
|
|
202
|
+
|
|
|
203
|
+ errSupportedVersions = prgPath program <> ": We only support llvm " <> show minLlvmVersion <> " upto " <> show maxLlvmVersion <> " (non-inclusive)"
|
|
|
204
|
+ case versionMay of
|
|
|
205
|
+ Nothing -> throwE (errSupportedVersions <> " (no version found).")
|
|
|
206
|
+ Just version -> when
|
|
|
207
|
+ (version < minLlvmVersion || version > maxLlvmVersion)
|
|
|
208
|
+ (throwE $ errSupportedVersions <> " (found " <> show version <> ").")
|
|
|
209
|
+ return program
|
|
158
|
210
|
where
|
|
159
|
|
- toProgram path = Program { prgPath = path, prgFlags = fromMaybe [] (poFlags userSpec) }
|
|
160
|
|
-
|
|
161
|
|
- find_it name = do
|
|
162
|
|
- r <- liftIO $ findExecutable name
|
|
163
|
|
- case r of
|
|
164
|
|
- Nothing -> throwE $ name ++ " not found in search path"
|
|
165
|
|
- -- Use the given `prgPath` or candidate name rather than the
|
|
166
|
|
- -- absolute path returned by `findExecutable`.
|
|
167
|
|
- Just _x -> return name
|
|
|
211
|
+ versionedCandidates =
|
|
|
212
|
+ [ candidate <> postfix
|
|
|
213
|
+ | llvmVersion <- show <$> [maxLlvmVersion, maxLlvmVersion-1 .. minLlvmVersion]
|
|
|
214
|
+ , postfix <-
|
|
|
215
|
+ [ "-" <> llvmVersion
|
|
|
216
|
+ , "-" <> llvmVersion <> ".0"
|
|
|
217
|
+ , llvmVersion
|
|
|
218
|
+ ]
|
|
|
219
|
+ ]
|
|
|
220
|
+
|
|
|
221
|
+maybeFindProgramFromProgOpts :: String -> ProgOpt -> Maybe (M Program)
|
|
|
222
|
+maybeFindProgramFromProgOpts description userSpec = case poPath userSpec of
|
|
|
223
|
+ Nothing -> Nothing
|
|
|
224
|
+ Just path -> Just $ do
|
|
|
225
|
+ let err =
|
|
|
226
|
+ [ "Failed to find " ++ description ++ "."
|
|
|
227
|
+ , "Looked for user-specified program '" ++ path ++ "' in the system search path."
|
|
|
228
|
+ ]
|
|
|
229
|
+ path' <- findExecutableErr path <|> throwEs err
|
|
|
230
|
+ return Program { prgPath = path', prgFlags = fromMaybe [] (poFlags userSpec) }
|
|
|
231
|
+
|
|
|
232
|
+findExecutableErr :: String -> M FilePath
|
|
|
233
|
+findExecutableErr name = do
|
|
|
234
|
+ r <- liftIO $ findExecutable name
|
|
|
235
|
+ case r of
|
|
|
236
|
+ Nothing -> throwE $ name ++ " not found in search path"
|
|
|
237
|
+ -- Use the given `prgPath` or candidate name rather than the
|
|
|
238
|
+ -- absolute path returned by `findExecutable`.
|
|
|
239
|
+ Just _x -> return name
|
|
168
|
240
|
|
|
169
|
241
|
-------------------- Compiling utilities --------------------
|
|
170
|
242
|
|