最もシンプルだが完全なCMakeの例


117

どういうわけか私はCMakeの動作に完全に混乱しています。CMakeがどのように記述されることを意図しているかを理解するようになるたびに、それは私が読んだ次の例では消えます。私が知りたいのは、自分のプロジェクトをどのように構成すればよいのかということです。これにより、私のCMakeが将来最小限の保守を必要とするだけです。たとえば、他のすべてのsrcフォルダーとまったく同じように機能する新しいフォルダーをsrcツリーに追加するときに、CMakeList.txtを更新したくありません。

これが私のプロジェクトの構造を想像する方法ですが、これはほんの一例です。推奨される方法が異なる場合は、教えてください。方法も教えてください。

myProject
    src/
        module1/
            module1.h
            module1.cpp
        module2/
            [...]
        main.cpp
    test/
        test1.cpp
    resources/
        file.png
    bin
        [execute cmake ..]

ちなみに、私のプログラムはリソースがどこにあるかを知っていることが重要です。推奨されるリソース管理方法を教えてください。「../resources/file.png」で自分のリソースにアクセスしたくない


1
For example I don't want to update my CMakeList.txt when I am adding a new folder in my src treeソースを自動的に収集するIDEの例を教えてください。

7
ideは通常、ソースを自動的に収集しません。必要がないためです。新しいファイルまたはフォルダーを追加すると、IDE内で追加され、プロジェクトが更新されます。反対側のビルドシステムは、一部のファイルを変更しても気付かないため、すべてのソースファイルを自動的に収集することが望ましい動作です
Arne

4
そのリンクを見ると、CMakeが解決したかった最も重要なタスクである、クロスプラットフォームビルドシステムの簡単化に失敗したように見えます。
Arne

回答:


94

いくつかの調査の後、私は最も単純だが完全なcmakeの例の自分のバージョンを手に入れました。ここにあり、リソースやパッケージングを含むほとんどの基本をカバーしようとします。

非標準で行うことの1つは、リソースの処理です。デフォルトでは、cmakeはそれらを/ usr / share /、/ usr / local / share /、およびWindowsの同等のものに配置しようとしています。どこでも抽出して実行できるシンプルなzip / tar.gzが欲しかった。したがって、リソースは実行可能ファイルに関連してロードされます。

cmakeコマンドを理解するための基本的な規則は、次の構文です:<function-name>(<arg1> [<arg2> ...])コンマまたはセミカラー なし。各引数は文字列です。foobar(3.0)foobar("3.0")同じです。リスト/変数はで設定できますset(args arg1 arg2)。この変数を設定するfoobar(${args})foobar(arg1 arg2)、実質的に同じです。存在しない変数は空のリストと同等です。リストは、内部的には要素を区切るセミコロンの付いた単なる文字列です。したがって、要素が1つだけのリストは、定義上その要素だけであり、ボックス化は行われません。変数はグローバルです。組み込み関数は、またはのようないくつかのIDを期待するという事実により、何らかの形式の名前付き引数を提供しますPUBLICDESTINATION引数リストで、引数をグループ化します。しかし、これは言語機能ではありません。これらのIDも単なる文字列であり、関数の実装によって解析されます。

あなたはgithubからすべてをクローンすることができます

cmake_minimum_required(VERSION 3.0)
project(example_project)

###############################################################################
## file globbing ##############################################################
###############################################################################

