ラベル c-helper の投稿を表示しています。 すべての投稿を表示
ラベル c-helper の投稿を表示しています。 すべての投稿を表示

2013年12月9日月曜日

進捗どうでしょう

「進捗どうですか」と聞いて「進捗ダメです」って答えるネタが流行っているみたい。元ネタは良く知らないのだけど。
この記事は進捗Advent Calendar 2013 9日目向けのものです。アドベントカレンダーなるものに初めて参加してみます。

修士論文

僕の進捗といえば修論の話をしなくてはいけません。なぜなら修士2年生だからです。そう、もう数ヶ月で卒業…。大学に入学して6年、ああ、小学校時代と同じ長さなのか!そりゃすんげえ!

修論は論文誌に投稿するのが12月中、学校に提出するのが1月後半という目標です。
単純なページ数的進捗で言えば、論文誌用の論文の方は2/6ページ書きました。執筆が捗るときと捗らないときの波が結構ありますけど、残りも頑張ります。ちなみにC-Helperが僕の研究です。

マイスタイル

僕が論文書くときのスタイルなどを書いておきます。「我も同じじゃ」とか「こうしたほうが良いよ」などありましたらコメントで教えてください。参考にさせていただくかもしれません!

音楽

BGMをかけつつ文章を書くのが好きです。電子音がピコピコするアニソン系の曲、東方JAZZアレンジ系、UKロックなど聴きます。日本語の歌詞が聞き取りやすい曲も好きですが、作業効率は落ちる気がします。
そうそう、最近はDiverse Systemのworks.3を買いまして、修論のお供に良く使ってます。アルバムタイトルの通り、結構優秀な作業用BGMだと思います。

場所

執筆活動をする場所も僕には重要です。僕は自宅ではゲームなどやってしまって執筆が捗ったためしがありません。比較的集中できるのはドトールなどのカフェや通学電車です。自宅以外だとお気に入りのゲームも無いですし、適度に人の目(カフェ席で寝てたら恥ずかしいので寝ないですみます)もありますから。
さらに付加効果として、カフェでMacBook Airを広げると多少優越感があります。Haswell搭載のノートPCにすれば、一日中電源なしでカフェを転々として執筆活動にいそしめます。

道具

Vim+TeXをターミナルから使って論文書いてます。Vimで.texファイルを保存するたびにmakeが非同期で走るように.vimrcを設定してあり、自動でTeXコンパイルされます。PDFのプレビューにSkimを使えば、ファイルの更新を自動で検出して再読み込みしてくれるので便利です。しかも、再読込時に他のウィンドウよりも手前、ターミナルよりも後ろという超絶妙な位置に再配置されるので、非常に使いやすい。おすすめ。

修論の同士たち、一緒に頑張りましょう。


さて、明日は wraith13 さんの担当で御座います。進捗は如何でしょう?

2013年2月18日月曜日

【Eclipseプラグイン開発】 配布時に他プラグインへの依存をなくす

Eclipseプラグインの開発時と配布時の、他プラグインへの依存関係を変えたいことがあります。
例えば、開発時にJUnitを使ってテストをするような場合。
配布先ではテストをしないでしょうから、末端ユーザーの環境にJUnitプラグインがなくても大丈夫なようにしたい。
このように、開発時と配布時の依存関係を変えたいという要望があるでしょう。

まず基礎知識として、開発しているEclipseプラグインの依存設定はplugin.xmlで設定します。
マニフェストエディタでplugin.xmlを開けばDependenciesタブがあります(下図)。


Plug-in Dependencies - Help Eclipse Platform
を読むと、依存設定の"Optional"オプションを有効化すると、その依存プラグインが配布先環境に存在してもしなくても、配布したプラグインが実行できるそうです。

ということで、実際に試してみたところ、JUnitプラグインが存在しない環境でもC-Helperがインストール&実行できるようになりました。

2012年8月11日土曜日

C-Helper がインデント乱れとmalloc/sizeof警告に対応しました

