ビルド (ソフトウェア)
ソフトウェアのビルド(build)は、プログラミング言語で書かれたソースコードファイルや各種リソースファイルを独立したソフトウェア生成物に変換するコンピュータ上で実行されるプロセス、またはその結果を指す。ビルドの最終生成物はバイナリ形式の実行ファイルであったり、再利用可能なライブラリであったり、バイトコードあるいはそれらをまとめたアーカイブであったりすることもある。
ビルドにはいくつかのステップがあり、その内容はプログラミング言語やビルドツール、開発環境や実行環境(ターゲットアーキテクチャ、オペレーティングシステムあるいは仮想マシン)によっても異なる。例えばC言語あるいはC++の場合、ソースファイル(ソースコード)をコンパイラによってオブジェクトファイル(オブジェクトコード)に翻訳(コンパイル)した後、リンカによってオブジェクトコードを結合し、実行ファイルを生成する。プログラムから実行時に参照される各種アイコン画像やローカライズされた文字列などを実行ファイル内に「リソース」として含めることもあり、ビルドプロセスでリソースのコンパイルとリンクを実行する。
単純なプログラムでは、単一のソースファイルをコンパイルするだけで済むが、複雑なソフトウェアでは通例メンテナンス性向上の観点からソースコードはモジュールごとに分類・分割された多数のファイルによって構成されており、各ファイルを個別にコンパイル(分割コンパイル)した後で、リンク時に実体の依存関係を解決する。C/C++のような原始的なプログラミング言語では、ヘッダファイルを利用して、ユーザー定義型や定数、および関数などの定義または宣言を共有することが多い。ヘッダーファイルのインクルードにより、ファイル間の依存関係が発生する。
コンピュータプログラムのビルドは、一般にビルドツールと呼ばれるプログラムを使い、他のプログラムを制御・統合して行う。ビルドツールの例としては、make、ant、maven、SCons、MSBuild、Gradleなどがある。統合開発環境には通例何らかのビルドツールが内蔵されており、直感的かつシームレスにプログラミングとビルド作業のサイクルを行なえるようになっている。ビルドユーティリティは、各種ファイル群を正しい順序でコンパイルしリンクする必要がある。また、開発時には何度もビルドを繰り返すが、前回のビルドから何も変更されていないファイルはコンパイルする必要がない(ただし、ヘッダファイルなどの依存関係も考慮する必要がある)。洗練されたビルドユーティリティは無駄な再コンパイルをしないようにして、ビルドに要する時間を短縮している。Subversionなどのバージョン管理システムはビルドユーティリティの機能を内蔵している。さらに複雑なプロセスになると、ビルド中に他のプログラムを使ってコードやデータを生成することもある。
ビルドの自動化とは、以下のような開発作業をスクリプト化または自動化することを指す。
ソースコードをバイナリコードにコンパイルする。
バイナリコードをパッケージ化する。
テストを実行する。
生産システムに配備する。
文書やリリースノートを作成する。
ビルドを自動化する利点は次の通り。
製品品質を向上させる。
特定のエキスパートに依存しなくて済む。
履歴を保持することで、問題発生時に原因を究明しやすい。
時間と金を節約できる。
ビルドツールはユーザーが手動で起動することもできるが、時間を決めて毎日実行する場合(夜間に行うことが多いのでナイトリービルド (Nightly Build) などと呼ぶ)、バージョン管理システムがソースファイルの変更がコミットされるたびに自動的に実行する場合などもある。
実行ファイル形式のプログラムを成果物(最終出力)とする方式のソフトウェア開発では、まずソースファイル(ソースコード)をコンパイルしてオブジェクトファイル(オブジェクトコード)に変換した後、そのオブジェクトファイルをリンクし、コンピュータ(オペレーティングシステム)が直接実行できるバイナリ形式のファイルを生成する作業が必要となる。ソースファイルが1つしかないならば、それらの作業をコマンドラインから実行するのも簡単だが、ソースファイルが多数存在するプロジェクトでは、正しい順序とオプションで手作業するのは至難の業である。この問題を解決する手段としてmakeのようなスクリプト言語が登場した。それにより、コンパイルやリンクを正しい順序で行うスクリプトを書くことができる。GNU Makeでは更にソースコードの依存関係を管理でき、変更された部分だけをコンパイルするインクリメンタルビルドが可能になった。これがビルドの自動化の始まりである。その第一の目的はコンパイラやリンカの呼び出しを自動化することだった。ビルドプロセスが複雑化するに従い、コンパイラ呼び出しの前後に様々な作業(バージョン管理システムからのチェックアウトや生成した実行ファイルのテスト環境への投入など)を追加するようになった。
さらに、ビルドツールは進化し、スクリプト(Makefile)自体を生成するものが登場した。これらのツールは、頻繁にビルドを行う継続的インテグレーションで特に便利である。また、複数のターゲットプラットフォームや異なる開発環境に応じたビルドスクリプトを必要に応じて自動生成するのにも役立つ。
高度なビルドツールは分散ビルドや分散処理が可能である。「分散ビルド」とは、実際のコンパイルやリンクをそれぞれ別々のマシン上で行い、ビルドを高速化する手法である。一方、分散処理とは例えば、テスト用スクリプトを複数のマシンに配置し、新たな実行ファイルのテストを分散環境で自動的に行うことなどを指す。分散ビルドを行うには、ソースコードの依存関係を自動的に把握することが必須である。一部のビルドツールは依存関係を自動的に発見でき(Rational ClearMake、Electric Cloud ElectricAcceleratorなど)、別のビルドツールではユーザーが定義した依存関係のみを扱う(Platform LSF lsmakeなど)。ソースコードの依存関係を把握できるビルドツールは、コンパイルおよびリンクを並行モードで実行するよう設定できる。すなわち、コンパイラやリンカを複数コアのマシン上でマルチスレッドモードで実行することができる。全てのビルドツールが分散ビルドを実施できるわけではない。
分散ビルドの例として Xoreax の IncrediBuildがある。
複数のソースファイルから成るソフトウェアの開発プロセスにおいて、ソースファイルにちょっとした変更を加えただけで、毎回すべてのソースファイルを無条件にコンパイルし直すのは効率が悪い。自動化されたビルドシステムでは、タイムスタンプなどを利用して変更のあったファイルのみを検出し、そのファイル自身とそれに依存する関連ファイルのみをコンパイルし直してリンクする「インクリメンタルビルド」(増分ビルド)をサポートしている。これにより、コード編集・ビルド・実行・テストの開発サイクルを高速化することができる。
さらに、変更のあったモジュールのみをリンクし直す「インクリメンタルリンク」(増分リンク)をサポートする処理系も存在する。実行時に個別のライブラリモジュールをロードして結合する動的リンクライブラリ方式の場合、リンク処理はシンボル情報のみが書かれたインポートライブラリを実行ファイルにリンクするだけなので高速だが、ビルド時にすべてのモジュールを結合して1つのファイルを生成する静的リンクライブラリ方式の場合、リンク処理には時間がかかるため、インクリメンタルリンクが特に効果を発揮する。
ビルドシステムではなく、コンパイラモジュール自身が「インクリメンタルコンパイル」(増分コンパイル)をサポートしている言語もある。例えばRustのコンパイラはバージョン1.24以降でインクリメンタルコンパイルをサポートする。Rustコンパイラは、最初のステップとして抽象構文木 (abstract syntax tree, AST) を構築し、次にASTを解析することで型情報 (type information) および個々の関数に関する中位の中間表現 (mid-level intermediate representation, MIR) を生成する。その後、エラーが見つからなければ、ソースレベルのモジュールごとにオブジェクトファイルを生成しながら、MIRから機械語コードに変換される。これらの型情報やMIR、およびオブジェクトファイルの要素を中間結果 (intermediate result) とみなし、ディスクにキャッシュとして保存しておき、2回目以降のコンパイルではキャッシュからロードする。また依存関係グラフをもとに、変更のあった要素だけを更新する。ただしRustのインクリメンタルコンパイルは2回目以降のビルドサイクルを高速化させるものの、最終的に生成されるバイナリの実行速度を若干低下させるデメリットがあるため、リリースビルドでは有効化されない。
Go言語のコマンドラインツールは、ソースファイルのコンパイルとパッケージのビルドをそれぞれ別々にサポートするが、Go 1.10以降のgo buildコマンドは、純粋にソースファイルの内容、ビルドフラグ、およびコンパイル済みパッケージ内に保存されているメタデータに基づいて、無効になったパッケージを検出してインクリメンタルビルドするようになり、ファイルの更新日時は無関係となった。
ビルド自動化の一形態として、Makefileの自動生成がある。例えば、次のようなツールがMakefileを自動生成する。