Compare commits
339 Commits
improve-pw
...
nightlies
| Author | SHA1 | Date | |
|---|---|---|---|
| 34ac9cec4d | |||
| 513f7446b3 | |||
| aed478153d | |||
| 210816769a | |||
| 42bf21c86e | |||
| 4b34cddcda | |||
| 1ba2361fea | |||
| 278a3005d1 | |||
|
|
78d68e381a | ||
| 17ffc459db | |||
| afcb482866 | |||
|
c28de19faa
|
|||
|
7ae952c82e
|
|||
|
|
98098035c9 | ||
|
acdc0786ba
|
|||
|
7fa72a8892
|
|||
|
fa22920e51
|
|||
|
f084fbce43
|
|||
|
|
1850c00e9d | ||
|
c20deceaa8
|
|||
|
89e4145baf
|
|||
|
|
f5f7c26d8a | ||
|
784942ca58
|
|||
|
75de2a7bc1
|
|||
|
ea6c8d338c
|
|||
|
ae625b181c
|
|||
|
89ae54a083
|
|||
|
1bd73591ba
|
|||
|
f709f6e714
|
|||
|
3d7e07c371
|
|||
|
8bf17379ac
|
|||
|
4b1225ad71
|
|||
|
d628848af6
|
|||
|
48381be001
|
|||
|
b547324253
|
|||
|
2b1599c234
|
|||
|
7ac8989dfc
|
|||
|
cd6666ed30
|
|||
|
5b7478438a
|
|||
|
4a830d9fb7
|
|||
|
785fb895b4
|
|||
|
75e801e9e6
|
|||
|
6ffd5328a4
|
|||
|
ed509e482b
|
|||
|
420323f43b
|
|||
|
432962792c
|
|||
|
cb193f6069
|
|||
|
2f268b6a25
|
|||
|
580606af14
|
|||
|
faa1c3992b
|
|||
|
d17efef853
|
|||
|
179d4dd493
|
|||
|
e03c5ee4a1
|
|||
|
e57a8abd3d
|
|||
|
5fa10390a3
|
|||
|
e1e6f579d5
|
|||
|
72f8e53344
|
|||
|
9c464ec9fc
|
|||
|
1c9b296a5e
|
|||
|
|
275522584e | ||
|
|
804520c4bb | ||
|
|
9d25581f3c | ||
|
|
e798037d80 | ||
|
|
2afe5858cb | ||
|
f575dcdad6
|
|||
|
6cf9967e7c
|
|||
|
15a75d790a
|
|||
|
988672ea75
|
|||
|
6d3e8d65e1
|
|||
|
895e4b3f18
|
|||
|
20f0505120
|
|||
|
31e83cac5e
|
|||
|
|
9baba88f75 | ||
|
d3a1115b99
|
|||
|
6d46849fec
|
|||
|
53e324bfee
|
|||
|
2e39b7b603
|
|||
|
048932bf50
|
|||
|
69d325bf90
|
|||
|
3d1b8859cd
|
|||
|
db89ca9942
|
|||
|
bba009d98c
|
|||
|
9d954ea174
|
|||
|
da9c9049d2
|
|||
|
a4c00d2c56
|
|||
|
|
b30f565871 | ||
|
|
fa378a1d34 | ||
|
|
119efb1ff4 | ||
|
1fb4101b49
|
|||
|
ec8333b223
|
|||
|
54b979aa0b
|
|||
|
ba274307c0
|
|||
|
|
a623d0809d | ||
|
e00899d176
|
|||
|
a38ca1954b
|
|||
|
3f5a19c63e
|
|||
|
525e9672e8
|
|||
|
070c6e1cf1
|
|||
|
195fd00e0a
|
|||
|
733d014c19
|
|||
|
16039769d5
|
|||
|
5eeb8ca9fc
|
|||
|
317a06bbc3
|
|||
|
f693adcd7c
|
|||
|
ac88d2bd50
|
|||
|
a427146de5
|
|||
|
a16bcddeaa
|
|||
|
74edf1fc07
|
|||
|
1e32639873
|
|||
|
0704d2640a
|
|||
|
26a6368d79
|
|||
|
54af66d115
|
|||
|
850799c21a
|
|||
| d4834d7541 | |||
|
2895dd9d13
|
|||
|
eb9a0b66c4
|
|||
|
8d0432b961
|
|||
|
ab2c01d1c9
|
|||
|
fffaa65b7f
|
|||
|
703be0a706
|
|||
|
4be97ffd7c
|
|||
|
009f9211a9
|
|||
|
109187eb6f
|
|||
|
e881705323
|
|||
|
ea06c155a7
|
|||
|
d4732e15a7
|
|||
|
db6f784a1f
|
|||
|
82e3837dd9
|
|||
|
957c5918b8
|
|||
|
9d4c923649
|
|||
|
24c36ef856
|
|||
|
2783b8f693
|
|||
|
d5a680e3c6
|
|||
|
d1075987de
|
|||
|
e116a2392e
|
|||
|
7dd6f1f4a4
|
|||
|
4d82c37539
|
|||
|
801b1edfa7
|
|||
|
c1b67e1787
|
|||
|
70dd106549
|
|||
|
b098aa4e65
|
|||
|
74b784fcfb
|
|||
|
673db344d6
|
|||
|
5594a19c02
|
|||
|
|
a5bc13fe50 | ||
|
a5f2067d76
|
|||
|
be8fa57be1
|
|||
|
6ad9963889
|
|||
|
bcddb05b1d
|
|||
|
f7d2033e25
|
|||
|
6ce7649cfe
|
|||
|
cb0d8b80c3
|
|||
|
95869f9560
|
|||
|
e8586cf993
|
|||
|
d195a3f86c
|
|||
|
b171afa09d
|
|||
|
5cf49bffac
|
|||
|
5659de8516
|
|||
|
0cd2b6d549
|
|||
|
ae092de4b6
|
|||
|
a7e6e7c27d
|
|||
|
175a301a0d
|
|||
|
823458910b
|
|||
|
2abcb46199
|
|||
|
75b891147a
|
|||
|
de208f004e
|
|||
|
ecb0676fea
|
|||
|
957867ff1c
|
|||
|
b1b21f000d
|
|||
|
fbbafc33be
|
|||
|
4a46de4c49
|
|||
|
77419ea41d
|
|||
|
043500e8e8
|
|||
|
e924ad8278
|
|||
|
010db93b93
|
|||
|
9fdc6eebe8
|
|||
|
7c8d013b6e
|
|||
|
96eb0c3532
|
|||
|
94c01ee362
|
|||
|
4297a46f13
|
|||
|
c07e1bbc8f
|
|||
|
9ee2df3841
|
|||
|
3c5505d222
|
|||
|
|
70df740f9d | ||
|
|
761f3253c3 | ||
|
787edc17af
|
|||
|
9902adab6d
|
|||
|
156b4724f3
|
|||
|
e9575aba5c
|
|||
|
81c7f6a32a
|
|||
|
|
ab97c80b80 | ||
|
|
04369673ef | ||
|
|
63dfdc2da6 | ||
|
14de382129
|
|||
|
d97c10dbe6
|
|||
|
|
8420bf093e | ||
|
|
5e28074522 | ||
|
c842c41a78
|
|||
|
8b93eaad59
|
|||
|
2cba97cf1c
|
|||
|
7d74178295
|
|||
|
c37bef55e9
|
|||
|
837ba8b46a
|
|||
|
|
c25e73408a | ||
|
|
71c3172cf5 | ||
|
|
1f2855a107 | ||
|
|
d949c4375e | ||
|
|
22f0081303 | ||
|
5562be18e3
|
|||
|
6baa891424
|
|||
|
a544feffb3
|
|||
|
3b6bb0df46
|
|||
|
60299b6bb8
|
|||
|
4d20f4e07c
|
|||
|
5f6b5f845d
|
|||
|
b0fecce0d1
|
|||
|
|
27c06ddde7 | ||
|
3154d2839b
|
|||
|
|
511d8d5ed8 | ||
|
fe22405ee1
|
|||
|
ea828cd13a
|
|||
|
00fa70b9de
|
|||
|
823275363c
|
|||
|
2f299ee48d
|
|||
|
284fe1b3b6
|
|||
|
35bda8d67a
|
|||
|
7a2a5074fa
|
|||
|
ce239ab88e
|
|||
|
f3c703d655
|
|||
|
b6ff5bc764
|
|||
|
b8aeb1f935
|
|||
|
9673d28d3e
|
|||
|
99a51d67a1
|
|||
|
b0ef0590a2
|
|||
|
256e1942f2
|
|||
|
aa71f0dfa1
|
|||
|
04d527c98a
|
|||
|
|
ca5c5550ab | ||
|
7b59621179
|
|||
|
9d59463ded
|
|||
|
|
3d49f79beb | ||
|
|
e9740d13fc | ||
|
|
2bd5a8fe1a | ||
|
|
0acccae523 | ||
|
974112016e
|
|||
|
9fb2889696
|
|||
|
63f22b28d7
|
|||
|
9a72fa13d5
|
|||
|
86a8a32032
|
|||
|
13e01ab453
|
|||
|
873dd77a6f
|
|||
|
544c618473
|
|||
|
a264cb088e
|
|||
|
1a43fddca9
|
|||
|
|
9ceb66ef21 | ||
|
|
7cbe38b011 | ||
|
|
3bbc1edb19 | ||
|
|
b8dac2d7cd | ||
|
bdfb1a3a9b
|
|||
|
9b8b3e8126
|
|||
|
d657c17df4
|
|||
|
|
0e1fd68d93 | ||
|
|
c7eceb2330 | ||
|
|
e143c06697 | ||
|
|
29da21f5dc | ||
|
028696d4be
|
|||
|
4022edb12e
|
|||
|
fde5044194
|
|||
|
3af1286ab7
|
|||
|
bcff46d3d4
|
|||
|
d1c72cdff4
|
|||
|
565bb59f45
|
|||
|
aae3f31c50
|
|||
|
0ce9b5d352
|
|||
|
bf0e5b37ca
|
|||
|
fe620835be
|
|||
|
c7dc77e6bc
|
|||
|
05c72a3de6
|
|||
|
0653844931
|
|||
|
7661046bcb
|
|||
|
16888a12d4
|
|||
|
9f7df33692
|
|||
|
b7007aa100
|
|||
|
03dfd0cba0
|
|||
|
0e64d1f22f
|
|||
|
c7774450bf
|
|||
|
9375255452
|
|||
|
b8b3a16589
|
|||
|
e1d86c77d0
|
|||
|
001d33eabb
|
|||
|
2845425099
|
|||
|
c56b9ec3ce
|
|||
|
68c81577a4
|
|||
|
b5fb8772fe
|
|||
|
5741e069ad
|
|||
|
df89ddcdf5
|
|||
|
c9e1261af2
|
|||
|
d5efc86d85
|
|||
|
430b655785
|
|||
|
1cffa358b8
|
|||
|
ca89112a8e
|
|||
|
65f02a5a7a
|
|||
|
9ccf29903e
|
|||
|
e4b8c9748a
|
|||
|
3318c30cee
|
|||
|
b9aba98cd5
|
|||
|
55fdc41137
|
|||
|
c9790e5823
|
|||
|
fa924eac15
|
|||
|
db4e411dfd
|
|||
|
48aee1e76c
|
|||
|
2a2ace603b
|
|||
|
25f9ac71ca
|
|||
|
61e2801838
|
|||
|
e60b8ee238
|
|||
|
dc0ea5a59c
|
|||
|
10e704cd73
|
|||
|
8004cc0537
|
|||
|
0a2373f407
|
|||
|
96f87eaf5f
|
|||
|
e9bd687b8f
|
|||
|
3ffa38cf98
|
|||
|
a770c4bcca
|
|||
|
f648a6e698
|
|||
|
a72a12b96d
|
|||
|
591c54b5f7
|
|||
|
a6a54f34cf
|
|||
|
f7811961b5
|
|||
|
ee778e1177
|
|||
|
5787a662ed
|
|||
|
fce654f3c7
|
|||
|
0f052c3465
|
|||
|
c733810fdc
|
|||
|
5130cb013b
|
|||
|
991e540c11
|
|||
|
a34d9b7b89
|
|||
|
4e62f559fa
|
|||
|
8c3d2b6740
|
|||
|
b036c9861f
|
28
.cirrus.yml
Normal file
28
.cirrus.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
freebsd_instance:
|
||||
image_family: freebsd-13-2
|
||||
|
||||
build_task:
|
||||
name: build
|
||||
env:
|
||||
GHC_VER: 9.2.4
|
||||
CABAL_VER: 3.8.1.0
|
||||
ARTIFACT: "x86_64-portbld-freebsd-ghcup"
|
||||
ARCH: 64
|
||||
RUNNER_OS: FreeBSD
|
||||
DISTRO: na
|
||||
GITHUB_WORKSPACE: ${CIRRUS_WORKING_DIR}
|
||||
JSON_VERSION: "0.0.7"
|
||||
CIRRUS_CLONE_SUBMODULES: true
|
||||
AWS_ACCESS_KEY_ID: ENCRYPTED[6ed6287e2dd78ab5f84b22232c5245834ab042bd8ba443883aaf4b4d1ecc0481add1fdfad5ae6f6a8cfb418e6f19b2fc]
|
||||
AWS_SECRET_ACCESS_KEY: ENCRYPTED[16f3cda2954c7cee99444e6788eb5997382aa4ce1477e7523fef2586077541f43b5c816156961fc6b4677259679875a7]
|
||||
S3_HOST: ENCRYPTED[ce961780a33159f7d1d8046956b5ac6ebc3bfc8149428e5f538576cda51d9f3d0c35b79cdd1e325793639ff6e31f889d]
|
||||
install_script:
|
||||
- sed -i.bak -e 's/quarterly/latest/' /etc/pkg/FreeBSD.conf
|
||||
- pkg install -y ghc hs-cabal-install git bash misc/compat10x misc/compat11x misc/compat12x gmake llvm14
|
||||
script:
|
||||
- tzsetup Etc/GMT
|
||||
- adjkerntz -a
|
||||
- bash .github/scripts/build.sh
|
||||
- bash .github/scripts/test.sh
|
||||
binaries_artifacts:
|
||||
path: "out/*"
|
||||
4946
.github/ghc-8.10.3-linux.alpine.files
vendored
Normal file
4946
.github/ghc-8.10.3-linux.alpine.files
vendored
Normal file
File diff suppressed because it is too large
Load Diff
8734
.github/ghc-8.10.3-linux.files
vendored
Normal file
8734
.github/ghc-8.10.3-linux.files
vendored
Normal file
File diff suppressed because it is too large
Load Diff
10321
.github/ghc-8.10.3-windows.files
vendored
Normal file
10321
.github/ghc-8.10.3-windows.files
vendored
Normal file
File diff suppressed because it is too large
Load Diff
37
.github/ghcup-run.files
vendored
Normal file
37
.github/ghcup-run.files
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
.
|
||||
./cabal
|
||||
./ghc
|
||||
./ghc-8.10.7
|
||||
./ghc-pkg
|
||||
./ghc-pkg-8.10.7
|
||||
./ghci
|
||||
./ghci-8.10.7
|
||||
./haddock
|
||||
./haddock-8.10.7
|
||||
./haskell-language-server-8.10.6
|
||||
./haskell-language-server-8.10.6~1.6.1.0
|
||||
./haskell-language-server-8.10.7
|
||||
./haskell-language-server-8.10.7~1.6.1.0
|
||||
./haskell-language-server-8.6.5
|
||||
./haskell-language-server-8.6.5~1.6.1.0
|
||||
./haskell-language-server-8.8.4
|
||||
./haskell-language-server-8.8.4~1.6.1.0
|
||||
./haskell-language-server-9.0.1
|
||||
./haskell-language-server-9.0.1~1.6.1.0
|
||||
./haskell-language-server-9.0.2
|
||||
./haskell-language-server-9.0.2~1.6.1.0
|
||||
./haskell-language-server-9.2.1
|
||||
./haskell-language-server-9.2.1~1.6.1.0
|
||||
./haskell-language-server-wrapper
|
||||
./haskell-language-server-wrapper-1.6.1.0
|
||||
./hp2ps
|
||||
./hp2ps-8.10.7
|
||||
./hpc
|
||||
./hpc-8.10.7
|
||||
./hsc2hs
|
||||
./hsc2hs-8.10.7
|
||||
./runghc
|
||||
./runghc-8.10.7
|
||||
./runhaskell
|
||||
./runhaskell-8.10.7
|
||||
./stack
|
||||
31
.github/ghcup-run.files.alpine
vendored
Normal file
31
.github/ghcup-run.files.alpine
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
.
|
||||
./cabal
|
||||
./ghc
|
||||
./ghc-8.10.7
|
||||
./ghc-pkg
|
||||
./ghc-pkg-8.10.7
|
||||
./ghci
|
||||
./ghci-8.10.7
|
||||
./haddock
|
||||
./haddock-8.10.7
|
||||
./haskell-language-server-8.10.7
|
||||
./haskell-language-server-8.10.7~1.6.1.0
|
||||
./haskell-language-server-8.8.4
|
||||
./haskell-language-server-8.8.4~1.6.1.0
|
||||
./haskell-language-server-9.0.2
|
||||
./haskell-language-server-9.0.2~1.6.1.0
|
||||
./haskell-language-server-9.2.1
|
||||
./haskell-language-server-9.2.1~1.6.1.0
|
||||
./haskell-language-server-wrapper
|
||||
./haskell-language-server-wrapper-1.6.1.0
|
||||
./hp2ps
|
||||
./hp2ps-8.10.7
|
||||
./hpc
|
||||
./hpc-8.10.7
|
||||
./hsc2hs
|
||||
./hsc2hs-8.10.7
|
||||
./runghc
|
||||
./runghc-8.10.7
|
||||
./runhaskell
|
||||
./runhaskell-8.10.7
|
||||
./stack
|
||||
81
.github/ghcup-run.files.windows
vendored
Normal file
81
.github/ghcup-run.files.windows
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
.
|
||||
./cabal.exe
|
||||
./cabal.shim
|
||||
./ghc-8.10.7.exe
|
||||
./ghc-8.10.7.shim
|
||||
./ghc-pkg-8.10.7.exe
|
||||
./ghc-pkg-8.10.7.shim
|
||||
./ghc-pkg.exe
|
||||
./ghc-pkg.shim
|
||||
./ghc.exe
|
||||
./ghc.shim
|
||||
./ghci-8.10.7.exe
|
||||
./ghci-8.10.7.shim
|
||||
./ghci.exe
|
||||
./ghci.shim
|
||||
./ghcii-8.10.7.sh-8.10.7.exe
|
||||
./ghcii-8.10.7.sh-8.10.7.shim
|
||||
./ghcii-8.10.7.sh.exe
|
||||
./ghcii-8.10.7.sh.shim
|
||||
./ghcii.sh-8.10.7.exe
|
||||
./ghcii.sh-8.10.7.shim
|
||||
./ghcii.sh.exe
|
||||
./ghcii.sh.shim
|
||||
./haddock-8.10.7.exe
|
||||
./haddock-8.10.7.shim
|
||||
./haddock.exe
|
||||
./haddock.shim
|
||||
./haskell-language-server-8.10.6.exe
|
||||
./haskell-language-server-8.10.6.shim
|
||||
./haskell-language-server-8.10.6~1.6.1.0.exe
|
||||
./haskell-language-server-8.10.6~1.6.1.0.shim
|
||||
./haskell-language-server-8.10.7.exe
|
||||
./haskell-language-server-8.10.7.shim
|
||||
./haskell-language-server-8.10.7~1.6.1.0.exe
|
||||
./haskell-language-server-8.10.7~1.6.1.0.shim
|
||||
./haskell-language-server-8.6.5.exe
|
||||
./haskell-language-server-8.6.5.shim
|
||||
./haskell-language-server-8.6.5~1.6.1.0.exe
|
||||
./haskell-language-server-8.6.5~1.6.1.0.shim
|
||||
./haskell-language-server-8.8.4.exe
|
||||
./haskell-language-server-8.8.4.shim
|
||||
./haskell-language-server-8.8.4~1.6.1.0.exe
|
||||
./haskell-language-server-8.8.4~1.6.1.0.shim
|
||||
./haskell-language-server-9.0.1.exe
|
||||
./haskell-language-server-9.0.1.shim
|
||||
./haskell-language-server-9.0.1~1.6.1.0.exe
|
||||
./haskell-language-server-9.0.1~1.6.1.0.shim
|
||||
./haskell-language-server-9.0.2.exe
|
||||
./haskell-language-server-9.0.2.shim
|
||||
./haskell-language-server-9.0.2~1.6.1.0.exe
|
||||
./haskell-language-server-9.0.2~1.6.1.0.shim
|
||||
./haskell-language-server-9.2.1.exe
|
||||
./haskell-language-server-9.2.1.shim
|
||||
./haskell-language-server-9.2.1~1.6.1.0.exe
|
||||
./haskell-language-server-9.2.1~1.6.1.0.shim
|
||||
./haskell-language-server-wrapper-1.6.1.0.exe
|
||||
./haskell-language-server-wrapper-1.6.1.0.shim
|
||||
./haskell-language-server-wrapper.exe
|
||||
./haskell-language-server-wrapper.shim
|
||||
./hp2ps-8.10.7.exe
|
||||
./hp2ps-8.10.7.shim
|
||||
./hp2ps.exe
|
||||
./hp2ps.shim
|
||||
./hpc-8.10.7.exe
|
||||
./hpc-8.10.7.shim
|
||||
./hpc.exe
|
||||
./hpc.shim
|
||||
./hsc2hs-8.10.7.exe
|
||||
./hsc2hs-8.10.7.shim
|
||||
./hsc2hs.exe
|
||||
./hsc2hs.shim
|
||||
./runghc-8.10.7.exe
|
||||
./runghc-8.10.7.shim
|
||||
./runghc.exe
|
||||
./runghc.shim
|
||||
./runhaskell-8.10.7.exe
|
||||
./runhaskell-8.10.7.shim
|
||||
./runhaskell.exe
|
||||
./runhaskell.shim
|
||||
./stack.exe
|
||||
./stack.shim
|
||||
18
.github/scripts/bootstrap.sh
vendored
Normal file
18
.github/scripts/bootstrap.sh
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eux
|
||||
|
||||
. .github/scripts/env.sh
|
||||
|
||||
mkdir -p "$CI_PROJECT_DIR"/.local/bin
|
||||
|
||||
git describe --always
|
||||
|
||||
### build
|
||||
|
||||
./scripts/bootstrap/bootstrap-haskell
|
||||
|
||||
[ "$(ghc --numeric-version)" = "${BOOTSTRAP_HASKELL_GHC_VERSION}" ]
|
||||
# https://github.com/actions/runner-images/issues/7061
|
||||
[ "$(ghcup config | grep --color=never meta-mode)" = "meta-mode: Lax" ]
|
||||
|
||||
27
.github/scripts/brew.sh
vendored
Normal file
27
.github/scripts/brew.sh
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eux
|
||||
|
||||
. .github/scripts/env.sh
|
||||
|
||||
if [ -e "$HOME/.brew" ] ; then
|
||||
(
|
||||
cd "$HOME/.brew"
|
||||
git fetch --depth 1
|
||||
git reset --hard origin/master
|
||||
)
|
||||
else
|
||||
git clone --depth=1 https://github.com/Homebrew/brew "$HOME/.brew"
|
||||
fi
|
||||
export PATH="$HOME/.brew/bin:$HOME/.brew/sbin:$PATH"
|
||||
|
||||
mkdir -p $CI_PROJECT_DIR/.brew_cache
|
||||
export HOMEBREW_CACHE=$CI_PROJECT_DIR/.brew_cache
|
||||
mkdir -p $CI_PROJECT_DIR/.brew_logs
|
||||
export HOMEBREW_LOGS=$CI_PROJECT_DIR/.brew_logs
|
||||
mkdir -p /private/tmp/.brew_tmp
|
||||
export HOMEBREW_TEMP=/private/tmp/.brew_tmp
|
||||
|
||||
brew update
|
||||
brew install ${1+"$@"}
|
||||
|
||||
35
.github/scripts/build.sh
vendored
Normal file
35
.github/scripts/build.sh
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eux
|
||||
|
||||
. .github/scripts/common.sh
|
||||
|
||||
git_describe
|
||||
|
||||
# ensure ghcup
|
||||
install_ghcup
|
||||
|
||||
# ensure cabal-cache
|
||||
download_cabal_cache "$HOME/.local/bin/cabal-cache"
|
||||
|
||||
# install toolchain (if necessary)
|
||||
ghcup -v install ghc --set --force "$GHC_VER"
|
||||
ghcup -v install cabal --force "$CABAL_VER"
|
||||
ghc --version
|
||||
cabal --version
|
||||
GHC="ghc-${GHC_VER}"
|
||||
|
||||
# build
|
||||
ecabal update
|
||||
build_with_cache --project-file=cabal.project.release -w "${GHC}" --enable-tests
|
||||
|
||||
# set up artifacts
|
||||
mkdir -p out
|
||||
binary=$(cabal --project-file=cabal.project.release list-bin ghcup)
|
||||
binary_test=$(cabal --project-file=cabal.project.release list-bin ghcup-test)
|
||||
ver=$("${binary}" --numeric-version)
|
||||
strip_binary "${binary}"
|
||||
cp "${binary}" "out/${ARTIFACT}-${ver}${ext}"
|
||||
cp "${binary_test}" "out/test-${ARTIFACT}-${ver}${ext}"
|
||||
cp ./dist-newstyle/cache/plan.json "out/${ARTIFACT}.plan.json"
|
||||
|
||||
170
.github/scripts/common.sh
vendored
Normal file
170
.github/scripts/common.sh
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
#!/bin/sh
|
||||
|
||||
. .github/scripts/env.sh
|
||||
|
||||
ecabal() {
|
||||
cabal "$@"
|
||||
}
|
||||
|
||||
nonfatal() {
|
||||
"$@" || "$* failed"
|
||||
}
|
||||
|
||||
sync_from() {
|
||||
if [ "${RUNNER_OS}" != "Windows" ] ; then
|
||||
cabal_store_path="$(dirname "$(cabal help user-config | tail -n 1 | xargs)")/store"
|
||||
fi
|
||||
|
||||
cabal-cache sync-from-archive \
|
||||
--host-name-override=${S3_HOST} \
|
||||
--host-port-override=443 \
|
||||
--host-ssl-override=True \
|
||||
--region us-west-2 \
|
||||
$([ "${RUNNER_OS}" != "Windows" ] && echo --store-path="$cabal_store_path") \
|
||||
--archive-uri "s3://ghcup-hs/${RUNNER_OS}-${ARCH}-${DISTRO}"
|
||||
}
|
||||
|
||||
sync_to() {
|
||||
if [ "${RUNNER_OS}" != "Windows" ] ; then
|
||||
cabal_store_path="$(dirname "$(cabal help user-config | tail -n 1 | xargs)")/store"
|
||||
fi
|
||||
|
||||
cabal-cache sync-to-archive \
|
||||
--host-name-override=${S3_HOST} \
|
||||
--host-port-override=443 \
|
||||
--host-ssl-override=True \
|
||||
--region us-west-2 \
|
||||
$([ "${RUNNER_OS}" != "Windows" ] && echo --store-path="$cabal_store_path") \
|
||||
--archive-uri "s3://ghcup-hs/${RUNNER_OS}-${ARCH}-${DISTRO}"
|
||||
}
|
||||
|
||||
raw_eghcup() {
|
||||
"$GHCUP_BIN/ghcup${ext}" -v -c "$@"
|
||||
}
|
||||
|
||||
eghcup() {
|
||||
if [ "${OS}" = "Windows" ] ; then
|
||||
"$GHCUP_BIN/ghcup${ext}" -c -s "file:/$CI_PROJECT_DIR/data/metadata/ghcup-${JSON_VERSION}.yaml" "$@"
|
||||
else
|
||||
"$GHCUP_BIN/ghcup${ext}" -c -s "file://$CI_PROJECT_DIR/data/metadata/ghcup-${JSON_VERSION}.yaml" "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
sha_sum() {
|
||||
if [ "${OS}" = "FreeBSD" ] ; then
|
||||
sha256 "$@"
|
||||
else
|
||||
sha256sum "$@"
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
git_describe() {
|
||||
git config --global --get-all safe.directory | grep '^\*$' || git config --global --add safe.directory "*"
|
||||
git describe --always
|
||||
}
|
||||
|
||||
download_cabal_cache() {
|
||||
(
|
||||
set -e
|
||||
mkdir -p "$HOME/.local/bin"
|
||||
dest="$HOME/.local/bin/cabal-cache"
|
||||
url=""
|
||||
exe=""
|
||||
cd /tmp
|
||||
case "${RUNNER_OS}" in
|
||||
"Linux")
|
||||
case "${ARCH}" in
|
||||
"32") url=https://downloads.haskell.org/~ghcup/unofficial-bindists/cabal-cache/experimental5/i386-linux-cabal-cache
|
||||
;;
|
||||
"64") url=https://downloads.haskell.org/~ghcup/unofficial-bindists/cabal-cache/experimental5/x86_64-linux-cabal-cache
|
||||
;;
|
||||
"ARM64") url=https://downloads.haskell.org/~ghcup/unofficial-bindists/cabal-cache/experimental5/aarch64-linux-cabal-cache
|
||||
;;
|
||||
"ARM") url=https://downloads.haskell.org/~ghcup/unofficial-bindists/cabal-cache/experimental5/armv7-linux-cabal-cache
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
"FreeBSD")
|
||||
url=https://downloads.haskell.org/~ghcup/unofficial-bindists/cabal-cache/experimental5/x86_64-portbld-freebsd-cabal-cache
|
||||
;;
|
||||
"Windows")
|
||||
exe=".exe"
|
||||
url=https://downloads.haskell.org/~ghcup/unofficial-bindists/cabal-cache/experimental5/x86_64-mingw64-cabal-cache
|
||||
;;
|
||||
"macOS")
|
||||
case "${ARCH}" in
|
||||
"ARM64") url=https://downloads.haskell.org/~ghcup/unofficial-bindists/cabal-cache/experimental5/aarch64-apple-darwin-cabal-cache
|
||||
;;
|
||||
"64") url=https://downloads.haskell.org/~ghcup/unofficial-bindists/cabal-cache/experimental5/x86_64-apple-darwin-cabal-cache
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -n "${url}" ] ; then
|
||||
case "${url##*.}" in
|
||||
"gz")
|
||||
curl -L -o - "${url}" | gunzip > cabal-cache${exe}
|
||||
;;
|
||||
*)
|
||||
curl -o cabal-cache${exe} -L "${url}"
|
||||
;;
|
||||
esac
|
||||
sha_sum cabal-cache${exe}
|
||||
mv "cabal-cache${exe}" "${dest}${exe}"
|
||||
chmod +x "${dest}${exe}"
|
||||
fi
|
||||
)
|
||||
}
|
||||
|
||||
build_with_cache() {
|
||||
ecabal configure "$@"
|
||||
ecabal build --dependencies-only "$@" --dry-run
|
||||
sync_from
|
||||
ecabal build --dependencies-only "$@" || sync_to
|
||||
sync_to
|
||||
ecabal build "$@"
|
||||
sync_to
|
||||
}
|
||||
|
||||
install_ghcup() {
|
||||
case "${RUNNER_OS}" in
|
||||
"Linux")
|
||||
case "${ARCH}" in
|
||||
"ARM"*)
|
||||
if command -v ghcup ; then
|
||||
mkdir -p "$GHCUP_BIN"
|
||||
cp "$(command -v ghcup)" "$GHCUP_BIN/ghcup${ext}"
|
||||
else
|
||||
install_ghcup_curl_sh
|
||||
fi
|
||||
;;
|
||||
*) install_ghcup_curl_sh
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*) install_ghcup_curl_sh
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
install_ghcup_curl_sh() {
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | BOOTSTRAP_HASKELL_NONINTERACTIVE=1 BOOTSTRAP_HASKELL_MINIMAL=1 BOOTSTRAP_HASKELL_INSTALL_NO_STACK=yes sh
|
||||
}
|
||||
|
||||
strip_binary() {
|
||||
(
|
||||
set -e
|
||||
local binary=$1
|
||||
case "$(uname -s)" in
|
||||
"Darwin"|"darwin")
|
||||
;;
|
||||
MSYS_*|MINGW*)
|
||||
;;
|
||||
*)
|
||||
strip -s "${binary}"
|
||||
;;
|
||||
esac
|
||||
)
|
||||
}
|
||||
30
.github/scripts/env.sh
vendored
Normal file
30
.github/scripts/env.sh
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ "${RUNNER_OS}" = "Windows" ] ; then
|
||||
ext=".exe"
|
||||
else
|
||||
ext=''
|
||||
fi
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
export TZ=Asia/Singapore
|
||||
|
||||
export OS="$RUNNER_OS"
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
|
||||
if [ "${RUNNER_OS}" = "Windows" ] ; then
|
||||
# on windows use pwd to get unix style path
|
||||
CI_PROJECT_DIR="$(pwd)"
|
||||
export CI_PROJECT_DIR
|
||||
export GHCUP_INSTALL_BASE_PREFIX="/c"
|
||||
export GHCUP_BIN="$GHCUP_INSTALL_BASE_PREFIX/ghcup/bin"
|
||||
export PATH="$GHCUP_BIN:$PATH"
|
||||
export CABAL_DIR="C:\\Users\\runneradmin\\AppData\\Roaming\\cabal"
|
||||
else
|
||||
export CI_PROJECT_DIR="${GITHUB_WORKSPACE}"
|
||||
export GHCUP_INSTALL_BASE_PREFIX="$CI_PROJECT_DIR"
|
||||
export GHCUP_BIN="$GHCUP_INSTALL_BASE_PREFIX/.ghcup/bin"
|
||||
export PATH="$GHCUP_BIN:$PATH"
|
||||
export CABAL_DIR="$CI_PROJECT_DIR/cabal"
|
||||
export CABAL_CACHE="$CI_PROJECT_DIR/cabal-cache"
|
||||
fi
|
||||
72
.github/scripts/hls.sh
vendored
Normal file
72
.github/scripts/hls.sh
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eux
|
||||
|
||||
. .github/scripts/common.sh
|
||||
|
||||
mkdir -p "$CI_PROJECT_DIR"/.local/bin
|
||||
|
||||
### build
|
||||
|
||||
|
||||
|
||||
if [ "${OS}" = "Windows" ] ; then
|
||||
GHCUP_DIR="${GHCUP_INSTALL_BASE_PREFIX}"/ghcup
|
||||
else
|
||||
GHCUP_DIR="${GHCUP_INSTALL_BASE_PREFIX}"/.ghcup
|
||||
fi
|
||||
|
||||
rm -rf "${GHCUP_DIR}"
|
||||
mkdir -p "${GHCUP_BIN}"
|
||||
|
||||
ls -lah out
|
||||
find out
|
||||
cp "out/${ARTIFACT}"-* "$GHCUP_BIN/ghcup${ext}"
|
||||
chmod +x "$GHCUP_BIN/ghcup${ext}"
|
||||
echo "$PATH"
|
||||
|
||||
"$GHCUP_BIN/ghcup${ext}" --version
|
||||
eghcup --version
|
||||
sha_sum "$GHCUP_BIN/ghcup${ext}"
|
||||
sha_sum "$(raw_eghcup --offline whereis ghcup)"
|
||||
|
||||
git_describe
|
||||
|
||||
eghcup install ghc "${GHC_VERSION}"
|
||||
eghcup install cabal "${CABAL_VERSION}"
|
||||
|
||||
ecabal update
|
||||
|
||||
if ! command -v cabal-cache ; then
|
||||
download_cabal_cache "$HOME/.local/bin/cabal-cache"
|
||||
fi
|
||||
|
||||
if ! cabal-cache version ; then
|
||||
build_cabal_cache "$HOME/.local/bin"
|
||||
fi
|
||||
|
||||
|
||||
eghcup debug-info
|
||||
|
||||
(
|
||||
cd /tmp
|
||||
git clone --depth 1 --branch "${HLS_TARGET_VERSION}" \
|
||||
https://github.com/haskell/haskell-language-server.git \
|
||||
"haskell-language-server-${HLS_TARGET_VERSION}"
|
||||
cd "haskell-language-server-${HLS_TARGET_VERSION}/"
|
||||
ecabal configure -w "ghc-${GHC_VERSION}" --disable-profiling --disable-tests --jobs="$(nproc)"
|
||||
ecabal build --dependencies-only -w "ghc-${GHC_VERSION}" --disable-profiling --disable-tests --jobs="$(nproc)" --dry-run
|
||||
sync_from
|
||||
ecabal build --dependencies-only -w "ghc-${GHC_VERSION}" --disable-profiling --disable-tests --jobs="$(nproc)" || sync_to
|
||||
sync_to
|
||||
)
|
||||
|
||||
eghcup -v compile hls -j "$(nproc)" -g "${HLS_TARGET_VERSION}" --ghc "${GHC_VERSION}"
|
||||
|
||||
[ "$($(eghcup whereis hls "${HLS_TARGET_VERSION}") --numeric-version)" = "${HLS_TARGET_VERSION}" ] ||
|
||||
[ "$($(eghcup whereis hls "${HLS_TARGET_VERSION}") --numeric-version | sed 's/.0$//')" = "${HLS_TARGET_VERSION}" ]
|
||||
|
||||
# nuke
|
||||
eghcup nuke
|
||||
[ ! -e "${GHCUP_DIR}" ]
|
||||
|
||||
262
.github/scripts/test.sh
vendored
Normal file
262
.github/scripts/test.sh
vendored
Normal file
@@ -0,0 +1,262 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eux
|
||||
|
||||
. .github/scripts/common.sh
|
||||
|
||||
|
||||
if [ "${OS}" = "Windows" ] ; then
|
||||
GHCUP_DIR="${GHCUP_INSTALL_BASE_PREFIX}"/ghcup
|
||||
else
|
||||
GHCUP_DIR="${GHCUP_INSTALL_BASE_PREFIX}"/.ghcup
|
||||
fi
|
||||
|
||||
git_describe
|
||||
|
||||
rm -rf "${GHCUP_DIR}"
|
||||
mkdir -p "${GHCUP_BIN}"
|
||||
|
||||
cp "out/${ARTIFACT}"-* "$GHCUP_BIN/ghcup${ext}"
|
||||
cp "out/test-${ARTIFACT}"-* "ghcup-test${ext}"
|
||||
chmod +x "$GHCUP_BIN/ghcup${ext}"
|
||||
chmod +x "ghcup-test${ext}"
|
||||
|
||||
"$GHCUP_BIN/ghcup${ext}" --version
|
||||
eghcup --version
|
||||
sha_sum "$GHCUP_BIN/ghcup${ext}"
|
||||
sha_sum "$(raw_eghcup --offline whereis ghcup)"
|
||||
|
||||
### Haskell test suite
|
||||
|
||||
./ghcup-test${ext}
|
||||
rm ghcup-test${ext}
|
||||
|
||||
### manual cli based testing
|
||||
|
||||
eghcup --numeric-version
|
||||
|
||||
eghcup install ghc ${GHC_VER}
|
||||
eghcup unset ghc ${GHC_VER}
|
||||
ls -lah "$(eghcup whereis -d ghc ${GHC_VER})"
|
||||
[ "`$(eghcup whereis ghc ${GHC_VER}) --numeric-version`" = "${GHC_VER}" ]
|
||||
[ "`eghcup run --ghc ${GHC_VER} -- ghc --numeric-version`" = "${GHC_VER}" ]
|
||||
[ "`ghcup run --ghc ${GHC_VER} -- ghc -e 'Control.Monad.join (Control.Monad.fmap System.IO.putStr System.Environment.getExecutablePath)'`" = "`$(ghcup whereis ghc ${GHC_VER}) -e 'Control.Monad.join (Control.Monad.fmap System.IO.putStr System.Environment.getExecutablePath)'`" ]
|
||||
eghcup set ghc ${GHC_VER}
|
||||
eghcup install cabal ${CABAL_VER}
|
||||
[ "`$(eghcup whereis cabal ${CABAL_VER}) --numeric-version`" = "${CABAL_VER}" ]
|
||||
eghcup unset cabal
|
||||
"$GHCUP_BIN"/cabal --version && exit 1 || echo yes
|
||||
|
||||
# make sure no cabal is set when running 'ghcup run' to check that PATH propagages properly
|
||||
# https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/375
|
||||
[ "`eghcup run --cabal ${CABAL_VER} -- cabal --numeric-version`" = "${CABAL_VER}" ]
|
||||
eghcup set cabal ${CABAL_VER}
|
||||
|
||||
[ "`$(eghcup whereis cabal ${CABAL_VER}) --numeric-version`" = "${CABAL_VER}" ]
|
||||
|
||||
if [ "${OS}" != "FreeBSD" ] ; then
|
||||
if [ "${ARCH}" = "64" ] && [ "${DISTRO}" != "Alpine" ] ; then
|
||||
eghcup run --ghc 8.10.7 --cabal 3.4.1.0 --hls 1.6.1.0 --stack 2.7.3 --install --bindir "$(pwd)/.bin"
|
||||
if [ "${OS}" = "Windows" ] ; then
|
||||
cat "$( cd "$(dirname "$0")" ; pwd -P )/../ghcup-run.files.windows" | sort > expected.txt
|
||||
elif [ "${DISTRO}" = "Alpine" ] ; then
|
||||
cat "$( cd "$(dirname "$0")" ; pwd -P )/../ghcup-run.files.alpine" | sort > expected.txt
|
||||
else
|
||||
cat "$( cd "$(dirname "$0")" ; pwd -P )/../ghcup-run.files" | sort > expected.txt
|
||||
fi
|
||||
(cd ".bin" && find . | sort) > actual.txt
|
||||
diff --strip-trailing-cr -w -u actual.txt expected.txt
|
||||
rm actual.txt expected.txt
|
||||
rm -rf .bin
|
||||
fi
|
||||
fi
|
||||
|
||||
cabal --version
|
||||
|
||||
eghcup debug-info
|
||||
|
||||
# also test etags
|
||||
eghcup list
|
||||
eghcup list -t ghc
|
||||
eghcup list -t cabal
|
||||
|
||||
ghc_ver=$(ghc --numeric-version)
|
||||
ghc --version
|
||||
ghc-${ghc_ver} --version
|
||||
if [ "${OS}" != "Windows" ] ; then
|
||||
ghci --version
|
||||
ghci-${ghc_ver} --version
|
||||
fi
|
||||
|
||||
|
||||
if [ "${OS}" = "macOS" ] && [ "${ARCH}" = "ARM64" ] ; then
|
||||
# missing bindists
|
||||
echo
|
||||
elif [ "${OS}" = "FreeBSD" ] ; then
|
||||
# not enough space
|
||||
echo
|
||||
else
|
||||
# test installing new ghc doesn't mess with currently set GHC
|
||||
# https://gitlab.haskell.org/haskell/ghcup-hs/issues/7
|
||||
if [ "${OS}" = "Linux" ] ; then
|
||||
eghcup --downloader=wget prefetch ghc 8.10.3
|
||||
eghcup --offline install ghc 8.10.3
|
||||
if [ "${ARCH}" = "64" ] ; then
|
||||
if [ "${DISTRO}" = "Alpine" ] ; then
|
||||
(cat "$( cd "$(dirname "$0")" ; pwd -P )/../ghc-8.10.3-linux.alpine.files" | sort) > expected.txt
|
||||
else
|
||||
(cat "$( cd "$(dirname "$0")" ; pwd -P )/../ghc-8.10.3-linux.files" | sort) > expected.txt
|
||||
fi
|
||||
(cd "${GHCUP_DIR}/ghc/8.10.3/" && find . | sort) > actual.txt
|
||||
# ignore docs
|
||||
sed -i '/share\/doc/d' actual.txt
|
||||
sed -i '/share\/doc/d' expected.txt
|
||||
diff --strip-trailing-cr -w -u actual.txt expected.txt
|
||||
rm actual.txt expected.txt
|
||||
fi
|
||||
elif [ "${OS}" = "Windows" ] ; then
|
||||
eghcup prefetch ghc 8.10.3
|
||||
eghcup --offline install ghc 8.10.3
|
||||
(cat "$( cd "$(dirname "$0")" ; pwd -P )/../ghc-8.10.3-windows.files" | sort) > expected.txt
|
||||
(cd "${GHCUP_DIR}/ghc/8.10.3/" && find . | sort) > actual.txt
|
||||
diff --strip-trailing-cr -w -u actual.txt expected.txt
|
||||
rm actual.txt expected.txt
|
||||
else
|
||||
eghcup prefetch ghc 8.10.3
|
||||
eghcup --offline install ghc 8.10.3
|
||||
fi
|
||||
[ "$(ghc --numeric-version)" = "${ghc_ver}" ]
|
||||
eghcup --offline set 8.10.3
|
||||
eghcup set 8.10.3
|
||||
[ "$(ghc --numeric-version)" = "8.10.3" ]
|
||||
eghcup set ${GHC_VER}
|
||||
[ "$(ghc --numeric-version)" = "${ghc_ver}" ]
|
||||
eghcup unset ghc
|
||||
"$GHCUP_BIN"/ghc --numeric-version && exit 1 || echo yes
|
||||
eghcup set ${GHC_VER}
|
||||
eghcup --offline rm 8.10.3
|
||||
[ "$(ghc --numeric-version)" = "${ghc_ver}" ]
|
||||
|
||||
|
||||
ls -lah "$GHCUP_BIN"
|
||||
|
||||
if [ "${OS}" = "macOS" ] ; then
|
||||
eghcup install hls
|
||||
$(eghcup whereis hls) --version
|
||||
|
||||
eghcup install stack
|
||||
$(eghcup whereis stack) --version
|
||||
elif [ "${OS}" = "Linux" ] ; then
|
||||
if [ "${ARCH}" = "64" ] && [ "${DISTRO}" != "Alpine" ] ; then
|
||||
eghcup install hls
|
||||
haskell-language-server-wrapper --version
|
||||
eghcup unset hls
|
||||
"$GHCUP_BIN"/haskell-language-server-wrapper --version && exit 1 || echo yes
|
||||
|
||||
eghcup install stack
|
||||
stack --version
|
||||
eghcup unset stack
|
||||
"$GHCUP_BIN"/stack --version && exit 1 || echo yes
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# check that lazy loading works for 'whereis'
|
||||
cp "$CI_PROJECT_DIR/data/metadata/ghcup-${JSON_VERSION}.yaml" "$CI_PROJECT_DIR/data/metadata/ghcup-${JSON_VERSION}.yaml.bak"
|
||||
echo '**' > "$CI_PROJECT_DIR/data/metadata/ghcup-${JSON_VERSION}.yaml"
|
||||
eghcup whereis ghc $(ghc --numeric-version)
|
||||
mv -f "$CI_PROJECT_DIR/data/metadata/ghcup-${JSON_VERSION}.yaml.bak" "$CI_PROJECT_DIR/data/metadata/ghcup-${JSON_VERSION}.yaml"
|
||||
|
||||
eghcup rm $(ghc --numeric-version)
|
||||
|
||||
# https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/116
|
||||
if [ "${OS}" = "Linux" ] ; then
|
||||
if [ "${ARCH}" = "64" ] ; then
|
||||
eghcup install cabal -u https://downloads.haskell.org/~ghcup/unofficial-bindists/cabal/3.7.0.0-pre20220407/cabal-install-3.7-x86_64-linux-alpine.tar.xz 3.4.0.0-rc4
|
||||
eghcup rm cabal 3.4.0.0-rc4
|
||||
fi
|
||||
fi
|
||||
|
||||
eghcup gc -c
|
||||
|
||||
# test etags
|
||||
rm -f "${GHCUP_DIR}/cache/ghcup-${JSON_VERSION}.yaml"
|
||||
raw_eghcup -s https://www.haskell.org/ghcup/data/ghcup-${JSON_VERSION}.yaml list
|
||||
# snapshot yaml and etags file
|
||||
etag=$(cat "${GHCUP_DIR}/cache/ghcup-${JSON_VERSION}.yaml.etags")
|
||||
sha=$(sha_sum "${GHCUP_DIR}/cache/ghcup-${JSON_VERSION}.yaml")
|
||||
# invalidate access time timer, which is 5minutes, so we re-download
|
||||
touch -a -m -t '199901010101' "${GHCUP_DIR}/cache/ghcup-${JSON_VERSION}.yaml"
|
||||
# redownload same file with some newlines added
|
||||
raw_eghcup -s https://raw.githubusercontent.com/haskell/ghcup-metadata/exp/ghcup-0.0.7.yaml list
|
||||
# snapshot new yaml and etags file
|
||||
etag2=$(cat "${GHCUP_DIR}/cache/ghcup-${JSON_VERSION}.yaml.etags")
|
||||
sha2=$(sha_sum "${GHCUP_DIR}/cache/ghcup-${JSON_VERSION}.yaml")
|
||||
# compare
|
||||
[ "${etag}" != "${etag2}" ]
|
||||
[ "${sha}" != "${sha2}" ]
|
||||
# invalidate access time timer, which is 5minutes, but don't expect a re-download
|
||||
touch -a -m -t '199901010101' "${GHCUP_DIR}/cache/ghcup-${JSON_VERSION}.yaml"
|
||||
# this time, we expect the same hash and etag
|
||||
raw_eghcup -s https://raw.githubusercontent.com/haskell/ghcup-metadata/exp/ghcup-0.0.7.yaml list
|
||||
etag3=$(cat "${GHCUP_DIR}/cache/ghcup-${JSON_VERSION}.yaml.etags")
|
||||
sha3=$(sha_sum "${GHCUP_DIR}/cache/ghcup-${JSON_VERSION}.yaml")
|
||||
[ "${etag2}" = "${etag3}" ]
|
||||
[ "${sha2}" = "${sha3}" ]
|
||||
|
||||
# test isolated installs
|
||||
if [ "${DISTRO}" != "Alpine" ] ; then
|
||||
eghcup install ghc -i "$(pwd)/isolated" 8.10.5
|
||||
[ "$(isolated/bin/ghc --numeric-version)" = "8.10.5" ]
|
||||
! eghcup install ghc -i "$(pwd)/isolated" 8.10.5
|
||||
if [ "${ARCH}" = "64" ] ; then
|
||||
if [ "${OS}" = "Linux" ] || [ "${OS}" = "Windows" ] ; then
|
||||
eghcup install cabal -i "$(pwd)/isolated" 3.4.0.0
|
||||
[ "$(isolated/cabal --numeric-version)" = "3.4.0.0" ]
|
||||
eghcup install stack -i "$(pwd)/isolated" 2.7.3
|
||||
[ "$(isolated/stack --numeric-version)" = "2.7.3" ]
|
||||
eghcup install hls -i "$(pwd)/isolated" 1.3.0
|
||||
[ "$(isolated/haskell-language-server-wrapper --numeric-version)" = "1.3.0" ] ||
|
||||
[ "$(isolated/haskell-language-server-wrapper --numeric-version)" = "1.3.0.0" ]
|
||||
|
||||
# test that isolated installs don't clean up target directory
|
||||
cat <<EOF > "${GHCUP_BIN}/gmake"
|
||||
#!/bin/bash
|
||||
exit 1
|
||||
EOF
|
||||
chmod +x "${GHCUP_BIN}/gmake"
|
||||
mkdir isolated_tainted/
|
||||
touch isolated_tainted/lol
|
||||
|
||||
! eghcup install ghc -i "$(pwd)/isolated_tainted" 8.10.5 --force
|
||||
[ -e "$(pwd)/isolated_tainted/lol" ]
|
||||
rm "${GHCUP_BIN}/gmake"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
eghcup upgrade
|
||||
eghcup upgrade -f
|
||||
|
||||
# restore old ghcup, because we want to test nuke
|
||||
cp "out/${ARTIFACT}"-* "$GHCUP_BIN/ghcup${ext}"
|
||||
chmod +x "$GHCUP_BIN/ghcup${ext}"
|
||||
|
||||
# test that doing fishy symlinks into GHCup dir doesn't cause weird stuff on 'ghcup nuke'
|
||||
mkdir no_nuke/
|
||||
mkdir no_nuke/bar
|
||||
echo 'foo' > no_nuke/file
|
||||
echo 'bar' > no_nuke/bar/file
|
||||
ln -s "$CI_PROJECT_DIR"/no_nuke/ "${GHCUP_DIR}"/cache/no_nuke
|
||||
ln -s "$CI_PROJECT_DIR"/no_nuke/ "${GHCUP_DIR}"/logs/no_nuke
|
||||
|
||||
# nuke
|
||||
eghcup nuke
|
||||
[ ! -e "${GHCUP_DIR}" ]
|
||||
|
||||
# make sure nuke doesn't resolve symlinks
|
||||
[ -e "$CI_PROJECT_DIR"/no_nuke/file ]
|
||||
[ -e "$CI_PROJECT_DIR"/no_nuke/bar/file ]
|
||||
|
||||
58
.github/workflows/bootstrap.yaml
vendored
Normal file
58
.github/workflows/bootstrap.yaml
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
name: Bootstrap tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
bootstrap:
|
||||
name: bootstrap
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
BOOTSTRAP_HASKELL_CABAL_VERSION: 3.6.2.0
|
||||
BOOTSTRAP_HASKELL_GHC_VERSION: 8.10.7
|
||||
BOOTSTRAP_HASKELL_NONINTERACTIVE: yes
|
||||
ARCH: 64
|
||||
JSON_VERSION: "0.0.7"
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
DISTRO: Ubuntu
|
||||
- os: macOS-11
|
||||
DISTRO: na
|
||||
- os: windows-latest
|
||||
DISTRO: na
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
- if: runner.os == 'Linux'
|
||||
name: Run bootstrap
|
||||
run: |
|
||||
sudo apt-get install -y libnuma-dev zlib1g-dev libgmp-dev libgmp10 libssl-dev liblzma-dev libbz2-dev git wget lsb-release software-properties-common gnupg2 apt-transport-https gcc autoconf automake build-essential curl gzip
|
||||
sh ./.github/scripts/bootstrap.sh
|
||||
env:
|
||||
DISTRO: ${{ matrix.DISTRO }}
|
||||
|
||||
- if: runner.os == 'macOS'
|
||||
name: Run bootstrap
|
||||
run: sh ./.github/scripts/bootstrap.sh
|
||||
env:
|
||||
DISTRO: ${{ matrix.DISTRO }}
|
||||
|
||||
- if: runner.os == 'Windows'
|
||||
name: Run bootstrap
|
||||
run: |
|
||||
$curDir = Get-Location
|
||||
Write-Host "Current Working Directory: $curDir"
|
||||
./scripts/bootstrap/bootstrap-haskell.ps1 -InstallDir ${GITHUB_WORKSPACE} -BootstrapUrl ("{0}/scripts/bootstrap/bootstrap-haskell" -f $curDir) -InBash
|
||||
shell: pwsh
|
||||
37
.github/workflows/cache.yaml
vendored
Normal file
37
.github/workflows/cache.yaml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: Cache eviction
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
key:
|
||||
description: Which cache to evict
|
||||
required: true
|
||||
default: '/'
|
||||
type: choice
|
||||
options:
|
||||
- FreeBSD-64-na
|
||||
- Linux-32-Alpine
|
||||
- Linux-64-Alpine
|
||||
- Linux-64-Ubuntu
|
||||
- Linux-ARM-Ubuntu
|
||||
- Linux-ARM64-Ubuntu
|
||||
- Windows-64-na
|
||||
- macOS-64-na
|
||||
- macOS-ARM64-na
|
||||
- /
|
||||
jobs:
|
||||
evict:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Remove from S3
|
||||
uses: vitorsgomes/s3-rm-action@master
|
||||
with:
|
||||
args: --recursive
|
||||
env:
|
||||
AWS_S3_ENDPOINT: https://${{ secrets.S3_HOST }}
|
||||
AWS_S3_BUCKET: ghcup-hs
|
||||
AWS_REGION: us-west-2
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
PATH_TO_DELETE: ${{ github.event.inputs.key }}
|
||||
125
.github/workflows/docker.yaml
vendored
Normal file
125
.github/workflows/docker.yaml
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
name: Docker image builds
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
jobs:
|
||||
docker-alpine32:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and push (alpine 32bit)
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: ./docker/alpine32
|
||||
push: true
|
||||
tags: hasufell/i386-alpine-haskell:3.12
|
||||
platforms: |
|
||||
linux/i386
|
||||
linux/amd64
|
||||
|
||||
docker-alpine:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and push (alpine 64bit)
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: ./docker/alpine64
|
||||
push: true
|
||||
tags: hasufell/alpine-haskell:3.12
|
||||
platforms: linux/amd64
|
||||
|
||||
docker-arm32:
|
||||
runs-on: [self-hosted, Linux, ARM64]
|
||||
steps:
|
||||
- uses: docker://arm64v8/ubuntu:focal
|
||||
name: Cleanup (aarch64 linux)
|
||||
with:
|
||||
args: "find . -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push (debian buster)
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: ./docker/arm32v7/buster
|
||||
push: true
|
||||
tags: hasufell/arm32v7-debian-haskell:10
|
||||
platforms: linux/arm
|
||||
|
||||
- name: Build and push (ubuntu focal)
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: ./docker/arm32v7/focal
|
||||
push: true
|
||||
tags: hasufell/arm32v7-ubuntu-haskell:focal
|
||||
platforms: linux/arm
|
||||
|
||||
docker-aarch:
|
||||
runs-on: [self-hosted, Linux, ARM64]
|
||||
steps:
|
||||
- uses: docker://arm64v8/ubuntu:focal
|
||||
name: Cleanup (aarch64 linux)
|
||||
with:
|
||||
args: "find . -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push (debian buster)
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: ./docker/arm64v8/buster
|
||||
push: true
|
||||
tags: hasufell/arm64v8-debian-haskell:10
|
||||
platforms: linux/arm64
|
||||
|
||||
- name: Build and push (ubuntu focal)
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: ./docker/arm64v8/focal
|
||||
push: true
|
||||
tags: hasufell/arm64v8-ubuntu-haskell:focal
|
||||
platforms: linux/arm64
|
||||
26
.github/workflows/hlint.yaml
vendored
Normal file
26
.github/workflows/hlint.yaml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Hlint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
hlint:
|
||||
name: hlint
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
JSON_VERSION: "0.0.7"
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
- name: Run hlint
|
||||
run: curl -sSL https://raw.github.com/ndmitchell/hlint/master/misc/run.sh | sh -s -- -r lib/ test/
|
||||
33
.github/workflows/mkdocs.yaml
vendored
Normal file
33
.github/workflows/mkdocs.yaml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: MkDocs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
mkdocs:
|
||||
name: mkdocs
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
JSON_VERSION: "0.0.7"
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
- name: Install mkdocs deps
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y python3-pip
|
||||
sudo pip3 install mkdocs
|
||||
|
||||
- name: Run mkdocs
|
||||
run: |
|
||||
mkdocs build
|
||||
581
.github/workflows/release.yaml
vendored
581
.github/workflows/release.yaml
vendored
@@ -1,109 +1,514 @@
|
||||
name: Create Release
|
||||
name: Build and release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
schedule:
|
||||
- cron: '0 2 * * *'
|
||||
|
||||
jobs:
|
||||
draft_release:
|
||||
name: Draft Release
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
|
||||
steps:
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Release ${{ github.ref }}
|
||||
body: |
|
||||
Changes in this Release
|
||||
- First Change
|
||||
- Second Change
|
||||
draft: true
|
||||
prerelease: false
|
||||
|
||||
release-mac:
|
||||
name: Create Release
|
||||
needs: draft_release
|
||||
build-linux:
|
||||
name: Build linux binary
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
MACOSX_DEPLOYMENT_TARGET: 10.13
|
||||
CABAL_VER: 3.8.1.0
|
||||
JSON_VERSION: "0.0.7"
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
S3_HOST: ${{ secrets.S3_HOST }}
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
os:
|
||||
- macOS-10.15
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
ARTIFACT: "i386-linux-ghcup"
|
||||
GHC_VER: 8.10.7
|
||||
ARCH: 32
|
||||
- os: ubuntu-latest
|
||||
ARTIFACT: "x86_64-linux-ghcup"
|
||||
GHC_VER: 8.10.7
|
||||
ARCH: 64
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- uses: haskell/actions/setup@v1.2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ghc-version: 8.10.7
|
||||
cabal-version: 3.6.2.0
|
||||
submodules: 'true'
|
||||
|
||||
- name: create ~/.local/bin
|
||||
run: mkdir -p "$HOME/.local/bin"
|
||||
shell: bash
|
||||
|
||||
- name: Add ~/.local/bin to PATH
|
||||
run: echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||
shell: bash
|
||||
|
||||
- name: Update cabal cache
|
||||
run: cabal update
|
||||
shell: bash
|
||||
|
||||
- name: Install cabal dependencies
|
||||
run: cabal build --only-dependencies --constraint="zlib +bundled-c-zlib" --constraint="lzma +static" -ftui
|
||||
shell: bash
|
||||
|
||||
- name: Build
|
||||
run: cabal build --constraint="zlib +bundled-c-zlib" --constraint="lzma +static" -ftui
|
||||
shell: bash
|
||||
|
||||
- name: Install
|
||||
run: cp "$(cabal list-bin exe:ghcup)" ~/.local/bin/ghcup
|
||||
shell: bash
|
||||
|
||||
- name: Strip
|
||||
run: strip ~/.local/bin/ghcup
|
||||
shell: bash
|
||||
|
||||
- name: Run tests
|
||||
run: cabal test --constraint="zlib +bundled-c-zlib" --constraint="lzma +static" all
|
||||
shell: bash
|
||||
|
||||
- name: Install git
|
||||
run: brew install git
|
||||
|
||||
- name: set HOME
|
||||
run: echo "HOME=$HOME" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Set ASSET_PATH
|
||||
run: echo "ASSET_PATH=$HOME/.local/bin/ghcup" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Upload Release Asset
|
||||
id: upload-release-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
- if: matrix.ARCH == '32'
|
||||
name: Run build (32 bit linux)
|
||||
uses: docker://hasufell/i386-alpine-haskell:3.12
|
||||
with:
|
||||
args: sh .github/scripts/build.sh
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
ARTIFACT: ${{ matrix.ARTIFACT }}
|
||||
ARCH: ${{ matrix.ARCH }}
|
||||
GHC_VER: ${{ matrix.GHC_VER }}
|
||||
DISTRO: Alpine
|
||||
AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_ACCESS_KEY_ID: ${{ env.AWS_ACCESS_KEY_ID }}
|
||||
S3_HOST: ${{ env.S3_HOST }}
|
||||
|
||||
- if: matrix.ARCH == '64'
|
||||
name: Run build (64 bit linux)
|
||||
uses: docker://hasufell/alpine-haskell:3.12
|
||||
with:
|
||||
upload_url: ${{ needs.draft_release.outputs.upload_url }}
|
||||
asset_path: ${{ env.ASSET_PATH }}
|
||||
asset_name: ghcup-${{ matrix.os }}
|
||||
asset_content_type: application/octet-stream
|
||||
args: sh .github/scripts/build.sh
|
||||
env:
|
||||
ARTIFACT: ${{ matrix.ARTIFACT }}
|
||||
ARCH: ${{ matrix.ARCH }}
|
||||
GHC_VER: ${{ matrix.GHC_VER }}
|
||||
DISTRO: Alpine
|
||||
AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_ACCESS_KEY_ID: ${{ env.AWS_ACCESS_KEY_ID }}
|
||||
S3_HOST: ${{ env.S3_HOST }}
|
||||
|
||||
- if: always()
|
||||
uses: actions/upload-artifact@v2
|
||||
name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: plan.json
|
||||
path: ./dist-newstyle/cache/plan.json
|
||||
name: artifacts
|
||||
path: |
|
||||
./out/*
|
||||
|
||||
|
||||
build-arm:
|
||||
name: Build ARM binary
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CABAL_VER: 3.6.2.0
|
||||
JSON_VERSION: "0.0.7"
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
S3_HOST: ${{ secrets.S3_HOST }}
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
include:
|
||||
- os: [self-hosted, Linux, ARM64]
|
||||
ARTIFACT: "armv7-linux-ghcup"
|
||||
GHC_VER: 9.2.2
|
||||
ARCH: ARM
|
||||
- os: [self-hosted, Linux, ARM64]
|
||||
ARTIFACT: "aarch64-linux-ghcup"
|
||||
GHC_VER: 9.2.6
|
||||
ARCH: ARM64
|
||||
steps:
|
||||
- uses: docker://arm64v8/debian:10
|
||||
name: Cleanup (aarch64 linux)
|
||||
with:
|
||||
args: "find . -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +"
|
||||
|
||||
- name: git config
|
||||
run: |
|
||||
git config --global --get-all safe.directory | grep '^\*$' || git config --global --add safe.directory "*"
|
||||
shell: bash
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
- if: matrix.ARCH == 'ARM'
|
||||
uses: docker://hasufell/arm32v7-debian-haskell:10
|
||||
name: Run build (armv7 linux)
|
||||
with:
|
||||
args: sh .github/scripts/build.sh
|
||||
env:
|
||||
ARTIFACT: ${{ matrix.ARTIFACT }}
|
||||
ARCH: ${{ matrix.ARCH }}
|
||||
GHC_VER: ${{ matrix.GHC_VER }}
|
||||
DISTRO: Ubuntu
|
||||
AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_ACCESS_KEY_ID: ${{ env.AWS_ACCESS_KEY_ID }}
|
||||
S3_HOST: ${{ env.S3_HOST }}
|
||||
|
||||
- if: matrix.ARCH == 'ARM64'
|
||||
uses: docker://hasufell/arm64v8-debian-haskell:10
|
||||
name: Run build (aarch64 linux)
|
||||
with:
|
||||
args: sh .github/scripts/build.sh
|
||||
env:
|
||||
ARTIFACT: ${{ matrix.ARTIFACT }}
|
||||
ARCH: ${{ matrix.ARCH }}
|
||||
GHC_VER: ${{ matrix.GHC_VER }}
|
||||
DISTRO: Ubuntu
|
||||
AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_ACCESS_KEY_ID: ${{ env.AWS_ACCESS_KEY_ID }}
|
||||
S3_HOST: ${{ env.S3_HOST }}
|
||||
|
||||
- if: always()
|
||||
name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: artifacts
|
||||
path: |
|
||||
./out/*
|
||||
|
||||
build-macwin:
|
||||
name: Build binary (Mac/Win)
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CABAL_VER: 3.8.1.0
|
||||
MACOSX_DEPLOYMENT_TARGET: 10.13
|
||||
JSON_VERSION: "0.0.7"
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
S3_HOST: ${{ secrets.S3_HOST }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: [self-hosted, macOS, ARM64]
|
||||
ARTIFACT: "aarch64-apple-darwin-ghcup"
|
||||
GHC_VER: 9.2.6
|
||||
ARCH: ARM64
|
||||
- os: macOS-11
|
||||
ARTIFACT: "x86_64-apple-darwin-ghcup"
|
||||
GHC_VER: 9.2.6
|
||||
ARCH: 64
|
||||
- os: windows-latest
|
||||
ARTIFACT: "x86_64-mingw64-ghcup"
|
||||
GHC_VER: 8.10.7
|
||||
ARCH: 64
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
- if: matrix.ARCH == 'ARM64' && runner.os == 'macOS'
|
||||
name: Run build
|
||||
run: |
|
||||
bash .github/scripts/brew.sh git coreutils llvm@11 autoconf automake
|
||||
export PATH="$HOME/.brew/bin:$HOME/.brew/sbin:$HOME/.brew/opt/llvm@11/bin:$PATH"
|
||||
export CC="$HOME/.brew/opt/llvm@11/bin/clang"
|
||||
export CXX="$HOME/.brew/opt/llvm@11/bin/clang++"
|
||||
export LD=ld
|
||||
export AR="$HOME/.brew/opt/llvm@11/bin/llvm-ar"
|
||||
export RANLIB="$HOME/.brew/opt/llvm@11/bin/llvm-ranlib"
|
||||
bash .github/scripts/build.sh
|
||||
env:
|
||||
ARTIFACT: ${{ matrix.ARTIFACT }}
|
||||
ARCH: ${{ matrix.ARCH }}
|
||||
GHC_VER: ${{ matrix.GHC_VER }}
|
||||
DISTRO: na
|
||||
AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_ACCESS_KEY_ID: ${{ env.AWS_ACCESS_KEY_ID }}
|
||||
S3_HOST: ${{ env.S3_HOST }}
|
||||
HOMEBREW_CHANGE_ARCH_TO_ARM: 1
|
||||
|
||||
- if: matrix.ARCH == '64' && runner.os == 'macOS'
|
||||
name: Run build (windows/mac)
|
||||
run: |
|
||||
bash .github/scripts/brew.sh coreutils
|
||||
export PATH="$HOME/.brew/bin:$HOME/.brew/sbin:$PATH"
|
||||
bash .github/scripts/build.sh
|
||||
env:
|
||||
ARTIFACT: ${{ matrix.ARTIFACT }}
|
||||
ARCH: ${{ matrix.ARCH }}
|
||||
GHC_VER: ${{ matrix.GHC_VER }}
|
||||
DISTRO: na
|
||||
AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_ACCESS_KEY_ID: ${{ env.AWS_ACCESS_KEY_ID }}
|
||||
S3_HOST: ${{ env.S3_HOST }}
|
||||
HOMEBREW_CHANGE_ARCH_TO_ARM: 1
|
||||
|
||||
- if: runner.os == 'Windows'
|
||||
name: Run build (windows/mac)
|
||||
run: |
|
||||
bash .github/scripts/brew.sh git coreutils autoconf automake
|
||||
bash .github/scripts/build.sh
|
||||
env:
|
||||
ARTIFACT: ${{ matrix.ARTIFACT }}
|
||||
ARCH: ${{ matrix.ARCH }}
|
||||
GHC_VER: ${{ matrix.GHC_VER }}
|
||||
DISTRO: na
|
||||
AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_ACCESS_KEY_ID: ${{ env.AWS_ACCESS_KEY_ID }}
|
||||
S3_HOST: ${{ env.S3_HOST }}
|
||||
HOMEBREW_CHANGE_ARCH_TO_ARM: 1
|
||||
|
||||
- if: always()
|
||||
name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: artifacts
|
||||
path: |
|
||||
./out/*
|
||||
|
||||
test-linux:
|
||||
name: Test linux
|
||||
needs: "build-linux"
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CABAL_VER: 3.8.1.0
|
||||
JSON_VERSION: "0.0.7"
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
ARTIFACT: "i386-linux-ghcup"
|
||||
GHC_VER: 8.10.7
|
||||
ARCH: 32
|
||||
DISTRO: Alpine
|
||||
- os: ubuntu-latest
|
||||
ARTIFACT: "x86_64-linux-ghcup"
|
||||
GHC_VER: 8.10.7
|
||||
ARCH: 64
|
||||
DISTRO: Alpine
|
||||
- os: ubuntu-latest
|
||||
ARTIFACT: "x86_64-linux-ghcup"
|
||||
GHC_VER: 8.10.7
|
||||
ARCH: 64
|
||||
DISTRO: Ubuntu
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: artifacts
|
||||
path: ./out
|
||||
|
||||
- if: matrix.ARCH == '32' && matrix.DISTRO == 'Alpine'
|
||||
name: Run test (32 bit linux Alpine)
|
||||
uses: docker://hasufell/i386-alpine-haskell:3.12
|
||||
with:
|
||||
args: sh .github/scripts/test.sh
|
||||
env:
|
||||
ARTIFACT: ${{ matrix.ARTIFACT }}
|
||||
ARCH: ${{ matrix.ARCH }}
|
||||
GHC_VER: ${{ matrix.GHC_VER }}
|
||||
DISTRO: ${{ matrix.DISTRO }}
|
||||
|
||||
- if: matrix.ARCH == '64' && matrix.DISTRO == 'Alpine'
|
||||
name: Run test (64 bit linux Alpine)
|
||||
uses: docker://hasufell/alpine-haskell:3.12
|
||||
with:
|
||||
args: sh .github/scripts/test.sh
|
||||
env:
|
||||
ARTIFACT: ${{ matrix.ARTIFACT }}
|
||||
ARCH: ${{ matrix.ARCH }}
|
||||
GHC_VER: ${{ matrix.GHC_VER }}
|
||||
DISTRO: ${{ matrix.DISTRO }}
|
||||
|
||||
- if: matrix.DISTRO != 'Alpine'
|
||||
name: Run test (64 bit linux)
|
||||
run: |
|
||||
sudo apt-get install -y libnuma-dev zlib1g-dev libgmp-dev libgmp10 libssl-dev liblzma-dev libbz2-dev git wget lsb-release software-properties-common gnupg2 apt-transport-https gcc autoconf automake build-essential curl gzip
|
||||
sh .github/scripts/test.sh
|
||||
env:
|
||||
ARTIFACT: ${{ matrix.ARTIFACT }}
|
||||
ARCH: ${{ matrix.ARCH }}
|
||||
GHC_VER: ${{ matrix.GHC_VER }}
|
||||
DISTRO: ${{ matrix.DISTRO }}
|
||||
|
||||
- if: failure()
|
||||
name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: testfiles
|
||||
path: |
|
||||
./test/golden/unix/GHCupInfo*json
|
||||
|
||||
test-arm:
|
||||
name: Test ARM
|
||||
needs: "build-arm"
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CABAL_VER: 3.6.2.0
|
||||
JSON_VERSION: "0.0.7"
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: [self-hosted, Linux, ARM64]
|
||||
ARTIFACT: "armv7-linux-ghcup"
|
||||
GHC_VER: 9.2.2
|
||||
ARCH: ARM
|
||||
DISTRO: Ubuntu
|
||||
- os: [self-hosted, Linux, ARM64]
|
||||
ARTIFACT: "aarch64-linux-ghcup"
|
||||
GHC_VER: 9.2.6
|
||||
ARCH: ARM64
|
||||
DISTRO: Ubuntu
|
||||
|
||||
steps:
|
||||
- uses: docker://arm64v8/debian:10
|
||||
name: Cleanup (aarch64 linux)
|
||||
with:
|
||||
args: "find . -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +"
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: artifacts
|
||||
path: ./out
|
||||
|
||||
- if: matrix.ARCH == 'ARM'
|
||||
uses: docker://hasufell/arm32v7-debian-haskell:10
|
||||
name: Run test (armv7 linux)
|
||||
with:
|
||||
args: sh .github/scripts/test.sh
|
||||
env:
|
||||
ARTIFACT: ${{ matrix.ARTIFACT }}
|
||||
ARCH: ${{ matrix.ARCH }}
|
||||
GHC_VER: ${{ matrix.GHC_VER }}
|
||||
DISTRO: Ubuntu
|
||||
|
||||
- if: matrix.ARCH == 'ARM64'
|
||||
uses: docker://hasufell/arm64v8-debian-haskell:10
|
||||
name: Run test (aarch64 linux)
|
||||
with:
|
||||
args: sh .github/scripts/test.sh
|
||||
env:
|
||||
ARTIFACT: ${{ matrix.ARTIFACT }}
|
||||
ARCH: ${{ matrix.ARCH }}
|
||||
GHC_VER: ${{ matrix.GHC_VER }}
|
||||
DISTRO: Ubuntu
|
||||
|
||||
- if: failure()
|
||||
name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: testfiles
|
||||
path: |
|
||||
./test/golden/unix/GHCupInfo*json
|
||||
|
||||
test-macwin:
|
||||
name: Test Mac/Win
|
||||
needs: "build-macwin"
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CABAL_VER: 3.8.1.0
|
||||
MACOSX_DEPLOYMENT_TARGET: 10.13
|
||||
JSON_VERSION: "0.0.7"
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: [self-hosted, macOS, ARM64]
|
||||
ARTIFACT: "aarch64-apple-darwin-ghcup"
|
||||
GHC_VER: 9.2.6
|
||||
ARCH: ARM64
|
||||
DISTRO: na
|
||||
- os: macOS-11
|
||||
ARTIFACT: "x86_64-apple-darwin-ghcup"
|
||||
GHC_VER: 9.2.6
|
||||
ARCH: 64
|
||||
DISTRO: na
|
||||
- os: windows-latest
|
||||
ARTIFACT: "x86_64-mingw64-ghcup"
|
||||
GHC_VER: 8.10.7
|
||||
ARCH: 64
|
||||
DISTRO: na
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: artifacts
|
||||
path: ./out
|
||||
|
||||
- if: runner.os == 'macOS'
|
||||
name: Run test
|
||||
run: |
|
||||
bash .github/scripts/brew.sh coreutils
|
||||
export PATH="$HOME/.brew/bin:$HOME/.brew/sbin:$PATH"
|
||||
bash .github/scripts/test.sh
|
||||
env:
|
||||
ARTIFACT: ${{ matrix.ARTIFACT }}
|
||||
ARCH: ${{ matrix.ARCH }}
|
||||
GHC_VER: ${{ matrix.GHC_VER }}
|
||||
DISTRO: ${{ matrix.DISTRO }}
|
||||
HOMEBREW_CHANGE_ARCH_TO_ARM: 1
|
||||
|
||||
- if: runner.os != 'macOS'
|
||||
name: Run test
|
||||
run: bash .github/scripts/test.sh
|
||||
env:
|
||||
ARTIFACT: ${{ matrix.ARTIFACT }}
|
||||
ARCH: ${{ matrix.ARCH }}
|
||||
GHC_VER: ${{ matrix.GHC_VER }}
|
||||
DISTRO: ${{ matrix.DISTRO }}
|
||||
HOMEBREW_CHANGE_ARCH_TO_ARM: 1
|
||||
|
||||
- if: failure() && runner.os == 'Windows'
|
||||
name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: testfiles
|
||||
path: |
|
||||
./test/golden/windows/GHCupInfo*json
|
||||
|
||||
- if: failure() && runner.os != 'Windows'
|
||||
name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: testfiles
|
||||
path: |
|
||||
./test/golden/unix/GHCupInfo*json
|
||||
hls:
|
||||
name: hls
|
||||
needs: build-linux
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GHC_VERSION: "8.10.7"
|
||||
HLS_TARGET_VERSION: "1.8.0.0"
|
||||
CABAL_VERSION: "3.8.1.0"
|
||||
JSON_VERSION: "0.0.7"
|
||||
ARTIFACT: "x86_64-linux-ghcup"
|
||||
DISTRO: Ubuntu
|
||||
ARCH: 64
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
S3_HOST: ${{ secrets.S3_HOST }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: artifacts
|
||||
path: ./out
|
||||
|
||||
- name: Run hls build
|
||||
run: |
|
||||
sudo apt-get install -y libnuma-dev zlib1g-dev libgmp-dev libgmp10 libssl-dev liblzma-dev libbz2-dev git wget lsb-release software-properties-common gnupg2 apt-transport-https gcc autoconf automake build-essential curl gzip
|
||||
sh .github/scripts/hls.sh
|
||||
|
||||
release:
|
||||
name: release
|
||||
needs: ["test-linux", "test-arm", "test-macwin", "hls"]
|
||||
runs-on: ubuntu-latest
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: artifacts
|
||||
path: ./out
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: true
|
||||
files: |
|
||||
./out/*
|
||||
|
||||
28
.github/workflows/shellcheck.yaml
vendored
Normal file
28
.github/workflows/shellcheck.yaml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
name: Shellcheck
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
shellcheck:
|
||||
name: shellcheck
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
JSON_VERSION: "0.0.7"
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
- name: Run shellcheck
|
||||
uses: docker://koalaman/shellcheck-alpine
|
||||
with:
|
||||
args: shellcheck scripts/bootstrap/bootstrap-haskell
|
||||
108
.gitlab-ci.yml
108
.gitlab-ci.yml
@@ -13,7 +13,7 @@ variables:
|
||||
|
||||
# Sequential version number of all cached things.
|
||||
# Bump to invalidate GitLab CI cache.
|
||||
CACHE_REV: 0
|
||||
CACHE_REV: 1
|
||||
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
|
||||
@@ -125,6 +125,10 @@ variables:
|
||||
- test/golden
|
||||
- dist-newstyle/cache/
|
||||
when: on_failure
|
||||
cache:
|
||||
key: ghcup-test-$CACHE_REV
|
||||
paths:
|
||||
- cabal-cache
|
||||
|
||||
# .test_ghcup_scoop:
|
||||
# script:
|
||||
@@ -136,6 +140,10 @@ variables:
|
||||
- .debian
|
||||
before_script:
|
||||
- ./.gitlab/before_script/linux/install_deps.sh
|
||||
- ./.gitlab/script/ci.sh extract_cabal_cache
|
||||
after_script:
|
||||
- ./.gitlab/script/ci.sh save_cabal_cache
|
||||
- bash ./.gitlab/after_script.sh
|
||||
|
||||
.test_ghcup_version:linux32:
|
||||
extends:
|
||||
@@ -143,6 +151,10 @@ variables:
|
||||
- .alpine:32bit
|
||||
before_script:
|
||||
- ./.gitlab/before_script/linux/alpine/install_deps.sh
|
||||
- ./.gitlab/script/ci.sh extract_cabal_cache
|
||||
after_script:
|
||||
- ./.gitlab/script/ci.sh save_cabal_cache
|
||||
- bash ./.gitlab/after_script.sh
|
||||
|
||||
.test_ghcup_version:armv7:
|
||||
extends:
|
||||
@@ -150,6 +162,10 @@ variables:
|
||||
- .linux:armv7
|
||||
before_script:
|
||||
- ./.gitlab/before_script/linux/install_deps.sh
|
||||
- ./.gitlab/script/ci.sh extract_cabal_cache
|
||||
after_script:
|
||||
- ./.gitlab/script/ci.sh save_cabal_cache
|
||||
- bash ./.gitlab/after_script.sh
|
||||
|
||||
.test_ghcup_version:aarch64:
|
||||
extends:
|
||||
@@ -157,45 +173,42 @@ variables:
|
||||
- .linux:aarch64
|
||||
before_script:
|
||||
- ./.gitlab/before_script/linux/install_deps.sh
|
||||
- ./.gitlab/script/ci.sh extract_cabal_cache
|
||||
after_script:
|
||||
- ./.gitlab/script/ci.sh save_cabal_cache
|
||||
- bash ./.gitlab/after_script.sh
|
||||
|
||||
.test_ghcup_version:darwin:
|
||||
extends:
|
||||
- .test_ghcup_version
|
||||
- .darwin
|
||||
- .root_cleanup
|
||||
before_script:
|
||||
- ./.gitlab/before_script/darwin/install_deps.sh
|
||||
- ./.gitlab/script/ci.sh extract_cabal_cache
|
||||
after_script:
|
||||
- ./.gitlab/script/ci.sh save_cabal_cache
|
||||
- bash ./.gitlab/after_script.sh
|
||||
|
||||
.test_ghcup_version:darwin:aarch64:
|
||||
extends:
|
||||
- .test_ghcup_version
|
||||
- .darwin:aarch64
|
||||
- .root_cleanup
|
||||
cache:
|
||||
key: darwin-brew-$CACHE_REV
|
||||
paths:
|
||||
- .brew
|
||||
- .brew_cache
|
||||
- brew_cache
|
||||
key: ghcup-test-$CACHE_REV
|
||||
paths:
|
||||
- cabal-cache
|
||||
before_script:
|
||||
# Install brew locally in the project dir. Packages will also be installed here.
|
||||
- '[ -e "$CI_PROJECT_DIR/.brew" ] || git clone --depth=1 https://github.com/Homebrew/brew $CI_PROJECT_DIR/.brew'
|
||||
- export PATH="$CI_PROJECT_DIR/.brew/bin:$CI_PROJECT_DIR/.brew/sbin:$PATH"
|
||||
|
||||
# extract brew cache
|
||||
- ./.gitlab/script/ci.sh extract_brew_cache
|
||||
# otherwise we seem to get intel binaries
|
||||
- export HOMEBREW_CHANGE_ARCH_TO_ARM=1
|
||||
|
||||
# make sure to not pollute the machine with temp files etc
|
||||
- mkdir -p $CI_PROJECT_DIR/.brew_cache
|
||||
- export HOMEBREW_CACHE=$CI_PROJECT_DIR/.brew_cache
|
||||
- mkdir -p $CI_PROJECT_DIR/.brew_logs
|
||||
- export HOMEBREW_LOGS=$CI_PROJECT_DIR/.brew_logs
|
||||
- mkdir -p /private/tmp/.brew_tmp
|
||||
- export HOMEBREW_TEMP=/private/tmp/.brew_tmp
|
||||
|
||||
# update and install packages
|
||||
- brew update
|
||||
- brew install llvm
|
||||
- brew install autoconf automake coreutils
|
||||
- /bin/bash ./.gitlab/script/brew.sh llvm autoconf automake coreutils
|
||||
# extract cabal cache
|
||||
- ./.gitlab/script/ci.sh extract_cabal_cache
|
||||
script: |
|
||||
export PATH="$CI_PROJECT_DIR/.brew/opt/llvm/bin:$CI_PROJECT_DIR/.brew/bin:$CI_PROJECT_DIR/.brew/sbin:$PATH"
|
||||
export CC=$CI_PROJECT_DIR/.brew/opt/llvm/bin/clang
|
||||
@@ -205,40 +218,51 @@ variables:
|
||||
export RANLIB=$CI_PROJECT_DIR/.brew/opt/llvm/bin/llvm-ranlib
|
||||
./.gitlab/before_script/darwin/install_deps.sh
|
||||
./.gitlab/script/ghcup_version.sh
|
||||
after_script:
|
||||
- ./.gitlab/script/ci.sh save_cabal_cache
|
||||
- ./.gitlab/script/ci.sh save_brew_cache
|
||||
- bash ./.gitlab/after_script.sh
|
||||
|
||||
.test_ghcup_version:freebsd12:
|
||||
extends:
|
||||
- .test_ghcup_version
|
||||
- .freebsd12
|
||||
- .root_cleanup
|
||||
before_script:
|
||||
- ./.gitlab/before_script/freebsd/install_deps.sh
|
||||
- ./.gitlab/script/ci.sh extract_cabal_cache
|
||||
after_script:
|
||||
- ./.gitlab/script/ci.sh save_cabal_cache
|
||||
- bash ./.gitlab/after_script.sh
|
||||
|
||||
.test_ghcup_version:freebsd13:
|
||||
extends:
|
||||
- .test_ghcup_version
|
||||
- .freebsd13
|
||||
- .root_cleanup
|
||||
before_script:
|
||||
- sudo pkg update
|
||||
- sudo pkg install --yes compat12x-amd64
|
||||
- sudo ln -s libncurses.so.6 /usr/local/lib/libncurses.so.6.2
|
||||
- ./.gitlab/before_script/freebsd/install_deps.sh
|
||||
- ./.gitlab/script/ci.sh extract_cabal_cache
|
||||
after_script:
|
||||
- ./.gitlab/script/ci.sh save_cabal_cache
|
||||
- bash ./.gitlab/after_script.sh
|
||||
|
||||
.test_ghcup_version:windows:
|
||||
extends:
|
||||
- .test_ghcup_version
|
||||
- .windows
|
||||
- .root_cleanup
|
||||
before_script:
|
||||
- set CABAL_DIR="$CI_PROJECT_DIR/cabal"
|
||||
- bash ./.gitlab/before_script/windows/install_deps.sh
|
||||
- bash ./.gitlab/script/ci.sh extract_cabal_cache
|
||||
after_script:
|
||||
- bash ./.gitlab/script/ci.sh save_cabal_cache
|
||||
- bash ./.gitlab/after_script.sh
|
||||
|
||||
# .test_ghcup_scoop:windows:
|
||||
# extends:
|
||||
# - .windows
|
||||
# - .test_ghcup_scoop
|
||||
# - .root_cleanup
|
||||
|
||||
.release_ghcup:
|
||||
script:
|
||||
@@ -258,9 +282,12 @@ variables:
|
||||
test:linux:stack:
|
||||
stage: test
|
||||
before_script:
|
||||
- ./.gitlab/script/ci.sh extract_stack_cache
|
||||
- ./.gitlab/before_script/linux/install_deps_minimal.sh
|
||||
script:
|
||||
- ./.gitlab/script/ghcup_stack.sh
|
||||
after_script:
|
||||
- ./.gitlab/script/ci.sh save_stack_cache
|
||||
extends:
|
||||
- .debian
|
||||
needs: []
|
||||
@@ -290,6 +317,7 @@ test:windows:bootstrap_powershell_script:
|
||||
- "[Environment]::SetEnvironmentVariable('GHCUP_MSYS2', $null, [System.EnvironmentVariableTarget]::User)"
|
||||
- "[Environment]::SetEnvironmentVariable('CABAL_DIR', $null, [System.EnvironmentVariableTarget]::User)"
|
||||
- bash ./.gitlab/after_script.sh
|
||||
- bash ./.gitlab/script/ci.sh save_cabal_cache
|
||||
variables:
|
||||
GHC_VERSION: "8.10.7"
|
||||
CABAL_VERSION: "3.6.2.0"
|
||||
@@ -536,28 +564,17 @@ release:darwin:aarch64:
|
||||
cache:
|
||||
key: darwin-brew-$CACHE_REV
|
||||
paths:
|
||||
- .brew
|
||||
- .brew_cache
|
||||
- brew_cache
|
||||
key: ghcup-test-$CACHE_REV
|
||||
paths:
|
||||
- cabal-cache
|
||||
before_script:
|
||||
# Install brew locally in the project dir. Packages will also be installed here.
|
||||
- '[ -e "$CI_PROJECT_DIR/.brew" ] || git clone --depth=1 https://github.com/Homebrew/brew $CI_PROJECT_DIR/.brew'
|
||||
- export PATH="$CI_PROJECT_DIR/.brew/bin:$CI_PROJECT_DIR/.brew/sbin:$PATH"
|
||||
|
||||
- ./.gitlab/script/ci.sh extract_brew_cache
|
||||
- ./.gitlab/script/ci.sh extract_cabal_cache
|
||||
# otherwise we seem to get intel binaries
|
||||
- export HOMEBREW_CHANGE_ARCH_TO_ARM=1
|
||||
|
||||
# make sure to not pollute the machine with temp files etc
|
||||
- mkdir -p $CI_PROJECT_DIR/.brew_cache
|
||||
- export HOMEBREW_CACHE=$CI_PROJECT_DIR/.brew_cache
|
||||
- mkdir -p $CI_PROJECT_DIR/.brew_logs
|
||||
- export HOMEBREW_LOGS=$CI_PROJECT_DIR/.brew_logs
|
||||
- mkdir -p /private/tmp/.brew_tmp
|
||||
- export HOMEBREW_TEMP=/private/tmp/.brew_tmp
|
||||
|
||||
# update and install packages
|
||||
- brew update
|
||||
- brew install llvm
|
||||
- brew install autoconf automake
|
||||
- /bin/bash ./.gitlab/script/brew.sh llvm autoconf automake coreutils
|
||||
script: |
|
||||
export PATH="$CI_PROJECT_DIR/.brew/opt/llvm/bin:$CI_PROJECT_DIR/.brew/bin:$CI_PROJECT_DIR/.brew/sbin:$PATH"
|
||||
export CC=$CI_PROJECT_DIR/.brew/opt/llvm/bin/clang
|
||||
@@ -567,6 +584,9 @@ release:darwin:aarch64:
|
||||
export RANLIB=$CI_PROJECT_DIR/.brew/opt/llvm/bin/llvm-ranlib
|
||||
./.gitlab/before_script/darwin/install_deps.sh
|
||||
./.gitlab/script/ghcup_release.sh
|
||||
after_script:
|
||||
- ./.gitlab/script/ci.sh save_cabal_cache
|
||||
- ./.gitlab/script/ci.sh save_brew_cache
|
||||
variables:
|
||||
ARTIFACT: "aarch64-apple-darwin-ghcup"
|
||||
GHC_VERSION: "8.10.7"
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
if [ "${OS}" = "WINDOWS" ] ; then
|
||||
export GHCUP_INSTALL_BASE_PREFIX="$CI_PROJECT_DIR"
|
||||
export GHCUP_BIN="$CI_PROJECT_DIR/ghcup/bin"
|
||||
export PATH="$GHCUP_BIN:$CI_PROJECT_DIR/.local/bin:$PATH"
|
||||
export TMPDIR="$CI_PROJECT_DIR/tmp"
|
||||
export GHCUP_INSTALL_BASE_PREFIX="$CI_PROJECT_DIR"
|
||||
export GHCUP_BIN="$CI_PROJECT_DIR/ghcup/bin"
|
||||
export PATH="$GHCUP_BIN:$CI_PROJECT_DIR/.local/bin:$PATH"
|
||||
export TMPDIR="$CI_PROJECT_DIR/tmp"
|
||||
export CABAL_DIR="$CI_PROJECT_DIR/cabal"
|
||||
export CABAL_CACHE="$CI_PROJECT_DIR/cabal-cache"
|
||||
export STACK_ROOT="$CI_PROJECT_DIR/stack"
|
||||
export STACK_CACHE="$CI_PROJECT_DIR/stack-cache"
|
||||
export BREW_DIR="$CI_PROJECT_DIR/.brew_cache"
|
||||
export BREW_CACHE="$CI_PROJECT_DIR/brew-cache"
|
||||
else
|
||||
export GHCUP_INSTALL_BASE_PREFIX="$CI_PROJECT_DIR"
|
||||
export GHCUP_BIN="$CI_PROJECT_DIR/.ghcup/bin"
|
||||
export PATH="$GHCUP_BIN:$CI_PROJECT_DIR/.local/bin:$PATH"
|
||||
export TMPDIR="$CI_PROJECT_DIR/tmp"
|
||||
export GHCUP_INSTALL_BASE_PREFIX="$CI_PROJECT_DIR"
|
||||
export GHCUP_BIN="$CI_PROJECT_DIR/.ghcup/bin"
|
||||
export PATH="$GHCUP_BIN:$CI_PROJECT_DIR/.local/bin:$PATH"
|
||||
export TMPDIR="$CI_PROJECT_DIR/tmp"
|
||||
export CABAL_DIR="$CI_PROJECT_DIR/cabal"
|
||||
export CABAL_CACHE="$CI_PROJECT_DIR/cabal-cache"
|
||||
export STACK_ROOT="$CI_PROJECT_DIR/stack"
|
||||
export STACK_CACHE="$CI_PROJECT_DIR/stack-cache"
|
||||
export BREW_DIR="$CI_PROJECT_DIR/.brew_cache"
|
||||
export BREW_CACHE="$CI_PROJECT_DIR/brew-cache"
|
||||
fi
|
||||
|
||||
19
.gitlab/script/brew.sh
Executable file
19
.gitlab/script/brew.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuxo pipefail
|
||||
|
||||
# Install brew locally in the project dir. Packages will also be installed here.
|
||||
[ -e "$CI_PROJECT_DIR/.brew" ] || git clone --depth=1 https://github.com/Homebrew/brew $CI_PROJECT_DIR/.brew
|
||||
export PATH="$CI_PROJECT_DIR/.brew/bin:$CI_PROJECT_DIR/.brew/sbin:$PATH"
|
||||
|
||||
# make sure to not pollute the machine with temp files etc
|
||||
mkdir -p $CI_PROJECT_DIR/.brew_cache
|
||||
export HOMEBREW_CACHE=$CI_PROJECT_DIR/.brew_cache
|
||||
mkdir -p $CI_PROJECT_DIR/.brew_logs
|
||||
export HOMEBREW_LOGS=$CI_PROJECT_DIR/.brew_logs
|
||||
mkdir -p /private/tmp/.brew_tmp
|
||||
export HOMEBREW_TEMP=/private/tmp/.brew_tmp
|
||||
|
||||
# update and install packages
|
||||
brew update
|
||||
brew install ${1+"$@"}
|
||||
70
.gitlab/script/ci.sh
Executable file
70
.gitlab/script/ci.sh
Executable file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
TOP="$( cd "$(dirname "$0")" ; pwd -P )"
|
||||
. "${TOP}/../ghcup_env"
|
||||
|
||||
function save_cabal_cache () {
|
||||
echo "Storing cabal cache from $CABAL_DIR to $CABAL_CACHE..."
|
||||
rm -Rf "$CABAL_CACHE"
|
||||
mkdir -p "$CABAL_CACHE"
|
||||
if [ -d "$CABAL_DIR" ]; then
|
||||
cp -Rf "$CABAL_DIR" "$CABAL_CACHE/"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
function extract_cabal_cache () {
|
||||
if [ -d "$CABAL_CACHE" ]; then
|
||||
echo "Extracting cabal cache from $CABAL_CACHE to $CABAL_DIR..."
|
||||
mkdir -p "$CABAL_DIR"
|
||||
cp -Rf "$CABAL_CACHE"/* "$CABAL_DIR"
|
||||
fi
|
||||
}
|
||||
|
||||
function save_stack_cache () {
|
||||
echo "Storing stack cache from $STACK_ROOT to $STACK_CACHE..."
|
||||
rm -Rf "$STACK_CACHE"
|
||||
mkdir -p "$STACK_CACHE"
|
||||
if [ -d "$STACK_ROOT" ]; then
|
||||
cp -Rf "$STACK_DIR" "$STACK_CACHE"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
function extract_stack_cache () {
|
||||
if [ -d "$STACK_CACHE" ]; then
|
||||
echo "Extracting stack cache from $STACK_CACHE to $STACK_ROOT..."
|
||||
mkdir -p "$STACK_ROOT"
|
||||
cp -Rf "$STACK_CACHE"/* "$STACK_ROOT"
|
||||
fi
|
||||
}
|
||||
|
||||
function save_brew_cache () {
|
||||
echo "Storing brew cache from $BREW_DIR to $BREW_CACHE..."
|
||||
rm -Rf "$BREW_CACHE"
|
||||
mkdir -p "$BREW_CACHE"
|
||||
if [ -d "$BREW_DIR" ]; then
|
||||
cp -Rf "$BREW_DIR" "$BREW_CACHE"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
function extract_brew_cache () {
|
||||
if [ -d "$BREW_CACHE" ]; then
|
||||
echo "Extracting stack cache from $BREW_CACHE to $BREW_DIR..."
|
||||
mkdir -p "$BREW_DIR"
|
||||
cp -Rf "$BREW_CACHE"/* "$BREW_DIR"
|
||||
fi
|
||||
}
|
||||
|
||||
case $1 in
|
||||
extract_cabal_cache) extract_cabal_cache ;;
|
||||
save_cabal_cache) save_cabal_cache ;;
|
||||
extract_stack_cache) extract_stack_cache ;;
|
||||
save_stack_cache) save_stack_cache ;;
|
||||
extract_brew_cache) extract_brew_cache ;;
|
||||
save_brew_cache) save_brew_cache ;;
|
||||
*) echo "unknown mode $1" ; exit 11 ;;
|
||||
esac
|
||||
@@ -6,20 +6,10 @@ set -eux
|
||||
|
||||
mkdir -p "$CI_PROJECT_DIR"/.local/bin
|
||||
|
||||
ecabal() {
|
||||
cabal "$@"
|
||||
}
|
||||
|
||||
eghcup() {
|
||||
ghcup -v -c -s file://$(pwd)/ghcup-${JSON_VERSION}.yaml "$@"
|
||||
}
|
||||
|
||||
git describe --always
|
||||
|
||||
### build
|
||||
|
||||
ecabal update
|
||||
|
||||
export BOOTSTRAP_HASKELL_NONINTERACTIVE=yes
|
||||
export BOOTSTRAP_HASKELL_GHC_VERSION=$GHC_VERSION
|
||||
export BOOTSTRAP_HASKELL_CABAL_VERSION=$CABAL_VERSION
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eux
|
||||
|
||||
@@ -8,6 +8,7 @@ mkdir -p "$CI_PROJECT_DIR"/.local/bin
|
||||
|
||||
CI_PROJECT_DIR=$(pwd)
|
||||
|
||||
|
||||
ecabal() {
|
||||
cabal "$@"
|
||||
}
|
||||
@@ -34,6 +35,8 @@ git describe --always
|
||||
|
||||
### build
|
||||
|
||||
rm -rf "${GHCUP_DIR}"/share
|
||||
|
||||
ecabal update
|
||||
|
||||
if [ "${OS}" = "DARWIN" ] ; then
|
||||
@@ -94,16 +97,23 @@ rm -rf "${GHCUP_DIR}"
|
||||
eghcup --numeric-version
|
||||
|
||||
eghcup install ghc ${GHC_VERSION}
|
||||
[ `$(eghcup whereis ghc ${GHC_VERSION}) --numeric-version` = "${GHC_VERSION}" ]
|
||||
[ `eghcup run --ghc ${GHC_VERSION} -- ghc --numeric-version` = "${GHC_VERSION}" ]
|
||||
eghcup unset ghc ${GHC_VERSION}
|
||||
ls -lah "$(eghcup whereis -d ghc ${GHC_VERSION})"
|
||||
[ "`$(eghcup whereis ghc ${GHC_VERSION}) --numeric-version`" = "${GHC_VERSION}" ]
|
||||
[ "`eghcup run --ghc ${GHC_VERSION} -- ghc --numeric-version`" = "${GHC_VERSION}" ]
|
||||
[ "`ghcup run --ghc ${GHC_VERSION} -- ghc -e 'Control.Monad.join (Control.Monad.fmap System.IO.putStr System.Environment.getExecutablePath)'`" = "`$(ghcup whereis ghc ${GHC_VERSION}) -e 'Control.Monad.join (Control.Monad.fmap System.IO.putStr System.Environment.getExecutablePath)'`" ]
|
||||
eghcup set ghc ${GHC_VERSION}
|
||||
eghcup install cabal ${CABAL_VERSION}
|
||||
[ `$(eghcup whereis cabal ${CABAL_VERSION}) --numeric-version` = "${CABAL_VERSION}" ]
|
||||
[ "`$(eghcup whereis cabal ${CABAL_VERSION}) --numeric-version`" = "${CABAL_VERSION}" ]
|
||||
eghcup unset cabal
|
||||
"$GHCUP_BIN"/cabal --version && exit || echo yes
|
||||
"$GHCUP_BIN"/cabal --version && exit 1 || echo yes
|
||||
|
||||
# make sure no cabal is set when running 'ghcup run' to check that PATH propagages properly
|
||||
# https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/375
|
||||
[ "`eghcup run --cabal ${CABAL_VERSION} -- cabal --numeric-version`" = "${CABAL_VERSION}" ]
|
||||
eghcup set cabal ${CABAL_VERSION}
|
||||
[ `$(eghcup whereis cabal ${CABAL_VERSION}) --numeric-version` = "${CABAL_VERSION}" ]
|
||||
[ `eghcup run --cabal ${CABAL_VERSION} -- cabal --numeric-version` = "${CABAL_VERSION}" ]
|
||||
|
||||
[ "`$(eghcup whereis cabal ${CABAL_VERSION}) --numeric-version`" = "${CABAL_VERSION}" ]
|
||||
|
||||
if [ "${OS}" != "FREEBSD" ] ; then
|
||||
if [ "${ARCH}" = "64" ] ; then
|
||||
@@ -170,12 +180,14 @@ else
|
||||
eghcup set ${GHC_VERSION}
|
||||
[ "$(ghc --numeric-version)" = "${ghc_ver}" ]
|
||||
eghcup unset ghc
|
||||
"$GHCUP_BIN"/ghc --numeric-version && exit || echo yes
|
||||
"$GHCUP_BIN"/ghc --numeric-version && exit 1 || echo yes
|
||||
eghcup set ${GHC_VERSION}
|
||||
eghcup --offline rm 8.10.3
|
||||
[ "$(ghc --numeric-version)" = "${ghc_ver}" ]
|
||||
|
||||
|
||||
ls -lah "$GHCUP_BIN"
|
||||
|
||||
if [ "${OS}" = "DARWIN" ] ; then
|
||||
eghcup install hls
|
||||
$(eghcup whereis hls) --version
|
||||
@@ -187,12 +199,12 @@ else
|
||||
eghcup install hls
|
||||
haskell-language-server-wrapper --version
|
||||
eghcup unset hls
|
||||
"$GHCUP_BIN"/haskell-language-server-wrapper --version && exit || echo yes
|
||||
"$GHCUP_BIN"/haskell-language-server-wrapper --version && exit 1 || echo yes
|
||||
|
||||
eghcup install stack
|
||||
stack --version
|
||||
eghcup unset hls
|
||||
"$GHCUP_BIN"/stack --version && exit || echo yes
|
||||
eghcup unset stack
|
||||
"$GHCUP_BIN"/stack --version && exit 1 || echo yes
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -210,11 +222,13 @@ eghcup rm $(ghc --numeric-version)
|
||||
# https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/116
|
||||
if [ "${OS}" = "LINUX" ] ; then
|
||||
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 install cabal -u https://downloads.haskell.org/~ghcup/unofficial-bindists/cabal/3.7.0.0-pre20220407/cabal-install-3.7-x86_64-linux-alpine.tar.xz 3.4.0.0-rc4
|
||||
eghcup rm cabal 3.4.0.0-rc4
|
||||
fi
|
||||
fi
|
||||
|
||||
eghcup gc -c
|
||||
|
||||
sha_sum() {
|
||||
if [ "${OS}" = "FREEBSD" ] ; then
|
||||
sha256 "$@"
|
||||
@@ -262,13 +276,39 @@ if [ "${ARCH}" = "64" ] ; then
|
||||
eghcup install hls -i "$(pwd)/isolated" 1.3.0
|
||||
[ "$(isolated/haskell-language-server-wrapper --numeric-version)" = "1.3.0" ] ||
|
||||
[ "$(isolated/haskell-language-server-wrapper --numeric-version)" = "1.3.0.0" ]
|
||||
|
||||
# test that isolated installs don't clean up target directory
|
||||
cat <<EOF > "${GHCUP_BIN}/gmake"
|
||||
#!/bin/bash
|
||||
exit 1
|
||||
EOF
|
||||
chmod +x "${GHCUP_BIN}/gmake"
|
||||
mkdir isolated_tainted/
|
||||
touch isolated_tainted/lol
|
||||
|
||||
! eghcup install ghc -i "$(pwd)/isolated_tainted" 8.10.5 --force
|
||||
[ -e "$(pwd)/isolated_tainted/lol" ]
|
||||
rm "${GHCUP_BIN}/gmake"
|
||||
fi
|
||||
fi
|
||||
|
||||
eghcup upgrade
|
||||
eghcup upgrade -f
|
||||
|
||||
# test that doing fishy symlinks into GHCup dir doesn't cause weird stuff on 'ghcup nuke'
|
||||
mkdir no_nuke/
|
||||
mkdir no_nuke/bar
|
||||
echo 'foo' > no_nuke/file
|
||||
echo 'bar' > no_nuke/bar/file
|
||||
ln -s "$CI_PROJECT_DIR"/no_nuke/ "${GHCUP_DIR}"/cache/no_nuke
|
||||
ln -s "$CI_PROJECT_DIR"/no_nuke/ "${GHCUP_DIR}"/logs/no_nuke
|
||||
|
||||
# nuke
|
||||
eghcup nuke
|
||||
[ ! -e "${GHCUP_DIR}" ]
|
||||
|
||||
# make sure nuke doesn't resolve symlinks
|
||||
[ -e "$CI_PROJECT_DIR"/no_nuke/file ]
|
||||
[ -e "$CI_PROJECT_DIR"/no_nuke/bar/file ]
|
||||
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
- ignore: {name: "Avoid lambda"}
|
||||
- ignore: {name: "Use uncurry"}
|
||||
- ignore: {name: "Use replicateM"}
|
||||
- ignore: {name: "Use unless"}
|
||||
- ignore: {name: "Redundant irrefutable pattern"}
|
||||
|
||||
|
||||
|
||||
25
.travis.yml
25
.travis.yml
@@ -1,25 +0,0 @@
|
||||
jobs:
|
||||
include:
|
||||
- os: osx
|
||||
osx_image: xcode10.1
|
||||
language: generic
|
||||
env: ARTIFACT=x86_64-apple-darwin-10.13-ghcup
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode11.3
|
||||
language: generic
|
||||
env: ARTIFACT=x86_64-apple-darwin-10.14-ghcup
|
||||
|
||||
|
||||
script: ".travis/build.sh"
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: "hT2od8Iy04tdFVuonPSWv0NX5hZDmv4al8Q0GbIWmviUetROuM7c6/MCHUcgyiw6H2L3pmH4F24GBYWpKBT3ZMbxrKXhZOZ3KPLXzlnuRlm1qymKqqwsJs3466bMftaiBr16rx1VpAuditN4A32oSmTFcQAJc84Bxn2WZ4t8hk9muS8YPyLhqg3/NxT6ob8dzNp9eS2cA0WODMb/fMzaMruRtepSK8JvuXb/SnTvaDcl9plmPzEa+eW54jwVsDps8ZpQMQlTtGIjYHIwTQ36/iLH4LoAvD7OEnB7qf753LOzmI/bvlB75xYGsLxe1qgpzPMjuG3AK0jb2KGSZCzyAyrbBFSQMIyC1gNKMtab3CohnA9WdQqAT1xrzPzA9zNw516G5Fn/z+t9Ek1f6L2OYO2hJfweNhWh+ChAIsOags2QBpqc0qjkwUS4wqxCWBdyVfgPTUoGelvjCfjQgypgIyLEHFvXt9rlj+kd97FY7nG3vxZrsvWTKKKT551OqUYX5zWTyvGR71jKyNst/p93Pg3DkRy31gHrGnG9zfNgN5tWxJqDd/suR/BAFTp0VtkFb8fR3ct7WMVeJXtE2+bKqxO5Fnocs1VjEm8pKPk7glnp0muu08kaO0h54wiSOCbk1RvO1KZtHue4wKWrHcI18dwW2WtzoBQ4P1lOSkS81UY="
|
||||
file: $ARTIFACT
|
||||
on:
|
||||
repo: hasufell/ghcup-hs
|
||||
tags: true
|
||||
skip_cleanup: true
|
||||
draft: true
|
||||
84
CHANGELOG.md
84
CHANGELOG.md
@@ -1,5 +1,89 @@
|
||||
# Revision history for ghcup
|
||||
|
||||
## 0.1.19.2 -- 2023-2-24
|
||||
|
||||
* Follow-up fix for JFS/ReiserFS and other filesystem that don't support `d_type`, fixes [#787](https://github.com/haskell/ghcup-hs/issues/787)
|
||||
- the previous release had a bug that invalidated that broke it
|
||||
* Implement 'latest-prerelease' tag wrt [#788](https://github.com/haskell/ghcup-hs/issues/788)
|
||||
* Fix 'Could not parse version of stray directory.DS_Store' warnings on macOs wrt [#797](https://github.com/haskell/ghcup-hs/issues/797)
|
||||
|
||||
## 0.1.19.1 -- 2023-2-19
|
||||
|
||||
* Fix GHCup on JFS/ReiserFS and other filesystem that don't support `d_type`, fixes [#766](https://github.com/haskell/ghcup-hs/issues/766)
|
||||
* Don't fail on setModificationTime, fixes [#784](https://github.com/haskell/ghcup-hs/issues/784) and many GitHub actions issues
|
||||
* Make armv7/aarch64 linux binaries more portable (built on Debian buster)
|
||||
* Improve usability on 'ghcup config add-release-channel', fixes [#751](https://github.com/haskell/ghcup-hs/issues/751)
|
||||
* Make version shortcuts work with 'ghcup set', fixes [#757](https://github.com/haskell/ghcup-hs/issues/757)
|
||||
* Don't implicitly smuggle in config options in `ghcup config set` wrt [#775](https://github.com/haskell/ghcup-hs/issues/775)
|
||||
* Fix build on unix with -ftui
|
||||
|
||||
## 0.1.19.0 -- 2023-1-13
|
||||
|
||||
* restore proper support for FreeBSD and Linux armv7
|
||||
* integrate with [errors.haskell.org](https://errors.haskell.org/index.html), wrt [#434](https://github.com/haskell/ghcup-hs/issues/434)
|
||||
* allow to overwrite distro detection via config wrt [#421](https://github.com/haskell/ghcup-hs/issues/421)
|
||||
- this is particularly useful for e.g. Ubuntu derivates, where ghcup doesn't pick the optimal bindist, also see the [GHCup documentation on overriding distro detection](https://www.haskell.org/ghcup/guide/#overriding-distro-detection)
|
||||
* Add proper support for mirrors wrt [#357](https://github.com/haskell/ghcup-hs/issues/357)
|
||||
* fix a (harmless) bug in `ghcup nuke` on windows
|
||||
* improvements to `ghcup add-release-channel` wrt [#708](https://github.com/haskell/ghcup-hs/issues/708)
|
||||
* fix building newer GHC from source wrt [#433](https://github.com/haskell/ghcup-hs/issues/433)
|
||||
* Fix `ghcup install hls -u` on windows
|
||||
* Fix failure with `--isolate=dir --force`
|
||||
* Add `--metadata-fetching-mode` arg, fixes [#440](https://github.com/haskell/ghcup-hs/issues/440)
|
||||
* Add content-length property to downloads
|
||||
* [Fix a grave bug on armv7](https://github.com/haskell/ghcup-hs/commit/78ee956df2618862f421178a565c82548ff7e578) during installation wrt [#415](https://github.com/haskell/ghcup-hs/issues/415)
|
||||
* improve many warning/error messages (contributions by @taylorfausak)
|
||||
* some minor optimization in `ghcup whereis ghcup`
|
||||
* improve `--keep=always` to not clean up directories in certain circumstances
|
||||
|
||||
## 0.1.18.1 -- 2022-08-06
|
||||
|
||||
* fix sdist and unbreak hackage, wrt [#399](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/399)
|
||||
|
||||
## 0.1.18.0 -- 2022-07-30
|
||||
|
||||
* Fix tui set wrt [#266](https://gitlab.haskell.org/haskell/ghcup-hs/-/merge_requests/266) by Arjun Kathuria
|
||||
- Ask the user to install the tool via prompt when setting an non-installed version
|
||||
* improvements to safe (un-)installations
|
||||
- bindists that don't support `make DESTDIR=/some/tmp/dir install` are now unsupported
|
||||
- installed GHC files are now recorded to avoid use of `removePathForcibly`
|
||||
- internally uses a newtype wrapper for user-input paths and restrict destructive operations to validated paths
|
||||
* Add `--disable-ld-override` for darwin bindists wrt #391
|
||||
* Allow passing bindist configure args wrt #377
|
||||
* use of `TMPDIR` is dropped... now uses an internal tmp dir `~/.ghcup/tmp`
|
||||
* improvements to error handling and warnings
|
||||
* Require --isolate to have an absolute directory, fixes #367
|
||||
* Fix mingw PATH handling wrt #371
|
||||
* Add --mingw-path switch to `ghcup run`
|
||||
* Fix `ghcup run` on windows, fixes #375
|
||||
* Improve `ghcup compile <hls|ghc>`
|
||||
- short hashes now work
|
||||
- print the long hash in addition to the detected version
|
||||
* Improve `ghcup compile hls`
|
||||
- add `--git-describe-version` switch as an alternative to `--overwrite-version`
|
||||
- Allow to build HLS from hackage (now is the default)
|
||||
- Allow to run 'cabal update' automatically before the HLS build
|
||||
- Fix parser and completer for 'ghcup compile hls --version'
|
||||
* Improve `ghcup compile ghc`
|
||||
- Allow to build from arbitrary GHC source dists
|
||||
|
||||
## 0.1.17.10 -- 2022-05-12
|
||||
|
||||
* windows hotfix (hackage-only release)
|
||||
|
||||
## 0.1.17.9 -- 2022-05-12
|
||||
|
||||
* broken sdist (hackage-only release)
|
||||
|
||||
## 0.1.17.8 -- 2022-05-11
|
||||
|
||||
* Fix a serious (but hard to trigger) bug when combining `--isolate <DIR>` with `--force`, please make sure to upgrade or avoid `--force`
|
||||
* Fix HLS build not cleaning up properly on failed installations, fixes [#361](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/361)
|
||||
* Fix parsing of symlinks with multiple slashes, wrt [#353](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/353)
|
||||
* Re-enable upgrade functionality for all configurations wrt [MR #250](https://gitlab.haskell.org/haskell/ghcup-hs/-/merge_requests/250) and [VSCode haskell issue #601](https://github.com/haskell/vscode-haskell/issues/601)
|
||||
* Fix `ghcup run --ghc 8.10` (for short versions) wrt [#360](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/360)
|
||||
- this also introduces a `--quick` switch for `ghcup run`
|
||||
|
||||
## 0.1.17.7 -- 2022-04-21
|
||||
|
||||
* Fix `ghcup run` on windows wrt [#345](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/345)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[](https://gitter.im/haskell/ghcup?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
<a href="https://opencollective.com/ghcup#category-CONTRIBUTE"><img src="https://opencollective.com/webpack/donate/button@2x.png?color=blue" alt="Donate" width="150"></a>
|
||||
|
||||
GHCup is an installer for the general purpose language [Haskell](https://www.haskell.org/).
|
||||
GHCup is the main installer for the general purpose language [Haskell](https://www.haskell.org/).
|
||||
|
||||
Visit the [documentation](https://www.haskell.org/ghcup/) for installation instructions.
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE TypeApplications #-}
|
||||
{-# LANGUAGE RankNTypes #-}
|
||||
{-# LANGUAGE ViewPatterns #-}
|
||||
|
||||
module BrickMain where
|
||||
|
||||
@@ -13,9 +14,12 @@ import GHCup.Errors
|
||||
import GHCup.Types.Optics ( getDirs )
|
||||
import GHCup.Types hiding ( LeanAppState(..) )
|
||||
import GHCup.Utils
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Utils.Prelude ( decUTF8Safe )
|
||||
import GHCup.Utils.File
|
||||
import GHCup.OptParse.Common (logGHCPostRm)
|
||||
import GHCup.Prelude ( decUTF8Safe )
|
||||
import GHCup.Prelude.File
|
||||
import GHCup.Prelude.Logger
|
||||
import GHCup.Prelude.Process
|
||||
import GHCup.Prompts
|
||||
|
||||
import Brick
|
||||
import Brick.Widgets.Border
|
||||
@@ -44,7 +48,6 @@ import Data.Vector ( Vector
|
||||
import Data.Versions hiding ( str )
|
||||
import Haskus.Utils.Variant.Excepts
|
||||
import Prelude hiding ( appendFile )
|
||||
import System.Directory ( canonicalizePath )
|
||||
import System.FilePath
|
||||
import System.Exit
|
||||
import System.IO.Unsafe
|
||||
@@ -52,6 +55,8 @@ import Text.PrettyPrint.HughesPJClass ( prettyShow )
|
||||
import URI.ByteString
|
||||
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.Lazy.Builder as B
|
||||
import qualified Data.Text.Lazy as L
|
||||
import qualified Graphics.Vty as Vty
|
||||
import qualified Data.Vector as V
|
||||
import System.Environment (getExecutablePath)
|
||||
@@ -68,8 +73,8 @@ data BrickData = BrickData
|
||||
deriving Show
|
||||
|
||||
data BrickSettings = BrickSettings
|
||||
{ showAllVersions :: Bool
|
||||
, showAllTools :: Bool
|
||||
{ showAllVersions :: Bool
|
||||
, showAllTools :: Bool
|
||||
}
|
||||
deriving Show
|
||||
|
||||
@@ -91,14 +96,14 @@ data BrickState = BrickState
|
||||
keyHandlers :: KeyBindings
|
||||
-> [ ( Vty.Key
|
||||
, BrickSettings -> String
|
||||
, BrickState -> EventM n (Next BrickState)
|
||||
, BrickState -> EventM String BrickState ()
|
||||
)
|
||||
]
|
||||
keyHandlers KeyBindings {..} =
|
||||
[ (bQuit, const "Quit" , halt)
|
||||
[ (bQuit, const "Quit" , \_ -> halt)
|
||||
, (bInstall, const "Install" , withIOAction install')
|
||||
, (bUninstall, const "Uninstall", withIOAction del')
|
||||
, (bSet, const "Set" , withIOAction ((liftIO .) . set'))
|
||||
, (bSet, const "Set" , withIOAction set')
|
||||
, (bChangelog, const "ChangeLog", withIOAction changelog')
|
||||
, ( bShowAllVersions
|
||||
, \BrickSettings {..} ->
|
||||
@@ -110,14 +115,14 @@ keyHandlers KeyBindings {..} =
|
||||
if showAllTools then "Don't show all tools" else "Show all tools"
|
||||
, hideShowHandler showAllVersions (not . showAllTools)
|
||||
)
|
||||
, (bUp, const "Up", \BrickState {..} -> continue BrickState{ appState = moveCursor 1 appState Up, .. })
|
||||
, (bDown, const "Down", \BrickState {..} -> continue BrickState{ appState = moveCursor 1 appState Down, .. })
|
||||
, (bUp, const "Up", \BrickState {..} -> put BrickState{ appState = moveCursor 1 appState Up, .. })
|
||||
, (bDown, const "Down", \BrickState {..} -> put BrickState{ appState = moveCursor 1 appState Down, .. })
|
||||
]
|
||||
where
|
||||
hideShowHandler f p BrickState{..} =
|
||||
let newAppSettings = appSettings { showAllVersions = f appSettings , showAllTools = p appSettings }
|
||||
newInternalState = constructList appData newAppSettings (Just appState)
|
||||
in continue (BrickState appData newAppSettings newInternalState appKeys)
|
||||
in put (BrickState appData newAppSettings newInternalState appKeys)
|
||||
|
||||
|
||||
showKey :: Vty.Key -> String
|
||||
@@ -138,7 +143,7 @@ ui dimAttrs BrickState{ appSettings = as@BrickSettings{}, ..}
|
||||
|
||||
where
|
||||
footer =
|
||||
withAttr "help"
|
||||
withAttr (attrName "help")
|
||||
. txtWrap
|
||||
. T.pack
|
||||
. foldr1 (\x y -> x <> " " <> y)
|
||||
@@ -150,12 +155,15 @@ ui dimAttrs BrickState{ appSettings = as@BrickSettings{}, ..}
|
||||
<+> minHSize 15 (str "Version")
|
||||
<+> padLeft (Pad 1) (minHSize 25 $ str "Tags")
|
||||
<+> padLeft (Pad 5) (str "Notes")
|
||||
renderList' = withDefAttr listAttr . drawListElements renderItem True
|
||||
renderItem _ b listResult@ListResult{..} =
|
||||
renderList' bis@BrickInternalState{..} =
|
||||
let getMinLength = length . intercalate "," . fmap tagToString
|
||||
minLength = V.maximum $ V.map (getMinLength . lTag) clr
|
||||
in withDefAttr listAttr . drawListElements (renderItem minLength) True $ bis
|
||||
renderItem minTagSize _ b listResult@ListResult{lTag = lTag', ..} =
|
||||
let marks = if
|
||||
| lSet -> (withAttr "set" $ str "✔✔")
|
||||
| lInstalled -> (withAttr "installed" $ str "✓ ")
|
||||
| otherwise -> (withAttr "not-installed" $ str "✗ ")
|
||||
| lSet -> (withAttr (attrName "set") $ str "✔✔")
|
||||
| lInstalled -> (withAttr (attrName "installed") $ str "✓ ")
|
||||
| otherwise -> (withAttr (attrName "not-installed") $ str "✗ ")
|
||||
ver = case lCross of
|
||||
Nothing -> T.unpack . prettyVer $ lVer
|
||||
Just c -> T.unpack (c <> "-" <> prettyVer lVer)
|
||||
@@ -163,13 +171,13 @@ ui dimAttrs BrickState{ appSettings = as@BrickSettings{}, ..}
|
||||
| lNoBindist && not lInstalled
|
||||
&& not b -- TODO: overloading dim and active ignores active
|
||||
-- so we hack around it here
|
||||
= updateAttrMap (const dimAttrs) . withAttr "no-bindist"
|
||||
= updateAttrMap (const dimAttrs) . withAttr (attrName "no-bindist")
|
||||
| otherwise = id
|
||||
hooray
|
||||
| elem Latest lTag && not lInstalled =
|
||||
withAttr "hooray"
|
||||
| elem Latest lTag' && not lInstalled =
|
||||
withAttr (attrName "hooray")
|
||||
| otherwise = id
|
||||
active = if b then putCursor "GHCup" (Location (0,0)) . forceAttr "active" else id
|
||||
active = if b then putCursor "GHCup" (Location (0,0)) . forceAttr (attrName "active") else id
|
||||
in hooray $ active $ dim
|
||||
( marks
|
||||
<+> padLeft (Pad 2)
|
||||
@@ -177,8 +185,8 @@ ui dimAttrs BrickState{ appSettings = as@BrickSettings{}, ..}
|
||||
(printTool lTool)
|
||||
)
|
||||
<+> minHSize 15 (str ver)
|
||||
<+> (let l = catMaybes . fmap printTag $ sort lTag
|
||||
in padLeft (Pad 1) $ minHSize 25 $ if null l
|
||||
<+> (let l = catMaybes . fmap printTag $ sort lTag'
|
||||
in padLeft (Pad 1) $ minHSize minTagSize $ if null l
|
||||
then emptyWidget
|
||||
else foldr1 (\x y -> x <+> str "," <+> y) l
|
||||
)
|
||||
@@ -191,11 +199,14 @@ ui dimAttrs BrickState{ appSettings = as@BrickSettings{}, ..}
|
||||
<+> vLimit 1 (fill ' ')
|
||||
)
|
||||
|
||||
printTag Recommended = Just $ withAttr "recommended" $ str "recommended"
|
||||
printTag Latest = Just $ withAttr "latest" $ str "latest"
|
||||
printTag Prerelease = Just $ withAttr "prerelease" $ str "prerelease"
|
||||
printTag Recommended = Just $ withAttr (attrName "recommended") $ str "recommended"
|
||||
printTag Latest = Just $ withAttr (attrName "latest") $ str "latest"
|
||||
printTag Prerelease = Just $ withAttr (attrName "prerelease") $ str "prerelease"
|
||||
printTag Nightly = Just $ withAttr (attrName "nightly") $ str "nightly"
|
||||
printTag (Base pvp'') = Just $ str ("base-" ++ T.unpack (prettyPVP pvp''))
|
||||
printTag Old = Nothing
|
||||
printTag LatestPrerelease = Just $ withAttr (attrName "latest-prerelease") $ str "latest-prerelease"
|
||||
printTag LatestNightly = Just $ withAttr (attrName "latest-nightly") $ str "latest-nightly"
|
||||
printTag (UnknownTag t) = Just $ str t
|
||||
|
||||
printTool Cabal = str "cabal"
|
||||
@@ -205,10 +216,13 @@ ui dimAttrs BrickState{ appSettings = as@BrickSettings{}, ..}
|
||||
printTool Stack = str "Stack"
|
||||
|
||||
printNotes ListResult {..} =
|
||||
(if hlsPowered then [withAttr "hls-powered" $ str "hls-powered"] else mempty
|
||||
(if hlsPowered then [withAttr (attrName "hls-powered") $ str "hls-powered"] else mempty
|
||||
)
|
||||
++ (if fromSrc then [withAttr "compiled" $ str "compiled"] else mempty)
|
||||
++ (if lStray then [withAttr "stray" $ str "stray"] else mempty)
|
||||
++ (if fromSrc then [withAttr (attrName "compiled") $ str "compiled"] else mempty)
|
||||
++ (if lStray then [withAttr (attrName "stray") $ str "stray"] else mempty)
|
||||
++ (case lReleaseDay of
|
||||
Nothing -> mempty
|
||||
Just d -> [withAttr (attrName "day") $ str (show d)])
|
||||
|
||||
-- | Draws the list elements.
|
||||
--
|
||||
@@ -238,8 +252,8 @@ ui dimAttrs BrickState{ appSettings = as@BrickSettings{}, ..}
|
||||
selItemAttr = if foc
|
||||
then withDefAttr listSelectedFocusedAttr
|
||||
else withDefAttr listSelectedAttr
|
||||
makeVisible = if isSelected then visible . selItemAttr else id
|
||||
in addSeparator $ makeVisible elemWidget
|
||||
makeVisible' = if isSelected then visible . selItemAttr else id
|
||||
in addSeparator $ makeVisible' elemWidget
|
||||
|
||||
in render
|
||||
$ viewport "GHCup" Vertical
|
||||
@@ -254,8 +268,8 @@ minHSize s' = hLimit s' . vLimit 1 . (<+> fill ' ')
|
||||
app :: AttrMap -> AttrMap -> App BrickState e String
|
||||
app attrs dimAttrs =
|
||||
App { appDraw = \st -> [ui dimAttrs st]
|
||||
, appHandleEvent = eventHandler
|
||||
, appStartEvent = return
|
||||
, appHandleEvent = \be -> get >>= \s -> eventHandler s be
|
||||
, appStartEvent = return ()
|
||||
, appAttrMap = const attrs
|
||||
, appChooseCursor = showFirstCursor
|
||||
}
|
||||
@@ -263,18 +277,22 @@ app attrs dimAttrs =
|
||||
defaultAttributes :: Bool -> AttrMap
|
||||
defaultAttributes no_color = attrMap
|
||||
Vty.defAttr
|
||||
[ ("active" , Vty.defAttr `withBackColor` Vty.blue)
|
||||
, ("not-installed", Vty.defAttr `withForeColor` Vty.red)
|
||||
, ("set" , Vty.defAttr `withForeColor` Vty.green)
|
||||
, ("installed" , Vty.defAttr `withForeColor` Vty.green)
|
||||
, ("recommended" , Vty.defAttr `withForeColor` Vty.green)
|
||||
, ("hls-powered" , Vty.defAttr `withForeColor` Vty.green)
|
||||
, ("latest" , Vty.defAttr `withForeColor` Vty.yellow)
|
||||
, ("prerelease" , Vty.defAttr `withForeColor` Vty.red)
|
||||
, ("compiled" , Vty.defAttr `withForeColor` Vty.blue)
|
||||
, ("stray" , Vty.defAttr `withForeColor` Vty.blue)
|
||||
, ("help" , Vty.defAttr `withStyle` Vty.italic)
|
||||
, ("hooray" , Vty.defAttr `withForeColor` Vty.brightWhite)
|
||||
[ (attrName "active" , Vty.defAttr `withBackColor` Vty.blue)
|
||||
, (attrName "not-installed" , Vty.defAttr `withForeColor` Vty.red)
|
||||
, (attrName "set" , Vty.defAttr `withForeColor` Vty.green)
|
||||
, (attrName "installed" , Vty.defAttr `withForeColor` Vty.green)
|
||||
, (attrName "recommended" , Vty.defAttr `withForeColor` Vty.green)
|
||||
, (attrName "hls-powered" , Vty.defAttr `withForeColor` Vty.green)
|
||||
, (attrName "latest" , Vty.defAttr `withForeColor` Vty.yellow)
|
||||
, (attrName "latest-prerelease" , Vty.defAttr `withForeColor` Vty.red)
|
||||
, (attrName "latest-nightly" , Vty.defAttr `withForeColor` Vty.red)
|
||||
, (attrName "prerelease" , Vty.defAttr `withForeColor` Vty.red)
|
||||
, (attrName "nightly" , Vty.defAttr `withForeColor` Vty.red)
|
||||
, (attrName "compiled" , Vty.defAttr `withForeColor` Vty.blue)
|
||||
, (attrName "stray" , Vty.defAttr `withForeColor` Vty.blue)
|
||||
, (attrName "day" , Vty.defAttr `withForeColor` Vty.blue)
|
||||
, (attrName "help" , Vty.defAttr `withStyle` Vty.italic)
|
||||
, (attrName "hooray" , Vty.defAttr `withForeColor` Vty.brightWhite)
|
||||
]
|
||||
where
|
||||
withForeColor | no_color = const
|
||||
@@ -288,31 +306,31 @@ defaultAttributes no_color = attrMap
|
||||
dimAttributes :: Bool -> AttrMap
|
||||
dimAttributes no_color = attrMap
|
||||
(Vty.defAttr `Vty.withStyle` Vty.dim)
|
||||
[ ("active" , Vty.defAttr `withBackColor` Vty.blue) -- has no effect ??
|
||||
, ("no-bindist", Vty.defAttr `Vty.withStyle` Vty.dim)
|
||||
[ (attrName "active" , Vty.defAttr `withBackColor` Vty.blue) -- has no effect ??
|
||||
, (attrName "no-bindist", Vty.defAttr `Vty.withStyle` Vty.dim)
|
||||
]
|
||||
where
|
||||
withBackColor | no_color = \attr _ -> attr `Vty.withStyle` Vty.reverseVideo
|
||||
| otherwise = Vty.withBackColor
|
||||
|
||||
eventHandler :: BrickState -> BrickEvent n e -> EventM n (Next BrickState)
|
||||
eventHandler :: BrickState -> BrickEvent String e -> EventM String BrickState ()
|
||||
eventHandler st@BrickState{..} ev = do
|
||||
AppState { keyBindings = kb } <- liftIO $ readIORef settings'
|
||||
case ev of
|
||||
(MouseDown _ Vty.BScrollUp _ _) ->
|
||||
continue (BrickState { appState = moveCursor 1 appState Up, .. })
|
||||
put (BrickState { appState = moveCursor 1 appState Up, .. })
|
||||
(MouseDown _ Vty.BScrollDown _ _) ->
|
||||
continue (BrickState { appState = moveCursor 1 appState Down, .. })
|
||||
(VtyEvent (Vty.EvResize _ _)) -> continue st
|
||||
put (BrickState { appState = moveCursor 1 appState Down, .. })
|
||||
(VtyEvent (Vty.EvResize _ _)) -> put st
|
||||
(VtyEvent (Vty.EvKey Vty.KUp _)) ->
|
||||
continue BrickState{ appState = moveCursor 1 appState Up, .. }
|
||||
put BrickState{ appState = moveCursor 1 appState Up, .. }
|
||||
(VtyEvent (Vty.EvKey Vty.KDown _)) ->
|
||||
continue BrickState{ appState = moveCursor 1 appState Down, .. }
|
||||
put BrickState{ appState = moveCursor 1 appState Down, .. }
|
||||
(VtyEvent (Vty.EvKey key _)) ->
|
||||
case find (\(key', _, _) -> key' == key) (keyHandlers kb) of
|
||||
Nothing -> continue st
|
||||
Nothing -> put st
|
||||
Just (_, _, handler) -> handler st
|
||||
_ -> continue st
|
||||
_ -> put st
|
||||
|
||||
|
||||
moveCursor :: Int -> BrickInternalState -> Direction -> BrickInternalState
|
||||
@@ -325,13 +343,14 @@ moveCursor steps ais@BrickInternalState{..} direction =
|
||||
|
||||
-- | Suspend the current UI and run an IO action in terminal. If the
|
||||
-- IO action returns a Left value, then it's thrown as userError.
|
||||
withIOAction :: (BrickState
|
||||
withIOAction :: Ord n
|
||||
=> (BrickState
|
||||
-> (Int, ListResult)
|
||||
-> ReaderT AppState IO (Either String a))
|
||||
-> BrickState
|
||||
-> EventM n (Next BrickState)
|
||||
-> EventM n BrickState ()
|
||||
withIOAction action as = case listSelectedElement' (appState as) of
|
||||
Nothing -> continue as
|
||||
Nothing -> put as
|
||||
Just (ix, e) -> do
|
||||
suspendAndResume $ do
|
||||
settings <- readIORef settings'
|
||||
@@ -400,13 +419,17 @@ filterVisible :: Bool -> Bool -> ListResult -> Bool
|
||||
filterVisible v t e | lInstalled e = True
|
||||
| v
|
||||
, not t
|
||||
, Nightly `notElem` lTag e
|
||||
, lTool e `notElem` hiddenTools = True
|
||||
| not v
|
||||
, t
|
||||
, Old `notElem` lTag e = True
|
||||
, Old `notElem` lTag e
|
||||
, Nightly `notElem` lTag e = True
|
||||
| v
|
||||
, Nightly `notElem` lTag e
|
||||
, t = True
|
||||
| otherwise = (Old `notElem` lTag e) &&
|
||||
| otherwise = (Old `notElem` lTag e) &&
|
||||
(Nightly `notElem` lTag e) &&
|
||||
(lTool e `notElem` hiddenTools)
|
||||
|
||||
|
||||
@@ -430,6 +453,7 @@ install' _ (_, ListResult {..}) = do
|
||||
, BuildFailed
|
||||
, TagNotFound
|
||||
, DigestError
|
||||
, ContentLengthError
|
||||
, GPGError
|
||||
, DownloadFailed
|
||||
, DirNotEmpty
|
||||
@@ -437,6 +461,9 @@ install' _ (_, ListResult {..}) = do
|
||||
, TarDirDoesNotExist
|
||||
, FileAlreadyExistsError
|
||||
, ProcessError
|
||||
, ToolShadowed
|
||||
, UninstallFailed
|
||||
, MergeFileTreeError
|
||||
]
|
||||
|
||||
run (do
|
||||
@@ -446,19 +473,19 @@ install' _ (_, ListResult {..}) = do
|
||||
case lTool of
|
||||
GHC -> do
|
||||
let vi = getVersionInfo lVer GHC dls
|
||||
liftE $ installGHCBin lVer Nothing False $> (vi, dirs, ce)
|
||||
liftE $ installGHCBin lVer GHCupInternal False [] $> (vi, dirs, ce)
|
||||
Cabal -> do
|
||||
let vi = getVersionInfo lVer Cabal dls
|
||||
liftE $ installCabalBin lVer Nothing False $> (vi, dirs, ce)
|
||||
liftE $ installCabalBin lVer GHCupInternal False $> (vi, dirs, ce)
|
||||
GHCup -> do
|
||||
let vi = snd <$> getLatest dls GHCup
|
||||
liftE $ upgradeGHCup Nothing False $> (vi, dirs, ce)
|
||||
liftE $ upgradeGHCup Nothing False False $> (vi, dirs, ce)
|
||||
HLS -> do
|
||||
let vi = getVersionInfo lVer HLS dls
|
||||
liftE $ installHLSBin lVer Nothing False $> (vi, dirs, ce)
|
||||
liftE $ installHLSBin lVer GHCupInternal False $> (vi, dirs, ce)
|
||||
Stack -> do
|
||||
let vi = getVersionInfo lVer Stack dls
|
||||
liftE $ installStackBin lVer Nothing False $> (vi, dirs, ce)
|
||||
liftE $ installStackBin lVer GHCupInternal False $> (vi, dirs, ce)
|
||||
)
|
||||
>>= \case
|
||||
VRight (vi, Dirs{..}, Just ce) -> do
|
||||
@@ -479,13 +506,16 @@ install' _ (_, ListResult {..}) = do
|
||||
pure $ Right ()
|
||||
VLeft (V (AlreadyInstalled _ _)) -> pure $ Right ()
|
||||
VLeft (V NoUpdate) -> pure $ Right ()
|
||||
VLeft e -> pure $ Left $ prettyShow e <> "\n"
|
||||
VLeft e -> pure $ Left $ prettyHFError e <> "\n"
|
||||
<> "Also check the logs in ~/.ghcup/logs"
|
||||
|
||||
|
||||
set' :: BrickState -> (Int, ListResult) -> IO (Either String ())
|
||||
set' _ (_, ListResult {..}) = do
|
||||
settings <- readIORef settings'
|
||||
set' :: (MonadReader AppState m, MonadIO m, MonadThrow m, MonadFail m, MonadMask m, MonadUnliftIO m)
|
||||
=> BrickState
|
||||
-> (Int, ListResult)
|
||||
-> m (Either String ())
|
||||
set' bs input@(_, ListResult {..}) = do
|
||||
settings <- liftIO $ readIORef settings'
|
||||
|
||||
let run =
|
||||
flip runReaderT settings
|
||||
@@ -501,7 +531,28 @@ set' _ (_, ListResult {..}) = do
|
||||
)
|
||||
>>= \case
|
||||
VRight _ -> pure $ Right ()
|
||||
VLeft e -> pure $ Left (prettyShow e)
|
||||
VLeft e -> case e of
|
||||
(V (NotInstalled tool _)) -> do
|
||||
promptAnswer <- getUserPromptResponse userPrompt
|
||||
case promptAnswer of
|
||||
PromptYes -> do
|
||||
res <- install' bs input
|
||||
case res of
|
||||
(Left err) -> pure $ Left err
|
||||
(Right _) -> do
|
||||
logInfo "Setting now..."
|
||||
set' bs input
|
||||
|
||||
PromptNo -> pure $ Left (prettyHFError e)
|
||||
where
|
||||
userPrompt = L.toStrict . B.toLazyText . B.fromString $
|
||||
"This Version of "
|
||||
<> show tool
|
||||
<> " you are trying to set is not installed.\n"
|
||||
<> "Would you like to install it first? [Y/N]: "
|
||||
|
||||
_ -> pure $ Left (prettyHFError e)
|
||||
|
||||
|
||||
|
||||
del' :: (MonadReader AppState m, MonadIO m, MonadFail m, MonadMask m, MonadUnliftIO m)
|
||||
@@ -511,7 +562,7 @@ del' :: (MonadReader AppState m, MonadIO m, MonadFail m, MonadMask m, MonadUnlif
|
||||
del' _ (_, ListResult {..}) = do
|
||||
AppState { ghcupInfo = GHCupInfo { _ghcupDownloads = dls }} <- ask
|
||||
|
||||
let run = runE @'[NotInstalled]
|
||||
let run = runE @'[NotInstalled, UninstallFailed]
|
||||
|
||||
run (do
|
||||
let vi = getVersionInfo lVer lTool dls
|
||||
@@ -524,10 +575,11 @@ del' _ (_, ListResult {..}) = do
|
||||
)
|
||||
>>= \case
|
||||
VRight vi -> do
|
||||
when (lTool == GHC) $ logGHCPostRm (mkTVer lVer)
|
||||
forM_ (_viPostRemove =<< vi) $ \msg ->
|
||||
logInfo msg
|
||||
pure $ Right ()
|
||||
VLeft e -> pure $ Left (prettyShow e)
|
||||
VLeft e -> pure $ Left (prettyHFError e)
|
||||
|
||||
|
||||
changelog' :: (MonadReader AppState m, MonadIO m)
|
||||
@@ -536,7 +588,7 @@ changelog' :: (MonadReader AppState m, MonadIO m)
|
||||
-> m (Either String ())
|
||||
changelog' _ (_, ListResult {..}) = do
|
||||
AppState { pfreq, ghcupInfo = GHCupInfo { _ghcupDownloads = dls }} <- ask
|
||||
case getChangeLog dls lTool (Left lVer) of
|
||||
case getChangeLog dls lTool (ToolVersion lVer) of
|
||||
Nothing -> pure $ Left $
|
||||
"Could not find ChangeLog for " <> prettyShow lTool <> ", version " <> T.unpack (prettyVer lVer)
|
||||
Just uri -> do
|
||||
@@ -547,7 +599,7 @@ changelog' _ (_, ListResult {..}) = do
|
||||
Windows -> "start"
|
||||
exec cmd [T.unpack $ decUTF8Safe $ serializeURIRef' uri] Nothing Nothing >>= \case
|
||||
Right _ -> pure $ Right ()
|
||||
Left e -> pure $ Left $ prettyShow e
|
||||
Left e -> pure $ Left $ prettyHFError e
|
||||
|
||||
|
||||
settings' :: IORef AppState
|
||||
@@ -600,12 +652,12 @@ getGHCupInfo = do
|
||||
|
||||
r <-
|
||||
flip runReaderT settings
|
||||
. runE @'[DigestError, GPGError, JSONError , DownloadFailed , FileDoesNotExistError]
|
||||
. runE @'[DigestError, ContentLengthError, GPGError, JSONError , DownloadFailed , FileDoesNotExistError]
|
||||
$ liftE getDownloadsF
|
||||
|
||||
case r of
|
||||
VRight a -> pure $ Right a
|
||||
VLeft e -> pure $ Left (prettyShow e)
|
||||
VLeft e -> pure $ Left (prettyHFError e)
|
||||
|
||||
|
||||
getAppData :: Maybe GHCupInfo
|
||||
@@ -616,5 +668,5 @@ getAppData mgi = runExceptT $ do
|
||||
settings <- liftIO $ readIORef settings'
|
||||
|
||||
flip runReaderT settings $ do
|
||||
lV <- listVersions Nothing Nothing
|
||||
lV <- listVersions Nothing [] False True (Nothing, Nothing)
|
||||
pure $ BrickData (reverse lV)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
module GHCup.OptParse (
|
||||
module GHCup.OptParse.Common
|
||||
, module GHCup.OptParse.Install
|
||||
, module GHCup.OptParse.Test
|
||||
, module GHCup.OptParse.Set
|
||||
, module GHCup.OptParse.UnSet
|
||||
, module GHCup.OptParse.Rm
|
||||
@@ -31,6 +32,7 @@ module GHCup.OptParse (
|
||||
|
||||
import GHCup.OptParse.Common
|
||||
import GHCup.OptParse.Install
|
||||
import GHCup.OptParse.Test
|
||||
import GHCup.OptParse.Set
|
||||
import GHCup.OptParse.UnSet
|
||||
import GHCup.OptParse.Rm
|
||||
@@ -67,13 +69,14 @@ import URI.ByteString
|
||||
import qualified Data.ByteString.UTF8 as UTF8
|
||||
|
||||
|
||||
|
||||
data Options = Options
|
||||
{
|
||||
-- global options
|
||||
optVerbose :: Maybe Bool
|
||||
, optCache :: Maybe Bool
|
||||
, optMetaCache :: Maybe Integer
|
||||
, optMetaMode :: Maybe MetaMode
|
||||
, optPlatform :: Maybe PlatformRequest
|
||||
, optUrlSource :: Maybe URI
|
||||
, optNoVerify :: Maybe Bool
|
||||
, optKeepDirs :: Maybe KeepDirs
|
||||
@@ -86,6 +89,7 @@ data Options = Options
|
||||
|
||||
data Command
|
||||
= Install (Either InstallCommand InstallOptions)
|
||||
| Test TestCommand
|
||||
| InstallCabalLegacy InstallOptions
|
||||
| Set (Either SetCommand SetOptions)
|
||||
| UnSet UnsetCommand
|
||||
@@ -96,7 +100,7 @@ data Command
|
||||
| Config ConfigCommand
|
||||
| Whereis WhereisOptions WhereisCommand
|
||||
#ifndef DISABLE_UPGRADE
|
||||
| Upgrade UpgradeOpts Bool
|
||||
| Upgrade UpgradeOpts Bool Bool
|
||||
#endif
|
||||
| ToolRequirements ToolReqOpts
|
||||
| ChangeLog ChangeLogOptions
|
||||
@@ -107,6 +111,7 @@ data Command
|
||||
| Prefetch PrefetchCommand
|
||||
| GC GCOptions
|
||||
| Run RunOptions
|
||||
| PrintAppErrors
|
||||
|
||||
|
||||
|
||||
@@ -115,7 +120,18 @@ opts =
|
||||
Options
|
||||
<$> invertableSwitch "verbose" (Just 'v') False (help "Enable verbosity (default: disabled)")
|
||||
<*> invertableSwitch "cache" (Just 'c') False (help "Cache downloads in ~/.ghcup/cache (default: disabled)")
|
||||
<*> optional (option auto (long "metadata-caching" <> help "How long the yaml metadata caching interval is (in seconds), 0 to disable" <> internal))
|
||||
<*> optional (option auto (long "metadata-caching" <> metavar "SEC" <> help "How long the yaml metadata caching interval is (in seconds), 0 to disable"))
|
||||
<*> optional (option auto (long "metadata-fetching-mode" <> metavar "<Strict|Lax>" <> help "Whether to fail on metadata download failure (Strict) or fall back to cached version (Lax (default))"))
|
||||
<*> optional
|
||||
(option
|
||||
(eitherReader platformParser)
|
||||
( short 'p'
|
||||
<> long "platform"
|
||||
<> metavar "PLATFORM"
|
||||
<> help
|
||||
"Override for platform (triple matching ghc tarball names), e.g. x86_64-fedora27-linux"
|
||||
)
|
||||
)
|
||||
<*> optional
|
||||
(option
|
||||
(eitherReader parseUri)
|
||||
@@ -192,6 +208,14 @@ com =
|
||||
<> footerDoc (Just $ text installToolFooter)
|
||||
)
|
||||
)
|
||||
<> command
|
||||
"test"
|
||||
(info
|
||||
(Test <$> testParser <**> helper)
|
||||
( progDesc "Run tests for a tool (if any) [EXPERIMENTAL!]"
|
||||
<> footerDoc (Just $ text testFooter)
|
||||
)
|
||||
)
|
||||
<> command
|
||||
"set"
|
||||
(info
|
||||
@@ -220,20 +244,21 @@ com =
|
||||
<> command
|
||||
"list"
|
||||
(info (List <$> listOpts <**> helper)
|
||||
(progDesc "Show available GHCs and other tools")
|
||||
(progDesc "Show available GHCs and other tools"
|
||||
<> footerDoc (Just $ text listToolFooter))
|
||||
)
|
||||
#ifndef DISABLE_UPGRADE
|
||||
<> command
|
||||
"upgrade"
|
||||
(info
|
||||
( (Upgrade <$> upgradeOptsP <*> switch
|
||||
(short 'f' <> long "force" <> help "Force update")
|
||||
<*> switch
|
||||
(long "fail-if-shadowed" <> help "Fails after upgrading if the upgraded ghcup binary is shadowed by something else in PATH (useful for CI)")
|
||||
)
|
||||
<**> helper
|
||||
)
|
||||
(progDesc "Upgrade ghcup")
|
||||
)
|
||||
#endif
|
||||
<> command
|
||||
"compile"
|
||||
( Compile
|
||||
@@ -329,3 +354,10 @@ com =
|
||||
<> commandGroup "Nuclear Commands:"
|
||||
<> hidden
|
||||
)
|
||||
<|> subparser
|
||||
(command
|
||||
"print-app-errors"
|
||||
(info (pure PrintAppErrors <**> helper)
|
||||
(progDesc ""))
|
||||
<> internal
|
||||
)
|
||||
|
||||
@@ -12,9 +12,12 @@ module GHCup.OptParse.ChangeLog where
|
||||
|
||||
|
||||
import GHCup.Types
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Errors
|
||||
import GHCup.OptParse.Common
|
||||
import GHCup.Utils.String.QQ
|
||||
import GHCup.Prelude
|
||||
import GHCup.Prelude.Logger
|
||||
import GHCup.Prelude.String.QQ
|
||||
import GHCup.Prelude.Process (exec)
|
||||
|
||||
#if !MIN_VERSION_base(4,13,0)
|
||||
import Control.Monad.Fail ( MonadFail )
|
||||
@@ -32,10 +35,7 @@ import qualified Data.Text as T
|
||||
import Control.Exception.Safe (MonadMask)
|
||||
import GHCup.Types.Optics
|
||||
import GHCup.Utils
|
||||
import Data.Versions
|
||||
import URI.ByteString (serializeURIRef')
|
||||
import GHCup.Utils.Prelude
|
||||
import GHCup.Utils.File (exec)
|
||||
import Data.Char (toLower)
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ data ChangeLogOptions = ChangeLogOptions
|
||||
--[ Parsers ]--
|
||||
---------------
|
||||
|
||||
|
||||
|
||||
changelogP :: Parser ChangeLogOptions
|
||||
changelogP =
|
||||
(\x y -> ChangeLogOptions x y)
|
||||
@@ -71,15 +71,16 @@ changelogP =
|
||||
"cabal" -> Right Cabal
|
||||
"ghcup" -> Right GHCup
|
||||
"stack" -> Right Stack
|
||||
"hls" -> Right HLS
|
||||
e -> Left e
|
||||
)
|
||||
)
|
||||
(short 't' <> long "tool" <> metavar "<ghc|cabal|ghcup>" <> help
|
||||
(short 't' <> long "tool" <> metavar "<ghc|cabal|hls|ghcup>" <> help
|
||||
"Open changelog for given tool (default: ghc)"
|
||||
<> completer toolCompleter
|
||||
)
|
||||
)
|
||||
<*> optional (toolVersionArgument Nothing Nothing)
|
||||
<*> optional (toolVersionTagArgument [] Nothing)
|
||||
|
||||
|
||||
|
||||
@@ -113,19 +114,15 @@ changelog :: ( Monad m
|
||||
changelog ChangeLogOptions{..} runAppState runLogger = do
|
||||
GHCupInfo { _ghcupDownloads = dls } <- runAppState getGHCupInfo
|
||||
let tool = fromMaybe GHC clTool
|
||||
ver' = maybe
|
||||
(Right Latest)
|
||||
(\case
|
||||
ToolVersion tv -> Left (_tvVersion tv) -- FIXME: ugly sharing of ToolVersion
|
||||
ToolTag t -> Right t
|
||||
)
|
||||
ver' = fromMaybe
|
||||
(ToolTag Latest)
|
||||
clToolVer
|
||||
muri = getChangeLog dls tool ver'
|
||||
case muri of
|
||||
Nothing -> do
|
||||
runLogger
|
||||
(logWarn $
|
||||
"Could not find ChangeLog for " <> T.pack (prettyShow tool) <> ", version " <> either prettyVer (T.pack . show) ver'
|
||||
"Could not find ChangeLog for " <> T.pack (prettyShow tool) <> ", version " <> T.pack (prettyShow ver')
|
||||
)
|
||||
pure ExitSuccess
|
||||
Just uri -> do
|
||||
@@ -146,6 +143,6 @@ changelog ChangeLogOptions{..} runAppState runLogger = do
|
||||
Nothing
|
||||
>>= \case
|
||||
Right _ -> pure ExitSuccess
|
||||
Left e -> logError (T.pack $ prettyShow e)
|
||||
Left e -> logError (T.pack $ prettyHFError e)
|
||||
>> pure (ExitFailure 13)
|
||||
else liftIO $ putStrLn uri' >> pure ExitSuccess
|
||||
|
||||
@@ -16,15 +16,16 @@ import GHCup.Platform
|
||||
import GHCup.Types
|
||||
import GHCup.Types.Optics
|
||||
import GHCup.Utils
|
||||
import GHCup.Utils.File
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Utils.MegaParsec
|
||||
import GHCup.Utils.Prelude
|
||||
import GHCup.Prelude
|
||||
import GHCup.Prelude.Process
|
||||
import GHCup.Prelude.Logger
|
||||
import GHCup.Prelude.MegaParsec
|
||||
|
||||
import Control.DeepSeq
|
||||
import Control.Concurrent
|
||||
import Control.Concurrent.Async
|
||||
import Control.Exception.Safe
|
||||
import Control.Monad.Identity (Identity(..))
|
||||
#if !MIN_VERSION_base(4,13,0)
|
||||
import Control.Monad.Fail ( MonadFail )
|
||||
#endif
|
||||
@@ -44,6 +45,8 @@ import Data.Functor
|
||||
import Data.List ( nub, sort, sortBy, isPrefixOf, stripPrefix )
|
||||
import Data.Maybe
|
||||
import Data.Text ( Text )
|
||||
import Data.Time.Calendar ( Day )
|
||||
import Data.Time.Format ( parseTimeM, defaultTimeLocale )
|
||||
import Data.Versions hiding ( str )
|
||||
import Data.Void
|
||||
import qualified Data.Vector as V
|
||||
@@ -52,7 +55,6 @@ import Haskus.Utils.Variant.Excepts
|
||||
import Options.Applicative hiding ( style )
|
||||
import Prelude hiding ( appendFile )
|
||||
import Safe
|
||||
import System.Directory
|
||||
import System.Process ( readProcess )
|
||||
import System.FilePath
|
||||
import Text.HTML.TagSoup hiding ( Tag )
|
||||
@@ -65,28 +67,33 @@ import qualified Text.Megaparsec as MP
|
||||
import qualified System.FilePath.Posix as FP
|
||||
import GHCup.Version
|
||||
import Control.Exception (evaluate)
|
||||
import qualified Cabal.Config as CC
|
||||
|
||||
|
||||
-------------
|
||||
--[ Types ]--
|
||||
-------------
|
||||
|
||||
data ToolVersion = ToolVersion GHCTargetVersion -- target is ignored for cabal
|
||||
| ToolTag Tag
|
||||
|
||||
-- a superset of ToolVersion
|
||||
data SetToolVersion = SetToolVersion GHCTargetVersion
|
||||
data SetToolVersion = SetGHCVersion GHCTargetVersion
|
||||
| SetToolVersion Version
|
||||
| SetToolTag Tag
|
||||
| SetToolDay Day
|
||||
| SetRecommended
|
||||
| SetNext
|
||||
|
||||
prettyToolVer :: ToolVersion -> String
|
||||
prettyToolVer (ToolVersion v') = T.unpack $ tVerToText v'
|
||||
prettyToolVer (ToolTag t) = show t
|
||||
prettyToolVer (GHCVersion v') = T.unpack $ tVerToText v'
|
||||
prettyToolVer (ToolVersion v') = T.unpack $ prettyVer v'
|
||||
prettyToolVer (ToolTag t) = show t
|
||||
prettyToolVer (ToolDay day) = show day
|
||||
|
||||
toSetToolVer :: Maybe ToolVersion -> SetToolVersion
|
||||
toSetToolVer (Just (GHCVersion v')) = SetGHCVersion v'
|
||||
toSetToolVer (Just (ToolVersion v')) = SetToolVersion v'
|
||||
toSetToolVer (Just (ToolTag t')) = SetToolTag t'
|
||||
toSetToolVer (Just (ToolDay d')) = SetToolDay d'
|
||||
toSetToolVer Nothing = SetRecommended
|
||||
|
||||
|
||||
@@ -97,32 +104,30 @@ toSetToolVer Nothing = SetRecommended
|
||||
--------------
|
||||
|
||||
|
||||
-- | same as toolVersionParser, except as an argument.
|
||||
toolVersionArgument :: Maybe ListCriteria -> Maybe Tool -> Parser ToolVersion
|
||||
toolVersionArgument criteria tool =
|
||||
argument (eitherReader toolVersionEither)
|
||||
toolVersionTagArgument :: [ListCriteria] -> Maybe Tool -> Parser ToolVersion
|
||||
toolVersionTagArgument criteria tool =
|
||||
argument (eitherReader (parser tool))
|
||||
(metavar (mv tool)
|
||||
<> completer (tagCompleter (fromMaybe GHC tool) [])
|
||||
<> foldMap (completer . versionCompleter criteria) tool)
|
||||
where
|
||||
mv (Just GHC) = "GHC_VERSION|TAG"
|
||||
mv (Just HLS) = "HLS_VERSION|TAG"
|
||||
mv _ = "VERSION|TAG"
|
||||
mv (Just GHC) = "GHC_VERSION|TAG|RELEASE_DATE"
|
||||
mv (Just HLS) = "HLS_VERSION|TAG|RELEASE_DATE"
|
||||
mv _ = "VERSION|TAG|RELEASE_DATE"
|
||||
|
||||
parser (Just GHC) = ghcVersionTagEither
|
||||
parser Nothing = ghcVersionTagEither
|
||||
parser _ = toolVersionTagEither
|
||||
|
||||
|
||||
versionParser :: Parser GHCTargetVersion
|
||||
versionParser = option
|
||||
(eitherReader tVersionEither)
|
||||
(short 'v' <> long "version" <> metavar "VERSION" <> help "The target version"
|
||||
)
|
||||
|
||||
versionParser' :: Maybe ListCriteria -> Maybe Tool -> Parser Version
|
||||
versionParser' :: [ListCriteria] -> Maybe Tool -> Parser Version
|
||||
versionParser' criteria tool = argument
|
||||
(eitherReader (first show . version . T.pack))
|
||||
(metavar "VERSION" <> foldMap (completer . versionCompleter criteria) tool)
|
||||
|
||||
versionArgument :: Maybe ListCriteria -> Maybe Tool -> Parser GHCTargetVersion
|
||||
versionArgument criteria tool = argument (eitherReader tVersionEither) (metavar "VERSION" <> foldMap (completer . versionCompleter criteria) tool)
|
||||
ghcVersionArgument :: [ListCriteria] -> Maybe Tool -> Parser GHCTargetVersion
|
||||
ghcVersionArgument criteria tool = argument (eitherReader ghcVersionEither)
|
||||
(metavar "VERSION" <> foldMap (completer . versionCompleter criteria) tool)
|
||||
|
||||
|
||||
-- https://github.com/pcapriotti/optparse-applicative/issues/148
|
||||
@@ -227,28 +232,40 @@ absolutePathParser f = case isValid f && isAbsolute f of
|
||||
False -> Left "Please enter a valid absolute filepath."
|
||||
|
||||
isolateParser :: FilePath -> Either String FilePath
|
||||
isolateParser f = case isValid f of
|
||||
isolateParser f = case isValid f && isAbsolute f of
|
||||
True -> Right $ normalise f
|
||||
False -> Left "Please enter a valid filepath for isolate dir."
|
||||
|
||||
toolVersionEither :: String -> Either String ToolVersion
|
||||
toolVersionEither s' =
|
||||
second ToolTag (tagEither s') <|> second ToolVersion (tVersionEither s')
|
||||
-- this accepts cross prefix
|
||||
ghcVersionTagEither :: String -> Either String ToolVersion
|
||||
ghcVersionTagEither s' =
|
||||
second ToolDay (dayParser s') <|> second ToolTag (tagEither s') <|> second GHCVersion (ghcVersionEither s')
|
||||
|
||||
-- this ignores cross prefix
|
||||
toolVersionTagEither :: String -> Either String ToolVersion
|
||||
toolVersionTagEither s' =
|
||||
second ToolDay (dayParser s') <|> second ToolTag (tagEither s') <|> second ToolVersion (toolVersionEither s')
|
||||
|
||||
tagEither :: String -> Either String Tag
|
||||
tagEither s' = case fmap toLower s' of
|
||||
"recommended" -> Right Recommended
|
||||
"latest" -> Right Latest
|
||||
"recommended" -> Right Recommended
|
||||
"latest" -> Right Latest
|
||||
"latest-prerelease" -> Right LatestPrerelease
|
||||
"latest-nightly" -> Right LatestNightly
|
||||
('b':'a':'s':'e':'-':ver') -> case pvp (T.pack ver') of
|
||||
Right x -> Right (Base x)
|
||||
Left _ -> Left $ "Invalid PVP version for base " <> ver'
|
||||
other -> Left $ "Unknown tag " <> other
|
||||
other -> Left $ "Unknown tag " <> other
|
||||
|
||||
|
||||
tVersionEither :: String -> Either String GHCTargetVersion
|
||||
tVersionEither =
|
||||
ghcVersionEither :: String -> Either String GHCTargetVersion
|
||||
ghcVersionEither =
|
||||
first (const "Not a valid version") . MP.parse ghcTargetVerP "" . T.pack
|
||||
|
||||
toolVersionEither :: String -> Either String Version
|
||||
toolVersionEither =
|
||||
first (const "Not a valid version") . MP.parse (version' <* MP.eof) "" . T.pack
|
||||
|
||||
|
||||
toolParser :: String -> Either String Tool
|
||||
toolParser s' | t == T.pack "ghc" = Right GHC
|
||||
@@ -258,12 +275,22 @@ toolParser s' | t == T.pack "ghc" = Right GHC
|
||||
| otherwise = Left ("Unknown tool: " <> s')
|
||||
where t = T.toLower (T.pack s')
|
||||
|
||||
dayParser :: String -> Either String Day
|
||||
dayParser s = maybe (Left $ "Could not parse \"" <> s <> "\". Expected format is: YYYY-MM-DD") Right
|
||||
$ parseTimeM True defaultTimeLocale "%Y-%-m-%-d" s
|
||||
|
||||
|
||||
criteriaParser :: String -> Either String ListCriteria
|
||||
criteriaParser s' | t == T.pack "installed" = Right ListInstalled
|
||||
| t == T.pack "set" = Right ListSet
|
||||
| t == T.pack "available" = Right ListAvailable
|
||||
| otherwise = Left ("Unknown criteria: " <> s')
|
||||
criteriaParser s' | t == T.pack "installed" = Right $ ListInstalled True
|
||||
| t == T.pack "set" = Right $ ListSet True
|
||||
| t == T.pack "available" = Right $ ListAvailable True
|
||||
| t == T.pack "+installed" = Right $ ListInstalled True
|
||||
| t == T.pack "+set" = Right $ ListSet True
|
||||
| t == T.pack "+available" = Right $ ListAvailable True
|
||||
| t == T.pack "-installed" = Right $ ListInstalled False
|
||||
| t == T.pack "-set" = Right $ ListSet False
|
||||
| t == T.pack "-available" = Right $ ListAvailable False
|
||||
| otherwise = Left ("Unknown criteria: " <> s')
|
||||
where t = T.toLower (T.pack s')
|
||||
|
||||
|
||||
@@ -439,11 +466,13 @@ tagCompleter tool add = listIOCompleter $ do
|
||||
let allTags = filter (/= Old)
|
||||
$ _viTags =<< M.elems (availableToolVersions (_ghcupDownloads ghcupInfo) tool)
|
||||
pure $ nub $ (add ++) $ fmap tagToString allTags
|
||||
VLeft _ -> pure (nub $ ["recommended", "latest"] ++ add)
|
||||
VLeft _ -> pure (nub $ ["recommended", "latest", "latest-prerelease"] ++ add)
|
||||
|
||||
versionCompleter :: [ListCriteria] -> Tool -> Completer
|
||||
versionCompleter criteria tool = versionCompleter' criteria tool (const True)
|
||||
|
||||
versionCompleter :: Maybe ListCriteria -> Tool -> Completer
|
||||
versionCompleter criteria tool = listIOCompleter $ do
|
||||
versionCompleter' :: [ListCriteria] -> Tool -> (Version -> Bool) -> Completer
|
||||
versionCompleter' criteria tool filter' = listIOCompleter $ do
|
||||
dirs' <- liftIO getAllDirs
|
||||
let loggerConfig = LoggerConfig
|
||||
{ lcPrintDebug = False
|
||||
@@ -471,8 +500,8 @@ versionCompleter criteria tool = listIOCompleter $ do
|
||||
|
||||
runEnv = flip runReaderT appState
|
||||
|
||||
installedVersions <- runEnv $ listVersions (Just tool) criteria
|
||||
return $ T.unpack . prettyVer . lVer <$> installedVersions
|
||||
installedVersions <- runEnv $ listVersions (Just tool) criteria False False (Nothing, Nothing)
|
||||
return $ fmap (T.unpack . prettyVer) . filter filter' . fmap lVer $ installedVersions
|
||||
|
||||
|
||||
toolDlCompleter :: Tool -> Completer
|
||||
@@ -639,6 +668,7 @@ fromVersion :: ( HasLog env
|
||||
-> Tool
|
||||
-> Excepts
|
||||
'[ TagNotFound
|
||||
, DayNotFound
|
||||
, NextVerNotFound
|
||||
, NoToolVersionSet
|
||||
] m (GHCTargetVersion, Maybe VersionInfo)
|
||||
@@ -657,6 +687,7 @@ fromVersion' :: ( HasLog env
|
||||
-> Tool
|
||||
-> Excepts
|
||||
'[ TagNotFound
|
||||
, DayNotFound
|
||||
, NextVerNotFound
|
||||
, NoToolVersionSet
|
||||
] m (GHCTargetVersion, Maybe VersionInfo)
|
||||
@@ -664,7 +695,7 @@ fromVersion' SetRecommended tool = do
|
||||
GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
|
||||
bimap mkTVer Just <$> getRecommended dls tool
|
||||
?? TagNotFound Recommended tool
|
||||
fromVersion' (SetToolVersion v) tool = do
|
||||
fromVersion' (SetGHCVersion v) tool = do
|
||||
GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
|
||||
let vi = getVersionInfo (_tvVersion v) tool dls
|
||||
case pvp $ prettyVer (_tvVersion v) of -- need to be strict here
|
||||
@@ -676,9 +707,32 @@ fromVersion' (SetToolVersion v) tool = do
|
||||
when (v' /= _tvVersion v) $ lift $ logWarn ("Assuming you meant version " <> prettyVer v')
|
||||
pure (GHCTargetVersion (_tvTarget v) v', Just vi')
|
||||
Nothing -> pure (v, vi)
|
||||
fromVersion' (SetToolVersion v) tool = do
|
||||
GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
|
||||
let vi = getVersionInfo v tool dls
|
||||
case pvp $ prettyVer v of -- need to be strict here
|
||||
Left _ -> pure (mkTVer v, vi)
|
||||
Right pvpIn ->
|
||||
lift (getLatestToolFor tool pvpIn dls) >>= \case
|
||||
Just (pvp_, vi') -> do
|
||||
v' <- lift $ pvpToVersion pvp_ ""
|
||||
when (v' /= v) $ lift $ logWarn ("Assuming you meant version " <> prettyVer v')
|
||||
pure (GHCTargetVersion mempty v', Just vi')
|
||||
Nothing -> pure (mkTVer v, vi)
|
||||
fromVersion' (SetToolTag Latest) tool = do
|
||||
GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
|
||||
bimap mkTVer Just <$> getLatest dls tool ?? TagNotFound Latest tool
|
||||
fromVersion' (SetToolDay day) tool = do
|
||||
GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
|
||||
bimap mkTVer Just <$> case getByReleaseDay dls tool day of
|
||||
Left ad -> throwE $ DayNotFound day tool ad
|
||||
Right v -> pure v
|
||||
fromVersion' (SetToolTag LatestPrerelease) tool = do
|
||||
GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
|
||||
bimap mkTVer Just <$> getLatestPrerelease dls tool ?? TagNotFound LatestPrerelease tool
|
||||
fromVersion' (SetToolTag LatestNightly) tool = do
|
||||
GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
|
||||
bimap mkTVer Just <$> getLatestNightly dls tool ?? TagNotFound LatestNightly tool
|
||||
fromVersion' (SetToolTag Recommended) tool = do
|
||||
GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
|
||||
bimap mkTVer Just <$> getRecommended dls tool ?? TagNotFound Recommended tool
|
||||
@@ -748,7 +802,7 @@ checkForUpdates :: ( MonadReader env m
|
||||
=> m [(Tool, Version)]
|
||||
checkForUpdates = do
|
||||
GHCupInfo { _ghcupDownloads = dls } <- getGHCupInfo
|
||||
lInstalled <- listVersions Nothing (Just ListInstalled)
|
||||
lInstalled <- listVersions Nothing [ListInstalled True] False False (Nothing, Nothing)
|
||||
let latestInstalled tool = (fmap lVer . lastMay . filter (\lr -> lTool lr == tool)) lInstalled
|
||||
|
||||
ghcup <- forMM (getLatest dls GHCup) $ \(l, _) -> do
|
||||
@@ -764,3 +818,12 @@ checkForUpdates = do
|
||||
pure $ catMaybes (ghcup:otherTools)
|
||||
where
|
||||
forMM a f = fmap join $ forM a f
|
||||
|
||||
|
||||
logGHCPostRm :: (MonadReader env m, HasLog env, MonadIO m) => GHCTargetVersion -> m ()
|
||||
logGHCPostRm ghcVer = do
|
||||
cabalStore <- liftIO $ handleIO (\_ -> if isWindows then pure "C:\\cabal\\store" else pure "~/.cabal/store")
|
||||
(runIdentity . CC.cfgStoreDir <$> CC.readConfig)
|
||||
let storeGhcDir = cabalStore </> ("ghc-" <> T.unpack (prettyVer $ _tvVersion ghcVer))
|
||||
logInfo $ T.pack $ "After removing GHC you might also want to clean up your cabal store at: " <> storeGhcDir
|
||||
|
||||
|
||||
@@ -12,14 +12,15 @@ module GHCup.OptParse.Compile where
|
||||
|
||||
|
||||
import GHCup
|
||||
import qualified GHCup.GHC as GHC
|
||||
import qualified GHCup.HLS as HLS
|
||||
import GHCup.Errors
|
||||
import GHCup.Utils.File
|
||||
import GHCup.Types
|
||||
import GHCup.Types.Optics
|
||||
import GHCup.Utils
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Prelude.Logger
|
||||
import GHCup.Prelude.String.QQ
|
||||
import GHCup.OptParse.Common
|
||||
import GHCup.Utils.String.QQ
|
||||
|
||||
#if !MIN_VERSION_base(4,13,0)
|
||||
import Control.Monad.Fail ( MonadFail )
|
||||
@@ -31,18 +32,18 @@ import Control.Monad.Trans.Resource
|
||||
import Data.Bifunctor
|
||||
import Data.Functor
|
||||
import Data.Maybe
|
||||
import Data.Versions ( Version, prettyVer, version )
|
||||
import Data.Versions ( Version, prettyVer, version, pvp )
|
||||
import qualified Data.Versions as V
|
||||
import Data.Text ( Text )
|
||||
import Haskus.Utils.Variant.Excepts
|
||||
import Options.Applicative hiding ( style )
|
||||
import Options.Applicative.Help.Pretty ( text )
|
||||
import Prelude hiding ( appendFile )
|
||||
import System.Exit
|
||||
import Text.PrettyPrint.HughesPJClass ( prettyShow )
|
||||
|
||||
import URI.ByteString hiding ( uriParser )
|
||||
import qualified Data.Text as T
|
||||
import Control.Exception.Safe (MonadMask)
|
||||
import Control.Exception.Safe (MonadMask, displayException)
|
||||
import System.FilePath (isPathSeparator)
|
||||
import Text.Read (readEither)
|
||||
|
||||
@@ -65,7 +66,7 @@ data CompileCommand = CompileGHC GHCCompileOptions
|
||||
|
||||
|
||||
data GHCCompileOptions = GHCCompileOptions
|
||||
{ targetGhc :: Either Version GitBranch
|
||||
{ targetGhc :: GHC.GHCVer Version
|
||||
, bootstrapGhc :: Either Version FilePath
|
||||
, jobs :: Maybe Int
|
||||
, buildConfig :: Maybe FilePath
|
||||
@@ -79,11 +80,13 @@ data GHCCompileOptions = GHCCompileOptions
|
||||
, isolateDir :: Maybe FilePath
|
||||
}
|
||||
|
||||
|
||||
data HLSCompileOptions = HLSCompileOptions
|
||||
{ targetHLS :: Either Version GitBranch
|
||||
{ targetHLS :: HLS.HLSVer
|
||||
, jobs :: Maybe Int
|
||||
, setCompile :: Bool
|
||||
, ovewrwiteVer :: Maybe Version
|
||||
, updateCabal :: Bool
|
||||
, ovewrwiteVer :: Either Bool Version
|
||||
, isolateDir :: Maybe FilePath
|
||||
, cabalProject :: Maybe (Either FilePath URI)
|
||||
, cabalProjectLocal :: Maybe URI
|
||||
@@ -99,7 +102,7 @@ data HLSCompileOptions = HLSCompileOptions
|
||||
--[ Parsers ]--
|
||||
---------------
|
||||
|
||||
|
||||
|
||||
compileP :: Parser CompileCommand
|
||||
compileP = subparser
|
||||
( command
|
||||
@@ -146,29 +149,31 @@ Examples:
|
||||
|
||||
compileHLSFooter = [s|Discussion:
|
||||
Compiles and installs the specified HLS version.
|
||||
The last argument is a list of GHC versions to compile for.
|
||||
The --ghc arguments are necessary to specify which GHC version to build for/against.
|
||||
These need to be available in PATH prior to compilation.
|
||||
|
||||
Examples:
|
||||
# compile 1.4.0 for ghc 8.10.5 and 8.10.7
|
||||
ghcup compile hls -v 1.4.0 -j 12 --ghc 8.10.5 --ghc 8.10.7
|
||||
# compile from master for ghc 8.10.7, linking everything dynamically
|
||||
ghcup compile hls -g master -j 12 --ghc 8.10.7 -- --ghc-options='-dynamic'|]
|
||||
# compile 1.7.0.0 from hackage for 8.10.7, running 'cabal update' before the build
|
||||
ghcup compile hls --version 1.7.0.0 --ghc 8.10.7 --cabal-update
|
||||
# compile from master for ghc 9.2.3 using 'git describe' to name the binary and ignore the pinned index state
|
||||
ghcup compile hls -g master --git-describe-version --ghc 9.2.3 -- --index-state=@(date '+%s')
|
||||
# compile a specific commit for ghc 9.2.3 and set a specifc version for the binary name
|
||||
ghcup compile hls -g a32db0b -o 1.7.0.0-p1 --ghc 9.2.3|]
|
||||
|
||||
|
||||
ghcCompileOpts :: Parser GHCCompileOptions
|
||||
ghcCompileOpts =
|
||||
GHCCompileOptions
|
||||
<$> ((Left <$> option
|
||||
<$> ((GHC.SourceDist <$> option
|
||||
(eitherReader
|
||||
(first (const "Not a valid version") . version . T.pack)
|
||||
)
|
||||
(short 'v' <> long "version" <> metavar "VERSION" <> help
|
||||
"The tool version to compile"
|
||||
<> (completer $ versionCompleter Nothing GHC)
|
||||
<> (completer $ versionCompleter [] GHC)
|
||||
)
|
||||
) <|>
|
||||
(Right <$> (GitBranch <$> option
|
||||
(GHC.GitDist <$> (GitBranch <$> option
|
||||
str
|
||||
(short 'g' <> long "git-ref" <> metavar "GIT_REFERENCE" <> help
|
||||
"The git commit/branch/ref to build from"
|
||||
@@ -177,7 +182,18 @@ ghcCompileOpts =
|
||||
short 'r' <> long "repository" <> metavar "GIT_REPOSITORY" <> help "The git repository to build from (defaults to GHC upstream)"
|
||||
<> completer (gitFileUri ["https://gitlab.haskell.org/ghc/ghc.git"])
|
||||
))
|
||||
)))
|
||||
))
|
||||
<|>
|
||||
(
|
||||
GHC.RemoteDist <$> (option
|
||||
(eitherReader uriParser)
|
||||
(long "remote-source-dist" <> metavar "URI" <> help
|
||||
"URI (https/http/file) to a GHC source distribution"
|
||||
<> completer fileUri
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
<*> option
|
||||
(eitherReader
|
||||
(\x ->
|
||||
@@ -189,7 +205,7 @@ ghcCompileOpts =
|
||||
<> metavar "BOOTSTRAP_GHC"
|
||||
<> help
|
||||
"The GHC version (or full path) to bootstrap with (must be installed)"
|
||||
<> (completer $ versionCompleter Nothing GHC)
|
||||
<> (completer $ versionCompleter [] GHC)
|
||||
)
|
||||
<*> optional
|
||||
(option
|
||||
@@ -233,7 +249,7 @@ ghcCompileOpts =
|
||||
"Build cross-compiler for this platform"
|
||||
)
|
||||
)
|
||||
<*> many (argument str (metavar "CONFIGURE_ARGS" <> help "Additional arguments to configure, prefix with '-- ' (longopts)"))
|
||||
<*> many (argument str (metavar "CONFIGURE_ARGS" <> help "Additional arguments to compile configure, prefix with '-- ' (longopts)"))
|
||||
<*> fmap (fromMaybe False) (invertableSwitch "set" Nothing False (help "Set as active version after install"))
|
||||
<*> optional
|
||||
(option
|
||||
@@ -242,7 +258,7 @@ ghcCompileOpts =
|
||||
)
|
||||
(short 'o' <> long "overwrite-version" <> metavar "OVERWRITE_VERSION" <> help
|
||||
"Allows to overwrite the finally installed VERSION with a different one, e.g. when you build 8.10.4 with your own patches, you might want to set this to '8.10.4-p1'"
|
||||
<> (completer $ versionCompleter Nothing GHC)
|
||||
<> (completer $ versionCompleter [] GHC)
|
||||
)
|
||||
)
|
||||
<*> optional
|
||||
@@ -269,24 +285,46 @@ ghcCompileOpts =
|
||||
hlsCompileOpts :: Parser HLSCompileOptions
|
||||
hlsCompileOpts =
|
||||
HLSCompileOptions
|
||||
<$> ((Left <$> option
|
||||
<$> ((HLS.HackageDist <$> option
|
||||
(eitherReader
|
||||
(first (const "Not a valid version") . version . T.pack)
|
||||
((>>= first displayException . V.version . V.prettyPVP) . first (const "Not a valid PVP version") . pvp . T.pack)
|
||||
)
|
||||
(short 'v' <> long "version" <> metavar "VERSION" <> help
|
||||
"The tool version to compile"
|
||||
<> (completer $ versionCompleter Nothing HLS)
|
||||
"The version to compile (pulled from hackage)"
|
||||
<> (completer $ versionCompleter' [] HLS (either (const False) (const True) . V.pvp . V.prettyVer))
|
||||
)
|
||||
) <|>
|
||||
(Right <$> (GitBranch <$> option
|
||||
)
|
||||
<|>
|
||||
(HLS.GitDist <$> (GitBranch <$> option
|
||||
str
|
||||
(short 'g' <> long "git-ref" <> metavar "GIT_REFERENCE" <> help
|
||||
"The git commit/branch/ref to build from"
|
||||
"The git commit/branch/ref to build from (accepts anything 'git checkout' accepts)"
|
||||
) <*>
|
||||
optional (option str (short 'r' <> long "repository" <> metavar "GIT_REPOSITORY" <> help "The git repository to build from (defaults to HLS upstream)"
|
||||
<> completer (gitFileUri ["https://github.com/haskell/haskell-language-server.git"])
|
||||
))
|
||||
)))
|
||||
))
|
||||
<|>
|
||||
(HLS.SourceDist <$> (option
|
||||
(eitherReader
|
||||
(first (const "Not a valid version") . version . T.pack)
|
||||
)
|
||||
(long "source-dist" <> metavar "VERSION" <> help
|
||||
"The version to compile (pulled from packaged git sources)"
|
||||
<> (completer $ versionCompleter [] HLS)
|
||||
)
|
||||
))
|
||||
<|>
|
||||
(
|
||||
HLS.RemoteDist <$> (option
|
||||
(eitherReader uriParser)
|
||||
(long "remote-source-dist" <> metavar "URI" <> help
|
||||
"URI (https/http/file) to a HLS source distribution"
|
||||
<> completer fileUri
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
<*> optional
|
||||
(option
|
||||
(eitherReader (readEither @Int))
|
||||
@@ -296,16 +334,26 @@ hlsCompileOpts =
|
||||
)
|
||||
)
|
||||
<*> fmap (fromMaybe True) (invertableSwitch "set" Nothing True (help "Don't set as active version after install"))
|
||||
<*> optional
|
||||
(option
|
||||
<*> switch (long "cabal-update" <> help "Run 'cabal update' before the build")
|
||||
<*>
|
||||
(
|
||||
(Right <$> option
|
||||
(eitherReader
|
||||
(first (const "Not a valid version") . version . T.pack)
|
||||
)
|
||||
(short 'o' <> long "overwrite-version" <> metavar "OVERWRITE_VERSION" <> help
|
||||
"Allows to overwrite the finally installed VERSION with a different one, e.g. when you build 8.10.4 with your own patches, you might want to set this to '8.10.4-p1'"
|
||||
<> (completer $ versionCompleter Nothing HLS)
|
||||
<> (completer $ versionCompleter [] HLS)
|
||||
)
|
||||
)
|
||||
<|>
|
||||
(Left <$> (switch
|
||||
(long "git-describe-version"
|
||||
<> help "Use the output of 'git describe' (if building from git) as the VERSION component of the installed binary."
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
<*> optional
|
||||
(option
|
||||
(eitherReader isolateParser)
|
||||
@@ -352,10 +400,10 @@ hlsCompileOpts =
|
||||
)
|
||||
)
|
||||
<*> some (
|
||||
option (eitherReader toolVersionEither)
|
||||
option (eitherReader ghcVersionTagEither)
|
||||
( long "ghc" <> metavar "GHC_VERSION|TAG" <> help "For which GHC version to compile for (can be specified multiple times)"
|
||||
<> completer (tagCompleter GHC [])
|
||||
<> completer (versionCompleter Nothing GHC))
|
||||
<> completer (versionCompleter [] GHC))
|
||||
)
|
||||
<*> many (argument str (metavar "CABAL_ARGS" <> help "Additional arguments to cabal install, prefix with '-- ' (longopts)"))
|
||||
|
||||
@@ -371,6 +419,7 @@ hlsCompileOpts =
|
||||
type GHCEffects = '[ AlreadyInstalled
|
||||
, BuildFailed
|
||||
, DigestError
|
||||
, ContentLengthError
|
||||
, GPGError
|
||||
, DownloadFailed
|
||||
, GHCupSetError
|
||||
@@ -388,10 +437,13 @@ type GHCEffects = '[ AlreadyInstalled
|
||||
, ProcessError
|
||||
, CopyError
|
||||
, BuildFailed
|
||||
, UninstallFailed
|
||||
, MergeFileTreeError
|
||||
]
|
||||
type HLSEffects = '[ AlreadyInstalled
|
||||
, BuildFailed
|
||||
, DigestError
|
||||
, ContentLengthError
|
||||
, GPGError
|
||||
, DownloadFailed
|
||||
, GHCupSetError
|
||||
@@ -401,11 +453,14 @@ type HLSEffects = '[ AlreadyInstalled
|
||||
, UnknownArchive
|
||||
, TarDirDoesNotExist
|
||||
, TagNotFound
|
||||
, DayNotFound
|
||||
, NextVerNotFound
|
||||
, NoToolVersionSet
|
||||
, NotInstalled
|
||||
, DirNotEmpty
|
||||
, ArchiveResult
|
||||
, UninstallFailed
|
||||
, MergeFileTreeError
|
||||
]
|
||||
|
||||
|
||||
@@ -454,7 +509,7 @@ compile compileCommand settings Dirs{..} runAppState runLogger = do
|
||||
(CompileHLS HLSCompileOptions { .. }) -> do
|
||||
runCompileHLS runAppState (do
|
||||
case targetHLS of
|
||||
Left targetVer -> do
|
||||
HLS.SourceDist targetVer -> do
|
||||
GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
|
||||
let vi = getVersionInfo targetVer HLS dls
|
||||
forM_ (_viPreCompile =<< vi) $ \msg -> do
|
||||
@@ -462,16 +517,17 @@ compile compileCommand settings Dirs{..} runAppState runLogger = do
|
||||
lift $ logInfo
|
||||
"...waiting for 5 seconds, you can still abort..."
|
||||
liftIO $ threadDelay 5000000 -- for compilation, give the user a sec to intervene
|
||||
Right _ -> pure ()
|
||||
_ -> pure ()
|
||||
ghcs <- liftE $ forM targetGHCs (\ghc -> fmap (_tvVersion . fst) . fromVersion (Just ghc) $ GHC)
|
||||
targetVer <- liftE $ compileHLS
|
||||
targetHLS
|
||||
ghcs
|
||||
jobs
|
||||
ovewrwiteVer
|
||||
isolateDir
|
||||
(maybe GHCupInternal IsolateDir isolateDir)
|
||||
cabalProject
|
||||
cabalProjectLocal
|
||||
updateCabal
|
||||
patches
|
||||
cabalArgs
|
||||
GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
|
||||
@@ -490,14 +546,14 @@ compile compileCommand settings Dirs{..} runAppState runLogger = do
|
||||
pure ExitSuccess
|
||||
VLeft err@(V (BuildFailed tmpdir _)) -> do
|
||||
case keepDirs settings of
|
||||
Never -> runLogger $ logError $ T.pack $ prettyShow err
|
||||
_ -> runLogger (logError $ T.pack (prettyShow err) <> "\n" <>
|
||||
"Check the logs at " <> T.pack logsDir <> " and the build directory "
|
||||
Never -> runLogger $ logError $ T.pack $ prettyHFError err
|
||||
_ -> runLogger (logError $ T.pack (prettyHFError err) <> "\n" <>
|
||||
"Check the logs at " <> T.pack (fromGHCupPath logsDir) <> " and the build directory "
|
||||
<> T.pack tmpdir <> " for more clues." <> "\n" <>
|
||||
"Make sure to clean up " <> T.pack tmpdir <> " afterwards.")
|
||||
pure $ ExitFailure 9
|
||||
VLeft e -> do
|
||||
runLogger $ logError $ T.pack $ prettyShow e
|
||||
runLogger $ logError $ T.pack $ prettyHFError e
|
||||
pure $ ExitFailure 9
|
||||
(CompileGHC GHCCompileOptions { hadrian = True, crossTarget = Just _ }) -> do
|
||||
runLogger $ logError "Hadrian cross compile support is not yet implemented!"
|
||||
@@ -505,7 +561,7 @@ compile compileCommand settings Dirs{..} runAppState runLogger = do
|
||||
(CompileGHC GHCCompileOptions {..}) ->
|
||||
runCompileGHC runAppState (do
|
||||
case targetGhc of
|
||||
Left targetVer -> do
|
||||
GHC.SourceDist targetVer -> do
|
||||
GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
|
||||
let vi = getVersionInfo targetVer GHC dls
|
||||
forM_ (_viPreCompile =<< vi) $ \msg -> do
|
||||
@@ -513,9 +569,12 @@ compile compileCommand settings Dirs{..} runAppState runLogger = do
|
||||
lift $ logInfo
|
||||
"...waiting for 5 seconds, you can still abort..."
|
||||
liftIO $ threadDelay 5000000 -- for compilation, give the user a sec to intervene
|
||||
Right _ -> pure ()
|
||||
_ -> pure ()
|
||||
targetVer <- liftE $ compileGHC
|
||||
(first (GHCTargetVersion crossTarget) targetGhc)
|
||||
((\case
|
||||
GHC.SourceDist v -> GHC.SourceDist $ GHCTargetVersion crossTarget v
|
||||
GHC.GitDist g -> GHC.GitDist g
|
||||
GHC.RemoteDist r -> GHC.RemoteDist r) targetGhc)
|
||||
ovewrwiteVer
|
||||
bootstrapGhc
|
||||
jobs
|
||||
@@ -524,7 +583,7 @@ compile compileCommand settings Dirs{..} runAppState runLogger = do
|
||||
addConfArgs
|
||||
buildFlavour
|
||||
hadrian
|
||||
isolateDir
|
||||
(maybe GHCupInternal IsolateDir isolateDir)
|
||||
GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
|
||||
let vi = getVersionInfo (_tvVersion targetVer) GHC dls
|
||||
when setCompile $ void $ liftE $
|
||||
@@ -541,20 +600,20 @@ compile compileCommand settings Dirs{..} runAppState runLogger = do
|
||||
pure ExitSuccess
|
||||
VLeft (V (AlreadyInstalled _ v)) -> do
|
||||
runLogger $ logWarn $
|
||||
"GHC ver " <> prettyVer v <> " already installed; if you really want to reinstall it, you may want to run 'ghcup install ghc --force " <> prettyVer v <> "'"
|
||||
"GHC ver " <> prettyVer v <> " already installed, remove it first to reinstall"
|
||||
pure ExitSuccess
|
||||
VLeft (V (DirNotEmpty fp)) -> do
|
||||
runLogger $ logWarn $
|
||||
"Install directory " <> T.pack fp <> " is not empty. Use 'ghcup install ghc --isolate " <> T.pack fp <> " --force ..." <> "' to install regardless."
|
||||
runLogger $ logError $
|
||||
"Install directory " <> T.pack fp <> " is not empty."
|
||||
pure $ ExitFailure 3
|
||||
VLeft err@(V (BuildFailed tmpdir _)) -> do
|
||||
case keepDirs settings of
|
||||
Never -> runLogger $ logError $ T.pack $ prettyShow err
|
||||
_ -> runLogger (logError $ T.pack (prettyShow err) <> "\n" <>
|
||||
"Check the logs at " <> T.pack logsDir <> " and the build directory "
|
||||
Never -> runLogger $ logError $ T.pack $ prettyHFError err
|
||||
_ -> runLogger (logError $ T.pack (prettyHFError err) <> "\n" <>
|
||||
"Check the logs at " <> T.pack (fromGHCupPath logsDir) <> " and the build directory "
|
||||
<> T.pack tmpdir <> " for more clues." <> "\n" <>
|
||||
"Make sure to clean up " <> T.pack tmpdir <> " afterwards.")
|
||||
pure $ ExitFailure 9
|
||||
VLeft e -> do
|
||||
runLogger $ logError $ T.pack $ prettyShow e
|
||||
runLogger $ logError $ T.pack $ prettyHFError e
|
||||
pure $ ExitFailure 9
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE DuplicateRecordFields #-}
|
||||
{-# LANGUAGE RankNTypes #-}
|
||||
{-# LANGUAGE ExplicitForAll #-}
|
||||
|
||||
module GHCup.OptParse.Config where
|
||||
|
||||
@@ -15,9 +14,9 @@ module GHCup.OptParse.Config where
|
||||
import GHCup.Errors
|
||||
import GHCup.Types
|
||||
import GHCup.Utils
|
||||
import GHCup.Utils.Prelude
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Utils.String.QQ
|
||||
import GHCup.Prelude
|
||||
import GHCup.Prelude.Logger
|
||||
import GHCup.Prelude.String.QQ
|
||||
import GHCup.OptParse.Common
|
||||
|
||||
#if !MIN_VERSION_base(4,13,0)
|
||||
@@ -52,7 +51,7 @@ data ConfigCommand
|
||||
= ShowConfig
|
||||
| SetConfig String (Maybe String)
|
||||
| InitConfig
|
||||
| AddReleaseChannel URI
|
||||
| AddReleaseChannel Bool URI
|
||||
|
||||
|
||||
|
||||
@@ -60,7 +59,7 @@ data ConfigCommand
|
||||
--[ Parsers ]--
|
||||
---------------
|
||||
|
||||
|
||||
|
||||
configP :: Parser ConfigCommand
|
||||
configP = subparser
|
||||
( command "init" initP
|
||||
@@ -75,7 +74,7 @@ configP = subparser
|
||||
showP = info (pure ShowConfig) (progDesc "Show current config (default)")
|
||||
setP = info argsP (progDesc "Set config KEY to VALUE (or specify as single json value)" <> footerDoc (Just $ text configSetFooter))
|
||||
argsP = SetConfig <$> argument str (metavar "<JSON_VALUE | YAML_KEY>") <*> optional (argument str (metavar "YAML_VALUE"))
|
||||
addP = info (AddReleaseChannel <$> argument (eitherReader uriParser) (metavar "URI" <> completer fileUri))
|
||||
addP = info (AddReleaseChannel <$> switch (long "force" <> help "Delete existing entry (if any) and append instead of failing") <*> argument (eitherReader uriParser) (metavar "URI" <> completer fileUri))
|
||||
(progDesc "Add a release channel from a URI")
|
||||
|
||||
|
||||
@@ -121,18 +120,38 @@ formatConfig :: UserSettings -> String
|
||||
formatConfig = UTF8.toString . Y.encode
|
||||
|
||||
|
||||
updateSettings :: UserSettings -> Settings -> Settings
|
||||
updateSettings UserSettings{..} Settings{..} =
|
||||
let cache' = fromMaybe cache uCache
|
||||
metaCache' = fromMaybe metaCache uMetaCache
|
||||
noVerify' = fromMaybe noVerify uNoVerify
|
||||
keepDirs' = fromMaybe keepDirs uKeepDirs
|
||||
downloader' = fromMaybe downloader uDownloader
|
||||
verbose' = fromMaybe verbose uVerbose
|
||||
urlSource' = fromMaybe urlSource uUrlSource
|
||||
noNetwork' = fromMaybe noNetwork uNoNetwork
|
||||
gpgSetting' = fromMaybe gpgSetting uGPGSetting
|
||||
in Settings cache' metaCache' noVerify' keepDirs' downloader' verbose' urlSource' noNetwork' gpgSetting' noColor
|
||||
updateSettings :: UserSettings -> UserSettings -> UserSettings
|
||||
updateSettings usl usr =
|
||||
let cache' = uCache usl <|> uCache usr
|
||||
metaCache' = uMetaCache usl <|> uMetaCache usr
|
||||
metaMode' = uMetaMode usl <|> uMetaMode usr
|
||||
noVerify' = uNoVerify usl <|> uNoVerify usr
|
||||
verbose' = uVerbose usl <|> uVerbose usr
|
||||
keepDirs' = uKeepDirs usl <|> uKeepDirs usr
|
||||
downloader' = uDownloader usl <|> uDownloader usr
|
||||
urlSource' = uUrlSource usl <|> uUrlSource usr
|
||||
noNetwork' = uNoNetwork usl <|> uNoNetwork usr
|
||||
gpgSetting' = uGPGSetting usl <|> uGPGSetting usr
|
||||
platformOverride' = uPlatformOverride usl <|> uPlatformOverride usr
|
||||
mirrors' = uMirrors usl <|> uMirrors usr
|
||||
in UserSettings cache' metaCache' metaMode' noVerify' verbose' keepDirs' downloader' (updateKeyBindings (uKeyBindings usl) (uKeyBindings usr)) urlSource' noNetwork' gpgSetting' platformOverride' mirrors'
|
||||
where
|
||||
updateKeyBindings :: Maybe UserKeyBindings -> Maybe UserKeyBindings -> Maybe UserKeyBindings
|
||||
updateKeyBindings Nothing Nothing = Nothing
|
||||
updateKeyBindings (Just kbl) Nothing = Just kbl
|
||||
updateKeyBindings Nothing (Just kbr) = Just kbr
|
||||
updateKeyBindings (Just kbl) (Just kbr) =
|
||||
Just $ UserKeyBindings {
|
||||
kUp = kUp kbl <|> kUp kbr
|
||||
, kDown = kDown kbl <|> kDown kbr
|
||||
, kQuit = kQuit kbl <|> kQuit kbr
|
||||
, kInstall = kInstall kbl <|> kInstall kbr
|
||||
, kUninstall = kUninstall kbl <|> kUninstall kbr
|
||||
, kSet = kSet kbl <|> kSet kbr
|
||||
, kChangelog = kChangelog kbl <|> kChangelog kbr
|
||||
, kShowAll = kShowAll kbl <|> kShowAll kbr
|
||||
, kShowAllTools = kShowAllTools kbl <|> kShowAllTools kbr
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -140,6 +159,9 @@ updateSettings UserSettings{..} Settings{..} =
|
||||
--[ Entrypoint ]--
|
||||
------------------
|
||||
|
||||
data Duplicate = Duplicate -- ^ there is a duplicate somewhere in the middle
|
||||
| NoDuplicate -- ^ there is no duplicate
|
||||
| DuplicateLast -- ^ there's a duplicate, but it's the last element
|
||||
|
||||
|
||||
config :: forall m. ( Monad m
|
||||
@@ -149,10 +171,11 @@ config :: forall m. ( Monad m
|
||||
)
|
||||
=> ConfigCommand
|
||||
-> Settings
|
||||
-> UserSettings
|
||||
-> KeyBindings
|
||||
-> (ReaderT LeanAppState m () -> m ())
|
||||
-> m ExitCode
|
||||
config configCommand settings keybindings runLogger = case configCommand of
|
||||
config configCommand settings userConf keybindings runLogger = case configCommand of
|
||||
InitConfig -> do
|
||||
path <- getConfigFilePath
|
||||
liftIO $ writeFile path $ formatConfig $ fromSettings settings (Just keybindings)
|
||||
@@ -183,21 +206,55 @@ config configCommand settings keybindings runLogger = case configCommand of
|
||||
pure $ ExitFailure 65
|
||||
VLeft _ -> pure $ ExitFailure 65
|
||||
|
||||
AddReleaseChannel uri -> do
|
||||
case urlSource settings of
|
||||
AddSource xs -> do
|
||||
doConfig (defaultUserSettings { uUrlSource = Just $ AddSource (xs <> [Right uri]) })
|
||||
pure ExitSuccess
|
||||
_ -> do
|
||||
doConfig (defaultUserSettings { uUrlSource = Just $ AddSource [Right uri] })
|
||||
AddReleaseChannel force uri -> do
|
||||
r <- runE @'[DuplicateReleaseChannel] $ do
|
||||
case urlSource settings of
|
||||
AddSource xs -> do
|
||||
case checkDuplicate xs (Right uri) of
|
||||
Duplicate
|
||||
| not force -> throwE (DuplicateReleaseChannel uri)
|
||||
DuplicateLast -> pure ()
|
||||
_ -> lift $ doConfig (defaultUserSettings { uUrlSource = Just $ AddSource (appendUnique xs (Right uri)) })
|
||||
GHCupURL -> do
|
||||
lift $ doConfig (defaultUserSettings { uUrlSource = Just $ AddSource [Right uri] })
|
||||
pure ()
|
||||
OwnSource xs -> do
|
||||
case checkDuplicate xs (Right uri) of
|
||||
Duplicate
|
||||
| not force -> throwE (DuplicateReleaseChannel uri)
|
||||
DuplicateLast -> pure ()
|
||||
_ -> lift $ doConfig (defaultUserSettings { uUrlSource = Just $ OwnSource (appendUnique xs (Right uri)) })
|
||||
OwnSpec spec -> do
|
||||
lift $ doConfig (defaultUserSettings { uUrlSource = Just $ OwnSource [Left spec, Right uri] })
|
||||
pure ()
|
||||
case r of
|
||||
VRight _ -> do
|
||||
pure ExitSuccess
|
||||
VLeft e -> do
|
||||
runLogger $ logError $ T.pack $ prettyHFError e
|
||||
pure $ ExitFailure 15
|
||||
|
||||
where
|
||||
checkDuplicate :: Eq a => [a] -> a -> Duplicate
|
||||
checkDuplicate xs a
|
||||
| last xs == a = DuplicateLast
|
||||
| a `elem` xs = Duplicate
|
||||
| otherwise = NoDuplicate
|
||||
|
||||
-- appends the element to the end of the list, but also removes it from the existing list
|
||||
appendUnique :: Eq a => [a] -> a -> [a]
|
||||
appendUnique xs' e = go xs'
|
||||
where
|
||||
go [] = [e]
|
||||
go (x:xs)
|
||||
| x == e = go xs -- skip
|
||||
| otherwise = x : go xs
|
||||
|
||||
doConfig :: MonadIO m => UserSettings -> m ()
|
||||
doConfig usersettings = do
|
||||
let settings' = updateSettings usersettings settings
|
||||
let settings' = updateSettings usersettings userConf
|
||||
path <- liftIO getConfigFilePath
|
||||
liftIO $ writeFile path $ formatConfig $ fromSettings settings' (Just keybindings)
|
||||
liftIO $ writeFile path $ formatConfig $ settings'
|
||||
runLogger $ logDebug $ T.pack $ show settings'
|
||||
pure ()
|
||||
|
||||
|
||||
@@ -17,9 +17,10 @@ import GHCup
|
||||
import GHCup.Errors
|
||||
import GHCup.Version
|
||||
import GHCup.Types
|
||||
import GHCup.Utils.Prelude
|
||||
import GHCup.Utils.Dirs
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Prelude
|
||||
import GHCup.Prelude.Logger
|
||||
import GHCup.Prelude.Process
|
||||
|
||||
#if !MIN_VERSION_base(4,13,0)
|
||||
import Control.Monad.Fail ( MonadFail )
|
||||
@@ -36,7 +37,6 @@ import Text.PrettyPrint.HughesPJClass ( prettyShow )
|
||||
|
||||
import qualified Data.Text as T
|
||||
import Control.Exception.Safe (MonadMask)
|
||||
import GHCup.Utils.File
|
||||
import Language.Haskell.TH
|
||||
|
||||
|
||||
@@ -115,5 +115,5 @@ dinfo runAppState runLogger = do
|
||||
liftIO $ putStrLn $ prettyDebugInfo di
|
||||
pure ExitSuccess
|
||||
VLeft e -> do
|
||||
runLogger $ logError $ T.pack $ prettyShow e
|
||||
runLogger $ logError $ T.pack $ prettyHFError e
|
||||
pure $ ExitFailure 8
|
||||
|
||||
@@ -14,8 +14,8 @@ module GHCup.OptParse.GC where
|
||||
import GHCup
|
||||
import GHCup.Errors
|
||||
import GHCup.Types
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Utils.String.QQ
|
||||
import GHCup.Prelude.Logger
|
||||
import GHCup.Prelude.String.QQ
|
||||
|
||||
#if !MIN_VERSION_base(4,13,0)
|
||||
import Control.Monad.Fail ( MonadFail )
|
||||
@@ -27,7 +27,6 @@ import Haskus.Utils.Variant.Excepts
|
||||
import Options.Applicative hiding ( style )
|
||||
import Prelude hiding ( appendFile )
|
||||
import System.Exit
|
||||
import Text.PrettyPrint.HughesPJClass ( prettyShow )
|
||||
|
||||
import qualified Data.Text as T
|
||||
import Control.Exception.Safe (MonadMask)
|
||||
@@ -56,26 +55,26 @@ data GCOptions = GCOptions
|
||||
--[ Parsers ]--
|
||||
---------------
|
||||
|
||||
|
||||
|
||||
gcP :: Parser GCOptions
|
||||
gcP =
|
||||
GCOptions
|
||||
<$>
|
||||
<$>
|
||||
switch
|
||||
(short 'o' <> long "ghc-old" <> help "Remove GHC versions marked as 'old'")
|
||||
<*>
|
||||
<*>
|
||||
switch
|
||||
(short 'p' <> long "profiling-libs" <> help "Remove profiling libs of GHC versions")
|
||||
<*>
|
||||
<*>
|
||||
switch
|
||||
(short 's' <> long "share-dir" <> help "Remove GHC share directories (documentation)")
|
||||
<*>
|
||||
<*>
|
||||
switch
|
||||
(short 'h' <> long "hls-no-ghc" <> help "Remove HLS versions that don't have a corresponding installed GHC version")
|
||||
<*>
|
||||
<*>
|
||||
switch
|
||||
(short 'c' <> long "cache" <> help "GC the GHCup cache")
|
||||
<*>
|
||||
<*>
|
||||
switch
|
||||
(short 't' <> long "tmpdirs" <> help "Remove tmpdir leftovers")
|
||||
|
||||
@@ -98,7 +97,7 @@ gcFooter = [s|Discussion:
|
||||
---------------------------
|
||||
|
||||
|
||||
type GCEffects = '[ NotInstalled ]
|
||||
type GCEffects = '[ NotInstalled, UninstallFailed ]
|
||||
|
||||
|
||||
runGC :: MonadUnliftIO m
|
||||
@@ -129,7 +128,7 @@ gc :: ( Monad m
|
||||
-> (ReaderT LeanAppState m () -> m ())
|
||||
-> m ExitCode
|
||||
gc GCOptions{..} runAppState runLogger = runGC runAppState (do
|
||||
when gcOldGHC rmOldGHC
|
||||
when gcOldGHC (liftE rmOldGHC)
|
||||
lift $ when gcProfilingLibs rmProfilingLibs
|
||||
lift $ when gcShareDir rmShareDir
|
||||
liftE $ when gcHLSNoGHC rmHLSNoGHC
|
||||
@@ -139,5 +138,5 @@ gc GCOptions{..} runAppState runLogger = runGC runAppState (do
|
||||
VRight _ -> do
|
||||
pure ExitSuccess
|
||||
VLeft e -> do
|
||||
runLogger $ logError $ T.pack $ prettyShow e
|
||||
runLogger $ logError $ T.pack $ prettyHFError e
|
||||
pure $ ExitFailure 27
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE DuplicateRecordFields #-}
|
||||
{-# LANGUAGE ViewPatterns #-}
|
||||
{-# LANGUAGE TypeOperators #-}
|
||||
|
||||
module GHCup.OptParse.Install where
|
||||
|
||||
@@ -18,8 +19,10 @@ import GHCup.OptParse.Common
|
||||
import GHCup
|
||||
import GHCup.Errors
|
||||
import GHCup.Types
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Utils.String.QQ
|
||||
import GHCup.Utils.Dirs
|
||||
import GHCup.Prelude
|
||||
import GHCup.Prelude.Logger
|
||||
import GHCup.Prelude.String.QQ
|
||||
|
||||
import Codec.Archive
|
||||
#if !MIN_VERSION_base(4,13,0)
|
||||
@@ -30,13 +33,11 @@ import Control.Monad.Trans.Resource
|
||||
import Data.Either
|
||||
import Data.Functor
|
||||
import Data.Maybe
|
||||
import Data.Versions hiding ( str )
|
||||
import Haskus.Utils.Variant.Excepts
|
||||
import Options.Applicative hiding ( style )
|
||||
import Options.Applicative.Help.Pretty ( text )
|
||||
import Prelude hiding ( appendFile )
|
||||
import System.Exit
|
||||
import Text.PrettyPrint.HughesPJClass ( prettyShow )
|
||||
import URI.ByteString hiding ( uriParser )
|
||||
|
||||
import qualified Data.Text as T
|
||||
@@ -64,11 +65,11 @@ data InstallCommand = InstallGHC InstallOptions
|
||||
|
||||
data InstallOptions = InstallOptions
|
||||
{ instVer :: Maybe ToolVersion
|
||||
, instPlatform :: Maybe PlatformRequest
|
||||
, instBindist :: Maybe URI
|
||||
, instSet :: Bool
|
||||
, isolateDir :: Maybe FilePath
|
||||
, forceInstall :: Bool
|
||||
, addConfArgs :: [T.Text]
|
||||
}
|
||||
|
||||
|
||||
@@ -168,23 +169,13 @@ Examples:
|
||||
ghcup install ghc 8.10.2
|
||||
|
||||
# install GHC head fedora bindist
|
||||
ghcup install ghc -u https://gitlab.haskell.org/api/v4/projects/1/jobs/artifacts/master/raw/ghc-x86_64-fedora27-linux.tar.xz?job=validate-x86_64-linux-fedora27 head|]
|
||||
ghcup install ghc -u 'https://gitlab.haskell.org/ghc/ghc/-/jobs/artifacts/master/raw/ghc-x86_64-linux-fedora33-release.tar.xz?job=x86_64-linux-fedora33-release' head|]
|
||||
|
||||
|
||||
installOpts :: Maybe Tool -> Parser InstallOptions
|
||||
installOpts tool =
|
||||
(\p (u, v) b is f -> InstallOptions v p u b is f)
|
||||
<$> optional
|
||||
(option
|
||||
(eitherReader platformParser)
|
||||
( short 'p'
|
||||
<> long "platform"
|
||||
<> metavar "PLATFORM"
|
||||
<> help
|
||||
"Override for platform (triple matching ghc tarball names), e.g. x86_64-fedora27-linux"
|
||||
)
|
||||
)
|
||||
<*> ( ( (,)
|
||||
(\(u, v) b is f -> InstallOptions v u b is f)
|
||||
<$> ( ( (,)
|
||||
<$> optional
|
||||
(option
|
||||
(eitherReader uriParser)
|
||||
@@ -193,7 +184,7 @@ installOpts tool =
|
||||
<> completer (toolDlCompleter (fromMaybe GHC tool))
|
||||
)
|
||||
)
|
||||
<*> (Just <$> toolVersionArgument Nothing tool)
|
||||
<*> (Just <$> toolVersionTagArgument [] tool)
|
||||
)
|
||||
<|> pure (Nothing, Nothing)
|
||||
)
|
||||
@@ -210,13 +201,14 @@ installOpts tool =
|
||||
)
|
||||
)
|
||||
<*> switch
|
||||
(short 'f' <> long "force" <> help "Force install")
|
||||
(short 'f' <> long "force" <> help "Force install (THIS IS UNSAFE, only use it in Dockerfiles or CI)")
|
||||
<*> many (argument str (metavar "CONFIGURE_ARGS" <> help "Additional arguments to bindist configure, prefix with '-- ' (longopts)"))
|
||||
where
|
||||
setDefault = case tool of
|
||||
Nothing -> False
|
||||
Just GHC -> False
|
||||
Just _ -> True
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -249,7 +241,9 @@ type InstallEffects = '[ AlreadyInstalled
|
||||
, NotInstalled
|
||||
, BuildFailed
|
||||
, TagNotFound
|
||||
, DayNotFound
|
||||
, DigestError
|
||||
, ContentLengthError
|
||||
, GPGError
|
||||
, DownloadFailed
|
||||
, TarDirDoesNotExist
|
||||
@@ -257,116 +251,52 @@ type InstallEffects = '[ AlreadyInstalled
|
||||
, NoToolVersionSet
|
||||
, FileAlreadyExistsError
|
||||
, ProcessError
|
||||
|
||||
, (AlreadyInstalled, ())
|
||||
, (UnknownArchive, ())
|
||||
, (ArchiveResult, ())
|
||||
, (FileDoesNotExistError, ())
|
||||
, (CopyError, ())
|
||||
, (NotInstalled, ())
|
||||
, (DirNotEmpty, ())
|
||||
, (NoDownload, ())
|
||||
, (NotInstalled, ())
|
||||
, (BuildFailed, ())
|
||||
, (TagNotFound, ())
|
||||
, (DigestError, ())
|
||||
, (GPGError, ())
|
||||
, (DownloadFailed, ())
|
||||
, (TarDirDoesNotExist, ())
|
||||
, (NextVerNotFound, ())
|
||||
, (NoToolVersionSet, ())
|
||||
, (FileAlreadyExistsError, ())
|
||||
, (ProcessError, ())
|
||||
|
||||
, (AlreadyInstalled, NotInstalled)
|
||||
, (UnknownArchive, NotInstalled)
|
||||
, (ArchiveResult, NotInstalled)
|
||||
, (FileDoesNotExistError, NotInstalled)
|
||||
, (CopyError, NotInstalled)
|
||||
, (NotInstalled, NotInstalled)
|
||||
, (DirNotEmpty, NotInstalled)
|
||||
, (NoDownload, NotInstalled)
|
||||
, (NotInstalled, NotInstalled)
|
||||
, (BuildFailed, NotInstalled)
|
||||
, (TagNotFound, NotInstalled)
|
||||
, (DigestError, NotInstalled)
|
||||
, (GPGError, NotInstalled)
|
||||
, (DownloadFailed, NotInstalled)
|
||||
, (TarDirDoesNotExist, NotInstalled)
|
||||
, (NextVerNotFound, NotInstalled)
|
||||
, (NoToolVersionSet, NotInstalled)
|
||||
, (FileAlreadyExistsError, NotInstalled)
|
||||
, (ProcessError, NotInstalled)
|
||||
|
||||
, ((), NotInstalled)
|
||||
, UninstallFailed
|
||||
, MergeFileTreeError
|
||||
, InstallSetError
|
||||
]
|
||||
|
||||
|
||||
runInstTool :: AppState
|
||||
-> Maybe PlatformRequest
|
||||
-> Excepts InstallEffects (ResourceT (ReaderT AppState IO)) a
|
||||
-> IO (VEither InstallEffects a)
|
||||
runInstTool appstate' mInstPlatform =
|
||||
flip runReaderT (maybe appstate' (\x -> appstate'{ pfreq = x } :: AppState) mInstPlatform)
|
||||
runInstTool appstate' =
|
||||
flip runReaderT appstate'
|
||||
. runResourceT
|
||||
. runE
|
||||
@InstallEffects
|
||||
|
||||
|
||||
type InstallGHCEffects = '[ TagNotFound
|
||||
, NextVerNotFound
|
||||
, NoToolVersionSet
|
||||
type InstallGHCEffects = '[ AlreadyInstalled
|
||||
, ArchiveResult
|
||||
, BuildFailed
|
||||
, CopyError
|
||||
, DigestError
|
||||
, ContentLengthError
|
||||
, DirNotEmpty
|
||||
, AlreadyInstalled
|
||||
|
||||
, (AlreadyInstalled, NotInstalled)
|
||||
, (UnknownArchive, NotInstalled)
|
||||
, (ArchiveResult, NotInstalled)
|
||||
, (FileDoesNotExistError, NotInstalled)
|
||||
, (CopyError, NotInstalled)
|
||||
, (NotInstalled, NotInstalled)
|
||||
, (DirNotEmpty, NotInstalled)
|
||||
, (NoDownload, NotInstalled)
|
||||
, (BuildFailed, NotInstalled)
|
||||
, (TagNotFound, NotInstalled)
|
||||
, (DigestError, NotInstalled)
|
||||
, (GPGError, NotInstalled)
|
||||
, (DownloadFailed, NotInstalled)
|
||||
, (TarDirDoesNotExist, NotInstalled)
|
||||
, (NextVerNotFound, NotInstalled)
|
||||
, (NoToolVersionSet, NotInstalled)
|
||||
, (FileAlreadyExistsError, NotInstalled)
|
||||
, (ProcessError, NotInstalled)
|
||||
|
||||
, (AlreadyInstalled, ())
|
||||
, (UnknownArchive, ())
|
||||
, (ArchiveResult, ())
|
||||
, (FileDoesNotExistError, ())
|
||||
, (CopyError, ())
|
||||
, (NotInstalled, ())
|
||||
, (DirNotEmpty, ())
|
||||
, (NoDownload, ())
|
||||
, (BuildFailed, ())
|
||||
, (TagNotFound, ())
|
||||
, (DigestError, ())
|
||||
, (GPGError, ())
|
||||
, (DownloadFailed, ())
|
||||
, (TarDirDoesNotExist, ())
|
||||
, (NextVerNotFound, ())
|
||||
, (NoToolVersionSet, ())
|
||||
, (FileAlreadyExistsError, ())
|
||||
, (ProcessError, ())
|
||||
|
||||
, ((), NotInstalled)
|
||||
, DownloadFailed
|
||||
, FileAlreadyExistsError
|
||||
, FileDoesNotExistError
|
||||
, GPGError
|
||||
, MergeFileTreeError
|
||||
, NextVerNotFound
|
||||
, NoDownload
|
||||
, NoToolVersionSet
|
||||
, NotInstalled
|
||||
, ProcessError
|
||||
, TagNotFound
|
||||
, DayNotFound
|
||||
, TarDirDoesNotExist
|
||||
, UninstallFailed
|
||||
, UnknownArchive
|
||||
, InstallSetError
|
||||
]
|
||||
|
||||
runInstGHC :: AppState
|
||||
-> Maybe PlatformRequest
|
||||
-> Excepts InstallGHCEffects (ResourceT (ReaderT AppState IO)) a
|
||||
-> IO (VEither InstallGHCEffects a)
|
||||
runInstGHC appstate' mInstPlatform =
|
||||
flip runReaderT (maybe appstate' (\x -> appstate'{ pfreq = x } :: AppState) mInstPlatform)
|
||||
runInstGHC appstate' =
|
||||
flip runReaderT appstate'
|
||||
. runResourceT
|
||||
. runE
|
||||
@InstallGHCEffects
|
||||
@@ -391,25 +321,27 @@ install installCommand settings getAppState' runLogger = case installCommand of
|
||||
installGHC InstallOptions{..} = do
|
||||
s'@AppState{ dirs = Dirs{ .. } } <- liftIO getAppState'
|
||||
(case instBindist of
|
||||
Nothing -> runInstGHC s' instPlatform $ do
|
||||
Nothing -> runInstGHC s' $ do
|
||||
(v, vi) <- liftE $ fromVersion instVer GHC
|
||||
void $ liftE $ sequenceE (installGHCBin
|
||||
liftE $ runBothE' (installGHCBin
|
||||
(_tvVersion v)
|
||||
isolateDir
|
||||
(maybe GHCupInternal IsolateDir isolateDir)
|
||||
forceInstall
|
||||
addConfArgs
|
||||
)
|
||||
$ when instSet $ when (isNothing isolateDir) $ void $ setGHC v SetGHCOnly Nothing
|
||||
$ when instSet $ when (isNothing isolateDir) $ liftE $ void $ setGHC v SetGHCOnly Nothing
|
||||
pure vi
|
||||
Just uri -> do
|
||||
runInstGHC s'{ settings = settings {noVerify = True}} instPlatform $ do
|
||||
runInstGHC s'{ settings = settings {noVerify = True}} $ do
|
||||
(v, vi) <- liftE $ fromVersion instVer GHC
|
||||
void $ liftE $ sequenceE (installGHCBindist
|
||||
(DownloadInfo uri (Just $ RegexDir "ghc-.*") "")
|
||||
liftE $ runBothE' (installGHCBindist
|
||||
(DownloadInfo uri (Just $ RegexDir "ghc-.*") "" Nothing Nothing)
|
||||
(_tvVersion v)
|
||||
isolateDir
|
||||
(maybe GHCupInternal IsolateDir isolateDir)
|
||||
forceInstall
|
||||
addConfArgs
|
||||
)
|
||||
$ when instSet $ when (isNothing isolateDir) $ void $ setGHC v SetGHCOnly Nothing
|
||||
$ when instSet $ when (isNothing isolateDir) $ liftE $ void $ setGHC v SetGHCOnly Nothing
|
||||
pure vi
|
||||
)
|
||||
>>= \case
|
||||
@@ -419,43 +351,41 @@ install installCommand settings getAppState' runLogger = case installCommand of
|
||||
runLogger $ logInfo msg
|
||||
pure ExitSuccess
|
||||
|
||||
VLeft (V (AlreadyInstalled _ v, ())) -> do
|
||||
runLogger $ logWarn $
|
||||
"GHC ver " <> prettyVer v <> " already installed; if you really want to reinstall it, you may want to run 'ghcup install ghc --force " <> prettyVer v <> "'"
|
||||
VLeft e@(V (AlreadyInstalled _ _)) -> do
|
||||
runLogger $ logWarn $ T.pack $ prettyHFError e
|
||||
pure ExitSuccess
|
||||
VLeft (V (AlreadyInstalled _ v)) -> do
|
||||
runLogger $ logWarn $
|
||||
"GHC ver " <> prettyVer v <> " already installed; if you really want to reinstall it, you may want to run 'ghcup install ghc --force " <> prettyVer v <> "'"
|
||||
VLeft e@(V (AlreadyInstalled _ _)) -> do
|
||||
runLogger $ logWarn $ T.pack $ prettyHFError e
|
||||
pure ExitSuccess
|
||||
|
||||
VLeft (V (DirNotEmpty fp)) -> do
|
||||
runLogger $ logWarn $
|
||||
"Install directory " <> T.pack fp <> " is not empty. Use 'ghcup install ghc --isolate " <> T.pack fp <> " --force ..." <> "' to install regardless."
|
||||
runLogger $ logError $
|
||||
"Install directory " <> T.pack fp <> " is not empty."
|
||||
pure $ ExitFailure 3
|
||||
VLeft (V (DirNotEmpty fp, ())) -> do
|
||||
runLogger $ logWarn $
|
||||
"Install directory " <> T.pack fp <> " is not empty. Use 'ghcup install ghc --isolate " <> T.pack fp <> " --force ..." <> "' to install regardless."
|
||||
VLeft (V (DirNotEmpty fp)) -> do
|
||||
runLogger $ logError $
|
||||
"Install directory " <> T.pack fp <> " is not empty."
|
||||
pure $ ExitFailure 3
|
||||
|
||||
VLeft err@(V (BuildFailed tmpdir _)) -> do
|
||||
case keepDirs settings of
|
||||
Never -> runLogger (logError $ T.pack $ prettyShow err)
|
||||
_ -> runLogger (logError $ T.pack (prettyShow err) <> "\n" <>
|
||||
"Check the logs at " <> T.pack logsDir <> " and the build directory " <> T.pack tmpdir <> " for more clues." <> "\n" <>
|
||||
Never -> runLogger (logError $ T.pack $ prettyHFError err)
|
||||
_ -> runLogger (logError $ T.pack (prettyHFError err) <> "\n" <>
|
||||
"Check the logs at " <> T.pack (fromGHCupPath logsDir) <> " and the build directory " <> T.pack tmpdir <> " for more clues." <> "\n" <>
|
||||
"Make sure to clean up " <> T.pack tmpdir <> " afterwards.")
|
||||
pure $ ExitFailure 3
|
||||
VLeft err@(V (BuildFailed tmpdir _, ())) -> do
|
||||
VLeft err@(V (BuildFailed tmpdir _)) -> do
|
||||
case keepDirs settings of
|
||||
Never -> runLogger (logError $ T.pack $ prettyShow err)
|
||||
_ -> runLogger (logError $ T.pack (prettyShow err) <> "\n" <>
|
||||
"Check the logs at " <> T.pack logsDir <> " and the build directory " <> T.pack tmpdir <> " for more clues." <> "\n" <>
|
||||
Never -> runLogger (logError $ T.pack $ prettyHFError err)
|
||||
_ -> runLogger (logError $ T.pack (prettyHFError err) <> "\n" <>
|
||||
"Check the logs at " <> T.pack (fromGHCupPath logsDir) <> " and the build directory " <> T.pack tmpdir <> " for more clues." <> "\n" <>
|
||||
"Make sure to clean up " <> T.pack tmpdir <> " afterwards.")
|
||||
pure $ ExitFailure 3
|
||||
|
||||
VLeft e -> do
|
||||
runLogger $ do
|
||||
logError $ T.pack $ prettyShow e
|
||||
logError $ "Also check the logs in " <> T.pack logsDir
|
||||
logError $ T.pack $ prettyHFError e
|
||||
logError $ "Also check the logs in " <> T.pack (fromGHCupPath logsDir)
|
||||
pure $ ExitFailure 3
|
||||
|
||||
|
||||
@@ -463,23 +393,23 @@ install installCommand settings getAppState' runLogger = case installCommand of
|
||||
installCabal InstallOptions{..} = do
|
||||
s'@AppState{ dirs = Dirs{ .. } } <- liftIO getAppState'
|
||||
(case instBindist of
|
||||
Nothing -> runInstTool s' instPlatform $ do
|
||||
Nothing -> runInstTool s' $ do
|
||||
(_tvVersion -> v, vi) <- liftE $ fromVersion instVer Cabal
|
||||
void $ liftE $ sequenceE (installCabalBin
|
||||
liftE $ runBothE' (installCabalBin
|
||||
v
|
||||
isolateDir
|
||||
(maybe GHCupInternal IsolateDir isolateDir)
|
||||
forceInstall
|
||||
) $ when instSet $ when (isNothing isolateDir) $ void $ setCabal v
|
||||
) $ when instSet $ when (isNothing isolateDir) $ liftE $ setCabal v
|
||||
pure vi
|
||||
Just uri -> do
|
||||
runInstTool s'{ settings = settings { noVerify = True}} instPlatform $ do
|
||||
runInstTool s'{ settings = settings { noVerify = True}} $ do
|
||||
(_tvVersion -> v, vi) <- liftE $ fromVersion instVer Cabal
|
||||
void $ liftE $ sequenceE (installCabalBindist
|
||||
(DownloadInfo uri Nothing "")
|
||||
liftE $ runBothE' (installCabalBindist
|
||||
(DownloadInfo uri Nothing "" Nothing Nothing)
|
||||
v
|
||||
isolateDir
|
||||
(maybe GHCupInternal IsolateDir isolateDir)
|
||||
forceInstall
|
||||
) $ when instSet $ when (isNothing isolateDir) $ void $ setCabal v
|
||||
) $ when instSet $ when (isNothing isolateDir) $ liftE $ setCabal v
|
||||
pure vi
|
||||
)
|
||||
>>= \case
|
||||
@@ -488,50 +418,48 @@ install installCommand settings getAppState' runLogger = case installCommand of
|
||||
forM_ (_viPostInstall =<< vi) $ \msg ->
|
||||
runLogger $ logInfo msg
|
||||
pure ExitSuccess
|
||||
VLeft (V (AlreadyInstalled _ v)) -> do
|
||||
runLogger $ logWarn $
|
||||
"Cabal ver " <> prettyVer v <> " already installed; if you really want to reinstall it, you may want to run 'ghcup install cabal --force " <> prettyVer v <> "'"
|
||||
VLeft e@(V (AlreadyInstalled _ _)) -> do
|
||||
runLogger $ logWarn $ T.pack $ prettyHFError e
|
||||
pure ExitSuccess
|
||||
VLeft (V (FileAlreadyExistsError fp)) -> do
|
||||
runLogger $ logWarn $
|
||||
"File " <> T.pack fp <> " already exists. Use 'ghcup install cabal --isolate " <> T.pack fp <> " --force ..." <> "' if you want to overwrite."
|
||||
pure $ ExitFailure 3
|
||||
VLeft (V (AlreadyInstalled _ v, ())) -> do
|
||||
runLogger $ logWarn $
|
||||
"Cabal ver " <> prettyVer v <> " already installed; if you really want to reinstall it, you may want to run 'ghcup install cabal --force " <> prettyVer v <> "'"
|
||||
VLeft e@(V (AlreadyInstalled _ _)) -> do
|
||||
runLogger $ logWarn $ T.pack $ prettyHFError e
|
||||
pure ExitSuccess
|
||||
VLeft (V (FileAlreadyExistsError fp, ())) -> do
|
||||
VLeft (V (FileAlreadyExistsError fp)) -> do
|
||||
runLogger $ logWarn $
|
||||
"File " <> T.pack fp <> " already exists. Use 'ghcup install cabal --isolate " <> T.pack fp <> " --force ..." <> "' if you want to overwrite."
|
||||
pure $ ExitFailure 3
|
||||
VLeft e -> do
|
||||
runLogger $ do
|
||||
logError $ T.pack $ prettyShow e
|
||||
logError $ "Also check the logs in " <> T.pack logsDir
|
||||
logError $ T.pack $ prettyHFError e
|
||||
logError $ "Also check the logs in " <> T.pack (fromGHCupPath logsDir)
|
||||
pure $ ExitFailure 4
|
||||
|
||||
installHLS :: InstallOptions -> IO ExitCode
|
||||
installHLS InstallOptions{..} = do
|
||||
s'@AppState{ dirs = Dirs{ .. } } <- liftIO getAppState'
|
||||
(case instBindist of
|
||||
Nothing -> runInstTool s' instPlatform $ do
|
||||
Nothing -> runInstTool s' $ do
|
||||
(_tvVersion -> v, vi) <- liftE $ fromVersion instVer HLS
|
||||
void $ liftE $ sequenceE (installHLSBin
|
||||
liftE $ runBothE' (installHLSBin
|
||||
v
|
||||
isolateDir
|
||||
(maybe GHCupInternal IsolateDir isolateDir)
|
||||
forceInstall
|
||||
) $ when instSet $ when (isNothing isolateDir) $ void $ setHLS v SetHLSOnly Nothing
|
||||
) $ when instSet $ when (isNothing isolateDir) $ liftE $ setHLS v SetHLSOnly Nothing
|
||||
pure vi
|
||||
Just uri -> do
|
||||
runInstTool s'{ settings = settings { noVerify = True}} instPlatform $ do
|
||||
runInstTool s'{ settings = settings { noVerify = True}} $ do
|
||||
(_tvVersion -> v, vi) <- liftE $ fromVersion instVer HLS
|
||||
-- TODO: support legacy
|
||||
void $ liftE $ sequenceE (installHLSBindist
|
||||
(DownloadInfo uri (Just $ RegexDir "haskell-language-server-*") "")
|
||||
liftE $ runBothE' (installHLSBindist
|
||||
(DownloadInfo uri (if isWindows then Nothing else Just (RegexDir "haskell-language-server-*")) "" Nothing Nothing)
|
||||
v
|
||||
isolateDir
|
||||
(maybe GHCupInternal IsolateDir isolateDir)
|
||||
forceInstall
|
||||
) $ when instSet $ when (isNothing isolateDir) $ void $ setHLS v SetHLSOnly Nothing
|
||||
) $ when instSet $ when (isNothing isolateDir) $ liftE $ setHLS v SetHLSOnly Nothing
|
||||
pure vi
|
||||
)
|
||||
>>= \case
|
||||
@@ -540,57 +468,47 @@ install installCommand settings getAppState' runLogger = case installCommand of
|
||||
forM_ (_viPostInstall =<< vi) $ \msg ->
|
||||
runLogger $ logInfo msg
|
||||
pure ExitSuccess
|
||||
VLeft (V (AlreadyInstalled _ v)) -> do
|
||||
runLogger $ logWarn $
|
||||
"HLS ver "
|
||||
<> prettyVer v
|
||||
<> " already installed; if you really want to reinstall it, you may want to run 'ghcup install hls --force "
|
||||
<> prettyVer v
|
||||
<> "'"
|
||||
VLeft e@(V (AlreadyInstalled _ _)) -> do
|
||||
runLogger $ logWarn $ T.pack $ prettyHFError e
|
||||
pure ExitSuccess
|
||||
VLeft (V (FileAlreadyExistsError fp)) -> do
|
||||
runLogger $ logWarn $
|
||||
"File " <> T.pack fp <> " already exists. Use 'ghcup install hls --isolate " <> T.pack fp <> " --force ..." <> "' if you want to overwrite."
|
||||
pure $ ExitFailure 3
|
||||
VLeft (V (AlreadyInstalled _ v, ())) -> do
|
||||
runLogger $ logWarn $
|
||||
"HLS ver "
|
||||
<> prettyVer v
|
||||
<> " already installed; if you really want to reinstall it, you may want to run 'ghcup install hls --force "
|
||||
<> prettyVer v
|
||||
<> "'"
|
||||
VLeft e@(V (AlreadyInstalled _ _)) -> do
|
||||
runLogger $ logWarn $ T.pack $ prettyHFError e
|
||||
pure ExitSuccess
|
||||
VLeft (V (FileAlreadyExistsError fp, ())) -> do
|
||||
VLeft (V (FileAlreadyExistsError fp)) -> do
|
||||
runLogger $ logWarn $
|
||||
"File " <> T.pack fp <> " already exists. Use 'ghcup install hls --isolate " <> T.pack fp <> " --force ..." <> "' if you want to overwrite."
|
||||
pure $ ExitFailure 3
|
||||
VLeft e -> do
|
||||
runLogger $ do
|
||||
logError $ T.pack $ prettyShow e
|
||||
logError $ "Also check the logs in " <> T.pack logsDir
|
||||
logError $ T.pack $ prettyHFError e
|
||||
logError $ "Also check the logs in " <> T.pack (fromGHCupPath logsDir)
|
||||
pure $ ExitFailure 4
|
||||
|
||||
installStack :: InstallOptions -> IO ExitCode
|
||||
installStack InstallOptions{..} = do
|
||||
s'@AppState{ dirs = Dirs{ .. } } <- liftIO getAppState'
|
||||
(case instBindist of
|
||||
Nothing -> runInstTool s' instPlatform $ do
|
||||
Nothing -> runInstTool s' $ do
|
||||
(_tvVersion -> v, vi) <- liftE $ fromVersion instVer Stack
|
||||
void $ liftE $ sequenceE (installStackBin
|
||||
liftE $ runBothE' (installStackBin
|
||||
v
|
||||
isolateDir
|
||||
(maybe GHCupInternal IsolateDir isolateDir)
|
||||
forceInstall
|
||||
) $ when instSet $ when (isNothing isolateDir) $ void $ setStack v
|
||||
) $ when instSet $ when (isNothing isolateDir) $ liftE $ setStack v
|
||||
pure vi
|
||||
Just uri -> do
|
||||
runInstTool s'{ settings = settings { noVerify = True}} instPlatform $ do
|
||||
runInstTool s'{ settings = settings { noVerify = True}} $ do
|
||||
(_tvVersion -> v, vi) <- liftE $ fromVersion instVer Stack
|
||||
void $ liftE $ sequenceE (installStackBindist
|
||||
(DownloadInfo uri Nothing "")
|
||||
liftE $ runBothE' (installStackBindist
|
||||
(DownloadInfo uri Nothing "" Nothing Nothing)
|
||||
v
|
||||
isolateDir
|
||||
(maybe GHCupInternal IsolateDir isolateDir)
|
||||
forceInstall
|
||||
) $ when instSet $ when (isNothing isolateDir) $ void $ setStack v
|
||||
) $ when instSet $ when (isNothing isolateDir) $ liftE $ setStack v
|
||||
pure vi
|
||||
)
|
||||
>>= \case
|
||||
@@ -599,25 +517,22 @@ install installCommand settings getAppState' runLogger = case installCommand of
|
||||
forM_ (_viPostInstall =<< vi) $ \msg ->
|
||||
runLogger $ logInfo msg
|
||||
pure ExitSuccess
|
||||
VLeft (V (AlreadyInstalled _ v)) -> do
|
||||
runLogger $ logWarn $
|
||||
"Stack ver " <> prettyVer v <> " already installed; if you really want to reinstall it, you may want to run 'ghcup install stack --force " <> prettyVer v <> "'"
|
||||
VLeft e@(V (AlreadyInstalled _ _)) -> do
|
||||
runLogger $ logWarn $ T.pack $ prettyHFError e
|
||||
pure ExitSuccess
|
||||
VLeft (V (FileAlreadyExistsError fp)) -> do
|
||||
runLogger $ logWarn $
|
||||
"File " <> T.pack fp <> " already exists. Use 'ghcup install stack --isolate " <> T.pack fp <> " --force ..." <> "' if you want to overwrite."
|
||||
pure $ ExitFailure 3
|
||||
VLeft (V (AlreadyInstalled _ v, ())) -> do
|
||||
runLogger $ logWarn $
|
||||
"Stack ver " <> prettyVer v <> " already installed; if you really want to reinstall it, you may want to run 'ghcup install stack --force " <> prettyVer v <> "'"
|
||||
VLeft e@(V (AlreadyInstalled _ _)) -> do
|
||||
runLogger $ logWarn $ T.pack $ prettyHFError e
|
||||
pure ExitSuccess
|
||||
VLeft (V (FileAlreadyExistsError fp, ())) -> do
|
||||
VLeft (V (FileAlreadyExistsError fp)) -> do
|
||||
runLogger $ logWarn $
|
||||
"File " <> T.pack fp <> " already exists. Use 'ghcup install stack --isolate " <> T.pack fp <> " --force ..." <> "' if you want to overwrite."
|
||||
pure $ ExitFailure 3
|
||||
VLeft e -> do
|
||||
runLogger $ do
|
||||
logError $ T.pack $ prettyShow e
|
||||
logError $ "Also check the logs in " <> T.pack logsDir
|
||||
logError $ T.pack $ prettyHFError e
|
||||
logError $ "Also check the logs in " <> T.pack (fromGHCupPath logsDir)
|
||||
pure $ ExitFailure 4
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
{-# LANGUAGE DataKinds #-}
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
{-# LANGUAGE DuplicateRecordFields #-}
|
||||
{-# LANGUAGE RankNTypes #-}
|
||||
|
||||
@@ -11,9 +12,10 @@ module GHCup.OptParse.List where
|
||||
|
||||
|
||||
import GHCup
|
||||
import GHCup.Utils.Prelude
|
||||
import GHCup.Prelude
|
||||
import GHCup.Types
|
||||
import GHCup.OptParse.Common
|
||||
import GHCup.Prelude.String.QQ
|
||||
|
||||
#if !MIN_VERSION_base(4,13,0)
|
||||
import Control.Monad.Fail ( MonadFail )
|
||||
@@ -24,6 +26,7 @@ import Data.Char
|
||||
import Data.List ( intercalate, sort )
|
||||
import Data.Functor
|
||||
import Data.Maybe
|
||||
import Data.Time.Calendar ( Day )
|
||||
import Data.Versions hiding ( str )
|
||||
import Data.Void
|
||||
import Options.Applicative hiding ( style )
|
||||
@@ -50,6 +53,10 @@ import qualified Text.Megaparsec.Char as MPC
|
||||
data ListOptions = ListOptions
|
||||
{ loTool :: Maybe Tool
|
||||
, lCriteria :: Maybe ListCriteria
|
||||
, lFrom :: Maybe Day
|
||||
, lTo :: Maybe Day
|
||||
, lHideOld :: Bool
|
||||
, lShowNightly :: Bool
|
||||
, lRawFormat :: Bool
|
||||
}
|
||||
|
||||
@@ -60,7 +67,6 @@ data ListOptions = ListOptions
|
||||
--[ Parsers ]--
|
||||
---------------
|
||||
|
||||
|
||||
listOpts :: Parser ListOptions
|
||||
listOpts =
|
||||
ListOptions
|
||||
@@ -69,7 +75,7 @@ listOpts =
|
||||
(eitherReader toolParser)
|
||||
(short 't' <> long "tool" <> metavar "<ghc|cabal|hls|stack>" <> help
|
||||
"Tool to list versions for. Default is all"
|
||||
<> completer (toolCompleter)
|
||||
<> completer toolCompleter
|
||||
)
|
||||
)
|
||||
<*> optional
|
||||
@@ -78,15 +84,53 @@ listOpts =
|
||||
( short 'c'
|
||||
<> long "show-criteria"
|
||||
<> metavar "<installed|set|available>"
|
||||
<> help "Show only installed/set/available tool versions"
|
||||
<> completer (listCompleter ["installed", "set", "available"])
|
||||
<> help "Apply filtering criteria, prefix with + or -"
|
||||
<> completer (listCompleter
|
||||
[ "+installed", "+set", "+available", "-installed", "-set", "-available"])
|
||||
)
|
||||
)
|
||||
<*> optional
|
||||
(option
|
||||
(eitherReader dayParser)
|
||||
(short 's' <> long "since" <> metavar "YYYY-MM-DD" <> help
|
||||
"List only tools with release date starting at YYYY-MM-DD or later"
|
||||
<> completer toolCompleter
|
||||
)
|
||||
)
|
||||
<*> optional
|
||||
(option
|
||||
(eitherReader dayParser)
|
||||
(short 'u' <> long "until" <> metavar "YYYY-MM-DD" <> help
|
||||
"List only tools with release date earlier than YYYY-MM-DD"
|
||||
<> completer toolCompleter
|
||||
)
|
||||
)
|
||||
<*> switch
|
||||
(short 'o' <> long "hide-old" <> help "Hide 'old' GHC versions (installed ones are always shown)"
|
||||
)
|
||||
<*> switch
|
||||
(short 'n' <> long "show-nightly" <> help "Show nightlies (installed ones are always shown)"
|
||||
)
|
||||
<*> switch
|
||||
(short 'r' <> long "raw-format" <> help "More machine-parsable format"
|
||||
)
|
||||
|
||||
|
||||
--------------
|
||||
--[ Footer ]--
|
||||
--------------
|
||||
|
||||
|
||||
listToolFooter :: String
|
||||
listToolFooter = [s|Discussion:
|
||||
Lists tool versions with optional criteria.
|
||||
Nightlies are by default hidden.
|
||||
|
||||
Examples:
|
||||
# query nightlies in a specific range
|
||||
ghcup list --show-nightly --since 2022-12-07 --until 2022-12-31
|
||||
# show all installed GHC versions
|
||||
ghcup list -t ghc -c installed|]
|
||||
|
||||
|
||||
-----------------
|
||||
@@ -105,8 +149,11 @@ printListResult no_color raw lr = do
|
||||
printTag Recommended = color Green "recommended"
|
||||
printTag Latest = color Yellow "latest"
|
||||
printTag Prerelease = color Red "prerelease"
|
||||
printTag Nightly = color Red "nightly"
|
||||
printTag (Base pvp'') = "base-" ++ T.unpack (prettyPVP pvp'')
|
||||
printTag (UnknownTag t ) = t
|
||||
printTag LatestPrerelease = color Red "latest-prerelease"
|
||||
printTag LatestNightly = color Red "latest-nightly"
|
||||
printTag Old = ""
|
||||
|
||||
let
|
||||
@@ -135,6 +182,9 @@ printListResult no_color raw lr = do
|
||||
)
|
||||
++ (if fromSrc then [color Blue "compiled"] else mempty)
|
||||
++ (if lStray then [color Yellow "stray"] else mempty)
|
||||
++ (case lReleaseDay of
|
||||
Nothing -> mempty
|
||||
Just d -> [color Blue (show d)])
|
||||
++ (if lNoBindist
|
||||
then [color Red "no-bindist"]
|
||||
else mempty
|
||||
@@ -259,7 +309,7 @@ list :: ( Monad m
|
||||
-> m ExitCode
|
||||
list ListOptions{..} no_color runAppState =
|
||||
runAppState (do
|
||||
l <- listVersions loTool lCriteria
|
||||
l <- listVersions loTool (maybeToList lCriteria) lHideOld lShowNightly (lFrom, lTo)
|
||||
liftIO $ printListResult no_color lRawFormat l
|
||||
pure ExitSuccess
|
||||
)
|
||||
|
||||
@@ -14,7 +14,7 @@ module GHCup.OptParse.Nuke where
|
||||
import GHCup
|
||||
import GHCup.Errors
|
||||
import GHCup.Types
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Prelude.Logger
|
||||
|
||||
#if !MIN_VERSION_base(4,13,0)
|
||||
import Control.Monad.Fail ( MonadFail )
|
||||
@@ -26,7 +26,6 @@ import Haskus.Utils.Variant.Excepts
|
||||
import Options.Applicative hiding ( style )
|
||||
import Prelude hiding ( appendFile )
|
||||
import System.Exit
|
||||
import Text.PrettyPrint.HughesPJClass ( prettyShow )
|
||||
|
||||
import qualified Data.Text as T
|
||||
import Control.Exception.Safe (MonadMask)
|
||||
@@ -42,7 +41,7 @@ import Control.Concurrent (threadDelay)
|
||||
---------------------------
|
||||
|
||||
|
||||
type NukeEffects = '[ NotInstalled ]
|
||||
type NukeEffects = '[ NotInstalled, UninstallFailed ]
|
||||
|
||||
|
||||
runNuke :: AppState
|
||||
@@ -77,8 +76,8 @@ nuke appState runLogger = do
|
||||
|
||||
lift $ logInfo "Initiating Nuclear Sequence 🚀🚀🚀"
|
||||
lift $ logInfo "Nuking in 3...2...1"
|
||||
|
||||
lInstalled <- lift $ listVersions Nothing (Just ListInstalled)
|
||||
|
||||
lInstalled <- lift $ listVersions Nothing [ListInstalled True] False True (Nothing, Nothing)
|
||||
|
||||
forM_ lInstalled (liftE . rmTool)
|
||||
|
||||
@@ -95,5 +94,5 @@ nuke appState runLogger = do
|
||||
pure ExitSuccess
|
||||
|
||||
VLeft e -> do
|
||||
runLogger $ logError $ T.pack $ prettyShow e
|
||||
runLogger $ logError $ T.pack $ prettyHFError e
|
||||
pure $ ExitFailure 15
|
||||
|
||||
@@ -14,9 +14,10 @@ module GHCup.OptParse.Prefetch where
|
||||
import GHCup
|
||||
import GHCup.Errors
|
||||
import GHCup.Types
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Prelude.File
|
||||
import GHCup.Prelude.Logger
|
||||
import GHCup.Prelude.String.QQ
|
||||
import GHCup.OptParse.Common
|
||||
import GHCup.Utils.String.QQ
|
||||
|
||||
#if !MIN_VERSION_base(4,13,0)
|
||||
import Control.Monad.Fail ( MonadFail )
|
||||
@@ -29,11 +30,9 @@ import Haskus.Utils.Variant.Excepts
|
||||
import Options.Applicative hiding ( style )
|
||||
import Prelude hiding ( appendFile )
|
||||
import System.Exit
|
||||
import Text.PrettyPrint.HughesPJClass ( prettyShow )
|
||||
|
||||
import qualified Data.Text as T
|
||||
import Control.Exception.Safe (MonadMask)
|
||||
import GHCup.Utils.Prelude
|
||||
import GHCup.Download (getDownloadsF)
|
||||
|
||||
|
||||
@@ -74,44 +73,44 @@ data PrefetchGHCOptions = PrefetchGHCOptions {
|
||||
--[ Parsers ]--
|
||||
---------------
|
||||
|
||||
|
||||
|
||||
prefetchP :: Parser PrefetchCommand
|
||||
prefetchP = subparser
|
||||
( command
|
||||
"ghc"
|
||||
(info
|
||||
(info
|
||||
(PrefetchGHC
|
||||
<$> (PrefetchGHCOptions
|
||||
<$> ( switch (short 's' <> long "source" <> help "Download source tarball instead of bindist") <**> helper )
|
||||
<*> optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)" <> completer (bashCompleter "directory"))))
|
||||
<*> optional (toolVersionArgument Nothing (Just GHC)) )
|
||||
<*> optional (toolVersionTagArgument [] (Just GHC)) )
|
||||
( progDesc "Download GHC assets for installation")
|
||||
)
|
||||
<>
|
||||
command
|
||||
"cabal"
|
||||
(info
|
||||
(info
|
||||
(PrefetchCabal
|
||||
<$> fmap PrefetchOptions (optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)" <> completer (bashCompleter "directory"))))
|
||||
<*> ( optional (toolVersionArgument Nothing (Just Cabal)) <**> helper ))
|
||||
<*> ( optional (toolVersionTagArgument [] (Just Cabal)) <**> helper ))
|
||||
( progDesc "Download cabal assets for installation")
|
||||
)
|
||||
<>
|
||||
command
|
||||
"hls"
|
||||
(info
|
||||
(info
|
||||
(PrefetchHLS
|
||||
<$> fmap PrefetchOptions (optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)" <> completer (bashCompleter "directory"))))
|
||||
<*> ( optional (toolVersionArgument Nothing (Just HLS)) <**> helper ))
|
||||
<*> ( optional (toolVersionTagArgument [] (Just HLS)) <**> helper ))
|
||||
( progDesc "Download HLS assets for installation")
|
||||
)
|
||||
<>
|
||||
command
|
||||
"stack"
|
||||
(info
|
||||
(info
|
||||
(PrefetchStack
|
||||
<$> fmap PrefetchOptions (optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)" <> completer (bashCompleter "directory"))))
|
||||
<*> ( optional (toolVersionArgument Nothing (Just Stack)) <**> helper ))
|
||||
<*> ( optional (toolVersionTagArgument [] (Just Stack)) <**> helper ))
|
||||
( progDesc "Download stack assets for installation")
|
||||
)
|
||||
<>
|
||||
@@ -149,10 +148,12 @@ Examples:
|
||||
|
||||
|
||||
type PrefetchEffects = '[ TagNotFound
|
||||
, DayNotFound
|
||||
, NextVerNotFound
|
||||
, NoToolVersionSet
|
||||
, NoDownload
|
||||
, DigestError
|
||||
, ContentLengthError
|
||||
, GPGError
|
||||
, DownloadFailed
|
||||
, JSONError
|
||||
@@ -215,5 +216,5 @@ prefetch prefetchCommand runAppState runLogger =
|
||||
VRight _ -> do
|
||||
pure ExitSuccess
|
||||
VLeft e -> do
|
||||
runLogger $ logError $ T.pack $ prettyShow e
|
||||
runLogger $ logError $ T.pack $ prettyHFError e
|
||||
pure $ ExitFailure 15
|
||||
|
||||
@@ -18,9 +18,9 @@ import GHCup.Errors
|
||||
import GHCup.Types
|
||||
import GHCup.Types.Optics
|
||||
import GHCup.Utils
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Prelude.Logger
|
||||
import GHCup.Prelude.String.QQ
|
||||
import GHCup.OptParse.Common
|
||||
import GHCup.Utils.String.QQ
|
||||
|
||||
#if !MIN_VERSION_base(4,13,0)
|
||||
import Control.Monad.Fail ( MonadFail )
|
||||
@@ -34,7 +34,6 @@ import Haskus.Utils.Variant.Excepts
|
||||
import Options.Applicative hiding ( style )
|
||||
import Prelude hiding ( appendFile )
|
||||
import System.Exit
|
||||
import Text.PrettyPrint.HughesPJClass ( prettyShow )
|
||||
|
||||
import qualified Data.Text as T
|
||||
import Control.Exception.Safe (MonadMask)
|
||||
@@ -71,7 +70,7 @@ data RmOptions = RmOptions
|
||||
--[ Parsers ]--
|
||||
---------------
|
||||
|
||||
|
||||
|
||||
rmParser :: Parser (Either RmCommand RmOptions)
|
||||
rmParser =
|
||||
(Left <$> subparser
|
||||
@@ -81,19 +80,19 @@ rmParser =
|
||||
<> command
|
||||
"cabal"
|
||||
( RmCabal
|
||||
<$> info (versionParser' (Just ListInstalled) (Just Cabal) <**> helper)
|
||||
<$> info (versionParser' [ListInstalled True] (Just Cabal) <**> helper)
|
||||
(progDesc "Remove Cabal version")
|
||||
)
|
||||
<> command
|
||||
"hls"
|
||||
( RmHLS
|
||||
<$> info (versionParser' (Just ListInstalled) (Just HLS) <**> helper)
|
||||
<$> info (versionParser' [ListInstalled True] (Just HLS) <**> helper)
|
||||
(progDesc "Remove haskell-language-server version")
|
||||
)
|
||||
<> command
|
||||
"stack"
|
||||
( RmStack
|
||||
<$> info (versionParser' (Just ListInstalled) (Just Stack) <**> helper)
|
||||
<$> info (versionParser' [ListInstalled True] (Just Stack) <**> helper)
|
||||
(progDesc "Remove stack version")
|
||||
)
|
||||
)
|
||||
@@ -103,7 +102,7 @@ rmParser =
|
||||
|
||||
|
||||
rmOpts :: Maybe Tool -> Parser RmOptions
|
||||
rmOpts tool = RmOptions <$> versionArgument (Just ListInstalled) tool
|
||||
rmOpts tool = RmOptions <$> ghcVersionArgument [ListInstalled True] tool
|
||||
|
||||
|
||||
|
||||
@@ -127,7 +126,7 @@ rmFooter = [s|Discussion:
|
||||
---------------------------
|
||||
|
||||
|
||||
type RmEffects = '[ NotInstalled ]
|
||||
type RmEffects = '[ NotInstalled, UninstallFailed ]
|
||||
|
||||
|
||||
runRm :: (ReaderT env m (VEither RmEffects a) -> m (VEither RmEffects a))
|
||||
@@ -175,11 +174,11 @@ rm rmCommand runAppState runLogger = case rmCommand of
|
||||
)
|
||||
>>= \case
|
||||
VRight vi -> do
|
||||
forM_ (_viPostRemove =<< vi) $ \msg ->
|
||||
runLogger $ logInfo msg
|
||||
runLogger $ logGHCPostRm ghcVer
|
||||
postRmLog vi
|
||||
pure ExitSuccess
|
||||
VLeft e -> do
|
||||
runLogger $ logError $ T.pack $ prettyShow e
|
||||
runLogger $ logError $ T.pack $ prettyHFError e
|
||||
pure $ ExitFailure 7
|
||||
|
||||
rmCabal' tv =
|
||||
@@ -191,11 +190,10 @@ rm rmCommand runAppState runLogger = case rmCommand of
|
||||
)
|
||||
>>= \case
|
||||
VRight vi -> do
|
||||
forM_ (_viPostRemove =<< vi) $ \msg ->
|
||||
runLogger $ logInfo msg
|
||||
postRmLog vi
|
||||
pure ExitSuccess
|
||||
VLeft e -> do
|
||||
runLogger $ logError $ T.pack $ prettyShow e
|
||||
runLogger $ logError $ T.pack $ prettyHFError e
|
||||
pure $ ExitFailure 15
|
||||
|
||||
rmHLS' tv =
|
||||
@@ -207,11 +205,10 @@ rm rmCommand runAppState runLogger = case rmCommand of
|
||||
)
|
||||
>>= \case
|
||||
VRight vi -> do
|
||||
forM_ (_viPostRemove =<< vi) $ \msg ->
|
||||
runLogger $ logInfo msg
|
||||
postRmLog vi
|
||||
pure ExitSuccess
|
||||
VLeft e -> do
|
||||
runLogger $ logError $ T.pack $ prettyShow e
|
||||
runLogger $ logError $ T.pack $ prettyHFError e
|
||||
pure $ ExitFailure 15
|
||||
|
||||
rmStack' tv =
|
||||
@@ -223,10 +220,12 @@ rm rmCommand runAppState runLogger = case rmCommand of
|
||||
)
|
||||
>>= \case
|
||||
VRight vi -> do
|
||||
forM_ (_viPostRemove =<< vi) $ \msg ->
|
||||
runLogger $ logInfo msg
|
||||
postRmLog vi
|
||||
pure ExitSuccess
|
||||
VLeft e -> do
|
||||
runLogger $ logError $ T.pack $ prettyShow e
|
||||
runLogger $ logError $ T.pack $ prettyHFError e
|
||||
pure $ ExitFailure 15
|
||||
|
||||
postRmLog vi =
|
||||
forM_ (_viPostRemove =<< vi) $ \msg ->
|
||||
runLogger $ logInfo msg
|
||||
|
||||
@@ -5,19 +5,24 @@
|
||||
{-# LANGUAGE RankNTypes #-}
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE TypeFamilies #-}
|
||||
{-# LANGUAGE ViewPatterns #-}
|
||||
module GHCup.OptParse.Run where
|
||||
|
||||
|
||||
import GHCup
|
||||
import GHCup.Utils
|
||||
import GHCup.Utils.Prelude
|
||||
import GHCup.Utils.File
|
||||
import GHCup.OptParse.Common
|
||||
import GHCup.Errors
|
||||
import GHCup.Types
|
||||
import GHCup.Types.Optics
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Utils.String.QQ
|
||||
import GHCup.Prelude
|
||||
import GHCup.Prelude.File
|
||||
#ifdef IS_WINDOWS
|
||||
import GHCup.Prelude.Process
|
||||
import GHCup.Prelude.Process.Windows ( execNoMinGW )
|
||||
#endif
|
||||
import GHCup.Prelude.Logger
|
||||
import GHCup.Prelude.String.QQ
|
||||
|
||||
import Control.Exception.Safe ( MonadMask, MonadCatch )
|
||||
#if !MIN_VERSION_base(4,13,0)
|
||||
@@ -32,17 +37,16 @@ import Data.List ( intercalate )
|
||||
import Haskus.Utils.Variant.Excepts
|
||||
import Options.Applicative hiding ( style )
|
||||
import Prelude hiding ( appendFile )
|
||||
import System.Directory
|
||||
import System.FilePath
|
||||
import System.Environment
|
||||
import System.Exit
|
||||
import Text.PrettyPrint.HughesPJClass ( prettyShow )
|
||||
|
||||
import qualified Data.Map.Strict as Map
|
||||
import qualified Data.Text as T
|
||||
#ifndef IS_WINDOWS
|
||||
import qualified System.Posix.Process as SPP
|
||||
#endif
|
||||
import Data.Versions ( prettyVer, Version )
|
||||
|
||||
|
||||
|
||||
@@ -56,11 +60,13 @@ import qualified System.Posix.Process as SPP
|
||||
data RunOptions = RunOptions
|
||||
{ runAppendPATH :: Bool
|
||||
, runInstTool' :: Bool
|
||||
, runMinGWPath :: Bool
|
||||
, runGHCVer :: Maybe ToolVersion
|
||||
, runCabalVer :: Maybe ToolVersion
|
||||
, runHLSVer :: Maybe ToolVersion
|
||||
, runStackVer :: Maybe ToolVersion
|
||||
, runBinDir :: Maybe FilePath
|
||||
, runQuick :: Bool
|
||||
, runCOMMAND :: [String]
|
||||
}
|
||||
|
||||
@@ -70,8 +76,8 @@ data RunOptions = RunOptions
|
||||
--[ Parsers ]--
|
||||
---------------
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
runOpts :: Parser RunOptions
|
||||
runOpts =
|
||||
RunOptions
|
||||
@@ -79,36 +85,38 @@ runOpts =
|
||||
(short 'a' <> long "append" <> help "Append bin/ dir to PATH instead of prepending (this means that e.g. a system installation may take precedence)")
|
||||
<*> switch
|
||||
(short 'i' <> long "install" <> help "Install the tool, if missing")
|
||||
<*> switch
|
||||
(short 'm' <> long "mingw-path" <> help "On windows, add mingw64 PATHs to environment (does nothing on unix)")
|
||||
<*> optional
|
||||
(option
|
||||
(eitherReader toolVersionEither)
|
||||
(eitherReader ghcVersionTagEither)
|
||||
(metavar "GHC_VERSION" <> long "ghc" <> help "The ghc version"
|
||||
<> completer (tagCompleter GHC [])
|
||||
<> (completer $ versionCompleter Nothing GHC)
|
||||
<> (completer $ versionCompleter [] GHC)
|
||||
)
|
||||
)
|
||||
<*> optional
|
||||
(option
|
||||
(eitherReader toolVersionEither)
|
||||
(eitherReader toolVersionTagEither)
|
||||
(metavar "CABAL_VERSION" <> long "cabal" <> help "The cabal version"
|
||||
<> completer (tagCompleter Cabal [])
|
||||
<> (completer $ versionCompleter Nothing Cabal)
|
||||
<> (completer $ versionCompleter [] Cabal)
|
||||
)
|
||||
)
|
||||
<*> optional
|
||||
(option
|
||||
(eitherReader toolVersionEither)
|
||||
(eitherReader toolVersionTagEither)
|
||||
(metavar "HLS_VERSION" <> long "hls" <> help "The HLS version"
|
||||
<> completer (tagCompleter HLS [])
|
||||
<> (completer $ versionCompleter Nothing HLS)
|
||||
<> (completer $ versionCompleter [] HLS)
|
||||
)
|
||||
)
|
||||
<*> optional
|
||||
(option
|
||||
(eitherReader toolVersionEither)
|
||||
(eitherReader toolVersionTagEither)
|
||||
(metavar "STACK_VERSION" <> long "stack" <> help "The stack version"
|
||||
<> completer (tagCompleter Stack [])
|
||||
<> (completer $ versionCompleter Nothing Stack)
|
||||
<> (completer $ versionCompleter [] Stack)
|
||||
)
|
||||
)
|
||||
<*> optional
|
||||
@@ -121,8 +129,10 @@ runOpts =
|
||||
<> completer (bashCompleter "directory")
|
||||
)
|
||||
)
|
||||
<*> switch
|
||||
(short 'q' <> long "quick" <> help "Avoid any expensive work (such as downloads, version/tag resolution etc.). Disables --install.")
|
||||
<*> many (argument str (metavar "COMMAND" <> help "The command to run, with arguments (use longopts --). If omitted, just prints the created bin/ dir to stdout and exits."))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -165,7 +175,9 @@ type RunEffects = '[ AlreadyInstalled
|
||||
, NotInstalled
|
||||
, BuildFailed
|
||||
, TagNotFound
|
||||
, DayNotFound
|
||||
, DigestError
|
||||
, ContentLengthError
|
||||
, GPGError
|
||||
, DownloadFailed
|
||||
, TarDirDoesNotExist
|
||||
@@ -173,6 +185,8 @@ type RunEffects = '[ AlreadyInstalled
|
||||
, NoToolVersionSet
|
||||
, FileAlreadyExistsError
|
||||
, ProcessError
|
||||
, UninstallFailed
|
||||
, MergeFileTreeError
|
||||
]
|
||||
|
||||
runLeanRUN :: (MonadUnliftIO m, MonadIO m)
|
||||
@@ -206,7 +220,7 @@ runRUN appState action' = do
|
||||
|
||||
|
||||
|
||||
run :: forall m.
|
||||
run :: forall m .
|
||||
( MonadFail m
|
||||
, MonadMask m
|
||||
, MonadCatch m
|
||||
@@ -219,29 +233,19 @@ run :: forall m.
|
||||
-> (ReaderT LeanAppState m () -> m ())
|
||||
-> m ExitCode
|
||||
run RunOptions{..} runAppState leanAppstate runLogger = do
|
||||
r <- if or (fmap (maybe False isToolTag) [runGHCVer, runCabalVer, runHLSVer, runStackVer]) || runInstTool'
|
||||
r <- if not runQuick
|
||||
then runRUN runAppState $ do
|
||||
toolchain <- liftE resolveToolchainFull
|
||||
tmp <- case runBinDir of
|
||||
Just bindir -> do
|
||||
liftIO $ createDirRecursive' bindir
|
||||
liftIO $ canonicalizePath bindir
|
||||
Nothing -> do
|
||||
d <- liftIO $ predictableTmpDir toolchain
|
||||
liftIO $ createDirRecursive' d
|
||||
liftIO $ canonicalizePath d
|
||||
|
||||
-- oh dear
|
||||
r <- lift ask
|
||||
tmp <- lift . lift . lift . flip runReaderT (fromAppState r) $ createTmpDir toolchain
|
||||
|
||||
liftE $ installToolChainFull toolchain tmp
|
||||
pure tmp
|
||||
else runLeanRUN leanAppstate $ do
|
||||
toolchain <- resolveToolchain
|
||||
tmp <- case runBinDir of
|
||||
Just bindir -> do
|
||||
liftIO $ createDirRecursive' bindir
|
||||
liftIO $ canonicalizePath bindir
|
||||
Nothing -> do
|
||||
d <- liftIO $ predictableTmpDir toolchain
|
||||
liftIO $ createDirRecursive' d
|
||||
liftIO $ canonicalizePath d
|
||||
tmp <- lift $ createTmpDir toolchain
|
||||
liftE $ installToolChain toolchain tmp
|
||||
pure tmp
|
||||
case r of
|
||||
@@ -251,28 +255,26 @@ run RunOptions{..} runAppState leanAppstate runLogger = do
|
||||
liftIO $ putStr tmp
|
||||
pure ExitSuccess
|
||||
(cmd:args) -> do
|
||||
newEnv <- liftIO $ addToPath tmp
|
||||
newEnv <- liftIO $ addToPath tmp runAppendPATH
|
||||
#ifndef IS_WINDOWS
|
||||
void $ liftIO $ SPP.executeFile cmd True args (Just newEnv)
|
||||
pure ExitSuccess
|
||||
#else
|
||||
r' <- runLeanRUN leanAppstate $ liftE $ lEM @_ @'[ProcessError] $ exec cmd args Nothing (Just newEnv)
|
||||
r' <- if runMinGWPath
|
||||
then runLeanRUN leanAppstate $ liftE $ lEM @_ @'[ProcessError] $ exec cmd args Nothing (Just newEnv)
|
||||
else runLeanRUN leanAppstate $ liftE $ lEM @_ @'[ProcessError] $ execNoMinGW cmd args Nothing (Just newEnv)
|
||||
case r' of
|
||||
VRight _ -> pure ExitSuccess
|
||||
VLeft e -> do
|
||||
runLogger $ logError $ T.pack $ prettyShow e
|
||||
runLogger $ logError $ T.pack $ prettyHFError e
|
||||
pure $ ExitFailure 28
|
||||
#endif
|
||||
VLeft e -> do
|
||||
runLogger $ logError $ T.pack $ prettyShow e
|
||||
runLogger $ logError $ T.pack $ prettyHFError e
|
||||
pure $ ExitFailure 27
|
||||
|
||||
where
|
||||
|
||||
isToolTag :: ToolVersion -> Bool
|
||||
isToolTag (ToolTag _) = True
|
||||
isToolTag _ = False
|
||||
|
||||
-- TODO: doesn't work for cross
|
||||
resolveToolchainFull :: ( MonadFail m
|
||||
, MonadThrow m
|
||||
@@ -281,6 +283,7 @@ run RunOptions{..} runAppState leanAppstate runLogger = do
|
||||
)
|
||||
=> Excepts
|
||||
'[ TagNotFound
|
||||
, DayNotFound
|
||||
, NextVerNotFound
|
||||
, NoToolVersionSet
|
||||
] (ResourceT (ReaderT AppState m)) Toolchain
|
||||
@@ -290,29 +293,33 @@ run RunOptions{..} runAppState leanAppstate runLogger = do
|
||||
pure v
|
||||
cabalVer <- forM runCabalVer $ \ver -> do
|
||||
(v, _) <- liftE $ fromVersion (Just ver) Cabal
|
||||
pure v
|
||||
pure (_tvVersion v)
|
||||
hlsVer <- forM runHLSVer $ \ver -> do
|
||||
(v, _) <- liftE $ fromVersion (Just ver) HLS
|
||||
pure v
|
||||
pure (_tvVersion v)
|
||||
stackVer <- forM runStackVer $ \ver -> do
|
||||
(v, _) <- liftE $ fromVersion (Just ver) Stack
|
||||
pure v
|
||||
pure (_tvVersion v)
|
||||
pure Toolchain{..}
|
||||
|
||||
resolveToolchain = do
|
||||
ghcVer <- case runGHCVer of
|
||||
Just (ToolVersion v) -> pure $ Just v
|
||||
Just (GHCVersion v) -> pure $ Just v
|
||||
Just (ToolVersion v) -> pure $ Just (mkTVer v)
|
||||
Nothing -> pure Nothing
|
||||
_ -> fail "Internal error"
|
||||
cabalVer <- case runCabalVer of
|
||||
Just (GHCVersion v) -> pure $ Just (_tvVersion v)
|
||||
Just (ToolVersion v) -> pure $ Just v
|
||||
Nothing -> pure Nothing
|
||||
_ -> fail "Internal error"
|
||||
hlsVer <- case runHLSVer of
|
||||
Just (GHCVersion v) -> pure $ Just (_tvVersion v)
|
||||
Just (ToolVersion v) -> pure $ Just v
|
||||
Nothing -> pure Nothing
|
||||
_ -> fail "Internal error"
|
||||
stackVer <- case runStackVer of
|
||||
Just (GHCVersion v) -> pure $ Just (_tvVersion v)
|
||||
Just (ToolVersion v) -> pure $ Just v
|
||||
Nothing -> pure Nothing
|
||||
_ -> fail "Internal error"
|
||||
@@ -327,6 +334,7 @@ run RunOptions{..} runAppState leanAppstate runLogger = do
|
||||
-> FilePath
|
||||
-> Excepts
|
||||
'[ TagNotFound
|
||||
, DayNotFound
|
||||
, NextVerNotFound
|
||||
, NoToolVersionSet
|
||||
, UnknownArchive
|
||||
@@ -338,41 +346,53 @@ run RunOptions{..} runAppState leanAppstate runLogger = do
|
||||
, DownloadFailed
|
||||
, DirNotEmpty
|
||||
, DigestError
|
||||
, ContentLengthError
|
||||
, BuildFailed
|
||||
, ArchiveResult
|
||||
, AlreadyInstalled
|
||||
, FileAlreadyExistsError
|
||||
, CopyError
|
||||
, UninstallFailed
|
||||
, MergeFileTreeError
|
||||
] (ResourceT (ReaderT AppState m)) ()
|
||||
installToolChainFull Toolchain{..} tmp = do
|
||||
forM_ [(GHC,) <$> ghcVer, (Cabal,) <$> cabalVer, (HLS,) <$> hlsVer, (Stack,) <$> stackVer] $ \mt -> do
|
||||
isInstalled <- maybe (pure False) (\(tool, v) -> lift $ checkIfToolInstalled' tool v) mt
|
||||
case mt of
|
||||
Just (GHC, v) -> do
|
||||
unless isInstalled $ when (runInstTool' && isNothing (_tvTarget v)) $ void $ liftE $ installGHCBin
|
||||
(_tvVersion v)
|
||||
Nothing
|
||||
False
|
||||
setTool GHC v tmp
|
||||
Just (Cabal, v) -> do
|
||||
unless isInstalled $ when runInstTool' $ void $ liftE $ installCabalBin
|
||||
(_tvVersion v)
|
||||
Nothing
|
||||
False
|
||||
setTool Cabal v tmp
|
||||
Just (Stack, v) -> do
|
||||
unless isInstalled $ when runInstTool' $ void $ liftE $ installStackBin
|
||||
(_tvVersion v)
|
||||
Nothing
|
||||
False
|
||||
setTool Stack v tmp
|
||||
Just (HLS, v) -> do
|
||||
unless isInstalled $ when runInstTool' $ void $ liftE $ installHLSBin
|
||||
(_tvVersion v)
|
||||
Nothing
|
||||
False
|
||||
setTool HLS v tmp
|
||||
_ -> pure ()
|
||||
case ghcVer of
|
||||
Just v -> do
|
||||
isInstalled <- lift $ checkIfToolInstalled' GHC v
|
||||
unless isInstalled $ when (runInstTool' && isNothing (_tvTarget v)) $ void $ liftE $ installGHCBin
|
||||
(_tvVersion v)
|
||||
GHCupInternal
|
||||
False
|
||||
[]
|
||||
setGHC' v tmp
|
||||
_ -> pure ()
|
||||
case cabalVer of
|
||||
Just v -> do
|
||||
isInstalled <- lift $ checkIfToolInstalled' Cabal (mkTVer v)
|
||||
unless isInstalled $ when runInstTool' $ void $ liftE $ installCabalBin
|
||||
v
|
||||
GHCupInternal
|
||||
False
|
||||
setCabal' v tmp
|
||||
_ -> pure ()
|
||||
case stackVer of
|
||||
Just v -> do
|
||||
isInstalled <- lift $ checkIfToolInstalled' Stack (mkTVer v)
|
||||
unless isInstalled $ when runInstTool' $ void $ liftE $ installStackBin
|
||||
v
|
||||
GHCupInternal
|
||||
False
|
||||
setStack' v tmp
|
||||
_ -> pure ()
|
||||
case hlsVer of
|
||||
Just v -> do
|
||||
isInstalled <- lift $ checkIfToolInstalled' HLS (mkTVer v)
|
||||
unless isInstalled $ when runInstTool' $ void $ liftE $ installHLSBin
|
||||
v
|
||||
GHCupInternal
|
||||
False
|
||||
setHLS' v tmp
|
||||
_ -> pure ()
|
||||
|
||||
installToolChain :: ( MonadFail m
|
||||
, MonadThrow m
|
||||
@@ -383,67 +403,79 @@ run RunOptions{..} runAppState leanAppstate runLogger = do
|
||||
-> FilePath
|
||||
-> Excepts '[NotInstalled] (ReaderT LeanAppState m) ()
|
||||
installToolChain Toolchain{..} tmp = do
|
||||
forM_ [(GHC,) <$> ghcVer, (Cabal,) <$> cabalVer, (HLS,) <$> hlsVer, (Stack,) <$> stackVer] $ \mt -> do
|
||||
case mt of
|
||||
Just (GHC, v) -> setTool GHC v tmp
|
||||
Just (Cabal, v) -> setTool Cabal v tmp
|
||||
Just (Stack, v) -> setTool Stack v tmp
|
||||
Just (HLS, v) -> setTool HLS v tmp
|
||||
_ -> pure ()
|
||||
case ghcVer of
|
||||
Just v -> setGHC' v tmp
|
||||
_ -> pure ()
|
||||
case cabalVer of
|
||||
Just v -> setCabal' v tmp
|
||||
_ -> pure ()
|
||||
case stackVer of
|
||||
Just v -> setStack' v tmp
|
||||
_ -> pure ()
|
||||
case hlsVer of
|
||||
Just v -> setHLS' v tmp
|
||||
_ -> pure ()
|
||||
|
||||
setTool tool v tmp =
|
||||
case tool of
|
||||
GHC -> do
|
||||
setGHC' v tmp = do
|
||||
void $ liftE $ setGHC v SetGHC_XYZ (Just tmp)
|
||||
void $ liftE $ setGHC v SetGHCOnly (Just tmp)
|
||||
Cabal -> do
|
||||
bin <- liftE $ whereIsTool Cabal v
|
||||
setCabal' v tmp = do
|
||||
bin <- liftE $ whereIsTool Cabal (mkTVer v)
|
||||
cbin <- liftIO $ canonicalizePath bin
|
||||
lift $ createLink (relativeSymlink tmp cbin) (tmp </> ("cabal" <.> exeExt))
|
||||
Stack -> do
|
||||
bin <- liftE $ whereIsTool Stack v
|
||||
setStack' v tmp = do
|
||||
bin <- liftE $ whereIsTool Stack (mkTVer v)
|
||||
cbin <- liftIO $ canonicalizePath bin
|
||||
lift $ createLink (relativeSymlink tmp cbin) (tmp </> ("stack" <.> exeExt))
|
||||
HLS -> do
|
||||
setHLS' v tmp = do
|
||||
Dirs {..} <- getDirs
|
||||
let v' = _tvVersion v
|
||||
legacy <- isLegacyHLS v'
|
||||
legacy <- isLegacyHLS v
|
||||
if legacy
|
||||
then do
|
||||
-- TODO: factor this out
|
||||
hlsWrapper <- liftE @_ @'[NotInstalled] $ hlsWrapperBinary v' !? (NotInstalled HLS (mkTVer v'))
|
||||
hlsWrapper <- liftE @_ @'[NotInstalled] $ hlsWrapperBinary v !? (NotInstalled HLS (mkTVer v))
|
||||
cw <- liftIO $ canonicalizePath (binDir </> hlsWrapper)
|
||||
lift $ createLink (relativeSymlink tmp cw) (tmp </> takeFileName cw)
|
||||
hlsBins <- hlsServerBinaries v' Nothing >>= liftIO . traverse (canonicalizePath . (binDir </>))
|
||||
hlsBins <- hlsServerBinaries v Nothing >>= liftIO . traverse (canonicalizePath . (binDir </>))
|
||||
forM_ hlsBins $ \bin ->
|
||||
lift $ createLink (relativeSymlink tmp bin) (tmp </> takeFileName bin)
|
||||
liftE $ setHLS (_tvVersion v) SetHLSOnly (Just tmp)
|
||||
liftE $ setHLS v SetHLSOnly (Just tmp)
|
||||
else do
|
||||
liftE $ setHLS (_tvVersion v) SetHLS_XYZ (Just tmp)
|
||||
liftE $ setHLS (_tvVersion v) SetHLSOnly (Just tmp)
|
||||
GHCup -> pure ()
|
||||
|
||||
addToPath path = do
|
||||
cEnv <- Map.fromList <$> getEnvironment
|
||||
let paths = ["PATH", "Path"]
|
||||
curPaths = (\x -> maybe [] splitSearchPath (Map.lookup x cEnv)) =<< paths
|
||||
newPath = intercalate [searchPathSeparator] (if runAppendPATH then (curPaths ++ [path]) else (path : curPaths))
|
||||
envWithoutPath = foldr (\x y -> Map.delete x y) cEnv paths
|
||||
pathVar = if isWindows then "Path" else "PATH"
|
||||
envWithNewPath = Map.toList $ Map.insert pathVar newPath envWithoutPath
|
||||
liftIO $ setEnv pathVar newPath
|
||||
return envWithNewPath
|
||||
liftE $ setHLS v SetHLS_XYZ (Just tmp)
|
||||
liftE $ setHLS v SetHLSOnly (Just tmp)
|
||||
|
||||
predictableTmpDir (Toolchain Nothing Nothing Nothing Nothing) =
|
||||
liftIO (getTemporaryDirectory >>= \tmp -> pure (tmp </> "ghcup-none"))
|
||||
createTmpDir :: ( MonadUnliftIO m
|
||||
, MonadCatch m
|
||||
, MonadThrow m
|
||||
, MonadMask m
|
||||
, MonadIO m
|
||||
)
|
||||
=> Toolchain
|
||||
-> ReaderT LeanAppState m FilePath
|
||||
createTmpDir toolchain =
|
||||
case runBinDir of
|
||||
Just bindir -> do
|
||||
liftIO $ createDirRecursive' bindir
|
||||
liftIO $ canonicalizePath bindir
|
||||
Nothing -> do
|
||||
d <- predictableTmpDir toolchain
|
||||
liftIO $ createDirRecursive' d
|
||||
liftIO $ canonicalizePath d
|
||||
|
||||
predictableTmpDir :: Monad m
|
||||
=> Toolchain
|
||||
-> ReaderT LeanAppState m FilePath
|
||||
predictableTmpDir (Toolchain Nothing Nothing Nothing Nothing) = do
|
||||
Dirs { tmpDir } <- getDirs
|
||||
pure (fromGHCupPath tmpDir </> "ghcup-none")
|
||||
predictableTmpDir Toolchain{..} = do
|
||||
tmp <- getTemporaryDirectory
|
||||
pure $ tmp
|
||||
Dirs { tmpDir } <- getDirs
|
||||
pure $ fromGHCupPath tmpDir
|
||||
</> ("ghcup-" <> intercalate "_"
|
||||
( maybe [] ( (:[]) . ("ghc-" <>) . T.unpack . tVerToText) ghcVer
|
||||
<> maybe [] ( (:[]) . ("cabal-" <>) . T.unpack . tVerToText) cabalVer
|
||||
<> maybe [] ( (:[]) . ("hls-" <>) . T.unpack . tVerToText) hlsVer
|
||||
<> maybe [] ( (:[]) . ("stack-" <>) . T.unpack . tVerToText) stackVer
|
||||
<> maybe [] ( (:[]) . ("cabal-" <>) . T.unpack . prettyVer) cabalVer
|
||||
<> maybe [] ( (:[]) . ("hls-" <>) . T.unpack . prettyVer) hlsVer
|
||||
<> maybe [] ( (:[]) . ("stack-" <>) . T.unpack . prettyVer) stackVer
|
||||
)
|
||||
)
|
||||
|
||||
@@ -457,7 +489,7 @@ run RunOptions{..} runAppState leanAppstate runLogger = do
|
||||
|
||||
data Toolchain = Toolchain
|
||||
{ ghcVer :: Maybe GHCTargetVersion
|
||||
, cabalVer :: Maybe GHCTargetVersion
|
||||
, hlsVer :: Maybe GHCTargetVersion
|
||||
, stackVer :: Maybe GHCTargetVersion
|
||||
}
|
||||
, cabalVer :: Maybe Version
|
||||
, hlsVer :: Maybe Version
|
||||
, stackVer :: Maybe Version
|
||||
} deriving Show
|
||||
|
||||
@@ -17,8 +17,8 @@ import GHCup.OptParse.Common
|
||||
import GHCup
|
||||
import GHCup.Errors
|
||||
import GHCup.Types
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Utils.String.QQ
|
||||
import GHCup.Prelude.Logger
|
||||
import GHCup.Prelude.String.QQ
|
||||
|
||||
#if !MIN_VERSION_base(4,13,0)
|
||||
import Control.Monad.Fail ( MonadFail )
|
||||
@@ -35,7 +35,6 @@ import Options.Applicative hiding ( style )
|
||||
import Options.Applicative.Help.Pretty ( text )
|
||||
import Prelude hiding ( appendFile )
|
||||
import System.Exit
|
||||
import Text.PrettyPrint.HughesPJClass ( prettyShow )
|
||||
|
||||
import qualified Data.Text as T
|
||||
import Data.Bifunctor (second)
|
||||
@@ -74,7 +73,7 @@ data SetOptions = SetOptions
|
||||
--[ Parsers ]--
|
||||
---------------
|
||||
|
||||
|
||||
|
||||
setParser :: Parser (Either SetCommand SetOptions)
|
||||
setParser =
|
||||
(Left <$> subparser
|
||||
@@ -82,7 +81,7 @@ setParser =
|
||||
"ghc"
|
||||
( SetGHC
|
||||
<$> info
|
||||
(setOpts (Just GHC) <**> helper)
|
||||
(setOpts GHC <**> helper)
|
||||
( progDesc "Set GHC version"
|
||||
<> footerDoc (Just $ text setGHCFooter)
|
||||
)
|
||||
@@ -91,7 +90,7 @@ setParser =
|
||||
"cabal"
|
||||
( SetCabal
|
||||
<$> info
|
||||
(setOpts (Just Cabal) <**> helper)
|
||||
(setOpts Cabal <**> helper)
|
||||
( progDesc "Set Cabal version"
|
||||
<> footerDoc (Just $ text setCabalFooter)
|
||||
)
|
||||
@@ -100,7 +99,7 @@ setParser =
|
||||
"hls"
|
||||
( SetHLS
|
||||
<$> info
|
||||
(setOpts (Just HLS) <**> helper)
|
||||
(setOpts HLS <**> helper)
|
||||
( progDesc "Set haskell-language-server version"
|
||||
<> footerDoc (Just $ text setHLSFooter)
|
||||
)
|
||||
@@ -109,14 +108,14 @@ setParser =
|
||||
"stack"
|
||||
( SetStack
|
||||
<$> info
|
||||
(setOpts (Just Stack) <**> helper)
|
||||
(setOpts Stack <**> helper)
|
||||
( progDesc "Set stack version"
|
||||
<> footerDoc (Just $ text setStackFooter)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
<|> (Right <$> setOpts Nothing)
|
||||
<|> (Right <$> setOpts GHC)
|
||||
where
|
||||
setGHCFooter :: String
|
||||
setGHCFooter = [s|Discussion:
|
||||
@@ -137,22 +136,25 @@ setParser =
|
||||
Sets the the current haskell-language-server version.|]
|
||||
|
||||
|
||||
setOpts :: Maybe Tool -> Parser SetOptions
|
||||
setOpts :: Tool -> Parser SetOptions
|
||||
setOpts tool = SetOptions <$>
|
||||
(fromMaybe SetRecommended <$>
|
||||
optional (setVersionArgument (Just ListInstalled) tool))
|
||||
optional (setVersionArgument [ListInstalled True] tool))
|
||||
|
||||
setVersionArgument :: Maybe ListCriteria -> Maybe Tool -> Parser SetToolVersion
|
||||
setVersionArgument :: [ListCriteria] -> Tool -> Parser SetToolVersion
|
||||
setVersionArgument criteria tool =
|
||||
argument (eitherReader setEither)
|
||||
(metavar "VERSION|TAG|next"
|
||||
<> completer (tagCompleter (fromMaybe GHC tool) ["next"])
|
||||
<> foldMap (completer . versionCompleter criteria) tool)
|
||||
<> completer (tagCompleter tool ["next"])
|
||||
<> (completer . versionCompleter criteria) tool)
|
||||
where
|
||||
setEither s' =
|
||||
parseSet s'
|
||||
<|> second SetToolTag (tagEither s')
|
||||
<|> second SetToolVersion (tVersionEither s')
|
||||
<|> se s'
|
||||
se s' = case tool of
|
||||
GHC -> second SetGHCVersion (ghcVersionEither s')
|
||||
_ -> second SetToolVersion (toolVersionEither s')
|
||||
parseSet s' = case fmap toLower s' of
|
||||
"next" -> Right SetNext
|
||||
other -> Left $ "Unknown tag/version " <> other
|
||||
@@ -182,6 +184,7 @@ setFooter = [s|Discussion:
|
||||
type SetGHCEffects = '[ FileDoesNotExistError
|
||||
, NotInstalled
|
||||
, TagNotFound
|
||||
, DayNotFound
|
||||
, NextVerNotFound
|
||||
, NoToolVersionSet]
|
||||
|
||||
@@ -196,6 +199,7 @@ runSetGHC runAppState =
|
||||
|
||||
type SetCabalEffects = '[ NotInstalled
|
||||
, TagNotFound
|
||||
, DayNotFound
|
||||
, NextVerNotFound
|
||||
, NoToolVersionSet]
|
||||
|
||||
@@ -210,6 +214,7 @@ runSetCabal runAppState =
|
||||
|
||||
type SetHLSEffects = '[ NotInstalled
|
||||
, TagNotFound
|
||||
, DayNotFound
|
||||
, NextVerNotFound
|
||||
, NoToolVersionSet]
|
||||
|
||||
@@ -224,6 +229,7 @@ runSetHLS runAppState =
|
||||
|
||||
type SetStackEffects = '[ NotInstalled
|
||||
, TagNotFound
|
||||
, DayNotFound
|
||||
, NextVerNotFound
|
||||
, NoToolVersionSet]
|
||||
|
||||
@@ -257,22 +263,19 @@ set :: forall m env.
|
||||
-> m (VEither eff GHCTargetVersion))
|
||||
-> (ReaderT LeanAppState m () -> m ())
|
||||
-> m ExitCode
|
||||
set setCommand runAppState runLeanAppState runLogger = case setCommand of
|
||||
set setCommand runAppState _ runLogger = case setCommand of
|
||||
(Right sopts) -> do
|
||||
runLogger (logWarn "This is an old-style command for setting GHC. Use 'ghcup set ghc' instead.")
|
||||
setGHC' sopts
|
||||
(Left (SetGHC sopts)) -> setGHC' sopts
|
||||
(Left (SetGHC sopts)) -> setGHC' sopts
|
||||
(Left (SetCabal sopts)) -> setCabal' sopts
|
||||
(Left (SetHLS sopts)) -> setHLS' sopts
|
||||
(Left (SetHLS sopts)) -> setHLS' sopts
|
||||
(Left (SetStack sopts)) -> setStack' sopts
|
||||
|
||||
where
|
||||
setGHC' :: SetOptions
|
||||
-> m ExitCode
|
||||
setGHC' SetOptions{ sToolVer } =
|
||||
case sToolVer of
|
||||
(SetToolVersion v) -> runSetGHC runLeanAppState (liftE $ setGHC v SetGHCOnly Nothing >> pure v)
|
||||
_ -> runSetGHC runAppState (do
|
||||
setGHC' SetOptions{ sToolVer } = runSetGHC runAppState (do
|
||||
v <- liftE $ fst <$> fromVersion' sToolVer GHC
|
||||
liftE $ setGHC v SetGHCOnly Nothing
|
||||
)
|
||||
@@ -283,67 +286,58 @@ set setCommand runAppState runLeanAppState runLogger = case setCommand of
|
||||
"GHC " <> prettyVer _tvVersion <> " successfully set as default version" <> maybe "" (" for cross target " <>) _tvTarget
|
||||
pure ExitSuccess
|
||||
VLeft e -> do
|
||||
runLogger $ logError $ T.pack $ prettyShow e
|
||||
runLogger $ logError $ T.pack $ prettyHFError e
|
||||
pure $ ExitFailure 5
|
||||
|
||||
|
||||
setCabal' :: SetOptions
|
||||
-> m ExitCode
|
||||
setCabal' SetOptions{ sToolVer } =
|
||||
case sToolVer of
|
||||
(SetToolVersion v) -> runSetCabal runLeanAppState (liftE $ setCabal (_tvVersion v) >> pure v)
|
||||
_ -> runSetCabal runAppState (do
|
||||
setCabal' SetOptions{ sToolVer } = runSetCabal runAppState (do
|
||||
v <- liftE $ fst <$> fromVersion' sToolVer Cabal
|
||||
liftE $ setCabal (_tvVersion v)
|
||||
pure v
|
||||
)
|
||||
>>= \case
|
||||
VRight GHCTargetVersion{..} -> do
|
||||
VRight v -> do
|
||||
runLogger
|
||||
$ logInfo $
|
||||
"Cabal " <> prettyVer _tvVersion <> " successfully set as default version"
|
||||
"Cabal " <> prettyVer (_tvVersion v) <> " successfully set as default version"
|
||||
pure ExitSuccess
|
||||
VLeft e -> do
|
||||
runLogger $ logError $ T.pack $ prettyShow e
|
||||
runLogger $ logError $ T.pack $ prettyHFError e
|
||||
pure $ ExitFailure 14
|
||||
|
||||
setHLS' :: SetOptions
|
||||
-> m ExitCode
|
||||
setHLS' SetOptions{ sToolVer } =
|
||||
case sToolVer of
|
||||
(SetToolVersion v) -> runSetHLS runLeanAppState (liftE $ setHLS (_tvVersion v) SetHLSOnly Nothing >> pure v)
|
||||
_ -> runSetHLS runAppState (do
|
||||
setHLS' SetOptions{ sToolVer } = runSetHLS runAppState (do
|
||||
v <- liftE $ fst <$> fromVersion' sToolVer HLS
|
||||
liftE $ setHLS (_tvVersion v) SetHLSOnly Nothing
|
||||
pure v
|
||||
)
|
||||
>>= \case
|
||||
VRight GHCTargetVersion{..} -> do
|
||||
VRight v -> do
|
||||
runLogger
|
||||
$ logInfo $
|
||||
"HLS " <> prettyVer _tvVersion <> " successfully set as default version"
|
||||
"HLS " <> prettyVer (_tvVersion v) <> " successfully set as default version"
|
||||
pure ExitSuccess
|
||||
VLeft e -> do
|
||||
runLogger $ logError $ T.pack $ prettyShow e
|
||||
runLogger $ logError $ T.pack $ prettyHFError e
|
||||
pure $ ExitFailure 14
|
||||
|
||||
|
||||
setStack' :: SetOptions
|
||||
-> m ExitCode
|
||||
setStack' SetOptions{ sToolVer } =
|
||||
case sToolVer of
|
||||
(SetToolVersion v) -> runSetStack runLeanAppState (liftE $ setStack (_tvVersion v) >> pure v)
|
||||
_ -> runSetStack runAppState (do
|
||||
setStack' SetOptions{ sToolVer } = runSetStack runAppState (do
|
||||
v <- liftE $ fst <$> fromVersion' sToolVer Stack
|
||||
liftE $ setStack (_tvVersion v)
|
||||
pure v
|
||||
)
|
||||
>>= \case
|
||||
VRight GHCTargetVersion{..} -> do
|
||||
VRight v -> do
|
||||
runLogger
|
||||
$ logInfo $
|
||||
"Stack " <> prettyVer _tvVersion <> " successfully set as default version"
|
||||
"Stack " <> prettyVer (_tvVersion v) <> " successfully set as default version"
|
||||
pure ExitSuccess
|
||||
VLeft e -> do
|
||||
runLogger $ logError $ T.pack $ prettyShow e
|
||||
runLogger $ logError $ T.pack $ prettyHFError e
|
||||
pure $ ExitFailure 14
|
||||
|
||||
189
app/ghcup/GHCup/OptParse/Test.hs
Normal file
189
app/ghcup/GHCup/OptParse/Test.hs
Normal file
@@ -0,0 +1,189 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE DataKinds #-}
|
||||
{-# LANGUAGE TypeApplications #-}
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE TemplateHaskell #-}
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE DuplicateRecordFields #-}
|
||||
{-# LANGUAGE ViewPatterns #-}
|
||||
{-# LANGUAGE TypeOperators #-}
|
||||
|
||||
module GHCup.OptParse.Test where
|
||||
|
||||
|
||||
|
||||
|
||||
import GHCup.OptParse.Common
|
||||
|
||||
import GHCup
|
||||
import GHCup.Errors
|
||||
import GHCup.Types
|
||||
import GHCup.Utils.Dirs
|
||||
import GHCup.Prelude.Logger
|
||||
import GHCup.Prelude.String.QQ
|
||||
|
||||
import Codec.Archive
|
||||
#if !MIN_VERSION_base(4,13,0)
|
||||
import Control.Monad.Fail ( MonadFail )
|
||||
#endif
|
||||
import Control.Monad.Reader
|
||||
import Control.Monad.Trans.Resource
|
||||
import Data.Functor
|
||||
import Data.Maybe
|
||||
import Haskus.Utils.Variant.Excepts
|
||||
import Options.Applicative hiding ( style )
|
||||
import Options.Applicative.Help.Pretty ( text )
|
||||
import Prelude hiding ( appendFile )
|
||||
import System.Exit
|
||||
import URI.ByteString hiding ( uriParser )
|
||||
|
||||
import qualified Data.Text as T
|
||||
|
||||
|
||||
|
||||
|
||||
----------------
|
||||
--[ Commands ]--
|
||||
----------------
|
||||
|
||||
|
||||
data TestCommand = TestGHC TestOptions
|
||||
|
||||
|
||||
|
||||
|
||||
---------------
|
||||
--[ Options ]--
|
||||
---------------
|
||||
|
||||
|
||||
data TestOptions = TestOptions
|
||||
{ testVer :: Maybe ToolVersion
|
||||
, testBindist :: Maybe URI
|
||||
, addMakeArgs :: [T.Text]
|
||||
}
|
||||
|
||||
|
||||
|
||||
---------------
|
||||
--[ Footers ]--
|
||||
---------------
|
||||
|
||||
testFooter :: String
|
||||
testFooter = [s|Discussion:
|
||||
Runs test suites from the test bindist.|]
|
||||
|
||||
|
||||
|
||||
|
||||
---------------
|
||||
--[ Parsers ]--
|
||||
---------------
|
||||
|
||||
testParser :: Parser TestCommand
|
||||
testParser =
|
||||
subparser
|
||||
( command
|
||||
"ghc"
|
||||
( TestGHC
|
||||
<$> info
|
||||
(testOpts (Just GHC) <**> helper)
|
||||
( progDesc "Test GHC"
|
||||
<> footerDoc (Just $ text testGHCFooter)
|
||||
)
|
||||
)
|
||||
)
|
||||
where
|
||||
testGHCFooter :: String
|
||||
testGHCFooter = [s|Discussion:
|
||||
Runs the GHC test suite from the test bindist.|]
|
||||
|
||||
|
||||
testOpts :: Maybe Tool -> Parser TestOptions
|
||||
testOpts tool =
|
||||
(\(u, v) args -> TestOptions v u args)
|
||||
<$> ( ( (,)
|
||||
<$> optional
|
||||
(option
|
||||
(eitherReader uriParser)
|
||||
(short 'u' <> long "url" <> metavar "BINDIST_URL" <> help
|
||||
"Install the specified version from this bindist"
|
||||
<> completer (toolDlCompleter (fromMaybe GHC tool))
|
||||
)
|
||||
)
|
||||
<*> (Just <$> toolVersionTagArgument [] tool)
|
||||
)
|
||||
<|> pure (Nothing, Nothing)
|
||||
)
|
||||
<*> many (argument str (metavar "MAKE_ARGS" <> help "Additional arguments to 'make', prefix with '-- ' (longopts)"))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
---------------------------
|
||||
--[ Effect interpreters ]--
|
||||
---------------------------
|
||||
|
||||
|
||||
type TestGHCEffects = [ DigestError
|
||||
, ContentLengthError
|
||||
, GPGError
|
||||
, DownloadFailed
|
||||
, NoDownload
|
||||
, ArchiveResult
|
||||
, TarDirDoesNotExist
|
||||
, UnknownArchive
|
||||
, TestFailed
|
||||
, NextVerNotFound
|
||||
, TagNotFound
|
||||
, DayNotFound
|
||||
, NoToolVersionSet
|
||||
]
|
||||
|
||||
runTestGHC :: AppState
|
||||
-> Excepts TestGHCEffects (ResourceT (ReaderT AppState IO)) a
|
||||
-> IO (VEither TestGHCEffects a)
|
||||
runTestGHC appstate' =
|
||||
flip runReaderT appstate'
|
||||
. runResourceT
|
||||
. runE
|
||||
@TestGHCEffects
|
||||
|
||||
|
||||
-------------------
|
||||
--[ Entrypoints ]--
|
||||
-------------------
|
||||
|
||||
|
||||
test :: TestCommand -> Settings -> IO AppState -> (ReaderT LeanAppState IO () -> IO ()) -> IO ExitCode
|
||||
test testCommand settings getAppState' runLogger = case testCommand of
|
||||
(TestGHC iopts) -> go iopts
|
||||
where
|
||||
go :: TestOptions -> IO ExitCode
|
||||
go TestOptions{..} = do
|
||||
s'@AppState{ dirs = Dirs{ .. } } <- liftIO getAppState'
|
||||
(case testBindist of
|
||||
Nothing -> runTestGHC s' $ do
|
||||
(v, vi) <- liftE $ fromVersion testVer GHC
|
||||
liftE $ testGHCVer (_tvVersion v) addMakeArgs
|
||||
pure vi
|
||||
Just uri -> do
|
||||
runTestGHC s'{ settings = settings {noVerify = True}} $ do
|
||||
(v, vi) <- liftE $ fromVersion testVer GHC
|
||||
liftE $ testGHCBindist (DownloadInfo uri (Just $ RegexDir ".*/.*") "" Nothing Nothing) (_tvVersion v) addMakeArgs
|
||||
pure vi
|
||||
)
|
||||
>>= \case
|
||||
VRight _ -> do
|
||||
runLogger $ logInfo "GHC test successful"
|
||||
pure ExitSuccess
|
||||
VLeft e -> do
|
||||
runLogger $ do
|
||||
logError $ T.pack $ prettyHFError e
|
||||
logError $ "Also check the logs in " <> T.pack (fromGHCupPath logsDir)
|
||||
pure $ ExitFailure 3
|
||||
|
||||
@@ -11,8 +11,8 @@ module GHCup.OptParse.ToolRequirements where
|
||||
|
||||
import GHCup.Errors
|
||||
import GHCup.Types
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Utils.String.QQ
|
||||
import GHCup.Prelude.Logger
|
||||
import GHCup.Prelude.String.QQ
|
||||
|
||||
#if !MIN_VERSION_base(4,13,0)
|
||||
import Control.Monad.Fail ( MonadFail )
|
||||
@@ -23,14 +23,13 @@ import Haskus.Utils.Variant.Excepts
|
||||
import Options.Applicative hiding ( style )
|
||||
import Prelude hiding ( appendFile )
|
||||
import System.Exit
|
||||
import Text.PrettyPrint.HughesPJClass ( prettyShow )
|
||||
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.IO as T
|
||||
import Control.Exception.Safe (MonadMask)
|
||||
import GHCup.Types.Optics
|
||||
import GHCup.Platform
|
||||
import GHCup.Utils.Prelude
|
||||
import GHCup.Prelude
|
||||
import GHCup.Requirements
|
||||
import System.IO
|
||||
|
||||
@@ -118,5 +117,5 @@ toolRequirements ToolReqOpts{..} runAppState runLogger = runToolRequirements run
|
||||
>>= \case
|
||||
VRight _ -> pure ExitSuccess
|
||||
VLeft e -> do
|
||||
runLogger $ logError $ T.pack $ prettyShow e
|
||||
runLogger $ logError $ T.pack $ prettyHFError e
|
||||
pure $ ExitFailure 12
|
||||
|
||||
@@ -16,8 +16,8 @@ module GHCup.OptParse.UnSet where
|
||||
import GHCup
|
||||
import GHCup.Errors
|
||||
import GHCup.Types
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Utils.String.QQ
|
||||
import GHCup.Prelude.Logger
|
||||
import GHCup.Prelude.String.QQ
|
||||
|
||||
#if !MIN_VERSION_base(4,13,0)
|
||||
import Control.Monad.Fail ( MonadFail )
|
||||
@@ -31,7 +31,6 @@ import Options.Applicative hiding ( style )
|
||||
import Options.Applicative.Help.Pretty ( text )
|
||||
import Prelude hiding ( appendFile )
|
||||
import System.Exit
|
||||
import Text.PrettyPrint.HughesPJClass ( prettyShow )
|
||||
|
||||
import qualified Data.Text as T
|
||||
import Control.Exception.Safe (MonadMask)
|
||||
@@ -189,7 +188,7 @@ unset unsetCommand runLeanAppState runLogger = case unsetCommand of
|
||||
runLogger $ logInfo "GHC successfully unset"
|
||||
pure ExitSuccess
|
||||
VLeft e -> do
|
||||
runLogger $ logError $ T.pack $ prettyShow e
|
||||
runLogger $ logError $ T.pack $ prettyHFError e
|
||||
pure $ ExitFailure 14
|
||||
(UnsetCabal (UnsetOptions _)) -> do
|
||||
void $ runLeanAppState (VRight <$> unsetCabal)
|
||||
|
||||
@@ -14,7 +14,8 @@ module GHCup.OptParse.Upgrade where
|
||||
import GHCup
|
||||
import GHCup.Errors
|
||||
import GHCup.Types
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Prelude.File
|
||||
import GHCup.Prelude.Logger
|
||||
|
||||
#if !MIN_VERSION_base(4,13,0)
|
||||
import Control.Monad.Fail ( MonadFail )
|
||||
@@ -27,7 +28,6 @@ import Haskus.Utils.Variant.Excepts
|
||||
import Options.Applicative hiding ( style )
|
||||
import Prelude hiding ( appendFile )
|
||||
import System.Exit
|
||||
import Text.PrettyPrint.HughesPJClass ( prettyShow )
|
||||
|
||||
import qualified Data.Text as T
|
||||
import Control.Exception.Safe (MonadMask)
|
||||
@@ -59,15 +59,16 @@ data UpgradeOpts = UpgradeInplace
|
||||
--[ Parsers ]--
|
||||
---------------
|
||||
|
||||
|
||||
|
||||
upgradeOptsP :: Parser UpgradeOpts
|
||||
upgradeOptsP =
|
||||
flag'
|
||||
UpgradeInplace
|
||||
(short 'i' <> long "inplace" <> help
|
||||
"Upgrade ghcup in-place (wherever it's at)"
|
||||
"Upgrade ghcup in-place"
|
||||
)
|
||||
<|> ( UpgradeAt
|
||||
<|>
|
||||
( UpgradeAt
|
||||
<$> option
|
||||
str
|
||||
(short 't' <> long "target" <> metavar "TARGET_DIR" <> help
|
||||
@@ -86,12 +87,14 @@ upgradeOptsP =
|
||||
|
||||
|
||||
type UpgradeEffects = '[ DigestError
|
||||
, ContentLengthError
|
||||
, GPGError
|
||||
, NoDownload
|
||||
, NoUpdate
|
||||
, FileDoesNotExistError
|
||||
, CopyError
|
||||
, DownloadFailed
|
||||
, ToolShadowed
|
||||
]
|
||||
|
||||
|
||||
@@ -120,18 +123,19 @@ upgrade :: ( Monad m
|
||||
)
|
||||
=> UpgradeOpts
|
||||
-> Bool
|
||||
-> Bool
|
||||
-> Dirs
|
||||
-> (forall a. ReaderT AppState m (VEither UpgradeEffects a) -> m (VEither UpgradeEffects a))
|
||||
-> (ReaderT LeanAppState m () -> m ())
|
||||
-> m ExitCode
|
||||
upgrade uOpts force' Dirs{..} runAppState runLogger = do
|
||||
upgrade uOpts force' fatal Dirs{..} runAppState runLogger = do
|
||||
target <- case uOpts of
|
||||
UpgradeInplace -> Just <$> liftIO getExecutablePath
|
||||
(UpgradeAt p) -> pure $ Just p
|
||||
UpgradeGHCupDir -> pure (Just (binDir </> "ghcup" <> exeExt))
|
||||
|
||||
runUpgrade runAppState (do
|
||||
v' <- liftE $ upgradeGHCup target force'
|
||||
v' <- liftE $ upgradeGHCup target force' fatal
|
||||
GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
|
||||
pure (v', dls)
|
||||
) >>= \case
|
||||
@@ -147,5 +151,5 @@ upgrade uOpts force' Dirs{..} runAppState runLogger = do
|
||||
runLogger $ logWarn "No GHCup update available"
|
||||
pure ExitSuccess
|
||||
VLeft e -> do
|
||||
runLogger $ logError $ T.pack $ prettyShow e
|
||||
runLogger $ logError $ T.pack $ prettyHFError e
|
||||
pure $ ExitFailure 11
|
||||
|
||||
@@ -17,8 +17,9 @@ import GHCup
|
||||
import GHCup.Errors
|
||||
import GHCup.OptParse.Common
|
||||
import GHCup.Types
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Utils.String.QQ
|
||||
import GHCup.Utils
|
||||
import GHCup.Prelude.Logger
|
||||
import GHCup.Prelude.String.QQ
|
||||
|
||||
#if !MIN_VERSION_base(4,13,0)
|
||||
import Control.Monad.Fail ( MonadFail )
|
||||
@@ -31,8 +32,8 @@ import Haskus.Utils.Variant.Excepts
|
||||
import Options.Applicative hiding ( style )
|
||||
import Options.Applicative.Help.Pretty ( text )
|
||||
import Prelude hiding ( appendFile )
|
||||
import System.Environment
|
||||
import System.Exit
|
||||
import Text.PrettyPrint.HughesPJClass ( prettyShow )
|
||||
|
||||
import qualified Data.Text as T
|
||||
import Control.Exception.Safe (MonadMask)
|
||||
@@ -74,14 +75,14 @@ data WhereisOptions = WhereisOptions {
|
||||
--[ Parsers ]--
|
||||
---------------
|
||||
|
||||
|
||||
|
||||
whereisP :: Parser WhereisCommand
|
||||
whereisP = subparser
|
||||
(commandGroup "Tools locations:" <>
|
||||
(commandGroup "Tools locations:" <>
|
||||
command
|
||||
"ghc"
|
||||
(WhereisTool GHC <$> info
|
||||
( optional (toolVersionArgument Nothing (Just GHC)) <**> helper )
|
||||
( optional (toolVersionTagArgument [] (Just GHC)) <**> helper )
|
||||
( progDesc "Get GHC location"
|
||||
<> footerDoc (Just $ text whereisGHCFooter ))
|
||||
)
|
||||
@@ -89,7 +90,7 @@ whereisP = subparser
|
||||
command
|
||||
"cabal"
|
||||
(WhereisTool Cabal <$> info
|
||||
( optional (toolVersionArgument Nothing (Just Cabal)) <**> helper )
|
||||
( optional (toolVersionTagArgument [] (Just Cabal)) <**> helper )
|
||||
( progDesc "Get cabal location"
|
||||
<> footerDoc (Just $ text whereisCabalFooter ))
|
||||
)
|
||||
@@ -97,7 +98,7 @@ whereisP = subparser
|
||||
command
|
||||
"hls"
|
||||
(WhereisTool HLS <$> info
|
||||
( optional (toolVersionArgument Nothing (Just HLS)) <**> helper )
|
||||
( optional (toolVersionTagArgument [] (Just HLS)) <**> helper )
|
||||
( progDesc "Get HLS location"
|
||||
<> footerDoc (Just $ text whereisHLSFooter ))
|
||||
)
|
||||
@@ -105,7 +106,7 @@ whereisP = subparser
|
||||
command
|
||||
"stack"
|
||||
(WhereisTool Stack <$> info
|
||||
( optional (toolVersionArgument Nothing (Just Stack)) <**> helper )
|
||||
( optional (toolVersionTagArgument [] (Just Stack)) <**> helper )
|
||||
( progDesc "Get stack location"
|
||||
<> footerDoc (Just $ text whereisStackFooter ))
|
||||
)
|
||||
@@ -221,6 +222,7 @@ type WhereisEffects = '[ NotInstalled
|
||||
, NoToolVersionSet
|
||||
, NextVerNotFound
|
||||
, TagNotFound
|
||||
, DayNotFound
|
||||
]
|
||||
|
||||
|
||||
@@ -267,7 +269,14 @@ whereis :: ( Monad m
|
||||
whereis whereisCommand whereisOptions runAppState leanAppstate runLogger = do
|
||||
Dirs{ .. } <- runReaderT getDirs leanAppstate
|
||||
case (whereisCommand, whereisOptions) of
|
||||
(WhereisTool tool (Just (ToolVersion v)), WhereisOptions{..}) ->
|
||||
(WhereisTool GHCup _, WhereisOptions{..}) -> do
|
||||
loc <- liftIO (getExecutablePath >>= canonicalizePath )
|
||||
if directory
|
||||
then liftIO $ putStr $ takeDirectory loc
|
||||
else liftIO $ putStr loc
|
||||
pure ExitSuccess
|
||||
|
||||
(WhereisTool tool (Just (GHCVersion v)), WhereisOptions{..}) ->
|
||||
runLeanWhereIs leanAppstate (do
|
||||
loc <- liftE $ whereIsTool tool v
|
||||
if directory
|
||||
@@ -279,7 +288,21 @@ whereis whereisCommand whereisOptions runAppState leanAppstate runLogger = do
|
||||
liftIO $ putStr r
|
||||
pure ExitSuccess
|
||||
VLeft e -> do
|
||||
runLogger $ logError $ T.pack $ prettyShow e
|
||||
runLogger $ logError $ T.pack $ prettyHFError e
|
||||
pure $ ExitFailure 30
|
||||
(WhereisTool tool (Just (ToolVersion v)), WhereisOptions{..}) ->
|
||||
runLeanWhereIs leanAppstate (do
|
||||
loc <- liftE $ whereIsTool tool (mkTVer v)
|
||||
if directory
|
||||
then pure $ takeDirectory loc
|
||||
else pure loc
|
||||
)
|
||||
>>= \case
|
||||
VRight r -> do
|
||||
liftIO $ putStr r
|
||||
pure ExitSuccess
|
||||
VLeft e -> do
|
||||
runLogger $ logError $ T.pack $ prettyHFError e
|
||||
pure $ ExitFailure 30
|
||||
|
||||
(WhereisTool tool whereVer, WhereisOptions{..}) -> do
|
||||
@@ -295,11 +318,11 @@ whereis whereisCommand whereisOptions runAppState leanAppstate runLogger = do
|
||||
liftIO $ putStr r
|
||||
pure ExitSuccess
|
||||
VLeft e -> do
|
||||
runLogger $ logError $ T.pack $ prettyShow e
|
||||
runLogger $ logError $ T.pack $ prettyHFError e
|
||||
pure $ ExitFailure 30
|
||||
|
||||
(WhereisBaseDir, _) -> do
|
||||
liftIO $ putStr baseDir
|
||||
liftIO $ putStr $ fromGHCupPath baseDir
|
||||
pure ExitSuccess
|
||||
|
||||
(WhereisBinDir, _) -> do
|
||||
@@ -307,13 +330,13 @@ whereis whereisCommand whereisOptions runAppState leanAppstate runLogger = do
|
||||
pure ExitSuccess
|
||||
|
||||
(WhereisCacheDir, _) -> do
|
||||
liftIO $ putStr cacheDir
|
||||
liftIO $ putStr $ fromGHCupPath cacheDir
|
||||
pure ExitSuccess
|
||||
|
||||
(WhereisLogsDir, _) -> do
|
||||
liftIO $ putStr logsDir
|
||||
liftIO $ putStr $ fromGHCupPath logsDir
|
||||
pure ExitSuccess
|
||||
|
||||
(WhereisConfDir, _) -> do
|
||||
liftIO $ putStr confDir
|
||||
liftIO $ putStr $ fromGHCupPath confDir
|
||||
pure ExitSuccess
|
||||
|
||||
@@ -14,6 +14,8 @@ module Main where
|
||||
import BrickMain ( brickMain )
|
||||
#endif
|
||||
|
||||
import qualified GHCup.GHC as GHC
|
||||
import qualified GHCup.HLS as HLS
|
||||
import GHCup.OptParse
|
||||
|
||||
import GHCup.Download
|
||||
@@ -22,9 +24,9 @@ import GHCup.Platform
|
||||
import GHCup.Types
|
||||
import GHCup.Types.Optics hiding ( toolRequirements )
|
||||
import GHCup.Utils
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Utils.Prelude
|
||||
import GHCup.Utils.String.QQ
|
||||
import GHCup.Prelude
|
||||
import GHCup.Prelude.Logger
|
||||
import GHCup.Prelude.String.QQ
|
||||
import GHCup.Version
|
||||
|
||||
import Cabal.Plan ( findPlanJson, SearchPlanJson(..) )
|
||||
@@ -61,7 +63,7 @@ import qualified GHCup.Types as Types
|
||||
|
||||
|
||||
|
||||
toSettings :: Options -> IO (Settings, KeyBindings)
|
||||
toSettings :: Options -> IO (Settings, KeyBindings, UserSettings)
|
||||
toSettings options = do
|
||||
noColor <- isJust <$> lookupEnv "NO_COLOR"
|
||||
userConf <- runE @'[ JSONError ] ghcupConfigFile >>= \case
|
||||
@@ -71,12 +73,13 @@ toSettings options = do
|
||||
pure defaultUserSettings
|
||||
_ -> do
|
||||
die "Unexpected error!"
|
||||
pure $ mergeConf options userConf noColor
|
||||
pure $ (\(s', k) -> (s', k, userConf)) $ mergeConf options userConf noColor
|
||||
where
|
||||
mergeConf :: Options -> UserSettings -> Bool -> (Settings, KeyBindings)
|
||||
mergeConf Options{..} UserSettings{..} noColor =
|
||||
let cache = fromMaybe (fromMaybe (Types.cache defaultSettings) uCache) optCache
|
||||
metaCache = fromMaybe (fromMaybe (Types.metaCache defaultSettings) uMetaCache) optMetaCache
|
||||
metaMode = fromMaybe (fromMaybe (Types.metaMode defaultSettings) uMetaMode) optMetaMode
|
||||
noVerify = fromMaybe (fromMaybe (Types.noVerify defaultSettings) uNoVerify) optNoVerify
|
||||
verbose = fromMaybe (fromMaybe (Types.verbose defaultSettings) uVerbose) optVerbose
|
||||
keepDirs = fromMaybe (fromMaybe (Types.keepDirs defaultSettings) uKeepDirs) optKeepDirs
|
||||
@@ -85,6 +88,8 @@ toSettings options = do
|
||||
urlSource = maybe (fromMaybe (Types.urlSource defaultSettings) uUrlSource) (OwnSource . (:[]) . Right) optUrlSource
|
||||
noNetwork = fromMaybe (fromMaybe (Types.noNetwork defaultSettings) uNoNetwork) optNoNetwork
|
||||
gpgSetting = fromMaybe (fromMaybe (Types.gpgSetting defaultSettings) uGPGSetting) optGpg
|
||||
platformOverride = optPlatform <|> (uPlatformOverride <|> Types.platformOverride defaultSettings)
|
||||
mirrors = fromMaybe (Types.mirrors defaultSettings) uMirrors
|
||||
in (Settings {..}, keyBindings)
|
||||
#if defined(INTERNAL_DOWNLOADER)
|
||||
defaultDownloader = Internal
|
||||
@@ -141,9 +146,7 @@ main = do
|
||||
)
|
||||
let listCommands = infoOption
|
||||
("install set rm install-cabal list"
|
||||
#ifndef DISABLE_UPGRADE
|
||||
<> " upgrade"
|
||||
#endif
|
||||
<> " compile debug-info tool-requirements changelog"
|
||||
)
|
||||
( long "list-commands"
|
||||
@@ -157,11 +160,10 @@ main = do
|
||||
versions. It maintains a self-contained ~/.ghcup directory.
|
||||
|
||||
ENV variables:
|
||||
* TMPDIR: where ghcup does the work (unpacking, building, ...)
|
||||
* GHCUP_INSTALL_BASE_PREFIX: the base of ghcup (default: $HOME)
|
||||
* GHCUP_USE_XDG_DIRS: set to anything to use XDG style directories
|
||||
|
||||
Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
Report bugs at <https://github.com/haskell/ghcup-hs/issues>|]
|
||||
|
||||
customExecParser
|
||||
(prefs showHelpOnError)
|
||||
@@ -174,7 +176,7 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
-- create ~/.ghcup dir
|
||||
ensureDirectories dirs
|
||||
|
||||
(settings, keybindings) <- toSettings opt
|
||||
(settings, keybindings, userConf) <- toSettings opt
|
||||
|
||||
-- logger interpreter
|
||||
logfile <- runReaderT initGHCupFileLogging dirs
|
||||
@@ -199,30 +201,30 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
|
||||
|
||||
let appState = do
|
||||
pfreq <- (
|
||||
runLogger . runE @'[NoCompatiblePlatform, NoCompatibleArch, DistroNotFound] . liftE $ platformRequest
|
||||
) >>= \case
|
||||
VRight r -> pure r
|
||||
VLeft e -> do
|
||||
runLogger
|
||||
(logError $ T.pack $ prettyShow e)
|
||||
exitWith (ExitFailure 2)
|
||||
pfreq <- case platformOverride settings of
|
||||
Just pfreq' -> return pfreq'
|
||||
Nothing -> (runLogger . runE @'[NoCompatiblePlatform, NoCompatibleArch, DistroNotFound] . liftE $ platformRequest) >>= \case
|
||||
VRight r -> pure r
|
||||
VLeft e -> do
|
||||
runLogger
|
||||
(logError $ T.pack $ prettyHFError e)
|
||||
exitWith (ExitFailure 2)
|
||||
|
||||
ghcupInfo <-
|
||||
( flip runReaderT leanAppstate
|
||||
. runE @'[DigestError, GPGError, JSONError , DownloadFailed, FileDoesNotExistError]
|
||||
. runE @'[DigestError, ContentLengthError, GPGError, JSONError , DownloadFailed, FileDoesNotExistError]
|
||||
$ liftE getDownloadsF
|
||||
)
|
||||
>>= \case
|
||||
VRight r -> pure r
|
||||
VLeft e -> do
|
||||
runLogger
|
||||
(logError $ T.pack $ prettyShow e)
|
||||
(logError $ T.pack $ prettyHFError e)
|
||||
exitWith (ExitFailure 2)
|
||||
let s' = AppState settings dirs keybindings ghcupInfo pfreq loggerConfig
|
||||
|
||||
race_ (liftIO $ runReaderT cleanupTrash s')
|
||||
(threadDelay 5000000 >> runLogger (logWarn $ "Killing cleanup thread (exceeded 5s timeout)... please remove leftover files in " <> T.pack recycleDir <> " manually"))
|
||||
(threadDelay 5000000 >> runLogger (logWarn $ "Killing cleanup thread (exceeded 5s timeout)... please remove leftover files in " <> T.pack (fromGHCupPath recycleDir) <> " manually"))
|
||||
|
||||
case optCommand of
|
||||
Nuke -> pure ()
|
||||
@@ -238,26 +240,22 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
_
|
||||
| Just False <- optVerbose -> pure ()
|
||||
| otherwise -> lookupEnv "GHCUP_SKIP_UPDATE_CHECK" >>= \case
|
||||
Nothing -> void . flip runReaderT s' . runE @'[TagNotFound, NextVerNotFound, NoToolVersionSet] $ do
|
||||
newTools <- lift checkForUpdates
|
||||
Nothing -> void . flip runReaderT s' . runE @'[TagNotFound, DayNotFound, NextVerNotFound, NoToolVersionSet] $ do
|
||||
newTools <- lift checkForUpdates
|
||||
forM_ newTools $ \newTool@(t, l) -> do
|
||||
-- https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/283
|
||||
alreadyInstalling' <- alreadyInstalling optCommand newTool
|
||||
when (not alreadyInstalling') $
|
||||
case t of
|
||||
#ifdef DISABLE_UPGRADE
|
||||
GHCup -> pure ()
|
||||
#else
|
||||
GHCup -> runLogger $
|
||||
logWarn ("New GHCup version available: "
|
||||
<> prettyVer l
|
||||
<> ". To upgrade, run 'ghcup upgrade'")
|
||||
#endif
|
||||
_ -> runLogger $
|
||||
logWarn ("New "
|
||||
<> T.pack (prettyShow t)
|
||||
<> " version available. "
|
||||
<> "To upgrade, run 'ghcup install "
|
||||
<> "If you want to install this latest version, run 'ghcup install "
|
||||
<> T.pack (prettyShow t)
|
||||
<> " "
|
||||
<> prettyVer l
|
||||
@@ -269,7 +267,7 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
VRight _ -> pure ()
|
||||
VLeft e -> do
|
||||
runLogger
|
||||
(logError $ T.pack $ prettyShow e)
|
||||
(logError $ T.pack $ prettyHFError e)
|
||||
exitWith (ExitFailure 30)
|
||||
pure s'
|
||||
|
||||
@@ -284,7 +282,7 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
runAppState action' = do
|
||||
s' <- liftIO appState
|
||||
runReaderT action' s'
|
||||
|
||||
|
||||
|
||||
-----------------
|
||||
-- Run command --
|
||||
@@ -296,26 +294,26 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
s' <- appState
|
||||
liftIO $ brickMain s' >> pure ExitSuccess
|
||||
#endif
|
||||
Install installCommand -> install installCommand settings appState runLogger
|
||||
InstallCabalLegacy iopts -> install (Left (InstallCabal iopts)) settings appState runLogger
|
||||
Set setCommand -> set setCommand runAppState runLeanAppState runLogger
|
||||
UnSet unsetCommand -> unset unsetCommand runLeanAppState runLogger
|
||||
List lo -> list lo no_color runAppState
|
||||
Rm rmCommand -> rm rmCommand runAppState runLogger
|
||||
DInfo -> dinfo runAppState runLogger
|
||||
Compile compileCommand -> compile compileCommand settings dirs runAppState runLogger
|
||||
Config configCommand -> config configCommand settings keybindings runLogger
|
||||
Install installCommand -> install installCommand settings appState runLogger
|
||||
InstallCabalLegacy iopts -> install (Left (InstallCabal iopts)) settings appState runLogger
|
||||
Test testCommand -> test testCommand settings appState runLogger
|
||||
Set setCommand -> set setCommand runAppState runLeanAppState runLogger
|
||||
UnSet unsetCommand -> unset unsetCommand runLeanAppState runLogger
|
||||
List lo -> list lo no_color runAppState
|
||||
Rm rmCommand -> rm rmCommand runAppState runLogger
|
||||
DInfo -> dinfo runAppState runLogger
|
||||
Compile compileCommand -> compile compileCommand settings dirs runAppState runLogger
|
||||
Config configCommand -> config configCommand settings userConf keybindings runLogger
|
||||
Whereis whereisOptions
|
||||
whereisCommand -> whereis whereisCommand whereisOptions runAppState leanAppstate runLogger
|
||||
#ifndef DISABLE_UPGRADE
|
||||
Upgrade uOpts force' -> upgrade uOpts force' dirs runAppState runLogger
|
||||
#endif
|
||||
ToolRequirements topts -> toolRequirements topts runAppState runLogger
|
||||
ChangeLog changelogOpts -> changelog changelogOpts runAppState runLogger
|
||||
Nuke -> nuke appState runLogger
|
||||
Prefetch pfCom -> prefetch pfCom runAppState runLogger
|
||||
GC gcOpts -> gc gcOpts runAppState runLogger
|
||||
Run runCommand -> run runCommand appState leanAppstate runLogger
|
||||
whereisCommand -> whereis whereisCommand whereisOptions runAppState leanAppstate runLogger
|
||||
Upgrade uOpts force' fatal -> upgrade uOpts force' fatal dirs runAppState runLogger
|
||||
ToolRequirements topts -> toolRequirements topts runAppState runLogger
|
||||
ChangeLog changelogOpts -> changelog changelogOpts runAppState runLogger
|
||||
Nuke -> nuke appState runLogger
|
||||
Prefetch pfCom -> prefetch pfCom runAppState runLogger
|
||||
GC gcOpts -> gc gcOpts runAppState runLogger
|
||||
Run runCommand -> run runCommand appState leanAppstate runLogger
|
||||
PrintAppErrors -> putStrLn allHFError >> pure ExitSuccess
|
||||
|
||||
case res of
|
||||
ExitSuccess -> pure ()
|
||||
@@ -337,6 +335,7 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
-> (Tool, Version)
|
||||
-> Excepts
|
||||
'[ TagNotFound
|
||||
, DayNotFound
|
||||
, NextVerNotFound
|
||||
, NoToolVersionSet
|
||||
] m Bool
|
||||
@@ -346,16 +345,16 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
alreadyInstalling (Install (Left (InstallHLS InstallOptions{..}))) (HLS, ver) = cmp' HLS instVer ver
|
||||
alreadyInstalling (Install (Left (InstallStack InstallOptions{..}))) (Stack, ver) = cmp' Stack instVer ver
|
||||
alreadyInstalling (Compile (CompileGHC GHCCompileOptions{ ovewrwiteVer = Just over }))
|
||||
(GHC, ver) = cmp' GHC (Just $ ToolVersion (mkTVer over)) ver
|
||||
alreadyInstalling (Compile (CompileGHC GHCCompileOptions{ targetGhc = Left tver }))
|
||||
(GHC, ver) = cmp' GHC (Just $ ToolVersion (mkTVer tver)) ver
|
||||
alreadyInstalling (Compile (CompileHLS HLSCompileOptions{ ovewrwiteVer = Just over }))
|
||||
(HLS, ver) = cmp' HLS (Just $ ToolVersion (mkTVer over)) ver
|
||||
alreadyInstalling (Compile (CompileHLS HLSCompileOptions{ targetHLS = Left tver }))
|
||||
(HLS, ver) = cmp' HLS (Just $ ToolVersion (mkTVer tver)) ver
|
||||
#ifndef DISABLE_UPGRADE
|
||||
alreadyInstalling (Upgrade _ _) (GHCup, _) = pure True
|
||||
#endif
|
||||
(GHC, ver) = cmp' GHC (Just $ GHCVersion (mkTVer over)) ver
|
||||
alreadyInstalling (Compile (CompileGHC GHCCompileOptions{ targetGhc = GHC.SourceDist tver }))
|
||||
(GHC, ver) = cmp' GHC (Just $ ToolVersion tver) ver
|
||||
alreadyInstalling (Compile (CompileHLS HLSCompileOptions{ ovewrwiteVer = Right over }))
|
||||
(HLS, ver) = cmp' HLS (Just $ ToolVersion over) ver
|
||||
alreadyInstalling (Compile (CompileHLS HLSCompileOptions{ targetHLS = HLS.SourceDist tver }))
|
||||
(HLS, ver) = cmp' HLS (Just $ ToolVersion tver) ver
|
||||
alreadyInstalling (Compile (CompileHLS HLSCompileOptions{ targetHLS = HLS.HackageDist tver }))
|
||||
(HLS, ver) = cmp' HLS (Just $ ToolVersion tver) ver
|
||||
alreadyInstalling (Upgrade {}) (GHCup, _) = pure True
|
||||
alreadyInstalling _ _ = pure False
|
||||
|
||||
cmp' :: ( HasLog env
|
||||
@@ -372,6 +371,7 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
-> Version
|
||||
-> Excepts
|
||||
'[ TagNotFound
|
||||
, DayNotFound
|
||||
, NextVerNotFound
|
||||
, NoToolVersionSet
|
||||
] m Bool
|
||||
|
||||
@@ -5,16 +5,9 @@ optional-packages: ./vendored/*/*.cabal
|
||||
optimization: 2
|
||||
|
||||
package ghcup
|
||||
tests: True
|
||||
flags: +tui
|
||||
|
||||
source-repository-package
|
||||
type: git
|
||||
location: https://github.com/bgamari/terminal-size.git
|
||||
tag: 34ea816bd63f75f800eedac12c6908c6f3736036
|
||||
|
||||
constraints: http-io-streams -brotli,
|
||||
any.Cabal ==3.6.2.0,
|
||||
any.aeson >= 2.0.1.0
|
||||
|
||||
package libarchive
|
||||
@@ -29,6 +22,8 @@ package cabal-plan
|
||||
package aeson
|
||||
flags: +ordered-keymap
|
||||
|
||||
allow-newer: base, ghc-prim, template-haskell, language-c
|
||||
package streamly
|
||||
flags: +use-unliftio
|
||||
|
||||
|
||||
with-compiler: ghc-8.10.7
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
active-repositories: hackage.haskell.org:merge
|
||||
constraints: any.Cabal ==3.6.2.0,
|
||||
constraints: any.Cabal ==3.6.3.0,
|
||||
Cabal -bundled-binary-generic,
|
||||
any.Cabal-syntax ==3.8.1.0,
|
||||
any.HUnit ==1.6.2.0,
|
||||
any.HsOpenSSL ==0.11.7.2,
|
||||
any.HsOpenSSL ==0.11.7.4,
|
||||
HsOpenSSL -fast-bignum -homebrew-openssl -macports-openssl -use-pkg-config,
|
||||
any.OneTuple ==0.3.1,
|
||||
any.QuickCheck ==2.14.2,
|
||||
@@ -10,13 +11,13 @@ constraints: any.Cabal ==3.6.2.0,
|
||||
any.StateVar ==1.2.2,
|
||||
any.abstract-deque ==0.3,
|
||||
abstract-deque -usecas,
|
||||
any.aeson ==2.0.3.0,
|
||||
any.aeson ==2.1.1.0,
|
||||
aeson -cffi +ordered-keymap,
|
||||
any.aeson-pretty ==0.8.9,
|
||||
aeson-pretty +lib-only,
|
||||
any.alex ==3.2.7.1,
|
||||
any.ansi-terminal ==0.11.1,
|
||||
ansi-terminal -example,
|
||||
any.ansi-terminal ==0.11.4,
|
||||
ansi-terminal -example +win32-2-13-1,
|
||||
any.ansi-wl-pprint ==0.6.9,
|
||||
ansi-wl-pprint -example,
|
||||
any.array ==0.5.4.0,
|
||||
@@ -28,23 +29,27 @@ constraints: any.Cabal ==3.6.2.0,
|
||||
any.attoparsec ==0.14.4,
|
||||
attoparsec -developer,
|
||||
any.base ==4.14.3.0,
|
||||
any.base-compat ==0.12.1,
|
||||
any.base-compat-batteries ==0.12.1,
|
||||
any.base-orphans ==0.8.6,
|
||||
any.base-compat ==0.12.2,
|
||||
any.base-compat-batteries ==0.12.2,
|
||||
any.base-orphans ==0.8.7,
|
||||
any.base16-bytestring ==1.0.2.0,
|
||||
any.base64-bytestring ==1.2.1.0,
|
||||
any.bifunctors ==5.5.11,
|
||||
any.bifunctors ==5.5.14,
|
||||
bifunctors +semigroups +tagged,
|
||||
any.bimap ==0.5.0,
|
||||
any.binary ==0.8.8.0,
|
||||
any.binary-instances ==1.0.3,
|
||||
any.binary-orphans ==1.0.3,
|
||||
any.blaze-builder ==0.4.2.2,
|
||||
any.brick ==0.64.2,
|
||||
any.brick ==1.5,
|
||||
brick -demos,
|
||||
any.bytestring ==0.10.12.0,
|
||||
any.bz2 ==1.0.1.0,
|
||||
bz2 -cross +with-bzlib,
|
||||
any.c2hs ==0.28.8,
|
||||
c2hs +base3 -regression,
|
||||
any.cabal-plan ==0.7.2.1,
|
||||
any.cabal-install-parsers ==0.5,
|
||||
any.cabal-plan ==0.7.2.3,
|
||||
cabal-plan -_ -exe -license-report,
|
||||
any.call-stack ==0.4.0,
|
||||
any.case-insensitive ==1.2.1.0,
|
||||
@@ -52,14 +57,12 @@ constraints: any.Cabal ==3.6.2.0,
|
||||
any.chs-cabal ==0.1.1.1,
|
||||
any.chs-deps ==0.1.0.0,
|
||||
chs-deps -cross,
|
||||
any.clock ==0.8.3,
|
||||
clock -llvm,
|
||||
any.colour ==2.3.6,
|
||||
any.comonad ==5.0.8,
|
||||
comonad +containers +distributive +indexed-traversable,
|
||||
any.composition-prelude ==3.0.0.2,
|
||||
composition-prelude -development,
|
||||
any.config-ini ==0.2.4.0,
|
||||
any.config-ini ==0.2.5.0,
|
||||
config-ini -enable-doctests,
|
||||
any.containers ==0.6.5.1,
|
||||
any.contravariant ==1.5.5,
|
||||
@@ -69,6 +72,7 @@ constraints: any.Cabal ==3.6.2.0,
|
||||
any.cryptohash-sha1 ==0.11.101.0,
|
||||
any.cryptohash-sha256 ==0.11.102.1,
|
||||
cryptohash-sha256 -exe +use-cbits,
|
||||
any.data-array-byte ==0.1.0.1,
|
||||
any.data-clist ==0.2,
|
||||
any.data-fix ==0.3.2,
|
||||
any.deepseq ==1.4.4.0,
|
||||
@@ -80,93 +84,91 @@ constraints: any.Cabal ==3.6.2.0,
|
||||
dlist -werror,
|
||||
any.exceptions ==0.10.4,
|
||||
any.filepath ==1.4.2.1,
|
||||
any.free ==5.1.7,
|
||||
any.free ==5.1.10,
|
||||
any.fusion-plugin-types ==0.1.0,
|
||||
any.generic-arbitrary ==0.2.0,
|
||||
any.ghc ==8.10.7,
|
||||
any.ghc-boot ==8.10.7,
|
||||
any.generic-arbitrary ==0.2.2,
|
||||
any.generically ==0.1,
|
||||
any.ghc-boot-th ==8.10.7,
|
||||
any.ghc-byteorder ==4.11.0.0.10,
|
||||
any.ghc-heap ==8.10.7,
|
||||
any.ghc-prim ==0.6.1,
|
||||
any.ghci ==8.10.7,
|
||||
any.happy ==1.20.0,
|
||||
any.hashable ==1.4.0.2,
|
||||
hashable +containers +integer-gmp -random-initial-seed,
|
||||
any.hashable ==1.4.2.0,
|
||||
hashable +integer-gmp -random-initial-seed,
|
||||
any.haskell-lexer ==1.1.1,
|
||||
any.haskus-utils-data ==1.4,
|
||||
any.haskus-utils-types ==1.5.1,
|
||||
any.haskus-utils-variant ==3.2.1,
|
||||
any.heaps ==0.4,
|
||||
any.hpc ==0.6.1.0,
|
||||
any.hsc2hs ==0.68.8,
|
||||
hsc2hs -in-ghc-tree,
|
||||
any.hspec ==2.9.4,
|
||||
any.hspec-core ==2.9.4,
|
||||
any.hspec-discover ==2.9.4,
|
||||
any.hspec ==2.10.8,
|
||||
any.hspec-core ==2.10.8,
|
||||
any.hspec-discover ==2.10.8,
|
||||
any.hspec-expectations ==0.8.2,
|
||||
any.hspec-golden-aeson ==0.9.0.0,
|
||||
any.http-io-streams ==0.1.6.0,
|
||||
any.http-io-streams ==0.1.6.1,
|
||||
http-io-streams -brotli +fast-xor,
|
||||
any.indexed-profunctors ==0.1.1,
|
||||
any.indexed-traversable ==0.1.2,
|
||||
any.indexed-traversable-instances ==0.1.1,
|
||||
any.indexed-traversable-instances ==0.1.1.1,
|
||||
any.integer-gmp ==1.0.3.0,
|
||||
any.integer-logarithms ==1.0.3.1,
|
||||
integer-logarithms -check-bounds +integer-gmp,
|
||||
any.io-streams ==1.5.2.1,
|
||||
any.io-streams ==1.5.2.2,
|
||||
io-streams +network -nointeractivetests +zlib,
|
||||
any.language-c ==0.9.0.1,
|
||||
any.language-c ==0.9.2,
|
||||
language-c -allwarnings +iecfpextension +usebytestrings,
|
||||
any.libarchive ==3.0.3.2,
|
||||
libarchive -cross -low-memory +no-exe -system-libarchive,
|
||||
any.libyaml-streamly ==0.2.1,
|
||||
libyaml-streamly -no-unicode -system-libyaml,
|
||||
any.lockfree-queue ==0.2.3.1,
|
||||
any.lzma-static ==5.2.5.4,
|
||||
any.megaparsec ==9.2.0,
|
||||
any.lockfree-queue ==0.2.4,
|
||||
any.lukko ==0.1.1.3,
|
||||
lukko +ofd-locking,
|
||||
any.lzma-static ==5.2.5.5,
|
||||
any.megaparsec ==9.2.1,
|
||||
megaparsec -dev,
|
||||
any.microlens ==0.4.12.0,
|
||||
any.microlens-mtl ==0.2.0.1,
|
||||
any.microlens-th ==0.4.3.10,
|
||||
any.monad-control ==1.0.3.1,
|
||||
any.microlens ==0.4.13.1,
|
||||
any.microlens-mtl ==0.2.0.3,
|
||||
any.microlens-th ==0.4.3.11,
|
||||
any.mtl ==2.2.2,
|
||||
any.network ==3.1.2.7,
|
||||
network -devel,
|
||||
any.network-uri ==2.6.4.1,
|
||||
any.network-uri ==2.6.4.2,
|
||||
any.openssl-streams ==1.2.3.0,
|
||||
any.optics ==0.4,
|
||||
any.optics-core ==0.4,
|
||||
any.optics ==0.4.2,
|
||||
any.optics-core ==0.4.1,
|
||||
optics-core -explicit-generic-labels,
|
||||
any.optics-extra ==0.4,
|
||||
any.optics-th ==0.4,
|
||||
any.optics-extra ==0.4.2.1,
|
||||
any.optics-th ==0.4.1,
|
||||
any.optparse-applicative ==0.17.0.0,
|
||||
optparse-applicative +process,
|
||||
any.os-release ==1.0.2.1,
|
||||
os-release -devel,
|
||||
any.parallel ==3.2.2.0,
|
||||
any.parsec ==3.1.14.0,
|
||||
any.parsec ==3.1.16.1,
|
||||
any.parser-combinators ==1.3.0,
|
||||
parser-combinators -dev,
|
||||
any.polyparse ==1.13,
|
||||
any.pretty ==1.1.3.6,
|
||||
any.pretty-terminal ==0.1.0.0,
|
||||
any.primitive ==0.7.3.0,
|
||||
any.primitive ==0.7.4.0,
|
||||
any.process ==1.6.13.2,
|
||||
any.profunctors ==5.6.2,
|
||||
any.quickcheck-arbitrary-adt ==0.3.1.0,
|
||||
any.quickcheck-io ==0.2.0,
|
||||
any.random ==1.2.1,
|
||||
any.recursion-schemes ==5.2.2.2,
|
||||
any.random ==1.2.1.1,
|
||||
any.recursion-schemes ==5.2.2.3,
|
||||
recursion-schemes +template-haskell,
|
||||
any.regex-base ==0.94.0.2,
|
||||
any.regex-posix ==0.96.0.1,
|
||||
regex-posix -_regex-posix-clib,
|
||||
any.resourcet ==1.2.4.3,
|
||||
any.resourcet ==1.2.6,
|
||||
any.retry ==0.8.1.2,
|
||||
retry -lib-werror,
|
||||
any.rts ==1.0.1,
|
||||
any.safe ==0.3.19,
|
||||
any.safe-exceptions ==0.1.7.2,
|
||||
any.safe-exceptions ==0.1.7.3,
|
||||
any.scientific ==0.3.7.0,
|
||||
scientific -bytestring-builder -integer-simple,
|
||||
any.semialign ==1.2.0.1,
|
||||
@@ -174,32 +176,36 @@ constraints: any.Cabal ==3.6.2.0,
|
||||
any.semigroupoids ==5.3.7,
|
||||
semigroupoids +comonad +containers +contravariant +distributive +tagged +unordered-containers,
|
||||
any.setenv ==0.1.1.3,
|
||||
any.split ==0.2.3.4,
|
||||
any.split ==0.2.3.5,
|
||||
any.splitmix ==0.1.0.4,
|
||||
splitmix -optimised-mixer,
|
||||
any.stm ==2.5.0.1,
|
||||
any.streamly ==0.8.2,
|
||||
streamly -debug -dev -fusion-plugin -has-llvm -inspection -limit-build-mem -no-fusion +opt -streamk -streamly-core -use-c-malloc -use-unliftio,
|
||||
any.streamly ==0.8.3,
|
||||
streamly -debug -dev -fusion-plugin -has-llvm -inspection -limit-build-mem -no-fusion +opt -streamk -streamly-core -use-c-malloc +use-unliftio,
|
||||
any.strict ==0.4.0.1,
|
||||
strict +assoc,
|
||||
any.strict-base ==0.4.0.0,
|
||||
any.tagged ==0.8.6.1,
|
||||
tagged +deepseq +transformers,
|
||||
any.tagsoup ==0.14.8,
|
||||
any.tar ==0.5.1.1,
|
||||
tar -old-bytestring -old-time,
|
||||
any.template-haskell ==2.16.0.0,
|
||||
any.temporary ==1.3,
|
||||
any.terminal-progress-bar ==0.4.1,
|
||||
any.terminal-size ==0.3.2.1,
|
||||
any.terminal-size ==0.3.3,
|
||||
any.terminfo ==0.4.1.4,
|
||||
any.text ==1.2.4.1,
|
||||
any.text ==2.0.1,
|
||||
text -developer +simdutf,
|
||||
any.text-binary ==0.2.1.1,
|
||||
any.text-short ==0.1.5,
|
||||
text-short -asserts,
|
||||
any.text-zipper ==0.11,
|
||||
any.text-zipper ==0.12,
|
||||
any.tf-random ==0.5,
|
||||
any.th-abstraction ==0.4.3.0,
|
||||
any.th-compat ==0.1.3,
|
||||
any.th-abstraction ==0.4.5.0,
|
||||
any.th-compat ==0.1.4,
|
||||
any.th-lift ==0.8.2,
|
||||
any.th-lift-instances ==0.1.19,
|
||||
any.th-lift-instances ==0.1.20,
|
||||
any.these ==1.1.1.1,
|
||||
these +assoc,
|
||||
any.time ==1.9.3,
|
||||
@@ -208,16 +214,16 @@ constraints: any.Cabal ==3.6.2.0,
|
||||
any.transformers ==0.5.6.2,
|
||||
any.transformers-base ==0.4.6,
|
||||
transformers-base +orphaninstances,
|
||||
any.transformers-compat ==0.7.1,
|
||||
any.transformers-compat ==0.7.2,
|
||||
transformers-compat -five +five-three -four +generic-deriving +mtl -three -two,
|
||||
any.unicode-data ==0.3.0,
|
||||
any.unicode-data ==0.3.1,
|
||||
unicode-data -ucd2haskell,
|
||||
any.unix ==2.7.2.2,
|
||||
any.unix-bytestring ==0.3.7.6,
|
||||
any.unix-compat ==0.5.4,
|
||||
any.unix-bytestring ==0.3.7.8,
|
||||
any.unix-compat ==0.6,
|
||||
unix-compat -old-time,
|
||||
any.unliftio-core ==0.2.0.1,
|
||||
any.unordered-containers ==0.2.17.0,
|
||||
any.unordered-containers ==0.2.19.1,
|
||||
unordered-containers -debug,
|
||||
any.uri-bytestring ==0.3.3.1,
|
||||
uri-bytestring -lib-werror,
|
||||
@@ -225,15 +231,16 @@ constraints: any.Cabal ==3.6.2.0,
|
||||
any.uuid-types ==1.0.5,
|
||||
any.vector ==0.12.3.1,
|
||||
vector +boundschecks -internalchecks -unsafechecks -wall,
|
||||
any.versions ==5.0.3,
|
||||
any.vty ==5.33,
|
||||
any.vector-binary-instances ==0.2.5.2,
|
||||
any.versions ==5.0.4,
|
||||
any.vty ==5.37,
|
||||
any.witherable ==0.4.2,
|
||||
any.word-wrap ==0.5,
|
||||
any.word8 ==0.1.3,
|
||||
any.xor ==0.0.1.1,
|
||||
any.yaml-streamly ==0.12.1,
|
||||
yaml-streamly +no-examples +no-exe,
|
||||
any.zlib ==0.6.2.3,
|
||||
any.zlib ==0.6.3.0,
|
||||
zlib -bundled-c-zlib -non-blocking-ffi -pkg-config,
|
||||
any.zlib-bindings ==0.1.1.5
|
||||
index-state: hackage.haskell.org 2022-03-15T16:43:02Z
|
||||
index-state: hackage.haskell.org 2023-01-12T04:22:48Z
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
packages: ./ghcup.cabal
|
||||
|
||||
optional-packages: ./vendored/*/*.cabal
|
||||
|
||||
optimization: 2
|
||||
|
||||
package ghcup
|
||||
tests: True
|
||||
flags: +tui
|
||||
|
||||
source-repository-package
|
||||
type: git
|
||||
location: https://github.com/bgamari/terminal-size.git
|
||||
tag: 34ea816bd63f75f800eedac12c6908c6f3736036
|
||||
|
||||
constraints: http-io-streams -brotli,
|
||||
any.Cabal ==3.6.2.0,
|
||||
any.aeson >= 2.0.1.0
|
||||
|
||||
package libarchive
|
||||
flags: -system-libarchive
|
||||
|
||||
package aeson-pretty
|
||||
flags: +lib-only
|
||||
|
||||
package cabal-plan
|
||||
flags: -exe
|
||||
|
||||
package aeson
|
||||
flags: +ordered-keymap
|
||||
|
||||
allow-newer: base, ghc-prim, template-haskell, language-c
|
||||
|
||||
with-compiler: ghc-9.0.2
|
||||
@@ -1,239 +0,0 @@
|
||||
active-repositories: hackage.haskell.org:merge
|
||||
constraints: any.Cabal ==3.6.2.0,
|
||||
Cabal -bundled-binary-generic,
|
||||
any.HUnit ==1.6.2.0,
|
||||
any.HsOpenSSL ==0.11.7.2,
|
||||
HsOpenSSL -fast-bignum -homebrew-openssl -macports-openssl -use-pkg-config,
|
||||
any.OneTuple ==0.3.1,
|
||||
any.QuickCheck ==2.14.2,
|
||||
QuickCheck -old-random +templatehaskell,
|
||||
any.StateVar ==1.2.2,
|
||||
any.abstract-deque ==0.3,
|
||||
abstract-deque -usecas,
|
||||
any.aeson ==2.0.3.0,
|
||||
aeson -cffi +ordered-keymap,
|
||||
any.aeson-pretty ==0.8.9,
|
||||
aeson-pretty +lib-only,
|
||||
any.alex ==3.2.7.1,
|
||||
any.ansi-terminal ==0.11.1,
|
||||
ansi-terminal -example,
|
||||
any.ansi-wl-pprint ==0.6.9,
|
||||
ansi-wl-pprint -example,
|
||||
any.array ==0.5.4.0,
|
||||
any.assoc ==1.0.2,
|
||||
any.async ==2.2.4,
|
||||
async -bench,
|
||||
any.atomic-primops ==0.8.4,
|
||||
atomic-primops -debug,
|
||||
any.attoparsec ==0.14.4,
|
||||
attoparsec -developer,
|
||||
any.base ==4.15.1.0,
|
||||
any.base-compat ==0.12.1,
|
||||
any.base-compat-batteries ==0.12.1,
|
||||
any.base-orphans ==0.8.6,
|
||||
any.base16-bytestring ==1.0.2.0,
|
||||
any.base64-bytestring ==1.2.1.0,
|
||||
any.bifunctors ==5.5.11,
|
||||
bifunctors +semigroups +tagged,
|
||||
any.binary ==0.8.8.0,
|
||||
any.blaze-builder ==0.4.2.2,
|
||||
any.brick ==0.64.2,
|
||||
brick -demos,
|
||||
any.bytestring ==0.10.12.1,
|
||||
any.bz2 ==1.0.1.0,
|
||||
bz2 -cross +with-bzlib,
|
||||
any.c2hs ==0.28.8,
|
||||
c2hs +base3 -regression,
|
||||
any.cabal-plan ==0.7.2.1,
|
||||
cabal-plan -_ -exe -license-report,
|
||||
any.call-stack ==0.4.0,
|
||||
any.case-insensitive ==1.2.1.0,
|
||||
any.casing ==0.1.4.1,
|
||||
any.chs-cabal ==0.1.1.1,
|
||||
any.chs-deps ==0.1.0.0,
|
||||
chs-deps -cross,
|
||||
any.clock ==0.8.3,
|
||||
clock -llvm,
|
||||
any.colour ==2.3.6,
|
||||
any.comonad ==5.0.8,
|
||||
comonad +containers +distributive +indexed-traversable,
|
||||
any.composition-prelude ==3.0.0.2,
|
||||
composition-prelude -development,
|
||||
any.config-ini ==0.2.4.0,
|
||||
config-ini -enable-doctests,
|
||||
any.containers ==0.6.4.1,
|
||||
any.contravariant ==1.5.5,
|
||||
contravariant +semigroups +statevar +tagged,
|
||||
any.cpphs ==1.20.9.1,
|
||||
cpphs -old-locale,
|
||||
any.cryptohash-sha1 ==0.11.101.0,
|
||||
any.cryptohash-sha256 ==0.11.102.1,
|
||||
cryptohash-sha256 -exe +use-cbits,
|
||||
any.data-clist ==0.2,
|
||||
any.data-fix ==0.3.2,
|
||||
any.deepseq ==1.4.5.0,
|
||||
any.directory ==1.3.6.2,
|
||||
any.disk-free-space ==0.1.0.1,
|
||||
any.distributive ==0.6.2.1,
|
||||
distributive +semigroups +tagged,
|
||||
any.dlist ==1.0,
|
||||
dlist -werror,
|
||||
any.exceptions ==0.10.4,
|
||||
any.filepath ==1.4.2.1,
|
||||
any.free ==5.1.7,
|
||||
any.fusion-plugin-types ==0.1.0,
|
||||
any.generic-arbitrary ==0.2.0,
|
||||
any.ghc ==9.0.2,
|
||||
any.ghc-bignum ==1.1,
|
||||
any.ghc-boot ==9.0.2,
|
||||
any.ghc-boot-th ==9.0.2,
|
||||
any.ghc-byteorder ==4.11.0.0.10,
|
||||
any.ghc-heap ==9.0.2,
|
||||
any.ghc-prim ==0.7.0,
|
||||
any.ghci ==9.0.2,
|
||||
any.happy ==1.20.0,
|
||||
any.hashable ==1.4.0.2,
|
||||
hashable +containers +integer-gmp -random-initial-seed,
|
||||
any.haskus-utils-data ==1.4,
|
||||
any.haskus-utils-types ==1.5.1,
|
||||
any.haskus-utils-variant ==3.2.1,
|
||||
any.heaps ==0.4,
|
||||
any.hpc ==0.6.1.0,
|
||||
any.hsc2hs ==0.68.8,
|
||||
hsc2hs -in-ghc-tree,
|
||||
any.hspec ==2.9.4,
|
||||
any.hspec-core ==2.9.4,
|
||||
any.hspec-discover ==2.9.4,
|
||||
any.hspec-expectations ==0.8.2,
|
||||
any.hspec-golden-aeson ==0.9.0.0,
|
||||
any.http-io-streams ==0.1.6.0,
|
||||
http-io-streams -brotli +fast-xor,
|
||||
any.indexed-profunctors ==0.1.1,
|
||||
any.indexed-traversable ==0.1.2,
|
||||
any.indexed-traversable-instances ==0.1.1,
|
||||
any.integer-logarithms ==1.0.3.1,
|
||||
integer-logarithms -check-bounds +integer-gmp,
|
||||
any.io-streams ==1.5.2.1,
|
||||
io-streams +network -nointeractivetests +zlib,
|
||||
any.language-c ==0.9.0.1,
|
||||
language-c -allwarnings +iecfpextension +usebytestrings,
|
||||
any.libarchive ==3.0.3.2,
|
||||
libarchive -cross -low-memory +no-exe -system-libarchive,
|
||||
any.libyaml-streamly ==0.2.1,
|
||||
libyaml-streamly -no-unicode -system-libyaml,
|
||||
any.lockfree-queue ==0.2.3.1,
|
||||
any.lzma-static ==5.2.5.4,
|
||||
any.megaparsec ==9.2.0,
|
||||
megaparsec -dev,
|
||||
any.microlens ==0.4.12.0,
|
||||
any.microlens-mtl ==0.2.0.1,
|
||||
any.microlens-th ==0.4.3.10,
|
||||
any.monad-control ==1.0.3.1,
|
||||
any.mtl ==2.2.2,
|
||||
any.network ==3.1.2.7,
|
||||
network -devel,
|
||||
any.network-uri ==2.6.4.1,
|
||||
any.openssl-streams ==1.2.3.0,
|
||||
any.optics ==0.4,
|
||||
any.optics-core ==0.4,
|
||||
optics-core -explicit-generic-labels,
|
||||
any.optics-extra ==0.4,
|
||||
any.optics-th ==0.4,
|
||||
any.optparse-applicative ==0.17.0.0,
|
||||
optparse-applicative +process,
|
||||
any.os-release ==1.0.2.1,
|
||||
os-release -devel,
|
||||
any.parallel ==3.2.2.0,
|
||||
any.parsec ==3.1.14.0,
|
||||
any.parser-combinators ==1.3.0,
|
||||
parser-combinators -dev,
|
||||
any.polyparse ==1.13,
|
||||
any.pretty ==1.1.3.6,
|
||||
any.pretty-terminal ==0.1.0.0,
|
||||
any.primitive ==0.7.3.0,
|
||||
any.process ==1.6.13.2,
|
||||
any.profunctors ==5.6.2,
|
||||
any.quickcheck-arbitrary-adt ==0.3.1.0,
|
||||
any.quickcheck-io ==0.2.0,
|
||||
any.random ==1.2.1,
|
||||
any.recursion-schemes ==5.2.2.2,
|
||||
recursion-schemes +template-haskell,
|
||||
any.regex-base ==0.94.0.2,
|
||||
any.regex-posix ==0.96.0.1,
|
||||
regex-posix -_regex-posix-clib,
|
||||
any.resourcet ==1.2.4.3,
|
||||
any.retry ==0.8.1.2,
|
||||
retry -lib-werror,
|
||||
any.rts ==1.0.2,
|
||||
any.safe ==0.3.19,
|
||||
any.safe-exceptions ==0.1.7.2,
|
||||
any.scientific ==0.3.7.0,
|
||||
scientific -bytestring-builder -integer-simple,
|
||||
any.semialign ==1.2.0.1,
|
||||
semialign +semigroupoids,
|
||||
any.semigroupoids ==5.3.7,
|
||||
semigroupoids +comonad +containers +contravariant +distributive +tagged +unordered-containers,
|
||||
any.setenv ==0.1.1.3,
|
||||
any.split ==0.2.3.4,
|
||||
any.splitmix ==0.1.0.4,
|
||||
splitmix -optimised-mixer,
|
||||
any.stm ==2.5.0.0,
|
||||
any.streamly ==0.8.2,
|
||||
streamly -debug -dev -fusion-plugin -has-llvm -inspection -limit-build-mem -no-fusion +opt -streamk -streamly-core -use-c-malloc -use-unliftio,
|
||||
any.strict ==0.4.0.1,
|
||||
strict +assoc,
|
||||
any.strict-base ==0.4.0.0,
|
||||
any.tagged ==0.8.6.1,
|
||||
tagged +deepseq +transformers,
|
||||
any.tagsoup ==0.14.8,
|
||||
any.template-haskell ==2.17.0.0,
|
||||
any.temporary ==1.3,
|
||||
any.terminal-progress-bar ==0.4.1,
|
||||
any.terminal-size ==0.3.2.1,
|
||||
any.terminfo ==0.4.1.5,
|
||||
any.text ==1.2.5.0,
|
||||
any.text-short ==0.1.5,
|
||||
text-short -asserts,
|
||||
any.text-zipper ==0.11,
|
||||
any.tf-random ==0.5,
|
||||
any.th-abstraction ==0.4.3.0,
|
||||
any.th-compat ==0.1.3,
|
||||
any.th-lift ==0.8.2,
|
||||
any.th-lift-instances ==0.1.19,
|
||||
any.these ==1.1.1.1,
|
||||
these +assoc,
|
||||
any.time ==1.9.3,
|
||||
any.time-compat ==1.9.6.1,
|
||||
time-compat -old-locale,
|
||||
any.transformers ==0.5.6.2,
|
||||
any.transformers-base ==0.4.6,
|
||||
transformers-base +orphaninstances,
|
||||
any.transformers-compat ==0.7.1,
|
||||
transformers-compat -five +five-three -four +generic-deriving +mtl -three -two,
|
||||
any.unicode-data ==0.3.0,
|
||||
unicode-data -ucd2haskell,
|
||||
any.unix ==2.7.2.2,
|
||||
any.unix-bytestring ==0.3.7.6,
|
||||
any.unix-compat ==0.5.4,
|
||||
unix-compat -old-time,
|
||||
any.unliftio-core ==0.2.0.1,
|
||||
any.unordered-containers ==0.2.17.0,
|
||||
unordered-containers -debug,
|
||||
any.uri-bytestring ==0.3.3.1,
|
||||
uri-bytestring -lib-werror,
|
||||
any.utf8-string ==1.0.2,
|
||||
any.uuid-types ==1.0.5,
|
||||
any.vector ==0.12.3.1,
|
||||
vector +boundschecks -internalchecks -unsafechecks -wall,
|
||||
any.versions ==5.0.3,
|
||||
any.vty ==5.33,
|
||||
any.witherable ==0.4.2,
|
||||
any.word-wrap ==0.5,
|
||||
any.word8 ==0.1.3,
|
||||
any.xor ==0.0.1.1,
|
||||
any.yaml-streamly ==0.12.1,
|
||||
yaml-streamly +no-examples +no-exe,
|
||||
any.zlib ==0.6.2.3,
|
||||
zlib -bundled-c-zlib -non-blocking-ffi -pkg-config,
|
||||
any.zlib-bindings ==0.1.1.5
|
||||
index-state: hackage.haskell.org 2022-03-15T16:43:02Z
|
||||
@@ -2,22 +2,11 @@ packages: ./ghcup.cabal
|
||||
|
||||
optional-packages: ./vendored/*/*.cabal
|
||||
|
||||
optimization: 2
|
||||
|
||||
package ghcup
|
||||
tests: True
|
||||
flags: +tui
|
||||
|
||||
source-repository-package
|
||||
type: git
|
||||
location: https://github.com/bgamari/terminal-size.git
|
||||
tag: 34ea816bd63f75f800eedac12c6908c6f3736036
|
||||
|
||||
constraints: http-io-streams -brotli,
|
||||
any.Cabal ==3.6.2.0,
|
||||
any.aeson >= 2.0.1.0,
|
||||
-- https://github.com/typeable/generic-arbitrary/issues/14
|
||||
any.generic-arbitrary < 0.2.1
|
||||
any.aeson >= 2.0.1.0
|
||||
|
||||
package libarchive
|
||||
flags: -system-libarchive
|
||||
@@ -31,4 +20,6 @@ package cabal-plan
|
||||
package aeson
|
||||
flags: +ordered-keymap
|
||||
|
||||
allow-newer: base, ghc-prim, template-haskell, language-c
|
||||
package streamly
|
||||
flags: +use-unliftio
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
-- windows picks weird version
|
||||
constraints: any.hsc2hs ==0.68.7
|
||||
constraints: any.hsc2hs ==0.68.8
|
||||
|
||||
50
cabal.project.release
Normal file
50
cabal.project.release
Normal file
@@ -0,0 +1,50 @@
|
||||
packages: ./ghcup.cabal
|
||||
|
||||
optional-packages: ./vendored/*/*.cabal
|
||||
|
||||
optimization: 2
|
||||
|
||||
if os(linux)
|
||||
package ghcup
|
||||
flags: +tui
|
||||
if arch(x86_64) || arch(i386)
|
||||
package *
|
||||
ghc-options: -split-sections -optl-static
|
||||
elif os(darwin)
|
||||
constraints: zlib +bundled-c-zlib,
|
||||
lzma +static
|
||||
package ghcup
|
||||
flags: +tui
|
||||
elif os(mingw32)
|
||||
constraints: zlib +bundled-c-zlib,
|
||||
lzma +static,
|
||||
text -simdutf
|
||||
package ghcup
|
||||
flags: -tui
|
||||
elif os(freebsd)
|
||||
constraints: zlib +bundled-c-zlib,
|
||||
zip +disable-zstd
|
||||
package *
|
||||
ghc-options: -split-sections -pgmc clang++14
|
||||
package ghcup
|
||||
flags: +tui
|
||||
|
||||
constraints: http-io-streams -brotli,
|
||||
any.aeson >= 2.0.1.0,
|
||||
any.hsc2hs ==0.68.8
|
||||
|
||||
package libarchive
|
||||
flags: -system-libarchive
|
||||
|
||||
package aeson-pretty
|
||||
flags: +lib-only
|
||||
|
||||
package cabal-plan
|
||||
flags: -exe
|
||||
|
||||
package aeson
|
||||
flags: +ordered-keymap
|
||||
|
||||
package streamly
|
||||
flags: +use-unliftio
|
||||
|
||||
7
cbits/dirutils.c
Normal file
7
cbits/dirutils.c
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "dirutils.h"
|
||||
|
||||
unsigned int
|
||||
__posixdir_d_type(struct dirent* d)
|
||||
{
|
||||
return(d -> d_type);
|
||||
}
|
||||
16
cbits/dirutils.h
Normal file
16
cbits/dirutils.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef POSIXPATHS_CBITS_DIRUTILS_H
|
||||
#define POSIXPATHS_CBITS_DIRUTILS_H
|
||||
|
||||
#include <HsFFI.h>
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
|
||||
extern unsigned int
|
||||
__posixdir_d_type(struct dirent* d)
|
||||
;
|
||||
|
||||
#endif
|
||||
@@ -40,6 +40,12 @@ key-bindings:
|
||||
# of the file. These usually are in '~/.ghcup/cache/ghcup-<ver>.yaml'.
|
||||
meta-cache: 300 # in seconds
|
||||
|
||||
# When trying to download ghcup metadata, this option decides what to do
|
||||
# when the download fails:
|
||||
# 1. Lax: use existing ~/.ghcup/cache/ghcup-<ver>.yaml as fallback (default)
|
||||
# 2. Strict: fail hard
|
||||
meta-mode: Lax # Strict | Lax
|
||||
|
||||
# Where to get GHC/cabal/hls download info/versions from. For more detailed explanation
|
||||
# check the 'URLSource' type in the code.
|
||||
url-source:
|
||||
@@ -75,3 +81,41 @@ url-source:
|
||||
# AddSource:
|
||||
# - Right: "file:///home/jule/git/ghcup-hs/ghcup-prereleases.yaml"
|
||||
# - Right: "file:///home/jule/git/ghcup-hs/ghcup-custom.yaml"
|
||||
|
||||
# This is a way to override platform detection, e.g. when you're running
|
||||
# a Ubuntu derivate based on 18.04, you could do:
|
||||
#
|
||||
# platform-override:
|
||||
# arch: A_64
|
||||
# platform:
|
||||
# contents: Ubuntu
|
||||
# tag: Linux
|
||||
# version: '18.04'
|
||||
platform-override: null
|
||||
|
||||
# Support for mirrors. Currently there are 3 hosts you can mirror:
|
||||
# - github.com (for stack and some older HLS versions)
|
||||
# - raw.githubusercontent.com (for the yaml metadata)
|
||||
# - downloads.haskell.org (for everything else)
|
||||
#
|
||||
# E.g. when we have 'https://raw.githubusercontent.com/haskell/ghcup-metadata/master/ghcup-0.0.7.yaml'
|
||||
# and the following mirror config
|
||||
#
|
||||
# "raw.githubusercontent.com":
|
||||
# authority:
|
||||
# host: "mirror.sjtu.edu.cn"
|
||||
# pathPrefix: "ghcup/yaml"
|
||||
#
|
||||
# Then the resulting url will be 'https://mirror.sjtu.edu.cn/ghcup/yaml/haskell/ghcup-metadata/master/ghcup-0.0.7.yaml'
|
||||
mirrors:
|
||||
"github.com":
|
||||
authority:
|
||||
host: "mirror.sjtu.edu.cn"
|
||||
"raw.githubusercontent.com":
|
||||
authority:
|
||||
host: "mirror.sjtu.edu.cn"
|
||||
pathPrefix: "ghcup/yaml"
|
||||
"downloads.haskell.org":
|
||||
authority:
|
||||
host: "mirror.sjtu.edu.cn"
|
||||
|
||||
|
||||
Submodule data/metadata updated: 6fae2f7bc2...92efb732ac
71
docker/alpine32/Dockerfile
Normal file
71
docker/alpine32/Dockerfile
Normal file
@@ -0,0 +1,71 @@
|
||||
FROM --platform=linux/i386 i386/alpine:3.12
|
||||
|
||||
ENV LANG C.UTF-8
|
||||
|
||||
RUN apk add --no-cache \
|
||||
curl \
|
||||
gcc \
|
||||
g++ \
|
||||
binutils \
|
||||
binutils-gold \
|
||||
coreutils \
|
||||
bsd-compat-headers \
|
||||
gmp-dev \
|
||||
ncurses-dev \
|
||||
libffi-dev \
|
||||
make \
|
||||
xz \
|
||||
tar \
|
||||
perl \
|
||||
bash \
|
||||
diffutils \
|
||||
git \
|
||||
gzip \
|
||||
gnupg && \
|
||||
apk add --no-cache \
|
||||
zlib \
|
||||
zlib-dev \
|
||||
zlib-static \
|
||||
bzip2 \
|
||||
bzip2-dev \
|
||||
bzip2-static \
|
||||
gmp \
|
||||
gmp-dev \
|
||||
openssl-dev \
|
||||
openssl-libs-static \
|
||||
xz \
|
||||
xz-dev \
|
||||
ncurses-static
|
||||
|
||||
ARG GHCUP_VERSION=0.1.18.0
|
||||
ARG GPG_KEY=7784930957807690A66EBDBE3786C5262ECB4A3F
|
||||
|
||||
# install ghcup
|
||||
RUN gpg --batch --keyserver keys.openpgp.org --recv-keys $GPG_KEY && \
|
||||
curl -sSfL -O https://downloads.haskell.org/~ghcup/$GHCUP_VERSION/i386-linux-ghcup-$GHCUP_VERSION && \
|
||||
curl -sSfL -O https://downloads.haskell.org/~ghcup/$GHCUP_VERSION/SHA256SUMS && \
|
||||
curl -sSfL -O https://downloads.haskell.org/~ghcup/$GHCUP_VERSION/SHA256SUMS.sig && \
|
||||
gpg --verify SHA256SUMS.sig SHA256SUMS && \
|
||||
sha256sum -c --ignore-missing SHA256SUMS && \
|
||||
mv i386-linux-ghcup-$GHCUP_VERSION /usr/bin/ghcup && \
|
||||
chmod +x /usr/bin/ghcup && \
|
||||
rm -rf SHA256SUMS SHA256SUMS.sig
|
||||
|
||||
ARG GHC=8.10.7
|
||||
ARG CABAL_INSTALL=3.6.2.0
|
||||
ARG STACK=2.9.1
|
||||
|
||||
ENV GHCUP_CURL_OPTS="--silent"
|
||||
ENV NO_COLOR=1
|
||||
|
||||
# install haskell toolchain
|
||||
RUN ghcup config set gpg-setting GPGStrict && \
|
||||
ghcup --verbose install ghc --isolate=/usr --force ${GHC} && \
|
||||
ghcup --verbose install cabal --isolate=/usr/bin --force ${CABAL_INSTALL} && \
|
||||
find "/usr/lib/ghc-${GHC}/" \( -name "*_p.a" -o -name "*.p_hi" \) -type f -delete && \
|
||||
rm -r "/usr/share/doc/ghc-${GHC}" && \
|
||||
rm -rf /tmp/ghcup* && \
|
||||
ghcup gc -p -s -c -t
|
||||
|
||||
ENV PATH /root/.cabal/bin:/root/.ghcup:/root/.local/bin:$PATH
|
||||
|
||||
71
docker/alpine64/Dockerfile
Normal file
71
docker/alpine64/Dockerfile
Normal file
@@ -0,0 +1,71 @@
|
||||
FROM alpine:3.12
|
||||
|
||||
ENV LANG C.UTF-8
|
||||
|
||||
RUN apk add --no-cache \
|
||||
curl \
|
||||
gcc \
|
||||
g++ \
|
||||
binutils \
|
||||
binutils-gold \
|
||||
coreutils \
|
||||
bsd-compat-headers \
|
||||
gmp-dev \
|
||||
ncurses-dev \
|
||||
libffi-dev \
|
||||
make \
|
||||
xz \
|
||||
tar \
|
||||
perl \
|
||||
bash \
|
||||
diffutils \
|
||||
git \
|
||||
gzip \
|
||||
gnupg && \
|
||||
apk add --no-cache \
|
||||
zlib \
|
||||
zlib-dev \
|
||||
zlib-static \
|
||||
bzip2 \
|
||||
bzip2-dev \
|
||||
bzip2-static \
|
||||
gmp \
|
||||
gmp-dev \
|
||||
openssl-dev \
|
||||
openssl-libs-static \
|
||||
xz \
|
||||
xz-dev \
|
||||
ncurses-static
|
||||
|
||||
ARG GHCUP_VERSION=0.1.18.0
|
||||
ARG GPG_KEY=7784930957807690A66EBDBE3786C5262ECB4A3F
|
||||
|
||||
# install ghcup
|
||||
RUN gpg --batch --keyserver keys.openpgp.org --recv-keys $GPG_KEY && \
|
||||
curl -sSfL -O https://downloads.haskell.org/~ghcup/$GHCUP_VERSION/x86_64-linux-ghcup-$GHCUP_VERSION && \
|
||||
curl -sSfL -O https://downloads.haskell.org/~ghcup/$GHCUP_VERSION/SHA256SUMS && \
|
||||
curl -sSfL -O https://downloads.haskell.org/~ghcup/$GHCUP_VERSION/SHA256SUMS.sig && \
|
||||
gpg --verify SHA256SUMS.sig SHA256SUMS && \
|
||||
sha256sum -c --ignore-missing SHA256SUMS && \
|
||||
mv x86_64-linux-ghcup-$GHCUP_VERSION /usr/bin/ghcup && \
|
||||
chmod +x /usr/bin/ghcup && \
|
||||
rm -rf SHA256SUMS SHA256SUMS.sig
|
||||
|
||||
ARG GHC=8.10.7
|
||||
ARG CABAL_INSTALL=3.6.2.0
|
||||
ARG STACK=2.9.1
|
||||
|
||||
ENV GHCUP_CURL_OPTS="--silent"
|
||||
ENV NO_COLOR=1
|
||||
|
||||
# install haskell toolchain
|
||||
RUN ghcup config set gpg-setting GPGStrict && \
|
||||
ghcup --verbose install ghc --isolate=/usr --force ${GHC} && \
|
||||
ghcup --verbose install cabal --isolate=/usr/bin --force ${CABAL_INSTALL} && \
|
||||
find "/usr/lib/ghc-${GHC}/" \( -name "*_p.a" -o -name "*.p_hi" \) -type f -delete && \
|
||||
rm -r "/usr/share/doc/ghc-${GHC}" && \
|
||||
rm -rf /tmp/ghcup* && \
|
||||
ghcup gc -p -s -c -t
|
||||
|
||||
ENV PATH /root/.cabal/bin:/root/.ghcup:/root/.local/bin:$PATH
|
||||
|
||||
61
docker/arm32v7/buster/Dockerfile
Normal file
61
docker/arm32v7/buster/Dockerfile
Normal file
@@ -0,0 +1,61 @@
|
||||
FROM arm32v7/debian:10
|
||||
|
||||
ENV LANG C.UTF-8
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV TZ=Asia/Singapore
|
||||
|
||||
COPY update_opt.sh /usr/bin/update_opt.sh
|
||||
RUN chmod +x /usr/bin/update_opt.sh
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
curl \
|
||||
dirmngr \
|
||||
g++ \
|
||||
git \
|
||||
gnupg \
|
||||
libsqlite3-dev \
|
||||
libtinfo-dev \
|
||||
libgmp-dev \
|
||||
make \
|
||||
netbase \
|
||||
openssh-client \
|
||||
xz-utils \
|
||||
zlib1g-dev \
|
||||
libnuma-dev libgmp10 libssl-dev liblzma-dev libbz2-dev wget lsb-release software-properties-common apt-transport-https gcc autoconf automake build-essential gzip patchelf tree \
|
||||
llvm-11 clang-11 && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN update_opt.sh 11 1
|
||||
|
||||
ARG GHCUP_VERSION=0.1.17.8
|
||||
ARG GPG_KEY=7784930957807690A66EBDBE3786C5262ECB4A3F
|
||||
|
||||
# install ghcup
|
||||
RUN gpg --batch --keyserver keys.openpgp.org --recv-keys $GPG_KEY && \
|
||||
curl -sSfL -O https://downloads.haskell.org/~ghcup/$GHCUP_VERSION/armv7-linux-ghcup-$GHCUP_VERSION && \
|
||||
curl -sSfL -O https://downloads.haskell.org/~ghcup/$GHCUP_VERSION/SHA256SUMS && \
|
||||
curl -sSfL -O https://downloads.haskell.org/~ghcup/$GHCUP_VERSION/SHA256SUMS.sig && \
|
||||
gpg --verify SHA256SUMS.sig SHA256SUMS && \
|
||||
sha256sum -c --ignore-missing SHA256SUMS && \
|
||||
mv armv7-linux-ghcup-$GHCUP_VERSION /usr/bin/ghcup && \
|
||||
chmod +x /usr/bin/ghcup && \
|
||||
rm -rf SHA256SUMS SHA256SUMS.sig
|
||||
|
||||
ARG GHC=8.10.7
|
||||
ARG CABAL_INSTALL=3.6.2.0
|
||||
ARG STACK=2.9.1
|
||||
|
||||
ENV GHCUP_CURL_OPTS="--silent"
|
||||
ENV NO_COLOR=1
|
||||
|
||||
# install haskell toolchain
|
||||
RUN ghcup config set gpg-setting GPGStrict && \
|
||||
ghcup --verbose install ghc --isolate=/usr --force ${GHC} && \
|
||||
ghcup --verbose install cabal --isolate=/usr/bin --force ${CABAL_INSTALL} && \
|
||||
ghcup gc -s -c -t
|
||||
|
||||
ENV PATH /root/.cabal/bin:/root/.ghcup/bin:/root/.local/bin:$PATH
|
||||
|
||||
CMD ["ghci"]
|
||||
36
docker/arm32v7/buster/update_opt.sh
Executable file
36
docker/arm32v7/buster/update_opt.sh
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
# update_alternatives.sh
|
||||
|
||||
update_alternatives() {
|
||||
local version=${1}
|
||||
local priority=${2}
|
||||
local master=${3}
|
||||
local slaves=${4}
|
||||
local path=${5}
|
||||
local cmdln
|
||||
|
||||
cmdln="--verbose --install ${path}${master} ${master} ${path}${master}-${version} ${priority}"
|
||||
for slave in ${slaves}; do
|
||||
cmdln="${cmdln} --slave ${path}${slave} ${slave} ${path}${slave}-${version}"
|
||||
done
|
||||
update-alternatives ${cmdln}
|
||||
}
|
||||
|
||||
if [[ ${#} -ne 2 ]]; then
|
||||
echo usage: "${0}" clang_version priority
|
||||
exit 1
|
||||
fi
|
||||
|
||||
version=${1}
|
||||
priority=${2}
|
||||
path="/usr/bin/"
|
||||
|
||||
master="llvm-config"
|
||||
slaves="llvm-addr2line llvm-ar llvm-as llvm-bcanalyzer llvm-bitcode-strip llvm-cat llvm-cfi-verify llvm-cov llvm-c-test llvm-cvtres llvm-cxxdump llvm-cxxfilt llvm-cxxmap llvm-debuginfod llvm-debuginfod-find llvm-diff llvm-dis llvm-dlltool llvm-dwarfdump llvm-dwarfutil llvm-dwp llvm-exegesis llvm-extract llvm-gsymutil llvm-ifs llvm-install-name-tool llvm-jitlink llvm-jitlink-executor llvm-lib llvm-libtool-darwin llvm-link llvm-lipo llvm-lto llvm-lto2 llvm-mc llvm-mca llvm-ml llvm-modextract llvm-mt llvm-nm llvm-objcopy llvm-objdump llvm-omp-device-info llvm-opt-report llvm-otool llvm-pdbutil llvm-PerfectShuffle llvm-profdata llvm-profgen llvm-ranlib llvm-rc llvm-readelf llvm-readobj llvm-reduce llvm-remark-size-diff llvm-rtdyld llvm-sim llvm-size llvm-split llvm-stress llvm-strings llvm-strip llvm-symbolizer llvm-tapi-diff llvm-tblgen llvm-tli-checker llvm-undname llvm-windres llvm-xray"
|
||||
|
||||
update_alternatives "${version}" "${priority}" "${master}" "${slaves}" "${path}"
|
||||
|
||||
master="clang"
|
||||
slaves="analyze-build asan_symbolize bugpoint c-index-test clang++ clang-apply-replacements clang-change-namespace clang-check clang-cl clang-cpp clangd clang-doc clang-extdef-mapping clang-format clang-format-diff clang-include-fixer clang-linker-wrapper clang-move clang-nvlink-wrapper clang-offload-bundler clang-offload-packager clang-offload-wrapper clang-pseudo clang-query clang-refactor clang-rename clang-reorder-fields clang-repl clang-scan-deps clang-tidy count diagtool dsymutil FileCheck find-all-symbols git-clang-format hmaptool hwasan_symbolize intercept-build ld64.lld ld.lld llc lld lldb lldb-argdumper lldb-instr lldb-server lldb-vscode lld-link lli lli-child-target modularize not obj2yaml opt pp-trace run-clang-tidy sancov sanstats scan-build scan-build-py scan-view split-file UnicodeNameMappingGenerator verify-uselistorder wasm-ld yaml2obj yaml-bench"
|
||||
|
||||
update_alternatives "${version}" "${priority}" "${master}" "${slaves}" "${path}"
|
||||
61
docker/arm32v7/focal/Dockerfile
Normal file
61
docker/arm32v7/focal/Dockerfile
Normal file
@@ -0,0 +1,61 @@
|
||||
FROM arm32v7/ubuntu:focal
|
||||
|
||||
ENV LANG C.UTF-8
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV TZ=Asia/Singapore
|
||||
|
||||
COPY update_opt.sh /usr/bin/update_opt.sh
|
||||
RUN chmod +x /usr/bin/update_opt.sh
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
curl \
|
||||
dirmngr \
|
||||
g++ \
|
||||
git \
|
||||
gnupg \
|
||||
libsqlite3-dev \
|
||||
libtinfo-dev \
|
||||
libgmp-dev \
|
||||
make \
|
||||
netbase \
|
||||
openssh-client \
|
||||
xz-utils \
|
||||
zlib1g-dev \
|
||||
libnuma-dev libgmp10 libssl-dev liblzma-dev libbz2-dev wget lsb-release software-properties-common apt-transport-https gcc autoconf automake build-essential gzip patchelf tree \
|
||||
llvm-9 clang-9 && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN update_opt.sh 9 1
|
||||
|
||||
ARG GHCUP_VERSION=0.1.17.8
|
||||
ARG GPG_KEY=7784930957807690A66EBDBE3786C5262ECB4A3F
|
||||
|
||||
# install ghcup
|
||||
RUN gpg --batch --keyserver keys.openpgp.org --recv-keys $GPG_KEY && \
|
||||
curl -sSfL -O https://downloads.haskell.org/~ghcup/$GHCUP_VERSION/armv7-linux-ghcup-$GHCUP_VERSION && \
|
||||
curl -sSfL -O https://downloads.haskell.org/~ghcup/$GHCUP_VERSION/SHA256SUMS && \
|
||||
curl -sSfL -O https://downloads.haskell.org/~ghcup/$GHCUP_VERSION/SHA256SUMS.sig && \
|
||||
gpg --verify SHA256SUMS.sig SHA256SUMS && \
|
||||
sha256sum -c --ignore-missing SHA256SUMS && \
|
||||
mv armv7-linux-ghcup-$GHCUP_VERSION /usr/bin/ghcup && \
|
||||
chmod +x /usr/bin/ghcup && \
|
||||
rm -rf SHA256SUMS SHA256SUMS.sig
|
||||
|
||||
ARG GHC=8.10.7
|
||||
ARG CABAL_INSTALL=3.6.2.0
|
||||
ARG STACK=2.9.1
|
||||
|
||||
ENV GHCUP_CURL_OPTS="--silent"
|
||||
ENV NO_COLOR=1
|
||||
|
||||
# install haskell toolchain
|
||||
RUN ghcup config set gpg-setting GPGStrict && \
|
||||
ghcup --verbose install ghc --isolate=/usr --force ${GHC} && \
|
||||
ghcup --verbose install cabal --isolate=/usr/bin --force ${CABAL_INSTALL} && \
|
||||
ghcup gc -s -c -t
|
||||
|
||||
ENV PATH /root/.cabal/bin:/root/.ghcup/bin:/root/.local/bin:$PATH
|
||||
|
||||
CMD ["ghci"]
|
||||
36
docker/arm32v7/focal/update_opt.sh
Executable file
36
docker/arm32v7/focal/update_opt.sh
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
# update_alternatives.sh
|
||||
|
||||
update_alternatives() {
|
||||
local version=${1}
|
||||
local priority=${2}
|
||||
local master=${3}
|
||||
local slaves=${4}
|
||||
local path=${5}
|
||||
local cmdln
|
||||
|
||||
cmdln="--verbose --install ${path}${master} ${master} ${path}${master}-${version} ${priority}"
|
||||
for slave in ${slaves}; do
|
||||
cmdln="${cmdln} --slave ${path}${slave} ${slave} ${path}${slave}-${version}"
|
||||
done
|
||||
update-alternatives ${cmdln}
|
||||
}
|
||||
|
||||
if [[ ${#} -ne 2 ]]; then
|
||||
echo usage: "${0}" clang_version priority
|
||||
exit 1
|
||||
fi
|
||||
|
||||
version=${1}
|
||||
priority=${2}
|
||||
path="/usr/bin/"
|
||||
|
||||
master="llvm-config"
|
||||
slaves="llvm-addr2line llvm-ar llvm-as llvm-bcanalyzer llvm-bitcode-strip llvm-cat llvm-cfi-verify llvm-cov llvm-c-test llvm-cvtres llvm-cxxdump llvm-cxxfilt llvm-cxxmap llvm-debuginfod llvm-debuginfod-find llvm-diff llvm-dis llvm-dlltool llvm-dwarfdump llvm-dwarfutil llvm-dwp llvm-exegesis llvm-extract llvm-gsymutil llvm-ifs llvm-install-name-tool llvm-jitlink llvm-jitlink-executor llvm-lib llvm-libtool-darwin llvm-link llvm-lipo llvm-lto llvm-lto2 llvm-mc llvm-mca llvm-ml llvm-modextract llvm-mt llvm-nm llvm-objcopy llvm-objdump llvm-omp-device-info llvm-opt-report llvm-otool llvm-pdbutil llvm-PerfectShuffle llvm-profdata llvm-profgen llvm-ranlib llvm-rc llvm-readelf llvm-readobj llvm-reduce llvm-remark-size-diff llvm-rtdyld llvm-sim llvm-size llvm-split llvm-stress llvm-strings llvm-strip llvm-symbolizer llvm-tapi-diff llvm-tblgen llvm-tli-checker llvm-undname llvm-windres llvm-xray"
|
||||
|
||||
update_alternatives "${version}" "${priority}" "${master}" "${slaves}" "${path}"
|
||||
|
||||
master="clang"
|
||||
slaves="analyze-build asan_symbolize bugpoint c-index-test clang++ clang-apply-replacements clang-change-namespace clang-check clang-cl clang-cpp clangd clang-doc clang-extdef-mapping clang-format clang-format-diff clang-include-fixer clang-linker-wrapper clang-move clang-nvlink-wrapper clang-offload-bundler clang-offload-packager clang-offload-wrapper clang-pseudo clang-query clang-refactor clang-rename clang-reorder-fields clang-repl clang-scan-deps clang-tidy count diagtool dsymutil FileCheck find-all-symbols git-clang-format hmaptool hwasan_symbolize intercept-build ld64.lld ld.lld llc lld lldb lldb-argdumper lldb-instr lldb-server lldb-vscode lld-link lli lli-child-target modularize not obj2yaml opt pp-trace run-clang-tidy sancov sanstats scan-build scan-build-py scan-view split-file UnicodeNameMappingGenerator verify-uselistorder wasm-ld yaml2obj yaml-bench"
|
||||
|
||||
update_alternatives "${version}" "${priority}" "${master}" "${slaves}" "${path}"
|
||||
61
docker/arm64v8/buster/Dockerfile
Normal file
61
docker/arm64v8/buster/Dockerfile
Normal file
@@ -0,0 +1,61 @@
|
||||
FROM arm64v8/debian:10
|
||||
|
||||
ENV LANG C.UTF-8
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV TZ=Asia/Singapore
|
||||
|
||||
COPY update_opt.sh /usr/bin/update_opt.sh
|
||||
RUN chmod +x /usr/bin/update_opt.sh
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
curl \
|
||||
dirmngr \
|
||||
g++ \
|
||||
git \
|
||||
gnupg \
|
||||
libsqlite3-dev \
|
||||
libtinfo-dev \
|
||||
libgmp-dev \
|
||||
make \
|
||||
netbase \
|
||||
openssh-client \
|
||||
xz-utils \
|
||||
zlib1g-dev \
|
||||
libnuma-dev libgmp10 libssl-dev liblzma-dev libbz2-dev wget lsb-release software-properties-common apt-transport-https gcc autoconf automake build-essential gzip patchelf tree \
|
||||
llvm-11 clang-11 && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN update_opt.sh 11 1
|
||||
|
||||
ARG GHCUP_VERSION=0.1.18.0
|
||||
ARG GPG_KEY=7784930957807690A66EBDBE3786C5262ECB4A3F
|
||||
|
||||
# install ghcup
|
||||
RUN gpg --batch --keyserver keys.openpgp.org --recv-keys $GPG_KEY && \
|
||||
curl -sSfL -O https://downloads.haskell.org/~ghcup/$GHCUP_VERSION/aarch64-linux-ghcup-$GHCUP_VERSION && \
|
||||
curl -sSfL -O https://downloads.haskell.org/~ghcup/$GHCUP_VERSION/SHA256SUMS && \
|
||||
curl -sSfL -O https://downloads.haskell.org/~ghcup/$GHCUP_VERSION/SHA256SUMS.sig && \
|
||||
gpg --verify SHA256SUMS.sig SHA256SUMS && \
|
||||
sha256sum -c --ignore-missing SHA256SUMS && \
|
||||
mv aarch64-linux-ghcup-$GHCUP_VERSION /usr/bin/ghcup && \
|
||||
chmod +x /usr/bin/ghcup && \
|
||||
rm -rf SHA256SUMS SHA256SUMS.sig
|
||||
|
||||
ARG GHC=8.10.7
|
||||
ARG CABAL_INSTALL=3.6.2.0
|
||||
ARG STACK=2.9.1
|
||||
|
||||
ENV GHCUP_CURL_OPTS="--silent"
|
||||
ENV NO_COLOR=1
|
||||
|
||||
# install haskell toolchain
|
||||
RUN ghcup config set gpg-setting GPGStrict && \
|
||||
ghcup --verbose install ghc --isolate=/usr --force ${GHC} && \
|
||||
ghcup --verbose install cabal --isolate=/usr/bin --force ${CABAL_INSTALL} && \
|
||||
ghcup gc -s -c -t
|
||||
|
||||
ENV PATH /root/.cabal/bin:/root/.ghcup/bin:/root/.local/bin:$PATH
|
||||
|
||||
CMD ["ghci"]
|
||||
36
docker/arm64v8/buster/update_opt.sh
Executable file
36
docker/arm64v8/buster/update_opt.sh
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
# update_alternatives.sh
|
||||
|
||||
update_alternatives() {
|
||||
local version=${1}
|
||||
local priority=${2}
|
||||
local master=${3}
|
||||
local slaves=${4}
|
||||
local path=${5}
|
||||
local cmdln
|
||||
|
||||
cmdln="--verbose --install ${path}${master} ${master} ${path}${master}-${version} ${priority}"
|
||||
for slave in ${slaves}; do
|
||||
cmdln="${cmdln} --slave ${path}${slave} ${slave} ${path}${slave}-${version}"
|
||||
done
|
||||
update-alternatives ${cmdln}
|
||||
}
|
||||
|
||||
if [[ ${#} -ne 2 ]]; then
|
||||
echo usage: "${0}" clang_version priority
|
||||
exit 1
|
||||
fi
|
||||
|
||||
version=${1}
|
||||
priority=${2}
|
||||
path="/usr/bin/"
|
||||
|
||||
master="llvm-config"
|
||||
slaves="llvm-addr2line llvm-ar llvm-as llvm-bcanalyzer llvm-bitcode-strip llvm-cat llvm-cfi-verify llvm-cov llvm-c-test llvm-cvtres llvm-cxxdump llvm-cxxfilt llvm-cxxmap llvm-debuginfod llvm-debuginfod-find llvm-diff llvm-dis llvm-dlltool llvm-dwarfdump llvm-dwarfutil llvm-dwp llvm-exegesis llvm-extract llvm-gsymutil llvm-ifs llvm-install-name-tool llvm-jitlink llvm-jitlink-executor llvm-lib llvm-libtool-darwin llvm-link llvm-lipo llvm-lto llvm-lto2 llvm-mc llvm-mca llvm-ml llvm-modextract llvm-mt llvm-nm llvm-objcopy llvm-objdump llvm-omp-device-info llvm-opt-report llvm-otool llvm-pdbutil llvm-PerfectShuffle llvm-profdata llvm-profgen llvm-ranlib llvm-rc llvm-readelf llvm-readobj llvm-reduce llvm-remark-size-diff llvm-rtdyld llvm-sim llvm-size llvm-split llvm-stress llvm-strings llvm-strip llvm-symbolizer llvm-tapi-diff llvm-tblgen llvm-tli-checker llvm-undname llvm-windres llvm-xray"
|
||||
|
||||
update_alternatives "${version}" "${priority}" "${master}" "${slaves}" "${path}"
|
||||
|
||||
master="clang"
|
||||
slaves="analyze-build asan_symbolize bugpoint c-index-test clang++ clang-apply-replacements clang-change-namespace clang-check clang-cl clang-cpp clangd clang-doc clang-extdef-mapping clang-format clang-format-diff clang-include-fixer clang-linker-wrapper clang-move clang-nvlink-wrapper clang-offload-bundler clang-offload-packager clang-offload-wrapper clang-pseudo clang-query clang-refactor clang-rename clang-reorder-fields clang-repl clang-scan-deps clang-tidy count diagtool dsymutil FileCheck find-all-symbols git-clang-format hmaptool hwasan_symbolize intercept-build ld64.lld ld.lld llc lld lldb lldb-argdumper lldb-instr lldb-server lldb-vscode lld-link lli lli-child-target modularize not obj2yaml opt pp-trace run-clang-tidy sancov sanstats scan-build scan-build-py scan-view split-file UnicodeNameMappingGenerator verify-uselistorder wasm-ld yaml2obj yaml-bench"
|
||||
|
||||
update_alternatives "${version}" "${priority}" "${master}" "${slaves}" "${path}"
|
||||
61
docker/arm64v8/focal/Dockerfile
Normal file
61
docker/arm64v8/focal/Dockerfile
Normal file
@@ -0,0 +1,61 @@
|
||||
FROM arm64v8/ubuntu:focal
|
||||
|
||||
ENV LANG C.UTF-8
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV TZ=Asia/Singapore
|
||||
|
||||
COPY update_opt.sh /usr/bin/update_opt.sh
|
||||
RUN chmod +x /usr/bin/update_opt.sh
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
curl \
|
||||
dirmngr \
|
||||
g++ \
|
||||
git \
|
||||
gnupg \
|
||||
libsqlite3-dev \
|
||||
libtinfo-dev \
|
||||
libgmp-dev \
|
||||
make \
|
||||
netbase \
|
||||
openssh-client \
|
||||
xz-utils \
|
||||
zlib1g-dev \
|
||||
libnuma-dev libgmp10 libssl-dev liblzma-dev libbz2-dev wget lsb-release software-properties-common apt-transport-https gcc autoconf automake build-essential gzip patchelf tree \
|
||||
llvm-9 clang-9 && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN update_opt.sh 9 1
|
||||
|
||||
ARG GHCUP_VERSION=0.1.18.0
|
||||
ARG GPG_KEY=7784930957807690A66EBDBE3786C5262ECB4A3F
|
||||
|
||||
# install ghcup
|
||||
RUN gpg --batch --keyserver keys.openpgp.org --recv-keys $GPG_KEY && \
|
||||
curl -sSfL -O https://downloads.haskell.org/~ghcup/$GHCUP_VERSION/aarch64-linux-ghcup-$GHCUP_VERSION && \
|
||||
curl -sSfL -O https://downloads.haskell.org/~ghcup/$GHCUP_VERSION/SHA256SUMS && \
|
||||
curl -sSfL -O https://downloads.haskell.org/~ghcup/$GHCUP_VERSION/SHA256SUMS.sig && \
|
||||
gpg --verify SHA256SUMS.sig SHA256SUMS && \
|
||||
sha256sum -c --ignore-missing SHA256SUMS && \
|
||||
mv aarch64-linux-ghcup-$GHCUP_VERSION /usr/bin/ghcup && \
|
||||
chmod +x /usr/bin/ghcup && \
|
||||
rm -rf SHA256SUMS SHA256SUMS.sig
|
||||
|
||||
ARG GHC=8.10.7
|
||||
ARG CABAL_INSTALL=3.6.2.0
|
||||
ARG STACK=2.9.1
|
||||
|
||||
ENV GHCUP_CURL_OPTS="--silent"
|
||||
ENV NO_COLOR=1
|
||||
|
||||
# install haskell toolchain
|
||||
RUN ghcup config set gpg-setting GPGStrict && \
|
||||
ghcup --verbose install ghc --isolate=/usr --force ${GHC} && \
|
||||
ghcup --verbose install cabal --isolate=/usr/bin --force ${CABAL_INSTALL} && \
|
||||
ghcup gc -s -c -t
|
||||
|
||||
ENV PATH /root/.cabal/bin:/root/.ghcup/bin:/root/.local/bin:$PATH
|
||||
|
||||
CMD ["ghci"]
|
||||
36
docker/arm64v8/focal/update_opt.sh
Executable file
36
docker/arm64v8/focal/update_opt.sh
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
# update_alternatives.sh
|
||||
|
||||
update_alternatives() {
|
||||
local version=${1}
|
||||
local priority=${2}
|
||||
local master=${3}
|
||||
local slaves=${4}
|
||||
local path=${5}
|
||||
local cmdln
|
||||
|
||||
cmdln="--verbose --install ${path}${master} ${master} ${path}${master}-${version} ${priority}"
|
||||
for slave in ${slaves}; do
|
||||
cmdln="${cmdln} --slave ${path}${slave} ${slave} ${path}${slave}-${version}"
|
||||
done
|
||||
update-alternatives ${cmdln}
|
||||
}
|
||||
|
||||
if [[ ${#} -ne 2 ]]; then
|
||||
echo usage: "${0}" clang_version priority
|
||||
exit 1
|
||||
fi
|
||||
|
||||
version=${1}
|
||||
priority=${2}
|
||||
path="/usr/bin/"
|
||||
|
||||
master="llvm-config"
|
||||
slaves="llvm-addr2line llvm-ar llvm-as llvm-bcanalyzer llvm-bitcode-strip llvm-cat llvm-cfi-verify llvm-cov llvm-c-test llvm-cvtres llvm-cxxdump llvm-cxxfilt llvm-cxxmap llvm-debuginfod llvm-debuginfod-find llvm-diff llvm-dis llvm-dlltool llvm-dwarfdump llvm-dwarfutil llvm-dwp llvm-exegesis llvm-extract llvm-gsymutil llvm-ifs llvm-install-name-tool llvm-jitlink llvm-jitlink-executor llvm-lib llvm-libtool-darwin llvm-link llvm-lipo llvm-lto llvm-lto2 llvm-mc llvm-mca llvm-ml llvm-modextract llvm-mt llvm-nm llvm-objcopy llvm-objdump llvm-omp-device-info llvm-opt-report llvm-otool llvm-pdbutil llvm-PerfectShuffle llvm-profdata llvm-profgen llvm-ranlib llvm-rc llvm-readelf llvm-readobj llvm-reduce llvm-remark-size-diff llvm-rtdyld llvm-sim llvm-size llvm-split llvm-stress llvm-strings llvm-strip llvm-symbolizer llvm-tapi-diff llvm-tblgen llvm-tli-checker llvm-undname llvm-windres llvm-xray"
|
||||
|
||||
update_alternatives "${version}" "${priority}" "${master}" "${slaves}" "${path}"
|
||||
|
||||
master="clang"
|
||||
slaves="analyze-build asan_symbolize bugpoint c-index-test clang++ clang-apply-replacements clang-change-namespace clang-check clang-cl clang-cpp clangd clang-doc clang-extdef-mapping clang-format clang-format-diff clang-include-fixer clang-linker-wrapper clang-move clang-nvlink-wrapper clang-offload-bundler clang-offload-packager clang-offload-wrapper clang-pseudo clang-query clang-refactor clang-rename clang-reorder-fields clang-repl clang-scan-deps clang-tidy count diagtool dsymutil FileCheck find-all-symbols git-clang-format hmaptool hwasan_symbolize intercept-build ld64.lld ld.lld llc lld lldb lldb-argdumper lldb-instr lldb-server lldb-vscode lld-link lli lli-child-target modularize not obj2yaml opt pp-trace run-clang-tidy sancov sanstats scan-build scan-build-py scan-view split-file UnicodeNameMappingGenerator verify-uselistorder wasm-ld yaml2obj yaml-bench"
|
||||
|
||||
update_alternatives "${version}" "${priority}" "${master}" "${slaves}" "${path}"
|
||||
@@ -42,8 +42,8 @@ All you wanted to know about GHCup.
|
||||
|
||||
## How to help
|
||||
|
||||
* if you want to contribute code or documentation, check out the [issue tracker](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues) and the [Development guide](./dev.md)
|
||||
* if you want to propose features or write user feedback, feel free to [open a ticket](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/new?issue)
|
||||
* if you want to contribute code or documentation, check out the [issue tracker](https://github.com/haskell/ghcup-hs/issues) and the [Development guide](./dev.md)
|
||||
* if you want to propose features or write user feedback, feel free to [open a ticket](https://github.com/haskell/ghcup-hs/issues/new)
|
||||
* if you want to donate to the project, visit our [opencollective](https://opencollective.com/ghcup#category-CONTRIBUTE) page
|
||||
|
||||
## Design goals
|
||||
@@ -60,6 +60,29 @@ All you wanted to know about GHCup.
|
||||
3. handling cabal projects
|
||||
4. being a stack alternative
|
||||
|
||||
## Distribution policies
|
||||
|
||||
Like most Linux distros and other distribution channels, GHCup also
|
||||
follows certain policies. These are as follows:
|
||||
|
||||
1. The end-user experience is our primary concern
|
||||
- ghcup in CI systems as a use case is a first class citizen
|
||||
2. We strive to collaborate with all maintainers of all the tools we support and maintain a good relationship
|
||||
3. We may fix build system or other distribution bugs in upstream bindists
|
||||
- these are always communicated upstream
|
||||
4. We may even patch source code of supported tools in very rare cases if that is required to ensure that the end-user experience does not break
|
||||
- we'll first try to upstream any such required patch and request a new release to avoid downstream patching
|
||||
- patches will be communicated to the maintainers either way and we'll strive to get their review
|
||||
- they will also be communicated to the end-user
|
||||
- they will be uploaded along with the bindist
|
||||
- we will avoid maintaining long-running downstream patches (currently zero)
|
||||
5. We may add bindists for platforms that upstream does not support
|
||||
- this is currently the case for GHC for e.g. Alpine and possibly FreeBSD in the future
|
||||
- this is currently also the case for stack on darwin M1
|
||||
- we don't guarantee for unofficial bindists that the test suite passes at the moment (this may change in the future)
|
||||
6. We GPG sign all the GHCup metadata as well as the unofficial bindists
|
||||
- any trust issues relating to missing checksums or GPG signatures is a bug and given high priority
|
||||
|
||||
## How
|
||||
|
||||
Installs a specified GHC version into `~/.ghcup/ghc/<ver>`, and places `ghc-<ver>` symlinks in `~/.ghcup/bin/`.
|
||||
@@ -74,13 +97,16 @@ cabal-install/HLS/stack are installed in `~/.ghcup/bin/<tool>-<ver>` and have un
|
||||
|
||||
## Known users
|
||||
|
||||
* Github actions:
|
||||
- [actions/virtual-environments](https://github.com/actions/virtual-environments)
|
||||
- [haskell/actions/setup](https://github.com/haskell/actions/tree/main/setup)
|
||||
* CI:
|
||||
- [Github actions/virtual-environments](https://github.com/actions/virtual-environments)
|
||||
- [Github haskell/actions/setup](https://github.com/haskell/actions/tree/main/setup)
|
||||
- [haskell-ci](https://github.com/haskell-CI/haskell-ci)
|
||||
* mirrors:
|
||||
- [sjtug](https://mirror.sjtu.edu.cn/docs/ghcup)
|
||||
- [sjtug](https://mirror.sjtu.edu.cn/docs/ghcup)
|
||||
* tools:
|
||||
- [vabal](https://github.com/Franciman/vabal)
|
||||
- [vscode-haskell](https://github.com/haskell/vscode-haskell)
|
||||
- [nvim-lsp-installer](https://github.com/williamboman/nvim-lsp-installer)
|
||||
- [vabal](https://github.com/Franciman/vabal)
|
||||
|
||||
## Known problems
|
||||
|
||||
@@ -152,6 +178,11 @@ Windows 7 and Powershell 2.0 aren't well supported at the moment, also see:
|
||||
|
||||
## FAQ
|
||||
|
||||
### Is ghcup really the main installer?
|
||||
|
||||
This is based on the Haskell survey results from 2022, which show that more
|
||||
than half of survey participants use GHCup: https://taylor.fausak.me/2022/11/18/haskell-survey-results/
|
||||
|
||||
### Why reimplement stack?
|
||||
|
||||
GHCup is not a reimplementation of stack. The only common part is automatic installation of GHC,
|
||||
|
||||
@@ -69,9 +69,9 @@ Head over to: [https://github.com/haskell/ghcup-metadata#adding-a-new-ghc-versio
|
||||
|
||||
### Adding a new CLI command
|
||||
|
||||
An example illustration on how to deal with [optparse-applicative](https://hackage.haskell.org/package/optparse-applicative) can be seen here: [https://gitlab.haskell.org/haskell/ghcup-hs/-/commit/c19dd5ee8b2edbaf0336af143f1c75b6f4843e26](https://gitlab.haskell.org/haskell/ghcup-hs/-/commit/c19dd5ee8b2edbaf0336af143f1c75b6f4843e26)
|
||||
An example illustration on how to deal with [optparse-applicative](https://hackage.haskell.org/package/optparse-applicative) can be seen here: [https://github.com/haskell/ghcup-hs/commit/c19dd5ee8b2edbaf0336af143f1c75b6f4843e26](https://github.com/haskell/ghcup-hs/commit/c19dd5ee8b2edbaf0336af143f1c75b6f4843e26)
|
||||
|
||||
Every subcommand now lives in its own module under [GHCup.OptParse.MyCommand](https://gitlab.haskell.org/haskell/ghcup-hs/-/tree/master/app/ghcup/GHCup/OptParse).
|
||||
Every subcommand now lives in its own module under [GHCup.OptParse.MyCommand](https://github.com/haskell/ghcup-hs/tree/master/app/ghcup/GHCup/OptParse).
|
||||
|
||||
## Major refactors
|
||||
|
||||
|
||||
289
docs/guide.md
289
docs/guide.md
@@ -4,7 +4,7 @@ This is a more in-depth guide specific to GHCup. `ghcup --help` is your friend.
|
||||
|
||||
## Basic usage
|
||||
|
||||
For the simple interactive TUI (not available on windows), run:
|
||||
For the simple, interactive, text-based user interface (TUI) (not available on windows), run:
|
||||
|
||||
```sh
|
||||
ghcup tui
|
||||
@@ -50,26 +50,46 @@ For man pages to work you need [man-db](http://man-db.nongnu.org/) as your `man`
|
||||
|
||||
## Shell-completion
|
||||
|
||||
Shell completions are in [scripts/shell-completions](https://gitlab.haskell.org/haskell/ghcup-hs/-/tree/master/scripts/shell-completions) directory of this repository.
|
||||
Shell completions are in [scripts/shell-completions](https://github.com/haskell/ghcup-hs/tree/master/scripts/shell-completions) directory of this repository.
|
||||
|
||||
For bash: install `shell-completions/bash`
|
||||
as e.g. `/etc/bash_completion.d/ghcup` (depending on distro)
|
||||
and make sure your bashrc sources the startup script
|
||||
(`/usr/share/bash-completion/bash_completion` on some distros).
|
||||
|
||||
## Portability
|
||||
|
||||
`ghcup` is very portable. There are a few exceptions though:
|
||||
|
||||
1. `ghcup tui` is only available on non-windows platforms
|
||||
2. legacy subcommands `ghcup install` (without a tool identifier) and `ghcup install-cabal` may be removed in the future
|
||||
|
||||
# Configuration
|
||||
|
||||
A configuration file can be put in `~/.ghcup/config.yaml`. The default config file
|
||||
explaining all possible configurations can be found in this repo: [config.yaml](https://gitlab.haskell.org/haskell/ghcup-hs/-/blob/master/data/config.yaml).
|
||||
explaining all possible configurations can be found in this repo: [config.yaml](https://github.com/haskell/ghcup-hs/blob/master/data/config.yaml).
|
||||
|
||||
Partial configuration is fine. Command line options always override the config file settings.
|
||||
|
||||
## Overriding distro detection
|
||||
|
||||
If you're running e.g. an Ubuntu derivate based on 18.04 and ghcup is picking bindists that
|
||||
don't work well, you could do this in `config.yaml`:
|
||||
|
||||
```yml
|
||||
platform-override:
|
||||
arch: A_64
|
||||
platform:
|
||||
contents: Ubuntu
|
||||
tag: Linux
|
||||
version: '18.04'
|
||||
```
|
||||
|
||||
## Env variables
|
||||
|
||||
This is the complete list of env variables that change GHCup behavior:
|
||||
|
||||
* `GHCUP_USE_XDG_DIRS`: see [XDG support](#xdg-support) above
|
||||
* `TMPDIR`: where ghcup does the work (unpacking, building, ...)
|
||||
* `GHCUP_INSTALL_BASE_PREFIX`: the base of ghcup (default: `$HOME`)
|
||||
* `GHCUP_CURL_OPTS`: additional options that can be passed to curl
|
||||
* `GHCUP_WGET_OPTS`: additional options that can be passed to wget
|
||||
@@ -77,6 +97,10 @@ 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's additionally:
|
||||
|
||||
* `GHCUP_MSYS2`: Has to point to the root of an existing MSYS2 installation (when installed by GHCup, that's e.g. `C:\ghcup\msys64`). GHCup bootstrap takes care of this usually.
|
||||
|
||||
### XDG support
|
||||
|
||||
To enable XDG style directories, set the environment variable `GHCUP_USE_XDG_DIRS` to anything.
|
||||
@@ -127,7 +151,7 @@ url-source:
|
||||
OwnSource: "https://some-url/ghcup-0.0.6.yaml"
|
||||
```
|
||||
|
||||
See [config.yaml](https://gitlab.haskell.org/haskell/ghcup-hs/-/blob/master/data/config.yaml)
|
||||
See [config.yaml](https://github.com/haskell/ghcup-hs/blob/master/data/config.yaml)
|
||||
for more options.
|
||||
|
||||
Alternatively you can do it via a cli switch:
|
||||
@@ -139,6 +163,7 @@ ghcup --url-source=https://some-url/ghcup-0.0.6.yaml list
|
||||
#### Known mirrors
|
||||
|
||||
1. [https://mirror.sjtu.edu.cn/docs/ghcup](https://mirror.sjtu.edu.cn/docs/ghcup)
|
||||
2. [https://mirrors.ustc.edu.cn/help/ghcup.html](https://mirrors.ustc.edu.cn/help/ghcup.html)
|
||||
|
||||
### (Pre-)Release channels
|
||||
|
||||
@@ -178,8 +203,125 @@ url-source:
|
||||
- "https://raw.githubusercontent.com/haskell/ghcup-metadata/master/ghcup-prereleases-0.0.7.yaml"
|
||||
```
|
||||
|
||||
### Nightlies
|
||||
|
||||
Nightlies are just a nother release channel. Currently, only GHC supports nightlies, which are binary releases
|
||||
that are built every night from `master`.
|
||||
|
||||
To add the nightly channel, run:
|
||||
|
||||
```sh
|
||||
ghcup config add-release-channel https://ghc.gitlab.haskell.org/ghcup-metadata/ghcup-nightlies-0.0.7.yaml
|
||||
```
|
||||
|
||||
To list all nightlies from 2023, run:
|
||||
|
||||
```sh
|
||||
ghcup list --show-nightly --tool=ghc --since=2023-01-01
|
||||
```
|
||||
|
||||
Ways to install a nightly:
|
||||
|
||||
```sh
|
||||
# by date
|
||||
ghcup install ghc 2023-06-20
|
||||
# by version
|
||||
ghcup install ghc 9.7.20230619
|
||||
# by tag
|
||||
ghcup install ghc latest-nightly
|
||||
```
|
||||
|
||||
## Stack integration
|
||||
|
||||
Stack manages GHC versions internally by default. In order to make it use ghcup installed
|
||||
GHC versions there are two strategies.
|
||||
|
||||
### Strategy 1: System GHC (works on all stack versions)
|
||||
|
||||
You can instruct stack to use "system" GHC versions (whatever is in PATH). To do so,
|
||||
run the following commands:
|
||||
|
||||
```sh
|
||||
stack config set install-ghc false --global
|
||||
stack config set system-ghc true --global
|
||||
```
|
||||
|
||||
### Strategy 2: Stack hooks (new, recommended)
|
||||
|
||||
Since stack 2.9.1 you can customize the installation logic of GHC completely, see [https://docs.haskellstack.org/en/stable/yaml_configuration/#ghc-installation-customisation](https://docs.haskellstack.org/en/stable/yaml_configuration/#ghc-installation-customisation).
|
||||
|
||||
We can use this to simply invoke ghcup whenever stack is trying to install/discover a GHC versions. This
|
||||
is done via placing a shell script at `~/.stack/hooks/ghc-install.sh` and making it executable.
|
||||
|
||||
The ghcup bootstrap script asks you during installation whether you want to install this shell script. You can also
|
||||
install/update it manually like so:
|
||||
|
||||
```sh
|
||||
mkdir -p ~/.stack/hooks/
|
||||
curl https://raw.githubusercontent.com/haskell/ghcup-hs/master/scripts/hooks/stack/ghc-install.sh \
|
||||
> ~/.stack/hooks/ghc-install.sh
|
||||
chmod +x ~/.stack/hooks/ghc-install.sh
|
||||
# hooks are only run when 'system-ghc: false'
|
||||
stack config set system-ghc false --global
|
||||
```
|
||||
|
||||
By default, when the hook fails for whatever reason, stack will fall back to its own installation logic. To disable
|
||||
this, run `stack config set install-ghc false --global`.
|
||||
|
||||
### Windows
|
||||
|
||||
On windows, you may find the following config options useful too:
|
||||
`skip-msys`, `extra-path`, `extra-include-dirs`, `extra-lib-dirs`.
|
||||
|
||||
Also check out: [https://docs.haskellstack.org/en/stable/yaml_configuration](https://docs.haskellstack.org/en/stable/yaml_configuration)
|
||||
|
||||
# More on installation
|
||||
|
||||
## Customisation of the installation scripts
|
||||
|
||||
The scripts offered to install GHCup are available here:
|
||||
|
||||
* [bootstrap-haskell](https://github.com/haskell/ghcup-hs/blob/master/scripts/bootstrap/bootstrap-haskell#L7)
|
||||
for Unix-like operating systems
|
||||
* [bootstrap-haskell.ps1](https://github.com/haskell/ghcup-hs/blob/master/scripts/bootstrap/bootstrap-haskell.ps1)
|
||||
for Windows (PowerShell). This will, in turn, run the final bootstrap script
|
||||
(by default, that for the Unix-like operating systems).
|
||||
|
||||
The effect of the scripts can be customised by setting one or more
|
||||
`BOOTSTRAP_HASKELL_*` environment variables (as set out in the first script)
|
||||
and, in the case of Windows, by specifying parameters (as set out in the
|
||||
PowerShell script).
|
||||
|
||||
For example, you can toggle:
|
||||
|
||||
* non-interactive installation
|
||||
* a more verbose installation
|
||||
* whether to install only GHCup (and, on Windows, MSYS2)
|
||||
* not to trigger the upgrade of GHCup
|
||||
* whether to install the latest version of HLS
|
||||
* whether to install the latest version of Stack
|
||||
* whether to respect the XDG Base Directory Specification
|
||||
* whether to adjust (prepend) the PATH in `bashrc`
|
||||
* on Windows, whether to adjust MINGW paths in `cabal.config`
|
||||
|
||||
You can also specify:
|
||||
|
||||
* the GHC version to install
|
||||
* the Cabal version to install
|
||||
* which downloader to use (the default is `curl`)
|
||||
* the base URL for the download of the GHCup binary distribution
|
||||
|
||||
On Windows, you can also use the parameters to:
|
||||
|
||||
* toggle whether to overwrite a previous installation
|
||||
* specify the GHCup installation root directory
|
||||
* specify the Cabal root directory
|
||||
* specify the directory of an existing installation of MSYS2 (for example,
|
||||
the one supplied by Stack)
|
||||
* specify the URL of the final bootstrap script
|
||||
* toggle whether to run the final bootstrap script via `bash` (instead of in a
|
||||
new MSYS2 shell)
|
||||
|
||||
## Installing custom bindists
|
||||
|
||||
There are a couple of good use cases to install custom bindists:
|
||||
@@ -187,7 +329,8 @@ There are a couple of good use cases to install custom bindists:
|
||||
1. manually built bindists (e.g. with patches)
|
||||
- example: `ghcup install ghc -u 'file:///home/mearwald/tmp/ghc-eff-patches/ghc-8.10.2-x86_64-deb10-linux.tar.xz' 8.10.2-eff`
|
||||
2. GHC head CI bindists
|
||||
- example: `ghcup install ghc -u 'https://gitlab.haskell.org/api/v4/projects/1/jobs/artifacts/master/raw/ghc-x86_64-fedora27-linux.tar.xz?job=validate-x86_64-linux-fedora27' head`
|
||||
- example specifying a branch (`master`): `ghcup install ghc -u 'https://gitlab.haskell.org/ghc/ghc/-/jobs/artifacts/master/raw/ghc-x86_64-linux-fedora33-release.tar.xz?job=x86_64-linux-fedora33-release' head`
|
||||
- example specifying a job id (`1129565`): `ghcup install ghc -u ' https://gitlab.haskell.org/api/v4/projects/1/jobs/1129565/artifacts/ghc-x86_64-linux-alpine3_12-validate+fully_static.tar.xz' mr7847`
|
||||
3. DWARF bindists
|
||||
- example: `ghcup install ghc -u 'https://downloads.haskell.org/~ghc/8.10.2/ghc-8.10.2-x86_64-deb10-linux-dwarf.tar.xz' 8.10.2-dwarf`
|
||||
|
||||
@@ -196,18 +339,78 @@ and produce the binaries `ghc-8.10.2-eff` and `ghc-head` respectively.
|
||||
GHCup always needs to know which version the bindist corresponds to (this is not automatically
|
||||
detected).
|
||||
|
||||
## Compiling GHC from source
|
||||
## Compiling from source
|
||||
|
||||
### GHC
|
||||
|
||||
Compiling from source is supported for both source tarballs and arbitrary git refs. See `ghcup compile ghc --help`
|
||||
for a list of all available options.
|
||||
|
||||
If you need to overwrite the existing `build.mk`, check the default files
|
||||
in [data/build_mk](https://gitlab.haskell.org/haskell/ghcup-hs/-/tree/master/data/build_mk), copy them somewhere, adjust them and
|
||||
in [data/build_mk](https://github.com/haskell/ghcup-hs/tree/master/data/build_mk), copy them somewhere, adjust them and
|
||||
pass `--config path/to/build.mk` to `ghcup compile ghc`.
|
||||
Common `build.mk` options are explained [here](https://gitlab.haskell.org/ghc/ghc/-/wikis/building/using#build-configuration).
|
||||
|
||||
Make sure your system meets all the [prerequisites](https://gitlab.haskell.org/ghc/ghc/-/wikis/building/preparation).
|
||||
|
||||
### HLS
|
||||
|
||||
There are 3 main ways to compile HLS from source.
|
||||
|
||||
1. from hackage (should have up to date version bounds)
|
||||
- `ghcup compile hls --version 1.7.0.0 --ghc 9.2.3`
|
||||
2. from git (allows to build latest sources and PRs)
|
||||
- `ghcup compile hls --git-ref master --ghc 9.2.3`
|
||||
- `ghcup compile hls --git-ref a32db0b --ghc 9.2.3`
|
||||
- `ghcup compile hls --git-ref 1.7.0.0 --ghc 9.2.3`
|
||||
3. from source distribution that's packaged during release from the corresponding git sources
|
||||
- `ghcup compile hls --source-dist 1.7.0.0 --ghc 9.2.3`
|
||||
|
||||
All these use `cabal v2-install` under the hood, so all build components are cached.
|
||||
You can pass arbitrary arguments to cabal, e.g. set the index state like so:
|
||||
|
||||
```sh
|
||||
ghcup compile hls --git-ref master --ghc 9.2.3 -- --index-state=2022-06-12T00:00:00Z --allow-newer
|
||||
```
|
||||
|
||||
You can pass `--ghc <ver>` multiple times to install for many GHCs at once.
|
||||
|
||||
When building from git sources, ghcup will auto-detect the HLS version that the git commit corresponds to
|
||||
from the `haskell-language-server.cabal` file. This version might not have been updated since the last release.
|
||||
If you want to avoid overwriting the existing installed HLS version, you can instruct ghcup to use `git describe`
|
||||
to set the HLS version instead:
|
||||
|
||||
```sh
|
||||
ghcup compile hls --git-ref master --ghc 9.2.3 --git-describe-version
|
||||
```
|
||||
|
||||
You can also set the version explicitly:
|
||||
|
||||
```sh
|
||||
ghcup compile hls --git-ref master --ghc 9.2.3 --overwrite-version 1.7.0.0-p1
|
||||
```
|
||||
|
||||
To instruct cabal to run `cabal update` before building, run `ghcup compile hls --version 1.7.0.0 --ghc 9.2.3 --cabal-update`
|
||||
|
||||
As always, check `ghcup compile hls --help`.
|
||||
|
||||
#### Updating HLS for a new GHC version
|
||||
|
||||
First try to build from hackage with some tricks:
|
||||
|
||||
```sh
|
||||
ghcup compile hls --version 1.7.0.0 --ghc 9.2.4 --cabal-update -- --allow-newer --index-state=2022-06-12T00:00:00Z
|
||||
```
|
||||
|
||||
This augments the currently installed 1.7.0.0 official bindists in ghcup with new GHC versions support.
|
||||
|
||||
If that fails (since `--allow-newer` is quite brutal), you can install from HLS master branch (which may contain new fixes) like so:
|
||||
```
|
||||
ghcup compile hls --git-ref master --git-describe-version --ghc 8.10.7 --ghc 9.2.4 --cabal-update
|
||||
```
|
||||
|
||||
This however will create a new HLS version in ghcup, e.g. `1.7.0.0-105-gdc682ba1`, for both 8.10.7 and 9.2.4. If you want to switch back to the official bindists, run `ghcup set hls 1.7.0.0`.
|
||||
|
||||
### Cross support
|
||||
|
||||
ghcup can compile and install a cross GHC for any target. However, this
|
||||
@@ -221,6 +424,8 @@ See `ghcup compile ghc --help` for further information.
|
||||
|
||||
## Isolated installs
|
||||
|
||||
**Before using isolated installs, make sure to have at least GHCup version 0.1.17.8!**
|
||||
|
||||
Ghcup also enables you to install a tool (GHC, Cabal, HLS, Stack) at an isolated location of your choosing.
|
||||
These installs, as the name suggests, are separate from your main installs and DO NOT conflict with them.
|
||||
|
||||
@@ -233,41 +438,43 @@ You need to use the `--isolate` or `-i` flag followed by the directory path.
|
||||
|
||||
Examples:
|
||||
|
||||
1. install an isolated GHC version at location /home/user/isolated_dir/ghc/
|
||||
1. install an isolated GHC version at location /home/user/isolated_dir/ghc/
|
||||
- `ghcup install ghc 8.10.5 --isolate /home/user/isolated_dir/ghc`
|
||||
|
||||
2. isolated install Cabal at a location you desire
|
||||
2. isolated install Cabal at a location you desire
|
||||
- `ghcup install cabal --isolate /home/username/my_isolated_dir/`
|
||||
|
||||
3. do an isolated install with a custom bindist
|
||||
- `ghcup install ghc --isolate /home/username/my_isolated_dir/ -u 'https://gitlab.haskell.org/api/v4/projects/1/jobs/artifacts/master/raw/ghc-x86_64-fedora27-linux.tar.xz?job=validate-x86_64-linux-fedora27' head`
|
||||
3. do an isolated install with a custom bindist
|
||||
- `ghcup install ghc --isolate /home/username/my_isolated_dir/ -u 'https://gitlab.haskell.org/ghc/ghc/-/jobs/artifacts/master/raw/ghc-x86_64-linux-fedora33-release.tar.xz?job=x86_64-linux-fedora33-release' head`
|
||||
|
||||
4. isolated install HLS
|
||||
4. isolated install HLS
|
||||
- `ghcup install hls --isolate /home/username/dir/hls/`
|
||||
|
||||
5. you can even compile ghc to an isolated location.
|
||||
- `ghcup compile ghc -j 4 -v 9.0.1 -b 8.10.5 -i /home/username/my/dir/ghc`
|
||||
5. you can even compile ghc to an isolated location.
|
||||
- `ghcup compile ghc -j 4 -v 9.0.1 -b 8.10.5 -i /home/username/my/dir/ghc`
|
||||
|
||||
## Continuous integration
|
||||
|
||||
On windows, ghcup can be installed automatically on a CI runner non-interactively like so:
|
||||
On Windows, GHCup can be installed automatically on a CI runner
|
||||
non-interactively, as below. The paramaters to the PowerShell script are
|
||||
specified positionally, after `-ArgumentList`:
|
||||
|
||||
```ps
|
||||
Set-ExecutionPolicy Bypass -Scope Process -Force;[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072;Invoke-Command -ScriptBlock ([ScriptBlock]::Create((Invoke-WebRequest https://www.haskell.org/ghcup/sh/bootstrap-haskell.ps1 -UseBasicParsing))) -ArgumentList $false,$true,$true,$false,$false,$false,$false,"C:\"
|
||||
$ErrorActionPreference = 'Stop';Set-ExecutionPolicy Bypass -Scope Process -Force;[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072;try { Invoke-Command -ScriptBlock ([ScriptBlock]::Create((Invoke-WebRequest https://www.haskell.org/ghcup/sh/bootstrap-haskell.ps1 -UseBasicParsing))) -ArgumentList $false,$true,$true,$false,$false,$false,$false,"C:\" } catch { Write-Error $_ }
|
||||
```
|
||||
|
||||
`$ErrorActionPreference = 'Stop'` here acts like `set -e` and stops execution if ghcup installation fails.
|
||||
|
||||
On linux/darwin/freebsd, run the following on your runner:
|
||||
|
||||
```sh
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | BOOTSTRAP_HASKELL_NONINTERACTIVE=1 BOOTSTRAP_HASKELL_MINIMAL=1 sh
|
||||
```
|
||||
|
||||
This will just install `ghcup` and on windows additionally `msys2`.
|
||||
This will just install `ghcup` and on Windows additionally MSYS2.
|
||||
|
||||
For the full list of env variables and parameters to tweak the script behavior, see:
|
||||
|
||||
* [bootstrap-haskell for linux/darwin/freebsd](https://gitlab.haskell.org/haskell/ghcup-hs/-/blob/master/scripts/bootstrap/bootstrap-haskell#L7)
|
||||
* [bootstrap-haskell.ps1 for windows](https://gitlab.haskell.org/haskell/ghcup-hs/-/blob/master/scripts/bootstrap/bootstrap-haskell.ps1#L17)
|
||||
See the installation scripts referred to above for the full list of environment
|
||||
variables and, in the case of Windows, parameters to tweak the script behavior.
|
||||
|
||||
### github workflows
|
||||
|
||||
@@ -279,10 +486,11 @@ GHCup itself is also pre-installed on all platforms, but may use non-standard in
|
||||
GHCup supports verifying the GPG signature of the metadata file. The metadata file then contains SHA256 hashes of all downloads, so
|
||||
this is cryptographically secure.
|
||||
|
||||
First, obtain the gpg key:
|
||||
First, obtain the gpg keys:
|
||||
|
||||
```sh
|
||||
gpg --batch --keyserver keys.openpgp.org --recv-keys 7784930957807690A66EBDBE3786C5262ECB4A3F
|
||||
gpg --batch --keyserver keys.openpgp.org --recv-keys 7D1E8AFD1D4A16D71FADA2F2CCC85C0E40C06A8C
|
||||
gpg --batch --keyserver keyserver.ubuntu.com --recv-keys FE5AB6C91FEA597C3B31180B73EDE9E8CFBAEF01
|
||||
```
|
||||
|
||||
Then verify the gpg key in one of these ways:
|
||||
@@ -301,7 +509,7 @@ gpg-setting: GPGLax # GPGStrict | GPGLax | GPGNone
|
||||
|
||||
In `GPGStrict` mode, ghcup will fail if verification fails. In `GPGLax` mode it will just print a warning.
|
||||
You can also pass the mode via `ghcup --gpg <strict|lax|none>`.
|
||||
|
||||
|
||||
# Tips and tricks
|
||||
|
||||
## ghcup run
|
||||
@@ -315,3 +523,34 @@ ghcup run --ghc 8.10.7 --cabal latest --hls latest --stack latest --install -- c
|
||||
```
|
||||
|
||||
This will execute vscode with GHC set to 8.10.7 and all other tools to their latest version.
|
||||
|
||||
# Troubleshooting
|
||||
|
||||
## Script immediately exits on windows
|
||||
|
||||
There are two possible reasons:
|
||||
|
||||
1. your company blocks the script (some have a whitelist)... ask your administrator
|
||||
2. your Antivirus or Windows Defender interfere with the installation. Disable them temporarily.
|
||||
|
||||
## C compiler cannot create executables
|
||||
|
||||
### Darwin
|
||||
|
||||
You need to update your XCode command line tools, e.g. [like this](https://stackoverflow.com/questions/34617452/how-to-update-xcode-from-command-line).
|
||||
|
||||
## Certificate authority errors (curl)
|
||||
|
||||
If your certificates are outdated or improperly configured, curl may be unable
|
||||
to download ghcup.
|
||||
|
||||
There are two known workarounds:
|
||||
|
||||
1. Tell curl to ignore certificate errors (dangerous): `curl -k https://raw.githubusercontent.com/haskell/ghcup-hs/master/scripts/bootstrap/bootstrap-haskell | GHCUP_CURL_OPTS="-k" sh`
|
||||
2. Try to use wget instead: `wget -O /dev/stdout https://raw.githubusercontent.com/haskell/ghcup-hs/master/scripts/bootstrap/bootstrap-haskell | BOOTSTRAP_HASKELL_DOWNLOADER=wget sh`
|
||||
|
||||
On windows, you can disable curl like so:
|
||||
|
||||
```pwsh
|
||||
Set-ExecutionPolicy Bypass -Scope Process -Force;[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072;try { Invoke-Command -ScriptBlock ([ScriptBlock]::Create((Invoke-WebRequest https://www.haskell.org/ghcup/sh/bootstrap-haskell.ps1 -UseBasicParsing))) -ArgumentList $true,$false,$false,$false,$false,$false,$false,"","","","",$true } catch { Write-Error $_ }
|
||||
```
|
||||
|
||||
@@ -4,19 +4,15 @@ hide:
|
||||
- toc
|
||||
---
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
<script src="javascripts/extra.js"></script>
|
||||
|
||||
|
||||
<section class="index-ghcup-hero">
|
||||
<img alt="haskell logo" src="./haskell_logo.png" />
|
||||
<h1>GHCup</h1>
|
||||
</section>
|
||||
|
||||
<p class="ghcup-intro">GHCup is an installer for the general purpose language <a href="https://www.haskell.org">Haskell</a>.</p>
|
||||
<p class="ghcup-intro">GHCup is the main installer for the general purpose language <a href="https://www.haskell.org">Haskell</a>.</p>
|
||||
|
||||
<div class="text-center main-buttons">
|
||||
<a href="install/" class="btn btn-primary" role="button">Getting Started</a>
|
||||
<a href="install/" class="btn btn-primary" role="button">Installation</a>
|
||||
<a href="steps/" class="btn btn-primary" role="button">First steps</a>
|
||||
<a href="guide/" class="btn btn-primary" role="button">User Guide</a>
|
||||
</div>
|
||||
@@ -35,7 +31,7 @@ hide:
|
||||
<span>
|
||||
</span>
|
||||
<div class="footer">
|
||||
<a href="https://gitlab.haskell.org/haskell/ghcup-hs/-/blob/master/scripts/bootstrap/bootstrap-haskell" target="_blank">What does this do?</a> <b> · </b> <a href="https://www.haskell.org/ghcup/install/#manual-install">I don't like curl | sh</a> <div class="show-all-platforms"><b> · </b> <a class="show-all-platforms-button" href="#">Show all platforms</a></div></p>
|
||||
<a href="https://github.com/haskell/ghcup-hs/blob/master/scripts/bootstrap/bootstrap-haskell" target="_blank">What does this do?</a> <b> · </b> <a href="https://www.haskell.org/ghcup/install/#manual-installation">I don't like curl | sh</a> <div class="show-all-platforms"><b> · </b> <a class="show-all-platforms-button" href="#">Show all platforms</a></div></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -45,19 +41,19 @@ hide:
|
||||
|
||||
<div class="command-button">
|
||||
<pre>
|
||||
<span class="ghcup-command" id="ghcup-command-windows">Set-ExecutionPolicy Bypass -Scope Process -Force;[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072;Invoke-Command -ScriptBlock ([ScriptBlock]::Create((Invoke-WebRequest https://www.haskell.org/ghcup/sh/bootstrap-haskell.ps1 -UseBasicParsing))) -ArgumentList $true
|
||||
<span class="ghcup-command" id="ghcup-command-windows">Set-ExecutionPolicy Bypass -Scope Process -Force;[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; try { Invoke-Command -ScriptBlock ([ScriptBlock]::Create((Invoke-WebRequest https://www.haskell.org/ghcup/sh/bootstrap-haskell.ps1 -UseBasicParsing))) -ArgumentList $true } catch { Write-Error $_ }
|
||||
</span>
|
||||
</pre>
|
||||
<button class="btn" onclick="copyToClipboardWin()" id="ghcup-windows-button"><i class="fa fa-copy"></i></button>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<a href="https://gitlab.haskell.org/haskell/ghcup-hs/-/blob/master/scripts/bootstrap/bootstrap-haskell.ps1" target="_blank">What does this do?</a> <b> · </b> <a href="https://www.haskell.org/ghcup/install/#manual-install">I don't like curl | sh</a> <div class="show-all-platforms"><b> · </b> <a class="show-all-platforms-button" href="#">Show all platforms</a></div></p>
|
||||
<a href="https://github.com/haskell/ghcup-hs/blob/master/scripts/bootstrap/bootstrap-haskell.ps1" target="_blank">What does this do?</a> <b> · </b> <a href="https://www.haskell.org/ghcup/install/#manual-installation">I don't like curl | sh</a> <div class="show-all-platforms"><b> · </b> <a class="show-all-platforms-button" href="#">Show all platforms</a></div></p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<p id="help" class="ghcup-help">
|
||||
Need help? Ask on
|
||||
Need help? Check the <a href="guide/#troubleshooting">Troubleshooting section</a> or ask on
|
||||
<span>
|
||||
<a href="https://kiwiirc.com/nextclient/irc.libera.chat/?nick=Guest%7C?#haskell,#haskell-ghcup">
|
||||
<img src="irc.svg" alt="" />
|
||||
@@ -77,16 +73,13 @@ hide:
|
||||
</span>
|
||||
or
|
||||
<span>
|
||||
<a href="https://gitlab.haskell.org/haskell/ghcup-hs/issues">
|
||||
<a href="https://github.com/haskell/ghcup-hs/issues">
|
||||
report a bug
|
||||
<img src="Octicons-bug.svg" alt="" />
|
||||
</a>
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<script type="text/javascript" src="javascripts/ghcup.js"></script>
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
161
docs/install.md
161
docs/install.md
@@ -1,10 +1,10 @@
|
||||
# Getting started
|
||||
# Installation
|
||||
|
||||
GHCup makes it easy to install specific versions of GHC on GNU/Linux,
|
||||
macOS (aka Darwin), FreeBSD and Windows and can also bootstrap a fresh [Haskell developer environment](./install/#supported-tools) from scratch.
|
||||
macOS (aka Darwin), FreeBSD and Windows and can also bootstrap a fresh [Haskell developer environment](./#supported-tools) from scratch.
|
||||
It follows the UNIX philosophy of [do one thing and do it well](https://en.wikipedia.org/wiki/Unix_philosophy#Do_One_Thing_and_Do_It_Well). Similar in scope to [rustup](https://github.com/rust-lang-nursery/rustup.rs), [pyenv](https://github.com/pyenv/pyenv) and [jenv](http://www.jenv.be).
|
||||
|
||||
## Installation
|
||||
## How to install
|
||||
|
||||
The following commands will download the `ghcup` binary into `~/.ghcup/bin` (or `C:\ghcup\bin` on windows) and then
|
||||
run it to interactively install the [Haskell Toolchain](#supported-tools). These commands should be run as **non-root/non-admin
|
||||
@@ -19,12 +19,12 @@ curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
|
||||
For Windows, run this in a PowerShell session:
|
||||
|
||||
```psh
|
||||
Set-ExecutionPolicy Bypass -Scope Process -Force;[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072;Invoke-Command -ScriptBlock ([ScriptBlock]::Create((Invoke-WebRequest https://www.haskell.org/ghcup/sh/bootstrap-haskell.ps1 -UseBasicParsing))) -ArgumentList $true
|
||||
Set-ExecutionPolicy Bypass -Scope Process -Force;[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; try { Invoke-Command -ScriptBlock ([ScriptBlock]::Create((Invoke-WebRequest https://www.haskell.org/ghcup/sh/bootstrap-haskell.ps1 -UseBasicParsing))) -ArgumentList $true } catch { Write-Error $_ }
|
||||
```
|
||||
|
||||
There's also a [youtube video](https://www.youtube.com/watch?v=bB4fmQiUYPw) explaining installation on windows.
|
||||
|
||||
If you want to know what these scripts do, check out the [source code at the repository](https://gitlab.haskell.org/haskell/ghcup-hs/-/tree/master/scripts/bootstrap). Advanced users may want to perform a [manual installation](#manual-install) and GPG verify the binaries.
|
||||
If you want to know what these scripts do, check out the [source code at the repository](https://github.com/haskell/ghcup-hs/tree/master/scripts/bootstrap). Advanced users may want to perform a [manual installation](#manual-installation) and GPG verify the binaries.
|
||||
|
||||
### Which versions get installed?
|
||||
|
||||
@@ -34,6 +34,55 @@ GHCup has two main channels for every tool: **recommended** and **latest**. By d
|
||||
|
||||
Also see [tags and shortcuts](../guide/#tags-and-shortcuts) for more information.
|
||||
|
||||
## System requirements
|
||||
|
||||
### Linux Debian
|
||||
|
||||
The following distro packages are required: `build-essential curl libffi-dev libffi6 libgmp-dev libgmp10 libncurses-dev libncurses5 libtinfo5`
|
||||
|
||||
### Linux Ubuntu
|
||||
|
||||
The following distro packages are required: `build-essential curl libffi-dev libffi6 libgmp-dev libgmp10 libncurses-dev libncurses5 libtinfo5`
|
||||
|
||||
### Linux Fedora
|
||||
|
||||
The following distro packages are required: `gcc gcc-c++ gmp gmp-devel make ncurses ncurses-compat-libs xz perl`
|
||||
|
||||
### Linux Mageia
|
||||
|
||||
The following distro packages are required: `curl gcc gcc-c++ gmp libffi-devel libffi7 libgmp-devel libgmp10 make libncurses-devel libncurses5 xz perl`
|
||||
|
||||
### Linux CentOS
|
||||
|
||||
The following distro packages are required: `gcc gcc-c++ gmp gmp-devel make ncurses ncurses-compat-libs xz perl`
|
||||
|
||||
### Linux Alpine
|
||||
|
||||
The following distro packages are required: `binutils-gold curl gcc g++ gmp-dev libc-dev libffi-dev make musl-dev ncurses-dev perl tar xz`
|
||||
|
||||
### Linux VoidLinux
|
||||
|
||||
The following distro packages are required: `gcc gmp curl coreutils xz ncurses make ncurses-libtinfo-libs perl tar`
|
||||
|
||||
### Linux (generic)
|
||||
|
||||
You need the following packages: curl g++ gcc gmp make ncurses realpath xz-utils. Consult your distro documentation on the exact names of those packages.
|
||||
|
||||
### Darwin
|
||||
|
||||
On OS X, in the course of running ghcup you will be given a dialog box to install the command line tools. Accept and the requirements will be installed for you. You will then need to run the command again.
|
||||
On Darwin M1 you might also need a working llvm installed (e.g. via brew) and have the toolchain exposed in PATH.
|
||||
|
||||
### FreeBSD
|
||||
|
||||
The following distro packages are required: `curl gcc gmp gmake ncurses perl5 libffi libiconv`
|
||||
|
||||
Notice that only FreeBSD 13.x is supported. If the installation fails, complaining about `libncursesw.8.so`, you will need to install FreeBSD 12 compat package first, for example, `pkg install misc/compat12x`.
|
||||
|
||||
### Windows
|
||||
|
||||
On Windows, msys2 should already have been set up during the installation, so most users should just proceed. If you are installing manually, make sure to have a working mingw64 toolchain and shell.
|
||||
|
||||
## Next steps
|
||||
|
||||
1. Follow the [First steps guide](../steps) on how to build a "Hello world" program, use `ghc`, run an interactive REPL and create a Haskell project
|
||||
@@ -55,11 +104,21 @@ GHCup supports the following tools, which are also known as the **Haskell Toolch
|
||||
<table>
|
||||
<thead><tr><th>GHC Version</th><th>Tags</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>9.2.2</td><td><span style="color:blue">latest</span>, base-4.16.1.0</td></tr>
|
||||
<tr><td>9.6.1</td><td><span style="color:blue">latest</span>, base-4.18.0.0</td></tr>
|
||||
<tr><td>9.4.4</td><td>base-4.17.0.0</td></tr>
|
||||
<tr><td>9.4.3</td><td>base-4.17.0.0</td></tr>
|
||||
<tr><td>9.4.2</td><td>base-4.17.0.0</td></tr>
|
||||
<tr><td>9.4.1</td><td>base-4.17.0.0</td></tr>
|
||||
<tr><td>9.2.7</td><td>base-4.16.4.0</td></tr>
|
||||
<tr><td>9.2.6</td><td>base-4.16.4.0</td></tr>
|
||||
<tr><td>9.2.5</td><td><span style="color:green">recommended</span>, base-4.16.4.0</td></tr>
|
||||
<tr><td>9.2.4</td><td>base-4.16.3.0</td></tr>
|
||||
<tr><td>9.2.3</td><td>base-4.16.2.0</td></tr>
|
||||
<tr><td>9.2.2</td><td>base-4.16.1.0</td></tr>
|
||||
<tr><td>9.2.1</td><td>base-4.16.0.0</td></tr>
|
||||
<tr><td>9.0.2</td><td>base-4.15.1.0</td></tr>
|
||||
<tr><td>9.0.1</td><td>base-4.15.0.0</td></tr>
|
||||
<tr><td>8.10.7</td><td><span style="color:green">recommended</span>, base-4.14.3.0</td></tr>
|
||||
<tr><td>8.10.7</td><td>base-4.14.3.0</td></tr>
|
||||
<tr><td>8.10.6</td><td>base-4.14.3.0</td></tr>
|
||||
<tr><td>8.10.5</td><td>base-4.14.2.0</td></tr>
|
||||
<tr><td>8.10.4</td><td>base-4.14.1.0</td></tr>
|
||||
@@ -90,7 +149,9 @@ GHCup supports the following tools, which are also known as the **Haskell Toolch
|
||||
<table>
|
||||
<thead><tr><th>Cabal Version</th><th>Tags</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>3.6.2.0</td><td><span style="color:blue">latest</span>, <span style="color:green">recommended</span></td></tr>
|
||||
<tr><td>3.10.1.0</td><td><span style="color:blue">latest</span></td></tr>
|
||||
<tr><td>3.8.1.0</td><td></td></tr>
|
||||
<tr><td>3.6.2.0</td><td><span style="color:green">recommended</span></td></tr>
|
||||
<tr><td>3.6.0.0</td><td></td></tr>
|
||||
<tr><td>3.4.1.0</td><td></td></tr>
|
||||
<tr><td>3.4.0.0</td><td></td></tr>
|
||||
@@ -105,7 +166,10 @@ GHCup supports the following tools, which are also known as the **Haskell Toolch
|
||||
<table>
|
||||
<thead><tr><th>HLS Version</th><th>Tags</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>1.7.0.0</td><td><span style="color:blue">latest</span>, <span style="color:green">recommended</span></td></tr>
|
||||
<tr><td>1.9.1.0</td><td><span style="color:blue">latest</span></td></tr>
|
||||
<tr><td>1.9.0.0</td><td><span style="color:green">recommended</span></td></tr>
|
||||
<tr><td>1.8.0.0</td><td></td></tr>
|
||||
<tr><td>1.7.0.0</td><td></td></tr>
|
||||
<tr><td>1.6.1.0</td><td></td></tr>
|
||||
<tr><td>1.6.0.0</td><td></td></tr>
|
||||
<tr><td>1.5.1</td><td></td></tr>
|
||||
@@ -122,7 +186,9 @@ GHCup supports the following tools, which are also known as the **Haskell Toolch
|
||||
<table>
|
||||
<thead><tr><th>Stack Version</th><th>Tags</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>2.7.5</td><td><span style="color:blue">latest</span>, <span style="color:green">recommended</span></td></tr>
|
||||
<tr><td>2.9.3</td><td><span style="color:blue">latest</span>, <span style="color:green">recommended</span></td></tr>
|
||||
<tr><td>2.9.1</td><td></td></tr>
|
||||
<tr><td>2.7.5</td><td></td></tr>
|
||||
<tr><td>2.7.3</td><td></td></tr>
|
||||
<tr><td>2.7.1</td><td></td></tr>
|
||||
<tr><td>2.5.1</td><td></td></tr>
|
||||
@@ -175,19 +241,22 @@ There are various issues with GHC itself.
|
||||
|
||||
### FreeBSD
|
||||
|
||||
Lacks some upstream bindists and may need compat libs, since most bindists are built on FreeBSD-12.
|
||||
Lacks some upstream bindists and may need compat libs (such as `misc/compat12x`).
|
||||
HLS bindists are experimental.
|
||||
Only latest FreeBSD is generally supported.
|
||||
|
||||
### Linux ARMv7/AARCH64
|
||||
|
||||
Lower availability of bindists. Stack and HLS binaries are experimental.
|
||||
|
||||
## Manual install
|
||||
## Manual installation
|
||||
|
||||
### Unix
|
||||
|
||||
Download the binary for your platform at [https://downloads.haskell.org/~ghcup/](https://downloads.haskell.org/~ghcup/)
|
||||
and place it into your `PATH` anywhere.
|
||||
|
||||
If you want to GPG verify the binaries, import the following key first: `7784930957807690A66EBDBE3786C5262ECB4A3F`.
|
||||
If you want to GPG verify the binaries, import the following keys first: `7D1E8AFD1D4A16D71FADA2F2CCC85C0E40C06A8C` and `FE5AB6C91FEA597C3B31180B73EDE9E8CFBAEF01`.
|
||||
|
||||
Then adjust your `PATH` in `~/.bashrc` (or similar, depending on your shell) like so:
|
||||
|
||||
@@ -195,14 +264,78 @@ Then adjust your `PATH` in `~/.bashrc` (or similar, depending on your shell) lik
|
||||
export PATH="$HOME/.cabal/bin:$HOME/.ghcup/bin:$PATH"
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
1. Install ghcup binary
|
||||
- choose a base directory for installation, e.g. `C:\` that has sufficient space
|
||||
- then create the directory, e.g. `C:\ghcup\bin`
|
||||
- download the binary: https://downloads.haskell.org/~ghcup/x86_64-mingw64-ghcup.exe
|
||||
- place it as `ghcup.exe` into e.g. `C:\ghcup\bin`
|
||||
2. Install MSYS2
|
||||
- download https://repo.msys2.org/distrib/msys2-x86_64-latest.exe and execute it
|
||||
- remember the installation destination you choose (default is `C:\msys64`)
|
||||
- finish the installation
|
||||
* Add environment variables and update `Path`
|
||||
- open search bar and type in "Edit the system environment variables", then open it
|
||||
- click on "Environment Variables..." at the near bottom
|
||||
- in the upper half, select `Path` variable and double click on it
|
||||
- in the new window, click "New", type in `C:\ghcup\bin` (depending on step 1.) and press enter
|
||||
- click "OK" at the bottom
|
||||
- in the upper half, click on "New..."
|
||||
- enter `GHCUP_MSYS2` under "Variable name" and the installation destination from step 2. under "Variable value"
|
||||
- click "OK" at the bottom
|
||||
- in the upper half, click on "New..."
|
||||
- enter `GHCUP_INSTALL_BASE_PREFIX` under "Variable name" and based on the installation destination from step 1. enter the device directory (default `C:\`)
|
||||
- click "OK" at the bottom
|
||||
- in the upper half, click on "New..."
|
||||
- enter `CABAL_DIR` under "Variable name" and based on the installation destination from step 1. enter the device directory + `cabal` subdir (default `C:\cabal`)
|
||||
- click "OK" at the bottom
|
||||
- click "OK" at the bottom
|
||||
- click "OK" at the bottom
|
||||
3. Install tools
|
||||
- open powershell
|
||||
- run `ghcup install ghc --set recommended`
|
||||
- run `ghcup install cabal latest`
|
||||
- run `ghcup install stack latest`
|
||||
- run `ghcup install hls latest`
|
||||
- run `cabal update`
|
||||
4. Update msys2
|
||||
- run `ghcup run -m -- pacman --noconfirm -Syuu`
|
||||
- run `ghcup run -m -- pacman --noconfirm -Syuu`
|
||||
- run `ghcup run -m -- pacman --noconfirm -S --needed curl autoconf mingw-w64-x86_64-pkgconf`
|
||||
- run `ghcup run -m -- pacman --noconfirm -S ca-certificates`
|
||||
5. Update cabal config
|
||||
- go to e.g. `C:\cabal` (based on device you picked in 1.)
|
||||
- open file `config`
|
||||
- uncomment `extra-include-dirs` (the `-- `) and add the value (depending on installation destination you chose in 2.), e.g. `C:\msys64\mingw64\include`... so the final line should be `extra-include-dirs: C:\msys64\mingw64\include`
|
||||
- uncomment `extra-lib-dirs` and do the same, adding `C:\msys64\mingw64\lib`
|
||||
- uncomment `extra-prog-path` and set it to `C:\ghcup\bin, C:\cabal\bin, C:\msys64\mingw64\bin, C:\msys64\usr\bin`, depending on your install destinations from 1. and 2.
|
||||
6. Set up msys2 shell
|
||||
- run `ghcup run -m -- sed -i -e 's/db_home:.*$/db_home: windows/' /etc/nsswitch.conf` to make the HOME in your msys2 shell match the one from windows
|
||||
- make a desktop shortcut from `C:\msys64\msys2_shell.cmd`, which will allow you to start a proper msys2 shell
|
||||
- run `ghcup run -m -- sed -i -e 's/#MSYS2_PATH_TYPE=.*/MSYS2_PATH_TYPE=inherit/' /c/msys64/msys2.ini`
|
||||
- run `ghcup run -m -- sed -i -e 's/rem set MSYS2_PATH_TYPE=inherit/set MSYS2_PATH_TYPE=inherit/' /c/msys64/msys2_shell.cmd`
|
||||
|
||||
All set. You can run `cabal init` now in an empty directory to start a project.
|
||||
|
||||
## Vim integration
|
||||
|
||||
See [ghcup.vim](https://github.com/hasufell/ghcup.vim).
|
||||
|
||||
## VSCode integration
|
||||
The developers of the Haskell Language Server offer an [extension](https://github.com/haskell/vscode-haskell) tightly integrated with the [Haskell Language Server](https://github.com/haskell/haskell-language-server). To get started:
|
||||
|
||||
1. Install GHCup. During installation, opt in to install the Haskell Language Server (HLS).
|
||||
2. Install the extension (from VSCode: Ctrl + P and then `ext install haskell.haskell`).
|
||||
3. Make sure your project uses the GHC version installed from GHCup (otherwise HLS is likely to fail on launch):
|
||||
- instructions for [stack](https://docs.haskellstack.org/en/stable/yaml_configuration/#system-ghc)
|
||||
|
||||
On Linux, some users have reported an issue when VSCode is not launched from a terminal ("cannot find ghc version"). A solution is to [let HLS know about your GHCup on $PATH](https://github.com/haskell/vscode-haskell#stackcabalghc-can-not-be-found).
|
||||
|
||||
## Get help
|
||||
|
||||
* [Libera IRC chat on #haskell-ghcup or #haskell](https://kiwiirc.com/nextclient/irc.libera.chat/?nick=Guest%7C?#haskell,#haskell-ghcup)
|
||||
* [GHCup issue tracker](https://gitlab.haskell.org/haskell/ghcup-hs/issues)
|
||||
* [GHCup issue tracker](https://github.com/haskell/ghcup-hs/issues/new)
|
||||
* [Matrix](https://app.element.io/#/room/#haskell-tooling:matrix.org)
|
||||
* [Discord](https://discord.gg/pKYf3zDQU7)
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 40 KiB |
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 40 KiB |
@@ -1,201 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ config.theme.locale|default('en') }}">
|
||||
<head>
|
||||
{%- block site_meta %}
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
{% if page and page.is_homepage %}<meta name="description" content="{{ config['site_description'] }}">{% endif %}
|
||||
{% if config.site_author %}<meta name="author" content="{{ config.site_author }}">{% endif %}
|
||||
{% if page and page.canonical_url %}<link rel="canonical" href="{{ page.canonical_url }}">{% endif %}
|
||||
{% if config.site_favicon %}<link rel="shortcut icon" href="{{ config.site_favicon|url }}">
|
||||
{% else %}<link rel="shortcut icon" href="{{ 'img/favicon.ico'|url }}">{% endif %}
|
||||
{%- endblock %}
|
||||
|
||||
{%- block htmltitle %}
|
||||
<title>{% if page and page.title and not page.is_homepage %}{{ page.title }} - {% endif %}{{ config.site_name }}</title>
|
||||
{%- endblock %}
|
||||
|
||||
{%- block styles %}
|
||||
<link href="{{ 'css/bootstrap.min.css'|url }}" rel="stylesheet">
|
||||
<link href="{{ 'css/font-awesome.min.css'|url }}" rel="stylesheet">
|
||||
<link href="{{ 'css/base.css'|url }}" rel="stylesheet">
|
||||
{%- if config.theme.highlightjs %}
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.5.0/styles/{{ config.theme.hljs_style }}.min.css">
|
||||
{%- endif %}
|
||||
{%- for path in extra_css %}
|
||||
<link href="{{ path }}" rel="stylesheet">
|
||||
{%- endfor %}
|
||||
{%- endblock %}
|
||||
|
||||
{%- block libs %}
|
||||
|
||||
<script src="{{ 'js/jquery-1.10.2.min.js'|url }}" defer></script>
|
||||
<script src="{{ 'js/bootstrap.min.js'|url }}" defer></script>
|
||||
{%- if config.theme.highlightjs %}
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.5.0/highlight.min.js"></script>
|
||||
{%- for lang in config.theme.hljs_languages %}
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.5.0/languages/{{lang}}.min.js"></script>
|
||||
{%- endfor %}
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
{%- endif %}
|
||||
{%- endblock %}
|
||||
|
||||
{%- block analytics %}
|
||||
{%- if config.theme.analytics.gtag %}
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id={{ config.theme.analytics.gtag }}"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', '{{ config.theme.analytics.gtag }}');
|
||||
</script>
|
||||
{%- elif config.google_analytics %}
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', '{{ config.google_analytics[0] }}', '{{ config.google_analytics[1] }}');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
{%- endif %}
|
||||
{%- endblock %}
|
||||
|
||||
{%- block extrahead %} {% endblock %}
|
||||
</head>
|
||||
|
||||
<body{% if page and page.is_homepage %} class="homepage"{% endif %}>
|
||||
<div class="navbar fixed-top navbar-expand-lg navbar-{% if config.theme.nav_style == "light" %}light{% else %}dark{% endif %} bg-{{ config.theme.nav_style }}">
|
||||
<div class="container">
|
||||
|
||||
{%- block site_name %}
|
||||
<a class="navbar-brand" href="{{ nav.homepage.url|url }}">{{ config.site_name }}</a>
|
||||
{%- endblock %}
|
||||
|
||||
{%- if nav|length>1 or (page and (page.next_page or page.previous_page)) or config.repo_url %}
|
||||
<!-- Expander button -->
|
||||
<button type="button" class="navbar-toggler" data-toggle="collapse" data-target="#navbar-collapse">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
{%- endif %}
|
||||
|
||||
<!-- Expanded navigation -->
|
||||
<div id="navbar-collapse" class="navbar-collapse collapse">
|
||||
{%- block site_nav %}
|
||||
{%- if nav|length>1 %}
|
||||
<!-- Main navigation -->
|
||||
<ul class="nav navbar-nav">
|
||||
{%- for nav_item in nav %}
|
||||
{%- if nav_item.children %}
|
||||
<li class="dropdown{% if nav_item.active %} active{% endif %}">
|
||||
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown">{{ nav_item.title }} <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
{%- for nav_item in nav_item.children %}
|
||||
{% include "nav-sub.html" %}
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{%- else %}
|
||||
<li class="navitem{% if nav_item.active %} active{% endif %}">
|
||||
<a href="{{ nav_item.url|url }}" class="nav-link">{{ nav_item.title }}</a>
|
||||
</li>
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
{%- endif %}
|
||||
{%- endblock %}
|
||||
|
||||
<ul class="nav navbar-nav ml-auto">
|
||||
{%- block search_button %}
|
||||
{%- if 'search' in config['plugins'] %}
|
||||
<li class="nav-item">
|
||||
<a href="#" class="nav-link" data-toggle="modal" data-target="#mkdocs_search_modal">
|
||||
<i class="fa fa-search"></i> {% trans %}Search{% endtrans %}
|
||||
</a>
|
||||
</li>
|
||||
{%- endif %}
|
||||
{%- endblock %}
|
||||
|
||||
{%- block next_prev %}
|
||||
{%- endblock %}
|
||||
|
||||
{%- block repo %}
|
||||
{%- if page and page.edit_url %}
|
||||
<li class="nav-item">
|
||||
<a href="{{ page.edit_url }}" class="nav-link">
|
||||
{%- if config.repo_name == 'GitHub' -%}
|
||||
<i class="fa fa-github"></i> {% trans repo_name=config.repo_name %}Edit on {{ repo_name }}{% endtrans %}
|
||||
{%- elif config.repo_name == 'Bitbucket' -%}
|
||||
<i class="fa fa-bitbucket"></i> {% trans repo_name=config.repo_name %}Edit on {{ repo_name }}{% endtrans %}
|
||||
{%- elif config.repo_name == 'GitLab' -%}
|
||||
<i class="fa fa-gitlab"></i> {% trans repo_name=config.repo_name %}Edit on {{ repo_name }}{% endtrans %}
|
||||
{%- else -%}
|
||||
{% trans repo_name=config.repo_name%}Edit on {{ repo_name }}{% endtrans %}
|
||||
{%- endif -%}
|
||||
</a>
|
||||
</li>
|
||||
{%- elif config.repo_url %}
|
||||
<li class="nav-item">
|
||||
<a href="{{ config.repo_url }}" class="nav-link">
|
||||
{%- if config.repo_name == 'GitHub' -%}
|
||||
<i class="fa fa-github"></i> {{ config.repo_name }}
|
||||
{%- elif config.repo_name == 'Bitbucket' -%}
|
||||
<i class="fa fa-bitbucket"></i> {{ config.repo_name }}
|
||||
{%- elif config.repo_name == 'GitLab' -%}
|
||||
<i class="fa fa-gitlab"></i> {{ config.repo_name }}
|
||||
{%- else -%}
|
||||
{{ config.repo_name }}
|
||||
{%- endif -%}
|
||||
</a>
|
||||
</li>
|
||||
{%- endif %}
|
||||
{%- endblock %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
{%- block content %}
|
||||
<div class="col-md-3">{% include "toc.html" %}</div>
|
||||
<div class="col-md-9" role="main">{% include "content.html" %}</div>
|
||||
{%- endblock %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="col-md-12">
|
||||
{%- block footer %}
|
||||
<hr>
|
||||
{%- if config.copyright %}
|
||||
<p>{{ config.copyright }}</p>
|
||||
{%- endif %}
|
||||
<p>{% trans mkdocs_link='<a href="https://www.mkdocs.org/">MkDocs</a>' %}Documentation built with {{ mkdocs_link }}.{% endtrans %}</p>
|
||||
{%- endblock %}
|
||||
</footer>
|
||||
|
||||
{%- block scripts %}
|
||||
<script>
|
||||
var base_url = {{ base_url | tojson }},
|
||||
shortcuts = {{ config.theme.shortcuts | tojson }};
|
||||
</script>
|
||||
<script src="{{ 'js/base.js'|url }}" defer></script>
|
||||
{%- for path in extra_javascript %}
|
||||
<script src="{{ path }}" defer></script>
|
||||
{%- endfor %}
|
||||
{%- endblock %}
|
||||
|
||||
{% if 'search' in config['plugins'] %}{%- include "search-modal.html" %}{% endif %}
|
||||
{%- include "keyboard-modal.html" %}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
{% if page and page.is_homepage %}
|
||||
<!--
|
||||
MkDocs version : {{ mkdocs_version }}
|
||||
Build Date UTC : {{ build_date_utc }}
|
||||
-->
|
||||
{% endif %}
|
||||
BIN
docs/overrides/img/favicon.ico
Normal file
BIN
docs/overrides/img/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
4
docs/overrides/main.html
Normal file
4
docs/overrides/main.html
Normal file
@@ -0,0 +1,4 @@
|
||||
{% extends "base.html" %}
|
||||
<!-- Get rid of the next/prev buttons -->
|
||||
{% block next_prev %}
|
||||
{% endblock %}
|
||||
@@ -177,7 +177,7 @@ A more thorough introduction to GHCi can be found in the
|
||||
### Using external packages in ghci
|
||||
|
||||
By default, GHCi can only load and use packages that are
|
||||
[included with the GHC installation](https://downloads.haskell.org/ghc/latest/docs/html/users_guide/9.2.2-notes.html#included-libraries).
|
||||
[included with the GHC installation](https://downloads.haskell.org/ghc/9.4.2/docs/users_guide/9.4.2-notes.html#included-libraries).
|
||||
|
||||
However, users of the [cabal-install](https://www.haskell.org/cabal) and
|
||||
[stack](http://haskellstack.org) build tools can download and load external packages
|
||||
@@ -335,7 +335,7 @@ see the [Cabal user guide](https://cabal.readthedocs.io/en/stable/getting-starte
|
||||
|
||||
To learn Haskell, try any of those:
|
||||
|
||||
- A beginner friendly [4-lectures course](https://github.com/haskell-beginners-2022/course-plan) with exercises (by [Kowainik](https://kowainik.github.io/))
|
||||
- A beginner friendly [4-lectures course](https://github.com/haskell-beginners-2022/course-plan) with exercises (by [Dmitrii Kovanikov](https://kodimensional.dev/))
|
||||
- An in-depth university [CIS 194 Haskell course](https://www.cis.upenn.edu/~cis194/spring13/) including exercises (by [Brent Yorgey](https://byorgey.wordpress.com/))
|
||||
|
||||
## Projects to contribute to
|
||||
@@ -343,7 +343,7 @@ To learn Haskell, try any of those:
|
||||
* [https://github.com/haskell/haskell-language-server](https://github.com/haskell/haskell-language-server)
|
||||
* [https://github.com/haskell/cabal](https://github.com/haskell/cabal)
|
||||
* [https://github.com/commercialhaskell/stack](https://github.com/commercialhaskell/stack)
|
||||
* [https://gitlab.haskell.org/haskell/ghcup-hs](https://gitlab.haskell.org/haskell/ghcup-hs)
|
||||
* [https://github.com/haskell/ghcup-hs](https://github.com/haskell/ghcup-hs)
|
||||
* [https://github.com/jgm/pandoc](https://github.com/jgm/pandoc)
|
||||
* [https://github.com/simonmichael/hledger](https://github.com/simonmichael/hledger)
|
||||
* [https://github.com/koalaman/shellcheck](https://github.com/koalaman/shellcheck)
|
||||
|
||||
189
ghcup.cabal
189
ghcup.cabal
@@ -1,13 +1,13 @@
|
||||
cabal-version: 3.0
|
||||
cabal-version: 2.4
|
||||
name: ghcup
|
||||
version: 0.1.17.7
|
||||
version: 0.1.19.2
|
||||
license: LGPL-3.0-only
|
||||
license-file: LICENSE
|
||||
copyright: Julian Ospald 2020
|
||||
maintainer: hasufell@posteo.de
|
||||
author: Julian Ospald
|
||||
homepage: https://gitlab.haskell.org/haskell/ghcup-hs
|
||||
bug-reports: https://gitlab.haskell.org/haskell/ghcup-hs/issues
|
||||
homepage: https://github.com/haskell/ghcup-hs
|
||||
bug-reports: https://github.com/haskell/ghcup-hs/issues/
|
||||
synopsis: ghc toolchain installer
|
||||
description:
|
||||
A rewrite of the shell script ghcup, for providing
|
||||
@@ -21,13 +21,18 @@ extra-doc-files:
|
||||
README.md
|
||||
|
||||
extra-source-files:
|
||||
cbits/dirutils.c
|
||||
cbits/dirutils.h
|
||||
data/build_mk/cross
|
||||
data/build_mk/default
|
||||
test/golden/GHCupInfo.json
|
||||
test/data/dir/.keep
|
||||
test/data/file
|
||||
test/golden/unix/GHCupInfo.json
|
||||
test/golden/windows/GHCupInfo.json
|
||||
|
||||
source-repository head
|
||||
type: git
|
||||
location: https://gitlab.haskell.org/haskell/ghcup-hs.git
|
||||
location: https://github.com/haskell/ghcup-hs.git
|
||||
|
||||
flag tui
|
||||
description:
|
||||
@@ -44,38 +49,40 @@ flag internal-downloader
|
||||
manual: True
|
||||
|
||||
flag no-exe
|
||||
description: Don't build any executables
|
||||
default: False
|
||||
manual: True
|
||||
|
||||
flag disable-upgrade
|
||||
description:
|
||||
Disable upgrade functionality. This is mainly to support brew packagers.
|
||||
|
||||
description: Don't build any executables
|
||||
default: False
|
||||
manual: True
|
||||
|
||||
library
|
||||
exposed-modules:
|
||||
GHCup
|
||||
GHCup.Cabal
|
||||
GHCup.Download
|
||||
GHCup.Download.Utils
|
||||
GHCup.Errors
|
||||
GHCup.GHC
|
||||
GHCup.HLS
|
||||
GHCup.List
|
||||
GHCup.Platform
|
||||
GHCup.Prelude
|
||||
GHCup.Prelude.File
|
||||
GHCup.Prelude.File.Search
|
||||
GHCup.Prelude.Internal
|
||||
GHCup.Prelude.Logger
|
||||
GHCup.Prelude.Logger.Internal
|
||||
GHCup.Prelude.MegaParsec
|
||||
GHCup.Prelude.Process
|
||||
GHCup.Prelude.String.QQ
|
||||
GHCup.Prelude.Version.QQ
|
||||
GHCup.Prompts
|
||||
GHCup.Requirements
|
||||
GHCup.Stack
|
||||
GHCup.Types
|
||||
GHCup.Types.JSON
|
||||
GHCup.Types.JSON.Utils
|
||||
GHCup.Types.Optics
|
||||
GHCup.Utils
|
||||
GHCup.Utils.Dirs
|
||||
GHCup.Utils.File
|
||||
GHCup.Utils.File.Common
|
||||
GHCup.Utils.Logger
|
||||
GHCup.Utils.MegaParsec
|
||||
GHCup.Utils.Prelude
|
||||
GHCup.Utils.String.QQ
|
||||
GHCup.Utils.Version.QQ
|
||||
GHCup.Version
|
||||
|
||||
hs-source-dirs: lib
|
||||
@@ -107,8 +114,8 @@ library
|
||||
, base >=4.12 && <5
|
||||
, base16-bytestring >=0.1.1.6 && <1.1
|
||||
, binary ^>=0.8.6.0
|
||||
, bytestring ^>=0.10
|
||||
, Cabal ^>=3.6.2.0
|
||||
, bytestring >=0.10 && <0.12
|
||||
, Cabal ^>=3.0.0.0 || ^>=3.2.0.0 || ^>=3.4.0.0 || ^>=3.6.0.0
|
||||
, case-insensitive ^>=1.2.1.0
|
||||
, casing ^>=0.1.4.1
|
||||
, containers ^>=0.6
|
||||
@@ -116,6 +123,7 @@ library
|
||||
, deepseq ^>=1.4.4.0
|
||||
, directory ^>=1.3.6.0
|
||||
, disk-free-space ^>=0.1.0.1
|
||||
, exceptions ^>=0.10
|
||||
, filepath ^>=1.4.2.1
|
||||
, haskus-utils-types ^>=1.5
|
||||
, haskus-utils-variant ^>=3.2.1
|
||||
@@ -129,15 +137,16 @@ library
|
||||
, pretty-terminal ^>=0.1.0.0
|
||||
, regex-posix ^>=0.96
|
||||
, resourcet ^>=1.2.2
|
||||
, retry ^>=0.8.1.2
|
||||
, retry ^>=0.8.1.2 || ^>=0.9
|
||||
, safe ^>=0.3.18
|
||||
, safe-exceptions ^>=0.1
|
||||
, split ^>=0.2.3.4
|
||||
, streamly ^>=0.8.2
|
||||
, strict-base ^>=0.4
|
||||
, template-haskell >=2.7 && <2.18
|
||||
, template-haskell >=2.7 && <2.20
|
||||
, temporary ^>=1.3
|
||||
, text ^>=1.2.4.0
|
||||
, time ^>=1.9.3
|
||||
, text ^>=2.0
|
||||
, time ^>=1.9.3 || ^>=1.10 || ^>=1.11
|
||||
, transformers ^>=0.5
|
||||
, unliftio-core ^>=0.2.0.1
|
||||
, unordered-containers ^>=0.2.10.0
|
||||
@@ -152,38 +161,45 @@ library
|
||||
exposed-modules: GHCup.Download.IOStreams
|
||||
cpp-options: -DINTERNAL_DOWNLOADER
|
||||
build-depends:
|
||||
, HsOpenSSL >=0.11.4.18
|
||||
, HsOpenSSL >=0.11.7.2
|
||||
, http-io-streams >=0.1.2.0
|
||||
, io-streams >=1.5.2.1
|
||||
, terminal-progress-bar >=0.4.1
|
||||
|
||||
if os(windows)
|
||||
cpp-options: -DIS_WINDOWS
|
||||
cpp-options: -DIS_WINDOWS
|
||||
other-modules:
|
||||
GHCup.Utils.File.Windows
|
||||
GHCup.Utils.Prelude.Windows
|
||||
GHCup.Utils.Windows
|
||||
GHCup.Prelude.File.Windows
|
||||
GHCup.Prelude.Windows
|
||||
|
||||
-- GHCup.OptParse.Run uses this
|
||||
exposed-modules: GHCup.Prelude.Process.Windows
|
||||
build-depends:
|
||||
, bzlib
|
||||
, process ^>=1.6.11.0
|
||||
, Win32 ^>=2.10
|
||||
, Win32 >=2.10
|
||||
|
||||
else
|
||||
other-modules:
|
||||
GHCup.Utils.File.Posix
|
||||
GHCup.Utils.Posix
|
||||
GHCup.Utils.Prelude.Posix
|
||||
GHCup.Prelude.File.Posix
|
||||
GHCup.Prelude.File.Posix.Foreign
|
||||
GHCup.Prelude.Posix
|
||||
GHCup.Prelude.Process.Posix
|
||||
|
||||
exposed-modules: GHCup.Prelude.File.Posix.Traversals
|
||||
include-dirs: cbits
|
||||
includes: dirutils.h
|
||||
install-includes: dirutils.h
|
||||
c-sources: cbits/dirutils.c
|
||||
build-depends:
|
||||
, bz2 >=0.5.0.5 && <1.1
|
||||
, terminal-size ^>=0.3.2.1
|
||||
, terminal-size ^>=0.3.3
|
||||
, 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
|
||||
build-depends: vty ^>=5.37
|
||||
|
||||
executable ghcup
|
||||
main-is: Main.hs
|
||||
@@ -202,8 +218,10 @@ executable ghcup
|
||||
GHCup.OptParse.Rm
|
||||
GHCup.OptParse.Run
|
||||
GHCup.OptParse.Set
|
||||
GHCup.OptParse.Test
|
||||
GHCup.OptParse.ToolRequirements
|
||||
GHCup.OptParse.UnSet
|
||||
GHCup.OptParse.Upgrade
|
||||
GHCup.OptParse.Whereis
|
||||
|
||||
hs-source-dirs: app/ghcup
|
||||
@@ -223,38 +241,41 @@ executable ghcup
|
||||
-fwarn-incomplete-record-updates -threaded
|
||||
|
||||
build-depends:
|
||||
, aeson >=1.4
|
||||
, aeson-pretty ^>=0.8.8
|
||||
, async ^>=2.2.3
|
||||
, base >=4.12 && <5
|
||||
, bytestring ^>=0.10
|
||||
, cabal-plan ^>=0.7.2
|
||||
, containers ^>=0.6
|
||||
, deepseq ^>=1.4
|
||||
, directory ^>=1.3.6.0
|
||||
, filepath ^>=1.4.2.1
|
||||
, aeson >=1.4
|
||||
, aeson-pretty ^>=0.8.8
|
||||
, async ^>=2.2.3
|
||||
, base >=4.12 && <5
|
||||
, bytestring >=0.10 && <0.12
|
||||
, cabal-install-parsers >=0.4.5
|
||||
, cabal-plan ^>=0.7.2
|
||||
, containers ^>=0.6
|
||||
, deepseq ^>=1.4
|
||||
, directory ^>=1.3.6.0
|
||||
, filepath ^>=1.4.2.1
|
||||
, ghcup
|
||||
, haskus-utils-variant ^>=3.2.1
|
||||
, libarchive ^>=3.0.3.0
|
||||
, megaparsec >=8.0.0 && <9.3
|
||||
, mtl ^>=2.2
|
||||
, optparse-applicative >=0.15.1.0 && <0.18
|
||||
, pretty ^>=1.1.3.1
|
||||
, pretty-terminal ^>=0.1.0.0
|
||||
, process ^>=1.6.11.0
|
||||
, resourcet ^>=1.2.2
|
||||
, safe ^>=0.3.18
|
||||
, safe-exceptions ^>=0.1
|
||||
, tagsoup ^>=0.14
|
||||
, template-haskell >=2.7 && <2.18
|
||||
, temporary ^>=1.3
|
||||
, text ^>=1.2.4.0
|
||||
, unordered-containers ^>=0.2
|
||||
, uri-bytestring ^>=0.3.2.2
|
||||
, utf8-string ^>=1.0
|
||||
, vector ^>=0.12
|
||||
, versions >=4.0.1 && <5.1
|
||||
, yaml-streamly ^>=0.12.0
|
||||
, haskus-utils-types ^>=1.5
|
||||
, haskus-utils-variant ^>=3.2.1
|
||||
, libarchive ^>=3.0.3.0
|
||||
, megaparsec >=8.0.0 && <9.3
|
||||
, mtl ^>=2.2
|
||||
, optparse-applicative >=0.15.1.0 && <0.18
|
||||
, pretty ^>=1.1.3.1
|
||||
, pretty-terminal ^>=0.1.0.0
|
||||
, process ^>=1.6.11.0
|
||||
, resourcet ^>=1.2.2
|
||||
, safe ^>=0.3.18
|
||||
, safe-exceptions ^>=0.1
|
||||
, tagsoup ^>=0.14
|
||||
, template-haskell >=2.7 && <2.20
|
||||
, temporary ^>=1.3
|
||||
, text ^>=2.0
|
||||
, time ^>=1.9.3 || ^>=1.10 || ^>=1.11
|
||||
, unordered-containers ^>=0.2
|
||||
, uri-bytestring ^>=0.3.2.2
|
||||
, utf8-string ^>=1.0
|
||||
, vector ^>=0.12
|
||||
, versions >=4.0.1 && <5.1
|
||||
, yaml-streamly ^>=0.12.0
|
||||
|
||||
if flag(internal-downloader)
|
||||
cpp-options: -DINTERNAL_DOWNLOADER
|
||||
@@ -263,10 +284,10 @@ executable ghcup
|
||||
cpp-options: -DBRICK
|
||||
other-modules: BrickMain
|
||||
build-depends:
|
||||
, brick ^>=0.64
|
||||
, brick ^>=1.5
|
||||
, transformers ^>=0.5
|
||||
, unix ^>=2.7
|
||||
, vty >=5.28.2 && <5.34
|
||||
, vty ^>=5.37
|
||||
|
||||
if os(windows)
|
||||
cpp-options: -DIS_WINDOWS
|
||||
@@ -277,12 +298,6 @@ executable ghcup
|
||||
if flag(no-exe)
|
||||
buildable: False
|
||||
|
||||
if flag(disable-upgrade)
|
||||
cpp-options: -DDISABLE_UPGRADE
|
||||
|
||||
else
|
||||
other-modules: GHCup.OptParse.Upgrade
|
||||
|
||||
test-suite ghcup-test
|
||||
type: exitcode-stdio-1.0
|
||||
main-is: Main.hs
|
||||
@@ -290,7 +305,9 @@ test-suite ghcup-test
|
||||
hs-source-dirs: test
|
||||
other-modules:
|
||||
GHCup.ArbitraryTypes
|
||||
GHCup.Prelude.File.Posix.TraversalsSpec
|
||||
GHCup.Types.JSONSpec
|
||||
GHCup.Utils.FileSpec
|
||||
Spec
|
||||
|
||||
default-language: Haskell2010
|
||||
@@ -308,14 +325,24 @@ test-suite ghcup-test
|
||||
|
||||
build-depends:
|
||||
, base >=4.12 && <5
|
||||
, bytestring ^>=0.10
|
||||
, bytestring >=0.10 && <0.12
|
||||
, containers ^>=0.6
|
||||
, generic-arbitrary >=0.1.0 && <0.3
|
||||
, directory ^>=1.3.6.0
|
||||
, filepath ^>=1.4.2.1
|
||||
, generic-arbitrary >=0.1.0 && <0.2.1 || >=0.2.2 && <0.3
|
||||
, ghcup
|
||||
, hspec >=2.7.10 && <2.10
|
||||
, hspec >=2.7.10 && <2.11
|
||||
, hspec-golden-aeson ^>=0.9
|
||||
, QuickCheck ^>=2.14.1
|
||||
, quickcheck-arbitrary-adt ^>=0.3.1.0
|
||||
, text ^>=1.2.4.0
|
||||
, streamly ^>=0.8.2
|
||||
, time ^>=1.9.3 || ^>=1.10 || ^>=1.11
|
||||
, text ^>=2.0
|
||||
, uri-bytestring ^>=0.3.2.2
|
||||
, versions >=4.0.1 && <5.1
|
||||
|
||||
if os(windows)
|
||||
cpp-options: -DIS_WINDOWS
|
||||
|
||||
else
|
||||
build-depends: unix ^>=2.7
|
||||
|
||||
2773
lib/GHCup.hs
2773
lib/GHCup.hs
File diff suppressed because it is too large
Load Diff
285
lib/GHCup/Cabal.hs
Normal file
285
lib/GHCup/Cabal.hs
Normal file
@@ -0,0 +1,285 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE DataKinds #-}
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
{-|
|
||||
Module : GHCup.Cabal
|
||||
Description : GHCup installation functions for Cabal
|
||||
Copyright : (c) Julian Ospald, 2020
|
||||
License : LGPL-3.0
|
||||
Maintainer : hasufell@hasufell.de
|
||||
Stability : experimental
|
||||
Portability : portable
|
||||
-}
|
||||
module GHCup.Cabal where
|
||||
|
||||
import GHCup.Download
|
||||
import GHCup.Errors
|
||||
import GHCup.Types
|
||||
import GHCup.Types.JSON ( )
|
||||
import GHCup.Types.Optics
|
||||
import GHCup.Utils
|
||||
import GHCup.Prelude
|
||||
import GHCup.Prelude.File
|
||||
import GHCup.Prelude.Logger
|
||||
|
||||
import Codec.Archive ( ArchiveResult )
|
||||
import Control.Applicative
|
||||
import Control.Exception.Safe
|
||||
import Control.Monad
|
||||
#if !MIN_VERSION_base(4,13,0)
|
||||
import Control.Monad.Fail ( MonadFail )
|
||||
#endif
|
||||
import Control.Monad.Reader
|
||||
import Control.Monad.Trans.Resource
|
||||
hiding ( throwM )
|
||||
import Data.Either
|
||||
import Data.List
|
||||
import Data.Maybe
|
||||
import Data.Versions hiding ( patch )
|
||||
import Haskus.Utils.Variant.Excepts
|
||||
import Optics
|
||||
import Prelude hiding ( abs
|
||||
, writeFile
|
||||
)
|
||||
import Safe hiding ( at )
|
||||
import System.FilePath
|
||||
import System.IO.Error
|
||||
|
||||
import qualified Data.Text as T
|
||||
|
||||
|
||||
|
||||
-------------------------
|
||||
--[ Tool installation ]--
|
||||
-------------------------
|
||||
|
||||
|
||||
-- | Like 'installCabalBin', except takes the 'DownloadInfo' as
|
||||
-- argument instead of looking it up from 'GHCupDownloads'.
|
||||
installCabalBindist :: ( MonadMask m
|
||||
, MonadCatch m
|
||||
, MonadReader env m
|
||||
, HasPlatformReq env
|
||||
, HasDirs env
|
||||
, HasSettings env
|
||||
, HasLog env
|
||||
, MonadResource m
|
||||
, MonadIO m
|
||||
, MonadUnliftIO m
|
||||
, MonadFail m
|
||||
)
|
||||
=> DownloadInfo
|
||||
-> Version
|
||||
-> InstallDir
|
||||
-> Bool -- ^ Force install
|
||||
-> Excepts
|
||||
'[ AlreadyInstalled
|
||||
, CopyError
|
||||
, DigestError
|
||||
, ContentLengthError
|
||||
, GPGError
|
||||
, DownloadFailed
|
||||
, NoDownload
|
||||
, NotInstalled
|
||||
, UnknownArchive
|
||||
, TarDirDoesNotExist
|
||||
, ArchiveResult
|
||||
, FileAlreadyExistsError
|
||||
]
|
||||
m
|
||||
()
|
||||
installCabalBindist dlinfo ver installDir forceInstall = do
|
||||
lift $ logDebug $ "Requested to install cabal version " <> prettyVer ver
|
||||
|
||||
PlatformRequest {..} <- lift getPlatformReq
|
||||
Dirs {..} <- lift getDirs
|
||||
|
||||
-- check if we already have a regular cabal already installed
|
||||
regularCabalInstalled <- lift $ cabalInstalled ver
|
||||
|
||||
if
|
||||
| not forceInstall
|
||||
, regularCabalInstalled
|
||||
, GHCupInternal <- installDir -> do
|
||||
throwE $ AlreadyInstalled Cabal ver
|
||||
|
||||
| forceInstall
|
||||
, regularCabalInstalled
|
||||
, GHCupInternal <- installDir -> do
|
||||
lift $ logInfo "Removing the currently installed version first!"
|
||||
liftE $ rmCabalVer ver
|
||||
|
||||
| otherwise -> pure ()
|
||||
|
||||
|
||||
-- download (or use cached version)
|
||||
dl <- liftE $ downloadCached dlinfo Nothing
|
||||
|
||||
-- unpack
|
||||
tmpUnpack <- lift withGHCupTmpDir
|
||||
liftE $ cleanUpOnError tmpUnpack (unpackToDir (fromGHCupPath tmpUnpack) dl)
|
||||
liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromGHCupPath tmpUnpack)
|
||||
|
||||
-- the subdir of the archive where we do the work
|
||||
workdir <- fromGHCupPath <$> maybe (pure tmpUnpack) (liftE . intoSubdir tmpUnpack) (view dlSubdir dlinfo)
|
||||
|
||||
case installDir of
|
||||
IsolateDir isoDir -> do -- isolated install
|
||||
lift $ logInfo $ "isolated installing Cabal to " <> T.pack isoDir
|
||||
liftE $ installCabalUnpacked workdir (IsolateDirResolved isoDir) ver forceInstall
|
||||
|
||||
GHCupInternal -> do -- regular install
|
||||
liftE $ installCabalUnpacked workdir (GHCupBinDir binDir) ver forceInstall
|
||||
|
||||
|
||||
-- | Install an unpacked cabal distribution.Symbol
|
||||
installCabalUnpacked :: (MonadCatch m, HasLog env, MonadIO m, MonadReader env m)
|
||||
=> FilePath -- ^ Path to the unpacked cabal bindist (where the executable resides)
|
||||
-> InstallDirResolved -- ^ Path to install to
|
||||
-> Version
|
||||
-> Bool -- ^ Force Install
|
||||
-> Excepts '[CopyError, FileAlreadyExistsError] m ()
|
||||
installCabalUnpacked path inst ver forceInstall = do
|
||||
lift $ logInfo "Installing cabal"
|
||||
let cabalFile = "cabal"
|
||||
liftIO $ createDirRecursive' (fromInstallDir inst)
|
||||
let destFileName = cabalFile
|
||||
<> (case inst of
|
||||
IsolateDirResolved _ -> ""
|
||||
_ -> ("-" <>) . T.unpack . prettyVer $ ver
|
||||
)
|
||||
<> exeExt
|
||||
let destPath = fromInstallDir inst </> destFileName
|
||||
|
||||
copyFileE
|
||||
(path </> cabalFile <> exeExt)
|
||||
destPath
|
||||
(not forceInstall)
|
||||
lift $ chmod_755 destPath
|
||||
|
||||
-- | Installs cabal into @~\/.ghcup\/bin/cabal-\<ver\>@ and
|
||||
-- creates a default @cabal -> cabal-x.y.z.q@ symlink for
|
||||
-- the latest installed version.
|
||||
installCabalBin :: ( MonadMask m
|
||||
, MonadCatch m
|
||||
, MonadReader env m
|
||||
, HasPlatformReq env
|
||||
, HasGHCupInfo env
|
||||
, HasDirs env
|
||||
, HasSettings env
|
||||
, HasLog env
|
||||
, MonadResource m
|
||||
, MonadIO m
|
||||
, MonadUnliftIO m
|
||||
, MonadFail m
|
||||
)
|
||||
=> Version
|
||||
-> InstallDir
|
||||
-> Bool -- force install
|
||||
-> Excepts
|
||||
'[ AlreadyInstalled
|
||||
, CopyError
|
||||
, DigestError
|
||||
, ContentLengthError
|
||||
, GPGError
|
||||
, DownloadFailed
|
||||
, NoDownload
|
||||
, NotInstalled
|
||||
, UnknownArchive
|
||||
, TarDirDoesNotExist
|
||||
, ArchiveResult
|
||||
, FileAlreadyExistsError
|
||||
]
|
||||
m
|
||||
()
|
||||
installCabalBin ver installDir forceInstall = do
|
||||
dlinfo <- liftE $ getDownloadInfo Cabal ver
|
||||
installCabalBindist dlinfo ver installDir forceInstall
|
||||
|
||||
|
||||
-----------------
|
||||
--[ Set cabal ]--
|
||||
-----------------
|
||||
|
||||
|
||||
-- | Set the @~\/.ghcup\/bin\/cabal@ symlink.
|
||||
setCabal :: ( MonadMask m
|
||||
, MonadReader env m
|
||||
, HasDirs env
|
||||
, HasLog env
|
||||
, MonadFail m
|
||||
, MonadIO m
|
||||
, MonadUnliftIO m)
|
||||
=> Version
|
||||
-> Excepts '[NotInstalled] m ()
|
||||
setCabal ver = do
|
||||
let targetFile = "cabal-" <> T.unpack (prettyVer ver) <> exeExt
|
||||
|
||||
-- symlink destination
|
||||
Dirs {..} <- lift getDirs
|
||||
|
||||
whenM (liftIO $ not <$> doesFileExist (binDir </> targetFile))
|
||||
$ throwE
|
||||
$ NotInstalled Cabal (GHCTargetVersion Nothing ver)
|
||||
|
||||
let cabalbin = binDir </> "cabal" <> exeExt
|
||||
|
||||
-- create link
|
||||
let destL = targetFile
|
||||
lift $ createLink destL cabalbin
|
||||
|
||||
liftIO (isShadowed cabalbin) >>= \case
|
||||
Nothing -> pure ()
|
||||
Just pa -> lift $ logWarn $ T.pack $ prettyHFError (ToolShadowed Cabal pa cabalbin ver)
|
||||
|
||||
pure ()
|
||||
|
||||
unsetCabal :: ( MonadMask m
|
||||
, MonadReader env m
|
||||
, HasDirs env
|
||||
, MonadIO m)
|
||||
=> m ()
|
||||
unsetCabal = do
|
||||
Dirs {..} <- getDirs
|
||||
let cabalbin = binDir </> "cabal" <> exeExt
|
||||
hideError doesNotExistErrorType $ rmLink cabalbin
|
||||
|
||||
|
||||
----------------
|
||||
--[ Rm cabal ]--
|
||||
----------------
|
||||
|
||||
|
||||
-- | Delete a cabal version. Will try to fix the @cabal@ symlink
|
||||
-- after removal (e.g. setting it to an older version).
|
||||
rmCabalVer :: ( MonadMask m
|
||||
, MonadReader env m
|
||||
, HasDirs env
|
||||
, MonadThrow m
|
||||
, HasLog env
|
||||
, MonadIO m
|
||||
, MonadFail m
|
||||
, MonadCatch m
|
||||
, MonadUnliftIO m
|
||||
)
|
||||
=> Version
|
||||
-> Excepts '[NotInstalled] m ()
|
||||
rmCabalVer ver = do
|
||||
whenM (lift $ fmap not $ cabalInstalled ver) $ throwE (NotInstalled Cabal (GHCTargetVersion Nothing ver))
|
||||
|
||||
cSet <- lift cabalSet
|
||||
|
||||
Dirs {..} <- lift getDirs
|
||||
|
||||
let cabalFile = "cabal-" <> T.unpack (prettyVer ver) <> exeExt
|
||||
lift $ hideError doesNotExistErrorType $ recycleFile (binDir </> cabalFile)
|
||||
|
||||
when (Just ver == cSet) $ do
|
||||
cVers <- lift $ fmap rights getInstalledCabals
|
||||
case headMay . reverse . sort $ cVers of
|
||||
Just latestver -> setCabal latestver
|
||||
Nothing -> lift $ rmLink (binDir </> "cabal" <> exeExt)
|
||||
@@ -34,9 +34,10 @@ import GHCup.Types
|
||||
import GHCup.Types.Optics
|
||||
import GHCup.Types.JSON ( )
|
||||
import GHCup.Utils.Dirs
|
||||
import GHCup.Utils.File
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Utils.Prelude
|
||||
import GHCup.Prelude
|
||||
import GHCup.Prelude.File
|
||||
import GHCup.Prelude.Logger.Internal
|
||||
import GHCup.Prelude.Process
|
||||
import GHCup.Version
|
||||
|
||||
import Control.Applicative
|
||||
@@ -69,13 +70,11 @@ import Prelude hiding ( abs
|
||||
, writeFile
|
||||
)
|
||||
import Safe
|
||||
import System.Directory
|
||||
import System.Environment
|
||||
import System.Exit
|
||||
import System.FilePath
|
||||
import System.IO.Error
|
||||
import System.IO.Temp
|
||||
import Text.PrettyPrint.HughesPJClass ( prettyShow )
|
||||
import URI.ByteString
|
||||
|
||||
import qualified Crypto.Hash.SHA256 as SHA256
|
||||
@@ -114,7 +113,7 @@ getDownloadsF :: ( FromJSONKey Tool
|
||||
, MonadMask m
|
||||
)
|
||||
=> Excepts
|
||||
'[DigestError, GPGError, JSONError , DownloadFailed , FileDoesNotExistError]
|
||||
'[DigestError, ContentLengthError, GPGError, JSONError , DownloadFailed , FileDoesNotExistError]
|
||||
m
|
||||
GHCupInfo
|
||||
getDownloadsF = do
|
||||
@@ -145,7 +144,7 @@ getDownloadsF = do
|
||||
yamlFromCache :: (MonadReader env m, HasDirs env) => URI -> m FilePath
|
||||
yamlFromCache uri = do
|
||||
Dirs{..} <- getDirs
|
||||
pure (cacheDir </> (T.unpack . decUTF8Safe . urlBaseName . view pathL' $ uri))
|
||||
pure (fromGHCupPath cacheDir </> (T.unpack . decUTF8Safe . urlBaseName . view pathL' $ uri))
|
||||
|
||||
|
||||
etagsFile :: FilePath -> FilePath
|
||||
@@ -162,17 +161,21 @@ getBase :: ( MonadReader env m
|
||||
, MonadMask m
|
||||
)
|
||||
=> URI
|
||||
-> Excepts '[GPGError, DigestError, JSONError, FileDoesNotExistError] m GHCupInfo
|
||||
-> Excepts '[DownloadFailed, GPGError, DigestError, ContentLengthError, JSONError, FileDoesNotExistError] m GHCupInfo
|
||||
getBase uri = do
|
||||
Settings { noNetwork, downloader } <- lift getSettings
|
||||
Settings { noNetwork, downloader, metaMode } <- lift getSettings
|
||||
|
||||
-- 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 -> lift (warnCache (displayException e) downloader) >> pure Nothing)
|
||||
. catchE @_ @_ @'[] (\e@(DownloadFailed _) -> lift (warnCache (prettyShow e) downloader) >> pure Nothing)
|
||||
else handleIO (\e -> case metaMode of
|
||||
Strict -> throwIO e
|
||||
Lax -> lift (warnCache (displayException e) downloader) >> pure Nothing)
|
||||
. catchE @_ @_ @'[DownloadFailed] (\e@(DownloadFailed _) -> case metaMode of
|
||||
Strict -> throwE e
|
||||
Lax -> lift (warnCache (prettyHFError e) downloader) >> pure Nothing)
|
||||
. fmap Just
|
||||
. smartDl
|
||||
$ uri
|
||||
@@ -184,7 +187,7 @@ getBase uri = do
|
||||
liftE
|
||||
. onE_ (onError actualYaml)
|
||||
. lEM' @_ @_ @'[JSONError] (\(displayException -> e) -> JSONDecodeError $ unlines [e, "Consider removing " <> actualYaml <> " manually."])
|
||||
. liftIO
|
||||
. liftIO
|
||||
. Y.decodeFileEither
|
||||
$ actualYaml
|
||||
where
|
||||
@@ -229,6 +232,7 @@ getBase uri = do
|
||||
-> Excepts
|
||||
'[ DownloadFailed
|
||||
, DigestError
|
||||
, ContentLengthError
|
||||
, GPGError
|
||||
]
|
||||
m1
|
||||
@@ -242,7 +246,7 @@ getBase uri = do
|
||||
Settings { metaCache } <- lift getSettings
|
||||
|
||||
-- for local files, let's short-circuit and ignore access time
|
||||
if | scheme == "file" -> liftE $ download uri' Nothing Nothing cacheDir Nothing True
|
||||
if | scheme == "file" -> liftE $ download uri' Nothing Nothing Nothing (fromGHCupPath cacheDir) Nothing True
|
||||
| e -> do
|
||||
accessTime <- fmap utcTimeToPOSIXSeconds $ liftIO $ getAccessTime json_file
|
||||
let sinceLastAccess = utcTimeToPOSIXSeconds currentTime - accessTime
|
||||
@@ -258,9 +262,13 @@ getBase uri = do
|
||||
where
|
||||
dlWithMod modTime json_file = do
|
||||
let (dir, fn) = splitFileName json_file
|
||||
f <- liftE $ download uri' (Just $ over pathL' (<> ".sig") uri') Nothing dir (Just fn) True
|
||||
liftIO $ setModificationTime f modTime
|
||||
liftIO $ setAccessTime f modTime
|
||||
f <- liftE $ download uri' (Just $ over pathL' (<> ".sig") uri') Nothing Nothing dir (Just fn) True
|
||||
|
||||
-- make these failures non-fatal, also see:
|
||||
-- https://github.com/actions/runner-images/issues/7061
|
||||
handleIO (\e -> logWarn $ "setModificationTime failed with: " <> T.pack (displayException e)) $ liftIO $ setModificationTime f modTime
|
||||
handleIO (\e -> logWarn $ "setAccessTime failed with: " <> T.pack (displayException e)) $ liftIO $ setAccessTime f modTime
|
||||
|
||||
pure f
|
||||
|
||||
|
||||
@@ -324,23 +332,26 @@ download :: ( MonadReader env m
|
||||
=> URI
|
||||
-> Maybe URI -- ^ URI for gpg sig
|
||||
-> Maybe T.Text -- ^ expected hash
|
||||
-> Maybe Integer -- ^ expected content length
|
||||
-> FilePath -- ^ destination dir (ignored for file:// scheme)
|
||||
-> Maybe FilePath -- ^ optional filename
|
||||
-> Bool -- ^ whether to read an write etags
|
||||
-> Excepts '[DigestError , DownloadFailed, GPGError] m FilePath
|
||||
download uri gpgUri eDigest dest mfn etags
|
||||
| scheme == "https" = dl
|
||||
| scheme == "http" = dl
|
||||
-> Excepts '[DigestError, ContentLengthError, DownloadFailed, GPGError] m FilePath
|
||||
download rawUri gpgUri eDigest eCSize dest mfn etags
|
||||
| scheme == "https" = liftE dl
|
||||
| scheme == "http" = liftE dl
|
||||
| scheme == "file" = do
|
||||
let destFile' = T.unpack . decUTF8Safe $ view pathL' uri
|
||||
let destFile' = T.unpack . decUTF8Safe $ view pathL' rawUri
|
||||
lift $ logDebug $ "using local file: " <> T.pack destFile'
|
||||
forM_ eDigest (liftE . flip checkDigest destFile')
|
||||
pure destFile'
|
||||
| otherwise = throwE $ DownloadFailed (variantFromValue UnsupportedScheme)
|
||||
|
||||
where
|
||||
scheme = view (uriSchemeL' % schemeBSL') uri
|
||||
scheme = view (uriSchemeL' % schemeBSL') rawUri
|
||||
dl = do
|
||||
Settings{ mirrors } <- lift getSettings
|
||||
let uri = applyMirrors mirrors rawUri
|
||||
baseDestFile <- liftE . reThrowAll @_ @_ @'[DownloadFailed] DownloadFailed $ getDestFile uri mfn
|
||||
lift $ logInfo $ "downloading: " <> (decUTF8Safe . serializeURIRef') uri <> " as file " <> T.pack baseDestFile
|
||||
|
||||
@@ -351,7 +362,7 @@ download uri gpgUri eDigest dest mfn etags
|
||||
-- download
|
||||
flip onException
|
||||
(lift $ hideError doesNotExistErrorType $ recycleFile (tmpFile baseDestFile))
|
||||
$ catchAllE @_ @'[GPGError, ProcessError, DownloadFailed, UnsupportedScheme, DigestError] @'[DigestError, DownloadFailed, GPGError]
|
||||
$ catchAllE @_ @'[GPGError, ProcessError, DownloadFailed, UnsupportedScheme, DigestError, ContentLengthError] @'[DigestError, ContentLengthError, DownloadFailed, GPGError]
|
||||
(\e' -> do
|
||||
lift $ hideError doesNotExistErrorType $ recycleFile (tmpFile baseDestFile)
|
||||
case e' of
|
||||
@@ -386,7 +397,7 @@ download uri gpgUri eDigest dest mfn etags
|
||||
liftE $ flip onException
|
||||
(lift $ hideError doesNotExistErrorType $ recycleFile (tmpFile gpgDestFile))
|
||||
$ catchAllE @_ @'[GPGError, ProcessError, UnsupportedScheme, DownloadFailed] @'[GPGError]
|
||||
(\e -> if gpgSetting == GPGStrict then throwE (GPGError e) else lift $ logWarn $ T.pack (prettyShow (GPGError e))
|
||||
(\e -> if gpgSetting == GPGStrict then throwE (GPGError e) else lift $ logWarn $ T.pack (prettyHFError (GPGError e))
|
||||
) $ do
|
||||
o' <- liftIO getGpgOpts
|
||||
lift $ logDebug $ "downloading: " <> (decUTF8Safe . serializeURIRef') gpgUri' <> " as file " <> T.pack gpgDestFile
|
||||
@@ -401,19 +412,37 @@ download uri gpgUri eDigest dest mfn etags
|
||||
CapturedProcess { _stdErr } -> lift $ logDebug $ decUTF8Safe' _stdErr
|
||||
_ -> pure ()
|
||||
|
||||
forM_ eCSize (liftE . flip checkCSize baseDestFile)
|
||||
forM_ eDigest (liftE . flip checkDigest baseDestFile)
|
||||
pure baseDestFile
|
||||
|
||||
curlDL :: (MonadCatch m, MonadMask m, MonadIO m) => [String] -> FilePath -> URI -> Excepts '[ProcessError, DownloadFailed, UnsupportedScheme] m ()
|
||||
curlDL :: ( MonadCatch m
|
||||
, MonadMask m
|
||||
, MonadIO m
|
||||
)
|
||||
=> [String]
|
||||
-> FilePath
|
||||
-> URI
|
||||
-> Excepts '[ProcessError, DownloadFailed, UnsupportedScheme] m ()
|
||||
curlDL o' destFile (decUTF8Safe . serializeURIRef' -> uri') = do
|
||||
let destFileTemp = tmpFile destFile
|
||||
flip finally (try @_ @SomeException $ rmFile destFileTemp) $ do
|
||||
liftE $ lEM @_ @'[ProcessError] $ exec "curl"
|
||||
(o' ++ ["-fL", "-o", destFileTemp, T.unpack uri']) Nothing Nothing
|
||||
(o' ++ ["-fL", "-o", destFileTemp, T.unpack uri']
|
||||
++ maybe [] (\s -> ["--max-filesize", show s]) eCSize
|
||||
) Nothing Nothing
|
||||
liftIO $ renameFile destFileTemp destFile
|
||||
|
||||
curlEtagsDL :: (MonadReader env m, HasLog env, MonadCatch m, MonadMask m, MonadIO m)
|
||||
=> [String] -> FilePath -> URI -> Excepts '[ProcessError, DownloadFailed, UnsupportedScheme] m ()
|
||||
curlEtagsDL :: ( MonadReader env m
|
||||
, HasLog env
|
||||
, MonadCatch m
|
||||
, MonadMask m
|
||||
, MonadIO m
|
||||
)
|
||||
=> [String]
|
||||
-> FilePath
|
||||
-> URI
|
||||
-> Excepts '[ProcessError, DownloadFailed, UnsupportedScheme] m ()
|
||||
curlEtagsDL o' destFile (decUTF8Safe . serializeURIRef' -> uri') = do
|
||||
let destFileTemp = tmpFile destFile
|
||||
dh <- liftIO $ emptySystemTempFile "curl-header"
|
||||
@@ -440,7 +469,14 @@ download uri gpgUri eDigest dest mfn etags
|
||||
|
||||
lift $ writeEtags destFile (parseEtags headers)
|
||||
|
||||
wgetDL :: (MonadCatch m, MonadMask m, MonadIO m) => [String] -> FilePath -> URI -> Excepts '[ProcessError, DownloadFailed, UnsupportedScheme] m ()
|
||||
wgetDL :: ( MonadCatch m
|
||||
, MonadMask m
|
||||
, MonadIO m
|
||||
)
|
||||
=> [String]
|
||||
-> FilePath
|
||||
-> URI
|
||||
-> Excepts '[ProcessError, DownloadFailed, UnsupportedScheme] m ()
|
||||
wgetDL o' destFile (decUTF8Safe . serializeURIRef' -> uri') = do
|
||||
let destFileTemp = tmpFile destFile
|
||||
flip finally (try @_ @SomeException $ rmFile destFileTemp) $ do
|
||||
@@ -449,8 +485,16 @@ download uri gpgUri eDigest dest mfn etags
|
||||
liftIO $ renameFile destFileTemp destFile
|
||||
|
||||
|
||||
wgetEtagsDL :: (MonadReader env m, HasLog env, MonadCatch m, MonadMask m, MonadIO m)
|
||||
=> [String] -> FilePath -> URI -> Excepts '[ProcessError, DownloadFailed, UnsupportedScheme] m ()
|
||||
wgetEtagsDL :: ( MonadReader env m
|
||||
, HasLog env
|
||||
, MonadCatch m
|
||||
, MonadMask m
|
||||
, MonadIO m
|
||||
)
|
||||
=> [String]
|
||||
-> FilePath
|
||||
-> URI
|
||||
-> Excepts '[ProcessError, DownloadFailed, UnsupportedScheme] m ()
|
||||
wgetEtagsDL o' destFile (decUTF8Safe . serializeURIRef' -> uri') = do
|
||||
let destFileTemp = tmpFile destFile
|
||||
flip finally (try @_ @SomeException $ rmFile destFileTemp) $ do
|
||||
@@ -471,7 +515,10 @@ download uri gpgUri eDigest dest mfn etags
|
||||
| otherwise -> throwE (NonZeroExit i' "wget" opts)
|
||||
|
||||
#if defined(INTERNAL_DOWNLOADER)
|
||||
internalDL :: (MonadCatch m, MonadMask m, MonadIO m)
|
||||
internalDL :: ( MonadCatch m
|
||||
, MonadMask m
|
||||
, MonadIO m
|
||||
)
|
||||
=> FilePath -> URI -> Excepts '[DownloadFailed, UnsupportedScheme] m ()
|
||||
internalDL destFile uri' = do
|
||||
let destFileTemp = tmpFile destFile
|
||||
@@ -481,11 +528,16 @@ download uri gpgUri eDigest dest mfn etags
|
||||
@'[DownloadFailed]
|
||||
(\e@(HTTPNotModified _) ->
|
||||
throwE @_ @'[DownloadFailed] (DownloadFailed (toVariantAt @0 e :: V '[HTTPNotModified])))
|
||||
$ downloadToFile https host fullPath port destFileTemp mempty
|
||||
$ downloadToFile https host fullPath port destFileTemp mempty eCSize
|
||||
liftIO $ renameFile destFileTemp destFile
|
||||
|
||||
|
||||
internalEtagsDL :: (MonadReader env m, HasLog env, MonadCatch m, MonadMask m, MonadIO m)
|
||||
internalEtagsDL :: ( MonadReader env m
|
||||
, HasLog env
|
||||
, MonadCatch m
|
||||
, MonadMask m
|
||||
, MonadIO m
|
||||
)
|
||||
=> FilePath -> URI -> Excepts '[DownloadFailed, UnsupportedScheme] m ()
|
||||
internalEtagsDL destFile uri' = do
|
||||
let destFileTemp = tmpFile destFile
|
||||
@@ -497,7 +549,7 @@ download uri gpgUri eDigest dest mfn etags
|
||||
liftE
|
||||
$ catchE @HTTPNotModified @'[DownloadFailed] @'[] (\(HTTPNotModified etag) -> lift $ writeEtags destFile (pure $ Just etag))
|
||||
$ do
|
||||
r <- downloadToFile https host fullPath port destFileTemp addHeaders
|
||||
r <- downloadToFile https host fullPath port destFileTemp addHeaders eCSize
|
||||
liftIO $ renameFile destFileTemp destFile
|
||||
lift $ writeEtags destFile (pure $ decUTF8Safe <$> getHeader r "etag")
|
||||
#endif
|
||||
@@ -505,7 +557,7 @@ download uri gpgUri eDigest dest mfn etags
|
||||
|
||||
-- Manage to find a file we can write the body into.
|
||||
getDestFile :: Monad m => URI -> Maybe FilePath -> Excepts '[NoUrlBase] m FilePath
|
||||
getDestFile uri' mfn' =
|
||||
getDestFile uri' mfn' =
|
||||
let path = view pathL' uri'
|
||||
in case mfn' of
|
||||
Just fn -> pure (dest </> fn)
|
||||
@@ -574,14 +626,16 @@ downloadCached :: ( MonadReader env m
|
||||
)
|
||||
=> DownloadInfo
|
||||
-> Maybe FilePath -- ^ optional filename
|
||||
-> Excepts '[DigestError , DownloadFailed, GPGError] m FilePath
|
||||
-> Excepts '[DigestError, ContentLengthError, DownloadFailed, GPGError] m FilePath
|
||||
downloadCached dli mfn = do
|
||||
Settings{ cache } <- lift getSettings
|
||||
case cache of
|
||||
True -> downloadCached' dli mfn Nothing
|
||||
False -> do
|
||||
tmp <- lift withGHCupTmpDir
|
||||
liftE $ download (_dlUri dli) Nothing (Just (_dlHash dli)) tmp mfn False
|
||||
liftE $ download (_dlUri dli) Nothing (Just (_dlHash dli)) (_dlCSize dli) (fromGHCupPath tmp) outputFileName False
|
||||
where
|
||||
outputFileName = mfn <|> _dlOutput dli
|
||||
|
||||
|
||||
downloadCached' :: ( MonadReader env m
|
||||
@@ -596,18 +650,21 @@ downloadCached' :: ( MonadReader env m
|
||||
=> DownloadInfo
|
||||
-> Maybe FilePath -- ^ optional filename
|
||||
-> Maybe FilePath -- ^ optional destination dir (default: cacheDir)
|
||||
-> Excepts '[DigestError , DownloadFailed, GPGError] m FilePath
|
||||
-> Excepts '[DigestError, ContentLengthError, DownloadFailed, GPGError] m FilePath
|
||||
downloadCached' dli mfn mDestDir = do
|
||||
Dirs { cacheDir } <- lift getDirs
|
||||
let destDir = fromMaybe cacheDir mDestDir
|
||||
let fn = fromMaybe ((T.unpack . decUTF8Safe) $ urlBaseName $ view (dlUri % pathL') dli) mfn
|
||||
let destDir = fromMaybe (fromGHCupPath cacheDir) mDestDir
|
||||
let fn = fromMaybe ((T.unpack . decUTF8Safe) $ urlBaseName $ view (dlUri % pathL') dli) outputFileName
|
||||
let cachfile = destDir </> fn
|
||||
fileExists <- liftIO $ doesFileExist cachfile
|
||||
if
|
||||
| fileExists -> do
|
||||
forM_ (view dlCSize dli) $ \s -> liftE $ checkCSize s cachfile
|
||||
liftE $ checkDigest (view dlHash dli) cachfile
|
||||
pure cachfile
|
||||
| otherwise -> liftE $ download (_dlUri dli) Nothing (Just (_dlHash dli)) destDir mfn False
|
||||
| otherwise -> liftE $ download (_dlUri dli) Nothing (Just (_dlHash dli)) (_dlCSize dli) destDir outputFileName False
|
||||
where
|
||||
outputFileName = mfn <|> _dlOutput dli
|
||||
|
||||
|
||||
|
||||
@@ -638,6 +695,25 @@ checkDigest eDigest file = do
|
||||
cDigest <- throwEither . E.decodeUtf8' . B16.encode . SHA256.hashlazy $ c
|
||||
when ((cDigest /= eDigest) && verify) $ throwE (DigestError file cDigest eDigest)
|
||||
|
||||
checkCSize :: ( MonadReader env m
|
||||
, HasDirs env
|
||||
, HasSettings env
|
||||
, MonadIO m
|
||||
, MonadThrow m
|
||||
, HasLog env
|
||||
)
|
||||
=> Integer
|
||||
-> FilePath
|
||||
-> Excepts '[ContentLengthError] m ()
|
||||
checkCSize eCSize file = do
|
||||
Settings{ noVerify } <- lift getSettings
|
||||
let verify = not noVerify
|
||||
when verify $ do
|
||||
let p' = takeFileName file
|
||||
lift $ logInfo $ "verifying content length of: " <> T.pack p'
|
||||
cSize <- liftIO $ getFileSize file
|
||||
when ((eCSize /= cSize) && verify) $ throwE (ContentLengthError (Just file) (Just cSize) eCSize)
|
||||
|
||||
|
||||
-- | Get additional curl args from env. This is an undocumented option.
|
||||
getCurlOpts :: IO [String]
|
||||
@@ -685,3 +761,17 @@ getLastHeader = T.unlines . lastDef [] . filter (\x -> not (null x)) . splitOn [
|
||||
|
||||
tmpFile :: FilePath -> FilePath
|
||||
tmpFile = (<.> "tmp")
|
||||
|
||||
|
||||
applyMirrors :: DownloadMirrors -> URI -> URI
|
||||
applyMirrors (DM ms) uri@(URI { uriAuthority = Just (Authority { authorityHost = Host host }) }) =
|
||||
case M.lookup (decUTF8Safe host) ms of
|
||||
Nothing -> uri
|
||||
Just (DownloadMirror auth (Just prefix)) ->
|
||||
uri { uriAuthority = Just auth
|
||||
, uriPath = E.encodeUtf8 $ T.pack ("/" <> T.unpack prefix <> (T.unpack . decUTF8Safe . uriPath $ uri))
|
||||
}
|
||||
Just (DownloadMirror auth Nothing) ->
|
||||
uri { uriAuthority = Just auth }
|
||||
applyMirrors _ uri = uri
|
||||
|
||||
|
||||
@@ -10,21 +10,19 @@ module GHCup.Download.IOStreams where
|
||||
import GHCup.Download.Utils
|
||||
import GHCup.Errors
|
||||
import GHCup.Types.JSON ( )
|
||||
import GHCup.Utils.Prelude
|
||||
import GHCup.Prelude
|
||||
|
||||
import Control.Applicative
|
||||
import Control.Exception.Safe
|
||||
import Control.Monad
|
||||
import Control.Monad.Reader
|
||||
import Data.ByteString ( ByteString )
|
||||
import Data.ByteString.Builder
|
||||
import Data.CaseInsensitive ( CI, original, mk )
|
||||
import Data.IORef
|
||||
import Data.Maybe
|
||||
import Data.Text.Read
|
||||
import Haskus.Utils.Variant.Excepts
|
||||
import Network.Http.Client hiding ( URL )
|
||||
import Optics
|
||||
import Prelude hiding ( abs
|
||||
, readFile
|
||||
, writeFile
|
||||
@@ -33,7 +31,6 @@ import System.ProgressBar
|
||||
import URI.ByteString
|
||||
|
||||
import qualified Data.ByteString as BS
|
||||
import qualified Data.ByteString.Lazy as L
|
||||
import qualified Data.Map.Strict as M
|
||||
import qualified System.IO.Streams as Streams
|
||||
|
||||
@@ -46,27 +43,6 @@ import qualified System.IO.Streams as Streams
|
||||
----------------------------
|
||||
|
||||
|
||||
-- | Load the result of this download into memory at once.
|
||||
downloadBS' :: MonadIO m
|
||||
=> Bool -- ^ https?
|
||||
-> ByteString -- ^ host (e.g. "www.example.com")
|
||||
-> ByteString -- ^ path (e.g. "/my/file") including query
|
||||
-> Maybe Int -- ^ optional port (e.g. 3000)
|
||||
-> Excepts
|
||||
'[ HTTPStatusError
|
||||
, URIParseError
|
||||
, UnsupportedScheme
|
||||
, NoLocationHeader
|
||||
, TooManyRedirs
|
||||
]
|
||||
m
|
||||
L.ByteString
|
||||
downloadBS' https host path port = do
|
||||
bref <- liftIO $ newIORef (mempty :: Builder)
|
||||
let stepper bs = modifyIORef bref (<> byteString bs)
|
||||
void $ downloadInternal False https host path port stepper (pure ()) mempty
|
||||
liftIO (readIORef bref <&> toLazyByteString)
|
||||
|
||||
|
||||
downloadToFile :: (MonadMask m, MonadIO m)
|
||||
=> Bool -- ^ https?
|
||||
@@ -75,8 +51,9 @@ downloadToFile :: (MonadMask m, MonadIO m)
|
||||
-> Maybe Int -- ^ optional port (e.g. 3000)
|
||||
-> FilePath -- ^ destination file to create and write to
|
||||
-> M.Map (CI ByteString) ByteString -- ^ additional headers
|
||||
-> Maybe Integer -- ^ expected content length
|
||||
-> Excepts '[DownloadFailed, HTTPNotModified] m Response
|
||||
downloadToFile https host fullPath port destFile addHeaders = do
|
||||
downloadToFile https host fullPath port destFile addHeaders eCSize = do
|
||||
let stepper = BS.appendFile destFile
|
||||
setup = BS.writeFile destFile mempty
|
||||
catchAllE (\case
|
||||
@@ -84,7 +61,7 @@ downloadToFile https host fullPath port destFile addHeaders = do
|
||||
| i == 304
|
||||
, Just e <- M.lookup (mk "etag") headers -> throwE $ HTTPNotModified (decUTF8Safe e)
|
||||
v -> throwE $ DownloadFailed v
|
||||
) $ downloadInternal True https host fullPath port stepper setup addHeaders
|
||||
) $ downloadInternal True https host fullPath port stepper setup addHeaders eCSize
|
||||
|
||||
|
||||
downloadInternal :: MonadIO m
|
||||
@@ -96,19 +73,21 @@ downloadInternal :: MonadIO m
|
||||
-> (ByteString -> IO a) -- ^ the consuming step function
|
||||
-> IO a -- ^ setup action
|
||||
-> M.Map (CI ByteString) ByteString -- ^ additional headers
|
||||
-> Maybe Integer
|
||||
-> Excepts
|
||||
'[ HTTPStatusError
|
||||
, URIParseError
|
||||
, UnsupportedScheme
|
||||
, NoLocationHeader
|
||||
, TooManyRedirs
|
||||
, ContentLengthError
|
||||
]
|
||||
m
|
||||
Response
|
||||
downloadInternal = go (5 :: Int)
|
||||
|
||||
where
|
||||
go redirs progressBar https host path port consumer setup addHeaders = do
|
||||
go redirs progressBar https host path port consumer setup addHeaders eCSize = do
|
||||
r <- liftIO $ withConnection' https host port action
|
||||
veitherToExcepts r >>= \case
|
||||
Right r' ->
|
||||
@@ -138,25 +117,39 @@ downloadInternal = go (5 :: Int)
|
||||
followRedirectURL bs = case parseURI strictURIParserOptions bs of
|
||||
Right uri' -> do
|
||||
(https', host', fullPath', port') <- liftE $ uriToQuadruple uri'
|
||||
go (redirs - 1) progressBar https' host' fullPath' port' consumer setup addHeaders
|
||||
go (redirs - 1) progressBar https' host' fullPath' port' consumer setup addHeaders eCSize
|
||||
Left e -> throwE e
|
||||
|
||||
downloadStream r i' = do
|
||||
void setup
|
||||
let size = case getHeader r "Content-Length" of
|
||||
Just x' -> case decimal $ decUTF8Safe x' of
|
||||
Left _ -> 0
|
||||
Right (r', _) -> r'
|
||||
Nothing -> 0
|
||||
Left _ -> Nothing
|
||||
Right (r', _) -> Just r'
|
||||
Nothing -> Nothing
|
||||
|
||||
(mpb :: Maybe (ProgressBar ())) <- if progressBar
|
||||
then Just <$> newProgressBar defStyle 10 (Progress 0 size ())
|
||||
else pure Nothing
|
||||
forM_ size $ \s -> forM_ eCSize $ \es -> when (es /= s) $ throwIO (ContentLengthError Nothing (Just s) es)
|
||||
let size' = eCSize <|> size
|
||||
|
||||
(mpb :: Maybe (ProgressBar ())) <- case (progressBar, size') of
|
||||
(True, Just size'') -> Just <$> newProgressBar defStyle 10 (Progress 0 (fromInteger size'') ())
|
||||
_ -> pure Nothing
|
||||
|
||||
ior <- liftIO $ newIORef 0
|
||||
|
||||
outStream <- liftIO $ Streams.makeOutputStream
|
||||
(\case
|
||||
Just bs -> do
|
||||
forM_ mpb $ \pb -> incProgress pb (BS.length bs)
|
||||
let len = BS.length bs
|
||||
forM_ mpb $ \pb -> incProgress pb len
|
||||
|
||||
-- check we don't exceed size
|
||||
forM_ size' $ \s -> do
|
||||
cs <- readIORef ior
|
||||
when ((cs + toInteger len) > s) $ throwIO (ContentLengthError Nothing (Just (cs + toInteger len)) s)
|
||||
|
||||
modifyIORef ior (+ toInteger len)
|
||||
|
||||
void $ consumer bs
|
||||
Nothing -> pure ()
|
||||
)
|
||||
|
||||
@@ -10,7 +10,7 @@ module GHCup.Download.Utils where
|
||||
import GHCup.Errors
|
||||
import GHCup.Types.Optics
|
||||
import GHCup.Types.JSON ( )
|
||||
import GHCup.Utils.Prelude
|
||||
import GHCup.Prelude
|
||||
|
||||
import Control.Applicative
|
||||
import Control.Monad
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
{-# LANGUAGE StandaloneDeriving #-}
|
||||
{-# LANGUAGE TypeOperators #-}
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
{-# LANGUAGE RankNTypes #-}
|
||||
|
||||
{-|
|
||||
Module : GHCup.Errors
|
||||
@@ -27,15 +28,165 @@ import Data.CaseInsensitive ( CI )
|
||||
import Data.Text ( Text )
|
||||
import Data.Versions
|
||||
import Haskus.Utils.Variant
|
||||
import System.FilePath
|
||||
import Text.PrettyPrint hiding ( (<>) )
|
||||
import Text.PrettyPrint.HughesPJClass hiding ( (<>) )
|
||||
import URI.ByteString
|
||||
|
||||
import qualified Data.Map.Strict as M
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.Encoding as E
|
||||
import qualified Data.Text.Encoding.Error as E
|
||||
import Data.Data (Proxy(..))
|
||||
import Data.Time (Day)
|
||||
|
||||
|
||||
|
||||
allHFError :: String
|
||||
allHFError = unlines allErrors
|
||||
where
|
||||
format p = "GHCup-" <> show (eBase p) <> " " <> eDesc p
|
||||
format'' e p = "GHCup-" <> show (eNum e) <> " " <> eDesc p
|
||||
format' e _ = "GHCup-" <> show (eNum e) <> " " <> prettyShow e
|
||||
format''' e _ str' = "GHCup-" <> show (eNum e) <> " " <> str'
|
||||
allErrors =
|
||||
[ "# low level errors (1 to 500)"
|
||||
, let proxy = Proxy :: Proxy NoCompatiblePlatform in format proxy
|
||||
, let proxy = Proxy :: Proxy NoDownload in format proxy
|
||||
, let proxy = Proxy :: Proxy NoUpdate in format proxy
|
||||
, let proxy = Proxy :: Proxy DistroNotFound in format proxy
|
||||
, let proxy = Proxy :: Proxy UnknownArchive in format proxy
|
||||
, let proxy = Proxy :: Proxy UnsupportedScheme in format proxy
|
||||
, let proxy = Proxy :: Proxy CopyError in format proxy
|
||||
, let proxy = Proxy :: Proxy MergeFileTreeError in format proxy
|
||||
, let proxy = Proxy :: Proxy TagNotFound in format proxy
|
||||
, let proxy = Proxy :: Proxy DayNotFound in format proxy
|
||||
, let proxy = Proxy :: Proxy NextVerNotFound in format proxy
|
||||
, let proxy = Proxy :: Proxy AlreadyInstalled in format proxy
|
||||
, let proxy = Proxy :: Proxy DirNotEmpty in format proxy
|
||||
, let proxy = Proxy :: Proxy NotInstalled in format proxy
|
||||
, let proxy = Proxy :: Proxy UninstallFailed in format proxy
|
||||
, let proxy = Proxy :: Proxy NotFoundInPATH in format proxy
|
||||
, let proxy = Proxy :: Proxy JSONError in format proxy
|
||||
, let proxy = Proxy :: Proxy FileDoesNotExistError in format proxy
|
||||
, let proxy = Proxy :: Proxy FileAlreadyExistsError in format proxy
|
||||
, let proxy = Proxy :: Proxy TarDirDoesNotExist in format proxy
|
||||
, let proxy = Proxy :: Proxy DigestError in format proxy
|
||||
, let proxy = Proxy :: Proxy GPGError in format proxy
|
||||
, let proxy = Proxy :: Proxy HTTPStatusError in format proxy
|
||||
, let proxy = Proxy :: Proxy MalformedHeaders in format proxy
|
||||
, let proxy = Proxy :: Proxy HTTPNotModified in format proxy
|
||||
, let proxy = Proxy :: Proxy NoLocationHeader in format proxy
|
||||
, let proxy = Proxy :: Proxy TooManyRedirs in format proxy
|
||||
, let proxy = Proxy :: Proxy PatchFailed in format proxy
|
||||
, let proxy = Proxy :: Proxy NoToolRequirements in format proxy
|
||||
, let proxy = Proxy :: Proxy InvalidBuildConfig in format proxy
|
||||
, let proxy = Proxy :: Proxy NoToolVersionSet in format proxy
|
||||
, let proxy = Proxy :: Proxy NoNetwork in format proxy
|
||||
, let proxy = Proxy :: Proxy HadrianNotFound in format proxy
|
||||
, let proxy = Proxy :: Proxy ToolShadowed in format proxy
|
||||
, let proxy = Proxy :: Proxy ContentLengthError in format proxy
|
||||
, let proxy = Proxy :: Proxy DuplicateReleaseChannel in format proxy
|
||||
, ""
|
||||
, "# high level errors (4000+)"
|
||||
, let proxy = Proxy :: Proxy DownloadFailed in format proxy
|
||||
, let proxy = Proxy :: Proxy InstallSetError in format proxy
|
||||
, let proxy = Proxy :: Proxy TestFailed in format proxy
|
||||
, let proxy = Proxy :: Proxy BuildFailed in format proxy
|
||||
, let proxy = Proxy :: Proxy GHCupSetError in format proxy
|
||||
, ""
|
||||
, "# true exceptions (500+)"
|
||||
, let proxy = Proxy :: Proxy ParseError in format proxy
|
||||
, let proxy = Proxy :: Proxy UnexpectedListLength in format proxy
|
||||
, let proxy = Proxy :: Proxy NoUrlBase in format proxy
|
||||
, ""
|
||||
, "# orphans (800+)"
|
||||
, let proxy = Proxy :: Proxy URIParseError in format proxy
|
||||
, let proxy = Proxy :: Proxy URIParseError
|
||||
e = MalformedScheme MissingColon
|
||||
in format' e proxy
|
||||
, let proxy = Proxy :: Proxy URIParseError
|
||||
e = MalformedUserInfo
|
||||
in format' e proxy
|
||||
, let proxy = Proxy :: Proxy URIParseError
|
||||
e = MalformedQuery
|
||||
in format' e proxy
|
||||
, let proxy = Proxy :: Proxy URIParseError
|
||||
e = MalformedFragment
|
||||
in format' e proxy
|
||||
, let proxy = Proxy :: Proxy URIParseError
|
||||
e = MalformedHost
|
||||
in format' e proxy
|
||||
, let proxy = Proxy :: Proxy URIParseError
|
||||
e = MalformedPort
|
||||
in format' e proxy
|
||||
, let proxy = Proxy :: Proxy URIParseError
|
||||
e = MalformedPath
|
||||
in format' e proxy
|
||||
, let proxy = Proxy :: Proxy URIParseError
|
||||
e = OtherError ""
|
||||
in format'' e proxy
|
||||
, let proxy = Proxy :: Proxy ArchiveResult in format proxy
|
||||
, let proxy = Proxy :: Proxy ArchiveResult
|
||||
e = ArchiveFatal
|
||||
in format' e proxy
|
||||
, let proxy = Proxy :: Proxy ArchiveResult
|
||||
e = ArchiveFailed
|
||||
in format' e proxy
|
||||
, let proxy = Proxy :: Proxy ArchiveResult
|
||||
e = ArchiveWarn
|
||||
in format' e proxy
|
||||
, let proxy = Proxy :: Proxy ArchiveResult
|
||||
e = ArchiveRetry
|
||||
in format' e proxy
|
||||
, let proxy = Proxy :: Proxy ArchiveResult
|
||||
e = ArchiveOk
|
||||
in format' e proxy
|
||||
, let proxy = Proxy :: Proxy ArchiveResult
|
||||
e = ArchiveEOF
|
||||
in format' e proxy
|
||||
|
||||
, let proxy = Proxy :: Proxy ProcessError in format proxy
|
||||
, let proxy = Proxy :: Proxy ProcessError
|
||||
e = NonZeroExit 0 "" []
|
||||
in format''' e proxy "A process returned a non-zero exit code."
|
||||
, let proxy = Proxy :: Proxy ProcessError
|
||||
e = PTerminated "" []
|
||||
in format''' e proxy "A process terminated prematurely."
|
||||
, let proxy = Proxy :: Proxy ProcessError
|
||||
e = PStopped "" []
|
||||
in format''' e proxy "A process stopped prematurely."
|
||||
, let proxy = Proxy :: Proxy ProcessError
|
||||
e = NoSuchPid "" []
|
||||
in format''' e proxy "Could not find PID for this process."
|
||||
]
|
||||
|
||||
|
||||
prettyHFError :: (Pretty e, HFErrorProject e) => e -> String
|
||||
prettyHFError e =
|
||||
let errorCode = "GHCup-" <> padIntAndShow (eNum e)
|
||||
in ("[" <> linkEscapeCode errorCode (hfErrorLink errorCode) <> "] ") <> prettyShow e
|
||||
where
|
||||
hfErrorLink errorCode = "https://errors.haskell.org/messages/" <> errorCode
|
||||
padIntAndShow i
|
||||
| i < 10 = "0000" <> show i
|
||||
| i < 100 = "000" <> show i
|
||||
| i < 1000 = "00" <> show i
|
||||
| i < 10000 = "0" <> show i
|
||||
| otherwise = show i
|
||||
|
||||
class HFErrorProject a where
|
||||
eNum :: a -> Int
|
||||
eNum _ = eBase (Proxy :: Proxy a)
|
||||
|
||||
eBase :: Proxy a -> Int
|
||||
|
||||
eDesc :: Proxy a -> String
|
||||
|
||||
linkEscapeCode :: String -> String -> String
|
||||
linkEscapeCode linkText link = "\ESC]8;;" <> link <> "\ESC\\" <> linkText <> "\ESC]8;;\ESC\\"
|
||||
|
||||
|
||||
------------------------
|
||||
--[ Low-level errors ]--
|
||||
------------------------
|
||||
@@ -50,20 +201,32 @@ instance Pretty NoCompatiblePlatform where
|
||||
pPrint (NoCompatiblePlatform str') =
|
||||
text ("Could not find a compatible platform. Got: " ++ str')
|
||||
|
||||
instance HFErrorProject NoCompatiblePlatform where
|
||||
eBase _ = 1
|
||||
eDesc _ = "No compatible platform could be found"
|
||||
|
||||
-- | Unable to find a download for the requested version/distro.
|
||||
data NoDownload = NoDownload
|
||||
deriving Show
|
||||
|
||||
instance Pretty NoDownload where
|
||||
pPrint NoDownload =
|
||||
text "Unable to find a download for the requested version/distro."
|
||||
text (eDesc (Proxy :: Proxy NoDownload))
|
||||
|
||||
instance HFErrorProject NoDownload where
|
||||
eBase _ = 10
|
||||
eDesc _ = "Unable to find a download for the requested version/distro."
|
||||
|
||||
-- | No update available or necessary.
|
||||
data NoUpdate = NoUpdate
|
||||
deriving Show
|
||||
|
||||
instance Pretty NoUpdate where
|
||||
pPrint NoUpdate = text "No update available or necessary."
|
||||
pPrint NoUpdate = text (eDesc (Proxy :: Proxy NoUpdate))
|
||||
|
||||
instance HFErrorProject NoUpdate where
|
||||
eBase _ = 20
|
||||
eDesc _ = "No update available or necessary."
|
||||
|
||||
-- | The Architecture is unknown and unsupported.
|
||||
data NoCompatibleArch = NoCompatibleArch String
|
||||
@@ -73,13 +236,21 @@ instance Pretty NoCompatibleArch where
|
||||
pPrint (NoCompatibleArch arch) =
|
||||
text ("The Architecture is unknown or unsupported. Got: " ++ arch)
|
||||
|
||||
instance HFErrorProject NoCompatibleArch where
|
||||
eBase _ = 30
|
||||
eDesc _ = "The Architecture is unknown and unsupported"
|
||||
|
||||
-- | Unable to figure out the distribution of the host.
|
||||
data DistroNotFound = DistroNotFound
|
||||
deriving Show
|
||||
|
||||
instance Pretty DistroNotFound where
|
||||
pPrint DistroNotFound =
|
||||
text "Unable to figure out the distribution of the host."
|
||||
text (eDesc (Proxy :: Proxy DistroNotFound))
|
||||
|
||||
instance HFErrorProject DistroNotFound where
|
||||
eBase _ = 40
|
||||
eDesc _ = "Unable to figure out the distribution of the host"
|
||||
|
||||
-- | The archive format is unknown. We don't know how to extract it.
|
||||
data UnknownArchive = UnknownArchive FilePath
|
||||
@@ -89,12 +260,21 @@ instance Pretty UnknownArchive where
|
||||
pPrint (UnknownArchive file) =
|
||||
text $ "The archive format is unknown. We don't know how to extract the file " <> file
|
||||
|
||||
instance HFErrorProject UnknownArchive where
|
||||
eBase _ = 50
|
||||
eDesc _ = "The archive format is unknown. We don't know how to extract it."
|
||||
|
||||
-- | The scheme is not supported (such as ftp).
|
||||
data UnsupportedScheme = UnsupportedScheme
|
||||
deriving Show
|
||||
|
||||
instance Pretty UnsupportedScheme where
|
||||
pPrint UnsupportedScheme = text "The scheme is not supported (such as ftp)."
|
||||
pPrint UnsupportedScheme =
|
||||
text (eDesc (Proxy :: Proxy UnsupportedScheme))
|
||||
|
||||
instance HFErrorProject UnsupportedScheme where
|
||||
eBase _ = 60
|
||||
eDesc _ = "The scheme is not supported (such as ftp)."
|
||||
|
||||
-- | Unable to copy a file.
|
||||
data CopyError = CopyError String
|
||||
@@ -104,6 +284,23 @@ instance Pretty CopyError where
|
||||
pPrint (CopyError reason) =
|
||||
text ("Unable to copy a file. Reason was: " ++ reason)
|
||||
|
||||
instance HFErrorProject CopyError where
|
||||
eBase _ = 70
|
||||
eDesc _ = "Unable to copy a file."
|
||||
|
||||
-- | Unable to merge file trees.
|
||||
data MergeFileTreeError = MergeFileTreeError IOException FilePath FilePath
|
||||
deriving Show
|
||||
|
||||
instance Pretty MergeFileTreeError where
|
||||
pPrint (MergeFileTreeError e from to) =
|
||||
text "Failed to merge file tree from" <+> text from <+> text "to" <+> text to <+> text "\nexception was:" <+> text (displayException e)
|
||||
<+> text "\n...you may need to delete" <+> text to <+> text "manually. Make sure it's gone."
|
||||
|
||||
instance HFErrorProject MergeFileTreeError where
|
||||
eBase _ = 80
|
||||
eDesc _ = "Unable to merge file trees during installation"
|
||||
|
||||
-- | Unable to find a tag of a tool.
|
||||
data TagNotFound = TagNotFound Tag Tool
|
||||
deriving Show
|
||||
@@ -112,6 +309,25 @@ instance Pretty TagNotFound where
|
||||
pPrint (TagNotFound tag tool) =
|
||||
text "Unable to find tag" <+> pPrint tag <+> text "of tool" <+> pPrint tool
|
||||
|
||||
instance HFErrorProject TagNotFound where
|
||||
eBase _ = 90
|
||||
eDesc _ = "Unable to find a tag of a tool"
|
||||
|
||||
-- | Unable to find a release day of a tool
|
||||
data DayNotFound = DayNotFound Day Tool (Maybe Day)
|
||||
deriving Show
|
||||
|
||||
instance Pretty DayNotFound where
|
||||
pPrint (DayNotFound day tool Nothing) =
|
||||
text "Unable to find release date" <+> text (show day) <+> text "of tool" <+> pPrint tool
|
||||
pPrint (DayNotFound day tool (Just alternateDay)) =
|
||||
text "Unable to find release date" <+> text (show day) <+> text "of tool" <+> pPrint tool <+>
|
||||
text "but found an alternative date" <+> text (show alternateDay)
|
||||
|
||||
instance HFErrorProject DayNotFound where
|
||||
eBase _ = 95
|
||||
eDesc _ = "Unable to find a release date of a tool"
|
||||
|
||||
-- | Unable to find the next version of a tool (the one after the currently
|
||||
-- set one).
|
||||
data NextVerNotFound = NextVerNotFound Tool
|
||||
@@ -121,21 +337,35 @@ instance Pretty NextVerNotFound where
|
||||
pPrint (NextVerNotFound tool) =
|
||||
text "Unable to find next (the one after the currently set one) version of tool" <+> pPrint tool
|
||||
|
||||
instance HFErrorProject NextVerNotFound where
|
||||
eBase _ = 100
|
||||
eDesc _ = "Unable to find the next version of a tool (the one after the currently set one)"
|
||||
|
||||
-- | The tool (such as GHC) is already installed with that version.
|
||||
data AlreadyInstalled = AlreadyInstalled Tool Version
|
||||
deriving Show
|
||||
|
||||
instance Pretty AlreadyInstalled where
|
||||
pPrint (AlreadyInstalled tool ver') =
|
||||
pPrint tool <+> text "-" <+> pPrint ver' <+> text "is already installed"
|
||||
(pPrint tool <> text "-" <> pPrint ver') <+> text "is already installed;"
|
||||
<+> text "if you really want to reinstall it, you may want to run 'ghcup install" <+> pPrint tool <+> text "--force" <+> (pPrint ver' <> text "'")
|
||||
|
||||
instance HFErrorProject AlreadyInstalled where
|
||||
eBase _ = 110
|
||||
eDesc _ = "The tool (such as GHC) is already installed with that version"
|
||||
|
||||
-- | The Directory is supposed to be empty, but wasn't.
|
||||
data DirNotEmpty = DirNotEmpty {path :: FilePath}
|
||||
deriving Show
|
||||
|
||||
instance Pretty DirNotEmpty where
|
||||
pPrint (DirNotEmpty path) = do
|
||||
text $ "The directory was expected to be empty, but isn't: " <> path
|
||||
|
||||
instance HFErrorProject DirNotEmpty where
|
||||
eBase _ = 120
|
||||
eDesc _ = "The Directory is supposed to be empty, but wasn't"
|
||||
|
||||
-- | The tool is not installed. Some operations rely on a tool
|
||||
-- to be installed (such as setting the current GHC version).
|
||||
data NotInstalled = NotInstalled Tool GHCTargetVersion
|
||||
@@ -145,6 +375,21 @@ instance Pretty NotInstalled where
|
||||
pPrint (NotInstalled tool ver) =
|
||||
text "The version" <+> pPrint ver <+> text "of the tool" <+> pPrint tool <+> text "is not installed."
|
||||
|
||||
instance HFErrorProject NotInstalled where
|
||||
eBase _ = 130
|
||||
eDesc _ = "The required tool is not installed"
|
||||
|
||||
data UninstallFailed = UninstallFailed FilePath [FilePath]
|
||||
deriving Show
|
||||
|
||||
instance Pretty UninstallFailed where
|
||||
pPrint (UninstallFailed dir files) =
|
||||
text "The following files survived uninstallation: " <+> pPrint files <+> text "...consider removing" <+> pPrint dir <+> text "manually."
|
||||
|
||||
instance HFErrorProject UninstallFailed where
|
||||
eBase _ = 140
|
||||
eDesc _ = "Uninstallation failed with leftover files"
|
||||
|
||||
-- | An executable was expected to be in PATH, but was not found.
|
||||
data NotFoundInPATH = NotFoundInPATH FilePath
|
||||
deriving Show
|
||||
@@ -155,6 +400,10 @@ instance Pretty NotFoundInPATH where
|
||||
pPrint (NotFoundInPATH exe) =
|
||||
text $ "The exe " <> exe <> " was not found in PATH."
|
||||
|
||||
instance HFErrorProject NotFoundInPATH where
|
||||
eBase _ = 150
|
||||
eDesc _ = "An executable was expected to be in PATH, but was not found"
|
||||
|
||||
-- | JSON decoding failed.
|
||||
data JSONError = JSONDecodeError String
|
||||
deriving Show
|
||||
@@ -163,6 +412,10 @@ instance Pretty JSONError where
|
||||
pPrint (JSONDecodeError err) =
|
||||
text $ "JSON decoding failed with: " <> err
|
||||
|
||||
instance HFErrorProject JSONError where
|
||||
eBase _ = 160
|
||||
eDesc _ = "JSON decoding failed"
|
||||
|
||||
-- | A file that is supposed to exist does not exist
|
||||
-- (e.g. when we use file scheme to "download" something).
|
||||
data FileDoesNotExistError = FileDoesNotExistError FilePath
|
||||
@@ -172,6 +425,10 @@ instance Pretty FileDoesNotExistError where
|
||||
pPrint (FileDoesNotExistError file) =
|
||||
text $ "File " <> file <> " does not exist."
|
||||
|
||||
instance HFErrorProject FileDoesNotExistError where
|
||||
eBase _ = 170
|
||||
eDesc _ = "A file that is supposed to exist does not exist (oops)"
|
||||
|
||||
-- | The file already exists
|
||||
-- (e.g. when we use isolated installs with the same path).
|
||||
-- (e.g. This is done to prevent any overwriting)
|
||||
@@ -182,6 +439,10 @@ instance Pretty FileAlreadyExistsError where
|
||||
pPrint (FileAlreadyExistsError file) =
|
||||
text $ "File " <> file <> " Already exists."
|
||||
|
||||
instance HFErrorProject FileAlreadyExistsError where
|
||||
eBase _ = 180
|
||||
eDesc _ = "A file already exists that wasn't expected to exist"
|
||||
|
||||
data TarDirDoesNotExist = TarDirDoesNotExist TarDir
|
||||
deriving Show
|
||||
|
||||
@@ -189,6 +450,10 @@ instance Pretty TarDirDoesNotExist where
|
||||
pPrint (TarDirDoesNotExist dir) =
|
||||
text "Tar directory does not exist:" <+> pPrint dir
|
||||
|
||||
instance HFErrorProject TarDirDoesNotExist where
|
||||
eBase _ = 190
|
||||
eDesc _ = "The tar directory (e.g. inside an archive) does not exist"
|
||||
|
||||
-- | File digest verification failed.
|
||||
data DigestError = DigestError FilePath Text Text
|
||||
deriving Show
|
||||
@@ -199,7 +464,11 @@ instance Pretty DigestError where
|
||||
<+> text (T.unpack expectedDigest) <+> text "but got" <+> pPrint currentDigest <+> text
|
||||
"\nConsider removing the file in case it's cached and try again."
|
||||
|
||||
-- | File digest verification failed.
|
||||
instance HFErrorProject DigestError where
|
||||
eBase _ = 200
|
||||
eDesc _ = "File digest verification failed"
|
||||
|
||||
-- | File PGP verification failed.
|
||||
data GPGError = forall xs . (ToVariantMaybe DownloadFailed xs, PopVariant DownloadFailed xs, Show (V xs), Pretty (V xs)) => GPGError (V xs)
|
||||
|
||||
deriving instance Show GPGError
|
||||
@@ -207,6 +476,10 @@ deriving instance Show GPGError
|
||||
instance Pretty GPGError where
|
||||
pPrint (GPGError reason) = text "GPG verify failed:" <+> pPrint reason
|
||||
|
||||
instance HFErrorProject GPGError where
|
||||
eBase _ = 210
|
||||
eDesc _ = "File PGP verification failed"
|
||||
|
||||
-- | Unexpected HTTP status.
|
||||
data HTTPStatusError = HTTPStatusError Int (M.Map (CI ByteString) ByteString)
|
||||
deriving Show
|
||||
@@ -215,6 +488,10 @@ instance Pretty HTTPStatusError where
|
||||
pPrint (HTTPStatusError status _) =
|
||||
text "Unexpected HTTP status:" <+> pPrint status
|
||||
|
||||
instance HFErrorProject HTTPStatusError where
|
||||
eBase _ = 220
|
||||
eDesc _ = "Unexpected HTTP status error (e.g. during downloads)"
|
||||
|
||||
-- | Malformed headers.
|
||||
data MalformedHeaders = MalformedHeaders Text
|
||||
deriving Show
|
||||
@@ -223,6 +500,10 @@ instance Pretty MalformedHeaders where
|
||||
pPrint (MalformedHeaders h) =
|
||||
text "Headers are malformed: " <+> pPrint h
|
||||
|
||||
instance HFErrorProject MalformedHeaders where
|
||||
eBase _ = 230
|
||||
eDesc _ = "Malformed headers during download"
|
||||
|
||||
-- | Unexpected HTTP status.
|
||||
data HTTPNotModified = HTTPNotModified Text
|
||||
deriving Show
|
||||
@@ -231,13 +512,21 @@ instance Pretty HTTPNotModified where
|
||||
pPrint (HTTPNotModified etag) =
|
||||
text "Remote resource not modifed, etag was:" <+> pPrint etag
|
||||
|
||||
instance HFErrorProject HTTPNotModified where
|
||||
eBase _ = 240
|
||||
eDesc _ = "Not modified HTTP status error (e.g. during downloads)."
|
||||
|
||||
-- | The 'Location' header was expected during a 3xx redirect, but not found.
|
||||
data NoLocationHeader = NoLocationHeader
|
||||
deriving Show
|
||||
|
||||
instance Pretty NoLocationHeader where
|
||||
pPrint NoLocationHeader =
|
||||
text "The 'Location' header was expected during a 3xx redirect, but not found."
|
||||
text (eDesc (Proxy :: Proxy NoLocationHeader))
|
||||
|
||||
instance HFErrorProject NoLocationHeader where
|
||||
eBase _ = 250
|
||||
eDesc _ = "The 'Location' header was expected during a 3xx redirect, but not found."
|
||||
|
||||
-- | Too many redirects.
|
||||
data TooManyRedirs = TooManyRedirs
|
||||
@@ -245,7 +534,11 @@ data TooManyRedirs = TooManyRedirs
|
||||
|
||||
instance Pretty TooManyRedirs where
|
||||
pPrint TooManyRedirs =
|
||||
text "Too many redirections."
|
||||
text (eDesc (Proxy :: Proxy TooManyRedirs))
|
||||
|
||||
instance HFErrorProject TooManyRedirs where
|
||||
eBase _ = 260
|
||||
eDesc _ = "Too many redirections."
|
||||
|
||||
-- | A patch could not be applied.
|
||||
data PatchFailed = PatchFailed
|
||||
@@ -253,7 +546,11 @@ data PatchFailed = PatchFailed
|
||||
|
||||
instance Pretty PatchFailed where
|
||||
pPrint PatchFailed =
|
||||
text "A patch could not be applied."
|
||||
text (eDesc (Proxy :: Proxy PatchFailed))
|
||||
|
||||
instance HFErrorProject PatchFailed where
|
||||
eBase _ = 270
|
||||
eDesc _ = "A patch could not be applied."
|
||||
|
||||
-- | The tool requirements could not be found.
|
||||
data NoToolRequirements = NoToolRequirements
|
||||
@@ -261,7 +558,11 @@ data NoToolRequirements = NoToolRequirements
|
||||
|
||||
instance Pretty NoToolRequirements where
|
||||
pPrint NoToolRequirements =
|
||||
text "The Tool requirements could not be found."
|
||||
text (eDesc (Proxy :: Proxy NoToolRequirements))
|
||||
|
||||
instance HFErrorProject NoToolRequirements where
|
||||
eBase _ = 280
|
||||
eDesc _ = "The Tool requirements could not be found."
|
||||
|
||||
data InvalidBuildConfig = InvalidBuildConfig Text
|
||||
deriving Show
|
||||
@@ -270,6 +571,10 @@ instance Pretty InvalidBuildConfig where
|
||||
pPrint (InvalidBuildConfig reason) =
|
||||
text "The build config is invalid. Reason was:" <+> pPrint reason
|
||||
|
||||
instance HFErrorProject InvalidBuildConfig where
|
||||
eBase _ = 290
|
||||
eDesc _ = "The build config is invalid."
|
||||
|
||||
data NoToolVersionSet = NoToolVersionSet Tool
|
||||
deriving Show
|
||||
|
||||
@@ -277,27 +582,103 @@ instance Pretty NoToolVersionSet where
|
||||
pPrint (NoToolVersionSet tool) =
|
||||
text "No version is set for tool" <+> pPrint tool <+> text "."
|
||||
|
||||
instance HFErrorProject NoToolVersionSet where
|
||||
eBase _ = 300
|
||||
eDesc _ = "No version is set for tool (but was expected)."
|
||||
|
||||
data NoNetwork = NoNetwork
|
||||
deriving Show
|
||||
|
||||
instance Pretty NoNetwork where
|
||||
pPrint NoNetwork =
|
||||
text "A download was required or requested, but '--offline' was specified."
|
||||
text (eDesc (Proxy :: Proxy NoNetwork))
|
||||
|
||||
instance HFErrorProject NoNetwork where
|
||||
eBase _ = 310
|
||||
eDesc _ = "A download was required or requested, but '--offline' was specified."
|
||||
|
||||
data HadrianNotFound = HadrianNotFound
|
||||
deriving Show
|
||||
|
||||
instance Pretty HadrianNotFound where
|
||||
pPrint HadrianNotFound =
|
||||
text "Could not find Hadrian build files. Does this GHC version support Hadrian builds?"
|
||||
text (eDesc (Proxy :: Proxy HadrianNotFound))
|
||||
|
||||
instance HFErrorProject HadrianNotFound where
|
||||
eBase _ = 320
|
||||
eDesc _ = "Could not find Hadrian build files. Does this GHC version support Hadrian builds?"
|
||||
|
||||
data ToolShadowed = ToolShadowed
|
||||
Tool
|
||||
FilePath -- shadow binary
|
||||
FilePath -- upgraded binary
|
||||
Version -- upgraded version
|
||||
deriving Show
|
||||
|
||||
instance Pretty ToolShadowed where
|
||||
pPrint (ToolShadowed tool sh up _) =
|
||||
text (prettyShow tool
|
||||
<> " is shadowed by "
|
||||
<> sh
|
||||
<> ".\nThe upgrade will not be in effect, unless you remove "
|
||||
<> sh
|
||||
<> "\nor make sure "
|
||||
<> takeDirectory up
|
||||
<> " comes before "
|
||||
<> takeDirectory sh
|
||||
<> " in PATH."
|
||||
)
|
||||
|
||||
instance HFErrorProject ToolShadowed where
|
||||
eBase _ = 330
|
||||
eDesc _ = "A tool is shadowed in PATH."
|
||||
|
||||
-- | File content length verification failed.
|
||||
data ContentLengthError = ContentLengthError (Maybe FilePath) (Maybe Integer) Integer
|
||||
deriving Show
|
||||
|
||||
instance Pretty ContentLengthError where
|
||||
pPrint (ContentLengthError Nothing Nothing expectedSize) =
|
||||
text "Content length exceeded expected size:"
|
||||
<+> text (show expectedSize)
|
||||
<+> text "\nConsider removing the file in case it's cached and try again."
|
||||
pPrint (ContentLengthError Nothing (Just currentSize) expectedSize) =
|
||||
text "Content length error. Expected"
|
||||
<+> text (show expectedSize) <+> text "but got" <+> pPrint currentSize <+> text
|
||||
"\nConsider removing the file in case it's cached and try again."
|
||||
pPrint (ContentLengthError (Just fp) (Just currentSize) expectedSize) =
|
||||
text "Content length error for" <+> text (fp <> ": expected")
|
||||
<+> text (show expectedSize) <+> text "but got" <+> pPrint currentSize <+> text
|
||||
"\nConsider removing the file in case it's cached and try again."
|
||||
pPrint (ContentLengthError (Just fp) Nothing expectedSize) =
|
||||
text "Content length error for" <+> text (fp <> ": expected")
|
||||
<+> text (show expectedSize) <+> text "\nConsider removing the file in case it's cached and try again."
|
||||
|
||||
instance Exception ContentLengthError
|
||||
|
||||
instance HFErrorProject ContentLengthError where
|
||||
eBase _ = 340
|
||||
eDesc _ = "File content length verification failed"
|
||||
|
||||
data DuplicateReleaseChannel = DuplicateReleaseChannel URI
|
||||
deriving Show
|
||||
|
||||
instance HFErrorProject DuplicateReleaseChannel where
|
||||
eBase _ = 350
|
||||
eDesc _ = "Duplicate release channel detected when adding URI.\nGiving up. You can use '--force' to remove and append the duplicate URI (this may change order/semantics)."
|
||||
|
||||
instance Pretty DuplicateReleaseChannel where
|
||||
pPrint (DuplicateReleaseChannel uri) =
|
||||
text $ "Duplicate release channel detected when adding: \n "
|
||||
<> (T.unpack . E.decodeUtf8With E.lenientDecode . serializeURIRef') uri
|
||||
<> "\nGiving up. You can use '--force' to remove and append the duplicate URI (this may change order/semantics)."
|
||||
|
||||
-------------------------
|
||||
--[ High-level errors ]--
|
||||
-------------------------
|
||||
|
||||
-- | A download failed. The underlying error is encapsulated.
|
||||
data DownloadFailed = forall xs . (ToVariantMaybe DownloadFailed xs, PopVariant DownloadFailed xs, Show (V xs), Pretty (V xs)) => DownloadFailed (V xs)
|
||||
data DownloadFailed = forall xs . (HFErrorProject (V xs), ToVariantMaybe DownloadFailed xs, PopVariant DownloadFailed xs, Show (V xs), Pretty (V xs)) => DownloadFailed (V xs)
|
||||
|
||||
instance Pretty DownloadFailed where
|
||||
pPrint (DownloadFailed reason) =
|
||||
@@ -307,9 +688,47 @@ instance Pretty DownloadFailed where
|
||||
|
||||
deriving instance Show DownloadFailed
|
||||
|
||||
instance HFErrorProject DownloadFailed where
|
||||
eBase _ = 5000
|
||||
eNum (DownloadFailed xs) = 5000 + eNum xs
|
||||
eDesc _ = "A download failed."
|
||||
|
||||
data InstallSetError = forall xs1 xs2 . (Show (V xs1), Pretty (V xs1), HFErrorProject (V xs1), Show (V xs2), Pretty (V xs2), HFErrorProject (V xs2)) => InstallSetError (V xs1) (V xs2)
|
||||
|
||||
instance Pretty InstallSetError where
|
||||
pPrint (InstallSetError reason1 reason2) =
|
||||
text "Both installation and setting the tool failed. Install error was:"
|
||||
<+> pPrint reason1
|
||||
<+> text "\nSet error was:"
|
||||
<+> pPrint reason2
|
||||
|
||||
deriving instance Show InstallSetError
|
||||
|
||||
instance HFErrorProject InstallSetError where
|
||||
eBase _ = 7000
|
||||
-- will there be collisions?
|
||||
eNum (InstallSetError xs1 xs2) = 7000 + eNum xs1 + eNum xs2
|
||||
eDesc _ = "Installation or setting the tool failed."
|
||||
|
||||
|
||||
-- | A test failed.
|
||||
data TestFailed = forall es . (ToVariantMaybe TestFailed es, PopVariant TestFailed es, Pretty (V es), Show (V es), HFErrorProject (V es)) => TestFailed FilePath (V es)
|
||||
|
||||
instance Pretty TestFailed where
|
||||
pPrint (TestFailed path reason) =
|
||||
case reason of
|
||||
VMaybe (_ :: TestFailed) -> pPrint reason
|
||||
_ -> text ("The test failed. GHC test suite is fragile and non-portable. Please also check out the " <> linkEscapeCode "issue tracker" " https://gitlab.haskell.org/ghc/ghc/-/issues/?sort=updated_desc&state=opened&label_name%5B%5D=testsuite&label_name%5B%5D=packaging&first_page_size=20" <> ".\nBuild dir was:") <+> text path <+> text "\nReason was:" <+> pPrint reason
|
||||
|
||||
deriving instance Show TestFailed
|
||||
|
||||
instance HFErrorProject TestFailed where
|
||||
eBase _ = 4000
|
||||
eNum (TestFailed _ xs2) = 4000 + eNum xs2
|
||||
eDesc _ = "The test failed."
|
||||
|
||||
-- | A build failed.
|
||||
data BuildFailed = forall es . (ToVariantMaybe BuildFailed es, PopVariant BuildFailed es, Pretty (V es), Show (V es)) => BuildFailed FilePath (V es)
|
||||
data BuildFailed = forall es . (ToVariantMaybe BuildFailed es, PopVariant BuildFailed es, Pretty (V es), Show (V es), HFErrorProject (V es)) => BuildFailed FilePath (V es)
|
||||
|
||||
instance Pretty BuildFailed where
|
||||
pPrint (BuildFailed path reason) =
|
||||
@@ -319,18 +738,28 @@ instance Pretty BuildFailed where
|
||||
|
||||
deriving instance Show BuildFailed
|
||||
|
||||
instance HFErrorProject BuildFailed where
|
||||
eBase _ = 8000
|
||||
eNum (BuildFailed _ xs2) = 8000 + eNum xs2
|
||||
eDesc _ = "The build failed."
|
||||
|
||||
|
||||
-- | Setting the current GHC version failed.
|
||||
data GHCupSetError = forall es . (ToVariantMaybe GHCupSetError es, PopVariant GHCupSetError es, Show (V es), Pretty (V es)) => GHCupSetError (V es)
|
||||
data GHCupSetError = forall es . (ToVariantMaybe GHCupSetError es, PopVariant GHCupSetError es, Show (V es), Pretty (V es), HFErrorProject (V es)) => GHCupSetError (V es)
|
||||
|
||||
instance Pretty GHCupSetError where
|
||||
pPrint (GHCupSetError reason) =
|
||||
case reason of
|
||||
VMaybe (_ :: GHCupSetError) -> pPrint reason
|
||||
_ -> text "Setting the current GHC version failed:" <+> pPrint reason
|
||||
_ -> text "Setting the current version failed:" <+> pPrint reason
|
||||
|
||||
deriving instance Show GHCupSetError
|
||||
|
||||
instance HFErrorProject GHCupSetError where
|
||||
eBase _ = 9000
|
||||
eNum (GHCupSetError xs) = 9000 + eNum xs
|
||||
eDesc _ = "Setting the current version failed."
|
||||
|
||||
|
||||
---------------------------------------------
|
||||
--[ True Exceptions (e.g. for MonadThrow) ]--
|
||||
@@ -347,6 +776,10 @@ instance Pretty ParseError where
|
||||
|
||||
instance Exception ParseError
|
||||
|
||||
instance HFErrorProject ParseError where
|
||||
eBase _ = 500
|
||||
eDesc _ = "A parse error occured."
|
||||
|
||||
|
||||
data UnexpectedListLength = UnexpectedListLength String
|
||||
deriving Show
|
||||
@@ -357,6 +790,10 @@ instance Pretty UnexpectedListLength where
|
||||
|
||||
instance Exception UnexpectedListLength
|
||||
|
||||
instance HFErrorProject UnexpectedListLength where
|
||||
eBase _ = 510
|
||||
eDesc _ = "A list had an unexpected length."
|
||||
|
||||
data NoUrlBase = NoUrlBase Text
|
||||
deriving Show
|
||||
|
||||
@@ -366,6 +803,10 @@ instance Pretty NoUrlBase where
|
||||
|
||||
instance Exception NoUrlBase
|
||||
|
||||
instance HFErrorProject NoUrlBase where
|
||||
eBase _ = 520
|
||||
eDesc _ = "URL does not have a base filename."
|
||||
|
||||
|
||||
|
||||
------------------------
|
||||
@@ -385,6 +826,23 @@ instance
|
||||
Right x -> pPrint x
|
||||
Left xs -> pPrint xs
|
||||
|
||||
instance HFErrorProject (V '[]) where
|
||||
{-# INLINABLE eBase #-}
|
||||
eBase _ = undefined
|
||||
{-# INLINABLE eDesc #-}
|
||||
eDesc _ = undefined
|
||||
|
||||
instance
|
||||
( HFErrorProject x
|
||||
, HFErrorProject (V xs)
|
||||
) => HFErrorProject (V (x ': xs))
|
||||
where
|
||||
eNum v = case popVariantHead v of
|
||||
Right x -> eNum x
|
||||
Left xs -> eNum xs
|
||||
eDesc _ = undefined
|
||||
eBase _ = undefined
|
||||
|
||||
instance Pretty URIParseError where
|
||||
pPrint (MalformedScheme reason) =
|
||||
text "Failed to parse URI. Malformed scheme:" <+> text (show reason)
|
||||
@@ -403,6 +861,22 @@ instance Pretty URIParseError where
|
||||
pPrint (OtherError err) =
|
||||
text "Failed to parse URI:" <+> pPrint err
|
||||
|
||||
instance HFErrorProject URIParseError where
|
||||
eBase _ = 800
|
||||
|
||||
eNum (MalformedScheme NonAlphaLeading) = 801
|
||||
eNum (MalformedScheme InvalidChars) = 802
|
||||
eNum (MalformedScheme MissingColon) = 803
|
||||
eNum MalformedUserInfo = 804
|
||||
eNum MalformedQuery = 805
|
||||
eNum MalformedFragment = 806
|
||||
eNum MalformedHost = 807
|
||||
eNum MalformedPort = 808
|
||||
eNum MalformedPath = 809
|
||||
eNum (OtherError _) = 810
|
||||
|
||||
eDesc _ = "Failed to parse URI."
|
||||
|
||||
instance Pretty ArchiveResult where
|
||||
pPrint ArchiveFatal = text "Archive result: fatal"
|
||||
pPrint ArchiveFailed = text "Archive result: failed"
|
||||
@@ -411,5 +885,37 @@ instance Pretty ArchiveResult where
|
||||
pPrint ArchiveOk = text "Archive result: Ok"
|
||||
pPrint ArchiveEOF = text "Archive result: EOF"
|
||||
|
||||
instance HFErrorProject ArchiveResult where
|
||||
eBase _ = 820
|
||||
|
||||
eNum ArchiveFatal = 821
|
||||
eNum ArchiveFailed = 822
|
||||
eNum ArchiveWarn = 823
|
||||
eNum ArchiveRetry = 824
|
||||
eNum ArchiveOk = 825
|
||||
eNum ArchiveEOF = 826
|
||||
|
||||
eDesc _ = "Archive extraction result."
|
||||
|
||||
instance Pretty T.Text where
|
||||
pPrint = text . T.unpack
|
||||
|
||||
instance Pretty ProcessError where
|
||||
pPrint (NonZeroExit e exe args) =
|
||||
text "Process" <+> pPrint exe <+> text "with arguments" <+> pPrint args <+> text "failed with exit code" <+> text (show e <> ".")
|
||||
pPrint (PTerminated exe args) =
|
||||
text "Process" <+> pPrint exe <+> text "with arguments" <+> pPrint args <+> text "terminated."
|
||||
pPrint (PStopped exe args) =
|
||||
text "Process" <+> pPrint exe <+> text "with arguments" <+> pPrint args <+> text "stopped."
|
||||
pPrint (NoSuchPid exe args) =
|
||||
text "Could not find PID for process running " <+> pPrint exe <+> text " with arguments " <+> text (show args) <+> text "."
|
||||
|
||||
instance HFErrorProject ProcessError where
|
||||
eBase _ = 840
|
||||
|
||||
eNum NonZeroExit{} = 841
|
||||
eNum (PTerminated _ _) = 842
|
||||
eNum (PStopped _ _) = 843
|
||||
eNum (NoSuchPid _ _) = 844
|
||||
|
||||
eDesc _ = "A process exited prematurely."
|
||||
|
||||
1305
lib/GHCup/GHC.hs
Normal file
1305
lib/GHCup/GHC.hs
Normal file
File diff suppressed because it is too large
Load Diff
725
lib/GHCup/HLS.hs
Normal file
725
lib/GHCup/HLS.hs
Normal file
@@ -0,0 +1,725 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE BangPatterns #-}
|
||||
{-# LANGUAGE DataKinds #-}
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
{-|
|
||||
Module : GHCup.HLS
|
||||
Description : GHCup installation functions for HLS
|
||||
Copyright : (c) Julian Ospald, 2020
|
||||
License : LGPL-3.0
|
||||
Maintainer : hasufell@hasufell.de
|
||||
Stability : experimental
|
||||
Portability : portable
|
||||
-}
|
||||
module GHCup.HLS where
|
||||
|
||||
import GHCup.Download
|
||||
import GHCup.Errors
|
||||
import GHCup.Types
|
||||
import GHCup.Types.JSON ( )
|
||||
import GHCup.Types.Optics
|
||||
import GHCup.Utils
|
||||
import GHCup.Prelude
|
||||
import GHCup.Prelude.File
|
||||
import GHCup.Prelude.Logger
|
||||
import GHCup.Prelude.Process
|
||||
import GHCup.Prelude.String.QQ
|
||||
|
||||
import Codec.Archive ( ArchiveResult )
|
||||
import Control.Applicative
|
||||
import Control.Exception.Safe
|
||||
import Control.Monad
|
||||
#if !MIN_VERSION_base(4,13,0)
|
||||
import Control.Monad.Fail ( MonadFail )
|
||||
#endif
|
||||
import Control.Monad.Reader
|
||||
import Control.Monad.Trans.Resource
|
||||
hiding ( throwM )
|
||||
import Data.ByteString ( ByteString )
|
||||
import Data.Either
|
||||
import Data.List
|
||||
import Data.Maybe
|
||||
import Data.String ( fromString )
|
||||
import Data.Text ( Text )
|
||||
import Data.Versions hiding ( patch )
|
||||
import Distribution.Types.Version hiding ( Version )
|
||||
import Distribution.Types.PackageId
|
||||
import Distribution.Types.PackageDescription
|
||||
import Distribution.Types.GenericPackageDescription
|
||||
import Distribution.PackageDescription.Parsec
|
||||
import GHC.IO.Exception
|
||||
import Haskus.Utils.Variant.Excepts
|
||||
import Optics
|
||||
import Prelude hiding ( abs
|
||||
, writeFile
|
||||
)
|
||||
import Safe hiding ( at )
|
||||
import System.FilePath
|
||||
import System.IO.Error
|
||||
import Text.Regex.Posix
|
||||
import URI.ByteString
|
||||
|
||||
import qualified Data.List.NonEmpty as NE
|
||||
import qualified Data.ByteString as B
|
||||
import qualified Data.Text as T
|
||||
import qualified Text.Megaparsec as MP
|
||||
|
||||
|
||||
data HLSVer = SourceDist Version
|
||||
| GitDist GitBranch
|
||||
| HackageDist Version
|
||||
| RemoteDist URI
|
||||
|
||||
|
||||
|
||||
--------------------
|
||||
--[ Installation ]--
|
||||
--------------------
|
||||
|
||||
|
||||
-- | Like 'installHLSBin, except takes the 'DownloadInfo' as
|
||||
-- argument instead of looking it up from 'GHCupDownloads'.
|
||||
installHLSBindist :: ( MonadMask m
|
||||
, MonadCatch m
|
||||
, MonadReader env m
|
||||
, HasPlatformReq env
|
||||
, HasDirs env
|
||||
, HasSettings env
|
||||
, HasLog env
|
||||
, MonadResource m
|
||||
, MonadIO m
|
||||
, MonadUnliftIO m
|
||||
, MonadFail m
|
||||
)
|
||||
=> DownloadInfo
|
||||
-> Version
|
||||
-> InstallDir -- ^ isolated install path, if user passed any
|
||||
-> Bool -- ^ Force install
|
||||
-> Excepts
|
||||
'[ AlreadyInstalled
|
||||
, CopyError
|
||||
, DigestError
|
||||
, ContentLengthError
|
||||
, GPGError
|
||||
, DownloadFailed
|
||||
, NoDownload
|
||||
, NotInstalled
|
||||
, UnknownArchive
|
||||
, TarDirDoesNotExist
|
||||
, ArchiveResult
|
||||
, FileAlreadyExistsError
|
||||
, ProcessError
|
||||
, DirNotEmpty
|
||||
, UninstallFailed
|
||||
, MergeFileTreeError
|
||||
]
|
||||
m
|
||||
()
|
||||
installHLSBindist dlinfo ver installDir forceInstall = do
|
||||
lift $ logDebug $ "Requested to install hls version " <> prettyVer ver
|
||||
|
||||
PlatformRequest {..} <- lift getPlatformReq
|
||||
Dirs {..} <- lift getDirs
|
||||
|
||||
regularHLSInstalled <- lift $ hlsInstalled ver
|
||||
|
||||
if
|
||||
| not forceInstall
|
||||
, regularHLSInstalled
|
||||
, GHCupInternal <- installDir -> do -- regular install
|
||||
throwE $ AlreadyInstalled HLS ver
|
||||
|
||||
| forceInstall
|
||||
, regularHLSInstalled
|
||||
, GHCupInternal <- installDir -> do -- regular forced install
|
||||
lift $ logInfo "Removing the currently installed version of HLS before force installing!"
|
||||
liftE $ rmHLSVer ver
|
||||
|
||||
| otherwise -> pure ()
|
||||
|
||||
-- download (or use cached version)
|
||||
dl <- liftE $ downloadCached dlinfo Nothing
|
||||
|
||||
-- unpack
|
||||
tmpUnpack <- lift withGHCupTmpDir
|
||||
liftE $ cleanUpOnError tmpUnpack (unpackToDir (fromGHCupPath tmpUnpack) dl)
|
||||
liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromGHCupPath tmpUnpack)
|
||||
|
||||
-- the subdir of the archive where we do the work
|
||||
workdir <- fromGHCupPath <$> maybe (pure tmpUnpack) (liftE . intoSubdir tmpUnpack) (view dlSubdir dlinfo)
|
||||
legacy <- liftIO $ isLegacyHLSBindist workdir
|
||||
|
||||
if
|
||||
| not forceInstall
|
||||
, not legacy
|
||||
, (IsolateDir fp) <- installDir -> liftE $ installDestSanityCheck (IsolateDirResolved fp)
|
||||
| otherwise -> pure ()
|
||||
|
||||
case installDir of
|
||||
IsolateDir isoDir -> do
|
||||
lift $ logInfo $ "isolated installing HLS to " <> T.pack isoDir
|
||||
if legacy
|
||||
then liftE $ installHLSUnpackedLegacy workdir (IsolateDirResolved isoDir) ver forceInstall
|
||||
else liftE $ runBuildAction tmpUnpack $ installHLSUnpacked workdir (IsolateDirResolved isoDir) ver forceInstall
|
||||
|
||||
GHCupInternal -> do
|
||||
if legacy
|
||||
then liftE $ installHLSUnpackedLegacy workdir (GHCupBinDir binDir) ver forceInstall
|
||||
else do
|
||||
inst <- ghcupHLSDir ver
|
||||
liftE $ runBuildAction tmpUnpack
|
||||
$ installHLSUnpacked workdir (GHCupDir inst) ver forceInstall
|
||||
liftE $ setHLS ver SetHLS_XYZ Nothing
|
||||
|
||||
|
||||
isLegacyHLSBindist :: FilePath -- ^ Path to the unpacked hls bindist
|
||||
-> IO Bool
|
||||
isLegacyHLSBindist path = do
|
||||
not <$> doesFileExist (path </> "GNUmakefile")
|
||||
|
||||
-- | Install an unpacked hls distribution.
|
||||
installHLSUnpacked :: ( MonadMask m
|
||||
, MonadUnliftIO m
|
||||
, MonadReader env m
|
||||
, MonadFail m
|
||||
, HasLog env
|
||||
, HasDirs env
|
||||
, HasSettings env
|
||||
, MonadCatch m
|
||||
, MonadIO m
|
||||
, MonadResource m
|
||||
, HasPlatformReq env
|
||||
)
|
||||
=> FilePath -- ^ Path to the unpacked hls bindist (where the executable resides)
|
||||
-> InstallDirResolved -- ^ Path to install to
|
||||
-> Version
|
||||
-> Bool
|
||||
-> Excepts '[ProcessError, CopyError, FileAlreadyExistsError, NotInstalled, MergeFileTreeError] m ()
|
||||
installHLSUnpacked path inst ver forceInstall = do
|
||||
PlatformRequest { .. } <- lift getPlatformReq
|
||||
lift $ logInfo "Installing HLS"
|
||||
tmpInstallDest <- lift withGHCupTmpDir
|
||||
lEM $ make ["DESTDIR=" <> fromGHCupPath tmpInstallDest, "PREFIX=" <> fromInstallDir inst, "install"] (Just path)
|
||||
liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromGHCupPath tmpInstallDest)
|
||||
liftE $ mergeFileTree (tmpInstallDest `appendGHCupPath` dropDrive (fromInstallDir inst))
|
||||
inst
|
||||
HLS
|
||||
(mkTVer ver)
|
||||
(\f t -> liftIO $ do
|
||||
mtime <- ifM (pathIsSymbolicLink f) (pure Nothing) (Just <$> getModificationTime f)
|
||||
install f t (not forceInstall)
|
||||
forM_ mtime $ setModificationTime t)
|
||||
|
||||
-- | Install an unpacked hls distribution (legacy).
|
||||
installHLSUnpackedLegacy :: (MonadReader env m, MonadFail m, HasLog env, MonadCatch m, MonadIO m)
|
||||
=> FilePath -- ^ Path to the unpacked hls bindist (where the executable resides)
|
||||
-> InstallDirResolved -- ^ Path to install to
|
||||
-> Version
|
||||
-> Bool -- ^ is it a force install
|
||||
-> Excepts '[CopyError, FileAlreadyExistsError] m ()
|
||||
installHLSUnpackedLegacy path installDir ver forceInstall = do
|
||||
lift $ logInfo "Installing HLS"
|
||||
liftIO $ createDirRecursive' (fromInstallDir installDir)
|
||||
|
||||
-- install haskell-language-server-<ghcver>
|
||||
bins@(_:_) <- liftIO $ findFiles
|
||||
path
|
||||
(makeRegexOpts compExtended
|
||||
execBlank
|
||||
([s|^haskell-language-server-[0-9].*$|] :: ByteString)
|
||||
)
|
||||
forM_ bins $ \f -> do
|
||||
let toF = dropSuffix exeExt f
|
||||
<> (case installDir of
|
||||
IsolateDirResolved _ -> ""
|
||||
_ -> ("~" <>) . T.unpack . prettyVer $ ver
|
||||
)
|
||||
<> exeExt
|
||||
|
||||
let srcPath = path </> f
|
||||
let destPath = fromInstallDir installDir </> toF
|
||||
|
||||
-- destination could be an existing symlink
|
||||
-- for new make-based HLSes
|
||||
liftIO $ rmFileForce destPath
|
||||
|
||||
copyFileE
|
||||
srcPath
|
||||
destPath
|
||||
(not forceInstall)
|
||||
lift $ chmod_755 destPath
|
||||
|
||||
-- install haskell-language-server-wrapper
|
||||
let wrapper = "haskell-language-server-wrapper"
|
||||
toF = wrapper
|
||||
<> (case installDir of
|
||||
IsolateDirResolved _ -> ""
|
||||
_ -> ("-" <>) . T.unpack . prettyVer $ ver
|
||||
)
|
||||
<> exeExt
|
||||
srcWrapperPath = path </> wrapper <> exeExt
|
||||
destWrapperPath = fromInstallDir installDir </> toF
|
||||
|
||||
liftIO $ rmFileForce destWrapperPath
|
||||
copyFileE
|
||||
srcWrapperPath
|
||||
destWrapperPath
|
||||
(not forceInstall)
|
||||
|
||||
lift $ chmod_755 destWrapperPath
|
||||
|
||||
|
||||
|
||||
-- | Installs hls binaries @haskell-language-server-\<ghcver\>@
|
||||
-- into @~\/.ghcup\/bin/@, as well as @haskell-languager-server-wrapper@.
|
||||
installHLSBin :: ( MonadMask m
|
||||
, MonadCatch m
|
||||
, MonadReader env m
|
||||
, HasPlatformReq env
|
||||
, HasGHCupInfo env
|
||||
, HasDirs env
|
||||
, HasSettings env
|
||||
, HasLog env
|
||||
, MonadResource m
|
||||
, MonadIO m
|
||||
, MonadUnliftIO m
|
||||
, MonadFail m
|
||||
)
|
||||
=> Version
|
||||
-> InstallDir
|
||||
-> Bool -- force install
|
||||
-> Excepts
|
||||
'[ AlreadyInstalled
|
||||
, CopyError
|
||||
, DigestError
|
||||
, ContentLengthError
|
||||
, GPGError
|
||||
, DownloadFailed
|
||||
, NoDownload
|
||||
, NotInstalled
|
||||
, UnknownArchive
|
||||
, TarDirDoesNotExist
|
||||
, ArchiveResult
|
||||
, FileAlreadyExistsError
|
||||
, ProcessError
|
||||
, DirNotEmpty
|
||||
, UninstallFailed
|
||||
, MergeFileTreeError
|
||||
]
|
||||
m
|
||||
()
|
||||
installHLSBin ver installDir forceInstall = do
|
||||
dlinfo <- liftE $ getDownloadInfo HLS ver
|
||||
installHLSBindist dlinfo ver installDir forceInstall
|
||||
|
||||
|
||||
compileHLS :: ( MonadMask m
|
||||
, MonadCatch m
|
||||
, MonadReader env m
|
||||
, HasDirs env
|
||||
, HasSettings env
|
||||
, HasPlatformReq env
|
||||
, HasGHCupInfo env
|
||||
, HasLog env
|
||||
, MonadResource m
|
||||
, MonadIO m
|
||||
, MonadUnliftIO m
|
||||
, MonadFail m
|
||||
)
|
||||
=> HLSVer
|
||||
-> [Version]
|
||||
-> Maybe Int
|
||||
-> Either Bool Version
|
||||
-> InstallDir
|
||||
-> Maybe (Either FilePath URI)
|
||||
-> Maybe URI
|
||||
-> Bool
|
||||
-> Maybe (Either FilePath [URI]) -- ^ patches
|
||||
-> [Text] -- ^ additional args to cabal install
|
||||
-> Excepts '[ NoDownload
|
||||
, GPGError
|
||||
, DownloadFailed
|
||||
, DigestError
|
||||
, ContentLengthError
|
||||
, UnknownArchive
|
||||
, TarDirDoesNotExist
|
||||
, ArchiveResult
|
||||
, BuildFailed
|
||||
, NotInstalled
|
||||
] m Version
|
||||
compileHLS targetHLS ghcs jobs ov installDir cabalProject cabalProjectLocal updateCabal patches cabalArgs = do
|
||||
PlatformRequest { .. } <- lift getPlatformReq
|
||||
GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
|
||||
Dirs { .. } <- lift getDirs
|
||||
|
||||
when updateCabal $ reThrowAll @_ @'[ProcessError] DownloadFailed $ do
|
||||
lift $ logInfo "Updating cabal DB"
|
||||
lEM $ exec "cabal" ["update"] (Just $ fromGHCupPath tmpDir) Nothing
|
||||
|
||||
(workdir, tmpUnpack, tver, git_describe) <- case targetHLS of
|
||||
-- unpack from version tarball
|
||||
SourceDist tver -> do
|
||||
lift $ logDebug $ "Requested to compile: " <> prettyVer tver
|
||||
|
||||
-- download source tarball
|
||||
dlInfo <-
|
||||
preview (ix HLS % ix tver % viSourceDL % _Just) dls
|
||||
?? NoDownload
|
||||
dl <- liftE $ downloadCached dlInfo Nothing
|
||||
|
||||
-- unpack
|
||||
tmpUnpack <- lift mkGhcupTmpDir
|
||||
liftE $ cleanUpOnError tmpUnpack (unpackToDir (fromGHCupPath tmpUnpack) dl)
|
||||
liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromGHCupPath tmpUnpack)
|
||||
|
||||
workdir <- maybe (pure tmpUnpack)
|
||||
(liftE . intoSubdir tmpUnpack)
|
||||
(view dlSubdir dlInfo)
|
||||
|
||||
pure (workdir, tmpUnpack, tver, Nothing)
|
||||
|
||||
HackageDist tver -> do
|
||||
lift $ logDebug $ "Requested to compile (from hackage): " <> prettyVer tver
|
||||
|
||||
-- download source tarball
|
||||
tmpUnpack <- lift mkGhcupTmpDir
|
||||
let hls = "haskell-language-server-" <> T.unpack (prettyVer tver)
|
||||
reThrowAll @_ @'[ProcessError] DownloadFailed $ do
|
||||
-- unpack
|
||||
lEM $ exec "cabal" ["unpack", hls] (Just $ fromGHCupPath tmpUnpack) Nothing
|
||||
|
||||
let workdir = appendGHCupPath tmpUnpack hls
|
||||
|
||||
pure (workdir, tmpUnpack, tver, Nothing)
|
||||
|
||||
RemoteDist uri -> do
|
||||
lift $ logDebug $ "Requested to compile (from uri): " <> T.pack (show uri)
|
||||
|
||||
-- download source tarball
|
||||
tmpDownload <- lift withGHCupTmpDir
|
||||
tmpUnpack <- lift mkGhcupTmpDir
|
||||
tar <- liftE $ download uri Nothing Nothing Nothing (fromGHCupPath tmpDownload) Nothing False
|
||||
(cf, tver) <- liftE $ cleanUpOnError tmpUnpack $ do
|
||||
unpackToDir (fromGHCupPath tmpUnpack) tar
|
||||
let regex = [s|^(.*/)*haskell-language-server\.cabal$|] :: B.ByteString
|
||||
[cabalFile] <- liftIO $ findFilesDeep
|
||||
tmpUnpack
|
||||
(makeRegexOpts compExtended
|
||||
execBlank
|
||||
regex
|
||||
)
|
||||
tver <- getCabalVersion (fromGHCupPath tmpUnpack </> cabalFile)
|
||||
pure (cabalFile, tver)
|
||||
|
||||
let workdir = appendGHCupPath tmpUnpack (takeDirectory cf)
|
||||
|
||||
pure (workdir, tmpUnpack, tver, Nothing)
|
||||
|
||||
-- clone from git
|
||||
GitDist GitBranch{..} -> do
|
||||
tmpUnpack <- lift mkGhcupTmpDir
|
||||
let git args = execLogged "git" ("--no-pager":args) (Just $ fromGHCupPath tmpUnpack) "git" Nothing
|
||||
reThrowAll @_ @'[ProcessError] DownloadFailed $ do
|
||||
let rep = fromMaybe "https://github.com/haskell/haskell-language-server.git" repo
|
||||
lift $ logInfo $ "Fetching git repo " <> T.pack rep <> " at ref " <> T.pack ref <> " (this may take a while)"
|
||||
lEM $ git [ "init" ]
|
||||
lEM $ git [ "remote"
|
||||
, "add"
|
||||
, "origin"
|
||||
, fromString rep ]
|
||||
|
||||
-- figure out if we can do a shallow clone
|
||||
remoteBranches <- catchE @ProcessError @'[ProcessError] @'[] (\_ -> pure [])
|
||||
$ fmap processBranches $ gitOut ["ls-remote", "--heads", "origin"] (fromGHCupPath tmpUnpack)
|
||||
let shallow_clone
|
||||
| gitDescribeRequested = False
|
||||
| isCommitHash ref = True
|
||||
| fromString ref `elem` remoteBranches = True
|
||||
| otherwise = False
|
||||
|
||||
lift $ logDebug $ "Shallow clone: " <> T.pack (show shallow_clone)
|
||||
|
||||
-- fetch
|
||||
let fetch_args
|
||||
| shallow_clone = ["fetch", "--depth", "1", "--quiet", "origin", fromString ref]
|
||||
| otherwise = ["fetch", "--tags", "--quiet", "origin" ]
|
||||
lEM $ git fetch_args
|
||||
|
||||
-- checkout
|
||||
lEM $ git [ "checkout", fromString ref ]
|
||||
|
||||
-- gather some info
|
||||
git_describe <- if shallow_clone
|
||||
then pure Nothing
|
||||
else fmap Just $ gitOut ["describe", "--tags"] (fromGHCupPath tmpUnpack)
|
||||
chash <- gitOut ["rev-parse", "HEAD" ] (fromGHCupPath tmpUnpack)
|
||||
tver <- getCabalVersion (fromGHCupPath tmpUnpack </> "haskell-language-server.cabal")
|
||||
|
||||
liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromGHCupPath tmpUnpack)
|
||||
lift $ logInfo $ "Examining git ref " <> T.pack ref <> "\n " <>
|
||||
"HLS version (from cabal file): " <> prettyVer tver <>
|
||||
(if not shallow_clone then "\n " <> "'git describe' output: " <> fromJust git_describe else mempty) <>
|
||||
(if isCommitHash ref then mempty else "\n " <> "commit hash: " <> chash)
|
||||
|
||||
pure (tmpUnpack, tmpUnpack, tver, git_describe)
|
||||
|
||||
-- the version that's installed may differ from the
|
||||
-- compiled version, so the user can overwrite it
|
||||
installVer <- case ov of
|
||||
Left True -> case git_describe of
|
||||
-- git describe
|
||||
Just h -> either (fail . displayException) pure . version $ h
|
||||
-- git describe, but not building from git, lol
|
||||
Nothing -> pure tver
|
||||
-- default: use detected version
|
||||
Left False -> pure tver
|
||||
-- overwrite version with users value
|
||||
Right v -> pure v
|
||||
|
||||
liftE $ runBuildAction
|
||||
tmpUnpack
|
||||
(reThrowAll @_ @'[GPGError, DownloadFailed, DigestError, ContentLengthError, PatchFailed, ProcessError, FileAlreadyExistsError, CopyError] @'[BuildFailed] (BuildFailed $ fromGHCupPath workdir) $ do
|
||||
let tmpInstallDir = fromGHCupPath workdir </> "out"
|
||||
liftIO $ createDirRecursive' tmpInstallDir
|
||||
|
||||
-- apply patches
|
||||
liftE $ applyAnyPatch patches (fromGHCupPath workdir)
|
||||
|
||||
-- set up project files
|
||||
cp <- case cabalProject of
|
||||
Just (Left cp)
|
||||
| isAbsolute cp -> do
|
||||
copyFileE cp (fromGHCupPath workdir </> "cabal.project") False
|
||||
pure "cabal.project"
|
||||
| otherwise -> pure (takeFileName cp)
|
||||
Just (Right uri) -> do
|
||||
tmpUnpack' <- lift withGHCupTmpDir
|
||||
cp <- liftE $ download uri Nothing Nothing Nothing (fromGHCupPath tmpUnpack') (Just "cabal.project") False
|
||||
copyFileE cp (fromGHCupPath workdir </> "cabal.project") False
|
||||
pure "cabal.project"
|
||||
Nothing
|
||||
| HackageDist _ <- targetHLS -> do
|
||||
liftIO $ B.writeFile (fromGHCupPath workdir </> "cabal.project") "packages: ./"
|
||||
pure "cabal.project"
|
||||
| RemoteDist _ <- targetHLS -> do
|
||||
let cabalFile = fromGHCupPath workdir </> "cabal.project"
|
||||
liftIO $ whenM (not <$> doesFileExist cabalFile) $ B.writeFile cabalFile "packages: ./"
|
||||
pure "cabal.project"
|
||||
| otherwise -> pure "cabal.project"
|
||||
forM_ cabalProjectLocal $ \uri -> do
|
||||
tmpUnpack' <- lift withGHCupTmpDir
|
||||
cpl <- liftE $ download uri Nothing Nothing Nothing (fromGHCupPath tmpUnpack') (Just (cp <.> "local")) False
|
||||
copyFileE cpl (fromGHCupPath workdir </> cp <.> "local") False
|
||||
artifacts <- forM (sort ghcs) $ \ghc -> do
|
||||
let ghcInstallDir = tmpInstallDir </> T.unpack (prettyVer ghc)
|
||||
liftIO $ createDirRecursive' tmpInstallDir
|
||||
lift $ logInfo $ "Building HLS " <> prettyVer installVer <> " for GHC version " <> prettyVer ghc
|
||||
liftE $ lEM @_ @'[ProcessError] $
|
||||
execLogged "cabal" ( [ "v2-install"
|
||||
, "-w"
|
||||
, "ghc-" <> T.unpack (prettyVer ghc)
|
||||
, "--install-method=copy"
|
||||
] ++
|
||||
maybe [] (\j -> ["--jobs=" <> show j]) jobs ++
|
||||
[ "--overwrite-policy=always"
|
||||
, "--disable-profiling"
|
||||
, "--disable-tests"
|
||||
, "--installdir=" <> ghcInstallDir
|
||||
, "--project-file=" <> cp
|
||||
] ++ fmap T.unpack cabalArgs ++ [
|
||||
"exe:haskell-language-server"
|
||||
, "exe:haskell-language-server-wrapper"]
|
||||
)
|
||||
(Just $ fromGHCupPath workdir)
|
||||
"cabal"
|
||||
Nothing
|
||||
pure ghcInstallDir
|
||||
|
||||
forM_ artifacts $ \artifact -> do
|
||||
logDebug $ T.pack (show artifact)
|
||||
liftIO $ renameFile (artifact </> "haskell-language-server" <.> exeExt)
|
||||
(tmpInstallDir </> "haskell-language-server-" <> takeFileName artifact <.> exeExt)
|
||||
liftIO $ renameFile (artifact </> "haskell-language-server-wrapper" <.> exeExt)
|
||||
(tmpInstallDir </> "haskell-language-server-wrapper" <.> exeExt)
|
||||
|
||||
case installDir of
|
||||
IsolateDir isoDir -> do
|
||||
lift $ logInfo $ "isolated installing HLS to " <> T.pack isoDir
|
||||
liftE $ installHLSUnpackedLegacy tmpInstallDir (IsolateDirResolved isoDir) installVer True
|
||||
GHCupInternal -> do
|
||||
liftE $ installHLSUnpackedLegacy tmpInstallDir (GHCupBinDir binDir) installVer True
|
||||
)
|
||||
|
||||
pure installVer
|
||||
where
|
||||
gitDescribeRequested = case ov of
|
||||
Left b -> b
|
||||
_ -> False
|
||||
|
||||
|
||||
-----------------
|
||||
--[ Set/Unset ]--
|
||||
-----------------
|
||||
|
||||
-- | Set the haskell-language-server symlinks.
|
||||
setHLS :: ( MonadReader env m
|
||||
, HasDirs env
|
||||
, HasLog env
|
||||
, MonadIO m
|
||||
, MonadMask m
|
||||
, MonadFail m
|
||||
, MonadUnliftIO m
|
||||
)
|
||||
=> Version
|
||||
-> SetHLS
|
||||
-> Maybe FilePath -- if set, signals that we're not operating in ~/.ghcup/bin
|
||||
-- and don't want mess with other versions
|
||||
-> Excepts '[NotInstalled] m ()
|
||||
setHLS ver shls mBinDir = do
|
||||
whenM (lift $ not <$> hlsInstalled ver) (throwE (NotInstalled HLS (GHCTargetVersion Nothing ver)))
|
||||
|
||||
-- symlink destination
|
||||
binDir <- case mBinDir of
|
||||
Just x -> pure x
|
||||
Nothing -> do
|
||||
Dirs {binDir = f} <- lift getDirs
|
||||
pure f
|
||||
|
||||
-- first delete the old symlinks
|
||||
when (isNothing mBinDir) $
|
||||
case shls of
|
||||
-- not for legacy
|
||||
SetHLS_XYZ -> liftE $ rmMinorHLSSymlinks ver
|
||||
-- legacy and new
|
||||
SetHLSOnly -> liftE rmPlainHLS
|
||||
|
||||
case shls of
|
||||
-- not for legacy
|
||||
SetHLS_XYZ -> do
|
||||
bins <- lift $ hlsInternalServerScripts ver Nothing
|
||||
|
||||
forM_ bins $ \f -> do
|
||||
let fname = takeFileName f
|
||||
destL <- binarySymLinkDestination binDir f
|
||||
let target = if "haskell-language-server-wrapper" `isPrefixOf` fname
|
||||
then fname <> "-" <> T.unpack (prettyVer ver) <> exeExt
|
||||
else fname <> "~" <> T.unpack (prettyVer ver) <> exeExt
|
||||
lift $ createLink destL (binDir </> target)
|
||||
|
||||
-- legacy and new
|
||||
SetHLSOnly -> do
|
||||
-- set haskell-language-server-<ghcver> symlinks
|
||||
bins <- lift $ hlsServerBinaries ver Nothing
|
||||
when (null bins) $ throwE $ NotInstalled HLS (GHCTargetVersion Nothing ver)
|
||||
|
||||
forM_ bins $ \f -> do
|
||||
let destL = f
|
||||
let target = (<> exeExt) . head . splitOn "~" $ f
|
||||
lift $ createLink destL (binDir </> target)
|
||||
|
||||
-- set haskell-language-server-wrapper symlink
|
||||
let destL = "haskell-language-server-wrapper-" <> T.unpack (prettyVer ver) <> exeExt
|
||||
let wrapper = binDir </> "haskell-language-server-wrapper" <> exeExt
|
||||
|
||||
lift $ createLink destL wrapper
|
||||
|
||||
when (isNothing mBinDir) $
|
||||
lift warnAboutHlsCompatibility
|
||||
|
||||
liftIO (isShadowed wrapper) >>= \case
|
||||
Nothing -> pure ()
|
||||
Just pa -> lift $ logWarn $ T.pack $ prettyHFError (ToolShadowed HLS pa wrapper ver)
|
||||
|
||||
|
||||
unsetHLS :: ( MonadMask m
|
||||
, MonadReader env m
|
||||
, HasDirs env
|
||||
, MonadIO m)
|
||||
=> m ()
|
||||
unsetHLS = do
|
||||
Dirs {..} <- getDirs
|
||||
let wrapper = binDir </> "haskell-language-server-wrapper" <> exeExt
|
||||
bins <- liftIO $ handleIO (\_ -> pure []) $ findFiles'
|
||||
binDir
|
||||
(MP.chunk "haskell-language-server-" <* pvp' <* MP.chunk (T.pack exeExt) <* MP.eof)
|
||||
forM_ bins (hideError doesNotExistErrorType . rmLink . (binDir </>))
|
||||
hideError doesNotExistErrorType $ rmLink wrapper
|
||||
|
||||
|
||||
|
||||
|
||||
---------------
|
||||
--[ Removal ]--
|
||||
---------------
|
||||
|
||||
|
||||
-- | Delete a hls version. Will try to fix the hls symlinks
|
||||
-- after removal (e.g. setting it to an older version).
|
||||
rmHLSVer :: ( MonadMask m
|
||||
, MonadReader env m
|
||||
, HasDirs env
|
||||
, MonadThrow m
|
||||
, HasLog env
|
||||
, MonadIO m
|
||||
, MonadFail m
|
||||
, MonadCatch m
|
||||
, MonadUnliftIO m
|
||||
)
|
||||
=> Version
|
||||
-> Excepts '[NotInstalled, UninstallFailed] m ()
|
||||
rmHLSVer ver = do
|
||||
whenM (lift $ fmap not $ hlsInstalled ver) $ throwE (NotInstalled HLS (GHCTargetVersion Nothing ver))
|
||||
|
||||
isHlsSet <- lift hlsSet
|
||||
|
||||
liftE $ rmMinorHLSSymlinks ver
|
||||
|
||||
when (Just ver == isHlsSet) $ do
|
||||
-- delete all set symlinks
|
||||
liftE rmPlainHLS
|
||||
|
||||
hlsDir' <- ghcupHLSDir ver
|
||||
let hlsDir = fromGHCupPath hlsDir'
|
||||
lift (getInstalledFiles HLS (mkTVer ver)) >>= \case
|
||||
Just files -> do
|
||||
lift $ logInfo $ "Removing files safely from: " <> T.pack hlsDir
|
||||
forM_ files (lift . hideError NoSuchThing . recycleFile . (\f -> hlsDir </> dropDrive f))
|
||||
removeEmptyDirsRecursive hlsDir
|
||||
survivors <- liftIO $ hideErrorDef [doesNotExistErrorType] [] $ listDirectory hlsDir
|
||||
f <- recordedInstallationFile HLS (mkTVer ver)
|
||||
lift $ recycleFile f
|
||||
when (not (null survivors)) $ throwE $ UninstallFailed hlsDir survivors
|
||||
Nothing -> do
|
||||
isDir <- liftIO $ doesDirectoryExist hlsDir
|
||||
isSyml <- liftIO $ handleIO (\_ -> pure False) $ pathIsSymbolicLink hlsDir
|
||||
when (isDir && not isSyml) $ do
|
||||
lift $ logInfo $ "Removing legacy directory recursively: " <> T.pack hlsDir
|
||||
recyclePathForcibly hlsDir'
|
||||
|
||||
when (Just ver == isHlsSet) $ do
|
||||
-- set latest hls
|
||||
hlsVers <- lift $ fmap rights getInstalledHLSs
|
||||
case headMay . reverse . sort $ hlsVers of
|
||||
Just latestver -> liftE $ setHLS latestver SetHLSOnly Nothing
|
||||
Nothing -> pure ()
|
||||
|
||||
|
||||
getCabalVersion :: (MonadIO m, MonadFail m) => FilePath -> m Version
|
||||
getCabalVersion fp = do
|
||||
contents <- liftIO $ B.readFile fp
|
||||
gpd <- case parseGenericPackageDescriptionMaybe contents of
|
||||
Nothing -> fail $ "could not parse cabal file: " <> fp
|
||||
Just r -> pure r
|
||||
let tver = (\c -> Version Nothing c [] Nothing)
|
||||
. NE.fromList . fmap (NE.fromList . (:[]) . digits . fromIntegral)
|
||||
. versionNumbers
|
||||
. pkgVersion
|
||||
. package
|
||||
. packageDescription
|
||||
$ gpd
|
||||
pure tver
|
||||
448
lib/GHCup/List.hs
Normal file
448
lib/GHCup/List.hs
Normal file
@@ -0,0 +1,448 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE DataKinds #-}
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
{-|
|
||||
Module : GHCup.List
|
||||
Description : Listing versions and tools
|
||||
Copyright : (c) Julian Ospald, 2020
|
||||
License : LGPL-3.0
|
||||
Maintainer : hasufell@hasufell.de
|
||||
Stability : experimental
|
||||
Portability : portable
|
||||
-}
|
||||
module GHCup.List where
|
||||
|
||||
import GHCup.Download
|
||||
import GHCup.Errors
|
||||
import GHCup.Types
|
||||
import GHCup.Types.JSON ( )
|
||||
import GHCup.Types.Optics
|
||||
import GHCup.Utils
|
||||
import GHCup.Prelude.Logger
|
||||
import GHCup.Version
|
||||
|
||||
import Control.Applicative
|
||||
import Control.Exception.Safe
|
||||
import Control.Monad
|
||||
#if !MIN_VERSION_base(4,13,0)
|
||||
import Control.Monad.Fail ( MonadFail )
|
||||
#endif
|
||||
import Control.Monad.Reader
|
||||
import Data.Either
|
||||
import Data.List
|
||||
import Data.Maybe
|
||||
import Data.Text ( Text )
|
||||
import Data.Time.Calendar ( Day )
|
||||
import Data.Versions hiding ( patch )
|
||||
import Haskus.Utils.Variant.Excepts
|
||||
import Optics
|
||||
import Prelude hiding ( abs
|
||||
, writeFile
|
||||
)
|
||||
|
||||
import qualified Data.Map.Strict as Map
|
||||
import qualified Data.Text as T
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
------------------
|
||||
--[ List tools ]--
|
||||
------------------
|
||||
|
||||
|
||||
-- | Filter data type for 'listVersions'.
|
||||
data ListCriteria = ListInstalled Bool
|
||||
| ListSet Bool
|
||||
| ListAvailable Bool
|
||||
deriving Show
|
||||
|
||||
-- | A list result describes a single tool version
|
||||
-- and various of its properties.
|
||||
data ListResult = ListResult
|
||||
{ lTool :: Tool
|
||||
, lVer :: Version
|
||||
, lCross :: Maybe Text -- ^ currently only for GHC
|
||||
, lTag :: [Tag]
|
||||
, lInstalled :: Bool
|
||||
, lSet :: Bool -- ^ currently active version
|
||||
, fromSrc :: Bool -- ^ compiled from source
|
||||
, lStray :: Bool -- ^ not in download info
|
||||
, lNoBindist :: Bool -- ^ whether the version is available for this platform/arch
|
||||
, hlsPowered :: Bool
|
||||
, lReleaseDay :: Maybe Day
|
||||
}
|
||||
deriving (Eq, Ord, Show)
|
||||
|
||||
|
||||
-- | Extract all available tool versions and their tags.
|
||||
availableToolVersions :: GHCupDownloads -> Tool -> Map.Map Version VersionInfo
|
||||
availableToolVersions av tool = view
|
||||
(at tool % non Map.empty)
|
||||
av
|
||||
|
||||
|
||||
-- | List all versions from the download info, as well as stray
|
||||
-- versions.
|
||||
listVersions :: ( MonadCatch m
|
||||
, HasLog env
|
||||
, MonadThrow m
|
||||
, HasLog env
|
||||
, MonadIO m
|
||||
, MonadReader env m
|
||||
, HasDirs env
|
||||
, HasPlatformReq env
|
||||
, HasGHCupInfo env
|
||||
)
|
||||
=> Maybe Tool
|
||||
-> [ListCriteria]
|
||||
-> Bool
|
||||
-> Bool
|
||||
-> (Maybe Day, Maybe Day)
|
||||
-> m [ListResult]
|
||||
listVersions lt' criteria hideOld showNightly days = do
|
||||
-- some annoying work to avoid too much repeated IO
|
||||
cSet <- cabalSet
|
||||
cabals <- getInstalledCabals
|
||||
hlsSet' <- hlsSet
|
||||
hlses <- getInstalledHLSs
|
||||
sSet <- stackSet
|
||||
stacks <- getInstalledStacks
|
||||
|
||||
go lt' cSet cabals hlsSet' hlses sSet stacks
|
||||
where
|
||||
go lt cSet cabals hlsSet' hlses sSet stacks = do
|
||||
case lt of
|
||||
Just t -> do
|
||||
GHCupInfo { _ghcupDownloads = dls } <- getGHCupInfo
|
||||
-- get versions from GHCupDownloads
|
||||
let avTools = availableToolVersions dls t
|
||||
lr <- filter' <$> forM (Map.toList avTools) (toListResult t cSet cabals hlsSet' hlses sSet stacks)
|
||||
|
||||
case t of
|
||||
GHC -> do
|
||||
slr <- strayGHCs avTools
|
||||
pure (sort (slr ++ lr))
|
||||
Cabal -> do
|
||||
slr <- strayCabals avTools cSet cabals
|
||||
pure (sort (slr ++ lr))
|
||||
HLS -> do
|
||||
slr <- strayHLS avTools hlsSet' hlses
|
||||
pure (sort (slr ++ lr))
|
||||
Stack -> do
|
||||
slr <- strayStacks avTools sSet stacks
|
||||
pure (sort (slr ++ lr))
|
||||
GHCup -> do
|
||||
let cg = maybeToList $ currentGHCup avTools
|
||||
pure (sort (cg ++ lr))
|
||||
Nothing -> do
|
||||
ghcvers <- go (Just GHC) cSet cabals hlsSet' hlses sSet stacks
|
||||
cabalvers <- go (Just Cabal) cSet cabals hlsSet' hlses sSet stacks
|
||||
hlsvers <- go (Just HLS) cSet cabals hlsSet' hlses sSet stacks
|
||||
ghcupvers <- go (Just GHCup) cSet cabals hlsSet' hlses sSet stacks
|
||||
stackvers <- go (Just Stack) cSet cabals hlsSet' hlses sSet stacks
|
||||
pure (ghcvers <> cabalvers <> hlsvers <> stackvers <> ghcupvers)
|
||||
strayGHCs :: ( MonadCatch m
|
||||
, MonadReader env m
|
||||
, HasDirs env
|
||||
, MonadThrow m
|
||||
, HasLog env
|
||||
, MonadIO m
|
||||
)
|
||||
=> Map.Map Version VersionInfo
|
||||
-> m [ListResult]
|
||||
strayGHCs avTools = do
|
||||
ghcs <- getInstalledGHCs
|
||||
fmap catMaybes $ forM ghcs $ \case
|
||||
Right tver@GHCTargetVersion{ _tvTarget = Nothing, .. } -> do
|
||||
case Map.lookup _tvVersion avTools of
|
||||
Just _ -> pure Nothing
|
||||
Nothing -> do
|
||||
lSet <- fmap (maybe False (\(GHCTargetVersion _ v ) -> v == _tvVersion)) $ ghcSet Nothing
|
||||
fromSrc <- ghcSrcInstalled tver
|
||||
hlsPowered <- fmap (elem _tvVersion) hlsGHCVersions
|
||||
pure $ Just $ ListResult
|
||||
{ lTool = GHC
|
||||
, lVer = _tvVersion
|
||||
, lCross = Nothing
|
||||
, lTag = []
|
||||
, lInstalled = True
|
||||
, lStray = isNothing (Map.lookup _tvVersion avTools)
|
||||
, lNoBindist = False
|
||||
, lReleaseDay = Nothing
|
||||
, ..
|
||||
}
|
||||
Right tver@GHCTargetVersion{ .. } -> do
|
||||
lSet <- fmap (maybe False (\(GHCTargetVersion _ v ) -> v == _tvVersion)) $ ghcSet _tvTarget
|
||||
fromSrc <- ghcSrcInstalled tver
|
||||
hlsPowered <- fmap (elem _tvVersion) hlsGHCVersions
|
||||
pure $ Just $ ListResult
|
||||
{ lTool = GHC
|
||||
, lVer = _tvVersion
|
||||
, lCross = _tvTarget
|
||||
, lTag = []
|
||||
, lInstalled = True
|
||||
, lStray = True -- NOTE: cross currently cannot be installed via bindist
|
||||
, lNoBindist = False
|
||||
, lReleaseDay = Nothing
|
||||
, ..
|
||||
}
|
||||
Left e -> do
|
||||
logWarn
|
||||
$ "Could not parse version of stray directory" <> T.pack e
|
||||
pure Nothing
|
||||
|
||||
strayCabals :: ( MonadReader env m
|
||||
, HasDirs env
|
||||
, MonadCatch m
|
||||
, MonadThrow m
|
||||
, HasLog env
|
||||
, MonadIO m
|
||||
)
|
||||
=> Map.Map Version VersionInfo
|
||||
-> Maybe Version
|
||||
-> [Either FilePath Version]
|
||||
-> m [ListResult]
|
||||
strayCabals avTools cSet cabals = do
|
||||
fmap catMaybes $ forM cabals $ \case
|
||||
Right ver ->
|
||||
case Map.lookup ver avTools of
|
||||
Just _ -> pure Nothing
|
||||
Nothing -> do
|
||||
let lSet = cSet == Just ver
|
||||
pure $ Just $ ListResult
|
||||
{ lTool = Cabal
|
||||
, lVer = ver
|
||||
, lCross = Nothing
|
||||
, lTag = []
|
||||
, lInstalled = True
|
||||
, lStray = isNothing (Map.lookup ver avTools)
|
||||
, lNoBindist = False
|
||||
, fromSrc = False -- actually, we don't know :>
|
||||
, hlsPowered = False
|
||||
, lReleaseDay = Nothing
|
||||
, ..
|
||||
}
|
||||
Left e -> do
|
||||
logWarn
|
||||
$ "Could not parse version of stray directory" <> T.pack e
|
||||
pure Nothing
|
||||
|
||||
strayHLS :: ( MonadReader env m
|
||||
, HasDirs env
|
||||
, MonadCatch m
|
||||
, MonadThrow m
|
||||
, HasLog env
|
||||
, MonadIO m)
|
||||
=> Map.Map Version VersionInfo
|
||||
-> Maybe Version
|
||||
-> [Either FilePath Version]
|
||||
-> m [ListResult]
|
||||
strayHLS avTools hlsSet' hlss = do
|
||||
fmap catMaybes $ forM hlss $ \case
|
||||
Right ver ->
|
||||
case Map.lookup ver avTools of
|
||||
Just _ -> pure Nothing
|
||||
Nothing -> do
|
||||
let lSet = hlsSet' == Just ver
|
||||
pure $ Just $ ListResult
|
||||
{ lTool = HLS
|
||||
, lVer = ver
|
||||
, lCross = Nothing
|
||||
, lTag = []
|
||||
, lInstalled = True
|
||||
, lStray = isNothing (Map.lookup ver avTools)
|
||||
, lNoBindist = False
|
||||
, fromSrc = False -- actually, we don't know :>
|
||||
, hlsPowered = False
|
||||
, lReleaseDay = Nothing
|
||||
, ..
|
||||
}
|
||||
Left e -> do
|
||||
logWarn
|
||||
$ "Could not parse version of stray directory" <> T.pack e
|
||||
pure Nothing
|
||||
|
||||
strayStacks :: ( MonadReader env m
|
||||
, HasDirs env
|
||||
, MonadCatch m
|
||||
, MonadThrow m
|
||||
, HasLog env
|
||||
, MonadIO m
|
||||
)
|
||||
=> Map.Map Version VersionInfo
|
||||
-> Maybe Version
|
||||
-> [Either FilePath Version]
|
||||
-> m [ListResult]
|
||||
strayStacks avTools stackSet' stacks = do
|
||||
fmap catMaybes $ forM stacks $ \case
|
||||
Right ver ->
|
||||
case Map.lookup ver avTools of
|
||||
Just _ -> pure Nothing
|
||||
Nothing -> do
|
||||
let lSet = stackSet' == Just ver
|
||||
pure $ Just $ ListResult
|
||||
{ lTool = Stack
|
||||
, lVer = ver
|
||||
, lCross = Nothing
|
||||
, lTag = []
|
||||
, lInstalled = True
|
||||
, lStray = isNothing (Map.lookup ver avTools)
|
||||
, lNoBindist = False
|
||||
, fromSrc = False -- actually, we don't know :>
|
||||
, hlsPowered = False
|
||||
, lReleaseDay = Nothing
|
||||
, ..
|
||||
}
|
||||
Left e -> do
|
||||
logWarn
|
||||
$ "Could not parse version of stray directory" <> T.pack e
|
||||
pure Nothing
|
||||
|
||||
currentGHCup :: Map.Map Version VersionInfo -> Maybe ListResult
|
||||
currentGHCup av =
|
||||
let currentVer = fromJust $ pvpToVersion ghcUpVer ""
|
||||
listVer = Map.lookup currentVer av
|
||||
latestVer = fst <$> headOf (getTagged Latest) av
|
||||
recommendedVer = fst <$> headOf (getTagged Latest) av
|
||||
isOld = maybe True (> currentVer) latestVer && maybe True (> currentVer) recommendedVer
|
||||
in if | Map.member currentVer av -> Nothing
|
||||
| otherwise -> Just $ ListResult { lVer = currentVer
|
||||
, lTag = maybe (if isOld then [Old] else []) _viTags listVer
|
||||
, lCross = Nothing
|
||||
, lTool = GHCup
|
||||
, fromSrc = False
|
||||
, lStray = isNothing listVer
|
||||
, lSet = True
|
||||
, lInstalled = True
|
||||
, lNoBindist = False
|
||||
, hlsPowered = False
|
||||
, lReleaseDay = Nothing
|
||||
}
|
||||
|
||||
-- NOTE: this are not cross ones, because no bindists
|
||||
toListResult :: ( HasLog env
|
||||
, MonadReader env m
|
||||
, HasDirs env
|
||||
, HasGHCupInfo env
|
||||
, HasPlatformReq env
|
||||
, MonadIO m
|
||||
, MonadCatch m
|
||||
)
|
||||
=> Tool
|
||||
-> Maybe Version
|
||||
-> [Either FilePath Version]
|
||||
-> Maybe Version
|
||||
-> [Either FilePath Version]
|
||||
-> Maybe Version
|
||||
-> [Either FilePath Version]
|
||||
-> (Version, VersionInfo)
|
||||
-> m ListResult
|
||||
toListResult t cSet cabals hlsSet' hlses stackSet' stacks (v, VersionInfo{..}) = do
|
||||
case t of
|
||||
GHC -> do
|
||||
lNoBindist <- fmap (isLeft . veitherToEither) $ runE @'[NoDownload] $ getDownloadInfo GHC v
|
||||
let tver = mkTVer v
|
||||
lSet <- fmap (maybe False (\(GHCTargetVersion _ v') -> v' == v)) $ ghcSet Nothing
|
||||
lInstalled <- ghcInstalled tver
|
||||
fromSrc <- ghcSrcInstalled tver
|
||||
hlsPowered <- fmap (elem v) hlsGHCVersions
|
||||
pure ListResult { lVer = v, lCross = Nothing , lTag = _viTags, lTool = t, lStray = False, lReleaseDay = _viReleaseDay, .. }
|
||||
Cabal -> do
|
||||
lNoBindist <- fmap (isLeft . veitherToEither) $ runE @'[NoDownload] $ getDownloadInfo Cabal v
|
||||
let lSet = cSet == Just v
|
||||
let lInstalled = elem v $ rights cabals
|
||||
pure ListResult { lVer = v
|
||||
, lCross = Nothing
|
||||
, lTag = _viTags
|
||||
, lTool = t
|
||||
, fromSrc = False
|
||||
, lStray = False
|
||||
, hlsPowered = False
|
||||
, lReleaseDay = _viReleaseDay
|
||||
, ..
|
||||
}
|
||||
GHCup -> do
|
||||
let lSet = prettyPVP ghcUpVer == prettyVer v
|
||||
let lInstalled = lSet
|
||||
pure ListResult { lVer = v
|
||||
, lTag = _viTags
|
||||
, lCross = Nothing
|
||||
, lTool = t
|
||||
, fromSrc = False
|
||||
, lStray = False
|
||||
, lNoBindist = False
|
||||
, hlsPowered = False
|
||||
, lReleaseDay = _viReleaseDay
|
||||
, ..
|
||||
}
|
||||
HLS -> do
|
||||
lNoBindist <- fmap (isLeft . veitherToEither) $ runE @'[NoDownload] $ getDownloadInfo HLS v
|
||||
let lSet = hlsSet' == Just v
|
||||
let lInstalled = elem v $ rights hlses
|
||||
pure ListResult { lVer = v
|
||||
, lCross = Nothing
|
||||
, lTag = _viTags
|
||||
, lTool = t
|
||||
, fromSrc = False
|
||||
, lStray = False
|
||||
, hlsPowered = False
|
||||
, lReleaseDay = _viReleaseDay
|
||||
, ..
|
||||
}
|
||||
Stack -> do
|
||||
lNoBindist <- fmap (isLeft . veitherToEither) $ runE @'[NoDownload] $ getDownloadInfo Stack v
|
||||
let lSet = stackSet' == Just v
|
||||
let lInstalled = elem v $ rights stacks
|
||||
pure ListResult { lVer = v
|
||||
, lCross = Nothing
|
||||
, lTag = _viTags
|
||||
, lTool = t
|
||||
, fromSrc = False
|
||||
, lStray = False
|
||||
, hlsPowered = False
|
||||
, lReleaseDay = _viReleaseDay
|
||||
, ..
|
||||
}
|
||||
|
||||
|
||||
filter' :: [ListResult] -> [ListResult]
|
||||
filter' = filterNightly . filterOld . filter (\lr -> foldr (\a b -> fromCriteria a lr && b) True criteria) . filterDays
|
||||
|
||||
filterDays :: [ListResult] -> [ListResult]
|
||||
filterDays lrs = case days of
|
||||
(Nothing, Nothing) -> lrs
|
||||
(Just from, Just to') -> filter (\ListResult{..} -> maybe False (\d -> d >= from && d <= to') lReleaseDay) lrs
|
||||
(Nothing, Just to') -> filter (\ListResult{..} -> maybe False (<= to') lReleaseDay) lrs
|
||||
(Just from, Nothing) -> filter (\ListResult{..} -> maybe False (>= from) lReleaseDay) lrs
|
||||
|
||||
fromCriteria :: ListCriteria -> ListResult -> Bool
|
||||
fromCriteria lc ListResult{..} = case lc of
|
||||
ListInstalled b -> f b lInstalled
|
||||
ListSet b -> f b lSet
|
||||
ListAvailable b -> f b $ not lNoBindist
|
||||
where
|
||||
f b
|
||||
| b = id
|
||||
| otherwise = not
|
||||
|
||||
filterOld :: [ListResult] -> [ListResult]
|
||||
filterOld lr
|
||||
| hideOld = filter (\ListResult {..} -> lInstalled || Old `notElem` lTag) lr
|
||||
| otherwise = lr
|
||||
|
||||
filterNightly :: [ListResult] -> [ListResult]
|
||||
filterNightly lr
|
||||
| showNightly = lr
|
||||
| otherwise = filter (\ListResult {..} -> lInstalled || (Nightly `notElem` lTag && LatestNightly `notElem` lTag)) lr
|
||||
|
||||
@@ -23,10 +23,11 @@ import GHCup.Errors
|
||||
import GHCup.Types
|
||||
import GHCup.Types.Optics
|
||||
import GHCup.Types.JSON ( )
|
||||
import GHCup.Utils.File
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Utils.Prelude
|
||||
import GHCup.Utils.String.QQ
|
||||
import GHCup.Utils.Dirs
|
||||
import GHCup.Prelude
|
||||
import GHCup.Prelude.Logger
|
||||
import GHCup.Prelude.Process
|
||||
import GHCup.Prelude.String.QQ
|
||||
|
||||
#if !MIN_VERSION_base(4,13,0)
|
||||
import Control.Monad.Fail ( MonadFail )
|
||||
@@ -46,7 +47,6 @@ import Prelude hiding ( abs
|
||||
, writeFile
|
||||
)
|
||||
import System.Info
|
||||
import System.Directory
|
||||
import System.OsRelease
|
||||
import Text.PrettyPrint.HughesPJClass ( prettyShow )
|
||||
import Text.Regex.Posix
|
||||
|
||||
90
lib/GHCup/Prelude.hs
Normal file
90
lib/GHCup/Prelude.hs
Normal file
@@ -0,0 +1,90 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE DataKinds #-}
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
{-# LANGUAGE TypeFamilies #-}
|
||||
{-# LANGUAGE TypeOperators #-}
|
||||
|
||||
{-|
|
||||
Module : GHCup.Prelude
|
||||
Description : MegaParsec utilities
|
||||
Copyright : (c) Julian Ospald, 2020
|
||||
License : LGPL-3.0
|
||||
Maintainer : hasufell@hasufell.de
|
||||
Stability : experimental
|
||||
Portability : portable
|
||||
|
||||
GHCup specific prelude. Lots of Excepts functionality.
|
||||
-}
|
||||
module GHCup.Prelude
|
||||
(module GHCup.Prelude,
|
||||
module GHCup.Prelude.Internal,
|
||||
#if defined(IS_WINDOWS)
|
||||
module GHCup.Prelude.Windows
|
||||
#else
|
||||
module GHCup.Prelude.Posix
|
||||
#endif
|
||||
)
|
||||
where
|
||||
|
||||
import GHCup.Errors
|
||||
import GHCup.Prelude.Internal
|
||||
import GHCup.Types.Optics (HasLog)
|
||||
import GHCup.Prelude.Logger (logWarn)
|
||||
#if defined(IS_WINDOWS)
|
||||
import GHCup.Prelude.Windows
|
||||
#else
|
||||
import GHCup.Prelude.Posix
|
||||
#endif
|
||||
|
||||
import Control.Monad.IO.Class
|
||||
import Control.Monad.Reader
|
||||
import Haskus.Utils.Variant.Excepts
|
||||
import Text.PrettyPrint.HughesPJClass ( Pretty )
|
||||
import qualified Data.Text as T
|
||||
|
||||
|
||||
|
||||
-- for some obscure reason... this won't type-check if we move it to a different module
|
||||
catchWarn :: forall es m env . ( Pretty (V es)
|
||||
, HFErrorProject (V es)
|
||||
, MonadReader env m
|
||||
, HasLog env
|
||||
, MonadIO m
|
||||
, Monad m) => Excepts es m () -> Excepts '[] m ()
|
||||
catchWarn = catchAllE @_ @es (\v -> lift $ logWarn (T.pack . prettyHFError $ v))
|
||||
|
||||
|
||||
runBothE' :: forall e m a b .
|
||||
( Monad m
|
||||
, Show (V e)
|
||||
, Pretty (V e)
|
||||
, HFErrorProject (V e)
|
||||
, PopVariant InstallSetError e
|
||||
, LiftVariant' e (InstallSetError ': e)
|
||||
, e :<< (InstallSetError ': e)
|
||||
)
|
||||
=> Excepts e m a
|
||||
-> Excepts e m b
|
||||
-> Excepts (InstallSetError ': e) m ()
|
||||
runBothE' a1 a2 = do
|
||||
r1 <- lift $ runE @e a1
|
||||
r2 <- lift $ runE @e a2
|
||||
case (r1, r2) of
|
||||
(VLeft e1, VLeft e2) -> throwE (InstallSetError e1 e2)
|
||||
(VLeft e , _ ) -> throwSomeE e
|
||||
(_ , VLeft e ) -> throwSomeE e
|
||||
(VRight _, VRight _) -> pure ()
|
||||
|
||||
-- "throwSomeE" function has been upstreamed in haskus-utils-variant-3.3
|
||||
-- So, only conditionally include this shim if
|
||||
-- haskus-utils-variant version is < 3.3
|
||||
|
||||
#if MIN_VERSION_haskus_utils_variant(3,3,0)
|
||||
#else
|
||||
-- | Throw some exception
|
||||
throwSomeE :: forall es' es a m. (Monad m, LiftVariant es' es) => V es' -> Excepts es m a
|
||||
{-# INLINABLE throwSomeE #-}
|
||||
throwSomeE = Excepts . pure . VLeft . liftVariant
|
||||
#endif
|
||||
426
lib/GHCup/Prelude/File.hs
Normal file
426
lib/GHCup/Prelude/File.hs
Normal file
@@ -0,0 +1,426 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE BangPatterns #-}
|
||||
{-# LANGUAGE DataKinds #-}
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE TypeFamilies #-}
|
||||
{-# LANGUAGE TypeOperators #-}
|
||||
|
||||
module GHCup.Prelude.File (
|
||||
mergeFileTree,
|
||||
copyFileE,
|
||||
findFilesDeep,
|
||||
getDirectoryContentsRecursive,
|
||||
getDirectoryContentsRecursiveBFS,
|
||||
getDirectoryContentsRecursiveDFS,
|
||||
getDirectoryContentsRecursiveUnsafe,
|
||||
getDirectoryContentsRecursiveBFSUnsafe,
|
||||
getDirectoryContentsRecursiveDFSUnsafe,
|
||||
recordedInstallationFile,
|
||||
module GHCup.Prelude.File.Search,
|
||||
|
||||
chmod_755,
|
||||
isBrokenSymlink,
|
||||
copyFile,
|
||||
deleteFile,
|
||||
install,
|
||||
removeEmptyDirectory,
|
||||
removeDirIfEmptyOrIsSymlink,
|
||||
removeEmptyDirsRecursive,
|
||||
rmFileForce,
|
||||
createDirRecursive',
|
||||
recyclePathForcibly,
|
||||
rmDirectory,
|
||||
recycleFile,
|
||||
rmFile,
|
||||
rmDirectoryLink,
|
||||
moveFilePortable,
|
||||
moveFile,
|
||||
rmPathForcibly,
|
||||
|
||||
exeExt,
|
||||
exeExt',
|
||||
getLinkTarget,
|
||||
pathIsLink,
|
||||
rmLink,
|
||||
createLink
|
||||
) where
|
||||
|
||||
import GHCup.Utils.Dirs
|
||||
import GHCup.Prelude.Logger.Internal (logInfo, logDebug)
|
||||
import GHCup.Prelude.Internal
|
||||
import GHCup.Prelude.File.Search
|
||||
#if IS_WINDOWS
|
||||
import GHCup.Prelude.File.Windows
|
||||
import GHCup.Prelude.Windows
|
||||
#else
|
||||
import GHCup.Prelude.File.Posix
|
||||
import GHCup.Prelude.Posix
|
||||
#endif
|
||||
import GHCup.Errors
|
||||
import GHCup.Types
|
||||
import GHCup.Types.Optics
|
||||
|
||||
import Text.Regex.Posix
|
||||
import Control.Monad.IO.Unlift ( MonadUnliftIO )
|
||||
import Control.Exception.Safe
|
||||
import Control.Monad.Reader
|
||||
import Data.ByteString ( ByteString )
|
||||
import Haskus.Utils.Variant.Excepts
|
||||
import System.FilePath
|
||||
import Text.PrettyPrint.HughesPJClass (prettyShow)
|
||||
|
||||
import qualified Data.Text as T
|
||||
import qualified Streamly.Prelude as S
|
||||
import Control.DeepSeq (force)
|
||||
import Control.Exception (evaluate)
|
||||
import GHC.IO.Exception
|
||||
import System.IO.Error
|
||||
|
||||
|
||||
-- | Merge one file tree to another given a copy operation.
|
||||
--
|
||||
-- Records every successfully installed file into the destination
|
||||
-- returned by 'recordedInstallationFile'.
|
||||
--
|
||||
-- If any copy operation fails, the record file is deleted, as well
|
||||
-- as the partially installed files.
|
||||
mergeFileTree :: ( MonadMask m
|
||||
, S.MonadAsync m
|
||||
, MonadReader env m
|
||||
, HasDirs env
|
||||
, HasLog env
|
||||
, MonadCatch m
|
||||
)
|
||||
=> GHCupPath -- ^ source base directory from which to install findFiles
|
||||
-> InstallDirResolved -- ^ destination base dir
|
||||
-> Tool
|
||||
-> GHCTargetVersion
|
||||
-> (FilePath -> FilePath -> m ()) -- ^ file copy operation
|
||||
-> Excepts '[MergeFileTreeError] m ()
|
||||
mergeFileTree _ (GHCupBinDir fp) _ _ _ =
|
||||
throwIO $ userError ("mergeFileTree: internal error, called on " <> fp)
|
||||
mergeFileTree sourceBase destBase tool v' copyOp = do
|
||||
lift $ logInfo $ "Merging file tree from \""
|
||||
<> T.pack (fromGHCupPath sourceBase)
|
||||
<> "\" to \""
|
||||
<> T.pack (fromInstallDir destBase)
|
||||
<> "\""
|
||||
recFile <- recordedInstallationFile tool v'
|
||||
|
||||
wrapInExcepts $ do
|
||||
-- These checks are not atomic, but we perform them to have
|
||||
-- the opportunity to abort before copying has started.
|
||||
--
|
||||
-- The actual copying might still fail.
|
||||
liftIO $ baseCheck (fromGHCupPath sourceBase)
|
||||
liftIO $ destCheck (fromInstallDir destBase)
|
||||
|
||||
-- we only record for non-isolated installs
|
||||
when (isSafeDir destBase) $ do
|
||||
whenM (liftIO $ doesFileExist recFile)
|
||||
$ throwIO $ userError ("mergeFileTree: DB file " <> recFile <> " already exists!")
|
||||
liftIO $ createDirectoryIfMissing True (takeDirectory recFile)
|
||||
|
||||
-- we want the cleanup action to leak through in case of exception
|
||||
onE_ (cleanupOnPartialInstall recFile) $ wrapInExcepts $ do
|
||||
logDebug "Starting merge"
|
||||
lift $ flip S.mapM_ (getDirectoryContentsRecursive sourceBase) $ \f -> do
|
||||
copy f
|
||||
logDebug $ T.pack "Recording installed file: " <> T.pack f
|
||||
recordInstalledFile f recFile
|
||||
pure f
|
||||
|
||||
where
|
||||
wrapInExcepts = handleIO (\e -> throwE $ MergeFileTreeError e (fromGHCupPath sourceBase) (fromInstallDir destBase))
|
||||
|
||||
cleanupOnPartialInstall recFile = when (isSafeDir destBase) $ do
|
||||
(force -> !l) <- hideErrorDef [NoSuchThing] [] $ lines <$> liftIO
|
||||
(readFile recFile >>= evaluate)
|
||||
logDebug "Deleting recorded files due to partial install"
|
||||
forM_ l $ \f -> do
|
||||
let dest = fromInstallDir destBase </> dropDrive f
|
||||
logDebug $ "rm -f " <> T.pack f
|
||||
hideError NoSuchThing $ rmFile dest
|
||||
pure ()
|
||||
logDebug $ "rm -f " <> T.pack recFile
|
||||
hideError NoSuchThing $ rmFile recFile
|
||||
logDebug $ "rm -f " <> T.pack (fromInstallDir destBase)
|
||||
hideError UnsatisfiedConstraints $ hideError NoSuchThing $
|
||||
removeEmptyDirsRecursive (fromInstallDir destBase)
|
||||
|
||||
|
||||
recordInstalledFile f recFile = when (isSafeDir destBase) $
|
||||
liftIO $ appendFile recFile (f <> "\n")
|
||||
|
||||
copy source = do
|
||||
let dest = fromInstallDir destBase </> source
|
||||
src = fromGHCupPath sourceBase </> source
|
||||
|
||||
when (isAbsolute source)
|
||||
$ throwIO $ userError ("mergeFileTree: source file " <> source <> " is not relative!")
|
||||
|
||||
liftIO . createDirectoryIfMissing True . takeDirectory $ dest
|
||||
|
||||
copyOp src dest
|
||||
|
||||
|
||||
baseCheck src = do
|
||||
when (isRelative src)
|
||||
$ throwIO $ userError ("mergeFileTree: source base directory " <> src <> " is not absolute!")
|
||||
whenM (not <$> doesDirectoryExist src)
|
||||
$ throwIO $ userError ("mergeFileTree: source base directory " <> src <> " does not exist!")
|
||||
destCheck dest = do
|
||||
when (isRelative dest)
|
||||
$ throwIO $ userError ("mergeFileTree: destination base directory " <> dest <> " is not absolute!")
|
||||
|
||||
|
||||
|
||||
copyFileE :: (CopyError :< xs, MonadCatch m, MonadIO m) => FilePath -> FilePath -> Bool -> Excepts xs m ()
|
||||
copyFileE from to = handleIO (throwE . CopyError . show) . liftIO . copyFile from to
|
||||
|
||||
|
||||
-- | 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.
|
||||
--
|
||||
-- depth first
|
||||
getDirectoryContentsRecursiveDFS :: (MonadCatch m, S.MonadAsync m, MonadMask m)
|
||||
=> GHCupPath
|
||||
-> S.SerialT m FilePath
|
||||
getDirectoryContentsRecursiveDFS (fromGHCupPath -> fp) = getDirectoryContentsRecursiveDFSUnsafe fp
|
||||
|
||||
-- breadth first
|
||||
getDirectoryContentsRecursiveBFS :: (MonadCatch m, S.MonadAsync m, MonadMask m)
|
||||
=> GHCupPath
|
||||
-> S.SerialT m FilePath
|
||||
getDirectoryContentsRecursiveBFS (fromGHCupPath -> fp) = getDirectoryContentsRecursiveBFSUnsafe fp
|
||||
|
||||
|
||||
getDirectoryContentsRecursive :: (MonadCatch m, S.MonadAsync m, MonadMask m)
|
||||
=> GHCupPath
|
||||
-> S.SerialT m FilePath
|
||||
getDirectoryContentsRecursive = getDirectoryContentsRecursiveBFS
|
||||
|
||||
getDirectoryContentsRecursiveUnsafe :: (MonadCatch m, S.MonadAsync m, MonadMask m)
|
||||
=> FilePath
|
||||
-> S.SerialT m FilePath
|
||||
getDirectoryContentsRecursiveUnsafe = getDirectoryContentsRecursiveBFSUnsafe
|
||||
|
||||
findFilesDeep :: GHCupPath -> Regex -> IO [FilePath]
|
||||
findFilesDeep path regex =
|
||||
S.toList $ S.filter (match regex) $ getDirectoryContentsRecursive path
|
||||
|
||||
|
||||
recordedInstallationFile :: ( MonadReader env m
|
||||
, HasDirs env
|
||||
)
|
||||
=> Tool
|
||||
-> GHCTargetVersion
|
||||
-> m FilePath
|
||||
recordedInstallationFile t v' = do
|
||||
Dirs {..} <- getDirs
|
||||
pure (fromGHCupPath dbDir </> prettyShow t </> T.unpack (tVerToText v'))
|
||||
|
||||
removeDirIfEmptyOrIsSymlink :: (MonadMask m, MonadIO m, MonadCatch m) => FilePath -> m ()
|
||||
removeDirIfEmptyOrIsSymlink filepath =
|
||||
hideError UnsatisfiedConstraints $
|
||||
handleIO' InappropriateType
|
||||
(handleIfSym filepath)
|
||||
(liftIO $ removeEmptyDirectory filepath)
|
||||
where
|
||||
handleIfSym fp e = do
|
||||
isSym <- liftIO $ pathIsSymbolicLink fp
|
||||
if isSym
|
||||
then rmFileForce fp
|
||||
else liftIO $ ioError e
|
||||
|
||||
removeEmptyDirsRecursive :: (MonadMask m, MonadIO m, MonadCatch m) => FilePath -> m ()
|
||||
removeEmptyDirsRecursive = go
|
||||
where
|
||||
go fp = do
|
||||
cs <- liftIO $ listDirectory fp >>= filterM doesDirectoryExist . fmap (fp </>)
|
||||
forM_ cs go
|
||||
liftIO $ removeEmptyDirectory fp
|
||||
|
||||
rmFileForce :: (MonadMask m, MonadIO m) => FilePath -> m ()
|
||||
rmFileForce filepath = do
|
||||
hideError doesNotExistErrorType
|
||||
$ hideError InappropriateType $ rmFile filepath
|
||||
|
||||
-- | More permissive version of 'createDirRecursive'. This doesn't
|
||||
-- error when the destination is a symlink to a directory.
|
||||
createDirRecursive' :: FilePath -> IO ()
|
||||
createDirRecursive' p =
|
||||
handleIO (\e -> if isAlreadyExistsError e then isSymlinkDir e else throwIO e)
|
||||
. createDirectoryIfMissing True
|
||||
$ p
|
||||
|
||||
where
|
||||
isSymlinkDir e = do
|
||||
ft <- pathIsSymbolicLink p
|
||||
case ft of
|
||||
True -> do
|
||||
rp <- canonicalizePath p
|
||||
rft <- doesDirectoryExist rp
|
||||
case rft of
|
||||
True -> pure ()
|
||||
_ -> throwIO e
|
||||
_ -> throwIO e
|
||||
|
||||
|
||||
-- https://github.com/haskell/directory/issues/110
|
||||
-- https://github.com/haskell/directory/issues/96
|
||||
-- https://www.sqlite.org/src/info/89f1848d7f
|
||||
recyclePathForcibly :: ( MonadIO m
|
||||
, MonadReader env m
|
||||
, HasDirs env
|
||||
, MonadMask m
|
||||
)
|
||||
=> GHCupPath
|
||||
-> m ()
|
||||
recyclePathForcibly fp
|
||||
| isWindows = do
|
||||
Dirs { recycleDir } <- getDirs
|
||||
tmp <- liftIO $ createTempGHCupDirectory recycleDir "recyclePathForcibly"
|
||||
let dest = tmp `appendGHCupPath` takeFileName (fromGHCupPath fp)
|
||||
liftIO (moveFile (fromGHCupPath fp) (fromGHCupPath dest))
|
||||
`catch`
|
||||
(\e -> if | isDoesNotExistError e -> pure ()
|
||||
| isPermissionError e || ioeGetErrorType e == UnsupportedOperation {- EXDEV on windows -} -> recover (liftIO $ removePathForcibly fp)
|
||||
| otherwise -> throwIO e)
|
||||
`finally`
|
||||
liftIO (handleIO (\_ -> pure ()) $ removePathForcibly tmp)
|
||||
| otherwise = liftIO $ removePathForcibly fp
|
||||
|
||||
|
||||
|
||||
rmDirectory :: (MonadIO m, MonadMask m)
|
||||
=> GHCupPath
|
||||
-> m ()
|
||||
rmDirectory fp
|
||||
| isWindows = recover (liftIO $ removeDirectory fp)
|
||||
| otherwise = liftIO $ removeDirectory fp
|
||||
|
||||
|
||||
-- https://www.sqlite.org/src/info/89f1848d7f
|
||||
-- https://github.com/haskell/directory/issues/96
|
||||
recycleFile :: ( MonadIO m
|
||||
, MonadMask m
|
||||
, MonadReader env m
|
||||
, HasDirs env
|
||||
)
|
||||
=> FilePath
|
||||
-> m ()
|
||||
recycleFile fp
|
||||
| isWindows = do
|
||||
Dirs { recycleDir } <- getDirs
|
||||
liftIO $ whenM (doesDirectoryExist fp) $ ioError (IOError Nothing InappropriateType "recycleFile" "" Nothing (Just fp))
|
||||
tmp <- liftIO $ createTempGHCupDirectory recycleDir "recycleFile"
|
||||
let dest = fromGHCupPath tmp </> takeFileName fp
|
||||
liftIO (moveFile fp dest)
|
||||
`catch`
|
||||
(\e -> if isPermissionError e || ioeGetErrorType e == UnsupportedOperation {- EXDEV on windows -} then recover (liftIO $ rmFile fp) else throwIO e)
|
||||
`finally`
|
||||
liftIO (handleIO (\_ -> pure ()) $ removePathForcibly tmp)
|
||||
| otherwise = liftIO $ removeFile fp
|
||||
|
||||
|
||||
rmFile :: ( MonadIO m
|
||||
, MonadMask m
|
||||
)
|
||||
=> FilePath
|
||||
-> m ()
|
||||
rmFile fp
|
||||
| isWindows = recover (liftIO $ removeFile fp)
|
||||
| otherwise = liftIO $ removeFile fp
|
||||
|
||||
|
||||
rmDirectoryLink :: (MonadIO m, MonadMask m, MonadReader env m, HasDirs env)
|
||||
=> FilePath
|
||||
-> m ()
|
||||
rmDirectoryLink fp
|
||||
| isWindows = recover (liftIO $ removeDirectoryLink fp)
|
||||
| otherwise = liftIO $ removeDirectoryLink fp
|
||||
|
||||
|
||||
rmPathForcibly :: ( MonadIO m
|
||||
, MonadMask m
|
||||
)
|
||||
=> GHCupPath
|
||||
-> m ()
|
||||
rmPathForcibly fp
|
||||
| isWindows = recover (liftIO $ removePathForcibly fp)
|
||||
| otherwise = liftIO $ removePathForcibly fp
|
||||
|
||||
|
||||
-- | The file extension for executables.
|
||||
exeExt :: String
|
||||
exeExt
|
||||
| isWindows = ".exe"
|
||||
| otherwise = ""
|
||||
|
||||
-- | The file extension for executables.
|
||||
exeExt' :: ByteString
|
||||
exeExt'
|
||||
| isWindows = ".exe"
|
||||
| otherwise = ""
|
||||
|
||||
|
||||
rmLink :: (MonadReader env m, HasDirs env, MonadIO m, MonadMask m) => FilePath -> m ()
|
||||
rmLink fp
|
||||
| isWindows = do
|
||||
hideError doesNotExistErrorType . recycleFile $ fp
|
||||
hideError doesNotExistErrorType . recycleFile $ (dropExtension fp <.> "shim")
|
||||
| otherwise = hideError doesNotExistErrorType . recycleFile $ fp
|
||||
|
||||
|
||||
-- | Creates a symbolic link on unix and a fake symlink on windows for
|
||||
-- executables, which:
|
||||
-- 1. is a shim exe
|
||||
-- 2. has a corresponding .shim file in the same directory that
|
||||
-- contains the target
|
||||
--
|
||||
-- This overwrites previously existing files.
|
||||
--
|
||||
-- On windows, this requires that 'ensureGlobalTools' was run beforehand.
|
||||
createLink :: ( MonadMask m
|
||||
, MonadThrow m
|
||||
, HasLog env
|
||||
, MonadIO m
|
||||
, MonadReader env m
|
||||
, HasDirs env
|
||||
, MonadUnliftIO m
|
||||
, MonadFail m
|
||||
)
|
||||
=> FilePath -- ^ path to the target executable
|
||||
-> FilePath -- ^ path to be created
|
||||
-> m ()
|
||||
createLink link exe
|
||||
| isWindows = do
|
||||
dirs <- getDirs
|
||||
let shimGen = fromGHCupPath (cacheDir dirs) </> "gs.exe"
|
||||
|
||||
let shim = dropExtension exe <.> "shim"
|
||||
-- For hardlinks, link needs to be absolute.
|
||||
-- If link is relative, it's relative to the target exe.
|
||||
-- Note that (</>) drops lhs when rhs is absolute.
|
||||
fullLink = takeDirectory exe </> link
|
||||
shimContents = "path = " <> fullLink
|
||||
|
||||
logDebug $ "rm -f " <> T.pack exe
|
||||
rmLink exe
|
||||
|
||||
logDebug $ "ln -s " <> T.pack fullLink <> " " <> T.pack exe
|
||||
liftIO $ copyFile shimGen exe False
|
||||
liftIO $ writeFile shim shimContents
|
||||
| otherwise = do
|
||||
logDebug $ "rm -f " <> T.pack exe
|
||||
hideError doesNotExistErrorType $ recycleFile exe
|
||||
|
||||
logDebug $ "ln -s " <> T.pack link <> " " <> T.pack exe
|
||||
liftIO $ createFileLink link exe
|
||||
335
lib/GHCup/Prelude/File/Posix.hs
Normal file
335
lib/GHCup/Prelude/File/Posix.hs
Normal file
@@ -0,0 +1,335 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE DataKinds #-}
|
||||
{-# LANGUAGE MultiWayIf #-}
|
||||
{-# LANGUAGE CApiFFI #-}
|
||||
|
||||
{-|
|
||||
Module : GHCup.Utils.File.Posix
|
||||
Description : File and directory handling for unix
|
||||
Copyright : (c) Julian Ospald, 2020
|
||||
License : LGPL-3.0
|
||||
Maintainer : hasufell@hasufell.de
|
||||
Stability : experimental
|
||||
Portability : POSIX
|
||||
-}
|
||||
module GHCup.Prelude.File.Posix where
|
||||
|
||||
import GHCup.Prelude.File.Posix.Traversals
|
||||
|
||||
import Control.Exception.Safe
|
||||
import Control.Monad.Reader
|
||||
import Foreign.C.String
|
||||
import Foreign.C.Error
|
||||
import Foreign.C.Types
|
||||
import System.IO ( hClose, hSetBinaryMode )
|
||||
import System.IO.Error hiding ( catchIOError )
|
||||
import System.FilePath
|
||||
import System.Directory ( removeFile, pathIsSymbolicLink, getSymbolicLinkTarget, doesPathExist )
|
||||
import System.Posix.Directory
|
||||
import System.Posix.Error ( throwErrnoPathIfMinus1Retry )
|
||||
import System.Posix.Internals ( withFilePath )
|
||||
import System.Posix.Files
|
||||
import System.Posix.Types
|
||||
|
||||
|
||||
import qualified System.Posix.Directory as PD
|
||||
import qualified System.Posix.Files as PF
|
||||
import qualified System.Posix.IO as SPI
|
||||
import qualified System.Posix as Posix
|
||||
import qualified Streamly.FileSystem.Handle as FH
|
||||
import qualified Streamly.Internal.FileSystem.Handle
|
||||
as IFH
|
||||
import qualified Streamly.Prelude as S
|
||||
import qualified GHCup.Prelude.File.Posix.Foreign as FD
|
||||
import qualified Streamly.Internal.Data.Stream.StreamD.Type
|
||||
as D
|
||||
import Streamly.Internal.Data.Unfold.Type
|
||||
import qualified Streamly.Internal.Data.Unfold as U
|
||||
import Streamly.Internal.Control.Concurrent ( withRunInIO )
|
||||
import Streamly.Internal.Data.IOFinalizer ( newIOFinalizer, runIOFinalizer )
|
||||
import GHC.IO.Exception (IOException(ioe_type), IOErrorType (..))
|
||||
|
||||
|
||||
-- | On unix, we can use symlinks, so we just get the
|
||||
-- symbolic link target.
|
||||
--
|
||||
-- On windows, we have to emulate symlinks via shims,
|
||||
-- see 'createLink'.
|
||||
getLinkTarget :: FilePath -> IO FilePath
|
||||
getLinkTarget = getSymbolicLinkTarget
|
||||
|
||||
|
||||
-- | Checks whether the path is a link.
|
||||
pathIsLink :: FilePath -> IO Bool
|
||||
pathIsLink = pathIsSymbolicLink
|
||||
|
||||
|
||||
chmod_755 :: 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
|
||||
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
|
||||
|
||||
|
||||
-- | Checks whether the binary is a broken link.
|
||||
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
|
||||
|
||||
copyFile :: FilePath -- ^ source file
|
||||
-> FilePath -- ^ destination file
|
||||
-> Bool -- ^ fail if file exists
|
||||
-> IO ()
|
||||
copyFile from to fail' = do
|
||||
bracket
|
||||
(openFdHandle from SPI.ReadOnly [FD.oNofollow] Nothing)
|
||||
(hClose . snd)
|
||||
$ \(fromFd, fH) -> do
|
||||
sourceFileMode <- fileMode <$> getFdStatus fromFd
|
||||
let dflags = [ FD.oNofollow
|
||||
, if fail' then FD.oExcl else FD.oTrunc
|
||||
]
|
||||
let openFdHandle' = openFdHandle to SPI.WriteOnly dflags $ Just sourceFileMode
|
||||
bracket
|
||||
(handleIO (\e -> if
|
||||
-- if we copy from regular file to symlink, we need
|
||||
-- to delete the symlink
|
||||
| ioe_type e == InvalidArgument
|
||||
, not fail' -> do
|
||||
removeLink to
|
||||
openFdHandle'
|
||||
| otherwise -> throwIO e
|
||||
)
|
||||
openFdHandle')
|
||||
(hClose . snd)
|
||||
$ \(_, tH) -> do
|
||||
hSetBinaryMode fH True
|
||||
hSetBinaryMode tH True
|
||||
streamlyCopy (fH, tH)
|
||||
where
|
||||
openFdHandle fp omode flags fM = do
|
||||
fd <- openFd' fp omode flags fM
|
||||
handle' <- SPI.fdToHandle fd
|
||||
pure (fd, handle')
|
||||
streamlyCopy (fH, tH) =
|
||||
S.fold (FH.writeChunks tH) $ IFH.toChunksWithBufferOf (256 * 1024) fH
|
||||
|
||||
foreign import capi unsafe "fcntl.h open"
|
||||
c_open :: CString -> CInt -> Posix.CMode -> IO CInt
|
||||
|
||||
|
||||
open_ :: CString
|
||||
-> Posix.OpenMode
|
||||
-> [FD.Flags]
|
||||
-> Maybe Posix.FileMode
|
||||
-> IO Posix.Fd
|
||||
open_ str how optional_flags maybe_mode = do
|
||||
fd <- c_open str all_flags mode_w
|
||||
return (Posix.Fd fd)
|
||||
where
|
||||
all_flags = FD.unionFlags $ optional_flags ++ [open_mode] ++ creat
|
||||
|
||||
|
||||
(creat, mode_w) = case maybe_mode of
|
||||
Nothing -> ([],0)
|
||||
Just x -> ([FD.oCreat], x)
|
||||
|
||||
open_mode = case how of
|
||||
Posix.ReadOnly -> FD.oRdonly
|
||||
Posix.WriteOnly -> FD.oWronly
|
||||
Posix.ReadWrite -> FD.oRdwr
|
||||
|
||||
|
||||
-- |Open and optionally create this file. See 'System.Posix.Files'
|
||||
-- for information on how to use the 'FileMode' type.
|
||||
--
|
||||
-- Note that passing @Just x@ as the 4th argument triggers the
|
||||
-- `oCreat` status flag, which must be set when you pass in `oExcl`
|
||||
-- to the status flags. Also see the manpage for @open(2)@.
|
||||
openFd' :: FilePath
|
||||
-> Posix.OpenMode
|
||||
-> [FD.Flags] -- ^ status flags of @open(2)@
|
||||
-> Maybe Posix.FileMode -- ^ @Just x@ => creates the file with the given modes, Nothing => the file must exist.
|
||||
-> IO Posix.Fd
|
||||
openFd' name how optional_flags maybe_mode =
|
||||
withFilePath name $ \str ->
|
||||
throwErrnoPathIfMinus1Retry "openFd" name $
|
||||
open_ str how optional_flags maybe_mode
|
||||
|
||||
|
||||
-- |Deletes the given file. Raises `eISDIR`
|
||||
-- if run on a directory. Does not follow symbolic links.
|
||||
--
|
||||
-- Throws:
|
||||
--
|
||||
-- - `InappropriateType` for wrong file type (directory)
|
||||
-- - `NoSuchThing` if the file does not exist
|
||||
-- - `PermissionDenied` if the directory cannot be read
|
||||
--
|
||||
-- Notes: calls `unlink`
|
||||
deleteFile :: FilePath -> IO ()
|
||||
deleteFile = removeLink
|
||||
|
||||
|
||||
-- |Recreate a symlink.
|
||||
--
|
||||
-- In `Overwrite` copy mode only files and empty directories are deleted.
|
||||
--
|
||||
-- Safety/reliability concerns:
|
||||
--
|
||||
-- * `Overwrite` mode is inherently non-atomic
|
||||
--
|
||||
-- Throws:
|
||||
--
|
||||
-- - `InvalidArgument` if source file is wrong type (not a symlink)
|
||||
-- - `PermissionDenied` if output directory cannot be written to
|
||||
-- - `PermissionDenied` if source directory cannot be opened
|
||||
-- - `SameFile` if source and destination are the same file
|
||||
-- (`HPathIOException`)
|
||||
--
|
||||
--
|
||||
-- Throws in `Strict` mode only:
|
||||
--
|
||||
-- - `AlreadyExists` if destination already exists
|
||||
--
|
||||
-- Throws in `Overwrite` mode only:
|
||||
--
|
||||
-- - `UnsatisfiedConstraints` if destination file is non-empty directory
|
||||
--
|
||||
-- Notes:
|
||||
--
|
||||
-- - calls `symlink`
|
||||
recreateSymlink :: FilePath -- ^ the old symlink file
|
||||
-> FilePath -- ^ destination file
|
||||
-> Bool -- ^ fail if destination file exists
|
||||
-> IO ()
|
||||
recreateSymlink symsource newsym fail' = do
|
||||
sympoint <- readSymbolicLink symsource
|
||||
case fail' of
|
||||
True -> pure ()
|
||||
False ->
|
||||
handleIO (\e -> if doesNotExistErrorType == ioeGetErrorType e then pure () else liftIO . ioError $ e) $ deleteFile newsym
|
||||
createSymbolicLink sympoint newsym
|
||||
|
||||
|
||||
-- copys files, recreates symlinks, fails on all other types
|
||||
install :: FilePath -> FilePath -> Bool -> IO ()
|
||||
install from to fail' = do
|
||||
fs <- PF.getSymbolicLinkStatus from
|
||||
decide fs
|
||||
where
|
||||
decide fs | PF.isRegularFile fs = copyFile from to fail'
|
||||
| PF.isSymbolicLink fs = recreateSymlink from to fail'
|
||||
| otherwise = ioError $ mkIOError illegalOperationErrorType "install: not a regular file or symlink" Nothing (Just from)
|
||||
|
||||
moveFile :: FilePath -> FilePath -> IO ()
|
||||
moveFile = rename
|
||||
|
||||
|
||||
moveFilePortable :: FilePath -> FilePath -> IO ()
|
||||
moveFilePortable from to = do
|
||||
catchErrno [eXDEV] (moveFile from to) $ do
|
||||
copyFile from to True
|
||||
removeFile from
|
||||
|
||||
|
||||
catchErrno :: [Errno] -- ^ errno to catch
|
||||
-> IO a -- ^ action to try, which can raise an IOException
|
||||
-> IO a -- ^ action to carry out in case of an IOException and
|
||||
-- if errno matches
|
||||
-> IO a
|
||||
catchErrno en a1 a2 =
|
||||
catchIOError a1 $ \e -> do
|
||||
errno <- getErrno
|
||||
if errno `elem` en
|
||||
then a2
|
||||
else ioError e
|
||||
|
||||
removeEmptyDirectory :: FilePath -> IO ()
|
||||
removeEmptyDirectory = PD.removeDirectory
|
||||
|
||||
|
||||
-- | Create an 'Unfold' of directory contents.
|
||||
unfoldDirContents :: (MonadMask m, MonadIO m, S.MonadAsync m) => Unfold m FilePath (FD.DirType, FilePath)
|
||||
unfoldDirContents = U.bracket (liftIO . openDirStreamPortable) (liftIO . closeDirStreamPortable) (Unfold step return)
|
||||
where
|
||||
{-# INLINE [0] step #-}
|
||||
step dirstream = do
|
||||
(typ, e) <- liftIO $ readDirEntPortable dirstream
|
||||
return $ if
|
||||
| null e -> D.Stop
|
||||
| "." == e -> D.Skip dirstream
|
||||
| ".." == e -> D.Skip dirstream
|
||||
| otherwise -> D.Yield (typ, e) dirstream
|
||||
|
||||
|
||||
getDirectoryContentsRecursiveDFSUnsafe :: (MonadMask m, MonadIO m, S.MonadAsync m)
|
||||
=> FilePath
|
||||
-> S.SerialT m FilePath
|
||||
getDirectoryContentsRecursiveDFSUnsafe fp = go ""
|
||||
where
|
||||
go cd = flip S.concatMap (S.unfold unfoldDirContents (fp </> cd)) $ \(t, f) ->
|
||||
if | t == FD.dtDir -> go (cd </> f)
|
||||
| otherwise -> pure (cd </> f)
|
||||
|
||||
|
||||
getDirectoryContentsRecursiveUnfold :: (MonadMask m, MonadIO m, S.MonadAsync m) => Unfold m FilePath FilePath
|
||||
getDirectoryContentsRecursiveUnfold = Unfold step (\s -> return (s, Nothing, [""]))
|
||||
where
|
||||
{-# INLINE [0] step #-}
|
||||
step (_, Nothing, []) = return D.Stop
|
||||
|
||||
step (topdir, Just (cdir, dirstream, finalizer), dirs) = flip onException (runIOFinalizer finalizer) $ do
|
||||
(dt, f) <- liftIO $ readDirEntPortable dirstream
|
||||
if | f == "" -> do
|
||||
runIOFinalizer finalizer
|
||||
return $ D.Skip (topdir, Nothing, dirs)
|
||||
| f == "." || f == ".."
|
||||
-> return $ D.Skip (topdir, Just (cdir, dirstream, finalizer), dirs)
|
||||
| FD.dtDir == dt -> return $ D.Skip (topdir, Just (cdir, dirstream, finalizer), (cdir </> f):dirs)
|
||||
| otherwise -> return $ D.Yield (cdir </> f) (topdir, Just (cdir, dirstream, finalizer), dirs)
|
||||
|
||||
step (topdir, Nothing, dir:dirs) = do
|
||||
(s, f) <- acquire (topdir </> dir)
|
||||
return $ D.Skip (topdir, Just (dir, s, f), dirs)
|
||||
|
||||
acquire dir =
|
||||
withRunInIO $ \run -> mask_ $ run $ do
|
||||
dirstream <- liftIO $ openDirStreamPortable dir
|
||||
ref <- newIOFinalizer (liftIO $ closeDirStreamPortable dirstream)
|
||||
return (dirstream, ref)
|
||||
|
||||
getDirectoryContentsRecursiveBFSUnsafe :: (MonadMask m, MonadIO m, S.MonadAsync m)
|
||||
=> FilePath
|
||||
-> S.SerialT m FilePath
|
||||
getDirectoryContentsRecursiveBFSUnsafe = S.unfold getDirectoryContentsRecursiveUnfold
|
||||
|
||||
|
||||
58
lib/GHCup/Prelude/File/Posix/Foreign.hsc
Normal file
58
lib/GHCup/Prelude/File/Posix/Foreign.hsc
Normal file
@@ -0,0 +1,58 @@
|
||||
{-# LANGUAGE PatternSynonyms #-}
|
||||
|
||||
module GHCup.Prelude.File.Posix.Foreign where
|
||||
|
||||
import Data.Bits
|
||||
import Data.List (foldl')
|
||||
import Foreign.C.Types
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
newtype DirType = DirType Int deriving (Eq, Show)
|
||||
data Flags = Flags Int | UnsupportedFlag String deriving (Eq, Show)
|
||||
|
||||
unFlags :: Flags -> Int
|
||||
unFlags (Flags i) = i
|
||||
unFlags (UnsupportedFlag name) = error (name ++ " is not supported on this platform")
|
||||
|
||||
-- |Returns @True@ if posix-paths was compiled with support for the provided
|
||||
-- flag. (As of this writing, the only flag for which this check may be
|
||||
-- necessary is 'oCloexec'; all other flags will always yield @True@.)
|
||||
isSupported :: Flags -> Bool
|
||||
isSupported (Flags _) = True
|
||||
isSupported _ = False
|
||||
|
||||
-- |@O_CLOEXEC@ is not supported on every POSIX platform. Use
|
||||
-- @'isSupported' oCloexec@ to determine if support for @O_CLOEXEC@ was
|
||||
-- compiled into your version of posix-paths. (If not, using @oCloexec@ will
|
||||
-- throw an exception.)
|
||||
oCloexec :: Flags
|
||||
#ifdef O_CLOEXEC
|
||||
oCloexec = Flags #{const O_CLOEXEC}
|
||||
#else
|
||||
{-# WARNING oCloexec
|
||||
"This version of posix-paths was compiled without @O_CLOEXEC@ support." #-}
|
||||
oCloexec = UnsupportedFlag "O_CLOEXEC"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
-- If these enum declarations occur earlier in the file, haddock
|
||||
-- gets royally confused about the above doc comments.
|
||||
-- Probably http://trac.haskell.org/haddock/ticket/138
|
||||
|
||||
#{enum DirType, DirType, DT_BLK, DT_CHR, DT_DIR, DT_FIFO, DT_LNK, DT_REG, DT_SOCK, DT_UNKNOWN}
|
||||
|
||||
#{enum Flags, Flags, O_APPEND, O_ASYNC, O_CREAT, O_DIRECTORY, O_EXCL, O_NOCTTY, O_NOFOLLOW, O_NONBLOCK, O_RDONLY, O_WRONLY, O_RDWR, O_SYNC, O_TRUNC}
|
||||
|
||||
pathMax :: Int
|
||||
pathMax = #{const PATH_MAX}
|
||||
|
||||
unionFlags :: [Flags] -> CInt
|
||||
unionFlags = fromIntegral . foldl' ((. unFlags) . (.|.)) 0
|
||||
|
||||
139
lib/GHCup/Prelude/File/Posix/Traversals.hsc
Normal file
139
lib/GHCup/Prelude/File/Posix/Traversals.hsc
Normal file
@@ -0,0 +1,139 @@
|
||||
{-# LANGUAGE CApiFFI #-}
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE ForeignFunctionInterface #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE ViewPatterns #-}
|
||||
|
||||
{-# OPTIONS_GHC -Wall #-}
|
||||
|
||||
|
||||
module GHCup.Prelude.File.Posix.Traversals (
|
||||
-- lower-level stuff
|
||||
readDirEnt
|
||||
, readDirEntPortable
|
||||
, openDirStreamPortable
|
||||
, closeDirStreamPortable
|
||||
, unpackDirStream
|
||||
, DirStreamPortable
|
||||
) where
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
|
||||
#if __GLASGOW_HASKELL__ < 710
|
||||
import Control.Applicative ((<$>))
|
||||
#endif
|
||||
import GHCup.Prelude.File.Posix.Foreign
|
||||
|
||||
import Unsafe.Coerce (unsafeCoerce)
|
||||
import Foreign.C.Error
|
||||
import Foreign.C.String
|
||||
import Foreign.C.Types
|
||||
import Foreign.Ptr
|
||||
import Foreign.Storable
|
||||
import System.Posix
|
||||
import Foreign (alloca)
|
||||
import System.Posix.Internals (peekFilePath)
|
||||
import System.FilePath
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----------------------------------------------------------
|
||||
-- dodgy stuff
|
||||
|
||||
data {-# CTYPE "DIR" #-} CDir
|
||||
data {-# CTYPE "struct dirent" #-} CDirent
|
||||
|
||||
-- Posix doesn't export DirStream, so to re-use that type we need to use
|
||||
-- unsafeCoerce. It's just a newtype, so this is a legitimate usage.
|
||||
-- ugly trick.
|
||||
unpackDirStream :: DirStream -> Ptr CDir
|
||||
unpackDirStream = unsafeCoerce
|
||||
|
||||
-- the __hscore_* functions are defined in the unix package. We can import them and let
|
||||
-- the linker figure it out.
|
||||
foreign import ccall unsafe "__hscore_readdir"
|
||||
c_readdir :: Ptr CDir -> Ptr (Ptr CDirent) -> IO CInt
|
||||
|
||||
foreign import ccall unsafe "__hscore_free_dirent"
|
||||
c_freeDirEnt :: Ptr CDirent -> IO ()
|
||||
|
||||
foreign import ccall unsafe "__hscore_d_name"
|
||||
c_name :: Ptr CDirent -> IO CString
|
||||
|
||||
foreign import capi unsafe "dirutils.h __posixdir_d_type"
|
||||
c_type :: Ptr CDirent -> IO DirType
|
||||
|
||||
----------------------------------------------------------
|
||||
-- less dodgy but still lower-level
|
||||
|
||||
|
||||
readDirEnt :: DirStream -> IO (DirType, FilePath)
|
||||
readDirEnt (unpackDirStream -> dirp) =
|
||||
alloca $ \ptr_dEnt -> loop ptr_dEnt
|
||||
where
|
||||
loop ptr_dEnt = do
|
||||
resetErrno
|
||||
r <- c_readdir dirp ptr_dEnt
|
||||
if r == 0
|
||||
then do
|
||||
dEnt <- peek ptr_dEnt
|
||||
if dEnt == nullPtr
|
||||
then return (dtUnknown, mempty)
|
||||
else do
|
||||
dName <- c_name dEnt >>= peekFilePath
|
||||
dType <- c_type dEnt
|
||||
c_freeDirEnt dEnt
|
||||
return (dType, dName)
|
||||
else do
|
||||
errno <- getErrno
|
||||
if errno == eINTR
|
||||
then loop ptr_dEnt
|
||||
else do
|
||||
let (Errno eo) = errno
|
||||
if eo == 0
|
||||
then return (dtUnknown, mempty)
|
||||
else throwErrno "readDirEnt"
|
||||
|
||||
|
||||
newtype DirStreamPortable = DirStreamPortable (FilePath, DirStream)
|
||||
|
||||
openDirStreamPortable :: FilePath -> IO DirStreamPortable
|
||||
openDirStreamPortable fp = do
|
||||
dirs <- openDirStream fp
|
||||
pure $ DirStreamPortable (fp, dirs)
|
||||
|
||||
closeDirStreamPortable :: DirStreamPortable -> IO ()
|
||||
closeDirStreamPortable (DirStreamPortable (_, dirs)) = closeDirStream dirs
|
||||
|
||||
readDirEntPortable :: DirStreamPortable -> IO (DirType, FilePath)
|
||||
readDirEntPortable (DirStreamPortable (basedir, dirs)) = do
|
||||
(dt, fp) <- readDirEnt dirs
|
||||
case (dt, fp) of
|
||||
(DirType #{const DT_BLK}, _) -> pure (dt, fp)
|
||||
(DirType #{const DT_CHR}, _) -> pure (dt, fp)
|
||||
(DirType #{const DT_DIR}, _) -> pure (dt, fp)
|
||||
(DirType #{const DT_FIFO}, _) -> pure (dt, fp)
|
||||
(DirType #{const DT_LNK}, _) -> pure (dt, fp)
|
||||
(DirType #{const DT_REG}, _) -> pure (dt, fp)
|
||||
(DirType #{const DT_SOCK}, _) -> pure (dt, fp)
|
||||
(_, _)
|
||||
| fp /= "" -> do
|
||||
stat <- getSymbolicLinkStatus (basedir </> fp)
|
||||
pure $ (, fp) $ if | isBlockDevice stat -> DirType #{const DT_BLK}
|
||||
| isCharacterDevice stat -> DirType #{const DT_CHR}
|
||||
| isDirectory stat -> DirType #{const DT_DIR}
|
||||
| isNamedPipe stat -> DirType #{const DT_FIFO}
|
||||
| isSymbolicLink stat -> DirType #{const DT_LNK}
|
||||
| isRegularFile stat -> DirType #{const DT_REG}
|
||||
| isSocket stat -> DirType #{const DT_SOCK}
|
||||
| otherwise -> DirType #{const DT_UNKNOWN}
|
||||
| otherwise -> pure (dt, fp)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user