Windows でも pyenv + pipenv したい

# あらまし
どうも、B1 と B2 の学生向けに、とある講座を任された M1 です。
内容は Tensorflow **1.12** の使い方を教える、というもので、つまりは **環境地獄** と **互換性地獄** を見てこい、というものです。

とりあえず彼らが開発に着手できるような環境を整えるため、まずは **Windows ユーザ** が開発できるような状態を作るのが、この記事の目的になります。

結論から先に述べると、 [こんな感じの powershell スクリプト](https://gist.github.com/MokkeMeguru/f39e4e4f5cf1dd40b71f97c28b359214) を書きました。こいつを実行することで良い感じの環境が手に入ります。

# 前置き：無限大に広がる開発環境
開発環境というのは人それぞれで、私は Manjaro Linux x Docker x Emacs x LSP というなかなか剛毅な環境を使っているのですが、これは別に私だけが異常、というわけではありません。

大学の授業としては (~~Emacs 嫌いを増やすために~~) Emacs を教えることが多いですが、その甲斐あってか、vim や neovim、gvim、あるいは VSCode などエディタだけでもモリモリバラエティというのが現状問題として存在しています。

さらに、OSの選択肢として、キラキラと名高い macOS や、なんかデキる人に見える Ubuntu、そしてなんかよくわからないけどそこいらで売っている Windows 8 (/10) なんかが入ってくると、組み合わせ爆発がヤバイ。 

# 要件を整理する
まずスクリプトを作成するにあたり要件を整理します。

1. Windows 10 ユーザであること

    Mac や Linux は別にチュートリアルを作るとして、 Windows については 10 にアップデートして頂く形で進めます。幸いにも私の所属している大学側は Windows 10 を配布しているので、Windows 8 / 8.1 の方はそちらにアップデートしてもらう方針を取ります。

2. Powershell が使えること

    WSL や Bash on Ubuntu、Hyper-V は、パフォーマンスやソフトのかみ合わせで無効化する可能性が十分にあるので使えません。Powershell なら…きっと大丈夫と信じます。

3. なんらかの **まともな** エディタを使っていること

    最近では LSP (language-server-protocol) が人気の補完・チェックツールとして伸びてきています。例えば一般的なエディタである VSCode や Vim、Emacs なんかは LSP をサポートしているます。こちらを使えるようなエディタをきっと使っていると信じます。~~メモ帳とかはちょっとわからないっす~~

この要件で、スクリプトを書いていきます。
# 作ったスクリプトと使い方

```powershell:python_install_on_windows.ps1
# installation scoop
try {
 scoop --version
} finally {
  Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh')
  Set-ExecutionPolicy RemoteSigned -scope CurrentUser
}

# install some utility applications
scoop install python wget curl git
python --version
pip install pipenv

# install pyenv environment
if (!(get-command pyenv -errorAction SilentlyContinue)) {
  pip install pyenv-win --target $env:USERPROFILE\.pyenv
  [Environment]::SetEnvironmentVariable("PATH", "$env:Path;$env:USERPROFILE\.pyenv\pyenv-win\bin;$env:USERPROFILE\.pyenv\pyenv-win\shims", "User")
  $env:PATH = "$env:Path;$env:USERPROFILE\.pyenv\pyenv-win\bin;$env:USERPROFILE\.pyenv\pyenv-win\shims"
}

pyenv install 3.6.8-amd64

# create temporary file
if (!(Test-Path $env:USERPROFILE\.cache)) {
  New-Item $env:USERPROFILE\.cache -ItemType Directory
}
if (!(Test-Path $env:USERPROFILE\.cache\tmp)) {
  New-Item $env:USERPROFILE\.cache\tmp -ItemType Directory
}

# create project file
if (!(Test-Path $env:USERPROFILE\tensorflow_tutorial)) {
  New-Item $env:USERPROFILE\tensorflow_tutorial -ItemType Directory
}

# setup python environment
Set-Location $env:USERPROFILE\tensorflow_tutorial
pipenv install --python 3.6.8-amd64
pipenv install tensorflow==1.12.0 python-language-server[all] ipython
```

これを適当な場所に保存していただいて、ファイルプロパティを次のようにして、実行可能にします。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147520/65848e7f-2c17-1a08-5761-0cb12427eb15.png" width="30%">

次にこのファイルを Powershell 上で

```
./python_install_on_windows.ps1
```

と走らせれば勝手にインストールされて、サンプルプロジェクトとして、 `C:\Users\<USername>\tensorflow_tutorial` が作成されます。

# 何をしているのか
以降は何をしているのか、という説明になります。

このスクリプトが行っていることは、

- Windows のパッケージ管理システム Scoop のインストール
- Python のインストール
- Pyenv のインストール
- LSP のための一時フォルダの作成
- サンプルプロジェクトの作成

となっています。

## Scoop のインストール
[Scoop](https://scoop.sh/) は Unix ライクにパッケージをユーザ毎でインストールするためのツールです。このツールの利点は **パーミッションがいらない**、 **簡単にインストールができる**、**LinuxやmacOSと似たようなコマンド** といったところです。


```powershell
# installation scoop
try {
 scoop --version
} finally {
  Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh')
  Set-ExecutionPolicy RemoteSigned -scope CurrentUser
}
```

## Python のインストール
Scoop がインストールできたので Python のような **一般的な** ソフトウェア/アプリケーションはインストールできます。

git や curl 、wget なんかは B3、B4 あるいはそれ以降は知っていて欲しいコマンドですし、git は就活なんかでも聞かれることがあるので、ついでにこの機会に学んでもらいましょう。

```powershell
# install some utility applications
scoop install python wget curl git
python --version
```

## Pyenv のインストール
Pyenv は Python のバージョン管理ツールのために導入します。 Python は 3.5 とか 3.6 とか色々バージョンがあって、~~クソッタレな~~ Tensorflow なんかはこのバージョンがゴリゴリに制限されます。

全部個別にバイナリインストールして、パスを通す、という手段もありますが、今回はこれを使わずに Pyenv というツールを使って面倒な画面操作を減らします。

```powershell
# install pyenv environment
if (!(get-command pyenv -errorAction SilentlyContinue)) {
  pip install pyenv-win --target $env:USERPROFILE\.pyenv
  [Environment]::SetEnvironmentVariable("PATH", "$env:Path;$env:USERPROFILE\.pyenv\pyenv-win\bin;$env:USERPROFILE\.pyenv\pyenv-win\shims", "User")
  $env:PATH = "$env:Path;$env:USERPROFILE\.pyenv\pyenv-win\bin;$env:USERPROFILE\.pyenv\pyenv-win\shims"
}
```

## LSP のための一時フォルダの作成
Language Server Protocol は簡単に言うと、コード補完、エラーメッセージなんかを行うためのフォーマットの定義、稀にその実装をさすこともある、といったものです。
LSPではエディタとは別に、サーバと呼ばれるコード解析をするシステムが登場します。下は [VSCode のドキュメント](https://docs.microsoft.com/ja-jp/visualstudio/extensibility/language-server-protocol?view=vs-2019) より引用ですが、


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147520/704fbb99-a2c9-dab3-8d52-d1fccf84443a.png)


「言語にかかわらず、こんな感じでコードやエラーメッセージ、コードドキュメントを送受信しよう、そうすれば各エディタで補完なんかの実装がしやすいでしょ」というようなのがモチベーションの一つになっています。

これを導入すると、星の数ほどあるエディタについて、補完周りの設定をある程度共通化出来ます。

```powershell
# create temporary file
if (!(Test-Path $env:USERPROFILE\.cache)) {
  New-Item $env:USERPROFILE\.cache -ItemType Directory
}
if (!(Test-Path $env:USERPROFILE\.cache\tmp)) {
  New-Item $env:USERPROFILE\.cache\tmp -ItemType Directory
}
```
## サンプルプロジェクトの作成
サンプルプロジェクトとして、 **tensorflow 1.12** の環境を作ります。

tensorflow 1.12 は **<= python3.6** の環境しか対応していないので、 **3.6.8** をインストールします。

次にプロジェクトフォルダを作成して、それから pipenv という **パッケージ管理ツール** (バージョンじゃないです) を使って、 python 3.6.8 の環境下に tensorflow と python-language-sever、そして ipython をインストールします。


```powershell
pip install pipenv
pyenv install 3.6.8-amd64

# create project file
if (!(Test-Path $env:USERPROFILE\tensorflow_tutorial)) {
  New-Item $env:USERPROFILE\tensorflow_tutorial -ItemType Directory
}

# setup python environment
Set-Location $env:USERPROFILE\tensorflow_tutorial
pipenv install --python 3.6.8-amd64
pipenv install tensorflow==1.12.0 python-language-server[all] ipython
```

### 実行方法
サンプルプロジェクトを実行するには、例えば vim の場合、

```powershell
pipenv run vim
```

という形で起こします。VSCode の場合は、そのまま `tensorflow_tutorial` のフォルダを開けば OK です。
#### 追記：vim の設定ファイルの例
vim の設定ファイルの例は [ここ](https://gist.github.com/MokkeMeguru/992bfb9303c31789eaaa7ae6b451ffae) ですが、私は vim 歴が長くないので、多分 Twitter でハッシュタグつけて聞いたほうが良いです

この設定ファイルを有効にするには、次の手順を踏みます。

1. vundle のインストール

    ```powershell
    mkdir ~/.vim
    mkdir ~/.vim/bundle
    git clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vim
    ```

2. `:PluginInstall`
    vim 上で `:PlugInstall` します。