こんにちは、高木です。
前回はPHPからコンパイラーを呼び出す方法について解説しました。今回もその流れで、Makefileのようにソースファイルの更新があった場合だけコンパイルを行い、それ以外は何もしないようにしてみたいと思います。
PHPでファイルの更新時刻を取得するにはfilemtime関数を使います。filemtime関数を使ってソースファイルとコンパイル結果のファイルの更新時刻を求め、ソースファイルの方が新しければコンパイルを行い、そうでなければ何もしないようにすればOKです。filemtime関数はint型のUNIXタイムスタンプを返しますので、どちらのファイルが新しいのか調べるのは簡単です。
前回の例でいえば、ソースファイル「hello.cc」をコンパイルすればコンパイル結果のファイル「a.out」が生成されます。a.outが存在しないか、hello.ccの更新時刻がa.outの更新時刻より新しければコンパイルを行うようにします。具体的には次のようにします。
0 1 2 3 4 5 6 7 8 9 10 |
<?php $source_file = 'hello.cc'; $target_file = 'a.out'; if (!file_exists($target_file) || filemtime($source_file) > filemtime($target_file)) { $command = "g++ -o $target_file $source_file"; passthru($command); } |
前回使ったhello.ccはあえてコンパイルエラーが発生するようにしていたことに注意してください。coutとendlにstd::を付けてあげればコンパイルが通るようになるはずです。
ここまでが基本です。hello.ccではこれで問題ありませんが、もし(処理系や外部ライブラリーが提供するヘッダー以外の)ヘッダーファイルをインクルードしていた場合には、それらの更新時刻も調べてコンパイルするかどうかを判断しなければなりません。そのためにはソースファイルの依存関係を調べる必要があります。
GCCの場合、コンパイル時に-MMDオプションを指定してあげれば、Makefileに適した形式で依存関係を表すファイルが生成されます。ソースファイルがhello.ccであれば依存関係を表すファイルはhello.dのように、添字が.dになります。
依存関係を表すファイルが生成されることを確認するために、次のようなa.ccを作成してみましょう。
0 1 2 3 4 5 6 7 |
#include "b.hh" #include "c.hh" int main() { } |
インクルードしているb.hhとc.hhは空のファイルでかまいません。これを次のコマンドでコンパイルしてみます。
0 1 2 |
g++ -MMD -o a.out a.cc |
すると、次のようなa.dが生成されます。
0 1 2 |
a.out: a.cc b.hh c.hh |
見ての通り、コンパイル結果のファイルa.outは、a.cc,、b.hh、c.hhの3つファイルに依存していることがわかります。
依存関係を考慮してコンパイルする、しないを判定できるようにするには次のようにすればいいでしょう。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<?php $source_file = 'a.cc'; $target_file = 'a.out'; $dependency_file = str_replace('.cc', '.d', $source_file); // コンパイル要求フラグ $compile_request = !file_exists($target_file); if (!$compile_request) { // 依存するファイルの更新時刻を調べてコンパイル要求フラグを立てる。 $target_mtime = filemtime($target_file); if (file_exists($dependency_file)) { $dependency = array_slice(explode(' ', trim(file_get_contents($dependency_file))), 1); foreach ($dependency as $file) { if (filemtime($file) > $target_mtime) { $compile_request = true; break; } } } else { $compile_request = filemtime($source_file) > $target_mtime; } } // コンパイル要求フラグが立っている場合だけコンパイルする。 if ($compile_request) { $command = "g++ -MMD -o $target_file $source_file"; passthru($command); } |
今回のような方法はGCCやClangではうまくいきますが、Visual C++のように依存関係を表すファイルの形式が異なる処理系では使えません。Visual C++ 2019以降では依存関係を表すファイルはJSON形式で出力されます。これに対応する方法はまた別の機会に紹介したいと思います。