x86_64 ubuntu上でcatapultをarm64用にビルドする(cow)

catapult

2019.06.07
ノードが動くことは確認しましたが色々と上手く行かない部分が多いのでcatapultのバージョンを一旦cowに下げました。
Dragonはまた別で書きます。

さいしょに

ブロックチェーンのコアエンジンであるcatapultをRaspberryPiで動かしたい。

しかし、RaspberryPi上でビルドしようとすると、とても時間がかかる。実際に一度やってみて、boostライブラリのビルドの段階であまりにも時間がかかるので諦めました。

そこで、クロスコンパイルすれば良いじゃないかということでやってみた結果です。なお、これを書いたのは2019.6.2であり、間違い等あれば修正されていくと思います。

なお、前提として

と思って取り組んだため、mongozeromqは触っていません。(これらはapi-node用のエクステンションになるはずなので)

catapultのバージョン

Dragon
Cow

コンパイルするPC(OS)

x86_64 GNU/Linux
NAME="Ubuntu”
VERSION="18.04.2 LTS (Bionic Beaver)”

RaspberryPi

raspberry pi 3 model B
OS : ubuntu 18.04
https://wiki.ubuntu.com/ARM/RaspberryPi
(上記URLのubuntu-18.04.2-preinstalled-server-arm64+raspi3.img.xz。hfではありません。)

手順の前に

基本的には下記の流れに沿います。

https://github.com/nemtech/catapult-server/blob/master/BUILDING.md

以下、『BUILDING.mdの通り』などと書いていれば、上記URLに書いてある手順と同じだと思ってください。

手順

クロスコンパイラのインストール

$ sudo apt install g++-aarch64-linux-gnu

これで、クロスコンパイル用のツール群が/usr/binに入ります。
/usr/binの中を見てみると、aarch64-linux-gnu-gccaarch64-linux-gnu-gcc-7などを見つけることが出来るはずです。

toolchain

cmakeコマンド時にコンパイラの指定を行うことも出来ますが、それだと大変になるのでツールチェイン用のファイルを作成します。

toolchain.arm.cmakeという名前のファイルを作成し、このファイルの中にコンパイラやリンカ等の情報を書き込みます。

SET(CMAKE_C_COMPILER /usr/bin/aarch64-linux-gnu-gcc-7)

SET(CMAKE_CXX_COMPILER /usr/bin/aarch64-linux-gnu-g++-7)

SET(CMAKE_LINKER /usr/bin/aarch64-linux-gnu-ld.gold)

SET(CMAKE_NM /usr/bin/aarch64-linux-gnu-nm)

SET(CMAKE_OBJCOPY /usr/bin/aarch64-linux-gnu-objcopy)

SET(CMAKE_OBJDUMP /usr/bin/aarch64-linux-gnu-objdump)

SET(CMAKE_RANLIB /usr/bin/aarch64-linux-gnu-ranlib)

Prerequisites

BUILDING.mdに書いてあるとおりです。g++は既にクロスコンパイル用のものをインストール済みなのですが、別の場所で必要になるので抜かないでください。

sudo apt install autoconf libtool cmake curl git xz-utils \
     libatomic-ops-dev libunwind-dev g++ gdb libgflags-dev \
     libsnappy-dev ninja-build python3 python3-ply

Boost

$ curl -o boost_1_69_0.tar.gz -SL https://dl.bintray.com/boostorg/release/1.69.0/source/boost_1_69_0.tar.gz
$ tar -xzf boost_1_69_0.tar.gz

$ mkdir boost-build-1.69.0
$ cd boost_1_69_0
$ ./bootstrap.sh --prefix=${HOME}/boost-build-1.69.0

ここまではBUILDING.mdと同じです。

ここで、boost_1_69_0内になるproject-config.jam12行目を書き換えます。

# Boost.Build Configuration
# Automatically generated by bootstrap.sh

import option ;
import feature ;

# Compiler configuration. This definition will be used unless
# you already have defined some toolsets in your user-config.jam
# file.
if ! gcc in [ feature.values <toolset> ]
{
    # ここを下記のように書き換える
    using gcc : arm : aarch64-linux-gnu-g++ ;
}

project : default-build <toolset>gcc ;

(以下略)

以降は、BUILDING.mdに書いてあるとおり、

$ ./b2 --prefix=${HOME}/boost-build-1.69.0 -j 4 stage release
$ ./b2 install --prefix=${HOME}/boost-build-1.69.0

これで、ビルドされたものがboost-build-1.69.0に入ります。

GTest

toolchainの指定を追加するだけです。

$ git clone https://github.com/google/googletest.git googletest.git
$ cd googletest.git
$ git checkout release-1.8.0

$ mkdir _build && cd _build
$ cmake -DCMAKE_TOOLCHAIN_FILE=~/toolchain.arm.cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON ..
$ make
$ sudo make install

google benchmark

下記参照
https://github.com/google/benchmark/issues/351

-DCMAKE_CROSSCOMPILING=1これは必須。

