Terminal type set to 'unknown' gnuplot>と表示されている.
$ sudo apt-get install libqt4-devとして,gnuplotをもう一度configureからやり直します.
$ cd gnuplotのソースがある $ ./configure --prefix=... $ make clean ## 重要 $ make $ make installmake cleanは,すでに一度コンパイルしたものを消去するために必要です. これをせずにmakeとだけやっても,再コンパイルしてくれないことが多々あります. 本来はconfigureによってMakefileが書き換わったことを検知して, 再コンパイルしてくれるべき,という気がしますが,そうなっているかどうかは, パッケージを用意した人次第,というところがあります.あまり一般的に こうなっていると決め付けることは出来ません.
checking xxxxxxx ... noのように,ある機能が環境にそなわっていないことが判定された行を, 注意深く見る.
lib機能名-devなどという名前をしていることが多い.
$ apt-cache search libqtまたは
$ aptitude search libqt などとして,qt関係のパッケージ名を検索できる.
$ sudo apt-get build-dep パッケージ名とすることで,構築に必要なライブラリがインストールされるので, よくわからなかったこれを試すのも良い. ただしこの方法にも限界はある.盲信してはいけない.
チーム... が,gpaint-2 をビルドした時のあれこれを共有します. これも,ソフトの不出来をディスってすませるのは簡単ですが, 自分がソフトを開発する立場に立って考えると, 「他の人の環境で動かない」ということは,大いにありそうなわけで, これを逆の目線から見ると「ちょっとした環境の違いで動かない」 ときに,そこで諦めてしまう人から, 修正して動かせるようになる人へのステップアップということも出来ます. 他のソフトでも似たようなことが起きる可能性は大いにあるので, 一般的な教訓として共有しておきます.
configureとmakeを行うと,以下のエラーに遭遇します.
drawing.c: In function ‘drawing_prompt_to_save’: drawing.c:432:69: error: ‘GTK_RESPONSE_DISCARD’ undeclared (first use in this function) gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_DISCARD,GTK_RESPONS
さて今回は,configureが失敗するのではなく,コ ンパイルに失敗するわけですが,どうしたらよいで しょうか.もちろん今回も,万能公式などありません. 結局,本当にぶっ壊れたソースなのかも知れませんが, まがりなりにも配布しているコードですから, どんな環境でもコンパイルできない,というところまで 壊れたコードということは考えがたく,やはり, 「環境依存」の問題である可能性のほうがはるかに高いです.
では何が行けないのかを突き止める方法は? 非常にしばしば功を奏する方法が,「教えて! Google先生」 という方法です.エラーメッセージをそのままGoogleに ぶち込んで,同じ問題を報告している人を探します. 今回の場合,「error: GTK_RESPONSE_DISCARD」 という文字列をGoogleに放り込むだけで, 同じ問題に遭遇している人の バグレポートとその後のやりとりを記したページが見つかります. あとは英語を読むだけです. なんてすばらしい(ここで,同じ問題に遭遇し,それを最初にバグレポートに あげた人にも感謝をしなくてはいけません.逆に言うと, 答えが見つからなかった時に,レポートをあげるということも, 重要な貢献だということです).
答えは,GTKという,gpaintが用いているライブラリのバージョンが, gpaintが書かれた当時よりもあがってしまい, 互換性のない変更が行われていた(結果としてこれまで 存在していたGTK_RESPONSE_DISCARDというマクロが削除された), というものでした.
この問題をクリアすると次にぶつかる問題は,最後のリンク時のエラーで,
... $ gcc -O0 -g -o gpaint-2 about.o brush.o callbacks.o canvas.o color_palette.o drawing.o file.o fill.o freehand.o gtkscrollframe.o image.o image_processing.o lasso.o main.o menu.o paste.o pen.o pixmaps.o polyselect.o rectselect.o print.o selection.o shape.o support.o text.o tool_palette.o ui.o -lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgio-2.0 -lpangoft2-1.0 -lpangocairo-1.0 -lgdk_pixbuf-2.0 -lcairo -lfontconfig -lfreetype -lpango-1.0 -lgobject-2.0 -lglib-2.0 /usr/bin/ld: image.o: シンボル 'cos@@GLIBC_2.2.5' への未定義参照です
エラーメッセージ: 「シンボル 'cos@@GLIBC_2.2.5' への未定義参照」 が最初の手がかりになります.これは要するに,cosという関数が 見当たらないというエラーで,cos関数を使うためには,-lm という フラグを与えて,libmをリンクしなくてはならないということです.
作業の進め方として,まずは それを手動で実行してみて同じエラーが出ることを確認 するのが効率が良いです.手動で,コマンドラインの最後に -lm を追加して, 実行してみます.
$ gcc -O0 -g -o gpaint-2 about.o brush.o callbacks.o canvas.o color_palette.o drawing.o file.o fill.o freehand.o gtkscrollframe.o image.o image_processing.o lasso.o main.o menu.o paste.o pen.o pixmaps.o polyselect.o rectselect.o print.o selection.o shape.o support.o text.o tool_palette.o ui.o -lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgio-2.0 -lpangoft2-1.0 -lpangocairo-1.0 -lgdk_pixbuf-2.0 -lcairo -lfontconfig -lfreetype -lpango-1.0 -lgobject-2.0 -lglib-2.0 -lm
首尾よく行きました.ということで残された問題は, どうやってmakeに,-lmというオプションを付けさせるか,です. とりあえずストレートな考え方は,Makefile中で, このコマンドを実行しているらしきところを突き止め, そこを書き換えるということになります. ただし,Makefile自身,configureによって生成されるものなので, 実際にはMakefileを書き換える方式はうまくありません. Makefileは実は,Makefile.in というファイルをテンプレートとして生成されています (実はMakefile.in自身,人間が手で書いたものではないという, かなり病的な世界なのですが,今の本題とは関係ないの飛ばします). なので,Makefile.inから探しても似たようなものは見つかります. 今回の場合,src/Makefile.inから以下のような関係しそうな行が見つかりました.
gpaint-2: $(gpaint_2_OBJECTS) $(gpaint_2_DEPENDENCIES) @rm -f gpaint-2 $(LINK) $(gpaint_2_LDFLAGS) $(gpaint_2_OBJECTS) $(gpaint_2_LDADD) $(LIBS)
この,
gpaint-2:つまり,このルールがgpaint-2 というファイルをターゲットとしている (gpaint-2を作るためのルールである)という点です(Makefileの読み方 については教科書の付録: makeの基本を参照して下さい).
これを見るとコマンドラインの最後に,
LIBS =という行が見つかり,LIBSは空文字列になっています. Makefile.inでは対応する行はどうなっているかというと,
LIBS = @LIBS@となっています.実はconfigureが, 右辺の@LIBS@という形をした文字列を, 環境変数 LIBS で置き換えるということをします. CFLAGSなどの変数が置き換わるのも同じ仕組みです. 実際,src/Makefile.inには次のような行もあります.
CFLAGS = @CFLAGS@ですので実は LIBS という変数も CFLAGS と同様,configure時に 環境変数として指定することで,Makefileに行き渡らせることができます. 結論として,configureを以下のように行えば良いということです.
$ LIBS=-lm CFLAGS="-O0 -g" ./configure --prefix=$HOME/gpaint_install
疲れましたが教訓は,
Update: ふと思いまし たが,なぜ我々だけ-lmが必要なのか,の理由は, 我々が -O0 でコンパイルしているからかも知れ ません.-O0だと,cos関数の呼び出しがライブラ リ関数への呼び出しになりますが,最適化レベル を上げると(無指定の場合も含め),cosなどの関 数は組み込み関数としてインライン展開されると いうことがよくあります.ためしに-O0なしで, configure, makeしてみると確かめられます(やっ てません).この問題が開発者の手元で見つから なかったのはそういう理由かも知れません.
mecabは,mecab本体をインストールしてから, 辞書をインストールするという2段構成になっているようですが, 辞書をconfigureするときに mecab-config がない, というエラーに遭遇します.
todo: エラーログを貼れ
解決策ですが,辞書のconfigure時に,
./configure --with-mecab-config=...というオプションで,mecab-configのありかを指定してあげます. mecab-configは,mecab本体をインストールしたのと同じ場所に見つかります. なので,/home/denjo/mecab_install にmecabをインストールしたのであれば,
./configure --with-mecab-config=/home/denjo/mecab_install/binとしてやればよいようです.
ここでも,一般的な教訓をまとめておきます.--with-mecab-config というオプションは,
./configure --helpで表示されます.何かのファイルがなくてconfigureに失敗したら, それを教えるオプションがないか,--helpを使って見てあげましょう, というのが覚えておくべき教訓です.
inkscapeをビルドしようとすると,おそらく大概の人は, 色々と依存しているパッケージがない旨のエラーが出ます. それは本質的には, ビルドしたgnuplotで窓が出ないのと同じ問題で, 異なるのはgnuplotのように,機能OFFのままコンパイルするということは できないため,configureがエラーになることです.
なので,解決策も同じで,必要なパッケージを入れていくことです. 但し今回の場合,依存しているパッケージの量がかなり多いので, apt-get build-depを使うのが実践的です.
$ sudo apt-get build-dep inkscapeとして必要パッケージをどさっと投入すればうまく行くようです.
コードをいじって一発で動くなどということは事実上ありえないので, 多くの場合,施した変更を元に戻して試したい,ということが起こります. その時に本当に手でソースコードを元に戻すのは大変ですし, 間違いも起きます.そのうち何をやっているのかもわからなくなってくることでしょう. これをうまく管理する方法として, Cの #if ... #endif というディレクティブが使えます. 例えばソースコードに
#if FOO int foo() { ... } #endifと書いておいてコンパイル時に,
gcc -DFOO=1 ...としてコンパイルすれば, #if FOO ... #endif の間のコードが有効になり,
gcc -DFOO=0 ...とすれば無効になります. これをconfigureなどで CFLAGS="-DFOO=1 ..." または CFLAGS="-DFOO=0 ..." などとして与えれば, ある変更を有効・無効にしてコンパイルをすることが簡単になります. 詳しくは, 教科書の3.22 「より模範的なソースコードの変更の仕方」 を見てみて下さい.
if (getenv("HOGEHOGE")) { セットされている動作 } else { セットされていない動作 }環境変数をセットするには,コマンドを起動するときに,
HOGEHOGE=X ./your_program ...のようにします.こうするとgetenv("HOGEHOGE")が 0 でない ポインタ---具体的にはセットされた値へのポインタ(文字列)--- を返します. 上記のコードでは, HOGEHOGEがセットされているか否かだけを区別しましたが, 実際にセットされている値によって動作を変えるということも出来ます.
この方式の利点は,割合にお手軽であるということでです. ソースコード中のだいたいどんな場所にでも入れられます. 欠点その1は,タイプミスに(非常に)弱いということでしょう. getenvに渡す文字列や,コマンドラインでの文字列をタイプミスすると, 知らぬ間に予期しない方の動作をしてしまいます. その間違いを事前に防止したければ,
getenv("HOGEHOGE")のように,文字列を直打ちするのをやめて,
const char * ENV_HOGEHOGE = "HOGEHOGE";または
#define ENV_HOGEHOGE "HOGEHOGE"のように,どこかに一度だけ文字列の定義を書いておいて,
getenv(ENV_HOGEHOGE)のようにします.もしくは以下のように,getenvを呼ぶのは一度だけにして, どこかで変数に値を設定しておきます.
int VAL_HOGEHOGE = 0;
/* どこかで初期化 */ if (getenv("HOGEHOGE")) { VAL_HOGEHOGE = atoi(getenv("HOGEHOGE")); }
/* 使う場所 */ if (VAL_HOGEHOGE) { ... } else { ... }こうすると,VAL_HOGEHOGE などの変数が, ソースコードのどこからでも参照できるように, 適切なヘッダファイルを見つけてあげたり, なければ自分で作ってあげたりと, コードの変更量が多くなるというデメリットがあります. このコードの変更をするくらいならおそらく, コマンドライン引数を追加してあげるほうが, きれいと言えますし,タイプミスによる予期しない動作の 発生も防ぎやすいです(コマンドライン引数を間違えれば, そこでエラーになる).
いじったら動かなくなったプログラムも, やはりデバッガをうまく使うことで道が開けます. 「動かない」にも色々有りますが,典型例は以下の二つです.
(gdb) whereまたは
(gdb) bt ## backtraceコマンドによって,どのような関数呼び出しが積み重なって, その場所にたどり着いているのかを見ることができる. 例:
(gdb) bt #0 0x00007ffff632312d in poll () at ../sysdeps/unix/syscall-template.S:81 #1 0x00007ffff6949fe4 in ?? () from /lib/x86_64-linux-gnu/libglib-2.0.so.0 #2 0x00007ffff694a30a in g_main_loop_run () from /lib/x86_64-linux-gnu/libglib-2.0.so.0 #3 0x00007ffff78c8447 in gtk_main () from /usr/lib/x86_64-linux-gnu/libgtk-x11-2.0.so.0 #4 0x0000000000419584 in main (argc=1, argv=0x7fffffffe4a8) at main.c:60これは, main が gtk_main を呼び出し,それが g_main_loop_run を呼び出し,それが何かよくわからない関数 (??と表示されている. 関数は /lib/x86_64-linux-gnu/libglib-2.0.so.0 というライブラリ中にあることはわかる)を呼び出し,それが poll という 関数を呼び出していることを示している.
ここから,
(gdb) upコマンドによって,今いる位置の関数呼び出し元を調べることが出来る. upコマンドを適切な回数繰り返し実行するとやがて, 自分がコンパイルした関数にたどり着く.今の場合, 4回upコマンドを実行すれば, main関数が,gtk_main を 呼び出す行を表示してくれる(これは,表示する場所を 変更しているだけで,プログラムの実行が進んだり, 戻ったりしているわけではない).
(上の状態からupを4回実行した時のEmacs内の表示) create_window(); => gtk_main(); debug("main() returning"); return 0;運が良ければこのようにして, segmentation faultと直接関連の有りそうな, ソースコード上の位置を発見することができる. 少なくとも,「segmentation faultの直前」 で何が起きていたのかを知ることができる. もちろんこの状態で p で指揮や変数の値を表示したり もできるし,必要ならば,その少し前にブレークポイントを 設定して再実行,などということができる.
ただしもちろん,segmentation faultで落ちた際の呼び出し元 に,直すべき場所があるとは限らない. ここで説明したのはあくまで,「最初の手がかり」を得る方法. 例えて言うならば,殺人事件が起きた時, 被害者がその直前にどこで何をしていたのかを探る方法である.
segmentation faultではないが,プログラムが実行時エラーを吐いて 終了,ということもよくある.assertion failureというのもその一種で, それは元はといえば,
assert(x == y);のような文を書くだけで,実行時にx == yが成り立っていなければ, エラーを表示して終了してくれるというものである. assertion failureであろうと,その他のお手製のエラーメッセージであろうと, 最終的には,_exit関数,abort関数などを呼んで終了する. よって,それらにブレークポイントを設定してから実行すれば, プロセスがいなくなる直前を捉えることができる. それをとらえたあとの要領は,segmentation faultの追跡と同じである.
MozcをMacOSでビルドしているチームでの出来事. build_mozc.pyを使ってデバッグ用のコンパイルをするも, gdbで実行可能ファイル(MozcRenderer)を実行すると, no debug symbols... と言われる. コンパイルはgcc/g++ではなくclang++という,LLVMという コンパイラツールチェインの中のコマンドで行われている.
解決策というか,回避策を先に書いておくと,gdbの代わりに, lldbという,LLVMツールチェインの中に入っているデバッガを使えば良い.
ここから先はいつものように,「この答えに至るプロセスが大事」 という考えのもと,どうやってそこに至ったかを記録する.
まず最初になすべきことは,実際にビルドが行われているコマンドラインを 直視することである.今回はよくあるconfigure; make ではなく, gyp とうツールが使われている.どうであろうともまずは,コマンドラインに, -g, -O0 など,自分が指定したオプションが行き渡っていることを確かめるのが 先決.今回実行されたコマンドを見てみると確かに,-O0, -g が渡っていたが, 一点,疑う可能性のある点として,.c を .o にコンパイルするときは, -O0 -g がわたっているが,リンク(最後に実行可能ファイルを生成するところ) 時には,-g がついていない,ということがわかった.
この時点でそれが問題だと確信できるわけでは全くないが (自分の経験上は,リンク時に-gをつける必要は,大概の場合,ない), よくわからないことが起きているときは,とにかく万全を期すことができれば それに越したことはない.ということで,リンク時にも -g をつけることにしたい.
この時の作業の仕方としてふた通りある.
次に,2.の方法として,gyp経由で,リンク時の オプションに-gを追加するという方針で試みる. gypの設定ファイルの中から関連しそうなところを探す (そもそも設定ファイルをいじる以外の方法がないのかもよく知らない) も,あまり安全にいじれそうな気がしない.
両者とも行き詰まったところで頭を冷やす. これまでにわかっていることは,
$ clang++ -g -c a.c $ clang++ a.oとし,無事 gdb がそのシンボル情報を読めるのかを調べてみる. するとなんということか, こんな単純なケースでもうまく行かないということがわかった. そしてなんと,リンク時に -g を付けてもなお,ダメである.
$ clang++ -g -c a.c $ clang++ -g a.oここまで単純化すると,そもそもclang++が吐いたバイナリが gdbでデバッグできていないのではないか,ということに気づくことができる.
Mozcのチームから,OS XでデバッグビルドのMozcをインストールしても, 起動中のプロセスにlldbでアタッチした際にアセンブリしか表示されないという報告を受けたため, その原因を調査した. 結論からいうと,Mozcのgyp設定ファイルがデバッグビルドであったとしても stripコマンドでシンボル情報を削除していたのが原因であった. 以下は,それが判明するまでの記録である.
デバッグシンボルが読み込まれないという問題を一つ取っても,複数の原因で起きる可能性がある.
問題解決に際して,これらの可能性を一つ一つ消去法でつぶしていくのが有力な方法である. 少なくともどの段階で既に問題が生じているかは, 中間生成ファイルを調べることで明らかになる. 例えばOS Xの場合は,dsymutilというツールを利用することで, デバッグシンボルの情報を格納した*.dSYMというディレクトリを生成できる. この際にデバッグシンボルがない場合は警告を出すという挙動をするので, これを利用する.
$ cd src/out_mac/Debug/Mozc.app/Contents/MacOS $ dsymutil Mozc warning: no debug symbols in executable (-arch x86_64) $
逆に,正常にデバッグシンボルが存在する場合は,何もメッセージが出ない.
$ clang++ -c a.cpp -g -O0 $ clang++ a.o $ dsymutil a.out $
上のMozcの実行ファイルはデバッグ情報を持っていないと分かったので, 次にコンパイル時とリンク時のどちらで削除されたかを考える. コンパイル時にきちんと"-g -O0"が付いているかは, 実行しているコマンドを確認すれば分かる. Mozcのビルドでは(ほぼ全ての)コマンドが表示されているので, clang++に渡している引数を確認することは容易である. 実際,コンパイル時には正常に"-g -O0"が渡っていることが分かった.
そこで,リンク時があやしいと考えて, 実行可能ファイルだけを削除することでわざとリンクだけを発生させてみることを試みた. そして,その際のコマンドを眺めると, リンクのコマンドが呼ばれた後にstripコマンドを呼び出していることが判明した.
$ ./configure [オプション...] $ make $ make installでいっちょう上がりという風になっているソフトが多いですが, 最近は, CMakeというものを使っているものもよくあります. こちらに遭遇したときの最小手順. (ちょっと嘘をついていたようなので10/8微修正しています)
$ cmake . # ソースは'.'にあるよ, という意味 # CMakeCache.txt というファイルが出来るので必要に応じて修正 $ cmake . # CMakeCache.txtを修正したらまたこれをやる $ make VERBOSE=1 # VERBOSE=1は必須ではないがコマンドラインを表示してくれるので推奨 $ make install
$ cmake -DCMAKE_INSTALL_PREFIX:PATH=/home/tau/doss/Remmina/instみたいにすることも可能. ただし長いコマンドラインを入れるよりは, CMakeCache.txtをいじるほうがむしろ気持ちが良い.
Linuxでは実行された関数を記録, 表示してくれるuftraceというツールがあることを紹介しましたが, ややこしいプログラムと組み合わせると不幸にして動かないということがままあるようです.
その原因を追求する代わりにもう少し機能を落としたプログラムを, 自作しているので本邦初公開w します. libitrace と命名しています. 使い方は上記リンクで表示されるREADME.md を見てください. 当初開発したあと uftrace を見つけて, 出る幕なしと思っていましたが, uftraceが失敗するケースが, わりとあるので思ったよりも出番があるのかも知れません.
uftraceでMuseScoreをトレースすると失敗してしまう ということがわかり, その理由もわからない (下記補足参照)ので, uftrace押しした罪滅ぼしにlibitraceを適用してみました. libitraceのビルドは終わっているとします.
手順 (唯一のポイントは -finstrument-functions というコンパイル時フラグ. -pg の代わり):
$ cd MuseScore $ cmake -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_CXX_FLAGS_DEBUG="-O0 -g -finstrument-functions" -DCMAKE_C_FLAGS_DEBUG="-O0 -g -finstrument-functions" -DCMAKE_INSTALL_PREFIX:PATH=/home/tau/tau_doss_2019/04musescore/MuseScore/inst --build=bld . $ make VERBOSE=1 $ make install VERBOSE=1
確認:
$ cd inst/bin $ ls mscore* musescore@ QtWebEngineProcess
libitraceで実行(/path/to/libitrace.so は libitraceをビルドした時に出来ている .so ファイルへのパスとしてください):
$ LD_PRELOAD=/path/to/libitrace.so ITRACE_INIT_STATUS=0 ./mscore
この状態ではまだ記録は開始されていません. mscoreが立ち上がってキー入力を受け付ける状態になったら記録を開始するために, 別の端末から, 同じフォルダに行って以下を実行 (itrace_log_XXXX.log は同フォルダにあるログファイルですので見つけて, 適切な名前に置き換えてください)
$ cd inst/bin $ /path/to/itrace.py start itrace_log_XXXX.log
この状態で記録したい動作を実行. 例えば キー入力を1文字行う ('a' を押すとか). なるべく記録を純粋にするために余計なことをせずにすぐに以下を実行
$ /path/to/itrace.py stop itrace_log_XXXX.log
これで記録が終了. 以下で表示します.
$ /path/to/itrace.py show itrace_log_XXXX.log path /home/tau/tau_doss_2019/04musescore/MuseScore/inst/bin/mscore src /home/tau/tau_doss_2019/04musescore/MuseScore/thirdparty/freetype/include/freetype/internal/ftcalc.h fun 187 292 FT_MulFix_x86_64 fun 187 8073 FT_MulFix_x86_64 ...
読み方
path /home/tau/tau_doss_2019/04musescore/MuseScore/inst/bin/mscore src /home/tau/tau_doss_2019/04musescore/MuseScore/thirdparty/freetype/include/freetype/internal/ftcalc.h fun 187 292 FT_MulFix_x86_64/home/tau/tau_doss_2019/04musescore/MuseScore/inst/bin/mscore という実行可能ファイルを生成するのにソースファイル /home/tau/tau_doss_2019/04musescore/MuseScore/thirdparty/freetype/include/freetype/internal/ftcalc.h が使われその187行目にあるFT_MulFix_x86_64 という関数が292回呼び出された, という意味になります.
罪滅ぼしに mscore を立ち上げて 'a' を一回だけ入力した際の 結果です. また, mscore を立ち上げてショートカットを編集して, OKボタンを押した瞬間(編集を終えたあと記録 start して, OKを押して, 直後にstop)の 結果です. これでそれらしい関数がわかるか? ケースバイケースでしょうが, キーを一回押しただけですから, 実行回数が少ない関数, 基本は一回しか実行されていないもの, に目をつけるべきでしょう.
ただしコンパイル時のログを見ていると, CMAKE_{C,CXX}_FLAGS_DEBUG="-O0 -g -finstrument-functions" でcmakeに渡したオプションがすべてのコンパイルコマンドに渡っていない 形跡があり, 見たいところが全て取れているのか不安です. 多数あるサブフォルダ内のファイルまで指定したオプションが浸透していない形跡があります(これは解決可能と期待される. 解決したらもう一度やってみます). 10/8 16:28 加筆 そのとおりでした. 本質的な対処かどうかはわかりませんが, CMAKE_{C,CXX}_FLAGS="-O0 -g -finstrument-functions" とすれば良いようです. 再コンパイルして取り直した結果がこちら ('a'を押した時, ショートカットを変更したあとOKを押した時). 明らかに今度は多すぎて目では終えないという問題がありますが, 呼び出し回数が1回のものに限定するとか, この後の料理の仕方は あると思われます.