「これはヤバかったな」という遅刻エピソード

Windows7でmingwのgccを使っています。
win32apiを使っての開発の件でリソーススクリプトが有る場合と無い場
合とではmakefileの中身が違います。

リソーススクリプトが有る場合のmakefileは、下記です。
SRC=01.c
OBJS=$(SRC:.c=.o)
RC=02.rc
OBJS+=$(RC:.rc=.o)
PROG=01.exe
CC=gcc
MENU=windres
CFLAGS=-Wall -O3 -finput-charset=cp932 --exec-charset=cp932
LDFLAGS=-mwindows
RM=rm


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

%.o: %.rc
$(MENU) $(RC) $*.o

.PHONY : all
all: $(PROG)

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

.PHONY : clean
clean:
$(RM) $(OBJS)

リソーススクリプトが無い場合のmakefileは、下記です。
SRC=01.c
OBJS=$(SRC:.c=.o)
PROG=01.exe
CC=gcc
CFLAGS=-Wall -O3
#LDFLAGS=-mwindows
RM=rm


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

.PHONY : all
all: $(PROG)

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

.PHONY : clean
clean:
$(RM) $(OBJS)

上記二つのmakefilleをif文を用いて一つのmakefileに統一をしたいの
ですが。if文の書き方が分かりません。

済みません。makefileの提示をお願いします。
宜しく、お願いします。

質問者からの補足コメント

  • うーん・・・

    先程のお礼の所で、$(cc)が2回出て来ると言うのがおかしいと
    書きましたが、おかしくは無いと思います。と言うのは、

    最初のはC言語のコンパイル、
    2回目は、リンクの関係ですよね

    失礼しました。

    No.1の回答に寄せられた補足コメントです。 補足日時:2016/03/25 19:12
  • うーん・・・

    構文的に合っているのかは分かりませんが、
    UNAME = $(shell dir /b *.rc)
    上記を実行して、rcファイルが無い場合を条件分岐
    が出来れば解決をする様に思うのですが、
    どうなんでしょうか。

    No.4の回答に寄せられた補足コメントです。 補足日時:2016/03/27 02:40
  • うーん・・・

    お礼の中で最後の
    01.oと02.oから、01.exeを作るのは
    rcが無い時に走らせる事は出来ないですね。

    だから、この部分も飛ばす事が出来る
    makefileと言う事になります。
    然し、この場合のmakefileはターゲットが二つ存在をする
    と言う事は原理的には出来ない様な気がするのですが。

    ならば、最初からプログラムの方でrcファイルの有無でmakefile
    を作ると言うのが本来のやり方なんでしょうか。

    makefileの中での制御と言うのは、その仕様上無理な様な気も
    するんですが。どうなんでしょうか。

    No.5の回答に寄せられた補足コメントです。 補足日時:2016/03/27 10:16

A 回答 (6件)

何度も同じこと言わせないでください。


理解できないのなら、どの箇所が理解できないのか、具体的に書いてください。

> gcc -Wall -O3 -finput-charset=cp932 --exec-charset=cp932 -o 01.o -c 01.c
> windres 02.rc 02.o
> gcc 01.o 02.o -mwindows -o 01.exe

> ① 01.cから01.oを作る
> ② 02.rcから02.oを作る
> ③ 01.oと02.oから01.exeを作る

それは結果でしかありません。

・01.exe を作るためには、 01.o と 02.o が足りない → 用意しよう
 ・ 01.o が存在しないから、作ろう
   01.c があるから、 %.o: %.c のルールを使おう
   > ① 01.cから01.oを作る
   > gcc -Wall -O3 -finput-charset=cp932 --exec-charset=cp932 -o 01.o -c 01.c
 ・ 02.o が存在しないから、作ろう
   02.rc があるから、 %.o: %.rc のルールを使おう
   > ② 02.rcから02.oを作る
   > windres 02.rc 02.o
 01.o と 02.o が 用意できたから作ろう
 > ③ 01.oと02.oから01.exeを作る
 > gcc 01.o 02.o -mwindows -o 01.exe

