SCons入門(2)

SCons入門(1) - Pashango’s Blog

ファイル更新チェックの方法を変更する「Decider」

SConsでは、ファイルの更新チェックに標準でMD5を使用する。
そのため、タイムスタンプが異なっていても、ファイル内容が同じであれば不要なビルドを避けてくれる。


MD5で更新チェックは非常に便利なのだが、映像ファイルなどのファイルサイズが大きいものには不向きである。
そこでSConsでは、ファイルの更新チェック方法を「Decider」で設定することができる。

以下、Deciderの設定例。

#MD5で更新チェック(標準)
Decider('MD5')

#タイムスタンプが新しければ更新
Decider('timestamp-newer')
Decider('make')              #timestamp-newerの別名

#タイムスタンプが異なっていれば更新
Decider('timestamp-match')

#MD5が変更、または、タイムスタンプが新しければ更新
Decider('MD5-timestamp')

もちろん、カスタムの更新チェック関数を設定することも可能。

Program('hello.c')
def decide_if_changed(dependency, target, prev_ni):
    if self.get_timestamp() != prev_ni.timestamp:
        dep = str(dependency)
        tgt = str(target)
        if specific_part_of_file_has_changed(dep, tgt):
            return True
   return False
Decider(decide_if_changed)

ここで気をつけなければいけないのは、最後に記述された「Decider」が有効になるということ。
以下のようにa.txtは「make」で更新チェック、b.txtは「MD5」で更新チェックしようとしても、後に書かれたDeciderが有効になってしまい2つのファイルともMD5で更新チェックされてしまう。

env = Environment(ENV = os.environ)

env.Decider('make')
env.Command("a.out", "a.txt", "python my_convert.py $SOURCE $TARGET")

env.Decider('MD5')
env.Command("b.out", "b.txt", "python my_convert.py $SOURCE $TARGET")
#a.txtもb.txtもMD5で更新チェックされてしまう!

複数のDeciderを有効にしたい場合は、Environmentを分ける必要がある。

env_make = Environment(ENV = os.environ)
env_md5  = env_make.Clone()

env_make.Decider('make')
env_md5.Decider('MD5')

env_make.Command("a.out", "a.txt", "python my_convert.py $SOURCE $TARGET")
env_md5.Command("b.out", "b.txt", "python my_convert.py $SOURCE $TARGET")

出力ファイルをインストールする「Install,InstallAs」

出力ファイルを、指定ディレクトリにコピーするには「Install」または「InstallAs」を使う。
すでにコピー先にファイルがある場合、タイムスタンプなどを考慮して必要があれば上書きする。

#出力ファイルを/usr/binにインストール(コピー)する
output = Program('cmd.c')
Install('/usr/bin', output)

#複数ファイルのインストールも可能
Install('/usr/bin', ['cmd_a', 'cmd_b'])

#ファイル名を変えながらインストールする場合はInstallAsを使う
InstallAs('/usr/bin/new_cmd', 'cmd')

標準のビルドターゲットを設定する「Default」

SConsでビルドターゲットを省略した場合、標準のビルドターゲットがビルドされる。
「Default」で標準のビルドターゲットを設定する事が出来る。
以下、Sconstructの記述例、「hello.c」と「goodbye.c」の2つのタスクがあるが、「hello.c」を標準のビルドターゲットにする。

hello = Program('hello.c')
Program('goodbye.c')
Default(hello)

コマンドラインから「scons」と打つと、標準のビルドターゲット「hello.c」だけがビルドされる。
「goodbye.c」は明示的にビルドターゲットを指定しない限り、ビルドされなくなる。

> scons -Q
cc -c -o hello.o hello.c
cc -o hello hello.o
> scons -Q
scons: `hello' is up to date.
> scons -Q goodbye
cc -c -o goodbye.o goodbye.c
cc -o goodbye goodbye.o

クリーンされるファイルの制御「Clean,NoClean」

コマンドラインで「scons -c」と打つと、ビルドファイルがクリーンアップされる。
この時、SConsが削除すべきファイルを自動的に調べてくれるのだが、「Clean」で削除するファイル/ディレクトリを追加できる。

Cleanの第1引数は、削除対象ファイルと関連があるビルドターゲット。
第2引数は、追加する削除対象ファイルとなる。

以下の例では、ビルド時に出るlogファイルをクリーン時に削除する例。

a = Program('a.c')
b = Program('b.c')

Clean(a, 'a.log')
Clean(b, 'b.log')

コマンドラインから「scons -c」を打つと、logファイルも削除される。

> scons -c
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Cleaning targets ...
Removed a
Removed a.log
Removed b
Removed b.log
scons: done cleaning targets.

#ターゲット指定も可能(この場合はbとb.logは削除されない)
> scons -c a
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Cleaning targets ...
Removed a
Removed a.log
scons: done cleaning targets.


Clearの第1引数に'all'を指定すれば、ビルドターゲット省略時にクリーンされるようになる。
また「Glob」を使えば、ワイルドカードでファイルを指定できる。

#scons -c の時、workディレクトリも削除する
Clean('all', 'work')

#logファイルをすべて削除してければGlobを使う
Clean('all', Glob('*.log'))


逆にクリーンアップ時に削除して欲しくないファイルなどは「NoClean」で指定する。
例えば「Install」したファイルも標準では削除対象になってしまうので、「NoClean」を使う。

a = Program('a.c')
a_inst = Install('/usr/bin', a)

NoClean(a_inst)


SCons入門(3) - Pashango’s Blog