サイボウズ・ラボユースで製作中の C-Helper が、インデントの乱れや malloc / sizeof に関して警告できるようになりました。
さらにそれらの警告を Eclipse の Problem view に表示することで、プラグインっぽさが増しました。
どうでしょう。このUIならプログラミングの授業などで使ってもらえるでしょうか。

2012年7月19日木曜日

Eclipse CDT C言語パーサーに好きなヘッダファイルを使わせる

今まで Eclipse CDT でC言語のパースをする場合

IncludeFileContentProvider
にはIncludeFileContentProvider.getEmptyFilesProvider()を指定していました。(CDTでASTを参照)

ところが今日、この方法を使うとある問題にぶち当たることがわかりました。
想像はしていましたが、ヘッダファイルがまったく #include されずにパースが行われるのです。それでもなんとなくパースできちゃうのがCDTのすごいところですが、問題は識別子の型を正確に取れないということです。
ソースコードに現れる名前、例えば「add」が変数名なのか何なのかは、その宣言が無いと分かりません。たとえ「add(2, 3)」のように、いかにも関数呼び出し風に書かれていたとしても、もしかしたら関数ポインタ、すなわち単なる変数かもしれないのです。

ということで、正確に識別子の型を判別するには #include を無視せずにヘッダファイルを読み込むようにしなければなりません。そこで登場するのが
IncludeFileContentProvider.getSavedFilesProvider()
です。 SavedFilesProvider は、パース対象のC言語ソースコードがある場所と IScannerInfo に設定したインクルード検索パスをヒントにして実際のヘッダーファイルを探し、 #include をきちんと処理してくれます。

基本的にはこれで良いのですが、僕のプラグインの作り方が特殊なため、不具合がでるのです。僕はプラグインを Eclipse のプラグインとしてではなく、通常のJavaアプリとして開発しているため、プラグインの実行時にはワークスペースが開かれていない状態となります(Eclipse 上で動いていないので当たり前)。
その状態で SavedFilesProvider を使うと
java.lang.IllegalStateException: Workspace is closed.
at org.eclipse.core.resources.ResourcesPlugin.getWorkspace(ResourcesPlugin.java:399)
なる例外が発生してしまいます。

SavedFilesProvider はユーザーのヘッダファイルも探してくれる機能を持っているため、ワークスペースが開かれていないとユーザーのヘッダファイルを探せなくて例外が出ます。困りました。これでは単体テストができないじゃないですか。
さらに僕がやりたいのは、GCCの汚れたヘッダではなく、 ANSI-C に準拠するミニマムなヘッダを食わせてパースしたいんです。

そこで、僕が考えた解決策は InternalFileContentProvider (SavedFilesProvider の親クラス)を継承して、独自のプロバイダを書くことです。このクラスはどうやら内部使用が前提のクラスらしく、継承元に指定しようとすると Discouraged access などと警告が出るので気持ち悪いですが、エラーにはなりませんので無視します。
public class MyFileContentProvider extends InternalFileContentProvider {
    final private Map<String, char[]> stdHeaders;

    public MyFileContentProvider() {
        stdHeaders = new HashMap<String, char[]>();
        stdHeaders.put("/usr/include/stdio.h", "int puts(const char* s);".toCharArray());
        stdHeaders.put("/usr/include/stdlib.h", "".toCharArray());
    }

    @SuppressWarnings("restriction")
    @Override
    public InternalFileContent getContentForInclusion(String filePath,
            IMacroDictionary macroDictionary) {
        if (!getInclusionExists(filePath)) {
            return null;
        }

        if (stdHeaders.containsKey(filePath)) {
            return (InternalFileContent) FileContent.create(filePath, stdHeaders.get(filePath));
        }
        return SavedFilesProvider.getInstance().getContentForInclusion(filePath, macroDictionary);
    }

    @SuppressWarnings("restriction")
    @Override
    public InternalFileContent getContentForInclusion(IIndexFileLocation ifl,
            String astPath) {
        return SavedFilesProvider.getInstance().getContentForInclusion(ifl, astPath);
    }

}
結局作ったのがこんなクラスです。まだ製作途中なので標準ヘッダの中身が超適当ですが、これを拡充していけばいけるはずです。
ポイントは

  • 標準ライブラリのファイル名が来たら、組み込みの文字列からFileContentを作って返す
  • それ以外のファイル名がきたら、 SavedFilesProvider に委譲する
