GHC 8.8.1 alphaをstackでダウンロードして手持ちのパッケージをビルドする

Posted by Yuji Yamamoto(@igrep) on May 2, 2019Tags: GHC, stack

先日、我らがHaskellのデファクトスタンダードなコンパイラー、GHCのバージョン8.8.1-alpha1がリリースされました
このリリースはまだアルファ版であることからわかるとおり、主にテスト目的で使用するためのものです。
なのでいち早く試してみて、GHCのデバッグに貢献してみましょう。

そこで今回は、最近Haskellを始めた方なら使っている方も多いであろう、stackを使ってこの新しいGHCをインストールし、あなたのライブラリー・アプリケーションでテストする方法を紹介いたします。

Link to
here
TL;DR cabal-installでやったほうがよさそう

いきなりやろうとすることを真っ向から否定するようで恐縮ですが…😅
実際に私が試しにビルドしてみた感じ、普通にcabal-installをこちらからインストールして、cabal new-build --with-ghc=ghc-8.8.0.20190424などと実行した方がいいんじゃないかという気がしました…。
cabal-installにはGHCをインストールする機能はないので、その場合はGHCは別途インストールすることになりますghcupが使える?)
@takenobu-hsさんが書いてくれた、こちらの記事を参考にどうぞ!

なお、stackでやると面倒な理由についての詳細はこれから述べる手順で適宜触れます…。

Link to
here
1. setup-infoを作る

まずはじめに、stackGHCをインストールする際に参照する、setup-infoというYAMLを作りましょう。
setup-infostack setupstack buildを実行したとき、GHCなどの必要なソフトウェアがインストールされていなかった際、自動でGHCをインストールするために必要な情報です。
GHCのバージョンや対象となるプラットフォームごとに、GHCのビルド済みtarballへのURLやそのチェックサムが書いてあります。
stackはここに書かれたURLにアクセスすることで、GHCをインストールしているんですね。

デフォルトでは、stackこちらのYAMLファイルをsetup-infoとして扱っています。
このYAMLにはStackageが参照している、安定版のGHCについては書いてあるものの、LTS HaskellにもStackage Nightlyにもまだ採用されていないGHCについては、書かれていません。
当然アルファ版であるGHC 8.8.1-alpha1が書かれることはないため、GHC 8.8.1-alpha1用のsetup-infoを作る必要があります。

それでは書いてみましょう… と、言いたいところですが、このsetup-info、実際のところ自分で直接書く必要はなく、YAMLファイルへのURLやパスを指定するだけでstackは参照しに行ってくれます!
と、言うわけで、こちらにGHC 8.8.1-alpha1向けのsetup-infoを作ってアップロードしておきました!
(申し訳なくもLinuxについてはどう書けばいいかわからず、macOSWindows 64bitのみ対応いたしました… あしからず。🙇)

ひとまずみなさんは、下記のいずれかの方法で指定するだけでこの手順はクリアできます。

  • stack.yamlに記載する:

    • ビルドしたいプロジェクトや、GHC 8.8を試す用のディレクトリーを作って、そこに👆の内容が書かれたstack.yamlを置きましょう。
      ちょっと試したいだけならそのディレクトリーでstack exec ghciなどと実行すればOKです!
  • stack setupコマンドのオプションとして渡す:

    stack setup 8.8.0.20190424 --setup-info-yaml https://gist.github.com/igrep/7298e1e2515059ae332feaf5501c41a4/raw/d69cc0b75d9be6735bdfcca6aa3eb6398d98983f/stack-setup-info.yaml
    • --setup-info-yamlオプションを指定した上で8.8.0.20190424という引数を与えるのがポイントです。
      GHCの開発版の慣習上、8.8.1-alpha1ではなく8.8.0.20190424となっている点に注意してください!

8.8.1-alpha1じゃなくて、自分でビルドしたGHCstackでインストールできるようにしたい!」というマニアなあなたは、今回私が作ったsetup-infoをどうぞ参考にしてください!🙇

Link to
here
2. (必要なら)allow-newerを有効にする

ここからは、何かしら依存するパッケージがあるライブラリー・アプリケーションをGHC 8.8.1-alpha1で試しにビルドしたいという方向けです。
GHC 8.8.1-alpha1をちょっと試したいだけという方はこれ以降を読む必要はありません。

まずは、ひとまず対象となるプロジェクトのstack.yaml

