11

そこで、Advanced Auto-Dependency Generation の論文に従いました --

メイクファイル:

SRCS := main.c foo.c

main: main.o foo.o

%.o: %.c
    $(CC) -MMD -MG -MT '$@ $*.d' -c $< -o $@
    cp $*.d $*.tmp
    sed -e 's;#.*;;' -e 's;^[^:]*: *;;' -e 's; *\\$$;;' \
        -e '/^$$/d' -e 's;$$; :;' < $*.tmp >> $*.d
    rm $*.tmp

clean::
    -rm *.o *.d main

-include $(SRCS:.c=.d)

main.c :

#include "foo.h"

int main(int argc, char** argv) {
  foo() ;
  return 0 ;
}

foo.h :

#ifndef __FOO_H__
#define __FOO_H__

void foo() ;

#endif

--そして、それは魅力のように機能します。


しかしfoo.h、生成されたファイルになると -

メイクファイル:

...

HDRS := foo.h

$(HDRS):
    mk_header.sh $*

clean::
    -rm $(HDRS)
...

mk_header.sh:

#!/bin/bash
UP=$(tr "[:lower:]" "[:upper:]" <<< $1)

cat <<EOF > $1.h
#ifndef __${UP}_H__
#define __${UP}_H__

void $1() ;

#endif
EOF

初めて実行するmakemain.dまだ生成されていないためfoo.h、前提条件とは見なされず、生成されていません。

$ ls
foo.c  main.c  Makefile  mk_header.sh*

$ make
cc -MMD -MG -MT 'main.o main.d' -c main.c -o main.o
cp main.d main.tmp
sed -e 's;#.*;;' -e 's;^[^:]*: *;;' -e 's; *\\$;;' \
    -e '/^$/d' -e 's;$; :;' < main.tmp >> main.d
rm main.tmp
cc -MMD -MG -MT 'foo.o foo.d' -c foo.c -o foo.o
cp foo.d foo.tmp
sed -e 's;#.*;;' -e 's;^[^:]*: *;;' -e 's; *\\$;;' \
    -e '/^$/d' -e 's;$; :;' < foo.tmp >> foo.d
rm foo.tmp
cc   main.o foo.o   -o main

$ ls
foo.c  foo.d  foo.o  
main*  main.c  main.d  main.o  
Makefile  mk_header.sh*

の 2 回目の呼び出しでのみmakefoo.hが生成され、その結果、別のビルドがカスケードされます。

$ make
./mk_header.sh foo
cc -MMD -MG -MT 'main.o main.d' -c main.c -o main.o
cp main.d main.tmp
sed -e 's;#.*;;' -e 's;^[^:]*: *;;' -e 's; *\\$;;' \
    -e '/^$/d' -e 's;$; :;' < main.tmp >> main.d
rm main.tmp
cc   main.o foo.o   -o main

$ ls
foo.c  foo.d  foo.h  foo.o  
main*  main.c  main.d  main.o  
Makefile  mk_header.sh*

そして、その後になって初めて、次のことにmake気付きます。

