Windows support
This commit is contained in:
parent
b94a4123eb
commit
2c3ebe706d
@ -41,6 +41,9 @@ apk add --no-cache \
|
|||||||
zlib \
|
zlib \
|
||||||
zlib-dev \
|
zlib-dev \
|
||||||
zlib-static \
|
zlib-static \
|
||||||
|
bzip2 \
|
||||||
|
bzip2-dev \
|
||||||
|
bzip2-static \
|
||||||
gmp \
|
gmp \
|
||||||
gmp-dev \
|
gmp-dev \
|
||||||
openssl-dev \
|
openssl-dev \
|
||||||
|
@ -7,7 +7,7 @@ set -eux
|
|||||||
mkdir -p "${TMPDIR}"
|
mkdir -p "${TMPDIR}"
|
||||||
|
|
||||||
sudo apt-get update -y
|
sudo apt-get update -y
|
||||||
sudo apt-get install -y libnuma-dev zlib1g-dev libgmp-dev libgmp10 libssl-dev liblzma-dev git wget
|
sudo apt-get install -y libnuma-dev zlib1g-dev libgmp-dev libgmp10 libssl-dev liblzma-dev libbz2-dev git wget
|
||||||
|
|
||||||
curl -sSfL https://downloads.haskell.org/~ghcup/x86_64-linux-ghcup > ./ghcup-bin
|
curl -sSfL https://downloads.haskell.org/~ghcup/x86_64-linux-ghcup > ./ghcup-bin
|
||||||
chmod +x ghcup-bin
|
chmod +x ghcup-bin
|
||||||
|
@ -19,7 +19,7 @@ ednf() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ednf update
|
ednf update
|
||||||
ednf install gcc gcc-c++ gmp gmp-devel make ncurses ncurses-devel xz xz-devel perl zlib zlib-devel openssl-devel openssl-libs openssl libffi libffi-devel lbzip2 lbzip2-utils
|
ednf install gcc gcc-c++ gmp gmp-devel make ncurses ncurses-devel xz xz-devel perl zlib zlib-devel openssl-devel openssl-libs openssl libffi libffi-devel lbzip2 lbzip2-utils bzip2-devel
|
||||||
if [ "${ARCH}" = "ARM64" ] ; then
|
if [ "${ARCH}" = "ARM64" ] ; then
|
||||||
ednf install numactl numactl-libs numactl-devel
|
ednf install numactl numactl-libs numactl-devel
|
||||||
fi
|
fi
|
||||||
|
@ -7,4 +7,4 @@ set -eux
|
|||||||
mkdir -p "${TMPDIR}"
|
mkdir -p "${TMPDIR}"
|
||||||
|
|
||||||
sudo apt-get update -y
|
sudo apt-get update -y
|
||||||
sudo apt-get install -y libnuma-dev zlib1g-dev libgmp-dev libgmp10 libssl-dev liblzma-dev git wget
|
sudo apt-get install -y libnuma-dev zlib1g-dev libgmp-dev libgmp10 libssl-dev liblzma-dev libbz2-dev git wget
|
||||||
|
@ -7,11 +7,11 @@ set -eux
|
|||||||
mkdir -p "$CI_PROJECT_DIR"/.local/bin
|
mkdir -p "$CI_PROJECT_DIR"/.local/bin
|
||||||
|
|
||||||
ecabal() {
|
ecabal() {
|
||||||
cabal --store-dir="$(pwd)"/.store "$@"
|
cabal --store-dir="$CI_PROJECT_DIR"/.store "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
eghcup() {
|
eghcup() {
|
||||||
ghcup -v -c -s file://$(pwd)/ghcup-${JSON_VERSION}.yaml "$@"
|
ghcup -v -c -s file://$CI_PROJECT_DIR/ghcup-${JSON_VERSION}.yaml "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
git describe --always
|
git describe --always
|
||||||
@ -116,8 +116,12 @@ fi
|
|||||||
eghcup rm $(ghc --numeric-version)
|
eghcup rm $(ghc --numeric-version)
|
||||||
|
|
||||||
# https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/116
|
# https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/116
|
||||||
eghcup install cabal -u https://oleg.fi/cabal-install-3.4.0.0-rc4/cabal-install-3.4.0.0-x86_64-ubuntu-16.04.tar.xz 3.4.0.0-rc4
|
if [ "${OS}" = "LINUX" ] ; then
|
||||||
eghcup rm cabal 3.4.0.0-rc4
|
if [ "${ARCH}" = "64" ] ; then
|
||||||
|
eghcup install cabal -u https://oleg.fi/cabal-install-3.4.0.0-rc4/cabal-install-3.4.0.0-x86_64-ubuntu-16.04.tar.xz 3.4.0.0-rc4
|
||||||
|
eghcup rm cabal 3.4.0.0-rc4
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
eghcup upgrade
|
eghcup upgrade
|
||||||
eghcup upgrade -f
|
eghcup upgrade -f
|
||||||
|
@ -6,10 +6,6 @@
|
|||||||
|
|
||||||
This is an open variant, similar to [plucky](https://hackage.haskell.org/package/plucky) or [oops](https://github.com/i-am-tom/oops) and allows us to combine different error types. Maybe it is too much and it's a little bit [unergonomic](https://github.com/haskus/packages/issues/32) at times. If it really hurts maintenance, it will be removed. It was more of an experiment.
|
This is an open variant, similar to [plucky](https://hackage.haskell.org/package/plucky) or [oops](https://github.com/i-am-tom/oops) and allows us to combine different error types. Maybe it is too much and it's a little bit [unergonomic](https://github.com/haskus/packages/issues/32) at times. If it really hurts maintenance, it will be removed. It was more of an experiment.
|
||||||
|
|
||||||
### No use of filepath or directory
|
|
||||||
|
|
||||||
Filepath and directory have two fundamental problems: 1. they use String as filepath (see [AFPP](https://gitlab.haskell.org/ghc/ghc/-/wikis/proposal/abstract-file-path) as to why this is wrong) and 2. they try very hard to be cross-platform at the expense of low-level correctness. Instead, we use the [hpath](https://github.com/hasufell/hpath) libraries for file and filepath related stuff, which also gives us stronger filepath types.
|
|
||||||
|
|
||||||
### No use of haskell-TLS
|
### No use of haskell-TLS
|
||||||
|
|
||||||
I consider haskell-TLS an interesting experiment, but not a battle-tested and peer-reviewed crypto implementation. There is little to no research about what the intricacies of using haskell for low-level crypto are and how vulnerable such binaries are. Instead, we use either curl the binary (for FreeBSD and mac) or http-io-streams, which works with OpenSSL bindings.
|
I consider haskell-TLS an interesting experiment, but not a battle-tested and peer-reviewed crypto implementation. There is little to no research about what the intricacies of using haskell for low-level crypto are and how vulnerable such binaries are. Instead, we use either curl the binary (for FreeBSD and mac) or http-io-streams, which works with OpenSSL bindings.
|
||||||
|
@ -234,7 +234,7 @@ ghcup is not a reimplementation of stack. The only common part is automatic inst
|
|||||||
|
|
||||||
2. Why not support windows?
|
2. Why not support windows?
|
||||||
|
|
||||||
Windows support is [WIP](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/130).
|
We do.
|
||||||
|
|
||||||
3. Why the haskell reimplementation?
|
3. Why the haskell reimplementation?
|
||||||
|
|
||||||
|
@ -37,12 +37,11 @@ import Data.IORef
|
|||||||
import Data.List
|
import Data.List
|
||||||
import Data.String.Interpolate
|
import Data.String.Interpolate
|
||||||
import Data.Versions
|
import Data.Versions
|
||||||
import HPath ( toFilePath, rel )
|
|
||||||
import Haskus.Utils.Variant.Excepts
|
import Haskus.Utils.Variant.Excepts
|
||||||
import Optics
|
import Optics
|
||||||
|
import System.FilePath
|
||||||
import System.Exit
|
import System.Exit
|
||||||
import System.IO
|
import System.IO
|
||||||
import System.Posix.FilePath
|
|
||||||
import Text.ParserCombinators.ReadP
|
import Text.ParserCombinators.ReadP
|
||||||
import Text.PrettyPrint.HughesPJClass ( prettyShow )
|
import Text.PrettyPrint.HughesPJClass ( prettyShow )
|
||||||
import Text.Regex.Posix
|
import Text.Regex.Posix
|
||||||
@ -106,6 +105,10 @@ validate dls = do
|
|||||||
addError
|
addError
|
||||||
when ((notElem FreeBSD pspecs) && arch == A_64) $ lift $ $(logWarn)
|
when ((notElem FreeBSD pspecs) && arch == A_64) $ lift $ $(logWarn)
|
||||||
[i|FreeBSD missing for #{t} #{v'} #{arch'}|]
|
[i|FreeBSD missing for #{t} #{v'} #{arch'}|]
|
||||||
|
when (notElem Windows pspecs && arch == A_64) $ do
|
||||||
|
lift $ $(logError)
|
||||||
|
[i|Windows missing for for #{t} #{v'} #{arch'}|]
|
||||||
|
addError
|
||||||
|
|
||||||
-- alpine needs to be set explicitly, because
|
-- alpine needs to be set explicitly, because
|
||||||
-- we cannot assume that "Linux UnknownLinux" runs on Alpine
|
-- we cannot assume that "Linux UnknownLinux" runs on Alpine
|
||||||
@ -238,7 +241,7 @@ validateTarballs (TarballFilter tool versionRegex) dls = do
|
|||||||
$ do
|
$ do
|
||||||
case tool of
|
case tool of
|
||||||
Just GHCup -> do
|
Just GHCup -> do
|
||||||
let fn = [rel|ghcup|]
|
let fn = "ghcup"
|
||||||
dir <- liftIO ghcupCacheDir
|
dir <- liftIO ghcupCacheDir
|
||||||
p <- liftE $ download dli dir (Just fn)
|
p <- liftE $ download dli dir (Just fn)
|
||||||
liftE $ checkDigest dli p
|
liftE $ checkDigest dli p
|
||||||
@ -252,7 +255,7 @@ validateTarballs (TarballFilter tool versionRegex) dls = do
|
|||||||
case r of
|
case r of
|
||||||
VRight (Just basePath) -> do
|
VRight (Just basePath) -> do
|
||||||
case _dlSubdir dli of
|
case _dlSubdir dli of
|
||||||
Just (RealDir (toFilePath -> prel)) -> do
|
Just (RealDir prel) -> do
|
||||||
lift $ $(logInfo)
|
lift $ $(logInfo)
|
||||||
[i|verifying subdir: #{prel}|]
|
[i|verifying subdir: #{prel}|]
|
||||||
when (basePath /= prel) $ do
|
when (basePath /= prel) $ do
|
||||||
|
@ -14,6 +14,7 @@ import GHCup.Download
|
|||||||
import GHCup.Errors
|
import GHCup.Errors
|
||||||
import GHCup.Types
|
import GHCup.Types
|
||||||
import GHCup.Utils
|
import GHCup.Utils
|
||||||
|
import GHCup.Utils.Prelude ( decUTF8Safe )
|
||||||
import GHCup.Utils.File
|
import GHCup.Utils.File
|
||||||
import GHCup.Utils.Logger
|
import GHCup.Utils.Logger
|
||||||
|
|
||||||
@ -518,7 +519,8 @@ changelog' BrickState { appData = BrickData {..} } (_, ListResult {..}) = do
|
|||||||
Darwin -> "open"
|
Darwin -> "open"
|
||||||
Linux _ -> "xdg-open"
|
Linux _ -> "xdg-open"
|
||||||
FreeBSD -> "xdg-open"
|
FreeBSD -> "xdg-open"
|
||||||
exec cmd True [serializeURIRef' uri] Nothing Nothing >>= \case
|
Windows -> "start"
|
||||||
|
exec cmd [T.unpack $ decUTF8Safe $ serializeURIRef' uri] Nothing Nothing >>= \case
|
||||||
Right _ -> pure $ Right ()
|
Right _ -> pure $ Right ()
|
||||||
Left e -> pure $ Left $ prettyShow e
|
Left e -> pure $ Left $ prettyShow e
|
||||||
|
|
||||||
|
@ -53,8 +53,6 @@ import Data.Versions hiding ( str )
|
|||||||
import Data.Void
|
import Data.Void
|
||||||
import GHC.IO.Encoding
|
import GHC.IO.Encoding
|
||||||
import Haskus.Utils.Variant.Excepts
|
import Haskus.Utils.Variant.Excepts
|
||||||
import HPath
|
|
||||||
import HPath.IO
|
|
||||||
import Language.Haskell.TH
|
import Language.Haskell.TH
|
||||||
import Options.Applicative hiding ( style )
|
import Options.Applicative hiding ( style )
|
||||||
import Options.Applicative.Help.Pretty ( text )
|
import Options.Applicative.Help.Pretty ( text )
|
||||||
@ -64,6 +62,7 @@ import System.Console.Pretty hiding ( color )
|
|||||||
import qualified System.Console.Pretty as Pretty
|
import qualified System.Console.Pretty as Pretty
|
||||||
import System.Environment
|
import System.Environment
|
||||||
import System.Exit
|
import System.Exit
|
||||||
|
import System.FilePath
|
||||||
import System.IO hiding ( appendFile )
|
import System.IO hiding ( appendFile )
|
||||||
import Text.Read hiding ( lift )
|
import Text.Read hiding ( lift )
|
||||||
import Text.PrettyPrint.HughesPJClass ( prettyShow )
|
import Text.PrettyPrint.HughesPJClass ( prettyShow )
|
||||||
@ -170,17 +169,17 @@ data CompileCommand = CompileGHC GHCCompileOptions
|
|||||||
|
|
||||||
data GHCCompileOptions = GHCCompileOptions
|
data GHCCompileOptions = GHCCompileOptions
|
||||||
{ targetGhc :: Either Version GitBranch
|
{ targetGhc :: Either Version GitBranch
|
||||||
, bootstrapGhc :: Either Version (Path Abs)
|
, bootstrapGhc :: Either Version FilePath
|
||||||
, jobs :: Maybe Int
|
, jobs :: Maybe Int
|
||||||
, buildConfig :: Maybe (Path Abs)
|
, buildConfig :: Maybe FilePath
|
||||||
, patchDir :: Maybe (Path Abs)
|
, patchDir :: Maybe FilePath
|
||||||
, crossTarget :: Maybe Text
|
, crossTarget :: Maybe Text
|
||||||
, addConfArgs :: [Text]
|
, addConfArgs :: [Text]
|
||||||
, setCompile :: Bool
|
, setCompile :: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
data UpgradeOpts = UpgradeInplace
|
data UpgradeOpts = UpgradeInplace
|
||||||
| UpgradeAt (Path Abs)
|
| UpgradeAt FilePath
|
||||||
| UpgradeGHCupDir
|
| UpgradeGHCupDir
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
@ -721,8 +720,7 @@ ghcCompileOpts =
|
|||||||
<*> option
|
<*> option
|
||||||
(eitherReader
|
(eitherReader
|
||||||
(\x ->
|
(\x ->
|
||||||
(bimap (const "Not a valid version") Left . version . T.pack $ x)
|
(bimap (const "Not a valid version") Left . version . T.pack $ x) <|> (if isPathSeparator (head x) then pure $ Right x else Left "Not an absolute Path")
|
||||||
<|> (bimap show Right . parseAbs . E.encodeUtf8 . T.pack $ x)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
( short 'b'
|
( short 'b'
|
||||||
@ -740,26 +738,14 @@ ghcCompileOpts =
|
|||||||
)
|
)
|
||||||
<*> optional
|
<*> optional
|
||||||
(option
|
(option
|
||||||
(eitherReader
|
str
|
||||||
(\x ->
|
|
||||||
first show . parseAbs . E.encodeUtf8 . T.pack $ x :: Either
|
|
||||||
String
|
|
||||||
(Path Abs)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(short 'c' <> long "config" <> metavar "CONFIG" <> help
|
(short 'c' <> long "config" <> metavar "CONFIG" <> help
|
||||||
"Absolute path to build config file"
|
"Absolute path to build config file"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
<*> optional
|
<*> optional
|
||||||
(option
|
(option
|
||||||
(eitherReader
|
str
|
||||||
(\x ->
|
|
||||||
first show . parseAbs . E.encodeUtf8 . T.pack $ x :: Either
|
|
||||||
String
|
|
||||||
(Path Abs)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(short 'p' <> long "patchdir" <> metavar "PATCH_DIR" <> help
|
(short 'p' <> long "patchdir" <> metavar "PATCH_DIR" <> help
|
||||||
"Absolute path to patch directory (applied in order, uses -p1)"
|
"Absolute path to patch directory (applied in order, uses -p1)"
|
||||||
)
|
)
|
||||||
@ -1040,13 +1026,7 @@ upgradeOptsP =
|
|||||||
)
|
)
|
||||||
<|> ( UpgradeAt
|
<|> ( UpgradeAt
|
||||||
<$> option
|
<$> option
|
||||||
(eitherReader
|
str
|
||||||
(\x ->
|
|
||||||
first show . parseAbs . E.encodeUtf8 . T.pack $ x :: Either
|
|
||||||
String
|
|
||||||
(Path Abs)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(short 't' <> long "target" <> metavar "TARGET_DIR" <> help
|
(short 't' <> long "target" <> metavar "TARGET_DIR" <> help
|
||||||
"Absolute filepath to write ghcup into"
|
"Absolute filepath to write ghcup into"
|
||||||
)
|
)
|
||||||
@ -1058,9 +1038,9 @@ upgradeOptsP =
|
|||||||
describe_result :: String
|
describe_result :: String
|
||||||
describe_result = $( LitE . StringL <$>
|
describe_result = $( LitE . StringL <$>
|
||||||
runIO (do
|
runIO (do
|
||||||
CapturedProcess{..} <- executeOut [rel|git|] ["describe"] Nothing
|
CapturedProcess{..} <- executeOut "git" ["describe"] Nothing
|
||||||
case _exitCode of
|
case _exitCode of
|
||||||
ExitSuccess -> pure . T.unpack . decUTF8Safe $ _stdOut
|
ExitSuccess -> pure . T.unpack . decUTF8Safe' $ _stdOut
|
||||||
ExitFailure _ -> pure numericVer
|
ExitFailure _ -> pure numericVer
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -1114,7 +1094,7 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
|||||||
let loggerConfig = LoggerConfig
|
let loggerConfig = LoggerConfig
|
||||||
{ lcPrintDebug = verbose settings
|
{ lcPrintDebug = verbose settings
|
||||||
, colorOutter = B.hPut stderr
|
, colorOutter = B.hPut stderr
|
||||||
, rawOutter = appendFile logfile
|
, rawOutter = B.appendFile logfile
|
||||||
}
|
}
|
||||||
let runLogger = myLoggerT loggerConfig
|
let runLogger = myLoggerT loggerConfig
|
||||||
|
|
||||||
@ -1616,12 +1596,9 @@ Make sure to clean up #{tmpdir} afterwards.|])
|
|||||||
|
|
||||||
Upgrade uOpts force -> do
|
Upgrade uOpts force -> do
|
||||||
target <- case uOpts of
|
target <- case uOpts of
|
||||||
UpgradeInplace -> do
|
UpgradeInplace -> Just <$> liftIO getExecutablePath
|
||||||
efp <- liftIO getExecutablePath
|
|
||||||
p <- parseAbs . E.encodeUtf8 . T.pack $ efp
|
|
||||||
pure $ Just p
|
|
||||||
(UpgradeAt p) -> pure $ Just p
|
(UpgradeAt p) -> pure $ Just p
|
||||||
UpgradeGHCupDir -> pure (Just (binDir </> [rel|ghcup|]))
|
UpgradeGHCupDir -> pure (Just (binDir </> "ghcup"))
|
||||||
|
|
||||||
runUpgrade (liftE $ upgradeGHCup dls target force pfreq) >>= \case
|
runUpgrade (liftE $ upgradeGHCup dls target force pfreq) >>= \case
|
||||||
VRight v' -> do
|
VRight v' -> do
|
||||||
@ -1677,12 +1654,12 @@ Make sure to clean up #{tmpdir} afterwards.|])
|
|||||||
Darwin -> "open"
|
Darwin -> "open"
|
||||||
Linux _ -> "xdg-open"
|
Linux _ -> "xdg-open"
|
||||||
FreeBSD -> "xdg-open"
|
FreeBSD -> "xdg-open"
|
||||||
|
Windows -> "start"
|
||||||
|
|
||||||
if clOpen
|
if clOpen
|
||||||
then
|
then
|
||||||
exec cmd
|
exec cmd
|
||||||
True
|
[T.unpack $ decUTF8Safe $ serializeURIRef' uri]
|
||||||
[serializeURIRef' uri]
|
|
||||||
Nothing
|
Nothing
|
||||||
Nothing
|
Nothing
|
||||||
>>= \case
|
>>= \case
|
||||||
@ -1977,10 +1954,10 @@ checkForUpdates dls pfreq = do
|
|||||||
prettyDebugInfo :: DebugInfo -> String
|
prettyDebugInfo :: DebugInfo -> String
|
||||||
prettyDebugInfo DebugInfo {..} = [i|Debug Info
|
prettyDebugInfo DebugInfo {..} = [i|Debug Info
|
||||||
==========
|
==========
|
||||||
GHCup base dir: #{toFilePath diBaseDir}
|
GHCup base dir: #{diBaseDir}
|
||||||
GHCup bin dir: #{toFilePath diBinDir}
|
GHCup bin dir: #{diBinDir}
|
||||||
GHCup GHC directory: #{toFilePath diGHCDir}
|
GHCup GHC directory: #{diGHCDir}
|
||||||
GHCup cache directory: #{toFilePath diCacheDir}
|
GHCup cache directory: #{diCacheDir}
|
||||||
Architecture: #{prettyShow diArch}
|
Architecture: #{prettyShow diArch}
|
||||||
Platform: #{prettyShow diPlatform}
|
Platform: #{prettyShow diPlatform}
|
||||||
Version: #{describe_result}|]
|
Version: #{describe_result}|]
|
||||||
|
@ -10,6 +10,11 @@ package ghcup
|
|||||||
tests: True
|
tests: True
|
||||||
flags: +tui
|
flags: +tui
|
||||||
|
|
||||||
|
source-repository-package
|
||||||
|
type: git
|
||||||
|
location: https://github.com/Bodigrim/tar
|
||||||
|
tag: ac197ec7ea4838dc2b4e22b9b888b080cedf29cf
|
||||||
|
|
||||||
constraints: http-io-streams -brotli
|
constraints: http-io-streams -brotli
|
||||||
|
|
||||||
package libarchive
|
package libarchive
|
||||||
|
150
ghcup-0.0.4.yaml
150
ghcup-0.0.4.yaml
@ -170,6 +170,11 @@ ghcupDownloads:
|
|||||||
dlUri: https://downloads.haskell.org/~ghc/7.10.3/ghc-7.10.3-x86_64-portbld-freebsd.tar.bz2
|
dlUri: https://downloads.haskell.org/~ghc/7.10.3/ghc-7.10.3-x86_64-portbld-freebsd.tar.bz2
|
||||||
dlSubdir: ghc-7.10.3
|
dlSubdir: ghc-7.10.3
|
||||||
dlHash: 2aa396edd2bb651f4bc7eef7a396913ea24923de5aafdc76df6295333e487e48
|
dlHash: 2aa396edd2bb651f4bc7eef7a396913ea24923de5aafdc76df6295333e487e48
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~ghc/7.10.3/ghc-7.10.3-x86_64-unknown-mingw32.tar.xz
|
||||||
|
dlSubdir: ghc-7.10.3
|
||||||
|
dlHash: 63e1689fc9e2809ae4d7f422b4dc810052e54c9aa2afd08746e234180e711dde
|
||||||
A_32:
|
A_32:
|
||||||
Linux_Debian:
|
Linux_Debian:
|
||||||
unknown_versioning: &ghc-7103-32-deb8
|
unknown_versioning: &ghc-7103-32-deb8
|
||||||
@ -236,6 +241,11 @@ ghcupDownloads:
|
|||||||
dlUri: https://downloads.haskell.org/~ghc/8.0.2/ghc-8.0.2-x86_64-portbld-freebsd.tar.xz
|
dlUri: https://downloads.haskell.org/~ghc/8.0.2/ghc-8.0.2-x86_64-portbld-freebsd.tar.xz
|
||||||
dlSubdir: ghc-8.0.2
|
dlSubdir: ghc-8.0.2
|
||||||
dlHash: b36a20e5cae24d70bbb6116ae486f21811e9384f15d3892d260f02fba3e3bb8c
|
dlHash: b36a20e5cae24d70bbb6116ae486f21811e9384f15d3892d260f02fba3e3bb8c
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~ghc/8.0.2/ghc-8.0.2-x86_64-unknown-mingw32.tar.xz
|
||||||
|
dlSubdir: ghc-8.0.2
|
||||||
|
dlHash: 8c42c1f4af995205b9816a1e97e2752fe758544c1f5fe77958cdcd319c9c2d53
|
||||||
A_32:
|
A_32:
|
||||||
Linux_Debian:
|
Linux_Debian:
|
||||||
'( >= 7 && < 8 )':
|
'( >= 7 && < 8 )':
|
||||||
@ -300,6 +310,11 @@ ghcupDownloads:
|
|||||||
dlSubdir: ghc-8.2.2
|
dlSubdir: ghc-8.2.2
|
||||||
dlHash: cd351c704b92b9af23994024df07de8ca7090ea7675d5c8b14b2be857a46d804
|
dlHash: cd351c704b92b9af23994024df07de8ca7090ea7675d5c8b14b2be857a46d804
|
||||||
unknown_versioning: *ghc-822-64-fbsd11
|
unknown_versioning: *ghc-822-64-fbsd11
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~ghc/8.2.2/ghc-8.2.2-x86_64-unknown-mingw32.tar.xz
|
||||||
|
dlSubdir: ghc-8.2.2
|
||||||
|
dlHash: 1e033df2092aa546e763e7be63167720b32df64f76673ea1ce7ae7c9f564b223
|
||||||
A_32:
|
A_32:
|
||||||
Linux_Debian:
|
Linux_Debian:
|
||||||
'( >= 7 && < 8 )':
|
'( >= 7 && < 8 )':
|
||||||
@ -359,6 +374,11 @@ ghcupDownloads:
|
|||||||
dlUri: https://downloads.haskell.org/~ghc/8.4.1/ghc-8.4.1-x86_64-portbld11-freebsd.tar.xz
|
dlUri: https://downloads.haskell.org/~ghc/8.4.1/ghc-8.4.1-x86_64-portbld11-freebsd.tar.xz
|
||||||
dlSubdir: ghc-8.4.1
|
dlSubdir: ghc-8.4.1
|
||||||
dlHash: e748daec098445c6190090fe32bb2817a1140553be5acd2188e1af05ad24e5aa
|
dlHash: e748daec098445c6190090fe32bb2817a1140553be5acd2188e1af05ad24e5aa
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~ghc/8.4.1/ghc-8.4.1-x86_64-unknown-mingw32.tar.xz
|
||||||
|
dlSubdir: ghc-8.4.1
|
||||||
|
dlHash: 328b013fc651d34e075019107e58bb6c8a578f0155cf3ad4557e6f2661b03131
|
||||||
A_32:
|
A_32:
|
||||||
Linux_Debian:
|
Linux_Debian:
|
||||||
unknown_versioning: &ghc-841-32-deb8
|
unknown_versioning: &ghc-841-32-deb8
|
||||||
@ -414,6 +434,11 @@ ghcupDownloads:
|
|||||||
dlUri: https://downloads.haskell.org/~ghc/8.4.2/ghc-8.4.2-x86_64-portbld-freebsd.tar.xz
|
dlUri: https://downloads.haskell.org/~ghc/8.4.2/ghc-8.4.2-x86_64-portbld-freebsd.tar.xz
|
||||||
dlSubdir: ghc-8.4.2
|
dlSubdir: ghc-8.4.2
|
||||||
dlHash: e9ed417fdf94c2ff2c6e344ed16f332bf6b591511f6442c0d9ea94854882b66c
|
dlHash: e9ed417fdf94c2ff2c6e344ed16f332bf6b591511f6442c0d9ea94854882b66c
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~ghc/8.4.2/ghc-8.4.2-x86_64-unknown-mingw32.tar.xz
|
||||||
|
dlSubdir: ghc-8.4.2
|
||||||
|
dlHash: 797634aa9812fc6b2084a24ddb4fde44fa83a2f59daea82e0af81ca3dd323fde
|
||||||
A_32:
|
A_32:
|
||||||
Linux_Debian:
|
Linux_Debian:
|
||||||
unknown_versioning: &ghc-842-32-deb8
|
unknown_versioning: &ghc-842-32-deb8
|
||||||
@ -464,6 +489,11 @@ ghcupDownloads:
|
|||||||
dlUri: https://downloads.haskell.org/~ghc/8.4.3/ghc-8.4.3-x86_64-apple-darwin.tar.xz
|
dlUri: https://downloads.haskell.org/~ghc/8.4.3/ghc-8.4.3-x86_64-apple-darwin.tar.xz
|
||||||
dlSubdir: ghc-8.4.3
|
dlSubdir: ghc-8.4.3
|
||||||
dlHash: af0b455f6c46b9802b4b48dad996619cfa27cc6e2bf2ce5532387b4a8c00aa64
|
dlHash: af0b455f6c46b9802b4b48dad996619cfa27cc6e2bf2ce5532387b4a8c00aa64
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~ghc/8.4.3/ghc-8.4.3-x86_64-unknown-mingw32.tar.xz
|
||||||
|
dlSubdir: ghc-8.4.3
|
||||||
|
dlHash: 8a83cfbf9ae84de0443c39c93b931693bdf2a6d4bf163ffb41855f80f4bf883e
|
||||||
A_32:
|
A_32:
|
||||||
Linux_Debian:
|
Linux_Debian:
|
||||||
unknown_versioning: &ghc-843-32-deb8
|
unknown_versioning: &ghc-843-32-deb8
|
||||||
@ -532,6 +562,11 @@ ghcupDownloads:
|
|||||||
dlUri: https://downloads.haskell.org/~ghc/8.4.4/ghc-8.4.4-x86_64-portbld-freebsd11.tar.xz
|
dlUri: https://downloads.haskell.org/~ghc/8.4.4/ghc-8.4.4-x86_64-portbld-freebsd11.tar.xz
|
||||||
dlSubdir: ghc-8.4.4
|
dlSubdir: ghc-8.4.4
|
||||||
dlHash: 44fbd142d1c355d6110595c59c760e2c73866ff9259ec85ebf814edb244d1940
|
dlHash: 44fbd142d1c355d6110595c59c760e2c73866ff9259ec85ebf814edb244d1940
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~ghc/8.4.4/ghc-8.4.4-x86_64-unknown-mingw32.tar.xz
|
||||||
|
dlSubdir: ghc-8.4.4
|
||||||
|
dlHash: da29dbb0f1199611c7d5bb7b0dd6a7426ca98f67dfd6da1526b033cd3830dc05
|
||||||
A_32:
|
A_32:
|
||||||
Linux_Debian:
|
Linux_Debian:
|
||||||
unknown_versioning: &ghc-844-32-deb8
|
unknown_versioning: &ghc-844-32-deb8
|
||||||
@ -592,6 +627,11 @@ ghcupDownloads:
|
|||||||
dlUri: https://downloads.haskell.org/~ghc/8.6.1/ghc-8.6.1-x86_64-portbld-freebsd.tar.xz
|
dlUri: https://downloads.haskell.org/~ghc/8.6.1/ghc-8.6.1-x86_64-portbld-freebsd.tar.xz
|
||||||
dlSubdir: ghc-8.6.1
|
dlSubdir: ghc-8.6.1
|
||||||
dlHash: 51403b054a3a649039ac988e1d1112561f96750bfced63df864091a3fab36f08
|
dlHash: 51403b054a3a649039ac988e1d1112561f96750bfced63df864091a3fab36f08
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~ghc/8.6.1/ghc-8.6.1-x86_64-unknown-mingw32.tar.xz
|
||||||
|
dlSubdir: ghc-8.6.1
|
||||||
|
dlHash: 7316d9cb5e486460476754f872c7bac30ee2082e42f46da4342f872d10b88099
|
||||||
A_32:
|
A_32:
|
||||||
Linux_Debian:
|
Linux_Debian:
|
||||||
unknown_versioning: &ghc-861-32-deb8
|
unknown_versioning: &ghc-861-32-deb8
|
||||||
@ -638,6 +678,11 @@ ghcupDownloads:
|
|||||||
dlUri: https://downloads.haskell.org/~ghc/8.6.2/ghc-8.6.2-x86_64-apple-darwin.tar.xz
|
dlUri: https://downloads.haskell.org/~ghc/8.6.2/ghc-8.6.2-x86_64-apple-darwin.tar.xz
|
||||||
dlSubdir: ghc-8.6.2
|
dlSubdir: ghc-8.6.2
|
||||||
dlHash: 8ec46a25872226dd7e5cf7271e3f3450c05f32144b96e6b9cb44cc4079db50dc
|
dlHash: 8ec46a25872226dd7e5cf7271e3f3450c05f32144b96e6b9cb44cc4079db50dc
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~ghc/8.6.2/ghc-8.6.2-x86_64-unknown-mingw32.tar.xz
|
||||||
|
dlSubdir: ghc-8.6.2
|
||||||
|
dlHash: 9a398e133cab09ff2610834337355d4e26c35e0665403fb9ff8db79315f74d3d
|
||||||
A_32:
|
A_32:
|
||||||
Linux_Debian:
|
Linux_Debian:
|
||||||
unknown_versioning: &ghc-862-32-deb8
|
unknown_versioning: &ghc-862-32-deb8
|
||||||
@ -702,6 +747,11 @@ ghcupDownloads:
|
|||||||
dlUri: https://downloads.haskell.org/~ghc/8.6.3/ghc-8.6.3-x86_64-portbld-freebsd.tar.xz
|
dlUri: https://downloads.haskell.org/~ghc/8.6.3/ghc-8.6.3-x86_64-portbld-freebsd.tar.xz
|
||||||
dlSubdir: ghc-8.6.3
|
dlSubdir: ghc-8.6.3
|
||||||
dlHash: bc2419fa180f8a7808c49775987866435995df9bdd9ce08bcd38352d63ba6031
|
dlHash: bc2419fa180f8a7808c49775987866435995df9bdd9ce08bcd38352d63ba6031
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~ghc/8.6.3/ghc-8.6.3-x86_64-unknown-mingw32.tar.xz
|
||||||
|
dlSubdir: ghc-8.6.3
|
||||||
|
dlHash: 2fec383904e5fa79413e9afd328faf9bc700006c8c3d4bcdd8d4f2ccf0f7fa2a
|
||||||
A_32:
|
A_32:
|
||||||
Linux_Debian:
|
Linux_Debian:
|
||||||
unknown_versioning: &ghc-863-32-deb8
|
unknown_versioning: &ghc-863-32-deb8
|
||||||
@ -752,6 +802,11 @@ ghcupDownloads:
|
|||||||
dlUri: https://downloads.haskell.org/~ghc/8.6.4/ghc-8.6.4-x86_64-apple-darwin.tar.xz
|
dlUri: https://downloads.haskell.org/~ghc/8.6.4/ghc-8.6.4-x86_64-apple-darwin.tar.xz
|
||||||
dlSubdir: ghc-8.6.4
|
dlSubdir: ghc-8.6.4
|
||||||
dlHash: cccb58f142fe41b601d73690809f6089f7715b6a50a09aa3d0104176ab4db09e
|
dlHash: cccb58f142fe41b601d73690809f6089f7715b6a50a09aa3d0104176ab4db09e
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~ghc/8.6.4/ghc-8.6.4-x86_64-unknown-mingw32.tar.xz
|
||||||
|
dlSubdir: ghc-8.6.4
|
||||||
|
dlHash: e8d021b7a90772fc559862079da20538498d991956d7557b468ca19ddda22a08
|
||||||
A_32:
|
A_32:
|
||||||
Linux_Debian:
|
Linux_Debian:
|
||||||
unknown_versioning: &ghc-864-32-deb9
|
unknown_versioning: &ghc-864-32-deb9
|
||||||
@ -820,6 +875,11 @@ ghcupDownloads:
|
|||||||
dlUri: https://downloads.haskell.org/ghcup/unofficial-bindists/ghc/8.6.5/ghc-8.6.5-x86_64-portbld-freebsd.tar.xz
|
dlUri: https://downloads.haskell.org/ghcup/unofficial-bindists/ghc/8.6.5/ghc-8.6.5-x86_64-portbld-freebsd.tar.xz
|
||||||
dlSubdir: ghc-8.6.5
|
dlSubdir: ghc-8.6.5
|
||||||
dlHash: 83a3059a630d40a98e26cb5b520354e12094a96e36ba2f5ab002dad94cf2fb37
|
dlHash: 83a3059a630d40a98e26cb5b520354e12094a96e36ba2f5ab002dad94cf2fb37
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~ghc/8.6.5/ghc-8.6.5-x86_64-unknown-mingw32.tar.xz
|
||||||
|
dlSubdir: ghc-8.6.5
|
||||||
|
dlHash: 457024c6ea43bdce340af428d86319931f267089398b859b00efdfe2fd4ce93f
|
||||||
A_32:
|
A_32:
|
||||||
Linux_Debian:
|
Linux_Debian:
|
||||||
unknown_versioning: &ghc-865-32-deb9
|
unknown_versioning: &ghc-865-32-deb9
|
||||||
@ -890,6 +950,11 @@ ghcupDownloads:
|
|||||||
dlUri: https://downloads.haskell.org/~ghc/8.8.1/ghc-8.8.1-x86_64-apple-darwin.tar.xz
|
dlUri: https://downloads.haskell.org/~ghc/8.8.1/ghc-8.8.1-x86_64-apple-darwin.tar.xz
|
||||||
dlSubdir: ghc-8.8.1
|
dlSubdir: ghc-8.8.1
|
||||||
dlHash: 38c8917b47c31bedf58c9305dfca3abe198d8d35570366f0773c4e2948bd8abe
|
dlHash: 38c8917b47c31bedf58c9305dfca3abe198d8d35570366f0773c4e2948bd8abe
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~ghc/8.8.1/ghc-8.8.1-x86_64-unknown-mingw32.tar.xz
|
||||||
|
dlSubdir: ghc-8.8.1
|
||||||
|
dlHash: 29e56e6af38017a5a76b2b6995a39d3988fa58131e4b55b62dd317ba7186ac9b
|
||||||
A_32:
|
A_32:
|
||||||
Linux_Debian:
|
Linux_Debian:
|
||||||
unknown_versioning: &ghc-881-32-deb9
|
unknown_versioning: &ghc-881-32-deb9
|
||||||
@ -949,6 +1014,11 @@ ghcupDownloads:
|
|||||||
dlUri: https://downloads.haskell.org/~ghc/8.8.2/ghc-8.8.2-x86_64-apple-darwin.tar.xz
|
dlUri: https://downloads.haskell.org/~ghc/8.8.2/ghc-8.8.2-x86_64-apple-darwin.tar.xz
|
||||||
dlSubdir: ghc-8.8.2
|
dlSubdir: ghc-8.8.2
|
||||||
dlHash: 25c5c1a70036abf3f22b2b19c10d26adfdb08e8f8574f89d4b2042de5947f990
|
dlHash: 25c5c1a70036abf3f22b2b19c10d26adfdb08e8f8574f89d4b2042de5947f990
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~ghc/8.8.2/ghc-8.8.2-x86_64-unknown-mingw32.tar.xz
|
||||||
|
dlSubdir: ghc-8.8.2
|
||||||
|
dlHash: e25d9b16ee62cafc7387af2cd021eea676a99cd2c32b83533b016162c63065d9
|
||||||
A_32:
|
A_32:
|
||||||
Linux_Debian:
|
Linux_Debian:
|
||||||
unknown_versioning: &ghc-882-32-deb9
|
unknown_versioning: &ghc-882-32-deb9
|
||||||
@ -1013,6 +1083,11 @@ ghcupDownloads:
|
|||||||
dlUri: https://downloads.haskell.org/ghcup/unofficial-bindists/ghc/8.8.3/ghc-8.8.3-x86_64-portbld-freebsd.tar.xz
|
dlUri: https://downloads.haskell.org/ghcup/unofficial-bindists/ghc/8.8.3/ghc-8.8.3-x86_64-portbld-freebsd.tar.xz
|
||||||
dlSubdir: ghc-8.8.3
|
dlSubdir: ghc-8.8.3
|
||||||
dlHash: 569719075b4d14b3875a899df522090ae31e6fe085e6dffe518e875b09a2f0be
|
dlHash: 569719075b4d14b3875a899df522090ae31e6fe085e6dffe518e875b09a2f0be
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~ghc/8.8.3/ghc-8.8.3-x86_64-unknown-mingw32.tar.xz
|
||||||
|
dlSubdir: ghc-8.8.3
|
||||||
|
dlHash: e22586762af0911c06e8140f1792e3ca381a3a482a20d67b9054883038b3a422
|
||||||
A_32:
|
A_32:
|
||||||
Linux_Debian:
|
Linux_Debian:
|
||||||
unknown_versioning: &ghc-883-32-deb9
|
unknown_versioning: &ghc-883-32-deb9
|
||||||
@ -1087,6 +1162,11 @@ ghcupDownloads:
|
|||||||
dlUri: https://downloads.haskell.org/ghcup/unofficial-bindists/ghc/8.8.4/ghc-8.8.4-x86_64-portbld-freebsd.tar.xz
|
dlUri: https://downloads.haskell.org/ghcup/unofficial-bindists/ghc/8.8.4/ghc-8.8.4-x86_64-portbld-freebsd.tar.xz
|
||||||
dlSubdir: ghc-8.8.4
|
dlSubdir: ghc-8.8.4
|
||||||
dlHash: 8cebe5ccf454e82acd1ff52ca57590d1ab0f3f44a981b46257ec12158c8c447e
|
dlHash: 8cebe5ccf454e82acd1ff52ca57590d1ab0f3f44a981b46257ec12158c8c447e
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~ghc/8.8.4/ghc-8.8.4-x86_64-unknown-mingw32.tar.xz
|
||||||
|
dlSubdir: ghc-8.8.4
|
||||||
|
dlHash: d185055d2c8dc3bfe5b88afd59d6877eb1e722b672d1c9649f18296e148ed71f
|
||||||
A_32:
|
A_32:
|
||||||
Linux_Debian:
|
Linux_Debian:
|
||||||
unknown_versioning: &ghc-884-32-deb9
|
unknown_versioning: &ghc-884-32-deb9
|
||||||
@ -1164,6 +1244,11 @@ ghcupDownloads:
|
|||||||
dlUri: https://downloads.haskell.org/ghcup/unofficial-bindists/ghc/8.10.1/ghc-8.10.1-x86_64-portbld-freebsd.tar.xz
|
dlUri: https://downloads.haskell.org/ghcup/unofficial-bindists/ghc/8.10.1/ghc-8.10.1-x86_64-portbld-freebsd.tar.xz
|
||||||
dlSubdir: ghc-8.10.1
|
dlSubdir: ghc-8.10.1
|
||||||
dlHash: e8646ec9b60fd40aa9505ee055f22f04601290ab7a1342c2cf37c34de9d3f142
|
dlHash: e8646ec9b60fd40aa9505ee055f22f04601290ab7a1342c2cf37c34de9d3f142
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~ghc/8.10.1/ghc-8.10.1-x86_64-unknown-mingw32.tar.xz
|
||||||
|
dlSubdir: ghc-8.10.1
|
||||||
|
dlHash: 38a3166ea50cccd5bae7e1680eae3aae2b4ae31b61f82a1d8168fb821f43bd67
|
||||||
A_32:
|
A_32:
|
||||||
Linux_Debian:
|
Linux_Debian:
|
||||||
'( >= 9 && < 10 )': &ghc-8101-32-deb9
|
'( >= 9 && < 10 )': &ghc-8101-32-deb9
|
||||||
@ -1254,6 +1339,11 @@ ghcupDownloads:
|
|||||||
dlUri: https://downloads.haskell.org/~ghc/8.10.2/ghc-8.10.2-x86_64-unknown-freebsd.tar.xz
|
dlUri: https://downloads.haskell.org/~ghc/8.10.2/ghc-8.10.2-x86_64-unknown-freebsd.tar.xz
|
||||||
dlSubdir: ghc-8.10.2
|
dlSubdir: ghc-8.10.2
|
||||||
dlHash: 9e5957f3497f4b58ecd3699568d9caaa11a47a6d7e902032c261e450fa0f6686
|
dlHash: 9e5957f3497f4b58ecd3699568d9caaa11a47a6d7e902032c261e450fa0f6686
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~ghc/8.10.2/ghc-8.10.2-x86_64-unknown-mingw32.tar.xz
|
||||||
|
dlSubdir: ghc-8.10.2
|
||||||
|
dlHash: dcae4c173b9896e07ff048de5509aa0a4537233150e06e5ce8848303dfadc176
|
||||||
A_32:
|
A_32:
|
||||||
Linux_Debian:
|
Linux_Debian:
|
||||||
'( >= 9 && < 10 )': &ghc-8102-32-deb9
|
'( >= 9 && < 10 )': &ghc-8102-32-deb9
|
||||||
@ -1343,6 +1433,11 @@ ghcupDownloads:
|
|||||||
dlUri: https://downloads.haskell.org/~ghc/8.10.3/ghc-8.10.3-x86_64-portbld-freebsd.tar.xz
|
dlUri: https://downloads.haskell.org/~ghc/8.10.3/ghc-8.10.3-x86_64-portbld-freebsd.tar.xz
|
||||||
dlSubdir: ghc-8.10.3
|
dlSubdir: ghc-8.10.3
|
||||||
dlHash: 749007e995104db05cf6e3ad5bc36238cab8afac8055145661e5730e8f8af040
|
dlHash: 749007e995104db05cf6e3ad5bc36238cab8afac8055145661e5730e8f8af040
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~ghc/8.10.3/ghc-8.10.3-x86_64-unknown-mingw32.tar.xz
|
||||||
|
dlSubdir: ghc-8.10.3
|
||||||
|
dlHash: 927a6c699533a115cd49772ef2c753d9af2c13bf9f0b2d3bd13645cc6a144ee3
|
||||||
A_32:
|
A_32:
|
||||||
Linux_Debian:
|
Linux_Debian:
|
||||||
'( >= 9 && < 10 )': &ghc-8103-32-deb9
|
'( >= 9 && < 10 )': &ghc-8103-32-deb9
|
||||||
@ -1434,6 +1529,11 @@ ghcupDownloads:
|
|||||||
dlUri: https://downloads.haskell.org/~ghc/8.10.4/ghc-8.10.4-x86_64-portbld-freebsd.tar.xz
|
dlUri: https://downloads.haskell.org/~ghc/8.10.4/ghc-8.10.4-x86_64-portbld-freebsd.tar.xz
|
||||||
dlSubdir: ghc-8.10.4
|
dlSubdir: ghc-8.10.4
|
||||||
dlHash: c9776a2ccf9629b03e967206a507fcdcb6c5189800a626e9461ababf6733c357
|
dlHash: c9776a2ccf9629b03e967206a507fcdcb6c5189800a626e9461ababf6733c357
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~ghc/8.10.4/ghc-8.10.4-x86_64-unknown-mingw32.tar.xz
|
||||||
|
dlSubdir: ghc-8.10.4
|
||||||
|
dlHash: e9175a276504c3390a5e0084954e6997d56078737dbe7158049518892cf6bfb2
|
||||||
A_32:
|
A_32:
|
||||||
Linux_Debian:
|
Linux_Debian:
|
||||||
'( >= 9 && < 10 )': &ghc-8104-32-deb9
|
'( >= 9 && < 10 )': &ghc-8104-32-deb9
|
||||||
@ -1524,6 +1624,11 @@ ghcupDownloads:
|
|||||||
dlUri: https://downloads.haskell.org/~ghc/9.0.1/ghc-9.0.1-x86_64-portbld-freebsd.tar.xz
|
dlUri: https://downloads.haskell.org/~ghc/9.0.1/ghc-9.0.1-x86_64-portbld-freebsd.tar.xz
|
||||||
dlSubdir: ghc-9.0.1
|
dlSubdir: ghc-9.0.1
|
||||||
dlHash: 9dbc06d8832cae5c9f86dd7b2db729b3748a47beb4fd4b1e62bb66119817c3c1
|
dlHash: 9dbc06d8832cae5c9f86dd7b2db729b3748a47beb4fd4b1e62bb66119817c3c1
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~ghc/9.0.1/ghc-9.0.1-x86_64-unknown-mingw32.tar.xz
|
||||||
|
dlSubdir: ghc-9.0.1-x86_64-unknown-mingw32
|
||||||
|
dlHash: 4f4ab118df01cbc7e7c510096deca0cb25025339a97730de0466416296202493
|
||||||
A_32:
|
A_32:
|
||||||
Linux_Debian:
|
Linux_Debian:
|
||||||
'( >= 9 && < 10 )': &ghc-901-32-deb9
|
'( >= 9 && < 10 )': &ghc-901-32-deb9
|
||||||
@ -1614,6 +1719,11 @@ ghcupDownloads:
|
|||||||
dlUri: https://downloads.haskell.org/~ghc/9.2.1-alpha2/ghc-9.2.0.20210422-x86_64-apple-darwin.tar.xz
|
dlUri: https://downloads.haskell.org/~ghc/9.2.1-alpha2/ghc-9.2.0.20210422-x86_64-apple-darwin.tar.xz
|
||||||
dlSubdir: ghc-9.2.0.20210422
|
dlSubdir: ghc-9.2.0.20210422
|
||||||
dlHash: 8884c059f2b76e4c4309ff6bd7a7dde37663f751fd26220e9a2bcabb4d69a401
|
dlHash: 8884c059f2b76e4c4309ff6bd7a7dde37663f751fd26220e9a2bcabb4d69a401
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~ghc/9.2.1-alpha2/ghc-9.2.0.20210422-x86_64-unknown-mingw32.tar.xz
|
||||||
|
dlSubdir: ghc-9.0.1-x86_64-unknown-mingw32
|
||||||
|
dlHash: 33f173b754d18f26bb27f52bb77a92fd22a48675daa2b43a1879bf01dddd7e8f
|
||||||
A_32:
|
A_32:
|
||||||
Linux_Debian:
|
Linux_Debian:
|
||||||
'( >= 9 && < 10 )': &ghc-921-alpha2-32-deb9
|
'( >= 9 && < 10 )': &ghc-921-alpha2-32-deb9
|
||||||
@ -1666,6 +1776,11 @@ ghcupDownloads:
|
|||||||
dlUri: https://downloads.haskell.org/~cabal/cabal-install-2.4.1.0/cabal-install-2.4.1.0-x86_64-portbld-freebsd.tar.xz
|
dlUri: https://downloads.haskell.org/~cabal/cabal-install-2.4.1.0/cabal-install-2.4.1.0-x86_64-portbld-freebsd.tar.xz
|
||||||
dlSubdir:
|
dlSubdir:
|
||||||
dlHash: 33b7d37ea0688c93436eac9ec139d9967687875aa1fa13f2bb73bf05a9a59a1d
|
dlHash: 33b7d37ea0688c93436eac9ec139d9967687875aa1fa13f2bb73bf05a9a59a1d
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~cabal/cabal-install-2.4.1.0/cabal-install-2.4.1.0-x86_64-unknown-mingw32.zip
|
||||||
|
dlSubdir:
|
||||||
|
dlHash: 95f233efedb1ebf0e6db015fa2f55c1ed00b9938d207ec63c066f706fb4b6373
|
||||||
A_32:
|
A_32:
|
||||||
Linux_UnknownLinux:
|
Linux_UnknownLinux:
|
||||||
unknown_versioning:
|
unknown_versioning:
|
||||||
@ -1694,6 +1809,11 @@ ghcupDownloads:
|
|||||||
unknown_versioning:
|
unknown_versioning:
|
||||||
dlUri: https://downloads.haskell.org/~cabal/cabal-install-3.0.0.0/cabal-install-3.0.0.0-x86_64-portbld-freebsd.tar.xz
|
dlUri: https://downloads.haskell.org/~cabal/cabal-install-3.0.0.0/cabal-install-3.0.0.0-x86_64-portbld-freebsd.tar.xz
|
||||||
dlHash: 2240842ab2ae7b955feb8b526aba1c7991248c803383107adf39990441294d2a
|
dlHash: 2240842ab2ae7b955feb8b526aba1c7991248c803383107adf39990441294d2a
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~cabal/cabal-install-3.0.0.0/cabal-install-3.0.0.0-x86_64-unknown-mingw32.zip
|
||||||
|
dlSubdir:
|
||||||
|
dlHash: 8889963ebef5e829d86329fdb59832c107efd117cf7862a605f2fe2d2360de1f
|
||||||
A_32:
|
A_32:
|
||||||
Linux_Alpine:
|
Linux_Alpine:
|
||||||
unknown_versioning:
|
unknown_versioning:
|
||||||
@ -1725,6 +1845,11 @@ ghcupDownloads:
|
|||||||
unknown_versioning:
|
unknown_versioning:
|
||||||
dlUri: https://downloads.haskell.org/~cabal/cabal-install-3.2.0.0/cabal-install-3.2.0.0-x86_64-portbld-freebsd.tar.xz
|
dlUri: https://downloads.haskell.org/~cabal/cabal-install-3.2.0.0/cabal-install-3.2.0.0-x86_64-portbld-freebsd.tar.xz
|
||||||
dlHash: f1e35151cca91541b0fb4bdb3ed18f3c348038eab751845ad19c11307d66c273
|
dlHash: f1e35151cca91541b0fb4bdb3ed18f3c348038eab751845ad19c11307d66c273
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~cabal/cabal-install-3.2.0.0/cabal-install-3.2.0.0-x86_64-unknown-mingw32.zip
|
||||||
|
dlSubdir:
|
||||||
|
dlHash: 17778c3ade0482bc37f451eec326f8fce8fbe1f12b1d6aacb2e2b9e34786c054
|
||||||
A_32:
|
A_32:
|
||||||
Linux_Alpine:
|
Linux_Alpine:
|
||||||
unknown_versioning:
|
unknown_versioning:
|
||||||
@ -1759,6 +1884,11 @@ ghcupDownloads:
|
|||||||
unknown_versioning:
|
unknown_versioning:
|
||||||
dlUri: https://downloads.haskell.org/~cabal/cabal-install-3.4.0.0/cabal-install-3.4.0.0-x86_64-freebsd-12.1-release.tar.xz
|
dlUri: https://downloads.haskell.org/~cabal/cabal-install-3.4.0.0/cabal-install-3.4.0.0-x86_64-freebsd-12.1-release.tar.xz
|
||||||
dlHash: a1e2db664ec00e42a1e071a4181f6476f6e0bad321f1ddc0cf27831119f4c6d4
|
dlHash: a1e2db664ec00e42a1e071a4181f6476f6e0bad321f1ddc0cf27831119f4c6d4
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/~cabal/cabal-install-3.4.0.0/cabal-install-3.4.0.0-x86_64-windows.zip
|
||||||
|
dlSubdir:
|
||||||
|
dlHash: 860fff2d39a82d1dc0ca924a77164d0988af49c2c5f45e15d615144122beb647
|
||||||
A_32:
|
A_32:
|
||||||
Linux_UnknownLinux:
|
Linux_UnknownLinux:
|
||||||
unknown_versioning: &cabal-3400-32
|
unknown_versioning: &cabal-3400-32
|
||||||
@ -1797,6 +1927,10 @@ ghcupDownloads:
|
|||||||
unknown_versioning:
|
unknown_versioning:
|
||||||
dlUri: https://downloads.haskell.org/~ghcup/0.1.14.1/x86_64-portbld-freebsd-ghcup-0.1.14.1
|
dlUri: https://downloads.haskell.org/~ghcup/0.1.14.1/x86_64-portbld-freebsd-ghcup-0.1.14.1
|
||||||
dlHash: 89a70980d77888dae8b9fd0f05e7a7920f421bc3bb5192da8e73fd4e7b4cb86f
|
dlHash: 89a70980d77888dae8b9fd0f05e7a7920f421bc3bb5192da8e73fd4e7b4cb86f
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://TODO
|
||||||
|
dlHash: 89a70980d77888dae8b9fd0f05e7a7920f421bc3bb5192da8e73fd4e7b4cb86f
|
||||||
Linux_Alpine:
|
Linux_Alpine:
|
||||||
unknown_versioning: *ghcup-64
|
unknown_versioning: *ghcup-64
|
||||||
A_32:
|
A_32:
|
||||||
@ -1833,6 +1967,10 @@ ghcupDownloads:
|
|||||||
unknown_versioning:
|
unknown_versioning:
|
||||||
dlUri: https://github.com/haskell/haskell-language-server/releases/download/1.1.0/haskell-language-server-macOS-1.1.0.tar.gz
|
dlUri: https://github.com/haskell/haskell-language-server/releases/download/1.1.0/haskell-language-server-macOS-1.1.0.tar.gz
|
||||||
dlHash: 4e89b192e2f49637d772e974f2c17b16da067ecd5912575eaa542551de97681b
|
dlHash: 4e89b192e2f49637d772e974f2c17b16da067ecd5912575eaa542551de97681b
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://downloads.haskell.org/ghcup/unofficial-bindists/hls/1.1.0/haskell-language-server-Windows-1.1.0.tar.gz
|
||||||
|
dlHash: a1d3f451e64a041aa527a25da29e4716a2de6ae347cef4ef9312fc7611e168cc
|
||||||
Linux_Alpine:
|
Linux_Alpine:
|
||||||
unknown_versioning: *hls-64
|
unknown_versioning: *hls-64
|
||||||
Stack:
|
Stack:
|
||||||
@ -1853,6 +1991,12 @@ ghcupDownloads:
|
|||||||
dlHash: f4aedfa8fbe371f77286ee97ec5c3c553842e7ae15b2952a8b8442dccba04bf0
|
dlHash: f4aedfa8fbe371f77286ee97ec5c3c553842e7ae15b2952a8b8442dccba04bf0
|
||||||
dlSubdir:
|
dlSubdir:
|
||||||
RegexDir: "stack-.*"
|
RegexDir: "stack-.*"
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://github.com/commercialhaskell/stack/releases/download/v2.5.1/stack-2.5.1-windows-x86_64.tar.gz
|
||||||
|
dlHash: 57150b422cfd42249f5e629d0eb678df6d95dabe486ced57e8298d300b940d41
|
||||||
|
dlSubdir:
|
||||||
|
RegexDir: "stack-.*"
|
||||||
Linux_Alpine:
|
Linux_Alpine:
|
||||||
unknown_versioning: *stack-251-64
|
unknown_versioning: *stack-251-64
|
||||||
2.7.1:
|
2.7.1:
|
||||||
@ -1874,6 +2018,12 @@ ghcupDownloads:
|
|||||||
dlHash: 4248c6fbc87e8a2c06f39e867eb5ef28eae0d99470137cb415356c631c0dcbf2
|
dlHash: 4248c6fbc87e8a2c06f39e867eb5ef28eae0d99470137cb415356c631c0dcbf2
|
||||||
dlSubdir:
|
dlSubdir:
|
||||||
RegexDir: "stack-.*"
|
RegexDir: "stack-.*"
|
||||||
|
Windows:
|
||||||
|
unknown_versioning:
|
||||||
|
dlUri: https://github.com/commercialhaskell/stack/releases/download/v2.7.1/stack-2.7.1-windows-x86_64.tar.gz
|
||||||
|
dlHash: 8452f5fc9235620a84863f2f68e5f681c72d0d181cde50482f178a966ee0ceb9
|
||||||
|
dlSubdir:
|
||||||
|
RegexDir: "stack-.*"
|
||||||
Linux_Alpine:
|
Linux_Alpine:
|
||||||
unknown_versioning: *stack-64
|
unknown_versioning: *stack-64
|
||||||
|
|
||||||
|
71
ghcup.cabal
71
ghcup.cabal
@ -28,19 +28,23 @@ source-repository head
|
|||||||
location: https://gitlab.haskell.org/haskell/ghcup-hs.git
|
location: https://gitlab.haskell.org/haskell/ghcup-hs.git
|
||||||
|
|
||||||
flag tui
|
flag tui
|
||||||
description: Build the brick powered tui (ghcup tui)
|
description:
|
||||||
|
Build the brick powered tui (ghcup tui). This is disabled on windows.
|
||||||
|
|
||||||
default: False
|
default: False
|
||||||
manual: True
|
manual: True
|
||||||
|
|
||||||
flag internal-downloader
|
flag internal-downloader
|
||||||
description:
|
description:
|
||||||
Compile the internal downloader, which links against OpenSSL
|
Compile the internal downloader, which links against OpenSSL. This is disabled on windows.
|
||||||
|
|
||||||
default: False
|
default: False
|
||||||
manual: True
|
manual: True
|
||||||
|
|
||||||
flag tar
|
flag tar
|
||||||
description: Use tar-bytestring instead of libarchive
|
description:
|
||||||
|
Use tar-bytestring instead of libarchive. This is always enabled on windows.
|
||||||
|
|
||||||
default: False
|
default: False
|
||||||
manual: True
|
manual: True
|
||||||
|
|
||||||
@ -58,6 +62,7 @@ library
|
|||||||
GHCup.Utils
|
GHCup.Utils
|
||||||
GHCup.Utils.Dirs
|
GHCup.Utils.Dirs
|
||||||
GHCup.Utils.File
|
GHCup.Utils.File
|
||||||
|
GHCup.Utils.File.Common
|
||||||
GHCup.Utils.Logger
|
GHCup.Utils.Logger
|
||||||
GHCup.Utils.MegaParsec
|
GHCup.Utils.MegaParsec
|
||||||
GHCup.Utils.Prelude
|
GHCup.Utils.Prelude
|
||||||
@ -90,21 +95,19 @@ library
|
|||||||
, base16-bytestring >=0.1.1.6 && <1.1
|
, base16-bytestring >=0.1.1.6 && <1.1
|
||||||
, binary ^>=0.8.6.0
|
, binary ^>=0.8.6.0
|
||||||
, bytestring ^>=0.10
|
, bytestring ^>=0.10
|
||||||
, bz2 >=0.5.0.5 && <1.1
|
|
||||||
, case-insensitive ^>=1.2.1.0
|
, case-insensitive ^>=1.2.1.0
|
||||||
, casing ^>=0.1.4.1
|
, casing ^>=0.1.4.1
|
||||||
, concurrent-output ^>=1.10.11
|
, concurrent-output ^>=1.10.11
|
||||||
, containers ^>=0.6
|
, containers ^>=0.6
|
||||||
, cryptohash-sha256 ^>=0.11.101.0
|
, cryptohash-sha256 ^>=0.11.101.0
|
||||||
|
, deepseq ^>=1.4.4.0
|
||||||
|
, directory ^>=1.3.6.0
|
||||||
, disk-free-space ^>=0.1.0.1
|
, disk-free-space ^>=0.1.0.1
|
||||||
|
, extra ^>=1.7.9
|
||||||
|
, filepath ^>=1.4.2.1
|
||||||
, generics-sop ^>=0.5
|
, generics-sop ^>=0.5
|
||||||
, haskus-utils-types ^>=1.5
|
, haskus-utils-types ^>=1.5
|
||||||
, haskus-utils-variant >=3.0 && <3.2
|
, haskus-utils-variant >=3.0 && <3.2
|
||||||
, hpath >=0.11 && <0.13
|
|
||||||
, hpath-directory ^>=0.14.1
|
|
||||||
, hpath-filepath ^>=0.10.3
|
|
||||||
, hpath-io ^>=0.14.1
|
|
||||||
, hpath-posix ^>=0.13.2
|
|
||||||
, lzma-static ^>=5.2.5.2
|
, lzma-static ^>=5.2.5.2
|
||||||
, megaparsec >=8.0.0 && <9.1
|
, megaparsec >=8.0.0 && <9.1
|
||||||
, monad-logger ^>=0.3.31
|
, monad-logger ^>=0.3.31
|
||||||
@ -115,6 +118,7 @@ library
|
|||||||
, parsec ^>=3.1
|
, parsec ^>=3.1
|
||||||
, pretty ^>=1.1.3.1
|
, pretty ^>=1.1.3.1
|
||||||
, pretty-terminal ^>=0.1.0.0
|
, pretty-terminal ^>=0.1.0.0
|
||||||
|
, process ^>=1.6.9.0
|
||||||
, regex-posix ^>=0.96
|
, regex-posix ^>=0.96
|
||||||
, resourcet ^>=1.2.2
|
, resourcet ^>=1.2.2
|
||||||
, safe ^>=0.3.18
|
, safe ^>=0.3.18
|
||||||
@ -122,27 +126,25 @@ library
|
|||||||
, split ^>=0.2.3.4
|
, split ^>=0.2.3.4
|
||||||
, streamly ^>=0.7.3
|
, streamly ^>=0.7.3
|
||||||
, streamly-bytestring ^>=0.1.2
|
, streamly-bytestring ^>=0.1.2
|
||||||
, streamly-posix ^>=0.1.0.0
|
|
||||||
, strict-base ^>=0.4
|
, strict-base ^>=0.4
|
||||||
, string-interpolate >=0.2.0.0 && <0.4
|
, string-interpolate >=0.2.0.0 && <0.4
|
||||||
, template-haskell >=2.7 && <2.17
|
, template-haskell >=2.7 && <2.17
|
||||||
|
, temporary ^>=1.3
|
||||||
, text ^>=1.2.4.0
|
, text ^>=1.2.4.0
|
||||||
, time ^>=1.9.3
|
, time ^>=1.9.3
|
||||||
, transformers ^>=0.5
|
, transformers ^>=0.5
|
||||||
, unix ^>=2.7
|
|
||||||
, unix-bytestring ^>=0.3
|
|
||||||
, unliftio-core ^>=0.2.0.1
|
, unliftio-core ^>=0.2.0.1
|
||||||
, unordered-containers ^>=0.2.10.0
|
, unordered-containers ^>=0.2.10.0
|
||||||
, uri-bytestring ^>=0.3.2.2
|
, uri-bytestring ^>=0.3.2.2
|
||||||
, utf8-string ^>=1.0
|
, utf8-string ^>=1.0
|
||||||
, vector ^>=0.12
|
, vector ^>=0.12
|
||||||
, versions ^>=4.0.1
|
, versions ^>=4.0.1
|
||||||
, vty >=5.28.2 && <5.34
|
|
||||||
, word8 ^>=0.1.3
|
, word8 ^>=0.1.3
|
||||||
, yaml ^>=0.11.4.0
|
, yaml ^>=0.11.4.0
|
||||||
|
, zip ^>=1.7.0
|
||||||
, zlib ^>=0.6.2.2
|
, zlib ^>=0.6.2.2
|
||||||
|
|
||||||
if flag(internal-downloader)
|
if (flag(internal-downloader) && !os(windows))
|
||||||
exposed-modules: GHCup.Download.IOStreams
|
exposed-modules: GHCup.Download.IOStreams
|
||||||
cpp-options: -DINTERNAL_DOWNLOADER
|
cpp-options: -DINTERNAL_DOWNLOADER
|
||||||
build-depends:
|
build-depends:
|
||||||
@ -151,13 +153,31 @@ library
|
|||||||
, io-streams >=1.5
|
, io-streams >=1.5
|
||||||
, terminal-progress-bar >=0.4.1
|
, terminal-progress-bar >=0.4.1
|
||||||
|
|
||||||
if flag(tar)
|
if (flag(tar) || os(windows))
|
||||||
cpp-options: -DTAR
|
cpp-options: -DTAR
|
||||||
build-depends: tar-bytestring ^>=0.6.3.1
|
build-depends: tar
|
||||||
|
|
||||||
else
|
else
|
||||||
build-depends: libarchive ^>=3.0.0.0
|
build-depends: libarchive ^>=3.0.0.0
|
||||||
|
|
||||||
|
if os(windows)
|
||||||
|
cpp-options: -DIS_WINDOWS
|
||||||
|
other-modules: GHCup.Utils.File.Windows
|
||||||
|
build-depends: bzlib
|
||||||
|
|
||||||
|
else
|
||||||
|
other-modules: GHCup.Utils.File.Posix
|
||||||
|
build-depends:
|
||||||
|
bz2 >=0.5.0.5 && <1.1
|
||||||
|
, hpath-posix ^>=0.13.3
|
||||||
|
, streamly-posix ^>=0.1.0.0
|
||||||
|
, unix ^>=2.7
|
||||||
|
, unix-bytestring ^>=0.3.7.3
|
||||||
|
|
||||||
|
if (flag(tui) && !os(windows))
|
||||||
|
cpp-options: -DBRICK
|
||||||
|
build-depends: vty >=5.28.2 && <5.34
|
||||||
|
|
||||||
executable ghcup
|
executable ghcup
|
||||||
main-is: Main.hs
|
main-is: Main.hs
|
||||||
hs-source-dirs: app/ghcup
|
hs-source-dirs: app/ghcup
|
||||||
@ -181,10 +201,9 @@ executable ghcup
|
|||||||
, base >=4.13 && <5
|
, base >=4.13 && <5
|
||||||
, bytestring ^>=0.10
|
, bytestring ^>=0.10
|
||||||
, containers ^>=0.6
|
, containers ^>=0.6
|
||||||
|
, filepath ^>=1.4.2.1
|
||||||
, ghcup
|
, ghcup
|
||||||
, haskus-utils-variant >=3.0 && <3.2
|
, haskus-utils-variant >=3.0 && <3.2
|
||||||
, hpath >=0.11 && <0.13
|
|
||||||
, hpath-io ^>=0.14.1
|
|
||||||
, megaparsec >=8.0.0 && <9.1
|
, megaparsec >=8.0.0 && <9.1
|
||||||
, monad-logger ^>=0.3.31
|
, monad-logger ^>=0.3.31
|
||||||
, mtl ^>=2.2
|
, mtl ^>=2.2
|
||||||
@ -204,7 +223,7 @@ executable ghcup
|
|||||||
if flag(internal-downloader)
|
if flag(internal-downloader)
|
||||||
cpp-options: -DINTERNAL_DOWNLOADER
|
cpp-options: -DINTERNAL_DOWNLOADER
|
||||||
|
|
||||||
if flag(tui)
|
if (flag(tui) && !os(windows))
|
||||||
cpp-options: -DBRICK
|
cpp-options: -DBRICK
|
||||||
other-modules: BrickMain
|
other-modules: BrickMain
|
||||||
build-depends:
|
build-depends:
|
||||||
@ -212,7 +231,7 @@ executable ghcup
|
|||||||
, vector ^>=0.12
|
, vector ^>=0.12
|
||||||
, vty >=5.28.2 && <5.34
|
, vty >=5.28.2 && <5.34
|
||||||
|
|
||||||
if flag(tar)
|
if (flag(tar) || os(windows))
|
||||||
cpp-options: -DTAR
|
cpp-options: -DTAR
|
||||||
|
|
||||||
else
|
else
|
||||||
@ -241,10 +260,9 @@ executable ghcup-gen
|
|||||||
, base >=4.13 && <5
|
, base >=4.13 && <5
|
||||||
, bytestring ^>=0.10
|
, bytestring ^>=0.10
|
||||||
, containers ^>=0.6
|
, containers ^>=0.6
|
||||||
|
, filepath ^>=1.4.2.1
|
||||||
, ghcup
|
, ghcup
|
||||||
, haskus-utils-variant >=3.0 && <3.2
|
, haskus-utils-variant >=3.0 && <3.2
|
||||||
, hpath >=0.11 && <0.13
|
|
||||||
, hpath-filepath ^>=0.10.3
|
|
||||||
, monad-logger ^>=0.3.31
|
, monad-logger ^>=0.3.31
|
||||||
, mtl ^>=2.2
|
, mtl ^>=2.2
|
||||||
, optics >=0.2 && <0.5
|
, optics >=0.2 && <0.5
|
||||||
@ -262,9 +280,9 @@ executable ghcup-gen
|
|||||||
, versions ^>=4.0.1
|
, versions ^>=4.0.1
|
||||||
, yaml ^>=0.11.4.0
|
, yaml ^>=0.11.4.0
|
||||||
|
|
||||||
if flag(tar)
|
if (flag(tar) || os(windows))
|
||||||
cpp-options: -DTAR
|
cpp-options: -DTAR
|
||||||
build-depends: tar-bytestring ^>=0.6.3.1
|
build-depends: tar
|
||||||
|
|
||||||
else
|
else
|
||||||
build-depends: libarchive ^>=3.0.0.0
|
build-depends: libarchive ^>=3.0.0.0
|
||||||
@ -297,9 +315,8 @@ test-suite ghcup-test
|
|||||||
, containers ^>=0.6
|
, containers ^>=0.6
|
||||||
, generic-arbitrary ^>=0.1.0
|
, generic-arbitrary ^>=0.1.0
|
||||||
, ghcup
|
, ghcup
|
||||||
, hpath >=0.11 && <0.13
|
, hspec ^>=2.7.10
|
||||||
, hspec ^>=2.7.4
|
, hspec-golden-aeson >=0.9 && <0.10
|
||||||
, hspec-golden-aeson >=0.7 && <0.10
|
|
||||||
, QuickCheck ^>=2.14.1
|
, QuickCheck ^>=2.14.1
|
||||||
, quickcheck-arbitrary-adt ^>=0.3.1.0
|
, quickcheck-arbitrary-adt ^>=0.3.1.0
|
||||||
, text ^>=1.2.4.0
|
, text ^>=1.2.4.0
|
||||||
|
317
lib/GHCup.hs
317
lib/GHCup.hs
@ -18,7 +18,7 @@ Copyright : (c) Julian Ospald, 2020
|
|||||||
License : LGPL-3.0
|
License : LGPL-3.0
|
||||||
Maintainer : hasufell@hasufell.de
|
Maintainer : hasufell@hasufell.de
|
||||||
Stability : experimental
|
Stability : experimental
|
||||||
Portability : POSIX
|
Portability : portable
|
||||||
|
|
||||||
This module contains the main functions that correspond
|
This module contains the main functions that correspond
|
||||||
to the command line interface, like installation, listing versions
|
to the command line interface, like installation, listing versions
|
||||||
@ -58,6 +58,7 @@ import Control.Monad.Trans.Resource
|
|||||||
import Data.ByteString ( ByteString )
|
import Data.ByteString ( ByteString )
|
||||||
import Data.Either
|
import Data.Either
|
||||||
import Data.List
|
import Data.List
|
||||||
|
import Data.List.Extra
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.String ( fromString )
|
import Data.String ( fromString )
|
||||||
import Data.String.Interpolate
|
import Data.String.Interpolate
|
||||||
@ -65,10 +66,7 @@ import Data.Text ( Text )
|
|||||||
import Data.Time.Clock
|
import Data.Time.Clock
|
||||||
import Data.Time.Format.ISO8601
|
import Data.Time.Format.ISO8601
|
||||||
import Data.Versions
|
import Data.Versions
|
||||||
import Data.Word8
|
|
||||||
import GHC.IO.Exception
|
import GHC.IO.Exception
|
||||||
import HPath
|
|
||||||
import HPath.IO hiding ( hideError )
|
|
||||||
import Haskus.Utils.Variant.Excepts
|
import Haskus.Utils.Variant.Excepts
|
||||||
import Optics
|
import Optics
|
||||||
import Prelude hiding ( abs
|
import Prelude hiding ( abs
|
||||||
@ -76,10 +74,10 @@ import Prelude hiding ( abs
|
|||||||
, writeFile
|
, writeFile
|
||||||
)
|
)
|
||||||
import Safe hiding ( at )
|
import Safe hiding ( at )
|
||||||
|
import System.Directory hiding ( findFiles )
|
||||||
|
import System.Environment
|
||||||
|
import System.FilePath
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
import System.Posix.Env.ByteString ( getEnvironment, getEnv )
|
|
||||||
import System.Posix.FilePath ( getSearchPath, takeExtension )
|
|
||||||
import System.Posix.Files.ByteString
|
|
||||||
import Text.PrettyPrint.HughesPJClass ( prettyShow )
|
import Text.PrettyPrint.HughesPJClass ( prettyShow )
|
||||||
import Text.Regex.Posix
|
import Text.Regex.Posix
|
||||||
|
|
||||||
@ -149,7 +147,7 @@ installGHCBindist dlinfo ver pfreq = do
|
|||||||
|
|
||||||
where
|
where
|
||||||
toolchainSanityChecks = do
|
toolchainSanityChecks = do
|
||||||
r <- forM ["CC", "LD"] (liftIO . getEnv)
|
r <- forM ["CC", "LD"] (liftIO . lookupEnv)
|
||||||
case catMaybes r of
|
case catMaybes r of
|
||||||
[] -> pure ()
|
[] -> pure ()
|
||||||
_ -> do
|
_ -> do
|
||||||
@ -168,9 +166,9 @@ installPackedGHC :: ( MonadMask m
|
|||||||
, MonadIO m
|
, MonadIO m
|
||||||
, MonadUnliftIO m
|
, MonadUnliftIO m
|
||||||
)
|
)
|
||||||
=> Path Abs -- ^ Path to the packed GHC bindist
|
=> FilePath -- ^ Path to the packed GHC bindist
|
||||||
-> Maybe TarDir -- ^ Subdir of the archive
|
-> Maybe TarDir -- ^ Subdir of the archive
|
||||||
-> Path Abs -- ^ Path to install to
|
-> FilePath -- ^ Path to install to
|
||||||
-> Version -- ^ The GHC version
|
-> Version -- ^ The GHC version
|
||||||
-> PlatformRequest
|
-> PlatformRequest
|
||||||
-> Excepts
|
-> Excepts
|
||||||
@ -204,18 +202,24 @@ installUnpackedGHC :: ( MonadReader AppState m
|
|||||||
, MonadLogger m
|
, MonadLogger m
|
||||||
, MonadIO m
|
, MonadIO m
|
||||||
)
|
)
|
||||||
=> Path Abs -- ^ Path to the unpacked GHC bindist (where the configure script resides)
|
=> FilePath -- ^ Path to the unpacked GHC bindist (where the configure script resides)
|
||||||
-> Path Abs -- ^ Path to install to
|
-> FilePath -- ^ Path to install to
|
||||||
-> Version -- ^ The GHC version
|
-> Version -- ^ The GHC version
|
||||||
-> PlatformRequest
|
-> PlatformRequest
|
||||||
-> Excepts '[ProcessError] m ()
|
-> Excepts '[ProcessError] m ()
|
||||||
|
#if defined(IS_WINDOWS)
|
||||||
|
installUnpackedGHC path inst _ _ = do
|
||||||
|
lift $ $(logInfo) "Installing GHC (this may take a while)"
|
||||||
|
-- windows bindists are relocatable and don't need
|
||||||
|
-- to run configure
|
||||||
|
liftIO $ copyDirectoryRecursive path inst
|
||||||
|
#else
|
||||||
installUnpackedGHC path inst ver PlatformRequest{..} = do
|
installUnpackedGHC path inst ver PlatformRequest{..} = do
|
||||||
lift $ $(logInfo) "Installing GHC (this may take a while)"
|
lift $ $(logInfo) "Installing GHC (this may take a while)"
|
||||||
lEM $ execLogged "./configure"
|
lEM $ execLogged "sh"
|
||||||
False
|
("./configure" : ("--prefix=" <> inst) : alpineArgs)
|
||||||
(("--prefix=" <> toFilePath inst) : alpineArgs)
|
|
||||||
[rel|ghc-configure|]
|
|
||||||
(Just path)
|
(Just path)
|
||||||
|
"ghc-configure"
|
||||||
Nothing
|
Nothing
|
||||||
lEM $ make ["install"] (Just path)
|
lEM $ make ["install"] (Just path)
|
||||||
pure ()
|
pure ()
|
||||||
@ -225,6 +229,7 @@ installUnpackedGHC path inst ver PlatformRequest{..} = do
|
|||||||
= ["--disable-ld-override"]
|
= ["--disable-ld-override"]
|
||||||
| otherwise
|
| otherwise
|
||||||
= []
|
= []
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
-- | Installs GHC into @~\/.ghcup\/ghc/\<ver\>@ and places the
|
-- | Installs GHC into @~\/.ghcup\/ghc/\<ver\>@ and places the
|
||||||
@ -301,9 +306,9 @@ installCabalBindist dlinfo ver PlatformRequest {..} = do
|
|||||||
whenM
|
whenM
|
||||||
(lift (cabalInstalled ver) >>= \a -> liftIO $
|
(lift (cabalInstalled ver) >>= \a -> liftIO $
|
||||||
handleIO (\_ -> pure False)
|
handleIO (\_ -> pure False)
|
||||||
$ fmap (\x -> a && isSymbolicLink x)
|
$ fmap (\x -> a && x)
|
||||||
-- ignore when the installation is a legacy cabal (binary, not symlink)
|
-- ignore when the installation is a legacy cabal (binary, not symlink)
|
||||||
$ getSymbolicLinkStatus (toFilePath (binDir </> [rel|cabal|]))
|
$ pathIsSymbolicLink (binDir </> "cabal" <> exeExt)
|
||||||
)
|
)
|
||||||
(throwE $ AlreadyInstalled Cabal ver)
|
(throwE $ AlreadyInstalled Cabal ver)
|
||||||
|
|
||||||
@ -328,19 +333,18 @@ installCabalBindist dlinfo ver PlatformRequest {..} = do
|
|||||||
where
|
where
|
||||||
-- | Install an unpacked cabal distribution.
|
-- | Install an unpacked cabal distribution.
|
||||||
installCabal' :: (MonadLogger m, MonadCatch m, MonadIO m)
|
installCabal' :: (MonadLogger m, MonadCatch m, MonadIO m)
|
||||||
=> Path Abs -- ^ Path to the unpacked cabal bindist (where the executable resides)
|
=> FilePath -- ^ Path to the unpacked cabal bindist (where the executable resides)
|
||||||
-> Path Abs -- ^ Path to install to
|
-> FilePath -- ^ Path to install to
|
||||||
-> Excepts '[CopyError] m ()
|
-> Excepts '[CopyError] m ()
|
||||||
installCabal' path inst = do
|
installCabal' path inst = do
|
||||||
lift $ $(logInfo) "Installing cabal"
|
lift $ $(logInfo) "Installing cabal"
|
||||||
let cabalFile = [rel|cabal|]
|
let cabalFile = "cabal"
|
||||||
liftIO $ createDirRecursive' inst
|
liftIO $ createDirRecursive' inst
|
||||||
destFileName <- lift $ parseRel (toFilePath cabalFile <> "-" <> verToBS ver)
|
let destFileName = cabalFile <> "-" <> T.unpack (prettyVer ver) <> exeExt
|
||||||
let destPath = inst </> destFileName
|
let destPath = inst </> destFileName
|
||||||
handleIO (throwE . CopyError . show) $ liftIO $ copyFile
|
handleIO (throwE . CopyError . show) $ liftIO $ copyFile
|
||||||
(path </> cabalFile)
|
(path </> cabalFile <> exeExt)
|
||||||
destPath
|
destPath
|
||||||
Overwrite
|
|
||||||
lift $ chmod_755 destPath
|
lift $ chmod_755 destPath
|
||||||
|
|
||||||
|
|
||||||
@ -437,8 +441,8 @@ installHLSBindist dlinfo ver PlatformRequest{..} = do
|
|||||||
where
|
where
|
||||||
-- | Install an unpacked hls distribution.
|
-- | Install an unpacked hls distribution.
|
||||||
installHLS' :: (MonadFail m, MonadLogger m, MonadCatch m, MonadIO m)
|
installHLS' :: (MonadFail m, MonadLogger m, MonadCatch m, MonadIO m)
|
||||||
=> Path Abs -- ^ Path to the unpacked hls bindist (where the executable resides)
|
=> FilePath -- ^ Path to the unpacked hls bindist (where the executable resides)
|
||||||
-> Path Abs -- ^ Path to install to
|
-> FilePath -- ^ Path to install to
|
||||||
-> Excepts '[CopyError] m ()
|
-> Excepts '[CopyError] m ()
|
||||||
installHLS' path inst = do
|
installHLS' path inst = do
|
||||||
lift $ $(logInfo) "Installing HLS"
|
lift $ $(logInfo) "Installing HLS"
|
||||||
@ -452,20 +456,19 @@ installHLSBindist dlinfo ver PlatformRequest{..} = do
|
|||||||
([s|^haskell-language-server-[0-9].*$|] :: ByteString)
|
([s|^haskell-language-server-[0-9].*$|] :: ByteString)
|
||||||
)
|
)
|
||||||
forM_ bins $ \f -> do
|
forM_ bins $ \f -> do
|
||||||
toF <- parseRel (toFilePath f <> "~" <> verToBS ver)
|
let toF = dropSuffix exeExt f
|
||||||
|
<> "~" <> T.unpack (prettyVer ver) <> exeExt
|
||||||
handleIO (throwE . CopyError . show) $ liftIO $ copyFile
|
handleIO (throwE . CopyError . show) $ liftIO $ copyFile
|
||||||
(path </> f)
|
(path </> f)
|
||||||
(inst </> toF)
|
(inst </> toF)
|
||||||
Overwrite
|
|
||||||
lift $ chmod_755 (inst </> toF)
|
lift $ chmod_755 (inst </> toF)
|
||||||
|
|
||||||
-- install haskell-language-server-wrapper
|
-- install haskell-language-server-wrapper
|
||||||
let wrapper = [rel|haskell-language-server-wrapper|]
|
let wrapper = "haskell-language-server-wrapper"
|
||||||
toF <- parseRel (toFilePath wrapper <> "-" <> verToBS ver)
|
toF = wrapper <> "-" <> T.unpack (prettyVer ver) <> exeExt
|
||||||
handleIO (throwE . CopyError . show) $ liftIO $ copyFile
|
handleIO (throwE . CopyError . show) $ liftIO $ copyFile
|
||||||
(path </> wrapper)
|
(path </> wrapper <> exeExt)
|
||||||
(inst </> toF)
|
(inst </> toF)
|
||||||
Overwrite
|
|
||||||
lift $ chmod_755 (inst </> toF)
|
lift $ chmod_755 (inst </> toF)
|
||||||
|
|
||||||
|
|
||||||
@ -596,19 +599,18 @@ installStackBindist dlinfo ver PlatformRequest {..} = do
|
|||||||
where
|
where
|
||||||
-- | Install an unpacked stack distribution.
|
-- | Install an unpacked stack distribution.
|
||||||
installStack' :: (MonadLogger m, MonadCatch m, MonadIO m)
|
installStack' :: (MonadLogger m, MonadCatch m, MonadIO m)
|
||||||
=> Path Abs -- ^ Path to the unpacked stack bindist (where the executable resides)
|
=> FilePath -- ^ Path to the unpacked stack bindist (where the executable resides)
|
||||||
-> Path Abs -- ^ Path to install to
|
-> FilePath -- ^ Path to install to
|
||||||
-> Excepts '[CopyError] m ()
|
-> Excepts '[CopyError] m ()
|
||||||
installStack' path inst = do
|
installStack' path inst = do
|
||||||
lift $ $(logInfo) "Installing stack"
|
lift $ $(logInfo) "Installing stack"
|
||||||
let stackFile = [rel|stack|]
|
let stackFile = "stack"
|
||||||
liftIO $ createDirRecursive' inst
|
liftIO $ createDirRecursive' inst
|
||||||
destFileName <- lift $ parseRel (toFilePath stackFile <> "-" <> verToBS ver)
|
let destFileName = stackFile <> "-" <> T.unpack (prettyVer ver) <> exeExt
|
||||||
let destPath = inst </> destFileName
|
let destPath = inst </> destFileName
|
||||||
handleIO (throwE . CopyError . show) $ liftIO $ copyFile
|
handleIO (throwE . CopyError . show) $ liftIO $ copyFile
|
||||||
(path </> stackFile)
|
(path </> stackFile <> exeExt)
|
||||||
destPath
|
destPath
|
||||||
Overwrite
|
|
||||||
lift $ chmod_755 destPath
|
lift $ chmod_755 destPath
|
||||||
|
|
||||||
|
|
||||||
@ -640,7 +642,7 @@ setGHC :: ( MonadReader AppState m
|
|||||||
-> SetGHC
|
-> SetGHC
|
||||||
-> Excepts '[NotInstalled] m GHCTargetVersion
|
-> Excepts '[NotInstalled] m GHCTargetVersion
|
||||||
setGHC ver sghc = do
|
setGHC ver sghc = do
|
||||||
let verBS = verToBS (_tvVersion ver)
|
let verS = T.unpack $ prettyVer (_tvVersion ver)
|
||||||
ghcdir <- lift $ ghcupGHCDir ver
|
ghcdir <- lift $ ghcupGHCDir ver
|
||||||
|
|
||||||
whenM (lift $ not <$> ghcInstalled ver) (throwE (NotInstalled GHC ver))
|
whenM (lift $ not <$> ghcInstalled ver) (throwE (NotInstalled GHC ver))
|
||||||
@ -662,49 +664,50 @@ setGHC ver sghc = do
|
|||||||
mTargetFile <- case sghc of
|
mTargetFile <- case sghc of
|
||||||
SetGHCOnly -> pure $ Just file
|
SetGHCOnly -> pure $ Just file
|
||||||
SetGHC_XY -> do
|
SetGHC_XY -> do
|
||||||
v' <-
|
handle
|
||||||
handle
|
|
||||||
(\(e :: ParseError) -> lift $ $(logWarn) [i|#{e}|] >> pure Nothing)
|
(\(e :: ParseError) -> lift $ $(logWarn) [i|#{e}|] >> pure Nothing)
|
||||||
$ fmap Just
|
$ do
|
||||||
$ getMajorMinorV (_tvVersion ver)
|
(mj, mi) <- getMajorMinorV (_tvVersion ver)
|
||||||
forM v' $ \(mj, mi) ->
|
let major' = intToText mj <> "." <> intToText mi
|
||||||
let major' = E.encodeUtf8 $ intToText mj <> "." <> intToText mi
|
pure $ Just (file <> "-" <> T.unpack major')
|
||||||
in parseRel (toFilePath file <> B.singleton _hyphen <> major')
|
|
||||||
SetGHC_XYZ ->
|
SetGHC_XYZ ->
|
||||||
fmap Just $ parseRel (toFilePath file <> B.singleton _hyphen <> verBS)
|
pure $ Just (file <> "-" <> verS)
|
||||||
|
|
||||||
-- create symlink
|
-- create symlink
|
||||||
forM mTargetFile $ \targetFile -> do
|
forM mTargetFile $ \targetFile -> do
|
||||||
let fullF = binDir </> targetFile
|
let fullF = binDir </> targetFile <> exeExt
|
||||||
destL <- lift $ ghcLinkDestination (toFilePath file) ver
|
fileWithExt = file <> exeExt
|
||||||
lift $ $(logDebug) [i|ln -s #{destL} #{toFilePath fullF}|]
|
destL <- lift $ ghcLinkDestination fileWithExt ver
|
||||||
liftIO $ createSymlink fullF destL
|
lift $ $(logDebug) [i|rm -f #{fullF}|]
|
||||||
|
liftIO $ hideError doesNotExistErrorType $ removeFile fullF
|
||||||
|
lift $ $(logDebug) [i|ln -s #{destL} #{fullF}|]
|
||||||
|
liftIO $ createFileLink destL fullF
|
||||||
|
|
||||||
-- create symlink for share dir
|
-- create symlink for share dir
|
||||||
when (isNothing . _tvTarget $ ver) $ lift $ symlinkShareDir ghcdir verBS
|
when (isNothing . _tvTarget $ ver) $ lift $ symlinkShareDir ghcdir verS
|
||||||
|
|
||||||
pure ver
|
pure ver
|
||||||
|
|
||||||
where
|
where
|
||||||
|
|
||||||
symlinkShareDir :: (MonadReader AppState m, MonadIO m, MonadLogger m)
|
symlinkShareDir :: (MonadReader AppState m, MonadIO m, MonadLogger m)
|
||||||
=> Path Abs
|
=> FilePath
|
||||||
-> ByteString
|
-> String
|
||||||
-> m ()
|
-> m ()
|
||||||
symlinkShareDir ghcdir verBS = do
|
symlinkShareDir ghcdir ver' = do
|
||||||
AppState { dirs = Dirs {..} } <- ask
|
AppState { dirs = Dirs {..} } <- ask
|
||||||
let destdir = baseDir
|
let destdir = baseDir
|
||||||
case sghc of
|
case sghc of
|
||||||
SetGHCOnly -> do
|
SetGHCOnly -> do
|
||||||
let sharedir = [rel|share|]
|
let sharedir = "share"
|
||||||
let fullsharedir = ghcdir </> sharedir
|
let fullsharedir = ghcdir </> sharedir
|
||||||
whenM (liftIO $ doesDirectoryExist fullsharedir) $ do
|
whenM (liftIO $ doesDirectoryExist fullsharedir) $ do
|
||||||
let fullF = destdir </> sharedir
|
let fullF = destdir </> sharedir
|
||||||
let targetF = "./ghc/" <> verBS <> "/" <> toFilePath sharedir
|
let targetF = "." </> "ghc" </> ver' </> sharedir
|
||||||
$(logDebug) [i|rm -f #{fullF}|]
|
$(logDebug) [i|rm -f #{fullF}|]
|
||||||
liftIO $ hideError doesNotExistErrorType $ deleteFile fullF
|
liftIO $ hideError doesNotExistErrorType $ removeFile fullF
|
||||||
$(logDebug) [i|ln -s #{targetF} #{fullF}|]
|
$(logDebug) [i|ln -s #{targetF} #{fullF}|]
|
||||||
liftIO $ createSymlink fullF targetF
|
liftIO $ createDirectoryLink targetF fullF
|
||||||
_ -> pure ()
|
_ -> pure ()
|
||||||
|
|
||||||
|
|
||||||
@ -714,8 +717,7 @@ setCabal :: (MonadReader AppState m, MonadLogger m, MonadThrow m, MonadFail m, M
|
|||||||
=> Version
|
=> Version
|
||||||
-> Excepts '[NotInstalled] m ()
|
-> Excepts '[NotInstalled] m ()
|
||||||
setCabal ver = do
|
setCabal ver = do
|
||||||
let verBS = verToBS ver
|
let targetFile = "cabal-" <> T.unpack (prettyVer ver) <> exeExt
|
||||||
targetFile <- parseRel ("cabal-" <> verBS)
|
|
||||||
|
|
||||||
-- symlink destination
|
-- symlink destination
|
||||||
AppState {dirs = Dirs {..}} <- lift ask
|
AppState {dirs = Dirs {..}} <- lift ask
|
||||||
@ -725,17 +727,17 @@ setCabal ver = do
|
|||||||
$ throwE
|
$ throwE
|
||||||
$ NotInstalled Cabal (GHCTargetVersion Nothing ver)
|
$ NotInstalled Cabal (GHCTargetVersion Nothing ver)
|
||||||
|
|
||||||
let cabalbin = binDir </> [rel|cabal|]
|
let cabalbin = binDir </> "cabal" <> exeExt
|
||||||
|
|
||||||
-- delete old file (may be binary or symlink)
|
-- delete old file (may be binary or symlink)
|
||||||
lift $ $(logDebug) [i|rm -f #{toFilePath cabalbin}|]
|
lift $ $(logDebug) [i|rm -f #{cabalbin}|]
|
||||||
liftIO $ hideError doesNotExistErrorType $ deleteFile
|
liftIO $ hideError doesNotExistErrorType $ removeFile
|
||||||
cabalbin
|
cabalbin
|
||||||
|
|
||||||
-- create symlink
|
-- create symlink
|
||||||
let destL = toFilePath targetFile
|
let destL = targetFile
|
||||||
lift $ $(logDebug) [i|ln -s #{destL} #{toFilePath cabalbin}|]
|
lift $ $(logDebug) [i|ln -s #{destL} #{cabalbin}|]
|
||||||
liftIO $ createSymlink cabalbin destL
|
liftIO $ createFileLink destL cabalbin
|
||||||
|
|
||||||
pure ()
|
pure ()
|
||||||
|
|
||||||
@ -760,32 +762,32 @@ setHLS ver = do
|
|||||||
-- selected version, so we could end up with stray or incorrect symlinks.
|
-- selected version, so we could end up with stray or incorrect symlinks.
|
||||||
oldSyms <- lift hlsSymlinks
|
oldSyms <- lift hlsSymlinks
|
||||||
forM_ oldSyms $ \f -> do
|
forM_ oldSyms $ \f -> do
|
||||||
lift $ $(logDebug) [i|rm #{toFilePath (binDir </> f)}|]
|
lift $ $(logDebug) [i|rm #{binDir </> f}|]
|
||||||
liftIO $ deleteFile (binDir </> f)
|
liftIO $ removeFile (binDir </> f)
|
||||||
|
|
||||||
-- set haskell-language-server-<ghcver> symlinks
|
-- set haskell-language-server-<ghcver> symlinks
|
||||||
bins <- lift $ hlsServerBinaries ver
|
bins <- lift $ hlsServerBinaries ver
|
||||||
when (null bins) $ throwE $ NotInstalled HLS (GHCTargetVersion Nothing ver)
|
when (null bins) $ throwE $ NotInstalled HLS (GHCTargetVersion Nothing ver)
|
||||||
|
|
||||||
forM_ bins $ \f -> do
|
forM_ bins $ \f -> do
|
||||||
let destL = toFilePath f
|
let destL = f
|
||||||
target <- parseRel . head . B.split _tilde . toFilePath $ f
|
let target = (<> exeExt) . head . splitOn "~" $ f
|
||||||
|
|
||||||
lift $ $(logDebug) [i|rm -f #{toFilePath (binDir </> target)}|]
|
lift $ $(logDebug) [i|rm -f #{binDir </> target}|]
|
||||||
liftIO $ hideError doesNotExistErrorType $ deleteFile (binDir </> target)
|
liftIO $ hideError doesNotExistErrorType $ removeFile (binDir </> target)
|
||||||
|
|
||||||
lift $ $(logDebug) [i|ln -s #{destL} #{toFilePath (binDir </> target)}|]
|
lift $ $(logDebug) [i|ln -s #{destL} #{binDir </> target}|]
|
||||||
liftIO $ createSymlink (binDir </> target) destL
|
liftIO $ createFileLink destL (binDir </> target)
|
||||||
|
|
||||||
-- set haskell-language-server-wrapper symlink
|
-- set haskell-language-server-wrapper symlink
|
||||||
let destL = "haskell-language-server-wrapper-" <> verToBS ver
|
let destL = "haskell-language-server-wrapper-" <> T.unpack (prettyVer ver) <> exeExt
|
||||||
let wrapper = binDir </> [rel|haskell-language-server-wrapper|]
|
let wrapper = binDir </> "haskell-language-server-wrapper" <> exeExt
|
||||||
|
|
||||||
lift $ $(logDebug) [i|rm -f #{toFilePath wrapper}|]
|
lift $ $(logDebug) [i|rm -f #{wrapper}|]
|
||||||
liftIO $ hideError doesNotExistErrorType $ deleteFile wrapper
|
liftIO $ hideError doesNotExistErrorType $ removeFile wrapper
|
||||||
|
|
||||||
lift $ $(logDebug) [i|ln -s #{destL} #{toFilePath wrapper}|]
|
lift $ $(logDebug) [i|ln -s #{destL} #{wrapper}|]
|
||||||
liftIO $ createSymlink wrapper destL
|
liftIO $ createFileLink destL wrapper
|
||||||
|
|
||||||
pure ()
|
pure ()
|
||||||
|
|
||||||
@ -795,8 +797,7 @@ setStack :: (MonadReader AppState m, MonadLogger m, MonadThrow m, MonadFail m, M
|
|||||||
=> Version
|
=> Version
|
||||||
-> Excepts '[NotInstalled] m ()
|
-> Excepts '[NotInstalled] m ()
|
||||||
setStack ver = do
|
setStack ver = do
|
||||||
let verBS = verToBS ver
|
let targetFile = "stack-" <> T.unpack (prettyVer ver) <> exeExt
|
||||||
targetFile <- parseRel ("stack-" <> verBS)
|
|
||||||
|
|
||||||
-- symlink destination
|
-- symlink destination
|
||||||
AppState {dirs = Dirs {..}} <- lift ask
|
AppState {dirs = Dirs {..}} <- lift ask
|
||||||
@ -806,17 +807,16 @@ setStack ver = do
|
|||||||
$ throwE
|
$ throwE
|
||||||
$ NotInstalled Stack (GHCTargetVersion Nothing ver)
|
$ NotInstalled Stack (GHCTargetVersion Nothing ver)
|
||||||
|
|
||||||
let stackbin = binDir </> [rel|stack|]
|
let stackbin = binDir </> "stack" <> exeExt
|
||||||
|
|
||||||
-- delete old file (may be binary or symlink)
|
-- delete old file (may be binary or symlink)
|
||||||
lift $ $(logDebug) [i|rm -f #{toFilePath stackbin}|]
|
lift $ $(logDebug) [i|rm -f #{stackbin}|]
|
||||||
liftIO $ hideError doesNotExistErrorType $ deleteFile
|
liftIO $ hideError doesNotExistErrorType $ removeFile
|
||||||
stackbin
|
stackbin
|
||||||
|
|
||||||
-- create symlink
|
-- create symlink
|
||||||
let destL = toFilePath targetFile
|
lift $ $(logDebug) [i|ln -s #{targetFile} #{stackbin}|]
|
||||||
lift $ $(logDebug) [i|ln -s #{destL} #{toFilePath stackbin}|]
|
liftIO $ createFileLink targetFile stackbin
|
||||||
liftIO $ createSymlink stackbin destL
|
|
||||||
|
|
||||||
pure ()
|
pure ()
|
||||||
|
|
||||||
@ -948,13 +948,13 @@ listVersions av lt' criteria pfreq = do
|
|||||||
}
|
}
|
||||||
Left e -> do
|
Left e -> do
|
||||||
$(logWarn)
|
$(logWarn)
|
||||||
[i|Could not parse version of stray directory #{toFilePath e}|]
|
[i|Could not parse version of stray directory #{e}|]
|
||||||
pure Nothing
|
pure Nothing
|
||||||
|
|
||||||
strayCabals :: (MonadReader AppState m, MonadCatch m, MonadThrow m, MonadLogger m, MonadIO m)
|
strayCabals :: (MonadReader AppState m, MonadCatch m, MonadThrow m, MonadLogger m, MonadIO m)
|
||||||
=> Map.Map Version [Tag]
|
=> Map.Map Version [Tag]
|
||||||
-> Maybe Version
|
-> Maybe Version
|
||||||
-> [Either (Path Rel) Version]
|
-> [Either FilePath Version]
|
||||||
-> m [ListResult]
|
-> m [ListResult]
|
||||||
strayCabals avTools cSet cabals = do
|
strayCabals avTools cSet cabals = do
|
||||||
fmap catMaybes $ forM cabals $ \case
|
fmap catMaybes $ forM cabals $ \case
|
||||||
@ -977,7 +977,7 @@ listVersions av lt' criteria pfreq = do
|
|||||||
}
|
}
|
||||||
Left e -> do
|
Left e -> do
|
||||||
$(logWarn)
|
$(logWarn)
|
||||||
[i|Could not parse version of stray directory #{toFilePath e}|]
|
[i|Could not parse version of stray directory #{e}|]
|
||||||
pure Nothing
|
pure Nothing
|
||||||
|
|
||||||
strayHLS :: (MonadReader AppState m, MonadCatch m, MonadThrow m, MonadLogger m, MonadIO m)
|
strayHLS :: (MonadReader AppState m, MonadCatch m, MonadThrow m, MonadLogger m, MonadIO m)
|
||||||
@ -1005,7 +1005,7 @@ listVersions av lt' criteria pfreq = do
|
|||||||
}
|
}
|
||||||
Left e -> do
|
Left e -> do
|
||||||
$(logWarn)
|
$(logWarn)
|
||||||
[i|Could not parse version of stray directory #{toFilePath e}|]
|
[i|Could not parse version of stray directory #{e}|]
|
||||||
pure Nothing
|
pure Nothing
|
||||||
|
|
||||||
strayStacks :: (MonadReader AppState m, MonadCatch m, MonadThrow m, MonadLogger m, MonadIO m)
|
strayStacks :: (MonadReader AppState m, MonadCatch m, MonadThrow m, MonadLogger m, MonadIO m)
|
||||||
@ -1033,18 +1033,18 @@ listVersions av lt' criteria pfreq = do
|
|||||||
}
|
}
|
||||||
Left e -> do
|
Left e -> do
|
||||||
$(logWarn)
|
$(logWarn)
|
||||||
[i|Could not parse version of stray directory #{toFilePath e}|]
|
[i|Could not parse version of stray directory #{e}|]
|
||||||
pure Nothing
|
pure Nothing
|
||||||
|
|
||||||
-- NOTE: this are not cross ones, because no bindists
|
-- NOTE: this are not cross ones, because no bindists
|
||||||
toListResult :: (MonadLogger m, MonadReader AppState m, MonadIO m, MonadCatch m)
|
toListResult :: (MonadLogger m, MonadReader AppState m, MonadIO m, MonadCatch m)
|
||||||
=> Tool
|
=> Tool
|
||||||
-> Maybe Version
|
-> Maybe Version
|
||||||
-> [Either (Path Rel) Version]
|
-> [Either FilePath Version]
|
||||||
-> Maybe Version
|
-> Maybe Version
|
||||||
-> [Either (Path Rel) Version]
|
-> [Either FilePath Version]
|
||||||
-> Maybe Version
|
-> Maybe Version
|
||||||
-> [Either (Path Rel) Version]
|
-> [Either FilePath Version]
|
||||||
-> (Version, [Tag])
|
-> (Version, [Tag])
|
||||||
-> m ListResult
|
-> m ListResult
|
||||||
toListResult t cSet cabals hlsSet' hlses stackSet' stacks (v, tags) = case t of
|
toListResult t cSet cabals hlsSet' hlses stackSet' stacks (v, tags) = case t of
|
||||||
@ -1156,8 +1156,8 @@ rmGHCVer ver = do
|
|||||||
handle (\(_ :: ParseError) -> pure ()) $ liftE $ rmMajorSymlinks ver
|
handle (\(_ :: ParseError) -> pure ()) $ liftE $ rmMajorSymlinks ver
|
||||||
-- then fix them (e.g. with an earlier version)
|
-- then fix them (e.g. with an earlier version)
|
||||||
|
|
||||||
lift $ $(logInfo) [i|Removing directory recursively: #{toFilePath dir}|]
|
lift $ $(logInfo) [i|Removing directory recursively: #{dir}|]
|
||||||
liftIO $ deleteDirRecursive dir
|
liftIO $ removeDirectoryRecursive dir
|
||||||
|
|
||||||
v' <-
|
v' <-
|
||||||
handle
|
handle
|
||||||
@ -1171,7 +1171,7 @@ rmGHCVer ver = do
|
|||||||
|
|
||||||
liftIO
|
liftIO
|
||||||
$ hideError doesNotExistErrorType
|
$ hideError doesNotExistErrorType
|
||||||
$ deleteFile (baseDir </> [rel|share|])
|
$ removeFile (baseDir </> "share")
|
||||||
|
|
||||||
|
|
||||||
-- | Delete a cabal version. Will try to fix the @cabal@ symlink
|
-- | Delete a cabal version. Will try to fix the @cabal@ symlink
|
||||||
@ -1186,15 +1186,15 @@ rmCabalVer ver = do
|
|||||||
|
|
||||||
AppState {dirs = Dirs {..}} <- lift ask
|
AppState {dirs = Dirs {..}} <- lift ask
|
||||||
|
|
||||||
cabalFile <- lift $ parseRel ("cabal-" <> verToBS ver)
|
let cabalFile = "cabal-" <> T.unpack (prettyVer ver) <> exeExt
|
||||||
liftIO $ hideError doesNotExistErrorType $ deleteFile (binDir </> cabalFile)
|
liftIO $ hideError doesNotExistErrorType $ removeFile (binDir </> cabalFile)
|
||||||
|
|
||||||
when (Just ver == cSet) $ do
|
when (Just ver == cSet) $ do
|
||||||
cVers <- lift $ fmap rights getInstalledCabals
|
cVers <- lift $ fmap rights getInstalledCabals
|
||||||
case headMay . reverse . sort $ cVers of
|
case headMay . reverse . sort $ cVers of
|
||||||
Just latestver -> setCabal latestver
|
Just latestver -> setCabal latestver
|
||||||
Nothing -> liftIO $ hideError doesNotExistErrorType $ deleteFile
|
Nothing -> liftIO $ hideError doesNotExistErrorType $ removeFile
|
||||||
(binDir </> [rel|cabal|])
|
(binDir </> "cabal" <> exeExt)
|
||||||
|
|
||||||
|
|
||||||
-- | Delete a hls version. Will try to fix the hls symlinks
|
-- | Delete a hls version. Will try to fix the hls symlinks
|
||||||
@ -1210,14 +1210,15 @@ rmHLSVer ver = do
|
|||||||
AppState {dirs = Dirs {..}} <- lift ask
|
AppState {dirs = Dirs {..}} <- lift ask
|
||||||
|
|
||||||
bins <- lift $ hlsAllBinaries ver
|
bins <- lift $ hlsAllBinaries ver
|
||||||
forM_ bins $ \f -> liftIO $ deleteFile (binDir </> f)
|
forM_ bins $ \f -> liftIO $ removeFile (binDir </> f)
|
||||||
|
|
||||||
when (Just ver == isHlsSet) $ do
|
when (Just ver == isHlsSet) $ do
|
||||||
-- delete all set symlinks
|
-- delete all set symlinks
|
||||||
oldSyms <- lift hlsSymlinks
|
oldSyms <- lift hlsSymlinks
|
||||||
forM_ oldSyms $ \f -> do
|
forM_ oldSyms $ \f -> do
|
||||||
lift $ $(logDebug) [i|rm #{toFilePath (binDir </> f)}|]
|
let fullF = binDir </> f <> exeExt
|
||||||
liftIO $ deleteFile (binDir </> f)
|
lift $ $(logDebug) [i|rm #{fullF}|]
|
||||||
|
liftIO $ removeFile fullF
|
||||||
-- set latest hls
|
-- set latest hls
|
||||||
hlsVers <- lift $ fmap rights getInstalledHLSs
|
hlsVers <- lift $ fmap rights getInstalledHLSs
|
||||||
case headMay . reverse . sort $ hlsVers of
|
case headMay . reverse . sort $ hlsVers of
|
||||||
@ -1237,15 +1238,15 @@ rmStackVer ver = do
|
|||||||
|
|
||||||
AppState {dirs = Dirs {..}} <- lift ask
|
AppState {dirs = Dirs {..}} <- lift ask
|
||||||
|
|
||||||
stackFile <- lift $ parseRel ("stack-" <> verToBS ver)
|
let stackFile = "stack-" <> T.unpack (prettyVer ver) <> exeExt
|
||||||
liftIO $ hideError doesNotExistErrorType $ deleteFile (binDir </> stackFile)
|
liftIO $ hideError doesNotExistErrorType $ removeFile (binDir </> stackFile)
|
||||||
|
|
||||||
when (Just ver == sSet) $ do
|
when (Just ver == sSet) $ do
|
||||||
sVers <- lift $ fmap rights getInstalledStacks
|
sVers <- lift $ fmap rights getInstalledStacks
|
||||||
case headMay . reverse . sort $ sVers of
|
case headMay . reverse . sort $ sVers of
|
||||||
Just latestver -> setStack latestver
|
Just latestver -> setStack latestver
|
||||||
Nothing -> liftIO $ hideError doesNotExistErrorType $ deleteFile
|
Nothing -> liftIO $ hideError doesNotExistErrorType $ removeFile
|
||||||
(binDir </> [rel|stack|])
|
(binDir </> "stack" <> exeExt)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1290,10 +1291,10 @@ compileGHC :: ( MonadMask m
|
|||||||
)
|
)
|
||||||
=> GHCupDownloads
|
=> GHCupDownloads
|
||||||
-> Either GHCTargetVersion GitBranch -- ^ version to install
|
-> Either GHCTargetVersion GitBranch -- ^ version to install
|
||||||
-> Either Version (Path Abs) -- ^ version to bootstrap with
|
-> Either Version FilePath -- ^ version to bootstrap with
|
||||||
-> Maybe Int -- ^ jobs
|
-> Maybe Int -- ^ jobs
|
||||||
-> Maybe (Path Abs) -- ^ build config
|
-> Maybe FilePath -- ^ build config
|
||||||
-> Maybe (Path Abs) -- ^ patch directory
|
-> Maybe FilePath -- ^ patch directory
|
||||||
-> [Text] -- ^ additional args to ./configure
|
-> [Text] -- ^ additional args to ./configure
|
||||||
-> PlatformRequest
|
-> PlatformRequest
|
||||||
-> Excepts
|
-> Excepts
|
||||||
@ -1341,7 +1342,7 @@ compileGHC dls targetGhc bstrap jobs mbuildConfig patchdir aargs pfreq@PlatformR
|
|||||||
-- clone from git
|
-- clone from git
|
||||||
Right GitBranch{..} -> do
|
Right GitBranch{..} -> do
|
||||||
tmpUnpack <- lift mkGhcupTmpDir
|
tmpUnpack <- lift mkGhcupTmpDir
|
||||||
let git args = execLogged [s|git|] True ("--no-pager":args) [rel|git|] (Just tmpUnpack) Nothing
|
let git args = execLogged "git" ("--no-pager":args) (Just tmpUnpack) "git" Nothing
|
||||||
tver <- reThrowAll @_ @'[ProcessError] DownloadFailed $ do
|
tver <- reThrowAll @_ @'[ProcessError] DownloadFailed $ do
|
||||||
let rep = fromMaybe "https://gitlab.haskell.org/ghc/ghc.git" repo
|
let rep = fromMaybe "https://gitlab.haskell.org/ghc/ghc.git" repo
|
||||||
lift $ $(logInfo) [i|Fetching git repo #{rep} at ref #{ref} (this may take a while)|]
|
lift $ $(logInfo) [i|Fetching git repo #{rep} at ref #{ref} (this may take a while)|]
|
||||||
@ -1362,13 +1363,13 @@ compileGHC dls targetGhc bstrap jobs mbuildConfig patchdir aargs pfreq@PlatformR
|
|||||||
|
|
||||||
lEM $ git [ "checkout", "FETCH_HEAD" ]
|
lEM $ git [ "checkout", "FETCH_HEAD" ]
|
||||||
lEM $ git [ "submodule", "update", "--init", "--depth", "1" ]
|
lEM $ git [ "submodule", "update", "--init", "--depth", "1" ]
|
||||||
lEM $ execLogged "./boot" False [] [rel|ghc-bootstrap|] (Just tmpUnpack) Nothing
|
lEM $ execLogged "sh" ["./boot"] (Just tmpUnpack) "ghc-bootstrap" Nothing
|
||||||
lEM $ execLogged "./configure" False [] [rel|ghc-bootstrap|] (Just tmpUnpack) Nothing
|
lEM $ execLogged "sh" ["./configure"] (Just tmpUnpack) "ghc-bootstrap" Nothing
|
||||||
CapturedProcess {..} <- liftIO $ makeOut
|
CapturedProcess {..} <- liftIO $ makeOut
|
||||||
["show!", "--quiet", "VALUE=ProjectVersion" ] (Just tmpUnpack)
|
["show!", "--quiet", "VALUE=ProjectVersion" ] (Just tmpUnpack)
|
||||||
case _exitCode of
|
case _exitCode of
|
||||||
ExitSuccess -> throwEither . MP.parse ghcProjectVersion "" . decUTF8Safe $ _stdOut
|
ExitSuccess -> throwEither . MP.parse ghcProjectVersion "" . decUTF8Safe' $ _stdOut
|
||||||
ExitFailure c -> fail ("Could not figure out GHC project version. Exit code was: " <> show c <> ". Error was: " <> T.unpack (decUTF8Safe _stdErr))
|
ExitFailure c -> fail ("Could not figure out GHC project version. Exit code was: " <> show c <> ". Error was: " <> T.unpack (decUTF8Safe' _stdErr))
|
||||||
|
|
||||||
void $ liftIO $ darwinNotarization _rPlatform tmpUnpack
|
void $ liftIO $ darwinNotarization _rPlatform tmpUnpack
|
||||||
lift $ $(logInfo) [i|Git version #{ref} corresponds to GHC version #{prettyVer tver}|]
|
lift $ $(logInfo) [i|Git version #{ref} corresponds to GHC version #{prettyVer tver}|]
|
||||||
@ -1387,14 +1388,14 @@ compileGHC dls targetGhc bstrap jobs mbuildConfig patchdir aargs pfreq@PlatformR
|
|||||||
|
|
||||||
bghc <- case bstrap of
|
bghc <- case bstrap of
|
||||||
Right g -> pure $ Right g
|
Right g -> pure $ Right g
|
||||||
Left bver -> Left <$> parseRel ("ghc-" <> verToBS bver)
|
Left bver -> pure $ Left ("ghc-" <> (T.unpack . prettyVer $ bver))
|
||||||
|
|
||||||
(bindist, bmk) <- liftE $ runBuildAction
|
(bindist, bmk) <- liftE $ runBuildAction
|
||||||
tmpUnpack
|
tmpUnpack
|
||||||
Nothing
|
Nothing
|
||||||
(do
|
(do
|
||||||
b <- compileBindist bghc tver workdir
|
b <- compileBindist bghc tver workdir
|
||||||
bmk <- liftIO $ readFileStrict (build_mk workdir)
|
bmk <- liftIO $ B.readFile (build_mk workdir)
|
||||||
pure (b, bmk)
|
pure (b, bmk)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1407,7 +1408,7 @@ compileGHC dls targetGhc bstrap jobs mbuildConfig patchdir aargs pfreq@PlatformR
|
|||||||
(tver ^. tvVersion)
|
(tver ^. tvVersion)
|
||||||
pfreq
|
pfreq
|
||||||
|
|
||||||
liftIO $ writeFile (ghcdir </> ghcUpSrcBuiltFile) (Just newFilePerms) bmk
|
liftIO $ B.writeFile (ghcdir </> ghcUpSrcBuiltFile) bmk
|
||||||
|
|
||||||
reThrowAll GHCupSetError $ postGHCInstall tver
|
reThrowAll GHCupSetError $ postGHCInstall tver
|
||||||
|
|
||||||
@ -1439,13 +1440,13 @@ HADDOCK_DOCS = YES|]
|
|||||||
, MonadIO m
|
, MonadIO m
|
||||||
, MonadFail m
|
, MonadFail m
|
||||||
)
|
)
|
||||||
=> Either (Path Rel) (Path Abs)
|
=> Either FilePath FilePath
|
||||||
-> GHCTargetVersion
|
-> GHCTargetVersion
|
||||||
-> Path Abs
|
-> FilePath
|
||||||
-> Excepts
|
-> Excepts
|
||||||
'[FileDoesNotExistError, InvalidBuildConfig, PatchFailed, ProcessError, NotFoundInPATH, CopyError]
|
'[FileDoesNotExistError, InvalidBuildConfig, PatchFailed, ProcessError, NotFoundInPATH, CopyError]
|
||||||
m
|
m
|
||||||
(Path Abs) -- ^ output path of bindist
|
FilePath -- ^ output path of bindist
|
||||||
compileBindist bghc tver workdir = do
|
compileBindist bghc tver workdir = do
|
||||||
lift $ $(logInfo) [i|configuring build|]
|
lift $ $(logInfo) [i|configuring build|]
|
||||||
liftE checkBuildConfig
|
liftE checkBuildConfig
|
||||||
@ -1460,41 +1461,39 @@ HADDOCK_DOCS = YES|]
|
|||||||
bghcPath <- case bghc of
|
bghcPath <- case bghc of
|
||||||
Right ghc' -> pure ghc'
|
Right ghc' -> pure ghc'
|
||||||
Left bver -> do
|
Left bver -> do
|
||||||
spaths <- catMaybes . fmap parseAbs <$> liftIO getSearchPath
|
spaths <- liftIO getSearchPath
|
||||||
liftIO (searchPath spaths bver) !? NotFoundInPATH bver
|
liftIO (searchPath spaths bver) !? NotFoundInPATH bver
|
||||||
lEM $ execLogged
|
lEM $ execLogged
|
||||||
"./configure"
|
"sh"
|
||||||
False
|
("./configure" : maybe mempty
|
||||||
( maybe mempty
|
(\x -> ["--target=" <> T.unpack x])
|
||||||
(\x -> ["--target=" <> E.encodeUtf8 x])
|
|
||||||
(_tvTarget tver)
|
(_tvTarget tver)
|
||||||
++ fmap E.encodeUtf8 aargs
|
++ fmap T.unpack aargs
|
||||||
)
|
)
|
||||||
[rel|ghc-conf|]
|
|
||||||
(Just workdir)
|
(Just workdir)
|
||||||
(Just (("GHC", toFilePath bghcPath) : cEnv))
|
"ghc-conf"
|
||||||
|
(Just (("GHC", bghcPath) : cEnv))
|
||||||
| otherwise -> do
|
| otherwise -> do
|
||||||
lEM $ execLogged
|
lEM $ execLogged
|
||||||
"./configure"
|
"sh"
|
||||||
False
|
( [ "./configure", "--with-ghc=" <> either id id bghc
|
||||||
( [ "--with-ghc=" <> either toFilePath toFilePath bghc
|
|
||||||
]
|
]
|
||||||
++ maybe mempty
|
++ maybe mempty
|
||||||
(\x -> ["--target=" <> E.encodeUtf8 x])
|
(\x -> ["--target=" <> T.unpack x])
|
||||||
(_tvTarget tver)
|
(_tvTarget tver)
|
||||||
++ fmap E.encodeUtf8 aargs
|
++ fmap T.unpack aargs
|
||||||
)
|
)
|
||||||
[rel|ghc-conf|]
|
|
||||||
(Just workdir)
|
(Just workdir)
|
||||||
|
"ghc-conf"
|
||||||
(Just cEnv)
|
(Just cEnv)
|
||||||
|
|
||||||
case mbuildConfig of
|
case mbuildConfig of
|
||||||
Just bc -> liftIOException
|
Just bc -> liftIOException
|
||||||
doesNotExistErrorType
|
doesNotExistErrorType
|
||||||
(FileDoesNotExistError $ toFilePath bc)
|
(FileDoesNotExistError bc)
|
||||||
(liftIO $ copyFile bc (build_mk workdir) Overwrite)
|
(liftIO $ copyFile bc (build_mk workdir))
|
||||||
Nothing ->
|
Nothing ->
|
||||||
liftIO $ writeFile (build_mk workdir) (Just newFilePerms) defaultConf
|
liftIO $ B.writeFile (build_mk workdir) defaultConf
|
||||||
|
|
||||||
lift $ $(logInfo) [i|Building (this may take a while)...|]
|
lift $ $(logInfo) [i|Building (this may take a while)...|]
|
||||||
lEM $ make (maybe [] (\j -> ["-j" <> fS (show j)]) jobs) (Just workdir)
|
lEM $ make (maybe [] (\j -> ["-j" <> fS (show j)]) jobs) (Just workdir)
|
||||||
@ -1507,7 +1506,7 @@ HADDOCK_DOCS = YES|]
|
|||||||
execBlank
|
execBlank
|
||||||
([s|^ghc-.*\.tar\..*$|] :: ByteString)
|
([s|^ghc-.*\.tar\..*$|] :: ByteString)
|
||||||
)
|
)
|
||||||
c <- liftIO $ readFile (workdir </> tar)
|
c <- liftIO $ BL.readFile (workdir </> tar)
|
||||||
cDigest <-
|
cDigest <-
|
||||||
fmap (T.take 8)
|
fmap (T.take 8)
|
||||||
. lift
|
. lift
|
||||||
@ -1517,17 +1516,14 @@ HADDOCK_DOCS = YES|]
|
|||||||
. SHA256.hashlazy
|
. SHA256.hashlazy
|
||||||
$ c
|
$ c
|
||||||
cTime <- liftIO getCurrentTime
|
cTime <- liftIO getCurrentTime
|
||||||
tarName <-
|
let tarName = [i|ghc-#{tVerToText tver}-#{pfReqToString pfreq}-#{iso8601Show cTime}-#{cDigest}.tar#{takeExtension tar}|]
|
||||||
parseRel
|
|
||||||
[i|ghc-#{tVerToText tver}-#{pfReqToString pfreq}-#{iso8601Show cTime}-#{cDigest}.tar#{takeExtension (toFilePath tar)}|]
|
|
||||||
let tarPath = cacheDir </> tarName
|
let tarPath = cacheDir </> tarName
|
||||||
handleIO (throwE . CopyError . show) $ liftIO $ copyFile (workdir </> tar)
|
handleIO (throwE . CopyError . show) $ liftIO $ copyFile (workdir </> tar)
|
||||||
tarPath
|
tarPath
|
||||||
Strict
|
|
||||||
lift $ $(logInfo) [i|Copied bindist to #{tarPath}|]
|
lift $ $(logInfo) [i|Copied bindist to #{tarPath}|]
|
||||||
pure tarPath
|
pure tarPath
|
||||||
|
|
||||||
build_mk workdir = workdir </> [rel|mk/build.mk|]
|
build_mk workdir = workdir </> "mk" </> "build.mk"
|
||||||
|
|
||||||
checkBuildConfig :: (MonadCatch m, MonadIO m)
|
checkBuildConfig :: (MonadCatch m, MonadIO m)
|
||||||
=> Excepts
|
=> Excepts
|
||||||
@ -1537,10 +1533,10 @@ HADDOCK_DOCS = YES|]
|
|||||||
checkBuildConfig = do
|
checkBuildConfig = do
|
||||||
c <- case mbuildConfig of
|
c <- case mbuildConfig of
|
||||||
Just bc -> do
|
Just bc -> do
|
||||||
BL.toStrict <$> liftIOException
|
liftIOException
|
||||||
doesNotExistErrorType
|
doesNotExistErrorType
|
||||||
(FileDoesNotExistError $ toFilePath bc)
|
(FileDoesNotExistError bc)
|
||||||
(liftIO $ readFile bc)
|
(liftIO $ B.readFile bc)
|
||||||
Nothing -> pure defaultConf
|
Nothing -> pure defaultConf
|
||||||
let lines' = fmap T.strip . T.lines $ decUTF8Safe c
|
let lines' = fmap T.strip . T.lines $ decUTF8Safe c
|
||||||
|
|
||||||
@ -1572,7 +1568,7 @@ upgradeGHCup :: ( MonadMask m
|
|||||||
, MonadUnliftIO m
|
, MonadUnliftIO m
|
||||||
)
|
)
|
||||||
=> GHCupDownloads
|
=> GHCupDownloads
|
||||||
-> Maybe (Path Abs) -- ^ full file destination to write ghcup into
|
-> Maybe FilePath -- ^ full file destination to write ghcup into
|
||||||
-> Bool -- ^ whether to force update regardless
|
-> Bool -- ^ whether to force update regardless
|
||||||
-- of currently installed version
|
-- of currently installed version
|
||||||
-> PlatformRequest
|
-> PlatformRequest
|
||||||
@ -1592,25 +1588,24 @@ upgradeGHCup dls mtarget force pfreq = do
|
|||||||
when (not force && (latestVer <= pvpToVersion ghcUpVer)) $ throwE NoUpdate
|
when (not force && (latestVer <= pvpToVersion ghcUpVer)) $ throwE NoUpdate
|
||||||
dli <- lE $ getDownloadInfo GHCup latestVer pfreq dls
|
dli <- lE $ getDownloadInfo GHCup latestVer pfreq dls
|
||||||
tmp <- lift withGHCupTmpDir
|
tmp <- lift withGHCupTmpDir
|
||||||
let fn = [rel|ghcup|]
|
let fn = "ghcup" <> exeExt
|
||||||
p <- liftE $ download dli tmp (Just fn)
|
p <- liftE $ download dli tmp (Just fn)
|
||||||
let destDir = dirname destFile
|
let destDir = takeDirectory destFile
|
||||||
destFile = fromMaybe (binDir </> fn) mtarget
|
destFile = fromMaybe (binDir </> fn) mtarget
|
||||||
lift $ $(logDebug) [i|mkdir -p #{toFilePath destDir}|]
|
lift $ $(logDebug) [i|mkdir -p #{destDir}|]
|
||||||
liftIO $ createDirRecursive' destDir
|
liftIO $ createDirRecursive' destDir
|
||||||
lift $ $(logDebug) [i|rm -f #{toFilePath destFile}|]
|
lift $ $(logDebug) [i|rm -f #{destFile}|]
|
||||||
liftIO $ hideError NoSuchThing $ deleteFile destFile
|
liftIO $ hideError NoSuchThing $ removeFile destFile
|
||||||
lift $ $(logDebug) [i|cp #{toFilePath p} #{toFilePath destFile}|]
|
lift $ $(logDebug) [i|cp #{p} #{destFile}|]
|
||||||
handleIO (throwE . CopyError . show) $ liftIO $ copyFile p
|
handleIO (throwE . CopyError . show) $ liftIO $ copyFile p
|
||||||
destFile
|
destFile
|
||||||
Overwrite
|
|
||||||
lift $ chmod_755 destFile
|
lift $ chmod_755 destFile
|
||||||
|
|
||||||
liftIO (isInPath destFile) >>= \b -> unless b $
|
liftIO (isInPath destFile) >>= \b -> unless b $
|
||||||
lift $ $(logWarn) [i|"#{toFilePath (dirname destFile)}" is not in PATH! You have to add it in order to use ghcup.|]
|
lift $ $(logWarn) [i|"#{takeFileName destFile}" is not in PATH! You have to add it in order to use ghcup.|]
|
||||||
liftIO (isShadowed destFile) >>= \case
|
liftIO (isShadowed destFile) >>= \case
|
||||||
Nothing -> pure ()
|
Nothing -> pure ()
|
||||||
Just pa -> lift $ $(logWarn) [i|ghcup is shadowed by "#{toFilePath pa}". The upgrade will not be in effect, unless you remove "#{toFilePath pa}" or make sure "#{toFilePath destDir}" comes before "#{toFilePath (dirname pa)}" in PATH.|]
|
Just pa -> lift $ $(logWarn) [i|ghcup is shadowed by "#{pa}". The upgrade will not be in effect, unless you remove "#{pa}" or make sure "#{destDir}" comes before "#{takeFileName pa}" in PATH.|]
|
||||||
|
|
||||||
pure latestVer
|
pure latestVer
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ Copyright : (c) Julian Ospald, 2020
|
|||||||
License : LGPL-3.0
|
License : LGPL-3.0
|
||||||
Maintainer : hasufell@hasufell.de
|
Maintainer : hasufell@hasufell.de
|
||||||
Stability : experimental
|
Stability : experimental
|
||||||
Portability : POSIX
|
Portability : portable
|
||||||
|
|
||||||
Module for handling all download related functions.
|
Module for handling all download related functions.
|
||||||
|
|
||||||
@ -53,11 +53,11 @@ import Control.Monad.Trans.Resource
|
|||||||
hiding ( throwM )
|
hiding ( throwM )
|
||||||
import Data.Aeson
|
import Data.Aeson
|
||||||
import Data.Bifunctor
|
import Data.Bifunctor
|
||||||
import Data.ByteString ( ByteString )
|
|
||||||
#if defined(INTERNAL_DOWNLOADER)
|
#if defined(INTERNAL_DOWNLOADER)
|
||||||
|
import Data.ByteString ( ByteString )
|
||||||
import Data.CaseInsensitive ( CI )
|
import Data.CaseInsensitive ( CI )
|
||||||
#endif
|
#endif
|
||||||
import Data.List ( find )
|
import Data.List.Extra
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.String.Interpolate
|
import Data.String.Interpolate
|
||||||
import Data.Time.Clock
|
import Data.Time.Clock
|
||||||
@ -66,34 +66,29 @@ import Data.Time.Clock.POSIX
|
|||||||
import Data.Time.Format
|
import Data.Time.Format
|
||||||
#endif
|
#endif
|
||||||
import Data.Versions
|
import Data.Versions
|
||||||
import Data.Word8
|
|
||||||
import GHC.IO.Exception
|
import GHC.IO.Exception
|
||||||
import HPath
|
|
||||||
import HPath.IO as HIO hiding ( hideError )
|
|
||||||
import Haskus.Utils.Variant.Excepts
|
import Haskus.Utils.Variant.Excepts
|
||||||
import Optics
|
import Optics
|
||||||
import Prelude hiding ( abs
|
import Prelude hiding ( abs
|
||||||
, readFile
|
, readFile
|
||||||
, writeFile
|
, writeFile
|
||||||
)
|
)
|
||||||
|
import System.Directory
|
||||||
|
import System.Environment
|
||||||
|
import System.FilePath
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
import System.Posix.Env.ByteString ( getEnv )
|
|
||||||
import URI.ByteString
|
import URI.ByteString
|
||||||
|
|
||||||
import qualified Crypto.Hash.SHA256 as SHA256
|
import qualified Crypto.Hash.SHA256 as SHA256
|
||||||
import qualified Data.ByteString as BS
|
|
||||||
import qualified Data.ByteString.Base16 as B16
|
import qualified Data.ByteString.Base16 as B16
|
||||||
import qualified Data.ByteString.Lazy as L
|
import qualified Data.ByteString.Lazy as L
|
||||||
import qualified Data.Map.Strict as M
|
import qualified Data.Map.Strict as M
|
||||||
#if defined(INTERNAL_DOWNLOADER)
|
#if defined(INTERNAL_DOWNLOADER)
|
||||||
import qualified Data.CaseInsensitive as CI
|
import qualified Data.CaseInsensitive as CI
|
||||||
import qualified Data.Text as T
|
|
||||||
#endif
|
#endif
|
||||||
|
import qualified Data.Text as T
|
||||||
import qualified Data.Text.Encoding as E
|
import qualified Data.Text.Encoding as E
|
||||||
import qualified Data.Yaml as Y
|
import qualified Data.Yaml as Y
|
||||||
import qualified System.Posix.Files.ByteString as PF
|
|
||||||
import qualified System.Posix.RawFilePath.Directory
|
|
||||||
as RD
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -158,12 +153,12 @@ readFromCache = do
|
|||||||
lift $ $(logWarn)
|
lift $ $(logWarn)
|
||||||
[i|Could not get download info, trying cached version (this may not be recent!)|]
|
[i|Could not get download info, trying cached version (this may not be recent!)|]
|
||||||
let path = view pathL' ghcupURL
|
let path = view pathL' ghcupURL
|
||||||
yaml_file <- (cacheDir </>) <$> urlBaseName path
|
let yaml_file = cacheDir </> (T.unpack . decUTF8Safe . urlBaseName $ path)
|
||||||
bs <-
|
bs <-
|
||||||
handleIO' NoSuchThing
|
handleIO' NoSuchThing
|
||||||
(\_ -> throwE $ FileDoesNotExistError (toFilePath yaml_file))
|
(\_ -> throwE $ FileDoesNotExistError yaml_file)
|
||||||
$ liftIO
|
$ liftIO
|
||||||
$ readFile yaml_file
|
$ L.readFile yaml_file
|
||||||
lE' JSONDecodeError $ first show $ Y.decodeEither' (L.toStrict bs)
|
lE' JSONDecodeError $ first show $ Y.decodeEither' (L.toStrict bs)
|
||||||
|
|
||||||
|
|
||||||
@ -207,29 +202,27 @@ getBase =
|
|||||||
smartDl uri' = do
|
smartDl uri' = do
|
||||||
AppState {dirs = Dirs {..}} <- lift ask
|
AppState {dirs = Dirs {..}} <- lift ask
|
||||||
let path = view pathL' uri'
|
let path = view pathL' uri'
|
||||||
json_file <- (cacheDir </>) <$> urlBaseName path
|
let json_file = cacheDir </> (T.unpack . decUTF8Safe . urlBaseName $ path)
|
||||||
e <- liftIO $ doesFileExist json_file
|
e <- liftIO $ doesFileExist json_file
|
||||||
if e
|
if e
|
||||||
then do
|
then do
|
||||||
accessTime <-
|
accessTime <- liftIO $ getAccessTime json_file
|
||||||
PF.accessTimeHiRes
|
currentTime <- liftIO getCurrentTime
|
||||||
<$> liftIO (PF.getFileStatus (toFilePath json_file))
|
|
||||||
currentTime <- liftIO getPOSIXTime
|
|
||||||
|
|
||||||
-- access time won't work on most linuxes, but we can try regardless
|
-- access time won't work on most linuxes, but we can try regardless
|
||||||
if (currentTime - accessTime) > 300
|
if (utcTimeToPOSIXSeconds currentTime - utcTimeToPOSIXSeconds accessTime) > 300
|
||||||
then do -- no access in last 5 minutes, re-check upstream mod time
|
then do -- no access in last 5 minutes, re-check upstream mod time
|
||||||
getModTime >>= \case
|
getModTime >>= \case
|
||||||
Just modTime -> do
|
Just modTime -> do
|
||||||
fileMod <- liftIO $ getModificationTime json_file
|
fileMod <- liftIO $ getModificationTime json_file
|
||||||
if modTime > fileMod
|
if modTime > fileMod
|
||||||
then dlWithMod modTime json_file
|
then dlWithMod modTime json_file
|
||||||
else liftIO $ readFile json_file
|
else liftIO $ L.readFile json_file
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
lift $ $(logDebug) [i|Unable to get/parse Last-Modified header|]
|
lift $ $(logDebug) [i|Unable to get/parse Last-Modified header|]
|
||||||
dlWithoutMod json_file
|
dlWithoutMod json_file
|
||||||
else -- access in less than 5 minutes, re-use file
|
else -- access in less than 5 minutes, re-use file
|
||||||
liftIO $ readFile json_file
|
liftIO $ L.readFile json_file
|
||||||
else do
|
else do
|
||||||
liftIO $ createDirRecursive' cacheDir
|
liftIO $ createDirRecursive' cacheDir
|
||||||
getModTime >>= \case
|
getModTime >>= \case
|
||||||
@ -247,9 +240,9 @@ getBase =
|
|||||||
pure bs
|
pure bs
|
||||||
dlWithoutMod json_file = do
|
dlWithoutMod json_file = do
|
||||||
bs <- liftE $ downloadBS uri'
|
bs <- liftE $ downloadBS uri'
|
||||||
liftIO $ hideError doesNotExistErrorType $ deleteFile json_file
|
liftIO $ hideError doesNotExistErrorType $ removeFile json_file
|
||||||
liftIO $ writeFileL json_file (Just newFilePerms) bs
|
liftIO $ L.writeFile json_file bs
|
||||||
liftIO $ setModificationTime json_file (fromIntegral @Int 0)
|
liftIO $ setModificationTime json_file (posixSecondsToUTCTime (fromIntegral @Int 0))
|
||||||
pure bs
|
pure bs
|
||||||
|
|
||||||
|
|
||||||
@ -278,11 +271,10 @@ getBase =
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
writeFileWithModTime :: UTCTime -> Path Abs -> L.ByteString -> IO ()
|
writeFileWithModTime :: UTCTime -> FilePath -> L.ByteString -> IO ()
|
||||||
writeFileWithModTime utctime path content = do
|
writeFileWithModTime utctime path content = do
|
||||||
let mod_time = utcTimeToPOSIXSeconds utctime
|
L.writeFile path content
|
||||||
writeFileL path (Just newFilePerms) content
|
setModificationTime path utctime
|
||||||
setModificationTimeHiRes path mod_time
|
|
||||||
|
|
||||||
|
|
||||||
getDownloadInfo :: Tool
|
getDownloadInfo :: Tool
|
||||||
@ -334,9 +326,9 @@ download :: ( MonadMask m
|
|||||||
, MonadIO m
|
, MonadIO m
|
||||||
)
|
)
|
||||||
=> DownloadInfo
|
=> DownloadInfo
|
||||||
-> Path Abs -- ^ destination dir
|
-> FilePath -- ^ destination dir
|
||||||
-> Maybe (Path Rel) -- ^ optional filename
|
-> Maybe FilePath -- ^ optional filename
|
||||||
-> Excepts '[DigestError , DownloadFailed] m (Path Abs)
|
-> Excepts '[DigestError , DownloadFailed] m FilePath
|
||||||
download dli dest mfn
|
download dli dest mfn
|
||||||
| scheme == "https" = dl
|
| scheme == "https" = dl
|
||||||
| scheme == "http" = dl
|
| scheme == "http" = dl
|
||||||
@ -348,9 +340,9 @@ download dli dest mfn
|
|||||||
cp = do
|
cp = do
|
||||||
-- destination dir must exist
|
-- destination dir must exist
|
||||||
liftIO $ createDirRecursive' dest
|
liftIO $ createDirRecursive' dest
|
||||||
destFile <- getDestFile
|
let destFile = getDestFile
|
||||||
fromFile <- parseAbs path
|
let fromFile = T.unpack . decUTF8Safe $ path
|
||||||
liftIO $ copyFile fromFile destFile Strict
|
liftIO $ copyFile fromFile destFile
|
||||||
pure destFile
|
pure destFile
|
||||||
dl = do
|
dl = do
|
||||||
let uri' = decUTF8Safe (serializeURIRef' (view dlUri dli))
|
let uri' = decUTF8Safe (serializeURIRef' (view dlUri dli))
|
||||||
@ -358,25 +350,25 @@ download dli dest mfn
|
|||||||
|
|
||||||
-- destination dir must exist
|
-- destination dir must exist
|
||||||
liftIO $ createDirRecursive' dest
|
liftIO $ createDirRecursive' dest
|
||||||
destFile <- getDestFile
|
let destFile = getDestFile
|
||||||
|
|
||||||
-- download
|
-- download
|
||||||
flip onException
|
flip onException
|
||||||
(liftIO $ hideError doesNotExistErrorType $ deleteFile destFile)
|
(liftIO $ hideError doesNotExistErrorType $ removeFile destFile)
|
||||||
$ catchAllE @_ @'[ProcessError, DownloadFailed, UnsupportedScheme]
|
$ catchAllE @_ @'[ProcessError, DownloadFailed, UnsupportedScheme]
|
||||||
(\e ->
|
(\e ->
|
||||||
liftIO (hideError doesNotExistErrorType $ deleteFile destFile)
|
liftIO (hideError doesNotExistErrorType $ removeFile destFile)
|
||||||
>> (throwE . DownloadFailed $ e)
|
>> (throwE . DownloadFailed $ e)
|
||||||
) $ do
|
) $ do
|
||||||
lift getDownloader >>= \case
|
lift getDownloader >>= \case
|
||||||
Curl -> do
|
Curl -> do
|
||||||
o' <- liftIO getCurlOpts
|
o' <- liftIO getCurlOpts
|
||||||
liftE $ lEM @_ @'[ProcessError] $ liftIO $ exec "curl" True
|
liftE $ lEM @_ @'[ProcessError] $ liftIO $ exec "curl"
|
||||||
(o' ++ ["-fL", "-o", toFilePath destFile, serializeURIRef' $ view dlUri dli]) Nothing Nothing
|
(o' ++ ["-fL", "-o", destFile, (T.unpack . decUTF8Safe) $ serializeURIRef' $ view dlUri dli]) Nothing Nothing
|
||||||
Wget -> do
|
Wget -> do
|
||||||
o' <- liftIO getWgetOpts
|
o' <- liftIO getWgetOpts
|
||||||
liftE $ lEM @_ @'[ProcessError] $ liftIO $ exec "wget" True
|
liftE $ lEM @_ @'[ProcessError] $ liftIO $ exec "wget"
|
||||||
(o' ++ ["-O", toFilePath destFile , serializeURIRef' $ view dlUri dli]) Nothing Nothing
|
(o' ++ ["-O", destFile , (T.unpack . decUTF8Safe) $ serializeURIRef' $ view dlUri dli]) Nothing Nothing
|
||||||
#if defined(INTERNAL_DOWNLOADER)
|
#if defined(INTERNAL_DOWNLOADER)
|
||||||
Internal -> do
|
Internal -> do
|
||||||
(https, host, fullPath, port) <- liftE $ uriToQuadruple (view dlUri dli)
|
(https, host, fullPath, port) <- liftE $ uriToQuadruple (view dlUri dli)
|
||||||
@ -387,8 +379,8 @@ download dli dest mfn
|
|||||||
pure destFile
|
pure destFile
|
||||||
|
|
||||||
-- Manage to find a file we can write the body into.
|
-- Manage to find a file we can write the body into.
|
||||||
getDestFile :: MonadThrow m => m (Path Abs)
|
getDestFile :: FilePath
|
||||||
getDestFile = maybe (urlBaseName path <&> (dest </>)) (pure . (dest </>)) mfn
|
getDestFile = maybe (dest </> T.unpack (decUTF8Safe (urlBaseName path))) (dest </>) mfn
|
||||||
|
|
||||||
path = view (dlUri % pathL') dli
|
path = view (dlUri % pathL') dli
|
||||||
|
|
||||||
@ -404,14 +396,14 @@ downloadCached :: ( MonadMask m
|
|||||||
, MonadReader AppState m
|
, MonadReader AppState m
|
||||||
)
|
)
|
||||||
=> DownloadInfo
|
=> DownloadInfo
|
||||||
-> Maybe (Path Rel) -- ^ optional filename
|
-> Maybe FilePath -- ^ optional filename
|
||||||
-> Excepts '[DigestError , DownloadFailed] m (Path Abs)
|
-> Excepts '[DigestError , DownloadFailed] m FilePath
|
||||||
downloadCached dli mfn = do
|
downloadCached dli mfn = do
|
||||||
cache <- lift getCache
|
cache <- lift getCache
|
||||||
case cache of
|
case cache of
|
||||||
True -> do
|
True -> do
|
||||||
AppState {dirs = Dirs {..}} <- lift ask
|
AppState {dirs = Dirs {..}} <- lift ask
|
||||||
fn <- maybe (urlBaseName $ view (dlUri % pathL') dli) pure mfn
|
let fn = fromMaybe ((T.unpack . decUTF8Safe) $ urlBaseName $ view (dlUri % pathL') dli) mfn
|
||||||
let cachfile = cacheDir </> fn
|
let cachfile = cacheDir </> fn
|
||||||
fileExists <- liftIO $ doesFileExist cachfile
|
fileExists <- liftIO $ doesFileExist cachfile
|
||||||
if
|
if
|
||||||
@ -453,8 +445,8 @@ downloadBS uri'
|
|||||||
| scheme == "http"
|
| scheme == "http"
|
||||||
= dl False
|
= dl False
|
||||||
| scheme == "file"
|
| scheme == "file"
|
||||||
= liftIOException doesNotExistErrorType (FileDoesNotExistError path)
|
= liftIOException doesNotExistErrorType (FileDoesNotExistError $ T.unpack $ decUTF8Safe path)
|
||||||
(liftIO $ RD.readFile path)
|
(liftIO $ L.readFile (T.unpack $ decUTF8Safe path))
|
||||||
| otherwise
|
| otherwise
|
||||||
= throwE UnsupportedScheme
|
= throwE UnsupportedScheme
|
||||||
|
|
||||||
@ -470,20 +462,20 @@ downloadBS uri'
|
|||||||
lift getDownloader >>= \case
|
lift getDownloader >>= \case
|
||||||
Curl -> do
|
Curl -> do
|
||||||
o' <- liftIO getCurlOpts
|
o' <- liftIO getCurlOpts
|
||||||
let exe = [rel|curl|]
|
let exe = "curl"
|
||||||
args = o' ++ ["-sSfL", serializeURIRef' uri']
|
args = o' ++ ["-sSfL", T.unpack $ decUTF8Safe $ serializeURIRef' uri']
|
||||||
liftIO (executeOut exe args Nothing) >>= \case
|
liftIO (executeOut exe args Nothing) >>= \case
|
||||||
CapturedProcess ExitSuccess stdout _ -> do
|
CapturedProcess ExitSuccess stdout _ -> do
|
||||||
pure $ L.fromStrict stdout
|
pure stdout
|
||||||
CapturedProcess (ExitFailure i') _ _ -> throwE $ NonZeroExit i' (toFilePath exe) args
|
CapturedProcess (ExitFailure i') _ _ -> throwE $ NonZeroExit i' exe args
|
||||||
Wget -> do
|
Wget -> do
|
||||||
o' <- liftIO getWgetOpts
|
o' <- liftIO getWgetOpts
|
||||||
let exe = [rel|wget|]
|
let exe = "wget"
|
||||||
args = o' ++ ["-qO-", serializeURIRef' uri']
|
args = o' ++ ["-qO-", T.unpack $ decUTF8Safe $ serializeURIRef' uri']
|
||||||
liftIO (executeOut exe args Nothing) >>= \case
|
liftIO (executeOut exe args Nothing) >>= \case
|
||||||
CapturedProcess ExitSuccess stdout _ -> do
|
CapturedProcess ExitSuccess stdout _ -> do
|
||||||
pure $ L.fromStrict stdout
|
pure stdout
|
||||||
CapturedProcess (ExitFailure i') _ _ -> throwE $ NonZeroExit i' (toFilePath exe) args
|
CapturedProcess (ExitFailure i') _ _ -> throwE $ NonZeroExit i' exe args
|
||||||
#if defined(INTERNAL_DOWNLOADER)
|
#if defined(INTERNAL_DOWNLOADER)
|
||||||
Internal -> do
|
Internal -> do
|
||||||
(_, host', fullPath', port') <- liftE $ uriToQuadruple uri'
|
(_, host', fullPath', port') <- liftE $ uriToQuadruple uri'
|
||||||
@ -493,31 +485,31 @@ downloadBS uri'
|
|||||||
|
|
||||||
checkDigest :: (MonadIO m, MonadThrow m, MonadLogger m, MonadReader AppState m)
|
checkDigest :: (MonadIO m, MonadThrow m, MonadLogger m, MonadReader AppState m)
|
||||||
=> DownloadInfo
|
=> DownloadInfo
|
||||||
-> Path Abs
|
-> FilePath
|
||||||
-> Excepts '[DigestError] m ()
|
-> Excepts '[DigestError] m ()
|
||||||
checkDigest dli file = do
|
checkDigest dli file = do
|
||||||
verify <- lift ask <&> (not . noVerify . settings)
|
verify <- lift ask <&> (not . noVerify . settings)
|
||||||
when verify $ do
|
when verify $ do
|
||||||
p' <- toFilePath <$> basename file
|
let p' = takeFileName file
|
||||||
lift $ $(logInfo) [i|verifying digest of: #{p'}|]
|
lift $ $(logInfo) [i|verifying digest of: #{p'}|]
|
||||||
c <- liftIO $ readFile file
|
c <- liftIO $ L.readFile file
|
||||||
cDigest <- throwEither . E.decodeUtf8' . B16.encode . SHA256.hashlazy $ c
|
cDigest <- throwEither . E.decodeUtf8' . B16.encode . SHA256.hashlazy $ c
|
||||||
let eDigest = view dlHash dli
|
let eDigest = view dlHash dli
|
||||||
when ((cDigest /= eDigest) && verify) $ throwE (DigestError cDigest eDigest)
|
when ((cDigest /= eDigest) && verify) $ throwE (DigestError cDigest eDigest)
|
||||||
|
|
||||||
|
|
||||||
-- | Get additional curl args from env. This is an undocumented option.
|
-- | Get additional curl args from env. This is an undocumented option.
|
||||||
getCurlOpts :: IO [ByteString]
|
getCurlOpts :: IO [String]
|
||||||
getCurlOpts =
|
getCurlOpts =
|
||||||
getEnv "GHCUP_CURL_OPTS" >>= \case
|
lookupEnv "GHCUP_CURL_OPTS" >>= \case
|
||||||
Just r -> pure $ BS.split _space r
|
Just r -> pure $ splitOn " " r
|
||||||
Nothing -> pure []
|
Nothing -> pure []
|
||||||
|
|
||||||
|
|
||||||
-- | Get additional wget args from env. This is an undocumented option.
|
-- | Get additional wget args from env. This is an undocumented option.
|
||||||
getWgetOpts :: IO [ByteString]
|
getWgetOpts :: IO [String]
|
||||||
getWgetOpts =
|
getWgetOpts =
|
||||||
getEnv "GHCUP_WGET_OPTS" >>= \case
|
lookupEnv "GHCUP_WGET_OPTS" >>= \case
|
||||||
Just r -> pure $ BS.split _space r
|
Just r -> pure $ splitOn " " r
|
||||||
Nothing -> pure []
|
Nothing -> pure []
|
||||||
|
|
||||||
|
@ -24,8 +24,6 @@ import Data.CaseInsensitive ( CI )
|
|||||||
import Data.IORef
|
import Data.IORef
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.Text.Read
|
import Data.Text.Read
|
||||||
import HPath
|
|
||||||
import HPath.IO as HIO
|
|
||||||
import Haskus.Utils.Variant.Excepts
|
import Haskus.Utils.Variant.Excepts
|
||||||
import Network.Http.Client hiding ( URL )
|
import Network.Http.Client hiding ( URL )
|
||||||
import Optics
|
import Optics
|
||||||
@ -33,11 +31,8 @@ import Prelude hiding ( abs
|
|||||||
, readFile
|
, readFile
|
||||||
, writeFile
|
, writeFile
|
||||||
)
|
)
|
||||||
import "unix" System.Posix.IO.ByteString
|
|
||||||
hiding ( fdWrite )
|
|
||||||
import "unix-bytestring" System.Posix.IO.ByteString
|
|
||||||
( fdWrite )
|
|
||||||
import System.ProgressBar
|
import System.ProgressBar
|
||||||
|
import System.IO
|
||||||
import URI.ByteString
|
import URI.ByteString
|
||||||
|
|
||||||
import qualified Data.ByteString as BS
|
import qualified Data.ByteString as BS
|
||||||
@ -81,12 +76,12 @@ downloadToFile :: (MonadMask m, MonadIO m)
|
|||||||
-> ByteString -- ^ host (e.g. "www.example.com")
|
-> ByteString -- ^ host (e.g. "www.example.com")
|
||||||
-> ByteString -- ^ path (e.g. "/my/file") including query
|
-> ByteString -- ^ path (e.g. "/my/file") including query
|
||||||
-> Maybe Int -- ^ optional port (e.g. 3000)
|
-> Maybe Int -- ^ optional port (e.g. 3000)
|
||||||
-> Path Abs -- ^ destination file to create and write to
|
-> FilePath -- ^ destination file to create and write to
|
||||||
-> Excepts '[DownloadFailed] m ()
|
-> Excepts '[DownloadFailed] m ()
|
||||||
downloadToFile https host fullPath port destFile = do
|
downloadToFile https host fullPath port destFile = do
|
||||||
fd <- liftIO $ createRegularFileFd newFilePerms destFile
|
fd <- liftIO $ openFile destFile WriteMode
|
||||||
let stepper = fdWrite fd
|
let stepper = BS.hPut fd
|
||||||
flip finally (liftIO $ closeFd fd)
|
flip finally (liftIO $ hClose fd)
|
||||||
$ reThrowAll DownloadFailed $ downloadInternal True https host fullPath port stepper
|
$ reThrowAll DownloadFailed $ downloadInternal True https host fullPath port stepper
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,12 +15,11 @@ Copyright : (c) Julian Ospald, 2020
|
|||||||
License : LGPL-3.0
|
License : LGPL-3.0
|
||||||
Maintainer : hasufell@hasufell.de
|
Maintainer : hasufell@hasufell.de
|
||||||
Stability : experimental
|
Stability : experimental
|
||||||
Portability : POSIX
|
Portability : portable
|
||||||
-}
|
-}
|
||||||
module GHCup.Errors where
|
module GHCup.Errors where
|
||||||
|
|
||||||
import GHCup.Types
|
import GHCup.Types
|
||||||
import GHCup.Utils.Prelude
|
|
||||||
|
|
||||||
#if !defined(TAR)
|
#if !defined(TAR)
|
||||||
import Codec.Archive
|
import Codec.Archive
|
||||||
@ -28,11 +27,9 @@ import Codec.Archive
|
|||||||
import qualified Codec.Archive.Tar as Tar
|
import qualified Codec.Archive.Tar as Tar
|
||||||
#endif
|
#endif
|
||||||
import Control.Exception.Safe
|
import Control.Exception.Safe
|
||||||
import Data.ByteString ( ByteString )
|
|
||||||
import Data.String.Interpolate
|
import Data.String.Interpolate
|
||||||
import Data.Text ( Text )
|
import Data.Text ( Text )
|
||||||
import Data.Versions
|
import Data.Versions
|
||||||
import HPath
|
|
||||||
import Haskus.Utils.Variant
|
import Haskus.Utils.Variant
|
||||||
import Text.PrettyPrint
|
import Text.PrettyPrint
|
||||||
import Text.PrettyPrint.HughesPJClass
|
import Text.PrettyPrint.HughesPJClass
|
||||||
@ -86,12 +83,12 @@ instance Pretty DistroNotFound where
|
|||||||
text "Unable to figure out the distribution of the host."
|
text "Unable to figure out the distribution of the host."
|
||||||
|
|
||||||
-- | The archive format is unknown. We don't know how to extract it.
|
-- | The archive format is unknown. We don't know how to extract it.
|
||||||
data UnknownArchive = UnknownArchive ByteString
|
data UnknownArchive = UnknownArchive FilePath
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
instance Pretty UnknownArchive where
|
instance Pretty UnknownArchive where
|
||||||
pPrint (UnknownArchive file) =
|
pPrint (UnknownArchive file) =
|
||||||
text [i|The archive format is unknown. We don't know how to extract the file "#{decUTF8Safe file}"|]
|
text [i|The archive format is unknown. We don't know how to extract the file "#{file}"|]
|
||||||
|
|
||||||
-- | The scheme is not supported (such as ftp).
|
-- | The scheme is not supported (such as ftp).
|
||||||
data UnsupportedScheme = UnsupportedScheme
|
data UnsupportedScheme = UnsupportedScheme
|
||||||
@ -143,12 +140,12 @@ instance Pretty NotInstalled where
|
|||||||
text [i|The version "#{prettyShow ver}" of the tool "#{tool}" is not installed.|]
|
text [i|The version "#{prettyShow ver}" of the tool "#{tool}" is not installed.|]
|
||||||
|
|
||||||
-- | An executable was expected to be in PATH, but was not found.
|
-- | An executable was expected to be in PATH, but was not found.
|
||||||
data NotFoundInPATH = NotFoundInPATH (Path Rel)
|
data NotFoundInPATH = NotFoundInPATH FilePath
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
instance Pretty NotFoundInPATH where
|
instance Pretty NotFoundInPATH where
|
||||||
pPrint (NotFoundInPATH exe) =
|
pPrint (NotFoundInPATH exe) =
|
||||||
text [i|The exe "#{decUTF8Safe . toFilePath $ exe}" was not found in PATH.|]
|
text [i|The exe "#{exe}" was not found in PATH.|]
|
||||||
|
|
||||||
-- | JSON decoding failed.
|
-- | JSON decoding failed.
|
||||||
data JSONError = JSONDecodeError String
|
data JSONError = JSONDecodeError String
|
||||||
@ -160,12 +157,12 @@ instance Pretty JSONError where
|
|||||||
|
|
||||||
-- | A file that is supposed to exist does not exist
|
-- | A file that is supposed to exist does not exist
|
||||||
-- (e.g. when we use file scheme to "download" something).
|
-- (e.g. when we use file scheme to "download" something).
|
||||||
data FileDoesNotExistError = FileDoesNotExistError ByteString
|
data FileDoesNotExistError = FileDoesNotExistError FilePath
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
instance Pretty FileDoesNotExistError where
|
instance Pretty FileDoesNotExistError where
|
||||||
pPrint (FileDoesNotExistError file) =
|
pPrint (FileDoesNotExistError file) =
|
||||||
text [i|File "#{decUTF8Safe file}" does not exist.|]
|
text [i|File "#{file}" does not exist.|]
|
||||||
|
|
||||||
data TarDirDoesNotExist = TarDirDoesNotExist TarDir
|
data TarDirDoesNotExist = TarDirDoesNotExist TarDir
|
||||||
deriving Show
|
deriving Show
|
||||||
@ -252,11 +249,11 @@ deriving instance Show DownloadFailed
|
|||||||
|
|
||||||
|
|
||||||
-- | A build failed.
|
-- | A build failed.
|
||||||
data BuildFailed = forall es . Show (V es) => BuildFailed (Path Abs) (V es)
|
data BuildFailed = forall es . Show (V es) => BuildFailed FilePath (V es)
|
||||||
|
|
||||||
instance Pretty BuildFailed where
|
instance Pretty BuildFailed where
|
||||||
pPrint (BuildFailed path reason) =
|
pPrint (BuildFailed path reason) =
|
||||||
text [i|BuildFailed failed in dir "#{decUTF8Safe . toFilePath $ path}": #{reason}|]
|
text [i|BuildFailed failed in dir "#{path}": #{reason}|]
|
||||||
|
|
||||||
deriving instance Show BuildFailed
|
deriving instance Show BuildFailed
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ Copyright : (c) Julian Ospald, 2020
|
|||||||
License : LGPL-3.0
|
License : LGPL-3.0
|
||||||
Maintainer : hasufell@hasufell.de
|
Maintainer : hasufell@hasufell.de
|
||||||
Stability : experimental
|
Stability : experimental
|
||||||
Portability : POSIX
|
Portability : portable
|
||||||
-}
|
-}
|
||||||
module GHCup.Platform where
|
module GHCup.Platform where
|
||||||
|
|
||||||
@ -36,18 +36,20 @@ import Data.Maybe
|
|||||||
import Data.String.Interpolate
|
import Data.String.Interpolate
|
||||||
import Data.Text ( Text )
|
import Data.Text ( Text )
|
||||||
import Data.Versions
|
import Data.Versions
|
||||||
import HPath
|
|
||||||
import HPath.IO
|
|
||||||
import Haskus.Utils.Variant.Excepts
|
import Haskus.Utils.Variant.Excepts
|
||||||
import Prelude hiding ( abs
|
import Prelude hiding ( abs
|
||||||
, readFile
|
, readFile
|
||||||
, writeFile
|
, writeFile
|
||||||
)
|
)
|
||||||
import System.Info
|
import System.Info
|
||||||
|
import System.Directory
|
||||||
import System.OsRelease
|
import System.OsRelease
|
||||||
import Text.Regex.Posix
|
import Text.Regex.Posix
|
||||||
|
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
|
import qualified Data.Text.IO as T
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--------------------------
|
--------------------------
|
||||||
--[ Platform detection ]--
|
--[ Platform detection ]--
|
||||||
@ -96,22 +98,23 @@ getPlatform = do
|
|||||||
. versioning
|
. versioning
|
||||||
-- TODO: maybe do this somewhere else
|
-- TODO: maybe do this somewhere else
|
||||||
. getMajorVersion
|
. getMajorVersion
|
||||||
. decUTF8Safe
|
. decUTF8Safe'
|
||||||
<$> getDarwinVersion
|
<$> getDarwinVersion
|
||||||
pure $ PlatformResult { _platform = Darwin, _distroVersion = ver }
|
pure $ PlatformResult { _platform = Darwin, _distroVersion = ver }
|
||||||
"freebsd" -> do
|
"freebsd" -> do
|
||||||
ver <-
|
ver <-
|
||||||
either (const Nothing) Just . versioning . decUTF8Safe
|
either (const Nothing) Just . versioning . decUTF8Safe'
|
||||||
<$> getFreeBSDVersion
|
<$> getFreeBSDVersion
|
||||||
pure $ PlatformResult { _platform = FreeBSD, _distroVersion = ver }
|
pure $ PlatformResult { _platform = FreeBSD, _distroVersion = ver }
|
||||||
|
"mingw32" -> pure PlatformResult { _platform = Windows, _distroVersion = Nothing }
|
||||||
what -> throwE $ NoCompatiblePlatform what
|
what -> throwE $ NoCompatiblePlatform what
|
||||||
lift $ $(logDebug) [i|Identified Platform as: #{pfr}|]
|
lift $ $(logDebug) [i|Identified Platform as: #{pfr}|]
|
||||||
pure pfr
|
pure pfr
|
||||||
where
|
where
|
||||||
getMajorVersion = T.intercalate "." . take 2 . T.split (== '.')
|
getMajorVersion = T.intercalate "." . take 2 . T.split (== '.')
|
||||||
getFreeBSDVersion =
|
getFreeBSDVersion =
|
||||||
liftIO $ fmap _stdOut $ executeOut [rel|freebsd-version|] [] Nothing
|
liftIO $ fmap _stdOut $ executeOut "freebsd-version" [] Nothing
|
||||||
getDarwinVersion = liftIO $ fmap _stdOut $ executeOut [rel|sw_vers|]
|
getDarwinVersion = liftIO $ fmap _stdOut $ executeOut "sw_vers"
|
||||||
["-productVersion"]
|
["-productVersion"]
|
||||||
Nothing
|
Nothing
|
||||||
|
|
||||||
@ -147,12 +150,12 @@ getLinuxDistro = do
|
|||||||
where
|
where
|
||||||
regex x = makeRegexOpts compIgnoreCase execBlank ([s|\<|] ++ x ++ [s|\>|])
|
regex x = makeRegexOpts compIgnoreCase execBlank ([s|\<|] ++ x ++ [s|\>|])
|
||||||
|
|
||||||
lsb_release_cmd :: Path Rel
|
lsb_release_cmd :: FilePath
|
||||||
lsb_release_cmd = [rel|lsb-release|]
|
lsb_release_cmd = "lsb-release"
|
||||||
redhat_release :: Path Abs
|
redhat_release :: FilePath
|
||||||
redhat_release = [abs|/etc/redhat-release|]
|
redhat_release = "/etc/redhat-release"
|
||||||
debian_version :: Path Abs
|
debian_version :: FilePath
|
||||||
debian_version = [abs|/etc/debian_version|]
|
debian_version = "/etc/debian_version"
|
||||||
|
|
||||||
try_os_release :: IO (Text, Maybe Text)
|
try_os_release :: IO (Text, Maybe Text)
|
||||||
try_os_release = do
|
try_os_release = do
|
||||||
@ -165,11 +168,11 @@ getLinuxDistro = do
|
|||||||
(Just _) <- findExecutable lsb_release_cmd
|
(Just _) <- findExecutable lsb_release_cmd
|
||||||
name <- fmap _stdOut $ executeOut lsb_release_cmd ["-si"] Nothing
|
name <- fmap _stdOut $ executeOut lsb_release_cmd ["-si"] Nothing
|
||||||
ver <- fmap _stdOut $ executeOut lsb_release_cmd ["-sr"] Nothing
|
ver <- fmap _stdOut $ executeOut lsb_release_cmd ["-sr"] Nothing
|
||||||
pure (decUTF8Safe name, Just $ decUTF8Safe ver)
|
pure (decUTF8Safe' name, Just $ decUTF8Safe' ver)
|
||||||
|
|
||||||
try_redhat_release :: IO (Text, Maybe Text)
|
try_redhat_release :: IO (Text, Maybe Text)
|
||||||
try_redhat_release = do
|
try_redhat_release = do
|
||||||
t <- fmap decUTF8Safe' $ readFile redhat_release
|
t <- T.readFile redhat_release
|
||||||
let nameRegex n =
|
let nameRegex n =
|
||||||
makeRegexOpts compIgnoreCase
|
makeRegexOpts compIgnoreCase
|
||||||
execBlank
|
execBlank
|
||||||
@ -191,5 +194,5 @@ getLinuxDistro = do
|
|||||||
|
|
||||||
try_debian_version :: IO (Text, Maybe Text)
|
try_debian_version :: IO (Text, Maybe Text)
|
||||||
try_debian_version = do
|
try_debian_version = do
|
||||||
ver <- readFile debian_version
|
ver <- T.readFile debian_version
|
||||||
pure (T.pack "debian", Just . decUTF8Safe' $ ver)
|
pure (T.pack "debian", Just ver)
|
||||||
|
@ -7,7 +7,7 @@ Copyright : (c) Julian Ospald, 2020
|
|||||||
License : LGPL-3.0
|
License : LGPL-3.0
|
||||||
Maintainer : hasufell@hasufell.de
|
Maintainer : hasufell@hasufell.de
|
||||||
Stability : experimental
|
Stability : experimental
|
||||||
Portability : POSIX
|
Portability : portable
|
||||||
-}
|
-}
|
||||||
module GHCup.Requirements where
|
module GHCup.Requirements where
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
{-# LANGUAGE CPP #-}
|
{-# LANGUAGE CPP #-}
|
||||||
{-# LANGUAGE DeriveGeneric #-}
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE QuasiQuotes #-}
|
|
||||||
|
|
||||||
{-|
|
{-|
|
||||||
Module : GHCup.Types
|
Module : GHCup.Types
|
||||||
@ -11,26 +10,39 @@ Copyright : (c) Julian Ospald, 2020
|
|||||||
License : LGPL-3.0
|
License : LGPL-3.0
|
||||||
Maintainer : hasufell@hasufell.de
|
Maintainer : hasufell@hasufell.de
|
||||||
Stability : experimental
|
Stability : experimental
|
||||||
Portability : POSIX
|
Portability : portable
|
||||||
-}
|
-}
|
||||||
module GHCup.Types where
|
module GHCup.Types
|
||||||
|
( module GHCup.Types
|
||||||
|
#if defined(BRICK)
|
||||||
|
, Key(..)
|
||||||
|
#endif
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
import Data.Map.Strict ( Map )
|
import Data.Map.Strict ( Map )
|
||||||
import Data.List.NonEmpty ( NonEmpty (..) )
|
import Data.List.NonEmpty ( NonEmpty (..) )
|
||||||
import Data.String.Interpolate
|
|
||||||
import Data.Text ( Text )
|
import Data.Text ( Text )
|
||||||
import Data.Versions
|
import Data.Versions
|
||||||
import HPath
|
|
||||||
import Text.PrettyPrint.HughesPJClass (Pretty, pPrint, text)
|
import Text.PrettyPrint.HughesPJClass (Pretty, pPrint, text)
|
||||||
import URI.ByteString
|
import URI.ByteString
|
||||||
|
#if defined(BRICK)
|
||||||
|
import Graphics.Vty ( Key(..) )
|
||||||
|
#endif
|
||||||
|
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Data.Text.Encoding as E
|
|
||||||
import qualified Data.Text.Encoding.Error as E
|
|
||||||
import qualified GHC.Generics as GHC
|
import qualified GHC.Generics as GHC
|
||||||
import qualified Graphics.Vty as Vty
|
|
||||||
|
|
||||||
|
|
||||||
|
#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)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
--------------------
|
--------------------
|
||||||
--[ GHCInfo Tree ]--
|
--[ GHCInfo Tree ]--
|
||||||
@ -157,12 +169,15 @@ data Platform = Linux LinuxDistro
|
|||||||
| Darwin
|
| Darwin
|
||||||
-- ^ must exit
|
-- ^ must exit
|
||||||
| FreeBSD
|
| FreeBSD
|
||||||
|
| Windows
|
||||||
|
-- ^ must exit
|
||||||
deriving (Eq, GHC.Generic, Ord, Show)
|
deriving (Eq, GHC.Generic, Ord, Show)
|
||||||
|
|
||||||
platformToString :: Platform -> String
|
platformToString :: Platform -> String
|
||||||
platformToString (Linux distro) = "linux-" ++ distroToString distro
|
platformToString (Linux distro) = "linux-" ++ distroToString distro
|
||||||
platformToString Darwin = "darwin"
|
platformToString Darwin = "darwin"
|
||||||
platformToString FreeBSD = "freebsd"
|
platformToString FreeBSD = "freebsd"
|
||||||
|
platformToString Windows = "windows"
|
||||||
|
|
||||||
instance Pretty Platform where
|
instance Pretty Platform where
|
||||||
pPrint = text . platformToString
|
pPrint = text . platformToString
|
||||||
@ -218,12 +233,12 @@ data DownloadInfo = DownloadInfo
|
|||||||
|
|
||||||
|
|
||||||
-- | How to descend into a tar archive.
|
-- | How to descend into a tar archive.
|
||||||
data TarDir = RealDir (Path Rel)
|
data TarDir = RealDir FilePath
|
||||||
| RegexDir String -- ^ will be compiled to regex, the first match will "win"
|
| RegexDir String -- ^ will be compiled to regex, the first match will "win"
|
||||||
deriving (Eq, Ord, GHC.Generic, Show)
|
deriving (Eq, Ord, GHC.Generic, Show)
|
||||||
|
|
||||||
instance Pretty TarDir where
|
instance Pretty TarDir where
|
||||||
pPrint (RealDir path) = text [i|#{E.decodeUtf8With E.lenientDecode . toFilePath $ path}|]
|
pPrint (RealDir path) = text path
|
||||||
pPrint (RegexDir regex) = text regex
|
pPrint (RegexDir regex) = text regex
|
||||||
|
|
||||||
|
|
||||||
@ -250,42 +265,42 @@ defaultUserSettings :: UserSettings
|
|||||||
defaultUserSettings = UserSettings Nothing Nothing Nothing Nothing Nothing Nothing Nothing
|
defaultUserSettings = UserSettings Nothing Nothing Nothing Nothing Nothing Nothing Nothing
|
||||||
|
|
||||||
data UserKeyBindings = UserKeyBindings
|
data UserKeyBindings = UserKeyBindings
|
||||||
{ kUp :: Maybe Vty.Key
|
{ kUp :: Maybe Key
|
||||||
, kDown :: Maybe Vty.Key
|
, kDown :: Maybe Key
|
||||||
, kQuit :: Maybe Vty.Key
|
, kQuit :: Maybe Key
|
||||||
, kInstall :: Maybe Vty.Key
|
, kInstall :: Maybe Key
|
||||||
, kUninstall :: Maybe Vty.Key
|
, kUninstall :: Maybe Key
|
||||||
, kSet :: Maybe Vty.Key
|
, kSet :: Maybe Key
|
||||||
, kChangelog :: Maybe Vty.Key
|
, kChangelog :: Maybe Key
|
||||||
, kShowAll :: Maybe Vty.Key
|
, kShowAll :: Maybe Key
|
||||||
, kShowAllTools :: Maybe Vty.Key
|
, kShowAllTools :: Maybe Key
|
||||||
}
|
}
|
||||||
deriving (Show, GHC.Generic)
|
deriving (Show, GHC.Generic)
|
||||||
|
|
||||||
data KeyBindings = KeyBindings
|
data KeyBindings = KeyBindings
|
||||||
{ bUp :: Vty.Key
|
{ bUp :: Key
|
||||||
, bDown :: Vty.Key
|
, bDown :: Key
|
||||||
, bQuit :: Vty.Key
|
, bQuit :: Key
|
||||||
, bInstall :: Vty.Key
|
, bInstall :: Key
|
||||||
, bUninstall :: Vty.Key
|
, bUninstall :: Key
|
||||||
, bSet :: Vty.Key
|
, bSet :: Key
|
||||||
, bChangelog :: Vty.Key
|
, bChangelog :: Key
|
||||||
, bShowAllVersions :: Vty.Key
|
, bShowAllVersions :: Key
|
||||||
, bShowAllTools :: Vty.Key
|
, bShowAllTools :: Key
|
||||||
}
|
}
|
||||||
deriving (Show, GHC.Generic)
|
deriving (Show, GHC.Generic)
|
||||||
|
|
||||||
defaultKeyBindings :: KeyBindings
|
defaultKeyBindings :: KeyBindings
|
||||||
defaultKeyBindings = KeyBindings
|
defaultKeyBindings = KeyBindings
|
||||||
{ bUp = Vty.KUp
|
{ bUp = KUp
|
||||||
, bDown = Vty.KDown
|
, bDown = KDown
|
||||||
, bQuit = Vty.KChar 'q'
|
, bQuit = KChar 'q'
|
||||||
, bInstall = Vty.KChar 'i'
|
, bInstall = KChar 'i'
|
||||||
, bUninstall = Vty.KChar 'u'
|
, bUninstall = KChar 'u'
|
||||||
, bSet = Vty.KChar 's'
|
, bSet = KChar 's'
|
||||||
, bChangelog = Vty.KChar 'c'
|
, bChangelog = KChar 'c'
|
||||||
, bShowAllVersions = Vty.KChar 'a'
|
, bShowAllVersions = KChar 'a'
|
||||||
, bShowAllTools = Vty.KChar 't'
|
, bShowAllTools = KChar 't'
|
||||||
}
|
}
|
||||||
|
|
||||||
data AppState = AppState
|
data AppState = AppState
|
||||||
@ -305,11 +320,11 @@ data Settings = Settings
|
|||||||
deriving (Show, GHC.Generic)
|
deriving (Show, GHC.Generic)
|
||||||
|
|
||||||
data Dirs = Dirs
|
data Dirs = Dirs
|
||||||
{ baseDir :: Path Abs
|
{ baseDir :: FilePath
|
||||||
, binDir :: Path Abs
|
, binDir :: FilePath
|
||||||
, cacheDir :: Path Abs
|
, cacheDir :: FilePath
|
||||||
, logsDir :: Path Abs
|
, logsDir :: FilePath
|
||||||
, confDir :: Path Abs
|
, confDir :: FilePath
|
||||||
}
|
}
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
@ -326,10 +341,10 @@ data Downloader = Curl
|
|||||||
deriving (Eq, Show, Ord)
|
deriving (Eq, Show, Ord)
|
||||||
|
|
||||||
data DebugInfo = DebugInfo
|
data DebugInfo = DebugInfo
|
||||||
{ diBaseDir :: Path Abs
|
{ diBaseDir :: FilePath
|
||||||
, diBinDir :: Path Abs
|
, diBinDir :: FilePath
|
||||||
, diGHCDir :: Path Abs
|
, diGHCDir :: FilePath
|
||||||
, diCacheDir :: Path Abs
|
, diCacheDir :: FilePath
|
||||||
, diArch :: Architecture
|
, diArch :: Architecture
|
||||||
, diPlatform :: PlatformResult
|
, diPlatform :: PlatformResult
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ Copyright : (c) Julian Ospald, 2020
|
|||||||
License : LGPL-3.0
|
License : LGPL-3.0
|
||||||
Maintainer : hasufell@hasufell.de
|
Maintainer : hasufell@hasufell.de
|
||||||
Stability : experimental
|
Stability : experimental
|
||||||
Portability : POSIX
|
Portability : portable
|
||||||
-}
|
-}
|
||||||
module GHCup.Types.JSON where
|
module GHCup.Types.JSON where
|
||||||
|
|
||||||
@ -33,15 +33,11 @@ import Data.List.NonEmpty ( NonEmpty(..) )
|
|||||||
import Data.Text.Encoding as E
|
import Data.Text.Encoding as E
|
||||||
import Data.Versions
|
import Data.Versions
|
||||||
import Data.Void
|
import Data.Void
|
||||||
import Data.Word8
|
|
||||||
import HPath
|
|
||||||
import URI.ByteString
|
import URI.ByteString
|
||||||
import Text.Casing
|
import Text.Casing
|
||||||
|
|
||||||
import qualified Data.ByteString as BS
|
|
||||||
import qualified Data.List.NonEmpty as NE
|
import qualified Data.List.NonEmpty as NE
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Graphics.Vty as Vty
|
|
||||||
import qualified Text.Megaparsec as MP
|
import qualified Text.Megaparsec as MP
|
||||||
import qualified Text.Megaparsec.Char as MPC
|
import qualified Text.Megaparsec.Char as MPC
|
||||||
|
|
||||||
@ -64,7 +60,7 @@ deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''Downlo
|
|||||||
deriveJSON defaultOptions { sumEncoding = ObjectWithSingleField } ''URLSource
|
deriveJSON defaultOptions { sumEncoding = ObjectWithSingleField } ''URLSource
|
||||||
deriveJSON defaultOptions { fieldLabelModifier = \str' -> maybe str' T.unpack . T.stripPrefix (T.pack "u-") . T.pack . kebab $ str' } ''UserSettings
|
deriveJSON defaultOptions { fieldLabelModifier = \str' -> maybe str' T.unpack . T.stripPrefix (T.pack "u-") . T.pack . kebab $ str' } ''UserSettings
|
||||||
deriveJSON defaultOptions { fieldLabelModifier = \str' -> maybe str' T.unpack . T.stripPrefix (T.pack "k-") . T.pack . kebab $ str' } ''UserKeyBindings
|
deriveJSON defaultOptions { fieldLabelModifier = \str' -> maybe str' T.unpack . T.stripPrefix (T.pack "k-") . T.pack . kebab $ str' } ''UserKeyBindings
|
||||||
deriveJSON defaultOptions { sumEncoding = ObjectWithSingleField } ''Vty.Key
|
deriveJSON defaultOptions { sumEncoding = ObjectWithSingleField } ''Key
|
||||||
|
|
||||||
instance ToJSON Tag where
|
instance ToJSON Tag where
|
||||||
toJSON Latest = String "Latest"
|
toJSON Latest = String "Latest"
|
||||||
@ -128,11 +124,13 @@ instance ToJSONKey Platform where
|
|||||||
Darwin -> T.pack "Darwin"
|
Darwin -> T.pack "Darwin"
|
||||||
FreeBSD -> T.pack "FreeBSD"
|
FreeBSD -> T.pack "FreeBSD"
|
||||||
Linux d -> T.pack ("Linux_" <> show d)
|
Linux d -> T.pack ("Linux_" <> show d)
|
||||||
|
Windows -> T.pack "Windows"
|
||||||
|
|
||||||
instance FromJSONKey Platform where
|
instance FromJSONKey Platform where
|
||||||
fromJSONKey = FromJSONKeyTextParser $ \t -> if
|
fromJSONKey = FromJSONKeyTextParser $ \t -> if
|
||||||
| T.pack "Darwin" == t -> pure Darwin
|
| T.pack "Darwin" == t -> pure Darwin
|
||||||
| T.pack "FreeBSD" == t -> pure FreeBSD
|
| T.pack "FreeBSD" == t -> pure FreeBSD
|
||||||
|
| T.pack "Windows" == t -> pure Windows
|
||||||
| T.pack "Linux_" `T.isPrefixOf` t -> case
|
| T.pack "Linux_" `T.isPrefixOf` t -> case
|
||||||
T.stripPrefix (T.pack "Linux_") t
|
T.stripPrefix (T.pack "Linux_") t
|
||||||
of
|
of
|
||||||
@ -199,20 +197,6 @@ instance ToJSONKey Tool where
|
|||||||
instance FromJSONKey Tool where
|
instance FromJSONKey Tool where
|
||||||
fromJSONKey = genericFromJSONKey defaultJSONKeyOptions
|
fromJSONKey = genericFromJSONKey defaultJSONKeyOptions
|
||||||
|
|
||||||
instance ToJSON (Path Rel) where
|
|
||||||
toJSON p = case and . fmap isAscii . BS.unpack $ fp of
|
|
||||||
True -> toJSON . decUTF8Safe $ fp
|
|
||||||
False -> String "/not/a/valid/path"
|
|
||||||
where fp = toFilePath p
|
|
||||||
|
|
||||||
instance FromJSON (Path Rel) where
|
|
||||||
parseJSON = withText "HPath Rel" $ \t -> do
|
|
||||||
let d = encodeUtf8 t
|
|
||||||
case parseRel d of
|
|
||||||
Right x -> pure x
|
|
||||||
Left e -> fail $ "Failure in HPath Rel (FromJSON)" <> show e
|
|
||||||
|
|
||||||
|
|
||||||
instance ToJSON TarDir where
|
instance ToJSON TarDir where
|
||||||
toJSON (RealDir p) = toJSON p
|
toJSON (RealDir p) = toJSON p
|
||||||
toJSON (RegexDir r) = object ["RegexDir" .= r]
|
toJSON (RegexDir r) = object ["RegexDir" .= r]
|
||||||
|
@ -7,7 +7,7 @@ Copyright : (c) Julian Ospald, 2020
|
|||||||
License : LGPL-3.0
|
License : LGPL-3.0
|
||||||
Maintainer : hasufell@hasufell.de
|
Maintainer : hasufell@hasufell.de
|
||||||
Stability : experimental
|
Stability : experimental
|
||||||
Portability : POSIX
|
Portability : portable
|
||||||
-}
|
-}
|
||||||
module GHCup.Types.Optics where
|
module GHCup.Types.Optics where
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ Copyright : (c) Julian Ospald, 2020
|
|||||||
License : LGPL-3.0
|
License : LGPL-3.0
|
||||||
Maintainer : hasufell@hasufell.de
|
Maintainer : hasufell@hasufell.de
|
||||||
Stability : experimental
|
Stability : experimental
|
||||||
Portability : POSIX
|
Portability : portable
|
||||||
|
|
||||||
This module contains GHCup helpers specific to
|
This module contains GHCup helpers specific to
|
||||||
installation and introspection of files/versions etc.
|
installation and introspection of files/versions etc.
|
||||||
@ -39,6 +39,7 @@ import GHCup.Utils.String.QQ
|
|||||||
#if !defined(TAR)
|
#if !defined(TAR)
|
||||||
import Codec.Archive hiding ( Directory )
|
import Codec.Archive hiding ( Directory )
|
||||||
#endif
|
#endif
|
||||||
|
import Codec.Archive.Zip
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
import Control.Exception.Safe
|
import Control.Exception.Safe
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
@ -51,28 +52,21 @@ import Data.ByteString ( ByteString )
|
|||||||
import Data.Either
|
import Data.Either
|
||||||
import Data.Foldable
|
import Data.Foldable
|
||||||
import Data.List
|
import Data.List
|
||||||
|
import Data.List.Extra
|
||||||
import Data.List.NonEmpty ( NonEmpty( (:|) ))
|
import Data.List.NonEmpty ( NonEmpty( (:|) ))
|
||||||
import Data.List.Split
|
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.String.Interpolate
|
import Data.String.Interpolate
|
||||||
import Data.Text ( Text )
|
import Data.Text ( Text )
|
||||||
import Data.Versions
|
import Data.Versions
|
||||||
import Data.Word8
|
import Data.Word8
|
||||||
import GHC.IO.Exception
|
import GHC.IO.Exception
|
||||||
import HPath
|
|
||||||
import HPath.IO hiding ( hideError )
|
|
||||||
import Haskus.Utils.Variant.Excepts
|
import Haskus.Utils.Variant.Excepts
|
||||||
import Optics
|
import Optics
|
||||||
import Prelude hiding ( abs
|
|
||||||
, readFile
|
|
||||||
, writeFile
|
|
||||||
)
|
|
||||||
import Safe
|
import Safe
|
||||||
|
import System.Directory hiding ( findFiles )
|
||||||
|
import System.FilePath
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
import System.Posix.FilePath ( getSearchPath
|
import System.IO.Unsafe ( unsafeInterleaveIO )
|
||||||
, takeFileName
|
|
||||||
)
|
|
||||||
import System.Posix.Files.ByteString ( readSymbolicLink )
|
|
||||||
import Text.Regex.Posix
|
import Text.Regex.Posix
|
||||||
import URI.ByteString
|
import URI.ByteString
|
||||||
|
|
||||||
@ -85,9 +79,7 @@ import qualified Codec.Compression.Lzma as Lzma
|
|||||||
import qualified Data.ByteString as B
|
import qualified Data.ByteString as B
|
||||||
import qualified Data.ByteString.Lazy as BL
|
import qualified Data.ByteString.Lazy as BL
|
||||||
import qualified Data.Map.Strict as Map
|
import qualified Data.Map.Strict as Map
|
||||||
#if !defined(TAR)
|
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
#endif
|
|
||||||
import qualified Data.Text.Encoding as E
|
import qualified Data.Text.Encoding as E
|
||||||
import qualified Text.Megaparsec as MP
|
import qualified Text.Megaparsec as MP
|
||||||
|
|
||||||
@ -102,14 +94,13 @@ import qualified Text.Megaparsec as MP
|
|||||||
|
|
||||||
-- | The symlink destination of a ghc tool.
|
-- | The symlink destination of a ghc tool.
|
||||||
ghcLinkDestination :: (MonadReader AppState m, MonadThrow m, MonadIO m)
|
ghcLinkDestination :: (MonadReader AppState m, MonadThrow m, MonadIO m)
|
||||||
=> ByteString -- ^ the tool, such as 'ghc', 'haddock' etc.
|
=> FilePath -- ^ the tool, such as 'ghc', 'haddock' etc.
|
||||||
-> GHCTargetVersion
|
-> GHCTargetVersion
|
||||||
-> m ByteString
|
-> m FilePath
|
||||||
ghcLinkDestination tool ver = do
|
ghcLinkDestination tool ver = do
|
||||||
AppState { dirs = Dirs {..} } <- ask
|
AppState { dirs = Dirs {..} } <- ask
|
||||||
t <- parseRel tool
|
|
||||||
ghcd <- ghcupGHCDir ver
|
ghcd <- ghcupGHCDir ver
|
||||||
pure (relativeSymlink binDir (ghcd </> [rel|bin|] </> t))
|
pure (relativeSymlink binDir (ghcd </> "bin" </> tool))
|
||||||
|
|
||||||
|
|
||||||
-- | Removes the minor GHC symlinks, e.g. ghc-8.6.5.
|
-- | Removes the minor GHC symlinks, e.g. ghc-8.6.5.
|
||||||
@ -127,10 +118,10 @@ rmMinorSymlinks tv@GHCTargetVersion{..} = do
|
|||||||
|
|
||||||
files <- liftE $ ghcToolFiles tv
|
files <- liftE $ ghcToolFiles tv
|
||||||
forM_ files $ \f -> do
|
forM_ files $ \f -> do
|
||||||
f_xyz <- liftIO $ parseRel (toFilePath f <> B.singleton _hyphen <> verToBS _tvVersion)
|
let f_xyz = f <> "-" <> T.unpack (prettyVer _tvVersion) <> exeExt
|
||||||
let fullF = binDir </> f_xyz
|
let fullF = binDir </> f_xyz
|
||||||
lift $ $(logDebug) [i|rm -f #{toFilePath fullF}|]
|
lift $ $(logDebug) [i|rm -f #{fullF}|]
|
||||||
liftIO $ hideError doesNotExistErrorType $ deleteFile fullF
|
liftIO $ hideError doesNotExistErrorType $ removeFile fullF
|
||||||
|
|
||||||
|
|
||||||
-- | Removes the set ghc version for the given target, if any.
|
-- | Removes the set ghc version for the given target, if any.
|
||||||
@ -148,13 +139,13 @@ rmPlain target = do
|
|||||||
forM_ mtv $ \tv -> do
|
forM_ mtv $ \tv -> do
|
||||||
files <- liftE $ ghcToolFiles tv
|
files <- liftE $ ghcToolFiles tv
|
||||||
forM_ files $ \f -> do
|
forM_ files $ \f -> do
|
||||||
let fullF = binDir </> f
|
let fullF = binDir </> f <> exeExt
|
||||||
lift $ $(logDebug) [i|rm -f #{toFilePath fullF}|]
|
lift $ $(logDebug) [i|rm -f #{fullF}|]
|
||||||
liftIO $ hideError doesNotExistErrorType $ deleteFile fullF
|
liftIO $ hideError doesNotExistErrorType $ removeFile fullF
|
||||||
-- old ghcup
|
-- old ghcup
|
||||||
let hdc_file = binDir </> [rel|haddock-ghc|]
|
let hdc_file = binDir </> "haddock-ghc" <> exeExt
|
||||||
lift $ $(logDebug) [i|rm -f #{toFilePath hdc_file}|]
|
lift $ $(logDebug) [i|rm -f #{hdc_file}|]
|
||||||
liftIO $ hideError doesNotExistErrorType $ deleteFile hdc_file
|
liftIO $ hideError doesNotExistErrorType $ removeFile hdc_file
|
||||||
|
|
||||||
|
|
||||||
-- | Remove the major GHC symlink, e.g. ghc-8.6.
|
-- | Remove the major GHC symlink, e.g. ghc-8.6.
|
||||||
@ -174,10 +165,10 @@ rmMajorSymlinks tv@GHCTargetVersion{..} = do
|
|||||||
|
|
||||||
files <- liftE $ ghcToolFiles tv
|
files <- liftE $ ghcToolFiles tv
|
||||||
forM_ files $ \f -> do
|
forM_ files $ \f -> do
|
||||||
f_xyz <- liftIO $ parseRel (toFilePath f <> B.singleton _hyphen <> E.encodeUtf8 v')
|
let f_xy = f <> "-" <> T.unpack v' <> exeExt
|
||||||
let fullF = binDir </> f_xyz
|
let fullF = binDir </> f_xy
|
||||||
lift $ $(logDebug) [i|rm -f #{toFilePath fullF}|]
|
lift $ $(logDebug) [i|rm -f #{fullF}|]
|
||||||
liftIO $ hideError doesNotExistErrorType $ deleteFile fullF
|
liftIO $ hideError doesNotExistErrorType $ removeFile fullF
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -208,42 +199,40 @@ ghcSet :: (MonadReader AppState m, MonadThrow m, MonadIO m)
|
|||||||
-> m (Maybe GHCTargetVersion)
|
-> m (Maybe GHCTargetVersion)
|
||||||
ghcSet mtarget = do
|
ghcSet mtarget = do
|
||||||
AppState {dirs = Dirs {..}} <- ask
|
AppState {dirs = Dirs {..}} <- ask
|
||||||
ghc <- parseRel $ E.encodeUtf8 (maybe "ghc" (<> "-ghc") mtarget)
|
let ghc = maybe "ghc" (\t -> T.unpack t <> "-ghc") mtarget
|
||||||
let ghcBin = binDir </> ghc
|
let ghcBin = binDir </> ghc <> exeExt
|
||||||
|
|
||||||
-- link destination is of the form ../ghc/<ver>/bin/ghc
|
-- link destination is of the form ../ghc/<ver>/bin/ghc
|
||||||
-- for old ghcup, it is ../ghc/<ver>/bin/ghc-<ver>
|
-- for old ghcup, it is ../ghc/<ver>/bin/ghc-<ver>
|
||||||
liftIO $ handleIO' NoSuchThing (\_ -> pure Nothing) $ do
|
liftIO $ handleIO' NoSuchThing (\_ -> pure Nothing) $ do
|
||||||
link <- readSymbolicLink $ toFilePath ghcBin
|
link <- liftIO $ getSymbolicLinkTarget ghcBin
|
||||||
Just <$> ghcLinkVersion link
|
Just <$> ghcLinkVersion link
|
||||||
|
|
||||||
ghcLinkVersion :: MonadThrow m => ByteString -> m GHCTargetVersion
|
ghcLinkVersion :: MonadThrow m => FilePath -> m GHCTargetVersion
|
||||||
ghcLinkVersion bs = do
|
ghcLinkVersion (T.pack . dropSuffix exeExt -> t) = throwEither $ MP.parse parser "ghcLinkVersion" t
|
||||||
t <- throwEither $ E.decodeUtf8' bs
|
|
||||||
throwEither $ MP.parse parser "ghcLinkVersion" t
|
|
||||||
where
|
where
|
||||||
parser =
|
parser =
|
||||||
(do
|
(do
|
||||||
_ <- parseUntil1 (MP.chunk "/ghc/")
|
_ <- parseUntil1 ghcSubPath
|
||||||
_ <- MP.chunk "/ghc/"
|
_ <- ghcSubPath
|
||||||
r <- parseUntil1 (MP.chunk "/")
|
r <- parseUntil1 pathSep
|
||||||
rest <- MP.getInput
|
rest <- MP.getInput
|
||||||
MP.setInput r
|
MP.setInput r
|
||||||
x <- ghcTargetVerP
|
x <- ghcTargetVerP
|
||||||
MP.setInput rest
|
MP.setInput rest
|
||||||
pure x
|
pure x
|
||||||
)
|
)
|
||||||
<* MP.chunk "/"
|
<* pathSep
|
||||||
<* MP.takeRest
|
<* MP.takeRest
|
||||||
<* MP.eof
|
<* MP.eof
|
||||||
|
ghcSubPath = pathSep <* MP.chunk "ghc" *> pathSep
|
||||||
|
|
||||||
-- | Get all installed GHCs by reading ~/.ghcup/ghc/<dir>.
|
-- | Get all installed GHCs by reading ~/.ghcup/ghc/<dir>.
|
||||||
-- If a dir cannot be parsed, returns left.
|
-- If a dir cannot be parsed, returns left.
|
||||||
getInstalledGHCs :: (MonadReader AppState m, MonadIO m) => m [Either (Path Rel) GHCTargetVersion]
|
getInstalledGHCs :: (MonadReader AppState m, MonadIO m) => m [Either FilePath GHCTargetVersion]
|
||||||
getInstalledGHCs = do
|
getInstalledGHCs = do
|
||||||
ghcdir <- ghcupGHCBaseDir
|
ghcdir <- ghcupGHCBaseDir
|
||||||
fs <- liftIO $ hideErrorDef [NoSuchThing] [] $ getDirsFiles' ghcdir
|
fs <- liftIO $ hideErrorDef [NoSuchThing] [] $ listDirectory ghcdir
|
||||||
forM fs $ \f -> case parseGHCupGHCDir f of
|
forM fs $ \f -> case parseGHCupGHCDir f of
|
||||||
Right r -> pure $ Right r
|
Right r -> pure $ Right r
|
||||||
Left _ -> pure $ Left f
|
Left _ -> pure $ Left f
|
||||||
@ -251,7 +240,7 @@ getInstalledGHCs = do
|
|||||||
|
|
||||||
-- | Get all installed cabals, by matching on @~\/.ghcup\/bin/cabal-*@.
|
-- | Get all installed cabals, by matching on @~\/.ghcup\/bin/cabal-*@.
|
||||||
getInstalledCabals :: (MonadLogger m, MonadReader AppState m, MonadIO m, MonadCatch m)
|
getInstalledCabals :: (MonadLogger m, MonadReader AppState m, MonadIO m, MonadCatch m)
|
||||||
=> m [Either (Path Rel) Version]
|
=> m [Either FilePath Version]
|
||||||
getInstalledCabals = do
|
getInstalledCabals = do
|
||||||
cs <- cabalSet -- for legacy cabal
|
cs <- cabalSet -- for legacy cabal
|
||||||
getInstalledCabals' cs
|
getInstalledCabals' cs
|
||||||
@ -259,13 +248,13 @@ getInstalledCabals = do
|
|||||||
|
|
||||||
getInstalledCabals' :: (MonadLogger m, MonadReader AppState m, MonadIO m, MonadCatch m)
|
getInstalledCabals' :: (MonadLogger m, MonadReader AppState m, MonadIO m, MonadCatch m)
|
||||||
=> Maybe Version
|
=> Maybe Version
|
||||||
-> m [Either (Path Rel) Version]
|
-> m [Either FilePath Version]
|
||||||
getInstalledCabals' cs = do
|
getInstalledCabals' cs = do
|
||||||
AppState {dirs = Dirs {..}} <- ask
|
AppState {dirs = Dirs {..}} <- ask
|
||||||
bins <- liftIO $ handleIO (\_ -> pure []) $ findFiles
|
bins <- liftIO $ handleIO (\_ -> pure []) $ findFiles
|
||||||
binDir
|
binDir
|
||||||
(makeRegexOpts compExtended execBlank ([s|^cabal-.*$|] :: ByteString))
|
(makeRegexOpts compExtended execBlank ([s|^cabal-.*$|] :: ByteString))
|
||||||
vs <- forM bins $ \f -> case fmap (version . decUTF8Safe) . B.stripPrefix "cabal-" . toFilePath $ f of
|
vs <- forM bins $ \f -> case fmap (version . T.pack . dropSuffix exeExt) . stripPrefix "cabal-" $ f of
|
||||||
Just (Right r) -> pure $ Right r
|
Just (Right r) -> pure $ Right r
|
||||||
Just (Left _) -> pure $ Left f
|
Just (Left _) -> pure $ Left f
|
||||||
Nothing -> pure $ Left f
|
Nothing -> pure $ Left f
|
||||||
@ -283,22 +272,20 @@ cabalInstalled ver = do
|
|||||||
cabalSet :: (MonadLogger m, MonadReader AppState m, MonadIO m, MonadThrow m, MonadCatch m) => m (Maybe Version)
|
cabalSet :: (MonadLogger m, MonadReader AppState m, MonadIO m, MonadThrow m, MonadCatch m) => m (Maybe Version)
|
||||||
cabalSet = do
|
cabalSet = do
|
||||||
AppState {dirs = Dirs {..}} <- ask
|
AppState {dirs = Dirs {..}} <- ask
|
||||||
let cabalbin = binDir </> [rel|cabal|]
|
let cabalbin = binDir </> "cabal" <> exeExt
|
||||||
b <- handleIO (\_ -> pure False) $ fmap (== SymbolicLink) $ liftIO $ getFileType cabalbin
|
b <- handleIO (\_ -> pure False) $ liftIO $ pathIsSymbolicLink cabalbin
|
||||||
if
|
if
|
||||||
| b -> do
|
| b -> do
|
||||||
handleIO' NoSuchThing (\_ -> pure Nothing) $ do
|
handleIO' NoSuchThing (\_ -> pure Nothing) $ do
|
||||||
broken <- liftIO $ isBrokenSymlink cabalbin
|
broken <- liftIO $ isBrokenSymlink cabalbin
|
||||||
if broken
|
if broken
|
||||||
then do
|
then pure Nothing
|
||||||
$(logWarn) [i|Symlink #{cabalbin} is broken.|]
|
|
||||||
pure Nothing
|
|
||||||
else do
|
else do
|
||||||
link <- liftIO $ readSymbolicLink $ toFilePath cabalbin
|
link <- liftIO $ getSymbolicLinkTarget cabalbin
|
||||||
case linkVersion link of
|
case linkVersion link of
|
||||||
Right v -> pure $ Just v
|
Right v -> pure $ Just v
|
||||||
Left err -> do
|
Left err -> do
|
||||||
$(logWarn) [i|Failed to parse cabal symlink target with: "#{err}". The symlink #{toFilePath cabalbin} needs to point to valid cabal binary, such as 'cabal-3.4.0.0'.|]
|
$(logWarn) [i|Failed to parse cabal symlink target with: "#{err}". The symlink #{cabalbin} needs to point to valid cabal binary, such as 'cabal-3.4.0.0'.|]
|
||||||
pure Nothing
|
pure Nothing
|
||||||
| otherwise -> do -- legacy behavior
|
| otherwise -> do -- legacy behavior
|
||||||
mc <- liftIO $ handleIO (\_ -> pure Nothing) $ fmap Just $ executeOut
|
mc <- liftIO $ handleIO (\_ -> pure Nothing) $ fmap Just $ executeOut
|
||||||
@ -306,8 +293,8 @@ cabalSet = do
|
|||||||
["--numeric-version"]
|
["--numeric-version"]
|
||||||
Nothing
|
Nothing
|
||||||
fmap join $ forM mc $ \c -> if
|
fmap join $ forM mc $ \c -> if
|
||||||
| not (B.null (_stdOut c)), _exitCode c == ExitSuccess -> do
|
| not (BL.null (_stdOut c)), _exitCode c == ExitSuccess -> do
|
||||||
let reportedVer = fst . B.spanEnd (== _lf) . _stdOut $ c
|
let reportedVer = fst . B.spanEnd (== _lf) . BL.toStrict . _stdOut $ c
|
||||||
case version $ decUTF8Safe reportedVer of
|
case version $ decUTF8Safe reportedVer of
|
||||||
Left e -> throwM e
|
Left e -> throwM e
|
||||||
Right r -> pure $ Just r
|
Right r -> pure $ Just r
|
||||||
@ -316,10 +303,8 @@ cabalSet = do
|
|||||||
-- We try to be extra permissive with link destination parsing,
|
-- We try to be extra permissive with link destination parsing,
|
||||||
-- because of:
|
-- because of:
|
||||||
-- https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/119
|
-- https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/119
|
||||||
linkVersion :: MonadThrow m => ByteString -> m Version
|
linkVersion :: MonadThrow m => FilePath -> m Version
|
||||||
linkVersion bs = do
|
linkVersion = throwEither . MP.parse parser "" . T.pack . dropSuffix exeExt
|
||||||
t <- throwEither $ E.decodeUtf8' bs
|
|
||||||
throwEither $ MP.parse parser "" t
|
|
||||||
|
|
||||||
parser
|
parser
|
||||||
= MP.try (stripAbsolutePath *> cabalParse)
|
= MP.try (stripAbsolutePath *> cabalParse)
|
||||||
@ -329,10 +314,10 @@ cabalSet = do
|
|||||||
cabalParse = MP.chunk "cabal-" *> version'
|
cabalParse = MP.chunk "cabal-" *> version'
|
||||||
-- parses any path component ending with path separator,
|
-- parses any path component ending with path separator,
|
||||||
-- e.g. "foo/"
|
-- e.g. "foo/"
|
||||||
stripPathComponet = parseUntil1 "/" *> MP.chunk "/"
|
stripPathComponet = parseUntil1 pathSep *> pathSep
|
||||||
-- parses an absolute path up until the last path separator,
|
-- parses an absolute path up until the last path separator,
|
||||||
-- e.g. "/bar/baz/foo" -> "/bar/baz/", leaving "foo"
|
-- e.g. "/bar/baz/foo" -> "/bar/baz/", leaving "foo"
|
||||||
stripAbsolutePath = MP.chunk "/" *> MP.many (MP.try stripPathComponet)
|
stripAbsolutePath = pathSep *> MP.many (MP.try stripPathComponet)
|
||||||
-- parses a relative path up until the last path separator,
|
-- parses a relative path up until the last path separator,
|
||||||
-- e.g. "bar/baz/foo" -> "bar/baz/", leaving "foo"
|
-- e.g. "bar/baz/foo" -> "bar/baz/", leaving "foo"
|
||||||
stripRelativePath = MP.many (MP.try stripPathComponet)
|
stripRelativePath = MP.many (MP.try stripPathComponet)
|
||||||
@ -342,7 +327,7 @@ cabalSet = do
|
|||||||
-- | Get all installed hls, by matching on
|
-- | Get all installed hls, by matching on
|
||||||
-- @~\/.ghcup\/bin/haskell-language-server-wrapper-<\hlsver\>@.
|
-- @~\/.ghcup\/bin/haskell-language-server-wrapper-<\hlsver\>@.
|
||||||
getInstalledHLSs :: (MonadReader AppState m, MonadIO m, MonadCatch m)
|
getInstalledHLSs :: (MonadReader AppState m, MonadIO m, MonadCatch m)
|
||||||
=> m [Either (Path Rel) Version]
|
=> m [Either FilePath Version]
|
||||||
getInstalledHLSs = do
|
getInstalledHLSs = do
|
||||||
AppState { dirs = Dirs {..} } <- ask
|
AppState { dirs = Dirs {..} } <- ask
|
||||||
bins <- liftIO $ handleIO (\_ -> pure []) $ findFiles
|
bins <- liftIO $ handleIO (\_ -> pure []) $ findFiles
|
||||||
@ -353,7 +338,7 @@ getInstalledHLSs = do
|
|||||||
)
|
)
|
||||||
forM bins $ \f ->
|
forM bins $ \f ->
|
||||||
case
|
case
|
||||||
fmap (version . decUTF8Safe) . B.stripPrefix "haskell-language-server-wrapper-" . toFilePath $ f
|
fmap (version . T.pack . dropSuffix exeExt) . stripPrefix "haskell-language-server-wrapper-" $ f
|
||||||
of
|
of
|
||||||
Just (Right r) -> pure $ Right r
|
Just (Right r) -> pure $ Right r
|
||||||
Just (Left _) -> pure $ Left f
|
Just (Left _) -> pure $ Left f
|
||||||
@ -362,7 +347,7 @@ getInstalledHLSs = do
|
|||||||
-- | Get all installed stacks, by matching on
|
-- | Get all installed stacks, by matching on
|
||||||
-- @~\/.ghcup\/bin/stack-<\stackver\>@.
|
-- @~\/.ghcup\/bin/stack-<\stackver\>@.
|
||||||
getInstalledStacks :: (MonadReader AppState m, MonadIO m, MonadCatch m)
|
getInstalledStacks :: (MonadReader AppState m, MonadIO m, MonadCatch m)
|
||||||
=> m [Either (Path Rel) Version]
|
=> m [Either FilePath Version]
|
||||||
getInstalledStacks = do
|
getInstalledStacks = do
|
||||||
AppState { dirs = Dirs {..} } <- ask
|
AppState { dirs = Dirs {..} } <- ask
|
||||||
bins <- liftIO $ handleIO (\_ -> pure []) $ findFiles
|
bins <- liftIO $ handleIO (\_ -> pure []) $ findFiles
|
||||||
@ -373,7 +358,7 @@ getInstalledStacks = do
|
|||||||
)
|
)
|
||||||
forM bins $ \f ->
|
forM bins $ \f ->
|
||||||
case
|
case
|
||||||
fmap (version . decUTF8Safe) . B.stripPrefix "stack-" . toFilePath $ f
|
fmap (version . T.pack . dropSuffix exeExt) . stripPrefix "stack-" $ f
|
||||||
of
|
of
|
||||||
Just (Right r) -> pure $ Right r
|
Just (Right r) -> pure $ Right r
|
||||||
Just (Left _) -> pure $ Left f
|
Just (Left _) -> pure $ Left f
|
||||||
@ -384,20 +369,18 @@ getInstalledStacks = do
|
|||||||
stackSet :: (MonadReader AppState m, MonadIO m, MonadThrow m, MonadCatch m) => m (Maybe Version)
|
stackSet :: (MonadReader AppState m, MonadIO m, MonadThrow m, MonadCatch m) => m (Maybe Version)
|
||||||
stackSet = do
|
stackSet = do
|
||||||
AppState {dirs = Dirs {..}} <- ask
|
AppState {dirs = Dirs {..}} <- ask
|
||||||
let stackBin = binDir </> [rel|stack|]
|
let stackBin = binDir </> "stack" <> exeExt
|
||||||
|
|
||||||
liftIO $ handleIO' NoSuchThing (\_ -> pure Nothing) $ do
|
liftIO $ handleIO' NoSuchThing (\_ -> pure Nothing) $ do
|
||||||
broken <- isBrokenSymlink stackBin
|
broken <- isBrokenSymlink stackBin
|
||||||
if broken
|
if broken
|
||||||
then pure Nothing
|
then pure Nothing
|
||||||
else do
|
else do
|
||||||
link <- readSymbolicLink $ toFilePath stackBin
|
link <- liftIO $ getSymbolicLinkTarget stackBin
|
||||||
Just <$> linkVersion link
|
Just <$> linkVersion link
|
||||||
where
|
where
|
||||||
linkVersion :: MonadThrow m => ByteString -> m Version
|
linkVersion :: MonadThrow m => FilePath -> m Version
|
||||||
linkVersion bs = do
|
linkVersion = throwEither . MP.parse parser "" . T.pack . dropSuffix exeExt
|
||||||
t <- throwEither $ E.decodeUtf8' bs
|
|
||||||
throwEither $ MP.parse parser "" t
|
|
||||||
where
|
where
|
||||||
parser =
|
parser =
|
||||||
MP.chunk "stack-" *> version'
|
MP.chunk "stack-" *> version'
|
||||||
@ -420,20 +403,18 @@ hlsInstalled ver = do
|
|||||||
hlsSet :: (MonadReader AppState m, MonadIO m, MonadThrow m, MonadCatch m) => m (Maybe Version)
|
hlsSet :: (MonadReader AppState m, MonadIO m, MonadThrow m, MonadCatch m) => m (Maybe Version)
|
||||||
hlsSet = do
|
hlsSet = do
|
||||||
AppState {dirs = Dirs {..}} <- ask
|
AppState {dirs = Dirs {..}} <- ask
|
||||||
let hlsBin = binDir </> [rel|haskell-language-server-wrapper|]
|
let hlsBin = binDir </> "haskell-language-server-wrapper" <> exeExt
|
||||||
|
|
||||||
liftIO $ handleIO' NoSuchThing (\_ -> pure Nothing) $ do
|
liftIO $ handleIO' NoSuchThing (\_ -> pure Nothing) $ do
|
||||||
broken <- isBrokenSymlink hlsBin
|
broken <- isBrokenSymlink hlsBin
|
||||||
if broken
|
if broken
|
||||||
then pure Nothing
|
then pure Nothing
|
||||||
else do
|
else do
|
||||||
link <- readSymbolicLink $ toFilePath hlsBin
|
link <- liftIO $ getSymbolicLinkTarget hlsBin
|
||||||
Just <$> linkVersion link
|
Just <$> linkVersion link
|
||||||
where
|
where
|
||||||
linkVersion :: MonadThrow m => ByteString -> m Version
|
linkVersion :: MonadThrow m => FilePath -> m Version
|
||||||
linkVersion bs = do
|
linkVersion = throwEither . MP.parse parser "" . T.pack . dropSuffix exeExt
|
||||||
t <- throwEither $ E.decodeUtf8' bs
|
|
||||||
throwEither $ MP.parse parser "" t
|
|
||||||
where
|
where
|
||||||
parser =
|
parser =
|
||||||
MP.chunk "haskell-language-server-wrapper-" *> version'
|
MP.chunk "haskell-language-server-wrapper-" *> version'
|
||||||
@ -452,13 +433,12 @@ hlsGHCVersions = do
|
|||||||
bins <- hlsServerBinaries h'
|
bins <- hlsServerBinaries h'
|
||||||
pure $ fmap
|
pure $ fmap
|
||||||
(version
|
(version
|
||||||
. decUTF8Safe
|
. T.pack
|
||||||
. fromJust
|
. fromJust
|
||||||
. B.stripPrefix "haskell-language-server-"
|
. stripPrefix "haskell-language-server-"
|
||||||
. head
|
. head
|
||||||
. B.split _tilde
|
. splitOn "~"
|
||||||
. toFilePath
|
)
|
||||||
)
|
|
||||||
bins
|
bins
|
||||||
pure . rights . concat . maybeToList $ vers
|
pure . rights . concat . maybeToList $ vers
|
||||||
|
|
||||||
@ -466,7 +446,7 @@ hlsGHCVersions = do
|
|||||||
-- | Get all server binaries for an hls version, if any.
|
-- | Get all server binaries for an hls version, if any.
|
||||||
hlsServerBinaries :: (MonadReader AppState m, MonadIO m)
|
hlsServerBinaries :: (MonadReader AppState m, MonadIO m)
|
||||||
=> Version
|
=> Version
|
||||||
-> m [Path Rel]
|
-> m [FilePath]
|
||||||
hlsServerBinaries ver = do
|
hlsServerBinaries ver = do
|
||||||
AppState { dirs = Dirs {..} } <- ask
|
AppState { dirs = Dirs {..} } <- ask
|
||||||
liftIO $ handleIO (\_ -> pure []) $ findFiles
|
liftIO $ handleIO (\_ -> pure []) $ findFiles
|
||||||
@ -474,7 +454,7 @@ hlsServerBinaries ver = do
|
|||||||
(makeRegexOpts
|
(makeRegexOpts
|
||||||
compExtended
|
compExtended
|
||||||
execBlank
|
execBlank
|
||||||
([s|^haskell-language-server-.*~|] <> escapeVerRex ver <> [s|$|] :: ByteString
|
([s|^haskell-language-server-.*~|] <> escapeVerRex ver <> E.encodeUtf8 (T.pack exeExt) <> [s|$|] :: ByteString
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -482,7 +462,7 @@ hlsServerBinaries ver = do
|
|||||||
-- | Get the wrapper binary for an hls version, if any.
|
-- | Get the wrapper binary for an hls version, if any.
|
||||||
hlsWrapperBinary :: (MonadReader AppState m, MonadThrow m, MonadIO m)
|
hlsWrapperBinary :: (MonadReader AppState m, MonadThrow m, MonadIO m)
|
||||||
=> Version
|
=> Version
|
||||||
-> m (Maybe (Path Rel))
|
-> m (Maybe FilePath)
|
||||||
hlsWrapperBinary ver = do
|
hlsWrapperBinary ver = do
|
||||||
AppState { dirs = Dirs {..} } <- ask
|
AppState { dirs = Dirs {..} } <- ask
|
||||||
wrapper <- liftIO $ handleIO (\_ -> pure []) $ findFiles
|
wrapper <- liftIO $ handleIO (\_ -> pure []) $ findFiles
|
||||||
@ -490,7 +470,7 @@ hlsWrapperBinary ver = do
|
|||||||
(makeRegexOpts
|
(makeRegexOpts
|
||||||
compExtended
|
compExtended
|
||||||
execBlank
|
execBlank
|
||||||
([s|^haskell-language-server-wrapper-|] <> escapeVerRex ver <> [s|$|] :: ByteString
|
([s|^haskell-language-server-wrapper-|] <> escapeVerRex ver <> E.encodeUtf8 (T.pack exeExt) <> [s|$|] :: ByteString
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
case wrapper of
|
case wrapper of
|
||||||
@ -501,7 +481,7 @@ hlsWrapperBinary ver = do
|
|||||||
|
|
||||||
|
|
||||||
-- | Get all binaries for an hls version, if any.
|
-- | Get all binaries for an hls version, if any.
|
||||||
hlsAllBinaries :: (MonadReader AppState m, MonadIO m, MonadThrow m) => Version -> m [Path Rel]
|
hlsAllBinaries :: (MonadReader AppState m, MonadIO m, MonadThrow m) => Version -> m [FilePath]
|
||||||
hlsAllBinaries ver = do
|
hlsAllBinaries ver = do
|
||||||
hls <- hlsServerBinaries ver
|
hls <- hlsServerBinaries ver
|
||||||
wrapper <- hlsWrapperBinary ver
|
wrapper <- hlsWrapperBinary ver
|
||||||
@ -509,7 +489,7 @@ hlsAllBinaries ver = do
|
|||||||
|
|
||||||
|
|
||||||
-- | Get the active symlinks for hls.
|
-- | Get the active symlinks for hls.
|
||||||
hlsSymlinks :: (MonadReader AppState m, MonadIO m, MonadCatch m) => m [Path Rel]
|
hlsSymlinks :: (MonadReader AppState m, MonadIO m, MonadCatch m) => m [FilePath]
|
||||||
hlsSymlinks = do
|
hlsSymlinks = do
|
||||||
AppState { dirs = Dirs {..} } <- ask
|
AppState { dirs = Dirs {..} } <- ask
|
||||||
oldSyms <- liftIO $ handleIO (\_ -> pure []) $ findFiles
|
oldSyms <- liftIO $ handleIO (\_ -> pure []) $ findFiles
|
||||||
@ -519,9 +499,8 @@ hlsSymlinks = do
|
|||||||
([s|^haskell-language-server-.*$|] :: ByteString)
|
([s|^haskell-language-server-.*$|] :: ByteString)
|
||||||
)
|
)
|
||||||
filterM
|
filterM
|
||||||
( fmap (== SymbolicLink)
|
( liftIO
|
||||||
. liftIO
|
. pathIsSymbolicLink
|
||||||
. getFileType
|
|
||||||
. (binDir </>)
|
. (binDir </>)
|
||||||
)
|
)
|
||||||
oldSyms
|
oldSyms
|
||||||
@ -585,61 +564,61 @@ getLatestGHCFor major' minor' dls =
|
|||||||
|
|
||||||
-- | Unpack an archive to a temporary directory and return that path.
|
-- | Unpack an archive to a temporary directory and return that path.
|
||||||
unpackToDir :: (MonadLogger m, MonadIO m, MonadThrow m)
|
unpackToDir :: (MonadLogger m, MonadIO m, MonadThrow m)
|
||||||
=> Path Abs -- ^ destination dir
|
=> FilePath -- ^ destination dir
|
||||||
-> Path Abs -- ^ archive path
|
-> FilePath -- ^ archive path
|
||||||
-> Excepts '[UnknownArchive
|
-> Excepts '[UnknownArchive
|
||||||
#if !defined(TAR)
|
#if !defined(TAR)
|
||||||
, ArchiveResult
|
, ArchiveResult
|
||||||
#endif
|
#endif
|
||||||
] m ()
|
] m ()
|
||||||
unpackToDir dest av = do
|
unpackToDir dfp av = do
|
||||||
fp <- decUTF8Safe . toFilePath <$> basename av
|
let fn = takeFileName av
|
||||||
let dfp = decUTF8Safe . toFilePath $ dest
|
lift $ $(logInfo) [i|Unpacking: #{fn} to #{dfp}|]
|
||||||
lift $ $(logInfo) [i|Unpacking: #{fp} to #{dfp}|]
|
|
||||||
fn <- toFilePath <$> basename av
|
|
||||||
|
|
||||||
#if defined(TAR)
|
#if defined(TAR)
|
||||||
let untar :: MonadIO m => BL.ByteString -> Excepts '[] m ()
|
let untar :: MonadIO m => BL.ByteString -> Excepts '[] m ()
|
||||||
untar = liftIO . Tar.unpack (toFilePath dest) . Tar.read
|
untar = liftIO . Tar.unpack dfp . Tar.read
|
||||||
|
|
||||||
rf :: MonadIO m => Path Abs -> Excepts '[] m BL.ByteString
|
rf :: MonadIO m => FilePath -> Excepts '[] m BL.ByteString
|
||||||
rf = liftIO . readFile
|
rf = liftIO . BL.readFile
|
||||||
#else
|
#else
|
||||||
let untar :: MonadIO m => BL.ByteString -> Excepts '[ArchiveResult] m ()
|
let untar :: MonadIO m => BL.ByteString -> Excepts '[ArchiveResult] m ()
|
||||||
untar = lEM . liftIO . runArchiveM . unpackToDirLazy (T.unpack . decUTF8Safe . toFilePath $ dest)
|
untar = lEM . liftIO . runArchiveM . unpackToDirLazy dfp
|
||||||
|
|
||||||
rf :: MonadIO m => Path Abs -> Excepts '[ArchiveResult] m BL.ByteString
|
rf :: MonadIO m => FilePath -> Excepts '[ArchiveResult] m BL.ByteString
|
||||||
rf = liftIO . readFile
|
rf = liftIO . BL.readFile
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
-- extract, depending on file extension
|
-- extract, depending on file extension
|
||||||
if
|
if
|
||||||
| ".tar.gz" `B.isSuffixOf` fn -> liftE
|
| ".tar.gz" `isSuffixOf` fn -> liftE
|
||||||
(untar . GZip.decompress =<< rf av)
|
(untar . GZip.decompress =<< rf av)
|
||||||
| ".tar.xz" `B.isSuffixOf` fn -> do
|
| ".tar.xz" `isSuffixOf` fn -> do
|
||||||
filecontents <- liftE $ rf av
|
filecontents <- liftE $ rf av
|
||||||
let decompressed = Lzma.decompress filecontents
|
let decompressed = Lzma.decompress filecontents
|
||||||
liftE $ untar decompressed
|
liftE $ untar decompressed
|
||||||
| ".tar.bz2" `B.isSuffixOf` fn ->
|
| ".tar.bz2" `isSuffixOf` fn ->
|
||||||
liftE (untar . BZip.decompress =<< rf av)
|
liftE (untar . BZip.decompress =<< rf av)
|
||||||
| ".tar" `B.isSuffixOf` fn -> liftE (untar =<< rf av)
|
| ".tar" `isSuffixOf` fn -> liftE (untar =<< rf av)
|
||||||
|
| ".zip" `isSuffixOf` fn ->
|
||||||
|
withArchive av (unpackInto dfp)
|
||||||
| otherwise -> throwE $ UnknownArchive fn
|
| otherwise -> throwE $ UnknownArchive fn
|
||||||
|
|
||||||
|
|
||||||
getArchiveFiles :: (MonadLogger m, MonadIO m, MonadThrow m)
|
getArchiveFiles :: (MonadLogger m, MonadIO m, MonadThrow m)
|
||||||
=> Path Abs -- ^ archive path
|
=> FilePath -- ^ archive path
|
||||||
-> Excepts '[UnknownArchive
|
-> Excepts '[UnknownArchive
|
||||||
#if defined(TAR)
|
#if defined(TAR)
|
||||||
, Tar.FormatError
|
, Tar.FormatError
|
||||||
#else
|
#else
|
||||||
, ArchiveResult
|
, ArchiveResult
|
||||||
#endif
|
#endif
|
||||||
] m [ByteString]
|
] m [FilePath]
|
||||||
getArchiveFiles av = do
|
getArchiveFiles av = do
|
||||||
fn <- toFilePath <$> basename av
|
let fn = takeFileName av
|
||||||
|
|
||||||
#if defined(TAR)
|
#if defined(TAR)
|
||||||
let entries :: Monad m => BL.ByteString -> Excepts '[Tar.FormatError] m [ByteString]
|
let entries :: Monad m => BL.ByteString -> Excepts '[Tar.FormatError] m [FilePath]
|
||||||
entries =
|
entries =
|
||||||
lE @Tar.FormatError
|
lE @Tar.FormatError
|
||||||
. Tar.foldEntries
|
. Tar.foldEntries
|
||||||
@ -648,41 +627,45 @@ getArchiveFiles av = do
|
|||||||
(\e -> Left e)
|
(\e -> Left e)
|
||||||
. Tar.read
|
. Tar.read
|
||||||
|
|
||||||
rf :: MonadIO m => Path Abs -> Excepts '[Tar.FormatError] m BL.ByteString
|
rf :: MonadIO m => FilePath -> Excepts '[Tar.FormatError] m BL.ByteString
|
||||||
rf = liftIO . readFile
|
rf = liftIO . BL.readFile
|
||||||
#else
|
#else
|
||||||
let entries :: Monad m => BL.ByteString -> Excepts '[ArchiveResult] m [ByteString]
|
let entries :: Monad m => BL.ByteString -> Excepts '[ArchiveResult] m [FilePath]
|
||||||
entries = (fmap . fmap) (E.encodeUtf8 . T.pack . filepath) . lE . readArchiveBSL
|
entries = (fmap . fmap) filepath . lE . readArchiveBSL
|
||||||
|
|
||||||
rf :: MonadIO m => Path Abs -> Excepts '[ArchiveResult] m BL.ByteString
|
rf :: MonadIO m => FilePath -> Excepts '[ArchiveResult] m BL.ByteString
|
||||||
rf = liftIO . readFile
|
rf = liftIO . BL.readFile
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
-- extract, depending on file extension
|
-- extract, depending on file extension
|
||||||
if
|
if
|
||||||
| ".tar.gz" `B.isSuffixOf` fn -> liftE
|
| ".tar.gz" `isSuffixOf` fn -> liftE
|
||||||
(entries . GZip.decompress =<< rf av)
|
(entries . GZip.decompress =<< rf av)
|
||||||
| ".tar.xz" `B.isSuffixOf` fn -> do
|
| ".tar.xz" `isSuffixOf` fn -> do
|
||||||
filecontents <- liftE $ rf av
|
filecontents <- liftE $ rf av
|
||||||
let decompressed = Lzma.decompress filecontents
|
let decompressed = Lzma.decompress filecontents
|
||||||
liftE $ entries decompressed
|
liftE $ entries decompressed
|
||||||
| ".tar.bz2" `B.isSuffixOf` fn ->
|
| ".tar.bz2" `isSuffixOf` fn ->
|
||||||
liftE (entries . BZip.decompress =<< rf av)
|
liftE (entries . BZip.decompress =<< rf av)
|
||||||
| ".tar" `B.isSuffixOf` fn -> liftE (entries =<< rf av)
|
| ".tar" `isSuffixOf` fn -> liftE (entries =<< rf av)
|
||||||
|
| ".zip" `isSuffixOf` fn ->
|
||||||
|
withArchive av $ do
|
||||||
|
entries' <- getEntries
|
||||||
|
pure $ fmap unEntrySelector $ Map.keys entries'
|
||||||
| otherwise -> throwE $ UnknownArchive fn
|
| otherwise -> throwE $ UnknownArchive fn
|
||||||
|
|
||||||
|
|
||||||
intoSubdir :: (MonadLogger m, MonadIO m, MonadThrow m, MonadCatch m)
|
intoSubdir :: (MonadLogger m, MonadIO m, MonadThrow m, MonadCatch m)
|
||||||
=> Path Abs -- ^ unpacked tar dir
|
=> FilePath -- ^ unpacked tar dir
|
||||||
-> TarDir -- ^ how to descend
|
-> TarDir -- ^ how to descend
|
||||||
-> Excepts '[TarDirDoesNotExist] m (Path Abs)
|
-> Excepts '[TarDirDoesNotExist] m FilePath
|
||||||
intoSubdir bdir tardir = case tardir of
|
intoSubdir bdir tardir = case tardir of
|
||||||
RealDir pr -> do
|
RealDir pr -> do
|
||||||
whenM (fmap not . liftIO . doesDirectoryExist $ (bdir </> pr))
|
whenM (fmap not . liftIO . doesDirectoryExist $ (bdir </> pr))
|
||||||
(throwE $ TarDirDoesNotExist tardir)
|
(throwE $ TarDirDoesNotExist tardir)
|
||||||
pure (bdir </> pr)
|
pure (bdir </> pr)
|
||||||
RegexDir r -> do
|
RegexDir r -> do
|
||||||
let rs = splitOn "/" r
|
let rs = split (`elem` pathSeparators) r
|
||||||
foldlM
|
foldlM
|
||||||
(\y x ->
|
(\y x ->
|
||||||
(handleIO (\_ -> pure []) . liftIO . findFiles y . regex $ x) >>= (\case
|
(handleIO (\_ -> pure []) . liftIO . findFiles y . regex $ x) >>= (\case
|
||||||
@ -743,117 +726,124 @@ getDownloader = ask <&> downloader . settings
|
|||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|
||||||
urlBaseName :: MonadThrow m
|
urlBaseName :: ByteString -- ^ the url path (without scheme and host)
|
||||||
=> ByteString -- ^ the url path (without scheme and host)
|
-> ByteString
|
||||||
-> m (Path Rel)
|
urlBaseName = snd . B.breakEnd (== _slash) . urlDecode False
|
||||||
urlBaseName = parseRel . snd . B.breakEnd (== _slash) . urlDecode False
|
|
||||||
|
|
||||||
|
|
||||||
-- | Get tool files from @~\/.ghcup\/bin\/ghc\/\<ver\>\/bin\/\*@
|
-- | Get tool files from @~\/.ghcup\/bin\/ghc\/\<ver\>\/bin\/\*@
|
||||||
-- while ignoring @*-\<ver\>@ symlinks and accounting for cross triple prefix.
|
-- while ignoring @*-\<ver\>@ symlinks and accounting for cross triple prefix.
|
||||||
--
|
--
|
||||||
-- Returns unversioned relative files, e.g.:
|
-- Returns unversioned relative files without extension, e.g.:
|
||||||
--
|
--
|
||||||
-- - @["hsc2hs","haddock","hpc","runhaskell","ghc","ghc-pkg","ghci","runghc","hp2ps"]@
|
-- - @["hsc2hs","haddock","hpc","runhaskell","ghc","ghc-pkg","ghci","runghc","hp2ps"]@
|
||||||
ghcToolFiles :: (MonadReader AppState m, MonadThrow m, MonadFail m, MonadIO m)
|
ghcToolFiles :: (MonadReader AppState m, MonadThrow m, MonadFail m, MonadIO m)
|
||||||
=> GHCTargetVersion
|
=> GHCTargetVersion
|
||||||
-> Excepts '[NotInstalled] m [Path Rel]
|
-> Excepts '[NotInstalled] m [FilePath]
|
||||||
ghcToolFiles ver = do
|
ghcToolFiles ver = do
|
||||||
ghcdir <- lift $ ghcupGHCDir ver
|
ghcdir <- lift $ ghcupGHCDir ver
|
||||||
let bindir = ghcdir </> [rel|bin|]
|
let bindir = ghcdir </> "bin"
|
||||||
|
|
||||||
-- fail if ghc is not installed
|
-- fail if ghc is not installed
|
||||||
whenM (fmap not $ liftIO $ doesDirectoryExist ghcdir)
|
whenM (fmap not $ liftIO $ doesDirectoryExist ghcdir)
|
||||||
(throwE (NotInstalled GHC ver))
|
(throwE (NotInstalled GHC ver))
|
||||||
|
|
||||||
files <- liftIO $ getDirsFiles' bindir
|
files <- liftIO $ listDirectory bindir
|
||||||
-- figure out the <ver> suffix, because this might not be `Version` for
|
-- figure out the <ver> suffix, because this might not be `Version` for
|
||||||
-- alpha/rc releases, but x.y.a.somedate.
|
-- alpha/rc releases, but x.y.a.somedate.
|
||||||
|
|
||||||
-- for cross, this won't be "ghc", but e.g.
|
ghcIsHadrian <- liftIO $ isHadrian bindir
|
||||||
-- "armv7-unknown-linux-gnueabihf-ghc"
|
onlyUnversioned <- case ghcIsHadrian of
|
||||||
[ghcbin] <- liftIO $ findFiles
|
Right () -> pure id
|
||||||
bindir
|
Left (fmap (dropSuffix exeExt) -> [ghc, ghc_ver])
|
||||||
(makeRegexOpts compExtended
|
| (Just symver) <- stripPrefix (ghc <> "-") ghc_ver
|
||||||
execBlank
|
, not (null symver) -> pure $ filter (\x -> not $ symver `isInfixOf` x)
|
||||||
([s|^([a-zA-Z0-9_-]*[a-zA-Z0-9_]-)?ghc$|] :: ByteString)
|
_ -> fail "Fatal: Could not find internal GHC version"
|
||||||
)
|
|
||||||
|
|
||||||
let ghcbinPath = bindir </> ghcbin
|
pure $ onlyUnversioned $ fmap (dropSuffix exeExt) files
|
||||||
ghcIsHadrian <- liftIO $ isHadrian ghcbinPath
|
|
||||||
onlyUnversioned <- if ghcIsHadrian
|
|
||||||
then pure id
|
|
||||||
else do
|
|
||||||
(Just symver) <-
|
|
||||||
B.stripPrefix (toFilePath ghcbin <> "-") . takeFileName
|
|
||||||
<$> liftIO (readSymbolicLink $ toFilePath ghcbinPath)
|
|
||||||
when (B.null symver)
|
|
||||||
(throwIO $ userError "Fatal: ghc symlink target is broken")
|
|
||||||
pure $ filter (\x -> not $ symver `B.isSuffixOf` toFilePath x)
|
|
||||||
|
|
||||||
pure $ onlyUnversioned files
|
|
||||||
where
|
where
|
||||||
|
isNotAnyInfix xs t = foldr (\a b -> not (a `isInfixOf` t) && b) True xs
|
||||||
-- GHC is moving some builds to Hadrian for bindists,
|
-- GHC is moving some builds to Hadrian for bindists,
|
||||||
-- which doesn't create versioned binaries.
|
-- which doesn't create versioned binaries.
|
||||||
-- https://gitlab.haskell.org/haskell/ghcup-hs/issues/31
|
-- https://gitlab.haskell.org/haskell/ghcup-hs/issues/31
|
||||||
isHadrian :: Path Abs -- ^ ghcbin path
|
isHadrian :: FilePath -- ^ ghcbin path
|
||||||
-> IO Bool
|
-> IO (Either [String] ()) -- ^ Right for Hadrian
|
||||||
isHadrian = fmap (/= SymbolicLink) . getFileType
|
isHadrian dir = do
|
||||||
|
-- Non-hadrian has e.g. ["ghc", "ghc-8.10.4"]
|
||||||
|
-- which also requires us to discover the internal version
|
||||||
|
-- to filter the correct tool files.
|
||||||
|
-- We can't use the symlink on windows, so we fall back to some
|
||||||
|
-- more complicated logic.
|
||||||
|
fs <- fmap
|
||||||
|
-- regex over-matches
|
||||||
|
(filter (isNotAnyInfix ["haddock", "ghc-pkg", "ghci"]))
|
||||||
|
$ liftIO $ findFiles
|
||||||
|
dir
|
||||||
|
(makeRegexOpts compExtended
|
||||||
|
execBlank
|
||||||
|
-- for cross, this won't be "ghc", but e.g.
|
||||||
|
-- "armv7-unknown-linux-gnueabihf-ghc"
|
||||||
|
([s|^([a-zA-Z0-9_-]*[a-zA-Z0-9_]-)?ghc.*$|] :: ByteString)
|
||||||
|
)
|
||||||
|
if | length fs == 1 -> pure $ Right () -- hadrian
|
||||||
|
| length fs == 2 -> pure $ Left
|
||||||
|
(sortOn length fs) -- legacy make, result should
|
||||||
|
-- be ["ghc", "ghc-8.10.4"]
|
||||||
|
| otherwise -> fail "isHadrian failed!"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- | This file, when residing in @~\/.ghcup\/ghc\/\<ver\>\/@ signals that
|
-- | This file, when residing in @~\/.ghcup\/ghc\/\<ver\>\/@ signals that
|
||||||
-- this GHC was built from source. It contains the build config.
|
-- this GHC was built from source. It contains the build config.
|
||||||
ghcUpSrcBuiltFile :: Path Rel
|
ghcUpSrcBuiltFile :: FilePath
|
||||||
ghcUpSrcBuiltFile = [rel|.ghcup_src_built|]
|
ghcUpSrcBuiltFile = ".ghcup_src_built"
|
||||||
|
|
||||||
|
|
||||||
-- | Calls gmake if it exists in PATH, otherwise make.
|
-- | Calls gmake if it exists in PATH, otherwise make.
|
||||||
make :: (MonadThrow m, MonadIO m, MonadReader AppState m)
|
make :: (MonadThrow m, MonadIO m, MonadReader AppState m)
|
||||||
=> [ByteString]
|
=> [String]
|
||||||
-> Maybe (Path Abs)
|
-> Maybe FilePath
|
||||||
-> m (Either ProcessError ())
|
-> m (Either ProcessError ())
|
||||||
make args workdir = do
|
make args workdir = do
|
||||||
spaths <- catMaybes . fmap parseAbs <$> liftIO getSearchPath
|
spaths <- liftIO getSearchPath
|
||||||
has_gmake <- isJust <$> liftIO (searchPath spaths [rel|gmake|])
|
has_gmake <- isJust <$> liftIO (searchPath spaths "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 args workdir "ghc-make" Nothing
|
||||||
|
|
||||||
makeOut :: [ByteString]
|
makeOut :: [String]
|
||||||
-> Maybe (Path Abs)
|
-> Maybe FilePath
|
||||||
-> IO CapturedProcess
|
-> IO CapturedProcess
|
||||||
makeOut args workdir = do
|
makeOut args workdir = do
|
||||||
spaths <- catMaybes . fmap parseAbs <$> liftIO getSearchPath
|
spaths <- liftIO getSearchPath
|
||||||
has_gmake <- isJust <$> liftIO (searchPath spaths [rel|gmake|])
|
has_gmake <- isJust <$> liftIO (searchPath spaths "gmake")
|
||||||
let mymake = if has_gmake then [rel|gmake|] else [rel|make|]
|
let mymake = if has_gmake then "gmake" else "make"
|
||||||
liftIO $ executeOut mymake args workdir
|
liftIO $ executeOut mymake args workdir
|
||||||
|
|
||||||
|
|
||||||
-- | Try to apply patches in order. Fails with 'PatchFailed'
|
-- | Try to apply patches in order. Fails with 'PatchFailed'
|
||||||
-- on first failure.
|
-- on first failure.
|
||||||
applyPatches :: (MonadLogger m, MonadIO m)
|
applyPatches :: (MonadLogger m, MonadIO m)
|
||||||
=> Path Abs -- ^ dir containing patches
|
=> FilePath -- ^ dir containing patches
|
||||||
-> Path Abs -- ^ dir to apply patches in
|
-> FilePath -- ^ dir to apply patches in
|
||||||
-> Excepts '[PatchFailed] m ()
|
-> Excepts '[PatchFailed] m ()
|
||||||
applyPatches pdir ddir = do
|
applyPatches pdir ddir = do
|
||||||
patches <- liftIO $ getDirsFiles pdir
|
patches <- (fmap . fmap) (pdir </>) $ liftIO $ listDirectory pdir
|
||||||
forM_ (sort patches) $ \patch' -> do
|
forM_ (sort patches) $ \patch' -> do
|
||||||
lift $ $(logInfo) [i|Applying patch #{patch'}|]
|
lift $ $(logInfo) [i|Applying patch #{patch'}|]
|
||||||
fmap (either (const Nothing) Just)
|
fmap (either (const Nothing) Just)
|
||||||
(liftIO $ exec
|
(liftIO $ exec
|
||||||
"patch"
|
"patch"
|
||||||
True
|
["-p1", "-i", patch']
|
||||||
["-p1", "-i", toFilePath patch']
|
|
||||||
(Just ddir)
|
(Just ddir)
|
||||||
Nothing)
|
Nothing)
|
||||||
!? PatchFailed
|
!? PatchFailed
|
||||||
|
|
||||||
|
|
||||||
-- | https://gitlab.haskell.org/ghc/ghc/-/issues/17353
|
-- | https://gitlab.haskell.org/ghc/ghc/-/issues/17353
|
||||||
darwinNotarization :: Platform -> Path Abs -> IO (Either ProcessError ())
|
darwinNotarization :: Platform -> FilePath -> IO (Either ProcessError ())
|
||||||
darwinNotarization Darwin path = exec
|
darwinNotarization Darwin path = exec
|
||||||
"xattr"
|
"xattr"
|
||||||
True
|
["-r", "-d", "com.apple.quarantine", path]
|
||||||
["-r", "-d", "com.apple.quarantine", toFilePath path]
|
|
||||||
Nothing
|
Nothing
|
||||||
Nothing
|
Nothing
|
||||||
darwinNotarization _ _ = pure $ Right ()
|
darwinNotarization _ _ = pure $ Right ()
|
||||||
@ -871,19 +861,19 @@ getChangeLog dls tool (Right tag) =
|
|||||||
-- 1. the build directory, depending on the KeepDirs setting
|
-- 1. the build directory, depending on the KeepDirs setting
|
||||||
-- 2. the install destination, depending on whether the build failed
|
-- 2. the install destination, depending on whether the build failed
|
||||||
runBuildAction :: (Show (V e), MonadReader AppState m, MonadIO m, MonadMask m)
|
runBuildAction :: (Show (V e), MonadReader AppState m, MonadIO m, MonadMask m)
|
||||||
=> Path Abs -- ^ build directory (cleaned up depending on Settings)
|
=> FilePath -- ^ build directory (cleaned up depending on Settings)
|
||||||
-> Maybe (Path Abs) -- ^ dir to *always* clean up on exception
|
-> Maybe FilePath -- ^ dir to *always* clean up on exception
|
||||||
-> Excepts e m a
|
-> Excepts e m a
|
||||||
-> Excepts '[BuildFailed] m a
|
-> Excepts '[BuildFailed] m a
|
||||||
runBuildAction bdir instdir action = do
|
runBuildAction bdir instdir action = do
|
||||||
AppState { settings = Settings {..} } <- lift ask
|
AppState { settings = Settings {..} } <- lift ask
|
||||||
let exAction = do
|
let exAction = do
|
||||||
forM_ instdir $ \dir ->
|
forM_ instdir $ \dir ->
|
||||||
liftIO $ hideError doesNotExistErrorType $ deleteDirRecursive dir
|
liftIO $ hideError doesNotExistErrorType $ removeDirectoryRecursive dir
|
||||||
when (keepDirs == Never)
|
when (keepDirs == Never)
|
||||||
$ liftIO
|
$ liftIO
|
||||||
$ hideError doesNotExistErrorType
|
$ hideError doesNotExistErrorType
|
||||||
$ deleteDirRecursive bdir
|
$ removeDirectoryRecursive bdir
|
||||||
v <-
|
v <-
|
||||||
flip onException exAction
|
flip onException exAction
|
||||||
$ catchAllE
|
$ catchAllE
|
||||||
@ -892,32 +882,90 @@ runBuildAction bdir instdir action = do
|
|||||||
throwE (BuildFailed bdir es)
|
throwE (BuildFailed bdir es)
|
||||||
) action
|
) action
|
||||||
|
|
||||||
when (keepDirs == Never || keepDirs == Errors) $ liftIO $ deleteDirRecursive
|
when (keepDirs == Never || keepDirs == Errors) $ liftIO $ removeDirectoryRecursive
|
||||||
bdir
|
bdir
|
||||||
pure v
|
pure v
|
||||||
|
|
||||||
|
|
||||||
-- | More permissive version of 'createDirRecursive'. This doesn't
|
-- | More permissive version of 'createDirRecursive'. This doesn't
|
||||||
-- error when the destination is a symlink to a directory.
|
-- error when the destination is a symlink to a directory.
|
||||||
createDirRecursive' :: Path b -> IO ()
|
createDirRecursive' :: FilePath -> IO ()
|
||||||
createDirRecursive' p =
|
createDirRecursive' p =
|
||||||
handleIO (\e -> if isAlreadyExistsError e then isSymlinkDir e else throwIO e)
|
handleIO (\e -> if isAlreadyExistsError e then isSymlinkDir e else throwIO e)
|
||||||
. createDirRecursive newDirPerms
|
. createDirectoryIfMissing True
|
||||||
$ p
|
$ p
|
||||||
|
|
||||||
where
|
where
|
||||||
isSymlinkDir e = do
|
isSymlinkDir e = do
|
||||||
ft <- getFileType p
|
ft <- pathIsSymbolicLink p
|
||||||
case ft of
|
case ft of
|
||||||
SymbolicLink -> do
|
True -> do
|
||||||
rp <- canonicalizePath p
|
rp <- canonicalizePath p
|
||||||
rft <- getFileType rp
|
rft <- doesDirectoryExist rp
|
||||||
case rft of
|
case rft of
|
||||||
Directory -> pure ()
|
True -> pure ()
|
||||||
_ -> throwIO e
|
_ -> throwIO e
|
||||||
_ -> throwIO e
|
_ -> throwIO e
|
||||||
|
|
||||||
|
|
||||||
|
-- | Recursively copy the contents of one directory to another path.
|
||||||
|
--
|
||||||
|
-- This is a rip-off of Cabal library.
|
||||||
|
copyDirectoryRecursive :: FilePath -> FilePath -> IO ()
|
||||||
|
copyDirectoryRecursive srcDir destDir = do
|
||||||
|
srcFiles <- getDirectoryContentsRecursive srcDir
|
||||||
|
copyFilesWith copyFile destDir [ (srcDir, f)
|
||||||
|
| f <- srcFiles ]
|
||||||
|
where
|
||||||
|
-- | Common implementation of 'copyFiles', 'installOrdinaryFiles',
|
||||||
|
-- 'installExecutableFiles' and 'installMaybeExecutableFiles'.
|
||||||
|
copyFilesWith :: (FilePath -> FilePath -> IO ())
|
||||||
|
-> FilePath -> [(FilePath, FilePath)] -> IO ()
|
||||||
|
copyFilesWith doCopy targetDir srcFiles = do
|
||||||
|
|
||||||
|
-- Create parent directories for everything
|
||||||
|
let dirs = map (targetDir </>) . nub . map (takeDirectory . snd) $ srcFiles
|
||||||
|
traverse_ (createDirectoryIfMissing True) dirs
|
||||||
|
|
||||||
|
-- Copy all the files
|
||||||
|
sequence_ [ let src = srcBase </> srcFile
|
||||||
|
dest = targetDir </> srcFile
|
||||||
|
in doCopy src dest
|
||||||
|
| (srcBase, srcFile) <- srcFiles ]
|
||||||
|
|
||||||
|
-- | List all the files in a directory and all subdirectories.
|
||||||
|
--
|
||||||
|
-- The order places files in sub-directories after all the files in their
|
||||||
|
-- parent directories. The list is generated lazily so is not well defined if
|
||||||
|
-- the source directory structure changes before the list is used.
|
||||||
|
--
|
||||||
|
getDirectoryContentsRecursive :: FilePath -> IO [FilePath]
|
||||||
|
getDirectoryContentsRecursive topdir = recurseDirectories [""]
|
||||||
|
where
|
||||||
|
recurseDirectories :: [FilePath] -> IO [FilePath]
|
||||||
|
recurseDirectories [] = return []
|
||||||
|
recurseDirectories (dir:dirs) = unsafeInterleaveIO $ do
|
||||||
|
(files, dirs') <- collect [] [] =<< getDirectoryContents (topdir </> dir)
|
||||||
|
files' <- recurseDirectories (dirs' ++ dirs)
|
||||||
|
return (files ++ files')
|
||||||
|
|
||||||
|
where
|
||||||
|
collect files dirs' [] = return (reverse files
|
||||||
|
,reverse dirs')
|
||||||
|
collect files dirs' (entry:entries) | ignore entry
|
||||||
|
= collect files dirs' entries
|
||||||
|
collect files dirs' (entry:entries) = do
|
||||||
|
let dirEntry = dir </> entry
|
||||||
|
isDirectory <- doesDirectoryExist (topdir </> dirEntry)
|
||||||
|
if isDirectory
|
||||||
|
then collect files (dirEntry:dirs') entries
|
||||||
|
else collect (dirEntry:files) dirs' entries
|
||||||
|
|
||||||
|
ignore ['.'] = True
|
||||||
|
ignore ['.', '.'] = True
|
||||||
|
ignore _ = False
|
||||||
|
|
||||||
|
|
||||||
getVersionInfo :: Version
|
getVersionInfo :: Version
|
||||||
-> Tool
|
-> Tool
|
||||||
-> GHCupDownloads
|
-> GHCupDownloads
|
||||||
@ -938,3 +986,13 @@ traverseFold f = foldl (\mb a -> (<>) <$> mb <*> f a) (pure mempty)
|
|||||||
-- | Gathering monoidal values
|
-- | Gathering monoidal values
|
||||||
forFold :: (Foldable t, Applicative m, Monoid b) => t a -> (a -> m b) -> m b
|
forFold :: (Foldable t, Applicative m, Monoid b) => t a -> (a -> m b) -> m b
|
||||||
forFold = \t -> (`traverseFold` t)
|
forFold = \t -> (`traverseFold` t)
|
||||||
|
|
||||||
|
|
||||||
|
-- | The file extension for executables.
|
||||||
|
exeExt :: String
|
||||||
|
#if defined(IS_WINDOWS)
|
||||||
|
exeExt = ".exe"
|
||||||
|
#else
|
||||||
|
exeExt = ""
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
{-# LANGUAGE CPP #-}
|
||||||
{-# LANGUAGE DataKinds #-}
|
{-# LANGUAGE DataKinds #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
@ -12,7 +13,7 @@ Copyright : (c) Julian Ospald, 2020
|
|||||||
License : LGPL-3.0
|
License : LGPL-3.0
|
||||||
Maintainer : hasufell@hasufell.de
|
Maintainer : hasufell@hasufell.de
|
||||||
Stability : experimental
|
Stability : experimental
|
||||||
Portability : POSIX
|
Portability : portable
|
||||||
-}
|
-}
|
||||||
module GHCup.Utils.Dirs
|
module GHCup.Utils.Dirs
|
||||||
( getDirs
|
( getDirs
|
||||||
@ -34,7 +35,6 @@ import GHCup.Types.JSON ( )
|
|||||||
import GHCup.Utils.MegaParsec
|
import GHCup.Utils.MegaParsec
|
||||||
import GHCup.Utils.Prelude
|
import GHCup.Utils.Prelude
|
||||||
|
|
||||||
import Control.Applicative
|
|
||||||
import Control.Exception.Safe
|
import Control.Exception.Safe
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
import Control.Monad.IO.Unlift
|
import Control.Monad.IO.Unlift
|
||||||
@ -42,32 +42,20 @@ import Control.Monad.Logger
|
|||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
import Control.Monad.Trans.Resource hiding (throwM)
|
import Control.Monad.Trans.Resource hiding (throwM)
|
||||||
import Data.Bifunctor
|
import Data.Bifunctor
|
||||||
import Data.ByteString ( ByteString )
|
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.String.Interpolate
|
import Data.String.Interpolate
|
||||||
import GHC.IO.Exception ( IOErrorType(NoSuchThing) )
|
import GHC.IO.Exception ( IOErrorType(NoSuchThing) )
|
||||||
import Haskus.Utils.Variant.Excepts
|
import Haskus.Utils.Variant.Excepts
|
||||||
import HPath
|
|
||||||
import HPath.IO
|
|
||||||
import Optics
|
import Optics
|
||||||
import Prelude hiding ( abs
|
import System.Directory
|
||||||
, readFile
|
|
||||||
, writeFile
|
|
||||||
)
|
|
||||||
import System.DiskSpace
|
import System.DiskSpace
|
||||||
import System.Posix.Env.ByteString ( getEnv
|
import System.Environment
|
||||||
, getEnvDefault
|
import System.FilePath
|
||||||
)
|
import System.IO.Temp
|
||||||
import System.Posix.FilePath hiding ( (</>) )
|
|
||||||
import System.Posix.Temp.ByteString ( mkdtemp )
|
|
||||||
|
|
||||||
import qualified Data.ByteString.Lazy as L
|
import qualified Data.ByteString as BS
|
||||||
import qualified Data.ByteString.UTF8 as UTF8
|
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Data.Text.Encoding as E
|
|
||||||
import qualified Data.Yaml as Y
|
import qualified Data.Yaml as Y
|
||||||
import qualified System.Posix.FilePath as FP
|
|
||||||
import qualified System.Posix.User as PU
|
|
||||||
import qualified Text.Megaparsec as MP
|
import qualified Text.Megaparsec as MP
|
||||||
import Control.Concurrent (threadDelay)
|
import Control.Concurrent (threadDelay)
|
||||||
|
|
||||||
@ -82,96 +70,96 @@ import Control.Concurrent (threadDelay)
|
|||||||
--
|
--
|
||||||
-- If 'GHCUP_USE_XDG_DIRS' is set (to anything),
|
-- If 'GHCUP_USE_XDG_DIRS' is set (to anything),
|
||||||
-- then uses 'XDG_DATA_HOME/ghcup' as per xdg spec.
|
-- then uses 'XDG_DATA_HOME/ghcup' as per xdg spec.
|
||||||
ghcupBaseDir :: IO (Path Abs)
|
ghcupBaseDir :: IO FilePath
|
||||||
ghcupBaseDir = do
|
ghcupBaseDir = do
|
||||||
xdg <- useXDG
|
xdg <- useXDG
|
||||||
if xdg
|
if xdg
|
||||||
then do
|
then do
|
||||||
bdir <- getEnv "XDG_DATA_HOME" >>= \case
|
bdir <- lookupEnv "XDG_DATA_HOME" >>= \case
|
||||||
Just r -> parseAbs r
|
Just r -> pure r
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
home <- liftIO getHomeDirectory
|
home <- liftIO getHomeDirectory
|
||||||
pure (home </> [rel|.local/share|])
|
pure (home </> ".local" </> "share")
|
||||||
pure (bdir </> [rel|ghcup|])
|
pure (bdir </> "ghcup")
|
||||||
else do
|
else do
|
||||||
bdir <- getEnv "GHCUP_INSTALL_BASE_PREFIX" >>= \case
|
bdir <- lookupEnv "GHCUP_INSTALL_BASE_PREFIX" >>= \case
|
||||||
Just r -> parseAbs r
|
Just r -> pure r
|
||||||
Nothing -> liftIO getHomeDirectory
|
Nothing -> liftIO getHomeDirectory
|
||||||
pure (bdir </> [rel|.ghcup|])
|
pure (bdir </> ".ghcup")
|
||||||
|
|
||||||
|
|
||||||
-- | ~/.ghcup by default
|
-- | ~/.ghcup by default
|
||||||
--
|
--
|
||||||
-- If 'GHCUP_USE_XDG_DIRS' is set (to anything),
|
-- If 'GHCUP_USE_XDG_DIRS' is set (to anything),
|
||||||
-- then uses 'XDG_CONFIG_HOME/ghcup' as per xdg spec.
|
-- then uses 'XDG_CONFIG_HOME/ghcup' as per xdg spec.
|
||||||
ghcupConfigDir :: IO (Path Abs)
|
ghcupConfigDir :: IO FilePath
|
||||||
ghcupConfigDir = do
|
ghcupConfigDir = do
|
||||||
xdg <- useXDG
|
xdg <- useXDG
|
||||||
if xdg
|
if xdg
|
||||||
then do
|
then do
|
||||||
bdir <- getEnv "XDG_CONFIG_HOME" >>= \case
|
bdir <- lookupEnv "XDG_CONFIG_HOME" >>= \case
|
||||||
Just r -> parseAbs r
|
Just r -> pure r
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
home <- liftIO getHomeDirectory
|
home <- liftIO getHomeDirectory
|
||||||
pure (home </> [rel|.config|])
|
pure (home </> ".config")
|
||||||
pure (bdir </> [rel|ghcup|])
|
pure (bdir </> "ghcup")
|
||||||
else do
|
else do
|
||||||
bdir <- getEnv "GHCUP_INSTALL_BASE_PREFIX" >>= \case
|
bdir <- lookupEnv "GHCUP_INSTALL_BASE_PREFIX" >>= \case
|
||||||
Just r -> parseAbs r
|
Just r -> pure r
|
||||||
Nothing -> liftIO getHomeDirectory
|
Nothing -> liftIO getHomeDirectory
|
||||||
pure (bdir </> [rel|.ghcup|])
|
pure (bdir </> ".ghcup")
|
||||||
|
|
||||||
|
|
||||||
-- | If 'GHCUP_USE_XDG_DIRS' is set (to anything),
|
-- | If 'GHCUP_USE_XDG_DIRS' is set (to anything),
|
||||||
-- then uses 'XDG_BIN_HOME' env var or defaults to '~/.local/bin'
|
-- then uses 'XDG_BIN_HOME' env var or defaults to '~/.local/bin'
|
||||||
-- (which, sadly is not strictly xdg spec).
|
-- (which, sadly is not strictly xdg spec).
|
||||||
ghcupBinDir :: IO (Path Abs)
|
ghcupBinDir :: IO FilePath
|
||||||
ghcupBinDir = do
|
ghcupBinDir = do
|
||||||
xdg <- useXDG
|
xdg <- useXDG
|
||||||
if xdg
|
if xdg
|
||||||
then do
|
then do
|
||||||
getEnv "XDG_BIN_HOME" >>= \case
|
lookupEnv "XDG_BIN_HOME" >>= \case
|
||||||
Just r -> parseAbs r
|
Just r -> pure r
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
home <- liftIO getHomeDirectory
|
home <- liftIO getHomeDirectory
|
||||||
pure (home </> [rel|.local/bin|])
|
pure (home </> ".local" </> "bin")
|
||||||
else ghcupBaseDir <&> (</> [rel|bin|])
|
else ghcupBaseDir <&> (</> "bin")
|
||||||
|
|
||||||
|
|
||||||
-- | Defaults to '~/.ghcup/cache'.
|
-- | Defaults to '~/.ghcup/cache'.
|
||||||
--
|
--
|
||||||
-- If 'GHCUP_USE_XDG_DIRS' is set (to anything),
|
-- If 'GHCUP_USE_XDG_DIRS' is set (to anything),
|
||||||
-- then uses 'XDG_CACHE_HOME/ghcup' as per xdg spec.
|
-- then uses 'XDG_CACHE_HOME/ghcup' as per xdg spec.
|
||||||
ghcupCacheDir :: IO (Path Abs)
|
ghcupCacheDir :: IO FilePath
|
||||||
ghcupCacheDir = do
|
ghcupCacheDir = do
|
||||||
xdg <- useXDG
|
xdg <- useXDG
|
||||||
if xdg
|
if xdg
|
||||||
then do
|
then do
|
||||||
bdir <- getEnv "XDG_CACHE_HOME" >>= \case
|
bdir <- lookupEnv "XDG_CACHE_HOME" >>= \case
|
||||||
Just r -> parseAbs r
|
Just r -> pure r
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
home <- liftIO getHomeDirectory
|
home <- liftIO getHomeDirectory
|
||||||
pure (home </> [rel|.cache|])
|
pure (home </> ".cache")
|
||||||
pure (bdir </> [rel|ghcup|])
|
pure (bdir </> "ghcup")
|
||||||
else ghcupBaseDir <&> (</> [rel|cache|])
|
else ghcupBaseDir <&> (</> "cache")
|
||||||
|
|
||||||
|
|
||||||
-- | Defaults to '~/.ghcup/logs'.
|
-- | Defaults to '~/.ghcup/logs'.
|
||||||
--
|
--
|
||||||
-- If 'GHCUP_USE_XDG_DIRS' is set (to anything),
|
-- If 'GHCUP_USE_XDG_DIRS' is set (to anything),
|
||||||
-- then uses 'XDG_CACHE_HOME/ghcup/logs' as per xdg spec.
|
-- then uses 'XDG_CACHE_HOME/ghcup/logs' as per xdg spec.
|
||||||
ghcupLogsDir :: IO (Path Abs)
|
ghcupLogsDir :: IO FilePath
|
||||||
ghcupLogsDir = do
|
ghcupLogsDir = do
|
||||||
xdg <- useXDG
|
xdg <- useXDG
|
||||||
if xdg
|
if xdg
|
||||||
then do
|
then do
|
||||||
bdir <- getEnv "XDG_CACHE_HOME" >>= \case
|
bdir <- lookupEnv "XDG_CACHE_HOME" >>= \case
|
||||||
Just r -> parseAbs r
|
Just r -> pure r
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
home <- liftIO getHomeDirectory
|
home <- liftIO getHomeDirectory
|
||||||
pure (home </> [rel|.cache|])
|
pure (home </> ".cache")
|
||||||
pure (bdir </> [rel|ghcup/logs|])
|
pure (bdir </> "ghcup" </> "logs")
|
||||||
else ghcupBaseDir <&> (</> [rel|logs|])
|
else ghcupBaseDir <&> (</> "logs")
|
||||||
|
|
||||||
|
|
||||||
getDirs :: IO Dirs
|
getDirs :: IO Dirs
|
||||||
@ -194,11 +182,11 @@ ghcupConfigFile :: (MonadIO m)
|
|||||||
=> Excepts '[JSONError] m UserSettings
|
=> Excepts '[JSONError] m UserSettings
|
||||||
ghcupConfigFile = do
|
ghcupConfigFile = do
|
||||||
confDir <- liftIO ghcupConfigDir
|
confDir <- liftIO ghcupConfigDir
|
||||||
let file = confDir </> [rel|config.yaml|]
|
let file = confDir </> "config.yaml"
|
||||||
bs <- liftIO $ handleIO' NoSuchThing (\_ -> pure Nothing) $ Just <$> readFile file
|
contents <- liftIO $ handleIO' NoSuchThing (\_ -> pure Nothing) $ Just <$> BS.readFile file
|
||||||
case bs of
|
case contents of
|
||||||
Nothing -> pure defaultUserSettings
|
Nothing -> pure defaultUserSettings
|
||||||
Just bs' -> lE' JSONDecodeError . first show . Y.decodeEither' . L.toStrict $ bs'
|
Just contents' -> lE' JSONDecodeError . first show . Y.decodeEither' $ contents'
|
||||||
|
|
||||||
|
|
||||||
-------------------------
|
-------------------------
|
||||||
@ -207,10 +195,10 @@ ghcupConfigFile = do
|
|||||||
|
|
||||||
|
|
||||||
-- | ~/.ghcup/ghc by default.
|
-- | ~/.ghcup/ghc by default.
|
||||||
ghcupGHCBaseDir :: (MonadReader AppState m) => m (Path Abs)
|
ghcupGHCBaseDir :: (MonadReader AppState m) => m FilePath
|
||||||
ghcupGHCBaseDir = do
|
ghcupGHCBaseDir = do
|
||||||
AppState { dirs = Dirs {..} } <- ask
|
AppState { dirs = Dirs {..} } <- ask
|
||||||
pure (baseDir </> [rel|ghc|])
|
pure (baseDir </> "ghc")
|
||||||
|
|
||||||
|
|
||||||
-- | Gets '~/.ghcup/ghc/<ghcupGHCDir>'.
|
-- | Gets '~/.ghcup/ghc/<ghcupGHCDir>'.
|
||||||
@ -219,35 +207,32 @@ ghcupGHCBaseDir = do
|
|||||||
-- * 8.8.4
|
-- * 8.8.4
|
||||||
ghcupGHCDir :: (MonadReader AppState m, MonadThrow m)
|
ghcupGHCDir :: (MonadReader AppState m, MonadThrow m)
|
||||||
=> GHCTargetVersion
|
=> GHCTargetVersion
|
||||||
-> m (Path Abs)
|
-> m FilePath
|
||||||
ghcupGHCDir ver = do
|
ghcupGHCDir ver = do
|
||||||
ghcbasedir <- ghcupGHCBaseDir
|
ghcbasedir <- ghcupGHCBaseDir
|
||||||
verdir <- parseRel $ E.encodeUtf8 (tVerToText ver)
|
let verdir = T.unpack $ tVerToText ver
|
||||||
pure (ghcbasedir </> verdir)
|
pure (ghcbasedir </> verdir)
|
||||||
|
|
||||||
|
|
||||||
-- | See 'ghcupToolParser'.
|
-- | See 'ghcupToolParser'.
|
||||||
parseGHCupGHCDir :: MonadThrow m => Path Rel -> m GHCTargetVersion
|
parseGHCupGHCDir :: MonadThrow m => FilePath -> m GHCTargetVersion
|
||||||
parseGHCupGHCDir (toFilePath -> f) = do
|
parseGHCupGHCDir (T.pack -> fp) =
|
||||||
fp <- throwEither $ E.decodeUtf8' f
|
|
||||||
throwEither $ MP.parse ghcTargetVerP "" fp
|
throwEither $ MP.parse ghcTargetVerP "" fp
|
||||||
|
|
||||||
|
|
||||||
mkGhcupTmpDir :: (MonadUnliftIO m, MonadLogger m, MonadCatch m, MonadThrow m, MonadIO m) => m (Path Abs)
|
mkGhcupTmpDir :: (MonadUnliftIO m, MonadLogger m, MonadCatch m, MonadThrow m, MonadIO m) => m FilePath
|
||||||
mkGhcupTmpDir = do
|
mkGhcupTmpDir = do
|
||||||
tmpdir <- liftIO $ getEnvDefault "TMPDIR" "/tmp"
|
tmpdir <- liftIO getCanonicalTemporaryDirectory
|
||||||
let fp = T.unpack $ decUTF8Safe tmpdir
|
|
||||||
|
|
||||||
let minSpace = 5000 -- a rough guess, aight?
|
let minSpace = 5000 -- a rough guess, aight?
|
||||||
space <- handleIO (\_ -> pure Nothing) $ fmap Just $ liftIO $ getAvailSpace fp
|
space <- handleIO (\_ -> pure Nothing) $ fmap Just $ liftIO $ getAvailSpace tmpdir
|
||||||
when (maybe False (toBytes minSpace >) space) $ do
|
when (maybe False (toBytes minSpace >) space) $ do
|
||||||
$(logWarn) [i|Possibly insufficient disk space on #{fp}. At least #{minSpace} MB are recommended, but only #{toMB (fromJust space)} are free. Consider freeing up disk space or setting TMPDIR env variable.|]
|
$(logWarn) [i|Possibly insufficient disk space on #{tmpdir}. At least #{minSpace} MB are recommended, but only #{toMB (fromJust space)} are free. Consider freeing up disk space or setting TMPDIR env variable.|]
|
||||||
$(logWarn)
|
$(logWarn)
|
||||||
"...waiting for 10 seconds before continuing anyway, you can still abort..."
|
"...waiting for 10 seconds before continuing anyway, you can still abort..."
|
||||||
liftIO $ threadDelay 10000000 -- give the user a sec to intervene
|
liftIO $ threadDelay 10000000 -- give the user a sec to intervene
|
||||||
|
|
||||||
tmp <- liftIO $ mkdtemp (tmpdir FP.</> "ghcup-")
|
liftIO $ createTempDirectory tmpdir "ghcup"
|
||||||
parseAbs tmp
|
|
||||||
where
|
where
|
||||||
toBytes mb = mb * 1024 * 1024
|
toBytes mb = mb * 1024 * 1024
|
||||||
toMB b = show (truncate' (fromIntegral b / (1024 * 1024) :: Double) 2)
|
toMB b = show (truncate' (fromIntegral b / (1024 * 1024) :: Double) 2)
|
||||||
@ -256,8 +241,8 @@ mkGhcupTmpDir = do
|
|||||||
where t = 10^n
|
where t = 10^n
|
||||||
|
|
||||||
|
|
||||||
withGHCupTmpDir :: (MonadUnliftIO m, MonadLogger m, MonadCatch m, MonadResource m, MonadThrow m, MonadIO m) => m (Path Abs)
|
withGHCupTmpDir :: (MonadUnliftIO m, MonadLogger m, MonadCatch m, MonadResource m, MonadThrow m, MonadIO m) => m FilePath
|
||||||
withGHCupTmpDir = snd <$> withRunInIO (\run -> run $ allocate (run mkGhcupTmpDir) deleteDirRecursive)
|
withGHCupTmpDir = snd <$> withRunInIO (\run -> run $ allocate (run mkGhcupTmpDir) removeDirectoryRecursive)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -267,29 +252,19 @@ withGHCupTmpDir = snd <$> withRunInIO (\run -> run $ allocate (run mkGhcupTmpDir
|
|||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
|
||||||
getHomeDirectory :: IO (Path Abs)
|
|
||||||
getHomeDirectory = do
|
|
||||||
e <- getEnv "HOME"
|
|
||||||
case e of
|
|
||||||
Just fp -> parseAbs fp
|
|
||||||
Nothing -> do
|
|
||||||
h <- PU.homeDirectory <$> (PU.getEffectiveUserID >>= PU.getUserEntryForID)
|
|
||||||
parseAbs $ UTF8.fromString h -- this is a guess
|
|
||||||
|
|
||||||
|
|
||||||
useXDG :: IO Bool
|
useXDG :: IO Bool
|
||||||
useXDG = isJust <$> getEnv "GHCUP_USE_XDG_DIRS"
|
useXDG = isJust <$> lookupEnv "GHCUP_USE_XDG_DIRS"
|
||||||
|
|
||||||
|
|
||||||
relativeSymlink :: Path Abs -- ^ the path in which to create the symlink
|
relativeSymlink :: FilePath -- ^ the path in which to create the symlink
|
||||||
-> Path Abs -- ^ the symlink destination
|
-> FilePath -- ^ the symlink destination
|
||||||
-> ByteString
|
-> FilePath
|
||||||
relativeSymlink (toFilePath -> p1) (toFilePath -> p2) =
|
relativeSymlink p1 p2 =
|
||||||
let d1 = splitDirectories p1
|
let d1 = splitDirectories p1
|
||||||
d2 = splitDirectories p2
|
d2 = splitDirectories p2
|
||||||
common = takeWhile (\(x, y) -> x == y) $ zip d1 d2
|
common = takeWhile (\(x, y) -> x == y) $ zip d1 d2
|
||||||
cPrefix = drop (length common) d1
|
cPrefix = drop (length common) d1
|
||||||
in joinPath (replicate (length cPrefix) "..")
|
in joinPath (replicate (length cPrefix) "..")
|
||||||
<> joinPath ("/" : drop (length common) d2)
|
<> joinPath ([pathSeparator] : drop (length common) d2)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,494 +1,17 @@
|
|||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE CPP #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
module GHCup.Utils.File (
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
module GHCup.Utils.File.Common,
|
||||||
{-# LANGUAGE ViewPatterns #-}
|
#if IS_WINDOWS
|
||||||
|
module GHCup.Utils.File.Windows
|
||||||
{-|
|
#else
|
||||||
Module : GHCup.Utils.File
|
module GHCup.Utils.File.Posix
|
||||||
Description : File and unix APIs
|
#endif
|
||||||
Copyright : (c) Julian Ospald, 2020
|
) where
|
||||||
License : LGPL-3.0
|
|
||||||
Maintainer : hasufell@hasufell.de
|
import GHCup.Utils.File.Common
|
||||||
Stability : experimental
|
#if IS_WINDOWS
|
||||||
Portability : POSIX
|
import GHCup.Utils.File.Windows
|
||||||
|
#else
|
||||||
This module handles file and executable handling.
|
import GHCup.Utils.File.Posix
|
||||||
Some of these functions use sophisticated logging.
|
#endif
|
||||||
-}
|
|
||||||
module GHCup.Utils.File where
|
|
||||||
|
|
||||||
import GHCup.Utils.Prelude
|
|
||||||
import GHCup.Types
|
|
||||||
|
|
||||||
import Control.Concurrent
|
|
||||||
import Control.Concurrent.Async
|
|
||||||
import Control.Exception ( evaluate )
|
|
||||||
import Control.Exception.Safe
|
|
||||||
import Control.Monad
|
|
||||||
import Control.Monad.Logger
|
|
||||||
import Control.Monad.Reader
|
|
||||||
import Control.Monad.Trans.State.Strict
|
|
||||||
import Data.ByteString ( ByteString )
|
|
||||||
import Data.Foldable
|
|
||||||
import Data.Functor
|
|
||||||
import Data.IORef
|
|
||||||
import Data.Maybe
|
|
||||||
import Data.Sequence ( Seq, (|>) )
|
|
||||||
import Data.String.Interpolate
|
|
||||||
import Data.Text ( Text )
|
|
||||||
import Data.Void
|
|
||||||
import Data.Word8
|
|
||||||
import GHC.IO.Exception
|
|
||||||
import HPath
|
|
||||||
import HPath.IO hiding ( hideError )
|
|
||||||
import Optics hiding ((<|), (|>))
|
|
||||||
import System.Console.Pretty hiding ( Pretty )
|
|
||||||
import System.Console.Regions
|
|
||||||
import System.IO.Error
|
|
||||||
import System.Posix.Directory.ByteString
|
|
||||||
import System.Posix.FD as FD
|
|
||||||
import System.Posix.FilePath hiding ( (</>) )
|
|
||||||
import System.Posix.Files.ByteString
|
|
||||||
import System.Posix.Foreign ( oExcl, oAppend )
|
|
||||||
import "unix" System.Posix.IO.ByteString
|
|
||||||
hiding ( openFd )
|
|
||||||
import System.Posix.Process ( ProcessStatus(..) )
|
|
||||||
import System.Posix.Types
|
|
||||||
import Text.PrettyPrint.HughesPJClass hiding ( (<>) )
|
|
||||||
import Text.Regex.Posix
|
|
||||||
|
|
||||||
|
|
||||||
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.ByteString
|
|
||||||
as SPPB
|
|
||||||
import Streamly.External.Posix.DirStream
|
|
||||||
import qualified Streamly.Prelude as S
|
|
||||||
import qualified Text.Megaparsec as MP
|
|
||||||
import qualified Data.ByteString as BS
|
|
||||||
import qualified "unix-bytestring" System.Posix.IO.ByteString
|
|
||||||
as SPIB
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
data ProcessError = NonZeroExit Int ByteString [ByteString]
|
|
||||||
| PTerminated ByteString [ByteString]
|
|
||||||
| PStopped ByteString [ByteString]
|
|
||||||
| NoSuchPid ByteString [ByteString]
|
|
||||||
deriving Show
|
|
||||||
|
|
||||||
instance Pretty ProcessError where
|
|
||||||
pPrint (NonZeroExit e exe args) =
|
|
||||||
text [i|Process "#{decUTF8Safe exe}" with arguments #{fmap decUTF8Safe args} failed with exit code #{e}.|]
|
|
||||||
pPrint (PTerminated exe args) =
|
|
||||||
text [i|Process "#{decUTF8Safe exe}" with arguments #{fmap decUTF8Safe args} terminated.|]
|
|
||||||
pPrint (PStopped exe args) =
|
|
||||||
text [i|Process "#{decUTF8Safe exe}" with arguments #{fmap decUTF8Safe args} stopped.|]
|
|
||||||
pPrint (NoSuchPid exe args) =
|
|
||||||
text [i|Could not find PID for process running "#{decUTF8Safe exe}" with arguments #{fmap decUTF8Safe args}.|]
|
|
||||||
|
|
||||||
data CapturedProcess = CapturedProcess
|
|
||||||
{ _exitCode :: ExitCode
|
|
||||||
, _stdOut :: ByteString
|
|
||||||
, _stdErr :: ByteString
|
|
||||||
}
|
|
||||||
deriving (Eq, Show)
|
|
||||||
|
|
||||||
makeLenses ''CapturedProcess
|
|
||||||
|
|
||||||
|
|
||||||
-- | Find the given executable by searching all *absolute* PATH components.
|
|
||||||
-- Relative paths in PATH are ignored.
|
|
||||||
--
|
|
||||||
-- This shouldn't throw IO exceptions, unless getting the environment variable
|
|
||||||
-- PATH does.
|
|
||||||
findExecutable :: Path Rel -> IO (Maybe (Path Abs))
|
|
||||||
findExecutable ex = do
|
|
||||||
sPaths <- fmap (catMaybes . fmap parseAbs) getSearchPath
|
|
||||||
-- We don't want exceptions to mess up our result. If we can't
|
|
||||||
-- figure out if a file exists, then treat it as a negative result.
|
|
||||||
asum $ fmap
|
|
||||||
(handleIO (\_ -> pure Nothing)
|
|
||||||
-- asum for short-circuiting behavior
|
|
||||||
. (\s' -> (isExecutable (s' </> ex) >>= guard) $> Just (s' </> ex))
|
|
||||||
)
|
|
||||||
sPaths
|
|
||||||
|
|
||||||
|
|
||||||
-- | Execute the given command and collect the stdout, stderr and the exit code.
|
|
||||||
-- The command is run in a subprocess.
|
|
||||||
executeOut :: Path b -- ^ command as filename, e.g. 'ls'
|
|
||||||
-> [ByteString] -- ^ arguments to the command
|
|
||||||
-> Maybe (Path Abs) -- ^ chdir to this path
|
|
||||||
-> IO CapturedProcess
|
|
||||||
executeOut path args chdir = captureOutStreams $ do
|
|
||||||
maybe (pure ()) (changeWorkingDirectory . toFilePath) chdir
|
|
||||||
SPPB.executeFile (toFilePath path) True args Nothing
|
|
||||||
|
|
||||||
|
|
||||||
execLogged :: (MonadReader AppState m, MonadIO m, MonadThrow m)
|
|
||||||
=> ByteString -- ^ thing to execute
|
|
||||||
-> Bool -- ^ whether to search PATH for the thing
|
|
||||||
-> [ByteString] -- ^ args for the thing
|
|
||||||
-> Path Rel -- ^ log filename (opened in append mode)
|
|
||||||
-> Maybe (Path Abs) -- ^ optionally chdir into this
|
|
||||||
-> Maybe [(ByteString, ByteString)] -- ^ optional environment
|
|
||||||
-> m (Either ProcessError ())
|
|
||||||
execLogged exe spath args lfile chdir env = do
|
|
||||||
AppState { settings = Settings {..}, dirs = Dirs {..} } <- ask
|
|
||||||
logfile <- (logsDir </>) <$> parseRel (toFilePath lfile <> ".log")
|
|
||||||
liftIO $ bracket (openFd (toFilePath logfile) WriteOnly [oAppend] (Just newFilePerms))
|
|
||||||
closeFd
|
|
||||||
(action verbose)
|
|
||||||
where
|
|
||||||
action verbose fd = do
|
|
||||||
actionWithPipes $ \(stdoutRead, stdoutWrite) -> do
|
|
||||||
-- start the thread that logs to stdout
|
|
||||||
pState <- newEmptyMVar
|
|
||||||
done <- newEmptyMVar
|
|
||||||
void
|
|
||||||
$ forkIO
|
|
||||||
$ EX.handle (\(_ :: IOException) -> pure ())
|
|
||||||
$ EX.finally
|
|
||||||
(if verbose
|
|
||||||
then tee fd stdoutRead
|
|
||||||
else printToRegion fd stdoutRead 6 pState
|
|
||||||
)
|
|
||||||
(putMVar done ())
|
|
||||||
|
|
||||||
-- fork the subprocess
|
|
||||||
pid <- SPPB.forkProcess $ do
|
|
||||||
void $ dupTo stdoutWrite stdOutput
|
|
||||||
void $ dupTo stdoutWrite stdError
|
|
||||||
closeFd stdoutRead
|
|
||||||
closeFd stdoutWrite
|
|
||||||
|
|
||||||
-- execute the action
|
|
||||||
maybe (pure ()) (changeWorkingDirectory . toFilePath) chdir
|
|
||||||
void $ SPPB.executeFile exe spath args env
|
|
||||||
|
|
||||||
closeFd stdoutWrite
|
|
||||||
|
|
||||||
-- wait for the subprocess to finish
|
|
||||||
e <- toProcessError exe args <$!> SPPB.getProcessStatus True True pid
|
|
||||||
putMVar pState (either (const False) (const True) e)
|
|
||||||
|
|
||||||
void $ race (takeMVar done) (threadDelay (1000000 * 3))
|
|
||||||
closeFd stdoutRead
|
|
||||||
|
|
||||||
pure e
|
|
||||||
|
|
||||||
tee :: Fd -> Fd -> IO ()
|
|
||||||
tee fileFd fdIn = readTilEOF 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 -> 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 (forM_ rs (liftIO . closeConsoleRegion))
|
|
||||||
throw ex
|
|
||||||
)
|
|
||||||
$ readTilEOF (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 -> "[ " <> toFilePath 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
|
|
||||||
|
|
||||||
-- 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 = go
|
|
||||||
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
|
|
||||||
-- is run in a subprocess. Stdin is closed. You might want to
|
|
||||||
-- 'race' this to make sure it terminates.
|
|
||||||
captureOutStreams :: IO a
|
|
||||||
-- ^ the action to execute in a subprocess
|
|
||||||
-> IO CapturedProcess
|
|
||||||
captureOutStreams action = do
|
|
||||||
actionWithPipes $ \(parentStdoutRead, childStdoutWrite) ->
|
|
||||||
actionWithPipes $ \(parentStderrRead, childStderrWrite) -> do
|
|
||||||
pid <- SPPB.forkProcess $ do
|
|
||||||
-- dup stdout
|
|
||||||
void $ dupTo childStdoutWrite stdOutput
|
|
||||||
closeFd childStdoutWrite
|
|
||||||
closeFd parentStdoutRead
|
|
||||||
|
|
||||||
-- dup stderr
|
|
||||||
void $ dupTo childStderrWrite stdError
|
|
||||||
closeFd childStderrWrite
|
|
||||||
closeFd parentStderrRead
|
|
||||||
|
|
||||||
-- execute the action
|
|
||||||
a <- action
|
|
||||||
void $ evaluate a
|
|
||||||
|
|
||||||
-- close everything we don't need
|
|
||||||
closeFd childStdoutWrite
|
|
||||||
closeFd childStderrWrite
|
|
||||||
|
|
||||||
-- start thread that writes the output
|
|
||||||
refOut <- newIORef BS.empty
|
|
||||||
refErr <- newIORef BS.empty
|
|
||||||
done <- newEmptyMVar
|
|
||||||
_ <-
|
|
||||||
forkIO
|
|
||||||
$ EX.handle (\(_ :: IOException) -> pure ())
|
|
||||||
$ flip EX.finally (putMVar done ())
|
|
||||||
$ writeStds parentStdoutRead parentStderrRead refOut refErr
|
|
||||||
|
|
||||||
status <- SPPB.getProcessStatus True True pid
|
|
||||||
void $ race (takeMVar done) (threadDelay (1000000 * 3))
|
|
||||||
|
|
||||||
case status of
|
|
||||||
-- readFd will take care of closing the fd
|
|
||||||
Just (SPPB.Exited es) -> do
|
|
||||||
stdout' <- readIORef refOut
|
|
||||||
stderr' <- readIORef refErr
|
|
||||||
pure $ CapturedProcess { _exitCode = es
|
|
||||||
, _stdOut = stdout'
|
|
||||||
, _stdErr = stderr'
|
|
||||||
}
|
|
||||||
|
|
||||||
_ -> throwIO $ userError ("No such PID " ++ show pid)
|
|
||||||
|
|
||||||
where
|
|
||||||
writeStds pout perr rout rerr = do
|
|
||||||
doneOut <- newEmptyMVar
|
|
||||||
void
|
|
||||||
$ forkIO
|
|
||||||
$ hideError eofErrorType
|
|
||||||
$ flip EX.finally (putMVar doneOut ())
|
|
||||||
$ readTilEOF (\x -> modifyIORef' rout (<> x)) pout
|
|
||||||
doneErr <- newEmptyMVar
|
|
||||||
void
|
|
||||||
$ forkIO
|
|
||||||
$ hideError eofErrorType
|
|
||||||
$ flip EX.finally (putMVar doneErr ())
|
|
||||||
$ readTilEOF (\x -> modifyIORef' rerr (<> x)) perr
|
|
||||||
takeMVar doneOut
|
|
||||||
takeMVar doneErr
|
|
||||||
|
|
||||||
readTilEOF ~action' fd' = do
|
|
||||||
bs <- SPIB.fdRead fd' 512
|
|
||||||
void $ action' bs
|
|
||||||
readTilEOF action' fd'
|
|
||||||
|
|
||||||
|
|
||||||
actionWithPipes :: ((Fd, Fd) -> IO b) -> IO b
|
|
||||||
actionWithPipes a =
|
|
||||||
createPipe >>= \(p1, p2) -> flip finally (cleanup [p1, p2]) $ a (p1, p2)
|
|
||||||
|
|
||||||
cleanup :: [Fd] -> IO ()
|
|
||||||
cleanup fds = for_ fds $ \fd -> handleIO (\_ -> pure ()) $ closeFd fd
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- | Create a new regular file in write-only mode. The file must not exist.
|
|
||||||
createRegularFileFd :: FileMode -> Path b -> IO Fd
|
|
||||||
createRegularFileFd fm dest =
|
|
||||||
FD.openFd (toFilePath dest) WriteOnly [oExcl] (Just fm)
|
|
||||||
|
|
||||||
|
|
||||||
-- | Thin wrapper around `executeFile`.
|
|
||||||
exec :: ByteString -- ^ thing to execute
|
|
||||||
-> Bool -- ^ whether to search PATH for the thing
|
|
||||||
-> [ByteString] -- ^ args for the thing
|
|
||||||
-> Maybe (Path Abs) -- ^ optionally chdir into this
|
|
||||||
-> Maybe [(ByteString, ByteString)] -- ^ optional environment
|
|
||||||
-> IO (Either ProcessError ())
|
|
||||||
exec exe spath args chdir env = do
|
|
||||||
pid <- SPPB.forkProcess $ do
|
|
||||||
maybe (pure ()) (changeWorkingDirectory . toFilePath) chdir
|
|
||||||
SPPB.executeFile exe spath args env
|
|
||||||
|
|
||||||
fmap (toProcessError exe args) $ SPPB.getProcessStatus True True pid
|
|
||||||
|
|
||||||
|
|
||||||
toProcessError :: ByteString
|
|
||||||
-> [ByteString]
|
|
||||||
-> Maybe ProcessStatus
|
|
||||||
-> Either ProcessError ()
|
|
||||||
toProcessError exe args mps = case mps of
|
|
||||||
Just (SPPB.Exited (ExitFailure xi)) -> Left $ NonZeroExit xi exe args
|
|
||||||
Just (SPPB.Exited ExitSuccess ) -> Right ()
|
|
||||||
Just (Terminated _ _ ) -> Left $ PTerminated exe args
|
|
||||||
Just (Stopped _ ) -> Left $ PStopped exe args
|
|
||||||
Nothing -> Left $ NoSuchPid exe args
|
|
||||||
|
|
||||||
|
|
||||||
-- | Search for a file in the search paths.
|
|
||||||
--
|
|
||||||
-- Catches `PermissionDenied` and `NoSuchThing` and returns `Nothing`.
|
|
||||||
searchPath :: [Path Abs] -> Path Rel -> IO (Maybe (Path Abs))
|
|
||||||
searchPath paths needle = go paths
|
|
||||||
where
|
|
||||||
go [] = pure Nothing
|
|
||||||
go (x : xs) =
|
|
||||||
hideErrorDefM [InappropriateType, PermissionDenied, NoSuchThing] (go xs)
|
|
||||||
$ do
|
|
||||||
dirStream <- openDirStream (toFilePath x)
|
|
||||||
S.findM (\(_, p) -> isMatch x p) (dirContentsStream dirStream)
|
|
||||||
>>= \case
|
|
||||||
Just _ -> pure $ Just (x </> needle)
|
|
||||||
Nothing -> go xs
|
|
||||||
isMatch basedir p = do
|
|
||||||
if p == toFilePath needle
|
|
||||||
then isExecutable (basedir </> needle)
|
|
||||||
else pure False
|
|
||||||
|
|
||||||
|
|
||||||
-- | Check wether a binary is shadowed by another one that comes before
|
|
||||||
-- it in PATH. Returns the path to said binary, if any.
|
|
||||||
isShadowed :: Path Abs -> IO (Maybe (Path Abs))
|
|
||||||
isShadowed p = do
|
|
||||||
let dir = dirname p
|
|
||||||
fn <- basename p
|
|
||||||
spaths <- catMaybes . fmap parseAbs <$> liftIO getSearchPath
|
|
||||||
if dir `elem` spaths
|
|
||||||
then do
|
|
||||||
let shadowPaths = takeWhile (/= dir) spaths
|
|
||||||
searchPath shadowPaths fn
|
|
||||||
else pure Nothing
|
|
||||||
|
|
||||||
|
|
||||||
-- | Check whether the binary is in PATH. This returns only `True`
|
|
||||||
-- if the directory containing the binary is part of PATH.
|
|
||||||
isInPath :: Path Abs -> IO Bool
|
|
||||||
isInPath p = do
|
|
||||||
let dir = dirname p
|
|
||||||
fn <- basename p
|
|
||||||
spaths <- catMaybes . fmap parseAbs <$> liftIO getSearchPath
|
|
||||||
if dir `elem` spaths
|
|
||||||
then isJust <$> searchPath [dir] fn
|
|
||||||
else pure False
|
|
||||||
|
|
||||||
|
|
||||||
findFiles :: Path Abs -> Regex -> IO [Path Rel]
|
|
||||||
findFiles path regex = do
|
|
||||||
dirStream <- openDirStream (toFilePath path)
|
|
||||||
f <-
|
|
||||||
(fmap . fmap) snd
|
|
||||||
. S.toList
|
|
||||||
. S.filter (\(_, p) -> match regex p)
|
|
||||||
$ dirContentsStream dirStream
|
|
||||||
pure $ parseRel =<< f
|
|
||||||
|
|
||||||
|
|
||||||
findFiles' :: Path Abs -> MP.Parsec Void Text () -> IO [Path Rel]
|
|
||||||
findFiles' path parser = do
|
|
||||||
dirStream <- openDirStream (toFilePath path)
|
|
||||||
f <-
|
|
||||||
(fmap . fmap) snd
|
|
||||||
. S.toList
|
|
||||||
. S.filter (\(_, p) -> case E.decodeUtf8' p of
|
|
||||||
Left _ -> False
|
|
||||||
Right p' -> isJust $ MP.parseMaybe parser p')
|
|
||||||
$ dirContentsStream dirStream
|
|
||||||
pure $ parseRel =<< f
|
|
||||||
|
|
||||||
|
|
||||||
isBrokenSymlink :: Path Abs -> IO Bool
|
|
||||||
isBrokenSymlink p =
|
|
||||||
handleIO
|
|
||||||
(\e -> if ioeGetErrorType e == NoSuchThing then pure True else throwIO e)
|
|
||||||
$ do
|
|
||||||
_ <- canonicalizePath p
|
|
||||||
pure False
|
|
||||||
|
|
||||||
|
|
||||||
chmod_755 :: (MonadLogger m, MonadIO m) => Path a -> m ()
|
|
||||||
chmod_755 (toFilePath -> fp) = do
|
|
||||||
let exe_mode =
|
|
||||||
nullFileMode
|
|
||||||
`unionFileModes` ownerExecuteMode
|
|
||||||
`unionFileModes` ownerReadMode
|
|
||||||
`unionFileModes` ownerWriteMode
|
|
||||||
`unionFileModes` groupExecuteMode
|
|
||||||
`unionFileModes` groupReadMode
|
|
||||||
`unionFileModes` otherExecuteMode
|
|
||||||
`unionFileModes` otherReadMode
|
|
||||||
$(logDebug) [i|chmod 755 #{fp}|]
|
|
||||||
liftIO $ setFileMode fp exe_mode
|
|
||||||
|
122
lib/GHCup/Utils/File/Common.hs
Normal file
122
lib/GHCup/Utils/File/Common.hs
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
{-# LANGUAGE ViewPatterns #-}
|
||||||
|
|
||||||
|
module GHCup.Utils.File.Common where
|
||||||
|
|
||||||
|
import GHCup.Utils.Prelude
|
||||||
|
|
||||||
|
import Control.Exception
|
||||||
|
import Control.Monad.Extra
|
||||||
|
import Control.Monad.Reader
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.String.Interpolate
|
||||||
|
import GHC.IO.Exception
|
||||||
|
import Optics hiding ((<|), (|>))
|
||||||
|
import System.Directory
|
||||||
|
import System.FilePath
|
||||||
|
import System.IO.Error
|
||||||
|
import Text.PrettyPrint.HughesPJClass hiding ( (<>) )
|
||||||
|
import Text.Regex.Posix
|
||||||
|
|
||||||
|
import qualified Data.ByteString.Lazy as BL
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
data ProcessError = NonZeroExit Int FilePath [String]
|
||||||
|
| PTerminated FilePath [String]
|
||||||
|
| PStopped FilePath [String]
|
||||||
|
| NoSuchPid FilePath [String]
|
||||||
|
deriving Show
|
||||||
|
|
||||||
|
instance Pretty ProcessError where
|
||||||
|
pPrint (NonZeroExit e exe args) =
|
||||||
|
text [i|Process "#{exe}" with arguments #{args} failed with exit code #{e}.|]
|
||||||
|
pPrint (PTerminated exe args) =
|
||||||
|
text [i|Process "#{exe}" with arguments #{args} terminated.|]
|
||||||
|
pPrint (PStopped exe args) =
|
||||||
|
text [i|Process "#{exe}" with arguments #{args} stopped.|]
|
||||||
|
pPrint (NoSuchPid exe args) =
|
||||||
|
text [i|Could not find PID for process running "#{exe}" with arguments #{args}.|]
|
||||||
|
|
||||||
|
data CapturedProcess = CapturedProcess
|
||||||
|
{ _exitCode :: ExitCode
|
||||||
|
, _stdOut :: BL.ByteString
|
||||||
|
, _stdErr :: BL.ByteString
|
||||||
|
}
|
||||||
|
deriving (Eq, Show)
|
||||||
|
|
||||||
|
makeLenses ''CapturedProcess
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- | Search for a file in the search paths.
|
||||||
|
--
|
||||||
|
-- Catches `PermissionDenied` and `NoSuchThing` and returns `Nothing`.
|
||||||
|
searchPath :: [FilePath] -> FilePath -> IO (Maybe FilePath)
|
||||||
|
searchPath paths needle = go paths
|
||||||
|
where
|
||||||
|
go [] = pure Nothing
|
||||||
|
go (x : xs) =
|
||||||
|
hideErrorDefM [InappropriateType, PermissionDenied, NoSuchThing] (go xs)
|
||||||
|
$ do
|
||||||
|
contents <- listDirectory x
|
||||||
|
findM (isMatch x) contents >>= \case
|
||||||
|
Just _ -> pure $ Just (x </> needle)
|
||||||
|
Nothing -> go xs
|
||||||
|
isMatch basedir p = do
|
||||||
|
if p == needle
|
||||||
|
then isExecutable (basedir </> needle)
|
||||||
|
else pure False
|
||||||
|
|
||||||
|
isExecutable :: FilePath -> IO Bool
|
||||||
|
isExecutable file = executable <$> getPermissions file
|
||||||
|
|
||||||
|
|
||||||
|
-- | Check wether 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
|
||||||
|
let dir = takeDirectory p
|
||||||
|
let fn = takeFileName p
|
||||||
|
spaths <- liftIO getSearchPath
|
||||||
|
if dir `elem` spaths
|
||||||
|
then do
|
||||||
|
let shadowPaths = takeWhile (/= dir) spaths
|
||||||
|
searchPath shadowPaths fn
|
||||||
|
else pure Nothing
|
||||||
|
|
||||||
|
|
||||||
|
-- | Check whether the binary is in PATH. This returns only `True`
|
||||||
|
-- if the directory containing the binary is part of PATH.
|
||||||
|
isInPath :: FilePath -> IO Bool
|
||||||
|
isInPath p = do
|
||||||
|
let dir = takeDirectory p
|
||||||
|
let fn = takeFileName p
|
||||||
|
spaths <- liftIO getSearchPath
|
||||||
|
if dir `elem` spaths
|
||||||
|
then isJust <$> searchPath [dir] fn
|
||||||
|
else pure False
|
||||||
|
|
||||||
|
|
||||||
|
findFiles :: FilePath -> Regex -> IO [FilePath]
|
||||||
|
findFiles path regex = do
|
||||||
|
contents <- listDirectory path
|
||||||
|
pure $ filter (match regex) contents
|
||||||
|
|
||||||
|
|
||||||
|
isBrokenSymlink :: FilePath -> IO Bool
|
||||||
|
isBrokenSymlink fp = do
|
||||||
|
try (pathIsSymbolicLink fp) >>= \case
|
||||||
|
Right True -> do
|
||||||
|
let symDir = takeDirectory fp
|
||||||
|
tfp <- getSymbolicLinkTarget fp
|
||||||
|
not <$> doesPathExist
|
||||||
|
-- this drops 'symDir' if 'tfp' is absolute
|
||||||
|
(symDir </> tfp)
|
||||||
|
Right b -> pure b
|
||||||
|
Left e | isDoesNotExistError e -> pure False
|
||||||
|
| otherwise -> throwIO e
|
||||||
|
|
368
lib/GHCup/Utils/File/Posix.hs
Normal file
368
lib/GHCup/Utils/File/Posix.hs
Normal file
@ -0,0 +1,368 @@
|
|||||||
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
{-# LANGUAGE ViewPatterns #-}
|
||||||
|
|
||||||
|
{-|
|
||||||
|
Module : GHCup.Utils.File.Posix
|
||||||
|
Description : File and unix APIs
|
||||||
|
Copyright : (c) Julian Ospald, 2020
|
||||||
|
License : LGPL-3.0
|
||||||
|
Maintainer : hasufell@hasufell.de
|
||||||
|
Stability : experimental
|
||||||
|
Portability : POSIX
|
||||||
|
|
||||||
|
This module handles file and executable handling.
|
||||||
|
Some of these functions use sophisticated logging.
|
||||||
|
-}
|
||||||
|
module GHCup.Utils.File.Posix where
|
||||||
|
|
||||||
|
import GHCup.Utils.File.Common
|
||||||
|
import GHCup.Utils.Prelude
|
||||||
|
import GHCup.Types
|
||||||
|
|
||||||
|
import Control.Concurrent
|
||||||
|
import Control.Concurrent.Async
|
||||||
|
import Control.Exception ( evaluate )
|
||||||
|
import Control.Exception.Safe
|
||||||
|
import Control.Monad
|
||||||
|
import Control.Monad.Logger
|
||||||
|
import Control.Monad.Reader
|
||||||
|
import Control.Monad.Trans.State.Strict
|
||||||
|
import Data.ByteString ( ByteString )
|
||||||
|
import Data.Foldable
|
||||||
|
import Data.IORef
|
||||||
|
import Data.Sequence ( Seq, (|>) )
|
||||||
|
import Data.String.Interpolate
|
||||||
|
import Data.List
|
||||||
|
import Data.Word8
|
||||||
|
import GHC.IO.Exception
|
||||||
|
import System.Console.Pretty hiding ( Pretty )
|
||||||
|
import System.Console.Regions
|
||||||
|
import System.IO.Error
|
||||||
|
import System.FilePath
|
||||||
|
import System.Posix.Directory
|
||||||
|
import System.Posix.Files
|
||||||
|
import System.Posix.IO
|
||||||
|
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 Data.ByteString as BS
|
||||||
|
import qualified Data.ByteString.Lazy as BL
|
||||||
|
import qualified "unix-bytestring" System.Posix.IO.ByteString
|
||||||
|
as SPIB
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- | Execute the given command and collect the stdout, stderr and the exit code.
|
||||||
|
-- The command is run in a subprocess.
|
||||||
|
executeOut :: FilePath -- ^ command as filename, e.g. 'ls'
|
||||||
|
-> [String] -- ^ arguments to the command
|
||||||
|
-> Maybe FilePath -- ^ chdir to this path
|
||||||
|
-> IO CapturedProcess
|
||||||
|
executeOut path args chdir = captureOutStreams $ do
|
||||||
|
maybe (pure ()) changeWorkingDirectory chdir
|
||||||
|
SPP.executeFile path True args Nothing
|
||||||
|
|
||||||
|
|
||||||
|
execLogged :: (MonadReader AppState m, MonadIO m, MonadThrow m)
|
||||||
|
=> FilePath -- ^ thing to execute
|
||||||
|
-> [String] -- ^ args for the thing
|
||||||
|
-> Maybe FilePath -- ^ optionally chdir into this
|
||||||
|
-> FilePath -- ^ log filename (opened in append mode)
|
||||||
|
-> Maybe [(String, String)] -- ^ optional environment
|
||||||
|
-> m (Either ProcessError ())
|
||||||
|
execLogged exe args chdir lfile env = do
|
||||||
|
AppState { settings = Settings {..}, dirs = Dirs {..} } <- ask
|
||||||
|
let logfile = logsDir </> lfile <> ".log"
|
||||||
|
liftIO $ bracket (openFd logfile WriteOnly (Just newFilePerms) defaultFileFlags{ append = True })
|
||||||
|
closeFd
|
||||||
|
(action verbose)
|
||||||
|
where
|
||||||
|
action verbose fd = do
|
||||||
|
actionWithPipes $ \(stdoutRead, stdoutWrite) -> do
|
||||||
|
-- start the thread that logs to stdout
|
||||||
|
pState <- newEmptyMVar
|
||||||
|
done <- newEmptyMVar
|
||||||
|
void
|
||||||
|
$ forkIO
|
||||||
|
$ EX.handle (\(_ :: IOException) -> pure ())
|
||||||
|
$ EX.finally
|
||||||
|
(if verbose
|
||||||
|
then tee fd stdoutRead
|
||||||
|
else printToRegion fd stdoutRead 6 pState
|
||||||
|
)
|
||||||
|
(putMVar done ())
|
||||||
|
|
||||||
|
-- fork the subprocess
|
||||||
|
pid <- SPP.forkProcess $ do
|
||||||
|
void $ dupTo stdoutWrite stdOutput
|
||||||
|
void $ dupTo stdoutWrite stdError
|
||||||
|
closeFd stdoutRead
|
||||||
|
closeFd stdoutWrite
|
||||||
|
|
||||||
|
-- execute the action
|
||||||
|
maybe (pure ()) changeWorkingDirectory chdir
|
||||||
|
void $ SPP.executeFile exe (not ("./" `isPrefixOf` exe)) args env
|
||||||
|
|
||||||
|
closeFd stdoutWrite
|
||||||
|
|
||||||
|
-- wait for the subprocess to finish
|
||||||
|
e <- toProcessError exe args <$!> SPP.getProcessStatus True True pid
|
||||||
|
putMVar pState (either (const False) (const True) e)
|
||||||
|
|
||||||
|
void $ race (takeMVar done) (threadDelay (1000000 * 3))
|
||||||
|
closeFd stdoutRead
|
||||||
|
|
||||||
|
pure e
|
||||||
|
|
||||||
|
tee :: Fd -> Fd -> IO ()
|
||||||
|
tee fileFd fdIn = readTilEOF 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 -> 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 (forM_ rs (liftIO . closeConsoleRegion))
|
||||||
|
throw ex
|
||||||
|
)
|
||||||
|
$ readTilEOF (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 -> "[ " <> E.encodeUtf8 (T.pack 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
|
||||||
|
|
||||||
|
-- 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 = go
|
||||||
|
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
|
||||||
|
-- is run in a subprocess. Stdin is closed. You might want to
|
||||||
|
-- 'race' this to make sure it terminates.
|
||||||
|
captureOutStreams :: IO a
|
||||||
|
-- ^ the action to execute in a subprocess
|
||||||
|
-> IO CapturedProcess
|
||||||
|
captureOutStreams action = do
|
||||||
|
actionWithPipes $ \(parentStdoutRead, childStdoutWrite) ->
|
||||||
|
actionWithPipes $ \(parentStderrRead, childStderrWrite) -> do
|
||||||
|
pid <- SPP.forkProcess $ do
|
||||||
|
-- dup stdout
|
||||||
|
void $ dupTo childStdoutWrite stdOutput
|
||||||
|
closeFd childStdoutWrite
|
||||||
|
closeFd parentStdoutRead
|
||||||
|
|
||||||
|
-- dup stderr
|
||||||
|
void $ dupTo childStderrWrite stdError
|
||||||
|
closeFd childStderrWrite
|
||||||
|
closeFd parentStderrRead
|
||||||
|
|
||||||
|
-- execute the action
|
||||||
|
a <- action
|
||||||
|
void $ evaluate a
|
||||||
|
|
||||||
|
-- close everything we don't need
|
||||||
|
closeFd childStdoutWrite
|
||||||
|
closeFd childStderrWrite
|
||||||
|
|
||||||
|
-- start thread that writes the output
|
||||||
|
refOut <- newIORef BL.empty
|
||||||
|
refErr <- newIORef BL.empty
|
||||||
|
done <- newEmptyMVar
|
||||||
|
_ <-
|
||||||
|
forkIO
|
||||||
|
$ EX.handle (\(_ :: IOException) -> pure ())
|
||||||
|
$ flip EX.finally (putMVar done ())
|
||||||
|
$ writeStds parentStdoutRead parentStderrRead refOut refErr
|
||||||
|
|
||||||
|
status <- SPP.getProcessStatus True True pid
|
||||||
|
void $ race (takeMVar done) (threadDelay (1000000 * 3))
|
||||||
|
|
||||||
|
case status of
|
||||||
|
-- readFd will take care of closing the fd
|
||||||
|
Just (SPP.Exited es) -> do
|
||||||
|
stdout' <- readIORef refOut
|
||||||
|
stderr' <- readIORef refErr
|
||||||
|
pure $ CapturedProcess { _exitCode = es
|
||||||
|
, _stdOut = stdout'
|
||||||
|
, _stdErr = stderr'
|
||||||
|
}
|
||||||
|
|
||||||
|
_ -> throwIO $ userError ("No such PID " ++ show pid)
|
||||||
|
|
||||||
|
where
|
||||||
|
writeStds :: Fd -> Fd -> IORef BL.ByteString -> IORef BL.ByteString -> IO ()
|
||||||
|
writeStds pout perr rout rerr = do
|
||||||
|
doneOut <- newEmptyMVar
|
||||||
|
void
|
||||||
|
$ forkIO
|
||||||
|
$ hideError eofErrorType
|
||||||
|
$ flip EX.finally (putMVar doneOut ())
|
||||||
|
$ readTilEOF (\x -> modifyIORef' rout (<> BL.fromStrict x)) pout
|
||||||
|
doneErr <- newEmptyMVar
|
||||||
|
void
|
||||||
|
$ forkIO
|
||||||
|
$ hideError eofErrorType
|
||||||
|
$ flip EX.finally (putMVar doneErr ())
|
||||||
|
$ readTilEOF (\x -> modifyIORef' rerr (<> BL.fromStrict x)) perr
|
||||||
|
takeMVar doneOut
|
||||||
|
takeMVar doneErr
|
||||||
|
|
||||||
|
readTilEOF ~action' fd' = do
|
||||||
|
bs <- SPIB.fdRead fd' 512
|
||||||
|
void $ action' bs
|
||||||
|
readTilEOF action' fd'
|
||||||
|
|
||||||
|
|
||||||
|
actionWithPipes :: ((Fd, Fd) -> IO b) -> IO b
|
||||||
|
actionWithPipes a =
|
||||||
|
createPipe >>= \(p1, p2) -> flip finally (cleanup [p1, p2]) $ a (p1, p2)
|
||||||
|
|
||||||
|
cleanup :: [Fd] -> IO ()
|
||||||
|
cleanup fds = for_ fds $ \fd -> handleIO (\_ -> pure ()) $ closeFd fd
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- | Create a new regular file in write-only mode. The file must not exist.
|
||||||
|
createRegularFileFd :: FileMode -> FilePath -> IO Fd
|
||||||
|
createRegularFileFd fm dest =
|
||||||
|
openFd dest WriteOnly (Just fm) defaultFileFlags{ exclusive = True }
|
||||||
|
|
||||||
|
|
||||||
|
-- | Thin wrapper around `executeFile`.
|
||||||
|
exec :: String -- ^ thing to execute
|
||||||
|
-> [String] -- ^ args for the thing
|
||||||
|
-> Maybe FilePath -- ^ optionally chdir into this
|
||||||
|
-> Maybe [(String, String)] -- ^ optional environment
|
||||||
|
-> IO (Either ProcessError ())
|
||||||
|
exec exe args chdir env = do
|
||||||
|
pid <- SPP.forkProcess $ do
|
||||||
|
maybe (pure ()) changeWorkingDirectory chdir
|
||||||
|
SPP.executeFile exe (not ("./" `isPrefixOf` exe)) args env
|
||||||
|
|
||||||
|
fmap (toProcessError exe args) $ SPP.getProcessStatus True True pid
|
||||||
|
|
||||||
|
|
||||||
|
toProcessError :: FilePath
|
||||||
|
-> [String]
|
||||||
|
-> Maybe ProcessStatus
|
||||||
|
-> 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 (Terminated _ _ ) -> Left $ PTerminated exe args
|
||||||
|
Just (Stopped _ ) -> Left $ PStopped exe args
|
||||||
|
Nothing -> Left $ NoSuchPid exe args
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
chmod_755 :: (MonadLogger m, MonadIO m) => FilePath -> m ()
|
||||||
|
chmod_755 fp = do
|
||||||
|
let exe_mode =
|
||||||
|
nullFileMode
|
||||||
|
`unionFileModes` ownerExecuteMode
|
||||||
|
`unionFileModes` ownerReadMode
|
||||||
|
`unionFileModes` ownerWriteMode
|
||||||
|
`unionFileModes` groupExecuteMode
|
||||||
|
`unionFileModes` groupReadMode
|
||||||
|
`unionFileModes` otherExecuteMode
|
||||||
|
`unionFileModes` otherReadMode
|
||||||
|
$(logDebug) [i|chmod 755 #{fp}|]
|
||||||
|
liftIO $ setFileMode fp exe_mode
|
||||||
|
|
||||||
|
|
||||||
|
-- |Default permissions for a new file.
|
||||||
|
newFilePerms :: FileMode
|
||||||
|
newFilePerms =
|
||||||
|
ownerWriteMode
|
||||||
|
`unionFileModes` ownerReadMode
|
||||||
|
`unionFileModes` groupWriteMode
|
||||||
|
`unionFileModes` groupReadMode
|
||||||
|
`unionFileModes` otherWriteMode
|
||||||
|
`unionFileModes` otherReadMode
|
202
lib/GHCup/Utils/File/Windows.hs
Normal file
202
lib/GHCup/Utils/File/Windows.hs
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
|
||||||
|
{-|
|
||||||
|
Module : GHCup.Utils.File.Windows
|
||||||
|
Description : File and windows APIs
|
||||||
|
Copyright : (c) Julian Ospald, 2020
|
||||||
|
License : LGPL-3.0
|
||||||
|
Maintainer : hasufell@hasufell.de
|
||||||
|
Stability : experimental
|
||||||
|
Portability : Windows
|
||||||
|
|
||||||
|
This module handles file and executable handling.
|
||||||
|
Some of these functions use sophisticated logging.
|
||||||
|
-}
|
||||||
|
module GHCup.Utils.File.Windows where
|
||||||
|
|
||||||
|
import GHCup.Utils.File.Common
|
||||||
|
import GHCup.Types
|
||||||
|
|
||||||
|
import Control.Concurrent
|
||||||
|
import Control.DeepSeq
|
||||||
|
import Control.Exception.Safe
|
||||||
|
import Control.Monad
|
||||||
|
import Control.Monad.Reader
|
||||||
|
import Foreign.C.Error
|
||||||
|
import GHC.IO.Exception
|
||||||
|
import GHC.IO.Handle
|
||||||
|
import System.Directory
|
||||||
|
import System.FilePath
|
||||||
|
import System.IO
|
||||||
|
import System.Process
|
||||||
|
|
||||||
|
import qualified Control.Exception as EX
|
||||||
|
import qualified Data.ByteString as BS
|
||||||
|
import qualified Data.ByteString.Lazy as BL
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
toProcessError :: FilePath
|
||||||
|
-> [FilePath]
|
||||||
|
-> ExitCode
|
||||||
|
-> Either ProcessError ()
|
||||||
|
toProcessError exe args exitcode = case exitcode of
|
||||||
|
(ExitFailure xi) -> Left $ NonZeroExit xi exe args
|
||||||
|
ExitSuccess -> Right ()
|
||||||
|
|
||||||
|
|
||||||
|
-- | @readCreateProcessWithExitCode@ works exactly like 'readProcessWithExitCode' except that it
|
||||||
|
-- lets you pass 'CreateProcess' giving better flexibility.
|
||||||
|
--
|
||||||
|
-- Note that @Handle@s provided for @std_in@, @std_out@, or @std_err@ via the CreateProcess
|
||||||
|
-- record will be ignored.
|
||||||
|
--
|
||||||
|
-- @since 1.2.3.0
|
||||||
|
readCreateProcessWithExitCodeBS
|
||||||
|
:: CreateProcess
|
||||||
|
-> BL.ByteString
|
||||||
|
-> IO (ExitCode, BL.ByteString, BL.ByteString) -- ^ exitcode, stdout, stderr
|
||||||
|
readCreateProcessWithExitCodeBS cp input = do
|
||||||
|
let cp_opts = cp {
|
||||||
|
std_in = CreatePipe,
|
||||||
|
std_out = CreatePipe,
|
||||||
|
std_err = CreatePipe
|
||||||
|
}
|
||||||
|
withCreateProcess_ "readCreateProcessWithExitCode" cp_opts $
|
||||||
|
\mb_inh mb_outh mb_errh ph ->
|
||||||
|
case (mb_inh, mb_outh, mb_errh) of
|
||||||
|
(Just inh, Just outh, Just errh) -> do
|
||||||
|
|
||||||
|
out <- BS.hGetContents outh
|
||||||
|
err <- BS.hGetContents errh
|
||||||
|
|
||||||
|
-- fork off threads to start consuming stdout & stderr
|
||||||
|
withForkWait (EX.evaluate $ rnf out) $ \waitOut ->
|
||||||
|
withForkWait (EX.evaluate $ rnf err) $ \waitErr -> do
|
||||||
|
|
||||||
|
-- now write any input
|
||||||
|
unless (BL.null input) $
|
||||||
|
ignoreSigPipe $ BL.hPut inh input
|
||||||
|
-- hClose performs implicit hFlush, and thus may trigger a SIGPIPE
|
||||||
|
ignoreSigPipe $ hClose inh
|
||||||
|
|
||||||
|
-- wait on the output
|
||||||
|
waitOut
|
||||||
|
waitErr
|
||||||
|
|
||||||
|
hClose outh
|
||||||
|
hClose errh
|
||||||
|
|
||||||
|
-- wait on the process
|
||||||
|
ex <- waitForProcess ph
|
||||||
|
return (ex, BL.fromStrict out, BL.fromStrict err)
|
||||||
|
|
||||||
|
(Nothing,_,_) -> error "readCreateProcessWithExitCodeBS: Failed to get a stdin handle."
|
||||||
|
(_,Nothing,_) -> error "readCreateProcessWithExitCodeBS: Failed to get a stdout handle."
|
||||||
|
(_,_,Nothing) -> error "readCreateProcessWithExitCodeBS: Failed to get a stderr handle."
|
||||||
|
where
|
||||||
|
ignoreSigPipe :: IO () -> IO ()
|
||||||
|
ignoreSigPipe = EX.handle $ \e -> case e of
|
||||||
|
IOError { ioe_type = ResourceVanished
|
||||||
|
, ioe_errno = Just ioe }
|
||||||
|
| Errno ioe == ePIPE -> return ()
|
||||||
|
_ -> throwIO e
|
||||||
|
-- wrapper so we can get exceptions with the appropriate function name.
|
||||||
|
withCreateProcess_
|
||||||
|
:: String
|
||||||
|
-> CreateProcess
|
||||||
|
-> (Maybe Handle -> Maybe Handle -> Maybe Handle -> ProcessHandle -> IO a)
|
||||||
|
-> IO a
|
||||||
|
withCreateProcess_ fun c action =
|
||||||
|
EX.bracketOnError (createProcess_ fun c) cleanupProcess
|
||||||
|
(\(m_in, m_out, m_err, ph) -> action m_in m_out m_err ph)
|
||||||
|
|
||||||
|
-- | Fork a thread while doing something else, but kill it if there's an
|
||||||
|
-- exception.
|
||||||
|
--
|
||||||
|
-- This is important in the cases above because we want to kill the thread
|
||||||
|
-- that is holding the Handle lock, because when we clean up the process we
|
||||||
|
-- try to close that handle, which could otherwise deadlock.
|
||||||
|
--
|
||||||
|
withForkWait :: IO () -> (IO () -> IO a) -> IO a
|
||||||
|
withForkWait async' body = do
|
||||||
|
waitVar <- newEmptyMVar :: IO (MVar (Either SomeException ()))
|
||||||
|
mask $ \restore -> do
|
||||||
|
tid <- forkIO $ try (restore async') >>= putMVar waitVar
|
||||||
|
let wait' = takeMVar waitVar >>= either throwIO return
|
||||||
|
restore (body wait') `EX.onException` killThread tid
|
||||||
|
|
||||||
|
|
||||||
|
-- | Execute the given command and collect the stdout, stderr and the exit code.
|
||||||
|
-- The command is run in a subprocess.
|
||||||
|
executeOut :: FilePath -- ^ command as filename, e.g. 'ls'
|
||||||
|
-> [String] -- ^ arguments to the command
|
||||||
|
-> Maybe FilePath -- ^ chdir to this path
|
||||||
|
-> IO CapturedProcess
|
||||||
|
executeOut path args chdir = do
|
||||||
|
(exit, out, err) <- readCreateProcessWithExitCodeBS (proc path args){ cwd = chdir } ""
|
||||||
|
pure $ CapturedProcess exit out err
|
||||||
|
|
||||||
|
|
||||||
|
execLogged :: (MonadReader AppState m, MonadIO m, MonadThrow m)
|
||||||
|
=> FilePath -- ^ thing to execute
|
||||||
|
-> [String] -- ^ args for the thing
|
||||||
|
-> Maybe FilePath -- ^ optionally chdir into this
|
||||||
|
-> FilePath -- ^ log filename (opened in append mode)
|
||||||
|
-> Maybe [(String, String)] -- ^ optional environment
|
||||||
|
-> m (Either ProcessError ())
|
||||||
|
execLogged exe args chdir lfile env = do
|
||||||
|
AppState { dirs = Dirs {..} } <- ask
|
||||||
|
let stdoutLogfile = logsDir </> lfile <> ".stdout.log"
|
||||||
|
stderrLogfile = logsDir </> lfile <> ".stderr.log"
|
||||||
|
fmap (toProcessError exe args)
|
||||||
|
$ liftIO
|
||||||
|
$ withCreateProcess
|
||||||
|
(proc exe args){ cwd = chdir
|
||||||
|
, env = env
|
||||||
|
, std_in = CreatePipe
|
||||||
|
, std_out = CreatePipe
|
||||||
|
, std_err = CreatePipe
|
||||||
|
}
|
||||||
|
$ \_ mout merr ph ->
|
||||||
|
case (mout, merr) of
|
||||||
|
(Just cStdout, Just cStderr) -> do
|
||||||
|
withForkWait (tee stdoutLogfile cStdout) $ \waitOut ->
|
||||||
|
withForkWait (tee stderrLogfile cStderr) $ \waitErr -> do
|
||||||
|
waitOut
|
||||||
|
waitErr
|
||||||
|
waitForProcess ph
|
||||||
|
_ -> fail "Could not acquire out/err handle"
|
||||||
|
|
||||||
|
where
|
||||||
|
tee :: FilePath -> Handle -> IO ()
|
||||||
|
tee logFile handle' = go
|
||||||
|
where
|
||||||
|
go = do
|
||||||
|
some <- BS.hGetSome handle' 512
|
||||||
|
if BS.null some
|
||||||
|
then pure ()
|
||||||
|
else do
|
||||||
|
void $ BS.appendFile logFile some
|
||||||
|
void $ BS.hPut stdout some
|
||||||
|
go
|
||||||
|
|
||||||
|
|
||||||
|
-- | Thin wrapper around `executeFile`.
|
||||||
|
exec :: FilePath -- ^ thing to execute
|
||||||
|
-> [FilePath] -- ^ args for the thing
|
||||||
|
-> Maybe FilePath -- ^ optionally chdir into this
|
||||||
|
-> Maybe [(String, String)] -- ^ optional environment
|
||||||
|
-> IO (Either ProcessError ())
|
||||||
|
exec exe args chdir env = do
|
||||||
|
exit_code <- withCreateProcess
|
||||||
|
(proc exe args) { cwd = chdir, env = env } $ \_ _ _ p ->
|
||||||
|
waitForProcess p
|
||||||
|
pure $ toProcessError exe args exit_code
|
||||||
|
|
||||||
|
|
||||||
|
chmod_755 :: MonadIO m => FilePath -> m ()
|
||||||
|
chmod_755 fp =
|
||||||
|
let perm = setOwnerWritable True emptyPermissions
|
||||||
|
in liftIO $ setPermissions fp perm
|
@ -8,14 +8,13 @@ Copyright : (c) Julian Ospald, 2020
|
|||||||
License : LGPL-3.0
|
License : LGPL-3.0
|
||||||
Maintainer : hasufell@hasufell.de
|
Maintainer : hasufell@hasufell.de
|
||||||
Stability : experimental
|
Stability : experimental
|
||||||
Portability : POSIX
|
Portability : portable
|
||||||
|
|
||||||
Here we define our main logger.
|
Here we define our main logger.
|
||||||
-}
|
-}
|
||||||
module GHCup.Utils.Logger where
|
module GHCup.Utils.Logger where
|
||||||
|
|
||||||
import GHCup.Types
|
import GHCup.Types
|
||||||
import GHCup.Utils
|
|
||||||
import GHCup.Utils.File
|
import GHCup.Utils.File
|
||||||
import GHCup.Utils.String.QQ
|
import GHCup.Utils.String.QQ
|
||||||
|
|
||||||
@ -23,14 +22,15 @@ import Control.Monad
|
|||||||
import Control.Monad.IO.Class
|
import Control.Monad.IO.Class
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
import Control.Monad.Logger
|
import Control.Monad.Logger
|
||||||
import HPath
|
|
||||||
import HPath.IO
|
|
||||||
import Prelude hiding ( appendFile )
|
import Prelude hiding ( appendFile )
|
||||||
import System.Console.Pretty
|
import System.Console.Pretty
|
||||||
|
import System.Directory hiding ( findFiles )
|
||||||
|
import System.FilePath
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
import Text.Regex.Posix
|
import Text.Regex.Posix
|
||||||
|
|
||||||
import qualified Data.ByteString as B
|
import qualified Data.ByteString as B
|
||||||
|
import GHCup.Utils.Prelude
|
||||||
|
|
||||||
|
|
||||||
data LoggerConfig = LoggerConfig
|
data LoggerConfig = LoggerConfig
|
||||||
@ -68,19 +68,19 @@ myLoggerT LoggerConfig {..} loggingt = runLoggingT loggingt mylogger
|
|||||||
rawOutter outr
|
rawOutter outr
|
||||||
|
|
||||||
|
|
||||||
initGHCupFileLogging :: (MonadIO m, MonadReader AppState m) => m (Path Abs)
|
initGHCupFileLogging :: (MonadIO m, MonadReader AppState m) => m FilePath
|
||||||
initGHCupFileLogging = do
|
initGHCupFileLogging = do
|
||||||
AppState {dirs = Dirs {..}} <- ask
|
AppState {dirs = Dirs {..}} <- ask
|
||||||
let logfile = logsDir </> [rel|ghcup.log|]
|
let logfile = logsDir </> "ghcup.log"
|
||||||
liftIO $ do
|
liftIO $ do
|
||||||
createDirRecursive' logsDir
|
createDirectoryIfMissing True logsDir
|
||||||
logFiles <- findFiles
|
logFiles <- findFiles
|
||||||
logsDir
|
logsDir
|
||||||
(makeRegexOpts compExtended
|
(makeRegexOpts compExtended
|
||||||
execBlank
|
execBlank
|
||||||
([s|^.*\.log$|] :: B.ByteString)
|
([s|^.*\.log$|] :: B.ByteString)
|
||||||
)
|
)
|
||||||
forM_ logFiles $ hideError doesNotExistErrorType . deleteFile . (logsDir </>)
|
forM_ logFiles $ hideError doesNotExistErrorType . removeFile . (logsDir </>)
|
||||||
|
|
||||||
createRegularFile newFilePerms logfile
|
writeFile logfile ""
|
||||||
pure logfile
|
pure logfile
|
||||||
|
@ -8,7 +8,7 @@ Copyright : (c) Julian Ospald, 2020
|
|||||||
License : LGPL-3.0
|
License : LGPL-3.0
|
||||||
Maintainer : hasufell@hasufell.de
|
Maintainer : hasufell@hasufell.de
|
||||||
Stability : experimental
|
Stability : experimental
|
||||||
Portability : POSIX
|
Portability : portable
|
||||||
-}
|
-}
|
||||||
module GHCup.Utils.MegaParsec where
|
module GHCup.Utils.MegaParsec where
|
||||||
|
|
||||||
@ -23,6 +23,7 @@ import Data.Maybe
|
|||||||
import Data.Text ( Text )
|
import Data.Text ( Text )
|
||||||
import Data.Versions
|
import Data.Versions
|
||||||
import Data.Void
|
import Data.Void
|
||||||
|
import System.FilePath
|
||||||
|
|
||||||
import qualified Data.List.NonEmpty as NE
|
import qualified Data.List.NonEmpty as NE
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
@ -117,3 +118,7 @@ verP suffix = do
|
|||||||
v <- versioning'
|
v <- versioning'
|
||||||
MP.setInput rest
|
MP.setInput rest
|
||||||
pure v
|
pure v
|
||||||
|
|
||||||
|
|
||||||
|
pathSep :: MP.Parsec Void Text Char
|
||||||
|
pathSep = MP.oneOf pathSeparators
|
||||||
|
@ -12,7 +12,7 @@ Copyright : (c) Julian Ospald, 2020
|
|||||||
License : LGPL-3.0
|
License : LGPL-3.0
|
||||||
Maintainer : hasufell@hasufell.de
|
Maintainer : hasufell@hasufell.de
|
||||||
Stability : experimental
|
Stability : experimental
|
||||||
Portability : POSIX
|
Portability : portable
|
||||||
|
|
||||||
GHCup specific prelude. Lots of Excepts functionality.
|
GHCup specific prelude. Lots of Excepts functionality.
|
||||||
-}
|
-}
|
||||||
@ -32,8 +32,6 @@ import Data.Word8
|
|||||||
import Haskus.Utils.Types.List
|
import Haskus.Utils.Types.List
|
||||||
import Haskus.Utils.Variant.Excepts
|
import Haskus.Utils.Variant.Excepts
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
import System.Posix.Env.ByteString ( getEnvironment )
|
|
||||||
|
|
||||||
import qualified Data.ByteString as B
|
import qualified Data.ByteString as B
|
||||||
import qualified Data.ByteString.Lazy as L
|
import qualified Data.ByteString.Lazy as L
|
||||||
import qualified Data.Strict.Maybe as S
|
import qualified Data.Strict.Maybe as S
|
||||||
@ -242,6 +240,8 @@ throwEither' e eth = case eth of
|
|||||||
verToBS :: Version -> ByteString
|
verToBS :: Version -> ByteString
|
||||||
verToBS = E.encodeUtf8 . prettyVer
|
verToBS = E.encodeUtf8 . prettyVer
|
||||||
|
|
||||||
|
verToS :: Version -> String
|
||||||
|
verToS = T.unpack . prettyVer
|
||||||
|
|
||||||
intToText :: Integral a => a -> T.Text
|
intToText :: Integral a => a -> T.Text
|
||||||
intToText = TL.toStrict . B.toLazyText . B.decimal
|
intToText = TL.toStrict . B.toLazyText . B.decimal
|
||||||
@ -252,14 +252,6 @@ removeLensFieldLabel str' =
|
|||||||
maybe str' T.unpack . T.stripPrefix (T.pack "_") . T.pack $ str'
|
maybe str' T.unpack . T.stripPrefix (T.pack "_") . T.pack $ str'
|
||||||
|
|
||||||
|
|
||||||
addToCurrentEnv :: MonadIO m
|
|
||||||
=> [(ByteString, ByteString)]
|
|
||||||
-> m [(ByteString, ByteString)]
|
|
||||||
addToCurrentEnv adds = do
|
|
||||||
cEnv <- liftIO getEnvironment
|
|
||||||
pure (adds ++ cEnv)
|
|
||||||
|
|
||||||
|
|
||||||
pvpToVersion :: PVP -> Version
|
pvpToVersion :: PVP -> Version
|
||||||
pvpToVersion =
|
pvpToVersion =
|
||||||
either (\_ -> error "Couldn't convert PVP to Version") id
|
either (\_ -> error "Couldn't convert PVP to Version") id
|
||||||
|
@ -7,7 +7,7 @@ Copyright : (c) Audrey Tang <audreyt@audreyt.org> 2019, Julian Ospald <hasufel
|
|||||||
License : LGPL-3.0
|
License : LGPL-3.0
|
||||||
Maintainer : hasufell@hasufell.de
|
Maintainer : hasufell@hasufell.de
|
||||||
Stability : experimental
|
Stability : experimental
|
||||||
Portability : POSIX
|
Portability : portable
|
||||||
|
|
||||||
QuasiQuoter for non-interpolated strings, texts and bytestrings.
|
QuasiQuoter for non-interpolated strings, texts and bytestrings.
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ Copyright : (c) Julian Ospald, 2020
|
|||||||
License : LGPL-3.0
|
License : LGPL-3.0
|
||||||
Maintainer : hasufell@hasufell.de
|
Maintainer : hasufell@hasufell.de
|
||||||
Stability : experimental
|
Stability : experimental
|
||||||
Portability : POSIX
|
Portability : portable
|
||||||
-}
|
-}
|
||||||
module GHCup.Utils.Version.QQ where
|
module GHCup.Utils.Version.QQ where
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ Copyright : (c) Julian Ospald, 2020
|
|||||||
License : LGPL-3.0
|
License : LGPL-3.0
|
||||||
Maintainer : hasufell@hasufell.de
|
Maintainer : hasufell@hasufell.de
|
||||||
Stability : experimental
|
Stability : experimental
|
||||||
Portability : POSIX
|
Portability : portable
|
||||||
-}
|
-}
|
||||||
module GHCup.Version where
|
module GHCup.Version where
|
||||||
|
|
||||||
|
26
stack.yaml
26
stack.yaml
@ -1,4 +1,4 @@
|
|||||||
resolver: lts-17.4
|
resolver: lts-17.11
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
- .
|
- .
|
||||||
@ -7,6 +7,9 @@ extra-deps:
|
|||||||
- git: https://github.com/hasufell/text-conversions.git
|
- git: https://github.com/hasufell/text-conversions.git
|
||||||
commit: 9abf0e5e5664a3178367597c32db19880477a53c
|
commit: 9abf0e5e5664a3178367597c32db19880477a53c
|
||||||
|
|
||||||
|
- git: https://github.com/Bodigrim/tar
|
||||||
|
commit: ac197ec7ea4838dc2b4e22b9b888b080cedf29cf
|
||||||
|
|
||||||
- IfElse-0.85@sha256:6939b94acc6a55f545f63a168a349dd2fbe4b9a7cca73bf60282db5cc6aa47d2,445
|
- IfElse-0.85@sha256:6939b94acc6a55f545f63a168a349dd2fbe4b9a7cca73bf60282db5cc6aa47d2,445
|
||||||
- ascii-string-1.0.1.4@sha256:fa34f1d9ba57e8e89c0d4c9cef5e01ba32cb2d4373d13f92dcc0b531a6c6749b,2582
|
- ascii-string-1.0.1.4@sha256:fa34f1d9ba57e8e89c0d4c9cef5e01ba32cb2d4373d13f92dcc0b531a6c6749b,2582
|
||||||
- brotli-0.0.0.0@sha256:2bf383a4cd308745740986be0b18381c5a0784393fe69b91456aacb2d603de46,2964
|
- brotli-0.0.0.0@sha256:2bf383a4cd308745740986be0b18381c5a0784393fe69b91456aacb2d603de46,2964
|
||||||
@ -17,16 +20,18 @@ extra-deps:
|
|||||||
- haskus-utils-data-1.3@sha256:f62c4e49021b463185d043f7b69c727b63af641a71d7edd582d9f4f98e80e500,1466
|
- haskus-utils-data-1.3@sha256:f62c4e49021b463185d043f7b69c727b63af641a71d7edd582d9f4f98e80e500,1466
|
||||||
- haskus-utils-types-1.5.1@sha256:991c472f4e751e2f0d7aab6ad4220ef151d6160876dcf0511bbf876bbd432020,1298
|
- haskus-utils-types-1.5.1@sha256:991c472f4e751e2f0d7aab6ad4220ef151d6160876dcf0511bbf876bbd432020,1298
|
||||||
- haskus-utils-variant-3.0@sha256:8d51e45d3b664e61ccc25a58b37c0ccc4ee7537138b9fee21cd15c356906dd34,2159
|
- haskus-utils-variant-3.0@sha256:8d51e45d3b664e61ccc25a58b37c0ccc4ee7537138b9fee21cd15c356906dd34,2159
|
||||||
- hpath-0.11.0@sha256:12b8405bee13d0007d644a888ef8407069ce7bbbd76970f8746b801447124ade,1440
|
|
||||||
- hpath-directory-0.14.1@sha256:548ac1321222c34caa843a41a2379a77d961141082a4695bb37cc4731e91b2c7,5312
|
|
||||||
- hpath-filepath-0.10.4@sha256:e9e44fb5fdbade7f30b5b5451257dbee15b6ef1aae4060034d73008bb3b5d878,1269
|
|
||||||
- hpath-io-0.14.1@sha256:d91373cd81483eb370a1c683e4add6182250dccce32f9b682bb1104f7765c750,1522
|
|
||||||
- hpath-posix-0.13.2@sha256:eec4ff2b00dc86be847aca0f409fc8f6212ffd2170ec36a17dc9a52b46562392,1615
|
|
||||||
- http-io-streams-0.1.6.0@sha256:53f5bab177efb52cd65ec396fd04ed59b93e5f919fb3700cd7dacd6cfce6f06d,3582
|
- http-io-streams-0.1.6.0@sha256:53f5bab177efb52cd65ec396fd04ed59b93e5f919fb3700cd7dacd6cfce6f06d,3582
|
||||||
|
- hpath-filepath-0.10.4@sha256:e9e44fb5fdbade7f30b5b5451257dbee15b6ef1aae4060034d73008bb3b5d878,1269
|
||||||
|
- hpath-posix-0.13.3@sha256:abe472cf16bccd3a8b8814865ed3551a728fde0f3a2baea2acc03023bec6c565,1615
|
||||||
|
- hspec-2.7.10@sha256:c9e82c90086acebac576552a06f3cabd249bba048edd1667c7fae0b1313d5bce,1712
|
||||||
|
- hspec-core-2.7.10@sha256:2aba6ea126442b29e8183ab27f1c811706b19b1d83b02f193a896f6fc1589d13,4621
|
||||||
|
- hspec-discover-2.7.10@sha256:d08bf5dd785629f589571477d9beb7cd91529471bd89f39517c1cb4b9b38160f,2184
|
||||||
|
- hspec-golden-aeson-0.9.0.0@sha256:aa17274114026661ba4dfc9c60c230673c8f408bd86482fd611d2d5cb6aff996,2179
|
||||||
- lzma-static-5.2.5.2@sha256:ac38dcad9ab423342a72ba48415bd75f62234e9c9e11831495b75603b5a060f6,7184
|
- lzma-static-5.2.5.2@sha256:ac38dcad9ab423342a72ba48415bd75f62234e9c9e11831495b75603b5a060f6,7184
|
||||||
- libarchive-3.0.2.1@sha256:40ebf2a278e585802427bc58826867208bb33822f63d56107a1fcc3ca04d691d,10990
|
- libarchive-3.0.2.1@sha256:40ebf2a278e585802427bc58826867208bb33822f63d56107a1fcc3ca04d691d,10990
|
||||||
- os-release-1.0.1@sha256:1281c62081f438fc3f0874d3bae6a4887d5964ac25261ba06e29d368ab173467,2716
|
- os-release-1.0.1@sha256:1281c62081f438fc3f0874d3bae6a4887d5964ac25261ba06e29d368ab173467,2716
|
||||||
- primitive-0.7.0.1@sha256:a381571c36edc7dca28b77fe8159b43c14c640087ec5946adacf949feec64231,3433
|
- primitive-0.7.0.1@sha256:a381571c36edc7dca28b77fe8159b43c14c640087ec5946adacf949feec64231,3433
|
||||||
|
- regex-posix-clib-2.7
|
||||||
- streamly-0.7.3@sha256:ad2a488fe802692ed47cab9fd0416c2904aac9e51cf2d8aafd1c3a40064c42f5,27421
|
- streamly-0.7.3@sha256:ad2a488fe802692ed47cab9fd0416c2904aac9e51cf2d8aafd1c3a40064c42f5,27421
|
||||||
- streamly-bytestring-0.1.2@sha256:cc828f41d1c714c711d38fb213b4ed186febabba598ab080e13255f69c20b13c,2469
|
- streamly-bytestring-0.1.2@sha256:cc828f41d1c714c711d38fb213b4ed186febabba598ab080e13255f69c20b13c,2469
|
||||||
- streamly-posix-0.1.0.1@sha256:5d89b806281035d34020387ed99dde1ddab282c7ed66df3b7cd010b38fd3517b,2138
|
- streamly-posix-0.1.0.1@sha256:5d89b806281035d34020387ed99dde1ddab282c7ed66df3b7cd010b38fd3517b,2138
|
||||||
@ -40,13 +45,8 @@ flags:
|
|||||||
libarchive:
|
libarchive:
|
||||||
system-libarchive: false
|
system-libarchive: false
|
||||||
|
|
||||||
ghcup:
|
regex-posix:
|
||||||
tui: true
|
_regex-posix-clib: true
|
||||||
internal-downloader: true
|
|
||||||
|
|
||||||
system-ghc: true
|
|
||||||
compiler: ghc-8.10.4
|
|
||||||
compiler-check: match-exact
|
|
||||||
|
|
||||||
ghc-options:
|
ghc-options:
|
||||||
"$locals": -O2
|
"$locals": -O2
|
||||||
|
@ -11,7 +11,6 @@ import GHCup.Types
|
|||||||
import Data.ByteString ( ByteString )
|
import Data.ByteString ( ByteString )
|
||||||
import Data.Versions
|
import Data.Versions
|
||||||
import Data.List.NonEmpty
|
import Data.List.NonEmpty
|
||||||
import HPath
|
|
||||||
import Test.QuickCheck
|
import Test.QuickCheck
|
||||||
import Test.QuickCheck.Arbitrary.ADT ( ToADTArbitrary )
|
import Test.QuickCheck.Arbitrary.ADT ( ToADTArbitrary )
|
||||||
import Test.QuickCheck.Arbitrary.Generic
|
import Test.QuickCheck.Arbitrary.Generic
|
||||||
@ -164,11 +163,6 @@ instance Arbitrary VersionCmp where
|
|||||||
arbitrary = genericArbitrary
|
arbitrary = genericArbitrary
|
||||||
shrink = genericShrink
|
shrink = genericShrink
|
||||||
|
|
||||||
instance Arbitrary (Path Rel) where
|
|
||||||
arbitrary =
|
|
||||||
either (error . show) id . parseRel . E.encodeUtf8 . T.pack
|
|
||||||
<$> listOf1 (elements ['a' .. 'z'])
|
|
||||||
|
|
||||||
instance Arbitrary TarDir where
|
instance Arbitrary TarDir where
|
||||||
arbitrary = genericArbitrary
|
arbitrary = genericArbitrary
|
||||||
shrink = genericShrink
|
shrink = genericShrink
|
||||||
|
Loading…
Reference in New Issue
Block a user