メイクのしかた

あー、 お化粧の話ではないので、検索エンジンからそちら方面でこられた方はご容赦を〜。 (いないいない)

私は美しい(帰れ)


UNIX gcc編

●main関数をもつ実行プログラムを作る場合
Makefileはこのようになります。 ここでは sample.cをコンパイルして、sampleを作ります。

CC      = gcc
CFLAGS  = -O2 -I/usr/local/include
LIBS    = -L/usr/local/lib -ltcl8.4 -lm
.c.o:
	$(CC) -c $(CFLAGS) $*.c
sample: sample.o
	$(CC) -o $@ sample.o $(LIBS)

-Iと-Lには、それぞれtcl.hがあるディレクトリとlibtcl8.3.so があるディレクトリを指定します。 SunOS、FreeBSDなど一部のUNIXでは「libtcl83.a」 となっていることがあるので、その場合は「-ltcl8.3」を「-ltcl83」とします。 いくつかのUNIX系OSでは、 リンクのときに「-lm」 の他にいくつかのライブラリもリンクする必要があります。 例えばSolarisなら「-lnsl -lsocket」、 Linuxなら「-ldl」、HP-UXなら「-ldld」などです。

●Tkを使い、main関数をもつ実行プログラムを作る場合
Makefileはこのようになります。 ここでは sampletk.cをコンパイルして、sampletkを作ります。

CC      = gcc
CFLAGS  = -O2 -I/usr/local/include -I/usr/X11R6/include
LIBS    = -L/usr/local/lib -ltk8.4 -ltcl8.4 \
          -L/usr/X11R6/lib -lX11 -lm
.c.o:
	$(CC) -c $(CFLAGS) $*.c
sampletk: sampletk.o
	$(CC) -o $@ sample.o $(LIBS)

今度はTkのライブラリと Xウィンドウシステムのライブラリをリンクする必要があります。

●共有ライブラリを作る場合
Makefileは下のようになります。 ここではsample.cをコンパイルして、libsample.soを作ります。

CC      = gcc
SHLD    = gcc -shared
CFLAGS  = -O2 -fPIC -I/usr/local/include
LIBS    =
.c.o:
	$(CC) -c $(CFLAGS) $*.c
libsample.so: sample.o
	$(SHLD) -o $@ sample.o $(LIBS)

共有ライブラリを作るときのコンパイラやリンカーのオプションはOS と処理系によって全く違います。 ここでは万国共通なフリーのコンパイラgccを使っているので、 どのOSでもほぼ共通です。Tclのライブラリは、 動的ロードが可能なOSの場合は上のようにリンクしなくても構いません。 gccを使う場合は、コンパイラのオプションに「-fPIC」をつけ、 リンカーのオプションに「-shared」をつけます。ヒューレットパッカードの HP-UXなど一部のOSでは、 -sharedでリンクする全てのオブジェクトモジュールが必ずロケーション独立 (-fPICをつけてコンパイルしたもの)でなくてはいけません。 また、できあがる共有ライブラリの拡張子は、 そのOSの標準のものにする必要があります。 大部分のOS(SunOS,Solaris,IRIX,Linux,FreeBSDなど)は上のように「.so」 ですが、HP-UXの「.sl」のように例外もあります。 なお、gccではなくシステムのccコンパイラを使う場合はコンパイルオプションをマニュアルなどで調べてください。 一例をあげると、シリコングラフィックスのIRIXでは コンパイラオプションは特になしでリンカーに「-shared」をつけます。 FreeBSDはccコンパイラのスイッチはgccと同じでリンカーの起動を 「ld -Bshareable -x」とします。 HP-UXではコンパイラオプションに「+z」または「+Z」をつけます。 最後にLinuxはccコンパイラはイコールgccコンパイラですから、 どちらを使っても全く同じになります。


MS-Windows VC++編

今度は、Microsoft Visual C++のCL.EXEとLINK.EXEを呼び出す NMAKE に読ませる Makefile の例です。

●main関数をもつ実行プログラムを作る場合
ここでは sampletk.cをコンパイルして、sampletk.exe を作ります。

CC	= cl
TCLROOT	= \Program Files\TCL
CFLAGS	= /nologo /O2 /I"$(TCLROOT)\include"
LDFLAGS	= /nologo
LIBS	= "$(TCLROOT)\lib\tcl85.lib" "$(TCLROOT)\lib\tk85.lib"
.c.obj:
	$(CC) $(CFLAGS) -c $*.c
sampletk.exe: sampletk.obj
	$(CC) /Fe$@ $(LDFLAGS) sampletk.obj $(LIBS)