$ make
make: `main' is up to date.

私の質問は次のとおりです。フラグメントを含めるときにメイクツリー全体を再評価する必要がないことによって実現されるパフォーマンスの向上を排除することなく、生成されたヘッダーファイルを可能にするために、上記の論文で提案されたレシピを拡張する方法はありますか?*.d

4

4 に答える 4

12

問題は、すべてのヘッダー生成が完了した*.dにMakefile フラグメントの生成を実行する必要があることです。このように言えば、 make の依存関係を使用して正しい順序を強制できます。

SRCS := main.c foo.c
HDRS := foo.h

main: main.o foo.o

%.o: %.c | generated_headers
    $(CC) -MMD -MG -MT '$@ $*.d' -c $< -o $@
    cp $*.d $*.tmp
    sed -e 's;#.*;;' -e 's;^[^:]*: *;;' -e 's; *\\$$;;' \
        -e '/^$$/d' -e 's;$$; :;' < $*.tmp >> $*.d
    rm $*.tmp

-include $(SRCS:.c=.d)

$(HDRS):
    mk_header.sh $*

generated_headers: $(HDRS)

clean:
    -rm $(HDRS) *.o *.d main

.PHONY: clean generated_headers

ノート:

  1. 注文のみの依存関係を使用します。

  2. このソリューションはかなりスケーラブルです。各生成ヘッダー ルールは、generated_headers .PHONYターゲットの前提条件であるだけで済みます。ヘッダー生成ルールが適切に記述されていると仮定すると、正しく生成された後は、generated_headersターゲットを満たすことは何もないはずです。

  3. オブジェクトが生成されたヘッダーを必要としない場合でも、最初にプロジェクトの生成されたすべてのヘッダーを生成せずに、単一のオブジェクトをコンパイルすることはできません。これは技術的には健全ですが、開発者は文句を言うでしょう。

    したがってFAST_AND_LOOSE、この機能をオフにするフラグを設定することを検討する必要があります。

    %.o: %.c | $(if $(FAST_AND_LOOSE),,generated_headers)
        ...
    

    したがって、開発者は以下を発行できます。

    make FAST_AND_LOOSE=1 main.o
    
于 2011-04-20T13:26:12.643 に答える
3

元の質問のメイクファイルは、gcc 4.8.2 では機能しません。

cc -MMD -MG -MT main.d -c main.c -o main.o
cc1: error: -MG may only be used with -M or -MM

-MGgccは過去 4 年間のある時点での動作を変更したと思います。

生成されたヘッダー ファイルをサポートしたい場合、C プリプロセッサを 2 回起動せずに「.d」ファイルと「.o」ファイルを同時に生成する方法はもはやないようです。

そこで、レシピを次のように更新しました。

%.o: %.c
    $(CC) -MM -MG -MP -MT $*.o -MF $*.d $<
    $(CC) -c $< -o $@

(また、gcc は-MPヘッダーごとに偽のターゲットを生成する必要があるため、gcc の出力に対して sed を実行する必要がなくなったことにも注意してください。)

元の質問と同じ問題がまだ残っています-make最初の実行で生成に失敗しますfoo.h:

$ make
cc -MM -MG -MP -MT main.o -MF main.d main.c
cc -c main.c -o main.o
main.c:1:17: fatal error: foo.h: No such file or directory
 #include "foo.h"
                 ^
compilation terminated.
Makefile:7: recipe for target 'main.o' failed
make: *** [main.o] Error 1

もう一度実行するとうまくいきます:

$ make
./mk_header.sh foo
cc -MM -MG -MP -MT main.o -MF main.d main.c
cc -c main.c -o main.o
cc   main.o   -o main

.d とにかく C プリプロセッサを 2 回実行する必要があるため、別のルールでファイルを生成しましょう。

%.d: %.c
    $(CC) -MM -MG -MP -MT $*.o -MF $@ $<

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

ヘッダー ファイルが正しく生成されるようになりました。

$ make clean
rm -f *.o *.d main foo.h
$ make
cc -MM -MG -MP -MT main.o -MF main.d main.c
./mk_header.sh foo
cc -c main.c -o main.o
cc   main.o   -o main

これは、元の質問が回避しようとしていたパフォーマンスの問題に悩まされていますか? これは基本的に、Advanced Auto-Dependency Generationペーパーで説明されている「基本的な自動依存関係」ソリューションです。

その論文は、このソリューションの 3 つの問題を主張しています。

  1. 何か変更があれば再実行makeします。
  2. 醜いが無害な警告: 「main.d: そのようなファイルまたはディレクトリはありません」
  3. foo.h ファイルが削除された場合、たとえ .c ファイルから言及が削除されたとしても、致命的なエラー「ターゲット foo.h を作成するルールはありません」。

問題 2 は、-includeの代わりに を使用することで解決されincludeます。私が知る限り、これは make. 少なくとも、-include の代わりに使用して問題を引き起こすことはできませんでしたinclude

問題 3 は GCC -MP(または同等の sed スクリプト) によって解決されます。これは、make.

問題 1 は、次のような方法で多少改善される可能性があります。

%.d: %.c
    $(CC) -MM -MG -MP -MT $*.o -MF $@.new $<
    cmp $@.new $@ 2>/dev/null || mv $@.new $@; rm -f $@.new

その変更前:

$ make clean
rm -f *.o *.d main foo.h
$ make -d 2>&1 | grep Re-executing
Re-executing[1]: make -d
$ make -d 2>&1 | grep Re-executing
$ touch main.c; make -d 2>&1 | grep Re-executing
Re-executing[1]: make -d

その変更後:

$ make clean
rm -f *.o *.d main foo.h
$ make -d 2>&1 | grep Re-executing
Re-executing[1]: make -d
$ make -d 2>&1 | grep Re-executing
$ touch main.c; make -d 2>&1 | grep Re-executing

少し良い。もちろん、新しい依存関係が導入された場合makeでも、再実行する必要があります。これを改善するためにできることは何もないかもしれません。正確さと速度のトレードオフのようです。

上記のすべては、make 3.81 でテストされています。

于 2015-04-13T15:58:26.070 に答える
2

簡単な答え:いいえ。この論文で説明されているレシピは非常に巧妙で、私のお気に入りの1つですが、それは粗雑なツールの洗練された使用法です。これは、必要なすべてのヘッダーが存在する通常のスキームを利用します。それが解決しようとしているのは、最近変更された場合、どのヘッダーが特定のオブジェクトファイルの再構築を必要とするかを決定する問題です。特に、オブジェクトファイルが存在しない場合は、再構築する必要があります。その場合、コンパイラはヘッダーファイルを確実に検出するため、ヘッダーファイルについて心配する必要はありません。

これでヘッダーファイルが生成されます。したがってfoo.h、存在しない可能性があるため、誰かがスクリプトを実行して生成する必要があり、Makeのみがそれを実行できます。しかし、Makeはfoo.h、の分析を実行せずにそれが必要であることを知ることはできませんmain.cmainしかし、Makeが関連するルール(main.oまたは)の実行を開始するまで、これは実際には起こり得ません。これは、構築するターゲットを決定するmain.o.dまで実行できません。

したがって、使用する必要があります...再帰的なmake![ダンダンダン!]

Makeの再呼び出しを回避するという論文の目標を達成することはできませんが、少なくとも(一部の)不要な再構築を回避することはできます。このホワイトペーパーで説明されている「基本的な自動依存関係」のようなことを行うことができます。この論文では、そのアプローチの問題について説明しています。または、「詳細」レシピのようなコマンドを使用してヘッダーのリストを生成し、それを$(MAKE);に渡すこともできます。このアプローチはきちんとしていますが、コードツリーがどのように見えるかによっては、同じヘッダーで何度もMakeを呼び出す場合があります。

于 2011-03-09T03:47:49.673 に答える
1

生成されたヘッダーの明示的な依存関係ルールを作成できます。

main.o: foo.h

生成されたヘッダーが少数のファイルに直接含まれている場合、これは実行可能なアプローチになる可能性があります。

于 2011-03-08T10:24:14.847 に答える