※ ChatGPTを利用し、要約された質問です(原文:移植性の高いmakefileの作成)
移植性の高いmakefileの作成
このQ&Aのポイント
C言語ソースファイルをmakeツールでビルドする際の移植性向上のための問題について。BorlandのmakeツールとGCC系列のMinGWを使用する場合の問題点と対策方法を検討している。
BorlandのmakeツールとGCC系列のMinGWを同一のmakefileで対応する方法について検討している。ifdefやincludeディレクティブの互換性の問題があり、条件分岐やファイルの切り替えのテクニックを探している。
FireFox3.0のソースコードを参考に移植性の高いmakefileの記述方法を模索している。ifdefやincludeディレクティブの違いによる文法エラーの解決策を求めている。条件付マクロのテクニックも試したが失敗している。
移植性の高いmakefileの作成
C言語で書かれたソースファイルをmakeツールでビルドしています。
もともとコンパイラとして「Borland C++ Compiler 5.5.1」を使用し、Borland C++ 同ツール付属のmakeツール「MAKE Version 5.2」を使用していました。
makeの基本文法は理解しており、Borlandのコンパイラを使用している間は特に問題ありませんでした。
しかし最近、新たにGCC系列のMinGWという開発環境一式と、補助ツールMSYSを導入し、既存のmakefileをGCCおよびGNUmakeに対応させることにしました。
具体的には、
GMAKEを起動した場合はgccでコンパイルする。
Borland make を起動した場合にはbcc32でコンパイルする。
という切り替えを同一のmakefileで行おうと考えました。
しかし、ここで問題が発生しました。
makeの依存関係部分はborland付属make(以下Bmakeとします)とGNUmakeであまり違いはないのですが、単一のmakefileで対応しようとすると、どうしてもコンパイラオプションやコンパイルコマンドを切り替える部分が必要です。しかし、ifdefやincludeディレクティブに関してはお互いにまったく互換性がなく、片方のmakeで有効なフラグをつかってifdefで分岐しようとしても、もう片方では完全に文法エラーです。
例えば、ifdefやincludeディレクティブは、Bmakeでは先頭に!が必要ですが、GNUmakeには必要ありません。
以下の書籍でポータブルなmakefileの記述に関していろいろ調べたのですが、Bmakeの特殊な文法についての言及がなく、対応策が見つかりません。
様々な環境でビルドされるFireFox3.0のソースコードも参考にしようとmakefileと思われるファイルを全て検索したところ、1769個ほど見つかりました。
その中で!ifdefディレクティブを使用しているのは極わずかで、Makefile.winという名称のものがいくつかあっただけでした。
しかもそれらは最後の方にincludeディレクティブも使っていました。
例えば、MozillaFirefox3.0のソースコードにあるmakefaileの内、firefox-3.0-source.tar\mozilla\dbm\src\Makefile.winは「include <$(DEPTH)/config/rules.mak>等」、という一文がある一方、その他のifなどのディレクティブは頭に!をつけられており、全体としてはBmake(多分ターゲットはMSVC++だと思いますが。)です。
これはBmakeではエラーになるはずなのですが、動作機構は不明です。
また、条件付マクロというテクニックで、ある値が定義されていれば左値を、定義されてなければ右値を使ってマクロ置換するテクニックがあったので、これでディレクティブをマクロ置換してやろうと考えました。しかし、ディレクティブをマクロ定義することはできないようでこれも失敗しています。
#INCLUDE というマクロを定義して、
INCLUDE = $(_GNUMAKE_?include:!include)
#マクロをディレクティブ命令として使う
$(INCLUDE) testmake.mk
それぞれのmake専用makefileを作るのは簡単ですが、新しいプログラムを追加するたびに複数のmakefileを書き直すのは避けたいのです。
長々と書きましたが、解決策として次の2つのいずれかのようなものがあれば教えていただきたいです。
1. ifやincludeディレクティブに代わる、GNUmakeとBmake両方に使えるテクニック。
2. 起動したmakeによって条件分岐、もしくは読み込むファイルを切り替えるテクニック。
よろしくお願いいたします。
開発環境
OS: winXP
コンパイラ: 「Borland C++ Builder 6.0」「Borland C++ Compiler 5.5.1」「gcc version 3.4.5 (mingw special)」
make: 「MAKE Version 5.2 (C++ BuilderとC++ Compiler 5.5.1はおなじmakeを使用している模様)」「GNU Make version 3.79.1」
参考文献
1. 「GNU Make 第3版」オライリージャパン、2005年
2. 「C言語逆引き大辞典」秀和システム、2003年
3. 「GNUソフトウェアプログラミング」、?年
お礼
回答ありがとうございます。 依存関係ファイルを共有にし、それを読み込むファイルを切り替えるということですね? 調べてみたところ、 GNUMakeは、「GNUmakefile」、「makefile」、「Makefile」の順でファイルを探す。 それ以外のmakeは「makefile」か「Makefile」というファイルを探す。 ということですので、この方法で良さそうです。考えてみたら、includeされるファイルを切り替えるより、最初に読み込まれるファイルを切り替えて共有の依存関係を読み込むほうが自然ですね・・・。 ご助言を参考に以下の3ファイルを作り、現在正常に動いております。 GNUmakefile ・・・・・GNUMakeに読み込まれて、GNUMake用のマクロを設定し、依存関係ファイルComonMakefile.inを読み込む。 makefile ・・・・・・BorlandMakeに読み込まれて、BMake用のマクロを設定し、ComonMakefile.inを読み込む。 ComonMakefile.in・・・依存関係と、マクロを使った明示的な構築ルール、サフィックスルールを設定する。 これでうまくいきそうです。ありがとうございました。
補足
今後、同じ内容で質問される方のために補足で情報をまとめときます。参考までに。 同じ環境内でGNUMakeとBorlandMake(MSVC++のmakeであるnmakeとほぼ互換)を使用できるようにする。 依存関係ファイルは書式が共通なので、ひとつのファイルにまとめる。ファイル名「ComonMakefile.in」。 このファイルを読み込むmakeファイルを2系統作る。ファイル名指定無しで、デフォルトで読み込むファイル名の違いを使って、二つのmakeを使い分ける。 GNUMakeは最初に「GNUmakefile」というファイルを読み込むので、このファイルにGNUmake特有の設定やincludeを行う。 Borland Makeは最初に「makefile」を読み込む(Win環境ではMakefileと同じ)ので、このファイルにBmake特有の設定や !include などを行う。 以下に3つのファイルの例を挙げときます。 「GNUmakefile」 # コンパイルとリンク CC = gcc -o #コンパイルのみ(compile only) CCO = gcc -c -o # リソースの付加 RC = # # ファイルの消去 RM = rm -f # 通常使うプログラムの依存関係makefile include ComonMakefile.in # 一個ずつコンパイルするときに、フルネームでなく短いターゲット名を使うためのメイクファイル include sname.mk # 正式ではない、テスト段階のプログラムのメイクファイル include testmake.mk 「makefile」 # コンパイルとリンク CC = bcc32 -e # コンパイルのみ(compile only) CCO = bcc32 -c -o # リソースの付加 RC = brc32 -v # ファイルの消去 RM = del # 通常使うプログラムの依存関係makefile !include ComonMakefile.in # 一個ずつコンパイルするときに、フルネームでなく短いターゲット名を使うためのメイクファイル !include sname.mk # 正式ではない、テスト段階のプログラムのメイクファイル !include testmake.mk 「ComonMakefile.in」 # サフィックスルール .c.exe: ____CC $(*<).c ALLEXE = hello.exe helloC.exe all: $(ALLEXE) clean: ____$(RM) *.obj ____$(RM) *.tds ____$(RM) icon\*.res ____$(RM) *.bak hello.exe: hello.obj Lib-helloprint.obj ____$(CC)hello.obj Lib-helloprint.obj helloC.exe: helloC.obj Lib-helloprint.obj ____$(CC)helloC.obj Lib-helloprint.obj # 本体の依存関係 hello.obj: hello.c ____$(CCO)hello.obj hello.c helloC.obj: helloC.c ____$(CCO)helloC.obj helloC.c # ライブラリの依存関係 Lib-helloprint.obj: Lib-helloprint.c ____$(CCO)Lib-helloprint.obj Lib-helloprint.c Lib-testprint.h