ぼくのDOSS奮闘記 〜LibreOfficeを手探ってみた〜 3/3

論理和論理積、そしてシェル関数

発表まで残された日数はあと授業2回分しかないが、流石に実装したものが1つだけというのは物悲しいので、追加で機能を実装することにした。まず自分たちで思いついた案としては「if文で用いるような論理和論理積を記号を用いて"&&", "||"のように書けるようにする」というものがあった。もう一つ、先生が出してくれた案として、「libreoffice上からターミナル上で入力できるようなコマンドを実行できるようにする(シェル関数の実装)」というものがあり、これは自分たちには思いつかなかった面白い発想かつ大して面倒な実装ではない気がしたので、圧倒的感謝をしつつ早速取り組むことにした。

しかし、ここで1つ目の実装に6日もかかってしまったことを思い出した。また軽い気持ちで取り組んだらこの前のようになるのではないかという不安が3人の頭をよぎった。

だが、この2つの実装、どちらもあっけなく1日で終わってしまった。やはり前回のCombinationでノウハウを習得していたことが大きかっただろう。

簡単に実装内容を説明しておこう。まずシェル関数の実装に関しては=SHELL("")という形でコマンドを入力する形をとり、ユーザーが入力した文字列を中でpopen関数に渡すという形をとった。出力結果を返り値として取得してセルに反映させる。ただ1つ注意点として、libreofficeの中で文字列はOUStringという独自に定義した型を用いている。したがって入力として受け取ったOUString型をString型に一度戻してからpopenに流し込み、得られた出力結果は再度OUString型に直してからセルに出力するようにした。こうすることで各種シェルコマンドを実行してくれる関数が実装できたわけだが、当然セキュリティ的にはとても問題がありそうな匂いがするので実際に有用かと言われると微妙なところである。

さて、もう一つの実装である論理和論理積についてもまとめておく。こちらは一見すると組み合わせ演算子の時と変更点は全く同じように見えるが、一つだけつまずいたポイントがあった。それは演算子が&&など2文字で表されることである。どういうことかというと、組み合わせ演算子の時と同じようにするとプログラムは1文字目の&を読んだ時点でそれを演算子(&1文字だと文字列結合の演算子)と認識してしまい、演算子のあとに再び演算子→構文エラーとなってしまうのである。そこで、文字列から演算子を切り出す条件を決めている部分を探す必要があり、ここを見つけ出すのに少し苦労した。結果的にsc/source/core/tool/compiler.cxxに「この記号がきたら次来るのはこの要素」と決定している部分があった。言葉ではわかりづらいので具体的にコードを載せておく。

$ /* & */ t[38] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;

 38は&の文字コードで、この行は&演算子の後にCharまたはWordSepまたはValueSepが来ることを示していて、演算子としてはここで区切られることを示している。ここを変えてしまうことも考えたが、とりあえず&& と ||の時のみ対応させようと考え、「文字&または|を読み取った時、次の文字を見てそれが&か|だったらそれも同一の演算子に含める」という記述を加えることでこの問題は解決した。

おわりに

こうして追加で考えた2つの機能の実装も完了し、なんとか発表までにまとめることができた。一人では到底ここまで到達することができなかったので、ひとえに他のメンバーや教授、TAの方々のおかげである。ここに書いたことは実際に進捗を生んだ部分をかいつまんで書いているだけであり、本当はかなり何もできない時間が長かったことを知っておいてほしい。

この実験を通して普段いじらないような大規模なソフトに取り組めたことは貴重な経験だったし、デバッガがとても有効な手段だということを改めて知ることができた。また、全体像がわからなくても適当に存在しそうな変数名でgrepを実行すると案外たどり着くものだなという地道な努力の素晴らしさにも気づくことができた。後からこの記事を読んだ人がこの授業がどのようなものなのか、どのような成果が得られるのかを少しでも吸収してもらえたら嬉しい。

ぼくのDOSS奮闘記 〜LibreOfficeを手探ってみた〜 2/3

makeを無事終えたこの日の実験。全員がcalcの起動までできていたのでここからデバッガを動かしてみることにした。gnuplotの時に行った方法とは異なり、LibreOfficeでは開発用にこのようなコマンドが用意されていた。

$ make debugrun

これを実行することでgdbデバッグモードに移行する。何事もなくデバッガが立ち上がったように見えるが実は./autogen.shの実行時のオプションとしてつけた--enable -dbgutil がないとおそらくできなかったと思われる。つけ忘れていたらまたmakeし直しだったと思うと恐ろしい。

とりあえず上手いやり方もわからなかったのでgnuplotでやったようにmainにbreak pointを置いてrunしたのだが、この方法は完全に間違っていた。gnuplot程度の規模なら起動時点から動作を追っていけばいずれ所望の部分にたどり着くのだが、LibreOfficeはそんなことを許してくれる規模ではなかった。TAさんのアドバイスによれば、具体的に何を変えたいのかを決めてからそこの変更をするための部分をピンポイントで見つけ出した方が良いとのことだった。

