CMake从零开始—新手入门(一)

目录

一:说明

(1)make与makefile

(2)CMake介绍

(3)QMake介绍

(4)QMake和CMake有什么区别?

二:CMake基础指令学习

(1)CMake一个hello,world!

1、写一个hello,world!

2、写CMakeList.txt

3、使⽤cmake,⽣成makefile⽂件

4、使⽤make命令编译

5、最终生成了可执行文件hello

(2)CMake⼀个HelloWord-的基础语法介绍

1、PROJECT关键字

2、MESSAGE关键字

3、SET关键字

4、ADD_EXECUTABLE关键字 

5、语法的基本原则与注意事项

三、内部构建与外部构建

外部构建方式举例

(1)让hello,world!看起来更像一个工程

1、工程CMakeLists.txt

2、src下的CMakeLists.txt

3、ADD_SUBDIRECTORY指令

4、更改二进制的保存路径

(2)安装Hello,world!

1、安装文件COPYRIGHT和README

2、安装脚本runhello.sh

3、安装 doc 中的 hello.txt

4、安装过程(在build路径中)

5、最后的项目CMakeLists.txt内容

四、静态库与动态库的构建

(1)任务

(2)构建实例

1、hello.h内容

2、hello.cpp内容

3、项目中CMakeLists.txt的内容

4、lib下的 CMakeLists.txt内容

5、ADD_LIBRARY指令

(3)同时构建静态库和动态库

1、SET_TARGET_PROPERTIES指令

2、动态库的版本号

3、安装共享库和头文件

4、最后lib下的CMakeLists.txt内容

(4)使⽤外部共享库和头⽂件

1、准备⼯作,新建⼀个⽬录来使⽤外部共享库和头⽂件

2、main.cpp内容

3、解决:make后头⽂件找不到的问题

4、解决:找到引⽤的函数问题

5、查看main的链接情况

6、链接静态库

五:环境变量设置


一:说明

(1)make与makefile

        gcc是GNU Compiler Collection(GNU编译器套件),也可以认为是编译器,它可以编译很多种编程语言(括C、C++、Objective-C、Fortran、Java等等)。当你的程序只有一个源文件时,直接就可以用gcc命令编译它。但是当你的程序包含很多个源文件时,用gcc命令逐个去编译时,你就很容易混乱而且工作量大。所以出现了make工具。

        make工具可以看成是一个智能的批处理工具,它本身并没有编译和链接的功能,而是用类似于批处理的方式—通过调用makefile文件中用户指定的命令来进行编译和链接的。

        makefile是什么?简单的说就像一首歌的乐谱,make工具就像指挥家,指挥家根据乐谱指挥整个乐团怎么样演奏,make工具就根据makefile中的命令进行编译和链接的。makefile命令中就包含了调用gcc去编译某个源文件的命令。

        makefile在一些简单的工程完全可以人工手下,但是当工程非常大的时候,手写makefile也是非常麻烦的,如果换了个平台makefile又要重新修改。

(2)CMake介绍

        cmake的定义是什么 ?-----⾼级编译配置⼯具

        当多个⼈⽤不同的语⾔或者编译器开发⼀个项⽬,最终要输出⼀个可执⾏⽂件或者共享库(dll,so等等)这时候神器就出现了-----CMake!所有操作都是通过编译CMakeLists.txt来完成的。

        cmake就可以更加简单的生成makeifle文件给上面那个make用。当然cmake还有其他功能,就是可以跨平台生成对应平台能用的makefile,你就不用自己去修改了。

        cmake根据一个叫CMakeLists.txt文件(学名:组态档)去生成makefile。最后CMakeLists.txt文件谁写啊?是工程创建者亲自手写的。当然如果你用IDE,类似VS这些一般它都能帮你弄好了。

