全てのプロジェクトをビルドするスクリプト

今回のネタは凄く短い・・・

実は、少しぶれてた・・
要は、複数ディレクトリーに色々なプロジェクトがあり、それらを全てコンパイル
する手順だった。

「glfw_app」の場合

drwxr-xr-x 1 hira None   0 9月  22 09:02 basic/
drwxr-xr-x 1 hira None   0 9月  22 09:05 bmc/
drwxr-xr-x 1 hira None   0 9月  22 09:09 cave/
drwxr-xr-x 1 hira None   0 7月   9 08:21 common/
drwxr-xr-x 1 hira None   0 9月  22 09:13 daev/
drwxr-xr-x 1 hira None   0 9月  21 12:43 dllcollect/
drwxr-xr-x 1 hira None   0 7月   9 08:21 docs/
drwxr-xr-x 1 hira None   0 9月  22 09:16 effv/
drwxr-xr-x 1 hira None   0 9月  22 09:16 gui_test/
drwxr-xr-x 1 hira None   0 9月  22 00:00 image/
drwxr-xr-x 1 hira None   0 9月  22 09:16 iod_make/
drwxr-xr-x 1 hira None   0 7月   9 08:21 libraries/
drwxr-xr-x 1 hira None   0 8月  17 15:38 MPU6050viewer/
drwxr-xr-x 1 hira None   0 9月  22 09:24 open_ide/
drwxr-xr-x 1 hira None   0 9月  22 09:24 piano_sim/
drwxr-xr-x 1 hira None   0 9月  21 22:21 player/
drwxr-xr-x 1 hira None   0 9月  22 09:25 pmdv/
drwxr-xr-x 1 hira None   0 9月  22 09:27 pn/
drwxr-xr-x 1 hira None   0 9月  22 09:27 spinv/
drwxr-xr-x 1 hira None   0 9月  22 09:28 vfs/
drwxr-xr-x 1 hira None   0 9月  22 09:29 vplayer/

一番簡単なのは、各プロジェクトのディレクトリーを指定して、「make」を実行す
る方法だ、しかしこれだと、プロジェクトが増える度に編集が必要となる。

RL78 では、xxx_sample をプロジェクトのディレクトリーとして、そのディレクト
リーに移動して「make」するスクリプトを作っていた。

ADC_sample/            DS1371_sample/              KiCAD_lib/         SDC_sample/
ADC_SWITCH_sample/     DS3231_sample/              LCD_DOT_sample/    SOFT_DELAY_sample/
ff12a/                 LCD_FILER_sample/           START_BOARD/
ARITH_sample/          FIRST_sample/               LICENSE            TMPtest/
BMP180_sample/         G13/                        MPU6050_sample/    TOUCH_sample/
chip/                  I2C_sample/                 PWM_sample/        UART_sample/
common/                images/                     R5F100LGA.jpg      VS1063_PLAYER_sample/
DATA_FLASH_sample/     INTERVAL_TIMER_sample/      README.md          WAV_PLAYER_sample/
Doxyfile               INTERVAL_TIMER_TAU_sample/  rl78prog/

ただ、これだと、「rl78prog」内のツールはビルでされない・・

そこで、良く考えたら、「Makefile」が存在するディレクトリーだけ抜き出して、「make」
を起動すれば良いと判った、何でこんな簡単な事を思いつかなかったのか不思議である。

その要件に従い、shell スクリプトを作成した。
OS-X で期待した動作にならなかったので修正
「clean」を事前に全て実行するように修正

#!/bin/bash
CMDNAME=`basename $0`
if [[ $1 = "help" ]]; then
  echo "Usage: $CMDNAME [clean]"
  echo ""
  exit
fi

RED=`tput setaf 1`
GREEN=`tput setaf 2`
PINK=`tput setaf 5`
LIGHTBLUE=`tput setaf 6`
NOCOLOR=`tput sgr0`