という流れの中で、 ①②③が目に見えている、というだけです。


くどいかもしれませんが、エラーの原因は「01.exeを作るのに、02.oが必要だと指定してあるのに、02.oが無い。作り方もわからない」です。
「02.rcが無い」ことに拘っているようですけど、そこは枝葉です。


Makefile は 「if」と書いてないだけで、実質は「条件分岐」の集まりです。
特殊な事情が無ければ、if を使う必要はありません。

%.o: %.rc
 $(MENU) $(RC) $*.o



%=具体的なファイル名の一部
if %.o が必要 && %.rc が存在する && %.oが%.rcより古い then
 $(MENU) $(RC) $*.o
end if

と書いてあるようなものです。



----------------
あなたのやりたいことは

02.rcがあるとき

RC=02.rc

となっている箇所を、02.rcが無い場合には

RC=

と書き換えるだけ済むことなんです。
そうすると
OBJS+=$(RC:.rc=.o)

・RC=02.rcのとき:
RCの.rcを,oに置き換えた「02.o」がOBJSに追加される
・RC=のとき:
RCの.rcを,oに置き換えた「」がOBJSに追加される。つまりは OBJS にはなにも追加されない
となり、あなたのやりたいことが実現されます。


これを自動にする方法の一つが
RC=$(wildcard *.rc)
です。
02.rc が存在すれば、ワイルドカードにマッチして
RC=02.rc
と書いたのと同じことに、存在していなければ
RC=
と書いたのと同じことになります。



-------------


例えば、以下のMakefileは作り方としては間違っていますが動作します。
02.rcがある方のディレクトリで、 make cleanのあとで makeしてみてください。
02.cも01.rcも存在しませんが、コンパイル自体はちゃんとできてしまいます。
ーーー
SRC=02.c
OBJS=$(SRC:.c=.o)
RC=01.rc
OBJS+=$(RC:.rc=.o)
PROG=01.exe
CC=gcc
CFLAGS=-Wall -O3
#LDFLAGS=-mwindows
RM=rm


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

%.o: %.rc
 $(MENU) $< $@

.PHONY : all
all: $(PROG)

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

.PHONY : clean
clean:
 $(RM) $(OBJS)
    • good
    • 0
この回答へのお礼

有難う御座います。

出来ました。
然し、これはそんなに時間のかかる説明になるのでしょうか。
若しも、これを知っている方だったらここ迄長い説明には
ならないとは思いますが。

折角回答をされた方には申し訳ありませんが。
私ならもっと簡単にこれ位の説明は出来ますが。

何故に最初に一発でmakefileの提示をしてくれなかったので
しょうか。

私の質問が分からないと書いてありましたが。私の質問と言うのは
makefileを提示して下さい。それだけですが。

然し、それに対しては一切の提示は有りませんでした。ヒントとか解説
は確かに有りましたが。それは、私の求めている質問でも
求めている回答でも有りません。

前後の文脈を見れば分かるとは思いますが。

折角質問に回答して貰って気を悪くされたら謝ります。
然し、返す返す残念です。
私が若しもこのスキルが有ったとしたら最初の質問で一発で回答させて
質問者に大満足をさせられますが。

有難う御座いました。

お礼日時:2016/03/27 15:34

まずお詫び。


> c:\MinGW\bin\windres.exe: リソースがありません
windresが手許に無いので、空のリソースファイルが変換できないということを確かめずに書きました。


本題。
そのエラーが出る仕組みは、#4に書いた通りです。
拙い文章で申し分けないですが、理解してください。
 %.o: %.rc
  $(MENU) $(RC) $*.o
が原因ではありません。
実際、この2行を削除しても、
 make: *** '01.exe' に必要なターゲット '02.o' を make するルールがありません. 中止
と同じエラーになります。



SRC=01.c
OBJS=$(SRC:.c=.o)
RC=02.rc
OBJS+=$(RC:.rc=.o)



SRC=01.c
RC=02.rc
OBJS=01.o 02.o

