Linux
AGL

Qt を使った Auto Grade Linux アプリ開発方法

Qt 初心者が Qt を使った AGL アプリを作成する手順の記録。Linux GUI に慣れてないので、Qt アプリの作成を Mac OS で行い、クロスビルドから wgt の作成までを Ubuntu 16.04 LTS を使った。

ソースコード: https://github.com/propella/agl-hello

Qt インストール方法

簡単なアプリの作成

簡単なアプリのソース

自分が思いついた一番簡単な Qt の GUI のプロジェクトファイルは以下のようになった。

# agl-hello.pro

QT += widgets
TARGET = agl-hello
TEMPLATE = app
SOURCES += main.cpp
CONFIG += debug

pro ファイルは qmake というツールの設定ファイルで、qmake は pro ファイルから Makefile 等を生成する。代入形式で定義する。リストは空白区切り。

  • QT : プログラムで使う Qt Module
    • QT にはデフォルトで coregui 等の基本的な値がセットされているため、追加するには += を使う。
  • TARGET : 出来上がるファイルの名前。デフォルトで project name と同じになる。
  • TEMPLATE : アプリを作る時は app, ライブラリの時は lib
  • SOURCES : ソースコードを記述する。
  • CONFIG : デバッグしたい時 debug を追加
  • pro ファイルのデバッグには message が便利。

以下プログラムソースコード。

// main.cpp

#include <QApplication>
#include <QLabel>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QLabel label("Hello, World!");
    label.setWindowFlags(Qt::FramelessWindowHint);
    label.show();
    return a.exec();
}

ここで setWindowFlags(Qt::FramelessWindowHint) で FramelessWindowHint を指定するのがポイント。これが無いとなぜか AGL HomeScreen から表示出来なかった。

ビルド方法

Qt Creator を使う場合

  • File > Open File or Project > agl-hello.pro を選択
  • Configure Project でデフォルトのまま Configure Project を押す。いつの間にか qthello.pro.user が出来ている。
  • 再生ボタンで実行
  • ../build-qthello-Desktop_Qt_5_9_1_clang_64bit-Debug にビルド用フォルダが出来ている。

Terminal を使う場合

~/Qt/5.9.1/clang_64/bin/qmake
make

クロスビルド用 Docker 環境の作成

AGL SDK Quick Setup に、Docker を使ったクロスビルド環境の作成が紹介されている。Docker 無しで Ubuntu 16.04 の上に直接 SDK をインストールしても動くので、面倒ならこのステップは省略出来る。

この docker image は特定のディレクトリ構成で使うように出来ているので、~/ssd 及び ~/devel を作成する

mkdir ~/ssd ~/devel
chmod a+w ~/ssd ~/devel

すると以下のようにコンテナ内からホストにアクセス出来る。

container host 用途
/xdt ~/ssd/xdt_$ID SDK インストール場所
/home/devel/mirror ~/ssd/localmirror_$ID 作業場所
/home/devel/share ~/devel/docker/share ダウンロードファイル置き場

docker image をダウンロードして設定

wget https://download.automotivelinux.org/AGL/snapshots/sdk/docker/docker_agl_worker-3.0.tar.xz
sudo docker load -i docker_agl_worker-3.0.tar.xz
docker images
git clone https://git.automotivelinux.org/AGL/docker-worker-generator
cd docker-worker-generator
./contrib/create_container 0 docker.automotivelinux.org/agl/worker:3.0

コンテナにログイン (パスワードは devel) なぜか TERM=screen が無いと、screen 経由で vi が上手く動かない。

TERM=screen ssh -p 2222 devel@localhost

コンテナ内からは uid: 1664 / gid: 1664 の devel というユーザでホストとファイル共有する。以下のように設定するとホストとファイル交換が出来る。(linux 詳しく無いので間違ってるかも)

ホスト

sudo groupadd --gid 1664 devel
sudo useradd --uid 1664 --gid 1664 devel

コンテナ

sudo groupadd --gid 1002 tyamamiya
sudo gpasswd -a devel tyamamiya

SDK のインストール

AGL のターゲット向け SDK はどこからか拾ってくるか、自分で作る場合は AGL の build ディレクトリで agl-demo-platform-crosssdk をビルドする。

bitbake agl-demo-platform-crosssdk

SDK は tmp/deploy/sdk/poky-agl-glibc-x86_64-agl-demo-platform-crosssdk-aarch64-toolchain-3.99.1+snapshot.sh のような場所に出来る。Docker を使っている場合はこれを ~/devel/docker/share に置いてコンテナと共有し、コンテナの install_sdk でインストールする。

Docker を使わない場合はこのファイルを直接実行すればインストーラが起動する。

host

mv tmp/deploy/sdk/poky-agl-glibc-x86_64-agl-demo-platform-crosssdk-aarch64-toolchain-3.99.1+snapshot.sh ~/devel/docker/share/

container

install_sdk ~/share/poky-agl-glibc-x86_64-agl-demo-platform-crosssdk-aarch64-toolchain-3.99.1+snapshot.sh 

実機で動くかテストしてみる。(デバイスのホスト名が device とする)

$ source /xdt/sdk/environment-setup-aarch64-agl-linux
$ cat <<EOT > hello.c
#include <stdio.h>
int main() { printf("Hello world\n"); return 0; }
EOT
$ $CC -o hello hello.c
$ scp hello root@device:.
$ ssh root@device ./hello
Hello world

アプリを Widget にする

クロスコンパイルしてデバイス上に表示する

サンプルアプリをコンテナの ~/mirror にコピーする。SDK を source すると qmake が使えるようになるので、そのままコンテナでビルドし、デバイス上で動く事を確認する。

