こんにちは。田原です。

CMakeをスクリプト・エンジンとして使う場合、stringコマンドは中心的な役割を担います。std::stringが持つのと同様な基本的機能に加えて、正規表現での検索や置換などstringコマンドは意外に強力です。それらのよく使う機能について解説します。

1.まずは基本

stringコマンドの基本的な機能は、std::string と意外に似ています。まずはそれらについての解説です。

1-1. 追記

文字列の結合としてstring(APPEND)があります。setコマンドでも同じことができます。setコマンドに対して特に利点があるというわけでもなさそうです。
std::stringのappend()と同様な機能です。

コマンドのフォーマットは以下の通りです。(入力文字列は複数指定でき、それらを全て結合します。)

string(APPEND 出力先変数 ”入力文字列” …)

set(STR "abc def")
message(STATUS "STR=[${STR}]")

string(APPEND STR " ghi")
message(STATUS "STR=[${STR}]")

set(STR "${STR} jkl")
message(STATUS "STR=[${STR}]")
実行結果
>cmake -P sample.cmake
-- STR=[abc def]
-- STR=[abc def ghi]
-- STR=[abc def ghi jkl]

1-2. 部分取り出し

文字列の一部を取り出すにはstring(SUBSTRING)を使います。
std::stringのsubstr()と同様な機能です。

コマンドのフォーマットは以下の通りです。(文字列先頭は「開始位置=0」です。また、範囲外を指定するとエラーになります。)

string(SUBSTRING ”文字列” 開始位置 文字数 出力先変数)

set(STR "abc def ghi")
string(SUBSTRING "${STR}" 8 3 STR)
message(STATUS "STR=[${STR}]")

string(SUBSTRING "${STR}" 8 4 STR)
実行結果
>cmake -P sample.cmake
-- STR=[ghi]
CMake Error at sample.cmake:5 (string):
  string begin index: 8 is out of range 0 - 3

1-3. 検索

特定の文字列を見つけるにはstring(FIND)を使います。
std::stringのfind()と同様な機能です。

コマンドのフォーマットは以下の通りです。

string(FIND ”文字列” ”探す文字列” 出力先変数 [REVERSE])

set(STR "abcd$abcd")
string(FIND "${STR}" "bc" OUTPUT)
message(STATUS "OUTPUT=[${OUTPUT}]")

string(FIND "${STR}" "bc" OUTPUT REVERSE)
message(STATUS "OUTPUT=[${OUTPUT}]")
実行結果
>cmake -P sample.cmake
-- OUTPUT=[1]
-- OUTPUT=[6]

1-4. 置換

文字列の一部を置き換えるにはstring(REPLACE)を使います。
std::stringのreplace()と似た機能ですが少し異なります。std::stringのreplace()は指定した文字列を直接書き換えますが、string(REPLACE)は入力文字列と出力文字列をそれぞれ指定します。そして、複数の入力文字列を指定できます。

コマンドのフォーマットは以下の通りです。(入力文字列は複数指定でき、それらを結合して処理します。)

string(REPLACE ”探す文字列” ”置換先文字列” 出力先変数 ”入力文字列” …)

set(STR "Hallo, world!!")
string(REPLACE "Hallo" "Hello" OUTPUT "${STR}" " Hallo, Hallo")
message(STATUS "OUTPUT=[${OUTPUT}]")
実行結果
>cmake -P sample.cmake
-- OUTPUT=[Hello, world!! Hello, Hello]

2.そして応用

CMakeのstringコマンドには、std::stringにはない応用的な機能があります。その中で比較的使いそうなものを解説します。

2-1. 正規表現による検索と置換

CMakeは簡易版ですが、正規表現を使えます。

正規表現の説明を始めると長くなりますので、ここでは省略します。以下のリンクを参考にして下さい。

公式の正規表現のマニュアルはここです。
CMakeの正規表現について「CMake: 正規表現」に概要の解説があります。
一般の正規表現の入門解説が「サルにもわかる正規表現入門」にあります。(なお、CMakeの正規表現は簡易版ですが、概ねここに解説されているもので良さそうです。)

2-1-1. string(REGEX MATCH)

指定の文字列から最初にマッチした文字列を返却します。

コマンドのフォーマットは以下の通りです。(入力文字列は複数指定でき、それらを結合して処理します。)

string(REGEX MATCH ”正規表現” 出力先変数 ”入力文字列” …)

2-1-2. string(REGEX MATCHALL)

指定の文字列からマッチする全ての文字列をリストにして返却します。

