簡體   English   中英

如何在自定義目錄中安裝cmake軟件包並按名稱將其鏈接到target_link_libraries()?

[英]How to install a cmake package in a custom directory and link it with target_link_libraries() by name?

我有一個自定義庫,它導出了include/lib/lib/cmake/ ,其中包含具有以下內容的MyProjectConfig.cmake

set(MyProject_INCLUDE_DIRS "/home/.../cmake-build-debug/thirdparty/myproj/include")
set(MyProject_LIBRARY_DIRS "/home/.../cmake-build-debug/thirdparty/myproj/lib")
set(MyProject_LIBRARIES "MyProject")

message(STATUS "MyProject found. Headers: ${MyProject_INCLUDE_DIRS}")

我在另一個這樣的項目中使用它:

include(ExternalProject)

ExternalProject_Add(MyProjectExternal
    PREFIX "${CMAKE_BINARY_DIR}/external/myproject"
    GIT_REPOSITORY "git@bitbucket.org:myproject.git"
    GIT_TAG "master"
    CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/thirdparty/myproject
)

add_dependencies(${PROJECT_NAME} MyProjectExternal)

# prevent error on first project use when dir doesn't exist
if(EXISTS ${CMAKE_BINARY_DIR}/thirdparty/myproject/lib) 

    find_package(MyProject REQUIRED HINTS ${CMAKE_BINARY_DIR}/thirdparty/myproject/lib/cmake)
    find_library(MyProject_LIB MyProject HINTS ${MyProject_LIBRARY_DIRS})
    target_link_libraries(${PROJECT_NAME} PUBLIC ${MyProject_LIB})
    target_include_directories(${PROJECT_NAME} PRIVATE ${MyProject_INCLUDE_DIRS})

endif()

我希望cmake會自動拾取MyProjectConfig.cmake設置的變量,以按名稱查找庫,這將起作用:

find_package(MyProject REQUIRED HINTS ${CMAKE_BINARY_DIR}/thirdparty/myproject/lib/cmake)
target_link_libraries(${PROJECT_NAME} PUBLIC MyProject)

但事實並非如此:

[ 87%] Built target MyProjectExternal
[ 90%] Linking CXX executable RootProject
/usr/bin/ld: cannot find -lMyProject
collect2: error: ld returned 1 exit status

第二部分 按此處要求 完整代碼和重現問題的步驟

