Compare commits

...

8 Commits

Author SHA1 Message Date
14168a41fe Lala 2020-08-27 23:39:47 +02:00
6342e8edf0 Use 8.10.2 in gitlab CI 2020-08-15 23:54:50 +02:00
bbd8f0c84c Add GHC-8.10.2 for alpine 32big 2020-08-15 23:34:05 +02:00
873c951d6e Refactor chmod +x 2020-08-14 22:27:05 +02:00
d9c864d3c5 Make sure cabal is executable wrt #46 2020-08-14 22:07:39 +02:00
4280d7109a Fix 3.4.0.0-rc1 urls wrt #46 2020-08-14 21:49:01 +02:00
c8855c068f Update version in bootstrap-haskell 2020-08-14 20:36:14 +02:00
90503061e9 Add ghcup-0.1.10 2020-08-14 20:22:27 +02:00
13 changed files with 319 additions and 198 deletions

View File

@@ -113,7 +113,7 @@ test:linux:recommended:
test:linux:latest: test:linux:latest:
extends: .test_ghcup_version:linux extends: .test_ghcup_version:linux
variables: variables:
GHC_VERSION: "8.10.1" GHC_VERSION: "8.10.2"
CABAL_VERSION: "3.2.0.0" CABAL_VERSION: "3.2.0.0"
allow_failure: true allow_failure: true
@@ -136,7 +136,7 @@ test:mac:recommended:
test:mac:latest: test:mac:latest:
extends: .test_ghcup_version:darwin extends: .test_ghcup_version:darwin
variables: variables:
GHC_VERSION: "8.10.1" GHC_VERSION: "8.10.2"
CABAL_VERSION: "3.2.0.0" CABAL_VERSION: "3.2.0.0"
allow_failure: true allow_failure: true
@@ -152,7 +152,7 @@ test:freebsd:recommended:
test:freebsd:latest: test:freebsd:latest:
extends: .test_ghcup_version:freebsd extends: .test_ghcup_version:freebsd
variables: variables:
GHC_VERSION: "8.10.1" GHC_VERSION: "8.10.2"
CABAL_VERSION: "3.2.0.0" CABAL_VERSION: "3.2.0.0"
allow_failure: true allow_failure: true

View File

@@ -1,6 +1,6 @@
# Revision history for ghcup # Revision history for ghcup
## 0.1.10 -- yyyy-mm-dd ## 0.1.10 -- 2020-08-14
* Show stray Cabals (useful for pre-releases or compiled ones) * Show stray Cabals (useful for pre-releases or compiled ones)

View File