を追記しましょう。
これは、依存しているCabalパッケージのバージョンの、上限を取っ払うというものです。
依存パッケージのバージョンの上限は、パッケージの開発者が自身のパッケージを確実にビルドできるよう、「このパッケージはあのパッケージのバージョンN.M以下じゃないとビルドできないよ!」とCabalの依存関係リゾルバーに教えてあげるためのものです。
cabal-install(と、恐らくstackも必要に応じて)は、通常であればこの上限を見て、どのバージョンのパッケージをインストールするか決めます。
その上限により、残念ながら依存関係の解決に失敗することがあるのです。
そこでそうしたエラーを避けるためにもallow-newer: trueと設定して、上限を無視してみましょう。

というのも、このバージョンの上限はしばしば、予防のために実際より厳しめに設定されることがあるためです1
そりゃそうですよね。今作っているパッケージが依存しているAPIが、どのバージョンで使用できなくなるかなんて、大抵のパッケージではわかりませんし。
Haskellの世界にはPVPという、Semantic Versioningと似た思想のバージョン変更ポリシーがありまして、APIの互換性がなくなるような修正が含まれる場合、次のバージョンではA.B.CA.Bの箇所を変更することになっています。
これを信じて依存バージョンの上限(と下限)を設定してみても、実際にあなたが依存しているAPIが使用できなくなるとは限らないのです。

したがって、依存パッケージのバージョンの上限は、実際には無視してもよい場合がしばしばあります。
もちろん、自分で依存パッケージのバージョンを正しく書き換えて対応するというのもアリですし、将来的にはそうした方がより望ましいやり方です。
また、allow-newer: trueを設定することにより、GHC 8.8とは関係のない原因でビルドが失敗する可能性がある点にも注意してください。
とは言え、今回は手っ取り早くビルドしてみるために、敢えてallow-newer: trueを設定することと致しました。
「私はバージョンの上限を直してみたいんだー!」という方は、是非チャレンジしてみてください。

Link to
here
3. package-indicesを設定して、head.hackageを利用できるようにする

stack.yamlに書いておいた方が良い設定がもう一つあります。
それは、HEAD.hackageの設定です。

これからビルドするあなたのパッケージは、きっとたくさんのパッケージに依存していることでしょう。
残念ながら、その中にはGHC 8.8に対応できていないものも数多くあるでしょう😰。
特に今回はMonadFail Proposalによる、Monad型クラスの仕様変更を適切に周知できていなかったこともあり、まだ多くのパッケージが対応できていないようです。

しかし、まだ希望はあります。
あなたの依存パッケージに対する必要な修正は、すでにmasterブランチにマージされているかも知れませんし、すでに誰かがPull requestを送っているかも知れません。
さらにラッキーな場合、HEAD.hackageにパッチを当てたバージョンが上がっていることでしょう!

HEAD.hackageは、今回のようにGHCの開発版をいち早く試したい人が、新しいGHCに向けて修正を加えたパッケージを、いち早くアップロードするサイトです。
こちらのリポジトリーにパッチをアップロードすることで、cabal-installstackから、普通のhackageにあるパッケージとしてダウンロードできるようにしてくれます。

HEAD.hackagestackで利用するには、下記のように、package-indices:という設定を、stack.yamlに加えてください。
下記のように記載することで、stackは、HEAD.hackageにある修正済みのパッケージを優先して取得してくれるようになります2

これでGHC 8.8対応済みのパッケージを、簡単に取得できるようになります!

Link to
here
4. stack buildを実行しつつ、ひたすらextra-depsを追加・編集

ここまで設定できたら、いよいよstack buildしてみましょう3
とは言え、この状態ではほぼ間違いなく失敗が続くので、stack build --file-watchと、--file-watchオプションを付けて、stack.yamlを編集する度に再度ビルドが実行されるようにするのをおすすめします。

と、言うのも、恐らく次👇のようなエラーがたくさん出ると思われるからです。

...
In the dependencies for wss-client-0.2.1.1:
    http-client must match >=0.5.13, but the stack configuration has no specified version  (latest
                matching version is 0.6.4)
    http-client-tls needed, but the stack configuration has no specified version  (latest matching
                    version is 0.3.5.3)
    network-uri needed, but the stack configuration has no specified version  (latest matching
                version is 2.6.1.0)
    websockets must match >=0.12.0 && <0.13, but the stack configuration has no specified version
               (latest matching version is 0.12.5.3)
needed since wss-client is a build target.

Some different approaches to resolving this:

  * Consider trying 'stack solver', which uses the cabal-install solver to attempt to find some
    working build configuration. This can be convenient when dealing with many complicated
    constraint errors, but results may be unpredictable.

  * Recommended action: try adding the following to your extra-deps
    in C:\Users\igrep\Downloads\direct-hs\stack-ghc-8.8.yaml:

[email protected]:6a0baba19991e84ef939056e7b411ad3a1ea0fb5e1e8fce7ca50e96c84b206c8
[email protected]:d49e174ed0daecd059c52d13d4f4de87b5609c81212a22adbb92431f9cd58fff
...

