RustのLLVM IRでプログラム分析ことはじめ

この記事は、Rustその2 Advent Calendar 2019の22日目の記事です。

はじめに

Rustのコンパイラは内部でLLVMを利用しています。

LLVMとはいわゆるコンパイラ基盤というもので、Rustのコンパイル側でLLVMの中間コード(IR)まで生成できたら、あとはLLVM側が最適化や実行ファイルの作成までを行ってくれます。

Rust Blogの記事「Introducing MIR」の中にある画像が大変わかりやすいので、そちらを引用させていただきます。

f:id:toyamaguchi:20191221104040p:plain
ビルドの流れ

このコンパイラ基盤の視点から言うと、Rustコンパイラはフロントエンド、LLVM側はバックエンドという位置づけです。 LLVMが様々な環境で動くプログラムを生成できるからこそ、Rustも多くの環境で動くプログラムを比較的容易に生成できるわけです。 プログラムの最適化技術についても、LLVM側でそのノウハウを洗練させていけば、LLVMを利用しているプログラミング言語すべてで洗練されていきます。

LLVMコンパイルをする過程では、「Pass」という名前のモジュールにIRを何度か通すことになります。 Passは入力としてIRを受け付け、それを最適化して、IRを出力するフィルタのようなものです。 それぞれ異なる最適化手法が実装されたPassをいくつか通し、IRを洗練させていくようです。

これについては、Adrian Sampson氏のブログ記事「LLVM for Grad Students」(日本語記事)が初心者向けで大変詳しくてありがたいです。(むしろ、この記事をRustに適用したのが本記事と言っても過言はないです。) このブログ記事から、LLVMコンパイルの流れの図を引用したいと思います。

f:id:toyamaguchi:20191221110800p:plain
LLVMコンパイルの流れ

また、LLVMはただのコンパイラ基盤として使われるだけでなく、ソフトウェアの分析ツールとして使われることもあります。 ソースコードコンパイルする際に得られるメタデータを使って、ソフトウェアの構造を明らかにしたり、データの危険な使用がないかを調査するのです。 その調査をする際にも、さきほどのPassを使ってメタデータを取得していくようです。

前置きが長くなりましたが、このブログ記事ではRustのソースコードからLLVM IRを生成し、それをPassに読み込ませてプログラム分析をする「一歩目」を踏み出そうと思います。

LLVMのダウンロード

どうもRustはLLVM 9.0を使っているようなので、それをダウンロードします。

私のLinux環境はDebian Linuxなのですが、DebianUbuntuのユーザーにはaptでインストールできるnightly packageというものがあるようなので、今回はそれを使ってみます。

このサイトに従って、次のコマンドを実行しました。

bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)"

2019年12月21日現在では、LLVM 9.0.1がダウンロードできたようです。

Passの作成

LLVM 9.0の仕様に沿ったPassを用意します。

今回はPassの作り方も詳しくわからないため、次のレポジトリを利用します。 これはLLVMのPassのいくつかの実装例を公開してくれているレポジトリです。

このレポジトリの中には、PassのHelloWorldがありますので、それを使っていきます。 CMakeLists.txtとHelloWorld.cppをダウンロードしてましょう。

ただし、どうもこのままできあがったPassを使うと、使用した最後にSegmentation Faultが起こってしまうようです。 調べてみたところ、GitHubのIssueStackoverflowに似た問題があがっていたので、この解決方法を導入します。 Passのコンパイル時に、リンカオプションの-Wl,-znodeleteを追加することで、共有オブジェクトのアンロードを防ぎ、Segmentation Faultを防ぐことができるそうです。

CMakeLists.txtの中に、次に記すような行を追加しておきましょう。

if(NOT LLVM_ENABLE_RTTI)                                
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")   
endif()                                                 
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,-znodelete")    # add this line!

ちなみに、コンパイル方法は次のようになります。(一般的なcmakeの方法です。) 2つのファイルが置いてあるフォルダ内で実行しましょう。

mkdir build
cd build
cmake ..
make

これでlibHelloWorld.soというPassファイルが完成します。

RustのソースコードLLVM IRに変換

今回はRustのソースコードとして、「cargo new」してできるデフォルトのソースコードを利用します。 プロジェクトフォルダを作って、中に入りましょう。

cargo new hello_world
cd hello_world

rustcコマンドでLLVM IRを作成するコマンドは、次のようになります。 このコマンドで、人間が読めるLLVM IRのソースコードである、main.llが生成されます。

rustc src/main.rs --emit=llvm-ir

ただ、実際のところPassが読み込むのはLLVM IRのビットコードです。 rustcコマンドでLLVM IRのビットコード生成しましょう。 main.bcが生成されます。

rustc src/main.rs --emit=llvm-bc

Passによる分析

今回のPassはソースコード上の関数を読み込む際に実行される「FunctionPass」です。 しかも、関数の名前と引数の数を表示するだけの簡単なものなので、実は「分析」というほど難しいものではありません。

さっそくPassを使ってmain.bcを読み込んでみましょう。「opt」コマンドというものを使って実行します。

opt-9 -load pass-f/build/libHelloWorld.so --legacy-hello-world -o output.bc main.bc

