Makefile でのヘッダファイルの依存関係を自動的に解決する

Makefile でヘッダファイルの依存関係を書くのは結構面倒である。以前は X に付属の makedepend コマンドを使って行うのが普通だったが、便利な方法がないか調べてみた。

GNU make のマニュアルに、まさに依存関係を自動生成する方法そのものがかかれていた。
4.14 Generating Prerequisites Automatically に詳しい説明がある。make の info の日本語訳が GNU make 日本語訳(Coop編) - ルールの記述 があるが、ここにかかれている Makefile のルールそのままでは動作しなかった。英語の原文の方のルールにしないと動作しない。単に引用符の対応関係がとれていないだけ。

以下のようにすればよい。Makefile 全体を乗せておく。

  • 生成された *.d を include する。ただし、一番最初は *.d は存在しないので、-include としてエラーメッセージが表示されないようにする。
  • make clean では *.d も消去するようにする。
PROG=one
SRC=one.c two.c three.c
OBJS=$(patsubst %.c,%.o,$(SRC))
DEPENDS=$(patsubst %.c,%.d,$(SRC))

$(PROG): $(OBJS)
        $(CC) -o $@ $(OBJS)

.c.o:
        $(CC) $(CFLAGS) -c $<

.PHONY : clean depend
clean:
        -$(RM) $(PROG) $(OBJS) $(DEPENDS)

%.d: %.c
        @set -e; $(CC) -MM $(CPPFLAGS) $< \
                | sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' > $@; \
                [ -s $@ ] || rm -f $@
-include $(DEPENDS)

このルールは、コンパイラの -M オプションがインクルードされるファイルのリストを生成してくれる機能を利用している。-MM にすると、標準ディレクトリにあるファイルは表示されず、ローカルのファイルだけが対象となる。こんな感じになる。

#include <stdio.h>
#include "two.h"
#include "three.h"

int main(void)
{
  printf("One! ");
  two();
  three();

  return 0;
}
[motoki@centos3 maketest]$ gcc -M one.c
one.o: one.c /usr/include/stdio.h /usr/include/features.h \
  /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h \
  /usr/lib/gcc-lib/i386-redhat-linux/3.2.3/include/stddef.h \
  /usr/include/bits/types.h /usr/include/bits/wordsize.h \
  /usr/include/bits/typesizes.h /usr/include/libio.h \
  /usr/include/_G_config.h /usr/include/wchar.h /usr/include/bits/wchar.h \
  /usr/include/gconv.h \
  /usr/lib/gcc-lib/i386-redhat-linux/3.2.3/include/stdarg.h \
  /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h two.h \
  three.h
[motoki@centos3 maketest]$ gcc -MM one.c
one.o: one.c two.h three.h

生成される *.d はこんな感じ。

[motoki@centos3 maketest]$ make
cc  -c one.c
cc  -c two.c
cc  -c three.c
cc -o one one.o two.o three.o
[motoki@centos3 maketest]$ cat one.d
one.o one.d : one.c two.h three.h

生成された *.d ファイルの内容はこんな感じ。

参考情報

調査している過程で見つけた Makefile 関係のページをメモしておく。

RPM 作成

これまで Debian ばかりで Redhat 系の RPM は作成したことがなかったが、作成方法を知っておいた方が何かと便利そうなので、簡単なパッケージを作成してみることにした。
参考にしたのは、http://vinelinux.org/MakingRPM/MakingRPM.htmlrpm 3.x の頃の資料なのでちょっと古いかもしれないが、CentOSSRPM を持ってきてみたところ、説明が古そうな点は見あたらないようである。


まずは前準備。
RPM の作成は、システムを上書きしないように一般ユーザで行った方がよい。デフォルトで rpmbuild を行うと /usr/src/redhat 以下を使おうとするので、~/.rpmmacros を作成し、ホーム以下を使用するようにする。ここでは ~/redhat 以下で RPM 作成を行う。また、RPM 作成時の一時データを作成するフォルダも指定しておく。

[motoki@centos3 redhat]$ cat ~/.rpmmacros
%_topdir   /home/motoki/redhat
%_tmppath  %{_topdir}/tmp


さて、パッケージ作成。
簡単なプログラムである GNU hello を材料に spec ファイルを作成してみた。こんな感じ。非常に簡単であった。

Summary: The classic greeting, and a good example
Name: hello
Version: 2.2
Release: 1
Source: hello-2.2.tar.gz
URL: http://www.gnu.org/software/hello/
License: GPL
Group: Local
BuildRoot: %{_tmppath}/%{name}-root

%description
The GNU Hello program produces a familiar, friendly greeting.
It allows non-programmers to use a classic computer science tool which
would otherwise be unavailable to them.

%changelog
* Sat May 19 2007 Akihiro MOTOKI <amotoki@gmail.com>
- first release for version 1.1

%prep
%setup -q

%build
./configure --prefix=/usr
make

%install
make DESTDIR=$RPM_BUILD_ROOT install

%clean
rm -rf $RPM_BUILD_ROOT

%files
%defattr(-,root,root)
%doc ABOUT-NLS  AUTHORS  COPYING  INSTALL  NEWS  README  THANKS  TODO
/usr/bin/hello
/usr/share/locale/
/usr/share/info/hello.info.gz
/usr/share/man/man1/hello.1.gz

%files セクションは最初から書くのは大変なので、分かる範囲で書いて、残りは rpmbuild -bi でインストールまで実行してみてから調節すると楽である。rpmbuild -bi を実行すると、以下のように %files セクションに記載されていないファイルが表示されるので、これを参考に %files セクションを記載する。不要なファイルは %install スクリプト内で消しておくようにする。

error: Installed (but unpackaged) file(s) found:
/usr/bin/hello
/usr/share/info/hello.info.gz
/usr/share/locale/bg/LC_MESSAGES/hello.mo
/usr/share/locale/ca/LC_MESSAGES/hello.mo
/usr/share/locale/da/LC_MESSAGES/hello.mo

...

/usr/share/locale/vi/LC_MESSAGES/hello.mo
/usr/share/locale/zh_CN/LC_MESSAGES/hello.mo
/usr/share/locale/zh_TW/LC_MESSAGES/hello.mo
/usr/share/man/man1/hello.1.gz

LDP man-pages 2.48

The Linux Documentation Project Works を見に行ったら、man-pages 2.48 がリリースされていた。JM の更新は明日行おうっと。

しかし、これまで最新版が置かれていた ftp://ftp.win.tue.nl/pub/linux-local/manpages/ には最新版がなく、http://www.tldp.org/manpages/ に最新版が置かれている。配布場所が変更されたのだろうか。

久しぶりに作業

LDP_man-pages で draft のままでしばらくそのまましていたページをリリース。

  • get_mempolicy.2, set_mempolicy.2, mbind.2
  • inotify_init.2, inotify_add_watch.2, inotify_rm_watch.2
  • standards.7

inotify の watch の訳が「監視対象」になっているが、少し違和感がある。「監視対象」ではなくて「監視を行うエントリ」といった感じなので、近いうちに inotify.7 とあわせて見直しをしよう。