🍆

gccの"-fmax-include-depth"オプションについて

7 min read

はじめに

GCC 10.3から-fmax-include-depthというオプションが追加されています。
(GCC 9.4のドキュメントには-fmax-include-depthの記載は見つかりませんでした。)

https://gcc.gnu.org/onlinedocs/gcc-10.3.0/gcc/Preprocessor-Options.html#index-fmax-include-depth

-fmax-include-depth=depth
Set the maximum depth of the nested #include. The default is 200.

インクルードするファイルのネストの深さを設定できるようです。

Hello worldを例に見てみる。

例によって、以下のようにHello worldを用意します。

sample.c
#include <stdio.h>

int main(void){
	printf("hello\n");
	return 0;
}

当然ですが、普通にコンパイル、実行はできます。

$ gcc-10 sample.c && ./a.out 
hello

では、ここでインクルードしているstdio.hから
どれだけ他のヘッダファイルがインクルードされているかを見てみます。
こういうときは-Hオプションが使えます。

https://gcc.gnu.org/onlinedocs/gcc-10.3.0/gcc/Preprocessor-Options.html#index-H

-Hオプションを付けた結果は以下。
結構たくさんのヘッダファイルがインクルードされているのがわかります。

$ gcc-10 -H sample.c
. /usr/include/stdio.h
.. /usr/include/x86_64-linux-gnu/bits/libc-header-start.h
... /usr/include/features.h
.... /usr/include/x86_64-linux-gnu/sys/cdefs.h
..... /usr/include/x86_64-linux-gnu/bits/wordsize.h
..... /usr/include/x86_64-linux-gnu/bits/long-double.h
.... /usr/include/x86_64-linux-gnu/gnu/stubs.h
..... /usr/include/x86_64-linux-gnu/gnu/stubs-64.h
.. /usr/lib/gcc/x86_64-linux-gnu/10/include/stddef.h
.. /usr/lib/gcc/x86_64-linux-gnu/10/include/stdarg.h
.. /usr/include/x86_64-linux-gnu/bits/types.h
... /usr/include/x86_64-linux-gnu/bits/wordsize.h
... /usr/include/x86_64-linux-gnu/bits/timesize.h
... /usr/include/x86_64-linux-gnu/bits/typesizes.h
... /usr/include/x86_64-linux-gnu/bits/time64.h
.. /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h
... /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h
.. /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h
.. /usr/include/x86_64-linux-gnu/bits/types/__FILE.h
.. /usr/include/x86_64-linux-gnu/bits/types/FILE.h
.. /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h
.. /usr/include/x86_64-linux-gnu/bits/stdio_lim.h
.. /usr/include/x86_64-linux-gnu/bits/sys_errlist.h
Multiple include guards may be useful for:
/usr/include/x86_64-linux-gnu/bits/libc-header-start.h
/usr/include/x86_64-linux-gnu/bits/long-double.h
/usr/include/x86_64-linux-gnu/bits/sys_errlist.h
/usr/include/x86_64-linux-gnu/bits/time64.h
/usr/include/x86_64-linux-gnu/bits/timesize.h
/usr/include/x86_64-linux-gnu/bits/typesizes.h
/usr/include/x86_64-linux-gnu/gnu/stubs-64.h
/usr/include/x86_64-linux-gnu/gnu/stubs.h
/usr/lib/gcc/x86_64-linux-gnu/10/include/stddef.h

一番深い階層は5となるのでしょうか?

$ gcc-10 -H sample.c
. /usr/include/stdio.h ---> 1
.. /usr/include/x86_64-linux-gnu/bits/libc-header-start.h ---> 2
... /usr/include/features.h ---> 3
.... /usr/include/x86_64-linux-gnu/sys/cdefs.h  ---> 4
..... /usr/include/x86_64-linux-gnu/bits/wordsize.h  ---> 5
..... /usr/include/x86_64-linux-gnu/bits/long-double.h ---> 5
....

試しに -fmax-include-depth=5でコンパイルしてみます。
エラーが出てしまいました。

$ gcc-10 -H -fmax-include-depth=5 sample.c
. /usr/include/stdio.h
.. /usr/include/x86_64-linux-gnu/bits/libc-header-start.h
... /usr/include/features.h
.... /usr/include/x86_64-linux-gnu/sys/cdefs.h
In file included from /usr/include/features.h:461,
                 from /usr/include/x86_64-linux-gnu/bits/libc-header-start.h:33,
                 from /usr/include/stdio.h:27,
                 from sample.c:1:
