CMakeでコンパイラに対してinclude対象のディレクトリを設定するには、include_directories() だとか target_include_directories() がある。現在の推奨は後者らしいのだが、俺の場合わりと小規模なプロジェクト作ることが多いし、引数多いと面倒なので前者を使ってたりする。
# 以降のターゲット全てにincludeディレクトリを適用 include_directories(${INCLUDE_DIRS}) # fuga.exe には上記のinlucdeパスが適用される add_executable(fuga ${SOURCES})
で、どちらを使うにせよ、予め洗い出しておいたinclude対象パスの一覧をテキトーなリスト(上記で言うところのINCLUDE_DIRS)に入れて渡してやる感じになる。
しかし、色々なディレクトリに参照したいヘッダファイルが散乱してる場合、includeディレクトリを手動で洗い出すのが面倒だ。可能であれば、「ヘッダファイルが入ってるディレクトリ」を勝手に走査して、勝手にリストにぶちこんでほしい。でもそれを外部スクリプトで書いて環境変数にぶちこむのもなんか嫌だし、CMakeLists.txtの中だけで完結させたい。
という、需要があるのか無いのかよく分からない願望をもとにググってみたところ、なんとかそれらしきものを見つけ出せた。完全に拾い物なのだが、下記のようなマクロを使えば実現できるようだ。
# ヘッダが入ってるディレクトリを洗い出すマクロ MACRO(HEADER_DIRECTORIES return_list) FILE(GLOB_RECURSE new_list *.h) SET(dir_list "") FOREACH(file_path ${new_list}) GET_FILENAME_COMPONENT(dir_path ${file_path} PATH) SET(dir_list ${dir_list} ${dir_path}) ENDFOREACH() LIST(REMOVE_DUPLICATES dir_list) SET(${return_list} ${dir_list}) ENDMACRO() # マクロ呼び出し HEADER_DIRECTORIES(header_dir_list) # 件数カウント list(LENGTH header_dir_list header_dir_list_count) message(STATUS "Count: ${header_dir_list_count}") # リスト一覧出力 message(${header_dir_list}) # いつもの include_directories(${header_dir_list}) add_executable(fuga hoge.cpp)
しかし、なんかコピペだけして「動いた!閉廷!以上!皆解散!」で終わらせるのはなんとなく気持ち悪いので、お勉強がてらマクロ部分を分解して、何をやってるかちょっと見てみよう。
# マクロ開始 MACRO(HEADER_DIRECTORIES return_list) ... # マクロ終了 ENDMACRO() # マクロ呼び出し HEADER_DIRECTORIES(header_dir_list)
マクロの開始と終わり。プログラミング言語でいうところの { } みたいなもん。MACRO()には定義するマクロの名前に続けて、任意の引数を半角スペース区切りで渡すことができる。今回は、リスト「header_dir_list」にヘッダの一覧をぶちこみたいので、それをマクロ側で引数「return_list」として扱う。
FILE(GLOB_RECURSE new_list *.h)
作業フォルダ以下のヘッダファイル一覧を、new_listにぶちこむ。「GLOB_RECURSE」なので再帰的に潜って見てくれる。
SET(dir_list "")
変数dir_listを空で初期化しとく。
FOREACH(file_path ${new_list}) ... ENDFOREACH()
for~each的なやつ。new_listには、さっきFILEで洗い出したヘッダファイルの一覧が入っているので、そいつからfile_pathにひとつひとつ切り出して格納していく。
GET_FILENAME_COMPONENT(dir_path ${file_path} PATH)
GET_FILENAME_COMPONENT()を使えば、指定したファイル名を加工して変数に格納できるっぽい。でも最後のPATHってなんぞ?ってところで公式ドキュメント見てみる。
PATH = Legacy alias for DIRECTORY (use for CMake <= 2.8.11)
どうやら、「今はDIRECTORYだけど昔はPATHだったよ!」みたいな感じ。しかし俺が今使ってるバージョンは3.16だしなぁ。
DIRECTORY = Directory without file name
で、DIRECTORYの説明にはこう書いてあった。要は、ファイル名を抜いてディレクトリ名だけ持ってくるいうことやね。
SET(dir_list ${dir_list} ${dir_path})
その結果をdir_listに追加してると。なるほどなるほど。でも、これでやると同じ階層にヘッダファイルいっぱいあるわけじゃん?つまり、同じディレクトリ名が大量に「dir_list」に格納されない?そこんとこ大丈夫なの?
LIST(REMOVE_DUPLICATES dir_list) SET(${return_list} ${dir_list})
そこで出てくるのがこの「REMOVE_DUPLICATES」。公式ドキュメントの説明によると「Removes duplicated items in the list. 」とある。つまり、リストの中で重複してるやつはぶち殺しちゃうわけだな。天才かよ。結果として、マクロが終わる頃にはヘッダファイルが入っているフォルダの一覧が格納されたリストが完成する。
CMakeビギナーの俺にはまだマクロ自作できる気がしないが、先人が作った偉大な車輪を使えば大抵な事はなんとかなりそうな気がしてきたので、ありがたく使わせて頂こうぞ。