と等価になります。
さらに言えば、SRC,RCは使われていないので、実質

OBJS=01.o 02.o

だけのものと等価です。
※ $(MENU) $(RC) $*.o で RCが使われていますが、この書き方が問題なのは既に書いた通り。
$(CC) $(CFLAGS) -o $@ -c $< と同様に $@や$<等を使うのがセオリーです。

この状態では
$(PROG): $(OBJS)

01.exe: 01.o 02.o
です。
ここで、 存在しないし、作り方もわからない 02.o があるのが「ルールがありません」エラーの直接の原因です。

実際に、上4行を
OBJS=01.o 02.o
だけにした場合と
OBJS=01.o
だけにした場合を確認してみてください。

---
GnuMakeには、条件分岐があります。
http://www.ecoop.net/coop/translated/GNUMake3.77 …

if .rc が存在する
 #ルール追加
 %.o: %.rc
  $(MENU) $(RC) $*.o
endif

みたいな感じで解決すると思っているなら、それは間違い。このルールが原因じゃないのは前述の通り。

if .rc が存在する
$(PROG): .cから作った.o .rcから作った.o
else
$(PROG): .cから作った.o
endif

となるようにするのが目的にかなっています

ですが、これは、OBJSの値が条件によって目的に沿ったものになっていればいいだけの話です。
ルールをifで条件分岐させるのではなく、OBJSの値を変えるのが普通のやり方です。

if .rc が存在する
OBJS= .cから作った.o .rcから作った.o
else
OBJS= .cから作った.o
endif

ですが、OBJSはSRC,RCを元に作成しています。
それなら
SRC=(存在する).cファイルの一覧
RC=(存在する).rcファイルの一覧
となるようにすれば、OBJSは自動で「必要な.oファイルの一覧」になります。

if .rc が存在する
RC=.rcの一覧
else
#RCを空にする
RC=
endif

ですが、こんなところで分岐使うくらいなら、最初から
RC=存在する.rcファイルの一覧
と書いてしまった方がすっきりします
この回答への補足あり
    • good
    • 0
この回答へのお礼

有難う御座います。
整理をすると、
RCが有ると、下記が走ります。
01.cと02.rcの時には、

gcc -Wall -O3 -finput-charset=cp932 --exec-charset=cp932 -o 01.o -c 01.c
windres 02.rc 02.o
gcc 01.o 02.o -mwindows -o 01.exe

① 01.cから01.oを作る
② 02.rcから02.oを作る
③ 01.oと02.oから01.exeを作る

これが、rcが有る時のmakeの手順になります。
rcが無時は、①と③を実行すると言う事をやりたい。

このmakefileを作りたい。その為には、最初はrcの有無に無関係で
①を実行する。問題は、②で条件分岐をしたい。rcが有る時は②を実行
してrcが無い時は、②を実行しないで③を実行する。

rcが無い時に
OBJS+=$(RC:.rc=.o)

%.o: %.rc
$(MENU) $(RC) $*.o

上記の二つの実行をしない様にすれば良いとは思いますが。
これは、UNAME = $(shell dir /b *.rc)
の様な感じの変数を使っての条件式での制御は出来ないのでしょうか。

dirと言うのはウインドウズですのでunixではlsだとは思いますが。
どうなんでしょうか。

上記を実際に下記のmakefielに挿入をすると言う事になりますが。
SRC=01.c
OBJS=$(SRC:.c=.o)
RC=02.rc
OBJS+=$(RC:.rc=.o)
PROG=01.exe
CC=gcc
MENU=windres
CFLAGS=-Wall -O3 -finput-charset=cp932 --exec-charset=cp932
LDFLAGS=-mwindows
RM=rm

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

%.o: %.rc
$(MENU) $(RC) $*.o

.PHONY : all
all: $(PROG)

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

.PHONY : clean
clean:
$(RM) $(OBJS)

出来るのでしょうか。

お礼日時:2016/03/27 09:48

> RCファイルが無い場合は、


