ghcup 0.1.50.2 → 0.2.1.0
raw patch · 157 files changed
+18973/−26123 lines, 157 filesdep +dhalldep +dhall-jsondep +dhall-yamldep ~aesondep ~asyncdep ~base
Dependencies added: dhall, dhall-json, dhall-yaml, either, filepattern, hspec-golden, indexed-traversable, scientific, tasty-bench, th-lift, witherable
Dependency ranges changed: aeson, async, base, brick, bytestring, containers, deepseq, directory, filepath, mtl, optparse-applicative, pretty, safe-exceptions, tar, template-haskell, text, time, unordered-containers, uri-bytestring, variant, versions, yaml
Files
- CHANGELOG.md +44/−2
- app/ghcup/Main.hs +164/−167
- bench/Bench.hs +13/−0
- bench/BenchList.hs +71/−0
- bench/BenchParse.hs +21/−0
- bench/BenchSetup.hs +86/−0
- data/config.yaml +21/−0
- ghcup.cabal +194/−175
- lib-opt/GHCup/OptParse.hs +50/−46
- lib-opt/GHCup/OptParse/ChangeLog.hs +70/−72
- lib-opt/GHCup/OptParse/Common.hs +134/−106
- lib-opt/GHCup/OptParse/Compile.hs +162/−147
- lib-opt/GHCup/OptParse/Config.hs +109/−44
- lib-opt/GHCup/OptParse/DInfo.hs +0/−141
- lib-opt/GHCup/OptParse/DebugInfo.hs +142/−0
- lib-opt/GHCup/OptParse/GC.hs +19/−20
- lib-opt/GHCup/OptParse/Generate.hs +225/−0
- lib-opt/GHCup/OptParse/HealthCheck.hs +160/−0
- lib-opt/GHCup/OptParse/Install.hs +202/−351
- lib-opt/GHCup/OptParse/List.hs +92/−42
- lib-opt/GHCup/OptParse/Nuke.hs +23/−23
- lib-opt/GHCup/OptParse/Prefetch.hs +72/−43
- lib-opt/GHCup/OptParse/Reset.hs +65/−0
- lib-opt/GHCup/OptParse/Rm.hs +94/−113
- lib-opt/GHCup/OptParse/Run.hs +266/−193
- lib-opt/GHCup/OptParse/Set.hs +113/−180
- lib-opt/GHCup/OptParse/Test.hs +57/−54
- lib-opt/GHCup/OptParse/ToolRequirements.hs +16/−7
- lib-opt/GHCup/OptParse/UnSet.hs +39/−36
- lib-opt/GHCup/OptParse/Upgrade.hs +54/−53
- lib-opt/GHCup/OptParse/Whereis.hs +102/−94
- lib-tui/GHCup/Brick/Actions.hs +308/−299
- lib-tui/GHCup/Brick/App.hs +44/−21
- lib-tui/GHCup/Brick/Attributes.hs +1/−1
- lib-tui/GHCup/Brick/BrickState.hs +11/−10
- lib-tui/GHCup/Brick/Common.hs +11/−10
- lib-tui/GHCup/Brick/Widgets/KeyInfo.hs +6/−1
- lib-tui/GHCup/Brick/Widgets/Menu.hs +37/−30
- lib-tui/GHCup/Brick/Widgets/Menus/AdvanceInstall.hs +0/−135
- lib-tui/GHCup/Brick/Widgets/Menus/AdvancedInstall.hs +137/−0
- lib-tui/GHCup/Brick/Widgets/Menus/CompileGHC.hs +26/−23
- lib-tui/GHCup/Brick/Widgets/Menus/CompileHLS.hs +3/−3
- lib-tui/GHCup/Brick/Widgets/Menus/Context.hs +20/−18
- lib-tui/GHCup/Brick/Widgets/Navigation.hs +87/−61
- lib-tui/GHCup/Brick/Widgets/SectionList.hs +0/−206
- lib-tui/GHCup/Brick/Widgets/ToolInfo.hs +108/−0
- lib-tui/GHCup/Brick/Widgets/Tutorial.hs +5/−6
- lib-tui/GHCup/BrickMain.hs +34/−26
- lib/GHCup.hs +0/−680
- lib/GHCup/Builder.hs +104/−0
- lib/GHCup/Cabal.hs +0/−287
- lib/GHCup/CabalConfig.hs +22/−21
- lib/GHCup/Command/Compile/GHC.hs +834/−0
- lib/GHCup/Command/Compile/HLS.hs +473/−0
- lib/GHCup/Command/DebugInfo.hs +69/−0
- lib/GHCup/Command/GC.hs +204/−0
- lib/GHCup/Command/HealthCheck.hs +176/−0
- lib/GHCup/Command/Install.hs +300/−0
- lib/GHCup/Command/Install/LowLevel.hs +281/−0
- lib/GHCup/Command/List.hs +368/−0
- lib/GHCup/Command/Nuke.hs +139/−0
- lib/GHCup/Command/Prefetch.hs +119/−0
- lib/GHCup/Command/Rm.hs +218/−0
- lib/GHCup/Command/Set.hs +191/−0
- lib/GHCup/Command/Test/GHC.hs +199/−0
- lib/GHCup/Command/Upgrade.hs +160/−0
- lib/GHCup/Command/Whereis.hs +107/−0
- lib/GHCup/Compat/Pager.hs +69/−0
- lib/GHCup/Compat/Terminal.hs +116/−0
- lib/GHCup/Download.hs +202/−176
- lib/GHCup/Download/IOStreams.hs +4/−4
- lib/GHCup/Download/Utils.hs +0/−60
- lib/GHCup/Errors.hs +185/−122
- lib/GHCup/GHC.hs +0/−1426
- lib/GHCup/HLS.hs +0/−744
- lib/GHCup/Hardcoded/URLs.hs +46/−0
- lib/GHCup/Hardcoded/Version.hs +34/−0
- lib/GHCup/Input/Parsers.hs +507/−0
- lib/GHCup/Input/Parsers/URI.hs +97/−0
- lib/GHCup/Input/Prompts.hs +32/−0
- lib/GHCup/Input/SymlinkSpec.hs +91/−0
- lib/GHCup/Legacy/Cabal.hs +132/−0
- lib/GHCup/Legacy/HLS.hs +277/−0
- lib/GHCup/Legacy/Stack.hs +131/−0
- lib/GHCup/Legacy/Utils.hs +700/−0
- lib/GHCup/List.hs +0/−439
- lib/GHCup/PlanJson.hs +3/−2
- lib/GHCup/Platform.hs +0/−374
- lib/GHCup/Prelude.hs +36/−18
- lib/GHCup/Prelude/Attoparsec.hs +8/−8
- lib/GHCup/Prelude/File.hs +69/−57
- lib/GHCup/Prelude/File/Posix.hs +39/−29
- lib/GHCup/Prelude/File/Search.hs +26/−25
- lib/GHCup/Prelude/File/Windows.hs +15/−14
- lib/GHCup/Prelude/Internal.hs +47/−38
- lib/GHCup/Prelude/JSON.hs +14/−0
- lib/GHCup/Prelude/Logger.hs +19/−19
- lib/GHCup/Prelude/Logger/Internal.hs +33/−18
- lib/GHCup/Prelude/MegaParsec.hs +144/−27
- lib/GHCup/Prelude/Posix.hs +1/−1
- lib/GHCup/Prelude/Process/Posix.hs +29/−31
- lib/GHCup/Prelude/Process/Windows.hs +28/−27
- lib/GHCup/Prelude/String/QQ.hs +7/−36
- lib/GHCup/Prelude/Version.hs +110/−0
- lib/GHCup/Prelude/Version/QQ.hs +11/−11
- lib/GHCup/Prelude/Windows.hs +12/−12
- lib/GHCup/Prompts.hs +0/−32
- lib/GHCup/Query.hs +14/−0
- lib/GHCup/Query/DB.hs +398/−0
- lib/GHCup/Query/DB/HLS.hs +45/−0
- lib/GHCup/Query/GHCupDirs.hs +569/−0
- lib/GHCup/Query/GHCupDirs.hs-boot +37/−0
- lib/GHCup/Query/Metadata.hs +362/−0
- lib/GHCup/Query/Symlink.hs +133/−0
- lib/GHCup/Query/System.hs +371/−0
- lib/GHCup/Requirements.hs +0/−75
- lib/GHCup/Setup.hs +60/−0
- lib/GHCup/Stack.hs +0/−286
- lib/GHCup/System/Cmd.hs +176/−0
- lib/GHCup/System/Directory.hs +70/−0
- lib/GHCup/Types.hs +1416/−858
- lib/GHCup/Types/Dhall.hs +429/−0
- lib/GHCup/Types/JSON.hs +414/−111
- lib/GHCup/Types/JSON/MapIgnoreUnknownKeys.hs +8/−8
- lib/GHCup/Types/JSON/Utils.hs +15/−1
- lib/GHCup/Types/JSON/Versions.hs +9/−9
- lib/GHCup/Types/Optics.hs +58/−17
- lib/GHCup/Types/Stack.hs +80/−25
- lib/GHCup/Types/Tar.hs +33/−0
- lib/GHCup/Unpack.hs +140/−0
- lib/GHCup/Utils.hs +0/−1272
- lib/GHCup/Utils/Dirs.hs +0/−614
- lib/GHCup/Utils/Dirs.hs-boot +0/−37
- lib/GHCup/Utils/Output.hs +0/−116
- lib/GHCup/Utils/Pager.hs +0/−65
- lib/GHCup/Utils/Parsers.hs +0/−502
- lib/GHCup/Utils/Tar.hs +0/−141
- lib/GHCup/Utils/Tar/Types.hs +0/−33
- lib/GHCup/Utils/URI.hs +0/−61
- lib/GHCup/Version.hs +0/−96
- lib/GHCup/Warnings.hs +40/−0
- scripts/bootstrap/bootstrap-haskell +129/−48
- scripts/bootstrap/bootstrap-haskell.ps1 +11/−11
- test/ghcup-test/GHCup/ArbitraryTypes.hs +208/−4
- test/ghcup-test/GHCup/ParserSpec.hs +75/−14
- test/ghcup-test/GHCup/Prelude/File/Posix/TraversalsSpec.hs +0/−1
- test/ghcup-test/GHCup/Types/JSONSpec.hs +113/−7
- test/ghcup-test/golden/unix/GHCupInfo.json +2456/−13739
- test/optparse-test/ChangeLogTest.hs +9/−11
- test/optparse-test/CompileTest.hs +4/−4
- test/optparse-test/ConfigTest.hs +18/−0
- test/optparse-test/InstallTest.hs +35/−115
- test/optparse-test/ListTest.hs +25/−24
- test/optparse-test/RmTest.hs +10/−22
- test/optparse-test/RunTest.hs +8/−7
- test/optparse-test/SetTest.hs +28/−80
- test/optparse-test/WhereisTest.hs +11/−12
CHANGELOG.md view
@@ -1,5 +1,47 @@-# Revision history for ghcup+# Version history for ghcup +## 0.2.1.0 -- 2026-05-21++### New feature++* implement **"installer DSL"** (custom tool installation) wrt [#141](https://github.com/haskell/ghcup-hs/issues/141)+ - run `ghcup config add-release-channel 3rdparty` to get access to tools like `hlint`, `ormolu`, `agda` etc.+ - refer to the [Packaging documentation](https://www.haskell.org/ghcup/packaging) for more details+ - this caused heavy changes to the internal ghcup layout and database+* major design change in the TUI (two-pane view)+ - use left/right arrow keys or tab to switch between the tool and version list+* implement **revisions**+ - these are "distributor" updates (e.g. fixes to bindists or the metadata) without requiring a proper upstream release+ - refer to the [documentation](https://www.haskell.org/ghcup/guide/#revisions) for more details+* pave the way for **OpenBSD** support wrt [#182](https://github.com/haskell/ghcup-hs/issues/182)+ - we still lack GHC bindists+* add experimental support for **Dhall metadata** wrt [#60](https://github.com/haskell/ghcup-hs/issues/60)+ - dump the Dhall schema via `ghcup generate dhall-schema`+* add `ghcup config reset` subcommand by Vladislav Sabanov+* add experimental healthcheck command `ghcup check tool ghc <ver>`+ - this may only give useful information on newly installed versions since the database needs to be populated++### Improvements and bug fixes++* Introduce `--verbosity=<LEVEL>` (0-2) option+ - `--verbose` still exists and is equivalent to `--verbosity=1`+ - ghcup won't spam the installed file list on `--verbose`/`--verbosity=1` anymore+* Store log files under `XDG_STATE_HOME` instead of `XDG_CACHE_HOME` when following XDG style by Eisuke Kawashima+* Make `--tool` and `--show-criteria` a 'many' parser, fixes [#1235](https://github.com/haskell/ghcup-hs/issues/1235)+* Ignore local cache when dlUri has a `file:` scheme, fixes [#1312](https://github.com/haskell/ghcup-hs/issues/1312)+* don't show update warnings twice (if it's the same old warning)+* Fix `ghcup whereis` on windows by only using forward slashes in the output+* lots of other things we forgot++### Breaking changes++- `cabal install <ghc-ver>` is gone, use `cabal install ghc <ghc-ver>`+- `cabal install-cabal <cabal-ver>` is gone, use `cabal install cabal <cabal-ver>`+- `ghcup compile hls --isolate=/tmp/foo` installs binaries into `/tmp/foo/bin` instead of `/tmp/foo`+- `ghcup list` may show `-rX` suffix on versions when there's a revision update+ * if you rely on version equality checks in a script and don't want to deal with parsing those, you may consider `ghcup list --show-revisions=none`+- `~/.ghcup/bin/` is now all symlinks (except for `ghcup` binary itself) and the link targets may have different shape+ ## 0.1.50.2 -- 2025-05-17 * Respect system `ld` on all platforms and disable GHCs aggressive selection of `ld.gold`, fixes [#1032](https://github.com/haskell/ghcup-hs/issues/1032)@@ -148,7 +190,7 @@ * restore proper support for FreeBSD and Linux armv7 * integrate with [errors.haskell.org](https://errors.haskell.org/index.html), wrt [#434](https://github.com/haskell/ghcup-hs/issues/434) * allow to overwrite distro detection via config wrt [#421](https://github.com/haskell/ghcup-hs/issues/421)- - this is particularly useful for e.g. Ubuntu derivates, where ghcup doesn't pick the optimal bindist, also see the [GHCup documentation on overriding distro detection](https://www.haskell.org/ghcup/guide/#overriding-distro-detection)+ - this is particularly useful for e.g. Ubuntu derivatives, where ghcup doesn't pick the optimal bindist, also see the [GHCup documentation on overriding distro detection](https://www.haskell.org/ghcup/guide/#overriding-distro-detection) * Add proper support for mirrors wrt [#357](https://github.com/haskell/ghcup-hs/issues/357) * fix a (harmless) bug in `ghcup nuke` on windows * improvements to `ghcup add-release-channel` wrt [#708](https://github.com/haskell/ghcup-hs/issues/708)
app/ghcup/Main.hs view
@@ -1,70 +1,72 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-} {-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeApplications #-} module Main where -import GHCup.PlanJson-+import GHCup.PlanJson #if defined(BRICK)-import GHCup.BrickMain (brickMain)+import GHCup.BrickMain ( brickMain ) #endif--import qualified GHCup.GHC as GHC-import qualified GHCup.HLS as HLS-import GHCup.OptParse+import GHCup.Compat.Pager+import GHCup.Download+import GHCup.Errors+import GHCup.Hardcoded.Version+import GHCup.Input.Parsers ( resolveVersion )+import GHCup.OptParse+import GHCup.Prelude (enableAnsiSupport, decUTF8Safe')+import GHCup.Prelude.Logger+import GHCup.Prelude.String.QQ+import GHCup.Query.GHCupDirs+import GHCup.Query.Metadata+import GHCup.Query.System+import GHCup.Setup+import GHCup.Types+import GHCup.Types.Optics hiding ( toolRequirements ) -import GHCup.Utils.Pager-import GHCup.Download-import GHCup.Errors-import GHCup.Platform-import GHCup.Types-import GHCup.Types.Optics hiding ( toolRequirements )-import GHCup.Utils-import GHCup.Utils.Parsers (fromVersion)-import GHCup.Prelude-import GHCup.Prelude.Logger-import GHCup.Prelude.String.QQ-import GHCup.Version+import qualified GHCup.Command.Compile.GHC as GHC+import qualified GHCup.Command.Compile.HLS as HLS -import Control.Monad (when, forM_, unless)-import Control.Concurrent-import Control.Concurrent.Async-import Control.Exception.Safe+import Control.Concurrent+import Control.Concurrent.Async+import Control.Exception.Safe+import Control.Monad ( unless ) #if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )+import Control.Monad.Fail ( MonadFail ) #endif-import Control.Monad.Reader-import Data.Aeson ( decodeStrict', Value )-import Data.Aeson.Encode.Pretty ( encodePretty )-import Data.Either-import Data.Functor-import Data.Versions (version)-import Data.Maybe-import GHC.IO.Encoding-import Data.Variant.Excepts-import Language.Haskell.TH-import Language.Haskell.TH.Syntax ( Quasi(qAddDependentFile) )-import Options.Applicative hiding ( style )-import Options.Applicative.Pretty.Shim ( text )-import Prelude hiding ( appendFile )-import System.Environment-import System.Exit-import System.IO hiding ( appendFile )-import System.IO.Unsafe ( unsafeInterleaveIO )-import Text.PrettyPrint.HughesPJClass ( prettyShow )+import Control.Monad.Reader+import Data.Aeson ( Value, decodeStrict' )+import Data.Aeson.Encode.Pretty ( encodePretty )+import Data.Either+import Data.Functor+import Data.Maybe+import Data.Variant.Excepts+import Data.Versions ( version )+import GHC.IO.Encoding+import Language.Haskell.TH+import Language.Haskell.TH.Syntax ( Quasi (qAddDependentFile) )+import Options.Applicative hiding ( ParseError, style )+import Options.Applicative.Pretty.Shim ( text )+import Prelude hiding ( appendFile )+import System.Environment+import System.Exit+import System.FilePath ( (</>) )+import System.IO hiding ( appendFile )+import System.IO.Unsafe ( unsafeInterleaveIO )+import Text.PrettyPrint.HughesPJClass ( prettyShow )+import Witherable ( witherM ) -import qualified Data.ByteString as B-import qualified Data.Text as T-import qualified Data.Text.IO as T-import qualified Data.Text.Encoding as E-import qualified GHCup.Types as Types+import qualified Data.ByteString as B+import qualified Data.Text as T+import qualified Data.Text.Encoding as E+import qualified Data.Text.IO as T+import qualified GHCup.Types as Types @@ -85,7 +87,7 @@ metaCache = fromMaybe (fromMaybe (Types.metaCache defaultSettings) uMetaCache) optMetaCache metaMode = fromMaybe (fromMaybe (Types.metaMode defaultSettings) uMetaMode) optMetaMode noVerify = fromMaybe (fromMaybe (Types.noVerify defaultSettings) uNoVerify) optNoVerify- verbose = fromMaybe (fromMaybe (Types.verbose defaultSettings) uVerbose) optVerbose+ verbose = maybe (fromMaybe (Types.verbose defaultSettings) uVerbose) Verbosity optVerbose keepDirs = fromMaybe (fromMaybe (Types.keepDirs defaultSettings) uKeepDirs) optKeepDirs downloader = fromMaybe (fromMaybe defaultDownloader uDownloader) optsDownloader keyBindings = maybe defaultKeyBindings mergeKeys uKeyBindings@@ -95,10 +97,11 @@ platformOverride = optPlatform <|> (uPlatformOverride <|> Types.platformOverride defaultSettings) mirrors = fromMaybe (Types.mirrors defaultSettings) uMirrors defGHCConfOptions = fromMaybe (Types.defGHCConfOptions defaultSettings) uDefGHCConfOptions- pager = case fromMaybe (fromMaybe (Types.pager defaultSettings) uPager) (flip PagerConfig Nothing <$> optPager) of+ pager = case maybe (fromMaybe (Types.pager defaultSettings) uPager) (`PagerConfig` Nothing) optPager of PagerConfig b Nothing -> PagerConfig b pagerCmd- x -> x+ x -> x guessVersion = fromMaybe (fromMaybe (Types.guessVersion defaultSettings) uGuessVersion) optGuessVersion+ buildWrapper = uBuildWrapper in (Settings {..}, keyBindings) #if defined(INTERNAL_DOWNLOADER) defaultDownloader = Internal@@ -112,6 +115,9 @@ bUp = fromMaybe bUp kUp , bDown = fromMaybe bDown kDown , bQuit = fromMaybe bQuit kQuit+ , bLeft = fromMaybe bLeft kLeft+ , bRight = fromMaybe bRight kRight+ , bTab = fromMaybe bTab kTab , bInstall = fromMaybe bInstall kInstall , bUninstall = fromMaybe bUninstall kUninstall , bSet = fromMaybe bSet kSet@@ -197,17 +203,24 @@ -- logger interpreter logfile <- runReaderT initGHCupFileLogging dirs let loggerConfig = LoggerConfig- { lcPrintDebug = verbose settings+ { lcPrintDebugLvl = Just ((\(Verbosity i) -> i) . verbose $ settings) , consoleOutter = T.hPutStr stderr , fileOutter = case optCommand of Nuke -> \_ -> pure ()- _ -> T.appendFile logfile+ _ -> T.appendFile logfile , fancyColors = not no_color }- let leanAppstate = LeanAppState settings dirs keybindings loggerConfig+ pfreq <- case platformOverride settings of+ Just pfreq' -> return pfreq'+ Nothing -> (flip runReaderT loggerConfig . runE @'[NoCompatiblePlatform, NoCompatibleArch, DistroNotFound] . liftE $ platformRequest) >>= \case+ VRight r -> pure r+ VLeft e -> do+ runReaderT (logError $ T.pack $ prettyHFError e) loggerConfig+ exitWith (ExitFailure 2)+ let leanAppstate = LeanAppState settings dirs keybindings pfreq loggerConfig let runLogger = flip runReaderT leanAppstate- let siletRunLogger = flip runReaderT (leanAppstate { loggerConfig = loggerConfig { consoleOutter = \_ -> pure () } } :: LeanAppState)+ let silentRunLogger = flip runReaderT (leanAppstate { loggerConfig = loggerConfig { consoleOutter = \_ -> pure () } } :: LeanAppState) -------------------------@@ -215,18 +228,9 @@ ------------------------- - let appState = do- pfreq <- case platformOverride settings of- Just pfreq' -> return pfreq'- Nothing -> (runLogger . runE @'[NoCompatiblePlatform, NoCompatibleArch, DistroNotFound] . liftE $ platformRequest) >>= \case- VRight r -> pure r- VLeft e -> do- runLogger- (logError $ T.pack $ prettyHFError e)- exitWith (ExitFailure 2)-+ let getAppState_and_updateCheckAction = do ghcupInfo <-- ( flip runReaderT leanAppstate . runE @'[ContentLengthError, DigestError, DistroNotFound, DownloadFailed, FileDoesNotExistError, GPGError, JSONError, NoCompatibleArch, NoCompatiblePlatform, NoDownload, GHCup.Errors.ParseError, ProcessError, UnsupportedSetupCombo, StackPlatformDetectError] $ do+ ( flip runReaderT leanAppstate . runE @'[ContentLengthError, DigestError, DistroNotFound, DownloadFailed, FileDoesNotExistError, GPGError, JSONError, NoCompatibleArch, NoCompatiblePlatform, NoDownload, GHCup.Errors.ParseError, ProcessError, UnsupportedSetupCombo, StackPlatformDetectError, UnsupportedMetadataFormat] $ do liftE $ getDownloadsF pfreq ) >>= \case@@ -240,64 +244,54 @@ race_ (liftIO $ runReaderT cleanupTrash s') (threadDelay 5000000 >> runLogger (logWarn $ "Killing cleanup thread (exceeded 5s timeout)... please remove leftover files in " <> T.pack (fromGHCupPath recycleDir) <> " manually")) - case optCommand of- Nuke -> pure ()- Whereis _ _ -> pure ()- DInfo -> pure ()- ToolRequirements _ -> pure ()- ChangeLog _ -> pure ()- UnSet _ -> pure ()-#if defined(BRICK)- Interactive -> pure ()-#endif- -- check for new tools- _- | Just False <- optVerbose -> pure ()- | otherwise -> lookupEnv "GHCUP_SKIP_UPDATE_CHECK" >>= \case- Nothing -> void . flip runReaderT s' . runE @'[TagNotFound, DayNotFound, NextVerNotFound, NoToolVersionSet] $ do- newTools <- lift checkForUpdates- forM_ newTools $ \newTool@(t, l) -> do- -- https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/283- alreadyInstalling' <- alreadyInstalling optCommand newTool- when (not alreadyInstalling') $- case t of- GHCup -> runLogger $- logWarn ("New GHCup version available: "- <> tVerToText l- <> ". To upgrade, run 'ghcup upgrade'")- _ -> runLogger $- logWarn ("New "- <> T.pack (prettyShow t)- <> " version available. "- <> "If you want to install this latest version, run 'ghcup install "- <> T.pack (prettyShow t)- <> " "- <> tVerToText l- <> "'")- Just _ -> pure ()+ let updateCheckAction+ | Just 0 <- optVerbose = pure ()+ | otherwise = void . flip runReaderT s' . runE @'[TagNotFound, DayNotFound, NextVerNotFound, NoToolVersionSet, ParseError] $ liftIO (lookupEnv "GHCUP_SKIP_UPDATE_CHECK") >>= \case+ Nothing -> do+ newTools <- lift checkForUpdates+ warnMsg' <- fmap (T.intercalate "\n") $ flip witherM newTools $ \newTool@(t, l) -> do+ -- https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/283+ alreadyInstalling' <- alreadyInstalling optCommand newTool+ if alreadyInstalling'+ then pure Nothing+ else+ pure $ Just $+ case t of+ Tool "ghcup" -> "New GHCup version available: "+ <> T.pack (prettyShow l)+ <> ". To upgrade, run 'ghcup upgrade'"+ _ -> "New "+ <> T.pack (prettyShow t)+ <> " version available. "+ <> "If you want to install this latest version, run 'ghcup install "+ <> T.pack (prettyShow t)+ <> " "+ <> T.pack (prettyShow l) - -- TODO: always run for windows- siletRunLogger (flip runReaderT s' $ runE ensureShimGen) >>= \case+ let warnMsg = warnMsg' <> "'\nTo skip this check in the future, export GHCUP_SKIP_UPDATE_CHECK=1"+ let warnFile = fromGHCupPath cacheDir </> "ghcup_update_check"+ prevWarn <- try @_ @SomeException $ liftIO $ T.readFile warnFile+ if either (const Nothing) Just prevWarn == Just warnMsg+ then pure ()+ else do+ liftIO $ T.writeFile warnFile warnMsg+ lift $ logWarn warnMsg+ Just _ -> do+ logDebug "GHCUP_SKIP_UPDATE_CHECK is set, skipping update check"+ pure ()++ silentRunLogger (flip runReaderT s' $ runE ensureShimGen) >>= \case VRight _ -> pure () VLeft e -> do runLogger (logError $ T.pack $ prettyHFError e) exitWith (ExitFailure 30)- pure s' + pure (s', updateCheckAction) -#if defined(IS_WINDOWS)- -- FIXME: windows needs 'ensureGlobalTools', which requires- -- full appstate- runLeanAppState = runAppState-#else- runLeanAppState = flip runReaderT leanAppstate-#endif- runAppState action' = do- s' <- liftIO appState- runReaderT action' s' + ----------------- -- Run command -- -----------------@@ -305,30 +299,34 @@ res <- case optCommand of #if defined(BRICK) Interactive -> do- s' <- appState- liftIO $ brickMain s' >> pure ExitSuccess+ (appState, _) <- liftIO $ getAppState_and_updateCheckAction+ liftIO $ brickMain appState >> pure ExitSuccess #endif- Install installCommand -> install installCommand settings appState runLogger- InstallCabalLegacy iopts -> install (Left (InstallCabal iopts)) settings appState runLogger- Test testCommand -> test testCommand settings appState runLogger- Set setCommand -> set setCommand settings runAppState runLeanAppState runLogger- UnSet unsetCommand -> unset unsetCommand runLeanAppState runLogger- List lo -> list lo no_color (pager settings) runAppState- Rm rmCommand -> rm rmCommand runAppState runLogger- DInfo -> dinfo runAppState runLogger- Compile compileCommand -> compile compileCommand settings dirs runAppState runLogger- Config configCommand -> config configCommand settings userConf keybindings runLogger+ Install installCommand -> install installCommand settings (getAppState_and_updateCheckAction, leanAppstate)+ Test testCommand -> test testCommand settings (getAppState_and_updateCheckAction, leanAppstate)+ Set setCommand -> set setCommand settings (getAppState_and_updateCheckAction, leanAppstate)+ UnSet unsetCommand -> unset unsetCommand (getAppState_and_updateCheckAction, leanAppstate)+ List lo -> list lo no_color (pager settings) (getAppState_and_updateCheckAction, leanAppstate)+ Rm rmCommand -> rm rmCommand (getAppState_and_updateCheckAction, leanAppstate)+ DInfo -> dinfo (getAppState_and_updateCheckAction, leanAppstate)+ Compile compileCommand -> compile compileCommand settings dirs (getAppState_and_updateCheckAction, leanAppstate)+ Config configCommand -> config configCommand settings userConf keybindings (getAppState_and_updateCheckAction, leanAppstate) Whereis whereisOptions- whereisCommand -> whereis whereisCommand whereisOptions settings runAppState leanAppstate runLogger- Upgrade uOpts force' fatal -> upgrade uOpts force' fatal dirs runAppState runLogger- ToolRequirements topts -> toolRequirements topts runAppState runLogger- ChangeLog changelogOpts -> changelog changelogOpts runAppState runLogger- Nuke -> nuke appState runLogger- Prefetch pfCom -> prefetch pfCom settings runAppState runLogger- GC gcOpts -> gc gcOpts runAppState runLogger- Run runCommand -> run runCommand settings appState leanAppstate runLogger+ whereisCommand -> whereis whereisCommand whereisOptions settings (getAppState_and_updateCheckAction, leanAppstate)+ Upgrade uOpts force' fatal -> upgrade uOpts force' fatal dirs (getAppState_and_updateCheckAction, leanAppstate)+ ToolRequirements topts -> toolRequirements topts (getAppState_and_updateCheckAction, leanAppstate)+ ChangeLog changelogOpts -> changelog changelogOpts (getAppState_and_updateCheckAction, leanAppstate)+ Nuke -> nuke (getAppState_and_updateCheckAction, leanAppstate)+ Prefetch pfCom -> prefetch pfCom settings (getAppState_and_updateCheckAction, leanAppstate)+ GC gcOpts -> gc gcOpts (getAppState_and_updateCheckAction, leanAppstate)+ Run runCommand -> run runCommand settings (getAppState_and_updateCheckAction, leanAppstate) PrintAppErrors -> putStrLn allHFError >> pure ExitSuccess+ HealthCheck hcCommands -> hc hcCommands (getAppState_and_updateCheckAction, leanAppstate)+#if defined(DHALL)+ Generate gen -> generate gen (getAppState_and_updateCheckAction, leanAppstate)+#endif + case res of ExitSuccess -> pure () ef@(ExitFailure _) -> exitWith ef@@ -337,61 +335,60 @@ where alreadyInstalling :: ( HasLog env- , MonadFail m , MonadReader env m , HasGHCupInfo env , HasDirs env- , MonadThrow m- , MonadIO m- , MonadCatch m+ , MonadIOish m ) => Command- -> (Tool, GHCTargetVersion)+ -> (Tool, TargetVersionRev) -> Excepts '[ TagNotFound , DayNotFound , NextVerNotFound , NoToolVersionSet+ , ParseError ] m Bool- alreadyInstalling (Install (Right InstallOptions{..})) (GHC, ver) = cmp' GHC instVer ver- alreadyInstalling (Install (Left (InstallGHC InstallOptions{..}))) (GHC, ver) = cmp' GHC instVer ver- alreadyInstalling (Install (Left (InstallCabal InstallOptions{..}))) (Cabal, ver) = cmp' Cabal instVer ver- alreadyInstalling (Install (Left (InstallHLS InstallOptions{..}))) (HLS, ver) = cmp' HLS instVer ver- alreadyInstalling (Install (Left (InstallStack InstallOptions{..}))) (Stack, ver) = cmp' Stack instVer ver- alreadyInstalling (Compile (CompileGHC GHCCompileOptions{ overwriteVer = Just [S over] })) (GHC, ver)- | Right over' <- version (T.pack over) = cmp' GHC (Just $ GHCVersion (mkTVer over')) ver+ alreadyInstalling (Install (InstallGHC InstallOptions{..})) (Tool "ghc", ver) = cmp' ghc instVer ver+ alreadyInstalling (Install (InstallCabal InstallOptions{..})) (Tool "cabal", ver) = cmp' cabal instVer ver+ alreadyInstalling (Install (InstallHLS InstallOptions{..})) (Tool "hls", ver) = cmp' hls instVer ver+ alreadyInstalling (Install (InstallStack InstallOptions{..})) (Tool "stack", ver) = cmp' stack instVer ver+ alreadyInstalling (Install (InstallOtherTool InstallOptionsNew{..})) (tool, ver)+ | instTool == tool = cmp' tool instVer ver+ alreadyInstalling (Compile (CompileGHC GHCCompileOptions{ overwriteVer = Just [S over] })) (ghc', ver)+ | Right over' <- version (T.pack over) = cmp' ghc' (Just $ GHCVersion (mkTVer over' `TargetVersionReq` Nothing)) ver | otherwise = pure False alreadyInstalling (Compile (CompileGHC GHCCompileOptions{ targetGhc = GHC.SourceDist tver }))- (GHC, ver) = cmp' GHC (Just $ ToolVersion tver) ver- alreadyInstalling (Compile (CompileHLS HLSCompileOptions{ overwriteVer = Just [S over] })) (HLS, ver)- | Right over' <- version (T.pack over) = cmp' HLS (Just $ ToolVersion over') ver+ (Tool "ghc", ver) = cmp' ghc (Just $ ToolVersion (tver `VersionReq` Nothing)) ver+ alreadyInstalling (Compile (CompileHLS HLSCompileOptions{ overwriteVer = Just [S over] })) (hls', ver)+ | Right over' <- version (T.pack over) = cmp' hls' (Just $ ToolVersion (over' `VersionReq` Nothing)) ver | otherwise = pure False alreadyInstalling (Compile (CompileHLS HLSCompileOptions{ targetHLS = HLS.SourceDist tver }))- (HLS, ver) = cmp' HLS (Just $ ToolVersion tver) ver+ (Tool "hls", ver) = cmp' hls (Just $ ToolVersion (tver `VersionReq` Nothing)) ver alreadyInstalling (Compile (CompileHLS HLSCompileOptions{ targetHLS = HLS.HackageDist tver }))- (HLS, ver) = cmp' HLS (Just $ ToolVersion tver) ver- alreadyInstalling (Upgrade {}) (GHCup, _) = pure True+ (Tool "hls", ver) = cmp' hls (Just $ ToolVersion (tver `VersionReq` Nothing)) ver+ alreadyInstalling (Upgrade {}) (Tool "ghcup", _) = pure True alreadyInstalling _ _ = pure False cmp' :: ( HasLog env- , MonadFail m , MonadReader env m , HasGHCupInfo env , HasDirs env- , MonadThrow m- , MonadIO m- , MonadCatch m+ , MonadIOish m ) => Tool -> Maybe ToolVersion- -> GHCTargetVersion+ -> TargetVersionRev -> Excepts '[ TagNotFound , DayNotFound , NextVerNotFound , NoToolVersionSet+ , ParseError ] m Bool- cmp' tool instVer ver = do- (v, _) <- liftE $ fromVersion instVer GLax tool- pure (v == ver)+ cmp' tool instVer TargetVersionRev{..} = do+ GHCupInfo { _ghcupDownloads = dls } <- getGHCupInfo+ (TargetVersionReq v mRev) <- liftE $ resolveVersion instVer GLax tool+ let rev = fromMaybe (maybe 0 (\(_, i, _) -> i) $ getRev dls tool v Nothing) mRev+ pure (v == _tvrTargetVer && _tvrRev == rev)
+ bench/Bench.hs view
@@ -0,0 +1,13 @@+module Main (main) where++import Test.Tasty.Bench+import qualified BenchList+import qualified BenchParse+++main :: IO ()+main = do+ defaultMain [ BenchParse.benchMark+ , BenchList.benchMark+ ]+
+ bench/BenchList.hs view
@@ -0,0 +1,71 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeApplications #-}++module BenchList (benchMark) where++import BenchSetup++import GHCup.Command.List+import GHCup.Errors+import GHCup.Types++import Control.Monad.Reader (runReaderT)+import Data.Maybe+import Data.Variant.Excepts+import Prelude hiding ( appendFile )+import Test.Tasty.Bench+import Data.Text (Text)+import Data.Versions (Version)++import qualified Data.Map.Strict as Map+++benchMark :: Benchmark+benchMark =+ bgroup "GHCup.Commands.List"+ [ bgroup "ST"+ [ env (getListInfoArgs ["data/bench/ghcup-cross-0.1.0.yaml"]) (bench "cross" . nf listVersionsB)+ , env (getListInfoArgs ["data/bench/ghcup-0.1.0.yaml"]) (bench "def" . nf listVersionsB)+ , env (getListInfoArgs ["data/bench/ghcup-nightlies-2025-0.0.7.yaml"]) (bench "nightly" . nf listVersionsB)+ , env (getListInfoArgs ["data/bench/ghcup-nightlies-2025-0.0.7.yaml"+ ,"data/bench/ghcup-0.1.0.yaml"+ ,"data/bench/ghcup-cross-0.1.0.yaml"+ ,"data/bench/ghcup-prereleases-0.1.0.yaml"+ ,"data/bench/ghcup-3rdparty-0.1.0.yaml"+ ]) (bench "all" . nf listVersionsB)+ ]+ , bgroup "IO"+ [ env (getAppState ["data/bench/ghcup-cross-0.1.0.yaml"]) (bench "cross" . nfAppIO listVersionsA)+ , env (getAppState ["data/bench/ghcup-0.1.0.yaml"]) (bench "def" . nfAppIO listVersionsA)+ , env (getAppState ["data/bench/ghcup-nightlies-2025-0.0.7.yaml"]) (bench "nightly" . nfAppIO listVersionsA)+ , env (getAppState ["data/bench/ghcup-nightlies-2025-0.0.7.yaml"+ ,"data/bench/ghcup-0.1.0.yaml"+ ,"data/bench/ghcup-cross-0.1.0.yaml"+ ,"data/bench/ghcup-prereleases-0.1.0.yaml"+ ,"data/bench/ghcup-3rdparty-0.1.0.yaml"+ ]) (bench "all" . nfAppIO listVersionsA)+ ]+ ]++listVersionsA :: AppState -> IO ToolListResult+listVersionsA s' = do+ VRight r <- flip runReaderT s' . runE @'[ParseError] $ listVersions+ Nothing -- tools+ [] -- list criteria+ ShowUpdates -- showRevisions+ False -- hideOld+ True -- showNightly+ (Nothing, Nothing)+ pure r++listVersionsB :: (GHCupDownloads, PlatformRequest, Map.Map Tool (Map.Map (Maybe Text) ([VersionRev], Maybe VersionRev)), [Version]) -> ToolListResult+listVersionsB (dls, pfreq, instTools, hlsGHCs) =+ listVersions' dls pfreq instTools hlsGHCs+ Nothing -- tools (need to sync with getListInfoArgs)+ [] -- list criteria+ ShowUpdates -- showRevisions+ False -- hideOld+ True -- showNightly+ (Nothing, Nothing)+
+ bench/BenchParse.hs view
@@ -0,0 +1,21 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}++module BenchParse (benchMark) where++import BenchSetup++import Prelude hiding ( appendFile )+import Test.Tasty.Bench++import qualified Data.ByteString as BS+++benchMark :: Benchmark+benchMark =+ bgroup "Parse metadata"+ [ env (BS.readFile "data/bench/ghcup-cross-0.1.0.yaml") (bench "cross" . nfAppIO parseGHCupInfo')+ , env (BS.readFile "data/bench/ghcup-0.1.0.yaml") (bench "def" . nfAppIO parseGHCupInfo')+ , env (BS.readFile "data/bench/ghcup-nightlies-2025-0.0.7.yaml") (bench "nightly" . nfAppIO parseGHCupInfo')+ ]+
+ bench/BenchSetup.hs view
@@ -0,0 +1,86 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeApplications #-}+module BenchSetup where++import GHCup.Download+import GHCup.Errors+import GHCup.Prelude+import GHCup.Query.GHCupDirs+import GHCup.Types++import Control.DeepSeq (force)+import Control.Monad (forM)+import Control.Monad.Reader (runReaderT)+import Control.Exception (displayException, evaluate)+import Data.Maybe+import Data.Yaml (decodeFileEither, decodeEither')+import Data.Variant.Excepts+import Data.Versions (Version)+import Prelude hiding ( appendFile )+import Data.ByteString (ByteString)+import Data.Text (Text)+import GHCup.Types.Optics (getPlatformReq, getGHCupInfo)++import qualified Data.Map.Strict as Map+import GHCup.Query.DB+import GHCup.Query.DB.HLS+import Control.Monad.Trans (lift)++type DownloadErrors = '[ContentLengthError, DigestError, DistroNotFound, DownloadFailed, FileDoesNotExistError, GPGError, JSONError, NoCompatibleArch, NoCompatiblePlatform, NoDownload, GHCup.Errors.ParseError, ProcessError, UnsupportedSetupCombo, StackPlatformDetectError, UnsupportedMetadataFormat]++readGHCupInfo :: [FilePath] -> IO [NewURLSource]+readGHCupInfo fps =+ forM fps $ \fp -> do+ ghcupInfo <- either (fail . displayException) pure =<< decodeFileEither fp+ evaluate $ force $ NewGHCupInfo ghcupInfo++parseGHCupInfo' :: ByteString -> IO GHCupInfo+parseGHCupInfo' bs = do+ ghcupInfo <- either (fail . displayException) pure $ decodeEither' bs+ evaluate $ force ghcupInfo++getAppState :: [FilePath] -> IO AppState+getAppState fps = do+ infos <- readGHCupInfo fps+ spawnAppState infos++getListInfoArgs ::+ [FilePath]+ -> IO (GHCupDownloads, PlatformRequest, Map.Map Tool (Map.Map (Maybe Text) ([VersionRev], Maybe VersionRev)), [Version])+getListInfoArgs fps = do+ s' <- getAppState fps+ VRight r <- flip runReaderT s' . runE @'[ParseError] $ do+ GHCupInfo { _ghcupDownloads = dls } <- getGHCupInfo+ pfreq <- lift getPlatformReq+ instTools <- getAllInstalledTools Nothing+ hlsGHCs <- let hlsSet = do+ hlsMap <- instTools Map.!? hls+ (_, mSet) <- hlsMap Map.!? Nothing+ mSet+ in case hlsSet of+ Just VersionRev{..} -> do+ getHLSGHCs _vrVersion+ Nothing -> pure []+ pure (dls, pfreq, instTools, hlsGHCs)+ pure r++spawnAppState :: [NewURLSource] -> IO AppState+spawnAppState infos = do+ dirs <- getAllDirs+ let loggerConfig = LoggerConfig+ { lcPrintDebugLvl = Nothing+ , consoleOutter = mempty+ , fileOutter = mempty+ , fancyColors = False+ }+ let pfreq = PlatformRequest A_64 Darwin Nothing+ let settings = defaultSettings { urlSource = infos }+ let keybindings = defaultKeyBindings+ let leanAppstate = LeanAppState settings dirs keybindings pfreq loggerConfig++ VRight ghcupInfo <- flip runReaderT leanAppstate . runE @DownloadErrors $ do liftE $ getDownloadsF pfreq++ let s' = AppState settings dirs keybindings ghcupInfo pfreq loggerConfig+ evaluate (force s')+
data/config.yaml view
@@ -13,6 +13,21 @@ # whether/how to do gpg verification gpg-setting: GPGNone # GPGStrict | GPGLax | GPGNone +# A wrapper around configure/make+# Can be used to sandbox untrusted build systems, e.g.:+#+# build-wrapper: null+# cmd: bwrap+# cmdArgs:+# [ --ro-bind, /, /+# , --bind, /home/wurst/.ghcup, /home/wurst/.ghcup+# , --bind, /home/wurst/.cabal, /home/wurst/.cabal+# , --dev, /dev+# , --proc, /proc+# , --tmpfs, /tmp+# ]+build-wrapper: null+ # TUI key bindings, # see https://hackage.haskell.org/package/vty-5.31/docs/Graphics-Vty-Input-Events.html#t:Key # for possible values.@@ -26,6 +41,12 @@ KUp: [] down: KDown: []+ left:+ KLeft: []+ right:+ KRight: []+ tab:+ KChar: '\t' quit: KChar: 'q' install:
ghcup.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: ghcup-version: 0.1.50.2+version: 0.2.1.0 license: LGPL-3.0-only license-file: LICENSE copyright: Julian Ospald 2024@@ -36,11 +36,11 @@ test/ghcup-test/data/file test/ghcup-test/golden/unix/GHCupInfo.json -tested-with: GHC==9.6.6- , GHC==9.4.8- , GHC==9.2.8- , GHC==9.0.2- , GHC==8.10.7+tested-with: GHC==9.14.1+ , GHC==9.12.4+ , GHC==9.10.3+ , GHC==9.8.4+ , GHC==9.6.7 source-repository head type: git@@ -82,31 +82,77 @@ default: False manual: True +flag dhall+ description: Add support for dhall encoded metadata+ default: True+ manual: True++flag dhall-tests+ description: Run dhall tests depedning on dhall-yaml/dhall-json+ default: True+ manual: False+ common common-ghc-options ghc-options: -Wall -fwarn-tabs -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates -Werror=incomplete-patterns +common common-extensions+ default-extensions:+ DeriveGeneric+ LambdaCase+ MultiWayIf+ NamedFieldPuns+ PackageImports+ QuasiQuotes+ RecordWildCards+ ScopedTypeVariables+ StrictData+ TupleSections+ TypeApplications+ TypeFamilies+ ViewPatterns++common common-language+ default-language: Haskell2010++common platform-ifdefs+ if os(freebsd)+ cpp-options: -DIS_FREEBSD++ if os(windows)+ cpp-options: -DIS_WINDOWS+ common app-common-depends import: common-ghc-options+ import: common-extensions+ import: common-language+ import: platform-ifdefs + if flag(dhall)+ cpp-options: -DDHALL+ build-depends:+ dhall ^>=1.42+ , either ^>=5.0.3+ build-depends:- , aeson >=1.4+ , aeson >=1.4 , aeson-pretty ^>=0.8.8 , async ^>=2.2.3 , attoparsec ^>= 0.14- , base >=4.12 && <5- , bytestring >=0.10 && <0.13+ , base >=4.12 && <5+ , bytestring >=0.10 && <0.13 , containers ^>=0.6 || ^>=0.7 , deepseq ^>=1.4 || ^>=1.5 || ^>=1.6 , directory ^>=1.3.6.0- , filepath >=1.4.101.0+ , filepath >=1.4.101.0+ , indexed-traversable ^>=0.1.4 , variant ^>=1.0- , megaparsec >=8.0.0 && <9.8+ , megaparsec >=8.0.0 && <9.8 , mtl ^>=2.2 || ^>=2.3- , optics ^>=0.4- , optparse-applicative >=0.15.1.0 && <0.19- , pretty ^>=1.1.3.1+ , optics ^>=0.4+ , optparse-applicative >=0.15.1.0 && <0.20+ , pretty ^>=1.1.3.6 , pretty-terminal ^>=0.1.0.0 , process ^>=1.6.11.0 , resourcet ^>=1.2.2 || ^>=1.3@@ -114,15 +160,16 @@ , safe-exceptions ^>=0.1 , tagsoup ^>=0.14 , transformers ^>=0.5 || ^>=0.6- , template-haskell >=2.7 && <2.24+ , template-haskell >=2.7 && <2.25 , temporary ^>=1.3 , text ^>=2.0 || ^>=2.1- , time >=1.9.3 && <1.15+ , time >=1.9.3 && <1.17 , unordered-containers ^>=0.2- , uri-bytestring ^>=0.4.0.0+ , uri-bytestring ^>=0.4.0.1 , utf8-string ^>=1.0- , vector >=0.12 && <0.14- , versions >=6.0.5 && <6.1+ , vector >=0.12 && <0.14+ , versions >=6.0.5 && <6.1+ , witherable >=0.4 && <0.6 if flag(yaml-streamly) build-depends:@@ -131,10 +178,19 @@ build-depends: yaml ^>=0.11.0 + if os(windows)+ build-depends:+ , process ^>=1.6.11.0+ , Win32 >=2.10+ else+ build-depends:+ , unix ^>=2.7 || ^>=2.8+ , unix-bytestring ^>=0.4+ if flag(tar) cpp-options: -DTAR build-depends:- tar ^>=0.6.0.0+ tar ^>=0.7.0.0 , zip ^>=2.0.0 || ^>=2.1.0 else@@ -143,118 +199,103 @@ library import: app-common-depends exposed-modules:- GHCup- GHCup.Cabal+ GHCup.Builder GHCup.CabalConfig+ GHCup.Command.Compile.GHC+ GHCup.Command.Compile.HLS+ GHCup.Command.Install+ GHCup.Command.Install.LowLevel+ GHCup.Command.List+ GHCup.Command.Nuke+ GHCup.Command.DebugInfo+ GHCup.Command.Upgrade+ GHCup.Command.Whereis+ GHCup.Command.GC+ GHCup.Command.HealthCheck+ GHCup.Command.Prefetch+ GHCup.Command.Rm+ GHCup.Command.Set+ GHCup.Command.Test.GHC+ GHCup.Compat.Pager+ GHCup.Compat.Terminal GHCup.Download- GHCup.Download.Utils GHCup.Errors- GHCup.GHC- GHCup.HLS- GHCup.List- GHCup.Platform+ GHCup.Hardcoded.URLs+ GHCup.Hardcoded.Version+ GHCup.Input.Parsers+ GHCup.Input.Parsers.URI+ GHCup.Input.Prompts+ GHCup.Input.SymlinkSpec+ GHCup.Legacy.Cabal+ GHCup.Legacy.HLS+ GHCup.Legacy.Stack+ GHCup.Legacy.Utils GHCup.PlanJson GHCup.Prelude GHCup.Prelude.Attoparsec GHCup.Prelude.File GHCup.Prelude.File.Search GHCup.Prelude.Internal+ GHCup.Prelude.JSON GHCup.Prelude.Logger GHCup.Prelude.Logger.Internal GHCup.Prelude.MegaParsec GHCup.Prelude.Process GHCup.Prelude.String.QQ+ GHCup.Prelude.Version GHCup.Prelude.Version.QQ- GHCup.Prompts- GHCup.Requirements- GHCup.Stack+ GHCup.Query+ GHCup.Query.DB+ GHCup.Query.DB.HLS+ GHCup.Query.GHCupDirs+ GHCup.Query.Metadata+ GHCup.Query.Symlink+ GHCup.Query.System+ GHCup.Setup+ GHCup.System.Cmd+ GHCup.System.Directory GHCup.Types+ GHCup.Types.Dhall GHCup.Types.JSON GHCup.Types.JSON.MapIgnoreUnknownKeys GHCup.Types.JSON.Utils GHCup.Types.JSON.Versions GHCup.Types.Optics GHCup.Types.Stack- GHCup.Utils- GHCup.Utils.Dirs- GHCup.Utils.Tar- GHCup.Utils.Tar.Types- GHCup.Utils.URI- GHCup.Utils.Output- GHCup.Utils.Pager- GHCup.Utils.Parsers- GHCup.Version+ GHCup.Types.Tar+ GHCup.Unpack+ GHCup.Warnings hs-source-dirs: lib other-modules: Paths_ghcup autogen-modules: Paths_ghcup- default-language: Haskell2010- default-extensions:- DeriveGeneric- LambdaCase- MultiWayIf- NamedFieldPuns- PackageImports- QuasiQuotes- RecordWildCards- ScopedTypeVariables- StrictData- TupleSections- TypeApplications- TypeFamilies- ViewPatterns build-depends:- , aeson >=1.4- , async >=0.8 && <2.3- , attoparsec ^>= 0.14- , base >=4.12 && <5 , base16-bytestring >=0.1.1.6 && <1.1 , binary ^>=0.8.6.0 || ^>=0.9 || ^>=0.10- , bytestring >=0.10 && <0.13 , bz2 ^>=1.0.1.1 , Cabal ^>=3.0.0.0 || ^>=3.2.0.0 || ^>=3.4.0.0 || ^>=3.6.0.0 || ^>=3.8.0.0 || ^>= 3.10.0.0 || ^>= 3.12.0.0 || ^>= 3.14.0.0 , Cabal-syntax ^>=3.6.0.0 || ^>=3.8.0.0 || ^>= 3.10.0.0 || ^>= 3.12.0.0 || ^>= 3.14.0.0 , case-insensitive ^>=1.2.1.0 , casing ^>=0.1.4.1- , containers ^>=0.6 || ^>=0.7 , conduit ^>=1.3 , conduit-extra ^>=1.3 , cryptohash-sha256 ^>=0.11.101.0- , deepseq ^>=1.4 || ^>=1.5 || ^>=1.6- , directory ^>=1.3.6.0 , disk-free-space ^>=0.1.0.1 , exceptions ^>=0.10- , filepath >=1.4.101.0+ , filepattern ^>=0.1.3 , file-uri ^>=0.1.0.0- , variant ^>=1.0 , xz ^>=5.6.3- , megaparsec >=8.0.0 && <9.8- , mtl ^>=2.2 || ^>=2.3- , optics ^>=0.4 , os-release ^>=1.0.0 , parsec- , pretty ^>=1.1.3.1- , pretty-terminal ^>=0.1.0.0 , regex-posix ^>=0.96- , resourcet ^>=1.2.2 || ^>=1.3 , retry >=0.8.1.2 && <0.10- , safe ^>=0.3.18- , safe-exceptions ^>=0.1+ , scientific , split ^>=0.2.3.4 , strict-base ^>=0.4- , template-haskell >=2.7 && <2.24- , temporary ^>=1.3 , terminal-size ^>=0.3.3- , text ^>=2.0 || ^>=2.1- , time >=1.9.3 && <1.15- , transformers ^>=0.5 || ^>=0.6+ , th-lift ^>=0.8.7 , unliftio-core ^>=0.2.0.1- , unordered-containers ^>=0.2.10.0- , uri-bytestring ^>=0.4.0.0- , utf8-string ^>=1.0- , vector >=0.12 && <0.14- , versions >=6.0.5 && <6.1 , word8 ^>=0.1.3 , zlib ^>=0.6.2.2 || ^>=0.7 @@ -265,15 +306,6 @@ build-depends: yaml ^>=0.11.0 - if flag(tar)- cpp-options: -DTAR- build-depends:- tar ^>=0.6.0.0- , zip ^>=2.0.0 || ^>=2.1.0-- else- build-depends: libarchive ^>=3.0.3.0- if (flag(internal-downloader) && !os(windows)) exposed-modules: GHCup.Download.IOStreams cpp-options: -DINTERNAL_DOWNLOADER@@ -284,16 +316,12 @@ , terminal-progress-bar >=0.4.1 if os(windows)- cpp-options: -DIS_WINDOWS other-modules: GHCup.Prelude.File.Windows GHCup.Prelude.Windows -- GHCup.OptParse.Run uses this exposed-modules: GHCup.Prelude.Process.Windows- build-depends:- , process ^>=1.6.11.0- , Win32 >=2.10 else other-modules:@@ -306,13 +334,10 @@ include-dirs: cbits install-includes: dirutils.h c-sources: cbits/dirutils.c- build-depends:- , unix ^>=2.7 || ^>=2.8- , unix-bytestring ^>=0.4 if flag(tui) cpp-options: -DBRICK- build-depends: vty ^>=6.0 || ^>=6.1 || ^>=6.2+ build-depends: vty ^>=6.0 || ^>=6.1 || ^>=6.2 || ^>=6.3 || ^>=6.4 if (flag(strict-metadata-parsing)) cpp-options: -DSTRICT_METADATA_PARSING@@ -325,12 +350,15 @@ GHCup.OptParse.Common GHCup.OptParse.Compile GHCup.OptParse.Config- GHCup.OptParse.DInfo+ GHCup.OptParse.DebugInfo GHCup.OptParse.GC+ GHCup.OptParse.Generate+ GHCup.OptParse.HealthCheck GHCup.OptParse.Install GHCup.OptParse.List GHCup.OptParse.Nuke GHCup.OptParse.Prefetch+ GHCup.OptParse.Reset GHCup.OptParse.Rm GHCup.OptParse.Run GHCup.OptParse.Set@@ -342,16 +370,6 @@ Options.Applicative.Pretty.Shim hs-source-dirs: lib-opt- default-language: Haskell2010- default-extensions:- LambdaCase- MultiWayIf- NamedFieldPuns- PackageImports- RecordWildCards- ScopedTypeVariables- StrictData- TupleSections build-depends: ghcup @@ -361,12 +379,6 @@ if flag(tui) cpp-options: -DBRICK - if os(windows)- cpp-options: -DIS_WINDOWS-- else- build-depends: unix ^>=2.7 || ^>=2.8- library ghcup-tui import: app-common-depends exposed-modules:@@ -374,10 +386,10 @@ GHCup.Brick.Widgets.Navigation GHCup.Brick.Widgets.Tutorial GHCup.Brick.Widgets.KeyInfo- GHCup.Brick.Widgets.SectionList+ GHCup.Brick.Widgets.ToolInfo GHCup.Brick.Widgets.Menu GHCup.Brick.Widgets.Menus.Context- GHCup.Brick.Widgets.Menus.AdvanceInstall+ GHCup.Brick.Widgets.Menus.AdvancedInstall GHCup.Brick.Widgets.Menus.CompileGHC GHCup.Brick.Widgets.Menus.CompileHLS GHCup.Brick.Actions@@ -387,46 +399,20 @@ GHCup.Brick.Common hs-source-dirs: lib-tui- default-language: Haskell2010- default-extensions:- LambdaCase- MultiWayIf- NamedFieldPuns- PackageImports- RecordWildCards- ScopedTypeVariables- StrictData- TupleSections build-depends: , ghcup- , brick >=2.1 && <2.8- , vty ^>=6.0 || ^>=6.1 || ^>=6.2+ , brick >=2.1 && <2.13+ , vty ^>=6.0 || ^>=6.1 || ^>=6.2 || ^>=6.3 || ^>=6.4 if !flag(tui) buildable: False - if os(windows)- cpp-options: -DIS_WINDOWS-- else- build-depends: unix ^>=2.7 || ^>=2.8- executable ghcup import: app-common-depends main-is: Main.hs hs-source-dirs: app/ghcup- default-language: Haskell2010- default-extensions:- LambdaCase- MultiWayIf- NamedFieldPuns- PackageImports- RecordWildCards- ScopedTypeVariables- StrictData- TupleSections ghc-options: -threaded@@ -442,18 +428,15 @@ cpp-options: -DBRICK build-depends: , ghcup-tui- , transformers ^>=0.5 || ^>=0.6 - if os(windows)- cpp-options: -DIS_WINDOWS- else- build-depends: unix ^>=2.7 || ^>=2.8- if flag(no-exe) buildable: False test-suite ghcup-test import: common-ghc-options+ import: common-extensions+ import: common-language+ import: platform-ifdefs type: exitcode-stdio-1.0 main-is: Main.hs build-tool-depends: hspec-discover:hspec-discover -any@@ -466,45 +449,52 @@ GHCup.ParserSpec Spec - default-language: Haskell2010- default-extensions:- LambdaCase- MultiWayIf- PackageImports- RecordWildCards- ScopedTypeVariables- TupleSections- ghc-options: -fconstraint-solver-iterations=0 + if flag(dhall) && flag(dhall-tests)+ cpp-options: -DDHALL+ build-depends:+ dhall ^>=1.42+ , dhall-json ^>= 1.7.11+ , dhall-yaml ^>= 1.2.11+ , either ^>=5.0.3+ build-depends:- , base >=4.12 && <5- , bytestring >=0.10 && <0.13+ , aeson >=1.4+ , aeson-pretty ^>=0.8.8+ , base >=4.12 && <5+ , bytestring >=0.10 && <0.13 , containers ^>=0.6 || ^>=0.7 , conduit ^>=1.3 , directory ^>=1.3.6.0- , filepath >=1.4.101.0+ , filepath >=1.4.101.0 , generic-arbitrary ^>=0.1.0 || ^>=0.2.2 || ^>=1.0.0 , ghcup- , hspec >=2.7.10 && <2.12+ , hspec >=2.7.10 && <2.12+ , hspec-golden ^>=0.2.2.0 , hspec-golden-aeson ^>=0.9- , megaparsec >=8.0.0 && <9.8+ , megaparsec >=8.0.0 && <9.8+ , pretty ^>=1.1.3.6 , QuickCheck ^>=2.14.1 || ^>=2.15 , quickcheck-arbitrary-adt ^>=0.3.1.0 , text ^>=2.0 || ^>=2.1- , time >=1.9.3 && <1.15- , uri-bytestring ^>=0.4.0.0+ , time >=1.9.3 && <1.17+ , uri-bytestring ^>=0.4.0.1+ , yaml , versions >=6.0.5 && <6.1 - if os(windows)- cpp-options: -DIS_WINDOWS+ if os(freebsd)+ cpp-options: -DIS_FREEBSD - else+ if !os(windows) build-depends: unix ^>=2.7 || ^>=2.8 test-suite ghcup-optparse-test import: common-ghc-options+ import: common-extensions+ import: common-language+ import: platform-ifdefs type: exitcode-stdio-1.0 hs-source-dirs: test/optparse-test main-is: Main.hs@@ -524,10 +514,6 @@ Utils WhereisTest - if os(windows)- cpp-options: -DIS_WINDOWS-- default-language: Haskell2010 build-depends: , base , ghcup@@ -539,3 +525,36 @@ , text , uri-bytestring , versions++benchmark bench+ import: common-ghc-options+ import: common-extensions+ import: common-language+ import: platform-ifdefs++ main-is: Bench.hs+ other-modules: BenchList+ BenchParse+ BenchSetup+ type: exitcode-stdio-1.0+ hs-source-dirs: bench+ ghc-options: -O2 "-with-rtsopts=-A32m"+ if impl(ghc >= 8.6)+ ghc-options: -fproc-alignment=64+ build-depends: base,+ aeson,+ bytestring,+ containers,+ directory,+ deepseq,+ filepath,+ ghcup,+ mtl,+ safe-exceptions,+ tasty-bench,+ template-haskell,+ text,+ variant,+ versions,+ yaml+
lib-opt/GHCup/OptParse.hs view
@@ -2,7 +2,6 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TypeApplications #-} {-# LANGUAGE DuplicateRecordFields #-} @@ -23,7 +22,11 @@ , module GHCup.OptParse.ChangeLog , module GHCup.OptParse.Prefetch , module GHCup.OptParse.GC- , module GHCup.OptParse.DInfo+#if defined(DHALL)+ , module GHCup.OptParse.Generate+#endif+ , module GHCup.OptParse.HealthCheck+ , module GHCup.OptParse.DebugInfo , module GHCup.OptParse.Nuke , module GHCup.OptParse.ToolRequirements , module GHCup.OptParse.Run@@ -48,17 +51,19 @@ import GHCup.OptParse.ChangeLog import GHCup.OptParse.Prefetch import GHCup.OptParse.GC-import GHCup.OptParse.DInfo+#if defined(DHALL)+import GHCup.OptParse.Generate+#endif+import GHCup.OptParse.HealthCheck+import GHCup.OptParse.DebugInfo import GHCup.OptParse.ToolRequirements import GHCup.OptParse.Nuke import GHCup.Types-import GHCup.Utils.Parsers (gpgParser, downloaderParser, keepOnParser, platformParser, parseUrlSource)+import GHCup.Input.Parsers (gpgParser, downloaderParser, keepOnParser, platformParser) #if !MIN_VERSION_base(4,13,0) import Control.Monad.Fail ( MonadFail ) #endif-import Control.Monad.Reader-import Data.Either import Data.Functor import Data.Maybe import Options.Applicative hiding ( style )@@ -67,7 +72,7 @@ import System.Exit import System.Environment (getProgName) import System.IO-import GHCup.Utils.Pager+import GHCup.Compat.Pager import qualified Data.Text as T import Data.Function ((&)) @@ -76,7 +81,7 @@ data Options = Options { -- global options- optVerbose :: Maybe Bool+ optVerbose :: Maybe Int , optCache :: Maybe Bool , optMetaCache :: Maybe Integer , optMetaMode :: Maybe MetaMode@@ -95,13 +100,12 @@ } data Command- = Install (Either InstallCommand InstallOptions)+ = Install InstallCommand | Test TestCommand- | InstallCabalLegacy InstallOptions- | Set (Either SetCommand SetOptions)+ | Set SetCommand | UnSet UnsetCommand | List ListOptions- | Rm (Either RmCommand RmOptions)+ | Rm RmCommand | DInfo | Compile CompileCommand | Config ConfigCommand@@ -111,6 +115,9 @@ #endif | ToolRequirements ToolReqOpts | ChangeLog ChangeLogOptions+#if defined(DHALL)+ | Generate GenerateCommand+#endif | Nuke #if defined(BRICK) | Interactive@@ -118,14 +125,21 @@ | Prefetch PrefetchCommand | GC GCOptions | Run RunOptions+ | HealthCheck HealthCheckCommand | PrintAppErrors +toVerbosity :: Maybe Bool -> Maybe Int+toVerbosity (Just True) = Just 1+toVerbosity (Just False) = Just 0+toVerbosity _ = Nothing + opts :: Parser Options opts = Options- <$> invertableSwitch "verbose" (Just 'v') False (help "Enable verbosity (default: disabled)")+ <$> (fmap toVerbosity (invertableSwitch "verbose" (Just 'v') False (help "Enable verbosity (default: disabled)"))+ <|> optional (option auto (long "verbosity" <> metavar "LEVEL" <> help "verbosity level (0 for off, 1 for 'verbose', 2 for extra)"))) <*> invertableSwitch "cache" (Just 'c') False (help "Cache downloads in ~/.ghcup/cache (default: disabled)") <*> optional (option auto (long "metadata-caching" <> metavar "SEC" <> help "How long the yaml metadata caching interval is (in seconds), 0 to disable")) <*> optional (option auto (long "metadata-fetching-mode" <> metavar "<Strict|Lax>" <> help "Whether to fail on metadata download failure (Strict) or fall back to cached version (Lax (default))"))@@ -140,16 +154,7 @@ ) ) <*> optional- (option- (eitherReader parseUrlSource)- ( short 's'- <> long "url-source"- <> metavar "<URL_SOURCE|cross|prereleases|vanilla|default>"- <> help "Alternative ghcup download info"- <> internal- <> completer urlSourceCompleter- )- )+ parseUrlSourceP <*> (fmap . fmap) not (invertableSwitch "verify" (Just 'n') True (help "Disable tarball checksum verification (default: enabled)")) <*> optional (option (eitherReader keepOnParser)@@ -186,7 +191,7 @@ <> completer (listCompleter ["strict", "lax", "none"]) )) <*> invertableSwitch "stack-setup" Nothing False (help "Use stack's setup info for discovering and installing GHC versions")- <*> (invertableSwitch "paginate" Nothing False (help "Send output (e.g. from 'ghcup list') through pager (default: disabled)"))+ <*> invertableSwitch "paginate" Nothing False (help "Send output (e.g. from 'ghcup list') through pager (default: disabled)") <*> invertableSwitch "guess-version" Nothing True (help "Whether to guess the full version from an incomplete tool version, e.g. GHC '9.12' resolving to '9.12.2'") <*> com @@ -276,8 +281,8 @@ <> command "whereis" (info- ( (Whereis- <$> (WhereisOptions <$> switch (short 'd' <> long "directory" <> help "return directory of the binary instead of the binary location"))+ (+ (Whereis . WhereisOptions <$> switch (short 'd' <> long "directory" <> help "return directory of the binary instead of the binary location") <*> whereisP ) <**> helper )@@ -314,6 +319,12 @@ <> footerDoc ( Just $ text runFooter ) ) )+ <> command+ "check"+ ( HealthCheck <$>+ info (hcP <**> helper)+ (progDesc "Health check ghcup")+ ) <> commandGroup "Main commands:" ) <|> subparser@@ -340,37 +351,30 @@ <$> info (configP <**> helper) (progDesc "Show or set config" <> footerDoc (Just $ text configFooter)) )+#if defined(DHALL)+ <> command+ "generate"+ ( Generate+ <$> (info (generateP <**> helper)+ (progDesc "Various generation facilities related to metadata"))+ )+#endif+ <> command+ "nuke"+ (info (pure Nuke <**> helper)+ (progDesc "Completely remove ghcup from your system")) <> commandGroup "Other commands:" <> hidden )- <|> subparser- ( command- "install-cabal"- (info- ((InstallCabalLegacy <$> installOpts (Just Cabal)) <**> helper)- ( progDesc "Install or update cabal"- <> footerDoc (Just $ text installCabalFooter)- )- )- <> internal- ) <|> subparser (command- "nuke"- (info (pure Nuke <**> helper)- (progDesc "Completely remove ghcup from your system"))- <> commandGroup "Nuclear Commands:"- <> hidden- )- <|> subparser- (command "print-app-errors" (info (pure PrintAppErrors <**> helper) (progDesc "")) <> internal ) --- | Handle `ParserResult`.+-- | Handle 'ParserResult'. handleParseResult' :: Maybe FilePath -> Bool -> ParserResult a -> IO a handleParseResult' _ _ (Success a) = return a handleParseResult' pagerCmd hasHelp (Failure failure) = do
lib-opt/GHCup/OptParse/ChangeLog.hs view
@@ -1,56 +1,57 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-} {-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeApplications #-} module GHCup.OptParse.ChangeLog where -import GHCup.Types-import GHCup.Errors-import GHCup.OptParse.Common-import GHCup.Prelude-import GHCup.Prelude.Logger-import GHCup.Prelude.String.QQ-import GHCup.Prelude.Process (exec)+import GHCup.Errors+import GHCup.OptParse.Common+import GHCup.Prelude+import GHCup.Input.Parsers+import GHCup.Prelude.Process ( exec )+import GHCup.Prelude.String.QQ+import GHCup.Query.Metadata+import GHCup.Types #if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )+import Control.Monad.Fail ( MonadFail ) #endif-import Control.Monad.Reader-import Control.Monad.Trans.Resource-import Data.Functor-import Data.Maybe-import Options.Applicative hiding ( style )-import Prelude hiding ( appendFile )-import System.Exit-import System.Process ( system )-import Text.PrettyPrint.HughesPJClass ( prettyShow )--import qualified Data.Text as T-import Control.Exception.Safe (MonadMask)+import Control.Exception.Safe ( MonadMask )+import Control.Monad.Reader+import Control.Monad.Trans.Resource+import Data.Char ( toLower )+import Data.Functor+import Data.Maybe+import Data.Variant.Excepts import GHCup.Types.Optics-import GHCup.Utils-import URI.ByteString (serializeURIRef')-import Data.Char (toLower)+import Options.Applicative hiding ( style, ParseError )+import Prelude hiding ( appendFile )+import System.Exit+import System.Process ( system )+import Text.PrettyPrint.HughesPJClass ( prettyShow )+import URI.ByteString ( serializeURIRef' ) +import qualified Data.Text as T + --------------- --[ Options ]-- --------------- data ChangeLogOptions = ChangeLogOptions- { clOpen :: Bool- , clTool :: Maybe Tool+ { clOpen :: Bool+ , clTool :: Maybe Tool , clToolVer :: Maybe ToolVersion- } deriving (Eq, Show)+ }+ deriving (Eq, Show) @@ -67,13 +68,7 @@ <*> optional (option (eitherReader- (\s' -> case fmap toLower s' of- "ghc" -> Right GHC- "cabal" -> Right Cabal- "ghcup" -> Right GHCup- "stack" -> Right Stack- "hls" -> Right HLS- e -> Left e+ (\s' -> Right (Tool (fmap toLower s')) ) ) (short 't' <> long "tool" <> metavar "<ghc|cabal|hls|ghcup|stack>" <> help@@ -109,43 +104,46 @@ , MonadFail m ) => ChangeLogOptions- -> (forall a . ReaderT AppState m a -> m a)- -> (ReaderT LeanAppState m () -> m ())+ -> (IO (AppState, IO ()), LeanAppState) -> m ExitCode-changelog ChangeLogOptions{..} runAppState runLogger = do- GHCupInfo { _ghcupDownloads = dls } <- runAppState getGHCupInfo- let tool = fromMaybe GHC clTool- ver' = fromMaybe- (ToolTag Latest)- clToolVer- muri = getChangeLog dls tool ver'+changelog ChangeLogOptions{..} (getAppState', leanAppstate) = run (do+ GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo+ let tool = fromMaybe ghc clTool+ treq <- liftE $ resolveVersion clToolVer GLax tool+ let muri = getChangeLog dls tool $ _tvqTargetVer treq case muri of Nothing -> do- runLogger- (logWarn $- "Could not find ChangeLog for " <> T.pack (prettyShow tool) <> ", version " <> T.pack (prettyShow ver')- )- pure ExitSuccess+ lift $ logWarn $ "Could not find ChangeLog for " <> T.pack (prettyShow tool) <> ", version " <> T.pack (prettyShow treq) Just uri -> do- pfreq <- runAppState getPlatformReq+ pfreq <- lift getPlatformReq let uri' = T.unpack . decUTF8Safe . serializeURIRef' $ uri if clOpen then do- runAppState $- case _rPlatform pfreq of- Darwin -> exec "open" [T.unpack $ decUTF8Safe $ serializeURIRef' uri] Nothing Nothing- Linux _ -> exec "xdg-open" [T.unpack $ decUTF8Safe $ serializeURIRef' uri] Nothing Nothing- FreeBSD -> exec "xdg-open" [T.unpack $ decUTF8Safe $ serializeURIRef' uri] Nothing Nothing- OpenBSD -> exec "xdg-open" [T.unpack $ decUTF8Safe $ serializeURIRef' uri] Nothing Nothing- Windows -> do- let args = "start \"\" " ++ (T.unpack $ decUTF8Safe $ serializeURIRef' uri)- c <- liftIO $ system $ args- case c of- (ExitFailure xi) -> pure $ Left $ NonZeroExit xi "cmd.exe" [args]- ExitSuccess -> pure $ Right ()- >>= \case- Right _ -> pure ExitSuccess- Left e -> logError (T.pack $ prettyHFError e)- >> pure (ExitFailure 13)- else liftIO $ putStrLn uri' >> pure ExitSuccess+ case _rPlatform pfreq of+ Darwin -> lEM $ exec "open" [T.unpack $ decUTF8Safe $ serializeURIRef' uri] Nothing Nothing+ Linux _ -> lEM $ exec "xdg-open" [T.unpack $ decUTF8Safe $ serializeURIRef' uri] Nothing Nothing+ FreeBSD -> lEM $ exec "xdg-open" [T.unpack $ decUTF8Safe $ serializeURIRef' uri] Nothing Nothing+ OpenBSD -> lEM $ exec "xdg-open" [T.unpack $ decUTF8Safe $ serializeURIRef' uri] Nothing Nothing+ Windows -> do+ let args = "start \"\" " ++ T.unpack (decUTF8Safe $ serializeURIRef' uri)+ c <- liftIO $ system args+ case c of+ (ExitFailure xi) -> throwE $ NonZeroExit xi "cmd.exe" [args]+ ExitSuccess -> pure ()+ else liftIO $ putStrLn uri'+ ) >>= \case+ VRight _ -> do+ pure ExitSuccess+ VLeft e -> do+ runLogger $ logError $ T.pack $ prettyHFError e+ pure $ ExitFailure 11+ where+ run action' = do+ (appstate', _) <- liftIO getAppState'+ flip runReaderT appstate'+ . runResourceT+ . runE+ @'[ TagNotFound , DayNotFound , NextVerNotFound , NoToolVersionSet , ParseError, ProcessError ]+ $ action'+ runLogger = flip runReaderT leanAppstate
lib-opt/GHCup/OptParse/Common.hs view
@@ -1,87 +1,99 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-} {-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE TypeApplications #-} {-# LANGUAGE NumericUnderscores #-}-{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeApplications #-} module GHCup.OptParse.Common where -import GHCup-import GHCup.CabalConfig-import GHCup.Download-import GHCup.Platform-import GHCup.Types-import GHCup.Types.Optics-import GHCup.Utils-import qualified GHCup.Utils.Parsers as Parsers-import GHCup.Prelude-import GHCup.Prelude.Process-import GHCup.Prelude.Logger+import GHCup.CabalConfig+import GHCup.Command.List+import GHCup.Download+import GHCup.Hardcoded.Version+import GHCup.Input.Parsers+import GHCup.Prelude+import GHCup.Prelude.Process+import GHCup.Query.GHCupDirs+import GHCup.Query.Metadata+import GHCup.Query.System+import GHCup.System.Directory+import GHCup.Types+import GHCup.Types.Optics -import Control.Monad (forM, join)-import Control.DeepSeq-import Control.Concurrent-import Control.Concurrent.Async-import Control.Exception.Safe+import qualified GHCup.Input.Parsers as Parsers++import Control.Concurrent+import Control.Concurrent.Async+import Control.DeepSeq+import Control.Exception ( evaluate )+import Control.Exception.Safe+import Control.Monad ( forM, join ) #if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )+import Control.Monad.Fail ( MonadFail ) #endif-import Control.Monad.Reader-import Data.Aeson+import Control.Monad.Reader+import Data.Aeson #if MIN_VERSION_aeson(2,0,0) import qualified Data.Aeson.Key as KM import qualified Data.Aeson.KeyMap as KM #else import qualified Data.HashMap.Strict as KM #endif-import Data.ByteString.Lazy ( ByteString ) import Data.Bifunctor+import Data.ByteString.Lazy ( ByteString ) import Data.Char import Data.Either import Data.Functor-import Data.List ( nub, isPrefixOf, stripPrefix )+import Data.List ( isPrefixOf, nub, stripPrefix ) import Data.Maybe+import Data.Variant.Excepts+import qualified Data.Vector as V import Data.Versions-import qualified Data.Vector as V import GHC.IO.Exception-import Data.Variant.Excepts-import Options.Applicative hiding ( style )-import Prelude hiding ( appendFile )-import Safe (lastMay)-import System.Process ( readProcess )+import Options.Applicative hiding ( style )+import Prelude hiding ( appendFile )+import Safe ( lastMay ) import System.FilePath-import Text.HTML.TagSoup hiding ( Tag )+import System.Process ( readProcess )+import Text.HTML.TagSoup hiding ( Tag ) -import qualified Data.Map.Strict as M-import qualified Data.Text as T-import qualified System.FilePath.Posix as FP-import GHCup.Version-import Control.Exception (evaluate)+import qualified Data.Map.Strict as M+import qualified Data.Text as T+import qualified System.FilePath.Posix as FP -------------- --[ Parser ]-- -------------- +parseUrlSourceP :: Parser [NewURLSource]+parseUrlSourceP =+ option+ (eitherReader parseUrlSource)+ ( short 's'+ <> long "url-source"+ <> metavar "<URL_SOURCE|cross|prereleases|vanilla|default>"+ <> help "Alternative ghcup download info"+ <> completer urlSourceCompleter+ ) toolVersionTagArgument :: [ListCriteria] -> Maybe Tool -> Parser ToolVersion toolVersionTagArgument criteria tool = argument (eitherReader (parser tool)) (metavar (mv tool)- <> completer (tagCompleter (fromMaybe GHC tool) [])+ <> completer (tagCompleter (fromMaybe ghc tool) []) <> foldMap (completer . versionCompleter criteria) tool) where- mv (Just GHC) = "GHC_VERSION|TAG|RELEASE_DATE"- mv (Just HLS) = "HLS_VERSION|TAG|RELEASE_DATE"- mv _ = "VERSION|TAG|RELEASE_DATE"+ mv (Just (Tool "ghc")) = "GHC_VERSION|TAG|RELEASE_DATE"+ mv (Just (Tool "hls")) = "HLS_VERSION|TAG|RELEASE_DATE"+ mv _ = "VERSION|TAG|RELEASE_DATE" - parser (Just GHC) = Parsers.ghcVersionTagEither- parser Nothing = Parsers.ghcVersionTagEither- parser _ = Parsers.toolVersionTagEither+ parser (Just (Tool "ghc")) = Parsers.ghcVersionTagEither+ parser Nothing = Parsers.ghcVersionTagEither+ parser _ = Parsers.toolVersionTagEither versionParser' :: [ListCriteria] -> Maybe Tool -> Parser Version@@ -89,9 +101,9 @@ (eitherReader (first show . version . T.pack)) (metavar "VERSION" <> foldMap (completer . versionCompleter criteria) tool) -ghcVersionArgument :: [ListCriteria] -> Maybe Tool -> Parser GHCTargetVersion+ghcVersionArgument :: [ListCriteria] -> Maybe Tool -> Parser TargetVersion ghcVersionArgument criteria tool = argument (eitherReader Parsers.ghcVersionEither)- (metavar "VERSION" <> foldMap (completer . versionCompleter criteria) tool)+ (metavar "VERSION" <> help "Which version to install" <> foldMap (completer . versionCompleter criteria) tool) -- https://github.com/pcapriotti/optparse-applicative/issues/148@@ -136,6 +148,8 @@ --[ Completers ]-- ------------------ +revisionCompleter :: Completer+revisionCompleter = listCompleter ["updates", "all", "none"] toolCompleter :: Completer toolCompleter = listCompleter ["ghc", "cabal", "hls", "stack"]@@ -178,7 +192,7 @@ compgen action' r opts = do let cmd = unwords $ ["compgen", "-A", action'] <> opts <> ["--", requote r] result <- tryIO $ readProcess "bash" ["-c", cmd] ""- return . lines . either (const []) id $ result+ return . lines . fromRight [] $ result -- | Strongly quote the string we pass to compgen. --@@ -228,20 +242,20 @@ unescapeN = goX where goX ('\'' : xs) = goN xs- goX (x : xs) = x : goX xs- goX [] = []+ goX (x : xs) = x : goX xs+ goX [] = [] goN ('\\' : '\'' : xs) = '\'' : goN xs- goN ('\'' : xs) = goX xs- goN (x : xs) = x : goN xs- goN [] = []+ goN ('\'' : xs) = goX xs+ goN (x : xs) = x : goN xs+ goN [] = [] -- Unescape an unquoted string unescapeU = goX where- goX [] = []+ goX [] = [] goX ('\\' : x : xs) = x : goX xs- goX (x : xs) = x : goX xs+ goX (x : xs) = x : goX xs -- Unescape a weakly quoted string unescapeD = goX@@ -269,24 +283,25 @@ tagCompleter tool add = listIOCompleter $ do dirs' <- liftIO getAllDirs let loggerConfig = LoggerConfig- { lcPrintDebug = False+ { lcPrintDebugLvl = Nothing , consoleOutter = mempty , fileOutter = mempty , fancyColors = False }- let appState = LeanAppState- (defaultSettings { noNetwork = True })- dirs'- defaultKeyBindings- loggerConfig - mpFreq <- flip runReaderT appState . runE $ platformRequest+ mpFreq <- flip runReaderT loggerConfig . runE $ platformRequest forFold mpFreq $ \pfreq -> do+ let appState = LeanAppState+ (defaultSettings { noNetwork = True })+ dirs'+ defaultKeyBindings+ pfreq+ loggerConfig mGhcUpInfo <- flip runReaderT appState . runE $ getDownloadsF pfreq case mGhcUpInfo of VRight ghcupInfo -> do let allTags = filter (/= Old)- $ _viTags =<< M.elems (availableToolVersions (_ghcupDownloads ghcupInfo) tool)+ $ _vmTags =<< M.elems (availableToolVersions (_ghcupDownloads ghcupInfo) tool) pure $ nub $ (add ++) $ fmap tagToString allTags VLeft _ -> pure (nub $ ["recommended", "latest", "latest-prerelease"] ++ add) @@ -297,19 +312,20 @@ versionCompleter' criteria tool filter' = listIOCompleter $ do dirs' <- liftIO getAllDirs let loggerConfig = LoggerConfig- { lcPrintDebug = False+ { lcPrintDebugLvl = Nothing , consoleOutter = mempty , fileOutter = mempty , fancyColors = False }+ mpFreq <- flip runReaderT loggerConfig . runE $ platformRequest let settings = defaultSettings { noNetwork = True }- let leanAppState = LeanAppState- settings- dirs'- defaultKeyBindings- loggerConfig- mpFreq <- flip runReaderT leanAppState . runE $ platformRequest forFold mpFreq $ \pfreq -> do+ let leanAppState = LeanAppState+ settings+ dirs'+ defaultKeyBindings+ pfreq+ loggerConfig mGhcUpInfo <- flip runReaderT leanAppState . runE $ getDownloadsF pfreq forFold mGhcUpInfo $ \ghcupInfo -> do let appState = AppState@@ -320,10 +336,10 @@ pfreq loggerConfig - runEnv = flip runReaderT appState+ runEnv = flip runReaderT appState . runE - installedVersions <- runEnv $ listVersions (Just tool) criteria False False (Nothing, Nothing)- return $ fmap (T.unpack . prettyVer) . filter filter' . fmap lVer $ installedVersions+ (VRight installedVersions) <- runEnv $ listVersions (Just [tool]) criteria ShowUpdates False False (Nothing, Nothing)+ return $ fmap (T.unpack . prettyVer) . filter filter' . maybe [] (fmap lVer . snd) $ M.lookup tool installedVersions toolDlCompleter :: Tool -> Completer@@ -363,9 +379,9 @@ | "https://github.com/c" `isPrefixOf` word -> pure ["https://github.com/commercialhaskell/stack/releases/download/"] | "https://github.com/h" `isPrefixOf` word -> pure ["https://github.com/haskell/haskell-language-server/releases/download/"] | "https://g" `isPrefixOf` word- , tool == Stack -> pure ["https://github.com/commercialhaskell/stack/releases/download/"]+ , tool == stack -> pure ["https://github.com/commercialhaskell/stack/releases/download/"] | "https://g" `isPrefixOf` word- , tool == HLS -> pure ["https://github.com/haskell/haskell-language-server/releases/download/"]+ , tool == hls -> pure ["https://github.com/haskell/haskell-language-server/releases/download/"] | "https://d" `isPrefixOf` word -> pure $ filter ("https://downloads.haskell.org/" `isPrefixOf`) $ initUrl tool @@ -378,19 +394,20 @@ | otherwise -> pure [] where initUrl :: Tool -> [String]- initUrl GHC = [ "https://downloads.haskell.org/~ghc/"- , "https://downloads.haskell.org/~ghcup/unofficial-bindists/ghc/"- ]- initUrl Cabal = [ "https://downloads.haskell.org/~cabal/"- , "https://downloads.haskell.org/~ghcup/unofficial-bindists/cabal/"- ]- initUrl GHCup = [ "https://downloads.haskell.org/~ghcup/" ]- initUrl HLS = [ "https://github.com/haskell/haskell-language-server/releases/download/"- , "https://downloads.haskell.org/~ghcup/unofficial-bindists/haskell-language-server/"- ]- initUrl Stack = [ "https://github.com/commercialhaskell/stack/releases/download/"- , "https://downloads.haskell.org/~ghcup/unofficial-bindists/stack/"- ]+ initUrl (Tool "ghc") = [ "https://downloads.haskell.org/~ghc/"+ , "https://downloads.haskell.org/~ghcup/unofficial-bindists/ghc/"+ ]+ initUrl (Tool "cabal") = [ "https://downloads.haskell.org/~cabal/"+ , "https://downloads.haskell.org/~ghcup/unofficial-bindists/cabal/"+ ]+ initUrl (Tool "ghcup") = [ "https://downloads.haskell.org/~ghcup/" ]+ initUrl (Tool "hls") = [ "https://github.com/haskell/haskell-language-server/releases/download/"+ , "https://downloads.haskell.org/~ghcup/unofficial-bindists/haskell-language-server/"+ ]+ initUrl (Tool "stack") = [ "https://github.com/commercialhaskell/stack/releases/download/"+ , "https://downloads.haskell.org/~ghcup/unofficial-bindists/stack/"+ ]+ initUrl _ = [] completePrefix :: String -- ^ url, e.g. 'https://github.com/haskell/haskell-languag' -> String -- ^ match, e.g. 'haskell-language-server'@@ -452,11 +469,10 @@ getGithubAssets owner repo tag = withCurl url 3_000_000 $ \stdout -> do Just xs <- pure $ decode' @Object stdout Just (Array assets) <- pure $ KM.lookup (mkval "assets") xs- as <- fmap V.toList $ forM assets $ \val -> do+ fmap V.toList $ forM assets $ \val -> do (Object asset) <- pure val Just (String name) <- pure $ KM.lookup (mkval "name") asset pure $ T.unpack name- pure as where url = "https://api.github.com/repos/" <> owner <> "/" <> repo <> "/releases/tags/" <> tag @@ -472,33 +488,45 @@ , HasGHCupInfo env , HasDirs env , HasPlatformReq env- , MonadCatch m , HasLog env- , MonadThrow m- , MonadIO m- , MonadFail m+ , MonadIOish m )- => m [(Tool, GHCTargetVersion)]+ => m [(Tool, TargetVersionRev)] checkForUpdates = do- GHCupInfo { _ghcupDownloads = dls } <- getGHCupInfo- lInstalled <- listVersions Nothing [ListInstalled True] False False (Nothing, Nothing)- let latestInstalled tool = (fmap (\lr -> GHCTargetVersion (lCross lr) (lVer lr)) . lastMay . filter (\lr -> lTool lr == tool)) lInstalled+ dl@GHCupInfo { _ghcupDownloads = dls } <- getGHCupInfo+ pfreq <- getPlatformReq+ (VRight lInstalled') <- runE $ listVersions Nothing [ListInstalled True] ShowUpdates False False (Nothing, Nothing)+ let latestInstalled tool = do+ (_, xs) <- M.lookup tool lInstalled'+ ListResult{..} <- lastMay xs+ pure $ TargetVersion lCross lVer - ghcup <- forMM (getLatest dls GHCup) $ \(GHCTargetVersion _ l, _) -> do+ ghcup' <- forMM (getLatest dls ghcup) $ \(TargetVersion _ l) -> do (Right ghcup_ver) <- pure $ version $ prettyPVP ghcUpVer- if (l > ghcup_ver) then pure $ Just (GHCup, mkTVer l) else pure Nothing+ let tver = mkTVer l+ let rev = maybe 0 (\(_, i, _) -> i) $ getRev dls ghcup tver Nothing+ if l > ghcup_ver then pure $ Just (ghcup, TargetVersionRev tver rev) else pure Nothing - otherTools <- forM [GHC, Cabal, HLS, Stack] $ \t ->- forMM (getLatest dls t) $ \(l, _) -> do+ otherTools <- forM (fst <$> allAvailableTools dls) $ \t ->+ forMM (getLatest dls t) $ \l -> do let mver = latestInstalled t+ let rev = maybe 0 (\(_, i, _) -> i) $ getRev dls t l Nothing forMM mver $ \ver ->- if (l > ver) then pure $ Just (t, l) else pure Nothing+ if l > ver then pure $ Just (t, TargetVersionRev l rev) else pure Nothing - pure $ catMaybes (ghcup:otherTools)+ let revUpdates = mconcat $ M.toList lInstalled' <&> \(tool, (_, lrs)) -> catMaybes $+ lrs <&> \ListResult{..} -> do+ let tver = TargetVersion lCross lVer+ (rev, _) <- either (const Nothing) pure $ getDownloadInfo tool (TargetVersionReq tver Nothing) dl pfreq+ if rev > fst lRev+ then Just (tool, TargetVersionRev tver rev)+ else Nothing++ pure $ nub (catMaybes (ghcup':otherTools) <> revUpdates) where forMM a f = fmap join $ forM a f -logGHCPostRm :: (MonadReader env m, HasLog env, MonadIO m) => GHCTargetVersion -> m ()+logGHCPostRm :: (MonadReader env m, HasLog env, MonadIO m) => TargetVersion -> m () logGHCPostRm ghcVer = do cabalStore <- liftIO $ handleIO (\_ -> if isWindows then pure "C:\\cabal\\store" else pure "~/.cabal/store or ~/.local/state/cabal/store") getStoreDir
lib-opt/GHCup/OptParse/Compile.hs view
@@ -1,52 +1,55 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-} {-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeApplications #-} module GHCup.OptParse.Compile where -import GHCup-import qualified GHCup.GHC as GHC-import qualified GHCup.HLS as HLS-import GHCup.Errors-import GHCup.Types-import GHCup.Types.Optics-import GHCup.Utils-import GHCup.Utils.Parsers (fromVersion, uriParser, ghcVersionTagEither, isolateParser, overWriteVersionParser)-import GHCup.Prelude.Logger-import GHCup.Prelude.String.QQ-import GHCup.OptParse.Common+import GHCup.Command.Set+import GHCup.Errors+import GHCup.Input.Parsers+import GHCup.OptParse.Common+import GHCup.Prelude.Logger+import GHCup.Prelude.String.QQ+import GHCup.Query.DB+import GHCup.Query.GHCupDirs+import GHCup.Query.Metadata+import GHCup.Types+import GHCup.Types.Optics +import qualified GHCup.Command.Compile.GHC as GHC+import qualified GHCup.Command.Compile.HLS as HLS+ #if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )+import Control.Monad.Fail ( MonadFail ) #endif-import Control.Monad (when, forM_, forM)-import Control.Concurrent (threadDelay)-import Control.Monad.Reader-import Control.Monad.Trans.Resource-import Data.Bifunctor-import Data.Functor-import Data.Maybe-import Data.Versions ( Version, prettyVer, version, pvp )-import qualified Data.Versions as V-import Data.Text ( Text )-import Data.Variant.Excepts-import Options.Applicative hiding ( style )-import Options.Applicative.Pretty.Shim ( text, vsep )-import Prelude hiding ( appendFile )-import System.Exit+import Control.Concurrent ( threadDelay )+import Control.Exception.Safe ( MonadMask, displayException )+import Control.Monad ( forM, forM_, when, (<=<) )+import Control.Monad.Reader+import Control.Monad.Trans.Resource+import Data.Bifunctor+import Data.Functor+import Data.Maybe+import Data.Text ( Text )+import Data.Variant.Excepts+import Data.Versions ( Version, prettyVer, pvp, version )+import Options.Applicative hiding ( ParseError, style )+import Options.Applicative.Pretty.Shim ( text, vsep )+import Prelude hiding ( appendFile )+import System.Exit+import System.FilePath ( isPathSeparator )+import Text.PrettyPrint.HughesPJClass ( prettyShow )+import Text.Read ( readEither )+import URI.ByteString hiding ( uriParser ) -import URI.ByteString hiding ( uriParser )-import qualified Data.Text as T-import Control.Exception.Safe (MonadMask, displayException)-import System.FilePath (isPathSeparator)-import Text.Read (readEither)+import qualified Data.Text as T+import qualified Data.Versions as V @@ -56,9 +59,10 @@ ---------------- -data CompileCommand = CompileGHC GHCCompileOptions- | CompileHLS HLSCompileOptions- deriving (Eq, Show)+data CompileCommand+ = CompileGHC GHCCompileOptions+ | CompileHLS HLSCompileOptions+ deriving (Eq, Show) @@ -68,36 +72,38 @@ data GHCCompileOptions = GHCCompileOptions- { targetGhc :: GHC.GHCVer+ { targetGhc :: GHC.GHCVer , bootstrapGhc :: Either Version FilePath- , hadrianGhc :: Maybe (Either Version FilePath)- , jobs :: Maybe Int- , buildConfig :: Maybe FilePath- , patches :: Maybe (Either FilePath [URI])- , crossTarget :: Maybe Text- , addConfArgs :: [Text]- , setCompile :: Bool+ , hadrianGhc :: Maybe (Either Version FilePath)+ , jobs :: Maybe Int+ , buildConfig :: Maybe FilePath+ , patches :: Maybe (Either FilePath [URI])+ , crossTarget :: Maybe Text+ , addConfArgs :: [String]+ , setCompile :: Bool , overwriteVer :: Maybe [VersionPattern] , buildFlavour :: Maybe String- , buildSystem :: Maybe BuildSystem- , isolateDir :: Maybe FilePath- , installTargets :: T.Text- } deriving (Eq, Show)+ , buildSystem :: Maybe BuildSystem+ , isolateDir :: Maybe FilePath+ , installTargets :: Maybe [String]+ }+ deriving (Eq, Show) data HLSCompileOptions = HLSCompileOptions- { targetHLS :: HLS.HLSVer- , jobs :: Maybe Int- , setCompile :: Bool- , updateCabal :: Bool+ { targetHLS :: HLS.HLSVer+ , jobs :: Maybe Int+ , setCompile :: Bool+ , updateCabal :: Bool , overwriteVer :: Maybe [VersionPattern]- , isolateDir :: Maybe FilePath+ , isolateDir :: Maybe FilePath , cabalProject :: Maybe (Either FilePath URI) , cabalProjectLocal :: Maybe URI- , patches :: Maybe (Either FilePath [URI])- , targetGHCs :: [ToolVersion]- , cabalArgs :: [Text]- } deriving (Eq, Show)+ , patches :: Maybe (Either FilePath [URI])+ , targetGHCs :: [ToolVersion]+ , cabalArgs :: [Text]+ }+ deriving (Eq, Show) @@ -174,7 +180,7 @@ ) (short 'v' <> long "version" <> metavar "VERSION" <> help "The tool version to compile"- <> (completer $ versionCompleter [] GHC)+ <> completer (versionCompleter [] ghc) ) ) <|> (GHC.GitDist <$> (GitBranch <$> option@@ -189,7 +195,7 @@ )) <|> (- GHC.RemoteDist <$> (option+ GHC.RemoteDist <$> option (eitherReader uriParser) (long "remote-source-dist" <> metavar "URI" <> help "URI (https/http/file) to a GHC source distribution"@@ -197,7 +203,6 @@ ) ) )- ) <*> option (eitherReader (\x ->@@ -209,7 +214,7 @@ <> metavar "BOOTSTRAP_GHC" <> help "The GHC version (or full path) to bootstrap with (must be installed)"- <> (completer $ versionCompleter [] GHC)+ <> completer (versionCompleter [] ghc) ) <*> optional (option (eitherReader@@ -221,19 +226,19 @@ <> metavar "HADRIAN_GHC" <> help "The GHC version (or full path) that will be used to compile hadrian (must be installed)"- <> (completer $ versionCompleter [] GHC)+ <> completer (versionCompleter [] ghc) )) <*> optional (option (eitherReader (readEither @Int)) (short 'j' <> long "jobs" <> metavar "JOBS" <> help "How many jobs to use for make"- <> (completer $ listCompleter $ fmap show ([1..12] :: [Int]))+ <> completer (listCompleter $ fmap show ([1..12] :: [Int])) ) )- <*> (optional+ <*> optional (- (fmap Right $ many $ option+ fmap Right (many $ option (eitherReader uriParser) (long "patch" <> metavar "PATCH_URI" <> help "URI to a patch (https/http/file)"@@ -241,7 +246,7 @@ ) ) <|>- (fmap Left $ option+ fmap Left (option str (short 'p' <> long "patchdir" <> metavar "PATCH_DIR" <> help "Absolute path to patch directory (applies all .patch and .diff files in order using -p1. This order is determined by a quilt series file if it exists, or the patches are lexicographically ordered)"@@ -249,7 +254,6 @@ ) ) )- ) <*> optional (option str@@ -271,7 +275,7 @@ , text "%H long commit hash" , text "%g 'git describe' output" ])- <> (completer $ versionCompleter [] GHC)+ <> completer (versionCompleter [] ghc) ) ) <*> optional@@ -288,12 +292,12 @@ <|> ((\b c -> case (b, c) of -- --make specified- (True, _) -> (Just Make, c)+ (True, _) -> (Just Make, c) -- only --config specified... assume make (False, Just _) -> (Just Make, c) -- otherwise fall back to runtime detection of build -- system- (False, _) -> (Nothing, c)) <$> switch+ (False, _) -> (Nothing, c)) <$> switch (long "make" <> help "Use the make build system instead of hadrian. Tries to detect by default." ) <*> optional@@ -316,24 +320,24 @@ <> completer (bashCompleter "directory") ) )- <*> strOption+ <*> optional (option (eitherReader installTargetParser) ( long "install-targets" <> metavar "TARGETS"- <> help "Space separated list of install targets (default: install)"+ <> help "Overwrite make based install targets" <> completer (listCompleter ["install", "install_bin", "install_lib", "install_extra", "install_man", "install_docs", "install_data", "update_package_db"])- <> value "install" )+ ) hlsCompileOpts :: Parser HLSCompileOptions hlsCompileOpts = HLSCompileOptions <$> ((HLS.HackageDist <$> option (eitherReader- ((>>= first displayException . V.version . V.prettyPVP) . first (const "Not a valid PVP version") . pvp . T.pack)+ ((first displayException . V.version . V.prettyPVP) <=< first (const "Not a valid PVP version") . pvp . T.pack) ) (short 'v' <> long "version" <> metavar "VERSION" <> help "The version to compile (pulled from hackage)"- <> (completer $ versionCompleter' [] HLS (either (const False) (const True) . V.pvp . V.prettyVer))+ <> completer (versionCompleter' [] hls (either (const False) (const True) . V.pvp . V.prettyVer)) ) ) <|>@@ -347,18 +351,18 @@ )) )) <|>- (HLS.SourceDist <$> (option+ (HLS.SourceDist <$> option (eitherReader (first (const "Not a valid version") . version . T.pack) ) (long "source-dist" <> metavar "VERSION" <> help "The version to compile (pulled from packaged git sources)"- <> (completer $ versionCompleter [] HLS)+ <> completer (versionCompleter [] hls) )- ))+ ) <|> (- HLS.RemoteDist <$> (option+ HLS.RemoteDist <$> option (eitherReader uriParser) (long "remote-source-dist" <> metavar "URI" <> help "URI (https/http/file) to a HLS source distribution"@@ -366,13 +370,12 @@ ) ) )- ) <*> optional (option (eitherReader (readEither @Int)) (short 'j' <> long "jobs" <> metavar "JOBS" <> help "How many jobs to use for make"- <> (completer $ listCompleter $ fmap show ([1..12] :: [Int]))+ <> completer (listCompleter $ fmap show ([1..12] :: [Int])) ) ) <*> fmap (fromMaybe True) (invertableSwitch "set" Nothing True (help "Don't set as active version after install"))@@ -390,16 +393,15 @@ , text "%H long commit hash" , text "%g 'git describe' output" ])- <> (completer $ versionCompleter [] HLS)+ <> completer (versionCompleter [] hls) ) ) <|>- ((\b -> if b then Just [GitDescribe] else Nothing) <$> (switch+ ((\b -> if b then Just [GitDescribe] else Nothing) <$> switch (long "git-describe-version" <> help "Use the output of 'git describe' (if building from git) as the VERSION component of the installed binary." <> internal )- ) ) ) <*> optional@@ -414,7 +416,7 @@ ) <*> optional (option- ((fmap Right $ eitherReader uriParser) <|> (fmap Left str))+ (fmap Right (eitherReader uriParser) <|> fmap Left str) (long "cabal-project" <> metavar "CABAL_PROJECT" <> help "If relative filepath, specifies the path to cabal.project inside the unpacked HLS tarball/checkout. Otherwise expects a full URI with https/http/file scheme." <> completer fileUri@@ -428,9 +430,9 @@ <> completer fileUri ) )- <*> (optional+ <*> optional (- (fmap Right $ many $ option+ fmap Right (many $ option (eitherReader uriParser) (long "patch" <> metavar "PATCH_URI" <> help "URI to a patch (https/http/file)"@@ -438,7 +440,7 @@ ) ) <|>- (fmap Left $ option+ fmap Left (option str (short 'p' <> long "patchdir" <> metavar "PATCH_DIR" <> help "Absolute path to patch directory (applies all .patch and .diff files in order using -p1)"@@ -446,12 +448,11 @@ ) ) )- ) <*> some ( option (eitherReader ghcVersionTagEither) ( long "ghc" <> metavar "GHC_VERSION|TAG" <> help "For which GHC version to compile for (can be specified multiple times)"- <> completer (tagCompleter GHC [])- <> completer (versionCompleter [] GHC))+ <> completer (tagCompleter ghc [])+ <> completer (versionCompleter [] ghc)) ) <*> many (argument str (metavar "CABAL_ARGS" <> help "Additional arguments to cabal install, prefix with '-- ' (longopts)")) @@ -488,7 +489,12 @@ , UninstallFailed , MergeFileTreeError , URIParseError+ , FileAlreadyExistsError+ , ParseError+ , NoInstallInfo+ , MalformedInstallInfo ]+ type HLSEffects = '[ AlreadyInstalled , BuildFailed , DigestError@@ -511,32 +517,15 @@ , UninstallFailed , MergeFileTreeError , URIParseError+ , ParseError+ , NoInstallInfo+ , MalformedInstallInfo ] -runCompileGHC :: (MonadUnliftIO m, MonadIO m)- => (ReaderT AppState m (VEither GHCEffects a) -> m (VEither GHCEffects a))- -> Excepts GHCEffects (ResourceT (ReaderT AppState m)) a- -> m (VEither GHCEffects a)-runCompileGHC runAppState =- runAppState- . runResourceT- . runE- @GHCEffects -runCompileHLS :: (MonadUnliftIO m, MonadIO m)- => (ReaderT AppState m (VEither HLSEffects a) -> m (VEither HLSEffects a))- -> Excepts HLSEffects (ResourceT (ReaderT AppState m)) a- -> m (VEither HLSEffects a)-runCompileHLS runAppState =- runAppState- . runResourceT- . runE- @HLSEffects -- ------------------ --[ Entrypoint ]-- ------------------@@ -551,51 +540,62 @@ => CompileCommand -> Settings -> Dirs- -> (forall eff a . ReaderT AppState m (VEither eff a) -> m (VEither eff a))- -> (ReaderT LeanAppState m () -> m ())+ -> (IO (AppState, IO ()), LeanAppState) -> m ExitCode-compile compileCommand settings Dirs{..} runAppState runLogger = do+compile compileCommand settings Dirs{..} (getAppState', leanAppstate) = do+ (s', updateCheckAction) <- liftIO getAppState'+ liftIO updateCheckAction case compileCommand of (CompileHLS HLSCompileOptions { .. }) -> do- runCompileHLS runAppState (do+ runCompileHLS s' (do case targetHLS of HLS.SourceDist targetVer -> do GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo- let vi = getVersionInfo (mkTVer targetVer) HLS dls- forM_ (_viPreInstall =<< vi) $ \msg -> do+ let vm = getVersionMetadata (mkTVer targetVer) hls dls+ forM_ (_vmPreInstall =<< vm) $ \msg -> do lift $ logWarn msg lift $ logWarn "...waiting for 5 seconds, you can still abort..." liftIO $ threadDelay 5000000 -- give the user a sec to intervene- forM_ (_viPreCompile =<< vi) $ \msg -> do+ forM_ (_vmPreCompile =<< vm) $ \msg -> do lift $ logWarn msg lift $ logWarn "...waiting for 5 seconds, you can still abort..." liftIO $ threadDelay 5000000 -- for compilation, give the user a sec to intervene _ -> pure ()- ghcs <- liftE $ forM targetGHCs (\ghc -> fmap (_tvVersion . fst) . fromVersion (Just ghc) guessMode $ GHC)- targetVer <- liftE $ compileHLS+ ghcs' <- liftE $ forM targetGHCs (\ghc' -> resolveVersion (Just ghc') guessMode ghc)+ ghcs <- forM ghcs' $ \TargetVersionReq{..} -> do+ when (isJust $ _tvTarget _tvqTargetVer) $ fail "Cannot compile HLS for a cross GHC"+ pure (_tvVersion _tvqTargetVer)++ let instDir = maybe GHCupInternal IsolateDir isolateDir+ targetVer <- liftE $ HLS.compileHLS targetHLS ghcs jobs overwriteVer- (maybe GHCupInternal IsolateDir isolateDir)+ instDir cabalProject cabalProjectLocal updateCabal patches cabalArgs GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo- let vi = getVersionInfo (mkTVer targetVer) HLS dls- when setCompile $ void $ liftE $- setHLS targetVer SetHLSOnly Nothing- pure (vi, targetVer)+ let vm = getVersionMetadata (mkTVer targetVer) hls dls+ -- TODO: also set if it's already set+ case instDir of+ GHCupInternal -> do+ set <- liftE $ isSet hls (mkTVer targetVer)+ when (setCompile || isJust set) $ void $ liftE $+ setToolVersion hls (mkTVer targetVer)+ IsolateDir _ -> pure ()+ pure (vm, targetVer) ) >>= \case- VRight (vi, tv) -> do+ VRight (vm, tv) -> do runLogger $ logInfo "HLS successfully compiled and installed"- forM_ (_viPostInstall =<< vi) $ \msg ->+ forM_ (_vmPostInstall =<< vm) $ \msg -> runLogger $ logInfo msg liftIO $ putStr (T.unpack $ prettyVer tv) pure ExitSuccess@@ -611,23 +611,24 @@ runLogger $ logError $ T.pack $ prettyHFError e pure $ ExitFailure 9 (CompileGHC GHCCompileOptions {..}) ->- runCompileGHC runAppState (do+ runCompileGHC s' (do case targetGhc of GHC.SourceDist targetVer -> do GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo- let vi = getVersionInfo (mkTVer targetVer) GHC dls- forM_ (_viPreInstall =<< vi) $ \msg -> do+ let vm = getVersionMetadata (mkTVer targetVer) ghc dls+ forM_ (_vmPreInstall =<< vm) $ \msg -> do lift $ logWarn msg lift $ logWarn "...waiting for 5 seconds, you can still abort..." liftIO $ threadDelay 5000000 -- give the user a sec to intervene- forM_ (_viPreCompile =<< vi) $ \msg -> do+ forM_ (_vmPreCompile =<< vm) $ \msg -> do lift $ logWarn msg lift $ logWarn "...waiting for 5 seconds, you can still abort..." liftIO $ threadDelay 5000000 -- for compilation, give the user a sec to intervene _ -> pure ()- targetVer <- liftE $ compileGHC+ let instDir = maybe GHCupInternal IsolateDir isolateDir+ targetVer <- liftE $ GHC.compileGHC targetGhc crossTarget overwriteVer@@ -639,25 +640,29 @@ addConfArgs buildFlavour buildSystem- (maybe GHCupInternal IsolateDir isolateDir)+ instDir installTargets GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo- let vi = getVersionInfo targetVer GHC dls- when setCompile $ void $ liftE $- setGHC targetVer SetGHCOnly Nothing- pure (vi, targetVer)+ let vm = getVersionMetadata targetVer ghc dls+ case instDir of+ GHCupInternal -> do+ set <- liftE $ isSet ghc targetVer+ when (setCompile || isJust set) $ void $ liftE $+ setToolVersion ghc targetVer+ IsolateDir _ -> pure ()+ pure (vm, targetVer) ) >>= \case- VRight (vi, tv) -> do+ VRight (vm, tv) -> do runLogger $ logInfo "GHC successfully compiled and installed"- forM_ (_viPostInstall =<< vi) $ \msg ->+ forM_ (_vmPostInstall =<< vm) $ \msg -> runLogger $ logInfo msg liftIO $ putStr (T.unpack $ tVerToText tv) pure ExitSuccess VLeft (V (AlreadyInstalled _ v)) -> do runLogger $ logWarn $- "GHC ver " <> prettyVer v <> " already installed, remove it first to reinstall"+ "GHC ver " <> T.pack (prettyShow v) <> " already installed, remove it first to reinstall" pure ExitSuccess VLeft (V (DirNotEmpty fp)) -> do runLogger $ logError $@@ -675,5 +680,15 @@ runLogger $ logError $ T.pack $ prettyHFError e pure $ ExitFailure 9 where+ runLogger = flip runReaderT leanAppstate guessMode = if guessVersion settings then GLaxWithInstalled else GStrict-+ runCompileGHC appstate' =+ flip runReaderT appstate'+ . runResourceT+ . runE+ @GHCEffects+ runCompileHLS appstate' =+ flip runReaderT appstate'+ . runResourceT+ . runE+ @HLSEffects
lib-opt/GHCup/OptParse/Config.hs view
@@ -1,45 +1,45 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-} {-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeApplications #-} module GHCup.OptParse.Config where -import GHCup.Errors-import GHCup.Types-import GHCup.Utils-import GHCup.Utils.Parsers (parseNewUrlSource)-import GHCup.Prelude-import GHCup.Prelude.Logger-import GHCup.Prelude.String.QQ-import GHCup.OptParse.Common-import GHCup.Version+import GHCup.Errors+import GHCup.Hardcoded.URLs+import GHCup.Input.Parsers ( parseNewUrlSource )+import GHCup.OptParse.Common+import GHCup.OptParse.Reset ( resetUserConfig, toUserSettingsKey )+import GHCup.Prelude+import GHCup.Prelude.String.QQ+import GHCup.Query.GHCupDirs+import GHCup.Types #if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )+import Control.Monad.Fail ( MonadFail ) #endif-import Control.Monad (when)-import Control.Exception ( displayException )-import Control.Monad.Reader-import Control.Monad.Trans.Resource-import Data.Functor-import Data.Maybe-import Data.Variant.Excepts-import Options.Applicative hiding ( style, ParseError )-import Options.Applicative.Pretty.Shim ( text )-import Prelude hiding ( appendFile )-import System.Exit+import Control.Exception ( displayException )+import Control.Exception.Safe ( MonadMask )+import Control.Monad ( when )+import Control.Monad.Reader+import Control.Monad.Trans.Resource+import Data.Foldable ( foldl' )+import Data.Functor+import Data.Maybe+import Data.Variant.Excepts+import Options.Applicative hiding ( ParseError, style )+import Options.Applicative.Pretty.Shim ( text )+import Prelude hiding ( appendFile )+import System.Exit -import qualified Data.Text as T-import qualified Data.ByteString.UTF8 as UTF8-import qualified Data.Yaml.Aeson as Y-import Control.Exception.Safe (MonadMask)+import qualified Data.ByteString.UTF8 as UTF8+import qualified Data.Text as T+import qualified Data.Yaml.Aeson as Y @@ -52,10 +52,15 @@ data ConfigCommand = ShowConfig | SetConfig String (Maybe String)+ | ResetConfig ResetCommand | InitConfig | AddReleaseChannel Bool NewURLSource deriving (Eq, Show) +data ResetCommand+ = ResetKeys [String]+ | ResetAll+ deriving (Eq, Show) ---------------@@ -67,6 +72,7 @@ configP = subparser ( command "init" initP <> command "set" setP -- [set] KEY VALUE at help lhs+ <> command "reset" resetP <> command "show" showP <> command "add-release-channel" addP )@@ -74,15 +80,24 @@ where initP = info (pure InitConfig) (progDesc "Write default config to ~/.ghcup/config.yaml") showP = info (pure ShowConfig) (progDesc "Show current config (default)")- setP = info argsP (progDesc "Set config KEY to VALUE (or specify as single json value)" <> footerDoc (Just $ text configSetFooter))- argsP = SetConfig <$> argument str (metavar "<JSON_VALUE | YAML_KEY>") <*> optional (argument str (metavar "YAML_VALUE"))+ setP = info setArgsP (progDesc "Set config KEY to VALUE (or specify as single json value)" <> footerDoc (Just $ text configSetFooter))+ setArgsP = SetConfig <$> argument str (metavar "<JSON_VALUE | YAML_KEY>") <*> optional (argument str (metavar "YAML_VALUE"))+ resetP = info resetArgsP+ (progDesc "Reset the whole config or just specific keys" <> footerDoc (Just $ text configResetFooter))+ resetArgsP = ResetConfig <$> subparser+ ( command "all"+ (info (pure ResetAll) (progDesc "Reset the whole config"))+ <> command "keys"+ (info resetKeysP (progDesc "Reset specific keys of the config"))+ )+ resetKeysP = ResetKeys <$> some (strArgument+ ( metavar "YAML_KEY"+ <> help "Specify key(s)" )) addP = info (AddReleaseChannel <$> switch (long "force" <> help "Delete existing entry (if any) and append instead of failing") <*> argument (eitherReader parseNewUrlSource) (metavar "<URL_SOURCE|cross|prereleases|vanilla>" <> completer urlSourceCompleter)) (progDesc "Add a release channel, e.g. from a URI or using alias") -- -------------- --[ Footer ]-- --------------@@ -100,6 +115,9 @@ # set <key> <value> configuration pair ghcup config set <key> <value> + # reset config key(s)+ ghcup config reset keys <key> <key> ...+ # add a release channel ghcup config add-release-channel prereleases|] @@ -120,8 +138,18 @@ # set mirror for ghcup metadata ghcup config set '{url-source: { OwnSource: "<url>"}}'|] +configResetFooter :: String+configResetFooter = [s|Examples:+ # reset the whole config+ ghcup config reset all + # reset one key (cache)+ ghcup config reset keys cache + # reset some keys (cache, url-source and downloader)+ ghcup config reset keys cache url-source downloader|]++ ----------------- --[ Utilities ]-- -----------------@@ -148,7 +176,8 @@ defGHCconfOptions' = uDefGHCConfOptions usl <|> uDefGHCConfOptions usr pagerConfig' = uPager usl <|> uPager usr guessVersion' = uGuessVersion usl <|> uGuessVersion usr- in UserSettings cache' metaCache' metaMode' noVerify' verbose' keepDirs' downloader' (updateKeyBindings (uKeyBindings usl) (uKeyBindings usr)) urlSource' noNetwork' gpgSetting' platformOverride' mirrors' defGHCconfOptions' pagerConfig' guessVersion'+ buildWrapper' = uBuildWrapper usl <|> uBuildWrapper usr+ in UserSettings cache' metaCache' metaMode' noVerify' verbose' keepDirs' downloader' (updateKeyBindings (uKeyBindings usl) (uKeyBindings usr)) urlSource' noNetwork' gpgSetting' platformOverride' mirrors' defGHCconfOptions' pagerConfig' guessVersion' buildWrapper' where updateKeyBindings :: Maybe UserKeyBindings -> Maybe UserKeyBindings -> Maybe UserKeyBindings updateKeyBindings Nothing Nothing = Nothing@@ -159,6 +188,9 @@ kUp = kUp kbl <|> kUp kbr , kDown = kDown kbl <|> kDown kbr , kQuit = kQuit kbl <|> kQuit kbr+ , kLeft = kLeft kbl <|> kLeft kbr+ , kRight = kRight kbl <|> kRight kbr+ , kTab = kTab kbl <|> kTab kbr , kInstall = kInstall kbl <|> kInstall kbr , kUninstall = kUninstall kbl <|> kUninstall kbr , kSet = kSet kbl <|> kSet kbr@@ -172,9 +204,10 @@ --[ Entrypoint ]-- ------------------ -data Duplicate = Duplicate -- ^ there is a duplicate somewhere in the middle- | NoDuplicate -- ^ there is no duplicate- | DuplicateLast -- ^ there's a duplicate, but it's the last element+data Duplicate+ = Duplicate -- ^ there is a duplicate somewhere in the middle+ | NoDuplicate -- ^ there is no duplicate+ | DuplicateLast config :: forall m. ( Monad m@@ -186,9 +219,9 @@ -> Settings -> UserSettings -> KeyBindings- -> (ReaderT LeanAppState m () -> m ())+ -> (IO (AppState, IO ()), LeanAppState) -> m ExitCode-config configCommand settings userConf keybindings runLogger = case configCommand of+config configCommand settings userConf keybindings (_getAppState', leanAppstate) = case configCommand of InitConfig -> do path <- getConfigFilePath liftIO $ writeFile path $ formatConfig $ fromSettings settings (Just keybindings)@@ -224,6 +257,29 @@ VLeft e -> do runLogger (logError $ T.pack $ prettyHFError e) pure $ ExitFailure 65+ (ResetConfig resetCommand) -> do+ r <- runE @'[ParseError] $ do+ case resetCommand of+ ResetAll -> do+ lift $ doReset defaultUserSettings+ pure ()+ ResetKeys stringKeys -> do+ lift $ runLogger $ logDebug $ "Raw keys: " <> T.pack (show stringKeys)+ let eKeys = traverse toUserSettingsKey stringKeys+ lift $ runLogger $ logDebug $ "Handled keys: " <> T.pack (show eKeys)+ case eKeys of+ Left invalidString -> do+ throwE $ ParseError $ "Key <<" <> invalidString <> ">> is invalid"+ Right keys -> do+ lift $ runLogger $ logDebug $ "userConf: " <> T.pack (show userConf)+ let newUserConf = foldl' (\conf key -> resetUserConfig conf key ) userConf keys+ lift $ doReset newUserConf+ pure ()+ case r of+ VRight _ -> pure ExitSuccess+ VLeft e -> do+ runLogger (logError $ T.pack $ prettyHFError e)+ pure $ ExitFailure 65 AddReleaseChannel force new -> do r <- runE @'[DuplicateReleaseChannel] $ do@@ -242,6 +298,8 @@ pure $ ExitFailure 15 where+ runLogger = flip runReaderT leanAppstate+ checkDuplicate :: Eq a => [a] -> a -> Duplicate checkDuplicate xs a | last xs == a = DuplicateLast@@ -250,14 +308,21 @@ aliasToURI :: NewURLSource -> NewURLSource aliasToURI (NewChannelAlias a) = NewURI (channelURL a)- aliasToURI v = v+ aliasToURI v = v doConfig :: MonadIO m => UserSettings -> m () doConfig usersettings = do let settings' = updateSettings usersettings userConf path <- liftIO getConfigFilePath- liftIO $ writeFile path $ formatConfig $ settings'+ liftIO $ writeFile path $ formatConfig settings' runLogger $ logDebug $ T.pack $ show settings'+ pure ()++ doReset :: MonadIO m => UserSettings -> m ()+ doReset resetUserSettings = do+ path <- liftIO getConfigFilePath+ liftIO $ writeFile path $ formatConfig resetUserSettings+ runLogger $ logDebug $ "reset to config: " <> T.pack (show resetUserSettings) pure () decodeSettings = lE' (JSONDecodeError . displayException) . Y.decodeEither' . UTF8.fromString
− lib-opt/GHCup/OptParse/DInfo.hs
@@ -1,141 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE RankNTypes #-}--module GHCup.OptParse.DInfo where-----import GHCup-import GHCup.Errors-import GHCup.Version-import GHCup.Types-import GHCup.Utils.Dirs-import GHCup.Prelude-import GHCup.Prelude.Logger-import GHCup.Prelude.Process--#if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )-#endif-import Control.Monad.Reader-import Control.Monad.Trans.Resource-import Data.Functor-import Data.Maybe-import Data.List ( intercalate )-import Data.Variant.Excepts-import Options.Applicative hiding ( style )-import Prelude hiding ( appendFile )-import System.Exit-import System.FilePath-import Text.PrettyPrint.HughesPJClass ( prettyShow )-import URI.ByteString (serializeURIRef')--import qualified Data.Text as T-import Control.Exception.Safe (MonadMask)-import Language.Haskell.TH---- ------------------ --[ Utilities ]--- --------------------describe_result :: String-describe_result = $( LitE . StringL <$>- runIO (do- CapturedProcess{..} <- do- dirs <- liftIO getAllDirs- let settings = AppState (defaultSettings { noNetwork = True })- dirs- defaultKeyBindings- flip runReaderT settings $ executeOut "git" ["describe"] Nothing- case _exitCode of- ExitSuccess -> pure . T.unpack . decUTF8Safe' $ _stdOut- ExitFailure _ -> pure numericVer- )- )---prettyDebugInfo :: DebugInfo -> String-prettyDebugInfo DebugInfo { diDirs = Dirs { .. }, ..} =- "===== Main ======" <> "\n" <>- "Architecture: " <> prettyShow diArch <> "\n" <>- "Platform: " <> prettyShow diPlatform <> "\n" <>- "GHCup Version: " <> describe_result <> "\n" <>- "===== Directories ======" <> "\n" <>- "base: " <> fromGHCupPath baseDir <> "\n" <>- "bin: " <> binDir <> "\n" <>- "GHCs: " <> (fromGHCupPath baseDir </> "ghc") <> "\n" <>- "cache: " <> fromGHCupPath cacheDir <> "\n" <>- "logs: " <> fromGHCupPath logsDir <> "\n" <>- "config: " <> fromGHCupPath confDir <> "\n" <>- "db: " <> fromGHCupPath dbDir <> "\n" <>- whenWin ("recycle: " <> fromGHCupPath recycleDir <> "\n") <>- "temp: " <> fromGHCupPath tmpDir <> "\n" <>- whenWin ("msys2: " <> msys2Dir <> "\n") <>- "\n===== Metadata ======\n" <>- intercalate "\n" ((\(c, u) -> pad (c <> ":") <> " " <> u) <$> channels)- where- pad xs- | xl < maxLength = xs <> replicate (maxLength - xl) ' '- | otherwise = xs- where- xl = length xs- channels = (\(c, u) -> (T.unpack . channelAliasText $ c, T.unpack . decUTF8Safe . serializeURIRef'$ u)) <$> diChannels- maxLength = (+1) . maximum . fmap (length . fst) $ channels- whenWin x = if isWindows then x else mempty----- ---------------------------- --[ Effect interpreters ]--- ------------------------------type DInfoEffects = '[ NoCompatiblePlatform , NoCompatibleArch , DistroNotFound ]--runDebugInfo :: (ReaderT env m (VEither DInfoEffects a) -> m (VEither DInfoEffects a))- -> Excepts DInfoEffects (ReaderT env m) a- -> m (VEither DInfoEffects a)-runDebugInfo runAppState =- runAppState- . runE- @DInfoEffects---- ------------------- --[ Entrypoint ]--- ----------------------dinfo :: ( Monad m- , MonadMask m- , MonadUnliftIO m- , MonadFail m- , Alternative m- )- => (ReaderT AppState m (VEither DInfoEffects DebugInfo)- -> m (VEither DInfoEffects DebugInfo))- -> (ReaderT LeanAppState m () -> m ())- -> m ExitCode-dinfo runAppState runLogger = do- runDebugInfo runAppState (liftE getDebugInfo)- >>= \case- VRight di -> do- liftIO $ putStrLn $ prettyDebugInfo di- pure ExitSuccess- VLeft e -> do- runLogger $ logError $ T.pack $ prettyHFError e- pure $ ExitFailure 8
+ lib-opt/GHCup/OptParse/DebugInfo.hs view
@@ -0,0 +1,142 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE RankNTypes #-}++module GHCup.OptParse.DebugInfo where+++++import GHCup.Command.DebugInfo+import GHCup.Errors+import GHCup.Hardcoded.Version+import GHCup.Types+import GHCup.Query.GHCupDirs+import GHCup.Prelude+import GHCup.Prelude.Process++#if !MIN_VERSION_base(4,13,0)+import Control.Monad.Fail ( MonadFail )+#endif+import Control.Monad.Reader+import Control.Monad.Trans.Resource+import Data.Functor+import Data.Maybe+import Data.List ( intercalate )+import Data.Variant.Excepts+import Options.Applicative hiding ( style )+import Prelude hiding ( appendFile )+import System.Exit+import System.FilePath+import Text.PrettyPrint.HughesPJClass ( prettyShow )+import URI.ByteString (serializeURIRef')++import qualified Data.Text as T+import Control.Exception.Safe (MonadMask)+import Language.Haskell.TH+import Data.Bifunctor (bimap)++++ -----------------+ --[ Utilities ]--+ -----------------+++describe_result :: String+describe_result = $( LitE . StringL <$>+ runIO (do+ CapturedProcess{..} <- do+ dirs <- liftIO getAllDirs+ let settings = AppState (defaultSettings { noNetwork = True })+ dirs+ defaultKeyBindings+ flip runReaderT settings $ executeOut "git" ["describe"] Nothing+ case _exitCode of+ ExitSuccess -> pure . T.unpack . decUTF8Safe' $ _stdOut+ ExitFailure _ -> pure numericVer+ )+ )+++prettyDebugInfo :: DebugInfo -> String+prettyDebugInfo DebugInfo { diDirs = Dirs { .. }, ..} =+ "===== Main ======" <> "\n" <>+ "Architecture: " <> prettyShow diArch <> "\n" <>+ "Platform: " <> prettyShow diPlatform <> "\n" <>+ "GHCup Version: " <> describe_result <> "\n" <>+ "===== Directories ======" <> "\n" <>+ "base: " <> fromGHCupPath baseDir <> "\n" <>+ "bin: " <> binDir <> "\n" <>+ "GHCs: " <> (fromGHCupPath baseDir </> "ghc") <> "\n" <>+ "cache: " <> fromGHCupPath cacheDir <> "\n" <>+ "logs: " <> fromGHCupPath logsDir <> "\n" <>+ "config: " <> fromGHCupPath confDir <> "\n" <>+ "db: " <> fromGHCupPath dbDir <> "\n" <>+ whenWin ("recycle: " <> fromGHCupPath recycleDir <> "\n") <>+ "temp: " <> fromGHCupPath tmpDir <> "\n" <>+ whenWin ("msys2: " <> msys2Dir <> "\n") <>+ "\n===== Metadata ======\n" <>+ intercalate "\n" ((\(c, u) -> pad (c <> ":") <> " " <> u) <$> channels)+ where+ pad xs+ | xl < maxLength = xs <> replicate (maxLength - xl) ' '+ | otherwise = xs+ where+ xl = length xs+ channels = bimap (T.unpack . channelAliasText) (T.unpack . decUTF8Safe . serializeURIRef') <$> diChannels+ maxLength = (+1) . maximum . fmap (length . fst) $ channels+ whenWin x = if isWindows then x else mempty+++++ ---------------------------+ --[ Effect interpreters ]--+ ---------------------------+++type DInfoEffects = '[ NoCompatiblePlatform , NoCompatibleArch , DistroNotFound ]++++ ------------------+ --[ Entrypoint ]--+ ------------------++++dinfo :: ( Monad m+ , MonadMask m+ , MonadUnliftIO m+ , MonadFail m+ , Alternative m+ )+ => (IO (AppState, IO ()), LeanAppState)+ -> m ExitCode+dinfo (getAppState', leanAppstate) = do+ run (liftE getDebugInfo)+ >>= \case+ (VRight di, up) -> do+ liftIO $ putStrLn $ prettyDebugInfo di+ liftIO up+ pure ExitSuccess+ (VLeft e, _) -> do+ runLogger $ logError $ T.pack $ prettyHFError e+ pure $ ExitFailure 8+ where+ runLogger = flip runReaderT leanAppstate+ run action' = do+ (appstate', up) <- liftIO getAppState'+ r <- flip runReaderT appstate'+ . runResourceT+ . runE+ @DInfoEffects+ $ action'+ pure (r, up)
lib-opt/GHCup/OptParse/GC.hs view
@@ -3,7 +3,6 @@ {-# LANGUAGE TypeApplications #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE RankNTypes #-}@@ -11,7 +10,7 @@ module GHCup.OptParse.GC where -import GHCup+import GHCup.Command.GC import GHCup.Errors import GHCup.Types import GHCup.Prelude.Logger@@ -25,7 +24,7 @@ import Control.Monad.Trans.Resource import Data.Functor import Data.Variant.Excepts-import Options.Applicative hiding ( style )+import Options.Applicative hiding ( style, ParseError ) import Prelude hiding ( appendFile ) import System.Exit @@ -102,21 +101,11 @@ --------------------------- -type GCEffects = '[ NotInstalled, UninstallFailed ]+type GCEffects = '[ NotInstalled, UninstallFailed, ParseError, MalformedInstallInfo ] -runGC :: MonadUnliftIO m- => (ReaderT AppState m (VEither GCEffects a) -> m (VEither GCEffects a))- -> Excepts GCEffects (ResourceT (ReaderT AppState m)) a- -> m (VEither GCEffects a)-runGC runAppState =- runAppState- . runResourceT- . runE- @GCEffects - ------------------ --[ Entrypoint ]-- ------------------@@ -129,10 +118,9 @@ , MonadFail m ) => GCOptions- -> (forall a. ReaderT AppState m (VEither GCEffects a) -> m (VEither GCEffects a))- -> (ReaderT LeanAppState m () -> m ())+ -> (IO (AppState, IO ()), LeanAppState) -> m ExitCode-gc GCOptions{..} runAppState runLogger = runGC runAppState (do+gc GCOptions{..} (getAppState', leanAppstate) = run (do when gcOldGHC (liftE rmOldGHC) lift $ when gcProfilingLibs rmProfilingLibs lift $ when gcShareDir rmShareDir@@ -141,8 +129,19 @@ lift $ when gcTmp rmTmp liftE $ when gcUnset rmUnsetTools ) >>= \case- VRight _ -> do- pure ExitSuccess- VLeft e -> do+ (VRight _, up) -> do+ liftIO up+ pure ExitSuccess+ (VLeft e, _) -> do runLogger $ logError $ T.pack $ prettyHFError e pure $ ExitFailure 27+ where+ run action' = do+ (appstate', up) <- liftIO getAppState'+ r <- flip runReaderT appstate'+ . runResourceT+ . runE+ @GCEffects+ $ action'+ pure (r, up)+ runLogger = flip runReaderT leanAppstate
+ lib-opt/GHCup/OptParse/Generate.hs view
@@ -0,0 +1,225 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE RankNTypes #-}++module GHCup.OptParse.Generate where+++#if defined(DHALL)+import GHCup.Errors+import GHCup.Types+import GHCup.Input.Parsers+import GHCup.OptParse.Common+import GHCup.Types.Dhall ()+import GHCup.Prelude.Logger+import GHCup.Types.Optics+import GHCup.Download+import GHCup.Prelude (decUTF8Safe)++import Control.Monad.Reader+import Data.Functor+import Data.Variant.Excepts+import Options.Applicative hiding ( style, ParseError )+import Prelude hiding ( appendFile )+import System.Exit+import Dhall.Marshal.Encode ()++import qualified Dhall.Core as Dhall+import qualified Dhall.Binary as Dhall+import qualified Data.Either.Validation as Validation+import qualified Dhall hiding (Text)++import qualified Data.Text as T+import qualified Data.Text.IO as T+import qualified Data.Yaml as YAML+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as BSL+import qualified Data.Void as V++++++ ---------------+ --[ Options ]--+ ---------------++data GenerateCommand = GenerateDhallSchema GenOutput+ | GenerateDhallFromYaml ToDhallOptions+ | GenerateYamlFromDhall GenInput GenOutput+++data ToDhallOptions = ToDhallOptions {+ genInput :: GenInput+ , genOutput :: GenOutput+ , genBinary :: Bool+ }++data GenOutput = GenOutputFile FilePath+ | GenStdOut+++data GenInput = GenInputURL [NewURLSource]+ | GenStdIn+++ ---------------+ --[ Parsers ]--+ ---------------++generateP :: Parser GenerateCommand+generateP =+ subparser+ ( command+ "dhall-schema"+ (GenerateDhallSchema <$> info (genOutputP <**> helper) (progDesc "Generate Dhall schema from the GHCup types"))+ <> command+ "metadata-to-dhall"+ (GenerateDhallFromYaml+ <$> info (toDhallOptions <**> helper)+ (progDesc "Convert the input metadata to binary Dhall")+ )+ <> command+ "metadata-to-yaml"+ ((\(in', out) -> GenerateYamlFromDhall in' out)+ <$> info ((,) <$> genInputP <*> genOutputP <**> helper)+ (progDesc "Convert the input metadata to expanded YAML")+ )+ )++toDhallOptions :: Parser ToDhallOptions+toDhallOptions =+ ToDhallOptions+ <$> genInputP+ <*> genOutputP+ <*> (+ switch+ (short 'b' <> long "binary" <> help "Convert to Dhall binary format")+ )++genInputP :: Parser GenInput+genInputP =+ maybe GenStdIn GenInputURL <$>+ optional parseUrlSourceP++genOutputP :: Parser GenOutput+genOutputP =+ maybe GenStdOut GenOutputFile <$>+ optional+ (option+ (eitherReader filePathParser)+ ( short 'o'+ <> long "output-file"+ <> metavar "FILEPATH"+ <> help "Write the output to the specified file instead of stdout"+ <> completer (bashCompleter "file")+ )+ )++++ ---------------------------+ --[ Effect interpreters ]--+ ---------------------------+++type GenerateEffects = '[DigestError, ContentLengthError, GPGError, JSONError , DownloadFailed , FileDoesNotExistError, StackPlatformDetectError, UnsupportedMetadataFormat, ParseError]+++++ ------------------+ --[ Entrypoint ]--+ ------------------++++generate ::+ ( MonadIOish m+ )+ => GenerateCommand+ -> (IO (AppState, IO ()), LeanAppState)+ -> m ExitCode+generate gen (_getAppState', leanAppstate) = runLean (do+ pfreq <- lift getPlatformReq+ case gen of+ GenerateDhallSchema genOutput -> do+ withValidation (Dhall.expected (Dhall.auto @GHCupInfo)) $ \(Dhall.pretty -> result) -> do+ toOutputT genOutput result+ pure ()+ GenerateDhallFromYaml (ToDhallOptions genInput genOutput b) -> do+ r <- getInput genInput pfreq+ let dhallExpr = Dhall.denote $ (Dhall.embed @GHCupInfo Dhall.inject) r+ if b+ then toOutputBSL genOutput $ Dhall.encodeExpression dhallExpr+ else toOutputT genOutput $ Dhall.pretty dhallExpr+ pure ()+ GenerateYamlFromDhall genInput genOutput -> do+ r <- getInput genInput pfreq+ toOutputBS genOutput $ YAML.encode r+ ) >>= \case+ VRight _ -> do+ pure ExitSuccess+ VLeft e -> do+ runLogger $ logError $ T.pack $ prettyHFError e+ pure $ ExitFailure 27+ where+ getInput ::+ ( MonadReader env m+ , HasSettings env+ , HasDirs env+ , HasLog env+ , MonadIOish m+ )+ => GenInput+ -> PlatformRequest+ -> Excepts GenerateEffects m GHCupInfo+ getInput genInput pfreq =+ case genInput of+ GenInputURL xs -> do+ ghcupInfo <- liftE $ getDownloadsF' pfreq xs+ pure ghcupInfo+ GenStdIn -> do+ input <- liftIO BS.getContents+ liftIO $ asum [yaml input, dhallB input, dhall input]+ where+ yaml :: BS.ByteString -> IO GHCupInfo+ yaml input = either (fail . show) pure $ YAML.decodeEither' input++ dhallB :: BS.ByteString -> IO GHCupInfo+ dhallB input = do+ expr <- either (fail . show) pure $ Dhall.decodeExpression @V.Void @V.Void (BSL.fromStrict input)+ Dhall.rawInput Dhall.auto expr++ dhall :: BS.ByteString -> IO GHCupInfo+ dhall (decUTF8Safe -> input) = liftIO $ Dhall.input Dhall.auto input++ withValidation val action' =+ case val of+ Validation.Success r -> action' r+ Validation.Failure errors -> do+ throwE $ ParseError (show errors)+ toOutputT genOutput out =+ case genOutput of+ GenStdOut ->+ liftIO $ T.putStrLn out+ GenOutputFile fp ->+ liftIO $ T.writeFile fp out+ toOutputBSL genOutput out =+ case genOutput of+ GenStdOut ->+ liftIO $ BSL.putStr out+ GenOutputFile fp ->+ liftIO $ BSL.writeFile fp out+ toOutputBS genOutput out =+ case genOutput of+ GenStdOut ->+ liftIO $ BS.putStr out+ GenOutputFile fp ->+ liftIO $ BS.writeFile fp out+ runLogger = flip runReaderT leanAppstate+ runLean = flip runReaderT leanAppstate . runE @GenerateEffects++#endif
+ lib-opt/GHCup/OptParse/HealthCheck.hs view
@@ -0,0 +1,160 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE RankNTypes #-}++module GHCup.OptParse.HealthCheck where+++import GHCup.OptParse.Common++import GHCup.Command.HealthCheck+import GHCup.Errors+import GHCup.Input.Parsers+ ( toolParser )+import GHCup.Prelude.Logger+import GHCup.Prelude.String.QQ+import GHCup.Types++#if !MIN_VERSION_base(4,13,0)+import Control.Monad.Fail ( MonadFail )+#endif+import Control.Monad.Reader+import Control.Monad.Trans.Resource+import Data.Functor+import Data.Variant.Excepts+import Options.Applicative hiding ( style )+import Prelude hiding ( appendFile )+import System.Exit+import Options.Applicative.Pretty.Shim ( text )+import Text.PrettyPrint.HughesPJClass (prettyShow)+import Control.Exception.Safe (MonadMask)++import qualified Data.Text as T++++++ ----------------+ --[ Commands ]--+ ----------------+++data HealthCheckCommand+ = HealthCheckTool ToolOptions+ deriving (Eq, Show)+++++ ---------------+ --[ Options ]--+ ---------------+++data ToolOptions = ToolOptions+ { checkTool :: Tool+ , checkVer :: TargetVersion+ }+ deriving (Eq, Show)+++++ ---------------+ --[ Parsers ]--+ ---------------+++hcP :: Parser HealthCheckCommand+hcP =+ subparser+ ( command+ "tool"+ ( HealthCheckTool+ <$> info+ (toolOpts <**> helper)+ ( progDesc "Check tool for problems"+ <> footerDoc (Just $ text toolFooter)+ )+ )+ )+ where+ toolFooter :: String+ toolFooter = [s|Discussion:+ Check a tool for problems.++Examples:+ ghcup check tool |]++toolOpts :: Parser ToolOptions+toolOpts =+ (\t v -> ToolOptions t v)+ <$> argument (eitherReader toolParser) (metavar "TOOL" <> help "Which tool to install")+ <*> ghcVersionArgument [] Nothing+++ --------------+ --[ Footer ]--+ --------------+++hcFooter :: String+hcFooter = [s|Discussion:+ Performs various health checks. Good for attaching to bug reports.|]+++++ ---------------------------+ --[ Effect interpreters ]--+ ---------------------------+++type HCEffects = '[ ]+++++ ------------------+ --[ Entrypoint ]--+ ------------------++++hc :: ( Monad m+ , MonadMask m+ , MonadUnliftIO m+ , MonadFail m+ )+ => HealthCheckCommand+ -> (IO (AppState, IO ()), LeanAppState)+ -> m ExitCode+hc command' (getAppState', leanAppstate) = run (do+ case command' of+ HealthCheckTool ToolOptions{..} ->+ lift $ dbHealthCheck checkTool checkVer+ ) >>= \case+ (VRight r, up) -> do+ liftIO $ putStr $ prettyShow r+ liftIO up+ pure ExitSuccess+ (VLeft e, _) -> do+ runLogger $ logError $ T.pack $ prettyHFError e+ pure $ ExitFailure 27+ where+ runLogger = flip runReaderT leanAppstate+ run action' = do+ (appstate', up) <- liftIO getAppState'+ r <- flip runReaderT appstate'+ . runResourceT+ . runE+ @HCEffects+ $ action'+ pure (r, up)+
lib-opt/GHCup/OptParse/Install.hs view
@@ -1,48 +1,53 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-} {-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE ViewPatterns #-}-{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeOperators #-} module GHCup.OptParse.Install where -import GHCup.OptParse.Common--import GHCup-import GHCup.Errors-import GHCup.Types-import GHCup.Utils.Dirs-import GHCup.Utils.Parsers (fromVersion, isolateParser, uriParser)-import GHCup.Prelude-import GHCup.Prelude.Logger-import GHCup.Prelude.String.QQ+import GHCup.Command.Install+import GHCup.Command.Set+import GHCup.Errors+import GHCup.Input.Parsers+ ( installTargetParser+ , isolateParser+ , resolveVersion+ , toolParser+ , uriParser+ )+import GHCup.OptParse.Common+import GHCup.Prelude+import GHCup.Prelude.String.QQ+import GHCup.Query.GHCupDirs+import GHCup.Query.Metadata+import GHCup.Types+import GHCup.Types.Optics -import Control.Concurrent (threadDelay)+import Control.Concurrent ( threadDelay ) #if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )+import Control.Monad.Fail ( MonadFail ) #endif-import Control.Monad (when, forM_)-import Control.Monad.Reader-import Control.Monad.Trans.Resource-import Data.Either-import Data.Functor-import Data.Maybe-import Data.Variant.Excepts-import Options.Applicative hiding ( style )-import Options.Applicative.Pretty.Shim ( text )-import Prelude hiding ( appendFile )-import System.Exit-import URI.ByteString hiding ( uriParser )+import Control.Monad ( forM_, when )+import Control.Monad.Reader+import Control.Monad.Trans.Resource+import Data.Functor+import Data.Maybe+import Data.Variant.Excepts+import Options.Applicative hiding ( style )+import Options.Applicative.Pretty.Shim ( text )+import Prelude hiding ( appendFile )+import System.Exit+import Text.PrettyPrint.HughesPJClass ( prettyShow )+import URI.ByteString hiding ( uriParser ) -import qualified Data.Text as T+import qualified Data.Text as T @@ -52,11 +57,13 @@ ---------------- -data InstallCommand = InstallGHC InstallOptions- | InstallCabal InstallOptions- | InstallHLS InstallOptions- | InstallStack InstallOptions- deriving (Eq, Show)+data InstallCommand+ = InstallGHC InstallOptions+ | InstallCabal InstallOptions+ | InstallHLS InstallOptions+ | InstallStack InstallOptions+ | InstallOtherTool InstallOptionsNew+ deriving (Eq, Show) @@ -66,17 +73,30 @@ --------------- data InstallOptions = InstallOptions- { instVer :: Maybe ToolVersion- , instBindist :: Maybe URI- , instSet :: Bool- , isolateDir :: Maybe FilePath+ { instVer :: Maybe ToolVersion+ , instBindist :: Maybe URI+ , instSet :: Bool+ , isolateDir :: Maybe FilePath , forceInstall :: Bool- , installTargets :: T.Text- , addConfArgs :: [T.Text]- } deriving (Eq, Show)+ , installTargets :: Maybe [String]+ , addConfArgs :: [String]+ }+ deriving (Eq, Show) +data InstallOptionsNew = InstallOptionsNew+ { instTool :: Tool+ , instVer :: Maybe ToolVersion+ , instBindist :: Maybe URI+ , instSet :: Bool+ , isolateDir :: Maybe FilePath+ , forceInstall :: Bool+ , installTargets :: Maybe [String]+ , addConfArgs :: [String]+ }+ deriving (Eq, Show) + --------------- --[ Footers ]-- ---------------@@ -95,14 +115,14 @@ --[ Parsers ]-- --------------- -installParser :: Parser (Either InstallCommand InstallOptions)+installParser :: Parser InstallCommand installParser =- (Left <$> subparser+ subparser ( command "ghc" ( InstallGHC <$> info- (installOpts (Just GHC) <**> helper)+ (installOpts (Just ghc) <**> helper) ( progDesc "Install GHC" <> footerDoc (Just $ text installGHCFooter) )@@ -111,7 +131,7 @@ "cabal" ( InstallCabal <$> info- (installOpts (Just Cabal) <**> helper)+ (installOpts (Just cabal) <**> helper) ( progDesc "Install Cabal" <> footerDoc (Just $ text installCabalFooter) )@@ -120,7 +140,7 @@ "hls" ( InstallHLS <$> info- (installOpts (Just HLS) <**> helper)+ (installOpts (Just hls) <**> helper) ( progDesc "Install haskell-language-server" <> footerDoc (Just $ text installHLSFooter) )@@ -129,14 +149,15 @@ "stack" ( InstallStack <$> info- (installOpts (Just Stack) <**> helper)+ (installOpts (Just stack) <**> helper) ( progDesc "Install stack" <> footerDoc (Just $ text installStackFooter) ) ) )- )- <|> (Right <$> installOpts (Just GHC))+ <|> ( InstallOtherTool+ <$> installOptsNew+ ) where installHLSFooter :: String installHLSFooter = [s|Discussion:@@ -187,7 +208,7 @@ (eitherReader uriParser) (short 'u' <> long "url" <> metavar "BINDIST_URL" <> help "Install the specified version from this bindist"- <> completer (toolDlCompleter (fromMaybe GHC tool))+ <> completer (toolDlCompleter (fromMaybe ghc tool)) ) ) <*> (Just <$> toolVersionTagArgument [] tool)@@ -208,20 +229,58 @@ ) <*> switch (short 'f' <> long "force" <> help "Force install (THIS IS UNSAFE, only use it in Dockerfiles or CI)")- <*> strOption+ <*> optional (option (eitherReader installTargetParser) ( long "install-targets" <> metavar "TARGETS"- <> help "Space separated list of install targets (default: install)"+ <> help "Overwrite make based install targets" <> completer (listCompleter ["install", "install_bin", "install_lib", "install_extra", "install_man", "install_docs", "install_data", "update_package_db"])- <> value "install" )+ ) <*> many (argument str (metavar "CONFIGURE_ARGS" <> help "Additional arguments to bindist configure, prefix with '-- ' (longopts)")) where setDefault = case tool of- Nothing -> False- Just GHC -> False- Just _ -> True+ Nothing -> False+ Just (Tool "ghc") -> False+ Just _ -> True +installOptsNew :: Parser InstallOptionsNew+installOptsNew =+ (\t (u, v) -> InstallOptionsNew t v u)+ <$> argument (eitherReader toolParser) (metavar "TOOL" <> help "Which tool to install")+ <*> ( ( (,)+ <$> optional+ (option+ (eitherReader uriParser)+ (short 'u' <> long "url" <> metavar "BINDIST_URL" <> help+ "Install the specified version from this bindist"+ )+ )+ <*> (Just <$> toolVersionTagArgument [] Nothing)+ )+ <|> pure (Nothing, Nothing)+ )+ <*> fmap (fromMaybe False) (invertableSwitch "set" Nothing False+ (help "Set as active version after install"))+ <*> optional+ (option+ (eitherReader isolateParser)+ ( short 'i'+ <> long "isolate"+ <> metavar "DIR"+ <> help "install in an isolated absolute directory instead of the default one"+ <> completer (bashCompleter "directory")+ )+ )+ <*> switch+ (short 'f' <> long "force" <> help "Force install (THIS IS UNSAFE, only use it in Dockerfiles or CI)")+ <*> optional (option (eitherReader installTargetParser)+ ( long "install-targets"+ <> metavar "TARGETS"+ <> help "Overwrite make based install targets"+ <> completer (listCompleter ["install", "install_bin", "install_lib", "install_extra", "install_man", "install_docs", "install_data", "update_package_db"])+ )+ )+ <*> many (argument str (metavar "CONFIGURE_ARGS" <> help "Additional arguments to bindist configure, prefix with '-- ' (longopts)")) @@ -243,162 +302,126 @@ --[ Effect interpreters ]-- --------------------------- + type InstallEffects = '[ AlreadyInstalled- , UnknownArchive , ArchiveResult- , FileDoesNotExistError- , CopyError- , NotInstalled- , DirNotEmpty- , NoDownload- , NotInstalled , BuildFailed- , TagNotFound- , DayNotFound+ , CopyError , DigestError , ContentLengthError- , GPGError+ , DirNotEmpty , DownloadFailed- , TarDirDoesNotExist+ , FileAlreadyExistsError+ , FileDoesNotExistError+ , GPGError+ , MergeFileTreeError , NextVerNotFound+ , NoDownload , NoToolVersionSet- , FileAlreadyExistsError+ , NotInstalled , ProcessError+ , TagNotFound+ , DayNotFound+ , TarDirDoesNotExist , UninstallFailed- , MergeFileTreeError+ , UnknownArchive , InstallSetError+ , NoCompatiblePlatform+ , GHCup.Errors.ParseError+ , UnsupportedSetupCombo+ , DistroNotFound+ , NoCompatibleArch , URIParseError+ , NoInstallInfo+ , GHCup.Errors.ParseError+ , MalformedInstallInfo ] -runInstTool :: AppState- -> Excepts InstallEffects (ResourceT (ReaderT AppState IO)) a- -> IO (VEither InstallEffects a)-runInstTool appstate' =- flip runReaderT appstate'- . runResourceT- . runE- @InstallEffects --type InstallGHCEffects = '[ AlreadyInstalled- , ArchiveResult- , BuildFailed- , CopyError- , DigestError- , ContentLengthError- , DirNotEmpty- , DownloadFailed- , FileAlreadyExistsError- , FileDoesNotExistError- , GPGError- , MergeFileTreeError- , NextVerNotFound- , NoDownload- , NoToolVersionSet- , NotInstalled- , ProcessError- , TagNotFound- , DayNotFound- , TarDirDoesNotExist- , UninstallFailed- , UnknownArchive- , InstallSetError- , NoCompatiblePlatform- , GHCup.Errors.ParseError- , UnsupportedSetupCombo- , DistroNotFound- , NoCompatibleArch- , URIParseError- ]--runInstGHC :: AppState- -> Excepts InstallGHCEffects (ResourceT (ReaderT AppState IO)) a- -> IO (VEither InstallGHCEffects a)-runInstGHC appstate' =- flip runReaderT appstate'- . runResourceT- . runE- @InstallGHCEffects-- ------------------- --[ Entrypoints ]-- ------------------- -install :: Either InstallCommand InstallOptions -> Settings -> IO AppState -> (ReaderT LeanAppState IO () -> IO ()) -> IO ExitCode-install installCommand settings getAppState' runLogger = case installCommand of- (Right iGHCopts) -> do- runLogger (logWarn "This is an old-style command for installing GHC. Use 'ghcup install ghc' instead.")- installGHC iGHCopts- (Left (InstallGHC iGHCopts)) -> installGHC iGHCopts- (Left (InstallCabal iopts)) -> installCabal iopts- (Left (InstallHLS iopts)) -> installHLS iopts- (Left (InstallStack iopts)) -> installStack iopts+install :: InstallCommand -> Settings -> (IO (AppState, IO ()), LeanAppState) -> IO ExitCode+install installCommand settings (getAppState', leanAppstate) = case installCommand of+ (InstallGHC iGHCopts) -> installOther (toInstallOptionsNew ghc iGHCopts)+ (InstallCabal iopts) -> installOther (toInstallOptionsNew cabal iopts)+ (InstallHLS iopts) -> installOther (toInstallOptionsNew hls iopts)+ (InstallStack iopts) -> installOther (toInstallOptionsNew stack iopts)+ (InstallOtherTool iopts) -> installOther iopts where guessMode = if guessVersion settings then GLax else GStrict- installGHC :: InstallOptions -> IO ExitCode- installGHC InstallOptions{..} = do- s'@AppState{ dirs = Dirs{ .. } } <- liftIO getAppState'++ toInstallOptionsNew instTool InstallOptions{..} = InstallOptionsNew{..}++ installOther :: InstallOptionsNew -> IO ExitCode+ installOther InstallOptionsNew{..} = do+ (s'@AppState{ dirs = Dirs{ .. } }, updateCheckAction) <- getAppState' (case instBindist of- Nothing -> runInstGHC s' $ do- (v, vi) <- liftE $ fromVersion instVer guessMode GHC- forM_ (_viPreInstall =<< vi) $ \msg -> do+ Nothing -> run s' $ do+ GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo+ treq@(TargetVersionReq tver _) <- liftE $ resolveVersion instVer guessMode instTool+ let vm = getVersionMetadata tver instTool dls+ forM_ (_vmPreInstall =<< vm) $ \msg -> do lift $ logWarn msg lift $ logWarn "...waiting for 5 seconds, you can still abort..." liftIO $ threadDelay 5000000 -- give the user a sec to intervene- liftE $ runBothE' (installGHCBin- v- (maybe GHCupInternal IsolateDir isolateDir)- forceInstall- addConfArgs- installTargets- )- $ when instSet $ when (isNothing isolateDir) $ liftE $ void $ setGHC v SetGHCOnly Nothing- pure vi+ -- TODO: install targets+ liftE $ runBothE' (installTool+ instTool+ treq+ (maybe GHCupInternal IsolateDir isolateDir)+ forceInstall+ addConfArgs+ installTargets+ ) $ when instSet $ when (isNothing isolateDir) $ liftE $ void $ setToolVersion instTool tver+ pure vm Just uri -> do- runInstGHC s'{ settings = settings {noVerify = True}} $ do- (v, vi) <- liftE $ fromVersion instVer guessMode GHC- forM_ (_viPreInstall =<< vi) $ \msg -> do+ run s'{ settings = settings {noVerify = True}} $ do+ pfreq <- lift getPlatformReq+ GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo+ (TargetVersionReq tver _) <- liftE $ resolveVersion instVer guessMode instTool+ let vm = getVersionMetadata tver instTool dls+ forM_ (_vmPreInstall =<< vm) $ \msg -> do lift $ logWarn msg lift $ logWarn "...waiting for 5 seconds, you can still abort..." liftIO $ threadDelay 5000000 -- give the user a sec to intervene- liftE $ runBothE' (installGHCBindist- (DownloadInfo ((decUTF8Safe . serializeURIRef') uri) (Just $ RegexDir "ghc-.*") "" Nothing Nothing Nothing)- v+ liftE $ runBothE' (do+ (rev, _) <- liftE $ getDownloadInfoE' instTool (TargetVersionReq tver Nothing)+ installBindist+ instTool+ Nothing+ (DownloadInfo ((decUTF8Safe . serializeURIRef') uri) regexDir "" Nothing Nothing Nothing (toInstallationInputSpec <$> defaultToolInstallSpec instTool pfreq tver))+ (TargetVersionRev tver rev) (maybe GHCupInternal IsolateDir isolateDir) forceInstall addConfArgs installTargets )- $ when instSet $ when (isNothing isolateDir) $ liftE $ void $ setGHC v SetGHCOnly Nothing- pure vi+ $ when instSet $ when (isNothing isolateDir) $ liftE $ void $ setToolVersion instTool tver+ pure vm ) >>= \case- VRight vi -> do- runLogger $ logInfo "GHC installation successful"- forM_ (_viPostInstall =<< vi) $ \msg ->+ VRight vm -> do+ runLogger $ logInfo $ T.pack (prettyShow instTool) <> " installation successful"+ forM_ (_vmPostInstall =<< vm) $ \msg -> runLogger $ logInfo msg+ liftIO updateCheckAction pure ExitSuccess VLeft e@(V (AlreadyInstalled _ _)) -> do runLogger $ logWarn $ T.pack $ prettyHFError e- pure ExitSuccess- VLeft e@(V (AlreadyInstalled _ _)) -> do- runLogger $ logWarn $ T.pack $ prettyHFError e+ liftIO updateCheckAction pure ExitSuccess VLeft (V (DirNotEmpty fp)) -> do runLogger $ logError $ "Install directory " <> T.pack fp <> " is not empty." pure $ ExitFailure 3- VLeft (V (DirNotEmpty fp)) -> do- runLogger $ logError $- "Install directory " <> T.pack fp <> " is not empty."- pure $ ExitFailure 3 VLeft err@(V (BuildFailed tmpdir _)) -> do case keepDirs settings of@@ -407,195 +430,23 @@ "Check the logs at " <> T.pack (fromGHCupPath logsDir) <> " and the build directory " <> T.pack tmpdir <> " for more clues." <> "\n" <> "Make sure to clean up " <> T.pack tmpdir <> " afterwards.") pure $ ExitFailure 3- VLeft err@(V (BuildFailed tmpdir _)) -> do- case keepDirs settings of- Never -> runLogger (logError $ T.pack $ prettyHFError err)- _ -> runLogger (logError $ T.pack (prettyHFError err) <> "\n" <>- "Check the logs at " <> T.pack (fromGHCupPath logsDir) <> " and the build directory " <> T.pack tmpdir <> " for more clues." <> "\n" <>- "Make sure to clean up " <> T.pack tmpdir <> " afterwards.")- pure $ ExitFailure 3 VLeft e -> do runLogger $ do logError $ T.pack $ prettyHFError e logError $ "Also check the logs in " <> T.pack (fromGHCupPath logsDir) pure $ ExitFailure 3--- installCabal :: InstallOptions -> IO ExitCode- installCabal InstallOptions{..} = do- s'@AppState{ dirs = Dirs{ .. } } <- liftIO getAppState'- (case instBindist of- Nothing -> runInstTool s' $ do- (_tvVersion -> v, vi) <- liftE $ fromVersion instVer guessMode Cabal- forM_ (_viPreInstall =<< vi) $ \msg -> do- lift $ logWarn msg- lift $ logWarn- "...waiting for 5 seconds, you can still abort..."- liftIO $ threadDelay 5000000 -- give the user a sec to intervene- liftE $ runBothE' (installCabalBin- v- (maybe GHCupInternal IsolateDir isolateDir)- forceInstall- ) $ when instSet $ when (isNothing isolateDir) $ liftE $ setCabal v- pure vi- Just uri -> do- runInstTool s'{ settings = settings { noVerify = True}} $ do- (_tvVersion -> v, vi) <- liftE $ fromVersion instVer guessMode Cabal- forM_ (_viPreInstall =<< vi) $ \msg -> do- lift $ logWarn msg- lift $ logWarn- "...waiting for 5 seconds, you can still abort..."- liftIO $ threadDelay 5000000 -- give the user a sec to intervene- liftE $ runBothE' (installCabalBindist- (DownloadInfo ((decUTF8Safe . serializeURIRef') uri) Nothing "" Nothing Nothing Nothing)- v- (maybe GHCupInternal IsolateDir isolateDir)- forceInstall- ) $ when instSet $ when (isNothing isolateDir) $ liftE $ setCabal v- pure vi- )- >>= \case- VRight vi -> do- runLogger $ logInfo "Cabal installation successful"- forM_ (_viPostInstall =<< vi) $ \msg ->- runLogger $ logInfo msg- pure ExitSuccess- VLeft e@(V (AlreadyInstalled _ _)) -> do- runLogger $ logWarn $ T.pack $ prettyHFError e- pure ExitSuccess- VLeft (V (FileAlreadyExistsError fp)) -> do- runLogger $ logWarn $- "File " <> T.pack fp <> " already exists. Use 'ghcup install cabal --isolate " <> T.pack fp <> " --force ..." <> "' if you want to overwrite."- pure $ ExitFailure 3- VLeft e@(V (AlreadyInstalled _ _)) -> do- runLogger $ logWarn $ T.pack $ prettyHFError e- pure ExitSuccess- VLeft (V (FileAlreadyExistsError fp)) -> do- runLogger $ logWarn $- "File " <> T.pack fp <> " already exists. Use 'ghcup install cabal --isolate " <> T.pack fp <> " --force ..." <> "' if you want to overwrite."- pure $ ExitFailure 3- VLeft e -> do- runLogger $ do- logError $ T.pack $ prettyHFError e- logError $ "Also check the logs in " <> T.pack (fromGHCupPath logsDir)- pure $ ExitFailure 4-- installHLS :: InstallOptions -> IO ExitCode- installHLS InstallOptions{..} = do- s'@AppState{ dirs = Dirs{ .. } } <- liftIO getAppState'- (case instBindist of- Nothing -> runInstTool s' $ do- (_tvVersion -> v, vi) <- liftE $ fromVersion instVer guessMode HLS- forM_ (_viPreInstall =<< vi) $ \msg -> do- lift $ logWarn msg- lift $ logWarn- "...waiting for 5 seconds, you can still abort..."- liftIO $ threadDelay 5000000 -- give the user a sec to intervene- liftE $ runBothE' (installHLSBin- v- (maybe GHCupInternal IsolateDir isolateDir)- forceInstall- ) $ when instSet $ when (isNothing isolateDir) $ liftE $ setHLS v SetHLSOnly Nothing- pure vi- Just uri -> do- runInstTool s'{ settings = settings { noVerify = True}} $ do- (_tvVersion -> v, vi) <- liftE $ fromVersion instVer guessMode HLS- forM_ (_viPreInstall =<< vi) $ \msg -> do- lift $ logWarn msg- lift $ logWarn- "...waiting for 5 seconds, you can still abort..."- liftIO $ threadDelay 5000000 -- give the user a sec to intervene- -- TODO: support legacy- liftE $ runBothE' (installHLSBindist- (DownloadInfo ((decUTF8Safe . serializeURIRef') uri) (if isWindows then Nothing else Just (RegexDir "haskell-language-server-*")) "" Nothing Nothing Nothing)- v- (maybe GHCupInternal IsolateDir isolateDir)- forceInstall- ) $ when instSet $ when (isNothing isolateDir) $ liftE $ setHLS v SetHLSOnly Nothing- pure vi- )- >>= \case- VRight vi -> do- runLogger $ logInfo "HLS installation successful"- forM_ (_viPostInstall =<< vi) $ \msg ->- runLogger $ logInfo msg- pure ExitSuccess- VLeft e@(V (AlreadyInstalled _ _)) -> do- runLogger $ logWarn $ T.pack $ prettyHFError e- pure ExitSuccess- VLeft (V (FileAlreadyExistsError fp)) -> do- runLogger $ logWarn $- "File " <> T.pack fp <> " already exists. Use 'ghcup install hls --isolate " <> T.pack fp <> " --force ..." <> "' if you want to overwrite."- pure $ ExitFailure 3- VLeft e@(V (AlreadyInstalled _ _)) -> do- runLogger $ logWarn $ T.pack $ prettyHFError e- pure ExitSuccess- VLeft (V (FileAlreadyExistsError fp)) -> do- runLogger $ logWarn $- "File " <> T.pack fp <> " already exists. Use 'ghcup install hls --isolate " <> T.pack fp <> " --force ..." <> "' if you want to overwrite."- pure $ ExitFailure 3- VLeft e -> do- runLogger $ do- logError $ T.pack $ prettyHFError e- logError $ "Also check the logs in " <> T.pack (fromGHCupPath logsDir)- pure $ ExitFailure 4+ where+ run appstate' =+ flip runReaderT (appstate' :: AppState)+ . runResourceT+ . runE+ @InstallEffects+ runLogger = flip runReaderT leanAppstate+ regexDir = case instTool of+ (Tool "ghc") -> Just $ RegexDir "ghc-.*"+ (Tool "cabal") -> Nothing+ (Tool "stack") -> Nothing+ (Tool "hls") -> if isWindows then Nothing else Just (RegexDir "haskell-language-server-*")+ _ -> Nothing -- TODO - installStack :: InstallOptions -> IO ExitCode- installStack InstallOptions{..} = do- s'@AppState{ dirs = Dirs{ .. } } <- liftIO getAppState'- (case instBindist of- Nothing -> runInstTool s' $ do- (_tvVersion -> v, vi) <- liftE $ fromVersion instVer guessMode Stack- forM_ (_viPreInstall =<< vi) $ \msg -> do- lift $ logWarn msg- lift $ logWarn- "...waiting for 5 seconds, you can still abort..."- liftIO $ threadDelay 5000000 -- give the user a sec to intervene- liftE $ runBothE' (installStackBin- v- (maybe GHCupInternal IsolateDir isolateDir)- forceInstall- ) $ when instSet $ when (isNothing isolateDir) $ liftE $ setStack v- pure vi- Just uri -> do- runInstTool s'{ settings = settings { noVerify = True}} $ do- (_tvVersion -> v, vi) <- liftE $ fromVersion instVer guessMode Stack- forM_ (_viPreInstall =<< vi) $ \msg -> do- lift $ logWarn msg- lift $ logWarn- "...waiting for 5 seconds, you can still abort..."- liftIO $ threadDelay 5000000 -- give the user a sec to intervene- liftE $ runBothE' (installStackBindist- (DownloadInfo ((decUTF8Safe . serializeURIRef') uri) Nothing "" Nothing Nothing Nothing)- v- (maybe GHCupInternal IsolateDir isolateDir)- forceInstall- ) $ when instSet $ when (isNothing isolateDir) $ liftE $ setStack v- pure vi- )- >>= \case- VRight vi -> do- runLogger $ logInfo "Stack installation successful"- forM_ (_viPostInstall =<< vi) $ \msg ->- runLogger $ logInfo msg- pure ExitSuccess- VLeft e@(V (AlreadyInstalled _ _)) -> do- runLogger $ logWarn $ T.pack $ prettyHFError e- pure ExitSuccess- VLeft (V (FileAlreadyExistsError fp)) -> do- runLogger $ logWarn $- "File " <> T.pack fp <> " already exists. Use 'ghcup install stack --isolate " <> T.pack fp <> " --force ..." <> "' if you want to overwrite."- pure $ ExitFailure 3- VLeft e@(V (AlreadyInstalled _ _)) -> do- runLogger $ logWarn $ T.pack $ prettyHFError e- pure ExitSuccess- VLeft (V (FileAlreadyExistsError fp)) -> do- runLogger $ logWarn $- "File " <> T.pack fp <> " already exists. Use 'ghcup install stack --isolate " <> T.pack fp <> " --force ..." <> "' if you want to overwrite."- pure $ ExitFailure 3- VLeft e -> do- runLogger $ do- logError $ T.pack $ prettyHFError e- logError $ "Also check the logs in " <> T.pack (fromGHCupPath logsDir)- pure $ ExitFailure 4
lib-opt/GHCup/OptParse/List.hs view
@@ -5,20 +5,22 @@ {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeApplications #-} module GHCup.OptParse.List where -import GHCup+import GHCup.Command.List+import GHCup.Errors import GHCup.Prelude import GHCup.Types-import GHCup.Utils.Parsers (dayParser, toolParser, criteriaParser)+import GHCup.Input.Parsers (dayParser, toolParserWithGHCup, criteriaParser, revisionShowParser) import GHCup.OptParse.Common import GHCup.Prelude.String.QQ-import GHCup.Utils.Output-import GHCup.Utils.Pager+import GHCup.Compat.Terminal+import GHCup.Compat.Pager #if !MIN_VERSION_base(4,13,0) import Control.Monad.Fail ( MonadFail )@@ -31,18 +33,20 @@ import Data.Functor import Data.Maybe import Data.Time.Calendar ( Day )+import Data.Variant.Excepts import Data.Versions import Options.Applicative hiding ( style ) import Prelude hiding ( appendFile ) import System.Exit import System.Console.Pretty hiding ( color )+import Text.PrettyPrint.HughesPJClass (prettyShow) import qualified Data.Text as T import qualified Data.Text.IO as T import qualified System.Console.Pretty as Pretty import Control.Exception.Safe (MonadMask) import GHCup.Types.Optics-import GHCup.Prelude.Logger (logDebug)+import qualified Data.Map.Strict as M @@ -54,14 +58,16 @@ --------------- + data ListOptions = ListOptions- { loTool :: Maybe Tool- , lCriteria :: Maybe ListCriteria- , lFrom :: Maybe Day- , lTo :: Maybe Day- , lHideOld :: Bool- , lShowNightly :: Bool- , lRawFormat :: Bool+ { loTool :: Maybe [Tool]+ , lCriteria :: [ListCriteria]+ , lFrom :: Maybe Day+ , lTo :: Maybe Day+ , lHideOld :: Bool+ , lShowNightly :: Bool+ , lRawFormat :: Bool+ , lShowRevisions :: ShowRevisions } deriving (Eq, Show) @@ -75,14 +81,16 @@ listOpts = ListOptions <$> optional+ (some (option- (eitherReader toolParser)+ (eitherReader toolParserWithGHCup) (short 't' <> long "tool" <> metavar "<ghc|cabal|hls|stack>" <> help "Tool to list versions for. Default is all" <> completer toolCompleter ) )- <*> optional+ )+ <*> many (option (eitherReader criteriaParser) ( short 'c'@@ -118,6 +126,13 @@ <*> switch (short 'r' <> long "raw-format" <> help "More machine-parsable format" )+ <*> option+ (eitherReader revisionShowParser)+ (long "show-revisions" <> help "How to show revisions (default: updates)"+ <> metavar "<updates|all|none>"+ <> completer revisionCompleter+ <> value ShowUpdates+ ) --------------@@ -142,9 +157,18 @@ ----------------- -printListResult :: (HasLog env , MonadReader env m, MonadIO m)- => Bool -> PagerConfig -> Bool -> [ListResult] -> m ()-printListResult no_color (PagerConfig pList pCmd) raw lr = do+printListResult ::+ ( HasLog env+ , MonadReader env m+ , MonadIO m+ )+ => Bool+ -> ShowRevisions+ -> PagerConfig+ -> Bool+ -> ToolListResult+ -> m ()+printListResult no_color show_revisions (PagerConfig pList pCmd) raw lr = do let color | raw || no_color = (\_ x -> x)@@ -168,18 +192,28 @@ then x else [color Green "", "Tool", "Version", "Tags", "Notes"] : x )- . fmap- (\ListResult {..} ->+ . mconcat . fmap+ (\(lTool, (_, ls)) -> ls <&> \ListResult{..} -> let marks = if- | lSet -> (color Green (if isWindows then "IS" else "✔✔"))- | lInstalled -> (color Green (if isWindows then "I " else "✓ "))- | otherwise -> (color Red (if isWindows then "X " else "✗ "))+ | lSet -> (color Green (if isWindows then "IS" else "✔✔"))+ | lInstalled -> (color Green (if isWindows then "I " else "✓ "))+ | otherwise -> (color Red (if isWindows then "X " else "✗ ")) in (if raw then [] else [marks])- ++ [ fmap toLower . show $ lTool- , case lCross of- Nothing -> T.unpack . prettyVer $ lVer- Just c -> T.unpack (c <> "-" <> prettyVer lVer)+ ++ [ fmap toLower . prettyShow $ lTool+ , let rev = case lRev of+ (rev', RevUpdate)+ | show_revisions == ShowNone -> ""+ | otherwise -> "-r" <> show rev'+ (rev', RevOutdated)+ | show_revisions == ShowNone -> ""+ | otherwise -> "-r" <> show rev'+ (rev', RevNormal)+ | show_revisions == ShowAll -> "-r" <> show rev'+ | otherwise -> ""+ in case lCross of+ Nothing -> T.unpack (prettyVer lVer) <> rev+ Just c -> T.unpack (c <> "-" <> prettyVer lVer) <> rev , intercalate "," (filter (/= "") . fmap printTag $ sort lTag) , intercalate "," $ (if hlsPowered@@ -196,7 +230,7 @@ ) ] )- $ lr+ $ M.toList lr let cols = foldr (\xs ys -> zipWith (:) xs ys) (repeat []) rows lengths = fmap (maximum . fmap strWidth) cols@@ -226,19 +260,35 @@ -list :: ( Monad m- , MonadMask m- , MonadUnliftIO m- , MonadFail m- )- => ListOptions- -> Bool- -> PagerConfig- -> (ReaderT AppState m ExitCode -> m ExitCode)- -> m ExitCode-list ListOptions{..} no_color pgc runAppState =- runAppState (do- l <- listVersions loTool (maybeToList lCriteria) lHideOld lShowNightly (lFrom, lTo)- printListResult no_color pgc lRawFormat l+list ::+ ( Monad m+ , MonadMask m+ , MonadUnliftIO m+ , MonadFail m+ )+ => ListOptions+ -> Bool+ -> PagerConfig+ -> (IO (AppState, IO ()), LeanAppState)+ -> m ExitCode+list ListOptions{..} no_color pgc (getAppState', leanAppstate) = do+ r <- run $ do+ l <- listVersions loTool lCriteria lShowRevisions lHideOld lShowNightly (lFrom, lTo)+ lift $ printListResult no_color lShowRevisions pgc lRawFormat l+ case r of+ (VRight _, up) -> do+ liftIO up pure ExitSuccess- )+ (VLeft e, _) -> do+ runLogger $ logError $ T.pack $ prettyHFError e+ pure $ ExitFailure 44+ where+ runLogger = flip runReaderT leanAppstate+ run action' = do+ (appstate', up) <- liftIO getAppState'+ r <- flip runReaderT appstate'+ . runResourceT+ . runE+ @'[GHCup.Errors.ParseError]+ $ action'+ pure (r, up)
lib-opt/GHCup/OptParse/Nuke.hs view
@@ -11,7 +11,9 @@ -import GHCup+import GHCup.Command.List (ListResult(..), ListCriteria(..), listVersions)+import GHCup.Command.Rm+import GHCup.Command.Nuke import GHCup.Errors import GHCup.Types import GHCup.Prelude.Logger@@ -19,20 +21,19 @@ #if !MIN_VERSION_base(4,13,0) import Control.Monad.Fail ( MonadFail ) #endif-import Control.Monad (forM_, void)+import Control.Exception.Safe (MonadMask)+import Control.Concurrent (threadDelay)+import Control.Monad (forM_) import Control.Monad.Reader import Control.Monad.Trans.Resource+import Data.Foldable.WithIndex import Data.Maybe import Data.Variant.Excepts-import Options.Applicative hiding ( style )+import Options.Applicative hiding ( style, ParseError ) import Prelude hiding ( appendFile ) import System.Exit import qualified Data.Text as T-import Control.Exception.Safe (MonadMask)-import Control.DeepSeq-import Control.Exception-import Control.Concurrent (threadDelay) @@ -42,14 +43,7 @@ --------------------------- -type NukeEffects = '[ NotInstalled, UninstallFailed ]---runNuke :: AppState- -> Excepts NukeEffects (ReaderT AppState m) a- -> m (VEither NukeEffects a)-runNuke s' =- flip runReaderT s' . runE @NukeEffects+type NukeEffects = '[ NotInstalled, UninstallFailed, ParseError, MalformedInstallInfo ] @@ -64,13 +58,10 @@ , MonadUnliftIO m , MonadFail m )- => IO AppState- -> (ReaderT LeanAppState m () -> m ())+ => (IO (AppState, IO ()), LeanAppState) -> m ExitCode-nuke appState runLogger = do- s' <- liftIO appState- void $ liftIO $ evaluate $ force s'- runNuke s' (do+nuke (getAppState', leanAppstate) = do+ run (do lift $ logWarn "WARNING: This will remove GHCup and all installed components from your system." lift $ logWarn "Waiting 10 seconds before commencing, if you want to cancel it, now would be the time." liftIO $ threadDelay 10000000 -- wait 10s@@ -78,9 +69,9 @@ lift $ logInfo "Initiating Nuclear Sequence 🚀🚀🚀" lift $ logInfo "Nuking in 3...2...1" - lInstalled <- lift $ listVersions Nothing [ListInstalled True] False True (Nothing, Nothing)+ lInstalled' <- liftE $ listVersions Nothing [ListInstalled True] ShowNone False True (Nothing, Nothing) - forM_ lInstalled (liftE . rmTool)+ iforM_ lInstalled' $ \tool (_, ls) -> forM_ ls $ \ListResult{..} -> liftE $ rmToolVersion tool (TargetVersion lCross lVer) lift rmGhcupDirs @@ -97,3 +88,12 @@ VLeft e -> do runLogger $ logError $ T.pack $ prettyHFError e pure $ ExitFailure 15+ where+ run action' = do+ (appstate', _) <- liftIO getAppState'+ flip runReaderT appstate'+ . runResourceT+ . runE+ @NukeEffects+ $ action'+ runLogger = flip runReaderT leanAppstate
lib-opt/GHCup/OptParse/Prefetch.hs view
@@ -3,7 +3,6 @@ {-# LANGUAGE TypeApplications #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE RankNTypes #-}@@ -11,10 +10,10 @@ module GHCup.OptParse.Prefetch where -import GHCup+import GHCup.Command.Prefetch import GHCup.Errors import GHCup.Types-import GHCup.Utils.Parsers (fromVersion)+import GHCup.Input.Parsers (resolveVersion, toolParser) import GHCup.Types.Optics import GHCup.Prelude.File import GHCup.Prelude.Logger@@ -30,7 +29,7 @@ import Data.Functor import Data.Maybe import Data.Variant.Excepts-import Options.Applicative hiding ( style )+import Options.Applicative hiding ( style, ParseError ) import Prelude hiding ( appendFile ) import System.Exit @@ -50,6 +49,7 @@ | PrefetchCabal PrefetchOptions (Maybe ToolVersion) | PrefetchHLS PrefetchOptions (Maybe ToolVersion) | PrefetchStack PrefetchOptions (Maybe ToolVersion)+ | PrefetchTool PrefetchOptions (Maybe ToolVersion) Tool | PrefetchMetadata @@ -62,7 +62,8 @@ data PrefetchOptions = PrefetchOptions {- pfCacheDir :: Maybe FilePath+ pfSrc :: Bool+ , pfCacheDir :: Maybe FilePath } data PrefetchGHCOptions = PrefetchGHCOptions {@@ -86,7 +87,7 @@ <$> (PrefetchGHCOptions <$> ( switch (short 's' <> long "source" <> help "Download source tarball instead of bindist") <**> helper ) <*> optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)" <> completer (bashCompleter "directory"))))- <*> optional (toolVersionTagArgument [] (Just GHC)) )+ <*> optional (toolVersionTagArgument [] (Just ghc)) ) ( progDesc "Download GHC assets for installation") ) <>@@ -94,8 +95,11 @@ "cabal" (info (PrefetchCabal- <$> fmap PrefetchOptions (optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)" <> completer (bashCompleter "directory"))))- <*> ( optional (toolVersionTagArgument [] (Just Cabal)) <**> helper ))+ <$> (PrefetchOptions+ <$> ( switch (short 's' <> long "source" <> help "Download source tarball instead of bindist") <**> helper )+ <*> optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)" <> completer (bashCompleter "directory")))+ )+ <*> ( optional (toolVersionTagArgument [] (Just cabal)) <**> helper )) ( progDesc "Download cabal assets for installation") ) <>@@ -103,8 +107,11 @@ "hls" (info (PrefetchHLS- <$> fmap PrefetchOptions (optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)" <> completer (bashCompleter "directory"))))- <*> ( optional (toolVersionTagArgument [] (Just HLS)) <**> helper ))+ <$> (PrefetchOptions+ <$> ( switch (short 's' <> long "source" <> help "Download source tarball instead of bindist") <**> helper )+ <*> optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)" <> completer (bashCompleter "directory")))+ )+ <*> ( optional (toolVersionTagArgument [] (Just hls)) <**> helper )) ( progDesc "Download HLS assets for installation") ) <>@@ -112,8 +119,11 @@ "stack" (info (PrefetchStack- <$> fmap PrefetchOptions (optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)" <> completer (bashCompleter "directory"))))- <*> ( optional (toolVersionTagArgument [] (Just Stack)) <**> helper ))+ <$> (PrefetchOptions+ <$> ( switch (short 's' <> long "source" <> help "Download source tarball instead of bindist") <**> helper )+ <*> optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)" <> completer (bashCompleter "directory")))+ )+ <*> ( optional (toolVersionTagArgument [] (Just stack)) <**> helper )) ( progDesc "Download stack assets for installation") ) <>@@ -123,6 +133,14 @@ helper ( progDesc "Download ghcup's metadata, needed for various operations") )+ ) <|>+ ( (\o t v -> PrefetchTool o v t)+ <$> (PrefetchOptions+ <$> ( switch (short 's' <> long "source" <> help "Download source tarball instead of bindist") <**> helper )+ <*> optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)" <> completer (bashCompleter "directory")))+ )+ <*> argument (eitherReader toolParser) (metavar "TOOL" <> help "Which tool to prefetch")+ <*> optional (toolVersionTagArgument [] Nothing) ) @@ -162,22 +180,13 @@ , JSONError , FileDoesNotExistError , StackPlatformDetectError+ , UnsupportedMetadataFormat , URIParseError+ , ParseError ] -runPrefetch :: MonadUnliftIO m- => (ReaderT AppState m (VEither PrefetchEffects a) -> m (VEither PrefetchEffects a))- -> Excepts PrefetchEffects (ResourceT (ReaderT AppState m)) a- -> m (VEither PrefetchEffects a)-runPrefetch runAppState =- runAppState- . runResourceT- . runE- @PrefetchEffects -- ------------------ --[ Entrypoint ]-- ------------------@@ -191,41 +200,61 @@ ) => PrefetchCommand -> Settings- -> (forall a. ReaderT AppState m (VEither PrefetchEffects a) -> m (VEither PrefetchEffects a))- -> (ReaderT LeanAppState m () -> m ())+ -> (IO (AppState, IO ()), LeanAppState) -> m ExitCode-prefetch prefetchCommand settings runAppState runLogger =- runPrefetch runAppState (do+prefetch prefetchCommand settings (getAppState', leanAppstate) =+ run (do case prefetchCommand of PrefetchGHC (PrefetchGHCOptions pfGHCSrc pfCacheDir) mt -> do forM_ pfCacheDir (liftIO . createDirRecursive')- (v, _) <- liftE $ fromVersion mt guessMode GHC+ v <- liftE $ resolveVersion mt guessMode ghc if pfGHCSrc- then liftE $ fetchGHCSrc v pfCacheDir- else liftE $ fetchToolBindist v GHC pfCacheDir- PrefetchCabal PrefetchOptions {pfCacheDir} mt -> do+ then liftE $ fetchToolSrc ghc v pfCacheDir+ else liftE $ fetchToolBindist v ghc pfCacheDir+ PrefetchCabal PrefetchOptions {pfSrc, pfCacheDir} mt -> do forM_ pfCacheDir (liftIO . createDirRecursive')- (v, _) <- liftE $ fromVersion mt guessMode Cabal- liftE $ fetchToolBindist v Cabal pfCacheDir- PrefetchHLS PrefetchOptions {pfCacheDir} mt -> do+ v <- liftE $ resolveVersion mt guessMode cabal+ if pfSrc+ then liftE $ fetchToolSrc cabal v pfCacheDir+ else liftE $ fetchToolBindist v cabal pfCacheDir+ PrefetchHLS PrefetchOptions {pfSrc, pfCacheDir} mt -> do forM_ pfCacheDir (liftIO . createDirRecursive')- (v, _) <- liftE $ fromVersion mt guessMode HLS- liftE $ fetchToolBindist v HLS pfCacheDir- PrefetchStack PrefetchOptions {pfCacheDir} mt -> do+ v <- liftE $ resolveVersion mt guessMode hls+ if pfSrc+ then liftE $ fetchToolSrc hls v pfCacheDir+ else liftE $ fetchToolBindist v hls pfCacheDir+ PrefetchStack PrefetchOptions {pfSrc, pfCacheDir} mt -> do forM_ pfCacheDir (liftIO . createDirRecursive')- (v, _) <- liftE $ fromVersion mt guessMode Stack- liftE $ fetchToolBindist v Stack pfCacheDir+ v <- liftE $ resolveVersion mt guessMode stack+ if pfSrc+ then liftE $ fetchToolSrc stack v pfCacheDir+ else liftE $ fetchToolBindist v stack pfCacheDir+ PrefetchTool PrefetchOptions {pfCacheDir} mt tool -> do+ forM_ pfCacheDir (liftIO . createDirRecursive')+ v <- liftE $ resolveVersion mt guessMode tool+ liftE $ fetchToolBindist v tool pfCacheDir PrefetchMetadata -> do pfreq <- lift getPlatformReq _ <- liftE $ getDownloadsF pfreq pure "" ) >>= \case- VRight _ -> do- pure ExitSuccess- VLeft e -> do+ (VRight _, up) -> do+ liftIO up+ pure ExitSuccess+ (VLeft e, _) -> do runLogger $ logError $ T.pack $ prettyHFError e pure $ ExitFailure 15 where- guessMode = if guessVersion settings then GLaxWithInstalled else GStrict+ guessMode = if guessVersion settings then GLaxWithInstalled else GStrict++ run action' = do+ (appstate', up) <- liftIO getAppState'+ r <- flip runReaderT appstate'+ . runResourceT+ . runE+ @PrefetchEffects+ $ action'+ pure (r, up)+ runLogger = flip runReaderT leanAppstate
+ lib-opt/GHCup/OptParse/Reset.hs view
@@ -0,0 +1,65 @@+module GHCup.OptParse.Reset where++import GHCup.Types (UserSettings(..))++-- UserSettingsKey constructors correspond to UserSettings fields+data UserSettingsKey+ = Cache+ | MetaCache+ | MetaMode+ | NoVerify+ | Verbose+ | KeepDirs+ | Downloader+ | KeyBindings+ | UrlSource+ | NoNetwork+ | GPGSetting+ | PlatformOverride+ | Mirrors+ | DefGHCConfOptions+ | Pager+ | GuessVersion+ deriving (Show, Eq)++toUserSettingsKey :: String -> Either String UserSettingsKey+toUserSettingsKey = \case+ "cache" -> Right Cache+ "meta-cache" -> Right MetaCache+ "meta-mode" -> Right MetaMode+ "no-verify" -> Right NoVerify+ "verbose" -> Right Verbose+ "keep-dirs" -> Right KeepDirs+ "downloader" -> Right Downloader+ "key-bindings" -> Right KeyBindings+ "url-source" -> Right UrlSource+ "no-network" -> Right NoNetwork+ "gpg-setting" -> Right GPGSetting+ "platform-override" -> Right PlatformOverride+ "mirrors" -> Right Mirrors+ "def-ghc-conf-options" -> Right DefGHCConfOptions+ "pager" -> Right Pager+ "guess-version" -> Right GuessVersion+ invalidString -> Left invalidString++resetUserConfig ::+ UserSettings -> UserSettingsKey -> UserSettings+resetUserConfig settings key = case key of+ Cache -> settings { uCache = Nothing }+ MetaCache -> settings { uMetaCache = Nothing }+ MetaMode -> settings { uMetaMode = Nothing }+ NoVerify -> settings { uNoVerify = Nothing }+ Verbose -> settings { uVerbose = Nothing }+ KeepDirs -> settings { uKeepDirs = Nothing }+ Downloader -> settings { uDownloader = Nothing }+ KeyBindings -> settings { uKeyBindings = Nothing }+ UrlSource -> settings { uUrlSource = Nothing }+ NoNetwork -> settings { uNoNetwork = Nothing }+ GPGSetting -> settings { uGPGSetting = Nothing }+ PlatformOverride -> settings { uPlatformOverride = Nothing }+ Mirrors -> settings { uMirrors = Nothing }+ DefGHCConfOptions -> settings { uDefGHCConfOptions = Nothing }+ Pager -> settings { uPager = Nothing }+ GuessVersion -> settings { uGuessVersion = Nothing }++
lib-opt/GHCup/OptParse/Rm.hs view
@@ -1,44 +1,46 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-} {-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE ViewPatterns #-} module GHCup.OptParse.Rm where -import GHCup-import GHCup.Errors-import GHCup.Types-import GHCup.Types.Optics-import GHCup.Utils-import GHCup.Prelude.Logger-import GHCup.Prelude.String.QQ-import GHCup.OptParse.Common+import GHCup.Command.List+import GHCup.Command.Rm+import GHCup.Errors+import GHCup.Input.Parsers ( ghcVersionEither, toolParser )+import GHCup.OptParse.Common+import GHCup.Prelude.Logger+import GHCup.Prelude.String.QQ+import GHCup.Query.Metadata+import GHCup.Types+import GHCup.Types.Optics #if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )+import Control.Monad.Fail ( MonadFail ) #endif-import Control.Monad (forM_)-import Control.Monad.Reader-import Control.Monad.Trans.Resource-import Data.Functor-import Data.Maybe-import Data.Versions-import Data.Variant.Excepts-import Options.Applicative hiding ( style )-import Prelude hiding ( appendFile )-import System.Exit-import Text.PrettyPrint.HughesPJClass ( prettyShow )+import Control.Monad ( forM_, when )+import Control.Monad.Reader+import Control.Monad.Trans.Resource+import Data.Functor+import Data.Maybe+import Data.Variant.Excepts+import Data.Versions+import Options.Applicative hiding ( ParseError, style )+import Prelude hiding ( appendFile )+import System.Exit+import Text.PrettyPrint.HughesPJClass ( prettyShow ) -import qualified Data.Text as T-import Control.Exception.Safe (MonadMask)+import Control.Exception.Safe ( MonadMask )+import qualified Data.Text as T @@ -48,11 +50,13 @@ ---------------- -data RmCommand = RmGHC RmOptions- | RmCabal Version- | RmHLS Version- | RmStack Version- deriving (Eq, Show)+data RmCommand+ = RmGHC RmOptions+ | RmCabal Version+ | RmHLS Version+ | RmStack Version+ | RmOther RmOptionsNew+ deriving (Eq, Show) @@ -63,53 +67,67 @@ data RmOptions = RmOptions- { ghcVer :: GHCTargetVersion- } deriving (Eq, Show)+ { ghcVer :: TargetVersion+ }+ deriving (Eq, Show) +data RmOptionsNew = RmOptionsNew+ { rmTool :: Tool+ , ghcVer :: TargetVersion+ }+ deriving (Eq, Show) + --------------- --[ Parsers ]-- --------------- -rmParser :: Parser (Either RmCommand RmOptions)+rmParser :: Parser RmCommand rmParser =- (Left <$> subparser+ subparser ( command "ghc"- (RmGHC <$> info (rmOpts (Just GHC) <**> helper) (progDesc "Remove GHC version"))+ (RmGHC <$> info (rmOpts (Just ghc) <**> helper) (progDesc "Remove GHC version")) <> command "cabal" ( RmCabal- <$> info (versionParser' [ListInstalled True] (Just Cabal) <**> helper)+ <$> info (versionParser' [ListInstalled True] (Just cabal) <**> helper) (progDesc "Remove Cabal version") ) <> command "hls" ( RmHLS- <$> info (versionParser' [ListInstalled True] (Just HLS) <**> helper)+ <$> info (versionParser' [ListInstalled True] (Just hls) <**> helper) (progDesc "Remove haskell-language-server version") ) <> command "stack" ( RmStack- <$> info (versionParser' [ListInstalled True] (Just Stack) <**> helper)+ <$> info (versionParser' [ListInstalled True] (Just stack) <**> helper) (progDesc "Remove stack version") ) )- )- <|> (Right <$> rmOpts Nothing)+ <|> ( RmOther+ <$> rmOptsNew+ ) rmOpts :: Maybe Tool -> Parser RmOptions rmOpts tool = RmOptions <$> ghcVersionArgument [ListInstalled True] tool +rmOptsNew :: Parser RmOptionsNew+rmOptsNew = RmOptionsNew+ <$> argument (eitherReader toolParser) (metavar "TOOL")+ <*> argument (eitherReader ghcVersionEither) (metavar "VERSION") ++ -------------- --[ Footer ]-- --------------@@ -129,7 +147,7 @@ --------------------------- -type RmEffects = '[ NotInstalled, UninstallFailed ]+type RmEffects = '[ NotInstalled, UninstallFailed, ParseError, MalformedInstallInfo ] runRm :: (ReaderT env m (VEither RmEffects a) -> m (VEither RmEffects a))@@ -153,82 +171,45 @@ , MonadUnliftIO m , MonadFail m )- => Either RmCommand RmOptions- -> (ReaderT AppState m (VEither RmEffects (Maybe VersionInfo))- -> m (VEither RmEffects (Maybe VersionInfo)))- -> (ReaderT LeanAppState m () -> m ())+ => RmCommand+ -> (IO (AppState, IO ()), LeanAppState) -> m ExitCode-rm rmCommand runAppState runLogger = case rmCommand of- (Right rmopts) -> do- runLogger (logWarn "This is an old-style command for removing GHC. Use 'ghcup rm ghc' instead.")- rmGHC' rmopts- (Left (RmGHC rmopts)) -> rmGHC' rmopts- (Left (RmCabal rmopts)) -> rmCabal' rmopts- (Left (RmHLS rmopts)) -> rmHLS' rmopts- (Left (RmStack rmopts)) -> rmStack' rmopts+rm rmCommand (getAppState', leanAppstate) = case rmCommand of+ (RmGHC rmopts) -> rmOther (toRmOptionsNew ghc rmopts)+ (RmCabal (RmOptions . mkTVer -> rmopts)) -> rmOther (toRmOptionsNew cabal rmopts)+ (RmHLS (RmOptions . mkTVer -> rmopts)) -> rmOther (toRmOptionsNew hls rmopts)+ (RmStack (RmOptions . mkTVer -> rmopts)) -> rmOther (toRmOptionsNew stack rmopts)+ (RmOther rmopts) -> rmOther rmopts where- rmGHC' RmOptions{..} =- runRm runAppState (do- liftE $- rmGHCVer ghcVer- GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo- pure (getVersionInfo ghcVer GHC dls)- )- >>= \case- VRight vi -> do- postRmLog (tVerToText ghcVer) GHC vi- runLogger $ logGHCPostRm ghcVer- pure ExitSuccess- VLeft e -> do- runLogger $ logError $ T.pack $ prettyHFError e- pure $ ExitFailure 7-- rmCabal' tv =- runRm runAppState (do- liftE $- rmCabalVer tv- GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo- pure (getVersionInfo (mkTVer tv) Cabal dls)- )- >>= \case- VRight vi -> do- postRmLog (prettyVer tv) Cabal vi- pure ExitSuccess- VLeft e -> do- runLogger $ logError $ T.pack $ prettyHFError e- pure $ ExitFailure 15-- rmHLS' tv =- runRm runAppState (do- liftE $- rmHLSVer tv- GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo- pure (getVersionInfo (mkTVer tv) HLS dls)- )- >>= \case- VRight vi -> do- postRmLog (prettyVer tv) HLS vi- pure ExitSuccess- VLeft e -> do- runLogger $ logError $ T.pack $ prettyHFError e- pure $ ExitFailure 15+ toRmOptionsNew rmTool RmOptions{..} = RmOptionsNew{..}+ runLogger = flip runReaderT leanAppstate+ run action' = do+ (appstate', up) <- liftIO getAppState'+ r <- flip runReaderT appstate'+ . runResourceT+ . runE+ @RmEffects+ $ action'+ pure (r, up) - rmStack' tv =- runRm runAppState (do+ rmOther RmOptionsNew{..} =+ run (do liftE $- rmStackVer tv+ rmToolVersion rmTool ghcVer GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo- pure (getVersionInfo (mkTVer tv) Stack dls)+ pure (getVersionMetadata ghcVer rmTool dls) ) >>= \case- VRight vi -> do- postRmLog (prettyVer tv) Stack vi+ (VRight vm, up) -> do+ postRmLog (tVerToText ghcVer) rmTool vm+ when (rmTool == ghc) $ runLogger $ logGHCPostRm ghcVer+ liftIO up pure ExitSuccess- VLeft e -> do+ (VLeft e, _) -> do runLogger $ logError $ T.pack $ prettyHFError e- pure $ ExitFailure 15+ pure $ ExitFailure 7 - postRmLog tv tool vi = runLogger $ do- logInfo $ "Successfuly removed " <> T.pack (prettyShow tool) <> " " <> tv- forM_ (_viPostRemove =<< vi) logInfo+ postRmLog tv tool vm = runLogger $ do+ logInfo $ "Successfully removed " <> T.pack (prettyShow tool) <> " " <> tv+ forM_ (_vmPostRemove =<< vm) logInfo
lib-opt/GHCup/OptParse/Run.hs view
@@ -1,53 +1,60 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE ViewPatterns #-} module GHCup.OptParse.Run where -import GHCup-import GHCup.Utils-import GHCup.Utils.Parsers (fromVersion, ghcVersionTagEither, isolateParser, toolVersionTagEither)-import GHCup.OptParse.Common-import GHCup.Errors-import GHCup.Types-import GHCup.Types.Optics-import GHCup.Prelude-import GHCup.Prelude.File+import GHCup.Command.Install+import GHCup.Command.Install.LowLevel+import GHCup.Command.Set+import GHCup.Errors+import GHCup.Input.Parsers+import GHCup.Input.SymlinkSpec+import GHCup.Legacy.HLS ( setHLS )+import GHCup.Legacy.Utils+import GHCup.OptParse.Common+import GHCup.Prelude+import GHCup.Query.DB+import GHCup.Query.GHCupDirs+import GHCup.System.Directory+import GHCup.Types+import GHCup.Types.Optics #ifdef IS_WINDOWS-import GHCup.Prelude.Process-import GHCup.Prelude.Process.Windows ( execNoMinGW, resolveExecutable )+import Data.Maybe ( fromMaybe )+import GHCup.Prelude.Process+import GHCup.Prelude.Process.Windows ( execNoMinGW, resolveExecutable ) #endif-import GHCup.Prelude.Logger-import GHCup.Prelude.String.QQ+import GHCup.Prelude.String.QQ -import Control.Exception.Safe ( MonadMask, MonadCatch )+import Control.Exception.Safe ( displayException, handle ) #if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )+import Control.Monad.Fail ( MonadFail ) #endif-import Control.Monad (when, forM_, forM, unless)-import Control.Monad.Reader-import Control.Monad.Trans.Resource-import Data.Functor-import Data.Maybe (isNothing, fromMaybe)-import Data.List ( intercalate )-import Data.Variant.Excepts-import Options.Applicative hiding ( style )-import Prelude hiding ( appendFile )-import System.FilePath-import System.Environment-import System.Exit+import Control.Monad ( forM, forM_, when )+import Control.Monad.Reader+import Control.Monad.Trans.Resource+import Data.Data ( Proxy (..) )+import Data.Functor+import Data.List ( intercalate )+import Data.Maybe ( isNothing )+import Data.Variant.Excepts+import Options.Applicative hiding ( ParseError, style )+import Prelude hiding ( appendFile )+import System.Environment+import System.Exit+import System.FilePath+import Text.PrettyPrint.HughesPJClass ( prettyShow ) -import qualified Data.Map.Strict as Map-import qualified Data.Text as T+import qualified Data.Map.Strict as Map+import qualified Data.Text as T #ifndef IS_WINDOWS-import qualified System.Posix.Process as SPP+import qualified System.Posix.Process as SPP #endif-import Data.Versions ( prettyVer, Version ) @@ -60,16 +67,18 @@ data RunOptions = RunOptions { runAppendPATH :: Bool- , runInstTool' :: Bool- , runMinGWPath :: Bool- , runGHCVer :: Maybe ToolVersion- , runCabalVer :: Maybe ToolVersion- , runHLSVer :: Maybe ToolVersion- , runStackVer :: Maybe ToolVersion- , runBinDir :: Maybe FilePath- , runQuick :: Bool- , runCOMMAND :: [String]- } deriving (Eq, Show)+ , runInstTool' :: Bool+ , runMinGWPath :: Bool+ , runGHCVer :: Maybe ToolVersion+ , runCabalVer :: Maybe ToolVersion+ , runHLSVer :: Maybe ToolVersion+ , runStackVer :: Maybe ToolVersion+ , runToolVer :: [(Tool, ToolVersion)]+ , runBinDir :: Maybe FilePath+ , runQuick :: Bool+ , runCOMMAND :: [String]+ }+ deriving (Eq, Show) @@ -92,34 +101,42 @@ (option (eitherReader ghcVersionTagEither) (metavar "GHC_VERSION" <> long "ghc" <> help "The ghc version"- <> completer (tagCompleter GHC [])- <> (completer $ versionCompleter [] GHC)+ <> completer (tagCompleter ghc [])+ <> completer (versionCompleter [] ghc) ) ) <*> optional (option (eitherReader toolVersionTagEither) (metavar "CABAL_VERSION" <> long "cabal" <> help "The cabal version"- <> completer (tagCompleter Cabal [])- <> (completer $ versionCompleter [] Cabal)+ <> completer (tagCompleter cabal [])+ <> completer (versionCompleter [] cabal) ) ) <*> optional (option (eitherReader toolVersionTagEither) (metavar "HLS_VERSION" <> long "hls" <> help "The HLS version"- <> completer (tagCompleter HLS [])- <> (completer $ versionCompleter [] HLS)+ <> completer (tagCompleter hls [])+ <> completer (versionCompleter [] hls) ) ) <*> optional (option (eitherReader toolVersionTagEither) (metavar "STACK_VERSION" <> long "stack" <> help "The stack version"- <> completer (tagCompleter Stack [])- <> (completer $ versionCompleter [] Stack)+ <> completer (tagCompleter stack [])+ <> completer (versionCompleter [] stack) ) )+ <*> many+ ( option+ (eitherReader toolAndVersionParser)+ (metavar "TOOL,VERSION" <> long "tool" <> help "The tool and the version (separated by ',')"+ <> completer (tagCompleter stack [])+ <> completer (versionCompleter [] stack)+ )+ ) <*> optional (option (eitherReader isolateParser)@@ -194,33 +211,15 @@ , DistroNotFound , NoCompatibleArch , URIParseError+ , NoInstallInfo+ , ParseError+ , IncompatibleConfig+ , MalformedInstallInfo ] -runLeanRUN :: (MonadUnliftIO m, MonadIO m)- => LeanAppState- -> Excepts RunEffects (ReaderT LeanAppState m) a- -> m (VEither RunEffects a)-runLeanRUN leanAppstate =- -- Don't use runLeanAppState here, which is disabled on windows.- -- This is the only command on all platforms that doesn't need full appstate.- flip runReaderT leanAppstate- . runE- @RunEffects -runRUN :: MonadUnliftIO m- => IO AppState- -> Excepts RunEffects (ResourceT (ReaderT AppState m)) a- -> m (VEither RunEffects a)-runRUN appState action' = do- s' <- liftIO appState- flip runReaderT s'- . runResourceT- . runE- @RunEffects- $ action' - ------------------ --[ Entrypoint ]-- ------------------@@ -228,22 +227,16 @@ run :: forall m .- ( MonadFail m- , MonadMask m- , MonadCatch m- , MonadIO m- , MonadUnliftIO m+ ( MonadIOish m , Alternative m ) => RunOptions -> Settings- -> IO AppState- -> LeanAppState- -> (ReaderT LeanAppState m () -> m ())+ -> (IO (AppState, IO ()), LeanAppState) -> m ExitCode-run RunOptions{..} settings runAppState leanAppstate runLogger = do+run RunOptions{..} settings (getAppState', leanAppstate) = do r <- if not runQuick- then runRUN runAppState $ do+ then runRUN $ do toolchain <- liftE resolveToolchainFull -- oh dear@@ -252,13 +245,13 @@ liftE $ installToolChainFull toolchain tmp pure tmp- else runLeanRUN leanAppstate $ do+ else runLeanRUN $ do toolchain <- resolveToolchain tmp <- lift $ createTmpDir toolchain liftE $ installToolChain toolchain tmp pure tmp case r of- VRight tmp -> do+ VRight (fromInstallDir -> tmp) -> do case runCOMMAND of [] -> do liftIO $ putStr tmp@@ -273,8 +266,8 @@ #else resolvedCmd <- fmap (fromMaybe cmd) $ liftIO $ resolveExecutable cmd runMinGWPath r' <- if runMinGWPath- then runLeanRUN leanAppstate $ liftE $ lEM @_ @'[ProcessError] $ exec resolvedCmd args Nothing (Just newEnv)- else runLeanRUN leanAppstate $ liftE $ lEM @_ @'[ProcessError] $ execNoMinGW resolvedCmd args Nothing (Just newEnv)+ then runLeanRUN $ liftE $ lEM @_ @'[ProcessError] $ exec resolvedCmd args Nothing (Just newEnv)+ else runLeanRUN $ liftE $ lEM @_ @'[ProcessError] $ execNoMinGW resolvedCmd args Nothing (Just newEnv) case r' of VRight _ -> pure ExitSuccess VLeft e -> do@@ -289,64 +282,60 @@ guessMode = if guessVersion settings then GLaxWithInstalled else GStrict - -- TODO: doesn't work for cross- resolveToolchainFull :: ( MonadFail m- , MonadThrow m- , MonadIO m- , MonadCatch m- )+ resolveToolchainFull :: ( MonadIOish m ) => Excepts '[ TagNotFound , DayNotFound , NextVerNotFound , NoToolVersionSet+ , ParseError ] (ResourceT (ReaderT AppState m)) Toolchain resolveToolchainFull = do ghcVer <- forM runGHCVer $ \ver -> do- (v, _) <- liftE $ fromVersion (Just ver) guessMode GHC- pure v+ liftE $ resolveVersion (Just ver) guessMode ghc cabalVer <- forM runCabalVer $ \ver -> do- (v, _) <- liftE $ fromVersion (Just ver) guessMode Cabal- pure (_tvVersion v)+ fmap toVersionReq' $ liftE $ resolveVersion (Just ver) guessMode cabal hlsVer <- forM runHLSVer $ \ver -> do- (v, _) <- liftE $ fromVersion (Just ver) guessMode HLS- pure (_tvVersion v)+ fmap toVersionReq' $ liftE $ resolveVersion (Just ver) guessMode hls stackVer <- forM runStackVer $ \ver -> do- (v, _) <- liftE $ fromVersion (Just ver) guessMode Stack- pure (_tvVersion v)+ fmap toVersionReq' $ liftE $ resolveVersion (Just ver) guessMode stack+ toolVer <- forM runToolVer $ \(tool, ver) -> do+ v <- liftE $ resolveVersion (Just ver) guessMode tool+ pure (tool, v) pure Toolchain{..} resolveToolchain = do ghcVer <- case runGHCVer of Just (GHCVersion v) -> pure $ Just v- Just (ToolVersion v) -> pure $ Just (mkTVer v)+ Just (ToolVersion v) -> pure $ Just (toTargetVersionReq' v) Nothing -> pure Nothing- _ -> fail "Internal error"+ _ -> throwE $ IncompatibleConfig "Cannot resolve tags/dates in quick mode" cabalVer <- case runCabalVer of- Just (GHCVersion v) -> pure $ Just (_tvVersion v)+ Just (GHCVersion v) -> pure $ Just (toVersionReq' v) Just (ToolVersion v) -> pure $ Just v Nothing -> pure Nothing- _ -> fail "Internal error"+ _ -> throwE $ IncompatibleConfig "Cannot resolve tags/dates in quick mode" hlsVer <- case runHLSVer of- Just (GHCVersion v) -> pure $ Just (_tvVersion v)+ Just (GHCVersion v) -> pure $ Just (toVersionReq' v) Just (ToolVersion v) -> pure $ Just v Nothing -> pure Nothing- _ -> fail "Internal error"+ _ -> throwE $ IncompatibleConfig "Cannot resolve tags/dates in quick mode" stackVer <- case runStackVer of- Just (GHCVersion v) -> pure $ Just (_tvVersion v)+ Just (GHCVersion v) -> pure $ Just (toVersionReq' v) Just (ToolVersion v) -> pure $ Just v Nothing -> pure Nothing- _ -> fail "Internal error"+ _ -> throwE $ IncompatibleConfig "Cannot resolve tags/dates in quick mode"+ toolVer <- forM runToolVer $ \(tool, tver) -> case tver of+ (GHCVersion v) -> pure (tool, v)+ (ToolVersion v) -> pure (tool, toTargetVersionReq' v)+ _ -> throwE $ IncompatibleConfig "Cannot resolve tags/dates in quick mode" pure Toolchain{..} - installToolChainFull :: ( MonadFail m- , MonadThrow m- , MonadIO m- , MonadCatch m+ installToolChainFull :: ( MonadIOish m , Alternative m ) => Toolchain- -> FilePath+ -> InstallDirResolved -> Excepts '[ TagNotFound , DayNotFound@@ -375,87 +364,158 @@ , DistroNotFound , NoCompatibleArch , URIParseError+ , NoInstallInfo+ , ParseError+ , FileDoesNotExistError+ , MalformedInstallInfo ] (ResourceT (ReaderT AppState m)) () installToolChainFull Toolchain{..} tmp = do case ghcVer of- Just v -> do- isInstalled <- lift $ checkIfToolInstalled' GHC v- unless isInstalled $ when (runInstTool' && isNothing (_tvTarget v)) $ void $ liftE $ installGHCBin- v+ Just treq@(TargetVersionReq tver _rev) -> do+ hideExcept' @AlreadyInstalled Proxy $ when (runInstTool' && isNothing (_tvTarget tver)) $ void $ installTool+ ghc+ treq GHCupInternal False []- (T.pack "install")- setGHC' v tmp+ Nothing+ liftE $ setTool' ghc tver tmp _ -> pure () case cabalVer of- Just v -> do- isInstalled <- lift $ checkIfToolInstalled' Cabal (mkTVer v)- unless isInstalled $ when runInstTool' $ void $ liftE $ installCabalBin- v+ Just vreq@(VersionReq v _rev) -> do+ hideExcept' @AlreadyInstalled Proxy $ when runInstTool' $ void $ installTool+ cabal+ (toTargetVersionReq' vreq) GHCupInternal False- setCabal' v tmp+ []+ Nothing+ liftE $ setTool' cabal (mkTVer v) tmp _ -> pure () case stackVer of- Just v -> do- isInstalled <- lift $ checkIfToolInstalled' Stack (mkTVer v)- unless isInstalled $ when runInstTool' $ void $ liftE $ installStackBin- v+ Just vreq@(VersionReq v _rev) -> do+ hideExcept' @AlreadyInstalled Proxy $ when runInstTool' $ void $ installTool+ stack+ (toTargetVersionReq' vreq) GHCupInternal False- setStack' v tmp+ []+ Nothing+ liftE $ setTool' stack (mkTVer v) tmp _ -> pure () case hlsVer of- Just v -> do- isInstalled <- lift $ checkIfToolInstalled' HLS (mkTVer v)- unless isInstalled $ when runInstTool' $ void $ liftE $ installHLSBin- v+ Just vreq@(VersionReq v _rev) -> do+ hideExcept' @AlreadyInstalled Proxy $ when runInstTool' $ void $ installTool+ hls+ (toTargetVersionReq' vreq) GHCupInternal False- setHLS' v tmp+ []+ Nothing+ liftE $ setTool' hls (mkTVer v) tmp _ -> pure ()+ forM_ toolVer $ \(t, treq@(TargetVersionReq tver _rev)) -> do+ hideExcept' @AlreadyInstalled Proxy $ when (runInstTool' && isNothing (_tvTarget tver)) $ void $ installTool+ t+ treq+ GHCupInternal+ False+ []+ Nothing+ liftE $ setTool' t tver tmp - installToolChain :: ( MonadFail m- , MonadThrow m- , MonadIO m- , MonadCatch m+ installToolChain :: ( MonadIOish m ) => Toolchain- -> FilePath- -> Excepts '[NotInstalled] (ReaderT LeanAppState m) ()+ -> InstallDirResolved+ -> Excepts '[ParseError, NotInstalled, MalformedInstallInfo] (ReaderT LeanAppState m) () installToolChain Toolchain{..} tmp = do case ghcVer of- Just v -> setGHC' v tmp- _ -> pure ()+ Just (TargetVersionReq tver _rev) -> setTool' ghc tver tmp+ _ -> pure () case cabalVer of- Just v -> setCabal' v tmp- _ -> pure ()+ Just (VersionReq v _rev) -> setTool' cabal (mkTVer v) tmp+ _ -> pure () case stackVer of- Just v -> setStack' v tmp- _ -> pure ()+ Just (VersionReq v _rev) -> setTool' stack (mkTVer v) tmp+ _ -> pure () case hlsVer of- Just v -> setHLS' v tmp- _ -> pure ()+ Just (VersionReq v _rev) -> setTool' hls (mkTVer v) tmp+ _ -> pure ()+ forM_ toolVer $ \(t, TargetVersionReq tver _rev) -> setTool' t tver tmp - setGHC' v tmp = do- void $ liftE $ setGHC v SetGHC_XYZ (Just tmp)- void $ liftE $ setGHC v SetGHCOnly (Just tmp)- setCabal' v tmp = do- bin <- liftE $ whereIsTool Cabal (mkTVer v)- cbin <- liftIO $ canonicalizePath bin- lift $ createLink (relativeSymlink tmp cbin) (tmp </> ("cabal" <.> exeExt))- setStack' v tmp = do- bin <- liftE $ whereIsTool Stack (mkTVer v)- cbin <- liftIO $ canonicalizePath bin- lift $ createLink (relativeSymlink tmp cbin) (tmp </> ("stack" <.> exeExt))+ setTool' ::+ forall m1 env .+ ( MonadReader env m1+ , HasDirs env+ , HasPlatformReq env+ , HasLog env+ , MonadIOish m1+ )+ => Tool+ -> TargetVersion+ -> InstallDirResolved+ -> Excepts '[ParseError, NotInstalled, MalformedInstallInfo] m1 ()+ setTool' tool v tmp = do+ lift (runE @'[FileDoesNotExistError, ParseError, NoInstallInfo] (getSymlinkSpec' tool v)) >>= \case+ VRight symSpec -> do+ let tmp' = fromInstallDir tmp+ dest <- lift $ toolInstallDestination tool v+ liftE $ void $ symlinkBinaries (GHCupDir dest) symSpec tmp tool v+ liftE $ void $ setToolVersion' tool v (Just tmp')+ VLeft (V pe@(ParseError _)) -> fail $ prettyHFError pe+ VLeft _ -- legacy+ | tool == ghc -> do+ let tmp' = fromInstallDir tmp+ pfreq <- lift getPlatformReq+ symSpec <- forM (defaultGHCExeSymLinked pfreq v (ghcBinaries pfreq v)) (liftE . parseSymlinkSpec (_tvVersion v))+ dest <- lift $ toolInstallDestination tool v+ liftE $ void $ symlinkBinaries (GHCupDir dest) symSpec tmp tool v+ liftE $ void $ setToolVersion' tool v (Just tmp')+ -- we can't use 'symlinkBinaries' here for most tools, because+ -- it relies on everything residing within @~/.ghcup/<tool>@,+ -- which is not the case for early cabal/stack/hls+ | tool == cabal -> legacySet'+ | tool == stack -> legacySet'+ | tool == hls -> do+ let tmp' = fromInstallDir tmp+ setHLS' (_tvVersion v) tmp'+ | tool == ghcup -> do+ pure ()+ | otherwise ->+ throwE $ NotInstalled tool v+ pure ()+ where+ legacySet' = do+ let tmp' = fromInstallDir tmp+ Dirs {..} <- getDirs+ let tool' = prettyShow tool+ pvpExe = tool' <> "-" <> prettyShow v <.> exeExt++ -- create <tool>-X.Y.Z+ target <- binarySymLinkDestination tmp' (binDir </> pvpExe)+ lift $ createLink target (tmp' </> pvpExe)++ -- create <tool>-X.Y+ lift $ handle+ (\(e :: ParseError) -> logWarn (T.pack $ displayException e))+ $ do+ (mj, mi) <- getMajorMinorV (_tvVersion v)+ let exeMajorMinor = tool' <> "-" <> T.unpack (intToText mj) <> "." <> T.unpack (intToText mi) <.> exeExt+ createLink pvpExe (tmp' </> exeMajorMinor)++ -- create <tool>+ liftE $ void $ setToolVersion' tool v (Just tmp')+++ -- TODO: legacy setHLS' v tmp = do Dirs {..} <- getDirs legacy <- isLegacyHLS v if legacy then do -- TODO: factor this out- hlsWrapper <- liftE @_ @'[NotInstalled] $ hlsWrapperBinary v !? (NotInstalled HLS (mkTVer v))+ hlsWrapper <- liftE @_ @'[NotInstalled] $ hlsWrapperBinary v !? NotInstalled hls (mkTVer v) cw <- liftIO $ canonicalizePath (binDir </> hlsWrapper) lift $ createLink (relativeSymlink tmp cw) (tmp </> takeFileName cw) hlsBins <- hlsServerBinaries v Nothing >>= liftIO . traverse (canonicalizePath . (binDir </>))@@ -463,46 +523,57 @@ lift $ createLink (relativeSymlink tmp bin) (tmp </> takeFileName bin) liftE $ setHLS v SetHLSOnly (Just tmp) else do- liftE $ setHLS v SetHLS_XYZ (Just tmp)- liftE $ setHLS v SetHLSOnly (Just tmp)+ liftE $ void $ setToolVersion' hls (mkTVer v) (Just tmp) - createTmpDir :: ( MonadUnliftIO m- , MonadCatch m- , MonadThrow m- , MonadMask m- , MonadIO m- )+ createTmpDir :: ( MonadIOish m ) => Toolchain- -> ReaderT LeanAppState m FilePath+ -> ReaderT LeanAppState m InstallDirResolved createTmpDir toolchain = case runBinDir of Just bindir -> do liftIO $ createDirRecursive' bindir- liftIO $ canonicalizePath bindir+ fmap IsolateDirResolved $ liftIO $ canonicalizePath bindir Nothing -> do d <- predictableTmpDir toolchain- liftIO $ createDirRecursive' d- liftIO $ canonicalizePath d+ liftIO $ createDirRecursive' (fromGHCupPath d)+ pure $ GHCupDir d predictableTmpDir :: Monad m => Toolchain- -> ReaderT LeanAppState m FilePath- predictableTmpDir (Toolchain Nothing Nothing Nothing Nothing) = do+ -> ReaderT LeanAppState m GHCupPath+ predictableTmpDir (Toolchain Nothing Nothing Nothing Nothing []) = do Dirs { tmpDir } <- getDirs- pure (fromGHCupPath tmpDir </> "ghcup-none")+ pure (tmpDir `appendGHCupPath` "ghcup-none") predictableTmpDir Toolchain{..} = do Dirs { tmpDir } <- getDirs- pure $ fromGHCupPath tmpDir- </> ("ghcup-" <> intercalate "_"- ( maybe [] ( (:[]) . ("ghc-" <>) . T.unpack . tVerToText) ghcVer- <> maybe [] ( (:[]) . ("cabal-" <>) . T.unpack . prettyVer) cabalVer- <> maybe [] ( (:[]) . ("hls-" <>) . T.unpack . prettyVer) hlsVer- <> maybe [] ( (:[]) . ("stack-" <>) . T.unpack . prettyVer) stackVer+ pure $ tmpDir+ `appendGHCupPath` ("ghcup-" <> intercalate "_"+ ( maybe [] ( (:[]) . ("ghc-" <>) . prettyShow) ghcVer+ <> maybe [] ( (:[]) . ("cabal-" <>) . prettyShow) cabalVer+ <> maybe [] ( (:[]) . ("hls-" <>) . prettyShow) hlsVer+ <> maybe [] ( (:[]) . ("stack-" <>) . prettyShow) stackVer+ <> fmap (\(t, v) -> prettyShow t <> "-" <> prettyShow v) toolVer ) ) + runLogger = flip runReaderT leanAppstate+ runLeanRUN :: forall m1 a . Excepts RunEffects (ReaderT LeanAppState m1) a -> m1 (VEither RunEffects a)+ runLeanRUN =+ -- Don't use runLeanAppState here, which is disabled on windows.+ -- This is the only command on all platforms that doesn't need full appstate.+ flip runReaderT leanAppstate+ . runE+ @RunEffects + runRUN action' = do+ (appstate', _) <- liftIO getAppState'+ flip runReaderT appstate'+ . runResourceT+ . runE+ @RunEffects+ $ action' + ------------------------- --[ Other local types ]-- -------------------------@@ -510,8 +581,10 @@ data Toolchain = Toolchain- { ghcVer :: Maybe GHCTargetVersion- , cabalVer :: Maybe Version- , hlsVer :: Maybe Version- , stackVer :: Maybe Version- } deriving Show+ { ghcVer :: Maybe TargetVersionReq+ , cabalVer :: Maybe VersionReq+ , hlsVer :: Maybe VersionReq+ , stackVer :: Maybe VersionReq+ , toolVer :: [(Tool, TargetVersionReq)]+ }+ deriving (Show)
lib-opt/GHCup/OptParse/Set.hs view
@@ -1,46 +1,54 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-} {-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeApplications #-} module GHCup.OptParse.Set where -import GHCup.OptParse.Common+import GHCup.OptParse.Common -import GHCup-import GHCup.Errors-import GHCup.Types-import GHCup.Utils.Parsers (SetToolVersion(..), tagEither, ghcVersionEither, toolVersionEither, fromVersion')-import GHCup.Prelude.Logger-import GHCup.Prelude.String.QQ+import GHCup.Command.List+import GHCup.Command.Set+import GHCup.Errors+import GHCup.Input.Parsers+ ( SetToolVersion (..)+ , ghcVersionEither'+ , resolveVersion'+ , tagEither+ , toolParser+ , toolVersionEither'+ )+import GHCup.Prelude.Logger+import GHCup.Prelude.String.QQ+import GHCup.Types #if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )+import Control.Monad.Fail ( MonadFail ) #endif-import Control.Monad.Reader-import Control.Monad.Trans.Resource-import Data.Either-import Data.Functor-import Data.Maybe-import Data.Versions-import GHC.Unicode-import Data.Variant.Excepts-import Options.Applicative hiding ( style )-import Options.Applicative.Pretty.Shim ( text )-import Prelude hiding ( appendFile )-import System.Exit+import Control.Exception.Safe ( MonadMask )+import Control.Monad.Reader+import Control.Monad.Trans.Resource+import Data.Bifunctor ( second )+import Data.Either+import Data.Functor+import Data.Maybe+import Data.Variant.Excepts+import Data.Versions+import GHC.Unicode+import Options.Applicative hiding ( ParseError, style )+import Options.Applicative.Pretty.Shim ( text )+import Prelude hiding ( appendFile )+import System.Exit+import Text.PrettyPrint.HughesPJClass ( prettyShow ) -import qualified Data.Text as T-import Data.Bifunctor (second)-import Control.Exception.Safe (MonadMask)-import GHCup.Types.Optics+import qualified Data.Text as T @@ -50,11 +58,13 @@ ---------------- -data SetCommand = SetGHC SetOptions- | SetCabal SetOptions- | SetHLS SetOptions- | SetStack SetOptions- deriving (Eq, Show)+data SetCommand+ = SetGHC SetOptions+ | SetCabal SetOptions+ | SetHLS SetOptions+ | SetStack SetOptions+ | SetOther SetOptionsNew+ deriving (Eq, Show) @@ -66,24 +76,31 @@ data SetOptions = SetOptions { sToolVer :: SetToolVersion- } deriving (Eq, Show)+ }+ deriving (Eq, Show) +data SetOptionsNew = SetOptionsNew+ { sTool :: Tool+ , sToolVer :: SetToolVersion+ }+ deriving (Eq, Show) + --------------- --[ Parsers ]-- --------------- -setParser :: Parser (Either SetCommand SetOptions)+setParser :: Parser SetCommand setParser =- (Left <$> subparser+ subparser ( command "ghc" ( SetGHC <$> info- (setOpts GHC <**> helper)+ (setOpts ghc <**> helper) ( progDesc "Set GHC version" <> footerDoc (Just $ text setGHCFooter) )@@ -92,7 +109,7 @@ "cabal" ( SetCabal <$> info- (setOpts Cabal <**> helper)+ (setOpts cabal <**> helper) ( progDesc "Set Cabal version" <> footerDoc (Just $ text setCabalFooter) )@@ -101,7 +118,7 @@ "hls" ( SetHLS <$> info- (setOpts HLS <**> helper)+ (setOpts hls <**> helper) ( progDesc "Set haskell-language-server version" <> footerDoc (Just $ text setHLSFooter) )@@ -110,14 +127,15 @@ "stack" ( SetStack <$> info- (setOpts Stack <**> helper)+ (setOpts stack <**> helper) ( progDesc "Set stack version" <> footerDoc (Just $ text setStackFooter) ) ) )- )- <|> (Right <$> setOpts GHC)+ <|> ( SetOther+ <$> setOptsNew+ ) where setGHCFooter :: String setGHCFooter = [s|Discussion:@@ -139,10 +157,23 @@ setOpts :: Tool -> Parser SetOptions-setOpts tool = SetOptions <$>- (fromMaybe SetRecommended <$>- optional (setVersionArgument [ListInstalled True] tool))+setOpts tool =+ SetOptions . fromMaybe SetRecommended <$>+ optional (setVersionArgument [ListInstalled True] tool) +setOptsNew :: Parser SetOptionsNew+setOptsNew = SetOptionsNew+ <$> argument (eitherReader toolParser) (metavar "TOOL")+ <*> (fromMaybe SetRecommended <$> optional (argument (eitherReader setEither) (metavar "VERSION|TAG|next")))+ where+ setEither s' =+ parseSet s'+ <|> second SetToolTag (tagEither s')+ <|> second SetGHCVersion (ghcVersionEither' s')+ parseSet s' = case fmap toLower s' of+ "next" -> Right SetNext+ other -> Left $ "Unknown tag/version " <> other+ setVersionArgument :: [ListCriteria] -> Tool -> Parser SetToolVersion setVersionArgument criteria tool = argument (eitherReader setEither)@@ -155,8 +186,8 @@ <|> second SetToolTag (tagEither s') <|> se s' se s' = case tool of- GHC -> second SetGHCVersion (ghcVersionEither s')- _ -> second SetToolVersion (toolVersionEither s')+ Tool "ghc" -> second SetGHCVersion (ghcVersionEither' s')+ _ -> second SetToolVersion (toolVersionEither' s') parseSet s' = case fmap toLower s' of "next" -> Right SetNext other -> Left $ "Unknown tag/version " <> other@@ -183,65 +214,14 @@ --------------------------- -type SetGHCEffects = '[ FileDoesNotExistError+type SetEffects = '[ FileDoesNotExistError , NotInstalled , TagNotFound , DayNotFound , NextVerNotFound- , NoToolVersionSet]--runSetGHC :: (ReaderT env m (VEither SetGHCEffects a) -> m (VEither SetGHCEffects a))- -> Excepts SetGHCEffects (ReaderT env m) a- -> m (VEither SetGHCEffects a)-runSetGHC runAppState =- runAppState- . runE- @SetGHCEffects---type SetCabalEffects = '[ NotInstalled- , TagNotFound- , DayNotFound- , NextVerNotFound- , NoToolVersionSet]--runSetCabal :: (ReaderT env m (VEither SetCabalEffects a) -> m (VEither SetCabalEffects a))- -> Excepts SetCabalEffects (ReaderT env m) a- -> m (VEither SetCabalEffects a)-runSetCabal runAppState =- runAppState- . runE- @SetCabalEffects---type SetHLSEffects = '[ NotInstalled- , TagNotFound- , DayNotFound- , NextVerNotFound- , NoToolVersionSet]--runSetHLS :: (ReaderT env m (VEither SetHLSEffects a) -> m (VEither SetHLSEffects a))- -> Excepts SetHLSEffects (ReaderT env m) a- -> m (VEither SetHLSEffects a)-runSetHLS runAppState =- runAppState- . runE- @SetHLSEffects---type SetStackEffects = '[ NotInstalled- , TagNotFound- , DayNotFound- , NextVerNotFound- , NoToolVersionSet]--runSetStack :: (ReaderT env m (VEither SetStackEffects a) -> m (VEither SetStackEffects a))- -> Excepts SetStackEffects (ReaderT env m) a- -> m (VEither SetStackEffects a)-runSetStack runAppState =- runAppState- . runE- @SetStackEffects+ , NoToolVersionSet+ , ParseError+ ] @@ -250,99 +230,52 @@ ------------------- -set :: forall m env.- ( Monad m+set :: forall m. ( Monad m , MonadMask m , MonadUnliftIO m , MonadFail m- , HasDirs env- , HasLog env )- => Either SetCommand SetOptions+ => SetCommand -> Settings- -> (forall eff . ReaderT AppState m (VEither eff GHCTargetVersion)- -> m (VEither eff GHCTargetVersion))- -> (forall eff. ReaderT env m (VEither eff GHCTargetVersion)- -> m (VEither eff GHCTargetVersion))- -> (ReaderT LeanAppState m () -> m ())+ -> (IO (AppState, IO ()), LeanAppState) -> m ExitCode-set setCommand settings runAppState _ runLogger = case setCommand of- (Right sopts) -> do- runLogger (logWarn "This is an old-style command for setting GHC. Use 'ghcup set ghc' instead.")- setGHC' sopts- (Left (SetGHC sopts)) -> setGHC' sopts- (Left (SetCabal sopts)) -> setCabal' sopts- (Left (SetHLS sopts)) -> setHLS' sopts- (Left (SetStack sopts)) -> setStack' sopts+set setCommand settings (getAppState', leanAppstate) = case setCommand of+ (SetGHC sopts) -> setOther (toSetOptionsNew ghc sopts)+ (SetCabal sopts) -> setOther (toSetOptionsNew cabal sopts)+ (SetHLS sopts) -> setOther (toSetOptionsNew hls sopts)+ (SetStack sopts) -> setOther (toSetOptionsNew stack sopts)+ (SetOther sopts) -> setOther sopts where guessMode = if guessVersion settings then GLaxWithInstalled else GStrict-- setGHC' :: SetOptions- -> m ExitCode- setGHC' SetOptions{ sToolVer } = runSetGHC runAppState (do- v <- liftE $ fst <$> fromVersion' sToolVer guessMode GHC- liftE $ setGHC v SetGHCOnly Nothing- )- >>= \case- VRight GHCTargetVersion{..} -> do- runLogger- $ logInfo $- "GHC " <> prettyVer _tvVersion <> " successfully set as default version" <> maybe "" (" for cross target " <>) _tvTarget- pure ExitSuccess- VLeft e -> do- runLogger $ logError $ T.pack $ prettyHFError e- pure $ ExitFailure 5+ runLogger = flip runReaderT leanAppstate+ run action' = do+ (appstate', up) <- liftIO getAppState'+ r <- flip runReaderT appstate'+ . runResourceT+ . runE+ @SetEffects+ $ action'+ pure (r, up) - setCabal' :: SetOptions- -> m ExitCode- setCabal' SetOptions{ sToolVer } = runSetCabal runAppState (do- v <- liftE $ fst <$> fromVersion' sToolVer guessMode Cabal- liftE $ setCabal (_tvVersion v)- pure v- )- >>= \case- VRight v -> do- runLogger- $ logInfo $- "Cabal " <> prettyVer (_tvVersion v) <> " successfully set as default version"- pure ExitSuccess- VLeft e -> do- runLogger $ logError $ T.pack $ prettyHFError e- pure $ ExitFailure 14+ toSetOptionsNew sTool SetOptions{..} = SetOptionsNew{..} - setHLS' :: SetOptions- -> m ExitCode- setHLS' SetOptions{ sToolVer } = runSetHLS runAppState (do- v <- liftE $ fst <$> fromVersion' sToolVer guessMode HLS- liftE $ setHLS (_tvVersion v) SetHLSOnly Nothing- pure v+ setOther :: SetOptionsNew+ -> m ExitCode+ setOther SetOptionsNew{ sTool, sToolVer } = run (do+ (TargetVersionReq v _) <- liftE $ resolveVersion' sToolVer guessMode sTool+ liftE $ setToolVersion sTool v ) >>= \case- VRight v -> do+ (VRight TargetVersion{..}, up) -> do runLogger $ logInfo $- "HLS " <> prettyVer (_tvVersion v) <> " successfully set as default version"+ T.pack (prettyShow sTool) <> " " <> prettyVer _tvVersion <> " successfully set as default version" <> maybe "" (" for cross target " <>) _tvTarget+ liftIO up pure ExitSuccess- VLeft e -> do+ (VLeft e, _) -> do runLogger $ logError $ T.pack $ prettyHFError e- pure $ ExitFailure 14+ pure $ ExitFailure 5 - setStack' :: SetOptions- -> m ExitCode- setStack' SetOptions{ sToolVer } = runSetStack runAppState (do- v <- liftE $ fst <$> fromVersion' sToolVer guessMode Stack- liftE $ setStack (_tvVersion v)- pure v- )- >>= \case- VRight v -> do- runLogger- $ logInfo $- "Stack " <> prettyVer (_tvVersion v) <> " successfully set as default version"- pure ExitSuccess- VLeft e -> do- runLogger $ logError $ T.pack $ prettyHFError e- pure $ ExitFailure 14
lib-opt/GHCup/OptParse/Test.hs view
@@ -1,45 +1,44 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-} {-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE ViewPatterns #-}-{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeOperators #-} module GHCup.OptParse.Test where -import GHCup.OptParse.Common+import GHCup.OptParse.Common -import GHCup-import GHCup.Errors-import GHCup.Types-import GHCup.Utils.Dirs-import GHCup.Utils.Parsers (fromVersion, uriParser)-import GHCup.Prelude-import GHCup.Prelude.Logger-import GHCup.Prelude.String.QQ+import GHCup.Command.Test.GHC+import GHCup.Errors+import GHCup.Input.Parsers ( resolveVersion, uriParser )+import GHCup.Prelude+import GHCup.Prelude.String.QQ+import GHCup.Query.GHCupDirs+import GHCup.Query.Metadata+import GHCup.Types+import GHCup.Types.Optics #if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )+import Control.Monad.Fail ( MonadFail ) #endif-import Control.Monad.Reader-import Control.Monad.Trans.Resource-import Data.Functor-import Data.Maybe-import Data.Variant.Excepts-import Options.Applicative hiding ( style )-import Options.Applicative.Pretty.Shim ( text )-import Prelude hiding ( appendFile )-import System.Exit-import URI.ByteString hiding ( uriParser )+import Control.Monad.Reader+import Control.Monad.Trans.Resource+import Data.Functor+import Data.Maybe+import Data.Variant.Excepts+import Options.Applicative hiding ( ParseError, style )+import Options.Applicative.Pretty.Shim ( text )+import Prelude hiding ( appendFile )+import System.Exit+import URI.ByteString hiding ( uriParser ) -import qualified Data.Text as T+import qualified Data.Text as T @@ -60,9 +59,9 @@ data TestOptions = TestOptions- { testVer :: Maybe ToolVersion- , testBindist :: Maybe URI- , addMakeArgs :: [T.Text]+ { testVer :: Maybe ToolVersion+ , testBindist :: Maybe URI+ , addMakeArgs :: [T.Text] } @@ -89,7 +88,7 @@ "ghc" ( TestGHC <$> info- (testOpts (Just GHC) <**> helper)+ (testOpts (Just ghc) <**> helper) ( progDesc "Test GHC" <> footerDoc (Just $ text testGHCFooter) )@@ -110,7 +109,7 @@ (eitherReader uriParser) (short 'u' <> long "url" <> metavar "BINDIST_URL" <> help "Install the specified version from this bindist"- <> completer (toolDlCompleter (fromMaybe GHC tool))+ <> completer (toolDlCompleter (fromMaybe ghc tool)) ) ) <*> (Just <$> toolVersionTagArgument [] tool)@@ -144,16 +143,10 @@ , DayNotFound , NoToolVersionSet , URIParseError+ , NoInstallInfo+ , ParseError ] -runTestGHC :: AppState- -> Excepts TestGHCEffects (ResourceT (ReaderT AppState IO)) a- -> IO (VEither TestGHCEffects a)-runTestGHC appstate' =- flip runReaderT appstate'- . runResourceT- . runE- @TestGHCEffects -------------------@@ -161,29 +154,39 @@ ------------------- -test :: TestCommand -> Settings -> IO AppState -> (ReaderT LeanAppState IO () -> IO ()) -> IO ExitCode-test testCommand settings getAppState' runLogger = case testCommand of+test :: TestCommand -> Settings -> (IO (AppState, IO ()), LeanAppState) -> IO ExitCode+test testCommand settings (getAppState', leanAppstate) = case testCommand of (TestGHC iopts) -> go iopts where guessMode = if guessVersion settings then GLaxWithInstalled else GStrict+ runLogger = flip runReaderT leanAppstate go :: TestOptions -> IO ExitCode go TestOptions{..} = do- s'@AppState{ dirs = Dirs{ .. } } <- liftIO getAppState'+ (s'@AppState{ dirs = Dirs{ .. } }, up) <- getAppState'+ let run appstate' = flip runReaderT (appstate' :: AppState)+ . runResourceT+ . runE+ @TestGHCEffects (case testBindist of- Nothing -> runTestGHC s' $ do- (v, vi) <- liftE $ fromVersion testVer guessMode GHC- liftE $ testGHCVer v addMakeArgs- pure vi+ Nothing -> run s' $ do+ GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo+ treq@(TargetVersionReq tver _) <- liftE $ resolveVersion testVer guessMode ghc+ let vm = getVersionMetadata tver ghcup dls+ liftE $ testGHCVer treq addMakeArgs+ pure vm Just uri -> do- runTestGHC s'{ settings = settings {noVerify = True}} $ do- (v, vi) <- liftE $ fromVersion testVer guessMode GHC- liftE $ testGHCBindist (DownloadInfo ((decUTF8Safe . serializeURIRef') uri) (Just $ RegexDir ".*/.*") "" Nothing Nothing Nothing) v addMakeArgs- pure vi+ run s'{ settings = settings {noVerify = True}} $ do+ GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo+ (TargetVersionReq tver _) <- liftE $ resolveVersion testVer guessMode ghc+ let vm = getVersionMetadata tver ghcup dls+ liftE $ testGHCBindist (DownloadInfo ((decUTF8Safe . serializeURIRef') uri) (Just $ RegexDir ".*/.*") "" Nothing Nothing Nothing Nothing) tver addMakeArgs+ pure vm ) >>= \case VRight _ -> do runLogger $ logInfo "GHC test successful"+ liftIO up pure ExitSuccess VLeft e -> do runLogger $ do
lib-opt/GHCup/OptParse/ToolRequirements.hs view
@@ -28,9 +28,9 @@ import qualified Data.Text.IO as T import Control.Exception.Safe (MonadMask) import GHCup.Types.Optics-import GHCup.Platform+import GHCup.Query.System import GHCup.Prelude-import GHCup.Requirements+import GHCup.Query.Metadata import System.IO @@ -51,7 +51,7 @@ --[ Parsers ]-- --------------- - + toolReqP :: Parser ToolReqOpts toolReqP = ToolReqOpts@@ -103,13 +103,13 @@ , Alternative m ) => ToolReqOpts- -> (ReaderT AppState m (VEither ToolRequirementsEffects ()) -> m (VEither ToolRequirementsEffects ()))- -> (ReaderT LeanAppState m () -> m ())+ -> (IO (AppState, IO ()), LeanAppState) -> m ExitCode-toolRequirements ToolReqOpts{..} runAppState runLogger = runToolRequirements runAppState (do+toolRequirements ToolReqOpts{..} (getAppState', leanAppstate) = run (do GHCupInfo { .. } <- lift getGHCupInfo platform' <- liftE getPlatform- req <- getCommonRequirements platform' _toolRequirements ?? NoToolRequirements+ -- TODO+ req <- getCommonRequirements ghc platform' _toolRequirements ?? NoToolRequirements if tlrRaw then liftIO $ T.hPutStr stdout (rawRequirements req) else liftIO $ T.hPutStr stdout (prettyRequirements req)@@ -119,3 +119,12 @@ VLeft e -> do runLogger $ logError $ T.pack $ prettyHFError e pure $ ExitFailure 12+ where+ run action' = do+ (appstate', _) <- liftIO getAppState'+ flip runReaderT appstate'+ . runResourceT+ . runE+ @ToolRequirementsEffects+ $ action'+ runLogger = flip runReaderT leanAppstate
lib-opt/GHCup/OptParse/UnSet.hs view
@@ -2,7 +2,6 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE DuplicateRecordFields #-}@@ -13,8 +12,9 @@ -import GHCup import GHCup.Errors+import GHCup.Input.Parsers (toolParser)+import GHCup.Command.Set import GHCup.Types import GHCup.Prelude.Logger import GHCup.Prelude.String.QQ@@ -27,18 +27,19 @@ import Data.Functor import Data.Maybe import Data.Variant.Excepts-import Options.Applicative hiding ( style )+import Options.Applicative hiding ( style, ParseError ) import Options.Applicative.Pretty.Shim ( text ) import Prelude hiding ( appendFile ) import System.Exit import qualified Data.Text as T-import Control.Exception.Safe (MonadMask)-import GHCup.Types.Optics+import Control.Exception.Safe (MonadMask) +import Text.PrettyPrint.HughesPJClass (prettyShow) + ---------------- --[ Commands ]-- ----------------@@ -48,6 +49,7 @@ | UnsetCabal UnsetOptions | UnsetHLS UnsetOptions | UnsetStack UnsetOptions+ | UnsetOther UnsetOptionsNew deriving (Eq, Show) @@ -59,9 +61,13 @@ data UnsetOptions = UnsetOptions- { sToolVer :: Maybe T.Text -- target platform triple+ { sToolTriple :: Maybe T.Text -- target platform triple } deriving (Eq, Show) +data UnsetOptionsNew = UnsetOptionsNew+ { sTool :: Tool+ , sToolTriple :: Maybe T.Text -- target platform triple+ } deriving (Eq, Show) @@ -109,6 +115,8 @@ <> footerDoc (Just $ text unsetStackFooter) ) )+ ) <|>+ ( UnsetOther <$> unsetOptsNew ) where unsetGHCFooter :: String@@ -117,7 +125,7 @@ be a ~/.ghcup/bin/ghc anymore. Examples:- # unset ghc + # unset ghc ghcup unset ghc # unset ghc for the target version@@ -139,8 +147,13 @@ unsetOpts :: Parser UnsetOptions unsetOpts = UnsetOptions . fmap T.pack <$> optional (argument str (metavar "TRIPLE")) +unsetOptsNew :: Parser UnsetOptionsNew+unsetOptsNew = UnsetOptionsNew+ <$> argument (eitherReader toolParser) (metavar "TOOL")+ <*> (fmap T.pack <$> optional (argument str (metavar "TRIPLE"))) + -------------- --[ Footer ]-- --------------@@ -158,19 +171,11 @@ --------------------------- -type UnsetEffects = '[ NotInstalled ]+type UnsetEffects = '[ NotInstalled, ParseError, NoToolVersionSet ] -runUnsetGHC :: (ReaderT env m (VEither UnsetEffects a) -> m (VEither UnsetEffects a))- -> Excepts UnsetEffects (ReaderT env m) a- -> m (VEither UnsetEffects a)-runUnsetGHC runLeanAppState =- runLeanAppState- . runE- @UnsetEffects - ------------------ --[ Entrypoint ]-- ------------------@@ -181,32 +186,30 @@ , MonadMask m , MonadUnliftIO m , MonadFail m- , HasDirs env- , HasLog env ) => UnsetCommand- -> (ReaderT env m (VEither UnsetEffects ())- -> m (VEither UnsetEffects ()))- -> (ReaderT LeanAppState m () -> m ())+ -> (IO (AppState, IO ()), LeanAppState) -> m ExitCode-unset unsetCommand runLeanAppState runLogger = case unsetCommand of- (UnsetGHC (UnsetOptions triple)) -> runUnsetGHC runLeanAppState (unsetGHC triple)+unset unsetCommand (_getAppState', leanAppstate) = case unsetCommand of+ (UnsetGHC usopts) -> unsetOther (toUnsetOptionsNew ghc usopts)+ (UnsetCabal usopts) -> unsetOther (toUnsetOptionsNew cabal usopts)+ (UnsetHLS usopts) -> unsetOther (toUnsetOptionsNew hls usopts)+ (UnsetStack usopts) -> unsetOther (toUnsetOptionsNew stack usopts)+ (UnsetOther usopts) -> unsetOther usopts++ where+ runLogger = flip runReaderT leanAppstate++ toUnsetOptionsNew sTool UnsetOptions{..} = UnsetOptionsNew{..}++ unsetOther UnsetOptionsNew{..} =+ (flip runReaderT leanAppstate . runE @UnsetEffects) (liftE $ unsetTool sTool sToolTriple) >>= \case VRight _ -> do- runLogger $ logInfo "GHC successfully unset"+ runLogger $ logInfo $ T.pack (prettyShow sTool) <> " successfully unset" pure ExitSuccess VLeft e -> do runLogger $ logError $ T.pack $ prettyHFError e pure $ ExitFailure 14- (UnsetCabal (UnsetOptions _)) -> do- void $ runLeanAppState (VRight <$> unsetCabal)- runLogger $ logInfo "Cabal successfully unset"- pure ExitSuccess- (UnsetHLS (UnsetOptions _)) -> do- void $ runLeanAppState (VRight <$> unsetHLS)- runLogger $ logInfo "HLS successfully unset"- pure ExitSuccess- (UnsetStack (UnsetOptions _)) -> do- void $ runLeanAppState (VRight <$> unsetStack)- runLogger $ logInfo "Stack successfully unset"- pure ExitSuccess++
lib-opt/GHCup/OptParse/Upgrade.hs view
@@ -1,58 +1,59 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-} {-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeApplications #-} module GHCup.OptParse.Upgrade where -import GHCup-import GHCup.Errors-import GHCup.Types-import GHCup.Prelude.File-import GHCup.Prelude.Logger+import GHCup.Command.Upgrade+import GHCup.Errors+import GHCup.Prelude.File+import GHCup.Prelude.Logger+import GHCup.Query.Metadata+import GHCup.Types+import GHCup.Types.Optics -import Control.Concurrent (threadDelay)+import Control.Concurrent ( threadDelay ) #if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )+import Control.Monad.Fail ( MonadFail ) #endif-import Control.Monad (forM_)-import Control.Monad.Reader-import Control.Monad.Trans.Resource-import Data.Functor-import Data.Maybe-import Data.Variant.Excepts-import Options.Applicative hiding ( style )-import Prelude hiding ( appendFile )-import System.Exit--import qualified Data.Text as T-import Control.Exception.Safe (MonadMask)+import Control.Exception.Safe ( MonadMask )+import Control.Monad ( forM_ )+import Control.Monad.Reader+import Control.Monad.Trans.Resource+import Data.Functor+import Data.Maybe+import Data.Variant.Excepts+import Data.Versions+import Options.Applicative hiding ( style )+import Prelude hiding ( appendFile ) import System.Environment-import GHCup.Utils+import System.Exit import System.FilePath-import GHCup.Types.Optics-import Data.Versions +import qualified Data.Text as T + --------------- --[ Options ]-- --------------- -data UpgradeOpts = UpgradeInplace- | UpgradeAt FilePath- | UpgradeGHCupDir- deriving (Eq, Show)+data UpgradeOpts+ = UpgradeInplace+ | UpgradeAt FilePath+ | UpgradeGHCupDir+ deriving (Eq, Show) @@ -101,18 +102,8 @@ ] -runUpgrade :: MonadUnliftIO m- => (ReaderT AppState m (VEither UpgradeEffects a) -> m (VEither UpgradeEffects a))- -> Excepts UpgradeEffects (ResourceT (ReaderT AppState m)) a- -> m (VEither UpgradeEffects a)-runUpgrade runAppState =- runAppState- . runResourceT- . runE- @UpgradeEffects - ------------------ --[ Entrypoint ]-- ------------------@@ -128,38 +119,48 @@ -> Bool -> Bool -> Dirs- -> (forall a. ReaderT AppState m (VEither UpgradeEffects a) -> m (VEither UpgradeEffects a))- -> (ReaderT LeanAppState m () -> m ())+ -> (IO (AppState, IO ()), LeanAppState) -> m ExitCode-upgrade uOpts force' fatal Dirs{..} runAppState runLogger = do+upgrade uOpts force' fatal Dirs{..} (getAppState', leanAppstate) = do target <- case uOpts of UpgradeInplace -> Just <$> liftIO getExecutablePath (UpgradeAt p) -> pure $ Just p UpgradeGHCupDir -> pure (Just (binDir </> "ghcup" <> exeExt)) - runUpgrade runAppState (do+ run (do GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo- Just (tver, vi) <- pure $ getLatest dls GHCup+ Just tver <- pure $ getLatest dls ghcup let latestVer = _tvVersion tver- forM_ (_viPreInstall vi) $ \msg -> do+ let vm = getVersionMetadata tver ghcup dls+ forM_ (_vmPreInstall =<< vm) $ \msg -> do lift $ logWarn msg lift $ logWarn "...waiting for 5 seconds, you can still abort..." liftIO $ threadDelay 5000000 -- give the user a sec to intervene v' <- liftE $ upgradeGHCup' target force' fatal latestVer- pure (v', dls)+ pure (v', vm) ) >>= \case- VRight (v', dls) -> do+ (VRight (v', vm), up) -> do let pretty_v = prettyVer v'- let vi = fromJust $ snd <$> getLatest dls GHCup runLogger $ logInfo $ "Successfully upgraded GHCup to version " <> pretty_v- forM_ (_viPostInstall vi) $ \msg ->+ forM_ (_vmPostInstall =<< vm) $ \msg -> runLogger $ logInfo msg+ liftIO up pure ExitSuccess- VLeft (V NoUpdate) -> do+ (VLeft (V NoUpdate), _) -> do runLogger $ logWarn "No GHCup update available" pure ExitSuccess- VLeft e -> do+ (VLeft e, _) -> do runLogger $ logError $ T.pack $ prettyHFError e pure $ ExitFailure 11+ where+ runLogger = flip runReaderT leanAppstate+ run action' = do+ (appstate', up) <- liftIO getAppState'+ r <- flip runReaderT appstate'+ . runResourceT+ . runE+ @UpgradeEffects+ $ action'+ pure (r, up)
lib-opt/GHCup/OptParse/Whereis.hs view
@@ -1,45 +1,45 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-} {-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeApplications #-} module GHCup.OptParse.Whereis where -import GHCup-import GHCup.Errors-import GHCup.OptParse.Common-import GHCup.Types-import GHCup.Utils-import GHCup.Utils.Parsers (fromVersion)-import GHCup.Prelude.Logger-import GHCup.Prelude.String.QQ+import GHCup.Command.Whereis+import GHCup.Errors+import GHCup.Input.Parsers ( resolveVersion, toolParser )+import GHCup.OptParse.Common+import GHCup.Prelude.Logger+import GHCup.Prelude.String.QQ+import GHCup.Types #if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )+import Control.Monad.Fail ( MonadFail ) #endif-import Control.Monad.Reader-import Control.Monad.Trans.Resource-import Data.Functor-import Data.Maybe-import Data.Variant.Excepts-import Options.Applicative hiding ( style )-import Options.Applicative.Pretty.Shim ( text )-import Prelude hiding ( appendFile )-import System.Environment-import System.Exit+import Control.Monad.Reader+import Control.Monad.Trans.Resource+import Data.Functor+import Data.Maybe+import Data.Variant.Excepts+import Options.Applicative hiding ( ParseError, style )+import Options.Applicative.Pretty.Shim ( text )+import Prelude hiding ( appendFile )+import System.Environment+import System.Exit+import System.FilePath -import qualified Data.Text as T-import Control.Exception.Safe (MonadMask)-import System.FilePath (takeDirectory)-import GHCup.Types.Optics+import Control.Exception.Safe ( MonadMask )+import qualified Data.Text as T+import GHCup.Query.GHCupDirs+import GHCup.System.Directory+import GHCup.Types.Optics @@ -49,13 +49,14 @@ ---------------- -data WhereisCommand = WhereisTool Tool (Maybe ToolVersion)- | WhereisBaseDir- | WhereisBinDir- | WhereisCacheDir- | WhereisLogsDir- | WhereisConfDir- deriving (Eq, Show)+data WhereisCommand+ = WhereisTool Tool (Maybe ToolVersion)+ | WhereisBaseDir+ | WhereisBinDir+ | WhereisCacheDir+ | WhereisLogsDir+ | WhereisConfDir+ deriving (Eq, Show) @@ -66,9 +67,10 @@ --------------- -data WhereisOptions = WhereisOptions {- directory :: Bool-} deriving (Eq, Show)+data WhereisOptions = WhereisOptions+ { directory :: Bool+ }+ deriving (Eq, Show) @@ -83,40 +85,41 @@ (commandGroup "Tools locations:" <> command "ghc"- (WhereisTool GHC <$> info- ( optional (toolVersionTagArgument [] (Just GHC)) <**> helper )+ (WhereisTool ghc <$> info+ ( optional (toolVersionTagArgument [] (Just ghc)) <**> helper ) ( progDesc "Get GHC location" <> footerDoc (Just $ text whereisGHCFooter )) ) <> command "cabal"- (WhereisTool Cabal <$> info- ( optional (toolVersionTagArgument [] (Just Cabal)) <**> helper )+ (WhereisTool cabal <$> info+ ( optional (toolVersionTagArgument [] (Just cabal)) <**> helper ) ( progDesc "Get cabal location" <> footerDoc (Just $ text whereisCabalFooter )) ) <> command "hls"- (WhereisTool HLS <$> info- ( optional (toolVersionTagArgument [] (Just HLS)) <**> helper )+ (WhereisTool hls <$> info+ ( optional (toolVersionTagArgument [] (Just hls)) <**> helper ) ( progDesc "Get HLS location" <> footerDoc (Just $ text whereisHLSFooter )) ) <> command "stack"- (WhereisTool Stack <$> info- ( optional (toolVersionTagArgument [] (Just Stack)) <**> helper )+ (WhereisTool stack <$> info+ ( optional (toolVersionTagArgument [] (Just stack)) <**> helper ) ( progDesc "Get stack location" <> footerDoc (Just $ text whereisStackFooter )) ) <> command "ghcup"- (WhereisTool GHCup <$> info ( pure Nothing <**> helper ) ( progDesc "Get ghcup location" ))- ) <|> subparser ( commandGroup "Directory locations:"+ (WhereisTool ghcup <$> info ( pure Nothing <**> helper ) ( progDesc "Get ghcup location" ))+ )+ <|> subparser ( commandGroup "Directory locations:" <> command "basedir"@@ -148,6 +151,11 @@ ( progDesc "Get ghcup config directory location" ) ) )+ <|>+ (+ WhereisTool <$> argument (eitherReader toolParser) (metavar "TOOL")+ <*> optional (toolVersionTagArgument [] Nothing)+ ) where whereisGHCFooter = [s|Discussion: Finds the location of a GHC executable, which usually resides in@@ -225,32 +233,13 @@ , NextVerNotFound , TagNotFound , DayNotFound+ , ParseError+ , NoInstallInfo ] -runLeanWhereIs :: (MonadUnliftIO m, MonadIO m)- => LeanAppState- -> Excepts WhereisEffects (ReaderT LeanAppState m) a- -> m (VEither WhereisEffects a)-runLeanWhereIs leanAppstate =- -- Don't use runLeanAppState here, which is disabled on windows.- -- This is the only command on all platforms that doesn't need full appstate.- flip runReaderT leanAppstate- . runE- @WhereisEffects -runWhereIs :: (MonadUnliftIO m, MonadIO m)- => (ReaderT AppState m (VEither WhereisEffects a) -> m (VEither WhereisEffects a))- -> Excepts WhereisEffects (ReaderT AppState m) a- -> m (VEither WhereisEffects a)-runWhereIs runAppState =- runAppState- . runE- @WhereisEffects--- ------------------ --[ Entrypoint ]-- ------------------@@ -265,26 +254,24 @@ => WhereisCommand -> WhereisOptions -> Settings- -> (forall a. ReaderT AppState m (VEither WhereisEffects a) -> m (VEither WhereisEffects a))- -> LeanAppState- -> (ReaderT LeanAppState m () -> m ())+ -> (IO (AppState, IO ()), LeanAppState) -> m ExitCode-whereis whereisCommand whereisOptions settings runAppState leanAppstate runLogger = do+whereis whereisCommand whereisOptions settings (getAppState', leanAppstate) = do Dirs{ .. } <- runReaderT getDirs leanAppstate case (whereisCommand, whereisOptions) of- (WhereisTool GHCup _, WhereisOptions{..}) -> do- loc <- liftIO (getExecutablePath >>= canonicalizePath )+ (WhereisTool (Tool "ghcup") _, WhereisOptions{..}) -> do+ loc <- liftIO (getExecutablePath >>= canon ) if directory then liftIO $ putStr $ takeDirectory loc else liftIO $ putStr loc pure ExitSuccess - (WhereisTool tool (Just (GHCVersion v)), WhereisOptions{..}) ->- runLeanWhereIs leanAppstate (do+ (WhereisTool tool (Just (GHCVersion (TargetVersionReq v _))), WhereisOptions{..}) ->+ runLeanWhereIs (do loc <- liftE $ whereIsTool tool v if directory- then pure $ takeDirectory loc- else pure loc+ then takeDirectory <$> canon loc+ else canon loc ) >>= \case VRight r -> do@@ -293,12 +280,12 @@ VLeft e -> do runLogger $ logError $ T.pack $ prettyHFError e pure $ ExitFailure 30- (WhereisTool tool (Just (ToolVersion v)), WhereisOptions{..}) ->- runLeanWhereIs leanAppstate (do+ (WhereisTool tool (Just (ToolVersion (VersionReq v _))), WhereisOptions{..}) ->+ runLeanWhereIs (do loc <- liftE $ whereIsTool tool (mkTVer v) if directory- then pure $ takeDirectory loc- else pure loc+ then takeDirectory <$> canon loc+ else canon loc ) >>= \case VRight r -> do@@ -309,12 +296,12 @@ pure $ ExitFailure 30 (WhereisTool tool whereVer, WhereisOptions{..}) -> do- runWhereIs runAppState (do- (v, _) <- liftE $ fromVersion whereVer guessMode tool+ runWhereIs (do+ (TargetVersionReq v _) <- liftE $ resolveVersion whereVer guessMode tool loc <- liftE $ whereIsTool tool v if directory- then pure $ takeDirectory loc- else pure loc+ then takeDirectory <$> canon loc+ else canon loc ) >>= \case VRight r -> do@@ -325,23 +312,44 @@ pure $ ExitFailure 30 (WhereisBaseDir, _) -> do- liftIO $ putStr $ fromGHCupPath baseDir+ liftIO $ putStr =<< canon (fromGHCupPath baseDir) pure ExitSuccess (WhereisBinDir, _) -> do- liftIO $ putStr binDir+ liftIO $ putStr =<< canon binDir pure ExitSuccess (WhereisCacheDir, _) -> do- liftIO $ putStr $ fromGHCupPath cacheDir+ liftIO $ putStr =<< canon (fromGHCupPath cacheDir) pure ExitSuccess (WhereisLogsDir, _) -> do- liftIO $ putStr $ fromGHCupPath logsDir+ liftIO $ putStr =<< canon (fromGHCupPath logsDir) pure ExitSuccess (WhereisConfDir, _) -> do- liftIO $ putStr $ fromGHCupPath confDir+ liftIO $ putStr =<< canon (fromGHCupPath confDir) pure ExitSuccess where+ runLogger = flip runReaderT leanAppstate++ -- make sure we only have forward slashes on windows+ canon fp = do+ cfp <- liftIO $ canonicalizePath fp+ pure $ map (\c -> if isPathSeparator c then '/' else c) cfp+ guessMode = if guessVersion settings then GLaxWithInstalled else GStrict+ runLeanWhereIs =+ -- Don't use runLeanAppState here, which is disabled on windows.+ -- This is the only command on all platforms that doesn't need full appstate.+ flip runReaderT leanAppstate+ . runE+ @WhereisEffects++ runWhereIs action' = do+ (appstate', _) <- liftIO getAppState'+ flip runReaderT appstate'+ . runResourceT+ . runE+ @WhereisEffects+ $ action'
lib-tui/GHCup/Brick/Actions.hs view
@@ -1,88 +1,97 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeApplications #-} {-# OPTIONS_GHC -Wno-unused-record-wildcards #-} {-# OPTIONS_GHC -Wno-unused-matches #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ViewPatterns #-} module GHCup.Brick.Actions where -import GHCup-import GHCup.CabalConfig-import GHCup.Download-import GHCup.Errors-import GHCup.Types.Optics ( getDirs, getPlatformReq, HasLog )-import GHCup.Types hiding ( LeanAppState(..) )-import GHCup.Utils-import GHCup.Prelude ( decUTF8Safe, runBothE' )-import GHCup.Prelude.Logger-import GHCup.Prelude.Process-import GHCup.Prompts-import GHCup.Brick.Common (BrickData(..), BrickSettings(..), Name(..), Mode(..))-import qualified GHCup.Brick.Common as Common-import GHCup.Brick.BrickState-import GHCup.Brick.Widgets.SectionList-import qualified GHCup.Brick.Widgets.Menus.Context as ContextMenu-import GHCup.Brick.Widgets.Navigation (BrickInternalState)-import qualified GHCup.Brick.Widgets.Menus.AdvanceInstall as AdvanceInstall-import qualified GHCup.Brick.Widgets.Menus.CompileGHC as CompileGHC-import GHCup.Brick.Widgets.Menu (MenuKeyBindings(..))+import GHCup.CabalConfig+import GHCup.Command.Install+import GHCup.Command.List+import GHCup.Command.Rm+import GHCup.Command.Set+import GHCup.Command.Upgrade+import GHCup.Download+import GHCup.Errors+import GHCup.Input.Prompts+import GHCup.Prelude+import GHCup.Prelude.Process+import GHCup.Query.GHCupDirs+import GHCup.Query.Metadata+import GHCup.System.Directory+import GHCup.Types hiding ( LeanAppState (..) )+import GHCup.Types.Optics ( HasLog, getDirs, getPlatformReq ) +import qualified GHCup.Command.Compile.GHC as GHC+import qualified GHCup.Command.Compile.HLS as HLS+import qualified GHCup.Input.Parsers as Utils++import GHCup.Brick.BrickState+import GHCup.Brick.Common+ ( BrickData (..), BrickSettings (..), Mode (..), Name (..) )+import GHCup.Brick.Widgets.Menu ( MenuKeyBindings (..) )+import GHCup.Brick.Widgets.Navigation ( BrickInternalState )++import qualified GHCup.Brick.Common as Common+import qualified GHCup.Brick.Widgets.Menus.AdvancedInstall as AdvancedInstall+import qualified GHCup.Brick.Widgets.Menus.CompileGHC as CompileGHC+import qualified GHCup.Brick.Widgets.Menus.CompileHLS as CompileHLS+import qualified GHCup.Brick.Widgets.Menus.Context as ContextMenu+ import qualified Brick-import qualified Brick.Widgets.List as L-import qualified Brick.Focus as F+import qualified Brick.Widgets.List as L import Control.Applicative import Control.Exception.Safe-import Control.Monad (when, forM, forM_)+import Control.Monad ( forM, forM_, when ) #if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )+import Control.Monad.Fail ( MonadFail ) #endif-import Control.Monad.Reader-import Control.Monad.Trans.Except-import Control.Monad.Trans.Resource-import Data.Bool-import Data.Functor-import Data.Function ( (&), on)-import Data.List-import Data.Maybe-import Data.IORef (IORef, readIORef, newIORef, modifyIORef)-import Data.Versions hiding (Lens')-import Data.Variant.Excepts-import Prelude hiding ( appendFile )-import System.Exit-import System.IO.Unsafe-import System.Process ( system )-import Text.PrettyPrint.HughesPJClass ( prettyShow )-import URI.ByteString+import Control.Monad.Reader+import Control.Monad.Trans.Except+import Control.Monad.Trans.Resource+import Data.Bool+import Data.Function ( (&) )+import Data.Functor+import qualified Data.Map.Strict as M+import Data.IORef+ ( IORef, modifyIORef, newIORef, readIORef )+import Data.List+import Data.Maybe+import Data.Variant.Excepts+import Data.Versions hiding ( Lens' )+import Prelude hiding ( appendFile )+import System.Exit+import System.IO.Unsafe+import System.Process ( system )+import Text.PrettyPrint.HughesPJClass ( prettyShow )+import URI.ByteString -import qualified Data.Text as T-import qualified Data.Text.Lazy.Builder as B-import qualified Data.Text.Lazy as L-import qualified Graphics.Vty as Vty-import qualified Data.Vector as V-import System.Environment (getExecutablePath)+import qualified Data.Text as T+import qualified Data.Text.Lazy as L+import qualified Data.Text.Lazy.Builder as B+import qualified Data.Vector as V+import qualified Graphics.Vty as Vty+import System.Environment ( getExecutablePath ) #if !IS_WINDOWS-import GHCup.Prelude.File-import qualified System.Posix.Process as SPP+import qualified System.Posix.Process as SPP #endif -import System.FilePath+import System.FilePath -import Optics.State (use)-import Optics.State.Operators ( (.=))-import Optics.Operators ((.~),(%~))-import Optics.Getter (view)-import Optics.Optic ((%))-import Optics ((^.), to)-import qualified GHCup.Brick.Widgets.Menus.CompileHLS as CompileHLS-import Control.Concurrent (threadDelay)-import qualified GHCup.GHC as GHC-import qualified GHCup.Utils.Parsers as Utils-import qualified GHCup.HLS as HLS+import Control.Concurrent ( threadDelay )+import Optics ( to, (^.) )+import Optics.Getter ( view )+import Optics.Operators ( (.~) )+import Optics.Optic ( (%) )+import Optics.State ( use )+import Optics.State.Operators ( (.=), (%=) ) @@ -118,37 +127,41 @@ -- | Focus on the tool section and the predicate which matches. If no result matches, focus on index 0 selectBy :: Tool -> (ListResult -> Bool) -> BrickInternalState -> BrickInternalState selectBy tool predicate internal_state =- let new_focus = F.focusSetCurrent (Singular tool) (view sectionListFocusRingL internal_state)- tool_lens = sectionL (Singular tool)- in internal_state- & sectionListFocusRingL .~ new_focus- & tool_lens %~ L.listMoveTo 0 -- We move to 0 first- & tool_lens %~ L.listFindBy predicate -- The lookup by the predicate.+ L.listModify+ (\(t, (td, vlr)) -> (t, (td, maybe vlr (`L.listMoveToElement` vlr) . V.find predicate $ L.listElements vlr)))+ (L.listFindBy (\(t, _) -> tool == t) internal_state) --- | Select the latests GHC tool+-- | Select the latest GHC tool selectLatest :: BrickInternalState -> BrickInternalState-selectLatest = selectBy GHC (elem Latest . lTag)-+selectLatest = selectBy ghc (elem Latest . lTag) -- | Replace the @appState@ or construct it based on a filter function -- and a new @[ListResult]@ evidence. -- When passed an existing @appState@, tries to keep the selected element. replaceLR :: (ListResult -> Bool)- -> [ListResult]+ -> ToolListResult -> Maybe BrickInternalState -> BrickInternalState replaceLR filterF list_result s =- let oldElem = s >>= sectionListSelectedElement -- Maybe (Int, e)- newVec = [(Singular $ lTool (head g), V.fromList g) | g <- groupBy ((==) `on` lTool ) (filter filterF list_result)]- newSectionList = sectionList AllTools newVec 1+ let oldElem = s >>= L.listSelectedElement -- Maybe (Int, e) in case oldElem of- Just (_, el) -> selectBy (lTool el) (toolEqual el) newSectionList- Nothing -> selectLatest newSectionList+ Just (_, (tool, (toolDesc, elr))) -> do+ case L.listSelectedElement elr of+ Just (_, lr) ->+ selectBy tool (\lr' -> lVer lr == lVer lr' && lCross lr == lCross lr') newList+ Nothing -> selectBy tool (elem Latest . lTag) newList+ Nothing ->+ let bis' = foldl' (\bis tool -> selectBy tool (elem Latest . lTag) bis) newList (M.keys list_result)+ in L.listFindBy (\(t, _) -> ghc == t) bis' where- toolEqual e1 e2 =- lTool e1 == lTool e2 && lVer e1 == lVer e2 && lCross e1 == lCross e2+ newList :: BrickInternalState+ newList =+ L.list+ AllTools+ (V.fromList $ fmap (\(tool, (td, filter filterF -> lr)) -> (tool, (td, L.list (Singular tool) (V.fromList lr) 1))) $ M.toList list_result) 1 + filterVisible :: Bool -> ListResult -> Bool filterVisible v e | lInstalled e = True | v@@ -157,45 +170,70 @@ , Old `notElem` lTag e , Nightly `notElem` lTag e = True | otherwise = (Old `notElem` lTag e) &&- (Nightly `notElem` lTag e)+ (Nightly `notElem` lTag e) -- | Suspend the current UI and run an IO action in terminal. If the -- IO action returns a Left value, then it's thrown as userError. withIOAction :: (Ord n, Eq n)- => ( (Int, ListResult) -> ReaderT AppState IO (Either String a))+ => ( (Int, Tool, Maybe ToolDescription, ListResult) -> ReaderT AppState IO (Either String a)) -> Brick.EventM n BrickState () withIOAction action = do as <- Brick.get- case sectionListSelectedElement (view appState as) of+ case L.listSelectedElement (view appState as) of Nothing -> pure ()- Just (curr_ix, e) -> do- Brick.suspendAndResume $ do- settings <- readIORef settings'- flip runReaderT settings $ action (curr_ix, e) >>= \case- Left err -> liftIO $ putStrLn ("Error: " <> err)- Right _ -> liftIO $ putStrLn "Success"- getAppData Nothing >>= \case- Right data' -> do- putStrLn "Press enter to continue"- _ <- getLine- pure (updateList data' as)- Left err -> throwIO $ userError err+ Just (curr_ix, (tool, (td, vlr))) -> case L.listSelectedElement vlr of+ Just (curr_ix', lr) ->+ Brick.suspendAndResume $ do+ settings <- readIORef settings'+ flip runReaderT settings $ action (curr_ix', tool, td, lr) >>= \case+ Left err -> liftIO $ putStrLn ("Error: " <> err)+ Right _ -> liftIO $ putStrLn "Success"+ getAppData Nothing >>= \case+ Right data' -> do+ putStrLn "Press enter to continue"+ _ <- getLine+ pure (updateList data' as)+ Left err -> throwIO $ userError err+ Nothing -> pure () -installWithOptions :: (MonadReader AppState m, MonadIO m, MonadThrow m, MonadFail m, MonadMask m, MonadUnliftIO m, Alternative m)- => AdvanceInstall.InstallOptions- -> (Int, ListResult)+withIOActionRecommended :: (Ord n, Eq n)+ => ( (Int, Tool, Maybe ToolDescription, ListResult) -> ReaderT AppState IO (Either String a))+ -> Brick.EventM n BrickState ()+withIOActionRecommended action = do+ as <- Brick.get+ case L.listSelectedElement (view appState as) of+ Nothing -> pure ()+ Just (curr_ix, (tool, (td, vlr))) -> do+ let mlr = V.find (\ListResult{..} -> Recommended `elem` lTag) $ L.listElements vlr+ case mlr of+ Just lr ->+ Brick.suspendAndResume $ do+ settings <- readIORef settings'+ flip runReaderT settings $ action (curr_ix, tool, td, lr) >>= \case+ Left err -> liftIO $ putStrLn ("Error: " <> err)+ Right _ -> liftIO $ putStrLn "Success"+ getAppData Nothing >>= \case+ Right data' -> do+ putStrLn "Press enter to continue"+ _ <- getLine+ pure (updateList data' as)+ Left err -> throwIO $ userError err+ Nothing -> pure ()++installWithOptions :: (MonadReader AppState m, MonadIO m, MonadThrow m, MonadFail m, MonadMask m, MonadUnliftIO m)+ => AdvancedInstall.InstallOptions+ -> (Int, Tool, Maybe ToolDescription, ListResult) -> m (Either String ())-installWithOptions opts (_, ListResult {..}) = do+installWithOptions opts (_ix, lTool, td, ListResult {..}) = do AppState { ghcupInfo = GHCupInfo { _ghcupDownloads = dls }} <- ask let- misolated = opts ^. AdvanceInstall.isolateDirL- shouldIsolate = maybe GHCupInternal IsolateDir (opts ^. AdvanceInstall.isolateDirL)- shouldForce = opts ^. AdvanceInstall.forceInstallL- shouldSet = opts ^. AdvanceInstall.instSetL- extraArgs = opts ^. AdvanceInstall.addConfArgsL- installTargets = opts ^. AdvanceInstall.installTargetsL- v = fromMaybe (GHCTargetVersion lCross lVer) (opts ^. AdvanceInstall.instVersionL)- toolV = _tvVersion v+ misolated = opts ^. AdvancedInstall.isolateDirL+ shouldIsolate = maybe GHCupInternal IsolateDir (opts ^. AdvancedInstall.isolateDirL)+ shouldForce = opts ^. AdvancedInstall.forceInstallL+ shouldSet = opts ^. AdvancedInstall.instSetL+ extraArgs = opts ^. AdvancedInstall.addConfArgsL+ installTargets = opts ^. AdvancedInstall.installTargetsL+ v = fromMaybe (TargetVersionReq (TargetVersion lCross lVer) (Just (fst lRev))) (opts ^. AdvancedInstall.instVersionL) let run = runResourceT . runE@@ -227,6 +265,8 @@ , NoCompatibleArch , InstallSetError , URIParseError+ , NoInstallInfo+ , MalformedInstallInfo ] withNoVerify :: (MonadReader AppState m) => m a -> m a@@ -237,110 +277,52 @@ try @_ @SomeException $ getExecutablePath >>= canonicalizePath dirs <- lift getDirs case lTool of- GHC -> do- let vi = getVersionInfo v GHC dls- forM_ (_viPreInstall =<< vi) $ \msg -> do- lift $ logWarn msg- lift $ logWarn- "...waiting for 5 seconds, you can still abort..."- liftIO $ threadDelay 5000000 -- give the user a sec to intervene- case opts ^. AdvanceInstall.instBindistL of- Nothing -> do- liftE $- runBothE'- (installGHCBin v shouldIsolate shouldForce extraArgs installTargets)- (when (shouldSet && isNothing misolated) (liftE $ void $ setGHC v SetGHCOnly Nothing))- pure (vi, dirs, ce)- Just uri -> do- liftE $- runBothE'- (withNoVerify $ installGHCBindist- (DownloadInfo ((decUTF8Safe . serializeURIRef') uri) (Just $ RegexDir "ghc-.*") "" Nothing Nothing Nothing)- v- shouldIsolate- shouldForce- extraArgs- installTargets- )- (when (shouldSet && isNothing misolated) (liftE $ void $ setGHC v SetGHCOnly Nothing))- pure (vi, dirs, ce)-- Cabal -> do- let vi = getVersionInfo v Cabal dls- forM_ (_viPreInstall =<< vi) $ \msg -> do- lift $ logWarn msg- lift $ logWarn- "...waiting for 5 seconds, you can still abort..."- liftIO $ threadDelay 5000000 -- give the user a sec to intervene- case opts ^. AdvanceInstall.instBindistL of- Nothing -> do- liftE $- runBothE'- (installCabalBin toolV shouldIsolate shouldForce)- (when (shouldSet && isNothing misolated) (liftE $ void $ setCabal toolV))- pure (vi, dirs, ce)- Just uri -> do- liftE $- runBothE'- (withNoVerify $ installCabalBindist (DownloadInfo ((decUTF8Safe . serializeURIRef') uri) Nothing "" Nothing Nothing Nothing) toolV shouldIsolate shouldForce)- (when (shouldSet && isNothing misolated) (liftE $ void $ setCabal toolV))- pure (vi, dirs, ce)-- GHCup -> do- let vi = snd <$> getLatest dls GHCup- forM_ (_viPreInstall =<< vi) $ \msg -> do+ Tool "ghcup" -> do+ let vm = getLatest dls lTool >>= \t -> getVersionMetadata t lTool dls+ forM_ (_vmPreInstall =<< vm) $ \msg -> do lift $ logWarn msg lift $ logWarn "...waiting for 5 seconds, you can still abort..." liftIO $ threadDelay 5000000 -- give the user a sec to intervene- liftE $ upgradeGHCup Nothing False False $> (vi, dirs, ce)- HLS -> do- let vi = getVersionInfo v HLS dls- forM_ (_viPreInstall =<< vi) $ \msg -> do+ liftE $ upgradeGHCup Nothing False False $> (vm, dirs, ce)+ _ -> do+ let vm = getLatest dls lTool >>= \t -> getVersionMetadata t lTool dls+ forM_ (_vmPreInstall =<< vm) $ \msg -> do lift $ logWarn msg lift $ logWarn "...waiting for 5 seconds, you can still abort..." liftIO $ threadDelay 5000000 -- give the user a sec to intervene- case opts ^. AdvanceInstall.instBindistL of- Nothing -> do- liftE $- runBothE'- (installHLSBin toolV shouldIsolate shouldForce)- (when (shouldSet && isNothing misolated) (liftE $ void $ setHLS toolV SetHLSOnly Nothing))- pure (vi, dirs, ce)- Just uri -> do- liftE $- runBothE'- (withNoVerify $ installHLSBindist- (DownloadInfo ((decUTF8Safe . serializeURIRef') uri) (if isWindows then Nothing else Just (RegexDir "haskell-language-server-*")) "" Nothing Nothing Nothing)- toolV- shouldIsolate- shouldForce)- (when (shouldSet && isNothing misolated) (liftE $ void $ setHLS toolV SetHLSOnly Nothing))- pure (vi, dirs, ce)-- Stack -> do- let vi = getVersionInfo v Stack dls- case opts ^. AdvanceInstall.instBindistL of+ case opts ^. AdvancedInstall.instBindistL of Nothing -> do liftE $ runBothE'- (installStackBin toolV shouldIsolate shouldForce)- (when (shouldSet && isNothing misolated) (liftE $ void $ setStack toolV))- pure (vi, dirs, ce)+ (installTool lTool v shouldIsolate shouldForce (T.unpack <$> extraArgs) (words . T.unpack <$> installTargets))+ (when (shouldSet && isNothing misolated) (liftE $ void $ setToolVersion lTool (_tvqTargetVer v)))+ pure (vm, dirs, ce) Just uri -> do liftE $ runBothE'- (withNoVerify $ installStackBindist (DownloadInfo ((decUTF8Safe . serializeURIRef') uri) Nothing "" Nothing Nothing Nothing) toolV shouldIsolate shouldForce)- (when (shouldSet && isNothing misolated) (liftE $ void $ setStack toolV))- pure (vi, dirs, ce)+ (do+ rev <- liftE $ maybe (fmap fst $ getDownloadInfoE' lTool v) pure (_tvqRev v)+ withNoVerify $ installBindist+ lTool+ td+ (DownloadInfo ((decUTF8Safe . serializeURIRef') uri) (Just $ RegexDir "ghc-.*") "" Nothing Nothing Nothing Nothing)+ (TargetVersionRev (_tvqTargetVer v) rev)+ shouldIsolate+ shouldForce+ (T.unpack <$> extraArgs)+ (words . T.unpack <$> installTargets)+ )+ (when (shouldSet && isNothing misolated) (liftE $ void $ setToolVersion lTool (_tvqTargetVer v)))+ pure (vm, dirs, ce) ) >>= \case- VRight (vi, Dirs{..}, Just ce) -> do- forM_ (_viPostInstall =<< vi) $ \msg -> logInfo msg+ VRight (vm, Dirs{..}, Just ce) -> do+ forM_ (_vmPostInstall =<< vm) $ \msg -> logInfo msg case lTool of- GHCup -> do+ Tool "ghcup" -> do #if !IS_WINDOWS up <- liftIO $ fmap (either (const Nothing) Just) $ try @_ @SomeException $ canonicalizePath (binDir </> "ghcup" <.> exeExt)@@ -352,23 +334,23 @@ #endif _ -> pure () pure $ Right ()- VRight (vi, _, _) -> do- forM_ (_viPostInstall =<< vi) $ \msg -> logInfo msg+ VRight (vm, _, _) -> do+ forM_ (_vmPostInstall =<< vm) $ \msg -> logInfo msg logInfo "Please restart 'ghcup' for the changes to take effect" pure $ Right () VLeft (V (AlreadyInstalled _ _)) -> pure $ Right () VLeft (V NoUpdate) -> pure $ Right () VLeft e -> pure $ Left $ prettyHFError e <> "\n"- <> "Also check the logs in ~/.ghcup/logs"+ <> "Also check the logs in ~/.ghcup/logs or $XDG_STATE_HOME/ghcup/logs" -install' :: (MonadReader AppState m, MonadIO m, MonadThrow m, MonadFail m, MonadMask m, MonadUnliftIO m, Alternative m)- => (Int, ListResult) -> m (Either String ())-install' = installWithOptions (AdvanceInstall.InstallOptions Nothing False Nothing Nothing False [] "install")+install' :: (MonadReader AppState m, MonadIO m, MonadThrow m, MonadFail m, MonadMask m, MonadUnliftIO m)+ => (Int, Tool, Maybe ToolDescription, ListResult) -> m (Either String ())+install' = installWithOptions (AdvancedInstall.InstallOptions Nothing False Nothing Nothing False [] Nothing) -set' :: (MonadReader AppState m, MonadIO m, MonadThrow m, MonadFail m, MonadMask m, MonadUnliftIO m, Alternative m)- => (Int, ListResult)+set' :: (MonadReader AppState m, MonadIO m, MonadThrow m, MonadFail m, MonadMask m, MonadUnliftIO m)+ => (Int, Tool, Maybe ToolDescription, ListResult) -> m (Either String ())-set' input@(_, ListResult {..}) = do+set' input@(_, lTool, _, ListResult {..}) = do settings <- liftIO $ readIORef settings' let run =@@ -406,11 +388,7 @@ run (do case lTool of- GHC -> liftE $ setGHC (GHCTargetVersion lCross lVer) SetGHCOnly Nothing $> ()- Cabal -> liftE $ setCabal lVer $> ()- HLS -> liftE $ setHLS lVer SetHLSOnly Nothing $> ()- Stack -> liftE $ setStack lVer $> ()- GHCup -> do+ Tool "ghcup" -> do promptAnswer <- getUserPromptResponse "Switching GHCup versions is not supported.\nDo you want to install the latest version? [Y/n]: " PromptYes@@ -418,6 +396,7 @@ PromptYes -> do void $ liftE $ upgradeGHCup Nothing False False PromptNo -> pure ()+ _ -> liftE $ setToolVersion lTool (TargetVersion lCross lVer) $> () ) >>= \case VRight _ -> pure $ Right ()@@ -443,7 +422,7 @@ _ -> pure $ Left (prettyHFError e) -logGHCPostRm :: (MonadReader env m, HasLog env, MonadIO m) => GHCTargetVersion -> m ()+logGHCPostRm :: (MonadReader env m, HasLog env, MonadIO m) => TargetVersion -> m () logGHCPostRm ghcVer = do cabalStore <- liftIO $ handleIO (\_ -> if isWindows then pure "C:\\cabal\\store" else pure "~/.cabal/store or ~/.local/state/cabal/store") getStoreDir@@ -452,40 +431,37 @@ del' :: (MonadReader AppState m, MonadIO m, MonadFail m, MonadMask m, MonadUnliftIO m)- => (Int, ListResult)+ => (Int, Tool, Maybe ToolDescription, ListResult) -> m (Either String ())-del' (_, ListResult {..}) = do+del' (_, lTool, _, ListResult {..}) = do AppState { ghcupInfo = GHCupInfo { _ghcupDownloads = dls }} <- ask - let run = runE @'[NotInstalled, UninstallFailed]+ let run = runE @'[NotInstalled, UninstallFailed, ParseError, MalformedInstallInfo] run (do- let vi = getVersionInfo crossVer lTool dls+ let vi = getVersionMetadata crossVer lTool dls case lTool of- GHC -> liftE $ rmGHCVer crossVer $> vi- Cabal -> liftE $ rmCabalVer lVer $> vi- HLS -> liftE $ rmHLSVer lVer $> vi- Stack -> liftE $ rmStackVer lVer $> vi- GHCup -> pure Nothing+ Tool "ghcup" -> pure Nothing+ _ -> liftE $ rmToolVersion lTool crossVer $> vi ) >>= \case VRight vi -> do- when (lTool == GHC) $ logGHCPostRm crossVer- logInfo $ "Successfuly removed " <> T.pack (prettyShow lTool) <> " " <> (if lTool == GHC then tVerToText crossVer else prettyVer lVer)- forM_ (_viPostRemove =<< vi) $ \msg ->+ when (lTool == ghc) $ logGHCPostRm crossVer+ logInfo $ "Successfully removed " <> T.pack (prettyShow lTool) <> " " <> (if lTool == ghc then tVerToText crossVer else prettyVer lVer)+ forM_ (_vmPostRemove =<< vi) $ \msg -> logInfo msg pure $ Right () VLeft e -> pure $ Left (prettyHFError e) where- crossVer = GHCTargetVersion lCross lVer+ crossVer = TargetVersion lCross lVer changelog' :: (MonadReader AppState m, MonadIO m)- => (Int, ListResult)+ => (Int, Tool, Maybe ToolDescription, ListResult) -> m (Either String ())-changelog' (_, ListResult {..}) = do+changelog' (_, lTool, _, ListResult {..}) = do AppState { pfreq, ghcupInfo = GHCupInfo { _ghcupDownloads = dls }} <- ask- case getChangeLog dls lTool (ToolVersion lVer) of+ case getChangeLog dls lTool (mkTVer lVer) of Nothing -> pure $ Left $ "Could not find ChangeLog for " <> prettyShow lTool <> ", version " <> T.unpack (prettyVer lVer) Just uri -> do@@ -495,8 +471,8 @@ FreeBSD -> exec "xdg-open" [T.unpack $ decUTF8Safe $ serializeURIRef' uri] Nothing Nothing OpenBSD -> exec "xdg-open" [T.unpack $ decUTF8Safe $ serializeURIRef' uri] Nothing Nothing Windows -> do- let args = "start \"\" " ++ (T.unpack $ decUTF8Safe $ serializeURIRef' uri)- c <- liftIO $ system $ args+ let args = "start \"\" " ++ T.unpack (decUTF8Safe $ serializeURIRef' uri)+ c <- liftIO $ system args case c of (ExitFailure xi) -> pure $ Left $ NonZeroExit xi "cmd.exe" [args] ExitSuccess -> pure $ Right ()@@ -505,9 +481,9 @@ Right _ -> pure $ Right () Left e -> pure $ Left $ prettyHFError e -compileGHC :: (MonadReader AppState m, MonadIO m, MonadThrow m, MonadFail m, MonadMask m, MonadUnliftIO m, Alternative m)- => CompileGHC.CompileGHCOptions -> (Int, ListResult) -> m (Either String ())-compileGHC compopts (_, lr@ListResult{lTool = GHC, ..}) = do+compileGHC :: (MonadReader AppState m, MonadIO m, MonadThrow m, MonadFail m, MonadMask m, MonadUnliftIO m)+ => CompileGHC.CompileGHCOptions -> (Int, Tool, Maybe ToolDescription, ListResult) -> m (Either String ())+compileGHC compopts (_, Tool "ghc", _, lr@ListResult{..}) = do appstate <- ask let run = runResourceT@@ -535,6 +511,10 @@ , UninstallFailed , MergeFileTreeError , URIParseError+ , ParseError+ , FileAlreadyExistsError+ , NoInstallInfo+ , MalformedInstallInfo ] compileResult <- run (do AppState { ghcupInfo = GHCupInfo { _ghcupDownloads = dls }} <- ask@@ -542,20 +522,20 @@ Just ref -> pure (GHC.GitDist (GitBranch ref Nothing)) Nothing -> do -- Compile the version user is pointing to in the tui- let vi = getVersionInfo (mkTVer lVer) GHC dls- forM_ (_viPreInstall =<< vi) $ \msg -> do+ let vi = getVersionMetadata (mkTVer lVer) ghc dls+ forM_ (_vmPreInstall =<< vi) $ \msg -> do lift $ logWarn msg lift $ logWarn "...waiting for 5 seconds, you can still abort..." liftIO $ threadDelay 5000000 -- give the user a sec to intervene- forM_ (_viPreCompile =<< vi) $ \msg -> do+ forM_ (_vmPreCompile =<< vi) $ \msg -> do logInfo msg logInfo "...waiting for 5 seconds, you can still abort..." liftIO $ threadDelay 5000000 -- for compilation, give the user a sec to intervene pure (GHC.SourceDist lVer) - targetVer <- liftE $ GHCup.compileGHC+ targetVer <- liftE $ GHC.compileGHC ghcVer (compopts ^. CompileGHC.crossTarget) (compopts ^. CompileGHC.overwriteVer)@@ -564,27 +544,27 @@ (compopts ^. CompileGHC.jobs) (compopts ^. CompileGHC.buildConfig) (compopts ^. CompileGHC.patches)- (compopts ^. CompileGHC.addConfArgs)+ (fmap T.unpack $ compopts ^. CompileGHC.addConfArgs) (compopts ^. CompileGHC.buildFlavour) (compopts ^. CompileGHC.buildSystem) (maybe GHCupInternal IsolateDir $ compopts ^. CompileGHC.isolateDir)- (compopts ^. CompileGHC.installTargets)+ (fmap (words . T.unpack) $ compopts ^. CompileGHC.installTargets) AppState { ghcupInfo = GHCupInfo { _ghcupDownloads = dls2 }} <- ask- let vi2 = getVersionInfo targetVer GHC dls2+ let vi2 = getVersionMetadata targetVer ghc dls2 when (compopts ^. CompileGHC.setCompile)- (liftE . void $ GHCup.setGHC targetVer SetGHCOnly Nothing)+ (liftE . void $ setToolVersion ghc targetVer) pure (vi2, targetVer) ) case compileResult of VRight (vi, tv) -> do logInfo "GHC successfully compiled and installed"- forM_ (_viPostInstall =<< vi) $ \msg -> logInfo msg+ forM_ (_vmPostInstall =<< vi) $ \msg -> logInfo msg liftIO $ putStr (T.unpack $ tVerToText tv) pure $ Right () VLeft (V (AlreadyInstalled _ v)) -> do pure $ Left $- "GHC ver " <> T.unpack (prettyVer v) <> " already installed, remove it first to reinstall"+ "GHC ver " <> prettyShow v <> " already installed, remove it first to reinstall" VLeft (V (DirNotEmpty fp)) -> do pure $ Left $ "Install directory " <> fp <> " is not empty."@@ -592,20 +572,19 @@ case keepDirs (appstate & settings) of Never -> prettyHFError err _ -> prettyHFError err <> "\n"- <> "Check the logs at " <> (fromGHCupPath $ appstate & dirs & logsDir)+ <> "Check the logs at " <> fromGHCupPath (appstate & dirs & logsDir) <> " and the build directory " <> tmpdir <> " for more clues." <> "\n" <> "Make sure to clean up " <> tmpdir <> " afterwards." VLeft e -> do pure $ Left $ prettyHFError e -- This is the case when the tool is not GHC... which should be impossible but,--- it exhaustes pattern matches-compileGHC _ (_, ListResult{lTool = _}) = pure (Right ())-+-- it exhausts pattern matches+compileGHC _ (_, _, _, _) = pure (Right ()) -compileHLS :: (MonadReader AppState m, MonadIO m, MonadThrow m, MonadFail m, MonadMask m, MonadUnliftIO m, Alternative m)- => CompileHLS.CompileHLSOptions -> (Int, ListResult) -> m (Either String ())-compileHLS compopts (_, lr@ListResult{lTool = HLS, ..}) = do+compileHLS :: (MonadReader AppState m, MonadIO m, MonadThrow m, MonadFail m, MonadMask m, MonadUnliftIO m)+ => CompileHLS.CompileHLSOptions -> (Int, Tool, Maybe ToolDescription, ListResult) -> m (Either String ())+compileHLS compopts (_, Tool "hls", _, lr@ListResult{..}) = do appstate <- ask let run = runResourceT@@ -631,6 +610,7 @@ , UninstallFailed , MergeFileTreeError , URIParseError+ , ParseError ] compileResult <- run (do AppState { ghcupInfo = GHCupInfo { _ghcupDownloads = dls }} <- ask@@ -638,23 +618,26 @@ Just ref -> pure (HLS.GitDist (GitBranch ref Nothing)) Nothing -> do -- Compile the version user is pointing to in the tui- let vi = getVersionInfo (mkTVer lVer) HLS dls- forM_ (_viPreInstall =<< vi) $ \msg -> do+ let vi = getVersionMetadata (mkTVer lVer) hls dls+ forM_ (_vmPreInstall =<< vi) $ \msg -> do lift $ logWarn msg lift $ logWarn "...waiting for 5 seconds, you can still abort..." liftIO $ threadDelay 5000000 -- give the user a sec to intervene- forM_ (_viPreCompile =<< vi) $ \msg -> do+ forM_ (_vmPreCompile =<< vi) $ \msg -> do logInfo msg logInfo "...waiting for 5 seconds, you can still abort..." liftIO $ threadDelay 5000000 -- for compilation, give the user a sec to intervene pure (HLS.SourceDist lVer) - ghcs <-+ ghcs' <- liftE $ forM (compopts ^. CompileHLS.targetGHCs)- (\ghc -> fmap (_tvVersion . fst) . Utils.fromVersion (Just ghc) GStrict $ GHC)- targetVer <- liftE $ GHCup.compileHLS+ (\ghc' -> Utils.resolveVersion (Just ghc') GStrict ghc)+ ghcs <- forM ghcs' $ \TargetVersionReq{..} -> do+ when (isJust $ _tvTarget _tvqTargetVer) $ fail "Cannot compile HLS for a cross GHC"+ pure (_tvVersion _tvqTargetVer)+ targetVer <- liftE $ HLS.compileHLS hlsVer ghcs (compopts ^. CompileHLS.jobs)@@ -666,23 +649,23 @@ (compopts ^. CompileHLS.patches) (compopts ^. CompileHLS.cabalArgs) AppState { ghcupInfo = GHCupInfo { _ghcupDownloads = dls2 }} <- ask- let vi2 = getVersionInfo (mkTVer targetVer) GHC dls2+ let vi2 = getVersionMetadata (mkTVer targetVer) ghc dls2 when (compopts ^. CompileHLS.setCompile)- (liftE . void $ GHCup.setHLS targetVer SetHLSOnly Nothing)+ (liftE . void $ setToolVersion hls (mkTVer targetVer)) pure (vi2, targetVer) ) case compileResult of VRight (vi, tv) -> do logInfo "HLS successfully compiled and installed"- forM_ (_viPostInstall =<< vi) $ \msg -> logInfo msg+ forM_ (_vmPostInstall =<< vi) $ \msg -> logInfo msg liftIO $ putStr (T.unpack $ prettyVer tv) pure $ Right () VLeft err@(V (BuildFailed tmpdir _)) -> pure $ Left $ case keepDirs (appstate & settings) of Never -> prettyHFError err _ -> prettyHFError err <> "\n"- <> "Check the logs at " <> (fromGHCupPath $ appstate & dirs & logsDir)+ <> "Check the logs at " <> fromGHCupPath (appstate & dirs & logsDir) <> " and the build directory " <> tmpdir <> " for more clues." <> "\n" <> "Make sure to clean up " <> tmpdir <> " afterwards."@@ -690,14 +673,14 @@ pure $ Left $ prettyHFError e -- This is the case when the tool is not HLS... which should be impossible but, -- it exhaustes pattern matches-compileHLS _ (_, ListResult{lTool = _}) = pure (Right ())+compileHLS _ (_, _, _, _) = pure (Right ()) settings' :: IORef AppState {-# NOINLINE settings' #-} settings' = unsafePerformIO $ do dirs <- getAllDirs- let loggerConfig = LoggerConfig { lcPrintDebug = False+ let loggerConfig = LoggerConfig { lcPrintDebugLvl = Nothing , consoleOutter = \_ -> pure () , fileOutter = \_ -> pure () , fancyColors = True@@ -705,7 +688,7 @@ newIORef $ AppState defaultSettings dirs defaultKeyBindings- (GHCupInfo mempty mempty Nothing)+ (GHCupInfo mempty (GHCupDownloads mempty) Nothing) (PlatformRequest A_64 Darwin Nothing) loggerConfig @@ -716,7 +699,7 @@ r <- flip runReaderT settings- . runE @'[DigestError, ContentLengthError, GPGError, JSONError , DownloadFailed , FileDoesNotExistError, StackPlatformDetectError]+ . runE @'[DigestError, ContentLengthError, GPGError, JSONError , DownloadFailed , FileDoesNotExistError, StackPlatformDetectError, UnsupportedMetadataFormat] $ do pfreq <- lift getPlatformReq liftE $ getDownloadsF pfreq@@ -733,56 +716,82 @@ liftIO $ modifyIORef settings' (\s -> s { ghcupInfo = r }) settings <- liftIO $ readIORef settings' - flip runReaderT settings $ do- lV <- listVersions Nothing [] False True (Nothing, Nothing)- pure $ BrickData (reverse lV)+ r' <- lift $ flip runReaderT settings $ runE $ do+ lV <- listVersions Nothing [] ShowUpdates False True (Nothing, Nothing)+ pure $ BrickData lV+ ExceptT $ pure $ either (Left . prettyHFError) Right $ veitherToEither r' -- -keyHandlers :: KeyBindings+keyHandlersToolList :: KeyBindings -> [ ( KeyCombination- , BrickSettings -> String+ , Maybe (BrickSettings -> String) , Brick.EventM Name BrickState () ) ]-keyHandlers KeyBindings {..} =- [ (bQuit, const "Quit" , Brick.halt)- , (bInstall, const "Install" , withIOAction install')- , (bUninstall, const "Uninstall", withIOAction del')- , (bSet, const "Set" , withIOAction set')- , (bChangelog, const "ChangeLog", withIOAction changelog')+keyHandlersToolList KeyBindings {..} =+ [ (bQuit, Just $ const "Quit" , Brick.halt)+ , (bInstall, Just $ const "Install and set recommended version",+ withIOActionRecommended $ installWithOptions (AdvancedInstall.InstallOptions Nothing True Nothing Nothing False [] Nothing)) , ( bShowAllVersions- , \BrickSettings {..} ->+ , Just $ \BrickSettings {..} -> if _showAllVersions then "Don't show all versions" else "Show all versions" , hideShowHandler' (not . _showAllVersions) )- , (bUp, const "Up", Common.zoom appState moveUp)- , (bDown, const "Down", Common.zoom appState moveDown)- , (KeyCombination (Vty.KChar 'h') [], const "help", mode .= KeyInfo)- , (KeyCombination Vty.KEnter [], const "advance options", createMenuforTool )+ , (KeyCombination (Vty.KChar 'h') [], Just $ const "help", mode .= KeyInfo)+ , (KeyCombination Vty.KEnter [], Just $ const "Show tool details", mode .= Common.ToolInfo )+ , (KeyCombination KLeft [], Nothing, versionFocus .= False)+ , (KeyCombination KRight [], Nothing, versionFocus .= True)+ , (KeyCombination (Vty.KChar '\t') [], Nothing, versionFocus %= not) ]++keyHandlersVersionList :: KeyBindings+ -> [ ( KeyCombination+ , Maybe (BrickSettings -> String)+ , Brick.EventM Name BrickState ()+ )+ ]+keyHandlersVersionList KeyBindings {..} =+ [ (bQuit, Just $ const "Quit" , Brick.halt)+ , (bInstall, Just $ const "Install" , withIOAction install')+ , (bUninstall, Just $ const "Uninstall", withIOAction del')+ , (bSet, Just $ const "Set" , withIOAction set')+ , (bChangelog, Just $ const "ChangeLog", withIOAction changelog')+ , ( bShowAllVersions+ , Just $ \BrickSettings {..} ->+ if _showAllVersions then "Don't show all versions" else "Show all versions"+ , hideShowHandler' (not . _showAllVersions)+ )+ , (KeyCombination (Vty.KChar 'h') [], Just $ const "Help", mode .= KeyInfo)+ , (KeyCombination Vty.KEnter [], Just $ const "Advanced options", createMenuforTool )+ , (KeyCombination KLeft [], Nothing, versionFocus .= False)+ , (KeyCombination KRight [], Nothing, versionFocus .= True)+ , (KeyCombination (Vty.KChar '\t') [], Nothing, versionFocus %= not)+ ] where createMenuforTool = do- e <- use (appState % to sectionListSelectedElement)+ e <- use (appState % to L.listSelectedElement) case e of- Nothing -> pure ()- Just (_, r) -> do- -- Create new ContextMenu, but maintain the state of Install/Compile- -- menus. This is especially useful in case the user made a typo and- -- would like to retry the action.- contextMenu .= ContextMenu.create r- (MenuKeyBindings { mKbUp = bUp, mKbDown = bDown, mKbQuit = bQuit})- -- Set mode to context- mode .= ContextPanel+ Nothing -> pure ()+ Just (_, (t, (td, vlr))) -> case L.listSelectedElement vlr of+ Just (_, lr) -> do+ -- Create new ContextMenu, but maintain the state of Install/Compile+ -- menus. This is especially useful in case the user made a typo and+ -- would like to retry the action.+ contextMenu .= ContextMenu.create (t, (td, lr))+ (MenuKeyBindings { mKbUp = bUp, mKbDown = bDown, mKbQuit = bQuit})+ -- Set mode to context+ mode .= ContextPanel+ Nothing -> pure () pure () - --hideShowHandler' :: (BrickSettings -> Bool) -> (BrickSettings -> Bool) -> m ()- hideShowHandler' f = do- app_settings <- use appSettings- let- vers = f app_settings- newAppSettings = app_settings & Common.showAllVersions .~ vers- ad <- use appData- current_app_state <- use appState- appSettings .= newAppSettings- appState .= constructList ad newAppSettings (Just current_app_state)+hideShowHandler' :: (BrickSettings -> Bool) -> Brick.EventM Name BrickState ()+hideShowHandler' f = do+ app_settings <- use appSettings+ let+ vers = f app_settings+ newAppSettings = app_settings & Common.showAllVersions .~ vers+ ad <- use appData+ current_app_state <- use appState+ appSettings .= newAppSettings+ appState .= constructList ad newAppSettings (Just current_app_state)
lib-tui/GHCup/Brick/App.hs view
@@ -13,7 +13,7 @@ - Pattern match on the Mode - Dispatch drawing/events to the corresponding widget/s -In general each widget should know how to draw itself and how to handle its own events, so this+In general, each widget should know how to draw itself and how to handle its own events, so this module should only contain: - how to draw non-widget information. For example the footer@@ -25,18 +25,19 @@ import qualified GHCup.Brick.Actions as Actions import qualified GHCup.Brick.Attributes as Attributes-import GHCup.Brick.BrickState (BrickState (..), advanceInstallMenu, appKeys, appSettings, appState, contextMenu, mode, compileGHCMenu, compileHLSMenu)+import GHCup.Brick.BrickState (BrickState (..), advancedInstallMenu, appKeys, appSettings, appState, contextMenu, mode, compileGHCMenu, compileHLSMenu, versionFocus) import GHCup.Brick.Common (Mode (..), Name (..)) import qualified GHCup.Brick.Common as Common import qualified GHCup.Brick.Widgets.KeyInfo as KeyInfo+import qualified GHCup.Brick.Widgets.ToolInfo as ToolInfo import qualified GHCup.Brick.Widgets.Menus.Context as ContextMenu import qualified GHCup.Brick.Widgets.Navigation as Navigation import qualified GHCup.Brick.Widgets.Tutorial as Tutorial import qualified GHCup.Brick.Widgets.Menu as Menu-import qualified GHCup.Brick.Widgets.Menus.AdvanceInstall as AdvanceInstall+import qualified GHCup.Brick.Widgets.Menus.AdvancedInstall as AdvancedInstall -import GHCup.List (ListResult)-import GHCup.Types (AppState (AppState, keyBindings), KeyCombination (KeyCombination), KeyBindings (..))+import GHCup.Command.List (ListResult)+import GHCup.Types (AppState (AppState, keyBindings), KeyCombination (KeyCombination), KeyBindings (..), Tool, ToolDescription) import qualified Brick.Focus as F import Brick (@@ -68,6 +69,7 @@ import qualified GHCup.Brick.Widgets.Menus.CompileGHC as CompileGHC import qualified GHCup.Brick.Widgets.Menus.CompileHLS as CompileHLS import Control.Monad (void, when)+import Control.Monad.State.Class (get) app :: AttrMap -> AttrMap -> App BrickState () Name app attrs dimAttrs =@@ -93,17 +95,22 @@ . Brick.txtWrap . T.pack . foldr1 (\x y -> x <> " " <> y)- . fmap (\(KeyCombination key mods, pretty_setting, _)- -> intercalate "+" (Common.showKey key : (Common.showMod <$> mods)) <> ":" <> pretty_setting (st ^. appSettings)+ . fmap (\(KeyCombination key mods, mpretty_setting, _) ->+ case mpretty_setting of+ Just pretty_setting -> intercalate "+" (Common.showKey key : (Common.showMod <$> mods)) <> ":" <> pretty_setting (st ^. appSettings)+ Nothing -> "" )- $ Actions.keyHandlers (st ^. appKeys)- navg = Navigation.draw dimAttrs (st ^. appState) <=> footer+ $ if st ^. versionFocus+ then Actions.keyHandlersVersionList (st ^. appKeys)+ else Actions.keyHandlersToolList (st ^. appKeys)+ navg = Navigation.draw (st ^. versionFocus) dimAttrs (st ^. appState) <=> footer in case st ^. mode of Navigation -> [navg] Tutorial -> [Tutorial.draw (bQuit $ st ^. appKeys), navg] KeyInfo -> [KeyInfo.draw (st ^. appKeys), navg]+ ToolInfo -> [ToolInfo.draw (st ^. appState) (st ^. appKeys), navg] ContextPanel -> [ContextMenu.draw (st ^. contextMenu), navg]- AdvanceInstallPanel -> AdvanceInstall.draw (st ^. advanceInstallMenu) ++ [navg]+ AdvancedInstallPanel -> AdvancedInstall.draw (st ^. advancedInstallMenu) ++ [navg] CompileGHCPanel -> CompileGHC.draw (st ^. compileGHCMenu) ++ [navg] CompileHLSPanel -> CompileHLS.draw (st ^. compileHLSMenu) ++ [navg] @@ -118,6 +125,14 @@ | bQuit kb == KeyCombination key mods -> mode .= Navigation _ -> pure () +toolInfoHandler :: BrickEvent Name e -> EventM Name BrickState ()+toolInfoHandler ev = do+ AppState { keyBindings = kb } <- liftIO $ readIORef Actions.settings'+ case ev of+ VtyEvent (Vty.EvKey key mods)+ | bQuit kb == KeyCombination key mods -> mode .= Navigation+ _ -> pure ()+ -- | On q, go back to navigation. Else, do nothing tutorialHandler :: BrickEvent Name e -> EventM Name BrickState () tutorialHandler ev = do@@ -130,13 +145,19 @@ -- | Tab/Arrows to navigate. navigationHandler :: BrickEvent Name e -> EventM Name BrickState () navigationHandler ev = do+ BrickState{..} <- get AppState { keyBindings = kb } <- liftIO $ readIORef Actions.settings' case ev of- inner_event@(VtyEvent (Vty.EvKey key mods)) ->- case find (\(key', _, _) -> key' == KeyCombination key mods) (Actions.keyHandlers kb) of- Just (_, _, handler) -> handler- Nothing -> void $ Common.zoom appState $ Navigation.handler inner_event- inner_event -> Common.zoom appState $ Navigation.handler inner_event+ inner_event@(VtyEvent (Vty.EvKey key mods))+ | _versionFocus -> do+ case find (\(key', _, _) -> key' == KeyCombination key mods) (Actions.keyHandlersVersionList kb) of+ Just (_, _, handler) -> handler+ Nothing -> void $ Common.zoom appState $ Navigation.handler _versionFocus inner_event+ | otherwise -> do+ case find (\(key', _, _) -> key' == KeyCombination key mods) (Actions.keyHandlersToolList kb) of+ Just (_, _, handler) -> handler+ Nothing -> void $ Common.zoom appState $ Navigation.handler _versionFocus inner_event+ inner_event -> Common.zoom appState $ Navigation.handler _versionFocus inner_event contextMenuHandler :: BrickEvent Name e -> EventM Name BrickState () contextMenuHandler ev = do@@ -146,13 +167,13 @@ case (ev, focusedElement) of (_ , Nothing) -> pure () (VtyEvent (Vty.EvKey k m), Just n) | k == exitKey && m == mods -> mode .= Navigation- (VtyEvent (Vty.EvKey Vty.KEnter []), Just (Common.MenuElement Common.AdvanceInstallButton) ) -> mode .= Common.AdvanceInstallPanel+ (VtyEvent (Vty.EvKey Vty.KEnter []), Just (Common.MenuElement Common.AdvancedInstallButton) ) -> mode .= Common.AdvancedInstallPanel (VtyEvent (Vty.EvKey Vty.KEnter []), Just (Common.MenuElement Common.CompileGHCButton) ) -> mode .= Common.CompileGHCPanel (VtyEvent (Vty.EvKey Vty.KEnter []), Just (Common.MenuElement Common.CompileHLSButton) ) -> mode .= Common.CompileHLSPanel _ -> Common.zoom contextMenu $ ContextMenu.handler ev ---advanceInstallHandler :: BrickEvent Name e -> EventM Name BrickState ()-advanceInstallHandler = menuWithOverlayHandler advanceInstallMenu Actions.installWithOptions AdvanceInstall.handler+advancedInstallHandler :: BrickEvent Name e -> EventM Name BrickState ()+advancedInstallHandler = menuWithOverlayHandler advancedInstallMenu Actions.installWithOptions AdvancedInstall.handler compileGHCHandler :: BrickEvent Name e -> EventM Name BrickState () compileGHCHandler = menuWithOverlayHandler compileGHCMenu Actions.compileGHC CompileGHC.handler@@ -162,8 +183,9 @@ -- | Passes all events to innerHandler if an overlay is opened -- else handles the exitKey and Enter key for the Menu's "OkButton"-menuWithOverlayHandler :: Lens' BrickState (Menu.Menu t Name)- -> (t -> ((Int, ListResult) -> ReaderT AppState IO (Either String a)))+menuWithOverlayHandler ::+ Lens' BrickState (Menu.Menu t Name)+ -> (t -> ((Int, Tool, Maybe ToolDescription, ListResult) -> ReaderT AppState IO (Either String a))) -> (BrickEvent Name e -> EventM Name (Menu.Menu t Name) ()) -> BrickEvent Name e -> EventM Name BrickState ()@@ -187,9 +209,10 @@ m <- use mode case m of KeyInfo -> keyInfoHandler ev+ ToolInfo -> toolInfoHandler ev Tutorial -> tutorialHandler ev Navigation -> navigationHandler ev ContextPanel -> contextMenuHandler ev- AdvanceInstallPanel -> advanceInstallHandler ev+ AdvancedInstallPanel -> advancedInstallHandler ev CompileGHCPanel -> compileGHCHandler ev CompileHLSPanel -> compileHLSHandler ev
lib-tui/GHCup/Brick/Attributes.hs view
@@ -39,7 +39,7 @@ , (strayAttr , Vty.defAttr `withForeColor` Vty.brightCyan) , (dayAttr , Vty.defAttr `withForeColor` Vty.brightCyan) , (helpAttr , Vty.defAttr `withStyle` Vty.italic)- , (hoorayAttr , Vty.defAttr `withForeColor` Vty.brightWhite)+ , (hoorayAttr , Vty.defAttr `withStyle` Vty.bold) , (helpMsgAttr , Vty.defAttr `withForeColor` Vty.yellow) , (errMsgAttr , Vty.defAttr `withForeColor` Vty.red) ]
lib-tui/GHCup/Brick/BrickState.hs view
@@ -32,22 +32,23 @@ import GHCup.Brick.Common ( BrickData(..), BrickSettings(..), Mode(..)) import GHCup.Brick.Widgets.Navigation ( BrickInternalState) import GHCup.Brick.Widgets.Menus.Context (ContextMenu)-import GHCup.Brick.Widgets.Menus.AdvanceInstall (AdvanceInstallMenu)+import GHCup.Brick.Widgets.Menus.AdvancedInstall (AdvancedInstallMenu) import GHCup.Brick.Widgets.Menus.CompileGHC (CompileGHCMenu) import Optics.TH (makeLenses) import GHCup.Brick.Widgets.Menus.CompileHLS (CompileHLSMenu) data BrickState = BrickState- { _appData :: BrickData- , _appSettings :: BrickSettings- , _appState :: BrickInternalState- , _contextMenu :: ContextMenu- , _advanceInstallMenu :: AdvanceInstallMenu- , _compileGHCMenu :: CompileGHCMenu- , _compileHLSMenu :: CompileHLSMenu- , _appKeys :: KeyBindings- , _mode :: Mode+ { _appData :: BrickData+ , _appSettings :: BrickSettings+ , _appState :: BrickInternalState+ , _contextMenu :: ContextMenu+ , _advancedInstallMenu :: AdvancedInstallMenu+ , _compileGHCMenu :: CompileGHCMenu+ , _compileHLSMenu :: CompileHLSMenu+ , _appKeys :: KeyBindings+ , _mode :: Mode+ , _versionFocus :: Bool } --deriving Show
lib-tui/GHCup/Brick/Common.hs view
@@ -45,13 +45,13 @@ UrlEditBox, SetCheckBox, IsolateEditBox, ForceCheckBox, AdditionalEditBox , TargetGhcEditBox, BootstrapGhcEditBox, HadrianGhcEditBox, JobsEditBox, BuildConfigEditBox , PatchesEditBox, CrossTargetEditBox, AddConfArgsEditBox, OvewrwiteVerEditBox- , BuildFlavourEditBox, BuildSystemEditBox, OkButton, AdvanceInstallButton+ , BuildFlavourEditBox, BuildSystemEditBox, OkButton, AdvancedInstallButton , CompileGHCButton, CompileHLSButton, CabalProjectEditBox , CabalProjectLocalEditBox, UpdateCabalCheckBox, GitRefEditBox , BootstrapGhcSelectBox, HadrianGhcSelectBox, ToolVersionBox, GHCInstallTargets ) ) where -import GHCup.List ( ListResult )+import GHCup.Command.List ( ToolListResult ) import GHCup.Prelude ( isWindows ) import GHCup.Types ( Tool, KeyCombination (KeyCombination) ) import Data.List (intercalate)@@ -77,8 +77,8 @@ pattern OkButton :: ResourceId pattern OkButton = ResourceId 0-pattern AdvanceInstallButton :: ResourceId-pattern AdvanceInstallButton = ResourceId 100+pattern AdvancedInstallButton :: ResourceId+pattern AdvancedInstallButton = ResourceId 100 pattern CompileGHCButton :: ResourceId pattern CompileGHCButton = ResourceId 101 pattern CompileHLSButton :: ResourceId@@ -145,11 +145,11 @@ data Name = AllTools -- ^ The main list widget | Singular Tool -- ^ The particular list for each tool | ListItem Tool Int -- ^ An item in list- | KeyInfoBox -- ^ The text box widget with action informacion+ | KeyInfoBox -- ^ The text box widget with action information | TutorialBox -- ^ The tutorial widget | ContextBox -- ^ The resource for Context Menu | CompileGHCBox -- ^ The resource for CompileGHC Menu- | AdvanceInstallBox -- ^ The resource for AdvanceInstall Menu+ | AdvancedInstallBox -- ^ The resource for AdvancedInstall Menu | MenuElement ResourceId -- ^ Each element in a Menu. Resources must not be share for visible -- Menus, but MenuA and MenuB can share resources if they both are -- invisible, or just one of them is visible.@@ -159,9 +159,10 @@ -- | Mode type. It helps to dispatch events to different handlers. data Mode = Navigation | KeyInfo+ | ToolInfo | Tutorial | ContextPanel- | AdvanceInstallPanel+ | AdvancedInstallPanel | CompileGHCPanel | CompileHLSPanel deriving (Eq, Show, Ord)@@ -206,10 +207,10 @@ Brick.centerLayer . Brick.hLimitPercent 80 . Brick.vLimitPercent 75- . Brick.withBorderStyle Border.unicode+ . Brick.withBorderStyle Border.unicodeBold . Border.borderWithLabel (Brick.txt layer_name) --- | puts a cursor at the line beginning so It can be read by screen readers+-- | Puts a cursor at the line beginning so it can be read by screen readers enableScreenReader :: n -> Brick.Widget n -> Brick.Widget n enableScreenReader n = Brick.putCursor n (Brick.Location (0,0)) -- |- tip: when debugging, use Brick.showCursor instead@@ -219,7 +220,7 @@ zoom l = Brick.zoom (toLensVL l) data BrickData = BrickData- { _lr :: [ListResult]+ { _lr :: ToolListResult } deriving Show
lib-tui/GHCup/Brick/Widgets/KeyInfo.hs view
@@ -40,7 +40,12 @@ Brick.hBox [ Brick.txt "Press " , Common.keyToWidget bUp, Brick.txt " and ", Common.keyToWidget bDown- , Brick.txtWrap " to navigate the list of tools"+ , Brick.txtWrap " to select an element in the currently focused list (tools or versions)"+ ],+ Brick.hBox [+ Brick.txt "Press "+ , Brick.txt "← and → or TAB"+ , Brick.txtWrap " to switch between tool and version list" ] , Brick.hBox [ Brick.txt "Press "
lib-tui/GHCup/Brick/Widgets/Menu.hs view
@@ -34,14 +34,14 @@ b) a validator function c) a handler and a renderer -We have to use existential types to achive a composable API since every FieldInput has a different+We have to use existential types to achieve a composable API since every FieldInput has a different internal type, and every MenuField has a different Lens. For example: - The menu state is a record (MyRecord {uri: URI, flag : Bool}) - Then, there are two MenuField: - One MenuField has (Lens' MyRecord URI) and the other has (Lens' MyRecord Bool) - The MenuFields has FieldInputs with internal state Text and Bool, respectively- - Obviously, the MenuField has to be polimorphic in the Lens' and in the Input internal state,- But we must hide that polimorphisim (existential), in order to store all MenuField in a List+ - Obviously, the MenuField has to be polymorphic in the Lens' and in the Input internal state,+ But we must hide that polymorphism (existential), in order to store all MenuField in a List ************** -} @@ -100,7 +100,7 @@ type ErrorMessage = T.Text data ErrorStatus = Valid | Invalid ErrorMessage deriving (Eq) --- | A lens which does nothing. Usefull to defined no-op fields+-- | A lens which does nothing. Useful to define no-op fields emptyLens :: Lens' s () emptyLens = lens (const ()) (\s _ -> s) @@ -156,7 +156,7 @@ { selectStateItems :: (NonEmpty (Int, (i, Bool)), Bool) -- ^ All items along with their selected state -- And Bool to indicate if editable field is selected , selectStateEditState :: Maybe (Edit.Editor T.Text n) -- ^ Editable field's editor state- , selectStateFocusRing :: FocusRing Int -- ^ Focus ring using integeral values assigned to each item+ , selectStateFocusRing :: FocusRing Int -- ^ Focus ring using integral values assigned to each item , selectStateOverlayOpen :: Bool -- ^ Whether the select menu is open } @@ -199,7 +199,7 @@ where g (MenuField {..})= fieldInput ^. inputHelpL s (MenuField{..}) msg = MenuField {fieldInput = fieldInput & inputHelpL .~ msg , ..} --- | How to draw a field given a formater+-- | How to draw a field given a formatter drawField :: Formatter n -> Bool -> MenuField s n -> Widget n drawField amp focus (MenuField { fieldInput = FieldInput {..}, ..}) = let (input, overlay) = inputRender focus fieldStatus inputHelp fieldLabel inputState (amp focus)@@ -225,7 +225,7 @@ createCheckBoxInput :: FieldInput Bool Bool n createCheckBoxInput = FieldInput False Right "" checkBoxRender checkBoxHandler where- border w = Brick.txt "[" <+> (Brick.padRight (Brick.Pad 1) $ Brick.padLeft (Brick.Pad 2) w) <+> Brick.txt "]"+ border w = Brick.txt "[" <+> Brick.padRight (Brick.Pad 1) (Brick.padLeft (Brick.Pad 2) w) <+> Brick.txt "]" drawBool b = if b then border . Brick.withAttr Attributes.installedAttr $ Brick.str Common.checkBoxSelectedSign@@ -268,9 +268,9 @@ | focus -> borderBox editorContents | otherwise -> borderBox $ renderAsErrMsg msg mOverlay = if overlayOpen- then Just (overlayLayer ("Edit " <> label) $ overlay)+ then Just (overlayLayer ("Edit " <> label) overlay) else Nothing- overlay = Brick.vBox $+ overlay = Brick.vBox [ Brick.txtWrap help , Border.border $ Edit.renderEditor (Brick.txt . T.unlines) focus edi , case errMsg of@@ -325,7 +325,7 @@ createSelectInput :: (Ord n, Show n) => NonEmpty i -> (i -> T.Text)- -> (Int -> (NonEmpty (Int, (i, Bool)), Bool) -> ((NonEmpty (Int, (i, Bool))), Bool))+ -> (Int -> (NonEmpty (Int, (i, Bool)), Bool) -> (NonEmpty (Int, (i, Bool)), Bool)) -> (([i], Maybe T.Text) -> Either ErrorMessage k) -> n -> Maybe n@@ -341,10 +341,10 @@ (F.focusRing [1.. totalRows]) False getSelectedItems (SelectState {..}) =- ( fmap (fst . snd) . (filter (snd . snd)) . NE.toList . fst $ selectStateItems- , if snd selectStateItems then (T.init . T.unlines . Edit.getEditContents <$> selectStateEditState) else Nothing)+ ( fmap (fst . snd) . filter (snd . snd) . NE.toList . fst $ selectStateItems+ , if snd selectStateItems then T.init . T.unlines . Edit.getEditContents <$> selectStateEditState else Nothing) - border w = Brick.txt "[" <+> (Brick.padRight (Brick.Pad 1) $ Brick.padLeft (Brick.Pad 1) w) <+> Brick.txt "]"+ border w = Brick.txt "[" <+> Brick.padRight (Brick.Pad 1) (Brick.padLeft (Brick.Pad 1) w) <+> Brick.txt "]" selectRender focus errMsg help label s amp = (field, mOverlay) where field =@@ -353,7 +353,7 @@ (xs, mTxt) -> Just $ fmap (Brick.padRight (Brick.Pad 1) . Brick.txt . showItem) xs ++ (case mTxt of Just t -> [Brick.txt t]; Nothing -> []) in amp $ case (errMsg, mContents) of- (Valid, Nothing) -> (Brick.padLeft (Brick.Pad 1) . renderAsHelpMsg $ help)+ (Valid, Nothing) -> Brick.padLeft (Brick.Pad 1) . renderAsHelpMsg $ help (Valid, Just contents) -> border $ Brick.hBox contents (Invalid msg, Nothing) | focus -> Brick.padLeft (Brick.Pad 1) . renderAsHelpMsg $ help@@ -365,16 +365,16 @@ mOverlay = if selectStateOverlayOpen s then Just (overlayLayer ("Select " <> label) $ overlay s errMsg help) else Nothing- overlay (SelectState {..}) errMsg help = Brick.vBox $+ overlay (SelectState {..}) errMsg help = Brick.vBox [ if txtFieldFocused then Brick.txtWrap "Press Enter to finish editing and select custom value. Press Up/Down keys to navigate" else Brick.txt "Press " <+> Common.keyToWidget (kb ^. mKbQuitL) <+> Brick.txt " to go back, Press Enter to select" , case errMsg of Invalid msg -> renderAsErrMsg msg; _ -> Brick.emptyWidget- , Brick.vLimit (totalRows) $ Brick.withVScrollBars Brick.OnRight+ , Brick.vLimit totalRows $ Brick.withVScrollBars Brick.OnRight $ Brick.viewport viewportFieldName Brick.Vertical- $ Brick.vBox $ mEditableField ++ (NE.toList $ fmap (mkSelectRow focused) (fst selectStateItems))+ $ Brick.vBox $ mEditableField ++ NE.toList (fmap (mkSelectRow focused) (fst selectStateItems)) ] where focused = fromMaybe 1 $ F.focusGetCurrent selectStateFocusRing txtFieldFocused = focused == totalRows@@ -383,12 +383,12 @@ Nothing -> [] mkSelectRow focused (ix, (item, selected)) = (if focused == ix then Brick.visible else id) $- Brick.txt "[" <+> (Brick.padRight (Brick.Pad 1) $ Brick.padLeft (Brick.Pad 1) m) <+> Brick.txt "] "- <+> (renderAslabel (showItem item) (focused == ix))+ Brick.txt "[" <+> Brick.padRight (Brick.Pad 1) (Brick.padLeft (Brick.Pad 1) m) <+> Brick.txt "] "+ <+> renderAslabel (showItem item) (focused == ix) where m = if selected then Brick.txt "*" else Brick.txt " " mkEditTextRow focused edi selected help = (if focused then Brick.visible else id) $- Brick.txt "[" <+> (Brick.padRight (Brick.Pad 1) $ Brick.padLeft (Brick.Pad 1) m) <+> Brick.txt "] "+ Brick.txt "[" <+> Brick.padRight (Brick.Pad 1) (Brick.padLeft (Brick.Pad 1) m) <+> Brick.txt "] " <+> if not focused && Edit.getEditContents edi == [mempty] then Brick.txt "(Specify custom text value)" else Brick.vLimit 1 $ Border.vBorder <+> Brick.padRight Brick.Max (Edit.renderEditor (Brick.txt . T.unlines) focused edi) <+> Border.vBorder@@ -447,8 +447,8 @@ singleSelect :: Int -> (NonEmpty (Int, (i, Bool)), Bool) -> (NonEmpty (Int, (i, Bool)), Bool) singleSelect ix (ne, a) = (fmap (\(ix', (i, b)) -> if ix' == ix then (ix', (i, True)) else (ix', (i, False))) ne, ix == length ne + 1) - getSelection (_, Just txt) = either Left (Right . Left) $ validator txt- getSelection (ls, _) = maybe (either Left (Right . Left) $ validator "") (Right . Right . NE.head) $ NE.nonEmpty ls+ getSelection (_, Just txt) = Left <$> validator txt+ getSelection (ls, _) = maybe (Left <$> validator "") (Right . Right . NE.head) $ NE.nonEmpty ls {- *****************@@ -467,14 +467,14 @@ else Brick.txt t -- | Creates a left align column.--- Example: |- col2 is align dispite the length of col1+-- Example: |- col2 is align despite the length of col1 -- row1_col1 row1_col2 -- row2_col1_large row2_col2 leftify :: Int -> Brick.Widget n -> Brick.Widget n leftify i = Brick.hLimit i . Brick.padRight Brick.Max -- | Creates a right align column.--- Example: |- col2 is align dispite the length of col1+-- Example: |- col2 is align despite the length of col1 -- row1_col1 row1_col2 -- row2_col1_large row2_col2 rightify :: Int -> Brick.Widget n -> Brick.Widget n@@ -524,11 +524,18 @@ ''Menu isValidMenu :: Menu s n -> Bool-isValidMenu m = (all isValidField $ menuFields m)- && (case (menuValidator m) (menuState m) of { Nothing -> True; _ -> False })+isValidMenu m = all isValidField (menuFields m)+ && (case menuValidator m (menuState m) of { Nothing -> True; _ -> False }) -createMenu :: n -> s -> T.Text -> (s -> Maybe ErrorMessage)- -> MenuKeyBindings -> [Button s n] -> [MenuField s n] -> Menu s n+createMenu ::+ n+ -> s+ -> T.Text+ -> (s -> Maybe ErrorMessage)+ -> MenuKeyBindings+ -> [Button s n]+ -> [MenuField s n]+ -> Menu s n createMenu n initial title validator keys buttons fields = Menu fields initial validator buttons ring keys n title where ring = F.focusRing $ [field & fieldName | field <- fields] ++ [button & fieldName | button <- buttons] @@ -550,7 +557,7 @@ Just err -> menuButtonsL %= fmap (fieldStatusL .~ Invalid err) else menuButtonsL %= fmap (fieldStatusL .~ Invalid "Some fields are invalid") menuFieldsL .= updated_fields- case (drawFieldOverlay =<< focusedField) of+ case drawFieldOverlay =<< focusedField of Just _ -> case ev of VtyEvent e -> propagateEvent e _ -> pure ()@@ -600,7 +607,7 @@ -- A list of functions which draw a highlighted label with right padding at the left of a widget. amplifiers = let labelsWidgets = fmap renderAslabel fieldLabels- in fmap (\f b -> ((rightify (maxWidth + 1) (f b <+> Brick.txt " ")) <+>) ) labelsWidgets+ in fmap (\f b -> (rightify (maxWidth + 1) (f b <+> Brick.txt " ") <+>) ) labelsWidgets drawFields = fmap drawField amplifiers fieldWidgets = zipWith (F.withFocusRing (menu ^. menuFocusRingL)) drawFields (menu ^. menuFieldsL)
− lib-tui/GHCup/Brick/Widgets/Menus/AdvanceInstall.hs
@@ -1,135 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE ViewPatterns #-}-{-# OPTIONS_GHC -Wno-unused-record-wildcards #-}-{-# OPTIONS_GHC -Wno-unused-matches #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE InstanceSigs #-}-{-# OPTIONS_GHC -Wno-incomplete-patterns #-}--module GHCup.Brick.Widgets.Menus.AdvanceInstall (- InstallOptions (..),- AdvanceInstallMenu,- create,- handler,- draw,- instBindistL,- instSetL,- instVersionL,- isolateDirL,- forceInstallL,- addConfArgsL,- installTargetsL,-) where--import GHCup.Types (GHCTargetVersion(..))-import GHCup.Brick.Widgets.Menu (Menu, MenuKeyBindings)-import qualified GHCup.Brick.Widgets.Menu as Menu-import GHCup.Brick.Common(Name(..))-import Brick- ( BrickEvent(..),- EventM,- Widget(..))-import Prelude hiding ( appendFile )-import Optics.TH (makeLensesFor)-import qualified GHCup.Brick.Common as Common-import URI.ByteString (URI)-import qualified Data.Text as T-import Data.Bifunctor (Bifunctor(..))-import Data.Function ((&))-import Optics ((.~))-import Data.Char (isSpace)-import qualified GHCup.Utils.Parsers as Utils--data InstallOptions = InstallOptions- { instBindist :: Maybe URI- , instSet :: Bool- , instVersion :: Maybe GHCTargetVersion- -- ^ User specified version to override default- , isolateDir :: Maybe FilePath- , forceInstall :: Bool- , addConfArgs :: [T.Text]- , installTargets :: T.Text- } deriving (Eq, Show)--makeLensesFor [- ("instBindist", "instBindistL")- , ("instSet", "instSetL")- , ("instVersion", "instVersionL")- , ("isolateDir", "isolateDirL")- , ("forceInstall", "forceInstallL")- , ("addConfArgs", "addConfArgsL")- , ("installTargets", "installTargetsL")- ]- ''InstallOptions--type AdvanceInstallMenu = Menu InstallOptions Name--create :: MenuKeyBindings -> AdvanceInstallMenu-create k = Menu.createMenu AdvanceInstallBox initialState "Advance Install" validator k [ok] fields- where- initialInstallTargets = "install"- initialState = InstallOptions Nothing False Nothing Nothing False [] initialInstallTargets- validator InstallOptions {..} = case (instSet, isolateDir) of- (True, Just _) -> Just "Cannot set active when doing an isolated install"- _ -> Nothing- -- Brick's internal editor representation is [mempty].- emptyEditor i = T.null i || (i == "\n")-- whenEmpty :: a -> (T.Text -> Either Menu.ErrorMessage a) -> T.Text -> Either Menu.ErrorMessage a- whenEmpty emptyval f i = if not (emptyEditor i) then f i else Right emptyval-- uriValidator :: T.Text -> Either Menu.ErrorMessage (Maybe URI)- uriValidator = whenEmpty Nothing (second Just . readUri)- where readUri = first T.pack . Utils.uriParser . T.unpack-- filepathValidator :: T.Text -> Either Menu.ErrorMessage (Maybe FilePath)- filepathValidator = whenEmpty Nothing (bimap T.pack Just . Utils.absolutePathParser . T.unpack)-- toolVersionValidator :: T.Text -> Either Menu.ErrorMessage (Maybe GHCTargetVersion)- toolVersionValidator = whenEmpty Nothing (bimap T.pack Just . Utils.ghcVersionEither . T.unpack)-- additionalValidator :: T.Text -> Either Menu.ErrorMessage [T.Text]- additionalValidator = Right . T.split isSpace-- fields =- [ Menu.createEditableField (Common.MenuElement Common.UrlEditBox) uriValidator instBindistL- & Menu.fieldLabelL .~ "url"- & Menu.fieldHelpMsgL .~ "Install the specified version from this bindist"- , Menu.createCheckBoxField (Common.MenuElement Common.SetCheckBox) instSetL- & Menu.fieldLabelL .~ "set"- & Menu.fieldHelpMsgL .~ "Set as active version after install"- , Menu.createEditableField (Common.MenuElement Common.ToolVersionBox) toolVersionValidator instVersionL- & Menu.fieldLabelL .~ "version"- & Menu.fieldHelpMsgL .~ "Specify a custom version"- , Menu.createEditableField' initialInstallTargets (Common.MenuElement Common.GHCInstallTargets) Right installTargetsL- & Menu.fieldLabelL .~ "install-targets"- & Menu.fieldHelpMsgL .~ "Specify space separated list of make install targets"- , Menu.createEditableField (Common.MenuElement Common.IsolateEditBox) filepathValidator isolateDirL- & Menu.fieldLabelL .~ "isolated"- & Menu.fieldHelpMsgL .~ "install in an isolated absolute directory instead of the default one"- , Menu.createCheckBoxField (Common.MenuElement Common.ForceCheckBox) forceInstallL- & Menu.fieldLabelL .~ "force"- & Menu.fieldHelpMsgL .~ "Force install (THIS IS UNSAFE, only use it in Dockerfiles or CI)"- , Menu.createEditableField (Common.MenuElement Common.AdditionalEditBox) additionalValidator addConfArgsL- & Menu.fieldLabelL .~ "CONFIGURE_ARGS"- & Menu.fieldHelpMsgL .~ "Additional arguments to bindist configure"- ]-- ok = Menu.createButtonField (Common.MenuElement Common.OkButton)- & Menu.fieldLabelL .~ "Advance Install"- & Menu.fieldHelpMsgL .~ "Install with options below"--handler :: BrickEvent Name e -> EventM Name AdvanceInstallMenu ()-handler = Menu.handlerMenu---draw :: AdvanceInstallMenu -> [Widget Name]-draw = Menu.drawMenu
+ lib-tui/GHCup/Brick/Widgets/Menus/AdvancedInstall.hs view
@@ -0,0 +1,137 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ViewPatterns #-}+{-# OPTIONS_GHC -Wno-unused-record-wildcards #-}+{-# OPTIONS_GHC -Wno-unused-matches #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE InstanceSigs #-}+{-# OPTIONS_GHC -Wno-incomplete-patterns #-}++module GHCup.Brick.Widgets.Menus.AdvancedInstall (+ InstallOptions (..),+ AdvancedInstallMenu,+ create,+ handler,+ draw,+ instBindistL,+ instSetL,+ instVersionL,+ isolateDirL,+ forceInstallL,+ addConfArgsL,+ installTargetsL,+) where++import GHCup.Types (TargetVersionReq(..))+import GHCup.Brick.Widgets.Menu (Menu, MenuKeyBindings)+import qualified GHCup.Brick.Widgets.Menu as Menu+import GHCup.Brick.Common(Name(..))+import Brick+ ( BrickEvent(..),+ EventM,+ Widget(..))+import Prelude hiding ( appendFile )+import Optics.TH (makeLensesFor)+import qualified GHCup.Brick.Common as Common+import URI.ByteString (URI)+import qualified Data.Text as T+import Data.Bifunctor (Bifunctor(..))+import Data.Function ((&))+import Optics ((.~))+import Data.Char (isSpace)+import qualified GHCup.Input.Parsers as Utils++data InstallOptions = InstallOptions+ { instBindist :: Maybe URI+ , instSet :: Bool+ , instVersion :: Maybe TargetVersionReq+ -- ^ User specified version to override default+ , isolateDir :: Maybe FilePath+ , forceInstall :: Bool+ , addConfArgs :: [T.Text]+ , installTargets :: Maybe T.Text+ } deriving (Eq, Show)++makeLensesFor [+ ("instBindist", "instBindistL")+ , ("instSet", "instSetL")+ , ("instVersion", "instVersionL")+ , ("isolateDir", "isolateDirL")+ , ("forceInstall", "forceInstallL")+ , ("addConfArgs", "addConfArgsL")+ , ("installTargets", "installTargetsL")+ ]+ ''InstallOptions++type AdvancedInstallMenu = Menu InstallOptions Name++create :: MenuKeyBindings -> AdvancedInstallMenu+create k = Menu.createMenu AdvancedInstallBox initialState "Advanced Install" validator k [ok] fields+ where+ initialState = InstallOptions Nothing False Nothing Nothing False [] Nothing+ validator InstallOptions {..} = case (instSet, isolateDir) of+ (True, Just _) -> Just "Cannot set active when doing an isolated install"+ _ -> Nothing+ -- Brick's internal editor representation is [mempty].+ emptyEditor i = T.null i || (i == "\n")++ whenEmpty :: a -> (T.Text -> Either Menu.ErrorMessage a) -> T.Text -> Either Menu.ErrorMessage a+ whenEmpty emptyval f i = if not (emptyEditor i) then f i else Right emptyval++ uriValidator :: T.Text -> Either Menu.ErrorMessage (Maybe URI)+ uriValidator = whenEmpty Nothing (second Just . readUri)+ where readUri = first T.pack . Utils.uriParser . T.unpack++ filepathValidator :: T.Text -> Either Menu.ErrorMessage (Maybe FilePath)+ filepathValidator = whenEmpty Nothing (bimap T.pack Just . Utils.absolutePathParser . T.unpack)++ installTargetValidator :: T.Text -> Either Menu.ErrorMessage (Maybe T.Text)+ installTargetValidator = whenEmpty Nothing (Right . Just)++ toolVersionValidator :: T.Text -> Either Menu.ErrorMessage (Maybe TargetVersionReq)+ toolVersionValidator = whenEmpty Nothing (bimap T.pack Just . Utils.ghcVersionEither' . T.unpack)++ additionalValidator :: T.Text -> Either Menu.ErrorMessage [T.Text]+ additionalValidator = Right . T.split isSpace++ fields =+ [ Menu.createEditableField (Common.MenuElement Common.UrlEditBox) uriValidator instBindistL+ & Menu.fieldLabelL .~ "url"+ & Menu.fieldHelpMsgL .~ "Install the specified version from this bindist"+ , Menu.createCheckBoxField (Common.MenuElement Common.SetCheckBox) instSetL+ & Menu.fieldLabelL .~ "set"+ & Menu.fieldHelpMsgL .~ "Set as active version after install"+ , Menu.createEditableField (Common.MenuElement Common.ToolVersionBox) toolVersionValidator instVersionL+ & Menu.fieldLabelL .~ "version"+ & Menu.fieldHelpMsgL .~ "Specify a custom version"+ , Menu.createEditableField (Common.MenuElement Common.GHCInstallTargets) installTargetValidator installTargetsL+ & Menu.fieldLabelL .~ "install-targets"+ & Menu.fieldHelpMsgL .~ "Overwrite install targets (space separated list)"+ , Menu.createEditableField (Common.MenuElement Common.IsolateEditBox) filepathValidator isolateDirL+ & Menu.fieldLabelL .~ "isolated"+ & Menu.fieldHelpMsgL .~ "install in an isolated absolute directory instead of the default one"+ , Menu.createCheckBoxField (Common.MenuElement Common.ForceCheckBox) forceInstallL+ & Menu.fieldLabelL .~ "force"+ & Menu.fieldHelpMsgL .~ "Force install (THIS IS UNSAFE, only use it in Dockerfiles or CI)"+ , Menu.createEditableField (Common.MenuElement Common.AdditionalEditBox) additionalValidator addConfArgsL+ & Menu.fieldLabelL .~ "CONFIGURE_ARGS"+ & Menu.fieldHelpMsgL .~ "Additional arguments to bindist configure"+ ]++ ok = Menu.createButtonField (Common.MenuElement Common.OkButton)+ & Menu.fieldLabelL .~ "Advanced Install"+ & Menu.fieldHelpMsgL .~ "Install with options below"++handler :: BrickEvent Name e -> EventM Name AdvancedInstallMenu ()+handler = Menu.handlerMenu+++draw :: AdvancedInstallMenu -> [Widget Name]+draw = Menu.drawMenu
lib-tui/GHCup/Brick/Widgets/Menus/CompileGHC.hs view
@@ -61,7 +61,7 @@ import System.FilePath (isPathSeparator) import Control.Applicative (Alternative((<|>))) import Text.Read (readEither)-import qualified GHCup.Utils.Parsers as Utils+import qualified GHCup.Input.Parsers as Utils import Text.PrettyPrint.HughesPJClass ( prettyShow ) data CompileGHCOptions = CompileGHCOptions@@ -78,7 +78,7 @@ , _buildSystem :: Maybe BuildSystem , _isolateDir :: Maybe FilePath , _gitRef :: Maybe String- , _installTargets :: T.Text+ , _installTargets :: Maybe T.Text } deriving (Eq, Show) makeLenses ''CompileGHCOptions@@ -88,7 +88,7 @@ create :: MenuKeyBindings -> [Version] -> CompileGHCMenu create k availableGHCs = Menu.createMenu CompileGHCBox initialState "Compile GHC" validator k buttons fields where- initialInstallTargets = "install"+ initialInstallTargets = Nothing initialState = CompileGHCOptions (Right "")@@ -117,18 +117,18 @@ bootstrapV :: T.Text -> Either Menu.ErrorMessage (Either Version FilePath) bootstrapV i =- case not $ emptyEditor i of- True ->- let readVersion = bimap (const "Not a valid version") Left (version i)- readPath = do- mfilepath <- filepathV i- case mfilepath of- Nothing -> Left "Invalid path"- Just f -> Right (Right f)- in if T.any isPathSeparator i- then readPath- else readVersion- False -> Left "No version selected / no path specified"+ if not $ emptyEditor i+ then+ let readVersion = bimap (const "Not a valid version") Left (version i)+ readPath = do+ mfilepath <- filepathV i+ case mfilepath of+ Nothing -> Left "Invalid path"+ Just f -> Right (Right f)+ in if T.any isPathSeparator i+ then readPath+ else readVersion+ else Left "No version selected / no path specified" hadrianstrapV :: T.Text -> Either Menu.ErrorMessage (Maybe (Either Version FilePath)) hadrianstrapV i' =@@ -161,6 +161,9 @@ additionalValidator :: T.Text -> Either Menu.ErrorMessage [T.Text] additionalValidator = Right . T.split isSpace + installTargetValidator :: T.Text -> Either Menu.ErrorMessage (Maybe T.Text)+ installTargetValidator = whenEmpty Nothing (Right . Just)+ showMaybeBuildSystem :: Maybe BuildSystem -> T.Text showMaybeBuildSystem = \case Nothing -> "Auto select (prefer hadrian if available, and build config is not specified)"@@ -169,7 +172,7 @@ bootstrapGHCFields = case NE.nonEmpty availableGHCs of Just ne ->- let bootstrapGhc' = bootstrapGhc % (iso (either (Left . Left) (Left . Right)) (either id Left))+ let bootstrapGhc' = bootstrapGhc % iso (either (Left . Left) (Left . Right)) (either id Left) in [ Menu.createSelectFieldWithEditable (Common.MenuElement Common.BootstrapGhcSelectBox) (Common.MenuElement Common.BootstrapGhcEditBox) bootstrapGhc' bootstrapV ne (T.pack . prettyShow) k & Menu.fieldLabelL .~ "bootstrap-ghc" & Menu.fieldHelpMsgL .~ "The GHC version (or full path) to bootstrap with (must be installed)"@@ -183,7 +186,7 @@ hadrianGHCFields = case NE.nonEmpty availableGHCs of Just ne ->- let hadrianGhc' = hadrianGhc % (iso Left (either id (Just . Left)))+ let hadrianGhc' = hadrianGhc % iso Left (either id (Just . Left)) in [ Menu.createSelectFieldWithEditable (Common.MenuElement Common.HadrianGhcSelectBox) (Common.MenuElement Common.HadrianGhcEditBox) hadrianGhc' hadrianstrapV ne (T.pack . prettyShow) k & Menu.fieldLabelL .~ "hadrian-ghc" & Menu.fieldHelpMsgL .~ "The GHC version (or full path) that will be used to compile hadrian (must be installed)"@@ -205,17 +208,17 @@ & Menu.fieldHelpMsgL .~ "Set the compile build flavour (this value depends on the build system type: 'make' vs 'hadrian')" , Menu.createEditableField (Common.MenuElement Common.AdditionalEditBox) additionalValidator addConfArgs & Menu.fieldLabelL .~ "CONFIGURE_ARGS"- & Menu.fieldHelpMsgL .~ "Additional arguments to compile configure"+ & Menu.fieldHelpMsgL .~ "Additional arguments to bindist configure" , Menu.createEditableField (Common.MenuElement Common.BuildConfigEditBox) filepathV buildConfig & Menu.fieldLabelL .~ "build config" & Menu.fieldHelpMsgL .~ "Absolute path to build config file (make build system only)" , Menu.createEditableField (Common.MenuElement Common.PatchesEditBox) patchesV patches & Menu.fieldLabelL .~ "patches"- & Menu.fieldHelpMsgL .~ "Either a URI to a patch (https/http/file) or Absolute path to patch directory"+ & Menu.fieldHelpMsgL .~ "Either a URI to a patch (https/http/file) or absolute path to patch directory" , Menu.createEditableField (Common.MenuElement Common.CrossTargetEditBox) (Right . Just) crossTarget & Menu.fieldLabelL .~ "cross target" & Menu.fieldHelpMsgL .~ "Build cross-compiler for this platform"- , Menu.createSelectField (Common.MenuElement Common.BuildSystemEditBox) (buildSystem % (iso Just join)) (Nothing :| [Just Hadrian, Just Make]) showMaybeBuildSystem k+ , Menu.createSelectField (Common.MenuElement Common.BuildSystemEditBox) (buildSystem % iso Just join) (Nothing :| [Just Hadrian, Just Make]) showMaybeBuildSystem k & Menu.fieldLabelL .~ "build system" & Menu.fieldHelpMsgL .~ "Select the build system" , Menu.createEditableField (Common.MenuElement Common.OvewrwiteVerEditBox) versionV overwriteVer@@ -223,13 +226,13 @@ & Menu.fieldHelpMsgL .~ "Allows to overwrite the finally installed VERSION with a different one. Allows to specify patterns: %v (version), %b (branch name), %h (short commit hash), %H (long commit hash), %g ('git describe' output)" , Menu.createEditableField (Common.MenuElement Common.IsolateEditBox) filepathV isolateDir & Menu.fieldLabelL .~ "isolated"- & Menu.fieldHelpMsgL .~ "install in an isolated absolute directory instead of the default one"+ & Menu.fieldHelpMsgL .~ "Install in an isolated absolute directory instead of the default one" , Menu.createEditableField (Common.MenuElement Common.GitRefEditBox) (Right . Just . T.unpack) gitRef & Menu.fieldLabelL .~ "git-ref" & Menu.fieldHelpMsgL .~ "The git commit/branch/ref to build from"- , Menu.createEditableField' initialInstallTargets (Common.MenuElement Common.GHCInstallTargets) Right installTargets+ , Menu.createEditableField (Common.MenuElement Common.GHCInstallTargets) installTargetValidator installTargets & Menu.fieldLabelL .~ "install-targets"- & Menu.fieldHelpMsgL .~ "Specify space separated list of make install targets"+ & Menu.fieldHelpMsgL .~ "Overwrite install targets (space separated list)" ] buttons = [
lib-tui/GHCup/Brick/Widgets/Menus/CompileHLS.hs view
@@ -44,7 +44,7 @@ import Prelude hiding ( appendFile ) import Optics.TH (makeLenses) import qualified GHCup.Brick.Common as Common-import GHCup.Types (VersionPattern, ToolVersion(..))+import GHCup.Types (VersionPattern, ToolVersion(..), VersionReq(..)) import URI.ByteString (URI) import qualified Data.Text as T import Data.Bifunctor (Bifunctor(..))@@ -55,7 +55,7 @@ import Data.Versions import Control.Applicative (Alternative((<|>))) import Text.Read (readEither)-import qualified GHCup.Utils.Parsers as Utils+import qualified GHCup.Input.Parsers as Utils import Text.PrettyPrint.HughesPJClass ( prettyShow ) data CompileHLSOptions = CompileHLSOptions@@ -145,7 +145,7 @@ targetGHCsField = let label = "target GHC(s)"- in case NE.nonEmpty (fmap ToolVersion availableGHCs) of+ in case NE.nonEmpty (fmap (ToolVersion . (`VersionReq` Nothing)) availableGHCs) of Just ne -> Menu.createMultiSelectField (Common.MenuElement Common.TargetGhcEditBox) targetGHCs ne (T.pack . prettyShow) k & Menu.fieldLabelL .~ label & Menu.fieldHelpMsgL .~ "GHC versions to compile for (Press Enter to edit)"
lib-tui/GHCup/Brick/Widgets/Menus/Context.hs view
@@ -9,8 +9,8 @@ import Prelude hiding (appendFile) import Data.Versions (prettyVer)-import GHCup.List ( ListResult(..) )-import GHCup.Types (Tool (..))+import GHCup.Command.List ( ListResult(..) )+import GHCup.Types (Tool (..), ToolDescription(..)) import qualified GHCup.Brick.Common as Common import qualified GHCup.Brick.Widgets.Menu as Menu@@ -21,20 +21,21 @@ import qualified Brick.Focus as F import Brick.Widgets.Core ((<+>)) -import Optics (to)+import Optics (to, _1, _2) import Optics.Operators ((.~), (^.)) import Optics.Optic ((%)) import Data.Foldable (foldl')+import qualified Data.Text as T -type ContextMenu = Menu ListResult Name+type ContextMenu = Menu (Tool, (Maybe ToolDescription, ListResult)) Name -create :: ListResult -> MenuKeyBindings -> ContextMenu-create lr keyBindings = Menu.createMenu Common.ContextBox lr "" validator keyBindings buttons []+create :: (Tool, (Maybe ToolDescription, ListResult)) -> MenuKeyBindings -> ContextMenu+create (tool, (td, lr)) keyBindings = Menu.createMenu Common.ContextBox (tool, (td, lr)) "" validator keyBindings buttons [] where advInstallButton =- Menu.createButtonField (MenuElement Common.AdvanceInstallButton)+ Menu.createButtonField (MenuElement Common.AdvancedInstallButton) & Menu.fieldLabelL .~ "Install"- & Menu.fieldHelpMsgL .~ "Advance Installation Settings"+ & Menu.fieldHelpMsgL .~ "Advanced Installation Settings" compileGhcButton = Menu.createButtonField (MenuElement Common.CompileGHCButton) & Menu.fieldLabelL .~ "Compile"@@ -44,16 +45,16 @@ & Menu.fieldLabelL .~ "Compile" & Menu.fieldHelpMsgL .~ "Compile HLS from source" buttons =- case lTool lr of- GHC -> [advInstallButton, compileGhcButton]- HLS -> [advInstallButton, compileHLSButton]+ case tool of+ Tool "ghc" -> [advInstallButton, compileGhcButton]+ Tool "hls" -> [advInstallButton, compileHLSButton] _ -> [advInstallButton] validator = const Nothing draw :: ContextMenu -> Widget Name draw menu = Common.frontwardLayer- ("Context Menu for " <> tool_str <> " " <> prettyVer (lVer $ menu ^. Menu.menuStateL))+ ("Context Menu for " <> tool_str <> " " <> prettyVer (menu ^. Menu.menuStateL % _2 % _2 % to lVer)) $ Brick.vBox [ Brick.vBox buttonWidgets , Brick.txt " "@@ -72,12 +73,13 @@ drawButtons = fmap Menu.drawField buttonAmplifiers buttonWidgets = zipWith (F.withFocusRing (menu ^. Menu.menuFocusRingL)) drawButtons (menu ^. Menu.menuButtonsL) tool_str =- case menu ^. Menu.menuStateL % to lTool of- GHC -> "GHC"- GHCup -> "GHCup"- Cabal -> "Cabal"- HLS -> "HLS"- Stack -> "Stack"+ case menu ^. Menu.menuStateL % _1 of+ Tool "ghc" -> "GHC"+ Tool "ghcup" -> "GHCup"+ Tool "cabal" -> "Cabal"+ Tool "hls" -> "HLS"+ Tool "stack" -> "Stack"+ Tool t -> T.pack t handler :: BrickEvent Name e -> EventM Name ContextMenu () handler = Menu.handlerMenu
@@ -7,26 +7,19 @@ {-# OPTIONS_GHC -Wno-unused-matches #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}--{- Brick's navigation widget:-It is a FocusRing over many list's. Each list contains the information for each tool. Each list has an internal name (for Brick's runtime)-and a label which we can use in rendering. This data-structure helps to reuse Brick.Widget.List and to navegate easily across---}-+{-# LANGUAGE ViewPatterns #-} -module GHCup.Brick.Widgets.Navigation (BrickInternalState, create, handler, draw) where+module GHCup.Brick.Widgets.Navigation (BrickInternalState, handler, draw) where -import GHCup.List ( ListResult(..) )+import GHCup.Command.List ( ListResult(..), RevTag(..) ) import GHCup.Types- ( GHCTargetVersion(GHCTargetVersion),+ ( TargetVersion(TargetVersion), Tool(..), Tag(..), tVerToText,- tagToString )+ tagToString, ToolDescription ) import qualified GHCup.Brick.Common as Common import qualified GHCup.Brick.Attributes as Attributes-import qualified GHCup.Brick.Widgets.SectionList as SectionList import Brick ( BrickEvent(..), Padding(Max, Pad),@@ -36,78 +29,116 @@ (<+>), (<=>)) import qualified Brick-import Brick.Widgets.Border ( hBorder, borderWithLabel)+import Brick.Widgets.Border ( hBorder, borderWithLabel, vBorder) import Brick.Widgets.Border.Style ( unicode ) import Brick.Widgets.Center ( center ) import qualified Brick.Widgets.List as L import Data.List ( intercalate, sort ) import Data.Maybe ( mapMaybe )-import Data.Vector ( Vector) import Data.Versions ( prettyPVP, prettyVer ) import Prelude hiding ( appendFile ) import qualified Data.Text as T import qualified Data.Vector as V-+import Text.PrettyPrint.HughesPJClass (prettyShow)+import Control.Monad.State.Class (get, modify)+import qualified Graphics.Vty as Vty -type BrickInternalState = SectionList.SectionList Common.Name ListResult+type BrickList = L.GenericList Common.Name V.Vector --- | How to create a navigation widget-create :: Common.Name -- The name of the section list- -> [(Common.Name, Vector ListResult)] -- a list of tuples (section name, collection of elements)- -> Int -- The height of each item in a list. Commonly 1- -> BrickInternalState-create = SectionList.sectionList+type BrickInternalState = BrickList (Tool, (Maybe ToolDescription, BrickList ListResult)) -- | How the navigation handler handle events-handler :: BrickEvent Common.Name e -> EventM Common.Name BrickInternalState ()-handler = SectionList.handleGenericListEvent+handler :: Bool -> BrickEvent Common.Name e -> EventM Common.Name BrickInternalState ()+handler False (Brick.VtyEvent e) = L.handleListEvent e+handler True (Brick.VtyEvent e) = do+ bis :: BrickInternalState <- get+ case L.listSelectedElement bis of+ Nothing -> pure ()+ Just (_, (t, (td, vlr))) -> do+ updatedVlr <- Brick.nestEventM' vlr (handleVersionEvent e)+ modify (L.listModify $ (fmap . fmap) (const updatedVlr))+ where+ -- need to reverse because we reversed the list+ handleVersionEvent (Vty.EvKey Vty.KUp []) = L.handleListEvent (Vty.EvKey Vty.KDown [])+ handleVersionEvent (Vty.EvKey Vty.KDown []) = L.handleListEvent (Vty.EvKey Vty.KUp [])+ handleVersionEvent (Vty.EvKey Vty.KPageDown []) = L.handleListEvent (Vty.EvKey Vty.KPageUp [])+ handleVersionEvent (Vty.EvKey Vty.KPageUp []) = L.handleListEvent (Vty.EvKey Vty.KPageDown [])+ handleVersionEvent (Vty.EvKey Vty.KHome []) = L.handleListEvent (Vty.EvKey Vty.KEnd [])+ handleVersionEvent (Vty.EvKey Vty.KEnd []) = L.handleListEvent (Vty.EvKey Vty.KHome [])+ handleVersionEvent e' = L.handleListEvent e'+handler _ _ = pure () + -- | How to draw the navigation widget-draw :: AttrMap -> BrickInternalState -> Widget Common.Name-draw dimAttrs section_list+draw :: Bool -> AttrMap -> BrickInternalState -> Widget Common.Name+draw versionFocus dimAttrs bis = Brick.padBottom Max- ( Brick.withBorderStyle unicode+ ( Brick.joinBorders $ Brick.withBorderStyle unicode $ borderWithLabel (Brick.str "GHCup")- (center (header <=> hBorder <=> renderList' section_list))+ (center (Brick.vLimit 1 header <=> hBorder <=> renderList')) ) where+ minHSize s' = Brick.hLimit s' . Brick.vLimit 1 . (<+> Brick.fill ' ')+ allElements = L.listElements bis+ minToolSize = V.maximum $ V.map (length . prettyShow . fst) allElements+ selectedTool = fmap snd $ L.listSelectedElement bis+ minTagSize = maybe 0 (\(t, (_, vlr)) -> V.maximum $ V.map (length . intercalate "," . fmap tagToString . lTag) $ L.listElements vlr) selectedTool+ minVerSizeList = maybe 0 (\(t, (_, vlr)) -> V.maximum $ V.map (\ListResult{..} -> T.length $ tVerToText (TargetVersion lCross lVer)) $ L.listElements vlr) selectedTool+ minVerHeaderSize = length $ maybe "Versions" (\(fst -> t) -> prettyShow t <> " versions") selectedTool+ minVerSize = max minVerSizeList minVerHeaderSize header =- minHSize 2 Brick.emptyWidget- <+> Brick.padLeft (Pad 2) (minHSize 6 $ Brick.str "Tool")- <+> minHSize 15 (Brick.str "Version")- <+> Brick.padLeft (Pad 1) (minHSize 25 $ Brick.str "Tags")+ Brick.padLeft (Pad 1) (minHSize (minToolSize + 2) (Brick.str "Tool"))+ <+> vBorder+ <+> Brick.padLeft (Pad 1) (minHSize (minVerSize + 2) (maybe (Brick.str "Versions") (\(fst -> t) -> printTool t <+> Brick.str " versions") selectedTool))+ <+> Brick.padLeft (Pad 2) (minHSize minTagSize (Brick.str "Tags")) <+> Brick.padLeft (Pad 5) (Brick.str "Notes")- renderList' bis =- let allElements = V.concatMap L.listElements $ SectionList.sectionListElements bis- minTagSize = V.maximum $ V.map (length . intercalate "," . fmap tagToString . lTag) allElements- minVerSize = V.maximum $ V.map (\ListResult{..} -> T.length $ tVerToText (GHCTargetVersion lCross lVer)) allElements- in Brick.withDefAttr L.listAttr $ SectionList.renderSectionList (renderItem minTagSize minVerSize) True bis- renderItem minTagSize minVerSize listIx b listResult@ListResult{lTag = lTag', ..} =++ renderList' =+ let toolColumn = Brick.hLimit (minToolSize + 2)+ (Brick.withDefAttr L.listAttr (L.renderList renderTool (not versionFocus) bis))+ versionColumn = maybe Brick.emptyWidget+ (\(t, (_, vlr)) ->+ Brick.withDefAttr L.listAttr+ $ L.renderListWithIndex (renderItem t) versionFocus+ $ L.listReverse vlr+ )+ selectedTool+ in Brick.padLeft (Pad 1) toolColumn+ <+> vBorder+ <+> Brick.padLeft (Pad 1) versionColumn++ renderTool :: Bool -> (Tool, (Maybe ToolDescription, BrickList ListResult)) -> Widget Common.Name+ renderTool b (t, (tDesc, _)) = minHSize minToolSize $ printTool t++ renderItem t listIx b listResult@ListResult{lTag = lTag', ..} = let marks = if | lSet -> (Brick.withAttr Attributes.setAttr $ Brick.str Common.setSign) | lInstalled -> (Brick.withAttr Attributes.installedAttr $ Brick.str Common.installedSign) | otherwise -> (Brick.withAttr Attributes.notInstalledAttr $ Brick.str Common.notInstalledSign)+ rev = case lRev of+ (rev', RevUpdate) -> "-r" <> show rev'+ (rev', RevOutdated) -> "-r" <> show rev'+ (_, RevNormal) -> "" ver = case lCross of- Nothing -> T.unpack . prettyVer $ lVer- Just c -> T.unpack (c <> "-" <> prettyVer lVer)+ Nothing -> T.unpack (prettyVer lVer) <> rev+ Just c -> T.unpack (c <> "-" <> prettyVer lVer) <> rev dim | lNoBindist && not lInstalled- && not b -- TODO: overloading dim and active ignores active- -- so we hack around it here+ && ( not versionFocus -- TODO: overloading dim and active ignores active+ || not b -- so we hack around it here+ ) = Brick.updateAttrMap (const dimAttrs) . Brick.withAttr (Brick.attrName "no-bindist") | otherwise = id hooray- | elem Latest lTag' && not lInstalled =+ | elem Latest lTag' && not lInstalled && not lNoBindist = Brick.withAttr Attributes.hoorayAttr+ | (_, RevUpdate) <- lRev+ = Brick.withAttr Attributes.hoorayAttr | otherwise = id- active = if b then Common.enableScreenReader (Common.ListItem lTool listIx) else id- in Brick.clickable (Common.ListItem lTool listIx) $ hooray $ active $ dim+ active = if b then Common.enableScreenReader (Common.ListItem t listIx) else id+ in Brick.clickable (Common.ListItem t listIx) $ hooray $ active $ dim ( marks- <+> Brick.padLeft (Pad 2)- ( minHSize 6- (printTool lTool)- )- <+> minHSize minVerSize (Brick.str ver)+ <+> Brick.padLeft (Pad 1) (minHSize minVerSize (Brick.str ver)) <+> (let l = mapMaybe printTag $ sort lTag' in Brick.padLeft (Pad 1) $ minHSize minTagSize $ if null l then Brick.emptyWidget@@ -133,11 +164,12 @@ printTag Experimental = Just $ Brick.withAttr Attributes.latestNightlyAttr $ Brick.str "experimental" printTag (UnknownTag t) = Just $ Brick.str t - printTool Cabal = Brick.str "cabal"- printTool GHC = Brick.str "GHC"- printTool GHCup = Brick.str "GHCup"- printTool HLS = Brick.str "HLS"- printTool Stack = Brick.str "Stack"+ printTool (Tool "cabal") = Brick.str "cabal"+ printTool (Tool "ghc") = Brick.str "GHC"+ printTool (Tool "ghcup") = Brick.str "GHCup"+ printTool (Tool "hls") = Brick.str "HLS"+ printTool (Tool "stack") = Brick.str "Stack"+ printTool (Tool t) = Brick.str t printNotes ListResult {..} = (if hlsPowered then [Brick.withAttr Attributes.hlsPoweredAttr $ Brick.str "hls-powered"] else mempty@@ -147,9 +179,3 @@ Nothing -> mempty Just d -> [Brick.withAttr Attributes.dayAttr $ Brick.str (show d)]) - minHSize s' = Brick.hLimit s' . Brick.vLimit 1 . (<+> Brick.fill ' ')--instance SectionList.ListItemSectionNameIndex Common.Name where- getListItemSectionNameIndex = \case- Common.ListItem tool ix -> Just (Common.Singular tool, ix)- _ -> Nothing
− lib-tui/GHCup/Brick/Widgets/SectionList.hs
@@ -1,206 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE ViewPatterns #-}-{-# OPTIONS_GHC -Wno-unused-record-wildcards #-}-{-# OPTIONS_GHC -Wno-unused-matches #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE InstanceSigs #-}--{- A general system for lists with sections--Consider this code as private. GenericSectionList should not be used directly as the FocusRing should be align with the Vector containing-the elements, otherwise you'd be focusing on a non-existent widget with unknown result (In theory the code is safe unless you have an empty section list).--- To build a SectionList use the safe constructor sectionList-- To access sections use the lens provider sectionL and the name of the section you'd like to access-- You can modify Brick.Widget.List.GenericList within GenericSectionList via sectionL but do not- modify the vector length---}---module GHCup.Brick.Widgets.SectionList where---import Brick- ( BrickEvent(VtyEvent, MouseDown),- EventM,- Size(..),- Widget(..),- ViewportType (Vertical),- (<=>))-import qualified Brick-import Brick.Widgets.Border ( hBorder)-import qualified Brick.Widgets.List as L-import Brick.Focus (FocusRing)-import qualified Brick.Focus as F-import Data.Function ( (&))-import Data.Maybe ( fromMaybe )-import Data.Vector ( Vector )-import qualified GHCup.Brick.Common as Common-import Prelude hiding ( appendFile )--import qualified Graphics.Vty as Vty-import qualified Data.Vector as V--import Optics.TH (makeLensesFor)-import Optics.State (use)-import Optics.State.Operators ( (%=), (<%=))-import Optics.Operators ((.~), (^.))-import Optics.Lens (Lens', lens)--data GenericSectionList n t e- = GenericSectionList- { sectionListFocusRing :: FocusRing n -- ^ The FocusRing for all sections- , sectionListElements :: !(Vector (L.GenericList n t e)) -- ^ A vector of brick's built-in list- , sectionListName :: n -- ^ The section list name- }--makeLensesFor [("sectionListFocusRing", "sectionListFocusRingL"), ("sectionListElements", "sectionListElementsL"), ("sectionListName", "sectionListNameL")] ''GenericSectionList--type SectionList n e = GenericSectionList n V.Vector e---- | To support selection by mouse click we need to obtain section name and item--- index from the name of the item that got clicked. This helper class is to get that-class ListItemSectionNameIndex n where- getListItemSectionNameIndex :: n -> Maybe (n, Int)---- | Build a SectionList from nonempty list. If empty we could not defined sectionL lenses.-sectionList :: Foldable t- => n -- The name of the section list- -> [(n, t e)] -- a list of tuples (section name, collection of elements)- -> Int- -> GenericSectionList n t e-sectionList name elements height- = GenericSectionList- { sectionListFocusRing = F.focusRing [section_name | (section_name, _) <- elements]- , sectionListElements = V.fromList [L.list section_name els height | (section_name, els) <- elements]- , sectionListName = name- }--- | This lens constructor, takes a name and looks if a section has such a name.--- Used to dispatch events to sections. It is a partial function only meant to--- be used with the FocusRing inside GenericSectionList-sectionL :: Eq n => n -> Lens' (GenericSectionList n t e) (L.GenericList n t e)-sectionL section_name = lens g s- where is_section_name = (== section_name) . L.listName- g section_list =- let elms = section_list ^. sectionListElementsL- zeroth = elms V.! 0 -- TODO: This crashes for empty vectors.- in fromMaybe zeroth (V.find is_section_name elms)- s gl@(GenericSectionList _ elms _) list =- case V.findIndex is_section_name elms of- Nothing -> gl- Just i -> let new_elms = V.update elms (V.fromList [(i, list)])- in gl & sectionListElementsL .~ new_elms--moveDown :: (L.Splittable t, Ord n, Foldable t) => EventM n (GenericSectionList n t e) ()-moveDown = do- ring <- use sectionListFocusRingL- case F.focusGetCurrent ring of- Nothing -> pure ()- Just l -> do -- If it is the last element, move to the first element of the next focus; else, just handle regular list event.- current_list <- use (sectionL l)- let current_idx = L.listSelected current_list- list_length = current_list & length- if current_idx == Just (list_length - 1)- then do- new_focus <- sectionListFocusRingL <%= F.focusNext- case F.focusGetCurrent new_focus of- Nothing -> pure () -- |- Optic.Zoom.zoom doesn't typecheck but Lens.Micro.Mtl.zoom does. It is re-exported by Brick- Just new_l -> Common.zoom (sectionL new_l) (Brick.modify L.listMoveToBeginning)- else Common.zoom (sectionL l) $ Brick.modify L.listMoveDown--moveUp :: (L.Splittable t, Ord n, Foldable t) => EventM n (GenericSectionList n t e) ()-moveUp = do- ring <- use sectionListFocusRingL- case F.focusGetCurrent ring of- Nothing -> pure ()- Just l -> do -- If it is the first element, move to the last element of the prev focus; else, just handle regular list event.- current_list <- use (sectionL l)- let current_idx = L.listSelected current_list- if current_idx == Just 0- then do- new_focus <- sectionListFocusRingL <%= F.focusPrev- case F.focusGetCurrent new_focus of- Nothing -> pure ()- Just new_l -> Common.zoom (sectionL new_l) (Brick.modify L.listMoveToEnd)- else Common.zoom (sectionL l) $ Brick.modify L.listMoveUp--sectionListSelectItem :: (L.Splittable t, Eq n, ListItemSectionNameIndex n, Foldable t) => n -> EventM n (GenericSectionList n t e) ()-sectionListSelectItem selectedItem = case getListItemSectionNameIndex selectedItem of- Nothing -> pure ()- Just (secName, ix) -> do- sectionListFocusRingL %= F.focusSetCurrent secName- Common.zoom (sectionL secName) (Brick.modify $ L.listMoveTo ix)---- | Handle events for list cursor movement. Events handled are:------ * Up (up arrow key). If first element of section, then jump prev section--- * Down (down arrow key). If last element of section, then jump next section--- * Page Up (PgUp)--- * Page Down (PgDown)--- * Go to next section (Tab)--- * Go to prev section (BackTab)--- * Select an element via Mouse left click-handleGenericListEvent :: (Foldable t, L.Splittable t, Ord n, ListItemSectionNameIndex n)- => BrickEvent n a- -> EventM n (GenericSectionList n t e) ()-handleGenericListEvent (VtyEvent (Vty.EvResize _ _)) = pure ()-handleGenericListEvent (VtyEvent (Vty.EvKey (Vty.KChar '\t') [])) = sectionListFocusRingL %= F.focusNext-handleGenericListEvent (VtyEvent (Vty.EvKey Vty.KBackTab [])) = sectionListFocusRingL %= F.focusPrev-handleGenericListEvent (MouseDown n Vty.BLeft _ _) = sectionListSelectItem n-handleGenericListEvent (MouseDown _ Vty.BScrollDown _ _) = moveDown-handleGenericListEvent (MouseDown _ Vty.BScrollUp _ _) = moveUp-handleGenericListEvent (VtyEvent (Vty.EvKey Vty.KDown [])) = moveDown-handleGenericListEvent (VtyEvent (Vty.EvKey Vty.KUp [])) = moveUp-handleGenericListEvent (VtyEvent ev) = do- ring <- use sectionListFocusRingL- case F.focusGetCurrent ring of- Nothing -> pure ()- Just l -> Common.zoom (sectionL l) $ L.handleListEvent ev-handleGenericListEvent _ = pure ()---- This re-uses Brick.Widget.List.renderList-renderSectionList :: forall n t e . (Traversable t, Ord n, Show n, Eq n, L.Splittable t, Semigroup (t e))- => (Int -> Bool -> e -> Widget n) -- ^ Rendering function of the list element, True for the selected element- -> Bool -- ^ Whether the section list has focus- -> GenericSectionList n t e -- ^ The section list to render- -> Widget n-renderSectionList renderElem sectionFocus ge@(GenericSectionList focus elms slName) =- Brick.Widget Brick.Greedy Brick.Greedy $ Brick.render $ Brick.viewport slName Brick.Vertical $- V.ifoldl' (\(!accWidget) !i list ->- let hasFocusList = sectionIsFocused list- makeVisible = if hasFocusList then Brick.visibleRegion (Brick.Location (c, r)) (1, 1) else id- appendBorder = if i == 0 then id else (hBorder <=>)- newWidget = appendBorder (makeVisible $ renderInnerList hasFocusList list)- in accWidget <=> newWidget- )- Brick.emptyWidget- elms- where- -- A section is focused if the whole thing is focused, and the inner list has focus- sectionIsFocused :: L.GenericList n t e -> Bool- sectionIsFocused l = sectionFocus && (Just (L.listName l) == F.focusGetCurrent focus)-- renderInnerList :: Bool -> L.GenericList n t e -> Widget n- renderInnerList hasFocus l = Brick.vLimit (length l) $ L.renderListWithIndex (\i b -> renderElem i (b && hasFocus)) hasFocus l-- -- compute the location to focus on within the active section- (c, r) :: (Int, Int) = case sectionListSelectedElement ge of- Nothing -> (0, 0)- Just (selElIx, _) -> (0, selElIx)----- | Equivalent to listSelectedElement-sectionListSelectedElement :: (Eq n, L.Splittable t, Traversable t, Semigroup (t e)) => GenericSectionList n t e -> Maybe (Int, e)-sectionListSelectedElement generic_section_list = do- current_focus <- generic_section_list ^. sectionListFocusRingL & F.focusGetCurrent- let current_section = generic_section_list ^. sectionL current_focus- L.listSelectedElement current_section
+ lib-tui/GHCup/Brick/Widgets/ToolInfo.hs view
@@ -0,0 +1,108 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# OPTIONS_GHC -Wno-unused-record-wildcards #-}+{-# OPTIONS_GHC -Wno-unused-matches #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ViewPatterns #-}++module GHCup.Brick.Widgets.ToolInfo where++import GHCup.Brick.Widgets.Navigation ( BrickInternalState )+import GHCup.Prelude ( decUTF8Safe )+import GHCup.Types ( KeyBindings (..) )+import GHCup.Types.Optics++import qualified GHCup.Brick.Common as Common++import Brick+ ( Padding (Max), Widget (..), (<+>), (<=>) )+import Brick.Widgets.Center ( center )+import Data.Maybe ( fromMaybe )+import Optics ( to, (%), (^.) )+import Prelude hiding ( appendFile )+import Text.PrettyPrint.HughesPJClass ( prettyShow )+import URI.ByteString ( serializeURIRef' )++import qualified Brick+import qualified Brick.Widgets.List as L+import qualified Data.Text as T+++mkTextBox :: [Widget Common.Name] -> Widget Common.Name+mkTextBox = Brick.hLimitPercent 70 . Brick.vBox . fmap (Brick.padRight Brick.Max)++draw :: BrickInternalState -> KeyBindings -> Widget Common.Name+draw (L.listSelectedElement -> (Just (_, (tool, (Just td, _))))) (KeyBindings {..}) =+ Common.frontwardLayer (T.pack (prettyShow tool) <> " details")+ $ Brick.vBox [+ center $ Brick.hLimitPercent 70 $+ (Brick.vBox . fmap (Brick.padRight (Brick.Pad 2))) [+ Brick.hBox [+ Brick.txt "Description: "+ ],+ Brick.hBox [+ Brick.txt "Homepage: "+ ],+ Brick.hBox [+ Brick.txt "Repository: "+ ],+ Brick.hBox [+ Brick.txt "Author: "+ ],+ Brick.hBox [+ Brick.txt "Maintainer: "+ ],+ Brick.hBox [+ Brick.txt "Contact: "+ ],+ Brick.hBox [+ Brick.txt "License: "+ ]+ ]+ <+>+ (Brick.vBox . fmap (Brick.padRight Brick.Max)) [+ Brick.hBox [+ Brick.txt (td ^. toolDescription)+ ],+ Brick.hBox [+ td ^. toolHomepage % to maybeURI+ ],+ Brick.hBox [+ td ^. toolRepository % to maybeURI+ ],+ Brick.hBox [+ Brick.txt (td ^. toolAuthor % to maybeString)+ ],+ Brick.hBox [+ Brick.txt (td ^. toolMaintainer % to maybeString)+ ],+ Brick.hBox [+ Brick.txt (td ^. toolContact % to maybeString)+ ],+ Brick.hBox [+ Brick.txt (td ^. toolLicense % to maybeString)+ ]+ ]+ ]+ <=> Brick.hBox [Brick.txt "Press " <+> Common.keyToWidget bQuit <+> Brick.txt " to return to Navigation"]+ where+ maybeString = T.pack . fromMaybe "-- Not specified --"+ maybeURI = maybe (Brick.txt "-- Not specified --")+ (\url -> let link = decUTF8Safe . serializeURIRef' $ url+ in Brick.hyperlink link . Brick.txt $ link+ )+draw _ (KeyBindings {..}) =+ Common.frontwardLayer "Tool details"+ $ Brick.vBox [+ center $+ mkTextBox [+ Brick.hBox [+ Brick.txt "No details available"+ ]+ ]+ ] <=>+ Brick.hBox [Brick.txt "Press " <+> Common.keyToWidget bQuit <+> Brick.txt " to return to Navigation"]
lib-tui/GHCup/Brick/Widgets/Tutorial.hs view
@@ -42,12 +42,12 @@ Brick.hBox [ Brick.txt "This symbol " , Brick.withAttr Attributes.installedAttr (Brick.str Common.installedSign)- , Brick.txtWrap " means that the tool is installed but not in used"+ , Brick.txtWrap " means that the tool is installed but not in use" ] , Brick.hBox [ Brick.txt "This symbol " , Brick.withAttr Attributes.setAttr (Brick.str Common.setSign)- , Brick.txtWrap " means that the tool is installed and in used"+ , Brick.txtWrap " means that the tool is installed and in use" ] , Brick.hBox [ Brick.txt "This symbol "@@ -59,7 +59,7 @@ , mkTextBox [ Brick.hBox [ Brick.withAttr Attributes.recommendedAttr $ Brick.str "recommended"- , Brick.txtWrap " tag is based on community adoption, known bugs, etc... So It makes this version the least experimental"+ , Brick.txtWrap " tag is based on community adoption, known bugs, etc... So it makes this version the least experimental" ] , Brick.hBox [ Brick.withAttr Attributes.latestAttr $ Brick.str "latest"@@ -71,9 +71,8 @@ , Brick.withAttr Attributes.setAttr (Brick.str Common.setSign) , Brick.txt ") hls" ]- , Brick.txtWrap "base-X.Y.Z.W tag is the minimun version of the base package admited in such ghc version"+ , Brick.txtWrap "base-X.Y.Z.W tag is the minimum version of the base package admitted in such ghc version" ] , Brick.txt " " ])- <=> (Brick.padRight Brick.Max $- Brick.txt "Press " <+> Common.keyToWidget exitKey <+> Brick.txt " to exit the tutorial")+ <=> Brick.padRight Brick.Max ( Brick.txt "Press " <+> Common.keyToWidget exitKey <+> Brick.txt " to exit the tutorial")
lib-tui/GHCup/BrickMain.hs view
@@ -15,10 +15,10 @@ module GHCup.BrickMain where -import GHCup.List ( ListResult (..))+import GHCup.Command.List ( ListResult (..)) import GHCup.Types- ( Settings(noColor), Tool (GHC),- AppState(ghcupInfo, settings, keyBindings, loggerConfig), KeyBindings(..) )+ ( Settings(noColor),+ AppState(ghcupInfo, settings, keyBindings, loggerConfig), KeyBindings(..), Tool(..) ) import GHCup.Prelude.Logger ( logError ) import qualified GHCup.Brick.Actions as Actions import qualified GHCup.Brick.Common as Common@@ -26,12 +26,11 @@ import qualified GHCup.Brick.Attributes as Attributes import qualified GHCup.Brick.BrickState as AppState import qualified GHCup.Brick.Widgets.Menus.Context as ContextMenu-import qualified GHCup.Brick.Widgets.SectionList as Navigation-import qualified GHCup.Brick.Widgets.Menus.AdvanceInstall as AdvanceInstall+import qualified GHCup.Brick.Widgets.Menus.AdvancedInstall as AdvancedInstall import qualified GHCup.Brick.Widgets.Menus.CompileGHC as CompileGHC import GHCup.Brick.Widgets.Menu (MenuKeyBindings(..))+import qualified Brick.Widgets.List as L import qualified Brick-import qualified Graphics.Vty as Vty import Control.Monad.Reader ( ReaderT(runReaderT) ) import Data.Functor ( ($>) )@@ -41,6 +40,8 @@ import qualified Data.Text as T import qualified GHCup.Brick.Widgets.Menus.CompileHLS as CompileHLS+import Data.Maybe (isNothing)+import qualified Data.Map.Strict as M @@ -53,7 +54,7 @@ case eAppData of Right ad -> do let initial_list = Actions.constructList ad Common.defaultAppSettings Nothing- current_element = Navigation.sectionListSelectedElement initial_list+ current_element = L.listSelectedElement initial_list exit_key = let KeyBindings {..} = keyBindings s in MenuKeyBindings { mKbUp = bUp, mKbDown = bDown, mKbQuit = bQuit}@@ -61,25 +62,32 @@ Nothing -> do flip runReaderT s $ logError "Error building app state: empty ResultList" exitWith $ ExitFailure 2- Just (_, e) ->- let initapp =- BrickApp.app- (Attributes.defaultAttributes $ noColor $ settings s)- (Attributes.dimAttributes $ noColor $ settings s)- installedGHCs = fmap lVer $- filter (\(ListResult {..}) -> lInstalled && lTool == GHC && lCross == Nothing) (Common._lr ad)- initstate =- AppState.BrickState ad- Common.defaultAppSettings- initial_list- (ContextMenu.create e exit_key)- (AdvanceInstall.create exit_key)- (CompileGHC.create exit_key installedGHCs)- (CompileHLS.create exit_key installedGHCs)- (keyBindings s)- Common.Navigation- in Brick.defaultMain initapp initstate- $> ()+ Just (_, (t, (td, vlr))) ->+ case L.listSelectedElement vlr of+ Nothing -> do+ flip runReaderT s $ logError "Error building app state: empty ResultList"+ exitWith $ ExitFailure 2+ Just (_, lr) ->+ let initapp =+ BrickApp.app+ (Attributes.defaultAttributes $ noColor $ settings s)+ (Attributes.dimAttributes $ noColor $ settings s)+ installedGHCs = maybe [] (fmap lVer) $ do+ (_, lr') <- M.lookup (Tool "ghc") $ Common._lr ad+ pure $ filter (\(ListResult {..}) -> lInstalled && isNothing lCross) lr'+ initstate =+ AppState.BrickState ad+ Common.defaultAppSettings+ initial_list+ (ContextMenu.create (t, (td, lr)) exit_key)+ (AdvancedInstall.create exit_key)+ (CompileGHC.create exit_key installedGHCs)+ (CompileHLS.create exit_key installedGHCs)+ (keyBindings s)+ Common.Navigation+ False+ in Brick.defaultMain initapp initstate+ $> () Left e -> do flip runReaderT s $ logError $ "Error building app state: " <> T.pack (show e) exitWith $ ExitFailure 2
− lib/GHCup.hs
@@ -1,680 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-}--{-|-Module : GHCup-Description : GHCup installation functions-Copyright : (c) Julian Ospald, 2020-License : LGPL-3.0-Maintainer : hasufell@hasufell.de-Stability : experimental-Portability : portable--This module contains the main functions that correspond-to the command line interface, like installation, listing versions-and so on.--These are the entry points.--}-module GHCup (- module GHCup,- module GHCup.Cabal,- module GHCup.GHC,- module GHCup.HLS,- module GHCup.Stack,- module GHCup.List-) where---import GHCup.Cabal-import GHCup.GHC hiding ( GHCVer(..) )-import GHCup.HLS hiding ( HLSVer(..) )-import GHCup.Stack-import GHCup.List-import GHCup.Download-import GHCup.Errors-import GHCup.Platform-import GHCup.Types-import GHCup.Types.JSON ( )-import GHCup.Types.Optics-import GHCup.Utils-import GHCup.Prelude-import GHCup.Prelude.File-import GHCup.Prelude.Logger-import GHCup.Prelude.String.QQ-import GHCup.Version--import Conduit (sourceToList)-import Control.Applicative-import Control.Exception.Safe-import Control.Monad-#if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )-#endif-import Control.Monad.Reader-import Control.Monad.Trans.Resource- hiding ( throwM )-import Data.ByteString ( ByteString )-import Data.Either-import Data.List-import Data.Maybe-import Data.Versions hiding ( patch )-import GHC.IO.Exception-import Data.Variant.Excepts-import Optics-import Prelude hiding ( abs- , writeFile- )-import System.Environment-import System.FilePath-import System.IO.Error-import System.IO.Temp-import Text.Regex.Posix--import qualified Data.Text as T----- ---------------------- --[ Tool fetching ]--- ------------------------fetchToolBindist :: ( MonadFail m- , MonadMask m- , MonadCatch m- , MonadReader env m- , HasDirs env- , HasSettings env- , HasPlatformReq env- , HasGHCupInfo env- , HasLog env- , MonadResource m- , MonadIO m- , MonadUnliftIO m- )- => GHCTargetVersion- -> Tool- -> Maybe FilePath- -> Excepts- '[ DigestError- , ContentLengthError- , GPGError- , DownloadFailed- , NoDownload- , URIParseError- ]- m- FilePath-fetchToolBindist v t mfp = do- dlinfo <- liftE $ getDownloadInfo' t v- liftE $ downloadCached' dlinfo Nothing mfp---- ------------- --[ Nuke ]--- -----------------rmTool :: ( MonadReader env m- , HasDirs env- , HasLog env- , MonadFail m- , MonadMask m- , MonadUnliftIO m)- => ListResult- -> Excepts '[NotInstalled, UninstallFailed] m ()-rmTool ListResult {lVer, lTool, lCross} = do- let printRmTool = logInfo $ "removing " <> T.pack (show lTool) <> " version " <> prettyVer lVer- case lTool of- GHC -> do- let ghcTargetVersion = GHCTargetVersion lCross lVer- logInfo $ "removing " <> T.pack (show lTool) <> " version " <> tVerToText ghcTargetVersion- rmGHCVer ghcTargetVersion- HLS -> do- printRmTool- rmHLSVer lVer- Cabal -> do- printRmTool- liftE $ rmCabalVer lVer- Stack -> do- printRmTool- liftE $ rmStackVer lVer- GHCup -> do- printRmTool- lift rmGhcup---rmGhcupDirs :: ( MonadReader env m- , HasDirs env- , MonadIO m- , MonadUnliftIO m- , HasLog env- , MonadCatch m- , MonadMask m )- => m [FilePath]-rmGhcupDirs = do- Dirs- { baseDir- , binDir- , logsDir- , cacheDir- , recycleDir- , dbDir- , tmpDir- } <- getDirs-- let envFilePath = fromGHCupPath baseDir </> "env"-- confFilePath <- getConfigFilePath-- handleRm $ rmEnvFile envFilePath- handleRm $ rmConfFile confFilePath-- -- for xdg dirs, the order matters here- handleRm $ rmPathForcibly logsDir- handleRm $ rmPathForcibly tmpDir- handleRm $ rmPathForcibly cacheDir-- handleRm $ rmBinDir binDir- handleRm $ rmPathForcibly recycleDir- handleRm $ rmPathForcibly dbDir- when isWindows $ do- logInfo $ "removing " <> T.pack (fromGHCupPath baseDir </> "msys64")- handleRm $ rmPathForcibly (baseDir `appendGHCupPath` "msys64")-- handleRm $ removeEmptyDirsRecursive (fromGHCupPath baseDir)-- -- report files in baseDir that are left-over after- -- the standard location deletions above- hideErrorDef [doesNotExistErrorType] [] $ reportRemainingFiles (fromGHCupPath baseDir)-- where- handleRm :: (MonadReader env m, MonadCatch m, HasLog env, MonadIO m) => m () -> m ()- handleRm = handleIO (\e -> logDebug $ "Part of the cleanup action failed with error: " <> T.pack (displayException e) <> "\n"- <> "continuing regardless...")-- rmEnvFile :: (HasLog env, MonadReader env m, HasDirs env, MonadMask m, MonadIO m, MonadCatch m) => FilePath -> m ()- rmEnvFile enFilePath = do- logInfo "Removing Ghcup Environment File"- hideErrorDef [permissionErrorType] () $ rmFileForce enFilePath-- rmConfFile :: (HasLog env, MonadReader env m, HasDirs env, MonadMask m, MonadIO m, MonadCatch m) => FilePath -> m ()- rmConfFile confFilePath = do- logInfo "removing Ghcup Config File"- hideErrorDef [permissionErrorType] () $ rmFileForce confFilePath-- rmBinDir :: (MonadReader env m, HasDirs env, MonadMask m, MonadIO m, MonadCatch m) => FilePath -> m ()- rmBinDir binDir- | isWindows = removeDirIfEmptyOrIsSymlink binDir- | otherwise = do- isXDGStyle <- liftIO useXDG- when (not isXDGStyle) $- removeDirIfEmptyOrIsSymlink binDir-- reportRemainingFiles :: (MonadMask m, MonadIO m, MonadUnliftIO m) => FilePath -> m [FilePath]- reportRemainingFiles dir = do- remainingFiles <- runResourceT $ sourceToList $ getDirectoryContentsRecursiveUnsafe dir- let normalizedFilePaths = fmap normalise remainingFiles- let sortedByDepthRemainingFiles = sortBy (flip compareFn) normalizedFilePaths- let remainingFilesAbsolute = fmap (dir </>) sortedByDepthRemainingFiles-- pure remainingFilesAbsolute-- where- calcDepth :: FilePath -> Int- calcDepth = length . filter isPathSeparator-- compareFn :: FilePath -> FilePath -> Ordering- compareFn fp1 fp2 = compare (calcDepth fp1) (calcDepth fp2)----- ------------------- --[ Debug info ]--- ---------------------getDebugInfo :: ( Alternative m- , MonadFail m- , MonadReader env m- , HasDirs env- , HasLog env- , MonadCatch m- , MonadIO m- )- => Excepts- '[NoCompatiblePlatform , NoCompatibleArch , DistroNotFound]- m- DebugInfo-getDebugInfo = do- diDirs <- lift getDirs- let diChannels = fmap (\c -> (c, channelURL c)) [minBound..maxBound]- let diShimGenURL = shimGenURL- diArch <- lE getArchitecture- diPlatform <- liftE getPlatform- pure $ DebugInfo { .. }----- -------------------------- --[ GHCup upgrade etc ]--- ----------------------------- | Upgrade ghcup and place it in @~\/.ghcup\/bin\/ghcup@,--- if no path is provided.-upgradeGHCup :: ( MonadMask m- , MonadReader env m- , HasDirs env- , HasPlatformReq env- , HasGHCupInfo env- , HasSettings env- , MonadCatch m- , HasLog env- , MonadThrow m- , MonadFail m- , MonadResource m- , MonadIO m- , MonadUnliftIO m- )- => Maybe FilePath -- ^ full file destination to write ghcup into- -> Bool -- ^ whether to force update regardless- -- of currently installed version- -> Bool -- ^ whether to throw an error if ghcup is shadowed- -> Excepts- '[ CopyError- , DigestError- , ContentLengthError- , GPGError- , GPGError- , DownloadFailed- , NoDownload- , NoUpdate- , ToolShadowed- , URIParseError- ]- m- Version-upgradeGHCup mtarget force' fatal = do- GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo- let latestVer = _tvVersion $ fst (fromJust (getLatest dls GHCup))- upgradeGHCup' mtarget force' fatal latestVer----- | Upgrade ghcup and place it in @~\/.ghcup\/bin\/ghcup@,--- if no path is provided.-upgradeGHCup' :: ( MonadMask m- , MonadReader env m- , HasDirs env- , HasPlatformReq env- , HasGHCupInfo env- , HasSettings env- , MonadCatch m- , HasLog env- , MonadThrow m- , MonadFail m- , MonadResource m- , MonadIO m- , MonadUnliftIO m- )- => Maybe FilePath -- ^ full file destination to write ghcup into- -> Bool -- ^ whether to force update regardless- -- of currently installed version- -> Bool -- ^ whether to throw an error if ghcup is shadowed- -> Version- -> Excepts- '[ CopyError- , DigestError- , ContentLengthError- , GPGError- , GPGError- , DownloadFailed- , NoDownload- , NoUpdate- , ToolShadowed- , URIParseError- ]- m- Version-upgradeGHCup' mtarget force' fatal latestVer = do- Dirs {..} <- lift getDirs- lift $ logInfo "Upgrading GHCup..."- (Just ghcupPVPVer) <- pure $ pvpToVersion ghcUpVer ""- when (not force' && (latestVer <= ghcupPVPVer)) $ throwE NoUpdate- dli <- liftE $ getDownloadInfo GHCup latestVer- tmp <- fromGHCupPath <$> lift withGHCupTmpDir- let fn = "ghcup" <> exeExt- dlu <- lE $ parseURI' (_dlUri dli)- p <- liftE $ download dlu Nothing (Just (_dlHash dli)) (_dlCSize dli) tmp (Just fn) False- let destDir = takeDirectory destFile- destFile = fromMaybe (binDir </> fn) mtarget- lift $ logDebug $ "mkdir -p " <> T.pack destDir- liftIO $ createDirRecursive' destDir- lift $ logDebug $ "rm -f " <> T.pack destFile- lift $ hideError NoSuchThing $ recycleFile destFile- lift $ logDebug $ "cp " <> T.pack p <> " " <> T.pack destFile- copyFileE p destFile False- lift $ chmod_755 destFile-- liftIO (isInPath destFile) >>= \b -> unless b $- lift $ logWarn $ T.pack (takeFileName destFile) <> " is not in PATH! You have to add it in order to use ghcup."- liftIO (isShadowed destFile) >>= \case- Nothing -> pure ()- Just pa- | fatal -> throwE (ToolShadowed GHCup pa destFile latestVer)- | otherwise ->- lift $ logWarn $ T.pack $ prettyHFError (ToolShadowed GHCup pa destFile latestVer)-- pure latestVer----- assuming the current scheme of having just 1 ghcup bin, no version info is required.-rmGhcup :: ( MonadReader env m- , HasDirs env- , MonadIO m- , MonadCatch m- , HasLog env- , MonadMask m- , MonadUnliftIO m- )- => m ()-rmGhcup = do- Dirs { .. } <- getDirs- let ghcupFilename = "ghcup" <> exeExt- let ghcupFilepath = binDir </> ghcupFilename-- currentRunningExecPath <- liftIO getExecutablePath-- -- if paths do no exist, warn user, and continue to compare them, as is,- -- which should eventually fail and result in a non-standard install warning-- p1 <- handleIO' doesNotExistErrorType- (handlePathNotPresent currentRunningExecPath)- (liftIO $ canonicalizePath currentRunningExecPath)-- p2 <- handleIO' doesNotExistErrorType- (handlePathNotPresent ghcupFilepath)- (liftIO $ canonicalizePath ghcupFilepath)-- let areEqualPaths = equalFilePath p1 p2-- unless areEqualPaths $ logWarn $ nonStandardInstallLocationMsg currentRunningExecPath-- if isWindows- then do- -- since it doesn't seem possible to delete a running exe on windows- -- we move it to system temp dir, to be deleted at next reboot- tmp <- liftIO $ getCanonicalTemporaryDirectory >>= \t -> createTempDirectory t "ghcup"- logDebug $ "mv " <> T.pack ghcupFilepath <> " " <> T.pack (tmp </> "ghcup")- hideError UnsupportedOperation $- liftIO $ hideError NoSuchThing $- moveFile ghcupFilepath (tmp </> "ghcup")- else- -- delete it.- hideError doesNotExistErrorType $ rmFile ghcupFilepath-- where- handlePathNotPresent fp _err = do- logDebug $ "Error: The path does not exist, " <> T.pack fp- pure fp-- nonStandardInstallLocationMsg path = T.pack $- "current ghcup is invoked from a non-standard location: \n"- <> path <>- "\n you may have to uninstall it manually."---- ---------------- --[ Whereis ]--- --------------------- | Reports the binary location of a given tool:------ * for GHC, this reports: @~\/.ghcup\/ghc\/\<ver\>\/bin\/ghc@--- * for cabal, this reports @~\/.ghcup\/bin\/cabal-\<ver\>@--- * for hls, this reports @~\/.ghcup\/bin\/haskell-language-server-wrapper-\<ver\>@--- * for stack, this reports @~\/.ghcup\/bin\/stack-\<ver\>@--- * for ghcup, this reports the location of the currently running executable-whereIsTool :: ( MonadReader env m- , HasDirs env- , HasLog env- , MonadThrow m- , MonadFail m- , MonadIO m- , MonadCatch m- , MonadMask m- , MonadUnliftIO m- )- => Tool- -> GHCTargetVersion- -> Excepts '[NotInstalled] m FilePath-whereIsTool tool ver@GHCTargetVersion {..} = do- dirs <- lift getDirs-- case tool of- GHC -> do- whenM (lift $ fmap not $ ghcInstalled ver)- $ throwE (NotInstalled GHC ver)- bdir <- fromGHCupPath <$> lift (ghcupGHCDir ver)- pure (bdir </> "bin" </> ghcBinaryName ver)- Cabal -> do- whenM (lift $ fmap not $ cabalInstalled _tvVersion)- $ throwE (NotInstalled Cabal (GHCTargetVersion Nothing _tvVersion))- pure (binDir dirs </> "cabal-" <> T.unpack (prettyVer _tvVersion) <> exeExt)- HLS -> do- whenM (lift $ fmap not $ hlsInstalled _tvVersion)- $ throwE (NotInstalled HLS (GHCTargetVersion Nothing _tvVersion))- ifM (lift $ isLegacyHLS _tvVersion)- (pure (binDir dirs </> "haskell-language-server-wrapper-" <> T.unpack (prettyVer _tvVersion) <> exeExt))- $ do- bdir <- fromGHCupPath <$> lift (ghcupHLSDir _tvVersion)- pure (bdir </> "bin" </> "haskell-language-server-wrapper" <> exeExt)-- Stack -> do- whenM (lift $ fmap not $ stackInstalled _tvVersion)- $ throwE (NotInstalled Stack (GHCTargetVersion Nothing _tvVersion))- pure (binDir dirs </> "stack-" <> T.unpack (prettyVer _tvVersion) <> exeExt)- GHCup -> do- currentRunningExecPath <- liftIO getExecutablePath- liftIO $ canonicalizePath currentRunningExecPath----- | Doesn't work for cross GHC.-checkIfToolInstalled :: ( MonadIO m- , MonadReader env m- , HasDirs env- , MonadCatch m) =>- Tool ->- Version ->- m Bool-checkIfToolInstalled tool ver = checkIfToolInstalled' tool (mkTVer ver)---checkIfToolInstalled' :: ( MonadIO m- , MonadReader env m- , HasDirs env- , MonadCatch m) =>- Tool ->- GHCTargetVersion ->- m Bool-checkIfToolInstalled' tool ver =- case tool of- Cabal -> cabalInstalled (_tvVersion ver)- HLS -> hlsInstalled (_tvVersion ver)- Stack -> stackInstalled (_tvVersion ver)- GHC -> ghcInstalled ver- _ -> pure False----- --------------------------- --[ Garbage collection ]--- -----------------------------rmOldGHC :: ( MonadReader env m- , HasGHCupInfo env- , HasDirs env- , HasLog env- , MonadIO m- , MonadFail m- , MonadMask m- , MonadUnliftIO m- )- => Excepts '[NotInstalled, UninstallFailed] m ()-rmOldGHC = do- GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo- let oldGHCs = toListOf (ix GHC % getTagged Old % to fst) dls- ghcs <- lift $ fmap rights getInstalledGHCs- forM_ ghcs $ \ghc -> when (ghc `elem` oldGHCs) $ rmGHCVer ghc---rmUnsetTools :: ( MonadReader env m- , HasGHCupInfo env- , HasPlatformReq env- , HasDirs env- , HasLog env- , MonadIO m- , MonadFail m- , MonadMask m- , MonadUnliftIO m- )- => Excepts '[NotInstalled, UninstallFailed] m ()-rmUnsetTools = do- vers <- lift $ listVersions Nothing [ListInstalled True, ListSet False] False True (Nothing, Nothing)- forM_ vers $ \ListResult{..} -> case lTool of- GHC -> liftE $ rmGHCVer (GHCTargetVersion lCross lVer)- HLS -> liftE $ rmHLSVer lVer- Cabal -> liftE $ rmCabalVer lVer- Stack -> liftE $ rmStackVer lVer- GHCup -> pure ()---rmProfilingLibs :: ( MonadReader env m- , HasDirs env- , HasLog env- , MonadIO m- , MonadFail m- , MonadMask m- , MonadUnliftIO m- )- => m ()-rmProfilingLibs = do- ghcs <- fmap rights getInstalledGHCs-- let regexes :: [ByteString]- regexes = [[s|.*_p\.a$|], [s|.*\.p_hi$|]]-- forM_ regexes $ \regex ->- forM_ ghcs $ \ghc -> do- d <- ghcupGHCDir ghc- -- TODO: audit findFilesDeep- matches <- liftIO $ handleIO (\_ -> pure []) $ findFilesDeep- d- (makeRegexOpts compExtended- execBlank- regex- )- forM_ matches $ \m -> do- let p = fromGHCupPath d </> m- logDebug $ "rm " <> T.pack p- rmFile p----rmShareDir :: ( MonadReader env m- , HasDirs env- , HasLog env- , MonadIO m- , MonadFail m- , MonadMask m- , MonadUnliftIO m- )- => m ()-rmShareDir = do- ghcs <- fmap rights getInstalledGHCs- forM_ ghcs $ \ghc -> do- d <- ghcupGHCDir ghc- let p = d `appendGHCupPath` "share"- logDebug $ "rm -rf " <> T.pack (fromGHCupPath p)- rmPathForcibly p---rmHLSNoGHC :: ( MonadReader env m- , HasDirs env- , HasLog env- , MonadIO m- , MonadMask m- , MonadFail m- , MonadUnliftIO m- )- => Excepts '[NotInstalled, UninstallFailed] m ()-rmHLSNoGHC = do- Dirs {..} <- getDirs- ghcs <- fmap rights getInstalledGHCs- hlses <- fmap rights getInstalledHLSs- forM_ hlses $ \hls -> do- hlsGHCs <- fmap mkTVer <$> hlsGHCVersions' hls- let candidates = filter (`notElem` ghcs) hlsGHCs- if (length hlsGHCs - length candidates) <= 0- then rmHLSVer hls- else- forM_ candidates $ \ghc -> do- bins1 <- fmap (binDir </>) <$> hlsServerBinaries hls (Just $ _tvVersion ghc)- bins2 <- ifM (isLegacyHLS hls) (pure []) $ do- shs <- hlsInternalServerScripts hls (Just $ _tvVersion ghc)- bins <- hlsInternalServerBinaries hls (Just $ _tvVersion ghc)- libs <- hlsInternalServerLibs hls (_tvVersion ghc)- pure (shs ++ bins ++ libs)- forM_ (bins1 ++ bins2) $ \f -> do- logDebug $ "rm " <> T.pack f- rmFile f- pure ()---rmCache :: ( MonadReader env m- , HasDirs env- , HasLog env- , MonadIO m- , MonadMask m- )- => m ()-rmCache = do- Dirs {..} <- getDirs- contents <- liftIO $ listDirectory (fromGHCupPath cacheDir)- forM_ contents $ \f -> do- let p = fromGHCupPath cacheDir </> f- logDebug $ "rm " <> T.pack p- rmFile p---rmTmp :: ( MonadReader env m- , HasDirs env- , HasLog env- , MonadIO m- , MonadMask m- )- => m ()-rmTmp = do- ghcup_dirs <- liftIO getGHCupTmpDirs- forM_ ghcup_dirs $ \f -> do- logDebug $ "rm -rf " <> T.pack (fromGHCupPath f)- rmPathForcibly f--
+ lib/GHCup/Builder.hs view
@@ -0,0 +1,104 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}++module GHCup.Builder where++import GHCup.Errors+import GHCup.Prelude+import GHCup.Query.GHCupDirs+import GHCup.Types+import GHCup.Types.Optics++import Control.Exception.Safe+ ( Exception (displayException), handleIO, onException )+import Control.Monad+import Control.Monad.IO.Unlift ( MonadUnliftIO (withRunInIO) )+import Control.Monad.Reader ( MonadIO (..), MonadReader, MonadTrans (lift) )+import Data.Foldable ( foldlM )+import Data.List ( sort )+import Data.Variant.Excepts ( Excepts, onE_, throwE )+import System.FilePath ( pathSeparators )+import System.IO.Error ( doesNotExistErrorType )+import Text.Regex.Posix+ ( RegexMaker (makeRegexOpts), compIgnoreCase, execBlank )++import qualified Data.Text as T+import GHCup.System.Directory+++-- | Execute a build action while potentially cleaning up:+--+-- 1. the build directory, depending on the KeepDirs setting+runBuildAction :: ( MonadReader env m+ , HasDirs env+ , HasSettings env+ , MonadIOish m+ , HasLog env+ )+ => GHCupPath -- ^ build directory (cleaned up depending on Settings)+ -> Excepts e m a+ -> Excepts e m a+runBuildAction bdir action = do+ Settings {..} <- lift getSettings+ let exAction = do+ when (keepDirs == Never)+ $ rmBDir bdir+ v <-+ flip onException (lift exAction)+ $ onE_ exAction action+ when (keepDirs == Never || keepDirs == Errors) $ lift $ rmBDir bdir+ pure v++-- | Clean up the given directory if the action fails,+-- depending on the Settings.+cleanUpOnError :: forall e m a env .+ ( MonadReader env m+ , HasDirs env+ , HasSettings env+ , MonadIOish m+ , HasLog env+ )+ => GHCupPath -- ^ build directory (cleaned up depending on Settings)+ -> Excepts e m a+ -> Excepts e m a+cleanUpOnError bdir action = do+ Settings {..} <- lift getSettings+ let exAction = when (keepDirs == Never) $ rmBDir bdir+ flip onException (lift exAction) $ onE_ exAction action+++-- | Remove a build directory, ignoring if it doesn't exist and gracefully+-- printing other errors without crashing.+rmBDir :: (MonadReader env m, HasLog env, MonadIOish m) => GHCupPath -> m ()+rmBDir dir = withRunInIO (\run -> run $+ liftIO $ handleIO (\e -> run $ logWarn $+ "Couldn't remove build dir " <> T.pack (fromGHCupPath dir) <> ", error was: " <> T.pack (displayException e))+ $ hideError doesNotExistErrorType+ $ rmPathForcibly dir)+++++intoSubdir :: (MonadReader env m, HasLog env, MonadIOish m)+ => GHCupPath -- ^ unpacked tar dir+ -> TarDir -- ^ how to descend+ -> Excepts '[TarDirDoesNotExist] m GHCupPath+intoSubdir bdir tardir = case tardir of+ RealDir pr -> do+ whenM (fmap not . liftIO . doesDirectoryExist $ fromGHCupPath (bdir `appendGHCupPath` pr))+ (throwE $ TarDirDoesNotExist tardir)+ pure (bdir `appendGHCupPath` pr)+ RegexDir r -> do+ let rs = split (`elem` pathSeparators) r+ foldlM+ (\y x ->+ (handleIO (\_ -> pure []) . liftIO . findFiles (fromGHCupPath y) . regex $ x) >>= (\case+ [] -> throwE $ TarDirDoesNotExist tardir+ (p : _) -> pure (y `appendGHCupPath` p)) . sort+ )+ bdir+ rs+ where regex = makeRegexOpts compIgnoreCase execBlank+
− lib/GHCup/Cabal.hs
@@ -1,287 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedStrings #-}--{-|-Module : GHCup.Cabal-Description : GHCup installation functions for Cabal-Copyright : (c) Julian Ospald, 2020-License : LGPL-3.0-Maintainer : hasufell@hasufell.de-Stability : experimental-Portability : portable--}-module GHCup.Cabal where--import GHCup.Download-import GHCup.Errors-import GHCup.Types-import GHCup.Types.JSON ( )-import GHCup.Types.Optics-import GHCup.Utils-import GHCup.Prelude-import GHCup.Prelude.File-import GHCup.Prelude.Logger--import Control.Applicative-import Control.Exception.Safe-import Control.Monad-#if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )-#endif-import Control.Monad.Reader-import Control.Monad.Trans.Resource- hiding ( throwM )-import Data.Either-import Data.List-import Data.Ord-import Data.Maybe-import Data.Versions hiding ( patch )-import Data.Variant.Excepts-import Optics-import Prelude hiding ( abs- , writeFile- )-import Safe hiding ( at )-import System.FilePath-import System.IO.Error--import qualified Data.Text as T---- -------------------------- --[ Tool installation ]--- ------------------------------ | Like 'installCabalBin', except takes the 'DownloadInfo' as--- argument instead of looking it up from 'GHCupDownloads'.-installCabalBindist :: ( MonadMask m- , MonadCatch m- , MonadReader env m- , HasPlatformReq env- , HasDirs env- , HasSettings env- , HasLog env- , MonadResource m- , MonadIO m- , MonadUnliftIO m- , MonadFail m- )- => DownloadInfo- -> Version- -> InstallDir- -> Bool -- ^ Force install- -> Excepts- '[ AlreadyInstalled- , CopyError- , DigestError- , ContentLengthError- , GPGError- , DownloadFailed- , NoDownload- , NotInstalled- , UnknownArchive- , TarDirDoesNotExist- , ArchiveResult- , FileAlreadyExistsError- , URIParseError- ]- m- ()-installCabalBindist dlinfo ver installDir forceInstall = do- lift $ logDebug $ "Requested to install cabal version " <> prettyVer ver-- PlatformRequest {..} <- lift getPlatformReq- Dirs {..} <- lift getDirs-- -- check if we already have a regular cabal already installed- regularCabalInstalled <- lift $ cabalInstalled ver-- if- | not forceInstall- , regularCabalInstalled- , GHCupInternal <- installDir -> do- throwE $ AlreadyInstalled Cabal ver-- | forceInstall- , regularCabalInstalled- , GHCupInternal <- installDir -> do- lift $ logInfo "Removing the currently installed version first!"- liftE $ rmCabalVer ver-- | otherwise -> pure ()--- -- download (or use cached version)- dl <- liftE $ downloadCached dlinfo Nothing-- -- unpack- tmpUnpack <- lift withGHCupTmpDir- liftE $ cleanUpOnError tmpUnpack (unpackToDir (fromGHCupPath tmpUnpack) dl)- liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromGHCupPath tmpUnpack)-- -- the subdir of the archive where we do the work- workdir <- fromGHCupPath <$> maybe (pure tmpUnpack) (liftE . intoSubdir tmpUnpack) (view dlSubdir dlinfo)-- case installDir of- IsolateDir isoDir -> do -- isolated install- lift $ logInfo $ "isolated installing Cabal to " <> T.pack isoDir- liftE $ installCabalUnpacked workdir (IsolateDirResolved isoDir) ver forceInstall-- GHCupInternal -> do -- regular install- liftE $ installCabalUnpacked workdir (GHCupBinDir binDir) ver forceInstall----- | Install an unpacked cabal distribution.Symbol-installCabalUnpacked :: (MonadCatch m, HasLog env, MonadIO m, MonadReader env m)- => FilePath -- ^ Path to the unpacked cabal bindist (where the executable resides)- -> InstallDirResolved -- ^ Path to install to- -> Version- -> Bool -- ^ Force Install- -> Excepts '[CopyError, FileAlreadyExistsError] m ()-installCabalUnpacked path inst ver forceInstall = do- lift $ logInfo "Installing cabal"- let cabalFile = "cabal"- liftIO $ createDirRecursive' (fromInstallDir inst)- let destFileName = cabalFile- <> (case inst of- IsolateDirResolved _ -> ""- _ -> ("-" <>) . T.unpack . prettyVer $ ver- )- <> exeExt- let destPath = fromInstallDir inst </> destFileName-- copyFileE- (path </> cabalFile <> exeExt)- destPath- (not forceInstall)- lift $ chmod_755 destPath---- | Installs cabal into @~\/.ghcup\/bin/cabal-\<ver\>@ and--- creates a default @cabal -> cabal-x.y.z.q@ symlink for--- the latest installed version.-installCabalBin :: ( MonadMask m- , MonadCatch m- , MonadReader env m- , HasPlatformReq env- , HasGHCupInfo env- , HasDirs env- , HasSettings env- , HasLog env- , MonadResource m- , MonadIO m- , MonadUnliftIO m- , MonadFail m- )- => Version- -> InstallDir- -> Bool -- force install- -> Excepts- '[ AlreadyInstalled- , CopyError- , DigestError- , ContentLengthError- , GPGError- , DownloadFailed- , NoDownload- , NotInstalled- , UnknownArchive- , TarDirDoesNotExist- , ArchiveResult- , FileAlreadyExistsError- , URIParseError- ]- m- ()-installCabalBin ver installDir forceInstall = do- dlinfo <- liftE $ getDownloadInfo Cabal ver- installCabalBindist dlinfo ver installDir forceInstall--- ------------------ --[ Set cabal ]--- ---------------------- | Set the @~\/.ghcup\/bin\/cabal@ symlink.-setCabal :: ( MonadMask m- , MonadReader env m- , HasDirs env- , HasLog env- , MonadFail m- , MonadIO m- , MonadUnliftIO m)- => Version- -> Excepts '[NotInstalled] m ()-setCabal ver = do- let targetFile = "cabal-" <> T.unpack (prettyVer ver) <> exeExt-- -- symlink destination- Dirs {..} <- lift getDirs-- whenM (liftIO $ not <$> doesFileExist (binDir </> targetFile))- $ throwE- $ NotInstalled Cabal (GHCTargetVersion Nothing ver)-- let cabalbin = binDir </> "cabal" <> exeExt-- -- create link- let destL = targetFile- lift $ createLink destL cabalbin-- liftIO (isShadowed cabalbin) >>= \case- Nothing -> pure ()- Just pa -> lift $ logWarn $ T.pack $ prettyHFError (ToolShadowed Cabal pa cabalbin ver)-- pure ()--unsetCabal :: ( MonadMask m- , MonadReader env m- , HasDirs env- , MonadIO m)- => m ()-unsetCabal = do- Dirs {..} <- getDirs- let cabalbin = binDir </> "cabal" <> exeExt- hideError doesNotExistErrorType $ rmLink cabalbin--- ----------------- --[ Rm cabal ]--- --------------------- | Delete a cabal version. Will try to fix the @cabal@ symlink--- after removal (e.g. setting it to an older version).-rmCabalVer :: ( MonadMask m- , MonadReader env m- , HasDirs env- , MonadThrow m- , HasLog env- , MonadIO m- , MonadFail m- , MonadCatch m- , MonadUnliftIO m- )- => Version- -> Excepts '[NotInstalled] m ()-rmCabalVer ver = do- whenM (lift $ fmap not $ cabalInstalled ver) $ throwE (NotInstalled Cabal (GHCTargetVersion Nothing ver))-- cSet <- lift cabalSet-- Dirs {..} <- lift getDirs-- let cabalFile = "cabal-" <> T.unpack (prettyVer ver) <> exeExt- lift $ hideError doesNotExistErrorType $ recycleFile (binDir </> cabalFile)-- when (Just ver == cSet) $ do- cVers <- lift $ fmap rights getInstalledCabals- case headMay . sortBy (comparing Down) $ cVers of- Just latestver -> setCabal latestver- Nothing -> lift $ rmLink (binDir </> "cabal" <> exeExt)
lib/GHCup/CabalConfig.hs view
@@ -1,28 +1,29 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE UndecidableInstances #-} module GHCup.CabalConfig (getStoreDir) where -import Data.ByteString (ByteString)-import Data.List.NonEmpty (NonEmpty)-import Data.Map (Map)-import System.Directory (getAppUserDataDirectory, doesDirectoryExist, getXdgDirectory, XdgDirectory(XdgConfig))-import System.Environment (lookupEnv)-import System.FilePath ((</>))+import GHCup.System.Directory -import qualified Data.ByteString as BS-import qualified Data.Map.Strict as M-import qualified Distribution.CabalSpecVersion as C-import qualified Distribution.FieldGrammar as C-import qualified Distribution.FieldGrammar.Parsec as C-import qualified Distribution.Fields as C-import qualified Distribution.Fields.LexerMonad as C-import qualified Distribution.Parsec as C-import qualified Distribution.Utils.Generic as C-import qualified Text.Parsec as P+import Data.ByteString ( ByteString )+import Data.List.NonEmpty ( NonEmpty )+import Data.Map ( Map )+import System.Environment ( lookupEnv )+import System.FilePath ( (</>) ) -import Data.Foldable (for_)+import qualified Data.ByteString as BS+import qualified Data.Map.Strict as M+import qualified Distribution.CabalSpecVersion as C+import qualified Distribution.FieldGrammar as C+import qualified Distribution.FieldGrammar.Parsec as C+import qualified Distribution.Fields as C+import qualified Distribution.Fields.LexerMonad as C+import qualified Distribution.Parsec as C+import qualified Distribution.Utils.Generic as C+import qualified Text.Parsec as P++import Data.Foldable ( for_ ) import Distribution.Parsec.Error @@ -55,8 +56,8 @@ appDir <- getAppUserDataDirectory "cabal" isXdg <- not <$> doesDirectoryExist appDir if | Just dir <- cabalDirVar -> pure dir- | isXdg -> getXdgDirectory XdgConfig "cabal"- | otherwise -> pure appDir+ | isXdg -> getXdgDirectory XdgConfig "cabal"+ | otherwise -> pure appDir -------------------------------------------------------------------------------
+ lib/GHCup/Command/Compile/GHC.hs view
@@ -0,0 +1,834 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}++{-|+Module : GHCup.Command.Compile.GHC+Description : GHCup compile GHC+Copyright : (c) Julian Ospald, 2020+License : LGPL-3.0+Maintainer : hasufell@hasufell.de+Stability : experimental+Portability : portable+-}+module GHCup.Command.Compile.GHC where+++import GHCup.Builder+import GHCup.Command.Install+import GHCup.Command.Install.LowLevel+import GHCup.Command.Rm+import GHCup.Command.Set+import GHCup.Download+import GHCup.Errors+import GHCup.Prelude+import GHCup.Prelude.MegaParsec+import GHCup.Prelude.Process+import GHCup.Prelude.String.QQ+import GHCup.Prelude.Version.QQ+import GHCup.Query.DB+import GHCup.Query.GHCupDirs+import GHCup.System.Cmd+import GHCup.System.Directory+import GHCup.Types+import GHCup.Types.Optics+import GHCup.Unpack++import Control.Applicative+import Control.Concurrent ( threadDelay )+import Control.Exception.Safe+import Control.Monad+#if !MIN_VERSION_base(4,13,0)+import Control.Monad.Fail ( MonadFail )+#endif+import Control.Monad.Reader+import Control.Monad.Trans.Resource hiding ( throwM )+import Data.ByteString ( ByteString )+import Data.Either+import Data.List+import Data.Maybe+import Data.String ( fromString )+import Data.Text ( Text )+import Data.Time.Clock+import Data.Time.Format.ISO8601+import Data.Variant.Excepts+import Data.Versions hiding ( patch )+import GHC.IO.Exception+import Language.Haskell.TH+import Language.Haskell.TH.Syntax ( Quasi (qAddDependentFile) )+import Optics+import Prelude hiding ( abs, writeFile )+import System.Environment+import System.FilePath+import System.FilePattern ((?==))+import System.IO.Error+import Text.PrettyPrint.HughesPJClass ( prettyShow )+import Text.Regex.Posix+import URI.ByteString++import qualified Crypto.Hash.SHA256 as SHA256+import qualified Data.ByteString as B+import qualified Data.ByteString.Base16 as B16+import qualified Data.ByteString.Lazy as BL+import qualified Data.Map.Strict as Map+import qualified Data.Text as T+import qualified Data.Text.Encoding as E+import qualified Data.Text.IO as T+import qualified System.FilePath.Posix as Posix+import qualified Text.Megaparsec as MP+++data GHCVer+ = SourceDist Version+ | GitDist GitBranch+ | RemoteDist URI+ deriving (Eq, Show)+++++ -------------------------+ --[ Tool installation ]--+ -------------------------+++++mergeGHCFileTree :: ( MonadReader env m+ , HasPlatformReq env+ , HasDirs env+ , HasSettings env+ , MonadThrow m+ , HasLog env+ , MonadIO m+ , MonadUnliftIO m+ , MonadMask m+ , MonadResource m+ , MonadFail m+ )+ => GHCupPath -- ^ Path to the root of the tree+ -> InstallDirResolved -- ^ Path to install to+ -> TargetVersion -- ^ The GHC version+ -> Bool -- ^ Force install+ -> Excepts '[MergeFileTreeError] m ()+mergeGHCFileTree root inst tver forceInstall+ | isWindows = do+ liftE $ mergeFileTree root inst ghc tver (\source dest -> do+ mtime <- liftIO $ ifM (pathIsSymbolicLink source) (pure Nothing) (Just <$> getModificationTime source)+ when forceInstall $ hideError doesNotExistErrorType $ hideError InappropriateType $ recycleFile dest+ liftIO $ moveFilePortable source dest+ forM_ mtime $ liftIO . setModificationTime dest+ )+ False+ | otherwise = do+ liftE $ mergeFileTree root+ inst+ ghc+ tver+ (\f t -> liftIO $ do+ mtime <- ifM (pathIsSymbolicLink f) (pure Nothing) (Just <$> getModificationTime f)+ install f t (not forceInstall)+ forM_ mtime $ setModificationTime t)+ False+++++ ---------------+ --[ Compile ]--+ ---------------+++-- | Compile a GHC from source. This behaves wrt symlinks and installation+-- the same as 'installGHCBin'.+compileGHC :: ( MonadMask m+ , MonadReader env m+ , HasDirs env+ , HasPlatformReq env+ , HasGHCupInfo env+ , HasSettings env+ , MonadThrow m+ , MonadResource m+ , HasLog env+ , MonadIO m+ , MonadUnliftIO m+ , MonadFail m+ )+ => GHCVer+ -> Maybe Text -- ^ cross target+ -> Maybe [VersionPattern]+ -> Either Version FilePath -- ^ GHC version to bootstrap with+ -> Maybe (Either Version FilePath) -- ^ GHC version to compile hadrian with+ -> Maybe Int -- ^ jobs+ -> Maybe FilePath -- ^ build config+ -> Maybe (Either FilePath [URI]) -- ^ patches+ -> [String] -- ^ additional args to ./configure+ -> Maybe String -- ^ build flavour+ -> Maybe BuildSystem+ -> InstallDir+ -> Maybe [String]+ -> Excepts+ '[ AlreadyInstalled+ , BuildFailed+ , DigestError+ , ContentLengthError+ , GPGError+ , DownloadFailed+ , GHCupSetError+ , NoDownload+ , NotFoundInPATH+ , PatchFailed+ , UnknownArchive+ , TarDirDoesNotExist+ , NotInstalled+ , DirNotEmpty+ , ArchiveResult+ , FileDoesNotExistError+ , HadrianNotFound+ , InvalidBuildConfig+ , ProcessError+ , CopyError+ , BuildFailed+ , UninstallFailed+ , MergeFileTreeError+ , URIParseError+ , FileAlreadyExistsError+ , ParseError+ , NoInstallInfo+ , MalformedInstallInfo+ ]+ m+ TargetVersion+compileGHC targetGhc crossTarget vps bstrap hghc jobs mbuildConfig patches aargs buildFlavour buildSystem installDir installTargets+ = do+ pfreq@PlatformRequest { .. } <- lift getPlatformReq+ GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo++ (workdir, tmpUnpack, tver, ov) <- case targetGhc of+ -- unpack from version tarball+ SourceDist ver -> do+ let tver = mkTVer ver+ lift $ logDebug $ "Requested to compile: " <> prettyVer ver <> " with " <> either prettyVer T.pack bstrap++ -- download source tarball+ dlInfo <- (?? NoDownload (unsafeToTargetVersionReq tver) ghc (Just pfreq)) $ do+ (_, vi) <- preview (_GHCupDownloads % ix ghc % toolVersionsL % ix tver % revisionSpecL % mapLast) dls+ preview (viSourceDL % _Just) vi+ dl <- liftE $ downloadCached dlInfo Nothing++ -- unpack+ tmpUnpack <- lift mkGhcupTmpDir+ liftE $ cleanUpOnError tmpUnpack (unpackToDir (fromGHCupPath tmpUnpack) dl)+ liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform $ fromGHCupPath tmpUnpack++ workdir <- maybe (pure tmpUnpack)+ (liftE . intoSubdir tmpUnpack)+ (view dlSubdir dlInfo)+ liftE $ applyAnyPatch patches (fromGHCupPath workdir)++ ov <- case vps of+ Just vps' -> fmap Just $ expandVersionPattern (Just ver) "" "" "" "" vps'+ Nothing -> pure Nothing++ pure (workdir, tmpUnpack, Just (TargetVersion crossTarget ver), ov)++ RemoteDist uri -> do+ lift $ logDebug $ "Requested to compile (from uri): " <> T.pack (show uri)++ -- download source tarball+ tmpDownload <- lift withGHCupTmpDir+ tmpUnpack <- lift mkGhcupTmpDir+ tar <- liftE $ download uri Nothing Nothing Nothing (fromGHCupPath tmpDownload) Nothing False+ (bf, ver) <- liftE $ cleanUpOnError @'[UnknownArchive, ArchiveResult, ProcessError] tmpUnpack $ do+ liftE $ unpackToDir (fromGHCupPath tmpUnpack) tar+ let regex = [s|^(.*/)*boot$|] :: B.ByteString+ [bootFile] <- liftIO $ findFilesDeep+ tmpUnpack+ (makeRegexOpts compExtended+ execBlank+ regex+ )+ ver <- liftE $ catchAllE @_ @'[ProcessError, ParseError, NotFoundInPATH] @'[] (\_ -> pure Nothing) $ fmap Just $ getGHCVer+ (appendGHCupPath tmpUnpack (takeDirectory bootFile))+ pure (bootFile, ver)++ let workdir = appendGHCupPath tmpUnpack (takeDirectory bf)++ ov <- case vps of+ Just vps' -> fmap Just $ expandVersionPattern ver "" "" "" "" vps'+ Nothing -> pure Nothing++ let tver = TargetVersion crossTarget <$> ver+ pure (workdir, tmpUnpack, tver, ov)++ -- clone from git+ GitDist GitBranch{..} -> do+ tmpUnpack <- lift mkGhcupTmpDir+ let git args = execLogged "git" ("--no-pager":args) (Just $ fromGHCupPath tmpUnpack) "git" Nothing+ (ver, ov) <- cleanUpOnError tmpUnpack $ reThrowAll @_ @'[PatchFailed, ProcessError, NotFoundInPATH, DigestError, ContentLengthError, DownloadFailed, GPGError] DownloadFailed $ do+ let rep = fromMaybe "https://gitlab.haskell.org/ghc/ghc.git" repo+ lift $ logInfo $ "Fetching git repo " <> T.pack rep <> " at ref " <> T.pack ref <> " (this may take a while)"+ lEM $ git [ "init" ]+ lEM $ git [ "remote"+ , "add"+ , "origin"+ , fromString rep ]++ -- figure out if we can do a shallow clone+ remoteBranches <- catchE @ProcessError @'[PatchFailed, ProcessError, NotFoundInPATH, DigestError, ContentLengthError, DownloadFailed, GPGError] @'[PatchFailed, NotFoundInPATH, DigestError, DownloadFailed, GPGError] (\(_ :: ProcessError) -> pure [])+ $ fmap processBranches $ gitOut ["ls-remote", "--heads", "origin"] (fromGHCupPath tmpUnpack)+ let shallow_clone+ | isCommitHash ref = True+ | fromString ref `elem` remoteBranches = True+ | otherwise = False+ lift $ logDebug $ "Shallow clone: " <> T.pack (show shallow_clone)++ -- fetch+ let fetch_args+ | shallow_clone = ["fetch", "--depth", "1", "--quiet", "origin", fromString ref]+ | otherwise = ["fetch", "--tags", "--quiet", "origin" ]+ lEM $ git fetch_args++ -- initial checkout+ lEM $ git [ "checkout", fromString ref ]++ -- gather some info+ git_describe <- if shallow_clone+ then pure Nothing+ else fmap Just $ liftE $ gitOut ["describe", "--tags"] (fromGHCupPath tmpUnpack)+ chash <- liftE $ gitOut ["rev-parse", "HEAD" ] (fromGHCupPath tmpUnpack)+ branch <- liftE $ gitOut ["rev-parse", "--abbrev-ref", "HEAD" ] (fromGHCupPath tmpUnpack)++ -- clone submodules+ lEM $ git [ "submodule", "update", "--init", "--depth", "1" ]++ -- apply patches+ liftE $ applyAnyPatch patches (fromGHCupPath tmpUnpack)++ -- bootstrap+ ver <- liftE $ catchAllE @_ @'[ProcessError, ParseError, NotFoundInPATH] @'[] (\_ -> pure Nothing) $ fmap Just $ getGHCVer+ tmpUnpack+ liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromGHCupPath tmpUnpack)+ lift $ logInfo $ "Examining git ref " <> T.pack ref <> "\n " <>+ "GHC version (from Makefile): " <> T.pack (show (prettyVer <$> ver)) <>+ (if not shallow_clone then "\n " <> "'git describe' output: " <> fromJust git_describe else mempty) <>+ (if isCommitHash ref then mempty else "\n " <> "commit hash: " <> chash)+ liftIO $ threadDelay 5000000 -- give the user a sec to intervene++ ov <- case vps of+ Just vps' -> fmap Just $ expandVersionPattern+ ver+ (take 7 $ T.unpack chash)+ (T.unpack chash)+ (maybe "" T.unpack git_describe)+ (T.unpack branch)+ vps'+ Nothing -> pure Nothing++ pure (ver, ov)++ let tver = TargetVersion crossTarget <$> ver+ pure (tmpUnpack, tmpUnpack, tver, ov)+ -- the version that's installed may differ from the+ -- compiled version, so the user can overwrite it+ installVer <- if | Just ov' <- ov -> pure (TargetVersion crossTarget ov')+ | Just tver' <- tver -> pure tver'+ | otherwise -> fail "No GHC version given and couldn't detect version. Giving up..."++ let rev = fromMaybe 0 $ preview (_GHCupDownloads % ix ghc % toolVersionsL % ix installVer % revisionSpecL % mapLast % _1) dls++ alreadyInstalled <- lift $ isInstalled ghc installVer+ alreadySet <- liftE $ isSet ghc installVer++ when (isJust alreadyInstalled) $ do+ case installDir of+ IsolateDir isoDir ->+ lift $ logWarn $ "GHC " <> T.pack (prettyShow installVer) <> " already installed. Isolate installing to " <> T.pack isoDir+ GHCupInternal ->+ lift $ logWarn $ "GHC " <> T.pack (prettyShow installVer) <> " already installed. Will overwrite existing version."+ lift $ logWarn+ "...waiting for 10 seconds before continuing, you can still abort..."+ liftIO $ threadDelay 10000000 -- give the user a sec to intervene++ ghcdir <- case installDir of+ IsolateDir isoDir -> pure $ IsolateDirResolved isoDir+ GHCupInternal -> GHCupDir <$> lift (ghcupGHCDir installVer)++ mBindist <- liftE $ runBuildAction+ tmpUnpack+ (do+ -- prefer 'tver', because the real version carries out compatibility checks+ -- we don't want the user to do funny things with it+ let doHadrian = Just <$> compileHadrianBindist (fromMaybe installVer tver) (fromGHCupPath workdir) ghcdir+ doMake = compileMakeBindist (fromMaybe installVer tver) (fromGHCupPath workdir) ghcdir+ case buildSystem of+ Just Hadrian -> do+ lift $ logInfo "Requested to use Hadrian"+ liftE doHadrian+ Just Make -> do+ lift $ logInfo "Requested to use Make"+ doMake+ Nothing -> do+ supportsHadrian <- liftE $ catchE @HadrianNotFound @'[HadrianNotFound] @'[] (\_ -> return False)+ $ fmap (const True)+ $ findHadrianFile (fromGHCupPath workdir)+ if supportsHadrian+ then do+ lift $ logInfo "Detected Hadrian"+ liftE doHadrian+ else do+ lift $ logInfo "Detected Make"+ doMake+ )++ case installDir of+ GHCupInternal ->+ -- only remove old ghc in regular installs+ when (isJust alreadyInstalled) $ do+ lift $ logInfo "Deleting existing installation"+ liftE $ rmToolVersion ghc installVer++ _ -> pure ()++ let toolDesc = preview (_GHCupDownloads % ix ghc % toolDetails % _Just) dls++ let trev = TargetVersionRev installVer rev+ case mBindist of+ Just bindist -> do+ spec <- liftE $ compileInstallSpec bindist installVer+ cSize <- liftIO $ getFileSize bindist+ cDigest <- liftIO $ getFileDigest bindist+ liftE $ void $ installPackedBindist ghc toolDesc bindist+ (DownloadInfo {+ _dlUri = "file:" <> T.pack bindist+ , _dlSubdir = Just $ RegexDir "ghc-.*"+ , _dlHash = cDigest+ , _dlCSize = Just cSize+ , _dlOutput = Nothing+ , _dlTag = Nothing+ , _dlInstallSpec = Just (toInstallationInputSpec spec)+ })+ ghcdir+ trev+ False -- not a force install, since we already overwrite when compiling.+ aargs+ installTargets+ -- for old Make cross installations we don't get a bindist,+ -- so this needs to be done manually+ Nothing -> do+ spec <- liftE $ compileInstallSpec' (fromInstallDir ghcdir </> "bin") installVer+ let dlInfo = (DownloadInfo {+ _dlUri = ""+ , _dlSubdir = Just $ RegexDir "ghc-.*"+ , _dlHash = ""+ , _dlCSize = Nothing+ , _dlOutput = Nothing+ , _dlTag = Nothing+ , _dlInstallSpec = Just (toInstallationInputSpec spec)+ })+ lift $ recordInstallationInfo ghcdir ghc toolDesc trev dlInfo spec++ case installDir of+ -- set and make symlinks for regular (non-isolated) installs+ GHCupInternal -> do+ symSpec <- liftE $ getSymlinkSpec' ghc installVer+ Dirs {..} <- lift getDirs+ liftE $ symlinkBinaries ghcdir symSpec (GHCupBinDir binDir) ghc installVer+ -- restore+ when (isJust alreadySet) $ liftE $ void $ setToolVersion ghc installVer++ _ -> pure ()++ pure installVer++ where+ -- Infer the installation spec from the binary directory+ compileInstallSpec' ::+ ( MonadIOish m+ , MonadReader env m+ , HasPlatformReq env+ )+ => FilePath -- ^ Binary dir+ -> TargetVersion+ -> Excepts '[ UnknownArchive+ , ArchiveResult+ ] m InstallationSpecResolved+ compileInstallSpec' bindir installVer = do+ pfreq <- lift getPlatformReq+ let pred' f = not (null f) && not (prettyVer (_tvVersion installVer) `T.isSuffixOf` T.pack f)+ files <- liftIO $ listDirectoryFiles bindir+ let binaries = filter pred' . fmap takeFileName $ files+ -- ghcup whereis picks the first binary, so we need to ensure 'ghc' is at the front+ binariesSorted = nub (maybe id (\(T.unpack -> t) a -> t <> "-" <> a) (_tvTarget installVer) "ghc" <.> exeExt:binaries)+ pure $ (defaultGHCInstallSpec pfreq installVer) { _isExeSymLinked = syml binariesSorted }+ where+ syml binaries =+ (\b -> SymlinkSpec (b <.> exeExt)+ (takeFileName b <> "-${PKGVER}" <.> exeExt)+ True+ (Just $ takeFileName b <.> exeExt)+ ) . (\b -> "bin" Posix.</> b) <$> binaries++ -- Infer the installation spec from the tarball+ compileInstallSpec ::+ ( MonadIOish m+ , MonadReader env m+ , HasPlatformReq env+ )+ => FilePath -- ^ archive path+ -> TargetVersion+ -> Excepts '[ UnknownArchive+ , ArchiveResult+ ] m InstallationSpecResolved+ compileInstallSpec tarball installVer = do+ pfreq <- lift getPlatformReq+ archiveFiles <- liftE $ getArchiveFiles tarball+ let pred' f = not (null f) && not (prettyVer (_tvVersion installVer) `T.isSuffixOf` T.pack f)+ let binaries = filter pred' . fmap takeFileName . filter ("*/bin/*" ?==) $ archiveFiles+ -- ghcup whereis picks the first binary, so we need to ensure 'ghc' is at the front+ binariesSorted = nub (maybe id (\(T.unpack -> t) a -> t <> "-" <> a) (_tvTarget installVer) "ghc" <.> exeExt:binaries)+ pure $ (defaultGHCInstallSpec pfreq installVer) { _isExeSymLinked = syml binariesSorted }+ where+ syml binaries =+ (\b -> SymlinkSpec (b <.> exeExt)+ (takeFileName b <> "-${PKGVER}" <.> exeExt)+ True+ (Just $ takeFileName b <.> exeExt)+ ) . (\b -> "bin" Posix.</> b) <$> binaries++ getGHCVer :: ( MonadReader env m+ , HasSettings env+ , HasDirs env+ , HasLog env+ , MonadIOish m+ )+ => GHCupPath+ -> Excepts '[ProcessError, ParseError, NotFoundInPATH] m Version+ getGHCVer tmpUnpack = do+ liftE $ execWithWrapper "python3" ["./boot"] (Just $ fromGHCupPath tmpUnpack) "ghc-bootstrap" Nothing+ liftE $ configureWithGhcBoot Nothing [] (Just $ fromGHCupPath tmpUnpack) "ghc-bootstrap"+ let versionFile = fromGHCupPath tmpUnpack </> "VERSION"+ hasVersionFile <- liftIO $ doesFileExist versionFile+ if hasVersionFile+ then do+ lift $ logDebug "Detected VERSION file, trying to extract"+ contents <- liftIO $ readFile versionFile+ either (throwE . ParseError . show) pure . MP.parse version' "" . T.pack . stripNewlineEnd $ contents+ else do+ lift $ logDebug "Didn't detect VERSION file, trying to extract via legacy 'make'"+ CapturedProcess {..} <- lift $ makeOut+ ["show!", "--quiet", "VALUE=ProjectVersion" ] (Just $ fromGHCupPath tmpUnpack)+ case _exitCode of+ ExitSuccess -> either (throwE . ParseError . show) pure . MP.parse ghcProjectVersion "" . T.pack . stripNewlineEnd . T.unpack . decUTF8Safe' $ _stdOut+ ExitFailure c -> throwE $ NonZeroExit c "make" ["show!", "--quiet", "VALUE=ProjectVersion" ]++ defaultConf =+ let cross_mk = $(LitE . StringL <$> (qAddDependentFile "data/build_mk/cross" >> runIO (readFile "data/build_mk/cross")))+ default_mk = $(LitE . StringL <$> (qAddDependentFile "data/build_mk/default" >> runIO (readFile "data/build_mk/default")))+ in case crossTarget of+ Just _ -> cross_mk+ _ -> default_mk++ compileHadrianBindist :: ( MonadReader env m+ , HasDirs env+ , HasSettings env+ , HasPlatformReq env+ , HasLog env+ , MonadIOish m+ )+ => TargetVersion+ -> FilePath+ -> InstallDirResolved+ -> Excepts+ '[ FileDoesNotExistError+ , HadrianNotFound+ , InvalidBuildConfig+ , PatchFailed+ , ProcessError+ , NotFoundInPATH+ , CopyError]+ m+ FilePath -- ^ output path of bindist+ compileHadrianBindist tver workdir ghcdir = do+ liftE $ configureBindist tver workdir ghcdir++ lift $ logInfo $ "Building GHC version " <> tVerToText tver <> " (this may take a while)..."+ hadrian_build <- liftE $ findHadrianFile workdir+ hEnv <- case hghc of+ Nothing -> pure Nothing+ Just hghc' -> do+ cEnv <- Map.fromList <$> liftIO getEnvironment+ ghc' <- liftE $ resolveGHC hghc'+ pure . Just . Map.toList . Map.insert "GHC" ghc' $ cEnv+++ liftE $ execWithWrapper hadrian_build+ ( maybe [] (\j -> ["-j" <> show j] ) jobs+ ++ maybe [] (\bf -> ["--flavour=" <> bf]) buildFlavour+ ++ ["binary-dist"]+ )+ (Just workdir) "ghc-make"+ hEnv+ [tar] <- liftIO $ findFiles+ (workdir </> "_build" </> "bindist")+ (makeRegexOpts compExtended+ execBlank+ ([s|^ghc-.*\.tar\..*$|] :: ByteString)+ )+ liftE $ copyBindist tver tar (workdir </> "_build" </> "bindist")++ findHadrianFile :: (MonadIO m)+ => FilePath+ -> Excepts+ '[HadrianNotFound]+ m+ FilePath+ findHadrianFile workdir = do+ let possible_files = if isWindows+ then ((workdir </> "hadrian") </>) <$> ["build.bat"]+ else ((workdir </> "hadrian") </>) <$> ["build", "build.sh"]+ exists <- forM possible_files (\f -> liftIO (doesFileExist f) <&> (,f))+ case filter fst exists of+ [] -> throwE HadrianNotFound+ ((_, x):_) -> pure x++ compileMakeBindist :: ( MonadReader env m+ , HasDirs env+ , HasSettings env+ , HasPlatformReq env+ , MonadThrow m+ , MonadCatch m+ , HasLog env+ , MonadIO m+ , MonadFail m+ , MonadMask m+ , MonadUnliftIO m+ , MonadResource m+ )+ => TargetVersion+ -> FilePath+ -> InstallDirResolved+ -> Excepts+ '[ FileDoesNotExistError+ , HadrianNotFound+ , InvalidBuildConfig+ , PatchFailed+ , ProcessError+ , NotFoundInPATH+ , MergeFileTreeError+ , CopyError]+ m+ (Maybe FilePath) -- ^ output path of bindist, None for cross+ compileMakeBindist tver workdir ghcdir = do+ liftE $ configureBindist tver workdir ghcdir++ case mbuildConfig of+ Just bc -> liftIOException+ doesNotExistErrorType+ (FileDoesNotExistError bc)+ (liftIO $ copyFile bc (build_mk workdir) False)+ Nothing ->+ liftIO $ T.writeFile (build_mk workdir) (addBuildFlavourToConf defaultConf)++ liftE $ checkBuildConfig (build_mk workdir)++ lift $ logInfo $ "Building GHC version " <> tVerToText tver <> " (this may take a while)..."+ liftE $ makeWithWrapper (maybe [] (\j -> ["-j" <> fS (show j)]) jobs) (Just workdir) "ghc-make" Nothing++ if | isCross tver -> do -- this is effectively legacy too (and does not install a .spec)+ lift $ logInfo "Installing cross toolchain..."+ tmpInstallDest <- lift withGHCupTmpDir+ liftE $ makeWithWrapper ["DESTDIR=" <> fromGHCupPath tmpInstallDest, "install"] (Just workdir) "ghc-make" Nothing+ liftE $ mergeGHCFileTree (tmpInstallDest `appendGHCupPath` dropDrive (fromInstallDir ghcdir)) ghcdir tver True+ pure Nothing+ | otherwise -> do+ lift $ logInfo "Creating bindist..."+ liftE $ makeWithWrapper ["binary-dist"] (Just workdir) "ghc-make" Nothing+ [tar] <- liftIO $ findFiles+ workdir+ (makeRegexOpts compExtended+ execBlank+ ([s|^ghc-.*\.tar\..*$|] :: ByteString)+ )+ liftE $ fmap Just $ copyBindist tver tar workdir++ build_mk workdir = workdir </> "mk" </> "build.mk"++ copyBindist :: ( MonadReader env m+ , HasDirs env+ , HasSettings env+ , HasPlatformReq env+ , MonadIO m+ , MonadThrow m+ , MonadCatch m+ , HasLog env+ )+ => TargetVersion+ -> FilePath -- ^ tar file+ -> FilePath -- ^ workdir+ -> Excepts+ '[CopyError]+ m+ FilePath+ copyBindist tver tar workdir = do+ Dirs {..} <- lift getDirs+ pfreq <- lift getPlatformReq+ c <- liftIO $ BL.readFile (workdir </> tar)+ cDigest <-+ fmap (T.take 8)+ . lift+ . throwEither+ . E.decodeUtf8'+ . B16.encode+ . SHA256.hashlazy+ $ c+ cTime <- liftIO getCurrentTime+ let tarName = makeValid ("ghc-"+ <> T.unpack (tVerToText tver)+ <> "-"+ <> pfReqToString pfreq+ <> "-"+ <> iso8601Show cTime+ <> "-"+ <> T.unpack cDigest+ <> ".tar"+ <> takeExtension tar)+ let tarPath = fromGHCupPath cacheDir </> tarName+ copyFileE (workdir </> tar) tarPath False+ lift $ logInfo $ "Copied bindist to " <> T.pack tarPath+ pure tarPath++ checkBuildConfig :: (MonadReader env m, MonadCatch m, MonadIO m, HasLog env)+ => FilePath+ -> Excepts+ '[FileDoesNotExistError, InvalidBuildConfig]+ m+ ()+ checkBuildConfig bc = do+ c <- liftIOException+ doesNotExistErrorType+ (FileDoesNotExistError bc)+ (liftIO $ B.readFile bc)+ let lines' = fmap T.strip . T.lines $ decUTF8Safe c++ -- for cross, we need Stage1Only+ case crossTarget of+ Just _ -> when ("Stage1Only = YES" `notElem` lines') $ throwE+ (InvalidBuildConfig+ [s|Cross compiling needs to be a Stage1 build, add "Stage1Only = YES" to your config!|]+ )+ _ -> pure ()++ forM_ buildFlavour $ \bf ->+ when (T.pack ("BuildFlavour = " <> bf) `notElem` lines') $ do+ lift $ logWarn $ "Customly specified build config overwrites --flavour=" <> T.pack bf <> " switch! Waiting 5 seconds..."+ liftIO $ threadDelay 5000000++ addBuildFlavourToConf bc = case buildFlavour of+ Just bf -> "BuildFlavour = " <> T.pack bf <> "\n" <> bc+ Nothing -> bc++ isCross :: TargetVersion -> Bool+ isCross = isJust . _tvTarget+++ configureBindist :: ( MonadReader env m+ , HasDirs env+ , HasSettings env+ , HasPlatformReq env+ , HasLog env+ , MonadIOish m+ )+ => TargetVersion+ -> FilePath+ -> InstallDirResolved+ -> Excepts+ '[ FileDoesNotExistError+ , InvalidBuildConfig+ , PatchFailed+ , ProcessError+ , NotFoundInPATH+ , CopyError+ ]+ m+ ()+ configureBindist tver workdir (fromInstallDir -> ghcdir) = do+ PlatformRequest { .. } <- lift getPlatformReq+ lift $ logInfo [s|configuring build|]+ liftE $ configureWithGhcBoot (Just tver)+ (maybe mempty+ (\x -> ["--target=" <> T.unpack x])+ (_tvTarget tver)+ ++ ["--prefix=" <> ghcdir]+ ++ (if isWindows then ["--enable-tarballs-autodownload"] else [])+ -- https://github.com/haskell/ghcup-hs/issues/1032+ ++ ldOverride (_tvVersion tver) _rPlatform+ ++ aargs+ )+ (Just workdir)+ "ghc-conf"+ pure ()++ configureWithGhcBoot :: ( MonadReader env m+ , HasSettings env+ , HasDirs env+ , HasLog env+ , MonadIOish m+ )+ => Maybe TargetVersion+ -> [String] -- ^ args for configure+ -> Maybe FilePath -- ^ optionally chdir into this+ -> FilePath -- ^ log filename (opened in append mode)+ -> Excepts '[ProcessError, NotFoundInPATH] m ()+ configureWithGhcBoot mtver args dir logf = do+ bghc <- liftE $ resolveGHC bstrap+ let execNew = execWithWrapper+ "sh"+ ("./configure" : ("GHC=" <> bghc) : args)+ dir+ logf+ Nothing+ execOld = execWithWrapper+ "sh"+ ("./configure" : ("--with-ghc=" <> bghc) : args)+ dir+ logf+ Nothing+ if | Just tver <- mtver+ , _tvVersion tver >= [vver|8.8.0|] -> liftE execNew+ | Nothing <- mtver -> liftE execNew -- need some default for git checkouts where we don't know yet+ | otherwise -> liftE execOld++ resolveGHC :: MonadIO m => Either Version FilePath -> Excepts '[NotFoundInPATH] m FilePath+ resolveGHC = \case+ Right g -> pure g+ Left bver -> do+ let ghc' = "ghc-" <> (T.unpack . prettyVer $ bver) <> exeExt+ -- https://gitlab.haskell.org/ghc/ghc/-/issues/24682+ makeAbsolute ghc'+++++ -------------+ --[ Other ]--+ -------------+++ldOverride :: Version -> Platform -> [String]+ldOverride ver _plat+ | ver >= [vver|8.2.2|]+ = ["--disable-ld-override"]+ | otherwise+ = []++sanitizefGHCconfOptions :: MonadFail m => [String] -> m [String]+sanitizefGHCconfOptions args+ | "--prefix" `elem` fmap (takeWhile (/= '=')) args = fail "Don't explicitly set --prefix ...aborting"+ | otherwise = pure args+
+ lib/GHCup/Command/Compile/HLS.hs view
@@ -0,0 +1,473 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}++{-|+Module : GHCup.Command.Compile.HLS+Description : GHCup compile HLS+Copyright : (c) Julian Ospald, 2020+License : LGPL-3.0+Maintainer : hasufell@hasufell.de+Stability : experimental+Portability : portable+-}+module GHCup.Command.Compile.HLS where++import GHCup.Builder+import GHCup.Command.Install.LowLevel+import GHCup.Download+import GHCup.Errors+import GHCup.Input.SymlinkSpec+import GHCup.Legacy.HLS ( installHLSUnpackedLegacy )+import GHCup.Prelude+import GHCup.Prelude.Process+import GHCup.Prelude.String.QQ+import GHCup.Query.DB+import GHCup.Query.GHCupDirs+import GHCup.System.Cmd+import GHCup.System.Directory+import GHCup.Types+import GHCup.Types.Optics+import GHCup.Unpack++import Control.Applicative+import Control.Exception.Safe+import Control.Monad+#if !MIN_VERSION_base(4,13,0)+import Control.Monad.Fail ( MonadFail )+#endif+import Control.Monad.Reader+import Control.Monad.Trans.Resource hiding ( throwM )+import Data.Either+import Data.List+import Data.Maybe+import Data.String ( fromString )+import Data.Text ( Text )+import Data.Variant.Excepts+import Data.Versions hiding ( patch )+import Distribution.PackageDescription.Parsec+import Distribution.Types.GenericPackageDescription+import Distribution.Types.PackageDescription+import Distribution.Types.PackageId+import Distribution.Types.Version hiding ( Version )+import Optics+import Prelude hiding ( abs, writeFile )+import System.FilePath+import Text.Regex.Posix+import URI.ByteString++import qualified Data.ByteString as B+import qualified Data.List.NonEmpty as NE+import qualified Data.Text as T+++data HLSVer+ = SourceDist Version+ | GitDist GitBranch+ | HackageDist Version+ | RemoteDist URI+ deriving (Eq, Show)+++++compileHLS :: ( MonadMask m+ , MonadCatch m+ , MonadReader env m+ , HasDirs env+ , HasSettings env+ , HasPlatformReq env+ , HasGHCupInfo env+ , HasLog env+ , MonadResource m+ , MonadIO m+ , MonadUnliftIO m+ , MonadFail m+ )+ => HLSVer+ -> [Version]+ -> Maybe Int+ -> Maybe [VersionPattern]+ -> InstallDir+ -> Maybe (Either FilePath URI)+ -> Maybe URI+ -> Bool+ -> Maybe (Either FilePath [URI]) -- ^ patches+ -> [Text] -- ^ additional args to cabal install+ -> Excepts '[ NoDownload+ , GPGError+ , DownloadFailed+ , DigestError+ , ContentLengthError+ , UnknownArchive+ , TarDirDoesNotExist+ , ArchiveResult+ , BuildFailed+ , NotInstalled+ , URIParseError+ ] m Version+compileHLS targetHLS ghcs jobs vps installDir cabalProject cabalProjectLocal updateCabal patches cabalArgs = do+ pfreq@PlatformRequest { .. } <- lift getPlatformReq+ GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo+ let toolDesc = preview (_GHCupDownloads % ix hls % toolDetails % _Just) dls+ Dirs { .. } <- lift getDirs+++ when updateCabal $ reThrowAll @_ @'[ProcessError] DownloadFailed $ do+ lift $ logInfo "Updating cabal DB"+ liftE $ execWithWrapper "cabal" ["update"] (Just $ fromGHCupPath tmpDir) "cabal" Nothing++ (workdir, tmpUnpack, ver, rev, ov) <- case targetHLS of+ -- unpack from version tarball+ SourceDist ver -> do+ let tver = mkTVer ver+ lift $ logDebug $ "Requested to compile: " <> prettyVer ver++ -- download source tarball+ (rev, dlInfo) <- (?? NoDownload (unsafeToTargetVersionReq tver) hls (Just pfreq)) $ do+ (rev, vi) <- preview (_GHCupDownloads % ix hls % toolVersionsL % ix tver % revisionSpecL % mapLast) dls+ dlInfo <- preview (viSourceDL % _Just) vi+ pure (rev, dlInfo)+ dl <- liftE $ downloadCached dlInfo Nothing++ -- unpack+ tmpUnpack <- lift mkGhcupTmpDir+ liftE $ cleanUpOnError tmpUnpack (unpackToDir (fromGHCupPath tmpUnpack) dl)+ liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromGHCupPath tmpUnpack)++ workdir <- maybe (pure tmpUnpack)+ (liftE . intoSubdir tmpUnpack)+ (view dlSubdir dlInfo)++ ov <- case vps of+ Just vps' -> fmap Just $ expandVersionPattern (Just ver) "" "" "" "" vps'+ Nothing -> pure Nothing++ pure (workdir, tmpUnpack, ver, rev, ov)++ HackageDist ver -> do+ lift $ logDebug $ "Requested to compile (from hackage): " <> prettyVer ver++ -- download source tarball+ tmpUnpack <- lift mkGhcupTmpDir+ let hls' = "haskell-language-server-" <> T.unpack (prettyVer ver)+ reThrowAll @_ @'[ProcessError] DownloadFailed $ do+ -- unpack+ liftE $ execWithWrapper "cabal" ["unpack", hls'] (Just $ fromGHCupPath tmpUnpack) "cabal" Nothing++ let workdir = appendGHCupPath tmpUnpack hls'++ ov <- case vps of+ Just vps' -> fmap Just $ expandVersionPattern (Just ver) "" "" "" "" vps'+ Nothing -> pure Nothing++ let rev = fromMaybe 0 $ preview (_GHCupDownloads % ix hls % toolVersionsL % ix (mkTVer ver) % revisionSpecL % mapLast % _1) dls+ pure (workdir, tmpUnpack, ver, rev, ov)++ RemoteDist uri -> do+ lift $ logDebug $ "Requested to compile (from uri): " <> T.pack (show uri)++ -- download source tarball+ tmpDownload <- lift withGHCupTmpDir+ tmpUnpack <- lift mkGhcupTmpDir+ tar <- liftE $ download uri Nothing Nothing Nothing (fromGHCupPath tmpDownload) Nothing False+ (cf, ver) <- liftE $ cleanUpOnError tmpUnpack $ do+ unpackToDir (fromGHCupPath tmpUnpack) tar+ let regex = [s|^(.*/)*haskell-language-server\.cabal$|] :: B.ByteString+ [cabalFile] <- liftIO $ findFilesDeep+ tmpUnpack+ (makeRegexOpts compExtended+ execBlank+ regex+ )+ ver <- getCabalVersion (fromGHCupPath tmpUnpack </> cabalFile)+ pure (cabalFile, ver)++ let workdir = appendGHCupPath tmpUnpack (takeDirectory cf)++ ov <- case vps of+ Just vps' -> fmap Just $ expandVersionPattern (Just ver) "" "" "" "" vps'+ Nothing -> pure Nothing++ let rev = fromMaybe 0 $ preview (_GHCupDownloads % ix hls % toolVersionsL % ix (mkTVer ver) % revisionSpecL % mapLast % _1) dls+ pure (workdir, tmpUnpack, ver, rev, ov)++ -- clone from git+ GitDist GitBranch{..} -> do+ tmpUnpack <- lift mkGhcupTmpDir+ let git args = execLogged "git" ("--no-pager":args) (Just $ fromGHCupPath tmpUnpack) "git" Nothing+ cleanUpOnError tmpUnpack $ reThrowAll @_ @'[ProcessError] DownloadFailed $ do+ let rep = fromMaybe "https://github.com/haskell/haskell-language-server.git" repo+ lift $ logInfo $ "Fetching git repo " <> T.pack rep <> " at ref " <> T.pack ref <> " (this may take a while)"+ lEM $ git [ "init" ]+ lEM $ git [ "remote"+ , "add"+ , "origin"+ , fromString rep ]++ -- figure out if we can do a shallow clone+ remoteBranches <- catchE @ProcessError @'[ProcessError] @'[] (\_ -> pure [])+ $ fmap processBranches $ gitOut ["ls-remote", "--heads", "origin"] (fromGHCupPath tmpUnpack)+ let shallow_clone+ | gitDescribeRequested = False+ | isCommitHash ref = True+ | fromString ref `elem` remoteBranches = True+ | otherwise = False++ lift $ logDebug $ "Shallow clone: " <> T.pack (show shallow_clone)++ -- fetch+ let fetch_args+ | shallow_clone = ["fetch", "--depth", "1", "--quiet", "origin", fromString ref]+ | otherwise = ["fetch", "--tags", "--quiet", "origin" ]+ lEM $ git fetch_args++ -- checkout+ lEM $ git [ "checkout", fromString ref ]++ -- gather some info+ git_describe <- if shallow_clone+ then pure Nothing+ else fmap Just $ gitOut ["describe", "--tags"] (fromGHCupPath tmpUnpack)+ chash <- gitOut ["rev-parse", "HEAD" ] (fromGHCupPath tmpUnpack)+ branch <- gitOut ["rev-parse", "--abbrev-ref", "HEAD" ] (fromGHCupPath tmpUnpack)+ ver <- getCabalVersion (fromGHCupPath tmpUnpack </> "haskell-language-server.cabal")++ liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromGHCupPath tmpUnpack)++ ov <- case vps of+ Just vps' -> fmap Just $ expandVersionPattern+ (Just ver)+ (take 7 $ T.unpack chash)+ (T.unpack chash)+ (maybe "" T.unpack git_describe)+ (T.unpack branch)+ vps'+ Nothing -> pure Nothing++ lift $ logInfo $ "Examining git ref " <> T.pack ref <> "\n " <>+ "HLS version (from cabal file): " <> prettyVer ver <>+ "\n branch: " <> branch <>+ (if not shallow_clone then "\n " <> "'git describe' output: " <> fromJust git_describe else mempty) <>+ (if isCommitHash ref then mempty else "\n " <> "commit hash: " <> chash)++ let rev = fromMaybe 0 $ preview (_GHCupDownloads % ix hls % toolVersionsL % ix (mkTVer ver) % revisionSpecL % mapLast % _1) dls+ pure (tmpUnpack, tmpUnpack, ver, rev, ov)++ -- the version that's installed may differ from the+ -- compiled version, so the user can overwrite it+ installVer <- maybe (pure ver) pure ov++ liftE $ runBuildAction+ tmpUnpack+ (reThrowAll @_ @'[GPGError, DownloadFailed, DigestError, ContentLengthError, PatchFailed, ProcessError, FileAlreadyExistsError, CopyError, ParseError , MergeFileTreeError , MalformedInstallInfo, FileDoesNotExistError, NoInstallInfo] @'[BuildFailed] (BuildFailed $ fromGHCupPath workdir) $ do+ let tmpInstallDir = fromGHCupPath workdir </> "out"+ liftIO $ createDirRecursive' tmpInstallDir++ -- apply patches+ liftE $ applyAnyPatch patches (fromGHCupPath workdir)++ -- set up project files+ cp <- case cabalProject of+ Just (Left cp)+ | isAbsolute cp -> do+ copyFileE cp (fromGHCupPath workdir </> "cabal.project") False+ pure "cabal.project"+ | otherwise -> pure (takeFileName cp)+ Just (Right uri) -> do+ tmpUnpack' <- lift withGHCupTmpDir+ cp <- liftE $ download uri Nothing Nothing Nothing (fromGHCupPath tmpUnpack') (Just "cabal.project") False+ copyFileE cp (fromGHCupPath workdir </> "cabal.project") False+ pure "cabal.project"+ Nothing+ | HackageDist _ <- targetHLS -> do+ liftIO $ B.writeFile (fromGHCupPath workdir </> "cabal.project") "packages: ./"+ pure "cabal.project"+ | RemoteDist _ <- targetHLS -> do+ let cabalFile = fromGHCupPath workdir </> "cabal.project"+ liftIO $ whenM (not <$> doesFileExist cabalFile) $ B.writeFile cabalFile "packages: ./"+ pure "cabal.project"+ | otherwise -> pure "cabal.project"+ forM_ cabalProjectLocal $ \uri -> do+ tmpUnpack' <- lift withGHCupTmpDir+ cpl <- liftE $ download uri Nothing Nothing Nothing (fromGHCupPath tmpUnpack') (Just (cp <.> "local")) False+ copyFileE cpl (fromGHCupPath workdir </> cp <.> "local") False+ artifactDirs <- forM (sort ghcs) $ \ghc' -> do+ let ghcInstallDir = tmpInstallDir </> T.unpack (prettyVer ghc')+ liftIO $ createDirRecursive' tmpInstallDir+ lift $ logInfo $ "Building HLS " <> prettyVer installVer <> " for GHC version " <> prettyVer ghc'+ liftE $+ execWithWrapper "cabal" ( [ "v2-install"+ , "-w"+ , "ghc-" <> T.unpack (prettyVer ghc')+ , "--install-method=copy"+ ] +++ maybe [] (\j -> ["--jobs=" <> show j]) jobs +++ [ "--overwrite-policy=always"+ , "--disable-profiling"+ , "--disable-tests"+ , "--installdir=" <> ghcInstallDir+ , "--project-file=" <> cp+ ] ++ fmap T.unpack cabalArgs ++ [+ "exe:haskell-language-server"+ , "exe:haskell-language-server-wrapper"]+ )+ (Just $ fromGHCupPath workdir)+ "cabal"+ Nothing+ pure ghcInstallDir++ forM_ artifactDirs $ \artifactDir -> do+ logDebug $ "mv " <> T.pack (artifactDir </> "haskell-language-server" <.> exeExt) <> " "+ <> T.pack (tmpInstallDir </> "haskell-language-server-" <> takeFileName artifactDir <.> exeExt)+ liftIO $ renameFile (artifactDir </> "haskell-language-server" <.> exeExt)+ (tmpInstallDir </> "haskell-language-server-" <> takeFileName artifactDir <.> exeExt)+ logDebug $ "mv " <> T.pack (artifactDir </> "haskell-language-server-wrapper" <.> exeExt) <> " "+ <> T.pack (tmpInstallDir </> "haskell-language-server-wrapper" <.> exeExt)+ liftIO $ renameFile (artifactDir </> "haskell-language-server-wrapper" <.> exeExt)+ (tmpInstallDir </> "haskell-language-server-wrapper" <.> exeExt)+++ installDirResolved <- case installDir of+ IsolateDir isoDir -> pure $ IsolateDirResolved isoDir+ GHCupInternal -> GHCupDir <$> lift (toolInstallDestination hls (mkTVer installVer))++ runE (getInstallMetadata hls (mkTVer installVer)) >>= \case+ VRight metadata -> do+ destdir <- lift withGHCupTmpDir+ liftE $ addAdHocBinaries (Just metadata) toolDesc tmpInstallDir installDirResolved destdir (VersionRev installVer rev) True+ VLeft (V (FileDoesNotExistError _)) -> do+ inst <- lift $ isInstalled hls (mkTVer installVer)+ if isJust inst+ then liftE $ installHLSUnpackedLegacy tmpInstallDir (GHCupBinDir binDir) installVer True+ else do+ destdir <- lift withGHCupTmpDir+ liftE $ addAdHocBinaries Nothing toolDesc tmpInstallDir installDirResolved destdir (VersionRev installVer rev) True+ VLeft (V (ParseError pe)) -> liftE $ throwE @_ @'[ParseError] (ParseError pe)+ VLeft v -> lift $ fail (prettyHFError v)+ )++ pure installVer+ where+ gitDescribeRequested = maybe False (GitDescribe `elem`) vps++ adHocInstallationSpec :: [FilePath] -- ^ no .exe extension+ -> InstallationSpecResolved+ adHocInstallationSpec hlsExes =+ InstallationSpec {+ _isExeRules =+ hlsExes <&> (\exe -> InstallFileRule { _ibrInstallSource = exe <.> exeExt+ , _ibrInstallDest = Just $ "bin" </> exe <.> exeExt+ })+ , _isDataRules = []+ , _isConfigure = Nothing+ , _isMake = Nothing+ , _isExeSymLinked = hlsExes <&> \hlsExe ->+ if hlsExe == "haskell-language-server-wrapper"+ then SymlinkSpec { _slTarget = "bin" </> hlsExe <.> exeExt+ , _slLinkName = hlsExe <> "-${PKGVER}" <.> exeExt+ , _slPVPMajorLinks = False+ , _slSetName = Just (hlsExe <.> exeExt)+ }+ else SymlinkSpec { _slTarget = "bin" </> hlsExe <.> exeExt+ , _slLinkName = hlsExe <> "~${PKGVER}" <.> exeExt+ , _slPVPMajorLinks = False+ , _slSetName = Just (hlsExe <.> exeExt)+ }+ , _isPreserveMtimes = False+ }++ addAdHocBinaries :: forall m env .+ ( HasLog env+ , HasDirs env+ , HasSettings env+ , HasPlatformReq env+ , MonadReader env m+ , MonadIOish m+ )+ => Maybe InstallMetadata+ -> Maybe ToolDescription+ -> FilePath+ -> InstallDirResolved -- ^ Path to install to+ -> GHCupPath -- ^ DESTDIR+ -> VersionRev+ -> Bool+ -> Excepts '[ CopyError+ , MergeFileTreeError+ , ParseError+ , MalformedInstallInfo+ , NoInstallInfo+ , ProcessError+ ] m ()+ addAdHocBinaries mMetadata toolDesc workdir installDest tmpInstallDest VersionRev{..} forceInstall = do+ let tver = mkTVer _vrVersion+ binaries <- liftIO $ listDirectoryFiles workdir+ let spec = adHocInstallationSpec (dropSuffix exeExt <$> binaries)++ logDebug $ T.pack (show spec)+ logDebug "Install into tmp dir as per the spec"+ liftE $ installTheSpec (toInstallationInputSpec spec) workdir installDest tmpInstallDest [] Nothing forceInstall++ logDebug "Merge to filesystem"+ liftE $ mergeToFileSystem hls tver installDest tmpInstallDest (_isPreserveMtimes spec) forceInstall True+++ case installDir of+ -- set and make symlinks for regular (non-isolated) installs+ GHCupInternal -> do+ logDebug "Symlink binaries"+ Dirs {..} <- lift getDirs+ parsedSymlinkSpec <- forM (_isExeSymLinked spec) (liftE . parseSymlinkSpec (_tvVersion tver))+ liftE $ symlinkBinaries installDest parsedSymlinkSpec (GHCupBinDir binDir) hls tver++ -- write InstallationInfo to the disk+ logDebug "Writing installation info to disk"+ case mMetadata of+ (Just (InstallMetadata {..})) -> do+ lift $ recordInstallationInfo installDest hls toolDesc (TargetVersionRev tver _imRevision) _imDownloadInfo (manipulateSpec _imResolvedInstallSpec spec)+ Nothing -> do+ let dlInfo = DownloadInfo "" Nothing "" Nothing Nothing Nothing (Just $ toInstallationInputSpec spec)+ lift $ recordInstallationInfo installDest hls toolDesc (TargetVersionRev tver _vrRev) dlInfo spec+ _ -> pure ()++ pure ()+ where+ -- we install ad-hoc binaries and now need to adjust the exeSymLinked part+ -- so that uninstallation removes the additional symlinks+ manipulateSpec :: InstallationSpecResolved -> InstallationSpecResolved -> InstallationSpecResolved+ manipulateSpec origSpec adhocSpec =+ let oldSymls = _isExeSymLinked origSpec+ newSymls = _isExeSymLinked adhocSpec+ in origSpec { _isExeSymLinked = merge oldSymls newSymls }+ where+ merge :: [SymlinkSpec String] -> [SymlinkSpec String] -> [SymlinkSpec String]+ merge old new = nub (old <> new)++++ ---------------+ --[ Removal ]--+ ---------------++++getCabalVersion :: (MonadIO m, MonadFail m) => FilePath -> m Version+getCabalVersion fp = do+ contents <- liftIO $ B.readFile fp+ gpd <- case parseGenericPackageDescriptionMaybe contents of+ Nothing -> fail $ "could not parse cabal file: " <> fp+ Just r -> pure r+ let tver = (\c -> Version Nothing c Nothing Nothing)+ . Chunks+ . NE.fromList+ . fmap (Numeric . fromIntegral)+ . versionNumbers+ . pkgVersion+ . package+ . packageDescription+ $ gpd+ pure tver
+ lib/GHCup/Command/DebugInfo.hs view
@@ -0,0 +1,69 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}++{-|+Module : GHCup.Command.DebugInfo+Description : GHCup installation functions+Copyright : (c) Julian Ospald, 2020+License : LGPL-3.0+Maintainer : hasufell@hasufell.de+Stability : experimental+Portability : portable++This module contains the main functions that correspond+to the command line interface, like installation, listing versions+and so on.++These are the entry points.+-}+module GHCup.Command.DebugInfo where+++import GHCup.Errors+import GHCup.Hardcoded.URLs+import GHCup.Prelude+import GHCup.Query.System+import GHCup.Types+import GHCup.Types.JSON+ ()+import GHCup.Types.Optics++import Control.Applicative+import Control.Exception.Safe+import Control.Monad+#if !MIN_VERSION_base(4,13,0)+import Control.Monad.Fail ( MonadFail )+#endif+import Control.Monad.Reader+import Data.Variant.Excepts+import Prelude hiding ( abs, writeFile )+++++ ------------------+ --[ Debug info ]--+ ------------------+++getDebugInfo :: ( MonadFail m+ , MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadCatch m+ , MonadIO m+ )+ => Excepts+ '[NoCompatiblePlatform , NoCompatibleArch , DistroNotFound]+ m+ DebugInfo+getDebugInfo = do+ diDirs <- lift getDirs+ let diChannels = fmap (\c -> (c, channelURL c)) [minBound..maxBound]+ let diShimGenURL = shimGenURL+ diArch <- lE getArchitecture+ diPlatform <- liftE getPlatform+ pure $ DebugInfo { .. }
+ lib/GHCup/Command/GC.hs view
@@ -0,0 +1,204 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}++{-|+Module : GHCup.Command.GC+Description : GHCup gc+Copyright : (c) Julian Ospald, 2020+License : LGPL-3.0+Maintainer : hasufell@hasufell.de+Stability : experimental+Portability : portable+-}+module GHCup.Command.GC where+++import GHCup.Command.List+import GHCup.Command.Rm+import GHCup.Errors+import GHCup.Legacy.HLS+import GHCup.Legacy.Utils+import GHCup.Prelude+import GHCup.Prelude.String.QQ+import GHCup.Query.DB+import GHCup.Query.GHCupDirs+import GHCup.Query.Metadata+import GHCup.System.Directory+import GHCup.Types+import GHCup.Types.Optics++import Control.Applicative+import Control.Exception.Safe+import Control.Monad+#if !MIN_VERSION_base(4,13,0)+import Control.Monad.Fail ( MonadFail )+#endif+import Control.Monad.Reader+import Control.Monad.Trans.Resource hiding ( throwM )+import Data.ByteString ( ByteString )+import Data.Foldable.WithIndex+import Data.List+import Data.Maybe+import Data.Variant.Excepts+import Optics+import Prelude hiding ( abs, writeFile )+import System.FilePath+import Text.Regex.Posix++import qualified Data.Text as T++++ --------------------------+ --[ Garbage collection ]--+ --------------------------+++rmOldGHC :: ( MonadReader env m+ , HasGHCupInfo env+ , HasPlatformReq env+ , HasDirs env+ , HasLog env+ , MonadIOish m+ )+ => Excepts '[NotInstalled, UninstallFailed, ParseError, MalformedInstallInfo] m ()+rmOldGHC = do+ GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo+ let oldGHCs = toListOf (_GHCupDownloads % ix ghc % toolVersions % getTaggedL Old % to fst) dls+ ghcs <- lift $ getInstalledVersions' ghc+ forM_ (_tvrTargetVer <$> ghcs) $ \ghcVer -> when (ghcVer `elem` oldGHCs) $ rmToolVersion ghc ghcVer+++rmUnsetTools :: ( MonadReader env m+ , HasGHCupInfo env+ , HasPlatformReq env+ , HasDirs env+ , HasLog env+ , MonadIO m+ , MonadFail m+ , MonadMask m+ , MonadUnliftIO m+ )+ => Excepts '[NotInstalled, UninstallFailed, ParseError, MalformedInstallInfo] m ()+rmUnsetTools = do+ vers <- liftE $ listVersions Nothing [ListInstalled True, ListSet False] ShowUpdates False True (Nothing, Nothing)+ iforM_ vers $ \tool (_, ls) -> forM_ ls $ \ListResult{..} -> liftE $ rmToolVersion tool (TargetVersion lCross lVer)+++rmProfilingLibs :: ( MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadIO m+ , MonadFail m+ , MonadMask m+ , MonadUnliftIO m+ )+ => m ()+rmProfilingLibs = do+ ghcs <- getInstalledVersions' ghc++ let regexes :: [ByteString]+ regexes = [[s|.*_p\.a$|], [s|.*\.p_hi$|]]++ forM_ regexes $ \regex ->+ forM_ (_tvrTargetVer <$> ghcs) $ \ghc' -> do+ d <- ghcupGHCDir ghc'+ -- TODO: audit findFilesDeep+ matches <- liftIO $ handleIO (\_ -> pure []) $ findFilesDeep+ d+ (makeRegexOpts compExtended+ execBlank+ regex+ )+ forM_ matches $ \m -> do+ let p = fromGHCupPath d </> m+ logDebug $ "rm " <> T.pack p+ rmFile p++++rmShareDir :: ( MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadIO m+ , MonadFail m+ , MonadMask m+ , MonadUnliftIO m+ )+ => m ()+rmShareDir = do+ ghcs <- getInstalledVersions' ghc+ forM_ (_tvrTargetVer <$> ghcs) $ \ghc' -> do+ d <- ghcupGHCDir ghc'+ let p = d `appendGHCupPath` "share"+ logDebug $ "rm -rf " <> T.pack (fromGHCupPath p)+ rmPathForcibly p+++rmHLSNoGHC :: ( MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadIO m+ , MonadMask m+ , MonadFail m+ , MonadUnliftIO m+ )+ => Excepts '[NotInstalled, UninstallFailed] m ()+rmHLSNoGHC = do+ Dirs {..} <- getDirs+ ghcs <- lift $ getInstalledVersions' ghc+ hlses <- lift $ getInstalledVersions hls Nothing -- TODO: we don't support cross HLS+ forM_ (_vrVersion <$> hlses) $ \hls' -> do+ hlsGHCs <- fmap mkTVer <$> hlsGHCVersions' hls'+ let candidates = filter (`notElem` (_tvrTargetVer <$> ghcs)) hlsGHCs+ if (length hlsGHCs - length candidates) <= 0+ then rmHLSVer hls'+ else+ -- TODO: uff+ forM_ candidates $ \ghc' -> do+ bins1 <- fmap (binDir </>) <$> hlsServerBinaries hls' (Just $ _tvVersion ghc')+ bins2 <- ifM (isLegacyHLS hls') (pure []) $ do+ shs <- hlsInternalServerScripts hls' (Just $ _tvVersion ghc')+ bins <- hlsInternalServerBinaries hls' (Just $ _tvVersion ghc')+ libs <- hlsInternalServerLibs hls' (_tvVersion ghc')+ pure (shs ++ bins ++ libs)+ forM_ (bins1 ++ bins2) $ \f -> do+ logDebug $ "rm " <> T.pack f+ rmFile f+ pure ()+++rmCache :: ( MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadIO m+ , MonadMask m+ )+ => m ()+rmCache = do+ Dirs {..} <- getDirs+ contents <- liftIO $ listDirectory (fromGHCupPath cacheDir)+ forM_ contents $ \f -> do+ let p = fromGHCupPath cacheDir </> f+ logDebug $ "rm " <> T.pack p+ rmFile p+++rmTmp :: ( MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadIO m+ , MonadMask m+ )+ => m ()+rmTmp = do+ ghcup_dirs <- liftIO getGHCupTmpDirs+ forM_ ghcup_dirs $ \f -> do+ logDebug $ "rm -rf " <> T.pack (fromGHCupPath f)+ rmPathForcibly f+
+ lib/GHCup/Command/HealthCheck.hs view
@@ -0,0 +1,176 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE MultiWayIf #-}++module GHCup.Command.HealthCheck where++import GHCup.Errors+import GHCup.Legacy.Utils+import GHCup.Prelude+import GHCup.Query.DB+import GHCup.Query.GHCupDirs+import GHCup.Types+import GHCup.Types.Optics++import Control.Exception+ (SomeException )+import Control.Exception.Safe ( try, handleIO )+import Control.Monad ( forM )+import Control.Monad.IO.Class ( liftIO )+import Control.Monad.Reader ( MonadReader )+import Data.Functor ( (<&>) )+import Data.List ( foldl', sort )+import Data.Variant.Excepts+ ( pattern V, pattern VLeft, pattern VRight, runE )+import System.Directory ( doesFileExist )+import System.FilePath ( pathSeparator, (</>) )+import Text.PrettyPrint.HughesPJClass (Pretty(..), text, nest, vcat, ($$), ($+$))+import Witherable ( catMaybes, forMaybe )+++data SymlinkState = Fine FilePath FilePath+ | DestinationDoesn'tExist FilePath FilePath+ | WrongDestination FilePath FilePath FilePath+ | AllWrong FilePath FilePath FilePath+ | Missing FilePath+ deriving (Show, Ord, Eq)++instance Pretty SymlinkState where+ pPrint (DestinationDoesn'tExist f dest) = vcat $ fmap text+ [ "- Target does not exist:"+ , " * Symlink: " <> f+ , " * Target: " <> dest+ ]+ pPrint (WrongDestination f should actual) = vcat $ fmap text+ [ "- Wrong target:"+ , " * Symlink: " <> f+ , " * Target (should): " <> should+ , " * Target (actual): " <> actual+ ]+ pPrint (AllWrong f should actual) = vcat $ fmap text+ [ "- Wrong and broken target:"+ , " * Symlink: " <> f+ , " * Target (should): " <> should+ , " * Target (actual and broken): " <> actual+ ]+ pPrint (Missing f) = vcat $ fmap text+ [ "- Symlink missing:"+ , " * Symlink: " <> f+ ]+ pPrint (Fine f link) = vcat $ fmap text+ [ "- All good:"+ , " * Symlink: " <> f+ , " * Target: " <> link+ ]++data ToolHealthCheck = ToolHealthCheck {+ thHaveInstFile :: (FilePath, Bool)+ , thHaveSpecFile :: (FilePath, Bool)+ , thParseSpecFile :: Bool+ , thInstallDest :: FilePath+ , thSupposedInstalledFiles :: Maybe Int+ , thMissingFiles :: Maybe [FilePath]+ , thStrayFiles :: Maybe [FilePath]+ , thSymlinkState :: Maybe [SymlinkState]+ , thSetState :: Maybe [SymlinkState]+ }+ deriving (Show)++instance Pretty ToolHealthCheck where+ pPrint ToolHealthCheck{..} = vcat+ [ let (f, e) = thHaveInstFile in text $ "Installed artifacts file exists at " <> f <> ": " <> show e+ , let (f, e) = thHaveSpecFile in text $ "Installed specification file exists at " <> f <> ": " <> show e+ , text $ "Can parse specification file: " <> show thParseSpecFile+ , text $ "Install destination: " <> show thInstallDest+ , text $ "Number of files that should be installed: " <> maybe "(no information)" show thSupposedInstalledFiles+ , text "Missing files:" $$ nest 4 (ppFiles thMissingFiles)+ , text "Stray files:" $$ nest 4 (ppFiles thStrayFiles)+ , text "Symlink status:" $$ nest 4 (ppFiles thSymlinkState)+ , text "Set symlink status:" $$ nest 4 (ppFiles thSetState)+ ]+ where+ ppFiles Nothing = text "(no information)"+ ppFiles (Just []) = text "none"+ ppFiles (Just fs) = foldl' ($+$) mempty (map pPrint $ sort fs)++-- TODO+-- - (_:_:_) -> lift $ do+-- logWarn $ "Looks like you have a corrupted DB/installation\n"+-- <> "More than one revision installed of: " <> T.pack (prettyShow tool)+-- - detect legacy installations+dbHealthCheck ::+ ( MonadReader env m+ , HasDirs env+ , HasPlatformReq env+ , HasLog env+ , MonadIOish m+ )+ => Tool+ -> TargetVersion+ -> m ToolHealthCheck+dbHealthCheck tool tver = do+ Dirs{..} <- getDirs++ instFile <- recordedInstallationFile tool tver+ thHaveInstFile <- fmap (instFile,) $ liftIO $ doesFileExist instFile+ toolDest <- toolInstallDestination tool tver+ let thInstallDest = fromGHCupPath toolDest+ specFile <- recordedInstallationSpecFile tool tver++ (thHaveSpecFile, thParseSpecFile) <- fmap (\(a, b) -> ((specFile, a), b)) $ runE (getInstallMetadata tool tver) >>= \case+ VRight _ -> pure (True, True)+ VLeft (V (FileDoesNotExistError{})) -> pure (False, False)+ VLeft (V (ParseError{})) -> pure (True, False)+ VLeft _ -> fail "oops"++ symLSpec <- runE (getSymlinkSpec tool tver)++ thSymlinkState <- case symLSpec of+ VRight spec -> fmap Just $ do+ forMaybe spec $ \(SymlinkSpec target link _ _) -> do+ inspectSymlink (binDir </> link) target binDir toolDest+ VLeft _ -> pure Nothing++ installedFiles <- getInstalledFiles tool tver++ allFiles <- fmap (dropPrefix (fromGHCupPath toolDest <> [pathSeparator])) <$> liftIO (handleIO (\_ -> pure []) $ getFilesDeep toolDest)+ let thStrayFiles :: Maybe [FilePath] = installedFiles <&> \thif -> catMaybes $ allFiles <&> \f -> if f `notElem` thif then Just f else Nothing++ thMissingFiles <- forM installedFiles $ \mf -> forMaybe mf $ \f -> do+ b <- liftIO $ doesFileExist (fromGHCupPath toolDest </> f)+ if b then pure Nothing else pure (Just f)++ let thSupposedInstalledFiles = length <$> installedFiles++ thSetState <- runE (isSet tool tver) >>= \case+ VRight (Just _)+ | VRight spec <- symLSpec+ -> fmap Just $ forMaybe spec $ \case+ (SymlinkSpec _ _ _ Nothing) -> pure Nothing+ (SymlinkSpec target _ _ (Just setName)) -> inspectSymlink (binDir </> setName) target binDir toolDest+ VRight (Just _)+ | VLeft _ <- symLSpec+ -> pure Nothing+ VRight Nothing -> pure Nothing+ VLeft _ -> pure Nothing++ pure ToolHealthCheck{..}+ where+ inspectSymlink f target bindir tooldest = do+ mtfp <- try @_ @SomeException $ liftIO $ getLinkTarget f+ case mtfp of+ Left _ -> pure $ Just $ Missing f+ Right tfp -> do+ dest <- binarySymLinkDestination bindir (fromGHCupPath tooldest </> target)+ broken <- liftIO $ isBrokenSymlink f+ pure $ Just $ if | broken+ , tfp /= dest+ -> AllWrong f dest tfp+ | broken+ -> DestinationDoesn'tExist f tfp+ | tfp /= dest+ -> WrongDestination f dest tfp+ | otherwise+ -> Fine f dest+
+ lib/GHCup/Command/Install.hs view
@@ -0,0 +1,300 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}++module GHCup.Command.Install where++import GHCup.Builder+import GHCup.Command.Install.LowLevel+import GHCup.Command.Rm+import GHCup.Download+import GHCup.Errors+import GHCup.Input.SymlinkSpec+import GHCup.Prelude+import GHCup.Query.DB+import GHCup.Query.GHCupDirs+import GHCup.Query.Metadata+import GHCup.System.Cmd+import GHCup.Types+import GHCup.Types.Optics+import GHCup.Unpack++import Control.Applicative+import Control.Monad+#if !MIN_VERSION_base(4,13,0)+import Control.Monad.Fail ( MonadFail )+#endif+import Conduit ( runConduitRes, (.|) )+import Control.Monad.Reader+import Control.Monad.Trans.Resource hiding ( throwM )+import Data.Maybe+import Data.Variant.Excepts+import Optics+import Prelude hiding ( abs )+import System.FilePath ( dropDrive )+import System.IO.Error ( doesNotExistErrorType )+import Text.PrettyPrint.HughesPJClass ( prettyShow )++import qualified Data.Conduit.Combinators as C+import qualified Data.Text as T++++ -------------------------+ --[ Tool installation ]--+ -------------------------++-- | Installs cabal into @~\/.ghcup\/bin/cabal-\<ver\>@ and+-- creates a default @cabal -> cabal-x.y.z.q@ symlink for+-- the latest installed version.+installTool ::+ ( MonadReader env m+ , HasPlatformReq env+ , HasGHCupInfo env -- this is the main difference to the other functions+ , HasDirs env+ , HasSettings env+ , HasLog env+ , MonadResource m+ , MonadIOish m+ )+ => Tool+ -> TargetVersionReq+ -> InstallDir+ -> Bool+ -> [String]+ -> Maybe [String]+ -> Excepts+ '[ AlreadyInstalled+ , CopyError+ , DigestError+ , ContentLengthError+ , GPGError+ , DownloadFailed+ , NoDownload+ , NotInstalled+ , UnknownArchive+ , TarDirDoesNotExist+ , ArchiveResult+ , FileAlreadyExistsError+ , URIParseError+ , NoInstallInfo+ , MergeFileTreeError+ , ProcessError+ , ParseError+ , DirNotEmpty+ , UninstallFailed+ , MalformedInstallInfo+ ]+ m+ (InstallationSpecResolved, FilePath)+installTool tool treq installDir forceInstall extraArgs installTargets = do+ GHCupInfo { _ghcupDownloads = dls } <- getGHCupInfo+ (rev, dlinfo) <- liftE $ getDownloadInfoE' tool treq+ installBindist+ tool+ (preview (_GHCupDownloads % ix tool % toolDetails % _Just) dls)+ dlinfo+ (TargetVersionRev (treq ^. tvqTargetVer) rev)+ installDir+ forceInstall+ extraArgs+ installTargets++-- | Like 'installTool', except takes the 'DownloadInfo' as+-- argument instead of looking it up from 'GHCupDownloads'.+installBindist ::+ ( MonadReader env m+ , HasPlatformReq env+ , HasDirs env+ , HasSettings env+ , HasLog env+ , MonadResource m+ , MonadIOish m+ )+ => Tool+ -> Maybe ToolDescription+ -> DownloadInfo+ -> TargetVersionRev+ -> InstallDir+ -> Bool -- ^ Force install+ -> [String]+ -> Maybe [String]+ -> Excepts+ '[ AlreadyInstalled+ , CopyError+ , DigestError+ , ContentLengthError+ , GPGError+ , DownloadFailed+ , NoDownload+ , NotInstalled+ , UnknownArchive+ , TarDirDoesNotExist+ , ArchiveResult+ , FileAlreadyExistsError+ , URIParseError+ , NoInstallInfo+ , MergeFileTreeError+ , ProcessError+ , ParseError+ , DirNotEmpty+ , UninstallFailed+ , MalformedInstallInfo+ ]+ m+ (InstallationSpecResolved, FilePath)+installBindist tool toolDesc dlinfo trev@(TargetVersionRev tver rev) installDir forceInstall extraArgs installTargets = do+ lift $ logDebug $ "Requested to install "+ <> T.pack (prettyShow tool)+ <> " version " <> T.pack (prettyShow tver)++ installed <- lift $ isInstalled tool tver+ if+ | not forceInstall+ , Just irev <- installed+ , GHCupInternal <- installDir -> do+ if rev == irev+ then throwE $ AlreadyInstalled tool tver+ else do+ lift $ logInfo "Removing the currently installed revision first!"+ liftE $ rmToolVersion tool tver++ | forceInstall+ , Just _ <- installed+ , GHCupInternal <- installDir -> do+ lift $ logInfo "Removing the currently installed version first!"+ liftE $ rmToolVersion tool tver++ | otherwise -> pure ()+++ -- download (or use cached version)+ dl <- liftE $ downloadCached dlinfo Nothing++ case installDir of+ IsolateDir isoDir -> do -- isolated install+ lift $ logInfo $ "isolated installing Cabal to " <> T.pack isoDir+ installSpec <- liftE $ installPackedBindist tool toolDesc dl dlinfo (IsolateDirResolved isoDir) trev forceInstall extraArgs installTargets+ pure (installSpec, isoDir)++ GHCupInternal -> do -- regular install+ instDir <- lift $ toolInstallDestination tool tver+ installSpec <- liftE $ installPackedBindist tool toolDesc dl dlinfo (GHCupDir instDir) trev forceInstall extraArgs installTargets+ pure (installSpec, fromGHCupPath instDir)++installPackedBindist ::+ ( MonadReader env m+ , HasDirs env+ , HasPlatformReq env+ , HasSettings env+ , HasLog env+ , MonadResource m+ , MonadIOish m+ )+ => Tool+ -> Maybe ToolDescription+ -> FilePath -- ^ Path to the tarball+ -> DownloadInfo+ -> InstallDirResolved -- ^ Path to install to+ -> TargetVersionRev+ -> Bool+ -> [String]+ -> Maybe [String]+ -> Excepts '[ CopyError+ , FileAlreadyExistsError+ , MergeFileTreeError+ , ProcessError+ , ParseError+ , UnknownArchive+ , TarDirDoesNotExist+ , DirNotEmpty+ , ArchiveResult+ , NoInstallInfo+ , MalformedInstallInfo+ ] m InstallationSpecResolved+installPackedBindist tool toolDesc dl dlInfo inst trev forceInstall extraArgs installTargets = do+ PlatformRequest {..} <- lift getPlatformReq++ unless forceInstall+ (liftE $ installDestSanityCheck inst)++ tmpUnpack <- lift withGHCupTmpDir+ liftE $ cleanUpOnError tmpUnpack (unpackToDir (fromGHCupPath tmpUnpack) dl)+ liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform+ (fromGHCupPath tmpUnpack)++ -- the subdir of the archive where we do the work+ workdir <- fromGHCupPath <$> maybe (pure tmpUnpack)+ (liftE . intoSubdir tmpUnpack) (view dlSubdir dlInfo)++ tmpInstallDest <- lift withGHCupTmpDir++ liftE $ runBuildAction tmpUnpack $ installUnpackedBindist tool toolDesc workdir+ inst tmpInstallDest dlInfo trev forceInstall extraArgs installTargets++-- | Install an unpacked distribution.+installUnpackedBindist :: forall m env .+ ( HasLog env+ , HasDirs env+ , HasSettings env+ , HasPlatformReq env+ , MonadReader env m+ , MonadIOish m+ )+ => Tool+ -> Maybe ToolDescription+ -> FilePath -- ^ Path to the unpacked cabal bindist (where the executable resides)+ -> InstallDirResolved -- ^ Path to install to+ -> GHCupPath -- ^ DESTDIR+ -> DownloadInfo+ -> TargetVersionRev+ -> Bool+ -> [String]+ -> Maybe [String]+ -> Excepts '[ CopyError+ , FileAlreadyExistsError+ , MergeFileTreeError+ , ProcessError+ , ParseError+ , NoInstallInfo+ , MalformedInstallInfo+ ] m InstallationSpecResolved+installUnpackedBindist tool toolDesc workdir installDest tmpInstallDest dlInfo trev@(TargetVersionRev tver _rev) forceInstall extraArgs installTargets = do+ instSpec <- liftE $ installationSpecFromMetadata' dlInfo tool tver+ lift $ logInfo $ "Installing " <> T.pack (prettyShow tool)+ liftE $ installTheSpec instSpec workdir installDest tmpInstallDest extraArgs installTargets forceInstall+ let preserveMtimes = _isPreserveMtimes instSpec+ resolvedSymlinkSpecs <- liftE $ forM (view isExeSymLinked instSpec) (resolveSymlinkSpec (tmpInstallDest `appendGHCupPath` dropDrive (fromInstallDir installDest)))+ let resolvedInstSpec = resolveInstallationSpec (mconcat resolvedSymlinkSpecs) instSpec++ -- then merge to the live filesystem+ liftE $ mergeToFileSystem tool tver installDest tmpInstallDest preserveMtimes forceInstall False++ -- TODO: move to optparse install+ -- symlinking+ Dirs {..} <- lift getDirs+ parsedSymlinkSpec <- forM (_isExeSymLinked resolvedInstSpec) (liftE . parseSymlinkSpec (_tvVersion tver))+ liftE $ symlinkBinaries installDest parsedSymlinkSpec (GHCupBinDir binDir) tool tver++ -- write InstallationInfo to the disk+ lift $ recordInstallationInfo installDest tool toolDesc trev dlInfo resolvedInstSpec+ pure resolvedInstSpec+++-- | Does basic checks for isolated installs+-- Isolated Directory:+-- 1. if it doesn't exist -> proceed+-- 2. if it exists and is empty -> proceed+-- 3. if it exists and is non-empty -> panic and leave the house+installDestSanityCheck :: ( MonadIOish m+ ) =>+ InstallDirResolved ->+ Excepts '[DirNotEmpty] m ()+installDestSanityCheck (IsolateDirResolved isoDir) = do+ hideErrorDef [doesNotExistErrorType] () $ do+ empty' <- lift $ runConduitRes $ getDirectoryContentsRecursiveUnsafe isoDir .| C.null+ when (not empty') (throwE $ DirNotEmpty isoDir)+installDestSanityCheck _ = pure ()
+ lib/GHCup/Command/Install/LowLevel.hs view
@@ -0,0 +1,281 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}++module GHCup.Command.Install.LowLevel where++import GHCup.Errors+import GHCup.Input.SymlinkSpec+import GHCup.Prelude+import GHCup.Prelude.Process+import GHCup.Query.DB+import GHCup.Query.GHCupDirs+import GHCup.Query.Symlink+import GHCup.System.Cmd+import GHCup.System.Directory+import GHCup.Types+import GHCup.Types.Optics++import Control.Applicative+import Control.Exception.Safe+import Control.Monad+import Data.Yaml.Pretty+#if !MIN_VERSION_base(4,13,0)+import Control.Monad.Fail ( MonadFail )+#endif+import Control.Monad.Reader+import Data.Maybe+import Data.Variant.Excepts+import Data.Versions hiding ( patch )+import Data.Void+import Prelude hiding ( abs )+import System.FilePath+import System.FilePattern.Directory++import qualified Data.ByteString as B+import qualified Data.Text as T+import qualified System.FilePath.Posix as Posix+import qualified Text.Megaparsec as MP++++++mergeToFileSystem ::+ ( MonadMask m+ , MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadCatch m+ , MonadIOish m+ )+ => Tool+ -> TargetVersion+ -> InstallDirResolved -- ^ Path to install to+ -> GHCupPath -- ^ DESTDIR+ -> Bool+ -> Bool+ -> Bool+ -> Excepts '[MergeFileTreeError] m ()+mergeToFileSystem tool tver installDest tmpInstallDest preserveMtimes forceInstall append = do+ mergeFileTree+ (tmpInstallDest `appendGHCupPath` dropDrive (fromInstallDir installDest))+ installDest+ tool+ tver+ (\f t -> if preserveMtimes+ then liftIO $ copyFuncMtime f t+ else liftIO $ install f t (not forceInstall)+++ )+ append+ where+ copyFuncMtime f t = do+ mtime <- ifM (pathIsSymbolicLink f) (pure Nothing) (Just <$> getModificationTime f)+ install f t (not forceInstall)+ forM_ mtime $ setModificationTime t++installTheSpec ::+ ( HasLog env+ , HasDirs env+ , HasSettings env+ , MonadReader env m+ , MonadIOish m+ )+ => InstallationSpecInput+ -> FilePath -- ^ Path to the unpacked bindist+ -> InstallDirResolved -- ^ Path to install to+ -> GHCupPath -- ^ DESTDIR+ -> [String]+ -> Maybe [String]+ -> Bool+ -> Excepts '[ CopyError+ , MergeFileTreeError+ , ProcessError+ , ParseError+ , MalformedInstallInfo+ ] m ()+installTheSpec InstallationSpec{..} workdir installDest tmpInstallDest extraArgs installTargets forceInstall = do+ -- run configure first+ case _isConfigure of+ Just ConfigSpec{..} -> do+ addConfArgs <- lift $ sanitizefConfOptions extraArgs++ let configFile = fromMaybe "configure" _csConfigFile+ processedConfArgs <- forM _csConfigArgs $ \a -> either (throwE . ParseError . show) pure . parseDomain $ a+ newEnv <- forM _csConfigEnv $ \EnvSpec{..} -> liftIO (augmentEnvironment _esEnv _esUnion)+ liftE $ execWithWrapper "sh"+ (("." Posix.</> configFile) : processedConfArgs <> addConfArgs+ )+ (Just workdir)+ "ghc-configure"+ newEnv+ _ -> pure ()++ -- then make+ case _isMake of+ Just MakeSpec{..} -> do+ processedMakeArgs <- either (throwE . ParseError . show) pure $ forM (fromMaybe _msMakeArgs installTargets) parseDomain+ newEnv <- forM _msMakeEnv $ \EnvSpec{..} -> liftIO (augmentEnvironment _esEnv _esUnion)+ liftE $ makeWithWrapper processedMakeArgs (Just workdir) "make" newEnv+ _ -> pure ()++ -- then copy files manually, if any+ forM_ _isExeRules $ \case+ InstallFileRule{..} -> do+ f <- copy _ibrInstallSource _ibrInstallDest+ lift $ chmod_755 f+ InstallFilePatternRule{..} -> do+ files <- liftIO $ getDirectoryFiles workdir _ibrInstallPattern+ forM_ files $ \file -> do+ f <- copy file Nothing+ lift $ chmod_755 f++ forM_ _isDataRules $ \case+ InstallFileRule{..} ->+ void $ copy _ibrInstallSource _ibrInstallDest+ InstallFilePatternRule{..} -> do+ files <- liftIO $ getDirectoryFiles workdir _ibrInstallPattern+ forM_ files $ \file -> copy file Nothing++ where+ copy source destFile = do+ let from = workdir </> source+ destDir = fromGHCupPath tmpInstallDest </> dropDrive (fromInstallDir installDest)+ into = destDir </> fromMaybe source destFile+ liftIO $ createDirRecursive' (takeDirectory into)+ copyFileE from into (not forceInstall)+ logDebug $ "cp " <> (if forceInstall then "-f " else "") <> T.pack from <> " " <> T.pack into+ pure into++ parseDomain = MP.parse domainParser ""++ domainParser :: MP.Parsec Void String String+ domainParser = concat <$> many anyOrKnownVars+ where+ -- TODO: make this more efficient+ anyOrKnownVars :: MP.Parsec Void String String+ anyOrKnownVars = MP.try (fmap (const (fromInstallDir installDest)) (MP.chunk "${PREFIX}"))+ <|> MP.try (fmap (const (fromGHCupPath tmpInstallDest)) (MP.chunk "${TMPDIR}"))+ <|> fmap (:[]) MP.anySingle++makeWithWrapper ::+ ( MonadIOish m+ , HasSettings env+ , HasLog env+ , HasDirs env+ , MonadReader env m+ )+ => [String]+ -> Maybe FilePath+ -> FilePath+ -> Maybe [(String, String)]+ -> Excepts+ '[ ProcessError+ ]+ m+ ()+makeWithWrapper args' mWorkDir logLabel mEnv = do+ mymake <- liftIO getBestMake+ liftE $ execWithWrapper mymake args' mWorkDir logLabel mEnv++execWithWrapper ::+ ( MonadIOish m+ , HasSettings env+ , HasLog env+ , HasDirs env+ , MonadReader env m+ )+ => String+ -> [String]+ -> Maybe FilePath+ -> FilePath+ -> Maybe [(String, String)]+ -> Excepts+ '[ ProcessError+ ]+ m+ ()+execWithWrapper cmd' args' mWorkDir logLabel mEnv = do+ Settings{ buildWrapper } <- lift getSettings+ case buildWrapper of+ Just ProcessSpec{..} ->+ lEM $ execLogged cmd (cmdArgs <> ["--"] <> (cmd':args')) mWorkDir logLabel mEnv+ Nothing ->+ lEM $ execLogged cmd' args' mWorkDir logLabel mEnv++-- | Write InstallationInfo to the disk.+recordInstallationInfo ::+ ( MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadIOish m+ )+ => InstallDirResolved+ -> Tool+ -> Maybe ToolDescription+ -> TargetVersionRev+ -> DownloadInfo+ -> InstallationSpecResolved+ -> m ()+recordInstallationInfo installDest tool toolDesc TargetVersionRev{..} dlInfo instSpec+ | isSafeDir installDest = do+ spec <- recordedInstallationSpecFile tool _tvrTargetVer+ liftIO $ createDirectoryIfMissing True (takeDirectory spec)+ let metadata = InstallMetadata dlInfo instSpec toolDesc _tvrRev+ logDebug2 $ "Writing install metadata to " <> T.pack spec <> "\n " <> T.pack (show metadata)+ liftIO $ encodeFilePretty spec metadata+ | otherwise = do+ logDebug2 $ "Skipping spec installation, because installing into isolated dir: " <> T.pack (show installDest)+ pure ()+ where+ encodeFilePretty file json =+ let encoded = encodePretty defConfig json+ in B.writeFile file encoded++sanitizefConfOptions :: MonadFail m => [String] -> m [String]+sanitizefConfOptions args+ | "--prefix" `elem` fmap (takeWhile (/= '=')) args = fail "Don't explicitly set --prefix ...aborting"+ | otherwise = pure args+++-- TODO: move to other module+-- | Symlink e.g. @~/.ghcup/ghc/9.6.7/bin/ghc@ to+-- @~/.ghcup/bin/ghc-9.6.7@.+--+-- But we follow the spec from the metadata.+symlinkBinaries ::+ ( HasLog env+ , MonadReader env m+ , HasDirs env -- createLink needs it on windows+ , MonadIOish m+ )+ => InstallDirResolved -- ^ base dir of the tool+ -> [SymlinkSpec [Either Char Version]] -- ^ symlink spec+ -> InstallDirResolved -- ^ binary dir+ -> Tool+ -> TargetVersion+ -> Excepts '[MalformedInstallInfo] m ()+symlinkBinaries (IsolateDirResolved _) _ _ _ _ = pure ()+symlinkBinaries (fromInstallDir -> toolDir) rawSpec bindir tool tver = do+ let spec = substituteSpec <$> rawSpec+ pvpSyms <- lift $ getPVPSymlinks' (fromInstallDir bindir) spec toolDir+ forM_ pvpSyms $ lift . uncurry createLink+ case bindir of+ IsolateDirResolved d -> linkMajor d+ GHCupDir d -> linkMajor (fromGHCupPath d)+ GHCupBinDir d -> whenM (lift isLatest) $ linkMajor d+ where+ linkMajor d = do+ let ver = _tvVersion tver+ pvpMajorSyms <- liftE $ getPVPMajorSymlinks' d rawSpec ver toolDir+ forM_ pvpMajorSyms $ lift . uncurry createLink+ isLatest = do+ vers <- getInstalledVersions tool (_tvTarget tver)+ let latestVer = _vrVersion $ maximum vers+ pure $ latestVer == _tvVersion tver+
+ lib/GHCup/Command/List.hs view
@@ -0,0 +1,368 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE UndecidableInstances #-}++{-|+Module : GHCup.Command.List+Description : Listing versions and tools+Copyright : (c) Julian Ospald, 2020+License : LGPL-3.0+Maintainer : hasufell@hasufell.de+Stability : experimental+Portability : portable+-}+module GHCup.Command.List where++import GHCup.Errors+import GHCup.Query.DB+import GHCup.Query.DB.HLS ( getHLSGHCs )+import GHCup.Query.Metadata+import GHCup.Types+import GHCup.Types.JSON+ ()+import GHCup.Types.Optics++import Control.Applicative+import Control.DeepSeq ( NFData )+import Control.Monad+#if !MIN_VERSION_base(4,13,0)+import Control.Monad.Fail ( MonadFail )+#endif+import Control.Monad.Reader+import Control.Monad.ST+import Data.Bifunctor+import Data.List+import Data.Maybe+import Data.STRef+import Data.Set ( Set )+import Data.Text ( Text )+import Data.Time.Calendar ( Day )+import Data.Traversable.WithIndex ( iforM )+import Data.Variant.Excepts+import Data.Versions hiding ( patch )+import Optics+import Prelude hiding ( abs, writeFile )++import qualified Data.Map.Strict as M+import qualified Data.Map.Strict as Map+import qualified GHC.Generics as GHC+++++++ ------------------+ --[ List tools ]--+ ------------------+++-- | Filter data type for 'listVersions'.+data ListCriteria+ = ListInstalled Bool+ | ListSet Bool+ | ListAvailable Bool+ deriving (Eq, Show)+++type ToolListResult = M.Map Tool (Maybe ToolDescription, [ListResult])+type ProcessedListResult = M.Map Tool (Maybe ToolDescription, M.Map (Maybe Text) (M.Map Version (M.Map Int ListResult)))++data RevTag = RevUpdate+ | RevOutdated+ | RevNormal+ deriving (Eq, Ord, Show, GHC.Generic)++instance NFData RevTag++-- | A list result describes a single tool version+-- and various of its properties.+data ListResult = ListResult+ { lVer :: Version+ , lCross :: Maybe Text+ -- ^ currently only for GHC+ , lRev :: (Int, RevTag) -- the Bool indicates whether this is an update from an older rev+ , lTag :: [Tag]+ , lInstalled :: Bool+ , lSet :: Bool+ -- ^ currently active version+ , lStray :: Bool+ -- ^ not in download info+ , lNoBindist :: Bool+ -- ^ whether the version is available for this platform/arch+ , hlsPowered :: Bool+ , lReleaseDay :: Maybe Day+ }+ deriving (Eq, Ord, Show, GHC.Generic)++instance NFData ListResult++makeLensesWith (lensRules & lensField .~ mappingNamer (\n -> [n <> "L"])) ''ListResult++-- | Extract all available tool versions and their tags.+availableToolVersions :: GHCupDownloads -> Tool -> Map.Map TargetVersion VersionMetadata+availableToolVersions av tool = view+ (_GHCupDownloads % at tool % to ((unToolVersionSpec . _toolVersions) <$>) % non Map.empty)+ av+++-- | List all versions from the download info, as well as stray+-- versions.+listVersions ::+ forall m env .+ ( HasLog env+ , MonadReader env m+ , HasDirs env+ , HasPlatformReq env+ , HasGHCupInfo env+ , MonadIOish m+ )+ => Maybe [Tool]+ -> [ListCriteria]+ -> ShowRevisions+ -> Bool+ -> Bool+ -> (Maybe Day, Maybe Day)+ -> Excepts '[ParseError] m ToolListResult+listVersions lt' criteria showRevisions hideOld showNightly days = do+ GHCupInfo { _ghcupDownloads = dls } <- getGHCupInfo+ pfreq <- lift getPlatformReq+ instTools <- getAllInstalledTools lt'+ hlsGHCs <- let hlsSet = do+ hlsMap <- instTools M.!? hls+ (_, mSet) <- hlsMap M.!? Nothing+ mSet+ in case hlsSet of+ Just VersionRev{..} -> do+ getHLSGHCs _vrVersion+ Nothing -> pure []+ pure $ listVersions' dls pfreq instTools hlsGHCs lt' criteria showRevisions hideOld showNightly days++listVersions' ::+ GHCupDownloads+ -> PlatformRequest+ -> Map.Map Tool (Map.Map (Maybe Text) ([VersionRev], Maybe VersionRev))+ -> [Version]+ -> Maybe [Tool]+ -> [ListCriteria]+ -> ShowRevisions+ -> Bool+ -> Bool+ -> (Maybe Day, Maybe Day)+ -> ToolListResult+listVersions' dls pfreq instTools hlsGHCs lt' criteria showRevisions hideOld showNightly days =+ -- available tools from the metadata+ let allAvailableTools' :: [(Tool, [(TargetVersion, VersionMetadata)])]+ = maybe (allAvailableTools dls) (\ts -> filter (\(t, _) -> t `elem` ts) $ allAvailableTools dls) lt'+ avTools :: M.Map Tool (M.Map (Maybe Text) (Set (Version, VersionMetadata)))+ = M.fromList $ (fmap . fmap) groupByTargetS allAvailableTools'++ avToolsProcessed = runST $ do+ stRefAvToolsProcessed <- newSTRef mempty++ -- process installed tools first+ void $ iforM instTools $ \tool targetMap -> do+ iforM targetMap $ \target (vers', mset) ->+ forM_ vers' $ \vr@VersionRev{..} -> do+ let tver = TargetVersion target _vrVersion+ mvm = getVersionMetadata tver tool dls+ tags = maybe [] _vmTags mvm+ lReleaseDay = mvm >>= _vmReleaseDay+ lRev = (_vrRev, RevNormal)+ tDesc = preview (_GHCupDownloads % ix tool % toolDetails % _Just) dls++ hlsPowered = getHlsPowered tool target _vrVersion++ -- lNoBindist and lStray are updated when we traverse the metadata+ -- bindist tags (as opposed to tool tags) will also be added later+ let lr = ListResult { lVer = _vrVersion+ , lCross = target+ , lTag = tags+ , lSet = mset == Just vr+ , lInstalled = True+ , lNoBindist = False+ , lStray = True+ , ..+ }++ insertListResult stRefAvToolsProcessed tool target tDesc _vrVersion _vrRev lr++ installedProcessed <- readSTRef stRefAvToolsProcessed++ -- then process tools in the metadata, that are not installed+ -- and add the installed ones in the process+ void $ iforM avTools $ \tool targetMap -> do+ let tDesc = preview (ix tool % _1 % _Just) installedProcessed <|> preview (_GHCupDownloads % ix tool % toolDetails % _Just) dls+ iforM targetMap $ \target vers' ->+ forM_ vers' $ \(ver', VersionMetadata{..}) -> do+ -- versions from the metadata that's already installed+ let avInstVers :: M.Map Version (M.Map Int ListResult) =+ fromMaybe mempty $ do+ (_, m) <- installedProcessed M.!? tool+ m M.!? target++ let lReleaseDay = _vmReleaseDay++ let (Rev latestMetaRev) = maximum $ M.keys (unRevisionSpec _vmRevisionSpec)+++ -- add all revision+ forM_ (M.toList (unRevisionSpec _vmRevisionSpec)) $ \(Rev metaRev, vi) -> do+ let mdli = getDownloadInfo' pfreq vi+ let bTags = fromMaybe [] $ mdli >>= _dlTag++ -- this has to consider the installed rev as well,+ -- which may or may not be in the metadata++ case iheadOf (ix ver' % itraversed) avInstVers of+ -- it's installed+ Just (installedRev, installedLr) -> do+ if installedRev == metaRev+ then do -- and even the same revision+ -- add information from the metadata to the installed rev+ modifyListResult stRefAvToolsProcessed tool target ver' installedRev+ [ lNoBindistL .~ (isNothing mdli && not (lStray installedLr))+ , lTagL %~ (<> bTags)+ , lStrayL .~ False+ ]+ else do -- installed, but not this revision+ when (showRevisions /= ShowNone) $ do++ -- is it the latest revision?+ let isLatestRev = metaRev == latestMetaRev && (metaRev > installedRev)+ -- it could be an update+ let isUpdate = isLatestRev++ -- tag shenanigans+ let dropOld = if isUpdate then filter (/= Old) else id+ filterNotLatestRec = filter (`notElem` [Latest, Recommended])+ dropLatestRec = if isLatestRev then id else filterNotLatestRec++ when isUpdate $+ -- remove the latest/recommended tag from the installed revision+ modifyListResult stRefAvToolsProcessed tool target ver' installedRev+ [ lRevL % _2 .~ RevOutdated+ , lTagL %~ filterNotLatestRec+ ]++ -- Insert the installed revision+ -- updates are always inserted, but non-updates are only relevant if the user wants to see all revisions+ let lrNew = ListResult+ { lVer = ver'+ , lCross = target+ , lTag = dropLatestRec $ dropOld (_vmTags <> bTags)+ , lSet = False+ , lInstalled = False+ , hlsPowered = False+ , lNoBindist = isNothing mdli+ , lStray = False+ , lRev = (metaRev, if isUpdate then RevUpdate else RevNormal)+ , ..+ }+ when (showRevisions == ShowAll || isUpdate) $+ insertListResult stRefAvToolsProcessed tool target tDesc ver' metaRev lrNew+ -- not installed, just insert+ Nothing -> do+ let isLatestRev = metaRev == latestMetaRev+ let lr = ListResult+ { lVer = ver'+ , lCross = target+ , lTag = _vmTags <> bTags+ , lSet = False+ , lInstalled = False+ , hlsPowered = False+ , lNoBindist = isNothing mdli+ , lStray = False+ , lRev = (metaRev, RevNormal)+ , ..+ }+ when (showRevisions == ShowAll || isLatestRev) $+ insertListResult stRefAvToolsProcessed tool target tDesc ver' metaRev lr+ readSTRef stRefAvToolsProcessed++ in toToolListResult avToolsProcessed++ where+ toToolListResult :: ProcessedListResult -> ToolListResult+ toToolListResult = M.map (second (toListOf (traversed % traversed % traversed)))++ modifyListResult ::+ STRef s ProcessedListResult+ -> Tool+ -> Maybe Text+ -> Version+ -> Int+ -> [ListResult -> ListResult]+ -> ST s ()+ modifyListResult ref tool target ver' rev setters =+ modifySTRef' ref+ (ix tool % _2 % ix target % ix ver' % at rev %~ (>>= (\lr' -> filter' (foldl (&) lr' setters))))++ insertListResult ::+ STRef s ProcessedListResult+ -> Tool+ -> Maybe Text+ -> Maybe ToolDescription+ -> Version+ -> Int+ -> ListResult+ -> ST s ()+ insertListResult ref tool target tDesc ver' rev lr' =+ modifySTRef' ref+ (at tool % non (tDesc, mempty) % _2 % at target % non mempty % at ver' % non mempty % at rev .~ filter' lr')++ getHlsPowered tool target ver' =+ (tool == ghc && isNothing target) && ver' `elem` hlsGHCs++ filter' :: ListResult -> Maybe ListResult+ filter' lr = do+ lrN <- filterNightly lr+ lrOld <- filterOld lrN+ lrCrit <- if foldr (\a b -> fromCriteria a lrOld && b) True criteria+ then Just lrOld+ else Nothing+ filterDays lrCrit++ filterDays :: ListResult -> Maybe ListResult+ filterDays lr@ListResult{..} =+ case days of+ (Nothing, Nothing) -> Just lr+ (Just from, Just to')+ | Just True <- (\d -> d >= from && d <= to') <$> lReleaseDay+ -> Just lr+ | otherwise -> Nothing+ (Nothing, Just to')+ | Just True <- (<= to') <$> lReleaseDay+ -> Just lr+ | otherwise -> Nothing+ (Just from, Nothing)+ | Just True <- (>= from) <$> lReleaseDay+ -> Just lr+ | otherwise -> Nothing++ fromCriteria :: ListCriteria -> ListResult -> Bool+ fromCriteria lc ListResult{..} = case lc of+ ListInstalled b -> f b lInstalled+ ListSet b -> f b lSet+ ListAvailable b -> f b $ not lNoBindist+ where+ f b+ | b = id+ | otherwise = not++ filterOld :: ListResult -> Maybe ListResult+ filterOld lr@ListResult {..}+ | hideOld = if lInstalled || Old `notElem` lTag then Just lr else Nothing+ | otherwise = Just lr++ filterNightly :: ListResult -> Maybe ListResult+ filterNightly lr@ListResult {..}+ | showNightly = Just lr+ | otherwise = if lInstalled || (Nightly `notElem` lTag && LatestNightly `notElem` lTag)+ then Just lr+ else Nothing+
+ lib/GHCup/Command/Nuke.hs view
@@ -0,0 +1,139 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}++{-|+Module : GHCup.Command.Nuke+Description : GHCup nuke+Copyright : (c) Julian Ospald, 2020+License : LGPL-3.0+Maintainer : hasufell@hasufell.de+Stability : experimental+Portability : portable++This module contains the main functions that correspond+to the command line interface, like installation, listing versions+and so on.++These are the entry points.+-}+module GHCup.Command.Nuke where+++import GHCup.Prelude+import GHCup.Query.GHCupDirs+import GHCup.Types+import GHCup.Types.Optics++import Conduit ( sourceToList )+import Control.Applicative+import Control.Exception.Safe+import Control.Monad+#if !MIN_VERSION_base(4,13,0)+import Control.Monad.Fail ( MonadFail )+#endif+import Control.Monad.Reader+import Control.Monad.Trans.Resource hiding ( throwM )+import Data.List+import Prelude hiding ( abs, writeFile )+import System.FilePath+import System.IO.Error++import qualified Data.Text as T++++++ ------------+ --[ Nuke ]--+ ------------+++rmGhcupDirs :: ( MonadReader env m+ , HasDirs env+ , MonadIO m+ , MonadUnliftIO m+ , HasLog env+ , MonadCatch m+ , MonadMask m )+ => m [FilePath]+rmGhcupDirs = do+ Dirs+ { baseDir+ , binDir+ , logsDir+ , cacheDir+ , recycleDir+ , dbDir+ , tmpDir+ } <- getDirs++ let envFilePath = fromGHCupPath baseDir </> "env"++ confFilePath <- getConfigFilePath++ handleRm $ rmEnvFile envFilePath+ handleRm $ rmConfFile confFilePath++ -- for xdg dirs, the order matters here+ handleRm $ rmPathForcibly logsDir+ handleRm $ rmPathForcibly tmpDir+ handleRm $ rmPathForcibly cacheDir++ handleRm $ rmBinDir binDir+ handleRm $ rmPathForcibly recycleDir+ handleRm $ rmPathForcibly dbDir+ when isWindows $ do+ logInfo $ "removing " <> T.pack (fromGHCupPath baseDir </> "msys64")+ handleRm $ rmPathForcibly (baseDir `appendGHCupPath` "msys64")++ handleRm $ removeEmptyDirsRecursive (fromGHCupPath baseDir)++ -- report files in baseDir that are left-over after+ -- the standard location deletions above+ hideErrorDef [doesNotExistErrorType] [] $ reportRemainingFiles (fromGHCupPath baseDir)++ where+ handleRm :: (MonadReader env m, MonadCatch m, HasLog env, MonadIO m) => m () -> m ()+ handleRm = handleIO (\e -> logDebug $ "Part of the cleanup action failed with error: " <> T.pack (displayException e) <> "\n"+ <> "continuing regardless...")++ rmEnvFile :: (HasLog env, MonadReader env m, HasDirs env, MonadMask m, MonadIO m, MonadCatch m) => FilePath -> m ()+ rmEnvFile enFilePath = do+ logInfo "Removing Ghcup Environment File"+ hideErrorDef [permissionErrorType] () $ rmFileForce enFilePath++ rmConfFile :: (HasLog env, MonadReader env m, HasDirs env, MonadMask m, MonadIO m, MonadCatch m) => FilePath -> m ()+ rmConfFile confFilePath = do+ logInfo "removing Ghcup Config File"+ hideErrorDef [permissionErrorType] () $ rmFileForce confFilePath++ rmBinDir :: (MonadReader env m, HasDirs env, MonadMask m, MonadIO m, MonadCatch m) => FilePath -> m ()+ rmBinDir binDir+ | isWindows = removeDirIfEmptyOrIsSymlink binDir+ | otherwise = do+ isXDGStyle <- liftIO useXDG+ when (not isXDGStyle) $+ removeDirIfEmptyOrIsSymlink binDir++ reportRemainingFiles :: (MonadMask m, MonadIO m, MonadUnliftIO m) => FilePath -> m [FilePath]+ reportRemainingFiles dir = do+ remainingFiles <- runResourceT $ sourceToList $ getDirectoryContentsRecursiveUnsafe dir+ let normalizedFilePaths = fmap normalise remainingFiles+ let sortedByDepthRemainingFiles = sortBy (flip compareFn) normalizedFilePaths+ let remainingFilesAbsolute = fmap (dir </>) sortedByDepthRemainingFiles++ pure remainingFilesAbsolute++ where+ calcDepth :: FilePath -> Int+ calcDepth = length . filter isPathSeparator++ compareFn :: FilePath -> FilePath -> Ordering+ compareFn fp1 fp2 = compare (calcDepth fp1) (calcDepth fp2)++
+ lib/GHCup/Command/Prefetch.hs view
@@ -0,0 +1,119 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}++{-|+Module : GHCup.Command.Prefetch+Description : GHCup installation functions+Copyright : (c) Julian Ospald, 2020+License : LGPL-3.0+Maintainer : hasufell@hasufell.de+Stability : experimental+Portability : portable+-}+module GHCup.Command.Prefetch where+++import GHCup.Download+import GHCup.Errors+import GHCup.Prelude+import GHCup.Query.Metadata+import GHCup.Types+import GHCup.Types.JSON+ ()+import GHCup.Types.Optics++import Control.Exception.Safe+import Control.Monad+#if !MIN_VERSION_base(4,13,0)+import Control.Monad.Fail ( MonadFail )+#endif+import Control.Monad.Reader+import Control.Monad.Trans.Resource hiding ( throwM )+import Data.Maybe+import Data.Variant.Excepts+import Optics+import Prelude hiding ( abs, writeFile )++++ ---------------------+ --[ Tool fetching ]--+ ---------------------+++fetchToolBindist :: ( MonadFail m+ , MonadMask m+ , MonadCatch m+ , MonadReader env m+ , HasDirs env+ , HasSettings env+ , HasPlatformReq env+ , HasGHCupInfo env+ , HasLog env+ , MonadResource m+ , MonadIO m+ , MonadUnliftIO m+ )+ => TargetVersionReq+ -> Tool+ -> Maybe FilePath+ -> Excepts+ '[ DigestError+ , ContentLengthError+ , GPGError+ , DownloadFailed+ , NoDownload+ , URIParseError+ ]+ m+ FilePath+fetchToolBindist v t mfp = do+ (_, dlinfo) <- liftE $ getDownloadInfoE' t v+ liftE $ downloadCached' dlinfo Nothing mfp+++fetchToolSrc :: ( MonadFail m+ , MonadMask m+ , MonadCatch m+ , MonadReader env m+ , HasDirs env+ , HasSettings env+ , HasPlatformReq env+ , HasGHCupInfo env+ , HasLog env+ , MonadResource m+ , MonadIO m+ , MonadUnliftIO m+ )+ => Tool+ -> TargetVersionReq+ -> Maybe FilePath+ -> Excepts+ '[ DigestError+ , ContentLengthError+ , GPGError+ , DownloadFailed+ , NoDownload+ , URIParseError+ ]+ m+ FilePath+fetchToolSrc tool v@(TargetVersionReq{..}) mfp = do+ GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo+ dlInfo <-+ preview (_GHCupDownloads+ % ix tool+ % toolVersionsL+ % ix _tvqTargetVer+ % revisionSpecL+ % ixOrLast _tvqRev+ % _2+ % viSourceDL+ % _Just+ ) dls+ ?? NoDownload v ghc Nothing+ liftE $ downloadCached' dlInfo Nothing mfp+
+ lib/GHCup/Command/Rm.hs view
@@ -0,0 +1,218 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}++module GHCup.Command.Rm where++import GHCup.Errors+import GHCup.Input.SymlinkSpec+import GHCup.Legacy.Cabal+import GHCup.Legacy.HLS+import GHCup.Legacy.Stack+import GHCup.Legacy.Utils+import GHCup.Prelude+import GHCup.Query.DB+import GHCup.Query.GHCupDirs+import GHCup.Query.Symlink+import GHCup.System.Directory+import GHCup.Types+import GHCup.Types.Optics++import Control.Applicative+import Control.Exception.Safe+import Control.Monad+#if !MIN_VERSION_base(4,13,0)+import Control.Monad.Fail ( MonadFail )+#endif+import Control.Monad.Reader+import Control.Monad.Trans.Resource hiding ( throwM )+import Data.List+import Data.Maybe+import Data.Variant.Excepts+import GHC.IO.Exception+import Prelude hiding ( abs, writeFile )+import System.Environment+import System.FilePath+import System.IO.Error+import System.IO.Temp++import qualified Data.Text as T+++-- | Remove a tool completely.+--+-- This has two phases:+--+-- 1. the files installed from the bindist+-- 2. the files symlinked into the GHCup bin directory+--+-- Both are stored on the DB, but in different files/formats.+rmToolVersion ::+ ( MonadReader env m+ , HasDirs env+ , HasLog env+ , HasPlatformReq env+ , MonadIOish m+ )+ => Tool+ -> TargetVersion+ -> Excepts '[NotInstalled, UninstallFailed, ParseError, MalformedInstallInfo] m ()+rmToolVersion tool tver = do+ logInfo $ "Removing " <> prettyText tool <> " version " <> prettyText tver+ let target = _tvTarget tver+ ver' = _tvVersion tver++ unlessM (lift $ fmap isJust $ isInstalled tool tver) $ throwE (NotInstalled tool tver)++ mset <- liftE $ getSetVersion' tool target++ vspec <- lift $ runE $ getSymlinkSpec' tool tver++ case vspec of+ VLeft _ -> do+ -- legacy, no spec files+ pure ()+ VRight specRaw -> do+ let spec = substituteSpec <$> specRaw+ lift $ logInfo "Removing executable symlinks"++ toolDir <- fromGHCupPath <$> lift (toolInstallDestination tool tver)++ case mset of+ Just (vr@VersionRev{..}, mSetFile) -> do+ lift $ logDebug $ "Set version: " <> prettyVerRev vr+ when (_vrVersion == ver') $ lift $ do+ unqualSyml <- getUnqualifiedSymlinks spec toolDir+ forM_ unqualSyml (rmLink . snd)+ forM_ mSetFile recycleFile+ rmGHCShareDir+ Nothing -> pure ()++ pvpSyml <- lift $ getPVPSymlinks spec toolDir+ forM_ pvpSyml (lift . rmLink . snd)+ pvpMajorSyml <- liftE $ getPVPMajorSymlinks specRaw ver' toolDir+ forM_ pvpMajorSyml (rmLink . snd)+ f <- recordedInstallationSpecFile tool tver+ hideError NoSuchThing $ recycleFile f++ dir' <- lift $ toolInstallDestination tool tver+ let dir = fromGHCupPath dir'++ lift (getInstalledFiles tool tver) >>= \case+ Just files -> do+ -- @.spec@ file was added after the DB record files, so it's possible+ -- we may need to delete symlinks manually.+ -- We need to do this before the actual removal. The legacy methods+ -- rely on inspecting the internal GHC directories.+ case vspec of+ -- nothing to do, was already removed earlier+ VRight _ -> pure ()+ -- handle legacy symlink removal+ VLeft _+ | tool == ghc -> do+ liftE $ rmMinorGHCSymlinks tver+ liftE $ rmMajorGHCSymlinks tver+ set <- lift $ ghcSet target+ when (Just tver == set) $ do+ liftE $ rmPlainGHC target+ lift rmGHCShareDir++ | tool == hls -> do+ liftE $ rmMinorHLSSymlinks ver'+ set <- lift hlsSet+ when (Just ver' == set) $ liftE rmPlainHLS+ -- cabal and stack don't need special handling+ | otherwise -> pure ()++ lift $ logInfo $ "Removing files safely from: " <> T.pack dir+ forM_ files (lift . hideError NoSuchThing . recycleFile . (\f -> dir </> dropDrive f))+ hideError NoSuchThing $ hideError UnsatisfiedConstraints $ removeEmptyDirsRecursive dir+ survivors <- liftIO $ hideErrorDef [doesNotExistErrorType] [] $ listDirectory dir+ f <- recordedInstallationFile tool tver+ lift $ logDebug $ T.pack (show f)+ lift $ recycleFile f+ when (not (null survivors)) $ throwE $ UninstallFailed dir survivors++ Nothing+ | tool == ghc -> legacyGHCRm+ | tool == cabal -> liftE $ rmCabalVer ver'+ | tool == hls -> liftE $ rmHLSVer ver'+ | tool == stack -> liftE $ rmStackVer ver'+ | tool == ghcup -> lift rmGhcup+ | otherwise -> fail "Could not find installed files... your DB seems corrupted"+ where+ rmGHCShareDir = do+ Dirs {..} <- getDirs+ hideError doesNotExistErrorType $ rmDirectoryLink (fromGHCupPath baseDir </> "share")+ legacyGHCRm = do+ liftE $ rmMinorGHCSymlinks tver+ liftE $ rmMajorGHCSymlinks tver+ set <- lift $ ghcSet (_tvTarget tver)+ when (Just tver == set) $ do+ liftE $ rmPlainGHC (_tvTarget tver)+ lift rmGHCShareDir+ ghcdir <- ghcupGHCDir tver+ let dir = fromGHCupPath ghcdir+ isDir <- liftIO $ doesDirectoryExist dir+ isSyml <- liftIO $ handleIO (\_ -> pure False) $ pathIsSymbolicLink dir+ when (isDir && not isSyml) $ do+ lift $ logInfo $ "Removing legacy directory recursively: " <> T.pack dir+ recyclePathForcibly ghcdir+++-- assuming the current scheme of having just 1 ghcup bin, no version info is required.+rmGhcup :: ( MonadReader env m+ , HasDirs env+ , MonadIO m+ , MonadCatch m+ , HasLog env+ , MonadMask m+ , MonadUnliftIO m+ )+ => m ()+rmGhcup = do+ Dirs { .. } <- getDirs+ let ghcupFilename = "ghcup" <> exeExt+ let ghcupFilepath = binDir </> ghcupFilename++ currentRunningExecPath <- liftIO getExecutablePath++ -- if paths do no exist, warn user, and continue to compare them, as is,+ -- which should eventually fail and result in a non-standard install warning++ p1 <- handleIO' doesNotExistErrorType+ (handlePathNotPresent currentRunningExecPath)+ (liftIO $ canonicalizePath currentRunningExecPath)++ p2 <- handleIO' doesNotExistErrorType+ (handlePathNotPresent ghcupFilepath)+ (liftIO $ canonicalizePath ghcupFilepath)++ let areEqualPaths = equalFilePath p1 p2++ unless areEqualPaths $ logWarn $ nonStandardInstallLocationMsg currentRunningExecPath++ if isWindows+ then do+ -- since it doesn't seem possible to delete a running exe on windows+ -- we move it to system temp dir, to be deleted at next reboot+ tmp <- liftIO $ getCanonicalTemporaryDirectory >>= \t -> createTempDirectory t "ghcup"+ hideError UnsupportedOperation $+ liftIO $ hideError NoSuchThing $+ moveFile ghcupFilepath (tmp </> "ghcup")+ else+ -- delete it.+ hideError doesNotExistErrorType $ rmFile ghcupFilepath++ where+ handlePathNotPresent fp _err = do+ logDebug $ "Error: The path does not exist, " <> T.pack fp+ pure fp++ nonStandardInstallLocationMsg path = T.pack $+ "current ghcup is invoked from a non-standard location: \n"+ <> path <>+ "\n you may have to uninstall it manually."
+ lib/GHCup/Command/Set.hs view
@@ -0,0 +1,191 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}++module GHCup.Command.Set where++import GHCup.Errors+import GHCup.Legacy.Cabal+import GHCup.Legacy.HLS+import GHCup.Legacy.Stack+import GHCup.Legacy.Utils (rmPlainGHC, binarySymLinkDestination, ghcInternalBinDir, ghcToolFiles)+import GHCup.Prelude+import GHCup.Query.DB+import GHCup.Query.GHCupDirs+import GHCup.Query.Symlink+import GHCup.System.Directory+import GHCup.Types+import GHCup.Types.Optics+import GHCup.Warnings++import Control.Applicative+import Control.Monad+#if !MIN_VERSION_base(4,13,0)+import Control.Monad.Fail ( MonadFail )+#endif+import Control.Monad.Reader+import Data.Maybe+import Data.Data (Proxy(..))+import Data.Variant.Excepts+import Data.Versions hiding ( patch )+import Prelude hiding ( abs )+import System.FilePath+import System.IO.Error++import qualified Data.Text as T+import qualified Data.Text.IO as T+import GHCup.Query.DB.HLS++setToolVersion ::+ ( MonadReader env m+ , HasDirs env+ , HasLog env+ , HasPlatformReq env+ , MonadIOish m+ )+ => Tool+ -> TargetVersion+ -> Excepts '[ParseError, NotInstalled] m TargetVersion+setToolVersion tool ver = setToolVersion' tool ver Nothing++setToolVersion' ::+ ( MonadReader env m+ , HasDirs env+ , HasLog env+ , HasPlatformReq env+ , MonadIOish m+ )+ => Tool+ -> TargetVersion+ -> Maybe FilePath -- ^ if @Nothing@, then we operate in ~/.ghcup/bin,+ -- otherwise in a tmp directory+ -> Excepts '[ParseError, NotInstalled] m TargetVersion+-- TODO: warn about hls compatibility+setToolVersion' tool tver mTmpDir = do+ hideExcept' @NoToolVersionSet Proxy $ when (isNothing mTmpDir) $ unsetTool tool (_tvTarget tver)++ let ver' = _tvVersion tver+ vspec <- lift $ runE $ getSymlinkSpec tool tver+ case vspec of+ -- legacy+ VLeft _+ | tool == ghc -> liftE legacySetGHC+ | tool == cabal -> liftE $ setCabal ver' mTmpDir+ | tool == stack -> liftE $ setStack ver' mTmpDir+ | tool == hls -> liftE $ setHLS ver' SetHLSOnly mTmpDir+ | otherwise -> throwE $ NotInstalled tool tver+ VRight spec -> do+ lift $ logDebug2 $ T.pack (show spec)+ toolDir <- lift $ fmap fromGHCupPath $ toolInstallDestination tool tver+ symls <- maybe+ (lift $ getUnqualifiedSymlinks spec toolDir)+ (\tmpDir -> lift $ getUnqualifiedSymlinks' tmpDir spec toolDir)+ mTmpDir+ shadows <- fmap catMaybes <$> forM symls $ \(target, bin) -> do+ lift $ createLink target bin+ liftIO (isShadowed bin) >>= \case+ Nothing -> pure Nothing+ Just pa -> pure $ Just (pa, bin)+ unless (null shadows) $ logWarn $ T.pack $ prettyHFError (ToolShadowed tool (_tvVersion tver) shadows)++ -- record in 'set' file+ when (isNothing mTmpDir) $ do+ setFile <- lift $ recordedSetVersionFile tool (_tvTarget tver)+ liftIO $ createDirRecursive' (takeDirectory setFile)+ liftIO $ T.writeFile setFile (prettyVer . _tvVersion $ tver)+ currentHLS <- liftE $ getSetVersion' hls Nothing+ currentGHC <- liftE $ getSetVersion' ghc Nothing+ supportedGHC <- lift $ maybe (pure []) (getHLSGHCs . _vrVersion . fst) currentHLS+ lift $ warnAboutHlsCompatibility (_vrVersion . fst <$> currentHLS) (_vrVersion . fst <$> currentGHC) supportedGHC++ pure tver+ where+ legacySetGHC = do+ binDir' <- maybe (binDir <$> lift getDirs) pure mTmpDir+ ghcdir <- lift $ ghcupGHCDir tver+ -- for ghc tools (ghc, ghci, haddock, ...)+ verfiles <- ghcToolFiles tver+ forM_ verfiles $ \file -> do+ -- create symlink+ internalBinDir <- ghcInternalBinDir tver+ let fullF = binDir' </> file <> exeExt+ destL <- binarySymLinkDestination binDir' (internalBinDir </> file <> exeExt)+ lift $ createLink destL fullF++ when ((isNothing . _tvTarget $ tver) && isNothing mTmpDir)+ $ lift $ symlinkShareDir (fromGHCupPath ghcdir) (T.unpack $ prettyVer (_tvVersion tver))++ symlinkShareDir :: ( MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadIOish m+ )+ => FilePath+ -> String+ -> m ()+ symlinkShareDir ghcdir ver' = do+ Dirs {..} <- getDirs+ let destdir = fromGHCupPath baseDir+ let sharedir = "share"+ let fullsharedir = ghcdir </> sharedir+ logDebug $ "Checking for sharedir existence: " <> T.pack fullsharedir+ whenM (liftIO $ doesDirectoryExist fullsharedir) $ do+ let fullF = destdir </> sharedir+ let targetF = "." </> "ghc" </> ver' </> sharedir+ hideError doesNotExistErrorType $ rmDirectoryLink fullF++ if isWindows+ then liftIO+ -- On windows we need to be more permissive+ -- in case symlinks can't be created, be just+ -- give up here. This symlink isn't strictly necessary.+ $ hideError permissionErrorType+ $ hideError illegalOperationErrorType+ $ createDirectoryLink targetF fullF+ else liftIO+ $ createDirectoryLink targetF fullF++++unsetTool ::+ ( MonadReader env m+ , HasDirs env+ , HasLog env+ , HasPlatformReq env+ , MonadIOish m+ )+ => Tool+ -> Maybe T.Text+ -> Excepts '[ParseError, NotInstalled, NoToolVersionSet] m ()+unsetTool tool target = do+ unlessM (fmap isJust $ liftE $ getSetVersion' tool target) $ throwE (NoToolVersionSet tool target)++ mset <- liftE $ getSetVersion' tool target+ setFile <- lift $ recordedSetVersionFile tool target+ case mset of+ Just (VersionRev{..}, mSetFile) -> do+ let tver = TargetVersion target _vrVersion+ vspec <- lift $ runE $ getSymlinkSpec tool tver+ case vspec of+ VLeft _+ | tool == ghc -> liftE $ rmPlainGHC target+ | tool == cabal -> lift unsetCabal+ | tool == stack -> lift unsetStack+ | tool == hls -> lift unsetHLS+ | otherwise -> pure ()+ VRight spec -> do+ toolDir <- lift $ fmap fromGHCupPath $ toolInstallDestination tool tver+ unqualSyml <- lift $ getUnqualifiedSymlinks spec toolDir+ forM_ unqualSyml (rmLink . snd)+ forM_ mSetFile recycleFile+ Nothing -> pure ()+ when (tool == ghc) $ do+ Dirs {..} <- getDirs+ hideError doesNotExistErrorType $ rmDirectoryLink (fromGHCupPath baseDir </> "share")++ logDebug2 $ "rm -f " <> T.pack setFile+ hideError doesNotExistErrorType $ recycleFile setFile+
+ lib/GHCup/Command/Test/GHC.hs view
@@ -0,0 +1,199 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}++{-|+Module : GHCup.Command.Test.GHC+Description : GHCup installation functions for GHC+Copyright : (c) Julian Ospald, 2020+License : LGPL-3.0+Maintainer : hasufell@hasufell.de+Stability : experimental+Portability : portable+-}+module GHCup.Command.Test.GHC where+++import GHCup.Builder+import GHCup.Command.Install.LowLevel+import GHCup.Download+import GHCup.Errors+import GHCup.Prelude+import GHCup.Query.GHCupDirs+import GHCup.Types+import GHCup.Types.Optics+import GHCup.Unpack++import Control.Applicative+import Control.Exception.Safe+import Control.Monad+#if !MIN_VERSION_base(4,13,0)+import Control.Monad.Fail ( MonadFail )+#endif+import Control.Monad.Reader+import Control.Monad.Trans.Resource hiding ( throwM )+import Data.Maybe+import Data.Variant.Excepts+import Data.Versions hiding ( patch )+import Optics+import Prelude hiding ( abs, writeFile )+import System.Environment+import System.FilePath+import URI.ByteString++import qualified Data.Map.Strict as Map+import qualified Data.Text as T+++data GHCVer+ = SourceDist Version+ | GitDist GitBranch+ | RemoteDist URI+ deriving (Eq, Show)++++ --------------------+ --[ Tool testing ]--+ --------------------++++testGHCVer :: ( MonadFail m+ , MonadMask m+ , MonadCatch m+ , MonadReader env m+ , HasDirs env+ , HasSettings env+ , HasPlatformReq env+ , HasGHCupInfo env+ , HasLog env+ , MonadResource m+ , MonadIO m+ , MonadUnliftIO m+ )+ => TargetVersionReq+ -> [T.Text]+ -> Excepts+ '[ DigestError+ , ContentLengthError+ , GPGError+ , DownloadFailed+ , NoDownload+ , ArchiveResult+ , TarDirDoesNotExist+ , UnknownArchive+ , TestFailed+ , URIParseError+ ]+ m+ ()+testGHCVer tvr@TargetVersionReq{..} addMakeArgs = do+ GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo++ dlInfo <-+ preview (_GHCupDownloads % ix ghc % toolVersionsL % ix _tvqTargetVer % revisionSpecL % ixOrLast _tvqRev % _2 % viTestDL % _Just) dls+ ?? NoDownload tvr ghc Nothing++ liftE $ testGHCBindist dlInfo _tvqTargetVer addMakeArgs++++testGHCBindist :: ( MonadFail m+ , MonadMask m+ , MonadCatch m+ , MonadReader env m+ , HasDirs env+ , HasSettings env+ , HasPlatformReq env+ , HasGHCupInfo env+ , HasLog env+ , MonadResource m+ , MonadIO m+ , MonadUnliftIO m+ )+ => DownloadInfo+ -> TargetVersion+ -> [T.Text]+ -> Excepts+ '[ DigestError+ , ContentLengthError+ , GPGError+ , DownloadFailed+ , NoDownload+ , ArchiveResult+ , TarDirDoesNotExist+ , UnknownArchive+ , TestFailed+ , URIParseError+ ]+ m+ ()+testGHCBindist dlinfo ver addMakeArgs = do+ -- download (or use cached version)+ dl <- liftE $ downloadCached dlinfo Nothing++ liftE $ testPackedGHC dl (view dlSubdir dlinfo) ver addMakeArgs+++testPackedGHC :: ( MonadMask m+ , MonadCatch m+ , MonadReader env m+ , HasDirs env+ , HasPlatformReq env+ , HasSettings env+ , MonadThrow m+ , HasLog env+ , MonadIO m+ , MonadUnliftIO m+ , MonadFail m+ , MonadResource m+ )+ => FilePath -- ^ Path to the packed GHC bindist+ -> Maybe TarDir -- ^ Subdir of the archive+ -> TargetVersion -- ^ The GHC version+ -> [T.Text] -- ^ additional make args+ -> Excepts+ '[ ArchiveResult, UnknownArchive, TarDirDoesNotExist, TestFailed ] m ()+testPackedGHC dl msubdir ver addMakeArgs = do+ -- unpack+ tmpUnpack <- lift mkGhcupTmpDir+ liftE $ cleanUpOnError tmpUnpack (unpackToDir (fromGHCupPath tmpUnpack) dl)++ -- the subdir of the archive where we do the work+ workdir <- maybe (pure tmpUnpack)+ (liftE . intoSubdir tmpUnpack)+ msubdir++ reThrowAll @_ @'[ArchiveResult, UnknownArchive, TarDirDoesNotExist, ProcessError]+ (TestFailed (fromGHCupPath workdir)) $ liftE $ runBuildAction tmpUnpack+ (testUnpackedGHC workdir ver addMakeArgs)++testUnpackedGHC :: ( MonadReader env m+ , HasDirs env+ , HasSettings env+ , HasLog env+ , MonadIOish m+ )+ => GHCupPath -- ^ Path to the unpacked GHC bindist (where the make file resides)+ -> TargetVersion -- ^ The GHC version+ -> [T.Text] -- ^ additional configure args for bindist+ -> Excepts '[ProcessError] m ()+testUnpackedGHC path tver addMakeArgs = do+ lift $ logInfo $ "Testing GHC version " <> tVerToText tver <> "!"+ ghcDir <- lift $ ghcupGHCDir tver+ let ghcBinDir = fromGHCupPath ghcDir </> "bin"+ env <- liftIO $ addToPath [ghcBinDir] False+ let pathVar = if isWindows then "Path" else "PATH"+ forM_ (Map.lookup pathVar . Map.fromList $ env) $ liftIO . setEnv pathVar++ liftE $ makeWithWrapper (fmap T.unpack addMakeArgs)+ (Just $ fromGHCupPath path)+ "ghc-test"+ (Just $ ("STAGE1_GHC", maybe "" (T.unpack . (<> "-")) (_tvTarget tver)+ <> "ghc-"+ <> T.unpack (prettyVer $ _tvVersion tver)) : env)+ pure ()
+ lib/GHCup/Command/Upgrade.hs view
@@ -0,0 +1,160 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}++{-|+Module : GHCup.Command.Upgrade+Description : GHCup upgrade+Copyright : (c) Julian Ospald, 2020+License : LGPL-3.0+Maintainer : hasufell@hasufell.de+Stability : experimental+Portability : portable+-}+module GHCup.Command.Upgrade where+++import GHCup.Download+import GHCup.Errors+import GHCup.Hardcoded.Version+import GHCup.Input.Parsers.URI+import GHCup.Prelude+import GHCup.Query.GHCupDirs+import GHCup.Query.Metadata+import GHCup.Types+import GHCup.Types.Optics++import Control.Applicative+import Control.Exception.Safe+import Control.Monad+#if !MIN_VERSION_base(4,13,0)+import Control.Monad.Fail ( MonadFail )+#endif+import Control.Monad.Reader+import Control.Monad.Trans.Resource hiding ( throwM )+import Data.Maybe+import Data.Variant.Excepts+import Data.Versions hiding ( patch )+import GHC.IO.Exception+import Prelude hiding ( abs, writeFile )+import System.FilePath++import qualified Data.Text as T++++++ -------------------------+ --[ GHCup upgrade etc ]--+ -------------------------++-- | Upgrade ghcup and place it in @~\/.ghcup\/bin\/ghcup@,+-- if no path is provided.+upgradeGHCup :: ( MonadMask m+ , MonadReader env m+ , HasDirs env+ , HasPlatformReq env+ , HasGHCupInfo env+ , HasSettings env+ , MonadCatch m+ , HasLog env+ , MonadThrow m+ , MonadFail m+ , MonadResource m+ , MonadIO m+ , MonadUnliftIO m+ )+ => Maybe FilePath -- ^ full file destination to write ghcup into+ -> Bool -- ^ whether to force update regardless+ -- of currently installed version+ -> Bool -- ^ whether to throw an error if ghcup is shadowed+ -> Excepts+ '[ CopyError+ , DigestError+ , ContentLengthError+ , GPGError+ , GPGError+ , DownloadFailed+ , NoDownload+ , NoUpdate+ , ToolShadowed+ , URIParseError+ ]+ m+ Version+upgradeGHCup mtarget force' fatal = do+ GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo+ let latestVer = _tvVersion (fromJust (getLatest dls ghcup))+ upgradeGHCup' mtarget force' fatal latestVer+++-- | Upgrade ghcup and place it in @~\/.ghcup\/bin\/ghcup@,+-- if no path is provided.+upgradeGHCup' :: ( MonadMask m+ , MonadReader env m+ , HasDirs env+ , HasPlatformReq env+ , HasGHCupInfo env+ , HasSettings env+ , MonadCatch m+ , HasLog env+ , MonadThrow m+ , MonadFail m+ , MonadResource m+ , MonadIO m+ , MonadUnliftIO m+ )+ => Maybe FilePath -- ^ full file destination to write ghcup into+ -> Bool -- ^ whether to force update regardless+ -- of currently installed version+ -> Bool -- ^ whether to throw an error if ghcup is shadowed+ -> Version+ -> Excepts+ '[ CopyError+ , DigestError+ , ContentLengthError+ , GPGError+ , GPGError+ , DownloadFailed+ , NoDownload+ , NoUpdate+ , ToolShadowed+ , URIParseError+ ]+ m+ Version+upgradeGHCup' mtarget force' fatal latestVer = do+ Dirs {..} <- lift getDirs+ lift $ logInfo "Upgrading GHCup..."+ (Just ghcupPVPVer) <- pure $ pvpToVersion ghcUpVer ""+ when (not force' && (latestVer <= ghcupPVPVer)) $ throwE NoUpdate+ (_, dli) <- liftE $ getDownloadInfoE ghcup (VersionReq latestVer Nothing)+ tmp <- fromGHCupPath <$> lift withGHCupTmpDir+ let fn = "ghcup" <> exeExt+ dlu <- lE $ parseURI' (_dlUri dli)+ p <- liftE $ download dlu Nothing (Just (_dlHash dli)) (_dlCSize dli) tmp (Just fn) False+ let destDir = takeDirectory destFile+ destFile = fromMaybe (binDir </> fn) mtarget+ lift $ logDebug $ "mkdir -p " <> T.pack destDir+ liftIO $ createDirRecursive' destDir+ lift $ logDebug $ "rm -f " <> T.pack destFile+ lift $ hideError NoSuchThing $ recycleFile destFile+ lift $ logDebug $ "cp " <> T.pack p <> " " <> T.pack destFile+ copyFileE p destFile False+ lift $ chmod_755 destFile++ liftIO (isInPath destFile) >>= \b -> unless b $+ lift $ logWarn $ T.pack (takeFileName destFile) <> " is not in PATH! You have to add it in order to use ghcup."+ liftIO (isShadowed destFile) >>= \case+ Nothing -> pure ()+ Just pa+ | fatal -> throwE (ToolShadowed ghcup latestVer [(pa, destFile)])+ | otherwise ->+ lift $ logWarn $ T.pack $ prettyHFError (ToolShadowed ghcup latestVer [(pa, destFile)])++ pure latestVer+
+ lib/GHCup/Command/Whereis.hs view
@@ -0,0 +1,107 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}++{-|+Module : GHCup.Command.Whereis+Description : GHCup whereis+Copyright : (c) Julian Ospald, 2020+License : LGPL-3.0+Maintainer : hasufell@hasufell.de+Stability : experimental+Portability : portable+-}+module GHCup.Command.Whereis where+++import GHCup.Errors+import GHCup.Legacy.Utils+import GHCup.Prelude+import GHCup.Query.DB+import GHCup.Query.GHCupDirs+import GHCup.System.Directory+import GHCup.Types+import GHCup.Types.Optics++import Control.Applicative+import Control.Monad+#if !MIN_VERSION_base(4,13,0)+import Control.Monad.Fail ( MonadFail )+#endif+import Control.Monad.Reader+import Data.Maybe+import Data.Variant.Excepts+import Data.Versions hiding ( patch )+import Prelude hiding ( abs, writeFile )+import System.Environment+import System.FilePath++import qualified Data.Text as T++++++ ---------------+ --[ Whereis ]--+ ---------------++++-- | Reports the binary location of a given tool:+--+-- * for GHC, this reports: @~\/.ghcup\/ghc\/\<ver\>\/bin\/ghc@+-- * for cabal, this reports @~\/.ghcup\/bin\/cabal-\<ver\>@+-- * for hls, this reports @~\/.ghcup\/bin\/haskell-language-server-wrapper-\<ver\>@+-- * for stack, this reports @~\/.ghcup\/bin\/stack-\<ver\>@+-- * for ghcup, this reports the location of the currently running executable+whereIsTool :: ( MonadReader env m+ , HasDirs env+ , HasLog env+ , HasPlatformReq env+ , MonadIOish m+ )+ => Tool+ -> TargetVersion+ -> Excepts '[NotInstalled, ParseError, NoInstallInfo] m FilePath+whereIsTool tool ver@TargetVersion {..} = do+ dirs <- lift getDirs+ sSpec <- lift $ getSymlinkSpecPortable tool ver+ toolDir <- fromGHCupPath <$> lift (toolInstallDestination tool ver)++ case sSpec of+ (SymlinkSpec{..}:_) ->+ pure $ toolDir </> _slTarget+ _ ->+ case tool of+ Tool "ghc" -> do+ whenM (lift $ fmap not $ ghcInstalled ver)+ $ throwE (NotInstalled ghc ver)+ bdir <- fromGHCupPath <$> lift (ghcupGHCDir ver)+ pure (bdir </> "bin" </> ghcBinaryName ver)+ Tool "cabal" -> do+ whenM (lift $ fmap not $ cabalInstalled _tvVersion)+ $ throwE (NotInstalled cabal (TargetVersion Nothing _tvVersion))+ pure (binDir dirs </> "cabal-" <> T.unpack (prettyVer _tvVersion) <> exeExt)+ Tool "hls" -> do+ whenM (lift $ fmap not $ hlsInstalled _tvVersion)+ $ throwE (NotInstalled hls (TargetVersion Nothing _tvVersion))+ ifM (lift $ isLegacyHLS _tvVersion)+ (pure (binDir dirs </> "haskell-language-server-wrapper-" <> T.unpack (prettyVer _tvVersion) <> exeExt))+ $ do+ bdir <- fromGHCupPath <$> lift (ghcupHLSDir _tvVersion)+ pure (bdir </> "bin" </> "haskell-language-server-wrapper" <> exeExt)++ Tool "stack" -> do+ whenM (lift $ fmap not $ stackInstalled _tvVersion)+ $ throwE (NotInstalled stack (TargetVersion Nothing _tvVersion))+ pure (binDir dirs </> "stack-" <> T.unpack (prettyVer _tvVersion) <> exeExt)+ Tool "ghcup" -> do+ currentRunningExecPath <- liftIO getExecutablePath+ liftIO $ canonicalizePath currentRunningExecPath+ -- TODO+ _ -> pure ""+
+ lib/GHCup/Compat/Pager.hs view
@@ -0,0 +1,69 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}++module GHCup.Compat.Pager where++import GHCup.Compat.Terminal+import GHCup.System.Directory++import Control.Exception ( IOException, try )+import Control.Monad ( forM_, (<=<) )+import Data.Foldable ( asum )+import Data.Text ( Text )+import System.Environment+import System.Exit+import System.IO+import System.Process++import qualified Data.Text as T+import qualified Data.Text.IO as T+++getPager :: IO (Maybe FilePath)+getPager = do+ lookupEnv "GHCUP_PAGER" >>= \case+ Just r -> pure $ Just r+ Nothing -> lookupEnv "PAGER" >>= \case+ Just r' -> pure $ Just r'+ Nothing ->+ let pagers = ["most", "more", "less"]+ in fmap (either (const Nothing) Just)+ . try @IOException+ . asum+ . fmap (maybe (fail "could not find") pure <=< findExecutable)+ $ pagers++-- 'more' reads from STDERR, and requires std_err to be 'Inherit'+sendToPager :: FilePath -> [Text] -> IO (Either IOException ())+sendToPager pager text = try @IOException+ $ withCreateProcess (shell pager) { std_in = CreatePipe+ , std_err = Inherit+ , std_out = Inherit+ , delegate_ctlc = True+ }+ $ \mStdinH _ _ ph -> case mStdinH of+ Just stdinH -> do+ forM_ text $ T.hPutStrLn stdinH+ hClose stdinH+ exitCode <- waitForProcess ph+ case exitCode of+ ExitFailure i -> fail ("Pager exited with exit code " <> show i)+ _ -> pure ()+ Nothing -> fail "well, I got nothing!"+++sendToPager' :: Maybe FilePath -> [Text] -> IO ()+sendToPager' (Just pager) text = do+ fits <- fitsInTerminal text+ case fits of+ Just True -> do+ T.putStr $ T.unlines text+ _ -> sendToPager pager text >>= \case+ Right _ -> pure ()+ Left _ -> do+ T.putStrLn $ T.unlines text+sendToPager' _ text =+ forM_ text T.putStrLn+
+ lib/GHCup/Compat/Terminal.hs view
@@ -0,0 +1,116 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}++module GHCup.Compat.Terminal where++import Control.Applicative+import Data.Char+import Data.Void++import qualified Data.Text as T+import qualified System.Console.Terminal.Size as TP+import qualified Text.Megaparsec as MP+import qualified Text.Megaparsec.Char as MPC+++-- | Checks whether the given text lines fit in the terminal window.+-- Returns 'Nothing' if the terminal size could not be determined (e.g. bc we're+-- part of a pipe).+fitsInTerminal :: [T.Text] -> IO (Maybe Bool)+fitsInTerminal text = fmap (\(TP.Window h _) -> length text <= h - 2) <$> TP.size++-- | Like 'fitsInTerminal', but takes a single text blob.+fitsInTerminal' :: T.Text -> IO (Maybe Bool)+fitsInTerminal' = fitsInTerminal . T.lines++padTo :: String -> Int -> String+padTo str x =+ let lstr = strWidth str+ add' = x - lstr+ in if add' < 0 then str else str ++ replicate add' ' '+++-- | Calculate the render width of a string, considering+-- wide characters (counted as double width), ANSI escape codes+-- (not counted), and line breaks (in a multi-line string, the longest+-- line determines the width).+strWidth :: String -> Int+strWidth =+ maximum+ . (0 :)+ . map (foldr (\a b -> charWidth a + b) 0)+ . lines+ . stripAnsi++-- | Strip ANSI escape sequences from a string.+--+-- >>> stripAnsi "\ESC[31m-1\ESC[m"+-- "-1"+stripAnsi :: String -> String+stripAnsi s' =+ case+ MP.parseMaybe (many $ "" <$ MP.try ansi <|> pure <$> MP.anySingle) s'+ of+ Nothing -> error "Bad ansi escape" -- PARTIAL: should not happen+ Just xs -> concat xs+ where+ -- This parses lots of invalid ANSI escape codes, but that should be fine+ ansi =+ MPC.string "\ESC[" *> digitSemicolons *> suffix MP.<?> "ansi" :: MP.Parsec+ Void+ String+ Char+ digitSemicolons = MP.takeWhileP Nothing (\c -> isDigit c || c == ';')+ suffix = MP.oneOf ['A', 'B', 'C', 'D', 'H', 'J', 'K', 'f', 'm', 's', 'u']++-- | Get the designated render width of a character: 0 for a combining+-- character, 1 for a regular character, 2 for a wide character.+-- (Wide characters are rendered as exactly double width in apps and+-- fonts that support it.) (From Pandoc.)+charWidth :: Char -> Int+charWidth c = case c of+ _ | c < '\x0300' -> 1+ | c >= '\x0300' && c <= '\x036F' -> 0+ | -- combining+ c >= '\x0370' && c <= '\x10FC' -> 1+ | c >= '\x1100' && c <= '\x115F' -> 2+ | c >= '\x1160' && c <= '\x11A2' -> 1+ | c >= '\x11A3' && c <= '\x11A7' -> 2+ | c >= '\x11A8' && c <= '\x11F9' -> 1+ | c >= '\x11FA' && c <= '\x11FF' -> 2+ | c >= '\x1200' && c <= '\x2328' -> 1+ | c >= '\x2329' && c <= '\x232A' -> 2+ | c >= '\x232B' && c <= '\x2E31' -> 1+ | c >= '\x2E80' && c <= '\x303E' -> 2+ | c == '\x303F' -> 1+ | c >= '\x3041' && c <= '\x3247' -> 2+ | c >= '\x3248' && c <= '\x324F' -> 1+ | -- ambiguous+ c >= '\x3250' && c <= '\x4DBF' -> 2+ | c >= '\x4DC0' && c <= '\x4DFF' -> 1+ | c >= '\x4E00' && c <= '\xA4C6' -> 2+ | c >= '\xA4D0' && c <= '\xA95F' -> 1+ | c >= '\xA960' && c <= '\xA97C' -> 2+ | c >= '\xA980' && c <= '\xABF9' -> 1+ | c >= '\xAC00' && c <= '\xD7FB' -> 2+ | c >= '\xD800' && c <= '\xDFFF' -> 1+ | c >= '\xE000' && c <= '\xF8FF' -> 1+ | -- ambiguous+ c >= '\xF900' && c <= '\xFAFF' -> 2+ | c >= '\xFB00' && c <= '\xFDFD' -> 1+ | c >= '\xFE00' && c <= '\xFE0F' -> 1+ | -- ambiguous+ c >= '\xFE10' && c <= '\xFE19' -> 2+ | c >= '\xFE20' && c <= '\xFE26' -> 1+ | c >= '\xFE30' && c <= '\xFE6B' -> 2+ | c >= '\xFE70' && c <= '\xFEFF' -> 1+ | c >= '\xFF01' && c <= '\xFF60' -> 2+ | c >= '\xFF61' && c <= '\x16A38' -> 1+ | c >= '\x1B000' && c <= '\x1B001' -> 2+ | c >= '\x1D000' && c <= '\x1F1FF' -> 1+ | c >= '\x1F200' && c <= '\x1F251' -> 2+ | c >= '\x1F300' && c <= '\x1F773' -> 1+ | c >= '\x20000' && c <= '\x3FFFD' -> 2+ | otherwise -> 1
lib/GHCup/Download.hs view
@@ -1,9 +1,9 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-} {-| Module : GHCup.Download@@ -25,70 +25,78 @@ module GHCup.Download where #if defined(INTERNAL_DOWNLOADER)-import GHCup.Download.IOStreams-import GHCup.Download.Utils+import GHCup.Download.IOStreams #endif-import GHCup.Errors-import GHCup.Types-import qualified GHCup.Types.Stack as Stack-import GHCup.Types.Optics-import GHCup.Types.JSON ( )-import GHCup.Utils.Dirs-import GHCup.Utils.URI-import GHCup.Platform-import GHCup.Prelude-import GHCup.Prelude.File-import GHCup.Prelude.Logger.Internal-import GHCup.Prelude.Process-import GHCup.Version+import GHCup.Errors+import GHCup.Types+import GHCup.Types.JSON+ ()+import GHCup.Types.Optics+#if defined(DHALL)+import GHCup.Types.Dhall+ ()+#endif+import GHCup.Hardcoded.URLs+import GHCup.Input.Parsers.URI+import GHCup.Prelude+import GHCup.Prelude.Process+import GHCup.Query.GHCupDirs+import GHCup.Query.System+import GHCup.System.Directory -import Control.Applicative-import Control.Exception.Safe-import Control.Monad+import qualified GHCup.Types.Stack as Stack++import Control.Applicative+import Control.Exception.Safe+import Control.Monad #if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )+import Control.Monad.Fail ( MonadFail ) #endif-import Control.Monad.Reader-import Control.Monad.Trans.Resource- hiding ( throwM )-import Data.Aeson-import Data.ByteString ( ByteString )+import Control.Monad.Reader+import Control.Monad.Trans.Resource hiding ( throwM )+import Data.Aeson++#if defined(DHALL)+import qualified Data.ByteString.Lazy as BL+import Data.Void ( Void )+import qualified Dhall+import qualified Dhall.Binary as Dhall+#endif++import Data.ByteString ( ByteString ) #if defined(INTERNAL_DOWNLOADER)-import Data.CaseInsensitive ( mk )+import Data.CaseInsensitive ( mk ) #endif-import Data.Maybe-import Data.Either-import Data.List-import Data.Time.Clock-import Data.Time.Clock.POSIX-import Data.Versions-import Data.Word8 hiding ( isSpace )-import Data.Variant.Excepts+import Data.Either+import Data.List+import Data.Maybe+import Data.Time.Clock+import Data.Time.Clock.POSIX+import Data.Variant.Excepts+import Data.Versions+import Data.Word8 hiding ( isSpace ) #if defined(INTERNAL_DOWNLOADER)-import Network.Http.Client hiding ( URL )+import Network.Http.Client hiding ( URL ) #endif-import Optics-import Prelude hiding ( abs- , readFile- , writeFile- )-import Safe-import System.Environment-import System.Exit-import System.FilePath-import System.IO.Error-import System.IO.Temp-import URI.ByteString hiding (parseURI)+import Optics+import Prelude hiding ( abs, readFile, writeFile )+import Safe+import System.Environment+import System.Exit+import System.FilePath+import System.IO.Error+import System.IO.Temp+import URI.ByteString hiding ( parseURI ) -import qualified Crypto.Hash.SHA256 as SHA256-import qualified Data.ByteString as B-import qualified Data.ByteString.Base16 as B16-import qualified Data.ByteString.Lazy as L-import qualified Data.Map.Strict as M-import qualified Data.Text as T-import qualified Data.Text.IO as T-import qualified Data.Text.Encoding as E-import qualified Data.Yaml.Aeson as Y+import qualified Crypto.Hash.SHA256 as SHA256+import qualified Data.ByteString as B+import qualified Data.ByteString.Base16 as B16+import qualified Data.ByteString.Lazy as L+import qualified Data.Map.Strict as M+import qualified Data.Text as T+import qualified Data.Text.Encoding as E+import qualified Data.Text.IO as T+import qualified Data.Yaml.Aeson as Y @@ -102,28 +110,45 @@ ------------------ +getDownloadsF ::+ ( FromJSONKey Tool+ , FromJSONKey Version+ , FromJSON VersionInfo+ , MonadReader env m+ , HasSettings env+ , HasDirs env+ , HasLog env+ , MonadIOish m+ )+ => PlatformRequest+ -> Excepts+ '[DigestError, ContentLengthError, GPGError, JSONError , DownloadFailed , FileDoesNotExistError, StackPlatformDetectError, UnsupportedMetadataFormat]+ m+ GHCupInfo+getDownloadsF pfreq = do+ Settings { urlSource } <- lift getSettings+ getDownloadsF' pfreq urlSource -- | Downloads the download information! But only if we need to ;P-getDownloadsF :: ( FromJSONKey Tool- , FromJSONKey Version- , FromJSON VersionInfo- , MonadReader env m- , HasSettings env- , HasDirs env- , MonadIO m- , MonadCatch m- , HasLog env- , MonadThrow m- , MonadFail m- , MonadMask m- )- => PlatformRequest- -> Excepts- '[DigestError, ContentLengthError, GPGError, JSONError , DownloadFailed , FileDoesNotExistError, StackPlatformDetectError]+--+-- This ignores the 'urlSource' from 'Settings'.+getDownloadsF' ::+ ( FromJSONKey Tool+ , FromJSONKey Version+ , FromJSON VersionInfo+ , MonadReader env m+ , HasSettings env+ , HasDirs env+ , HasLog env+ , MonadIOish m+ )+ => PlatformRequest+ -> [NewURLSource]+ -> Excepts+ '[DigestError, ContentLengthError, GPGError, JSONError , DownloadFailed , FileDoesNotExistError, StackPlatformDetectError, UnsupportedMetadataFormat] m GHCupInfo-getDownloadsF pfreq@(PlatformRequest arch plat _) = do- Settings { urlSource } <- lift getSettings+getDownloadsF' pfreq@(PlatformRequest arch plat _) urlSource = do infos <- liftE $ mapM dl' urlSource keys <- if any isRight infos then liftE . reThrowAll @_ @_ @'[StackPlatformDetectError] StackPlatformDetectError $ getStackPlatformKey pfreq@@ -149,7 +174,7 @@ ) => NewURLSource -> Excepts- '[DownloadFailed, GPGError, DigestError, ContentLengthError, JSONError, FileDoesNotExistError]+ '[DownloadFailed, GPGError, DigestError, ContentLengthError, JSONError, FileDoesNotExistError, UnsupportedMetadataFormat] m (Either GHCupInfo Stack.SetupInfo) dl' NewGHCupURL = fmap Left $ liftE (getBase ghcupURL) >>= liftE . decodeMetadata @GHCupInfo dl' NewStackSetupURL = fmap Right $ liftE (getBase stackSetupURL) >>= liftE . decodeMetadata @Stack.SetupInfo@@ -159,10 +184,21 @@ dl' (NewSetupInfo si) = pure (Right si) dl' (NewURI uri) = do base <- liftE $ getBase uri- catchE @JSONError (\(JSONDecodeError s) -> do- logDebug $ "Couldn't decode " <> T.pack base <> " as GHCupInfo, trying as SetupInfo: " <> T.pack s- Right <$> decodeMetadata @Stack.SetupInfo base)- $ fmap Left (decodeMetadata @GHCupInfo base >>= \gI -> warnOnMetadataUpdate uri gI >> pure gI)+ gr <- runE $ decodeMetadata @GHCupInfo base >>= \gI -> warnOnMetadataUpdate uri gI >> pure gI+ case gr of+ VRight r -> pure $ Left r+ VLeft (V (JSONDecodeError ge)) -> do+ logDebug $ "Couldn't decode " <> T.pack base <> " as GHCupInfo, trying as SetupInfo: " <> T.pack ge+ sr <- runE $ decodeMetadata @Stack.SetupInfo base+ case sr of+ VRight r -> pure $ Right r+ VLeft (V (JSONDecodeError se)) -> do+ throwE $ JSONDecodeError $ "\nError decoding as GHCupInfo:\n"+ <> (unlines . fmap (" " <>) . lines $ ge)+ <> "\nError decoding as StackSetupURL:\n"+ <> (unlines . fmap (" " <>) . lines $ se)+ VLeft err -> throwSomeE err+ VLeft err -> throwSomeE err fromStackSetupInfo :: MonadThrow m => Stack.SetupInfo@@ -171,19 +207,19 @@ fromStackSetupInfo (Stack.siGHCs -> ghcDli) keys = do let ghcVersionsPerKey = (`M.lookup` ghcDli) . T.pack <$> keys ghcVersions = fromMaybe mempty . listToMaybe . catMaybes $ ghcVersionsPerKey- (ghcupInfo' :: M.Map GHCTargetVersion DownloadInfo) <-- M.mapKeys mkTVer <$> M.traverseMaybeWithKey (\_ a -> pure $ fromStackDownloadInfo a) ghcVersions- let ghcupDownloads' = M.singleton GHC (M.map fromDownloadInfo ghcupInfo')+ (ghcupInfo' :: M.Map TargetVersion DownloadInfo) <-+ M.mapKeys mkTVer <$> M.traverseMaybeWithKey (\v a -> pure $ fromStackDownloadInfo v a) ghcVersions+ let ghcupDownloads' = GHCupDownloads $ M.singleton ghc (ToolInfo (ToolVersionSpec $ M.map fromDownloadInfo ghcupInfo') Nothing) pure (GHCupInfo mempty ghcupDownloads' Nothing) where- fromDownloadInfo :: DownloadInfo -> VersionInfo- fromDownloadInfo dli = let aspec = MapIgnoreUnknownKeys $ M.singleton arch (MapIgnoreUnknownKeys $ M.singleton plat (M.singleton Nothing dli))- in VersionInfo [] Nothing Nothing Nothing Nothing aspec Nothing Nothing Nothing Nothing+ fromDownloadInfo :: DownloadInfo -> VersionMetadata+ fromDownloadInfo dli = let aspec = ArchitectureSpec $ MapIgnoreUnknownKeys $ M.singleton arch (PlatformSpec $ MapIgnoreUnknownKeys $ M.singleton plat (PlatformVersionSpec $ M.singleton Nothing dli))+ in VersionMetadata [] Nothing Nothing Nothing Nothing Nothing Nothing (RevisionSpec . M.singleton (Rev 0) . VersionInfo Nothing Nothing $ aspec) - fromStackDownloadInfo :: MonadThrow m => Stack.GHCDownloadInfo -> m DownloadInfo- fromStackDownloadInfo (Stack.GHCDownloadInfo { gdiDownloadInfo = Stack.DownloadInfo{..} }) = do+ fromStackDownloadInfo :: MonadThrow m => Version -> Stack.GHCDownloadInfo -> m DownloadInfo+ fromStackDownloadInfo ver (Stack.GHCDownloadInfo { gdiDownloadInfo = Stack.DownloadInfo{..} }) = do sha256 <- maybe (throwM $ DigestMissing downloadInfoUrl) (pure . E.decodeUtf8) downloadInfoSha256- pure $ DownloadInfo downloadInfoUrl (Just $ RegexDir "ghc-.*") sha256 Nothing Nothing Nothing+ pure $ DownloadInfo downloadInfoUrl (Just $ RegexDir "ghc-.*") sha256 Nothing Nothing Nothing (Just $ toInstallationInputSpec $ defaultGHCInstallSpec pfreq $ mkTVer ver) mergeGhcupInfo :: MonadFail m@@ -191,9 +227,13 @@ -> m GHCupInfo mergeGhcupInfo [] = fail "mergeGhcupInfo: internal error: need at least one GHCupInfo" mergeGhcupInfo xs@(GHCupInfo{}: _) =- let newDownloads = M.unionsWith (M.unionWith (\_ b2 -> b2)) (_ghcupDownloads <$> xs)+ let newDownloads =+ M.unionsWith+ (\(ToolInfo (ToolVersionSpec a) td) (ToolInfo (ToolVersionSpec a') td') ->+ ToolInfo (ToolVersionSpec (M.unionWith (\_ b2 -> b2) a a')) (td <|> td'))+ (unGHCupDownloads . _ghcupDownloads <$> xs) newToolReqs = M.unionsWith (M.unionWith (\_ b2 -> b2)) (_toolRequirements <$> xs)- in pure $ GHCupInfo newToolReqs newDownloads Nothing+ in pure $ GHCupInfo newToolReqs (GHCupDownloads newDownloads) Nothing @@ -242,8 +282,8 @@ warnCache :: (MonadReader env m, HasLog env, MonadMask m, MonadCatch m, MonadIO m) => FilePath -> Downloader -> m () warnCache s downloader' = do let tryDownloder = case downloader' of- Curl -> "Wget"- Wget -> "Curl"+ Curl -> "Wget"+ Wget -> "Curl" #if defined(INTERNAL_DOWNLOADER) Internal -> "Curl" #endif@@ -323,17 +363,17 @@ | scheme' uri == "file" , urlBase' uri /= urlBase' newUri = do confFile <- getConfigFilePath'- logWarn $ "New metadata version detected"+ logWarn $ "New available metadata version detected" <> "\n old URI: " <> (decUTF8Safe . serializeURIRef') uri <> "\n new URI: " <> (decUTF8Safe . serializeURIRef') newUri- <> "\nYou might need to update your " <> T.pack confFile+ <> "\nRun 'ghcup upgrade' first. If the waring persists, then you might need to update your " <> T.pack confFile | scheme' uri /= "file" , uri /= newUri = do confFile <- getConfigFilePath'- logWarn $ "New metadata version detected"+ logWarn $ "New available metadata version detected" <> "\n old URI: " <> (decUTF8Safe . serializeURIRef') uri <> "\n new URI: " <> (decUTF8Safe . serializeURIRef') newUri- <> "\nYou might need to update your " <> T.pack confFile+ <> "\nRun 'ghcup upgrade' first. If the waring persists, then you might need to update your " <> T.pack confFile where scheme' = view (uriSchemeL' % schemeBSL') urlBase' = T.unpack . decUTF8Safe . urlBaseName . view pathL'@@ -350,19 +390,55 @@ , HasLog env , MonadMask m , FromJSON j+#if defined(DHALL)+ , Dhall.FromDhall j+#endif ) => FilePath- -> Excepts '[JSONError, FileDoesNotExistError] m j-decodeMetadata actualYaml = do- lift $ logDebug $ "Decoding yaml at: " <> T.pack actualYaml+ -> Excepts '[JSONError, FileDoesNotExistError, UnsupportedMetadataFormat] m j+decodeMetadata metadata+ | takeExtension metadata `elem` [".yaml", ".yml"]= do+ lift $ logDebug $ "Decoding yaml at: " <> T.pack metadata+ liftE $ yamlMeta metadata+ | takeExtension metadata == ".json" = do+ lift $ logDebug $ "Decoding json at: " <> T.pack metadata+ liftE $ jsonMeta metadata+#if defined(DHALL)+ | takeExtension metadata == ".dhall" = do+ lift $ logDebug $ "Decoding dhall at: " <> T.pack metadata+ liftE $ dhallMeta metadata+ | takeExtension metadata `elem` [".dhallb", ".dhall-binary"] = do+ lift $ logDebug $ "Decoding dhall binary at: " <> T.pack metadata+ liftE $ dhallbMeta metadata+#endif+ | otherwise = throwE $ UnsupportedMetadataFormat (takeExtension metadata) - liftE- . onE_ (onError actualYaml)- . lEM' @_ @_ @'[JSONError] (\(displayException -> e) -> JSONDecodeError $ unlines [e, "Consider removing " <> actualYaml <> " manually."])- . liftIO- . Y.decodeFileEither- $ actualYaml where+#if defined(DHALL)+ dhallbMeta f =+ onE_ (onError f)+ . handleIO (\(displayException -> e) -> throwE @_ @'[JSONError] $ JSONDecodeError $ unlines [e, "Consider removing " <> f <> " manually."])+ . liftIO+ $ BL.readFile f >>= either (fail . show) pure . Dhall.decodeExpression @Void @Void >>= Dhall.rawInput Dhall.auto+ dhallMeta f =+ onE_ (onError f)+ . handleIO (\(displayException -> e) -> throwE @_ @'[JSONError] $ JSONDecodeError $ unlines [e, "Consider removing " <> f <> " manually."])+ . liftIO+ . Dhall.inputFile Dhall.auto+ $ f+#endif+ jsonMeta f =+ onE_ (onError f)+ . lEM' @_ @_ @'[JSONError] (\e -> JSONDecodeError $ unlines [e, "Consider removing " <> f <> " manually."])+ . liftIO+ . eitherDecodeFileStrict+ $ f+ yamlMeta f =+ onE_ (onError f)+ . lEM' @_ @_ @'[JSONError] (\(displayException -> e) -> JSONDecodeError $ unlines [e, "Consider removing " <> f <> " manually."])+ . liftIO+ . Y.decodeFileEither+ $ f -- On error, remove the etags file and set access time to 0. This should ensure the next invocation -- may re-download and succeed. onError :: (MonadReader env m, HasLog env, MonadMask m, MonadCatch m, MonadIO m) => FilePath -> m ()@@ -373,62 +449,6 @@ liftIO $ hideError doesNotExistErrorType $ setAccessTime fp (posixSecondsToUTCTime (fromIntegral @Int 0)) -getDownloadInfo :: ( MonadReader env m- , HasPlatformReq env- , HasGHCupInfo env- )- => Tool- -> Version- -- ^ tool version- -> Excepts- '[NoDownload]- m- DownloadInfo-getDownloadInfo t v = getDownloadInfo' t (mkTVer v)--getDownloadInfo' :: ( MonadReader env m- , HasPlatformReq env- , HasGHCupInfo env- )- => Tool- -> GHCTargetVersion- -- ^ tool version- -> Excepts- '[NoDownload]- m- DownloadInfo-getDownloadInfo' t v = do- pfreq@(PlatformRequest a p mv) <- lift getPlatformReq- GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo-- let distro_preview f g =- let platformVersionSpec =- preview (ix t % ix v % viArch % to unMapIgnoreUnknownKeys % ix a % to unMapIgnoreUnknownKeys % ix (f p)) dls- mv' = g mv- in fmap snd- . find- (\(mverRange, _) -> maybe- (isNothing mv')- (\range -> maybe False (`versionRange` range) mv')- mverRange- )- . M.toList- =<< platformVersionSpec- with_distro = distro_preview id id- without_distro_ver = distro_preview id (const Nothing)- without_distro = distro_preview (set _Linux UnknownLinux) (const Nothing)-- maybe- (throwE $ NoDownload v t (Just pfreq))- pure- (case p of- -- non-musl won't work on alpine- Linux Alpine -> with_distro <|> without_distro_ver- _ -> with_distro <|> without_distro_ver <|> without_distro- )--- -- | Tries to download from the given http or https url -- and saves the result in continuous memory into a file. -- If the filename is not provided, then we:@@ -481,9 +501,9 @@ (\e' -> do lift $ hideError doesNotExistErrorType $ recycleFile (tmpFile baseDestFile) case e' of- V e@GPGError {} -> throwE e+ V e@GPGError {} -> throwE e V e@DigestError {} -> throwE e- _ -> throwE (DownloadFailed e')+ _ -> throwE (DownloadFailed e') ) $ do Settings{ downloader, noNetwork, gpgSetting } <- lift getSettings when noNetwork $ throwE (DownloadFailed (V NoNetwork :: V '[NoNetwork]))@@ -744,12 +764,13 @@ -> Excepts '[URIParseError, DigestError, ContentLengthError, DownloadFailed, GPGError] m FilePath downloadCached dli mfn = do Settings{ cache } <- lift getSettings- case cache of- True -> downloadCached' dli mfn Nothing- False -> do- dlu <- lE $ parseURI' (_dlUri dli)- tmp <- lift withGHCupTmpDir- liftE $ download dlu Nothing (Just (_dlHash dli)) (_dlCSize dli) (fromGHCupPath tmp) outputFileName False+ dlu <- lE $ parseURI' (_dlUri dli)+ let scheme = view (uriSchemeL' % schemeBSL') dlu+ if cache && scheme /= "file"+ then downloadCached' dli mfn Nothing+ else do+ tmp <- lift withGHCupTmpDir+ liftE $ download dlu Nothing (Just (_dlHash dli)) (_dlCSize dli) (fromGHCupPath tmp) outputFileName False where outputFileName = mfn <|> _dlOutput dli @@ -808,8 +829,7 @@ when verify $ do let p' = takeFileName file lift $ logInfo $ "verifying digest of: " <> T.pack p'- c <- liftIO $ L.readFile file- cDigest <- throwEither . E.decodeUtf8' . B16.encode . SHA256.hashlazy $ c+ cDigest <- liftIO $ getFileDigest file when ((cDigest /= eDigest) && verify) $ throwE (DigestError file cDigest eDigest) checkCSize :: ( MonadReader env m@@ -891,3 +911,9 @@ Just (DownloadMirror auth Nothing) -> uri { uriAuthority = Just auth } applyMirrors _ uri = uri++getFileDigest :: FilePath -> IO T.Text+getFileDigest file = do+ c <- liftIO $ L.readFile file+ throwEither . E.decodeUtf8' . B16.encode . SHA256.hashlazy $ c+
lib/GHCup/Download/IOStreams.hs view
@@ -11,7 +11,7 @@ import GHCup.Errors import GHCup.Types.JSON ( ) import GHCup.Prelude-import GHCup.Utils.URI+import GHCup.Input.Parsers.URI import Control.Applicative import Control.Exception.Safe@@ -171,8 +171,8 @@ withConnection' https host port = bracket acquire closeConnection where- acquire = case https of- True -> do+ acquire = if https+ then do ctx <- baselineContextSSL openConnectionSSL ctx host (fromIntegral $ fromMaybe 443 port)- False -> openConnection host (fromIntegral $ fromMaybe 80 port)+ else openConnection host (fromIntegral $ fromMaybe 80 port)
− lib/GHCup/Download/Utils.hs
@@ -1,60 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TypeFamilies #-}---module GHCup.Download.Utils where---import GHCup.Errors-import GHCup.Types.Optics-import GHCup.Types.JSON ( )-import GHCup.Prelude--import Control.Applicative-import Control.Monad-import Data.ByteString ( ByteString )-import Data.Maybe-import Data.Variant.Excepts-import Optics-import Prelude hiding ( abs- , readFile- , writeFile- )-import URI.ByteString--import qualified Data.Binary.Builder as B-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as L----- | Extracts from a URI type: (https?, host, path+query, port)-uriToQuadruple :: Monad m- => URI- -> Excepts- '[UnsupportedScheme]- m- (Bool, ByteString, ByteString, Maybe Int)-uriToQuadruple URI {..} = do- let scheme = view schemeBSL' uriScheme-- host <-- preview (_Just % authorityHostL' % hostBSL') uriAuthority- ?? UnsupportedScheme-- https <- if- | scheme == "https" -> pure True- | scheme == "http" -> pure False- | otherwise -> throwE UnsupportedScheme-- let queryBS =- BS.intercalate "&"- . fmap (\(x, y) -> encodeQuery x <> "=" <> encodeQuery y)- $ queryPairs uriQuery- port =- preview (_Just % authorityPortL' % _Just % portNumberL') uriAuthority- fullpath = if BS.null queryBS then uriPath else uriPath <> "?" <> queryBS- pure (https, host, fullpath, port)- where encodeQuery = L.toStrict . B.toLazyByteString . urlEncodeQuery-
lib/GHCup/Errors.hs view
@@ -1,12 +1,12 @@ {-# OPTIONS_GHC -Wno-orphans #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE RankNTypes #-} {-# LANGUAGE StandaloneDeriving #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeOperators #-} {-| Module : GHCup.Errors@@ -19,23 +19,25 @@ -} module GHCup.Errors ( module GHCup.Errors, URIParseError ) where -import GHCup.Types+import GHCup.Types -import Control.Exception.Safe-import Data.ByteString ( ByteString )-import Data.CaseInsensitive ( CI )-import Data.Text ( Text )-import Data.Versions-import Data.Variant-import System.FilePath-import Text.PrettyPrint hiding ( (<>) )-import Text.PrettyPrint.HughesPJClass hiding ( (<>) )-import URI.ByteString+import Control.Exception.Safe+import Data.ByteString ( ByteString )+import Data.CaseInsensitive ( CI )+import Data.Data ( Proxy (..) )+import Data.Text ( Text )+import Data.Time ( Day )+import Data.Variant+import Data.Versions+import System.FilePath+import Text.PrettyPrint hiding ( (<>) )+import Text.PrettyPrint.HughesPJClass hiding ( (<>) )+import URI.ByteString+import Data.Functor ((<&>)) -import qualified Data.Map.Strict as M-import qualified Data.Text as T-import Data.Data (Proxy(..))-import Data.Time (Day)+import qualified Data.Map.Strict as M+import qualified Data.Text as T+import Data.List (intercalate) @@ -85,6 +87,10 @@ , let proxy = Proxy :: Proxy ContentLengthError in format proxy , let proxy = Proxy :: Proxy DuplicateReleaseChannel in format proxy , let proxy = Proxy :: Proxy UnsupportedSetupCombo in format proxy+ , let proxy = Proxy :: Proxy UnsupportedMetadataFormat in format proxy+ , let proxy = Proxy :: Proxy NoInstallInfo in format proxy+ , let proxy = Proxy :: Proxy MalformedInstallInfo in format proxy+ , let proxy = Proxy :: Proxy IncompatibleConfig in format proxy , "" , "# high level errors (4000+)" , let proxy = Proxy :: Proxy DownloadFailed in format proxy@@ -190,11 +196,39 @@ --[ Low-level errors ]-- ------------------------ +data NoInstallInfo = NoInstallInfo Tool TargetVersion+ deriving (Show) +instance Pretty NoInstallInfo where+ pPrint (NoInstallInfo tool tver) = text "Could not find install info for" <+> pPrint tool <+> text "version" <+> pPrint tver +instance HFErrorProject NoInstallInfo where+ eBase _ = 380+ eDesc _ = "Could not find install info for tool"++data IncompatibleConfig = IncompatibleConfig String+ deriving (Show)++instance Pretty IncompatibleConfig where+ pPrint (IncompatibleConfig str) = text ("Incompatible config: " <> str)++instance HFErrorProject IncompatibleConfig where+ eBase _ = 390+ eDesc _ = "Could not find install info for tool"++data MalformedInstallInfo = MalformedInstallInfo String+ deriving (Show)++instance Pretty MalformedInstallInfo where+ pPrint (MalformedInstallInfo str') = text $ "The installation info from the metadata did not pass validation: " <> str'++instance HFErrorProject MalformedInstallInfo where+ eBase _ = 400+ eDesc _ = "The installation info did not pass validation!"+ -- | A compatible platform could not be found. data NoCompatiblePlatform = NoCompatiblePlatform String -- the platform we got- deriving Show+ deriving (Show) instance Pretty NoCompatiblePlatform where pPrint (NoCompatiblePlatform str') =@@ -205,14 +239,14 @@ eDesc _ = "No compatible platform could be found" -- | Unable to find a download for the requested version/distro.-data NoDownload = NoDownload GHCTargetVersion Tool (Maybe PlatformRequest)- deriving Show+data NoDownload = NoDownload TargetVersionReq Tool (Maybe PlatformRequest)+ deriving (Show) instance Pretty NoDownload where- pPrint (NoDownload tver@(GHCTargetVersion mtarget vv) tool mpfreq) =+ pPrint (NoDownload treq@(TargetVersionReq (TargetVersion mtarget vv) _) tool mpfreq) = let helperMsg | (Just target) <- mtarget- , target `elem` (T.pack . prettyShow <$> enumFromTo (minBound :: Tool) (maxBound :: Tool)) =+ , T.unpack (T.toLower target) `elem` ["ghc", "cabal", "hls", "stack"] = "\nPerhaps you meant: 'ghcup <command> " <> T.unpack target <> " "@@ -222,7 +256,7 @@ in text $ "Unable to find a download for " <> show tool <> " version "- <> "'" <> T.unpack (tVerToText tver) <> "'"+ <> "'" <> prettyShow treq <> "'" <> maybe "" (\pfreq -> " on detected platform " <> pfReqToString pfreq) mpfreq <> helperMsg @@ -231,8 +265,9 @@ eDesc _ = "Unable to find a download for the requested version/distro." -- | No update available or necessary.-data NoUpdate = NoUpdate- deriving Show+data NoUpdate+ = NoUpdate+ deriving (Show) instance Pretty NoUpdate where pPrint NoUpdate = text (eDesc (Proxy :: Proxy NoUpdate))@@ -243,7 +278,7 @@ -- | The Architecture is unknown and unsupported. data NoCompatibleArch = NoCompatibleArch String- deriving Show+ deriving (Show) instance Pretty NoCompatibleArch where pPrint (NoCompatibleArch arch) =@@ -254,8 +289,9 @@ eDesc _ = "The Architecture is unknown and unsupported" -- | Unable to figure out the distribution of the host.-data DistroNotFound = DistroNotFound- deriving Show+data DistroNotFound+ = DistroNotFound+ deriving (Show) instance Pretty DistroNotFound where pPrint DistroNotFound =@@ -267,7 +303,7 @@ -- | The archive format is unknown. We don't know how to extract it. data UnknownArchive = UnknownArchive FilePath- deriving Show+ deriving (Show) instance Pretty UnknownArchive where pPrint (UnknownArchive file) =@@ -278,8 +314,9 @@ eDesc _ = "The archive format is unknown. We don't know how to extract it." -- | The scheme is not supported (such as ftp).-data UnsupportedScheme = UnsupportedScheme- deriving Show+data UnsupportedScheme+ = UnsupportedScheme+ deriving (Show) instance Pretty UnsupportedScheme where pPrint UnsupportedScheme =@@ -291,7 +328,7 @@ -- | Unable to copy a file. data CopyError = CopyError String- deriving Show+ deriving (Show) instance Pretty CopyError where pPrint (CopyError reason) =@@ -302,13 +339,18 @@ eDesc _ = "Unable to copy a file." -- | Unable to merge file trees.-data MergeFileTreeError = MergeFileTreeError IOException FilePath FilePath- deriving Show+data MergeFileTreeError = MergeFileTreeErrorPreCondition IOException FilePath FilePath+ | MergeFileTreeError IOException FilePath Tool TargetVersion+ deriving (Show) instance Pretty MergeFileTreeError where- pPrint (MergeFileTreeError e from to) =- text "Failed to merge file tree from" <+> text from <+> text "to" <+> text to <+> text "\nexception was:" <+> text (displayException e)- <+> text "\n...you may need to delete" <+> text to <+> text "manually. Make sure it's gone."+ pPrint (MergeFileTreeErrorPreCondition e from to) =+ text "Unmet precondition for merging file tree from" <+> text from <+> text "to" <+> text to <+> text "\nexception was:" <+> text (displayException e)+ pPrint (MergeFileTreeError e to tool tver) =+ text ("Error during merging file tree (" <> displayException e <> ")")+ <+> text "\nYou may need to delete" <+> text to <+> text "manually."+ <+> text "\nAlso run: ghcup healthcheck " <+> pPrint tool <+> pPrint tver+ <+> text "\nBecause ghcup database might now be corrupted." instance HFErrorProject MergeFileTreeError where eBase _ = 80@@ -316,7 +358,7 @@ -- | Unable to find a tag of a tool. data TagNotFound = TagNotFound Tag Tool- deriving Show+ deriving (Show) instance Pretty TagNotFound where pPrint (TagNotFound tag tool) =@@ -328,7 +370,7 @@ -- | Unable to find a release day of a tool data DayNotFound = DayNotFound Day Tool (Maybe Day)- deriving Show+ deriving (Show) instance Pretty DayNotFound where pPrint (DayNotFound day tool Nothing) =@@ -344,7 +386,7 @@ -- | Unable to find the next version of a tool (the one after the currently -- set one). data NextVerNotFound = NextVerNotFound Tool- deriving Show+ deriving (Show) instance Pretty NextVerNotFound where pPrint (NextVerNotFound tool) =@@ -355,8 +397,8 @@ eDesc _ = "Unable to find the next version of a tool (the one after the currently set one)" -- | The tool (such as GHC) is already installed with that version.-data AlreadyInstalled = AlreadyInstalled Tool Version- deriving Show+data AlreadyInstalled = AlreadyInstalled Tool TargetVersion+ deriving (Show) instance Pretty AlreadyInstalled where pPrint (AlreadyInstalled tool ver') =@@ -368,8 +410,10 @@ eDesc _ = "The tool (such as GHC) is already installed with that version" -- | The Directory is supposed to be empty, but wasn't.-data DirNotEmpty = DirNotEmpty {path :: FilePath}- deriving Show+data DirNotEmpty = DirNotEmpty+ { path :: FilePath+ }+ deriving (Show) instance Pretty DirNotEmpty where pPrint (DirNotEmpty path) = do@@ -381,8 +425,8 @@ -- | The tool is not installed. Some operations rely on a tool -- to be installed (such as setting the current GHC version).-data NotInstalled = NotInstalled Tool GHCTargetVersion- deriving Show+data NotInstalled = NotInstalled Tool TargetVersion+ deriving (Show) instance Pretty NotInstalled where pPrint (NotInstalled tool ver) =@@ -393,7 +437,7 @@ eDesc _ = "The required tool is not installed" data UninstallFailed = UninstallFailed FilePath [FilePath]- deriving Show+ deriving (Show) instance Pretty UninstallFailed where pPrint (UninstallFailed dir files) =@@ -405,7 +449,7 @@ -- | An executable was expected to be in PATH, but was not found. data NotFoundInPATH = NotFoundInPATH FilePath- deriving Show+ deriving (Show) instance Exception NotFoundInPATH @@ -419,7 +463,7 @@ -- | JSON decoding failed. data JSONError = JSONDecodeError String- deriving Show+ deriving (Show) instance Pretty JSONError where pPrint (JSONDecodeError err) =@@ -432,8 +476,10 @@ -- | A file that is supposed to exist does not exist -- (e.g. when we use file scheme to "download" something). data FileDoesNotExistError = FileDoesNotExistError FilePath- deriving Show+ deriving (Show) +instance Exception FileDoesNotExistError+ instance Pretty FileDoesNotExistError where pPrint (FileDoesNotExistError file) = text $ "File " <> file <> " does not exist."@@ -446,7 +492,7 @@ -- (e.g. when we use isolated installs with the same path). -- (e.g. This is done to prevent any overwriting) data FileAlreadyExistsError = FileAlreadyExistsError FilePath- deriving Show+ deriving (Show) instance Pretty FileAlreadyExistsError where pPrint (FileAlreadyExistsError file) =@@ -457,11 +503,13 @@ eDesc _ = "A file already exists that wasn't expected to exist" data TarDirDoesNotExist = TarDirDoesNotExist TarDir- deriving Show+ deriving (Show) instance Pretty TarDirDoesNotExist where- pPrint (TarDirDoesNotExist dir) =+ pPrint (TarDirDoesNotExist (RealDir dir)) = text "Tar directory does not exist:" <+> pPrint dir+ pPrint (TarDirDoesNotExist (RegexDir rx)) =+ text "Couldn't find tar dir via regex:" <+> pPrint rx instance HFErrorProject TarDirDoesNotExist where eBase _ = 190@@ -469,7 +517,7 @@ -- | File digest verification failed. data DigestError = DigestError FilePath Text Text- deriving Show+ deriving (Show) instance Pretty DigestError where pPrint (DigestError fp currentDigest expectedDigest) =@@ -482,7 +530,7 @@ eDesc _ = "File digest verification failed" -- | File PGP verification failed.-data GPGError = forall xs . (ToVariantMaybe DownloadFailed xs, PopVariant DownloadFailed xs, Show (V xs), Pretty (V xs)) => GPGError (V xs)+data GPGError = forall xs. (ToVariantMaybe DownloadFailed xs, PopVariant DownloadFailed xs, Show (V xs), Pretty (V xs)) => GPGError (V xs) deriving instance Show GPGError @@ -495,7 +543,7 @@ -- | Unexpected HTTP status. data HTTPStatusError = HTTPStatusError Int (M.Map (CI ByteString) ByteString)- deriving Show+ deriving (Show) instance Pretty HTTPStatusError where pPrint (HTTPStatusError status _) =@@ -507,7 +555,7 @@ -- | Malformed headers. data MalformedHeaders = MalformedHeaders Text- deriving Show+ deriving (Show) instance Pretty MalformedHeaders where pPrint (MalformedHeaders h) =@@ -519,7 +567,7 @@ -- | Unexpected HTTP status. data HTTPNotModified = HTTPNotModified Text- deriving Show+ deriving (Show) instance Pretty HTTPNotModified where pPrint (HTTPNotModified etag) =@@ -530,8 +578,9 @@ eDesc _ = "Not modified HTTP status error (e.g. during downloads)." -- | The 'Location' header was expected during a 3xx redirect, but not found.-data NoLocationHeader = NoLocationHeader- deriving Show+data NoLocationHeader+ = NoLocationHeader+ deriving (Show) instance Pretty NoLocationHeader where pPrint NoLocationHeader =@@ -542,8 +591,9 @@ eDesc _ = "The 'Location' header was expected during a 3xx redirect, but not found." -- | Too many redirects.-data TooManyRedirs = TooManyRedirs- deriving Show+data TooManyRedirs+ = TooManyRedirs+ deriving (Show) instance Pretty TooManyRedirs where pPrint TooManyRedirs =@@ -554,8 +604,9 @@ eDesc _ = "Too many redirections." -- | A patch could not be applied.-data PatchFailed = PatchFailed- deriving Show+data PatchFailed+ = PatchFailed+ deriving (Show) instance Pretty PatchFailed where pPrint PatchFailed =@@ -566,8 +617,9 @@ eDesc _ = "A patch could not be applied." -- | The tool requirements could not be found.-data NoToolRequirements = NoToolRequirements- deriving Show+data NoToolRequirements+ = NoToolRequirements+ deriving (Show) instance Pretty NoToolRequirements where pPrint NoToolRequirements =@@ -578,7 +630,7 @@ eDesc _ = "The Tool requirements could not be found." data InvalidBuildConfig = InvalidBuildConfig Text- deriving Show+ deriving (Show) instance Pretty InvalidBuildConfig where pPrint (InvalidBuildConfig reason) =@@ -588,19 +640,22 @@ eBase _ = 290 eDesc _ = "The build config is invalid." -data NoToolVersionSet = NoToolVersionSet Tool- deriving Show+data NoToolVersionSet = NoToolVersionSet Tool (Maybe Text)+ deriving (Show) instance Pretty NoToolVersionSet where- pPrint (NoToolVersionSet tool) =- text "No version is set for tool" <+> pPrint tool <+> text "."+ pPrint (NoToolVersionSet tool Nothing) =+ text "No version is set for tool" <+> pPrint tool+ pPrint (NoToolVersionSet tool (Just target)) =+ text "No version is set for tool" <+> pPrint tool <+> text "cross target" <+> text (T.unpack target) instance HFErrorProject NoToolVersionSet where eBase _ = 300 eDesc _ = "No version is set for tool (but was expected)." -data NoNetwork = NoNetwork- deriving Show+data NoNetwork+ = NoNetwork+ deriving (Show) instance Pretty NoNetwork where pPrint NoNetwork =@@ -610,8 +665,9 @@ eBase _ = 310 eDesc _ = "A download was required or requested, but '--offline' was specified." -data HadrianNotFound = HadrianNotFound- deriving Show+data HadrianNotFound+ = HadrianNotFound+ deriving (Show) instance Pretty HadrianNotFound where pPrint HadrianNotFound =@@ -621,25 +677,21 @@ eBase _ = 320 eDesc _ = "Could not find Hadrian build files. Does this GHC version support Hadrian builds?" -data ToolShadowed = ToolShadowed- Tool- FilePath -- shadow binary- FilePath -- upgraded binary- Version -- upgraded version- deriving Show+data ToolShadowed = ToolShadowed Tool Version [(FilePath, FilePath)]+ -- upgraded version+ deriving (Show) instance Pretty ToolShadowed where- pPrint (ToolShadowed tool sh up _) =+ pPrint (ToolShadowed tool ver shadows) = text (prettyShow tool- <> " is shadowed by "- <> sh- <> ".\nThe upgrade will not be in effect, unless you remove "- <> sh- <> "\nor make sure "- <> takeDirectory up- <> " comes before "- <> takeDirectory sh- <> " in PATH."+ <> " version " <> T.unpack (prettyVer ver)+ <> " has shadowed binaries:\n"+ <> intercalate "\n" (shadows <&> \(sh, bin) ->+ " * " <> bin <> " shadowed by " <> sh+ )+ <> ".\nThe upgrade will not be in effect, unless you make sure that "+ <> (takeDirectory . snd . head $ shadows)+ <> " comes first in PATH." ) instance HFErrorProject ToolShadowed where@@ -648,7 +700,7 @@ -- | File content length verification failed. data ContentLengthError = ContentLengthError (Maybe FilePath) (Maybe Integer) Integer- deriving Show+ deriving (Show) instance Pretty ContentLengthError where pPrint (ContentLengthError Nothing Nothing expectedSize) =@@ -674,7 +726,7 @@ eDesc _ = "File content length verification failed" data DuplicateReleaseChannel = DuplicateReleaseChannel NewURLSource- deriving Show+ deriving (Show) instance HFErrorProject DuplicateReleaseChannel where eBase _ = 350@@ -687,7 +739,7 @@ <> "\nGiving up. You can use '--force' to remove and append the duplicate source (this may change order/semantics)." data UnsupportedSetupCombo = UnsupportedSetupCombo Architecture Platform- deriving Show+ deriving (Show) instance Pretty UnsupportedSetupCombo where pPrint (UnsupportedSetupCombo arch plat) =@@ -697,18 +749,29 @@ eBase _ = 360 eDesc _ = "Could not find a compatible setup combo" +data UnsupportedMetadataFormat = UnsupportedMetadataFormat FilePath+ deriving (Show)++instance Pretty UnsupportedMetadataFormat where+ pPrint (UnsupportedMetadataFormat ext) =+ text "Unsupported file extension for metadata:" <+> pPrint ext++instance HFErrorProject UnsupportedMetadataFormat where+ eBase _ = 370+ eDesc _ = "Unsupported file extension for metadata"+ ------------------------- --[ High-level errors ]-- ------------------------- -- | A download failed. The underlying error is encapsulated.-data DownloadFailed = forall xs . (HFErrorProject (V xs), ToVariantMaybe DownloadFailed xs, PopVariant DownloadFailed xs, Show (V xs), Pretty (V xs)) => DownloadFailed (V xs)+data DownloadFailed = forall xs. (HFErrorProject (V xs), ToVariantMaybe DownloadFailed xs, PopVariant DownloadFailed xs, Show (V xs), Pretty (V xs)) => DownloadFailed (V xs) instance Pretty DownloadFailed where pPrint (DownloadFailed reason) = case reason of VMaybe (_ :: DownloadFailed) -> pPrint reason- _ -> text "Download failed:" <+> pPrint reason+ _ -> text "Download failed:" <+> pPrint reason deriving instance Show DownloadFailed @@ -717,7 +780,7 @@ eNum (DownloadFailed xs) = 5000 + eNum xs eDesc _ = "A download failed." -data InstallSetError = forall xs1 xs2 . (Show (V xs1), Pretty (V xs1), HFErrorProject (V xs1), Show (V xs2), Pretty (V xs2), HFErrorProject (V xs2)) => InstallSetError (V xs1) (V xs2)+data InstallSetError = forall xs1 xs2. (Show (V xs1), Pretty (V xs1), HFErrorProject (V xs1), Show (V xs2), Pretty (V xs2), HFErrorProject (V xs2)) => InstallSetError (V xs1) (V xs2) instance Pretty InstallSetError where pPrint (InstallSetError reason1 reason2) =@@ -736,7 +799,7 @@ -- | A test failed.-data TestFailed = forall es . (ToVariantMaybe TestFailed es, PopVariant TestFailed es, Pretty (V es), Show (V es), HFErrorProject (V es)) => TestFailed FilePath (V es)+data TestFailed = forall es. (ToVariantMaybe TestFailed es, PopVariant TestFailed es, Pretty (V es), Show (V es), HFErrorProject (V es)) => TestFailed FilePath (V es) instance Pretty TestFailed where pPrint (TestFailed path reason) =@@ -752,7 +815,7 @@ eDesc _ = "The test failed." -- | A build failed.-data BuildFailed = forall es . (ToVariantMaybe BuildFailed es, PopVariant BuildFailed es, Pretty (V es), Show (V es), HFErrorProject (V es)) => BuildFailed FilePath (V es)+data BuildFailed = forall es. (ToVariantMaybe BuildFailed es, PopVariant BuildFailed es, Pretty (V es), Show (V es), HFErrorProject (V es)) => BuildFailed FilePath (V es) instance Pretty BuildFailed where pPrint (BuildFailed path reason) =@@ -769,7 +832,7 @@ -- | Setting the current GHC version failed.-data GHCupSetError = forall es . (ToVariantMaybe GHCupSetError es, PopVariant GHCupSetError es, Show (V es), Pretty (V es), HFErrorProject (V es)) => GHCupSetError (V es)+data GHCupSetError = forall es. (ToVariantMaybe GHCupSetError es, PopVariant GHCupSetError es, Show (V es), Pretty (V es), HFErrorProject (V es)) => GHCupSetError (V es) instance Pretty GHCupSetError where pPrint (GHCupSetError reason) =@@ -785,7 +848,7 @@ eDesc _ = "Setting the current version failed." -- | Executing stacks platform detection failed.-data StackPlatformDetectError = forall es . (ToVariantMaybe StackPlatformDetectError es, PopVariant StackPlatformDetectError es, Show (V es), Pretty (V es), HFErrorProject (V es)) => StackPlatformDetectError (V es)+data StackPlatformDetectError = forall es. (ToVariantMaybe StackPlatformDetectError es, PopVariant StackPlatformDetectError es, Show (V es), Pretty (V es), HFErrorProject (V es)) => StackPlatformDetectError (V es) instance Pretty StackPlatformDetectError where pPrint (StackPlatformDetectError reason) =@@ -808,7 +871,7 @@ -- | Parsing failed. data ParseError = ParseError String- deriving Show+ deriving (Show) instance Pretty ParseError where pPrint (ParseError reason) =@@ -822,7 +885,7 @@ data UnexpectedListLength = UnexpectedListLength String- deriving Show+ deriving (Show) instance Pretty UnexpectedListLength where pPrint (UnexpectedListLength reason) =@@ -835,7 +898,7 @@ eDesc _ = "A list had an unexpected length." data NoUrlBase = NoUrlBase Text- deriving Show+ deriving (Show) instance Pretty NoUrlBase where pPrint (NoUrlBase url) =@@ -848,7 +911,7 @@ eDesc _ = "URL does not have a base filename." data DigestMissing = DigestMissing Text- deriving Show+ deriving (Show) instance Pretty DigestMissing where pPrint (DigestMissing uri) =@@ -917,25 +980,25 @@ eBase _ = 800 eNum (MalformedScheme NonAlphaLeading) = 801- eNum (MalformedScheme InvalidChars) = 802- eNum (MalformedScheme MissingColon) = 803- eNum MalformedUserInfo = 804- eNum MalformedQuery = 805- eNum MalformedFragment = 806- eNum MalformedHost = 807- eNum MalformedPort = 808- eNum MalformedPath = 809- eNum (OtherError _) = 810+ eNum (MalformedScheme InvalidChars) = 802+ eNum (MalformedScheme MissingColon) = 803+ eNum MalformedUserInfo = 804+ eNum MalformedQuery = 805+ eNum MalformedFragment = 806+ eNum MalformedHost = 807+ eNum MalformedPort = 808+ eNum MalformedPath = 809+ eNum (OtherError _) = 810 eDesc _ = "Failed to parse URI." instance Pretty ArchiveResult where- pPrint ArchiveFatal = text "Archive result: fatal"+ pPrint ArchiveFatal = text "Archive result: fatal" pPrint ArchiveFailed = text "Archive result: failed"- pPrint ArchiveWarn = text "Archive result: warning"- pPrint ArchiveRetry = text "Archive result: retry"- pPrint ArchiveOk = text "Archive result: Ok"- pPrint ArchiveEOF = text "Archive result: EOF"+ pPrint ArchiveWarn = text "Archive result: warning"+ pPrint ArchiveRetry = text "Archive result: retry"+ pPrint ArchiveOk = text "Archive result: Ok"+ pPrint ArchiveEOF = text "Archive result: EOF" instance HFErrorProject ArchiveResult where eBase _ = 820
− lib/GHCup/GHC.hs
@@ -1,1426 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE TemplateHaskell #-}--{-|-Module : GHCup.GHC-Description : GHCup installation functions for GHC-Copyright : (c) Julian Ospald, 2020-License : LGPL-3.0-Maintainer : hasufell@hasufell.de-Stability : experimental-Portability : portable--}-module GHCup.GHC where---import GHCup.Download-import GHCup.Errors-import GHCup.Types-import GHCup.Types.JSON ( )-import GHCup.Types.Optics-import GHCup.Utils-import GHCup.Prelude-import GHCup.Prelude.File-import GHCup.Prelude.Logger-import GHCup.Prelude.Process-import GHCup.Prelude.String.QQ-import GHCup.Prelude.Version.QQ-import GHCup.Prelude.MegaParsec--import Control.Applicative-import Control.Concurrent ( threadDelay )-import Control.Exception.Safe-import Control.Monad-#if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )-#endif-import Control.Monad.Reader-import Control.Monad.Trans.Resource- hiding ( throwM )-import Data.ByteString ( ByteString )-import Data.Either-import Data.List-import Data.Maybe-import Data.List.NonEmpty ( NonEmpty((:|)) )-import Data.String ( fromString )-import Data.Text ( Text )-import Data.Time.Clock-import Data.Time.Format.ISO8601-import Data.Versions hiding ( patch )-import GHC.IO.Exception-import Data.Variant.Excepts-import Language.Haskell.TH-import Language.Haskell.TH.Syntax ( Quasi(qAddDependentFile) )-import Optics-import Prelude hiding ( abs- , writeFile- )-import System.Environment-import System.FilePath-import System.IO.Error-import Text.PrettyPrint.HughesPJClass ( prettyShow )-import Text.Regex.Posix-import URI.ByteString--import qualified Crypto.Hash.SHA256 as SHA256-import qualified Data.ByteString.Base16 as B16-import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy as BL-import qualified Data.Map.Strict as Map-import qualified Data.Text as T-import qualified Data.Text.IO as T-import qualified Data.Text.Encoding as E-import qualified Text.Megaparsec as MP---data GHCVer = SourceDist Version- | GitDist GitBranch- | RemoteDist URI- deriving (Eq, Show)---- --------------------- --[ Tool testing ]--- ------------------------testGHCVer :: ( MonadFail m- , MonadMask m- , MonadCatch m- , MonadReader env m- , HasDirs env- , HasSettings env- , HasPlatformReq env- , HasGHCupInfo env- , HasLog env- , MonadResource m- , MonadIO m- , MonadUnliftIO m- )- => GHCTargetVersion- -> [T.Text]- -> Excepts- '[ DigestError- , ContentLengthError- , GPGError- , DownloadFailed- , NoDownload- , ArchiveResult- , TarDirDoesNotExist- , UnknownArchive- , TestFailed- , URIParseError- ]- m- ()-testGHCVer ver addMakeArgs = do- GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo-- dlInfo <-- preview (ix GHC % ix ver % viTestDL % _Just) dls- ?? NoDownload ver GHC Nothing-- liftE $ testGHCBindist dlInfo ver addMakeArgs----testGHCBindist :: ( MonadFail m- , MonadMask m- , MonadCatch m- , MonadReader env m- , HasDirs env- , HasSettings env- , HasPlatformReq env- , HasGHCupInfo env- , HasLog env- , MonadResource m- , MonadIO m- , MonadUnliftIO m- )- => DownloadInfo- -> GHCTargetVersion- -> [T.Text]- -> Excepts- '[ DigestError- , ContentLengthError- , GPGError- , DownloadFailed- , NoDownload- , ArchiveResult- , TarDirDoesNotExist- , UnknownArchive- , TestFailed- , URIParseError- ]- m- ()-testGHCBindist dlinfo ver addMakeArgs = do- -- download (or use cached version)- dl <- liftE $ downloadCached dlinfo Nothing-- liftE $ testPackedGHC dl (view dlSubdir dlinfo) ver addMakeArgs---testPackedGHC :: ( MonadMask m- , MonadCatch m- , MonadReader env m- , HasDirs env- , HasPlatformReq env- , HasSettings env- , MonadThrow m- , HasLog env- , MonadIO m- , MonadUnliftIO m- , MonadFail m- , MonadResource m- )- => FilePath -- ^ Path to the packed GHC bindist- -> Maybe TarDir -- ^ Subdir of the archive- -> GHCTargetVersion -- ^ The GHC version- -> [T.Text] -- ^ additional make args- -> Excepts- '[ ArchiveResult, UnknownArchive, TarDirDoesNotExist, TestFailed ] m ()-testPackedGHC dl msubdir ver addMakeArgs = do- -- unpack- tmpUnpack <- lift mkGhcupTmpDir- liftE $ cleanUpOnError tmpUnpack (unpackToDir (fromGHCupPath tmpUnpack) dl)-- -- the subdir of the archive where we do the work- workdir <- maybe (pure tmpUnpack)- (liftE . intoSubdir tmpUnpack)- msubdir-- reThrowAll @_ @'[ArchiveResult, UnknownArchive, TarDirDoesNotExist, ProcessError]- (TestFailed (fromGHCupPath workdir)) $ liftE $ runBuildAction tmpUnpack- (testUnpackedGHC workdir ver addMakeArgs)--testUnpackedGHC :: ( MonadReader env m- , HasDirs env- , HasSettings env- , MonadThrow m- , HasLog env- , MonadIO m- )- => GHCupPath -- ^ Path to the unpacked GHC bindist (where the make file resides)- -> GHCTargetVersion -- ^ The GHC version- -> [T.Text] -- ^ additional configure args for bindist- -> Excepts '[ProcessError] m ()-testUnpackedGHC path tver addMakeArgs = do- lift $ logInfo $ "Testing GHC version " <> tVerToText tver <> "!"- ghcDir <- lift $ ghcupGHCDir tver- let ghcBinDir = fromGHCupPath ghcDir </> "bin"- env <- liftIO $ addToPath [ghcBinDir] False- let pathVar = if isWindows then "Path" else "PATH"- forM_ (Map.lookup pathVar . Map.fromList $ env) $ liftIO . setEnv pathVar-- lEM $ make' (fmap T.unpack addMakeArgs)- (Just $ fromGHCupPath path)- "ghc-test"- (Just $ ("STAGE1_GHC", maybe "" (T.unpack . (<> "-")) (_tvTarget tver)- <> "ghc-"- <> T.unpack (prettyVer $ _tvVersion tver)) : env)- pure ()--- ---------------------- --[ Tool fetching ]--- -------------------------fetchGHCSrc :: ( MonadFail m- , MonadMask m- , MonadCatch m- , MonadReader env m- , HasDirs env- , HasSettings env- , HasPlatformReq env- , HasGHCupInfo env- , HasLog env- , MonadResource m- , MonadIO m- , MonadUnliftIO m- )- => GHCTargetVersion- -> Maybe FilePath- -> Excepts- '[ DigestError- , ContentLengthError- , GPGError- , DownloadFailed- , NoDownload- , URIParseError- ]- m- FilePath-fetchGHCSrc v mfp = do- GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo- dlInfo <-- preview (ix GHC % ix v % viSourceDL % _Just) dls- ?? NoDownload v GHC Nothing- liftE $ downloadCached' dlInfo Nothing mfp---- -------------------------- --[ Tool installation ]--- ------------------------------ | Like 'installGHCBin', except takes the 'DownloadInfo' as--- argument instead of looking it up from 'GHCupDownloads'.-installGHCBindist :: ( MonadFail m- , MonadMask m- , MonadCatch m- , MonadReader env m- , HasDirs env- , HasSettings env- , HasPlatformReq env- , HasLog env- , MonadResource m- , MonadIO m- , MonadUnliftIO m- )- => DownloadInfo -- ^ where/how to download- -> GHCTargetVersion -- ^ the version to install- -> InstallDir- -> Bool -- ^ Force install- -> [T.Text] -- ^ additional configure args for bindist- -> T.Text- -> Excepts- '[ AlreadyInstalled- , BuildFailed- , DigestError- , ContentLengthError- , GPGError- , DownloadFailed- , NoDownload- , NotInstalled- , UnknownArchive- , TarDirDoesNotExist- , DirNotEmpty- , ArchiveResult- , ProcessError- , UninstallFailed- , MergeFileTreeError- , URIParseError- ]- m- ()-installGHCBindist dlinfo tver installDir forceInstall addConfArgs installTargets = do- lift $ logDebug $ "Requested to install GHC with " <> tVerToText tver-- regularGHCInstalled <- lift $ ghcInstalled tver-- if- | not forceInstall- , regularGHCInstalled- , GHCupInternal <- installDir -> do- throwE $ AlreadyInstalled GHC (_tvVersion tver)-- | forceInstall- , regularGHCInstalled- , GHCupInternal <- installDir -> do- lift $ logInfo "Removing the currently installed GHC version first!"- liftE $ rmGHCVer tver-- | otherwise -> pure ()-- -- download (or use cached version)- dl <- liftE $ downloadCached dlinfo Nothing--- toolchainSanityChecks-- case installDir of- IsolateDir isoDir -> do -- isolated install- lift $ logInfo $ "isolated installing GHC to " <> T.pack isoDir- liftE $ installPackedGHC dl (view dlSubdir dlinfo) (IsolateDirResolved isoDir) tver forceInstall addConfArgs installTargets- GHCupInternal -> do -- regular install- -- prepare paths- ghcdir <- lift $ ghcupGHCDir tver-- liftE $ installPackedGHC dl (view dlSubdir dlinfo) (GHCupDir ghcdir) tver forceInstall addConfArgs installTargets-- -- make symlinks & stuff when regular install,- liftE $ postGHCInstall tver-- where- toolchainSanityChecks = do- r <- forM ["CC", "LD"] (liftIO . lookupEnv)- case catMaybes r of- [] -> pure ()- _ -> do- lift $ logWarn $ "CC/LD environment variable is set. This will change the compiler/linker"- <> "\n" <> "GHC uses internally and can cause defunct GHC in some cases (e.g. in Anaconda"- <> "\n" <> "environments). If you encounter problems, unset CC and LD and reinstall."----- | Install a packed GHC distribution. This only deals with unpacking and the GHC--- build system and nothing else.-installPackedGHC :: ( MonadMask m- , MonadCatch m- , MonadReader env m- , HasDirs env- , HasPlatformReq env- , HasSettings env- , MonadThrow m- , HasLog env- , MonadIO m- , MonadUnliftIO m- , MonadFail m- , MonadResource m- )- => FilePath -- ^ Path to the packed GHC bindist- -> Maybe TarDir -- ^ Subdir of the archive- -> InstallDirResolved- -> GHCTargetVersion -- ^ The GHC version- -> Bool -- ^ Force install- -> [T.Text] -- ^ additional configure args for bindist- -> T.Text- -> Excepts- '[ BuildFailed- , UnknownArchive- , TarDirDoesNotExist- , DirNotEmpty- , ArchiveResult- , ProcessError- , MergeFileTreeError- ] m ()-installPackedGHC dl msubdir inst ver forceInstall addConfArgs installTargets = do- PlatformRequest {..} <- lift getPlatformReq-- unless forceInstall- (liftE $ installDestSanityCheck inst)-- -- unpack- tmpUnpack <- lift mkGhcupTmpDir- liftE $ cleanUpOnError tmpUnpack (unpackToDir (fromGHCupPath tmpUnpack) dl)- liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromGHCupPath tmpUnpack)-- -- the subdir of the archive where we do the work- workdir <- maybe (pure tmpUnpack)- (liftE . intoSubdir tmpUnpack)- msubdir-- liftE $ runBuildAction tmpUnpack- (installUnpackedGHC workdir inst ver forceInstall addConfArgs installTargets)----- | Install an unpacked GHC distribution. This only deals with the GHC--- build system and nothing else.-installUnpackedGHC :: ( MonadReader env m- , HasPlatformReq env- , HasDirs env- , HasSettings env- , MonadThrow m- , HasLog env- , MonadIO m- , MonadUnliftIO m- , MonadMask m- , MonadResource m- , MonadFail m- )- => GHCupPath -- ^ Path to the unpacked GHC bindist (where the configure script resides)- -> InstallDirResolved -- ^ Path to install to- -> GHCTargetVersion -- ^ The GHC version- -> Bool -- ^ Force install- -> [T.Text] -- ^ additional configure args for bindist- -> T.Text- -> Excepts '[ProcessError, MergeFileTreeError] m ()-installUnpackedGHC path inst tver forceInstall addConfArgs installTargets- | isWindows = do- lift $ logInfo "Installing GHC (this may take a while)"- -- Windows bindists are relocatable and don't need- -- to run configure.- -- We also must make sure to preserve mtime to not confuse ghc-pkg.- liftE $ mergeGHCFileTree path inst tver forceInstall- | otherwise = do- PlatformRequest {..} <- lift getPlatformReq- Settings {..} <- lift getSettings-- addConfArgs' <- sanitizefGHCconfOptions (T.unpack <$> addConfArgs)- defGHCConfOptions' <- sanitizefGHCconfOptions defGHCConfOptions-- lift $ logInfo "Installing GHC (this may take a while)"- lEM $ execLogged "sh"- ("./configure" : ("--prefix=" <> fromInstallDir inst)- : (maybe mempty (\x -> ["--target=" <> T.unpack x]) (_tvTarget tver)- <> ldOverride (_tvVersion tver) _rPlatform- <> defGHCConfOptions'- <> addConfArgs')- )- (Just $ fromGHCupPath path)- "ghc-configure"- Nothing- tmpInstallDest <- lift withGHCupTmpDir- lEM $ make (["DESTDIR=" <> fromGHCupPath tmpInstallDest] <> (T.unpack <$> withStripTarget (_tvVersion tver) _rPlatform)) (Just $ fromGHCupPath path)- liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromGHCupPath tmpInstallDest)- liftE $ mergeGHCFileTree (tmpInstallDest `appendGHCupPath` dropDrive (fromInstallDir inst)) inst tver forceInstall- pure ()- where- installTargets' = T.words installTargets-- -- https://github.com/haskell/ghcup-hs/issues/319- withStripTarget ver plat- | ver >= [vver|7.10.0|]- , ver < [vver|8.3.0|]- , Linux _ <- plat- = "STRIP_CMD=true":installTargets'- | otherwise- = installTargets'----mergeGHCFileTree :: ( MonadReader env m- , HasPlatformReq env- , HasDirs env- , HasSettings env- , MonadThrow m- , HasLog env- , MonadIO m- , MonadUnliftIO m- , MonadMask m- , MonadResource m- , MonadFail m- )- => GHCupPath -- ^ Path to the root of the tree- -> InstallDirResolved -- ^ Path to install to- -> GHCTargetVersion -- ^ The GHC version- -> Bool -- ^ Force install- -> Excepts '[MergeFileTreeError] m ()-mergeGHCFileTree root inst tver forceInstall- | isWindows = do- liftE $ mergeFileTree root inst GHC tver $ \source dest -> do- mtime <- liftIO $ ifM (pathIsSymbolicLink source) (pure Nothing) (Just <$> getModificationTime source)- when forceInstall $ hideError doesNotExistErrorType $ hideError InappropriateType $ recycleFile dest- liftIO $ moveFilePortable source dest- forM_ mtime $ liftIO . setModificationTime dest- | otherwise = do- liftE $ mergeFileTree root- inst- GHC- tver- (\f t -> liftIO $ do- mtime <- ifM (pathIsSymbolicLink f) (pure Nothing) (Just <$> getModificationTime f)- install f t (not forceInstall)- forM_ mtime $ setModificationTime t)----- | Installs GHC into @~\/.ghcup\/ghc/\<ver\>@ and places the--- following symlinks in @~\/.ghcup\/bin@:------ * @ghc-x.y.z -> ..\/ghc\/x.y.z\/bin/ghc@--- * @ghc-x.y -> ..\/ghc\/x.y.z\/bin/ghc@ (if x.y.z is the latest x.y version)-installGHCBin :: ( MonadFail m- , MonadMask m- , MonadCatch m- , MonadReader env m- , HasPlatformReq env- , HasGHCupInfo env- , HasDirs env- , HasSettings env- , HasLog env- , MonadResource m- , MonadIO m- , MonadUnliftIO m- , Alternative m- )- => GHCTargetVersion -- ^ the version to install- -> InstallDir- -> Bool -- ^ force install- -> [T.Text] -- ^ additional configure args for bindist- -> T.Text- -> Excepts- '[ AlreadyInstalled- , BuildFailed- , DigestError- , ContentLengthError- , GPGError- , DownloadFailed- , NoDownload- , NotInstalled- , UnknownArchive- , TarDirDoesNotExist- , DirNotEmpty- , ArchiveResult- , ProcessError- , UninstallFailed- , MergeFileTreeError- , NoCompatiblePlatform- , ParseError- , UnsupportedSetupCombo- , DistroNotFound- , NoCompatibleArch- , URIParseError- ]- m- ()-installGHCBin tver installDir forceInstall addConfArgs installTargets = do- dlinfo <- liftE $ getDownloadInfo' GHC tver- liftE $ installGHCBindist dlinfo tver installDir forceInstall addConfArgs installTargets------ ---------------- --[ Set GHC ]--- --------------------- | Set GHC symlinks in @~\/.ghcup\/bin@ for the requested GHC version. The behavior depends--- on `SetGHC`:------ * SetGHCOnly: @~\/.ghcup\/bin\/ghc -> ~\/.ghcup\/ghc\/\<ver\>\/bin\/ghc@--- * SetGHC_XY: @~\/.ghcup\/bin\/ghc-X.Y -> ~\/.ghcup\/ghc\/\<ver\>\/bin\/ghc@--- * SetGHC_XYZ: @~\/.ghcup\/bin\/ghc-\<ver\> -> ~\/.ghcup\/ghc\/\<ver\>\/bin\/ghc@------ Additionally creates a @~\/.ghcup\/share -> ~\/.ghcup\/ghc\/\<ver\>\/share symlink@--- for 'SetGHCOnly' constructor.-setGHC :: ( MonadReader env m- , HasDirs env- , HasLog env- , MonadThrow m- , MonadFail m- , MonadIO m- , MonadCatch m- , MonadMask m- , MonadUnliftIO m- )- => GHCTargetVersion- -> SetGHC- -> Maybe FilePath -- if set, signals that we're not operating in ~/.ghcup/bin- -- and don't want mess with other versions- -> Excepts '[NotInstalled] m GHCTargetVersion-setGHC ver sghc mBinDir = do- let verS = T.unpack $ prettyVer (_tvVersion ver)- ghcdir <- lift $ ghcupGHCDir ver-- whenM (lift $ not <$> ghcInstalled ver) (throwE (NotInstalled GHC ver))-- -- symlink destination- binDir <- case mBinDir of- Just x -> pure x- Nothing -> do- Dirs {binDir = f} <- lift getDirs- pure f-- -- first delete the old symlinks (this fixes compatibility issues- -- with old ghcup)- when (isNothing mBinDir) $- case sghc of- SetGHCOnly -> liftE $ rmPlainGHC (_tvTarget ver)- SetGHC_XY -> liftE $ rmMajorGHCSymlinks ver- SetGHC_XYZ -> liftE $ rmMinorGHCSymlinks ver-- -- for ghc tools (ghc, ghci, haddock, ...)- verfiles <- ghcToolFiles ver- forM_ verfiles $ \file -> do- mTargetFile <- case sghc of- SetGHCOnly -> pure $ Just file- SetGHC_XY -> do- handle- (\(e :: ParseError) -> lift $ logWarn (T.pack $ displayException e) >> pure Nothing)- $ do- (mj, mi) <- getMajorMinorV (_tvVersion ver)- let major' = intToText mj <> "." <> intToText mi- pure $ Just (file <> "-" <> T.unpack major')- SetGHC_XYZ ->- pure $ Just (file <> "-" <> verS)-- -- create symlink- forM_ mTargetFile $ \targetFile -> do- bindir <- ghcInternalBinDir ver- let fullF = binDir </> targetFile <> exeExt- fileWithExt = bindir </> file <> exeExt- destL <- binarySymLinkDestination binDir fileWithExt- lift $ createLink destL fullF-- when (targetFile == "ghc") $- liftIO (isShadowed fullF) >>= \case- Nothing -> pure ()- Just pa -> lift $ logWarn $ T.pack $ prettyHFError (ToolShadowed GHC pa fullF (_tvVersion ver))-- when (isNothing mBinDir) $ do- -- create symlink for share dir- when (isNothing . _tvTarget $ ver) $ lift $ symlinkShareDir (fromGHCupPath ghcdir) verS-- when (sghc == SetGHCOnly) $ lift warnAboutHlsCompatibility-- pure ver-- where-- symlinkShareDir :: ( MonadReader env m- , HasDirs env- , MonadIO m- , HasLog env- , MonadCatch m- , MonadMask m- )- => FilePath- -> String- -> m ()- symlinkShareDir ghcdir ver' = do- Dirs {..} <- getDirs- let destdir = fromGHCupPath baseDir- case sghc of- SetGHCOnly -> do- let sharedir = "share"- let fullsharedir = ghcdir </> sharedir- logDebug $ "Checking for sharedir existence: " <> T.pack fullsharedir- whenM (liftIO $ doesDirectoryExist fullsharedir) $ do- let fullF = destdir </> sharedir- let targetF = "." </> "ghc" </> ver' </> sharedir- logDebug $ "rm -f " <> T.pack fullF- hideError doesNotExistErrorType $ rmDirectoryLink fullF- logDebug $ "ln -s " <> T.pack targetF <> " " <> T.pack fullF-- if isWindows- then liftIO- -- On windows we need to be more permissive- -- in case symlinks can't be created, be just- -- give up here. This symlink isn't strictly necessary.- $ hideError permissionErrorType- $ hideError illegalOperationErrorType- $ createDirectoryLink targetF fullF- else liftIO- $ createDirectoryLink targetF fullF- _ -> pure ()--unsetGHC :: ( MonadReader env m- , HasDirs env- , HasLog env- , MonadThrow m- , MonadFail m- , MonadIO m- , MonadMask m- )- => Maybe Text- -> Excepts '[NotInstalled] m ()-unsetGHC = rmPlainGHC------ --------------- --[ GHC rm ]--- ------------------- | Delete a ghc version and all its symlinks.------ This may leave GHCup without a "set" version.--- Will try to fix the ghc-x.y symlink after removal (e.g. to an--- older version).-rmGHCVer :: ( MonadReader env m- , HasDirs env- , MonadThrow m- , HasLog env- , MonadIO m- , MonadFail m- , MonadCatch m- , MonadMask m- , MonadUnliftIO m- )- => GHCTargetVersion- -> Excepts '[NotInstalled, UninstallFailed] m ()-rmGHCVer ver = do- isSetGHC <- lift $ fmap (== Just ver) $ ghcSet (_tvTarget ver)-- whenM (lift $ fmap not $ ghcInstalled ver) (throwE (NotInstalled GHC ver))-- -- this isn't atomic, order matters- when isSetGHC $ do- lift $ logInfo "Removing ghc symlinks"- liftE $ rmPlainGHC (_tvTarget ver)-- lift $ logInfo "Removing ghc-x.y.z symlinks"- liftE $ rmMinorGHCSymlinks ver-- lift $ logInfo "Removing/rewiring ghc-x.y symlinks"- -- first remove- handle (\(_ :: ParseError) -> pure ()) $ liftE $ rmMajorGHCSymlinks ver- -- then fix them (e.g. with an earlier version)-- dir' <- lift $ ghcupGHCDir ver- let dir = fromGHCupPath dir'- lift (getInstalledFiles GHC ver) >>= \case- Just files -> do- lift $ logInfo $ "Removing files safely from: " <> T.pack dir- forM_ files (lift . hideError NoSuchThing . recycleFile . (\f -> dir </> dropDrive f))- hideError UnsatisfiedConstraints $ removeEmptyDirsRecursive dir- survivors <- liftIO $ hideErrorDef [doesNotExistErrorType] [] $ listDirectory dir- f <- recordedInstallationFile GHC ver- lift $ recycleFile f- when (not (null survivors)) $ throwE $ UninstallFailed dir survivors- Nothing -> do- isDir <- liftIO $ doesDirectoryExist dir- isSyml <- liftIO $ handleIO (\_ -> pure False) $ pathIsSymbolicLink dir- when (isDir && not isSyml) $ do- lift $ logInfo $ "Removing legacy directory recursively: " <> T.pack dir- recyclePathForcibly dir'-- v' <-- handle- (\(e :: ParseError) -> lift $ logWarn (T.pack $ displayException e) >> pure Nothing)- $ fmap Just- $ getMajorMinorV (_tvVersion ver)- forM_ v' $ \(mj, mi) -> lift (getGHCForPVP (PVP (fromIntegral mj :| [fromIntegral mi])) (_tvTarget ver))- >>= mapM_ (\v -> liftE $ setGHC v SetGHC_XY Nothing)-- Dirs {..} <- lift getDirs-- when isSetGHC $ do- lift $ hideError doesNotExistErrorType $ rmDirectoryLink (fromGHCupPath baseDir </> "share")----- ---------------- --[ Compile ]--- -------------------- | Compile a GHC from source. This behaves wrt symlinks and installation--- the same as 'installGHCBin'.-compileGHC :: ( MonadMask m- , MonadReader env m- , HasDirs env- , HasPlatformReq env- , HasGHCupInfo env- , HasSettings env- , MonadThrow m- , MonadResource m- , HasLog env- , MonadIO m- , MonadUnliftIO m- , MonadFail m- )- => GHCVer- -> Maybe Text -- ^ cross target- -> Maybe [VersionPattern]- -> Either Version FilePath -- ^ GHC version to bootstrap with- -> Maybe (Either Version FilePath) -- ^ GHC version to compile hadrian with- -> Maybe Int -- ^ jobs- -> Maybe FilePath -- ^ build config- -> Maybe (Either FilePath [URI]) -- ^ patches- -> [Text] -- ^ additional args to ./configure- -> Maybe String -- ^ build flavour- -> Maybe BuildSystem- -> InstallDir- -> T.Text- -> Excepts- '[ AlreadyInstalled- , BuildFailed- , DigestError- , ContentLengthError- , GPGError- , DownloadFailed- , GHCupSetError- , NoDownload- , NotFoundInPATH- , PatchFailed- , UnknownArchive- , TarDirDoesNotExist- , NotInstalled- , DirNotEmpty- , ArchiveResult- , FileDoesNotExistError- , HadrianNotFound- , InvalidBuildConfig- , ProcessError- , CopyError- , BuildFailed- , UninstallFailed- , MergeFileTreeError- , URIParseError- ]- m- GHCTargetVersion-compileGHC targetGhc crossTarget vps bstrap hghc jobs mbuildConfig patches aargs buildFlavour buildSystem installDir installTargets- = do- pfreq@PlatformRequest { .. } <- lift getPlatformReq- GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo-- (workdir, tmpUnpack, tver, ov) <- case targetGhc of- -- unpack from version tarball- SourceDist ver -> do- lift $ logDebug $ "Requested to compile: " <> prettyVer ver <> " with " <> either prettyVer T.pack bstrap-- -- download source tarball- let tver = mkTVer ver- dlInfo <-- preview (ix GHC % ix tver % viSourceDL % _Just) dls- ?? NoDownload tver GHC (Just pfreq)- dl <- liftE $ downloadCached dlInfo Nothing-- -- unpack- tmpUnpack <- lift mkGhcupTmpDir- liftE $ cleanUpOnError tmpUnpack (unpackToDir (fromGHCupPath tmpUnpack) dl)- liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform $ fromGHCupPath tmpUnpack-- workdir <- maybe (pure tmpUnpack)- (liftE . intoSubdir tmpUnpack)- (view dlSubdir dlInfo)- liftE $ applyAnyPatch patches (fromGHCupPath workdir)-- ov <- case vps of- Just vps' -> fmap Just $ expandVersionPattern (Just ver) "" "" "" "" vps'- Nothing -> pure Nothing-- pure (workdir, tmpUnpack, Just (GHCTargetVersion crossTarget ver), ov)-- RemoteDist uri -> do- lift $ logDebug $ "Requested to compile (from uri): " <> T.pack (show uri)-- -- download source tarball- tmpDownload <- lift withGHCupTmpDir- tmpUnpack <- lift mkGhcupTmpDir- tar <- liftE $ download uri Nothing Nothing Nothing (fromGHCupPath tmpDownload) Nothing False- (bf, tver) <- liftE $ cleanUpOnError @'[UnknownArchive, ArchiveResult, ProcessError] tmpUnpack $ do- liftE $ unpackToDir (fromGHCupPath tmpUnpack) tar- let regex = [s|^(.*/)*boot$|] :: B.ByteString- [bootFile] <- liftIO $ findFilesDeep- tmpUnpack- (makeRegexOpts compExtended- execBlank- regex- )- tver <- liftE $ catchAllE @_ @'[ProcessError, ParseError, NotFoundInPATH] @'[] (\_ -> pure Nothing) $ fmap Just $ getGHCVer- (appendGHCupPath tmpUnpack (takeDirectory bootFile))- pure (bootFile, tver)-- let workdir = appendGHCupPath tmpUnpack (takeDirectory bf)-- ov <- case vps of- Just vps' -> fmap Just $ expandVersionPattern tver "" "" "" "" vps'- Nothing -> pure Nothing-- pure (workdir, tmpUnpack, GHCTargetVersion crossTarget <$> tver, ov)-- -- clone from git- GitDist GitBranch{..} -> do- tmpUnpack <- lift mkGhcupTmpDir- let git args = execLogged "git" ("--no-pager":args) (Just $ fromGHCupPath tmpUnpack) "git" Nothing- (tver, ov) <- cleanUpOnError tmpUnpack $ reThrowAll @_ @'[PatchFailed, ProcessError, NotFoundInPATH, DigestError, ContentLengthError, DownloadFailed, GPGError] DownloadFailed $ do- let rep = fromMaybe "https://gitlab.haskell.org/ghc/ghc.git" repo- lift $ logInfo $ "Fetching git repo " <> T.pack rep <> " at ref " <> T.pack ref <> " (this may take a while)"- lEM $ git [ "init" ]- lEM $ git [ "remote"- , "add"- , "origin"- , fromString rep ]-- -- figure out if we can do a shallow clone- remoteBranches <- catchE @ProcessError @'[PatchFailed, ProcessError, NotFoundInPATH, DigestError, ContentLengthError, DownloadFailed, GPGError] @'[PatchFailed, NotFoundInPATH, DigestError, DownloadFailed, GPGError] (\(_ :: ProcessError) -> pure [])- $ fmap processBranches $ gitOut ["ls-remote", "--heads", "origin"] (fromGHCupPath tmpUnpack)- let shallow_clone- | isCommitHash ref = True- | fromString ref `elem` remoteBranches = True- | otherwise = False- lift $ logDebug $ "Shallow clone: " <> T.pack (show shallow_clone)-- -- fetch- let fetch_args- | shallow_clone = ["fetch", "--depth", "1", "--quiet", "origin", fromString ref]- | otherwise = ["fetch", "--tags", "--quiet", "origin" ]- lEM $ git fetch_args-- -- initial checkout- lEM $ git [ "checkout", fromString ref ]-- -- gather some info- git_describe <- if shallow_clone- then pure Nothing- else fmap Just $ liftE $ gitOut ["describe", "--tags"] (fromGHCupPath tmpUnpack)- chash <- liftE $ gitOut ["rev-parse", "HEAD" ] (fromGHCupPath tmpUnpack)- branch <- liftE $ gitOut ["rev-parse", "--abbrev-ref", "HEAD" ] (fromGHCupPath tmpUnpack)-- -- clone submodules- lEM $ git [ "submodule", "update", "--init", "--depth", "1" ]-- -- apply patches- liftE $ applyAnyPatch patches (fromGHCupPath tmpUnpack)-- -- bootstrap- tver <- liftE $ catchAllE @_ @'[ProcessError, ParseError, NotFoundInPATH] @'[] (\_ -> pure Nothing) $ fmap Just $ getGHCVer- tmpUnpack- liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromGHCupPath tmpUnpack)- lift $ logInfo $ "Examining git ref " <> T.pack ref <> "\n " <>- "GHC version (from Makefile): " <> T.pack (show (prettyVer <$> tver)) <>- (if not shallow_clone then "\n " <> "'git describe' output: " <> fromJust git_describe else mempty) <>- (if isCommitHash ref then mempty else "\n " <> "commit hash: " <> chash)- liftIO $ threadDelay 5000000 -- give the user a sec to intervene-- ov <- case vps of- Just vps' -> fmap Just $ expandVersionPattern- tver- (take 7 $ T.unpack chash)- (T.unpack chash)- (maybe "" T.unpack git_describe)- (T.unpack branch)- vps'- Nothing -> pure Nothing-- pure (tver, ov)-- pure (tmpUnpack, tmpUnpack, GHCTargetVersion crossTarget <$> tver, ov)- -- the version that's installed may differ from the- -- compiled version, so the user can overwrite it- installVer <- if | Just ov' <- ov -> pure (GHCTargetVersion crossTarget ov')- | Just tver' <- tver -> pure tver'- | otherwise -> fail "No GHC version given and couldn't detect version. Giving up..."-- alreadyInstalled <- lift $ ghcInstalled installVer- alreadySet <- fmap (== Just installVer) $ lift $ ghcSet (_tvTarget installVer)-- when alreadyInstalled $ do- case installDir of- IsolateDir isoDir ->- lift $ logWarn $ "GHC " <> T.pack (prettyShow installVer) <> " already installed. Isolate installing to " <> T.pack isoDir- GHCupInternal ->- lift $ logWarn $ "GHC " <> T.pack (prettyShow installVer) <> " already installed. Will overwrite existing version."- lift $ logWarn- "...waiting for 10 seconds before continuing, you can still abort..."- liftIO $ threadDelay 10000000 -- give the user a sec to intervene-- ghcdir <- case installDir of- IsolateDir isoDir -> pure $ IsolateDirResolved isoDir- GHCupInternal -> GHCupDir <$> lift (ghcupGHCDir installVer)-- mBindist <- liftE $ runBuildAction- tmpUnpack- (do- -- prefer 'tver', because the real version carries out compatibility checks- -- we don't want the user to do funny things with it- let doHadrian = compileHadrianBindist (fromMaybe installVer tver) (fromGHCupPath workdir) ghcdir- doMake = compileMakeBindist (fromMaybe installVer tver) (fromGHCupPath workdir) ghcdir- case buildSystem of- Just Hadrian -> do- lift $ logInfo "Requested to use Hadrian"- liftE doHadrian- Just Make -> do- lift $ logInfo "Requested to use Make"- doMake- Nothing -> do- supportsHadrian <- liftE $ catchE @HadrianNotFound @'[HadrianNotFound] @'[] (\_ -> return False)- $ fmap (const True)- $ findHadrianFile (fromGHCupPath workdir)- if supportsHadrian- then do- lift $ logInfo "Detected Hadrian"- liftE doHadrian- else do- lift $ logInfo "Detected Make"- doMake- )-- case installDir of- GHCupInternal ->- -- only remove old ghc in regular installs- when alreadyInstalled $ do- lift $ logInfo "Deleting existing installation"- liftE $ rmGHCVer installVer-- _ -> pure ()-- forM_ mBindist $ \bindist -> do- liftE $ installPackedGHC bindist- (Just $ RegexDir "ghc-.*")- ghcdir- installVer- False -- not a force install, since we already overwrite when compiling.- []- installTargets-- case installDir of- -- set and make symlinks for regular (non-isolated) installs- GHCupInternal -> do- reThrowAll GHCupSetError $ postGHCInstall installVer- -- restore- when alreadySet $ liftE $ void $ setGHC installVer SetGHCOnly Nothing-- _ -> pure ()-- pure installVer-- where- getGHCVer :: ( MonadReader env m- , HasSettings env- , HasDirs env- , HasLog env- , MonadIO m- , MonadThrow m- )- => GHCupPath- -> Excepts '[ProcessError, ParseError, NotFoundInPATH] m Version- getGHCVer tmpUnpack = do- lEM $ execLogged "python3" ["./boot"] (Just $ fromGHCupPath tmpUnpack) "ghc-bootstrap" Nothing- liftE $ configureWithGhcBoot Nothing [] (Just $ fromGHCupPath tmpUnpack) "ghc-bootstrap"- let versionFile = fromGHCupPath tmpUnpack </> "VERSION"- hasVersionFile <- liftIO $ doesFileExist versionFile- if hasVersionFile- then do- lift $ logDebug "Detected VERSION file, trying to extract"- contents <- liftIO $ readFile versionFile- either (throwE . ParseError . show) pure . MP.parse version' "" . T.pack . stripNewlineEnd $ contents- else do- lift $ logDebug "Didn't detect VERSION file, trying to extract via legacy 'make'"- CapturedProcess {..} <- lift $ makeOut- ["show!", "--quiet", "VALUE=ProjectVersion" ] (Just $ fromGHCupPath tmpUnpack)- case _exitCode of- ExitSuccess -> either (throwE . ParseError . show) pure . MP.parse ghcProjectVersion "" . T.pack . stripNewlineEnd . T.unpack . decUTF8Safe' $ _stdOut- ExitFailure c -> throwE $ NonZeroExit c "make" ["show!", "--quiet", "VALUE=ProjectVersion" ]-- defaultConf =- let cross_mk = $(LitE . StringL <$> (qAddDependentFile "data/build_mk/cross" >> runIO (readFile "data/build_mk/cross")))- default_mk = $(LitE . StringL <$> (qAddDependentFile "data/build_mk/default" >> runIO (readFile "data/build_mk/default")))- in case crossTarget of- Just _ -> cross_mk- _ -> default_mk-- compileHadrianBindist :: ( MonadReader env m- , HasDirs env- , HasSettings env- , HasPlatformReq env- , MonadThrow m- , MonadCatch m- , HasLog env- , MonadIO m- , MonadFail m- )- => GHCTargetVersion- -> FilePath- -> InstallDirResolved- -> Excepts- '[ FileDoesNotExistError- , HadrianNotFound- , InvalidBuildConfig- , PatchFailed- , ProcessError- , NotFoundInPATH- , CopyError]- m- (Maybe FilePath) -- ^ output path of bindist, None for cross- compileHadrianBindist tver workdir ghcdir = do- liftE $ configureBindist tver workdir ghcdir-- lift $ logInfo $ "Building GHC version " <> tVerToText tver <> " (this may take a while)..."- hadrian_build <- liftE $ findHadrianFile workdir- hEnv <- case hghc of- Nothing -> pure Nothing- Just hghc' -> do- cEnv <- Map.fromList <$> liftIO getEnvironment- ghc <- liftE $ resolveGHC hghc'- pure . Just . Map.toList . Map.insert "GHC" ghc $ cEnv--- lEM $ execLogged hadrian_build- ( maybe [] (\j -> ["-j" <> show j] ) jobs- ++ maybe [] (\bf -> ["--flavour=" <> bf]) buildFlavour- ++ ["binary-dist"]- )- (Just workdir) "ghc-make"- hEnv- [tar] <- liftIO $ findFiles- (workdir </> "_build" </> "bindist")- (makeRegexOpts compExtended- execBlank- ([s|^ghc-.*\.tar\..*$|] :: ByteString)- )- liftE $ fmap Just $ copyBindist tver tar (workdir </> "_build" </> "bindist")-- findHadrianFile :: (MonadIO m)- => FilePath- -> Excepts- '[HadrianNotFound]- m- FilePath- findHadrianFile workdir = do- let possible_files = if isWindows- then ((workdir </> "hadrian") </>) <$> ["build.bat"]- else ((workdir </> "hadrian") </>) <$> ["build", "build.sh"]- exists <- forM possible_files (\f -> liftIO (doesFileExist f) <&> (,f))- case filter fst exists of- [] -> throwE HadrianNotFound- ((_, x):_) -> pure x-- compileMakeBindist :: ( MonadReader env m- , HasDirs env- , HasSettings env- , HasPlatformReq env- , MonadThrow m- , MonadCatch m- , HasLog env- , MonadIO m- , MonadFail m- , MonadMask m- , MonadUnliftIO m- , MonadResource m- )- => GHCTargetVersion- -> FilePath- -> InstallDirResolved- -> Excepts- '[ FileDoesNotExistError- , HadrianNotFound- , InvalidBuildConfig- , PatchFailed- , ProcessError- , NotFoundInPATH- , MergeFileTreeError- , CopyError]- m- (Maybe FilePath) -- ^ output path of bindist, None for cross- compileMakeBindist tver workdir ghcdir = do- liftE $ configureBindist tver workdir ghcdir-- case mbuildConfig of- Just bc -> liftIOException- doesNotExistErrorType- (FileDoesNotExistError bc)- (liftIO $ copyFile bc (build_mk workdir) False)- Nothing ->- liftIO $ T.writeFile (build_mk workdir) (addBuildFlavourToConf defaultConf)-- liftE $ checkBuildConfig (build_mk workdir)-- lift $ logInfo $ "Building GHC version " <> tVerToText tver <> " (this may take a while)..."- lEM $ make (maybe [] (\j -> ["-j" <> fS (show j)]) jobs) (Just workdir)-- if | isCross tver -> do- lift $ logInfo "Installing cross toolchain..."- tmpInstallDest <- lift withGHCupTmpDir- lEM $ make ["DESTDIR=" <> fromGHCupPath tmpInstallDest, "install"] (Just workdir)- liftE $ mergeGHCFileTree (tmpInstallDest `appendGHCupPath` dropDrive (fromInstallDir ghcdir)) ghcdir tver True- pure Nothing- | otherwise -> do- lift $ logInfo "Creating bindist..."- lEM $ make ["binary-dist"] (Just workdir)- [tar] <- liftIO $ findFiles- workdir- (makeRegexOpts compExtended- execBlank- ([s|^ghc-.*\.tar\..*$|] :: ByteString)- )- liftE $ fmap Just $ copyBindist tver tar workdir-- build_mk workdir = workdir </> "mk" </> "build.mk"-- copyBindist :: ( MonadReader env m- , HasDirs env- , HasSettings env- , HasPlatformReq env- , MonadIO m- , MonadThrow m- , MonadCatch m- , HasLog env- )- => GHCTargetVersion- -> FilePath -- ^ tar file- -> FilePath -- ^ workdir- -> Excepts- '[CopyError]- m- FilePath- copyBindist tver tar workdir = do- Dirs {..} <- lift getDirs- pfreq <- lift getPlatformReq- c <- liftIO $ BL.readFile (workdir </> tar)- cDigest <-- fmap (T.take 8)- . lift- . throwEither- . E.decodeUtf8'- . B16.encode- . SHA256.hashlazy- $ c- cTime <- liftIO getCurrentTime- let tarName = makeValid ("ghc-"- <> T.unpack (tVerToText tver)- <> "-"- <> pfReqToString pfreq- <> "-"- <> iso8601Show cTime- <> "-"- <> T.unpack cDigest- <> ".tar"- <> takeExtension tar)- let tarPath = fromGHCupPath cacheDir </> tarName- copyFileE (workdir </> tar) tarPath False- lift $ logInfo $ "Copied bindist to " <> T.pack tarPath- pure tarPath-- checkBuildConfig :: (MonadReader env m, MonadCatch m, MonadIO m, HasLog env)- => FilePath- -> Excepts- '[FileDoesNotExistError, InvalidBuildConfig]- m- ()- checkBuildConfig bc = do- c <- liftIOException- doesNotExistErrorType- (FileDoesNotExistError bc)- (liftIO $ B.readFile bc)- let lines' = fmap T.strip . T.lines $ decUTF8Safe c-- -- for cross, we need Stage1Only- case crossTarget of- Just _ -> when ("Stage1Only = YES" `notElem` lines') $ throwE- (InvalidBuildConfig- [s|Cross compiling needs to be a Stage1 build, add "Stage1Only = YES" to your config!|]- )- _ -> pure ()-- forM_ buildFlavour $ \bf ->- when (T.pack ("BuildFlavour = " <> bf) `notElem` lines') $ do- lift $ logWarn $ "Customly specified build config overwrites --flavour=" <> T.pack bf <> " switch! Waiting 5 seconds..."- liftIO $ threadDelay 5000000-- addBuildFlavourToConf bc = case buildFlavour of- Just bf -> "BuildFlavour = " <> T.pack bf <> "\n" <> bc- Nothing -> bc-- isCross :: GHCTargetVersion -> Bool- isCross = isJust . _tvTarget--- configureBindist :: ( MonadReader env m- , HasDirs env- , HasSettings env- , HasPlatformReq env- , MonadThrow m- , MonadCatch m- , HasLog env- , MonadIO m- , MonadFail m- )- => GHCTargetVersion- -> FilePath- -> InstallDirResolved- -> Excepts- '[ FileDoesNotExistError- , InvalidBuildConfig- , PatchFailed- , ProcessError- , NotFoundInPATH- , CopyError- ]- m- ()- configureBindist tver workdir (fromInstallDir -> ghcdir) = do- PlatformRequest { .. } <- lift getPlatformReq- lift $ logInfo [s|configuring build|]- liftE $ configureWithGhcBoot (Just tver)- (maybe mempty- (\x -> ["--target=" <> T.unpack x])- (_tvTarget tver)- ++ ["--prefix=" <> ghcdir]- ++ (if isWindows then ["--enable-tarballs-autodownload"] else [])- -- https://github.com/haskell/ghcup-hs/issues/1032- ++ ldOverride (_tvVersion tver) _rPlatform- ++ fmap T.unpack aargs- )- (Just workdir)- "ghc-conf"- pure ()-- configureWithGhcBoot :: ( MonadReader env m- , HasSettings env- , HasDirs env- , HasLog env- , MonadIO m- , MonadThrow m)- => Maybe GHCTargetVersion- -> [String] -- ^ args for configure- -> Maybe FilePath -- ^ optionally chdir into this- -> FilePath -- ^ log filename (opened in append mode)- -> Excepts '[ProcessError, NotFoundInPATH] m ()- configureWithGhcBoot mtver args dir logf = do- bghc <- liftE $ resolveGHC bstrap- let execNew = execLogged- "sh"- ("./configure" : ("GHC=" <> bghc) : args)- dir- logf- Nothing- execOld = execLogged- "sh"- ("./configure" : ("--with-ghc=" <> bghc) : args)- dir- logf- Nothing- if | Just tver <- mtver- , _tvVersion tver >= [vver|8.8.0|] -> lEM execNew- | Nothing <- mtver -> lEM execNew -- need some default for git checkouts where we don't know yet- | otherwise -> lEM execOld-- resolveGHC :: MonadIO m => Either Version FilePath -> Excepts '[NotFoundInPATH] m FilePath- resolveGHC = \case- Right g -> pure g- Left bver -> do- let ghc = "ghc-" <> (T.unpack . prettyVer $ bver) <> exeExt- -- https://gitlab.haskell.org/ghc/ghc/-/issues/24682- makeAbsolute ghc----- -------------- --[ Other ]--- ------------------- | Creates @ghc-x.y.z@ and @ghc-x.y@ symlinks. This is used for--- both installing from source and bindist.-postGHCInstall :: ( MonadReader env m- , HasDirs env- , HasLog env- , MonadThrow m- , MonadFail m- , MonadIO m- , MonadCatch m- , MonadMask m- , MonadUnliftIO m- )- => GHCTargetVersion- -> Excepts '[NotInstalled] m ()-postGHCInstall ver@GHCTargetVersion {..} = do- void $ liftE $ setGHC ver SetGHC_XYZ Nothing-- -- Create ghc-x.y symlinks. This may not be the current- -- version, create it regardless.- v' <-- handle (\(e :: ParseError) -> lift $ logWarn (T.pack $ displayException e) >> pure Nothing)- $ fmap Just- $ getMajorMinorV _tvVersion- forM_ v' $ \(mj, mi) -> lift (getGHCForPVP (PVP (fromIntegral mj :| [fromIntegral mi])) _tvTarget)- >>= mapM_ (\v -> liftE $ setGHC v SetGHC_XY Nothing)---ldOverride :: Version -> Platform -> [String]-ldOverride ver plat- | ver >= [vver|8.2.2|]- = ["--disable-ld-override"]- | otherwise- = []--sanitizefGHCconfOptions :: MonadFail m => [String] -> m [String]-sanitizefGHCconfOptions args- | "--prefix" `elem` fmap (takeWhile (/= '=')) args = fail "Don't explicitly set --prefix ...aborting"- | otherwise = pure args-
− lib/GHCup/HLS.hs
@@ -1,744 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-}--{-|-Module : GHCup.HLS-Description : GHCup installation functions for HLS-Copyright : (c) Julian Ospald, 2020-License : LGPL-3.0-Maintainer : hasufell@hasufell.de-Stability : experimental-Portability : portable--}-module GHCup.HLS where--import GHCup.Download-import GHCup.Errors-import GHCup.Types-import GHCup.Types.JSON ( )-import GHCup.Types.Optics-import GHCup.Utils-import GHCup.Prelude-import GHCup.Prelude.File-import GHCup.Prelude.Logger-import GHCup.Prelude.Process-import GHCup.Prelude.String.QQ--import Control.Applicative-import Control.Exception.Safe-import Control.Monad-#if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )-#endif-import Control.Monad.Reader-import Control.Monad.Trans.Resource- hiding ( throwM )-import Data.ByteString ( ByteString )-import Data.Either-import Data.List-import Data.Ord-import Data.Maybe-import Data.String ( fromString )-import Data.Text ( Text )-import Data.Versions hiding ( patch )-import Distribution.Types.Version hiding ( Version )-import Distribution.Types.PackageId-import Distribution.Types.PackageDescription-import Distribution.Types.GenericPackageDescription-import Distribution.PackageDescription.Parsec-import GHC.IO.Exception-import Data.Variant.Excepts-import Optics-import Prelude hiding ( abs- , writeFile- )-import Safe hiding ( at )-import System.FilePath-import System.IO.Error-import Text.Regex.Posix-import URI.ByteString--import qualified Data.List.NonEmpty as NE-import qualified Data.ByteString as B-import qualified Data.Text as T-import qualified Text.Megaparsec as MP---data HLSVer = SourceDist Version- | GitDist GitBranch- | HackageDist Version- | RemoteDist URI- deriving (Eq, Show)---- --------------------- --[ Installation ]--- ------------------------- | Like 'installHLSBin, except takes the 'DownloadInfo' as--- argument instead of looking it up from 'GHCupDownloads'.-installHLSBindist :: ( MonadMask m- , MonadCatch m- , MonadReader env m- , HasPlatformReq env- , HasDirs env- , HasSettings env- , HasLog env- , MonadResource m- , MonadIO m- , MonadUnliftIO m- , MonadFail m- )- => DownloadInfo- -> Version- -> InstallDir -- ^ isolated install path, if user passed any- -> Bool -- ^ Force install- -> Excepts- '[ AlreadyInstalled- , CopyError- , DigestError- , ContentLengthError- , GPGError- , DownloadFailed- , NoDownload- , NotInstalled- , UnknownArchive- , TarDirDoesNotExist- , ArchiveResult- , FileAlreadyExistsError- , ProcessError- , DirNotEmpty- , UninstallFailed- , MergeFileTreeError- , URIParseError- ]- m- ()-installHLSBindist dlinfo ver installDir forceInstall = do- lift $ logDebug $ "Requested to install hls version " <> prettyVer ver-- PlatformRequest {..} <- lift getPlatformReq- Dirs {..} <- lift getDirs-- regularHLSInstalled <- lift $ hlsInstalled ver-- if- | not forceInstall- , regularHLSInstalled- , GHCupInternal <- installDir -> do -- regular install- throwE $ AlreadyInstalled HLS ver-- | forceInstall- , regularHLSInstalled- , GHCupInternal <- installDir -> do -- regular forced install- lift $ logInfo "Removing the currently installed version of HLS before force installing!"- liftE $ rmHLSVer ver-- | otherwise -> pure ()-- -- download (or use cached version)- dl <- liftE $ downloadCached dlinfo Nothing-- -- unpack- tmpUnpack <- lift withGHCupTmpDir- liftE $ cleanUpOnError tmpUnpack (unpackToDir (fromGHCupPath tmpUnpack) dl)- liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromGHCupPath tmpUnpack)-- -- the subdir of the archive where we do the work- workdir <- fromGHCupPath <$> maybe (pure tmpUnpack) (liftE . intoSubdir tmpUnpack) (view dlSubdir dlinfo)- legacy <- liftIO $ isLegacyHLSBindist workdir-- if- | not forceInstall- , not legacy- , (IsolateDir fp) <- installDir -> liftE $ installDestSanityCheck (IsolateDirResolved fp)- | otherwise -> pure ()-- case installDir of- IsolateDir isoDir -> do- lift $ logInfo $ "isolated installing HLS to " <> T.pack isoDir- if legacy- then liftE $ installHLSUnpackedLegacy workdir (IsolateDirResolved isoDir) ver forceInstall- else liftE $ runBuildAction tmpUnpack $ installHLSUnpacked workdir (IsolateDirResolved isoDir) ver forceInstall-- GHCupInternal -> do- if legacy- then liftE $ installHLSUnpackedLegacy workdir (GHCupBinDir binDir) ver forceInstall- else do- inst <- ghcupHLSDir ver- liftE $ runBuildAction tmpUnpack- $ installHLSUnpacked workdir (GHCupDir inst) ver forceInstall- liftE $ setHLS ver SetHLS_XYZ Nothing---isLegacyHLSBindist :: FilePath -- ^ Path to the unpacked hls bindist- -> IO Bool-isLegacyHLSBindist path = do- not <$> doesFileExist (path </> "GNUmakefile")---- | Install an unpacked hls distribution.-installHLSUnpacked :: ( MonadMask m- , MonadUnliftIO m- , MonadReader env m- , MonadFail m- , HasLog env- , HasDirs env- , HasSettings env- , MonadCatch m- , MonadIO m- , MonadResource m- , HasPlatformReq env- )- => FilePath -- ^ Path to the unpacked hls bindist (where the executable resides)- -> InstallDirResolved -- ^ Path to install to- -> Version- -> Bool- -> Excepts '[ProcessError, CopyError, FileAlreadyExistsError, NotInstalled, MergeFileTreeError] m ()-installHLSUnpacked path inst ver forceInstall = do- PlatformRequest { .. } <- lift getPlatformReq- lift $ logInfo "Installing HLS"- tmpInstallDest <- lift withGHCupTmpDir- lEM $ make ["DESTDIR=" <> fromGHCupPath tmpInstallDest, "PREFIX=" <> fromInstallDir inst, "install"] (Just path)- liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromGHCupPath tmpInstallDest)- liftE $ mergeFileTree (tmpInstallDest `appendGHCupPath` dropDrive (fromInstallDir inst))- inst- HLS- (mkTVer ver)- (\f t -> liftIO $ do- mtime <- ifM (pathIsSymbolicLink f) (pure Nothing) (Just <$> getModificationTime f)- install f t (not forceInstall)- forM_ mtime $ setModificationTime t)---- | Install an unpacked hls distribution (legacy).-installHLSUnpackedLegacy :: (MonadReader env m, MonadFail m, HasLog env, MonadCatch m, MonadIO m)- => FilePath -- ^ Path to the unpacked hls bindist (where the executable resides)- -> InstallDirResolved -- ^ Path to install to- -> Version- -> Bool -- ^ is it a force install- -> Excepts '[CopyError, FileAlreadyExistsError] m ()-installHLSUnpackedLegacy path installDir ver forceInstall = do- lift $ logInfo "Installing HLS"- liftIO $ createDirRecursive' (fromInstallDir installDir)-- -- install haskell-language-server-<ghcver>- bins@(_:_) <- liftIO $ findFiles- path- (makeRegexOpts compExtended- execBlank- ([s|^haskell-language-server-[0-9].*$|] :: ByteString)- )- forM_ bins $ \f -> do- let toF = dropSuffix exeExt f- <> (case installDir of- IsolateDirResolved _ -> ""- _ -> ("~" <>) . T.unpack . prettyVer $ ver- )- <> exeExt-- let srcPath = path </> f- let destPath = fromInstallDir installDir </> toF-- -- destination could be an existing symlink- -- for new make-based HLSes- liftIO $ rmFileForce destPath-- copyFileE- srcPath- destPath- (not forceInstall)- lift $ chmod_755 destPath-- -- install haskell-language-server-wrapper- let wrapper = "haskell-language-server-wrapper"- toF = wrapper- <> (case installDir of- IsolateDirResolved _ -> ""- _ -> ("-" <>) . T.unpack . prettyVer $ ver- )- <> exeExt- srcWrapperPath = path </> wrapper <> exeExt- destWrapperPath = fromInstallDir installDir </> toF-- liftIO $ rmFileForce destWrapperPath- copyFileE- srcWrapperPath- destWrapperPath- (not forceInstall)-- lift $ chmod_755 destWrapperPath------ | Installs hls binaries @haskell-language-server-\<ghcver\>@--- into @~\/.ghcup\/bin/@, as well as @haskell-languager-server-wrapper@.-installHLSBin :: ( MonadMask m- , MonadCatch m- , MonadReader env m- , HasPlatformReq env- , HasGHCupInfo env- , HasDirs env- , HasSettings env- , HasLog env- , MonadResource m- , MonadIO m- , MonadUnliftIO m- , MonadFail m- )- => Version- -> InstallDir- -> Bool -- force install- -> Excepts- '[ AlreadyInstalled- , CopyError- , DigestError- , ContentLengthError- , GPGError- , DownloadFailed- , NoDownload- , NotInstalled- , UnknownArchive- , TarDirDoesNotExist- , ArchiveResult- , FileAlreadyExistsError- , ProcessError- , DirNotEmpty- , UninstallFailed- , MergeFileTreeError- , URIParseError- ]- m- ()-installHLSBin ver installDir forceInstall = do- dlinfo <- liftE $ getDownloadInfo HLS ver- installHLSBindist dlinfo ver installDir forceInstall---compileHLS :: ( MonadMask m- , MonadCatch m- , MonadReader env m- , HasDirs env- , HasSettings env- , HasPlatformReq env- , HasGHCupInfo env- , HasLog env- , MonadResource m- , MonadIO m- , MonadUnliftIO m- , MonadFail m- )- => HLSVer- -> [Version]- -> Maybe Int- -> Maybe [VersionPattern]- -> InstallDir- -> Maybe (Either FilePath URI)- -> Maybe URI- -> Bool- -> Maybe (Either FilePath [URI]) -- ^ patches- -> [Text] -- ^ additional args to cabal install- -> Excepts '[ NoDownload- , GPGError- , DownloadFailed- , DigestError- , ContentLengthError- , UnknownArchive- , TarDirDoesNotExist- , ArchiveResult- , BuildFailed- , NotInstalled- , URIParseError- ] m Version-compileHLS targetHLS ghcs jobs vps installDir cabalProject cabalProjectLocal updateCabal patches cabalArgs = do- pfreq@PlatformRequest { .. } <- lift getPlatformReq- GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo- Dirs { .. } <- lift getDirs-- when updateCabal $ reThrowAll @_ @'[ProcessError] DownloadFailed $ do- lift $ logInfo "Updating cabal DB"- lEM $ exec "cabal" ["update"] (Just $ fromGHCupPath tmpDir) Nothing-- (workdir, tmpUnpack, tver, ov) <- case targetHLS of- -- unpack from version tarball- SourceDist tver -> do- lift $ logDebug $ "Requested to compile: " <> prettyVer tver-- -- download source tarball- dlInfo <-- preview (ix HLS % ix (mkTVer tver) % viSourceDL % _Just) dls- ?? NoDownload (mkTVer tver) HLS (Just pfreq)- dl <- liftE $ downloadCached dlInfo Nothing-- -- unpack- tmpUnpack <- lift mkGhcupTmpDir- liftE $ cleanUpOnError tmpUnpack (unpackToDir (fromGHCupPath tmpUnpack) dl)- liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromGHCupPath tmpUnpack)-- workdir <- maybe (pure tmpUnpack)- (liftE . intoSubdir tmpUnpack)- (view dlSubdir dlInfo)-- ov <- case vps of- Just vps' -> fmap Just $ expandVersionPattern (Just tver) "" "" "" "" vps'- Nothing -> pure Nothing-- pure (workdir, tmpUnpack, tver, ov)-- HackageDist tver -> do- lift $ logDebug $ "Requested to compile (from hackage): " <> prettyVer tver-- -- download source tarball- tmpUnpack <- lift mkGhcupTmpDir- let hls = "haskell-language-server-" <> T.unpack (prettyVer tver)- reThrowAll @_ @'[ProcessError] DownloadFailed $ do- -- unpack- lEM $ exec "cabal" ["unpack", hls] (Just $ fromGHCupPath tmpUnpack) Nothing-- let workdir = appendGHCupPath tmpUnpack hls-- ov <- case vps of- Just vps' -> fmap Just $ expandVersionPattern (Just tver) "" "" "" "" vps'- Nothing -> pure Nothing-- pure (workdir, tmpUnpack, tver, ov)-- RemoteDist uri -> do- lift $ logDebug $ "Requested to compile (from uri): " <> T.pack (show uri)-- -- download source tarball- tmpDownload <- lift withGHCupTmpDir- tmpUnpack <- lift mkGhcupTmpDir- tar <- liftE $ download uri Nothing Nothing Nothing (fromGHCupPath tmpDownload) Nothing False- (cf, tver) <- liftE $ cleanUpOnError tmpUnpack $ do- unpackToDir (fromGHCupPath tmpUnpack) tar- let regex = [s|^(.*/)*haskell-language-server\.cabal$|] :: B.ByteString- [cabalFile] <- liftIO $ findFilesDeep- tmpUnpack- (makeRegexOpts compExtended- execBlank- regex- )- tver <- getCabalVersion (fromGHCupPath tmpUnpack </> cabalFile)- pure (cabalFile, tver)-- let workdir = appendGHCupPath tmpUnpack (takeDirectory cf)-- ov <- case vps of- Just vps' -> fmap Just $ expandVersionPattern (Just tver) "" "" "" "" vps'- Nothing -> pure Nothing-- pure (workdir, tmpUnpack, tver, ov)-- -- clone from git- GitDist GitBranch{..} -> do- tmpUnpack <- lift mkGhcupTmpDir- let git args = execLogged "git" ("--no-pager":args) (Just $ fromGHCupPath tmpUnpack) "git" Nothing- cleanUpOnError tmpUnpack $ reThrowAll @_ @'[ProcessError] DownloadFailed $ do- let rep = fromMaybe "https://github.com/haskell/haskell-language-server.git" repo- lift $ logInfo $ "Fetching git repo " <> T.pack rep <> " at ref " <> T.pack ref <> " (this may take a while)"- lEM $ git [ "init" ]- lEM $ git [ "remote"- , "add"- , "origin"- , fromString rep ]-- -- figure out if we can do a shallow clone- remoteBranches <- catchE @ProcessError @'[ProcessError] @'[] (\_ -> pure [])- $ fmap processBranches $ gitOut ["ls-remote", "--heads", "origin"] (fromGHCupPath tmpUnpack)- let shallow_clone- | gitDescribeRequested = False- | isCommitHash ref = True- | fromString ref `elem` remoteBranches = True- | otherwise = False-- lift $ logDebug $ "Shallow clone: " <> T.pack (show shallow_clone)-- -- fetch- let fetch_args- | shallow_clone = ["fetch", "--depth", "1", "--quiet", "origin", fromString ref]- | otherwise = ["fetch", "--tags", "--quiet", "origin" ]- lEM $ git fetch_args-- -- checkout- lEM $ git [ "checkout", fromString ref ]-- -- gather some info- git_describe <- if shallow_clone- then pure Nothing- else fmap Just $ gitOut ["describe", "--tags"] (fromGHCupPath tmpUnpack)- chash <- gitOut ["rev-parse", "HEAD" ] (fromGHCupPath tmpUnpack)- branch <- gitOut ["rev-parse", "--abbrev-ref", "HEAD" ] (fromGHCupPath tmpUnpack)- tver <- getCabalVersion (fromGHCupPath tmpUnpack </> "haskell-language-server.cabal")-- liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromGHCupPath tmpUnpack)-- ov <- case vps of- Just vps' -> fmap Just $ expandVersionPattern- (Just tver)- (take 7 $ T.unpack chash)- (T.unpack chash)- (maybe "" T.unpack git_describe)- (T.unpack branch)- vps'- Nothing -> pure Nothing-- lift $ logInfo $ "Examining git ref " <> T.pack ref <> "\n " <>- "HLS version (from cabal file): " <> prettyVer tver <>- "\n branch: " <> branch <>- (if not shallow_clone then "\n " <> "'git describe' output: " <> fromJust git_describe else mempty) <>- (if isCommitHash ref then mempty else "\n " <> "commit hash: " <> chash)- pure (tmpUnpack, tmpUnpack, tver, ov)-- -- the version that's installed may differ from the- -- compiled version, so the user can overwrite it- installVer <- maybe (pure tver) pure ov-- liftE $ runBuildAction- tmpUnpack- (reThrowAll @_ @'[GPGError, DownloadFailed, DigestError, ContentLengthError, PatchFailed, ProcessError, FileAlreadyExistsError, CopyError] @'[BuildFailed] (BuildFailed $ fromGHCupPath workdir) $ do- let tmpInstallDir = fromGHCupPath workdir </> "out"- liftIO $ createDirRecursive' tmpInstallDir-- -- apply patches- liftE $ applyAnyPatch patches (fromGHCupPath workdir)-- -- set up project files- cp <- case cabalProject of- Just (Left cp)- | isAbsolute cp -> do- copyFileE cp (fromGHCupPath workdir </> "cabal.project") False- pure "cabal.project"- | otherwise -> pure (takeFileName cp)- Just (Right uri) -> do- tmpUnpack' <- lift withGHCupTmpDir- cp <- liftE $ download uri Nothing Nothing Nothing (fromGHCupPath tmpUnpack') (Just "cabal.project") False- copyFileE cp (fromGHCupPath workdir </> "cabal.project") False- pure "cabal.project"- Nothing- | HackageDist _ <- targetHLS -> do- liftIO $ B.writeFile (fromGHCupPath workdir </> "cabal.project") "packages: ./"- pure "cabal.project"- | RemoteDist _ <- targetHLS -> do- let cabalFile = fromGHCupPath workdir </> "cabal.project"- liftIO $ whenM (not <$> doesFileExist cabalFile) $ B.writeFile cabalFile "packages: ./"- pure "cabal.project"- | otherwise -> pure "cabal.project"- forM_ cabalProjectLocal $ \uri -> do- tmpUnpack' <- lift withGHCupTmpDir- cpl <- liftE $ download uri Nothing Nothing Nothing (fromGHCupPath tmpUnpack') (Just (cp <.> "local")) False- copyFileE cpl (fromGHCupPath workdir </> cp <.> "local") False- artifacts <- forM (sort ghcs) $ \ghc -> do- let ghcInstallDir = tmpInstallDir </> T.unpack (prettyVer ghc)- liftIO $ createDirRecursive' tmpInstallDir- lift $ logInfo $ "Building HLS " <> prettyVer installVer <> " for GHC version " <> prettyVer ghc- liftE $ lEM @_ @'[ProcessError] $- execLogged "cabal" ( [ "v2-install"- , "-w"- , "ghc-" <> T.unpack (prettyVer ghc)- , "--install-method=copy"- ] ++- maybe [] (\j -> ["--jobs=" <> show j]) jobs ++- [ "--overwrite-policy=always"- , "--disable-profiling"- , "--disable-tests"- , "--installdir=" <> ghcInstallDir- , "--project-file=" <> cp- ] ++ fmap T.unpack cabalArgs ++ [- "exe:haskell-language-server"- , "exe:haskell-language-server-wrapper"]- )- (Just $ fromGHCupPath workdir)- "cabal"- Nothing- pure ghcInstallDir-- forM_ artifacts $ \artifact -> do- logDebug $ T.pack (show artifact)- liftIO $ renameFile (artifact </> "haskell-language-server" <.> exeExt)- (tmpInstallDir </> "haskell-language-server-" <> takeFileName artifact <.> exeExt)- liftIO $ renameFile (artifact </> "haskell-language-server-wrapper" <.> exeExt)- (tmpInstallDir </> "haskell-language-server-wrapper" <.> exeExt)-- case installDir of- IsolateDir isoDir -> do- lift $ logInfo $ "isolated installing HLS to " <> T.pack isoDir- liftE $ installHLSUnpackedLegacy tmpInstallDir (IsolateDirResolved isoDir) installVer True- GHCupInternal -> do- liftE $ installHLSUnpackedLegacy tmpInstallDir (GHCupBinDir binDir) installVer True- )-- pure installVer- where- gitDescribeRequested = maybe False (GitDescribe `elem`) vps--- ------------------ --[ Set/Unset ]--- --------------------- | Set the haskell-language-server symlinks.-setHLS :: ( MonadReader env m- , HasDirs env- , HasLog env- , MonadIO m- , MonadMask m- , MonadFail m- , MonadUnliftIO m- )- => Version- -> SetHLS- -> Maybe FilePath -- if set, signals that we're not operating in ~/.ghcup/bin- -- and don't want mess with other versions- -> Excepts '[NotInstalled] m ()-setHLS ver shls mBinDir = do- whenM (lift $ not <$> hlsInstalled ver) (throwE (NotInstalled HLS (GHCTargetVersion Nothing ver)))-- -- symlink destination- binDir <- case mBinDir of- Just x -> pure x- Nothing -> do- Dirs {binDir = f} <- lift getDirs- pure f-- -- first delete the old symlinks- when (isNothing mBinDir) $- case shls of- -- not for legacy- SetHLS_XYZ -> liftE $ rmMinorHLSSymlinks ver- -- legacy and new- SetHLSOnly -> liftE rmPlainHLS-- case shls of- -- not for legacy- SetHLS_XYZ -> do- bins <- lift $ hlsInternalServerScripts ver Nothing-- forM_ bins $ \f -> do- let fname = takeFileName f- destL <- binarySymLinkDestination binDir f- let target = if "haskell-language-server-wrapper" `isPrefixOf` fname- then fname <> "-" <> T.unpack (prettyVer ver) <> exeExt- else fname <> "~" <> T.unpack (prettyVer ver) <> exeExt- lift $ createLink destL (binDir </> target)-- -- legacy and new- SetHLSOnly -> do- -- set haskell-language-server-<ghcver> symlinks- bins <- lift $ hlsServerBinaries ver Nothing- when (null bins) $ throwE $ NotInstalled HLS (GHCTargetVersion Nothing ver)-- forM_ bins $ \f -> do- let destL = f- let target = (<> exeExt) . head . splitOn "~" $ f- lift $ createLink destL (binDir </> target)-- -- set haskell-language-server-wrapper symlink- let destL = "haskell-language-server-wrapper-" <> T.unpack (prettyVer ver) <> exeExt- let wrapper = binDir </> "haskell-language-server-wrapper" <> exeExt-- lift $ createLink destL wrapper-- when (isNothing mBinDir) $- lift warnAboutHlsCompatibility-- liftIO (isShadowed wrapper) >>= \case- Nothing -> pure ()- Just pa -> lift $ logWarn $ T.pack $ prettyHFError (ToolShadowed HLS pa wrapper ver)---unsetHLS :: ( MonadMask m- , MonadReader env m- , HasDirs env- , MonadIO m)- => m ()-unsetHLS = do- Dirs {..} <- getDirs- let wrapper = binDir </> "haskell-language-server-wrapper" <> exeExt- bins <- liftIO $ handleIO (\_ -> pure []) $ findFiles'- binDir- (MP.chunk "haskell-language-server-" <* pvp' <* MP.chunk (T.pack exeExt) <* MP.eof)- forM_ bins (hideError doesNotExistErrorType . rmLink . (binDir </>))- hideError doesNotExistErrorType $ rmLink wrapper----- ---------------- --[ Removal ]--- -------------------- | Delete a hls version. Will try to fix the hls symlinks--- after removal (e.g. setting it to an older version).-rmHLSVer :: ( MonadMask m- , MonadReader env m- , HasDirs env- , MonadThrow m- , HasLog env- , MonadIO m- , MonadFail m- , MonadCatch m- , MonadUnliftIO m- )- => Version- -> Excepts '[NotInstalled, UninstallFailed] m ()-rmHLSVer ver = do- whenM (lift $ fmap not $ hlsInstalled ver) $ throwE (NotInstalled HLS (GHCTargetVersion Nothing ver))-- isHlsSet <- lift hlsSet-- liftE $ rmMinorHLSSymlinks ver-- when (Just ver == isHlsSet) $ do- -- delete all set symlinks- liftE rmPlainHLS-- hlsDir' <- ghcupHLSDir ver- let hlsDir = fromGHCupPath hlsDir'- lift (getInstalledFiles HLS (mkTVer ver)) >>= \case- Just files -> do- lift $ logInfo $ "Removing files safely from: " <> T.pack hlsDir- forM_ files (lift . hideError NoSuchThing . recycleFile . (\f -> hlsDir </> dropDrive f))- removeEmptyDirsRecursive hlsDir- survivors <- liftIO $ hideErrorDef [doesNotExistErrorType] [] $ listDirectory hlsDir- f <- recordedInstallationFile HLS (mkTVer ver)- lift $ recycleFile f- when (not (null survivors)) $ throwE $ UninstallFailed hlsDir survivors- Nothing -> do- isDir <- liftIO $ doesDirectoryExist hlsDir- isSyml <- liftIO $ handleIO (\_ -> pure False) $ pathIsSymbolicLink hlsDir- when (isDir && not isSyml) $ do- lift $ logInfo $ "Removing legacy directory recursively: " <> T.pack hlsDir- recyclePathForcibly hlsDir'-- when (Just ver == isHlsSet) $ do- -- set latest hls- hlsVers <- lift $ fmap rights getInstalledHLSs- case headMay . sortBy (comparing Down) $ hlsVers of- Just latestver -> liftE $ setHLS latestver SetHLSOnly Nothing- Nothing -> pure ()---getCabalVersion :: (MonadIO m, MonadFail m) => FilePath -> m Version-getCabalVersion fp = do- contents <- liftIO $ B.readFile fp- gpd <- case parseGenericPackageDescriptionMaybe contents of- Nothing -> fail $ "could not parse cabal file: " <> fp- Just r -> pure r- let tver = (\c -> Version Nothing c Nothing Nothing)- . Chunks- . NE.fromList- . fmap (Numeric . fromIntegral)- . versionNumbers- . pkgVersion- . package- . packageDescription- $ gpd- pure tver
+ lib/GHCup/Hardcoded/URLs.hs view
@@ -0,0 +1,46 @@+{-# LANGUAGE QuasiQuotes #-}++{-|+Module : GHCup.Hardcoded.URLs+Description : URLs used by GHCup at runtime+Copyright : (c) Julian Ospald, 2020+License : LGPL-3.0+Maintainer : hasufell@hasufell.de+Stability : experimental+Portability : portable+-}+module GHCup.Hardcoded.URLs where++import GHCup.Types++import URI.ByteString+import URI.ByteString.QQ++import qualified Data.Text as T++-- | This reflects the API version of the YAML.+--+-- Note that when updating this, CI requires that the file exists AND the same file exists at+-- 'https://www.haskell.org/ghcup/exp/ghcup-<ver>.yaml' with some newlines added.+-- TODO: revert to master+ghcupURL :: URI+ghcupURL = [uri|https://raw.githubusercontent.com/haskell/ghcup-metadata/master/ghcup-0.1.0.yaml|]++stackSetupURL :: URI+stackSetupURL = [uri|https://raw.githubusercontent.com/commercialhaskell/stackage-content/master/stack/stack-setup-2.yaml|]++shimGenURL :: URI+shimGenURL = [uri|https://downloads.haskell.org/~ghcup/shimgen/shim-2.exe|]++shimGenSHA :: T.Text+shimGenSHA = T.pack "7c55e201f71860c5babea886007c8fa44b861abf50d1c07e5677eb0bda387a70"++channelURL :: ChannelAlias -> URI+channelURL = \case+ DefaultChannel -> ghcupURL+ StackChannel -> stackSetupURL+ CrossChannel -> [uri|https://raw.githubusercontent.com/haskell/ghcup-metadata/master/ghcup-cross-0.1.0.yaml|]+ PrereleasesChannel -> [uri|https://raw.githubusercontent.com/haskell/ghcup-metadata/master/ghcup-prereleases-0.1.0.yaml|]+ VanillaChannel -> [uri|https://raw.githubusercontent.com/haskell/ghcup-metadata/master/ghcup-vanilla-0.1.0.yaml|]+ ThirdPartyChannel -> [uri|https://raw.githubusercontent.com/haskell/ghcup-metadata/master/ghcup-3rdparty-0.1.0.yaml|]+
+ lib/GHCup/Hardcoded/Version.hs view
@@ -0,0 +1,34 @@+{-|+Module : GHCup.Hardcoded.Version+Description : Version information+Copyright : (c) Julian Ospald, 2020+License : LGPL-3.0+Maintainer : hasufell@hasufell.de+Stability : experimental+Portability : portable+-}+module GHCup.Hardcoded.Version where++import GHCup.Prelude.Version+import GHCup.Types+import Paths_ghcup ( version )++import Data.Maybe ( fromJust )+import Data.Version ( Version (versionBranch) )++import qualified Data.List.NonEmpty as NE+import qualified Data.Text as T+import qualified Data.Versions as V+++-- | The current ghcup version.+ghcUpVer :: V.PVP+ghcUpVer = V.PVP . NE.fromList . fmap fromIntegral $ versionBranch version++ghcUpVer' :: TargetVersion+ghcUpVer' = mkTVer $ fromJust $ pvpToVersion ghcUpVer mempty++-- | ghcup version as numeric string.+numericVer :: String+numericVer = T.unpack . V.prettyPVP $ ghcUpVer+
+ lib/GHCup/Input/Parsers.hs view
@@ -0,0 +1,507 @@+{-# OPTIONS_GHC -Wno-orphans #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ViewPatterns #-}++module GHCup.Input.Parsers where+++import GHCup.Command.List+import GHCup.Errors+import GHCup.Prelude+import GHCup.Prelude.Attoparsec as AP+import GHCup.Prelude.MegaParsec as MP+import GHCup.Query.DB+import GHCup.Query.Metadata+import GHCup.Types+import GHCup.Types.Optics+import GHCup.Input.Parsers.URI+import GHCup.Types.JSON++import Control.Applicative ( Alternative (..), (<|>) )+import Control.Monad ( forM, when )+#if !MIN_VERSION_base(4,13,0)+import Control.Monad.Fail ( MonadFail )+#endif+import Control.Monad.Reader+import Data.Aeson+import Data.Bifunctor+import Data.Char as C+import Data.Either+import Data.Functor+import Data.List ( sort )+import Data.Maybe+import Data.Text ( Text )+import Data.Time.Calendar ( Day )+import Data.Time.Format ( defaultTimeLocale, parseTimeM )+import Data.Variant.Excepts+import Data.Versions+import Data.Void+import Optics hiding ( set )+import Prelude hiding ( appendFile )+import Safe+import System.FilePath+import URI.ByteString hiding ( parseURI )+import Text.PrettyPrint.HughesPJClass (prettyShow)++import qualified Data.Attoparsec.ByteString as AP+import qualified Data.ByteString.UTF8 as UTF8+import qualified Data.Text as T+import qualified Data.Text.Lazy as LT+import qualified Data.Text.Lazy.Encoding as LE+import qualified Text.Megaparsec as MP+#if !MIN_VERSION_aeson(2,0,0)+import qualified Data.HashMap.Strict as KM+#endif++-- $setup+-- >>> :set -XOverloadedStrings+-- >>> :set -XDataKinds+-- >>> :set -XTypeApplications+-- >>> :set -XQuasiQuotes+-- >>> import System.Directory+-- >>> import URI.ByteString+-- >>> import qualified Data.Text as T+-- >>> import GHCup.Prelude+-- >>> import GHCup.Download+-- >>> import GHCup.Version+-- >>> import GHCup.Errors+-- >>> import GHCup.Types+-- >>> import GHCup.Query.GHCupDirs+-- >>> import GHCup.Types.Optics+-- >>> import Data.Versions+-- >>> import Optics+-- >>> import GHCup.Prelude.Version.QQ+-- >>> import qualified Data.Text.Encoding as E+-- >>> import qualified Data.Map.Strict as M+-- >>> import Control.Monad.Reader+-- >>> import Data.Variant.Excepts+-- >>> import Text.PrettyPrint.HughesPJClass ( prettyShow )+-- >>> let lc = LoggerConfig { lcPrintDebug = False, consoleOutter = mempty, fileOutter = mempty, fancyColors = False }+-- >>> dirs' <- getAllDirs+-- >>> let installedVersions = [ ([pver|8.10.7|], "-debug+lol", Nothing), ([pver|8.10.4|], "", Nothing), ([pver|8.8.4|], "", Nothing), ([pver|8.8.3|], "", Nothing) ]+-- >>> let settings = defaultSettings { cache = True, metaCache = 0, noNetwork = True }+-- >>> let leanAppState = LeanAppState settings dirs' defaultKeyBindings lc+-- >>> cwd <- getCurrentDirectory+-- >>> (Right ref) <- pure $ GHCup.Utils.parseURI $ "file://" <> E.encodeUtf8 (T.pack cwd) <> "/data/metadata/" <> (urlBaseName . view pathL' $ ghcupURL)+-- >>> (Right ref') <- pure $ GHCup.Utils.parseURI $ "file://" <> E.encodeUtf8 (T.pack cwd) <> "/data/metadata/" <> (urlBaseName . view pathL' $ channelURL PrereleasesChannel)+-- >>> (VRight r) <- (fmap . fmap) _ghcupDownloads $ flip runReaderT leanAppState . runE @'[DigestError, GPGError, JSONError , DownloadFailed , FileDoesNotExistError, ContentLengthError] $ liftE (getBase ref) >>= liftE . decodeMetadata @GHCupInfo+-- >>> (VRight r') <- (fmap . fmap) _ghcupDownloads $ flip runReaderT leanAppState . runE @'[DigestError, GPGError, JSONError , DownloadFailed , FileDoesNotExistError, ContentLengthError] $ liftE (getBase ref') >>= liftE . decodeMetadata @GHCupInfo+-- >>> let rr = M.unionsWith (M.unionWith (\_ b2 -> b2)) [r, r']+-- >>> let go = flip runReaderT leanAppState . fmap (tVerToText . fst)+++ -------------+ --[ Types ]--+ -------------++-- a superset of ToolVersion+data SetToolVersion+ = SetGHCVersion TargetVersionReq+ | SetToolVersion VersionReq+ | SetToolTag Tag+ | SetToolDay Day+ | SetRecommended+ | SetNext+ deriving (Eq, Show)++prettyToolVer :: ToolVersion -> String+prettyToolVer (GHCVersion v') = prettyShow v'+prettyToolVer (ToolVersion v') = prettyShow v'+prettyToolVer (ToolTag t) = show t+prettyToolVer (ToolDay day) = show day++toSetToolVer :: Maybe ToolVersion -> SetToolVersion+toSetToolVer (Just (GHCVersion v')) = SetGHCVersion v'+toSetToolVer (Just (ToolVersion v')) = SetToolVersion v'+toSetToolVer (Just (ToolTag t')) = SetToolTag t'+toSetToolVer (Just (ToolDay d')) = SetToolDay d'+toSetToolVer Nothing = SetRecommended+++platformParser :: String -> Either String PlatformRequest+platformParser s' = case MP.parse (platformP <* MP.eof) "" (T.pack s') of+ Right r -> pure r+ Left e -> Left $ errorBundlePretty e+ where+ archP :: MP.Parsec Void Text Architecture+ archP = choice' ((\x -> MP.chunk (T.pack $ archToString x) $> x) <$> ([minBound..maxBound] :: [Architecture]))+ platformP :: MP.Parsec Void Text PlatformRequest+ platformP = choice'+ [ (`PlatformRequest` FreeBSD)+ <$> (archP <* MP.chunk "-")+ <*> ( MP.chunk "portbld"+ *> ( MP.try (Just <$> verP (MP.chunk "-freebsd" <* MP.eof))+ <|> pure Nothing+ )+ <* MP.chunk "-freebsd"+ )+ , (`PlatformRequest` Darwin)+ <$> (archP <* MP.chunk "-")+ <*> ( MP.chunk "apple"+ *> ( MP.try (Just <$> verP (MP.chunk "-darwin" <* MP.eof))+ <|> pure Nothing+ )+ <* MP.chunk "-darwin"+ )+ , (\a d mv -> PlatformRequest a (Linux d) mv)+ <$> (archP <* MP.chunk "-")+ <*> distroP+ <*> ((MP.try (Just <$> verP (MP.chunk "-linux" <* MP.eof)) <|> pure Nothing+ )+ <* MP.chunk "-linux"+ )+ , (\a -> PlatformRequest a Windows Nothing)+ <$> ((archP <* MP.chunk "-")+ <* (MP.chunk "unknown-mingw32" <|> MP.chunk "unknown-windows" <|> MP.chunk "windows"))+ ]+ distroP :: MP.Parsec Void Text LinuxDistro+ distroP = choice' ((\d -> MP.chunk (T.pack $ distroToString d) $> d) <$> allDistros)+++uriParser :: String -> Either String URI+uriParser = first show . parseURI . UTF8.fromString++filePathParser :: FilePath -> Either String FilePath+filePathParser f =+ if isValid f+ then Right $ normalise f+ else Left "Please enter a valid filepath."++absolutePathParser :: FilePath -> Either String FilePath+absolutePathParser f =+ if isValid f && isAbsolute f+ then Right $ normalise f+ else Left "Please enter a valid absolute filepath."++isolateParser :: FilePath -> Either String FilePath+isolateParser f =+ if isValid f && isAbsolute f+ then Right $ normalise f+ else Left "Please enter a valid filepath for isolate dir."++toolParser :: String -> Either String Tool+toolParser s' = case fmap toLower s' of+ "ghcup" -> Left "'ghcup' is not a valid tool in this context"+ s'' -> Right $ Tool s''++toolParserWithGHCup :: String -> Either String Tool+toolParserWithGHCup s' = case fmap toLower s' of+ s'' -> Right $ Tool s''++installTargetParser :: String -> Either String [String]+installTargetParser = Right . words++toolAndVersionParser :: String -> Either String (Tool, ToolVersion)+toolAndVersionParser (T.pack -> s') = do+ let (tool, toolVersion) = T.breakOn "," s'+ when (toolVersion == "") $ Left "Missing tool version"+ t <- toolParser (T.unpack tool)+ v <- toolVersionTagEither (tail $ T.unpack toolVersion)+ pure (t, v)++-- this accepts cross prefix+ghcVersionTagEither :: String -> Either String ToolVersion+ghcVersionTagEither s' =+ second ToolDay (dayParser s') <|> second ToolTag (tagEither s') <|> second GHCVersion (ghcVersionEither' s')++-- this ignores cross prefix+toolVersionTagEither :: String -> Either String ToolVersion+toolVersionTagEither s' =+ second ToolDay (dayParser s') <|> second ToolTag (tagEither s') <|> second ToolVersion (toolVersionEither' s')++tagEither :: String -> Either String Tag+tagEither s' = case fmap toLower s' of+ "recommended" -> Right Recommended+ "latest" -> Right Latest+ "latest-prerelease" -> Right LatestPrerelease+ "latest-nightly" -> Right LatestNightly+ ('b':'a':'s':'e':'-':ver') -> case pvp (T.pack ver') of+ Right x -> Right (Base x)+ Left _ -> Left $ "Invalid PVP version for base " <> ver'+ other -> Left $ "Unknown tag " <> other+++ghcVersionEither :: String -> Either String TargetVersion+ghcVersionEither str' = do+ v <- first (const "Not a valid version") . MP.parse ghcTargetVerP "" . T.pack $ str'+ if safeVersion v+ then pure v+ else Left "Unsafe version, try something more vanilla like '1.2.3'"++toolVersionEither :: String -> Either String Version+toolVersionEither str' = do+ v <- first (const "Not a valid version") . MP.parse (version' <* MP.eof) "" . T.pack $ str'+ if safeVersion (mkTVer v)+ then pure v+ else Left "Unsafe version, try something more vanilla like '1.2.3'"++ghcVersionEither' :: String -> Either String TargetVersionReq+ghcVersionEither' str' = do+ treq <- first (const "Not a valid version") . MP.parse ghcTargetVerRevP "" . T.pack $ str'+ if safeVersion (_tvqTargetVer treq)+ then pure treq+ else Left "Unsafe version, try something more vanilla like '1.2.3'"++toolVersionEither' :: String -> Either String VersionReq+toolVersionEither' str' = do+ vreq <- first (const "Not a valid version") . MP.parse (verRevP <* MP.eof) "" . T.pack $ str'+ if safeVersion (mkTVer $ _vqVersion vreq)+ then pure vreq+ else Left "Unsafe version, try something more vanilla like '1.2.3'"+++dayParser :: String -> Either String Day+dayParser s = maybe (Left $ "Could not parse \"" <> s <> "\". Expected format is: YYYY-MM-DD") Right+ $ parseTimeM True defaultTimeLocale "%Y-%-m-%-d" s++revisionShowParser :: String -> Either String ShowRevisions+revisionShowParser s' | t == T.pack "updates" = Right ShowUpdates+ | t == T.pack "all" = Right ShowAll+ | t == T.pack "none" = Right ShowNone+ | otherwise = Left ("Unknown criteria: " <> s')+ where t = T.toLower (T.pack s')++criteriaParser :: String -> Either String ListCriteria+criteriaParser s' | t == T.pack "installed" = Right $ ListInstalled True+ | t == T.pack "set" = Right $ ListSet True+ | t == T.pack "available" = Right $ ListAvailable True+ | t == T.pack "+installed" = Right $ ListInstalled True+ | t == T.pack "+set" = Right $ ListSet True+ | t == T.pack "+available" = Right $ ListAvailable True+ | t == T.pack "-installed" = Right $ ListInstalled False+ | t == T.pack "-set" = Right $ ListSet False+ | t == T.pack "-available" = Right $ ListAvailable False+ | otherwise = Left ("Unknown criteria: " <> s')+ where t = T.toLower (T.pack s')++++keepOnParser :: String -> Either String KeepDirs+keepOnParser s' | t == T.pack "always" = Right Always+ | t == T.pack "errors" = Right Errors+ | t == T.pack "never" = Right Never+ | otherwise = Left ("Unknown keep value: " <> s')+ where t = T.toLower (T.pack s')+++downloaderParser :: String -> Either String Downloader+downloaderParser s' | t == T.pack "curl" = Right Curl+ | t == T.pack "wget" = Right Wget+#if defined(INTERNAL_DOWNLOADER)+ | t == T.pack "internal" = Right Internal+#endif+ | otherwise = Left ("Unknown downloader value: " <> s')+ where t = T.toLower (T.pack s')++gpgParser :: String -> Either String GPGSetting+gpgParser s' | t == T.pack "strict" = Right GPGStrict+ | t == T.pack "lax" = Right GPGLax+ | t == T.pack "none" = Right GPGNone+ | otherwise = Left ("Unknown gpg setting value: " <> s')+ where t = T.toLower (T.pack s')++++overWriteVersionParser :: String -> Either String [VersionPattern]+overWriteVersionParser = first (const "Not a valid version pattern") . MP.parse (MP.many versionPattern <* MP.eof) "" . T.pack+ where+ versionPattern :: MP.Parsec Void Text VersionPattern+ versionPattern = do+ str' <- T.unpack <$> MP.takeWhileP Nothing (/= '%')+ if str' /= mempty+ then pure (S str')+ else fmap (const CabalVer) v_cabal+ <|> fmap (const GitBranchName) b_name+ <|> fmap (const GitHashShort) s_hash+ <|> fmap (const GitHashLong) l_hash+ <|> fmap (const GitDescribe) g_desc+ <|> ((\a b -> S (a : T.unpack b)) <$> MP.satisfy (const True) <*> MP.takeWhileP Nothing (== '%')) -- invalid pattern, e.g. "%k"+ where+ v_cabal = MP.chunk "%v"+ b_name = MP.chunk "%b"+ s_hash = MP.chunk "%h"+ l_hash = MP.chunk "%H"+ g_desc = MP.chunk "%g"++ -----------------+ --[ Utilities ]--+ -----------------+++resolveVersion ::+ ( HasLog env+ , MonadFail m+ , MonadReader env m+ , HasGHCupInfo env+ , HasDirs env+ , MonadIOish m+ )+ => Maybe ToolVersion+ -> GuessMode+ -> Tool+ -> Excepts+ '[ TagNotFound+ , DayNotFound+ , NextVerNotFound+ , NoToolVersionSet+ , ParseError+ ] m TargetVersionReq+resolveVersion tv = resolveVersion' (toSetToolVer tv)++resolveVersion' ::+ ( HasLog env+ , MonadReader env m+ , HasGHCupInfo env+ , HasDirs env+ , MonadIOish m+ )+ => SetToolVersion+ -> GuessMode+ -> Tool+ -> Excepts+ '[ TagNotFound+ , DayNotFound+ , NextVerNotFound+ , NoToolVersionSet+ , ParseError+ ] m TargetVersionReq+resolveVersion' SetRecommended _ tool = do+ GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo+ fmap (`TargetVersionReq` Nothing) $ getRecommended dls tool ?? TagNotFound Recommended tool+resolveVersion' (SetGHCVersion (TargetVersionReq v rev)) guessMode tool = do+ GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo+ tv' <- lift $ guessFullVersion dls v tool guessMode+ pure (TargetVersionReq tv' rev)+resolveVersion' (SetToolVersion (toTargetVersionReq' -> treq)) guessMode tool = do+ let TargetVersionReq v rev = treq+ GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo+ tv' <- lift $ guessFullVersion dls v tool guessMode+ pure (TargetVersionReq tv' rev)+resolveVersion' (SetToolTag Latest) _ tool = do+ GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo+ fmap (`TargetVersionReq` Nothing) $ getLatest dls tool ?? TagNotFound Latest tool+resolveVersion' (SetToolDay day) _ tool = do+ GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo+ case getByReleaseDay dls tool day of+ Left ad -> throwE $ DayNotFound day tool ad+ Right (v, _) -> pure (TargetVersionReq v Nothing)+resolveVersion' (SetToolTag LatestPrerelease) _ tool = do+ GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo+ fmap (`TargetVersionReq` Nothing) $ getLatestPrerelease dls tool ?? TagNotFound LatestPrerelease tool+resolveVersion' (SetToolTag LatestNightly) _ tool = do+ GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo+ fmap (`TargetVersionReq` Nothing) $ getLatestNightly dls tool ?? TagNotFound LatestNightly tool+resolveVersion' (SetToolTag Recommended) _ tool = do+ GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo+ fmap (`TargetVersionReq` Nothing) $ getRecommended dls tool ?? TagNotFound Recommended tool+resolveVersion' (SetToolTag (Base pvp'')) _ t+ | t == ghc = do+ GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo+ fmap (`TargetVersionReq` Nothing) $ getLatestBaseVersion dls pvp'' ?? TagNotFound (Base pvp'') ghc+resolveVersion' SetNext _ tool = do+ set <- liftE $ getSetVersion tool Nothing !!? NoToolVersionSet tool Nothing -- TODO+ vers <- lift $ getInstalledVersions tool Nothing+ next <- (headMay+ . tail+ . dropWhile (/= set)+ . cycle+ . sort+ $ vers) ?? NoToolVersionSet tool Nothing+ let tv = mkTVer . _vrVersion $ next+ pure (TargetVersionReq tv Nothing)+resolveVersion' (SetToolTag t') _ tool =+ throwE $ TagNotFound t' tool++-- | Guess the full version from an input version, by possibly+-- examining the metadata and the installed versions.+--+-- >>> go $ guessFullVersion rr (mkTVer [vver|8|]) GHC GLax+-- "8.10.7"+-- >>> go $ guessFullVersion rr (mkTVer [vver|8.10|]) GHC GLax+-- "8.10.7"+-- >>> go $ guessFullVersion rr (mkTVer [vver|8.10.7|]) GHC GLax+-- "8.10.7"+-- >>> go $ guessFullVersion rr (mkTVer [vver|9.12.1|]) GHC GLax+-- "9.12.1"+-- >>> go $ guessFullVersion rr (mkTVer [vver|8|]) GHC GStrict+-- "8"+guessFullVersion :: ( HasLog env+ , MonadReader env m+ , HasDirs env+ , MonadIOish m+ )+ => GHCupDownloads+ -> TargetVersion+ -> Tool+ -> GuessMode+ -> m TargetVersion+guessFullVersion dls v tool guessMode = do+ case pvp $ prettyVer (_tvVersion v) of -- need to be strict here+ Left _ -> pure v+ Right pvpIn+ | (guessMode /= GStrict) && hasn't (_GHCupDownloads % ix tool % toolVersionsL % ix v) dls -> do+ ghcs <- if guessMode == GLaxWithInstalled then getInstalledTools else pure []+ if v `notElem` ghcs+ then getLatestToolFor tool (_tvTarget v) pvpIn dls >>= \case+ Just (pvp_, _vm, mt) -> do+ v' <- pvpToVersion pvp_ ""+ when (v' /= _tvVersion v) $ logWarn ("Assuming you meant version " <> prettyVer v')+ pure $ TargetVersion mt v'+ Nothing -> pure v+ else pure v+ _ -> pure v+ where+ getInstalledTools = fmap _tvrTargetVer <$> getInstalledVersions' tool+++parseUrlSource :: String -> Either String [NewURLSource]+parseUrlSource s = (fromURLSource <$> parseUrlSource' s)+ <|> ((:[]) <$> parseNewUrlSource s)+ <|> parseNewUrlSources s++parseUrlSource' :: String -> Either String URLSource+parseUrlSource' "GHCupURL" = pure GHCupURL+parseUrlSource' "StackSetupURL" = pure StackSetupURL+parseUrlSource' s' = (eitherDecode . LE.encodeUtf8 . LT.pack $ s')+ <|> (fmap (OwnSource . (:[]) . Right) . first show . parseURI . UTF8.fromString $ s')++parseNewUrlSource :: String -> Either String NewURLSource+parseNewUrlSource "GHCupURL" = pure NewGHCupURL+parseNewUrlSource "StackSetupURL" = pure NewStackSetupURL+parseNewUrlSource s' = (fmap NewChannelAlias . parseChannelAlias $ s')+ <|> (eitherDecode . LE.encodeUtf8 . LT.pack $ s')+ <|> (fmap NewURI . first show . parseURI . UTF8.fromString $ s')++parseNewUrlSources :: String -> Either String [NewURLSource]+parseNewUrlSources s = case AP.parseOnly+ (AP.parseList' <* AP.skipSpaces <* AP.endOfInput)+ (UTF8.fromString s) of+ Right bs ->+ forM bs $ \b -> AP.parseOnly (parse <* AP.skipSpaces <* AP.endOfInput) b+ Left e -> Left e+ where+ parse :: AP.Parser NewURLSource+ parse = (NewGHCupURL <$ AP.string "GHCupURL")+ <|> (NewStackSetupURL <$ AP.string "StackSetupURL")+ <|> AP.choice ((\x -> AP.string (UTF8.fromString . T.unpack . channelAliasText $ x) $> NewChannelAlias x) <$> ([minBound..maxBound] :: [ChannelAlias]))+ <|> (NewURI <$> parseURIP)++parseChannelAlias :: String -> Either String ChannelAlias+parseChannelAlias s =+ let aliases = map (\c -> (T.unpack (channelAliasText c), c)) [minBound..maxBound]+ in case lookup s aliases of+ Just c -> Right c+ Nothing -> Left $ "Unexpected ChannelAlias: " <> s++#if MIN_VERSION_transformers(0,6,0)+instance Alternative (Either [a]) where+ empty = Left []+ Left _ <|> n = n+ m <|> _ = m+#endif+
+ lib/GHCup/Input/Parsers/URI.hs view
@@ -0,0 +1,97 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DataKinds #-}++{-|+Module : GHCup.Input.Parsers.URI+Description : GHCup domain specific URI utilities+Copyright : (c) Julian Ospald, 2024+License : LGPL-3.0+Maintainer : hasufell@hasufell.de+Stability : experimental+Portability : portable++This module contains GHCup helpers specific to+URI handling.+-}+module GHCup.Input.Parsers.URI where++import GHCup.Types.Optics+import GHCup.Errors++import Data.Bifunctor (first)+import Data.Text ( Text )+import Control.Applicative+import Data.Attoparsec.ByteString+import Data.ByteString+import URI.ByteString hiding (parseURI)+import System.URI.File+import Data.Variant.Excepts (Excepts, throwE)+import Optics (view, preview, (%), _Just)++import qualified Data.Text.Encoding as E+import qualified Data.Binary.Builder as B+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as L++++ -----------+ --[ URI ]--+ -----------+++parseURI :: ByteString -> Either URIParseError (URIRef Absolute)+parseURI = first OtherError . parseOnly parseURIP++parseURI' :: Text -> Either URIParseError (URIRef Absolute)+parseURI' = first OtherError . parseOnly parseURIP . E.encodeUtf8++parseURIP :: Parser (URIRef Absolute)+parseURIP = do+ ref <- (Right <$> parseFile) <|> (Left <$> uriParser laxURIParserOptions)+ case ref of+ Left (URI { uriScheme = (Scheme "file") }) ->+#if defined(IS_WINDOWS)+ fail "Invalid file URI. File URIs must be absolute (start with a drive letter or UNC path) and not contain backslashes."+#else+ fail "Invalid file URI. File URIs must be absolute."+#endif+ Left o -> pure o+ Right (FileURI (Just _) _) -> fail "File URIs with auth part are not supported!"+ Right (FileURI _ fp) -> pure $ URI (Scheme "file") Nothing fp (Query []) Nothing+ where+ parseFile+#if defined(IS_WINDOWS)+ = fileURIExtendedWindowsP+#else+ = fileURIExtendedPosixP+#endif++-- | Extracts from a URI type: (https?, host, path+query, port)+uriToQuadruple :: Monad m+ => URI+ -> Excepts+ '[UnsupportedScheme]+ m+ (Bool, ByteString, ByteString, Maybe Int)+uriToQuadruple URI {..} = do+ let scheme = view schemeBSL' uriScheme++ host <- maybe (throwE UnsupportedScheme) pure $+ preview (_Just % authorityHostL' % hostBSL') uriAuthority++ https <- if+ | scheme == "https" -> pure True+ | scheme == "http" -> pure False+ | otherwise -> throwE UnsupportedScheme++ let queryBS =+ BS.intercalate "&"+ . fmap (\(x, y) -> encodeQuery x <> "=" <> encodeQuery y)+ $ queryPairs uriQuery+ port =+ preview (_Just % authorityPortL' % _Just % portNumberL') uriAuthority+ fullpath = if BS.null queryBS then uriPath else uriPath <> "?" <> queryBS+ pure (https, host, fullpath, port)+ where encodeQuery = L.toStrict . B.toLazyByteString . urlEncodeQuery
+ lib/GHCup/Input/Prompts.hs view
@@ -0,0 +1,32 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE FlexibleContexts #-}++module GHCup.Input.Prompts+ ( PromptQuestion,+ PromptResponse (..),+ getUserPromptResponse,+ )+where++import Control.Monad.Reader+import qualified Data.Text as T+import qualified Data.Text.IO as TIO+import GHCup.Prelude.Logger+import GHCup.Types.Optics+import GHCup.Types (PromptQuestion, PromptResponse(..))++getUserPromptResponse :: ( HasLog env+ , MonadReader env m+ , MonadIO m)+ => PromptQuestion+ -> PromptResponse -- ^ Default response to use if user doesn't type explicit response (e.g. just presses Return)+ -> m PromptResponse+getUserPromptResponse prompt defaultResponse = do+ logInfo prompt+ resp <- liftIO TIO.getLine+ pure $+ if T.null resp+ then defaultResponse+ else if resp `elem` ["YES", "yes", "y", "Y"]+ then PromptYes+ else PromptNo
+ lib/GHCup/Input/SymlinkSpec.hs view
@@ -0,0 +1,91 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}++module GHCup.Input.SymlinkSpec where++import GHCup.Errors+import GHCup.Query.GHCupDirs+import GHCup.Types+import GHCup.Types.Optics+import GHCup.Prelude.Logger++import Control.Applicative ( Alternative (many), (<|>) )+import Control.Monad ( forM )+import Control.Monad.IO.Class ( liftIO )+import Control.Monad.Reader ( MonadReader )+import Data.Variant.Excepts ( Excepts, throwE )+import Data.Versions ( Version, prettyVer )+import Data.Void ( Void )+import System.FilePath ( takeFileName )+import System.FilePattern.Directory ( getDirectoryFilesIgnore )++import qualified Data.Text as T+import qualified Text.Megaparsec as MP+++parseSymlinkSpec ::+ Monad m+ => Version+ -> SymlinkSpec String+ -> Excepts+ '[ParseError]+ m+ (SymlinkSpec [Either Char Version])+parseSymlinkSpec ver SymlinkSpec{..} = do+ target <- either (throwE . ParseError . show) pure $ parseDomain _slTarget+ linkName <- either (throwE . ParseError . show) pure $ parseDomain _slLinkName+ setName <- forM _slSetName $ either (throwE . ParseError . show) pure . parseDomain+ pure $ SymlinkSpec target linkName _slPVPMajorLinks setName+ where+ parseDomain = MP.parse domainParser ""++ domainParser :: MP.Parsec Void String [Either Char Version]+ domainParser = many anyOrKnownVars+ where+ -- TODO: make this more efficient+ anyOrKnownVars :: MP.Parsec Void String (Either Char Version)+ anyOrKnownVars = MP.try (fmap (const (Right ver)) (MP.chunk "${PKGVER}"))+ <|> fmap Left MP.anySingle++substituteSpec :: SymlinkSpec [Either Char Version] -> SymlinkFileSpec+substituteSpec SymlinkSpec{..} =+ let target = mconcat $ either (:[]) (T.unpack . prettyVer) <$> _slTarget+ linkName = mconcat $ either (:[]) (T.unpack . prettyVer) <$> _slLinkName+ setName = mconcat . fmap (either (:[]) (T.unpack . prettyVer)) <$> _slSetName+ in SymlinkSpec target linkName _slPVPMajorLinks setName+++resolveSymlinkSpec ::+ ( MonadReader env m+ , HasLog env+ , MonadIOish m+ )+ => GHCupPath+ -> SymlinkInputSpec+ -> Excepts+ '[ParseError]+ m+ [SymlinkSpec String]+resolveSymlinkSpec _ SymlinkInputSpec{..} = pure [SymlinkSpec{..}]+resolveSymlinkSpec (fromGHCupPath -> workDir) SymlinkPatternSpec{..} = do+ binaries <- liftIO $ getDirectoryFilesIgnore workDir _slTargetPattern _slTargetPatternIgnore+ logDebug2 $ "Resolved binaries found in " <> T.pack workDir <> ": " <> T.pack (show binaries)+ forM binaries $ \binary -> do+ let _slTarget = binary+ _slLinkName <- parseDomain (takeFileName binary) _slLinkName+ logDebug2 $ "Parsed slLinkName: " <> T.pack _slLinkName+ _slSetName <- forM _slSetName $ parseDomain (takeFileName binary)+ logDebug2 $ "Parsed slSetName: " <> T.pack (show _slSetName)+ pure SymlinkSpec{..}+ where+ parseDomain targetFn = either (throwE . ParseError . show) pure . MP.parse (domainParser targetFn) ""++ domainParser :: FilePath -> MP.Parsec Void String String+ domainParser targetFn = concat <$> many anyOrKnownVars+ where+ -- TODO: make this more efficient+ anyOrKnownVars :: MP.Parsec Void String String+ anyOrKnownVars = MP.try (fmap (const targetFn) (MP.chunk "${TARGETFN}"))+ <|> fmap (:[]) MP.anySingle+
+ lib/GHCup/Legacy/Cabal.hs view
@@ -0,0 +1,132 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}++{-|+Module : GHCup.Legacy.Cabal+Description : GHCup legacy installation functions for Cabal+Copyright : (c) Julian Ospald, 2020+License : LGPL-3.0+Maintainer : hasufell@hasufell.de+Stability : experimental+Portability : portable+-}+module GHCup.Legacy.Cabal where++import GHCup.Errors+import GHCup.Legacy.Utils+import GHCup.Prelude+import GHCup.Types+import GHCup.System.Directory+import GHCup.Types.Optics++import Control.Applicative+import Control.Exception.Safe+import Control.Monad+#if !MIN_VERSION_base(4,13,0)+import Control.Monad.Fail ( MonadFail )+#endif+import Control.Monad.Reader+import Control.Monad.Trans.Resource hiding ( throwM )+import Data.Either+import Data.List+import Data.Maybe+import Data.Ord+import Data.Variant.Excepts+import Data.Versions hiding ( patch )+import Prelude hiding ( abs, writeFile )+import Safe hiding ( at )+import System.FilePath+import System.IO.Error++import qualified Data.Text as T+++++ -----------------+ --[ Set cabal ]--+ -----------------+++-- | Set the @~\/.ghcup\/bin\/cabal@ symlink.+setCabal :: ( MonadMask m+ , MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadFail m+ , MonadIO m+ , MonadUnliftIO m)+ => Version+ -> Maybe FilePath+ -> Excepts '[NotInstalled] m ()+setCabal ver mTmpDir = do+ let targetFile = "cabal-" <> T.unpack (prettyVer ver) <> exeExt++ -- symlink destination+ Dirs {..} <- lift getDirs++ whenM (liftIO $ not <$> doesFileExist (binDir </> targetFile))+ $ throwE+ $ NotInstalled cabal (TargetVersion Nothing ver)++ let cabalbin = fromMaybe binDir mTmpDir </> "cabal" <> exeExt++ -- create link+ let destL = targetFile+ lift $ createLink destL cabalbin++ liftIO (isShadowed cabalbin) >>= \case+ Nothing -> pure ()+ Just pa -> lift $ logWarn $ T.pack $ prettyHFError (ToolShadowed cabal ver [(pa, cabalbin)])++ pure ()++unsetCabal :: ( MonadMask m+ , MonadReader env m+ , HasDirs env+ , MonadIO m)+ => m ()+unsetCabal = do+ Dirs {..} <- getDirs+ let cabalbin = binDir </> "cabal" <> exeExt+ hideError doesNotExistErrorType $ rmLink cabalbin+++ ----------------+ --[ Rm cabal ]--+ ----------------+++-- | Delete a cabal version. Will try to fix the @cabal@ symlink+-- after removal (e.g. setting it to an older version).+rmCabalVer :: ( MonadMask m+ , MonadReader env m+ , HasDirs env+ , MonadThrow m+ , HasLog env+ , MonadIO m+ , MonadFail m+ , MonadCatch m+ , MonadUnliftIO m+ )+ => Version+ -> Excepts '[NotInstalled] m ()+rmCabalVer ver = do+ whenM (lift $ fmap not $ cabalInstalled ver) $ throwE (NotInstalled cabal (TargetVersion Nothing ver))++ cSet <- lift cabalSet++ Dirs {..} <- lift getDirs++ let cabalFile = "cabal-" <> T.unpack (prettyVer ver) <> exeExt+ lift $ hideError doesNotExistErrorType $ recycleFile (binDir </> cabalFile)++ when (Just ver == cSet) $ do+ cVers <- lift $ fmap rights getInstalledCabals+ case headMay . sortBy (comparing Down) $ cVers of+ Just latestver -> setCabal latestver Nothing+ Nothing -> lift $ rmLink (binDir </> "cabal" <> exeExt)
+ lib/GHCup/Legacy/HLS.hs view
@@ -0,0 +1,277 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}++{-|+Module : GHCup.Legacy.HLS+Description : GHCup installation functions for legacy HLS+Copyright : (c) Julian Ospald, 2020+License : LGPL-3.0+Maintainer : hasufell@hasufell.de+Stability : experimental+Portability : portable+-}+module GHCup.Legacy.HLS where++import GHCup.Errors+import GHCup.Legacy.Utils+import GHCup.Prelude+import GHCup.Prelude.String.QQ+import GHCup.Query.DB+import GHCup.Query.GHCupDirs+import GHCup.System.Directory+import GHCup.Types+import GHCup.Types.Optics++import Control.Applicative+import Control.Exception.Safe+import Control.Monad+#if !MIN_VERSION_base(4,13,0)+import Control.Monad.Fail ( MonadFail )+#endif+import Control.Monad.Reader+import Control.Monad.Trans.Resource hiding ( throwM )+import Data.ByteString ( ByteString )+import Data.Either+import Data.List+import Data.Maybe+import Data.Ord+import Data.Variant.Excepts+import Data.Versions hiding ( patch )+import GHC.IO.Exception+import Prelude hiding ( abs, writeFile )+import Safe hiding ( at )+import System.FilePath+import System.IO.Error+import Text.Regex.Posix++import qualified Data.Text as T+import qualified Text.Megaparsec as MP++++ --------------------+ --[ Installation ]--+ --------------------++++-- | Install an unpacked hls distribution (legacy).+installHLSUnpackedLegacy :: (MonadReader env m, MonadFail m, HasLog env, MonadCatch m, MonadIO m)+ => FilePath -- ^ Path to the unpacked hls bindist (where the executable resides)+ -> InstallDirResolved -- ^ Path to install to+ -> Version+ -> Bool -- ^ is it a force install+ -> Excepts '[CopyError, FileAlreadyExistsError] m ()+installHLSUnpackedLegacy path installDir ver forceInstall = do+ lift $ logInfo "Installing HLS"+ liftIO $ createDirRecursive' (fromInstallDir installDir)++ lift $ logInfo $ "Install binaries from " <> T.pack path+ -- install haskell-language-server-<ghcver>+ bins@(_:_) <- liftIO $ findFiles+ path+ (makeRegexOpts compExtended+ execBlank+ ([s|^haskell-language-server-[0-9].*$|] :: ByteString)+ )++ lift $ logInfo $ "Found binaries " <> T.pack (show bins)+ forM_ bins $ \f -> do+ let toF = dropSuffix exeExt f+ <> (case installDir of+ IsolateDirResolved _ -> ""+ _ -> ("~" <>) . T.unpack . prettyVer $ ver+ )+ <> exeExt++ let srcPath = path </> f+ let destPath = fromInstallDir installDir </> toF++ -- destination could be an existing symlink+ -- for new make-based HLSes+ liftIO $ rmFileForce destPath++ copyFileE+ srcPath+ destPath+ (not forceInstall)+ lift $ chmod_755 destPath++ -- install haskell-language-server-wrapper+ let wrapper = "haskell-language-server-wrapper"+ toF = wrapper+ <> (case installDir of+ IsolateDirResolved _ -> ""+ _ -> ("-" <>) . T.unpack . prettyVer $ ver+ )+ <> exeExt+ srcWrapperPath = path </> wrapper <> exeExt+ destWrapperPath = fromInstallDir installDir </> toF++ liftIO $ rmFileForce destWrapperPath+ copyFileE+ srcWrapperPath+ destWrapperPath+ (not forceInstall)++ lift $ chmod_755 destWrapperPath++++-- | Installs hls binaries @haskell-language-server-\<ghcver\>@+-- into @~\/.ghcup\/bin/@, as well as @haskell-languager-server-wrapper@.++++ -----------------+ --[ Set/Unset ]--+ -----------------++-- | Set the haskell-language-server symlinks.+setHLS :: ( MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadIO m+ , MonadMask m+ , MonadFail m+ , MonadUnliftIO m+ )+ => Version+ -> SetHLS+ -> Maybe FilePath -- if set, signals that we're not operating in ~/.ghcup/bin+ -- and don't want mess with other versions+ -> Excepts '[NotInstalled] m ()+setHLS ver shls mBinDir = do+ whenM (lift $ not <$> hlsInstalled ver) (throwE (NotInstalled hls (TargetVersion Nothing ver)))++ -- symlink destination+ binDir <- case mBinDir of+ Just x -> pure x+ Nothing -> do+ Dirs {binDir = f} <- lift getDirs+ pure f++ -- first delete the old symlinks+ when (isNothing mBinDir) $+ case shls of+ -- not for legacy+ SetHLS_XYZ -> liftE $ rmMinorHLSSymlinks ver+ -- legacy and new+ SetHLSOnly -> liftE rmPlainHLS++ case shls of+ -- not for legacy+ SetHLS_XYZ -> do+ bins <- lift $ hlsInternalServerScripts ver Nothing++ forM_ bins $ \f -> do+ let fname = takeFileName f+ destL <- binarySymLinkDestination binDir f+ let target = if "haskell-language-server-wrapper" `isPrefixOf` fname+ then fname <> "-" <> T.unpack (prettyVer ver) <> exeExt+ else fname <> "~" <> T.unpack (prettyVer ver) <> exeExt+ lift $ createLink destL (binDir </> target)++ -- legacy and new+ SetHLSOnly -> do+ -- set haskell-language-server-<ghcver> symlinks+ bins <- lift $ hlsServerBinaries ver Nothing+ when (null bins) $ throwE $ NotInstalled hls (TargetVersion Nothing ver)++ forM_ bins $ \f -> do+ let destL = f+ let target = (<> exeExt) . head . splitOn "~" $ f+ lift $ createLink destL (binDir </> target)++ -- set haskell-language-server-wrapper symlink+ let destL = "haskell-language-server-wrapper-" <> T.unpack (prettyVer ver) <> exeExt+ let wrapper = binDir </> "haskell-language-server-wrapper" <> exeExt++ lift $ createLink destL wrapper++ when (isNothing mBinDir) $+ lift warnAboutHlsCompatibility++ liftIO (isShadowed wrapper) >>= \case+ Nothing -> pure ()+ Just pa -> lift $ logWarn $ T.pack $ prettyHFError (ToolShadowed hls ver [(pa, wrapper)])+++unsetHLS :: ( MonadMask m+ , MonadReader env m+ , HasDirs env+ , MonadIO m)+ => m ()+unsetHLS = do+ Dirs {..} <- getDirs+ let wrapper = binDir </> "haskell-language-server-wrapper" <> exeExt+ bins <- liftIO $ handleIO (\_ -> pure []) $ findFiles'+ binDir+ (MP.chunk "haskell-language-server-" <* pvp' <* MP.chunk (T.pack exeExt) <* MP.eof)+ forM_ bins (hideError doesNotExistErrorType . rmLink . (binDir </>))+ hideError doesNotExistErrorType $ rmLink wrapper+++++ ---------------+ --[ Removal ]--+ ---------------+++-- | Delete a hls version. Will try to fix the hls symlinks+-- after removal (e.g. setting it to an older version).+rmHLSVer :: ( MonadMask m+ , MonadReader env m+ , HasDirs env+ , MonadThrow m+ , HasLog env+ , MonadIO m+ , MonadFail m+ , MonadCatch m+ , MonadUnliftIO m+ )+ => Version+ -> Excepts '[NotInstalled, UninstallFailed] m ()+rmHLSVer ver = do+ whenM (lift $ fmap not $ hlsInstalled ver) $ throwE (NotInstalled hls (TargetVersion Nothing ver))++ isHlsSet <- lift hlsSet++ liftE $ rmMinorHLSSymlinks ver++ when (Just ver == isHlsSet) $ do+ -- delete all set symlinks+ liftE rmPlainHLS++ hlsDir' <- ghcupHLSDir ver+ let hlsDir = fromGHCupPath hlsDir'+ lift (getInstalledFiles hls (mkTVer ver)) >>= \case+ Just files -> do+ lift $ logInfo $ "Removing files safely from: " <> T.pack hlsDir+ forM_ files (lift . hideError NoSuchThing . recycleFile . (\f -> hlsDir </> dropDrive f))+ removeEmptyDirsRecursive hlsDir+ survivors <- liftIO $ hideErrorDef [doesNotExistErrorType] [] $ listDirectory hlsDir+ f <- recordedInstallationFile hls (mkTVer ver)+ lift $ recycleFile f+ when (not (null survivors)) $ throwE $ UninstallFailed hlsDir survivors+ Nothing -> do+ isDir <- liftIO $ doesDirectoryExist hlsDir+ isSyml <- liftIO $ handleIO (\_ -> pure False) $ pathIsSymbolicLink hlsDir+ when (isDir && not isSyml) $ do+ lift $ logInfo $ "Removing legacy directory recursively: " <> T.pack hlsDir+ recyclePathForcibly hlsDir'++ when (Just ver == isHlsSet) $ do+ -- set latest hls+ hlsVers <- lift $ fmap rights getInstalledHLSs+ case headMay . sortBy (comparing Down) $ hlsVers of+ Just latestver -> liftE $ setHLS latestver SetHLSOnly Nothing+ Nothing -> pure ()++
+ lib/GHCup/Legacy/Stack.hs view
@@ -0,0 +1,131 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}++{-|+Module : GHCup.Legacy.Stack+Description : GHCup legacy installation functions for Stack+Copyright : (c) Julian Ospald, 2020+License : LGPL-3.0+Maintainer : hasufell@hasufell.de+Stability : experimental+Portability : portable+-}+module GHCup.Legacy.Stack where++import GHCup.Errors+import GHCup.Legacy.Utils+import GHCup.Prelude+import GHCup.System.Directory+import GHCup.Types+import GHCup.Types.Optics++import Control.Applicative+import Control.Exception.Safe+import Control.Monad+#if !MIN_VERSION_base(4,13,0)+import Control.Monad.Fail ( MonadFail )+#endif+import Control.Monad.Reader+import Control.Monad.Trans.Resource hiding ( throwM )+import Data.Either+import Data.List+import Data.Maybe+import Data.Ord+import Data.Variant.Excepts+import Data.Versions hiding ( patch )+import Prelude hiding ( abs, writeFile )+import Safe hiding ( at )+import System.FilePath+import System.IO.Error++import qualified Data.Text as T++++ -----------------+ --[ Set stack ]--+ -----------------+++-- | Set the @~\/.ghcup\/bin\/stack@ symlink.+setStack :: ( MonadMask m+ , MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadThrow m+ , MonadFail m+ , MonadIO m+ , MonadUnliftIO m+ )+ => Version+ -> Maybe FilePath+ -> Excepts '[NotInstalled] m ()+setStack ver mTmpDir = do+ let targetFile = "stack-" <> T.unpack (prettyVer ver) <> exeExt++ -- symlink destination+ Dirs {..} <- lift getDirs++ whenM (liftIO $ not <$> doesFileExist (binDir </> targetFile))+ $ throwE+ $ NotInstalled stack (TargetVersion Nothing ver)++ let stackbin = fromMaybe binDir mTmpDir </> "stack" <> exeExt++ lift $ createLink targetFile stackbin++ liftIO (isShadowed stackbin) >>= \case+ Nothing -> pure ()+ Just pa -> lift $ logWarn $ T.pack $ prettyHFError (ToolShadowed stack ver [(pa, stackbin)])++ pure ()+++unsetStack :: ( MonadMask m+ , MonadReader env m+ , HasDirs env+ , MonadIO m)+ => m ()+unsetStack = do+ Dirs {..} <- getDirs+ let stackbin = binDir </> "stack" <> exeExt+ hideError doesNotExistErrorType $ rmLink stackbin+++ ----------------+ --[ Rm stack ]--+ ----------------++-- | Delete a stack version. Will try to fix the @stack@ symlink+-- after removal (e.g. setting it to an older version).+rmStackVer :: ( MonadMask m+ , MonadReader env m+ , HasDirs env+ , MonadThrow m+ , HasLog env+ , MonadIO m+ , MonadFail m+ , MonadCatch m+ , MonadUnliftIO m+ )+ => Version+ -> Excepts '[NotInstalled] m ()+rmStackVer ver = do+ whenM (lift $ fmap not $ stackInstalled ver) $ throwE (NotInstalled stack (TargetVersion Nothing ver))++ sSet <- lift stackSet++ Dirs {..} <- lift getDirs++ let stackFile = "stack-" <> T.unpack (prettyVer ver) <> exeExt+ lift $ hideError doesNotExistErrorType $ recycleFile (binDir </> stackFile)++ when (Just ver == sSet) $ do+ sVers <- lift $ fmap rights getInstalledStacks+ case headMay . sortBy (comparing Down) $ sVers of+ Just latestver -> setStack latestver Nothing+ Nothing -> lift $ rmLink (binDir </> "stack" <> exeExt)
+ lib/GHCup/Legacy/Utils.hs view
@@ -0,0 +1,700 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE ViewPatterns #-}++{-|+Module : GHCup.Legacy.Utils+Description : GHCup domain specific legacy utilities+Copyright : (c) Julian Ospald, 2020+License : LGPL-3.0+Maintainer : hasufell@hasufell.de+Stability : experimental+Portability : portable++This module contains GHCup helpers specific to+installation and introspection of files/versions etc.+-}+module GHCup.Legacy.Utils where+++#if defined(IS_WINDOWS)+import GHCup.Prelude.Windows+#else+import GHCup.Prelude.Posix+#endif+import Control.Applicative+import Control.Exception.Safe+import Control.Monad+import GHCup.Errors+import GHCup.Prelude+import GHCup.Prelude.MegaParsec+import GHCup.Prelude.String.QQ+import GHCup.Query.GHCupDirs+import GHCup.System.Directory+import GHCup.Types+import GHCup.Types.JSON+ (safeVersion, cabalBadNames)+import GHCup.Types.Optics+import qualified GHCup.Warnings as Warnings++#if !MIN_VERSION_base(4,13,0)+import Control.Monad.Fail ( MonadFail )+#endif+import Control.Monad.Reader+import Data.ByteString ( ByteString )+import Data.Either+import Data.List+ ( groupBy, isInfixOf, isPrefixOf, isSuffixOf, nub, sortBy, stripPrefix )+import Data.Maybe+import Data.Text ( Text )+import Data.Variant.Excepts+import Data.Versions hiding ( patch )+import GHC.IO.Exception+import Safe+import System.FilePath+import System.IO.Error+import Text.Regex.Posix++import qualified Data.Text as T+import qualified Data.Text.Encoding as E+import qualified Text.Megaparsec as MP+++++ ------------------------+ --[ Symlink handling ]--+ ------------------------+++-- | Create a relative symlink destination for the binary directory,+-- given a target toolpath.+binarySymLinkDestination :: ( MonadThrow m+ , MonadIO m+ )+ => FilePath -- ^ binary dir+ -> FilePath -- ^ the full toolpath+ -> m FilePath+binarySymLinkDestination binDir toolPath = do+ toolPath' <- liftIO $ canonicalizePath toolPath+ binDir' <- liftIO $ canonicalizePath binDir+ pure (relativeSymlink binDir' toolPath')+++-- | Removes the minor GHC symlinks, e.g. ghc-8.6.5.+rmMinorGHCSymlinks :: ( MonadReader env m+ , HasDirs env+ , MonadIO m+ , HasLog env+ , MonadThrow m+ , MonadFail m+ , MonadMask m+ )+ => TargetVersion+ -> Excepts '[NotInstalled] m ()+rmMinorGHCSymlinks tv@TargetVersion{..} = do+ Dirs {..} <- lift getDirs++ files <- liftE $ ghcToolFiles tv+ forM_ files $ \f -> do+ let f_xyz = f <> "-" <> T.unpack (prettyVer _tvVersion) <> exeExt+ let fullF = binDir </> f_xyz+ lift $ logDebug ("rm -f " <> T.pack fullF)+ lift $ hideError doesNotExistErrorType $ rmLink fullF+++-- | Removes the set ghc version for the given target, if any.+rmPlainGHC :: ( MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadThrow m+ , MonadFail m+ , MonadIO m+ , MonadMask m+ )+ => Maybe Text -- ^ target+ -> Excepts '[NotInstalled] m ()+rmPlainGHC target = do+ Dirs {..} <- lift getDirs+ mtv <- lift $ ghcSet target+ forM_ mtv $ \tv -> do+ files <- liftE $ ghcToolFiles tv+ forM_ files $ \f -> do+ let fullF = binDir </> f <> exeExt+ lift $ logDebug ("rm -f " <> T.pack fullF)+ lift $ hideError doesNotExistErrorType $ rmLink fullF+ -- old ghcup+ let hdc_file = binDir </> "haddock-ghc" <> exeExt+ lift $ logDebug ("rm -f " <> T.pack hdc_file)+ lift $ hideError doesNotExistErrorType $ rmLink hdc_file+++-- | Remove the major GHC symlink, e.g. ghc-8.6.+rmMajorGHCSymlinks :: ( MonadReader env m+ , HasDirs env+ , MonadIO m+ , HasLog env+ , MonadThrow m+ , MonadFail m+ , MonadMask m+ )+ => TargetVersion+ -> Excepts '[NotInstalled] m ()+rmMajorGHCSymlinks tv@TargetVersion{..} = do+ Dirs {..} <- lift getDirs+ (mj, mi) <- getMajorMinorV _tvVersion+ let v' = intToText mj <> "." <> intToText mi++ files <- liftE $ ghcToolFiles tv+ forM_ files $ \f -> do+ let f_xy = f <> "-" <> T.unpack v' <> exeExt+ let fullF = binDir </> f_xy+ lift $ logDebug ("rm -f " <> T.pack fullF)+ lift $ hideError doesNotExistErrorType $ rmLink fullF++++-- | Removes the minor HLS files, e.g. 'haskell-language-server-8.10.7~1.6.1.0'+-- and 'haskell-language-server-wrapper-1.6.1.0'.+rmMinorHLSSymlinks :: ( MonadReader env m+ , HasDirs env+ , MonadIO m+ , HasLog env+ , MonadThrow m+ , MonadFail m+ , MonadMask m+ )+ => Version+ -> Excepts '[NotInstalled] m ()+rmMinorHLSSymlinks ver = do+ Dirs {..} <- lift getDirs++ hlsBins <- hlsAllBinaries ver+ forM_ hlsBins $ \f -> do+ let fullF = binDir </> f+ lift $ logDebug ("rm -f " <> T.pack fullF)+ -- on unix, this may be either a file (legacy) or a symlink+ -- on windows, this is always a file... hence 'rmFile'+ -- works consistently across platforms+ lift $ rmFile fullF++-- | Removes the set HLS version, if any.+rmPlainHLS :: ( MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadThrow m+ , MonadFail m+ , MonadIO m+ , MonadMask m+ )+ => Excepts '[NotInstalled] m ()+rmPlainHLS = do+ Dirs {..} <- lift getDirs++ -- delete 'haskell-language-server-8.10.7'+ hlsBins <- fmap (filter (\f -> not ("haskell-language-server-wrapper" `isPrefixOf` f) && ('~' `notElem` f)))+ $ liftIO $ handleIO (\_ -> pure []) $ findFiles+ binDir+ (makeRegexOpts compExtended execBlank ([s|^haskell-language-server-.*$|] :: ByteString))+ forM_ hlsBins $ \f -> do+ let fullF = binDir </> f+ lift $ logDebug ("rm -f " <> T.pack fullF)+ if isWindows+ then lift $ rmLink fullF+ else lift $ rmFile fullF++ -- 'haskell-language-server-wrapper'+ let hlswrapper = binDir </> "haskell-language-server-wrapper" <> exeExt+ lift $ logDebug ("rm -f " <> T.pack hlswrapper)+ if isWindows+ then lift $ hideError doesNotExistErrorType $ rmLink hlswrapper+ else lift $ hideError doesNotExistErrorType $ rmFile hlswrapper++++ -----------------------------------+ --[ Set/Installed introspection ]--+ -----------------------------------+++-- | Whether the given GHC version is installed.+ghcInstalled :: (MonadIO m, MonadReader env m, HasDirs env, MonadThrow m) => TargetVersion -> m Bool+ghcInstalled ver = do+ ghcdir <- ghcupGHCDir ver+ liftIO $ doesDirectoryExist (fromGHCupPath ghcdir)+++-- | Whether the given GHC version is set as the current.+ghcSet :: (MonadReader env m, HasDirs env, MonadThrow m, MonadIO m)+ => Maybe Text -- ^ the target of the GHC version, if any+ -- (e.g. armv7-unknown-linux-gnueabihf)+ -> m (Maybe TargetVersion)+ghcSet mtarget = do+ Dirs {..} <- getDirs+ let ghc' = maybe "ghc" (\t -> T.unpack t <> "-ghc") mtarget+ let ghcBin = binDir </> ghc' <> exeExt++ -- link destination is of the form ../ghc/<ver>/bin/ghc+ -- for old ghcup, it is ../ghc/<ver>/bin/ghc-<ver>+ liftIO $ handleIO' NoSuchThing (\_ -> pure Nothing) $ do+ link <- liftIO $ getLinkTarget ghcBin+ Just <$> ghcLinkVersion' link+ where+ ghcLinkVersion' :: MonadThrow m => FilePath -> m TargetVersion+ ghcLinkVersion' (T.pack . dropSuffix exeExt -> t) = throwEither $+ MP.parse (MP.try ghcVersionFromPath <|> ghcLinkVersion) "ghcLinkVersion" t++-- | Get all installed GHCs by reading ~/.ghcup/ghc/<dir>.+-- If a dir cannot be parsed, returns left.+getInstalledGHCs :: (MonadReader env m, HasDirs env, MonadIO m) => m [Either FilePath TargetVersion]+getInstalledGHCs = filter (either (const True) safeVersion) <$> do+ ghcdir <- ghcupGHCBaseDir+ fs <- liftIO $ hideErrorDef [NoSuchThing] [] $ listDirectoryDirs (fromGHCupPath ghcdir)+ forM fs $ \f -> case parseGHCupGHCDir f of+ Right r -> pure $ Right r+ Left _ -> pure $ Left f+++-- | Get all installed cabals, by matching on @~\/.ghcup\/bin/cabal-*@.+getInstalledCabals :: ( MonadReader env m+ , HasDirs env+ , MonadIO m+ , MonadCatch m+ )+ => m [Either FilePath Version]+getInstalledCabals = filter (either (const True) (safeVersion . mkTVer)) <$> do+ Dirs {..} <- getDirs+ bins <- liftIO $ handleIO (\_ -> pure []) $ findFiles+ binDir+ (makeRegexOpts compExtended execBlank ([s|^cabal-.*$|] :: ByteString))+ vs <- forM bins $ \f -> case version . T.pack <$> (stripSuffix exeExt =<< stripPrefix "cabal-" f) of+ Just (Right r)+ | T.unpack (prettyVer r) `notPrefixElem` cabalBadNames -> pure $ Right r+ | otherwise -> pure $ Left f+ Just (Left _) -> pure $ Left f+ Nothing -> pure $ Left f+ pure $ nub vs+ where+ notPrefixElem a xs = not (any (`isPrefixOf` a) xs)+++-- | Whether the given cabal version is installed.+cabalInstalled :: (MonadIO m, MonadReader env m, HasDirs env, MonadCatch m) => Version -> m Bool+cabalInstalled ver = do+ vers <- fmap rights getInstalledCabals+ pure $ elem ver vers+++-- Return the currently set cabal version, if any.+cabalSet :: (HasLog env, MonadReader env m, HasDirs env, MonadIO m, MonadThrow m, MonadCatch m) => m (Maybe Version)+cabalSet = do+ Dirs {..} <- getDirs+ let cabalbin = binDir </> "cabal" <> exeExt++ handleIO' NoSuchThing (\_ -> pure Nothing) $ do+ broken <- liftIO $ isBrokenSymlink cabalbin+ if broken+ then do+ logWarn $ "Broken symlink at " <> T.pack cabalbin+ pure Nothing+ else do+ link <- liftIO+ $ handleIO' InvalidArgument+ (\e -> pure $ Left (toException e))+ $ fmap Right $ getLinkTarget cabalbin+ case linkVersion =<< link of+ Right v -> pure $ Just v+ Left err -> do+ logWarn $ "Failed to parse cabal symlink target with: "+ <> T.pack (displayException err)+ <> ". The symlink "+ <> T.pack cabalbin+ <> " needs to point to valid cabal binary, such as 'cabal-3.4.0.0'."+ pure Nothing+ where+ -- We try to be extra permissive with link destination parsing,+ -- because of:+ -- https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/119+ linkVersion :: MonadThrow m => FilePath -> m Version+ linkVersion = throwEither . MP.parse parser "linkVersion" . T.pack . dropSuffix exeExt++ parser+ = MP.try (stripAbsolutePath *> cabalParse)+ <|> MP.try (stripRelativePath *> cabalParse)+ <|> cabalParse+ <|> (_tvVersion <$> toolVersionFromPath cabal)+ -- parses the version of "cabal-3.2.0.0" -> "3.2.0.0"+ cabalParse = MP.chunk "cabal-" *> version'+ -- parses any path component ending with path separator,+ -- e.g. "foo/"+ stripPathComponent = parseUntil1 pathSep *> MP.some pathSep+ -- parses an absolute path up until the last path separator,+ -- e.g. "/bar/baz/foo" -> "/bar/baz/", leaving "foo"+ stripAbsolutePath = MP.some pathSep *> MP.many (MP.try stripPathComponent)+ -- parses a relative path up until the last path separator,+ -- e.g. "bar/baz/foo" -> "bar/baz/", leaving "foo"+ stripRelativePath = MP.many (MP.try stripPathComponent)++++-- | Get all installed hls, by matching on+-- @~\/.ghcup\/bin/haskell-language-server-wrapper-<\hlsver\>@,+-- as well as @~\/.ghcup\/hls\/<\hlsver\>@+getInstalledHLSs :: (MonadReader env m, HasDirs env, MonadIO m, MonadCatch m)+ => m [Either FilePath Version]+getInstalledHLSs = filter (either (const True) (safeVersion . mkTVer)) <$> do+ Dirs {..} <- getDirs+ bins <- liftIO $ handleIO (\_ -> pure []) $ findFiles+ binDir+ (makeRegexOpts compExtended+ execBlank+ ([s|^haskell-language-server-wrapper-.*$|] :: ByteString)+ )+ legacy <- forM bins $ \f ->+ case+ version . T.pack <$> (stripSuffix exeExt =<< stripPrefix "haskell-language-server-wrapper-" f)+ of+ Just (Right r) -> pure $ Right r+ Just (Left _) -> pure $ Left f+ Nothing -> pure $ Left f++ hlsdir <- ghcupHLSBaseDir+ fs <- liftIO $ hideErrorDef [NoSuchThing] [] $ listDirectoryDirs (fromGHCupPath hlsdir)+ new <- forM fs $ \f -> case parseGHCupHLSDir f of+ Right r -> pure $ Right r+ Left _ -> pure $ Left f+ pure (nub (new <> legacy))+++-- | Get all installed stacks, by matching on+-- @~\/.ghcup\/bin/stack-<\stackver\>@.+getInstalledStacks :: (MonadReader env m, HasDirs env, MonadIO m, MonadCatch m)+ => m [Either FilePath Version]+getInstalledStacks = filter (either (const True) (safeVersion . mkTVer)) <$> do+ Dirs {..} <- getDirs+ bins <- liftIO $ handleIO (\_ -> pure []) $ findFiles+ binDir+ (makeRegexOpts compExtended+ execBlank+ ([s|^stack-.*$|] :: ByteString)+ )+ forM bins $ \f ->+ case version . T.pack <$> (stripSuffix exeExt =<< stripPrefix "stack-" f) of+ Just (Right r) -> pure $ Right r+ Just (Left _) -> pure $ Left f+ Nothing -> pure $ Left f++-- Return the currently set stack version, if any.+stackSet :: (MonadReader env m, HasDirs env, MonadIO m, MonadThrow m, MonadCatch m, HasLog env) => m (Maybe Version)+stackSet = do+ Dirs {..} <- getDirs+ let stackBin = binDir </> "stack" <> exeExt++ handleIO' NoSuchThing (\_ -> pure Nothing) $ do+ broken <- liftIO $ isBrokenSymlink stackBin+ if broken+ then do+ logWarn $ "Broken symlink at " <> T.pack stackBin+ pure Nothing+ else do+ link <- liftIO+ $ handleIO' InvalidArgument+ (\e -> pure $ Left (toException e))+ $ fmap Right $ getLinkTarget stackBin+ case linkVersion =<< link of+ Right v -> pure $ Just v+ Left err -> do+ logWarn $ "Failed to parse stack symlink target with: "+ <> T.pack (displayException err)+ <> ". The symlink "+ <> T.pack stackBin+ <> " needs to point to valid stack binary, such as 'stack-2.7.1'."+ pure Nothing+ where+ linkVersion :: MonadThrow m => FilePath -> m Version+ linkVersion = throwEither . MP.parse parser "" . T.pack . dropSuffix exeExt+ where+ parser+ = MP.try (stripAbsolutePath *> cabalParse)+ <|> MP.try (stripRelativePath *> cabalParse)+ <|> cabalParse+ <|> (_tvVersion <$> toolVersionFromPath stack)+ -- parses the version of "stack-2.7.1" -> "2.7.1"+ cabalParse = MP.chunk "stack-" *> version'+ -- parses any path component ending with path separator,+ -- e.g. "foo/"+ stripPathComponent = parseUntil1 pathSep *> MP.some pathSep+ -- parses an absolute path up until the last path separator,+ -- e.g. "/bar/baz/foo" -> "/bar/baz/", leaving "foo"+ stripAbsolutePath = MP.some pathSep *> MP.many (MP.try stripPathComponent)+ -- parses a relative path up until the last path separator,+ -- e.g. "bar/baz/foo" -> "bar/baz/", leaving "foo"+ stripRelativePath = MP.many (MP.try stripPathComponent)++-- | Whether the given Stack version is installed.+stackInstalled :: (MonadIO m, MonadReader env m, HasDirs env, MonadCatch m) => Version -> m Bool+stackInstalled ver = do+ vers <- fmap rights getInstalledStacks+ pure $ elem ver vers++-- | Whether the given HLS version is installed.+hlsInstalled :: (MonadIO m, MonadReader env m, HasDirs env, MonadCatch m) => Version -> m Bool+hlsInstalled ver = do+ vers <- fmap rights getInstalledHLSs+ pure $ elem ver vers++isLegacyHLS :: (MonadIO m, MonadReader env m, HasDirs env, MonadCatch m) => Version -> m Bool+isLegacyHLS ver = do+ bdir <- ghcupHLSDir ver+ not <$> liftIO (doesDirectoryExist $ fromGHCupPath bdir)+++-- Return the currently set hls version, if any.+hlsSet :: (HasLog env, MonadReader env m, HasDirs env, MonadIO m, MonadThrow m, MonadCatch m) => m (Maybe Version)+hlsSet = do+ Dirs {..} <- getDirs+ let hlsBin = binDir </> "haskell-language-server-wrapper" <> exeExt++ handleIO' NoSuchThing (\_ -> pure Nothing) $ do+ broken <- liftIO $ isBrokenSymlink hlsBin+ if broken+ then do+ logWarn $ "Broken symlink at " <> T.pack hlsBin+ pure Nothing+ else do+ link <- liftIO $ getLinkTarget hlsBin+ Just <$> linkVersion link+ where+ linkVersion :: MonadThrow m => FilePath -> m Version+ linkVersion = throwEither . MP.parse parser "" . T.pack . dropSuffix exeExt+ where+ parser+ = MP.try (stripAbsolutePath *> cabalParse)+ <|> MP.try (stripRelativePath *> cabalParse)+ <|> cabalParse+ <|> (_tvVersion <$> toolVersionFromPath hls)+ -- parses the version of "haskell-language-server-wrapper-1.1.0" -> "1.1.0"+ cabalParse = MP.chunk "haskell-language-server-wrapper-" *> version'+ -- parses any path component ending with path separator,+ -- e.g. "foo/"+ stripPathComponent = parseUntil1 pathSep *> MP.some pathSep+ -- parses an absolute path up until the last path separator,+ -- e.g. "/bar/baz/foo" -> "/bar/baz/", leaving "foo"+ stripAbsolutePath = MP.some pathSep *> MP.many (MP.try stripPathComponent)+ -- parses a relative path up until the last path separator,+ -- e.g. "bar/baz/foo" -> "bar/baz/", leaving "foo"+ stripRelativePath = MP.many (MP.try stripPathComponent)+++-- | Return the GHC versions the currently selected HLS supports.+hlsGHCVersions :: ( MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadIO m+ , MonadThrow m+ , MonadCatch m+ )+ => m [Version]+hlsGHCVersions = do+ h <- hlsSet+ fromMaybe [] <$> forM h hlsGHCVersions'+++hlsGHCVersions' :: ( MonadReader env m+ , HasDirs env+ , MonadIO m+ , MonadThrow m+ , MonadCatch m+ )+ => Version+ -> m [Version]+hlsGHCVersions' v' = do+ bins <- hlsServerBinaries v' Nothing+ let vers = fmap+ (version+ . T.pack+ . fromJust+ . stripPrefix "haskell-language-server-"+ . head+ . splitOn "~"+ )+ bins+ pure . sortBy (flip compare) . rights $ vers+++-- | Get all server binaries for an hls version from the ~/.ghcup/bin directory, if any.+hlsServerBinaries :: (MonadReader env m, HasDirs env, MonadIO m)+ => Version+ -> Maybe Version -- ^ optional GHC version+ -> m [FilePath]+hlsServerBinaries ver mghcVer = do+ Dirs {..} <- getDirs+ liftIO $ handleIO (\_ -> pure []) $ findFiles+ binDir+ (makeRegexOpts+ compExtended+ execBlank+ ([s|^haskell-language-server-|]+ <> maybe [s|.*|] escapeVerRex mghcVer+ <> [s|~|]+ <> escapeVerRex ver+ <> E.encodeUtf8 (T.pack exeExt)+ <> [s|$|] :: ByteString+ )+ )++-- | Get all scripts for a hls version from the ~/.ghcup/hls/<ver>/bin directory, if any.+-- Returns the full path.+hlsInternalServerScripts :: (MonadReader env m, HasDirs env, MonadIO m, MonadThrow m)+ => Version+ -> Maybe Version -- ^ optional GHC version+ -> m [FilePath]+hlsInternalServerScripts ver mghcVer = do+ dir <- ghcupHLSDir ver+ let bdir = fromGHCupPath dir </> "bin"+ fmap (bdir </>) . filter (\f -> maybe True (\gv -> ("-" <> T.unpack (prettyVer gv)) `isSuffixOf` f) mghcVer)+ <$> liftIO (listDirectoryFiles bdir)++-- | Get all binaries for a hls version from the ~/.ghcup/hls/<ver>/lib/haskell-language-server-<ver>/bin directory, if any.+-- Returns the full path.+hlsInternalServerBinaries :: (MonadReader env m, HasDirs env, MonadIO m, MonadThrow m, MonadFail m)+ => Version+ -> Maybe Version -- ^ optional GHC version+ -> m [FilePath]+hlsInternalServerBinaries ver mghcVer = do+ dir <- fromGHCupPath <$> ghcupHLSDir ver+ let regex = makeRegexOpts compExtended execBlank ([s|^haskell-language-server-.*$|] :: ByteString)+ (Just bdir) <- fmap headMay $ liftIO $ expandFilePath [Left (dir </> "lib"), Right regex, Left "bin"]+ fmap (bdir </>) . filter (\f -> maybe True (\gv -> ("-" <> T.unpack (prettyVer gv)) `isSuffixOf` f) mghcVer)+ <$> liftIO (listDirectoryFiles bdir)++-- | Get all libraries for a hls version from the ~/.ghcup/hls/<ver>/lib/haskell-language-server-<ver>/lib/<ghc-ver>/+-- directory, if any.+-- Returns the full path.+hlsInternalServerLibs :: (MonadReader env m, HasDirs env, MonadIO m, MonadThrow m, MonadFail m)+ => Version+ -> Version -- ^ GHC version+ -> m [FilePath]+hlsInternalServerLibs ver ghcVer = do+ dir <- fromGHCupPath <$> ghcupHLSDir ver+ let regex = makeRegexOpts compExtended execBlank ([s|^haskell-language-server-.*$|] :: ByteString)+ (Just bdir) <- fmap headMay $ liftIO $ expandFilePath [Left (dir </> "lib"), Right regex, Left ("lib" </> T.unpack (prettyVer ghcVer))]+ fmap (bdir </>) <$> liftIO (listDirectoryFiles bdir)+++-- | Get the wrapper binary for an hls version, if any.+hlsWrapperBinary :: (MonadReader env m, HasDirs env, MonadThrow m, MonadIO m)+ => Version+ -> m (Maybe FilePath)+hlsWrapperBinary ver = do+ Dirs {..} <- getDirs+ wrapper <- liftIO $ handleIO (\_ -> pure []) $ findFiles+ binDir+ (makeRegexOpts+ compExtended+ execBlank+ ([s|^haskell-language-server-wrapper-|] <> escapeVerRex ver <> E.encodeUtf8 (T.pack exeExt) <> [s|$|] :: ByteString+ )+ )+ case wrapper of+ [] -> pure Nothing+ [x] -> pure $ Just x+ _ -> throwM $ UnexpectedListLength+ "There were multiple hls wrapper binaries for a single version"+++-- | Get all binaries for an hls version, if any.+hlsAllBinaries :: (MonadReader env m, HasDirs env, MonadIO m, MonadThrow m) => Version -> m [FilePath]+hlsAllBinaries ver = do+ hls' <- hlsServerBinaries ver Nothing+ wrapper <- hlsWrapperBinary ver+ pure (maybeToList wrapper ++ hls')++++++ -------------+ --[ Other ]--+ -------------+++-- | Usually @~\/.ghcup\/ghc\/\<ver\>\/bin\/@+ghcInternalBinDir :: (MonadReader env m, HasDirs env, MonadThrow m, MonadFail m, MonadIO m)+ => TargetVersion+ -> m FilePath+ghcInternalBinDir ver = do+ ghcdir <- fromGHCupPath <$> ghcupGHCDir ver+ pure (ghcdir </> "bin")+++-- | Get tool files from @~\/.ghcup\/ghc\/\<ver\>\/bin\/\*@+-- while ignoring @*-\<ver\>@ symlinks and accounting for cross triple prefix.+--+-- Returns unversioned relative files without extension, e.g.:+--+-- - @["hsc2hs","haddock","hpc","runhaskell","ghc","ghc-pkg","ghci","runghc","hp2ps"]@+ghcToolFiles :: (MonadReader env m, HasDirs env, HasLog env, MonadThrow m, MonadFail m, MonadIO m)+ => TargetVersion+ -> Excepts '[NotInstalled] m [FilePath]+ghcToolFiles ver = do+ bindir <- ghcInternalBinDir ver+ logDebug2 $ "(ghcToolFiles) bindir: " <> T.pack bindir++ -- fail if ghc is not installed+ whenM (fmap not $ ghcInstalled ver)+ (throwE (NotInstalled ghc ver))++ files <- liftIO (listDirectoryFiles bindir >>= filterM (doesFileExist . (bindir </>)))+ logDebug2 $ "(ghcToolFiles) files: " <> T.pack (show files)+ let unique = getUniqueTools . groupToolFiles . fmap (dropSuffix exeExt) $ files+ logDebug2 $ "(ghcToolFiles) unique: " <> T.pack (show unique)+ pure unique++ where++ groupToolFiles :: [FilePath] -> [[(FilePath, String)]]+ groupToolFiles = groupBy (\(a, _) (b, _) -> a == b) . fmap (splitOnPVP "-")++ getUniqueTools :: [[(FilePath, String)]] -> [String]+ getUniqueTools = filter (isNotAnyInfix blackListedTools) . nub . fmap fst . concatMap (filter ((== "") . snd))++ blackListedTools :: [String]+ blackListedTools = ["haddock-ghc"]++ isNotAnyInfix :: [String] -> String -> Bool+ isNotAnyInfix xs t = foldr (\a b -> not (a `isInfixOf` t) && b) True xs+++++-- | For ghc without arch triple, this is:+--+-- - ghc+--+-- For ghc with arch triple:+--+-- - <triple>-ghc (e.g. arm-linux-gnueabihf-ghc)+ghcBinaryName :: TargetVersion -> String+ghcBinaryName (TargetVersion (Just t) _) = T.unpack (t <> "-ghc" <> T.pack exeExt)+ghcBinaryName (TargetVersion Nothing _) = T.unpack ("ghc" <> T.pack exeExt)++++-- | Warn if the installed and set HLS is not compatible with the installed and+-- set GHC version.+warnAboutHlsCompatibility :: ( MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadIOish m+ )+ => m ()+warnAboutHlsCompatibility = do+ supportedGHC <- hlsGHCVersions+ currentGHC <- fmap _tvVersion <$> ghcSet Nothing+ currentHLS <- hlsSet+ Warnings.warnAboutHlsCompatibility currentHLS currentGHC supportedGHC
− lib/GHCup/List.hs
@@ -1,439 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedStrings #-}--{-|-Module : GHCup.List-Description : Listing versions and tools-Copyright : (c) Julian Ospald, 2020-License : LGPL-3.0-Maintainer : hasufell@hasufell.de-Stability : experimental-Portability : portable--}-module GHCup.List where--import GHCup.Download-import GHCup.Errors-import GHCup.Types-import GHCup.Types.JSON ( )-import GHCup.Types.Optics-import GHCup.Utils-import GHCup.Prelude.Logger-import GHCup.Version--import Control.Applicative-import Control.Exception.Safe-import Control.Monad-#if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )-#endif-import Control.Monad.Reader-import Data.Either-import Data.List-import Data.Maybe-import Data.Text ( Text )-import Data.Time.Calendar ( Day )-import Data.Versions hiding ( patch )-import Data.Variant.Excepts-import Optics-import Prelude hiding ( abs- , writeFile- )--import qualified Data.Map.Strict as Map-import qualified Data.Text as T----------- ------------------- --[ List tools ]--- ----------------------- | Filter data type for 'listVersions'.-data ListCriteria = ListInstalled Bool- | ListSet Bool- | ListAvailable Bool- deriving (Eq, Show)---- | A list result describes a single tool version--- and various of its properties.-data ListResult = ListResult- { lTool :: Tool- , lVer :: Version- , lCross :: Maybe Text -- ^ currently only for GHC- , lTag :: [Tag]- , lInstalled :: Bool- , lSet :: Bool -- ^ currently active version- , lStray :: Bool -- ^ not in download info- , lNoBindist :: Bool -- ^ whether the version is available for this platform/arch- , hlsPowered :: Bool- , lReleaseDay :: Maybe Day- }- deriving (Eq, Ord, Show)----- | Extract all available tool versions and their tags.-availableToolVersions :: GHCupDownloads -> Tool -> Map.Map GHCTargetVersion VersionInfo-availableToolVersions av tool = view- (at tool % non Map.empty)- av----- | List all versions from the download info, as well as stray--- versions.-listVersions :: ( MonadCatch m- , HasLog env- , MonadThrow m- , HasLog env- , MonadIO m- , MonadReader env m- , HasDirs env- , HasPlatformReq env- , HasGHCupInfo env- )- => Maybe Tool- -> [ListCriteria]- -> Bool- -> Bool- -> (Maybe Day, Maybe Day)- -> m [ListResult]-listVersions lt' criteria hideOld showNightly days = do- -- some annoying work to avoid too much repeated IO- cSet <- cabalSet- cabals <- getInstalledCabals- hlsSet' <- hlsSet- hlses <- getInstalledHLSs- sSet <- stackSet- stacks <- getInstalledStacks- hlsGHCVs <- fmap mkTVer <$> hlsGHCVersions-- go lt' hlsGHCVs cSet cabals hlsSet' hlses sSet stacks- where- go lt hlsGHCVs cSet cabals hlsSet' hlses sSet stacks = do- case lt of- Just t -> do- GHCupInfo { _ghcupDownloads = dls } <- getGHCupInfo- -- get versions from GHCupDownloads- let avTools = availableToolVersions dls t- lr <- filter' <$> forM (Map.toList avTools) (toListResult t hlsGHCVs cSet cabals hlsSet' hlses sSet stacks)-- case t of- GHC -> do- slr <- strayGHCs avTools- pure (sort (slr ++ lr))- Cabal -> do- slr <- strayCabals (Map.mapKeys _tvVersion avTools) cSet cabals- pure (sort (slr ++ lr))- HLS -> do- slr <- strayHLS (Map.mapKeys _tvVersion avTools) hlsSet' hlses- pure (sort (slr ++ lr))- Stack -> do- slr <- strayStacks (Map.mapKeys _tvVersion avTools) sSet stacks- pure (sort (slr ++ lr))- GHCup -> do- let cg = maybeToList $ currentGHCup avTools- pure (sort (cg ++ lr))- Nothing -> do- ghcvers <- go (Just GHC) hlsGHCVs cSet cabals hlsSet' hlses sSet stacks- cabalvers <- go (Just Cabal) hlsGHCVs cSet cabals hlsSet' hlses sSet stacks- hlsvers <- go (Just HLS) hlsGHCVs cSet cabals hlsSet' hlses sSet stacks- ghcupvers <- go (Just GHCup) hlsGHCVs cSet cabals hlsSet' hlses sSet stacks- stackvers <- go (Just Stack) hlsGHCVs cSet cabals hlsSet' hlses sSet stacks- pure (ghcvers <> cabalvers <> hlsvers <> stackvers <> ghcupvers)- strayGHCs :: ( MonadCatch m- , MonadReader env m- , HasDirs env- , MonadThrow m- , HasLog env- , MonadIO m- )- => Map.Map GHCTargetVersion VersionInfo- -> m [ListResult]- strayGHCs avTools = do- ghcs <- getInstalledGHCs- fmap catMaybes $ forM ghcs $ \case- Right tver@GHCTargetVersion{ .. } -> do- case Map.lookup tver avTools of- Just _ -> pure Nothing- Nothing -> do- lSet <- fmap (maybe False (\(GHCTargetVersion _ v ) -> v == _tvVersion)) $ ghcSet _tvTarget- hlsPowered <- fmap (elem _tvVersion) hlsGHCVersions- pure $ Just $ ListResult- { lTool = GHC- , lVer = _tvVersion- , lCross = _tvTarget- , lTag = []- , lInstalled = True- , lStray = isNothing (Map.lookup tver avTools)- , lNoBindist = False- , lReleaseDay = Nothing- , ..- }- Left e -> do- logWarn- $ "Could not parse version of stray directory " <> T.pack e- pure Nothing-- strayCabals :: ( MonadReader env m- , HasDirs env- , MonadCatch m- , MonadThrow m- , HasLog env- , MonadIO m- )- => Map.Map Version VersionInfo- -> Maybe Version- -> [Either FilePath Version]- -> m [ListResult]- strayCabals avTools cSet cabals = do- fmap catMaybes $ forM cabals $ \case- Right ver ->- case Map.lookup ver avTools of- Just _ -> pure Nothing- Nothing -> do- let lSet = cSet == Just ver- pure $ Just $ ListResult- { lTool = Cabal- , lVer = ver- , lCross = Nothing- , lTag = []- , lInstalled = True- , lStray = isNothing (Map.lookup ver avTools)- , lNoBindist = False- , hlsPowered = False- , lReleaseDay = Nothing- , ..- }- Left e -> do- logWarn- $ "Could not parse version of stray directory " <> T.pack e- pure Nothing-- strayHLS :: ( MonadReader env m- , HasDirs env- , MonadCatch m- , MonadThrow m- , HasLog env- , MonadIO m)- => Map.Map Version VersionInfo- -> Maybe Version- -> [Either FilePath Version]- -> m [ListResult]- strayHLS avTools hlsSet' hlss = do- fmap catMaybes $ forM hlss $ \case- Right ver ->- case Map.lookup ver avTools of- Just _ -> pure Nothing- Nothing -> do- let lSet = hlsSet' == Just ver- pure $ Just $ ListResult- { lTool = HLS- , lVer = ver- , lCross = Nothing- , lTag = []- , lInstalled = True- , lStray = isNothing (Map.lookup ver avTools)- , lNoBindist = False- , hlsPowered = False- , lReleaseDay = Nothing- , ..- }- Left e -> do- logWarn- $ "Could not parse version of stray directory " <> T.pack e- pure Nothing-- strayStacks :: ( MonadReader env m- , HasDirs env- , MonadCatch m- , MonadThrow m- , HasLog env- , MonadIO m- )- => Map.Map Version VersionInfo- -> Maybe Version- -> [Either FilePath Version]- -> m [ListResult]- strayStacks avTools stackSet' stacks = do- fmap catMaybes $ forM stacks $ \case- Right ver ->- case Map.lookup ver avTools of- Just _ -> pure Nothing- Nothing -> do- let lSet = stackSet' == Just ver- pure $ Just $ ListResult- { lTool = Stack- , lVer = ver- , lCross = Nothing- , lTag = []- , lInstalled = True- , lStray = isNothing (Map.lookup ver avTools)- , lNoBindist = False- , hlsPowered = False- , lReleaseDay = Nothing- , ..- }- Left e -> do- logWarn- $ "Could not parse version of stray directory " <> T.pack e- pure Nothing-- currentGHCup :: Map.Map GHCTargetVersion VersionInfo -> Maybe ListResult- currentGHCup av =- let currentVer = mkTVer $ fromJust $ pvpToVersion ghcUpVer ""- listVer = Map.lookup currentVer av- latestVer = fst <$> headOf (getTagged Latest) av- recommendedVer = fst <$> headOf (getTagged Latest) av- isOld = maybe True (> currentVer) latestVer && maybe True (> currentVer) recommendedVer- in if | Map.member currentVer av -> Nothing- | otherwise -> Just $ ListResult { lVer = _tvVersion currentVer- , lTag = maybe (if isOld then [Old] else []) _viTags listVer- , lCross = Nothing- , lTool = GHCup- , lStray = isNothing listVer- , lSet = True- , lInstalled = True- , lNoBindist = False- , hlsPowered = False- , lReleaseDay = Nothing- }-- -- NOTE: this are not cross ones, because no bindists- toListResult :: ( HasLog env- , MonadReader env m- , HasDirs env- , HasGHCupInfo env- , HasPlatformReq env- , MonadIO m- , MonadCatch m- )- => Tool- -> [GHCTargetVersion]- -> Maybe Version- -> [Either FilePath Version]- -> Maybe Version- -> [Either FilePath Version]- -> Maybe Version- -> [Either FilePath Version]- -> (GHCTargetVersion, VersionInfo)- -> m ListResult- toListResult t hlsGHCVs cSet cabals hlsSet' hlses stackSet' stacks (tver, VersionInfo{..}) = do- let v = _tvVersion tver- case t of- GHC -> do- dli <- fmap veitherToEither $ runE @'[NoDownload] $ getDownloadInfo' GHC tver- let lNoBindist = isLeft dli- let bTags = either (const []) (fromMaybe [] . _dlTag) dli- lSet <- fmap (== Just tver) $ ghcSet (_tvTarget tver)- lInstalled <- ghcInstalled tver- let hlsPowered = tver `elem` hlsGHCVs- pure ListResult { lVer = _tvVersion tver- , lCross = _tvTarget tver- , lTag = _viTags <> bTags- , lTool = t- , lStray = False- , lReleaseDay = _viReleaseDay- , ..- }- Cabal -> do- dli <- fmap veitherToEither $ runE @'[NoDownload] $ getDownloadInfo Cabal v- let lNoBindist = isLeft dli- let bTags = either (const []) (fromMaybe [] . _dlTag) dli- let lSet = cSet == Just v- let lInstalled = elem v $ rights cabals- pure ListResult { lVer = v- , lCross = Nothing- , lTag = _viTags <> bTags- , lTool = t- , lStray = False- , hlsPowered = False- , lReleaseDay = _viReleaseDay- , ..- }- GHCup -> do- let lSet = prettyPVP ghcUpVer == prettyVer v- let lInstalled = lSet- pure ListResult { lVer = v- , lTag = _viTags- , lCross = Nothing- , lTool = t- , lStray = False- , lNoBindist = False- , hlsPowered = False- , lReleaseDay = _viReleaseDay- , ..- }- HLS -> do- dli <- fmap veitherToEither $ runE @'[NoDownload] $ getDownloadInfo HLS v- let lNoBindist = isLeft dli- let bTags = either (const []) (fromMaybe [] . _dlTag) dli- let lSet = hlsSet' == Just v- let lInstalled = elem v $ rights hlses- pure ListResult { lVer = v- , lCross = Nothing- , lTag = _viTags <> bTags- , lTool = t- , lStray = False- , hlsPowered = False- , lReleaseDay = _viReleaseDay- , ..- }- Stack -> do- dli <- fmap veitherToEither $ runE @'[NoDownload] $ getDownloadInfo Stack v- let lNoBindist = isLeft dli- let bTags = either (const []) (fromMaybe [] . _dlTag) dli- let lSet = stackSet' == Just v- let lInstalled = elem v $ rights stacks- pure ListResult { lVer = v- , lCross = Nothing- , lTag = _viTags <> bTags- , lTool = t- , lStray = False- , hlsPowered = False- , lReleaseDay = _viReleaseDay- , ..- }--- filter' :: [ListResult] -> [ListResult]- filter' = filterNightly . filterOld . filter (\lr -> foldr (\a b -> fromCriteria a lr && b) True criteria) . filterDays-- filterDays :: [ListResult] -> [ListResult]- filterDays lrs = case days of- (Nothing, Nothing) -> lrs- (Just from, Just to') -> filter (\ListResult{..} -> maybe False (\d -> d >= from && d <= to') lReleaseDay) lrs- (Nothing, Just to') -> filter (\ListResult{..} -> maybe False (<= to') lReleaseDay) lrs- (Just from, Nothing) -> filter (\ListResult{..} -> maybe False (>= from) lReleaseDay) lrs-- fromCriteria :: ListCriteria -> ListResult -> Bool- fromCriteria lc ListResult{..} = case lc of- ListInstalled b -> f b lInstalled- ListSet b -> f b lSet- ListAvailable b -> f b $ not lNoBindist- where- f b- | b = id- | otherwise = not-- filterOld :: [ListResult] -> [ListResult]- filterOld lr- | hideOld = filter (\ListResult {..} -> lInstalled || Old `notElem` lTag) lr- | otherwise = lr-- filterNightly :: [ListResult] -> [ListResult]- filterNightly lr- | showNightly = lr- | otherwise = filter (\ListResult {..} -> lInstalled || (Nightly `notElem` lTag && LatestNightly `notElem` lTag)) lr-
lib/GHCup/PlanJson.hs view
@@ -1,8 +1,9 @@ module GHCup.PlanJson where -import Control.Monad (unless)+import GHCup.System.Directory++import Control.Monad ( unless ) import System.FilePath-import System.Directory findPlanJson :: FilePath
− lib/GHCup/Platform.hs
@@ -1,374 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE TemplateHaskellQuotes #-}---{-|-Module : GHCup.Platform-Description : Retrieving platform information-Copyright : (c) Julian Ospald, 2020-License : LGPL-3.0-Maintainer : hasufell@hasufell.de-Stability : experimental-Portability : portable--}-module GHCup.Platform where---import GHCup.Errors-import GHCup.Types-import GHCup.Types.Optics-import GHCup.Types.JSON ( )-import GHCup.Utils.Dirs-import GHCup.Prelude-import GHCup.Prelude.Logger-import GHCup.Prelude.Process-import GHCup.Prelude.String.QQ-import GHCup.Prelude.Version.QQ-import GHCup.Prelude.MegaParsec--#if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )-#endif-import Control.Applicative-import Control.Exception.Safe-import Control.Monad-import Control.Monad.Reader-import Data.ByteString ( ByteString )-import Data.Foldable-import Data.Maybe-import Data.Text ( Text )-import Data.Versions-import Data.Variant.Excepts-import Prelude hiding ( abs- , readFile- , writeFile- )-import System.Info-import System.OsRelease as OSR-import System.Exit-import System.FilePath-import Text.PrettyPrint.HughesPJClass ( prettyShow )-import Text.Regex.Posix--import qualified Text.Megaparsec as MP--import qualified Data.Text as T-import qualified Data.Text.IO as T-import Data.Void-import qualified Data.List as L----- --------------------------- --[ Platform detection ]--- ------------------------------- | Get the full platform request, consisting of architecture, distro, ...-platformRequest :: (MonadReader env m, Alternative m, MonadFail m, HasLog env, MonadCatch m, MonadIO m)- => Excepts- '[NoCompatiblePlatform, NoCompatibleArch, DistroNotFound]- m- PlatformRequest-platformRequest = do- (PlatformResult rp rv) <- liftE getPlatform- ar <- lE getArchitecture- pure $ PlatformRequest ar rp rv---getArchitecture :: Either NoCompatibleArch Architecture-getArchitecture = case arch of- "x86_64" -> Right A_64- "i386" -> Right A_32- "powerpc" -> Right A_PowerPC- "powerpc64" -> Right A_PowerPC64- "powerpc64le" -> Right A_PowerPC64- "sparc" -> Right A_Sparc- "sparc64" -> Right A_Sparc64- "arm" -> Right A_ARM- "aarch64" -> Right A_ARM64- what -> Left (NoCompatibleArch what)---getPlatform :: (Alternative m, MonadReader env m, HasLog env, MonadCatch m, MonadIO m, MonadFail m)- => Excepts- '[NoCompatiblePlatform, DistroNotFound]- m- PlatformResult-getPlatform = do- pfr <- case os of- "linux" -> do- (distro, ver) <- liftE getLinuxDistro- pure $ PlatformResult { _platform = Linux distro, _distroVersion = ver }- "darwin" -> do- ver <-- either (const Nothing) Just- . versioning- -- TODO: maybe do this somewhere else- . decUTF8Safe'- <$> getDarwinVersion- pure $ PlatformResult { _platform = Darwin, _distroVersion = ver }- "freebsd" -> do- ver <-- either (const Nothing) Just . versioning . decUTF8Safe'- <$> getFreeBSDVersion- pure $ PlatformResult { _platform = FreeBSD, _distroVersion = ver }- "openbsd" -> do- ver <-- either (const Nothing) Just . versioning . decUTF8Safe'- <$> getOpenBSDVersion- pure $ PlatformResult { _platform = OpenBSD, _distroVersion = ver }- "mingw32" -> pure PlatformResult { _platform = Windows, _distroVersion = Nothing }- what -> throwE $ NoCompatiblePlatform what- lift $ logDebug $ "Identified Platform as: " <> T.pack (prettyShow pfr)- pure pfr- where- getOpenBSDVersion = lift $ fmap _stdOut $ executeOut "uname" ["-r"] Nothing- getFreeBSDVersion = lift $ fmap _stdOut $ executeOut "freebsd-version" [] Nothing- getDarwinVersion = lift $ fmap _stdOut $ executeOut "sw_vers"- ["-productVersion"]- Nothing---getLinuxDistro :: (Alternative m, MonadCatch m, MonadIO m, MonadFail m)- => Excepts '[DistroNotFound] m (LinuxDistro, Maybe Versioning)-getLinuxDistro = do- -- TODO: don't do alternative on IO, because it hides bugs- (name, mid, ver) <- handleIO (\_ -> throwE DistroNotFound) $ lift $ asum- [ liftIO try_os_release- , try_lsb_release_cmd- , liftIO try_redhat_release- , liftIO try_debian_version- ]- let hasWord xs = let f t = any (\x -> match (regex x) (T.unpack t)) xs- in f name || maybe False f mid- let parsedVer = ver >>= either (const Nothing) Just . versioning- distro = if- | hasWord ["debian"] -> Debian- | hasWord ["ubuntu"] -> Ubuntu- | hasWord ["linuxmint", "Linux Mint"] -> Mint- | hasWord ["fedora"] -> Fedora- | hasWord ["centos"] -> CentOS- | hasWord ["Red Hat"] -> RedHat- | hasWord ["alpine"] -> Alpine- | hasWord ["exherbo"] -> Exherbo- | hasWord ["gentoo"] -> Gentoo- | hasWord ["opensuse", "suse"] -> OpenSUSE- | hasWord ["amazonlinux", "Amazon Linux"] -> AmazonLinux- | hasWord ["rocky", "Rocky Linux"] -> Rocky- -- https://github.com/void-linux/void-packages/blob/master/srcpkgs/base-files/files/os-release- | hasWord ["void", "Void Linux"] -> Void- | otherwise -> UnknownLinux- pure (distro, parsedVer)- where- regex x = makeRegexOpts compIgnoreCase execBlank ([s|\<|] ++ x ++ [s|\>|])-- lsb_release_cmd :: FilePath- lsb_release_cmd = "lsb-release"- redhat_release :: FilePath- redhat_release = "/etc/redhat-release"- debian_version :: FilePath- debian_version = "/etc/debian_version"-- try_os_release :: IO (Text, Maybe Text, Maybe Text)- try_os_release = do- Just OsRelease{ name = name, version_id = version_id, OSR.id = id' } <-- fmap osRelease <$> parseOsRelease- pure (T.pack name, Just (T.pack id'), fmap T.pack version_id)-- try_lsb_release_cmd :: (MonadFail m, MonadIO m)- => m (Text, Maybe Text, Maybe Text)- try_lsb_release_cmd = do- (Just _) <- liftIO $ findExecutable lsb_release_cmd- name <- fmap _stdOut $ executeOut lsb_release_cmd ["-si"] Nothing- ver <- fmap _stdOut $ executeOut lsb_release_cmd ["-sr"] Nothing- pure (decUTF8Safe' name, Nothing, Just $ decUTF8Safe' ver)-- try_redhat_release :: IO (Text, Maybe Text, Maybe Text)- try_redhat_release = do- t <- T.readFile redhat_release- let nameRegex n =- makeRegexOpts compIgnoreCase- execBlank- ([s|\<|] <> fS n <> [s|\>|] :: ByteString) :: Regex- let verRegex =- makeRegexOpts compIgnoreCase- execBlank- ([s|\<([0-9])+(.([0-9])+)*\>|] :: ByteString) :: Regex- let nameRe n =- fromEmpty . match (nameRegex n) $ T.unpack t :: Maybe String- verRe = fromEmpty . match verRegex $ T.unpack t :: Maybe String- (Just name) <- pure- (nameRe "CentOS" <|> nameRe "Fedora" <|> nameRe "Red Hat")- pure (T.pack name, Nothing, fmap T.pack verRe)- where- fromEmpty :: String -> Maybe String- fromEmpty "" = Nothing- fromEmpty s' = Just s'-- try_debian_version :: IO (Text, Maybe Text, Maybe Text)- try_debian_version = do- ver <- T.readFile debian_version- pure (T.pack "debian", Just (T.pack "debian"), Just ver)---getStackGhcBuilds :: (MonadReader env m, HasLog env, MonadIO m)- => PlatformResult- -> Excepts '[ParseError, NoCompatiblePlatform, DistroNotFound, ProcessError] m [String]-getStackGhcBuilds PlatformResult{..} = do- case _platform of- Linux _ -> do- -- Some systems don't have ldconfig in the PATH, so make sure to look in- -- /sbin and /usr/sbin as well- sbinEnv <- liftIO $ addToPath sbinDirs False- ldConfig <- lift $ executeOut' "ldconfig" ["-p"] Nothing (Just sbinEnv)- firstWords <- case ldConfig of- CapturedProcess ExitSuccess so _ ->- pure . mapMaybe (listToMaybe . T.words) . T.lines . T.pack . stripNewlineEnd . T.unpack . decUTF8Safe' $ so- CapturedProcess (ExitFailure _) _ _ ->- -- throwE $ NonZeroExit c "ldconfig" ["-p" ]- pure []- let checkLib :: (MonadReader env m, HasLog env, MonadIO m) => String -> m Bool- checkLib lib- | libT `elem` firstWords = do- logDebug $ "Found shared library " <> libT <> " in 'ldconfig -p' output"- pure True- | isWindows =- -- Cannot parse /usr/lib on Windows- pure False- | otherwise = hasMatches lib usrLibDirs- -- This is a workaround for the fact that libtinfo.so.x doesn't- -- appear in the 'ldconfig -p' output on Arch or Slackware even- -- when it exists. There doesn't seem to be an easy way to get the- -- true list of directories to scan for shared libs, but this- -- works for our particular cases.- where- libT = T.pack lib-- hasMatches :: (MonadReader env m, HasLog env, MonadIO m) => String -> [FilePath] -> m Bool- hasMatches lib dirs = do- matches <- filterM (liftIO . doesFileExist . (</> lib)) dirs- case matches of- [] -> logDebug ("Did not find shared library " <> libT) >> pure False- (path:_) -> logDebug ("Found shared library " <> libT <> " in " <> T.pack path) >> pure True- where- libT = T.pack lib-- getLibc6Version :: MonadIO m- => Excepts '[ParseError, ProcessError] m Version- getLibc6Version = do- CapturedProcess{..} <- lift $ executeOut "ldd" ["--version"] Nothing- case _exitCode of- ExitSuccess -> either (throwE . ParseError . show) pure- . MP.parse lddVersion "" . T.pack . stripNewlineEnd . T.unpack . decUTF8Safe' $ _stdOut- ExitFailure c -> throwE $ NonZeroExit c "ldd" ["--version" ]-- -- Assumes the first line of ldd has the format:- --- -- ldd (...) nn.nn- --- -- where nn.nn corresponds to the version of libc6.- lddVersion :: MP.Parsec Void Text Version- lddVersion = do- skipWhile (/= ')')- skip (== ')')- skipSpace- version'-- hasMusl <- hasMatches relFileLibcMuslx86_64So1 libDirs- mLibc6Version <- veitherToEither <$> runE getLibc6Version- case mLibc6Version of- Right libc6Version -> logDebug $ "Found shared library libc6 in version: " <> prettyVer libc6Version- Left _ -> logDebug "Did not find a version of shared library libc6."- let hasLibc6_2_32 = either (const False) (>= [vver|2.32|]) mLibc6Version- hastinfo5 <- checkLib relFileLibtinfoSo5- hastinfo6 <- checkLib relFileLibtinfoSo6- hasncurses6 <- checkLib relFileLibncurseswSo6- hasgmp5 <- checkLib relFileLibgmpSo10- hasgmp4 <- checkLib relFileLibgmpSo3- let libComponents = if hasMusl- then- [ ["musl"] ]- else- concat- [ if hastinfo6 && hasgmp5- then- if hasLibc6_2_32- then [["tinfo6"]]- else [["tinfo6-libc6-pre232"]]- else [[]]- , [ [] | hastinfo5 && hasgmp5 ]- , [ ["ncurses6"] | hasncurses6 && hasgmp5 ]- , [ ["gmp4"] | hasgmp4 ]- ]- pure $ map- (\c -> case c of- [] -> []- _ -> L.intercalate "-" c)- libComponents- OpenBSD -> pure []- FreeBSD ->- case _distroVersion of- Just fVer- | fVer >= [vers|12|] -> pure []- _ -> pure ["ino64"]- Darwin -> pure []- Windows -> pure []- where-- relFileLibcMuslx86_64So1 :: FilePath- relFileLibcMuslx86_64So1 = "libc.musl-x86_64.so.1"- libDirs :: [FilePath]- libDirs = ["/lib", "/lib64"]- usrLibDirs :: [FilePath]- usrLibDirs = ["/usr/lib", "/usr/lib64"]- sbinDirs :: [FilePath]- sbinDirs = ["/sbin", "/usr/sbin"]- relFileLibtinfoSo5 :: FilePath- relFileLibtinfoSo5 = "libtinfo.so.5"- relFileLibtinfoSo6 :: FilePath- relFileLibtinfoSo6 = "libtinfo.so.6"- relFileLibncurseswSo6 :: FilePath- relFileLibncurseswSo6 = "libncursesw.so.6"- relFileLibgmpSo10 :: FilePath- relFileLibgmpSo10 = "libgmp.so.10"- relFileLibgmpSo3 :: FilePath- relFileLibgmpSo3 = "libgmp.so.3"--getStackOSKey :: Monad m => PlatformRequest -> Excepts '[UnsupportedSetupCombo] m String-getStackOSKey PlatformRequest { .. } =- case (_rArch, _rPlatform) of- (A_32 , Linux _) -> pure "linux32"- (A_64 , Linux _) -> pure "linux64"- (A_32 , Darwin ) -> pure "macosx"- (A_64 , Darwin ) -> pure "macosx"- (A_32 , FreeBSD) -> pure "freebsd32"- (A_64 , FreeBSD) -> pure "freebsd64"- (A_32 , OpenBSD) -> pure "openbsd32"- (A_64 , OpenBSD) -> pure "openbsd64"- (A_32 , Windows) -> pure "windows32"- (A_64 , Windows) -> pure "windows64"- (A_ARM , Linux _) -> pure "linux-armv7"- (A_ARM64, Linux _) -> pure "linux-aarch64"- (A_Sparc, Linux _) -> pure "linux-sparc"- (A_ARM64, Darwin ) -> pure "macosx-aarch64"- (A_ARM64, FreeBSD) -> pure "freebsd-aarch64"- (A_ARM64, OpenBSD) -> pure "openbsd-aarch64"- (arch', os') -> throwE $ UnsupportedSetupCombo arch' os'--getStackPlatformKey :: (MonadReader env m, MonadFail m, HasLog env, MonadCatch m, MonadIO m)- => PlatformRequest- -> Excepts '[UnsupportedSetupCombo, ParseError, NoCompatiblePlatform, NoCompatibleArch, DistroNotFound, ProcessError] m [String]-getStackPlatformKey pfreq@PlatformRequest{..} = do- osKey <- liftE $ getStackOSKey pfreq- builds <- liftE $ getStackGhcBuilds (PlatformResult _rPlatform _rVersion)- let builds' = (\build -> if null build then osKey else osKey <> "-" <> build) <$> builds- logDebug $ "Potential GHC builds: " <> mconcat (L.intersperse ", " $ fmap T.pack builds')- pure builds'-
lib/GHCup/Prelude.hs view
@@ -1,10 +1,10 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-} {-| Module : GHCup.Prelude@@ -20,18 +20,26 @@ module GHCup.Prelude (module GHCup.Prelude, module GHCup.Prelude.Internal,+ module GHCup.Prelude.JSON,+ module GHCup.Prelude.File,+ module GHCup.Prelude.Logger,+ module GHCup.Prelude.Version, #if defined(IS_WINDOWS)- module GHCup.Prelude.Windows+ module GHCup.Prelude.Windows, #else- module GHCup.Prelude.Posix+ module GHCup.Prelude.Posix, #endif ) where -import GHCup.Errors-import GHCup.Prelude.Internal-import GHCup.Types.Optics (HasLog)-import GHCup.Prelude.Logger (logWarn)+import GHCup.Errors+import GHCup.Prelude.File+import GHCup.Prelude.Internal+import GHCup.Prelude.JSON+import GHCup.Prelude.Logger+import GHCup.Prelude.Version+import GHCup.Types ( EnvUnion (..) )+import GHCup.Types.Optics ( HasLog ) #if defined(IS_WINDOWS) import GHCup.Prelude.Windows #else@@ -40,13 +48,13 @@ import Control.Monad.IO.Class import Control.Monad.Reader+import Data.List ( intercalate )+import qualified Data.Map.Strict as Map+import qualified Data.Text as T import Data.Variant.Excepts-import Text.PrettyPrint.HughesPJClass ( Pretty )-import qualified Data.Text as T-import System.Environment (getEnvironment)-import qualified Data.Map.Strict as Map-import System.FilePath-import Data.List (intercalate)+import System.Environment ( getEnvironment )+import System.FilePath+import Text.PrettyPrint.HughesPJClass ( Pretty, prettyShow ) @@ -59,6 +67,8 @@ , Monad m) => Excepts es m () -> Excepts '[] m () catchWarn = catchAllE @_ @es (\v -> lift $ logWarn (T.pack . prettyHFError $ v)) +prettyText :: Pretty a => a -> T.Text+prettyText = T.pack . prettyShow runBothE' :: forall e m a b . ( Monad m@@ -81,6 +91,14 @@ (_ , VLeft e ) -> throwSomeE e (VRight _, VRight _) -> pure () +augmentEnvironment :: [(String, String)] -> EnvUnion -> IO [(String, String)]+augmentEnvironment (Map.fromList -> addEnv) PreferSpec = do+ cEnv <- Map.fromList <$> getEnvironment+ pure (Map.toList $ Map.union addEnv cEnv)+augmentEnvironment (Map.fromList -> addEnv) PreferSystem = do+ cEnv <- Map.fromList <$> getEnvironment+ pure (Map.toList $ Map.union cEnv addEnv)+augmentEnvironment addEnv OnlySpec = pure addEnv addToPath :: [FilePath] -> Bool -- ^ if False will prepend
lib/GHCup/Prelude/Attoparsec.hs view
@@ -1,5 +1,5 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-} {-| Module : GHCup.Utils.Attoparsec@@ -12,15 +12,15 @@ -} module GHCup.Prelude.Attoparsec where -import Control.Applicative+import Control.Applicative #if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )+import Control.Monad.Fail ( MonadFail ) #endif-import Data.ByteString (ByteString)-import Data.ByteString.Internal (w2c)-import Data.Functor+import Data.ByteString ( ByteString )+import Data.ByteString.Internal ( w2c )+import Data.Functor -import Data.Attoparsec.ByteString as AP+import Data.Attoparsec.ByteString as AP
lib/GHCup/Prelude/File.hs view
@@ -1,20 +1,22 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-} module GHCup.Prelude.File ( mergeFileTree, copyFileE, findFilesDeep,+ getFilesDeep, getDirectoryContentsRecursive, getDirectoryContentsRecursiveUnsafe, recordedInstallationFile,+ recordedInstallationSpecFile,+ recordedSetVersionFile, module GHCup.Prelude.File.Search, chmod_755,@@ -44,10 +46,10 @@ createLink ) where -import GHCup.Utils.Dirs-import GHCup.Prelude.Logger.Internal (logInfo, logDebug)-import GHCup.Prelude.Internal import GHCup.Prelude.File.Search+import GHCup.Prelude.Internal+import GHCup.Prelude.Logger.Internal ( logDebug, logDebug2 )+import GHCup.Query.GHCupDirs #if IS_WINDOWS import GHCup.Prelude.File.Windows import GHCup.Prelude.Windows@@ -55,26 +57,25 @@ import GHCup.Prelude.File.Posix import GHCup.Prelude.Posix #endif-import GHCup.Errors-import GHCup.Types-import GHCup.Types.Optics+import GHCup.Errors+import GHCup.System.Directory+import GHCup.Types+import GHCup.Types.Optics -import Text.Regex.Posix import Conduit-import qualified Data.Conduit.Combinators as C import Control.Exception.Safe import Control.Monad.Reader import Data.ByteString ( ByteString )+import qualified Data.Conduit.Combinators as C import Data.Variant.Excepts import System.FilePath-import Text.PrettyPrint.HughesPJClass (prettyShow)+import Text.PrettyPrint.HughesPJClass ( prettyShow )+import Text.Regex.Posix -import qualified Data.Text as T-import Control.DeepSeq (force)-import Control.Exception (evaluate)-import GHC.IO.Exception-import System.IO.Error-import Control.Monad (when, forM_, filterM)+import Control.Monad ( filterM, forM_, when, unless )+import qualified Data.Text as T+import GHC.IO.Exception+import System.IO.Error -- | Merge one file tree to another given a copy operation.@@ -95,20 +96,21 @@ => GHCupPath -- ^ source base directory from which to install findFiles -> InstallDirResolved -- ^ destination base dir -> Tool- -> GHCTargetVersion+ -> TargetVersion -> (FilePath -> FilePath -> m ()) -- ^ file copy operation+ -> Bool -- ^ whether to abort if the DB file already exists -> Excepts '[MergeFileTreeError] m ()-mergeFileTree _ (GHCupBinDir fp) _ _ _ =+mergeFileTree _ (GHCupBinDir fp) _ _ _ _ = throwIO $ userError ("mergeFileTree: internal error, called on " <> fp)-mergeFileTree sourceBase destBase tool v' copyOp = do- lift $ logInfo $ "Merging file tree from \""+mergeFileTree sourceBase destBase tool v' copyOp append = do+ lift $ logDebug $ "Merging file tree from \"" <> T.pack (fromGHCupPath sourceBase) <> "\" to \"" <> T.pack (fromInstallDir destBase) <> "\"" recFile <- recordedInstallationFile tool v' - wrapInExcepts $ do+ handleIO (\e -> throwE $ MergeFileTreeErrorPreCondition e (fromGHCupPath sourceBase) (fromInstallDir destBase)) $ do -- These checks are not atomic, but we perform them to have -- the opportunity to abort before copying has started. --@@ -118,37 +120,19 @@ -- we only record for non-isolated installs when (isSafeDir destBase) $ do- whenM (liftIO $ doesFileExist recFile)+ whenM (fmap (&& not append) $ liftIO $ doesFileExist recFile) $ throwIO $ userError ("mergeFileTree: DB file " <> recFile <> " already exists!") liftIO $ createDirectoryIfMissing True (takeDirectory recFile) -- we want the cleanup action to leak through in case of exception- onE_ (cleanupOnPartialInstall recFile) $ wrapInExcepts $ do- logDebug "Starting merge"+ logDebug "Starting merge"+ handleIO (\e -> throwE $ MergeFileTreeError e (fromInstallDir destBase) tool v') $ lift $ runConduitRes $ getDirectoryContentsRecursive sourceBase .| C.mapM_ (\f -> do lift $ copy f- logDebug $ T.pack "Recording installed file: " <> T.pack f+ logDebug2 $ T.pack "Recording installed file: " <> T.pack f recordInstalledFile f recFile) where- wrapInExcepts = handleIO (\e -> throwE $ MergeFileTreeError e (fromGHCupPath sourceBase) (fromInstallDir destBase))-- cleanupOnPartialInstall recFile = when (isSafeDir destBase) $ do- (force -> !l) <- hideErrorDef [NoSuchThing] [] $ lines <$> liftIO- (readFile recFile >>= evaluate)- logDebug "Deleting recorded files due to partial install"- forM_ l $ \f -> do- let dest = fromInstallDir destBase </> dropDrive f- logDebug $ "rm -f " <> T.pack f- hideError NoSuchThing $ rmFile dest- pure ()- logDebug $ "rm -f " <> T.pack recFile- hideError NoSuchThing $ rmFile recFile- logDebug $ "rm -f " <> T.pack (fromInstallDir destBase)- hideError UnsatisfiedConstraints $ hideError NoSuchThing $- removeEmptyDirsRecursive (fromInstallDir destBase)-- recordInstalledFile f recFile = when (isSafeDir destBase) $ liftIO $ appendFile recFile (f <> "\n") @@ -194,17 +178,46 @@ findFilesDeep path regex = runResourceT $ sourceToList $ getDirectoryContentsRecursive path .| C.filter (match regex) +getFilesDeep :: GHCupPath -> IO [FilePath]+getFilesDeep path =+ runResourceT $ sourceToList $ getDirectoryContentsRecursive path + recordedInstallationFile :: ( MonadReader env m , HasDirs env+ , MonadIO m ) => Tool- -> GHCTargetVersion+ -> TargetVersion -> m FilePath recordedInstallationFile t v' = do Dirs {..} <- getDirs pure (fromGHCupPath dbDir </> prettyShow t </> T.unpack (tVerToText v')) +recordedInstallationSpecFile :: ( MonadReader env m+ , HasDirs env+ )+ => Tool+ -> TargetVersion+ -> m FilePath+recordedInstallationSpecFile t v' = do+ Dirs {..} <- getDirs+ pure (fromGHCupPath dbDir </> prettyShow t </> T.unpack (tVerToText v') <.> "spec")++recordedSetVersionFile ::+ ( MonadReader env m+ , HasDirs env+ )+ => Tool+ -> Maybe T.Text+ -> m FilePath+recordedSetVersionFile t Nothing = do+ Dirs {..} <- getDirs+ pure (fromGHCupPath dbDir </> prettyShow t </> "set")+recordedSetVersionFile t (Just target) = do+ Dirs {..} <- getDirs+ pure (fromGHCupPath dbDir </> prettyShow t </> T.unpack target <.> "set")+ removeDirIfEmptyOrIsSymlink :: (MonadMask m, MonadIO m, MonadCatch m) => FilePath -> m () removeDirIfEmptyOrIsSymlink filepath = hideError UnsatisfiedConstraints $@@ -242,14 +255,13 @@ where isSymlinkDir e = do ft <- pathIsSymbolicLink p- case ft of- True -> do+ if ft+ then+ do rp <- canonicalizePath p rft <- doesDirectoryExist rp- case rft of- True -> pure ()- _ -> throwIO e- _ -> throwIO e+ unless rft $ throwIO e+ else throwIO e -- https://github.com/haskell/directory/issues/110
lib/GHCup/Prelude/File/Posix.hs view
@@ -1,8 +1,9 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE CApiFFI #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP #-} {-| Module : GHCup.Utils.File.Posix@@ -15,28 +16,31 @@ -} module GHCup.Prelude.File.Posix where -import Conduit-import Control.Exception.Safe-import Foreign.C.String-import Foreign.C.Error-import Foreign.C.Types-import System.IO ( hClose, hSetBinaryMode )-import System.IO.Error hiding ( catchIOError )-import System.FilePath-import System.Directory ( removeFile, pathIsSymbolicLink, getSymbolicLinkTarget, doesPathExist )-import System.Posix.Error ( throwErrnoPathIfMinus1Retry )-import System.Posix.Internals ( withFilePath )-import System.Posix.Files-import System.Posix.Types+import Conduit+import Control.Exception.Safe+import Control.Monad (unless)+import Foreign.C.Error+import Foreign.C.String+import Foreign.C.Types+import System.Directory+ ( doesPathExist, getSymbolicLinkTarget, pathIsSymbolicLink, removeFile )+import System.FilePath+import System.IO ( hClose, hSetBinaryMode )+import System.IO.Error hiding ( catchIOError )+import System.Posix.Error ( throwErrnoPathIfMinus1Retry )+import System.Posix.Files+import System.Posix.Internals ( withFilePath )+import System.Posix.Types -import qualified System.Posix.Directory as PD-import qualified System.Posix.Files as PF-import qualified System.Posix.IO as SPI-import qualified System.Posix as Posix-import qualified GHCup.Prelude.File.Posix.Foreign as FD-import GHCup.Prelude.File.Posix.Traversals-import GHC.IO.Exception (IOException(ioe_type), IOErrorType (..))+import GHC.IO.Exception+ ( IOException (ioe_errno) )+import qualified GHCup.Prelude.File.Posix.Foreign as FD+import GHCup.Prelude.File.Posix.Traversals+import qualified System.Posix as Posix+import qualified System.Posix.Directory as PD+import qualified System.Posix.Files as PF+import qualified System.Posix.IO as SPI import qualified Data.Conduit.Combinators as C @@ -112,7 +116,15 @@ (handleIO (\e -> if -- if we copy from regular file to symlink, we need -- to delete the symlink- | ioe_type e == InvalidArgument+ | ioe_errno e == Just ((\(Errno x) -> x)+#if defined(IS_FREEBSD)+-- https://man.freebsd.org/cgi/man.cgi?open(2)+ eMLINK+#else+-- https://man7.org/linux/man-pages/man2/open.2.html+ eLOOP+#endif+ ) , not fail' -> do removeLink to openFdHandle'@@ -221,10 +233,8 @@ -> IO () recreateSymlink symsource newsym fail' = do sympoint <- readSymbolicLink symsource- case fail' of- True -> pure ()- False ->- handleIO (\e -> if doesNotExistErrorType == ioeGetErrorType e then pure () else liftIO . ioError $ e) $ deleteFile newsym+ unless fail' $+ handleIO (\e -> if doesNotExistErrorType == ioeGetErrorType e then pure () else liftIO . ioError $ e) $ deleteFile newsym createSymbolicLink sympoint newsym
lib/GHCup/Prelude/File/Search.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE DataKinds #-} module GHCup.Prelude.File.Search ( module GHCup.Prelude.File.Search@@ -9,31 +9,32 @@ , CapturedProcess(..) ) where -import GHCup.Prelude.Internal ((!?))-import GHCup.Types(ProcessError(..), CapturedProcess(..))+import GHCup.Prelude.Internal ( (!?) )+import GHCup.Types ( CapturedProcess (..), ProcessError (..) ) -import Control.Monad (forM)-import Control.Monad.Reader-import Data.Maybe-import Data.Text ( Text )-import Data.Void-import GHC.IO.Exception-import System.Directory hiding ( removeDirectory- , removeDirectoryRecursive- , removePathForcibly- , findFiles- , makeAbsolute- )-import System.FilePath-import Text.Regex.Posix+import Control.Monad ( forM )+import Control.Monad.Reader+import Data.Maybe+import Data.Text ( Text )+import Data.Void+import GHC.IO.Exception+import System.Directory hiding+ ( findFiles+ , makeAbsolute+ , removeDirectory+ , removeDirectoryRecursive+ , removePathForcibly+ )+import System.FilePath+import Text.Regex.Posix -import qualified Data.Text as T-import qualified Text.Megaparsec as MP-import Control.Exception.Safe (handleIO)-import System.Directory.Internal.Prelude (ioeGetErrorType)-import Data.Variant.Excepts (Excepts)-import GHCup.Errors (NotFoundInPATH(..))+import Control.Exception.Safe ( handleIO )+import qualified Data.Text as T+import Data.Variant.Excepts ( Excepts )+import GHCup.Errors ( NotFoundInPATH (..) )+import System.Directory.Internal.Prelude ( ioeGetErrorType )+import qualified Text.Megaparsec as MP makeAbsolute :: MonadIO m => FilePath -> Excepts '[NotFoundInPATH] m FilePath@@ -71,7 +72,7 @@ if b' then t else f --- | Check wether a binary is shadowed by another one that comes before+-- | Check whether a binary is shadowed by another one that comes before -- it in PATH. Returns the path to said binary, if any. isShadowed :: FilePath -> IO (Maybe FilePath) isShadowed p = do
lib/GHCup/Prelude/File/Windows.hs view
@@ -1,6 +1,6 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE DataKinds #-} {-| Module : GHCup.Utils.File.Windows@@ -13,19 +13,20 @@ -} module GHCup.Prelude.File.Windows where -import GHCup.Utils.Dirs-import GHCup.Prelude.Internal+import GHCup.Prelude.Internal+import GHCup.Query.GHCupDirs+import GHCup.System.Directory import Control.Monad.Reader import Data.List-import qualified GHC.Unicode as U+import qualified GHC.Unicode as U import System.FilePath-import qualified System.IO.Error as IOE+import qualified System.IO.Error as IOE -import qualified System.Win32.Info as WS-import qualified System.Win32.File as WS+import qualified System.Win32.File as WS+import qualified System.Win32.Info as WS -import Data.Bits ((.&.))+import Data.Bits ( (.&.) ) import Conduit import qualified Data.Conduit.Combinators as C@@ -115,8 +116,8 @@ '\\' : '?' : '?' : '\\' : _ -> simplifiedPath '\\' : '\\' : '?' : '\\' : _ -> simplifiedPath '\\' : '\\' : '.' : '\\' : _ -> simplifiedPath- '\\' : subpath@('\\' : _) -> "\\\\?\\UNC" <> subpath- _ -> "\\\\?\\" <> simplifiedPath+ '\\' : subpath@('\\' : _) -> "\\\\?\\UNC" <> subpath+ _ -> "\\\\?\\" <> simplifiedPath where simplifiedPath = simplify path @@ -128,7 +129,7 @@ simplifyWindows path = case drive' of "\\\\?\\" -> drive' <> subpath- _ -> simplifiedPath+ _ -> simplifiedPath where simplifiedPath = joinDrive drive' subpath' (drive, subpath) = splitDrive path@@ -182,9 +183,9 @@ "." -> go ys' xs ".." -> case ys' of- [] -> go (x : ys') xs+ [] -> go (x : ys') xs ".." : _ -> go (x : ys') xs- _ : ys -> go ys xs+ _ : ys -> go ys xs _ -> go (x : ys') xs rawPrependCurrentDirectory :: FilePath -> IO FilePath
lib/GHCup/Prelude/Internal.hs view
@@ -1,10 +1,10 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-} {-| Module : GHCup.Prelude.Internal@@ -21,37 +21,38 @@ module GHCup.Prelude.Internal where -import Control.Applicative-import Control.Exception.Safe-import Control.Monad-import Control.Monad.IO.Class-import Control.Monad.Reader-import Data.Bifunctor-import Data.ByteString ( ByteString )-import Data.List ( intercalate, stripPrefix, isPrefixOf, dropWhileEnd )-import Data.Maybe-import Data.String-import Data.Text ( Text )-import Data.Versions-import Data.Word8 hiding ( isDigit )-import Data.Variant.Types-import Data.Variant.Excepts-import System.IO.Error--import Control.Retry-import GHC.IO.Exception+import Control.Applicative+import Control.Exception.Safe+import Control.Monad+import Control.Monad.IO.Class+import Control.Monad.Reader+import Control.Retry+import Data.Bifunctor+import Data.ByteString ( ByteString )+import Data.Data ( Proxy )+import Data.List+ ( dropWhileEnd, intercalate, isPrefixOf, stripPrefix )+import Data.Maybe+import Data.String+import Data.Text ( Text )+import Data.Variant.Excepts+import Data.Variant.Types+import Data.Versions+import Data.Word8 hiding ( isDigit )+import GHC.IO.Exception+import System.IO.Error -import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy as L-import qualified Data.Strict.Maybe as S-import qualified Data.List.Split as Split-import qualified Data.Text as T-import qualified Data.Text.Encoding as E-import qualified Data.Text.Encoding.Error as E-import qualified Data.Text.Lazy as TL-import qualified Data.Text.Lazy.Builder as B-import qualified Data.Text.Lazy.Builder.Int as B-import qualified Data.Text.Lazy.Encoding as TLE+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as L+import qualified Data.List.Split as Split+import qualified Data.Strict.Maybe as S+import qualified Data.Text as T+import qualified Data.Text.Encoding as E+import qualified Data.Text.Encoding.Error as E+import qualified Data.Text.Lazy as TL+import qualified Data.Text.Lazy.Builder as B+import qualified Data.Text.Lazy.Builder.Int as B+import qualified Data.Text.Lazy.Encoding as TLE @@ -135,7 +136,14 @@ -> Excepts es m a (!?) em e = lift em >>= (?? e) +(!!?) :: forall e es es' a m+ . (Monad m, e :< es', es' ~ Concat es '[e])+ => Excepts es m (Maybe a)+ -> e+ -> Excepts es' m a+(!!?) em e = appendE @'[e] em >>= (?? e) + lE :: forall e es a m . (Monad m, e :< es) => Either e a -> Excepts es m a lE = liftE . veitherToExcepts . fromEither @@ -220,7 +228,7 @@ hideExcept' :: forall e es es' m . (Monad m, e :< es, LiftVariant (Remove e es) es')- => e+ => Proxy e -> Excepts es m () -> Excepts es' m () hideExcept' _ =@@ -258,7 +266,7 @@ throwMaybe :: (Exception a, MonadThrow m) => a -> Maybe b -> m b throwMaybe a m = case m of Nothing -> throwM a- Just r -> pure r+ Just r -> pure r throwMaybeM :: (Exception a, MonadThrow m) => a -> m (Maybe b) -> m b throwMaybeM a am = do@@ -515,3 +523,4 @@ breakOn needle haystack | needle `isPrefixOf` haystack = ([], haystack) breakOn _ [] = ([], []) breakOn needle (x:xs) = first (x:) $ breakOn needle xs+
+ lib/GHCup/Prelude/JSON.hs view
@@ -0,0 +1,14 @@+module GHCup.Prelude.JSON where++import qualified Data.Text as T++mapHead :: (a -> a) -> [a] -> [a]+mapHead _ [] = []+mapHead f (x:xs) = f x : xs++lower :: Char -> Char+lower = T.head . T.toLower . T.singleton++upper :: Char -> Char+upper = T.head . T.toUpper . T.singleton+
lib/GHCup/Prelude/Logger.hs view
@@ -1,6 +1,6 @@+{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE OverloadedStrings #-} {-| Module : GHCup.Utils.Logger@@ -19,25 +19,25 @@ ) where -import GHCup.Prelude.Logger.Internal-import GHCup.Types-import GHCup.Types.Optics-import GHCup.Utils.Dirs (fromGHCupPath)-import GHCup.Prelude.Internal-import GHCup.Prelude.File.Search (findFiles)-import GHCup.Prelude.File (recycleFile)-import GHCup.Prelude.String.QQ+import GHCup.Prelude.File ( recycleFile )+import GHCup.Prelude.File.Search ( findFiles )+import GHCup.Prelude.Internal+import GHCup.Prelude.Logger.Internal+import GHCup.Prelude.String.QQ+import GHCup.Query.GHCupDirs ( fromGHCupPath )+import GHCup.Types+import GHCup.Types.Optics -import Control.Exception.Safe-import Control.Monad-import Control.Monad.IO.Class-import Control.Monad.Reader-import Prelude hiding ( appendFile )-import System.FilePath-import System.IO.Error-import Text.Regex.Posix+import Control.Exception.Safe+import Control.Monad+import Control.Monad.IO.Class+import Control.Monad.Reader+import Prelude hiding ( appendFile )+import System.FilePath+import System.IO.Error+import Text.Regex.Posix -import qualified Data.ByteString as B+import qualified Data.ByteString as B
lib/GHCup/Prelude/Logger/Internal.hs view
@@ -1,6 +1,6 @@+{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE OverloadedStrings #-} {-| Module : GHCup.Utils.Logger.Internal@@ -15,18 +15,18 @@ -} module GHCup.Prelude.Logger.Internal where -import GHCup.Types-import GHCup.Types.Optics+import GHCup.Types+import GHCup.Types.Optics -import Control.Monad-import Control.Monad.IO.Class-import Control.Monad.Reader-import Data.Text ( Text )-import Optics-import Prelude hiding ( appendFile )-import System.Console.Pretty+import Control.Monad+import Control.Monad.IO.Class+import Control.Monad.Reader+import Data.Text ( Text )+import Optics+import Prelude hiding ( appendFile )+import System.Console.Pretty -import qualified Data.Text as T+import qualified Data.Text as T logInfo :: ( MonadReader env m , LabelOptic' "loggerConfig" A_Lens env LoggerConfig@@ -50,8 +50,16 @@ ) => Text -> m ()-logDebug = logInternal Debug+logDebug = logInternal (Debug 1) +logDebug2 :: ( MonadReader env m+ , LabelOptic' "loggerConfig" A_Lens env LoggerConfig+ , MonadIO m+ )+ => Text+ -> m ()+logDebug2 = logInternal (Debug 2)+ logError :: ( MonadReader env m , LabelOptic' "loggerConfig" A_Lens env LoggerConfig , MonadIO m@@ -71,12 +79,12 @@ LoggerConfig {..} <- gets @"loggerConfig" let color' c = if fancyColors then color c else id let style' = case logLevel of- Debug -> style Bold . color' Blue+ Debug _ -> style Bold . color' Blue Info -> style Bold . color' Green Warn -> style Bold . color' Yellow Error -> style Bold . color' Red let l = case logLevel of- Debug -> style' "[ Debug ]"+ Debug _ -> style' "[ Debug ]" Info -> style' "[ Info ]" Warn -> style' "[ Warn ]" Error -> style' "[ Error ]"@@ -89,12 +97,19 @@ . fmap (\line' -> style' "[ ... ] " <> line' ) $ xs - when (lcPrintDebug || (not lcPrintDebug && (logLevel /= Debug)))- $ liftIO $ consoleOutter out+ case logLevel of+ Debug lvl+ | Just cfgLvl <- lcPrintDebugLvl+ , lvl <= cfgLvl+ -> liftIO $ consoleOutter out+ Info -> liftIO $ consoleOutter out+ Warn -> liftIO $ consoleOutter out+ Error -> liftIO $ consoleOutter out+ _ -> pure () -- raw output let lr = case logLevel of- Debug -> "Debug:"+ Debug _ -> "Debug:" Info -> "Info:" Warn -> "Warn:" Error -> "Error:"
lib/GHCup/Prelude/MegaParsec.hs view
@@ -1,5 +1,5 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-} {-| Module : GHCup.Prelude.MegaParsec@@ -12,24 +12,32 @@ -} module GHCup.Prelude.MegaParsec where -import GHCup.Types+import GHCup.Types -import Control.Applicative+import Control.Applicative ( Alternative((<|>), many) )+ #if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )+import Control.Monad.Fail ( MonadFail ) #endif-import Data.Functor-import Data.Maybe-import Data.Text ( Text )-import Data.Versions-import Data.Void-import System.FilePath -import qualified Data.List.NonEmpty as NE-import qualified Data.Text as T-import qualified Text.Megaparsec as MP+import Data.Foldable ( asum )+import Data.Functor+import Data.Maybe+import Data.Text ( Text )+import Data.Versions+import Data.Void+import System.FilePath+import Text.PrettyPrint.HughesPJClass ( prettyShow ) +import Data.List.NonEmpty ( NonEmpty ((:|)) )+import qualified Data.List.NonEmpty as NE+import qualified Data.Text as T+import qualified Text.Megaparsec as MP+import qualified Text.Megaparsec.Char as MPC+import qualified Text.Megaparsec.Char.Lexer as L ++ choice' :: (MonadFail f, MP.MonadParsec e s f) => [f a] -> f a choice' [] = fail "Empty list" choice' [x ] = x@@ -76,13 +84,12 @@ MP.setInput ver version' - -- | Extracts target triple and version from e.g. -- * armv7-unknown-linux-gnueabihf-8.8.3 -- * armv7-unknown-linux-gnueabihf-8.8.3-ghcTargetVerP :: MP.Parsec Void Text GHCTargetVersion+ghcTargetVerP :: MP.Parsec Void Text TargetVersion ghcTargetVerP =- (\x y -> GHCTargetVersion x y)+ (\x y -> TargetVersion x y) <$> (MP.try (Just <$> parseUntil1 (MP.chunk "-" *> verP') <* MP.chunk "-") <|> ((\ _ x -> x) Nothing <$> mempty) )@@ -104,7 +111,32 @@ then pure $ prettyVer v else fail "Oh" +ghcLinkVersion :: MP.Parsec Void Text TargetVersion+ghcLinkVersion =+ (\x y -> TargetVersion x y)+ <$>+ (MP.try (Just <$> parseUntil1 (MP.chunk "-ghc-" *> verP') <* MP.chunk "-")+ <|> ((\ _ x -> x) Nothing <$> mempty)+ )+ <*> (MP.chunk "ghc-" *> version' <* MP.eof)+ where+ verP' :: MP.Parsec Void Text Text+ verP' = do+ v <- version'+ let startsWithDigits =+ and+ . take 3+ . map (\case+ Numeric _ -> True+ Alphanum _ -> False)+ . NE.toList+ . (\(Chunks nec) -> nec)+ $ _vChunks v+ if startsWithDigits && isNothing (_vEpoch v)+ then pure $ prettyVer v+ else fail "Oh" + verP :: MP.Parsec Void Text Text -> MP.Parsec Void Text Versioning verP suffix = do ver <- parseUntil suffix@@ -140,13 +172,98 @@ -- Obtain the version from the link or shim path -- ../ghc/<ver>/bin/ghc -- ../ghc/<ver>/bin/ghc-<ver>-ghcVersionFromPath :: MP.Parsec Void Text GHCTargetVersion-ghcVersionFromPath =- do- beforeBin <- parseUntil1 binDir <* MP.some pathSep- MP.setInput beforeBin- _ <- parseTillLastPathSep- ghcTargetVerP- where- binDir = MP.some pathSep <* MP.chunk "bin" *> MP.some pathSep <* MP.takeWhile1P Nothing (not . isPathSeparator) <* MP.eof- parseTillLastPathSep = (MP.try (parseUntil1 pathSep *> MP.some pathSep) *> parseTillLastPathSep) <|> pure ()+ghcVersionFromPath :: MP.Parsec Void Text TargetVersion+ghcVersionFromPath = toolVersionFromPath ghc++toolVersionFromPath :: Tool -> MP.Parsec Void Text TargetVersion+toolVersionFromPath tool = MP.try legacyParse <|> newParse+ where+ legacyParse = do+ beforeBin <- parseUntil1 binDir <* MP.some pathSep+ MP.setInput beforeBin+ _ <- parseTillLastPathSep+ ghcTargetVerP+ where+ binDir = MP.some pathSep <* MP.chunk "bin" *> MP.some pathSep <* MP.takeWhile1P Nothing (not . isPathSeparator) <* MP.eof+ parseTillLastPathSep = (MP.try (parseUntil1 pathSep *> MP.some pathSep) *> parseTillLastPathSep) <|> pure ()++ newParse = asum $ pathSeparators <&> MP.try . newParse'+ newParse' sep = do+ let toolPath = T.pack $ [sep] <> prettyShow tool <> [sep]+ ver <- parseUntilEmpty toolPath *> MP.chunk toolPath *> parseUntil1 (MP.chunk $ T.singleton sep)+ MP.setInput ver+ ghcTargetVerP+++{--+ -- this doesn'twork because parseUntilEmpty can't take a parser as input+--}++ghcTargetVerRevP :: MP.Parsec Void Text TargetVersionReq+ghcTargetVerRevP = MP.try withRev <|> ((`TargetVersionReq` Nothing) <$> ghcTargetVerP)+ where+ withRev = do+ verText <- parseUntilEmpty "-r"+ rev <- MP.chunk "-r" *> L.decimal+ MP.setInput verText+ tver <- ghcTargetVerP+ pure $ TargetVersionReq tver (Just rev)++verRevP :: MP.Parsec Void Text VersionReq+verRevP = MP.try withRev <|> ((`VersionReq` Nothing) <$> version')+ where+ withRev = do+ verText <- parseUntilEmpty "-r"+ rev <- MP.chunk "-r" *> L.decimal+ MP.setInput verText+ ver <- version'+ pure $ VersionReq ver (Just rev)++-- find a parse in a greedy manner+parseUntilEmpty :: Text -> MP.Parsec Void Text Text+parseUntilEmpty needle = go+ where+ go = do+ prefix <- parseUntil (MP.chunk needle)+ s2 <- MP.try ((<>) <$> MP.chunk needle <*> go) <|> mempty+ pure $ prefix <> s2++versionCmpP :: MP.Parsec Void T.Text VersionCmp+versionCmpP = either (fail . T.unpack) pure =<< (translate <$> (MPC.space *> MP.try (MP.takeWhileP Nothing (`elem` ['>', '<', '=']))) <*> (MPC.space *> versioningEnd))+ where+ translate ">" v = Right $ VR_gt v+ translate ">=" v = Right $ VR_gteq v+ translate "<" v = Right $ VR_lt v+ translate "<=" v = Right $ VR_lteq v+ translate "==" v = Right $ VR_eq v+ translate "" v = Right $ VR_eq v+ translate c _ = Left $ "unexpected comparator: " <> c++versionRangeP :: MP.Parsec Void T.Text VersionRange+versionRangeP = go <* MP.eof+ where+ go =+ MP.try orParse+ <|> MP.try (fmap SimpleRange andParse)+ <|> fmap (SimpleRange . pure) versionCmpP++ orParse :: MP.Parsec Void T.Text VersionRange+ orParse =+ (\a o -> OrRange a o)+ <$> (MP.try andParse <|> fmap pure versionCmpP)+ <*> (MPC.space *> MP.chunk "||" *> MPC.space *> go)++ andParse :: MP.Parsec Void T.Text (NonEmpty VersionCmp)+ andParse =+ fmap (\h t -> h :| t)+ (MPC.space *> MP.chunk "(" *> MPC.space *> versionCmpP)+ <*> MP.try (MP.many (MPC.space *> MP.chunk "&&" *> MPC.space *> versionCmpP))+ <* MPC.space+ <* MP.chunk ")"+ <* MPC.space++versioningEnd :: MP.Parsec Void T.Text Versioning+versioningEnd =+ MP.try (verP (MP.chunk " " <|> MP.chunk ")" <|> MP.chunk "&&") <* MPC.space)+ <|> versioning'+
lib/GHCup/Prelude/Posix.hs view
@@ -4,7 +4,7 @@ -- | Enables ANSI support on windows, does nothing on unix. -- -- Returns 'Left str' on errors and 'Right bool' on success, where--- 'bool' markes whether ansi support was already enabled.+-- 'bool' marks whether ansi support was already enabled. -- -- This function never crashes. --
lib/GHCup/Prelude/Process/Posix.hs view
@@ -1,9 +1,9 @@+{-# LANGUAGE CApiFFI #-} {-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE MultiWayIf #-}-{-# LANGUAGE CApiFFI #-} {-| Module : GHCup.Utils.File.Posix@@ -16,47 +16,44 @@ -} module GHCup.Prelude.Process.Posix where -import GHCup.Utils.Dirs-import GHCup.Prelude.File-import GHCup.Prelude.File.Posix-import GHCup.Prelude-import GHCup.Prelude.Logger-import GHCup.Types-import GHCup.Types.Optics+import GHCup.Prelude+import GHCup.Prelude.File.Posix+import GHCup.Query.GHCupDirs+import GHCup.Types+import GHCup.Types.Optics import Control.Concurrent import Control.Concurrent.Async-import qualified Control.Exception as E+import qualified Control.Exception as E import Control.Exception.Safe import Control.Monad import Control.Monad.Reader import Control.Monad.Trans.State.Strict-import Data.ByteString ( ByteString )+import Data.ByteString ( ByteString ) import Data.Foldable import Data.IORef-import Data.Sequence ( Seq, (|>) ) import Data.List+import Data.Sequence ( Seq, (|>) ) import Data.Word8 import GHC.IO.Exception-import System.IO ( stderr )-import System.IO.Error hiding ( catchIOError ) import System.FilePath+import System.IO ( stderr )+import System.IO.Error hiding ( catchIOError ) import System.Posix.Directory import System.Posix.IO-import System.Posix.Process ( ProcessStatus(..) )+import System.Posix.Process ( ProcessStatus (..) ) import System.Posix.Types -import qualified Control.Exception as EX-import qualified Data.Sequence as Sq-import qualified Data.Text as T-import qualified Data.Text.Encoding as E-import qualified System.Posix.Process as SPP-import qualified System.Console.Terminal.Size as TP-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as BL-import qualified System.Posix.IO.ByteString.Ext- as SPIB+import qualified Control.Exception as EX+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as BL+import qualified Data.Sequence as Sq+import qualified Data.Text as T+import qualified Data.Text.Encoding as E+import qualified System.Console.Terminal.Size as TP+import qualified System.Posix.IO.ByteString.Ext as SPIB+import qualified System.Posix.Process as SPP @@ -117,9 +114,10 @@ $ forkIO $ EX.handle (\(_ :: IOException) -> pure ()) $ EX.finally- (if verbose- then tee fd stdoutRead- else printToRegion fd stdoutRead 6 pState no_color+ (case verbose of+ Verbosity i+ | i > 0 -> tee fd stdoutRead+ | otherwise -> printToRegion fd stdoutRead 6 pState no_color ) (putMVar done ()) @@ -373,7 +371,7 @@ -> Either ProcessError () toProcessError exe args mps = case mps of Just (SPP.Exited (ExitFailure xi)) -> Left $ NonZeroExit xi exe args- Just (SPP.Exited ExitSuccess ) -> Right ()+ Just (SPP.Exited ExitSuccess ) -> Right () Just (Terminated _ _ ) -> Left $ PTerminated exe args Just (Stopped _ ) -> Left $ PStopped exe args Nothing -> Left $ NoSuchPid exe args
lib/GHCup/Prelude/Process/Windows.hs view
@@ -1,6 +1,6 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE DataKinds #-} {-| Module : GHCup.Utils.Process.Windows@@ -13,32 +13,33 @@ -} module GHCup.Prelude.Process.Windows where -import GHCup.Utils.Dirs-import GHCup.Prelude.File.Search-import GHCup.Prelude.Logger.Internal-import GHCup.Types-import GHCup.Types.Optics+import GHCup.Prelude.File.Search+import GHCup.Prelude.Logger.Internal+import GHCup.Query.GHCupDirs+import GHCup.System.Directory+import GHCup.Types+import GHCup.Types.Optics -import Control.Concurrent-import Control.DeepSeq-import Control.Exception.Safe-import Control.Monad-import Control.Monad.Reader-import Data.List-import Foreign.C.Error-import GHC.IO.Exception-import GHC.IO.Handle-import System.Environment-import System.FilePath-import System.IO-import System.Process-import System.Win32.Info (getSystemDirectory, getWindowsDirectory)+import Control.Concurrent+import Control.DeepSeq+import Control.Exception.Safe+import Control.Monad+import Control.Monad.Reader+import Data.List+import Foreign.C.Error+import GHC.IO.Exception+import GHC.IO.Handle+import System.Environment+import System.FilePath+import System.IO+import System.Process+import System.Win32.Info ( getSystemDirectory, getWindowsDirectory ) -import qualified Control.Exception as EX-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as BL-import qualified Data.Map.Strict as Map-import qualified Data.Text as T+import qualified Control.Exception as EX+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as BL+import qualified Data.Map.Strict as Map+import qualified Data.Text as T @@ -49,7 +50,7 @@ -> Either ProcessError () toProcessError exe args exitcode = case exitcode of (ExitFailure xi) -> Left $ NonZeroExit xi exe args- ExitSuccess -> Right ()+ ExitSuccess -> Right () -- | @readCreateProcessWithExitCode@ works exactly like 'readProcessWithExitCode' except that it
lib/GHCup/Prelude/String/QQ.hs view
@@ -1,52 +1,23 @@ {-# LANGUAGE TemplateHaskellQuotes #-} -{-|-Module : GHCup.Utils.String.QQ-Description : String quasi quoters-Copyright : (c) Audrey Tang <audreyt@audreyt.org> 2019, Julian Ospald <hasufell@posteo.de> 2020-License : LGPL-3.0-Maintainer : hasufell@hasufell.de-Stability : experimental-Portability : portable--QuasiQuoter for non-interpolated strings, texts and bytestrings.--The "s" quoter contains a multi-line string with no interpolation at all,-except that the leading newline is trimmed and carriage returns stripped.--@-{-\# LANGUAGE QuasiQuotes #-}-import Data.Text (Text)-import Data.String.QQ-foo :: Text -- "String", "ByteString" etc also works-foo = [s|-Well here is a- multi-line string!-|]-@--Any instance of the IsString type is permitted.--(For GHC versions 6, write "[$s||]" instead of "[s||]".)---}+{-# LANGUAGE QuasiQuotes #-} module GHCup.Prelude.String.QQ ( s ) where -import Data.Char-import GHC.Exts ( IsString(..) )-import Language.Haskell.TH.Quote+import Data.Char+import GHC.Exts ( IsString (..) )+import Language.Haskell.TH.Quote -- | QuasiQuoter for a non-interpolating ASCII IsString literal. -- The pattern portion is undefined. s :: QuasiQuoter s = QuasiQuoter- (\s' -> case all isAscii s' of- True -> (\a -> [|fromString a|]) . trimLeadingNewline . removeCRs $ s'- False -> fail "Not ascii"+ (\s' -> if all isAscii s'+ then (\a -> [|fromString a|]) . trimLeadingNewline . removeCRs $ s'+ else fail "Not ascii" ) (error "Cannot use s as a pattern") (error "Cannot use s as a type")
+ lib/GHCup/Prelude/Version.hs view
@@ -0,0 +1,110 @@+{-|+Module : GHCup.Prelude.Version+Description : Manipulating versions+Copyright : (c) Julian Ospald, 2020+License : LGPL-3.0+Maintainer : hasufell@hasufell.de+Stability : experimental+Portability : portable+-}+module GHCup.Prelude.Version where++import GHCup.Types++import Control.Exception.Safe ( MonadThrow )+import Control.Monad.Catch ( throwM )+import Data.List.NonEmpty ( NonEmpty ((:|)) )+import Data.Text ( Text )+import Data.Void ( Void )+import GHCup.Errors ( ParseError (..) )+import Text.Megaparsec++import qualified Data.List.NonEmpty as NE+import qualified Data.Text as T+import qualified Data.Versions as V+import Control.Exception (Exception(displayException))+++versionCmp :: V.Versioning -> VersionCmp -> Bool+versionCmp ver1 (VR_gt ver2) = ver1 > ver2+versionCmp ver1 (VR_gteq ver2) = ver1 >= ver2+versionCmp ver1 (VR_lt ver2) = ver1 < ver2+versionCmp ver1 (VR_lteq ver2) = ver1 <= ver2+versionCmp ver1 (VR_eq ver2) = ver1 == ver2++versionRange :: V.Versioning -> VersionRange -> Bool+versionRange ver' (SimpleRange cmps) = all (versionCmp ver') cmps+versionRange ver' (OrRange cmps range) =+ versionRange ver' (SimpleRange cmps) || versionRange ver' range++pvpToVersion :: MonadThrow m => V.PVP -> Text -> m V.Version+pvpToVersion pvp_ rest =+ either (\_ -> throwM $ ParseError "Couldn't convert PVP to Version") pure . V.version . (<> rest) . V.prettyPVP $ pvp_++-- | Convert a version to a PVP and unparsable rest.+--+-- -- prop> \v -> let (Just (pvp', r)) = versionToPVP v in pvpToVersion pvp' r === Just v+versionToPVP :: MonadThrow m => V.Version -> m (V.PVP, Text)+versionToPVP (V.Version (Just _) _ _ _) = throwM $ ParseError "Unexpected epoch"+versionToPVP v = case parse pvp'' "Version->PVP" $ V.prettyVer v of+ Left _ -> throwM $ ParseError "Couldn't convert Version to PVP"+ Right r -> pure r+ where+ pvp'' :: Parsec Void T.Text (V.PVP, T.Text)+ pvp'' = do+ p <- V.pvp'+ s <- getParserState+ pure (p, stateInput s)++pvpFromList :: [Int] -> V.PVP+pvpFromList = V.PVP . NE.fromList . fmap fromIntegral++-- | Extract (major, minor) from any version.+getMajorMinorV :: MonadThrow m => V.Version -> m (Int, Int)+getMajorMinorV (V.Version _ (V.Chunks (V.Numeric x :| V.Numeric y : _)) _ _) = pure (fromIntegral x, fromIntegral y)+getMajorMinorV _ = throwM $ ParseError "Could not parse X.Y from version"++matchMajor :: V.Version -> Int -> Int -> Bool+matchMajor v' major' minor' = case getMajorMinorV v' of+ Just (x, y) -> x == major' && y == minor'+ Nothing -> False++-- | Match PVP prefix.+--+-- >>> matchPVPrefix [pver|8.8|] [pver|8.8.4|]+-- True+-- >>> matchPVPrefix [pver|8|] [pver|8.8.4|]+-- True+-- >>> matchPVPrefix [pver|8.10|] [pver|8.8.4|]+-- False+-- >>> matchPVPrefix [pver|8.10|] [pver|8.10.7|]+-- True+matchPVPrefix :: V.PVP -> V.PVP -> Bool+matchPVPrefix (toL -> prefix) (toL -> full) = and $ zipWith (==) prefix full++toL :: V.PVP -> [Int]+toL (V.PVP inner) = fmap fromIntegral $ NE.toList inner++-- | Expand a list of version patterns describing a string such as "%v-%h".+--+-- >>> expandVersionPattern (either (const Nothing) Just $ version "3.4.3") "a386748" "a3867484ccc391daad1a42002c3a2ba6a93c5221" "v0.1.20.0-119-ga386748" "issue-998" [CabalVer, S "-", GitHashShort, S "-", GitHashLong, S "-", GitBranchName, S "-", GitDescribe, S "-coco"]+-- Version {_vEpoch = Nothing, _vChunks = Chunks (Numeric 3 :| [Numeric 4,Numeric 3]), _vRel = Just (Release (Alphanum "a386748-a3867484ccc391daad1a42002c3a2ba6a93c5221-issue-998-v0" :| [Numeric 1,Numeric 20,Alphanum "0-119-ga386748-coco"])), _vMeta = Nothing}+expandVersionPattern :: MonadFail m+ => Maybe V.Version -- ^ cabal ver+ -> String -- ^ git hash (short), if any+ -> String -- ^ git hash (long), if any+ -> String -- ^ git describe output, if any+ -> String -- ^ git branch name, if any+ -> [VersionPattern]+ -> m V.Version+expandVersionPattern cabalVer gitHashS gitHashL gitDescribe gitBranch+ = either (fail . displayException) pure . V.version . T.pack . go+ where+ go [] = ""+ go (CabalVer:xs) = T.unpack (maybe T.empty V.prettyVer cabalVer) <> go xs+ go (GitHashShort:xs) = gitHashS <> go xs+ go (GitHashLong:xs) = gitHashL <> go xs+ go (GitDescribe:xs) = gitDescribe <> go xs+ go (GitBranchName:xs) = gitBranch <> go xs+ go (S str:xs) = str <> go xs+
lib/GHCup/Prelude/Version/QQ.hs view
@@ -1,8 +1,8 @@ {-# OPTIONS_GHC -Wno-orphans #-}-{-# LANGUAGE CPP #-}+{-# LANGUAGE CPP #-} {-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveLift #-}-{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE DeriveLift #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TemplateHaskellQuotes #-} @@ -18,17 +18,17 @@ -} module GHCup.Prelude.Version.QQ where -import Data.Data-import Data.Text ( Text )-import Data.Versions+import Data.Data+import Data.Text ( Text )+import Data.Versions #if !MIN_VERSION_base(4,13,0)-import GHC.Base+import GHC.Base #endif+import qualified Data.Text as T import Language.Haskell.TH-import Language.Haskell.TH.Quote ( QuasiQuoter(..) )-import Language.Haskell.TH.Syntax ( dataToExpQ )-import qualified Data.Text as T-import qualified Language.Haskell.TH.Syntax as TH+import Language.Haskell.TH.Quote ( QuasiQuoter (..) )+import Language.Haskell.TH.Syntax ( dataToExpQ )+import qualified Language.Haskell.TH.Syntax as TH #if !MIN_VERSION_base(4,13,0)
lib/GHCup/Prelude/Windows.hs view
@@ -1,21 +1,21 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-} module GHCup.Prelude.Windows where -import Control.Exception.Safe-import Control.Monad+import Control.Exception.Safe+import Control.Monad #if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )+import Control.Monad.Fail ( MonadFail ) #endif-import Data.Bits+import Data.Bits -import System.Win32.Console-import System.Win32.File hiding ( copyFile )-import System.Win32.Types+import System.Win32.Console+import System.Win32.File hiding ( copyFile )+import System.Win32.Types @@ -23,7 +23,7 @@ -- | Enables ANSI support on windows, does nothing on unix. -- -- Returns 'Left str' on errors and 'Right bool' on success, where--- 'bool' markes whether ansi support was already enabled.+-- 'bool' marks whether ansi support was already enabled. -- -- This function never crashes. --
− lib/GHCup/Prompts.hs
@@ -1,32 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE FlexibleContexts #-}--module GHCup.Prompts- ( PromptQuestion,- PromptResponse (..),- getUserPromptResponse,- )-where--import Control.Monad.Reader-import qualified Data.Text as T-import qualified Data.Text.IO as TIO-import GHCup.Prelude.Logger-import GHCup.Types.Optics-import GHCup.Types (PromptQuestion, PromptResponse(..))--getUserPromptResponse :: ( HasLog env- , MonadReader env m- , MonadIO m)- => PromptQuestion- -> PromptResponse -- ^ Default reponse to use if user doesn't type explicit response (e.g. just presses Return)- -> m PromptResponse-getUserPromptResponse prompt defaultResponse = do- logInfo prompt- resp <- liftIO TIO.getLine- pure $- if T.null resp- then defaultResponse- else if resp `elem` ["YES", "yes", "y", "Y"]- then PromptYes- else PromptNo
+ lib/GHCup/Query.hs view
@@ -0,0 +1,14 @@+module GHCup.Query+ (module GHCup.Query.DB+ ,module GHCup.Query.GHCupDirs+ ,module GHCup.Query.Metadata+ ,module GHCup.Query.Symlink+ ,module GHCup.Query.System+ )+ where++import GHCup.Query.DB+import GHCup.Query.GHCupDirs+import GHCup.Query.Metadata+import GHCup.Query.Symlink+import GHCup.Query.System
+ lib/GHCup/Query/DB.hs view
@@ -0,0 +1,398 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PatternSynonyms #-}++module GHCup.Query.DB where++import GHCup.Errors+import GHCup.Hardcoded.Version+import GHCup.Input.SymlinkSpec+import GHCup.Legacy.Utils+import GHCup.Prelude+import GHCup.Prelude.MegaParsec+import GHCup.Query.GHCupDirs+import GHCup.Types+import GHCup.Types.Optics++import Control.DeepSeq ( force )+import Control.Exception+ ( Exception (displayException), SomeException, evaluate )+import Control.Exception.Safe ( try )+import Control.Monad ( forM )+import Control.Monad.Catch ( MonadCatch )+import Control.Monad.IO.Class ( MonadIO, liftIO )+import Control.Monad.Reader ( MonadReader )+import Control.Monad.Trans ( lift )+import Data.Either ( rights )+import Data.Functor ( (<&>) )+import Data.List ( nub, nubBy )+import Data.Set ( Set )+import Data.Text ( Text )+import Data.Variant.Excepts+ ( Excepts, liftE, pattern V, pattern VLeft, pattern VRight, runE, throwE )+import Data.Versions ( Version, version )+import Data.Yaml ( decodeEither' )+import Optics ( preview, (%) )+import System.FilePath ( takeExtension, (</>) )+import System.IO.Error ( doesNotExistErrorType )+import Text.PrettyPrint.HughesPJClass ( prettyShow )++import qualified Data.ByteString as B+import qualified Data.Map.Strict as Map+import qualified Data.Set as Set+import qualified Data.Text as T+import qualified Data.Text.IO as T+import qualified Text.Megaparsec as MP++++ --------------------+ --[ DownloadInfo ]--+ --------------------+++getInstallMetadata ::+ ( MonadIO m+ , MonadCatch m+ , MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadFail m+ )+ => Tool+ -> TargetVersion+ -> Excepts '[FileDoesNotExistError, ParseError] m InstallMetadata+getInstallMetadata tool tver = do+ f <- lift $ recordedInstallationSpecFile tool tver+ -- we have to trigger 'doesNotExistErrorType' explicitly, since libyaml swallows it, so+ -- we have to avoid 'decodeFileEither':+ -- https://github.com/snoyberg/yaml/blob/7380d7f560daa2f45ff265d425866f497ca07966/libyaml/src/Text/Libyaml.hs#L656-L657+ r <- liftIOException doesNotExistErrorType (FileDoesNotExistError f) $ liftIO $ do+ contents <- B.readFile f+ pure $ decodeEither' contents+ either (throwE . ParseError . displayException) pure r+++getInstalledRevision ::+ ( MonadIO m+ , MonadCatch m+ , MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadFail m+ )+ => Tool+ -> TargetVersion+ -> m Int+getInstalledRevision tool tver = do+ runE (getInstallMetadata tool tver) >>= \case+ VLeft _ -> pure 0+ VRight InstallMetadata{..} -> pure _imRevision+++ --------------------+ --[ Symlink spec ]--+ --------------------+++getSymlinkSpec ::+ ( MonadReader env m+ , HasDirs env+ , HasPlatformReq env+ , HasLog env+ , MonadIOish m+ )+ => Tool+ -> TargetVersion+ -> Excepts '[FileDoesNotExistError, ParseError, NoInstallInfo] m [SymlinkFileSpec]+getSymlinkSpec tool tver = do+ spec <- getSymlinkSpec' tool tver++ -- substitute+ pure $ spec <&> substituteSpec++getSymlinkSpec' ::+ ( MonadReader env m+ , HasDirs env+ , HasPlatformReq env+ , HasLog env+ , MonadIOish m+ )+ => Tool+ -> TargetVersion+ -> Excepts+ '[FileDoesNotExistError, ParseError, NoInstallInfo]+ m+ [SymlinkSpec [Either Char Version]]+getSymlinkSpec' tool tver = do+ dli <- liftE $ getInstallMetadata tool tver+ logDebug2 $ T.pack $ show dli+ spec <- preview (imResolvedInstallSpec % isExeSymLinked) dli ?? NoInstallInfo tool tver++ forM spec (liftE . parseSymlinkSpec (_tvVersion tver))++getSymlinkSpecPortable ::+ ( MonadReader env m+ , HasDirs env+ , HasPlatformReq env+ , HasLog env+ , MonadIOish m+ )+ => Tool+ -> TargetVersion+ -> m [SymlinkFileSpec]+getSymlinkSpecPortable tool tver = do+ runE (getSymlinkSpec tool tver) >>= \case+ VRight r -> pure r+ VLeft (V pe@(ParseError _)) -> fail $ prettyHFError pe+ VLeft _ -> do -- legacy+ if | tool == ghc -> do+ pfreq <- getPlatformReq+ pure $ defaultGHCExeSymLinked pfreq tver (ghcBinaries pfreq tver)+ | tool == cabal -> pure []+ | tool == stack -> pure []+ | tool == hls -> pure []+ | tool == ghcup -> pure [] -- Hm+ | otherwise -> pure []+++++ -----------------+ --[ Installed ]--+ -----------------+++-- | Returns 'Nothing' for legacy installs.+getInstalledFiles :: ( MonadIO m+ , MonadCatch m+ , MonadReader env m+ , HasDirs env+ , MonadFail m+ )+ => Tool+ -> TargetVersion+ -> m (Maybe [FilePath])+getInstalledFiles t v' = hideErrorDef [doesNotExistErrorType] Nothing $ do+ f <- recordedInstallationFile t v'+ (force -> !c) <- liftIO+ (readFile f >>= evaluate)+ pure (Just $ lines c)+++isInstalled ::+ ( MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadIOish m+ )+ => Tool+ -> TargetVersion+ -> m (Maybe Int)+isInstalled tool ver+ | tool == ghc =+ isInstalledNew $ ghcInstalled ver+ | tool == hls =+ isInstalledNew $ hlsInstalled (_tvVersion ver)+ | tool == cabal =+ isInstalledNew $ cabalInstalled (_tvVersion ver)+ | tool == stack =+ isInstalledNew $ stackInstalled (_tvVersion ver)+ | tool == ghcup =+ if ghcUpVer' == ver+ then pure $ Just 0+ else pure Nothing+ | otherwise = isInstalledNew (pure False)+ where+ -- for new installations we can just check the DB+ isInstalledNew fallback = do+ runE (getInstallMetadata tool ver) >>= \case+ VLeft _ -> do+ b <- fallback+ if b+ then pure (Just 0)+ else pure Nothing+ VRight InstallMetadata{..} -> pure (Just _imRevision)++getInstalledVersions ::+ ( MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadIOish m+ )+ => Tool+ -> Maybe T.Text+ -> m [VersionRev]+getInstalledVersions tool mtarget = do+ r <- getInstalledVersions' tool+ pure $ extract' r+ where+ extract' = fmap (\TargetVersionRev{..} -> VersionRev (_tvVersion _tvrTargetVer) _tvrRev)+ . filter (\TargetVersionRev{..} -> _tvTarget _tvrTargetVer == mtarget)++getInstalledVersions' ::+ ( MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadIOish m+ )+ => Tool+ -> m [TargetVersionRev]+getInstalledVersions' tool+ | tool == ghc = do+ new <- getInstalledNew+ legacy <- fmap rights getInstalledGHCs+ pure $ nubBy (\a b -> _tvrTargetVer a == _tvrTargetVer b) (new ++ (unsafeToTargetVersionRev <$> legacy))+ | tool == cabal = do+ new <- getInstalledNew+ legacy <- fmap (fmap mkTVer . rights) getInstalledCabals+ pure $ nubBy (\a b -> _tvrTargetVer a == _tvrTargetVer b) (new ++ (unsafeToTargetVersionRev <$> legacy))+ | tool == stack = do+ new <- getInstalledNew+ legacy <- fmap (fmap mkTVer . rights) getInstalledStacks+ pure $ nubBy (\a b -> _tvrTargetVer a == _tvrTargetVer b) (new ++ (unsafeToTargetVersionRev <$> legacy))+ | tool == hls = do+ new <- getInstalledNew+ legacy <- fmap (fmap mkTVer . rights) getInstalledHLSs+ pure $ nubBy (\a b -> _tvrTargetVer a == _tvrTargetVer b) (new ++ (unsafeToTargetVersionRev <$> legacy))+ | tool == ghcup = do+ pure [unsafeToTargetVersionRev ghcUpVer']+ | otherwise = getInstalledNew+ where+ parseGHCVer = throwEither . MP.parse ghcTargetVerP "getInstalledVersions'" . T.pack+ getInstalledNew = do+ Dirs {..} <- getDirs+ let dbPath = fromGHCupPath dbDir </> prettyShow tool+ -- we have to ignore '.spec' and the 'set' file+ contents <- fmap (filter (\f -> takeExtension f `notElem` [".spec", ".set"] && f /= "set"))+ $ liftIO $ handleIO' doesNotExistErrorType (\_ -> pure []) $ listDirectoryFiles dbPath+ forM contents $ \c -> do+ tv <- parseGHCVer c+ rev <- getInstalledRevision tool tv+ pure $ TargetVersionRev tv rev+++getAllInstalledTools ::+ ( MonadReader env m+ , HasGHCupInfo env+ , HasDirs env+ , HasPlatformReq env+ , HasLog env+ , MonadIOish m+ )+ => Maybe [Tool]+ -> Excepts '[ParseError] m (Map.Map Tool (Map.Map (Maybe Text) ([VersionRev], Maybe VersionRev)))+getAllInstalledTools mtools = do+ Dirs{..} <- lift getDirs+ tools <- case mtools of+ Nothing -> do+ newTools <- fmap Tool <$> liftIO (listDirectoryDirs $ fromGHCupPath dbDir)+ pure (nub $ ghcup:ghc:cabal:hls:stack:newTools)+ Just tools'+ -- for GHC we also need to fetch HLS info, so we can display 'hls-powered'+ | ghc `elem` tools' -> pure (nub $ hls:tools')+ | otherwise -> pure (nub tools')+ fmap Map.fromList $ forM tools $ \newTool -> do+ vs <- lift $ getInstalledVersions' newTool+ -- add information on which is the 'set' version, if any+ nm <- Map.traverseWithKey (trav newTool) (groupByTarget' vs)++ pure (newTool, nm)+ where+ trav tool mtarget vers' = do+ mv <- getSetVersion tool mtarget+ pure (vers', mv)++++ -----------+ --[ Set ]--+ -----------+++getSetVersion' ::+ ( MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadIOish m+ )+ => Tool+ -> Maybe T.Text+ -> Excepts '[ParseError] m (Maybe (VersionRev, Maybe FilePath))+getSetVersion' tool target = do+ setFile <- lift $ recordedSetVersionFile tool target+ ec <- liftIO $ try @_ @SomeException (T.readFile setFile)+ case ec of+ Left _+ | tool == ghc ->+ lift $ fmap ((,Nothing) . (`VersionRev` 0) . _tvVersion) <$> ghcSet target+ | tool == cabal ->+ lift $ fmap ((,Nothing) . (`VersionRev` 0)) <$> cabalSet+ | tool == hls ->+ lift $ fmap ((,Nothing) . (`VersionRev` 0)) <$> hlsSet+ | tool == stack ->+ lift $ fmap ((,Nothing) . (`VersionRev` 0)) <$> stackSet+ | tool == ghcup ->+ pure (Just ((`VersionRev` 0) $ _tvVersion ghcUpVer', Nothing))+ | otherwise -> pure Nothing+ Right c -> do+ ver <- either (throwE . ParseError . displayException) pure . version $ c+ rev <- getInstalledRevision tool (TargetVersion target ver)+ pure $ Just (VersionRev ver rev, Just setFile)++getSetVersion ::+ ( MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadIOish m+ )+ => Tool+ -> Maybe T.Text+ -> Excepts '[ParseError] m (Maybe VersionRev)+getSetVersion tool = (fmap . fmap) fst . getSetVersion' tool++isSet ::+ ( MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadIOish m+ )+ => Tool+ -> TargetVersion+ -> Excepts '[ParseError] m (Maybe Int)+isSet tool tver = do+ mv <- (fmap . fmap) fst . getSetVersion' tool $ _tvTarget tver+ case mv of+ Just v ->+ if _tvVersion tver == _vrVersion v+ then pure $ Just (_vrRev v)+ else pure Nothing+ _ -> pure Nothing+++++ -------------+ --[ Other ]--+ -------------+++groupByTarget :: [(TargetVersionRev, VersionMetadata)] -> Map.Map (Maybe Text) [(VersionRev, VersionMetadata)]+groupByTarget = foldr (\(TargetVersionRev{..}, vm) -> Map.alter (f (VersionRev (_tvVersion _tvrTargetVer) _tvrRev) vm) (_tvTarget _tvrTargetVer)) mempty+ where+ f tvVersion' vm Nothing = Just [(tvVersion', vm)]+ f tvVersion' vm (Just xs) = Just ((tvVersion', vm):xs)++groupByTarget' :: [TargetVersionRev] -> Map.Map (Maybe Text) [VersionRev]+groupByTarget' = foldr (\(TargetVersionRev{..}) -> Map.alter (f (VersionRev (_tvVersion _tvrTargetVer) _tvrRev)) (_tvTarget _tvrTargetVer)) mempty+ where+ f tvVersion' Nothing = Just [tvVersion']+ f tvVersion' (Just xs) = Just (tvVersion':xs)++groupByTargetS :: [(TargetVersion, VersionMetadata)] -> Map.Map (Maybe Text) (Set (Version, VersionMetadata))+groupByTargetS = foldr (\(TargetVersion{..}, vm) -> Map.alter (f _tvVersion vm) _tvTarget) mempty+ where+ f tvVersion' vm Nothing = Just $ Set.singleton (tvVersion', vm)+ f tvVersion' vm (Just xs) = Just $ Set.insert (tvVersion', vm) xs+
+ lib/GHCup/Query/DB/HLS.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PatternSynonyms #-}++module GHCup.Query.DB.HLS where++import GHCup.Legacy.Utils+import GHCup.Prelude+import GHCup.Query.DB+import GHCup.Types+import GHCup.Types.Optics++import Control.Monad.Reader ( MonadReader )+import Data.List ( stripPrefix )+import Data.Maybe ( catMaybes )+import Data.Variant.Excepts ( pattern VLeft, pattern VRight, runE )+import Data.Versions ( Version, version )+import Safe ( headMay )++import qualified Data.Text as T+++getHLSGHCs ::+ ( MonadReader env m+ , HasDirs env+ , HasPlatformReq env+ , HasLog env+ , MonadIOish m+ )+ => Version+ -> m [Version]+getHLSGHCs hlsVer = do+ vspec <- runE $ getSymlinkSpec hls (mkTVer hlsVer)+ case vspec of+ VRight (fmap (\SymlinkSpec{..} -> _slLinkName) -> bins) -> do+ let extractGHCVerFromBinary bin = do+ prefix <- headMay $ splitOn "~" bin+ s <- stripPrefix "haskell-language-server-" prefix+ either (const Nothing) pure . version . T.pack $ s+ ghcs = catMaybes $ extractGHCVerFromBinary <$> bins+ pure ghcs+ -- legacy+ VLeft _ -> do+ hlsGHCVersions' hlsVer+
+ lib/GHCup/Query/GHCupDirs.hs view
@@ -0,0 +1,569 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE ViewPatterns #-}++{-|+Module : GHCup.Query.GHCupDirs+Description : Definition of GHCup directories+Copyright : (c) Julian Ospald, 2020+License : LGPL-3.0+Maintainer : hasufell@hasufell.de+Stability : experimental+Portability : portable+-}+module GHCup.Query.GHCupDirs+ ( getAllDirs+ , ghcupBaseDir+ , ghcupConfigFile+ , ghcupCacheDir+ , ghcupGHCBaseDir+ , toolInstallDestination+ , toolBaseDir+ , ghcupGHCDir+ , ghcupHLSBaseDir+ , ghcupHLSDir+ , mkGhcupTmpDir+ , parseGHCupGHCDir+ , parseGHCupHLSDir+ , relativeSymlink+ , withGHCupTmpDir+ , getConfigFilePath+ , getConfigFilePath'+ , useXDG+ , cleanupTrash+ , ghcupMsys2BinDirs+ , ghcupMsys2BinDirs'++ , GHCupPath+ , appendGHCupPath+ , fromGHCupPath+ , createTempGHCupDirectory+ , getGHCupTmpDirs++ , removeDirectory+ , removeDirectoryRecursive+ , removePathForcibly++ , listDirectoryFiles+ , listDirectoryDirs+ )+where+++import GHCup.Errors+import GHCup.Prelude.File.Search+import GHCup.Prelude.Logger.Internal ( logDebug, logWarn )+import GHCup.Prelude.MegaParsec+import GHCup.Prelude.String.QQ+import GHCup.System.Directory+import GHCup.Types+import GHCup.Types.JSON+ ()+import GHCup.Types.Optics+#if defined(IS_WINDOWS)+import GHCup.Prelude.Windows ( isWindows )+#else+import GHCup.Prelude.Posix ( isWindows )+#endif++import Control.DeepSeq ( NFData, rnf )+import Control.Exception.Safe+import Control.Monad+import Control.Monad.IO.Unlift+import Control.Monad.Reader+import Control.Monad.Trans.Resource hiding ( throwM )+import Data.Bifunctor+import Data.ByteString ( ByteString )+import Data.List+import Data.Maybe+import Data.Variant.Excepts+import Data.Versions+import GHC.IO.Exception ( IOErrorType (NoSuchThing) )+import Optics hiding ( uncons )+import Safe+import System.Environment+import System.FilePath+import System.Info+import System.IO.Error ( ioeGetErrorType )+import System.IO.Temp+import Text.PrettyPrint.HughesPJClass ( prettyShow )+import Text.Regex.Posix++import qualified Data.ByteString as BS+import qualified Data.Text as T+import qualified Data.Yaml.Aeson as Y+import qualified System.Directory as SD+import qualified Text.Megaparsec as MP++++ ---------------------------+ --[ GHCupPath utilities ]--+ ---------------------------++-- | A 'GHCupPath' is a safe sub-path that can be recursively deleted.+--+-- The constructor is not exported.+newtype GHCupPath+ = GHCupPath FilePath+ deriving (Eq, Ord, Show)++instance NFData GHCupPath where+ rnf (GHCupPath fp) = rnf fp++appendGHCupPath :: GHCupPath -> FilePath -> GHCupPath+appendGHCupPath (GHCupPath gp) fp = GHCupPath (gp </> fp)++fromGHCupPath :: GHCupPath -> FilePath+fromGHCupPath (GHCupPath gp) = gp++createTempGHCupDirectory :: GHCupPath -> FilePath -> IO GHCupPath+createTempGHCupDirectory (GHCupPath gp) d = GHCupPath <$> createTempDirectory gp d+++getGHCupTmpDirs :: IO [GHCupPath]+getGHCupTmpDirs = do+ tmpdir <- fromGHCupPath <$> ghcupTMPDir+ ghcup_dirs <- handleIO (\_ -> pure []) $ findFiles+ tmpdir+ (makeRegexOpts compExtended+ execBlank+ ([s|^ghcup-.*$|] :: ByteString)+ )+ pure (fmap (\p -> GHCupPath (tmpdir </> p)) $ filter (maybe False ("ghcup-" `isPrefixOf`) . lastMay . splitPath) ghcup_dirs)+++ ------------------------------+ --[ GHCup base directories ]--+ ------------------------------+++-- | ~/.ghcup by default+--+-- If 'GHCUP_USE_XDG_DIRS' is set (to anything),+-- then uses 'XDG_DATA_HOME/ghcup' as per xdg spec.+ghcupBaseDir :: IO GHCupPath+ghcupBaseDir+ | isWindows = do+ bdir <- fromMaybe "C:\\" <$> lookupEnv "GHCUP_INSTALL_BASE_PREFIX"+ pure (GHCupPath (bdir </> "ghcup"))+ | otherwise = do+ xdg <- useXDG+ if xdg+ then do+ bdir <- lookupEnv "XDG_DATA_HOME" >>= \case+ Just r -> pure r+ Nothing -> do+ home <- liftIO getHomeDirectory+ pure (home </> ".local" </> "share")+ pure (GHCupPath (bdir </> "ghcup"))+ else do+ bdir <- lookupEnv "GHCUP_INSTALL_BASE_PREFIX" >>= \case+ Just r -> pure r+ Nothing -> liftIO getHomeDirectory+ pure (GHCupPath (bdir </> ".ghcup"))+++-- | ~/.ghcup by default+--+-- If 'GHCUP_USE_XDG_DIRS' is set (to anything),+-- then uses 'XDG_CONFIG_HOME/ghcup' as per xdg spec.+ghcupConfigDir :: IO GHCupPath+ghcupConfigDir+ | isWindows = ghcupBaseDir+ | otherwise = do+ xdg <- useXDG+ if xdg+ then do+ bdir <- lookupEnv "XDG_CONFIG_HOME" >>= \case+ Just r -> pure r+ Nothing -> do+ home <- liftIO getHomeDirectory+ pure (home </> ".config")+ pure (GHCupPath (bdir </> "ghcup"))+ else do+ bdir <- lookupEnv "GHCUP_INSTALL_BASE_PREFIX" >>= \case+ Just r -> pure r+ Nothing -> liftIO getHomeDirectory+ pure (GHCupPath (bdir </> ".ghcup"))+++-- | If 'GHCUP_USE_XDG_DIRS' is set (to anything),+-- then uses 'XDG_BIN_HOME' env var or defaults to '~/.local/bin'+-- (which, sadly is not strictly xdg spec).+ghcupBinDir :: IO FilePath+ghcupBinDir+ | isWindows = (fromGHCupPath <$> ghcupBaseDir) <&> (</> "bin")+ | otherwise = do+ xdg <- useXDG+ if xdg+ then do+ lookupEnv "XDG_BIN_HOME" >>= \case+ Just r -> pure r+ Nothing -> do+ home <- liftIO getHomeDirectory+ pure (home </> ".local" </> "bin")+ else (fromGHCupPath <$> ghcupBaseDir) <&> (</> "bin")+++-- | Defaults to '~/.ghcup/cache'.+--+-- If 'GHCUP_USE_XDG_DIRS' is set (to anything),+-- then uses 'XDG_CACHE_HOME/ghcup' as per xdg spec.+ghcupCacheDir :: IO GHCupPath+ghcupCacheDir+ | isWindows = ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "cache"))+ | otherwise = do+ xdg <- useXDG+ if xdg+ then do+ bdir <- lookupEnv "XDG_CACHE_HOME" >>= \case+ Just r -> pure r+ Nothing -> do+ home <- liftIO getHomeDirectory+ pure (home </> ".cache")+ pure (GHCupPath (bdir </> "ghcup" </> "cache"))+ else ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "cache"))+++-- | Defaults to '~/.ghcup/logs'.+--+-- If 'GHCUP_USE_XDG_DIRS' is set (to anything),+-- then uses 'XDG_STATE_HOME/ghcup/logs' as per xdg spec.+ghcupLogsDir :: IO GHCupPath+ghcupLogsDir+ | isWindows = ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "logs"))+ | otherwise = do+ xdg <- useXDG+ if xdg+ then do+ bdir <- lookupEnv "XDG_STATE_HOME" >>= \case+ Just r -> pure r+ Nothing -> do+ home <- liftIO getHomeDirectory+ pure (home </> ".local" </> "state")+ pure (GHCupPath (bdir </> "ghcup" </> "logs"))+ else ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "logs"))+++-- | Defaults to '~/.ghcup/db.+--+-- If 'GHCUP_USE_XDG_DIRS' is set (to anything),+-- then uses 'XDG_CACHE_HOME/ghcup/db as per xdg spec.+ghcupDbDir :: IO GHCupPath+ghcupDbDir = ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "db"))+++-- | '~/.ghcup/trash'.+-- Mainly used on windows to improve file removal operations+ghcupRecycleDir :: IO GHCupPath+ghcupRecycleDir = ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "trash"))+++-- | Defaults to '~/.ghcup/tmp.+--+-- If 'GHCUP_USE_XDG_DIRS' is set (to anything),+-- then uses 'XDG_CACHE_HOME/ghcup/tmp as per xdg spec.+ghcupTMPDir :: IO GHCupPath+ghcupTMPDir+ | isWindows = ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "tmp"))+ | otherwise = do+ xdg <- useXDG+ if xdg+ then do+ bdir <- lookupEnv "XDG_CACHE_HOME" >>= \case+ Just r -> pure r+ Nothing -> do+ home <- liftIO getHomeDirectory+ pure (home </> ".cache")+ pure (GHCupPath (bdir </> "ghcup" </> "tmp"))+ else ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "tmp"))+++ghcupMsys2Dir :: IO FilePath+ghcupMsys2Dir =+ lookupEnv "GHCUP_MSYS2" >>= \case+ Just fp -> pure fp+ Nothing -> do+ baseDir <- liftIO ghcupBaseDir+ pure (fromGHCupPath baseDir </> "msys64")++ghcupMsys2BinDirs :: (MonadFail m, MonadIO m, MonadReader env m, HasDirs env) => m [FilePath]+ghcupMsys2BinDirs = do+ Dirs{..} <- getDirs+ liftIO $ ghcupMsys2BinDirs_ msys2Dir++ghcupMsys2BinDirs' :: IO [FilePath]+ghcupMsys2BinDirs' = do+ msys2Dir <- ghcupMsys2Dir+ ghcupMsys2BinDirs_ msys2Dir++ghcupMsys2BinDirs_ :: FilePath -> IO [FilePath]+ghcupMsys2BinDirs_ msys2Dir' = do+ env <- liftIO (lookupEnv "GHCUP_MSYS2_ENV") >>= \case+ Just env -> maybe (fail parseFailMsg) pure $ readMay @MSYS2Env env+ Nothing+ | "x86_64" <- arch -> pure MINGW64+ | "i386" <- arch -> pure MINGW32+ | "aarch64" <- arch -> pure CLANGARM64+ | otherwise -> fail "No compatible architecture for msys2"+ pure [msys2Dir' </> toEnvDir env </> "bin", msys2Dir' </> toEnvDir MSYS </> "bin"]+ where+ -- https://www.msys2.org/docs/environments/+ toEnvDir :: MSYS2Env -> FilePath+ toEnvDir MSYS = "usr"+ toEnvDir UCRT64 = "ucrt64"+ toEnvDir CLANG64 = "clang64"+ toEnvDir CLANGARM64 = "clangarm64"+ toEnvDir CLANG32 = "clang32"+ toEnvDir MINGW64 = "mingw64"+ toEnvDir MINGW32 = "mingw32"++ parseFailMsg = "Invalid value for GHCUP_MSYS2_ENV. Valid values are: MSYS, UCRT64, CLANG64, CLANGARM64, CLANG32, MINGW64, MINGW32"+++getAllDirs :: IO Dirs+getAllDirs = do+ baseDir <- ghcupBaseDir+ binDir <- ghcupBinDir+ cacheDir <- ghcupCacheDir+ logsDir <- ghcupLogsDir+ confDir <- ghcupConfigDir+ recycleDir <- ghcupRecycleDir+ tmpDir <- ghcupTMPDir+ dbDir <- ghcupDbDir+ msys2Dir <- ghcupMsys2Dir+ pure Dirs { .. }++++ -------------------+ --[ GHCup files ]--+ -------------------++getConfigFilePath :: (MonadIO m) => m FilePath+getConfigFilePath = do+ confDir <- liftIO ghcupConfigDir+ pure $ fromGHCupPath confDir </> "config.yaml"++getConfigFilePath' :: (MonadReader env m, HasDirs env) => m FilePath+getConfigFilePath' = do+ Dirs {..} <- getDirs+ pure $ fromGHCupPath confDir </> "config.yaml"+++ghcupConfigFile :: (MonadIO m)+ => Excepts '[JSONError] m UserSettings+ghcupConfigFile = do+ filepath <- getConfigFilePath+ contents <- liftIO $ handleIO (\e -> if NoSuchThing == ioeGetErrorType e then pure Nothing else liftIO $ ioError e) $ Just <$> BS.readFile filepath+ case contents of+ Nothing -> pure defaultUserSettings+ Just contents' -> liftE+ . veitherToExcepts @_ @'[JSONError]+ . either (VLeft . V) VRight+ . first (JSONDecodeError . displayException)+ . Y.decodeEither'+ $ contents'+++ -------------------------+ --[ GHCup directories ]--+ -------------------------+++-- | ~/.ghcup/ghc by default.+ghcupGHCBaseDir :: (MonadReader env m, HasDirs env) => m GHCupPath+ghcupGHCBaseDir = do+ Dirs {..} <- getDirs+ pure (baseDir `appendGHCupPath` "ghc")++-- | The install dir for a tool is simply determined by its tool name+-- and version.+toolInstallDestination :: (MonadReader env m, HasDirs env)+ => Tool+ -> TargetVersion+ -> m GHCupPath+toolInstallDestination tool ver = do+ bDir <- toolBaseDir tool+ pure (bDir `appendGHCupPath` prettyShow ver)++toolBaseDir ::+ (MonadReader env m, HasDirs env)+ => Tool+ -> m GHCupPath+toolBaseDir tool = do+ Dirs {..} <- getDirs+ pure (baseDir `appendGHCupPath` prettyShow tool)++-- | Gets '~/.ghcup/ghc/<ghcupGHCDir>'.+-- The dir may be of the form+-- * armv7-unknown-linux-gnueabihf-8.8.3+-- * 8.8.4+ghcupGHCDir :: (MonadReader env m, HasDirs env, MonadThrow m)+ => TargetVersion+ -> m GHCupPath+ghcupGHCDir ver = do+ ghcbasedir <- ghcupGHCBaseDir+ let verdir = T.unpack $ tVerToText ver+ pure (ghcbasedir `appendGHCupPath` verdir)+++-- | See 'ghcupToolParser'.+parseGHCupGHCDir :: MonadThrow m => FilePath -> m TargetVersion+parseGHCupGHCDir (T.pack -> fp) =+ throwEither $ MP.parse ghcTargetVerP "" fp++parseGHCupHLSDir :: MonadThrow m => FilePath -> m Version+parseGHCupHLSDir (T.pack -> fp) =+ throwEither $ MP.parse version' "" fp++-- TODO: inlined from GHCup.Prelude+throwEither :: (Exception a, MonadThrow m) => Either a b -> m b+throwEither a = case a of+ Left e -> throwM e+ Right r -> pure r++-- | ~/.ghcup/hls by default, for new-style installs.+ghcupHLSBaseDir :: (MonadReader env m, HasDirs env) => m GHCupPath+ghcupHLSBaseDir = do+ Dirs {..} <- getDirs+ pure (baseDir `appendGHCupPath` "hls")++-- | Gets '~/.ghcup/hls/<hls-ver>' for new-style installs.+ghcupHLSDir :: (MonadReader env m, HasDirs env, MonadThrow m)+ => Version+ -> m GHCupPath+ghcupHLSDir ver = do+ basedir <- ghcupHLSBaseDir+ let verdir = T.unpack $ prettyVer ver+ pure (basedir `appendGHCupPath` verdir)+++mkGhcupTmpDir :: ( MonadReader env m+ , HasDirs env+ , MonadUnliftIO m+ , HasLog env+ , MonadCatch m+ , MonadThrow m+ , MonadMask m+ , MonadIO m)+ => m GHCupPath+mkGhcupTmpDir = GHCupPath <$> do+ Dirs { tmpDir } <- getDirs+ liftIO $ createTempDirectory (fromGHCupPath tmpDir) "ghcup"+++withGHCupTmpDir :: ( MonadReader env m+ , HasDirs env+ , HasLog env+ , HasSettings env+ , MonadUnliftIO m+ , MonadCatch m+ , MonadResource m+ , MonadThrow m+ , MonadMask m+ , MonadIO m)+ => m GHCupPath+withGHCupTmpDir = do+ Settings{keepDirs} <- getSettings+ snd <$> withRunInIO (\run ->+ run+ $ allocate+ (run mkGhcupTmpDir)+ (\fp -> if -- we don't know whether there was a failure, so can only+ -- decide for 'Always'+ | keepDirs == Always -> pure ()+ | otherwise -> handleIO (\e -> run+ $ logDebug ("Resource cleanup failed for "+ <> T.pack (fromGHCupPath fp)+ <> ", error was: "+ <> T.pack (displayException e)))+ . removePathForcibly+ $ fp))+++++ --------------+ --[ Others ]--+ --------------+++useXDG :: IO Bool+useXDG = isJust <$> lookupEnv "GHCUP_USE_XDG_DIRS"+++-- | Like 'relpath'. Assumes the inputs are resolved in case of symlinks.+relativeSymlink :: FilePath -- ^ the path in which to create the symlink+ -> FilePath -- ^ the symlink destination+ -> FilePath+relativeSymlink p1 p2+ | isWindows = p2 -- windows quickly gets into MAX_PATH issues so we don't care about relative symlinks+ | otherwise =+ let d1 = splitDirectories p1+ d2 = splitDirectories p2+ common = takeWhile (\(x, y) -> x == y) $ zip d1 d2+ cPrefix = drop (length common) d1+ in joinPath (replicate (length cPrefix) "..")+ <> joinPath ([pathSeparator] : drop (length common) d2)+++cleanupTrash :: ( MonadIO m+ , MonadMask m+ , MonadReader env m+ , HasLog env+ , HasDirs env+ , HasSettings env+ )+ => m ()+cleanupTrash = do+ Dirs { recycleDir } <- getDirs+ contents <- liftIO $ listDirectory (fromGHCupPath recycleDir)+ if null contents+ then pure ()+ else do+ logWarn ("Removing leftover files in " <> T.pack (fromGHCupPath recycleDir))+ forM_ contents (\fp -> handleIO (\e ->+ logDebug ("Resource cleanup failed for " <> T.pack fp <> ", error was: " <> T.pack (displayException e))+ ) $ liftIO $ removePathForcibly (recycleDir `appendGHCupPath` fp))+++-- | List *actual files* in a directory, ignoring empty files and a couple+-- of blacklisted files, such as '.DS_Store' on mac.+listDirectoryFiles :: FilePath -> IO [FilePath]+listDirectoryFiles fp = do+ listDirectory fp >>= filterM (doesFileExist . (fp </>)) <&> filter (\fp' -> not (isHidden fp') && not (isBlacklisted fp'))++-- | List *actual directories* in a directory, ignoring empty directories and a couple+-- of blacklisted files, such as '.DS_Store' on mac.+listDirectoryDirs :: FilePath -> IO [FilePath]+listDirectoryDirs fp = do+ listDirectory fp >>= filterM (doesDirectoryExist . (fp </>)) <&> filter (\fp' -> not (isHidden fp') && not (isBlacklisted fp'))++isHidden :: FilePath -> Bool+isHidden fp'+ | isWindows = False+ | Just ('.', _) <- uncons fp' = True+ | otherwise = False++isBlacklisted :: FilePath -> Bool+{- HLINT ignore "Use ==" -}+isBlacklisted fp' = fp' `elem` [".DS_Store"]++++-- System.Directory re-exports with GHCupPath++removeDirectory :: GHCupPath -> IO ()+removeDirectory (GHCupPath fp) = SD.removeDirectory fp++removeDirectoryRecursive :: GHCupPath -> IO ()+removeDirectoryRecursive (GHCupPath fp) = SD.removeDirectoryRecursive fp++removePathForcibly :: GHCupPath -> IO ()+removePathForcibly (GHCupPath fp) = SD.removePathForcibly fp+
+ lib/GHCup/Query/GHCupDirs.hs-boot view
@@ -0,0 +1,37 @@+module GHCup.Query.GHCupDirs+ ( GHCupPath+ , appendGHCupPath+ , fromGHCupPath+ , createTempGHCupDirectory+ , removeDirectory+ , removeDirectoryRecursive+ , removePathForcibly+ )+ where++import Control.DeepSeq (NFData)+++-- | A 'GHCupPath' is a safe sub-path that can be recursively deleted.+newtype GHCupPath = GHCupPath FilePath++instance Show GHCupPath where++instance Eq GHCupPath where++instance Ord GHCupPath where++instance NFData GHCupPath where++appendGHCupPath :: GHCupPath -> FilePath -> GHCupPath++fromGHCupPath :: GHCupPath -> FilePath++createTempGHCupDirectory :: GHCupPath -> FilePath -> IO GHCupPath++removeDirectory :: GHCupPath -> IO ()++removeDirectoryRecursive :: GHCupPath -> IO ()++removePathForcibly :: GHCupPath -> IO ()+
+ lib/GHCup/Query/Metadata.hs view
@@ -0,0 +1,362 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE FlexibleContexts #-}++{-|+Module : GHCup.Query.Metadata+Description : Queries on GHCup metadata and its data+Copyright : (c) Julian Ospald, 2020+License : LGPL-3.0+Maintainer : hasufell@hasufell.de+Stability : experimental+Portability : portable+-}+module GHCup.Query.Metadata where++import GHCup.Errors+import GHCup.Prelude.Version+import GHCup.Types+import GHCup.Types.JSON+ ()+import GHCup.Types.Optics++import Control.Applicative+import Control.Monad.Catch ( MonadThrow )+import Control.Monad.Reader ( MonadReader )+import Data.List ( find )+import Data.Maybe+import Data.Text ( Text )+import Data.Time ( Day, addDays, diffDays )+import Data.Variant.Excepts ( Excepts, throwE, liftE )+import Data.Versions ( PVP )+import Optics+import Prelude hiding ( readFile, writeFile )+import Safe ( headMay, lastMay )+import URI.ByteString ( URI )++import qualified Data.Map.Strict as M+import qualified Data.Map.Strict as Map+import qualified Data.Text as T+import Control.Monad.Trans (lift)+import Control.Monad (guard)+import GHCup.Prelude++++ --------------------+ --[ Requirements ]--+ --------------------+++getDownloadInfoE ::+ ( MonadReader env m+ , HasPlatformReq env+ , HasGHCupInfo env+ )+ => Tool+ -> VersionReq+ -- ^ tool version+ -> Excepts+ '[NoDownload]+ m+ (Int, DownloadInfo)+getDownloadInfoE t VersionReq{..} = getDownloadInfoE' t (TargetVersionReq (mkTVer _vqVersion) _vqRev)+++getDownloadInfoE' ::+ ( MonadReader env m+ , HasPlatformReq env+ , HasGHCupInfo env+ )+ => Tool+ -> TargetVersionReq+ -- ^ tool version+ -> Excepts+ '[NoDownload]+ m+ (Int, DownloadInfo)+getDownloadInfoE' t tvr = do+ pfreq <- lift getPlatformReq+ ghcupInfo <- lift getGHCupInfo+ lE $ getDownloadInfo t tvr ghcupInfo pfreq+++getDownloadInfo ::+ Tool+ -> TargetVersionReq+ -> GHCupInfo+ -> PlatformRequest+ -> Either NoDownload (Int, DownloadInfo)+getDownloadInfo t tvr@TargetVersionReq{..} (GHCupInfo { _ghcupDownloads = dls }) pfreq =+ let mRev_Vi = preview (_GHCupDownloads+ % ix t+ % toolVersionsL+ % ix _tvqTargetVer+ % revisionSpecL+ % ixOrLast _tvqRev+ ) dls+ in maybe (Left $ NoDownload tvr t (Just pfreq)) Right (mRev_Vi >>= \(rev, vi) -> (rev,) <$> getDownloadInfo' pfreq vi)+++getDownloadInfo' ::+ PlatformRequest+ -> VersionInfo+ -> Maybe DownloadInfo+getDownloadInfo' (PlatformRequest a p mv) vi =+ let distro_preview f g =+ let platformVersionSpec = preview+ ( archL+ % to unMapIgnoreUnknownKeys+ % ix a+ % _PlatformSpec+ % to unMapIgnoreUnknownKeys+ % ix (f p)+ % _PlatformVersionSpec+ ) vi+ mv' = g mv+ in (\m ->+ fmap snd+ . find+ (\(mverRange, _) -> maybe+ (isNothing mv')+ (\range -> maybe False (`versionRange` range) mv')+ mverRange+ )+ . M.toList+ $ m+ ) =<< platformVersionSpec+ with_distro = distro_preview id id+ without_distro_ver = distro_preview id (const Nothing)+ without_distro = distro_preview (set _Linux UnknownLinux) (const Nothing)++ in case p of+ -- non-musl won't work on alpine+ Linux Alpine -> with_distro <|> without_distro_ver+ _ -> with_distro <|> without_distro_ver <|> without_distro+++ --------------------+ --[ Requirements ]--+ --------------------++-- | Get the requirements. Right now this combines GHC and cabal+-- and doesn't do fine-grained distinction. However, the 'ToolRequirements'+-- type allows it.+getCommonRequirements :: Tool+ -> PlatformResult+ -> ToolRequirements+ -> Maybe Requirements+getCommonRequirements tool pr tr =+ with_distro <|> without_distro_ver <|> without_distro+ where+ with_distro = distro_preview _platform _distroVersion+ without_distro_ver = distro_preview _platform (const Nothing)+ without_distro = distro_preview (set _Linux UnknownLinux . _platform) (const Nothing)++ distro_preview f g =+ let platformVersionSpec =+ preview (ix tool % ix Nothing % to unMapIgnoreUnknownKeys % ix (f pr)) tr+ mv' = g pr+ in fmap snd+ . find+ (\(mverRange, _) -> maybe+ (isNothing mv')+ (\range -> maybe False (`versionRange` range) mv')+ mverRange+ )+ . M.toList+ =<< platformVersionSpec+++prettyRequirements :: Requirements -> T.Text+prettyRequirements Requirements {..} =+ let d = if not . null $ _distroPKGs+ then "\n Please ensure the following distro packages "+ <> "are installed before continuing (you can exit ghcup "+ <> "and return at any time): "+ <> "\n "+ <> T.intercalate " " _distroPKGs+ else ""+ n = if not . T.null $ _notes then "\n Note: " <> _notes else ""+ in "System requirements " <> d <> n++rawRequirements :: Requirements -> T.Text+rawRequirements Requirements {..} =+ if not . null $ _distroPKGs+ then T.intercalate " " _distroPKGs+ else ""++++ ----------------+ --[ Versions ]--+ ----------------+++-- | Get the latest available ghc for the given PVP version, which+-- may only contain parts.+--+-- >>> (fmap . fmap) (\(p, _, _) -> p) $ getLatestToolFor GHC Nothing [pver|8|] r+-- Just (PVP {_pComponents = 8 :| [10,7]})+-- >>> (fmap . fmap) (\(p, _, _) -> p) $ getLatestToolFor GHC Nothing [pver|8.8|] r+-- Just (PVP {_pComponents = 8 :| [8,4]})+-- >>> (fmap . fmap) (\(p, _, _) -> p) $ getLatestToolFor GHC Nothing [pver|8.8.4|] r+-- Just (PVP {_pComponents = 8 :| [8,4]})+getLatestToolFor :: MonadThrow m+ => Tool+ -> Maybe Text+ -> PVP+ -> GHCupDownloads+ -> m (Maybe (PVP, VersionMetadata, Maybe Text))+getLatestToolFor tool target pvpIn dls = do+ let ls :: [(TargetVersion, VersionMetadata)]+ ls = fromMaybe [] $ preview (_GHCupDownloads % ix tool % toolVersionsL % to Map.toDescList) dls+ let ps :: [(PVP, VersionMetadata, Maybe Text)]+ ps = catMaybes $ fmap (\(v, vm) -> do+ (pvp, unparsable) <- versionToPVP (_tvVersion v)+ guard (T.null unparsable) -- TODO: hm+ pure (pvp, vm, _tvTarget v)+ ) ls+ pure . headMay . filter (\(v, _, t) -> matchPVPrefix pvpIn v && t == target) $ ps+++ ------------+ --[ Tags ]--+ ------------+++selectLatestRevL :: Getter ToolVersionSpec (M.Map TargetVersion (Int, VersionInfo))+selectLatestRevL = _ToolVersionSpec % to (M.mapMaybe (lastMay . Map.toAscList . Map.mapKeys unRev . unRevisionSpec . _vmRevisionSpec))++selectRevL :: Maybe Int -> AffineFold VersionMetadata (Int, VersionInfo)+selectRevL mrev = revisionSpecL % ixOrLast mrev++-- | Get the tool version that has this tag. If multiple have it,+-- picks the greatest version.+getTaggedL' :: Tag+ -> Lens' a [Tag]+ -> Fold (Map.Map TargetVersion a) (TargetVersion, a)+getTaggedL' tag getTags =+ to (Map.toDescList . Map.filter (\a -> tag `elem` fromMaybe [] (preview getTags a)))+ % folding id++getTaggedL :: Tag+ -> Fold ToolVersionSpec (TargetVersion, VersionMetadata)+getTaggedL tag = _ToolVersionSpec % getTaggedL' tag vmTags++getFirstTag :: Tag -> GHCupDownloads -> Tool -> Maybe TargetVersion+getFirstTag tag av tool = do+ (tv, _) <- headOf (_GHCupDownloads % ix tool % toolVersions % getTaggedL tag) av+ pure tv++getRev :: GHCupDownloads -> Tool -> TargetVersion -> Maybe Int -> Maybe (VersionMetadata, Int, VersionInfo)+getRev av tool tv mRev = do+ vm <- preview (_GHCupDownloads % ix tool % toolVersionsL % ix tv) av+ (rev, vi) <- preview (selectRevL mRev) vm+ pure (vm, rev, vi)++getByReleaseDay :: GHCupDownloads -> Tool -> Day -> Either (Maybe Day) (TargetVersion, VersionMetadata)+getByReleaseDay av tool day =+ let mvv = fromMaybe mempty $ headOf (_GHCupDownloads % ix tool % toolVersions % _ToolVersionSpec) av+ mdv = Map.foldrWithKey (\k vi@VersionMetadata{..} m ->+ maybe m (\d -> let diff = diffDays d day+ in Map.insert (abs diff) (diff, (k, vi)) m) _vmReleaseDay)+ Map.empty+ mvv+ in case headMay (Map.toAscList mdv) of+ Nothing -> Left Nothing+ Just (absDiff, (diff, (k, vi)))+ | absDiff == 0 -> Right (k, vi)+ | otherwise -> Left (Just (addDays diff day))++getByReleaseDayFold :: Day -> Fold (Map.Map TargetVersion VersionMetadata) TargetVersion+getByReleaseDayFold day = to (fmap fst . Map.toDescList . Map.filter (\VersionMetadata {..} -> Just day == _vmReleaseDay)) % folding id++getLatest :: GHCupDownloads -> Tool -> Maybe TargetVersion+getLatest = getFirstTag Latest++getLatestPrerelease :: GHCupDownloads -> Tool -> Maybe TargetVersion+getLatestPrerelease = getFirstTag LatestPrerelease++getLatestNightly :: GHCupDownloads -> Tool -> Maybe TargetVersion+getLatestNightly = getFirstTag LatestNightly++getRecommended :: GHCupDownloads -> Tool -> Maybe TargetVersion+getRecommended = getFirstTag Recommended++-- | Gets the latest GHC with a given base version.+getLatestBaseVersion :: GHCupDownloads -> PVP -> Maybe TargetVersion+getLatestBaseVersion g pvpVer = getFirstTag (Base pvpVer) g ghc+++getVersionMetadata :: TargetVersion+ -> Tool+ -> GHCupDownloads+ -> Maybe VersionMetadata+getVersionMetadata v' tool =+ headOf+ ( _GHCupDownloads+ % ix tool+ % toolVersions+ % _ToolVersionSpec+ % to (Map.filterWithKey (\k _ -> k == v'))+ % to Map.elems+ % _head+ )+++ -----------------+ --[ Changelog ]--+ -----------------+++getChangeLog :: GHCupDownloads -> Tool -> TargetVersion -> Maybe URI+getChangeLog dls tool tv =+ preview (_GHCupDownloads % ix tool % toolVersionsL % ix tv % vmChangeLog % _Just) dls++++ -------------+ --[ Other ]--+ -------------+++-- NOTE: there might be installed tools that are not in the available tools set+-- (e.g. because the metadata was removed)+allAvailableTools :: GHCupDownloads -> [(Tool, [(TargetVersion, VersionMetadata)])]+allAvailableTools = Map.toList . Map.mapWithKey (\_ (ToolInfo (ToolVersionSpec v) _) -> Map.toList v) . unGHCupDownloads++installationSpecFromMetadata' ::+ ( MonadReader env m+ , HasPlatformReq env+ )+ => DownloadInfo+ -> Tool+ -> TargetVersion+ -> Excepts '[NoInstallInfo] m InstallationSpecInput+installationSpecFromMetadata' dlinfo tool tver = do+ pfreq <- lift getPlatformReq+ case _dlInstallSpec dlinfo of+ Just installInfo -> pure installInfo+ Nothing+ | Just i <- toInstallationInputSpec <$> defaultToolInstallSpec tool pfreq tver+ -> pure i+ | otherwise+ -> throwE $ NoInstallInfo tool tver++-- has defaults, so works with legacy metadata+installationSpecFromMetadata ::+ ( MonadReader env m+ , HasGHCupInfo env+ , HasPlatformReq env+ )+ => Tool+ -> TargetVersionReq+ -> Excepts '[NoDownload, NoInstallInfo] m (Int, InstallationSpecInput)+installationSpecFromMetadata tool tver = do+ pfreq <- lift getPlatformReq+ ghcupInfo <- lift getGHCupInfo+ (rev, dlinfo) <- lE $ getDownloadInfo tool tver ghcupInfo pfreq+ spec <- liftE $ installationSpecFromMetadata' dlinfo tool (_tvqTargetVer tver)+ pure (rev, spec)+
+ lib/GHCup/Query/Symlink.hs view
@@ -0,0 +1,133 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}++module GHCup.Query.Symlink where++import GHCup.Errors+import GHCup.Legacy.Utils+import GHCup.Prelude+import GHCup.Types+import GHCup.Types.Optics++import Control.Exception ( Exception (..) )+import Control.Exception.Safe ( handle )+import Control.Monad ( forM, when )+import Control.Monad.Reader ( MonadReader )+import Data.Either ( partitionEithers )+import Data.Maybe ( catMaybes )+import Data.Variant.Excepts ( Excepts, throwE )+import Data.Versions ( Version, prettyVer )+import System.FilePath ( (</>) )++import qualified Data.Text as T+++-- | These are the symlinks we need to create+-- in order to 'set' a tool version.+getUnqualifiedSymlinks ::+ ( MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadIOish m+ )+ => [SymlinkFileSpec]+ -> FilePath+ -> m [(FilePath, FilePath)]+getUnqualifiedSymlinks specs toolDir = do+ Dirs {..} <- getDirs+ getUnqualifiedSymlinks' binDir specs toolDir++-- | Like 'getUnqualifiedSymlinks', except takes+-- the binary directory as an explicit argument.+getUnqualifiedSymlinks' ::+ ( MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadIOish m+ )+ => FilePath+ -> [SymlinkFileSpec]+ -> FilePath+ -> m [(FilePath, FilePath)]+getUnqualifiedSymlinks' bindir specs toolDir =+ fmap catMaybes $ forM specs $ \SymlinkSpec{..} ->+ case _slSetName of+ Nothing -> pure Nothing+ Just setName -> do+ target <- binarySymLinkDestination bindir (toolDir </> _slTarget)+ pure $ Just (target, bindir </> setName)++getPVPMajorSymlinks' ::+ ( MonadReader env m+ , HasLog env+ , MonadIOish m+ )+ => FilePath+ -> [SymlinkSpec [Either Char Version]]+ -> Version+ -> FilePath+ -> Excepts '[MalformedInstallInfo] m [(FilePath, FilePath)]+getPVPMajorSymlinks' bindir spec ver toolBinDir =+ fmap catMaybes $ forM spec $ \SymlinkSpec{..} -> do+ if _slPVPMajorLinks+ then do+ let target = mconcat $ either (:[]) (T.unpack . prettyVer) <$> _slTarget+ targetResolved <- binarySymLinkDestination bindir (toolBinDir </> target)+ handle+ (\(e :: ParseError) -> logWarn (T.pack $ displayException e) >> pure Nothing)+ $ do+ when (null . snd . partitionEithers $ _slLinkName) $ throwE $ MalformedInstallInfo "No ${PKGVER} found in linkName"+ (mj, mi) <- getMajorMinorV ver+ let major' = T.unpack (intToText mj <> "." <> intToText mi)+ link_name = mconcat $ either (:[]) (const major') <$> _slLinkName+ logDebug2 $ T.pack (show _slLinkName)+ logDebug2 $ T.pack (show link_name)+ logDebug2 $ T.pack (show major')++ -- if our tool has only two components anyway, we skip this+ if (intToText mj <> "." <> intToText mi) == prettyVer ver+ then pure Nothing+ else pure (Just (targetResolved, bindir </> link_name))+ else pure Nothing++getPVPMajorSymlinks ::+ ( MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadIOish m+ )+ => [SymlinkSpec [Either Char Version]]+ -> Version+ -> FilePath+ -> Excepts '[MalformedInstallInfo] m [(FilePath, FilePath)]+getPVPMajorSymlinks specs ver toolBinDir = do+ Dirs {..} <- getDirs+ getPVPMajorSymlinks' binDir specs ver toolBinDir++getPVPSymlinks' ::+ ( MonadReader env m+ , HasLog env+ , MonadIOish m+ )+ => FilePath+ -> [SymlinkFileSpec]+ -> FilePath+ -> m [(FilePath, FilePath)]+getPVPSymlinks' bindir specs toolDir = do+ forM specs $ \SymlinkSpec{..} -> do+ target <- binarySymLinkDestination bindir (toolDir </> _slTarget)+ pure (target, bindir </> _slLinkName)++getPVPSymlinks ::+ ( MonadReader env m+ , HasDirs env+ , HasLog env+ , MonadIOish m+ )+ => [SymlinkFileSpec]+ -> FilePath+ -> m [(FilePath, FilePath)]+getPVPSymlinks specs toolDir = do+ Dirs {..} <- getDirs+ getPVPSymlinks' binDir specs toolDir
+ lib/GHCup/Query/System.hs view
@@ -0,0 +1,371 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskellQuotes #-}+++{-|+Module : GHCup.Query.System+Description : Retrieving platform information+Copyright : (c) Julian Ospald, 2020+License : LGPL-3.0+Maintainer : hasufell@hasufell.de+Stability : experimental+Portability : portable+-}+module GHCup.Query.System where+++import GHCup.Errors+import GHCup.Prelude+import GHCup.Prelude.MegaParsec+import GHCup.Prelude.Process+import GHCup.Prelude.String.QQ+import GHCup.Prelude.Version.QQ+import GHCup.System.Directory+import GHCup.Types+import GHCup.Types.JSON+ ()+import GHCup.Types.Optics++#if !MIN_VERSION_base(4,13,0)+import Control.Monad.Fail ( MonadFail )+#endif+import Control.Applicative+import Control.Exception.Safe+import Control.Monad+import Control.Monad.Reader+import Data.ByteString ( ByteString )+import Data.Foldable+import Data.Maybe+import Data.Text ( Text )+import Data.Variant.Excepts+import Data.Versions+import Prelude hiding ( abs, readFile, writeFile )+import System.Exit+import System.FilePath+import System.Info+import System.OsRelease as OSR+import Text.PrettyPrint.HughesPJClass ( prettyShow )+import Text.Regex.Posix++import qualified Text.Megaparsec as MP++import qualified Data.List as L+import qualified Data.Text as T+import qualified Data.Text.IO as T+import Data.Void+++++ --------------------------+ --[ Platform detection ]--+ --------------------------+++-- | Get the full platform request, consisting of architecture, distro, ...+platformRequest :: (MonadReader env m, MonadFail m, HasLog env, MonadCatch m, MonadIO m)+ => Excepts+ '[NoCompatiblePlatform, NoCompatibleArch, DistroNotFound]+ m+ PlatformRequest+platformRequest = do+ (PlatformResult rp rv) <- liftE getPlatform+ ar <- lE getArchitecture+ pure $ PlatformRequest ar rp rv+++getArchitecture :: Either NoCompatibleArch Architecture+getArchitecture = case arch of+ "x86_64" -> Right A_64+ "i386" -> Right A_32+ "powerpc" -> Right A_PowerPC+ "powerpc64" -> Right A_PowerPC64+ "powerpc64le" -> Right A_PowerPC64+ "sparc" -> Right A_Sparc+ "sparc64" -> Right A_Sparc64+ "arm" -> Right A_ARM+ "aarch64" -> Right A_ARM64+ what -> Left (NoCompatibleArch what)+++getPlatform :: (MonadReader env m, HasLog env, MonadCatch m, MonadIO m, MonadFail m)+ => Excepts+ '[NoCompatiblePlatform, DistroNotFound]+ m+ PlatformResult+getPlatform = do+ pfr <- case os of+ "linux" -> do+ (distro, ver) <- liftE getLinuxDistro+ pure $ PlatformResult { _platform = Linux distro, _distroVersion = ver }+ "darwin" -> do+ ver <-+ either (const Nothing) Just+ . versioning+ -- TODO: maybe do this somewhere else+ . decUTF8Safe'+ <$> getDarwinVersion+ pure $ PlatformResult { _platform = Darwin, _distroVersion = ver }+ "freebsd" -> do+ ver <-+ either (const Nothing) Just . versioning . decUTF8Safe'+ <$> getFreeBSDVersion+ pure $ PlatformResult { _platform = FreeBSD, _distroVersion = ver }+ "openbsd" -> do+ ver <-+ either (const Nothing) Just . versioning . decUTF8Safe'+ <$> getOpenBSDVersion+ pure $ PlatformResult { _platform = OpenBSD, _distroVersion = ver }+ "mingw32" -> pure PlatformResult { _platform = Windows, _distroVersion = Nothing }+ what -> throwE $ NoCompatiblePlatform what+ lift $ logDebug $ "Identified Platform as: " <> T.pack (prettyShow pfr)+ pure pfr+ where+ getOpenBSDVersion = lift $ fmap _stdOut $ executeOut "uname" ["-r"] Nothing+ getFreeBSDVersion = lift $ fmap _stdOut $ executeOut "freebsd-version" [] Nothing+ getDarwinVersion = lift $ fmap _stdOut $ executeOut "sw_vers"+ ["-productVersion"]+ Nothing+++getLinuxDistro :: (MonadCatch m, MonadIO m, MonadFail m)+ => Excepts '[DistroNotFound] m (LinuxDistro, Maybe Versioning)+getLinuxDistro = do+ -- TODO: don't do alternative on IO, because it hides bugs+ (name, mid, ver) <- join $ liftIO $ handleIO (\_ -> pure (throwE DistroNotFound)) $ fmap pure $ asum+ [ try_os_release+ , try_lsb_release_cmd+ , try_redhat_release+ , try_debian_version+ ]+ let hasWord xs = let f t = any (\x -> match (regex x) (T.unpack t)) xs+ in f name || maybe False f mid+ let parsedVer = ver >>= either (const Nothing) Just . versioning+ distro = if+ | hasWord ["debian"] -> Debian+ | hasWord ["ubuntu"] -> Ubuntu+ | hasWord ["linuxmint", "Linux Mint"] -> Mint+ | hasWord ["fedora"] -> Fedora+ | hasWord ["centos"] -> CentOS+ | hasWord ["Red Hat"] -> RedHat+ | hasWord ["alpine"] -> Alpine+ | hasWord ["exherbo"] -> Exherbo+ | hasWord ["gentoo"] -> Gentoo+ | hasWord ["opensuse", "suse"] -> OpenSUSE+ | hasWord ["amazonlinux", "Amazon Linux"] -> AmazonLinux+ | hasWord ["rocky", "Rocky Linux"] -> Rocky+ -- https://github.com/void-linux/void-packages/blob/master/srcpkgs/base-files/files/os-release+ | hasWord ["void", "Void Linux"] -> Void+ | otherwise -> UnknownLinux+ pure (distro, parsedVer)+ where+ regex x = makeRegexOpts compIgnoreCase execBlank ([s|\<|] ++ x ++ [s|\>|])++ lsb_release_cmd :: FilePath+ lsb_release_cmd = "lsb-release"+ redhat_release :: FilePath+ redhat_release = "/etc/redhat-release"+ debian_version :: FilePath+ debian_version = "/etc/debian_version"++ try_os_release :: IO (Text, Maybe Text, Maybe Text)+ try_os_release = do+ Just OsRelease{ name = name, version_id = version_id, OSR.id = id' } <-+ fmap osRelease <$> parseOsRelease+ pure (T.pack name, Just (T.pack id'), fmap T.pack version_id)++ try_lsb_release_cmd :: (MonadFail m, MonadIO m)+ => m (Text, Maybe Text, Maybe Text)+ try_lsb_release_cmd = do+ (Just _) <- liftIO $ findExecutable lsb_release_cmd+ name <- fmap _stdOut $ executeOut lsb_release_cmd ["-si"] Nothing+ ver <- fmap _stdOut $ executeOut lsb_release_cmd ["-sr"] Nothing+ pure (decUTF8Safe' name, Nothing, Just $ decUTF8Safe' ver)++ try_redhat_release :: IO (Text, Maybe Text, Maybe Text)+ try_redhat_release = do+ t <- T.readFile redhat_release+ let nameRegex n =+ makeRegexOpts compIgnoreCase+ execBlank+ ([s|\<|] <> fS n <> [s|\>|] :: ByteString) :: Regex+ let verRegex =+ makeRegexOpts compIgnoreCase+ execBlank+ ([s|\<([0-9])+(.([0-9])+)*\>|] :: ByteString) :: Regex+ let nameRe n =+ fromEmpty . match (nameRegex n) $ T.unpack t :: Maybe String+ verRe = fromEmpty . match verRegex $ T.unpack t :: Maybe String+ (Just name) <- pure+ (nameRe "CentOS" <|> nameRe "Fedora" <|> nameRe "Red Hat")+ pure (T.pack name, Nothing, fmap T.pack verRe)+ where+ fromEmpty :: String -> Maybe String+ fromEmpty "" = Nothing+ fromEmpty s' = Just s'++ try_debian_version :: IO (Text, Maybe Text, Maybe Text)+ try_debian_version = do+ ver <- T.readFile debian_version+ pure (T.pack "debian", Just (T.pack "debian"), Just ver)+++getStackGhcBuilds :: (MonadReader env m, HasLog env, MonadIO m)+ => PlatformResult+ -> Excepts '[ParseError, NoCompatiblePlatform, DistroNotFound, ProcessError] m [String]+getStackGhcBuilds PlatformResult{..} = do+ case _platform of+ Linux _ -> do+ -- Some systems don't have ldconfig in the PATH, so make sure to look in+ -- /sbin and /usr/sbin as well+ sbinEnv <- liftIO $ addToPath sbinDirs False+ ldConfig <- lift $ executeOut' "ldconfig" ["-p"] Nothing (Just sbinEnv)+ firstWords <- case ldConfig of+ CapturedProcess ExitSuccess so _ ->+ pure . mapMaybe (listToMaybe . T.words) . T.lines . T.pack . stripNewlineEnd . T.unpack . decUTF8Safe' $ so+ CapturedProcess (ExitFailure _) _ _ ->+ -- throwE $ NonZeroExit c "ldconfig" ["-p" ]+ pure []+ let checkLib :: (MonadReader env m, HasLog env, MonadIO m) => String -> m Bool+ checkLib lib+ | libT `elem` firstWords = do+ logDebug $ "Found shared library " <> libT <> " in 'ldconfig -p' output"+ pure True+ | isWindows =+ -- Cannot parse /usr/lib on Windows+ pure False+ | otherwise = hasMatches lib usrLibDirs+ -- This is a workaround for the fact that libtinfo.so.x doesn't+ -- appear in the 'ldconfig -p' output on Arch or Slackware even+ -- when it exists. There doesn't seem to be an easy way to get the+ -- true list of directories to scan for shared libs, but this+ -- works for our particular cases.+ where+ libT = T.pack lib++ hasMatches :: (MonadReader env m, HasLog env, MonadIO m) => String -> [FilePath] -> m Bool+ hasMatches lib dirs = do+ matches <- filterM (liftIO . doesFileExist . (</> lib)) dirs+ case matches of+ [] -> logDebug ("Did not find shared library " <> libT) >> pure False+ (path:_) -> logDebug ("Found shared library " <> libT <> " in " <> T.pack path) >> pure True+ where+ libT = T.pack lib++ getLibc6Version :: MonadIO m+ => Excepts '[ParseError, ProcessError] m Version+ getLibc6Version = do+ CapturedProcess{..} <- lift $ executeOut "ldd" ["--version"] Nothing+ case _exitCode of+ ExitSuccess -> either (throwE . ParseError . show) pure+ . MP.parse lddVersion "" . T.pack . stripNewlineEnd . T.unpack . decUTF8Safe' $ _stdOut+ ExitFailure c -> throwE $ NonZeroExit c "ldd" ["--version" ]++ -- Assumes the first line of ldd has the format:+ --+ -- ldd (...) nn.nn+ --+ -- where nn.nn corresponds to the version of libc6.+ lddVersion :: MP.Parsec Void Text Version+ lddVersion = do+ skipWhile (/= ')')+ skip (== ')')+ skipSpace+ version'++ hasMusl <- hasMatches relFileLibcMuslx86_64So1 libDirs+ mLibc6Version <- veitherToEither <$> runE getLibc6Version+ case mLibc6Version of+ Right libc6Version -> logDebug $ "Found shared library libc6 in version: " <> prettyVer libc6Version+ Left _ -> logDebug "Did not find a version of shared library libc6."+ let hasLibc6_2_32 = either (const False) (>= [vver|2.32|]) mLibc6Version+ hastinfo5 <- checkLib relFileLibtinfoSo5+ hastinfo6 <- checkLib relFileLibtinfoSo6+ hasncurses6 <- checkLib relFileLibncurseswSo6+ hasgmp5 <- checkLib relFileLibgmpSo10+ hasgmp4 <- checkLib relFileLibgmpSo3+ let libComponents = if hasMusl+ then+ [ ["musl"] ]+ else+ concat+ [ if hastinfo6 && hasgmp5+ then+ if hasLibc6_2_32+ then [["tinfo6"]]+ else [["tinfo6-libc6-pre232"]]+ else [[]]+ , [ [] | hastinfo5 && hasgmp5 ]+ , [ ["ncurses6"] | hasncurses6 && hasgmp5 ]+ , [ ["gmp4"] | hasgmp4 ]+ ]+ pure $ map+ (\c -> case c of+ [] -> []+ _ -> L.intercalate "-" c)+ libComponents+ OpenBSD -> pure []+ FreeBSD ->+ case _distroVersion of+ Just fVer+ | fVer >= [vers|12|] -> pure []+ _ -> pure ["ino64"]+ Darwin -> pure []+ Windows -> pure []+ where++ relFileLibcMuslx86_64So1 :: FilePath+ relFileLibcMuslx86_64So1 = "libc.musl-x86_64.so.1"+ libDirs :: [FilePath]+ libDirs = ["/lib", "/lib64"]+ usrLibDirs :: [FilePath]+ usrLibDirs = ["/usr/lib", "/usr/lib64"]+ sbinDirs :: [FilePath]+ sbinDirs = ["/sbin", "/usr/sbin"]+ relFileLibtinfoSo5 :: FilePath+ relFileLibtinfoSo5 = "libtinfo.so.5"+ relFileLibtinfoSo6 :: FilePath+ relFileLibtinfoSo6 = "libtinfo.so.6"+ relFileLibncurseswSo6 :: FilePath+ relFileLibncurseswSo6 = "libncursesw.so.6"+ relFileLibgmpSo10 :: FilePath+ relFileLibgmpSo10 = "libgmp.so.10"+ relFileLibgmpSo3 :: FilePath+ relFileLibgmpSo3 = "libgmp.so.3"++getStackOSKey :: Monad m => PlatformRequest -> Excepts '[UnsupportedSetupCombo] m String+getStackOSKey PlatformRequest { .. } =+ case (_rArch, _rPlatform) of+ (A_32 , Linux _) -> pure "linux32"+ (A_64 , Linux _) -> pure "linux64"+ (A_32 , Darwin ) -> pure "macosx"+ (A_64 , Darwin ) -> pure "macosx"+ (A_32 , FreeBSD) -> pure "freebsd32"+ (A_64 , FreeBSD) -> pure "freebsd64"+ (A_32 , OpenBSD) -> pure "openbsd32"+ (A_64 , OpenBSD) -> pure "openbsd64"+ (A_32 , Windows) -> pure "windows32"+ (A_64 , Windows) -> pure "windows64"+ (A_ARM , Linux _) -> pure "linux-armv7"+ (A_ARM64, Linux _) -> pure "linux-aarch64"+ (A_Sparc, Linux _) -> pure "linux-sparc"+ (A_ARM64, Darwin ) -> pure "macosx-aarch64"+ (A_ARM64, FreeBSD) -> pure "freebsd-aarch64"+ (A_ARM64, OpenBSD) -> pure "openbsd-aarch64"+ (arch', os') -> throwE $ UnsupportedSetupCombo arch' os'++getStackPlatformKey :: (MonadReader env m, MonadFail m, HasLog env, MonadCatch m, MonadIO m)+ => PlatformRequest+ -> Excepts '[UnsupportedSetupCombo, ParseError, NoCompatiblePlatform, NoCompatibleArch, DistroNotFound, ProcessError] m [String]+getStackPlatformKey pfreq@PlatformRequest{..} = do+ osKey <- liftE $ getStackOSKey pfreq+ builds <- liftE $ getStackGhcBuilds (PlatformResult _rPlatform _rVersion)+ let builds' = (\build -> if null build then osKey else osKey <> "-" <> build) <$> builds+ logDebug $ "Potential GHC builds: " <> mconcat (L.intersperse ", " $ fmap T.pack builds')+ pure builds'+
− lib/GHCup/Requirements.hs
@@ -1,75 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--{-|-Module : GHCup.Requirements-Description : Requirements utilities-Copyright : (c) Julian Ospald, 2020-License : LGPL-3.0-Maintainer : hasufell@hasufell.de-Stability : experimental-Portability : portable--}-module GHCup.Requirements where--import GHCup.Types-import GHCup.Types.JSON ( )-import GHCup.Types.Optics-import GHCup.Version--import Control.Applicative-import Data.List ( find )-import Data.Maybe-import Optics-import Prelude hiding ( abs- , readFile- , writeFile- )--import qualified Data.Map.Strict as M-import qualified Data.Text as T----- | Get the requirements. Right now this combines GHC and cabal--- and doesn't do fine-grained distinction. However, the 'ToolRequirements'--- type allows it.-getCommonRequirements :: PlatformResult- -> ToolRequirements- -> Maybe Requirements-getCommonRequirements pr tr =- with_distro <|> without_distro_ver <|> without_distro- where- with_distro = distro_preview _platform _distroVersion- without_distro_ver = distro_preview _platform (const Nothing)- without_distro = distro_preview (set _Linux UnknownLinux . _platform) (const Nothing)-- distro_preview f g =- let platformVersionSpec =- preview (ix GHC % ix Nothing % to unMapIgnoreUnknownKeys % ix (f pr)) tr- mv' = g pr- in fmap snd- . find- (\(mverRange, _) -> maybe- (isNothing mv')- (\range -> maybe False (`versionRange` range) mv')- mverRange- )- . M.toList- =<< platformVersionSpec---prettyRequirements :: Requirements -> T.Text-prettyRequirements Requirements {..} =- let d = if not . null $ _distroPKGs- then "\n Please ensure the following distro packages "- <> "are installed before continuing (you can exit ghcup "- <> "and return at any time): "- <> T.intercalate " " _distroPKGs- else ""- n = if not . T.null $ _notes then "\n Note: " <> _notes else ""- in "System requirements " <> d <> n--rawRequirements :: Requirements -> T.Text-rawRequirements Requirements {..} =- if not . null $ _distroPKGs- then T.intercalate " " _distroPKGs- else ""
+ lib/GHCup/Setup.hs view
@@ -0,0 +1,60 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}++module GHCup.Setup where++import GHCup.Download+import GHCup.Errors+import GHCup.Hardcoded.URLs+import GHCup.Prelude+import GHCup.Query.GHCupDirs+import GHCup.Types+import GHCup.Types.Optics++import Control.Monad ( void )+import Control.Monad.Reader ( MonadReader )+import Control.Monad.Trans ( lift )+import Data.Variant.Excepts+import System.FilePath ( (</>) )+import System.IO.Error ( doesNotExistErrorType )+import URI.ByteString ( serializeURIRef' )++import qualified Data.Text as T++ensureShimGen :: ( HasLog env+ , MonadReader env m+ , HasDirs env+ , HasSettings env+ , HasGHCupInfo env+ , MonadIOish m+ )+ => Excepts '[URIParseError, GPGError, DigestError, ContentLengthError, DownloadFailed, NoDownload] m ()+ensureShimGen+ | isWindows = do+ dirs <- lift getDirs+ let shimDownload = DownloadInfo { _dlUri = decUTF8Safe . serializeURIRef' $ shimGenURL, _dlSubdir = Nothing, _dlHash = shimGenSHA, _dlCSize = Nothing, _dlOutput = Nothing, _dlTag = Nothing, _dlInstallSpec = Nothing }+ let dl = downloadCached' shimDownload (Just "gs.exe") Nothing+ void $ (\DigestError{} -> do+ lift $ logWarn "Digest doesn't match, redownloading gs.exe..."+ lift $ logDebug ("rm -f " <> T.pack (fromGHCupPath (cacheDir dirs) </> "gs.exe"))+ lift $ hideError doesNotExistErrorType $ recycleFile (fromGHCupPath (cacheDir dirs) </> "gs.exe")+ liftE @'[URIParseError, GPGError, DigestError, ContentLengthError, DownloadFailed] $ dl+ ) `catchE` liftE @'[URIParseError, GPGError, DigestError, ContentLengthError, DownloadFailed] dl+ | otherwise = pure ()+++-- | Ensure ghcup directory structure exists.+ensureDirectories :: Dirs -> IO ()+ensureDirectories (Dirs baseDir binDir cacheDir logsDir confDir trashDir dbDir tmpDir _) = do+ createDirRecursive' (fromGHCupPath baseDir)+ createDirRecursive' (fromGHCupPath baseDir </> "ghc")+ createDirRecursive' (fromGHCupPath baseDir </> "hls")+ createDirRecursive' binDir+ createDirRecursive' (fromGHCupPath cacheDir)+ createDirRecursive' (fromGHCupPath logsDir)+ createDirRecursive' (fromGHCupPath confDir)+ createDirRecursive' (fromGHCupPath trashDir)+ createDirRecursive' (fromGHCupPath dbDir)+ createDirRecursive' (fromGHCupPath tmpDir)+ pure ()
− lib/GHCup/Stack.hs
@@ -1,286 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedStrings #-}--{-|-Module : GHCup.Stack-Description : GHCup installation functions for Stack-Copyright : (c) Julian Ospald, 2020-License : LGPL-3.0-Maintainer : hasufell@hasufell.de-Stability : experimental-Portability : portable--}-module GHCup.Stack where--import GHCup.Download-import GHCup.Errors-import GHCup.Types-import GHCup.Types.JSON ( )-import GHCup.Types.Optics-import GHCup.Utils-import GHCup.Prelude-import GHCup.Prelude.File-import GHCup.Prelude.Logger--import Control.Applicative-import Control.Exception.Safe-import Control.Monad-#if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )-#endif-import Control.Monad.Reader-import Control.Monad.Trans.Resource- hiding ( throwM )-import Data.Either-import Data.List-import Data.Ord-import Data.Maybe-import Data.Versions hiding ( patch )-import Data.Variant.Excepts-import Optics-import Prelude hiding ( abs- , writeFile- )-import Safe hiding ( at )-import System.FilePath-import System.IO.Error--import qualified Data.Text as T---- --------------------- --[ Installation ]--- ------------------------- | Installs stack into @~\/.ghcup\/bin/stack-\<ver\>@ and--- creates a default @stack -> stack-x.y.z.q@ symlink for--- the latest installed version.-installStackBin :: ( MonadMask m- , MonadCatch m- , MonadReader env m- , HasDirs env- , HasSettings env- , HasPlatformReq env- , HasGHCupInfo env- , HasLog env- , MonadResource m- , MonadIO m- , MonadUnliftIO m- , MonadFail m- )- => Version- -> InstallDir- -> Bool -- ^ Force install- -> Excepts- '[ AlreadyInstalled- , CopyError- , DigestError- , ContentLengthError- , GPGError- , DownloadFailed- , NoDownload- , NotInstalled- , UnknownArchive- , TarDirDoesNotExist- , ArchiveResult- , FileAlreadyExistsError- , URIParseError- ]- m- ()-installStackBin ver installDir forceInstall = do- dlinfo <- liftE $ getDownloadInfo Stack ver- installStackBindist dlinfo ver installDir forceInstall----- | Like 'installStackBin', except takes the 'DownloadInfo' as--- argument instead of looking it up from 'GHCupDownloads'.-installStackBindist :: ( MonadMask m- , MonadCatch m- , MonadReader env m- , HasPlatformReq env- , HasDirs env- , HasSettings env- , HasLog env- , MonadResource m- , MonadIO m- , MonadUnliftIO m- , MonadFail m- )- => DownloadInfo- -> Version- -> InstallDir- -> Bool -- ^ Force install- -> Excepts- '[ AlreadyInstalled- , CopyError- , DigestError- , ContentLengthError- , GPGError- , DownloadFailed- , NoDownload- , NotInstalled- , UnknownArchive- , TarDirDoesNotExist- , ArchiveResult- , FileAlreadyExistsError- , URIParseError- ]- m- ()-installStackBindist dlinfo ver installDir forceInstall = do- lift $ logDebug $ "Requested to install stack version " <> prettyVer ver-- PlatformRequest {..} <- lift getPlatformReq- Dirs {..} <- lift getDirs-- regularStackInstalled <- lift $ stackInstalled ver-- if- | not forceInstall- , regularStackInstalled- , GHCupInternal <- installDir -> do- throwE $ AlreadyInstalled Stack ver-- | forceInstall- , regularStackInstalled- , GHCupInternal <- installDir -> do- lift $ logInfo "Removing the currently installed version of Stack first!"- liftE $ rmStackVer ver-- | otherwise -> pure ()-- -- download (or use cached version)- dl <- liftE $ downloadCached dlinfo Nothing-- -- unpack- tmpUnpack <- lift withGHCupTmpDir- liftE $ cleanUpOnError tmpUnpack (unpackToDir (fromGHCupPath tmpUnpack) dl)- liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromGHCupPath tmpUnpack)-- -- the subdir of the archive where we do the work- workdir <- maybe (pure tmpUnpack) (liftE . intoSubdir tmpUnpack) (view dlSubdir dlinfo)-- case installDir of- IsolateDir isoDir -> do -- isolated install- lift $ logInfo $ "isolated installing Stack to " <> T.pack isoDir- liftE $ installStackUnpacked workdir (IsolateDirResolved isoDir) ver forceInstall- GHCupInternal -> do -- regular install- liftE $ installStackUnpacked workdir (GHCupBinDir binDir) ver forceInstall----- | Install an unpacked stack distribution.-installStackUnpacked :: (MonadReader env m, HasLog env, MonadCatch m, MonadIO m)- => GHCupPath -- ^ Path to the unpacked stack bindist (where the executable resides)- -> InstallDirResolved- -> Version- -> Bool -- ^ Force install- -> Excepts '[CopyError, FileAlreadyExistsError] m ()-installStackUnpacked path installDir ver forceInstall = do- lift $ logInfo "Installing stack"- let stackFile = "stack"- liftIO $ createDirRecursive' (fromInstallDir installDir)- let destFileName = stackFile- <> (case installDir of- IsolateDirResolved _ -> ""- _ -> ("-" <>) . T.unpack . prettyVer $ ver- )- <> exeExt- destPath = fromInstallDir installDir </> destFileName-- copyFileE- (fromGHCupPath path </> stackFile <> exeExt)- destPath- (not forceInstall)- lift $ chmod_755 destPath---- ------------------ --[ Set stack ]--- ---------------------- | Set the @~\/.ghcup\/bin\/stack@ symlink.-setStack :: ( MonadMask m- , MonadReader env m- , HasDirs env- , HasLog env- , MonadThrow m- , MonadFail m- , MonadIO m- , MonadUnliftIO m- )- => Version- -> Excepts '[NotInstalled] m ()-setStack ver = do- let targetFile = "stack-" <> T.unpack (prettyVer ver) <> exeExt-- -- symlink destination- Dirs {..} <- lift getDirs-- whenM (liftIO $ not <$> doesFileExist (binDir </> targetFile))- $ throwE- $ NotInstalled Stack (GHCTargetVersion Nothing ver)-- let stackbin = binDir </> "stack" <> exeExt-- lift $ createLink targetFile stackbin-- liftIO (isShadowed stackbin) >>= \case- Nothing -> pure ()- Just pa -> lift $ logWarn $ T.pack $ prettyHFError (ToolShadowed Stack pa stackbin ver)-- pure ()---unsetStack :: ( MonadMask m- , MonadReader env m- , HasDirs env- , MonadIO m)- => m ()-unsetStack = do- Dirs {..} <- getDirs- let stackbin = binDir </> "stack" <> exeExt- hideError doesNotExistErrorType $ rmLink stackbin--- ----------------- --[ Rm stack ]--- -------------------- | Delete a stack version. Will try to fix the @stack@ symlink--- after removal (e.g. setting it to an older version).-rmStackVer :: ( MonadMask m- , MonadReader env m- , HasDirs env- , MonadThrow m- , HasLog env- , MonadIO m- , MonadFail m- , MonadCatch m- , MonadUnliftIO m- )- => Version- -> Excepts '[NotInstalled] m ()-rmStackVer ver = do- whenM (lift $ fmap not $ stackInstalled ver) $ throwE (NotInstalled Stack (GHCTargetVersion Nothing ver))-- sSet <- lift stackSet-- Dirs {..} <- lift getDirs-- let stackFile = "stack-" <> T.unpack (prettyVer ver) <> exeExt- lift $ hideError doesNotExistErrorType $ recycleFile (binDir </> stackFile)-- when (Just ver == sSet) $ do- sVers <- lift $ fmap rights getInstalledStacks- case headMay . sortBy (comparing Down) $ sVers of- Just latestver -> setStack latestver- Nothing -> lift $ rmLink (binDir </> "stack" <> exeExt)
+ lib/GHCup/System/Cmd.hs view
@@ -0,0 +1,176 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}++module GHCup.System.Cmd where++import GHCup.Download+import GHCup.Errors+import GHCup.Prelude+import GHCup.Prelude.Process+import GHCup.Prelude.String.QQ+import GHCup.Query.GHCupDirs+import GHCup.Types+import GHCup.Types.Optics++import Control.Exception.Safe+import Control.Monad+import Control.Monad.Reader+import Control.Monad.Trans.Resource ( MonadResource )+import Data.ByteString ( ByteString )+import Data.Char ( isHexDigit )+import Data.List ( isPrefixOf, sort, stripPrefix )+import Data.Maybe ( catMaybes, isJust )+import Data.Variant.Excepts+import GHC.IO.Exception ( ExitCode (ExitFailure, ExitSuccess) )+import Safe ( atMay )+import System.FilePath ( getSearchPath, (</>) )+import System.IO.Error ( isDoesNotExistError, isPermissionError )+import Text.Regex.Posix+ ( RegexMaker (makeRegexOpts), compExtended, execBlank )+import URI.ByteString ( URI )++import qualified Data.Text as T+++-- | Calls gmake if it exists in PATH, otherwise make.+make :: ( MonadIOish m+ , MonadReader env m+ , HasDirs env+ , HasLog env+ , HasSettings env+ )+ => [String]+ -> Maybe FilePath+ -> m (Either ProcessError ())+make args workdir = make' args workdir "ghc-make" Nothing+++-- | Calls gmake if it exists in PATH, otherwise make.+make' :: ( MonadThrow m+ , MonadIO m+ , MonadReader env m+ , HasDirs env+ , HasLog env+ , HasSettings env+ )+ => [String]+ -> Maybe FilePath+ -> FilePath -- ^ log filename (opened in append mode)+ -> Maybe [(String, String)] -- ^ optional environment+ -> m (Either ProcessError ())+make' args workdir logfile menv = do+ mymake <- liftIO getBestMake+ execLogged mymake args workdir logfile menv++getBestMake :: IO FilePath+getBestMake = do+ spaths <- liftIO getSearchPath+ has_gmake <- isJust <$> liftIO (searchPath spaths "gmake")+ let mymake = if has_gmake then "gmake" else "make"+ pure mymake+++makeOut :: (MonadReader env m, HasDirs env, MonadIO m)+ => [String]+ -> Maybe FilePath+ -> m CapturedProcess+makeOut args workdir = do+ spaths <- liftIO getSearchPath+ has_gmake <- isJust <$> liftIO (searchPath spaths "gmake")+ let mymake = if has_gmake then "gmake" else "make"+ executeOut mymake args workdir+++-- | Try to apply patches in order. The order is determined by+-- a quilt series file (in the patch directory) if one exists,+-- else the patches are applied in lexicographical order.+-- Fails with 'PatchFailed' on first failure.+applyPatches :: (MonadReader env m, HasDirs env, HasLog env, MonadIO m)+ => FilePath -- ^ dir containing patches+ -> FilePath -- ^ dir to apply patches in+ -> Excepts '[PatchFailed] m ()+applyPatches pdir ddir = do+ let lexicographical = (fmap . fmap) (pdir </>) $ sort <$> findFiles+ pdir+ (makeRegexOpts compExtended+ execBlank+ ([s|.+\.(patch|diff)$|] :: ByteString)+ )+ let quilt = map (pdir </>) . lines <$> readFile (pdir </> "series")++ patches <- liftIO $ quilt `catchIO` (\e ->+ if isDoesNotExistError e || isPermissionError e then+ lexicographical+ else throwIO e)+ forM_ patches $ \patch' -> applyPatch patch' ddir+++applyPatch :: (MonadReader env m, HasDirs env, HasLog env, MonadIO m)+ => FilePath -- ^ Patch+ -> FilePath -- ^ dir to apply patches in+ -> Excepts '[PatchFailed] m ()+applyPatch patch ddir = do+ lift $ logInfo $ "Applying patch " <> T.pack patch+ fmap (either (const Nothing) Just)+ (exec+ "patch"+ ["-p1", "-s", "-f", "-i", patch]+ (Just ddir)+ Nothing)+ !? PatchFailed+++applyAnyPatch :: ( MonadReader env m+ , HasDirs env+ , HasLog env+ , HasSettings env+ , MonadResource m+ , MonadIOish m+ )+ => Maybe (Either FilePath [URI])+ -> FilePath+ -> Excepts '[PatchFailed, DownloadFailed, DigestError, ContentLengthError, GPGError] m ()+applyAnyPatch Nothing _ = pure ()+applyAnyPatch (Just (Left pdir)) workdir = liftE $ applyPatches pdir workdir+applyAnyPatch (Just (Right uris)) workdir = do+ tmpUnpack <- fromGHCupPath <$> lift withGHCupTmpDir+ forM_ uris $ \uri -> do+ patch <- liftE $ download uri Nothing Nothing Nothing tmpUnpack Nothing False+ liftE $ applyPatch patch workdir+++-- | https://gitlab.haskell.org/ghc/ghc/-/issues/17353+darwinNotarization :: (MonadReader env m, HasDirs env, MonadIO m)+ => Platform+ -> FilePath+ -> m (Either ProcessError ())+darwinNotarization Darwin path = exec+ "/usr/bin/xattr"+ ["-r", "-d", "com.apple.quarantine", path]+ Nothing+ Nothing+darwinNotarization _ _ = pure $ Right ()++gitOut :: (MonadReader env m, HasLog env, MonadIO m) => [String] -> FilePath -> Excepts '[ProcessError] m T.Text+gitOut args dir = do+ CapturedProcess {..} <- lift $ executeOut "git" args (Just dir)+ case _exitCode of+ ExitSuccess -> pure $ T.pack $ stripNewlineEnd $ T.unpack $ decUTF8Safe' _stdOut+ ExitFailure c -> do+ let pe = NonZeroExit c "git" args+ lift $ logDebug $ T.pack (prettyHFError pe)+ throwE pe++processBranches :: T.Text -> [String]+processBranches str' = let lines' = lines (T.unpack str')+ words' = fmap words lines'+ refs = catMaybes $ fmap (`atMay` 1) words'+ branches = catMaybes $ fmap (stripPrefix "refs/heads/") $ filter (isPrefixOf "refs/heads/") refs+ in branches++isCommitHash :: String -> Bool+isCommitHash str' = let hex = all isHexDigit str'+ len = length str'+ in hex && len == 40
+ lib/GHCup/System/Directory.hs view
@@ -0,0 +1,70 @@+module GHCup.System.Directory (++ -- System.Directory re-exports+ createDirectory+ , createDirectoryIfMissing+ , renameDirectory+ , listDirectory+ , getDirectoryContents+ , getCurrentDirectory+ , setCurrentDirectory+ , withCurrentDirectory+ , getHomeDirectory+ , XdgDirectory(..)+ , getXdgDirectory+ , XdgDirectoryList(..)+ , getXdgDirectoryList+ , getAppUserDataDirectory+ , getUserDocumentsDirectory+ , getTemporaryDirectory+ , removeFile+ , renameFile+ , renamePath+ , getFileSize+ , canonicalizePath+ , makeAbsolute+ , makeRelativeToCurrentDirectory+ , doesPathExist+ , doesFileExist+ , doesDirectoryExist+ , findExecutable+ , findExecutables+ , findExecutablesInDirectories+ , findFile+ , findFileWith+ , findFilesWith+ , exeExtension+ , createFileLink+ , createDirectoryLink+ , removeDirectoryLink+ , pathIsSymbolicLink+ , getSymbolicLinkTarget+ , Permissions+ , emptyPermissions+ , readable+ , writable+ , executable+ , searchable+ , setOwnerReadable+ , setOwnerWritable+ , setOwnerExecutable+ , setOwnerSearchable+ , getPermissions+ , setPermissions+ , copyPermissions+ , getAccessTime+ , getModificationTime+ , setAccessTime+ , setModificationTime+ , isSymbolicLink+ )+ where++import GHCup.Prelude.File.Search+import System.Directory hiding+ ( findFiles+ , makeAbsolute+ , removeDirectory+ , removeDirectoryRecursive+ , removePathForcibly+ )
lib/GHCup/Types.hs view
@@ -1,861 +1,1419 @@ {-# OPTIONS_GHC -Wno-orphans #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE DuplicateRecordFields #-}--{-|-Module : GHCup.Types-Description : GHCup types-Copyright : (c) Julian Ospald, 2020-License : LGPL-3.0-Maintainer : hasufell@hasufell.de-Stability : experimental-Portability : portable--}-module GHCup.Types- ( module GHCup.Types-#if defined(BRICK)- , Key(..)- , Modifier(..)-#endif- , ArchiveResult(..)- )- where--import GHCup.Types.Stack ( SetupInfo )-import GHCup.Utils.Tar.Types ( ArchiveResult(..) )-import {-# SOURCE #-} GHCup.Utils.Dirs ( fromGHCupPath, GHCupPath )--import Control.DeepSeq ( NFData, rnf )-import Data.Map.Strict ( Map )-import Data.List.NonEmpty ( NonEmpty (..) )-import Data.Time.Calendar ( Day )-import Data.Text ( Text )-import Data.Versions-import GHC.IO.Exception ( ExitCode )-import Text.PrettyPrint.HughesPJClass (Pretty, pPrint, text)-import URI.ByteString-#if defined(BRICK)-import Graphics.Vty ( Key(..), Modifier(..) )-#endif--import qualified Data.ByteString.Lazy as BL-import qualified Data.Text as T-import qualified GHC.Generics as GHC-import qualified Data.List.NonEmpty as NE--#if !defined(BRICK)-data Key = KEsc | KChar Char | KBS | KEnter- | KLeft | KRight | KUp | KDown- | KUpLeft | KUpRight | KDownLeft | KDownRight | KCenter- | KFun Int | KBackTab | KPrtScr | KPause | KIns- | KHome | KPageUp | KDel | KEnd | KPageDown | KBegin | KMenu- deriving (Eq,Show,Read,Ord,GHC.Generic)--data Modifier = MShift | MCtrl | MMeta | MAlt- deriving (Eq,Show,Read,Ord,GHC.Generic)-#endif--data KeyCombination = KeyCombination { key :: Key, mods :: [Modifier] }- deriving (Eq,Show,Read,Ord,GHC.Generic)---- --------------------- --[ GHCInfo Tree ]--- -----------------------data GHCupInfo = GHCupInfo- { _toolRequirements :: ToolRequirements- , _ghcupDownloads :: GHCupDownloads- , _metadataUpdate :: Maybe URI- }- deriving (Show, GHC.Generic, Eq)--instance NFData GHCupInfo---- -------------------------- --[ Requirements Tree ]--- ----------------------------type ToolRequirements = Map Tool ToolReqVersionSpec-type ToolReqVersionSpec = Map (Maybe Version) PlatformReqSpec-type PlatformReqSpec = MapIgnoreUnknownKeys Platform PlatformReqVersionSpec-type PlatformReqVersionSpec = Map (Maybe VersionRange) Requirements---data Requirements = Requirements- { _distroPKGs :: [Text]- , _notes :: Text- }- deriving (Show, GHC.Generic, Eq)--instance NFData Requirements------ ---------------------- --[ Download Tree ]--- -------------------------- | Description of all binary and source downloads. This is a tree--- of nested maps.-type GHCupDownloads = Map Tool ToolVersionSpec-type ToolVersionSpec = Map GHCTargetVersion VersionInfo-type ArchitectureSpec = MapIgnoreUnknownKeys Architecture PlatformSpec-type PlatformSpec = MapIgnoreUnknownKeys Platform PlatformVersionSpec-type PlatformVersionSpec = Map (Maybe VersionRange) DownloadInfo----- | An installable tool.-data Tool = GHC- | Cabal- | GHCup- | HLS- | Stack- deriving (Eq, GHC.Generic, Ord, Show, Enum, Bounded)--instance Pretty Tool where- pPrint GHC = text "ghc"- pPrint Cabal = text "cabal"- pPrint GHCup = text "ghcup"- pPrint HLS = text "hls"- pPrint Stack = text "stack"--instance NFData Tool----- | All necessary information of a tool version, including--- source download and per-architecture downloads.-data VersionInfo = VersionInfo- { _viTags :: [Tag] -- ^ version specific tag- , _viReleaseDay :: Maybe Day- , _viChangeLog :: Maybe URI- , _viSourceDL :: Maybe DownloadInfo -- ^ source tarball- , _viTestDL :: Maybe DownloadInfo -- ^ test tarball- , _viArch :: ArchitectureSpec -- ^ descend for binary downloads per arch- -- informative messages- , _viPreInstall :: Maybe Text- , _viPostInstall :: Maybe Text- , _viPostRemove :: Maybe Text- , _viPreCompile :: Maybe Text- }- deriving (Eq, GHC.Generic, Show)--instance NFData VersionInfo----- | A tag. These are currently attached to a version of a tool.-data Tag = Latest -- ^ the latest version of a tool (unique per tool)- | Recommended -- ^ the recommended version of a tool (unique per tool)- | Prerelease -- ^ denotes a prerelease version- -- (a version should either be 'Prerelease' or- -- 'LatestPrerelease', but not both)- | LatestPrerelease -- ^ the latest prerelease (unique per tool)- | Nightly -- ^ denotes a nightly version- -- (a version should either be 'Nightly' or- -- 'LatestNightly', but not both)- | LatestNightly -- ^ the latest nightly (unique per tool)- | Base PVP -- ^ the base version shipped with GHC- | Old -- ^ old versions are hidden by default in TUI- | Experimental -- ^ an experiemntal version/bindist- | UnknownTag String -- ^ used for upwardscompat- deriving (Ord, Eq, GHC.Generic, Show) -- FIXME: manual JSON instance--instance NFData Tag--tagToString :: Tag -> String-tagToString Recommended = "recommended"-tagToString Latest = "latest"-tagToString Prerelease = "prerelease"-tagToString Nightly = "nightly"-tagToString (Base pvp'') = "base-" ++ T.unpack (prettyPVP pvp'')-tagToString (UnknownTag t ) = t-tagToString LatestPrerelease = "latest-prerelease"-tagToString LatestNightly = "latest-nightly"-tagToString Experimental = "experimental"-tagToString Old = ""--instance Pretty Tag where- pPrint Recommended = text "recommended"- pPrint Latest = text "latest"- pPrint Prerelease = text "prerelease"- pPrint Nightly = text "nightly"- pPrint (Base pvp'') = text ("base-" ++ T.unpack (prettyPVP pvp''))- pPrint (UnknownTag t ) = text t- pPrint LatestPrerelease = text "latest-prerelease"- pPrint LatestNightly = text "latest-prerelease"- pPrint Experimental = text "experimental"- pPrint Old = mempty--data Architecture = A_64- | A_32- | A_PowerPC- | A_PowerPC64- | A_Sparc- | A_Sparc64- | A_ARM- | A_ARM64- deriving (Eq, GHC.Generic, Ord, Show, Bounded, Enum)--instance NFData Architecture--archToString :: Architecture -> String-archToString A_64 = "x86_64"-archToString A_32 = "i386"-archToString A_PowerPC = "powerpc"-archToString A_PowerPC64 = "powerpc64"-archToString A_Sparc = "sparc"-archToString A_Sparc64 = "sparc64"-archToString A_ARM = "arm"-archToString A_ARM64 = "aarch64"--instance Pretty Architecture where- pPrint = text . archToString--data Platform = Linux LinuxDistro- -- ^ must exit- | Darwin- -- ^ must exit- | FreeBSD- | OpenBSD- | Windows- -- ^ must exit- deriving (Eq, GHC.Generic, Ord, Show)--instance NFData Platform--platformToString :: Platform -> String-platformToString (Linux distro) = "linux-" ++ distroToString distro-platformToString Darwin = "darwin"-platformToString FreeBSD = "freebsd"-platformToString OpenBSD = "openbsd"-platformToString Windows = "windows"--instance Pretty Platform where- pPrint = text . platformToString--data LinuxDistro = Debian- | Ubuntu- | Mint- | Fedora- | CentOS- | RedHat- | Alpine- | AmazonLinux- | Rocky- | Void- -- rolling- | Gentoo- | Exherbo- | OpenSUSE- -- not known- | UnknownLinux- deriving (Bounded, Eq, Enum, GHC.Generic, Ord, Show)--allDistros :: [LinuxDistro]-allDistros = enumFromTo minBound maxBound--instance NFData LinuxDistro--distroToString :: LinuxDistro -> String-distroToString Debian = "debian"-distroToString Ubuntu = "ubuntu"-distroToString Mint = "mint"-distroToString Fedora = "fedora"-distroToString CentOS = "centos"-distroToString RedHat = "redhat"-distroToString Alpine = "alpine"-distroToString AmazonLinux = "amazon"-distroToString Rocky = "rocky"-distroToString Void = "void"-distroToString Gentoo = "gentoo"-distroToString Exherbo = "exherbo"-distroToString OpenSUSE = "opensuse"-distroToString UnknownLinux = "unknown"--instance Pretty LinuxDistro where- pPrint = text . distroToString----- | An encapsulation of a download. This can be used--- to download, extract and install a tool.-data DownloadInfo = DownloadInfo- { _dlUri :: Text- , _dlSubdir :: Maybe TarDir- , _dlHash :: Text- , _dlCSize :: Maybe Integer- , _dlOutput :: Maybe FilePath- , _dlTag :: Maybe [Tag]- }- deriving (Eq, Ord, GHC.Generic, Show)--instance NFData DownloadInfo---- --------------- --[ Others ]--- ----------------data DownloadMirror = DownloadMirror {- authority :: Authority- , pathPrefix :: Maybe Text-} deriving (Eq, Ord, GHC.Generic, Show)--instance NFData DownloadMirror--newtype DownloadMirrors = DM (Map Text DownloadMirror)- deriving (Eq, Ord, GHC.Generic, Show)--instance NFData DownloadMirrors--instance NFData UserInfo-instance NFData Host-instance NFData Port-instance NFData Authority----- | How to descend into a tar archive.-data TarDir = RealDir FilePath- | RegexDir String -- ^ will be compiled to regex, the first match will "win"- deriving (Eq, Ord, GHC.Generic, Show)--instance NFData TarDir--instance Pretty TarDir where- pPrint (RealDir path) = text path- pPrint (RegexDir regex) = text regex----- | Where to fetch GHCupDownloads from.-data URLSource = GHCupURL- | StackSetupURL- | OwnSource [Either (Either GHCupInfo SetupInfo) URI] -- ^ complete source list- | OwnSpec (Either GHCupInfo SetupInfo)- | AddSource [Either (Either GHCupInfo SetupInfo) URI] -- ^ merge with GHCupURL- | SimpleList [NewURLSource]- deriving (Eq, GHC.Generic, Show)--data NewURLSource = NewGHCupURL- | NewStackSetupURL- | NewGHCupInfo GHCupInfo- | NewSetupInfo SetupInfo- | NewURI URI- | NewChannelAlias ChannelAlias- deriving (Eq, GHC.Generic, Show)--instance NFData NewURLSource---- | Alias for ease of URLSource selection-data ChannelAlias = DefaultChannel- | StackChannel- | CrossChannel- | PrereleasesChannel- | VanillaChannel- deriving (Eq, GHC.Generic, Show, Enum, Bounded)--channelAliasText :: ChannelAlias -> Text-channelAliasText DefaultChannel = "default"-channelAliasText StackChannel = "stack"-channelAliasText CrossChannel = "cross"-channelAliasText PrereleasesChannel = "prereleases"-channelAliasText VanillaChannel = "vanilla"--fromURLSource :: URLSource -> [NewURLSource]-fromURLSource GHCupURL = [NewGHCupURL]-fromURLSource StackSetupURL = [NewStackSetupURL]-fromURLSource (OwnSource arr) = convert' <$> arr-fromURLSource (AddSource arr) = NewGHCupURL:(convert' <$> arr)-fromURLSource (SimpleList arr) = arr-fromURLSource (OwnSpec (Left gi)) = [NewGHCupInfo gi]-fromURLSource (OwnSpec (Right si)) = [NewSetupInfo si]--convert' :: Either (Either GHCupInfo SetupInfo) URI -> NewURLSource-convert' (Left (Left gi)) = NewGHCupInfo gi-convert' (Left (Right si)) = NewSetupInfo si-convert' (Right uri) = NewURI uri--instance NFData URLSource-instance NFData ChannelAlias-instance NFData (URIRef Absolute) where- rnf (URI !_ !_ !_ !_ !_) = ()---data MetaMode = Strict- | Lax- deriving (Show, Read, Eq, GHC.Generic)--instance NFData MetaMode--data UserSettings = UserSettings- { uCache :: Maybe Bool- , uMetaCache :: Maybe Integer- , uMetaMode :: Maybe MetaMode- , uNoVerify :: Maybe Bool- , uVerbose :: Maybe Bool- , uKeepDirs :: Maybe KeepDirs- , uDownloader :: Maybe Downloader- , uKeyBindings :: Maybe UserKeyBindings- , uUrlSource :: Maybe URLSource- , uNoNetwork :: Maybe Bool- , uGPGSetting :: Maybe GPGSetting- , uPlatformOverride :: Maybe PlatformRequest- , uMirrors :: Maybe DownloadMirrors- , uDefGHCConfOptions :: Maybe [String]- , uPager :: Maybe PagerConfig- , uGuessVersion :: Maybe Bool- }- deriving (Show, GHC.Generic, Eq)--defaultUserSettings :: UserSettings-defaultUserSettings = UserSettings Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing--fromSettings :: Settings -> Maybe KeyBindings -> UserSettings-fromSettings Settings{..} Nothing =- UserSettings {- uCache = Just cache- , uMetaCache = Just metaCache- , uMetaMode = Just metaMode- , uNoVerify = Just noVerify- , uVerbose = Just verbose- , uKeepDirs = Just keepDirs- , uDownloader = Just downloader- , uNoNetwork = Just noNetwork- , uKeyBindings = Nothing- , uUrlSource = Just (SimpleList urlSource)- , uGPGSetting = Just gpgSetting- , uPlatformOverride = platformOverride- , uMirrors = Just mirrors- , uDefGHCConfOptions = Just defGHCConfOptions- , uPager = Just pager- , uGuessVersion = Just guessVersion- }-fromSettings Settings{..} (Just KeyBindings{..}) =- let ukb = UserKeyBindings- { kUp = Just bUp- , kDown = Just bDown- , kQuit = Just bQuit- , kInstall = Just bInstall- , kUninstall = Just bUninstall- , kSet = Just bSet- , kChangelog = Just bChangelog- , kShowAll = Just bShowAllVersions- }- in UserSettings {- uCache = Just cache- , uMetaCache = Just metaCache- , uMetaMode = Just metaMode- , uNoVerify = Just noVerify- , uVerbose = Just verbose- , uKeepDirs = Just keepDirs- , uDownloader = Just downloader- , uNoNetwork = Just noNetwork- , uKeyBindings = Just ukb- , uUrlSource = Just (SimpleList urlSource)- , uGPGSetting = Just gpgSetting- , uPlatformOverride = platformOverride- , uMirrors = Just mirrors- , uDefGHCConfOptions = Just defGHCConfOptions- , uPager = Just pager- , uGuessVersion = Just guessVersion- }--data UserKeyBindings = UserKeyBindings- { kUp :: Maybe KeyCombination- , kDown :: Maybe KeyCombination- , kQuit :: Maybe KeyCombination- , kInstall :: Maybe KeyCombination- , kUninstall :: Maybe KeyCombination- , kSet :: Maybe KeyCombination- , kChangelog :: Maybe KeyCombination- , kShowAll :: Maybe KeyCombination- }- deriving (Show, GHC.Generic, Eq)--data KeyBindings = KeyBindings- { bUp :: KeyCombination- , bDown :: KeyCombination- , bQuit :: KeyCombination- , bInstall :: KeyCombination- , bUninstall :: KeyCombination- , bSet :: KeyCombination- , bChangelog :: KeyCombination- , bShowAllVersions :: KeyCombination- }- deriving (Show, GHC.Generic)--instance NFData KeyBindings-#if !defined(BRICK)-instance NFData Key--instance NFData Modifier--#endif-instance NFData KeyCombination--defaultKeyBindings :: KeyBindings-defaultKeyBindings = KeyBindings- { bUp = KeyCombination { key = KUp , mods = [] }- , bDown = KeyCombination { key = KDown , mods = [] }- , bQuit = KeyCombination { key = KChar 'q', mods = [] }- , bInstall = KeyCombination { key = KChar 'i', mods = [] }- , bUninstall = KeyCombination { key = KChar 'u', mods = [] }- , bSet = KeyCombination { key = KChar 's', mods = [] }- , bChangelog = KeyCombination { key = KChar 'c', mods = [] }- , bShowAllVersions = KeyCombination { key = KChar 'a', mods = [] }- }--data AppState = AppState- { settings :: Settings- , dirs :: Dirs- , keyBindings :: KeyBindings- , ghcupInfo :: GHCupInfo- , pfreq :: PlatformRequest- , loggerConfig :: LoggerConfig- } deriving (Show, GHC.Generic)--instance NFData AppState--fromAppState :: AppState -> LeanAppState-fromAppState AppState {..} = LeanAppState {..}--data LeanAppState = LeanAppState- { settings :: Settings- , dirs :: Dirs- , keyBindings :: KeyBindings- , loggerConfig :: LoggerConfig- } deriving (Show, GHC.Generic)--instance NFData LeanAppState---data Settings = Settings- { cache :: Bool- , metaCache :: Integer- , metaMode :: MetaMode- , noVerify :: Bool- , keepDirs :: KeepDirs- , downloader :: Downloader- , verbose :: Bool- , urlSource :: [NewURLSource]- , noNetwork :: Bool- , gpgSetting :: GPGSetting- , noColor :: Bool -- this also exists in LoggerConfig- , platformOverride :: Maybe PlatformRequest- , mirrors :: DownloadMirrors- , defGHCConfOptions :: [String]- , pager :: PagerConfig- , guessVersion :: Bool- }- deriving (Show, GHC.Generic)--data PagerConfig = PagerConfig {- pagerList :: Bool- , pagerCmd :: Maybe String- }- deriving (Show, GHC.Generic, Eq)--instance NFData PagerConfig--defaultPagerConfig :: PagerConfig-defaultPagerConfig = PagerConfig False Nothing--allPagerConfig :: String -> PagerConfig-allPagerConfig cmd = PagerConfig True (Just cmd)--defaultMetaCache :: Integer-defaultMetaCache = 300 -- 5 minutes--defaultSettings :: Settings-defaultSettings = Settings False defaultMetaCache Lax False Never Curl False [NewGHCupURL] False GPGNone False Nothing (DM mempty) [] defaultPagerConfig True--instance NFData Settings--data Dirs = Dirs- { baseDir :: GHCupPath- , binDir :: FilePath- , cacheDir :: GHCupPath- , logsDir :: GHCupPath- , confDir :: GHCupPath- , dbDir :: GHCupPath- , recycleDir :: GHCupPath -- mainly used on windows- , tmpDir :: GHCupPath- , msys2Dir :: FilePath- }- deriving (Show, GHC.Generic)--instance NFData Dirs--data MSYS2Env = MSYS- | UCRT64- | CLANG64- | CLANGARM64- | CLANG32- | MINGW64- | MINGW32- deriving (Eq, Show, Ord, GHC.Generic, Read)--instance NFData MSYS2Env--data KeepDirs = Always- | Errors- | Never- deriving (Eq, Show, Ord, GHC.Generic)--instance NFData KeepDirs--data Downloader = Curl- | Wget-#if defined(INTERNAL_DOWNLOADER)- | Internal-#endif- deriving (Eq, Show, Ord, GHC.Generic)--instance NFData Downloader--data GPGSetting = GPGStrict- | GPGLax- | GPGNone- deriving (Eq, Show, Ord, GHC.Generic)--instance NFData GPGSetting--data DebugInfo = DebugInfo- { diDirs :: Dirs- , diArch :: Architecture- , diPlatform :: PlatformResult- , diChannels :: [(ChannelAlias, URI)]- , diShimGenURL :: URI- }- deriving Show---data SetGHC = SetGHCOnly -- ^ unversioned 'ghc'- | SetGHC_XY -- ^ ghc-x.y- | SetGHC_XYZ -- ^ ghc-x.y.z- deriving (Eq, Show)--data SetHLS = SetHLSOnly -- ^ unversioned 'hls'- | SetHLS_XYZ -- ^ haskell-language-server-a.b.c~x.y.z, where a.b.c is GHC version and x.y.z is HLS version- deriving (Eq, Show)---data PlatformResult = PlatformResult- { _platform :: Platform- , _distroVersion :: Maybe Versioning- }- deriving (Eq, Show, GHC.Generic)--instance NFData PlatformResult--platResToString :: PlatformResult -> String-platResToString PlatformResult { _platform = plat, _distroVersion = Just v' }- = show plat <> ", " <> T.unpack (prettyV v')-platResToString PlatformResult { _platform = plat, _distroVersion = Nothing }- = show plat--instance Pretty PlatformResult where- pPrint = text . platResToString--data PlatformRequest = PlatformRequest- { _rArch :: Architecture- , _rPlatform :: Platform- , _rVersion :: Maybe Versioning- }- deriving (Eq, Show, GHC.Generic)--instance NFData PlatformRequest--pfReqToString :: PlatformRequest -> String-pfReqToString (PlatformRequest arch plat ver) =- archToString arch ++ "-" ++ platformToString plat ++ pver- where- pver = case ver of- Just v' -> "-" ++ T.unpack (prettyV v')- Nothing -> ""--instance Pretty PlatformRequest where- pPrint = text . pfReqToString---- | A GHC identified by the target platform triple--- and the version.-data GHCTargetVersion = GHCTargetVersion- { _tvTarget :: Maybe Text- , _tvVersion :: Version- }- deriving (Ord, Eq, Show, GHC.Generic)--instance NFData GHCTargetVersion--data GitBranch = GitBranch- { ref :: String- , repo :: Maybe String- }- deriving (Ord, Eq, Show)--mkTVer :: Version -> GHCTargetVersion-mkTVer = GHCTargetVersion Nothing--tVerToText :: GHCTargetVersion -> Text-tVerToText (GHCTargetVersion (Just t) v') = t <> "-" <> prettyVer v'-tVerToText (GHCTargetVersion Nothing v') = prettyVer v'---- | Assembles a path of the form: <target-triple>-<version>-instance Pretty GHCTargetVersion where- pPrint = text . T.unpack . tVerToText----- | A comparator and a version.-data VersionCmp = VR_gt Versioning- | VR_gteq Versioning- | VR_lt Versioning- | VR_lteq Versioning- | VR_eq Versioning- deriving (Eq, GHC.Generic, Ord, Show)--instance NFData VersionCmp----- | A version range. Supports && and ||, but not arbitrary--- combinations. This is a little simplified.-data VersionRange = SimpleRange (NonEmpty VersionCmp) -- And- | OrRange (NonEmpty VersionCmp) VersionRange- deriving (Eq, GHC.Generic, Ord, Show)--instance NFData VersionRange--instance Pretty VersionCmp where- pPrint (VR_gt v) = text "> " <> pPrint v- pPrint (VR_gteq v) = text ">= " <> pPrint v- pPrint (VR_lt v) = text "< " <> pPrint v- pPrint (VR_lteq v) = text "<= " <> pPrint v- pPrint (VR_eq v) = text "= " <> pPrint v--instance Pretty VersionRange where- pPrint (SimpleRange xs) = foldl1 (\x y -> x <> text " && " <> y) $ NE.map pPrint xs- pPrint (OrRange xs vr) = foldMap pPrint xs <> " || " <> pPrint vr--instance Pretty Versioning where- pPrint = text . T.unpack . prettyV--instance Pretty Version where- pPrint = text . T.unpack . prettyVer--instance Show (a -> b) where- show _ = "<function>"--instance Show (IO ()) where- show _ = "<io>"---data LogLevel = Warn- | Info- | Debug- | Error- deriving (Eq, Ord, Show)--data LoggerConfig = LoggerConfig- { lcPrintDebug :: Bool -- ^ whether to print debug in colorOutter- , consoleOutter :: T.Text -> IO () -- ^ how to write the console output- , fileOutter :: T.Text -> IO () -- ^ how to write the file output- , fancyColors :: Bool- }- deriving Show--instance NFData LoggerConfig where- rnf (LoggerConfig !lcPrintDebug !_ !_ !fancyColors) = rnf (lcPrintDebug, fancyColors)--data ProcessError = NonZeroExit Int FilePath [String]- | PTerminated FilePath [String]- | PStopped FilePath [String]- | NoSuchPid FilePath [String]- deriving Show---data CapturedProcess = CapturedProcess- { _exitCode :: ExitCode- , _stdOut :: BL.ByteString- , _stdErr :: BL.ByteString- }- deriving (Eq, Show)----data InstallDir = IsolateDir FilePath- | GHCupInternal- deriving (Eq, Show)--data InstallDirResolved = IsolateDirResolved FilePath- | GHCupDir GHCupPath- | GHCupBinDir FilePath- deriving (Eq, Show)--fromInstallDir :: InstallDirResolved -> FilePath-fromInstallDir (IsolateDirResolved fp) = fp-fromInstallDir (GHCupDir fp) = fromGHCupPath fp-fromInstallDir (GHCupBinDir fp) = fp---isSafeDir :: InstallDirResolved -> Bool-isSafeDir (IsolateDirResolved _) = False-isSafeDir (GHCupDir _) = True-isSafeDir (GHCupBinDir _) = False--type PromptQuestion = Text--data PromptResponse = PromptYes | PromptNo- deriving (Show, Eq)--data ToolVersion = GHCVersion GHCTargetVersion- | ToolVersion Version- | ToolTag Tag- | ToolDay Day- deriving (Eq, Show)--instance Pretty ToolVersion where- pPrint (GHCVersion v) = pPrint v- pPrint (ToolVersion v) = pPrint v- pPrint (ToolTag t) = pPrint t- pPrint (ToolDay d) = text (show d)----data BuildSystem = Hadrian- | Make- deriving (Show, Eq)---data VersionPattern = CabalVer- | GitHashShort- | GitHashLong- | GitDescribe- | GitBranchName- | S String- deriving (Eq, Show)---- | Map with custom FromJSON instance which ignores unknown keys-newtype MapIgnoreUnknownKeys k v = MapIgnoreUnknownKeys { unMapIgnoreUnknownKeys :: Map k v }- deriving (Eq, Show, GHC.Generic)--instance (NFData k, NFData v) => NFData (MapIgnoreUnknownKeys k v)---- | Type representing our guessing modes when e.g. "incomplete" PVP version--- is specified, such as @ghcup set ghc 9.12@.-data GuessMode = GStrict -- ^ don't guess the proper tool version- | GLax -- ^ guess by using the metadata- | GLaxWithInstalled -- ^ guess by using metadata and installed versions+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+#if defined(DHALL)+{-# LANGUAGE DeriveAnyClass #-}+#endif++{-|+Module : GHCup.Types+Description : GHCup types+Copyright : (c) Julian Ospald, 2020+License : LGPL-3.0+Maintainer : hasufell@hasufell.de+Stability : experimental+Portability : portable+-}+module GHCup.Types+ ( module GHCup.Types+#if defined(BRICK)+ , Key(..)+ , Modifier(..)+#endif+ , ArchiveResult(..)+ )+ where++import {-# SOURCE #-} GHCup.Query.GHCupDirs ( GHCupPath, fromGHCupPath )+import GHCup.Types.Stack ( SetupInfo )+import GHCup.Types.Tar ( ArchiveResult (..) )++import Control.DeepSeq ( NFData, rnf )+import Control.Monad.Catch+import Control.Monad.IO.Class+import Control.Monad.Trans.Resource+#if defined(DHALL)+import Dhall ( FromDhall, ToDhall )+#endif+import Data.List.NonEmpty ( NonEmpty (..) )+import Data.Map.Strict ( Map )+import Data.Text ( Text )+import Data.Time.Calendar ( Day )+import Data.Versions+import GHC.IO.Exception ( ExitCode )+import Text.PrettyPrint.HughesPJClass ( Pretty, pPrint, text )+import URI.ByteString+#if defined(BRICK)+import Graphics.Vty ( Key (..), Modifier (..) )+#endif+import System.FilePattern++import qualified Data.ByteString.Lazy as BL+import qualified Data.List.NonEmpty as NE+import qualified Data.Text as T+import qualified GHC.Generics as GHC+import System.FilePath ( takeFileName, (<.>) )+import qualified System.FilePath.Posix as Posix+++ -------------------------+ --[ Typeclass aliases ]--+ -------------------------++++type MonadIOish m = ( MonadMask m+ , MonadCatch m+ , MonadIO m+ , MonadUnliftIO m+ , MonadFail m+ , MonadThrow m+ )++ --------------------+ --[ Brick compat ]--+ --------------------++#if !defined(BRICK)+data Key+ = KEsc+ | KChar Char+ | KBS+ | KEnter+ | KLeft+ | KRight+ | KUp+ | KDown+ | KUpLeft+ | KUpRight+ | KDownLeft+ | KDownRight+ | KCenter+ | KFun Int+ | KBackTab+ | KPrtScr+ | KPause+ | KIns+ | KHome+ | KPageUp+ | KDel+ | KEnd+ | KPageDown+ | KBegin+ | KMenu+ deriving (Eq, GHC.Generic, Ord, Read, Show)++data Modifier+ = MShift+ | MCtrl+ | MMeta+ | MAlt+ deriving (Eq, GHC.Generic, Ord, Read, Show)+#endif++data KeyCombination = KeyCombination+ { key :: Key+ , mods :: [Modifier]+ }+ deriving (Eq, GHC.Generic, Ord, Read, Show)++++ --------------------+ --[ GHCInfo Tree ]--+ --------------------+++data GHCupInfo = GHCupInfo+ { _toolRequirements :: ToolRequirements+ , _ghcupDownloads :: GHCupDownloads+ , _metadataUpdate :: Maybe URI+ }+ deriving (Eq, GHC.Generic, Show)++instance NFData GHCupInfo++++ -------------------------+ --[ Requirements Tree ]--+ -------------------------+++type ToolRequirements = Map Tool ToolReqVersionSpec+type ToolReqVersionSpec = Map (Maybe Version) PlatformReqSpec+type PlatformReqSpec = MapIgnoreUnknownKeys Platform PlatformReqVersionSpec+type PlatformReqVersionSpec = Map (Maybe VersionRange) Requirements+++data Requirements = Requirements+ { _distroPKGs :: [Text]+ , _notes :: Text+ }+ deriving (Eq, GHC.Generic, Show)++instance NFData Requirements++++++ ---------------------+ --[ Download Tree ]--+ ---------------------+++-- | Description of all binary and source downloads. This is a tree+-- of nested maps.+newtype GHCupDownloads = GHCupDownloads+ { unGHCupDownloads :: Map Tool ToolInfo }+ deriving (Eq, GHC.Generic, Ord, Show)++newtype ToolVersionSpec = ToolVersionSpec+ { unToolVersionSpec :: Map TargetVersion VersionMetadata }+ deriving (Eq, GHC.Generic, Ord, Show)++newtype RevisionSpec = RevisionSpec+ { unRevisionSpec :: Map Rev VersionInfo }+ deriving (Eq, GHC.Generic, Ord, Show)++-- for compatibility with Dhall format,+-- which uses:+-- >+-- > revisionSpec:+-- > - mapKey: 0+-- > mapValue:+-- > viArch:+-- > ...+newtype RevisionSpecDhall = RevisionSpecDhall+ { unRevisionSpecDhall :: [DhallRevision] }+ deriving (Eq, GHC.Generic, Ord, Show)++data DhallRevision = DhallRevision {+ mapKey :: Int+ , mapValue :: VersionInfo+ }+ deriving (Eq, GHC.Generic, Ord, Show)++newtype ArchitectureSpec = ArchitectureSpec+ { unArchitectureSpnec :: MapIgnoreUnknownKeys Architecture PlatformSpec }+ deriving (Eq, GHC.Generic, Ord, Show)++newtype PlatformSpec = PlatformSpec+ { unPlatformSpec :: MapIgnoreUnknownKeys Platform PlatformVersionSpec }+ deriving (Eq, GHC.Generic, Ord, Show)++newtype PlatformVersionSpec = PlatformVersionSpec+ { unPlatformVersionSpec :: Map (Maybe VersionRange) DownloadInfo }+ deriving (Eq, GHC.Generic, Ord, Show)++instance NFData GHCupDownloads+instance NFData ToolVersionSpec+instance NFData RevisionSpec+instance NFData ArchitectureSpec+instance NFData PlatformSpec+instance NFData PlatformVersionSpec++data ToolInfo = ToolInfo {+ _toolVersions :: ToolVersionSpec+ , _toolDetails :: Maybe ToolDescription+ }+ deriving (Eq, GHC.Generic, Ord, Show)++instance NFData ToolInfo++data ToolDescription = ToolDescription {+ _toolHomepage :: Maybe URI+ , _toolRepository :: Maybe URI+ , _toolDescription :: Text+ , _toolAuthor :: Maybe String+ , _toolMaintainer :: Maybe String+ , _toolLicense :: Maybe String+ , _toolContact :: Maybe String+ }+ deriving (Eq, GHC.Generic, Show, Ord)++instance NFData ToolDescription++-- | An installable tool.+newtype Tool = Tool String+ deriving (Eq, GHC.Generic, Show, Ord+#if defined(DHALL)+ , FromDhall+ , ToDhall+#endif+ )++-- this type is not consistently used... it's mainly to work+-- around quirks in Dhall, so we can have a proper instance on @Map Rev Blah@+newtype Rev = Rev { unRev :: Int }+ deriving (Eq, GHC.Generic, Show, Ord)++instance NFData Rev++toolPriority :: Tool -> Maybe Int+toolPriority (Tool "ghc") = Just 1+toolPriority (Tool "cabal") = Just 2+toolPriority (Tool "hls") = Just 3+toolPriority (Tool "stack") = Just 4+toolPriority (Tool "ghcup") = Just 5+toolPriority (Tool _) = Nothing+++ghc, hls, stack, cabal, ghcup :: Tool+ghc = Tool "ghc"+hls = Tool "hls"+stack = Tool "stack"+cabal = Tool "cabal"+ghcup = Tool "ghcup"++instance Pretty Tool where+ pPrint (Tool t) = text t++instance NFData Tool++data VersionMetadata = VersionMetadata+ { _vmTags :: [Tag]+ , _vmReleaseDay :: Maybe Day+ , _vmChangeLog :: Maybe URI+ , _vmPreInstall :: Maybe Text+ , _vmPostInstall :: Maybe Text+ , _vmPostRemove :: Maybe Text+ , _vmPreCompile :: Maybe Text+ , _vmRevisionSpec :: RevisionSpec+ }+ deriving (Eq, GHC.Generic, Ord, Show)++instance NFData VersionMetadata++-- | All necessary information of a tool version, including+-- source download and per-architecture downloads.+data VersionInfo = VersionInfo+ { _viSourceDL :: Maybe DownloadInfo+ -- ^ source tarball+ , _viTestDL :: Maybe DownloadInfo+ -- ^ test tarball+ , _viArch :: ArchitectureSpec+ -- ^ descend for binary downloads per arch+ -- informative messages+ }+ deriving (Eq, GHC.Generic, Ord, Show)++instance NFData VersionInfo+++-- | A tag. These are currently attached to a version of a tool.+data Tag+ = Latest -- ^ the latest version of a tool (unique per tool)+ | Recommended -- ^ the recommended version of a tool (unique per tool)+ | Prerelease -- ^ denotes a prerelease version+ -- (a version should either be 'Prerelease' or+ -- 'LatestPrerelease', but not both)+ | LatestPrerelease -- ^ the latest prerelease (unique per tool)+ | Nightly -- ^ denotes a nightly version+ -- (a version should either be 'Nightly' or+ -- 'LatestNightly', but not both)+ | LatestNightly -- ^ the latest nightly (unique per tool)+ | Base PVP -- ^ the base version shipped with GHC+ | Old -- ^ old versions are hidden by default in TUI+ | Experimental -- ^ an experimental version/bindist+ | UnknownTag String -- ^ used for upwardscompat+ deriving (Eq, GHC.Generic, Ord, Show)++instance NFData Tag++tagToString :: Tag -> String+tagToString Recommended = "recommended"+tagToString Latest = "latest"+tagToString Prerelease = "prerelease"+tagToString Nightly = "nightly"+tagToString (Base pvp'') = "base-" ++ T.unpack (prettyPVP pvp'')+tagToString (UnknownTag t ) = t+tagToString LatestPrerelease = "latest-prerelease"+tagToString LatestNightly = "latest-nightly"+tagToString Experimental = "experimental"+tagToString Old = ""++instance Pretty Tag where+ pPrint Recommended = text "recommended"+ pPrint Latest = text "latest"+ pPrint Prerelease = text "prerelease"+ pPrint Nightly = text "nightly"+ pPrint (Base pvp'') = text ("base-" ++ T.unpack (prettyPVP pvp''))+ pPrint (UnknownTag t ) = text t+ pPrint LatestPrerelease = text "latest-prerelease"+ pPrint LatestNightly = text "latest-prerelease"+ pPrint Experimental = text "experimental"+ pPrint Old = mempty++data Architecture+ = A_64+ | A_32+ | A_PowerPC+ | A_PowerPC64+ | A_Sparc+ | A_Sparc64+ | A_ARM+ | A_ARM64+ deriving (Bounded, Enum, Eq, GHC.Generic, Ord, Show)++instance NFData Architecture++archToString :: Architecture -> String+archToString A_64 = "x86_64"+archToString A_32 = "i386"+archToString A_PowerPC = "powerpc"+archToString A_PowerPC64 = "powerpc64"+archToString A_Sparc = "sparc"+archToString A_Sparc64 = "sparc64"+archToString A_ARM = "arm"+archToString A_ARM64 = "aarch64"++instance Pretty Architecture where+ pPrint = text . archToString++data Platform+ = Linux LinuxDistro+ -- ^ must exit+ | Darwin+ -- ^ must exit+ | FreeBSD+ | OpenBSD+ | Windows+ -- ^ must exit+ deriving (Eq, GHC.Generic, Ord, Show)++instance NFData Platform++platformToString :: Platform -> String+platformToString (Linux distro) = "linux-" ++ distroToString distro+platformToString Darwin = "darwin"+platformToString FreeBSD = "freebsd"+platformToString OpenBSD = "openbsd"+platformToString Windows = "windows"++instance Pretty Platform where+ pPrint = text . platformToString++data LinuxDistro = Debian+ | Ubuntu+ | Mint+ | Fedora+ | CentOS+ | RedHat+ | Alpine+ | AmazonLinux+ | Rocky+ | Void+ -- rolling+ | Gentoo+ | Exherbo+ | OpenSUSE+ -- not known+ | UnknownLinux+ deriving (Bounded, Eq, Enum, GHC.Generic, Ord, Show+#if defined(DHALL)+ , FromDhall+ , ToDhall+#endif+ )++allDistros :: [LinuxDistro]+allDistros = enumFromTo minBound maxBound++instance NFData LinuxDistro++distroToString :: LinuxDistro -> String+distroToString Debian = "debian"+distroToString Ubuntu = "ubuntu"+distroToString Mint = "mint"+distroToString Fedora = "fedora"+distroToString CentOS = "centos"+distroToString RedHat = "redhat"+distroToString Alpine = "alpine"+distroToString AmazonLinux = "amazon"+distroToString Rocky = "rocky"+distroToString Void = "void"+distroToString Gentoo = "gentoo"+distroToString Exherbo = "exherbo"+distroToString OpenSUSE = "opensuse"+distroToString UnknownLinux = "unknown"++instance Pretty LinuxDistro where+ pPrint = text . distroToString+++-- TODO: rename+-- | An encapsulation of a download. This can be used+-- to download, extract and install a tool.+data DownloadInfo = DownloadInfo+ { _dlUri :: Text+ , _dlSubdir :: Maybe TarDir+ , _dlHash :: Text+ , _dlCSize :: Maybe Integer+ , _dlOutput :: Maybe FilePath+ , _dlTag :: Maybe [Tag]+ , _dlInstallSpec :: Maybe InstallationSpecInput+ }+ deriving (Eq, GHC.Generic, Ord, Show)++instance NFData DownloadInfo++data InstallMetadata = InstallMetadata {+ _imDownloadInfo :: DownloadInfo+ , _imResolvedInstallSpec :: InstallationSpecResolved+ , _imToolDescription :: Maybe ToolDescription+ , _imRevision :: Int+ }+ deriving (Eq, GHC.Generic, Ord, Show)++instance NFData InstallMetadata++defaultGHCExeSymLinked :: PlatformRequest -> TargetVersion -> [String] -> [SymlinkSpec String]+defaultGHCExeSymLinked pfreq tver binaries =+ (\b -> SymlinkSpec (b <.> exeExt)+ (takeFileName b <> "-${PKGVER}" <.> exeExt)+ True+ (Just $ takeFileName b <.> exeExt)+ ) . (\b -> "bin" Posix.</> maybe "" (\t -> T.unpack t <> "-") (_tvTarget tver) <> b) <$> binaries+ where+ exeExt+ | _rPlatform pfreq == Windows = ".exe"+ | otherwise = ""+++defaultGHCInstallSpec :: PlatformRequest -> TargetVersion -> InstallationSpecResolved+defaultGHCInstallSpec pfreq@(PlatformRequest { _rPlatform = Windows }) tver =+ InstallationSpec {+ _isExeRules = [InstallFilePatternRule ["bin/**"]]+ , _isDataRules = [InstallFilePatternRule ["doc/**", "lib/**", "man/**", "mingw/**"]]+ , _isConfigure = Nothing+ , _isMake = Nothing+ , _isExeSymLinked = defaultGHCExeSymLinked pfreq tver (ghcBinaries pfreq tver)+ , _isPreserveMtimes = True+ }+defaultGHCInstallSpec pfreq tver =+ InstallationSpec {+ _isExeRules = []+ , _isDataRules = []+ , _isConfigure = Just ConfigSpec {+ _csConfigArgs = ["--prefix=${PREFIX}"]+ , _csConfigEnv = Nothing+ , _csConfigFile = Just "configure"+ }+ , _isMake = Just MakeSpec {+ _msMakeArgs = ["DESTDIR=${TMPDIR}", "install"]+ , _msMakeEnv = Nothing+ }+ , _isPreserveMtimes = True+ , _isExeSymLinked = defaultGHCExeSymLinked pfreq tver (ghcBinaries pfreq tver)+ }++ghcBinaries :: PlatformRequest -> TargetVersion -> [FilePath]+ghcBinaries _pfreq _tver = ["ghc", "haddock", "hpc", "hsc2hs", "ghci", "ghc-pkg", "hp2ps", "runhaskell", "runghc"]++defaultCabalInstallSpec :: PlatformRequest -> TargetVersion -> InstallationSpecResolved+defaultCabalInstallSpec pfreq _tver = InstallationSpec+ { _isExeRules = [InstallFileRule ("cabal" <.> exeExt) Nothing]+ , _isDataRules = []+ , _isExeSymLinked =+ [SymlinkSpec {+ _slTarget = "cabal" <.> exeExt+ , _slLinkName = "cabal-${PKGVER}" <.> exeExt+ , _slPVPMajorLinks = False+ , _slSetName = Just $ "cabal" <.> exeExt+ }]+ , _isConfigure = Nothing+ , _isMake = Nothing+ , _isPreserveMtimes = False+ }+ where+ exeExt+ | _rPlatform pfreq == Windows = ".exe"+ | otherwise = ""++defaultHLSInstallSpec :: PlatformRequest -> TargetVersion -> InstallationSpecResolved+defaultHLSInstallSpec _pfreq _tver =+ InstallationSpec {+ _isExeRules = []+ , _isDataRules = []+ , _isConfigure = Nothing+ , _isMake = Just MakeSpec {+ _msMakeArgs = ["DESTDIR=${TMPDIR}", "PREFIX=${PREFIX}", "install"]+ , _msMakeEnv = Nothing+ }+ , _isPreserveMtimes = False+ , _isExeSymLinked = [] -- we can't figure it out+ }++defaultStackInstallSpec :: PlatformRequest -> TargetVersion -> InstallationSpecResolved+defaultStackInstallSpec pfreq _tver = InstallationSpec+ { _isExeRules = [InstallFileRule ("stack" <.> exeExt) Nothing]+ , _isDataRules = []+ , _isExeSymLinked =+ [SymlinkSpec {+ _slTarget = "stack" <.> exeExt+ , _slLinkName = "stack-${PKGVER}" <.> exeExt+ , _slPVPMajorLinks = False+ , _slSetName = Just $ "stack" <.> exeExt+ }]+ , _isConfigure = Nothing+ , _isMake = Nothing+ , _isPreserveMtimes = False+ }+ where+ exeExt+ | _rPlatform pfreq == Windows = ".exe"+ | otherwise = ""++emptyInstallSpec :: InstallationSpecResolved+emptyInstallSpec = InstallationSpec {+ _isExeRules = []+ , _isDataRules = []+ , _isExeSymLinked = []+ , _isConfigure = Nothing+ , _isMake = Nothing+ , _isPreserveMtimes = False+ }++defaultToolInstallSpec :: Tool -> PlatformRequest -> TargetVersion -> Maybe InstallationSpecResolved+defaultToolInstallSpec (Tool "ghc") pfreq tver = Just $ defaultGHCInstallSpec pfreq tver+defaultToolInstallSpec (Tool "stack") pfreq tver = Just $ defaultStackInstallSpec pfreq tver+defaultToolInstallSpec (Tool "cabal") pfreq tver = Just $ defaultCabalInstallSpec pfreq tver+defaultToolInstallSpec (Tool "hls") pfreq tver = Just $ defaultHLSInstallSpec pfreq tver+defaultToolInstallSpec _ _ _ = Nothing++-- | How to union two environments. Either+-- we prefer the system environment in case+-- of duplicate keys or the spec.+data EnvUnion = PreferSystem+ | PreferSpec+ | OnlySpec+ deriving (Eq, Ord, GHC.Generic, Show+#if defined(DHALL)+ , FromDhall+ , ToDhall+#endif+ )++instance NFData EnvUnion++data EnvSpec = EnvSpec+ { _esEnv :: [(String, String)]+ , _esUnion :: EnvUnion+ }+ deriving (Eq, GHC.Generic, Ord, Show)++instance NFData EnvSpec++data ConfigSpec = ConfigSpec+ { _csConfigFile :: Maybe FilePath+ , _csConfigEnv :: Maybe EnvSpec+ , _csConfigArgs :: [String]+ }+ deriving (Eq, GHC.Generic, Ord, Show)++instance NFData ConfigSpec++data MakeSpec = MakeSpec+ { _msMakeEnv :: Maybe EnvSpec+ , _msMakeArgs :: [String]+ }+ deriving (Eq, GHC.Generic, Ord, Show)++instance NFData MakeSpec++-- | A specification of how to install the tool.+data InstallationSpecGen a = InstallationSpec+ { _isExeRules :: [InstallFileRule]+ -- ^ binaries to install+ , _isDataRules :: [InstallFileRule]+ -- ^ data files to install+ , _isExeSymLinked :: [a]+ -- ^ which binaries to symlink into ~/.ghcup/bin+ , _isConfigure :: Maybe ConfigSpec+ , _isMake :: Maybe MakeSpec+ , _isPreserveMtimes :: Bool+ }+ deriving (Eq, GHC.Generic, Ord, Show)++type InstallationSpecInput = InstallationSpecGen SymlinkInputSpec+type InstallationSpecResolved = InstallationSpecGen SymlinkFileSpec++resolveInstallationSpec :: [SymlinkFileSpec] -> InstallationSpecInput -> InstallationSpecResolved+resolveInstallationSpec symls InstallationSpec{..} =+ InstallationSpec{ _isExeSymLinked = symls, .. }++toInstallationInputSpec :: InstallationSpecResolved -> InstallationSpecInput+toInstallationInputSpec InstallationSpec{..} =+ InstallationSpec { _isExeSymLinked = toInputSpec <$> _isExeSymLinked, .. }++instance NFData a => NFData (InstallationSpecGen a)++data InstallFileRule+ = InstallFileRule+ { _ibrInstallSource :: FilePath+ -- ^ binary to install+ , _ibrInstallDest :: Maybe FilePath+ -- ^ install destination name (must not contain path separators)+ }+ | InstallFilePatternRule+ { _ibrInstallPattern :: [FilePattern]+ }+ deriving (Eq, GHC.Generic, Ord, Show)++instance NFData InstallFileRule++data SymlinkSpec a = SymlinkSpec+ { _slTarget :: a+ -- ^ where the symlink will point to+ , _slLinkName :: a+ -- ^ this is the symlink file we will create in @~/.ghcup/bin@+ , _slPVPMajorLinks :: Bool+ -- ^ also create @<tool>-x.y@ on installation+ , _slSetName :: Maybe a+ -- ^ what is the binary name if we "set" the tool+ -- if 'Nothing', then this binary will not be available unqualified+ }+ deriving (Eq, GHC.Generic, Ord, Show)++type SymlinkFileSpec = SymlinkSpec FilePath++instance NFData SymlinkFileSpec++toInputSpec :: SymlinkFileSpec -> SymlinkInputSpec+toInputSpec SymlinkSpec{..} = SymlinkInputSpec{..}++-- | Will be resolced to 'SymlinkSpec'.+data SymlinkInputSpec = SymlinkInputSpec+ { _slTarget :: FilePath+ -- ^ where the symlink will point to (this file exists already)+ , _slLinkName :: FilePath+ -- ^ this is the symlink file we will create in @~/.ghcup/bin@+ , _slPVPMajorLinks :: Bool+ -- ^ also create @<tool>-x.y@ on installation+ , _slSetName :: Maybe FilePath+ -- ^ what is the binary name if we "set" the tool+ -- if 'Nothing', then this binary will not be available unqualified+ } | SymlinkPatternSpec+ { _slTargetPattern :: [FilePattern]+ -- ^ where the symlink will point to (these files exist already)+ , _slTargetPatternIgnore :: [FilePattern]+ , _slLinkName :: FilePath+ -- ^ this is the symlink file we will create in @~/.ghcup/bin@+ , _slPVPMajorLinks :: Bool+ -- ^ also create @<tool>-x.y@ on installation+ , _slSetName :: Maybe FilePath+ -- ^ what is the binary name if we "set" the tool+ -- if 'Nothing', then this binary will not be available unqualified+ }+ deriving (Eq, GHC.Generic, Ord, Show)++instance NFData SymlinkInputSpec++++ --------------+ --[ Others ]--+ --------------++data DownloadMirror = DownloadMirror+ { authority :: Authority+ , pathPrefix :: Maybe Text+ }+ deriving (Eq, GHC.Generic, Ord, Show)++instance NFData DownloadMirror++newtype DownloadMirrors+ = DM (Map Text DownloadMirror)+ deriving (Eq, GHC.Generic, Ord, Show)++instance NFData DownloadMirrors++instance NFData UserInfo+instance NFData Host+instance NFData Port+instance NFData Authority+++-- | How to descend into a tar archive.+data TarDir = RealDir { realDir :: FilePath }+ | RegexDir { regexDir :: String } -- ^ will be compiled to regex, the first match will "win"+ deriving (Eq, Ord, GHC.Generic, Show+#if defined(DHALL)+ , FromDhall+ , ToDhall+#endif+ )++instance NFData TarDir++++-- | Where to fetch GHCupDownloads from.+data URLSource+ = GHCupURL+ | StackSetupURL+ | OwnSource [Either (Either GHCupInfo SetupInfo) URI] -- ^ complete source list+ | OwnSpec (Either GHCupInfo SetupInfo)+ | AddSource [Either (Either GHCupInfo SetupInfo) URI] -- ^ merge with GHCupURL+ | SimpleList [NewURLSource]+ deriving (Eq, GHC.Generic, Show)++data NewURLSource+ = NewGHCupURL+ | NewStackSetupURL+ | NewGHCupInfo GHCupInfo+ | NewSetupInfo SetupInfo+ | NewURI URI+ | NewChannelAlias ChannelAlias+ deriving (Eq, GHC.Generic, Show)++instance NFData NewURLSource++-- | Alias for ease of URLSource selection+data ChannelAlias+ = DefaultChannel+ | StackChannel+ | CrossChannel+ | PrereleasesChannel+ | VanillaChannel+ | ThirdPartyChannel+ deriving (Bounded, Enum, Eq, GHC.Generic, Show)++channelAliasText :: ChannelAlias -> Text+channelAliasText DefaultChannel = "default"+channelAliasText StackChannel = "stack"+channelAliasText CrossChannel = "cross"+channelAliasText PrereleasesChannel = "prereleases"+channelAliasText VanillaChannel = "vanilla"+channelAliasText ThirdPartyChannel = "3rdparty"++fromURLSource :: URLSource -> [NewURLSource]+fromURLSource GHCupURL = [NewGHCupURL]+fromURLSource StackSetupURL = [NewStackSetupURL]+fromURLSource (OwnSource arr) = convert' <$> arr+fromURLSource (AddSource arr) = NewGHCupURL:(convert' <$> arr)+fromURLSource (SimpleList arr) = arr+fromURLSource (OwnSpec (Left gi)) = [NewGHCupInfo gi]+fromURLSource (OwnSpec (Right si)) = [NewSetupInfo si]++convert' :: Either (Either GHCupInfo SetupInfo) URI -> NewURLSource+convert' (Left (Left gi)) = NewGHCupInfo gi+convert' (Left (Right si)) = NewSetupInfo si+convert' (Right uri) = NewURI uri++instance NFData URLSource+instance NFData ChannelAlias+instance NFData (URIRef Absolute) where+ rnf (URI !_ !_ !_ !_ !_) = ()+++data MetaMode+ = Strict+ | Lax+ deriving (Eq, GHC.Generic, Read, Show)++instance NFData MetaMode++newtype Verbosity = Verbosity Int+ deriving (Eq, GHC.Generic, Show)++instance NFData Verbosity++-- If you add, remove, or rename any fields,+-- make sure to update the GHCup.OptParse.Reset module as well.+data UserSettings = UserSettings+ { uCache :: Maybe Bool+ , uMetaCache :: Maybe Integer+ , uMetaMode :: Maybe MetaMode+ , uNoVerify :: Maybe Bool+ , uVerbose :: Maybe Verbosity+ , uKeepDirs :: Maybe KeepDirs+ , uDownloader :: Maybe Downloader+ , uKeyBindings :: Maybe UserKeyBindings+ , uUrlSource :: Maybe URLSource+ , uNoNetwork :: Maybe Bool+ , uGPGSetting :: Maybe GPGSetting+ , uPlatformOverride :: Maybe PlatformRequest+ , uMirrors :: Maybe DownloadMirrors+ , uDefGHCConfOptions :: Maybe [String]+ , uPager :: Maybe PagerConfig+ , uGuessVersion :: Maybe Bool+ , uBuildWrapper :: Maybe ProcessSpec+ }+ deriving (Eq, GHC.Generic, Show)++defaultUserSettings :: UserSettings+defaultUserSettings = UserSettings Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing++fromSettings :: Settings -> Maybe KeyBindings -> UserSettings+fromSettings Settings{..} Nothing =+ UserSettings {+ uCache = Just cache+ , uMetaCache = Just metaCache+ , uMetaMode = Just metaMode+ , uNoVerify = Just noVerify+ , uVerbose = Just verbose+ , uKeepDirs = Just keepDirs+ , uDownloader = Just downloader+ , uNoNetwork = Just noNetwork+ , uKeyBindings = Nothing+ , uUrlSource = Just (SimpleList urlSource)+ , uGPGSetting = Just gpgSetting+ , uPlatformOverride = platformOverride+ , uMirrors = Just mirrors+ , uDefGHCConfOptions = Just defGHCConfOptions+ , uPager = Just pager+ , uGuessVersion = Just guessVersion+ , uBuildWrapper = buildWrapper+ }+fromSettings Settings{..} (Just KeyBindings{..}) =+ let ukb = UserKeyBindings+ { kUp = Just bUp+ , kDown = Just bDown+ , kLeft = Just bLeft+ , kRight = Just bRight+ , kTab = Just bTab+ , kQuit = Just bQuit+ , kInstall = Just bInstall+ , kUninstall = Just bUninstall+ , kSet = Just bSet+ , kChangelog = Just bChangelog+ , kShowAll = Just bShowAllVersions+ }+ in UserSettings {+ uCache = Just cache+ , uMetaCache = Just metaCache+ , uMetaMode = Just metaMode+ , uNoVerify = Just noVerify+ , uVerbose = Just verbose+ , uKeepDirs = Just keepDirs+ , uDownloader = Just downloader+ , uNoNetwork = Just noNetwork+ , uKeyBindings = Just ukb+ , uUrlSource = Just (SimpleList urlSource)+ , uGPGSetting = Just gpgSetting+ , uPlatformOverride = platformOverride+ , uMirrors = Just mirrors+ , uDefGHCConfOptions = Just defGHCConfOptions+ , uPager = Just pager+ , uGuessVersion = Just guessVersion+ , uBuildWrapper = buildWrapper+ }++data UserKeyBindings = UserKeyBindings+ { kUp :: Maybe KeyCombination+ , kDown :: Maybe KeyCombination+ , kLeft :: Maybe KeyCombination+ , kRight :: Maybe KeyCombination+ , kTab :: Maybe KeyCombination+ , kQuit :: Maybe KeyCombination+ , kInstall :: Maybe KeyCombination+ , kUninstall :: Maybe KeyCombination+ , kSet :: Maybe KeyCombination+ , kChangelog :: Maybe KeyCombination+ , kShowAll :: Maybe KeyCombination+ }+ deriving (Eq, GHC.Generic, Show)++data KeyBindings = KeyBindings+ { bUp :: KeyCombination+ , bDown :: KeyCombination+ , bLeft :: KeyCombination+ , bRight :: KeyCombination+ , bTab :: KeyCombination+ , bQuit :: KeyCombination+ , bInstall :: KeyCombination+ , bUninstall :: KeyCombination+ , bSet :: KeyCombination+ , bChangelog :: KeyCombination+ , bShowAllVersions :: KeyCombination+ }+ deriving (GHC.Generic, Show)++instance NFData KeyBindings+#if !defined(BRICK)+instance NFData Key++instance NFData Modifier++#endif+instance NFData KeyCombination++defaultKeyBindings :: KeyBindings+defaultKeyBindings = KeyBindings+ { bUp = KeyCombination { key = KUp , mods = [] }+ , bDown = KeyCombination { key = KDown , mods = [] }+ , bLeft = KeyCombination { key = KLeft , mods = [] }+ , bRight = KeyCombination { key = KRight , mods = [] }+ , bTab = KeyCombination { key = KChar '\t' , mods = [] }+ , bQuit = KeyCombination { key = KChar 'q' , mods = [] }+ , bInstall = KeyCombination { key = KChar 'i' , mods = [] }+ , bUninstall = KeyCombination { key = KChar 'u' , mods = [] }+ , bSet = KeyCombination { key = KChar 's' , mods = [] }+ , bChangelog = KeyCombination { key = KChar 'c' , mods = [] }+ , bShowAllVersions = KeyCombination { key = KChar 'a' , mods = [] }+ }++data AppState = AppState+ { settings :: Settings+ , dirs :: Dirs+ , keyBindings :: KeyBindings+ , ghcupInfo :: GHCupInfo+ , pfreq :: PlatformRequest+ , loggerConfig :: LoggerConfig+ }+ deriving (GHC.Generic, Show)++instance NFData AppState++fromAppState :: AppState -> LeanAppState+fromAppState AppState {..} = LeanAppState {..}++data LeanAppState = LeanAppState+ { settings :: Settings+ , dirs :: Dirs+ , keyBindings :: KeyBindings+ , pfreq :: PlatformRequest+ , loggerConfig :: LoggerConfig+ }+ deriving (GHC.Generic, Show)++instance NFData LeanAppState+++data Settings = Settings+ { cache :: Bool+ , metaCache :: Integer+ , metaMode :: MetaMode+ , noVerify :: Bool+ , keepDirs :: KeepDirs+ , downloader :: Downloader+ , verbose :: Verbosity+ , urlSource :: [NewURLSource]+ , noNetwork :: Bool+ , gpgSetting :: GPGSetting+ , noColor :: Bool+ -- this also exists in LoggerConfig+ , platformOverride :: Maybe PlatformRequest+ , mirrors :: DownloadMirrors+ , defGHCConfOptions :: [String]+ , pager :: PagerConfig+ , guessVersion :: Bool+ , buildWrapper :: Maybe ProcessSpec+ }+ deriving (GHC.Generic, Show)++data ProcessSpec = ProcessSpec+ { cmd :: String+ , cmdArgs :: [String]+ }+ deriving (Eq, GHC.Generic, Show)++instance NFData ProcessSpec++data PagerConfig = PagerConfig+ { pagerList :: Bool+ , pagerCmd :: Maybe String+ }+ deriving (Eq, GHC.Generic, Show)++instance NFData PagerConfig++defaultPagerConfig :: PagerConfig+defaultPagerConfig = PagerConfig False Nothing++allPagerConfig :: String -> PagerConfig+allPagerConfig cmd = PagerConfig True (Just cmd)++defaultMetaCache :: Integer+defaultMetaCache = 300 -- 5 minutes++defaultSettings :: Settings+defaultSettings = Settings False defaultMetaCache Lax False Never Curl (Verbosity 0) [NewGHCupURL] False GPGNone False Nothing (DM mempty) [] defaultPagerConfig True Nothing++instance NFData Settings++data Dirs = Dirs+ { baseDir :: GHCupPath+ , binDir :: FilePath+ , cacheDir :: GHCupPath+ , logsDir :: GHCupPath+ , confDir :: GHCupPath+ , dbDir :: GHCupPath+ , recycleDir :: GHCupPath+ -- mainly used on windows+ , tmpDir :: GHCupPath+ , msys2Dir :: FilePath+ }+ deriving (GHC.Generic, Show)++instance NFData Dirs++data MSYS2Env+ = MSYS+ | UCRT64+ | CLANG64+ | CLANGARM64+ | CLANG32+ | MINGW64+ | MINGW32+ deriving (Eq, GHC.Generic, Ord, Read, Show)++instance NFData MSYS2Env++data KeepDirs+ = Always+ | Errors+ | Never+ deriving (Eq, GHC.Generic, Ord, Show)++instance NFData KeepDirs++data Downloader = Curl+ | Wget+#if defined(INTERNAL_DOWNLOADER)+ | Internal+#endif+ deriving (Eq, Show, Ord, GHC.Generic)++instance NFData Downloader++data GPGSetting+ = GPGStrict+ | GPGLax+ | GPGNone+ deriving (Eq, GHC.Generic, Ord, Show)++instance NFData GPGSetting++data DebugInfo = DebugInfo+ { diDirs :: Dirs+ , diArch :: Architecture+ , diPlatform :: PlatformResult+ , diChannels :: [(ChannelAlias, URI)]+ , diShimGenURL :: URI+ }+ deriving (Show)+++data SetGHC+ = SetGHCOnly -- ^ unversioned 'ghc'+ | SetGHC_XY -- ^ ghc-x.y+ | SetGHC_XYZ -- ^ ghc-x.y.z+ deriving (Eq, Show)++data SetHLS+ = SetHLSOnly -- ^ unversioned 'hls'+ | SetHLS_XYZ -- ^ haskell-language-server-a.b.c~x.y.z, where a.b.c is GHC version and x.y.z is HLS version+ deriving (Eq, Show)+++data PlatformResult = PlatformResult+ { _platform :: Platform+ , _distroVersion :: Maybe Versioning+ }+ deriving (Eq, GHC.Generic, Show)++instance NFData PlatformResult++platResToString :: PlatformResult -> String+platResToString PlatformResult { _platform = plat, _distroVersion = Just v' }+ = show plat <> ", " <> T.unpack (prettyV v')+platResToString PlatformResult { _platform = plat, _distroVersion = Nothing }+ = show plat++instance Pretty PlatformResult where+ pPrint = text . platResToString++data PlatformRequest = PlatformRequest+ { _rArch :: Architecture+ , _rPlatform :: Platform+ , _rVersion :: Maybe Versioning+ }+ deriving (Eq, GHC.Generic, Show)++instance NFData PlatformRequest++pfReqToString :: PlatformRequest -> String+pfReqToString (PlatformRequest arch plat ver) =+ archToString arch ++ "-" ++ platformToString plat ++ pver+ where+ pver = case ver of+ Just v' -> "-" ++ T.unpack (prettyV v')+ Nothing -> ""++instance Pretty PlatformRequest where+ pPrint = text . pfReqToString++-- | A version with a revision, denoting bindist 'versions' that are purely distribution specific.+--+-- The revision starts at 0.+data VersionRev = VersionRev+ { _vrVersion :: Version+ , _vrRev :: Int+ }+ deriving (Ord, Eq, GHC.Generic, Show)++instance NFData VersionRev++data VersionReq = VersionReq+ { _vqVersion :: Version+ , _vqRev :: Maybe Int+ }+ deriving (Ord, Eq, Show)++instance Pretty VersionReq where+ pPrint (VersionReq tver Nothing) = pPrint tver+ pPrint (VersionReq tver (Just rev)) = pPrint tver <> "-r" <> text (show rev)++mkVersionRev :: Version -> VersionRev+mkVersionRev v = VersionRev v 0++prettyVerRev :: VersionRev -> Text+prettyVerRev VersionRev{..} = prettyVer _vrVersion <> "-r" <> T.pack (show _vrRev)++-- | A GHC identified by the target platform triple+-- and the version.+data TargetVersion = TargetVersion+ { _tvTarget :: Maybe Text+ , _tvVersion :: Version+ }+ deriving (Eq, GHC.Generic, Ord, Show)++instance NFData TargetVersion++-- | A GHC identified by the target platform triple+-- and the version.+data TargetVersionRev = TargetVersionRev+ { _tvrTargetVer :: TargetVersion+ , _tvrRev :: Int+ }+ deriving (Ord, Eq, Show)++instance Pretty TargetVersionRev where+ pPrint (TargetVersionRev tver rev) = text $ T.unpack (tVerToText tver) <> "-r" <> show rev++toTargetVersionReq'' :: Version -> TargetVersionReq+toTargetVersionReq'' v = TargetVersionReq (TargetVersion Nothing v) Nothing++toTargetVersionReq' :: VersionReq -> TargetVersionReq+toTargetVersionReq' (VersionReq v' rev) = TargetVersionReq (TargetVersion Nothing v') rev++toVersionReq' :: TargetVersionReq -> VersionReq+toVersionReq' (TargetVersionReq (TargetVersion _ v') rev) = VersionReq v' rev++unsafeToTargetVersionRev :: TargetVersion -> TargetVersionRev+unsafeToTargetVersionRev tv = TargetVersionRev tv 0++unsafeToTargetVersionReq :: TargetVersion -> TargetVersionReq+unsafeToTargetVersionReq tv = TargetVersionReq tv (Just 0)++data TargetVersionReq = TargetVersionReq+ { _tvqTargetVer :: TargetVersion+ , _tvqRev :: Maybe Int+ }+ deriving (Ord, Eq, Show)++instance Pretty TargetVersionReq where+ pPrint (TargetVersionReq tver Nothing) = pPrint tver+ pPrint (TargetVersionReq tver (Just rev)) = text $ T.unpack (tVerToText tver) <> "-r" <> show rev++reqTV :: TargetVersionRev -> TargetVersionReq+reqTV TargetVersionRev{..} = TargetVersionReq _tvrTargetVer (Just _tvrRev)++data GitBranch = GitBranch+ { ref :: String+ , repo :: Maybe String+ }++ deriving (Eq, Ord, Show)++mkTVer :: Version -> TargetVersion+mkTVer = TargetVersion Nothing++unsafeMkTVerRev :: Version -> TargetVersionRev+unsafeMkTVerRev v' = TargetVersionRev (mkTVer v') 0++tVerToText :: TargetVersion -> Text+tVerToText (TargetVersion (Just t) v') = t <> "-" <> prettyVer v'+tVerToText (TargetVersion Nothing v') = prettyVer v'++tVerRevToText :: TargetVersionRev -> Text+tVerRevToText (TargetVersionRev tv rev) = tVerToText tv <> "-r" <> T.pack (show rev)++-- | Assembles a path of the form: <target-triple>-<version>+instance Pretty TargetVersion where+ pPrint = text . T.unpack . tVerToText+++-- | A comparator and a version.+data VersionCmp+ = VR_gt Versioning+ | VR_gteq Versioning+ | VR_lt Versioning+ | VR_lteq Versioning+ | VR_eq Versioning+ deriving (Eq, GHC.Generic, Ord, Show)++instance NFData VersionCmp++-- | A version range. Supports && and ||, but not arbitrary+-- combinations. This is a little simplified.+data VersionRange+ = SimpleRange (NonEmpty VersionCmp) -- And+ | OrRange (NonEmpty VersionCmp) VersionRange+ deriving (Eq, GHC.Generic, Ord, Show)++instance NFData VersionRange++instance Pretty VersionCmp where+ pPrint (VR_gt v) = text "> " <> pPrint v+ pPrint (VR_gteq v) = text ">= " <> pPrint v+ pPrint (VR_lt v) = text "< " <> pPrint v+ pPrint (VR_lteq v) = text "<= " <> pPrint v+ pPrint (VR_eq v) = text "= " <> pPrint v++instance Pretty VersionRange where+ pPrint (SimpleRange xs) = foldl1 (\x y -> x <> text " && " <> y) $ NE.map pPrint xs+ pPrint (OrRange xs vr) = foldMap pPrint xs <> " || " <> pPrint vr++instance Pretty Versioning where+ pPrint = text . T.unpack . prettyV++instance Pretty Version where+ pPrint = text . T.unpack . prettyVer++instance Show (a -> b) where+ show _ = "<function>"++instance Show (IO ()) where+ show _ = "<io>"+++data LogLevel+ = Warn+ | Info+ | Debug Int+ | Error+ deriving (Eq, Ord, Show)++data LoggerConfig = LoggerConfig+ { lcPrintDebugLvl :: Maybe Int+ -- ^ whether to print debug in colorOutter+ , consoleOutter :: T.Text -> IO ()+ -- ^ how to write the console output+ , fileOutter :: T.Text -> IO ()+ -- ^ how to write the file output+ , fancyColors :: Bool+ }+ deriving (Show)++instance NFData LoggerConfig where+ rnf (LoggerConfig !lcPrintDebug !_ !_ !fancyColors) = rnf (lcPrintDebug, fancyColors)++data ProcessError+ = NonZeroExit Int FilePath [String]+ | PTerminated FilePath [String]+ | PStopped FilePath [String]+ | NoSuchPid FilePath [String]+ deriving (Show)+++data CapturedProcess = CapturedProcess+ { _exitCode :: ExitCode+ , _stdOut :: BL.ByteString+ , _stdErr :: BL.ByteString+ }+ deriving (Eq, Show)++++data InstallDir+ = IsolateDir FilePath+ | GHCupInternal+ deriving (Eq, Show)++data InstallDirResolved+ = IsolateDirResolved FilePath+ | GHCupDir GHCupPath+ | GHCupBinDir FilePath+ deriving (Eq, Show)++fromInstallDir :: InstallDirResolved -> FilePath+fromInstallDir (IsolateDirResolved fp) = fp+fromInstallDir (GHCupDir fp) = fromGHCupPath fp+fromInstallDir (GHCupBinDir fp) = fp+++isSafeDir :: InstallDirResolved -> Bool+isSafeDir (IsolateDirResolved _) = False+isSafeDir (GHCupDir _) = True+isSafeDir (GHCupBinDir _) = False++type PromptQuestion = Text++data PromptResponse+ = PromptYes+ | PromptNo+ deriving (Eq, Show)++data ToolVersion+ = GHCVersion TargetVersionReq+ | ToolVersion VersionReq+ | ToolTag Tag+ | ToolDay Day+ deriving (Eq, Show)++instance Pretty ToolVersion where+ pPrint (GHCVersion v) = pPrint v+ pPrint (ToolVersion v) = pPrint v+ pPrint (ToolTag t) = pPrint t+ pPrint (ToolDay d) = text (show d)++++data BuildSystem+ = Hadrian+ | Make+ deriving (Eq, Show)+++data VersionPattern+ = CabalVer+ | GitHashShort+ | GitHashLong+ | GitDescribe+ | GitBranchName+ | S String+ deriving (Eq, Show)++-- | Map with custom FromJSON instance which ignores unknown keys+newtype MapIgnoreUnknownKeys k v+ = MapIgnoreUnknownKeys { unMapIgnoreUnknownKeys :: Map k v }+ deriving (Eq, GHC.Generic, Ord, Show)++instance (NFData k, NFData v) => NFData (MapIgnoreUnknownKeys k v)++-- | Type representing our guessing modes when e.g. "incomplete" PVP version+-- is specified, such as @ghcup set ghc 9.12@.+data GuessMode+ = GStrict -- ^ don't guess the proper tool version+ | GLax -- ^ guess by using the metadata+ | GLaxWithInstalled -- ^ guess by using metadata and installed versions+ deriving (Eq, Show)++data ShowRevisions = ShowUpdates+ | ShowAll+ | ShowNone deriving (Eq, Show)
+ lib/GHCup/Types/Dhall.hs view
@@ -0,0 +1,429 @@+{-# OPTIONS_GHC -Wno-orphans #-}++{-# LANGUAGE CPP #-}+#if defined(DHALL)+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PatternSynonyms #-}+#endif++{-|+Module : GHCup.Types.Dhall+Description : GHCup Dhall instances+Copyright : (c) Julian Ospald, 2020+License : LGPL-3.0+Maintainer : hasufell@hasufell.de+Stability : experimental+Portability : portable+-}+module GHCup.Types.Dhall () where++#if defined(DHALL)++import Dhall+ ( DhallErrors (..)+ , ExtractError (..)+ , Extractor+ , FromDhall (autoWith)+ , ToDhall (injectWith)+ , InterpretOptions (fieldModifier, singletonConstructors)+ , defaultInterpretOptions+ , genericToDhallWithInputNormalizer+ , extract+ , fromMonadic+ , genericAutoWith+ , toMonadic, SingletonConstructors (Bare)+ )+import qualified Dhall+import GHCup.Types+import GHCup.Prelude+import GHCup.Types.JSON+ (verRangeToText)+import GHCup.Types.Stack+ ()++import Data.Versions ( PVP, Version, pvp', version', prettyPVP, prettyVer, pvp )+import Data.Void ( Void )+import GHCup.Prelude.MegaParsec ( ghcTargetVerP, versionRangeP )+import qualified Text.Megaparsec as MP++import Control.Applicative ( (<|>) )+import Data.Aeson ( decodeStrict )+import Data.Map.Strict ( Map )+import Data.Bifunctor ( first )+import Data.Functor ( ($>) )+import qualified Data.List.NonEmpty as NE+import Data.Maybe ( fromJust )+import qualified Data.Text as T+import Data.Text.Encoding as E+import Dhall.Core ( Expr )+import qualified Dhall.Core+import Dhall.Parser ( Src )+import GHCup.Input.Parsers.URI ( parseURI' )+import URI.ByteString ( URI, serializeURIRef' )+import Text.PrettyPrint.HughesPJClass (prettyShow)+import Text.Read (readMaybe)+import qualified Data.Map.Strict as M+import qualified Dhall.Map+++pattern DhallString :: T.Text -> Expr s a+pattern DhallString t = Dhall.Core.TextLit (Dhall.Core.Chunks [] t)+++instance FromDhall VersionRange where+ autoWith _ =+ Dhall.string+ { extract = extractParser versionRangeP+ }++instance ToDhall VersionRange where+ injectWith _ = Dhall.Encoder+ { embed = \(verRangeToText -> t) -> DhallString t+ , declared = Dhall.Core.Text+ }++instance FromDhall Architecture where+ autoWith _ = genericAutoWith defaultInterpretOptions++instance ToDhall Architecture where+ injectWith = genericToDhallWithInputNormalizer defaultInterpretOptions++instance {-# OVERLAPPING #-} FromDhall Platform where+ autoWith _ =+ Dhall.string+ { extract = extractParser' parsePlatform+ }+ where+ parsePlatform t+ | "Darwin" == t = Right Darwin+ | "FreeBSD" == t = Right FreeBSD+ | "OpenBSD" == t = Right OpenBSD+ | "Windows" == t = Right Windows+ | "Linux_" `T.isPrefixOf` t = case+ T.stripPrefix (T.pack "Linux_") t+ of+ Just dstr ->+ case+ (decodeStrict (E.encodeUtf8 (T.pack "\"" <> dstr <> T.pack "\"")) :: Maybe+ LinuxDistro+ )+ of+ Just d -> Right $ Linux d+ Nothing ->+ Left+ $ "Unexpected failure in decoding LinuxDistro: "+ <> show dstr+ Nothing -> Left "Unexpected failure in Platform stripPrefix"+ | otherwise = Left "Failure in Platform (FromJSONKey)"++instance ToDhall Platform where+ injectWith _ = Dhall.Encoder+ { embed = \case+ Darwin -> DhallString "Darwin"+ FreeBSD -> DhallString "FreeBSD"+ Linux d -> DhallString ("Linux_" <> T.pack (show d))+ OpenBSD -> DhallString "OpenBSD"+ Windows -> DhallString "Windows"+ , declared = Dhall.Core.Text+ }++instance {-# OVERLAPPING #-} FromDhall (Maybe VersionRange) where+ autoWith _ =+ Dhall.string+ { extract = extractParser (MP.chunk "unknown_versioning" $> Nothing <|> (Just <$> versionRangeP))+ }++instance {-# OVERLAPPING #-} ToDhall (Maybe VersionRange) where+ injectWith _ = Dhall.Encoder+ { embed = \case+ Just t -> Dhall.embed @VersionRange Dhall.inject $ t+ Nothing -> DhallString "unknown_versioning"+ , declared = Dhall.Core.Text+ }++instance FromDhall PVP where+ autoWith _ =+ Dhall.string+ { extract = extractParser pvp'+ }++instance ToDhall PVP where+ injectWith _ = Dhall.Encoder+ { embed = \(prettyPVP -> t) -> DhallString t+ , declared = Dhall.Core.Text+ }++instance {-# OVERLAPPING #-} FromDhall (Maybe Version) where+ autoWith _ =+ Dhall.string+ { extract = extractParser (MP.chunk "unknown_version" $> Nothing <|> (Just <$> version'))+ }++instance ToDhall Version where+ injectWith _ = Dhall.Encoder+ { embed = \(prettyVer -> t) -> DhallString t+ , declared = Dhall.Core.Text+ }++instance {-# OVERLAPPING #-} ToDhall (Maybe Version) where+ injectWith _ = Dhall.Encoder+ { embed = \case+ Just t -> Dhall.embed @Version Dhall.inject $ t+ Nothing -> DhallString "unknown_version"+ , declared = Dhall.Core.Text+ }++instance FromDhall TargetVersion where+ autoWith _ =+ Dhall.string+ { extract = extractParser ghcTargetVerP+ }++instance ToDhall TargetVersion where+ injectWith _ = Dhall.Encoder+ { embed = \(T.pack . prettyShow -> t) -> DhallString t+ , declared = Dhall.Core.Text+ }++extractParser :: forall a. MP.Parsec Void T.Text a -> Expr Src Void -> Extractor Src Void a+extractParser parser = extractParser' (MP.parse parser "FromDhall")++extractParser' :: forall a e. Show e => (T.Text -> Either e a) -> Expr Src Void -> Extractor Src Void a+extractParser' parse expr = fromMonadic $ do+ t <- toMonadic $ extract Dhall.string expr+ first (DhallErrors . NE.singleton . ExtractError . T.pack . show) . parse . T.pack $ t++instance FromDhall Tag where+ autoWith _ = Dhall.string+ { extract = \case+ DhallString "Latest" -> pure Latest+ DhallString "Recommended" -> pure Recommended+ DhallString "Prerelease" -> pure Prerelease+ DhallString "Nightly" -> pure Nightly+ DhallString "LatestPrerelease" -> pure LatestPrerelease+ DhallString "LatestNightly" -> pure LatestNightly+ DhallString "Experimental" -> pure Experimental+ DhallString "old" -> pure Old+ e@(DhallString (T.unpack -> 'b' : 'a' : 's' : 'e' : '-' : ver')) -> case pvp (T.pack ver') of+ Right x -> pure $ Base x+ Left _ -> Dhall.typeError (Dhall.expected Dhall.string) e+ DhallString x -> pure (UnknownTag $ T.unpack x)+ e -> Dhall.typeError (Dhall.expected Dhall.string) e+ }++instance ToDhall Tag where+ injectWith _ = Dhall.Encoder+ { embed = \case+ Latest -> DhallString "Latest"+ Recommended -> DhallString "Recommended"+ Prerelease -> DhallString "Prerelease"+ Nightly -> DhallString "Nightly"+ Old -> DhallString "old"+ (Base pvp'') -> DhallString ("base-" <> prettyPVP pvp'')+ LatestPrerelease -> DhallString "LatestPrerelease"+ LatestNightly -> DhallString "LatestNightly"+ Experimental -> DhallString "Experimental"+ (UnknownTag x ) -> DhallString (T.pack x)+ , declared = Dhall.Core.Text+ }++instance FromDhall URI where+ autoWith _ =+ Dhall.string+ { extract = extractParser' parseURI'+ }++instance ToDhall URI where+ injectWith _ = Dhall.Encoder+ { embed = \(decUTF8Safe . serializeURIRef' -> uri) -> DhallString uri+ , declared = Dhall.Core.Text+ }++fieldModifierLowerCase :: Int -> T.Text -> T.Text+fieldModifierLowerCase i = (\(c, t) -> T.singleton (lower c) <> t) . fromJust . T.uncons . T.drop i++instance FromDhall InstallFileRule where+ autoWith _ = genericAutoWith defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 4 }++instance ToDhall InstallFileRule where+ injectWith = genericToDhallWithInputNormalizer defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 4 }++instance FromDhall (SymlinkSpec String) where+ autoWith _ = genericAutoWith defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 3 }++instance ToDhall (SymlinkSpec String) where+ injectWith = genericToDhallWithInputNormalizer defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 3 }++instance FromDhall SymlinkInputSpec where+ autoWith _ = genericAutoWith defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 3 }++instance ToDhall SymlinkInputSpec where+ injectWith = genericToDhallWithInputNormalizer defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 3 }++instance FromDhall Rev where+ autoWith _ = genericAutoWith defaultInterpretOptions+ { singletonConstructors = Bare }++instance ToDhall Rev where+ injectWith = genericToDhallWithInputNormalizer defaultInterpretOptions+ { singletonConstructors = Bare }++instance FromDhall RevisionSpec where+ autoWith _ = genericAutoWith defaultInterpretOptions+ { singletonConstructors = Bare }++instance ToDhall RevisionSpec where+ injectWith = genericToDhallWithInputNormalizer defaultInterpretOptions+ { singletonConstructors = Bare }++instance FromDhall ToolInfo where+ autoWith _ = genericAutoWith defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 1 }++instance ToDhall ToolInfo where+ injectWith = genericToDhallWithInputNormalizer defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 1 }++instance FromDhall ToolDescription where+ autoWith _ = genericAutoWith defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 1 }++instance ToDhall ToolDescription where+ injectWith = genericToDhallWithInputNormalizer defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 1 }++instance FromDhall EnvSpec where+ autoWith _ = genericAutoWith defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 3 }++instance ToDhall EnvSpec where+ injectWith = genericToDhallWithInputNormalizer defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 3 }++instance FromDhall ConfigSpec where+ autoWith _ = genericAutoWith defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 3 }++instance ToDhall ConfigSpec where+ injectWith = genericToDhallWithInputNormalizer defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 3 }++instance FromDhall MakeSpec where+ autoWith _ = genericAutoWith defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 3 }++instance ToDhall MakeSpec where+ injectWith = genericToDhallWithInputNormalizer defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 3 }++instance FromDhall a => FromDhall (InstallationSpecGen a) where+ autoWith _ = genericAutoWith defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 3 }++instance ToDhall a => ToDhall (InstallationSpecGen a) where+ injectWith = genericToDhallWithInputNormalizer defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 3 }++instance FromDhall DownloadInfo where+ autoWith _ = genericAutoWith defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 1 }++instance ToDhall DownloadInfo where+ injectWith = genericToDhallWithInputNormalizer defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 1 }++instance (Ord k, FromDhall k, FromDhall v) => FromDhall (MapIgnoreUnknownKeys k v) where+ autoWith opts = MapIgnoreUnknownKeys <$> autoWith opts++instance (Ord k, ToDhall k, ToDhall v) => ToDhall (MapIgnoreUnknownKeys k v) where+ injectWith opts = Dhall.Encoder+ { embed = \(MapIgnoreUnknownKeys m) -> Dhall.embed Dhall.inject $ m+ , declared = Dhall.declared enc+ }+ where+ enc = Dhall.injectWith opts :: Dhall.Encoder (Map k v)++instance FromDhall VersionInfo where+ autoWith _ = genericAutoWith defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 1 }++instance ToDhall VersionInfo where+ injectWith = genericToDhallWithInputNormalizer defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 1 }++instance FromDhall Requirements where+ autoWith _ = genericAutoWith defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 1 }++instance ToDhall Requirements where+ injectWith = genericToDhallWithInputNormalizer defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 1 }++instance FromDhall GHCupInfo where+ autoWith _ = genericAutoWith defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 1 }++instance ToDhall GHCupInfo where+ injectWith = genericToDhallWithInputNormalizer defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 1 }++instance FromDhall PlatformVersionSpec where+ autoWith _ = genericAutoWith defaultInterpretOptions+ { singletonConstructors = Bare }++instance ToDhall PlatformVersionSpec where+ injectWith = genericToDhallWithInputNormalizer defaultInterpretOptions+ { singletonConstructors = Bare }++instance FromDhall PlatformSpec where+ autoWith _ = genericAutoWith defaultInterpretOptions+ { singletonConstructors = Bare }++instance ToDhall PlatformSpec where+ injectWith = genericToDhallWithInputNormalizer defaultInterpretOptions+ { singletonConstructors = Bare }++instance FromDhall ArchitectureSpec where+ autoWith _ = genericAutoWith defaultInterpretOptions+ { singletonConstructors = Bare }++instance ToDhall ArchitectureSpec where+ injectWith = genericToDhallWithInputNormalizer defaultInterpretOptions+ { singletonConstructors = Bare }++instance FromDhall ToolVersionSpec where+ autoWith _ = genericAutoWith defaultInterpretOptions+ { singletonConstructors = Bare }++instance ToDhall ToolVersionSpec where+ injectWith = genericToDhallWithInputNormalizer defaultInterpretOptions+ { singletonConstructors = Bare }++instance FromDhall GHCupDownloads where+ autoWith _ = genericAutoWith defaultInterpretOptions+ { singletonConstructors = Bare }++instance ToDhall GHCupDownloads where+ injectWith = genericToDhallWithInputNormalizer defaultInterpretOptions+ { singletonConstructors = Bare }++instance FromDhall VersionMetadata where+ autoWith _ = genericAutoWith defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 3 }++instance ToDhall VersionMetadata where+ injectWith = genericToDhallWithInputNormalizer defaultInterpretOptions+ { fieldModifier = fieldModifierLowerCase 3 }++#endif
lib/GHCup/Types/JSON.hs view
@@ -1,14 +1,15 @@ {-# OPTIONS_GHC -Wno-orphans #-} -{-# LANGUAGE DataKinds #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-} {-| Module : GHCup.Types.JSON@@ -21,55 +22,158 @@ -} module GHCup.Types.JSON where -import GHCup.Types-import GHCup.Types.Stack (SetupInfo)-import GHCup.Types.JSON.MapIgnoreUnknownKeys ()-import GHCup.Types.JSON.Utils-import GHCup.Types.JSON.Versions ()-import GHCup.Prelude.MegaParsec-import GHCup.Utils.URI+import GHCup.Input.Parsers.URI+import GHCup.Prelude.JSON+import GHCup.Prelude.MegaParsec+import GHCup.Types+import GHCup.Types.JSON.MapIgnoreUnknownKeys+ ()+import GHCup.Types.JSON.Utils+import GHCup.Types.JSON.Versions+ ()+import GHCup.Types.Stack ( SetupInfo ) -import Control.Applicative ( (<|>) )-import Data.Aeson hiding (Key)-import Data.Aeson.TH-import Data.Aeson.Types hiding (Key)-import Data.ByteString ( ByteString )-import Data.List.NonEmpty ( NonEmpty(..) )-import Data.Maybe-import Data.Text.Encoding as E-import Data.Foldable-import Data.Versions-import Data.Void-import URI.ByteString hiding (parseURI)-import Text.Casing+import Control.Applicative ( (<|>) )+import Control.Monad+import Data.Aeson hiding ( Key )+import Data.Aeson.TH+import Data.Aeson.Types hiding ( Key )+import Data.ByteString ( ByteString )+import Data.Foldable+import Data.Maybe+import Data.Scientific (toBoundedInteger)+import Data.Text.Encoding as E+import Data.Versions+import System.FilePath ( hasDrive, isAbsolute, splitPath )+import Text.Casing+import URI.ByteString hiding ( parseURI ) -import qualified Data.List.NonEmpty as NE-import qualified Data.Text as T-import qualified Data.Text.Encoding.Error as E-import qualified Text.Megaparsec as MP-import qualified Text.Megaparsec.Char as MPC+import qualified Data.Map.Strict as M+import qualified Data.List.NonEmpty as NE+import qualified Data.Text as T+import qualified Data.Text.Encoding.Error as E+import qualified Text.Megaparsec as MP+import qualified Text.Megaparsec.Char.Lexer as L+import Data.Functor.Contravariant (Contravariant(contramap)) +safePath :: FilePath -> Bool+safePath fp+ | "." `notElem` fp'+ , ".." `notElem` fp'+ , not (hasDrive fp)+ , not (isAbsolute fp)+ = True+ | otherwise = False+ where+ fp' = splitPath fp++checkSafePath :: MonadFail m => FilePath -> m ()+checkSafePath fp = unless (safePath fp) $ fail "'..' or '.' are not allowed"++safeFilename :: FilePath -> Bool+safeFilename fp+ | length fp' == 1+ , "." `notElem` fp'+ , ".." `notElem` fp'+ , not (hasDrive fp)+ , not (isAbsolute fp)+ = True+ | otherwise = False+ where+ fp' = splitPath fp++checkSafeFilename :: MonadFail m => FilePath -> m ()+checkSafeFilename fp = unless (safeFilename fp) $ fail "'..' or '.' are not allowed and filepath must have no path separators"++safeVersion :: TargetVersion -> Bool+safeVersion TargetVersion{..}+ | pver `notElem` ["db", "set", "ghc", "cabal", "stack", "hls", "ghcup"]+ , pver' `notElem` cabalBadNames+ , safeFilename pver'+ , maybe True (`notElem` ["db", "set", "ghc", "cabal", "stack", "hls", "ghcup"]) _tvTarget+ , maybe True (safeFilename . T.unpack) _tvTarget+ , Left _ <- hasRev pver+ = True+ | otherwise = False+ where+ pver = prettyVer _tvVersion+ pver' = T.unpack $ prettyVer _tvVersion+ hasRev = MP.parse hasRevP "hasRev"+ hasRevP = do+ _ <- parseUntilEmpty "-r"+ _ <- MP.chunk "-r" *> (L.decimal @_ @_ @_ @Int)+ pure ()+++-- This is sad, but our version parsers are too lax,+-- so we need to make sure that e.g. 'cabal-audit'+-- is not parser as cabal with version 'audit'.+-- Backwards compatibility is a b*tch.+cabalBadNames :: [String]+cabalBadNames =+ [ "plan"+ , "add"+ , "audit"+ , "bounds"+ , "bundler"+ , "cache"+ , "clean"+ , "core-inspection"+ , "deps"+ , "diff"+ , "docspec"+ , "doctest"+ , "edit"+ , "env"+ , "fmt"+ , "haddock-server"+ , "hasklint"+ , "helper"+ , "hie"+ , "hoogle"+ , "ifacy-query"+ , "progdeps"+ , "sort"+ , "store-check"+ , "store-gc"+ ]+++checkSafeVersion :: MonadFail m => TargetVersion -> m ()+checkSafeVersion v' = unless (safeVersion v') $ fail "Unsafe version, try something more vanilla like '1.2.3'"++safeToolname :: Tool -> Bool+safeToolname (Tool t)+ | t `notElem` ["db", "set", "bin", "cache", "env", "config.yaml", "tmp", "trash", "logs"]+ , safeFilename t+ = True+ | otherwise = False++checkSafeToolname :: MonadFail m => Tool -> m ()+checkSafeToolname t = unless (safeToolname t) $ fail "Unsafe tool name, try something more vanilla"+ instance ToJSON LinuxDistro where toJSON = String . T.pack . show instance FromJSON LinuxDistro where parseJSON = withText "LinuxDistro" $ \t -> case T.unpack (T.toLower t) of- "debian" -> pure Debian- "ubuntu" -> pure Ubuntu- "mint" -> pure Mint- "fedora" -> pure Fedora- "centos" -> pure CentOS- "redhat" -> pure RedHat- "alpine" -> pure Alpine- "amazonlinux" -> pure AmazonLinux- "rocky" -> pure Rocky- "void" -> pure Void- "gentoo" -> pure Gentoo- "exherbo" -> pure Exherbo- "opensuse" -> pure OpenSUSE+ "debian" -> pure Debian+ "ubuntu" -> pure Ubuntu+ "mint" -> pure Mint+ "fedora" -> pure Fedora+ "centos" -> pure CentOS+ "redhat" -> pure RedHat+ "alpine" -> pure Alpine+ "amazonlinux" -> pure AmazonLinux+ "rocky" -> pure Rocky+ "void" -> pure Void+ "gentoo" -> pure Gentoo+ "exherbo" -> pure Exherbo+ "opensuse" -> pure OpenSUSE "unknownlinux" -> pure UnknownLinux- _ -> fail "Unknown Linux distro"+ _ -> fail "Unknown Linux distro" +deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''ProcessSpec deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''MetaMode deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''Architecture deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''VSep@@ -79,10 +183,10 @@ deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''Chunk deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''Release deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''SemVer-deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''Tool deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''KeepDirs deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''Downloader deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''GPGSetting+deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''EnvUnion deriveJSON defaultOptions { fieldLabelModifier = \str' -> maybe str' T.unpack . T.stripPrefix (T.pack "r-") . T.pack . kebab . tail $ str' } ''PlatformRequest instance ToJSON Tag where@@ -112,6 +216,15 @@ Left e -> fail . show $ e x -> pure (UnknownTag x) +instance ToJSON Tool where+ toJSON (Tool t) = String (T.toLower . T.pack $ t)++instance FromJSON Tool where+ parseJSON = withText "Tool" $ \t -> do+ let tool = Tool . T.unpack . T.toLower $ t+ checkSafeToolname tool+ pure tool+ instance ToJSON URI where toJSON = toJSON . E.decodeUtf8With E.lenientDecode . serializeURIRef' @@ -122,21 +235,33 @@ Right x -> pure x Left e -> fail . show $ e -instance ToJSON GHCTargetVersion where+instance ToJSON TargetVersion where toJSON = toJSON . tVerToText -instance FromJSON GHCTargetVersion where- parseJSON = withText "GHCTargetVersion" $ \t -> case MP.parse ghcTargetVerP "" t of- Right x -> pure x- Left e -> fail $ "Failure in GHCTargetVersion (FromJSON)" <> show e+instance FromJSON TargetVersion where+ parseJSON = withText "TargetVersion" $ \t -> case MP.parse ghcTargetVerP "" t of+ Right x -> do+ checkSafeVersion x+ pure x+ Left e -> fail $ "Failure in TargetVersion (FromJSON)" <> show e -instance ToJSONKey GHCTargetVersion where+instance ToJSONKey TargetVersion where toJSONKey = toJSONKeyText $ \x -> tVerToText x -instance FromJSONKey GHCTargetVersion where+deriveJSON defaultOptions { unwrapUnaryRecords = True } ''Rev++instance FromJSONKey Rev where+ fromJSONKey = Rev <$> fromJSONKey @Int++instance ToJSONKey Rev where+ toJSONKey = contramap unRev toJSONKey++instance FromJSONKey TargetVersion where fromJSONKey = FromJSONKeyTextParser $ \t -> case MP.parse ghcTargetVerP "" t of- Right x -> pure x- Left e -> fail $ "Failure in GHCTargetVersion (FromJSONKey)" <> show e+ Right x -> do+ checkSafeVersion x+ pure x+ Left e -> fail $ "Failure in TargetVersion (FromJSONKey)" <> show e instance ToJSONKey Platform where@@ -177,23 +302,26 @@ fromJSONKey = genericFromJSONKey defaultJSONKeyOptions instance ToJSONKey Tool where- toJSONKey = genericToJSONKey defaultJSONKeyOptions+ toJSONKey = toJSONKeyText $ \(Tool t) -> T.toLower . T.pack $ t instance FromJSONKey Tool where- fromJSONKey = genericFromJSONKey defaultJSONKeyOptions+ fromJSONKey = FromJSONKeyTextParser $ \(T.unpack . T.toLower -> t) -> pure (Tool t) instance ToJSON TarDir where- toJSON (RealDir p) = toJSON p+ toJSON (RealDir p) = object ["RealDir" .= p] toJSON (RegexDir r) = object ["RegexDir" .= r] instance FromJSON TarDir where- parseJSON v = realDir v <|> regexDir v+ parseJSON v = realDir v <|> regexDir v <|> realDirStrict v where realDir = withText "TarDir" $ \t -> do fp <- parseJSON (String t) pure (RealDir fp)+ realDirStrict = withObject "TarDir" $ \o -> do+ r <- o .:: ["RealDir", "realDir"]+ pure $ RealDir r regexDir = withObject "TarDir" $ \o -> do- r <- o .: "RegexDir"+ r <- o .:: ["RegexDir", "regexDir"] pure $ RegexDir r @@ -219,18 +347,6 @@ versionCmpToText (VR_lteq ver') = "<= " <> prettyV ver' versionCmpToText (VR_eq ver') = "== " <> prettyV ver' -versionCmpP :: MP.Parsec Void T.Text VersionCmp-versionCmpP = either (fail . T.unpack) pure =<< (translate <$> (MPC.space *> MP.try (MP.takeWhileP Nothing (`elem` ['>', '<', '=']))) <*> (MPC.space *> versioningEnd))- where- translate ">" v = Right $ VR_gt v- translate ">=" v = Right $ VR_gteq v- translate "<" v = Right $ VR_lt v- translate "<=" v = Right $ VR_lteq v- translate "==" v = Right $ VR_eq v- translate "" v = Right $ VR_eq v- translate c _ = Left $ "unexpected comparator: " <> c-- instance ToJSON VersionRange where toJSON = String . verRangeToText @@ -250,34 +366,7 @@ Right r -> pure r Left e -> fail (MP.errorBundlePretty e) -versionRangeP :: MP.Parsec Void T.Text VersionRange-versionRangeP = go <* MP.eof- where- go =- MP.try orParse- <|> MP.try (fmap SimpleRange andParse)- <|> fmap (SimpleRange . pure) versionCmpP - orParse :: MP.Parsec Void T.Text VersionRange- orParse =- (\a o -> OrRange a o)- <$> (MP.try andParse <|> fmap pure versionCmpP)- <*> (MPC.space *> MP.chunk "||" *> MPC.space *> go)-- andParse :: MP.Parsec Void T.Text (NonEmpty VersionCmp)- andParse =- fmap (\h t -> h :| t)- (MPC.space *> MP.chunk "(" *> MPC.space *> versionCmpP)- <*> MP.try (MP.many (MPC.space *> MP.chunk "&&" *> MPC.space *> versionCmpP))- <* MPC.space- <* MP.chunk ")"- <* MPC.space--versioningEnd :: MP.Parsec Void T.Text Versioning-versioningEnd =- MP.try (verP (MP.chunk " " <|> MP.chunk ")" <|> MP.chunk "&&") <* MPC.space)- <|> versioning'- instance ToJSONKey (Maybe VersionRange) where toJSONKey = toJSONKeyText $ \case Just x -> verRangeToText x@@ -291,26 +380,230 @@ Right x -> pure $ Just x Left e -> fail $ "Failure in (Maybe VersionRange) (FromJSONKey)" <> MP.errorBundlePretty e +deriveToJSON defaultOptions { fieldLabelModifier = mapHead lower . drop 3 } ''SymlinkSpec+deriveToJSON defaultOptions { fieldLabelModifier = mapHead lower . drop 3+ , sumEncoding = UntaggedValue+ } ''SymlinkInputSpec+deriveToJSON defaultOptions { fieldLabelModifier = mapHead lower . drop 4+ , sumEncoding = UntaggedValue+ } ''InstallFileRule+deriveJSON defaultOptions { fieldLabelModifier = mapHead lower . drop 3 } ''EnvSpec+deriveToJSON defaultOptions { fieldLabelModifier = mapHead lower . drop 3 } ''ConfigSpec+deriveToJSON defaultOptions { fieldLabelModifier = mapHead lower . drop 3 } ''MakeSpec +instance FromJSON SymlinkFileSpec where+ parseJSON = withObject "SymlinkSpec" $ \o -> do+ _slTarget <- o .: "target"+ _slLinkName <- o .: "linkName"+ _slPVPMajorLinks <- fromMaybe False <$> o .:? "pVPMajorLinks"+ _slSetName <- o .:? "setName"+ checkSafePath _slTarget+ checkSafeFilename _slLinkName+ forM_ _slSetName checkSafeFilename+ pure SymlinkSpec{..}++instance FromJSON SymlinkInputSpec where+ parseJSON = withObject "SymlinkInputSpec" $ \o -> do+ mTarget <- o .:? "target"+ case mTarget of+ Just _slTarget -> do+ _slLinkName <- o .: "linkName"+ _slPVPMajorLinks <- o .:? "pVPMajorLinks" .!= False+ _slSetName <- o .:? "setName"+ checkSafePath _slTarget+ checkSafeFilename _slLinkName+ forM_ _slSetName checkSafeFilename+ pure SymlinkInputSpec{..}+ Nothing -> do+ _slTargetPattern <- o .: "targetPattern"+ _slTargetPatternIgnore <- o .:? "targetPatternIgnore" .!= []+ _slLinkName <- o .: "linkName"+ _slPVPMajorLinks <- o .:? "pVPMajorLinks" .!= False+ _slSetName <- o .:? "setName"+ forM_ _slTargetPattern checkSafePath+ forM_ _slTargetPatternIgnore checkSafePath+ checkSafeFilename _slLinkName+ forM_ _slSetName checkSafeFilename+ pure SymlinkPatternSpec{..}++instance FromJSON InstallFileRule where+ parseJSON = withObject "InstallFileRule" $ \o -> do+ installPattern <- o .:? "installPattern"+ case installPattern of+ Just iPs -> do+ forM_ iPs checkSafePath+ pure $ InstallFilePatternRule iPs+ Nothing -> do+ installSource <- o .: "installSource"+ checkSafePath installSource+ installDest <- o .:? "installDest"+ checkSafePath (fromMaybe "" installDest)+ pure $ InstallFileRule installSource installDest+++instance FromJSON ConfigSpec where+ parseJSON = withObject "ConfigSpec" $ \o -> do+ _csConfigArgs <- o .: "configArgs"+ _csConfigEnv <- o .:? "configEnv"+ _csConfigFile <- o .:? "configFile"++ pure $ ConfigSpec {..}++instance FromJSON MakeSpec where+ parseJSON = withObject "MakeSpec" $ \o -> do+ _msMakeArgs <- o .: "makeArgs"+ _msMakeEnv <- o .:? "makeEnv"++ pure $ MakeSpec {..}++instance FromJSON a => FromJSON (InstallationSpecGen a) where+ parseJSON = withObject "InstallationSpec" $ \o -> do+ _isExeRules <- o .:? "exeRules" .!= []+ _isDataRules <- o .:? "dataRules" .!= []+ _isExeSymLinked <- o .:? "exeSymLinked" .!= []+ _isConfigure <- o .:? "configure"+ _isMake <- o .:? "make"+ _isPreserveMtimes <- o .:? "preserveMtimes" .!= False++ pure $ InstallationSpec {..}++instance ToJSON a => ToJSON (InstallationSpecGen a) where+ toJSON = genericToJSON defaultOptions { fieldLabelModifier = mapHead lower . drop 3 }++ deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''Requirements deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''DownloadInfo-deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''VersionInfo+deriveJSON defaultOptions { unwrapUnaryRecords = True } ''PlatformVersionSpec+deriveJSON defaultOptions { unwrapUnaryRecords = True } ''PlatformSpec+deriveJSON defaultOptions { unwrapUnaryRecords = True } ''ArchitectureSpec+deriveToJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''VersionInfo+deriveJSON defaultOptions { fieldLabelModifier = mapHead lower . drop 1 } ''ToolDescription +instance FromJSON InstallMetadata where+ parseJSON v = newParse v <|> legacyParse v+ where+ legacyParse o = do+ v'@DownloadInfo{..} <- parseJSON o+ case _dlInstallSpec of+ Nothing -> fail "No install metadata in legacy parser"+ Just InstallationSpec{..} -> do+ isExeSymLinked' <- forM _isExeSymLinked toSymlSpec+ pure $ InstallMetadata v' InstallationSpec{ _isExeSymLinked = isExeSymLinked', ..} Nothing 0++ toSymlSpec SymlinkInputSpec{..} = pure SymlinkSpec{..}+ toSymlSpec _ = fail "Can't handle SymlinkPatternSpec in legacy parser"++ newParse = do+ withObject "InstallMetadata" $ \o -> do+ _imDownloadInfo <- o .: "downloadInfo"+ _imResolvedInstallSpec <- o .: "resolvedInstallSpec"+ _imToolDescription <- o .:? "toolDescription"+ _imRevision <- o .:? "revision" .!= 0+ pure InstallMetadata{..}++instance FromJSON VersionInfo where+ parseJSON = withObject "VersionInfo" $ \o -> do+ _viSourceDL <- o .::? ["viSourceDL", "sourceDL"]+ _viTestDL <- o .::? ["viTestDL", "testDL"]+ _viArch <- o .:: ["viArch", "arch"]+ pure VersionInfo{..}++deriveJSON defaultOptions ''DhallRevision+deriveJSON defaultOptions { unwrapUnaryRecords = True } ''RevisionSpecDhall++instance FromJSON RevisionSpec where+ parseJSON v = newParse v <|> legacyParse v <|> dhallParse+ where+ legacyParse o = do+ vi <- parseJSON o+ pure $ RevisionSpec $ M.singleton (Rev 0) vi+ newParse o = do+ m <- parseJSON o+ pure $ RevisionSpec m+ dhallParse =+ (\(unRevisionSpecDhall -> dhallSpec) ->+ RevisionSpec $ M.fromList $ fmap (\DhallRevision{..} -> (Rev mapKey, mapValue)) dhallSpec+ )+ <$> parseJSON @RevisionSpecDhall v+++instance FromJSON VersionMetadata where+ parseJSON v = newParse v <|> legacyParse v+ where+ legacyParse =+ withObject "VersionMetadata" $ \o -> do+ _vmTags <- o .: "viTags"+ _vmReleaseDay <- o .:? "viReleaseDay"+ _vmChangeLog <- o .:? "viChangeLog"+ _viSourceDL <- o .:? "viSourceDL"+ _viTestDL <- o .:? "viTestDL"+ _viArch <- ArchitectureSpec <$> o .: "viArch"+ let vi = VersionInfo {..}+ let _vmRevisionSpec = RevisionSpec $ M.singleton (Rev 0) vi+ _vmPreInstall <- o .:? "viPreInstall"+ _vmPostInstall <- o .:? "viPostInstall"+ _vmPostRemove <- o .:? "viPostRemove"+ _vmPreCompile <- o .:? "viPreCompile"+ pure VersionMetadata{..}+ newParse = do+ withObject "VersionMetadata" $ \o -> do+ _vmTags <- o .:? "tags" .!= []+ _vmReleaseDay <- o .:? "releaseDay"+ _vmChangeLog <- o .::? ["changelog", "changeLog"]+ _vmPreInstall <- o .:? "preInstall"+ _vmPostInstall <- o .:? "postInstall"+ _vmPostRemove <- o .:? "postRemove"+ _vmPreCompile <- o .:? "preCompile"+ _vmRevisionSpec <- o .: "revisionSpec"+ pure VersionMetadata{..}++deriveToJSON defaultOptions { unwrapUnaryRecords = True } ''RevisionSpec++instance ToJSON VersionMetadata where+ toJSON VersionMetadata{..} = object+ [ "tags" .= _vmTags+ , "releaseDay" .= _vmReleaseDay+ , "changeLog" .= _vmChangeLog+ , "preInstall" .= _vmPreInstall+ , "postInstall" .= _vmPostInstall+ , "postRemove" .= _vmPostRemove+ , "preCompile" .= _vmPreCompile+ , "revisionSpec" .= _vmRevisionSpec+ ]++deriveJSON defaultOptions { unwrapUnaryRecords = True } ''ToolVersionSpec+deriveToJSON defaultOptions { fieldLabelModifier = mapHead lower . drop 3 } ''InstallMetadata+deriveToJSON defaultOptions { fieldLabelModifier = mapHead lower . drop 1 } ''ToolInfo++instance FromJSON ToolInfo where+ parseJSON v = newParse v <|> legacyParse v+ where+ legacyParse o = do+ v' <- parseJSON o+ pure $ ToolInfo v' Nothing+ newParse = do+ withObject "ToolInfo" $ \o -> do+ _toolVersions <- o .: "toolVersions"+ _toolDetails <- o .: "toolDetails"+ pure ToolInfo{..}++deriveJSON defaultOptions { unwrapUnaryRecords = True } ''GHCupDownloads+ instance FromJSON GHCupInfo where parseJSON = withObject "GHCupInfo" $ \o -> do- toolRequirements' <- o .:? "toolRequirements"+ toolRequirements' <- o .:? "toolRequirements" .!= mempty metadataUpdate <- o .:? "metadataUpdate" ghcupDownloads' <- o .: "ghcupDownloads"- pure (GHCupInfo (fromMaybe mempty toolRequirements') ghcupDownloads' metadataUpdate)+ pure (GHCupInfo toolRequirements' ghcupDownloads' metadataUpdate) deriveToJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''GHCupInfo instance ToJSON NewURLSource where- toJSON NewGHCupURL = String "GHCupURL"- toJSON NewStackSetupURL = String "StackSetupURL"- toJSON (NewGHCupInfo gi) = object [ "ghcup-info" .= gi ]- toJSON (NewSetupInfo si) = object [ "setup-info" .= si ]- toJSON (NewURI uri) = toJSON uri+ toJSON NewGHCupURL = String "GHCupURL"+ toJSON NewStackSetupURL = String "StackSetupURL"+ toJSON (NewGHCupInfo gi) = object [ "ghcup-info" .= gi ]+ toJSON (NewSetupInfo si) = object [ "setup-info" .= si ]+ toJSON (NewURI uri) = toJSON uri toJSON (NewChannelAlias c) = toJSON c instance ToJSON URLSource where@@ -323,7 +616,7 @@ parseJSON = withText "ChannelAlias" $ \t -> let aliases = map (\c -> (channelAliasText c, c)) [minBound..maxBound] in case lookup t aliases of- Just c -> pure c+ Just c -> pure c Nothing -> fail $ "Unexpected ChannelAlias: " <> T.unpack t deriveJSON defaultOptions { sumEncoding = ObjectWithSingleField } ''Key@@ -335,6 +628,7 @@ deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''DownloadMirror deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''DownloadMirrors + instance FromJSON URLSource where parseJSON v = parseGHCupURL v@@ -358,7 +652,7 @@ <|> parseNewUrlSource' v where convert'' :: Either GHCupInfo URI -> Either (Either GHCupInfo SetupInfo) URI- convert'' (Left gi) = Left (Left gi)+ convert'' (Left gi) = Left (Left gi) convert'' (Right uri) = Right uri parseOwnSourceLegacy = withObject "URLSource" $ \o -> do@@ -473,7 +767,16 @@ cmd <- o .:? "cmd" pure $ PagerConfig list cmd +instance FromJSON Verbosity where+ parseJSON v = new v <|> legacy v+ where+ legacy = withBool "Verbosity" $ \b -> do+ pure $ Verbosity (if b then 1 else 0)+ new = withScientific "Verbosity" $ \s -> do+ int <- maybe (fail "Verbosity integer out of bounds") pure $ toBoundedInteger s+ pure $ Verbosity int +deriveToJSON defaultOptions { unwrapUnaryRecords = True } ''Verbosity deriveToJSON defaultOptions { fieldLabelModifier = \str' -> maybe str' T.unpack . T.stripPrefix (T.pack "pager-") . T.pack . kebab $ str' } ''PagerConfig deriveToJSON defaultOptions { fieldLabelModifier = drop 2 . kebab } ''KeyBindings -- move under key-bindings key deriveJSON defaultOptions { fieldLabelModifier = \str' -> maybe str' T.unpack . T.stripPrefix (T.pack "k-") . T.pack . kebab $ str' } ''UserKeyBindings
lib/GHCup/Types/JSON/MapIgnoreUnknownKeys.hs view
@@ -1,19 +1,19 @@ {-# OPTIONS_GHC -Wno-orphans #-} -{-# LANGUAGE CPP #-}-{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE UndecidableInstances #-} module GHCup.Types.JSON.MapIgnoreUnknownKeys where -import GHCup.Types+import GHCup.Types -import Data.Aeson hiding (Key)-import Data.Aeson.Types hiding (Key)+import Data.Aeson hiding ( Key )+import Data.Aeson.Types hiding ( Key ) -import qualified Data.Aeson.Key as Key-import qualified Data.Aeson.KeyMap as KeyMap-import qualified Data.Map.Strict as Map+import qualified Data.Aeson.Key as Key+import qualified Data.Aeson.KeyMap as KeyMap+import qualified Data.Map.Strict as Map #if defined(STRICT_METADATA_PARSING) -- | Use the instance of Map
lib/GHCup/Types/JSON/Utils.hs view
@@ -10,8 +10,22 @@ module GHCup.Types.JSON.Utils where -import qualified Data.Text as T+import Control.Applicative ( optional )+import Data.Aeson ( FromJSON, Key, Object, (.:) )+import Data.Aeson.Types ( Parser )+import Data.Foldable ( asum )+import Data.Functor ( (<&>) ) +import qualified Data.Text as T+ removeLensFieldLabel :: String -> String removeLensFieldLabel str' = maybe str' T.unpack . T.stripPrefix (T.pack "_") . T.pack $ str'++(.::?) :: FromJSON a => Object -> [Key] -> Parser (Maybe a)+-- asum <$> traverse (o .:?) keys+(.::?) o keys = optional $ asum (keys <&> (o .:))++(.::) :: FromJSON a => Object -> [Key] -> Parser a+(.::) o keys = asum $ keys <&> (o .:)+
lib/GHCup/Types/JSON/Versions.hs view
@@ -1,10 +1,10 @@ {-# OPTIONS_GHC -Wno-orphans #-} -{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeFamilies #-} {-| Module : GHCup.Types.JSON.Versions@@ -17,11 +17,11 @@ -} module GHCup.Types.JSON.Versions where -import Data.Aeson hiding (Key)-import Data.Aeson.Types hiding (Key)-import Data.Versions+import Data.Aeson hiding ( Key )+import Data.Aeson.Types hiding ( Key )+import Data.Versions -import qualified Data.Text as T+import qualified Data.Text as T instance ToJSON Versioning where toJSON = toJSON . prettyV@@ -29,7 +29,7 @@ instance FromJSON Versioning where parseJSON = withText "Versioning" $ \t -> case versioning t of Right x -> pure x- Left e -> fail $ "Failure in GHCTargetVersion (FromJSON)" <> show e+ Left e -> fail $ "Failure in TargetVersion (FromJSON)" <> show e instance ToJSONKey Versioning where toJSONKey = toJSONKeyText $ \x -> prettyV x
lib/GHCup/Types/Optics.hs view
@@ -1,12 +1,12 @@ {-# OPTIONS_GHC -Wno-orphans #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE DataKinds #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-} {-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE AllowAmbiguousTypes #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-} {-| Module : GHCup.Types.Optics@@ -19,31 +19,61 @@ -} module GHCup.Types.Optics where -import GHCup.Types+import GHCup.Types -import Control.Monad.Reader-import Data.ByteString ( ByteString )-import Optics-import URI.ByteString+import Control.Monad.Reader+import Data.ByteString ( ByteString )+import Data.Map.Strict ( Map )+import Optics+import URI.ByteString +import qualified Data.Map.Strict as M + makePrisms ''Tool makePrisms ''Architecture makePrisms ''LinuxDistro makePrisms ''Platform makePrisms ''Tag+makePrisms ''EnvUnion+makePrisms ''GHCupDownloads+makePrisms ''ToolVersionSpec+makePrisms ''RevisionSpec+makePrisms ''ArchitectureSpec+makePrisms ''PlatformSpec+makePrisms ''PlatformVersionSpec+makePrisms ''Rev +makeLenses ''ToolInfo+makeLenses ''ToolDescription+makeLenses ''EnvSpec+makeLenses ''ConfigSpec+makeLenses ''MakeSpec+makeLenses ''InstallationSpecGen+makeLenses ''InstallMetadata makeLenses ''PlatformResult makeLenses ''DownloadInfo makeLenses ''Tag makeLenses ''VersionInfo+makeLenses ''VersionMetadata -makeLenses ''GHCTargetVersion+makeLenses ''TargetVersionReq+makeLenses ''TargetVersionRev+makeLenses ''InstallFileRule makeLenses ''GHCupInfo makeLenses ''CapturedProcess +toolVersionsL :: Lens' ToolInfo (Map TargetVersion VersionMetadata)+toolVersionsL = toolVersions % _ToolVersionSpec++revisionSpecL :: Getter VersionMetadata (Map Int VersionInfo)+revisionSpecL = vmRevisionSpec % _RevisionSpec % to (M.mapKeys unRev)++archL :: Lens' VersionInfo (MapIgnoreUnknownKeys Architecture PlatformSpec)+archL = viArch % _ArchitectureSpec+ uriSchemeL' :: Lens' (URIRef Absolute) Scheme uriSchemeL' = lensVL uriSchemeL @@ -71,8 +101,14 @@ queryL' :: Lens' (URIRef a) Query queryL' = lensVL queryL +mapLast :: AffineFold (Map k a) (k, a)+mapLast = to M.lookupMax % _Just +ixOrLast :: forall k v . Ord k => Maybe k -> AffineFold (Map k v) (k, v)+ixOrLast = maybe mapLast (\i -> castOptic @An_AffineFold (ix @(Map k v) i) % to (i,)) ++ ---------------------- --[ Lens utilities ]-- ----------------------@@ -88,18 +124,20 @@ getLeanAppState :: ( MonadReader env m- , LabelOptic' "settings" A_Lens env Settings- , LabelOptic' "dirs" A_Lens env Dirs- , LabelOptic' "keyBindings" A_Lens env KeyBindings+ , LabelOptic' "settings" A_Lens env Settings+ , LabelOptic' "dirs" A_Lens env Dirs+ , LabelOptic' "keyBindings" A_Lens env KeyBindings , LabelOptic' "loggerConfig" A_Lens env LoggerConfig+ , LabelOptic' "pfreq" A_Lens env PlatformRequest ) => m LeanAppState getLeanAppState = do s <- gets @"settings" d <- gets @"dirs" k <- gets @"keyBindings"+ p <- gets @"pfreq" l <- gets @"loggerConfig"- pure (LeanAppState s d k l)+ pure (LeanAppState s d k p l) getSettings :: ( MonadReader env m@@ -161,4 +199,7 @@ instance LabelOptic "dirs" A_Lens Dirs Dirs Dirs Dirs where+ labelOptic = lens id (\_ d -> d)++instance LabelOptic "loggerConfig" A_Lens LoggerConfig LoggerConfig LoggerConfig LoggerConfig where labelOptic = lens id (\_ d -> d)
lib/GHCup/Types/Stack.hs view
@@ -1,7 +1,11 @@ {-# OPTIONS_GHC -Wno-orphans #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE FlexibleInstances #-}+#if defined(DHALL)+{-# LANGUAGE DeriveAnyClass #-}+#endif {-| Module : GHCup.Types.Stack@@ -14,22 +18,57 @@ -} module GHCup.Types.Stack where -import GHCup.Types.JSON.Versions ()+import GHCup.Types.JSON.Versions+ () -import Control.Applicative-import Control.DeepSeq ( NFData )-import Data.ByteString-import Data.Aeson-import Data.Aeson.Types-import Data.Map.Strict ( Map )-import Data.Text ( Text )-import Data.Text.Encoding-import Data.Versions+import Control.Applicative+import Control.DeepSeq ( NFData )+import Data.Aeson+import Data.Aeson.Types+import Data.ByteString+import Data.Map.Strict ( Map )+import Data.Text.Encoding+import Data.Versions+#if defined(DHALL)+import Dhall+#else+import Data.Text ( Text )+#endif import qualified Data.Map as Map-import qualified GHC.Generics as GHC+#if defined(DHALL)+import Data.Bifunctor ( first )+import Data.Void ( Void )+import Dhall.Core ( Expr )+import Dhall.Src ( Src ) +import qualified Data.List.NonEmpty as NE+import qualified Data.Text as T+import qualified Text.Megaparsec as MP+#endif+import qualified GHC.Generics as GHC +++-- copy-pasted from GHCup.Types.Dhall++#if defined(DHALL)+instance FromDhall Version where+ autoWith _ =+ Dhall.string+ { extract = extractParser version'+ }++extractParser :: forall a. MP.Parsec Void T.Text a -> Expr Src Void -> Extractor Src Void a+extractParser parser = extractParser' (MP.parse parser "FromDhall")++extractParser' :: forall a e. Show e => (T.Text -> Either e a) -> Expr Src Void -> Extractor Src Void a+extractParser' parse' expr = fromMonadic $ do+ t <- toMonadic $ extract Dhall.string expr+ first (DhallErrors . NE.singleton . ExtractError . T.pack . show) . parse' . T.pack $ t+#endif++ -------------------------------------- --[ Stack download info copy pasta ]-- --------------------------------------@@ -37,11 +76,15 @@ data SetupInfo = SetupInfo { siSevenzExe :: Maybe DownloadInfo , siSevenzDll :: Maybe DownloadInfo- , siMsys2 :: Map Text VersionedDownloadInfo- , siGHCs :: Map Text (Map Version GHCDownloadInfo)- , siStack :: Map Text (Map Version DownloadInfo)+ , siMsys2 :: Map Text VersionedDownloadInfo+ , siGHCs :: Map Text (Map Version GHCDownloadInfo)+ , siStack :: Map Text (Map Version DownloadInfo) }- deriving (Show, Eq, GHC.Generic)+ deriving (Eq, GHC.Generic, Show+#if defined(DHALL)+ , FromDhall+#endif+ ) instance NFData SetupInfo @@ -87,13 +130,17 @@ -- | Build of the compiler distribution (e.g. standard, gmp4, tinfo6) -- | Information for a file to download. data DownloadInfo = DownloadInfo- { downloadInfoUrl :: Text+ { downloadInfoUrl :: Text -- ^ URL or absolute file path , downloadInfoContentLength :: Maybe Int- , downloadInfoSha1 :: Maybe ByteString- , downloadInfoSha256 :: Maybe ByteString+ , downloadInfoSha1 :: Maybe ByteString+ , downloadInfoSha256 :: Maybe ByteString }- deriving (Show, Eq, GHC.Generic)+ deriving (Eq, GHC.Generic, Show+#if defined(DHALL)+ , FromDhall+#endif+ ) instance ToJSON DownloadInfo where toJSON (DownloadInfo {..}) = object [ "url" .= downloadInfoUrl@@ -123,10 +170,14 @@ } data VersionedDownloadInfo = VersionedDownloadInfo- { vdiVersion :: Version+ { vdiVersion :: Version , vdiDownloadInfo :: DownloadInfo }- deriving (Show, Eq, GHC.Generic)+ deriving (Eq, GHC.Generic, Show+#if defined(DHALL)+ , FromDhall+#endif+ ) instance ToJSON VersionedDownloadInfo where toJSON (VersionedDownloadInfo {vdiDownloadInfo = DownloadInfo{..}, ..})@@ -150,10 +201,14 @@ data GHCDownloadInfo = GHCDownloadInfo { gdiConfigureOpts :: [Text]- , gdiConfigureEnv :: Map Text Text- , gdiDownloadInfo :: DownloadInfo+ , gdiConfigureEnv :: Map Text Text+ , gdiDownloadInfo :: DownloadInfo }- deriving (Show, Eq, GHC.Generic)+ deriving (Eq, GHC.Generic, Show+#if defined(DHALL)+ , FromDhall+#endif+ ) instance NFData GHCDownloadInfo
+ lib/GHCup/Types/Tar.hs view
@@ -0,0 +1,33 @@+{-# LANGUAGE CPP #-}+#if defined(TAR)+{-# LANGUAGE DeriveGeneric #-}+#endif++module GHCup.Types.Tar+ ( ArchiveResult(..)+ )+ where++#if defined(TAR)++import Control.Exception ( Exception )+import Control.DeepSeq ( NFData )+import qualified GHC.Generics as GHC++data ArchiveResult = ArchiveFatal+ | ArchiveFailed+ | ArchiveWarn+ | ArchiveRetry+ | ArchiveOk+ | ArchiveEOF+ deriving (Eq, Show, GHC.Generic)++instance NFData ArchiveResult++instance Exception ArchiveResult++#else++import Codec.Archive ( ArchiveResult(..) )++#endif
+ lib/GHCup/Unpack.hs view
@@ -0,0 +1,140 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}++{-|+Module : GHCup.Unpack+Description : GHCup tar abstractions+Copyright : (c) Julian Ospald, 2024+License : LGPL-3.0+Maintainer : hasufell@hasufell.de+Stability : experimental+Portability : portable+-}+module GHCup.Unpack where++import GHCup.Types.Tar ( ArchiveResult(..) )+import GHCup.Errors+import GHCup.Prelude+import GHCup.Types.Optics++import Control.Monad.Catch (MonadThrow)+import Control.Monad.Reader+import Data.List+import Data.Variant.Excepts+import System.FilePath++#if defined(TAR)+import Codec.Archive.Zip+import qualified Codec.Archive.Tar as Tar+import qualified Codec.Archive.Tar.Entry as Tar+import qualified Data.Map.Strict as Map+#else+import Codec.Archive hiding ( Directory+ , ArchiveResult -- imported from "GHCup.Unpack"+ )+#endif++import qualified Codec.Compression.BZip as BZip+import qualified Codec.Compression.GZip as GZip+import qualified Codec.Compression.Lzma as Lzma+import qualified Data.ByteString.Lazy as BL+import qualified Data.Text as T+++-- | Unpack an archive to a given directory.+unpackToDir :: (MonadReader env m, HasLog env, MonadIO m, MonadThrow m)+ => FilePath -- ^ destination dir+ -> FilePath -- ^ archive path+ -> Excepts '[UnknownArchive+ , ArchiveResult+ ] m ()+unpackToDir dfp av = do+ let fn = takeFileName av+ lift $ logInfo $ "Unpacking: " <> T.pack fn <> " to " <> T.pack dfp++#if defined(TAR)+ let untar :: MonadIO m => BL.ByteString -> Excepts '[ArchiveResult] m ()+ untar = liftIO . Tar.unpack dfp . Tar.read++ rf :: MonadIO m => FilePath -> Excepts '[ArchiveResult] m BL.ByteString+ rf = liftIO . BL.readFile+#else+ let untar :: MonadIO m => BL.ByteString -> Excepts '[ArchiveResult] m ()+ untar = lEM . liftIO . runArchiveM . unpackToDirLazy dfp++ rf :: MonadIO m => FilePath -> Excepts '[ArchiveResult] m BL.ByteString+ rf = liftIO . BL.readFile+#endif++ -- extract, depending on file extension+ if+ | ".tar.gz" `isSuffixOf` fn -> liftE+ (untar . GZip.decompress =<< rf av)+ | ".tar.xz" `isSuffixOf` fn -> do+ filecontents <- liftE $ rf av+ let decompressed = Lzma.decompressWith (Lzma.defaultDecompressParams { Lzma.decompressAutoDecoder= True }) filecontents+ liftE $ untar decompressed+ | ".tar.bz2" `isSuffixOf` fn ->+ liftE (untar . BZip.decompress =<< rf av)+ | ".tar" `isSuffixOf` fn -> liftE (untar =<< rf av)+#if defined(TAR)+ | ".zip" `isSuffixOf` fn -> withArchive av (unpackInto dfp)+#else+ -- libarchive supports zip+ | ".zip" `isSuffixOf` fn -> liftE (untar =<< rf av)+#endif+ | otherwise -> throwE $ UnknownArchive fn+++-- | Get all files from an archive.+getArchiveFiles :: (MonadIO m, MonadThrow m)+ => FilePath -- ^ archive path+ -> Excepts '[ UnknownArchive+ , ArchiveResult+ ] m [FilePath]+getArchiveFiles av = do+ let fn = takeFileName av+#if defined(TAR)+ let entries :: Monad m => BL.ByteString -> Excepts '[ArchiveResult] m [FilePath]+ entries =+ lE @ArchiveResult+ . Tar.foldEntries+ (\e x -> fmap (Tar.entryTarPath e :) x)+ (Right [])+ (\_ -> Left ArchiveFailed)+ . Tar.decodeLongNames+ . Tar.read++ rf :: MonadIO m => FilePath -> Excepts '[ArchiveResult] m BL.ByteString+ rf = liftIO . BL.readFile+#else+ let entries :: Monad m => BL.ByteString -> Excepts '[ArchiveResult] m [FilePath]+ entries = (fmap . fmap) filepath . lE . readArchiveBSL++ rf :: MonadIO m => FilePath -> Excepts '[ArchiveResult] m BL.ByteString+ rf = liftIO . BL.readFile+#endif++ -- extract, depending on file extension+ if+ | ".tar.gz" `isSuffixOf` fn -> liftE+ (entries . GZip.decompress =<< rf av)+ | ".tar.xz" `isSuffixOf` fn -> do+ filecontents <- liftE $ rf av+ let decompressed = Lzma.decompressWith (Lzma.defaultDecompressParams { Lzma.decompressAutoDecoder= True }) filecontents+ liftE $ entries decompressed+ | ".tar.bz2" `isSuffixOf` fn ->+ liftE (entries . BZip.decompress =<< rf av)+ | ".tar" `isSuffixOf` fn -> liftE (entries =<< rf av)+ | ".zip" `isSuffixOf` fn ->+#if defined(TAR)+ withArchive av $ do+ entries' <- getEntries+ pure $ fmap unEntrySelector $ Map.keys entries'+#else+ liftE (entries =<< rf av)+#endif+ | otherwise -> throwE $ UnknownArchive fn+
− lib/GHCup/Utils.hs
@@ -1,1272 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE ViewPatterns #-}--{-|-Module : GHCup.Utils-Description : GHCup domain specific utilities-Copyright : (c) Julian Ospald, 2020-License : LGPL-3.0-Maintainer : hasufell@hasufell.de-Stability : experimental-Portability : portable--This module contains GHCup helpers specific to-installation and introspection of files/versions etc.--}-module GHCup.Utils- ( module GHCup.Utils.Dirs- , module GHCup.Utils.Tar- , module GHCup.Utils- , module GHCup.Utils.URI-#if defined(IS_WINDOWS)- , module GHCup.Prelude.Windows-#else- , module GHCup.Prelude.Posix-#endif- )-where---#if defined(IS_WINDOWS)-import GHCup.Prelude.Windows-#else-import GHCup.Prelude.Posix-#endif-import GHCup.Download-import GHCup.Errors-import GHCup.Types-import GHCup.Types.Optics-import GHCup.Types.JSON ( )-import GHCup.Utils.Dirs-import GHCup.Utils.Tar-import GHCup.Utils.URI-import GHCup.Version-import GHCup.Prelude-import GHCup.Prelude.File-import GHCup.Prelude.Logger.Internal-import GHCup.Prelude.MegaParsec-import GHCup.Prelude.Process-import GHCup.Prelude.String.QQ-import Control.Applicative-import Control.Exception.Safe-import Control.Monad-#if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )-#endif-import Conduit ((.|), runConduitRes)-import Control.Monad.Reader-import Control.Monad.Trans.Resource- hiding ( throwM )-import Control.Monad.IO.Unlift ( MonadUnliftIO( withRunInIO ) )-import Data.Char ( isHexDigit )-import Data.ByteString ( ByteString )-import Data.Either-import Data.Foldable-import Data.List ( sort, stripPrefix, isPrefixOf, nub, isInfixOf, isSuffixOf, groupBy, sortBy )-import Data.List.NonEmpty ( NonEmpty( (:|) ))-import Data.Maybe-import Data.Text ( Text )-import Data.Versions hiding ( patch )-import GHC.IO.Exception-import Data.Variant.Excepts-import Optics-import Safe-import System.FilePath-import System.IO.Error-import Text.Regex.Posix-import Text.PrettyPrint.HughesPJClass (prettyShow)-import URI.ByteString hiding (parseURI)--import qualified Data.Map.Strict as Map-import qualified Data.Text as T-import qualified Data.Text.Encoding as E-import qualified Text.Megaparsec as MP-import qualified Data.List.NonEmpty as NE-import qualified Data.Conduit.Combinators as C--import Control.DeepSeq (force)-import GHC.IO (evaluate)-import Data.Time (Day(..), diffDays, addDays)----- $setup--- >>> :set -XOverloadedStrings--- >>> :set -XDataKinds--- >>> :set -XTypeApplications--- >>> :set -XQuasiQuotes--- >>> import System.Directory--- >>> import URI.ByteString--- >>> import qualified Data.Text as T--- >>> import GHCup.Prelude--- >>> import GHCup.Download--- >>> import GHCup.Version--- >>> import GHCup.Errors--- >>> import GHCup.Types--- >>> import GHCup.Types.Optics--- >>> import Data.Versions--- >>> import Optics--- >>> import GHCup.Prelude.Version.QQ--- >>> import qualified Data.Text.Encoding as E--- >>> import Control.Monad.Reader--- >>> import Data.Variant.Excepts--- >>> import Text.PrettyPrint.HughesPJClass ( prettyShow )--- >>> let lc = LoggerConfig { lcPrintDebug = False, consoleOutter = mempty, fileOutter = mempty, fancyColors = False }--- >>> dirs' <- getAllDirs--- >>> let installedVersions = [ ([pver|8.10.7|], "-debug+lol", Nothing), ([pver|8.10.4|], "", Nothing), ([pver|8.8.4|], "", Nothing), ([pver|8.8.3|], "", Nothing) ]--- >>> let settings = defaultSettings { cache = True, metaCache = 0, noNetwork = True }--- >>> let leanAppState = LeanAppState settings dirs' defaultKeyBindings lc--- >>> cwd <- getCurrentDirectory--- >>> (Right ref) <- pure $ GHCup.Utils.parseURI $ "file://" <> E.encodeUtf8 (T.pack cwd) <> "/data/metadata/" <> (urlBaseName . view pathL' $ ghcupURL)--- >>> (VRight r) <- (fmap . fmap) _ghcupDownloads $ flip runReaderT leanAppState . runE @'[DigestError, GPGError, JSONError , DownloadFailed , FileDoesNotExistError, ContentLengthError] $ liftE (getBase ref) >>= liftE . decodeMetadata @GHCupInfo---- ------------------------- --[ Symlink handling ]--- ----------------------------- | Create a relative symlink destination for the binary directory,--- given a target toolpath.-binarySymLinkDestination :: ( MonadThrow m- , MonadIO m- )- => FilePath -- ^ binary dir- -> FilePath -- ^ the full toolpath- -> m FilePath-binarySymLinkDestination binDir toolPath = do- toolPath' <- liftIO $ canonicalizePath toolPath- binDir' <- liftIO $ canonicalizePath binDir- pure (relativeSymlink binDir' toolPath')----- | Removes the minor GHC symlinks, e.g. ghc-8.6.5.-rmMinorGHCSymlinks :: ( MonadReader env m- , HasDirs env- , MonadIO m- , HasLog env- , MonadThrow m- , MonadFail m- , MonadMask m- )- => GHCTargetVersion- -> Excepts '[NotInstalled] m ()-rmMinorGHCSymlinks tv@GHCTargetVersion{..} = do- Dirs {..} <- lift getDirs-- files <- liftE $ ghcToolFiles tv- forM_ files $ \f -> do- let f_xyz = f <> "-" <> T.unpack (prettyVer _tvVersion) <> exeExt- let fullF = binDir </> f_xyz- lift $ logDebug ("rm -f " <> T.pack fullF)- lift $ hideError doesNotExistErrorType $ rmLink fullF----- | Removes the set ghc version for the given target, if any.-rmPlainGHC :: ( MonadReader env m- , HasDirs env- , HasLog env- , MonadThrow m- , MonadFail m- , MonadIO m- , MonadMask m- )- => Maybe Text -- ^ target- -> Excepts '[NotInstalled] m ()-rmPlainGHC target = do- Dirs {..} <- lift getDirs- mtv <- lift $ ghcSet target- forM_ mtv $ \tv -> do- files <- liftE $ ghcToolFiles tv- forM_ files $ \f -> do- let fullF = binDir </> f <> exeExt- lift $ logDebug ("rm -f " <> T.pack fullF)- lift $ hideError doesNotExistErrorType $ rmLink fullF- -- old ghcup- let hdc_file = binDir </> "haddock-ghc" <> exeExt- lift $ logDebug ("rm -f " <> T.pack hdc_file)- lift $ hideError doesNotExistErrorType $ rmLink hdc_file----- | Remove the major GHC symlink, e.g. ghc-8.6.-rmMajorGHCSymlinks :: ( MonadReader env m- , HasDirs env- , MonadIO m- , HasLog env- , MonadThrow m- , MonadFail m- , MonadMask m- )- => GHCTargetVersion- -> Excepts '[NotInstalled] m ()-rmMajorGHCSymlinks tv@GHCTargetVersion{..} = do- Dirs {..} <- lift getDirs- (mj, mi) <- getMajorMinorV _tvVersion- let v' = intToText mj <> "." <> intToText mi-- files <- liftE $ ghcToolFiles tv- forM_ files $ \f -> do- let f_xy = f <> "-" <> T.unpack v' <> exeExt- let fullF = binDir </> f_xy- lift $ logDebug ("rm -f " <> T.pack fullF)- lift $ hideError doesNotExistErrorType $ rmLink fullF----- | Removes the minor HLS files, e.g. 'haskell-language-server-8.10.7~1.6.1.0'--- and 'haskell-language-server-wrapper-1.6.1.0'.-rmMinorHLSSymlinks :: ( MonadReader env m- , HasDirs env- , MonadIO m- , HasLog env- , MonadThrow m- , MonadFail m- , MonadMask m- )- => Version- -> Excepts '[NotInstalled] m ()-rmMinorHLSSymlinks ver = do- Dirs {..} <- lift getDirs-- hlsBins <- hlsAllBinaries ver- forM_ hlsBins $ \f -> do- let fullF = binDir </> f- lift $ logDebug ("rm -f " <> T.pack fullF)- -- on unix, this may be either a file (legacy) or a symlink- -- on windows, this is always a file... hence 'rmFile'- -- works consistently across platforms- lift $ rmFile fullF---- | Removes the set HLS version, if any.-rmPlainHLS :: ( MonadReader env m- , HasDirs env- , HasLog env- , MonadThrow m- , MonadFail m- , MonadIO m- , MonadMask m- )- => Excepts '[NotInstalled] m ()-rmPlainHLS = do- Dirs {..} <- lift getDirs-- -- delete 'haskell-language-server-8.10.7'- hlsBins <- fmap (filter (\f -> not ("haskell-language-server-wrapper" `isPrefixOf` f) && ('~' `notElem` f)))- $ liftIO $ handleIO (\_ -> pure []) $ findFiles- binDir- (makeRegexOpts compExtended execBlank ([s|^haskell-language-server-.*$|] :: ByteString))- forM_ hlsBins $ \f -> do- let fullF = binDir </> f- lift $ logDebug ("rm -f " <> T.pack fullF)- if isWindows- then lift $ rmLink fullF- else lift $ rmFile fullF-- -- 'haskell-language-server-wrapper'- let hlswrapper = binDir </> "haskell-language-server-wrapper" <> exeExt- lift $ logDebug ("rm -f " <> T.pack hlswrapper)- if isWindows- then lift $ hideError doesNotExistErrorType $ rmLink hlswrapper- else lift $ hideError doesNotExistErrorType $ rmFile hlswrapper---- ------------------------------------ --[ Set/Installed introspection ]--- ---------------------------------------- | Whether the given GHC version is installed.-ghcInstalled :: (MonadIO m, MonadReader env m, HasDirs env, MonadThrow m) => GHCTargetVersion -> m Bool-ghcInstalled ver = do- ghcdir <- ghcupGHCDir ver- liftIO $ doesDirectoryExist (fromGHCupPath ghcdir)----- | Whether the given GHC version is set as the current.-ghcSet :: (MonadReader env m, HasDirs env, MonadThrow m, MonadIO m)- => Maybe Text -- ^ the target of the GHC version, if any- -- (e.g. armv7-unknown-linux-gnueabihf)- -> m (Maybe GHCTargetVersion)-ghcSet mtarget = do- Dirs {..} <- getDirs- let ghc = maybe "ghc" (\t -> T.unpack t <> "-ghc") mtarget- let ghcBin = binDir </> ghc <> exeExt-- -- link destination is of the form ../ghc/<ver>/bin/ghc- -- for old ghcup, it is ../ghc/<ver>/bin/ghc-<ver>- liftIO $ handleIO' NoSuchThing (\_ -> pure Nothing) $ do- link <- liftIO $ getLinkTarget ghcBin- Just <$> ghcLinkVersion link- where- ghcLinkVersion :: MonadThrow m => FilePath -> m GHCTargetVersion- ghcLinkVersion (T.pack . dropSuffix exeExt -> t) = throwEither $ MP.parse ghcVersionFromPath "ghcLinkVersion" t---- | Get all installed GHCs by reading ~/.ghcup/ghc/<dir>.--- If a dir cannot be parsed, returns left.-getInstalledGHCs :: (MonadReader env m, HasDirs env, MonadIO m) => m [Either FilePath GHCTargetVersion]-getInstalledGHCs = do- ghcdir <- ghcupGHCBaseDir- fs <- liftIO $ hideErrorDef [NoSuchThing] [] $ listDirectoryDirs (fromGHCupPath ghcdir)- forM fs $ \f -> case parseGHCupGHCDir f of- Right r -> pure $ Right r- Left _ -> pure $ Left f----- | Get all installed cabals, by matching on @~\/.ghcup\/bin/cabal-*@.-getInstalledCabals :: ( MonadReader env m- , HasDirs env- , MonadIO m- , MonadCatch m- )- => m [Either FilePath Version]-getInstalledCabals = do- Dirs {..} <- getDirs- bins <- liftIO $ handleIO (\_ -> pure []) $ findFiles- binDir- (makeRegexOpts compExtended execBlank ([s|^cabal-.*$|] :: ByteString))- vs <- forM bins $ \f -> case version . T.pack <$> (stripSuffix exeExt =<< stripPrefix "cabal-" f) of- Just (Right r) -> pure $ Right r- Just (Left _) -> pure $ Left f- Nothing -> pure $ Left f- pure $ nub vs----- | Whether the given cabal version is installed.-cabalInstalled :: (MonadIO m, MonadReader env m, HasDirs env, MonadCatch m) => Version -> m Bool-cabalInstalled ver = do- vers <- fmap rights getInstalledCabals- pure $ elem ver vers----- Return the currently set cabal version, if any.-cabalSet :: (HasLog env, MonadReader env m, HasDirs env, MonadIO m, MonadThrow m, MonadCatch m) => m (Maybe Version)-cabalSet = do- Dirs {..} <- getDirs- let cabalbin = binDir </> "cabal" <> exeExt-- handleIO' NoSuchThing (\_ -> pure Nothing) $ do- broken <- liftIO $ isBrokenSymlink cabalbin- if broken- then do- logWarn $ "Broken symlink at " <> T.pack cabalbin- pure Nothing- else do- link <- liftIO- $ handleIO' InvalidArgument- (\e -> pure $ Left (toException e))- $ fmap Right $ getLinkTarget cabalbin- case linkVersion =<< link of- Right v -> pure $ Just v- Left err -> do- logWarn $ "Failed to parse cabal symlink target with: "- <> T.pack (displayException err)- <> ". The symlink "- <> T.pack cabalbin- <> " needs to point to valid cabal binary, such as 'cabal-3.4.0.0'."- pure Nothing- where- -- We try to be extra permissive with link destination parsing,- -- because of:- -- https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/119- linkVersion :: MonadThrow m => FilePath -> m Version- linkVersion = throwEither . MP.parse parser "linkVersion" . T.pack . dropSuffix exeExt-- parser- = MP.try (stripAbsolutePath *> cabalParse)- <|> MP.try (stripRelativePath *> cabalParse)- <|> cabalParse- -- parses the version of "cabal-3.2.0.0" -> "3.2.0.0"- cabalParse = MP.chunk "cabal-" *> version'- -- parses any path component ending with path separator,- -- e.g. "foo/"- stripPathComponet = parseUntil1 pathSep *> MP.some pathSep- -- parses an absolute path up until the last path separator,- -- e.g. "/bar/baz/foo" -> "/bar/baz/", leaving "foo"- stripAbsolutePath = MP.some pathSep *> MP.many (MP.try stripPathComponet)- -- parses a relative path up until the last path separator,- -- e.g. "bar/baz/foo" -> "bar/baz/", leaving "foo"- stripRelativePath = MP.many (MP.try stripPathComponet)------ | Get all installed hls, by matching on--- @~\/.ghcup\/bin/haskell-language-server-wrapper-<\hlsver\>@,--- as well as @~\/.ghcup\/hls\/<\hlsver\>@-getInstalledHLSs :: (MonadReader env m, HasDirs env, MonadIO m, MonadCatch m)- => m [Either FilePath Version]-getInstalledHLSs = do- Dirs {..} <- getDirs- bins <- liftIO $ handleIO (\_ -> pure []) $ findFiles- binDir- (makeRegexOpts compExtended- execBlank- ([s|^haskell-language-server-wrapper-.*$|] :: ByteString)- )- legacy <- forM bins $ \f ->- case- version . T.pack <$> (stripSuffix exeExt =<< stripPrefix "haskell-language-server-wrapper-" f)- of- Just (Right r) -> pure $ Right r- Just (Left _) -> pure $ Left f- Nothing -> pure $ Left f-- hlsdir <- ghcupHLSBaseDir- fs <- liftIO $ hideErrorDef [NoSuchThing] [] $ listDirectoryDirs (fromGHCupPath hlsdir)- new <- forM fs $ \f -> case parseGHCupHLSDir f of- Right r -> pure $ Right r- Left _ -> pure $ Left f- pure (nub (new <> legacy))----- | Get all installed stacks, by matching on--- @~\/.ghcup\/bin/stack-<\stackver\>@.-getInstalledStacks :: (MonadReader env m, HasDirs env, MonadIO m, MonadCatch m)- => m [Either FilePath Version]-getInstalledStacks = do- Dirs {..} <- getDirs- bins <- liftIO $ handleIO (\_ -> pure []) $ findFiles- binDir- (makeRegexOpts compExtended- execBlank- ([s|^stack-.*$|] :: ByteString)- )- forM bins $ \f ->- case version . T.pack <$> (stripSuffix exeExt =<< stripPrefix "stack-" f) of- Just (Right r) -> pure $ Right r- Just (Left _) -> pure $ Left f- Nothing -> pure $ Left f---- Return the currently set stack version, if any.--- TODO: there's a lot of code duplication here :>-stackSet :: (MonadReader env m, HasDirs env, MonadIO m, MonadThrow m, MonadCatch m, HasLog env) => m (Maybe Version)-stackSet = do- Dirs {..} <- getDirs- let stackBin = binDir </> "stack" <> exeExt-- handleIO' NoSuchThing (\_ -> pure Nothing) $ do- broken <- liftIO $ isBrokenSymlink stackBin- if broken- then do- logWarn $ "Broken symlink at " <> T.pack stackBin- pure Nothing- else do- link <- liftIO- $ handleIO' InvalidArgument- (\e -> pure $ Left (toException e))- $ fmap Right $ getLinkTarget stackBin- case linkVersion =<< link of- Right v -> pure $ Just v- Left err -> do- logWarn $ "Failed to parse stack symlink target with: "- <> T.pack (displayException err)- <> ". The symlink "- <> T.pack stackBin- <> " needs to point to valid stack binary, such as 'stack-2.7.1'."- pure Nothing- where- linkVersion :: MonadThrow m => FilePath -> m Version- linkVersion = throwEither . MP.parse parser "" . T.pack . dropSuffix exeExt- where- parser- = MP.try (stripAbsolutePath *> cabalParse)- <|> MP.try (stripRelativePath *> cabalParse)- <|> cabalParse- -- parses the version of "stack-2.7.1" -> "2.7.1"- cabalParse = MP.chunk "stack-" *> version'- -- parses any path component ending with path separator,- -- e.g. "foo/"- stripPathComponet = parseUntil1 pathSep *> MP.some pathSep- -- parses an absolute path up until the last path separator,- -- e.g. "/bar/baz/foo" -> "/bar/baz/", leaving "foo"- stripAbsolutePath = MP.some pathSep *> MP.many (MP.try stripPathComponet)- -- parses a relative path up until the last path separator,- -- e.g. "bar/baz/foo" -> "bar/baz/", leaving "foo"- stripRelativePath = MP.many (MP.try stripPathComponet)---- | Whether the given Stack version is installed.-stackInstalled :: (MonadIO m, MonadReader env m, HasDirs env, MonadCatch m) => Version -> m Bool-stackInstalled ver = do- vers <- fmap rights getInstalledStacks- pure $ elem ver vers---- | Whether the given HLS version is installed.-hlsInstalled :: (MonadIO m, MonadReader env m, HasDirs env, MonadCatch m) => Version -> m Bool-hlsInstalled ver = do- vers <- fmap rights getInstalledHLSs- pure $ elem ver vers--isLegacyHLS :: (MonadIO m, MonadReader env m, HasDirs env, MonadCatch m) => Version -> m Bool-isLegacyHLS ver = do- bdir <- ghcupHLSDir ver- not <$> liftIO (doesDirectoryExist $ fromGHCupPath bdir)----- Return the currently set hls version, if any.-hlsSet :: (HasLog env, MonadReader env m, HasDirs env, MonadIO m, MonadThrow m, MonadCatch m) => m (Maybe Version)-hlsSet = do- Dirs {..} <- getDirs- let hlsBin = binDir </> "haskell-language-server-wrapper" <> exeExt-- handleIO' NoSuchThing (\_ -> pure Nothing) $ do- broken <- liftIO $ isBrokenSymlink hlsBin- if broken- then do- logWarn $ "Broken symlink at " <> T.pack hlsBin- pure Nothing- else do- link <- liftIO $ getLinkTarget hlsBin- Just <$> linkVersion link- where- linkVersion :: MonadThrow m => FilePath -> m Version- linkVersion = throwEither . MP.parse parser "" . T.pack . dropSuffix exeExt- where- parser- = MP.try (stripAbsolutePath *> cabalParse)- <|> MP.try (stripRelativePath *> cabalParse)- <|> cabalParse- -- parses the version of "haskell-language-server-wrapper-1.1.0" -> "1.1.0"- cabalParse = MP.chunk "haskell-language-server-wrapper-" *> version'- -- parses any path component ending with path separator,- -- e.g. "foo/"- stripPathComponet = parseUntil1 pathSep *> MP.some pathSep- -- parses an absolute path up until the last path separator,- -- e.g. "/bar/baz/foo" -> "/bar/baz/", leaving "foo"- stripAbsolutePath = MP.some pathSep *> MP.many (MP.try stripPathComponet)- -- parses a relative path up until the last path separator,- -- e.g. "bar/baz/foo" -> "bar/baz/", leaving "foo"- stripRelativePath = MP.many (MP.try stripPathComponet)----- | Return the GHC versions the currently selected HLS supports.-hlsGHCVersions :: ( MonadReader env m- , HasDirs env- , HasLog env- , MonadIO m- , MonadThrow m- , MonadCatch m- )- => m [Version]-hlsGHCVersions = do- h <- hlsSet- fromMaybe [] <$> forM h hlsGHCVersions'---hlsGHCVersions' :: ( MonadReader env m- , HasDirs env- , MonadIO m- , MonadThrow m- , MonadCatch m- )- => Version- -> m [Version]-hlsGHCVersions' v' = do- bins <- hlsServerBinaries v' Nothing- let vers = fmap- (version- . T.pack- . fromJust- . stripPrefix "haskell-language-server-"- . head- . splitOn "~"- )- bins- pure . sortBy (flip compare) . rights $ vers----- | Get all server binaries for an hls version from the ~/.ghcup/bin directory, if any.-hlsServerBinaries :: (MonadReader env m, HasDirs env, MonadIO m)- => Version- -> Maybe Version -- ^ optional GHC version- -> m [FilePath]-hlsServerBinaries ver mghcVer = do- Dirs {..} <- getDirs- liftIO $ handleIO (\_ -> pure []) $ findFiles- binDir- (makeRegexOpts- compExtended- execBlank- ([s|^haskell-language-server-|]- <> maybe [s|.*|] escapeVerRex mghcVer- <> [s|~|]- <> escapeVerRex ver- <> E.encodeUtf8 (T.pack exeExt)- <> [s|$|] :: ByteString- )- )---- | Get all scripts for a hls version from the ~/.ghcup/hls/<ver>/bin directory, if any.--- Returns the full path.-hlsInternalServerScripts :: (MonadReader env m, HasDirs env, MonadIO m, MonadThrow m)- => Version- -> Maybe Version -- ^ optional GHC version- -> m [FilePath]-hlsInternalServerScripts ver mghcVer = do- dir <- ghcupHLSDir ver- let bdir = fromGHCupPath dir </> "bin"- fmap (bdir </>) . filter (\f -> maybe True (\gv -> ("-" <> T.unpack (prettyVer gv)) `isSuffixOf` f) mghcVer)- <$> liftIO (listDirectoryFiles bdir)---- | Get all binaries for a hls version from the ~/.ghcup/hls/<ver>/lib/haskell-language-server-<ver>/bin directory, if any.--- Returns the full path.-hlsInternalServerBinaries :: (MonadReader env m, HasDirs env, MonadIO m, MonadThrow m, MonadFail m)- => Version- -> Maybe Version -- ^ optional GHC version- -> m [FilePath]-hlsInternalServerBinaries ver mghcVer = do- dir <- fromGHCupPath <$> ghcupHLSDir ver- let regex = makeRegexOpts compExtended execBlank ([s|^haskell-language-server-.*$|] :: ByteString)- (Just bdir) <- fmap headMay $ liftIO $ expandFilePath [Left (dir </> "lib"), Right regex, Left "bin"]- fmap (bdir </>) . filter (\f -> maybe True (\gv -> ("-" <> T.unpack (prettyVer gv)) `isSuffixOf` f) mghcVer)- <$> liftIO (listDirectoryFiles bdir)---- | Get all libraries for a hls version from the ~/.ghcup/hls/<ver>/lib/haskell-language-server-<ver>/lib/<ghc-ver>/--- directory, if any.--- Returns the full path.-hlsInternalServerLibs :: (MonadReader env m, HasDirs env, MonadIO m, MonadThrow m, MonadFail m)- => Version- -> Version -- ^ GHC version- -> m [FilePath]-hlsInternalServerLibs ver ghcVer = do- dir <- fromGHCupPath <$> ghcupHLSDir ver- let regex = makeRegexOpts compExtended execBlank ([s|^haskell-language-server-.*$|] :: ByteString)- (Just bdir) <- fmap headMay $ liftIO $ expandFilePath [Left (dir </> "lib"), Right regex, Left ("lib" </> T.unpack (prettyVer ghcVer))]- fmap (bdir </>) <$> liftIO (listDirectoryFiles bdir)----- | Get the wrapper binary for an hls version, if any.-hlsWrapperBinary :: (MonadReader env m, HasDirs env, MonadThrow m, MonadIO m)- => Version- -> m (Maybe FilePath)-hlsWrapperBinary ver = do- Dirs {..} <- getDirs- wrapper <- liftIO $ handleIO (\_ -> pure []) $ findFiles- binDir- (makeRegexOpts- compExtended- execBlank- ([s|^haskell-language-server-wrapper-|] <> escapeVerRex ver <> E.encodeUtf8 (T.pack exeExt) <> [s|$|] :: ByteString- )- )- case wrapper of- [] -> pure Nothing- [x] -> pure $ Just x- _ -> throwM $ UnexpectedListLength- "There were multiple hls wrapper binaries for a single version"----- | Get all binaries for an hls version, if any.-hlsAllBinaries :: (MonadReader env m, HasDirs env, MonadIO m, MonadThrow m) => Version -> m [FilePath]-hlsAllBinaries ver = do- hls <- hlsServerBinaries ver Nothing- wrapper <- hlsWrapperBinary ver- pure (maybeToList wrapper ++ hls)------ ------------------------------------------ --[ Major version introspection (X.Y) ]--- ---------------------------------------------- | Extract (major, minor) from any version.-getMajorMinorV :: MonadThrow m => Version -> m (Int, Int)-getMajorMinorV (Version _ (Chunks (Numeric x :| Numeric y : _)) _ _) = pure (fromIntegral x, fromIntegral y)-getMajorMinorV _ = throwM $ ParseError "Could not parse X.Y from version"--matchMajor :: Version -> Int -> Int -> Bool-matchMajor v' major' minor' = case getMajorMinorV v' of- Just (x, y) -> x == major' && y == minor'- Nothing -> False---- | Match PVP prefix.------ >>> matchPVPrefix [pver|8.8|] [pver|8.8.4|]--- True--- >>> matchPVPrefix [pver|8|] [pver|8.8.4|]--- True--- >>> matchPVPrefix [pver|8.10|] [pver|8.8.4|]--- False--- >>> matchPVPrefix [pver|8.10|] [pver|8.10.7|]--- True-matchPVPrefix :: PVP -> PVP -> Bool-matchPVPrefix (toL -> prefix) (toL -> full) = and $ zipWith (==) prefix full--toL :: PVP -> [Int]-toL (PVP inner) = fmap fromIntegral $ NE.toList inner----- | Get the latest installed full GHC version that satisfies the given (possibly partial)--- PVP version.-getGHCForPVP :: (MonadReader env m, HasDirs env, MonadIO m, MonadThrow m)- => PVP- -> Maybe Text -- ^ the target triple- -> m (Maybe GHCTargetVersion)-getGHCForPVP pvpIn mt = do- ghcs <- rights <$> getInstalledGHCs- -- we're permissive here... failed parse just means we have no match anyway- let ghcs' = catMaybes $ flip fmap ghcs $ \GHCTargetVersion{..} -> do- (pvp_, rest) <- versionToPVP _tvVersion- pure (pvp_, rest, _tvTarget)-- getGHCForPVP' pvpIn ghcs' mt---- | Like 'getGHCForPVP', except with explicit input parameter.------ >>> getGHCForPVP' [pver|8|] installedVersions Nothing--- Just (GHCTargetVersion {_tvTarget = Nothing, _tvVersion = Version {_vEpoch = Nothing, _vChunks = Chunks (Numeric 8 :| [Numeric 10,Numeric 7]), _vRel = Just (Release (Alphanum "debug" :| [])), _vMeta = Just "lol"}})--- >>> fmap prettyShow $ getGHCForPVP' [pver|8.8|] installedVersions Nothing--- "Just 8.8.4"--- >>> fmap prettyShow $ getGHCForPVP' [pver|8.10.4|] installedVersions Nothing--- "Just 8.10.4"-getGHCForPVP' :: MonadThrow m- => PVP- -> [(PVP, Text, Maybe Text)] -- ^ installed GHCs- -> Maybe Text -- ^ the target triple- -> m (Maybe GHCTargetVersion)-getGHCForPVP' pvpIn ghcs' mt = do- let mResult = lastMay- . sortBy (\(x, _, _) (y, _, _) -> compare x y)- . filter- (\(pvp_, _, target) ->- target == mt && matchPVPrefix pvp_ pvpIn- )- $ ghcs'- forM mResult $ \(pvp_, rest, target) -> do- ver' <- pvpToVersion pvp_ rest- pure (GHCTargetVersion target ver')----- | Get the latest available ghc for the given PVP version, which--- may only contain parts.------ >>> (fmap . fmap) (\(p, _, _) -> p) $ getLatestToolFor GHC Nothing [pver|8|] r--- Just (PVP {_pComponents = 8 :| [10,7]})--- >>> (fmap . fmap) (\(p, _, _) -> p) $ getLatestToolFor GHC Nothing [pver|8.8|] r--- Just (PVP {_pComponents = 8 :| [8,4]})--- >>> (fmap . fmap) (\(p, _, _) -> p) $ getLatestToolFor GHC Nothing [pver|8.8.4|] r--- Just (PVP {_pComponents = 8 :| [8,4]})-getLatestToolFor :: MonadThrow m- => Tool- -> Maybe Text- -> PVP- -> GHCupDownloads- -> m (Maybe (PVP, VersionInfo, Maybe Text))-getLatestToolFor tool target pvpIn dls = do- let ls :: [(GHCTargetVersion, VersionInfo)]- ls = fromMaybe [] $ preview (ix tool % to Map.toDescList) dls- let ps :: [((PVP, Text), VersionInfo, Maybe Text)]- ps = catMaybes $ fmap (\(v, vi) -> (,vi, _tvTarget v) <$> versionToPVP (_tvVersion v)) ls- pure . fmap (\((pv', _), vi, mt) -> (pv', vi, mt)) . headMay . filter (\((v, _), _, t) -> matchPVPrefix pvpIn v && t == target) $ ps---- ------------- --[ Tags ]--- ----------------- | Get the tool version that has this tag. If multiple have it,--- picks the greatest version.-getTagged :: Tag- -> Fold (Map.Map GHCTargetVersion VersionInfo) (GHCTargetVersion, VersionInfo)-getTagged tag =- to (Map.toDescList . Map.filter (\VersionInfo {..} -> tag `elem` _viTags))- % folding id--getByReleaseDay :: GHCupDownloads -> Tool -> Day -> Either (Maybe Day) (GHCTargetVersion, VersionInfo)-getByReleaseDay av tool day = let mvv = fromMaybe mempty $ headOf (ix tool) av- mdv = Map.foldrWithKey (\k vi@VersionInfo{..} m ->- maybe m (\d -> let diff = diffDays d day- in Map.insert (abs diff) (diff, (k, vi)) m) _viReleaseDay)- Map.empty mvv- in case headMay (Map.toAscList mdv) of- Nothing -> Left Nothing- Just (absDiff, (diff, (k, vi)))- | absDiff == 0 -> Right (k, vi)- | otherwise -> Left (Just (addDays diff day))--getByReleaseDayFold :: Day -> Fold (Map.Map GHCTargetVersion VersionInfo) (GHCTargetVersion, VersionInfo)-getByReleaseDayFold day = to (Map.toDescList . Map.filter (\VersionInfo {..} -> Just day == _viReleaseDay)) % folding id--getLatest :: GHCupDownloads -> Tool -> Maybe (GHCTargetVersion, VersionInfo)-getLatest av tool = headOf (ix tool % getTagged Latest) av--getLatestPrerelease :: GHCupDownloads -> Tool -> Maybe (GHCTargetVersion, VersionInfo)-getLatestPrerelease av tool = headOf (ix tool % getTagged LatestPrerelease) av--getLatestNightly :: GHCupDownloads -> Tool -> Maybe (GHCTargetVersion, VersionInfo)-getLatestNightly av tool = headOf (ix tool % getTagged LatestNightly) av--getRecommended :: GHCupDownloads -> Tool -> Maybe (GHCTargetVersion, VersionInfo)-getRecommended av tool = headOf (ix tool % getTagged Recommended) av----- | Gets the latest GHC with a given base version.-getLatestBaseVersion :: GHCupDownloads -> PVP -> Maybe (GHCTargetVersion, VersionInfo)-getLatestBaseVersion av pvpVer =- headOf (ix GHC % getTagged (Base pvpVer)) av----- -------------- --[ Other ]--- ----------------intoSubdir :: (MonadReader env m, HasLog env, MonadIO m, MonadThrow m, MonadCatch m)- => GHCupPath -- ^ unpacked tar dir- -> TarDir -- ^ how to descend- -> Excepts '[TarDirDoesNotExist] m GHCupPath-intoSubdir bdir tardir = case tardir of- RealDir pr -> do- whenM (fmap not . liftIO . doesDirectoryExist $ fromGHCupPath (bdir `appendGHCupPath` pr))- (throwE $ TarDirDoesNotExist tardir)- pure (bdir `appendGHCupPath` pr)- RegexDir r -> do- let rs = split (`elem` pathSeparators) r- foldlM- (\y x ->- (handleIO (\_ -> pure []) . liftIO . findFiles (fromGHCupPath y) . regex $ x) >>= (\case- [] -> throwE $ TarDirDoesNotExist tardir- (p : _) -> pure (y `appendGHCupPath` p)) . sort- )- bdir- rs- where regex = makeRegexOpts compIgnoreCase execBlank---- | Usually @~\/.ghcup\/ghc\/\<ver\>\/bin\/@-ghcInternalBinDir :: (MonadReader env m, HasDirs env, MonadThrow m, MonadFail m, MonadIO m)- => GHCTargetVersion- -> m FilePath-ghcInternalBinDir ver = do- ghcdir <- fromGHCupPath <$> ghcupGHCDir ver- pure (ghcdir </> "bin")----- | Get tool files from @~\/.ghcup\/ghc\/\<ver\>\/bin\/\*@--- while ignoring @*-\<ver\>@ symlinks and accounting for cross triple prefix.------ Returns unversioned relative files without extension, e.g.:------ - @["hsc2hs","haddock","hpc","runhaskell","ghc","ghc-pkg","ghci","runghc","hp2ps"]@-ghcToolFiles :: (MonadReader env m, HasDirs env, MonadThrow m, MonadFail m, MonadIO m)- => GHCTargetVersion- -> Excepts '[NotInstalled] m [FilePath]-ghcToolFiles ver = do- bindir <- ghcInternalBinDir ver-- -- fail if ghc is not installed- whenM (fmap not $ ghcInstalled ver)- (throwE (NotInstalled GHC ver))-- files <- liftIO (listDirectoryFiles bindir >>= filterM (doesFileExist . (bindir </>)))- pure (getUniqueTools . groupToolFiles . fmap (dropSuffix exeExt) $ files)-- where-- groupToolFiles :: [FilePath] -> [[(FilePath, String)]]- groupToolFiles = groupBy (\(a, _) (b, _) -> a == b) . fmap (splitOnPVP "-")-- getUniqueTools :: [[(FilePath, String)]] -> [String]- getUniqueTools = filter (isNotAnyInfix blackListedTools) . nub . fmap fst . concatMap (filter ((== "") . snd))-- blackListedTools :: [String]- blackListedTools = ["haddock-ghc"]-- isNotAnyInfix :: [String] -> String -> Bool- isNotAnyInfix xs t = foldr (\a b -> not (a `isInfixOf` t) && b) True xs------ | Calls gmake if it exists in PATH, otherwise make.-make :: ( MonadThrow m- , MonadIO m- , MonadReader env m- , HasDirs env- , HasLog env- , HasSettings env- )- => [String]- -> Maybe FilePath- -> m (Either ProcessError ())-make args workdir = make' args workdir "ghc-make" Nothing----- | Calls gmake if it exists in PATH, otherwise make.-make' :: ( MonadThrow m- , MonadIO m- , MonadReader env m- , HasDirs env- , HasLog env- , HasSettings env- )- => [String]- -> Maybe FilePath- -> FilePath -- ^ log filename (opened in append mode)- -> Maybe [(String, String)] -- ^ optional environment- -> m (Either ProcessError ())-make' args workdir logfile menv = do- spaths <- liftIO getSearchPath- has_gmake <- isJust <$> liftIO (searchPath spaths "gmake")- let mymake = if has_gmake then "gmake" else "make"- execLogged mymake args workdir logfile menv---makeOut :: (MonadReader env m, HasDirs env, MonadIO m)- => [String]- -> Maybe FilePath- -> m CapturedProcess-makeOut args workdir = do- spaths <- liftIO getSearchPath- has_gmake <- isJust <$> liftIO (searchPath spaths "gmake")- let mymake = if has_gmake then "gmake" else "make"- executeOut mymake args workdir----- | Try to apply patches in order. The order is determined by--- a quilt series file (in the patch directory) if one exists,--- else the patches are applied in lexicographical order.--- Fails with 'PatchFailed' on first failure.-applyPatches :: (MonadReader env m, HasDirs env, HasLog env, MonadIO m)- => FilePath -- ^ dir containing patches- -> FilePath -- ^ dir to apply patches in- -> Excepts '[PatchFailed] m ()-applyPatches pdir ddir = do- let lexicographical = (fmap . fmap) (pdir </>) $ sort <$> findFiles- pdir- (makeRegexOpts compExtended- execBlank- ([s|.+\.(patch|diff)$|] :: ByteString)- )- let quilt = map (pdir </>) . lines <$> readFile (pdir </> "series")-- patches <- liftIO $ quilt `catchIO` (\e ->- if isDoesNotExistError e || isPermissionError e then- lexicographical- else throwIO e)- forM_ patches $ \patch' -> applyPatch patch' ddir---applyPatch :: (MonadReader env m, HasDirs env, HasLog env, MonadIO m)- => FilePath -- ^ Patch- -> FilePath -- ^ dir to apply patches in- -> Excepts '[PatchFailed] m ()-applyPatch patch ddir = do- lift $ logInfo $ "Applying patch " <> T.pack patch- fmap (either (const Nothing) Just)- (exec- "patch"- ["-p1", "-s", "-f", "-i", patch]- (Just ddir)- Nothing)- !? PatchFailed---applyAnyPatch :: ( MonadReader env m- , HasDirs env- , HasLog env- , HasSettings env- , MonadUnliftIO m- , MonadCatch m- , MonadResource m- , MonadThrow m- , MonadMask m- , MonadIO m)- => Maybe (Either FilePath [URI])- -> FilePath- -> Excepts '[PatchFailed, DownloadFailed, DigestError, ContentLengthError, GPGError] m ()-applyAnyPatch Nothing _ = pure ()-applyAnyPatch (Just (Left pdir)) workdir = liftE $ applyPatches pdir workdir-applyAnyPatch (Just (Right uris)) workdir = do- tmpUnpack <- fromGHCupPath <$> lift withGHCupTmpDir- forM_ uris $ \uri -> do- patch <- liftE $ download uri Nothing Nothing Nothing tmpUnpack Nothing False- liftE $ applyPatch patch workdir----- | https://gitlab.haskell.org/ghc/ghc/-/issues/17353-darwinNotarization :: (MonadReader env m, HasDirs env, MonadIO m)- => Platform- -> FilePath- -> m (Either ProcessError ())-darwinNotarization Darwin path = exec- "/usr/bin/xattr"- ["-r", "-d", "com.apple.quarantine", path]- Nothing- Nothing-darwinNotarization _ _ = pure $ Right ()-----getChangeLog :: GHCupDownloads -> Tool -> ToolVersion -> Maybe URI-getChangeLog dls tool (GHCVersion v') =- preview (ix tool % ix v' % viChangeLog % _Just) dls-getChangeLog dls tool (ToolVersion (mkTVer -> v')) =- preview (ix tool % ix v' % viChangeLog % _Just) dls-getChangeLog dls tool (ToolTag tag) =- preview (ix tool % pre (getTagged tag) % to snd % viChangeLog % _Just) dls-getChangeLog dls tool (ToolDay day) =- preview (ix tool % pre (getByReleaseDayFold day) % to snd % viChangeLog % _Just) dls----- | Execute a build action while potentially cleaning up:------ 1. the build directory, depending on the KeepDirs setting-runBuildAction :: ( MonadReader env m- , HasDirs env- , HasSettings env- , MonadIO m- , MonadMask m- , HasLog env- , MonadUnliftIO m- , MonadFail m- , MonadCatch m- )- => GHCupPath -- ^ build directory (cleaned up depending on Settings)- -> Excepts e m a- -> Excepts e m a-runBuildAction bdir action = do- Settings {..} <- lift getSettings- let exAction = do- when (keepDirs == Never)- $ rmBDir bdir- v <-- flip onException (lift exAction)- $ onE_ exAction action- when (keepDirs == Never || keepDirs == Errors) $ lift $ rmBDir bdir- pure v----- | Clean up the given directory if the action fails,--- depending on the Settings.-cleanUpOnError :: forall e m a env .- ( MonadReader env m- , HasDirs env- , HasSettings env- , MonadIO m- , MonadMask m- , HasLog env- , MonadUnliftIO m- , MonadFail m- , MonadCatch m- )- => GHCupPath -- ^ build directory (cleaned up depending on Settings)- -> Excepts e m a- -> Excepts e m a-cleanUpOnError bdir action = do- Settings {..} <- lift getSettings- let exAction = when (keepDirs == Never) $ rmBDir bdir- flip onException (lift exAction) $ onE_ exAction action----- | Remove a build directory, ignoring if it doesn't exist and gracefully--- printing other errors without crashing.-rmBDir :: (MonadReader env m, HasLog env, MonadUnliftIO m, MonadIO m) => GHCupPath -> m ()-rmBDir dir = withRunInIO (\run -> run $- liftIO $ handleIO (\e -> run $ logWarn $- "Couldn't remove build dir " <> T.pack (fromGHCupPath dir) <> ", error was: " <> T.pack (displayException e))- $ hideError doesNotExistErrorType- $ rmPathForcibly dir)---getVersionInfo :: GHCTargetVersion- -> Tool- -> GHCupDownloads- -> Maybe VersionInfo-getVersionInfo v' tool =- headOf- ( ix tool- % to (Map.filterWithKey (\k _ -> k == v'))- % to Map.elems- % _head- )---ensureShimGen :: ( MonadMask m- , MonadThrow m- , HasLog env- , MonadIO m- , MonadReader env m- , HasDirs env- , HasSettings env- , HasGHCupInfo env- , MonadUnliftIO m- , MonadFail m- )- => Excepts '[URIParseError, GPGError, DigestError, ContentLengthError, DownloadFailed, NoDownload] m ()-ensureShimGen- | isWindows = do- dirs <- lift getDirs- let shimDownload = DownloadInfo (decUTF8Safe . serializeURIRef' $ shimGenURL) Nothing shimGenSHA Nothing Nothing Nothing- let dl = downloadCached' shimDownload (Just "gs.exe") Nothing- void $ (\DigestError{} -> do- lift $ logWarn "Digest doesn't match, redownloading gs.exe..."- lift $ logDebug ("rm -f " <> T.pack (fromGHCupPath (cacheDir dirs) </> "gs.exe"))- lift $ hideError doesNotExistErrorType $ recycleFile (fromGHCupPath (cacheDir dirs) </> "gs.exe")- liftE @'[URIParseError, GPGError, DigestError, ContentLengthError, DownloadFailed] $ dl- ) `catchE` liftE @'[URIParseError, GPGError, DigestError, ContentLengthError, DownloadFailed] dl- | otherwise = pure ()----- | Ensure ghcup directory structure exists.-ensureDirectories :: Dirs -> IO ()-ensureDirectories (Dirs baseDir binDir cacheDir logsDir confDir trashDir dbDir tmpDir _) = do- createDirRecursive' (fromGHCupPath baseDir)- createDirRecursive' (fromGHCupPath baseDir </> "ghc")- createDirRecursive' (fromGHCupPath baseDir </> "hls")- createDirRecursive' binDir- createDirRecursive' (fromGHCupPath cacheDir)- createDirRecursive' (fromGHCupPath logsDir)- createDirRecursive' (fromGHCupPath confDir)- createDirRecursive' (fromGHCupPath trashDir)- createDirRecursive' (fromGHCupPath dbDir)- createDirRecursive' (fromGHCupPath tmpDir)- pure ()----- | For ghc without arch triple, this is:------ - ghc------ For ghc with arch triple:------ - <triple>-ghc (e.g. arm-linux-gnueabihf-ghc)-ghcBinaryName :: GHCTargetVersion -> String-ghcBinaryName (GHCTargetVersion (Just t) _) = T.unpack (t <> "-ghc" <> T.pack exeExt)-ghcBinaryName (GHCTargetVersion Nothing _) = T.unpack ("ghc" <> T.pack exeExt)----- | Does basic checks for isolated installs--- Isolated Directory:--- 1. if it doesn't exist -> proceed--- 2. if it exists and is empty -> proceed--- 3. if it exists and is non-empty -> panic and leave the house-installDestSanityCheck :: ( MonadIO m- , MonadCatch m- , MonadMask m- , MonadUnliftIO m- ) =>- InstallDirResolved ->- Excepts '[DirNotEmpty] m ()-installDestSanityCheck (IsolateDirResolved isoDir) = do- hideErrorDef [doesNotExistErrorType] () $ do- empty' <- lift $ runConduitRes $ getDirectoryContentsRecursiveUnsafe isoDir .| C.null- when (not empty') (throwE $ DirNotEmpty isoDir)-installDestSanityCheck _ = pure ()----- | Returns 'Nothing' for legacy installs.-getInstalledFiles :: ( MonadIO m- , MonadCatch m- , MonadReader env m- , HasDirs env- , MonadFail m- )- => Tool- -> GHCTargetVersion- -> m (Maybe [FilePath])-getInstalledFiles t v' = hideErrorDef [doesNotExistErrorType] Nothing $ do- f <- recordedInstallationFile t v'- (force -> !c) <- liftIO- (readFile f >>= evaluate)- pure (Just $ lines c)----- | Warn if the installed and set HLS is not compatible with the installed and--- set GHC version.-warnAboutHlsCompatibility :: ( MonadReader env m- , HasDirs env- , HasLog env- , MonadThrow m- , MonadCatch m- , MonadIO m- )- => m ()-warnAboutHlsCompatibility = do- supportedGHC <- hlsGHCVersions- currentGHC <- fmap _tvVersion <$> ghcSet Nothing- currentHLS <- hlsSet-- case (currentGHC, currentHLS) of- (Just gv, Just hv) | gv `notElem` supportedGHC -> do- logWarn $- "GHC-" <> T.pack (prettyShow gv) <> " appears to have no corresponding HLS-" <> T.pack (prettyShow hv) <> " binary." <> "\n" <>- "Haskell IDE support may not work." <> "\n" <>- "You can try to either: " <> "\n" <>- " 1. Install a different HLS version (e.g. downgrade for older GHCs)" <> "\n" <>- " 2. Install and set one of the following GHCs: " <> T.pack (prettyShow supportedGHC) <> "\n" <>- " 3. Let GHCup compile HLS for you, e.g. run: ghcup compile hls -g " <> T.pack (prettyShow hv) <> " --ghc " <> T.pack (prettyShow gv) <> " --cabal-update\n" <>- " (see https://www.haskell.org/ghcup/guide/#hls for more information)"-- _ -> return ()---- ------------ --[ Git ]--- ---------------isCommitHash :: String -> Bool-isCommitHash str' = let hex = all isHexDigit str'- len = length str'- in hex && len == 40---gitOut :: (MonadReader env m, HasLog env, MonadIO m) => [String] -> FilePath -> Excepts '[ProcessError] m T.Text-gitOut args dir = do- CapturedProcess {..} <- lift $ executeOut "git" args (Just dir)- case _exitCode of- ExitSuccess -> pure $ T.pack $ stripNewlineEnd $ T.unpack $ decUTF8Safe' _stdOut- ExitFailure c -> do- let pe = NonZeroExit c "git" args- lift $ logDebug $ T.pack (prettyHFError pe)- throwE pe--processBranches :: T.Text -> [String]-processBranches str' = let lines' = lines (T.unpack str')- words' = fmap words lines'- refs = catMaybes $ fmap (`atMay` 1) words'- branches = catMaybes $ fmap (stripPrefix "refs/heads/") $ filter (isPrefixOf "refs/heads/") refs- in branches---- ------------------- --[ Versioning ]--- ----------------------- | Expand a list of version patterns describing a string such as "%v-%h".------ >>> expandVersionPattern (either (const Nothing) Just $ version "3.4.3") "a386748" "a3867484ccc391daad1a42002c3a2ba6a93c5221" "v0.1.20.0-119-ga386748" "issue-998" [CabalVer, S "-", GitHashShort, S "-", GitHashLong, S "-", GitBranchName, S "-", GitDescribe, S "-coco"]--- Version {_vEpoch = Nothing, _vChunks = Chunks (Numeric 3 :| [Numeric 4,Numeric 3]), _vRel = Just (Release (Alphanum "a386748-a3867484ccc391daad1a42002c3a2ba6a93c5221-issue-998-v0" :| [Numeric 1,Numeric 20,Alphanum "0-119-ga386748-coco"])), _vMeta = Nothing}-expandVersionPattern :: MonadFail m- => Maybe Version -- ^ cabal ver- -> String -- ^ git hash (short), if any- -> String -- ^ git hash (long), if any- -> String -- ^ git describe output, if any- -> String -- ^ git branch name, if any- -> [VersionPattern]- -> m Version-expandVersionPattern cabalVer gitHashS gitHashL gitDescribe gitBranch- = either (fail . displayException) pure . version . T.pack . go- where- go [] = ""- go (CabalVer:xs) = T.unpack (maybe "" prettyVer cabalVer) <> go xs- go (GitHashShort:xs) = gitHashS <> go xs- go (GitHashLong:xs) = gitHashL <> go xs- go (GitDescribe:xs) = gitDescribe <> go xs- go (GitBranchName:xs) = gitBranch <> go xs- go (S str:xs) = str <> go xs
− lib/GHCup/Utils/Dirs.hs
@@ -1,614 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ViewPatterns #-}-{-# LANGUAGE QuasiQuotes #-}--{-|-Module : GHCup.Utils.Dirs-Description : Definition of GHCup directories-Copyright : (c) Julian Ospald, 2020-License : LGPL-3.0-Maintainer : hasufell@hasufell.de-Stability : experimental-Portability : portable--}-module GHCup.Utils.Dirs- ( getAllDirs- , ghcupBaseDir- , ghcupConfigFile- , ghcupCacheDir- , ghcupGHCBaseDir- , ghcupGHCDir- , ghcupHLSBaseDir- , ghcupHLSDir- , mkGhcupTmpDir- , parseGHCupGHCDir- , parseGHCupHLSDir- , relativeSymlink- , withGHCupTmpDir- , getConfigFilePath- , getConfigFilePath'- , useXDG- , cleanupTrash- , ghcupMsys2BinDirs- , ghcupMsys2BinDirs'-- , GHCupPath- , appendGHCupPath- , fromGHCupPath- , createTempGHCupDirectory- , getGHCupTmpDirs-- , removeDirectory- , removeDirectoryRecursive- , removePathForcibly-- , listDirectoryFiles- , listDirectoryDirs-- -- System.Directory re-exports- , createDirectory- , createDirectoryIfMissing- , renameDirectory- , listDirectory- , getDirectoryContents- , getCurrentDirectory- , setCurrentDirectory- , withCurrentDirectory- , getHomeDirectory- , XdgDirectory(..)- , getXdgDirectory- , XdgDirectoryList(..)- , getXdgDirectoryList- , getAppUserDataDirectory- , getUserDocumentsDirectory- , getTemporaryDirectory- , removeFile- , renameFile- , renamePath- , getFileSize- , canonicalizePath- , makeAbsolute- , makeRelativeToCurrentDirectory- , doesPathExist- , doesFileExist- , doesDirectoryExist- , findExecutable- , findExecutables- , findExecutablesInDirectories- , findFile- , findFileWith- , findFilesWith- , exeExtension- , createFileLink- , createDirectoryLink- , removeDirectoryLink- , pathIsSymbolicLink- , getSymbolicLinkTarget- , Permissions- , emptyPermissions- , readable- , writable- , executable- , searchable- , setOwnerReadable- , setOwnerWritable- , setOwnerExecutable- , setOwnerSearchable- , getPermissions- , setPermissions- , copyPermissions- , getAccessTime- , getModificationTime- , setAccessTime- , setModificationTime- , isSymbolicLink- )-where---import GHCup.Errors-import GHCup.Types-import GHCup.Types.JSON ( )-import GHCup.Types.Optics-import GHCup.Prelude.MegaParsec-import GHCup.Prelude.File.Search-import GHCup.Prelude.String.QQ-import GHCup.Prelude.Logger.Internal (logWarn, logDebug)-#if defined(IS_WINDOWS)-import GHCup.Prelude.Windows ( isWindows )-#else-import GHCup.Prelude.Posix ( isWindows )-#endif--import Control.DeepSeq (NFData, rnf)-import Control.Exception.Safe-import Control.Monad-import Control.Monad.IO.Unlift-import Control.Monad.Reader-import Control.Monad.Trans.Resource hiding (throwM)-import Data.List-import Data.ByteString ( ByteString )-import Data.Bifunctor-import Data.Maybe-import Data.Versions-import GHC.IO.Exception ( IOErrorType(NoSuchThing) )-import Data.Variant.Excepts-import Optics hiding ( uncons )-import Safe-import System.Info-import System.Directory hiding ( removeDirectory- , removeDirectoryRecursive- , removePathForcibly- , findFiles- , makeAbsolute- )-import qualified System.Directory as SD--import System.Environment-import System.FilePath-import System.IO.Temp-import Text.Regex.Posix--import qualified Data.ByteString as BS-import qualified Data.Text as T-import qualified Data.Yaml.Aeson as Y-import qualified Text.Megaparsec as MP-import System.IO.Error (ioeGetErrorType)---- ---------------------------- --[ GHCupPath utilities ]--- ------------------------------- | A 'GHCupPath' is a safe sub-path that can be recursively deleted.------ The constructor is not exported.-newtype GHCupPath = GHCupPath FilePath- deriving (Show, Eq, Ord)--instance NFData GHCupPath where- rnf (GHCupPath fp) = rnf fp--appendGHCupPath :: GHCupPath -> FilePath -> GHCupPath-appendGHCupPath (GHCupPath gp) fp = GHCupPath (gp </> fp)--fromGHCupPath :: GHCupPath -> FilePath-fromGHCupPath (GHCupPath gp) = gp--createTempGHCupDirectory :: GHCupPath -> FilePath -> IO GHCupPath-createTempGHCupDirectory (GHCupPath gp) d = GHCupPath <$> createTempDirectory gp d---getGHCupTmpDirs :: IO [GHCupPath]-getGHCupTmpDirs = do- tmpdir <- fromGHCupPath <$> ghcupTMPDir- ghcup_dirs <- handleIO (\_ -> pure []) $ findFiles- tmpdir- (makeRegexOpts compExtended- execBlank- ([s|^ghcup-.*$|] :: ByteString)- )- pure (fmap (\p -> GHCupPath (tmpdir </> p)) $ filter (maybe False ("ghcup-" `isPrefixOf`) . lastMay . splitPath) ghcup_dirs)--- ------------------------------- --[ GHCup base directories ]--- ----------------------------------- | ~/.ghcup by default------ If 'GHCUP_USE_XDG_DIRS' is set (to anything),--- then uses 'XDG_DATA_HOME/ghcup' as per xdg spec.-ghcupBaseDir :: IO GHCupPath-ghcupBaseDir- | isWindows = do- bdir <- fromMaybe "C:\\" <$> lookupEnv "GHCUP_INSTALL_BASE_PREFIX"- pure (GHCupPath (bdir </> "ghcup"))- | otherwise = do- xdg <- useXDG- if xdg- then do- bdir <- lookupEnv "XDG_DATA_HOME" >>= \case- Just r -> pure r- Nothing -> do- home <- liftIO getHomeDirectory- pure (home </> ".local" </> "share")- pure (GHCupPath (bdir </> "ghcup"))- else do- bdir <- lookupEnv "GHCUP_INSTALL_BASE_PREFIX" >>= \case- Just r -> pure r- Nothing -> liftIO getHomeDirectory- pure (GHCupPath (bdir </> ".ghcup"))----- | ~/.ghcup by default------ If 'GHCUP_USE_XDG_DIRS' is set (to anything),--- then uses 'XDG_CONFIG_HOME/ghcup' as per xdg spec.-ghcupConfigDir :: IO GHCupPath-ghcupConfigDir- | isWindows = ghcupBaseDir- | otherwise = do- xdg <- useXDG- if xdg- then do- bdir <- lookupEnv "XDG_CONFIG_HOME" >>= \case- Just r -> pure r- Nothing -> do- home <- liftIO getHomeDirectory- pure (home </> ".config")- pure (GHCupPath (bdir </> "ghcup"))- else do- bdir <- lookupEnv "GHCUP_INSTALL_BASE_PREFIX" >>= \case- Just r -> pure r- Nothing -> liftIO getHomeDirectory- pure (GHCupPath (bdir </> ".ghcup"))----- | If 'GHCUP_USE_XDG_DIRS' is set (to anything),--- then uses 'XDG_BIN_HOME' env var or defaults to '~/.local/bin'--- (which, sadly is not strictly xdg spec).-ghcupBinDir :: IO FilePath-ghcupBinDir- | isWindows = (fromGHCupPath <$> ghcupBaseDir) <&> (</> "bin")- | otherwise = do- xdg <- useXDG- if xdg- then do- lookupEnv "XDG_BIN_HOME" >>= \case- Just r -> pure r- Nothing -> do- home <- liftIO getHomeDirectory- pure (home </> ".local" </> "bin")- else (fromGHCupPath <$> ghcupBaseDir) <&> (</> "bin")----- | Defaults to '~/.ghcup/cache'.------ If 'GHCUP_USE_XDG_DIRS' is set (to anything),--- then uses 'XDG_CACHE_HOME/ghcup' as per xdg spec.-ghcupCacheDir :: IO GHCupPath-ghcupCacheDir- | isWindows = ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "cache"))- | otherwise = do- xdg <- useXDG- if xdg- then do- bdir <- lookupEnv "XDG_CACHE_HOME" >>= \case- Just r -> pure r- Nothing -> do- home <- liftIO getHomeDirectory- pure (home </> ".cache")- pure (GHCupPath (bdir </> "ghcup" </> "cache"))- else ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "cache"))----- | Defaults to '~/.ghcup/logs'.------ If 'GHCUP_USE_XDG_DIRS' is set (to anything),--- then uses 'XDG_CACHE_HOME/ghcup/logs' as per xdg spec.-ghcupLogsDir :: IO GHCupPath-ghcupLogsDir- | isWindows = ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "logs"))- | otherwise = do- xdg <- useXDG- if xdg- then do- bdir <- lookupEnv "XDG_CACHE_HOME" >>= \case- Just r -> pure r- Nothing -> do- home <- liftIO getHomeDirectory- pure (home </> ".cache")- pure (GHCupPath (bdir </> "ghcup" </> "logs"))- else ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "logs"))----- | Defaults to '~/.ghcup/db.------ If 'GHCUP_USE_XDG_DIRS' is set (to anything),--- then uses 'XDG_CACHE_HOME/ghcup/db as per xdg spec.-ghcupDbDir :: IO GHCupPath-ghcupDbDir = ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "db"))----- | '~/.ghcup/trash'.--- Mainly used on windows to improve file removal operations-ghcupRecycleDir :: IO GHCupPath-ghcupRecycleDir = ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "trash"))----- | Defaults to '~/.ghcup/tmp.------ If 'GHCUP_USE_XDG_DIRS' is set (to anything),--- then uses 'XDG_CACHE_HOME/ghcup/tmp as per xdg spec.-ghcupTMPDir :: IO GHCupPath-ghcupTMPDir- | isWindows = ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "tmp"))- | otherwise = do- xdg <- useXDG- if xdg- then do- bdir <- lookupEnv "XDG_CACHE_HOME" >>= \case- Just r -> pure r- Nothing -> do- home <- liftIO getHomeDirectory- pure (home </> ".cache")- pure (GHCupPath (bdir </> "ghcup" </> "tmp"))- else ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "tmp"))---ghcupMsys2Dir :: IO FilePath-ghcupMsys2Dir =- lookupEnv "GHCUP_MSYS2" >>= \case- Just fp -> pure fp- Nothing -> do- baseDir <- liftIO ghcupBaseDir- pure (fromGHCupPath baseDir </> "msys64")--ghcupMsys2BinDirs :: (MonadFail m, MonadIO m, MonadReader env m, HasDirs env) => m [FilePath]-ghcupMsys2BinDirs = do- Dirs{..} <- getDirs- liftIO $ ghcupMsys2BinDirs_ msys2Dir--ghcupMsys2BinDirs' :: IO [FilePath]-ghcupMsys2BinDirs' = do- msys2Dir <- ghcupMsys2Dir- ghcupMsys2BinDirs_ msys2Dir--ghcupMsys2BinDirs_ :: FilePath -> IO [FilePath]-ghcupMsys2BinDirs_ msys2Dir' = do- env <- liftIO (lookupEnv "GHCUP_MSYS2_ENV") >>= \case- Just env -> maybe (fail parseFailMsg) pure $ readMay @MSYS2Env env- Nothing- | "x86_64" <- arch -> pure MINGW64- | "i386" <- arch -> pure MINGW32- | "aarch64" <- arch -> pure CLANGARM64- | otherwise -> fail "No compatible architecture for msys2"- pure [msys2Dir' </> toEnvDir env </> "bin", msys2Dir' </> toEnvDir MSYS </> "bin"]- where- -- https://www.msys2.org/docs/environments/- toEnvDir :: MSYS2Env -> FilePath- toEnvDir MSYS = "usr"- toEnvDir UCRT64 = "ucrt64"- toEnvDir CLANG64 = "clang64"- toEnvDir CLANGARM64 = "clangarm64"- toEnvDir CLANG32 = "clang32"- toEnvDir MINGW64 = "mingw64"- toEnvDir MINGW32 = "mingw32"-- parseFailMsg = "Invalid value for GHCUP_MSYS2_ENV. Valid values are: MSYS, UCRT64, CLANG64, CLANGARM64, CLANG32, MINGW64, MINGW32"---getAllDirs :: IO Dirs-getAllDirs = do- baseDir <- ghcupBaseDir- binDir <- ghcupBinDir- cacheDir <- ghcupCacheDir- logsDir <- ghcupLogsDir- confDir <- ghcupConfigDir- recycleDir <- ghcupRecycleDir- tmpDir <- ghcupTMPDir- dbDir <- ghcupDbDir- msys2Dir <- ghcupMsys2Dir- pure Dirs { .. }---- -------------------- --[ GHCup files ]--- ---------------------getConfigFilePath :: (MonadIO m) => m FilePath-getConfigFilePath = do- confDir <- liftIO ghcupConfigDir- pure $ fromGHCupPath confDir </> "config.yaml"--getConfigFilePath' :: (MonadReader env m, HasDirs env) => m FilePath-getConfigFilePath' = do- Dirs {..} <- getDirs- pure $ fromGHCupPath confDir </> "config.yaml"---ghcupConfigFile :: (MonadIO m)- => Excepts '[JSONError] m UserSettings-ghcupConfigFile = do- filepath <- getConfigFilePath- contents <- liftIO $ handleIO (\e -> if NoSuchThing == ioeGetErrorType e then pure Nothing else liftIO $ ioError e) $ Just <$> BS.readFile filepath- case contents of- Nothing -> pure defaultUserSettings- Just contents' -> liftE- . veitherToExcepts @_ @'[JSONError]- . either (VLeft . V) VRight- . first (JSONDecodeError . displayException)- . Y.decodeEither'- $ contents'--- -------------------------- --[ GHCup directories ]--- ------------------------------ | ~/.ghcup/ghc by default.-ghcupGHCBaseDir :: (MonadReader env m, HasDirs env) => m GHCupPath-ghcupGHCBaseDir = do- Dirs {..} <- getDirs- pure (baseDir `appendGHCupPath` "ghc")----- | Gets '~/.ghcup/ghc/<ghcupGHCDir>'.--- The dir may be of the form--- * armv7-unknown-linux-gnueabihf-8.8.3--- * 8.8.4-ghcupGHCDir :: (MonadReader env m, HasDirs env, MonadThrow m)- => GHCTargetVersion- -> m GHCupPath-ghcupGHCDir ver = do- ghcbasedir <- ghcupGHCBaseDir- let verdir = T.unpack $ tVerToText ver- pure (ghcbasedir `appendGHCupPath` verdir)----- | See 'ghcupToolParser'.-parseGHCupGHCDir :: MonadThrow m => FilePath -> m GHCTargetVersion-parseGHCupGHCDir (T.pack -> fp) =- throwEither $ MP.parse ghcTargetVerP "" fp--parseGHCupHLSDir :: MonadThrow m => FilePath -> m Version-parseGHCupHLSDir (T.pack -> fp) =- throwEither $ MP.parse version' "" fp---- TODO: inlined from GHCup.Prelude-throwEither :: (Exception a, MonadThrow m) => Either a b -> m b-throwEither a = case a of- Left e -> throwM e- Right r -> pure r---- | ~/.ghcup/hls by default, for new-style installs.-ghcupHLSBaseDir :: (MonadReader env m, HasDirs env) => m GHCupPath-ghcupHLSBaseDir = do- Dirs {..} <- getDirs- pure (baseDir `appendGHCupPath` "hls")---- | Gets '~/.ghcup/hls/<hls-ver>' for new-style installs.-ghcupHLSDir :: (MonadReader env m, HasDirs env, MonadThrow m)- => Version- -> m GHCupPath-ghcupHLSDir ver = do- basedir <- ghcupHLSBaseDir- let verdir = T.unpack $ prettyVer ver- pure (basedir `appendGHCupPath` verdir)---mkGhcupTmpDir :: ( MonadReader env m- , HasDirs env- , MonadUnliftIO m- , HasLog env- , MonadCatch m- , MonadThrow m- , MonadMask m- , MonadIO m)- => m GHCupPath-mkGhcupTmpDir = GHCupPath <$> do- Dirs { tmpDir } <- getDirs- liftIO $ createTempDirectory (fromGHCupPath tmpDir) "ghcup"---withGHCupTmpDir :: ( MonadReader env m- , HasDirs env- , HasLog env- , HasSettings env- , MonadUnliftIO m- , MonadCatch m- , MonadResource m- , MonadThrow m- , MonadMask m- , MonadIO m)- => m GHCupPath-withGHCupTmpDir = do- Settings{keepDirs} <- getSettings- snd <$> withRunInIO (\run ->- run- $ allocate- (run mkGhcupTmpDir)- (\fp -> if -- we don't know whether there was a failure, so can only- -- decide for 'Always'- | keepDirs == Always -> pure ()- | otherwise -> handleIO (\e -> run- $ logDebug ("Resource cleanup failed for "- <> T.pack (fromGHCupPath fp)- <> ", error was: "- <> T.pack (displayException e)))- . removePathForcibly- $ fp))----- --------------- --[ Others ]--- -----------------useXDG :: IO Bool-useXDG = isJust <$> lookupEnv "GHCUP_USE_XDG_DIRS"----- | Like 'relpath'. Assumes the inputs are resolved in case of symlinks.-relativeSymlink :: FilePath -- ^ the path in which to create the symlink- -> FilePath -- ^ the symlink destination- -> FilePath-relativeSymlink p1 p2- | isWindows = p2 -- windows quickly gets into MAX_PATH issues so we don't care about relative symlinks- | otherwise =- let d1 = splitDirectories p1- d2 = splitDirectories p2- common = takeWhile (\(x, y) -> x == y) $ zip d1 d2- cPrefix = drop (length common) d1- in joinPath (replicate (length cPrefix) "..")- <> joinPath ([pathSeparator] : drop (length common) d2)---cleanupTrash :: ( MonadIO m- , MonadMask m- , MonadReader env m- , HasLog env- , HasDirs env- , HasSettings env- )- => m ()-cleanupTrash = do- Dirs { recycleDir } <- getDirs- contents <- liftIO $ listDirectory (fromGHCupPath recycleDir)- if null contents- then pure ()- else do- logWarn ("Removing leftover files in " <> T.pack (fromGHCupPath recycleDir))- forM_ contents (\fp -> handleIO (\e ->- logDebug ("Resource cleanup failed for " <> T.pack fp <> ", error was: " <> T.pack (displayException e))- ) $ liftIO $ removePathForcibly (recycleDir `appendGHCupPath` fp))----- | List *actual files* in a directory, ignoring empty files and a couple--- of blacklisted files, such as '.DS_Store' on mac.-listDirectoryFiles :: FilePath -> IO [FilePath]-listDirectoryFiles fp = do- listDirectory fp >>= filterM (doesFileExist . (fp </>)) <&> filter (\fp' -> not (isHidden fp') && not (isBlacklisted fp'))---- | List *actual directories* in a directory, ignoring empty directories and a couple--- of blacklisted files, such as '.DS_Store' on mac.-listDirectoryDirs :: FilePath -> IO [FilePath]-listDirectoryDirs fp = do- listDirectory fp >>= filterM (doesDirectoryExist . (fp </>)) <&> filter (\fp' -> not (isHidden fp') && not (isBlacklisted fp'))--isHidden :: FilePath -> Bool-isHidden fp'- | isWindows = False- | Just ('.', _) <- uncons fp' = True- | otherwise = False--isBlacklisted :: FilePath -> Bool-{- HLINT ignore "Use ==" -}-isBlacklisted fp' = fp' `elem` [".DS_Store"]------ System.Directory re-exports with GHCupPath--removeDirectory :: GHCupPath -> IO ()-removeDirectory (GHCupPath fp) = SD.removeDirectory fp--removeDirectoryRecursive :: GHCupPath -> IO ()-removeDirectoryRecursive (GHCupPath fp) = SD.removeDirectoryRecursive fp--removePathForcibly :: GHCupPath -> IO ()-removePathForcibly (GHCupPath fp) = SD.removePathForcibly fp----
− lib/GHCup/Utils/Dirs.hs-boot
@@ -1,37 +0,0 @@-module GHCup.Utils.Dirs- ( GHCupPath- , appendGHCupPath- , fromGHCupPath- , createTempGHCupDirectory- , removeDirectory- , removeDirectoryRecursive- , removePathForcibly- )- where--import Control.DeepSeq (NFData)----- | A 'GHCupPath' is a safe sub-path that can be recursively deleted.-newtype GHCupPath = GHCupPath FilePath--instance Show GHCupPath where--instance Eq GHCupPath where--instance Ord GHCupPath where--instance NFData GHCupPath where--appendGHCupPath :: GHCupPath -> FilePath -> GHCupPath--fromGHCupPath :: GHCupPath -> FilePath--createTempGHCupDirectory :: GHCupPath -> FilePath -> IO GHCupPath--removeDirectory :: GHCupPath -> IO ()--removeDirectoryRecursive :: GHCupPath -> IO ()--removePathForcibly :: GHCupPath -> IO ()-
− lib/GHCup/Utils/Output.hs
@@ -1,116 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}--module GHCup.Utils.Output where--import Data.Void-import Data.Char-import Control.Applicative--import qualified Data.Text as T-import qualified Text.Megaparsec as MP-import qualified Text.Megaparsec.Char as MPC-import qualified System.Console.Terminal.Size as TP----- | Checks whether the given text lines fit in the terminal window.--- Returns 'Nothing' if the terminal size could not be determined (e.g. bc we're--- part of a pipe).-fitsInTerminal :: [T.Text] -> IO (Maybe Bool)-fitsInTerminal text = fmap (\(TP.Window h _) -> length text <= h - 2) <$> TP.size---- | Like 'fitsInTerminal', but takes a single text blob.-fitsInTerminal' :: T.Text -> IO (Maybe Bool)-fitsInTerminal' = fitsInTerminal . T.lines--padTo :: String -> Int -> String-padTo str x =- let lstr = strWidth str- add' = x - lstr- in if add' < 0 then str else str ++ replicate add' ' '----- | Calculate the render width of a string, considering--- wide characters (counted as double width), ANSI escape codes--- (not counted), and line breaks (in a multi-line string, the longest--- line determines the width).-strWidth :: String -> Int-strWidth =- maximum- . (0 :)- . map (foldr (\a b -> charWidth a + b) 0)- . lines- . stripAnsi---- | Strip ANSI escape sequences from a string.------ >>> stripAnsi "\ESC[31m-1\ESC[m"--- "-1"-stripAnsi :: String -> String-stripAnsi s' =- case- MP.parseMaybe (many $ "" <$ MP.try ansi <|> pure <$> MP.anySingle) s'- of- Nothing -> error "Bad ansi escape" -- PARTIAL: should not happen- Just xs -> concat xs- where- -- This parses lots of invalid ANSI escape codes, but that should be fine- ansi =- MPC.string "\ESC[" *> digitSemicolons *> suffix MP.<?> "ansi" :: MP.Parsec- Void- String- Char- digitSemicolons = MP.takeWhileP Nothing (\c -> isDigit c || c == ';')- suffix = MP.oneOf ['A', 'B', 'C', 'D', 'H', 'J', 'K', 'f', 'm', 's', 'u']---- | Get the designated render width of a character: 0 for a combining--- character, 1 for a regular character, 2 for a wide character.--- (Wide characters are rendered as exactly double width in apps and--- fonts that support it.) (From Pandoc.)-charWidth :: Char -> Int-charWidth c = case c of- _ | c < '\x0300' -> 1- | c >= '\x0300' && c <= '\x036F' -> 0- | -- combining- c >= '\x0370' && c <= '\x10FC' -> 1- | c >= '\x1100' && c <= '\x115F' -> 2- | c >= '\x1160' && c <= '\x11A2' -> 1- | c >= '\x11A3' && c <= '\x11A7' -> 2- | c >= '\x11A8' && c <= '\x11F9' -> 1- | c >= '\x11FA' && c <= '\x11FF' -> 2- | c >= '\x1200' && c <= '\x2328' -> 1- | c >= '\x2329' && c <= '\x232A' -> 2- | c >= '\x232B' && c <= '\x2E31' -> 1- | c >= '\x2E80' && c <= '\x303E' -> 2- | c == '\x303F' -> 1- | c >= '\x3041' && c <= '\x3247' -> 2- | c >= '\x3248' && c <= '\x324F' -> 1- | -- ambiguous- c >= '\x3250' && c <= '\x4DBF' -> 2- | c >= '\x4DC0' && c <= '\x4DFF' -> 1- | c >= '\x4E00' && c <= '\xA4C6' -> 2- | c >= '\xA4D0' && c <= '\xA95F' -> 1- | c >= '\xA960' && c <= '\xA97C' -> 2- | c >= '\xA980' && c <= '\xABF9' -> 1- | c >= '\xAC00' && c <= '\xD7FB' -> 2- | c >= '\xD800' && c <= '\xDFFF' -> 1- | c >= '\xE000' && c <= '\xF8FF' -> 1- | -- ambiguous- c >= '\xF900' && c <= '\xFAFF' -> 2- | c >= '\xFB00' && c <= '\xFDFD' -> 1- | c >= '\xFE00' && c <= '\xFE0F' -> 1- | -- ambiguous- c >= '\xFE10' && c <= '\xFE19' -> 2- | c >= '\xFE20' && c <= '\xFE26' -> 1- | c >= '\xFE30' && c <= '\xFE6B' -> 2- | c >= '\xFE70' && c <= '\xFEFF' -> 1- | c >= '\xFF01' && c <= '\xFF60' -> 2- | c >= '\xFF61' && c <= '\x16A38' -> 1- | c >= '\x1B000' && c <= '\x1B001' -> 2- | c >= '\x1D000' && c <= '\x1F1FF' -> 1- | c >= '\x1F200' && c <= '\x1F251' -> 2- | c >= '\x1F300' && c <= '\x1F773' -> 1- | c >= '\x20000' && c <= '\x3FFFD' -> 2- | otherwise -> 1
− lib/GHCup/Utils/Pager.hs
@@ -1,65 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}--module GHCup.Utils.Pager where--import System.Environment-import GHCup.Utils.Dirs (findExecutable)-import Data.Foldable (asum)-import System.Process-import System.Exit-import System.IO-import Data.Text (Text)-import qualified Data.Text.IO as T-import Control.Monad (forM_, (<=<))-import Control.Exception (IOException, try)-import GHCup.Utils.Output-import qualified Data.Text as T---getPager :: IO (Maybe FilePath)-getPager = do- lookupEnv "GHCUP_PAGER" >>= \case- Just r -> pure $ Just r- Nothing -> lookupEnv "PAGER" >>= \case- Just r' -> pure $ Just r'- Nothing ->- let pagers = ["most", "more", "less"]- in fmap (either (const Nothing) Just)- . try @IOException- . asum- . fmap (maybe (fail "could not find") pure <=< findExecutable)- $ pagers---- 'more' reads from STDERR, and requires std_err to be 'Inherit'-sendToPager :: FilePath -> [Text] -> IO (Either IOException ())-sendToPager pager text = try @IOException- $ withCreateProcess (shell pager) { std_in = CreatePipe- , std_err = Inherit- , std_out = Inherit- , delegate_ctlc = True- }- $ \(Just stdinH) _ _ ph -> do- forM_ text $ T.hPutStrLn stdinH- hClose stdinH- exitCode <- waitForProcess ph- case exitCode of- ExitFailure i -> fail ("Pager exited with exit code " <> show i)- _ -> pure ()---sendToPager' :: Maybe FilePath -> [Text] -> IO ()-sendToPager' (Just pager) text = do- fits <- fitsInTerminal text- case fits of- Just True -> do- T.putStr $ T.unlines text- _ -> sendToPager pager text >>= \case- Right _ -> pure ()- Left _ -> do- T.putStrLn $ T.unlines text-sendToPager' _ text =- forM_ text T.putStrLn-
− lib/GHCup/Utils/Parsers.hs
@@ -1,502 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE ViewPatterns #-}--module GHCup.Utils.Parsers where---import GHCup.Errors-import GHCup.Types-import GHCup.Types.Optics-import GHCup.List-import GHCup.Utils-import GHCup.Prelude-import GHCup.Prelude.Logger-import GHCup.Prelude.Attoparsec as AP-import GHCup.Prelude.MegaParsec as MP--import Control.Applicative ((<|>), Alternative(..))-import Control.Monad (forM, when)-import Control.Exception.Safe-#if !MIN_VERSION_base(4,13,0)-import Control.Monad.Fail ( MonadFail )-#endif-import Control.Monad.Reader-import Data.Aeson-import qualified Data.Attoparsec.ByteString as AP-#if MIN_VERSION_aeson(2,0,0)-#else-import qualified Data.HashMap.Strict as KM-#endif-import Data.Bifunctor-import Data.Char as C-import Data.Either-import Data.Functor-import Data.List ( sort, sortBy )-import Data.Maybe-import Data.Text ( Text )-import Data.Time.Calendar ( Day )-import Data.Time.Format ( parseTimeM, defaultTimeLocale )-import Data.Versions-import Data.Void-import Data.Variant.Excepts-import Optics hiding ( set )-import Prelude hiding ( appendFile )-import Safe-import System.FilePath-import URI.ByteString hiding (parseURI)--import qualified Data.ByteString.UTF8 as UTF8-import qualified Data.Text as T-import qualified Data.Text.Lazy.Encoding as LE-import qualified Data.Text.Lazy as LT-import qualified Text.Megaparsec as MP-import GHCup.Version---- $setup--- >>> :set -XOverloadedStrings--- >>> :set -XDataKinds--- >>> :set -XTypeApplications--- >>> :set -XQuasiQuotes--- >>> import System.Directory--- >>> import URI.ByteString--- >>> import qualified Data.Text as T--- >>> import GHCup.Prelude--- >>> import GHCup.Download--- >>> import GHCup.Version--- >>> import GHCup.Errors--- >>> import GHCup.Types--- >>> import GHCup.Utils.Dirs--- >>> import GHCup.Types.Optics--- >>> import Data.Versions--- >>> import Optics--- >>> import GHCup.Prelude.Version.QQ--- >>> import qualified Data.Text.Encoding as E--- >>> import qualified Data.Map.Strict as M--- >>> import Control.Monad.Reader--- >>> import Data.Variant.Excepts--- >>> import Text.PrettyPrint.HughesPJClass ( prettyShow )--- >>> let lc = LoggerConfig { lcPrintDebug = False, consoleOutter = mempty, fileOutter = mempty, fancyColors = False }--- >>> dirs' <- getAllDirs--- >>> let installedVersions = [ ([pver|8.10.7|], "-debug+lol", Nothing), ([pver|8.10.4|], "", Nothing), ([pver|8.8.4|], "", Nothing), ([pver|8.8.3|], "", Nothing) ]--- >>> let settings = defaultSettings { cache = True, metaCache = 0, noNetwork = True }--- >>> let leanAppState = LeanAppState settings dirs' defaultKeyBindings lc--- >>> cwd <- getCurrentDirectory--- >>> (Right ref) <- pure $ GHCup.Utils.parseURI $ "file://" <> E.encodeUtf8 (T.pack cwd) <> "/data/metadata/" <> (urlBaseName . view pathL' $ ghcupURL)--- >>> (Right ref') <- pure $ GHCup.Utils.parseURI $ "file://" <> E.encodeUtf8 (T.pack cwd) <> "/data/metadata/" <> (urlBaseName . view pathL' $ channelURL PrereleasesChannel)--- >>> (VRight r) <- (fmap . fmap) _ghcupDownloads $ flip runReaderT leanAppState . runE @'[DigestError, GPGError, JSONError , DownloadFailed , FileDoesNotExistError, ContentLengthError] $ liftE (getBase ref) >>= liftE . decodeMetadata @GHCupInfo--- >>> (VRight r') <- (fmap . fmap) _ghcupDownloads $ flip runReaderT leanAppState . runE @'[DigestError, GPGError, JSONError , DownloadFailed , FileDoesNotExistError, ContentLengthError] $ liftE (getBase ref') >>= liftE . decodeMetadata @GHCupInfo--- >>> let rr = M.unionsWith (M.unionWith (\_ b2 -> b2)) [r, r']--- >>> let go = flip runReaderT leanAppState . fmap (tVerToText . fst)--- -------------- --[ Types ]--- ----------------- a superset of ToolVersion-data SetToolVersion = SetGHCVersion GHCTargetVersion- | SetToolVersion Version- | SetToolTag Tag- | SetToolDay Day- | SetRecommended- | SetNext- deriving (Eq, Show)--prettyToolVer :: ToolVersion -> String-prettyToolVer (GHCVersion v') = T.unpack $ tVerToText v'-prettyToolVer (ToolVersion v') = T.unpack $ prettyVer v'-prettyToolVer (ToolTag t) = show t-prettyToolVer (ToolDay day) = show day--toSetToolVer :: Maybe ToolVersion -> SetToolVersion-toSetToolVer (Just (GHCVersion v')) = SetGHCVersion v'-toSetToolVer (Just (ToolVersion v')) = SetToolVersion v'-toSetToolVer (Just (ToolTag t')) = SetToolTag t'-toSetToolVer (Just (ToolDay d')) = SetToolDay d'-toSetToolVer Nothing = SetRecommended---platformParser :: String -> Either String PlatformRequest-platformParser s' = case MP.parse (platformP <* MP.eof) "" (T.pack s') of- Right r -> pure r- Left e -> Left $ errorBundlePretty e- where- archP :: MP.Parsec Void Text Architecture- archP = choice' ((\x -> MP.chunk (T.pack $ archToString x) $> x) <$> ([minBound..maxBound] :: [Architecture]))- platformP :: MP.Parsec Void Text PlatformRequest- platformP = choice'- [ (`PlatformRequest` FreeBSD)- <$> (archP <* MP.chunk "-")- <*> ( MP.chunk "portbld"- *> ( MP.try (Just <$> verP (MP.chunk "-freebsd" <* MP.eof))- <|> pure Nothing- )- <* MP.chunk "-freebsd"- )- , (`PlatformRequest` Darwin)- <$> (archP <* MP.chunk "-")- <*> ( MP.chunk "apple"- *> ( MP.try (Just <$> verP (MP.chunk "-darwin" <* MP.eof))- <|> pure Nothing- )- <* MP.chunk "-darwin"- )- , (\a d mv -> PlatformRequest a (Linux d) mv)- <$> (archP <* MP.chunk "-")- <*> distroP- <*> ((MP.try (Just <$> verP (MP.chunk "-linux" <* MP.eof)) <|> pure Nothing- )- <* MP.chunk "-linux"- )- , (\a -> PlatformRequest a Windows Nothing)- <$> ((archP <* MP.chunk "-")- <* (MP.chunk "unknown-mingw32" <|> MP.chunk "unknown-windows" <|> MP.chunk "windows"))- ]- distroP :: MP.Parsec Void Text LinuxDistro- distroP = choice' ((\d -> MP.chunk (T.pack $ distroToString d) $> d) <$> allDistros)---uriParser :: String -> Either String URI-uriParser = first show . parseURI . UTF8.fromString---absolutePathParser :: FilePath -> Either String FilePath-absolutePathParser f = case isValid f && isAbsolute f of- True -> Right $ normalise f- False -> Left "Please enter a valid absolute filepath."--isolateParser :: FilePath -> Either String FilePath-isolateParser f = case isValid f && isAbsolute f of- True -> Right $ normalise f- False -> Left "Please enter a valid filepath for isolate dir."---- this accepts cross prefix-ghcVersionTagEither :: String -> Either String ToolVersion-ghcVersionTagEither s' =- second ToolDay (dayParser s') <|> second ToolTag (tagEither s') <|> second GHCVersion (ghcVersionEither s')---- this ignores cross prefix-toolVersionTagEither :: String -> Either String ToolVersion-toolVersionTagEither s' =- second ToolDay (dayParser s') <|> second ToolTag (tagEither s') <|> second ToolVersion (toolVersionEither s')--tagEither :: String -> Either String Tag-tagEither s' = case fmap toLower s' of- "recommended" -> Right Recommended- "latest" -> Right Latest- "latest-prerelease" -> Right LatestPrerelease- "latest-nightly" -> Right LatestNightly- ('b':'a':'s':'e':'-':ver') -> case pvp (T.pack ver') of- Right x -> Right (Base x)- Left _ -> Left $ "Invalid PVP version for base " <> ver'- other -> Left $ "Unknown tag " <> other---ghcVersionEither :: String -> Either String GHCTargetVersion-ghcVersionEither =- first (const "Not a valid version") . MP.parse ghcTargetVerP "" . T.pack--toolVersionEither :: String -> Either String Version-toolVersionEither =- first (const "Not a valid version") . MP.parse (version' <* MP.eof) "" . T.pack---toolParser :: String -> Either String Tool-toolParser s' | t == T.pack "ghc" = Right GHC- | t == T.pack "cabal" = Right Cabal- | t == T.pack "hls" = Right HLS- | t == T.pack "stack" = Right Stack- | otherwise = Left ("Unknown tool: " <> s')- where t = T.toLower (T.pack s')--dayParser :: String -> Either String Day-dayParser s = maybe (Left $ "Could not parse \"" <> s <> "\". Expected format is: YYYY-MM-DD") Right- $ parseTimeM True defaultTimeLocale "%Y-%-m-%-d" s---criteriaParser :: String -> Either String ListCriteria-criteriaParser s' | t == T.pack "installed" = Right $ ListInstalled True- | t == T.pack "set" = Right $ ListSet True- | t == T.pack "available" = Right $ ListAvailable True- | t == T.pack "+installed" = Right $ ListInstalled True- | t == T.pack "+set" = Right $ ListSet True- | t == T.pack "+available" = Right $ ListAvailable True- | t == T.pack "-installed" = Right $ ListInstalled False- | t == T.pack "-set" = Right $ ListSet False- | t == T.pack "-available" = Right $ ListAvailable False- | otherwise = Left ("Unknown criteria: " <> s')- where t = T.toLower (T.pack s')----keepOnParser :: String -> Either String KeepDirs-keepOnParser s' | t == T.pack "always" = Right Always- | t == T.pack "errors" = Right Errors- | t == T.pack "never" = Right Never- | otherwise = Left ("Unknown keep value: " <> s')- where t = T.toLower (T.pack s')---downloaderParser :: String -> Either String Downloader-downloaderParser s' | t == T.pack "curl" = Right Curl- | t == T.pack "wget" = Right Wget-#if defined(INTERNAL_DOWNLOADER)- | t == T.pack "internal" = Right Internal-#endif- | otherwise = Left ("Unknown downloader value: " <> s')- where t = T.toLower (T.pack s')--gpgParser :: String -> Either String GPGSetting-gpgParser s' | t == T.pack "strict" = Right GPGStrict- | t == T.pack "lax" = Right GPGLax- | t == T.pack "none" = Right GPGNone- | otherwise = Left ("Unknown gpg setting value: " <> s')- where t = T.toLower (T.pack s')----overWriteVersionParser :: String -> Either String [VersionPattern]-overWriteVersionParser = first (const "Not a valid version pattern") . MP.parse (MP.many versionPattern <* MP.eof) "" . T.pack- where- versionPattern :: MP.Parsec Void Text VersionPattern- versionPattern = do- str' <- T.unpack <$> MP.takeWhileP Nothing (/= '%')- if str' /= mempty- then pure (S str')- else fmap (const CabalVer) v_cabal- <|> fmap (const GitBranchName) b_name- <|> fmap (const GitHashShort) s_hash- <|> fmap (const GitHashLong) l_hash- <|> fmap (const GitDescribe) g_desc- <|> ((\a b -> S (a : T.unpack b)) <$> MP.satisfy (const True) <*> MP.takeWhileP Nothing (== '%')) -- invalid pattern, e.g. "%k"- where- v_cabal = MP.chunk "%v"- b_name = MP.chunk "%b"- s_hash = MP.chunk "%h"- l_hash = MP.chunk "%H"- g_desc = MP.chunk "%g"-- ------------------ --[ Utilities ]--- --------------------fromVersion :: ( HasLog env- , MonadFail m- , MonadReader env m- , HasGHCupInfo env- , HasDirs env- , MonadThrow m- , MonadIO m- , MonadCatch m- )- => Maybe ToolVersion- -> GuessMode- -> Tool- -> Excepts- '[ TagNotFound- , DayNotFound- , NextVerNotFound- , NoToolVersionSet- ] m (GHCTargetVersion, Maybe VersionInfo)-fromVersion tv = fromVersion' (toSetToolVer tv)--fromVersion' :: ( HasLog env- , MonadFail m- , MonadReader env m- , HasGHCupInfo env- , HasDirs env- , MonadThrow m- , MonadIO m- , MonadCatch m- )- => SetToolVersion- -> GuessMode- -> Tool- -> Excepts- '[ TagNotFound- , DayNotFound- , NextVerNotFound- , NoToolVersionSet- ] m (GHCTargetVersion, Maybe VersionInfo)-fromVersion' SetRecommended _ tool = do- GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo- second Just <$> getRecommended dls tool- ?? TagNotFound Recommended tool-fromVersion' (SetGHCVersion v) guessMode tool = do- GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo- lift $ guessFullVersion dls v tool guessMode-fromVersion' (SetToolVersion (mkTVer -> v)) guessMode tool = do- GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo- lift $ guessFullVersion dls v tool guessMode-fromVersion' (SetToolTag Latest) _ tool = do- GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo- second Just <$> getLatest dls tool ?? TagNotFound Latest tool-fromVersion' (SetToolDay day) _ tool = do- GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo- second Just <$> case getByReleaseDay dls tool day of- Left ad -> throwE $ DayNotFound day tool ad- Right v -> pure v-fromVersion' (SetToolTag LatestPrerelease) _ tool = do- GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo- second Just <$> getLatestPrerelease dls tool ?? TagNotFound LatestPrerelease tool-fromVersion' (SetToolTag LatestNightly) _ tool = do- GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo- second Just <$> getLatestNightly dls tool ?? TagNotFound LatestNightly tool-fromVersion' (SetToolTag Recommended) _ tool = do- GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo- second Just <$> getRecommended dls tool ?? TagNotFound Recommended tool-fromVersion' (SetToolTag (Base pvp'')) _ GHC = do- GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo- second Just <$> getLatestBaseVersion dls pvp'' ?? TagNotFound (Base pvp'') GHC-fromVersion' SetNext _ tool = do- GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo- next <- case tool of- GHC -> do- set <- fmap _tvVersion $ ghcSet Nothing !? NoToolVersionSet tool- ghcs <- rights <$> lift getInstalledGHCs- (headMay- . tail- . dropWhile (\GHCTargetVersion {..} -> _tvVersion /= set)- . cycle- . sortBy (\x y -> compare (_tvVersion x) (_tvVersion y))- . filter (\GHCTargetVersion {..} -> isNothing _tvTarget)- $ ghcs) ?? NoToolVersionSet tool- Cabal -> do- set <- cabalSet !? NoToolVersionSet tool- cabals <- rights <$> lift getInstalledCabals- (fmap (GHCTargetVersion Nothing)- . headMay- . tail- . dropWhile (/= set)- . cycle- . sort- $ cabals) ?? NoToolVersionSet tool- HLS -> do- set <- hlsSet !? NoToolVersionSet tool- hlses <- rights <$> lift getInstalledHLSs- (fmap (GHCTargetVersion Nothing)- . headMay- . tail- . dropWhile (/= set)- . cycle- . sort- $ hlses) ?? NoToolVersionSet tool- Stack -> do- set <- stackSet !? NoToolVersionSet tool- stacks <- rights <$> lift getInstalledStacks- (fmap (GHCTargetVersion Nothing)- . headMay- . tail- . dropWhile (/= set)- . cycle- . sort- $ stacks) ?? NoToolVersionSet tool- GHCup -> fail "GHCup cannot be set"- let vi = getVersionInfo next tool dls- pure (next, vi)-fromVersion' (SetToolTag t') _ tool =- throwE $ TagNotFound t' tool---- | Guess the full version from an input version, by possibly--- examining the metadata and the installed versions.------ >>> go $ guessFullVersion rr (mkTVer [vver|8|]) GHC GLax--- "8.10.7"--- >>> go $ guessFullVersion rr (mkTVer [vver|8.10|]) GHC GLax--- "8.10.7"--- >>> go $ guessFullVersion rr (mkTVer [vver|8.10.7|]) GHC GLax--- "8.10.7"--- >>> go $ guessFullVersion rr (mkTVer [vver|9.12.1|]) GHC GLax--- "9.12.1"--- >>> go $ guessFullVersion rr (mkTVer [vver|8|]) GHC GStrict--- "8"-guessFullVersion :: ( HasLog env- , MonadFail m- , MonadReader env m- , HasDirs env- , MonadThrow m- , MonadIO m- , MonadCatch m- )- => GHCupDownloads- -> GHCTargetVersion- -> Tool- -> GuessMode- -> m (GHCTargetVersion, Maybe VersionInfo)-guessFullVersion dls v tool guessMode = do- let vi = getVersionInfo v tool dls- case pvp $ prettyVer (_tvVersion v) of -- need to be strict here- Left _ -> pure (v, vi)- Right pvpIn- | (guessMode /= GStrict) && hasn't (ix tool % ix v) dls -> do- ghcs <- if guessMode == GLaxWithInstalled then fmap rights getInstalledTools else pure []- if v `notElem` ghcs- then getLatestToolFor tool (_tvTarget v) pvpIn dls >>= \case- Just (pvp_, vi', mt) -> do- v' <- pvpToVersion pvp_ ""- when (v' /= _tvVersion v) $ logWarn ("Assuming you meant version " <> prettyVer v')- pure (GHCTargetVersion mt v', Just vi')- Nothing -> pure (v, vi)- else pure (v, vi)- _ -> pure (v, vi)- where- getInstalledTools = case tool of- GHC -> getInstalledGHCs- Cabal -> (fmap . fmap) mkTVer <$> getInstalledCabals- HLS -> (fmap . fmap) mkTVer <$> getInstalledHLSs- Stack -> (fmap . fmap) mkTVer <$> getInstalledStacks- GHCup -> pure []---parseUrlSource :: String -> Either String [NewURLSource]-parseUrlSource s = (fromURLSource <$> parseUrlSource' s)- <|> ((:[]) <$> parseNewUrlSource s)- <|> parseNewUrlSources s--parseUrlSource' :: String -> Either String URLSource-parseUrlSource' "GHCupURL" = pure GHCupURL-parseUrlSource' "StackSetupURL" = pure StackSetupURL-parseUrlSource' s' = (eitherDecode . LE.encodeUtf8 . LT.pack $ s')- <|> (fmap (OwnSource . (:[]) . Right) . first show . parseURI . UTF8.fromString $ s')--parseNewUrlSource :: String -> Either String NewURLSource-parseNewUrlSource "GHCupURL" = pure NewGHCupURL-parseNewUrlSource "StackSetupURL" = pure NewStackSetupURL-parseNewUrlSource s' = (fmap NewChannelAlias . parseChannelAlias $ s')- <|> (eitherDecode . LE.encodeUtf8 . LT.pack $ s')- <|> (fmap NewURI . first show . parseURI . UTF8.fromString $ s')--parseNewUrlSources :: String -> Either String [NewURLSource]-parseNewUrlSources s = case AP.parseOnly- (AP.parseList' <* AP.skipSpaces <* AP.endOfInput)- (UTF8.fromString s) of- Right bs ->- forM bs $ \b -> AP.parseOnly (parse <* AP.skipSpaces <* AP.endOfInput) b- Left e -> Left e- where- parse :: AP.Parser NewURLSource- parse = (NewGHCupURL <$ AP.string "GHCupURL")- <|> (NewStackSetupURL <$ AP.string "StackSetupURL")- <|> AP.choice ((\x -> AP.string (UTF8.fromString . T.unpack . channelAliasText $ x) $> NewChannelAlias x) <$> ([minBound..maxBound] :: [ChannelAlias]))- <|> (NewURI <$> parseURIP)--parseChannelAlias :: String -> Either String ChannelAlias-parseChannelAlias s =- let aliases = map (\c -> (T.unpack (channelAliasText c), c)) [minBound..maxBound]- in case lookup s aliases of- Just c -> Right c- Nothing -> Left $ "Unexpected ChannelAlias: " <> s--#if MIN_VERSION_transformers(0,6,0)-instance Alternative (Either [a]) where- empty = Left []- Left _ <|> n = n- m <|> _ = m-#endif-
− lib/GHCup/Utils/Tar.hs
@@ -1,141 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-}--{-|-Module : GHCup.Utils.Tar-Description : GHCup tar abstractions-Copyright : (c) Julian Ospald, 2024-License : LGPL-3.0-Maintainer : hasufell@hasufell.de-Stability : experimental-Portability : portable--}-module GHCup.Utils.Tar where--import GHCup.Utils.Tar.Types ( ArchiveResult(..) )-import GHCup.Errors-import GHCup.Prelude-import GHCup.Prelude.Logger.Internal-import GHCup.Types.Optics--import Control.Monad.Catch (MonadThrow)-import Control.Monad.Reader-import Data.List-import Data.Variant.Excepts-import System.FilePath--#if defined(TAR)-import Codec.Archive.Zip-import qualified Codec.Archive.Tar as Tar-import qualified Codec.Archive.Tar.Entry as Tar-import qualified Data.Map.Strict as Map-#else-import Codec.Archive hiding ( Directory- , ArchiveResult -- imported from "GHCup.Utils.Tar.Types"- )-#endif--import qualified Codec.Compression.BZip as BZip-import qualified Codec.Compression.GZip as GZip-import qualified Codec.Compression.Lzma as Lzma-import qualified Data.ByteString.Lazy as BL-import qualified Data.Text as T----- | Unpack an archive to a given directory.-unpackToDir :: (MonadReader env m, HasLog env, MonadIO m, MonadThrow m)- => FilePath -- ^ destination dir- -> FilePath -- ^ archive path- -> Excepts '[UnknownArchive- , ArchiveResult- ] m ()-unpackToDir dfp av = do- let fn = takeFileName av- lift $ logInfo $ "Unpacking: " <> T.pack fn <> " to " <> T.pack dfp--#if defined(TAR)- let untar :: MonadIO m => BL.ByteString -> Excepts '[ArchiveResult] m ()- untar = liftIO . Tar.unpack dfp . Tar.read-- rf :: MonadIO m => FilePath -> Excepts '[ArchiveResult] m BL.ByteString- rf = liftIO . BL.readFile-#else- let untar :: MonadIO m => BL.ByteString -> Excepts '[ArchiveResult] m ()- untar = lEM . liftIO . runArchiveM . unpackToDirLazy dfp-- rf :: MonadIO m => FilePath -> Excepts '[ArchiveResult] m BL.ByteString- rf = liftIO . BL.readFile-#endif-- -- extract, depending on file extension- if- | ".tar.gz" `isSuffixOf` fn -> liftE- (untar . GZip.decompress =<< rf av)- | ".tar.xz" `isSuffixOf` fn -> do- filecontents <- liftE $ rf av- let decompressed = Lzma.decompressWith (Lzma.defaultDecompressParams { Lzma.decompressAutoDecoder= True }) filecontents- liftE $ untar decompressed- | ".tar.bz2" `isSuffixOf` fn ->- liftE (untar . BZip.decompress =<< rf av)- | ".tar" `isSuffixOf` fn -> liftE (untar =<< rf av)-#if defined(TAR)- | ".zip" `isSuffixOf` fn -> withArchive av (unpackInto dfp)-#else- -- libarchive supports zip- | ".zip" `isSuffixOf` fn -> liftE (untar =<< rf av)-#endif- | otherwise -> throwE $ UnknownArchive fn----- | Get all files from an archive.-getArchiveFiles :: (MonadReader env m, HasLog env, MonadIO m, MonadThrow m)- => FilePath -- ^ archive path- -> Excepts '[ UnknownArchive- , ArchiveResult- ] m [FilePath]-getArchiveFiles av = do- let fn = takeFileName av-#if defined(TAR)- let entries :: Monad m => BL.ByteString -> Excepts '[ArchiveResult] m [FilePath]- entries =- lE @ArchiveResult- . Tar.foldEntries- (\e x -> fmap (Tar.entryTarPath e :) x)- (Right [])- (\_ -> Left ArchiveFailed)- . Tar.decodeLongNames- . Tar.read-- rf :: MonadIO m => FilePath -> Excepts '[ArchiveResult] m BL.ByteString- rf = liftIO . BL.readFile-#else- let entries :: Monad m => BL.ByteString -> Excepts '[ArchiveResult] m [FilePath]- entries = (fmap . fmap) filepath . lE . readArchiveBSL-- rf :: MonadIO m => FilePath -> Excepts '[ArchiveResult] m BL.ByteString- rf = liftIO . BL.readFile-#endif-- -- extract, depending on file extension- if- | ".tar.gz" `isSuffixOf` fn -> liftE- (entries . GZip.decompress =<< rf av)- | ".tar.xz" `isSuffixOf` fn -> do- filecontents <- liftE $ rf av- let decompressed = Lzma.decompressWith (Lzma.defaultDecompressParams { Lzma.decompressAutoDecoder= True }) filecontents- liftE $ entries decompressed- | ".tar.bz2" `isSuffixOf` fn ->- liftE (entries . BZip.decompress =<< rf av)- | ".tar" `isSuffixOf` fn -> liftE (entries =<< rf av)- | ".zip" `isSuffixOf` fn ->-#if defined(TAR)- withArchive av $ do- entries' <- getEntries- pure $ fmap unEntrySelector $ Map.keys entries'-#else- liftE (entries =<< rf av)-#endif- | otherwise -> throwE $ UnknownArchive fn-
− lib/GHCup/Utils/Tar/Types.hs
@@ -1,33 +0,0 @@-{-# LANGUAGE CPP #-}-#if defined(TAR)-{-# LANGUAGE DeriveGeneric #-}-#endif--module GHCup.Utils.Tar.Types- ( ArchiveResult(..)- )- where--#if defined(TAR)--import Control.Exception ( Exception )-import Control.DeepSeq ( NFData )-import qualified GHC.Generics as GHC--data ArchiveResult = ArchiveFatal- | ArchiveFailed- | ArchiveWarn- | ArchiveRetry- | ArchiveOk- | ArchiveEOF- deriving (Eq, Show, GHC.Generic)--instance NFData ArchiveResult--instance Exception ArchiveResult--#else--import Codec.Archive ( ArchiveResult(..) )--#endif
− lib/GHCup/Utils/URI.hs
@@ -1,61 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE OverloadedStrings #-}--{-|-Module : GHCup.Utils.URI-Description : GHCup domain specific URI utilities-Copyright : (c) Julian Ospald, 2024-License : LGPL-3.0-Maintainer : hasufell@hasufell.de-Stability : experimental-Portability : portable--This module contains GHCup helpers specific to-URI handling.--}-module GHCup.Utils.URI where--import GHCup.Prelude.Internal--import Data.Bifunctor (first)-import Data.Text ( Text )-import Control.Applicative-import Data.Attoparsec.ByteString-import Data.ByteString-import URI.ByteString hiding (parseURI)-import System.URI.File-import qualified Data.Text.Encoding as E---- ------------ --[ URI ]--- --------------parseURI :: ByteString -> Either URIParseError (URIRef Absolute)-parseURI = first OtherError . parseOnly parseURIP--parseURI' :: Text -> Either URIParseError (URIRef Absolute)-parseURI' = first OtherError . parseOnly parseURIP . E.encodeUtf8--parseURIP :: Parser (URIRef Absolute)-parseURIP = do- ref <- (Right <$> parseFile) <|> (Left <$> uriParser laxURIParserOptions)- case ref of- Left (URI { uriScheme = (Scheme "file") }) ->-#if defined(IS_WINDOWS)- fail "Invalid file URI. File URIs must be absolute (start with a drive letter or UNC path) and not contain backslashes."-#else- fail "Invalid file URI. File URIs must be absolute."-#endif- Left o -> pure o- Right (FileURI (Just _) _) -> fail "File URIs with auth part are not supported!"- Right (FileURI _ fp) -> pure $ URI (Scheme "file") Nothing fp (Query []) Nothing- where- parseFile-#if defined(IS_WINDOWS)- = fileURIExtendedWindowsP-#else- = fileURIExtendedPosixP-#endif
− lib/GHCup/Version.hs
@@ -1,96 +0,0 @@-{-# LANGUAGE QuasiQuotes #-}---{-|-Module : GHCup.Version-Description : Version information and version handling.-Copyright : (c) Julian Ospald, 2020-License : LGPL-3.0-Maintainer : hasufell@hasufell.de-Stability : experimental-Portability : portable--}-module GHCup.Version where--import GHCup.Types-import Paths_ghcup (version)--import Data.Version (Version(versionBranch))-import URI.ByteString-import URI.ByteString.QQ--import qualified Data.List.NonEmpty as NE-import qualified Data.Text as T-import qualified Data.Versions as V-import Control.Exception.Safe (MonadThrow)-import Data.Text (Text)-import Control.Monad.Catch (throwM)-import GHCup.Errors (ParseError(..))-import Text.Megaparsec-import Data.Void (Void)---- | This reflects the API version of the YAML.------ Note that when updating this, CI requires that the file exists AND the same file exists at--- 'https://www.haskell.org/ghcup/exp/ghcup-<ver>.yaml' with some newlines added.-ghcupURL :: URI-ghcupURL = [uri|https://raw.githubusercontent.com/haskell/ghcup-metadata/master/ghcup-0.0.9.yaml|]--stackSetupURL :: URI-stackSetupURL = [uri|https://raw.githubusercontent.com/commercialhaskell/stackage-content/master/stack/stack-setup-2.yaml|]--shimGenURL :: URI-shimGenURL = [uri|https://downloads.haskell.org/~ghcup/shimgen/shim-2.exe|]--shimGenSHA :: T.Text-shimGenSHA = T.pack "7c55e201f71860c5babea886007c8fa44b861abf50d1c07e5677eb0bda387a70"---- | The current ghcup version.-ghcUpVer :: V.PVP-ghcUpVer = V.PVP . NE.fromList . fmap fromIntegral $ versionBranch version---- | ghcup version as numeric string.-numericVer :: String-numericVer = T.unpack . V.prettyPVP $ ghcUpVer--versionCmp :: V.Versioning -> VersionCmp -> Bool-versionCmp ver1 (VR_gt ver2) = ver1 > ver2-versionCmp ver1 (VR_gteq ver2) = ver1 >= ver2-versionCmp ver1 (VR_lt ver2) = ver1 < ver2-versionCmp ver1 (VR_lteq ver2) = ver1 <= ver2-versionCmp ver1 (VR_eq ver2) = ver1 == ver2--versionRange :: V.Versioning -> VersionRange -> Bool-versionRange ver' (SimpleRange cmps) = all (versionCmp ver') cmps-versionRange ver' (OrRange cmps range) =- versionRange ver' (SimpleRange cmps) || versionRange ver' range--pvpToVersion :: MonadThrow m => V.PVP -> Text -> m V.Version-pvpToVersion pvp_ rest =- either (\_ -> throwM $ ParseError "Couldn't convert PVP to Version") pure . V.version . (<> rest) . V.prettyPVP $ pvp_---- | Convert a version to a PVP and unparsable rest.------ -- prop> \v -> let (Just (pvp', r)) = versionToPVP v in pvpToVersion pvp' r === Just v-versionToPVP :: MonadThrow m => V.Version -> m (V.PVP, Text)-versionToPVP (V.Version (Just _) _ _ _) = throwM $ ParseError "Unexpected epoch"-versionToPVP v = case parse pvp'' "Version->PVP" $ V.prettyVer v of- Left _ -> throwM $ ParseError "Couldn't convert Version to PVP"- Right r -> pure r- where- pvp'' :: Parsec Void T.Text (V.PVP, T.Text)- pvp'' = do- p <- V.pvp'- s <- getParserState- pure (p, stateInput s)--pvpFromList :: [Int] -> V.PVP-pvpFromList = V.PVP . NE.fromList . fmap fromIntegral--channelURL :: ChannelAlias -> URI-channelURL = \case- DefaultChannel -> ghcupURL- StackChannel -> stackSetupURL- CrossChannel -> [uri|https://raw.githubusercontent.com/haskell/ghcup-metadata/master/ghcup-cross-0.0.9.yaml|]- PrereleasesChannel -> [uri|https://raw.githubusercontent.com/haskell/ghcup-metadata/master/ghcup-prereleases-0.0.9.yaml|]- VanillaChannel -> [uri|https://raw.githubusercontent.com/haskell/ghcup-metadata/master/ghcup-vanilla-0.0.9.yaml|]
+ lib/GHCup/Warnings.hs view
@@ -0,0 +1,40 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}++module GHCup.Warnings where++import GHCup.Prelude+import GHCup.Types+import GHCup.Types.Optics++import Control.Monad.Reader ( MonadReader )+import Data.Versions ( Version )+import Text.PrettyPrint.HughesPJClass ( prettyShow )++import qualified Data.Text as T+++warnAboutHlsCompatibility ::+ ( MonadReader env m+ , HasLog env+ , MonadIOish m+ )+ => Maybe Version+ -> Maybe Version+ -> [Version]+ -> m ()+warnAboutHlsCompatibility currentHLS currentGHC supportedGHC = do+ case (currentGHC, currentHLS) of+ (Just gv, Just hv) | gv `notElem` supportedGHC -> do+ logWarn $+ "GHC-" <> T.pack (prettyShow gv) <> " appears to have no corresponding HLS-" <> T.pack (prettyShow hv) <> " binary." <> "\n" <>+ "Haskell IDE support may not work." <> "\n" <>+ "You can try to either: " <> "\n" <>+ " 1. Install a different HLS version (e.g. downgrade for older GHCs)" <> "\n" <>+ " 2. Install and set one of the following GHCs: " <> T.pack (prettyShow supportedGHC) <> "\n" <>+ " 3. Let GHCup compile HLS for you, e.g. run: ghcup compile hls -g " <> T.pack (prettyShow hv) <> " --ghc " <> T.pack (prettyShow gv) <>+ " (see https://www.haskell.org/ghcup/guide/#hls for more information)"++ _ -> return ()+
scripts/bootstrap/bootstrap-haskell view
@@ -5,7 +5,7 @@ # that affect the installation procedure. # Main settings:-# * BOOTSTRAP_HASKELL_NONINTERACTIVE - any nonzero value for noninteractive installation+# * BOOTSTRAP_HASKELL_NONINTERACTIVE - any non-empty value for noninteractive installation # * BOOTSTRAP_HASKELL_NO_UPGRADE - any nonzero value to not trigger the upgrade # * BOOTSTRAP_HASKELL_MINIMAL - any nonzero value to only install ghcup # * GHCUP_USE_XDG_DIRS - any nonzero value to respect The XDG Base Directory Specification@@ -22,7 +22,7 @@ # * BOOTSTRAP_HASKELL_ADJUST_CABAL_CONFIG - whether to adjust mingw paths in cabal.config on windows # * BOOTSTRAP_HASKELL_DOWNLOADER - which downloader to use (default: curl) # * GHCUP_BASE_URL - the base url for ghcup binary download (use this to overwrite https://downloads.haskell.org/~ghcup with a mirror)-# * GHCUP_MSYS2_ENV - the msys2 environment to use on windows, see https://www.msys2.org/docs/environments/ (defauts to MINGW64, MINGW32 or CLANGARM64, depending on the architecture)+# * GHCUP_MSYS2_ENV - the msys2 environment to use on windows, see https://www.msys2.org/docs/environments/ (defaults to MINGW64, MINGW32 or CLANGARM64, depending on the architecture) # License: LGPL-3.0 @@ -41,7 +41,7 @@ plat="$(uname -s)" arch=$(uname -m)-ghver="0.1.50.2"+ghver="0.2.1.0" : "${GHCUP_BASE_URL:=https://downloads.haskell.org/~ghcup}" export GHCUP_SKIP_UPDATE_CHECK=yes@@ -83,6 +83,7 @@ MSYS*|MINGW*|CYGWIN*) : "${GHCUP_INSTALL_BASE_PREFIX:=/c}" GHCUP_DIR=$(cygpath -u "${GHCUP_INSTALL_BASE_PREFIX}/ghcup")+ GHCUP_CONFDIR=${GHCUP_DIR} GHCUP_BIN=$(cygpath -u "${GHCUP_INSTALL_BASE_PREFIX}/ghcup/bin") : "${GHCUP_MSYS2:=${GHCUP_DIR}/msys64}" set_msys2_env_dir@@ -92,9 +93,11 @@ if [ -n "${GHCUP_USE_XDG_DIRS}" ] ; then GHCUP_DIR=${XDG_DATA_HOME:=$HOME/.local/share}/ghcup+ GHCUP_CONFDIR=${XDG_DATA_HOME:=$HOME/.config}/ghcup GHCUP_BIN=${XDG_BIN_HOME:=$HOME/.local/bin} else GHCUP_DIR=${GHCUP_INSTALL_BASE_PREFIX}/.ghcup+ GHCUP_CONFDIR=${GHCUP_DIR} GHCUP_BIN=${GHCUP_INSTALL_BASE_PREFIX}/.ghcup/bin fi ;;@@ -338,6 +341,18 @@ esac _url=${GHCUP_BASE_URL}/${ghver}/x86_64-portbld-freebsd-ghcup-${ghver} ;;+ "OpenBSD"|"openbsd")+ case "${arch}" in+ x86_64|amd64)+ ;;+ i*86)+ die "i386 currently not supported!"+ ;;+ *) die "Unknown architecture: ${arch}"+ ;;+ esac+ _url=${GHCUP_BASE_URL}/${ghver}/x86_64-openbsd-ghcup-${ghver}+ ;; "Darwin"|"darwin") case "${arch}" in x86_64|amd64)@@ -479,16 +494,27 @@ warn "" while true; do - warn "[S] Skip [D] Default (GHCup maintained) [V] Vanilla (Upstream maintained) [?] Help (default is \"Skip\")."+ if [ "$preexisting_config_file" -eq 1 ] ; then+ warn "[K] Keep current config [G] GHCup maintained [V] Vanilla (upstream maintained) [?] Help (default is \"K\")."+ else+ warn "[G] GHCup maintained [V] Vanilla (upstream maintained) [?] Help (default is \"G\")."+ fi warn "" - read -r bashrc_answer </dev/tty+ read -r basechannel_answer </dev/tty - case $bashrc_answer in- [Ss]* | "")- return 0- ;;- [Dd]*)+ # Handle the [K]eep choice only if we actually offered it+ if [ "$preexisting_config_file" -eq 1 ] ; then+ case $basechannel_answer in+ [Kk]* | "")+ return 0+ ;;+ esac+ fi++ # Handle the other choices+ case $basechannel_answer in+ [Gg]* | "") return 1 ;; [Vv]*)@@ -497,22 +523,26 @@ *) echo "Possible choices are:" echo- echo "[S]kip - Do nothing and leave GHCup config as is"- echo "[D]efault - The default channel maintained by GHCup developers"+ if [ "$preexisting_config_file" -eq 1 ] ; then+ echo "[K]eep - Do nothing and leave your existing GHCup config as-is"+ fi+ echo "[G]HCup - The default channel maintained by GHCup developers" echo " (receives QA and support from GHCup and may provide" echo " unofficial bindists for less supported platforms)" echo "[V]anilla - Only contains unchanged upstream binary distributions" echo " (is updated with newer releases faster since it receives no GHCup QA)" echo- echo "Selecting Default or Vanilla will overwrite any pre-existing channel"- echo "config you might have (if you've previously installed GHCup)."- echo+ if [ "$preexisting_config_file" -eq 1 ] ; then+ echo "Selecting GHCup or Vanilla will overwrite the channel config in"+ echo "your existing GHCup installation."+ echo+ fi echo "Please make your choice and press ENTER." ;; esac done - unset bashrc_answer+ unset basechannel_answer } ask_prerelease_channel() {@@ -522,15 +552,16 @@ echo "-------------------------------------------------------------------------------" warn ""- warn "Do you also want to enable the pre-releases channel, getting access to."+ warn "Do you also want to enable the pre-releases channel, getting access to" warn "alpha, betas and release candidates?" warn "" warn "[N] No [Y] Yes [?] Help (default is \"N\")."+ warn "" while true; do - read -r bashrc_answer </dev/tty+ read -r prereleasechannel_answer </dev/tty - case $bashrc_answer in+ case $prereleasechannel_answer in [Nn]* | "") return 0 ;;@@ -548,7 +579,7 @@ esac done - unset bashrc_answer+ unset prereleasechannel_answer } ask_cross_channel() {@@ -565,9 +596,9 @@ warn "[N] No [Y] Yes [?] Help (default is \"N\")." warn "" - read -r bashrc_answer </dev/tty+ read -r crosschannel_answer </dev/tty - case $bashrc_answer in+ case $crosschannel_answer in [Nn]* | "") return 0 ;;@@ -577,18 +608,55 @@ *) echo "Possible choices are:" echo- echo "N - No, don't enable cross (default)"- echo "Y - Yes, enable prereleases"+ echo "N - No, don't enable the cross channel (default)"+ echo "Y - Yes, enable the cross channel" echo echo "Please make your choice and press ENTER." ;; esac done - unset bashrc_answer+ unset crosschannel_answer } +ask_thirdparty_channel() {+ if [ -n "${BOOTSTRAP_HASKELL_NONINTERACTIVE}" ] ; then+ return 0+ fi+ while true; do+ echo "-------------------------------------------------------------------------------" + warn ""+ warn "Do you also want to enable the 3rdparty channel, getting access to"+ warn "to various non-core tools such as hlint, dhall and ormolu?"+ warn ""+ warn "[N] No [Y] Yes [?] Help (default is \"N\")."+ warn ""++ read -r thirdparty_answer </dev/tty++ case $thirdparty_answer in+ [Nn]* | "")+ return 0+ ;;+ [Yy]*)+ return 1+ ;;+ *)+ echo "Possible choices are:"+ echo+ echo "N - No, don't enable the cross channel (default)"+ echo "Y - Yes, enable the cross channel"+ echo+ echo "Please make your choice and press ENTER."+ ;;+ esac+ done++ unset thirdparty_answer+}++ # Ask user if they want to adjust the bashrc. ask_bashrc() { if [ -n "${BOOTSTRAP_HASKELL_ADJUST_BASHRC}" ] ; then@@ -620,7 +688,8 @@ return 2 ;; [Nn]*)- return 0;;+ return 0+ ;; *) echo "Possible choices are:" echo@@ -951,32 +1020,14 @@ read -r answer </dev/tty fi -if [ -z "${BOOTSTRAP_HASKELL_MINIMAL}" ] ; then- ask_base_channel- ask_base_channel_answer=$?- if [ $ask_base_channel_answer != 0 ] ; then- ask_prerelease_channel- ask_prerelease_channel_answer=$?- ask_cross_channel- ask_cross_channel_answer=$?- fi-fi-ask_bashrc-ask_bashrc_answer=$?-ask_cabal_config_init-ask_cabal_config_init_answer=$?-if [ -z "${BOOTSTRAP_HASKELL_MINIMAL}" ] ; then- ask_hls- ask_hls_answer=$?- ask_stack- ask_stack_answer=$?-fi+preexisting_config_file=0+test -f "${GHCUP_CONFDIR}/config.yaml" && preexisting_config_file=1 edo mkdir -p "${GHCUP_BIN}" if command -V "ghcup" >/dev/null 2>&1 ; then if [ -z "${BOOTSTRAP_HASKELL_NO_UPGRADE}" ] ; then- ( _eghcup upgrade ) || download_ghcup+ download_ghcup fi else download_ghcup@@ -987,8 +1038,8 @@ echo if [ -z "${BOOTSTRAP_HASKELL_NONINTERACTIVE}" ] ; then+ echo warn "Press ENTER to proceed or ctrl-c to abort."- warn "Installation may take a while." echo # Wait for user input to continue.@@ -996,6 +1047,29 @@ read -r answer </dev/tty fi +if [ -z "${BOOTSTRAP_HASKELL_MINIMAL}" ] ; then+ ask_base_channel+ ask_base_channel_answer=$?+ if [ $ask_base_channel_answer != 0 ] ; then+ ask_prerelease_channel+ ask_prerelease_channel_answer=$?+ ask_cross_channel+ ask_cross_channel_answer=$?+ ask_thirdparty_channel+ ask_thirdparty_channel_answer=$?+ fi+fi+ask_bashrc+ask_bashrc_answer=$?+ask_cabal_config_init+ask_cabal_config_init_answer=$?+if [ -z "${BOOTSTRAP_HASKELL_MINIMAL}" ] ; then+ ask_hls+ ask_hls_answer=$?+ ask_stack+ ask_stack_answer=$?+fi+ case $ask_base_channel_answer in 1) _eghcup config set url-source GHCupURL@@ -1016,6 +1090,13 @@ case $ask_cross_channel_answer in 1) _eghcup config add-release-channel cross+ ;;+ *) ;;+esac++case $ask_thirdparty_channel_answer in+ 1)+ _eghcup config add-release-channel 3rdparty ;; *) ;; esac
scripts/bootstrap/bootstrap-haskell.ps1 view
@@ -47,12 +47,12 @@ [switch]$DontWriteDesktopShortcuts, # Whether to disable adjusting bashrc (in msys2 env) with PATH [switch]$DontAdjustBashRc, - # The msys2 environment to use, see https://www.msys2.org/docs/environments/ (defauts to MINGW64, MINGW32 or CLANGARM64, depending on the architecture) + # The msys2 environment to use, see https://www.msys2.org/docs/environments/ (defaults to MINGW64, MINGW32 or CLANGARM64, depending on the architecture) [string]$Msys2Env ) -$DefaultMsys2Version = "20221216" -$DefaultMsys2Hash = "18370d32b0264915c97e3d7c618f7b32d48ad80858923883fde5145acd32ca0f" +$DefaultMsys2Version = "20250221" +$DefaultMsys2Hash = "6d4952fd65d1924c56620355c3c7d5a1b40e3c7d7be358f6eb1017363d2dbbb1" $Silent = !$Interactive @@ -561,12 +561,6 @@ Exec "$env:windir\system32\taskkill.exe" /F /FI "MODULES eq msys-2.0.dll" - Print-Msg -msg 'Upgrading full system...' - Exec "$Bash" '-lc' 'pacman --noconfirm -Syuu' - - Print-Msg -msg 'Upgrading full system twice...' - Exec "$Bash" '-lc' 'pacman --noconfirm -Syuu' - Print-Msg -msg 'Installing Dependencies...' Exec "$Bash" '-lc' ('pacman --noconfirm -S --needed curl autoconf {0}' -f $PkgConf) @@ -650,9 +644,15 @@ $GhcInstArgs = ('{0} -mintty -c "pacman --noconfirm -S --needed base-devel gettext autoconf make libtool automake python p7zip patch unzip"' -f $ShellType) Create-Shortcut -SourceExe ('{0}\msys2_shell.cmd' -f $MsysDir) -ArgumentsToSourceExe $GhcInstArgs -DestinationPath 'Install GHC dev dependencies.lnk' -TempPath $GhcupDir - Create-Shortcut -SourceExe ('{0}\msys2_shell.cmd' -f $MsysDir) -ArgumentsToSourceExe $ShellType -DestinationPath 'Mingw haskell shell.lnk' -TempPath $GhcupDir - Create-Shortcut -SourceExe 'https://www.msys2.org/docs/package-management' -ArgumentsToSourceExe '' -DestinationPath 'Mingw package management docs.url' -TempPath $GhcupDir + $DesktopDir = [Environment]::GetFolderPath("Desktop") + $shellScript = (@' +powershell.exe -ExecutionPolicy Bypass -Command ". '{0}\msys2_shell.cmd' '{1}' '-here' '-no-start' '-shell' 'bash' '-use-full-path' '-c' 'echo && echo To keep your msys2 env and bash shell up to date, run: pacman -Syuu && /usr/bin/bash -l'" +'@ -f $MsysDir, $ShellType) + $null = New-Item -Path $MsysDir -Name "Haskell Shell.bat" -ItemType "file" -Force -Value $shellScript + Create-Shortcut -SourceExe ('{0}\Haskell Shell.bat' -f $MsysDir) -ArgumentsToSourceExe '' -DestinationPath 'Mingw haskell shell.lnk' -TempPath $GhcupDir+ + Create-Shortcut -SourceExe 'https://www.msys2.org/docs/package-management' -ArgumentsToSourceExe '' -DestinationPath 'Mingw package management docs.url' -TempPath $GhcupDir $null = New-Item -Path $DesktopDir -Name "Uninstall Haskell.ps1" -ItemType "file" -Force -Value $uninstallShortCut }
test/ghcup-test/GHCup/ArbitraryTypes.hs view
@@ -7,8 +7,11 @@ import GHCup.Types+import GHCup.Types.Stack+import GHCup.Types.JSON import Data.ByteString ( ByteString )+import Data.Char ( toLower ) import Data.Versions import Data.List.NonEmpty import Data.Time.Calendar ( Day(..) )@@ -26,6 +29,39 @@ import qualified Data.Text.Lazy.Builder.Int as B +instance Arbitrary ToolVersionSpec where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary GHCupDownloads where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary Rev where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary RevisionSpec where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary VersionMetadata where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary ArchitectureSpec where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary PlatformSpec where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary PlatformVersionSpec where+ arbitrary = genericArbitrary+ shrink = genericShrink++ ----------------- --[ utilities ]-- -----------------@@ -40,7 +76,10 @@ <*> arbitrary <*> arbitrary +instance ToADTArbitrary ToolDescription +instance ToADTArbitrary ToolInfo+ instance ToADTArbitrary GHCupInfo @@ -127,14 +166,81 @@ --[ ghcup arbitrary ]-- ----------------------- +instance Arbitrary ToolDescription where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary ToolInfo where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary EnvUnion where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary EnvSpec where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary ConfigSpec where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary MakeSpec where+ arbitrary = genericArbitrary+ shrink = genericShrink+++instance Arbitrary InstallationSpecInput where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary (NonEmpty InstallFileRule) where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary InstallFileRule where+ arbitrary = suchThat genericArbitrary pred'+ where+ pred' (InstallFileRule source dest) = safePath source && maybe True safePath dest+ pred' (InstallFilePatternRule fps) = all safePath fps+ shrink = genericShrink++instance Arbitrary (SymlinkSpec String) where+ arbitrary = suchThat genericArbitrary pred'+ where+ pred' SymlinkSpec{..} =+ safePath _slTarget+ && safeFilename _slLinkName+ && maybe True safeFilename _slSetName+ shrink = genericShrink++instance Arbitrary SymlinkInputSpec where+ arbitrary = suchThat genericArbitrary pred'+ where+ pred' SymlinkInputSpec{..} =+ safePath _slTarget+ && safeFilename _slLinkName+ && maybe True safeFilename _slSetName+ pred' SymlinkPatternSpec{..} =+ all safePath _slTargetPattern+ && all safePath _slTargetPatternIgnore+ && safeFilename _slLinkName+ && maybe True safeFilename _slSetName+ shrink = genericShrink+ instance Arbitrary Requirements where arbitrary = genericArbitrary shrink = genericShrink -instance Arbitrary DownloadInfo where+instance Arbitrary GHCup.Types.DownloadInfo where arbitrary = genericArbitrary shrink = genericShrink +instance Arbitrary GHCup.Types.Stack.DownloadInfo where+ arbitrary = genericArbitrary+ shrink = genericShrink+ instance Arbitrary LinuxDistro where arbitrary = oneof (pure <$> allDistros)@@ -159,6 +265,98 @@ arbitrary = genericArbitrary shrink = genericShrink +instance Arbitrary UserSettings where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary PagerConfig where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary PlatformRequest where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary MetaMode where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary GPGSetting where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary Verbosity where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary URLSource where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary KeepDirs where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary Downloader where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary SetupInfo where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary UserKeyBindings where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary NewURLSource where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary ChannelAlias where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary KeyCombination where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary Key where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary Modifier where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary GHCDownloadInfo where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary ProcessSpec where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary UserInfo where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary Authority where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary DownloadMirror where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary DownloadMirrors where+ arbitrary = genericArbitrary+ shrink = genericShrink++instance Arbitrary VersionedDownloadInfo where+ arbitrary = genericArbitrary+ shrink = genericShrink+ instance Arbitrary (NonEmpty VersionCmp) where arbitrary = genericArbitrary shrink = genericShrink@@ -172,15 +370,21 @@ shrink = genericShrink instance Arbitrary Tool where- arbitrary = genericArbitrary+ arbitrary = flip suchThat pred' $ do+ ASCIIString str <- arbitrary+ pure $ Tool (toLower <$> str)+ where+ pred' = safeToolname shrink = genericShrink instance Arbitrary GHCupInfo where arbitrary = genericArbitrary shrink = genericShrink -instance Arbitrary GHCTargetVersion where- arbitrary = GHCTargetVersion Nothing <$> arbitrary+instance Arbitrary TargetVersion where+ arbitrary = suchThat (TargetVersion Nothing <$> arbitrary) pred'+ where+ pred' = safeVersion shrink = genericShrink
test/ghcup-test/GHCup/ParserSpec.hs view
@@ -1,20 +1,23 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE CPP #-} module GHCup.ParserSpec where -import GHCup.Types-import GHCup.Types.JSON-import GHCup.Prelude.Version.QQ-import GHCup.Prelude.MegaParsec+import GHCup.Prelude.MegaParsec+import GHCup.Prelude.Version.QQ+import GHCup.Types+import GHCup.Types.JSON+ () -import Data.List.NonEmpty ( NonEmpty (..) )-import qualified Data.Set as Set+import Data.Either ( isLeft )+import Data.List.NonEmpty ( NonEmpty (..) )+import qualified Data.Set as Set import Data.Versions-import qualified Text.Megaparsec as MP+import qualified Text.Megaparsec as MP import Text.Megaparsec -import Test.Hspec+import Test.Hspec spec :: Spec spec = do@@ -53,8 +56,66 @@ MP.parse ghcVersionFromPath "" "../ghc/javascript-unknown-ghcjs-9.6.2/bin/javascript-unknown-ghcjs-ghc-9.6.2" `shouldBe` Right ghcjs962 MP.parse ghcVersionFromPath "" "c:/ghc/bin/ghcup/ghc/javascript-unknown-ghcjs-9.6.2/bin/javascript-unknown-ghcjs-ghc-9.6.2" `shouldBe` Right ghcjs962 +#if defined(IS_WINDOWS)+ -- windows+ MP.parse ghcVersionFromPath "" "c:\\ghcup\\ghc\\mytag9.4.8\\bin\\ghc-9.4.8" `shouldBe` Right ghcMytag+ MP.parse ghcVersionFromPath "" "..\\ghc\\9.4.8-rc2\\bin\\ghc-9.4.8" `shouldBe` Right ghc948rc2+#endif++ -- errors+ isLeft (MP.parse ghcVersionFromPath "" "") `shouldBe` True+ isLeft (MP.parse ghcVersionFromPath "" "k") `shouldBe` True+ isLeft (MP.parse ghcVersionFromPath "" "/ghc/") `shouldBe` True++ it "toolVersionFromPath" $ do+ -- other tools+ MP.parse (toolVersionFromPath hls) "" "../hls/2.13.0.0/bin/haskell-language-server-wrapper" `shouldBe` hls2_13_0_0+ MP.parse (toolVersionFromPath cabal) "" "../cabal/3.14.1.0/cabal" `shouldBe` cabal_3_14_1_0++ it "verRevP" $ do+ -- other tools+ MP.parse verRevP "" "1.2.3-roo-rar-r100" `shouldBe` Right VersionReq {+ _vqVersion = Version {+ _vEpoch = Nothing,+ _vChunks = Chunks (Numeric 1 :| [Numeric 2, Numeric 3]),+ _vRel = Just (Release (Alphanum "roo-rar" :| [])),+ _vMeta = Nothing+ },+ _vqRev = Just 100+ }+ MP.parse verRevP "" "1.2.3-roo-rar" `shouldBe` Right VersionReq {+ _vqVersion = Version {+ _vEpoch = Nothing,+ _vChunks = Chunks (Numeric 1 :| [Numeric 2, Numeric 3]),+ _vRel = Just (Release (Alphanum "roo-rar" :| [])),+ _vMeta = Nothing+ },+ _vqRev = Nothing+ }++ it "ghcLinkVersion" $ do+ MP.parse ghcLinkVersion "" "ghc-8.10.7" `shouldBe` Right ghc8107+ where- ghc8107 = GHCTargetVersion {_tvTarget = Nothing, _tvVersion = Version {_vEpoch = Nothing, _vChunks = Chunks (Numeric 8 :| [Numeric 10,Numeric 7]), _vRel = Nothing, _vMeta = Nothing}}- ghc948rc2 = GHCTargetVersion {_tvTarget = Nothing, _tvVersion = Version {_vEpoch = Nothing, _vChunks = Chunks (Numeric 9 :| [Numeric 4,Numeric 8]), _vRel = Just (Release (Alphanum "rc2" :| [])), _vMeta = Nothing}}- ghcMytag = GHCTargetVersion {_tvTarget = Nothing, _tvVersion = Version {_vEpoch = Nothing, _vChunks = Chunks (Alphanum "mytag9" :| [Numeric 4,Numeric 8]), _vRel = Nothing, _vMeta = Nothing}}- ghcjs962 = GHCTargetVersion {_tvTarget = Just "javascript-unknown-ghcjs", _tvVersion = Version { _vEpoch = Nothing, _vChunks = Chunks (Numeric 9 :| [Numeric 6,Numeric 2]), _vRel = Nothing, _vMeta = Nothing}}+ hls2_13_0_0 = Right TargetVersion {+ _tvTarget = Nothing,+ _tvVersion = Version {+ _vEpoch = Nothing,+ _vChunks = Chunks (Numeric 2 :| [Numeric 13, Numeric 0, Numeric 0]),+ _vRel = Nothing,+ _vMeta = Nothing+ }+ }+ cabal_3_14_1_0 = Right TargetVersion {+ _tvTarget = Nothing,+ _tvVersion = Version {+ _vEpoch = Nothing,+ _vChunks = Chunks (Numeric 3 :| [Numeric 14, Numeric 1, Numeric 0]),+ _vRel = Nothing,+ _vMeta = Nothing+ }+ }+ ghc8107 = TargetVersion {_tvTarget = Nothing, _tvVersion = Version {_vEpoch = Nothing, _vChunks = Chunks (Numeric 8 :| [Numeric 10,Numeric 7]), _vRel = Nothing, _vMeta = Nothing}}+ ghc948rc2 = TargetVersion {_tvTarget = Nothing, _tvVersion = Version {_vEpoch = Nothing, _vChunks = Chunks (Numeric 9 :| [Numeric 4,Numeric 8]), _vRel = Just (Release (Alphanum "rc2" :| [])), _vMeta = Nothing}}+ ghcMytag = TargetVersion {_tvTarget = Nothing, _tvVersion = Version {_vEpoch = Nothing, _vChunks = Chunks (Alphanum "mytag9" :| [Numeric 4,Numeric 8]), _vRel = Nothing, _vMeta = Nothing}}+ ghcjs962 = TargetVersion {_tvTarget = Just "javascript-unknown-ghcjs", _tvVersion = Version { _vEpoch = Nothing, _vChunks = Chunks (Numeric 9 :| [Numeric 6,Numeric 2]), _vRel = Nothing, _vMeta = Nothing}}
test/ghcup-test/GHCup/Prelude/File/Posix/TraversalsSpec.hs view
@@ -8,7 +8,6 @@ import Control.Monad.IO.Class (liftIO) import Data.List-import System.Posix.Directory import Unsafe.Coerce #endif
test/ghcup-test/GHCup/Types/JSONSpec.hs view
@@ -1,21 +1,127 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE TypeApplications #-} module GHCup.Types.JSONSpec where -import GHCup.ArbitraryTypes ()-import GHCup.Types hiding ( defaultSettings )-import GHCup.Types.JSON ()-import GHCup.Prelude+import GHCup.ArbitraryTypes ()+import GHCup.Prelude+import GHCup.Types hiding ( defaultSettings )+import GHCup.Types.Dhall ()+import GHCup.Types.JSON () -import Control.Monad (when)-import Test.Aeson.GenericSpecs-import Test.Hspec+import Control.Monad (when, forM_)+import System.FilePath+import Test.Aeson.GenericSpecs+import Test.Hspec.Golden+import Test.Hspec +import qualified Data.Text as T+import qualified Data.Text.IO as T+import qualified Data.Aeson.Encode.Pretty as Aeson+import qualified Data.Yaml as YAML +#if defined(DHALL)+import Dhall.Marshal.Encode ()+import qualified Dhall.Core as Dhall+import qualified Dhall.Binary as Dhall+import qualified Dhall hiding (Text)+import qualified Data.Void as V+import Dhall.Yaml (dhallToYaml, defaultOptions)+import Dhall.JSON (convertToHomogeneousMaps, Conversion(..))+#endif spec :: Spec spec = do roundtripSpecs (Proxy @LinuxDistro)+ roundtripSpecs (Proxy @EnvSpec)+ roundtripSpecs (Proxy @ConfigSpec)+ roundtripSpecs (Proxy @MakeSpec)+ roundtripSpecs (Proxy @SymlinkInputSpec)+ roundtripSpecs (Proxy @InstallationSpecInput)+ roundtripSpecs (Proxy @Tool)+ roundtripSpecs (Proxy @TargetVersion)+ roundtripSpecs (Proxy @VersionInfo)+ roundtripSpecs (Proxy @Architecture)+ roundtripSpecs (Proxy @Platform)+ roundtripSpecs (Proxy @VersionRange)+ roundtripSpecs (Proxy @URLSource)+ roundtripSpecs (Proxy @UserSettings)+ roundtripSpecs (Proxy @GHCupDownloads)+ roundtripSpecs (Proxy @ToolInfo)+ roundtripSpecs (Proxy @ToolDescription)+ roundtripSpecs (Proxy @ToolVersionSpec)+ roundtripSpecs (Proxy @VersionMetadata)+ roundtripSpecs (Proxy @RevisionSpec)+ roundtripSpecs (Proxy @VersionInfo)+ roundtripSpecs (Proxy @ArchitectureSpec)+ roundtripSpecs (Proxy @PlatformSpec)+ roundtripSpecs (Proxy @PlatformVersionSpec)+ roundtripSpecs (Proxy @DownloadInfo)+ roundtripSpecs (Proxy @GHCupInfo)+#if !defined(i386_HOST_ARCH) when (not isWindows) $ roundtripAndGoldenSpecsWithSettings (defaultSettings { goldenDirectoryOption = CustomDirectoryName "test/ghcup-test/golden/unix", sampleSize = 2 }) (Proxy @GHCupInfo)+#endif + describe "Parse old metadata" $ do+ forM_ yamls $ \v ->+ it v $ do+ (Right info) <- YAML.decodeFileEither @GHCupInfo $ "data/test/ghcup-" <> v+ pure $ Golden {+ output = decUTF8Safe' $ Aeson.encodePretty info+ , encodePretty = T.unpack+ , writeToFile = T.writeFile+ , readFromFile = T.readFile+ , goldenFile = "test/ghcup-test/golden/old_metadata/ghcup-" <> dropExtension v+ , actualFile = Nothing+ , failFirstTime = False+ }++#if defined(DHALL)+ describe "Dhall roundtrip" $ do+ beforeAll (do+ info <- YAML.decodeFileEither @GHCupInfo "data/test/ghcup-0.1.0.yaml" >>= either (fail . show) pure+ let expr = Dhall.denote $ Dhall.embed @GHCupInfo Dhall.inject info+ pure (info, expr)+ ) $ do+ it "Normal dhall" $ \(info, expr) -> do+ nInfo <- roundTripNormal expr+ nInfo `shouldBe` info+ it "Binary dhall" $ \(info, expr) -> do+ bInfo <- roundTripBinary expr+ bInfo `shouldBe` info+ it "dhall-to-yaml" $ \(info, expr) -> do+ yInfo <- roundTripYaml expr+ yInfo `shouldBe` info+#endif+ where+ yamls = [ "0.0.1.json"+ , "0.0.2.json"+ , "0.0.3.yaml"+ , "0.0.4.yaml"+ , "0.0.5.yaml"+ , "0.0.6.yaml"+ , "0.0.7.yaml"+ , "0.0.8.yaml"+ , "0.0.9.yaml"+ , "0.1.0.yaml"+ ]+#if defined(DHALL)+ roundTripNormal :: Dhall.Expr V.Void V.Void -> IO GHCupInfo+ roundTripNormal expr = do+ let dhallCode = Dhall.pretty expr+ Dhall.input @GHCupInfo Dhall.auto dhallCode++ roundTripBinary :: Dhall.Expr V.Void V.Void -> IO GHCupInfo+ roundTripBinary expr = do+ let dhallCode = Dhall.encodeExpression expr+ expr' <- either (fail . show) pure $ Dhall.decodeExpression @V.Void @V.Void dhallCode+ Dhall.rawInput Dhall.auto expr'++ -- using dhall-to-yaml essentially+ roundTripYaml :: Dhall.Expr V.Void V.Void -> IO GHCupInfo+ roundTripYaml (convertToHomogeneousMaps NoConversion -> expr) = do+ let dhallCode = Dhall.pretty expr+ yaml <- dhallToYaml defaultOptions Nothing dhallCode+ either (fail . show) pure $ YAML.decodeEither' yaml+#endif
test/ghcup-test/golden/unix/GHCupInfo.json view
@@ -2,13743 +2,2460 @@ "samples": [ { "ghcupDownloads": {- "Cabal": {- "1.1.8": {- "viArch": {- "A_ARM": {- "FreeBSD": {- "unknown_versioning": {- "dlCSize": -4,- "dlHash": "lzmi",- "dlOutput": null,- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [],- "dlUri": "https:vedinez"- }- }- },- "A_PowerPC": {- "Darwin": {- "( < 3.4.1 && < 1.2.2 && >= 2.4.5 && >= 1.1.4 && > 4.1.2 )": {- "dlCSize": -2,- "dlHash": "dqlsply",- "dlOutput": "KS𡅫➲>\u0002",- "dlSubdir": {- "RegexDir": "I\u0004"- },- "dlTag": [- "Nightly",- "LatestPrerelease",- "Experimental"- ],- "dlUri": "https:heaonl"- },- "( < 4.5.3 && == 2.3.1 && <= 1.2.3 ) || ( > 3.1.1 && > 1.1.1 && < 3.4.3 && <= 1.4.1 && <= 1.5.1 )": {- "dlCSize": 3,- "dlHash": "p",- "dlOutput": "X9峚𰃜",- "dlSubdir": "\u0014\u0015",- "dlTag": null,- "dlUri": "https:lpbi"- },- "( >= 5.5.3 && > 3.1.1 && >= 1.1.1 && > 3.5.2 && > 3.1.2 && > 5.5.4 && == 5.2.5 ) || ( == 4.2.3 && < 1.3.4 && < 3.1.2 )": {- "dlCSize": -2,- "dlHash": "tnhzuv",- "dlOutput": "9\u0011\u0013;",- "dlSubdir": {- "RegexDir": "\u000f\r\u0013"- },- "dlTag": [- "Prerelease",- "Latest",- "base-2.3.3",- "base-4.2.5",- "base-5.5.6"- ],- "dlUri": "https:x"- }- },- "FreeBSD": {- "( <= 4.1.1 && >= 5.5.5 )": {- "dlCSize": -2,- "dlHash": "cc",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "0"- },- "dlTag": null,- "dlUri": "http:ooueot"- },- "unknown_versioning": {- "dlCSize": -6,- "dlHash": "",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Experimental",- "base-2.6.4",- "LatestPrerelease",- "base-3.5.4",- "old",- "Prerelease",- "D\u000ba"- ],- "dlUri": "https:ghaoqgd"- }- },- "Linux_Fedora": {- "( < 4.3.4 && >= 4.2.1 && > 2.4.4 )": {- "dlCSize": null,- "dlHash": "zjbzn",- "dlOutput": "%",- "dlSubdir": {- "RegexDir": "H"- },- "dlTag": [- "base-5.4.1"- ],- "dlUri": "https:atcokx"- },- "( <= 3.5.1 && == 2.4.4 && > 4.1.5 && <= 3.5.4 && <= 2.3.5 )": {- "dlCSize": -3,- "dlHash": "dymxs",- "dlOutput": null,- "dlSubdir": "k",- "dlTag": [- "LatestNightly",- "LatestPrerelease",- "LatestNightly",- "Experimental",- "base-3.4.3",- "LatestPrerelease"- ],- "dlUri": "https:gki"- },- "( > 4.5.3 && < 5.5.3 && == 4.3.4 && >= 1.3.5 && == 1.3.2 && == 2.4.2 && <= 4.4.2 ) || ( >= 2.3.1 )": {- "dlCSize": -6,- "dlHash": "hco",- "dlOutput": "e&",- "dlSubdir": null,- "dlTag": [- "LatestPrerelease",- "LatestPrerelease",- "LatestPrerelease",- "Latest",- "Recommended",- "LatestPrerelease",- ""- ],- "dlUri": "https:trqoj"- },- "unknown_versioning": {- "dlCSize": 6,- "dlHash": "vbx",- "dlOutput": "𲇀蚗\u0014%\u001d",- "dlSubdir": {- "RegexDir": ";H𩹽Ὗ"- },- "dlTag": [],- "dlUri": "https:a"- }- }- },- "A_Sparc": {- "Darwin": {- "( < 5.2.3 && <= 5.4.5 && < 1.3.5 && >= 5.1.5 && <= 1.4.1 ) || ( <= 4.2.4 && < 4.4.3 && == 1.3.2 && >= 3.3.1 )": {- "dlCSize": null,- "dlHash": "j",- "dlOutput": null,- "dlSubdir": "3",- "dlTag": [- "base-6.2.2",- "Latest",- "J𫵥{>p𦐬",- "base-5.6.1",- "Latest",- "Recommended",- "Experimental"- ],- "dlUri": "https:ajaluz"- },- "( == 1.3.5 && >= 3.5.1 && < 5.2.4 && <= 3.2.4 && <= 5.5.1 && < 4.4.2 && <= 1.1.1 )": {- "dlCSize": null,- "dlHash": "yyala",- "dlOutput": "\u0018𔑑0m",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": null,- "dlUri": "https:"- },- "( > 1.1.1 && >= 5.3.1 && > 2.5.5 && <= 5.4.2 && > 1.4.1 )": {- "dlCSize": null,- "dlHash": "abr",- "dlOutput": "I\u001e",- "dlSubdir": "",- "dlTag": null,- "dlUri": "https:kbhpcd"- },- "( > 1.1.3 )": {- "dlCSize": null,- "dlHash": "tkevrp",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Experimental",- "old"- ],- "dlUri": "https:hqkbim"- },- "( > 1.1.3 ) || ( >= 1.1.4 )": {- "dlCSize": -7,- "dlHash": "lqyavl",- "dlOutput": "Si6\tD$",- "dlSubdir": {- "RegexDir": "`0"- },- "dlTag": [- "Nightly",- "Experimental",- "base-5.3.4",- "base-6.6.2"- ],- "dlUri": "https:q"- },- "unknown_versioning": {- "dlCSize": 3,- "dlHash": "ep",- "dlOutput": "mX",- "dlSubdir": null,- "dlTag": [- "Prerelease",- "LatestPrerelease",- "Experimental",- "Recommended"- ],- "dlUri": "https:m"- }- },- "Linux_CentOS": {- "( < 5.4.2 && >= 1.4.1 && >= 1.4.3 && > 5.2.3 && < 3.2.4 )": {- "dlCSize": -4,- "dlHash": "s",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Prerelease",- ""- ],- "dlUri": "http:pntmrxm"- },- "( == 1.2.2 ) || ( > 1.2.1 && <= 2.1.4 && >= 4.3.2 && > 1.2.3 && > 2.3.4 && == 4.5.1 ) || ( <= 1.1.1 && > 1.1.1 && == 2.2.2 )": {- "dlCSize": -3,- "dlHash": "i",- "dlOutput": "EG\u0000",- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:sso"- },- "( == 4.3.2 )": {- "dlCSize": -7,- "dlHash": "",- "dlOutput": "\u0000w[97J",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [],- "dlUri": "http:nlxuse"- }- },- "Linux_UnknownLinux": {- "( < 3.4.2 )": {- "dlCSize": 7,- "dlHash": "xbwtnq",- "dlOutput": "\tv",- "dlSubdir": "[v",- "dlTag": null,- "dlUri": "https:"- },- "( <= 2.2.3 && <= 3.4.3 && > 2.4.3 && >= 5.5.2 && <= 2.4.1 && > 1.2.4 ) || ( <= 2.2.1 && == 1.2.3 && == 4.1.4 && == 1.3.3 )": {- "dlCSize": 1,- "dlHash": "d",- "dlOutput": "",- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:saw"- },- "( == 2.1.4 && < 4.1.2 && > 4.1.2 ) || ( == 4.3.4 && >= 1.2.3 && == 2.1.2 ) || ( >= 3.2.4 && < 1.1.3 && == 2.3.3 && >= 3.3.3 && == 1.2.3 )": {- "dlCSize": -1,- "dlHash": "eftchm",- "dlOutput": "𢳀B",- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:"- },- "( > 1.5.3 )": {- "dlCSize": -4,- "dlHash": "gkaf",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "弬x𧚭"- },- "dlTag": [- "Experimental",- "LatestNightly",- "LatestPrerelease",- "Recommended"- ],- "dlUri": "https:xajbj"- },- "( > 6.4.3 && == 3.5.1 && > 3.5.4 ) || ( == 3.4.1 && >= 4.3.1 && >= 4.4.4 )": {- "dlCSize": 2,- "dlHash": "",- "dlOutput": "4k#𩾝\u0016\u001b",- "dlSubdir": "O𤑼\u000e",- "dlTag": [- "Nightly",- "Nightly",- "LatestPrerelease",- "Experimental",- "<\u0001\u0000",- "Nightly",- "Prerelease"- ],- "dlUri": "https:qswke"- },- "( >= 3.2.4 ) || ( <= 1.1.2 && > 4.4.4 && == 3.2.4 && >= 3.4.1 && < 2.2.3 ) || ( < 2.3.1 && < 1.4.2 && <= 2.2.2 && > 3.3.3 )": {- "dlCSize": 6,- "dlHash": "vknsso",- "dlOutput": "\u0010@/<",- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:eetgom"- },- "( >= 4.4.2 && > 5.1.2 && > 5.4.1 && <= 1.1.1 ) || ( > 4.1.1 && == 2.4.3 && == 1.2.3 )": {- "dlCSize": 0,- "dlHash": "trvzcm",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "SF\u001dS^\u0015"- },- "dlTag": [],- "dlUri": "https:bgkwzu"- }- },- "Windows": {- "( == 4.4.3 && > 2.5.3 )": {- "dlCSize": -7,- "dlHash": "lsjekp",- "dlOutput": null,- "dlSubdir": "",- "dlTag": [- "Nightly",- "Experimental"- ],- "dlUri": "http:"- },- "unknown_versioning": {- "dlCSize": 0,- "dlHash": "",- "dlOutput": "Vu𘖖N",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "Nightly",- "Latest",- "Recommended",- "Experimental",- "Z"- ],- "dlUri": "https:irvd"- }- }- }- },- "viChangeLog": null,- "viPostInstall": "ts",- "viPostRemove": "aadsa",- "viPreCompile": "sjpv",- "viPreInstall": "xvajrnk",- "viReleaseDay": "-21913023041306456-05-31",- "viSourceDL": null,- "viTags": [- "old",- "q-*\u0004",- "Nightly",- "base-1.6.2",- "old",- "Experimental"- ],- "viTestDL": {- "dlCSize": 0,- "dlHash": "xpegi",- "dlOutput": "",- "dlSubdir": "jR",- "dlTag": [- "Latest",- "LatestPrerelease",- "old",- "old",- "Nightly",- "Recommended"- ],- "dlUri": "http:h"- }- },- "1.3.1": {- "viArch": {- "A_ARM64": {- "Darwin": {- "( < 4.1.4 && > 3.2.1 && >= 4.2.3 && < 3.2.5 && <= 1.2.4 && <= 5.4.5 )": {- "dlCSize": 1,- "dlHash": "yv",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "t\u0002砡~&"- },- "dlTag": [- "Recommended",- "old",- "\u000c",- "old"- ],- "dlUri": "http:tekt"- },- "( == 2.2.5 && == 2.5.1 && > 2.2.2 && < 4.2.2 && > 3.5.1 && <= 4.4.2 && >= 1.3.1 ) || ( >= 1.3.2 && < 1.2.4 && >= 1.2.2 )": {- "dlCSize": null,- "dlHash": "f",- "dlOutput": "f8U",- "dlSubdir": "v",- "dlTag": [- "Prerelease",- "old",- "LatestNightly",- "LatestNightly",- "LatestNightly",- ""- ],- "dlUri": "https:"- },- "( == 5.1.2 && == 1.5.1 && <= 2.5.4 && >= 4.4.3 && >= 3.6.1 && >= 2.4.1 && < 4.3.2 )": {- "dlCSize": 7,- "dlHash": "lkvv",- "dlOutput": "",- "dlSubdir": null,- "dlTag": [- "Recommended",- "Prerelease"- ],- "dlUri": "http:en"- },- "( > 3.3.1 && < 6.4.2 && == 3.2.2 && < 2.3.2 ) || ( <= 3.3.3 && > 1.3.1 && == 1.3.2 && >= 2.4.3 ) || ( == 3.2.3 && >= 1.3.2 && <= 3.4.1 && >= 2.3.1 )": {- "dlCSize": -4,- "dlHash": "rw",- "dlOutput": "",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [],- "dlUri": "http:vqc"- },- "( > 5.4.2 && <= 6.4.2 && > 1.5.5 && == 5.4.5 && < 5.4.3 && > 2.5.5 ) || ( > 4.1.3 && <= 1.2.1 && == 4.1.4 && == 3.2.3 && > 1.1.3 && <= 4.2.2 ) || ( >= 3.3.3 ) || ( == 1.2.2 )": {- "dlCSize": -1,- "dlHash": "tk",- "dlOutput": "",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "Experimental",- "LatestPrerelease",- "Nightly"- ],- "dlUri": "http:gntqhy"- },- "( >= 4.5.2 && > 5.3.2 && < 4.2.3 && < 3.2.1 && > 3.3.6 && > 3.2.4 ) || ( < 2.1.2 )": {- "dlCSize": -2,- "dlHash": "lopa",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "*|\u001d"- },- "dlTag": null,- "dlUri": "https:"- },- "( >= 5.1.4 && > 4.3.5 && <= 4.5.3 && <= 2.2.2 && >= 3.2.2 )": {- "dlCSize": null,- "dlHash": "zw",- "dlOutput": null,- "dlSubdir": "G=\u001a",- "dlTag": [],- "dlUri": "https:ssoeizq"- }- },- "FreeBSD": {- "( > 3.1.3 && < 1.5.2 && <= 1.3.4 )": {- "dlCSize": -6,- "dlHash": "chngbv",- "dlOutput": "aV𝛼%",- "dlSubdir": "\u000c",- "dlTag": [- "Recommended",- "Experimental",- "Prerelease",- "Latest",- "Prerelease"- ],- "dlUri": "http:jzpusg"- },- "( > 4.2.5 && >= 4.2.1 )": {- "dlCSize": -5,- "dlHash": "lrgzgth",- "dlOutput": "[\u0019\u0008r",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": null,- "dlUri": "http:"- },- "unknown_versioning": {- "dlCSize": 3,- "dlHash": "u",- "dlOutput": "@\u001eOX}",- "dlSubdir": "+h",- "dlTag": [- "LatestPrerelease",- "Nightly",- "LatestNightly"- ],- "dlUri": "https:omjgcyi"- }- },- "Linux_AmazonLinux": {- "( <= 2.3.4 )": {- "dlCSize": 0,- "dlHash": "hothb",- "dlOutput": "D]\u0008*\u0014",- "dlSubdir": "D\u0003\u0012O#",- "dlTag": [- "Recommended",- "Recommended",- "Recommended",- "Prerelease",- "LatestNightly",- "LatestPrerelease"- ],- "dlUri": "https:yv"- },- "( == 2.1.1 && < 4.2.2 ) || ( < 2.3.1 )": {- "dlCSize": 5,- "dlHash": "nw",- "dlOutput": null,- "dlSubdir": "Q\u0006\\F",- "dlTag": [- "%𤰙Y4T",- "Prerelease",- "old",- "Nightly",- "LatestNightly"- ],- "dlUri": "http:"- },- "( == 2.6.5 && == 3.1.5 && > 1.3.1 && < 2.3.3 )": {- "dlCSize": 1,- "dlHash": "",- "dlOutput": "\u001az9𰋒N",- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:jxecej"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "iqvf",- "dlOutput": "\t5i",- "dlSubdir": "\u0016x",- "dlTag": [- "base-5.1.3",- "Latest",- "LatestPrerelease",- "Prerelease",- "old"- ],- "dlUri": "https:r"- }- },- "Linux_Mint": {- "( < 5.4.1 ) || ( <= 3.2.4 )": {- "dlCSize": -3,- "dlHash": "",- "dlOutput": "d$",- "dlSubdir": {- "RegexDir": "𗿦u6"- },- "dlTag": [- "Latest",- "base-3.2.2",- "LatestPrerelease",- "Experimental"- ],- "dlUri": "https:ouccox"- },- "( <= 5.3.1 && == 1.3.3 && == 2.1.2 ) || ( > 2.4.2 && == 1.3.2 && > 4.5.1 && == 4.3.2 ) || ( >= 2.2.2 && >= 3.2.1 && >= 2.3.2 ) || ( >= 2.2.2 && == 3.2.1 )": {- "dlCSize": 7,- "dlHash": "",- "dlOutput": "",- "dlSubdir": null,- "dlTag": [- "Prerelease",- "Nightly"- ],- "dlUri": "http:htumd"- },- "( > 1.5.3 && < 2.5.5 && < 3.1.5 && > 4.1.5 )": {- "dlCSize": null,- "dlHash": "kxeomhq",- "dlOutput": "𩼲",- "dlSubdir": {- "RegexDir": "|C𫾝S"- },- "dlTag": [- "Nightly"- ],- "dlUri": "https:trnup"- },- "( >= 2.2.1 && < 5.1.2 && > 2.5.2 && >= 4.3.5 && >= 5.3.5 ) || ( > 3.3.2 && >= 3.1.3 && < 3.2.2 && < 4.3.2 && == 2.3.3 && < 3.1.1 )": {- "dlCSize": 3,- "dlHash": "rsrml",- "dlOutput": "",- "dlSubdir": "D*T",- "dlTag": [- "LatestPrerelease",- "LatestPrerelease",- "LatestNightly"- ],- "dlUri": "https:abymyoi"- },- "( >= 6.4.2 && <= 2.2.5 && < 5.5.1 && == 3.1.5 ) || ( <= 2.3.3 && >= 2.2.2 && > 3.1.3 ) || ( >= 1.2.2 && < 3.2.2 )": {- "dlCSize": 0,- "dlHash": "bfyobhv",- "dlOutput": "",- "dlSubdir": null,- "dlTag": [- "old",- "",- "LatestNightly",- "Nightly",- "old",- "Recommended"- ],- "dlUri": "http:idvu"- },- "unknown_versioning": {- "dlCSize": -3,- "dlHash": "",- "dlOutput": null,- "dlSubdir": " \u0000y",- "dlTag": [- "base-6.5.4"- ],- "dlUri": "http:hh"- }- },- "Linux_RedHat": {- "( < 3.2.1 && >= 2.1.2 && > 1.6.5 && >= 3.4.4 && == 1.5.1 && > 4.1.1 )": {- "dlCSize": -5,- "dlHash": "",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "𩃀UR𮆽"- },- "dlTag": [- "LatestPrerelease",- "Latest",- "Nightly"- ],- "dlUri": "https:wqvr"- },- "( == 1.2.2 && <= 1.1.1 ) || ( <= 2.1.4 && >= 2.2.5 && < 4.1.1 && < 3.1.5 )": {- "dlCSize": 3,- "dlHash": "",- "dlOutput": "#5㨄$",- "dlSubdir": "hWB",- "dlTag": [- "LatestNightly"- ],- "dlUri": "https:uvvym"- },- "( > 1.4.2 && >= 5.4.2 && > 5.1.3 && >= 1.5.4 && >= 3.3.4 && < 2.2.4 && == 3.3.3 ) || ( < 3.2.3 && == 1.1.3 && >= 5.3.4 )": {- "dlCSize": 5,- "dlHash": "jtozy",- "dlOutput": null,- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:ooqmx"- },- "( >= 5.2.4 && >= 2.6.2 ) || ( > 2.2.2 && <= 2.3.1 ) || ( < 3.3.2 && >= 2.3.2 && < 2.3.1 && > 1.2.3 && < 2.3.1 ) || ( <= 2.1.2 && > 1.1.1 ) || ( >= 1.1.1 && < 1.1.1 ) || ( <= 1.2.1 ) || ( <= 1.1.1 ) || ( > 1.1.1 )": {- "dlCSize": -1,- "dlHash": "",- "dlOutput": "\u0018I\u001a",- "dlSubdir": {- "RegexDir": "xd"- },- "dlTag": null,- "dlUri": "https:"- },- "( >= 5.4.4 && <= 2.4.1 && > 3.5.5 )": {- "dlCSize": 2,- "dlHash": "k",- "dlOutput": "攤>\u0001X<実",- "dlSubdir": "Y\u000c",- "dlTag": [],- "dlUri": "http:aet"- },- "unknown_versioning": {- "dlCSize": -1,- "dlHash": "mias",- "dlOutput": ".𪕡PO",- "dlSubdir": {- "RegexDir": "\u00127J"- },- "dlTag": null,- "dlUri": "http:xgqvaxv"- }- },- "Windows": {- "( < 1.1.1 && <= 4.4.5 && <= 3.2.5 && <= 4.4.5 && < 2.1.4 && <= 2.3.5 )": {- "dlCSize": null,- "dlHash": "liccth",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "`,'q"- },- "dlTag": [- "Nightly",- "Recommended",- "Latest",- "base-4.6.3"- ],- "dlUri": "http:cr"- },- "( < 3.3.5 && <= 1.1.5 && >= 5.1.2 ) || ( == 4.4.4 && <= 2.2.4 && == 4.1.1 && <= 3.2.1 )": {- "dlCSize": 7,- "dlHash": "azlzsuh",- "dlOutput": null,- "dlSubdir": "NES\u001f",- "dlTag": null,- "dlUri": "http:x"- },- "( == 3.5.4 && > 3.4.3 && < 2.2.3 && <= 3.5.5 && <= 1.4.2 && == 2.2.2 )": {- "dlCSize": 3,- "dlHash": "fklgn",- "dlOutput": "𥮏=M",- "dlSubdir": {- "RegexDir": "\u0000\"!QR"- },- "dlTag": null,- "dlUri": "http:bdplzf"- },- "( == 5.5.2 && > 1.1.5 && <= 4.1.4 && == 5.1.2 && < 3.2.5 ) || ( <= 2.4.4 && < 1.3.1 && >= 3.2.1 && >= 1.2.4 ) || ( == 1.3.1 && > 1.2.2 && == 2.3.1 && >= 3.2.1 )": {- "dlCSize": -2,- "dlHash": "",- "dlOutput": "Z𠔖 \u000b",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "base-2.6.3",- "Recommended",- "old",- "Experimental"- ],- "dlUri": "https:zlmmza"- },- "( > 4.5.5 && <= 1.5.3 ) || ( >= 1.2.4 && < 2.4.3 )": {- "dlCSize": -5,- "dlHash": "rvgzqha",- "dlOutput": null,- "dlSubdir": "1}",- "dlTag": [- "Prerelease",- "LatestNightly",- "Prerelease",- "ɜjz",- "Nightly"- ],- "dlUri": "https:z"- },- "( >= 3.3.4 ) || ( == 3.2.2 && < 2.1.1 && == 4.3.3 )": {- "dlCSize": 6,- "dlHash": "plhh",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "\u001a"- },- "dlTag": null,- "dlUri": "https:"- },- "unknown_versioning": {- "dlCSize": 7,- "dlHash": "ymisz",- "dlOutput": null,- "dlSubdir": "J2𰿤",- "dlTag": [- "old",- "y \u0007\u001f",- "Nightly",- "Latest",- "Prerelease",- "LatestPrerelease",- "Recommended"- ],- "dlUri": "https:vw"- }- }- }- },- "viChangeLog": "https:rjtn",- "viPostInstall": "pckwp",- "viPostRemove": "t",- "viPreCompile": null,- "viPreInstall": "amr",- "viReleaseDay": "-24610868170639988-07-05",- "viSourceDL": {- "dlCSize": -6,- "dlHash": "zlk",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [],- "dlUri": "https:uvyxt"- },- "viTags": [- "old",- "old",- "Prerelease",- "",- "base-5.4.1",- "Experimental"- ],- "viTestDL": {- "dlCSize": null,- "dlHash": "mrw",- "dlOutput": "|\u0000,",- "dlSubdir": "\u00113𧌛",- "dlTag": [- "LatestNightly",- "1Y_"- ],- "dlUri": "https:virs"- }- },- "4.2.1": {- "viArch": {- "A_32": {- "FreeBSD": {- "( < 5.2.2 && >= 2.4.5 ) || ( < 3.2.4 )": {- "dlCSize": 4,- "dlHash": "qqf",- "dlOutput": null,- "dlSubdir": "5Oc",- "dlTag": [- "Nightly",- "Latest"- ],- "dlUri": "http:jtbr"- },- "( == 3.1.5 )": {- "dlCSize": 2,- "dlHash": "uyitmss",- "dlOutput": "]",- "dlSubdir": null,- "dlTag": [- "Experimental",- "LatestNightly"- ],- "dlUri": "https:bqm"- },- "( == 5.5.5 && == 4.4.2 && > 1.3.2 && >= 3.5.5 && > 3.5.1 && > 1.1.5 )": {- "dlCSize": 6,- "dlHash": "",- "dlOutput": "m\u000cIJ\u000c",- "dlSubdir": "𭬺",- "dlTag": [- "\u001arBK",- "Bnq",- "Experimental",- "Experimental"- ],- "dlUri": "http:zdqt"- },- "unknown_versioning": {- "dlCSize": 0,- "dlHash": "eolkn",- "dlOutput": "n\u001d&",- "dlSubdir": "PnJ",- "dlTag": null,- "dlUri": "https:"- }- }- },- "A_ARM": {},- "A_ARM64": {- "Linux_Alpine": {- "( < 1.5.4 && >= 1.2.5 && == 1.5.3 && == 3.3.2 ) || ( >= 1.2.4 && == 5.3.1 && == 1.3.2 && > 3.2.4 && == 4.2.2 && == 3.1.2 ) || ( >= 4.1.3 && >= 2.4.1 && <= 2.1.1 && > 2.3.2 && < 1.2.3 ) || ( < 3.1.2 && == 2.1.2 )": {- "dlCSize": null,- "dlHash": "ozx",- "dlOutput": "\u001fEv",- "dlSubdir": {- "RegexDir": "ꋑ㏄TA"- },- "dlTag": [],- "dlUri": "https:n"- },- "( == 3.5.3 && <= 3.4.3 && <= 3.1.1 && == 3.5.2 ) || ( == 3.2.1 && > 2.3.1 && > 3.3.1 && == 4.1.4 && > 1.4.3 ) || ( == 1.3.1 )": {- "dlCSize": -4,- "dlHash": "gtdl",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "\u0001u\u001b"- },- "dlTag": [- "old",- "base-5.2.2",- "",- "Prerelease",- "LatestNightly",- "Nightly",- "Latest"- ],- "dlUri": "https:"- },- "unknown_versioning": {- "dlCSize": 0,- "dlHash": "ubxrft",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "\u001b$ZWf&"- },- "dlTag": [- "LatestPrerelease",- "LatestPrerelease"- ],- "dlUri": "https:nugrj"- }- }- },- "A_PowerPC": {- "Darwin": {- "( < 5.4.2 )": {- "dlCSize": 2,- "dlHash": "bsasg",- "dlOutput": "\u001a",- "dlSubdir": {- "RegexDir": "OꉰuT"- },- "dlTag": [- "Recommended",- "LatestNightly",- "old",- "Experimental",- "Experimental"- ],- "dlUri": "http:h"- },- "( <= 4.4.2 && <= 2.4.5 ) || ( == 3.2.1 && > 3.1.1 && < 2.4.2 ) || ( <= 1.3.3 )": {- "dlCSize": 2,- "dlHash": "cbzdk",- "dlOutput": "𪣸줿\ts",- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:ewdmm"- },- "( > 2.2.3 && > 4.3.5 && < 1.1.3 && == 5.1.3 && == 3.5.1 && < 5.2.5 ) || ( < 3.3.3 && >= 3.3.3 ) || ( > 1.2.1 && <= 3.1.2 && >= 2.3.2 && > 3.2.3 && >= 2.2.3 )": {- "dlCSize": null,- "dlHash": "vlkxrw",- "dlOutput": "R\u0005Xo\u000c",- "dlSubdir": {- "RegexDir": "9"- },- "dlTag": null,- "dlUri": "http:iuy"- },- "unknown_versioning": {- "dlCSize": -6,- "dlHash": "",- "dlOutput": null,- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:d"- }- },- "FreeBSD": {- "( <= 2.5.3 )": {- "dlCSize": 0,- "dlHash": "zdknvdb",- "dlOutput": "U𨮺\u0006O,Y",- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:p"- },- "unknown_versioning": {- "dlCSize": 2,- "dlHash": "glvod",- "dlOutput": "Z\" ",- "dlSubdir": "q",- "dlTag": [- "LatestNightly",- "LatestNightly"- ],- "dlUri": "http:jowrkid"- }- },- "Linux_Debian": {- "( < 1.2.2 && == 4.1.1 && <= 3.6.3 && <= 4.3.3 && > 5.5.3 && > 5.5.2 ) || ( <= 3.2.1 && >= 4.2.3 ) || ( <= 1.3.3 && < 1.3.2 && == 1.2.1 )": {- "dlCSize": 0,- "dlHash": "otyr",- "dlOutput": "AfO",- "dlSubdir": null,- "dlTag": [- "Experimental",- "Nightly"- ],- "dlUri": "http:gutfwqk"- },- "( == 3.4.2 )": {- "dlCSize": -3,- "dlHash": "b",- "dlOutput": "7\u0006*\u0003",- "dlSubdir": {- "RegexDir": "(Y鞌뿊|"- },- "dlTag": null,- "dlUri": "https:q"- },- "unknown_versioning": {- "dlCSize": 7,- "dlHash": "h",- "dlOutput": ",",- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:vzuiib"- }- },- "Linux_Exherbo": {- "( == 1.4.3 && == 3.4.4 && == 2.3.1 ) || ( > 1.2.3 && == 1.4.4 ) || ( == 2.1.1 ) || ( < 2.1.2 && > 1.2.2 && < 1.2.2 && < 1.2.2 )": {- "dlCSize": 2,- "dlHash": "wolqmja",- "dlOutput": "}𐆓o",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": null,- "dlUri": "http:vc"- },- "( >= 3.4.4 && < 3.1.2 && >= 5.5.1 && >= 4.5.5 && >= 1.1.2 && > 5.1.4 ) || ( > 2.1.3 && >= 2.1.1 && < 2.4.3 && < 1.2.2 && <= 2.2.1 && >= 4.3.2 ) || ( < 2.3.2 )": {- "dlCSize": 3,- "dlHash": "zxrygc",- "dlOutput": null,- "dlSubdir": "~Q&\u001d",- "dlTag": [- "LatestNightly",- "old",- "Prerelease",- "Recommended",- "LatestNightly",- "uMhH𬅫*"- ],- "dlUri": "http:"- },- "unknown_versioning": {- "dlCSize": 4,- "dlHash": "",- "dlOutput": "q𩚵㈥\n",- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:ayf"- }- },- "Linux_Fedora": {- "( < 6.3.3 && < 2.3.2 && >= 3.5.5 ) || ( > 1.2.3 && <= 1.5.5 && == 4.2.4 && >= 2.4.1 && <= 2.4.2 )": {- "dlCSize": 7,- "dlHash": "s",- "dlOutput": "/?>\r",- "dlSubdir": "V^\n",- "dlTag": [],- "dlUri": "https:cb"- },- "( >= 5.1.5 && < 4.5.2 && >= 3.5.5 && <= 4.3.2 && < 3.2.4 ) || ( <= 2.1.1 && == 4.1.4 && == 1.3.1 && <= 2.1.1 ) || ( <= 2.3.3 && <= 3.3.3 && <= 1.2.2 && >= 1.3.2 && < 2.1.3 ) || ( < 1.1.2 && >= 1.1.2 && <= 1.1.2 )": {- "dlCSize": null,- "dlHash": "qwgkh",- "dlOutput": "If#=5\u000f",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [],- "dlUri": "http:"- }- },- "Windows": {- "( > 2.1.2 ) || ( <= 2.3.3 && > 2.1.4 && <= 2.2.2 && < 4.1.1 && > 2.1.3 && < 4.3.3 )": {- "dlCSize": -6,- "dlHash": "psu",- "dlOutput": "",- "dlSubdir": "Y\u0010櫦",- "dlTag": [- "Latest",- "old",- "Latest",- "LatestNightly",- "Experimental",- "Recommended",- "base-5.3.3"- ],- "dlUri": "https:ysarf"- },- "( > 5.5.4 && == 2.3.1 && >= 5.1.5 ) || ( <= 1.4.3 ) || ( == 1.1.3 && > 3.2.1 && >= 2.2.2 )": {- "dlCSize": null,- "dlHash": "",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "(\u0010\u0017p"- },- "dlTag": [],- "dlUri": "https:"- },- "unknown_versioning": {- "dlCSize": 1,- "dlHash": "unvlr",- "dlOutput": "\u001f",- "dlSubdir": "𧋢E4$",- "dlTag": [- "Latest",- "base-4.2.6",- "Recommended"- ],- "dlUri": "https:vlp"- }- }- },- "A_PowerPC64": {- "FreeBSD": {},- "Linux_Alpine": {- "( > 2.2.5 && >= 3.5.1 && <= 3.1.2 && >= 2.5.2 && <= 3.1.2 && == 4.2.4 && == 5.4.4 )": {- "dlCSize": 7,- "dlHash": "e",- "dlOutput": "\u0018",- "dlSubdir": "\u0002[",- "dlTag": null,- "dlUri": "http:xgkygba"- }- },- "Linux_Debian": {- "( <= 1.1.3 && == 4.3.4 && > 2.5.4 && > 5.4.2 && > 1.5.3 ) || ( == 2.1.1 && < 3.2.4 )": {- "dlCSize": -4,- "dlHash": "uxklunx",- "dlOutput": "U<fo\u0008\u0000",- "dlSubdir": "b𓁈k",- "dlTag": [- "Latest",- "old",- "Experimental",- "Recommended",- "Latest",- "Recommended"- ],- "dlUri": "https:"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "r",- "dlOutput": "{V\u0001%HO",- "dlSubdir": "𘊆\u000c",- "dlTag": null,- "dlUri": "https:u"- }- },- "Linux_Exherbo": {},- "Windows": {- "( <= 1.5.2 && <= 1.5.1 && >= 2.2.3 && <= 2.2.1 && <= 7.5.2 && <= 1.1.2 )": {- "dlCSize": -3,- "dlHash": "",- "dlOutput": "y}i\u0018w",- "dlSubdir": "Q",- "dlTag": [- "Latest",- "Latest",- "Recommended",- "LatestPrerelease",- "LatestNightly"- ],- "dlUri": "http:joyjye"- },- "( >= 1.5.5 )": {- "dlCSize": 4,- "dlHash": "z",- "dlOutput": "c\u0016",- "dlSubdir": "\u0007\u0016}𑑗2",- "dlTag": null,- "dlUri": "http:nevap"- },- "( >= 5.4.1 && >= 5.2.1 && >= 1.3.3 && <= 5.1.3 ) || ( <= 4.2.4 && > 4.2.4 && > 4.5.3 ) || ( > 3.3.1 && < 3.4.1 && <= 3.3.3 && == 2.2.2 && < 1.3.2 ) || ( > 1.2.2 && == 3.1.1 && < 1.1.2 )": {- "dlCSize": -6,- "dlHash": "niyotv",- "dlOutput": "1𝨂o",- "dlSubdir": {- "RegexDir": "k"- },- "dlTag": [- "Nightly",- "LatestPrerelease"- ],- "dlUri": "http:b"- },- "( >= 6.3.2 && > 3.1.5 ) || ( <= 4.3.3 && >= 4.4.2 && <= 1.3.2 )": {- "dlCSize": -5,- "dlHash": "j",- "dlOutput": "",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "Recommended"- ],- "dlUri": "http:"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "vt",- "dlOutput": "䜔\u000b(\u0013",- "dlSubdir": "\t𔕾kyG!",- "dlTag": [- "LatestNightly",- "Latest",- "Prerelease",- "LatestPrerelease",- "Prerelease",- "old",- "Experimental"- ],- "dlUri": "http:czwgy"- }- }- },- "A_Sparc64": {- "Darwin": {- "( < 5.3.5 && >= 1.3.4 )": {- "dlCSize": null,- "dlHash": "guentr",- "dlOutput": "\u0008",- "dlSubdir": "g)X밿>u",- "dlTag": null,- "dlUri": "http:"- },- "( <= 1.4.1 && <= 5.1.2 && <= 4.4.2 && >= 5.3.5 && == 3.1.2 && <= 1.2.1 && > 5.3.3 ) || ( <= 3.2.3 && <= 4.1.2 && < 3.1.1 && == 3.3.2 && > 2.1.4 ) || ( > 2.2.3 && > 1.1.3 && == 1.1.1 && > 2.1.3 ) || ( < 2.2.1 ) || ( >= 1.2.2 && <= 1.1.1 ) || ( <= 1.2.1 ) || ( == 1.1.1 )": {- "dlCSize": 0,- "dlHash": "iqa",- "dlOutput": "?𗽅",- "dlSubdir": {- "RegexDir": "\u001e\u0016;q"- },- "dlTag": [- "base-6.5.3",- "Latest",- "Experimental",- "LatestPrerelease",- "?"- ],- "dlUri": "https:"- },- "( <= 5.1.3 && <= 2.2.1 && > 3.4.2 )": {- "dlCSize": 5,- "dlHash": "hobqlar",- "dlOutput": "X!\u0017X𩇊",- "dlSubdir": "bZ𑆌J",- "dlTag": [- "Latest",- ",P\u0017",- "Recommended",- "s𦆨M]",- "Prerelease",- "LatestNightly"- ],- "dlUri": "http:lqwmz"- },- "( > 5.5.5 ) || ( <= 2.2.4 && < 1.3.3 && > 4.1.2 && == 2.2.4 && < 4.2.1 && == 1.4.2 )": {- "dlCSize": null,- "dlHash": "tbhtnso",- "dlOutput": null,- "dlSubdir": "",- "dlTag": [],- "dlUri": "http:xkkrb"- },- "( >= 3.5.4 && >= 4.6.4 && >= 2.5.4 && <= 5.1.4 && <= 5.5.4 && <= 3.2.3 && < 3.5.5 ) || ( <= 4.1.3 && < 4.5.4 && <= 2.1.3 )": {- "dlCSize": 5,- "dlHash": "yaq",- "dlOutput": "h\u001b",- "dlSubdir": {- "RegexDir": "if"- },- "dlTag": [- "old",- "Latest"- ],- "dlUri": "http:jc"- },- "( >= 4.1.3 && == 2.4.2 && <= 3.1.4 && >= 5.5.2 )": {- "dlCSize": 2,- "dlHash": "rhg",- "dlOutput": "M&",- "dlSubdir": "\u001b^琼",- "dlTag": null,- "dlUri": "http:q"- },- "unknown_versioning": {- "dlCSize": -7,- "dlHash": "l",- "dlOutput": null,- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:w"- }- },- "FreeBSD": {- "( < 3.5.3 && <= 5.5.4 && > 3.4.2 && == 5.3.5 && > 4.2.5 && > 1.2.3 && <= 5.4.4 )": {- "dlCSize": 1,- "dlHash": "ma",- "dlOutput": "WF",- "dlSubdir": null,- "dlTag": [- "LatestNightly",- "Nightly",- "Latest",- "Experimental",- "Latest",- "LatestPrerelease",- "Prerelease"- ],- "dlUri": "https:"- },- "( == 1.2.1 && >= 2.3.3 && >= 3.2.3 )": {- "dlCSize": 3,- "dlHash": "ale",- "dlOutput": "",- "dlSubdir": null,- "dlTag": [- "Prerelease",- "Experimental",- "LatestPrerelease",- "\u0017i\u0012\u0004sm",- "Recommended",- "old"- ],- "dlUri": "https:xtbe"- }- },- "Linux_Alpine": {- "unknown_versioning": {- "dlCSize": -2,- "dlHash": "y",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "+\u00184?"- },- "dlTag": [],- "dlUri": "http:ivhil"- }- },- "Linux_Ubuntu": {- "( < 1.1.1 && > 3.5.2 && >= 4.4.2 && > 2.4.5 && > 1.1.4 )": {- "dlCSize": 7,- "dlHash": "euxrl",- "dlOutput": "J",- "dlSubdir": {- "RegexDir": "e++'\u0008"- },- "dlTag": [- "Nightly"- ],- "dlUri": "https:lypoc"- },- "( <= 4.1.2 && == 5.5.2 && < 1.5.4 && <= 3.4.1 && >= 4.1.2 && == 5.1.2 && <= 4.4.4 )": {- "dlCSize": 7,- "dlHash": "vyqcxr",- "dlOutput": "~D",- "dlSubdir": null,- "dlTag": [],- "dlUri": "https:dv"- },- "( == 2.4.3 && >= 2.1.4 && <= 5.5.3 && > 3.1.5 && > 3.5.5 && > 1.5.4 )": {- "dlCSize": 6,- "dlHash": "o",- "dlOutput": "\u0001|𤙑",- "dlSubdir": "\u0003%Q",- "dlTag": [- "Latest",- "Experimental"- ],- "dlUri": "https:imb"- },- "( == 3.3.4 && < 3.4.3 && == 5.5.1 && <= 5.1.6 )": {- "dlCSize": -2,- "dlHash": "city",- "dlOutput": "\\",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "Recommended"- ],- "dlUri": "https:bsytvq"- },- "( == 5.1.3 )": {- "dlCSize": -3,- "dlHash": "h",- "dlOutput": "",- "dlSubdir": null,- "dlTag": [- "\u001d21",- "LatestNightly",- "Prerelease",- "LatestNightly"- ],- "dlUri": "https:"- },- "( >= 2.3.3 && <= 5.5.5 && > 5.2.1 ) || ( > 4.3.3 && == 4.2.3 && == 3.1.4 ) || ( > 1.2.2 && == 1.1.3 && == 3.1.2 ) || ( < 2.2.2 && < 1.2.1 && < 2.2.1 && < 1.1.2 )": {- "dlCSize": -7,- "dlHash": "",- "dlOutput": "]L[",- "dlSubdir": "<EM",- "dlTag": [- "Prerelease",- "Nightly",- "Prerelease"- ],- "dlUri": "http:ku"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "e",- "dlOutput": "b",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "ꥵt"- ],- "dlUri": "https:"- }- },- "Windows": {- "( > 3.2.5 && > 4.2.3 && < 1.3.2 ) || ( > 1.4.1 && <= 5.3.4 )": {- "dlCSize": -4,- "dlHash": "pggkj",- "dlOutput": "i𤨶F\u001a",- "dlSubdir": "hi𲍏𮃥l",- "dlTag": [- "LatestNightly",- "Experimental",- "Experimental",- "LatestPrerelease",- "Recommended"- ],- "dlUri": "http:fmnuwvr"- }- }- }- },- "viChangeLog": "https:cx",- "viPostInstall": "cswgna",- "viPostRemove": "g",- "viPreCompile": "mxcsqv",- "viPreInstall": "bzguld",- "viReleaseDay": null,- "viSourceDL": null,- "viTags": [- "Latest"- ],- "viTestDL": {- "dlCSize": 2,- "dlHash": "soj",- "dlOutput": "\u000bx.i[",- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:lqepg"- }- },- "5.8.7": {- "viArch": {- "A_ARM": {- "Darwin": {- "( == 3.3.4 ) || ( >= 1.4.4 && < 4.1.2 && > 2.4.3 && == 2.4.4 )": {- "dlCSize": null,- "dlHash": "jamb",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "𬃆S\r戝@"- },- "dlTag": null,- "dlUri": "http:"- },- "( == 5.2.4 && > 4.2.1 && == 3.2.1 && < 2.1.5 && > 2.2.4 && > 4.4.4 ) || ( == 1.4.1 && < 3.2.3 ) || ( > 2.3.3 && == 4.3.3 && >= 2.1.3 && == 2.2.2 ) || ( == 2.2.3 && < 2.1.2 && >= 1.1.1 && < 1.2.1 )": {- "dlCSize": 7,- "dlHash": "xhwmyw",- "dlOutput": null,- "dlSubdir": {- "RegexDir": ",icW"- },- "dlTag": [- "Prerelease",- "LatestPrerelease",- "old",- "Latest"- ],- "dlUri": "http:bpijwo"- },- "( > 3.5.4 ) || ( == 1.3.4 && > 3.2.4 )": {- "dlCSize": -2,- "dlHash": "tncs",- "dlOutput": "V",- "dlSubdir": {- "RegexDir": "u"- },- "dlTag": [],- "dlUri": "https:bbsyej"- },- "( > 4.1.4 && <= 4.2.4 && <= 2.5.3 && < 5.5.2 && >= 1.2.5 && <= 3.3.5 ) || ( > 3.1.2 && <= 1.2.2 && <= 1.3.5 )": {- "dlCSize": null,- "dlHash": "i",- "dlOutput": "N1s7𫦳y",- "dlSubdir": {- "RegexDir": "(<趂u\u0015:"- },- "dlTag": null,- "dlUri": "https:rrqqv"- },- "( >= 1.2.1 && >= 2.2.5 && == 2.5.4 ) || ( <= 4.3.4 && < 4.3.4 ) || ( == 1.3.2 && < 1.3.2 && < 2.2.2 && == 1.3.4 ) || ( >= 1.1.1 && >= 2.2.2 && == 1.1.1 )": {- "dlCSize": null,- "dlHash": "kvoi",- "dlOutput": "Qx\u0015\u001emW",- "dlSubdir": {- "RegexDir": "]`\u001d;pv"- },- "dlTag": [- "Recommended",- "LatestPrerelease",- "LatestNightly",- "Nightly",- "\"!ⲕ",- "Prerelease",- "Prerelease"- ],- "dlUri": "https:szjihfe"- },- "( >= 2.4.5 ) || ( <= 1.1.6 && > 4.2.3 && < 1.2.4 && > 2.4.4 && == 3.3.2 && > 4.3.2 )": {- "dlCSize": null,- "dlHash": "pwvvnm",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "NM\u001e,"- },- "dlTag": [- "base-3.4.4"- ],- "dlUri": "https:yl"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "rrmn",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "t>>𦟯R4"- },- "dlTag": [- "Nightly",- "LatestPrerelease",- "Prerelease",- "base-2.3.1",- "Prerelease",- "old"- ],- "dlUri": "https:w"- }- },- "FreeBSD": {- "( <= 5.4.5 && >= 5.5.5 && <= 5.5.5 && <= 5.3.1 && == 5.3.2 && > 1.3.3 ) || ( == 1.2.3 && >= 4.1.3 )": {- "dlCSize": null,- "dlHash": "",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "\u0003\u001aeNu"- },- "dlTag": null,- "dlUri": "https:mqyrvs"- },- "( == 4.2.2 && == 3.1.4 ) || ( >= 3.1.2 )": {- "dlCSize": null,- "dlHash": "hz",- "dlOutput": "K^q𦺁",- "dlSubdir": {- "RegexDir": "朕"- },- "dlTag": null,- "dlUri": "http:opas"- },- "( == 5.5.1 && == 3.1.3 )": {- "dlCSize": 0,- "dlHash": "qaxz",- "dlOutput": "\u0010\u000e\u001c\u0005>",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "base-2.4.2",- "Latest",- "VY2𪢠)𘥖",- "base-5.3.4",- "base-5.3.4"- ],- "dlUri": "http:"- },- "( > 5.3.1 ) || ( <= 4.1.3 && <= 1.2.2 && >= 2.1.3 && == 3.2.2 )": {- "dlCSize": -6,- "dlHash": "xkghz",- "dlOutput": ",𗘴\u0002𐡋\rw",- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:onqrtyw"- },- "( >= 2.1.3 && == 4.1.3 && < 1.1.5 && == 1.5.4 && == 4.5.2 && > 1.3.3 && > 3.3.1 )": {- "dlCSize": 1,- "dlHash": "iggtp",- "dlOutput": "`\u0008+E䢌7",- "dlSubdir": {- "RegexDir": "\u000e𧺚>"- },- "dlTag": [- "I,\u000b텗",- "Experimental",- "Prerelease",- "base-5.4.4",- "𮄾𣢰\u000b6",- "Nightly"- ],- "dlUri": "http:tvukht"- }- },- "Linux_Debian": {- "( <= 5.2.5 && >= 1.2.5 && < 2.4.2 && <= 2.2.1 && <= 4.1.1 ) || ( >= 4.2.3 && > 3.1.2 && <= 4.1.3 && < 4.2.2 && == 4.1.1 && > 1.4.1 ) || ( == 1.1.3 && > 3.2.1 && <= 1.3.3 && == 3.3.3 && <= 3.1.2 )": {- "dlCSize": 5,- "dlHash": "",- "dlOutput": "e]\u0001u",- "dlSubdir": {- "RegexDir": "`\u0016"- },- "dlTag": [- "old",- "old"- ],- "dlUri": "https:"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "cpunfek",- "dlOutput": "{\"",- "dlSubdir": "\u00081\u0011",- "dlTag": [- "Recommended",- "Nightly",- "Latest",- "Experimental",- "Latest"- ],- "dlUri": "https:l"- }- },- "Windows": {- "( <= 2.4.1 && < 5.1.2 && >= 4.3.1 && <= 1.4.1 && == 5.5.1 )": {- "dlCSize": 1,- "dlHash": "eq",- "dlOutput": "Y\u0010\u000e\u000c\u0012",- "dlSubdir": "%𬁁<",- "dlTag": null,- "dlUri": "https:tg"- },- "( <= 3.5.5 && <= 3.1.5 && < 4.3.1 && <= 4.4.1 && >= 1.1.2 )": {- "dlCSize": -5,- "dlHash": "uodlhxa",- "dlOutput": "",- "dlSubdir": "",- "dlTag": null,- "dlUri": "https:"- },- "( == 3.5.4 && >= 3.2.3 && > 3.4.1 )": {- "dlCSize": null,- "dlHash": "erdegjk",- "dlOutput": null,- "dlSubdir": "讃",- "dlTag": [],- "dlUri": "https:vz"- },- "( >= 2.1.4 ) || ( < 3.3.1 && == 4.1.2 && >= 1.4.1 )": {- "dlCSize": null,- "dlHash": "gumj",- "dlOutput": "\u001a",- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:wykdw"- },- "( >= 2.4.2 && >= 2.4.4 && <= 4.4.2 && >= 3.3.2 && > 5.4.3 && == 5.3.4 )": {- "dlCSize": -7,- "dlHash": "vkmdkgw",- "dlOutput": "",- "dlSubdir": "",- "dlTag": null,- "dlUri": "http:"- }- }- }- },- "viChangeLog": null,- "viPostInstall": "nrmxph",- "viPostRemove": "vabzro",- "viPreCompile": null,- "viPreInstall": "cfyw",- "viReleaseDay": null,- "viSourceDL": {- "dlCSize": 3,- "dlHash": "dnzbvt",- "dlOutput": "",- "dlSubdir": "4\u0012𜾾R",- "dlTag": [- "Latest",- "base-4.1.1"- ],- "dlUri": "http:h"- },- "viTags": [- "Experimental",- "Prerelease",- "old",- "Nightly"- ],- "viTestDL": {- "dlCSize": null,- "dlHash": "",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "I"- },- "dlTag": [- "Nightly"- ],- "dlUri": "https:tpnlb"- }- },- "8.4.2": {- "viArch": {- "A_PowerPC": {- "Darwin": {},- "Linux_Debian": {- "( < 3.2.3 && < 2.3.5 && < 3.1.1 && >= 2.2.4 && > 4.2.2 && == 5.4.3 && < 1.1.2 )": {- "dlCSize": -2,- "dlHash": "oake",- "dlOutput": "",- "dlSubdir": " ၰY",- "dlTag": [- "base-3.2.4",- "Prerelease",- "LatestNightly",- "Latest",- "Recommended"- ],- "dlUri": "http:uudjbeh"- },- "( < 3.2.4 && > 5.2.2 && > 3.1.4 && == 5.2.5 && == 5.5.2 && <= 2.4.4 && >= 3.3.5 ) || ( == 3.1.1 ) || ( < 2.2.3 && == 3.1.4 && <= 1.1.1 )": {- "dlCSize": 3,- "dlHash": "aah",- "dlOutput": "\t@P\u0007E",- "dlSubdir": {- "RegexDir": "6傭''"- },- "dlTag": [- "Prerelease",- "Nightly",- "Latest",- "LatestNightly"- ],- "dlUri": "http:c"- },- "( < 4.3.5 )": {- "dlCSize": null,- "dlHash": "inf",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Prerelease",- "Nightly",- "Experimental",- "LatestNightly",- "old",- "Latest"- ],- "dlUri": "https:"- },- "( < 5.1.1 && <= 1.3.4 && >= 2.4.4 ) || ( >= 1.2.3 && < 4.3.4 && == 4.1.3 && > 4.4.2 ) || ( == 2.1.1 && < 3.1.2 && == 1.4.1 && < 1.3.2 && >= 2.3.3 ) || ( >= 2.1.2 && == 2.2.1 && == 2.2.1 ) || ( <= 1.1.1 && < 2.1.1 && >= 1.1.1 )": {- "dlCSize": -3,- "dlHash": "z",- "dlOutput": "*z{Y",- "dlSubdir": "\nUuD\u001f",- "dlTag": null,- "dlUri": "http:zan"- },- "( == 5.3.3 && <= 2.3.3 ) || ( < 4.3.4 && < 5.4.4 && >= 1.3.2 && == 1.2.1 && <= 1.4.2 )": {- "dlCSize": 3,- "dlHash": "fmofl",- "dlOutput": "\u0012X*ꇛmk",- "dlSubdir": null,- "dlTag": [- "LatestNightly"- ],- "dlUri": "http:xgw"- },- "( > 2.3.4 && == 2.4.1 ) || ( > 5.3.5 && >= 2.2.2 && == 3.3.1 && > 2.2.2 ) || ( == 1.3.3 && > 3.2.2 ) || ( <= 2.1.1 )": {- "dlCSize": -3,- "dlHash": "td",- "dlOutput": "\u0004\u0014\u0000d;䞏",- "dlSubdir": {- "RegexDir": "𰼚`rp"- },- "dlTag": [],- "dlUri": "https:qsy"- },- "( >= 1.1.1 && < 2.1.2 && < 5.4.4 && > 5.2.5 && < 3.4.2 && >= 4.1.1 ) || ( <= 3.4.4 ) || ( >= 1.2.2 && == 1.3.2 && >= 1.3.3 && < 2.2.1 && <= 2.3.1 )": {- "dlCSize": 7,- "dlHash": "yq",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "텦d"- },- "dlTag": [- "old",- "old",- "Experimental",- "Prerelease",- "Experimental",- "LatestPrerelease"- ],- "dlUri": "https:"- },- "( >= 4.2.1 && >= 4.4.2 )": {- "dlCSize": -1,- "dlHash": "",- "dlOutput": null,- "dlSubdir": "!5]:",- "dlTag": [],- "dlUri": "https:uoltbef"- }- }- },- "A_Sparc": {- "FreeBSD": {- "( < 2.1.4 && > 5.3.3 && == 5.2.3 ) || ( >= 4.2.2 && > 1.5.3 && < 1.2.3 && < 2.4.2 ) || ( <= 3.1.3 && >= 3.2.2 && <= 2.3.1 )": {- "dlCSize": -3,- "dlHash": "fkhdm",- "dlOutput": ".\u0019\u000eF%~",- "dlSubdir": "Y'7",- "dlTag": [- ""- ],- "dlUri": "http:p"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "parxfp",- "dlOutput": "",- "dlSubdir": "D",- "dlTag": null,- "dlUri": "http:jnmhm"- }- }- }- },- "viChangeLog": null,- "viPostInstall": "hk",- "viPostRemove": "qwprb",- "viPreCompile": "jxnoav",- "viPreInstall": "u",- "viReleaseDay": "19934051486464766-02-01",- "viSourceDL": {- "dlCSize": -4,- "dlHash": "v",- "dlOutput": null,- "dlSubdir": "",- "dlTag": null,- "dlUri": "https:gtcg"- },- "viTags": [- "LatestNightly",- "old",- "LatestPrerelease",- "Nightly",- "Recommended",- "LatestNightly",- "old"- ],- "viTestDL": {- "dlCSize": 3,- "dlHash": "vn",- "dlOutput": "",- "dlSubdir": "",- "dlTag": [- "Nightly",- "f`P'",- "Prerelease",- "LatestNightly",- "LatestNightly"- ],- "dlUri": "https:vhl"- }- },- "8.7.6": {- "viArch": {- "A_32": {- "FreeBSD": {- "( < 1.1.4 && > 3.5.4 && > 5.1.4 && <= 1.3.2 && <= 5.5.4 ) || ( > 1.4.3 && <= 1.3.2 && < 2.4.1 )": {- "dlCSize": 2,- "dlHash": "c",- "dlOutput": "M𒄧",- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:prex"- },- "( <= 5.4.6 && > 2.4.4 && <= 2.2.4 && >= 5.5.1 && == 2.3.5 ) || ( > 5.3.4 && <= 1.1.3 && <= 2.2.2 )": {- "dlCSize": -6,- "dlHash": "hvgpo",- "dlOutput": "/\u0006",- "dlSubdir": {- "RegexDir": "2닪\u000f"- },- "dlTag": [- "LatestPrerelease",- "LatestNightly",- "old",- "Recommended",- "Experimental"- ],- "dlUri": "https:bpegya"- },- "( == 2.1.3 && == 2.1.3 && < 5.5.2 && == 6.5.2 && > 4.4.2 ) || ( == 4.3.1 && == 1.2.2 && > 1.2.6 && == 1.3.3 && == 3.1.3 && >= 3.4.4 )": {- "dlCSize": -3,- "dlHash": "yhyup",- "dlOutput": "\u001dm3?&\u0018",- "dlSubdir": "",- "dlTag": [- "LatestPrerelease"- ],- "dlUri": "https:i"- },- "( == 3.4.5 && >= 2.3.5 && > 4.3.2 && >= 2.5.3 && >= 5.4.1 )": {- "dlCSize": null,- "dlHash": "hmvpk",- "dlOutput": "\u0000jb",- "dlSubdir": {- "RegexDir": "\u0003𖪈"- },- "dlTag": [- "LatestPrerelease",- "LatestPrerelease",- "Nightly"- ],- "dlUri": "https:rqrkcd"- },- "( > 2.4.4 )": {- "dlCSize": 5,- "dlHash": "",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Latest"- ],- "dlUri": "https:o"- },- "unknown_versioning": {- "dlCSize": -5,- "dlHash": "",- "dlOutput": "h\u001d",- "dlSubdir": "K銴",- "dlTag": null,- "dlUri": "http:"- }- },- "Linux_CentOS": {- "( < 2.2.3 && < 4.1.4 && < 1.4.5 && == 4.5.4 && > 2.3.3 && < 3.2.3 && <= 5.3.3 ) || ( > 2.4.4 ) || ( == 1.4.3 && > 1.1.1 ) || ( >= 1.2.1 )": {- "dlCSize": null,- "dlHash": "qtvqnpr",- "dlOutput": null,- "dlSubdir": "\u000f\\<C熿",- "dlTag": [- "base-1.2.2"- ],- "dlUri": "https:mpy"- },- "( < 4.4.4 && > 3.2.1 && > 3.1.2 ) || ( <= 1.4.3 && > 1.3.4 && == 4.2.1 && <= 3.3.1 )": {- "dlCSize": null,- "dlHash": "ct",- "dlOutput": "",- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:fxlo"- },- "( <= 2.4.1 ) || ( < 4.3.2 ) || ( < 1.2.2 && >= 2.2.1 ) || ( >= 2.2.1 && < 2.2.1 && < 2.1.2 && >= 2.1.1 ) || ( >= 1.2.1 && <= 1.1.2 )": {- "dlCSize": null,- "dlHash": "tkdaczs",- "dlOutput": "E",- "dlSubdir": ")T\u000eh",- "dlTag": [- "Prerelease",- "Experimental",- "Prerelease"- ],- "dlUri": "http:"- }- },- "Linux_RedHat": {- "unknown_versioning": {- "dlCSize": 0,- "dlHash": "dwx",- "dlOutput": "\u00102\"9s\u00062",- "dlSubdir": "",- "dlTag": [- "Experimental",- "LatestNightly",- "Recommended"- ],- "dlUri": "https:rypeki"- }- },- "Windows": {- "( <= 3.2.5 && < 2.3.2 && > 5.6.3 && == 2.4.2 )": {- "dlCSize": null,- "dlHash": "thdaqe",- "dlOutput": "",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "Latest"- ],- "dlUri": "http:ayuhoj"- },- "( == 2.1.4 && == 1.2.2 && <= 3.3.1 && > 4.4.3 )": {- "dlCSize": null,- "dlHash": "sf",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Recommended",- ".",- "Nightly",- "old",- "Recommended"- ],- "dlUri": "https:irwc"- },- "( > 4.3.1 && <= 3.4.3 && <= 3.5.1 && < 2.5.4 && >= 2.4.2 ) || ( < 2.2.2 && >= 2.2.3 && == 2.2.4 && > 2.4.2 )": {- "dlCSize": -2,- "dlHash": "tqmns",- "dlOutput": "\u0001",- "dlSubdir": "",- "dlTag": [- "Recommended"- ],- "dlUri": "http:"- },- "( >= 2.2.5 && < 1.4.5 && >= 3.2.3 )": {- "dlCSize": -4,- "dlHash": "f",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "R𤃒\\!"- },- "dlTag": null,- "dlUri": "https:va"- }- }- },- "A_64": {- "FreeBSD": {- "( < 3.4.4 && <= 4.4.2 )": {- "dlCSize": -3,- "dlHash": "edeeu",- "dlOutput": "\\/bWI",- "dlSubdir": null,- "dlTag": [],- "dlUri": "https:vmgy"- },- "( >= 1.4.2 ) || ( <= 4.2.2 && == 4.3.2 ) || ( == 2.2.3 && >= 1.4.1 && < 1.2.2 && <= 3.1.2 && == 1.2.4 ) || ( <= 2.2.1 ) || ( >= 1.1.1 && >= 1.1.1 && < 1.1.1 )": {- "dlCSize": -2,- "dlHash": "t",- "dlOutput": "=\u0017",- "dlSubdir": {- "RegexDir": "𰙌{_𘲅\\᳙"- },- "dlTag": [- "",- "Latest",- "Latest",- "LatestNightly",- "Latest"- ],- "dlUri": "http:woiqer"- },- "( >= 2.4.1 && > 5.4.6 && == 1.2.3 && <= 4.4.2 && <= 2.1.1 && > 1.5.5 ) || ( >= 3.3.4 && == 2.4.2 && > 1.1.3 && < 4.1.3 && >= 3.3.1 && <= 2.1.4 ) || ( > 3.2.1 && >= 1.2.2 && >= 1.2.1 && <= 2.1.2 ) || ( <= 2.1.3 && < 2.1.2 && <= 1.1.1 ) || ( < 2.1.1 && <= 1.1.1 && <= 1.1.1 )": {- "dlCSize": -6,- "dlHash": "vqsoy",- "dlOutput": "\u0002/Xg",- "dlSubdir": "\r\u0014",- "dlTag": [- "LatestPrerelease"- ],- "dlUri": "https:"- },- "( >= 3.5.3 && == 1.2.1 && <= 3.5.1 && <= 4.4.4 && <= 1.5.2 && > 3.2.5 ) || ( >= 3.4.1 && >= 3.2.3 && >= 3.3.1 && < 4.3.3 ) || ( <= 1.2.3 && >= 3.2.2 && >= 2.2.4 )": {- "dlCSize": 1,- "dlHash": "n",- "dlOutput": "\u000e",- "dlSubdir": {- "RegexDir": "Jni"- },- "dlTag": [],- "dlUri": "http:bwwfhoy"- }- },- "Linux_AmazonLinux": {- "( < 2.3.5 )": {- "dlCSize": null,- "dlHash": "dchgqk",- "dlOutput": "",- "dlSubdir": "",- "dlTag": [- "Experimental",- "Experimental",- "Nightly",- "Recommended",- "base-5.4.5",- "Latest",- "Nightly"- ],- "dlUri": "http:gvw"- },- "( == 1.2.2 && > 1.5.4 && >= 1.3.4 && > 2.3.1 && == 3.2.1 && == 3.5.5 ) || ( < 2.2.2 ) || ( <= 3.2.2 && > 3.3.1 && >= 2.2.1 ) || ( > 2.1.3 && >= 1.2.1 ) || ( < 1.1.1 && >= 1.1.1 )": {- "dlCSize": -3,- "dlHash": "rmv",- "dlOutput": "D\ni\u0017",- "dlSubdir": {- "RegexDir": "tfs+"- },- "dlTag": [],- "dlUri": "http:vg"- },- "( == 3.1.3 && == 5.2.5 && >= 4.1.4 ) || ( >= 4.4.1 && >= 1.2.4 && >= 1.3.1 && >= 1.1.1 && <= 5.3.3 && >= 1.3.1 )": {- "dlCSize": null,- "dlHash": "ylmn",- "dlOutput": null,- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "base-3.6.6",- "LatestNightly",- "Nightly",- "base-1.5.1"- ],- "dlUri": "http:zivq"- },- "( > 5.1.3 && == 1.4.5 && == 2.3.5 && < 2.1.5 && == 1.2.2 && >= 5.5.1 && == 4.4.3 ) || ( == 3.3.3 && <= 1.1.1 && < 4.1.4 && >= 2.1.4 )": {- "dlCSize": null,- "dlHash": "gcfd",- "dlOutput": "s-b\u0002菹",- "dlSubdir": "𗈝\u0015\u000e\u0008",- "dlTag": [- "LatestNightly",- "Recommended"- ],- "dlUri": "https:qac"- },- "( >= 5.1.5 )": {- "dlCSize": 0,- "dlHash": "bd",- "dlOutput": "䫫t}q",- "dlSubdir": {- "RegexDir": "V藣\u000bu𗖖"- },- "dlTag": [- "old",- "base-1.4.4",- "Nightly",- "old"- ],- "dlUri": "https:hslqrk"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "tjgjhc",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "&}_"- },- "dlTag": [- "old",- "Recommended",- "Latest",- "LatestPrerelease",- "Recommended",- "Prerelease",- "LatestNightly"- ],- "dlUri": "https:t"- }- },- "Linux_CentOS": {- "( < 2.6.2 && > 2.1.2 && <= 1.3.5 && >= 4.4.5 && >= 1.4.2 && > 3.4.5 )": {- "dlCSize": -6,- "dlHash": "",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "𘍞𑨸"- },- "dlTag": [- "old",- "base-5.6.6",- "Latest",- "Latest",- "Latest",- "Latest"- ],- "dlUri": "http:iktwsyx"- },- "( < 4.3.5 && == 1.5.4 && >= 5.3.2 && == 3.1.1 && >= 2.1.1 )": {- "dlCSize": -6,- "dlHash": "saqxln",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "\u0013DH\u0016\u000f"- },- "dlTag": null,- "dlUri": "https:fhr"- },- "( == 3.4.5 && > 3.3.1 ) || ( > 3.2.2 && > 1.4.4 && == 1.4.4 && <= 2.2.3 && == 2.3.3 )": {- "dlCSize": 7,- "dlHash": "ruh",- "dlOutput": "𨔡I\u0018>\\Z",- "dlSubdir": {- "RegexDir": ">𮓴\u001b\u0005"- },- "dlTag": null,- "dlUri": "https:tyu"- },- "unknown_versioning": {- "dlCSize": 7,- "dlHash": "",- "dlOutput": "gC",- "dlSubdir": {- "RegexDir": "d12vᦃ\u001a"- },- "dlTag": null,- "dlUri": "https:"- }- },- "Linux_Debian": {- "( <= 1.3.2 && < 1.3.5 && == 3.1.4 && <= 2.5.2 && <= 6.3.2 && >= 2.3.5 )": {- "dlCSize": 1,- "dlHash": "kqsmsw",- "dlOutput": "-U\u000e\u0001TH",- "dlSubdir": null,- "dlTag": [- "Prerelease",- "",- "old",- "Recommended",- "E.:C",- "Nightly"- ],- "dlUri": "https:l"- },- "( <= 5.5.3 && > 4.1.5 && < 1.3.3 && >= 5.3.4 && > 2.2.4 && < 1.4.4 && >= 4.2.5 ) || ( >= 4.1.2 && > 4.2.3 && <= 3.1.1 ) || ( <= 3.1.3 && < 2.2.2 && >= 2.1.4 && > 2.3.2 && <= 1.1.1 )": {- "dlCSize": 1,- "dlHash": "rbxnq",- "dlOutput": ")%🃪2",- "dlSubdir": {- "RegexDir": "U-\u0019"- },- "dlTag": [- "Latest",- "base-3.2.4",- "base-2.2.2"- ],- "dlUri": "https:cntexx"- },- "( == 2.1.2 && > 3.1.1 && == 1.5.2 && > 4.4.5 && > 6.1.2 && < 3.3.5 )": {- "dlCSize": null,- "dlHash": "hr",- "dlOutput": "i\u0015𥤑",- "dlSubdir": "",- "dlTag": [- "Latest",- "LatestPrerelease",- "old"- ],- "dlUri": "https:geajrro"- },- "( >= 1.3.3 && >= 3.3.3 && > 3.1.2 )": {- "dlCSize": -7,- "dlHash": "epd",- "dlOutput": "#𥼼",- "dlSubdir": null,- "dlTag": [- "Prerelease",- "Latest"- ],- "dlUri": "https:qtjv"- },- "( >= 4.1.1 ) || ( == 2.2.1 && >= 4.4.1 )": {- "dlCSize": -3,- "dlHash": "ycgcn",- "dlOutput": "Q\u001f𫿯\u000c",- "dlSubdir": {- "RegexDir": "\tCR"- },- "dlTag": [- "Nightly",- "\u0019,q1x"- ],- "dlUri": "http:lzqtp"- }- },- "Linux_Ubuntu": {- "( < 1.3.5 && < 1.6.4 && > 2.4.1 && >= 2.4.5 ) || ( >= 4.3.1 && >= 1.1.2 ) || ( <= 2.2.2 && <= 3.1.2 && < 1.2.1 ) || ( < 1.3.2 && < 1.2.1 && >= 1.2.4 ) || ( < 2.2.1 && == 1.1.1 ) || ( == 1.1.1 ) || ( > 2.1.1 ) || ( < 1.1.1 )": {- "dlCSize": 7,- "dlHash": "r",- "dlOutput": "\n䨢z",- "dlSubdir": "𨩠?7",- "dlTag": [- "LatestPrerelease",- "\u001e待𩡊k",- "D!"- ],- "dlUri": "https:"- },- "unknown_versioning": {- "dlCSize": -7,- "dlHash": "rhslpof",- "dlOutput": "𱅉;",- "dlSubdir": null,- "dlTag": [- "Prerelease",- "X\u0018$V𝥚~"- ],- "dlUri": "http:bk"- }- },- "Windows": {- "( < 4.4.5 )": {- "dlCSize": 7,- "dlHash": "hc",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "5"- },- "dlTag": [- "Prerelease",- "base-2.3.5",- "Recommended",- "LatestNightly",- "old",- "Latest",- "Prerelease"- ],- "dlUri": "https:rqiwv"- },- "( <= 1.1.1 && > 2.2.2 && >= 4.1.1 && == 3.1.1 && > 1.2.3 && <= 2.2.5 && <= 5.4.1 )": {- "dlCSize": 3,- "dlHash": "l",- "dlOutput": "UB$𤌎0\u0006",- "dlSubdir": null,- "dlTag": [- "t𤺈"- ],- "dlUri": "http:gmhnh"- },- "( <= 1.4.3 && >= 1.5.4 && <= 2.4.1 && > 3.2.1 ) || ( >= 4.1.1 ) || ( > 2.2.2 && > 3.3.1 ) || ( <= 2.1.3 && >= 2.1.1 && <= 1.2.2 ) || ( < 1.1.2 && > 1.2.1 && < 1.2.1 )": {- "dlCSize": 2,- "dlHash": "rs",- "dlOutput": "",- "dlSubdir": "=",- "dlTag": [],- "dlUri": "http:lrfih"- }- }- },- "A_ARM": {- "Darwin": {- "( < 5.4.5 && >= 5.5.2 && < 1.5.3 && >= 1.4.5 ) || ( > 4.2.2 && == 2.3.4 )": {- "dlCSize": 1,- "dlHash": "fd",- "dlOutput": "/}<u\u001a",- "dlSubdir": null,- "dlTag": [- "old",- "'\u0008l\u0015n",- "old",- "Experimental",- "base-3.6.3"- ],- "dlUri": "https:yfjj"- },- "( <= 3.3.3 && < 2.3.5 && <= 3.2.2 )": {- "dlCSize": 7,- "dlHash": "hnht",- "dlOutput": "\u0001@W芞\u001e1",- "dlSubdir": "\u000bYu",- "dlTag": [- "\u0005\u0002",- "LatestNightly",- "CUU[",- "M;+"- ],- "dlUri": "http:fzq"- },- "( <= 4.5.1 && > 5.3.4 && == 2.1.4 && > 3.3.1 && == 1.4.2 && == 4.2.4 && == 4.3.4 )": {- "dlCSize": 3,- "dlHash": "",- "dlOutput": ")_x",- "dlSubdir": "N\u0014\u001d",- "dlTag": [- "LatestPrerelease",- "",- "LatestNightly",- "Recommended",- ">0h",- "Prerelease"- ],- "dlUri": "https:"- },- "( > 3.2.3 ) || ( >= 4.4.1 && > 3.1.4 && == 3.1.3 && == 4.4.2 )": {- "dlCSize": null,- "dlHash": "vmt",- "dlOutput": "\u0010%",- "dlSubdir": null,- "dlTag": [],- "dlUri": "https:uzrb"- },- "( >= 4.4.3 && > 4.4.3 && > 2.2.5 && == 2.4.3 && <= 4.3.3 ) || ( <= 4.2.5 && <= 2.4.1 && >= 2.5.3 ) || ( <= 2.1.3 && < 2.1.1 ) || ( <= 1.1.1 && == 2.1.3 )": {- "dlCSize": -1,- "dlHash": "coln",- "dlOutput": "}\u0007z",- "dlSubdir": null,- "dlTag": [- "Recommended",- "Latest",- "Nightly",- "q𬏕46"- ],- "dlUri": "https:k"- },- "unknown_versioning": {- "dlCSize": 2,- "dlHash": "",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "\u0000\u001ec"- },- "dlTag": [- "old"- ],- "dlUri": "https:nun"- }- }- },- "A_ARM64": {- "Darwin": {- "( == 2.5.5 && == 1.4.3 && > 4.4.2 && <= 5.3.3 )": {- "dlCSize": 0,- "dlHash": "",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "L:@\u0000"- },- "dlTag": null,- "dlUri": "https:"- },- "( > 2.2.2 && > 1.2.5 && < 4.4.3 && < 4.3.2 && > 4.1.1 && > 4.3.5 ) || ( > 1.4.1 && < 2.1.3 && > 1.4.1 && > 4.1.1 ) || ( >= 1.3.3 && <= 3.2.1 && == 1.2.3 && < 2.2.1 && < 3.1.3 ) || ( == 3.1.3 && <= 2.2.1 ) || ( > 2.1.2 && < 1.1.2 && > 1.1.1 ) || ( <= 2.1.1 && < 1.1.2 ) || ( == 2.1.1 ) || ( < 1.1.1 ) || ( > 1.3.1 ) || ( < 1.1.1 )": {- "dlCSize": -4,- "dlHash": "elgeoic",- "dlOutput": "$J",- "dlSubdir": {- "RegexDir": "\u0019t"- },- "dlTag": null,- "dlUri": "http:ku"- },- "( >= 1.2.4 )": {- "dlCSize": 6,- "dlHash": "hgpjt",- "dlOutput": ",⫅9",- "dlSubdir": {- "RegexDir": "𖥭nV"- },- "dlTag": null,- "dlUri": "https:hzs"- }- },- "Linux_RedHat": {- "( == 4.3.6 && > 6.1.2 && < 4.4.1 && >= 3.1.1 )": {- "dlCSize": -7,- "dlHash": "",- "dlOutput": "Y\n83?",- "dlSubdir": "\u0006",- "dlTag": [- "Experimental",- "Nightly",- "Experimental",- "Nightly",- "Prerelease"- ],- "dlUri": "https:xz"- },- "( == 5.5.1 && <= 5.4.5 && > 4.2.4 && == 5.4.4 )": {- "dlCSize": 4,- "dlHash": "",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "Z𩗲\u0014K"- },- "dlTag": null,- "dlUri": "https:ewc"- },- "( > 3.2.1 ) || ( < 1.1.2 && >= 2.3.3 )": {- "dlCSize": -7,- "dlHash": "fszk",- "dlOutput": null,- "dlSubdir": "Qf\u00026Y",- "dlTag": [- "Recommended",- "Latest",- "Latest",- "Latest",- "Nightly",- "Nightly"- ],- "dlUri": "https:qse"- },- "( >= 3.3.3 && >= 3.3.4 && == 1.4.4 )": {- "dlCSize": null,- "dlHash": "nc",- "dlOutput": "]]4𦁪\u0001<!",- "dlSubdir": {- "RegexDir": "𫟝𡲮𢒵`"- },- "dlTag": [- "old",- "LatestPrerelease",- "Experimental"- ],- "dlUri": "http:sokqq"- },- "( >= 3.4.1 && == 2.5.2 && < 4.3.1 && >= 4.2.4 ) || ( <= 5.5.3 && >= 2.3.1 && >= 1.4.4 && <= 4.2.1 && < 2.4.1 && < 3.1.4 )": {- "dlCSize": null,- "dlHash": "j",- "dlOutput": null,- "dlSubdir": "KP",- "dlTag": [- "LatestPrerelease",- "Prerelease"- ],- "dlUri": "http:qog"- },- "( >= 4.5.2 && >= 2.2.4 && <= 2.5.5 && >= 3.2.5 ) || ( < 3.2.1 ) || ( > 2.1.3 && >= 2.1.2 && < 2.2.3 && <= 2.1.2 && > 2.1.2 )": {- "dlCSize": 1,- "dlHash": "zrdpwog",- "dlOutput": "\u000cMe",- "dlSubdir": "t\nr3Tc",- "dlTag": [- "LatestPrerelease",- "old",- "LatestNightly",- "Z",- "old"- ],- "dlUri": "https:"- },- "unknown_versioning": {- "dlCSize": -3,- "dlHash": "b",- "dlOutput": "",- "dlSubdir": "ANY",- "dlTag": null,- "dlUri": "http:xuvjfc"- }- },- "Linux_Void": {- "unknown_versioning": {- "dlCSize": -2,- "dlHash": "t",- "dlOutput": "7\u001f",- "dlSubdir": "\u001a",- "dlTag": [],- "dlUri": "http:hrm"- }- },- "Windows": {- "( <= 3.2.1 && == 1.2.2 && < 5.2.4 && < 3.4.1 )": {- "dlCSize": 4,- "dlHash": "jybp",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "LatestNightly",- "old",- "old",- "Latest",- "Nightly"- ],- "dlUri": "http:takejc"- },- "( >= 3.3.2 && > 2.5.1 && <= 2.2.1 && > 4.4.4 && > 4.5.5 && > 1.5.5 && <= 3.3.3 ) || ( >= 3.1.3 && <= 2.4.2 && > 3.1.2 && > 3.3.2 && < 3.3.1 )": {- "dlCSize": -1,- "dlHash": "olbjtc",- "dlOutput": "09B𰪶",- "dlSubdir": "&譢\u0000+",- "dlTag": [- "LatestNightly"- ],- "dlUri": "http:exmlnxg"- },- "( >= 4.4.1 )": {- "dlCSize": -5,- "dlHash": "zolnvlr",- "dlOutput": "\u0002\u0007",- "dlSubdir": {- "RegexDir": "Z𱺂"- },- "dlTag": [- "LatestPrerelease",- "LatestNightly",- "Latest",- "Latest",- "Nightly"- ],- "dlUri": "https:ropwq"- },- "( >= 5.4.4 && > 1.2.5 && < 1.4.6 && < 5.1.4 && == 5.1.1 ) || ( > 3.3.1 && <= 3.1.4 && < 4.1.2 ) || ( < 3.2.1 && >= 1.3.1 && < 3.3.1 && <= 1.1.4 )": {- "dlCSize": null,- "dlHash": "ptqdq",- "dlOutput": "\u001cSV",- "dlSubdir": "Ac",- "dlTag": [- "Nightly",- "\u0003𐿇`1/",- "Recommended",- "LatestNightly",- "Nightly",- "Prerelease"- ],- "dlUri": "http:axygq"- },- "unknown_versioning": {- "dlCSize": 5,- "dlHash": "dxmk",- "dlOutput": "",- "dlSubdir": {- "RegexDir": ".\u000c"- },- "dlTag": [- "old",- "old",- "old",- "",- "\"",- "Latest",- "old"- ],- "dlUri": "https:yahdkck"- }- }- },- "A_Sparc64": {- "Darwin": {- "( <= 3.4.2 && < 1.3.1 && > 1.1.4 && <= 6.4.4 && < 2.3.2 && > 3.2.2 && == 3.3.7 )": {- "dlCSize": -5,- "dlHash": "wnbu",- "dlOutput": null,- "dlSubdir": "𗵡{2",- "dlTag": [- "Experimental"- ],- "dlUri": "https:"- },- "( >= 1.5.1 )": {- "dlCSize": 1,- "dlHash": "gbnak",- "dlOutput": "꞊",- "dlSubdir": null,- "dlTag": [- "LatestPrerelease",- "Recommended",- "Experimental"- ],- "dlUri": "http:lvm"- }- },- "FreeBSD": {- "unknown_versioning": {- "dlCSize": 5,- "dlHash": "ao",- "dlOutput": "",- "dlSubdir": "\u0013",- "dlTag": [- "LatestPrerelease",- "Nightly",- "Prerelease",- ">"- ],- "dlUri": "https:tvrk"- }- },- "Linux_Void": {- "( <= 1.2.5 && < 3.4.4 ) || ( <= 4.1.2 && > 3.3.2 ) || ( <= 3.3.3 && < 3.1.2 && >= 4.1.1 && <= 2.3.4 && < 2.1.1 )": {- "dlCSize": -1,- "dlHash": "vixd",- "dlOutput": "",- "dlSubdir": "R\u0004",- "dlTag": null,- "dlUri": "http:"- },- "( <= 3.5.4 )": {- "dlCSize": -5,- "dlHash": "umsg",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "\u001b"- },- "dlTag": [- "base-5.5.5",- "LatestNightly",- "LatestNightly",- "\u0010}",- " K5_",- "LatestNightly"- ],- "dlUri": "https:fw"- },- "( <= 4.5.1 && < 5.2.1 )": {- "dlCSize": null,- "dlHash": "gftay",- "dlOutput": "eHg",- "dlSubdir": "`*\u001a\"",- "dlTag": [- "LatestPrerelease",- "Latest",- "LatestNightly",- "LatestPrerelease",- "LatestNightly",- "base-4.6.4",- "@j8뎹"- ],- "dlUri": "http:grys"- },- "unknown_versioning": {- "dlCSize": -4,- "dlHash": "iqbx",- "dlOutput": null,- "dlSubdir": "t",- "dlTag": [- "Experimental",- "LatestPrerelease",- "Prerelease",- "Recommended",- "Latest",- "LatestPrerelease"- ],- "dlUri": "https:vourzma"- }- },- "Windows": {- "( < 3.2.5 && >= 5.1.1 && < 2.1.3 && > 5.2.1 ) || ( <= 1.3.1 && <= 1.4.1 && == 3.2.4 && >= 2.4.2 && < 3.1.1 && == 4.1.4 ) || ( <= 3.3.3 && >= 3.1.3 && >= 1.1.3 && > 2.2.1 )": {- "dlCSize": 1,- "dlHash": "",- "dlOutput": null,- "dlSubdir": "^3",- "dlTag": [],- "dlUri": "http:qme"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "shfoo",- "dlOutput": "諆𥛢/a ",- "dlSubdir": null,- "dlTag": [],- "dlUri": "http:z"- }- }- }- },- "viChangeLog": null,- "viPostInstall": null,- "viPostRemove": "eyxan",- "viPreCompile": "whwdbl",- "viPreInstall": "r",- "viReleaseDay": "-19419099547954845-03-07",- "viSourceDL": null,- "viTags": [- "Nightly",- "Recommended"- ],- "viTestDL": {- "dlCSize": 1,- "dlHash": "",- "dlOutput": "\u0016𢇉N",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [],- "dlUri": "https:aio"- }- },- "8.8.5": {- "viArch": {- "A_32": {- "Darwin": {- "( < 1.5.1 && <= 4.5.3 )": {- "dlCSize": -2,- "dlHash": "zgxddf",- "dlOutput": "6%m",- "dlSubdir": "N\u001a/\u001b",- "dlTag": [- "Latest",- "Recommended",- "Recommended",- "\u0001)CZ/c",- "old",- "LatestNightly"- ],- "dlUri": "http:hq"- },- "( <= 1.3.3 && >= 4.3.1 && == 3.1.2 && > 2.1.5 && < 3.2.2 )": {- "dlCSize": 1,- "dlHash": "chdtrzq",- "dlOutput": "3\"",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "LatestNightly",- "Latest"- ],- "dlUri": "https:u"- },- "( == 2.3.4 && == 3.3.5 && <= 2.3.5 && <= 5.3.3 )": {- "dlCSize": -6,- "dlHash": "furc",- "dlOutput": "JUm<",- "dlSubdir": "\u001a𫐣x",- "dlTag": null,- "dlUri": "http:a"- },- "( > 2.2.4 && >= 4.3.5 && >= 5.5.5 && > 2.5.4 && == 5.2.3 && == 1.3.1 && >= 1.5.3 )": {- "dlCSize": 3,- "dlHash": "jt",- "dlOutput": "\u001b'舶,",- "dlSubdir": "\u000c𩣋",- "dlTag": [],- "dlUri": "https:rnfa"- },- "( >= 4.2.1 && > 2.4.2 && <= 2.5.1 && >= 2.1.2 && >= 2.5.2 && >= 2.2.2 && > 2.2.5 ) || ( == 3.2.1 && < 4.3.2 && < 1.4.4 )": {- "dlCSize": -1,- "dlHash": "dfta",- "dlOutput": "\u0017",- "dlSubdir": {- "RegexDir": "𨃪"- },- "dlTag": [- "LatestNightly",- "Nightly",- "Nightly",- "Nightly",- "Nightly",- "Latest",- "Recommended"- ],- "dlUri": "https:xfynvdl"- },- "unknown_versioning": {- "dlCSize": -1,- "dlHash": "hbdgra",- "dlOutput": "㇇磰aKow",- "dlSubdir": "",- "dlTag": null,- "dlUri": "http:vekerca"- }- },- "FreeBSD": {- "( <= 1.2.2 && < 3.1.1 && <= 2.4.4 && <= 4.3.2 )": {- "dlCSize": 3,- "dlHash": "ekeyafu",- "dlOutput": "k𨡂c",- "dlSubdir": null,- "dlTag": [- "LatestNightly",- "base-1.1.5",- "Latest",- "Recommended",- "Experimental",- "Recommended",- "LatestPrerelease"- ],- "dlUri": "http:nde"- }- }- },- "A_64": {- "Windows": {}- },- "A_ARM64": {- "FreeBSD": {- "( < 5.3.3 && > 4.1.1 && <= 1.2.3 && == 2.2.2 && > 4.5.5 && == 1.3.1 && > 4.4.4 ) || ( > 2.1.5 && > 2.3.3 && >= 1.2.2 && >= 1.2.3 && == 1.1.2 && > 3.2.3 )": {- "dlCSize": 3,- "dlHash": "werav",- "dlOutput": "\u0011\u0016g膬𩰖\u0005",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": null,- "dlUri": "https:"- },- "( <= 2.3.4 ) || ( > 1.1.1 && < 3.2.1 && <= 4.2.2 && == 1.4.1 )": {- "dlCSize": -1,- "dlHash": "sz",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "}Q\u0018\u0007"- },- "dlTag": [- "Experimental",- "LatestNightly",- "base-5.3.2",- "LatestPrerelease",- "Recommended",- "LatestNightly",- "Nightly"- ],- "dlUri": "https:xz"- },- "( == 2.4.2 && >= 1.4.1 ) || ( < 2.2.1 && == 2.2.4 && == 2.3.4 && >= 1.2.2 && >= 1.2.2 )": {- "dlCSize": 0,- "dlHash": "",- "dlOutput": "",- "dlSubdir": "\u0000\u0010\u0005k𦸙",- "dlTag": [- "base-6.3.5",- "Experimental",- "LatestNightly"- ],- "dlUri": "https:smwo"- },- "( >= 5.5.1 && < 4.3.1 )": {- "dlCSize": -1,- "dlHash": "fikxfg",- "dlOutput": "訁\u000bv",- "dlSubdir": {- "RegexDir": ";"- },- "dlTag": [- "Experimental"- ],- "dlUri": "https:okv"- },- "unknown_versioning": {- "dlCSize": -7,- "dlHash": "",- "dlOutput": "𪳃K",- "dlSubdir": "𛃆I\u0011",- "dlTag": [- "Prerelease",- "LatestNightly",- "LatestNightly",- "Latest"- ],- "dlUri": "https:jhhyfrj"- }- },- "Windows": {- "( < 4.3.2 && == 2.2.5 && > 3.1.4 )": {- "dlCSize": 2,- "dlHash": "vrh",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Prerelease",- "base-5.5.5",- "Experimental"- ],- "dlUri": "https:"- },- "( == 3.2.2 && == 2.1.3 && < 2.3.5 && > 2.2.2 && == 4.4.2 && == 4.5.2 && > 5.5.2 )": {- "dlCSize": 6,- "dlHash": "ggm",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [],- "dlUri": "https:x"- },- "( == 3.4.5 && >= 4.2.2 && < 1.5.4 && <= 3.2.3 && < 4.5.3 && > 3.5.1 ) || ( == 4.3.1 ) || ( < 3.4.3 )": {- "dlCSize": 2,- "dlHash": "lnwhgvq",- "dlOutput": "wF怕\u0008",- "dlSubdir": "\u0005Kᆲ",- "dlTag": [- "Prerelease",- "Latest",- "base-3.6.4"- ],- "dlUri": "http:yovtdoc"- },- "( == 3.5.5 && < 2.4.4 && < 4.2.5 && > 5.2.3 && == 4.5.1 && < 5.2.3 && < 4.6.2 ) || ( < 4.1.1 && == 1.1.3 ) || ( >= 2.2.1 && == 3.4.2 && >= 3.1.3 )": {- "dlCSize": 5,- "dlHash": "rebswlw",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "𪾙"- },- "dlTag": [- "Recommended",- "old",- "Prerelease",- "Recommended",- "LatestPrerelease",- "Latest"- ],- "dlUri": "https:s"- },- "( > 4.2.4 && == 4.3.5 && > 4.1.1 && == 3.5.5 && < 4.1.5 && <= 4.2.2 )": {- "dlCSize": -5,- "dlHash": "g",- "dlOutput": ":🎉.9𫣸",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "Latest",- "old",- "base-5.1.6"- ],- "dlUri": "https:m"- },- "unknown_versioning": {- "dlCSize": 3,- "dlHash": "qgrl",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "v/䊪"- },- "dlTag": [- "base-5.4.5",- "Nightly"- ],- "dlUri": "https:pjk"- }- }- },- "A_PowerPC": {- "Darwin": {- "( > 6.5.4 && < 5.5.5 && == 3.5.1 && <= 2.4.2 && >= 3.1.2 && <= 2.5.5 && > 4.3.1 ) || ( > 3.2.4 )": {- "dlCSize": 0,- "dlHash": "rdxytxc",- "dlOutput": "i3",- "dlSubdir": "rE~𦁵aR",- "dlTag": [- "Prerelease",- "Prerelease",- "Recommended"- ],- "dlUri": "http:zbi"- },- "unknown_versioning": {- "dlCSize": -7,- "dlHash": "azttl",- "dlOutput": ".𩇭𛋂\u001d\u0011k",- "dlSubdir": "VLS",- "dlTag": null,- "dlUri": "http:ta"- }- },- "FreeBSD": {- "( > 2.3.6 && < 4.4.5 && <= 1.3.2 && <= 3.5.5 && > 1.2.5 ) || ( <= 3.3.4 && == 4.4.2 )": {- "dlCSize": -5,- "dlHash": "nyxr",- "dlOutput": "\u0017w]",- "dlSubdir": null,- "dlTag": [- "base-5.1.4",- "Recommended",- "old",- "Recommended",- "Latest",- "LatestPrerelease"- ],- "dlUri": "http:dwir"- }- },- "Linux_CentOS": {- "( < 5.2.3 && >= 2.4.3 && == 3.2.1 && <= 2.4.4 )": {- "dlCSize": 4,- "dlHash": "ynscxjs",- "dlOutput": null,- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:uy"- }- },- "Windows": {- "( <= 1.5.3 && <= 4.3.4 && >= 5.2.2 )": {- "dlCSize": 0,- "dlHash": "adgj",- "dlOutput": null,- "dlSubdir": "`p\u0010*",- "dlTag": [- "Nightly",- "LatestNightly",- "LatestPrerelease",- "LatestNightly"- ],- "dlUri": "https:ze"- },- "( <= 4.1.3 ) || ( < 1.2.3 && < 4.4.3 && > 2.4.4 && <= 4.1.1 && == 5.4.4 ) || ( > 2.2.2 && == 2.1.2 && <= 2.2.2 && == 1.1.2 && == 3.2.1 ) || ( > 2.1.1 ) || ( >= 1.2.1 && == 2.1.1 && >= 2.2.1 ) || ( == 1.1.2 ) || ( < 1.1.1 ) || ( < 1.1.2 )": {- "dlCSize": 7,- "dlHash": "obkwyw",- "dlOutput": "G𬤫",- "dlSubdir": null,- "dlTag": [- "base-5.3.2",- "Prerelease"- ],- "dlUri": "http:nzv"- },- "( <= 4.5.1 && >= 5.4.4 && < 5.2.4 && >= 4.3.1 && >= 4.1.4 ) || ( > 4.1.2 && <= 5.4.4 && <= 5.2.1 && <= 3.3.2 )": {- "dlCSize": null,- "dlHash": "ezstbf",- "dlOutput": "\u000b",- "dlSubdir": {- "RegexDir": "{,h\u000c𠞗"- },- "dlTag": null,- "dlUri": "https:x"- },- "( <= 5.5.4 && <= 4.4.4 && >= 4.5.4 && >= 4.3.4 && > 3.4.2 ) || ( >= 2.4.3 && > 1.2.1 && >= 2.3.4 && > 3.3.2 && >= 1.1.1 && >= 3.1.1 )": {- "dlCSize": 1,- "dlHash": "cik",- "dlOutput": "hէ_\u001e>",- "dlSubdir": "",- "dlTag": null,- "dlUri": "http:pzgavmr"- },- "( > 2.1.2 )": {- "dlCSize": 2,- "dlHash": "puakriv",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Nightly",- "base-6.3.1",- "LatestPrerelease",- "Nightly",- "Prerelease",- "LatestNightly",- "Nightly"- ],- "dlUri": "https:f"- },- "( > 2.1.4 ) || ( >= 4.4.3 && < 2.3.4 ) || ( == 3.3.2 && < 1.2.3 )": {- "dlCSize": 1,- "dlHash": "qzktr",- "dlOutput": ")/",- "dlSubdir": "s1+",- "dlTag": null,- "dlUri": "https:tm"- },- "( >= 5.2.2 )": {- "dlCSize": -1,- "dlHash": "hcpqq",- "dlOutput": "N=\u001f먒r\u0013",- "dlSubdir": {- "RegexDir": "t\u0000蓜Q\u001d"- },- "dlTag": [- "LatestNightly",- "base-4.5.4",- "LatestPrerelease"- ],- "dlUri": "http:vuog"- }- }- },- "A_Sparc": {- "Darwin": {},- "FreeBSD": {- "( < 4.2.2 && == 1.1.5 && <= 4.1.5 )": {- "dlCSize": 5,- "dlHash": "bxtx",- "dlOutput": "%.\u0014",- "dlSubdir": "Z~\"\u001a",- "dlTag": [- "Prerelease",- "Prerelease",- "Latest",- "Experimental",- "\u0000P浙",- "LatestNightly"- ],- "dlUri": "http:xqbxr"- },- "( <= 1.2.1 && < 4.3.4 && == 2.4.3 && <= 1.1.2 && < 5.5.4 && == 2.3.4 )": {- "dlCSize": 5,- "dlHash": "z",- "dlOutput": "\u0000\u000c\u000c𞤔OC",- "dlSubdir": null,- "dlTag": [- "LatestNightly"- ],- "dlUri": "https:u"- },- "( <= 4.5.6 && <= 5.3.3 && < 6.2.2 && >= 4.3.1 && >= 3.2.3 && >= 4.4.1 ) || ( < 4.4.2 && >= 2.1.3 && < 1.1.4 )": {- "dlCSize": null,- "dlHash": "jldpyjf",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [],- "dlUri": "https:xs"- },- "( == 5.4.3 )": {- "dlCSize": null,- "dlHash": "skv",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "𫜚𗣘𰇴\r"- },- "dlTag": [- "LatestNightly",- "Latest",- "Recommended",- "Recommended"- ],- "dlUri": "https:wytt"- },- "( >= 1.4.3 && > 1.3.2 && == 2.3.3 && < 5.3.1 )": {- "dlCSize": null,- "dlHash": "",- "dlOutput": "[Ḛ\"ꙁ]&",- "dlSubdir": {- "RegexDir": "G-6I"- },- "dlTag": [],- "dlUri": "http:ffu"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "tc",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Prerelease",- "Latest",- "old",- "Nightly"- ],- "dlUri": "https:rlqh"- }- }- }- },- "viChangeLog": "https:dyjcf",- "viPostInstall": "",- "viPostRemove": "d",- "viPreCompile": null,- "viPreInstall": "gavk",- "viReleaseDay": "7052234926266086-04-11",- "viSourceDL": null,- "viTags": [- "LatestPrerelease",- "Latest",- "old"- ],- "viTestDL": {- "dlCSize": 2,- "dlHash": "",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "=\u0015"- },- "dlTag": [- "\u0013𢮪"- ],- "dlUri": "http:jtjl"- }- }- },- "GHC": {- "1.7.5": {- "viArch": {- "A_32": {- "FreeBSD": {- "( < 3.4.1 && >= 1.3.5 && >= 3.2.2 ) || ( > 5.3.1 && >= 5.4.2 && < 1.3.2 && == 2.1.1 && > 3.1.3 )": {- "dlCSize": -7,- "dlHash": "yk",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "7"- },- "dlTag": [- "old",- "DSv",- "LatestNightly",- "Nightly",- "Recommended"- ],- "dlUri": "https:eiutp"- },- "( <= 3.4.2 && == 5.2.4 && <= 2.4.2 ) || ( <= 4.1.2 && == 4.3.1 && < 2.4.3 )": {- "dlCSize": null,- "dlHash": "n",- "dlOutput": "s",- "dlSubdir": null,- "dlTag": [- "base-4.6.6",- "Nightly",- "base-4.4.6",- "old",- "Nightly",- "base-1.5.3",- "LatestNightly"- ],- "dlUri": "https:u"- },- "( <= 4.2.2 && < 2.5.1 ) || ( > 1.1.2 ) || ( <= 3.3.2 && > 3.1.1 && < 2.1.3 && <= 2.2.2 && > 3.3.1 )": {- "dlCSize": null,- "dlHash": "blynrzb",- "dlOutput": "a\u0018수",- "dlSubdir": {- "RegexDir": "mv"- },- "dlTag": null,- "dlUri": "http:rffusb"- },- "( <= 4.6.1 && == 2.2.2 && == 3.4.1 && <= 5.2.4 )": {- "dlCSize": null,- "dlHash": "i",- "dlOutput": "R<h|",- "dlSubdir": null,- "dlTag": [- "Prerelease",- "LatestNightly",- "Recommended",- "Nightly",- "Experimental"- ],- "dlUri": "https:pcuxudk"- },- "unknown_versioning": {- "dlCSize": 0,- "dlHash": "foabv",- "dlOutput": "",- "dlSubdir": null,- "dlTag": [],- "dlUri": "https:jvyjadv"- }- },- "Linux_AmazonLinux": {- "( > 1.1.4 && == 1.2.5 && < 3.5.3 )": {- "dlCSize": null,- "dlHash": "c",- "dlOutput": "\u001ep:d",- "dlSubdir": "z\u0012\u001d",- "dlTag": [- "Prerelease",- "LatestNightly"- ],- "dlUri": "https:fdvsqf"- },- "( > 1.5.5 ) || ( < 3.1.3 && < 1.1.3 && < 2.2.1 && == 3.2.1 && == 2.2.3 ) || ( >= 1.3.1 && == 1.1.3 )": {- "dlCSize": null,- "dlHash": "mxihzdr",- "dlOutput": "\u0018\u0000𣵡\r",- "dlSubdir": {- "RegexDir": "\u001e"- },- "dlTag": null,- "dlUri": "https:qpxt"- },- "unknown_versioning": {- "dlCSize": 2,- "dlHash": "wqpzyb",- "dlOutput": null,- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:grswle"- }- },- "Linux_RedHat": {- "( == 3.4.4 && < 3.4.2 && < 1.2.3 && < 2.1.1 && <= 4.5.3 && < 4.4.1 && >= 3.1.2 )": {- "dlCSize": -5,- "dlHash": "fnlyzbk",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "?䢙\u0000^P"- },- "dlTag": [- "Prerelease",- "Recommended",- "Recommended",- "Recommended",- "Recommended",- "Prerelease",- "Latest"- ],- "dlUri": "https:sz"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "dncid",- "dlOutput": "*|7\u001e%\u0012",- "dlSubdir": {- "RegexDir": "Y"- },- "dlTag": null,- "dlUri": "http:j"- }- },- "Linux_Void": {- "( >= 3.1.3 && <= 3.1.5 && > 3.3.4 )": {- "dlCSize": -4,- "dlHash": "ldzizyk",- "dlOutput": ":2H𱻼]",- "dlSubdir": null,- "dlTag": [- "base-2.1.2",- "Experimental"- ],- "dlUri": "http:kvfkto"- },- "unknown_versioning": {- "dlCSize": -6,- "dlHash": "etjj",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "LatestPrerelease",- "Latest",- "base-6.3.5"- ],- "dlUri": "http:dxpxgf"- }- }- },- "A_64": {- "FreeBSD": {- "( > 1.1.3 ) || ( < 2.4.4 )": {- "dlCSize": -6,- "dlHash": "sgalrjz",- "dlOutput": "畩\u0001",- "dlSubdir": null,- "dlTag": [],- "dlUri": "https:ovwq"- },- "( >= 1.3.5 && < 4.4.2 && > 3.2.3 && <= 4.3.3 && >= 3.5.3 && > 1.1.2 ) || ( <= 2.3.1 && < 4.1.1 && < 2.2.1 && >= 3.1.1 && == 2.4.2 )": {- "dlCSize": -4,- "dlHash": "xach",- "dlOutput": "\u0014𫨰Y",- "dlSubdir": {- "RegexDir": "K3੶]𨕵"- },- "dlTag": [- "Latest",- "Latest",- "old",- "Latest",- "LatestPrerelease",- "Prerelease"- ],- "dlUri": "http:jnv"- },- "( >= 2.4.4 && >= 2.5.3 && < 5.3.1 && >= 3.2.4 && < 4.1.4 && < 4.3.2 && <= 2.1.5 )": {- "dlCSize": 2,- "dlHash": "gukto",- "dlOutput": "\\a\u0019C^\u0019",- "dlSubdir": {- "RegexDir": "\"m"- },- "dlTag": [- ":u",- "old"- ],- "dlUri": "https:dolnvh"- },- "( >= 5.4.2 && <= 2.5.4 && < 2.5.5 && > 2.2.5 )": {- "dlCSize": null,- "dlHash": "j",- "dlOutput": "B27",- "dlSubdir": null,- "dlTag": [- "base-1.4.5",- "Recommended",- "Nightly",- "LatestNightly"- ],- "dlUri": "https:sxtq"- },- "unknown_versioning": {- "dlCSize": 4,- "dlHash": "fupmm",- "dlOutput": "m𔓱p",- "dlSubdir": {- "RegexDir": "\u0013hU랦8"- },- "dlTag": [],- "dlUri": "https:i"- }- },- "Linux_Ubuntu": {- "( < 2.4.5 && < 1.2.2 && <= 2.1.1 && <= 4.1.2 && >= 3.4.3 && >= 1.4.2 && < 4.2.1 )": {- "dlCSize": null,- "dlHash": "ergg",- "dlOutput": "𫉭/&\u000b",- "dlSubdir": {- "RegexDir": "\u0005\u0001a"- },- "dlTag": [- "Recommended",- "Latest",- "Nightly",- "base-5.6.2",- "Recommended"- ],- "dlUri": "https:dhwvqpj"- },- "( == 2.4.3 )": {- "dlCSize": -2,- "dlHash": "z",- "dlOutput": "\u0013㏳f\r",- "dlSubdir": "Z硱r_",- "dlTag": [- "dL3W5G",- "old",- "r)b|",- "LatestNightly"- ],- "dlUri": "http:yxzkln"- },- "( == 4.4.4 && > 3.5.4 && > 1.1.2 ) || ( <= 3.3.4 && > 1.4.3 && > 1.2.1 && <= 1.1.1 ) || ( == 3.3.1 && == 3.1.3 && > 4.2.3 && < 1.2.1 && == 3.1.2 )": {- "dlCSize": 7,- "dlHash": "",- "dlOutput": null,- "dlSubdir": {- "RegexDir": ""- },- "dlTag": null,- "dlUri": "https:ixwet"- },- "( == 6.5.2 && < 2.4.6 && > 1.3.4 ) || ( > 1.4.1 && == 2.4.2 )": {- "dlCSize": 5,- "dlHash": "dihps",- "dlOutput": "",- "dlSubdir": "T\u0014𰊨",- "dlTag": [],- "dlUri": "http:hdtlc"- },- "( > 1.2.2 && >= 4.3.1 && <= 2.3.1 && >= 5.4.4 )": {- "dlCSize": -1,- "dlHash": "omlhepu",- "dlOutput": "z\t=:",- "dlSubdir": {- "RegexDir": "[F.\ra"- },- "dlTag": [- "",- "Latest",- "Recommended",- "Latest",- "i",- "LatestPrerelease"- ],- "dlUri": "https:"- },- "( > 1.3.5 && >= 4.3.5 && >= 1.2.1 && >= 3.4.1 && >= 2.1.3 && > 4.4.5 ) || ( < 1.1.1 && < 4.1.4 && <= 2.4.2 && < 1.1.4 )": {- "dlCSize": 2,- "dlHash": "",- "dlOutput": "M𠾉㪋oM-",- "dlSubdir": null,- "dlTag": [- "Latest",- "Prerelease",- "base-3.2.3",- "LatestNightly",- "Nightly",- "old"- ],- "dlUri": "http:pokv"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "fierm",- "dlOutput": "?\u0011UIx슆",- "dlSubdir": {- "RegexDir": "\\#\u0005\\"- },- "dlTag": [- "x",- "LatestPrerelease"- ],- "dlUri": "http:m"- }- },- "Windows": {- "( <= 1.2.2 ) || ( < 2.3.3 && == 3.3.2 && >= 2.1.3 && >= 2.3.3 && < 3.3.2 && >= 4.1.1 )": {- "dlCSize": -2,- "dlHash": "ozlcxn",- "dlOutput": "]5𮚍",- "dlSubdir": null,- "dlTag": [],- "dlUri": "http:eddvv"- },- "( <= 2.6.3 )": {- "dlCSize": -7,- "dlHash": "ujeeq",- "dlOutput": "f4\u0001\u0014",- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:zpucd"- },- "( <= 3.3.3 && < 4.4.3 && > 3.4.5 && == 3.1.4 && == 2.4.4 && <= 5.2.1 ) || ( == 4.1.4 && == 1.2.4 ) || ( == 2.2.1 && > 1.1.3 && < 3.1.2 && >= 2.3.3 )": {- "dlCSize": null,- "dlHash": "qws",- "dlOutput": null,- "dlSubdir": "",- "dlTag": [- "Experimental",- "1🁅2",- "base-5.4.5",- "old"- ],- "dlUri": "http:telxeu"- },- "( >= 4.5.5 && >= 1.4.1 && <= 2.3.4 && <= 2.2.2 && == 1.2.3 ) || ( <= 1.4.3 && == 1.3.2 )": {- "dlCSize": null,- "dlHash": "xhvsoi",- "dlOutput": "\u0015\u0008\u0008]\u0002K",- "dlSubdir": "\u0014r\u000e",- "dlTag": [],- "dlUri": "http:slifh"- },- "unknown_versioning": {- "dlCSize": -3,- "dlHash": "dwx",- "dlOutput": "s\u0004𡜜\n\u0015h",- "dlSubdir": {- "RegexDir": "\u0011"- },- "dlTag": [],- "dlUri": "http:"- }- }- },- "A_ARM": {- "Darwin": {- "( <= 2.3.3 && <= 1.4.4 && > 2.2.5 && > 3.2.5 && <= 3.5.2 )": {- "dlCSize": -6,- "dlHash": "cmyj",- "dlOutput": "j ",- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:h"- }- },- "FreeBSD": {- "( <= 5.2.1 && <= 1.1.4 && == 3.4.4 )": {- "dlCSize": 5,- "dlHash": "jo",- "dlOutput": "p𣨮펰𬵾?𘈅",- "dlSubdir": "&%aH",- "dlTag": null,- "dlUri": "https:e"- },- "unknown_versioning": {- "dlCSize": 1,- "dlHash": "o",- "dlOutput": "𩎐h_h0\u001e ",- "dlSubdir": "$?b𠽏m",- "dlTag": [- "base-2.2.1",- "Latest",- "Nightly",- "\u0008\u00128\u001c",- "Prerelease",- "Prerelease",- "Experimental"- ],- "dlUri": "http:itah"- }- },- "Windows": {- "( < 1.2.2 && > 1.5.5 && < 3.5.3 && == 4.4.4 && >= 5.5.5 && == 3.1.3 && > 3.5.3 ) || ( < 3.1.1 && > 3.2.1 && <= 2.1.3 && == 3.4.4 && <= 2.2.3 ) || ( == 1.1.1 && > 3.1.1 )": {- "dlCSize": 5,- "dlHash": "wdgz",- "dlOutput": null,- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:hzt"- },- "( < 1.3.1 && < 4.5.1 && == 5.4.2 && == 5.2.4 && > 2.3.1 ) || ( < 4.2.1 ) || ( >= 3.3.1 && >= 1.2.4 && <= 1.3.1 )": {- "dlCSize": 7,- "dlHash": "mai",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "#"- },- "dlTag": [- "Experimental",- "old",- "l5{",- "base-6.1.4",- "𭘡W",- "LatestNightly",- "Nightly"- ],- "dlUri": "http:wigby"- },- "( < 3.2.4 && == 2.3.5 && == 1.3.1 && >= 4.2.5 && < 3.1.5 && <= 5.2.2 ) || ( <= 2.4.4 && >= 4.2.2 )": {- "dlCSize": null,- "dlHash": "gwqsk",- "dlOutput": "ﱖ/",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "old",- "Latest",- "Latest",- "Recommended"- ],- "dlUri": "http:te"- },- "( < 4.5.3 && <= 2.2.3 && <= 3.5.4 ) || ( == 1.2.2 && < 2.1.1 )": {- "dlCSize": 7,- "dlHash": "q",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "LatestPrerelease"- ],- "dlUri": "http:ltyquir"- },- "( < 5.3.3 && < 3.3.1 && < 2.1.2 && == 2.2.1 && < 1.4.2 && == 1.5.1 && < 4.4.1 ) || ( > 4.1.4 && >= 2.2.3 )": {- "dlCSize": 3,- "dlHash": "lvzr",- "dlOutput": null,- "dlSubdir": "Z",- "dlTag": [- "Prerelease"- ],- "dlUri": "http:ntry"- },- "( == 4.3.1 && >= 1.5.4 && == 5.1.1 && == 3.1.3 && == 5.2.4 && >= 1.4.5 && < 3.3.1 )": {- "dlCSize": -4,- "dlHash": "",- "dlOutput": "29?0.",- "dlSubdir": "\u00175W[",- "dlTag": [- "Prerelease",- "Nightly"- ],- "dlUri": "https:skh"- },- "( >= 5.5.2 && == 3.2.5 && < 3.1.5 ) || ( >= 1.3.1 ) || ( >= 1.1.2 && < 3.2.3 ) || ( >= 1.1.2 && > 1.2.2 && >= 2.2.1 ) || ( <= 1.1.2 ) || ( > 1.1.1 )": {- "dlCSize": -7,- "dlHash": "ve",- "dlOutput": null,- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:ylmv"- }- }- },- "A_ARM64": {- "Darwin": {},- "FreeBSD": {- "( < 2.1.5 && <= 5.5.4 && <= 5.4.5 && < 1.3.5 && <= 2.2.1 && < 2.3.2 && > 4.4.3 ) || ( <= 2.3.2 && > 2.1.1 && >= 1.4.4 && >= 1.4.2 ) || ( <= 3.1.4 && > 1.1.1 && == 3.3.1 && > 2.2.3 )": {- "dlCSize": 3,- "dlHash": "cckguou",- "dlOutput": "D(\u000ei",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "Nightly",- "Recommended",- "LatestPrerelease"- ],- "dlUri": "http:"- },- "( <= 3.4.1 && < 2.3.5 && > 2.5.5 && < 4.4.2 && > 5.2.5 && == 4.5.4 && > 4.2.2 ) || ( >= 3.4.3 && == 4.5.4 && == 3.1.2 && == 1.3.2 && > 1.1.2 && > 4.2.4 )": {- "dlCSize": 7,- "dlHash": "qmlkr",- "dlOutput": null,- "dlSubdir": " F",- "dlTag": null,- "dlUri": "http:djpxgn"- },- "( == 5.2.2 && <= 3.4.4 && < 1.2.3 && > 3.4.2 && > 3.5.2 && < 1.1.5 ) || ( > 2.3.1 && == 3.3.2 ) || ( < 1.1.1 && == 2.4.1 && >= 2.1.1 && > 2.1.1 && <= 3.2.1 )": {- "dlCSize": 3,- "dlHash": "",- "dlOutput": "\u001f",- "dlSubdir": "",- "dlTag": null,- "dlUri": "https:hawid"- },- "( == 5.3.1 && > 2.5.5 && < 2.4.4 && == 3.5.1 ) || ( == 4.5.1 && <= 3.5.1 && == 4.1.4 && >= 1.4.3 && <= 3.1.1 ) || ( == 1.3.4 && > 2.1.2 && <= 3.2.1 && == 2.3.1 )": {- "dlCSize": -1,- "dlHash": "",- "dlOutput": "\u0015G",- "dlSubdir": "uuᄓZ𰩴",- "dlTag": [- "Experimental",- "Experimental",- "LatestPrerelease",- "🌌D𱸂gJ🔪"- ],- "dlUri": "http:nbyzp"- },- "unknown_versioning": {- "dlCSize": -3,- "dlHash": "t",- "dlOutput": "_3`\u000e켦\t",- "dlSubdir": "9W𛇨",- "dlTag": [- "Prerelease"- ],- "dlUri": "http:loi"- }- },- "Linux_RedHat": {- "( == 4.1.2 && < 4.4.3 && <= 2.3.2 && >= 3.5.4 && > 2.3.1 )": {- "dlCSize": null,- "dlHash": "",- "dlOutput": null,- "dlSubdir": "\u0019뵫",- "dlTag": [- "",- "+-",- "Nightly",- "old",- "Prerelease",- "\u0016䃛",- "base-1.3.5"- ],- "dlUri": "http:fvwyan"- },- "( > 5.2.2 && < 2.4.4 && < 4.3.1 && <= 1.3.1 && >= 5.4.5 && < 2.4.4 && > 4.2.3 ) || ( <= 4.5.1 ) || ( >= 3.2.1 && > 3.3.3 && <= 1.1.3 && >= 2.3.2 )": {- "dlCSize": null,- "dlHash": "yv",- "dlOutput": "YY->\u0014",- "dlSubdir": {- "RegexDir": "\u0006+𨘂Q,"- },- "dlTag": [- "/Sp|",- "",- "Latest",- "old"- ],- "dlUri": "http:gsrodgd"- },- "unknown_versioning": {- "dlCSize": 3,- "dlHash": "",- "dlOutput": "FI",- "dlSubdir": "𮩩\u0017s",- "dlTag": null,- "dlUri": "http:rmep"- }- },- "Windows": {- "( < 6.2.4 && > 2.1.2 && <= 1.6.3 && == 5.1.5 && <= 2.5.1 ) || ( > 4.4.2 && <= 2.4.2 && >= 4.3.4 && <= 3.1.3 && == 1.1.4 && >= 3.2.2 )": {- "dlCSize": -6,- "dlHash": "",- "dlOutput": "𰖕\u0002\u001f𡹞",- "dlSubdir": "\u0014e\u0016𫢺",- "dlTag": [- "Experimental",- "old",- "Recommended"- ],- "dlUri": "https:kpdecs"- }- }- },- "A_PowerPC": {},- "A_Sparc64": {- "Darwin": {},- "FreeBSD": {- "( < 4.1.4 && == 4.2.1 ) || ( < 2.3.1 && >= 2.4.1 && == 1.4.3 && < 3.3.2 && >= 4.4.3 ) || ( == 2.2.2 && > 3.1.4 )": {- "dlCSize": null,- "dlHash": "",- "dlOutput": "\u001d\u00135|$",- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:qvee"- },- "( == 3.5.4 && == 2.4.2 && == 3.2.1 && >= 2.3.1 && <= 2.5.2 && > 1.2.1 && <= 4.5.3 )": {- "dlCSize": -1,- "dlHash": "acl",- "dlOutput": "H",- "dlSubdir": "d\u0017茁\u0018A",- "dlTag": [- "Recommended",- "Nightly",- "base-5.1.1",- "Experimental",- "base-4.5.6",- "Nightly",- "LatestPrerelease"- ],- "dlUri": "https:zg"- }- },- "Linux_Gentoo": {- "( <= 2.2.3 && == 5.5.4 && > 4.5.5 && == 3.3.4 )": {- "dlCSize": 2,- "dlHash": "",- "dlOutput": "v)n~",- "dlSubdir": {- "RegexDir": "G\u0003x>r"- },- "dlTag": [- "Prerelease",- "\u0015d1\u0003\u001c"- ],- "dlUri": "http:sfmhqa"- },- "( >= 5.4.3 && == 1.1.1 && < 3.5.2 && > 1.3.5 && > 2.3.1 && == 2.2.4 && > 1.3.3 ) || ( < 1.1.4 )": {- "dlCSize": null,- "dlHash": "syzdckt",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "્]"- },- "dlTag": [- "base-6.6.1",- "Prerelease",- "Nightly",- "base-2.3.2"- ],- "dlUri": "http:zk"- }- },- "Windows": {- "( < 3.4.5 && <= 5.3.1 && <= 2.5.5 && > 4.5.2 && > 2.4.4 && <= 2.1.1 )": {- "dlCSize": -4,- "dlHash": "gspd",- "dlOutput": "xYW\u0016",- "dlSubdir": "\u001b\u0017",- "dlTag": [- "old",- "Recommended",- "Latest",- "LatestPrerelease",- "Nightly",- "Experimental"- ],- "dlUri": "http:efn"- },- "( <= 2.1.4 && <= 5.3.1 && > 3.3.3 )": {- "dlCSize": -5,- "dlHash": "hhl",- "dlOutput": "\u000c\u0000",- "dlSubdir": "",- "dlTag": [- "base-3.3.5",- "base-4.3.5",- "LatestPrerelease",- "Experimental",- "Recommended",- "\u0017",- "Latest"- ],- "dlUri": "http:abzbu"- },- "( == 1.3.2 && < 5.5.5 && > 3.4.2 && > 4.5.5 && == 3.2.5 ) || ( <= 1.1.4 && > 1.4.4 && < 2.4.4 && == 3.3.3 )": {- "dlCSize": 3,- "dlHash": "srtjnr",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "𢢾$`T"- },- "dlTag": [- "Nightly",- "Latest",- "old",- "\u0012:Uf",- "LatestPrerelease",- "old"- ],- "dlUri": "https:ggx"- }- }- }- },- "viChangeLog": "https:wz",- "viPostInstall": null,- "viPostRemove": null,- "viPreCompile": "",- "viPreInstall": null,- "viReleaseDay": "19388636140044521-07-03",- "viSourceDL": null,- "viTags": [- "Nightly"- ],- "viTestDL": {- "dlCSize": 0,- "dlHash": "rka",- "dlOutput": null,- "dlSubdir": "e.",- "dlTag": [- "Nightly"- ],- "dlUri": "https:mb"- }- },- "2.1.6": {- "viArch": {- "A_64": {- "Darwin": {- "( <= 4.3.1 && == 4.1.4 && < 5.1.3 && >= 1.3.2 ) || ( == 2.4.4 ) || ( > 3.3.1 )": {- "dlCSize": 7,- "dlHash": "lugsea",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Prerelease",- "Experimental"- ],- "dlUri": "http:pnitofk"- },- "unknown_versioning": {- "dlCSize": 3,- "dlHash": "paw",- "dlOutput": "풗x",- "dlSubdir": {- "RegexDir": "𣟰"- },- "dlTag": [- "LatestPrerelease",- "old",- "LatestPrerelease",- "old",- "old"- ],- "dlUri": "http:c"- }- },- "FreeBSD": {- "( < 4.2.4 && > 1.1.5 && <= 1.1.3 && >= 5.5.4 && < 4.4.1 ) || ( < 3.4.4 && < 4.3.3 && <= 3.3.2 ) || ( < 3.3.1 && < 2.1.1 && > 2.1.2 ) || ( < 2.2.1 && >= 1.2.2 && < 1.3.1 && < 1.2.2 ) || ( > 1.1.1 && < 2.1.1 )": {- "dlCSize": null,- "dlHash": "",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "`Go۰\u0018"- },- "dlTag": null,- "dlUri": "https:hxoz"- },- "( == 4.3.3 && >= 4.4.2 && > 4.4.2 && >= 3.4.3 && == 3.1.2 && <= 5.3.1 ) || ( >= 2.2.1 )": {- "dlCSize": -2,- "dlHash": "ybkov",- "dlOutput": "",- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:"- },- "( > 5.5.4 && == 2.3.5 )": {- "dlCSize": -4,- "dlHash": "occ",- "dlOutput": null,- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "LatestPrerelease",- "XGW\u0018",- "Prerelease",- "old",- "Recommended",- "LatestPrerelease",- "Prerelease"- ],- "dlUri": "https:wvppqx"- },- "( >= 3.2.5 && >= 3.3.5 && == 2.3.5 && < 4.3.5 )": {- "dlCSize": 4,- "dlHash": "orqxwrd",- "dlOutput": "",- "dlSubdir": "\u0010~j>",- "dlTag": [- "Latest",- "Nightly",- "old",- "Experimental"- ],- "dlUri": "http:ei"- },- "( >= 5.3.5 && >= 4.3.4 && < 3.2.2 )": {- "dlCSize": -6,- "dlHash": "xtqch",- "dlOutput": "\u001b",- "dlSubdir": null,- "dlTag": [- "Latest",- "Latest",- "Prerelease",- "Latest"- ],- "dlUri": "https:sixkqt"- },- "unknown_versioning": {- "dlCSize": 5,- "dlHash": "touzv",- "dlOutput": "𮊫",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "Experimental",- "Latest",- "Prerelease",- "Recommended",- "LatestPrerelease",- "base-4.2.3"- ],- "dlUri": "http:t"- }- },- "Linux_Alpine": {- "( < 1.2.3 && == 3.5.1 && < 2.4.5 && < 4.1.3 && < 4.4.5 && == 4.3.4 && == 2.3.2 )": {- "dlCSize": 5,- "dlHash": "aehyiak",- "dlOutput": "",- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:"- },- "( <= 2.5.5 && > 1.2.2 && >= 2.3.3 && >= 3.2.2 && >= 4.3.1 && <= 4.4.3 ) || ( == 2.1.2 && > 2.3.1 && >= 1.4.3 && < 2.3.4 && > 1.3.4 )": {- "dlCSize": -7,- "dlHash": "cviu",- "dlOutput": "G;A𫈗s^",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "base-2.6.3",- "Experimental",- "old",- "old",- "Latest",- "Prerelease"- ],- "dlUri": "http:rlyizgi"- },- "( <= 5.2.2 && < 2.2.3 && < 1.5.2 && == 3.4.3 && <= 3.1.4 )": {- "dlCSize": 4,- "dlHash": "fc",- "dlOutput": "t}~",- "dlSubdir": "𧞆𦂦",- "dlTag": [- "LatestNightly"- ],- "dlUri": "http:ai"- },- "( == 1.1.3 && <= 4.3.1 && <= 1.2.4 && >= 3.5.2 && < 5.3.4 && <= 3.1.1 && <= 5.2.3 ) || ( == 2.1.4 && > 2.2.2 ) || ( >= 3.2.1 && < 1.3.2 && <= 2.3.1 && > 2.2.3 )": {- "dlCSize": null,- "dlHash": "iiqvsa",- "dlOutput": "䄤",- "dlSubdir": "ᄽ{D",- "dlTag": [- "Recommended",- "base-1.5.5",- "Recommended",- "old",- "|"- ],- "dlUri": "https:ebixwda"- },- "unknown_versioning": {- "dlCSize": -7,- "dlHash": "khbbc",- "dlOutput": "j",- "dlSubdir": "",- "dlTag": [],- "dlUri": "https:"- }- },- "Linux_Exherbo": {},- "Windows": {- "( <= 2.3.2 && < 5.4.4 && <= 4.6.3 )": {- "dlCSize": 5,- "dlHash": "xkwuq",- "dlOutput": "𛇚z(\u001a𧩸펁",- "dlSubdir": "",- "dlTag": null,- "dlUri": "https:twvrehj"- },- "( <= 2.3.4 && < 3.3.2 && >= 2.2.5 && > 2.5.4 && > 2.5.2 )": {- "dlCSize": -2,- "dlHash": "nqd",- "dlOutput": null,- "dlSubdir": ";듏K\u0002",- "dlTag": [- "Latest",- "Experimental",- "Nightly",- "LatestNightly",- "Nightly"- ],- "dlUri": "http:kwydvvb"- },- "( <= 2.3.6 && == 4.3.3 ) || ( < 2.4.3 && == 3.1.3 && > 3.2.4 && == 1.1.3 && >= 4.2.1 && < 3.2.4 )": {- "dlCSize": 6,- "dlHash": "tlaf",- "dlOutput": "O(𦮀%",- "dlSubdir": {- "RegexDir": "}N("- },- "dlTag": null,- "dlUri": "http:tjqmsst"- },- "( == 2.1.3 && >= 1.4.4 ) || ( < 4.1.2 && < 3.3.3 && <= 1.4.2 && > 4.4.1 )": {- "dlCSize": -7,- "dlHash": "qtnxmv",- "dlOutput": "\u000c🔥W/𮓫",- "dlSubdir": "\u0017\u0007\u0013IE",- "dlTag": [- "LatestNightly",- "Nightly",- "LatestPrerelease",- "Recommended",- "Latest",- "Latest"- ],- "dlUri": "https:m"- },- "( >= 5.2.1 && <= 1.5.4 && > 2.4.5 && < 5.1.3 && < 2.2.3 && <= 2.3.4 && < 3.2.1 )": {- "dlCSize": 6,- "dlHash": "e",- "dlOutput": "1T\u0018\n",- "dlSubdir": "\u0014",- "dlTag": [- "old",- "LatestPrerelease",- "LatestPrerelease"- ],- "dlUri": "http:kv"- },- "unknown_versioning": {- "dlCSize": 6,- "dlHash": "vtwmxs",- "dlOutput": ")𦧍;C",- "dlSubdir": "jL\u0001X",- "dlTag": [- "base-5.6.3",- "LatestPrerelease",- "Recommended",- "old",- "LatestPrerelease",- "Prerelease",- "Experimental"- ],- "dlUri": "http:wjopdja"- }- }- },- "A_ARM64": {- "FreeBSD": {- "( <= 2.1.6 && == 1.3.2 && > 4.3.5 && > 4.4.3 && == 2.4.1 && < 3.1.4 && > 5.4.4 )": {- "dlCSize": -3,- "dlHash": "pgxg",- "dlOutput": "T𢛹\u0001U",- "dlSubdir": "0<[m",- "dlTag": null,- "dlUri": "http:mkicwhw"- },- "( > 3.1.3 && >= 4.5.1 )": {- "dlCSize": 5,- "dlHash": "eroc",- "dlOutput": null,- "dlSubdir": "\u0014{\u0003",- "dlTag": [- "old",- "",- "Prerelease",- "LatestPrerelease",- "Prerelease",- "Recommended"- ],- "dlUri": "http:clc"- },- "unknown_versioning": {- "dlCSize": -6,- "dlHash": "t",- "dlOutput": "}<dr𡵃[",- "dlSubdir": {- "RegexDir": "b"- },- "dlTag": [- "LatestPrerelease",- "Prerelease"- ],- "dlUri": "http:ggjmcsc"- }- },- "Linux_Debian": {- "( <= 4.2.2 && <= 3.4.2 )": {- "dlCSize": 4,- "dlHash": "sfd",- "dlOutput": "\tGT5i",- "dlSubdir": null,- "dlTag": [- "Recommended",- "\u0011*]",- "Latest",- "Recommended"- ],- "dlUri": "http:uxtybkw"- },- "( <= 4.2.5 && > 5.3.3 && < 1.3.5 && == 2.2.1 )": {- "dlCSize": 5,- "dlHash": "mruvjkm",- "dlOutput": "t\u00172AcM",- "dlSubdir": "",- "dlTag": [- "Experimental",- "Latest",- "Experimental",- "Nightly"- ],- "dlUri": "http:v"- },- "( == 3.2.3 && >= 4.3.3 && == 5.2.2 )": {- "dlCSize": null,- "dlHash": "jas",- "dlOutput": "$𘔩RL",- "dlSubdir": null,- "dlTag": [- "Prerelease"- ],- "dlUri": "http:"- },- "unknown_versioning": {- "dlCSize": -6,- "dlHash": "bjxqp",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "Ky/"- },- "dlTag": [- "LatestPrerelease",- "Prerelease"- ],- "dlUri": "http:hico"- }- },- "Linux_Fedora": {- "( == 3.4.3 )": {- "dlCSize": -1,- "dlHash": "hucnxw",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "\n"- },- "dlTag": [- "Nightly",- "Prerelease"- ],- "dlUri": "https:zry"- },- "unknown_versioning": {- "dlCSize": -2,- "dlHash": "wplay",- "dlOutput": "\u0005GOJ驏",- "dlSubdir": "{G",- "dlTag": [- "Latest",- "Nightly"- ],- "dlUri": "http:dv"- }- },- "Linux_Gentoo": {- "( <= 3.2.4 && >= 2.4.2 && < 5.3.1 && <= 2.3.1 && > 1.2.5 && == 4.2.2 ) || ( > 2.4.3 ) || ( > 1.2.3 )": {- "dlCSize": -6,- "dlHash": "dj",- "dlOutput": "EhI%E",- "dlSubdir": "\u000bHL\u0005u",- "dlTag": [- "LatestNightly",- "LatestPrerelease",- "base-1.3.6",- "",- "base-3.3.2",- "LatestPrerelease",- "base-4.1.4"- ],- "dlUri": "https:jhlim"- },- "( == 4.1.1 && <= 2.1.1 && >= 1.3.5 )": {- "dlCSize": -7,- "dlHash": "uupjxj",- "dlOutput": null,- "dlSubdir": "",- "dlTag": null,- "dlUri": "http:"- },- "( > 1.4.1 ) || ( >= 4.3.4 && < 3.3.4 && > 2.3.4 && <= 3.3.1 ) || ( > 3.2.3 && <= 3.1.1 && > 2.3.2 ) || ( == 2.1.1 && >= 2.1.2 && == 2.2.2 )": {- "dlCSize": 0,- "dlHash": "gj",- "dlOutput": "",- "dlSubdir": "n",- "dlTag": [- "Prerelease",- "old",- "Nightly",- "Latest",- "Recommended"- ],- "dlUri": "https:x"- },- "unknown_versioning": {- "dlCSize": 2,- "dlHash": "wrtta",- "dlOutput": "\u000e}<9\u0010f㴿",- "dlSubdir": "ke",- "dlTag": [- "LatestNightly"- ],- "dlUri": "https:jspzzr"- }- }- },- "A_Sparc64": {}- },- "viChangeLog": "https:waroy",- "viPostInstall": "drhyyr",- "viPostRemove": null,- "viPreCompile": null,- "viPreInstall": "psnezi",- "viReleaseDay": null,- "viSourceDL": {- "dlCSize": -1,- "dlHash": "e",- "dlOutput": "|",- "dlSubdir": null,- "dlTag": [- "Latest"- ],- "dlUri": "https:k"- },- "viTags": [- "Nightly"- ],- "viTestDL": null- },- "2.4.6": {- "viArch": {- "A_64": {- "Darwin": {- "( < 2.1.6 && < 3.2.4 && > 4.5.2 && <= 5.1.3 && > 4.5.3 )": {- "dlCSize": -4,- "dlHash": "lpeacsa",- "dlOutput": "fB\u000b𤧢s\u001d",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [],- "dlUri": "https:totyx"- },- "( < 3.2.5 && <= 3.1.3 ) || ( <= 1.1.3 && == 3.1.2 && >= 1.1.4 )": {- "dlCSize": null,- "dlHash": "",- "dlOutput": "\u001bj",- "dlSubdir": "d\tM:",- "dlTag": null,- "dlUri": "http:axslbto"- },- "( <= 1.5.3 && > 3.3.5 && <= 5.3.5 && <= 1.3.3 && >= 3.5.3 && > 4.2.2 )": {- "dlCSize": -7,- "dlHash": "ulln",- "dlOutput": "\t\u001dT",- "dlSubdir": "",- "dlTag": null,- "dlUri": "https:vd"- },- "( > 4.1.1 && < 2.4.5 && > 4.3.3 && >= 1.1.3 ) || ( >= 3.4.4 ) || ( < 1.1.3 && >= 1.2.2 && <= 1.3.2 && > 3.3.3 ) || ( >= 1.1.2 && == 2.1.2 && == 2.1.2 ) || ( > 1.1.1 && >= 1.1.1 )": {- "dlCSize": null,- "dlHash": "zcxpsnc",- "dlOutput": "+\u001b",- "dlSubdir": {- "RegexDir": "53?_"- },- "dlTag": [- "Experimental",- "Prerelease",- "base-1.5.1",- "jA",- "Recommended"- ],- "dlUri": "http:mnfj"- },- "( >= 3.1.5 && < 4.5.1 && < 1.1.4 && >= 1.6.2 && > 1.4.5 ) || ( <= 1.1.4 )": {- "dlCSize": 3,- "dlHash": "vnsndb",- "dlOutput": ">#",- "dlSubdir": "\u001ch\u0014",- "dlTag": null,- "dlUri": "http:zmmaovf"- },- "( >= 5.3.5 && < 4.1.3 && > 5.3.3 && > 1.2.4 && >= 2.1.2 && == 4.4.5 )": {- "dlCSize": -3,- "dlHash": "z",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "qI\"\u0019"- },- "dlTag": [- ",^ᒐ𤳮\u001cn",- "LatestPrerelease",- "Experimental"- ],- "dlUri": "https:"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "nbdqdm",- "dlOutput": ":u`u>g",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [],- "dlUri": "http:n"- }- },- "Linux_Debian": {},- "Windows": {- "( <= 5.1.3 && >= 4.5.5 && < 5.2.4 && > 5.1.1 && > 4.2.3 && <= 1.2.3 && < 2.2.5 ) || ( >= 3.3.4 && <= 3.3.4 ) || ( > 1.2.3 && < 1.2.2 && >= 2.3.2 ) || ( < 2.3.2 && <= 2.3.1 ) || ( < 1.2.1 && <= 1.1.1 && >= 1.1.1 ) || ( > 2.1.1 && == 1.1.1 ) || ( == 1.1.1 ) || ( >= 2.1.1 )": {- "dlCSize": 5,- "dlHash": "eqka",- "dlOutput": "]V",- "dlSubdir": null,- "dlTag": [- "LatestPrerelease",- "Nightly"- ],- "dlUri": "https:ebzhvus"- },- "( == 1.2.2 )": {- "dlCSize": 3,- "dlHash": "udxlgox",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "@😡IM"- },- "dlTag": [- "Prerelease",- "𫝜n𢃑\u000e,䳞",- "Prerelease"- ],- "dlUri": "https:psohtx"- },- "unknown_versioning": {- "dlCSize": -5,- "dlHash": "uqb",- "dlOutput": "+𤥙",- "dlSubdir": {- "RegexDir": "\u0004W\u0013"- },- "dlTag": [],- "dlUri": "http:tpqz"- }- }- },- "A_PowerPC": {- "Darwin": {- "( < 2.1.3 && == 4.2.5 && < 5.6.5 )": {- "dlCSize": 6,- "dlHash": "bprb",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "𭸴𘗰"- },- "dlTag": [- "base-5.6.1",- "Nightly",- "LatestPrerelease",- "Nightly",- "Recommended",- "Prerelease",- "LatestNightly"- ],- "dlUri": "https:"- },- "( <= 2.3.2 ) || ( < 4.3.1 && == 4.4.3 && >= 4.2.1 && == 2.2.4 ) || ( == 3.3.3 ) || ( >= 1.1.1 )": {- "dlCSize": null,- "dlHash": "icxyjcb",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "XrB\u0003>R"- },- "dlTag": null,- "dlUri": "http:ee"- },- "( == 5.1.5 && < 4.1.5 )": {- "dlCSize": null,- "dlHash": "wnw",- "dlOutput": "3띩u\u0010\r",- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:"- },- "( == 6.3.5 && == 1.1.5 && == 2.1.3 && <= 2.3.5 && >= 5.3.2 && < 3.5.1 && == 1.5.3 )": {- "dlCSize": -1,- "dlHash": "oysxdu",- "dlOutput": "] t8\u00036",- "dlSubdir": "𩸓R^E",- "dlTag": [- "LatestNightly"- ],- "dlUri": "https:yk"- },- "( >= 3.2.3 && <= 2.2.5 && < 1.4.3 && <= 3.2.5 && > 1.3.2 && == 5.2.3 ) || ( < 1.4.1 && == 4.3.4 && == 1.1.3 )": {- "dlCSize": null,- "dlHash": "slnqg",- "dlOutput": "1h\"\u001clD7",- "dlSubdir": {- "RegexDir": "Ft4+"- },- "dlTag": null,- "dlUri": "http:aud"- }- },- "FreeBSD": {},- "Linux_Void": {},- "Windows": {- "( < 1.4.4 && == 3.5.1 )": {- "dlCSize": 6,- "dlHash": "yrcimeg",- "dlOutput": "",- "dlSubdir": "𐧍𘓗𮧑",- "dlTag": null,- "dlUri": "https:teo"- },- "( < 5.1.1 && < 3.2.5 && <= 4.2.1 && <= 4.1.2 )": {- "dlCSize": null,- "dlHash": "i",- "dlOutput": null,- "dlSubdir": {- "RegexDir": ""- },- "dlTag": null,- "dlUri": "https:rujd"- },- "( == 1.5.1 && > 3.2.1 && >= 4.5.2 && == 3.4.3 )": {- "dlCSize": null,- "dlHash": "ysm",- "dlOutput": "^𨷴\u0016uk:",- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:ggyfkie"- },- "( == 2.2.1 && >= 5.2.5 && <= 4.4.1 && >= 1.4.5 && <= 4.5.2 && <= 5.5.5 )": {- "dlCSize": null,- "dlHash": "jrcnn",- "dlOutput": null,- "dlSubdir": {- "RegexDir": ""- },- "dlTag": null,- "dlUri": "http:"- },- "( == 5.2.2 && == 4.3.2 && >= 2.4.4 )": {- "dlCSize": null,- "dlHash": "",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "o"- },- "dlTag": null,- "dlUri": "http:w"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "westo",- "dlOutput": "j5\"lS|",- "dlSubdir": {- "RegexDir": "覆"- },- "dlTag": [- ".\u0011𡀭\u0018",- "old",- "Nightly"- ],- "dlUri": "http:xmwvul"- }- }- },- "A_Sparc64": {- "Darwin": {- "( <= 3.3.5 && < 1.4.1 && >= 4.2.2 && > 2.5.4 && <= 6.2.1 ) || ( > 4.3.5 )": {- "dlCSize": null,- "dlHash": "zzboa",- "dlOutput": "",- "dlSubdir": "\u001f",- "dlTag": [- "old",- "Nightly",- "Recommended",- "LatestPrerelease",- "Prerelease",- "Recommended",- "Recommended"- ],- "dlUri": "https:tlnpgv"- },- "( >= 4.2.2 && == 5.5.4 ) || ( < 1.4.1 && == 4.4.2 && <= 2.1.1 && == 5.3.4 && < 1.3.4 )": {- "dlCSize": -2,- "dlHash": "",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "base-1.4.2",- "Experimental",- "Latest",- "Recommended",- "LatestPrerelease",- "Experimental"- ],- "dlUri": "https:"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "",- "dlOutput": null,- "dlSubdir": "M",- "dlTag": [- "Latest",- "LatestPrerelease",- "LatestPrerelease"- ],- "dlUri": "http:j"- }- },- "FreeBSD": {- "( == 2.5.3 && == 1.5.4 && >= 1.2.3 && < 1.1.5 ) || ( < 2.3.2 && <= 3.3.1 && >= 4.3.2 && <= 3.4.1 )": {- "dlCSize": 0,- "dlHash": "ind",- "dlOutput": "@",- "dlSubdir": null,- "dlTag": [- "Recommended"- ],- "dlUri": "http:x"- },- "( >= 2.5.4 )": {- "dlCSize": null,- "dlHash": "em",- "dlOutput": "P",- "dlSubdir": {- "RegexDir": "𥻩\u0019l"- },- "dlTag": [- "Nightly",- "LatestPrerelease",- "LatestPrerelease",- "Nightly",- "old"- ],- "dlUri": "https:tzcy"- },- "( >= 3.5.1 && > 3.2.3 && <= 6.2.2 )": {- "dlCSize": null,- "dlHash": "",- "dlOutput": "|",- "dlSubdir": null,- "dlTag": [],- "dlUri": "https:q"- },- "( >= 5.5.5 && >= 3.3.4 ) || ( <= 1.4.4 && < 4.2.2 && <= 2.4.1 && <= 3.1.4 && >= 3.2.3 )": {- "dlCSize": 0,- "dlHash": "yr",- "dlOutput": null,- "dlSubdir": "n[1螛",- "dlTag": null,- "dlUri": "https:qbrzj"- },- "unknown_versioning": {- "dlCSize": 0,- "dlHash": "iooqt",- "dlOutput": "",- "dlSubdir": null,- "dlTag": [- "Nightly",- "LatestPrerelease",- "Recommended",- "Latest",- "Prerelease",- "base-6.5.6"- ],- "dlUri": "http:"- }- },- "Windows": {- "( >= 1.1.1 && >= 2.2.2 ) || ( > 4.2.2 && >= 4.2.2 && == 3.1.1 && == 2.1.3 && <= 4.4.5 && == 3.2.3 )": {- "dlCSize": -4,- "dlHash": "jpevks",- "dlOutput": "wW3/h;ꋟ",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": null,- "dlUri": "http:fjni"- },- "unknown_versioning": {- "dlCSize": 0,- "dlHash": "pb",- "dlOutput": "]a!V\n\u0010",- "dlSubdir": {- "RegexDir": "\"iV5"- },- "dlTag": [],- "dlUri": "https:ppxe"- }- }- }- },- "viChangeLog": "http:kezg",- "viPostInstall": "q",- "viPostRemove": "pq",- "viPreCompile": "rppwcvp",- "viPreInstall": "gpxfxy",- "viReleaseDay": "8645004479922981-03-17",- "viSourceDL": {- "dlCSize": -6,- "dlHash": "clndqp",- "dlOutput": "쒚\u000e|",- "dlSubdir": {- "RegexDir": "c𭣚\u001d"- },- "dlTag": [- "Recommended",- "Nightly",- "Experimental"- ],- "dlUri": "https:gx"- },- "viTags": [- "LatestPrerelease",- "Nightly",- "Nightly",- "Experimental"- ],- "viTestDL": {- "dlCSize": -1,- "dlHash": "osfn",- "dlOutput": "\u0014?]𰼲",- "dlSubdir": "𡤼\u000e]\\\u0014",- "dlTag": [- "Nightly",- "old",- "Prerelease",- "base-5.4.3",- "Latest"- ],- "dlUri": "http:ylgyiv"- }- },- "3.8.1": {- "viArch": {- "A_ARM": {- "Darwin": {- "( <= 3.4.3 && == 4.3.2 && >= 5.3.5 && <= 1.4.2 && > 5.1.2 ) || ( <= 1.3.2 && < 2.4.4 && < 4.4.2 )": {- "dlCSize": null,- "dlHash": "zj",- "dlOutput": "\u0005dG",- "dlSubdir": "",- "dlTag": null,- "dlUri": "https:"- }- },- "FreeBSD": {- "( < 1.5.3 && < 2.4.1 && > 5.1.5 )": {- "dlCSize": -6,- "dlHash": "ljovcfo",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Experimental",- "LatestNightly",- "浂9E",- "",- "old",- "old",- "Recommended"- ],- "dlUri": "http:o"- }- },- "Linux_Debian": {- "( < 3.3.1 && <= 5.1.1 && < 5.3.1 && <= 5.2.2 && == 5.1.4 && >= 2.2.4 && < 5.4.5 ) || ( < 1.4.4 && >= 1.3.3 && <= 2.2.3 && <= 1.4.3 ) || ( >= 3.1.1 )": {- "dlCSize": null,- "dlHash": "jylrmie",- "dlOutput": null,- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [],- "dlUri": "https:scp"- },- "( >= 3.2.4 && >= 4.3.3 && >= 3.5.3 && == 5.4.1 && > 3.4.5 && == 2.5.3 && > 5.2.4 ) || ( < 2.4.2 && <= 2.4.4 && >= 2.2.4 && < 2.4.3 ) || ( > 1.2.3 ) || ( == 2.1.2 && == 2.1.3 && == 1.2.1 ) || ( < 1.1.2 )": {- "dlCSize": -5,- "dlHash": "ruvn",- "dlOutput": "",- "dlSubdir": "7v",- "dlTag": [- "LatestNightly",- "old",- "old",- "LatestNightly"- ],- "dlUri": "https:jq"- },- "( >= 4.5.5 && < 1.5.4 && > 5.2.4 && < 1.1.2 && <= 1.2.4 && <= 2.4.4 && > 2.2.4 )": {- "dlCSize": null,- "dlHash": "qpkwjlj",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "i\u0000"- },- "dlTag": [],- "dlUri": "https:zyqs"- },- "unknown_versioning": {- "dlCSize": 4,- "dlHash": "gziqqyc",- "dlOutput": null,- "dlSubdir": "O)\u0015\u0005",- "dlTag": [- "LatestPrerelease",- "Prerelease",- "Experimental",- "Recommended"- ],- "dlUri": "https:qeecime"- }- },- "Windows": {- "( < 1.2.5 )": {- "dlCSize": 7,- "dlHash": "nkhnay",- "dlOutput": null,- "dlSubdir": "T𣥺|~%e",- "dlTag": [- "Recommended",- "LatestPrerelease",- "Prerelease",- "Experimental",- "Experimental",- "Experimental",- "base-3.4.6"- ],- "dlUri": "https:dtrks"- },- "( < 2.5.2 && == 1.4.1 && > 5.3.4 && <= 5.3.5 && <= 1.3.1 && == 4.5.1 && >= 4.4.4 )": {- "dlCSize": 3,- "dlHash": "p",- "dlOutput": "i馽",- "dlSubdir": "`",- "dlTag": [],- "dlUri": "https:"- },- "( == 3.1.3 && < 1.3.2 )": {- "dlCSize": 4,- "dlHash": "urxrnw",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "𘝥\u000fpP"- },- "dlTag": [- "base-6.5.4",- "Nightly",- "Latest"- ],- "dlUri": "https:sfq"- },- "( >= 2.2.2 )": {- "dlCSize": -2,- "dlHash": "p",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Cs𥷗S",- "Nightly",- "⏡\u0001\u001e\u0010",- "Prerelease",- "Experimental"- ],- "dlUri": "http:xzprxly"- },- "( >= 5.2.4 && >= 1.4.2 && > 2.2.3 && >= 2.3.1 ) || ( <= 1.3.2 )": {- "dlCSize": 2,- "dlHash": "",- "dlOutput": "",- "dlSubdir": null,- "dlTag": [- "LatestNightly",- "Latest",- "LatestNightly",- "Latest",- "Prerelease",- "m\\\u0011",- "Nightly"- ],- "dlUri": "http:ljc"- },- "unknown_versioning": {- "dlCSize": 2,- "dlHash": "ykoghr",- "dlOutput": "r",- "dlSubdir": "\u0010`Z",- "dlTag": [],- "dlUri": "https:drhdzz"- }- }- },- "A_Sparc": {- "Linux_Debian": {- "( == 4.1.6 && <= 2.4.2 && < 3.3.5 && == 3.2.3 )": {- "dlCSize": 4,- "dlHash": "ir",- "dlOutput": "=A",- "dlSubdir": null,- "dlTag": [- "Prerelease",- "Nightly",- "base-3.5.5"- ],- "dlUri": "https:w"- },- "( >= 4.4.4 && >= 3.1.3 && >= 5.1.5 )": {- "dlCSize": null,- "dlHash": "g",- "dlOutput": "\u0007\u001bH𑂝\u001d",- "dlSubdir": {- "RegexDir": "\u001a\u0016^"- },- "dlTag": [],- "dlUri": "http:dnkh"- }- },- "Linux_Ubuntu": {- "( < 5.4.1 && > 3.6.4 && == 6.2.2 && <= 3.3.4 && <= 4.2.4 && > 1.1.4 )": {- "dlCSize": -2,- "dlHash": "hupr",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "old",- "base-4.6.2",- "LatestNightly"- ],- "dlUri": "http:"- },- "( < 5.4.4 && <= 2.5.1 && > 4.1.4 && <= 2.3.1 && >= 2.4.2 && < 4.5.5 )": {- "dlCSize": -3,- "dlHash": "vhjwyo",- "dlOutput": "𢯆GM\u001ec玹s",- "dlSubdir": "\u0005xꯟ",- "dlTag": [- "base-5.1.5",- "old",- "LatestNightly",- "Prerelease"- ],- "dlUri": "https:jdzugm"- },- "( <= 3.3.4 && == 3.3.3 && == 1.3.1 && > 2.2.3 && == 3.1.2 && <= 2.3.4 && == 1.4.5 ) || ( < 4.2.1 && > 2.3.1 && < 1.3.3 && <= 3.1.4 && == 2.4.2 && == 4.1.3 ) || ( < 1.2.3 && >= 1.3.3 && > 3.2.1 && == 1.2.2 ) || ( < 2.1.1 && == 1.2.2 && < 2.1.2 && >= 2.1.1 ) || ( > 2.1.1 && <= 1.1.2 ) || ( <= 1.1.1 && >= 1.1.1 ) || ( == 1.1.1 )": {- "dlCSize": -3,- "dlHash": "",- "dlOutput": "",- "dlSubdir": ":",- "dlTag": [- "Recommended"- ],- "dlUri": "https:"- },- "( >= 2.2.3 && >= 4.3.5 && < 4.2.1 && <= 4.4.1 && < 4.1.2 && <= 3.3.2 && <= 4.2.1 ) || ( < 4.2.3 )": {- "dlCSize": -1,- "dlHash": "sihxrs",- "dlOutput": "\u000f𡙕",- "dlSubdir": "\u001c\u0005\u0015",- "dlTag": [- "Experimental",- "Experimental",- "old",- "base-1.2.6",- "Ody"- ],- "dlUri": "https:bgnmbjh"- },- "( >= 3.2.3 && < 4.4.4 && < 4.2.2 && < 3.1.4 && < 4.3.1 && < 3.3.2 ) || ( < 2.3.4 && <= 4.4.3 && <= 4.1.1 && < 1.1.4 && >= 4.4.3 && <= 4.1.4 )": {- "dlCSize": 6,- "dlHash": "dnyw",- "dlOutput": "d*\u0010",- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:dzrpc"- },- "unknown_versioning": {- "dlCSize": 4,- "dlHash": "",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "['\u0014/P\u000c"- },- "dlTag": [- "LatestPrerelease",- "Prerelease",- "Latest",- "Experimental"- ],- "dlUri": "http:f"- }- }- },- "A_Sparc64": {- "Darwin": {- "( < 3.1.5 && <= 4.2.1 && <= 3.1.4 && < 4.4.3 ) || ( >= 4.1.2 && <= 1.1.4 )": {- "dlCSize": 6,- "dlHash": "bgw",- "dlOutput": "M𰎔rk",- "dlSubdir": {- "RegexDir": "z\u0001"- },- "dlTag": [],- "dlUri": "https:"- },- "( < 4.2.2 && <= 3.3.3 && <= 3.1.4 && > 2.4.1 && < 1.4.3 )": {- "dlCSize": null,- "dlHash": "clel",- "dlOutput": "\u0014m7\u0014(",- "dlSubdir": {- "RegexDir": "珤g"- },- "dlTag": null,- "dlUri": "https:lmb"- },- "( < 4.4.4 && > 4.2.3 && >= 5.1.3 && > 4.1.4 && > 1.1.1 && > 3.2.5 ) || ( > 1.4.4 && <= 4.4.5 && < 2.4.2 && == 2.2.3 )": {- "dlCSize": -5,- "dlHash": "",- "dlOutput": "a5𩩎VE_D",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": null,- "dlUri": "https:"- },- "( == 4.3.3 && <= 4.5.3 && == 3.5.5 && > 2.2.4 && > 5.5.2 && > 1.6.6 && > 1.3.1 )": {- "dlCSize": 3,- "dlHash": "mradhp",- "dlOutput": "\u0014𡱷\t`p:",- "dlSubdir": "=",- "dlTag": [],- "dlUri": "https:htvzh"- },- "( == 5.4.4 && < 5.3.5 && < 4.4.4 && == 1.6.2 && < 1.5.2 )": {- "dlCSize": null,- "dlHash": "ibgu",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Latest",- "LatestPrerelease",- "old"- ],- "dlUri": "https:vqweox"- },- "unknown_versioning": {- "dlCSize": 3,- "dlHash": "suw",- "dlOutput": "m\u001bT",- "dlSubdir": null,- "dlTag": [- "Nightly",- "Prerelease"- ],- "dlUri": "https:pu"- }- },- "FreeBSD": {- "( <= 1.1.4 && >= 4.5.2 && == 3.4.4 && <= 1.3.1 && > 4.1.3 && >= 1.4.2 && > 2.2.4 ) || ( < 1.3.4 && < 2.4.2 && <= 3.1.2 && < 2.2.2 && == 4.1.4 && < 1.3.2 ) || ( >= 1.1.1 && == 3.3.1 && == 1.2.2 && == 3.2.4 ) || ( < 2.1.1 && >= 2.2.1 )": {- "dlCSize": 0,- "dlHash": "q",- "dlOutput": "~\u0007/\u000c⒏g",- "dlSubdir": {- "RegexDir": "q'"- },- "dlTag": [- "Latest",- "Latest",- "\u0011<",- "old",- "base-4.1.3"- ],- "dlUri": "http:"- },- "( <= 1.5.2 && > 3.5.1 ) || ( < 4.4.1 ) || ( < 2.2.2 && == 3.2.2 && < 3.1.3 && == 1.2.3 && < 2.1.3 ) || ( >= 1.1.1 && == 1.1.2 ) || ( < 2.1.1 ) || ( == 1.1.1 && > 1.1.1 )": {- "dlCSize": 6,- "dlHash": "v",- "dlOutput": null,- "dlSubdir": "zy)]㲍",- "dlTag": [- "Latest",- "old",- "old",- "Nightly"- ],- "dlUri": "http:n"- },- "( == 5.5.5 && < 2.1.2 && <= 1.1.5 && > 3.1.2 && <= 1.4.3 && < 5.4.2 )": {- "dlCSize": -3,- "dlHash": "daowsmd",- "dlOutput": "𡕆",- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:"- },- "( >= 1.2.2 && < 2.3.3 && > 3.4.5 && == 5.4.5 && <= 4.2.5 && <= 3.5.4 )": {- "dlCSize": null,- "dlHash": "",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "i"- },- "dlTag": null,- "dlUri": "http:kcf"- },- "( >= 3.4.4 ) || ( > 2.1.2 && >= 1.2.2 && >= 1.4.1 && > 3.4.5 && < 2.4.4 && >= 4.2.2 ) || ( == 2.3.3 && == 3.1.1 )": {- "dlCSize": 5,- "dlHash": "hbd",- "dlOutput": "3X",- "dlSubdir": "",- "dlTag": [- "Experimental",- "old",- "LatestNightly",- "Prerelease",- "LatestPrerelease",- "\u0017)\u000c_l",- "Prerelease"- ],- "dlUri": "https:esf"- },- "unknown_versioning": {- "dlCSize": -2,- "dlHash": "ty",- "dlOutput": "\u001b",- "dlSubdir": {- "RegexDir": "Iv㮜$\u0004"- },- "dlTag": [- "Experimental",- "Prerelease",- "Recommended",- "Latest",- "貄.F"- ],- "dlUri": "http:z"- }- },- "Linux_RedHat": {- "( <= 1.5.2 && > 4.5.3 && >= 3.1.5 && <= 4.4.2 && < 1.1.2 && < 4.2.4 ) || ( < 2.4.1 && <= 2.1.1 && >= 4.1.2 && >= 3.3.1 && > 2.4.4 )": {- "dlCSize": null,- "dlHash": "fl",- "dlOutput": null,- "dlSubdir": "j`\u0014",- "dlTag": [],- "dlUri": "https:nqd"- },- "( == 5.2.2 && == 4.5.5 && == 3.5.1 && < 1.1.5 )": {- "dlCSize": null,- "dlHash": "",- "dlOutput": "ng",- "dlSubdir": {- "RegexDir": "⮾𠹂?"- },- "dlTag": [- "K9O",- "Latest",- "LatestPrerelease",- "Recommended",- "Experimental",- "LatestNightly",- "LatestNightly"- ],- "dlUri": "http:vmlr"- },- "( >= 3.1.4 ) || ( < 4.3.3 && < 3.2.4 && == 3.4.2 && > 3.4.4 ) || ( <= 3.2.4 )": {- "dlCSize": null,- "dlHash": "pvqsdaa",- "dlOutput": "",- "dlSubdir": "\u0007钺\u001f𬝱",- "dlTag": [],- "dlUri": "http:z"- },- "( >= 3.4.5 && > 5.3.3 && < 1.4.1 && == 2.1.4 && == 4.4.2 )": {- "dlCSize": null,- "dlHash": "zsn",- "dlOutput": "\u00163\"BO",- "dlSubdir": "\u000e𣔿p_A",- "dlTag": [- "\u00177"- ],- "dlUri": "https:lpr"- },- "unknown_versioning": {- "dlCSize": -1,- "dlHash": "w",- "dlOutput": null,- "dlSubdir": "\u001b\u0011",- "dlTag": [- "base-5.4.4",- "Prerelease",- "Nightly",- "old",- "Latest"- ],- "dlUri": "http:wgcsttg"- }- },- "Linux_Ubuntu": {- "( < 2.5.1 && <= 3.1.2 && >= 4.3.4 && < 4.1.3 && < 2.2.3 ) || ( <= 1.4.3 && == 2.3.2 ) || ( < 4.1.3 && == 2.1.3 )": {- "dlCSize": -5,- "dlHash": "c",- "dlOutput": "𥶼瀻j\u0012𝓳/",- "dlSubdir": {- "RegexDir": "i"- },- "dlTag": [- "⼱5\u0011",- "Recommended",- "base-3.4.3"- ],- "dlUri": "https:baieh"- },- "( > 3.4.5 ) || ( <= 2.4.4 && > 1.4.4 && <= 2.4.1 && <= 2.2.6 )": {- "dlCSize": 2,- "dlHash": "rksmvfr",- "dlOutput": null,- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "base-5.1.3",- "old",- "Nightly"- ],- "dlUri": "https:s"- },- "( >= 1.1.2 && == 3.1.5 && <= 5.1.1 && < 5.3.4 ) || ( == 1.1.4 && <= 3.2.1 && < 3.3.4 )": {- "dlCSize": null,- "dlHash": "t",- "dlOutput": "\u000bO𪎠o",- "dlSubdir": "-jm",- "dlTag": [- "LatestPrerelease",- "\u0000",- "base-1.4.5",- "Experimental",- "Experimental",- "Recommended",- "old"- ],- "dlUri": "https:fyqen"- }- },- "Windows": {- "( >= 1.5.1 ) || ( <= 4.1.2 && < 2.3.2 && <= 2.2.2 )": {- "dlCSize": 3,- "dlHash": "zga",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "s\u0011\u00122"- },- "dlTag": [- "Nightly",- "0#",- "LatestNightly",- "Prerelease"- ],- "dlUri": "https:"- },- "( >= 5.4.1 && > 4.4.2 && > 4.2.2 && < 5.4.5 && < 3.2.3 && < 3.2.1 && > 2.4.3 ) || ( <= 2.3.1 && > 4.2.1 && < 2.1.2 ) || ( < 3.3.2 && > 3.3.3 && > 2.1.2 ) || ( <= 2.2.2 && == 1.2.3 )": {- "dlCSize": null,- "dlHash": "ewrhaq",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [],- "dlUri": "http:qqgl"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "fmblmdj",- "dlOutput": "9",- "dlSubdir": {- "RegexDir": " 怶\u000f𫓝F"- },- "dlTag": [- "q\u0015",- "old",- "LatestNightly",- "Nightly"- ],- "dlUri": "https:ptpzv"- }- }- }- },- "viChangeLog": "http:vaa",- "viPostInstall": null,- "viPostRemove": null,- "viPreCompile": null,- "viPreInstall": "buwpwbj",- "viReleaseDay": "-18833626801749283-08-10",- "viSourceDL": null,- "viTags": [],- "viTestDL": null- },- "4.6.7": {- "viArch": {- "A_64": {- "FreeBSD": {}- },- "A_Sparc64": {- "Windows": {- "( < 1.5.5 )": {- "dlCSize": -4,- "dlHash": "",- "dlOutput": "q𪟻)",- "dlSubdir": {- "RegexDir": "\u0011y𬕫"- },- "dlTag": [- "Prerelease",- "Latest",- "Prerelease",- "old",- "LatestNightly",- "Recommended",- "Latest"- ],- "dlUri": "https:mxtrdx"- },- "unknown_versioning": {- "dlCSize": -6,- "dlHash": "sy",- "dlOutput": "\u000c+[",- "dlSubdir": null,- "dlTag": [- "LatestPrerelease",- "Latest",- "Experimental",- "LatestNightly"- ],- "dlUri": "https:hgmevc"- }- }- }- },- "viChangeLog": "http:wqs",- "viPostInstall": "",- "viPostRemove": null,- "viPreCompile": null,- "viPreInstall": null,- "viReleaseDay": "12686742183186342-03-13",- "viSourceDL": {- "dlCSize": 4,- "dlHash": "tisnp",- "dlOutput": "~\u0013𛉂𱷉",- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:c"- },- "viTags": [- "LatestPrerelease",- "\u0000\u000f",- "W",- "Latest"- ],- "viTestDL": null- },- "4.7.5": {- "viArch": {},- "viChangeLog": "https:gkfn",- "viPostInstall": "trfpyky",- "viPostRemove": "cd",- "viPreCompile": null,- "viPreInstall": null,- "viReleaseDay": "23792748981354003-09-27",- "viSourceDL": {- "dlCSize": 1,- "dlHash": "myfcnq",- "dlOutput": "𨖷\u0000HY,",- "dlSubdir": null,- "dlTag": [- "base-5.5.2",- "Nightly",- "Prerelease",- "LatestPrerelease",- "old",- "Y彪b"- ],- "dlUri": "https:"- },- "viTags": [- "Nightly",- "old",- "Recommended",- "LatestNightly"- ],- "viTestDL": {- "dlCSize": 6,- "dlHash": "i",- "dlOutput": "𰟟",- "dlSubdir": "Bl",- "dlTag": [- "Experimental"- ],- "dlUri": "http:odr"- }- },- "5.4.7": {- "viArch": {- "A_PowerPC": {}- },- "viChangeLog": "http:axls",- "viPostInstall": "phmfx",- "viPostRemove": "xb",- "viPreCompile": "iyzbsvj",- "viPreInstall": "yanfjz",- "viReleaseDay": "2519010635288629-07-07",- "viSourceDL": {- "dlCSize": null,- "dlHash": "zglr",- "dlOutput": "𩏱2𪎍",- "dlSubdir": "I0Tu",- "dlTag": null,- "dlUri": "http:gc"- },- "viTags": [- "Recommended",- "Nightly",- "Prerelease",- "old"- ],- "viTestDL": null- },- "8.8.5": {- "viArch": {- "A_32": {- "Darwin": {- "( < 6.3.1 && == 3.5.5 && <= 5.4.1 && == 2.5.1 && <= 2.2.1 && >= 2.4.4 )": {- "dlCSize": 2,- "dlHash": "vm",- "dlOutput": "𫪥⓰Ip=",- "dlSubdir": "",- "dlTag": [- "Latest",- "C"- ],- "dlUri": "https:swco"- },- "( == 2.1.5 && >= 1.3.6 && < 3.1.5 && == 3.3.2 && < 1.3.4 && == 2.5.4 )": {- "dlCSize": null,- "dlHash": "mrkbneu",- "dlOutput": "B",- "dlSubdir": {- "RegexDir": "X"- },- "dlTag": null,- "dlUri": "http:r"- },- "unknown_versioning": {- "dlCSize": 0,- "dlHash": "eyjyim",- "dlOutput": "0",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": null,- "dlUri": "https:uz"- }- },- "Linux_Exherbo": {- "( < 2.2.1 && <= 2.2.5 && < 2.3.5 && == 3.5.2 && == 5.3.5 && < 1.2.2 )": {- "dlCSize": 1,- "dlHash": "n",- "dlOutput": "v/\u0005l",- "dlSubdir": null,- "dlTag": [- "base-4.1.5",- "Latest"- ],- "dlUri": "https:j"- },- "( < 3.2.5 && < 2.2.3 )": {- "dlCSize": 2,- "dlHash": "tcuwfa",- "dlOutput": "L-𥈍F",- "dlSubdir": {- "RegexDir": "^"- },- "dlTag": [- "LatestNightly",- "base-2.4.6"- ],- "dlUri": "https:z"- },- "( <= 2.3.4 && >= 1.4.2 && > 5.2.2 )": {- "dlCSize": 6,- "dlHash": "znaopyf",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "\u000c"- },- "dlTag": [- "Nightly",- "Prerelease",- "base-5.1.2",- "Experimental",- "Latest",- ""- ],- "dlUri": "http:plyzn"- },- "( == 3.1.1 && < 2.4.1 && < 5.4.5 && == 3.2.3 && == 5.3.1 && < 4.2.3 && > 1.5.3 ) || ( == 3.1.3 && >= 4.2.4 && < 4.4.3 && >= 1.2.2 && < 3.1.4 && == 2.3.1 )": {- "dlCSize": null,- "dlHash": "juture",- "dlOutput": "<",- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:eppi"- },- "( > 1.2.3 && >= 1.6.4 ) || ( >= 2.2.3 && > 3.1.3 ) || ( >= 3.2.1 ) || ( <= 2.1.3 ) || ( == 1.1.1 && > 1.1.1 && <= 1.1.1 ) || ( >= 2.1.1 && > 1.1.1 )": {- "dlCSize": -1,- "dlHash": "laa",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "\u0000A"- },- "dlTag": [- "base-6.6.1",- "N5'𰾦𡣪f",- "base-3.5.2",- "Latest",- "Recommended",- "Recommended",- "LatestNightly"- ],- "dlUri": "http:"- },- "( > 6.2.5 ) || ( >= 5.1.1 )": {- "dlCSize": 3,- "dlHash": "peo",- "dlOutput": "s\u001c@{\u0019",- "dlSubdir": {- "RegexDir": "<喓𢷻\u0017\t"- },- "dlTag": [- "Recommended",- "base-1.1.1",- "old",- "Prerelease"- ],- "dlUri": "http:eyki"- },- "unknown_versioning": {- "dlCSize": -6,- "dlHash": "qhgti",- "dlOutput": "*^XU9",- "dlSubdir": "&\n",- "dlTag": [- "D\u0016\u0012W.",- "LatestPrerelease",- "LatestNightly",- "old",- "LatestNightly"- ],- "dlUri": "https:eszn"- }- },- "Linux_Fedora": {- "( == 1.2.5 && >= 4.1.1 && > 3.2.1 && >= 4.2.5 && == 3.2.4 && == 4.4.2 && == 5.1.1 ) || ( < 2.2.3 )": {- "dlCSize": 7,- "dlHash": "hvuf",- "dlOutput": "S[::\u0005",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "LatestNightly",- "LatestNightly",- "base-2.4.1",- "LatestPrerelease"- ],- "dlUri": "https:"- },- "( == 4.2.3 && > 5.1.2 ) || ( > 1.3.2 ) || ( < 1.2.1 && == 2.2.2 && < 2.1.2 && == 2.1.2 && > 2.2.3 ) || ( < 1.1.2 && == 2.2.2 ) || ( > 1.1.2 )": {- "dlCSize": null,- "dlHash": "qcd",- "dlOutput": "K@",- "dlSubdir": "xL",- "dlTag": [- "Prerelease",- ""- ],- "dlUri": "http:oqfl"- },- "( > 5.2.1 && <= 1.3.4 && <= 5.5.3 && >= 1.5.4 )": {- "dlCSize": null,- "dlHash": "gzyhvwf",- "dlOutput": "\u0011",- "dlSubdir": null,- "dlTag": [- "LatestPrerelease",- "LatestPrerelease"- ],- "dlUri": "https:ze"- },- "( > 5.4.1 )": {- "dlCSize": 0,- "dlHash": "kpl",- "dlOutput": "\u0011:Ok",- "dlSubdir": null,- "dlTag": [- "Nightly",- "Experimental",- "Nightly",- "LatestNightly"- ],- "dlUri": "http:cavdxd"- },- "( >= 3.5.5 && < 3.5.1 && <= 3.4.3 && == 2.1.4 && > 1.6.1 && <= 4.3.2 ) || ( >= 3.4.2 && > 4.1.4 )": {- "dlCSize": null,- "dlHash": "",- "dlOutput": ",\u0019𡐭6R\t",- "dlSubdir": "1OP",- "dlTag": [- "Recommended",- "Nightly",- "base-2.3.1",- "Experimental",- "Nightly"- ],- "dlUri": "http:n"- },- "unknown_versioning": {- "dlCSize": -7,- "dlHash": "ti",- "dlOutput": "wIfe",- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:a"- }- }- },- "A_PowerPC": {- "Linux_Fedora": {- "( <= 1.2.2 ) || ( < 4.1.3 )": {- "dlCSize": 4,- "dlHash": "ov",- "dlOutput": "x\u000bi",- "dlSubdir": {- "RegexDir": "㈽簾\u000b"- },- "dlTag": null,- "dlUri": "http:xkdhe"- },- "( <= 3.2.5 && < 3.3.2 && == 3.2.5 && <= 1.2.4 && >= 5.5.4 && < 5.2.2 && > 3.2.1 ) || ( >= 4.4.2 && <= 1.4.3 && >= 2.4.2 && >= 2.3.4 && >= 4.4.2 && == 4.4.2 ) || ( == 1.1.3 && > 2.1.2 && <= 2.2.2 && <= 2.3.2 )": {- "dlCSize": -6,- "dlHash": "gy",- "dlOutput": "r\u000c8ᆝ&\u0016",- "dlSubdir": {- "RegexDir": "!"- },- "dlTag": [- "LatestNightly",- "LatestPrerelease",- "old",- "old"- ],- "dlUri": "http:gcnk"- },- "( > 4.4.4 && <= 3.1.5 ) || ( == 4.3.2 && == 3.2.1 && <= 2.1.1 && >= 2.2.2 && > 3.4.4 && < 4.1.1 ) || ( > 2.3.2 && <= 2.3.2 && >= 1.1.1 && < 3.3.2 && <= 1.3.1 )": {- "dlCSize": 2,- "dlHash": "jfsqbw",- "dlOutput": "3p",- "dlSubdir": {- "RegexDir": ".\n"- },- "dlTag": [- "LatestNightly",- "LatestNightly",- "base-6.3.6",- "Nightly",- "Nightly",- "Recommended",- "Latest"- ],- "dlUri": "http:"- },- "( >= 4.4.3 && >= 1.2.4 && <= 4.2.2 && < 2.6.3 && == 5.5.5 && > 2.2.1 && <= 1.1.4 ) || ( <= 4.1.2 && <= 3.4.4 ) || ( < 3.1.3 )": {- "dlCSize": 5,- "dlHash": "puhuxc",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "7"- },- "dlTag": [- "old"- ],- "dlUri": "https:jugqioa"- },- "unknown_versioning": {- "dlCSize": 4,- "dlHash": "",- "dlOutput": "t\niZDlB",- "dlSubdir": "ZGC",- "dlTag": [- "base-5.6.1",- "base-6.1.1",- "Prerelease",- "\u000b'",- "Recommended"- ],- "dlUri": "https:cg"- }- },- "Linux_Gentoo": {},- "Windows": {}- },- "A_PowerPC64": {- "Darwin": {- "( < 1.4.5 && > 1.5.2 && < 2.1.1 && > 4.3.5 && == 6.1.2 )": {- "dlCSize": -3,- "dlHash": "ladxcc",- "dlOutput": "9㽪",- "dlSubdir": "",- "dlTag": [],- "dlUri": "https:zzyxu"- },- "( < 2.3.2 ) || ( < 3.3.1 && == 2.3.1 && > 1.2.1 ) || ( >= 1.3.1 && > 3.3.3 && == 1.1.4 && == 2.2.1 && >= 4.3.3 )": {- "dlCSize": -7,- "dlHash": "xaswycm",- "dlOutput": "'FV\u0012\r\u0007\u0011",- "dlSubdir": {- "RegexDir": "\u0011iRyS"- },- "dlTag": null,- "dlUri": "https:btcz"- },- "( < 5.5.3 ) || ( > 3.1.3 && < 2.3.2 && == 5.1.4 && > 1.2.1 && < 1.1.3 )": {- "dlCSize": -2,- "dlHash": "z",- "dlOutput": "!H/𐧝\u0002^",- "dlSubdir": "",- "dlTag": [- "old",- "Recommended",- "Nightly",- "Nightly",- "Recommended",- "S\r?V",- "Experimental"- ],- "dlUri": "https:egkuhbu"- },- "( <= 4.3.3 && == 3.2.4 && > 3.3.2 ) || ( >= 3.1.1 )": {- "dlCSize": 4,- "dlHash": "eq",- "dlOutput": "亅救",- "dlSubdir": {- "RegexDir": "d\u0019"- },- "dlTag": null,- "dlUri": "https:uu"- },- "( <= 5.5.4 && == 5.2.3 && <= 2.4.6 && <= 2.4.5 && >= 1.4.3 && <= 4.2.2 ) || ( <= 3.1.2 )": {- "dlCSize": -4,- "dlHash": "xspjlhf",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Nightly",- "LatestPrerelease"- ],- "dlUri": "https:acailu"- },- "( > 4.5.1 ) || ( < 4.3.4 ) || ( > 1.2.2 && < 2.1.1 && == 3.2.2 && <= 1.3.3 ) || ( > 1.2.1 && >= 2.2.1 && > 3.2.2 && == 2.1.1 ) || ( > 2.2.1 )": {- "dlCSize": -6,- "dlHash": "lgfjce",- "dlOutput": "\u0013𰓘",- "dlSubdir": {- "RegexDir": "?"- },- "dlTag": [- "Latest",- "old",- "base-5.3.6",- "Latest",- "LatestNightly",- "LatestPrerelease"- ],- "dlUri": "http:j"- },- "unknown_versioning": {- "dlCSize": 2,- "dlHash": "hboqbmq",- "dlOutput": null,- "dlSubdir": {- "RegexDir": ".LOp&="- },- "dlTag": [- "Latest",- "Recommended",- "LatestPrerelease",- "Recommended"- ],- "dlUri": "https:jqe"- }- },- "FreeBSD": {- "( < 3.1.5 && < 1.1.1 && > 5.2.5 && < 3.2.5 )": {- "dlCSize": null,- "dlHash": "dzcx",- "dlOutput": null,- "dlSubdir": {- "RegexDir": ":\u001d)\u0005"- },- "dlTag": null,- "dlUri": "https:suorba"- },- "( <= 2.1.2 && == 5.1.2 && >= 2.1.2 && == 1.3.6 && < 1.5.2 )": {- "dlCSize": null,- "dlHash": "uldol",- "dlOutput": "",- "dlSubdir": "\u0003",- "dlTag": [- "LatestPrerelease"- ],- "dlUri": "https:ajbi"- },- "( <= 4.2.1 && > 2.1.3 && <= 4.2.1 )": {- "dlCSize": 7,- "dlHash": "b",- "dlOutput": "F臫k",- "dlSubdir": "&8\u0010a\u0004",- "dlTag": null,- "dlUri": "http:hqcm"- },- "( >= 4.3.3 && <= 1.3.3 && == 1.4.3 && < 2.2.2 && < 3.5.4 && > 5.4.2 && <= 3.1.1 ) || ( < 1.2.3 && == 1.3.1 && > 3.2.4 ) || ( >= 1.1.1 && >= 3.1.4 && > 1.2.2 && == 4.2.4 && == 2.3.2 ) || ( <= 1.1.1 )": {- "dlCSize": null,- "dlHash": "",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "W"- },- "dlTag": [- "🙓\u0006+"- ],- "dlUri": "https:ffnh"- },- "unknown_versioning": {- "dlCSize": 4,- "dlHash": "er",- "dlOutput": "\u0012\u0004i띮",- "dlSubdir": "e",- "dlTag": [- "Nightly",- "base-4.2.6",- "Experimental",- "Latest"- ],- "dlUri": "https:aqsav"- }- },- "Linux_Rocky": {- "( < 1.4.5 && < 3.3.4 && >= 1.2.4 && <= 1.3.4 )": {- "dlCSize": null,- "dlHash": "p",- "dlOutput": "\u0018uQ",- "dlSubdir": "𝃀0\u0004\u0014g",- "dlTag": [],- "dlUri": "http:"- },- "( < 4.1.5 && == 2.2.4 && <= 5.2.3 && <= 1.3.2 && < 5.5.2 && > 5.3.4 && <= 4.1.3 ) || ( <= 4.1.1 ) || ( > 2.3.1 && == 2.3.3 ) || ( == 2.3.2 && < 1.2.2 && < 1.2.2 ) || ( >= 2.1.1 && > 1.1.1 )": {- "dlCSize": -1,- "dlHash": "mdb",- "dlOutput": "y9\u0003\u0002\u0006چN",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [],- "dlUri": "https:rut"- },- "( <= 1.4.4 && > 5.3.3 ) || ( >= 4.2.4 && == 3.2.4 && <= 1.2.4 && > 1.2.2 && <= 4.2.3 && < 2.4.1 ) || ( <= 1.4.3 )": {- "dlCSize": null,- "dlHash": "vz",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "Fz \u001bI^"- },- "dlTag": [- "LatestNightly",- "Prerelease",- "base-1.4.1"- ],- "dlUri": "http:klwri"- },- "( <= 2.6.1 && >= 4.4.5 && == 5.4.1 && >= 3.3.4 ) || ( == 3.2.3 && >= 2.2.3 && > 1.3.3 && > 3.1.2 && < 4.4.3 ) || ( <= 2.2.2 )": {- "dlCSize": -5,- "dlHash": "dkezcms",- "dlOutput": "g\u001d.0/_",- "dlSubdir": "𖽼\u000e\\j",- "dlTag": [- "base-3.5.5",- "LatestNightly",- "LatestPrerelease",- "Nightly",- "Nightly"- ],- "dlUri": "http:bi"- },- "( <= 4.2.3 ) || ( >= 1.3.1 && <= 2.1.4 ) || ( < 3.2.1 && < 3.3.1 ) || ( >= 1.1.2 && == 1.2.2 && > 1.1.1 )": {- "dlCSize": null,- "dlHash": "htvgyzp",- "dlOutput": "",- "dlSubdir": null,- "dlTag": [- "Prerelease",- "Nightly"- ],- "dlUri": "https:lohmaxy"- },- "( == 5.1.5 && < 1.1.2 && == 1.3.4 ) || ( > 2.2.2 && >= 1.3.2 && < 3.2.3 && == 2.2.2 && >= 1.2.1 ) || ( >= 2.1.2 && > 4.3.1 ) || ( > 2.1.1 )": {- "dlCSize": -1,- "dlHash": "rbj",- "dlOutput": "쫤HO6",- "dlSubdir": {- "RegexDir": "J5"- },- "dlTag": [- "Recommended",- "base-6.6.3",- "Latest",- "Recommended",- "LatestPrerelease"- ],- "dlUri": "https:pit"- },- "unknown_versioning": {- "dlCSize": 7,- "dlHash": "",- "dlOutput": "[Q]𤝊\n",- "dlSubdir": null,- "dlTag": [- "Nightly",- "Latest",- "base-6.6.1",- "LatestPrerelease",- "Latest",- "Latest",- ""- ],- "dlUri": "https:zc"- }- },- "Windows": {- "( < 1.2.2 && <= 2.4.1 && > 5.3.5 && < 3.5.4 && == 2.5.4 )": {- "dlCSize": 3,- "dlHash": "jtzji",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "|E\u0012܍"- },- "dlTag": [- "Experimental"- ],- "dlUri": "https:jpeifyl"- },- "( < 3.4.4 && < 3.2.5 && <= 1.4.1 && <= 5.2.6 && > 2.3.2 )": {- "dlCSize": null,- "dlHash": "cprqudk",- "dlOutput": "3蜜\u0018y]",- "dlSubdir": {- "RegexDir": "s\r\u001f^"- },- "dlTag": [- "Nightly",- "Experimental"- ],- "dlUri": "http:"- },- "( <= 4.5.5 && <= 4.4.1 && > 3.3.1 && == 4.1.5 )": {- "dlCSize": -5,- "dlHash": "ul",- "dlOutput": "zb𗤴",- "dlSubdir": "%:䌈",- "dlTag": [- "LatestNightly",- "",- "LatestNightly",- "Recommended",- "old",- "base-2.4.3",- "LatestPrerelease"- ],- "dlUri": "https:mhl"- },- "( >= 2.4.3 && < 2.1.2 && == 1.5.4 && >= 5.2.5 ) || ( == 1.3.1 )": {- "dlCSize": null,- "dlHash": "gfi",- "dlOutput": "䥴\u00192\u000cD",- "dlSubdir": null,- "dlTag": [],- "dlUri": "http:zgt"- }- }- },- "A_Sparc": {- "Darwin": {- "( < 1.3.2 && > 2.4.4 && >= 5.5.1 )": {- "dlCSize": 3,- "dlHash": "zsivo",- "dlOutput": "g[",- "dlSubdir": null,- "dlTag": [- "LatestNightly",- "Recommended",- "Experimental"- ],- "dlUri": "http:lyrjv"- },- "( < 2.2.1 ) || ( <= 4.1.2 && >= 1.2.3 && <= 3.4.1 && < 3.1.4 && > 3.2.4 && >= 2.3.2 )": {- "dlCSize": 0,- "dlHash": "u",- "dlOutput": "YD=",- "dlSubdir": {- "RegexDir": "\u0011𡹏%"- },- "dlTag": [],- "dlUri": "https:k"- },- "( < 4.3.2 && == 5.5.4 && == 3.5.4 && < 2.1.2 && == 1.2.3 ) || ( < 4.1.1 && >= 4.4.4 && < 1.1.3 && > 2.2.2 && > 1.3.2 && > 1.3.3 ) || ( == 1.3.2 && < 3.1.3 && > 1.1.1 ) || ( < 2.1.2 )": {- "dlCSize": 7,- "dlHash": "g",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "z𱲰"- },- "dlTag": [- "",- "Nightly",- "Recommended",- "Prerelease",- "Nightly"- ],- "dlUri": "http:vmxsaan"- },- "( <= 1.2.1 )": {- "dlCSize": 2,- "dlHash": "j",- "dlOutput": "1",- "dlSubdir": {- "RegexDir": "\u0018)"- },- "dlTag": null,- "dlUri": "https:lptwfeb"- },- "( <= 4.1.3 && == 4.4.5 && <= 3.5.1 && <= 5.3.2 && <= 3.3.2 && >= 5.5.5 && >= 3.3.1 )": {- "dlCSize": -3,- "dlHash": "yzrsgx",- "dlOutput": "\u0018쨃",- "dlSubdir": {- "RegexDir": "*"- },- "dlTag": [- "Prerelease",- "LatestNightly",- "\u0012\u001a",- "old",- "base-5.3.5"- ],- "dlUri": "http:bilaipn"- },- "( >= 3.4.2 && <= 3.4.2 && <= 4.4.4 && > 1.1.4 && >= 3.1.4 && > 2.1.1 && > 3.2.4 ) || ( >= 1.1.4 && < 2.2.4 ) || ( <= 2.1.1 && > 2.2.1 && >= 1.3.1 && < 2.3.1 )": {- "dlCSize": -2,- "dlHash": "yjpv",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "^\u001d"- },- "dlTag": [- "Prerelease",- "Experimental",- "Latest",- "LatestPrerelease",- "Experimental"- ],- "dlUri": "http:hdvmeb"- }- },- "FreeBSD": {- "( < 2.3.5 && > 4.4.4 && > 5.1.4 ) || ( == 4.1.2 && == 3.4.3 && > 2.1.2 && < 2.3.2 ) || ( >= 1.2.1 && >= 3.1.2 && > 2.1.3 )": {- "dlCSize": -4,- "dlHash": "nrwwht",- "dlOutput": "",- "dlSubdir": "𫺿rg\u001cZ",- "dlTag": null,- "dlUri": "http:"- },- "( <= 3.5.1 && > 1.1.1 && > 4.5.2 && > 4.5.4 && <= 4.3.3 && > 3.3.5 && > 3.4.2 ) || ( > 2.1.2 && <= 1.2.2 && < 1.3.2 && < 2.3.3 ) || ( <= 2.1.3 ) || ( == 1.1.1 && < 2.1.2 && <= 2.1.1 )": {- "dlCSize": null,- "dlHash": "zsmwen",- "dlOutput": null,- "dlSubdir": "z",- "dlTag": [- "Recommended",- "Prerelease",- "Experimental",- "LatestNightly",- "Prerelease",- "Latest",- "Recommended"- ],- "dlUri": "http:fbpsc"- },- "( <= 5.1.2 && < 2.5.4 && >= 3.5.6 && <= 3.5.4 ) || ( < 3.3.2 && >= 2.1.2 )": {- "dlCSize": null,- "dlHash": "azs",- "dlOutput": "S",- "dlSubdir": "\u001a\u0012𧙺\u0017\u0000",- "dlTag": [- "",- "\u00120",- "Recommended",- "LatestNightly",- "X𠣇\u0018쥷\\\u0014"- ],- "dlUri": "http:gcig"- },- "( >= 2.2.2 && == 1.2.1 && >= 3.5.5 && <= 4.1.4 && <= 5.3.1 )": {- "dlCSize": 3,- "dlHash": "gznp",- "dlOutput": "\\0|\u0016m\"",- "dlSubdir": "I",- "dlTag": null,- "dlUri": "http:ciuxtjl"- },- "( >= 3.2.4 && >= 4.5.3 && == 3.3.3 && < 4.2.5 && == 3.3.1 && < 4.3.1 )": {- "dlCSize": 3,- "dlHash": "eoacjxu",- "dlOutput": ")x𣷭\u0011\u001b",- "dlSubdir": {- "RegexDir": "6]"- },- "dlTag": [- "Nightly",- "LatestPrerelease",- "LatestPrerelease",- "Recommended"- ],- "dlUri": "https:c"- },- "unknown_versioning": {- "dlCSize": -5,- "dlHash": "ukhu",- "dlOutput": null,- "dlSubdir": "a9",- "dlTag": null,- "dlUri": "http:dzllg"- }- },- "Linux_CentOS": {- "( == 2.5.3 && >= 1.4.1 ) || ( <= 2.4.2 && >= 4.5.2 && > 2.3.3 && >= 3.1.3 && < 1.3.1 && >= 3.4.3 ) || ( == 3.1.1 && <= 2.1.2 && >= 3.2.1 && >= 3.1.2 ) || ( <= 2.1.1 && > 3.1.1 )": {- "dlCSize": 2,- "dlHash": "",- "dlOutput": "\u0002\u000c\u0005\"tG",- "dlSubdir": null,- "dlTag": [- "Nightly",- "old",- "LatestPrerelease",- "I"- ],- "dlUri": "https:hdl"- }- },- "Linux_Debian": {},- "Windows": {- "( < 1.5.1 && >= 4.5.5 && >= 2.2.4 )": {- "dlCSize": -2,- "dlHash": "tvf",- "dlOutput": "",- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:"- },- "( < 2.3.1 && > 2.4.2 && == 3.4.5 && > 4.4.4 && == 4.5.2 && >= 3.3.4 ) || ( <= 1.5.2 && > 4.3.1 && <= 2.3.2 && < 1.2.4 && >= 3.3.4 && >= 4.3.4 )": {- "dlCSize": -7,- "dlHash": "iwn",- "dlOutput": "𡶘0𖨰!ㆸ",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [],- "dlUri": "http:"- },- "( > 1.2.4 && < 6.2.3 && >= 2.2.2 ) || ( <= 2.3.3 ) || ( == 1.1.3 && < 1.3.2 && >= 2.1.2 ) || ( >= 2.1.1 && <= 2.1.2 )": {- "dlCSize": -4,- "dlHash": "h",- "dlOutput": "",- "dlSubdir": "VQ+W",- "dlTag": null,- "dlUri": "https:pzln"- },- "( > 2.1.5 && == 3.5.3 && > 1.1.3 && > 3.3.2 && <= 4.3.4 && <= 4.3.5 )": {- "dlCSize": null,- "dlHash": "",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "?𗈸|_R\u0008"- },- "dlTag": [- "Experimental",- "base-5.3.2",- "LatestNightly",- "LatestPrerelease",- "LatestPrerelease",- "Experimental"- ],- "dlUri": "https:gxzpkau"- },- "unknown_versioning": {- "dlCSize": -5,- "dlHash": "yhn",- "dlOutput": "\\77",- "dlSubdir": null,- "dlTag": [- "old",- "Recommended",- "old",- "Latest",- "Recommended",- "old"- ],- "dlUri": "http:qyfpzss"- }- }- },- "A_Sparc64": {- "FreeBSD": {- "( <= 4.2.1 && <= 3.1.2 ) || ( >= 2.4.2 && <= 1.4.3 && < 1.3.2 && == 3.4.3 ) || ( == 3.2.3 && == 1.1.4 )": {- "dlCSize": 1,- "dlHash": "frf",- "dlOutput": "ru\u0015",- "dlSubdir": {- "RegexDir": "'q냘>"- },- "dlTag": null,- "dlUri": "http:zawah"- },- "( == 3.3.3 && == 3.1.4 && <= 3.3.4 && <= 3.1.4 && <= 4.2.1 && > 5.5.1 )": {- "dlCSize": 2,- "dlHash": "ftey",- "dlOutput": "3\u000f",- "dlSubdir": {- "RegexDir": "%<\u000fuY"- },- "dlTag": [- "LatestNightly",- "LatestNightly",- "LatestPrerelease",- "Latest",- "Recommended"- ],- "dlUri": "https:oq"- },- "( > 4.2.2 && > 5.5.5 && < 2.1.2 && >= 1.4.1 && <= 1.1.5 && < 2.5.2 && < 1.2.5 ) || ( > 1.2.1 && < 4.1.3 && > 4.4.1 ) || ( > 1.3.1 && >= 1.2.3 && < 1.1.1 && <= 1.2.3 ) || ( == 2.2.1 && < 1.3.1 ) || ( <= 1.2.1 && > 1.2.1 && < 1.1.1 ) || ( < 1.2.1 )": {- "dlCSize": -1,- "dlHash": "hsnw",- "dlOutput": "ꋸF\u0018Ya|",- "dlSubdir": "",- "dlTag": [],- "dlUri": "http:bkfw"- },- "( > 5.2.2 && == 5.1.2 && >= 4.2.5 && <= 2.2.5 && < 6.6.3 && == 2.2.3 && == 4.4.3 ) || ( >= 1.3.2 && > 2.2.1 && > 1.2.2 && <= 3.4.2 ) || ( <= 3.3.2 && >= 2.3.3 && <= 3.3.1 && == 1.2.1 ) || ( == 1.1.1 && >= 2.2.2 && >= 2.1.1 )": {- "dlCSize": null,- "dlHash": "zm",- "dlOutput": "2",- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:ojmsmv"- },- "( >= 4.1.4 && <= 1.3.5 && > 1.1.1 && < 2.5.3 && == 5.5.1 ) || ( > 4.2.2 ) || ( >= 2.1.1 && < 3.1.1 && <= 4.3.2 && < 1.1.1 && >= 1.2.3 ) || ( > 2.1.2 && <= 2.2.1 && <= 1.1.2 && < 2.4.1 ) || ( == 2.1.1 && >= 1.1.1 && <= 1.1.2 ) || ( <= 1.2.2 && > 1.2.1 ) || ( > 1.1.1 ) || ( >= 1.1.1 )": {- "dlCSize": 1,- "dlHash": "rht",- "dlOutput": "%",- "dlSubdir": "H",- "dlTag": [- "Recommended"- ],- "dlUri": "http:"- },- "unknown_versioning": {- "dlCSize": 2,- "dlHash": "nwl",- "dlOutput": "S&h+",- "dlSubdir": null,- "dlTag": [- "old",- "LatestNightly",- "Nightly"- ],- "dlUri": "http:blo"- }- },- "Linux_Void": {- "( < 1.2.3 && >= 5.4.4 && > 3.5.4 ) || ( < 1.5.2 && < 1.2.2 && <= 2.4.2 && <= 4.4.4 && < 4.3.1 && >= 4.4.4 ) || ( > 1.3.2 && > 3.1.1 && <= 1.1.3 )": {- "dlCSize": 0,- "dlHash": "s",- "dlOutput": "䀛㸭\u0006",- "dlSubdir": {- "RegexDir": "\u0010x"- },- "dlTag": null,- "dlUri": "https:guaiat"- },- "( <= 3.3.4 && == 3.1.3 )": {- "dlCSize": -1,- "dlHash": "tfihyxh",- "dlOutput": "c\u0016𫋱M%^",- "dlSubdir": "*",- "dlTag": [- "LatestPrerelease",- "Recommended",- "Recommended"- ],- "dlUri": "http:mhytnl"- },- "( > 2.2.4 && < 2.5.2 && > 5.2.1 && > 4.5.4 )": {- "dlCSize": -3,- "dlHash": "tmuf",- "dlOutput": "g𨈱",- "dlSubdir": {- "RegexDir": "6WpZ"- },- "dlTag": null,- "dlUri": "https:uunun"- },- "unknown_versioning": {- "dlCSize": 0,- "dlHash": "xnxzwkm",- "dlOutput": null,- "dlSubdir": "",- "dlTag": [- "LatestPrerelease",- "Nightly",- "LatestNightly",- "𦩪B\\"- ],- "dlUri": "https:"- }- },- "Windows": {- "( < 3.2.5 && >= 2.4.1 )": {- "dlCSize": -4,- "dlHash": "ujst",- "dlOutput": "5i\"*[=W",- "dlSubdir": {- "RegexDir": "\r]4𣯈𓁑"- },- "dlTag": null,- "dlUri": "https:vjzs"- },- "( <= 3.2.5 && <= 5.5.3 && >= 3.2.4 )": {- "dlCSize": 2,- "dlHash": "sx",- "dlOutput": null,- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "LatestPrerelease",- "LatestPrerelease",- "",- "base-1.4.2",- "base-5.2.6",- "Experimental",- "Latest"- ],- "dlUri": "http:pmtmo"- },- "( == 2.5.4 && == 5.3.3 && <= 1.1.4 && >= 3.2.4 && < 5.3.4 && > 5.1.2 ) || ( >= 4.1.4 && < 2.1.2 && >= 1.2.2 && >= 2.4.1 && == 1.4.1 && >= 3.1.2 )": {- "dlCSize": 5,- "dlHash": "krhmi",- "dlOutput": "0HC𣿡r)s",- "dlSubdir": {- "RegexDir": "/7 "- },- "dlTag": null,- "dlUri": "http:t"- },- "( == 3.4.1 && < 5.4.1 && == 3.1.3 && > 4.4.5 && >= 5.3.4 && <= 4.5.2 && >= 1.5.3 )": {- "dlCSize": 2,- "dlHash": "gcljafm",- "dlOutput": "o",- "dlSubdir": null,- "dlTag": [],- "dlUri": "http:wnusahu"- },- "( > 3.2.2 && == 5.3.6 && < 5.5.4 && >= 4.3.3 && <= 3.4.2 && == 4.3.4 ) || ( > 2.4.1 && <= 4.3.4 )": {- "dlCSize": null,- "dlHash": "dhm",- "dlOutput": null,- "dlSubdir": "E:𢀬L",- "dlTag": [],- "dlUri": "http:fj"- },- "( > 4.1.6 && >= 2.1.5 && > 1.3.1 && == 4.5.3 && <= 2.5.1 && <= 2.3.2 )": {- "dlCSize": -5,- "dlHash": "jna",- "dlOutput": "[",- "dlSubdir": "\u000e+w2",- "dlTag": [- "LatestPrerelease",- "",- "LatestNightly",- "LatestNightly",- "Latest",- "Recommended"- ],- "dlUri": "http:eb"- },- "( > 4.3.1 && > 2.2.2 && > 4.2.4 && < 4.4.2 ) || ( == 4.2.3 && <= 4.1.2 && >= 3.3.4 && >= 3.1.2 ) || ( < 1.1.2 && == 1.3.2 && >= 1.1.3 && == 3.1.1 && >= 2.2.2 )": {- "dlCSize": 4,- "dlHash": "kfjgqd",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Nightly",- "Nightly",- "Nightly"- ],- "dlUri": "https:cnurnyl"- }- }- }- },- "viChangeLog": "https:dew",- "viPostInstall": "qxtaqw",- "viPostRemove": null,- "viPreCompile": null,- "viPreInstall": "v",- "viReleaseDay": "-18790648527864394-12-26",- "viSourceDL": {- "dlCSize": -5,- "dlHash": "emc",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "LatestPrerelease",- "LatestPrerelease",- "",- "Q\u001cj"- ],- "dlUri": "https:f"- },- "viTags": [- "]"- ],- "viTestDL": {- "dlCSize": null,- "dlHash": "jzsvl",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "\u0007\u0011\u0008\u0008"- },- "dlTag": null,- "dlUri": "https:kg"- }- }- },- "GHCup": {- "4.6.4": {- "viArch": {- "A_ARM64": {- "Darwin": {- "( == 5.4.2 && == 5.1.2 && == 5.2.2 && >= 1.1.1 ) || ( == 1.2.3 && >= 3.4.3 )": {- "dlCSize": 6,- "dlHash": "ggvkccn",- "dlOutput": "q\u0007뻀%",- "dlSubdir": null,- "dlTag": [- "㍨ꊥ[",- "LatestNightly",- "Latest"- ],- "dlUri": "http:m"- },- "( >= 2.5.4 && == 4.5.5 && <= 2.5.5 ) || ( == 2.3.1 && >= 1.2.3 && > 1.5.3 && > 4.3.1 )": {- "dlCSize": 1,- "dlHash": "ffdynd",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "H"- },- "dlTag": [- "Recommended"- ],- "dlUri": "http:xkgtwg"- }- },- "FreeBSD": {- "( < 3.1.3 && >= 4.1.3 && == 3.4.1 && == 5.3.2 && == 3.4.2 && == 5.5.2 )": {- "dlCSize": -2,- "dlHash": "d",- "dlOutput": "\u0011",- "dlSubdir": "Y",- "dlTag": [- "Recommended",- "old",- "Prerelease",- "\u001b$"- ],- "dlUri": "http:wx"- },- "( < 5.2.3 && == 1.4.2 && >= 3.1.1 && <= 5.4.5 ) || ( >= 1.3.1 && <= 4.4.3 && > 3.4.2 && <= 1.2.2 ) || ( > 2.1.3 && > 3.2.2 ) || ( > 1.2.2 && > 1.2.1 ) || ( >= 1.1.1 && <= 1.2.1 )": {- "dlCSize": 2,- "dlHash": "ocatngv",- "dlOutput": "橽n",- "dlSubdir": "둮\u0001",- "dlTag": null,- "dlUri": "https:derm"- },- "( == 5.5.1 && == 3.3.3 && == 1.5.4 && == 1.5.3 && <= 3.5.1 && >= 1.2.1 )": {- "dlCSize": 4,- "dlHash": "h",- "dlOutput": "t",- "dlSubdir": {- "RegexDir": "9B\u0015}"- },- "dlTag": [- "LatestPrerelease",- "old",- "LatestPrerelease",- "LatestPrerelease",- "old"- ],- "dlUri": "http:gmgzjc"- },- "unknown_versioning": {- "dlCSize": 7,- "dlHash": "amlpoga",- "dlOutput": "*z\u000e\u0010",- "dlSubdir": "\u0017d",- "dlTag": [- "Latest"- ],- "dlUri": "https:quqb"- }- }- }- },- "viChangeLog": "https:mq",- "viPostInstall": null,- "viPostRemove": "dgpvm",- "viPreCompile": null,- "viPreInstall": "dsoxxea",- "viReleaseDay": "-19034419492295200-03-11",- "viSourceDL": null,- "viTags": [],- "viTestDL": {- "dlCSize": 3,- "dlHash": "ykdpi",- "dlOutput": "厍0",- "dlSubdir": {- "RegexDir": "𪒼jL"- },- "dlTag": [- "Recommended",- "Prerelease",- "Prerelease"- ],- "dlUri": "http:hsc"- }- },- "5.7.1": {- "viArch": {- "A_Sparc64": {- "FreeBSD": {- "unknown_versioning": {- "dlCSize": -1,- "dlHash": "sij",- "dlOutput": null,- "dlSubdir": "剝!\u0013O$",- "dlTag": [],- "dlUri": "http:do"- }- }- }- },- "viChangeLog": "https:mwwo",- "viPostInstall": null,- "viPostRemove": "vmyc",- "viPreCompile": "p",- "viPreInstall": null,- "viReleaseDay": null,- "viSourceDL": null,- "viTags": [- "Recommended",- "Nightly",- "Latest",- "LatestNightly"- ],- "viTestDL": {- "dlCSize": -6,- "dlHash": "uwkv",- "dlOutput": "[\u000cZ4",- "dlSubdir": {- "RegexDir": "𫢳n"- },- "dlTag": [- "LatestPrerelease"- ],- "dlUri": "http:vb"- }- },- "8.8.6": {- "viArch": {- "A_ARM": {- "Darwin": {- "( < 1.4.2 ) || ( < 3.4.1 && < 1.4.1 ) || ( == 1.2.3 && > 2.2.3 )": {- "dlCSize": -7,- "dlHash": "vsoqdgl",- "dlOutput": "𦃘\u0002𩮫\u0018",- "dlSubdir": "\u0003𫆛𞋿\u0011%",- "dlTag": [- "Latest",- "",- "Latest"- ],- "dlUri": "https:be"- },- "( <= 1.1.4 ) || ( < 3.3.1 && == 4.4.3 && >= 1.4.4 && <= 2.1.4 && < 2.2.4 ) || ( == 3.2.2 && > 2.1.2 && == 3.2.1 && <= 2.2.2 ) || ( == 2.1.2 && >= 2.1.2 && <= 1.1.1 && == 1.1.2 )": {- "dlCSize": 4,- "dlHash": "pk",- "dlOutput": "e,2\u0019",- "dlSubdir": "Bj",- "dlTag": null,- "dlUri": "https:qcxotfq"- },- "( <= 1.4.4 && > 3.1.4 && <= 3.5.1 && >= 6.2.4 && == 4.5.1 && <= 1.2.1 && >= 5.5.3 )": {- "dlCSize": -7,- "dlHash": "cxl",- "dlOutput": "",- "dlSubdir": "oo",- "dlTag": [],- "dlUri": "http:jf"- },- "( == 1.2.3 && > 2.5.3 ) || ( >= 4.1.3 && == 3.3.1 && < 4.4.3 )": {- "dlCSize": -1,- "dlHash": "bxl",- "dlOutput": "𬾨",- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:ep"- }- },- "FreeBSD": {- "( < 4.2.4 && == 1.2.4 )": {- "dlCSize": 2,- "dlHash": "pplvyqh",- "dlOutput": "dBR",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [],- "dlUri": "https:zs"- },- "( <= 3.5.2 && < 4.5.4 && == 2.5.4 )": {- "dlCSize": -4,- "dlHash": "mfthpv",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Recommended",- "Ti"- ],- "dlUri": "https:ejvz"- },- "( > 2.1.2 ) || ( < 4.4.4 && > 1.3.1 ) || ( >= 2.3.1 && < 2.1.1 ) || ( < 1.1.2 )": {- "dlCSize": -2,- "dlHash": "zdxl",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "5{\u0016T7"- },- "dlTag": [- "base-5.1.6",- "base-6.5.1",- "Experimental",- "Prerelease",- "Latest",- "Prerelease",- "LatestPrerelease"- ],- "dlUri": "https:uplxp"- },- "( >= 2.4.2 && < 1.1.1 && > 2.3.4 && >= 2.5.5 && == 5.1.3 )": {- "dlCSize": 0,- "dlHash": "",- "dlOutput": "T𗉈-",- "dlSubdir": {- "RegexDir": "3"- },- "dlTag": [- "Experimental",- "Latest",- "Recommended",- "𱺔",- "\u0007𗩖#-",- "LatestPrerelease",- "LatestNightly"- ],- "dlUri": "http:ttf"- },- "( >= 3.5.2 && <= 2.2.3 && == 3.3.2 ) || ( == 4.3.4 && <= 3.4.4 && > 4.2.4 && == 1.3.5 && <= 2.2.2 ) || ( <= 3.3.2 && < 2.1.2 ) || ( <= 2.1.1 )": {- "dlCSize": 6,- "dlHash": "qizvnd",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "Za\u0019"- },- "dlTag": [- "Nightly"- ],- "dlUri": "https:kdw"- },- "unknown_versioning": {- "dlCSize": -7,- "dlHash": "awgrykd",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "坶"- },- "dlTag": [- "Latest",- "𫐥V+g",- "LatestNightly"- ],- "dlUri": "https:ojp"- }- },- "Linux_OpenSUSE": {- "( < 1.5.5 && >= 2.6.4 ) || ( <= 4.1.1 && == 1.4.4 && >= 1.1.3 && >= 2.3.1 && < 4.3.3 )": {- "dlCSize": 6,- "dlHash": "tnrzgar",- "dlOutput": "𮩦~L",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": null,- "dlUri": "http:sat"- },- "( < 1.6.1 && > 1.4.1 && == 2.2.4 && >= 2.3.4 )": {- "dlCSize": -3,- "dlHash": "ehak",- "dlOutput": "Y\"? *7",- "dlSubdir": "pc\u000c",- "dlTag": [],- "dlUri": "https:pxbfsgz"- },- "( < 4.5.5 && <= 4.1.3 && >= 5.2.5 && <= 4.2.2 && >= 2.1.1 ) || ( >= 2.2.3 && >= 3.1.4 && == 4.2.2 && >= 4.3.1 && < 3.1.2 && > 4.1.4 ) || ( == 1.1.1 && == 2.1.3 && > 3.1.2 && <= 2.1.3 && >= 4.1.2 ) || ( < 3.1.2 && > 2.1.1 && >= 1.2.3 )": {- "dlCSize": 3,- "dlHash": "u",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "m𨚣"- },- "dlTag": null,- "dlUri": "https:cmheb"- },- "( <= 1.1.5 && > 2.4.4 && < 5.2.3 && >= 4.4.4 && > 3.2.1 && == 5.6.5 && >= 2.2.1 ) || ( == 1.4.1 && <= 1.4.1 && >= 3.4.1 && >= 2.3.3 && > 2.3.3 )": {- "dlCSize": -1,- "dlHash": "lbcyxu",- "dlOutput": "\u0003𢒬",- "dlSubdir": {- "RegexDir": "X𡾸^\u001d"- },- "dlTag": [- "LatestPrerelease",- "Latest",- "Prerelease",- "Nightly"- ],- "dlUri": "http:dpng"- },- "( == 1.4.1 && < 2.5.5 && >= 4.2.3 ) || ( <= 4.3.3 && <= 3.4.4 && > 4.1.4 ) || ( < 3.2.3 && <= 3.2.2 && <= 3.2.4 )": {- "dlCSize": -5,- "dlHash": "uhxopjy",- "dlOutput": null,- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:xbmem"- },- "( == 2.3.5 && > 4.2.5 && <= 4.1.5 && < 2.3.1 )": {- "dlCSize": null,- "dlHash": "",- "dlOutput": null,- "dlSubdir": "\u001b",- "dlTag": [- "Prerelease",- "LatestPrerelease",- "old",- "Latest",- "LatestNightly"- ],- "dlUri": "http:bfudzyy"- },- "( >= 4.5.3 && >= 1.4.1 && > 3.5.3 && >= 3.3.5 && > 3.5.5 && < 2.3.4 )": {- "dlCSize": null,- "dlHash": "",- "dlOutput": "hm,\\.",- "dlSubdir": "",- "dlTag": [],- "dlUri": "https:ismkbcx"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "nhp",- "dlOutput": "W\u000cy~랰",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [],- "dlUri": "https:g"- }- },- "Windows": {- "( == 4.3.3 && > 5.5.1 && <= 5.5.1 && < 4.2.3 && < 4.2.2 && < 4.2.6 && > 4.5.1 )": {- "dlCSize": -6,- "dlHash": "gyuti",- "dlOutput": "\u001a\u0006",- "dlSubdir": {- "RegexDir": "-}?R"- },- "dlTag": [- "Recommended"- ],- "dlUri": "https:ug"- },- "( >= 1.5.2 && == 1.5.2 && < 4.1.3 ) || ( >= 3.1.3 && < 4.4.4 && >= 1.2.3 && > 1.1.1 && < 1.4.4 ) || ( < 3.2.3 && <= 2.3.3 && > 2.4.2 && < 1.3.3 ) || ( < 2.2.2 && == 1.1.1 && >= 1.1.1 && >= 1.1.2 )": {- "dlCSize": 4,- "dlHash": "ebcd",- "dlOutput": "摚gjAz\u0019",- "dlSubdir": {- "RegexDir": "@}8l"- },- "dlTag": null,- "dlUri": "https:vxahgo"- },- "unknown_versioning": {- "dlCSize": -2,- "dlHash": "piqhfgz",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Experimental",- "Latest",- "LatestPrerelease",- "old"- ],- "dlUri": "https:qk"- }- }- },- "A_ARM64": {- "Linux_AmazonLinux": {- "( < 3.2.3 && == 4.2.3 && >= 3.3.5 && == 1.2.2 )": {- "dlCSize": -5,- "dlHash": "nzos",- "dlOutput": "eS(..",- "dlSubdir": {- "RegexDir": ")"- },- "dlTag": [- "old",- "Prerelease",- "base-6.6.4",- "Prerelease",- "Experimental",- "Latest",- "base-1.5.2"- ],- "dlUri": "https:jnszxp"- },- "( <= 1.1.2 && <= 2.4.1 && <= 3.4.1 && >= 1.5.4 && == 3.2.5 && < 3.2.2 ) || ( < 1.4.4 && > 2.4.3 && > 1.1.4 && < 2.2.4 )": {- "dlCSize": 5,- "dlHash": "ncbjkf",- "dlOutput": "",- "dlSubdir": null,- "dlTag": [],- "dlUri": "http:gvootri"- },- "( <= 5.3.5 && < 5.5.2 && >= 2.1.1 && == 2.2.3 && < 1.3.5 ) || ( == 4.3.4 && >= 3.2.1 && <= 4.3.2 && == 5.4.1 && < 3.2.4 && > 4.1.4 ) || ( >= 3.1.3 )": {- "dlCSize": null,- "dlHash": "trdvd",- "dlOutput": "]\u0001持j",- "dlSubdir": {- "RegexDir": "衶k9\u0007"- },- "dlTag": [- "Experimental",- "Latest",- "Latest",- "Recommended",- "old"- ],- "dlUri": "http:tdf"- },- "( == 3.4.1 && == 4.4.5 && <= 2.3.3 && < 1.1.2 )": {- "dlCSize": null,- "dlHash": "ffn",- "dlOutput": "𰋹e",- "dlSubdir": "𥹖b",- "dlTag": [- "\u000f\u0013\u0005",- "base-4.5.3",- "Nightly"- ],- "dlUri": "http:"- },- "( == 3.5.1 && < 3.4.4 ) || ( < 2.3.3 && > 5.3.3 ) || ( <= 4.1.3 && == 3.1.3 && > 2.1.2 && > 1.1.1 )": {- "dlCSize": -7,- "dlHash": "kwvwyh",- "dlOutput": ">𗐕\u0014",- "dlSubdir": {- "RegexDir": "i\t"- },- "dlTag": [- "Nightly",- "Nightly",- "old",- "\u0008",- "LatestPrerelease",- "_䒖\t"- ],- "dlUri": "https:zu"- },- "( > 1.6.3 && >= 4.4.2 && < 3.2.3 )": {- "dlCSize": null,- "dlHash": "m",- "dlOutput": "z:",- "dlSubdir": null,- "dlTag": [- "Experimental",- "1\u0017b",- "Experimental",- "Experimental",- "Qho,8",- "Prerelease",- "\u0000!\u0015"- ],- "dlUri": "https:njun"- },- "( >= 2.5.2 && <= 2.3.1 ) || ( <= 4.3.3 ) || ( <= 2.1.1 && < 3.2.3 && >= 2.3.3 && <= 3.3.3 && == 1.1.1 ) || ( >= 2.2.2 && > 2.2.1 && <= 1.2.2 && >= 2.3.1 )": {- "dlCSize": 6,- "dlHash": "s",- "dlOutput": null,- "dlSubdir": "b\u0018#&",- "dlTag": [- "LatestPrerelease",- ""- ],- "dlUri": "http:d"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "pwd",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "hX \u0006J"- },- "dlTag": [- "LatestNightly",- "base-6.2.6",- "LatestPrerelease",- "Prerelease",- "Latest",- "Experimental",- "base-6.4.2"- ],- "dlUri": "http:"- }- },- "Linux_Rocky": {- "( > 1.1.3 )": {- "dlCSize": 7,- "dlHash": "x",- "dlOutput": ";\u0007l3WI",- "dlSubdir": {- "RegexDir": "`}r‟\u0004"- },- "dlTag": [- "Recommended",- "Prerelease",- "Recommended",- "Experimental",- "old",- "old",- "LatestPrerelease"- ],- "dlUri": "https:drj"- },- "( >= 1.5.2 && < 4.2.4 && > 4.2.2 && == 5.5.4 && < 5.1.5 )": {- "dlCSize": -1,- "dlHash": "nod",- "dlOutput": null,- "dlSubdir": "\u0004\u001e\u0003𘁯",- "dlTag": [- "old",- "Latest",- "Recommended"- ],- "dlUri": "http:zsalcv"- }- },- "Windows": {- "( == 5.2.5 && == 4.1.4 && > 2.1.4 && == 4.3.3 && == 3.3.2 && >= 1.5.1 && == 5.6.2 ) || ( <= 4.1.1 && > 3.4.3 )": {- "dlCSize": 3,- "dlHash": "itcqa",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "\u0013c\"k𡮇\u001d"- },- "dlTag": [],- "dlUri": "https:q"- },- "unknown_versioning": {- "dlCSize": 7,- "dlHash": "zvtbkrx",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "k\u001bI]1"- },- "dlTag": [- "+畗",- "LatestPrerelease",- "old",- "old",- "LatestPrerelease"- ],- "dlUri": "https:rysalju"- }- }- },- "A_PowerPC64": {- "Darwin": {- "( <= 2.3.1 && <= 5.1.2 && >= 5.3.3 ) || ( <= 4.4.4 )": {- "dlCSize": 3,- "dlHash": "bx",- "dlOutput": "mE~f\u000b𦊾",- "dlSubdir": {- "RegexDir": "d\u001e'i\""- },- "dlTag": null,- "dlUri": "https:lsd"- },- "( <= 5.5.2 && > 3.4.4 ) || ( >= 4.4.1 && <= 4.3.1 )": {- "dlCSize": null,- "dlHash": "tczq",- "dlOutput": "r",- "dlSubdir": "S?",- "dlTag": [- "old",- "LatestNightly",- "LatestNightly",- "$\u0007im",- "LatestNightly",- "Latest",- "LatestPrerelease"- ],- "dlUri": "http:da"- },- "( == 4.5.2 && < 3.2.3 && >= 2.4.1 && < 2.1.2 )": {- "dlCSize": 5,- "dlHash": "flxjha",- "dlOutput": "qn*\u0007<",- "dlSubdir": null,- "dlTag": [- "Prerelease",- "Recommended",- "Prerelease",- "Recommended",- "Recommended"- ],- "dlUri": "http:zl"- },- "( > 5.3.4 && == 3.1.2 && > 3.3.1 && < 5.4.4 && < 1.2.4 ) || ( > 1.3.3 && <= 3.1.5 && >= 1.3.3 && > 4.4.4 && == 1.3.1 ) || ( <= 2.3.3 && > 1.1.1 )": {- "dlCSize": -3,- "dlHash": "",- "dlOutput": "\u0001\u0010~𣉥𪞦\u0005S",- "dlSubdir": null,- "dlTag": [],- "dlUri": "http:w"- },- "( >= 1.1.1 && == 1.5.3 && < 2.4.4 )": {- "dlCSize": null,- "dlHash": "tv",- "dlOutput": "e0#d𒎑$",- "dlSubdir": "\u000fO\n",- "dlTag": [- "Experimental",- "LatestPrerelease",- "Experimental",- "Latest",- "Prerelease",- "LatestNightly",- "old"- ],- "dlUri": "http:"- },- "( >= 1.5.3 && > 1.3.4 && <= 3.5.2 && <= 3.3.2 )": {- "dlCSize": -4,- "dlHash": "w",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "\u001e\u000e\u0015"- },- "dlTag": null,- "dlUri": "http:mbp"- },- "( >= 4.3.4 && >= 5.5.3 && == 4.2.5 && >= 2.1.3 && <= 4.4.2 )": {- "dlCSize": 7,- "dlHash": "zf",- "dlOutput": "G0i@.\u0015ﻇ",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "old",- "base-1.1.5",- "Prerelease",- "Experimental",- "LatestPrerelease",- "LatestPrerelease"- ],- "dlUri": "https:ftlweht"- },- "unknown_versioning": {- "dlCSize": -6,- "dlHash": "cvpifx",- "dlOutput": "𱭘D#ᇪ𩤶",- "dlSubdir": {- "RegexDir": "g\u001b\u0018"- },- "dlTag": [- "LatestNightly",- "LatestPrerelease",- "LatestPrerelease"- ],- "dlUri": "https:dgs"- }- },- "FreeBSD": {},- "Linux_Mint": {- "( < 4.2.4 && <= 1.5.5 && == 1.4.3 && <= 1.4.1 && <= 4.3.5 && <= 1.5.5 ) || ( <= 3.2.2 )": {- "dlCSize": -3,- "dlHash": "aocqrpm",- "dlOutput": "L1I",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "Experimental"- ],- "dlUri": "https:dr"- },- "( <= 3.5.3 && >= 1.1.3 && >= 1.3.2 ) || ( > 1.3.2 && <= 3.2.1 && >= 3.2.4 && == 3.1.4 && > 1.2.1 && == 2.5.4 ) || ( < 3.1.2 && <= 3.3.3 && == 2.4.2 && >= 1.1.2 )": {- "dlCSize": 2,- "dlHash": "rl",- "dlOutput": "n",- "dlSubdir": {- "RegexDir": "\"i"- },- "dlTag": null,- "dlUri": "http:hyelox"- }- }- },- "A_Sparc": {- "FreeBSD": {- "( < 2.2.4 && <= 3.2.2 && > 4.5.1 && >= 5.6.3 && < 5.2.5 ) || ( > 2.2.1 && > 3.2.3 && == 5.4.3 && >= 4.3.4 && <= 2.4.2 && <= 2.4.3 )": {- "dlCSize": -3,- "dlHash": "krke",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "N#"- },- "dlTag": [- "LatestPrerelease",- "base-6.1.3",- "LatestNightly",- "LatestNightly",- "Latest"- ],- "dlUri": "https:d"- },- "( < 4.1.5 && >= 3.3.6 && == 5.5.2 )": {- "dlCSize": null,- "dlHash": "dawi",- "dlOutput": "/\u0016",- "dlSubdir": "\u001fb^",- "dlTag": null,- "dlUri": "https:"- },- "( <= 4.4.3 && < 5.4.5 && <= 4.4.5 && < 5.2.5 && > 4.3.2 && == 2.5.1 )": {- "dlCSize": -3,- "dlHash": "ors",- "dlOutput": null,- "dlSubdir": {- "RegexDir": ""- },- "dlTag": null,- "dlUri": "https:jtf"- },- "( == 1.3.2 && <= 3.2.4 && > 5.2.2 )": {- "dlCSize": null,- "dlHash": "u",- "dlOutput": "l0",- "dlSubdir": "𩽺",- "dlTag": [- "Nightly",- "J>"- ],- "dlUri": "http:oufgls"- },- "unknown_versioning": {- "dlCSize": -7,- "dlHash": "hskzni",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "1J"- },- "dlTag": [- "LatestNightly",- "𩕈x",- "Prerelease"- ],- "dlUri": "https:gdqvkhu"- }- },- "Linux_Mint": {- "unknown_versioning": {- "dlCSize": 3,- "dlHash": "liy",- "dlOutput": null,- "dlSubdir": "",- "dlTag": [- "U",- "Latest",- "{꧍\u000e\u0014",- "base-2.5.2",- "LatestNightly",- "Experimental",- "Experimental"- ],- "dlUri": "https:e"- }- },- "Windows": {- "( <= 1.3.2 && < 2.4.1 && >= 3.4.1 && >= 3.2.4 && > 2.4.4 ) || ( == 1.3.3 && <= 1.2.2 && < 2.4.3 && > 1.3.3 && > 1.3.3 && > 1.1.2 )": {- "dlCSize": -5,- "dlHash": "t",- "dlOutput": ")>᧿\u001e",- "dlSubdir": {- "RegexDir": "{7\u000cFP"- },- "dlTag": [- "LatestNightly",- "\u000e",- "LatestNightly",- "Experimental"- ],- "dlUri": "https:vkhym"- },- "( <= 3.4.4 && == 3.1.4 && >= 1.1.4 && == 3.2.2 && > 5.3.3 )": {- "dlCSize": 0,- "dlHash": "ee",- "dlOutput": "",- "dlSubdir": null,- "dlTag": [- "Experimental",- "Experimental",- "Experimental",- "LatestNightly",- ";%\u0011𤢰",- "base-3.5.2",- "LatestPrerelease"- ],- "dlUri": "https:fzz"- },- "( == 1.5.2 && == 2.4.3 && >= 2.4.4 && == 1.4.3 && > 1.1.5 ) || ( >= 1.3.4 && == 2.1.3 && == 2.4.3 && == 4.2.4 && == 2.2.3 ) || ( == 3.1.2 )": {- "dlCSize": 1,- "dlHash": "tfsf",- "dlOutput": "\u0014{g}&D䳬",- "dlSubdir": {- "RegexDir": "ৈ"- },- "dlTag": [- "LatestPrerelease",- "Experimental",- "LatestNightly",- "Experimental",- "Nightly"- ],- "dlUri": "http:ss"- },- "( > 2.1.4 && < 5.4.3 && < 2.5.1 && <= 2.5.5 )": {- "dlCSize": null,- "dlHash": "y",- "dlOutput": "\u001f7\u0001>0L",- "dlSubdir": {- "RegexDir": "u4\u0000\u000c"- },- "dlTag": [- "Recommended",- "base-6.1.1"- ],- "dlUri": "https:vegtdum"- },- "( > 3.5.5 && < 4.1.2 && < 5.3.2 && >= 4.4.3 ) || ( >= 3.2.1 && == 4.3.4 && < 2.4.4 && == 2.2.2 )": {- "dlCSize": null,- "dlHash": "ew",- "dlOutput": null,- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "Nightly",- "Recommended",- "old",- "LatestNightly",- "Recommended",- "Experimental",- "Recommended"- ],- "dlUri": "http:aau"- },- "( >= 1.4.6 && >= 4.3.2 )": {- "dlCSize": 3,- "dlHash": "dk",- "dlOutput": "𣄹w",- "dlSubdir": "L\r署e\u0002",- "dlTag": [- "base-2.4.3",- "old",- "Recommended",- "Recommended",- "old"- ],- "dlUri": "http:arzg"- }- }- }- },- "viChangeLog": "http:dphbion",- "viPostInstall": "",- "viPostRemove": null,- "viPreCompile": "i",- "viPreInstall": null,- "viReleaseDay": "12597021425758112-08-30",- "viSourceDL": {- "dlCSize": null,- "dlHash": "ofc",- "dlOutput": "",- "dlSubdir": null,- "dlTag": [- "Nightly",- "Experimental",- "Recommended"- ],- "dlUri": "http:orv"- },- "viTags": [- "base-3.5.4",- "Recommended",- "-",- "base-1.3.6"- ],- "viTestDL": {- "dlCSize": 1,- "dlHash": "eg",- "dlOutput": "'T\u0008.",- "dlSubdir": {- "RegexDir": "h✾,."- },- "dlTag": [- "base-3.1.2",- "Nightly"- ],- "dlUri": "https:dh"- }- }- },- "HLS": {- "3.1.7": {- "viArch": {- "A_32": {},- "A_PowerPC": {- "Darwin": {- "( <= 3.5.3 && <= 3.2.5 && <= 2.2.2 && >= 4.5.2 && > 1.1.5 && > 2.3.1 )": {- "dlCSize": -1,- "dlHash": "zhdjfn",- "dlOutput": "Yq",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": null,- "dlUri": "https:u"- },- "( <= 4.2.3 )": {- "dlCSize": -6,- "dlHash": "awngwr",- "dlOutput": null,- "dlSubdir": {- "RegexDir": ""- },- "dlTag": null,- "dlUri": "https:ova"- },- "( > 3.2.5 && == 3.5.1 && >= 4.3.5 && < 4.4.3 )": {- "dlCSize": null,- "dlHash": "jier",- "dlOutput": "",- "dlSubdir": null,- "dlTag": [- "LatestPrerelease",- "base-4.3.8",- "\tK",- "Latest",- "Latest",- "Recommended"- ],- "dlUri": "https:div"- }- }- },- "A_PowerPC64": {- "Darwin": {},- "FreeBSD": {- "( <= 4.4.4 && == 1.5.5 && == 2.6.2 && < 5.1.5 ) || ( > 4.2.1 && < 4.1.4 && == 4.4.4 )": {- "dlCSize": -4,- "dlHash": "akhcqcq",- "dlOutput": "𰄔𱙲HT",- "dlSubdir": null,- "dlTag": [],- "dlUri": "http:xdefsuy"- },- "( > 2.2.5 ) || ( == 4.5.4 )": {- "dlCSize": -4,- "dlHash": "t",- "dlOutput": "",- "dlSubdir": {- "RegexDir": ",\u0003\"-"- },- "dlTag": null,- "dlUri": "http:fyp"- },- "( > 5.2.2 && <= 5.3.2 && == 5.2.4 && <= 4.1.3 && < 5.3.5 && > 4.1.5 )": {- "dlCSize": -7,- "dlHash": "e",- "dlOutput": "\u0008K",- "dlSubdir": "qF",- "dlTag": null,- "dlUri": "http:prypbz"- },- "( >= 2.1.4 ) || ( <= 4.1.3 && > 3.4.1 && >= 4.2.4 ) || ( <= 1.3.1 && == 3.2.1 && >= 2.1.2 && < 2.2.2 && == 1.2.1 )": {- "dlCSize": 4,- "dlHash": "py",- "dlOutput": "HzO",- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:"- },- "unknown_versioning": {- "dlCSize": 6,- "dlHash": "mww",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "\u0010\u000f\u0006"- },- "dlTag": [- "LatestNightly",- "old",- "Latest",- "LatestNightly",- "Recommended",- "\u001d[Wlo",- "Nightly"- ],- "dlUri": "http:w"- }- },- "Linux_CentOS": {- "( < 3.1.5 ) || ( < 3.3.4 && <= 2.1.2 && < 5.2.1 ) || ( <= 1.3.3 && > 1.3.4 )": {- "dlCSize": 6,- "dlHash": "yinvusc",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "r"- },- "dlTag": [- "Recommended",- "Prerelease",- "LatestNightly",- "old"- ],- "dlUri": "https:ewobz"- },- "( == 1.4.3 && <= 4.1.4 && == 5.5.1 && == 3.1.4 && < 3.5.5 && < 2.1.5 ) || ( > 3.3.3 && >= 4.2.4 && > 2.3.3 ) || ( > 1.3.2 && >= 2.2.2 )": {- "dlCSize": 3,- "dlHash": "iewtqz",- "dlOutput": null,- "dlSubdir": "",- "dlTag": [- "LatestPrerelease",- "Nightly",- "Experimental",- "Nightly",- "LatestNightly"- ],- "dlUri": "http:snbslu"- },- "( == 2.1.3 && < 2.3.4 ) || ( == 2.1.1 && == 2.2.1 && > 3.5.1 && <= 1.3.1 && < 4.1.2 ) || ( >= 2.3.2 && >= 2.4.2 && > 3.3.1 && >= 2.1.2 && == 2.2.4 )": {- "dlCSize": 1,- "dlHash": "zhslh",- "dlOutput": "jy\twR^%",- "dlSubdir": null,- "dlTag": [- "Nightly",- "Recommended"- ],- "dlUri": "http:hqao"- },- "( == 5.5.2 && >= 2.5.6 && > 5.4.3 && == 5.2.5 && > 4.1.3 && < 5.4.1 ) || ( <= 2.2.4 ) || ( >= 1.2.2 && >= 3.1.2 && >= 1.1.4 && < 3.1.3 && > 2.2.2 )": {- "dlCSize": -3,- "dlHash": "htshdwm",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "0 𥳟,R"- },- "dlTag": null,- "dlUri": "https:mq"- },- "( > 6.2.3 && == 2.2.4 && == 3.5.4 && < 1.5.5 && < 5.2.1 ) || ( == 4.1.4 && == 1.1.3 && < 1.4.2 ) || ( <= 2.2.1 && == 1.3.4 && <= 3.1.3 ) || ( <= 1.2.1 && > 1.2.1 ) || ( >= 2.1.1 && >= 2.1.1 && > 1.2.1 ) || ( < 1.1.1 && == 1.1.1 )": {- "dlCSize": 3,- "dlHash": "fnqh",- "dlOutput": "뢿(",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "Prerelease",- "LatestNightly",- "LatestPrerelease",- "Latest",- "LatestNightly"- ],- "dlUri": "https:"- },- "( >= 5.5.5 && == 5.1.4 && < 4.3.1 && == 4.3.2 && > 3.2.3 )": {- "dlCSize": 0,- "dlHash": "ye",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "𠛟𭧧𗰏A"- },- "dlTag": [- "old",- "\u0004",- "LatestNightly",- "base-3.4.5",- "Experimental",- "",- "Experimental"- ],- "dlUri": "https:nnt"- },- "unknown_versioning": {- "dlCSize": -1,- "dlHash": "a",- "dlOutput": null,- "dlSubdir": {- "RegexDir": ""- },- "dlTag": null,- "dlUri": "http:grvdw"- }- },- "Linux_Exherbo": {- "( >= 5.3.4 && <= 2.2.1 && <= 5.1.2 && <= 5.3.2 && >= 1.4.4 && > 4.4.2 && <= 1.1.5 )": {- "dlCSize": 4,- "dlHash": "nyyimps",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "7\u0018ᕢ𪕪"- },- "dlTag": [- "",- "LatestNightly",- "Experimental",- "old",- "Latest",- "Prerelease",- "Nightly"- ],- "dlUri": "http:xvnqiwk"- }- },- "Windows": {- "( > 2.1.5 && > 5.1.1 && >= 2.3.4 && <= 1.3.1 )": {- "dlCSize": -3,- "dlHash": "dzqbkd",- "dlOutput": "ಜZ\u0019",- "dlSubdir": {- "RegexDir": "/"- },- "dlTag": [- "Recommended",- "base-2.1.5",- "Experimental",- "Nightly",- "LatestPrerelease",- "LatestNightly"- ],- "dlUri": "http:k"- },- "( > 2.4.5 && > 5.3.4 && < 3.4.1 && >= 3.2.2 ) || ( == 4.1.4 && < 3.1.3 && >= 3.4.3 && >= 2.4.2 && == 4.3.1 && > 5.1.3 ) || ( <= 2.1.3 && == 1.1.1 && > 3.2.2 && > 3.1.3 )": {- "dlCSize": -4,- "dlHash": "io",- "dlOutput": "U𜾄T\u0003\u001f\u0016",- "dlSubdir": "laH.",- "dlTag": [],- "dlUri": "https:"- },- "( >= 1.2.4 && > 4.3.5 )": {- "dlCSize": -3,- "dlHash": "lwwvx",- "dlOutput": "푖4=䡏V\u0003",- "dlSubdir": "<",- "dlTag": [- "밃",- "Prerelease",- "Recommended"- ],- "dlUri": "http:j"- },- "( >= 2.5.1 && >= 4.2.3 && < 4.1.2 && <= 4.3.3 ) || ( <= 3.2.1 && < 4.4.4 && >= 2.1.2 && == 4.1.1 && <= 3.1.1 )": {- "dlCSize": null,- "dlHash": "yxq",- "dlOutput": "u\"\u0012A",- "dlSubdir": null,- "dlTag": [- "Experimental",- "Experimental"- ],- "dlUri": "http:wrkwn"- },- "unknown_versioning": {- "dlCSize": 6,- "dlHash": "",- "dlOutput": "\u001eU4E",- "dlSubdir": "\u0004磶}bi2",- "dlTag": [- "Recommended",- "33lU",- "Experimental",- "Recommended",- "Nightly",- "LatestNightly"- ],- "dlUri": "http:"- }- }- },- "A_Sparc64": {- "Darwin": {- "( < 6.5.2 && <= 2.1.2 && == 3.4.4 && > 3.2.2 && == 1.5.2 )": {- "dlCSize": 4,- "dlHash": "ahka",- "dlOutput": null,- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "LatestNightly",- "%\u0017",- "Prerelease",- "base-4.3.6",- "Prerelease"- ],- "dlUri": "https:ehmpkj"- },- "( <= 5.2.4 && <= 1.1.2 && <= 4.5.1 && >= 3.3.5 && > 1.1.1 && < 4.3.5 && < 4.5.4 ) || ( < 3.3.4 && <= 2.1.2 && <= 2.1.4 && <= 2.4.3 ) || ( > 1.2.3 ) || ( >= 2.1.2 )": {- "dlCSize": 4,- "dlHash": "zfgbr",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "8\u001e\u0015E",- "",- "Latest",- "Experimental"- ],- "dlUri": "https:sjdkg"- },- "( == 5.1.2 && == 1.2.6 && >= 1.4.2 && < 4.3.2 ) || ( >= 3.4.2 && <= 4.4.1 && <= 2.2.4 && <= 4.3.1 ) || ( > 1.3.1 && > 1.2.3 && >= 3.1.2 ) || ( == 2.2.2 )": {- "dlCSize": null,- "dlHash": "zaxhubp",- "dlOutput": "B𓍘",- "dlSubdir": "𱬤𠔵B",- "dlTag": [- "base-1.4.1",- "Prerelease",- "Prerelease",- "LatestNightly",- "Experimental",- "Nightly"- ],- "dlUri": "http:ynmn"- },- "( > 5.1.5 && > 1.3.4 ) || ( == 2.2.4 && > 2.2.1 && == 2.1.4 && >= 2.3.3 && == 4.2.4 && > 4.4.2 ) || ( >= 1.2.1 && > 2.3.3 && < 2.3.2 && >= 3.1.2 && <= 1.1.3 )": {- "dlCSize": null,- "dlHash": "",- "dlOutput": "h\u0010:\u0001\u0015뤘",- "dlSubdir": null,- "dlTag": [- "old"- ],- "dlUri": "http:owsu"- },- "( >= 1.1.2 && > 5.5.4 && > 5.2.1 ) || ( > 3.3.4 && == 3.4.4 && >= 5.1.4 && <= 4.2.2 && == 2.4.2 ) || ( < 1.1.3 && <= 3.2.2 && > 2.2.3 && <= 2.1.1 && <= 1.1.3 )": {- "dlCSize": null,- "dlHash": "kq",- "dlOutput": "%",- "dlSubdir": {- "RegexDir": "\u001f"- },- "dlTag": [- "base-3.1.4",- "Recommended",- "Recommended",- "Experimental",- "old",- "LatestPrerelease"- ],- "dlUri": "https:zpnhx"- },- "( >= 2.1.2 )": {- "dlCSize": null,- "dlHash": "mi",- "dlOutput": "",- "dlSubdir": "g;g\u0014#",- "dlTag": [- "old",- "old",- "base-6.2.6"- ],- "dlUri": "http:v"- },- "unknown_versioning": {- "dlCSize": -4,- "dlHash": "h",- "dlOutput": "lF᳢P&)",- "dlSubdir": null,- "dlTag": [- "6"- ],- "dlUri": "http:av"- }- },- "FreeBSD": {- "( <= 1.1.3 && == 4.5.2 && < 3.1.3 )": {- "dlCSize": null,- "dlHash": "wzv",- "dlOutput": "",- "dlSubdir": "1",- "dlTag": [- "J\u0014K\u0000p뭎",- "Experimental",- "Latest",- "Prerelease",- "LatestPrerelease"- ],- "dlUri": "http:iszerz"- },- "( <= 1.1.5 )": {- "dlCSize": -5,- "dlHash": "a",- "dlOutput": ";9",- "dlSubdir": "",- "dlTag": [- "Experimental",- "base-4.2.1",- "LatestPrerelease"- ],- "dlUri": "https:rylimrq"- },- "( == 2.4.2 && < 4.1.4 && == 3.3.2 && >= 5.2.3 && > 2.4.1 && <= 3.4.1 && >= 3.4.3 )": {- "dlCSize": 7,- "dlHash": "nfw",- "dlOutput": "2oMom",- "dlSubdir": {- "RegexDir": "\u0008𱔌v\u001f"- },- "dlTag": [- "Experimental",- "Recommended",- "\t=\u0004J",- "x\r𥊊qP",- "base-1.4.3",- "base-1.5.6",- "Latest"- ],- "dlUri": "http:umzwger"- },- "( > 1.4.2 && < 1.1.4 && > 2.2.5 ) || ( == 1.4.2 && > 4.2.1 && <= 2.1.2 && == 1.4.3 && == 3.4.1 && == 2.4.2 )": {- "dlCSize": 0,- "dlHash": "fwsge",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "LatestPrerelease",- "base-4.5.4",- "Nightly",- "Prerelease",- "LatestPrerelease",- "Experimental",- "base-5.7.3"- ],- "dlUri": "http:wmjzdk"- },- "( > 3.1.5 && < 5.5.4 && <= 2.4.5 && > 1.2.1 && >= 1.5.5 )": {- "dlCSize": null,- "dlHash": "vbba",- "dlOutput": null,- "dlSubdir": "4\u0017O",- "dlTag": [- "Experimental",- "LatestNightly",- "LatestNightly",- "Latest"- ],- "dlUri": "https:autp"- },- "( >= 1.3.4 && < 1.3.2 && == 2.4.4 && > 2.6.4 ) || ( == 4.3.1 && <= 3.2.2 && > 3.5.1 && < 2.1.3 && > 3.1.2 && < 4.2.3 )": {- "dlCSize": 1,- "dlHash": "g",- "dlOutput": "&",- "dlSubdir": {- "RegexDir": "\u000b\u0016𢆔A📸𠌬"- },- "dlTag": [],- "dlUri": "https:lrht"- },- "unknown_versioning": {- "dlCSize": 4,- "dlHash": "ztxtpc",- "dlOutput": "!,𤌊뢋\u000fE",- "dlSubdir": "^\u0017|x",- "dlTag": null,- "dlUri": "http:aofpq"- }- },- "Windows": {- "( == 2.2.4 )": {- "dlCSize": -1,- "dlHash": "qcq",- "dlOutput": "",- "dlSubdir": "NO\u000e",- "dlTag": [- "base-5.2.6",- "LatestNightly",- "Latest",- "LatestNightly"- ],- "dlUri": "https:rq"- },- "( > 2.5.5 && < 4.1.3 ) || ( > 4.1.2 && == 2.2.2 && >= 3.3.1 && >= 4.4.5 && <= 2.3.3 && == 2.4.2 ) || ( <= 2.2.2 ) || ( >= 1.2.1 )": {- "dlCSize": 0,- "dlHash": "xkk",- "dlOutput": "촫08\u0000",- "dlSubdir": null,- "dlTag": [- "Prerelease"- ],- "dlUri": "http:m"- },- "( >= 4.2.3 && == 2.6.4 && >= 3.1.2 && > 6.2.4 && < 2.2.3 && < 3.1.3 ) || ( < 4.3.1 && < 4.3.1 && >= 3.3.2 ) || ( < 3.2.2 && == 1.3.3 && >= 2.1.3 && >= 1.1.2 && == 3.2.1 ) || ( < 1.2.1 && > 1.1.1 && < 3.1.1 ) || ( <= 1.1.2 ) || ( > 1.2.2 ) || ( <= 1.1.1 )": {- "dlCSize": 7,- "dlHash": "k",- "dlOutput": null,- "dlSubdir": ";",- "dlTag": [- "Prerelease",- "R",- "LatestPrerelease",- "Recommended",- "LatestPrerelease",- "Nightly",- "Latest"- ],- "dlUri": "https:ss"- },- "unknown_versioning": {- "dlCSize": -4,- "dlHash": "ntjck",- "dlOutput": "b9",- "dlSubdir": {- "RegexDir": "b\u0012年"- },- "dlTag": null,- "dlUri": "http:huf"- }- }- }- },- "viChangeLog": "http:gsmbxe",- "viPostInstall": "sstfo",- "viPostRemove": "whb",- "viPreCompile": null,- "viPreInstall": null,- "viReleaseDay": "-18208623040331981-12-07",- "viSourceDL": null,- "viTags": [- "Nightly"- ],- "viTestDL": {- "dlCSize": 1,- "dlHash": "pyamm",- "dlOutput": "rz\u0005",- "dlSubdir": "\u000eO",- "dlTag": [- "Latest"- ],- "dlUri": "https:oxrzu"- }- },- "8.8.2": {- "viArch": {- "A_ARM64": {- "Darwin": {- "( <= 1.3.4 && <= 4.1.2 && >= 3.1.1 && < 5.5.1 && >= 4.1.5 && > 3.4.2 ) || ( < 3.1.4 && >= 2.4.2 ) || ( >= 2.2.1 && > 1.3.2 && > 1.3.2 && > 2.1.3 && > 1.1.3 )": {- "dlCSize": 6,- "dlHash": "yvjs",- "dlOutput": "x\u0006tiI",- "dlSubdir": "",- "dlTag": [- "LatestPrerelease",- "Nightly",- "Experimental",- "old",- "Recommended"- ],- "dlUri": "http:kdofah"- },- "( == 1.5.1 && <= 2.1.5 && == 1.5.4 ) || ( < 3.1.3 )": {- "dlCSize": null,- "dlHash": "fi",- "dlOutput": "8f:N`𗧼𬻘",- "dlSubdir": {- "RegexDir": "𰇹"- },- "dlTag": [- "Prerelease",- "Prerelease",- "Nightly",- "Nightly"- ],- "dlUri": "http:j"- }- },- "FreeBSD": {},- "Linux_Fedora": {- "( < 1.3.6 ) || ( > 5.3.1 && < 3.1.4 && > 3.4.1 )": {- "dlCSize": null,- "dlHash": "kqg",- "dlOutput": "iN\u0011\t",- "dlSubdir": null,- "dlTag": [- "Latest",- "Prerelease",- "LatestPrerelease",- "Prerelease"- ],- "dlUri": "https:wfujvlt"- },- "( < 3.3.5 && < 3.1.3 && == 3.3.2 && < 2.1.4 && <= 5.5.5 && < 1.1.4 && <= 4.5.5 )": {- "dlCSize": null,- "dlHash": "xncfr",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "䮋N"- },- "dlTag": [- "LatestPrerelease",- "Latest",- "LatestNightly"- ],- "dlUri": "https:"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "d",- "dlOutput": "4&u\nX(",- "dlSubdir": null,- "dlTag": [- "Latest"- ],- "dlUri": "https:hv"- }- },- "Linux_Mint": {- "( <= 2.4.5 ) || ( >= 2.2.4 && == 1.4.1 ) || ( == 3.1.1 && == 3.4.2 ) || ( == 1.2.2 && == 1.2.1 && < 2.2.2 ) || ( < 1.1.1 && >= 1.1.1 && > 1.1.1 ) || ( == 1.1.1 && < 1.1.1 )": {- "dlCSize": 1,- "dlHash": "z",- "dlOutput": null,- "dlSubdir": "M0",- "dlTag": [- "old",- "LatestNightly",- "Recommended",- "Prerelease",- "Nightly"- ],- "dlUri": "http:on"- },- "( <= 4.3.4 && >= 4.2.6 && >= 5.3.4 && >= 1.3.2 && > 1.3.3 ) || ( > 4.3.2 && >= 1.2.1 && == 1.4.4 ) || ( <= 2.2.2 && == 1.1.4 && <= 1.1.2 && == 1.1.2 ) || ( > 2.2.3 && <= 2.2.2 && <= 1.2.2 && > 2.1.2 )": {- "dlCSize": null,- "dlHash": "lrfmlyn",- "dlOutput": "V",- "dlSubdir": "qo",- "dlTag": [- "Experimental"- ],- "dlUri": "https:wm"- },- "( == 2.2.2 && == 5.6.5 ) || ( == 4.1.1 && >= 4.4.1 && > 1.1.4 && < 4.1.2 && > 1.2.1 ) || ( > 2.2.3 && == 1.4.3 && == 2.1.1 )": {- "dlCSize": 4,- "dlHash": "ya",- "dlOutput": null,- "dlSubdir": "&J",- "dlTag": null,- "dlUri": "https:pfxrjwp"- },- "( == 3.1.3 && > 2.1.1 ) || ( == 1.1.3 ) || ( < 2.1.3 && == 2.3.2 && < 1.1.2 && >= 2.3.2 ) || ( == 2.1.2 && == 2.2.1 && > 2.1.2 )": {- "dlCSize": 0,- "dlHash": "lodktw",- "dlOutput": "I\u000c",- "dlSubdir": {- "RegexDir": "-Xu𠒛𰺦"- },- "dlTag": [- "9j)"- ],- "dlUri": "https:huezccd"- },- "( >= 3.6.1 && <= 1.2.2 && <= 2.5.2 && > 1.1.1 && <= 2.1.5 && >= 5.2.2 && >= 5.3.6 ) || ( <= 1.2.1 && >= 1.4.4 && == 5.4.2 && >= 4.1.2 && <= 3.5.1 )": {- "dlCSize": -5,- "dlHash": "cf",- "dlOutput": "j\u0010]",- "dlSubdir": {- "RegexDir": "𮈧\u0000\tqr{"- },- "dlTag": [- "Recommended",- "old",- "old",- "base-2.5.6",- "LatestPrerelease"- ],- "dlUri": "http:hj"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "jsnjdu",- "dlOutput": "W\u000e'⟌䰯",- "dlSubdir": "v低\u000f",- "dlTag": null,- "dlUri": "https:mu"- }- },- "Windows": {- "( < 3.1.6 && == 1.3.3 && > 4.5.2 && <= 2.2.3 && > 1.1.4 )": {- "dlCSize": -1,- "dlHash": "ttwbj",- "dlOutput": "\u0010\u0004\u0010R_",- "dlSubdir": "uL",- "dlTag": [- "LatestNightly",- "Recommended",- "LatestPrerelease"- ],- "dlUri": "http:vnwda"- },- "( <= 3.2.4 && >= 3.3.3 && >= 2.5.1 && == 1.2.3 && <= 3.1.1 && == 5.3.2 && <= 1.5.3 )": {- "dlCSize": 2,- "dlHash": "",- "dlOutput": "e",- "dlSubdir": null,- "dlTag": [- "}骿o "- ],- "dlUri": "http:ncknwu"- },- "( <= 3.5.2 && < 3.1.1 && <= 4.1.3 ) || ( < 3.1.4 && < 4.2.1 && == 2.4.1 && < 2.3.3 )": {- "dlCSize": -4,- "dlHash": "pemfz",- "dlOutput": "TvyG",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "LatestNightly"- ],- "dlUri": "https:zazrt"- },- "( <= 3.6.1 && > 3.1.5 && <= 5.2.5 && <= 3.1.1 && >= 2.4.3 )": {- "dlCSize": 4,- "dlHash": "vehnciz",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "|𬝢"- },- "dlTag": null,- "dlUri": "http:"- },- "( > 1.3.2 && < 3.5.2 && == 1.2.6 && == 5.2.2 && <= 5.4.1 && == 3.2.3 )": {- "dlCSize": 6,- "dlHash": "tcqs",- "dlOutput": "",- "dlSubdir": {- "RegexDir": " "- },- "dlTag": null,- "dlUri": "http:fpabg"- },- "( > 1.4.3 && == 1.4.2 && <= 1.2.5 && >= 3.4.4 ) || ( > 1.1.2 && < 3.2.3 && == 2.2.2 && == 2.5.2 && >= 2.4.3 ) || ( > 1.3.1 && > 3.2.2 && >= 1.2.1 && > 3.1.2 ) || ( > 3.1.3 && <= 2.2.3 && <= 3.1.4 ) || ( >= 1.2.1 && < 1.1.1 )": {- "dlCSize": null,- "dlHash": "z",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "~v\u0000q"- },- "dlTag": [- "Prerelease",- "Experimental"- ],- "dlUri": "http:jzdjr"- },- "unknown_versioning": {- "dlCSize": 0,- "dlHash": "",- "dlOutput": "\u001dE%",- "dlSubdir": null,- "dlTag": [- "old",- "old",- "LatestNightly"- ],- "dlUri": "http:i"- }- }- },- "A_PowerPC64": {- "FreeBSD": {- "( < 1.5.1 && == 1.5.2 && <= 3.2.1 ) || ( >= 2.1.4 && >= 3.3.1 && <= 2.4.3 && < 4.2.4 && <= 4.4.1 )": {- "dlCSize": 1,- "dlHash": "v",- "dlOutput": null,- "dlSubdir": "",- "dlTag": [- "old",- "Nightly",- "Prerelease"- ],- "dlUri": "https:hvxbbv"- },- "( < 4.3.4 && > 5.5.5 && < 2.3.3 )": {- "dlCSize": -7,- "dlHash": "kkkrzee",- "dlOutput": "\u001e8浗_",- "dlSubdir": {- "RegexDir": "^"- },- "dlTag": [- "Nightly",- "Nightly",- "Nightly",- "base-4.6.1",- "𐳟M5",- "Recommended"- ],- "dlUri": "https:"- },- "( == 5.3.1 && == 3.6.2 && > 4.2.4 && == 2.3.2 && > 2.3.5 && >= 3.4.1 )": {- "dlCSize": null,- "dlHash": "hxi",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "\u000e;\u001b"- },- "dlTag": [- "Experimental",- "old",- "\n?a\u0007",- "Nightly",- "Prerelease"- ],- "dlUri": "http:xt"- },- "unknown_versioning": {- "dlCSize": 6,- "dlHash": "ilho",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "𭺋\u001aq^"- },- "dlTag": [- "Recommended"- ],- "dlUri": "http:"- }- },- "Windows": {- "( < 3.1.1 && <= 1.2.5 && == 4.1.4 )": {- "dlCSize": 0,- "dlHash": "vtv",- "dlOutput": "袆𣕃MK",- "dlSubdir": {- "RegexDir": "\u0011.~"- },- "dlTag": [- "Latest",- "Latest",- "Recommended",- "old"- ],- "dlUri": "https:"- },- "unknown_versioning": {- "dlCSize": -6,- "dlHash": "",- "dlOutput": "?",- "dlSubdir": {- "RegexDir": "xa"- },- "dlTag": [],- "dlUri": "https:a"- }- }- },- "A_Sparc": {- "Darwin": {},- "FreeBSD": {- "( < 4.5.3 && >= 5.4.4 && < 4.3.1 && >= 5.4.5 ) || ( == 4.2.1 )": {- "dlCSize": -1,- "dlHash": "s",- "dlOutput": "\u001b",- "dlSubdir": null,- "dlTag": [],- "dlUri": "http:wvvrvpr"- },- "( <= 5.3.3 && <= 5.1.1 ) || ( < 4.1.1 && < 1.3.2 && < 3.2.4 && > 3.1.1 ) || ( > 1.1.2 ) || ( <= 2.1.2 && >= 2.1.2 && <= 2.2.1 && > 2.1.1 )": {- "dlCSize": null,- "dlHash": "fpx",- "dlOutput": "#'Ca$",- "dlSubdir": {- "RegexDir": "𩔄頍n"- },- "dlTag": null,- "dlUri": "https:nefbwq"- },- "( > 1.3.2 && >= 4.2.4 && >= 3.7.4 ) || ( <= 1.2.1 && > 1.3.4 ) || ( > 3.3.3 && == 3.3.3 && < 3.1.3 && <= 1.1.2 ) || ( <= 1.1.2 && <= 1.2.1 && > 2.2.1 ) || ( <= 1.1.1 && > 1.1.1 )": {- "dlCSize": 5,- "dlHash": "vzrgm",- "dlOutput": null,- "dlSubdir": "첌r\u0000^-c",- "dlTag": [- "base-1.3.3",- "old"- ],- "dlUri": "https:et"- },- "( > 2.3.3 ) || ( == 4.1.2 && >= 1.3.2 && < 4.5.4 && >= 1.3.2 ) || ( <= 1.2.1 )": {- "dlCSize": null,- "dlHash": "qmv",- "dlOutput": "",- "dlSubdir": "0Ev6𫥾\u0001",- "dlTag": null,- "dlUri": "http:d"- },- "unknown_versioning": {- "dlCSize": 7,- "dlHash": "kn",- "dlOutput": "8𡔇U腇",- "dlSubdir": null,- "dlTag": [- "Experimental",- "LatestPrerelease",- "Latest",- "R)o",- "Latest",- "base-5.2.5",- "LatestNightly"- ],- "dlUri": "http:e"- }- },- "Linux_Fedora": {- "( < 2.1.2 && >= 3.1.3 && <= 3.1.3 && <= 1.5.2 && > 1.1.3 && > 3.5.4 && <= 1.4.2 ) || ( < 3.3.4 && == 3.2.1 && < 1.3.3 && == 4.1.1 && == 2.3.3 && < 4.2.3 )": {- "dlCSize": -6,- "dlHash": "lygdwuc",- "dlOutput": "vg",- "dlSubdir": "LuGXy\u001e",- "dlTag": [- "Latest",- "old",- "",- "base-3.2.2",- "",- "Latest",- "LatestPrerelease"- ],- "dlUri": "https:gb"- },- "( < 4.5.5 && > 1.1.5 && == 1.3.4 ) || ( < 1.4.3 && > 3.1.4 ) || ( <= 3.3.3 && == 3.1.2 && >= 3.3.1 && == 4.3.1 ) || ( > 2.2.2 && >= 2.1.2 && == 2.3.2 && <= 2.4.2 )": {- "dlCSize": -2,- "dlHash": "uux",- "dlOutput": "\u001c\u001e.f\u001c ",- "dlSubdir": null,- "dlTag": [- "Nightly",- "Prerelease"- ],- "dlUri": "https:jwhdibc"- },- "( >= 4.2.3 && <= 1.4.5 && <= 6.3.2 && < 2.5.1 )": {- "dlCSize": -5,- "dlHash": "acehtzn",- "dlOutput": "R\u0010\t",- "dlSubdir": "8",- "dlTag": [- "Recommended",- "Nightly",- "Nightly",- "_2䨊;",- "base-5.5.1"- ],- "dlUri": "http:zgmu"- },- "unknown_versioning": {- "dlCSize": -7,- "dlHash": "sjkk",- "dlOutput": "m-!",- "dlSubdir": "k5\u001eD",- "dlTag": null,- "dlUri": "http:kvel"- }- },- "Windows": {- "( < 5.3.5 && == 3.3.3 )": {- "dlCSize": 7,- "dlHash": "v",- "dlOutput": "I/𠖳E?\u000f",- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:sivsqd"- },- "( <= 4.4.2 && == 5.1.5 && > 2.1.1 && == 4.3.6 && >= 5.5.1 && > 3.4.6 && >= 4.4.3 )": {- "dlCSize": -1,- "dlHash": "frjga",- "dlOutput": ":@𖪗\u000b)Me",- "dlSubdir": "%\u0016w",- "dlTag": null,- "dlUri": "https:dqdgwn"- },- "( > 5.2.5 && == 2.1.1 && <= 5.3.4 && == 4.3.4 )": {- "dlCSize": 1,- "dlHash": "fi",- "dlOutput": "5\u001e\u0019𩷼]\u001d",- "dlSubdir": {- "RegexDir": "T\u001f"- },- "dlTag": [- "Recommended"- ],- "dlUri": "https:"- },- "unknown_versioning": {- "dlCSize": 2,- "dlHash": "hhtx",- "dlOutput": "pK",- "dlSubdir": "\u0012\u0004Lj",- "dlTag": [- "Latest",- "old",- "old",- "LatestNightly",- "Prerelease",- "Experimental"- ],- "dlUri": "http:ytfdiib"- }- }- },- "A_Sparc64": {}- },- "viChangeLog": "http:ncxm",- "viPostInstall": null,- "viPostRemove": null,- "viPreCompile": null,- "viPreInstall": "fnek",- "viReleaseDay": "-256391684329685-10-18",- "viSourceDL": {- "dlCSize": -1,- "dlHash": "qhl",- "dlOutput": "8*I\u0008",- "dlSubdir": "",- "dlTag": null,- "dlUri": "http:dvex"- },- "viTags": [- "Experimental",- "Prerelease",- "Experimental",- "LatestPrerelease"- ],- "viTestDL": null- }- },- "Stack": {- "4.8.7": {- "viArch": {- "A_64": {- "FreeBSD": {- "( < 2.1.1 && == 5.1.1 && <= 5.1.3 && >= 4.2.4 ) || ( <= 4.2.3 && >= 1.1.3 && <= 4.5.2 && > 2.2.4 ) || ( <= 3.3.1 && == 3.1.1 ) || ( == 1.2.2 && >= 1.2.1 && > 2.2.1 ) || ( < 2.1.1 && < 1.1.1 && <= 2.1.1 )": {- "dlCSize": -7,- "dlHash": "",- "dlOutput": "\u0008𩎹",- "dlSubdir": "",- "dlTag": [- "LatestPrerelease",- "Recommended",- "LatestNightly",- "2Go蘱x",- "old",- "LatestNightly"- ],- "dlUri": "https:dco"- },- "( < 3.1.4 && == 3.2.3 && <= 3.2.1 )": {- "dlCSize": -3,- "dlHash": "hi",- "dlOutput": "o<F",- "dlSubdir": "\u0007",- "dlTag": [- "Prerelease",- "old",- "old"- ],- "dlUri": "https:"- },- "( == 4.3.3 )": {- "dlCSize": 7,- "dlHash": "",- "dlOutput": ">nb",- "dlSubdir": "J` ",- "dlTag": null,- "dlUri": "https:sl"- },- "( == 5.4.3 && >= 3.1.4 && > 2.5.5 && > 5.4.3 && < 1.1.6 && <= 2.1.2 )": {- "dlCSize": 7,- "dlHash": "xyue",- "dlOutput": "NM",- "dlSubdir": "",- "dlTag": [- "LatestNightly",- "old",- ">=",- "old"- ],- "dlUri": "https:xsk"- },- "( >= 2.3.2 && <= 2.4.5 && > 1.2.2 ) || ( <= 1.3.1 && >= 1.1.2 && < 3.4.2 ) || ( > 3.2.3 && >= 1.1.2 && > 1.2.1 && < 1.2.2 && == 1.1.1 )": {- "dlCSize": 7,- "dlHash": "b",- "dlOutput": "\u0017\u0019I/ik",- "dlSubdir": null,- "dlTag": [- "GmUfb\t"- ],- "dlUri": "https:trdkp"- }- },- "Windows": {- "( <= 3.3.4 && > 1.4.3 && <= 1.2.3 ) || ( <= 1.3.4 && > 4.2.2 && == 5.1.2 )": {- "dlCSize": 7,- "dlHash": "mvfgogy",- "dlOutput": "mL{\u001dxk",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": null,- "dlUri": "http:uzlhc"- },- "( == 3.5.3 && > 4.2.1 )": {- "dlCSize": 3,- "dlHash": "xhokt",- "dlOutput": null,- "dlSubdir": "",- "dlTag": null,- "dlUri": "https:d"- },- "( > 4.3.3 ) || ( < 1.3.3 && >= 2.1.2 && == 4.3.1 ) || ( < 3.2.2 && <= 3.3.2 && > 2.2.2 && > 3.3.1 && <= 2.1.1 ) || ( <= 1.1.1 && <= 2.1.2 )": {- "dlCSize": null,- "dlHash": "xh",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "cfIo\u000f"- },- "dlTag": null,- "dlUri": "https:adicxvs"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "gnlsysv",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "R="- },- "dlTag": [- "LatestNightly",- "",- "Latest",- "Latest"- ],- "dlUri": "https:mqxqk"- }- }- },- "A_PowerPC": {},- "A_Sparc64": {- "FreeBSD": {- "( >= 3.1.3 && <= 5.4.2 && > 3.5.2 && == 3.5.4 && >= 3.5.1 && < 4.5.5 )": {- "dlCSize": -1,- "dlHash": "rmvtvo",- "dlOutput": "gB",- "dlSubdir": "{\u001bgꏵ𖪡",- "dlTag": [- "Recommended",- "LatestNightly"- ],- "dlUri": "http:lz"- }- },- "Linux_Ubuntu": {- "( <= 2.5.5 && < 1.5.2 && <= 3.4.5 ) || ( >= 1.1.3 && > 2.3.4 )": {- "dlCSize": -2,- "dlHash": "",- "dlOutput": null,- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "LatestNightly",- "",- "LatestPrerelease",- "LatestPrerelease",- "Prerelease",- "Experimental"- ],- "dlUri": "https:"- },- "( <= 3.1.4 && <= 3.2.5 && < 2.4.2 && < 3.1.4 && <= 4.1.3 && <= 4.2.2 && < 4.3.2 )": {- "dlCSize": null,- "dlHash": "qgob",- "dlOutput": "<",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "Nightly",- "base-4.1.4",- "base-6.1.6",- "LatestNightly",- "Nightly"- ],- "dlUri": "http:jxondo"- },- "( > 1.2.4 && >= 2.1.5 && < 5.3.5 && > 3.1.3 && == 1.2.5 ) || ( <= 2.4.2 && == 2.3.3 && == 1.4.4 ) || ( <= 2.3.3 )": {- "dlCSize": null,- "dlHash": "f",- "dlOutput": ";",- "dlSubdir": null,- "dlTag": [- "Latest",- "Experimental"- ],- "dlUri": "http:feny"- },- "( >= 3.1.1 ) || ( < 1.2.3 && <= 3.1.3 && > 4.2.1 )": {- "dlCSize": 4,- "dlHash": "",- "dlOutput": "",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "base-3.4.5",- "Nightly",- "LatestPrerelease",- "\u0002𥚖\u0017K\u000f",- "Recommended",- "Nightly"- ],- "dlUri": "http:snq"- },- "unknown_versioning": {- "dlCSize": 1,- "dlHash": "vykjb",- "dlOutput": ">\u001e^",- "dlSubdir": ";XY🡨&e",- "dlTag": [- "*SCF",- "Recommended",- "old",- "Prerelease",- "Prerelease"- ],- "dlUri": "https:lyeaxz"- }- },- "Windows": {- "( <= 4.5.3 && < 1.1.2 )": {- "dlCSize": 4,- "dlHash": "vcr",- "dlOutput": ",",- "dlSubdir": null,- "dlTag": [- "LatestNightly",- "base-4.4.6",- "LatestPrerelease"- ],- "dlUri": "http:"- },- "( == 3.2.2 && == 5.6.4 && == 2.4.3 && > 4.3.3 )": {- "dlCSize": 3,- "dlHash": "npidtg",- "dlOutput": "%@𰝉\u0004",- "dlSubdir": "Iu{",- "dlTag": null,- "dlUri": "http:bn"- },- "( == 5.5.4 && < 5.3.2 && >= 5.1.4 && < 3.1.5 && <= 4.1.4 && < 5.2.2 ) || ( < 3.3.3 && < 2.2.3 && > 4.2.4 )": {- "dlCSize": -6,- "dlHash": "sjzhx",- "dlOutput": "IP\u001cL\to",- "dlSubdir": null,- "dlTag": [- "Recommended",- "Latest",- "",- "Latest",- "Prerelease",- "Nightly"- ],- "dlUri": "http:qfpky"- },- "( > 5.1.1 && < 4.5.5 && < 2.4.3 && < 4.5.1 && <= 5.1.4 )": {- "dlCSize": 5,- "dlHash": "ojqxv",- "dlOutput": "XC\u0011",- "dlSubdir": "M",- "dlTag": [- "",- "Prerelease",- "LatestPrerelease",- "LatestNightly",- "Latest",- "Recommended",- "Latest"- ],- "dlUri": "http:"- },- "( >= 1.2.1 && == 1.2.4 && <= 4.3.4 && == 5.4.5 ) || ( <= 1.2.3 && >= 3.3.4 && >= 1.1.2 && > 2.4.4 && < 1.5.1 && == 1.4.3 )": {- "dlCSize": -2,- "dlHash": "ud",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "LvlM~"- },- "dlTag": null,- "dlUri": "http:"- }- }- }- },- "viChangeLog": "http:j",- "viPostInstall": null,- "viPostRemove": "bbuy",- "viPreCompile": "qowsq",- "viPreInstall": "mdxec",- "viReleaseDay": "-6647971724430441-01-30",- "viSourceDL": {- "dlCSize": 2,- "dlHash": "hq",- "dlOutput": "𡳼",- "dlSubdir": {- "RegexDir": "d==)"- },- "dlTag": [- "Recommended",- "Prerelease",- "LatestPrerelease",- "base-1.4.4",- "Recommended"- ],- "dlUri": "https:hs"- },- "viTags": [- "\t",- "Recommended",- "Prerelease",- "Latest",- "LatestPrerelease",- "old"- ],- "viTestDL": null- }- }- },- "metadataUpdate": "http:zvc",- "toolRequirements": {- "GHC": {- "3.4.5": {- "Darwin": {- "( < 3.4.1 && == 4.5.5 && <= 1.5.3 && < 3.1.1 && == 2.2.3 )": {- "distroPKGs": [],- "notes": "ph"- },- "unknown_versioning": {- "distroPKGs": [- "",- "pdfux",- "zvff",- ""- ],- "notes": "gwi"- }- },- "FreeBSD": {- "( == 3.4.2 && > 1.5.5 ) || ( >= 3.4.2 && < 4.4.4 && == 4.3.3 )": {- "distroPKGs": [- "caqunn",- "cczphyq",- "",- "aedkf",- "ndbfp"- ],- "notes": "qtru"- },- "( > 3.2.1 && == 5.4.5 && > 1.5.5 && <= 1.3.3 && >= 1.6.3 && < 1.3.5 )": {- "distroPKGs": [- ""- ],- "notes": "fntfd"- },- "( >= 2.1.2 && >= 1.1.5 && < 4.2.5 && > 2.3.2 && > 2.1.3 && > 2.5.2 && <= 5.3.1 ) || ( < 2.3.2 && <= 4.3.4 && == 1.4.4 && >= 4.4.3 && <= 4.4.2 && > 4.2.1 ) || ( == 1.4.3 && >= 1.4.3 && == 3.3.3 && > 3.1.3 && > 2.4.3 )": {- "distroPKGs": [],- "notes": "jiu"- },- "( >= 3.5.3 && < 4.3.4 && <= 3.5.2 && > 3.4.1 && <= 5.1.1 )": {- "distroPKGs": [- "jcpyqj",- "ryytz",- "q",- "",- "pcdb",- "ev",- "gzt"- ],- "notes": "iom"- }- },- "Windows": {- "( == 1.2.2 && < 4.4.2 && >= 1.1.3 && >= 5.2.5 ) || ( >= 3.2.3 && >= 2.1.1 && <= 1.4.2 && <= 2.2.1 ) || ( <= 1.4.3 && > 3.2.2 && == 1.1.2 ) || ( >= 3.1.1 && < 1.2.1 && > 2.1.2 && == 2.2.1 ) || ( < 1.1.2 && >= 1.1.1 ) || ( >= 1.1.3 && == 1.1.1 )": {- "distroPKGs": [- "gmsu"- ],- "notes": "ktadh"- },- "unknown_versioning": {- "distroPKGs": [- "dhbn",- "nbin"- ],- "notes": "f"- }- }- },- "5.4.8": {- "Darwin": {- "( < 3.2.5 && >= 2.5.1 && <= 1.5.1 )": {- "distroPKGs": [],- "notes": "znb"- },- "( < 3.5.4 && > 1.3.4 && >= 4.4.1 ) || ( > 1.4.2 && < 3.1.4 && >= 4.1.4 ) || ( <= 3.2.1 ) || ( < 2.2.2 )": {- "distroPKGs": [- "macgaby"- ],- "notes": "ksdjzd"- },- "( <= 2.1.4 && <= 1.3.4 && > 1.1.4 && < 1.2.2 )": {- "distroPKGs": [- "on",- "iqjsmbg",- "brdrruo",- "ei"- ],- "notes": "scvt"- },- "( <= 2.2.6 )": {- "distroPKGs": [- "",- "fp",- "ldhib",- "",- "kpr"- ],- "notes": "gk"- },- "( <= 3.3.5 && < 4.4.6 && < 4.3.5 && > 5.1.4 ) || ( == 4.1.2 && <= 2.3.3 && >= 4.1.3 )": {- "distroPKGs": [],- "notes": "hmub"- },- "( > 1.4.1 && > 3.3.2 && <= 3.2.5 && >= 2.1.5 && <= 2.3.1 && == 2.6.3 && < 5.1.1 )": {- "distroPKGs": [- "lhvam",- "teunnf",- "d",- "af",- "h",- "frpv",- "qowqj"- ],- "notes": "iynkj"- },- "( >= 1.2.3 && > 4.3.3 && < 3.2.2 && <= 1.5.2 && <= 4.2.3 && < 5.5.3 && == 4.1.4 )": {- "distroPKGs": [- "lcn",- "mbhytud",- "xpqjr",- "",- "llh",- "dw"- ],- "notes": "rxusfr"- },- "unknown_versioning": {- "distroPKGs": [- "rpmx"- ],- "notes": "lwserqs"- }- },- "Windows": {}- }- },- "Stack": {- "1.8.6": {- "Darwin": {- "( < 3.4.1 && <= 5.5.3 && <= 2.4.5 && >= 2.2.1 )": {- "distroPKGs": [- "fsj",- "y",- "whhz",- "gxblcii",- "shsbjfx"- ],- "notes": "htuc"- },- "( <= 1.3.4 && == 4.3.5 && <= 2.3.5 && > 1.5.3 && > 4.4.4 ) || ( >= 1.2.1 && >= 4.3.4 && == 1.3.1 && <= 3.2.4 && < 3.1.1 )": {- "distroPKGs": [- "",- "ty",- "h",- "",- "bkio"- ],- "notes": "kgr"- },- "( <= 3.3.1 && < 2.2.4 && <= 2.1.1 && == 3.1.1 && > 5.1.5 ) || ( > 4.3.3 && < 3.4.2 && == 4.1.1 && <= 3.1.2 )": {- "distroPKGs": [- "faeoz",- "qzzhkf",- "i",- "ex",- "mawj"- ],- "notes": "bcj"- },- "( <= 5.1.5 && == 4.4.4 ) || ( < 4.2.1 && >= 3.3.1 && >= 1.2.1 && < 1.1.3 && > 3.2.3 && <= 1.1.1 ) || ( == 1.3.2 && <= 1.1.3 && < 3.1.3 && > 1.3.2 ) || ( >= 2.1.1 )": {- "distroPKGs": [- "bj",- "tgzoh",- "ilmasub",- "u",- "tyxyhfs"- ],- "notes": "ohulzi"- },- "( == 1.4.4 && > 2.4.4 )": {- "distroPKGs": [- "xbgef",- "nxxdq",- "twlxgj",- "vv",- "cgain",- "ethdy",- "vuzt"- ],- "notes": "vqr"- },- "( > 2.1.3 && < 4.1.3 && <= 2.4.1 )": {- "distroPKGs": [- "upvuff",- "u",- "nsmeerw"- ],- "notes": ""- },- "unknown_versioning": {- "distroPKGs": [- "",- "m",- "zocyf",- "",- "si",- ""- ],- "notes": "wsa"- }- },- "FreeBSD": {- "( < 4.1.5 && <= 3.4.4 && <= 4.4.4 && > 2.4.5 ) || ( >= 3.1.3 && <= 2.3.3 && == 2.2.1 && == 2.2.4 )": {- "distroPKGs": [- "bvchlp",- "i",- "",- "csooqg",- "yslp"- ],- "notes": "dd"- },- "( < 4.5.4 && < 1.1.3 && >= 5.4.5 && > 3.1.2 && > 2.5.3 && >= 2.4.1 && < 2.5.1 )": {- "distroPKGs": [- "ppqqris",- "qrhr"- ],- "notes": ""- },- "( > 1.3.1 && == 2.5.3 && > 4.1.5 && >= 5.2.2 ) || ( > 3.1.3 && > 1.1.4 && < 4.5.2 && < 2.2.3 && < 3.1.2 && > 3.2.3 ) || ( <= 1.1.2 && < 3.1.1 && == 2.1.3 ) || ( > 1.1.2 && < 1.2.1 && >= 2.2.2 ) || ( >= 1.1.1 ) || ( >= 1.2.1 )": {- "distroPKGs": [- "vmperqw",- "lztlsbd"- ],- "notes": "rgglhhw"- },- "( > 2.2.1 && >= 3.4.3 && > 3.5.4 && > 4.2.2 ) || ( < 3.2.2 && < 3.2.2 && == 3.2.4 && <= 2.2.1 )": {- "distroPKGs": [- "jio",- "cot",- "bd"- ],- "notes": "chcg"- },- "( > 5.1.3 && >= 2.2.1 && == 6.2.5 && <= 4.3.2 && < 5.4.2 ) || ( > 1.4.4 ) || ( == 2.3.1 && > 2.2.1 && >= 2.3.3 && == 1.1.2 && >= 2.1.2 )": {- "distroPKGs": [- "zgvmkz",- "dqzh",- "maiheoh"- ],- "notes": ""- },- "unknown_versioning": {- "distroPKGs": [- "swc",- "nvhy",- "xkwrm",- "fgkbg",- "sed",- "xlgkeu"- ],- "notes": "dz"- }- },- "Linux_UnknownLinux": {- "( < 3.5.5 && <= 2.3.2 && < 4.1.2 && < 5.5.3 && > 3.2.5 && > 3.5.1 )": {- "distroPKGs": [- "",- ""- ],- "notes": "a"- },- "( == 3.1.5 )": {- "distroPKGs": [- "dylcx",- "wdypfjw",- "nm"- ],- "notes": ""- },- "( == 5.3.1 && < 2.2.4 && == 5.1.3 && <= 3.4.4 && < 5.2.2 && <= 2.4.4 && > 1.4.5 ) || ( < 2.1.4 && == 1.4.3 && == 3.1.1 ) || ( > 1.1.2 && > 1.3.3 && == 4.3.2 && <= 2.2.2 && > 5.2.1 ) || ( < 1.1.2 && < 2.1.1 && == 1.2.2 && <= 1.2.1 )": {- "distroPKGs": [- "ju",- "eisimx",- "flpigjs",- "bhezmsj"- ],- "notes": "jxf"- }- },- "Windows": {- "( < 2.3.4 && > 5.4.6 && <= 2.2.1 && > 4.2.2 )": {- "distroPKGs": [- "ier",- "",- "xueyka",- "",- "xjzhzmn"- ],- "notes": "zpb"- },- "( == 2.3.1 ) || ( >= 4.1.3 && > 1.3.4 ) || ( > 3.1.3 )": {- "distroPKGs": [- "hbebzn",- "y",- "",- "shchzbq"- ],- "notes": "eniidat"- },- "( >= 1.4.4 && == 1.4.4 && == 4.5.2 && >= 5.1.2 && <= 1.2.1 && < 5.2.2 && <= 1.3.4 )": {- "distroPKGs": [- "ceooik",- "kaggxnv",- "kdmi",- "gck",- "b",- "ol",- "joksm"- ],- "notes": "lmnv"- },- "unknown_versioning": {- "distroPKGs": [- "uppiivq"- ],- "notes": "ckxpx"- }- }- },- "2.2.4": {- "Windows": {- "( >= 4.4.5 && < 5.5.1 && == 2.6.3 && < 1.4.2 && >= 4.1.2 && > 2.1.3 )": {- "distroPKGs": [- "kfskuo",- "",- "",- "hm",- "tge",- "dum",- "vrewg"- ],- "notes": "bhggmup"- }- }- },- "2.7.7": {- "Darwin": {- "( <= 1.2.1 && > 1.5.2 && < 4.2.4 )": {- "distroPKGs": [],- "notes": "pqfa"- },- "( <= 1.5.4 && <= 1.4.5 && <= 5.3.2 && > 1.2.2 && == 3.3.4 && >= 4.5.5 && >= 1.5.4 ) || ( > 4.3.2 && == 2.3.4 ) || ( > 1.2.2 ) || ( == 1.1.1 && < 1.2.2 && > 2.1.3 && <= 2.1.1 )": {- "distroPKGs": [- "n",- "jcbqi"- ],- "notes": "yjrncke"- },- "( == 2.4.3 && == 4.5.3 && > 5.4.1 ) || ( <= 4.2.2 && > 2.3.2 && <= 2.2.4 && >= 3.2.3 && < 3.4.4 && == 2.4.2 )": {- "distroPKGs": [- "nzlxc",- "jvfuzgh",- "fxr",- "uylennu",- "",- "oscrxss"- ],- "notes": ""- },- "( == 3.5.5 && == 5.2.5 && >= 5.3.1 ) || ( > 2.1.3 && == 4.2.4 && == 1.3.4 && >= 4.2.1 && < 2.2.4 && > 5.1.3 ) || ( == 1.1.1 && >= 3.3.2 && <= 3.2.2 )": {- "distroPKGs": [- ""- ],- "notes": ""- },- "( >= 1.2.4 && >= 5.2.5 && == 3.2.4 && < 4.5.1 && >= 2.4.1 )": {- "distroPKGs": [- "ngpe",- "fyfa",- "",- "ltwdh",- "sey",- "nfudbi",- "ce"- ],- "notes": "rahac"- },- "( >= 1.3.5 && <= 3.2.5 && == 6.2.1 && == 4.2.4 && < 2.1.4 )": {- "distroPKGs": [- "dyujpe"- ],- "notes": "vp"- },- "( >= 5.4.2 && > 1.1.1 && < 5.5.4 && > 2.3.3 && > 5.4.5 && >= 3.5.5 && <= 4.1.5 ) || ( <= 4.4.4 && >= 2.2.3 ) || ( <= 1.2.1 && > 1.1.1 && > 3.3.1 ) || ( > 1.2.1 && == 1.1.2 && > 2.1.1 && < 1.2.3 ) || ( >= 2.1.1 ) || ( == 1.1.1 && < 1.1.1 ) || ( < 1.1.1 ) || ( > 2.2.1 )": {- "distroPKGs": [],- "notes": "vc"- },- "unknown_versioning": {- "distroPKGs": [- "ksptj",- "jkdzak",- "kdrjl",- "mkmuma"- ],- "notes": "rhppkld"- }- },- "FreeBSD": {- "( <= 3.4.5 && <= 2.2.3 && == 3.2.3 ) || ( >= 2.1.1 ) || ( <= 1.3.2 && < 3.1.2 ) || ( >= 1.2.1 && <= 2.1.2 && > 2.2.1 ) || ( <= 1.1.1 && < 1.1.1 && < 1.1.1 ) || ( >= 2.1.2 ) || ( < 1.1.1 ) || ( < 1.1.2 )": {- "distroPKGs": [- "vzntgvh"- ],- "notes": "fhugvz"- },- "( <= 5.1.4 )": {- "distroPKGs": [- "b",- "tpw",- "unlvn",- "wojkac",- "dal",- "mhofh",- "kv"- ],- "notes": "t"- },- "( == 5.3.4 && > 3.5.1 && > 2.5.5 ) || ( == 1.3.4 && <= 2.4.3 ) || ( == 1.1.2 && > 1.3.1 && > 2.2.1 && > 3.1.2 && >= 2.1.3 )": {- "distroPKGs": [- "jbk",- "aavt",- "ke",- "a"- ],- "notes": "thhhyv"- },- "( > 1.1.1 && <= 5.5.2 && <= 3.4.4 && <= 4.4.1 && == 4.5.5 && < 3.3.4 )": {- "distroPKGs": [],- "notes": "bd"- },- "unknown_versioning": {- "distroPKGs": [- "inzh"- ],- "notes": ""- }- },- "Linux_AmazonLinux": {- "( <= 5.1.3 && >= 3.2.5 && <= 3.1.5 && >= 3.1.3 && <= 1.1.5 && == 4.2.3 && == 1.4.1 ) || ( == 2.3.4 && < 4.1.1 ) || ( == 2.2.3 && < 1.3.2 )": {- "distroPKGs": [- "qckpqv",- "figgk",- ""- ],- "notes": "hxpoh"- }- },- "Linux_Gentoo": {- "( <= 3.4.1 && == 5.4.4 && > 2.1.5 && <= 5.5.3 && <= 5.5.4 && <= 3.1.1 && == 4.4.4 ) || ( <= 5.2.4 && >= 2.6.4 ) || ( <= 3.4.1 && < 3.3.3 )": {- "distroPKGs": [- "plc",- "lnuqwb",- ""- ],- "notes": "s"- },- "( == 4.5.5 && <= 6.5.1 && >= 4.3.4 && > 4.2.4 )": {- "distroPKGs": [],- "notes": "yiudh"- },- "( >= 4.3.4 && == 4.3.2 )": {- "distroPKGs": [- "d",- "rqcd",- "",- "bdcwwrl",- "",- "zfhrnx"- ],- "notes": ""- },- "( >= 5.5.2 && >= 1.4.4 && <= 5.5.4 && < 4.1.2 && < 3.3.1 && < 5.2.1 && <= 1.2.5 )": {- "distroPKGs": [- "f"- ],- "notes": "zwmwgeh"- },- "unknown_versioning": {- "distroPKGs": [- "biekdt",- "o"- ],- "notes": "re"- }- }- },- "6.8.4": {- "FreeBSD": {- "( == 1.4.5 && <= 1.1.2 && >= 4.2.1 && >= 1.1.5 )": {- "distroPKGs": [- "",- "g",- "pwtydn",- ""- ],- "notes": "po"- },- "( == 5.3.5 && >= 5.2.3 && <= 1.1.4 && <= 1.3.1 ) || ( > 1.4.3 && >= 2.3.2 && == 4.3.3 && == 4.1.2 && > 1.1.4 && <= 2.2.2 )": {- "distroPKGs": [- "",- "l",- "xjhxvzn",- "qs",- "sask"- ],- "notes": "aogkrwa"- },- "( > 1.2.1 && == 4.1.4 && == 5.2.1 && >= 1.5.2 && >= 2.5.4 && >= 2.5.5 )": {- "distroPKGs": [- "",- "lqrbg",- "s",- "",- "otu"- ],- "notes": "tosd"- },- "( >= 2.3.4 && > 3.4.1 && > 1.3.2 && <= 1.1.3 ) || ( >= 4.2.1 && < 3.2.1 && < 1.2.3 && < 1.2.2 && >= 2.1.3 && > 3.3.4 )": {- "distroPKGs": [- "faivjh",- "",- "xxdmsa"- ],- "notes": "qt"- },- "unknown_versioning": {- "distroPKGs": [- "ckvtmvi",- "xkezz",- "xdfthx",- "eucoqa",- "zppeszt",- "ojgto",- "x"- ],- "notes": "i"- }- },- "Windows": {- "( < 2.3.5 && == 5.3.2 && <= 4.1.4 && == 5.3.4 && > 4.3.4 && <= 2.3.3 ) || ( == 4.3.3 ) || ( == 1.1.3 && >= 2.1.1 && > 1.2.2 && > 1.2.2 && <= 2.3.2 )": {- "distroPKGs": [- "i",- "",- "pbbmalu",- "odhxpfl",- "ywmop",- "wjl",- "qn"- ],- "notes": "gl"- },- "( < 5.2.4 ) || ( <= 5.1.4 && <= 3.1.2 )": {- "distroPKGs": [- "rxb",- "dr",- "u",- "ztrvua",- "pvw"- ],- "notes": "gfy"- },- "( <= 5.4.2 && > 1.4.4 && > 3.1.1 && == 3.5.2 && < 1.5.3 && < 6.4.2 ) || ( < 1.2.3 && >= 4.3.1 && >= 2.1.4 ) || ( <= 1.3.2 && >= 2.1.3 && > 3.1.2 && > 3.3.1 && < 2.1.2 ) || ( >= 1.4.1 && > 1.1.2 )": {- "distroPKGs": [],- "notes": "zfi"- },- "( == 2.2.1 )": {- "distroPKGs": [- "dthpvq",- "skwf",- "xqeqmrp",- "q",- "avdone",- "gb"- ],- "notes": ""- },- "( > 1.3.4 && <= 3.2.2 && > 4.2.3 && < 4.3.2 && == 5.2.1 && <= 3.6.1 && >= 2.4.4 ) || ( <= 5.2.3 && > 3.1.2 && <= 4.2.1 && > 5.4.1 )": {- "distroPKGs": [- "mzdntj",- "vjjqxn",- "lzi",- "rjfwyom",- "eot",- "v",- "uyudpj"- ],- "notes": "tiusdgl"- },- "unknown_versioning": {- "distroPKGs": [- "zf",- "nwx",- "g",- "gknaeoj",- "",- "ravwwgq",- "lgvau"- ],- "notes": "vwrra"- }- }- },- "8.3.2": {- "FreeBSD": {- "( < 4.1.2 && >= 5.1.1 ) || ( > 1.3.2 && >= 1.4.3 && <= 1.2.4 && < 4.3.1 )": {- "distroPKGs": [- "iwn",- "l",- "dwz"- ],- "notes": "gbeadc"- },- "( > 1.2.1 && >= 3.4.4 && >= 3.3.1 && < 5.1.3 && < 3.6.5 && >= 2.5.4 )": {- "distroPKGs": [],- "notes": "tb"- },- "( > 1.5.2 && == 3.3.2 && <= 2.5.4 && > 3.3.3 && >= 1.2.5 && <= 2.3.4 && <= 4.1.2 ) || ( >= 2.4.5 && < 4.4.3 && < 2.4.2 && > 3.1.4 ) || ( >= 3.1.2 && == 3.1.1 && <= 3.1.3 && == 1.2.1 )": {- "distroPKGs": [],- "notes": "lqb"- },- "( > 4.4.3 && < 2.2.4 && < 2.3.5 && >= 1.3.6 && >= 5.1.1 && >= 4.3.3 ) || ( <= 3.3.2 && > 3.2.2 )": {- "distroPKGs": [- "egzfx",- "ix",- "xmkhxqy",- "ize"- ],- "notes": "mfooe"- },- "( > 4.4.5 && == 5.3.5 && < 1.5.4 && > 4.3.2 && > 2.5.1 && == 1.4.4 ) || ( == 2.1.1 && > 1.1.4 && > 5.4.1 && == 2.4.4 && > 2.4.2 )": {- "distroPKGs": [- "oacvwt"- ],- "notes": "jbrq"- },- "unknown_versioning": {- "distroPKGs": [- "f"- ],- "notes": "po"- }- },- "Linux_Debian": {- "( <= 1.3.2 && == 2.4.4 && >= 4.1.4 && <= 2.3.5 )": {- "distroPKGs": [- "bpojqn",- "srm"- ],- "notes": "hgbnf"- }- },- "Windows": {- "( < 3.1.3 && == 2.2.5 && < 4.2.4 && < 4.4.2 )": {- "distroPKGs": [- "wpf",- "stvws",- "fsnc",- "qcdpf",- "",- "tzgg"- ],- "notes": ""- },- "( == 2.3.4 && <= 5.3.4 && >= 3.1.3 && <= 2.2.5 && <= 1.1.5 && >= 5.5.1 && < 3.5.2 ) || ( > 3.1.1 )": {- "distroPKGs": [- "ihicfkk",- "vnhpar",- "blpfb",- "fjxzr"- ],- "notes": "tgjadwo"- },- "( > 3.5.3 && == 1.1.5 && > 2.1.4 && > 4.2.1 ) || ( < 3.3.4 && == 3.4.2 && < 1.2.4 && < 4.2.3 && <= 4.4.3 ) || ( > 3.3.2 && > 1.3.4 && >= 2.1.1 ) || ( == 1.3.2 && > 2.2.2 && >= 1.2.2 )": {- "distroPKGs": [- "h",- "iychqh"- ],- "notes": "bugbgk"- },- "( >= 2.2.3 && < 5.2.1 && < 5.3.5 && == 1.3.2 && >= 4.4.1 && == 3.4.3 )": {- "distroPKGs": [- "oiw",- "w",- "x",- "hvcczcx",- "mysrab",- "rsrcq",- "ze"- ],- "notes": ""- },- "unknown_versioning": {- "distroPKGs": [- "",- "ib",- "c",- "ro",- "twuuqzm",- "vjndhwg"- ],- "notes": "wxehuaj"- }- }- },- "unknown_version": {- "FreeBSD": {- "( < 3.3.3 )": {- "distroPKGs": [],- "notes": ""- },- "( == 3.2.4 && > 5.4.3 && == 5.5.2 && > 1.2.5 && <= 4.4.3 )": {- "distroPKGs": [- "u",- "c",- "foqv",- "pupmk",- "cru",- "",- "yhldf"- ],- "notes": "uoecga"- },- "( == 4.5.4 && >= 3.2.4 && == 5.5.1 && <= 2.1.2 && < 1.4.2 && >= 4.1.5 )": {- "distroPKGs": [- "qowkmjz",- ""- ],- "notes": "q"- },- "( > 3.2.2 && > 1.3.2 && < 2.5.5 && > 4.3.1 && == 5.4.2 && > 5.5.5 && == 1.1.5 )": {- "distroPKGs": [- "yiqut",- "kcau",- "",- "cfe",- "nlwwcf"- ],- "notes": "rur"- },- "( >= 2.5.2 ) || ( >= 4.3.1 && <= 1.2.2 && > 2.4.3 && >= 4.3.2 && >= 1.2.1 )": {- "distroPKGs": [- "cgnj",- "",- "ztz",- "qp"- ],- "notes": "qzke"- },- "( >= 4.5.5 && > 2.1.1 && == 5.1.1 && >= 2.4.4 && == 4.3.1 )": {- "distroPKGs": [- "gis"- ],- "notes": "okgdges"- },- "unknown_versioning": {- "distroPKGs": [- "ycgh"- ],- "notes": "lazve"- }- },- "Linux_Mint": {}- }- }- }- },- {- "ghcupDownloads": {- "GHCup": {- "3.7.8": {- "viArch": {},- "viChangeLog": "https:uva",- "viPostInstall": "hzfgsq",- "viPostRemove": "wnwug",- "viPreCompile": "npb",- "viPreInstall": "kbrplks",- "viReleaseDay": null,- "viSourceDL": null,- "viTags": [],- "viTestDL": {- "dlCSize": 5,- "dlHash": "l",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "hD@"- },- "dlTag": null,- "dlUri": "http:t"- }- },- "5.8.8": {- "viArch": {- "A_64": {},- "A_PowerPC": {- "FreeBSD": {- "( >= 1.4.4 && >= 1.5.5 ) || ( < 1.2.1 ) || ( <= 2.1.1 && > 1.1.2 && >= 2.3.2 )": {- "dlCSize": -4,- "dlHash": "uxek",- "dlOutput": "\u000e𘰈",- "dlSubdir": "𬦴<Hn\u000f",- "dlTag": [],- "dlUri": "http:l"- },- "( >= 3.3.1 && >= 1.6.4 && >= 3.2.2 && > 2.3.5 && == 1.1.4 && < 2.4.3 && >= 1.2.4 )": {- "dlCSize": 2,- "dlHash": "mggd",- "dlOutput": "k[Y",- "dlSubdir": "][PY",- "dlTag": [- "",- "base-3.2.3"- ],- "dlUri": "https:f"- },- "( >= 5.5.5 && > 1.1.2 ) || ( >= 2.4.1 && >= 4.2.2 && >= 3.4.4 ) || ( == 2.4.2 )": {- "dlCSize": 7,- "dlHash": "tuuoft",- "dlOutput": "𖢱\u000b",- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:rua"- },- "unknown_versioning": {- "dlCSize": -7,- "dlHash": "kbkkp",- "dlOutput": "^u",- "dlSubdir": {- "RegexDir": "\r^𣣠P"- },- "dlTag": [- "Experimental",- "Experimental",- "old",- "LatestPrerelease",- "Recommended",- "Nightly",- "Prerelease"- ],- "dlUri": "http:gt"- }- },- "Linux_Gentoo": {- "( <= 5.1.3 && <= 3.1.2 && <= 3.5.2 && == 4.5.1 ) || ( > 2.3.4 && <= 3.2.4 && == 1.2.4 && > 5.2.5 && <= 2.2.2 ) || ( == 3.1.1 && < 3.1.1 && < 2.1.2 && == 2.1.3 )": {- "dlCSize": 7,- "dlHash": "czntg",- "dlOutput": "1I`",- "dlSubdir": {- "RegexDir": "`\r\u001f"- },- "dlTag": null,- "dlUri": "http:mvnxy"- },- "unknown_versioning": {- "dlCSize": 1,- "dlHash": "tmiw",- "dlOutput": "\u001ec",- "dlSubdir": {- "RegexDir": "𬪙3"- },- "dlTag": [- "base-3.5.2",- "Ot~U\u001a\n",- "\u001d睹𮜐J3",- "Nightly",- "Prerelease"- ],- "dlUri": "http:jd"- }- },- "Linux_OpenSUSE": {- "( < 2.5.2 ) || ( < 2.4.2 && >= 2.4.4 && <= 4.1.1 && > 4.1.3 && < 2.4.3 && <= 1.2.4 ) || ( < 3.1.1 && > 3.2.4 && < 3.3.3 && <= 3.2.2 && >= 2.2.1 )": {- "dlCSize": 4,- "dlHash": "",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Recommended",- "Recommended",- "Prerelease"- ],- "dlUri": "https:y"- },- "( < 5.3.5 && <= 3.4.1 && >= 2.3.4 && <= 1.5.1 && <= 2.1.2 && <= 3.5.3 ) || ( > 1.4.1 && >= 1.3.4 && <= 3.1.4 && == 1.2.3 )": {- "dlCSize": null,- "dlHash": "ooztaj",- "dlOutput": "=",- "dlSubdir": null,- "dlTag": [- "Latest"- ],- "dlUri": "https:"- },- "( > 2.2.5 && == 1.4.3 )": {- "dlCSize": -4,- "dlHash": "jv",- "dlOutput": null,- "dlSubdir": "|",- "dlTag": [- "Nightly"- ],- "dlUri": "http:mq"- },- "( > 2.4.3 && == 2.1.5 && <= 3.2.4 && < 4.5.4 && < 1.4.5 && < 2.5.4 && < 2.4.5 ) || ( < 1.2.3 && >= 2.1.2 ) || ( == 1.1.2 && >= 4.3.1 && < 1.1.1 && <= 2.2.2 ) || ( >= 3.1.2 )": {- "dlCSize": 3,- "dlHash": "xmgzvi",- "dlOutput": "\u0005i",- "dlSubdir": "",- "dlTag": [- "old"- ],- "dlUri": "https:"- },- "( >= 4.3.2 && == 2.3.5 && <= 4.6.1 && < 2.1.1 )": {- "dlCSize": -6,- "dlHash": "kvcgpw",- "dlOutput": "_|\u0008",- "dlSubdir": {- "RegexDir": "衹iO\u0001\u001c"- },- "dlTag": null,- "dlUri": "http:uzq"- },- "unknown_versioning": {- "dlCSize": -6,- "dlHash": "lxhaj",- "dlOutput": "E",- "dlSubdir": "k;𬽭0*",- "dlTag": [- "LatestNightly",- "Nightly",- "Latest",- "Recommended",- "LatestNightly",- "Experimental"- ],- "dlUri": "http:"- }- }- },- "A_PowerPC64": {- "Darwin": {- "( < 2.4.2 && >= 3.4.4 && == 4.3.1 && == 3.3.1 && == 1.3.4 && <= 1.5.3 && >= 2.3.4 )": {- "dlCSize": 7,- "dlHash": "uhc",- "dlOutput": null,- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:gkqugi"- },- "( <= 2.3.2 )": {- "dlCSize": null,- "dlHash": "bgyhswo",- "dlOutput": "o4/e",- "dlSubdir": "",- "dlTag": [- "LatestNightly",- "Nightly",- "LatestNightly",- "base-5.5.5",- "Recommended",- "Prerelease"- ],- "dlUri": "https:hbazg"- },- "( <= 3.3.5 )": {- "dlCSize": 5,- "dlHash": "twhme",- "dlOutput": "",- "dlSubdir": "꠬穞",- "dlTag": null,- "dlUri": "http:hmxeiow"- },- "( > 3.4.1 && > 1.2.5 )": {- "dlCSize": null,- "dlHash": "vvojzxe",- "dlOutput": "B\u0008KN79",- "dlSubdir": {- "RegexDir": "𬅊rI"- },- "dlTag": [- "base-6.2.4",- "base-6.5.1"- ],- "dlUri": "https:xsgzs"- },- "( >= 4.5.5 && >= 1.3.4 && <= 5.3.6 && <= 3.3.1 && <= 2.2.3 )": {- "dlCSize": -3,- "dlHash": "",- "dlOutput": "a%",- "dlSubdir": "X[",- "dlTag": [- "",- "Recommended",- "base-6.6.3",- "Prerelease",- "Latest"- ],- "dlUri": "http:l"- },- "unknown_versioning": {- "dlCSize": -4,- "dlHash": "etddrj",- "dlOutput": null,- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "Prerelease",- "Recommended",- "Nightly",- "Recommended",- "Experimental"- ],- "dlUri": "http:lirlgy"- }- },- "FreeBSD": {- "( <= 4.3.2 && > 4.1.2 && >= 2.2.5 )": {- "dlCSize": null,- "dlHash": "cbh",- "dlOutput": "gMt",- "dlSubdir": {- "RegexDir": "2w?[t𒂁"- },- "dlTag": [- "Experimental",- "base-1.7.6",- "old",- "LatestPrerelease",- "Recommended",- "Prerelease",- "Nightly"- ],- "dlUri": "https:bzq"- },- "( == 1.6.3 && <= 1.5.3 && >= 3.2.2 && <= 2.2.2 && > 2.3.4 && < 5.1.5 ) || ( < 4.3.4 && == 1.4.1 && < 4.4.3 ) || ( == 2.3.1 )": {- "dlCSize": -1,- "dlHash": "r",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "base-2.4.1",- "Nightly",- "old",- "old",- "Nightly",- "Experimental",- "Latest"- ],- "dlUri": "http:rr"- },- "( > 2.2.3 && > 3.6.4 && > 2.5.3 && <= 1.6.2 )": {- "dlCSize": null,- "dlHash": "dksflox",- "dlOutput": null,- "dlSubdir": {- "RegexDir": ""- },- "dlTag": null,- "dlUri": "http:gjtgn"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "ax",- "dlOutput": null,- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:evea"- }- },- "Windows": {- "( < 2.2.2 && <= 2.4.4 )": {- "dlCSize": 4,- "dlHash": "q",- "dlOutput": "",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "Nightly"- ],- "dlUri": "https:zqsvwm"- },- "( == 2.3.3 && < 1.2.4 && >= 5.2.3 )": {- "dlCSize": null,- "dlHash": "rrqxtlo",- "dlOutput": "2𫡾",- "dlSubdir": "g\u000e",- "dlTag": [- "Latest",- "Nightly",- "LatestPrerelease",- "Recommended",- "Experimental",- "Experimental"- ],- "dlUri": "http:iv"- },- "( > 2.2.5 && < 4.2.2 && >= 3.2.5 ) || ( <= 4.4.2 && >= 4.1.3 && == 1.4.3 && >= 3.1.2 && > 1.1.2 && <= 1.2.1 )": {- "dlCSize": 3,- "dlHash": "",- "dlOutput": ".Ts𬼱an",- "dlSubdir": "𮀏𘄀ỦRr[",- "dlTag": null,- "dlUri": "http:oq"- },- "( > 4.1.3 && == 5.4.4 )": {- "dlCSize": null,- "dlHash": "rdodibe",- "dlOutput": "B\u0003",- "dlSubdir": "",- "dlTag": [],- "dlUri": "http:blvbi"- },- "( >= 4.2.3 && <= 2.3.3 && <= 2.1.3 && < 3.2.2 )": {- "dlCSize": -2,- "dlHash": "alp",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [],- "dlUri": "http:yjbdhl"- }- }- },- "A_Sparc": {}- },- "viChangeLog": null,- "viPostInstall": "zsscc",- "viPostRemove": "mqt",- "viPreCompile": null,- "viPreInstall": null,- "viReleaseDay": "-18953436539163570-11-25",- "viSourceDL": {- "dlCSize": -5,- "dlHash": "u",- "dlOutput": "t",- "dlSubdir": "",- "dlTag": [- "Latest",- "Nightly",- "base-1.5.5",- "Experimental",- "!",- "Experimental"- ],- "dlUri": "http:"- },- "viTags": [- "base-7.4.6",- "base-2.2.3",- "LatestNightly",- "q",- "base-3.3.5"- ],- "viTestDL": {- "dlCSize": null,- "dlHash": "",- "dlOutput": "/s(𱠚S\u001d",- "dlSubdir": "e",- "dlTag": [- "Recommended"- ],- "dlUri": "http:"- }- }- },- "Stack": {- "3.8.6": {- "viArch": {- "A_ARM": {- "Darwin": {- "( <= 1.1.3 && == 3.3.4 && > 2.1.1 && <= 4.2.2 )": {- "dlCSize": -1,- "dlHash": "l",- "dlOutput": "𩽘)t+\u0006",- "dlSubdir": "M|",- "dlTag": null,- "dlUri": "http:"- },- "( <= 2.4.1 && < 2.4.5 && < 1.3.5 && < 4.2.1 && <= 5.4.3 && == 2.3.2 ) || ( == 3.4.3 && <= 3.3.3 && > 4.1.2 && <= 2.3.3 && >= 4.3.2 && == 1.2.2 )": {- "dlCSize": -2,- "dlHash": "hl",- "dlOutput": null,- "dlSubdir": "G\u0011𪚎",- "dlTag": [- "LatestPrerelease",- "Recommended",- "Latest"- ],- "dlUri": "http:trdxyc"- },- "( > 5.4.3 && > 3.4.5 && < 1.4.4 && == 3.1.1 && >= 5.2.4 && == 1.2.2 && == 1.4.1 ) || ( >= 1.2.4 && > 4.1.3 && > 1.2.2 && > 2.1.4 && < 3.4.4 && > 1.2.1 )": {- "dlCSize": 2,- "dlHash": "qds",- "dlOutput": null,- "dlSubdir": "I",- "dlTag": [- "old",- "LatestPrerelease",- "LatestNightly",- "Prerelease",- "LatestNightly"- ],- "dlUri": "https:oihov"- },- "( >= 4.4.5 && == 2.2.2 && <= 5.3.4 && >= 2.4.1 && > 5.1.2 && == 1.1.2 && < 2.4.3 )": {- "dlCSize": -2,- "dlHash": "jnjl",- "dlOutput": null,- "dlSubdir": "{i",- "dlTag": [],- "dlUri": "http:ef"- },- "( >= 5.5.4 && == 3.3.2 ) || ( >= 5.4.1 && <= 3.3.4 && <= 1.3.2 && >= 4.3.4 && <= 3.4.4 )": {- "dlCSize": null,- "dlHash": "nsjf",- "dlOutput": "\u0007Igp",- "dlSubdir": {- "RegexDir": "XO\u001f𥞛\r"- },- "dlTag": null,- "dlUri": "http:"- },- "unknown_versioning": {- "dlCSize": 1,- "dlHash": "e",- "dlOutput": "𣉎#",- "dlSubdir": "~\u0017\\",- "dlTag": [- "Latest",- "Prerelease",- "old",- "LatestNightly",- "Latest"- ],- "dlUri": "https:oorbmyc"- }- },- "Linux_Alpine": {- "( == 3.1.1 && <= 3.2.2 && <= 2.5.3 && > 2.1.4 && > 1.5.5 && < 3.1.2 && < 2.1.1 ) || ( <= 4.4.3 )": {- "dlCSize": null,- "dlHash": "afsbjo",- "dlOutput": "=\u0010\u0002e\u000eb",- "dlSubdir": "[\u0012C",- "dlTag": [],- "dlUri": "http:bx"- },- "( >= 5.5.3 && <= 4.2.6 && == 3.2.2 )": {- "dlCSize": null,- "dlHash": "rzd",- "dlOutput": "/",- "dlSubdir": "\u0014g𘢟N4&",- "dlTag": null,- "dlUri": "https:qn"- },- "unknown_versioning": {- "dlCSize": 6,- "dlHash": "uscxvk",- "dlOutput": "",- "dlSubdir": null,- "dlTag": [- "LatestPrerelease"- ],- "dlUri": "https:tef"- }- }- },- "A_PowerPC64": {- "Darwin": {- "( < 5.1.1 && > 2.4.2 && > 2.1.4 && > 1.4.3 && > 1.1.5 && > 4.5.1 ) || ( <= 1.2.2 && <= 5.4.3 && >= 4.2.4 && > 1.4.1 && <= 2.4.3 ) || ( < 3.1.1 && < 1.3.3 ) || ( >= 2.1.2 && == 2.1.2 && >= 2.2.2 && == 2.1.2 )": {- "dlCSize": null,- "dlHash": "cgbpec",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "?",- "Recommended"- ],- "dlUri": "http:xnzyixz"- },- "( == 2.4.2 && < 2.1.4 && <= 5.5.3 && == 3.1.4 ) || ( > 2.2.4 && > 3.1.4 ) || ( == 3.3.3 && < 1.3.2 && > 3.2.3 && > 2.3.3 )": {- "dlCSize": -3,- "dlHash": "yyu",- "dlOutput": null,- "dlSubdir": "槈𧇌)^",- "dlTag": null,- "dlUri": "http:m"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "ddqh",- "dlOutput": "8\u0000",- "dlSubdir": "X𠝲c\u0007",- "dlTag": [],- "dlUri": "https:o"- }- },- "FreeBSD": {- "( < 4.5.3 && < 3.1.1 && > 2.4.2 && < 5.4.3 && == 1.4.5 && == 5.2.5 && > 1.2.1 )": {- "dlCSize": -1,- "dlHash": "usdxwul",- "dlOutput": "eUyk\u0000𢜰",- "dlSubdir": "\u0010.\u001d\t",- "dlTag": [],- "dlUri": "https:lb"- },- "( < 5.2.3 && <= 5.2.2 && == 3.3.5 ) || ( >= 2.5.4 && < 1.2.4 && <= 1.4.4 )": {- "dlCSize": 1,- "dlHash": "",- "dlOutput": "w",- "dlSubdir": null,- "dlTag": [],- "dlUri": "https:lokra"- },- "unknown_versioning": {- "dlCSize": -5,- "dlHash": "",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "4t*"- },- "dlTag": null,- "dlUri": "https:xey"- }- },- "Linux_Rocky": {- "( < 5.4.2 && > 2.5.5 && >= 3.3.5 && >= 5.4.5 && >= 2.3.5 && < 1.5.1 && <= 5.3.2 )": {- "dlCSize": null,- "dlHash": "vy",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "\u001d*8e0"- },- "dlTag": [- "Experimental",- "Latest",- "Prerelease"- ],- "dlUri": "https:kjfujg"- },- "( > 5.4.2 && <= 1.4.3 && >= 1.2.4 )": {- "dlCSize": 6,- "dlHash": "kqn",- "dlOutput": "摷\n𱶕",- "dlSubdir": "\u001c",- "dlTag": null,- "dlUri": "https:ghjos"- },- "( >= 2.2.1 && < 2.2.1 && < 2.2.6 && >= 5.1.3 && > 3.5.1 ) || ( <= 3.2.4 && < 3.3.1 && == 4.4.3 && >= 4.2.2 )": {- "dlCSize": -1,- "dlHash": "amxjqes",- "dlOutput": "C𦮭𝀷/㱀𨒻\u0017",- "dlSubdir": {- "RegexDir": "OK𬠀U(O"- },- "dlTag": null,- "dlUri": "http:xcj"- },- "( >= 3.2.3 && > 2.4.2 && < 3.4.1 && <= 4.2.5 && == 2.1.3 && > 5.3.4 && >= 1.4.4 )": {- "dlCSize": -4,- "dlHash": "jbjtmkl",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "old",- "base-4.1.3"- ],- "dlUri": "https:nvsi"- },- "( >= 5.4.5 && <= 4.3.1 && <= 3.5.5 && < 4.5.4 && > 5.4.1 )": {- "dlCSize": -2,- "dlHash": "er",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "c"- },- "dlTag": null,- "dlUri": "http:vph"- },- "unknown_versioning": {- "dlCSize": 0,- "dlHash": "zahhutv",- "dlOutput": "+8",- "dlSubdir": null,- "dlTag": [- "Recommended",- "Recommended",- "old",- "Nightly",- "LatestPrerelease",- "7\u001a]N",- "B\u0002!\u0008X\u001f"- ],- "dlUri": "https:qaeyeo"- }- }- },- "A_Sparc": {},- "A_Sparc64": {}- },- "viChangeLog": "http:v",- "viPostInstall": null,- "viPostRemove": "gnk",- "viPreCompile": null,- "viPreInstall": "at",- "viReleaseDay": "13570284092790869-12-04",- "viSourceDL": null,- "viTags": [- "old",- "base-4.5.2",- "Nightly",- "Jj%𘏾",- "o䳬+g]\u001d"- ],- "viTestDL": {- "dlCSize": -5,- "dlHash": "fyt",- "dlOutput": "ꋞ7F\u0011O`",- "dlSubdir": null,- "dlTag": [- "LatestPrerelease",- "Experimental",- "Nightly",- "Prerelease"- ],- "dlUri": "https:xwhzg"- }- },- "6.7.2": {- "viArch": {- "A_ARM64": {- "Darwin": {- "( < 1.2.4 && == 1.3.2 ) || ( < 1.3.4 && < 4.1.4 ) || ( == 1.1.3 && >= 1.3.1 && <= 3.3.2 )": {- "dlCSize": -1,- "dlHash": "y",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "ឹ,\u0010]+"- },- "dlTag": [- "Latest",- "base-4.4.1",- "old",- "Latest",- "Recommended"- ],- "dlUri": "http:qithu"- },- "( == 3.4.2 ) || ( < 2.3.2 && >= 3.3.2 && <= 4.3.4 )": {- "dlCSize": null,- "dlHash": "iuxkgp",- "dlOutput": "\u000e,h",- "dlSubdir": "=\u0014",- "dlTag": [- "k",- "old",- "Recommended"- ],- "dlUri": "http:so"- },- "( > 1.4.4 && <= 3.2.2 && >= 1.2.4 )": {- "dlCSize": null,- "dlHash": "b",- "dlOutput": "",- "dlSubdir": "",- "dlTag": [- "Nightly",- "Experimental",- "old",- "Recommended"- ],- "dlUri": "http:"- },- "( > 4.3.5 && >= 2.5.5 && <= 5.2.1 && > 4.4.2 && <= 1.4.2 && <= 1.5.1 )": {- "dlCSize": 3,- "dlHash": "d",- "dlOutput": "\u0010\u0002\u0006\u0018",- "dlSubdir": {- "RegexDir": "rT\u000f\u0018'"- },- "dlTag": [- "Recommended",- "Prerelease",- "LatestPrerelease",- "Prerelease"- ],- "dlUri": "https:obcszq"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "qkwejo",- "dlOutput": null,- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "Prerelease",- "%𣀠=",- "\u000eZ",- "Prerelease"- ],- "dlUri": "https:"- }- },- "FreeBSD": {},- "Windows": {- "( <= 2.2.4 && > 2.4.4 && > 3.4.2 && <= 3.3.1 && >= 5.3.4 ) || ( > 3.3.2 && > 2.1.3 && >= 2.3.1 && == 4.4.2 && == 2.3.4 )": {- "dlCSize": -4,- "dlHash": "",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Nightly",- "LatestPrerelease"- ],- "dlUri": "http:wdgqit"- },- "( >= 1.1.5 && < 1.3.2 && == 5.5.5 && >= 5.3.5 && >= 3.2.2 && <= 3.2.5 ) || ( > 4.3.4 && <= 5.1.1 )": {- "dlCSize": -3,- "dlHash": "tbiutl",- "dlOutput": "捬",- "dlSubdir": "y\u0007Li",- "dlTag": [- "Nightly",- "Nightly",- "Prerelease",- "Latest"- ],- "dlUri": "https:e"- },- "( >= 5.4.4 && >= 1.3.3 && >= 5.5.5 && >= 2.5.2 && < 2.4.5 && < 2.1.5 ) || ( == 3.2.5 && == 1.4.1 && <= 1.2.1 && == 2.2.3 ) || ( == 2.1.2 && >= 2.1.1 && == 1.3.1 && >= 2.1.1 ) || ( > 1.1.2 && == 2.1.1 && <= 2.1.1 ) || ( == 1.1.1 ) || ( <= 1.1.1 ) || ( == 1.1.1 ) || ( < 1.1.2 )": {- "dlCSize": 5,- "dlHash": "mcerj",- "dlOutput": "\n{\u0006",- "dlSubdir": "",- "dlTag": [- "base-5.5.3"- ],- "dlUri": "https:rdooycg"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "jhlzh",- "dlOutput": "𤹙^䄉V8On",- "dlSubdir": {- "RegexDir": "\u0018^"- },- "dlTag": [- "Latest"- ],- "dlUri": "https:"- }- }- },- "A_PowerPC": {- "Darwin": {- "( <= 5.5.4 && <= 1.5.1 && <= 5.6.5 && < 1.1.4 )": {- "dlCSize": -2,- "dlHash": "efqy",- "dlOutput": "|",- "dlSubdir": {- "RegexDir": "v𑿙8𭰚"- },- "dlTag": [- "old",- "<",- "Experimental",- "Prerelease"- ],- "dlUri": "http:uw"- },- "( == 3.1.5 ) || ( <= 4.5.1 && <= 3.4.2 && == 3.4.3 && >= 4.3.2 && > 4.1.4 ) || ( < 3.3.1 && > 3.1.2 && == 2.1.3 && >= 3.2.1 && < 1.2.4 ) || ( > 2.1.3 && < 1.1.2 && >= 3.1.2 && > 3.2.1 ) || ( >= 2.1.2 )": {- "dlCSize": -1,- "dlHash": "tfkpw",- "dlOutput": "\"",- "dlSubdir": "졾]H\u001d\\",- "dlTag": [- "old",- "LatestNightly",- "Latest",- "Recommended",- "LatestPrerelease",- "LatestNightly",- "LatestNightly"- ],- "dlUri": "http:k"- },- "( == 5.1.2 && > 4.3.4 && > 1.3.1 ) || ( > 2.4.4 && == 3.2.3 ) || ( >= 3.3.3 && >= 1.3.1 && >= 2.1.1 && <= 2.1.1 && <= 1.2.1 )": {- "dlCSize": 2,- "dlHash": "ntkw",- "dlOutput": null,- "dlSubdir": "",- "dlTag": null,- "dlUri": "https:xnqrjp"- },- "( >= 1.4.6 && < 2.4.1 )": {- "dlCSize": -1,- "dlHash": "d",- "dlOutput": "t礥`t\u0017",- "dlSubdir": "p*觛",- "dlTag": null,- "dlUri": "https:nas"- },- "( >= 3.4.2 && > 3.5.4 )": {- "dlCSize": -5,- "dlHash": "f",- "dlOutput": " F]?34",- "dlSubdir": "\u001b",- "dlTag": [- "Prerelease"- ],- "dlUri": "https:pt"- },- "unknown_versioning": {- "dlCSize": 4,- "dlHash": "ciwojg",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [],- "dlUri": "https:"- }- },- "FreeBSD": {- "( < 1.1.5 && > 5.2.4 && >= 1.5.5 && < 3.5.4 && < 2.5.2 && >= 3.1.5 ) || ( >= 3.1.2 && > 1.3.3 && > 2.3.2 )": {- "dlCSize": 1,- "dlHash": "",- "dlOutput": "",- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:eisjkje"- },- "( < 2.1.3 && == 2.5.5 && >= 3.2.2 && == 4.5.3 && < 5.4.2 )": {- "dlCSize": null,- "dlHash": "hkw",- "dlOutput": null,- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:invidgh"- },- "( <= 1.1.2 && == 3.5.2 && < 5.1.5 && > 5.1.4 && == 1.5.1 && >= 1.3.3 ) || ( == 4.1.2 && < 1.3.2 && >= 1.3.1 ) || ( > 3.1.2 )": {- "dlCSize": -3,- "dlHash": "cxwczma",- "dlOutput": "F𫧙(J!A𰦶",- "dlSubdir": {- "RegexDir": "𧖂𔖓"- },- "dlTag": [- "LatestNightly",- "Nightly"- ],- "dlUri": "http:tserifo"- },- "( == 3.3.2 && > 3.3.3 && >= 5.5.2 )": {- "dlCSize": null,- "dlHash": "so",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Experimental",- "old",- "Recommended",- "base-3.4.3"- ],- "dlUri": "https:qd"- },- "( == 4.4.1 && >= 1.2.4 && >= 5.1.5 && > 1.4.1 && >= 4.4.5 && <= 3.4.1 ) || ( == 4.1.4 && <= 4.3.2 && < 5.4.1 && < 3.4.3 ) || ( > 3.1.1 )": {- "dlCSize": null,- "dlHash": "pv",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "z=V"- },- "dlTag": [- "Prerelease",- "6𧾼\u0012𢪉",- "LatestPrerelease",- "",- "Rm4𦖋>",- "Nightly"- ],- "dlUri": "https:jtmh"- },- "( == 4.5.5 && < 3.2.4 && > 4.1.2 && == 5.3.5 && >= 5.3.1 )": {- "dlCSize": 0,- "dlHash": "",- "dlOutput": "aU\u000c",- "dlSubdir": {- "RegexDir": "T#\u0016\u0015"- },- "dlTag": [- "Recommended",- "Recommended",- "LatestPrerelease"- ],- "dlUri": "https:jnag"- },- "( >= 5.2.3 && < 3.5.5 && == 4.2.1 && > 1.4.3 && >= 5.4.5 ) || ( >= 4.2.1 && < 1.1.1 )": {- "dlCSize": 7,- "dlHash": "n",- "dlOutput": "@g)\u001d\u0008#",- "dlSubdir": null,- "dlTag": [],- "dlUri": "https:hbn"- },- "unknown_versioning": {- "dlCSize": 3,- "dlHash": "slhuw",- "dlOutput": "\u0016墆",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "LatestPrerelease",- "LatestNightly",- "Latest"- ],- "dlUri": "http:xohj"- }- },- "Linux_CentOS": {- "( < 2.1.3 && >= 1.5.1 && <= 2.1.2 && >= 5.4.5 && <= 4.4.4 )": {- "dlCSize": -2,- "dlHash": "khvwiz",- "dlOutput": "\"53𥾆Y+c",- "dlSubdir": "𲎗.C(=!",- "dlTag": null,- "dlUri": "http:mf"- },- "( == 2.2.3 && == 4.2.2 && <= 2.2.3 && > 4.2.1 && < 5.2.4 )": {- "dlCSize": 6,- "dlHash": "pstcjiy",- "dlOutput": "o\u000e\u000c",- "dlSubdir": "\u001eB\u0008",- "dlTag": null,- "dlUri": "https:kmgff"- },- "( > 1.4.5 && == 3.2.4 && < 4.5.3 && <= 3.5.1 && > 3.4.3 && == 1.2.4 && > 5.1.4 )": {- "dlCSize": 7,- "dlHash": "eto",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Latest",- "Experimental",- "Latest",- "",- "Recommended",- "LatestNightly",- "LatestPrerelease"- ],- "dlUri": "http:"- },- "( > 2.1.2 && <= 4.5.3 && <= 5.1.5 && <= 4.4.1 && == 4.2.4 ) || ( > 2.3.3 && > 2.4.3 && == 2.5.1 && <= 1.2.2 ) || ( >= 1.2.3 )": {- "dlCSize": -3,- "dlHash": "obfw",- "dlOutput": "m㙱𦼱",- "dlSubdir": {- "RegexDir": "w\u0017蹎|"- },- "dlTag": [- "base-4.4.3",- "Nightly",- "Experimental",- "Experimental",- "Experimental"- ],- "dlUri": "http:scthhm"- },- "( >= 5.2.1 && >= 4.5.4 && == 3.5.4 && < 5.1.1 && < 1.5.3 )": {- "dlCSize": null,- "dlHash": "io",- "dlOutput": ")\u000bsm4",- "dlSubdir": {- "RegexDir": "\u0013"- },- "dlTag": [- "Nightly",- "Latest",- "LatestPrerelease",- "Prerelease",- "Nightly",- "old"- ],- "dlUri": "https:kf"- },- "unknown_versioning": {- "dlCSize": -3,- "dlHash": "cht",- "dlOutput": "+)𗿩\u0017i᠄",- "dlSubdir": {- "RegexDir": "뛂}\u0001"- },- "dlTag": null,- "dlUri": "https:kema"- }- },- "Windows": {- "( < 4.5.3 && == 3.2.2 )": {- "dlCSize": -1,- "dlHash": "kcuy",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "@$@~"- },- "dlTag": [- "Nightly"- ],- "dlUri": "https:xtjjqcf"- },- "( > 1.3.2 && <= 2.3.5 && >= 1.5.4 && == 1.2.2 && <= 2.5.3 && < 2.2.2 )": {- "dlCSize": 2,- "dlHash": "o",- "dlOutput": "𭠩\u0006q",- "dlSubdir": "D\u00063\n𰂩\u0007",- "dlTag": [- "Prerelease",- "Latest",- "base-6.2.1",- "Experimental",- "Experimental",- "LatestPrerelease"- ],- "dlUri": "http:pnhzoav"- },- "( > 3.2.5 && <= 4.2.5 && == 2.3.3 && > 3.6.5 && > 2.1.1 && >= 1.2.1 ) || ( <= 3.3.1 && < 4.1.3 && <= 4.3.2 && == 4.2.1 && <= 1.1.1 ) || ( > 2.2.2 && < 1.1.3 && < 1.3.2 )": {- "dlCSize": null,- "dlHash": "ygal",- "dlOutput": "Xn\u0012s",- "dlSubdir": "",- "dlTag": null,- "dlUri": "https:sevo"- },- "( > 5.1.5 && >= 3.3.4 )": {- "dlCSize": 5,- "dlHash": "xll",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "\t𨕷"- },- "dlTag": [- "",- "Recommended",- "LatestPrerelease",- "Prerelease"- ],- "dlUri": "https:xseoh"- },- "( >= 5.1.1 && < 3.2.4 && >= 3.5.1 && <= 4.4.2 ) || ( <= 4.2.1 && < 3.4.3 && == 5.1.5 && > 3.4.4 )": {- "dlCSize": 5,- "dlHash": "hhcl",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "t"- },- "dlTag": [- "old",- "LatestPrerelease",- "Nightly",- "Nightly"- ],- "dlUri": "https:vmesvgr"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "rzvgcts",- "dlOutput": "Epe0𫄭@L",- "dlSubdir": null,- "dlTag": [- "Latest"- ],- "dlUri": "http:teojr"- }- }- },- "A_PowerPC64": {- "Darwin": {- "( < 4.2.4 ) || ( <= 2.4.4 && < 4.2.4 && < 3.3.5 )": {- "dlCSize": 3,- "dlHash": "hw",- "dlOutput": "\u001c\u001e䟾!s3",- "dlSubdir": {- "RegexDir": ">`ꢓ"- },- "dlTag": [- "Recommended",- "base-3.2.2",- "Recommended",- "Experimental",- "Nightly",- "Prerelease"- ],- "dlUri": "https:llewfr"- },- "( <= 4.5.2 && == 4.3.5 && >= 5.4.3 )": {- "dlCSize": 2,- "dlHash": "bz",- "dlOutput": "\u0003;",- "dlSubdir": {- "RegexDir": "W𪞳\""- },- "dlTag": [],- "dlUri": "https:"- },- "( > 5.3.5 ) || ( < 1.2.3 && == 4.4.3 && <= 4.3.4 ) || ( <= 3.3.2 && > 3.1.2 && > 2.3.3 ) || ( > 1.2.2 && < 1.2.1 ) || ( >= 1.1.1 ) || ( <= 1.1.1 ) || ( >= 1.1.1 )": {- "dlCSize": 7,- "dlHash": "rmj",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "LatestPrerelease"- ],- "dlUri": "http:pnvokmk"- },- "( >= 5.5.5 && > 1.3.3 && <= 1.4.2 && < 1.3.3 && < 4.5.3 ) || ( > 4.3.3 && > 2.1.3 && > 3.3.3 )": {- "dlCSize": 1,- "dlHash": "jwwrt",- "dlOutput": "㞞",- "dlSubdir": "B\u0012",- "dlTag": [- "old",- "Nightly"- ],- "dlUri": "https:hrq"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "e",- "dlOutput": "^u",- "dlSubdir": "",- "dlTag": [- "Nightly"- ],- "dlUri": "http:pa"- }- },- "FreeBSD": {- "( <= 1.2.2 && < 5.3.2 && <= 4.5.5 && >= 4.5.2 && <= 1.2.4 && > 1.6.1 && > 3.2.6 ) || ( < 2.2.2 && <= 1.1.1 )": {- "dlCSize": null,- "dlHash": "danwp",- "dlOutput": "+\u0019F_\u000cs",- "dlSubdir": "S",- "dlTag": null,- "dlUri": "http:hml"- },- "( <= 5.2.1 && < 2.1.1 && >= 3.4.2 && == 5.2.3 && < 5.1.5 && < 5.3.5 ) || ( >= 4.1.4 && == 2.1.4 && < 4.3.4 && == 2.2.2 && == 4.2.1 && == 2.1.1 )": {- "dlCSize": null,- "dlHash": "",- "dlOutput": "哢XH䕀G",- "dlSubdir": null,- "dlTag": [- "base-5.6.3",- "old",- "Nightly",- "Nightly",- "Experimental"- ],- "dlUri": "https:ljvuhk"- },- "( == 1.5.2 && < 5.4.1 ) || ( > 1.2.2 && == 1.4.1 && == 1.1.3 && == 2.2.1 && > 3.4.4 ) || ( > 2.1.2 && == 4.2.1 && > 3.3.1 && >= 1.1.3 && >= 2.3.3 ) || ( <= 2.2.3 && > 1.1.2 ) || ( <= 1.1.2 )": {- "dlCSize": 4,- "dlHash": "nrgv",- "dlOutput": "",- "dlSubdir": "𘀕H",- "dlTag": [- "Prerelease",- "LatestPrerelease",- "Prerelease",- "LatestNightly"- ],- "dlUri": "http:"- },- "( >= 4.4.3 && <= 4.1.1 && == 2.5.2 && > 4.3.5 && == 4.2.1 && == 2.5.5 && > 5.3.1 ) || ( <= 1.4.3 && <= 3.3.3 && < 4.1.2 && <= 3.1.6 )": {- "dlCSize": null,- "dlHash": "ojyjyb",- "dlOutput": "\u000e\u0002",- "dlSubdir": "\u0013l\u000b𖹾",- "dlTag": [- "",- "LatestPrerelease"- ],- "dlUri": "http:qfwe"- },- "unknown_versioning": {- "dlCSize": 3,- "dlHash": "obctoy",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "base-5.2.3",- "base-5.3.1",- "Experimental",- "Experimental",- "Nightly",- "Latest",- "old"- ],- "dlUri": "https:nroz"- }- },- "Windows": {- "( <= 2.1.4 && < 1.3.3 && >= 1.3.5 )": {- "dlCSize": null,- "dlHash": "kwnhae",- "dlOutput": "𨉧aSQ",- "dlSubdir": "\u0007`7𫓏e",- "dlTag": null,- "dlUri": "http:u"- },- "( <= 2.4.3 && > 5.2.5 && == 1.4.6 && > 1.1.4 && >= 1.2.2 )": {- "dlCSize": -5,- "dlHash": "txr",- "dlOutput": "$-F쟵_\u001b",- "dlSubdir": {- "RegexDir": "|r0튆"- },- "dlTag": [],- "dlUri": "https:"- },- "( > 3.3.5 && == 6.2.4 && == 5.2.3 && >= 1.4.3 && == 2.1.4 && <= 1.4.2 ) || ( < 2.3.1 && == 2.1.2 && > 1.2.1 && == 1.4.3 && == 2.2.1 ) || ( > 3.3.3 && >= 1.3.2 && >= 2.2.1 )": {- "dlCSize": 4,- "dlHash": "e",- "dlOutput": "1n6\u0016r",- "dlSubdir": "𧂭",- "dlTag": null,- "dlUri": "http:er"- },- "( > 4.3.6 && < 2.2.3 ) || ( == 4.2.2 ) || ( > 3.3.2 && < 1.2.2 && <= 1.2.2 && >= 3.2.3 )": {- "dlCSize": 6,- "dlHash": "p",- "dlOutput": "",- "dlSubdir": "",- "dlTag": [- "old",- "Prerelease",- "base-4.1.4",- "Latest"- ],- "dlUri": "http:oprlws"- }- }- }- },- "viChangeLog": "http:ooo",- "viPostInstall": null,- "viPostRemove": "ecszq",- "viPreCompile": "ws",- "viPreInstall": "lsa",- "viReleaseDay": "12787782918143810-09-08",- "viSourceDL": {- "dlCSize": null,- "dlHash": "xpefg",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "old",- "Latest",- "base-3.5.6"- ],- "dlUri": "https:wtycq"- },- "viTags": [- "Prerelease",- "Recommended"- ],- "viTestDL": {- "dlCSize": 5,- "dlHash": "zaeaus",- "dlOutput": "㭬+$",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "Prerelease",- "LatestNightly",- "old"- ],- "dlUri": "https:s"- }- },- "7.3.8": {- "viArch": {- "A_32": {- "Darwin": {},- "FreeBSD": {- "( < 2.5.6 && >= 4.5.3 && <= 6.1.1 && <= 2.1.2 && < 4.3.3 && == 4.5.1 )": {- "dlCSize": 5,- "dlHash": "yeh",- "dlOutput": "\u0011\u000b䫯PaL",- "dlSubdir": null,- "dlTag": [- "Experimental",- "Latest",- "Nightly"- ],- "dlUri": "http:yhqucay"- },- "( < 4.3.5 && > 1.3.2 && <= 5.1.1 && <= 3.3.5 && <= 3.1.1 && == 6.3.2 && >= 4.5.1 )": {- "dlCSize": 7,- "dlHash": "wgvkqrs",- "dlOutput": "$",- "dlSubdir": null,- "dlTag": [- "Latest",- "Experimental",- "old",- "base-3.4.1",- "M",- "old"- ],- "dlUri": "http:"- },- "( <= 3.2.5 && < 4.1.1 && == 4.4.4 )": {- "dlCSize": null,- "dlHash": "mdmvr",- "dlOutput": "9",- "dlSubdir": null,- "dlTag": [- "LatestNightly",- "Latest",- "Recommended",- "base-5.5.3",- "Recommended",- "Latest"- ],- "dlUri": "http:ctj"- },- "( <= 3.4.3 && >= 4.5.5 && > 3.4.4 ) || ( == 1.3.2 && <= 1.1.1 )": {- "dlCSize": -6,- "dlHash": "gbgmx",- "dlOutput": "-0",- "dlSubdir": null,- "dlTag": [- "Experimental",- "LatestNightly",- "LatestNightly"- ],- "dlUri": "http:del"- },- "( == 2.2.5 && >= 3.1.2 && == 5.2.4 && > 5.4.5 )": {- "dlCSize": 6,- "dlHash": "np",- "dlOutput": "'",- "dlSubdir": "ro",- "dlTag": [],- "dlUri": "http:vfsphgh"- },- "( > 4.4.4 && <= 4.2.2 && >= 4.3.1 && >= 3.3.4 && <= 4.2.5 && <= 5.2.4 && > 2.4.5 )": {- "dlCSize": null,- "dlHash": "zneuvo",- "dlOutput": "",- "dlSubdir": "u♅, i",- "dlTag": [- "LatestPrerelease",- "old"- ],- "dlUri": "http:vz"- }- },- "Linux_Exherbo": {- "( < 3.3.4 )": {- "dlCSize": -3,- "dlHash": "",- "dlOutput": null,- "dlSubdir": "\u001e",- "dlTag": null,- "dlUri": "https:dtkmdi"- },- "( == 2.2.4 && > 2.5.5 && < 2.3.3 && < 4.4.2 && > 3.2.5 && < 3.1.1 && >= 4.5.4 )": {- "dlCSize": -4,- "dlHash": "xzb",- "dlOutput": "kw\u00160",- "dlSubdir": "\u000e\u001f\u0007He",- "dlTag": [- "Prerelease",- "old"- ],- "dlUri": "https:sjdi"- },- "( > 3.3.6 && <= 1.2.5 )": {- "dlCSize": -4,- "dlHash": "lqm",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "RBU"- },- "dlTag": null,- "dlUri": "https:qjmdw"- },- "( > 5.3.1 ) || ( > 4.1.3 && == 4.3.4 ) || ( <= 2.1.3 && > 2.2.1 && < 2.2.4 && > 1.1.3 )": {- "dlCSize": -5,- "dlHash": "gr",- "dlOutput": null,- "dlSubdir": "\u0015",- "dlTag": [- "Latest",- "old",- ":kEy",- "Nightly",- "Nightly",- "LatestNightly",- "base-2.6.5"- ],- "dlUri": "http:pk"- },- "( >= 3.5.5 && == 3.6.5 )": {- "dlCSize": 5,- "dlHash": "",- "dlOutput": null,- "dlSubdir": "6\u0015\u001a,",- "dlTag": null,- "dlUri": "https:od"- },- "( >= 4.2.3 && == 5.4.2 && == 1.4.4 && == 5.5.5 && >= 2.2.1 && == 4.2.5 && < 4.1.1 ) || ( <= 1.1.1 && < 3.1.2 && > 4.2.3 && < 4.1.2 )": {- "dlCSize": null,- "dlHash": "nse",- "dlOutput": "K\u0000K𨗑や1\u001f",- "dlSubdir": {- "RegexDir": "I%𡏞v"- },- "dlTag": null,- "dlUri": "http:i"- },- "unknown_versioning": {- "dlCSize": -5,- "dlHash": "onferhn",- "dlOutput": "$t𪒼3OQp",- "dlSubdir": "N.",- "dlTag": [],- "dlUri": "https:d"- }- },- "Linux_UnknownLinux": {- "( == 1.4.2 && < 3.2.2 && < 3.1.2 )": {- "dlCSize": 3,- "dlHash": "l",- "dlOutput": "𭨕v5m$",- "dlSubdir": "dC/b",- "dlTag": [- "Experimental",- "LatestPrerelease",- "Y[",- "F\u0001\u0016U𣁥",- "old",- "𐅾9\u000f",- "Experimental"- ],- "dlUri": "https:a"- },- "( == 3.5.2 && > 1.2.4 && >= 3.3.5 && < 1.3.1 && < 3.3.4 )": {- "dlCSize": null,- "dlHash": "ii",- "dlOutput": "5,𡞉",- "dlSubdir": "\u001c👧c\r𠹰*",- "dlTag": [- "&u\r0𩮣",- "base-6.5.1"- ],- "dlUri": "http:ebv"- },- "( > 2.2.5 && < 3.5.2 && == 6.1.4 && > 4.1.5 && > 2.5.4 && < 4.1.3 )": {- "dlCSize": 6,- "dlHash": "gvdp",- "dlOutput": "",- "dlSubdir": {- "RegexDir": ",]gb"- },- "dlTag": null,- "dlUri": "http:z"- },- "( > 6.2.3 && > 3.1.3 )": {- "dlCSize": -6,- "dlHash": "amt",- "dlOutput": "2",- "dlSubdir": {- "RegexDir": "8yd"- },- "dlTag": [- "Recommended"- ],- "dlUri": "https:ccnav"- },- "( >= 5.2.1 && >= 5.4.6 && >= 5.3.4 )": {- "dlCSize": -2,- "dlHash": "ravod",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "𝛜\t"- },- "dlTag": [],- "dlUri": "https:zsrii"- },- "unknown_versioning": {- "dlCSize": -6,- "dlHash": "yyotl",- "dlOutput": "?",- "dlSubdir": "G\u0008\u001c",- "dlTag": [- "Prerelease"- ],- "dlUri": "https:u"- }- },- "Windows": {- "( <= 2.6.2 && <= 3.2.4 && <= 1.5.3 && == 5.3.1 && == 1.3.2 && > 2.5.1 && > 1.4.3 ) || ( > 2.4.3 && < 3.4.2 && >= 4.2.4 && > 2.1.2 )": {- "dlCSize": 6,- "dlHash": "khoycsv",- "dlOutput": "G.d\u0002",- "dlSubdir": null,- "dlTag": [- "Recommended",- "base-5.6.1",- "base-6.2.1",- "Nightly",- "LatestPrerelease"- ],- "dlUri": "https:zc"- },- "( <= 3.1.1 && < 3.2.5 && < 2.4.1 && < 2.6.5 && == 1.1.1 && == 4.4.2 ) || ( > 4.4.1 )": {- "dlCSize": -5,- "dlHash": "t",- "dlOutput": "3`>)",- "dlSubdir": {- "RegexDir": "\u000cq\u0012"- },- "dlTag": [- "Experimental",- "Prerelease",- "LatestPrerelease"- ],- "dlUri": "https:dmcejqc"- },- "( <= 4.1.2 && > 1.5.2 && < 4.2.1 && > 5.4.3 )": {- "dlCSize": 6,- "dlHash": "n",- "dlOutput": "",- "dlSubdir": null,- "dlTag": [- "old",- "Latest",- "Prerelease",- "Nightly",- "old",- "Prerelease"- ],- "dlUri": "http:yon"- },- "( >= 2.3.2 && == 1.2.4 && >= 2.5.5 && == 1.5.3 && < 3.1.4 ) || ( == 3.2.1 && <= 3.1.2 && >= 4.3.3 && < 1.4.4 && <= 2.1.4 )": {- "dlCSize": -6,- "dlHash": "udb",- "dlOutput": "\u001df𩛡,sb",- "dlSubdir": null,- "dlTag": [- "Recommended",- "Nightly",- "𨿦"- ],- "dlUri": "http:mcg"- },- "( >= 3.2.5 ) || ( == 1.4.2 && < 3.1.3 && >= 3.2.1 && < 2.1.3 && <= 3.2.1 )": {- "dlCSize": null,- "dlHash": "btydrgm",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "w .qNo"- },- "dlTag": [- "LatestPrerelease",- "Nightly"- ],- "dlUri": "https:naalwx"- },- "( >= 4.5.2 && >= 1.3.5 && < 2.2.4 && < 4.1.5 ) || ( < 1.3.3 && == 2.2.4 && == 1.1.1 )": {- "dlCSize": -3,- "dlHash": "k",- "dlOutput": "X!῏",- "dlSubdir": {- "RegexDir": "M"- },- "dlTag": [],- "dlUri": "https:lmbymb"- }- }- },- "A_PowerPC64": {- "Linux_Debian": {- "( < 3.5.3 && <= 5.4.4 && <= 5.1.3 && >= 4.1.4 )": {- "dlCSize": null,- "dlHash": "",- "dlOutput": "\t\u000e+T𢥅\\",- "dlSubdir": {- "RegexDir": "l\u001bb"- },- "dlTag": [- "base-4.3.3",- "Experimental"- ],- "dlUri": "https:hqgfrm"- },- "( < 5.3.1 && <= 3.5.1 && <= 1.4.4 && >= 5.1.5 && <= 2.3.1 && > 5.5.4 ) || ( > 2.1.3 && >= 1.3.2 && <= 2.1.3 )": {- "dlCSize": 6,- "dlHash": "xl",- "dlOutput": "",- "dlSubdir": "wn𤄥M",- "dlTag": [- "Latest",- "old",- "LatestPrerelease",- "Latest",- "LatestNightly",- "old",- "Nightly"- ],- "dlUri": "https:rfeatin"- },- "( == 1.1.5 )": {- "dlCSize": -1,- "dlHash": "sl",- "dlOutput": "𬨓8",- "dlSubdir": {- "RegexDir": "냌]SC𬜛"- },- "dlTag": [- "Recommended",- "base-1.1.5",- "Prerelease",- "LatestNightly"- ],- "dlUri": "https:"- },- "( == 3.1.4 ) || ( >= 1.1.3 && <= 2.2.1 && >= 2.2.2 ) || ( == 2.2.2 && > 3.2.2 ) || ( > 1.2.2 && <= 1.3.1 )": {- "dlCSize": null,- "dlHash": "y",- "dlOutput": "(\u0008",- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:zsmw"- },- "( == 4.5.5 && <= 1.2.3 && > 3.5.3 && == 2.5.5 && <= 1.3.5 ) || ( < 3.4.4 && >= 2.2.4 && == 1.4.1 && == 4.3.3 && == 3.1.3 ) || ( == 3.3.3 && < 3.3.1 && >= 2.1.1 && == 3.3.3 ) || ( >= 1.3.2 && < 2.1.1 && > 2.3.3 ) || ( >= 1.1.2 && == 2.2.1 ) || ( > 1.1.1 && >= 1.1.2 )": {- "dlCSize": null,- "dlHash": "hpz",- "dlOutput": "",- "dlSubdir": null,- "dlTag": [- "Latest"- ],- "dlUri": "http:fsvpjnh"- },- "( == 6.3.5 && == 1.5.1 && < 4.3.1 && <= 1.4.2 && > 5.3.2 && <= 5.1.5 ) || ( >= 4.3.3 && > 3.1.3 ) || ( > 1.1.3 && >= 1.1.2 && >= 2.4.2 )": {- "dlCSize": null,- "dlHash": "hs",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "𖠝p\u0017"- },- "dlTag": [- "Experimental",- "LatestNightly",- "Prerelease",- "Prerelease",- "old",- ">"- ],- "dlUri": "http:aw"- },- "( >= 5.2.1 && >= 3.6.2 )": {- "dlCSize": 2,- "dlHash": "tq",- "dlOutput": "\u0007]",- "dlSubdir": null,- "dlTag": [- "Experimental",- "LatestNightly",- "old",- "base-3.2.1",- "LatestNightly",- "\u0002S?b",- "LatestNightly"- ],- "dlUri": "http:"- },- "unknown_versioning": {- "dlCSize": -2,- "dlHash": "",- "dlOutput": " l\u000f\u0017",- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:urjz"- }- }- },- "A_Sparc": {- "Darwin": {- "( < 1.2.3 && >= 3.1.2 )": {- "dlCSize": 0,- "dlHash": "zijlfow",- "dlOutput": "Ra",- "dlSubdir": "y𒌔[",- "dlTag": [- "LatestPrerelease",- "Nightly",- "LatestPrerelease"- ],- "dlUri": "http:qg"- }- },- "FreeBSD": {- "( > 2.1.4 && >= 2.3.1 )": {- "dlCSize": 7,- "dlHash": "jabzipn",- "dlOutput": null,- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:k"- },- "( >= 3.2.2 && < 2.5.2 && >= 4.5.4 && >= 1.4.2 ) || ( == 2.3.4 && < 3.1.3 && >= 4.1.1 && > 3.4.1 && == 4.2.3 && == 4.4.4 ) || ( == 1.5.4 && < 3.2.2 && < 3.2.3 && == 3.3.2 )": {- "dlCSize": 4,- "dlHash": "tou",- "dlOutput": null,- "dlSubdir": "",- "dlTag": [],- "dlUri": "https:txx"- },- "unknown_versioning": {- "dlCSize": 0,- "dlHash": "lub",- "dlOutput": "J𥬚5h",- "dlSubdir": {- "RegexDir": "+|o"- },- "dlTag": null,- "dlUri": "https:g"- }- },- "Windows": {}- },- "A_Sparc64": {- "Darwin": {- "( <= 3.2.2 ) || ( <= 5.3.3 && <= 1.4.4 && < 4.2.3 && < 1.3.4 && >= 3.4.2 && == 4.2.4 ) || ( <= 2.2.2 && >= 3.3.2 && < 3.3.2 && == 2.1.1 && > 1.1.3 )": {- "dlCSize": 3,- "dlHash": "ip",- "dlOutput": "\u001a*A𱜘",- "dlSubdir": {- "RegexDir": "aa\u0018D"- },- "dlTag": [- "Prerelease"- ],- "dlUri": "https:ho"- },- "( == 4.5.3 && < 4.4.5 && >= 5.3.1 && >= 4.2.1 && > 3.3.2 )": {- "dlCSize": 2,- "dlHash": "twyuus",- "dlOutput": "2GD",- "dlSubdir": {- "RegexDir": "\u0006\u001b_"- },- "dlTag": [- "Prerelease",- "old",- "old",- "Experimental",- "base-4.3.4"- ],- "dlUri": "http:pinxdt"- },- "( >= 3.1.5 ) || ( > 5.4.3 && >= 2.1.2 )": {- "dlCSize": null,- "dlHash": "lyvuydo",- "dlOutput": null,- "dlSubdir": "}",- "dlTag": [- "Latest",- "old",- "LatestNightly"- ],- "dlUri": "https:x"- },- "unknown_versioning": {- "dlCSize": 1,- "dlHash": "rcfm",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "old"- ],- "dlUri": "https:fivv"- }- }- }- },- "viChangeLog": "https:rl",- "viPostInstall": "ztlnqav",- "viPostRemove": "odhugyq",- "viPreCompile": "xpdodj",- "viPreInstall": "",- "viReleaseDay": null,- "viSourceDL": null,- "viTags": [],- "viTestDL": {- "dlCSize": -2,- "dlHash": "",- "dlOutput": "J\u001d+\u0012\u0008",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [],- "dlUri": "http:tzhapz"- }- },- "7.7.2": {- "viArch": {- "A_32": {- "Darwin": {},- "FreeBSD": {- "( < 3.1.4 ) || ( < 2.1.3 && >= 1.3.2 )": {- "dlCSize": -2,- "dlHash": "dbvnduo",- "dlOutput": "r",- "dlSubdir": null,- "dlTag": [- "Prerelease",- "Experimental",- "base-3.5.2",- "J\u000b",- "Latest"- ],- "dlUri": "http:osiho"- },- "( == 2.1.3 && < 2.2.5 && >= 1.3.3 ) || ( < 3.5.2 && <= 1.1.4 && <= 1.2.4 && > 2.2.3 && < 4.2.2 ) || ( >= 2.1.3 ) || ( < 1.2.2 && >= 1.1.2 && == 1.1.2 ) || ( == 1.1.1 && >= 1.1.1 && <= 1.1.1 )": {- "dlCSize": -2,- "dlHash": "qf",- "dlOutput": ">",- "dlSubdir": null,- "dlTag": [],- "dlUri": "https:prcjfea"- },- "( == 3.4.2 && > 5.5.2 ) || ( <= 4.1.2 && <= 4.4.3 && >= 2.4.4 && >= 4.3.3 && < 1.2.3 && == 3.3.1 )": {- "dlCSize": 7,- "dlHash": "eaypp",- "dlOutput": "\u001a",- "dlSubdir": null,- "dlTag": [- "Experimental",- "Latest",- "Nightly",- "Nightly"- ],- "dlUri": "http:iytcu"- },- "( == 3.4.4 && < 3.1.3 && >= 4.1.2 && < 2.4.3 && == 1.3.2 && == 3.5.1 ) || ( >= 3.4.2 && < 2.4.1 && > 4.1.3 && <= 1.1.2 ) || ( > 1.1.3 && == 3.2.3 && <= 2.4.2 && < 3.1.3 && == 2.3.2 ) || ( >= 1.2.2 && == 1.1.1 ) || ( >= 1.1.1 && >= 2.2.1 && < 1.1.1 )": {- "dlCSize": null,- "dlHash": "ffqdvs",- "dlOutput": null,- "dlSubdir": ".",- "dlTag": null,- "dlUri": "http:smutz"- },- "( == 5.3.4 && >= 3.1.2 && <= 4.5.2 && > 4.5.2 && < 1.1.1 && >= 5.3.4 && >= 3.3.4 )": {- "dlCSize": -4,- "dlHash": "gzhqpr",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [],- "dlUri": "https:faai"- },- "( >= 2.4.1 ) || ( > 2.3.1 && < 4.2.2 && > 1.1.2 && > 4.4.1 ) || ( <= 1.1.1 )": {- "dlCSize": null,- "dlHash": "",- "dlOutput": "&",- "dlSubdir": null,- "dlTag": [- "LatestNightly",- "Experimental",- "LatestNightly",- "x"- ],- "dlUri": "http:p"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "o",- "dlOutput": "\u0011K",- "dlSubdir": {- "RegexDir": "]#"- },- "dlTag": null,- "dlUri": "https:go"- }- },- "Linux_Debian": {- "( < 1.5.2 && >= 4.5.1 && > 5.1.3 && > 2.1.3 && >= 3.4.1 ) || ( == 1.4.3 && <= 1.2.2 && > 3.2.1 ) || ( > 1.3.2 ) || ( > 1.2.1 && <= 1.1.2 && <= 1.1.3 )": {- "dlCSize": -4,- "dlHash": "x",- "dlOutput": "QG",- "dlSubdir": null,- "dlTag": [- "Recommended"- ],- "dlUri": "http:qewuvum"- },- "( == 3.2.6 && > 1.1.3 && > 3.5.3 && <= 2.2.3 && >= 3.6.5 && > 2.4.3 && <= 2.1.5 )": {- "dlCSize": -4,- "dlHash": "o",- "dlOutput": "*\u0015G",- "dlSubdir": "<2𠥮>p",- "dlTag": [- "LatestNightly"- ],- "dlUri": "http:chg"- }- },- "Linux_RedHat": {- "( <= 5.2.1 && == 5.5.3 && == 4.2.1 && > 5.2.1 && > 3.5.3 && <= 5.2.2 )": {- "dlCSize": null,- "dlHash": "y",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "base-4.1.7"- ],- "dlUri": "http:rordqv"- },- "( > 1.2.2 ) || ( < 1.3.4 && >= 2.3.2 && < 2.1.3 ) || ( <= 1.1.3 ) || ( == 4.1.2 )": {- "dlCSize": null,- "dlHash": "",- "dlOutput": "&Q\r=",- "dlSubdir": null,- "dlTag": [- "base-5.3.3",- "",- "LatestNightly",- "Latest",- "Prerelease",- "Latest"- ],- "dlUri": "https:mo"- },- "( > 5.3.1 && >= 5.1.4 && < 3.4.5 )": {- "dlCSize": 6,- "dlHash": "",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "]~"- },- "dlTag": [],- "dlUri": "http:dmfetf"- },- "( >= 2.3.5 && == 4.2.3 ) || ( > 4.1.4 && == 3.4.2 )": {- "dlCSize": 5,- "dlHash": "nsqmqgm",- "dlOutput": "\n𒄥)I",- "dlSubdir": null,- "dlTag": [],- "dlUri": "https:eljuqxa"- }- },- "Windows": {- "( == 5.2.2 && == 6.2.1 ) || ( == 4.3.2 && < 4.1.4 && > 3.1.1 && <= 3.4.3 ) || ( > 3.1.1 && >= 3.3.3 && >= 1.1.2 )": {- "dlCSize": -4,- "dlHash": "tdojrll",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [],- "dlUri": "https:nsnhjmj"- },- "( > 1.1.1 && < 1.3.5 && <= 5.1.1 && >= 3.2.1 && <= 2.3.5 ) || ( == 4.2.4 )": {- "dlCSize": -1,- "dlHash": "",- "dlOutput": "\u000ct",- "dlSubdir": null,- "dlTag": [- "Prerelease",- "Recommended",- "base-5.2.2",- "LatestPrerelease",- "Recommended",- "Prerelease"- ],- "dlUri": "http:bi"- },- "( > 3.1.1 )": {- "dlCSize": 3,- "dlHash": "apr",- "dlOutput": "",- "dlSubdir": "'fl4",- "dlTag": [- "LatestPrerelease",- "\u000ec",- "old",- "old",- "P`",- "Latest"- ],- "dlUri": "http:mdpe"- }- }- },- "A_64": {- "Darwin": {- "( <= 4.3.2 && >= 4.3.2 ) || ( >= 4.3.1 && <= 1.2.5 && < 1.1.3 )": {- "dlCSize": -3,- "dlHash": "qy",- "dlOutput": "",- "dlSubdir": "PP",- "dlTag": [- "Recommended",- "LatestNightly",- "base-1.3.2",- "xp",- "LatestPrerelease"- ],- "dlUri": "https:mdula"- }- },- "FreeBSD": {- "( < 4.2.1 && < 5.1.2 && >= 1.4.1 && < 4.3.2 && == 3.3.6 && == 4.1.5 )": {- "dlCSize": 5,- "dlHash": "crt",- "dlOutput": null,- "dlSubdir": "\u0014",- "dlTag": [- "base-6.1.4",- "old"- ],- "dlUri": "https:zt"- },- "( < 5.4.3 && > 1.2.1 && > 3.3.4 && <= 1.1.1 )": {- "dlCSize": -7,- "dlHash": "udl",- "dlOutput": "j",- "dlSubdir": null,- "dlTag": [- "base-6.6.5"- ],- "dlUri": "https:ap"- },- "( > 1.1.4 && >= 4.4.1 && < 5.3.4 && == 1.5.3 && > 5.3.5 && == 1.2.4 && <= 2.3.4 ) || ( > 1.5.3 ) || ( > 3.3.1 && <= 3.2.3 && < 3.3.3 )": {- "dlCSize": null,- "dlHash": "qxc",- "dlOutput": "\u000c\u0014",- "dlSubdir": {- "RegexDir": "8\u001f\u0003\u0011 E"- },- "dlTag": [- "o\u0017S",- "Recommended",- "Prerelease"- ],- "dlUri": "http:oykvswd"- },- "( >= 3.4.3 ) || ( <= 3.2.4 ) || ( >= 3.1.3 && <= 1.1.3 && == 2.3.2 && < 2.2.3 )": {- "dlCSize": 6,- "dlHash": "lg",- "dlOutput": "\u0015M'K𩆛",- "dlSubdir": "ly",- "dlTag": [- "Prerelease",- "Experimental",- "Prerelease"- ],- "dlUri": "https:s"- },- "unknown_versioning": {- "dlCSize": -4,- "dlHash": "qg",- "dlOutput": "L",- "dlSubdir": "=+G\u0002Q",- "dlTag": [- "Latest"- ],- "dlUri": "https:cmfk"- }- },- "Linux_AmazonLinux": {- "( < 1.4.2 && < 3.1.4 && > 1.4.5 )": {- "dlCSize": -5,- "dlHash": "piynn",- "dlOutput": "৴4\u000b&\u001c𐎳",- "dlSubdir": null,- "dlTag": [- "L𮅥"- ],- "dlUri": "http:sg"- },- "( < 5.2.4 && > 3.3.1 && < 2.1.1 && <= 1.4.4 && > 4.1.2 && <= 3.4.2 && >= 5.4.2 ) || ( >= 2.4.3 && <= 2.3.3 && < 1.1.4 && >= 1.3.1 )": {- "dlCSize": 5,- "dlHash": "vvwg",- "dlOutput": "𰱪7>445",- "dlSubdir": "𢬈l",- "dlTag": [- "Recommended"- ],- "dlUri": "https:hycodxn"- },- "( == 1.2.5 && <= 5.3.1 && <= 3.3.4 && < 4.5.1 && >= 3.3.2 ) || ( == 3.3.4 && > 3.1.3 )": {- "dlCSize": null,- "dlHash": "",- "dlOutput": "B=𞁄~Gp",- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:pfx"- },- "( > 5.2.5 ) || ( < 3.1.1 && <= 2.3.2 ) || ( < 3.1.3 && <= 1.1.2 && == 2.1.3 )": {- "dlCSize": -6,- "dlHash": "ulz",- "dlOutput": "?2\u0014!\u0002",- "dlSubdir": {- "RegexDir": "会5}"- },- "dlTag": [- "LatestNightly",- "Recommended"- ],- "dlUri": "http:wa"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "n",- "dlOutput": "+m",- "dlSubdir": "",- "dlTag": null,- "dlUri": "https:wcz"- }- },- "Linux_Debian": {- "( >= 1.2.1 && < 4.4.2 && <= 3.3.5 && >= 1.4.1 && > 5.2.3 && >= 5.6.5 ) || ( <= 3.1.3 && >= 4.4.2 && <= 1.3.2 && == 4.3.4 && < 2.4.4 )": {- "dlCSize": null,- "dlHash": "apha",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "base-6.6.6",- "Latest",- "𠁸穑A",- "base-5.6.3",- "Recommended"- ],- "dlUri": "https:htp"- }- },- "Windows": {- "( <= 1.1.4 && < 3.5.2 )": {- "dlCSize": 5,- "dlHash": "vlpqfu",- "dlOutput": "PTZ𬝦6𱟋",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "Latest",- "LatestNightly",- "LatestNightly"- ],- "dlUri": "http:ylnxlr"- },- "( <= 1.3.1 && < 1.3.3 )": {- "dlCSize": 4,- "dlHash": "u",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Recommended",- "Nightly"- ],- "dlUri": "https:rqj"- },- "( <= 2.4.1 && > 3.3.2 && == 4.3.5 )": {- "dlCSize": 1,- "dlHash": "jvmht",- "dlOutput": null,- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:"- },- "( == 2.3.2 && <= 1.2.2 && < 5.1.2 && > 2.3.2 && > 5.2.1 && >= 4.1.4 && < 4.1.5 ) || ( == 1.4.2 && < 4.3.3 && >= 2.1.1 && < 4.1.3 && < 4.3.1 ) || ( == 3.2.2 && == 1.2.2 && >= 2.1.1 )": {- "dlCSize": 4,- "dlHash": "heqe",- "dlOutput": null,- "dlSubdir": "𗄗/prj",- "dlTag": null,- "dlUri": "http:qk"- },- "( > 4.5.1 && >= 3.4.1 && >= 4.1.2 && >= 3.3.5 && > 1.1.5 ) || ( <= 4.3.4 && >= 3.1.4 && < 1.3.2 && <= 2.4.3 && <= 4.2.3 && < 1.4.2 ) || ( > 1.3.2 ) || ( == 3.2.1 && == 1.2.3 && <= 1.1.1 ) || ( < 1.1.2 && < 1.1.1 )": {- "dlCSize": null,- "dlHash": "ewbjr",- "dlOutput": "OZ",- "dlSubdir": "8𨜝Ng_",- "dlTag": null,- "dlUri": "http:puqsp"- },- "( > 5.2.5 && < 1.4.5 && < 4.1.5 && <= 1.4.1 && >= 3.4.3 && <= 5.5.5 && < 2.2.5 ) || ( <= 1.2.2 && < 4.1.4 && > 2.3.2 && == 6.3.2 && <= 4.2.2 && == 1.4.4 ) || ( == 2.2.2 && <= 1.2.1 && > 1.1.3 && <= 3.1.1 && == 2.2.2 ) || ( <= 2.3.2 && > 2.1.1 )": {- "dlCSize": -4,- "dlHash": "b",- "dlOutput": "\u0008",- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:"- },- "unknown_versioning": {- "dlCSize": -7,- "dlHash": "mjg",- "dlOutput": "\\\u001c𥖫\u0016'\u0016q",- "dlSubdir": "5",- "dlTag": [- "LatestNightly",- "Experimental"- ],- "dlUri": "https:plmnf"- }- }- },- "A_ARM64": {- "Darwin": {- "( < 2.1.3 && == 2.1.2 && <= 3.5.1 && < 5.6.5 && <= 3.2.4 && >= 5.6.1 )": {- "dlCSize": 1,- "dlHash": "qimcc",- "dlOutput": "㠥\u0012_𖣊?.",- "dlSubdir": "㪟&𭙑",- "dlTag": [- "old"- ],- "dlUri": "https:ye"- },- "( <= 2.4.3 ) || ( <= 3.4.3 )": {- "dlCSize": 6,- "dlHash": "nnrtyvt",- "dlOutput": "<\u0006𰉴",- "dlSubdir": {- "RegexDir": "\u0010D:^k\n"- },- "dlTag": [- "LatestNightly",- "Experimental",- "LatestPrerelease",- "base-6.5.5",- "LatestPrerelease",- "Recommended"- ],- "dlUri": "http:biwkp"- },- "( <= 3.1.3 && == 3.2.4 ) || ( < 3.3.1 && < 1.2.4 )": {- "dlCSize": 7,- "dlHash": "tiuw",- "dlOutput": "𘩻_LE ",- "dlSubdir": {- "RegexDir": ">x"- },- "dlTag": null,- "dlUri": "https:r"- },- "( <= 3.2.2 && <= 5.1.3 && >= 2.6.3 && >= 5.4.2 && > 1.5.4 && > 4.4.3 ) || ( >= 2.4.2 && >= 4.1.4 && <= 4.4.5 )": {- "dlCSize": 1,- "dlHash": "cwzo",- "dlOutput": "Y\u000b,%",- "dlSubdir": ")",- "dlTag": null,- "dlUri": "https:hk"- },- "( == 3.2.5 && <= 3.3.3 && == 2.1.5 && >= 4.4.2 && > 5.5.5 && <= 4.1.3 && < 2.2.4 ) || ( < 3.4.2 && > 4.3.3 && > 4.4.4 && == 2.3.3 && <= 2.3.4 && >= 2.2.3 )": {- "dlCSize": null,- "dlHash": "yzj",- "dlOutput": "+J",- "dlSubdir": ",u\n9|𤣾",- "dlTag": [- "LatestNightly",- "𤀐\t𣕶n",- "base-1.3.3",- "old"- ],- "dlUri": "https:kiqtic"- },- "( > 2.1.4 )": {- "dlCSize": 4,- "dlHash": "nxvecil",- "dlOutput": ".4n(\r\u0011",- "dlSubdir": "8\"}",- "dlTag": [- "LatestNightly",- "base-3.5.2",- "LatestNightly",- "LatestPrerelease"- ],- "dlUri": "http:hpvk"- },- "( >= 3.4.1 && > 4.5.3 && == 1.1.2 && > 3.1.2 && > 4.5.4 && >= 5.3.1 && == 5.3.1 )": {- "dlCSize": -1,- "dlHash": "kfydgw",- "dlOutput": "deiV",- "dlSubdir": {- "RegexDir": "ZO7W"- },- "dlTag": null,- "dlUri": "http:mgnecl"- }- },- "FreeBSD": {- "( == 3.1.4 && < 3.5.5 && == 2.3.2 && < 1.5.3 && >= 3.5.4 ) || ( <= 4.4.4 && == 3.4.2 && < 3.3.4 ) || ( >= 3.1.1 && <= 3.2.2 && < 2.1.2 && <= 1.3.1 )": {- "dlCSize": 3,- "dlHash": "kr",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "z"- },- "dlTag": [- "Nightly",- "LatestNightly",- "Latest",- "Prerelease",- "LatestNightly",- "Recommended",- "base-4.6.3"- ],- "dlUri": "https:zcf"- },- "( == 4.3.4 && <= 1.1.1 && < 1.3.3 && > 2.2.1 && > 5.1.2 )": {- "dlCSize": -5,- "dlHash": "gj",- "dlOutput": "U)TD",- "dlSubdir": {- "RegexDir": "rh\u000e"- },- "dlTag": [- "LatestPrerelease"- ],- "dlUri": "http:nsvwhth"- },- "( > 4.2.5 && < 3.4.4 && <= 1.1.2 && >= 4.4.1 && >= 2.4.2 )": {- "dlCSize": -6,- "dlHash": "q",- "dlOutput": ")",- "dlSubdir": "J䆵\u0015<",- "dlTag": [- "old",- "LatestNightly",- "Nightly"- ],- "dlUri": "http:vega"- },- "( >= 3.4.2 && <= 5.5.2 && == 2.5.2 && == 5.2.6 && >= 4.5.4 && == 2.4.3 )": {- "dlCSize": 0,- "dlHash": "fjv",- "dlOutput": null,- "dlSubdir": "!\u0001",- "dlTag": [- "Latest",- "LatestPrerelease",- "B\t",- "old"- ],- "dlUri": "https:eunuxzx"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "lcen",- "dlOutput": "T$\u0006[w",- "dlSubdir": {- "RegexDir": "𘑮3$\u0018"- },- "dlTag": [],- "dlUri": "http:"- }- },- "Linux_CentOS": {- "( >= 5.2.2 && < 4.1.4 && >= 3.5.4 ) || ( > 4.2.3 && >= 1.4.4 && >= 2.4.1 && >= 4.4.4 ) || ( <= 1.2.3 && <= 1.2.1 )": {- "dlCSize": -1,- "dlHash": "xnvz",- "dlOutput": "\u0017TdV-",- "dlSubdir": {- "RegexDir": "귎 /𪁨廣"- },- "dlTag": null,- "dlUri": "http:wlxa"- }- }- },- "A_PowerPC": {- "Darwin": {- "( < 4.1.5 && == 3.4.2 && >= 4.5.3 && > 2.1.4 ) || ( == 2.1.1 && == 1.3.4 && <= 2.4.1 && < 3.1.1 && <= 4.2.4 )": {- "dlCSize": -5,- "dlHash": "hwywc",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "base-4.2.3",- "Latest",- "old",- "old",- "LatestPrerelease",- "Prerelease"- ],- "dlUri": "https:b"- },- "( < 4.4.1 )": {- "dlCSize": null,- "dlHash": "bpee",- "dlOutput": null,- "dlSubdir": "Z\r)",- "dlTag": [- "\u0014\u001f#",- "LatestNightly",- "K꺤𥊵"- ],- "dlUri": "http:elbz"- },- "( == 5.1.4 && <= 2.4.5 && <= 3.4.5 && == 4.1.3 && == 1.4.2 && > 5.2.5 && > 2.3.3 )": {- "dlCSize": -6,- "dlHash": "mluqghk",- "dlOutput": "oW'\u0017!",- "dlSubdir": null,- "dlTag": [- "LatestNightly",- "Nightly",- "𮤵\u0015켿Q",- "base-6.6.1",- "Recommended"- ],- "dlUri": "https:"- }- },- "FreeBSD": {- "( < 1.5.3 )": {- "dlCSize": 4,- "dlHash": "anvo",- "dlOutput": "𧕈\u000e𤎄=",- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:qqx"- },- "( <= 2.2.2 && < 6.1.3 && >= 1.4.4 && < 4.1.3 ) || ( <= 4.4.4 )": {- "dlCSize": 2,- "dlHash": "jeo",- "dlOutput": "%號𨳐1\u0012N",- "dlSubdir": "[H\u0001\u001a\u001b",- "dlTag": [- "base-2.5.6"- ],- "dlUri": "http:cqngc"- },- "( <= 2.3.5 && < 2.1.3 && <= 3.1.1 && < 3.4.3 && <= 3.1.1 ) || ( == 3.1.4 && >= 4.4.4 && < 3.4.2 && >= 1.4.1 && <= 4.3.3 )": {- "dlCSize": -3,- "dlHash": "",- "dlOutput": "\u0013\u0007𣁤I4",- "dlSubdir": "",- "dlTag": [- "old",- "old",- "old",- "Recommended",- "A:%H\u0019",- "Recommended"- ],- "dlUri": "https:"- },- "( >= 5.1.3 && == 5.2.4 && >= 1.3.4 && < 3.1.3 ) || ( <= 4.4.3 && > 1.2.1 && < 3.2.1 ) || ( < 1.2.3 && == 3.2.3 && >= 2.1.4 && == 3.3.1 ) || ( >= 2.2.2 && < 1.1.1 )": {- "dlCSize": 7,- "dlHash": "",- "dlOutput": null,- "dlSubdir": "𫝎",- "dlTag": [- "LatestPrerelease"- ],- "dlUri": "http:vl"- }- }- },- "A_PowerPC64": {},- "A_Sparc64": {- "Darwin": {- "( < 1.4.1 && >= 2.5.2 && == 1.1.3 && < 2.3.3 )": {- "dlCSize": null,- "dlHash": "dlfmp",- "dlOutput": "W\u0016䍷",- "dlSubdir": {- "RegexDir": "0:"- },- "dlTag": [- "Experimental",- "Recommended",- "Experimental",- "LatestNightly"- ],- "dlUri": "http:eymtcb"- },- "( < 5.2.2 && <= 2.5.5 && == 4.3.2 && >= 1.2.3 && < 4.5.1 && < 1.2.1 && < 1.4.2 )": {- "dlCSize": -7,- "dlHash": "qofekmi",- "dlOutput": "\u0000",- "dlSubdir": "|",- "dlTag": null,- "dlUri": "http:piiaa"- },- "( > 1.2.5 && <= 2.5.4 && > 6.5.1 && > 3.3.4 && < 4.2.4 && < 5.2.4 && > 3.3.3 ) || ( == 3.1.2 && < 1.3.2 && == 1.1.2 && >= 2.1.1 )": {- "dlCSize": null,- "dlHash": "opopu",- "dlOutput": "𩽱",- "dlSubdir": "8g;~",- "dlTag": [- "old",- "Latest",- "LatestPrerelease",- "LatestNightly"- ],- "dlUri": "https:wdnkgme"- },- "( >= 2.5.1 && > 6.1.4 && <= 3.4.4 && == 3.4.3 && < 4.3.2 && > 2.4.4 )": {- "dlCSize": -3,- "dlHash": "",- "dlOutput": "itR/",- "dlSubdir": {- "RegexDir": "2,"- },- "dlTag": [- "Experimental",- "old"- ],- "dlUri": "http:hqvhw"- },- "unknown_versioning": {- "dlCSize": 6,- "dlHash": "bwrlwx",- "dlOutput": "𮌕`",- "dlSubdir": {- "RegexDir": "'#C𛊸XM"- },- "dlTag": [- "base-3.6.3",- "Experimental",- "Experimental",- "Nightly",- "LatestNightly"- ],- "dlUri": "https:ad"- }- },- "FreeBSD": {- "( > 1.3.1 && == 2.5.1 && > 5.6.4 && == 3.2.1 && == 4.5.3 ) || ( >= 2.5.4 && == 4.2.1 && <= 4.4.4 ) || ( <= 1.1.1 && >= 3.2.3 && > 2.1.2 && > 1.3.1 && == 1.2.2 ) || ( == 1.1.1 )": {- "dlCSize": 6,- "dlHash": "s",- "dlOutput": null,- "dlSubdir": "f>⩽Y/\\",- "dlTag": null,- "dlUri": "http:cmfj"- },- "unknown_versioning": {- "dlCSize": -3,- "dlHash": "",- "dlOutput": "\u0018际xH:",- "dlSubdir": null,- "dlTag": [- "base-5.6.4",- "Recommended",- "Latest",- "Latest",- "LatestNightly",- "LatestNightly"- ],- "dlUri": "https:dlutev"- }- },- "Linux_CentOS": {- "( < 2.3.5 && < 4.3.5 ) || ( >= 3.3.1 && == 4.3.3 && <= 3.4.2 && >= 4.2.4 && <= 1.2.3 && <= 4.3.3 ) || ( == 3.3.2 && <= 3.1.1 ) || ( > 1.2.1 && < 2.1.3 && >= 1.1.2 && > 1.1.2 )": {- "dlCSize": -4,- "dlHash": "ndpgk",- "dlOutput": "n,\u0007\u000c𗮆(",- "dlSubdir": "𱚞3\u0005}\u0001",- "dlTag": [- "Latest",- "LatestNightly",- "LatestNightly",- "old",- "Recommended",- "Nightly"- ],- "dlUri": "https:inmpi"- },- "( <= 4.3.2 && == 2.5.2 && >= 3.5.1 ) || ( == 1.2.1 && <= 1.2.1 && < 4.3.2 && < 3.1.2 && == 1.4.2 && < 2.3.2 )": {- "dlCSize": 5,- "dlHash": "i",- "dlOutput": "98\u0012",- "dlSubdir": "~2M𘗸L",- "dlTag": [- "base-6.6.2",- "Recommended",- "Experimental",- "base-1.4.6"- ],- "dlUri": "https:vbydb"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "q",- "dlOutput": "+⣰\u0003\u0010",- "dlSubdir": "0oik𪸝슝",- "dlTag": null,- "dlUri": "http:bumuzn"- }- },- "Linux_Rocky": {- "( < 5.4.4 && < 5.3.2 && == 5.3.4 && > 3.3.2 && == 2.4.1 && == 4.3.4 && >= 2.1.3 ) || ( > 2.1.1 )": {- "dlCSize": -6,- "dlHash": "puxdyt",- "dlOutput": null,- "dlSubdir": "]~𧚼𛇌𰽕Y",- "dlTag": [- "LatestNightly",- "Prerelease",- "old",- "Prerelease",- "old"- ],- "dlUri": "https:huxq"- },- "( >= 4.1.2 && >= 5.4.5 && >= 1.2.1 && > 5.4.4 )": {- "dlCSize": 2,- "dlHash": "ezeb",- "dlOutput": "",- "dlSubdir": null,- "dlTag": [- "Latest",- "Latest",- "old",- "Nightly",- "Latest",- "LatestNightly",- "Recommended"- ],- "dlUri": "https:w"- },- "unknown_versioning": {- "dlCSize": -5,- "dlHash": "d",- "dlOutput": "𨢩\u000c}I\u0003\u0018:",- "dlSubdir": {- "RegexDir": "\u0010歔`tr"- },- "dlTag": null,- "dlUri": "https:twmr"- }- },- "Linux_Ubuntu": {- "( < 2.4.4 && <= 5.2.2 && < 3.2.3 && >= 5.4.1 && <= 4.5.2 )": {- "dlCSize": 7,- "dlHash": "rfli",- "dlOutput": "7VTPo",- "dlSubdir": {- "RegexDir": "'!d"- },- "dlTag": [- "LatestPrerelease",- "",- ")\u0007?",- "Prerelease",- "Experimental",- "Recommended",- "Prerelease"- ],- "dlUri": "https:ovsa"- },- "( <= 4.2.2 && > 1.3.3 && == 6.5.5 && < 2.3.4 && == 3.3.4 && > 3.5.1 )": {- "dlCSize": -2,- "dlHash": "qxpptqv",- "dlOutput": "",- "dlSubdir": "\u001f",- "dlTag": [- "Latest",- "Experimental",- "base-5.2.2",- "Latest"- ],- "dlUri": "http:ikqhdfs"- },- "( > 2.4.1 ) || ( >= 4.1.3 )": {- "dlCSize": 3,- "dlHash": "dabzowa",- "dlOutput": "3t",- "dlSubdir": "A\r\u000b",- "dlTag": null,- "dlUri": "https:uod"- },- "unknown_versioning": {- "dlCSize": -4,- "dlHash": "r",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "vnS\u0003"- },- "dlTag": null,- "dlUri": "https:mhj"- }- },- "Windows": {- "( <= 3.4.1 )": {- "dlCSize": 4,- "dlHash": "qjqupa",- "dlOutput": "x🮠\u0012",- "dlSubdir": {- "RegexDir": "^w\u001c"- },- "dlTag": null,- "dlUri": "http:woywo"- },- "( <= 3.5.2 && <= 1.5.4 && >= 4.4.5 && < 5.1.2 && >= 5.2.1 ) || ( >= 2.2.2 && < 4.3.2 && >= 4.4.3 && == 4.2.3 ) || ( <= 2.2.3 && >= 3.3.2 )": {- "dlCSize": 0,- "dlHash": "shz",- "dlOutput": "裗",- "dlSubdir": "SZ𗕐",- "dlTag": [- "Recommended",- "base-6.4.6"- ],- "dlUri": "https:pkbw"- },- "( == 1.2.3 && == 5.3.5 && < 1.1.1 ) || ( == 1.2.2 )": {- "dlCSize": -7,- "dlHash": "bb",- "dlOutput": null,- "dlSubdir": "\u0018\u0019",- "dlTag": null,- "dlUri": "http:jnsmrl"- },- "( == 2.1.5 && > 4.5.1 && < 1.5.2 && >= 2.4.4 && > 3.1.4 )": {- "dlCSize": -1,- "dlHash": "jnlhedo",- "dlOutput": "\u0005r",- "dlSubdir": "C\u000cD𱒯",- "dlTag": [- "LatestPrerelease",- "Nightly",- "LatestPrerelease",- "Prerelease",- "\u0014",- "old",- "LatestNightly"- ],- "dlUri": "https:huxmfo"- },- "( > 3.6.5 && > 1.3.2 && < 4.1.5 && <= 1.3.5 && >= 4.4.5 ) || ( == 2.1.5 && <= 1.5.1 && <= 3.2.2 )": {- "dlCSize": null,- "dlHash": "utt",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "Q"- },- "dlTag": [- "Recommended"- ],- "dlUri": "http:"- },- "unknown_versioning": {- "dlCSize": 1,- "dlHash": "",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "d\u0005A\u001c"- },- "dlTag": [- "Nightly",- "Recommended",- "Latest",- "LatestNightly",- "Prerelease"- ],- "dlUri": "http:"- }- }- }- },- "viChangeLog": null,- "viPostInstall": "gce",- "viPostRemove": "",- "viPreCompile": "fokigel",- "viPreInstall": "qda",- "viReleaseDay": "1041212727146945-04-15",- "viSourceDL": null,- "viTags": [- "Prerelease",- "Latest",- "LatestPrerelease",- "Prerelease"- ],- "viTestDL": {- "dlCSize": -5,- "dlHash": "jm",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "\r\u0005𡈆"- },- "dlTag": null,- "dlUri": "http:keoany"- }- },- "8.5.3": {- "viArch": {- "A_32": {- "Darwin": {- "( <= 5.1.1 && <= 4.3.1 && > 4.3.5 && == 2.5.2 && == 4.5.5 && > 5.2.2 ) || ( <= 2.4.4 )": {- "dlCSize": 1,- "dlHash": "knoi",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Latest",- "LatestPrerelease",- "Prerelease",- "LatestPrerelease",- "Nightly"- ],- "dlUri": "http:gyxbzls"- },- "( == 2.2.5 && >= 4.5.5 && == 2.2.1 && > 5.2.4 && == 4.4.5 && >= 5.2.2 ) || ( < 1.4.4 && == 3.3.4 && >= 3.2.4 && == 4.2.1 && <= 4.3.1 ) || ( == 1.1.1 && <= 2.3.1 && >= 2.2.2 && >= 1.1.1 )": {- "dlCSize": 6,- "dlHash": "pt",- "dlOutput": "𬒨@v\u0015",- "dlSubdir": null,- "dlTag": [],- "dlUri": "https:dxxb"- },- "( > 3.1.4 && < 3.1.3 && == 5.2.4 && >= 5.5.5 && >= 1.3.1 && <= 3.3.5 && < 3.5.4 )": {- "dlCSize": -1,- "dlHash": "ycuhy",- "dlOutput": "O\u000f\u0007",- "dlSubdir": {- "RegexDir": ">d"- },- "dlTag": [- "Latest",- "old",- "Nightly",- "Nightly",- "LatestPrerelease"- ],- "dlUri": "https:m"- },- "( >= 3.1.1 && < 3.1.1 && <= 5.2.1 )": {- "dlCSize": -7,- "dlHash": "",- "dlOutput": "A𥫌\u000b",- "dlSubdir": {- "RegexDir": "kcy)\u000b"- },- "dlTag": [- "Nightly",- "base-1.6.4",- "Nightly",- "Latest"- ],- "dlUri": "https:n"- },- "( >= 3.2.3 )": {- "dlCSize": null,- "dlHash": "zqjcs",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "儉i𘋣9"- },- "dlTag": [- "𭔿𫻦𰫪"- ],- "dlUri": "https:ll"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "vc",- "dlOutput": null,- "dlSubdir": "9[~𭏪5",- "dlTag": [],- "dlUri": "https:mqr"- }- },- "Linux_AmazonLinux": {- "( == 5.3.2 )": {- "dlCSize": null,- "dlHash": "yy",- "dlOutput": "n#\"G\u0011N",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": null,- "dlUri": "http:"- },- "( >= 5.4.2 && == 5.3.6 && == 3.4.3 && == 1.2.2 )": {- "dlCSize": -1,- "dlHash": "cgkyu",- "dlOutput": "",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [],- "dlUri": "https:tbarp"- }- },- "Linux_Rocky": {- "unknown_versioning": {- "dlCSize": 6,- "dlHash": "gvuhib",- "dlOutput": "rX",- "dlSubdir": null,- "dlTag": [- "Experimental",- "Experimental",- "LatestPrerelease",- "LatestNightly",- "Latest",- "old",- "Latest"- ],- "dlUri": "https:clr"- }- },- "Windows": {- "( <= 4.4.3 && <= 1.4.5 && > 5.3.4 && < 4.4.3 ) || ( > 2.4.2 && <= 2.1.3 ) || ( >= 2.2.4 && >= 3.3.1 && >= 2.2.3 && >= 3.2.2 ) || ( == 1.1.1 && == 1.2.2 && > 3.1.1 && >= 1.1.2 ) || ( <= 1.2.1 && > 1.1.2 && <= 1.1.1 ) || ( <= 1.1.2 && > 1.1.1 ) || ( == 1.1.1 )": {- "dlCSize": 2,- "dlHash": "sfmc",- "dlOutput": null,- "dlSubdir": "%,m",- "dlTag": [- "Recommended",- "LatestNightly",- "Prerelease",- "",- "Latest",- "Recommended"- ],- "dlUri": "http:zulvo"- },- "( == 1.5.6 && > 4.3.2 ) || ( <= 1.1.1 && >= 2.4.3 && >= 1.2.1 && >= 4.4.3 && < 4.3.4 )": {- "dlCSize": -6,- "dlHash": "ysz",- "dlOutput": "\u001da ^\u0001,\u0006",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "Recommended"- ],- "dlUri": "https:hvjrrbb"- },- "( > 4.6.5 && <= 3.3.5 ) || ( <= 4.1.2 && > 3.3.5 && == 2.4.1 && <= 4.2.1 && > 3.2.4 ) || ( == 1.3.3 && < 2.3.3 && > 1.2.2 && > 1.1.3 ) || ( < 1.2.1 && > 1.1.2 )": {- "dlCSize": null,- "dlHash": "wcukj",- "dlOutput": "|+s\u0006|",- "dlSubdir": "𠒉BT",- "dlTag": [],- "dlUri": "http:zbyoega"- },- "( >= 5.4.5 )": {- "dlCSize": 5,- "dlHash": "",- "dlOutput": "@T):-;",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "LatestNightly",- "old",- "old",- "Latest",- "Latest"- ],- "dlUri": "https:dbkp"- }- }- },- "A_ARM64": {- "FreeBSD": {- "( < 5.2.2 ) || ( >= 2.3.1 && < 4.3.3 && > 3.3.2 && < 4.1.1 && >= 2.4.4 && == 1.1.3 ) || ( <= 1.1.4 && > 1.3.2 && < 1.1.3 && <= 1.2.3 && >= 1.3.1 ) || ( >= 1.3.2 && > 1.1.1 && <= 2.1.2 && == 1.1.1 )": {- "dlCSize": -5,- "dlHash": "krzrt",- "dlOutput": "𥵹i𡧳iG",- "dlSubdir": "\u0017)|𭧬b",- "dlTag": [- "Nightly",- "Experimental"- ],- "dlUri": "https:"- },- "( <= 4.3.2 && <= 5.4.1 && < 1.2.1 && <= 2.4.2 && >= 3.1.4 )": {- "dlCSize": 4,- "dlHash": "jwuw",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "@<x"- },- "dlTag": [- "Latest",- "old",- "Experimental"- ],- "dlUri": "http:r"- },- "( <= 5.1.5 && < 4.5.1 && < 2.5.3 && <= 5.3.5 ) || ( >= 4.4.4 && > 4.2.5 && >= 3.3.2 )": {- "dlCSize": null,- "dlHash": "bxek",- "dlOutput": "",- "dlSubdir": "",- "dlTag": null,- "dlUri": "http:tiiv"- },- "( > 3.5.2 )": {- "dlCSize": 1,- "dlHash": "yxby",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "\u001dR %"- },- "dlTag": null,- "dlUri": "http:mkio"- },- "( >= 1.2.4 && == 1.5.2 && > 4.5.2 && <= 2.3.2 )": {- "dlCSize": 3,- "dlHash": "mib",- "dlOutput": "𰲘K\u0012-\\",- "dlSubdir": {- "RegexDir": "M𔕱GU"- },- "dlTag": [- "LatestNightly",- "Recommended",- "Prerelease",- "old",- "LatestNightly",- "Prerelease"- ],- "dlUri": "https:szuqi"- }- },- "Linux_Exherbo": {- "( < 3.5.2 && <= 3.1.4 && > 1.1.3 && > 5.2.1 && == 4.5.1 )": {- "dlCSize": 2,- "dlHash": "vocuot",- "dlOutput": null,- "dlSubdir": null,- "dlTag": null,- "dlUri": "https:ncy"- },- "( < 4.5.3 && < 1.2.1 && >= 1.4.3 && < 1.1.6 && < 1.1.5 )": {- "dlCSize": 4,- "dlHash": "mcbypxg",- "dlOutput": "6e3T\u000b\u0014",- "dlSubdir": null,- "dlTag": [- "base-6.5.6",- "VP\\o",- "LatestPrerelease",- "Latest",- "old",- "Latest",- "base-4.3.3"- ],- "dlUri": "https:cnvgxq"- },- "( < 5.4.3 && <= 4.1.1 && >= 2.5.3 ) || ( == 1.1.4 && <= 2.4.3 && <= 4.1.2 )": {- "dlCSize": -4,- "dlHash": "oed",- "dlOutput": "\tL",- "dlSubdir": {- "RegexDir": "8v7"- },- "dlTag": [- "Experimental",- "Latest",- "Nightly",- "old",- "LatestNightly",- "Recommended"- ],- "dlUri": "http:xcjc"- },- "( <= 2.2.3 && >= 2.3.2 && > 4.2.3 && >= 5.5.3 && >= 5.3.3 && <= 4.5.4 && > 1.5.1 )": {- "dlCSize": -4,- "dlHash": "z",- "dlOutput": "G\u0014\u0003Z27",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": null,- "dlUri": "https:vtkh"- },- "( <= 5.4.1 && > 3.3.1 ) || ( <= 3.4.3 && < 1.2.5 && <= 4.2.4 && == 3.2.2 && >= 3.4.2 )": {- "dlCSize": -6,- "dlHash": "xvwqpc",- "dlOutput": "Td5Wࣞh|",- "dlSubdir": {- "RegexDir": "{('"- },- "dlTag": null,- "dlUri": "http:"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "vheeqz",- "dlOutput": "Op*",- "dlSubdir": {- "RegexDir": "j༫𒔾"- },- "dlTag": [- "Latest",- "Recommended",- "LatestPrerelease",- "",- ""- ],- "dlUri": "https:do"- }- },- "Linux_Rocky": {},- "Windows": {- "( < 2.1.1 && >= 4.2.5 && >= 5.4.1 )": {- "dlCSize": null,- "dlHash": "aktghf",- "dlOutput": "k",- "dlSubdir": "\u0003i\u0017M𬌖멸",- "dlTag": [- "Prerelease",- "Latest",- "Recommended",- "Prerelease",- "old",- "B\u0006K+F"- ],- "dlUri": "http:xhaxda"- },- "( <= 1.4.1 && == 2.5.4 && > 6.4.5 && < 1.2.5 && > 1.3.1 && >= 2.1.2 ) || ( >= 2.2.2 && >= 4.3.4 && < 2.1.3 && > 1.4.3 && < 2.1.3 ) || ( <= 1.4.1 && > 2.2.1 && == 3.1.3 && > 1.3.1 ) || ( >= 2.2.1 )": {- "dlCSize": 7,- "dlHash": "oqa",- "dlOutput": "WG\u0003b\u0003w",- "dlSubdir": {- "RegexDir": "9Wi"- },- "dlTag": [- "Prerelease",- "Recommended",- "a\u001c_"- ],- "dlUri": "https:bgbe"- },- "( <= 2.4.2 && < 4.5.3 && <= 2.3.2 )": {- "dlCSize": null,- "dlHash": "eiyaikw",- "dlOutput": null,- "dlSubdir": "",- "dlTag": null,- "dlUri": "https:zncuow"- },- "( >= 2.5.5 && <= 4.5.3 && <= 5.5.4 )": {- "dlCSize": null,- "dlHash": "porrmf",- "dlOutput": "Q\u0007\\?𑵴;",- "dlSubdir": {- "RegexDir": "𝇦H𬉲"- },- "dlTag": null,- "dlUri": "http:fo"- },- "unknown_versioning": {- "dlCSize": 4,- "dlHash": "ms",- "dlOutput": "%|",- "dlSubdir": {- "RegexDir": "6\u0016 "- },- "dlTag": null,- "dlUri": "https:sai"- }- }- },- "A_PowerPC64": {- "Darwin": {- "( > 5.4.3 && == 3.5.2 && > 2.6.5 && <= 2.2.4 && <= 5.3.4 && >= 1.1.2 )": {- "dlCSize": 6,- "dlHash": "a",- "dlOutput": null,- "dlSubdir": "2",- "dlTag": [- "base-4.2.3",- "Prerelease",- "base-1.5.5",- "Latest",- "LatestPrerelease",- "𠕯]7𡖑",- "LatestPrerelease"- ],- "dlUri": "http:tykzm"- },- "( >= 4.3.4 && > 3.1.2 && > 2.5.1 && >= 3.3.1 && > 3.5.4 && > 2.2.4 )": {- "dlCSize": -3,- "dlHash": "veyvaw",- "dlOutput": "M\u0016A",- "dlSubdir": {- "RegexDir": "H𦷔\tjg"- },- "dlTag": null,- "dlUri": "http:mdojj"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "qxxkys",- "dlOutput": "\u0000h\rb\r#=",- "dlSubdir": {- "RegexDir": "`𣙈5\n𫔊"- },- "dlTag": [- "Nightly",- "Latest",- "Prerelease",- "Recommended",- "Prerelease",- "LatestPrerelease"- ],- "dlUri": "http:bcot"- }- },- "FreeBSD": {- "( == 1.4.1 && < 1.2.1 && > 5.6.1 && <= 1.1.1 && >= 2.5.2 ) || ( == 5.3.3 ) || ( < 3.1.3 && == 1.3.1 )": {- "dlCSize": null,- "dlHash": "pgtz",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "O2\u0011\u001d\u0014"- },- "dlTag": null,- "dlUri": "http:polgsx"- },- "( > 1.5.2 && <= 4.4.4 ) || ( == 3.3.4 && >= 1.3.4 && < 2.4.3 && <= 1.3.3 ) || ( <= 2.3.1 && < 3.1.2 )": {- "dlCSize": -7,- "dlHash": "qqh",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "䞖"- },- "dlTag": [- "LatestPrerelease",- "LatestNightly",- "base-4.1.1",- "LatestPrerelease",- "Experimental",- "old"- ],- "dlUri": "https:ptmavu"- }- },- "Linux_Mint": {},- "Linux_Ubuntu": {- "( <= 1.1.4 ) || ( > 4.3.4 && > 4.3.2 && <= 1.2.3 && <= 3.2.1 ) || ( == 1.1.1 && > 1.2.3 && <= 3.1.2 && >= 2.4.1 ) || ( <= 2.3.2 && > 1.2.1 && > 1.2.1 ) || ( < 1.1.2 ) || ( >= 1.2.1 && <= 1.1.2 )": {- "dlCSize": -6,- "dlHash": "",- "dlOutput": "sWR先rK",- "dlSubdir": {- "RegexDir": "4\u0016<"- },- "dlTag": [- "LatestPrerelease",- "i"- ],- "dlUri": "http:"- }- },- "Linux_Void": {- "( < 2.2.5 && == 1.5.4 && >= 2.2.3 && > 3.5.4 && > 1.1.4 && >= 2.2.3 )": {- "dlCSize": -2,- "dlHash": "d",- "dlOutput": "(\u0010㼍G",- "dlSubdir": "6#`\u000e\u0013",- "dlTag": [- "Nightly",- "Nightly",- "LatestNightly",- "Prerelease",- "Recommended",- "Recommended"- ],- "dlUri": "http:jitcgfy"- },- "( <= 1.5.5 && < 3.5.3 && > 4.5.3 && > 5.3.5 && < 5.5.4 && > 5.2.2 ) || ( >= 4.2.2 && < 3.1.2 && > 1.4.1 && == 1.3.2 )": {- "dlCSize": 4,- "dlHash": "frzkdku",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "'xu'\n"- },- "dlTag": [- "Experimental",- "z𮇴#N"- ],- "dlUri": "https:evsgza"- },- "( == 4.1.1 && < 3.4.4 && <= 2.4.4 ) || ( < 4.1.1 && == 1.4.3 && >= 1.1.1 ) || ( == 2.3.1 && <= 1.2.1 && > 1.1.3 && == 1.3.3 )": {- "dlCSize": 4,- "dlHash": "",- "dlOutput": "𒉭𪼙\u0018sZꯡg",- "dlSubdir": {- "RegexDir": "i\u0019sc7"- },- "dlTag": [- "Prerelease",- "Prerelease",- "Prerelease"- ],- "dlUri": "http:adw"- },- "( > 2.5.1 ) || ( == 2.2.1 && >= 3.1.4 ) || ( < 3.1.3 )": {- "dlCSize": -1,- "dlHash": "ngdql",- "dlOutput": null,- "dlSubdir": "",- "dlTag": null,- "dlUri": "https:fwwdlec"- },- "( > 4.5.2 && >= 1.5.1 && > 5.2.1 && > 4.1.4 && > 4.2.5 && == 3.4.5 )": {- "dlCSize": 0,- "dlHash": "ajrbip",- "dlOutput": "",- "dlSubdir": null,- "dlTag": [- "Latest",- "Experimental",- "base-3.1.6",- "base-1.6.2",- "Experimental",- "Latest",- "LatestPrerelease"- ],- "dlUri": "https:us"- },- "( > 5.4.3 && >= 1.2.3 && == 2.4.4 && < 3.1.1 ) || ( >= 2.1.4 ) || ( > 2.2.3 && < 2.3.2 && == 3.3.3 && < 2.5.2 && <= 3.1.2 ) || ( <= 1.2.1 && > 2.2.1 && > 2.1.2 && >= 2.1.2 )": {- "dlCSize": -7,- "dlHash": "qndhxzg",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "base-6.1.1",- "Experimental",- "Recommended"- ],- "dlUri": "http:qtm"- },- "( >= 4.3.3 && < 3.3.4 && > 2.1.3 && < 4.1.4 && > 1.1.2 && > 3.3.5 )": {- "dlCSize": null,- "dlHash": "g",- "dlOutput": "\u0003#{𪲛d6",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "LatestPrerelease"- ],- "dlUri": "https:wlsutzk"- }- },- "Windows": {- "( < 3.4.3 && < 3.4.2 && < 1.4.4 && >= 5.5.3 )": {- "dlCSize": null,- "dlHash": "",- "dlOutput": null,- "dlSubdir": "",- "dlTag": null,- "dlUri": "https:tc"- },- "( <= 5.5.4 && < 4.4.1 && < 1.1.3 && >= 4.2.4 && <= 6.5.4 )": {- "dlCSize": -7,- "dlHash": "ejvhn",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Latest",- "LatestNightly",- "Nightly"- ],- "dlUri": "http:bl"- },- "( == 3.3.4 ) || ( < 2.1.4 && == 3.3.4 && < 1.3.3 && < 3.1.3 ) || ( < 1.2.1 && > 2.1.2 && == 1.1.1 && <= 3.2.2 && == 1.3.2 )": {- "dlCSize": -7,- "dlHash": "wutxg",- "dlOutput": "\n",- "dlSubdir": null,- "dlTag": [- "Experimental",- "LatestNightly",- "LatestNightly",- "Experimental",- "𓇶\u000e",- "Recommended",- "Latest"- ],- "dlUri": "https:pncfaik"- },- "( >= 2.4.4 && > 1.4.4 && <= 5.5.4 && == 3.4.4 ) || ( == 4.4.2 && <= 4.3.2 && > 1.4.4 && >= 2.4.1 && == 3.1.4 && <= 1.4.4 ) || ( <= 1.1.2 )": {- "dlCSize": 7,- "dlHash": "d",- "dlOutput": "?iKg",- "dlSubdir": {- "RegexDir": "\u0005"- },- "dlTag": null,- "dlUri": "http:rcpl"- },- "( >= 2.5.5 && == 3.3.3 && > 2.5.2 )": {- "dlCSize": 2,- "dlHash": "pokyjmt",- "dlOutput": null,- "dlSubdir": "+e驪ncu",- "dlTag": null,- "dlUri": "http:orndago"- },- "unknown_versioning": {- "dlCSize": 7,- "dlHash": "aoejxxh",- "dlOutput": "",- "dlSubdir": "",- "dlTag": [- "Latest",- "LatestNightly",- "Latest",- "base-2.6.5",- "LatestPrerelease",- "LatestPrerelease",- "\u0016h#\u0004"- ],- "dlUri": "http:egno"- }- }- },- "A_Sparc": {- "Darwin": {- "( < 3.2.4 )": {- "dlCSize": null,- "dlHash": "vdmtw",- "dlOutput": "O𑻯𐽿i(6",- "dlSubdir": "r^",- "dlTag": [- "Latest",- "base-5.2.3",- "Experimental",- "LatestNightly"- ],- "dlUri": "https:c"- },- "( < 5.2.2 && == 3.2.4 && <= 5.4.3 && < 4.2.4 && == 3.4.2 && > 1.1.5 )": {- "dlCSize": 0,- "dlHash": "i",- "dlOutput": "M.B\\",- "dlSubdir": {- "RegexDir": "𤝜𧟕\u000c\u0011"- },- "dlTag": [],- "dlUri": "http:dmdr"- },- "( <= 2.2.3 && > 4.3.3 )": {- "dlCSize": -3,- "dlHash": "jl",- "dlOutput": "",- "dlSubdir": "|\u0015X>0",- "dlTag": null,- "dlUri": "https:oemfq"- },- "( <= 4.4.3 && == 2.1.1 && == 1.1.2 && > 5.1.2 )": {- "dlCSize": null,- "dlHash": "ihzncbv",- "dlOutput": "𢉈2eM",- "dlSubdir": "\u000b",- "dlTag": [- "Prerelease"- ],- "dlUri": "https:cpdba"- },- "( <= 5.3.2 && == 1.2.3 && < 3.3.5 ) || ( <= 2.5.4 && < 4.4.2 && > 3.3.4 && < 4.4.3 && <= 3.3.3 && == 3.2.1 )": {- "dlCSize": -3,- "dlHash": "wpdnjb",- "dlOutput": "",- "dlSubdir": null,- "dlTag": [],- "dlUri": "https:lwz"- },- "( > 1.4.5 )": {- "dlCSize": 7,- "dlHash": "hqsiy",- "dlOutput": "y\\F犮",- "dlSubdir": "F\u0014`",- "dlTag": [],- "dlUri": "https:"- },- "( > 2.5.3 && < 4.5.2 && < 2.2.2 ) || ( >= 3.2.5 && > 5.1.3 && < 4.3.4 && > 1.1.3 )": {- "dlCSize": 4,- "dlHash": "d",- "dlOutput": null,- "dlSubdir": "F;\"",- "dlTag": null,- "dlUri": "https:f"- }- },- "FreeBSD": {- "( > 5.2.3 ) || ( == 3.3.3 && > 4.4.2 && > 1.4.1 && >= 4.3.1 && <= 4.1.1 )": {- "dlCSize": null,- "dlHash": "skb",- "dlOutput": null,- "dlSubdir": null,- "dlTag": [- "Nightly",- "base-2.5.3"- ],- "dlUri": "http:azhh"- },- "unknown_versioning": {- "dlCSize": 1,- "dlHash": "vtonjw",- "dlOutput": "",- "dlSubdir": "^",- "dlTag": [- "old"- ],- "dlUri": "https:lduyxm"- }- },- "Linux_Mint": {- "( <= 2.2.2 && <= 3.3.4 && >= 5.4.3 && >= 2.5.2 && > 4.2.4 && < 3.1.2 && >= 3.4.1 )": {- "dlCSize": -7,- "dlHash": "fj",- "dlOutput": "\u0004V𦐹/",- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "LatestPrerelease",- "Nightly"- ],- "dlUri": "https:ergibm"- },- "( == 2.2.5 && < 3.4.2 && > 3.5.3 ) || ( == 3.3.4 && > 1.3.1 && >= 2.2.1 && <= 3.2.2 && >= 4.1.2 )": {- "dlCSize": 2,- "dlHash": "b",- "dlOutput": "?",- "dlSubdir": null,- "dlTag": [- "LatestPrerelease",- ""- ],- "dlUri": "https:uyvaxil"- },- "( == 2.5.5 && <= 1.3.5 && >= 2.4.3 && == 1.5.3 && <= 3.3.1 && >= 4.1.3 ) || ( == 2.2.4 && <= 4.3.1 && == 4.3.3 && >= 1.4.4 ) || ( <= 3.3.3 && >= 3.1.2 && >= 3.1.1 && > 3.1.3 && <= 2.3.2 ) || ( <= 3.1.1 && >= 2.2.1 ) || ( < 2.1.2 )": {- "dlCSize": -7,- "dlHash": "am",- "dlOutput": "\t)w",- "dlSubdir": {- "RegexDir": "\u000e"- },- "dlTag": [- "Prerelease"- ],- "dlUri": "http:jompdr"- },- "( == 5.2.5 && < 1.2.2 ) || ( > 1.3.2 && <= 1.3.2 && < 1.4.1 ) || ( <= 2.3.3 ) || ( <= 2.2.2 && <= 1.4.2 && >= 2.1.1 && > 3.2.2 )": {- "dlCSize": -4,- "dlHash": "u",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "\u0006wM\u0018"- },- "dlTag": [- "Nightly",- "old",- "old",- "Latest"- ],- "dlUri": "https:ihmzk"- },- "( > 4.5.5 && < 5.1.4 && >= 4.4.1 && == 2.5.5 )": {- "dlCSize": null,- "dlHash": "vluz",- "dlOutput": "\u001dEd14",- "dlSubdir": null,- "dlTag": [- "Experimental",- "Nightly"- ],- "dlUri": "http:ybvls"- },- "( >= 2.4.5 && < 2.4.5 )": {- "dlCSize": null,- "dlHash": "vv",- "dlOutput": "",- "dlSubdir": {- "RegexDir": "!K7"- },- "dlTag": [- "Recommended",- "Latest",- "Prerelease",- "Experimental",- "Prerelease",- "LatestNightly"- ],- "dlUri": "https:qqeii"- },- "( >= 5.4.3 && > 3.2.4 && >= 5.3.2 ) || ( >= 3.3.1 && > 1.4.4 && == 4.3.3 && <= 2.1.1 && < 3.4.1 ) || ( < 2.2.1 && < 3.1.3 && <= 1.3.2 && < 3.3.3 && <= 1.2.1 )": {- "dlCSize": -4,- "dlHash": "xx",- "dlOutput": "BmG",- "dlSubdir": {- "RegexDir": "`쐭%"- },- "dlTag": null,- "dlUri": "http:dm"- },- "unknown_versioning": {- "dlCSize": null,- "dlHash": "k",- "dlOutput": "𡲘",- "dlSubdir": {- "RegexDir": "\u0003O𮞊yN"- },- "dlTag": [- "base-6.2.3",- "Nightly",- "old",- "old",- "Recommended",- "Experimental"- ],- "dlUri": "http:frteyk"- }- },- "Linux_UnknownLinux": {- "( < 2.1.5 && > 4.3.5 && >= 4.3.3 && >= 2.5.3 && > 2.5.1 && > 3.2.3 && >= 4.2.1 )": {- "dlCSize": -6,- "dlHash": "jioim",- "dlOutput": "",- "dlSubdir": null,- "dlTag": [- "base-6.5.2",- "Latest"- ],- "dlUri": "http:br"- },- "( <= 2.2.1 ) || ( <= 1.3.4 )": {- "dlCSize": null,- "dlHash": "rbm",- "dlOutput": null,- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [],- "dlUri": "https:prnsi"- },- "( <= 2.3.5 && <= 2.2.5 && == 2.3.2 && >= 3.3.3 && == 3.4.2 ) || ( <= 4.3.4 && == 4.4.4 )": {- "dlCSize": -2,- "dlHash": "hhgpqko",- "dlOutput": null,- "dlSubdir": "/\\",- "dlTag": [- "old"- ],- "dlUri": "http:imq"- },- "( == 2.4.5 ) || ( > 4.2.1 && < 2.2.1 ) || ( > 1.3.4 && < 2.1.3 && > 4.3.2 && <= 3.2.2 && >= 3.3.2 )": {- "dlCSize": -1,- "dlHash": "r",- "dlOutput": "A𨭐a󠅦",- "dlSubdir": {- "RegexDir": "𢧋\u0005B\nh"- },- "dlTag": [- "LatestPrerelease",- "j[",- "LatestNightly"- ],- "dlUri": "https:dapuity"- },- "( >= 1.3.1 && >= 1.2.4 )": {- "dlCSize": 6,- "dlHash": "wtvy",- "dlOutput": null,- "dlSubdir": null,- "dlTag": null,- "dlUri": "http:u"- },- "( >= 2.4.1 && >= 1.5.3 && <= 1.5.4 && > 3.3.4 && <= 2.5.4 )": {- "dlCSize": null,- "dlHash": "lvjr",- "dlOutput": null,- "dlSubdir": {- "RegexDir": "<;"- },- "dlTag": [- "LatestPrerelease",- "LatestPrerelease",- "LatestNightly",- "Nightly",- "base-2.2.1",- "",- "*Jg𡜳\u0019"- ],- "dlUri": "https:xwtxrk"- },- "unknown_versioning": {- "dlCSize": 5,- "dlHash": "z",- "dlOutput": null,- "dlSubdir": {- "RegexDir": ""- },- "dlTag": [- "Prerelease",- "Prerelease",- "old",- "Nightly",- "LatestNightly",- "Nightly"- ],- "dlUri": "http:"- }- },- "Windows": {- "( == 1.3.1 && <= 5.5.1 && >= 1.1.4 && < 2.5.4 && <= 4.1.1 && <= 5.5.1 && == 5.3.1 )": {- "dlCSize": null,- "dlHash": "fkdtts",- "dlOutput": "\u000f\u0006XB",- "dlSubdir": {- "RegexDir": "<\u0003q\u0008\u0002"- },- "dlTag": null,- "dlUri": "http:pwniy"- },- "( > 2.4.2 && > 2.4.5 && < 4.1.4 && <= 1.3.1 && > 1.4.1 ) || ( > 3.2.3 && >= 2.4.1 && > 4.3.2 && > 3.3.5 && > 3.4.1 && < 3.3.3 ) || ( >= 1.2.3 && > 3.2.2 && == 3.1.2 && > 1.1.1 )": {- "dlCSize": 3,- "dlHash": "",- "dlOutput": "",- "dlSubdir": null,- "dlTag": [- "base-4.3.1"- ],- "dlUri": "http:axrww"- },- "unknown_versioning": {- "dlCSize": -2,- "dlHash": "vbijgk",- "dlOutput": "\u001d祐F1𗪗PT",- "dlSubdir": "\u00133\u0016k[~",- "dlTag": null,- "dlUri": "http:ycnh"- }- }- }- },- "viChangeLog": "https:jvo",- "viPostInstall": "zdmmgqv",- "viPostRemove": null,- "viPreCompile": "ws",- "viPreInstall": "ukoevy",- "viReleaseDay": "3938621829868156-10-26",- "viSourceDL": {- "dlCSize": -3,- "dlHash": "ie",- "dlOutput": "\u001e;g]\ra",- "dlSubdir": {- "RegexDir": "q𤀒B"- },- "dlTag": null,- "dlUri": "http:fnzea"- },- "viTags": [- "base-2.4.6",- "old",- "base-4.3.5"- ],- "viTestDL": {- "dlCSize": -5,- "dlHash": "abl",- "dlOutput": "\u0019)",- "dlSubdir": null,- "dlTag": [- "Recommended",- "Latest",- "LatestPrerelease",- "Prerelease",- "Nightly",- "old"- ],- "dlUri": "http:bp"- }- }- }- },- "metadataUpdate": "https:",- "toolRequirements": {- "Cabal": {- "1.2.4": {- "FreeBSD": {- "( <= 4.1.5 && == 1.2.5 && <= 2.4.4 && > 2.1.5 ) || ( >= 3.2.4 )": {- "distroPKGs": [- "xzkibnx"- ],- "notes": "m"- },- "( == 2.2.4 && >= 1.2.5 && >= 3.5.4 && > 4.4.3 && <= 1.4.2 && == 4.5.4 && > 4.4.5 ) || ( > 4.4.3 ) || ( <= 3.2.1 && == 4.3.1 && >= 2.2.2 && < 1.1.3 ) || ( == 2.2.1 && < 2.3.2 && <= 1.1.1 ) || ( > 1.1.1 && <= 1.1.2 )": {- "distroPKGs": [- "b",- "",- "o",- "p",- "ucrr",- "uq",- "nboecb"- ],- "notes": "ty"- },- "( == 3.2.1 && >= 5.3.3 && <= 4.4.2 && == 1.1.3 && == 1.3.5 ) || ( >= 1.2.3 && > 4.1.4 && < 5.2.1 && < 1.4.3 && <= 2.3.3 && <= 4.1.1 )": {- "distroPKGs": [- "tzjiwy",- "aodupc",- "",- "jffq"- ],- "notes": "gfupb"- },- "unknown_versioning": {- "distroPKGs": [- "",- "zy",- "pbol",- "gwfj",- "j"- ],- "notes": "prdes"- }- }- },- "1.5.1": {},- "1.6.4": {- "Darwin": {- "( <= 2.5.1 && >= 1.5.1 && >= 3.1.4 && == 3.2.6 && > 4.3.2 && == 2.3.2 )": {- "distroPKGs": [],- "notes": "q"- },- "( == 5.2.4 ) || ( < 3.4.4 && > 4.3.2 && < 3.4.1 ) || ( > 1.3.3 ) || ( < 1.1.2 && <= 2.2.1 && > 1.2.1 ) || ( == 1.1.2 && < 1.2.1 && > 1.1.1 )": {- "distroPKGs": [- "mkzmajq",- "enyyzyl",- "qsvpg",- ""- ],- "notes": "mhfomak"- },- "( > 4.3.4 )": {- "distroPKGs": [- "lseqj",- "fnx",- "bdnjz",- "wqxp",- "ybictp",- "w",- "awi"- ],- "notes": "undim"- },- "unknown_versioning": {- "distroPKGs": [- "yi",- "umlwl",- "",- "",- "rf",- "drzem",- "k"- ],- "notes": "ppzga"- }- },- "Linux_Alpine": {- "( == 3.5.5 && >= 2.1.5 && == 5.4.3 )": {- "distroPKGs": [],- "notes": "jbs"- },- "( > 2.1.5 && <= 3.5.5 && <= 5.2.5 && >= 5.2.3 && < 3.4.2 && >= 2.1.3 )": {- "distroPKGs": [],- "notes": "nnedp"- },- "( >= 1.4.3 && <= 4.2.4 && <= 1.3.4 && == 3.2.2 && > 2.2.3 ) || ( >= 4.2.1 && <= 1.1.3 && > 4.1.2 && == 2.3.4 && < 2.4.4 && == 1.4.1 ) || ( == 2.1.2 && == 2.1.1 && == 1.1.3 && == 3.4.2 && >= 3.2.1 ) || ( > 2.2.2 && > 2.1.2 ) || ( >= 1.1.2 ) || ( == 2.1.2 && < 1.1.1 ) || ( < 2.2.1 )": {- "distroPKGs": [- "shku",- "tdjojbs",- "",- "iibq",- "rwnhpqz",- "rph"- ],- "notes": "hlz"- },- "unknown_versioning": {- "distroPKGs": [- "lkrdee",- ""- ],- "notes": "fps"- }- },- "Windows": {- "( < 1.2.2 && <= 1.4.2 && > 4.4.3 && == 4.1.2 && >= 1.1.3 && < 4.5.4 ) || ( > 3.4.2 && > 2.1.1 && > 1.3.2 )": {- "distroPKGs": [],- "notes": "ohnkoz"- },- "( == 1.1.1 ) || ( > 4.2.2 )": {- "distroPKGs": [- "mpkf",- "bom",- "ntoikd",- "q",- "co",- "xuux",- "txwij"- ],- "notes": "gt"- },- "( == 4.4.3 ) || ( >= 3.3.3 && >= 1.4.3 && < 1.2.1 && >= 1.1.3 && == 1.4.1 && > 3.2.2 )": {- "distroPKGs": [- "kuqpubm",- "to"- ],- "notes": "etdwdl"- },- "( == 4.5.1 ) || ( > 1.1.1 && <= 3.1.1 && < 4.4.2 && > 2.4.1 && <= 4.2.1 && <= 2.4.4 )": {- "distroPKGs": [- "aonsj",- "qit",- "ppaie"- ],- "notes": "di"- },- "( > 1.2.1 && < 4.4.4 && < 1.1.1 && >= 2.1.1 && <= 3.3.1 && == 5.5.1 && < 4.4.4 )": {- "distroPKGs": [],- "notes": "hmlk"- },- "( > 4.1.5 && >= 4.5.3 )": {- "distroPKGs": [- "gg",- "tdsxhi",- "anzukv",- "mxxvk",- "peutsaw",- "sd"- ],- "notes": "bc"- },- "( >= 1.1.2 && < 3.1.1 && <= 5.5.4 && > 1.3.2 && == 4.2.3 && == 5.1.5 && < 3.4.4 )": {- "distroPKGs": [- "suvz",- "a",- "cm"- ],- "notes": ""- },- "unknown_versioning": {- "distroPKGs": [- "",- "zceo",- "grnalj",- "igrlf"- ],- "notes": "ypzv"- }- }- },- "4.3.5": {- "Darwin": {- "( >= 1.5.4 && > 2.4.5 && <= 4.3.2 && > 1.3.5 && == 5.2.2 && <= 1.5.4 ) || ( == 4.4.1 && < 2.1.2 ) || ( > 2.1.1 && >= 3.3.2 && <= 3.1.3 && <= 3.1.2 && == 3.2.3 )": {- "distroPKGs": [- "",- "t",- "gr",- "",- "x",- "lj",- "cq"- ],- "notes": "btsw"- },- "unknown_versioning": {- "distroPKGs": [- "jciqb"- ],- "notes": "p"- }- },- "Windows": {}- },- "5.8.7": {},- "6.6.3": {- "Darwin": {- "( < 3.2.1 && < 3.3.5 && == 4.2.5 && <= 2.2.4 && <= 1.3.3 ) || ( == 3.2.3 && < 3.2.1 && > 3.1.3 && > 3.2.2 && <= 1.2.2 && == 1.4.2 ) || ( >= 2.3.2 && <= 2.1.3 ) || ( > 1.2.2 && >= 3.1.2 && <= 2.1.2 )": {- "distroPKGs": [- "t",- "fiunji"- ],- "notes": "iyh"- },- "( < 5.4.1 && > 2.1.1 ) || ( >= 3.3.2 && > 2.3.2 && == 2.1.4 && > 3.4.3 && == 1.3.4 && < 3.3.3 )": {- "distroPKGs": [- "",- "dqj",- "kcgiy"- ],- "notes": "wgnn"- },- "( == 1.1.4 && == 3.4.3 )": {- "distroPKGs": [- "qcxal",- "gss",- "sqs",- "zajv"- ],- "notes": "vf"- },- "( == 4.4.1 && >= 1.4.4 )": {- "distroPKGs": [- "oxma",- "og",- "",- "xqzwod",- "dydy",- "vlyxz"- ],- "notes": "d"- },- "( == 5.4.2 ) || ( < 1.4.3 && == 4.1.1 ) || ( > 2.1.2 )": {- "distroPKGs": [- "z",- "k",- "wi"- ],- "notes": "yzixmsk"- },- "( >= 4.4.2 )": {- "distroPKGs": [],- "notes": "ixgw"- },- "unknown_versioning": {- "distroPKGs": [- "tie",- "epfzlll",- "nmlc",- "gnqy",- "",- ""- ],- "notes": "h"- }- },- "FreeBSD": {},- "Linux_Mint": {- "( < 5.5.4 && >= 3.1.1 && < 5.4.4 && < 3.5.5 ) || ( > 3.3.1 && < 1.4.5 && < 2.4.4 && == 3.2.1 )": {- "distroPKGs": [- "yhtyyrz"- ],- "notes": ""- },- "( > 3.2.2 && > 5.3.2 ) || ( >= 3.2.4 && == 2.1.1 && == 4.6.1 && < 2.4.2 && > 4.4.2 )": {- "distroPKGs": [- "ywvthv",- "ualffo",- "p",- "ivxxrh",- "xig",- "b"- ],- "notes": ""- }- }- },- "unknown_version": {}- }- }- }- ],- "seed": 821709850+ "\u001d;\u0005y": {+ "toolDetails": null,+ "toolVersions": {+ "1.1.1": {+ "changelog": "https:",+ "postInstall": null,+ "postRemove": "",+ "preCompile": "",+ "preInstall": "",+ "releaseDay": "-8791569164896357-04-08",+ "revisionSpec": {},+ "tags": []+ },+ "2.1.1": {+ "changelog": "http:",+ "postInstall": "",+ "postRemove": "",+ "preCompile": "",+ "preInstall": "",+ "releaseDay": null,+ "revisionSpec": {},+ "tags": []+ },+ "3.3.2": {+ "changelog": "http:",+ "postInstall": "",+ "postRemove": "",+ "preCompile": null,+ "preInstall": null,+ "releaseDay": "17435268342960108-04-12",+ "revisionSpec": {},+ "tags": []+ }+ }+ },+ "(l": {+ "toolDetails": {+ "toolAuthor": "",+ "toolContact": "",+ "toolDescription": "",+ "toolHomepage": "http:",+ "toolLicense": "",+ "toolMaintainer": "",+ "toolRepository": "http:"+ },+ "toolVersions": {+ "1.1.3": {+ "changelog": "https:",+ "postInstall": null,+ "postRemove": "",+ "preCompile": "",+ "preInstall": "",+ "releaseDay": "-20399206759176242-07-10",+ "revisionSpec": {},+ "tags": []+ }+ }+ },+ "6": {+ "toolDetails": {+ "toolAuthor": "",+ "toolContact": "",+ "toolDescription": "",+ "toolHomepage": "https:",+ "toolLicense": "",+ "toolMaintainer": "",+ "toolRepository": null+ },+ "toolVersions": {}+ },+ "spo\u0000\u001a": {+ "toolDetails": {+ "toolAuthor": "",+ "toolContact": "",+ "toolDescription": "",+ "toolHomepage": "https:",+ "toolLicense": "",+ "toolMaintainer": "",+ "toolRepository": "https:"+ },+ "toolVersions": {}+ },+ "z\u0001x6\n": {+ "toolDetails": {+ "toolAuthor": "",+ "toolContact": "",+ "toolDescription": "",+ "toolHomepage": "http:",+ "toolLicense": "",+ "toolMaintainer": "",+ "toolRepository": null+ },+ "toolVersions": {}+ }+ },+ "metadataUpdate": "http:sy",+ "toolRequirements": {+ ",\u0013\u0000y": {},+ "6<": {+ "4.4.5": {+ "Darwin": {+ "( < 1.1.1 ) || ( == 1.2.1 && <= 1.1.1 )": {+ "distroPKGs": [+ "qb",+ "",+ "cjm",+ "xen"+ ],+ "notes": ""+ },+ "( < 1.2.1 && < 2.2.2 && == 2.1.1 && > 1.1.3 )": {+ "distroPKGs": [+ "ikg",+ "q"+ ],+ "notes": "ub"+ },+ "( < 2.2.2 )": {+ "distroPKGs": [+ "y",+ ""+ ],+ "notes": ""+ },+ "( <= 2.1.1 && < 1.1.1 && == 1.2.1 && >= 2.2.2 )": {+ "distroPKGs": [+ "ctnp",+ "r"+ ],+ "notes": "vp"+ },+ "( == 1.1.1 ) || ( > 1.1.2 ) || ( == 2.1.1 )": {+ "distroPKGs": [+ "r",+ ""+ ],+ "notes": ""+ },+ "unknown_versioning": {+ "distroPKGs": [+ "eg",+ "c",+ ""+ ],+ "notes": ""+ }+ },+ "FreeBSD": {+ "( <= 1.1.1 )": {+ "distroPKGs": [+ "ynjo"+ ],+ "notes": ""+ },+ "( == 1.1.1 ) || ( <= 1.1.1 ) || ( <= 1.2.1 ) || ( < 2.1.2 )": {+ "distroPKGs": [+ "lcaq"+ ],+ "notes": "uzmt"+ },+ "( >= 1.2.1 && <= 1.2.2 && < 1.1.1 ) || ( == 1.2.1 )": {+ "distroPKGs": [+ "azs",+ "zt"+ ],+ "notes": "zwtf"+ },+ "( >= 1.2.1 && >= 1.2.1 ) || ( == 1.2.1 && < 1.1.1 )": {+ "distroPKGs": [+ "dzqb",+ "g"+ ],+ "notes": "bp"+ },+ "( >= 1.2.2 && <= 1.2.3 && < 2.2.1 && < 2.1.2 )": {+ "distroPKGs": [+ "h",+ "oc",+ "wf"+ ],+ "notes": "no"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "dlu",+ "",+ "b"+ ],+ "notes": "ypvr"+ }+ },+ "OpenBSD": {+ "( <= 1.1.1 && >= 1.1.2 && < 1.1.1 ) || ( > 1.1.2 ) || ( >= 2.2.1 ) || ( <= 1.1.2 )": {+ "distroPKGs": [+ "pcb",+ "fni",+ ""+ ],+ "notes": "gvwv"+ },+ "( == 1.1.1 ) || ( < 1.1.2 ) || ( <= 1.1.1 )": {+ "distroPKGs": [+ "pxo",+ "o",+ "ar",+ "r"+ ],+ "notes": "wdy"+ },+ "( == 2.2.1 && > 1.1.1 && < 1.1.1 ) || ( == 1.1.2 )": {+ "distroPKGs": [+ "hd"+ ],+ "notes": "on"+ }+ },+ "Windows": {+ "( < 1.1.2 ) || ( >= 1.1.1 && == 1.1.1 ) || ( >= 1.2.1 )": {+ "distroPKGs": [+ "y"+ ],+ "notes": "rcwa"+ },+ "( < 2.1.1 && <= 1.1.1 ) || ( >= 1.2.1 ) || ( <= 1.1.1 ) || ( > 1.1.1 )": {+ "distroPKGs": [],+ "notes": "v"+ },+ "( > 1.1.1 && == 1.1.2 && < 2.1.1 ) || ( < 2.1.1 ) || ( > 1.1.1 )": {+ "distroPKGs": [+ "",+ "",+ ""+ ],+ "notes": "lzkb"+ },+ "( > 2.2.1 && == 1.3.1 )": {+ "distroPKGs": [+ "toay",+ "kafy"+ ],+ "notes": "h"+ },+ "( >= 1.2.1 && <= 2.2.2 && <= 1.1.2 && == 2.1.2 )": {+ "distroPKGs": [+ "pq",+ "gwu"+ ],+ "notes": "dv"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "v"+ ],+ "notes": "avri"+ }+ }+ },+ "6.5.1": {},+ "6.7.2": {+ "FreeBSD": {+ "( > 1.1.2 && < 1.1.1 && <= 1.1.2 ) || ( >= 1.1.1 ) || ( == 1.2.1 ) || ( > 1.1.1 )": {+ "distroPKGs": [+ "i",+ ""+ ],+ "notes": "jv"+ },+ "( > 2.2.1 && > 1.1.1 && == 1.1.1 )": {+ "distroPKGs": [+ "unze",+ "",+ "qckn",+ "u"+ ],+ "notes": "i"+ }+ },+ "Linux_Gentoo": {},+ "OpenBSD": {+ "unknown_versioning": {+ "distroPKGs": [],+ "notes": ""+ }+ },+ "Windows": {}+ },+ "8.3.2": {+ "Linux_Fedora": {},+ "Windows": {+ "( < 1.2.2 && > 1.1.1 && <= 2.2.1 && == 2.1.1 )": {+ "distroPKGs": [],+ "notes": "wwfg"+ },+ "( <= 1.1.1 && < 1.1.1 ) || ( < 2.1.2 && >= 1.1.1 )": {+ "distroPKGs": [+ "waps",+ "mto"+ ],+ "notes": "wll"+ },+ "( <= 1.2.2 && > 2.1.1 && <= 1.2.1 && <= 2.2.1 )": {+ "distroPKGs": [+ "vbfb"+ ],+ "notes": "wta"+ },+ "( <= 2.1.1 && >= 1.1.2 && <= 2.4.2 )": {+ "distroPKGs": [+ "tr",+ "ue",+ "",+ ""+ ],+ "notes": "td"+ },+ "( == 1.2.2 ) || ( <= 1.1.1 ) || ( > 1.1.1 )": {+ "distroPKGs": [+ "",+ ""+ ],+ "notes": ""+ },+ "unknown_versioning": {+ "distroPKGs": [+ "",+ "x",+ "kz",+ ""+ ],+ "notes": "dvm"+ }+ }+ },+ "unknown_version": {}+ },+ "p\u0006@": {+ "3.8.7": {+ "FreeBSD": {+ "( <= 1.1.1 && < 1.2.1 ) || ( < 1.1.1 )": {+ "distroPKGs": [+ "",+ "",+ "er",+ "xn"+ ],+ "notes": "hqjd"+ },+ "( > 1.1.1 && > 1.1.1 ) || ( == 1.1.1 ) || ( < 1.2.1 )": {+ "distroPKGs": [],+ "notes": "ef"+ },+ "( > 1.1.1 ) || ( == 1.2.2 && > 2.1.1 )": {+ "distroPKGs": [+ "",+ "ykmj",+ "fx"+ ],+ "notes": "xy"+ }+ },+ "OpenBSD": {+ "( < 2.2.1 && <= 1.1.1 ) || ( > 1.1.1 && <= 1.1.1 ) || ( < 2.1.1 )": {+ "distroPKGs": [],+ "notes": "tca"+ },+ "( == 2.1.1 && > 2.2.1 && <= 2.2.1 )": {+ "distroPKGs": [],+ "notes": "fobc"+ },+ "( > 1.1.2 )": {+ "distroPKGs": [+ "oau"+ ],+ "notes": "la"+ },+ "( > 2.1.2 && <= 1.2.2 )": {+ "distroPKGs": [+ "wctr",+ "s",+ "tiyo",+ "qrw"+ ],+ "notes": ""+ },+ "( >= 1.2.2 )": {+ "distroPKGs": [+ "kzre",+ "",+ "d",+ "rh"+ ],+ "notes": "fbrk"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "wpzu",+ "",+ "yl"+ ],+ "notes": "gi"+ }+ },+ "Windows": {+ "( <= 1.1.1 ) || ( <= 1.1.2 ) || ( >= 1.1.2 ) || ( >= 1.1.1 )": {+ "distroPKGs": [+ "",+ "sv"+ ],+ "notes": "jb"+ },+ "( <= 2.1.1 ) || ( > 2.2.1 )": {+ "distroPKGs": [+ "ifqe",+ "xhju"+ ],+ "notes": "rav"+ },+ "( == 2.1.2 )": {+ "distroPKGs": [+ "jbym",+ "axqs"+ ],+ "notes": "clm"+ },+ "( > 1.2.1 && == 2.2.2 && > 2.1.1 && == 1.1.1 )": {+ "distroPKGs": [],+ "notes": "xg"+ },+ "( > 1.2.2 && > 1.1.3 && >= 2.1.1 )": {+ "distroPKGs": [+ "xusc",+ "gis",+ "hxhg",+ "zw"+ ],+ "notes": "f"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "zqar",+ "y",+ "slr"+ ],+ "notes": "a"+ }+ }+ },+ "4.2.7": {+ "FreeBSD": {+ "( <= 1.1.1 && <= 1.1.1 && <= 2.1.1 ) || ( == 2.1.1 && > 1.1.1 )": {+ "distroPKGs": [+ "od",+ "kwu",+ "u"+ ],+ "notes": "vmr"+ },+ "( >= 1.1.1 && <= 1.2.1 ) || ( == 2.2.1 && >= 1.1.1 )": {+ "distroPKGs": [],+ "notes": "qh"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "rp",+ "ov",+ "",+ "vgw"+ ],+ "notes": "ezsz"+ }+ },+ "Linux_Debian": {+ "( < 1.1.1 && == 1.1.1 && >= 2.1.1 ) || ( < 1.1.1 && == 1.1.1 ) || ( < 2.2.1 )": {+ "distroPKGs": [],+ "notes": "ara"+ },+ "( <= 1.1.1 ) || ( >= 1.1.1 )": {+ "distroPKGs": [+ "qrdj",+ "dqgn"+ ],+ "notes": "q"+ },+ "( == 1.1.1 && == 1.2.1 ) || ( == 1.1.2 ) || ( == 1.1.1 )": {+ "distroPKGs": [+ "",+ "dax",+ "",+ "bg"+ ],+ "notes": "rr"+ },+ "( >= 2.2.1 && > 1.1.2 && >= 1.2.2 && >= 1.1.1 )": {+ "distroPKGs": [+ "x"+ ],+ "notes": "u"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "q",+ "hxbs",+ "ktos",+ "ealq"+ ],+ "notes": "ca"+ }+ },+ "Linux_Exherbo": {+ "( == 1.1.1 ) || ( < 1.1.2 && == 1.1.1 )": {+ "distroPKGs": [+ "cbq",+ "uirl",+ "a"+ ],+ "notes": "rgkl"+ },+ "( == 1.1.1 ) || ( > 2.1.1 )": {+ "distroPKGs": [+ "xdcb"+ ],+ "notes": "ho"+ },+ "( > 1.1.1 && <= 2.1.1 && <= 1.2.2 ) || ( <= 1.1.1 )": {+ "distroPKGs": [+ "kine"+ ],+ "notes": "h"+ },+ "( > 1.2.2 )": {+ "distroPKGs": [+ "kd",+ "wksy"+ ],+ "notes": "sfq"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "cee",+ "m",+ "jt",+ "uxd"+ ],+ "notes": ""+ }+ },+ "Linux_Ubuntu": {+ "unknown_versioning": {+ "distroPKGs": [+ "nglj",+ "a"+ ],+ "notes": "ln"+ }+ }+ },+ "7.6.5": {+ "Darwin": {+ "( < 1.1.2 && < 1.1.2 && == 1.2.2 ) || ( >= 1.1.1 )": {+ "distroPKGs": [+ "xdhh",+ "gxq"+ ],+ "notes": "qene"+ },+ "( > 1.1.1 ) || ( <= 1.1.1 && > 1.1.2 )": {+ "distroPKGs": [],+ "notes": "sgl"+ },+ "( > 1.2.2 && == 1.1.1 && > 1.1.1 ) || ( > 1.2.1 ) || ( > 1.1.2 ) || ( == 1.2.2 )": {+ "distroPKGs": [+ "aixd",+ "",+ "hyw",+ "bpb"+ ],+ "notes": ""+ },+ "( > 4.2.2 && > 1.2.2 && >= 2.1.2 )": {+ "distroPKGs": [+ "q",+ "mw",+ ""+ ],+ "notes": "qg"+ },+ "( >= 1.1.1 && < 2.1.1 ) || ( >= 1.1.1 )": {+ "distroPKGs": [+ "jfv",+ "tzkz",+ "goi",+ "eb"+ ],+ "notes": "kdp"+ },+ "( >= 1.1.1 ) || ( == 1.2.1 ) || ( == 1.1.1 )": {+ "distroPKGs": [+ "mnac",+ "ixd",+ "oc"+ ],+ "notes": "pvsb"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "",+ "n",+ "n"+ ],+ "notes": "wct"+ }+ },+ "FreeBSD": {+ "( < 2.2.2 && > 1.2.2 && >= 2.2.3 && == 2.1.2 )": {+ "distroPKGs": [+ "s"+ ],+ "notes": ""+ },+ "( <= 2.1.2 && > 2.1.2 )": {+ "distroPKGs": [+ "b",+ "rmf",+ "cghz"+ ],+ "notes": ""+ },+ "( >= 1.1.1 && < 1.1.1 && == 2.1.1 ) || ( == 1.1.1 && < 1.1.1 ) || ( > 1.1.2 )": {+ "distroPKGs": [+ "g"+ ],+ "notes": "hl"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "mbid",+ "qz",+ "q",+ "dnc"+ ],+ "notes": ""+ }+ },+ "Linux_Exherbo": {+ "( < 1.1.1 && < 1.1.1 ) || ( == 1.2.1 ) || ( <= 2.1.1 ) || ( > 1.1.1 )": {+ "distroPKGs": [+ "oia",+ ""+ ],+ "notes": "jky"+ },+ "( < 1.1.1 && == 2.1.2 ) || ( == 1.2.1 ) || ( >= 1.1.1 ) || ( <= 1.1.1 )": {+ "distroPKGs": [],+ "notes": ""+ },+ "( <= 1.2.2 )": {+ "distroPKGs": [+ ""+ ],+ "notes": "j"+ },+ "( <= 2.2.1 )": {+ "distroPKGs": [+ "k",+ "hdid",+ "zdvh",+ "mx"+ ],+ "notes": "o"+ },+ "( > 2.1.2 && < 1.1.2 && < 2.1.1 )": {+ "distroPKGs": [],+ "notes": "f"+ },+ "( >= 2.1.1 && == 1.1.2 && < 1.2.1 ) || ( > 1.1.1 && <= 2.1.1 ) || ( < 1.1.1 )": {+ "distroPKGs": [],+ "notes": "erau"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "dz",+ "pv",+ "c",+ ""+ ],+ "notes": "azh"+ }+ },+ "Windows": {+ "( < 2.2.2 )": {+ "distroPKGs": [+ "",+ ""+ ],+ "notes": "bkk"+ },+ "( == 2.1.1 && >= 2.2.2 )": {+ "distroPKGs": [+ "dax",+ "t",+ "dobh"+ ],+ "notes": "d"+ },+ "( > 1.3.2 && == 1.2.3 )": {+ "distroPKGs": [+ "b"+ ],+ "notes": "h"+ }+ }+ },+ "8.5.7": {+ "Darwin": {+ "( == 1.1.1 && > 1.2.1 && <= 2.1.1 ) || ( >= 1.1.1 && > 1.1.1 ) || ( <= 1.1.1 )": {+ "distroPKGs": [+ "kk",+ "qbou",+ "hzey",+ "d"+ ],+ "notes": "ow"+ },+ "unknown_versioning": {+ "distroPKGs": [],+ "notes": "it"+ }+ },+ "Linux_Debian": {+ "( == 1.1.1 && <= 1.1.2 && >= 1.1.1 ) || ( >= 1.1.1 && <= 1.1.1 )": {+ "distroPKGs": [+ "ag"+ ],+ "notes": ""+ }+ },+ "Linux_RedHat": {+ "( == 1.1.1 ) || ( >= 1.1.1 && > 2.1.1 ) || ( <= 1.1.1 )": {+ "distroPKGs": [+ "ifad",+ "isj",+ "e",+ "yp"+ ],+ "notes": "xw"+ }+ },+ "OpenBSD": {},+ "Windows": {+ "( < 1.2.2 && >= 1.1.1 ) || ( >= 1.1.1 ) || ( <= 2.2.1 ) || ( <= 1.1.2 )": {+ "distroPKGs": [+ "scaw",+ "jr"+ ],+ "notes": "tb"+ },+ "( <= 1.1.1 ) || ( <= 1.1.1 && < 1.1.1 )": {+ "distroPKGs": [+ "zweb",+ "fjpp",+ "x",+ "pl"+ ],+ "notes": "pq"+ },+ "( == 1.2.2 && > 1.1.1 && == 1.1.2 && < 1.1.1 )": {+ "distroPKGs": [+ "vm"+ ],+ "notes": "dx"+ },+ "( == 2.2.1 && >= 2.1.1 )": {+ "distroPKGs": [+ "bqpc",+ "s",+ "tbdo",+ ""+ ],+ "notes": "cfcg"+ },+ "( >= 1.1.1 && > 1.1.2 && >= 1.1.1 ) || ( < 1.1.1 && == 1.1.1 )": {+ "distroPKGs": [],+ "notes": "bj"+ },+ "unknown_versioning": {+ "distroPKGs": [],+ "notes": "j"+ }+ }+ }+ }+ }+ },+ {+ "ghcupDownloads": {+ "4": {+ "toolDetails": {+ "toolAuthor": "",+ "toolContact": "",+ "toolDescription": "",+ "toolHomepage": "https:",+ "toolLicense": "",+ "toolMaintainer": "",+ "toolRepository": null+ },+ "toolVersions": {}+ },+ "g`\u0006anf": {+ "toolDetails": {+ "toolAuthor": "",+ "toolContact": "",+ "toolDescription": "",+ "toolHomepage": "http:",+ "toolLicense": "",+ "toolMaintainer": "",+ "toolRepository": "http:"+ },+ "toolVersions": {+ "2.2.2": {+ "changelog": null,+ "postInstall": "",+ "postRemove": "",+ "preCompile": "",+ "preInstall": "",+ "releaseDay": "-21696335035624399-06-28",+ "revisionSpec": {},+ "tags": []+ }+ }+ },+ "k": {+ "toolDetails": {+ "toolAuthor": null,+ "toolContact": null,+ "toolDescription": "",+ "toolHomepage": "http:",+ "toolLicense": null,+ "toolMaintainer": null,+ "toolRepository": "http:"+ },+ "toolVersions": {+ "1.3.3": {+ "changelog": "https:",+ "postInstall": "",+ "postRemove": null,+ "preCompile": null,+ "preInstall": "",+ "releaseDay": "-18865655703723805-07-27",+ "revisionSpec": {},+ "tags": []+ },+ "3.1.2": {+ "changelog": "http:",+ "postInstall": "",+ "postRemove": null,+ "preCompile": "",+ "preInstall": null,+ "releaseDay": "10366089730599605-06-20",+ "revisionSpec": {},+ "tags": []+ }+ }+ },+ "n?": {+ "toolDetails": {+ "toolAuthor": "",+ "toolContact": "",+ "toolDescription": "",+ "toolHomepage": null,+ "toolLicense": "",+ "toolMaintainer": "",+ "toolRepository": "http:"+ },+ "toolVersions": {+ "1.3.1": {+ "changelog": "https:",+ "postInstall": "",+ "postRemove": "",+ "preCompile": "",+ "preInstall": null,+ "releaseDay": "6344031471043112-04-29",+ "revisionSpec": {},+ "tags": []+ },+ "2.3.1": {+ "changelog": null,+ "postInstall": null,+ "postRemove": "",+ "preCompile": "",+ "preInstall": "",+ "releaseDay": "5360518418508929-11-03",+ "revisionSpec": {},+ "tags": []+ },+ "3.2.3": {+ "changelog": null,+ "postInstall": "",+ "postRemove": "",+ "preCompile": "",+ "preInstall": null,+ "releaseDay": "-5593712519057301-10-10",+ "revisionSpec": {},+ "tags": []+ }+ }+ },+ "v[r": {+ "toolDetails": {+ "toolAuthor": "",+ "toolContact": "",+ "toolDescription": "",+ "toolHomepage": "https:",+ "toolLicense": "",+ "toolMaintainer": "",+ "toolRepository": "https:"+ },+ "toolVersions": {+ "2.1.2": {+ "changelog": "https:",+ "postInstall": "",+ "postRemove": "",+ "preCompile": "",+ "preInstall": "",+ "releaseDay": "-9262895168850227-07-18",+ "revisionSpec": {},+ "tags": []+ },+ "2.2.1": {+ "changelog": "https:",+ "postInstall": "",+ "postRemove": "",+ "preCompile": "",+ "preInstall": "",+ "releaseDay": "-24619781721787646-04-30",+ "revisionSpec": {},+ "tags": []+ },+ "5.2.3": {+ "changelog": "https:",+ "postInstall": "",+ "postRemove": null,+ "preCompile": "",+ "preInstall": null,+ "releaseDay": null,+ "revisionSpec": {},+ "tags": []+ }+ }+ },+ "vv3s\u001c": {+ "toolDetails": {+ "toolAuthor": "",+ "toolContact": null,+ "toolDescription": "",+ "toolHomepage": "http:",+ "toolLicense": null,+ "toolMaintainer": "",+ "toolRepository": "https:"+ },+ "toolVersions": {+ "1.3.3": {+ "changelog": "https:",+ "postInstall": null,+ "postRemove": "",+ "preCompile": "",+ "preInstall": "",+ "releaseDay": null,+ "revisionSpec": {},+ "tags": []+ }+ }+ }+ },+ "metadataUpdate": "http:",+ "toolRequirements": {+ "\u0003\u0006qia5_": {+ "3.4.5": {+ "Darwin": {+ "( <= 1.1.1 && > 1.1.1 && < 1.2.1 ) || ( > 1.1.1 )": {+ "distroPKGs": [+ "tad"+ ],+ "notes": ""+ },+ "( > 1.1.1 && == 1.1.1 ) || ( <= 1.1.1 && > 1.1.1 ) || ( > 1.1.1 )": {+ "distroPKGs": [+ "",+ "kdh",+ "h",+ "mizt"+ ],+ "notes": "jp"+ },+ "( > 1.1.2 && < 2.1.2 && == 1.2.1 && <= 3.1.1 )": {+ "distroPKGs": [+ "f",+ "",+ "c",+ "ma"+ ],+ "notes": "sz"+ },+ "( >= 1.1.1 && <= 2.1.1 ) || ( <= 1.1.1 )": {+ "distroPKGs": [+ "",+ "pdeq"+ ],+ "notes": "dthe"+ },+ "( >= 2.1.1 ) || ( > 1.1.2 ) || ( <= 1.1.1 ) || ( < 2.1.1 )": {+ "distroPKGs": [],+ "notes": ""+ },+ "unknown_versioning": {+ "distroPKGs": [],+ "notes": "w"+ }+ },+ "Linux_UnknownLinux": {+ "( < 2.1.2 && == 2.1.2 && < 1.2.3 && == 2.2.3 )": {+ "distroPKGs": [],+ "notes": ""+ },+ "( <= 1.1.1 ) || ( < 2.1.2 && > 1.2.1 )": {+ "distroPKGs": [+ "b",+ "nh",+ "au",+ "uy"+ ],+ "notes": "z"+ },+ "( <= 1.2.2 && == 3.1.2 )": {+ "distroPKGs": [],+ "notes": "zue"+ },+ "( <= 2.1.2 && < 1.1.1 && >= 1.1.2 && > 2.1.2 )": {+ "distroPKGs": [+ "",+ "w",+ "re",+ "mg"+ ],+ "notes": "miqk"+ },+ "( == 1.1.1 && < 1.1.1 ) || ( >= 2.1.1 ) || ( <= 2.1.1 ) || ( > 1.1.2 )": {+ "distroPKGs": [+ "zp"+ ],+ "notes": "poxy"+ },+ "( == 1.1.1 ) || ( >= 1.1.1 ) || ( < 1.1.1 ) || ( == 1.1.1 )": {+ "distroPKGs": [+ "kbr",+ "iki"+ ],+ "notes": "z"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "n",+ ""+ ],+ "notes": "oqz"+ }+ },+ "OpenBSD": {+ "( < 2.2.1 && < 2.1.1 && <= 1.2.2 )": {+ "distroPKGs": [+ "",+ "ab",+ "qbq"+ ],+ "notes": ""+ },+ "( == 1.2.1 && < 2.2.2 && <= 3.3.1 )": {+ "distroPKGs": [],+ "notes": "v"+ },+ "( > 1.1.1 && >= 2.1.2 && == 2.2.1 && == 2.1.3 )": {+ "distroPKGs": [+ "d",+ ""+ ],+ "notes": "zkuu"+ },+ "( >= 1.1.1 && > 1.1.1 && == 1.1.1 ) || ( >= 1.1.2 )": {+ "distroPKGs": [+ "tych",+ ""+ ],+ "notes": "wta"+ }+ }+ },+ "8.1.5": {+ "Darwin": {+ "( < 1.1.2 && >= 1.1.1 && <= 1.1.1 ) || ( <= 1.2.1 && <= 1.1.1 ) || ( == 1.1.2 ) || ( == 2.1.2 )": {+ "distroPKGs": [+ "wm",+ "ut",+ "ab",+ "zs"+ ],+ "notes": "l"+ },+ "( < 1.2.2 && >= 1.1.2 && <= 1.1.2 && <= 2.1.2 )": {+ "distroPKGs": [+ "jtav",+ "",+ "r",+ "y"+ ],+ "notes": "ursf"+ },+ "( > 1.1.2 && <= 1.1.1 ) || ( < 1.1.1 && >= 1.1.1 ) || ( <= 2.1.1 )": {+ "distroPKGs": [+ "",+ "k",+ "ddg"+ ],+ "notes": "oae"+ },+ "unknown_versioning": {+ "distroPKGs": [],+ "notes": "a"+ }+ },+ "OpenBSD": {+ "( == 1.2.2 )": {+ "distroPKGs": [+ "oywn",+ "cec"+ ],+ "notes": ""+ },+ "( > 2.1.2 && <= 2.2.1 )": {+ "distroPKGs": [+ "nyar",+ "fax",+ "jic"+ ],+ "notes": ""+ },+ "( >= 2.2.1 && < 1.1.1 )": {+ "distroPKGs": [],+ "notes": "dd"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "eor",+ "dw"+ ],+ "notes": ""+ }+ },+ "Windows": {+ "( < 2.1.1 && <= 2.2.1 && > 2.1.1 )": {+ "distroPKGs": [+ "pu",+ "k",+ "",+ "o"+ ],+ "notes": "mac"+ },+ "( < 2.1.4 && == 2.1.1 && >= 3.2.1 )": {+ "distroPKGs": [+ "dc",+ "r",+ "twsi",+ "p"+ ],+ "notes": ""+ },+ "( <= 1.1.1 ) || ( < 1.1.1 && >= 1.1.1 )": {+ "distroPKGs": [+ "qk",+ "",+ ""+ ],+ "notes": "my"+ },+ "( <= 2.2.2 && >= 2.1.1 )": {+ "distroPKGs": [+ "rcw",+ "",+ "mtzb",+ "a"+ ],+ "notes": "kqwg"+ },+ "( <= 3.2.3 && > 2.1.1 )": {+ "distroPKGs": [+ "ny"+ ],+ "notes": "w"+ },+ "( == 2.1.3 )": {+ "distroPKGs": [],+ "notes": "hnp"+ },+ "( >= 1.2.1 && < 2.1.1 ) || ( > 1.1.1 ) || ( >= 1.1.1 ) || ( > 1.1.1 )": {+ "distroPKGs": [+ "",+ "pl"+ ],+ "notes": "ugjq"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "qj",+ "o"+ ],+ "notes": "h"+ }+ }+ },+ "8.5.8": {+ "Darwin": {+ "( < 1.1.1 && < 1.1.2 && <= 1.1.1 ) || ( == 1.3.1 ) || ( <= 1.1.1 )": {+ "distroPKGs": [+ "l",+ "",+ "lyl"+ ],+ "notes": "g"+ },+ "( < 1.1.2 ) || ( > 1.1.1 && > 2.1.1 )": {+ "distroPKGs": [+ "",+ "x",+ "rntu",+ "od"+ ],+ "notes": "txwe"+ },+ "( > 2.2.2 && < 2.2.2 && > 2.2.1 && > 1.3.2 )": {+ "distroPKGs": [+ "znl",+ "",+ "cvql"+ ],+ "notes": ""+ },+ "unknown_versioning": {+ "distroPKGs": [+ "ecey",+ "j",+ "",+ "ndhy"+ ],+ "notes": "mem"+ }+ },+ "OpenBSD": {+ "unknown_versioning": {+ "distroPKGs": [+ "tiy",+ "hah",+ "rifw",+ "jnwo"+ ],+ "notes": "uvl"+ }+ },+ "Windows": {+ "( == 1.1.1 ) || ( > 1.1.1 && <= 1.1.2 )": {+ "distroPKGs": [+ "g",+ "zquk",+ "",+ "hrfo"+ ],+ "notes": "gtvz"+ },+ "( == 1.2.1 ) || ( >= 1.1.1 )": {+ "distroPKGs": [+ "wdv"+ ],+ "notes": "ozip"+ },+ "( > 1.1.1 && == 1.1.2 && >= 1.1.1 ) || ( > 1.1.1 ) || ( > 2.1.1 )": {+ "distroPKGs": [+ "da"+ ],+ "notes": "wk"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "jz",+ "q",+ "rcie",+ "zsq"+ ],+ "notes": "yov"+ }+ }+ }+ },+ ">)ex1": {+ "2.1.7": {+ "Darwin": {+ "( < 2.1.1 && >= 1.2.3 && <= 2.2.2 )": {+ "distroPKGs": [],+ "notes": "d"+ },+ "( <= 2.2.2 )": {+ "distroPKGs": [],+ "notes": "hbo"+ },+ "( == 1.1.2 && <= 1.2.1 && < 2.1.1 && <= 3.2.1 )": {+ "distroPKGs": [],+ "notes": ""+ },+ "( == 1.1.2 && > 2.2.3 )": {+ "distroPKGs": [],+ "notes": "in"+ },+ "( == 2.1.1 )": {+ "distroPKGs": [],+ "notes": "dtyb"+ },+ "( > 1.1.2 ) || ( <= 1.2.1 && <= 1.1.1 ) || ( == 1.2.1 ) || ( > 1.1.2 )": {+ "distroPKGs": [],+ "notes": "i"+ },+ "unknown_versioning": {+ "distroPKGs": [],+ "notes": "at"+ }+ },+ "FreeBSD": {+ "( > 1.2.1 && > 1.1.2 && >= 1.1.1 ) || ( < 1.1.1 ) || ( > 1.1.1 )": {+ "distroPKGs": [],+ "notes": ""+ },+ "( > 1.3.1 && < 1.2.3 )": {+ "distroPKGs": [+ "mf"+ ],+ "notes": "nb"+ },+ "( > 2.2.2 )": {+ "distroPKGs": [+ "oph"+ ],+ "notes": "j"+ },+ "( >= 2.2.2 && > 2.2.1 && < 2.2.1 )": {+ "distroPKGs": [],+ "notes": ""+ },+ "unknown_versioning": {+ "distroPKGs": [+ "krcg",+ ""+ ],+ "notes": "r"+ }+ },+ "Linux_RedHat": {+ "( <= 1.1.1 && <= 1.1.1 && < 1.1.2 ) || ( == 1.1.2 && >= 1.1.1 ) || ( >= 2.1.1 )": {+ "distroPKGs": [+ "cyp"+ ],+ "notes": "oqi"+ },+ "( <= 1.2.1 ) || ( >= 2.1.1 && <= 1.1.2 )": {+ "distroPKGs": [+ "nx",+ "cv",+ "yo",+ "to"+ ],+ "notes": "tu"+ },+ "( == 2.1.1 && < 2.1.1 && < 1.1.2 && > 2.1.2 )": {+ "distroPKGs": [],+ "notes": ""+ },+ "( == 2.1.2 && >= 1.3.1 )": {+ "distroPKGs": [+ "h"+ ],+ "notes": ""+ },+ "( == 2.2.3 && == 3.1.2 && >= 2.1.2 && == 1.2.2 )": {+ "distroPKGs": [+ "lmi",+ "pb",+ "ulve"+ ],+ "notes": "dtnh"+ },+ "( > 2.1.2 && > 2.3.2 )": {+ "distroPKGs": [+ "edko"+ ],+ "notes": ""+ },+ "unknown_versioning": {+ "distroPKGs": [+ "pri"+ ],+ "notes": "mim"+ }+ },+ "OpenBSD": {+ "( <= 1.1.1 && > 1.1.1 && >= 1.1.1 ) || ( > 1.1.1 && < 1.1.1 )": {+ "distroPKGs": [+ "e",+ "rhsm",+ "ecbc"+ ],+ "notes": "jj"+ },+ "( <= 1.2.2 && == 1.1.1 && == 1.1.2 )": {+ "distroPKGs": [+ "wzl"+ ],+ "notes": "xo"+ },+ "( > 1.1.2 && < 2.2.2 && <= 2.2.1 && == 1.1.2 )": {+ "distroPKGs": [+ "dr",+ ""+ ],+ "notes": "l"+ },+ "( >= 1.1.1 && < 1.1.1 )": {+ "distroPKGs": [+ "x",+ "x",+ ""+ ],+ "notes": "nloo"+ },+ "( >= 1.1.2 && < 2.1.2 && >= 1.1.1 && > 1.2.2 )": {+ "distroPKGs": [+ "ysnq"+ ],+ "notes": "idw"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "klx"+ ],+ "notes": "r"+ }+ }+ },+ "3.2.5": {+ "FreeBSD": {+ "( == 3.1.2 )": {+ "distroPKGs": [+ "",+ "g",+ "dkx"+ ],+ "notes": "mhag"+ },+ "( >= 1.1.1 ) || ( == 1.1.1 && < 1.1.1 )": {+ "distroPKGs": [+ "hx",+ "ko"+ ],+ "notes": "wt"+ }+ },+ "Linux_Gentoo": {+ "( < 1.1.1 && < 1.1.2 ) || ( <= 1.1.1 )": {+ "distroPKGs": [+ "zkw",+ "ahu",+ "o"+ ],+ "notes": "z"+ },+ "( == 2.2.2 && <= 2.2.2 && == 2.3.2 && >= 1.1.2 )": {+ "distroPKGs": [+ "fxu",+ "z",+ "jgdz",+ "bdvl"+ ],+ "notes": "dac"+ },+ "( > 1.1.1 ) || ( > 1.1.1 && <= 1.1.1 )": {+ "distroPKGs": [+ "mpkh",+ "okz",+ "scw",+ "oqa"+ ],+ "notes": "xrgy"+ },+ "( > 1.2.1 && < 1.1.1 && <= 1.1.1 ) || ( >= 2.1.1 )": {+ "distroPKGs": [+ "aivo"+ ],+ "notes": "usr"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "",+ "",+ "c",+ "yzh"+ ],+ "notes": "fo"+ }+ },+ "OpenBSD": {+ "( == 1.1.2 )": {+ "distroPKGs": [+ "",+ "gnww",+ "t",+ "ddr"+ ],+ "notes": "m"+ },+ "( > 1.1.2 ) || ( < 1.1.1 )": {+ "distroPKGs": [+ "vxz"+ ],+ "notes": ""+ },+ "( > 1.2.1 && < 1.1.1 ) || ( >= 1.2.2 && <= 1.1.2 )": {+ "distroPKGs": [+ "lbzz",+ "nwhg",+ "poqx",+ "av"+ ],+ "notes": "mrfh"+ },+ "( >= 2.3.1 && <= 2.1.2 && == 2.1.1 && == 2.1.2 )": {+ "distroPKGs": [+ "",+ "bl",+ "yrf"+ ],+ "notes": "cxf"+ }+ }+ },+ "3.5.1": {+ "FreeBSD": {+ "( < 1.1.1 && >= 1.1.2 && > 1.1.2 && >= 1.2.2 )": {+ "distroPKGs": [],+ "notes": "k"+ },+ "( < 1.1.2 && <= 1.2.2 ) || ( <= 1.1.1 && >= 1.1.1 )": {+ "distroPKGs": [+ "",+ "gz",+ "",+ "z"+ ],+ "notes": ""+ },+ "( < 1.2.1 && > 1.1.1 && == 1.1.3 )": {+ "distroPKGs": [+ "jcn",+ "",+ "tthr",+ "qk"+ ],+ "notes": "pmb"+ },+ "( <= 1.1.1 && >= 2.1.1 ) || ( == 1.1.1 && >= 1.2.1 ) || ( < 2.2.1 ) || ( < 2.1.1 )": {+ "distroPKGs": [+ "kqx"+ ],+ "notes": "sh"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "ee"+ ],+ "notes": "j"+ }+ },+ "Linux_Alpine": {+ "( < 1.1.2 ) || ( >= 1.1.1 ) || ( >= 2.1.2 )": {+ "distroPKGs": [],+ "notes": "q"+ },+ "( < 2.1.2 ) || ( < 1.1.1 ) || ( > 1.2.1 )": {+ "distroPKGs": [+ "else",+ "ra",+ "a"+ ],+ "notes": "x"+ },+ "( <= 1.2.1 ) || ( >= 1.2.1 )": {+ "distroPKGs": [+ "pml"+ ],+ "notes": "tjt"+ },+ "( <= 2.2.2 && < 2.1.1 )": {+ "distroPKGs": [+ "ax",+ "",+ "li",+ "dl"+ ],+ "notes": "f"+ },+ "( == 2.2.2 && <= 1.3.2 )": {+ "distroPKGs": [+ "airx"+ ],+ "notes": "y"+ },+ "( == 2.2.2 && > 3.2.4 && < 1.4.2 )": {+ "distroPKGs": [+ "fc",+ "x",+ "",+ "opl"+ ],+ "notes": "l"+ },+ "( >= 2.2.2 && >= 2.2.3 )": {+ "distroPKGs": [+ "av",+ "rac",+ "j",+ ""+ ],+ "notes": "yyc"+ },+ "unknown_versioning": {+ "distroPKGs": [+ ""+ ],+ "notes": "va"+ }+ },+ "Linux_Rocky": {+ "( < 2.2.1 && == 2.3.2 && == 1.1.1 && < 2.1.2 )": {+ "distroPKGs": [+ "v"+ ],+ "notes": "cu"+ },+ "( <= 1.1.1 ) || ( > 1.1.1 )": {+ "distroPKGs": [+ "",+ "d",+ "f"+ ],+ "notes": "wwg"+ },+ "( <= 3.2.1 )": {+ "distroPKGs": [+ "ms",+ "p",+ "tngt",+ ""+ ],+ "notes": "so"+ },+ "( == 1.1.2 && >= 2.1.1 && <= 1.1.1 ) || ( < 2.1.1 && < 1.1.1 ) || ( >= 1.2.1 ) || ( < 1.1.1 )": {+ "distroPKGs": [+ "k",+ "ogri",+ "rg",+ "bilt"+ ],+ "notes": "bi"+ },+ "( == 1.2.1 && <= 2.1.2 )": {+ "distroPKGs": [+ "c",+ "tsp"+ ],+ "notes": "phac"+ },+ "( >= 1.2.2 )": {+ "distroPKGs": [+ "jhc",+ "lus",+ "w"+ ],+ "notes": "ai"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "r",+ "z",+ "tcz"+ ],+ "notes": "mov"+ }+ },+ "Linux_UnknownLinux": {},+ "OpenBSD": {+ "unknown_versioning": {+ "distroPKGs": [+ "h",+ "fa",+ "k"+ ],+ "notes": "gc"+ }+ },+ "Windows": {}+ },+ "4.6.1": {+ "Darwin": {+ "( <= 3.2.2 && < 1.1.1 && < 2.1.2 )": {+ "distroPKGs": [+ "zepu",+ "hewj",+ "bkpn"+ ],+ "notes": ""+ },+ "( >= 1.1.1 && < 2.1.1 ) || ( == 1.1.1 && > 1.1.1 )": {+ "distroPKGs": [+ "",+ ""+ ],+ "notes": "zh"+ },+ "( >= 1.2.1 ) || ( < 1.2.2 )": {+ "distroPKGs": [+ "qbm"+ ],+ "notes": "kre"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "eb"+ ],+ "notes": "egb"+ }+ },+ "OpenBSD": {+ "unknown_versioning": {+ "distroPKGs": [+ ""+ ],+ "notes": "zcl"+ }+ }+ },+ "6.3.4": {+ "Darwin": {+ "( == 1.3.2 )": {+ "distroPKGs": [+ "ee",+ "shu",+ ""+ ],+ "notes": "xrwu"+ },+ "( == 2.1.2 && < 2.2.2 && >= 1.2.1 && >= 1.1.2 )": {+ "distroPKGs": [+ ""+ ],+ "notes": "l"+ },+ "( > 1.1.1 ) || ( < 1.1.1 ) || ( < 1.2.1 )": {+ "distroPKGs": [+ "c",+ "d"+ ],+ "notes": "sw"+ },+ "( > 2.2.1 ) || ( == 2.1.1 && > 1.1.2 )": {+ "distroPKGs": [+ "vd",+ "",+ "ow"+ ],+ "notes": "xhcd"+ },+ "( >= 2.1.1 && < 1.2.2 && >= 1.2.1 && < 2.2.3 )": {+ "distroPKGs": [+ "esm",+ "pmnn",+ "j",+ ""+ ],+ "notes": ""+ },+ "( >= 2.1.1 && > 2.1.1 && < 2.1.1 )": {+ "distroPKGs": [+ "mhn"+ ],+ "notes": "cx"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "",+ "",+ "",+ ""+ ],+ "notes": "d"+ }+ },+ "FreeBSD": {+ "( < 1.1.1 ) || ( == 1.1.1 )": {+ "distroPKGs": [+ "tfw"+ ],+ "notes": "pz"+ },+ "( < 1.2.1 && < 1.1.1 ) || ( > 1.2.1 ) || ( > 1.2.1 )": {+ "distroPKGs": [+ "pcc",+ "pu",+ "xfr"+ ],+ "notes": ""+ },+ "( <= 3.2.1 )": {+ "distroPKGs": [+ ""+ ],+ "notes": ""+ },+ "( == 2.2.2 && > 2.1.2 )": {+ "distroPKGs": [+ "znta",+ "er"+ ],+ "notes": "au"+ },+ "( == 3.2.2 && == 1.2.1 )": {+ "distroPKGs": [+ "zkyj",+ "htdv"+ ],+ "notes": "pa"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "w",+ "o",+ "uve"+ ],+ "notes": "bq"+ }+ },+ "Windows": {+ "( == 1.1.1 && >= 1.1.1 ) || ( > 1.1.1 )": {+ "distroPKGs": [+ "f",+ "jufy",+ "eif",+ ""+ ],+ "notes": "w"+ },+ "( >= 2.1.2 && <= 1.2.2 && >= 1.1.1 && > 2.1.1 )": {+ "distroPKGs": [+ "d"+ ],+ "notes": "cffa"+ },+ "unknown_versioning": {+ "distroPKGs": [],+ "notes": ""+ }+ }+ }+ },+ "?\u0014s": {+ "2.5.6": {+ "FreeBSD": {+ "( <= 2.1.2 && == 2.2.2 && == 2.2.2 )": {+ "distroPKGs": [],+ "notes": "ucso"+ },+ "unknown_versioning": {+ "distroPKGs": [],+ "notes": "x"+ }+ }+ },+ "5.4.1": {+ "FreeBSD": {},+ "Windows": {+ "( < 3.2.1 && <= 1.1.1 && >= 2.1.1 && < 2.1.2 )": {+ "distroPKGs": [+ "cpyk",+ "",+ "",+ "xlc"+ ],+ "notes": "xzmh"+ },+ "( <= 1.2.1 && > 2.1.1 )": {+ "distroPKGs": [+ "pn",+ "",+ "a",+ "im"+ ],+ "notes": "tetf"+ },+ "unknown_versioning": {+ "distroPKGs": [],+ "notes": "uoq"+ }+ }+ },+ "7.5.7": {+ "Darwin": {+ "( <= 1.1.1 ) || ( <= 1.1.1 && >= 2.2.1 )": {+ "distroPKGs": [+ "y"+ ],+ "notes": ""+ },+ "( <= 1.1.1 ) || ( > 1.1.1 ) || ( >= 1.1.1 ) || ( > 2.2.2 )": {+ "distroPKGs": [+ "vcse",+ "kfbx"+ ],+ "notes": "tinp"+ },+ "( <= 2.1.2 && == 2.1.3 ) || ( == 1.1.1 ) || ( < 1.1.1 ) || ( == 1.1.1 )": {+ "distroPKGs": [],+ "notes": "vmz"+ },+ "( <= 2.2.1 )": {+ "distroPKGs": [+ "zws"+ ],+ "notes": "ykk"+ },+ "( == 1.1.1 && < 1.1.2 && > 1.1.1 ) || ( == 2.2.2 && < 1.2.1 )": {+ "distroPKGs": [],+ "notes": "cqpr"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "vgbb",+ "ncvm"+ ],+ "notes": ""+ }+ },+ "FreeBSD": {+ "( < 1.1.1 && >= 1.1.1 ) || ( >= 1.1.1 )": {+ "distroPKGs": [+ "sqob"+ ],+ "notes": "iuel"+ },+ "( > 1.1.2 && <= 1.1.1 ) || ( < 1.1.1 )": {+ "distroPKGs": [+ "o",+ "gzk"+ ],+ "notes": "h"+ },+ "unknown_versioning": {+ "distroPKGs": [],+ "notes": "cgm"+ }+ },+ "Linux_Debian": {+ "( < 1.1.2 )": {+ "distroPKGs": [],+ "notes": "d"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "k",+ "vmw"+ ],+ "notes": "zri"+ }+ },+ "Linux_Mint": {+ "( > 2.1.1 ) || ( > 1.1.1 ) || ( == 2.1.1 ) || ( > 1.1.2 )": {+ "distroPKGs": [+ "dgto",+ "adwn",+ ""+ ],+ "notes": "lc"+ },+ "( > 2.1.2 && <= 1.3.1 )": {+ "distroPKGs": [+ "lyr",+ "rzs",+ "o",+ ""+ ],+ "notes": "u"+ },+ "( >= 2.1.2 && >= 1.1.1 )": {+ "distroPKGs": [+ "olh",+ "ca",+ "onek",+ ""+ ],+ "notes": "mg"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "difw",+ "ykdo"+ ],+ "notes": "zd"+ }+ },+ "OpenBSD": {},+ "Windows": {+ "( < 1.3.2 && <= 2.1.1 && >= 1.2.1 && < 2.1.2 )": {+ "distroPKGs": [+ "zq",+ "qo"+ ],+ "notes": "do"+ },+ "( >= 2.2.1 ) || ( >= 1.1.1 )": {+ "distroPKGs": [+ "rjzr",+ "wq",+ ""+ ],+ "notes": "prw"+ }+ }+ },+ "unknown_version": {+ "FreeBSD": {+ "( == 1.1.1 && > 1.1.1 && < 1.2.2 && <= 2.2.2 )": {+ "distroPKGs": [+ ""+ ],+ "notes": ""+ },+ "( == 1.1.2 && >= 2.1.1 && < 2.1.1 && <= 1.1.2 )": {+ "distroPKGs": [+ "xmh",+ "r",+ "vl",+ "xiea"+ ],+ "notes": ""+ }+ },+ "Linux_AmazonLinux": {+ "( <= 2.2.1 && > 1.1.2 && <= 2.2.2 && == 2.2.2 )": {+ "distroPKGs": [+ ""+ ],+ "notes": "hhso"+ }+ },+ "OpenBSD": {+ "( < 2.1.1 && > 1.1.1 ) || ( > 1.1.1 && == 1.1.1 ) || ( > 1.1.1 ) || ( < 1.1.1 )": {+ "distroPKGs": [],+ "notes": "krb"+ },+ "( > 1.2.2 && > 1.3.2 && > 2.3.3 && == 4.2.1 )": {+ "distroPKGs": [+ "mzm",+ "loc"+ ],+ "notes": "i"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "p",+ "",+ "cxh",+ "ljcb"+ ],+ "notes": ""+ }+ },+ "Windows": {+ "( < 1.1.1 ) || ( < 2.1.1 && >= 1.1.1 )": {+ "distroPKGs": [+ "g",+ "ui",+ "onqc"+ ],+ "notes": ""+ },+ "( <= 1.1.1 && > 2.2.1 && == 2.2.1 )": {+ "distroPKGs": [+ "",+ "j",+ "",+ "rqn"+ ],+ "notes": "b"+ },+ "( <= 1.1.2 && < 1.1.1 && < 1.1.2 && == 1.3.1 )": {+ "distroPKGs": [+ "",+ "e",+ "assp",+ "tt"+ ],+ "notes": "omp"+ },+ "( <= 1.1.3 && >= 1.1.1 ) || ( >= 1.1.1 && <= 1.1.1 )": {+ "distroPKGs": [+ "gvi",+ "",+ "ay"+ ],+ "notes": "lnvt"+ },+ "( == 1.2.1 && < 2.1.2 )": {+ "distroPKGs": [+ "wami"+ ],+ "notes": ""+ },+ "unknown_versioning": {+ "distroPKGs": [+ "",+ "owpc"+ ],+ "notes": "yje"+ }+ }+ }+ },+ "f\u0002": {+ "3.6.2": {},+ "5.5.3": {},+ "6.8.7": {+ "Darwin": {+ "( < 2.2.1 && <= 2.2.2 )": {+ "distroPKGs": [+ "de",+ "ox"+ ],+ "notes": ""+ },+ "unknown_versioning": {+ "distroPKGs": [+ "u",+ "vpps",+ "tret",+ "w"+ ],+ "notes": "ppt"+ }+ },+ "FreeBSD": {+ "( < 1.2.2 && <= 2.1.2 && <= 1.2.1 && < 1.2.1 )": {+ "distroPKGs": [+ ""+ ],+ "notes": "fim"+ },+ "( <= 1.1.1 && >= 1.1.1 && > 1.2.1 ) || ( < 2.1.1 ) || ( == 1.1.1 ) || ( > 1.1.1 )": {+ "distroPKGs": [],+ "notes": "ej"+ },+ "( == 1.2.1 && <= 1.1.2 && < 1.2.1 ) || ( < 1.1.1 ) || ( == 1.1.1 ) || ( < 2.1.1 )": {+ "distroPKGs": [+ "tu",+ "",+ "j",+ "tv"+ ],+ "notes": "drq"+ },+ "( >= 1.1.1 && >= 1.1.1 ) || ( < 2.1.1 && > 1.1.1 ) || ( <= 2.1.1 ) || ( < 2.1.1 )": {+ "distroPKGs": [+ "fv"+ ],+ "notes": "c"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "g",+ "srvi"+ ],+ "notes": "elio"+ }+ }+ },+ "unknown_version": {+ "FreeBSD": {},+ "OpenBSD": {+ "( <= 1.2.1 && <= 2.2.2 )": {+ "distroPKGs": [+ "oedy",+ "kss"+ ],+ "notes": "ljv"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "",+ "",+ "am"+ ],+ "notes": "tc"+ }+ }+ }+ },+ "xe&": {+ "1.3.1": {+ "FreeBSD": {+ "( <= 1.1.1 ) || ( <= 1.2.1 ) || ( > 1.2.2 )": {+ "distroPKGs": [+ "he",+ ""+ ],+ "notes": "jqi"+ }+ },+ "OpenBSD": {+ "( < 2.1.1 && >= 2.2.2 && < 1.2.2 )": {+ "distroPKGs": [+ "xesg",+ "ka",+ "igs"+ ],+ "notes": "s"+ },+ "( <= 1.2.3 && <= 3.4.1 )": {+ "distroPKGs": [+ "",+ "yfwo"+ ],+ "notes": "lk"+ },+ "( > 1.1.2 )": {+ "distroPKGs": [+ "b"+ ],+ "notes": "o"+ },+ "( > 2.2.1 && > 2.2.2 && >= 2.2.1 && <= 2.2.1 )": {+ "distroPKGs": [+ "gvzo"+ ],+ "notes": "afvr"+ }+ },+ "Windows": {+ "( == 2.1.1 ) || ( == 1.1.1 ) || ( >= 1.1.1 ) || ( <= 2.1.1 )": {+ "distroPKGs": [+ "lyn",+ "ac",+ "",+ ""+ ],+ "notes": "uqj"+ },+ "unknown_versioning": {+ "distroPKGs": [+ "uzi"+ ],+ "notes": ""+ }+ }+ },+ "1.5.4": {+ "Linux_Void": {}+ },+ "5.8.6": {+ "Windows": {+ "( < 1.1.1 ) || ( >= 1.1.1 && >= 2.1.1 )": {+ "distroPKGs": [+ "cl"+ ],+ "notes": ""+ },+ "( == 2.2.1 && <= 2.1.2 && <= 2.1.2 )": {+ "distroPKGs": [+ "lhvn",+ "aduq",+ "rdoh"+ ],+ "notes": "z"+ },+ "( >= 1.1.1 ) || ( == 1.1.1 ) || ( > 2.1.1 )": {+ "distroPKGs": [+ "hma",+ "i",+ ""+ ],+ "notes": ""+ },+ "( >= 2.1.1 && <= 1.1.1 && < 1.1.1 ) || ( > 1.1.1 && > 1.1.1 ) || ( >= 1.1.2 ) || ( > 2.1.1 )": {+ "distroPKGs": [+ "mk",+ "",+ "lg",+ "zp"+ ],+ "notes": "k"+ }+ }+ },+ "unknown_version": {+ "Darwin": {+ "( >= 1.1.1 )": {+ "distroPKGs": [+ "e"+ ],+ "notes": "uk"+ }+ },+ "FreeBSD": {+ "( <= 2.3.2 && > 1.2.1 )": {+ "distroPKGs": [],+ "notes": "gz"+ },+ "( == 1.2.2 )": {+ "distroPKGs": [+ "jb",+ "wzuq",+ "jjis"+ ],+ "notes": ""+ },+ "( > 3.2.2 )": {+ "distroPKGs": [+ "ncdi",+ "m"+ ],+ "notes": "c"+ },+ "( >= 1.1.1 ) || ( > 1.1.1 )": {+ "distroPKGs": [+ "yzmz",+ "",+ "yn",+ "u"+ ],+ "notes": "imjq"+ },+ "( >= 2.1.2 )": {+ "distroPKGs": [+ "rz",+ ""+ ],+ "notes": "ahhy"+ },+ "( >= 3.2.2 )": {+ "distroPKGs": [+ "xwyt"+ ],+ "notes": ""+ }+ },+ "Linux_Alpine": {+ "( <= 1.1.1 && >= 2.1.1 ) || ( == 1.2.1 )": {+ "distroPKGs": [],+ "notes": "wbh"+ },+ "( <= 1.2.1 && < 1.1.1 && >= 1.1.1 ) || ( < 1.1.1 )": {+ "distroPKGs": [+ ""+ ],+ "notes": "i"+ },+ "( <= 2.1.1 && >= 1.2.2 && >= 1.2.1 ) || ( < 2.1.1 )": {+ "distroPKGs": [],+ "notes": "zyyh"+ },+ "( == 2.1.1 ) || ( > 1.1.1 ) || ( <= 1.1.2 ) || ( < 2.1.2 )": {+ "distroPKGs": [],+ "notes": "ael"+ },+ "( == 2.1.2 && < 1.2.2 && < 1.2.1 ) || ( == 1.1.1 )": {+ "distroPKGs": [+ "i"+ ],+ "notes": "ob"+ },+ "( > 2.2.1 && < 4.2.2 )": {+ "distroPKGs": [+ "db"+ ],+ "notes": "embx"+ },+ "unknown_versioning": {+ "distroPKGs": [],+ "notes": "ol"+ }+ },+ "Linux_Ubuntu": {+ "( <= 1.2.2 && < 2.1.1 && > 2.2.3 && <= 2.3.2 )": {+ "distroPKGs": [],+ "notes": "xlyq"+ },+ "( > 2.2.1 && >= 1.1.1 )": {+ "distroPKGs": [+ "",+ "pu",+ "m",+ "uwo"+ ],+ "notes": ""+ }+ },+ "OpenBSD": {+ "( < 1.1.1 && <= 1.2.2 && >= 1.1.1 ) || ( < 1.1.1 )": {+ "distroPKGs": [+ "nki"+ ],+ "notes": "mozu"+ },+ "( < 1.1.3 && <= 2.1.1 && <= 1.2.1 ) || ( >= 1.1.1 && > 2.1.1 )": {+ "distroPKGs": [+ "by",+ "hfgv"+ ],+ "notes": "e"+ },+ "( == 1.1.2 ) || ( > 1.1.2 && >= 1.2.1 )": {+ "distroPKGs": [+ "k",+ "e"+ ],+ "notes": "un"+ },+ "( > 2.1.1 ) || ( >= 1.1.1 )": {+ "distroPKGs": [],+ "notes": ""+ }+ },+ "Windows": {+ "( == 2.1.3 )": {+ "distroPKGs": [+ "of",+ ""+ ],+ "notes": "g"+ }+ }+ }+ }+ }+ }+ ],+ "seed": 1832749699 }
test/optparse-test/ChangeLogTest.hs view
@@ -23,23 +23,21 @@ checkList = [ ("changelog", ChangeLogOptions False Nothing Nothing) , ("changelog -o", ChangeLogOptions True Nothing Nothing)- , ("changelog -t ghc", ChangeLogOptions False (Just GHC) Nothing)- , ("changelog -t cabal", ChangeLogOptions False (Just Cabal) Nothing)- , ("changelog -t hls", ChangeLogOptions False (Just HLS) Nothing)- , ("changelog -t stack", ChangeLogOptions False (Just Stack) Nothing)- , ("changelog -t ghcup", ChangeLogOptions False (Just GHCup) Nothing)+ , ("changelog -t ghc", ChangeLogOptions False (Just ghc) Nothing)+ , ("changelog -t cabal", ChangeLogOptions False (Just cabal) Nothing)+ , ("changelog -t hls", ChangeLogOptions False (Just hls) Nothing)+ , ("changelog -t stack", ChangeLogOptions False (Just stack) Nothing)+ , ("changelog -t ghcup", ChangeLogOptions False (Just ghcup) Nothing) , ("changelog 9.2", ChangeLogOptions False Nothing (Just $ GHCVersion- $ GHCTargetVersion- Nothing+ $ toTargetVersionReq'' $(versionQ "9.2")) ) , ("changelog recommended", ChangeLogOptions False Nothing (Just $ ToolTag Recommended))- , ("changelog -t cabal recommended", ChangeLogOptions False (Just Cabal) (Just $ ToolTag Recommended))- , ("changelog -t cabal 3.10.1.0", ChangeLogOptions False (Just Cabal)+ , ("changelog -t cabal recommended", ChangeLogOptions False (Just cabal) (Just $ ToolTag Recommended))+ , ("changelog -t cabal 3.10.1.0", ChangeLogOptions False (Just cabal) (Just $ GHCVersion- $ GHCTargetVersion- Nothing+ $ toTargetVersionReq'' $(versionQ "3.10.1.0")) ) , ("changelog 2023-07-22", ChangeLogOptions False Nothing (Just (ToolDay (read "2023-07-22"))))
test/optparse-test/CompileTest.hs view
@@ -13,8 +13,8 @@ import URI.ByteString.QQ import qualified GHCup.OptParse.Compile as GHC (GHCCompileOptions(..)) import qualified GHCup.OptParse.Compile as HLS (HLSCompileOptions(..))-import GHCup.GHC as GHC-import GHCup.HLS as HLS+import GHCup.Command.Compile.GHC as GHC+import GHCup.Command.Compile.HLS as HLS compileTests :: TestTree@@ -40,7 +40,7 @@ Nothing Nothing Nothing- "install"+ Nothing mkDefaultHLSCompileOptions :: HLSVer -> [ToolVersion] -> HLSCompileOptions mkDefaultHLSCompileOptions target ghcs =@@ -204,7 +204,7 @@ [ghc928] ghc928 :: ToolVersion- ghc928 = GHCVersion $ GHCTargetVersion Nothing $(versionQ "9.2.8")+ ghc928 = GHCVersion $ toTargetVersionReq'' $(versionQ "9.2.8") compileParseWith :: [String] -> IO CompileCommand compileParseWith args = do
test/optparse-test/ConfigTest.hs view
@@ -48,6 +48,24 @@ , AddReleaseChannel False (NewChannelAlias VanillaChannel) ) , ("config set cache true", SetConfig "cache" (Just "true"))+ , ("config reset all", ResetConfig ResetAll)+ , ("config reset keys cache downloader", ResetConfig (ResetKeys ["cache", "downloader"]))+ , ("config reset keys cache", ResetConfig (ResetKeys ["cache"]))+ , ("config reset keys meta-cache", ResetConfig (ResetKeys ["meta-cache"]))+ , ("config reset keys meta-mode", ResetConfig (ResetKeys ["meta-mode"]))+ , ("config reset keys no-verify", ResetConfig (ResetKeys ["no-verify"]))+ , ("config reset keys verbose", ResetConfig (ResetKeys ["verbose"]))+ , ("config reset keys keep-dirs", ResetConfig (ResetKeys ["keep-dirs"]))+ , ("config reset keys downloader", ResetConfig (ResetKeys ["downloader"]))+ , ("config reset keys key-bindings", ResetConfig (ResetKeys ["key-bindings"]))+ , ("config reset keys url-source", ResetConfig (ResetKeys ["url-source"]))+ , ("config reset keys no-network", ResetConfig (ResetKeys ["no-network"]))+ , ("config reset keys gpg-setting", ResetConfig (ResetKeys ["gpg-setting"]))+ , ("config reset keys platform-override", ResetConfig (ResetKeys ["platform-override"]))+ , ("config reset keys mirrors", ResetConfig (ResetKeys ["mirrors"]))+ , ("config reset keys def-ghc-conf-options", ResetConfig (ResetKeys ["def-ghc-conf-options"]))+ , ("config reset keys pager", ResetConfig (ResetKeys ["pager"]))+ , ("config reset keys guess-version", ResetConfig (ResetKeys ["guess-version"])) ] configParseWith :: [String] -> IO ConfigCommand
test/optparse-test/InstallTest.hs view
@@ -2,6 +2,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE DuplicateRecordFields #-} module InstallTest where @@ -11,8 +12,6 @@ import GHCup.Types import Data.Versions import Data.List.NonEmpty (NonEmpty ((:|)))-import GHCup.OptParse.Install as Install-import URI.ByteString.QQ -- Some interests: -- install ghc *won't* select `set as activate version` as default@@ -24,180 +23,101 @@ installTests = testGroup "install" $ map (buildTestTree installParseWith)- [ ("old-style", oldStyleCheckList)- , ("ghc", installGhcCheckList)+ [ ("ghc", installGhcCheckList) , ("cabal", installCabalCheckList) , ("hls", installHlsCheckList) , ("stack", installStackCheckList) ] defaultOptions :: InstallOptions-defaultOptions = InstallOptions Nothing Nothing False Nothing False "install" []+defaultOptions = InstallOptions Nothing Nothing False Nothing False Nothing [] -- | Don't set as active version mkInstallOptions :: ToolVersion -> InstallOptions-mkInstallOptions ver = InstallOptions (Just ver) Nothing False Nothing False "install" []+mkInstallOptions ver = InstallOptions (Just ver) Nothing False Nothing False Nothing [] -- | Set as active version mkInstallOptions' :: ToolVersion -> InstallOptions-mkInstallOptions' ver = InstallOptions (Just ver) Nothing True Nothing False "install" []--oldStyleCheckList :: [(String, Either InstallCommand InstallOptions)]-oldStyleCheckList =- ("install", Right defaultOptions)- : ("install --set", Right defaultOptions{instSet = True})- : ("install --force", Right defaultOptions{forceInstall = True})-#ifdef IS_WINDOWS- : ("install -i C:\\\\", Right defaultOptions{Install.isolateDir = Just "C:\\"})-#else- : ("install -i /", Right defaultOptions{Install.isolateDir = Just "/"})-#endif- : ("install -u https://gitlab.haskell.org/ghc/ghc/-/jobs/artifacts/master/raw/ghc-x86_64-linux-fedora33-release.tar.xz head"- , Right defaultOptions- { instBindist = Just [uri|https://gitlab.haskell.org/ghc/ghc/-/jobs/artifacts/master/raw/ghc-x86_64-linux-fedora33-release.tar.xz|]- , instVer = Just $ GHCVersion $ GHCTargetVersion Nothing $(versionQ "head")- }- )- : mapSecond- (Right . mkInstallOptions)- [ ("install ghc-9.2", GHCVersion- $ GHCTargetVersion- (Just "ghc")- $(versionQ "9.2")- )- -- invalid- , ("install next", GHCVersion- $ GHCTargetVersion- Nothing- $(versionQ "next")- )- , ("install latest", ToolTag Latest)- , ("install nightly", GHCVersion- $ GHCTargetVersion- Nothing- $(versionQ "nightly")- )- , ("install recommended", ToolTag Recommended)- , ("install prerelease", GHCVersion- $ GHCTargetVersion- Nothing- $(versionQ "prerelease")- )- , ("install latest-prerelease", ToolTag LatestPrerelease)- , ("install latest-nightly", ToolTag LatestNightly)- , ("install ghc-javascript-unknown-ghcjs-9.6", GHCVersion- $ GHCTargetVersion- (Just "ghc-javascript-unknown-ghcjs")- $(versionQ "9.6")- )- , ("install base-4.18", ToolTag (Base (PVP {_pComponents = 4 :| [18]})))- , ("install cabal-3.10", GHCVersion- $ GHCTargetVersion- (Just "cabal")- $(versionQ "3.10")- )- , ("install hls-2.0.0.0", GHCVersion- $ GHCTargetVersion- (Just "hls")- $(versionQ "2.0.0.0")- )- , ("install stack-2.9.3", GHCVersion- $ GHCTargetVersion- (Just "stack")- $(versionQ "2.9.3")- )- ]+mkInstallOptions' ver = InstallOptions (Just ver) Nothing True Nothing False Nothing [] -installGhcCheckList :: [(String, Either InstallCommand InstallOptions)]+installGhcCheckList :: [(String, InstallCommand)] installGhcCheckList =- ("install ghc", Left $ InstallGHC defaultOptions)- : mapSecond (Left . InstallGHC . mkInstallOptions)+ ("install ghc", InstallGHC defaultOptions)+ : mapSecond (InstallGHC . mkInstallOptions) [ ("install ghc 9.2", GHCVersion- $ GHCTargetVersion- Nothing+ $ toTargetVersionReq'' $(versionQ "9.2") ) , ("install ghc next", GHCVersion- $ GHCTargetVersion- Nothing+ $ toTargetVersionReq'' $(versionQ "next") ) , ("install ghc latest", ToolTag Latest) , ("install ghc nightly", GHCVersion- $ GHCTargetVersion- Nothing+ $ toTargetVersionReq'' $(versionQ "nightly") ) , ("install ghc recommended", ToolTag Recommended) , ("install ghc prerelease", GHCVersion- $ GHCTargetVersion- Nothing+ $ toTargetVersionReq'' $(versionQ "prerelease") ) , ("install ghc latest-prerelease", ToolTag LatestPrerelease) , ("install ghc latest-nightly", ToolTag LatestNightly) , ("install ghc javascript-unknown-ghcjs-9.6", GHCVersion- $ GHCTargetVersion+ $ (`TargetVersionReq` Nothing) $ TargetVersion (Just "javascript-unknown-ghcjs") $(versionQ "9.6") ) , ("install ghc base-4.18", ToolTag (Base (PVP {_pComponents = 4 :| [18]})))- , ("install ghc ghc-9.2", GHCVersion- $ GHCTargetVersion- (Just "ghc")- $(versionQ "9.2")- ) ] -installCabalCheckList :: [(String, Either InstallCommand InstallOptions)]+installCabalCheckList :: [(String, InstallCommand)] installCabalCheckList =- ("install cabal", Left $ InstallCabal defaultOptions{instSet = True})- : mapSecond (Left . InstallCabal . mkInstallOptions')- [ ("install cabal 3.10", ToolVersion $(versionQ "3.10"))- , ("install cabal next", ToolVersion $(versionQ "next"))+ ("install cabal", InstallCabal (defaultOptions{ instSet = True } :: InstallOptions))+ : mapSecond (InstallCabal . mkInstallOptions')+ [ ("install cabal 3.10", ToolVersion $ (`VersionReq` Nothing) $(versionQ "3.10"))+ , ("install cabal next", ToolVersion $ (`VersionReq` Nothing) $(versionQ "next")) , ("install cabal latest", ToolTag Latest)- , ("install cabal nightly", ToolVersion $(versionQ "nightly"))+ , ("install cabal nightly", ToolVersion $ (`VersionReq` Nothing) $(versionQ "nightly")) , ("install cabal recommended", ToolTag Recommended)- , ("install cabal prerelease", ToolVersion $(versionQ "prerelease"))+ , ("install cabal prerelease", ToolVersion $ (`VersionReq` Nothing) $(versionQ "prerelease")) , ("install cabal latest-prerelease", ToolTag LatestPrerelease) , ("install cabal latest-nightly", ToolTag LatestNightly) , ("install cabal base-4.18", ToolTag (Base (PVP {_pComponents = 4 :| [18]})))- , ("install cabal cabal-3.10", ToolVersion $(versionQ "cabal-3.10")) ] -installHlsCheckList :: [(String, Either InstallCommand InstallOptions)]+installHlsCheckList :: [(String, InstallCommand)] installHlsCheckList =- ("install hls", Left $ InstallHLS defaultOptions{instSet = True})- : mapSecond (Left . InstallHLS . mkInstallOptions')- [ ("install hls 3.10", ToolVersion $(versionQ "3.10"))- , ("install hls next", ToolVersion $(versionQ "next"))+ ("install hls", InstallHLS defaultOptions{instSet = True})+ : mapSecond (InstallHLS . mkInstallOptions')+ [ ("install hls 3.10", ToolVersion $ (`VersionReq` Nothing) $(versionQ "3.10"))+ , ("install hls next", ToolVersion $ (`VersionReq` Nothing) $(versionQ "next")) , ("install hls latest", ToolTag Latest)- , ("install hls nightly", ToolVersion $(versionQ "nightly"))+ , ("install hls nightly", ToolVersion $ (`VersionReq` Nothing) $(versionQ "nightly")) , ("install hls recommended", ToolTag Recommended)- , ("install hls prerelease", ToolVersion $(versionQ "prerelease"))+ , ("install hls prerelease", ToolVersion $ (`VersionReq` Nothing) $(versionQ "prerelease")) , ("install hls latest-prerelease", ToolTag LatestPrerelease) , ("install hls latest-nightly", ToolTag LatestNightly) , ("install hls base-4.18", ToolTag (Base (PVP {_pComponents = 4 :| [18]})))- , ("install hls hls-2.0", ToolVersion $(versionQ "hls-2.0")) ] -installStackCheckList :: [(String, Either InstallCommand InstallOptions)]+installStackCheckList :: [(String, InstallCommand)] installStackCheckList =- ("install stack", Left $ InstallStack defaultOptions{instSet = True})- : mapSecond (Left . InstallStack . mkInstallOptions')- [ ("install stack 3.10", ToolVersion $(versionQ "3.10"))- , ("install stack next", ToolVersion $(versionQ "next"))+ ("install stack", InstallStack defaultOptions{instSet = True})+ : mapSecond (InstallStack . mkInstallOptions')+ [ ("install stack 3.10", ToolVersion $ (`VersionReq` Nothing) $(versionQ "3.10"))+ , ("install stack next", ToolVersion $ (`VersionReq` Nothing) $(versionQ "next")) , ("install stack latest", ToolTag Latest)- , ("install stack nightly", ToolVersion $(versionQ "nightly"))+ , ("install stack nightly", ToolVersion $ (`VersionReq` Nothing) $(versionQ "nightly")) , ("install stack recommended", ToolTag Recommended)- , ("install stack prerelease", ToolVersion $(versionQ "prerelease"))+ , ("install stack prerelease", ToolVersion $ (`VersionReq` Nothing) $(versionQ "prerelease")) , ("install stack latest-prerelease", ToolTag LatestPrerelease) , ("install stack latest-nightly", ToolTag LatestNightly) , ("install stack base-4.18", ToolTag (Base (PVP {_pComponents = 4 :| [18]})))- , ("install stack stack-2.9", ToolVersion $(versionQ "stack-2.9")) ] -installParseWith :: [String] -> IO (Either InstallCommand InstallOptions)+installParseWith :: [String] -> IO InstallCommand installParseWith args = do Install a <- parseWith args pure a
test/optparse-test/ListTest.hs view
@@ -3,7 +3,7 @@ import Test.Tasty import GHCup.OptParse import Utils-import GHCup.List+import GHCup.Command.List import GHCup.Types @@ -11,33 +11,34 @@ listTests = buildTestTree listParseWith ("list", listCheckList) defaultOptions :: ListOptions-defaultOptions = ListOptions Nothing Nothing Nothing Nothing False False False+defaultOptions = ListOptions Nothing [] Nothing Nothing False False False ShowUpdates listCheckList :: [(String, ListOptions)] listCheckList =- [ ("list", defaultOptions)- , ("list -t ghc", defaultOptions{loTool = Just GHC})- , ("list -t cabal", defaultOptions{loTool = Just Cabal})- , ("list -t hls", defaultOptions{loTool = Just HLS})- , ("list -t stack", defaultOptions{loTool = Just Stack})- , ("list -c installed", defaultOptions{lCriteria = Just $ ListInstalled True})- , ("list -c +installed", defaultOptions{lCriteria = Just $ ListInstalled True})- , ("list -c -installed", defaultOptions{lCriteria = Just $ ListInstalled False})- , ("list -c set", defaultOptions{lCriteria = Just $ ListSet True})- , ("list -c +set", defaultOptions{lCriteria = Just $ ListSet True})- , ("list -c -set", defaultOptions{lCriteria = Just $ ListSet False})- , ("list -c available", defaultOptions{lCriteria = Just $ ListAvailable True})- , ("list -c +available", defaultOptions{lCriteria = Just $ ListAvailable True})- , ("list -c -available", defaultOptions{lCriteria = Just $ ListAvailable False})- , ("list -s 2023-07-22", defaultOptions{lFrom = Just $ read "2023-07-22"})- , ("list -u 2023-07-22", defaultOptions{lTo = Just $ read "2023-07-22"})- , ("list --since 2023-07-22 --until 2023-07-22", defaultOptions{lFrom = Just $ read "2023-07-22", lTo = Just $ read "2023-07-22"})- , ("list -o", defaultOptions{lHideOld = True})- , ("list --hide-old", defaultOptions{lHideOld = True})- , ("list -n", defaultOptions{lShowNightly = True})+ [ ("list", defaultOptions)+ , ("list -t ghc", defaultOptions{loTool = Just [ghc]})+ , ("list -t cabal", defaultOptions{loTool = Just [cabal]})+ , ("list -t hls", defaultOptions{loTool = Just [hls]})+ , ("list -t stack", defaultOptions{loTool = Just [stack]})+ , ("list -c installed", defaultOptions{lCriteria = [ListInstalled True]})+ , ("list -c +installed", defaultOptions{lCriteria = [ListInstalled True]})+ , ("list -c -installed", defaultOptions{lCriteria = [ListInstalled False]})+ , ("list -c set", defaultOptions{lCriteria = [ListSet True]})+ , ("list -c +set", defaultOptions{lCriteria = [ListSet True]})+ , ("list -c -set", defaultOptions{lCriteria = [ListSet False]})+ , ("list -c available", defaultOptions{lCriteria = [ListAvailable True]})+ , ("list -c +available", defaultOptions{lCriteria = [ListAvailable True]})+ , ("list -c -available", defaultOptions{lCriteria = [ListAvailable False]})+ , ("list -s 2023-07-22", defaultOptions{lFrom = Just $ read "2023-07-22"})+ , ("list -u 2023-07-22", defaultOptions{lTo = Just $ read "2023-07-22"})+ , ("list --since 2023-07-22 --until 2023-07-22",+ defaultOptions{lFrom = Just $ read "2023-07-22", lTo = Just $ read "2023-07-22"})+ , ("list -o", defaultOptions{lHideOld = True})+ , ("list --hide-old", defaultOptions{lHideOld = True})+ , ("list -n", defaultOptions{lShowNightly = True}) , ("list --show-nightly", defaultOptions{lShowNightly = True})- , ("list -r", defaultOptions{lRawFormat = True})- , ("list --raw-format", defaultOptions{lRawFormat = True})+ , ("list -r", defaultOptions{lRawFormat = True})+ , ("list --raw-format", defaultOptions{lRawFormat = True}) ] listParseWith :: [String] -> IO ListOptions
test/optparse-test/RmTest.hs view
@@ -14,49 +14,37 @@ rmTests = testGroup "rm" $ map (buildTestTree rmParseWith)- [ ("old-style", oldStyleCheckList)- , ("ghc", rmGhcCheckList)+ [ ("ghc", rmGhcCheckList) , ("cabal", rmCabalCheckList) , ("hls", rmHlsCheckList) , ("stack", rmStackCheckList) ] -oldStyleCheckList :: [(String, Either RmCommand RmOptions)]-oldStyleCheckList = mapSecond (Right . RmOptions)- [ -- failed with ("rm", xxx)- ("rm 9.2.8", mkTVer $(versionQ "9.2.8"))- , ("rm ghc-9.2.8", GHCTargetVersion (Just "ghc") $(versionQ "9.2.8"))- ]--rmGhcCheckList :: [(String, Either RmCommand RmOptions)]-rmGhcCheckList = mapSecond (Left . RmGHC . RmOptions)+rmGhcCheckList :: [(String, RmCommand)]+rmGhcCheckList = mapSecond (RmGHC . RmOptions) [ -- failed with ("rm ghc", xxx) ("rm ghc 9.2.8", mkTVer $(versionQ "9.2.8"))- , ("rm ghc ghc-9.2.8", GHCTargetVersion (Just "ghc") $(versionQ "9.2.8")) ] -rmCabalCheckList :: [(String, Either RmCommand RmOptions)]-rmCabalCheckList = mapSecond (Left . RmCabal)+rmCabalCheckList :: [(String, RmCommand)]+rmCabalCheckList = mapSecond RmCabal [ -- failed with ("rm cabal", xxx) ("rm cabal 3.10", $(versionQ "3.10"))- , ("rm cabal cabal-3.10", $(versionQ "cabal-3.10")) ] -rmHlsCheckList :: [(String, Either RmCommand RmOptions)]-rmHlsCheckList = mapSecond (Left . RmHLS)+rmHlsCheckList :: [(String, RmCommand)]+rmHlsCheckList = mapSecond RmHLS [ -- failed with ("rm hls", xxx) ("rm hls 2.0", $(versionQ "2.0"))- , ("rm hls hls-2.0", $(versionQ "hls-2.0")) ] -rmStackCheckList :: [(String, Either RmCommand RmOptions)]-rmStackCheckList = mapSecond (Left . RmStack)+rmStackCheckList :: [(String, RmCommand)]+rmStackCheckList = mapSecond RmStack [ -- failed with ("rm stack", xxx) ("rm stack 2.9.1", $(versionQ "2.9.1"))- , ("rm stack stack-2.9.1", $(versionQ "stack-2.9.1")) ] -rmParseWith :: [String] -> IO (Either RmCommand RmOptions)+rmParseWith :: [String] -> IO RmCommand rmParseWith args = do Rm a <- parseWith args pure a
test/optparse-test/RunTest.hs view
@@ -24,6 +24,7 @@ Nothing Nothing Nothing+ [] Nothing False []@@ -37,11 +38,11 @@ , ("run --install", defaultOptions{runInstTool' = True}) , ("run -m", defaultOptions{runMinGWPath = True}) , ("run --mingw-path", defaultOptions{runMinGWPath = True})- , ("run --ghc 9.2.8", defaultOptions{runGHCVer = Just $ GHCVersion $ mkTVer $(versionQ "9.2.8")})+ , ("run --ghc 9.2.8", defaultOptions{runGHCVer = Just $ GHCVersion $ (`TargetVersionReq` Nothing) $ mkTVer $(versionQ "9.2.8")}) , ("run --ghc latest", defaultOptions{runGHCVer = Just $ ToolTag Latest})- , ("run --cabal 3.10", defaultOptions{runCabalVer = Just $ ToolVersion $(versionQ "3.10")})- , ("run --hls 2.0", defaultOptions{runHLSVer = Just $ ToolVersion $(versionQ "2.0")})- , ("run --stack 2.9", defaultOptions{runStackVer = Just $ ToolVersion $(versionQ "2.9") })+ , ("run --cabal 3.10", defaultOptions{runCabalVer = Just $ ToolVersion $ (`VersionReq` Nothing) $(versionQ "3.10")})+ , ("run --hls 2.0", defaultOptions{runHLSVer = Just $ ToolVersion $ (`VersionReq` Nothing) $(versionQ "2.0")})+ , ("run --stack 2.9", defaultOptions{runStackVer = Just $ ToolVersion $ (`VersionReq` Nothing) $(versionQ "2.9") }) #ifdef IS_WINDOWS , ("run -b C:\\\\tmp\\dir", defaultOptions{runBinDir = Just "C:\\tmp\\dir"}) , ("run --bindir C:\\\\tmp\\dir", defaultOptions{runBinDir = Just "C:\\tmp\\dir"})@@ -54,9 +55,9 @@ , ("run --ghc latest --cabal 3.10 --stack 2.9 --hls 2.0 --install", defaultOptions { runGHCVer = Just $ ToolTag Latest- , runCabalVer = Just $ ToolVersion $(versionQ "3.10")- , runHLSVer = Just $ ToolVersion $(versionQ "2.0")- , runStackVer = Just $ ToolVersion $(versionQ "2.9")+ , runCabalVer = Just $ ToolVersion $ (`VersionReq` Nothing) $(versionQ "3.10")+ , runHLSVer = Just $ ToolVersion $ (`VersionReq` Nothing) $(versionQ "2.0")+ , runStackVer = Just $ ToolVersion $ (`VersionReq` Nothing) $(versionQ "2.9") , runInstTool' = True } )
test/optparse-test/SetTest.hs view
@@ -4,7 +4,7 @@ module SetTest where import GHCup.OptParse-import GHCup.Utils.Parsers (SetToolVersion(..))+import GHCup.Input.Parsers (SetToolVersion(..)) import Test.Tasty import GHCup.Types import Data.Versions@@ -16,142 +16,90 @@ testGroup "set" $ map (buildTestTree setParseWith)- [ ("old-style", oldStyleCheckList)- , ("ghc", setGhcCheckList)+ [ ("ghc", setGhcCheckList) , ("cabal", setCabalCheckList) , ("hls", setHlsCheckList) , ("stack", setStackCheckList) ] -oldStyleCheckList :: [(String, Either SetCommand SetOptions)]-oldStyleCheckList = mapSecond (Right . SetOptions)- [ ("set", SetRecommended)- , ("set ghc-9.2", SetGHCVersion- $ GHCTargetVersion- (Just "ghc")- $(versionQ "9.2")- )- , ("set next", SetNext)- , ("set latest", SetToolTag Latest)- , ("set nightly", SetGHCVersion- $ GHCTargetVersion- Nothing- $(versionQ "nightly")- )- -- different from `set`- , ("set recommended", SetToolTag Recommended)- , ("set prerelease", SetGHCVersion- $ GHCTargetVersion- Nothing- $(versionQ "prerelease")- )- , ("set latest-prerelease", SetToolTag LatestPrerelease)- , ("set latest-nightly", SetToolTag LatestNightly)- , ("set ghc-javascript-unknown-ghcjs-9.6", SetGHCVersion- $ GHCTargetVersion- (Just "ghc-javascript-unknown-ghcjs")- $(versionQ "9.6")- )- , ("set base-4.18", SetToolTag (Base (PVP {_pComponents = 4 :| [18]})))- , ("set cabal-3.10", SetGHCVersion- $ GHCTargetVersion- (Just "cabal")- $(versionQ "3.10")- )- , ("set hls-2.0.0.0", SetGHCVersion- $ GHCTargetVersion- (Just "hls")- $(versionQ "2.0.0.0")- )- , ("set stack-2.9.3", SetGHCVersion- $ GHCTargetVersion- (Just "stack")- $(versionQ "2.9.3")- )- ]--setGhcCheckList :: [(String, Either SetCommand SetOptions)]-setGhcCheckList = mapSecond (Left . SetGHC . SetOptions)+setGhcCheckList :: [(String, SetCommand)]+setGhcCheckList = mapSecond (SetGHC . SetOptions) [ ("set ghc", SetRecommended) , ("set ghc 9.2", SetGHCVersion- $ GHCTargetVersion+ $ (`TargetVersionReq` Nothing) $ TargetVersion Nothing $(versionQ "9.2") ) , ("set ghc next", SetNext) , ("set ghc latest", SetToolTag Latest) , ("set ghc nightly", SetGHCVersion- $ GHCTargetVersion+ $ (`TargetVersionReq` Nothing) $ TargetVersion Nothing $(versionQ "nightly") ) , ("set ghc recommended", SetToolTag Recommended) , ("set ghc prerelease", SetGHCVersion- $ GHCTargetVersion+ $ (`TargetVersionReq` Nothing) $ TargetVersion Nothing $(versionQ "prerelease") ) , ("set ghc latest-prerelease", SetToolTag LatestPrerelease) , ("set ghc latest-nightly", SetToolTag LatestNightly) , ("set ghc javascript-unknown-ghcjs-9.6", SetGHCVersion- $ GHCTargetVersion+ $ (`TargetVersionReq` Nothing) $ TargetVersion (Just "javascript-unknown-ghcjs") $(versionQ "9.6") ) , ("set ghc base-4.18", SetToolTag (Base (PVP {_pComponents = 4 :| [18]})))- , ("set ghc ghc-9.2", SetGHCVersion- $ GHCTargetVersion- (Just "ghc")- $(versionQ "9.2")- ) ] -setCabalCheckList :: [(String, Either SetCommand SetOptions)]-setCabalCheckList = mapSecond (Left . SetCabal . SetOptions)+setCabalCheckList :: [(String, SetCommand)]+setCabalCheckList = mapSecond (SetCabal . SetOptions) [ ("set cabal", SetRecommended)- , ("set cabal 3.10", SetToolVersion $(versionQ "3.10"))+ , ("set cabal 3.10", SetToolVersion $ (`VersionReq` Nothing) $(versionQ "3.10")) , ("set cabal next", SetNext) , ("set cabal latest", SetToolTag Latest)- , ("set cabal nightly", SetToolVersion $(versionQ "nightly"))+ , ("set cabal nightly", SetToolVersion $ (`VersionReq` Nothing) $(versionQ "nightly")) , ("set cabal recommended", SetToolTag Recommended)- , ("set cabal prerelease", SetToolVersion $(versionQ "prerelease"))+ , ("set cabal prerelease", SetToolVersion $ (`VersionReq` Nothing) $(versionQ "prerelease")) , ("set cabal latest-prerelease", SetToolTag LatestPrerelease) , ("set cabal latest-nightly", SetToolTag LatestNightly) , ("set cabal base-4.18", SetToolTag (Base (PVP {_pComponents = 4 :| [18]})))- , ("set cabal cabal-3.10", SetToolVersion $(versionQ "cabal-3.10"))+ , ("set cabal cabal-3.10", SetToolVersion $ (`VersionReq` Nothing) $(versionQ "cabal-3.10")) ] -setHlsCheckList :: [(String, Either SetCommand SetOptions)]-setHlsCheckList = mapSecond (Left . SetHLS . SetOptions)+setHlsCheckList :: [(String, SetCommand)]+setHlsCheckList = mapSecond (SetHLS . SetOptions) [ ("set hls", SetRecommended)- , ("set hls 2.0", SetToolVersion $(versionQ "2.0"))+ , ("set hls 2.0", SetToolVersion $ (`VersionReq` Nothing) $(versionQ "2.0")) , ("set hls next", SetNext) , ("set hls latest", SetToolTag Latest)- , ("set hls nightly", SetToolVersion $(versionQ "nightly"))+ , ("set hls nightly", SetToolVersion $ (`VersionReq` Nothing) $(versionQ "nightly")) , ("set hls recommended", SetToolTag Recommended)- , ("set hls prerelease", SetToolVersion $(versionQ "prerelease"))+ , ("set hls prerelease", SetToolVersion $ (`VersionReq` Nothing) $(versionQ "prerelease")) , ("set hls latest-prerelease", SetToolTag LatestPrerelease) , ("set hls latest-nightly", SetToolTag LatestNightly) , ("set hls base-4.18", SetToolTag (Base (PVP {_pComponents = 4 :| [18]})))- , ("set hls hls-2.0", SetToolVersion $(versionQ "hls-2.0"))+ , ("set hls hls-2.0", SetToolVersion $ (`VersionReq` Nothing) $(versionQ "hls-2.0")) ] -setStackCheckList :: [(String, Either SetCommand SetOptions)]-setStackCheckList = mapSecond (Left . SetStack . SetOptions)+setStackCheckList :: [(String, SetCommand)]+setStackCheckList = mapSecond (SetStack . SetOptions) [ ("set stack", SetRecommended)- , ("set stack 2.9", SetToolVersion $(versionQ "2.9"))+ , ("set stack 2.9", SetToolVersion $ (`VersionReq` Nothing) $(versionQ "2.9")) , ("set stack next", SetNext) , ("set stack latest", SetToolTag Latest)- , ("set stack nightly", SetToolVersion $(versionQ "nightly"))+ , ("set stack nightly", SetToolVersion $ (`VersionReq` Nothing) $(versionQ "nightly")) , ("set stack recommended", SetToolTag Recommended)- , ("set stack prerelease", SetToolVersion $(versionQ "prerelease"))+ , ("set stack prerelease", SetToolVersion $ (`VersionReq` Nothing) $(versionQ "prerelease")) , ("set stack latest-prerelease", SetToolTag LatestPrerelease) , ("set stack latest-nightly", SetToolTag LatestNightly) , ("set stack base-4.18", SetToolTag (Base (PVP {_pComponents = 4 :| [18]})))- , ("set stack stack-2.9", SetToolVersion $(versionQ "stack-2.9"))+ , ("set stack stack-2.9", SetToolVersion $ (`VersionReq` Nothing) $(versionQ "stack-2.9")) ] -setParseWith :: [String] -> IO (Either SetCommand SetOptions)+setParseWith :: [String] -> IO SetCommand setParseWith args = do Set a <- parseWith args pure a+
test/optparse-test/WhereisTest.hs view
@@ -14,14 +14,13 @@ whereisCheckList :: [(String, (WhereisOptions, WhereisCommand))] whereisCheckList = concatMap mk- [ ("whereis ghc", WhereisTool GHC Nothing)- , ("whereis ghc 9.2.8", WhereisTool GHC (Just $ GHCVersion $ mkTVer $(versionQ "9.2.8")))- , ("whereis ghc ghc-9.2.8", WhereisTool GHC (Just $ GHCVersion $ GHCTargetVersion (Just "ghc") $(versionQ "9.2.8")))- , ("whereis ghc latest", WhereisTool GHC (Just $ ToolTag Latest))- , ("whereis cabal", WhereisTool Cabal Nothing)- , ("whereis hls", WhereisTool HLS Nothing)- , ("whereis stack", WhereisTool Stack Nothing)- , ("whereis ghcup", WhereisTool GHCup Nothing)+ [ ("whereis ghc", WhereisTool ghc Nothing)+ , ("whereis ghc 9.2.8", WhereisTool ghc (Just $ GHCVersion $ toTargetVersionReq'' $(versionQ "9.2.8")))+ , ("whereis ghc latest", WhereisTool ghc (Just $ ToolTag Latest))+ , ("whereis cabal", WhereisTool cabal Nothing)+ , ("whereis hls", WhereisTool hls Nothing)+ , ("whereis stack", WhereisTool stack Nothing)+ , ("whereis ghcup", WhereisTool ghcup Nothing) , ("whereis basedir", WhereisBaseDir) , ("whereis bindir", WhereisBinDir) , ("whereis cachedir", WhereisCacheDir)@@ -30,10 +29,10 @@ ] where mk :: (String, WhereisCommand) -> [(String, (WhereisOptions, WhereisCommand))]- mk (cmd, res) =- [ (cmd, (WhereisOptions False, res))- , (cmd <> " -d", (WhereisOptions True, res))- , (cmd <> " --directory", (WhereisOptions True, res))+ mk (cmd', res) =+ [ (cmd', (WhereisOptions False, res))+ , (cmd' <> " -d", (WhereisOptions True, res))+ , (cmd' <> " --directory", (WhereisOptions True, res)) ] whereisParseWith :: [String] -> IO (WhereisOptions, WhereisCommand)