(3)QMake介绍

        qmake是什么,先说一下Qt。Qt是跨平台C++图形用户界面应用程序开发框架。它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器。简单的说就是C++的第三方库,使用这个库你可以很容易生成windows,Linux,MAC os等等平台的图形界面。现在的Qt还包含了开发各种软件一般需要用到的功能模块(网络,数据库,XML,多线程啊等等),比你直接用C++要方便和简单。

        你可以用Qt简简单单就实现非常复杂的功能,是因为Qt对C++进行了扩展,你写一行代码,Qt在背后帮你写了几百上千行,而这些多出来的代码就是靠Qt专有的moc编译器(The Meta-Object Compiler)和uic编译器(User Interface Complier)来翻译你那一行代码。

        你在进行编译前要先调用moc和uic对Qt源文件进行预处理,然后再调用编译器进行编译。普通makefile文件是不适用的,它没办法对qt源文件进行预处理。所以qmake就产生了。

        qmake工具就是Qt公司制造出来,用来生成Qt 专用makefile文件,这种makefile文件就能自动智能调用moc和uic对源程序进行预处理和编译。qmake当然也是跨平台的,跟cmake一样能对应各种平台生成对应makefile文件。qmake是根据Qt 工程文件(.pro)来生成对应的makefile的。工程文件(.pro)相对来说比较简单,一般工程你都可以自己手写,但是一般都是由Qt的开发环境 Qt Creator自动生成的。

        由于qmake很简单很好用又支持跨平台,而且是可以独立于它的IDE,所以你也可以用在非Qt工程上面,照样可以生成普通的makefile,只要在pro文件中加入CONFIG -= qt 就可以了。

(4)QMake和CMake有什么区别?

        1、cmake也是同样支持Qt程序的,cmake也能生成针对qt 程序的那种特殊makefile,只是cmake的CMakeLists.txt 写起来相对与qmake的pro文件复杂点。

        2、qmake 是为 Qt 量身打造的,使用起来非常方便,但是cmake功能比qmake强大。

        一般的Qt工程你就直接使用qmake就可以了,cmake的强大功能一般人是用不到的。当你的工程非常大的时候,又有qt部分的子工程,又有其他语言的部分子工程,用cmake会非常方便。

二:CMake基础指令学习

sudo apt-get install cmake

(1)CMake一个hello,world!

1、写一个hello,world!

#include<iostream>
using namespace std;

int main(){
    cout<<"hello,world!"<<endl;
    return 0;
}

2、写CMakeList.txt

#严格遵行大小写
vim CMakeLists.txt

#CMakeLists.txt


PROJECT (HELLO)


SET(SRC_LIST main.cpp)


MESSAGE(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR})
MESSAGE(STATUS "This is SOURCE dir "${HELLO_SOURCE_DIR})


ADD_EXECUTABLE(hello ${SRC_LIST})

#指令可小写

3、使⽤cmake,⽣成makefile⽂件

4、使⽤make命令编译

5、最终生成了可执行文件hello

(2)CMake⼀个HelloWord-的基础语法介绍

1、PROJECT关键字

可以⽤来指定⼯程的名字和⽀持的语⾔,默认⽀持所有语⾔
PROJECT (HELLO) 指定了⼯程的名字,并且⽀持所有语⾔
PROJECT (HELLO CXX) 指定了⼯程的名字,并且⽀持语⾔是C++
PROJECT (HELLO C CXX) 指定了⼯程的名字,并且⽀持语⾔是C和C++

2、MESSAGE关键字

向终端输出⽤户⾃定义的信息
主要包含三种信息:
SEND_ERROR,产⽣错误,⽣成过程被跳过。
SATUS,输出前缀为—的信息。
FATAL_ERROR,⽴即终⽌所有 cmake 过程.

该CMakeLists.txt定义了两个CMAKE的变量:

_BINARY_DIR,本例中是 HELLO_BINARY_DIR
_SOURCE_DIR,本例中是 HELLO_SOURCE_DIR

        就可以直接使⽤者两个变量,当前都指向当前的⼯作⽬录,后⾯会讲外部编译。
问题:如果改了⼯程名,这两个变量名也会改变。 

