[ C++で開発 ]
X Window Systemを構築する際に使用されるmakeツールの一つ。プラットフォーム間の違いを吸収する仕組みを持ち、各プログラムの構築にはMakefileを記述するより簡単な記述でMakefileを生成する。最近ではAutomakeが使われることが増えてきているが、X Window Systemがある限りimakeは不滅でしょう。
最近のUNIX系OSは標準でX Window Systemが搭載されているので、そのままでimakeが利用できる環境となっています。XFree86にも含まれているので、Linuxでも勿論利用可能です。
一方、Windowsでは、Cygwinに含まれるXFree86パッケージ群を導入することによりimake環境を利用することができます。
imake環境は、XFree86-binパッケージに含まれるコマンドの他、Imake用設定ファイルが含まれるXFree86-progパッケージが必要です。
xmkmfを実行するとエラーが発生します。
ex1$ xmkmf mv -f Makefile Makefile.bak imake -DUseInstalled -I/usr/X11R6/lib/X11/config /usr/X11R6/lib/X11/config/site.def:44: host.def: No such file or directory /usr/X11R6/lib/X11/config/site.def:146: host.def: No such file or directory imake: Exit code 1. Stop. ex1$ |
メッセージのとおり、host.defが見つからないというものです。とはいえ正しい対処方法がわからないので、暫定処置として、/usr/X11R6/lib/X11/config/xf86site.defをhost.defとして置く事にします。(あってる?)
ex1$ cd /usr/X11R6/lib/X11/config config$ cp xf86site.def host.def config$ |
これでxmkmfが動くようになりました。
ex1$ xmkmf mv -f Makefile Makefile.bak imake -DUseInstalled -I/usr/X11R6/lib/X11/config ex1$ |
まずはC言語の単一ソースからなるプログラムhelloをimakeで構築します。ここではWindows+Cygwinで実行していますが、UNIX全般でも共通と思います。
#include <stdio.h> int main(int argc, char* argv[]) { printf("Hello, world!\n"); } |
hello.cをコンパイルして、実行ファイルhelloをリンクして作成するためのルールをImakefileに記述します。
SRCS = hello.c AllTarget(hello.exe) NormalProgramTarget(hello,hello.o,NullParameter,NullParameter,NullParameter) DependTarget() |
まだ一度もxmkmfコマンドを実行していなければ、一回実行します。imakeでは、Makefileを生成するためにmakeコマンドにターゲット"Makefile"を指定して実行します。しかし、最初はMakefileが存在しないため、xmkmfによって"Makefile"ターゲットが記述されたMakefileを生成します。一度Makefileが生成されたら、以後はxmkmfは実行し直すことはないでしょう。
ex1$ xmkmf mv -f Makefile Makefile.bak imake -DUseInstalled -I/usr/X11R6/lib/X11/config ex1$ |
次に、ターゲット"Makefile"を指定してmakeを実行してMakefileを生成します。
ex1$ make Makefile + rm -f Makefile.bak + mv -f Makefile Makefile.bak imake -DUseInstalled -I/usr/X11R6/lib/X11/config -DTOPDIR=. -DCURDIR=. ex1$ |
ではいよいよ最初のプログラムをビルドしてみましょう。
ex1$ make gcc -O2 -fno-strength-reduce -Wall -Wpointer-arith -I/usr/X11R6/include - D__i386__ -DWIN32_LEAN_AND_MEAN -DX_LOCALE -D_X86_ -D__CYGWIN__ -D_XOPEN_SOURCE -D_POSIX_C_SOURCE=199309L -D_BSD_SOURCE -D_SVID_SOURCE -D_GNU_SOURCE -DNO_ALLOCA -DFUNCPROTO=15 -DNARROWPROTO -c -o hello.o hello.c hello.c: In function `main': hello.c:6: warning: control reaches end of non-void function rm -f hello.exe gcc -o hello.exe -O2 -fno-strength-reduce -Wall -Wpointer-arith -L/usr/X11R6 /lib hello.o -Wl,--enable-auto-import ex1$ |
生成した最終ターゲットファイル、中間ファイルを削除します。
ex1$ make clean rm -f hello.exe rm -f *.CKP *.ln *.BAK *.bak *.o core errs ,* *~ *.a .emacs_* tags TAGS make.log MakeOut *.obj *.orig *.rej junk.c *.exe *.dll *.lib *~ "#"* ex1$ |
ソースファイルとヘッダファイルの依存関係を生成します。
ex1$ make depend makedepend -- -I/usr/X11R6/include -D__i386__ -DWIN32_LEAN_AND_MEAN -DX_LO CALE -D_X86_ -D__CYGWIN__ -D_XOPEN_SOURCE -D_POSIX_C_SOURCE=199309L -D_BSD_SOURC E -D_SVID_SOURCE -D_GNU_SOURCE -DNO_ALLOCA -DFUNCPROTO=15 -DNARROWPROTO -DU SE_MAKEDEPEND -- hello.c ex1$ |
すると、Makefileの末尾にソースファイルからヘッダファイルへの依存関係が追記されます。
# ---------------------------------------------------------------------- # dependencies generated by makedepend # DO NOT DELETE hello.o: /usr/include/stdio.h /usr/include/_ansi.h /usr/include/newlib.h hello.o: /usr/include/sys/config.h /usr/include/machine/ieeefp.h hello.o: /usr/include/cygwin/config.h hello.o: /usr/lib/gcc-lib/i686-pc-cygwin/3.2/include/stddef.h hello.o: /usr/lib/gcc-lib/i686-pc-cygwin/3.2/include/stdarg.h hello.o: /usr/include/sys/reent.h /usr/include/sys/_types.h hello.o: /usr/include/sys/types.h /usr/include/machine/types.h hello.o: /usr/include/sys/features.h /usr/include/cygwin/types.h hello.o: /usr/include/sys/sysmacros.h /usr/include/stdint.h hello.o: /usr/include/sys/stdio.h |
今度はC++言語の単一ソースからなるプログラムhelloをimakeで構築します。
#include <iostream> int main(int argc, char* argv[]) { std::cout << "Hello, world!" << std::endl; } |
hello.cをコンパイルして、実行ファイルhelloをリンクして作成するためのルールをImakefileに記述します。
SRCS = hello.cpp AllTarget(hello.exe) NormalCplusplusProgramTarget(hello,hello.o,NullParameter, NullParameter,NullParameter) |
xmkmfを実行します。
ex2$ xmkmf mv -f Makefile Makefile.bak imake -DUseInstalled -I/usr/X11R6/lib/X11/config ex2$ |
次に、ターゲット"Makefile"を指定してmakeを実行してMakefileを生成します。
ex2$ make Makefile + rm -f Makefile.bak + mv -f Makefile Makefile.bak imake -DUseInstalled -I/usr/X11R6/lib/X11/config -DTOPDIR=. -DCURDIR=. ex2$ |
ではいよいよ最初のプログラムをビルドしてみましょう。
ex2$ make g++ -O2 -fno-strength-reduce -I/usr/X11R6/include -D__i386__ -DWIN32_LEAN_A ND_MEAN -DX_LOCALE -D_X86_ -D__CYGWIN__ -D_XOPEN_SOURCE -D_POSIX_C_SOURCE=199309 L -D_BSD_SOURCE -D_SVID_SOURCE -D_GNU_SOURCE -DNO_ALLOCA -c -o hello.o hel lo.cpp rm -f hello.exe g++ -o hello.exe -O2 -fno-strength-reduce -L/usr/X11R6/lib hello.o -Wl,--enable-auto-import ex2$ |
となりビルドOKです。
ImakefileのAllTargetに、hello.exeではなくhelloとUNIXのように指定すると、allを指定してmakeするとエラーとなってしまいます。下記参照。
ex2$ make g++ -O2 -fno-strength-reduce -I/usr/X11R6/include -D__i386__ -DWIN32_LEAN_A ND_MEAN -DX_LOCALE -D_X86_ -D__CYGWIN__ -D_XOPEN_SOURCE -D_POSIX_C_SOURCE=199309 L -D_BSD_SOURCE -D_SVID_SOURCE -D_GNU_SOURCE -DNO_ALLOCA -c -o hello.o hel lo.cpp gcc hello.o -o hello hello.o(.text+0x30):hello.cpp: undefined reference to `std::cout' hello.o(.text+0x35):hello.cpp: undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_o stream<char, std::char_traits<char> >&, char const*)' hello.o(.text+0x40):hello.cpp: undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_os tream<char, std::char_traits<char> >&)' hello.o(.text+0x45):hello.cpp: undefined reference to `std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream<char, std::char_traits<c har> >& (*)(std::basic_ostream<char, std::char_traits<char> >&))' hello.o(.text+0x93):hello.cpp: undefined reference to `std::ios_base::Init::Init [in-charge]()' hello.o(.text+0x87):hello.cpp: undefined reference to `std::ios_base::Init::~Ini t [in-charge]()' hello.o(.eh_frame+0x11):hello.cpp: undefined reference to `___gxx_personality_v0 ' collect2: ld returned 1 exit status make: *** [hello] エラー 1 ex2$ |
エラーは、リンク時にコマンドg++ではなくgccが使用されているために発生しています。
ここで生成されたMakefileの一部を見ると
SRCS = hello.cpp all:: hello hello.exe: hello.o $(RM) $@ $(CXXLINK) -o $@ $(CXXLDOPTIONS) hello.o $(LDLIBS) $(EXTRA_LOAD_FLAGS) |
となっています。ターゲット無し(all)でmakeすると、ターゲットhelloに依存していますが、ターゲットhelloはMakefile中には記述がないため(hello.exeは文字列が異なるため別ターゲット)、暗黙のhello.oへの依存となります。hello.oは、デフォルトでは$(CC)すなわちgccによってリンクされるのでエラーとなってしまいます。
原因は、AllTarget(hello)と指定した場合、all::
hello.exe と生成されずに all:: hello と生成されることにあります。
Imakefileの記述で回避するのでなければ、Imake.rulesを変更します。
*** Imake.rules Fri Aug 1 13:04:16 2003 --- Imake.rules.modifiedDefineAllTarget Sun Sep 7 09:04:40 2003 *************** *** 3263,3269 **** */ #ifndef AllTarget #define AllTarget(depends) @@\ ! all:: depends #endif /* AllTarget */ #ifdef DefineOldLibraryRules --- 3263,3269 ---- */ #ifndef AllTarget #define AllTarget(depends) @@\ ! all:: ProgramTargetName(depends) #endif /* AllTarget */ #ifdef DefineOldLibraryRules |
C++の場合は、SimpleCplusplusProgramTargetとなります。一つのソースから一つの実行ファイルを構築するときに使用できます。
DEPLIBS = SimpleCplusplusProgramTarget(hello) |
このImakefileによって、ソースファイルhello.cpp(拡張子は.cc/.Cでも可)から実行ファイルhelloをビルドするためのMakefileを生成します。Simple(Cplusplus)ProgramTargetによって以下のmakeターゲットが生成されます。
日本語環境では
make: *** $s` $s' に必要なターゲット ` $s' を make するルールがありません $s。中止。
英語環境では
make: *** No rule to make target `hello.man', needed by `hello._man'. Stop.
のようなエラーが発生するかもしれません。日本語のエラーは意味不明ですが英語にすると分かります。hello.manがないが、これを生成するルールが分からないとおっしゃっています。もっともです。ちゃんとmanファイルを書けばよいのですが、回避策としてはtouchコマンド等で空のmanファイルを作成しておきます。
C++の場合は、ComplexCplusplusProgramTargetとなります。複数のソースから一つの実行ファイルを構築するときに使用できます。
SRCSシンボルにソースファイルの名前をリストします。
OBJSシンボルにソースファイルからコンパイルされる中間オブジェクトファイルの名前をリストします。
LOCAL_LIBRARIESに指定したライブラリがそのままリンク時に指定されます。
SYS_LIBRARIESには、システムライブラリの中でリンクするものを指定します。そのため、-l形式で指定しています。
DEPLIBSは、ComplexProgramTargetで指定したターゲットを構築するのに依存するライブラリファイル名を指定します。デフォルトではXlib、Xext、Xt、Xmu、Xawが設定されているので不要な場合は空定義しておきます。
Complex(Cplusplus)ProgramTargetは、OBJSに指定したファイルに依存するターゲットを実行するルールを生成します。
SRCS = add.cpp sub.cpp mul.cpp div.cpp OBJS = $(SRCS:.cpp=.o) LOCAL_LIBRARIES = $(TOP)/lib/libabc.a SYS_LIBRARIES = -lxyz DEPLIBS = ComplexCplusplusProgramTarget(cal) |
以下のmakeターゲットが生成されます。
C++の場合は、ComplexCplusplusProgramTarget_Nとなります。ComplexProgramTargetでは、一つのMakefileの中で一つのプログラムしかビルド指定することができません。そこで、複数のプログラムをビルドする際にこのマクロルールを使用します。Nには1から10の値を指定することができます。
PROGRAMS = prog1 prog2 prog3 SRCS1 = prog1.cpp add.cpp OBJS1 = $(SRCS1:.cpp=.o) DEPLIBS1 = $(DEPXLIB) SRCS2 = prog2.cpp sub.cpp OBJS2 = $(SRCS2:.cpp=.o) DEPLIBS2 = SRCS3 = prog3.cpp mul.cpp div.cpp OBJS3 = $(SRCS3:.cpp=.o) DEPLIBS = ComplexCplusplusProgramTarget_1(prog1,$(XLIB),NullParameter) ComplexCplusplusProgramTarget_2(prog2,NullParameter,NullParameter) ComplexCplusplusProgramTarget_3(prog3,NullParameter,-lm) |
C++の場合は、NormalCplusplusProgramTargetとなります。一つのMakefileでいくつでも指定できますが、指定がやや面倒なことと、生成されるターゲットが少ないので欲するならば手動でターゲットを生成するマクロを記述しなくてはなりません。
SRCS1 = prog1.cpp add.cpp OBJS1 = $(SRCS1:.cpp=.o) SRCS2 = prog2.cpp sub.cpp OBJS2 = $(SRCS2:.cpp=.o) SRCS3 = prog3.cpp mul.cpp div.cpp OBJS3 = $(SRCS3:.cpp=.o) AllTarget(prog1) NormalCplusplusProgramTarget(prog1,$(OBJS1),$(DEPXLIB),$(XLIB),NullParameter) InstallProgram(prog1,$(BINDIR)) InstallManPage(prog1,$(MANDIR)) AllTarget(prog2) NormalCplusplusProgramTarget(prog2,$(OBJS2),NullParameter,NullParameter,NullParameter) InstallProgram(prog2,$(BINDIR)) InstallManPage(prog2,$(MANDIR)) AllTarget(prog3) NormalCplusplusProgramTarget(prog3,$(OBJS3),NullParameter,NullParameter,-lm) InstallProgram(prog3,$(BINDIR)) InstallManPage(prog3,$(MANDIR)) DependTarget() LintTarget() |
make all、make install、make install.man、make depend、make lint は生成しないので、Imakefile中に手動でそれぞれのターゲット生成用マクロを記述しています。
要調査ターゲット
SRCS = add.cpp sub.cpp mul.cpp div.cpp OBJS = $(SRCS:.cpp=.o) NormalLibraryObjectRule() NormalLibraryTarget(calc,$(OBJS)) InstallLibrary(calc,$(USRLIBDIR)) DependTarget() |
※要検証
#define IHaveSubdirs #define PassCDebugFlags SUBDIRS = proga progb ...
C++の場合、#define PassCPlusPlusDebugFlags(ないので他にあるか調査)
サブディレクトリも対象にMakefileを生成するには
$ make Makefiles
を実行する