これを実行すると、標準出力に次のような内容が出力されました。

Visiting: _ZN3std2rt10lang_start17hf1171c84b02c6532E (takes 3 args)
Visiting: _ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17h7ce99b67d7e681a9E (takes 1 args)
Visiting: _ZN3std3sys4unix7process14process_common8ExitCode6as_i3217h8b78488b8649031eE (takes 1 args)
Visiting: _ZN4core3fmt9Arguments6new_v117hcb57928db4a7c5fcE (takes 5 args)
Visiting: _ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17hfa439257376c02c0E (takes 1 args)
Visiting: _ZN4core3ops8function6FnOnce9call_once17h04664e48e200f143E (takes 1 args)
Visiting: _ZN4core3ptr18real_drop_in_place17h11442e413e5b1531E (takes 1 args)
Visiting: _ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17hfd48e48bde5a14dcE (takes 0 args)
Visiting: _ZN68_$LT$std..process..ExitCode$u20$as$u20$std..process..Termination$GT$6report17h7035bf1bc5cd901aE (takes 1 args)
Visiting: _ZN4main4main17h9e0ea2b04575dfdbE (takes 0 args)
Visiting: main (takes 2 args)

HelloWorld.cpp内でいうところの次のコードが、関数の情報を表示しているところです。

void visitor(Function &F) {
    errs() << "Visiting: ";
    errs() << F.getName() << " (takes ";
    errs() << F.arg_size() << " args)\n";
}

IRのビットコードには、main関数以外にもプログラムの実行に関わる目に見えていなかった関数があったことがわかりますね。

まとめ

Rustの内部では、コンパイラ基盤のLLVMを使っています。

このLLVMを使うとプログラム分析を行うことができます。

今回はFunctionPassという種類のPassを作成しました。

Passの種類は他にもModulePass、CallGraphSCCPass、BasicBlockPassなど、興味深い名前のものもあります。

いろいろな種類のPassを作ってみて、Rustのソースコードを分析できるようにしたら楽しそうですね。

参考文献

f:id:toyamaguchi:20191221150338p:plain

技術同人誌執筆中のコンテンツの抜け漏れ対策スクリプト

この記事は技術同人誌 Advent Calendar 2019の12月8日分の記事となります。

私は、今年の9月に技術書典7に初参加し、技術同人誌『RustではじめるOpenGL』を頒布させていただきました。 現在もPDF形式のダウンロード版と、紙版の両方をBOOTH.pmで販売させていただいてます。

toyamaguchi.booth.pm

ただし、紙版のほうは一部コンテンツが抜けていたことがあとから発覚しまして、別記事にて内容を通知させていただいております。

このようなコンテンツの抜け漏れがあった場合、PDF形式のダウンロード版ではすぐにアップデートをかけることができたのですが、紙版では対応が難しいところが難点です。

今回、私がAdvent Calendarのためにお伝えする内容は、このような抜け漏れ対策のためのものです。

コンテンツの抜け漏れ対策スクリプト

私がやらかしてしまったのは、章や節のタイトルは書いてあるのに、その中の本文を書き忘れた、というものです。 Re:Viewの記法でいえば、「= 章のキャプション」もしくは「== 節のキャプション」だけしか書いていなかったわけです。

印刷するまえに、より周到な確認ができていたらよかったのですが、さすがに一人で執筆していると気が回らないこともあります。 こんな状況下で書き忘れに気づくためには、ことあるごとに警告を出すようにするしかないと思いつきました。

そこでできあがったのが、コンテンツの抜け漏れ対策スクリプトです。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import yaml


def read_catalog_yaml():
    fp = open('articles/catalog.yml')
    yaml_data = yaml.load(fp)
    fp.close()

    file_name_list = list()
    for item in ['PREDEF', 'CHAPS', 'APPENDIX', 'POSTDEF']:
        if not isinstance(yaml_data[item], type(None)):
            file_name_list.extend(yaml_data[item])

    return file_name_list


def read_contents(file_name):
    contents_list = list()

    fp = open(os.path.join('articles', file_name))
    for line in fp:
        line = line.strip()
        if 0 == len(line):
            continue
        if line.startswith('=') and not line.startswith('===['):
            contents_list.append([line, 0])
        elif 0 != len(contents_list):
            contents_list[-1][1] = contents_list[-1][1] + 1
    fp.close()

    return contents_list


def main():
    file_name_list = read_catalog_yaml()
    contents_list = list()
    for file_name in file_name_list:
        contents_list.extend(read_contents(file_name))

    error_flag = False
    for contents in contents_list:
        if 0 == contents[1]:
            if False == error_flag:
                print('#########################################')
                print('# content checker found empty contents! #')
                print('#########################################')
                error_flag = True
            print('{} is empty'.format(contents[0]))

    if error_flag:
        exit(1)

    print('content checker found no issue!')
    exit(0)


if __name__ == '__main__':
    main()

解説

まずこのスクリプトはarticles/catalog.ymlを読み込み、すべての記事ファイルのリスト作ります。

