cmake 01


原文链接: cmake 01

这篇文章主要介绍如何一步一步自己写cmake文件。

一,内部链接和外部链接

在总文件夹下新建main.c

//main.c
#include<stdio.h>
int main(){
    printf("hello world from t1 main!\n");
    return 0;
}

在同一目录下写好CMakeLists.txt(这个文件很重要,大小写有特殊要求,如果存在多个目录,要在每一个目录下面都要存在一个CMakeLists.txt)

PROJECT(HELLO)#新建项目
SET(SRC_LIST main.c)#把文件名指定为特殊字符 如果有多个,那么SET(SRC_LIST main.c t1.c t2.c)-->基本语法:参数之间用空格分开即可。
#指令是大小写无关的,但是参数和变量是大小写相关的。
MESSAGE(STATUS "this is BINARY dir" ${PROJECT_BINARY_DIR})#这里默认变量
MESSAGE(STATUS "this is  SOURCE dir" ${PROJECT_SOURCE_DIR})#这里是默认变量
#message 语法:SEND_ERROR:产生错误,生成过程被跳过。
#STATUS:输出前缀为-的信息。FATAL_ERROR:立刻终止cmake过程。
ADD_EXECUTABLE(hello ${SRC_LIST})#添加到编译生成的执行文件
#定义的这个工程会生成一个文件名字为hello的可执行文件
#对于变量的引用,只有在IF的情况下,变量才会被直接引用,其他时候都要加入${}才可以。

接下来输入:

cmake .
make

就生成执行文件.(以上为内部构建,比较冗长,容易生成很多无用的信息)

外部构建
mkdir build
cd build
cmake ..
make

我们可以在当前目录下获得目标文件hello。最大的好处是对原有工程没有任何影响,所有动作都全部发生在编译目录。
此时默认变量:PROJECT_SOURCE_DIR 依然是工程目录
但是 PROJECT_BINARY_DIR 在外部编译之后从原来的工程目录转化为工程目录/build

二,多文件下的cmake与安装(外部构建)

文件分层:src--工程源码(main.c) doc--工程文档 COPYRIGHT/README 放在根目录 bin--构建后目标文件所在的子目录
安装:我们要把编译得到的内容安装到/tmp/usr/test2 中
src下的main.c文件:

#include<stdio.h>
int main(){
    printf("hello world from t1 main!\n");
    return 0;
}

src下的CMakeList.txt 文件

SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
#设定变量的得到的编译执行文件输出到bin文件下。
#如果有编译好的lib文件,那么使用
#SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
#在哪里加入ADD_EXECUTABLE or ADD_LIBRARY,在哪里写这两句话
ADD_EXECUTABLE(hello main.c)
install(TARGETS hello RUNTIME DESTINATION bin)
#安装目标文件(三种类型)可执行文件-RUNTIME 动态库-LIBRARY 静态库-ARCHIVE 
#example:
#INSTALL(TARGETS myrun mylib mystaticlib
#              RUNTIME DESTINATION bin
#              LIBRARY DESTINATION lib
#              ARCHIVE DESTINATION libstatic
#)

根目录下的CMakeList.txt文件

cmake_minimum_required(VERSION 3.5)
#确定最小版本号
PROJECT(HELLO)
install(FILES COPYRIGHT README DESTINATION share/doc/cmake/test2)
#安装普通文件
install(PROGRAMS runhello.sh DESTINATION bin)
#安装非目标文件的可执行程序
install(DIRECTORY doc/ DESTINATION share/doc/cmake/test2)
#安装文件夹
add_subdirectory(src)
#向当前工程添加存放源文件的子目录,并可以指定编译输出的结果
#add_subdirectory(source_dir [binary_dir]) 如果编译输出目录为空,那么就是build下的相同目录

在安装之前,必须要定义安装的位置,否则将会默认为/usr/local
我们使用DCMAKE_INSTALL_PREFIX 定义

cmake  -DCMAKE_INSTALL_PREFIX=/tmp/test2/usr  ..
make 
make install

静态库与动态库的构建

首先建立lib文件夹,写入hello.c 和 hello.h

//hello.c
#include "hello.h"
void HelloFunc(){
    printf("hello world in test3!\n");
}
//hello.h
#ifndef HELLO_H
#define HELLO_H
#include <stdio.h>
void HelloFunc();
#endif

lib下的CMakeLists.txt:

set(LIBHELLO_SRC hello.c)
add_library(hello SHARED ${LIBHELLO_SRC})
#生成共享库
add_library(hello_static STATIC ${LIBHELLO_SRC})
#生成静态库(这里不能重名,否则只能生成一个),不需要写全名
#SHARED 动态库 STATIC 静态库
set_target_properties(hello_static PROPERTIES OUTPUT_NAME "hello")
#我希望输出的名字一样,所以要改变静态库的名字
set_target_properties(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
set_target_properties(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
#cmake会在构建新的target时候,尝试清理其他使用这个名字的库,我们要回避这个问题,所以要修改参数
set_target_properties(hello PROPERTIES VERSION 1.2 SOVERSION 1)
#设置动态库版本
get_target_property(OUTPUT_VALUE hello_static OUTPUT_NAME)
#获取输出名字的内容
message(STATUS "this is the hello_static output_name:"${OUTPUT_VALUE})
install(TARGETS hello hello_static 
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib)
#安装静态库和动态库
install(FILES hello.h DESTINATION include/hello)
#安装

主目录下的CMakeList.txt

project(HELLOLIB)
add_subdirectory(lib lib_HTF)
#如果要修改输出位置,可以在add_subdirectory 中设置或者在lib/CMakeList.txt中设置SET(LIBRARY_OUTPUT_PATH ...)中设置。

三,使用外部共享库和头文件

在src文件下写入源文件 main.c :

#include <hello.h>
int main(){
    HelloFunc();
    return 0;
}

编写src/CMakeLists.txt:

include_directories(/tmp/usr/test3/include/hello)
#添加包含目录
link_directories(/tmp/usr/test3/lib)
#添加库目录
add_executable(main hello.c)
#链接执行文件
target_link_libraries(main hello)
#添加执行文件下所需要的以来动态库

编写根目录下的CMakeList.txt

cmake_minimum_required(VERSION 3.5)
#设定cmake版本最小号
PROJECT(NEWHELLO)
EXEC_PROGRAM(ls ARGS "-l" OUTPUT_VARIABLE LS_OUTPUT RETUREN_VALUE LS_RVALUE)
#cmake中处理执行命令,args添加参数,output_variable为输出值,return_value为返回值
IF(NOT LS_RVALUE)
message(STATUS "ls result: "${LS_OUTPUT})
ENDIF(NOT LS_RVALUE)
aux_source_directory(. SRC_LIST)
#作用是发现一个目录下所有的源代码并将列表储存到一个变量中。
IF(NOT SRC_LIST)
message(STATUS "do not find any files")
ENDIF(NOT SRC_LIST)
foreach(F ${SRC_LIST})
    message(${F})
endforeach(F)
#foreach循环 ,使用方法:
#1,foreach(F  ${SRC_LIST})
#2,foreach(VAR RANGE 10)
#3, foreach (loop_var RANGE start stop [step])
add_subdirectory(src)

如果使用find的命令来查找,那么src/CMakeLists.txt 修改为:

#export CMAKE_INCLUDE_PATH=/tmp/usr/test3/include/hello
#export CMAKE_LIBRARY_PATH=/tmp/usr/test3/lib
这个两句都是外部命令,也可以用下面来指定
find_path(myHeader hello.h /tmp/usr/test3/include/hello)
#查找文件位置是否存在
IF(myHeader)
message(STATUS ${myHeader})
include_directories(${myHeader})
ENDIF(myHeader)

find_library(myLib hello /tmp/usr/test3/lib)
查找lib是否存在
IF(myLib)
message(STATUS ${myLib})
ELSEIF(NOT myLib)
MESSAGE(FAULT "libhello not found")
ENDIF(myLib)

add_executable(main hello.c)
target_link_libraries(main ${myLib})
#添加lib,这里应该是绝对路径了

四,cmake常用变量

1,引用方式 {} ,只有在IF情况下才直接用 2,常用变量 CMAKE_BINARY_DIR=PROJECT_BINARY_DIR=<projectname>_BINARY_DIR CMAKE_SOURCE_DIR=PROJECT_SOURCE_DIR=<projectname>_SOURCE_DIR CMAKE_CURRENT_SOURCE_DIR 当前处理的CMakeList所在路径 CMAKE_CURRENT_BINARY_DIR,out-of-source 中指的是target编译路径 3,cmake 调用环境变量ENV{NAME} 可以直接调用环境变量
message(STATUS "home dir: $ENV{HOME}")

五,自定义模块和模块使用

一般使用FIND_PACKAGE(XXX)来使用模块
每一个模块都有这几个变量:
1,_FOUND
2,_INCLUDE_DIR or _INCLUDES
3,_LIBRARY or _LIBRARIES

编写属于自己的FindHello 模块

建立cmake文件夹,编写cmake/FindHELLO.cmake 模块

FIND_PATH(HELLO_INCLUDE_DIR hello.h /tmp/usr/test3/include/hello)
FIND_LIBRARY(HELLO_LIBRARY hello /tmp/usr/test3/lib)
IF(HELLO_INCLUDE_DIR AND HELLO_LIBRARY)
    SET(HELLO_FOUND TRUE)
ELSE(HELLO_INCLUDE_DIR AND HELLO_LIBRARY)
    message(STATUS "include: " ${HELLO_INCLUDE_DIR})
    message(STATUS "lib: " ${HELLO_LIBRARY})
ENDIF(HELLO_INCLUDE_DIR AND HELLO_LIBRARY)
IF(HELLO_FOUND)
    IF(NOT HELLO_FIND_QUIETLY)
#REQUIRED 参数,就是确定共享库是否必须
        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")
    ELSE(HELLO_FIND_REQUIRED)
        message(STATUS "in find_hello.cmake,we do not find hello lib")
    ENDIF(HELLO_FIND_REQUIRED)
ENDIF(HELLO_FOUND)

定义src/CMakeLists.txt

set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
#注意确定cmake module的路径
FIND_PACKAGE(HELLO)
#这里可以改为 FIND_PACKAGE(HELLO QUIET) or FIND_PACKAGE(HELLO REQUIRED)
IF(HELLO_FOUND)
    ADD_EXECUTABLE(hello main.c)
    include_directories(${HELLO_INCLUDE_DIR})
    target_link_libraries(hello ${HELLO_LIBRARY})
    message(STATUS "include: " ${HELLO_INCLUDE_DIR})
    message(STATUS "lib: " ${HELLO_LIBRARY})
ELSEIF(HELLO_FOUND)
    message("do not find hello!")
ENDIF(HELLO_FOUND)

定义src/main.c

#include<hello.h>
int main(){
    HelloFunc();
    return 0;
}

主目录下的CMakeLists.txt

cmake_minimum_required(VERSION 3.5)
project(HELLO)
add_subdirectory(src)
`