Compare commits
8 Commits
stack-fork
...
fix-instal
| Author | SHA1 | Date | |
|---|---|---|---|
|
dbf1d6f420
|
|||
|
0a0fbd0cb6
|
|||
|
f13f53b910
|
|||
|
6dfc04a9f6
|
|||
|
72133d0002
|
|||
|
6e07e9e56b
|
|||
|
e903aeb555
|
|||
|
2792f6f4b6
|
8720
.gitlab/ghc-8.10.3-linux.files
Normal file
8720
.gitlab/ghc-8.10.3-linux.files
Normal file
File diff suppressed because it is too large
Load Diff
10321
.gitlab/ghc-8.10.3-windows.files
Normal file
10321
.gitlab/ghc-8.10.3-windows.files
Normal file
File diff suppressed because it is too large
Load Diff
@@ -116,7 +116,20 @@ else
|
||||
if [ "${OS}" = "LINUX" ] ; then
|
||||
eghcup --downloader=wget prefetch ghc 8.10.3
|
||||
eghcup --offline install ghc 8.10.3
|
||||
else # test wget a bit
|
||||
if [ "${ARCH}" = "64" ] ; then
|
||||
expected=$(cat "$( cd "$(dirname "$0")" ; pwd -P )/../ghc-8.10.3-linux.files" | sort)
|
||||
actual=$(cd "${GHCUP_DIR}/ghc/8.10.3/" && find | sort)
|
||||
[ "${actual}" = "${expected}" ]
|
||||
unset actual expected
|
||||
fi
|
||||
elif [ "${OS}" = "WINDOWS" ] ; then
|
||||
eghcup prefetch ghc 8.10.3
|
||||
eghcup --offline install ghc 8.10.3
|
||||
expected=$(cat "$( cd "$(dirname "$0")" ; pwd -P )/../ghc-8.10.3-windows.files" | sort)
|
||||
actual=$(cd "${GHCUP_DIR}/ghc/8.10.3/" && find | sort)
|
||||
[ "${actual}" = "${expected}" ]
|
||||
unset actual expected
|
||||
else
|
||||
eghcup prefetch ghc 8.10.3
|
||||
eghcup --offline install ghc 8.10.3
|
||||
fi
|
||||
|
||||
48
README.md
48
README.md
@@ -19,8 +19,6 @@ Similar in scope to [rustup](https://github.com/rust-lang-nursery/rustup.rs), [p
|
||||
* [Env variables](#env-variables)
|
||||
* [Installing custom bindists](#installing-custom-bindists)
|
||||
* [Tips and tricks](#tips-and-tricks)
|
||||
* [Stack hooks](#stack-hooks)
|
||||
* [Sharing MSys2 between stack and ghcup](#sharing-msys2-between-stack-and-ghcup)
|
||||
* [Design goals](#design-goals)
|
||||
* [How](#how)
|
||||
* [Known users](#known-users)
|
||||
@@ -141,10 +139,6 @@ This is the complete list of env variables that change GHCup behavior:
|
||||
* `GHCUP_SKIP_UPDATE_CHECK`: Skip the (possibly annoying) update check when you run a command
|
||||
* `CC`/`LD` etc.: full environment is passed to the build system when compiling GHC via GHCup
|
||||
|
||||
On windows, there are additional variables:
|
||||
|
||||
* `GHCUP_MSYS2`: where to find msys2, so we can invoke shells and other cool stuff
|
||||
|
||||
### Installing custom bindists
|
||||
|
||||
There are a couple of good use cases to install custom bindists:
|
||||
@@ -208,36 +202,6 @@ with_ghc 8.10.5 code path/to/haskell/source
|
||||
Cabal and HLS will now see `8.10.5` as the primary GHC, without the need to
|
||||
run `ghcup set` all the time when switching between projects.
|
||||
|
||||
### Stack hooks
|
||||
|
||||
GHCup distributes a patched Stack, which has support for custom installation hooks, see:
|
||||
|
||||
* https://github.com/commercialhaskell/stack/pull/5585
|
||||
|
||||
Usually, the bootstrap script will already install a hook for you. If not,
|
||||
download it [here](https://gitlab.haskell.org/haskell/ghcup-hs/-/tree/master/hooks/stack/ghc-install.sh),
|
||||
place it in `~/.stack/hooks/ghc-install.sh` and make sure it's executable.
|
||||
|
||||
Hooks aren't run when `system-ghc: true` is set in `stack.yaml`. If you want stack
|
||||
to never fall back to its own installation logic if ghcup fails, run the following command:
|
||||
|
||||
```sh
|
||||
stack config set install-ghc false --global
|
||||
```
|
||||
|
||||
### Sharing MSys2 between stack and ghcup
|
||||
|
||||
You can tell stack to use GHCup's MSys2 installation. Add the following lines to `~/.stack/config.yaml`:
|
||||
|
||||
```yml
|
||||
skip-msys: true
|
||||
extra-path:
|
||||
- "C:\\ghcup\\msys64\\usr\\bin"
|
||||
- "C:\\ghcup\\msys64\\mingw64\\bin"
|
||||
extra-include-dirs: "C:\\ghcup\\msys64\\mingw64\\include"
|
||||
extra-lib-dirs: "C:\\ghcup\\msys64\\mingw64\\lib"
|
||||
```
|
||||
|
||||
## Design goals
|
||||
|
||||
1. simplicity
|
||||
@@ -314,6 +278,18 @@ to figure out whether you have the correct toolchain and
|
||||
the correct dependencies. Refer to [the official docs](https://ghc.haskell.org/trac/ghc/wiki/Building/Preparation/Linux)
|
||||
on how to prepare your environment for building GHC.
|
||||
|
||||
### Stack support
|
||||
|
||||
There may be a number of bugs when trying to make ghcup installed GHC versions work with stack,
|
||||
such as:
|
||||
|
||||
- https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/188
|
||||
|
||||
Further, stack's upgrade procedure may break/confuse ghcup. There are a number of integration
|
||||
issues discussed here:
|
||||
|
||||
- https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/153
|
||||
|
||||
### Windows support
|
||||
|
||||
Windows support is in early stages. Since windows doesn't support symbolic links properly,
|
||||
|
||||
@@ -237,7 +237,7 @@ validateTarballs (TarballFilter etool versionRegex) dls gt = do
|
||||
($(logError) $ T.pack $ prettyShow e)
|
||||
liftIO $ exitWith (ExitFailure 2)
|
||||
|
||||
let appstate = AppState (Settings True False Never Curl False GHCupURL False) dirs defaultKeyBindings (GHCupInfo mempty mempty mempty) pfreq
|
||||
let appstate = AppState (Settings True False Never Curl True GHCupURL False) dirs defaultKeyBindings (GHCupInfo mempty mempty mempty) pfreq
|
||||
|
||||
r <-
|
||||
runLogger
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
# * BOOTSTRAP_HASKELL_GHC_VERSION - the ghc version to install
|
||||
# * BOOTSTRAP_HASKELL_CABAL_VERSION - the cabal version to install
|
||||
# * BOOTSTRAP_HASKELL_INSTALL_STACK - whether to install latest stack
|
||||
# * BOOTSTRAP_HASKELL_INSTALL_STACK_HOOK - whether to install stack hook as well
|
||||
# * BOOTSTRAP_HASKELL_INSTALL_HLS - whether to install latest hls
|
||||
# * BOOTSTRAP_HASKELL_ADJUST_BASHRC - whether to adjust PATH in bashrc (prepend)
|
||||
# * BOOTSTRAP_HASKELL_ADJUST_CABAL_CONFIG - whether to adjust mingw paths in cabal.config on windows
|
||||
@@ -509,25 +508,17 @@ ask_hls() {
|
||||
|
||||
ask_stack() {
|
||||
if [ -n "${BOOTSTRAP_HASKELL_INSTALL_STACK}" ] ; then
|
||||
if [ -n "${BOOTSTRAP_HASKELL_INSTALL_STACK_HOOK}" ] ; then
|
||||
return 2
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -z "${BOOTSTRAP_HASKELL_NONINTERACTIVE}" ] ; then
|
||||
echo "-------------------------------------------------------------------------------"
|
||||
|
||||
warn "Do you want to install stack and stack hooks?"
|
||||
warn ""
|
||||
warn "Do you want to install stack?"
|
||||
warn "Stack is a haskell build tool similar to cabal that is used by some projects."
|
||||
warn "Also see https://docs.haskellstack.org/"
|
||||
warn ""
|
||||
warn "Stack hooks allow stack to use ghcup for GHC installation (usually stack handles"
|
||||
warn "installation itself, leading to possibly duplicated GHC versions)."
|
||||
warn ""
|
||||
warn "[Y] Yes, with hooks [L] Yes, no hooks [N] No [?] Help (default is \"N\")."
|
||||
warn "[Y] Yes [N] No [?] Help (default is \"N\")."
|
||||
warn ""
|
||||
|
||||
while true; do
|
||||
@@ -535,16 +526,13 @@ ask_stack() {
|
||||
|
||||
case $stack_answer in
|
||||
[Yy]*)
|
||||
return 2 ;;
|
||||
[Ll]*)
|
||||
return 1 ;;
|
||||
[Nn]* | "")
|
||||
return 0 ;;
|
||||
*)
|
||||
echo "Possible choices are:"
|
||||
echo
|
||||
echo "Y - Yes, install stack with hooks"
|
||||
echo "L - Yes, install stack without hooks"
|
||||
echo "Y - Yes, install stack"
|
||||
echo "N - No, don't install anything more (default)"
|
||||
echo
|
||||
echo "Please make your choice and press ENTER."
|
||||
@@ -648,27 +636,7 @@ esac
|
||||
|
||||
case $ask_stack_answer in
|
||||
1)
|
||||
{
|
||||
_eghcup --cache install stack
|
||||
edo stack update
|
||||
} || warn "Stack installation failed, continuing anyway"
|
||||
;;
|
||||
2)
|
||||
{
|
||||
_eghcup --cache install stack
|
||||
edo stack update
|
||||
if [ -n "${STACK_ROOT}" ] ; then
|
||||
stack_root=$STACK_ROOT
|
||||
else
|
||||
stack_root=${HOME}/.stack
|
||||
fi
|
||||
edo mkdir -p "${stack_root}/hooks/"
|
||||
edo curl -Lf "https://www.haskell.org/ghcup/sh/hooks/stack/ghc-install.sh" > "${stack_root}/hooks/ghc-install.sh"
|
||||
edo chmod +x "${stack_root}/hooks/ghc-install.sh"
|
||||
edo stack config set system-ghc false --global
|
||||
edo stack config set install-ghc false --global
|
||||
|
||||
} || warn "Stack installation failed, continuing anyway"
|
||||
_eghcup --cache install stack || warn "Stack installation failed, continuing anyway"
|
||||
;;
|
||||
*) ;;
|
||||
esac
|
||||
|
||||
@@ -29,8 +29,6 @@ param (
|
||||
[switch]$InBash,
|
||||
# Whether to install stack as well
|
||||
[switch]$InstallStack,
|
||||
# Whether to install stack hooks as well
|
||||
[switch]$InstallStackHook,
|
||||
# Whether to install hls as well
|
||||
[switch]$InstallHLS,
|
||||
# Skip adjusting cabal.config with mingw paths
|
||||
@@ -225,7 +223,6 @@ $null = [Environment]::SetEnvironmentVariable("GHCUP_INSTALL_BASE_PREFIX", $Ghcu
|
||||
|
||||
|
||||
$GhcupDir = ('{0}\ghcup' -f $GhcupBasePrefix)
|
||||
$StackDir = ('{0}\sr' -f $GhcupBasePrefix)
|
||||
$MsysDir = ('{0}\msys64' -f $GhcupDir)
|
||||
$Bash = ('{0}\usr\bin\bash' -f $MsysDir)
|
||||
if (!($BootstrapUrl)) {
|
||||
@@ -313,28 +310,15 @@ if (!($InstallHLS)) {
|
||||
}
|
||||
|
||||
# ask whether to install stack
|
||||
$InstallStackDoc = @'
|
||||
Do you want to install stack and stack hooks?
|
||||
|
||||
Stack is a haskell build tool similar to cabal that is used by some projects.
|
||||
Also see https://docs.haskellstack.org/
|
||||
|
||||
Stack hooks allow stack to use ghcup for GHC installation (usually stack handles
|
||||
installation itself, leading to possibly duplicated GHC versions).
|
||||
'@
|
||||
if (!($InstallStack)) {
|
||||
if (!($Silent)) {
|
||||
$StackDecision = $Host.UI.PromptForChoice('Install stack'
|
||||
, $InstallStackDoc
|
||||
, [System.Management.Automation.Host.ChoiceDescription[]] @('&Yes, with hooks'
|
||||
'&Light install, without hooks'
|
||||
, 'Do you want to install stack as well?'
|
||||
, [System.Management.Automation.Host.ChoiceDescription[]] @('&Yes'
|
||||
'&No'
|
||||
'&Abort'), 2)
|
||||
'&Abort'), 1)
|
||||
|
||||
if ($StackDecision -eq 0) {
|
||||
$InstallStack = $true
|
||||
$InstallStackHook = $true
|
||||
} elseif ($StackDecision -eq 1) {
|
||||
$InstallStack = $true
|
||||
} elseif ($StackDecision -eq 2) {
|
||||
Exit 0
|
||||
@@ -515,11 +499,6 @@ $SilentExport = 'export BOOTSTRAP_HASKELL_NONINTERACTIVE=1 ;'
|
||||
|
||||
if ($InstallStack) {
|
||||
$StackInstallExport = 'export BOOTSTRAP_HASKELL_INSTALL_STACK=1 ;'
|
||||
$null = [Environment]::SetEnvironmentVariable("STACK_ROOT", $StackDir, [System.EnvironmentVariableTarget]::User)
|
||||
}
|
||||
|
||||
if ($InstallStackHook) {
|
||||
$StackInstallHookExport = 'export BOOTSTRAP_HASKELL_INSTALL_STACK_HOOK=1 ;'
|
||||
}
|
||||
|
||||
if ($InstallHLS) {
|
||||
@@ -531,9 +510,9 @@ if (!($NoAdjustCabalConfig)) {
|
||||
}
|
||||
|
||||
if ((Get-Process -ID $PID).ProcessName.StartsWith("bootstrap-haskell") -Or $InBash) {
|
||||
Exec "$Bash" '-lc' ('{4} {6} {7} {8} {9} [ -n ''{1}'' ] && export GHCUP_MSYS2=$(cygpath -m ''{1}'') ; [ -n ''{2}'' ] && export GHCUP_INSTALL_BASE_PREFIX=$(cygpath -m ''{2}/'') ; export PATH=$(cygpath -u ''{3}/bin''):$PATH ; export CABAL_DIR=''{5}'' ; export STACK_ROOT=$(cygpath -m ''{10}/'') ; [[ ''{0}'' = https* ]] && curl --proto ''=https'' --tlsv1.2 -sSf {0} | bash || cat $(cygpath -m ''{0}'') | bash' -f $BootstrapUrl, $MsysDir, $GhcupBasePrefix, $GhcupDir, $SilentExport, $CabalDirFull, $StackInstallExport, $HLSInstallExport, $AdjustCabalConfigExport, $StackInstallHookExport, $StackDir)
|
||||
Exec "$Bash" '-lc' ('{4} {6} {7} {8} [ -n ''{1}'' ] && export GHCUP_MSYS2=$(cygpath -m ''{1}'') ; [ -n ''{2}'' ] && export GHCUP_INSTALL_BASE_PREFIX=$(cygpath -m ''{2}/'') ; export PATH=$(cygpath -u ''{3}/bin''):$PATH ; export CABAL_DIR=''{5}'' ; [[ ''{0}'' = https* ]] && curl --proto ''=https'' --tlsv1.2 -sSf {0} | bash || cat $(cygpath -m ''{0}'') | bash' -f $BootstrapUrl, $MsysDir, $GhcupBasePrefix, $GhcupDir, $SilentExport, $CabalDirFull, $StackInstallExport, $HLSInstallExport, $AdjustCabalConfigExport)
|
||||
} else {
|
||||
Exec "$Msys2Shell" '-mingw64' '-mintty' '-c' ('{4} {6} {7} {8} {9} [ -n ''{1}'' ] && export GHCUP_MSYS2=$(cygpath -m ''{1}'') ; [ -n ''{2}'' ] && export GHCUP_INSTALL_BASE_PREFIX=$(cygpath -m ''{2}/'') ; export PATH=$(cygpath -u ''{3}/bin''):$PATH ; export CABAL_DIR=''{5}'' ; export STACK_ROOT=$(cygpath -m ''{10}/'') ; trap ''echo Press any key to exit && read -n 1 && exit'' 2 ; [[ ''{0}'' = https* ]] && curl --proto ''=https'' --tlsv1.2 -sSf {0} | bash || cat $(cygpath -m ''{0}'') | bash ; echo ''Press any key to exit'' && read -n 1' -f $BootstrapUrl, $MsysDir, $GhcupBasePrefix, $GhcupDir, $SilentExport, $CabalDirFull, $StackInstallExport, $HLSInstallExport, $AdjustCabalConfigExport, $StackInstallHookExport, $StackDir)
|
||||
Exec "$Msys2Shell" '-mingw64' '-mintty' '-c' ('{4} {6} {7} {8} [ -n ''{1}'' ] && export GHCUP_MSYS2=$(cygpath -m ''{1}'') ; [ -n ''{2}'' ] && export GHCUP_INSTALL_BASE_PREFIX=$(cygpath -m ''{2}/'') ; export PATH=$(cygpath -u ''{3}/bin''):$PATH ; export CABAL_DIR=''{5}'' ; trap ''echo Press any key to exit && read -n 1 && exit'' 2 ; [[ ''{0}'' = https* ]] && curl --proto ''=https'' --tlsv1.2 -sSf {0} | bash || cat $(cygpath -m ''{0}'') | bash ; echo ''Press any key to exit'' && read -n 1' -f $BootstrapUrl, $MsysDir, $GhcupBasePrefix, $GhcupDir, $SilentExport, $CabalDirFull, $StackInstallExport, $HLSInstallExport, $AdjustCabalConfigExport)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2206,7 +2206,8 @@ ghcupDownloads:
|
||||
unknown_versioning: *stack-271-64
|
||||
2.7.3:
|
||||
viTags:
|
||||
- old
|
||||
- Latest
|
||||
- Recommended
|
||||
viChangeLog: https://github.com/commercialhaskell/stack/blob/master/ChangeLog.md#v273
|
||||
viPostInstall: *stack-post
|
||||
viArch:
|
||||
@@ -2231,65 +2232,4 @@ ghcupDownloads:
|
||||
RegexDir: "stack-.*"
|
||||
Linux_Alpine:
|
||||
unknown_versioning: *stack-273-64
|
||||
2.7.3.1:
|
||||
viTags:
|
||||
- Latest
|
||||
- Recommended
|
||||
viChangeLog: https://github.com/commercialhaskell/stack/blob/master/ChangeLog.md#v273
|
||||
viPostInstall: &stack-post-new |
|
||||
Stack manages GHC versions internally by default. It can also use hooks to automatically
|
||||
install and locate GHC versions utilizing GHCup:
|
||||
https://gitlab.haskell.org/haskell/ghcup-hs#stack-hooks
|
||||
|
||||
Alternatively, you can also tell stack to use the system GHC version (whatever is in PATH):
|
||||
stack config set system-ghc true --global
|
||||
|
||||
If you want stack to use GHCup's provided MSys2, follow the instructions here (not recommended):
|
||||
https://gitlab.haskell.org/haskell/ghcup-hs/-/tree/stack-fork#sharing-msys2-between-stack-and-ghcup
|
||||
|
||||
Also check out: https://docs.haskellstack.org/en/stable/yaml_configuration
|
||||
|
||||
!!! GHCup ships a patched version of stack. Before reporting bugs to stack developers !!!
|
||||
!!! make sure it is reproducible with the original stack version: !!!
|
||||
!!! https://docs.haskellstack.org/en/stable/install_and_upgrade/ !!!
|
||||
viArch:
|
||||
A_64:
|
||||
Linux_UnknownLinux:
|
||||
unknown_versioning: &stack-2731-64
|
||||
dlUri: https://downloads.haskell.org/ghcup/unofficial-bindists/stack/2.7.3.1/stack-2.7.3.1-linux-x86_64.tar.gz
|
||||
dlSubdir:
|
||||
RegexDir: "stack-.*"
|
||||
dlHash: 7c10090d568651208b6d6c003c8d78ef79099f4b472aea5ca800f3e4a7a1c6ce
|
||||
Darwin:
|
||||
unknown_versioning:
|
||||
dlUri: https://downloads.haskell.org/ghcup/unofficial-bindists/stack/2.7.3.1/stack-2.7.3.1-osx-x86_64.tar.gz
|
||||
dlSubdir:
|
||||
RegexDir: "stack-.*"
|
||||
dlHash: 23030868be377d62ed324332d239ddd915b12f7a64c887f297e1b60a3f65894f
|
||||
Windows:
|
||||
unknown_versioning:
|
||||
dlUri: https://downloads.haskell.org/ghcup/unofficial-bindists/stack/2.7.3.1/stack-2.7.3.1-windows-x86_64.tar.bz2
|
||||
dlHash: 983ac24219370cdf220f47f9edb17d0aeb831a1253fd777d322fd1bff358c511
|
||||
Linux_Alpine:
|
||||
unknown_versioning: *stack-2731-64
|
||||
FreeBSD:
|
||||
unknown_versioning:
|
||||
dlUri: https://downloads.haskell.org/ghcup/unofficial-bindists/stack/2.7.3.1/stack-2.7.3.1-freebsd-x86_64.tar.gz
|
||||
dlHash: b6b1eb59981b8c1d3a04b061f03296b1540ecbadbc34cbd4fe712a575a155ee9
|
||||
A_32:
|
||||
Linux_UnknownLinux:
|
||||
unknown_versioning: &stack-2731-32
|
||||
dlUri: https://downloads.haskell.org/ghcup/unofficial-bindists/stack/2.7.3.1/stack-2.7.3.1-linux-i386.tar.gz
|
||||
dlHash: da2081d84517d16a8216e929582dd39caa19643c819b77c6f68f138bd177a758
|
||||
Linux_Alpine:
|
||||
unknown_versioning: *stack-2731-32
|
||||
A_ARM64:
|
||||
Linux_UnknownLinux:
|
||||
unknown_versioning:
|
||||
dlUri: https://downloads.haskell.org/ghcup/unofficial-bindists/stack/2.7.3.1/stack-2.7.3.1-linux-aarch64.tar.gz
|
||||
dlHash: 0e1789852a476dd87f495fa0b481309398b2bafc00cf7c8d980c91977b39d9a6
|
||||
A_ARM:
|
||||
Linux_UnknownLinux:
|
||||
unknown_versioning:
|
||||
dlUri: https://downloads.haskell.org/ghcup/unofficial-bindists/stack/2.7.3.1/stack-2.7.3.1-linux-armv7.tar.gz
|
||||
dlHash: 13d61616c169615d80381ea0c84e13540410b736a553565cd48d10b4d2bd7f23
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
case $HOOK_GHC_TYPE in
|
||||
bindist)
|
||||
ghcup --offline whereis ghc $HOOK_GHC_VERSION || {
|
||||
ghcup --cache install ghc $HOOK_GHC_VERSION && ghcup whereis ghc $HOOK_GHC_VERSION
|
||||
}
|
||||
;;
|
||||
git)
|
||||
>&2 echo "Hook doesn't support installing from source."
|
||||
>&2 echo "Consider enabling stack GHC installs for this project, via:"
|
||||
>&2 echo " stack config set install-ghc true"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
>&2 echo "Unsupported GHC installation type: ${HOOK_GHC_TYPE}."
|
||||
>&2 echo "Consider enabling stack GHC installs for this project, via:"
|
||||
>&2 echo " stack config set install-ghc true"
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
@@ -300,10 +300,6 @@ installUnpackedGHC path inst ver = do
|
||||
setModificationTime dest mtime
|
||||
#else
|
||||
PlatformRequest {..} <- lift getPlatformReq
|
||||
liftIO $ copyDirectoryRecursive path inst $ \source dest -> do
|
||||
mtime <- getModificationTime source
|
||||
copyFile source dest
|
||||
setModificationTime dest mtime
|
||||
|
||||
let alpineArgs
|
||||
| ver >= [vver|8.2.2|], Linux Alpine <- _rPlatform
|
||||
|
||||
@@ -172,21 +172,31 @@ getBase :: ( MonadReader env m
|
||||
-> Excepts '[JSONError] m GHCupInfo
|
||||
getBase uri = do
|
||||
Settings { noNetwork } <- lift getSettings
|
||||
yaml <- lift $ yamlFromCache uri
|
||||
unless noNetwork $
|
||||
handleIO (\e -> warnCache (displayException e))
|
||||
. catchE @_ @_ @'[] (\e@(DownloadFailed _) -> warnCache (prettyShow e))
|
||||
. reThrowAll @_ @_ @'[DownloadFailed] DownloadFailed
|
||||
. smartDl
|
||||
$ uri
|
||||
|
||||
-- try to download yaml... usually this writes it into cache dir,
|
||||
-- but in some cases not (e.g. when using file://), so we honour
|
||||
-- the return filepath, if any
|
||||
mYaml <- if noNetwork && view (uriSchemeL' % schemeBSL') uri /= "file" -- for file://, let it fall through
|
||||
then pure Nothing
|
||||
else handleIO (\e -> warnCache (displayException e) >> pure Nothing)
|
||||
. catchE @_ @_ @'[] (\e@(DownloadFailed _) -> warnCache (prettyShow e) >> pure Nothing)
|
||||
. reThrowAll @_ @_ @'[DownloadFailed] DownloadFailed
|
||||
. fmap Just
|
||||
. smartDl
|
||||
$ uri
|
||||
|
||||
-- if we didn't get a filepath from the download, use the cached yaml
|
||||
actualYaml <- maybe (lift $ yamlFromCache uri) pure mYaml
|
||||
lift $ $(logDebug) [i|Decoding yaml at: #{actualYaml}|]
|
||||
|
||||
liftE
|
||||
. onE_ (onError yaml)
|
||||
. onE_ (onError actualYaml)
|
||||
. lEM' @_ @_ @'[JSONError] JSONDecodeError
|
||||
. fmap (first (\e -> [i|#{displayException e}
|
||||
Consider removing "#{yaml}" manually.|]))
|
||||
Consider removing "#{actualYaml}" manually.|]))
|
||||
. liftIO
|
||||
. Y.decodeFileEither
|
||||
$ yaml
|
||||
$ actualYaml
|
||||
where
|
||||
-- On error, remove the etags file and set access time to 0. This should ensure the next invocation
|
||||
-- may re-download and succeed.
|
||||
@@ -221,28 +231,32 @@ Consider removing "#{yaml}" manually.|]))
|
||||
, DigestError
|
||||
]
|
||||
m1
|
||||
()
|
||||
FilePath
|
||||
smartDl uri' = do
|
||||
json_file <- lift $ yamlFromCache uri'
|
||||
let scheme = view (uriSchemeL' % schemeBSL') uri'
|
||||
e <- liftIO $ doesFileExist json_file
|
||||
currentTime <- liftIO getCurrentTime
|
||||
if e
|
||||
then do
|
||||
accessTime <- liftIO $ getAccessTime json_file
|
||||
Dirs { cacheDir } <- lift getDirs
|
||||
|
||||
-- access time won't work on most linuxes, but we can try regardless
|
||||
when ((utcTimeToPOSIXSeconds currentTime - utcTimeToPOSIXSeconds accessTime) > 300) $
|
||||
-- no access in last 5 minutes, re-check upstream mod time
|
||||
dlWithMod currentTime json_file
|
||||
else
|
||||
dlWithMod currentTime json_file
|
||||
-- for local files, let's short-circuit and ignore access time
|
||||
if | scheme == "file" -> liftE $ download uri' Nothing cacheDir Nothing True
|
||||
| e -> do
|
||||
accessTime <- liftIO $ getAccessTime json_file
|
||||
|
||||
-- access time won't work on most linuxes, but we can try regardless
|
||||
if | ((utcTimeToPOSIXSeconds currentTime - utcTimeToPOSIXSeconds accessTime) > 300) ->
|
||||
-- no access in last 5 minutes, re-check upstream mod time
|
||||
dlWithMod currentTime json_file
|
||||
| otherwise -> pure json_file
|
||||
| otherwise -> dlWithMod currentTime json_file
|
||||
where
|
||||
dlWithMod modTime json_file = do
|
||||
let (dir, fn) = splitFileName json_file
|
||||
f <- liftE $ download uri' Nothing dir (Just fn) True
|
||||
liftIO $ setModificationTime f modTime
|
||||
liftIO $ setAccessTime f modTime
|
||||
|
||||
pure f
|
||||
|
||||
|
||||
getDownloadInfo :: ( MonadReader env m
|
||||
@@ -304,27 +318,25 @@ download :: ( MonadReader env m
|
||||
)
|
||||
=> URI
|
||||
-> Maybe T.Text -- ^ expected hash
|
||||
-> FilePath -- ^ destination dir
|
||||
-> FilePath -- ^ destination dir (ignored for file:// scheme)
|
||||
-> Maybe FilePath -- ^ optional filename
|
||||
-> Bool -- ^ whether to read an write etags
|
||||
-> Excepts '[DigestError , DownloadFailed] m FilePath
|
||||
download uri eDigest dest mfn etags
|
||||
| scheme == "https" = dl
|
||||
| scheme == "http" = dl
|
||||
| scheme == "file" = cp
|
||||
| scheme == "file" = do
|
||||
let destFile' = T.unpack . decUTF8Safe $ path
|
||||
lift $ $(logDebug) [i|using local file: #{destFile'}|]
|
||||
forM_ eDigest (liftE . flip checkDigest destFile')
|
||||
pure destFile'
|
||||
| otherwise = throwE $ DownloadFailed (variantFromValue UnsupportedScheme)
|
||||
|
||||
where
|
||||
scheme = view (uriSchemeL' % schemeBSL') uri
|
||||
cp = do
|
||||
-- destination dir must exist
|
||||
liftIO $ createDirRecursive' dest
|
||||
let fromFile = T.unpack . decUTF8Safe $ path
|
||||
liftIO $ copyFile fromFile destFile
|
||||
pure destFile
|
||||
dl = do
|
||||
let uri' = decUTF8Safe (serializeURIRef' uri)
|
||||
lift $ $(logInfo) [i|downloading: #{uri'}|]
|
||||
destFile <- liftE . reThrowAll @_ @_ @'[DownloadFailed] DownloadFailed $ getDestFile
|
||||
lift $ $(logInfo) [i|downloading: #{uri'} as file #{destFile}|]
|
||||
|
||||
-- destination dir must exist
|
||||
liftIO $ createDirRecursive' dest
|
||||
@@ -366,7 +378,7 @@ download uri eDigest dest mfn etags
|
||||
_ -> liftE $ throwE @_ @'[DownloadFailed] (DownloadFailed (toVariantAt @0 (MalformedHeaders headers)
|
||||
:: V '[MalformedHeaders]))
|
||||
|
||||
writeEtags (parseEtags headers)
|
||||
writeEtags destFile (parseEtags headers)
|
||||
else
|
||||
liftE $ lEM @_ @'[ProcessError] $ exec "curl"
|
||||
(o' ++ ["-fL", "-o", destFile, T.unpack uri']) Nothing Nothing
|
||||
@@ -383,13 +395,13 @@ download uri eDigest dest mfn etags
|
||||
case _exitCode of
|
||||
ExitSuccess -> do
|
||||
liftIO $ copyFile destFileTemp destFile
|
||||
writeEtags (parseEtags (decUTF8Safe' _stdErr))
|
||||
writeEtags destFile (parseEtags (decUTF8Safe' _stdErr))
|
||||
ExitFailure i'
|
||||
| i' == 8
|
||||
, Just _ <- find (T.pack "304 Not Modified" `T.isInfixOf`) . T.lines . decUTF8Safe' $ _stdErr
|
||||
-> do
|
||||
$logDebug "Not modified, skipping download"
|
||||
writeEtags (parseEtags (decUTF8Safe' _stdErr))
|
||||
writeEtags destFile (parseEtags (decUTF8Safe' _stdErr))
|
||||
| otherwise -> throwE (NonZeroExit i' "wget" opts)
|
||||
else do
|
||||
let opts = o' ++ ["-O", destFileTemp , T.unpack uri']
|
||||
@@ -404,10 +416,10 @@ download uri eDigest dest mfn etags
|
||||
let addHeaders = maybe mempty (\etag -> M.fromList [ (mk . E.encodeUtf8 . T.pack $ "If-None-Match"
|
||||
, E.encodeUtf8 etag)]) metag
|
||||
liftE
|
||||
$ catchE @HTTPNotModified @'[DownloadFailed] @'[] (\(HTTPNotModified etag) -> lift $ writeEtags (pure $ Just etag))
|
||||
$ catchE @HTTPNotModified @'[DownloadFailed] @'[] (\(HTTPNotModified etag) -> lift $ writeEtags destFile (pure $ Just etag))
|
||||
$ do
|
||||
r <- downloadToFile https host fullPath port destFile addHeaders
|
||||
writeEtags (pure $ decUTF8Safe <$> getHeader r "etag")
|
||||
writeEtags destFile (pure $ decUTF8Safe <$> getHeader r "etag")
|
||||
else void $ liftE $ catchE @HTTPNotModified
|
||||
@'[DownloadFailed]
|
||||
(\e@(HTTPNotModified _) ->
|
||||
@@ -420,12 +432,18 @@ download uri eDigest dest mfn etags
|
||||
|
||||
|
||||
-- Manage to find a file we can write the body into.
|
||||
destFile :: FilePath
|
||||
destFile = maybe (dest </> T.unpack (decUTF8Safe (urlBaseName path)))
|
||||
(dest </>)
|
||||
mfn
|
||||
getDestFile :: Monad m => Excepts '[NoUrlBase] m FilePath
|
||||
getDestFile =
|
||||
case mfn of
|
||||
Just fn -> pure (dest </> fn)
|
||||
Nothing
|
||||
| let urlBase = T.unpack (decUTF8Safe (urlBaseName path))
|
||||
, not (null urlBase) -> pure (dest </> urlBase)
|
||||
-- TODO: remove this once we use hpath again
|
||||
| otherwise -> throwE $ NoUrlBase uri'
|
||||
|
||||
path = view pathL' uri
|
||||
uri' = decUTF8Safe (serializeURIRef' uri)
|
||||
|
||||
parseEtags :: (MonadLogger m, MonadIO m, MonadThrow m) => T.Text -> m (Maybe T.Text)
|
||||
parseEtags stderr = do
|
||||
@@ -444,8 +462,8 @@ download uri eDigest dest mfn etags
|
||||
$logDebug "No etags header found"
|
||||
pure Nothing
|
||||
|
||||
writeEtags :: (MonadLogger m, MonadIO m, MonadThrow m) => m (Maybe T.Text) -> m ()
|
||||
writeEtags getTags = do
|
||||
writeEtags :: (MonadLogger m, MonadIO m, MonadThrow m) => FilePath -> m (Maybe T.Text) -> m ()
|
||||
writeEtags destFile getTags = do
|
||||
getTags >>= \case
|
||||
Just t -> do
|
||||
$logDebug [i|Writing etagsFile #{(etagsFile destFile)}|]
|
||||
|
||||
@@ -327,6 +327,15 @@ instance Pretty UnexpectedListLength where
|
||||
|
||||
instance Exception UnexpectedListLength
|
||||
|
||||
data NoUrlBase = NoUrlBase Text
|
||||
deriving Show
|
||||
|
||||
instance Pretty NoUrlBase where
|
||||
pPrint (NoUrlBase url) =
|
||||
text [i|Couldn't get a base filename from url #{url}|]
|
||||
|
||||
instance Exception NoUrlBase
|
||||
|
||||
|
||||
|
||||
------------------------
|
||||
|
||||
Reference in New Issue
Block a user