みつきんのメモ

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

DarlingをUbuntu 18.04で試す

はじめに

ちょっと前にツイッターLinux上で動くmacOSエミュレータDarlingというやつの情報が流れてきた。

wineのmacOS版みたいな感じのようだ。興味が湧いたので自分でも試してみる。

環境はUbuntu 18.04。

依存パッケージのインストール

sudo apt install cmake clang bison flex libfuse-dev libudev-dev pkg-config libc6-dev-i386 \
linux-headers-generic gcc-multilib libcairo2-dev libgl1-mesa-dev libglu1-mesa-dev libtiff5-dev \
libfreetype6-dev git libelf-dev libxml2-dev libegl1-mesa-dev libfontconfig1-dev libbsd-dev \
libxrandr-dev libxcursor-dev libgif-dev libavutil-dev libpulse-dev libavformat-dev libavcodec-dev \
libavresample-dev libdbus-1-dev libxkbfile-dev libssl-dev libfuse-dev libbsd-dev

/usr/include/asmのシンボリックリンク

これをしないとエラーになるため、リンクを作成する。

$ sudo ln -s /usr/include/asm-generic /usr/include/asm

環境の作成

ソースの取得

$ git clone --recursive https://github.com/darlinghq/darling.git

更新は下記

$ git pull
$ git submodule init
$ git submodule update

ビルドおよびインストール

# Move into the cloned sources
$ cd darling

# Make a build directory
$ mkdir build && cd build

# Configure the build
$ cmake ..

# Build and install Darling
$ make -j $(nproc)
$ sudo make install

カーネルモジュールのビルド及びインストール

Darling also requires a kernel module named darling-mach

とのことで、これもビルドする必要がある。

まず、依存するパッケージをインストールする。

$ sudo apt install linux-headers-`uname -r`

次にビルド及びインストール。

$ make lkm
$ sudo make lkm_install

lkmはlinux kernel moduleの略か。

実行

shell

shellを実行してみる。

$ darling shell

コンパイラの設定

Darling [~]$ xcode-select --install

hello world

プログラムの作成とビルド

Darling [~]$ cat > hello-world.c
#include <stdio.h>

int main() {
    puts("Hello World!");
}
Darling [~]$ clang hello-world.c -o hello-world

実行

Darling [~]$ ./hello-world
Hello world!

問題なく事項できる。

まとめ

環境の導入自体はそこまで難しくない。

macOS自体は詳しくないが、おそらくちゃんとmacOS向けのバイナリが動いているしコンパイラをいれてコンパイルもできた。

macOS向けのテスト環境がない場合、ちょっとしたことならこれで試せるのかも。

参考

CMakeLists 覚書 (2020年版)

はじめに

以前にもCMakeLists.txtの書き方をまとめたが内容が古くなったので改めて調べ直した。当時の理解が甘かったところやCMakeが3になってできることなども対応した。

Ubuntu 20.04では3.16に対応しているため、そのバージョンで使用できる機能を紹介する。

公式サイト

cmakeの公式サイトではバージョンごとに使用できる機能が検索できるようになっている。

使ってるバージョンのcmakeで特定の機能が使用可能かを確認したい場合はここで確認すると良い。

プロジェクト名の設定

projectでプロジェクト名を設定すると、プロジェクト名を${PROJECT_NAME}で参照できるようになる。

cmake_minimum_required(VERSION 3.16)
project(hello)

add_executable(${PROJECT_NAME} main.cpp)

C++バージョンの設定

CMAKE_CXX_STANDARDで使用するC++のバージョンを指定できる。cmake 3.16では、9811141720が指定できる。 Ubuntu 18.04で使用されているcmake 3.10では17まで指定できる。

CMAKE_CXX_STANDARD_REQUIREDONにすると対応しているC++バージョンが使用できない環境ではエラーにすることができる。 OFFの場合は古いバージョンにフォールバックされる。

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

パッケージの追加

外部のライブラリをリンクしたい場合はパッケージを追加する必要がある。

ここでいうパッケージはaptなどのパッケージシステムのことではなく古くはconfiugreスクリプトなどで参照される開発用パッケージのこと。 これらはcmakeでは*.cmakeによって定義される。

昔からあるpkg-configでは*.pcによって定義されている。

find_packageによるパッケージの追加

*.cmakeが提供されているライブラリはfind_packageでプロジェクトに追加することができる。

この例ではabseil-cppを追加している。

REQUIREDを指定すると見つからない場合はエラーになる。

パッケージが見つかったかどうかは<プロジェクト名>_FOUNDという変数で確認することができるが、REQUIREDをしている場合はパッケージが見つからなければcmakeが失敗するため、 このチェックを省くことができる。

find_package(absl REQUIRED)
# 見つからないとエラー

逆に見つからなくても処理を続行したい場合はREQUIRDをつけずに実行し、必要に応じて下記のように処理を分岐する。

find_package(absl)
if (absl_FOUND)
    message("abseil is found!")
else()
    message("abseil is not found!") # 見つからなくても処理は継続する
endif()

COMPONENTS設定

OpenCVやBoost、Qt5などの規模の大きいライブラリではfind_packageでCOMPONETNSによってコンポーネントを指定すると、そのパッケージの中の必要な機能だけをプロジェクトに追加することができる。

特にQt5では必ず1つ以上のコンポーネントを指定する必要がある。

find_package(OpenCV COMPONENTS core)
find_package(Boost 1.74 COMPONENTS date_time thread)
find_package(Qt5 COMPONENTS Quick REQUIRED)

target_link_libraries(${PROJECT_NAME}
    ${OpenCV_LIBRARIES} # OpenCV::coreは指定できない
    ${Boost_LIBRARIES}  # Boost::date_timeのように個別でもOK
    Qt5::Quick          # Qt5_LIBRARIESは空となっているため実質的に指定できない
)

インクルードとライブラリの設定