庫代碼( mylib分支

CMakeLists.txt

cmake_minimum_required(VERSION 3.8)
project(MyLib VERSION 1.0.0 LANGUAGES CXX)

add_library(${PROJECT_NAME} SHARED
    src/mylib/hello.cpp
    )
target_include_directories(${PROJECT_NAME}
    PUBLIC $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include> $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
    )

set_target_properties(${PROJECT_NAME} PROPERTIES
    CXX_STANDARD 11
    CXX_STANDARD_REQUIRED YES
    CXX_EXTENSIONS NO
    )

include(cmake/install.cmake)

cmake / install.cmake

include(GNUInstallDirs)
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Targets
    ARCHIVE  DESTINATION ${CMAKE_INSTALL_LIBDIR}
    LIBRARY  DESTINATION ${CMAKE_INSTALL_LIBDIR}
    RUNTIME  DESTINATION ${CMAKE_INSTALL_BINDIR}
    INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
    PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

include(GenerateExportHeader)
generate_export_header(${PROJECT_NAME}
    EXPORT_MACRO_NAME EXPORT
    NO_EXPORT_MACRO_NAME NO_EXPORT
    PREFIX_NAME MYLIB_
    EXPORT_FILE_NAME ${CMAKE_BINARY_DIR}/include-exports/mylib/export.h)
target_include_directories(${PROJECT_NAME}
    PUBLIC
        $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/include-exports>
        $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
install(DIRECTORY ${CMAKE_BINARY_DIR}/include-exports/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

include(CMakePackageConfigHelpers)
set_property(TARGET ${PROJECT_NAME} PROPERTY VERSION ${PROJECT_VERSION})
set_property(TARGET ${PROJECT_NAME} PROPERTY SOVERSION ${PROJECT_VERSION_MAJOR})
set_property(TARGET ${PROJECT_NAME} PROPERTY INTERFACE_${PROJECT_NAME}_MAJOR_VERSION ${PROJECT_VERSION_MAJOR})
set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY COMPATIBLE_INTERFACE_STRING ${PROJECT_VERSION_MAJOR})
write_basic_package_version_file(
    "${CMAKE_BINARY_DIR}/CMakePackage/${PROJECT_NAME}ConfigVersion.cmake"
    VERSION ${PROJECT_VERSION}
    COMPATIBILITY SameMajorVersion
)
export(EXPORT ${PROJECT_NAME}Targets
    FILE "${CMAKE_BINARY_DIR}/CMakePackage/${PROJECT_NAME}.cmake"
)
SET(CONFIG_SOURCE_DIR ${CMAKE_SOURCE_DIR})
SET(CONFIG_DIR ${CMAKE_BINARY_DIR})
SET(${PROJECT_NAME}_INCLUDE_DIR "\${${PROJECT_NAME}_SOURCE_DIR}/include")
configure_package_config_file(${CMAKE_SOURCE_DIR}/cmake/Config.cmake.in
    "${CMAKE_BINARY_DIR}/CMakePackage/${PROJECT_NAME}Config.cmake"
    INSTALL_DESTINATION lib/cmake/${PROJECT_NAME}
    PATH_VARS ${PROJECT_NAME}_INCLUDE_DIR)
install(EXPORT ${PROJECT_NAME}Targets
    FILE ${PROJECT_NAME}.cmake
    DESTINATION lib/cmake/${PROJECT_NAME}
)
install(
    FILES
        "${CMAKE_BINARY_DIR}/CMakePackage/${PROJECT_NAME}Config.cmake"
        "${CMAKE_BINARY_DIR}/CMakePackage/${PROJECT_NAME}ConfigVersion.cmake"
    DESTINATION lib/cmake/${PROJECT_NAME}
    COMPONENT Devel
)

cmake / Config.cmake.in

@PACKAGE_INIT@

set_and_check(@PROJECT_NAME@_INCLUDE_DIRS "@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_INCLUDEDIR@")
set_and_check(@PROJECT_NAME@_LIBRARY_DIRS "@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@")
set(@PROJECT_NAME@_LIBRARIES "@PROJECT_NAME@")

check_required_components(@PROJECT_NAME@)

message(STATUS "@PROJECT_NAME@ found. Headers: ${@PROJECT_NAME@_INCLUDE_DIRS}")

包括/ mylib / hello.h

#ifndef MYLIB_HELLO_H
#define MYLIB_HELLO_H

#include <mylib/export.h>

namespace mylib {
    extern MYLIB_EXPORT void hello();
}

#endif

src / mylib / hello.cpp

#include <mylib/hello.h>
#include <iostream>

namespace mylib {
    void hello() {
        std::cout << "Hello, MyLib!" << std::endl;
    }
}

應用代碼( 主分支

CMakeLists.txt

cmake_minimum_required(VERSION 3.8)
project(MyLibConsumer VERSION 1.0.0 LANGUAGES CXX)

add_executable(${PROJECT_NAME}
    main.cpp)

set_target_properties(${PROJECT_NAME} PROPERTIES
    CXX_STANDARD 11
    CXX_STANDARD_REQUIRED YES
    CXX_EXTENSIONS NO
    )

include(ExternalProject)
set(LIB_INSTALL_DIR ${CMAKE_BINARY_DIR}/thirdparty/mylib)
ExternalProject_Add(MyLibExternal
    PREFIX "${CMAKE_BINARY_DIR}/external/mylib"
    GIT_REPOSITORY "https://github.com/arteniioleg/stackoverflow-question-46772541.git"
    GIT_TAG "mylib"
    CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIB_INSTALL_DIR}
    )
add_dependencies(${PROJECT_NAME} MyLibExternal)
if(EXISTS ${LIB_INSTALL_DIR}/lib) # prevent error on first cmake load
    find_package(MyLib REQUIRED HINTS ${LIB_INSTALL_DIR}/lib/cmake)

    # fixme: make this work
    target_link_libraries(${PROJECT_NAME} PUBLIC MyLib)

    # to test: comment the above line and uncomment the below lines
    #find_library(MyLib_LIB MyLib HINTS ${MyLib_LIBRARY_DIRS})
    #target_link_libraries(${PROJECT_NAME} PRIVATE ${MyLib_LIB})
    #target_include_directories(${PROJECT_NAME} PRIVATE ${MyLib_INCLUDE_DIRS})
endif()

main.cpp

#include <mylib/hello.h>

int main()
{
    mylib::hello();
    return 0;
}

錯誤

main.cpp:1:25: fatal error: mylib/hello.h: No such file or directory
 #include <mylib/hello.h>
                         ^

使用由find_package()創建的MyLib_*變量可以解決此問題,但是它太冗長了。

這里的建議:

但是,推薦的方法是使用CMake功能創建配置文件。 這樣,將創建完整的目標,並將其用於鏈接。

怎么做?

我想分兩步使用我的圖書館,例如Qt:

  1. find_package(Qt5Widgets)
  2. target_link_libraries(myApp Qt5::Widgets)

我希望cmake會自動選擇MyProjectConfig.cmake設置的變量,以按名稱查找庫。

find_package命令中最“神奇”的是它如何搜索*Config.cmake腳本。 找到腳本后,只需在調用者的上下文中執行腳本即可。

在您的情況下,CMake設置變量MyProject_INCLUDE_DIRSMyProject_LIBRARY_DIRSMyProject_LIBRARIES 沒什么了 它不會創建MyProject目標,等等。

如果要find_package()准備與MyProject庫的鏈接,則需要相應地編寫MyProjectConfig.cmake腳本(例如, link_directories()調用link_directories() )。

但是,推薦的方法是使用CMake功能創建配置文件。 這樣,將創建完整的目標 ,並將其用於鏈接。 有關更多信息,請參見cmake-packages文檔

只需在MyProjectConfig.cmake包括MyProject.cmake (默認情況下不包括)

include(${CMAKE_CURRENT_LIST_DIR}/MyProject.cmake)

密鑰文件的最終版本:

庫/cmake/Config.cmake.in

刪除的冗余變量: *_INCLUDE_DIRS*_LIBRARY_DIRS*_LIBRARIES

@PACKAGE_INIT@

include(${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@.cmake)

check_required_components(@PROJECT_NAME@)

message(STATUS "@PROJECT_NAME@ found.")

EXECUTABLE / CMakeLists.txt

如果未構建外部項目,則將外部項目移至可執行文件之前,然后將其移動到return()

cmake_minimum_required(VERSION 3.8)
project(MyLibConsumer VERSION 1.0.0 LANGUAGES CXX)

set(LIB_INSTALL_DIR ${CMAKE_BINARY_DIR}/thirdparty/mylib)
include(ExternalProject)
ExternalProject_Add(MyLibExternal
    PREFIX "${CMAKE_BINARY_DIR}/external/mylib"
    GIT_REPOSITORY "https://github.com/arteniioleg/stackoverflow-question-46772541.git"
    GIT_TAG "mylib"
    CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIB_INSTALL_DIR}
    )
if(NOT EXISTS ${LIB_INSTALL_DIR}/lib)
    # Happens on first CMake run.
    # Can't continue because the below `find_package(REQUIRED)` will fail.
    # Build all external project targets then rerun CMake and build the project target.
    message(AUTHOR_WARNING "Build all external projects then reload cmake.")
    return()
endif()

add_executable(${PROJECT_NAME} main.cpp)
set_target_properties(${PROJECT_NAME} PROPERTIES
    CXX_STANDARD 11
    CXX_STANDARD_REQUIRED YES
    CXX_EXTENSIONS NO
    )

add_dependencies(${PROJECT_NAME} MyLibExternal)
find_package(MyLib REQUIRED HINTS ${LIB_INSTALL_DIR})
target_link_libraries(${PROJECT_NAME} PRIVATE MyLib)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM