FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

DSLでビルドツールを自作する (23日目 EmacsプロジェクトをRumyでコンパイルしてみる試行 ~必要な機能は何か~)

この記事は「Qiita Advent Calendar 2019 DSLで自作ビルドツールを作ろう」の23日目の記事です。

23日目 EmacsプロジェクトをRumyでコンパイルしてみる試行 ~必要な機能は何か~

もうすこし大きなプロジェクトをビルドしてみたいと思います。いろいろと調べてみましたが、LLVMはあまりにも大きすぎて直ぐにできそうにありません。もうすこしステップバイステップでできるものはないか...ということで、これも大きなチャレンジではありますが、EmacsパッケージをRumyでコンパイルしてみることにしました。ここまで大きなプロジェクトであれば、Rumyでビルドしてみると今のRumyに何が足りないのかが見えてくるはずです。Emacsソースコードとビルド環境は、以下からダウンロードすることができます。

http://ftp.jaist.ac.jp/pub/GNU/emacs/

curl -L https://mirror-hk.koddos.net/gnu/emacs/emacs-26.3.tar.gz | tar xz

ダウンロードして解凍したEmacsを、まずは./configureからのmakeMakefileを作ってみます。これで、Rumyへ移行対象となるビルドファイルを作ってみようという訳です。

cd emacs-26.3.tar.gz
./configure
make

猛烈におおきなMakefileが出来上がりました。しかもMakefileが複数存在しておりこれは厄介です。少しずつ進めることにします。まずは一番元となるターゲットですが、make allとすればemacsのバイナリが作れるようです。そして、バイナリを作るために複数のディレクトリを渡ってmakeを実行しており、階層ビルドが使われています。

  • Makefile
### The nt/ subdirectory gets built only for MinGW
NTDIR=
...
SUBDIR = $(NTDIR) lib lib-src src lisp

all: ${SUBDIR} info

lib lib-src lisp nt: Makefile
    $(MAKE) -C $@ all

src: Makefile
    $(MAKE) -C $@ VCSWITNESS='$(VCSWITNESS)' all

このlib, lib-src, lisp, srcというディレクトリのビルドがキモとなりそうですので、これらをまずはこれらをターゲットにした依存関係ファイルを作っていきます。RumyのExternal-Targetを使用しましょう。

  • build.rb
external_target "lib", "lib"
external_target "lib-src", "lib-src"
external_target "src", "src", ["lib-src"]
external_target "lisp", "lisp"
external_target "info", "info"

make_target :all do
  global
  depends ["lib", "lib-src", "src", "lisp", "info"]
end

しかしここまで作っておいてふと思いました。「おいおいこのサブディレクトリのRumyファイル全部移植しないとコンパイルできないのか?」これはちょっと面倒です。最終的にビルドテストをするまでにかなり時間がかかってしまいます。そこで、最初はサブディレクトリの呼び出しはMakeを使い、サブディレクトリを1つずつRumyに変換していきます。このため、トップディレクトリのRumyファイルからいきなりexternal_targetでサブディレクトリのRumyを呼ぶのではなく、サブディレクトリのMakefileを呼び出す仕組みと作っておきます。external_makeというコマンドを作りました。これはサブディレクトリRumyビルドではなく、Makeビルドを呼び出します。最初はMakeでビルドしておいて、少しずつRumyに移行する、という訳です。

external_make "lib", "lib"
external_make "lib-src", "lib-src"
external_make "src", "src"
external_make "lisp", "lisp"
external_make "info", "info"

make_target :all do
  global
  depends ["lib", "lib-src", "src", "lisp", "info"]
end
  • rumy-target.rb
##
## External Target Make call
##
def external_make(lib_name, dir, depends = [])
  target = Target.new(lib_name)
  target.executes(["make -C #{dir}"])
  target.depends(depends)
  $target_list[lib_name] = target
end

まずはこれで一度ビルドが通りました。次に、大物そうなsrcディレクトリを変換していきます。とにかくやることは、Makefileに書いてあることをそのままRumyに移すことです。RumyはMakefileとほぼ同じ機能を持っているので、基本的にすべて移行できるはずです。Makefileの機能(notdirbasenameなど)は、Rubyの機能(File.basenameなど)を使って書き換えていきます。filterfilter-outなどの機能も、Rubymapを使って書き換えていきました。

書きながら試行錯誤していると、やはりconfigureからのMakeは上手くできていると思いました。依存関係のファイルと、各ソースコードから依存関係を抜き出している(?).dファイルが作られており、依存関係を上手く抽出しています。Rumyにはない機能で、これを移植するのはかなり骨が折れそうです。Makefileすごい。

deps.mkの依存関係の記述も、とりあえずひたすら置換することでRumyの依存関係に書き換えていきました。もっと楽に移行できる機能を付けておけばよかった。executesコマンドとか省略されて入ればデフォルトでgcc立ち上げるとかどうだろう?

  • deps.mk
...
atimer.o: atimer.c atimer.h syssignal.h systime.h lisp.h blockinput.h \
 globals.h ../lib/unistd.h msdos.h $(config_h)
bidi.o: bidi.c buffer.h character.h dispextern.h msdos.h lisp.h \
   globals.h $(config_h)
buffer.o: buffer.c buffer.h region-cache.h commands.h window.h \
...
  • build.rb
make_target "atimer.o" do
  executes ["#{CC} #{ALL_CFLAGS} -c #{name} #{name.sub(".o", ".c")}"]
  depends ["atimer.c", "atimer.h", "syssignal.h", "systime.h", "lisp.h", "blockinput.h",
           "globals.h", "../lib/unistd.h", "msdos.h", config_h]
end
make_target "bidi.o" do
  executes ["#{CC} #{ALL_CFLAGS} -c #{name} #{name.sub(".o", ".c")}"]
  depends ["bidi.c", "buffer.h", "character.h", "dispextern.h", "msdos.h", "lisp.h",
           "globals.h", config_h]
end
make_target "buffer.o" do
  executes ["#{CC} #{ALL_CFLAGS} -c #{name} #{name.sub(".o", ".c")}"]
  depends ["buffer.c", "buffer.h", "region-cache.h", "commands.h", "window.h",
           INTERVALS_H, "blockinput.h", "atimer.h", "systime.h", "character.h", "../lib/unistd.h",
           "indent.h", "keyboard.h", "coding.h", "keymap.h", "frame.h", "lisp.h", "globals.h", config_h]
end

移行作業はまだまだ続く...