$ git clone git@github.com:propella/agl-hello.git
$ cd agl-hello
$ source /xdt/sdk/environment-setup-aarch64-agl-linux
$ qmake
$ make
$ scp agl-hello root@device:.

AGL 起動時に Home Screen が表示される場合、そのままでは普通の Qt アプリが動かないので weston.ini を編集して ivi-shell.so を無効にする。

$ vi /etc/xdg/weston/weston.ini

# shell=ivi-shell.so (shell=ivi-shell.so をコメントアウト)

$ systemctl restart weston
$ ~/agl-hello

AGL デバイスの画面に寂しく Hello World! が表示されれば成功。終わったら weston.ini の設定を戻して systemctl restart weston する。

Wiget を作成する。

AGL のアプリは W3C Widget 仕様に基づいて作成した wgt ファイルだ。SDK 内に wgt ファイルを作るためのツールがある。パッケージを作るためには以下のファイルが必要。

  • config.xml : 今から作ります
  • アイコンファイル : なんでも良い
  • 実行ファイル
<?xml version="1.0" encoding="UTF-8"?>
<widget xmlns="http://www.w3.org/ns/widgets" id="poi" version="0.1">
  <name>Hello World</name>
  <icon src="icon.png"/>
  <content src="agl-hello" type="application/x-executable"/>
  <description>A Simple Hello World</description>
  <author>Takashi Yamamiya</author>
  <feature name="urn:AGL:widget:required-permission">
    <param name="http://tizen.org/privilege/internal/dbus" value="required" />
  </feature>
  <license>MIT</license>
</widget>

AGL App Framework は widget の id 属性でアプリを識別します。今回何故か poi という ID にしてみました。本来 ID はアプリを表す名前であるべきですが、現時点 (AGL 3.0) では HomeScreen の実装が不十分で、プリインストールされた決め打ちの ID のアイコンしか表示されません。今回は問題のある HomeScreen を騙すために、poi という既に存在するアプリの ID を流用します。

これらをまとめて適当なディレクトリに置きます。SDK の wgtpkg-pack コマンドで wgt ファイルを作成し、デバイスに転送します。

mkdir root
cp config.xml root/
cp icon.png root/
cp agl-hello root/
wgtpkg-pack -f -o agl-hello.wgt root
scp agl-hello.wgt root@device:.

デバイス側では afm-util コマンドを使ってアプリの登録削除を行います。

afm-util uninstall poi@0.1 # 既存の poi アプリを削除
afm-util install agl-hello.wgt # 作成した Hello World を poi 名前で登録

これで AGL Home Screen から POINT OF INTEREST のアイコンを押すと Hello World が表示されます。起動中にアプリの入れ替えは出来ないようなので、うまくいかない時はリブートして下さい。

afm-util には便利な機能が沢山あって Quick Tutorial に詳しい紹介があります。私がよく使うのは以下です。

  • afm-util uninstall (id@バージョン) # 既存の poi アプリを削除
  • afm-util install (wgt ファイル) # 作成した Hello World を poi 名前で登録
  • afm-util runners # 起動中のアプリを確認
  • afm-util terminate (runid) # runid で起動しているアプリを停止
  • systemctl --user restart HomeScreen # HomeScreen 再起動
    • アプリの入れ替えには役に立たないが、インストールしていない状態がからインストールした時にこのコマンドでアイコンを表示し直せる。

CMake で Widget ファイルを作成する

折角なのでこれらの作業を自動化するために cmake を使う。https://git.automotivelinux.org/apps/hvac 等の他のアプリを見ると qmake だけでも自動化出来るようだが、たまたま参考にした poi が cmake を使っていたので cmake になってしまった。

# CMakeLists.txt

cmake_minimum_required(VERSION 2.8.11)

project(agl-hello)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fPIC ")
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)

find_package(Qt5Widgets REQUIRED)
qt5_add_resources(agl_hello_QRC )
add_executable(agl-hello main.cpp ${agl_hello_SRC} ${agl_hello_QRC})
target_link_libraries(agl-hello Qt5::Core Qt5::Widgets)

install (TARGETS agl-hello DESTINATION bin)

configure_file(config.xml.in config.xml)
add_custom_command(
        OUTPUT agl-hello.wgt
        DEPENDS agl-hello
        COMMAND rm -rf package
        COMMAND mkdir -p package/root
        COMMAND cp config.xml package/root/
        COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/icon.png package/root/icon.png
        COMMAND cp agl-hello package/root/agl-hello
        COMMAND wgtpkg-pack -f -o package/agl-hello.wgt package/root
)
add_custom_target(widget ALL DEPENDS agl-hello.wgt)

qmake から cmake への変換は http://doc.qt.io/qt-5/cmake-manual.html に詳しく書いてあるが、ポイントは以下

  • set(CMAKE_INCLUDE_CURRENT_DIR ON) # 良くわからないがとりあえず入れておく。
  • set(CMAKE_AUTOMOC ON) # 良くわからないがとりあえず入れておく。
  • find_package(Qt5Widgets REQUIRED) # qt5_ 関連のマクロが使えるようになる。
  • qt5_add_resources(agl_hello_QRC ) # この例では使ってないが、リソースファイルをビルドしたい時に使う。
  • target_link_libraries(agl-hello Qt5::Core Qt5::Widgets) # cmake の QT と同様の内容を記述

これで結局ビルド手順は以下のようになる。

cmake
make

package/agl-hello.wgt に Widget が出来る。

AGL Recipe の書き方

ここまで行くとあとはレシピを書くだけだが、力尽きたので今度書く。

基本 cmake_qt5, pkgconfig, aglwgt を inherit するだけでパッケージのインストールまで行われる。