そのあと、それぞれの記事ファイルを読み込み、各章・節の中に何行のコンテンツがあるのか数えていきます。 空行はカウントしないようにします。

すべてのファイルのコンテンツ(文字が存在する行の数)を数え上げ、まったくコンテンツを含まない章・節が存在した場合には、警告して終了ステータス1で終わります。 空コンテンツを発見したときの出力例はこちらです。

#########################################
# content checker found empty contents! #
#########################################
= はじめに is empty
= 著者紹介 is empty

問題なければ、その旨を表示して終了ステータス0で終わります。 空コンテンツがなかったときの出力例はこちらです。

content checker found no issue!

このアイデアは以前から持っていたのですが、Advent Calendarのために昨晩サクッと作りました。 もっとシンプルに書ける人は、自分で自作するとよいのではないでしょうか。 自作のツールはカスタマイズも楽ですし。

注意するところといえば、「=」が連続して始まる行が章・節の始まりですが、コラムの始まりと終わりは「===[column]」「===[/column]」と表せるようなので、その処理だけ区別しましょう。

まとめ

どうしても一人で書籍の執筆をしていると、執筆するほうに集中して、文章の確認作業がおろそかになったりします。 自分の代わりに随時内容をチェックしてくれる人がいたら、どれだけ助かることかと思います。

しかし、それもかなわない場合は、せめてあらかじめ予防線を張っておくことも大切です。 特にプログラムでのチェックは苦も無く確認してくれるので、ビルドするたびにチェックしたらよいと思います。

もしかすると技術同人誌を書いている多くの人が、文章校正ツールの「textlint」や、表記揺れのための「prh」を使っているかもしれません。 使っていない方は、後悔先に立たずですので、使うことを強くおすすめします。

ちなみに、この記事は私のやらかしちゃった話が元になっているわけですが、「本番環境でやらかしちゃった人 Advent Calendar 2019」というものもあって、興味深い記事もありますのでよろしければどうぞ。

技術同人誌の表紙をフリーソフトのGimpとImageMagickで作る

最近は、技術書典技術書同人誌博覧会などが定期的に開催されるようになったので、技術書同人誌を作って頒布したいかたも増えているようですね。

かく言う私も、技術書典7で書籍『RustではじめるOpenGL』を作成して、多くの人たちに頒布することができました。

技術同人誌を作るとき、本文の推敲に頭を悩ませることは多いのですが、本の体裁を整える上で、表紙を準備することも忘れがちですが大切な作業です。

たいてい表紙を作るときは、印刷所が指定するテンプレートをもとに作り始めていきますが、このファイル形式がPhotoshopのPSD形式だったり、EPS形式だったりすると、「やっぱりPhotoshopが必要なのか……」とPhotoshopの月額課金を始めることになったりします。

しかし、技術同人誌の表紙はフリーソフトで作れます!

現に私の書籍『RustではじめるOpenGL』も、フリーソフトで作りました。

f:id:toyamaguchi:20191116175326j:plain

本記事は、その方法を簡単に説明していきます。

ちなみに使用するソフトは、掲題の通り、GimpImageMagickを使っていきます。

テンプレートのダウンロード

私は印刷所として日光企画に印刷をお願いしました。バックアップ印刷所の一つなので、いろいろ特典もあります。

日光企画 - 【トンボダウンロード】オフセット用

Webページのタイトルには「オフセット用」と書いてありますが、オンデマンド印刷としてこのテンプレートを使わせていただきました。

B5版のEPS形式をダウンロードします。

テンプレートの読み込み

テンプレートの画像ファイルをGimpで開いてみましょう。 ここではEPSファイルをGimpに読み込ませてみます。

GimpはEPSファイルを読み込むと、その解像度を質問してきます。 というのも、EPSファイルは拡大縮小しても大丈夫なベクター形式だからです。 これを固定の画像サイズにするために解像度を入力します。

日光企画 - FAQよくある質問(オフセット編)

フルカラー原稿の場合は350dpiとなります。 色刷り原稿の場合、グレースケールは350dpi~600dpi、モノクロ2階調は600dpiを推奨しております。

この記述に従って、解像度を350に変更します。

f:id:toyamaguchi:20191116160807p:plain
解像度の変更

ちなみに、この数値の単位はppi (pixel per inch)のようで、dpi (dot per inch)と違います。 おそらく、ディスプレイにおける表示ピクセルと、印刷におけるドットの数という違いがあるのだと思いますが、大きな誤差はなさそうなので、これでいきます。

実際に印刷される領域を知る

本は、そのページ数によって背表紙の幅が決まります。

さきほどのトンボダウンロードのページの中に、「背幅の計算方法」について説明しているところがあります。

表紙込み総ページ数 × 本文用紙の厚さ = 背幅 (mm)

表紙込み総ページ数を、例えば100ページとします。 本文用紙の厚さは材質によって異なりますが、特に凝らなければ「上質紙90kg」というものになります。

これを踏まえて計算すると、

100枚 × 0.063mm/枚 = 6.3mm

だいたい6.3mm分です。

その場合は、この画像のように、中心から表紙側方向に約3mm、裏表紙方向に約3mm、合計約6mm程度の領域が背表紙になります。 (3mm目盛りが背表紙の領域の境界線です。)

