【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}>)