Posted at
CMakeDay 6

CMake: 条件分岐

More than 3 years have passed since last update.


はじめに

みなさん、こんにちは。今回は条件分岐について書いていきます。


構文

条件分岐にはif()コマンドを使います。このif()コマンドは、elseif()else()endif()コマンドとセットで使用します。構文は下記のとおりです。


if(expression)

# then section.
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
elseif(expression2)
# elseif section.
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
else(expression)
# else section.
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
endif(expression)

if — CMake 3.0.2 Documentation


elseif()コマンドとelse()コマンドは省略可能です。また、else()コマンドとendif()コマンドの引数は、おそらくラベル以上の意味を持たないので記述する必要はありません。


式の種類

さて、if()コマンドおよびelseif()コマンドの引数には、そのブロックが実行される条件式を記述します。この条件式には以下に示す種類があります。


ブール定数

CMakeの値はすべて文字列型ですが、この条件式の文脈では以下の値がBoolean値として認識されます。また、これらの値は大文字・小文字の区別がされません。


真と解釈される値



  • 0でない数値

  • ON

  • YES

  • TRUE

  • Y

if(1)

if(TRUE)
if(on)
if(Yes)


偽と解釈される値


  • 空文字列

  • 0

  • OFF

  • NO

  • FALSE

  • N

  • IGNORE

  • NOTFOUND

  • 末尾が-NOTFOUNDである文字列

if(0)

if(FALSE)
if(off)
if(No)
if(HogePackage-NotFound)


ブール演算子

論理積を表すAND、論理和を表すOR、否定を表すNOTが使えます。また、これらはすべて大文字でなければなりません。

if(true AND true)

if(true OR false)
if(NOT false)


括弧()内の式

複雑な条件式を構築するために使います。

括弧()でくくられた式は一番最初に評価されます。

if(true AND (false OR true))


テスト

変数が定義済みかどうかのチェックや数値の比較などを行います。

if(DEFINED variable)

if(1 LESS 2)


式の優先順位

上で示した各種式の優先順位は以下のとおりとなっています。


  1. 括弧()で囲まれた式


  2. 単項テスト

    EXISTSDEFINEDTARGETなど




  3. 二項テスト

    EQUALLESSGREATERなど



  4. NOTブール演算子


  5. ANDブール演算子


  6. ORブール演算子



テストの種類

テストには以下に示す種類があります。


対象が存在するか

対象が存在するかどうかを判定する単項テストです。


  • DEFINED <variable-name>

  • COMMAND <command-name>

  • POLICY <policy-id>

  • TARGET <target-name>

  • EXISTS <path>

if(DEFINED value)    # value という変数がセットされているかどうか

if(COMMAND message) # message というコマンドが存在するかどうか
if(POLICY CMP0011) # CMP0011 というポリシーが存在するかどうか
if(TARGET test) # test というターゲットが定義されているかどうか
if(EXISTS src/a.cpp) # src/a.cpp というパスが存在しているかどうか


対象がある性質を満たしているか

対象がある性質を満たしているかどうかを判定する単項テストです。


  • IS_DIRECTORY <path>

  • IS_SYMLINK <path>

  • IS_ABSOLUTE <path>

if(IS_DIRECTORY src/)  # src/ がディレクトリかどうか

if(IS_SYMLINK /bin/sh) # /bin/sh がシンボリックリンクかどうか
if(IS_ABSOLUTE /) # / が絶対パスかどうか


文字列がパターンに一致するか

<string> MATCHES <regexp>

文字列<string>が正規表現<regexp>にマッチするかどうかを判定する二項テストです。文字列<string>がない場合は常にfalseを返します。

if("aaaab" MATCHES "^a+b$") # "aaaab" =~ /^a+b$/


数値を比較

数値の比較を行う二項テストです。


  • <lvalue> EQUAL <rvalue>

  • <lvalue> LESS <rvalue>

  • <lvalue> GREATER <rvalue>

if(1 EQUAL 1)   # 1 == 1

if(1 LESS 2) # 1 < 2
if(2 GREATER 1) # 2 > 1


文字列を比較

文字列の比較を行う二項テストです。辞書順に基づきます。


  • <lvalue> STREQUAL <rvalue>

  • <lvalue> STRLESS <rvalue>

  • <lvalue> STRGREATER <rvalue>

if("a" STREQUAL "a")   # "a" == "a"

if("a" STRLESS "aa") # "a" < "aa"
if("b" STRGREATER "a") # "b" > "a"


バージョンを比較

バージョンの比較を行う二項テストです。バージョンとはmajor[.minor[.patch[.tweak]]]というフォーマットになっているものです。数値的な順序や辞書順には基づきません。例えば、0.1という値と0.1.0.0という値は等しいと評価されます。また、1.191.2より大きいと評価されます。


  • <lvalue> VERSION_EQUAL <rvalue>

  • <lvalue> VERSION_LESS <rvalue>

  • <lvalue> VERSION_GREATER <rvalue>

if(0.1 VERSION_EQUAL 0.1.0.0)  # 0.1 == 0.1.0.0

if(0.1 VERSION_LESS 0.1.1) # 0.1 < 0.1.1
if(1.19 VERSION_GREATER 1.2) # 1.19 > 1.2


ファイルのタイムスタンプを比較

<file1> IS_NEWER_THAN <file2>

ファイル<file1>とファイル<file2>のタイムスタンプの比較を行う二項テストです。<file1><file2>より新しい場合にtrueを返します。また、どちらかのファイルが存在しない場合にもtrueを返します。

if(build/my_exe IS_NEWER_THAN /usr/local/bin/my_exe)


変数名と解釈される値

条件式に記述する値は、歴史的経緯により、次の文脈で変数名であると解釈されることがあります。


  • 単一の値

  • ブール演算子の左辺値および右辺値


  • MATCHESテストの左辺値


  • IS_NEWER_THAN以外の比較テストの左辺値および右辺値

この文脈において、ブール定数に解釈されない値で、かつ名前がその値である変数が存在するとき、それは変数名であると解釈され、変数の値で評価されます。

set(value 1) #[[

true や false といった名前にすると変数名ではなく、
ブール定数と解釈されてしまうので使用してはいけない。
]]

if(value) # if(1)
if(NOT value) # if(NOT 1)
if(value OR false) # if(1 OR false)
if(value MATCHES "[0-9]") # if(1 MATCHES "[0-9]")
if(value EQUAL 1) # if(1 EQUAL 1)
if(2 GREATER value) # if(2 GREATER 1)


おわりに

以上、条件分岐でした。if()コマンドは簡単かと思いきや、歴史的経緯の仕様などが合わさり複雑になっています。そのため、使用には注意が必要です。

明日は、mrk_21 さんの『CMake: ループ』です。