CMake:外部プロジェクトを構築してそのターゲットを含める方法


113

静的ライブラリをターゲットとしてエクスポートするプロジェクトAがあります。

install(TARGETS alib DESTINATION lib EXPORT project_a-targets)
install(EXPORT project_a-targets DESTINATION lib/alib)

ここで、プロジェクトAをプロジェクトBの外部プロジェクトとして使用し、ビルドされたターゲットを含めます。

ExternalProject_Add(project_a
  URL ...project_a.tar.gz
  PREFIX ${CMAKE_CURRENT_BINARY_DIR}/project_a
  CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
)

include(${CMAKE_CURRENT_BINARY_DIR}/lib/project_a/project_a-targets.cmake)

問題は、プロジェクトBのCMakeListsを実行したときに、インクルードファイルがまだ存在しないことです。

ビルドを外部プロジェクトに依存させる方法はありますか?

更新:この問題と、私が遭遇したその他の一般的な問題に基づいて、 CMake by Exampleの短いチュートリアルを書きました。

回答:


67

ここでは2つの異なるパラダイムを混同していると思います。

お気づきのとおり、非常に柔軟なExternalProjectモジュールはビルド時にコマンドを実行します。プロジェクトAがインストールされて初めて作成されるため、プロジェクトAのインポートファイルを直接使用することはできません。

あなたがしたい場合はinclude、プロジェクトAのインポートファイル、あなたはよ持っている任意の他のサードパーティの依存関係がこのように追加または経由して同じように-手動でプロジェクトBのCMakeLists.txtを呼び出す前に、プロジェクトAをインストールしますfind_file/ find_library/ find_package

を使用する場合はExternalProject_Add、CMakeLists.txtに次のようなものを追加する必要があります。

ExternalProject_Add(project_a
  URL ...project_a.tar.gz
  PREFIX ${CMAKE_CURRENT_BINARY_DIR}/project_a
  CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
)

include(${CMAKE_CURRENT_BINARY_DIR}/lib/project_a/project_a-targets.cmake)

ExternalProject_Get_Property(project_a install_dir)
include_directories(${install_dir}/include)

add_dependencies(project_b_exe project_a)
target_link_libraries(project_b_exe ${install_dir}/lib/alib.lib)

2
ご回答有難うございます。あなたが提案することは、私が以前持っていたものに似ています。libパスを手動で指定するよりも優れたインターフェースのように見えるため、エクスポートされたターゲットを利用する方法を見つけたいと思っていました...
mirkokiefer

7
ソースツリーに外部プロジェクトのソースを含める必要を避けたいと思いました。すべてのターゲットのExternalProject_Addように振る舞いadd_subdirectory、すべてのターゲットを公開することは素晴らしいことです。上で説明したソリューションは、おそらく最もクリーンです。
mirkokiefer 2013

2
両方のExternalProjectビルドを作成し、BをAに依存させることを検討してください。プロジェクトBのCMakeListsファイルには、プロジェクトAのターゲットファイルが含まれますが、「スーパービルド」CMakeListsは、AとBの両方をExternalProjectsとしてビルドします。 ...
DLRdave 2013年

3
@DLRdave-Super Buildソリューションが何度か推奨されているのを見てきましたが、を介して一部の外部プロジェクトを含めるだけの場合に比べて、どのような利点があるのか​​わかりませんExternalProject。それは一貫性ですか、それとももっと標準的なものですか?私はここで基本的な何かを見逃していると確信しています。
フレーザー

6
このソリューションの問題の1つは、ライブラリ名(alib.lib)をハードコーディングしたことです。これにより、ビルドシステムがクロスプラットフォームではなくなります。これは、OSによって共有ライブラリに異なる命名方式が使用され、これらの異なる命名に適応するためです。スキームはCMakeの機能の1つです。
nsg

22

この投稿には適切な答えがあります:

CMakeLists.txt.in

cmake_minimum_required(VERSION 2.8.2)

project(googletest-download NONE)

include(ExternalProject)
ExternalProject_Add(googletest
  GIT_REPOSITORY    https://github.com/google/googletest.git
  GIT_TAG           master
  SOURCE_DIR        "${CMAKE_BINARY_DIR}/googletest-src"
  BINARY_DIR        "${CMAKE_BINARY_DIR}/googletest-build"
  CONFIGURE_COMMAND ""
  BUILD_COMMAND     ""
  INSTALL_COMMAND   ""
  TEST_COMMAND      ""
)

