ストレスフリーにUbuntuでc++20のために最新のg++, Cmake, Boostを導入する

# Ubuntu 18.04にc++20のために最新のg++, Cmake, Boostを導入

まだ正式規格が出てないc++20だが，既に一部の機能はコンパイラに実装されている．
ここでは，正式規格が出てきても問題なくアップデートできる，ストレスフリーに環境を更新できるc++20開発環境の導入手順を示します．

## 目標
- すべてのソフトウェアは「継続的に更新」できること: 例`git repository`を用いて最新のものを追う
- いつでももとの環境に戻せる状態であること: 既存の環境を覆い隠すが，そのまま残す(/usr/localを用いる)
- g++を最新版に置き換えはしない: コンパイラは環境変数`CXX`,`CC`あるいは`cmake`実行時に渡してもらう
- CmakeLists.txtで`CXX_STANDARD 20`の指定で`c++20`を有効にする．
- CmakeLists.txtで単に`find_package(Boost)`と指定することで導入した(最新の)Boostを探し当てる．
- システムインストールなのでソースコードのディレクトリ位置には依存しない．

## gcc 最新版を入手
最新のgccは単一のppaで追いかけることができます。
ちなみに依存関係で自動的にc++20用標準ライブラリも手に入ります。

```bash
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt update
sudo apt install -y g++-9
```

ここでインストールするg++のバージョンはその時の最新にするのが良いです。g++-9の前提でこれから先書いていますが、適宜読み替えてください

### gcc 環境設定
環境変数CXXとCCを定義して、g++-9をデフォルトで使ってもらいやすくしておきます。

```bash
echo "export CXX='g++-9'" >> ~/.bashrc
echo "export CC='gcc-9'" >> ~/.bashrc
```

ここまでできたら一度端末をすべて閉じて開きなおし、CXXとCCを定義させます。

### gcc インストール確認方法
`g++-9 --version` で何か表示される。失敗しているとコマンドが見つからないと言われます。

## Cmake 最新版をインストール
CMakeのGit repositoryについて、githubはミラーで、`https://gitlab.kitware.com/cmake/cmake` が真の本体となっています
せっかくなのでオリジナルを利用しましょう

```bash
git clone https://gitlab.kitware.com/cmake/cmake.git
cd cmake
./bootstrap
make -j5
sudo make install
```

CXXとCCを判別するので、環境変数が適切にセットされていれば最新のコンパイラでコンパイルできます

### Cmake インストールチェック

```bash
cmake --version
```

上のコマンドが正しく動作することを確認してください。
CMakeのバージョンは適切か確認します。 `cmake version 3.16.20191223-g20e0d3c`のような形式をしているので、数日以内の更新のバージョンを使っていればOKです

## CMakeが対応しているBoostのインストール
### CMakeが対応しているバージョンを調べる
先程ダウンロードしたCMakeのFindBoostModuleのコードを調査します

```bash
# cd cmake
grep -n -A 2 _Boost_KNOWN_VERSIONS Modules/FindBoost.cmake
```

その結果、例えば以下のような結果が帰ってくる

```bash
> grep -n -A 2 _Boost_KNOWN_VERSIONS Modules/FindBoost.cmake
1431:  set(_Boost_KNOWN_VERSIONS ${Boost_ADDITIONAL_VERSIONS}
1432-    "1.72.0" "1.72" "1.71.0" "1.71" "1.70.0" "1.70" "1.69.0" "1.69"
1433-    "1.68.0" "1.68" "1.67.0" "1.67" "1.66.0" "1.66" "1.65.1" "1.65.0" "1.65"
--
1447:    foreach(version ${_Boost_KNOWN_VERSIONS})
1448-      if(NOT "${version}" VERSION_LESS "${Boost_FIND_VERSION}")
1449-        # This version is high enough.
--
1459:    set(_boost_TEST_VERSIONS "${_Boost_KNOWN_VERSIONS}")
1460-  endif()
1461-endif()
```

通常は一番先頭のバージョンが対応する最も高いバージョンとなっています。ここでは`1.72.0`となるため、以後の説明ではこのバージョンを用いますが、適宜読み替えてください。

### Boostのダウンロードとインストール
BoostはGit repositoryからコンパイルして用います
先に言っておくとダウンロードにもコンパイルにも相当時間がかかります

```bash
git clone https://github.com/boostorg/boost.git
```

他のソフトウェアと違ってBoostのバージョンは常に最新とは行きません、先程調査したCMake対応の最新バージョンに切り替えます

```bash
cd boost
git checkout boost-1.72.0 # Please replace with your latest version
git submodule update --init
./bootstrap.sh
./b2 toolset=gcc-9 --prefix=/usr/local -j5
sudo ./b2 install toolset=gcc-9 --prefix=/usr/local -j5
```

