みつきんのメモ

組み込みエンジニアです。Interface誌で「My オリジナルLinuxの作り方」連載中

meta-clangの使い方

はじめに

meta-clangの使い方を調べた。

meta-clangはbitbakeでのパッケージのビルドやSDKgccの代わりにclangを使えるようにするためのレイヤ。

meta-clangを使う

既存のビルド環境にmeta-clangを組み込むには下記のようにする。

$ git clone https://github.com/kraj/meta-clang -b dunfell

ビルド対象に組み込むにはbitbake-layersを実行する。

$ bitbake-layers add-layers ../layers/meta-clang

デフォルトのツールチェインを切り替える

bitbakeで使用するデフォルトのツールチェインをgccからclangに変更するにはlocal.confで下記を設定する。

TOOLCHAIN = "clang"

一部、gccじゃないとうまくビルドできないようなパッケージは、meta-clang/conf/nonclangable.confで次のように設定されている(抜粋)。

# __ai uint8x16_t vabdq_u8(uint8x16_t __p0, uint8x16_t __p1) {
TOOLCHAIN_pn-firefox = "gcc"
TOOLCHAIN_pn-gcc = "gcc"
TOOLCHAIN_pn-gcc-runtime = "gcc"
TOOLCHAIN_pn-gcc-sanitizers = "gcc"
TOOLCHAIN_pn-glibc = "gcc"
TOOLCHAIN_pn-glibc-initial = "gcc"
TOOLCHAIN_pn-glibc-locale = "gcc"
TOOLCHAIN_pn-glibc-mtrace = "gcc"
TOOLCHAIN_pn-glibc-scripts = "gcc"
TOOLCHAIN_pn-glibc-testsuite = "gcc"
TOOLCHAIN_pn-grub = "gcc"
TOOLCHAIN_pn-grub-efi = "gcc"

追加で設定したい場合はこの書式に則ってlocal.confに設定を追加する。

レシピ毎のツールチェインを切り替える

デフォルトのツールチェインは変更せずにレシピ毎に使用するツールチェインをclangに設定したい場合は、それぞれのレシピで下記の内容を設定する。

TOOLCHAIN = "clang"

SDKにclangを含める

SDKにclangを含めるにはlocal.confに下記を設定する。

CLANGSDK = "1"
# Adding clang in generated SDK toolchain

clang based cross compiler is not included into the generated SDK using `bitbake meta-toolchain` or
`bitbake -cpopulate_sdk <image>` if clang is expected to be part of SDK, add `CLANGSDK = "1"`
in `local.conf`

CLANGSDKに関するREADME.mdの説明は下記のようになっている。

# Removing clang from generated SDK toolchain

clang based cross compiler is automatically included into the generated SDK using `bitbake meta-toolchain` or
`bitbake -cpopulate_sdk <image>` in circumstanced where clang is not expected to be part of SDK, then reset `CLANGSDK`
variable in `local.conf`

```shell
CLANGSDK = ""
```

clangをSDKに含めないようにするにはCLANGSDK = ""として値をリセットするようにという説明しか無いため、 最初読んだ時今いち理解ができなかった。

これはもともとCLANGSDKのデフォルトが1になっていて、SDKに含まれることが前提だったためこのような書き方になっている。

デフォルトは826feacfeb64e2b4fe3ff50153a8ba4fa4bfff76のコミットで0に修正されている。

commit 826feacfeb64e2b4fe3ff50153a8ba4fa4bfff76
Author: Khem Raj <raj.khem@gmail.com>
Date:   Thu Apr 2 14:21:57 2020 -0700

    layer.conf: Mark CLANGSDK 0 by default
    
    This ensures that clangsdk does not inserts itself unless user asks for
    
    Fixes #234
    
    Signed-off-by: Khem Raj <raj.khem@gmail.com>

diff --git a/.drone.yml b/.drone.yml
index 548873f..7f14e38 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -40,6 +40,7 @@ steps:
       - echo IMAGE_CLASSES += \"testimage testsdk\" >> conf/local.conf
       - echo INHERIT += \"report-error rm_work blacklist\" >> conf/local.conf
       - echo TOOLCHAIN = \"clang\" >> conf/local.conf
+      - echo CLANGSDK = \"1\" >> conf/local.conf
       - echo IMAGE_INSTALL_append = \" clang \" >> conf/local.conf
       - echo XZ_DEFAULTS = \"--threads=8\" >> conf/local.conf
       - echo LICENSE_FLAGS_WHITELIST_append = \" commercial non-commercial\" >> conf/local.conf
diff --git a/conf/layer.conf b/conf/layer.conf
index f3e3fcc..792ba40 100644
--- a/conf/layer.conf
+++ b/conf/layer.conf
@@ -26,8 +26,8 @@ PREFERRED_PROVIDER_llvm-native = "clang-native"
 PREFERRED_PROVIDER_nativesdk-llvm = "nativesdk-clang"
 INHERIT += "clang"
 
-# include clang in SDK
-CLANGSDK ??= "1"
+# Do not include clang in SDK unless user wants to
+CLANGSDK ??= "0"
 
 LLVMVERSION = "10.0.0"

この時、ドキュメントは更新されなかったためこのような状態になっている。

masterを見ると修正されていたのでblameしてみると、下記のコミットで修正されたらしい。