基本的にはインクルードに関しては自動的に設定されるため特にCMakeLists.txtで設定する必要はない。これについては自動生成されるflags.makeで確認することができる。

ライブラリに関してはtarget_link_libraries<プロジェクト名>_LIBRARIESを追加すれば良いケースがほとんどだが、 Qt5などはQt5::Quickなどコンポーネントを個別に追加する必要があるパッケージも存在する。

Qt5に関してはQt5内でのコンポーネント間の依存関係を自動的に解決してくれるようになっている。

モジュールモードとコンフィグモード

find_packageのモジュールモード、コンフィグモードの違いに関しては実践C++応用講座CMake編 第10回 find_packageの仕組みと使い方が詳しい。

簡単に言うと、find_moduleでパッケージの検索に必要な*.cmakeを提供しているのがCMake側なのがモジュールモードで、ライブラリ側なのがコンフィグモードということらしい。

pkg-configによるパッケージの追加

pkg-configコマンドによるパッケージの追加を有効化するにはfind_package(PkgConfig)をする必要がある。

パッケージを追加するにはpkg_check_modulesによってパッケージを指定する。こちらはfind_packageによるパッケージの追加と異なり、インクルードとライブラリの設定は手動で行う必要がある。 インクルードパスの設定は<pkg_check_modulesの第1引数>_INCLUDE_DIRSで参照することができる。

ライブラリの設定も同様に<pkg_check_modulesの第1引数>_LIBRARIESで参照できる。

find_package(PkgConfig REQUIRED)          # pkg-configを有効化
pkg_check_modules(STLINK REQUIRED stlink) # stlink.pcが提供されている

# "<pkg_check_modulesの第1引数>_INCLUDE_DIRS"でアクセスできる
include_directories(${STLINK_INCLUDE_DIRS})

... add_executable ...

# "<pkg_check_modulesの第1引数>_LIBRARIES"でアクセスできる
target_link_libraries(${PROJECT_NAME}
  ${STLINK_LIBRARIES}
)

インクルードディレクトリの設定

include_directoriesによって追加する。

# 2回目以降は追加となる
include_directories(${STLINK_INCLUDE_DIRS})        # pkg-configの変数
include_directories(${PROJECT_SOURCE_DIR}/include) # 自プロジェクトのincludeを追加

# 1回で複数追加することもできる
include_directories(
    ${STLINK_INCLUDE_DIRS}
    ${PROJECT_SOURCE_DIR}/include
    )

ライブラリの追加

target_link_librariesによって追加する。

# 2回目以降は追加となる
target_link_libraries(${PROJECT_NAME} ${STILNK_LIBRARIES}) # pkg-configの変数
target_link_libraries(${PROJECT_NAME} Qt5::Quick)          # COMPONENTSの指定
target_link_libraries(${PROJECT_NAME} -lrt)                # オプション直の指定も可能

# 1回で複数追加することもできる
target_link_libraries(${PROJECT_NAME}
    ${STILNK_LIBRARIES}
    Qt5::Quick
    -lrt)

ライブラリの追加(static)

こちらもtarget_link_librariesによって追加する。

ビルド済みのバイナリをリンクする場合は*.aまでのフルパスを指定する。

target_link_libraries(${PROJECT_NAME}
    ${PROJECT_SOURCE_DIR}/prebuilt_lib/libsdi.a
    ${PROJECT_SOURCE_DIR}/prebuilt_lib/libgdi.a
)

実行ファイルの生成

CMakeで実行形式を生成したい場合は、add_executable(<ターゲット名> <ソースファイル名>)で設定する。

ソースファイルは暗黙に${PROJECT_SOURCE_DIR}のものを参照する。

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

# ${PROJECT_SOURCE_DIR}を省略しても同じ
add_executable(${PROJECT_NAME}
    main.cpp
)

add_executableは1つのCMakeLists.txtで複数記述できるが、同じターゲット名は指定できない。

# これはできない
add_executable(${PROJECT_NAME} main.cpp)
add_executable(${PROJECT_NAME} print.cpp) 

異なるプロジェクト名であれば指定できる。

# これはOK
add_executable(${PROJECT_NAME} main.cpp)
add_executable(subtool print.cpp) 

アセンブラの有効化

add_executableにhello.Sなどアセンブラのソースファイルを登録したい場合は、予め有効化しておく必要がある。

# アセンブラを有効化
enable_language(ASM)

add_executable(${PROJ_NAME}
    ${PROJECT_SOURCE_DIR}/main.c
    ${PROJECT_SOURCE_DIR}/hello.S
)

サブディレクトリの登録

ライブラリなどのサブモジュールを一緒にビルドする場合、1つのCMakeLists.txtに全て定義することもできるが、 従来のMakefileのようにディレクトリごとにCMakeLists.txtを置くこともできる。

その場合はadd_subdirectory(ディレクトリ名)で登録する。

add_subdirectory(sayhello)
add_subdirectory(saybye)

サブディレクトリでライブラリをビルドする場合はtarget_link_librariesにサブディレクトリ名を指定する。

target_link_libraries(${PROJECT_NAME} sayhello)

これはサブディレクトリで生成するライブラリが動的でも静的でもおなじ。

ライブラリの生成

ライブラリをビルドする場合はadd_librarySHAREDを設定する。

project(sayhello)

add_library(${PROJECT_NAME} SHARED
    hello.cpp
)

もしくはBUILD_SHARED_LIBSONにすれば、add_libraryにSHAREDの指定を省略できる。

project(sayhello)

set(BUILD_SHARED_LIBS ON)

add_library(${PROJECT_NAME}
    hello.cpp
)

この例ではlibsayhello.soが生成される。

ライブラリの生成(スタティック)

ライブラリをビルドする場合はadd_librarySTATICを設定する。

project(sayhello)

add_library(${PROJECT_NAME} STATIC
    hello.cpp
)

