このページでは、ビルドシステムの概要、ビルドシステムの機能、ビルドシステムを使用する理由、組織の規模が拡大するにつれてコンパイラとビルド スクリプトが最適な選択肢ではなくなる理由について説明します。これは、ビルドシステムの経験があまりないデベロッパーを対象としています。
ビルドシステムとは
基本的に、すべてのビルドシステムには単純な目的があります。エンジニアが作成したソースコードを、マシンが読み取れる実行可能バイナリに変換することです。ビルドシステムは、人間が作成したコードだけでなく、テスト用や本番環境へのリリース用に、マシンがビルドを自動的に作成できるようにもします。数千人のエンジニアがいる組織では、ほとんどのビルドがエンジニアによって直接トリガーされるのではなく、自動的にトリガーされるのが一般的です。
コンパイラを使用できないのですか?
ビルドシステムの必要性はすぐには明らかでない場合があります。ほとんどのエンジニアは、コードを学習する際にビルドシステムを使用しません。ほとんどのエンジニアは、コマンドラインから gcc
や javac
などのツールを直接呼び出すか、統合開発環境(IDE)で同等のツールを呼び出すことから始めます。すべてのソースコードが同じディレクトリにある限り、次のようなコマンドで問題なく動作します。
javac *.java
これにより、Java コンパイラは、現在のディレクトリ内のすべての Java ソースファイルをバイナリ クラスファイルに変換します。最も単純なケースでは、これだけで十分です。
しかし、コードが拡張されると、複雑さが増します。javac
は、現在のディレクトリのサブディレクトリを検索して、インポートするコードを見つけることができるほどスマートです。ただし、ファイルシステムの他の部分(複数のプロジェクトで共有されているライブラリなど)に保存されているコードを見つける方法はありません。また、Java コードのビルド方法のみを認識します。大規模なシステムでは、さまざまなプログラミング言語で記述されたさまざまな部分が、それらの部分の間に依存関係のウェブを形成していることがあります。つまり、単一言語のコンパイラではシステム全体をビルドできない可能性があります。
複数の言語または複数のコンパイル単位のコードを使用する場合、コードのビルドは 1 ステップのプロセスではなくなります。次に、コードが依存するものを評価し、それらの部分を適切な順序でビルドする必要があります。場合によっては、部分ごとに異なるツールセットを使用します。依存関係が変更された場合は、古いバイナリに依存しないように、このプロセスを繰り返す必要があります。中程度のサイズのコードベースでも、このプロセスはすぐに面倒になり、エラーが発生しやすくなります。
また、コンパイラは、Java のサードパーティの JAR
ファイルなどの外部依存関係を処理する方法も知りません。ビルドシステムがない場合、インターネットから依存関係をダウンロードし、ハードドライブの lib
フォルダに配置し、そのディレクトリからライブラリを読み取るようにコンパイラを構成することで、この問題を管理できます。時間が経つにつれて、これらの外部依存関係の更新、バージョン、ソースを維持することは困難になります。
シェル スクリプトはどうですか?
趣味のプロジェクトが最初はコンパイラだけでビルドできるほどシンプルだったとしても、前述の問題に直面することがあります。ビルドシステムが必要ないと考えている場合でも、簡単なシェル スクリプトを使用して、退屈な部分を自動化し、正しい順序でビルドを処理できます。これはしばらくは役立ちますが、すぐにさらに多くの問題に直面するようになります。
面倒になります。システムが複雑になるにつれて、実際のコードと同じくらい多くの時間をビルド スクリプトの作成に費やすようになります。シェル スクリプトのデバッグは、ハックが重ねて追加されるため、困難です。
動作が遅い。古いライブラリに誤って依存しないように、ビルド スクリプトは実行するたびにすべての依存関係を順番にビルドします。再ビルドが必要な部分を検出するロジックを追加することを検討しますが、スクリプトとしては複雑でエラーが発生しやすくなります。または、再ビルドする必要がある部分を毎回指定することを検討しますが、その場合、最初からやり直すことになります。
リリースの準備が整いました。最終ビルドを作成するために jar コマンドに渡す必要があるすべての引数を把握しておいてください。また、アップロードして中央リポジトリに push する方法を覚えておきます。ドキュメントの更新をビルドして push し、ユーザーに通知を送信します。うーん、別のスクリプトが必要かも...
みなさんにハードドライブがクラッシュし、システム全体を再作成する必要がある場合。すべてのソースファイルをバージョン管理下に置いているのは賢明ですが、ダウンロードしたライブラリはどうでしょうか。削除したファイルをすべて確認し、最初にダウンロードしたときと同じバージョンであることを確認できますか?スクリプトは、特定のツールが特定の場所にインストールされていることに依存していた可能性があります。その同じ環境を復元して、スクリプトが再び機能するようにできますか?コンパイラを適切に動作させるために設定した環境変数を、ずっと前に設定して忘れてしまった場合はどうすればよいでしょうか。
問題があっても、プロジェクトは十分に成功しているため、エンジニアの採用を開始できます。災害が起きなくても、以前の問題が発生する可能性があることがわかりました。新しいデベロッパーがチームに加わるたびに、同じ面倒なブートストラップ プロセスを繰り返す必要があります。最善を尽くしても、各ユーザーのシステムには小さな違いが残ります。多くの場合、ある人のマシンで動作するものが別のマシンでは動作せず、そのたびにツールパスやライブラリ バージョンのデバッグに数時間かかり、違いを見つけるのに苦労します。
ビルドシステムを自動化する必要があると判断しました。理論的には、新しいコンピュータを入手して、cron を使用してビルド スクリプトを毎晩実行するように設定するだけです。面倒な設定プロセスは依然として必要ですが、人間の脳が小さな問題を検出して解決できるという利点はありません。毎朝出社すると、昨日デベロッパーが行った変更が、そのデベロッパーのシステムでは動作したが、自動ビルド システムでは動作しなかったため、前夜のビルドが失敗したことがわかります。毎回簡単な修正ですが、頻繁に発生するため、これらの簡単な修正を見つけて適用することに毎日多くの時間を費やしていることになります。
プロジェクトが大きくなるにつれて、ビルドの速度はどんどん遅くなります。ある日、ビルドの完了を待っている間、休暇中の同僚のアイドル状態のデスクトップを悲しげに見つめ、無駄になっている計算能力をすべて活用する方法があればいいのに、と願っています。
これは、スケールに関する古典的な問題です。1 人のデベロッパーが最大 200 行程度のコードに 1 ~ 2 週間ほど取り組む場合(大学を卒業したばかりのジュニア デベロッパーのこれまでの経験がすべてこれに該当する場合があります)、コンパイラだけで十分です。スクリプトを使用すると、さらに一歩進むことができます。ただし、複数のデベロッパーとそのマシン間で調整する必要がある場合、マシンのわずかな違いを考慮することが非常に難しくなるため、完璧なビルド スクリプトでも不十分です。この時点で、このシンプルなアプローチは機能しなくなり、本格的なビルドシステムに投資する時期です。