この例では、環境変数PATHをCL.EXEなどに通るようにしておき、 環境変数INCLUDEとLIBにそれぞれ\msdev\includeと\msdev\lib など処理系標準のヘッダファイルとライブラリのディレクトリをセットしておく必要があります。 これでできるプログラムは、 統合開発環境で言うところの「コンソールアプリケーション」になります。 Tkを使わない場合にはtk85.libをリンクしなくてもOKです。
なお、/Feオプションは、以前のVC++では/oというオプションでした。 Visual Studio 2008(ver9.0)では、/oは「推奨されない」と表示されたので、 随時書き換えていく予定ですが、 まだ昔の記法が残っているかもしれません。

●Tkを使い、winMain関数をもつ実行プログラムを作る場合
Makefileは下のようになります。 Tcl/Tk処理系のソースプログラムwinMain.cを改造してオリジナルのwish を作る場合はこれになります。 ここでは sampletk.cをコンパイルして、sampletk.exe を作ります。

CC	= cl
TCLROOT	= \Program Files\TCL
CFLAGS	= /nologo /O2 /I"$(TCLROOT)\include"
LDFLAGS	= /nologo /SUBSYSTEM:WINDOWS
LIBS	= "$(TCLROOT)\lib\tcl83.lib" "$(TCLROOT)\lib\tk83.lib"
.c.obj:
	$(CC) $(CFLAGS) -c $*.c
sampletk.exe: sampletk.obj
	$(CC) /Fe$@ $(LDFLAGS) sampletk.obj $(LIBS)

どこか1か所だけ変わっていますね。 この場合はTkを使わないことはありえないので必ずTkのライブラリもリンクします。

●ダイナミックリンクライブラリ(DLL)を作る場合
Makefileはこのようになります。 ここでは sample.cをコンパイルして、libsample.dll を作ります。

SHLD	= cl /LD
TCLROOT	= \Program Files\TCL
CFLAGS	= /nologo /O2 /I"$(TCLROOT)\include"
LDFLAGS	= /nologo
LIBS	= "$(TCLROOT)\lib\tcl83.lib" "$(TCLROOT)\lib\tk83.lib"
.c.obj:
	$(CC) $(CFLAGS) -c $*.c
libsample.dll: sample.obj
	$(SHLD) /Fe$@ $(LDFLAGS) sample.obj $(LIBS)

MS-Windowsでは、ライブラリを作る場合でも必ず Tcl/Tk のライブラリをリンク時に指定する必要があります。 CL.EXEのDLLを作るオプションは「/LD」ですが、 リンカーLINK.EXEを直接呼び出しても構わないでしょう。
あれこれサード・パーティ提供のライブラリを活用してオリジナルのDLL を作ろうとした際にVC++で出くわす最大の難関が defaultlib「MSVCRT」やらdefaultlib「LIBCMTD」やらが 「他のライブラリの使用と競合しています。/NODEFAULTLIB:libraryを使用してください。」 と出る場合です。 指示に従って/link /NODEFAULTLIB:MSVCRT をcl.exeのオプションにつけて解決すればいいのですが、 今度は「未解決の外部シンボル○○が参照されました」 というエラーになってもうこの生活どうすりゃいいの? 状態になることもしばしば。 この原因は、各オブジェクトをコンパイルしたときのcl.exeのオプションが、 /MD、/MT、/MTdが混じっていることです。 それぞれ、/MDを指定するとMSVCRT.LIB、 /MTならLIBCMT.LIB、/MTdならLIBCMTD.LIBをリンクしようとするのですが、 混ざっているとリンク時にシンボル(関数)が競合します。 根本的な解決策は、全てのプログラムをコンパイルする際の上記のオプションを統一することです。 (そして、多分それだけが根本的な解決策です)
また上の問題を調査する際に使える、 Tclマニュアルのloadコマンドの項目にも記載されているこねたを1つ。 頑張って作ったDLLをloadコマンドやpackage requireコマンドでロードしようとしたとき、 「this library or a dependent library could not be found in library path」 というエラーが出ることがあります。 このDLLのパスが間違っていないとしたら、 依存するDLLが何か足りないに違いありません。 何というDLLがパスに足りないのか、どうやって調べればいいのか? 実はDUMPBINというコマンドがあって、 「dumpbin -imports {今回作ったDLL}」を実行すると、 他のDLLに依存しているシンボル(関数)名とそのDLL名が表示されるので、 「何かが足りない」状態から「何が足りない」のかが分かる状態へとエヴォリューションするでありましょう。


Borland C/C++編

ボーランドのC/C++コンパイラ(BC++)はときどき雑誌の付録に収録されているので、 格安で手に入ります。Tcl/Tkの拡張はこのBC++でもちゃんとビルドできますが、 Tcl/Tk処理系についてくるライブラリがVisual C++向けなので、 少しだけ事前準備が必要です。

 まず、BC++の付属ユーティリティimplibを使って、 TclとTkのBC++用インポートライブラリを作ります。

  implib -a tcl84imp.lib C:\progra~1\tcl\bin\tcl84.dll
  implib -a tk84imp.lib  C:\progra~1\tcl\bin\tk84.dll
