2016/08/16

[勉]make

夏休みぼけしているので、makeというかMakefileについて復習しよう。

このブログを読んでいたのだが、自分が変数代入やコマンドを書いたりするくらいしかやってないことに気付いたので、使いそうな内容だけ復習というか覚えるというか、勉強しようという気になったのだ。
Getting More Out of Make


addprefix

こんなMakefileを作ってみた。

DBG_DIR := ./_debug
SRC := main.c
OBJ := $(SRC:.c=.o)
DBG_OBJ := $(addprefix $(DBG_DIR)/, $(OBJ))
$(info DBG_OBJ=$(DBG_OBJ))

実行すると、こうだ。
DBG_OBJ=./_debug/main.o

じゃあ、これでいいやん!と思ったのだ。

DBG_OBJ := $(DBG_DIR)/$(OBJ)

まあ、SRCがこれだったら別に良いのだが、普通はこんな感じだろう。

SRC := func1.c func2.c main.c

これを、addprefixを使わずに各オブジェクトの先頭に$(DBG_DIR)を付けようとしたら、できるのかもしれないけど大ごとになりそうだ。
addprefixだと、
DBG_OBJ=./_debug/func1.o ./_debug/func2.o ./_debug/main.o
と、ちゃんとそれぞれにつけてくれる。

この技を知らなかったので、自作したMakefileは.oファイルがソースと同じところにできるのであった。。


$(info)

Makefileがうまく動いてくれないときで、とりあえず変数がどうなっているのかを見たいときに使っている。

これを知るまではターゲットでechoさせていたけど、$(info)はターゲット外に書けるので気が楽になった。


.PHONY

使ったことが無い。。。
「偽りのターゲット」で、ターゲットと同じ名前のファイルがあると実行しなくなるので、「これはファイルじゃないんですよ」ということを宣言するみたいだ。

やってみよう。

Makefile:
    @echo Hello Makefile!

$ make Makefile
make: 'Makefile' is up to date.

ふむ、実行されない。

.PHONY: Makefile
Makefile:
    @echo Hello Makefile!

$ make Makefile
Hello Makefile!
make: Nothing to be done for 'Makefile'.

なるほど、実行された。
習慣的に付けるか、困ってから付けるか、だな。


=と:=

私はいつも=で代入しているのだが、NordicのMakefileだと:=と=を使い分けているような気がする。

Makefile で代入の扱い - Qiita

その場で値を評価するか、使用時に評価するからしい。

ifeq ($(VALUE_TEST),y)
HELLO (代入) "hello"
else
HELLO (代入) "bye"
endif

all:
    VALUE_TEST=y
    @echo $(HELLO)

これで、:=だとすぐに代入されるから、VALUE_TESTがyでないので"bye"になり、=だと実行時に評価するから"hello"にな。。。。ならない!
どっちも"bye"になった。

なので、動的というのは、そういう動的ではない、ということだ。

 

all: VALUE_TEST := y
all:
    @echo $(HELLO)

HELLO = value=$(VALUE_TEST)
$(info HELLO=$(HELLO))

$ make all
HELLO=value=
value=y

こういう動的なのだ。
地の文(?)の$(info)の段階では、まだVALUE_TESTは空なので、「value=」しか見えない。
しかしallの中ではyが代入されるので「value=y」になる。

動的というか、:=はその場で評価するが、=は式ごと保持するので、使う段階で評価されるということですな。
こういうタイプは、$(info)で見てもわからないということか。。


$なんとか

よく使うけど、覚えられず、いつもまねしている。
それが$のついた変数だ。
自動変数、と呼ぶらしい。

$@, $%, $<, $?, $^, $+, $*、と、数は7つしかない。
覚えられないのは、覚えようとしていないからか。。。

 

SRC := func1.c func2.c main.c
all: $(SRC)
    @echo "@="$@
    @echo "%="$%
    @echo "<="$<
    @echo "?="$?
    @echo "^="$^
    @echo "+="$+
    @echo "*="$*

これを実行すると、ターゲットfunc1.cを処理するルールがない、と怒られた。
確かにないのだが、touchで空ファイルを作ると怒られなかった。
これが「暗黙のルール」というやつだろうから、.PHONYで対抗した。

.PHONY: func1.c func2.c main.c
SRC := func1.c func2.c main.c
all: $(SRC)
    @echo "@="$@
    @echo "%="$%
    @echo "<="$<
    @echo "?="$?
    @echo "^="$^
    @echo "+="$+
    @echo "*="$*