/usr/include/x86_64-linux-gnu/sys/cdefs.h:452:27: error: #include nested depth 5 exceeds maximum of 5 (use -fmax-include-depth=DEPTH to increase the maximum)
  452 | #include <bits/wordsize.h>
      |                           ^
/usr/include/x86_64-linux-gnu/sys/cdefs.h:453:30: error: #include nested depth 5 exceeds maximum of 5 (use -fmax-include-depth=DEPTH to increase the maximum)
  453 | #include <bits/long-double.h>
      |                              ^
.... /usr/include/x86_64-linux-gnu/gnu/stubs.h
In file included from /usr/include/features.h:485,
                 from /usr/include/x86_64-linux-gnu/bits/libc-header-start.h:33,
                 from /usr/include/stdio.h:27,
                 from sample.c:1:
/usr/include/x86_64-linux-gnu/gnu/stubs.h:10:27: error: #include nested depth 5 exceeds maximum of 5 (use -fmax-include-depth=DEPTH to increase the maximum)
   10 | # include <gnu/stubs-64.h>
      |                           ^
.. /usr/lib/gcc/x86_64-linux-gnu/10/include/stddef.h
.. /usr/lib/gcc/x86_64-linux-gnu/10/include/stdarg.h
.. /usr/include/x86_64-linux-gnu/bits/types.h
<省略>

階層の数え方(?)が違うのでしょうか、-fmax-include-depth=6としたら
コンパイルできました。

$ gcc-10 -fmax-include-depth=6 sample.c
$ ./a.out 
hello

もうちょっと単純な例で見てみる

以下のようなコードを用意してみました。
sample.cがsample.hをインクルードして、sample.h内でsample2.hをインクルードしているだけのものです。

sample.c
#include "sample.h"
int main(void){
	return 0;
}
sample.h
#include "sample2.h"
int a;
sample2.h
int b;

これを-fmax-include-depth=2でコンパイルしてみます。

$ gcc-10 -fmax-include-depth=2 sample.c
In file included from put.c:1:
sample.h:1:21: error: #include nested depth 2 exceeds maximum of 2 (use -fmax-include-depth=DEPTH to increase the maximum)
    1 | #include "sample2.h"
      |                     ^

だめでした。-fmax-include-depth=3だと大丈夫です。

$ gcc-10 -fmax-include-depth=3 sample.c
$ 

何となく自分のイメージでは以下のように
ピンク色の数が階層の深さかなと思っていました。

graph LR
    A[sample.c] --> id1[sample.h]
    id1 --> id2[sample2.h]
    style id1 fill:#f9f,stroke:#333,stroke-width:4px
    style id2 fill:#f9f,stroke:#333,stroke-width:4px

ですが、動きを見てみると以下のようです。
(sample.cが1階層目ということ?)

graph LR
    id3[sample.c] --> id1[sample.h]
    id1 --> id2[sample2.h]
    style id1 fill:#f9f,stroke:#333,stroke-width:4px
    style id2 fill:#f9f,stroke:#333,stroke-width:4px
    style id3 fill:#f9f,stroke:#333,stroke-width:4px

デフォルトの200は?

あらためてドキュメントの記載を見ると、
以下のように記載されています。

Set the maximum depth of the nested #include. The default is 200.

なんで、デフォルトは200なのでしょうか。
ChangeLogを見ると以下の記載がありました。

ChangeLog
2019-07-02  qing zhao

	PR preprocessor/90581
	* directives.c (do_include_common): Replace CPP_STACK_MAX with
	CPP_OPTION (pfile, max_include_depth).
	* include/cpplib.h (struct cpp_options): Add new field
	max_include_depth.
	* init.c (cpp_create_reader): Initiate new field max_include_depth.
	* internal.h: Delete CPP_STACK_MAX.

gcc9.4のソースを見ると、以下のようにCPP_STACK_MAXが定義されていました

/* Maximum nesting of cpp_buffers.  We use a static limit, partly for
    efficiency, and partly to limit runaway recursion.  */
#define CPP_STACK_MAX 200

まとめ

普段何気なく#include <***.h>とか使っていますが、
中身を追ってみると、面白そうです。

-Hオプションをヒントにコードを読んでみるのもよいかと。
clangのドキュメントはまだちゃんと読めていませんが、
-fmax-include-depth相当のオプションはあるのでしょうか。

ドキュメントを読むといろいろなオプションが見つけられて楽しいですよ。