commit 2730fe62d5d0af59d49bc2d8fe703753331aacf2
Author: Khem Raj <raj.khem@gmail.com>
Commit: Khem Raj <raj.khem@gmail.com>

    README: Update documentation
    
    Few knobs have changed and has not been yet reflected in README
    
    Signed-off-by: Khem Raj <raj.khem@gmail.com>

SDKにクロスコンパイラが含まれない場合

既にSDKをビルドしてしまった状態で、local.confにCLANGSDK = "1"を設定した場合、SDKにclangのクロスコンパイラが含まれない場合がある。

実際にSDKにclangのクロスコンパイラを入れる処理はpackagegroup-cross-canadianというレシピが行っているが、 CLANGSDKの値の変更はbitbake時に検出しないため、このパッケージをcleanする必要があるようだ。

確実にCLANGSDKの変更を反映するには下記のようにコマンドを実行する。

$ bitbake packagegroup-cross-canadian -c cleanall
$ bitbake core-image-base -c populate_sdk -f

SDKをインストールする際の注意点

build/tmp/deploy/sdk以下に生成されるインストーラではインストール先のディレクトリ変更することができるが、2020/12/13時点でWhen setting up sdk relocating error #119の問題が解決されてないため、デフォルトのままインストールするほうが無難。

インストールしたSDKのclangを使う

インストールしたSDKを使用するには最初に環境変数を設定する必要がある。

$ source /opt/poky/3.1.4/environment-setup-aarch64-poky-linux

Cソースのビルド

今回は適当にHello worldを用意する。下記の内容でhello.cを作成する。

#include <stdio.h>

int main(void)
{
    printf("hello world from C\n");
    return 0;
}

ターゲット向けにCのソースファイルをビルドするには下記のようにする。

$ ${CLANGCC} hello.c -ohello -O2

ターゲット用のビルドを行うためのコマンドラインCLANGCC環境変数に設定されている。

C++ソースのビルド

こちらもC++Hello world。下記の内容でhello.cppを作成する。

#include <iostream>

int main(void)
{
    std::cout << "hello world" << std::endl;
    return 0;
}

ターゲット向けにC++のソースファイルをビルドするには下記のようにする。

$ ${CLANGCXX} hello.cpp -ohello -O2

autotoolsベースのビルド

Makefile.amを以下の内容で作成する。

bin_PROGRAMS=hello
hello_SOURCES=hello.cpp

configure.acを以下の内容で作成する。

AC_INIT(hello.cpp)
AM_INIT_AUTOMAKE(hello,0.1)
AC_PROG_CC
AC_PROG_CXX
AC_PROG_INSTALL
AC_OUTPUT(Makefile)

次のような配置にする。

.
├── Makefile.am
├── configure.ac
└── hello.cpp

下記のように環境変数を設定する。

$ export CC=${CLANGCC}
$ export CXX=${CLANGCXX}
$ export CPP=${CLANGCPP}

次のコマンドを実行してビルドする。

$ touch NEWS README AUTHORS ChangeLog
$ autoreconf -i
$ ./configure --host=${CROSS_COMPILE}
$ make

Makefileを確認するとclangでビルドされていることが分かる。

   ...(snip)...
CC = aarch64-poky-linux-clang  -march=armv8-a+crc -fstack-protector-strong  -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security -mlittle-endian --sysroot=/opt/poky/3.1.4/sysroots/aarch64-poky-linux
CCDEPMODE = depmode=gcc3
CFLAGS =  -O2 -pipe -g -feliminate-unused-debug-types 
CPPFLAGS = 
CXX = aarch64-poky-linux-clang++  -march=armv8-a+crc -fstack-protector-strong  -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security -mlittle-endian --sysroot=/opt/poky/3.1.4/sysroots/aarch64-poky-linux
   ...(snip)...

CMakeベースのビルド

CMakeLists.txtを下記の内容で作成する。

cmake_minimum_required(VERSION 3.10)
project("hello")

add_executable(${PROJECT_NAME}
    ${PROJECT_SOURCE_DIR}/hello.cpp
)

次のような配置にする。

.
├── CMakeLists.txt
└── hello.cpp

下記のように環境変数を設定する。

$ export CC=${CLANGCC}
$ export CXX=${CLANGCXX}
$ export CPP=${CLANGCPP}

次のコマンドを実行してビルドする。

$ mkdir build && cd build
$ cmake ..
$ make

ccmakeなどで確認するとclangでビルドされていることが分かる。

CMAKE_CXX_COMPILER               /opt/poky/3.1.4/sysroots/x86_64-pokysdk-linux/usr/bin/aarch64-poky-linux/aarch64-poky-linux-clang++
CMAKE_CXX_COMPILER_AR            /opt/poky/3.1.4/sysroots/x86_64-pokysdk-linux/usr/bin/aarch64-poky-linux/aarch64-poky-linux-llvm-ar
CMAKE_CXX_COMPILER_ARG1            -march=armv8-a+crc -fstack-protector-strong  -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security -mlittle-endian --sysroot=/opt/poky/3.1.4/sysroots/aarch64-poky-linux

まとめ

meta-clangの使い方をまとめた。

もともと存在は知っていたが、実際に動作を確認したのは初めてだった。

Yoctoでのアプリケーション開発でclangが使用できると新し目のC++の機能を使ったりもできるので便利かもしれない。