の2点です。結局 SavedFilesProvider を使ってしまっていますが、単体テストでは標準ライブラリだけを使うようにすれば、 SavedFilesProvider は呼び出されませんので、例外が発生しません。

以上、ヘッダファイルを考慮に入れた CDT でのパース方法でした。

2012年7月8日日曜日

Eclipse の機能を使ったプログラムを java コマンドで実行する

Eclipse のプラグインを開発する場合、多かれ少なかれ他のプラグイン(Eclipse 自体がプラグインで出来ています)のクラス群を利用することになります。また、 Eclipse のプラグインではない普通のプログラムを作る場合にもそれらクラス群を利用可能です。実際に Eclipseプラグインの開発サイクル短縮化計画 では、Eclipseプラグインを通常のJavaアプリに変えて実行しています。


Eclipse をインストールしたディレクトリの plugins ディレクトリを開いてみれば、膨大な数の jar ファイルに驚くと思います。これらの jar ファイルに含まれるクラス群を自由に使っていいと思うと非常にワクワクしますね!

今回はコマンドラインから java コマンドを用いて、 Eclipse のクラス群を利用した通常のアプリを起動させる方法を模索しました。なぜかって?アプリのテストを自動化するためです。java コマンドで起動させられれば、 Eclipse の上で実行せずとも、シェルスクリプトなどを用いてアプリを起動でき、 Eclipse と疎結合なテストが書けるのです。

さて、ここで問題になるのは、 Eclipse のクラス群を CLASSPATH に設定しなくちゃいけないということです。例えば CLASSPATH を設定せずに僕が作っているアプリを起動させると

Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/core/runtime/IPath
 at com.github.uchan_nos.c_helper.Launcher.main(Launcher.java:112)
Caused by: java.lang.ClassNotFoundException: org.eclipse.core.runtime.IPath
 at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
 at java.security.AccessController.doPrivileged(Native Method)
 at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
 at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
 ... 1 more
という出力が出てきます。
ここで「ふむふむ、 org.eclipse.core.runtime.IPath が見つからないのか。では org.eclipse.core.runtime_3.8.0.v20120521-2346.jar を CLASSPATH に追加しよう。」と考えても、実は IPath クラスはそのjarファイルに含まれておらず、先ほどの例外は解決しないのです。
どうも、Eclipseのプラグイン(プラグイン=jarファイルと考えていいだろう)の名前と、それに含まれるクラスのパッケージ名が一致しないらしいのです。うーん、こまった。


ということで、 Eclipse の plugins ディレクトリにあるすべてのjarファイル名を列挙して、それをコロン:区切りで結合した文字列を出力するだけのスクリプトを書いてみました。
import os
import sys
import re

if __name__ == '__main__':
 plugindir = '/Applications/eclipse-juno/plugins'
 if len(sys.argv) == 2:
  plugindir = sys.argv[1]
 elif len(sys.argv) != 1:
  print('Usage: python searchplugins.py [plugin-directory]')
 
 classpath = '.'
 jarpat = re.compile(r'.*\.jar')
 files = os.listdir(plugindir)
 for f in files:
  m = jarpat.match(f)
  if m:
   classpath += ':' + os.path.join(plugindir, f)
 print(classpath)
とても愚直な実装だが。。。このスクリプトは/Applications/eclipse-junoをデフォルトのEclipseインストールディレクトリとしていますから、使う場合には変えたほうがいいでしょう。このスクリプトを searchplugins.py のような名前で保存し
python searchplugins.py
と実行すれば、/Applications/eclipse-juno/pluginsディレクトリの中にあるすべてのjarファイルのフルパスが : 区切りで出力されます。
.:/Applications/eclipse-juno/plugins/ch.qos.logback.classic_1.0.0.v20111214-2030.jar:/Applications/eclipse-juno/plugins/ch.qos.logback.core_1.0.0.v20111214-2030.jar
というような感じに。先頭にカレントディレクトリ.を含めてあるので、この出力をそのまま CLASSPATH に設定すれば、Eclipseのクラス群を利用したアプリが起動できるということになります。