そう、何を変えるかをまだ決めていなかったのだ。メンバーで話し合った結果、関数を用いた計算の処理に少し変更を加えようという話になり、まずウォーミングアップとして=Combin(9,2)と表記する組み合わせの計算を=9C2のように演算子のように書けるようにしようと決めた。今考えると、これをウォーミングアップなどと言っていた過去の自分を殴りたい。

ここからの作業がひたすら泥沼だった。とりあえず+や-などの演算子の記号と処理の対応づけをしているファイルがあるはずなのでそれを探すことにしたが、いくら探しても見つからない。というか探す出発点がまるで思いつかない。とりあえず思いつく演算子関連のワードを色々とgrepで検索してみるがいまいち見つからない。GUIでセルを選択した時に新たなスレッドが立つのでこのスレッドから流れを追おうとも考えたがこの作戦もうまく行かなかった。また困ったことにルートディレクトリからgrepをかけてもファイル数が多すぎるため検索ができないのである程度フォルダに検討をつけてgrepするしかないのだ。幸い、Calc関連のファイルがscというディレクトリにまとまっていることに気づいたので少し探索はしやすくなったのだが、このディレクトリ内にあるだろうという先入観がまた発見の遅れを生むことになる。3人がかりでクサいファイルの捜索にあたっていたが、丸々2日間ほどは大きな進捗は生まれずただ時間だけが流れていった。

4日目あたりでようやく演算子の定義に関する重要なファイルをいくつか発見できた。まず /formula/inc/core_resource.hrcというファイル(hrcってなんの拡張子?)に記号と実行する演算の対応表を見つけた。具体的には下のような感じ。

 

{ "+" , SC_OPCODE_ADD },
{ "-" , SC_OPCODE_SUB },
{ "*" , SC_OPCODE_MUL },

 つまり、追加したい演算子の文字と、それに対応する何かを関連づけている。このSC_OPCODE_ADDなどを定義している部分を探すと、include/formula/compiler.hxxにこれらを定義している部分を見つけた。 

 

#define SC_OPCODE_ADD 50
#define SC_OPCODE_SUB 51

また、include/formula/opcode.hxxには演算子を識別するtokenをこの定数に結びつけていると思われる部分を見つけた。

 

 ocAdd = SC_OPCODE_ADD,
ocSub = SC_OPCODE_SUB,

 

ocAddなどのtokenを実際の計算では使用すると推測されたので、この3つを書き換えることで新たな演算子を登録することができるのではないかと考えた。

また、ScCompiler::CompileStringという関数で入力文字列から演算子に変換すること、ScInterpreter::Interpretという関数で実際に計算をする関数に飛ぶ(ocAddならScAdd関数など)ことを突き止めた。この辺りを新しい演算子に対応させれば早くも動作するようになるのではないかと少し希望が見えてきたところである。

さて、ここで少し別の問題が浮上していた。makeをすると途中で落ちてしまうのである。どうやらTestで落ちているらしいのだが、実行するごとに違う部分でエラーを吐いてしまうのでとても困っていた。TAさんに聞いても根本的な解決方法はわからなかったため、いっそテスト自体を実行しない方法を探したほうが良さそうと言われた。探してみるとすぐにテストなしのmakeを行う次のようなコマンドを見つけた。

 

$ make build-nocheck

この方法でmakeをしたところ正常にmakeが完了したのでとりあえずこれで先に進むことにした。結局最後までtestが通らない原因はわからなかった...

さて、演算子の実装の話に戻ろう。表示されるエラーなどから演算子の文字として用いようとしている'C'はセル番号や関数名の先頭の文字と重なることから不適切であることがわかった。そこでおそらく何にも使われていないであろう'@'を'C'の代わりとして用いることにした。

ここまでで'@'が今回実装したいCombinationの演算子としては認識されたが、なぜか演算が実行されなかった。ここでまたしばらく足止めを食らうことになるのだが、再びgrepなどを繰り返し行った結果、formula/source/core/api/FormulaCompiler.cxxで木構造から演算の順序を決める処理をしている部分を発見した。つまり掛け算は足し算より優先して行う、のような取り決めをしている部分である。組み合わせ演算子は当然加減算よりは優先されるので、今回は累乗を表す演算子ocPowと同等の優先度を持つ位置にocCombinを配置することにした。

さて、6日間かかってようやく一つの演算子を追加することに成功した。全くウォーミングアップという内容ではなかったが、計算の流れをなんとなく追うことができたと思う。ラストの記事ではこの実装をもとに残り2つの機能の実装をどのようにしたかをまとめようと思う。

 