CMakeLists.txt

# Download and unpack googletest at configure time
configure_file(CMakeLists.txt.in
               googletest-download/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
  WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )
execute_process(COMMAND ${CMAKE_COMMAND} --build .
  WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )

# Prevent GoogleTest from overriding our compiler/linker options
# when building with Visual Studio
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)

# Add googletest directly to our build. This adds
# the following targets: gtest, gtest_main, gmock
# and gmock_main
add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src
                 ${CMAKE_BINARY_DIR}/googletest-build)

# The gtest/gmock targets carry header search path
# dependencies automatically when using CMake 2.8.11 or
# later. Otherwise we have to add them here ourselves.
if (CMAKE_VERSION VERSION_LESS 2.8.11)
  include_directories("${gtest_SOURCE_DIR}/include"
                      "${gmock_SOURCE_DIR}/include")
endif()

# Now simply link your own targets against gtest, gmock,
# etc. as appropriate

しかし、それはかなりハックに見えます。代替ソリューションを提案したいのですが、Gitサブモジュールを使用します。

cd MyProject/dependencies/gtest
git submodule add https://github.com/google/googletest.git
cd googletest
git checkout release-1.8.0
cd ../../..
git add *
git commit -m "Add googletest"

次に、MyProject/dependencies/gtest/CMakeList.txtあなたは次のようなことをすることができます:

cmake_minimum_required(VERSION 3.3)

if(TARGET gtest) # To avoid diamond dependencies; may not be necessary depending on you project.
    return()
endif()

add_subdirectory("googletest")

私はまだこれを広範囲に試していませんが、それはよりきれいに見えます。

編集:このアプローチには欠点がありinstall()ます。サブディレクトリは、不要なコマンドを実行する可能性があります。この投稿はそれらを無効にするアプローチを持っていますが、それはバグがあり、私にとってはうまくいきませんでした。

編集2:使用add_subdirectory("googletest" EXCLUDE_FROM_ALL)する場合install()、サブディレクトリのコマンドはデフォルトでは使用されないようです。


これは単なる例であり、gtestはおそらく非常に安定しているため、私は過度に慎重になっていると思いGIT_TAGますが、クローン中には常に特定のものを使用することを強くお勧めします。2年後にビルドスクリプトを実行している誰かがあなたがしたものとは異なるバージョン。CMakeのドキュメントもこれを推奨しています。
jrh

5

編集:CMakeには、これに対する組み込みサポートがあります。新しい答えを参照してください。

従属ターゲットのビルドを二次的なmakeプロセスで強制することもできます

関連トピックに関する私の回答を参照しください。


1

cmakeはExternalProject_Add確かに使用できますが、私がそれについて気に入らなかったのは、ビルドや継続的なポーリングなどの間に何かを実行することです...ビルドフェーズでプロジェクトをビルドしたいのですが、それ以外は何もしません。私はExternalProject_Addいくつかの試みでオーバーライドを試みましたが、残念ながら成功しませんでした。

次に、gitサブモジュールも追加しようとしましたが、それによってgitリポジトリ全体がドラッグされますが、場合によってはgitリポジトリ全体のサブセットしか必要ありません。私がチェックしたこと-まばらなgitチェックアウトを実行することは確かに可能ですが、それは私が以下に書いた別の機能を必要とします。

#-----------------------------------------------------------------------------
#
# Performs sparse (partial) git checkout
#
#   into ${checkoutDir} from ${url} of ${branch}
#
# List of folders and files to pull can be specified after that.
#-----------------------------------------------------------------------------
function (SparseGitCheckout checkoutDir url branch)
    if(EXISTS ${checkoutDir})
        return()
    endif()

    message("-------------------------------------------------------------------")
    message("sparse git checkout to ${checkoutDir}...")
    message("-------------------------------------------------------------------")

    file(MAKE_DIRECTORY ${checkoutDir})

    set(cmds "git init")
    set(cmds ${cmds} "git remote add -f origin --no-tags -t master ${url}")
    set(cmds ${cmds} "git config core.sparseCheckout true")

    # This command is executed via file WRITE
    # echo <file or folder> >> .git/info/sparse-checkout")

    set(cmds ${cmds} "git pull --depth=1 origin ${branch}")

    # message("In directory: ${checkoutDir}")

    foreach( cmd ${cmds})
        message("- ${cmd}")
        string(REPLACE " " ";" cmdList ${cmd})

        #message("Outfile: ${outFile}")
        #message("Final command: ${cmdList}")

        if(pull IN_LIST cmdList)
            string (REPLACE ";" "\n" FILES "${ARGN}")
            file(WRITE ${checkoutDir}/.git/info/sparse-checkout ${FILES} )
        endif()

        execute_process(
            COMMAND ${cmdList}
            WORKING_DIRECTORY ${checkoutDir}
            RESULT_VARIABLE ret
        )

        if(NOT ret EQUAL "0")
            message("error: previous command failed, see explanation above")
            file(REMOVE_RECURSE ${checkoutDir})
            break()
        endif()
    endforeach()