-a は、マイクロソフトのコンパイラが__CDECLの関数名の頭に自動的に「_」 をつける処理に互換性をもたせるスイッチで、必須です。 で、この tcl84imp.lib と tk84imp.lib を適当な場所、 ここでは C:\Program Files\Tcl\Lib に置いておきます。

●main関数をもつ実行プログラムを作る場合
次に下のようなMakefileを作ります。 ここではsample.cをコンパイルして、sampletk.exeを作ります。

TCLROOT    = \program files\tcl
CC         = bcc32
CFLAGS     = -O2 -I"$(TCLROOT)\include"
LIBS       = "$(TCLROOT)\lib\tcl83imp.lib" "$(TCLROOT)\lib\tk83imp.lib"
.c.obj:
	$(CC) $(CFLAGS) -c $?
sampletk.exe: sampletk.obj
	$(CC) -e$@ sample.obj $(LIBS)

●ダイナミックリンクライブラリ(DLL)を作る場合
Makefileは次のようになります。なお、 BC++ではtcl.hで定義されているマクロDLLEXPORTが効かないので、 「__declspec(dllexport)」とWindows風味ばりばりに書く必要があります。

TCLROOT	= C:\Program Files\tcl
CC	= bcc32
SHLD	= bcc32 -tWD
CFLAGS	= -O2 -I"$(TCLROOT)\include"
LIBS	= "$(TCLROOT)\lib\tcl83imp.lib" "$(TCLROOT)\lib\tk83imp.lib"
.c.obj:
	$(CC) $(CFLAGS) -c $*.c
libsample.dll: sample.obj
	$(SHLD) -e$@ sample.obj $(LIBS)


MinGW編

 Windows環境でのgccコンパイラMinGWは、基本的にはgccで、 makeも付属しています。 (私は動作が謎っぽかったので、Cygwinのmakeを使っていますが) 従って、MakefileはUNIX環境におけるそれとほぼ同じといってよいでしょう。 二重書きになるとあれなので、ここでは共有ライブラリの例だけを載せます。

CC      = gcc
SHLD    = gcc -shared
CFLAGS  = -O2 -fPIC -I/cygwin/usr/local/include
LIBS    = -ltcl84 -lm
.c.o:
	$(CC) -c $(CFLAGS) $*.c
libsample.so: sample.o
	$(SHLD) -o $@ sample.o $(LIBS)

MinGWはCygwinと組み合わせて使うことが多いと思いますが、 MinGWのコンパイラはCygwinのコンパイラと違い、 Cygwinのファイルシステムのエミュレーション(仮想的なマウントポイント) を認識せず、 -Iや-Lで指定するパスはあくまでC:\を基準のパスとなります。 (スラッシュ区切りでなく、バックスラッシュ区切りのパスも認識します) ビルドしてできる.soファイルは、いわゆるDLLです。 ですから、.dllという名前でビルドしても別に構いません。 これは慣用的なモノで、別にどちらでもよいでしょう。


Cygwin編

 Cygwin環境の場合、まず注意が必要なことは、Tcl/Tkは、CygwinのUNIXエミュレーション (CYGWIN1.DLL)をサポートしていないことです。 最近のバージョンでは、Tcl/Tkの処理系自身をビルドするときに、 Cygwinで-mno-cygwinスイッチをつけずにビルドしようとすると、 エラーメッセージが出て止められるようになっています。 従って、普通にCygwin環境でのTcl/Tkというと、-mno-cygwinスイッチのついた状態を想定します。 その環境下において、DLLをビルドするMakefileのサンプルは次のようになります。 つまり、コンパイラオプションに-mno-cygwinを追加するほかは、 MinGWや、一般的なUNIXでのgccの場合と変わりません。

CC      = gcc
SHLD    = gcc -shared
CFLAGS  = -mno-cygwin -O2 -I/cygwin/usr/local/include
LIBS    = -ltcl84 -lm
.c.o:
	$(CC) -c $(CFLAGS) $*.c
libsample.so: sample.o
	$(SHLD) -o $@ sample.o $(LIBS)

-mno-cygwinをつけてビルドしたDLL(ここでは.soという拡張子にしていますが、 MinGWの場合と同様、.dllという拡張子の方がよければそうしてもOKです)は、 Cygwinのマウントポイントの外にコピーして使うこともできます。 実際、Cygwinの-mno-cygwinでビルドしたDLLを、ActiveStateのバイナリ配布や、 他のコンパイラでビルドしたtclshやwishからロードして使うこともできます。

なもなも top
(first uploaded 1999/07/11 last updated 2010/09/20, MISUMI URANO)