こんにちは、ナナです。
Visual Studioといった統合開発環境を利用している方は、プログラムから実行ファイルを作り出す時に「ビルド」と呼ばれるコマンドを使っていますよね。
「ビルド」コマンドは、次のように統合開発環境のメニューから実行していることでしょう。「ビルド」のすぐ下に「リビルド」というコマンドもあるのがわかります。
皆さん、この「ビルド」と「リビルド」の違いを理解していますか?
「ビルド」と「リビルド」の違いを理解していない方って結構いるのです。この違いを知ることで効率的な開発ができるようになります。
本記事では次の疑問点を解消する内容となっています。
では、「ビルド」の仕組みとは何かを学んでいきましょう。
ビルドとは何なのか?
まず最初に「ビルド」コマンドとは何かを押さえておきましょう。
「ビルド」はアプリケーションを構築するためのコマンド
「ビルド(build)」とは「構築する」という意味です。「構築する」という言葉って、家のようですね。
プログラムの作成イメージは、実は建築物と似ています。
「ソースファイル」とはアプリケーションという家を構築するための資材であり、「ビルド」という工程により構築されるのです。
「ビルド」の実体はコンパイラとリンカ
C言語の開発において「ビルド」というコマンドの正体は、「コンパイラ」と「リンカ」というツールです。
「コンパイラ」と「リンカ」の役割は、次のようなものになります。
ツール | 役割 |
---|---|
コンパイラ | プログラムをコンピュータ上で動作する部品に加工する。 |
リンカ | 翻訳されたプログラム部品をひとつのアプリケーションとして組み立てる。 |
各ツールの詳細が知りたい方は、次の記事を読んでおくとよいでしょう。
》参考:コンパイラの役割【ビルドエラーの取り除き方の鉄則】
》参考:リンカの役割【リンクエラーが起きた時の対処方法】
「ビルド」コマンドとは「コンパイラ」と「リンカ」を動かすことで、ソースファイルからアプリケーションを構築するための命令なのです。
コンパイラとリンカの役割
「コンパイラ」と「リンカ」には異なる役割があります。
次の図は簡略化した図ですが、「ビルド」と「リビルド」の違いを知るための最低限の構成図です。
ツール | 実施単位 |
---|---|
コンパイル | コンパイラにより、ソースファイル1つに対して1度実施される。 |
リンク | リンカにより、アプリケーションを作り出す際に1度だけ実施される。 |
「コンパイル」はソースファイル1つに対して1回、「リンク」は全体に対して1回のみ実施されます。
つまり、ソースファイルが3つ存在した場合は、コンパイルは3回、リンクは1回実施されることになります。
これは「ビルド」というものを知る上で非常に大事な知識になります。
1つのアプリケーションを作り出すためのソースファイルとは、複数存在することが認められています。大規模なプロジェクトになると数百や数千ファイルといったことになることもあります。
差分ビルドの必要性と仕組み
実は、「ビルド」コマンドは実際は「差分ビルド」というものです。それでは「差分ビルド」とは何なのかを解説しましょう。
コンパイルは時間が掛かる
皆さんこれまでにプログラムを作ってたくさんの「ビルド」コマンドを実施してきたことでしょう。
その際に「ビルド」コマンドが完了して、結果が出るまでの時間が気になることはあまりなかったのではないでしょうか。
しかし、それは規模の小さなアプリケーションを作ってきたからにすぎないのです。
仮に1つのソースファイルをコンパイルするのに1秒、全体をリンクするのに1秒かかるとしましょう。大した時間ではないように感じますね。
それではソースファイルが100個存在するアプリケーションのビルドには、いったいどれくらいの時間が必要でしょうか?
ソースファイル数 | コンパイル時間 | リンク時間 | 合計時間 |
---|---|---|---|
1個 | 1秒 | 1秒 | 2秒 |
100個 | 100秒 | 1秒 | 101秒 |
このように小さな規模であると大したことがないように思いますが、大きな規模になると案外ビルドに必要な時間が大きくなることがわかります。
この時間を短縮するビルドが「差分ビルド」と呼ばれるビルド方法です。
続いて「差分ビルド」の仕組みを解説しましょう!
オブジェクトファイルを生成するときのルール
「オブジェクトファイル」とはソースファイルをコンパイルした結果、生成されるファイルのことです。
1つの「ソースファイル」に対して、1つの「オブジェクトファイル」が生成されます。
ここで「オブジェクトファイル」を生成する上で、大事なルールがあります。それは、
ソースファイルの内容が変わらなければ、生成されるオブジェクトファイルは変わらない
ことです。
このルールを利用して「差分ビルド」は行われます。
「差分ビルド」を利用したオブジェクトファイルの生成イメージ
「差分ビルド」の目的はビルド時間の短縮です。では、短縮する流れをイメージ図で示しましょう。
仮に100個のソースファイルがあるとしましょう。「ビルド」コマンドでアプリケーションを作成したとします。
このように最初に行う「ビルド」においては最大ビルド時間が必要となります。この時間は短縮することはできません!
では、この後に「tmp2.c」ファイルのみを修正して再度アプリケーションを再作成したとしましょう。
この時に更新が必要なオブジェクトファイルは「tmp2.obj」のみです。
他のソースファイルは更新していないのですから、すでに生成済みのオブジェクトファイルを使いまわせばよいわけです。これによってビルド時間が大幅に早くなりました。
このように開発サイクルというのは、たくさんのソースファイルを少しずつビルドしながら開発していくため「差分ビルド」が時間短縮に有効なのです。
「ビルド」コマンドを実施すると、編集されたソースファイルに対してのみオブジェクトファイルを更新するようになっています。これが「差分ビルド」です。
開発工程における「ビルド」⇒「修正」⇒「ビルド」⇒「修正」を繰り返す流れにおいて、「差分ビルド」は効率的にビルド時間を短縮することができるのです。
オブジェクトファイルを再生成する判定方法
それでは「ソースファイルが更新されたものに対して、オブジェクトファイルを作り出す」という判断は、いったい何をもって実施するのでしょう。
実はこの判断は、ファイルの更新時刻によって行われます。
時間軸に沿って「ソースファイル」と「オブジェクトファイル」がビルドコマンドでどのように判断されるかを図示化しましょう。
ソースファイルを作りたての状態で「ビルド」を行うと差分ビルドが実施されます。
この場合、オブジェクトファイルはもともと存在しないため、差分ビルドによってオブジェクトファイルが生成されることになります。
それでは、続けてもう一度「ビルド」を実施した場合はどうなるでしょうか?
差分ビルドでは「ソースファイル」と「オブジェクトファイル」のファイル更新時刻を比較します。
オブジェクトファイルの方が新しい場合は、「ソースファイルは更新されていない!」と判断しオブジェクトファイルの更新をしないようにします。
つまり、このケースにおいては「オブジェクトファイルを更新しなくてよい」と判断するのです。
それでは続いて、ソースファイルに修正をした後に再度「ビルド」を行ってみましょう。
ソースファイルを修正したためファイルの更新時刻が新しくなりました。
「ビルド」を行うとオブジェクトファイルの時刻よりも、ソースファイルの時刻の方が新しいため、「ソースファイルが新しく更新されたぞ!」と判断し、オブジェクトファイルを再生成するのです。
「ソースファイル」と「オブジェクトファイル」のファイル時刻を比較することで、オブジェクトファイルを生成する必要性があるのかを判断しているのです。
これが「差分ビルド」の仕組みです。
差分ビルドの仕組みを知らない人が罠にハマるシーンを紹介
「差分ビルド」の仕組みを知ることで、無駄なコンパイル時間を短縮することができますが、それ以外にも知っておくべき理由があります。
実は「差分ビルド」の仕組みを知らない人が、よくある開発作業でハマってしまうシーンがあります。それを紹介しましょう。
ソースファイルを一時的にバックアップしておくシーンが危険
皆さんは顧客の要望に従い、とあるソースファイルを作っていたとしましょう。「ビルド」を行ってオブジェクトファイルを作り、アプリケーションを動かしていたとします。
ここで新たに、顧客から「プログラムを修正してほしい」との依頼がありました。
プログラミングに慎重な皆さんは、「変更することでうまく動かなくなってしまったときのため、念のため元のソースファイルをバックアップしておこう!」と考え、別のフォルダにソースファイルをコピーしておきました。
皆さんはソースファイルをいろいろと変更し、顧客の修正要望に応えられるように頑張りました。
しかし、ここで顧客から一報が入ります。「プログラムの修正を中止してください。もとのプログラムの動作で問題なかったです」と。
皆さんは「なんだよ~」と思いつつ、「ソースファイルをバックアップしておいたから、元のファイルに戻せばいいんだけど!」と言いながら、元のファイルを上書きしました。
皆さんはソースファイルを元に戻したのですから、再度「ビルド」を実施しますよね。
ここで、問題が発生するのです。
さぁ、バックアップしておいたファイルで上書きした「ソースファイル」と「オブジェクトファイル」の更新時刻の関係はどうなっているでしょうか?
本記事をしっかりと理解した方はわかりますね。
そうです。「ビルド」による差分更新をしてもオブジェクトファイルの更新時間の方が新しいため、オブジェクトファイルの更新がされないのです。
それはつまり、ソースファイルは元に戻しましたが、アプリケーションの動きは元に戻っていないことを示しているのです。
「差分ビルド」の仕組みを知らないと、このような手順において問題があることに気が付くことができません。だからこそ仕組みを知る必要性があるのです。
では、このような手順で作業した場合はどうしたら問題が解決するのか?それが「リビルド」というコマンドなんです。
「リビルド」コマンドの役割
「リビルド(re-build)」とは「再構築する」という意味です。リビルドの役割を解説していきましょう。
「リビルド」と「ビルド」の違い
まず、「ビルド」と「リビルド」の違いを簡単に説明しましょう。
ビルドの種類 | ビルド対象範囲 |
---|---|
ビルド | 更新されたソースファイルに対してビルドを行う。差分ビルドとも呼ぶ。 |
リビルド | 全てのソースファイルを強制的にビルドを行う。フルビルドとも呼ぶ。 |
このように「リビルド」とは、全てを再構築するという意味であり、ソースファイルの更新状態とは関係なく強制的に全ソースファイルを再度コンパイルします。
数百といったソースファイルが存在するプロジェクトでは、不用意に「リビルド」を行うとなかなかビルドが終わらないため注意しましょう。
リビルドを使うシーン
開発作業の中では基本的には差分ビルドである「ビルド」コマンドを実行していくことになります。
それでは「リビルド」コマンドはいったいどんな時に使うのでしょうか?
「ビルド」と「リビルド」の特徴をよく理解した上で皆さんも使い分けるとようになっておきましょう。
わたしの経験上ですがこのようなシーンで「リビルド」コマンドは使われます。