ビルド (ソフトウェア)

ソフトウェアのビルドは、プログラミング言語で書かれたソースコードファイルや各種リソースファイルdate=2023年3月}}を独立したソフトウェア生成物に変換するコンピュータ上で実行されるプロセス、またはその結果を指す。ビルドの最終生成物はバイナリ形式の実行ファイルであったり、再利用可能なライブラリであったり、バイトコードあるいはそれらをまとめたアーカイブであったりすることもある。

1. 概要
-----

ビルドにはいくつかのステップがあり、その内容はプログラミング言語やビルドツール、開発環境や実行環境によっても異なる。例えばC言語あるいはC++の場合、ソースファイルをコンパイラによってオブジェクトファイルに翻訳した後、リンカによってオブジェクトコードを結合し、実行ファイルを生成する。プログラムから実行時に参照される各種アイコン画像やローカライズされた文字列などを実行ファイル内に「リソース」として含めることもあり、ビルドプロセスでリソースのコンパイルとリンクを実行する。

単純なプログラムでは、単一のソースファイルをコンパイルするだけで済むが、複雑なソフトウェアでは通例メンテナンス性向上の観点からソースコードはモジュールごとに分類・分割された多数のファイルによって構成されており、各ファイルを個別にコンパイルした後で、リンク時に実体の依存関係を解決する。C/C++のような原始的なプログラミング言語では、ヘッダファイルを利用して、ユーザー定義型や定数、および関数などの定義または宣言を共有することが多い。ヘッダーファイルのインクルードにより、ファイル間の依存関係が発生する。

コンピュータプログラムのビルドは、一般にビルドツールと呼ばれるプログラムを使い、他のプログラムを制御・統合して行う。ビルドツールの例としては、make、ant、maven、SCons、MSBuild、Gradleなどがある。統合開発環境には通例何らかのビルドツールが内蔵されており、直感的かつシームレスにプログラミングとビルド作業のサイクルを行なえるようになっている。ビルドユーティリティは、各種ファイル群を正しい順序でコンパイルしリンクする必要がある。また、開発時には何度もビルドを繰り返すが、前回のビルドから何も変更されていないファイルはコンパイルする必要がない。洗練されたビルドユーティリティは無駄な再コンパイルをしないようにして、ビルドに要する時間を短縮している。Subversionなどのバージョン管理システムはビルドユーティリティの機能を内蔵している。さらに複雑なプロセスになると、ビルド中に他のプログラムを使ってコードやデータを生成することもある。

2. ビルドの自動化
----------

ビルドの自動化とは、以下のような開発作業をスクリプト化または自動化することを指す。

ソースコードをバイナリコードにコンパイルする。
バイナリコードをパッケージ化する。
テストを実行する。
生産システムに配備する。
文書やリリースノートを作成する。ビルドを自動化する利点は次の通り。

製品品質を向上させる。
コンパイルとリンクの所要時間短縮
冗長なタスクの排除
手順ミスの排除
特定のエキスパートに依存しなくて済む。
履歴を保持することで、問題発生時に原因を究明しやすい。
時間と金を節約できる。ビルドツールはユーザーが手動で起動することもできるが、時間を決めて毎日実行する場合、バージョン管理システムがソースファイルの変更がコミットされるたびに自動的に実行する場合などもある。

3. 歴史
-----

実行ファイル形式のプログラムを成果物とする方式のソフトウェア開発では、まずソースファイルをコンパイルしてオブジェクトファイルに変換した後、そのオブジェクトファイルをリンクし、コンピュータが直接実行できるバイナリ形式のファイルを生成する作業が必要となる。ソースファイルが1つしかないならば、それらの作業をコマンドラインから実行するのも簡単だが、ソースファイルが多数存在するプロジェクトでは、正しい順序とオプションで手作業するのは至難の業である。この問題を解決する手段としてmakeのようなスクリプト言語が登場した。それにより、コンパイルやリンクを正しい順序で行うスクリプトを書くことができる。GNU Makeでは更にソースコードの依存関係を管理でき、変更された部分だけをコンパイルするインクリメンタルビルドが可能になった。これがビルドの自動化の始まりである。その第一の目的はコンパイラやリンカの呼び出しを自動化することだった。ビルドプロセスが複雑化するに従い、コンパイラ呼び出しの前後に様々な作業を追加するようになった。