CLASSPATH=`python ../test/searchplugins.py` java com/github/uchan_nos/c_helper/Launcher
実際には上記のようなコマンドを実行することにより、 Eclipse プラグインがすべて CLASSPATH に設定された状態で目的のアプリが起動し、めでたく実行できました。やったね!

2012年6月29日金曜日

Eclipseプラグインの開発サイクル短縮化計画

みなさんはEclipseのプラグインを開発したことがあるでしょうか?

Eclipseのプラグインを開発するときは、普通の手順ではEclipseの上で「テスト用のEclipse」を起動させてプラグインを動かします。

Eclipse自体が非常に重たいソフトウェアですから、それを2つ起動するともなるとメモリ使用量がものすごいことになります。しかも、メソッドが追加・削除される度にテスト用Eclipseを再起動させねばなりません。
それはつまり、1回の開発サイクルにほぼ毎回15秒くらいの待ち時間が入ってくることを意味します。Eclipse終了→ビルド→Eclipse起動→プラグインテスト

開発サイクルを速めるのに効果的なのは、プラグインとして開発しているソフトウェアを普通のJavaアプリにしてしまうことです。要するに、どこかにmainメソッドを定義し、そこから起動する形式にするのです。(このやり方は、ラボユースにおいて社員さんの方々とのブレインストーミングで浮上してきた案を元にしています)

Eclipseプラグインは通常、様々なやり方で起動されます。ツールバーのボタンから、メインメニューから、右クリックのコンテキストメニューからなど。各部分に主要な処理を埋め込んでいると僕の提案するやり方はやりにくいでしょう。

僕の提案手法が適用しやすいのは、プラグインの主要機能が1つのメソッドにまとまっており、その機能の入力データが手動で生成しやすい形式であるような場合です。手動で生成しやすいデータは例えばプログラムのソースコードです。単に文字列として表現できますので、簡単にプログラム上で表現できます。

対して、プラグインが複雑なデータを入力として必要とする場合、そのデータを用意するにはプラグインをプラグインとして起動させないといけない可能性があります。そういう場合、単純に僕の提案手法を使おうとしてもダメかもしれません。しかし、プラグインを普通のJavaアプリとして開発していくことで、開発効率が上がる、という考え方は有用なのではないかと思います。

2012年6月19日火曜日

CDTでAST

今日はちょっとだけC-Helperの開発として、EclipseのCDTを使って、C言語のソースコードから抽象構文木(AST)を生成する部分を実装しました。
同じような実装は卒論のときにもしたけれど、そのたびによくわからないのがAST生成メソッドの引数です。

ILanguage.getASTTranslationUnit(
        FileContent content,
        IScannerInfo scanInfo,
        IncludeFileContentProvider fileCreator,
        IIndex index,
        int options,
        IParserLogService log)

引数がいっぱいあるけれど、これらについてまともなドキュメントは存在しません。
何を渡せばいいのかはっきりせず、卒論のときも今も、自己流で引数を用意しています。こんな感じに。

        ILanguage language = GCCLanguage.getDefault();

        FileContent reader = FileContent.create(filePath, source);

        Map<String, String> macroDefinitions = null;
        String[] includeSearchPath = null;
        IScannerInfo scanInfo = new ScannerInfo(macroDefinitions, includeSearchPath );

        IncludeFileContentProvider fileCreator = IncludeFileContentProvider.getEmptyFilesProvider();
        IIndex index = null;
        int options = 0;
        IParserLogService log = new DefaultLogService();

        try {
            IASTTranslationUnit translationUnit = language.getASTTranslationUnit(reader, scanInfo, fileCreator, index, options, log);

とりあえずFileContentだけきちんと解析したいソースコードの内容を渡すようにすれば、とりあえずはそれなりの抽象構文木が手に入るなあという印象です。