@=all
%=
<=func1.c
?=func1.c func2.c main.c
^=func1.c func2.c main.c
+=func1.c func2.c main.c
*=

同じように、func1.cのルールを作った。

.PHONY: func1.c func2.c main.c
SRC := func1.c func2.c main.c
all: $(SRC)
    @echo "@="$@
    @echo "%="$%
    @echo "<="$<
    @echo "?="$?
    @echo "^="$^
    @echo "+="$+
    @echo "*="$*

func1.c:
    @echo "func1 @="$@
    @echo "func1 %="$%
    @echo "func1 <="$<
    @echo "func1 ?="$?
    @echo "func1 ^="$^
    @echo "func1 +="$+
    @echo "func1 *="$*

func1 @=func1.c
func1 %=
func1 <=
func1 ?=
func1 ^=
func1 +=
func1 *=func1

@=all
%=
<=func1.c
?=func1.c func2.c main.c
^=func1.c func2.c main.c
+=func1.c func2.c main.c
*=

$@はわかりやすい。
ターゲットの名前だ。

$<もなんとなくわかるのだが、func1.cターゲットの場合には出てこない。
あくまで「依存関係の先頭」だから、依存するものが無いから出てこないのだ。
しかし、使い道はなんだ?

main.o: main.c
    $(CC) -o $@ -c $<

なるほど、オブジェクト名がターゲットで、それと依存関係を持つのはmain.cだから、コンパイルするファイル名は$<ということか。
もう少し一般的にすると、こうらしい。

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

nRF5 SDKのexamplesでは、こういう書き方をしていた。

$(OBJECT_DIRECTORY)/%.o: %.c
    @echo Compiling file: $(notdir $<)
    $(NO_ECHO)$(CC) $(CFLAGS) $(INC_PATHS) -c -o $@ $<

 

$?, $^, $+も「依存関係の」だ。
$^と$+は依存関係を全部並べるタイプで、$?は更新された依存関係だけを並べるタイプだ。
今回はallという偽ターゲットだから、$?で全部出ているのだろうか?

残りは、使う機会があれば覚えよう。
$%がアーカイブ用で、$*は語幹らしい。


gcc -MMなど

これは、NordicのMakefileでは使われていない。
gccのコマンドなのだけど、依存関係をMakefileがわかる形式で出力するオプションだ。
Nordicブログでは、各ソースファイルごとに依存関係のファイルを作っているようだが、私は1つの依存関係ファイルに追加していくタイプだった。
個別にした方が融通は利きやすいのかな?

依存関係がほしいのは、主にソースファイルとヘッダファイルの紐付けだ。
今のexamplesだと依存関係がわからないので、ヘッダファイルが更新されてもビルドをしてくれないのだ。
Nordicのexamplesだと、最初にcleanしてからビルドするので、毎回時間がかかるのだ(大した時間じゃないけど)。


abspath

これは、addprefixと同じく関数だ。
名前の通り、相対パスなどを絶対パスに置き換えてくれる。

が、これがcygwinと相性が悪いようだ。
cygwinというか、Windows版のgccコンパイラと、というか。
コマンドプロンプトでmakeするときはファイルを見つけてくれるのだが、Cygwinでmakeするとファイルが見つからないのだ。
たぶん「/cygpath/~」の形式になるからだろう。
コンパイラはcygwinの世界以外からやってきてるから、知らないはずだ。

cygpathを使えばいいよ、ということらしいが、そうするとLinuxで使えなくなるし。
GNU Win32のmakeを使うのが楽そうだ。
コンパイラはShift-JIS出力だから文字化けするが、まあそこは許容するしかあるまい。


自分が使いそうなものを書き出してみたが、いくつかパターンを作って置いて、テンプレートとして持っておくのが良い気がする。

自動的に作ってくれるスクリプトや、automakeもあるのだが、スクリプトだと自分の気に入った配置になってないことがあるし(変更すればいいのだけど)、automakeはちょっと大ごと過ぎる気がする。
cmakeもたまに見るけど、あれは楽なんだろうか?

楽をしたいだけなのだが、間違いがあっても困るし、時間をあまり掛けたいわけじゃない。。と、いつもうろうろ考えてしまいます。

0 件のコメント:

コメントを投稿

コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。

注: コメントを投稿できるのは、このブログのメンバーだけです。