Skip to content

Commit 13f3ba5

Browse files
authored
Merge pull request #160 from haskell-jp/stack-ghc-8.8
🆕 記事の追加: GHC 8.8.1 alphaをstackでダウンロードして手持ちのパッケージをビルドする
2 parents c745a08 + eee5961 commit 13f3ba5

File tree

1 file changed

+218
-0
lines changed

1 file changed

+218
-0
lines changed
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
---
2+
title: GHC 8.8.1 alphaをstackでダウンロードして手持ちのパッケージをビルドする
3+
headingBackgroundImage: ../../img/background.png
4+
headingDivClass: post-heading
5+
author: Yuji Yamamoto
6+
postedBy: <a href="http://the.igreque.info/">Yuji Yamamoto(@igrep)</a>
7+
date: May 2, 2019
8+
tags: GHC, stack
9+
...
10+
---
11+
12+
先日、我らがHaskellのデファクトスタンダードなコンパイラー、[GHCのバージョン8.8.1-alpha1がリリースされました](https://mail.haskell.org/pipermail/ghc-devs/2019-April/017550.html)
13+
このリリースはまだアルファ版であることからわかるとおり、主にテスト目的で使用するためのものです。
14+
なのでいち早く試してみて、GHCのデバッグに貢献してみましょう。
15+
16+
そこで今回は、最近Haskellを始めた方なら使っている方も多いであろう、stackを使ってこの新しいGHCをインストールし、あなたのライブラリー・アプリケーションでテストする方法を紹介いたします。
17+
18+
# TL;DR cabal-installでやったほうがよさそう
19+
20+
いきなりやろうとすることを真っ向から否定するようで恐縮ですが...😅
21+
実際に私が試しにビルドしてみた感じ、普通に[cabal-installをこちらから](https://www.haskell.org/cabal/download.html)インストールして、`cabal new-build --with-ghc=ghc-8.8.0.20190424`などと実行した方がいいんじゃないかという気がしました...。
22+
cabal-installにはGHCをインストールする機能はないので、その場合はGHCは別途インストールすることになります<small>([`ghcup`](https://github.com/haskell/ghcup)が使える?)</small>。
23+
[\@takenobu-hsさんが書いてくれた、こちらの記事](../2017/06-ghc-install.html)を参考にどうぞ!
24+
25+
なお、stackでやると面倒な理由についての詳細はこれから述べる手順で適宜触れます...。
26+
27+
# 1. `setup-info`を作る
28+
29+
まずはじめに、stackがGHCをインストールする際に参照する、[`setup-info`](https://docs.haskellstack.org/en/stable/yaml_configuration/#setup-info)というYAMLを作りましょう。
30+
`setup-info``stack setup``stack build`を実行したとき、GHCなどの必要なソフトウェアがインストールされていなかった際、自動でGHCをインストールするために必要な情報です。
31+
GHCのバージョンや対象となるプラットフォームごとに、GHCのビルド済みtarballへのURLやそのチェックサムが書いてあります。
32+
stackはここに書かれたURLにアクセスすることで、GHCをインストールしているんですね。
33+
34+
デフォルトでは、stackは[こちらのYAML](https://raw.githubusercontent.com/commercialhaskell/stackage-content/master/stack/stack-setup-2.yaml)ファイルを`setup-info`として扱っています。
35+
このYAMLには[Stackage](https://www.stackage.org/)が参照している、安定版のGHCについては書いてあるものの、LTS HaskellにもStackage Nightlyにもまだ採用されていないGHCについては、書かれていません。
36+
当然アルファ版であるGHC 8.8.1-alpha1が書かれることはないため、GHC 8.8.1-alpha1用の`setup-info`を作る必要があります。
37+
38+
それでは書いてみましょう... と、言いたいところですが、この`setup-info`、実際のところ自分で直接書く必要はなく、YAMLファイルへのURLやパスを指定するだけでstackは参照しに行ってくれます!
39+
と、言うわけで、[こちらにGHC 8.8.1-alpha1向けの`setup-info`](https://gist.github.com/igrep/7298e1e2515059ae332feaf5501c41a4)を作ってアップロードしておきました!
40+
<small>(申し訳なくもLinuxについてはどう書けばいいかわからず、macOSとWindows 64bitのみ対応いたしました... あしからず。🙇)</small>
41+
42+
ひとまずみなさんは、下記のいずれかの方法で指定するだけでこの手順はクリアできます。
43+
44+
- `stack.yaml`に記載する:
45+
```yaml
46+
resolver: ghc-8.8
47+
setup-info: "https://gist.githubusercontent.com/igrep/7298e1e2515059ae332feaf5501c41a4/raw/d69cc0b75d9be6735bdfcca6aa3eb6398d98983f/stack-setup-info.yaml"
48+
# ... 以下略 ...
49+
```
50+
51+
- ビルドしたいプロジェクトや、GHC 8.8を試す用のディレクトリーを作って、そこに👆の内容が書かれた`stack.yaml`を置きましょう。
52+
ちょっと試したいだけならそのディレクトリーで`stack exec ghci`などと実行すればOKです!
53+
- `stack setup`コマンドのオプションとして渡す:
54+
```
55+
stack setup 8.8.0.20190424 --setup-info-yaml https://gist.github.com/igrep/7298e1e2515059ae332feaf5501c41a4/raw/d69cc0b75d9be6735bdfcca6aa3eb6398d98983f/stack-setup-info.yaml
56+
```
57+
58+
- `--setup-info-yaml`オプションを指定した上で`8.8.0.20190424`という引数を与えるのがポイントです。
59+
GHCの開発版の慣習上、`8.8.1-alpha1`**ではなく**`8.8.0.20190424`となっている点に注意してください!
60+
61+
「8.8.1-alpha1じゃなくて、自分でビルドしたGHCを`stack`でインストールできるようにしたい!」というマニアなあなたは、[今回私が作った`setup-info`](https://gist.github.com/igrep/7298e1e2515059ae332feaf5501c41a4)をどうぞ参考にしてください!🙇
62+
63+
# 2. (必要なら)allow-newerを有効にする
64+
65+
ここからは、何かしら依存するパッケージがあるライブラリー・アプリケーションをGHC 8.8.1-alpha1で試しにビルドしたいという方向けです。
66+
GHC 8.8.1-alpha1をちょっと試したいだけという方はこれ以降を読む必要はありません。
67+
68+
まずは、ひとまず対象となるプロジェクトの`stack.yaml`
69+
70+
```yaml
71+
allow-newer: true
72+
```
73+
74+
を追記しましょう。
75+
これは、依存しているCabalパッケージのバージョンの、上限を取っ払うというものです。
76+
依存パッケージのバージョンの上限は、パッケージの開発者が自身のパッケージを確実にビルドできるよう、「このパッケージはあのパッケージのバージョンN.M**以下**じゃないとビルドできないよ!」とCabalの依存関係リゾルバーに教えてあげるためのものです。
77+
cabal-install<small>(と、恐らくstackも必要に応じて)</small>は、通常であればこの上限を見て、どのバージョンのパッケージをインストールするか決めます。
78+
その上限により、残念ながら依存関係の解決に失敗することがあるのです。
79+
そこでそうしたエラーを避けるためにも`allow-newer: true`と設定して、上限を無視してみましょう。
80+
81+
というのも、このバージョンの上限はしばしば、予防のために実際より厳しめに設定されることがあるためです[^upper]。
82+
そりゃそうですよね。今作っているパッケージが依存しているAPIが、どのバージョンで使用できなくなるかなんて、大抵のパッケージではわかりませんし。
83+
Haskellの世界には[PVP](https://pvp.haskell.org/)という、[Semantic Versioning](https://semver.org/lang/ja/)と似た思想のバージョン変更ポリシーがありまして、APIの互換性がなくなるような修正が含まれる場合、次のバージョンでは`A.B.C`の`A.B`の箇所を変更することになっています。
84+
これを信じて依存バージョンの上限(と下限)を設定してみても、実際にあなたが依存しているAPIが使用できなくなるとは限らないのです。
85+
86+
[^upper]: もっとも、私のようにものぐさな人間が作るパッケージには、そもそも上限も何も書いてないことが多いのですが...😰
87+
88+
したがって、依存パッケージのバージョンの上限は、実際には無視してもよい場合がしばしばあります。
89+
もちろん、自分で依存パッケージのバージョンを正しく書き換えて対応するというのもアリですし、将来的にはそうした方がより望ましいやり方です。
90+
また、`allow-newer: true`を設定することにより、**GHC 8.8とは関係のない原因でビルドが失敗**する可能性がある点にも注意してください。
91+
とは言え、今回は手っ取り早くビルドしてみるために、敢えて`allow-newer: true`を設定することと致しました。
92+
「私はバージョンの上限を直してみたいんだー!」という方は、是非チャレンジしてみてください。
93+
94+
# 3. package-indicesを設定して、head.hackageを利用できるようにする
95+
96+
`stack.yaml`に書いておいた方が良い設定がもう一つあります。
97+
それは、[HEAD.hackage](http://head.hackage.haskell.org/)の設定です。
98+
99+
これからビルドするあなたのパッケージは、きっとたくさんのパッケージに依存していることでしょう。
100+
残念ながら、その中にはGHC 8.8に対応できていないものも数多くあるでしょう😰。
101+
特に今回は[`MonadFail` Proposal](https://scrapbox.io/haskell-shoen/MonadFail)による、`Monad`型クラスの仕様変更を適切に周知できていなかったこともあり、まだ多くのパッケージが対応できていないようです。
102+
103+
しかし、まだ希望はあります。
104+
あなたの依存パッケージに対する必要な修正は、すでにmasterブランチにマージされているかも知れませんし、すでに誰かがPull requestを送っているかも知れません。
105+
さらにラッキーな場合、HEAD.hackageにパッチを当てたバージョンが上がっていることでしょう!
106+
107+
HEAD.hackageは、今回のようにGHCの開発版をいち早く試したい人が、新しいGHCに向けて修正を加えたパッケージを、いち早くアップロードするサイトです。
108+
[こちらのリポジトリー](https://github.com/hvr/head.hackage)にパッチをアップロードすることで、cabal-installやstackから、普通のhackageにあるパッケージとしてダウンロードできるようにしてくれます。
109+
110+
HEAD.hackageをstackで利用するには、下記のように、`package-indices:`という設定を、`stack.yaml`に加えてください。
111+
下記のように記載することで、stackは、HEAD.hackageにある修正済みのパッケージを優先して取得してくれるようになります[^security]。
112+
113+
[^security]: 本来であればHackage Securityの設定も必要なはずなんですが、なぜかうまくいかず...😱。[こちら](https://github.com/commercialhaskell/stack/issues/3844)で紹介されたworkaroundにしたがって、関連する設定を除くことにしました...。
114+
115+
```yaml
116+
package-indices:
117+
- name: head.hackage
118+
download-prefix: http://head.hackage.haskell.org/package/
119+
http: http://head.hackage.haskell.org/01-index.tar.gz
120+
- name: Hackage
121+
download-prefix: https://hackage.haskell.org/package/
122+
http: https://hackage.haskell.org/01-index.tar.gz
123+
```
124+
125+
これでGHC 8.8対応済みのパッケージを、簡単に取得できるようになります!
126+
127+
# 4. stack buildを実行しつつ、ひたすらextra-depsを追加・編集
128+
129+
ここまで設定できたら、いよいよ`stack build`してみましょう[^solver]!
130+
とは言え、この状態ではほぼ間違いなく失敗が続くので、`stack build --file-watch`と、**`--file-watch`オプションを付けて、`stack.yaml`を編集する度に再度**ビルドが実行されるようにするのをおすすめします。
131+
132+
[^solver]: `stack solver`コマンドを使えば、この節で紹介するエラーは簡単にクリアできそうだということを聞いて試した<small>(Thanks, [\@mizunashi-mana](https://github.com/mizunashi-mana)さん!)</small>のですが、手元のパッケージでは依存関係を解決できず、エラーになってしまいました...。
133+
134+
と、言うのも、恐らく次👇のようなエラーがたくさん出ると思われるからです。
135+
136+
```
137+
...
138+
In the dependencies for wss-client-0.2.1.1:
139+
http-client must match >=0.5.13, but the stack configuration has no specified version (latest
140+
matching version is 0.6.4)
141+
http-client-tls needed, but the stack configuration has no specified version (latest matching
142+
version is 0.3.5.3)
143+
network-uri needed, but the stack configuration has no specified version (latest matching
144+
version is 2.6.1.0)
145+
websockets must match >=0.12.0 && <0.13, but the stack configuration has no specified version
146+
(latest matching version is 0.12.5.3)
147+
needed since wss-client is a build target.
148+
149+
Some different approaches to resolving this:
150+
151+
* Consider trying 'stack solver', which uses the cabal-install solver to attempt to find some
152+
working build configuration. This can be convenient when dealing with many complicated
153+
constraint errors, but results may be unpredictable.
154+
155+
* Recommended action: try adding the following to your extra-deps
156+
in C:\Users\igrep\Downloads\direct-hs\stack-ghc-8.8.yaml:
157+
158+
attoparsec-0.13.2.2@sha256:6a0baba19991e84ef939056e7b411ad3a1ea0fb5e1e8fce7ca50e96c84b206c8
159+
base-compat-0.10.5@sha256:d49e174ed0daecd059c52d13d4f4de87b5609c81212a22adbb92431f9cd58fff
160+
...
161+
```
162+
163+
このエラー、見かけたことがある人も多いでしょう。
164+
そう、指定したresolver<small>(stackが使用するパッケージのバージョンの一覧。Stackageに登録されている`lts-13.12`などもその一つ)</small>に、必要なバージョンのパッケージが登録されていない場合に起こるエラーです。
165+
みなさんが普段利用する`lts-13.12`などのresolverでは、数多くのパッケージが登録されています<small>([最新版のLTS Haskell 13.19](https://www.stackage.org/lts-13.19)で2346件。Stackageをメンテしている皆さんのおかげですね)</small>。
166+
167+
一方、最初の手順で我々が指定したresolver、すなわち`resolver: ghc-8.8`は、GHC 8.8に添付されたパッケージ<small>(`base`パッケージや、`array`パッケージなど)</small>しか入っていない、実質空っぽなresolverなのです<small>([参考](https://docs.haskellstack.org/en/stable/yaml_configuration/#resolver))</small>。
168+
そのため、あなたが必要なほとんどのパッケージはないため、stackはやむなく「`extra-deps`にこれらのパッケージを追加してね!」というエラーを出すことになります。
169+
これではstackの良さを生かせません...。cabal-installで`cabal new-build`していれば、cabal-installは黙って必要なパッケージのバージョンを決定し、あとは`cabal new-freeze`でもすれば、完全にビルドを再現可能な状態にしてくれます。
170+
やっぱりstackはあくまでもStackageを活かすためのツールと捉えた方がいいのかも知れません😥。
171+
172+
`extra-deps`へのパッケージの追記を何度か繰り返すと、ようやくパッケージのビルドが始まります。
173+
HEAD.hackageに収録されたパッケージを正しく取得できていれば、現在Hackageにアップロードされているバージョンではビルドできない依存パッケージも、無事ビルドできることでしょう。
174+
依存するパッケージの数にもよりますが、やっぱり時間がかかるかと思います。待ちましょう☕️。
175+
176+
## それでもうまくいかない場合: `extra-deps`を使い倒す
177+
178+
しかしやっぱり、必要な変更が施されたパッケージが、HEAD.hackageにもアップロードされていない場合はあります。
179+
そうした場合、自分で修正して<small>(Pull requestを送りつつ)</small>パッチを[HEAD.hackageのリポジトリー](https://github.com/hvr/head.hackage)にアップロードすることもできますが、`stack.yaml`の`extra-deps`を次のように使えば、もっと手っ取り早く修正したバージョンのビルドを試すことができます。
180+
181+
### 自分以外の人が対象のパッケージを修正した場合:
182+
183+
自分以外の人が対象のパッケージを修正したので、すでにどこかのリポジトリーにpush済みのコミットがある、という場合、下記👇のように書くと、Gitリポジトリーの特定のコミットを直接参照した状態で、依存関係に加えることができます。
184+
185+
```yaml
186+
extra-deps:
187+
- git: https://github.com/github_user/repository_name.git
188+
commit: <修正したコミットのSHA>
189+
```
190+
191+
### 自分で対象のパッケージを修正する、という場合:
192+
193+
そうでない場合、対象のパッケージのリポジトリーを一旦`git submodule add`して、自分のリポジトリーの一部に含めてしまいましょう。
194+
その上で、`extra-deps`には下記のように書けば、stackはローカルのファイルシステムに置かれたディレクトリーも、直接依存するパッケージとして追加してくれます。
195+
196+
```yaml
197+
extra-deps:
198+
- ./path/to/package
199+
```
200+
201+
逐一別のディレクトリーに`git clone`して`git commit`して`git push`して作られたコミットのSHAを参照して... なんてのを繰り返していたら、面倒だからです。
202+
203+
### 対象のパッケージがGitリポジトリーで管理されてない場合は?
204+
205+
臨機応変に対応しましょう...😰
206+
ちなみに、[extra-depsのドキュメント](https://docs.haskellstack.org/en/stable/yaml_configuration/#git-and-mercurial-repos)いわくstackはMercurialもサポートしています。
207+
208+
# 番外編: Operation Vanguard
209+
210+
以上がstackを使ったGHC 8.8-alpha1のインストール方法や、それを利用したパッケージのビルド手順です。自分でGHCをビルドしたときなども参考にしてみてください。
211+
これで終わり...!と、言いたいところですが、GHC 8.8に関連して、非常に意欲的なプロジェクト💪を紹介させてください。
212+
213+
それは、[Operation Vanguard](https://github.com/haskell-vanguard/haskell-vanguard)です。
214+
[\@fumieval](https://github.com/fumieval/)さんが始めた、「エコシステムの主要なパッケージの最新版を一挙にGHC 8.8に対応させる」プロジェクトです。
215+
一旦submoduleとして対象のパッケージのリポジトリーをcloneする、という方法は、Operation Vanguardのリポジトリーを見ていて知りました💡。
216+
217+
すでに対応のほとんどが終了したとのことですが、GHC 8.8に対応していないパッケージは恐らくまだたくさんあります。
218+
ゴールデンウィークももう半分が終わりましたが、時間をとってOperation Vanguardのようにチャレンジしてみるのはいかがでしょうか💪💪💪

0 commit comments

Comments
 (0)