【Tips】何度も繰り返す開発環境構築をMakefileとシェルスクリプトで楽にする

前書き:環境構築は面倒だから、楽をしたい

開発環境の構築は、その規模の大小に関わらず、面倒な作業です。

環境構築の面倒な部分
  • 忘れてしまった環境構築手順を思い出す事
  • 設定ファイルの修正(数が多いと特に面倒)
  • パッケージマネージャが使えない場合、アプリの手動ビルド&インストール
  • 依存関係が自動解決できない場合、依存関係を調査
  • 構築を複数回繰り返す事(自宅/会社PCへの対応あるいは環境を壊した時

環境構築は、プログラミングをする前段階の作業ですので、ここで消耗したくありません。

そこで、本記事ではMakefileとシェルスクリプトを併用する形で、環境構築を自動化する方法を説明します(何故、Makefileを使うのかという理由も含めて説明します)。

# 余談ですが、世の中には「何でもかんでもMakefileで解決する方法」を嫌いな人も居るので、本記事の内容をチームに展開する際は気をつけてください。

           

シェルスクリプトのみのキツイ点

環境構築を自動化しようと考えた場合、多くの人がBash等でシェルスクリプトを作成すると思います。シェルスクリプトのみで環境構築を管理する場合は、インストールしたいアプリ数が増えてくると、以下の問題が見え隠れしてきます。

インストーラを単一スクリプトにすると、スクリプトが手に追えないサイズ(負債)に変化

前提として、複数アプリのインストーラを単一スクリプトで作成した場合、「一部のアプリのみインストール」がしづらくなります。オプションでインストール対象アプリを切り分ける事もできますが、その処理を追加する分だけスクリプトが肥大化します。

サイズが1000Stepに達する辺りで、楽をするためのスクリプトが管理しづらい負債(もしくは自分しか管理できないスクリプト)へと変わっている事に気づく筈です。Google先生が薦めるシェルスクリプトサイズは100Stepなので、その10倍もある状態はヨシッ!と言えないでしょう。

              

インストーラを複数のスクリプトにすると、一括インストールがしづらく

スクリプトの肥大化を防ぐために、アプリ単位でインストールスクリプトを作成したとしましょう。この場合、任意のアプリインストールは楽になりますが、全てのアプリを一括インストールしづらくなります。

そこで、少し考えた私は一括インストールを容易にするため、各インストーラを制御するスクリプトを作成しました。すると、どうでしょう。「インストーラ同士の依存関係を制御する処理(※1)」や「オプションによるインストーラ呼び出し処理」を書き始めた辺りで、同じ過ち(=インストーラ制御スクリプトの肥大化)を犯している事に気づきます。

※1 例えば、RubyとRuby on Railsのインストーラがあった場合、RoRより先にRubyをインストールします。RoRインストーラにRubyインストール処理を書いても良いのですが、Rubyインストーラとコードが重複するため、好ましくないです。

     

まとめると、シェルスクリプトのみで環境構築を行う場合、以下の点が問題になります。

シェルスクリプトのみの環境構築でキツイ点
  • 単一スクリプトで複数アプリのインストーラを作成した場合、管理しづらい
  • インストーラスクリプトを分割すると、一括インストールがしづらい
  • インストーラスクリプト間の依存関係解決がしづらい

                      

解決策:Makefileでインストーラスクリプトを制御

Makefile(makeコマンド)は、C/C++で頻繁に用いられるツールであり、コンパイル/依存関係の管理/インストールを簡単にしてくれます。

Makefileの書式は、以下の通りです。

上記の書式において、

  • ターゲット = インストールしたいアプリ名
  • 依存するファイル(依存するターゲット)= 依存するアプリ名
  • 処理 = インストーラスクリプトの呼び出し

とすれば、インストーラ間の依存関係を解決できます。また、全てのアプリをインストールする場合は、allターゲット(依存関係 = 全てのインストール対象アプリに依存)を作成すれば良いです。

以下、実装例です。

基本的には、各ターゲットはinstallersディレクトリに移動した後、指定のインストーラスクリプトを実行するような作りです。依存対象がある場合は、依存しているターゲット処理を先に行います。

例えば、”$ make java”と実行した場合、apt/sdkmanターゲットの処理を行った後にjavaインストールが始まります。

また、.DEFAULT_GOALをhelpターゲットとする事によって、makeコマンドを引数なしで実行した場合はインストール対象を促すhelpメッセージを表示します。以下、helpの出力例です。

                      

helpターゲットの処理:Makefileの自己文書化

Makefileのhelpターゲットに書かれた処理について、どのような処理を実行しているかを不思議に感じた人が居るかも知れません。

上記の処理は、Makefileを自己文書化するための記述です。自己文書化=helpメッセージというニュアンスであり、2016年にPOSTDの記事で紹介された内容を拝借しました。

自己文書化を行う処理は、最終行(grepコマンドを実行する行)です。この行は、「ターゲット名」と「ターゲットと同じ行に書かれ、かつ”##”から始まるコメント」を抽出します。そして、ターゲット名でソートを行い、色付きで抽出行を出力します。

正規表現やawk、printfに関する詳細な説明が必要な場合は、外部サイトに説明があります。

                

最後に

Makefileとシェルスクリプトを組み合わせて環境構築する方法は、修正箇所が少なく、汎用性も高いです。Makefileに馴染みがない方には取っ付きづらいかも知れませんが、C/C++を嗜む方には受け入れて貰える方法だと考えています。

                                            

おすすめ

2件のフィードバック

  1. 2021年5月22日

    […] […]

  2. 2022年1月11日

    […] Makefileは自己文書化されています。makeコマンドを引数なしで叩いた場合はビルドが始まらず、Makefileがどのようなターゲットを持ち、そのターゲットが何をするのかの説明文(helpメッセージ)が表示されます。  […]