ぶていのログでぶログ

思い出したが吉日

mruby-commit-idを作った

github.com

かなりニッチだが、使用しているmrubyのコミットIDを示すグローバル定数としてMRUBY_COMMIT_IDとMRUBY_COMMIT_ID_SHORTを追加するmruby-commit-idを作った。 mrubyは日々開発が進められていてmruby-3.3.0の頃と今のmasterではbug fixされていたり機能が追加されていたりと結構違う。 私の作っているrfコマンドでは常に最新のmrubyを使っているため、今どのコミットIDのmrubyを使っているかが重要になる(とおもう)。 そのため、このmruby-commit-idを作ったという流れ。

使い方は簡単でbuild_config.rbなりに依存を追加してmrubyをビルドするだけである。

MRuby::Build.new do |conf|
  conf.gem github:'buty4649/mruby-commit-id'
-- snip --
end

実際に実行してみると以下のような出力になる。

$ ./bin/mruby -e 'puts MRUBY_COMMIT_ID'
f2dc44215c8207e8ba7c161fd699c55075a266e1

$ ./bin/mruby -e 'puts MRUBY_COMMIT_ID_SHORT'
f2dc44215

なお、コミットIDはgitの情報から引っ張ってくるため、.gitディレクトリがない場合*1はエラーになるので注意。

おまけ: C言語でマクロ定義オプションで定義した文字列をプログラム中で文字列として扱いたい

mruby-commit-idではコミットIDのソースコードへの注入にマクロ定義オプション(-D)で渡している。 mrbgem.rake的には spec.cc.definesなので spec.cc.defines << %Q(MRUBY_COMMIT_ID=#{commit_id}) みたいにしている。 これで、C言語のソースをコンパイルするとMRUBY_COMMIT_IDというマクロ定数でアクセスできる…のだが、文字列にしようと少し厄介なことが起こる。 具体的な例でしめすと、以下のようなコードはエラーになる。

#include<stdio.h>

int main() {
    printf("MRUBY_COMMIT_ID is %s", MRUBY_COMMIT_ID);
    return 0;
}
//=> $ gcc -DMRUBY_COMMIT_ID=f2dc44215c8207e8ba7c161fd699c55075a266e1 test.c
//=> test.c: In function ‘main’:
//=> <command-line>: error: ‘f2dc44215c8207e8ba7c161fd699c55075a266e1’ `(first use in this function)
//=> test.c:7:37: note: in expansion of macro ‘MRUBY_COMMIT_ID’
//=>     7 |     printf("MRUBY_COMMIT_ID is %s", MRUBY_COMMIT_ID);
//=>       |                                     ^~~~~~~~~~~~~~~
//=> <command-line>: note: each undeclared identifier is reported only once for each function it appears in
//=> test.c:7:37: note: in expansion of macro ‘MRUBY_COMMIT_ID’
//=>     7 |     printf("MRUBY_COMMIT_ID is %s", MRUBY_COMMIT_ID);
//=>       |                                     ^~~~~~~~~~~~~~~

なんで???っとなるのだが、冷静に脳内でマクロ展開すると以下のようなになる。

    printf("MRUBY_COMMIT_ID is %s", f2dc44215c8207e8ba7c161fd699c55075a266e1);

f2dc44215c8207e8ba7c161fd699c55075a266e1 はクォートされていないので(!)関数名またはマクロ名として解釈されるために、undeclared となりエラーになっている。 これを回避するには2つのやり方がある。 まず1つ目は文字列化演算子 (#)を使う方法である。 以下のようにquote(x)q(x)マクロを定義し、それを経由することでクォートされた文字列を生成する方法である。

#include<stdio.h>

#define quote(x) q(x)
#define q(x) #x

int main() {
    printf("MRUBY_COMMIT_ID is %s", quote(MRUBY_COMMIT_ID));
    return 0;
}

2つ目は-Dオプションに渡す文字列を二重にクォートする方法である。 具体的には -DMRUBY_COMMIT_ID="\"f2dc44215c8207e8ba7c161fd699c55075a266e1\""っとするとマクロ展開後にクォートされているために文字列リテラルとして扱われないのでエラーにならない。

参考

*1:https://mruby.org/downloads/ からダウンロードするなど