> %.o: %.rc
> $(MENU) $(RC) $*.o
> 上記はエラーで実行されません

具体的にどんなエラーなんですか?
ただエラーとだけ言われても対策などわかりません。


私の拙い想像力をフルに活用するに。

ディレクトリの中にあるファイル:
 01.c
 makefile (リソース有り版)

という状態で、
 make
を実行したときに、
 make: *** No rule to make target '02.o', needed by '01.exe'. Stop.
または、
 make: *** '01.exe' に必要なターゲット '02.o' を make するルールがありません. 中止.
というエラーになるものでは?

この場合だと、「.rcが無いからエラー」という単純な理由ではありません。
Makefileには
・01.exe は 01.o , 02.o から作る
・〜.c があれば 、 〜.o は 〜.cから作る
・〜.rc があれば 、 〜.o は 〜.rcから作る
と書いてあります。
この状態では
・01.exe を作るためには、 02.o が足りない → 用意しよう
・ 02.o が存在しないから、作ろう
  02.c が無いから、 %.o: %.c のルールは使えない
  02.rc が無いから、 %.o: %.rc のルールは使えない
  02.fが無いから (デフォルトにある) %.o: %.dのルールは使えない
   (以下、 02.? から 02.o を作るルールを全部試す)
 → 02.oを作る方法が見つからない → 「ターゲット '02.o' を make するルールがありません」
となります。

このエラーの根源は「いらないはずの02.oを必要だと指定していること」です。
02.oは OBJS に含まれていています。よって、 OBJSに02.oが含まれないようにすることです。
OBJSに02.oを含むようにしているのは OBJS+=$(RC:.rc=.o) の部分です。
つまりは、 RCに存在しない 02.rc が含まれていることが原因です。

対処方は
・RC= として、 存在しない02.rc を含まないようにする
・wildcard関数を使うなどして、 存在するファイルだけが含まれるようにする
http://www.ecoop.net/coop/translated/GNUMake3.77 …
・02.rcを作る。中身は空でいい
等です。



「どんな状態でも、これ一本で全て対応できる万能Makefile」なんてものを作るのは無理だと思います。
1プロジェクトにMakefileというのが本来の使い方だと思います。Visual Studio でプロジェクト毎に.vcprojが作られるように。
この回答への補足あり
    • good
    • 0
この回答へのお礼

有難う御座います。
RCファイルが無い場合のエラーは、下記です。
gcc -Wall -O3 -finput-charset=cp932 --exec-charset=cp932 -o 01.o -c 01.c
make: *** `01.exe' に必要なターゲット `02.o' を make するルールがありません. 中止.

私が思うには、ここで中止になっているので中止をしなければ
良いと思ったので。

そう言う事が出来なれば、また別の方法をここでは考えないと
いけないと思うのですが。
どうなんでしょうか。

02.rcの中身の無いので実行しましたが、下記のエラーが出ます。
windres 02.rc 02.o
c:\MinGW\bin\windres.exe: リソースがありません

思ったのですが、wildcard *.rcを使って該当しなければ、以降に実行するmake
の処理をif文での制御と言うのは出来るんでしょうか。若し、
それが出来るのであれば、それで解決すると思いますが。

どうなんでしょうか。

お礼日時:2016/03/27 01:25

・Aを作るのに、B1,B2,... が必要。

その際、 Cというコマンドを実行する
・B1を作るのには b1 が必要(以下同様)
・b1を作るのには b'1 が必要(以下同様)
....
・B2を作るのには b2 が必要(以下同様)
・b2を作るのには b'2 が必要(以下同様)
....

というルールに従って、必要なコマンドを実行していく、というのがmakeの仕組です。
この一連の依存関係に含まれないファイルは、例えルールが記述してあっても、実行されません。
実際、makeにはデフォルトで設定されているルールがいっぱいありますが、その多くは実行されません
(例えば、Fortranのソース .f からオブジェクトファイル .oにコンパイルするルールはデフォルトで定義されていますが、質問にあるmakefileではそのルールが使われることはありません)