This warning is for project developers. Use -Wno-dev to suppress it.
-- The C compiler identification is GNU 10.2.1
-- The CXX compiler identification is GNU 10.2.1
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- This is BINARY dir /root/cmake
-- This is SOURCE dir /root/cmake
-- Configuring done
-- Generating done
-- Build files have been written to: /root/cmake

        解决:⼜定义两个预定义变量:PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR,这两个变量和HELLO_BINARY_DIR,HELLO_SOURCE_DIR是⼀致的。所以改了⼯程名也没有关系。

3、SET关键字

⽤来显示的指定变量的:
SET(SRC_LIST main.cpp) SRC_LIST变量就包含了main.cpp
也可以 SET(SRC_LIST main.cpp e1.cpp e2.cpp)

4、ADD_EXECUTABLE关键字 

⽣成可执⾏⽂件:
ADD_EXECUTABLE(hello ${SRC_LIST}) ⽣成的可执⾏⽂件名是hello,源⽂件读取变量SRC_LIST中的内容。
也可以直接写 ADD_EXECUTABLE(hello main.cpp)


上述例⼦可以简化的写成:
PROJECT(HELLO) ADD_EXECUTABLE(hello main.cpp)


注意:⼯程名的 HELLO 和⽣成的可执⾏⽂件 hello 是没有任何关系的

5、语法的基本原则与注意事项

        1、变量使⽤${}⽅式取值,但是在 if 控制语句中是直接使⽤变量名。
        2、指令(参数 1 参数 2...) 参数使⽤括弧括起,参数之间使⽤空格或分号分开。

        以上⾯的 ADD_EXECUTABLE 指令为例,如果存在另外⼀个 func.cpp 源⽂件就要写成:ADD_EXECUTABLE(hello main.cpp func.cpp)或者ADD_EXECUTABLE(hello main.cpp;func.cpp)

        3、指令是⼤⼩写⽆关的,参数和变量是⼤⼩写相关的。但推荐你全部使⽤⼤写指令
        4、SET(SRC_LIST main.cpp) 可以写成 SET(SRC_LIST “main.cpp”),如果源⽂件名中含有空格,就必须要加双引号
        5、ADD_EXECUTABLE(hello main) 后缀可以不⾏,他会⾃动去找.c和.cpp,最好不要这样写,可能会有这两个⽂件main.cpp和main。

三、内部构建与外部构建

        上述例⼦就是内部构建,他⽣产的临时⽂件特别多,不⽅便清理。外部构建,就会把⽣成的临时⽂件放在build⽬录下,不会对源⽂件有任何影响强烈使⽤外部构建⽅式。

外部构建方式举例

1、建⽴⼀个build⽬录,可以在任何地⽅,建议在当前⽬录下。
2、进⼊build,运⾏cmake .. 当然..表示上⼀级⽬录,你可以写CMakeLists.txt所在的绝对路径,⽣产的⽂件都在build⽬录下了。
3、在build⽬录下,运⾏make来构建⼯程。注意外部构建的两个变量。
1、HELLO_SOURCE_DIR 还是⼯程路径。
2、HELLO_BINARY_DIR 编译路径也就是 /root/cmake/bulid。

(1)让hello,world!看起来更像一个工程

1、为⼯程添加⼀个⼦⽬录 src,⽤来放置⼯程源代码。
2、添加⼀个⼦⽬录 doc,⽤来放置这个⼯程的⽂档 hello.txt。
3、在⼯程⽬录添加⽂本⽂件 COPYRIGHT, README。
4、在⼯程⽬录添加⼀个 runhello.sh 脚本,⽤来调⽤ hello ⼆进制。
5、将构建后的⽬标⽂件放⼊构建⽬录的 bin ⼦⽬录。
6、将 doc ⽬录 的内容以及 COPYRIGHT/README 安装到/usr/share/doc/cmake/。

最简单的目录树结构:

1、工程CMakeLists.txt

PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)

2、src下的CMakeLists.txt

ADD_EXECUTABLE(hello main.cpp)

3、ADD_SUBDIRECTORY指令

ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
这个指令⽤于向当前⼯程添加存放源⽂件的⼦⽬录,并可以指定中间⼆进制和⽬标⼆进制存放的位置。
EXCLUDE_FROM_ALL函数是将写的⽬录从编译中排除,如程序中的example
ADD_SUBDIRECTORY(src bin)
将 src⼦⽬录加⼊⼯程并指定编译输出(包含编译中间结果)路径为bin ⽬录。
如果不进⾏ bin⽬录的指定,那么编译结果(包括中间结果)都将存放在build/src ⽬录。