# these instructions search the directory tree when cmake is
# invoked and put all files that match the pattern in the variables 
# `sources` and `data`
file(GLOB_RECURSE sources      src/main/*.cpp src/main/*.h)
file(GLOB_RECURSE sources_test src/test/*.cpp)
file(GLOB_RECURSE data resources/*)
# you can use set(sources src/main.cpp) etc if you don't want to
# use globing to find files automatically

###############################################################################
## target definitions #########################################################
###############################################################################

# add the data to the target, so it becomes visible in some IDE
add_executable(example ${sources} ${data})

# just for example add some compiler flags
target_compile_options(example PUBLIC -std=c++1y -Wall -Wfloat-conversion)

# this lets me include files relative to the root src dir with a <> pair
target_include_directories(example PUBLIC src/main)

# this copies all resource files in the build directory
# we need this, because we want to work with paths relative to the executable
file(COPY ${data} DESTINATION resources)

###############################################################################
## dependencies ###############################################################
###############################################################################

# this defines the variables Boost_LIBRARIES that contain all library names
# that we need to link to
find_package(Boost 1.36.0 COMPONENTS filesystem system REQUIRED)

target_link_libraries(example PUBLIC
  ${Boost_LIBRARIES}
  # here you can add any library dependencies
)

###############################################################################
## testing ####################################################################
###############################################################################

# this is for our testing framework
# we don't add REQUIRED because it's just for testing
find_package(GTest)

if(GTEST_FOUND)
  add_executable(unit_tests ${sources_test} ${sources})

  # we add this define to prevent collision with the main
  # this might be better solved by not adding the source with the main to the
  # testing target
  target_compile_definitions(unit_tests PUBLIC UNIT_TESTS)

  # this allows us to use our executable as a link library
  # therefore we can inherit all compiler options and library dependencies
  set_target_properties(example PROPERTIES ENABLE_EXPORTS on)

  target_link_libraries(unit_tests PUBLIC
    ${GTEST_BOTH_LIBRARIES}
    example
  )

  target_include_directories(unit_tests PUBLIC
    ${GTEST_INCLUDE_DIRS} # doesn't do anything on Linux
  )
endif()

###############################################################################
## packaging ##################################################################
###############################################################################

# all install commands get the same destination. this allows us to use paths
# relative to the executable.
install(TARGETS example DESTINATION example_destination)
# this is basically a repeat of the file copy instruction that copies the
# resources in the build directory, but here we tell cmake that we want it
# in the package
install(DIRECTORY resources DESTINATION example_destination)

# now comes everything we need, to create a package
# there are a lot more variables you can set, and some
# you need to set for some package types, but we want to
# be minimal here
set(CPACK_PACKAGE_NAME "MyExample")
set(CPACK_PACKAGE_VERSION "1.0.0")

# we don't want to split our program up into several things
set(CPACK_MONOLITHIC_INSTALL 1)

# This must be last
include(CPack)

8
@SteveLorimer同意しない、そのファイルグロビングは悪いスタイルです。ファイルツリーをCMakeLists.txtに手動でコピーすることは、冗長であるため悪いスタイルだと思います。しかし、人々がこのトピックに同意しないことを知っているので、コードにコメントを残しました。ここで、グロビングをすべてのソースファイルを明示的に含むリストに置き換えることができます。を検索しset(sources src/main.cpp)ます。
Arne

3
@SteveLorimerはい、よくcmakeをもう一度呼び出さなければなりませんでした。ディレクトリツリーに何かを追加するたびに、手動でcmakeを再起動して、グロビングが再評価されるようにする必要があります。ファイルをに配置するとCMakeLists.txt、通常のmake(または忍者)がcmakeの再起動をトリガーするため、忘れることはできません。また、チームメンバーもcmakeを実行することを忘れないため、少しチームフレンドリになります。しかし、誰かがファイルを追加したからといって、makefileを変更する必要はないと思います。一度書くと、誰も二度とそれについて考える必要はありません。
Arne

3
@SteveLorimerまた、プロジェクトのすべてのディレクトリに1つのCMakeLists.txtを配置するパターンにも同意しません。プロジェクトのあらゆる場所に構成を分散させるだけです。それを行うには1つのファイルで十分です。それ以外の場合は、概要を失います。ビルドプロセスで実際に行われます。これは、独自のCMakeLists.txtを含むサブディレクトリが存在できないことを意味するのではなく、例外と考えるべきです。
Arne

2
「VCS」「バージョン管理システム」の略であると仮定すると、それは無関係です。問題は、アーティファクトがソース管理に追加されないことではありません。問題は、CMakeが追加されたソースファイルの再評価に失敗することです。ビルドシステムの入力ファイルは再生成されません。ビルドシステムは古くなった入力ファイルに問題なく従い、エラーが発生するか(運が良ければ)、運が悪ければ気付かれないままになります。GLOBbingは、依存関係計算チェーンにギャップを生じます。これ重要な問題であり、コメントではこれを適切に認めていません。
IInspectable 2017

2
CMakeとVCSは完全に分離して動作します。VCSはCMakeを認識せず、CMakeはどのVCSも認識しません。それらの間にリンクはありません。開発者が手動の手順を実行し、VCSから情報を取り出し、何らかのヒューリスティックなクリーンに基づいてCMakeを再実行するように提案しない限り。これは明らかにスケーリングされず、人間に固有の誤謬の影響を受けやすくなります。いいえ、申し訳ありませんが、これまでのところ、GLOBbingファイルについて説得力のあるポイントを作成していません。
IInspectable 2017

39

最も基本的だが完全な例は、CMakeチュートリアルにあります。

cmake_minimum_required (VERSION 2.6)
project (Tutorial)
add_executable(Tutorial tutorial.cxx)

あなたのプロジェクトの例では、次のようになります:

cmake_minimum_required (VERSION 2.6)
project (MyProject)
add_executable(myexec src/module1/module1.cpp src/module2/module2.cpp src/main.cpp)
add_executable(mytest test1.cpp)

追加の質問については、チュートリアルにもう一度行く方法があります。コードに含める構成可能なヘッダーファイルを作成します。これを行うにはconfiguration.h.in、次の内容のファイルを作成します。

#define RESOURCES_PATH "@RESOURCES_PATH@"

次に、CMakeLists.txt追加で:

set(RESOURCES_PATH "${PROJECT_SOURCE_DIR}/resources/"
# configure a header file to pass some of the CMake settings
# to the source code
configure_file (
  "${PROJECT_SOURCE_DIR}/configuration.h.in"
  "${PROJECT_BINARY_DIR}/configuration.h"
)

# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
include_directories("${PROJECT_BINARY_DIR}")

最後に、コードでパスが必要な場合は、次のことができます。

#include "configuration.h"

...

string resourcePath = string(RESOURCE_PATH) + "file.png";

特にRESOURCE_PATHに感謝します。configure_fileが探しているものであるとはどういうわけかわかりませんでした。しかし、プロジェクトからすべてのファイルを手動で追加しました。srcツリーからすべてのファイルが追加されるパターンを単純に定義するより良い方法はありますか?
Arne

ディーターの答えだけでなく、なぜそれを使うべきではないかについての私のコメントも見てください。本当に自動化したい場合は、ソースファイルのリストを再生成するために実行できるスクリプトを作成することをお勧めします(または、これを行うcmake対応のIDEを使用します。詳しくは知りません)。
sgvd 2014年

3
@sgvd IMHO ソースディレクトリへの絶対パスをstring resourcePath = string(RESOURCE_PATH) + "file.png"ハードコーディングするのは悪い考えです。プロジェクトをインストールする必要がある場合はどうなりますか?

2
ソースを自動的に収集することはいいことだと思いますが、それはあらゆる種類の複雑化につながる可能性があります。簡単な説明については、しばらく前にこの質問をご覧ください:stackoverflow.com/q/10914607/1401351
Peter

2
cmakeを実行しないと、まったく同じエラーが発生します。手作業でファイルを追加すると、1秒に1秒かかります。コンパイルごとにcmakeを実行すると、毎回1秒かかります。あなたは実際にcmakeの機能を壊します。同じプロジェクトで作業し、変更を取り込む誰かが実行します:runを実行->未定義の参照を取得->うまくいけば、cmakeを再実行するか、バグをファイルして-> cmakeを実行-> run makeを正常に実行しますが、ファイルを追加した場合手で彼はします:実行は成功しました->家族と時間を過ごします まとめると、怠惰にならないでください。将来、自分や他の人の頭痛を惜しまないでください。
sgvd 2014年

2

ここでは、最も単純ですが完全なCMakeLists.txtファイルのサンプルを作成します。

ソースコード

  1. Hello WorldからクロスプラットフォームのAndroid / iOS / Web / Desktopまでのチュートリアル。
  2. 各プラットフォームでサンプルアプリケーションをリリースしました。
  3. 08-cross_platformファイル構造体は私の仕事によって検証されています
  4. 完璧ではないかもしれませんが、自分のチームにとっては便利でベストな方法です

その後、詳細について資料を提供しました。

ご不明な点がございましたら、お気軽にご連絡ください。説明させていただきます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.