makefile と build.ninja の比較。実アプリのビルド時間比較。 make から ninja への移行検討メモ

# これは何

GNU make から ninja に移行を検討中のメモです。
[H.264 のリファレンスソフトウエアデコーダ](http://iphome.hhi.de/suehring/tml/download/)を題材に、 make と ninja のビルド時間を比較します。

単に makefile と build.ninja を見比べたい人は [hello world の makefile と build.ninja](https://qiita.com/2fbCvmiYKX/items/671ea66b252f8571b2ab) や [.a ファイルのmakefile と build.ninja](https://qiita.com/2fbCvmiYKX/items/818735df2576e46b0775) をどうぞご覧ください。

## H.264 リファレンスデコーダって何?

H.264 という、一昔前のビデオ圧縮形式があります。
これのデコーダを開発する開発者や、圧縮アルゴリズムを検討したい研究者が利用することを想定したソフトウエアでソースコードが公開されています。
Linux 向けのビルド環境は素の makefile なので、これと ninja とのビルド時間を比較してみます。

# 想定読者

makefile を自分でかける人
`.PHONY: clean` を毎回書くのに飽き飽きした人
素の make の動作が遅いなぁと思う人
ninja 向けのメタビルドツールを作りたい人

# 結論

| コマンド名 | 所要時間(秒) |
|-----------|------------:|
| ninja     |   17.547524 |
| make      |   32.162396 |

ninja が圧倒的に早いです。[ninja 公式](https://ninja-build.org/)が売りにしているとおり、早いです。
簡単な構成のプログラムであれば、build.ninja を直接書くのも全然ありと思います。
[meson](https://mesonbuild.com/) や [cmake](https://cmake.org/) を使うと、記述量が減りますが、build.ninja の生成時間も気になりますんで。

## 測定環境

alpine linux on docker for windows 10
コンテナイメージは、下記 Dockerfile です。

```Dockerfile:Dockerfile
FROM gliderlabs/alpine:latest
RUN set -eux; \
	apk --update add --no-cache \
		libc-dev \
		gcc \
		make \
		tar \
		ccache \
		ninja
WORKDIR /root
```

このイメージで、 make と ninja を実施し、 `powershell -C Measure-Command { cmd /C ninja.bat -C build }` のように、 powershell で実行時間を測定しています。ninja.bat の中身は下記です。makeを試すときは、ninja を make に書き換えたファイルを使用しています。

```bat:ninja.bat
@echo off
setlocal
set docker_image=gcc
cd /d "%~dp0"
docker run --rm -it -v "%CD%":/root %docker_image% ninja %*
if errorlevel 1 pause
```


# ファイル構成

`jm19.zip` を取得し、展開すると下記のディレクトリが作成されます。

```
JM/
|- bin
|- doc
|- JM.xcodeproj
|- lcommon
|- ldecod
|- lencod
|- rtp_loss
`- rtpdump
```

## make での `bin/ldecod.exe` のビルド

カレントディレクトリを `JM` として、 `make -C ldecod STC=1` を実行すると、今回の測定と同じオプションでビルドできます。

## ninja での `bin/ldecod` のビルド

下記 build.ninja を `JM/build.ninja` に作成します。ビルドは、カレントディレクトリが `JM` とすると `ninja` となります。 `JM/bin/ldecod` が生成されます。

```ninja:build.ninja
root = .
builddir = build
exedir = bin

#cc = ccache cc
cc = cc
cflags = -Wall -O3 -std=gnu99 -pedantic -ffloat-store -fno-strict-aliasing -fsigned-char
cppflags = -DNDEBUG -D__USE_LARGEFILE64 -D_FILE_OFFSET_BITS=64 -I$root/lcommon/inc -I$root/ldecod/inc
ldlibs = -lm -static

rule compile
     description = compiling object file "$out" ...
     deps    = gcc
     depfile = $out.d
     command = $cc -MMD -MP -MF $out.d $cflags $cppflags $target_arch -c -o $out $in

rule link
     description = creating binary "$out"
     command = $cc $ldflags $target_arch $in $loadlibes $ldlibs -o $out

build $exedir/ldecod: link $
  $builddir/blk_prediction.o $
  $builddir/config_common.o $
  $builddir/img_io.o $
  $builddir/img_process.o $
  $builddir/input.o $
  $builddir/io_raw.o $
  $builddir/io_tiff.o $
  $builddir/mbuffer_common.o $
  $builddir/memalloc.o $
  $builddir/mv_prediction.o $
  $builddir/nalucommon.o $
  $builddir/parsetcommon.o $
  $builddir/resize.o $
  $builddir/transform.o $
  $builddir/win32.o $
  $
  $builddir/annexb.o $
  $builddir/biaridecod.o $
  $builddir/block.o $
  $builddir/cabac.o $
  $builddir/configfile.o $
  $builddir/context_ini.o $
  $builddir/decoder_test.o $
  $builddir/dec_statistics.o $
  $builddir/erc_api.o $
  $builddir/erc_do_i.o $
  $builddir/erc_do_p.o $
  $builddir/errorconcealment.o $
  $builddir/filehandle.o $
  $builddir/fmo.o $
  $builddir/header.o $
  $builddir/image.o $
  $builddir/intra16x16_pred.o $
  $builddir/intra16x16_pred_mbaff.o $
  $builddir/intra16x16_pred_normal.o $
  $builddir/intra4x4_pred.o $
  $builddir/intra4x4_pred_mbaff.o $
  $builddir/intra4x4_pred_normal.o $
  $builddir/intra8x8_pred.o $
  $builddir/intra8x8_pred_mbaff.o $
  $builddir/intra8x8_pred_normal.o $
  $builddir/intra_chroma_pred.o $
  $builddir/intra_chroma_pred_mbaff.o $
  $builddir/intra_pred_common.o $
  $builddir/ldecod.o $
  $builddir/leaky_bucket.o $
  $builddir/loopFilter.o $
  $builddir/loop_filter_mbaff.o $
  $builddir/loop_filter_normal.o $
  $builddir/macroblock.o $
  $builddir/mbuffer.o $
  $builddir/mbuffer_mvc.o $
  $builddir/mb_access.o $
  $builddir/mb_prediction.o $
  $builddir/mb_read.o $
  $builddir/mc_direct.o $
  $builddir/mc_prediction.o $
  $builddir/nal.o $
  $builddir/nalu.o $
  $builddir/output.o $
  $builddir/parset.o $
  $builddir/quant.o $
  $builddir/read_comp_cabac.o $
  $builddir/read_comp_cavlc.o $
  $builddir/rtp.o $
  $builddir/sei.o $
  $builddir/transform8x8.o $
  $builddir/vlc.o

build $builddir/blk_prediction.o : compile $root/lcommon/src/blk_prediction.c
build $builddir/config_common.o : compile $root/lcommon/src/config_common.c
build $builddir/img_io.o : compile $root/lcommon/src/img_io.c
build $builddir/img_process.o : compile $root/lcommon/src/img_process.c
build $builddir/input.o : compile $root/lcommon/src/input.c
build $builddir/io_raw.o : compile $root/lcommon/src/io_raw.c
build $builddir/io_tiff.o : compile $root/lcommon/src/io_tiff.c
build $builddir/mbuffer_common.o : compile $root/lcommon/src/mbuffer_common.c
build $builddir/memalloc.o : compile $root/lcommon/src/memalloc.c
build $builddir/mv_prediction.o : compile $root/lcommon/src/mv_prediction.c
build $builddir/nalucommon.o : compile $root/lcommon/src/nalucommon.c
build $builddir/parsetcommon.o : compile $root/lcommon/src/parsetcommon.c
build $builddir/resize.o : compile $root/lcommon/src/resize.c
build $builddir/transform.o : compile $root/lcommon/src/transform.c
build $builddir/win32.o : compile $root/lcommon/src/win32.c

build $builddir/annexb.o : compile $root/ldecod/src/annexb.c
build $builddir/biaridecod.o : compile $root/ldecod/src/biaridecod.c
build $builddir/block.o : compile $root/ldecod/src/block.c
build $builddir/cabac.o : compile $root/ldecod/src/cabac.c
build $builddir/configfile.o : compile $root/ldecod/src/configfile.c
build $builddir/context_ini.o : compile $root/ldecod/src/context_ini.c
build $builddir/decoder_test.o : compile $root/ldecod/src/decoder_test.c
build $builddir/dec_statistics.o : compile $root/ldecod/src/dec_statistics.c
build $builddir/erc_api.o : compile $root/ldecod/src/erc_api.c
build $builddir/erc_do_i.o : compile $root/ldecod/src/erc_do_i.c
build $builddir/erc_do_p.o : compile $root/ldecod/src/erc_do_p.c
build $builddir/errorconcealment.o : compile $root/ldecod/src/errorconcealment.c
build $builddir/filehandle.o : compile $root/ldecod/src/filehandle.c
build $builddir/fmo.o : compile $root/ldecod/src/fmo.c
build $builddir/header.o : compile $root/ldecod/src/header.c
build $builddir/image.o : compile $root/ldecod/src/image.c
build $builddir/intra16x16_pred.o : compile $root/ldecod/src/intra16x16_pred.c
build $builddir/intra16x16_pred_mbaff.o : compile $root/ldecod/src/intra16x16_pred_mbaff.c
build $builddir/intra16x16_pred_normal.o : compile $root/ldecod/src/intra16x16_pred_normal.c
build $builddir/intra4x4_pred.o : compile $root/ldecod/src/intra4x4_pred.c
build $builddir/intra4x4_pred_mbaff.o : compile $root/ldecod/src/intra4x4_pred_mbaff.c
build $builddir/intra4x4_pred_normal.o : compile $root/ldecod/src/intra4x4_pred_normal.c
build $builddir/intra8x8_pred.o : compile $root/ldecod/src/intra8x8_pred.c
build $builddir/intra8x8_pred_mbaff.o : compile $root/ldecod/src/intra8x8_pred_mbaff.c
build $builddir/intra8x8_pred_normal.o : compile $root/ldecod/src/intra8x8_pred_normal.c
build $builddir/intra_chroma_pred.o : compile $root/ldecod/src/intra_chroma_pred.c
build $builddir/intra_chroma_pred_mbaff.o : compile $root/ldecod/src/intra_chroma_pred_mbaff.c
build $builddir/intra_pred_common.o : compile $root/ldecod/src/intra_pred_common.c
build $builddir/ldecod.o : compile $root/ldecod/src/ldecod.c
build $builddir/leaky_bucket.o : compile $root/ldecod/src/leaky_bucket.c
build $builddir/loopFilter.o : compile $root/ldecod/src/loopFilter.c
build $builddir/loop_filter_mbaff.o : compile $root/ldecod/src/loop_filter_mbaff.c
build $builddir/loop_filter_normal.o : compile $root/ldecod/src/loop_filter_normal.c
build $builddir/macroblock.o : compile $root/ldecod/src/macroblock.c
build $builddir/mbuffer.o : compile $root/ldecod/src/mbuffer.c
build $builddir/mbuffer_mvc.o : compile $root/ldecod/src/mbuffer_mvc.c
build $builddir/mb_access.o : compile $root/ldecod/src/mb_access.c
build $builddir/mb_prediction.o : compile $root/ldecod/src/mb_prediction.c
build $builddir/mb_read.o : compile $root/ldecod/src/mb_read.c
build $builddir/mc_direct.o : compile $root/ldecod/src/mc_direct.c
build $builddir/mc_prediction.o : compile $root/ldecod/src/mc_prediction.c
build $builddir/nal.o : compile $root/ldecod/src/nal.c
build $builddir/nalu.o : compile $root/ldecod/src/nalu.c
build $builddir/output.o : compile $root/ldecod/src/output.c
build $builddir/parset.o : compile $root/ldecod/src/parset.c
build $builddir/quant.o : compile $root/ldecod/src/quant.c
build $builddir/read_comp_cabac.o : compile $root/ldecod/src/read_comp_cabac.c
build $builddir/read_comp_cavlc.o : compile $root/ldecod/src/read_comp_cavlc.c
build $builddir/rtp.o : compile $root/ldecod/src/rtp.c
build $builddir/sei.o : compile $root/ldecod/src/sei.c
build $builddir/transform8x8.o : compile $root/ldecod/src/transform8x8.c
build $builddir/vlc.o : compile $root/ldecod/src/vlc.c
```

## build.ninja 風の makefile でビルドを試す

標準のビルド結果と, ninja のビルド結果がバイナリ一致しなかったので、微妙にオプションがそろえられていなかったようです。なので、下記 makefile でもビルドしてみました。 `JM/build_mk/makefile` に配置しています。
`make -C build_mk` を実行すると、 `JM/bin/ldecod.exe` が生成されます。ビルド時間は 32.0608236 秒でした。標準の makefile とほぼ同じで、測定誤差程度の差といえるでしょう。

```makefile:makefile
CFLAGS=-Wall -O3 -std=gnu99 -pedantic -ffloat-store -fno-strict-aliasing -fsigned-char -MMD -MP
CPPFLAGS=-DNDEBUG -D__USE_LARGEFILE64 -D_FILE_OFFSET_BITS=64 -I../lcommon/inc -I../ldecod/inc
LDLIBS=-lm -static

vpath %.c ../lcommon/src
vpath %.c ../ldecod/src

OBJS= \
  blk_prediction.o \
  config_common.o \
  img_io.o \
  img_process.o \
  input.o \
  io_raw.o \
  io_tiff.o \
  mbuffer_common.o \
  memalloc.o \
  mv_prediction.o \
  nalucommon.o \
  parsetcommon.o \
  resize.o \
  transform.o \
  win32.o \
  \
  annexb.o \
  biaridecod.o \
  block.o \
  cabac.o \
  configfile.o \
  context_ini.o \
  decoder_test.o \
  dec_statistics.o \
  erc_api.o \
  erc_do_i.o \
  erc_do_p.o \
  errorconcealment.o \
  filehandle.o \
  fmo.o \
  header.o \
  image.o \
  intra16x16_pred.o \
  intra16x16_pred_mbaff.o \
  intra16x16_pred_normal.o \
  intra4x4_pred.o \
  intra4x4_pred_mbaff.o \
  intra4x4_pred_normal.o \
  intra8x8_pred.o \
  intra8x8_pred_mbaff.o \
  intra8x8_pred_normal.o \
  intra_chroma_pred.o \
  intra_chroma_pred_mbaff.o \
  intra_pred_common.o \
  ldecod.o \
  leaky_bucket.o \
  loopFilter.o \
  loop_filter_mbaff.o \
  loop_filter_normal.o \
  macroblock.o \
  mbuffer.o \
  mbuffer_mvc.o \
  mb_access.o \
  mb_prediction.o \
  mb_read.o \
  mc_direct.o \
  mc_prediction.o \
  nal.o \
  nalu.o \
  output.o \
  parset.o \
  quant.o \
  read_comp_cabac.o \
  read_comp_cavlc.o \
  rtp.o \
  sei.o \
  transform8x8.o \
  vlc.o

.PHONY: clean
../bin/ldecod.exe:$(OBJS)
	@echo 'creating binary "$@"'
	@$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
	@echo '... done'
clean:
	@echo remove all objects
	$(RM) $(OBJS:%.o=%.d) $(OBJS) ldecod.exe
-include $(OBJS:%.o=%.d)
```

# おまけ

visual studio 用の build.ninja も作ってみました。 chocolatey で install される version 1.8.2 では、バグを踏んで動かないので注意ください。ビルドは  6.3088257秒でした。早い。 docker 立ち上げないからだとおもいます。

```build.ninja
root = .
builddir = build_win
exedir = bin

msvc_deps_prefix = メモ: インクルード ファイル:
cflags = /Wall /Os
cppflags = /DNDEBUG /DWIN64 /I$root\lcommon\inc /I$root\ldecod\inc
ldlibs = Ws2_32.lib

rule compile
     description = compiling object file "$out" ...
     deps    = msvc
     command = cl /nologo /showIncludes $cflags $cppflags /c /Fo$out $in

rule link
     description = creating binary "$out"
     command = link /NOLOGO $ldflags $target_arch $loadlibes $ldlibs /OUT:$out $in

build $exedir\ldecod.exe : link $
  $builddir\blk_prediction.obj $
  $builddir\config_common.obj $
  $builddir\img_io.obj $
  $builddir\img_process.obj $
  $builddir\input.obj $
  $builddir\io_raw.obj $
  $builddir\io_tiff.obj $
  $builddir\mbuffer_common.obj $
  $builddir\memalloc.obj $
  $builddir\mv_prediction.obj $
  $builddir\nalucommon.obj $
  $builddir\parsetcommon.obj $
  $builddir\resize.obj $
  $builddir\transform.obj $
  $builddir\win32.obj $
  $
  $builddir\annexb.obj $
  $builddir\biaridecod.obj $
  $builddir\block.obj $
  $builddir\cabac.obj $
  $builddir\configfile.obj $
  $builddir\context_ini.obj $
  $builddir\decoder_test.obj $
  $builddir\dec_statistics.obj $
  $builddir\erc_api.obj $
  $builddir\erc_do_i.obj $
  $builddir\erc_do_p.obj $
  $builddir\errorconcealment.obj $
  $builddir\filehandle.obj $
  $builddir\fmo.obj $
  $builddir\header.obj $
  $builddir\image.obj $
  $builddir\intra16x16_pred.obj $
  $builddir\intra16x16_pred_mbaff.obj $
  $builddir\intra16x16_pred_normal.obj $
  $builddir\intra4x4_pred.obj $
  $builddir\intra4x4_pred_mbaff.obj $
  $builddir\intra4x4_pred_normal.obj $
  $builddir\intra8x8_pred.obj $
  $builddir\intra8x8_pred_mbaff.obj $
  $builddir\intra8x8_pred_normal.obj $
  $builddir\intra_chroma_pred.obj $
  $builddir\intra_chroma_pred_mbaff.obj $
  $builddir\intra_pred_common.obj $
  $builddir\ldecod.obj $
  $builddir\leaky_bucket.obj $
  $builddir\loopFilter.obj $
  $builddir\loop_filter_mbaff.obj $
  $builddir\loop_filter_normal.obj $
  $builddir\macroblock.obj $
  $builddir\mbuffer.obj $
  $builddir\mbuffer_mvc.obj $
  $builddir\mb_access.obj $
  $builddir\mb_prediction.obj $
  $builddir\mb_read.obj $
  $builddir\mc_direct.obj $
  $builddir\mc_prediction.obj $
  $builddir\nal.obj $
  $builddir\nalu.obj $
  $builddir\output.obj $
  $builddir\parset.obj $
  $builddir\quant.obj $
  $builddir\read_comp_cabac.obj $
  $builddir\read_comp_cavlc.obj $
  $builddir\rtp.obj $
  $builddir\sei.obj $
  $builddir\transform8x8.obj $
  $builddir\vlc.obj

build $builddir\blk_prediction.obj : compile $root\lcommon\src\blk_prediction.c
build $builddir\config_common.obj : compile $root\lcommon\src\config_common.c
build $builddir\img_io.obj : compile $root\lcommon\src\img_io.c
build $builddir\img_process.obj : compile $root\lcommon\src\img_process.c
build $builddir\input.obj : compile $root\lcommon\src\input.c
build $builddir\io_raw.obj : compile $root\lcommon\src\io_raw.c
build $builddir\io_tiff.obj : compile $root\lcommon\src\io_tiff.c
build $builddir\mbuffer_common.obj : compile $root\lcommon\src\mbuffer_common.c
build $builddir\memalloc.obj : compile $root\lcommon\src\memalloc.c
build $builddir\mv_prediction.obj : compile $root\lcommon\src\mv_prediction.c
build $builddir\nalucommon.obj : compile $root\lcommon\src\nalucommon.c
build $builddir\parsetcommon.obj : compile $root\lcommon\src\parsetcommon.c
build $builddir\resize.obj : compile $root\lcommon\src\resize.c
build $builddir\transform.obj : compile $root\lcommon\src\transform.c
build $builddir\win32.obj : compile $root\lcommon\src\win32.c            

build $builddir\annexb.obj : compile $root\ldecod\src\annexb.c
build $builddir\biaridecod.obj : compile $root\ldecod\src\biaridecod.c
build $builddir\block.obj : compile $root\ldecod\src\block.c
build $builddir\cabac.obj : compile $root\ldecod\src\cabac.c
build $builddir\configfile.obj : compile $root\ldecod\src\configfile.c
build $builddir\context_ini.obj : compile $root\ldecod\src\context_ini.c
build $builddir\decoder_test.obj : compile $root\ldecod\src\decoder_test.c
build $builddir\dec_statistics.obj : compile $root\ldecod\src\dec_statistics.c
build $builddir\erc_api.obj : compile $root\ldecod\src\erc_api.c
build $builddir\erc_do_i.obj : compile $root\ldecod\src\erc_do_i.c
build $builddir\erc_do_p.obj : compile $root\ldecod\src\erc_do_p.c
build $builddir\errorconcealment.obj : compile $root\ldecod\src\errorconcealment.c
build $builddir\filehandle.obj : compile $root\ldecod\src\filehandle.c
build $builddir\fmo.obj : compile $root\ldecod\src\fmo.c
build $builddir\header.obj : compile $root\ldecod\src\header.c
build $builddir\image.obj : compile $root\ldecod\src\image.c
build $builddir\intra16x16_pred.obj : compile $root\ldecod\src\intra16x16_pred.c
build $builddir\intra16x16_pred_mbaff.obj : compile $root\ldecod\src\intra16x16_pred_mbaff.c
build $builddir\intra16x16_pred_normal.obj : compile $root\ldecod\src\intra16x16_pred_normal.c
build $builddir\intra4x4_pred.obj : compile $root\ldecod\src\intra4x4_pred.c
build $builddir\intra4x4_pred_mbaff.obj : compile $root\ldecod\src\intra4x4_pred_mbaff.c
build $builddir\intra4x4_pred_normal.obj : compile $root\ldecod\src\intra4x4_pred_normal.c
build $builddir\intra8x8_pred.obj : compile $root\ldecod\src\intra8x8_pred.c
build $builddir\intra8x8_pred_mbaff.obj : compile $root\ldecod\src\intra8x8_pred_mbaff.c
build $builddir\intra8x8_pred_normal.obj : compile $root\ldecod\src\intra8x8_pred_normal.c
build $builddir\intra_chroma_pred.obj : compile $root\ldecod\src\intra_chroma_pred.c
build $builddir\intra_chroma_pred_mbaff.obj : compile $root\ldecod\src\intra_chroma_pred_mbaff.c
build $builddir\intra_pred_common.obj : compile $root\ldecod\src\intra_pred_common.c
build $builddir\ldecod.obj : compile $root\ldecod\src\ldecod.c
build $builddir\leaky_bucket.obj : compile $root\ldecod\src\leaky_bucket.c
build $builddir\loopFilter.obj : compile $root\ldecod\src\loopFilter.c
build $builddir\loop_filter_mbaff.obj : compile $root\ldecod\src\loop_filter_mbaff.c
build $builddir\loop_filter_normal.obj : compile $root\ldecod\src\loop_filter_normal.c
build $builddir\macroblock.obj : compile $root\ldecod\src\macroblock.c
build $builddir\mbuffer.obj : compile $root\ldecod\src\mbuffer.c
build $builddir\mbuffer_mvc.obj : compile $root\ldecod\src\mbuffer_mvc.c
build $builddir\mb_access.obj : compile $root\ldecod\src\mb_access.c
build $builddir\mb_prediction.obj : compile $root\ldecod\src\mb_prediction.c
build $builddir\mb_read.obj : compile $root\ldecod\src\mb_read.c
build $builddir\mc_direct.obj : compile $root\ldecod\src\mc_direct.c
build $builddir\mc_prediction.obj : compile $root\ldecod\src\mc_prediction.c
build $builddir\nal.obj : compile $root\ldecod\src\nal.c
build $builddir\nalu.obj : compile $root\ldecod\src\nalu.c
build $builddir\output.obj : compile $root\ldecod\src\output.c
build $builddir\parset.obj : compile $root\ldecod\src\parset.c
build $builddir\quant.obj : compile $root\ldecod\src\quant.c
build $builddir\read_comp_cabac.obj : compile $root\ldecod\src\read_comp_cabac.c
build $builddir\read_comp_cavlc.obj : compile $root\ldecod\src\read_comp_cavlc.c
build $builddir\rtp.obj : compile $root\ldecod\src\rtp.c
build $builddir\sei.obj : compile $root\ldecod\src\sei.c
build $builddir\transform8x8.obj : compile $root\ldecod\src\transform8x8.c
build $builddir\vlc.obj : compile $root\ldecod\src\vlc.c
```

# 感想

クリーンビルドでは差が無いのでは無いかと思っていたが、ninja が倍近く早くてびっくりしました。積極的に ninja を使う理由になります。
ただ、build.ninja は makefile に比べて記述量が増えるのは間違いないです。面倒です。
`include文` を駆使することで、 makefile でいうところの wildcard 的な記述ができないか、実験してみたいと思います。まぁ、駄目な気もしますが。設計思想で wildcard は 速度低下の元になるので、あえてサポートしていないそうなんで。

# 参考リンク

[MS-DOSコマンドで処理時間を計測する](https://blog.y-yuki.net/entry/2017/11/01/130000)