4、更改二进制的保存路径

SET 指令重新定义 EXECUTABLE_OUTPUT_PATH 和 LIBRARY_OUTPUT_PATH 变量来指定最终的⽬标⼆进制的位置。
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) SET(LIBRARY_OUTPUT_PATH
${PROJECT_BINARY_DIR}/lib)
思考:加载哪个CMakeLists.txt当中,
哪⾥要改变⽬标存放路径,就在哪⾥加⼊上述的定义,所以应该在src下的CMakeLists.txt下写。

(2)安装Hello,world!

 ⼀种是从代码编译后直接 make install 安装
⼀种是打包时的指定 ⽬录安装。
简单的可以这样指定⽬录:make install DESTDIR=/tmp/test
稍微复杂⼀点可以这样指定⽬录:./configure –prefix=/usr

1、安装文件COPYRIGHT和README

INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/)
FILES:⽂件
DESTINATION:
1、写绝对路径.
2、可以写相对路径,相对路径实际路径是:${CMAKE_INSTALL_PREFIX}/<DESTINATION 定义的路径>.

CMAKE_INSTALL_PREFIX 默认是在 /usr/local/
cmake -DCMAKE_INSTALL_PREFIX=/usr 在cmake的时候指定CMAKE_INSTALL_PREFIX变量的路径.

2、安装脚本runhello.sh

PROGRAMS:⾮⽬标⽂件的可执⾏程序安装(⽐如脚本之类)
INSTALL(PROGRAMS runhello.sh DESTINATION bin)
说明:实际安装到的是 /usr/bin

3、安装 doc 中的 hello.txt

⼀、是通过在 doc ⽬录建⽴CMakeLists.txt ,通过install下的file
⼆、是直接在⼯程⽬录通过
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)
DIRECTORY 后⾯连接的是所在 Source ⽬录的相对路径
注意:abc 和 abc/有很⼤的区别
⽬录名不以/结尾:这个⽬录将被安装为⽬标路径下的
⽬录名以/结尾:将这个⽬录中的内容安装到⽬标路径

4、安装过程(在build路径中)

1、cmake ..
2、make
3、make install

5、最后的项目CMakeLists.txt内容

PROJECT(HELLO)

ADD SUBDIRECTORY(src bin)

INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake)

INSTALL(PROGRAMS runhello.sh DESTINATION bin)

INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)

四、静态库与动态库的构建

(1)任务

1、建⽴⼀个静态库和动态库,提供 HelloFunc 函数供其他程序编程使⽤,HelloFunc 向终端输出 Hello,World!字符串。
2、安装头⽂件与共享库。
静态库和动态库的区别:
静态库的扩展名⼀般为“.a”或“.lib”;动态库的扩展名⼀般为“.so”或“.dll”。
静态库在编译时会直接整合到⽬标程序中,编译成功的可执⾏⽂件可独⽴运⾏
动态库在编译时不会放到连接的⽬标程序中,即可执⾏⽂件⽆法单独运⾏。

(2)构建实例

目录树结构:

1、hello.h内容

#ifndef HELLO_H
#define Hello_H

void HelloFunc();

#endif

2、hello.cpp内容

#include "hello.h"
#include <iostream>
using namespace std;

void HelloFunc(){

 cout << "Hello World" << endl;

}

3、项目中CMakeLists.txt的内容

PROJECT(HELLO)
ADD_SUBDIRECTORY(lib bin)

4、lib下的 CMakeLists.txt内容

SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})

可以编译运行安装一下试试。在bin目录下应该会有libhello.so 

5、ADD_LIBRARY指令

ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
hello:就是正常的库名,⽣成的名字前⾯会加上lib,最终产⽣的⽂件是libhello.so

SHARED:动态库;STATIC:静态库
${LIBHELLO_SRC} :源⽂件

(3)同时构建静态库和动态库