コマンドのフォーマットは以下の通りです。(入力文字列は複数指定でき、それらを結合して処理します。)

string(REGEX MATCHALL ”正規表現” 出力先変数 ”入力文字列” …)

2-1-3. string(REGEX REPLACE)

指定の文字列からマッチする全ての文字列を置き換えます。置き換える時、正規表現で()で括った部分文字列を先頭から\\1, \\2, …にて取り出せます。

コマンドのフォーマットは以下の通りです。(入力文字列は複数指定でき、それらを結合して処理します。)

string(REGEX REPLACE ”正規表現” ”置換先文字列” 出力先変数 ”入力文字列” …)

2-1-4. サンプル

yyyy/mm/dd形式の日付を検索したり、yyyy-mm-dd形式へ置換したりするサンプルです。

set(STR "2019/10/10" ", " "2019/11/11")
message(STATUS "STR=[${STR}]")
string(REGEX MATCH "[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]" OUTPUT ${STR})
message(STATUS "OUTPUT=[${OUTPUT}]")

string(REGEX MATCHALL "[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]" OUTPUT ${STR})
message(STATUS "OUTPUT=[${OUTPUT}]")

string(REGEX REPLACE  "([0-9][0-9][0-9][0-9])/([0-9][0-9])/([0-9][0-9])" "\\1-\\2-\\3" OUTPUT ${STR})
message(STATUS "OUTPUT=[${OUTPUT}]")
実行結果
>cmake -P sample.cmake
-- STR=[2019/10/10;, ;2019/11/11]
-- OUTPUT=[2019/10/10]
-- OUTPUT=[2019/10/10;2019/11/11]
-- OUTPUT=[2019-10-10, 2019-11-11]

2-2. configure_fileの文字列版

CMakeはconfigure_fileコマンドは、指定ファイルの内容の ${変数名} や @変数名@ をそれぞれの変数の内容で置き換えました。
string(CONFIGURE)は指定した文字列内の${変数名} や @変数名@ をそれぞれの変数の内容で置き換えます。

コマンドのフォーマットは以下の通りです。(@ONLYESCAPE_QUOTESオプションについては、当ブログのconfigre_fileで解説していますのでそちらを参考にして下さい。)

string(CONFIGURE ”入力文字列” 出力先変数 [@ONLY] [ESCAPE_QUOTES])

set(STR "This is a @SOMETHING@.")
set(SOMETHING "pen")
string(CONFIGURE "${STR}" OUTPUT)
message(STATUS "OUTPUT=[${OUTPUT}]")

set(SOMETHING "pineapple")
string(CONFIGURE "${STR}" OUTPUT)
message(STATUS "OUTPUT=[${OUTPUT}]")
実行結果
>cmake -P sample.cmake
-- OUTPUT=[This is a pen.]
-- OUTPUT=[This is a pineapple.]

2-3. 現在時刻(Time Stamp)

string(TIMESTAMP)コマンドで現在の日時を(指定のフォーマットで)取り出せます。

コマンドのフォーマットは以下の通りです。(UTCを指定すると世界標準時(UTC)で返ってきます。指定しないとローカル時間です。)

string(TIMESTAMP 出力先変数 [“書式文字列”] [UTC])

書式文字列については簡単ですので上記公式の説明を参照下さい。

string(TIMESTAMP OUTPUT)
message(STATUS "OUTPUT=[${OUTPUT}]")

string(TIMESTAMP OUTPUT "%Y/%m/%d %H:%M%S")
message(STATUS "OUTPUT=[${OUTPUT}]")

string(TIMESTAMP OUTPUT "%Y/%m/%d %H:%M%S" UTC)
message(STATUS "OUTPUT=[${OUTPUT}]")
実行結果
>cmake -P sample.cmake
-- OUTPUT=[2019-11-03T00:42:34]
-- OUTPUT=[2019/11/03 00:4234]
-- OUTPUT=[2019/11/02 15:4234]

3.まとめ

stringコマンドは、基本的には普通の文字列操作コマンドですが応用的な機能もあります。今回は省略しましたがMD5やSHA-1ハッシュ計算やUUIDを計算する機能もあります。
特にCMakeの正規表現は stringコマンド以外にもifコマンド等、様々なコマンドで使え、応用する機会もそこそこありそうです。
スクリプト・モードでは結構使う機会の多いコマンドですが、Makefile生成モードでも時には使いますので、是非参考にされて下さい。

それでは今回はこれで終わります。お疲れさまでした!