もしくはBUILD_SHARED_LIBSONにしなければ、add_libraryにSTATICの指定を省略できる。

project(sayhello)

# set(BUILD_SHARED_LIBS ON) 省略時のadd_libraryのデフォルトはSTATIC
add_library(${PROJECT_NAME}
    hello.cpp
)

この例ではlibsayhello.aが生成される。

ビルドタイプの設定

デバッグ版やリリース版などで最適化オプションを切り替えるにはCMAKE_BUILD_TYPEを設定する。

ビルドタイプには下記の4種類が設定できる。

Build Type C++ optio variables 概要
Debug CMAKE_CXX_FLAGS_DEBUG デバッグ
Release CMAKE_CXX_FLAGS_RELEASE リリース
RelWithDebInfo CMAKE_CXX_FLAGS_RELWITHDEBINFO デバッグ情報つきリリース
MinSizeRel CMAKE_CXX_FLAGS_MINSIZEREL コードサイズ最適化

CMAKE_CXX_FLAGS_xxxの他にも下記のようなものが定義されている。

  • CMAKE_C_FLAGS_xxx
  • CMAKE_EXE_LINKER_FLAGS_xxx
  • CMAKE_MODULE_LINKER_FLAGS_xxx
  • CMAKE_SHARED_LINKER_FLAGS_xxx
  • CMAKE_STATIC_LINKER_FLAGS_xxx

ビルドタイプをDebugに設定する場合は下記のようにする。

set(CMAKE_BUILD_TYPE Debug)

Debugモードの時のC++のビルドオプションを変更したい場合は下記のようにする。

set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g")

CXXFLAGSなどの設定

CXXFLAGSなどの一般的なに変数に関してはCMAKE_CXX_FLAGSのように予め定義してある。

下記のようにsetで設定できる。

set(CMAKE_CXX_FLAGS "-D__ARMV8")

既存の変数に追加したい場合はいくつか書き方があるが下記のようにすると良さそう。

# 自分を展開し再代入する
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__ARMV8")

# 文字列として追加する(先頭の空白は必須)
string(APPEND CMAKE_CXX_FLAGS " -D__ARMV8")

list(APPEND ...):区切りとなるためこのケースでは使用できない。

定義済みの変数は下記の通り。

  • CMAKE_C_FLAGS(CFLAGS)
  • CMAKE_CXX_FLAGS(CXXFLAGS)
  • CMAKE_CUDA_FLAGS(CUDAFLAGS)
  • CMAKE_Fortran_FLAGS(FFLAGS)

インストール

make installなど、インストールのルールを追加するにはinstallで設定する。

いろいろ細かく設定できるが生成された実行ファイルをインストールするだけなら下記のような感じでよい。

install(TARGETS ${PROJECT_NAME}
        DESTINATION bin)

インストール先はCMAKE_INSTALL_PREFIXと結合される。デフォルトでは/usr/localに設定されているため、この場合は/usr/local/binにインストールされることになる。

compile_commands.jsonを生成

compile_commands.jsonはclangdにソースコードコンパイルをする際のコンパイルオプションを知らせるためのもので、 これがないと、外部ライブラリを使用する場合などに補完が残念になる。

compile_commands.jsonについてはClangdでC++ソースコードを補完する際にコンパイルオプションを指定する方法が詳しい。

cmakeでcompile_commands.jsonを生成するにはCMAKE_EXPORT_COMPILE_COMMANDSをONに設定する。

set (CMAKE_EXPORT_COMPILE_COMMANDS ON)

このようにCMakeLists.txtで設定してもよいが、毎回生成する必要は無いので下記のようにコマンドラインでしていするのでも良い。

$ cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

cmakeを実行したディレクトリに生成されるので、必要に応じてファイルを移動するなりシンボリックリンクを貼るなりする。

まとめ

CMakeが3になってしばらく経ったが、2の時代からいろいろと進化していた。

Kasのコンフィグファイルを提供してみる

はじめに

なにか適当なレイヤをでっち上げて、kasのコンフィグファイルを作ってみる。

サンプルのレイヤとしてラズパイ向けにハローワールドプログラムを追加したレイヤを作成する。

環境の作成

ソース取得

ラズパイ環境のビルドのためのレイヤをダウンロードする。

$ mkdir -p rpi-dunfell/layers
$ cd rpi-dunfell/layers
$ git clone git://git.yoctoproject.org/poky.git -b dunfell
$ git clone git://git.yoctoproject.org/meta-raspberrypi -b dunfell
$ cd ../

環境変数の設定

bitbakeを実行できるようにするため環境変数を設定する。

$ source layers/poky/oe-init-build-env build

レイヤの追加

ビルド対象にレイヤを追加する。

$ bitbake-layers add-layer ../layers/meta-raspberrypi

local.confの修正

MACHINEをラズパイ4にする。

INITはsystemdで。あとは細かい設定。 弊ブログでは定番の設定だが、今回はこの部分もkasのコンフィグに組み込む。

MACHINE = "raspberrypi4"
DL_DIR ?= "${TOPDIR}/../downloads"

# enable uart
ENABLE_UART = "1"

# systemd
DISTRO_FEATURES_append = " systemd pam"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""

イメージの作成

コンソール向けイメージをビルドする。

$ bitbake core-image-base

これで最初の環境は完成。

レイヤの作成

ハローワールドを組み込むためのレイヤを作成する。

$ bitbake-layers create-layer meta-hello
$ mv meta-hello ../layers
$ bitbake-layers add-layer ../layers/meta-hello

レシピの作成

ハローワールドのレシピは自作しても良いが、今回は本筋からそれてしまうため、pokyのドキュメントに含まれているhello.bbを拝借することにする。

$ cp -r ../layers/poky/documentation/ref-manual/examples/hello-single ../layers/meta-hello/recipes-example/

ただしこのままではbitbakeに失敗する。