f:id:toyamaguchi:20191116161701p:plain
背表紙の幅

また、そのほかの境界線をすべて考慮すると、次のような境界線が引けます。境界線をつけるまえと、つけたあとで比較してください。

f:id:toyamaguchi:20191116163014p:plain
テンプレートに境界線をつける前

f:id:toyamaguchi:20191116163105p:plain
テンプレートに境界線をつけたあと

これで、表紙の領域がわかりました。 あとは皆さんの好みの表紙を作っていけばOKです!

作業中の画像は、Gimpのファイル形式であるXCF形式で保存し、最終的に完成した際にはPNG形式でExportしました。

ImageMagickによるRGBからCMYKへの変換

通常、パソコンはRGB (レッド、グリーン、ブルー)の色要素によって、画面に画像を表示しています。 しかし、印刷業界ではCMYK (シアン、マゼンタ、イエロー、ブラック)の色要素を使って、インクを紙にくっつけて、印刷しています。 そのため、表紙の画像データもRGB形式からCMYK形式に変換する必要があります。

特にこの時点で、やむを得ずPhotoshopIllustratorの力を借りて、変換する人が多いのではないかと思います。 しかし、我々、ITエンジニアは便利な画像変換コマンドを知っているはず。

そう、ImageMagickです。

ImageMagickは、画像変換のための便利なコマンドがいくつか含まれたパッケージです。 今回はその中でも、convertコマンドによってRGBのPNG形式をCMYKのTIF形式に変換します。

convert before.png -colorspace CMYK after.tif

日光企画が推奨しているデータの保存形式のリストが、こちらのページにあります。

FAQよくある質問(オフセット編) - データの保存形式は何にすれば良いですか?

私はPhotoshopを持っていないので無理にPSD形式で保存せず、TIF形式でお願いしました。

初めての印刷だったので、実際に日光企画のお茶の水店に行って、データをお渡ししましたが、データ形式に関しては問題なかったようです。

RGBからCMYKに変換したことによる色の変化

このようにImageMagickを使うことで、簡単にCMYK形式の画像に変換することができましたが、実際に印刷するとモニタで見ていたときよりも少し暗い印象の仕上がりになることがあります。

これはImageMagickのせいというよりも、RGBからCMYKに変換したときによくある現象です。

この対策のために、少しだけ明るめに作っておくと良いです。 特にビビッドな赤や青の色を表紙のメインカラーに使っている場合は、要注意です。 「RGB CMYK変換」などでGoogle画像検索して、どの程度の変化があるのか確認してみましょう。

真っ黒な表紙についての補足

私の作った『RustではじめるOpenGL』は、真っ黒な表紙が特徴的です。

ImageMagickで画像変換したデータを日光企画に持っていったところ、受付のお姉さんに黒色の値に関しての注意を教えてもらいました。

RGBでの黒色の表現は(0, 0, 0)で、これをImageMagickで変換するとCYMKでの表現は(0, 0, 0, 255)となるようです。

このデータを印刷しても十分に黒いのですが、このデータよりももっと真っ黒になれる黒表現があるというのです。

それは、(100, 100, 100, 255)や、もしくは(255, 255, 255, 255)のような黒表現です。

実際にググってみると、CYMKのKだけで塗った黒をスミベタ、「C:40%, M:40%, Y:40%, K:100%」をリッチブラック、すべて100%にした4色ベタという種類があるそうです。

私は、日光企画の受付で(0, 0, 0, 255)の黒を即座に4色ベタに変換させるノウハウを持ち合わせていなかったので、そのまま入稿させていただきました。

ただ、実際にできあがったものを見てみても、「黒」としか認識できないくらい黒でした。

f:id:toyamaguchi:20191116175326j:plain
実際の表紙

特にこだわりがなければ、CYMKのKだけで塗ったスミベタの表紙でも問題なさそうです。

まとめ

このように、技術同人誌の表紙はフリーソフトでなんとかなります。

もしPhotoshopを使うことが高い障壁になっているかたがいたら、GimpImageMagickをおすすめします。

書籍『RustではじめるOpenGL』の内容漏れと更新のお知らせ

技術書典7で頒布いたしました『RustではじめるOpenGL』に、1ページ分程度の内容漏れが見つかりましたので、お知らせいたします。

該当箇所は、「第2章 SDL」の中にある「2.1.3 Windowsの場合」の内容が抜けておりました。

メインで利用しているPCがLinuxであったため、記事データがすべてLinux上にあったのですが、Windowsの内容を記事にする際に、いつもとは別ファイルに書いていたため、マージし忘れていたようです。

大変申し訳ありませんでした。

実際の内容

「2.1.3 Windowsの場合」では、マルチメディアライブラリである「SDL」をWindows環境で使えるように準備をします。

次の内容が、実際に書かれるはずであったものです。

2.1.3 Windowsの場合

Windowsでsdl2 crateを使うために、SDLのライブラリをインストールする必要があります。

このインストール方法については、上記のsdl2 crateのREADME.mdにも書かれていますが、本書でも解説していきます。

