0 | module Idris.SetOptions
2 | import Compiler.Common
5 | import Core.Directory
8 | import Libraries.Utils.Path
9 | import Libraries.Data.List.Extra
11 | import Idris.CommandLine
12 | import Idris.Package.Types
15 | import Idris.ProcessIdr
18 | import Idris.Version
24 | import System.Directory
30 | constructor MkPkgDir
36 | version : Maybe PkgVersion
38 | ttcVersions : List Int
40 | record QualifiedPkgDir where
41 | constructor MkQualifiedPkgDir
49 | pkgDir : (dirName : String) -> (ttcDirs : List Int) -> PkgDir
50 | pkgDir dirName ttcDirs =
57 | case unsnoc $
split (== '-') dirName of
58 | (Nil, last) => MkPkgDir dirName last Nothing ttcDirs
60 | case toVersion last of
61 | Just v => MkPkgDir dirName (concat $
intersperse "-" init) (Just v) ttcDirs
62 | Nothing => MkPkgDir dirName dirName Nothing ttcDirs
64 | toVersion : String -> Maybe PkgVersion
65 | toVersion = map MkPkgVersion
66 | . traverse parsePositive
69 | listDirOrEmpty : String -> IO (List String)
70 | listDirOrEmpty dir = either (const []) id <$> listDir dir
72 | getPackageDirs : String -> IO (List PkgDir)
73 | getPackageDirs dname = do
74 | packageDirNames <- listDirOrEmpty dname
75 | traverse (\d => pkgDir d <$> ttcVersions d) packageDirNames
77 | ttcVersions : String -> IO (List Int)
78 | ttcVersions dir = catMaybes . map parsePositive <$> listDirOrEmpty (dname </> dir)
88 | String -> String -> PkgVersionBounds ->
89 | Core (List (String, Maybe PkgVersion))
90 | candidateDirs dname pkgName bounds = do
91 | dirs <- coreLift (getPackageDirs dname)
92 | checkedDirs <- traverse check dirs
93 | pure (catMaybes checkedDirs)
96 | data CandidateError = OutOfBounds | TTCMismatch
98 | checkNameAndBounds : PkgDir -> Either CandidateError PkgDir
99 | checkNameAndBounds pkg =
100 | if pkg.pkgName == pkgName && inBounds pkg.version bounds
102 | else Left OutOfBounds
104 | checkTTCVersion : PkgDir -> Either CandidateError PkgDir
105 | checkTTCVersion pkg =
106 | if ttcVersion `elem` pkg.ttcVersions
108 | else Left TTCMismatch
110 | unpack : PkgDir -> (String, Maybe PkgVersion)
111 | unpack (MkPkgDir dirName pkgName ver _) =
112 | ((dname </> dirName), ver)
114 | check : PkgDir -> Core (Maybe (String, Maybe PkgVersion))
116 | let checkedPkg = checkNameAndBounds pkg >>= checkTTCVersion
119 | Right pkg' => pure . Just $
unpack pkg'
120 | Left OutOfBounds => pure Nothing
121 | Left TTCMismatch => do
122 | let pkgVersion = maybe "unversioned" (\v => "version \{show v} of") pkg.version
123 | recordWarning $ GenericWarn EmptyFC $
125 | Found \{pkgVersion} package \{pkg.pkgName} installed with no compatible binaries for the current Idris2 compiler.
127 | Reinstall \{pkg.pkgName} with the current Idris2 compiler to resolve the issue.
144 | PkgVersionBounds ->
145 | Core (List (String, Maybe PkgVersion))
146 | findPkgDirs p bounds = do
147 | localdir <- pkgLocalDirectory
150 | locFiles <- candidateDirs localdir p bounds
153 | pkgFiles <- traverse (\d => candidateDirs (show d) p bounds) d.package_search_paths
156 | let allFiles = if isNil locFiles
157 | then concat pkgFiles
160 | pure $
sortBy (\x, y => compare (snd y) (snd x)) allFiles
166 | PkgVersionBounds ->
167 | Core (Maybe String)
168 | findPkgDir p bounds = do
170 | [] <- findPkgDirs p bounds | ((p,_) :: _) => pure (Just p)
175 | if defs.options.session.ignoreMissingPkg
177 | else throw (CantFindPackage (p ++ " (" ++ show bounds ++ ")"))
182 | addPkgDir : {auto c : Ref Ctxt Defs} ->
183 | String -> PkgVersionBounds -> Core ()
184 | addPkgDir p bounds = do
185 | Just p <- findPkgDir p bounds
186 | | Nothing => pure ()
189 | visiblePackages : String -> IO (List QualifiedPkgDir)
190 | visiblePackages dir = map (MkQualifiedPkgDir dir) <$> filter viable <$> getPackageDirs dir
191 | where notHidden : PkgDir -> Bool
192 | notHidden = not . isPrefixOf "." . pkgName
194 | notDenylisted : PkgDir -> Bool
195 | notDenylisted = not . flip elem (the (List String) ["include", "lib", "support", "refc"]) . pkgName
197 | viable : PkgDir -> Bool
198 | viable p = notHidden p && notDenylisted p
200 | findPackages : {auto c : Ref Ctxt Defs} -> Core (List QualifiedPkgDir)
203 | pkgPathPkgs <- coreLift $
traverse (\d => visiblePackages $
show d) d.package_search_paths
205 | localPkgs <- coreLift $
visiblePackages !pkgLocalDirectory
206 | pure $
localPkgs ++ join pkgPathPkgs
208 | listPackages : {auto c : Ref Ctxt Defs} ->
209 | {auto o : Ref ROpts REPLOpts} ->
212 | = do pkgs <- sortBy (compare `on` (pkgName . pkgDir)) <$> findPackages
213 | printIdrisTTCVersion
214 | traverse_ (iputStrLn . pkgDesc) pkgs
216 | printIdrisTTCVersion : Core ()
217 | printIdrisTTCVersion = iputStrLn $
218 | pretty0 "Idris2 TTC Version: \{show ttcVersion}" <+> line <+>
219 | (replicateChar 5 '─')
221 | pkgTTCVersions : PkgDir -> Doc IdrisAnn
222 | pkgTTCVersions (MkPkgDir _ _ _ ttcVersions) =
223 | pretty0 "├ TTC Versions:" <++> prettyTTCVersions
225 | annotate : Int -> Doc IdrisAnn
227 | if version == ttcVersion
228 | then pretty0 $
show version
229 | else warning ((pretty0 $
show version) <++> (parens "incompatible"))
231 | prettyTTCVersions : Doc IdrisAnn
232 | prettyTTCVersions = (concatWith (\x,y => x <+> "," <++> y)) $
annotate <$> sort ttcVersions
234 | pkgPath : String -> Doc IdrisAnn
235 | pkgPath path = pretty0 "└ \{path}"
237 | extraInfo : QualifiedPkgDir -> Doc IdrisAnn
238 | extraInfo (MkQualifiedPkgDir path pkg) =
239 | let extra = indent 2 $
240 | pkgTTCVersions pkg <+> line <+>
244 | pkgDesc : QualifiedPkgDir -> Doc IdrisAnn
245 | pkgDesc pkg@(MkQualifiedPkgDir _ (MkPkgDir _ pkgName version _)) =
246 | pretty0 pkgName <++> parens (pretty0 $
maybe "unversioned" show version)
249 | dirOption : {auto c : Ref Ctxt Defs} ->
250 | {auto o : Ref ROpts REPLOpts} ->
251 | Dirs -> DirCommand -> Core ()
252 | dirOption dirs LibDir
253 | = coreLift $
putStrLn
254 | (prefix_dir dirs </> "idris2-" ++ showVersion False version)
255 | dirOption dirs BlodwenPaths
256 | = iputStrLn $
pretty0 (toString dirs)
257 | dirOption dirs Prefix
258 | = coreLift $
putStrLn (prefix_dir dirs)
264 | findIpkg : Core (List String)
266 | do Just srcdir <- coreLift currentDir
267 | | Nothing => throw (InternalError "Can't get current directory")
268 | Right fs <- coreLift $
listDir srcdir
269 | | Left err => pure []
270 | pure $
filter (".ipkg" `isSuffixOf`) fs
273 | prefixOnly : String -> List String -> List String
274 | prefixOnly x = sortedNub . filter (\s => x /= s && isPrefixOf x s)
278 | prefixOnlyIfNonEmpty : String -> List String -> List String
279 | prefixOnlyIfNonEmpty "--" = id
280 | prefixOnlyIfNonEmpty s = prefixOnly s
284 | codegens : {auto c : Ref Ctxt Defs} -> Core (List String)
285 | codegens = map fst . availableCGs . options <$> get Ctxt
287 | logLevels : List String
288 | logLevels = map fst knownTopics >>= prefixes . forget . split (== '.')
289 | where prefixes : List String -> List String
291 | prefixes (x :: xs) = x :: map (x ++ "." ++) (prefixes xs)
298 | opts : {auto c : Ref Ctxt Defs} -> String -> String -> Core (List String)
299 | opts "--" "idris2" = pure optionFlags
302 | opts x "--cg" = prefixOnlyIfNonEmpty x <$> codegens
303 | opts x "--codegen" = prefixOnlyIfNonEmpty x <$> codegens
306 | opts x "-p" = prefixOnlyIfNonEmpty x . (map $
pkgName . pkgDir) <$> findPackages
307 | opts x "--package" = prefixOnlyIfNonEmpty x . (map $
pkgName . pkgDir) <$> findPackages
310 | opts x "--log" = pure $
prefixOnlyIfNonEmpty x logLevels
313 | opts "--" "-o" = pure []
314 | opts "--" "--output" = pure []
315 | opts "--" "--source-dir" = pure []
316 | opts "--" "--build-dir" = pure []
317 | opts "--" "--output-dir" = pure []
320 | opts x "--build" = prefixOnlyIfNonEmpty x <$> findIpkg
321 | opts x "--dump-ipkg-json" = prefixOnlyIfNonEmpty x <$> findIpkg
322 | opts x "--install" = prefixOnlyIfNonEmpty x <$> findIpkg
323 | opts x "--mkdoc" = prefixOnlyIfNonEmpty x <$> findIpkg
324 | opts x "--typecheck" = prefixOnlyIfNonEmpty x <$> findIpkg
325 | opts x "--clean" = prefixOnlyIfNonEmpty x <$> findIpkg
326 | opts x "--repl" = prefixOnlyIfNonEmpty x <$> findIpkg
329 | opts x _ = pure $
if (x `elem` optionFlags)
333 | else prefixOnly x optionFlags
336 | bashCompletionScript : (fun : String) -> String
337 | bashCompletionScript fun = let fun' = "_" ++ fun in """
340 | ED=$([ -z $2 ] && echo "--" || echo $2)
341 | COMPREPLY=($(idris2 --bash-completion $ED $3))
344 | complete -F \{ fun' } -o default idris2
347 | zshCompletionScript : (fun : String) -> String
348 | zshCompletionScript fun = let fun' = "_" ++ fun in """
350 | compdef \{fun'} idris2
354 | PREV_IDX=$((CURRENT-1))
356 | CURRENT_PARTIAL=$([[ -z ${PREFIX} ]] && echo "--" || echo "${PREFIX}")
357 | PREVIOUS="${words[$PREV_IDX]}"
359 | REPLY=($(idris2 --bash-completion "$CURRENT_PARTIAL" "$PREVIOUS"))
361 | if [[ -z $REPLY ]]; then
364 | _describe 'idris2' REPLY
368 | # don't run the completion function when being source-ed or eval-ed
369 | if [ "$funcstack[1]" = "\{fun'}" ]; then
379 | setIncrementalCG : {auto c : Ref Ctxt Defs} ->
380 | {auto o : Ref ROpts REPLOpts} ->
381 | Bool -> String -> Core ()
382 | setIncrementalCG failOnError cgn
383 | = do defs <- get Ctxt
384 | case getCG (options defs) cgn of
386 | do Just cgd <- getCG cg
387 | | Nothing => pure ()
388 | let Just _ = incCompileFile cgd
391 | then do coreLift $
putStrLn $
cgn ++ " does not support incremental builds"
392 | coreLift $
exitWith (ExitFailure 1)
394 | setSession ({ incrementalCGs $= (cg :: )} !getSession)
397 | then do coreLift $
putStrLn "No such code generator"
398 | coreLift $
putStrLn $
"Code generators available: " ++
399 | showSep ", " (map fst (availableCGs (options defs)))
400 | coreLift $
exitWith (ExitFailure 1)
405 | preOptions : {auto c : Ref Ctxt Defs} ->
406 | {auto o : Ref ROpts REPLOpts} ->
407 | List CLOpt -> Core Bool
408 | preOptions [] = pure True
409 | preOptions (NoBanner :: opts)
410 | = do setSession ({ nobanner := True } !getSession)
413 | preOptions (OutputFile _ :: opts)
414 | = do setSession ({ nobanner := True } !getSession)
416 | preOptions (ExecFn _ :: opts)
417 | = do setSession ({ nobanner := True } !getSession)
419 | preOptions (IdeMode :: opts)
420 | = do setSession ({ nobanner := True } !getSession)
422 | preOptions (IdeModeSocket _ :: opts)
423 | = do setSession ({ nobanner := True } !getSession)
425 | preOptions (CheckOnly :: opts)
426 | = do setSession ({ nobanner := True } !getSession)
428 | preOptions (Profile :: opts)
429 | = do setSession ({ profile := True } !getSession)
431 | preOptions (Quiet :: opts)
432 | = do setOutput (REPL VerbosityLvl.ErrorLvl)
434 | preOptions (NoPrelude :: opts)
435 | = do setSession ({ noprelude := True } !getSession)
437 | preOptions (SetCG e :: opts)
438 | = do defs <- get Ctxt
439 | case getCG (options defs) e of
440 | Just cg => do setCG cg
443 | do coreLift $
putStrLn "No such code generator"
444 | coreLift $
putStrLn $
"Code generators available: " ++
445 | showSep ", " (map fst (availableCGs (options defs)))
446 | coreLift $
exitWith (ExitFailure 1)
447 | preOptions (Directive d :: opts)
448 | = do setSession ({ directives $= (d::) } !getSession)
450 | preOptions (PkgPath p :: opts)
451 | = do addPkgDir p anyBounds
453 | preOptions (SourceDir d :: opts)
454 | = do setSourceDir (Just d)
456 | preOptions (BuildDir d :: opts)
459 | preOptions (OutputDir d :: opts)
460 | = do setOutputDir (Just d)
462 | preOptions (Directory d :: opts)
463 | = do defs <- get Ctxt
464 | dirOption (dirs (options defs)) d
466 | preOptions (ListPackages :: opts)
469 | preOptions (Timing tm :: opts)
470 | = do setLogTimings (fromMaybe 10 tm)
472 | preOptions (DebugElabCheck :: opts)
473 | = do setDebugElabCheck True
475 | preOptions (AltErrorCount c :: opts)
476 | = do setSession ({ logErrorCount := c } !getSession)
478 | preOptions (RunREPL _ :: opts)
479 | = do setOutput (REPL VerbosityLvl.ErrorLvl)
480 | setSession ({ nobanner := True } !getSession)
482 | preOptions (FindIPKG :: opts)
483 | = do setSession ({ findipkg := True } !getSession)
485 | preOptions (IgnoreMissingIPKG :: opts)
486 | = do setSession ({ ignoreMissingPkg := True } !getSession)
488 | preOptions (DumpCases f :: opts)
489 | = do setSession ({ dumpcases := Just f } !getSession)
491 | preOptions (DumpLifted f :: opts)
492 | = do setSession ({ dumplifted := Just f } !getSession)
494 | preOptions (DumpANF f :: opts)
495 | = do setSession ({ dumpanf := Just f } !getSession)
497 | preOptions (DumpVMCode f :: opts)
498 | = do setSession ({ dumpvmcode := Just f } !getSession)
500 | preOptions (Logging n :: opts)
501 | = do setSession ({ logEnabled := True,
502 | logLevel $= insertLogLevel n } !getSession)
504 | preOptions (ConsoleWidth n :: opts)
505 | = do setConsoleWidth n
507 | preOptions (ShowImplicits :: opts)
508 | = do updatePPrint { showImplicits := True }
510 | preOptions (ShowMachineNames :: opts)
511 | = do updatePPrint { showMachineNames := True }
513 | preOptions (ShowNamespaces :: opts)
514 | = do updatePPrint { fullNamespace := True }
516 | preOptions (Color b :: opts)
519 | preOptions (WarningsAsErrors :: opts)
520 | = do updateSession ({ warningsAsErrors := True })
522 | preOptions (IgnoreShadowingWarnings :: opts)
523 | = do updateSession ({ showShadowingWarning := False })
525 | preOptions (HashesInsteadOfModTime :: opts)
526 | = do throw (InternalError "-Xcheck-hashes disabled (see issue #1935)")
527 | updateSession ({ checkHashesInsteadOfModTime := True })
529 | preOptions (CaseTreeHeuristics :: opts)
530 | = do updateSession ({ caseTreeHeuristics := True })
532 | preOptions (IncrementalCG e :: opts)
533 | = do defs <- get Ctxt
534 | setIncrementalCG True e
536 | preOptions (WholeProgram :: opts)
537 | = do updateSession ({ wholeProgram := True })
539 | preOptions (BashCompletion a b :: _)
540 | = do os <- opts a b
541 | coreLift $
putStr $
unlines os
543 | preOptions (BashCompletionScript fun :: _)
544 | = do coreLift $
putStrLn $
bashCompletionScript fun
546 | preOptions (ZshCompletionScript fun :: _)
547 | = do coreLift $
putStrLn $
zshCompletionScript fun
549 | preOptions (Total :: opts)
550 | = do updateSession ({ totalReq := Total })
552 | preOptions (NoCSE :: opts)
553 | = do updateSession ({ noCSE := True })
555 | preOptions (_ :: opts) = preOptions opts
560 | postOptions : {auto c : Ref Ctxt Defs} ->
561 | {auto u : Ref UST UState} ->
562 | {auto s : Ref Syn SyntaxInfo} ->
563 | {auto m : Ref MD Metadata} ->
564 | {auto o : Ref ROpts REPLOpts} ->
565 | REPLResult -> List CLOpt -> Core Bool
566 | postOptions _ [] = pure True
567 | postOptions res@(ErrorLoadingFile {}) (OutputFile _ :: rest)
568 | = do ignore $
postOptions res rest
570 | postOptions res (OutputFile outfile :: rest)
571 | = do ignore $
compileExp (PRef EmptyFC (UN $
Basic "main")) outfile
572 | ignore $
postOptions res rest
574 | postOptions res (ExecFn expr :: rest)
575 | = do setCurrentElabSource expr
576 | let Right (_, _, e) = runParser (Virtual Interactive) Nothing expr $
aPTerm <* eoi
577 | | Left err => throw err
579 | ignore $
postOptions res rest
581 | postOptions res (CheckOnly :: rest)
582 | = do ignore $
postOptions res rest
584 | postOptions res (RunREPL str :: rest)
587 | postOptions res (_ :: rest) = postOptions res rest
590 | ideMode : List CLOpt -> Bool
592 | ideMode (IdeMode :: _) = True
593 | ideMode (_ :: xs) = ideMode xs
596 | ideModeSocket : List CLOpt -> Bool
597 | ideModeSocket [] = False
598 | ideModeSocket (IdeModeSocket _ :: _) = True
599 | ideModeSocket (_ :: xs) = ideModeSocket xs