# make clean
if [[ $1 = "clean" ]]; then
  for file in `ls -d *`
  do
    if [ -e "${file}/Makefile" ]; then
      cd "${file}"
      echo "${GREEN}Clean project: " ${file} "${NOCOLOR}"
      make clean >& /dev/null
      if [ $? -ne 0 ]; then
        echo "${RED}Error: " ${file} "${NOCOLOR}"
        echo ""
        break;
      fi
      cd ..
    fi
  done
fi

# make
for file in `ls -d *`
do
  if [ -e "${file}/Makefile" ]; then
    echo "${PINK}Start project: " ${file} "${NOCOLOR}"
    cd "${file}"
    make > /dev/null
    if [ $? -ne 0 ]; then
      echo "${RED}Compile error: " ${file} "${NOCOLOR}"
      echo ""
      break;
    fi
    cd ..
    echo "${LIGHTBLUE}Build project: " ${file} "${NOCOLOR}"
  fi
done

これで、glfw_app、R8C、RL78 と全て動的に全ビルドが出来るようになった。

機能としては、

sh all_project_build.sh

単独で起動すれば、全体に「make」を行なう。

sh all_project_build.sh clean

とすれば、一旦「make clean」してから「make」する。

OS-X って、何で「echo」の「-e」
オプションが出来ないのか・・・

汎用的なMakefile

主に更新を続けている Git なのだけど、ちょっとした工夫で、開発環境が
より良くなる事は多い。

以前から、Makefile を少しづつアップデートをし続けて、現在に至ってい
る。
知り合いに、cmake を使うと柔軟で便利と言われていて、確かにそうだけど、
そこは天邪鬼な自分、「make」だけで何とかならないか・・・
※cmake を使って Makefile を作る手間が面倒

gcc のビルドだと、最初に「configure」を動かして、ビルドのオプション
を設定し、必要な中間ファイルを作成したりして、最終的に「Makefile」が
作成される。

また、Windows、OS-X を主なプラットホームにしているので、どちらでも遜
色無く機能してもらい、一元管理したい。

結局、「Makefile」だけの工夫で、全てまかなえている。
・従属規則を自動で生成する事
・コンパイルして生成したオブジェクトを1つのディレクトリーに集約する事
・リリース、デバッグなどのビルド切り替えが出来る事
・基本「make」だけタイプすれば、全てが生成できる事

ユーザーが記述すべき部分:

#=======================================================#
#                                                       #
#   RL78 Makefile                                       #
#                                                       #
#=======================================================#
TARGET      =    lcd_filer_sample
DEVICE      =    R5F100LGAFB
FATFS_VER   =    ff12a
BUILD       =    release
VPATH       =    ../
ASOURCES    =    common/start.s
CSOURCES    =    common/init.c \
                 common/vect.c \
                 common/option_bytes.c \
                 $(FATFS_VER)/src/ff.c \
                 $(FATFS_VER)/src/option/unicode.c \
                 common/time.c
PSOURCES    =    main.cpp \
                 common/font6x12.cpp

これは、「RL78/G13」の「Makefile」の先頭部分ではあるが、基本的に、ソース
コードの指定だけすれば良い。
従属規則は、自動で生成される。
「ASOURCES」はアセンブラのソース
「CSOURCES」は C 用ソース
「PSOURCES」は C++ 用ソースとなっている。
バックスラッシュで改行をエスケープして、1行に1ファイルとしている。
※この方が見やすいし、再利用する場合に利便性が高い。

「VPATH」は特別な変数で、この場合、一つ手前のディレクトリー以下にあるファ
イルを参照出来るようにしている。
※「VPATH」の値には make がサーチするディレクトリのリストを指定します。

「BUILD」に「release」とすれば、リリースビルド、「debug」とすればデバッグ
ビルドとなる。
生成されたオブジェクトは、「release」又は「debug」ディレクトリーが作られて
その中に全て格納される。
この中には、従属規則のパスが記述されたファイル、「source.d」も格納される。

新しいプロジェクトでは、Makefile をコピーして、必要な部分を少し編集すれば
良く、IDE でプロジェクトを再設定するより簡単だとおもう、この辺りも、IDE を
使わない理由の一つだと思う。

デバッグとリリース時のコンパイラオプションの違い:

