2016-06-30 31 views
6

私のC++プロジェクトには、サードパーティのライブラリ(現在はgitサブモジュール)のソースコードが含まれています。CMakeで外部ライブラリを一度だけ構築する

このライブラリは、add_subdirectoryを使用してメインのCMakelistsによってプロジェクトに追加され、ライブラリはメインターゲットとリンクされます。ここで

が私の現在のcmakeのファイルの縮小版である:

add_subdirectory(foo) 
set(FOO_LIBRARY ${CMAKE_CURRENT_SOURCE_DIR}/libfoo/libfoo.so) 

add_executable(target main.cpp) 
add_dependencies(target foo) 
target_link_libraries(target ${FOO_LIBRARY}) 

このライブラリは、構築するために長い時間がかかると、私はそのコードを変更しないので、私はそれがビルド構成ごとに(一度しか建て必要)。しかし、私は私のコードをきれいにして再構築すると、ライブラリファイルを消去して再コンパイルします。

私はライブラリのディレクトリにCLEAN_NO_CUSTOMというプロパティを設定しようとしましたが、ドキュメンテーションによると、カスタムコマンドターゲットでのみ機能します。

CMakeには、このライブラリターゲットを一度だけ生成する必要があるか、またはmake cleanでクリーニングしないように指定できるメカニズムがありますか?

+2

サードパーティのライブラリの*内部*のターゲットを使用していないので、 'ExternalProject_Add'でのアプローチは' add_subdirectory'よりも優れているようです。 'ExternalProject_Add'はクリーンルールを指定していないので、CMakeはライブラリのクリーンアップを試みません。 – Tsyvarev

答えて

4

@Tsyvarevが言ったように、あなたのケースではExternalProject_Addadd_subdirectoryより優れています。 add_subdirectoryは、作成するターゲットがtarget_link_libraries()コマンドの右側で使用できるので、ExternalProject_Addで作成されたターゲットでは使用できないため、プロジェクトをビルドシステムの不可欠な部分にしたい場合に適しています。

私のプロジェクトで使用したアプローチは次のとおりです。必要なライブラリを見つけようとすると、それが見つからなかった場合にのみビルドします。私はINTERFACEライブラリを使ってFOO_EXTERNALをtarget_link_libraries()で受け入れ可能なターゲットに変えました。

add_library(foo INTERFACE) 
find_package(foo ${FOO_VER}) 
if(NOT foo_FOUND) 
    include(ExternalProject) 
    include(GNUInstallDirs) 
    ExternalProject_Add(FOO_EXTERNAL 
        SOURCE_DIR "${FOO_SOURCE_DIR}" 
        BINARY_DIR "${FOO_BINARY_DIR}" 
        INSTALL_DIR "${FOO_INSTALL_DIR}" 
        CMAKE_ARGS "-DCMAKE_C_FLAGS=${CMAKE_C_FLAGS}" 
           "-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}" 
           "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" 

           "-DCMAKE_INSTALL_PREFIX=${FOO_INSTALL_DIR}" 
        ) 

    add_dependencies(foo FOO_EXTERNAL) 
    set(foo_LIBRARY 
      "${FOO_INSTALL_DIR}/${CMAKE_INSTALL_LIBDIR}/${CMAKE_STATIC_LIBRARY_PREFIX}foo${CMAKE_STATIC_LIBRARY_SUFFIX}") 
    set(foo_INCLUDE_DIR "${FOO_INSTALL_DIR}/include") 
endif() 

target_link_libraries(foo INTERFACE ${foo_LIBRARY}) 
target_include_directories(foo INTERFACE ${foo_INCLUDE_DIR}) 
3

@Hikkeの優れた答えに基づいて、私は外部プロジェクトの使用を簡略化するために2つのマクロを書きました。

コード

include(ExternalProject) 

# 
# Add external project. 
# 
# \param name    Name of external project 
# \param path    Path to source directory 
# \param external   Name of the external target 
# 
macro(add_external_project name path) 
    # Create external project 
    set(${name}_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${path}) 
    set(${name}_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${path}) 
    ExternalProject_Add(${name} 
     SOURCE_DIR "${${name}_SOURCE_DIR}" 
     BINARY_DIR "${${name}_BINARY_DIR}" 
     CMAKE_ARGS "-DCMAKE_C_FLAGS=${CMAKE_C_FLAGS}" 
        "-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}" 
        "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" 
        # These are only useful if you're cross-compiling. 
        # They, however, will not hurt regardless. 
        "-DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}" 
        "-DCMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR}" 
        "-DCMAKE_AR=${CMAKE_AR}" 
        "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}" 
        "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" 
        "-DCMAKE_RC_COMPILER=${CMAKE_RC_COMPILER}" 
        "-DCMAKE_COMPILER_PREFIX=${CMAKE_COMPILER_PREFIX}" 
        "-DCMAKE_FIND_ROOT_PATH=${CMAKE_FIND_ROOT_PATH}" 
     INSTALL_COMMAND "" 
    ) 

endmacro(add_external_project) 

# 
# Add external target to external project. 
# 
# \param name    Name of external project 
# \param includedir  Path to include directory 
# \param libdir   Path to library directory 
# \param build_type  Build type {STATIC, SHARED} 
# \param external   Name of the external target 
# 
macro(add_external_target name includedir libdir build_type external) 
    # Configurations 
    set(${name}_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${libdir}) 

    # Create external library 
    add_library(${name} ${build_type} IMPORTED) 
    set(${name}_LIBRARY "${${name}_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${CMAKE_${build_type}_LIBRARY_PREFIX}${name}${CMAKE_${build_type}_LIBRARY_SUFFIX}") 

    # Find paths and set dependencies 
    add_dependencies(${name} ${external}) 
    set(${name}_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${includedir}") 

    # Set interface properties 
    set_target_properties(${name} PROPERTIES IMPORTED_LOCATION ${${name}_LIBRARY}) 
    set_target_properties(${name} PROPERTIES INCLUDE_DIRECTORIES ${${name}_INCLUDE_DIR}) 
endmacro(add_external_target) 

説明

第2のステップは、必要な依存関係を設定し、インターフェイスを定義しながら、最初のマクロが、全体の外部ビルドステップを行い、外部プロジェクトを作成します。ほとんどのプロジェクトには複数のインターフェース/ライブラリーがあるため、2つの区切りは重要です。

例は、私がgoogletestサブフォルダに私のプロジェクトではサブモジュールとしてGoogleTestを持っていると言います。次のインターフェイスを使用して、gtestgtest_mainのマクロを定義することができます。これは、Googleがそれを行う方法と非常によく似ています。

target_link_libraries(target_tests 
    gtest 
    gtest_main 
    # The CMAKE_THREAD_LIBS_INIT can be found from `find_package(Threads)` 
    # and is required for all but MinGW builds. 
    ${CMAKE_THREAD_LIBS_INIT} 
) 

これもCMakeの主導のプロジェクトで、実際の外部ビルドプロセスを簡素化するのに十分な定型を提供する必要があります。

add_external_project(googletest_external googletest) 
add_external_target(gtest googletest/googletest/include googletest/googlemock/gtest STATIC googletest_external) 
add_external_target(gtest_main googletest/googletest/include googletest/googlemock/gtest STATIC googletest_external) 

は、私はその前に同じようgoogletestに私の目標をリンクすることができます。

関連する問題