CMake第五课--总结
CMake第五课 总结
手把手教你学CMake,最后一节我们将前面学到的知识点综合应用。如果编译软件使用了外部库,事先并不知道它的头文件和链接库的位置,得在编译命令中加上包含它们的查找路径。CMake使用FIND_PACKAGE()命令来解决这个问题,FIND_PACKAGE() 命令会在模块路径中寻找 Find<name>.cmake 。首先CMake查看${CMAKE_MODULE_PATH} 中的所有目录,然后再查看它自己的模块目录。
我们这一节有两个大模块,第一个是先介绍一下 cmake 提供的 FindCURL 模块的使用;第二个就是基于我们前面学的 libhello 共享库,编写一个 FindHello.cmake 模块。
===================================================================================
目标:
1.首先介绍一下 cmake 提供的 FindCURL 模块的使用。
2.基于我们前面的 libhello 共享库,编写一个 FindHello.cmake 模块。
====================================================================================
一.使用 FindCURL 模块
绝对路径:/home/guo/cmake_practice/5(工程目录) 标红的那部分改成自己的用户名
目录结构(按照下面结构添加文件夹和文件):
1.一个CMakeLists.txt文件(/home/guo/cmake_practice/5):
CMAKE_MINIMUM_REQUIRED( VERSION 2.8 ) # 声明要求的 cmake 最低版本 PROJECT(CURLTEST) # 声明一个 cmake 工程 ADD_SUBDIRECTORY(src)
2.一个src文件夹(/home/guo/cmake_practice/5/src),里面包含CMakeLists.txt和main.c
src/main.c内容:
//这段main.c代码的作用是通过curl取回 www.linux-ren.org 的首页并写入/home/guo/cmake_practice/5/test文件中
#include <curl/curl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> FILE *fp; int write_data(void *ptr, size_t size, size_t nmemb, void *stream) { int written = fwrite(ptr, size, nmemb, (FILE *)fp); return written; } int main() { const char * path = "/home/guo/cmake_practice/5/test"; const char * mode = "w"; fp = fopen(path,mode); curl_global_init(CURL_GLOBAL_ALL); CURLcode res; CURL *curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "http://www.linux-ren.org"); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); res = curl_easy_perform(curl); curl_easy_cleanup(curl); }
src/CMakeLists.txt内容:
CMAKE_MINIMUM_REQUIRED( VERSION 2.8 ) # 声明要求的 cmake 最低版本 ADD_EXECUTABLE(curltest main.c) #INCLUDE_DIRECTORIES(/usr/include) #TARGET_LINK_LIBRARIES(curltest curl) FIND_PACKAGE(CURL) IF(CURL_FOUND) INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR}) TARGET_LINK_LIBRARIES(curltest ${CURL_LIBRARY}) ELSE(CURL_FOUND) MESSAGE(FATAL_ERROR ”CURL library not found”) ENDIF(CURL_FOUND)
外部编译:
cd /home/guo/cmake_practice/5
mkdir build (新建build文件夹)
cd build
cmake ..
make
执行二进制可执行文件
cd /home/guo/cmake_practice/5/build/src
./curltest
执行完程序后,可以在 /home/guo/cmake_practice/5下看到有一个test文件
-------------------------------------------------------------------------------
详细解释:
1.添加 curl 的头文件路径和库文件的两种方法。
方法一:直接通过 INCLUDE_DIRECTORIES 和 TARGET_LINK_LIBRARIES 指令添加
我们可以直接在 src/CMakeLists.txt 中添加:
INCLUDE_DIRECTORIES(/usr/include)
TARGET_LINK_LIBRARIES(curltest curl)
然后建立 build 目录进行外部构建即可。
方法二:使用 FindCURL 模块( 即:FindCURL 模块)
向 src/CMakeLists.txt 中添加:
FIND_PACKAGE(CURL)
IF(CURL_FOUND)
INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(curltest ${CURL_LIBRARY})
ELSE(CURL_FOUND)
MESSAGE(FATAL_ERROR ”CURL library not found”)
ENDIF(CURL_FOUND)
2.对于系统预定义的 Find<name>.cmake 模块,使用方法一般如上例所示:
只要找到包,都会定义以下几个变量:
- <name>_FOUND
- <name>_INCLUDE_DIR or <name>_INCLUDES
- <name>_LIBRARY or <name>_LIBRARIES
- <name>_DEFINITIONS
你可以通过<name>_FOUND 来判断模块是否被找到,如果没有找到,按照工程的需要关闭某些特性、给出提醒或者中止编译,上面的例子就是报出致命错误并终止构建。如果<name>_FOUND 为真,则 将<name>_INCLUDE_DIR 加入 INCLUDE_DIRECTORIES; 将<name>_LIBRARY 加入TARGET_LINK_LIBRARIES 中。
3.想知道更多关于FOUND_PACKAGE的内容可以参考这篇博客:https://blog.csdn.net/xs1102/article/details/78241291
====================================================================================
二.编写属于自己的 FindHello 模块
第一步,构建动态库、静态库并进行安装
绝对路径:/home/guo/cmake_practice/6(工程目录)
目录结构:
1.工程目录下(/home/guo/cmake_practice/6)CMakeLists.txt 内容如下:
PROJECT(HELLOLIB)
ADD_SUBDIRECTORY(lib)
2.一个lib文件夹(/home/guo/cmake_practice/6/lib) 建立两个源文件 hello.cpp 与 hello.h 和 一个CMakeLists.txt
lib/hello.h内容:
#ifndef HELLO_H //防止别的头文件可能重复包含hello.h文件,防止重定义错误 #define HELLO_H #include <iostream> using namespace std; int HelloFunc(); #endif
lib/hello.cpp内容:
#include "hello.h" int HelloFunc() { cout<<"Hello World!"<<endl; return 0; }
lib/CMakeLists.txt内容:
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)# 声明要求的 cmake 最低版本 SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC}) SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC}) SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello") SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1) SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1) SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1) INSTALL(TARGETS hello hello_static LIBRARY DESTINATION hello/lib ARCHIVE DESTINATION hello/lib) INSTALL(FILES hello.h DESTINATION hello/include)
外部编译:
cd /home/guo/cmake_practice/6
mkdir build (新建build文件夹)
cd build
cmake -DCMAKE_INSTALL_PREFIX=/home/guo/cmake_practice/6 ..
make
sudo make install
我们就可以将头文件和共享库安装到<prefix>/hello/include 和<prefix>/hello/lib中了,即/home/guo/cmake_practice/6/hello/include和
/home/guo/cmake_practice/6/hello/lib。(<prefix>在这里就是指/home/guo/cmake_practice/6)
-------------------------------------------------------------------------------
第二步,构建工程调用刚才自己构建的模块
绝对路径:/home/guo/cmake_practice/7(工程目录)
目录结构:
1.工程目录下(/home/guo/cmake_practice/7)CMakeLists.txt内容如下:
CMAKE_MINIMUM_REQUIRED( VERSION 2.8 ) # 声明要求的cmake最低版本 PROJECT(HELLOTEST) # 声明一个 cmake 工程 SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) #为了让cmake能找到FindHello模块,设置路径 ADD_SUBDIRECTORY(src)
2.一个src文件夹 (/home/guo/cmake_practice/7/src) 建立源文件 main.cpp 和 一个CMakeLists.txt
src/main.cpp内容为:
#include <hello.h> int main() { HelloFunc(); return 0; }
src/CMakeLists.txt内容为:
FIND_PACKAGE(HELLO)
IF(HELLO_FOUND)
LINK_DIRECTORIES(${HELLO_LIBRARY})
INCLUDE_DIRECTORIES(${HELLO_INCLUDE_DIR})
ADD_EXECUTABLE(hello main.cpp)
TARGET_LINK_LIBRARIES(hello ${HELLO_LIBRARY})
ENDIF(HELLO_FOUND)
3.一个cmake文件夹 ,编写FindHELLO.cmake文件 (FindXXXX.cmake)
cmake/FindHELLO.cmake文件内容:
FIND_PATH(HELLO_INCLUDE_DIR hello.h /home/guo/cmake_practice/6/hello/include ) FIND_LIBRARY(HELLO_LIBRARY NAMES hello PATHS /home/guo/cmake_practice/6/hello/lib )
#如果头文件和库都找到,设置标志位为真 IF (HELLO_INCLUDE_DIR AND HELLO_LIBRARY) SET(HELLO_FOUND TRUE) ENDIF (HELLO_INCLUDE_DIR AND HELLO_LIBRARY) IF (HELLO_FOUND) IF (NOT HELLO_FIND_QUIETLY) MESSAGE(STATUS "Found Hello: ${HELLO_LIBRARY}") ENDIF (NOT HELLO_FIND_QUIETLY) ELSE (HELLO_FOUND) IF (HELLO_FIND_REQUIRED) MESSAGE(FATAL_ERROR "Could not find hello library") ENDIF (HELLO_FIND_REQUIRED) ENDIF (HELLO_FOUND)
外部编译:
cd /home/guo/cmake_practice/7
mkdir build (新建build文件夹)
cd build
cmake ..
make
执行二进制可执行文件
cd /home/guo/cmake_practice/7/build/
./src/hello
===============================================================================
详细解释:
1.FIND_PACKAGE(<name> [major.minor] [QUIET] [NO_MODULE]
[[REQUIRED|COMPONENTS] [componets...]])
- 前面的 CURL 例子中我们使用了最简单的 FIND_PACKAGE 指令,其实他可以使用多种参数,QUIET 参数,对应与我们编写的 FindHELLO 中的HELLO_FIND_QUIETLY,如果不指定这个参数,就会执行:MESSAGE(STATUS "Found Hello: ${HELLO_LIBRARY}")
- REQUIRED 参数,其含义是指这个共享库是否是工程必须的,如果使用了这个参数,说明这个链接库是必备库,如果找不到这个链接库,则工程不能编译。对应于FindHELLO.cmake 模块中的 HELLO_FIND_REQUIRED 变量。
- 同样,我们在上面的模块中定义了 HELLO_FOUND,HELLO_INCLUDE_DIR,HELLO_LIBRARY 变量供开发者在 FIND_PACKAGE 指令中使用。
2.为了能够让工程找到 FindHELLO.cmake 模块(存放在工程中的 cmake 目录),我们在主工程文件 CMakeLists.txt 中加入:
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
3.安装的头文件和库所在的路径,注意“名字的大小写”和“PATHS的‘S’别漏掉”
FIND_PATH(HELLO_INCLUDE_DIR hello.h /home/guo/cmake_practice/6/hello/include ) FIND_LIBRARY(HELLO_LIBRARY NAMES hello PATHS /home/guo/cmake_practice/6/hello/lib )
4.在编写FindHELLO需要注意的问题!!!!!重要问题!!!!!
①要保证src/CMakeLists.txt下的:
-
-
-
-
-
-
-
-
-
- FIND_PACKAGE(name)
- name_FOUND
- name_INCLUDE_DIR or name_INCLUDES
- name_LIBRARY or name_LIBRARIES
- name_DEFINITIONS
-
-
-
-
-
-
-
-
和cmake下的:
-
-
-
-
-
-
-
-
-
- Findname.cmake文件名
-
-
-
-
-
-
-
-
这几个name一定要一致!!!!
②并且确保程序引用的头文件和库一定要包含在name_INCLUDE_DIR 和name_LIBRARY所指示的路径里。