-DRUN_HAVE_STD_REGEX=0
-DRUN_HAVE_POSIX_REGEX=0
-DRUN_HAVE_GNU_POSIX_REGEX=0
-DRUN_HAVE_STEADY_CLOCK=0

これらは今回、全て指定したが、本来はいくつか指定すればよいだけかもしれない。
これにtoolchain.arm.cmakeの指定とCMAKE_BUILD_TYPE=ReleaseBENCHMARK_ENABLE_GTEST_TESTS=OFFを加えて、結果、cmake時のコマンドは下記のようになる。

$ cmake -DCMAKE_TOOLCHAIN_FILE=~/toolchain.arm.cmake \
        -DCMAKE_CROSSCOMPILING=1 \
        -DRUN_HAVE_STD_REGEX=0 \
        -DRUN_HAVE_POSIX_REGEX=0 \
        -DRUN_HAVE_GNU_POSIX_REGEX=0 \
        -DRUN_HAVE_STEADY_CLOCK=0 \
        -DCMAKE_BUILD_TYPE=Release \
        -DBENCHMARK_ENABLE_GTEST_TESTS=OFF \
        ..
$ git clone https://github.com/google/benchmark.git google.benchmark.git
$ git clone https: cd google.benchmark.git
$ git checkout v1.4.1

$ mkdir _build && cd _build
$ cmake -DCMAKE_TOOLCHAIN_FILE=~/toolchain.arm.cmake -DCMAKE_CROSSCOMPILING=1 -DRUN_HAVE_STD_REGEX=0 -DRUN_HAVE_POSIX_REGEX=0 -DRUN_HAVE_GNU_POSIX_REGEX=0 -DRUN_HAVE_STEADY_CLOCK=0 -DCMAKE_BUILD_TYPE=Release -DBENCHMARK_ENABLE_GTEST_TESTS=OFF ..
$ make
$ sudo make install

Mongo

今回はpeer向けなので不要です。

ZMQ

今回はpeer向けなので不要です。

RocksDB

$ git clone https://github.com/facebook/rocksdb.git rocksdb.git
$ cd rocksdb.git
$ git checkout -B "5.18.fb" "origin/5.18.fb"

ここで、rocksdb.git/CMakeLists.txt211行目を下記のように書き換えます。
(cmake時にオプションで変更することが出来る気がするけどやり方が分からない。)

(省略)
else()
  if(MSVC)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX2")
  else()
    if(NOT HAVE_POWER8)
      # ここが211行目
      # 元は set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv8-a")
    endif()
  endif()
endif()
(省略)

要するに、CMAKE_CXX_FLAGS-march=armv8-a、つまり、g++コマンドに-march=armv8-aオプションを渡してあげるために書き換える。この、armv8-aなどは、rapsberryPiのバージョンによって変わることになります。

これで、BUILDING.mdに書いてあるとおりの操作に戻ります。cmake時にはツールチェインの指定を忘れずに。

$ mkdir _build && cd _build
$ cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=OFF -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_TOOLCHAIN_FILE=~/toolchain.arm.cmake ..
$ make
$ sudo make install

catapult

git clone https://github.com/nemtech/catapult-server.git
cd catapult-server

ここで、CMakeGlobalSettings.cmake46行名set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")を追記します。下記のようになります。

(略)

endif()

### set compiler settingsa

# 追記
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")

if(MSVC)
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /WX /EHsc")
        # in debug disable "potentially uninitialized local variable" (FP)
        set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd /D_SCL_SECURE_NO_WARNINGS /wd4701")
        set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /MD /Zi")
        set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD")

(略)

さらに、monogozeromqはAPI用のエクステンションになるので、今回は不要なはずです。

ということで、catapult-server/extensions/CMakeLists.txtmongozeromqの部分をコメントアウトします。(もっとスマートな方法があるのかもしれない)

catapult-server/extensions/CMakeLists.txt

# ファイルの最下部

add_subdirectory(addressextraction)
add_subdirectory(diagnostics)
add_subdirectory(eventsource)
add_subdirectory(filespooling)
add_subdirectory(harvesting)
add_subdirectory(hashcache)
#add_subdirectory(mongo) コメントアウト
add_subdirectory(networkheight)
add_subdirectory(nodediscovery)
add_subdirectory(packetserver)
add_subdirectory(partialtransaction)
add_subdirectory(pluginhandlers)
add_subdirectory(sync)
add_subdirectory(syncsource)
add_subdirectory(timesync)
add_subdirectory(transactionsink)
add_subdirectory(unbondedpruning)
#add_subdirectory(zeromq) コメントアウト

これで下準備は完了です。例によって、cmakeにツールチェインとしてtoolchain.arm.cmakeを追加で指定します。

$ mkdir _build && cd _build
$ cmake -DBOOST_ROOT=~/boost-build-1.69.0 -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=~/toolchain.arm.cmake ..
$ make publish

ここで出力されるログで、CMAKE_CXX_FLAGSの部分に-pthreadがあることを確認しましょう。CMakeGlobalSettings.cmakeで追記した部分が反映されているはずです。

$ cmake -DBOOST_ROOT=~/boost-build-1.69.0 -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=~/toolchain.arm.cmake ..

