Clangを始めよう

組込み開発のスキル強化の目標をいくつか立てたので、しばらくC/C++を触ってみたい。

clangはコンパイラの一つである。LLVMのフロントエンドだ。C++の最新の言語仕様をいち早く実装していることでc++界隈では有名だ。C++erとしてこれは使ってみたいし、組込みにも使えたらうれしいと思っている。

C言語のコンパイラの定番といえば、WindowsならVisual Studio C++ Compiler、Linuxならgccであった。最近はgccに代わってclangが良く使われるようになってきている。しかし、IDEの完成度・デバッグの容易さといった総合的な開発環境はまだまだ前者の方が優れている気もする。

とはいえclangをローカル環境で使えたら勉強になるだろう。デバッガであるlldbだってコマンド操作を覚えればよい。IDEが欲しければ、Eclipseと連携させる方法がある。

ClangでコンパイルしたプログラムをWindows上で実行するにはWindows用のランタイムが必要だ。これはWindows SDKかに含まれているものを使うか、Windows向けGCCであるMinGWのランタイムを使うか、Cygwinレイヤを挟むかだ。

また、組込み用途の場合はクロスコンパイルが必要になる。すなわち、Windowsマシン上でLinux向け・ARM向けのバイナリを生成する環境を用意しないといけない。clang以前の問題だと感じるので、これはまたあとで対処したい。

LLVMをインストールした。1.1GBを消費する。

PS> clang -v
clang version 8.0.1 (tags/RELEASE_801/final)
Target: x86_64-pc-windows-msvc
Thread model: posix
InstalledDir: C:\Program Files\LLVM\bin

とりあえずコンパイルしてみる

適当なC++のコードをClangでコンパイルしてみた。

#include <iostream>
int main()
{
    std::cout << "Hello clang" << std::endl;
    return 0;
}
$ clang -o main.cpp main.exe
LINK : warning LNK4217: シンボル '__std_terminate' ('libvcruntime.lib(ehhelpers.obj)' で定義) が 'main-d95587.o' によって関数 '"int
`public: static unsigned __int64 __cdecl std::_Narrow_char_traits<char,int>::length(char const * const)'::`1'::dtor$2" (?dtor$2@?0??length@?$_Narrow_char_traits@DH@std@@SA_KQEBD@Z@4HA)' 内でインポー
トされています
LINK : warning LNK4217: シンボル '_CxxThrowException' ('libvcruntime.lib(throw.obj)' で定義) が 'main-d95587.o' によって関数 '"public: void __cdecl std::ios_base::clear(int,bool)" (?clear@ios_base@std@@QEAAXH_N@Z)' 内でインポートされています

このコマンドを使うとワーニングが消えた。

$ clang-cl -o main.cpp main.exe

あと次の省略形でもa.exeが生成した。どういうコマンドが発行されているのだろうか。(-vで確認可能)

$ clang++ main.cpp

Visual Studio Codeと連携

折角なので、VSCodeと連携させてみたい。

VSCode側でC/C++のプロジェクト設定ファイルを作成すればよいのだが、clangを使った場合のincludePathが不明で困ってしまった。"Clang for Windows"は内部でMSVCを利用する。

情報が多いので、MinGW用のclangを導入することにした。MSYS2をインストールし、MinGWを使う。MinGWをそのままインストールしてMinGW-getしてもよかったが、多分MSYS2(cygwinみたいな)で取得したパッケージを使ってもいいと思う。

$ pacman -S mingw-w64-x86_64-clang --disable-download-timeout

MSYSでインストールしたパッケージはWindows上で直接呼び出すことができる。

PS C:\msys64\mingw64\bin> .\clang.exe -v
clang version 8.0.0 (tags/RELEASE_800/final)
Target: x86_64-w64-windows-gnu
Thread model: posix
InstalledDir: C:\msys64\mingw64\bin

他にもこの辺のパッケージを入れるとよさげ。

  • mingw-w64-x86_64-make
  • mingw-w64-x86_64-gcc
  • mingw-w64-x86_64-gdb
  • mingw-w64-x86_64-clang
  • mingw-w64-x86_64-lldb

mingw-w64-x86_64-toolchainはada, object-c, fortranが入るので注意。

C:\msys64\mingw64\binにパスを通せば色々捗ると思うが、コマンドの衝突がちょっとだけ怖い。

インストールしたコンパイラへのパスをVSCodeから設定する。

VSCodeのc_cpp_properties.jsonを編集する。

{
  "configurations": [
      {
        "name": "Win32",
        "includePath": [
          "${workspaceFolder}/**"
        ],
        "defines": [
          "_DEBUG",
          "UNICODE",
          "_UNICODE"
        ],
        "windowsSdkVersion": "10.0.17763.0",
        "compilerPath": "\"C:/Program Files/LLVM/bin/clang.exe\"",
        "cStandard": "c11",
        "cppStandard": "c++17",
        "intelliSenseMode": "clang-x64"
      }
  ],
  "version": 4
}

変更後。

{
  "configurations": [
    {
      "name": "Win32",
      "includePath": [
        "${workspaceFolder}/**"
      ],
      "defines": [
        "_DEBUG",
        "UNICODE",
        "_UNICODE"
      ],
      "windowsSdkVersion": "10.0.17763.0",
      "compilerPath": "C:/msys64/mingw64/bin/clang.exe",
      "cStandard": "c11",
      "cppStandard": "c++17",
      "intelliSenseMode": "clang-x64"
    }
  ],
  "version": 4
}

compilerPathを変更しただけなのだが、Intellisenseが働くようになった。これはどういう理由なのかよくわかっていない。Program Files以下にインストールした場合はパスにスペースが含まれているから、という可能性もありえる。

遊んでいて気付いたのだが、x86_64-w64-windows-gnux86_64-w64-windows-msvcは結構違う。MSVC版はclangコマンドでcppファイルをビルドできる。Linuxだとclang++を使わなければエラーになる。

VSCodeからClangを使ってビルドする

ビルド方法を設定するには

  1. clangコマンドを使う(普通使わない)
  2. makeファイルを書く
  3. cmakeファイルを記述する(クロスプラットフォーム向け)

がよさそう。