ERROR: hello-1.0-r0 do_package_qa: QA Issue: File /usr/bin/helloworld in package hello doesn't have GNU_HASH (didn't pass LDFLAGS?) [ldflags]
ERROR: hello-1.0-r0 do_package_qa: QA run found fatal errors. Please consider fixing them.
ERROR: Logfile of failure stored in: /home/mickey/work/yocto/kas-dunfell/build/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/hello/1.0-r0/temp/log.do_package_qa.26500
ERROR: Task (/home/mickey/work/yocto/kas-dunfell/layers/meta-hello/recipes-example/hello-single/hello.bb:do_package_qa) failed with exit code '1'
NOTE: Tasks Summary: Attempted 550 tasks of which 0 didn't need to be rerun and 1 failed.

ので、下記のように修正する。

--- hello.bb~    2020-11-17 13:19:16.719291894 +0900
+++ hello.bb  2020-11-17 13:47:15.962567623 +0900
@@ -8,7 +8,7 @@
 S = "${WORKDIR}"
 
 do_compile() {
-  ${CC} helloworld.c -o helloworld
+   ${CC} ${CFLAGS} ${LDFLAGS} helloworld.c -o helloworld
 }
 
 do_install() {

local.confの修正

local.confにハローワールドを追加する。

# hello world
IMAGE_INSTALL_append = " hello"

kasコンフィグについて

kasコンフィグはYAML形式で記述されたテキストファイルとなっている。 YAMLなので階層構造をスペースのインデントで表現する。

kasコンフィグの詳細についてはWelcome to the kas documentation, a setup tool for bitbake based projects を参照してほしい。

トップレベルの階層

最低限記述しなければならないトップレベルの階層としては下記のものがある。

アイテム 概要 備考
header コンフィグのヘッダ情報
machine ターゲットマシン raspberrypi4など
distro ディストリビューション pokyなど
target ビルドターゲット core-image-baseなど
repos リポジトリ情報
bblayers_config_header bblayers.confの雛形
local_conf_header local.confの雛形

header

コンフィグ自体のヘッダ情報、具体的にはコンフィグ形式のバージョンを指定する。

下記のように記載する。

header:
  version: 9

2020年11月時点でバージョンは1〜9まで指定できる。

machine

ターゲットマシン(ボード)を指定する。local.confのMACHINEに設定される。

machine: raspberrypi4

distro

bitbakeで生成されるディストリビューションを指定する。local.confのDISTROに設定される。

distro: poky

target

ビルドターゲットを指定する。bitbakeコマンドのターゲットに使用される。

target:
 - core-image-base

repos

使用するメタデータ(レイヤ)のリポジトリ情報を指定する。リポジトリのURLやrefspecを指定する。 リポジトリが複数のレイヤを含んでいる場合はlayersで指定する。 meta-raspberrypiのようにリポジトリが1つのレイヤのみを含んでいる場合はlayersの記述は必要ない。

最初は自分自身の情報を記載する。

repos:
  meta-hello: # 自分自身

  poky: # pokyのリポジトリ情報
    url: https://git.yoctoproject.org/git/poky 
    refspec: dunfell
    layers:
      meta:           # 
      meta-poky:      # pokyからはこの3つのレイヤがしようされる
      meta-yocto-bsp: #

  meta-raspberrypi:
    url: git://git.yoctoproject.org/meta-raspberrypi
    refspec: dunfell

上記はブランチ名を指定しているが下記のようにコミットハッシュを指定することもできる。

repos:
  meta-hello:

  poky:
    url: https://git.yoctoproject.org/git/poky
    refspec: d3ad6a22dbb8b9a21d7fe11bf437a20dea95ec81
    layers:
      meta:
      meta-poky:
      meta-yocto-bsp:

  meta-raspberrypi:
    url: git://git.yoctoproject.org/meta-raspberrypi
    refspec: 987993209716302eb8f314f69a2a3340555f94d8

ちなみにbitbakeを実行した時点で使用されたレイヤのコミットハッシュはtmp/log/cooker/raspberrypi4/console-latest.logで確認できる。

$ cat ./tmp/log/cooker/raspberrypi4/console-latest.log | head -n 20

bblayers_conf_header

bblayers.confの雛形を記述する。

bblayers_conf_header:
  standard: |
    POKY_BBLAYERS_CONF_VERSION ?= "2"
    BBPATH ?= "${TOPDIR}"
    BBFILES ?= ""

BBLAYERSに設定されるビルド対象となるレイヤ情報はreposから自動的に構成されるため、改めて記述する必要はない。

local_conf_header

local.confの雛形を記述する。

local_conf_header:
  standard: |
    CONF_VERSION ?= "1"
    PACKAGE_CLASSES ?= "package_rpm"
    USER_CLASSES ?= "buildstats image-mklibs image-prelink"
    PATCHRESOLVE ?= "noop"
  debug-tweaks: |
    EXTRA_IMAGE_FEATURES ?= "debug-tweaks"

階層構造となっているが、local.confへは下記のようにコメントと1つのかたまりとして展開される。

# standard
CONF_VERSION ?= "1"
PACKAGE_CLASSES ?= "package_rpm"
USER_CLASSES ?= "buildstats image-mklibs image-prelink"
PATCHRESOLVE ?= "noop"

# debug-tweaks
EXTRA_IMAGE_FEATURES ?= "debug-tweaks"

ちょっと調べた感じだと、kasコマンド実行時に特定の階層を削除する(たとえばdebug-tweaksは無効とか)ようなことはできなさそう*1なので、 おそらく見た目以外の意味はなさそう。

kasコンフィグの作成

ここまでの内容を踏まえてmeta-hello向けのkasコンフィグを作成する。

meta-helloの直下にkas-poky-hello.ymlとしてコンフィグファイルを作成する。

$ vi ../layers/meta-hello/kas-poky-hello.yml

kas-poky-hello.ymlを下記の内容にする。

header:
  version: 9

machine: raspberrypi4
distro: poky
target:
 - core-image-base

repos:
  meta-hello:

  poky:
    url: https://git.yoctoproject.org/git/poky 
    refspec: dunfell
    layers:
      meta:
      meta-poky:
      meta-yocto-bsp:

  meta-raspberrypi:
    url: git://git.yoctoproject.org/meta-raspberrypi
    refspec: dunfell

bblayers_conf_header:
  standard: |
    POKY_BBLAYERS_CONF_VERSION ?= "2"
    BBPATH ?= "${TOPDIR}"
    BBFILES ?= ""

local_conf_header:
  standard: |
    PACKAGE_CLASSES ?= "package_rpm"
    USER_CLASSES ?= "buildstats image-mklibs image-prelink"
    PATCHRESOLVE = "noop"
    CONF_VERSION = "1"
    PACKAGECONFIG_append_pn-qemu-system-native = " sdl"

  image features: |
    EXTRA_IMAGE_FEATURES ?= "debug-tweaks"

  diskmon: |
    BB_DISKMON_DIRS ??= "\
      STOPTASKS,${TMPDIR},1G,100K \
      STOPTASKS,${DL_DIR},1G,100K \
      STOPTASKS,${SSTATE_DIR},1G,100K \
      STOPTASKS,/tmp,100M,100K \
      ABORT,${TMPDIR},100M,1K \
      ABORT,${DL_DIR},100M,1K \
      ABORT,${SSTATE_DIR},100M,1K \
      ABORT,/tmp,10M,1K"

  change download directory: |
    DL_DIR ?= "${TOPDIR}/../downloads"

  enable uart: |
    ENABLE_UART = "1"

  systemd: |
    DISTRO_FEATURES_append = " systemd pam"
    VIRTUAL-RUNTIME_init_manager = "systemd"
    DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
    VIRTUAL-RUNTIME_initscripts = ""

  hello world: |
    IMAGE_INSTALL_append = " hello"

kasによるビルド

新しいシェルを開き下記のコマンドを実行する。

$ kas build meta-hello/kas-poky-hello.yml

まとめ

kasコンフィグはyaml形式のテキストであるため、比較的簡単に書ける。 複数のgitリポジトリを管理するためにrepoツールなどもあるが、 bitbakeに特化した作業をするのであればkasは便利かもしれない。

ただ、kasはあくまでも完成したレイヤを利用する人が使うには便利だが、 レイヤを開発する段階ではbitbakeを直接使用したほうが便利だと思う。

*1:できたらやり方教えてください

Yocto meta-raspberrypi環境の作成にkasを使ってみる

はじめに

meta-raspberrypiのQuick Startを眺めていると、Quick Start with kasという項目ががあった。

kasというコマンドに見覚えがなかったので調べてみると、下記のようにあった。

This tool provides an easy mechanism to setup bitbake based projects.

bitbakeをラップして、操作を簡略化するためのツールのようだ。

試しにmeta-raspberrypiをkasで作成してみる。

kasのインストール

pipコマンドでインストールする。

$ pip3 install --user kas

meta-raspberrypiの取得

kasはプロジェクト設定が記述してあるymlにある依存関係を解決する機能がある。 この機能を確かめるために、pokyなど他のメタデータはあえてダウンロードしない。

$ mkdir kas_meta-raspberrypi
$ cd kas_meta-raspberrypi
$ git clone git://git.yoctoproject.org/meta-raspberrypi -b dunfell

kasによるビルド

下記のコマンドでビルドする。

$ kas build meta-raspberrypi/kas-poky-rpi.yml

動作ログ

meta-raspberrypiはdunfellブランチだが、依存関係があるメタデータkas-poky-rpi.ymlの内容に従う。

kasの実行時にブランチを上書きするようなオプションも見当たらないので、使用するブランチを変更したければkas-poky-rpi.ymlを編集する必要がありそう。

Build Configuration:
BB_VERSION           = "1.47.0"
BUILD_SYS            = "x86_64-linux"
NATIVELSBSTRING      = "ubuntu-18.04"
TARGET_SYS           = "arm-poky-linux-gnueabi"
MACHINE              = "raspberrypi4"
DISTRO               = "poky"
DISTRO_VERSION       = "3.1+snapshot-20200902"
TUNE_FEATURES        = "arm vfp cortexa7 neon vfpv4 thumb callconvention-hard"
TARGET_FPU           = "hard"
meta-networking      
meta-oe              
meta-perl            
meta-python          = "master:6a2b4a8ca4fb407cc93b7eb865cb5f4e324585be"
                     = "master:5144a99a696a8a72c0765ea0e8be8ceafaa78f85"
                     = "dunfell:93305f7c3a9c338a3d2daef61514f61b4256f979"
meta                 
meta-poky            
meta-yocto-bsp       = "master:104ff3c834d9ea60de4600fca374c0bdc9ca843d"

自動生成のファイル

local.conf

kasによって生成されたlocal.confを示す。

# debug-tweaks
EXTRA_IMAGE_FEATURES = "debug-tweaks"

# diskmon
BB_DISKMON_DIRS = "\
    STOPTASKS,${TMPDIR},1G,100K \
    STOPTASKS,${DL_DIR},1G,100K \
    STOPTASKS,${SSTATE_DIR},1G,100K \
    STOPTASKS,/tmp,100M,100K \
    ABORT,${TMPDIR},100M,1K \
    ABORT,${DL_DIR},100M,1K \
    ABORT,${SSTATE_DIR},100M,1K \
    ABORT,/tmp,10M,1K"

# reduce_diskspace
INHERIT += "rm_work_and_downloads"

# standard
CONF_VERSION = "1"
PACKAGE_CLASSES = "package_rpm"
SDKMACHINE = "x86_64"
USER_CLASSES = "buildstats image-mklibs image-prelink"
PATCHRESOLVE = "noop"

MACHINE ??= "raspberrypi4"
DISTRO ??= "poky"
BBMULTICONFIG ?= ""

INHERIT += "rm_work_and_downloads"が設定されている。ディスク容量を空けるために中間ファイルを削除している。 これは、メタデータを修正する必要がないケースでは有効だが、パッチの作成やデバッグに必要なファイルが削除されてしまう。

bblayers.conf

こちらは特筆することは無い。bitbake-layersでいちいち追加しなくて良いのはラク

# standard
POKY_BBLAYERS_CONF_VERSION = "2"
BBPATH = "${TOPDIR}"
BBFILES ?= ""

BBLAYERS ?= " \
    /home/mickey/work/yocto/kas_meta-raspberrypi/meta-openembedded/meta-networking \
    /home/mickey/work/yocto/kas_meta-raspberrypi/meta-openembedded/meta-oe \
    /home/mickey/work/yocto/kas_meta-raspberrypi/meta-openembedded/meta-perl \
    /home/mickey/work/yocto/kas_meta-raspberrypi/meta-openembedded/meta-python \
    /home/mickey/work/yocto/kas_meta-raspberrypi/meta-qt5/ \
    /home/mickey/work/yocto/kas_meta-raspberrypi/meta-raspberrypi/ \
    /home/mickey/work/yocto/kas_meta-raspberrypi/poky/meta \
    /home/mickey/work/yocto/kas_meta-raspberrypi/poky/meta-poky \
    /home/mickey/work/yocto/kas_meta-raspberrypi/poky/meta-yocto-bsp"

コンパイルエラー

通常のbitbakeとほぼ同じ。特に解析に役立つ付加情報があるわけではなさそう。

| ../libnl-3.5.0/lib/route/cls/ematch_syntax.y: warning: fix-its can be applied.  Rerun with option '--update'. [-Wother]
| WARNING: /home/mickey/work/yocto/kas_meta-raspberrypi/build/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/libnl/1_3.5.0-r0/temp/run.do_compile.9628:191 exit 1 from 'exit 1'
| WARNING: Backtrace (BB generated script):
|   #1: bbfatal_log, /home/mickey/work/yocto/kas_meta-raspberrypi/build/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/libnl/1_3.5.0-r0/temp/run.do_compile.9628, line 191
|   #2: die, /home/mickey/work/yocto/kas_meta-raspberrypi/build/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/libnl/1_3.5.0-r0/temp/run.do_compile.9628, line 166
|   #3: oe_runmake, /home/mickey/work/yocto/kas_meta-raspberrypi/build/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/libnl/1_3.5.0-r0/temp/run.do_compile.9628, line 161
|   #4: autotools_do_compile, /home/mickey/work/yocto/kas_meta-raspberrypi/build/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/libnl/1_3.5.0-r0/temp/run.do_compile.9628, line 156
|   #5: do_compile, /home/mickey/work/yocto/kas_meta-raspberrypi/build/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/libnl/1_3.5.0-r0/temp/run.do_compile.9628, line 151
|   #6: main, /home/mickey/work/yocto/kas_meta-raspberrypi/build/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/libnl/1_3.5.0-r0/temp/run.do_compile.9628, line 195
| 
| Backtrace (metadata-relative locations):
|   #1: bbfatal_log, /home/mickey/work/yocto/kas_meta-raspberrypi/poky/meta/classes/logging.bbclass, line 72
|   #2: die, /home/mickey/work/yocto/kas_meta-raspberrypi/poky/meta/classes/base.bbclass, line 56
|   #3: oe_runmake, /home/mickey/work/yocto/kas_meta-raspberrypi/poky/meta/classes/base.bbclass, line 65
|   #4: autotools_do_compile, /home/mickey/work/yocto/kas_meta-raspberrypi/poky/meta/classes/autotools.bbclass, line 243
|   #5: do_compile, autogenerated, line 2
ERROR: Task (/home/mickey/work/yocto/kas_meta-raspberrypi/poky/meta/recipes-support/libnl/libnl_3.5.0.bb:do_compile) failed with exit code '1'
NOTE: Tasks Summary: Attempted 5670 tasks of which 1 didn't need to be rerun and 1 failed.

Summary: 1 task failed:
  /home/mickey/work/yocto/kas_meta-raspberrypi/poky/meta/recipes-support/libnl/libnl_3.5.0.bb:do_compile
Summary: There were 2 WARNING messages shown.
Summary: There were 2 ERROR messages shown, returning a non-zero exit code.

kasの-d, --debug Enable debug loggingオプションは、kas自身のデバッグ用っぽいので、こちらもあまりbitbakeのエラーには有効ではなさそう。

他のマシンむけビルド

デフォルト以外のマシン向けにビルドしたい場合は環境変数KAS_MACHINEで設定する。

$ KAS_MACHINE="raspberrypi3" kas build meta-raspberrypi/kas-poky-rpi.yml

他のイメージのビルド

core-image-satoをビルドする場合は下記のように--targetオプションで指定する。

$ kas build meta-raspberrypi/kas-poky-rpi.yml --target core-image-sato

個別のパッケージをビルドする

個別のパッケージのみビルドする場合も--targetオプションで指定する。

$ kas build meta-raspberrypi/kas-poky-rpi.yml --target virtual/kernel

個別のタスクを実行する

do_compileなど個別のタスクを実行したい場合は-c TASK, --cmd TASK, --task TASKオプションで指定する。

$ kas build meta-raspberrypi/kas-poky-rpi.yml --target virtual/kernel -c compile

まとめ

kasを使ってmeta-raspberrypiをビルドしてみた。

第一印象としてはカーネルなどにパッチを作成する必要がある場合や、メタデータ自体が開発中の場合はkasを使用するよりもbitbakeを直接使用するほうが便利。

meta-raspberrypiの例では、kas-poky-rpi.ymlで指定されたブランチをコマンド実行時に上書きできないため、 meta-raspberrypi以外のメタデータはmasterブランチが適用されてしまうため、zeusなどさらに古いブランチを使用する場合は、 各パッケージのバージョンのミスマッチによってビルドエラーになる可能性が高くなる。

これについてはmeta-raspberrypi側で各ブランチ毎にkas-poky-rpi.ymlをメンテナンスするなどで対処できるかもしれないが。

kas自体は、リリースバージョンなどのカスタマイズが必要ない状態のメタデータからLinuxイメージを作成する場合に、 メタデータ同士の依存関係の解決などを自動化してくれるため、便利なツールだと言える。

自分がメタデータを公開する際には、kasのコンフィグファイルも一緒に提供してあげると、ユーザーの間口が広がるかもしれない。

Yocto meta-raspberrypi環境の書き込みにbmaptoolを使ってみる

meta-raspberrypiでbitbakeして作成されるイメージがいつの間にかrpi-sdimgからwic.bz2になっている。 イメージを書き込む手順もddEtcherからbmaptoolになっている。

dunfellのブランチを使用して、最近の手順で試しにラズパイ4を動かしてみる。

構築手順

作成するディレクトリツリーは下記のようなイメージ。

.
├── build
├── downloads
└── layers
    ├── meta-raspberrypi
    └── poky

ソース取得

必要なメタデータを一式ダウンロードする。

$ mkdir -p rpi-dunfell/layers
$ cd rpi-dunfell/layers
$ git clone git://git.yoctoproject.org/poky.git -b dunfell
$ git clone git://git.yoctoproject.org/meta-raspberrypi -b dunfell
$ cd ../

環境変数設定

bitbakeの実行に必要な環境変数を設定する。

$ source layers/poky/oe-init-build-env build

自動的にビルドディレクトリに移動される。 これで、bitbake関連のツールが使用可能になる。

レイヤ追加

meta-raspberryをbitbakeの対象に追加する。

$ bitbake-layers add-layer ../layers/meta-raspberrypi

local.confの修正

MACHINEをraspberrypi4に設定し、UARTを有効化する。

OSSソースコード格納するdownloadsの位置も変更する。また、INITもsystemdに変更する。

MACHINE = "raspberrypi4"
DL_DIR ?= "${TOPDIR}/../downloads"

# enable uart
ENABLE_UART = "1"

# systemd
DISTRO_FEATURES_append = " systemd pam"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""

# connman
IMAGE_INSTALL_append = " connman \
                 connman-client \
"

ビルド

core-image-baseをビルドする。

$ bitbake core-image-base

書き込み

tmp/deploy/images/raspberrypi4core-image-base-raspberrypi4.wic.bz2が生成されるのでこれをbmaptoolで書き込む。

$ sudo bmaptool copy core-image-base-raspberrypi4.wic.bz2 /dev/sdX

/dev/sdXは環境に応じて適宜読み替える。

bmaptoolコマンドについて

bmaptoolコマンドがインストールされてない場合、Ubuntuではbmaptoolはaptでインストールできる。

$ sudo apt install bmap-tools

bmaptoolで書き込む場合、bitbakeで一緒に生成されるcore-image-base-raspberrypi4.wic.bmapがイメージファイルと同じディレクトリに存在すると、 このブロックマップ情報により書き込みが最適化される。

bitbakeでbmapファイルを生成するにはIMAGE_FSTYPESwic.bmapを設定する。

meta-raspberrypiではデフォルトで設定されている。これは下記のコマンドで確認できる。

$ bitbake core-image-base -e | grep '^IMAGE_FSTYPES='
IMAGE_FSTYPES="tar.bz2 ext3 wic.bz2 wic.bmap"

bmapファイルが存在しない場合はbmaptool copy --nomapオプションで使用しない方法や、 イメージファイルからbmaptool createで生成したりする方法もある。 その場合、bitbakeで生成されたbmapを使用したときほど書き込みに係る時間は短縮されない。

また、wicファイルはddコマンドでも書き込みできるがbunzip2などで伸張する必要があるし、 書き込み時間はbmapを使用しない場合とほぼ同じとなる。

そのため、イメージを他の場所に移動する場合などはbitbakeで生成されたbmapファイルとセットで管理するとよさそう。

時間比較

bitbakeで生成されたbmapを使用するケース。

$ sudo bmaptool copy core-image-base-raspberrypi4.wic.bz2 /dev/sda
bmaptool: info: copying time: 13.7s, copying speed 10.5 MiB/sec

bmapファイルを使用しないケース

$ sudo bmaptool copy --nobmap core-image-base-raspberrypi4.wic.bz2 /dev/sda
bmaptool: info: copying time: 22.4s, copying speed 11.8 MiB/sec

ddコマンドで書き込むケース

$ sudo dd if=./core-image-base-raspberrypi4.wic of=/dev/sda bs=100M
276824064 bytes (277 MB, 264 MiB) copied, 21.9507 s, 12.6 MB/s

これらをまとめると次のようになる。

コマンド 時間(秒) 速度(MiB/s)
bmaptool(bmapあり) 13.7 10.5
bmaptool(bmapなし) 22.4 11.8
dd*1 21.9 12.0*2

bmapなしのケースとddがほぼ同等。bmapありのケースで約半分の時間となっている。

この結果の原因としては、書き込み速度自体はほぼ変わらない(というかddのほうが速度は出ている)が、 ブロックマップ情報が適切に定義されて不要な書き込みをしないためと推測している。

まとめ

最近(dunfell以降かな)のmeta-raspberrypiではイメージの出力形式もwic.bz2になっており、 書き込みにもbmaptoolが推奨されるようになっていた。

rpi-sdimgと比較してもbz2で圧縮されている分、イメージファイルのサイズも小さくなっている。 適切なbmapファイルを使用してbmaptoolで書き込む場合、ddよりも高速に書き込めるということがわかった。

*1:bz2の伸張時間は含まない。

*2:ddの速度表示はMB/sだったのでMiB/sに計算し直している。

YoctoのSDKでスタティックライブラリしか提供していないパッケージを使う

はじめに

meta-oeなどには様々なライブラリのパッケージが提供されている。

その中には依存関係のあるパッケージのビルド時にだけ使用する、*.aしか提供しないパッケージも存在する。

SDKを使用してアプリケーションを開発する際にそのようなパッケージの提供するライブラリを使用したいケースでは、 少しコツが必要なのでまとめておく。

今回はそのようなパッケージの例としてabseil-cppを使用する。

サンプルアプリ

abseilを使用するサンプルアプリケーションを作成していく。

ソースコード

main.cppとして下記の内容を作成する。

内容はここから拝借している。

#include <iostream>
#include <string>
#include <vector>
#include "absl/strings/str_join.h"

int main()
{
  std::vector<std::string> v = {"foo", "bar", "baz"};
  std::string s = absl::StrJoin(v, "-");

  std::cout << "Joined string:" << s << "\n";
}

CMakeLists.txt

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

こちらもここから拝借。

cmake_minimum_required(VERSION 3.5)

project(my_project)

# Abseil requires C++11
set(CMAKE_CXX_STANDARD 11)

# Import Abseil's CMake targets
find_package(absl REQUIRED)

add_executable(hello_world hello_world.cc)

# Declare dependency on the absl::strings library
target_link_libraries(hello_world absl::strings)

SDKでビルド

local.confで下記を設定した状態で作成したSDKでこれをビルドする。

IMAGE_INSTALL_append = " abseil-cpp"

アプリのビルドは下記の手順。

$ source /opt/poky/3.1.3/environment-setup-cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi
$ mkdir build
$ cd build
$ cmake ..

abseilのパッケージ(absl)が見つからないというエラーになる。

Call Stack (most recent call first):
  /opt/poky/3.1.3/sysroots/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/usr/lib/cmake/absl/abslConfig.cmake:41 (include)
  CMakeLists.txt:8 (find_package)


-- Configuring incomplete, errors occurred!

SDKのsysrootにabseilのライブラリが正しく含まれていないため発生する。

レシピを作成

適当なレイヤを作ってrecipes-exampleの下にabsl_0.1.bbを作成する。

DESCRIPTION = "abseil sample application"
SECTION = "examples"
LICENSE = "Apache-2.0"
HOMEPAGE = "https://abseil.io/docs/cpp/tools/cmake-installs"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"

SRC_URI = "file://main.cpp \
       file://CMakeLists.txt \
"

inherit cmake
DEPENDS += "abseil-cpp"


S = "${WORKDIR}"

FILES_${PN} = "${bindir}/*"

main.cppCMakeLists.txtはレシピと同じディレクトリのfilesディレクトリに格納する。

このレシピからはabseil-cppのパッケージに依存関係を設定している。

bitbakeでabslをビルド

作成したabsl_0.1.bbが格納されているレイヤをbitbake対象に追加した状態で次のコマンドを実行する。

$ bitbake absl

問題なくビルドが通る。

それではこのレシピをlocal.confIMAGE_INSTALL_appendした状態でSDKを作成すれば解決できるだろうか。

実際にやってみると分かるが、この方法ではSDKにabseilのライブラリが含まれることは無い。

abseil-cppのパッケージはスタティックなライブラリのみを提供しているため、 ビルドに必要なライブラリはすべてabseil-cpp-staticdevに含まれている。ビルド時の依存関係の指定はレシピ名を設定すると そのレシピが提供するサブパッケージがすべて解決されるが、SDK作成時には明示的に指定しないとstaticdevパッケージは含まれないためである。

SDK作成時に特定のレシピのstaticdevを追加

SDKを作成する際に特定のレシピが提供するstaticdevパッケージを追加するにはlocal.confに下記を追加する。

TOOLCHAIN_TARGET_TASK += "abseil-cpp-staticdev"

SDK作成時にすべてのレシピのstaticdevを追加

SDKを作成する際に すべての レシピが提供するstaticdevパッケージを追加するにはlocal.confに下記を追加する。

SDKIMAGE_FEATURES += "staticdev-pkgs"

まとめ

bitbakeでイメージを生成する際にはあまり意識する必要がないstaticdevパッケージだが、 イメージのユーザーにSDKを提供する際には注意が必要となる。

参考

Yocto環境でLogitech Unifyingデバイスが動かない場合

はじめに

Jetson NanoにYocto環境を作ってLogtech Unifyingのキーボード・マウスを接続したが、 全く反応しなかった。

原因

イメージに必要なドライバがインストールされていないことが原因だった。

下記のコマンドでカーネルコンフィグを確認したところ必要なデバイス[m]になっている。

$ bitbake virtual/kernel -c menuconfig

関連ありそうなドライバ

結局hid_logitech_hidppが必要だったが、依存関係的にhid_logitech_djも必要らしい。

対処

local.confに下記の設定を追加する。

# logitech unifying drivers
IMAGE_INSTALL_append = " \
                     kernel-module-hid-logitech-dj \
                     kernel-module-hid-logitech-hidpp \
"

まとめ

Logitech UnifyingのデバイスをYocto環境で使用する場合は注意が必要。

例えばkernel-modulesメタパッケージがインストールされており、 カーネルモジュールがすべてインストールされるような環境では問題にはならない。 ただ、モジュールすべてをインストールするとディスク容量を大量に消費するため、 必要最小限のみをインストールするような場合のほうが多い。

PCでは動いたデバイスが動かない場合は、次の2つを確認してみると良い。

  1. カーネルコンフィグ
  2. 必要なモジュールがイメージにインストールされているか

意外と2は見落としがち。