このエラー、見かけたことがある人も多いでしょう。
そう、指定したresolverstackが使用するパッケージのバージョンの一覧。Stackageに登録されているlts-13.12などもその一つ)に、必要なバージョンのパッケージが登録されていない場合に起こるエラーです。
みなさんが普段利用するlts-13.12などのresolverでは、数多くのパッケージが登録されています最新版のLTS Haskell 13.192346件。Stackageをメンテしている皆さんのおかげですね)

一方、最初の手順で我々が指定したresolver、すなわちresolver: ghc-8.8は、GHC 8.8に添付されたパッケージbaseパッケージや、arrayパッケージなど)しか入っていない、実質空っぽなresolverなのです参考
そのため、あなたが必要なほとんどのパッケージはないため、stackはやむなく「extra-depsにこれらのパッケージを追加してね!」というエラーを出すことになります。
これではstackの良さを生かせません…。cabal-installcabal new-buildしていれば、cabal-installは黙って必要なパッケージのバージョンを決定し、あとはcabal new-freezeでもすれば、完全にビルドを再現可能な状態にしてくれます。
やっぱりstackはあくまでもStackageを活かすためのツールと捉えた方がいいのかも知れません😥。

extra-depsへのパッケージの追記を何度か繰り返すと、ようやくパッケージのビルドが始まります。
HEAD.hackageに収録されたパッケージを正しく取得できていれば、現在Hackageにアップロードされているバージョンではビルドできない依存パッケージも、無事ビルドできることでしょう。
依存するパッケージの数にもよりますが、やっぱり時間がかかるかと思います。待ちましょう☕️。

Link to
here
それでもうまくいかない場合: extra-depsを使い倒す

しかしやっぱり、必要な変更が施されたパッケージが、HEAD.hackageにもアップロードされていない場合はあります。
そうした場合、自分で修正してPull requestを送りつつ)パッチをHEAD.hackageのリポジトリーにアップロードすることもできますが、stack.yamlextra-depsを次のように使えば、もっと手っ取り早く修正したバージョンのビルドを試すことができます。

Link to
here
自分以外の人が対象のパッケージを修正した場合:

自分以外の人が対象のパッケージを修正したので、すでにどこかのリポジトリーにpush済みのコミットがある、という場合、下記👇のように書くと、Gitリポジトリーの特定のコミットを直接参照した状態で、依存関係に加えることができます。

Link to
here
自分で対象のパッケージを修正する、という場合:

そうでない場合、対象のパッケージのリポジトリーを一旦git submodule addして、自分のリポジトリーの一部に含めてしまいましょう。
その上で、extra-depsには下記のように書けば、stackはローカルのファイルシステムに置かれたディレクトリーも、直接依存するパッケージとして追加してくれます。

逐一別のディレクトリーにgit cloneしてgit commitしてgit pushして作られたコミットのSHAを参照して… なんてのを繰り返していたら、面倒だからです。

Link to
here
対象のパッケージがGitリポジトリーで管理されてない場合は?

臨機応変に対応しましょう…😰
ちなみに、extra-depsのドキュメントいわくstackMercurialもサポートしています。

Link to
here
番外編: Operation Vanguard

以上がstackを使ったGHC 8.8-alpha1のインストール方法や、それを利用したパッケージのビルド手順です。自分でGHCをビルドしたときなども参考にしてみてください。
これで終わり…!と、言いたいところですが、GHC 8.8に関連して、非常に意欲的なプロジェクト💪を紹介させてください。

それは、Operation Vanguardです。
@fumievalさんが始めた、「エコシステムの主要なパッケージの最新版を一挙にGHC 8.8に対応させる」プロジェクトです。
一旦submoduleとして対象のパッケージのリポジトリーをcloneする、という方法は、Operation Vanguardのリポジトリーを見ていて知りました💡。

すでに対応のほとんどが終了したとのことですが、GHC 8.8に対応していないパッケージは恐らくまだたくさんあります。
ゴールデンウィークももう半分が終わりましたが、時間をとってOperation Vanguardのようにチャレンジしてみるのはいかがでしょうか💪💪💪


  1. もっとも、私のようにものぐさな人間が作るパッケージには、そもそも上限も何も書いてないことが多いのですが…😰

  2. 本来であればHackage Securityの設定も必要なはずなんですが、なぜかうまくいかず…😱。こちらで紹介されたworkaroundにしたがって、関連する設定を除くことにしました…。

  3. stack solverコマンドを使えば、この節で紹介するエラーは簡単にクリアできそうだということを聞いて試したThanks, @mizunashi-manaさん!)のですが、手元のパッケージでは依存関係を解決できず、エラーになってしまいました…。