endfunction()


SparseGitCheckout(${CMAKE_BINARY_DIR}/catch_197 https://github.com/catchorg/Catch2.git v1.9.7 single_include)
SparseGitCheckout(${CMAKE_BINARY_DIR}/catch_master https://github.com/catchorg/Catch2.git master single_include)

関数の使用方法を説明するために、以下に2つの関数呼び出しを追加しました。

マスター/トランクが壊れている可能性があるため、誰かがマスター/トランクをチェックアウトしたくない場合があります。特定のタグを指定することは常に可能です。

キャッシュフォルダーをクリアするまで、チェックアウトは一度だけ実行されます。


1

私は同様の解決策を探していました。ここでの返信と上部のチュートリアルは参考情報です。私が成功した鉱山を構築するためにここで参照された投稿/ブログを研究しました。私のために働いた完全なCMakeLists.txtを投稿しています。これは、初心者向けの基本的なテンプレートとして役立つと思います。

"CMakeLists.txt"

cmake_minimum_required(VERSION 3.10.2)

# Target Project
project (ClientProgram)

# Begin: Including Sources and Headers
include_directories(include)
file (GLOB SOURCES "src/*.c")
# End: Including Sources and Headers


# Begin: Generate executables
add_executable (ClientProgram ${SOURCES})
# End: Generate executables


# This Project Depends on External Project(s) 
include (ExternalProject)

# Begin: External Third Party Library
set (libTLS ThirdPartyTlsLibrary)
ExternalProject_Add (${libTLS}
    PREFIX          ${CMAKE_CURRENT_BINARY_DIR}/${libTLS}
# Begin: Download Archive from Web Server
    URL             http://myproject.com/MyLibrary.tgz
    URL_HASH        SHA1=<expected_sha1sum_of_above_tgz_file>
    DOWNLOAD_NO_PROGRESS ON
# End: Download Archive from Web Server

# Begin: Download Source from GIT Repository
#    GIT_REPOSITORY  https://github.com/<project>.git
#    GIT_TAG         <Refer github.com releases -> Tags>
#    GIT_SHALLOW     ON
# End: Download Source from GIT Repository

# Begin: CMAKE Comamnd Argiments
    CMAKE_ARGS      -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/${libTLS}
    CMAKE_ARGS      -DUSE_SHARED_LIBRARY:BOOL=ON
# End: CMAKE Comamnd Argiments    
)

# The above ExternalProject_Add(...) construct wil take care of \
# 1. Downloading sources
# 2. Building Object files
# 3. Install under DCMAKE_INSTALL_PREFIX Directory

# Acquire Installation Directory of 
ExternalProject_Get_Property (${libTLS} install_dir)

# Begin: Importing Headers & Library of Third Party built using ExternalProject_Add(...)
# Include PATH that has headers required by Target Project
include_directories (${install_dir}/include)

# Import librarues from External Project required by Target Project
add_library (lmytls SHARED IMPORTED)
set_target_properties (lmytls PROPERTIES IMPORTED_LOCATION ${install_dir}/lib/libmytls.so)
add_library (lmyxdot509 SHARED IMPORTED)
set_target_properties(lmyxdot509 PROPERTIES IMPORTED_LOCATION ${install_dir}/lib/libmyxdot509.so)

# End: Importing Headers & Library of Third Party built using ExternalProject_Add(...)
# End: External Third Party Library

# Begin: Target Project depends on Third Party Component
add_dependencies(ClientProgram ${libTLS})
# End: Target Project depends on Third Party Component

# Refer libraries added above used by Target Project
target_link_libraries (ClientProgram lmytls lmyxdot509)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.