SDL公式サイトの中にあるダウンロードページ(https://www.libsdl.org/download-2.0.php)から、開発向けライブラリ(Development Libraries)のSDL2-devel-2.0.x-VC.zipをダウンロードします。

  • SDLの64bit向け開発ライブラリのあるディレクトリ:
    • SDL2-devel-2.0.x-VC\SDL2-2.0.x\lib\x64\

圧縮ファイルを展開したあと、上記の64bit向け開発ライブラリのあるディレクトリの中にあるファイルをすべてコピーし、次のディレクトリの中へペーストします。

  • ライブラリのインストール先:
    • C:\Users\USER_NAME\.rustup\toolchains\CURRENT_TOOLCHAIN\lib\rustlib\CURRENT_TOOLCHAIN\lib

上記の「USER_NAME」と書いてあるところには、あなたのアカウント名を入れてください。 また、「CURRENT_TOOLCHAIN」と書いてあるところが2個ありますが、現在利用しているRustのツールチェイン名を入れてください。

ツールチェインの調べ方は、「rustup show」コマンドで知ることができます。 64 bit Windowsで、Visual Studioを使っている場合、ツールチェインの名前は、x86_64-pc-windows-msvcになるはずです。

ファイルのコピーが完了したら、Windows上でRustからSDL2を使う準備ができました。

ダウンロード版コンテンツの更新

現在、BOOTHにて『RustではじめるOpenGL』のPDFのダウンロード(DL)版コンテンツを販売しております。

toyamaguchi.booth.pm

PDFファイルに関してはすでに内容を更新し、v1.1.0をアップロードいたしました。

すでにダウンロードした方も、新しいバージョンのPDFをダウンロードできるはずです。

紙版に関しては更新ができないため、このブログ記事を合わせて読んでいただけるとありがたいです。

改めて、本書に内容漏れがありましたことをお詫びいたします。

技術書典7にて100冊以上頒布達成しました

9月22日(日)に開催されました「技術書典7」にて、『RustではじめるOpenGL』という書籍を頒布させていただきました。

f:id:toyamaguchi:20190925224400j:plain
技術書典7のテーブルの様子

無事に書籍も現地に配達され、多くのお客様に書籍を届けることができました。

我々のテーブルに来てくださった皆さん、Web上のページを確認してくださった皆さん、大変ありがとうございました。 また、技術書典の運営に携わっていた皆さん、素晴らしい本に仕上げてくださった印刷所の日光企画さんには、感謝の気持ちでいっぱいです。

私は夢・目標の一つとして、自分の本を出版してみたいという気持ちがあり、技術書典ではそれが叶えられたことをとても嬉しく思っています。

この貴重な体験で得たことを多くの人に共有するため、この記事にまとめていきます。 私はサークル初参加、技術同人誌の製作も初体験でしたので、これから参加しようとしている皆さんの参考になれば幸いです。

頒布状況と後払いシステムの使用頻度

全部で150冊印刷し、そのうち117冊の書籍を頒布することができました。 余った書籍は、自分の手元に持っておきたい分を除いて、会場からオンラインショップのBOOTHへダイレクト入庫しました。

売れた117冊のうちで、44冊が後払いシステムによる購入でした。 かなりの割合の人たちが後払いシステムを使っていたので、必須ツールと言っても過言ではないでしょう。

サークル被チェック数の推移

サークル運営者としては、書籍の印刷数を決定することは死活問題です。 たくさん頒布したい気持ちもありますが、余ってしまったら管理が大変です。

印刷数を決定するための大切な要素として、サークルの被チェック数があります。 技術書典のサークルチェックのページ上で一般参加者がサークルをチェックすると、サークル運営者たちが被チェック数を確認することができました。

毎朝、早朝の5:30頃にサークルの被チェック数を確認していたところ、次のような推移となりました。

  • 2019-09-13: 41
  • 2019-09-14: 43
  • 2019-09-15: 45
  • 2019-09-16: 46
  • 2019-09-17: 54
  • 2019-09-18: 59
  • 2019-09-19: 73 (Twitterの推し祭りの効果)
  • 2019-09-20: 81
  • 2019-09-21: 89
  • 2019-09-22: 121 (技術書典当日の朝)

f:id:toyamaguchi:20190926204328p:plain
技術書典7におけるサークルの被チェック数

Twitterで告知を行ったときと、開催直前に多くの人がチェックリストに追加してくれたようです。 おおよそ同じ冊数くらい売れたように思っています。

売れ行きの時間推移と考察

まず、午前中のほうが、たくさん本が売れました。 やはり事前に買うものをチェックして、確実にゲットするためにチケットを買って来場してくれているので、午前中の参加者は買う気に満ちています。 サークル側としてもすごく良いスタートダッシュが切れて、「すごい!我々の本が売れていく!」と、お隣さんと感動しておりました。

ところが、午後になると、売れるスピードが遅くなっていきました。 書籍が入っていたダンボールも一箱目は早い段階で空いたのに、そこからなかなか減っていきませんでした。

我々の書籍の扱っているテーマがRustで、しかもOpenGLの3Dプログラミングなので、かなりニッチです。 ソフトウェア開発の一般的な話題や、Web開発のように開発者人口の多いジャンルでしたら、初見でも興味を持ってくれる人が多かったかもしれません。

ただ、逆に「Rust×OpenGL」のようなニッチなテーマでも、内容をしっかり書いて、表紙を作り込み、きちんとした本の体裁にまとめれば、100冊は売れるのだろうという肌感覚を得ることができました。 せっかくの技術書典ですし、普通の書店には並ばないような、ニッチな内容の本を書いてみたいですよね。

ただ、どんな書籍でも、Twitterやブログで宣伝することはとても大切です。 知ってもらえて、初めて買う物リストに追加してくれます。 そうして、そのリストを元に、当日テーブルに来てくれることになります。 宣伝・告知はしたほうが良いです。

書籍作成のノウハウ

今回、初めてRe:VIEWを使った版組を行いました。 その際に、TechBooster製の技術同人誌に特化したテンプレートを書籍執筆の土台として使わせていただきました。

本当はLaTeXが好きだったので、そちらを使おうかと思っていました。 しかし、「トンボ」「ノンブル」って何?という状態からのスタートでしたし、印刷用PDFも電子書籍版PDFも同時に作成できる利点もあったので、かなり早い段階で上記のテンプレートに切り替えました。

実際のところ、自分のコーディングするスピードはある程度把握しているものの、文章を執筆するスピードがわからないために、完成するのか不安になる場面もありました。 その上さらに、LaTeXでのノンブルの実現方法を悩んでいたら、かなりツラい気持ちになっていたはずなので、早めに切り替えていて正解でした。

このような書籍作成のノウハウは、「技術書典」「Re:VIEW」などのキーワードで検索すると見つけることができると思います。 追々、技術書典対策のために私が行ったことを、ブログの記事にしていくつもりですので、乞うご期待です!

BOOTHからの販売開始

技術書典7では30冊ほど書籍が余りましたので、会場からダイレクトに、オンラインショップのBOOTHに送りました。

toyamaguchi.booth.pm

もうすでに入荷待ちをしてくれている方がいらっしゃるようです。 気になる方は「入荷お知らせリクエスト」という機能を使うと、入荷された際にメールを受信できるようです。

BOOTHから購入できるのは、紙版とPDFのダウンロード版(DL版)です。 セットはありませんので、ご了承ください。

まとめ

最後にもう一度、今回テーブルに来てくださった皆様には心より感謝いたします。

また、新しい書籍を頒布できる準備が整いましたら、技術書典やオンラインショップなどで公開したいと思っています。

技術書典7にて『RustではじめるOpenGL』を頒布します

来る2019年9月22日、サンシャインシティ 文化会館ビル 展示ホールで開催される技術同人誌イベント「技術書典7」にて、書籍『RustではじめるOpenGL』を頒布いたします。

f:id:toyamaguchi:20190907231432p:plain:w300
『RustではじめるOpenGL』表紙
f:id:toyamaguchi:20190907231520p:plain:w300
『RustではじめるOpenGL』裏表紙

書籍の情報

  • 書籍名: RustではじめるOpenGL
  • サイズ: B5版
  • ページ数: 92ページ
  • 価格: 1000円
  • サークル名: Team Jackalope (リンク)
  • 販売ブース: 「こ32D」(池袋サンシャインシティ 文化会館ビル 展示ホール 2F ホールD) (マップ)

書籍執筆に寄せて

本書は、プログラミング言語のRustでOpenGLを扱うための入門書です。

私はこれまで、OpenGLを使ってネットワーク上のデータの流れを3Dで可視化させるソフトウェアを作成してきました。 まったくOpenGLを知らないところからスタートして、様々なところにつまづきながら、4年間で多くのことを学ぶことができました。 本書はその集大成として、入門者向けの内容をまとめたものです。

Rustを使うメリット

本を執筆していることを周りの人に伝えると、よく「なぜRustからOpenGLを使うのですか?」という質問を受けることがありました。 本書では、RustからOpenGLを使ったときの3つのメリットを挙げています。

  1. 処理速度が速い
    • C/C++にも負けない処理速度を出せるRustならば、多少の処理でも60 FPSを維持することができるはず。
  2. ガベージコレクションがないので、画面がカクつかない
  3. モダンなプログラミング言語が持つ、便利な特徴・環境を使用できる
    • 様々なプラットフォームでの動くバイナリファイルが簡単に作成できたり、新しいライブラリの導入が楽だったりするのは、モダンなプログラミング言語ならではです。

これらのメリットもあって、RustからOpenGLを使う方法を執筆しようと思いました。

GUIからパラメータを操作できるサンプルコード付き

また、本書の特徴的なところとして、OpenGLに簡単にGUIを追加できるライブラリ「Dear ImGui 」(通称、ImGui)を導入しているところが挙げられます。 もともと、OpenGLチェックボックスやスライダーなどのウィジェットを追加するのは、とてつもなく面倒です。初心者には無理です。 しかし、このライブラリを導入することで、OpenGL特有のパラメータをON・OFFできるGUIを簡単に追加することができます。

例えば、照明の位置や強さを変えたり、カメラの視点を変えたり、ワイヤーフレームに切り替えたりするような作業をGUIから操作することで、より直感的にその効果を把握できるようになります。

次のスクリーンショットは、書籍のサンプルコードを実装すると動作するサンプルアプリです。

f:id:toyamaguchi:20190908095847p:plain
3Dオブジェクトを制御するGUI

LinuxでもWindowsでも動作可能な独自OpenGLコンソールが作れる

本書のプログラムが採用しているフレームワークとして、SDLというものがあります。 これは多くのゲームにも採用されている、クロスプラットフォームなマルチメディアフレームワークです。 また、OpenGLも多くの環境で動作する3DプログラミングのためのAPI規格です。 そして、さらに、それらを呼び出すRust自体も、様々な環境で動作できるプログラムをコンパイルできます。

これらをすべて合わせると、自分だけのクロスプラットフォームOpenGL製コンソールが作成できるのです。(バイナリファイルは各プラットフォームごとに必要です。)

例えば、データベースから得た情報を元にグラフをぐいぐい動かしたり、映画『アイアンマン』に出てくるカッコいいビジュアルのコンソールを作り出せます。

f:id:toyamaguchi:20190908105031p:plain
Dear ImGuiのサンプル画像

どうです。ワクワクしませんか!

私はカッコいいGUIを作りたくてしょうがないです!

そんな情熱から本書を執筆いたしました。

紙版・電子版

紙版は技術書典7で150部頒布し、残った場合はBOOTHに委託しようと考えています。

また、電子書籍版もこちらに公開します。電子書籍版は部数に制限はないです。

toyamaguchi.booth.pm

現在は、準備中ですが上記のリンクの場所に公開する予定です。

書籍の表紙について

先日、書籍の表紙の完成をTwitterでつぶやいたところ、多くの人から「いいね」をいただけましたので、この表紙について少し解説いたします。

タイトルデザインの方針

f:id:toyamaguchi:20190908084446p:plain:w300
表紙のロゴ

私は大学時代の美術サークルに入っていたこともあり、このようなデザインの作成はすごく好きです。 せっかく自分の書籍を作るのだから、表紙を凝らないでどうする、という気持ちから、いろいろ悩みました。

OpenGLがテーマでしたので、よく見るポリゴンスタイルの背景壁紙のような案や、立方体や正八面体をモチーフにした案なども考えていました。 いろいろ考えていた中で、OpenGLのシェーダの中でも、わざと画面にノイズを加えた表現方法である「グリッチ」のアイデアが浮かびました。 しかし、グリッチの度合いを強くし過ぎると視認性が悪くなるので、グリッチ効果をより抽象的にして、グリッチの度合いを弱めた表現に変更しました。

フォント

また、画像の中で使用させていただいたフォントは、「851ゴチカクット」です。 昔の松下電器の「ナショナル」の文字にインスパイアしてできたフォントで、このカクカクしたところが近未来的でポップな印象を与えてくれます。 素晴らしいです。ありがとうございます。作者に感謝します。

f:id:toyamaguchi:20190908102114p:plain
851ゴチカクット

フォントが与える印象は大きいです。 最近のコミックやライトノベルの表紙に使われているロゴを見てもわかりますが、コミカルでポップなストーリーと、世界観が確立されたシリアスなストーリーとでは、使っているフォントの傾向が違います。 自分の書籍の印象をどういう雰囲気にするのかを考慮して、フォントをしっかり決めましょう。

私のやり方としてはそれほど難しくなく、「日本語 フリーフォント」などで検索して、良さそうなフォントの画像をダウンロードします。 その中から、最も表紙に似合うフォントを選んでいます。

ただ、すでにお気に入りのフォントというのもあって、今回はチームロゴと裏表紙の英語ロゴで使用しました。

ちなみに、チームロゴで使っているフォントは、丸い文字がかわいい「Comfortaa」です。

f:id:toyamaguchi:20190908091744p:plain:w200
チームロゴ

裏表紙の英語ロゴは、サイバーな雰囲気を与えてくれる「Orbitron」です。

f:id:toyamaguchi:20190908092821p:plain:w400
裏表紙のロゴ

目次

本書の内容を箇条書きでここに記しますので、参考にしていただけたらと思います。

  • ギャラリー
  • はじめに
    • 想定読者
    • 使用するライブラリとプラットフォーム
    • ソースコードのダウンロード
    • 免責事項
  • 第1章 開発環境の準備
  • 第2章 SDL
    • 2.1 準備
      • 2.1.1 Cargo.tomlの編集
      • 2.1.2 Linuxの場合
      • 2.1.3 Windowsの場合
    • 2.2 プログラムの作成
      • 2.2.1 ソースコードの全体
      • 2.2.2 初期化
      • 2.2.3 ウィンドウ
      • 2.2.4 キャンバス
      • 2.2.5 イベントループ
    • 2.3 プログラムの完成
  • 第3章 OpenGL
    • 3.1 準備
    • 3.2 プログラムの作成
      • 3.2.1 ソースコードの全体
      • 3.2.2 初期化
      • コラム : OpenGL 3.1
      • 3.2.3 OpenGLによる描画の仕組み
      • 3.2.4 シェーダ
      • 3.2.5 Vertex Array ObjectとVertex Buffer Object
      • コラム : ソースコードの整形を回避する
      • 3.2.6 三角形の描画
  • 第4章 Dear ImGui
    • 4.1 準備
    • 4.2 プログラムの作成
      • 4.2.1 初期化
      • 4.2.2 イベント処理
      • 4.2.3 ウィンドウの作成
      • 4.2.4 ウィジェットの生成
    • 4.3 プログラムの完成
    • 4.4 効果的な使い方
    • コラム : 自分専用プログラムの楽しみ
  • 第5章 3Dオブジェクト
  • 第6章 テクスチャ
    • 6.1 準備
    • 6.2 プログラムの作成
      • 6.2.1 ソースコードの全体
      • 6.2.2 テクスチャの準備
      • 6.2.3 頂点座標、法線ベクトル、テクスチャ座標
      • 6.2.4 テクスチャと照明を追加したシェーダ
      • 6.2.5 ユニフォーム変数
      • 6.2.6 テクスチャの描画
    • 6.3 プログラムの完成
    • 6.4 光の効果
  • おわりに
  • 著者紹介

最後に

技術書典7は9/22 (火)にて、サンシャインシティ 文化会館ビル 展示ホールで開催されます。

他にも面白い書籍がたくさんありますので、サークルリストで確認してから足を運ぶと良いと思います。

techbookfest.org

また、私のサークルはこちらになります。

techbookfest.org

RustのCrate調査 (better-panic)

Rust製の実行ファイルが異常終了した際に出力されるスタックトレースを、見た目の良いものに置き換えてくれるcrateを発見し、さっそく使ってみました。

crateの名前は「better-panic」です。 作者はPythonのflaskで有名な「mitsuhiko」ことArmin Ronacher氏。

github.com

コンパイラのバージョンは「rustc 1.35.0」、「better-panic = "0.1.1"」で試してみたいと思います。

better-panic適用前

例えば、次のようなソースコードを用意して、実行したら必ずpanicになるようにします。

fn main() {
    println!("Hello, world!");
    panic!("Encounter Panic!");
}

通常ですと、次のようなエラーメッセージが表示され、環境変数としてRUST_BACKTRACE=1をセットしないといけないな、となります。

Hello, world!
thread 'main' panicked at 'Encounter Panic!', src/main.rs:3:5
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

環境変数をセットしてから再び実行した結果が、次の内容です。

Hello, world!
thread 'main' panicked at 'Encounter Panic!', src/main.rs:3:5
stack backtrace:
   0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
             at src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:39
   1: std::sys_common::backtrace::_print
             at src/libstd/sys_common/backtrace.rs:71
   2: std::panicking::default_hook::{{closure}}
             at src/libstd/sys_common/backtrace.rs:59
             at src/libstd/panicking.rs:197
   3: std::panicking::default_hook
             at src/libstd/panicking.rs:211
   4: std::panicking::rust_panic_with_hook
             at src/libstd/panicking.rs:474
   5: std::panicking::begin_panic
             at /rustc/3c235d5600393dfe6c36eeed34042efad8d4f26e/src/libstd/panicking.rs:408
   6: try_better_panic::main
             at src/main.rs:3
   7: std::rt::lang_start::{{closure}}
             at /rustc/3c235d5600393dfe6c36eeed34042efad8d4f26e/src/libstd/rt.rs:64
   8: std::panicking::try::do_call
             at src/libstd/rt.rs:49
             at src/libstd/panicking.rs:293
   9: __rust_maybe_catch_panic
             at src/libpanic_unwind/lib.rs:87
  10: std::rt::lang_start_internal
             at src/libstd/panicking.rs:272
             at src/libstd/panic.rs:388
             at src/libstd/rt.rs:48
  11: std::rt::lang_start
             at /rustc/3c235d5600393dfe6c36eeed34042efad8d4f26e/src/libstd/rt.rs:64
  12: main
  13: __libc_start_main
  14: _start

自分が作成したソースコード以外のスタックが、こんなにも積まれていたことに気づいた瞬間です。

全部出力されると、余計な部分も多く、自分のコードがどこなのかわからないですね。

better-panic適用後

プログラムの最初に、better_panic::install()を追加します。

fn main() {
    better_panic::install(); // add better-panic!
    println!("Hello, world!");
    panic!("Encounter Panic!");
}

これによって、置き換えられたスタックトレースがこちらになります。

f:id:toyamaguchi:20190621191837p:plain

カラフルになり、自分のソースコードの箇所だけスタックトレースが表示されるようになりました。

また、スレッド情報も書いてあるようなので、マルチスレッドのプログラムが異常終了した場合でも、どのスレッドで問題があったのかがわかるかもしれません。

実行した環境にソースファイルを見つけた場合、そのソーススニペットを表示してくれるそうです。

まとめ

今回はbetter-panicについて調査をしました。

GitHubにも書かれていますが、これはPythonのtracebackからインスパイアされて作ったそうです。

また、気になるcrateがあったら調査していこうと思います。