依存関係にの中に「.rcから変換された.o」がなければ、「.rcから.oへ変換するルール」は適用されないのです。
よって
RC=
とRC の中を空にすれば、 OBJSには 「.rcから変換された.o」 は含まれなくなります。
そのため「.rcから.oへ変換するルール」
%.o: %.rc
 $(MENU) $(RC) $*.o
は適用されません。

----
> 一つのmakefileに統一をしたい

これ、どんな意図で言ってますか?

使用するソースファイル名やターゲットファイル名が違えば、SRC,RC,PROGRAM等は変更する必要があります。
1つのmakefileを雛形にして、SRC,RC,PROGRAMだけを変えて使いたい(01.exeを作るMakefileと11.exeを作るMakefileをできるかぎり少ない変更で用意したい)、ということなら、「リソース有り版」を雛形にできます。
※ それでも、利用するライブラリの違い等で常に同じでできるわけではありませんが。

Makefileは変更せずに、いろんなターゲットをコンパイルしたい、という意図なら、できなくはありません。
ですが、なんらかの方法で、各ターゲットの依存関係を指定する必要があります。
・変数は make PROGRAM=11.exe 等とコマンドラインから指定することができます。
 また、コマンドラインからの指定された変数をifで判定して、 SRC,RC,PROGRAM等を設定する、という書き方もできます。
 → 毎回書かなければいけないので、かえって面倒です。
・複数のターゲットに対応したMakefileを作る
  all: $(PROGRAM1) $(PROGRAM2) 等と、最終ターゲットを複数にし、 PROGRAM1→OBJ1→SRC1,RC1 PROGRAM2→OBJ2→SRC2,RC2 等と各ターゲット用の依存関係を指定する
 → makefileについての多少の知識が必要です。

----
RC=02.rc 03.rc
のと、複数のリソースファイルを使うとき、OBJSには
02.o 03.o
が追加されます。このとき依存関係から
・02.rcから02.oを
・03.rcから03.oを
ということになり、次のルールが適用されます。
%.o: %.rc
 $(MENU) $(RC) $*.o

実際に実行する際には、各変数が展開されて
・02.rcから02.oのとき
 windres 02.rc 03.rc 02.o
・03.rcから03.oを
 windres 02.rc 03.rc 03.o
が実行されます。

今MinGWを試せる環境が無いので実際どうなるかがわかりませんが
・windresが複数の入力ファイルに対応できて無いなら、これらのコマンドはエラーになるはずです。
 あるいは、 02.rcの変換結果が03.rcに上書きされるかもしれません。
・複数の入力ファイルに対応できたとしても、02.oと03.oには同じリソースが重複しています。
 最後のリンクの段階でエラーになります。


このことから、残念ですが、その筆者はMakefileについてあまり詳しく無い(少なくとも、執筆当時は詳しくなかった)と思われます。

余談)
メニュー用リソースをコンパイルするからMENU=windres という理屈なら、 ゲーム用プログラムをコンパイルするのは GAME=gcc ってことになりますよね
変数名は自由だからといって、こんな命名規則を使っている点でも、参考にしない方がよいMakefileです。

----
GnuMake については、書籍、マニュアル(の翻訳)など、資料はいろいろあります。
この先makeのお世話になるなら、プログラムの勉強と一緒に、makeの勉強もしてはいかがでしょうか
    • good
    • 0
この回答へのお礼

有難う御座います。
質問を変えます。

RCファイルが無い場合は、
%.o: %.rc
$(MENU) $(RC) $*.o
上記はエラーで実行されません。

ここで、エラーが発生してもそれを
無視して、
.PHONY : all
all: $(PROG)

$(PROG): $(OBJS)
$(CC) $(OBJS) $(LDFLAGS) -o $@
が実行出来れば良い様に思いますが。

要は、下記がRCが有る時は実行して
無い時は無視をする。
%.o: %.rc
$(MENU) $(RC) $*.o

こう言うのが出来ればRCファイルがどの状態に有っても
makeは実行出来るのではと思いますが。
どうなんでしょうか。

