2012年7月31日火曜日

MacTex に新しいスタイルファイルを組み込む

MacTeX に .sty ファイルをインストールするにはどうすればいいかのメモ。

.sty ファイルを TeX に組み込むには、基本的に texmf ディレクトリを探し、その中の対応するサブディレクトリに放り込めばよい。
といっても、後から手動で追加するファイルをシステムのディレクトリに入れるのは少し気持ち悪いので、 MacTeX ではどこが個人ディレクトリになるのかを調査した。

によれば、 MacTeX では
  • ~/Library/texmf/tex
  • ~/Library/texmf/tex/latex
の2つを標準で検索してくれるらしい。(~/Library はホームディレクトリの中の Library ディレクトリである。例えば僕の場合は /Users/uchan/Library となる)
なければ自分で作ればいいので心配無用だ。
Finder だともしかしたら Library ディレクトリが見えないかもしれないが、そんな場合は Finder のメニューから「移動」>「フォルダへ移動」の画面で「/Users/hoge/Library」と入力すれば Library ディレクトリに移動できる。

試しに FOSE2012 のスタイルファイルを FOSE 2012 論文募集 のページの「フォーマット」「latex」のリンクから取ってきてインストールしてみた。
ファイル名は fose2012_latex_sty.zip であり、展開すると次のようなファイルが含まれたディレクトリ fose2012_latex_sty が生成される
  • example.pdf
  • example.tex
  • fose2005logo.eps
  • fose2012.cls
  • fose2012.sty
上3ファイルは .cls ファイルと .sty ファイルがきちんとインストールされたかどうかを試すためのスタイルファイル使用サンプルである。
下2ファイルは肝心要のクラスファイルとスタイルファイルでる。

展開して生成された fose2012_latex_sty ディレクトリを目的の場所に移動する。
~/Library/texmf/tex/latex/fose2012_latex_sty
となるように移動(またはコピー)すれば完成である。mktexlsrの実行などは必要ない。

2012年7月21日土曜日

プログラム開発にはCUIでの経験が生きる カレントディレクトリ的な意味で

画像表示の質問 - C言語何でも質問掲示板での質問で見られるように、GUIしか使ったことがないと「カレントディレクトリ」というものの存在を認識する機会に恵まれません。そんなものを意識せずとも作業ができるように、ある意味でUIが進化した結果なのだと思いますし、意識しなくてもできるようなことしかしないのならそれでいいと思います。

しかしいざプログラミングしようとしたときに、カレントディレクトリは結構詰まりやすいポイントだと思います。プログラムが現在動作しているディレクトリなんて、GUIしかやってなかったら想像つかないでしょう。

こう考えると、プログラミング初心者にプログラムを教えるときもターミナル上でコマンドを叩く方式で教えたほうが、なんだかいい気がしてきました。

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年7月4日水曜日

Eclipse subversive SSH で private key のパスフレーズを入力できない問題

Eclipse に subversive プラグインを載せて SVN を使っています。今まで sourceforge.jp のリポジトリに公開鍵暗号方式で正常にアクセスできていました。ところが今日、作業を少し中断してから作業を再開しようとすると、なんと秘密鍵用のパスフレーズを何度入力しても弾かれるという問題が発生しました。

Eclipse + subversive な組み合わせで SSH を用いてリポジトリにアクセスしようとすると SSH の秘密鍵とパスフレーズを入力する画面が出るのですが、そのパスフレーズに正しいパスフレーズを入れても、直後にまた同じ画面が出てきて、どうやらパスフレーズが弾かれてしまいます。同じパスフレーズを持った鍵を作りなおしてもダメ、空パスフレーズの鍵を作ってみてもダメ。

途方に暮れていましたが、試しにワークスペースを切り替え、 SVN リポジトリからのインポートを試してみると、なんと上手く行くのです。そこで、本物のワークスペースの .metadata ディレクトリを削除後、試してみました。結果は成功。ワークスペースにあるプロジェクトは全部インポートし直す必要はありますが、とりあえず問題は解決したのでよしとしましょう。

追記 2012/07/17
.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.team.*
を削除したら復旧するというところまで特定しました。

追記 2012/07/30

.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.team.svn.core.prefs
を削除したら復旧するというところまで特定しました。

追記 2012/08/26 解決!
Bug 239871 で同じようなバグが報告されていました(2008年)
それが最近(2012年7月24日)になってようやく修正されたらしいとのことで、
Early Access Build版の Subversive を試してみたところ、見事問題が修正されていました!
Early Access Build版については Latest Releases - Eclipse Subversive の

Early Access Build

という項目を見てください。アップデートサイトのURLが載っていますので、そこからインストールできます。

2012年7月3日火曜日

Greek on Mac OS X

最近はどういうことか「古典ギリシア語」を学習しております。それで、Macでギリシア語を入力するという需要がありましたので、環境構築をしてみました。といっても、かなりの部分はMacの標準機能で対応できました。

ギリシア語を入力するには幾つかの方法があるのだと思いますが、僕はUnicodeでギリシア語を入力する方法を使いました。幾つかキーポイントがあります。
  • 入力ソースに「Greek Polytonic」を追加する
    システム環境設定 > 言語とテキスト > 入力ソース から設定できます
  • Command + Space でギリシア語モードを選択する
    Command + Space を一度押したあと、Commandキーを離さずに押しっぱなしにするとポップアップが出て簡単に選択できます
  • キーボードビューアを表示する
    図の場所に「キーボードビューアを表示」という項目があります
以上はMac標準機能。これだけでギリシア語を入力できるようになります。
しかし、標準のフォントだとアクセント記号などが見にくかったりしますので、お気に入りフォントを探しましょう。参考記事: 古典ギリシャ語用のフォントがサンプル付きで沢山のフォントを比較していますので、非常に便利です。僕は、悩んだ末「Aristarcoj」というフォントを入れさせていただきました。

2012年7月2日月曜日

Dell Color Laser Printer 3110cn on Mac OS X Lion

DellのA4カラーレーザーネットワークプリンタ「Dell Color Laser Printer 3110cn」をMac OS X Lionに追加する方法です。
ポイントは
  • IPアドレスを直打ちすること
  • プロトコルを「LPD (Line Printer Daemon)」にすること
  • 使用するドライバを「一般的な PostScript プリンタ」とすること
です。

プロトコルをIPPにしたりドライバをPCLにしたりしてもプリンタをうまく認識した風な表示にはなりますが、いざ印刷する段階で「プリンタでの処理が混み合っています」というメッセージが出っぱなしになり、一向に印刷できませんのでご注意ください。