// 如果⽤这种⽅式,只会构建⼀个动态库,不会构建出静态库,虽然静态库的后缀是.a
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})

// 修改静态库的名字,这样是可以的,但是我们往往希望他们的名字是相同的,只是后缀不同⽽已
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})

1、SET_TARGET_PROPERTIES指令

        这条指令可以⽤来设置输出的名称,对于动态库,还可以⽤来指定动态库版本和 API 版本
同时构建静态和动态库。

SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})

//对hello_static的重名为hello
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
//cmake 在构建⼀个新的target 时,会尝试清理掉其他使⽤这个名字的库,因为,在构建 libhello.so 时, 就会清理掉 libhello.a
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)


ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})


SET_TARGET_PROPERTIES(hello PROPERTIES OUTPUT_NAME "hello")


SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)

2、动态库的版本号

⼀般动态库都有⼀个版本号的关联

libhello.so.1.2
libhello.so ->libhello.so.1
libhello.so.1->libhello.so.1.2

CMakeLists.txt 插⼊如下:

SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1) 

VERSION 指代动态库版本,SOVERSION 指代 API 版本。

3、安装共享库和头文件

本例中我们将 hello 的共享库安装到/lib⽬录,
将 hello.h 安装到/include/hello ⽬录。

//⽂件放到该⽬录下
INSTALL(FILES hello.h DESTINATION include/hello)

INSTALL(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)

TARGETS 用于安装⼆进制,静态库,动态库
ARCHIVE 特指静态库,LIBRARY 特指动态库,RUNTIME 特指可执⾏⽬标⼆进制。 

注意:
安装的时候,指定⼀下路径,放到系统下

cmake -DCMAKE_INSTALL_PREFIX=/usr ..

4、最后lib下的CMakeLists.txt内容

SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})

SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")

SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)

ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})

SET_TARGET_PROPERTIES(hello PROPERTIES OUTPUT_NAME "hello")

SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)

(4)使⽤外部共享库和头⽂件

1、准备⼯作,新建⼀个⽬录来使⽤外部共享库和头⽂件

2、main.cpp内容

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

3、解决:make后头⽂件找不到的问题

PS:include <hello/hello.h> 这样include是可以,这么做的话,就没啥好讲的了
关键字:INCLUDE_DIRECTORIES 这条指令可以⽤来向⼯程添加多个特定的头⽂件搜索路径,路径之间⽤空格分割,在CMakeLists.txt中加⼊头⽂件搜索路径。


INCLUDE_DIRECTORIES(/usr/include/hello)

4、解决:找到引⽤的函数问题

报错信息:undefined reference to `HelloFunc()'
关键字:LINK_DIRECTORIES 添加⾮标准的共享库搜索路径
关键字:TARGET_LINK_LIBRARIES 添加需要链接的共享库
在CMakeLists.txt中插⼊链接共享库,主要要插在executable的后⾯
TARGET_LINK_LIBRARIES(main libhello.so)

5、查看main的链接情况

 linux-vdso.so.1 => (0x00007ffedfda4000)
 libhello.so => /lib64/libhello.so (0x00007f41c0d8f000)
 libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f41c0874000)
 libm.so.6 => /lib64/libm.so.6 (0x00007f41c0572000)
 libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f41c035c000)
 libc.so.6 => /lib64/libc.so.6 (0x00007f41bff8e000)
 /lib64/ld-linux-x86-64.so.2 (0x00007f41c0b7c000)

6、链接静态库

TARGET_LINK_LIBRARIES(main libhello.a)

五:环境变量设置

特殊的环境变量 CMAKE_INCLUDE_PATH 和 CMAKE_LIBRARY_PATH

注意:这两个是环境变量⽽不是 cmake 变量,可以在linux的bash中进⾏设置。
我们上⾯例⼦中使⽤了绝对路径INCLUDE_DIRECTORIES(/usr/include/hello)来指明include路径的位置,我们还可以使⽤另外⼀种⽅式,使⽤环境变量:

export CMAKE_INCLUDE_PATH=/usr/include/hello

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ITC小浪花、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值