-- CATAPULT_VERSION_DESCRIPTION: 95e0c6a [master]
-- CMAKE_CXX_COMPILER_ID: GNU
-- CMAKE_CXX_FLAGS:  -pthread -Wall -Wextra -Werror -Wstrict-aliasing=1 -fvisibility=hidden
-- CMAKE_PREFIX_PATH:
-- BOOST_ROOT:

あとは、必要な部分をビルドしていくだけです。大量のテスト群もビルドしたくないので、必要な部分のみをビルドします。

makeはターゲットを指定すると、本体とそれが依存するライブラリをビルドしてくれるので、それを利用して必要な部分のみをビルドします。

※version cowに下げたことに併せてplugin群など書き換えています。

server

$ make catapult.server -j4

extension群。

$ make extension.addressextraction  \
       extension.diagnostics        \
       extension.eventsource        \
       extension.filechain          \
       extension.harvesting         \
       extension.hashcache          \
       extension.networkheight      \
       extension.nodediscovery      \
       extension.packetserver       \
       extension.partialtransaction \
       extension.pluginhandlers     \
       extension.sync               \
       extension.syncsource         \
       extension.timesync           \
       extension.transactionsink    \
       extension.unbondedpruning    \
       -j4

plugin群。

$ make catapult.plugins.accountlink \
       catapult.plugins.aggregate   \
       catapult.plugins.hashcache   \
       catapult.plugins.lockhash    \
       catapult.plugins.locksecret  \
       catapult.plugins.mosaic      \
       catapult.plugins.multisig    \
       catapult.plugins.namespace   \
       catapult.plugins.property    \
       catapult.plugins.signature   \
       catapult.plugins.transfer    \
       -j4

試運転

これで一通り入りました。あとは、設定をして動かしてみるだけです。

とりあえずエミュレーターを使って試しに動かしてみたいときは下記のようにしてください。

$ sudo apt install qemu-user-binfmt
$ qemu-aarch64 -L /usr/aarch64-linux-gnu ./catapult.server

RaspberryPi上で動かす

必要になるのはcatapult-serverrocksdb.gitです。これらをRaspberryPiに移しましょう。

実際、catapult内で必要になるのは_buildの中身のみなので、抜き出しても構いません。ただし、そのままだと設定ファイルがはいっていないので、その一つ下のresourcesの中身を、_build内のresourcesに移しておきましょう。

ここで、rocksdb.gitで、librocksdb.solibrocksdb.so.5がコピー出来ないことがあります(許可されない操作と出るはず)。 20190602_building_catapult_for_arm この場合は、一旦無視して、rocksdb.gitをRaspberryPiに移した後で、librocksdb.solibrocksdb.so.5をRaspberryPi上で作成します。

これらはシンボリックリンクなので、lnコマンドで生成します。

# シンボリックリンクを作成
rocksdb.git/_build$ ln -s librocksdb.so.5.18.3 librocksdb.so.5
rocksdb.git/_build$ ln -s librocksdb.so.5 librocksdb.so

# 確認
rocksdb.git/_build$ ls -l
lrwxrwxrwx 1 XXX  XXX        15 Jun  2 08:32 librocksdb.so -> librocksdb.so.5
lrwxrwxrwx 1 XXX  XXX        20 Jun  2 08:32 librocksdb.so.5 -> librocksdb.so.5.18.3
-rwxrwxr-x 1 XXX  XXX   6974480 Jun  2 08:32 librocksdb.so.5.18.3

USBメモリ等でRaspberryPiに_buildをコピーできたら、まず最初にld(ライブラリ等の探査コマンド)が必要な共有ライブラリを見つけられるように、パスを通します。

まず最初に確認。

/_build/bin$ ldd ./* | grep "not found"

大量のnot foundが出ると思いますが、よく見てみると、libboost_XXXlibrocksdb.so.5であることが分かると思います。

では、パスを通します。パスを通すにはふたとおりの方法があり、環境変数LD_LIBRARY_PATHにパスを指定するか、/etc/ld.so.confにパスを書き込むかのどちらかです。
(それらの違いは別で調べてください。)

設定を反映させるにはldconfigで反映させます。

/_build/bin/$ export LD_LIBRARYは_PATH=$LD_LIBRARY_PATH:<_build/bin/boostのパス>:<rocksdb.git/_buildのパス>

# 適応
/_build/bin/$ ldconfig

# 確認(何も表示されなければok)
/_build/bin$ ldd ./* | grep "not found"

あとは、libgflagsnot foundとして残るはずです。これも入れましょう。

$ sudo apt-get install libgflags-dev

これで、not foundがなく、全ての必要なのライブラリにパスが通っていることが確認できれば、あとはcatapultの設定を行い、catapult.serverで起動します。

全ての機能については確認していませんが、ノードが起動します。

さいごに

わからないところ

所感

ビルドの時間は圧倒的に短縮できますが、セルフビルドと同じぐらい大変でした。
また、どこかしらのバージョンが変われば途端にビルド出来なくなる気がしています。