お礼日時:2016/03/26 09:28

「$(cc)が2回出て来ておかしい様な気もします。

」とか「$(cc)が2回出て来ると言うのがおかしいと書きましたが、おかしくは無いと思います。」とか書いてあるのを見ると, 「ひょっとして #1 に書いたことを何ひとつ理解できていないんじゃないだろうか」と不安に思えてしまう. 「$(CC) が 2回出てくる」というのは「リソースファイルがある場合」も「ない場合」も同じでしょ? だとしたら, なんで #1 でわざわざ「リソースファイルがある場合」と限定した (うえにその直後に「リソースコンパイラを動かすところ」とまで書いた) んだろうねぇ. 「リソースコンパイラを動かすところ」がどこか理解していないのかな?

というのをおいてはおかないがまず本質じゃないところから突っ込んでおくと
リソースコンパイラである windres に対して「MENU」という名前を付けた理由がさっぱりわからん
という点はあるな. たとえコピーしたとはいえ, あなたはそこに違和感がなかったんだよね? だとしたら, どうして「MENU という名前でいい」と思ったのか説明できるはずだね.

で本質としては
リソースファイルが複数あったらどうするのか
を考えれば (そして make の挙動を理解していれば) わかるはず.

ああ, 「make の挙動が理解できていない」ならそこから調べてくれ.
    • good
    • 0
この回答へのお礼

有難う御座います。

いつも本質から外れた意見ばかりで困りますが。
的確な回答をお願いします。
名前は適当に処理をしますから。

makefile提示をズバリお願いします。
makefileの提示が出来ないと言うのであれば
貴方も私と同じで出来ないので知らないと言う
事でしょうか。

貴方の過去の回答もこの様な回答が非常に多いので
またかと言う感じになります。若し、これが初めて
言われるので有れば。単に今迄が言われなかっただけの
話です。気を付けましょう。

若しも、理解をしているのならばこれは簡単な
事でしょう。私は理解をしていないので質問を
するので有って。それを理解が足りないと言うのは、
日本語として既におかしい。

簡単にすっきりとする回答はmakefileのみの提示を
お願いします。
また、ここでは例えばmakefileがリソースが有る場合に
単に名前だけの問題であったとしてもここではそれがどうした
と言うのか。

実際に動いていない訳では無いので。動かないと言うので
あれば問題ですが。他の方は、もっとまともな回答を
しますので。

的確な回答をお願いします。回答の中で理解が足りないと
言うのは当然の事です。それを回答の中で理解が足りないと
か、何を言わんのかです。

そんな回答では幼児に何一つとして大人としての素養有る回答
が出来ない結果になります。ちゃんとした回答をお願い
します。他の方は出来ているのですから。

宜しいでしょうか。相手に理解をさせる事が出来て初めて貴方も
理解をしていると言う事になります。それが出来ないので有れば
貴方も私と同様に理解をしていない事になります。

常に、答えと言う物はシンプルです。また、そうあるべきです。
この様な回答は苛立ちを覚えます。

お礼日時:2016/03/26 05:39

「リソースファイルがある場合」と「ない場合」を「RC」という変数の値の有無で区別するなら


ifeq($(strip $(RC)),)
リソースファイルがない場合
else
リソースファイルがある場合
endif
で OK... だけど, そもそも「リソースファイル」を「RC」という変数で表すこと自体奇妙な感じがする.

あと「リソースファイルがある場合」でリソースコンパイラを動かすところは処理がおかしいよ.
この回答への補足あり
    • good
    • 0
この回答へのお礼

有難う御座います。
リソースファイルが有る場合の処理は確かに
$(cc)が2回出て来ておかしい様な気もします。
これは、http://doremi.s206.xrea.com/c/window.html
をコピペした物です。

これは何処がおかしのいでしょうか。

済みません。
この場合の正しいmakefileの提示もあわせて
宜しくお願いします。

お礼日時:2016/03/25 19:08

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!


おすすめ情報