コンパイル時には`toolset=gcc-9`を指定して、最新のコンパイラで処理します
CXXやCCは適用されないので明示的に適用する必要があります
途中warningが大量に出ますが、std::auto_ptrやunused valueが騒いでいるだけです。Boostはあらゆるバージョン向けのコードが入っているので気にしないでください

j オプションの引数5は並列処理数です。CPUの論理コア数+1にして全く他の仕事をしなければ全力でコンパイルしてくれます。速度が段違いなので論理コア数をきちんと調べて指定すると良いです。

途中に入っている`git submodule`は必須です。もしアップデートするならgit submoduleを適切に使ってアップデートするか、boostのディレクトリを削除してこの手順をやり直してください。

## 動作確認
適当なディレクトリを作って、コンパイルを試します。
まずはディレクトリにCMakeLists.txtとmain.cppを作成します。

```
- CMakeLists.txt
- main.cpp
```

```cmake:CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(test)

find_package(Boost REQUIRED COMPONENTS system)
find_package(Threads REQUIRED)

include_directories(Boost::boost)

add_executable(main main.cpp)
target_link_libraries(main Boost::system Threads::Threads)

set(TARGETS main)

set_property(TARGET ${TARGETS} PROPERTY CXX_STANDARD 20)
set_property(TARGET ${TARGETS} PROPERTY CXX_STANDARD_REQUIRED ON)
set_property(TARGET ${TARGETS} PROPERTY CXX_EXTENSION OFF)
```

```cpp:main.cpp
#include <iostream>
#include <string_view>

#include <boost/asio.hpp>

int main(int argc, char** argv)
{
  namespace asio = boost::asio;
  using asio::ip::tcp;

  asio::io_service io_service;
  tcp::socket socket(io_service);

  boost::system::error_code error;
  socket.connect(tcp::endpoint(asio::ip::address::from_string("127.0.0.1"), 31400), error);

  if (error)
    std::cout << "connect failed : " << error.message() << std::endl;
  else
    std::cout << "connected" << std::endl;
}
```

ファイルを準備したらcmakeでコンパイルします。

```bash
cmake .
make
```

無事コンパイルできれば全てのインストールと設定は終了です。

`#include <string_view>`がエラーを吐いた場合はg++-9の設定がおかしいです。

Cmakeがboostライブラリを見つけたが、warnを吐いている場合はお使いのCMakeが最新のBoostに対応していません。Boostのバージョンを下げて再インストールすることで対応しましょう
また、使用したCMakeが最新のものでは無かった可能性もあります。`cmake --version`で確認してください

以上で全てのインストール作業は終わりです。

## Uninstall method
アンインストールするには、次のようにrmで削除してしまいます。

```bash
cd /usr/local
sudo rm -f bin/cmake bin/cpack bin/ctest
sudo rm -rf share/cmake-3.16 # fix with your version
sudo rm -f lib/libboost*
sudo rm -rf include/boost
sudo apt remove g++-9
```

また、 .bashrc に設定が2つあるので、テキストファイルで開いて最後の方を見て

```bash
export CXX='g++-9'
export CC='gcc-9'
```

この2行をまるまる削除すればアンインストール完了です
元の環境に戻っています

## Update method
### gcc
```bash
sudo apt update
sudo apt upgrade
```

あるいは新しいgccが出た場合は、インストールメソッドに従って最新版をインストールする

### CMake
```bash
cd cmake
git clean -fdx
git pull
./bootstrap --prefix=/usr/local
make -j5
sudo make install
```

### Boost
まずは更新したCMakeに対応するBoostのバージョンを調べ直す

```bash
# cd cmake
grep -n -A 2 _Boost_KNOWN_VERSIONS Modules/FindBoost.cmake
```

続いて、submoduleの扱いに十分に慎重になりながら作業をする

```bash
cd boost
git submodule deinit . # IMPORTANT this line at FIRST
git clean -fdx
git fetch # not pull. you need fetch
git checkout boost-1.72.0 # Please replace with your latest version
git submodule update --init # IMPORTANT --init option. must use.
./bootstrap.sh
./b2 toolset=gcc-9 --prefix=/usr/local -j5
sudo ./b2 install toolset=gcc-9 --prefix=/usr/local -j5
```

コツは、何かをする前に`git submodule deinit .`を実行することです。忘れて移動してしまうと面倒なことになります。その場合は(わかっているなら`.git`以外をすべて消して復元してもいいが)インストール手順をやり直すほうが早いと思います

# 最後に
この手順はc++20等関係なく、これから先gccかCMakeかBoostが滅びるまでそのまま適用できるはずなので、継続的に環境を更新していってくださいませ。