ぼくのDOSS奮闘記 〜LibreOfficeを手探ってみた〜 1/3

ここから後期実験「大規模ソフトウェアを手探る」のレポートを書いていきたいと思います。時系列に沿ってどのようなことを行ったか、裏にはどのような苦労があったのかをわかりやすくまとめていきたいと思います。なお、変更点の部分をコードでガンガン載せようとも考えたのですが、いきなりどこからか出てきたコードを出されてもわかりにくいと思ったのでできるだけ言葉でまとめました。

  •  make、そしてビルドまで

1日目で簡単なデバッガの使い方を身につけた後、2日目に僕たちは扱うソフトウェア決めという重要な分岐点に差し掛かっていた。emacsでのデバッグは少々不慣れだったもののある程度流れは掴めていたので、多少ソースコードの量が増えても同様の手順で進められるだろうとこの時は思っていた。

チームを組んだ3人がそこまでプログラミングに長けたメンバーではなかったため、ハードルは高いソフトウェアは避けようと思っていた。過去のブログなどをみた結果からchromiumなどのブラウザ系は処理が追いにくくソースコードも多そうだと(ほとんど推測のみである)見切りをつけた。また、InkSpaceなどのお絵かきソフトも手をつけやすそうだったが、触ったことのないソフトウェアを手探るのは流石に無茶ではないかという話になりこれもスルーした。そんなこんなでテーマ決めに悩んでいるときにふと学科PCに入っていたフリーのソフトウェア、LibreOfficeが目に入った。これなら全員が触ったこともあり、それほどコードが大規模すぎることはないのではないか(これが大きな間違い)と思い、軽い気持ちで着手することにした。これが僕たちとLibreOfficeの運命の出会いである。

さて、コードを手探るも何もまずはローカル環境でビルドしてみないことには話が進まない。僕たちは最初の関門「ビルド」に立ち向かっていく。

まずはビルドするにあたって公式のページを探っていくと、いい感じにビルドの手順がまとまっているページを見つけた。

https://wiki.documentfoundation.org/Development/BuildingOnLinux

このページに載っている通りにファイルをgit cloneしてautogenしてmakeすればいけそうな気がする。とりあえず以下のコマンドを叩いてみる。

$ git clone git://anongit.freedesktop.org/libreoffice/core
$ cd core
$ ./autogen.sh --enable-dbgutil --without-java --without-help --without-myspell-dicts

git cloneして「450万ファイルあるで」と出てきた時にこのソフトウェア実はやばいんじゃないかという予想が頭を掠めた。autogen.shの後についているオプションはlinuxでビルドする時につけといたほうがいいよーというものらしい。特に--enable-dbgutilは後でその重要性を知ることになる。

autogen.shを走らせるといくつかインストールしろと言ってくるので言われたとおりにいくつかインストールしたら無事に終わってくれた。さて、あとはmakeするだけなんですが、これがとーっても長いんですね。公式でも

"make is the command which takes a vast quantity of time to run for the first time."

って脅してくるくらいですから。vast quantity of timeって強そう。笑

さて、それではmakeしましょう!

$ make

 ......

 ........

 ..........

TAさんには5時間くらいかなと脅されたが、幸い3時間ちょいで終わった。はやいはやい。(汗)ちょっと外が寒い日だったのでフル稼働で激しく放熱する学科PCがいい暖房として機能していた。

makeが終わった後、instdirというディレクトリができていたので、サイトに従い以下のコマンドを打つと指定したソフトが起動した。この場合は--calcと指定することでLibreOffice Calcを起動する。

$ instdir/program/soffice --calc

ここでLibreOfficeについて簡単に説明しておくと、MicrosoftのWordやExcelに対応するようなソフトウェアをオープンソースとして無償で提供しているものである。現在も開発が積極的に行われていて、企業向けの安定版と最新機能をふんだんに盛り込んだ最新版の2つがリリースされている。今回は様々あるLibreOfficeのソフトの中からExcelに対応する「LibreOffice Calc」を手探ることにした。

とりあえずそこまで大きなトラブルもなくLibreOffice Calcの起動まで終えて割と順調な出だしだと思えた。makeが長いのは一回目だけだからと言われたのでとりあえず一安心してこの日の実験を終えた。この先に広がるデバッグgrepの闇などまだ知るはずもないのである... (次の記事に続く

 

はじめまして

こんばんは、ブログなんて書くの初めてな杉山です。

きっかけはeeicの授業「大規模ソフトウェアを手探る」という実験の最終レポートがブログでもいいよと言われたことですね。

せっかくなので備忘録的な何かをまとめておける場所になればいいかなーと思います。三日坊主にならなきゃいいですが、、

主な目的はプログラミング関連になると思いますがどんなジャンルに傾いていくのか楽しみですねw

それではよろしくおねがいします(๑╹ω╹๑ )