ifeq ($(BUILD),debug)
    POPT += -g
    COPT += -g
    PFLAGS += -DDEBUG
    CFLAGS += -DDEBUG
endif

ifeq ($(BUILD),release)
    PFLAGS += -DNDEBUG
    CFLAGS += -DNDEBUG
endif

ビルド・ディレクトリーが「release」か、「debug」で、コンパイル・オプション
を切り替えている。

従属規則の自動生成:
これが、一番のキモとも言える、従属規則は、ソースコードでインクルードされた
全てのファイルパスを示したもので、この規則を使って、「make」がファイルの更
新が起こった場合に自動で対応するソースをコンパイルする事が出来る。

この規則を自分で記述するのは愚かと思えるくらい面倒で、ソースのインクルード
を増やしたり減らしたりする度に記述をやりなおさないとならないし、インクルー
ドを全て巡らないとならない。
※大抵、make の入門編などでは、これを入力させるように説明している事が多い。

そこで、これらを、ソースコードから辿って、自動で生成する事にした。
以前は、専用コマンド「makedepend」を使っていたが、gcc に「-MM」というオプ
ションがありこれにより、ソースコードがインクルードしているファイルをリスト
出来る、ただし、gcc が出力するパスは、完全では無い為、「sed」を使って、完
全なパスを生成するように工夫している。

$(BUILD)/%.d: %.c
    mkdir -p $(dir $@); \
    $(CC) -MM -DDEPEND_ESCAPE $(COPT) $(CFLAGS) $(APPINCS) $< \
    | sed 's/$(notdir $*)\.o:/$(subst /,\/,$(patsubst %.d,%.o,$@) $@):/' > $@ ; \
    [ -s $@ ] || rm -f $@

※ C ソース用

$(BUILD)/%.d: %.cpp
    mkdir -p $(dir $@); \
    $(CP) -MM -DDEPEND_ESCAPE $(POPT) $(PFLAGS) $(APPINCS) $< \
    | sed 's/$(notdir $*)\.o:/$(subst /,\/,$(patsubst %.d,%.o,$@) $@):/' > $@ ; \
    [ -s $@ ] || rm -f $@

※ C++ ソース用
※「DEPEND_ESCAPE」は、特定のヘッダーで、従属規則が正しく生成できない場合に、
それをエスケープする。

release/main.o release/main.d: main.cpp ../G13/system.hpp ../common/io_utils.hpp \
 ../common/port_utils.hpp ../G13/port.hpp ../common/fifo.hpp \
 ../common/uart_io.hpp ../G13/sau.hpp ../G13/intr.hpp \
 ../common/itimer.hpp ../G13/timer.hpp ../common/task.hpp \
 ../common/adc_io.hpp ../G13/adc.hpp ../common/delay.hpp \
 ../common/format.hpp ../common/monograph.hpp ../common/font6x12.hpp \
 ../common/kfont12.hpp ../ff12a/src/ff.h ../ff12a/src/integer.h \
 ../ff12a/src/ffconf.h ../common/sdc_io.hpp ../ff12a/mmc_io.hpp \
 ../common/csi_io.hpp ../ff12a/src/diskio.h ../common/string_utils.hpp \
 ../common/time.h ../common/filer.hpp ../common/bitset.hpp \
 ../common/switch_man.hpp ../chip/ST7565.hpp

※これは、自動で生成された、「main.cpp」の従属規則「release/main.d」

プラットホーム毎の違いを吸収する:

ifeq ($(OS),Windows_NT)
SYSTEM := WIN
else
  UNAME := $(shell uname -s)
  ifeq ($(UNAME),Linux)
    SYSTEM := LINUX
  endif
  ifeq ($(UNAME),Darwin)
    SYSTEM := OSX
    OSX_VER := $(shell sw_vers -productVersion | sed 's/^\([0-9]*.[0-9]*\).[0-9]*/\1/')
  endif
endif

この判定で、変数「SYSTEM」にOSの属性が代入される。
又、「OSX」の場合、「OSX_VER」にバージョン番号が代入される。