本連載では以降、C++/CXによるWindowsランタイム・コンポーネント作成のケース・スタディとして、mruby(組み込みRuby)をWindowsランタイム・コンポーネント化する方法を考える。
mrubyは、MITライセンスで配布されている組み込み用途に特化したRuby処理系で、現時点ではGitHubを利用してソース・ファイルとして公開されている。mrubyは組み込み用ということで素の状態ではファイルIOに関する機能を持たない。またmrubyのソース・ファイルでは、あらかじめ標準入出力(通常はコンソールである)が利用できない環境への組み込み用の考慮もなされている。このため、Windowsストア・アプリへの組み込みは容易である。
mrubyのソース・ファイルは、C言語で記述されている。ただし一部の処理については、ビルド時にRubyのソース・ファイルをバイトコード・コンパイルし、バイトコードを組み込んだC言語のソース・ファイルとして組み込まれる。
mrubyをビルドすると、以下の実行ファイルとライブラリが生成される。
従って、mrubyをWindowsランタイム・コンポーネント化するということは、libmruby.libファイルをC++/CXでラップしたWindowsランタイム・コンポーネントを作成することである。
次回以降でこの方法を取るうえでの基本戦略とその実際を紹介していくが、その方法は、mrubyに限らず既存のCやC++のライブラリを組み込んだWindowsランタイム・コンポーネントを作成するときにも適用できる。
今回はまず、簡単にWindows用にmrubyのビルド方法を示す。
そして次回は、mrubyのような既存のCライブラリをWindowsランタイム・コンポーネント化するための方法を説明する。
本連載の最終回では、実際のコードを示し、どのようにWindowsランタイム・コンポーネント化すればよいかについて説明する。
mrubyのビルド方法
mrubyをビルドするには、Visual Studio 2012以外*2に以下のプログラムが必要である。
*2 本稿はWindowsランタイム・コンポーネントの作成についてのものなので、Visual Studio 2012が利用できることが前提である。
また、GitHubからGitを利用してソース・ファイルを入手するのであれば、Gitクライアントが必要となる。ただし単にスナップショットを取得するのであれば、その時点のリポジトリのzipファイルをダウンロードできる。どちらの方法でもよいので、ソース・ファイルを取得して、適当なディレクトリに展開しておこう。
mrubyをビルドするには、以下の手順を取る。
以上で、binディレクトリにmrbc/mruby/mirbの各実行ファイルが作成される。またライブラリのlibmruby.libファイルは、build\host\libディレクトリに作成される。
ただし、ここで生成したlibmruby.libファイルをそのままWindowsランタイム・コンポーネント化することはできない。
正確には、デバッグ・バージョンの作成には利用できるが、Windowsストアで配布する要件を満たさない。というのは、ファイルIOなどのCランタイムを利用しているからである。
この時点でlibmruby.libファイルを作成するために利用しているVisual C++のコンパイル・オプションを以下に示す。なお、この設定は、mrubyのソース・ファイルを展開したディレクトリの下にあるtasks\toolchains\vs2012.rakeファイルに組み込まれている。
/c /nologo /W3 /D_DEBUG /MDd /Zi /Od /RTC1 /DHAVE_STRING_H /DNO_GETTIMEOFDAY /D_CRT_SECURE_NO_WARNINGS
これらのオプションの意味を以下の表に示す。
オプション*3 | 意味 |
---|---|
/c | コンパイルのみを行う |
/nologo | 著作権情報をコンソールに出力しない |
/W3 | 警告レベルを3に設定する |
/D_DEBUG | _DEBUGを有効にする |
/MDd | デバッグ・バージョンのCランタイムDLL用にコンパイルする |
/Zi | デバッグ情報を生成する |
/Od | 最適化を無効にする |
/RTC1 | ランタイムエラー・チェック(スタック・フレームのチェックと未初期化変数のチェック)を有効にする |
/DHAVE_STRING_H | string.hファイルが存在することを示す |
/DNO_GETTIMEOFDAY | gettimeofdayシステム・コールが存在しないことを示す |
/D_CRT_SECURE_NO_WARNINGS | バッファ・オーバーフロー防御用関数への置き換え警告を無効にする |
mrubyのコンパイルで使用しているコンパイル・オプションの意味 |
*3 Visual C++のオプションについてはMSDNサイトの「コンパイラ オプション一覧 (カテゴリ別)」を参照のこと。
mrubyをWindowsランタイム・コンポーネントに組み込むためには、標準入出力の利用を抑制するための定義の設定と、上記のVisual C++のオプションからWindowsストア・アプリで利用できないAPIの呼び出しに関連するものを排除して、ライブラリをビルドすることになる。
なお、ビルド工程でのバイト・コンパイル用にmrbcの作成は必須であるが、mrbc用のmrubyのライブラリは標準入出力を利用する。このため、標準入出力を抑制したライブラリ生成用のビルドについては、クロス・コンパイル用のビルド設定をbuild_config.rbファイルに追加する。
以下に、Windowsランタイム・コンポーネントへ組み込むためのx86用のビルド設定コードと、ARM用のビルド設定コードを示す。
MRuby::CrossBuild.new('nostdio') do |conf| # 標準入出力抑制設定(x86用)
toolchain :vs2012 # vs2012用設定を元にする
conf.cc.flags = %w(/c /nologo /W3 /Zi /Od /DHAVE_STRING_H /DNO_GETTIMEOFDAY /D_CRT_SECURE_NO_WARNINGS /DDISABLE_STDIO /MD /DNDEBUG) # コンパイル・オプションを変更する。変更内容は本文参照
conf.build_mrbtest_lib_only
conf.bins = %w()
conf.gem 'examples/mrbgems/c_and_ruby_extension_example'
end
MRuby::CrossBuild.new('winrt-arm') do |conf| # 標準入出力制御設定(ARM用)
toolchain :vs2012
cl = 'cl.exe'
ENV['PATH'].split(File::PATH_SEPARATOR).each do |pdir| # 本文参照
if File.exist?("#{pdir}#{File::ALT_SEPARATOR}cl.exe")
cl = "#{pdir}#{File::ALT_SEPARATOR}x86_arm#{File::ALT_SEPARATOR}cl.exe"
break
end
end
conf.cc.command = cl
conf.cc.flags = %w(/c /nologo /W3 /MD /Zi /Od /DHAVE_STRING_H /DNO_GETTIMEOFDAY /D_CRT_SECURE_NO_WARNINGS /D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE /DDISABLE_STDIO /DNDEBUG)
conf.linker.flags << " /MACHINE:ARM"
conf.build_mrbtest_lib_only
conf.bins = %w()
conf.gem 'examples/mrbgems/c_and_ruby_extension_example'
end
x86環境でのARM用のVisual C++コンパイラ(cl.exe)は、x86用コンパイラを配置したディレクトリ直下のx86_armディレクトリに配置される。Visual C++ 2012をインストールすると[スタート]メニューに登録される[VS2012 ARM Cross Tools コマンド プロンプト]は、上記のディレクトリを(環境変数の)PATHに設定することで、ARM用コンパイラを実行するようにしている。ここでは、x86用のビルドと同じコマンド・プロンプトで実行できるように、スクリプト内で実行するcl.exeを変更している。
コンパイル・オプションは以下のように変更する。
オプション | 削除理由 |
---|---|
/D_DEBUG | デバッグを有効化しない |
/MDd | デバッグ・バージョンのCランタイムDLLは使用できない |
/RTC1 | ランタイムエラー・チェックのために、LoadLibraryやGetModuleFileNameなどの非サポートAPIを呼び出すため、削除する |
削除するコンパイル・オプション |
オプション | 追加理由 |
---|---|
/MD | リリース・バージョンのCランタイムDLLを利用する |
/DDISABLE_STDIO | mrubyの標準入出力利用を抑制する |
/DNDEBUG | デバッグ用トレースなどの出力を抑制する |
/D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE | ARM用コンパイル時に標準Cライブラリ関数を利用できるようにする |
追加するコンパイル・オプション |
ここで示したコードをbuild_config.rbファイルの末尾に追加し、再度「ruby minirake」コマンドを実行すると、クロス・コンパイルが行われる。クロス・コンパイルの結果は、buildディレクトリの、CrossBuild.newメソッドの引数として与えた名前のディレクトリ直下に作成される。
上のコードであれば、x86用ライブラリがbuild\nostdio\lib\libmruby.libファイルとして、ARM用ライブラリがbuild\winrt-arm\lib\libmruby.libファイルとして作成される。
C++/CXはネイティブC++なので、C++/CXで作成したオブジェクトファイルをここで作成したライブラリとリンクすることで、mrubyを利用できる。
例では、mrubyのソースをCコンパイラでコンパイルしている。これはmrubyのソースがC90規格で記述されているから可能なことだ。
オープンソースのCプログラムには、C99規格を前提として自由な位置での自動変数の宣言などを行っているものがある。この場合、Visual C++でビルドするには、ソース・ファイルに大量の修正が必要となる。
このようなソースをコンパイルするときに、/TPオプションを利用できる。
/TPオプションを付けると、ソース・ファイルの拡張子にかかわらずC++としてコンパイルされる。このため、C99で記述したCプログラムをエラーとせずにコンパイルできる可能性が高い。
なお、mrubyのようにあらかじめソース・レベルでstdio(標準入出力)の関数を利用しないように作られていないプログラムの場合は、独自のスタブを用意するか、またはマクロを使ってstdio呼び出しをスキップするように変更することになる。
今回はWindowsランタイム・コンポーネントを作成するうえでの注意点について見たうえで、C#でシンプルなWindowsランタイム・コンポーネントを作成し、それらの注意点がコードにどのように反映されるのかを見た。そして、既存コードをWindowsランタイム・コンポーネントとして再利用するための準備として、mrubyをコンパイルして、そのライブラリ・ファイルをWindowsランタイム・コンポーネントに組み込めるようにした。
次回は、このライブラリをWindowsランタイム・コンポーネント化する際にどのような方法があるか、そのメリット、デメリットなどについて見ていくことにしよう。
Copyright© Digital Advantage Corp. All Rights Reserved.