【CMake】安装项目

测试项目假设对系统库UUID依赖,如果没有找到UUID库,我们将通过预处理程序排除使用UUID库的代码。

项目文件结构:

├── CMakeLists.txt
├── src
│   ├── CMakeLists.txt
│   ├── main.cpp
│   ├── Message.cpp
│   └── Message.hpp
└── tests
    └── CMakeLists.txt

 

CMakeLists.txt文件:

 1 cmake_minimum_required(VERSION 3.6 FATAL_ERROR)
 2 project(
 3   CmakeTest
 4   LANGUAGES CXX
 5   VERSION 1.0.0)
 6 
 7 set(CMAKE_CXX_STANDARD 11)
 8 set(CMAKE_CXX_EXTENSIONS OFF)
 9 set(CMAKE_CXX_STANDARD_REQUIRED ON)
10 
11 set(CMAKE_INSTALL_PREFIX "${CMAKE_SOURCE_DIR}/install")
12 message(STATUS "Project will be installed to ${CMAKE_INSTALL_PREFIX}")
13 
14 if(NOT CMAKE_BUILD_TYPE)
15   set(CMAKE_BUILD_TYPE
16       Release
17       CACHE STRING "Build type" FORCE)
18 endif()
19 message(STATUS "Build type set to ${CMAKE_BUILD_TYPE}")
20 
21 include(GNUInstallDirs)
22 set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY
23     ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
24 set(CMAKE_LIBRARY_OUTPUT_DIRECTORY
25     ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
26 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY
27     ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_BINDIR})
28 
29 set(INSTALL_LIBDIR
30     ${CMAKE_INSTALL_LIBDIR}
31     CACHE PATH "Installation directory for libraries")
32 set(INSTALL_BINDIR
33     ${CMAKE_INSTALL_BINDIR}
34     CACHE PATH "Installation directory for executables")
35 set(INSTALL_INCLUDEDIR
36     ${CMAKE_INSTALL_INCLUDEDIR}
37     CACHE PATH "Installation directory for header files")
38 
39 if(WIN32 AND NOT CYGWIN)
40   set(DEF_INSTALL_CMAKEDIR CMake)
41 else()
42   set(DEF_INSTALL_CMAKEDIR share/cmake/${PROJECT_NAME})
43 endif()
44 set(INSTALL_CMAKEDIR
45     ${DEF_INSTALL_CMAKEDIR}
46     CACHE PATH "Installation directory for CMake files")
47 
48 foreach(p LIB BIN INCLUDE CMAKE)
49   file(TO_NATIVE_PATH ${CMAKE_INSTALL_PREFIX}/${INSTALL_${p}DIR} _path)
50   message(STATUS "Installing ${p} components to ${_path}")
51   unset(_path)
52 endforeach()
53 
54 add_subdirectory(src)
55 enable_testing()
56 add_subdirectory(tests)

 

src/CMakeLists.txt文件:

 1 # 查找系统依赖库UUID
 2 find_package(PkgConfig QUIET)
 3 if(PKG_CONFIG_FOUND)
 4   pkg_search_module(UUID uuid IMPORTED_TARGET)
 5   if(TARGET PkgConfig::UUID)
 6     message(STATUS "Found libuuid")
 7     set(UUID_FOUND TRUE)
 8   endif()
 9 endif()