@@ -8,6 +8,7 @@ import GHCup
import GHCup.Download import GHCup.Download
import GHCup.Types import GHCup.Types
import GHCup.Utils.Dirs import GHCup.Utils.Dirs
import GHCup.Utils.File
import GHCup.Utils.Logger import GHCup.Utils.Logger
import GHCup.Utils.Version.QQ import GHCup.Utils.Version.QQ
@@ -192,7 +193,7 @@ validateTarballs dls = do
where where
downloadAll dli = do downloadAll dli = do
dirs <- liftIO getDirs dirs <- liftIO getDirs
let settings = Settings True False Never Curl False dirs let settings = Settings True False Never Curl False dirs defExecCb
let runLogger = myLoggerT LoggerConfig { lcPrintDebug = True let runLogger = myLoggerT LoggerConfig { lcPrintDebug = True
, colorOutter = B.hPut stderr , colorOutter = B.hPut stderr
, rawOutter = (\_ -> pure ()) , rawOutter = (\_ -> pure ())

View File

@@ -14,8 +14,10 @@ import GHCup.Types
import GHCup.Utils import GHCup.Utils
import GHCup.Utils.File import GHCup.Utils.File
import GHCup.Utils.Logger import GHCup.Utils.Logger
import GHCup.Utils.Prelude
import Brick import Brick
import Brick.BChan
import Brick.Widgets.Border import Brick.Widgets.Border
import Brick.Widgets.Border.Style import Brick.Widgets.Border.Style
import Brick.Widgets.Center import Brick.Widgets.Center
@@ -23,11 +25,14 @@ import Brick.Widgets.List
#if !defined(TAR) #if !defined(TAR)
import Codec.Archive import Codec.Archive
#endif #endif
import Control.Concurrent
import Control.Concurrent.MVar
import Control.Exception.Safe import Control.Exception.Safe
import Control.Monad.Logger import Control.Monad.Logger
import Control.Monad.Reader import Control.Monad.Reader
import Control.Monad.Trans.Resource import Control.Monad.Trans.Resource
import Data.Bool import Data.Bool
import Data.ByteString ( ByteString )
import Data.Functor import Data.Functor
import Data.List import Data.List
import Data.Maybe import Data.Maybe
@@ -37,22 +42,40 @@ import Data.String.Interpolate
import Data.Vector ( Vector ) import Data.Vector ( Vector )
import Data.Versions hiding ( str ) import Data.Versions hiding ( str )
import Haskus.Utils.Variant.Excepts import Haskus.Utils.Variant.Excepts
import Prelude hiding ( appendFile ) import HPath
import HPath.IO hiding ( hideError )
import Prelude hiding ( abs, appendFile, writeFile )
import System.Exit import System.Exit
import System.IO.Unsafe import System.IO.Unsafe
import System.Posix.Types
import URI.ByteString import URI.ByteString
import qualified Data.Text as T import qualified Data.Text as T
import qualified Graphics.Vty as Vty import qualified Graphics.Vty as Vty
import qualified Data.Vector as V import qualified Data.Vector as V
import qualified "unix-bytestring" System.Posix.IO.ByteString
as SPIB
data SubProcess = SubProcess {
procName :: String
, exited :: Maybe (Either ProcessError ())
, procId :: Maybe ProcessID
, logLine :: Maybe ByteString
}
data AppState = AppState { data AppState = AppState {
lr :: LR lr :: LR
, dls :: GHCupDownloads , dls :: GHCupDownloads
, pfreq :: PlatformRequest , pfreq :: PlatformRequest
, mproc :: Maybe SubProcess
} }
data MyAppEvent = LogLine ByteString
| StartProc String
| GotProcId ProcessID
| EndProc (Either ProcessError ())
type LR = GenericList String Vector ListResult type LR = GenericList String Vector ListResult
@@ -68,21 +91,28 @@ keyHandlers =
ui :: AppState -> Widget String ui :: AppState -> Widget String
ui AppState {..} = ui AppState {..} =
( padBottom Max case mproc of
$ ( withBorderStyle unicode Just _ -> logDialog
$ borderWithLabel (str "GHCup") Nothing ->
$ (center $ renderList renderItem True lr) ( padBottom Max
) $ ( withBorderStyle unicode
) $ borderWithLabel (str "GHCup")
<=> ( withAttr "help" $ (center $ renderList renderItem True lr)
. txtWrap )
. T.pack )
. foldr1 (\x y -> x <> " " <> y) <=> ( withAttr "help"
. (++ ["↑↓:Navigation"]) . txtWrap
$ (fmap (\(c, s, _) -> (c : ':' : s)) keyHandlers) . T.pack
) . foldr1 (\x y -> x <> " " <> y)
. (++ ["↑↓:Navigation"])
$ (fmap (\(c, s, _) -> (c : ':' : s)) keyHandlers)
)
where where
logDialog = case mproc of
Nothing -> emptyWidget
Just (SubProcess name Nothing _ (Just logLine)) -> centerLayer . txtWrap . decUTF8Safe $ logLine
Just (SubProcess name Nothing _ (Just logLine)) -> centerLayer . txtWrap $ ""
renderItem b ListResult {..} = renderItem b ListResult {..} =
let marks = if let marks = if
| lSet -> (withAttr "set" $ str "✔✔") | lSet -> (withAttr "set" $ str "✔✔")
@@ -121,7 +151,7 @@ minHSize :: Int -> Widget n -> Widget n
minHSize s' = hLimit s' . vLimit 1 . (<+> fill ' ') minHSize s' = hLimit s' . vLimit 1 . (<+> fill ' ')
app :: App AppState e String app :: App AppState MyAppEvent String
app = App { appDraw = \st -> [ui st] app = App { appDraw = \st -> [ui st]
, appHandleEvent = eventHandler , appHandleEvent = eventHandler
, appStartEvent = return , appStartEvent = return
@@ -152,19 +182,33 @@ dimAttributes = attrMap
eventHandler :: AppState -> BrickEvent n e -> EventM n (Next AppState) eventHandler :: AppState -> BrickEvent n MyAppEvent -> EventM n (Next AppState)
eventHandler st (VtyEvent (Vty.EvResize _ _)) = continue st eventHandler st (VtyEvent (Vty.EvResize _ _)) = continue st
eventHandler st (VtyEvent (Vty.EvKey (Vty.KChar 'q') _)) = halt st eventHandler st (VtyEvent (Vty.EvKey (Vty.KChar 'q') _)) = halt st
eventHandler st (VtyEvent (Vty.EvKey Vty.KEsc _)) = halt st eventHandler st (VtyEvent (Vty.EvKey Vty.KEsc _)) = halt st
eventHandler AppState {..} (VtyEvent (Vty.EvKey (Vty.KUp) _)) = eventHandler AppState {..} (VtyEvent (Vty.EvKey (Vty.KUp) _)) =
continue (AppState (listMoveUp lr) dls pfreq) continue (AppState (listMoveUp lr) dls pfreq mproc)
eventHandler AppState {..} (VtyEvent (Vty.EvKey (Vty.KDown) _)) = eventHandler AppState {..} (VtyEvent (Vty.EvKey (Vty.KDown) _)) =
continue (AppState (listMoveDown lr) dls pfreq) continue (AppState (listMoveDown lr) dls pfreq mproc)
eventHandler as (VtyEvent (Vty.EvKey (Vty.KChar c) _)) = eventHandler as (VtyEvent (Vty.EvKey (Vty.KChar c) _)) =
case find (\(c', _, _) -> c' == c) keyHandlers of case find (\(c', _, _) -> c' == c) keyHandlers of
Nothing -> continue as Nothing -> continue as
Just (_, _, handler) -> handler as Just (_, _, handler) -> handler as
eventHandler st _ = continue st eventHandler st (AppEvent (StartProc str')) = continue st
{ mproc = Just SubProcess { procName = str'
, exited = Nothing
, procId = Nothing
, logLine = Nothing
}
}
eventHandler st@AppState { mproc = Just sp } (AppEvent (GotProcId pid)) =
continue st { mproc = Just sp { procId = Just pid } }
eventHandler st@AppState { mproc = Just sp } (AppEvent (EndProc exited)) =
continue st { mproc = Just sp { exited = Just exited, procId = Nothing } }
eventHandler st@AppState { mproc = Just sp } (AppEvent (LogLine bs)) =
continue st { mproc = Just sp { logLine = Just bs } }
eventHandler st (AppEvent _) = error "noes" -- TODO
eventHandler st _ = continue st
-- | Suspend the current UI and run an IO action in terminal. If the -- | Suspend the current UI and run an IO action in terminal. If the
@@ -174,19 +218,18 @@ withIOAction :: (AppState -> (Int, ListResult) -> IO (Either String a))
-> EventM n (Next AppState) -> EventM n (Next AppState)
withIOAction action as = case listSelectedElement (lr as) of withIOAction action as = case listSelectedElement (lr as) of
Nothing -> continue as Nothing -> continue as
Just (ix, e) -> suspendAndResume $ do Just (ix, e) -> do
action as (ix, e) >>= \case liftIO $ forkIO $ void $ action as (ix, e)
Left err -> putStrLn $ ("Error: " <> err) continue as
Right _ -> putStrLn "Success" -- apps <- (fmap . fmap)
apps <- (fmap . fmap) -- (\AppState {..} -> AppState { lr = listMoveTo ix lr, .. })
(\AppState {..} -> AppState { lr = listMoveTo ix lr, .. }) -- $ getAppState Nothing (pfreq as)
$ getAppState Nothing (pfreq as) -- case apps of
case apps of -- Right nas -> do
Right nas -> do -- putStrLn "Press enter to continue"
putStrLn "Press enter to continue" -- _ <- getLine
_ <- getLine -- pure nas
pure nas -- Left err -> throwIO $ userError err
Left err -> throwIO $ userError err
install' :: AppState -> (Int, ListResult) -> IO (Either String ()) install' :: AppState -> (Int, ListResult) -> IO (Either String ())
@@ -302,12 +345,13 @@ settings' :: IORef Settings
settings' = unsafePerformIO $ do settings' = unsafePerformIO $ do
dirs <- getDirs dirs <- getDirs
newIORef Settings { cache = True newIORef Settings { cache = True
, noVerify = False , noVerify = False
, keepDirs = Never , keepDirs = Never
, downloader = Curl , downloader = Curl
, verbose = False , verbose = False
, .. , execCb = (\_ _ _ _ _ -> liftIO $ writeFile [abs|/home/jule/git/ghcup-hs/cb.log|] Nothing "settings'")
} , ..
}
logger' :: IORef LoggerConfig logger' :: IORef LoggerConfig
@@ -321,16 +365,22 @@ logger' = unsafePerformIO
brickMain :: Settings -> Maybe URI -> LoggerConfig -> GHCupDownloads -> PlatformRequest -> IO () brickMain :: Settings -> Maybe URI -> LoggerConfig -> GHCupDownloads -> PlatformRequest -> IO ()
brickMain s muri l av pfreq' = do brickMain _ muri _ av pfreq' = do
writeIORef uri' muri writeIORef uri' muri
writeIORef settings' s s <- readIORef settings'
-- logger interpreter -- logger interpreter
writeIORef logger' l -- writeIORef logger' l
l <- readIORef logger'
let runLogger = myLoggerT l let runLogger = myLoggerT l
eApps <- getAppState (Just av) pfreq' eApps <- getAppState (Just av) pfreq'
case eApps of case eApps of
Right as -> defaultMain app (selectLatest as) $> () Right as -> do
eventChan <- newBChan 1000
let builder = Vty.mkVty Vty.defaultConfig
initialVty <- builder
writeIORef settings' s{ execCb = brickExecCb eventChan }
customMain initialVty builder (Just eventChan) app (selectLatest as) $> ()
Left e -> do Left e -> do
runLogger ($(logError) [i|Error building app state: #{show e}|]) runLogger ($(logError) [i|Error building app state: #{show e}|])
exitWith $ ExitFailure 2 exitWith $ ExitFailure 2
@@ -359,8 +409,25 @@ getAppState mg pfreq' = do
dls <- maybe (fmap _ghcupDownloads $ liftE $ getDownloadsF (maybe GHCupURL OwnSource muri)) pure mg dls <- maybe (fmap _ghcupDownloads $ liftE $ getDownloadsF (maybe GHCupURL OwnSource muri)) pure mg
lV <- lift $ listVersions dls Nothing Nothing pfreq' lV <- lift $ listVersions dls Nothing Nothing pfreq'
pure $ (AppState (list "Tool versions" (V.fromList lV) 1) dls pfreq') pure $ (AppState (list "Tool versions" (V.fromList lV) 1) dls pfreq' Nothing)
case r of case r of
VRight a -> pure $ Right a VRight a -> pure $ Right a
VLeft e -> pure $ Left [i|#{e}|] VLeft e -> pure $ Left [i|#{e}|]
brickExecCb :: BChan MyAppEvent -> ExecCb
brickExecCb chan _ fileFd stdoutRead pState lfile = do
liftIO $ writeFile [abs|/home/jule/git/ghcup-hs/cb.log|] Nothing "brickExecCb"
writeBChan chan (StartProc . T.unpack . decUTF8Safe $ lfile)
readLineTilEOF lineAction stdoutRead
takeMVar pState >>= \case
PExited e@(Left _) -> writeBChan chan (EndProc e)
_ -> error "no"
where
lineAction bs = do
void $ SPIB.fdWrite fileFd (bs <> "\n")
error "blah"
writeBChan chan (LogLine bs)

View File

@@ -831,7 +831,7 @@ toSettings Options {..} = do
downloader = optsDownloader downloader = optsDownloader
verbose = optVerbose verbose = optVerbose
dirs <- getDirs dirs <- getDirs
pure $ Settings { .. } pure $ Settings { execCb = (\_ _ _ _ _ -> liftIO $ HPath.IO.writeFile [HPath.abs|/home/jule/git/ghcup-hs/cb.log|] Nothing "toSettings"), ..}
upgradeOptsP :: Parser UpgradeOpts upgradeOptsP :: Parser UpgradeOpts

View File

@@ -59,7 +59,7 @@ _done() {
download_ghcup() { download_ghcup() {
_plat="$(uname -s)" _plat="$(uname -s)"
_arch=$(uname -m) _arch=$(uname -m)
_ghver="0.1.8" _ghver="0.1.10"
_base_url="https://downloads.haskell.org/~ghcup" _base_url="https://downloads.haskell.org/~ghcup"
case "${_plat}" in case "${_plat}" in

View File

@@ -1188,6 +1188,11 @@ ghcupDownloads:
unknown_versioning: *ghc-8102-32-deb9 unknown_versioning: *ghc-8102-32-deb9
Linux_UnknownLinux: Linux_UnknownLinux:
unknown_versioning: *ghc-8102-32-deb9 unknown_versioning: *ghc-8102-32-deb9
Linux_Alpine:
unknown_versioning:
dlUri: https://files.hasufell.de/ghc/ghc-8.10.2-i386-alpine-linux.tar.xz
dlSubdir: ghc-8.10.2
dlHash: 9ee1cf1e85e9536088b3c9e80e975074e525ea378cd4eb156071bbc4b7b38327
Cabal: Cabal:
2.4.1.0: 2.4.1.0:
viTags: [] viTags: []
@@ -1300,25 +1305,25 @@ ghcupDownloads:
viArch: viArch:
A_64: A_64:
Linux_Ubuntu: Linux_Ubuntu:
unknown_versioning: &cabal-3400rc1-trusty unknown_versioning: &cabal-3400rc1-ubuntu
dlUri: https://oleg.fi/cabal-install-3.4.0.0-rc1-bootstrapped/cabal-install-3.4.0.0-x86_64-trusty-linux-bootstrapped.tar.xz dlUri: http://oleg.fi/cabal-install-3.4.0.0-rc1/cabal-install-3.4.0.0-x86_64-ubuntu-16.04.tar.xz
dlHash: 553ce7e6ab6e375d4a1e437a76eaab0bb418983804d2da13da1f634707e1015a dlHash: 4a693eeacf91993d639b0296a366af7aec6899992352595835f7671e5adef4c6
Linux_Alpine: Linux_Alpine:
unknown_versioning: unknown_versioning:
dlUri: https://oleg.fi/cabal-install-3.4.0.0-rc1-bootstrapped/cabal-install-3.4.0.0-x86_64-alpine-3.11.6-linux-bootstrapped.tar.xz dlUri: http://oleg.fi/cabal-install-3.4.0.0-rc1/cabal-install-3.4.0.0-x86_64-alpine-3.11.6-static-noofd.tar.xz
dlHash: 7e030563242036975b37be707e43d9ba53df6e41ccb68faf9bf879cbf41abb47 dlHash: 143160e1768c9c21bad613f720a37aad34051f41fb3473f5f28c030f9ccb7aca
Linux_UnknownLinux: Linux_UnknownLinux:
unknown_versioning: *cabal-3400rc1-trusty unknown_versioning: *cabal-3400rc1-ubuntu
Darwin: Darwin:
unknown_versioning: unknown_versioning:
dlUri: https://oleg.fi/cabal-install-3.4.0.0-rc1-bootstrapped/cabal-install-3.4.0.0-x86_64-sierra-darwin-bootstrapped.tar.xz dlUri: http://oleg.fi/cabal-install-3.4.0.0-rc1/cabal-install-3.4.0.0-x86_64-darwin-sierra.tar.xz
dlHash: 755d32757b91e00e535fc601208ecd11567dbc4d832ae3bf8ce24eaba795aa1d dlHash: 98e362a57c3b5c1a76f75ede2c2a7c29439902a3e21c3e4f8dcd701e276b164f
FreeBSD: FreeBSD:
unknown_versioning: unknown_versioning:
dlUri: https://oleg.fi/cabal-install-3.4.0.0-rc1-bootstrapped/cabal-install-3.4.0.0-amd64-unknown-freebsd-bootstrapped.tar.xz dlUri: http://oleg.fi/cabal-install-3.4.0.0-rc1/cabal-install-3.4.0.0-amd64-freebsd-12.1-release.tar.xz
dlHash: 8660f588366355ad4487f7a2e81f31ecb24e15d168d31e227d43d5618a2948d0 dlHash: 0035cc5d35db15d254037a9448697e1daff0a6d21b12c8d43d72522c82cc7319
GHCup: GHCup:
0.1.9: 0.1.10:
viTags: viTags:
- Recommended - Recommended
- Latest - Latest
@@ -1328,22 +1333,22 @@ ghcupDownloads:
A_64: A_64:
Linux_UnknownLinux: Linux_UnknownLinux:
unknown_versioning: &ghcup-64 unknown_versioning: &ghcup-64
dlUri: https://downloads.haskell.org/~ghcup/0.1.9/x86_64-linux-ghcup-0.1.9 dlUri: https://downloads.haskell.org/~ghcup/0.1.10/x86_64-linux-ghcup-0.1.10
dlHash: d779ada6156b08da21e40c5bf218ec21d1308d5a9e48f7b9533f56b5d063a41c dlHash: 87661bd127f857b990174ac8d96ad4bd629865306b2058c8cc64d3b36ed317c9
Darwin: Darwin:
unknown_versioning: unknown_versioning:
dlUri: https://downloads.haskell.org/~ghcup/0.1.9/x86_64-apple-darwin-ghcup-0.1.9 dlUri: https://downloads.haskell.org/~ghcup/0.1.10/x86_64-apple-darwin-ghcup-0.1.10
dlHash: 58ad3bbdb9cbbc7599364c39013bd25394b2cc123645c91fea9dd10c1198d035 dlHash: e71666fde6a7700f307e1a55720859d3a042fe27c68ff32f3d1181f4436b7391
FreeBSD: FreeBSD:
unknown_versioning: unknown_versioning:
dlUri: https://downloads.haskell.org/~ghcup/0.1.9/x86_64-portbld-freebsd-ghcup-0.1.9 dlUri: https://downloads.haskell.org/~ghcup/0.1.10/x86_64-portbld-freebsd-ghcup-0.1.10
dlHash: 5fca520307d9d888b4536c394fafea590104a1f4fb5d5fb5a9f738ee7b473dd9 dlHash: b5ef1b0454f1a9c5a62b378c1e9c48c2b794d64a22086adf482b064dfb34e68d
Linux_Alpine: Linux_Alpine:
unknown_versioning: *ghcup-64 unknown_versioning: *ghcup-64
A_32: A_32:
Linux_UnknownLinux: Linux_UnknownLinux:
unknown_versioning: &ghcup-32 unknown_versioning: &ghcup-32
dlUri: https://downloads.haskell.org/~ghcup/0.1.9/i386-linux-ghcup-0.1.9 dlUri: https://downloads.haskell.org/~ghcup/0.1.10/i386-linux-ghcup-0.1.10
dlHash: ad7faf32665d19ced5dc636c0a0c1b14995c530fbd26ca88705a08498b572145 dlHash: 50ac43199b64bc0724400b0a3db674bef3ec53cf6d41acc04a2c4ca8557e534f
Linux_Alpine: Linux_Alpine:
unknown_versioning: *ghcup-32 unknown_versioning: *ghcup-32

View File

@@ -364,6 +364,7 @@ executable ghcup
, table-layout , table-layout
, template-haskell , template-haskell
, text , text
, unix-bytestring
, uri-bytestring , uri-bytestring
, utf8-string , utf8-string
, versions , versions

View File

@@ -154,7 +154,6 @@ installGHCBindist dlinfo ver (PlatformRequest {..}) = do
lEM $ execLogged "./configure" lEM $ execLogged "./configure"
False False
(["--prefix=" <> toFilePath inst] ++ alpineArgs) (["--prefix=" <> toFilePath inst] ++ alpineArgs)
[rel|ghc-configure|]
(Just path) (Just path)
Nothing Nothing
lEM $ make ["install"] (Just path) lEM $ make ["install"] (Just path)
@@ -275,10 +274,12 @@ installCabalBindist dlinfo ver (PlatformRequest {..}) = do
let cabalFile = [rel|cabal|] let cabalFile = [rel|cabal|]
liftIO $ createDirRecursive newDirPerms inst liftIO $ createDirRecursive newDirPerms inst
destFileName <- lift $ parseRel (toFilePath cabalFile <> "-" <> verToBS ver) destFileName <- lift $ parseRel (toFilePath cabalFile <> "-" <> verToBS ver)
let destPath = inst </> destFileName
handleIO (throwE . CopyError . show) $ liftIO $ copyFile handleIO (throwE . CopyError . show) $ liftIO $ copyFile
(path </> cabalFile) (path </> cabalFile)
(inst </> destFileName) (destPath)
Overwrite Overwrite
lift $ chmod_777 destPath
-- | Installs cabal into @~\/.ghcup\/bin/cabal-\<ver\>@ and -- | Installs cabal into @~\/.ghcup\/bin/cabal-\<ver\>@ and
@@ -855,7 +856,6 @@ Stage1Only = YES|]
) )
++ fmap E.encodeUtf8 aargs ++ fmap E.encodeUtf8 aargs
) )
[rel|ghc-conf|]
(Just workdir) (Just workdir)
(Just (("GHC", toFilePath bghcPath) : cEnv)) (Just (("GHC", toFilePath bghcPath) : cEnv))
| otherwise -> do | otherwise -> do
@@ -871,7 +871,6 @@ Stage1Only = YES|]
) )
++ fmap E.encodeUtf8 aargs ++ fmap E.encodeUtf8 aargs
) )
[rel|ghc-conf|]
(Just workdir) (Just workdir)
(Just cEnv) (Just cEnv)
@@ -1029,7 +1028,6 @@ compileCabal dls tver bghc jobs patchdir PlatformRequest{..} = do
lEM $ execLogged "./bootstrap.sh" lEM $ execLogged "./bootstrap.sh"
False False
(maybe [] (\j -> ["-j", fS (show j)]) jobs) (maybe [] (\j -> ["-j", fS (show j)]) jobs)
[rel|cabal-bootstrap|]
(Just workdir) (Just workdir)
(Just newEnv) (Just newEnv)
pure $ (tmp </> [rel|bin/cabal|]) pure $ (tmp </> [rel|bin/cabal|])
@@ -1075,17 +1073,12 @@ upgradeGHCup dls mtarget force pfreq = do
tmp <- lift withGHCupTmpDir tmp <- lift withGHCupTmpDir
let fn = [rel|ghcup|] let fn = [rel|ghcup|]
p <- liftE $ download dli tmp (Just fn) p <- liftE $ download dli tmp (Just fn)
let fileMode' =
newFilePerms
`unionFileModes` ownerExecuteMode
`unionFileModes` groupExecuteMode
`unionFileModes` otherExecuteMode
let fullDest = fromMaybe (binDir </> fn) mtarget let fullDest = fromMaybe (binDir </> fn) mtarget
liftIO $ hideError NoSuchThing $ deleteFile fullDest liftIO $ hideError NoSuchThing $ deleteFile fullDest
handleIO (throwE . CopyError . show) $ liftIO $ copyFile p handleIO (throwE . CopyError . show) $ liftIO $ copyFile p
fullDest fullDest
Overwrite Overwrite
liftIO $ setFileMode (toFilePath fullDest) fileMode' lift $ chmod_777 fullDest
pure latestVer pure latestVer

View File

@@ -13,6 +13,7 @@ module GHCup.Download.IOStreams where
import GHCup.Download.Utils import GHCup.Download.Utils
import GHCup.Errors import GHCup.Errors
import GHCup.Types
import GHCup.Types.Optics import GHCup.Types.Optics
import GHCup.Types.JSON ( ) import GHCup.Types.JSON ( )
import GHCup.Utils.File import GHCup.Utils.File

View File

@@ -1,6 +1,10 @@
{-# OPTIONS_GHC -Wno-orphans #-}
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-| {-|
Module : GHCup.Types Module : GHCup.Types
@@ -13,11 +17,14 @@ Portability : POSIX
-} -}
module GHCup.Types where module GHCup.Types where
import Control.Concurrent.MVar
import Data.ByteString ( ByteString )
import Data.Map.Strict ( Map ) import Data.Map.Strict ( Map )
import Data.Text ( Text ) import Data.Text ( Text )
import Data.Versions import Data.Versions
import HPath import HPath
import URI.ByteString import URI.ByteString
import System.Posix.Types
import qualified GHC.Generics as GHC import qualified GHC.Generics as GHC
@@ -163,6 +170,27 @@ data URLSource = GHCupURL
deriving Show deriving Show
data ProcessError = NonZeroExit Int ByteString [ByteString]
| PTerminated ByteString [ByteString]
| PStopped ByteString [ByteString]
| NoSuchPid ByteString [ByteString]
deriving (Eq, Show)
data ProcState = PRunning ProcessID
| PExited (Either ProcessError ())
deriving Eq
type ExecCb = Bool -- verbose
-> Fd -- log file fd
-> Fd -- input fd to read from
-> MVar ProcState -- state of the producing process
-> ByteString -- log filename
-> IO ()
instance Show ExecCb where
show _ = "**ExecCb**"
data Settings = Settings data Settings = Settings
{ -- set by user { -- set by user
cache :: Bool cache :: Bool
@@ -173,6 +201,7 @@ data Settings = Settings
-- set on app start -- set on app start
, dirs :: Dirs , dirs :: Dirs
, execCb :: ExecCb
} }
deriving Show deriving Show

View File

@@ -550,7 +550,7 @@ make args workdir = do
spaths <- catMaybes . fmap parseAbs <$> (liftIO getSearchPath) spaths <- catMaybes . fmap parseAbs <$> (liftIO getSearchPath)
has_gmake <- isJust <$> (liftIO $ searchPath spaths [rel|gmake|]) has_gmake <- isJust <$> (liftIO $ searchPath spaths [rel|gmake|])
let mymake = if has_gmake then "gmake" else "make" let mymake = if has_gmake then "gmake" else "make"
execLogged mymake True args [rel|ghc-make|] workdir Nothing execLogged mymake True args workdir Nothing
-- | Try to apply patches in order. Fails with 'PatchFailed' -- | Try to apply patches in order. Fails with 'PatchFailed'

View File

@@ -2,6 +2,7 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ViewPatterns #-}
{-| {-|
Module : GHCup.Utils.File Module : GHCup.Utils.File
@@ -25,6 +26,7 @@ import Control.Concurrent.Async
import Control.Exception ( evaluate ) import Control.Exception ( evaluate )
import Control.Exception.Safe import Control.Exception.Safe
import Control.Monad import Control.Monad
import Control.Monad.Logger
import Control.Monad.Reader import Control.Monad.Reader
import Control.Monad.Trans.State.Strict import Control.Monad.Trans.State.Strict
import Data.ByteString ( ByteString ) import Data.ByteString ( ByteString )
@@ -33,6 +35,7 @@ import Data.Functor
import Data.IORef import Data.IORef
import Data.Maybe import Data.Maybe
import Data.Sequence ( Seq, (|>) ) import Data.Sequence ( Seq, (|>) )
import Data.String.Interpolate
import Data.Text ( Text ) import Data.Text ( Text )
import Data.Void import Data.Void
import Data.Word8 import Data.Word8
@@ -46,6 +49,7 @@ import System.IO.Error
import System.Posix.Directory.ByteString import System.Posix.Directory.ByteString
import System.Posix.FD as FD import System.Posix.FD as FD
import System.Posix.FilePath hiding ( (</>) ) import System.Posix.FilePath hiding ( (</>) )
import System.Posix.Files.ByteString
import System.Posix.Foreign ( oExcl ) import System.Posix.Foreign ( oExcl )
import "unix" System.Posix.IO.ByteString import "unix" System.Posix.IO.ByteString
hiding ( openFd ) hiding ( openFd )
@@ -68,14 +72,6 @@ import qualified "unix-bytestring" System.Posix.IO.ByteString
as SPIB as SPIB
data ProcessError = NonZeroExit Int ByteString [ByteString]
| PTerminated ByteString [ByteString]
| PStopped ByteString [ByteString]
| NoSuchPid ByteString [ByteString]
deriving Show
data CapturedProcess = CapturedProcess data CapturedProcess = CapturedProcess
{ _exitCode :: ExitCode { _exitCode :: ExitCode
, _stdOut :: ByteString , _stdOut :: ByteString
@@ -117,30 +113,31 @@ execLogged :: (MonadReader Settings m, MonadIO m, MonadThrow m)
=> ByteString -- ^ thing to execute => ByteString -- ^ thing to execute
-> Bool -- ^ whether to search PATH for the thing -> Bool -- ^ whether to search PATH for the thing
-> [ByteString] -- ^ args for the thing -> [ByteString] -- ^ args for the thing
-> Path Rel -- ^ log filename
-> Maybe (Path Abs) -- ^ optionally chdir into this -> Maybe (Path Abs) -- ^ optionally chdir into this
-> Maybe [(ByteString, ByteString)] -- ^ optional environment -> Maybe [(ByteString, ByteString)] -- ^ optional environment
-> m (Either ProcessError ()) -> m (Either ProcessError ())
execLogged exe spath args lfile chdir env = do execLogged exe spath args chdir env = do
Settings {dirs = Dirs {..}, ..} <- ask Settings {dirs = Dirs {..}, ..} <- ask
logfile <- (logsDir </>) <$> parseRel (toFilePath lfile <> ".log") logfile <- (logsDir </>) <$> parseRel (lfile <> ".log")
liftIO $ bracket (createFile (toFilePath logfile) newFilePerms) liftIO $ bracket (createFile (toFilePath logfile) newFilePerms)
closeFd closeFd
(action verbose) (action verbose execCb)
where where
action verbose fd = do lfile = fromMaybe exe $ BS.stripPrefix "./" exe
action verbose cb fd = do
actionWithPipes $ \(stdoutRead, stdoutWrite) -> do actionWithPipes $ \(stdoutRead, stdoutWrite) -> do
-- start the thread that logs to stdout -- start the thread that logs to stdout
pState <- newEmptyMVar pState <- newEmptyMVar
done <- newEmptyMVar done <- newEmptyMVar
liftIO $ HPath.IO.writeFile [HPath.abs|/home/jule/git/ghcup-hs/cb.log|] Nothing "execLogged1"
void void
$ forkIO $ forkIO
$ EX.handle (\(_ :: IOException) -> pure ()) $ EX.handle (\(_ :: IOException) -> pure ())
$ flip EX.finally (putMVar done ()) $ flip EX.finally (putMVar done ())
$ (if verbose $ (do
then tee fd stdoutRead liftIO $ HPath.IO.writeFile [HPath.abs|/home/jule/git/ghcup-hs/cb.log|] Nothing "execLogged"
else printToRegion fd stdoutRead 6 pState cb verbose fd stdoutRead pState lfile)
)
-- fork the subprocess -- fork the subprocess
pid <- SPPB.forkProcess $ do pid <- SPPB.forkProcess $ do
@@ -153,115 +150,57 @@ execLogged exe spath args lfile chdir env = do
maybe (pure ()) (changeWorkingDirectory . toFilePath) chdir maybe (pure ()) (changeWorkingDirectory . toFilePath) chdir
void $ SPPB.executeFile exe spath args env void $ SPPB.executeFile exe spath args env
putMVar pState (PRunning pid)
closeFd stdoutWrite closeFd stdoutWrite
-- wait for the subprocess to finish -- wait for the subprocess to finish
e <- toProcessError exe args <$!> SPPB.getProcessStatus True True pid e <- toProcessError exe args <$!> SPPB.getProcessStatus True True pid
putMVar pState (either (const False) (const True) e) void $ swapMVar pState (PExited e)
void $ race (takeMVar done) (threadDelay (1000000 * 3)) void $ race (takeMVar done) (threadDelay (1000000 * 3))
closeFd stdoutRead closeFd stdoutRead
pure e pure e
tee :: Fd -> Fd -> IO ()
tee fileFd fdIn = readTilEOF lineAction fdIn
where readLineTilEOF :: MonadIO m => (ByteString -> m a) -> Fd -> m ()
lineAction :: ByteString -> IO () readLineTilEOF ~action' fd' = go mempty
lineAction bs' = do where
void $ SPIB.fdWrite fileFd (bs' <> "\n") go bs' = do
void $ SPIB.fdWrite stdOutput (bs' <> "\n") (bs, rest, eof) <- readLine fd' bs'
if eof
then liftIO $ ioError (mkIOError eofErrorType "" Nothing Nothing)
else (void $ action' bs) >> go rest
-- Reads fdIn and logs the output in a continous scrolling area
-- of 'size' terminal lines. Also writes to a log file.
printToRegion :: Fd -> Fd -> Int -> MVar Bool -> IO ()
printToRegion fileFd fdIn size pState = do
void $ displayConsoleRegions $ do
rs <-
liftIO
. fmap Sq.fromList
. sequence
. replicate size
. openConsoleRegion
$ Linear
flip runStateT mempty
$ handle
(\(ex :: SomeException) -> do
ps <- liftIO $ takeMVar pState
when (ps == True) (forM_ rs (liftIO . closeConsoleRegion))
throw ex
)
$ readTilEOF (lineAction rs) fdIn
where -- Consecutively read from Fd in 512 chunks until we hit
-- action to perform line by line -- newline or EOF.
-- TODO: do this with vty for efficiency readLine :: MonadIO m
lineAction :: (MonadMask m, MonadIO m) => Fd -- ^ input file descriptor
=> Seq ConsoleRegion -> ByteString -- ^ rest buffer (read across newline)
-> ByteString -> m (ByteString, ByteString, Bool) -- ^ (full line, rest, eof)
-> StateT (Seq ByteString) m () readLine fd = \inBs -> go inBs
lineAction rs = \bs' -> do where
void $ liftIO $ SPIB.fdWrite fileFd (bs' <> "\n") go inBs = do
modify (swapRegs bs') -- if buffer is not empty, process it first
regs <- get mbs <- if BS.length inBs == 0
liftIO $ forM_ (Sq.zip regs rs) $ \(bs, r) -> setConsoleRegion r $ do -- otherwise attempt read
w <- consoleWidth then liftIO
return $ handleIO (\e -> if isEOFError e then pure Nothing else ioError e)
. T.pack $ fmap Just
. color Blue $ SPIB.fdRead fd 512
. T.unpack else pure $ Just inBs
. decUTF8Safe case mbs of
. trim w Nothing -> pure ("", "", True)
. (\b -> "[ " <> toFilePath lfile <> " ] " <> b) Just bs -> do
$ bs -- split on newline
let (line, rest) = BS.span (/= _lf) bs
if
| BS.length rest /= 0 -> pure (line, BS.tail rest, False)
-- if rest is empty, then there was no newline, process further
| otherwise -> (\(l, r, b) -> (line <> l, r, b)) <$!> go mempty
swapRegs :: a -> Seq a -> Seq a
swapRegs bs = \regs -> if
| Sq.length regs < size -> regs |> bs
| otherwise -> Sq.drop 1 regs |> bs
-- trim output line to terminal width
trim :: Int -> ByteString -> ByteString
trim w = \bs -> if
| BS.length bs > w && w > 5 -> BS.take (w - 4) bs <> "..."
| otherwise -> bs
-- Consecutively read from Fd in 512 chunks until we hit
-- newline or EOF.
readLine :: MonadIO m
=> Fd -- ^ input file descriptor
-> ByteString -- ^ rest buffer (read across newline)
-> m (ByteString, ByteString, Bool) -- ^ (full line, rest, eof)
readLine fd = \inBs -> go inBs
where
go inBs = do
-- if buffer is not empty, process it first
mbs <- if BS.length inBs == 0
-- otherwise attempt read
then liftIO
$ handleIO (\e -> if isEOFError e then pure Nothing else ioError e)
$ fmap Just
$ SPIB.fdRead fd 512
else pure $ Just inBs
case mbs of
Nothing -> pure ("", "", True)
Just bs -> do
-- split on newline
let (line, rest) = BS.span (/= _lf) bs
if
| BS.length rest /= 0 -> pure (line, BS.tail rest, False)
-- if rest is empty, then there was no newline, process further
| otherwise -> (\(l, r, b) -> (line <> l, r, b)) <$!> go mempty
readTilEOF :: MonadIO m => (ByteString -> m a) -> Fd -> m ()
readTilEOF ~action' fd' = go mempty
where
go bs' = do
(bs, rest, eof) <- readLine fd' bs'
if eof
then liftIO $ ioError (mkIOError eofErrorType "" Nothing Nothing)
else (void $ action' bs) >> go rest
-- | Capture the stdout and stderr of the given action, which -- | Capture the stdout and stderr of the given action, which
@@ -375,7 +314,7 @@ toProcessError :: ByteString
-> Maybe ProcessStatus -> Maybe ProcessStatus
-> Either ProcessError () -> Either ProcessError ()
toProcessError exe args mps = case mps of toProcessError exe args mps = case mps of
Just (SPPB.Exited (ExitFailure i)) -> Left $ NonZeroExit i exe args Just (SPPB.Exited (ExitFailure xi)) -> Left $ NonZeroExit xi exe args
Just (SPPB.Exited ExitSuccess ) -> Right () Just (SPPB.Exited ExitSuccess ) -> Right ()
Just (Terminated _ _ ) -> Left $ PTerminated exe args Just (Terminated _ _ ) -> Left $ PTerminated exe args
Just (Stopped _ ) -> Left $ PStopped exe args Just (Stopped _ ) -> Left $ PStopped exe args
@@ -434,3 +373,88 @@ isBrokenSymlink p =
$ do $ do
_ <- canonicalizePath p _ <- canonicalizePath p
pure False pure False
chmod_777 :: (MonadLogger m, MonadIO m) => Path a -> m ()
chmod_777 (toFilePath -> fp) = do
let exe_mode =
newFilePerms
`unionFileModes` ownerExecuteMode
`unionFileModes` groupExecuteMode
`unionFileModes` otherExecuteMode
$(logDebug) [i|chmod 777 #{fp}|]
liftIO $ setFileMode fp exe_mode
-- | The default callback for logging/printing on 'execLogged'.
defExecCb :: ExecCb
defExecCb verbose fd stdoutRead pState lfile = if verbose
then tee fd stdoutRead
else printToRegion fd stdoutRead 6
where
tee :: Fd -> Fd -> IO ()
tee fileFd fdIn = readLineTilEOF lineAction fdIn
where
lineAction :: ByteString -> IO ()
lineAction bs' = do
void $ SPIB.fdWrite fileFd (bs' <> "\n")
void $ SPIB.fdWrite stdOutput (bs' <> "\n")
-- Reads fdIn and logs the output in a continous scrolling area
-- of 'size' terminal lines. Also writes to a log file.
printToRegion :: Fd -> Fd -> Int -> IO ()
printToRegion fileFd fdIn size = do
void $ displayConsoleRegions $ do
rs <-
liftIO
. fmap Sq.fromList
. sequence
. replicate size
. openConsoleRegion
$ Linear
flip runStateT mempty
$ handle
(\(ex :: SomeException) -> do
let closeEventually = readMVar pState >>= \case
PExited (Right _) -> forM_ rs (liftIO . closeConsoleRegion)
_ -> threadDelay 500 >> closeEventually
void $ liftIO $ race closeEventually (threadDelay (1000000 * 3))
throw ex
)
$ readLineTilEOF (lineAction rs) fdIn
where
-- action to perform line by line
-- TODO: do this with vty for efficiency
lineAction :: (MonadMask m, MonadIO m)
=> Seq ConsoleRegion
-> ByteString
-> StateT (Seq ByteString) m ()
lineAction rs = \bs' -> do
void $ liftIO $ SPIB.fdWrite fileFd (bs' <> "\n")
modify (swapRegs bs')
regs <- get
liftIO $ forM_ (Sq.zip regs rs) $ \(bs, r) -> setConsoleRegion r $ do
w <- consoleWidth
return
. T.pack
. color Blue
. T.unpack
. decUTF8Safe
. trim w
. (\b -> "[ " <> lfile <> " ] " <> b)
$ bs
swapRegs :: a -> Seq a -> Seq a
swapRegs bs = \regs -> if
| Sq.length regs < size -> regs |> bs
| otherwise -> Sq.drop 1 regs |> bs
-- trim output line to terminal width
trim :: Int -> ByteString -> ByteString
trim w = \bs -> if
| BS.length bs > w && w > 5 -> BS.take (w - 4) bs <> "..."
| otherwise -> bs