さらに、ビルドツールは進化し、スクリプト自体を生成するものが登場した。これらのツールは、頻繁にビルドを行う継続的インテグレーションで特に便利である。また、複数のターゲットプラットフォームや異なる開発環境に応じたビルドスクリプトを必要に応じて自動生成するのにも役立つ。

高度なビルドツールは分散ビルドや分散処理が可能である。「分散ビルド」とは、実際のコンパイルやリンクをそれぞれ別々のマシン上で行い、ビルドを高速化する手法である。一方、分散処理とは例えば、テスト用スクリプトを複数のマシンに配置し、新たな実行ファイルのテストを分散環境で自動的に行うことなどを指す。分散ビルドを行うには、ソースコードの依存関係を自動的に把握することが必須である。一部のビルドツールは依存関係を自動的に発見でき、別のビルドツールではユーザーが定義した依存関係のみを扱う。ソースコードの依存関係を把握できるビルドツールは、コンパイルおよびリンクを並行モードで実行するよう設定できる。すなわち、コンパイラやリンカを複数コアのマシン上でマルチスレッドモードで実行することができる。全てのビルドツールが分散ビルドを実施できるわけではない。

分散ビルドの例として Xoreax の IncrediBuildがある。

4. インクリメンタルビルド
--------------

複数のソースファイルから成るソフトウェアの開発プロセスにおいて、ソースファイルにちょっとした変更を加えただけで、毎回すべてのソースファイルを無条件にコンパイルし直すのは効率が悪い。自動化されたビルドシステムでは、タイムスタンプなどを利用して変更のあったファイルのみを検出し、そのファイル自身とそれに依存する関連ファイルのみをコンパイルし直してリンクする「インクリメンタルビルド」をサポートしている。これにより、コード編集・ビルド・実行・テストの開発サイクルを高速化することができる。

さらに、変更のあったモジュールのみをリンクし直す「インクリメンタルリンク」をサポートする処理系も存在する。実行時に個別のライブラリモジュールをロードして結合する動的リンクライブラリ方式の場合、リンク処理はシンボル情報のみが書かれたインポートライブラリを実行ファイルにリンクするだけなので高速だが、ビルド時にすべてのモジュールを結合して1つのファイルを生成する静的リンクライブラリ方式の場合、リンク処理には時間がかかるため、インクリメンタルリンクが特に効果を発揮する。

ビルドシステムではなく、コンパイラモジュール自身が「インクリメンタルコンパイル」をサポートしている言語もある。例えばRustのコンパイラはバージョン1.24以降でインクリメンタルコンパイルをサポートする。Rustコンパイラは、最初のステップとして抽象構文木  を構築し、次にASTを解析することで型情報  および個々の関数に関する中位の中間表現  を生成する。その後、エラーが見つからなければ、ソースレベルのモジュールごとにオブジェクトファイルを生成しながら、MIRから機械語コードに変換される。これらの型情報やMIR、およびオブジェクトファイルの要素を中間結果  とみなし、ディスクにキャッシュとして保存しておき、2回目以降のコンパイルではキャッシュからロードする。また依存関係グラフをもとに、変更のあった要素だけを更新する。ただしRustのインクリメンタルコンパイルは2回目以降のビルドサイクルを高速化させるものの、最終的に生成されるバイナリの実行速度を若干低下させるデメリットがあるため、リリースビルドでは有効化されない。

4.1. Go
-------

Go言語のコマンドラインツールは、ソースファイルのコンパイルとパッケージのビルドをそれぞれ別々にサポートするが、Go 1.10以降のgo buildコマンドは、純粋にソースファイルの内容、ビルドフラグ、およびコンパイル済みパッケージ内に保存されているメタデータに基づいて、無効になったパッケージを検出してインクリメンタルビルドするようになり、ファイルの更新日時は無関係となった。

5. Makefile生成
-------------

ビルド自動化の一形態として、Makefileの自動生成がある。例えば、次のようなツールがMakefileを自動生成する。

GNU Automake
CMake
imake
qmake
Apache Ant
Apache Maven

6. 脚注
-----

7. 参考文献
-------

Mike Clark: //Pragmatic Project Automation//, The Pragmatic Programmers ISBN 0-9745140-3-9

8. 関連項目
-------

スクリプト言語
ソフトウェアテスト
ソフトウェアのバージョン