10 
11 # 添加动态库message
12 add_library(message-shared SHARED "")
13 # 指定动态库源码
14 target_sources(message-shared PRIVATE ${CMAKE_CURRENT_LIST_DIR}/Message.cpp)
15 # 声明编译时定义
16 target_compile_definitions(message-shared
17                            PUBLIC $<$<BOOL:${UUID_FOUND}>:HAVE_UUID>)
18 # 生命依赖库
19 target_link_libraries(message-shared
20                       PUBLIC $<$<BOOL:${UUID_FOUND}>:PkgConfig::UUID>)
21 # 设置目标的附加属性 
22 # POSITION_INDEPENDENT_CODE 1:设置生成位置无关代码所需的编译器标志 
23 #SOVERSION SOVERSION :这是动态库提供的应用程序编程接口(API)版本 
24 # OUTPUT_NAME "message":这告诉CMake库的名称message
25 # DEBUG_POSTFIX "_d":这告诉CMake,如果我们以Debug配置构建项目,则将_d后缀添加到生成的动态库 
26 #PUBLIC_HEADER "Message.hpp":我们使用这个属性来设置头文件列表(本例中只有一个头文件),声明提供的API函数 
27 #MACOSX_RPATH ON:这将动态库的install_name部分(目录)设置为macOS上的@rpath 
28 #WINDOWS_EXPORT_ALL_SYMBOLS ON:这将强制在Windows上编译以导出所有符号
29 set_target_properties(
30   message-shared
31   PROPERTIES POSITION_INDEPENDENT_CODE 1
32              SOVERSION ${PROJECT_VERSION_MAJOR}
33              OUTPUT_NAME "message"
34              DEBUG_POSTFIX "_d"
35              PUBLIC_HEADER "Message.hpp"
36              MACOSX_RPATH ON
37              WINDOWS_EXPORT_ALL_SYMBOLS ON)
38 # 添加可执行目标
39 add_executable(${PROJECT_NAME} main.cpp)
40 # 可执行目标链接到动态库
41 target_link_libraries(${PROJECT_NAME} PUBLIC message-shared)
42 
43 # file(RELATIVE_PATH <variable> <directory> <file>)
44 # 计算目录<directory>到<file>的相对路径存储到变量<variable>45 file(RELATIVE_PATH _rel ${CMAKE_INSTALL_PREFIX}/${INSTALL_BINDIR}
46      ${CMAKE_INSTALL_PREFIX})
47 
48 # 设置变量_rpath
49 if(APPLE)
50   set(_rpath "@loader_path/${_rel}")
51 else()
52   set(_rpath "\$ORIGIN/${_rel}")
53 endif()
54 
55 # 将cmake风格的路径(/)转换为系统风格的路径(\在 Windows 主机和/ 其他地方)
56 file(TO_NATIVE_PATH "${_rpath}/${INSTALL_LIBDIR}" message_RPATH)
57 
58 # 设置target的rpath 
59 # SKIP_BUILD_RPATH OFF:告诉CMake生成适当的RPATH,以便能够在构建树中运行可执行文件。
60 # BUILD_WITH_INSTALL_RPATH OFF:关闭生成可执行目标,使其RPATH调整为与安装树的RPATH相同。在构建树中不运行可执行文件。
61 # INSTALL_RPATH "${message_RPATH}":将已安装的可执行目标的RPATH设置为先前的路径。
62 # INSTALL_RPATH_USE_LINK_PATH ON:告诉CMake将链接器搜索路径附加到可执行文件的RPATH中。
63 set_target_properties(
64   ${PROJECT_NAME}
65   PROPERTIES MACOSX_RPATH ON
66              SKIP_BUILD_RPATH OFF
67              BUILD_WITH_INSTALL_RPATH OFF
68              INSTALL_RPATH "${message_RPATH}"
69              INSTALL_RPATH_USE_LINK_PATH ON)
70 
71 # install命令来指定安装位置。注意,路径是相对的
72 install(
73   TARGETS message-shared ${PROJECT_NAME}
74   ARCHIVE DESTINATION ${INSTALL_LIBDIR} COMPONENT lib
75   RUNTIME DESTINATION ${INSTALL_BINDIR} COMPONENT bin
76   LIBRARY DESTINATION ${INSTALL_LIBDIR} COMPONENT lib
77   PUBLIC_HEADER DESTINATION ${INSTALL_INCLUDEDIR}/message COMPONENT dev)

 

 

src/main.cpp文件:

 1 #include <cstdlib>
 2 #include <iostream>
 3 #include "Message.hpp"
 4 int main()
 5 {
 6     Message say_hello("Hello, CMake World!");
 7     std::cout << say_hello << std::endl;
 8     Message say_goodbye("Goodbye, CMake World");
 9     std::cout << say_goodbye << std::endl;
10     return EXIT_SUCCESS;
11 }

 

src/Message.hpp文件:

 1 #pragma once
 2 #include <iosfwd>
 3 #include <string>
 4 class Message
 5 {
 6 public:
 7     Message(const std::string &m) : message_(m) {}
 8     friend std::ostream &operator<<(std::ostream &os, Message &obj)
 9     {
10         return obj.printObject(os);
11     }
12 
13 private:
14     std::string message_;
15     std::ostream &printObject(std::ostream &os);
16 };
17 std::string getUUID();

 

src/Message.cpp文件:

 1 #include "Message.hpp"
 2 #include <iostream>
 3 #include <string>
 4 #ifdef HAVE_UUID
 5 #include <uuid/uuid.h>
 6 #endif
 7 std::ostream &Message::printObject(std::ostream &os)
 8 {
 9     os << "This is my very nice message: " << std::endl;
10     os << message_ << std::endl;
11     os << "...and here is its UUID: " << getUUID();
12     return os;
13 }
14 #ifdef HAVE_UUID
15 std::string getUUID()
16 {
17     uuid_t uuid;
18     uuid_generate(uuid);
19     char uuid_str[37];
20     uuid_unparse_lower(uuid, uuid_str);
21     uuid_clear(uuid);
22     std::string uuid_cxx(uuid_str);
23     return uuid_cxx;
24 }
25 #else
26 std::string getUUID()
27 {
28     return "Ooooops, no UUID for you!";
29 }
30 #endif

 

tests/CMakeLists.txt文件:

1 add_test(NAME test_shared COMMAND $<TARGET_FILE:${PROJECT_NAME}>)

 

posted @ 2022-08-09 00:04  禅元天道  阅读(148)  评论(0编辑  收藏  举报