公司引擎是用cmake根据目标平台来构建工程的,刚接触的时候深深体会到cmake的方便:如果目标平台是windows,它可以帮你自动构建出vs工程;如果是安卓,自动构建出eclipse工程,如果是IOS,自动构建出xcode工程。想想以前用vs建工程的时候,如果要引入第三方库,必须要手动配置第三方库路径,如果引入的库少,那还没什么,如果多的话就悲剧了,配个环境都要半天。再想想以前在linux平台下手动写Makefile的时候,如果工程比较小,模块少还好办,如果工程大,模块多,各种写依赖关系都要让你抓狂。有了cmake这个工具,我们完全可以靠它来帮我们构建vs工程,写Makefile文件。既然cmake构建工程这么方便,当然需要拿来用,可是对于我这个小白来说,怎么学呢?果断谷歌,百度,不过并没有找到比较有价值的学习资料,很多人都是贴出了cmake的源文件CMakeLists.txt,然后对文件中的每行都讲了下作用。看完这些,我依然不知道为什么要这么写,为什么这行要这样写,那行要那样写?后来才反应过来,cmake官网肯定有讲啊,虽然是英文的,虽然自己英文比较挫,没办法,谁叫没有其它资料呢(其实官网讲的才说最权威的,不要因为是英文就畏惧,看多了其实英文也没那么难,很多人自认为英文不行或者看英文吃力就去网上找各种中文资料,结果可能花费了大量时间在找资料上,到最后啥都没学到)。本文主要通过讲解cmake中一些比较简单的命令来构建自己的工程,作为初学者的入门。

1、两行命令帮你构建输出hello world的vs工程

为了自动构建工程,需要在源文件所在的最上层目录写一个CMakeLists.txt文件,它是cmake的源文件,也可以看作是cmake的脚本文件,这个文件描述了cmake怎样帮我们自动构建工程。现在我们有一个hello.cpp文件,需要用这个文件来构建一个vs工程,手动的方法就是打开vs,新建一个工程hello,然后把hello.cpp添加到hello工程里面。而有了cmake,只需要在CMakeLists.txt写两行命令,第一行给自己工程命个名hello,第二行hello工程需要的源文件hello.cpp。然后通过下面几个步骤,就可以生成一个vs工程了,生成其它工程的步骤相同,只是在选择目标工程的时候不同。

1.1 编写CMakeLists.txt文件和hello.cpp文件

CMakeLists.txt

project(hello)

add_executable(hello hello.cpp)

hello.cpp

#include <stdio.h>

int main (int argc, char *argv[])
{
printf("hello world!");
return ;
}

1.2 设置路径

1.3 设置目标工程为vs工程

1.4  产生vs工程

1.5 打开vs工程,编译运行程序

2 添加子模块

      对于比较大的工程来说,包含多个子模块是很常见的事,因为通常每个人只是负责他自己的模块。那么怎样将各个模块加入到主工程中呢?首先我们需要使用cmake来创建各个子模块的工程,然后再将这些模块加入到整个工程中。假设现在我们有一个子模块myhello,它提供了一个函数PrintHelloWorld来打印hello world!,主模块hello调用这个函数来打印。首先我们在hello.cpp所在目录创建myhello文件夹,将myhello.cpp和myhello.h放到里面,然后在这个文件夹中创建CMakeLists.txt。这三个文件的具体内容如下:

myhello/myhello.h:

void PrintHelloWorld();

myhello/myhello.cpp

#include <stdio.h>

void PrintHelloWorld()
{
printf("hello world!");
}

myhello/CMakeLists.txt

add_library(myhello myhello.cpp)

这个CMakeLists.txt主要是告诉cmake,为myhello创建一个库工程。

hello.cpp

#include "myhello/myhello.h"

int main (int argc, char *argv[])
{
PrintHelloWorld();
return ;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.5)

project(hello)

add_subdirectory(myhello)
set (EXTRA_LIBS ${EXTRA_LIBS} myhello) add_executable(hello hello.cpp) target_link_libraries (hello ${EXTRA_LIBS})

add_subdirectory将myhello子工程加入到主工程,target_link_libraries将子模myhello链接到hello中。然后重新cmake下,打开vs就可以编译运行啦。

3 添加可配置的头文件

cmake可以通过可配置的头文件来产生实际的头文件,如下面的可配置头文件hello.h.in,里面@@引用的变量可以通过CMakeLists.txt来设置,最后通过cmake来替换hello.h.in文件中的变量并生成hello.h内容。

hello.h.in

#define VERSION_MAJOR @VERSION_MAJOR@
#define VERSION_MINOR @VERSION_MINOR@

CMakeLists.txt

cmake_minimum_required(VERSION 3.5)

project(hello)

include_directories("${PROJECT_BINARY_DIR}")
set(VERSION_MAJOR 1)
set(VERSION_MINOR 0)
configure_file(
"${PROJECT_SOURCE_DIR}/hello.h.in"
"${PROJECT_BINARY_DIR}/hello.h"
) add_subdirectory(myhello)
set (EXTRA_LIBS ${EXTRA_LIBS} myhello) add_executable(hello hello.cpp) target_link_libraries (hello ${EXTRA_LIBS})

上面加红的命令主要用来设置hello.h.in中的两个变量,并且让cmake生成hello.h文件。生成的hello.h如下:

hello.h

#define VERSION_MAJOR 1
#define VERSION_MINOR 0

再修改下hello.cpp文件使用这两个变量,

hello.cpp

#include "myhello/myhello.h"
#include "hello.h"
#include <stdio.h> int main (int argc, char *argv[])
{
printf("version:%d.%d\n", VERSION_MAJOR, VERSION_MINOR);
PrintHelloWorld();
return ;
}

打开vs工程,编译运行输出者两个变量的值。这样就可以通过在CMakeLists.txt中设置变量的内容来动态修改.h文件,增加了代码的灵活性。

4 检测系统是否有支持工程需要的函数

对于跨平台的工程来说,检查系统是否支持某些特性是很有必要的,这样程序中就可以通过系统的特性来选择具体执行哪些代码。其中检查是否支持某些函数是我们经常要做的事情,如epoll函数,可能有的linux系统就不支持,对于不支持的系统我们只能用poll来替代等。在cmake中检查系统是否支持某个函数也很简单,先包含一个CheckFunctionExists库,然后使用check_function_exists来判断就行了。

CMakeLists.txt

cmake_minimum_required(VERSION 3.5)

project(hello)

include (CheckFunctionExists)
check_function_exists (printf HAVE_PRINTF) include_directories("${PROJECT_BINARY_DIR}")
set(VERSION_MAJOR )
set(VERSION_MINOR )
configure_file(
"${PROJECT_SOURCE_DIR}/hello.h.in"
"${PROJECT_BINARY_DIR}/hello.h"
) add_subdirectory(myhello)
set (EXTRA_LIBS ${EXTRA_LIBS} myhello) add_executable(hello hello.cpp) target_link_libraries (hello ${EXTRA_LIBS})

在配置的头文件hello.h.in中加入#cmakedefine HAVE_PRINTF,这样如果系统中有printf函数,最终生成的hello.h中会定义HAVE_PRINTF这个宏,否则不会生成这个宏,在hello.cpp文件中可以根据这个宏来是否定义来判断是否应该使用printf函数。

hello.h.in

#define VERSION_MAJOR @VERSION_MAJOR@
#define VERSION_MINOR @VERSION_MINOR@ #cmakedefine HAVE_PRINTF

hello.cpp

#include "myhello/myhello.h"
#include "hello.h"
#include <stdio.h> int main (int argc, char *argv[])
{
#ifdef HAVE_PRINTF
printf("version:%d.%d\n", VERSION_MAJOR, VERSION_MINOR);
#endif
PrintHelloWorld();
return ;
}

运行结果:

5 配置可选项

有时候代码可能包含了所有平台的模块代码,但是对于特定的目标平台,只需要配置该平台需要模块的代码,而不需要配置其它平台模块的代码。这种需求可以通过cmake的配置可选项来完成,配置可选项就是cmake在生成工程的时候提示你一些选项,根据你的选项来具体选择需要添加到工程中的模块代码。例如我现在需要提高是否使用myhello模块的选项,可以在CMakeLists.txt中加option命令来实现,代码如下:

cmake_minimum_required(VERSION 3.5)

project(hello)

include (CheckFunctionExists)
check_function_exists (printf HAVE_PRINTF) include_directories("${PROJECT_BINARY_DIR}")
set(VERSION_MAJOR )
set(VERSION_MINOR ) option (USE_MYHELLO
"Use myhello" ON) configure_file(
"${PROJECT_SOURCE_DIR}/hello.h.in"
"${PROJECT_BINARY_DIR}/hello.h"
) add_subdirectory(myhello)
set (EXTRA_LIBS ${EXTRA_LIBS} myhello) add_executable(hello hello.cpp) target_link_libraries (hello ${EXTRA_LIBS})

并且在hello.h.in中添加由cmake根据选项来定义USE_MYHELLO宏。

#define VERSION_MAJOR @VERSION_MAJOR@
#define VERSION_MINOR @VERSION_MINOR@ #cmakedefine HAVE_PRINTF
#cmakedefine USE_MYHELLO

这样在运行cmake的时候,会提示我们一些选项来进行选择:

通过USE_MYHELLO是否被选择,cmake来确定是否要在hello.h中定义USE_MYHELLO宏,最终我们可以在hello.cpp中判断USE_MYHELLO宏是否定义来是否使用myhello模块中的PrintHelloWorld函数。

hello.cpp

#include "myhello/myhello.h"
#include "hello.h"
#include <stdio.h> int main (int argc, char *argv[])
{
#ifdef HAVE_PRINTF
printf("version:%d.%d\n", VERSION_MAJOR, VERSION_MINOR);
#endif
#ifdef USE_MYHELLO
PrintHelloWorld();
#else
printf("xx hello world!");
#endif
return ;
}

最后通过选中或者不选中USE_MYHELLO选择,得到的结果会不同。

选中结果

没选中结果:

6 总结

本文主要介绍了下cmake的比较常用的一些命令:project、include、include_directories、set、option、configure_file、add_subdirectory、add_executable、target_link_libraries、add_library,算是一个入门吧。需要用好cmake,熟悉cmake的命令和多写cmake脚本是必须的,具体每个命令的介绍看以参考官方文档:https://cmake.org/cmake/help/v3.5/manual/cmake-commands.7.html,脚步的编写语法可以参考官网文档:https://cmake.org/cmake/help/v3.5/manual/cmake-language.7.html。以后大点的工程创建完全可以交给cmake来完成,同时也是熟悉cmake的过程。

参考:https://cmake.org/cmake/help/v3.5/index.html

使用cmake自动构建工程的更多相关文章

  1. 自动构建Makefile(1)--C/C++编译流程&Makefile规则简介

      前言: 大家在Windows上使用VS构建C/C++程序时,不需要自己编辑略显晦涩的Makefile文件,而对于初学者而言, 他们甚至没意识到它的存在.VS是自动生成Makefile文件, 并构建 ...

  2. (转)HelloWorld CMake CMake中构建静态库与动态库及其使用

    继续完善Hello World,建立它的共享库, 包括静态库和动态库. 本节的任务: 1,建立一个静态库和动态库,提供HelloFunc函数供其他程序编程使用,HelloFunc 向终端输出Hello ...

  3. Android Studio添加原生库并自动构建

    [时间:2017-09] [状态:Open] [关键词:Android,Android Studio,gradle,native,c,c++,cmake,原生开发,ndk-build] 0 引言 最近 ...

  4. xmake v2.2.2, 让C/C++拥有包依赖自动构建

    前言 历经四个多月,xmake终于更新了新版本v2.2.2,并且上线了重量级功能:原生支持的远程依赖包管理. 而这个特性,其实我陆陆续续写了将近一年的时间,才初步完成,对于此特性的开发进展和历史,有兴 ...

  5. 跨平台自动构建工具v1.0.2 发布

    XMake是一个跨平台自动构建工具,支持在各种主流平台上构建项目,类似cmake.automake.premake,但是更加的方便易用,工程描述语法更简洁直观,支持平台更多,并且集创建.配置.编译.打 ...

  6. 用Ant实现Java项目的自动构建和部署

    原文地址:http://tech.it168.com/j/2007-11-09/200711091344781.shtml         本文请勿转载! Ant是一个Apache基金会下的跨平台的构 ...

  7. 利用ant脚本 自动构建svn增量/全量 系统程序升级包

    首先请允许我这样说,作为开发或测试,你一定要具备这种 本领.你可以手动打包.部署你的工程,但这不是最好的方法.最好的方式就是全自动化的方式.开发人员提交了代码后,可以自动构建.打包.部署到测试环境. ...

  8. Yeoman自动构建js项目

    Aug 19, 2013 Tags: bowergruntJavascriptjsnodejsyeomanyo Comments: 10 Comments Yeoman自动构建js项目 从零开始nod ...

  9. [系统集成] Android 自动构建系统

    一.简介 android app 自动构建服务器用于自动下载app代码.自动打包.发布,要建立这样的服务器,关键要解决以下几个问题: 1. android app 自动化打包android 的打包一般 ...

随机推荐

  1. 一起学微软Power BI系列-使用技巧(5)自定义PowerBI时间日期表

    1.日期函数表作用 经常使用Excel或者PowerBI,Power Pivot做报表,时间日期是一个重要的纬度,加上做一些钻取,时间日期函数表不可避免.所以今天就给大家分享一个自定义的做日期表的方法 ...

  2. WebAPi之SelfHost自创建证书启动Https疑难解惑及无法正确返回结果

    前言 话说又来需求了,之前对于在SelfHost中需要嵌套页面并操作为非正常需求,这回来正常需求了,客户端现在加了https,老大过来说WebAPi访问不了了,这是什么情况,我去试了试,还真是这个情况 ...

  3. 如何在ASP.Net创建各种3D图表

    我们都知道,图表在ASP.NET技术中是一种特别受欢迎而又很重要的工具.图表是表示数据的图形,一般含有X和Y两个坐标轴.我们可以用折线,柱状,块状来表示数据.通过图表控件,我们即能表示数据又能比较各种 ...

  4. 基于fis3的组件可视化道路

    首先说明一下,即使不熟悉fis3,阅读文本应该也会有所收获. 本文以fis-parser-imweb-tplv2插件为模板插件,目的不在于使用哪个模板,而是组件可视化的实现思路,不必担心. 先说说模板 ...

  5. ,net core mvc 文件上传

    工作用到文件上传的功能,在这个分享下 ~~ Controller: public class PictureController : Controller { private IHostingEnvi ...

  6. HTML 5 应用程序缓存manifest

    什么是应用程序缓存(Application Cache)? HTML5 引入了应用程序缓存,这意味着 web 应用可进行缓存,并可在没有因特网连接时进行访问. 应用程序缓存为应用带来三个优势: 离线浏 ...

  7. CentOS:ECDSA host key "ip地址" for has changed and you have requested strict checking(转)

    原文地址:http://blog.csdn.net/ausboyue/article/details/52775281 Linux SSH命令错误:ECDSA host key "ip地址& ...

  8. Prometheus 系统监控方案 一

    最近一直在折腾时序类型的数据库,经过一段时间项目应用,觉得十分不错.而Prometheus又是刚刚推出不久的开源方案,中文资料较少,所以打算写一系列应用的实践过程分享一下. Prometheus 是什 ...

  9. μCos-ii学习笔记1_概述

    一.μCos-ii _概述 网上关于μCosii的文章多不胜数,本人学习的过程中也参考了很多人的理解和想法,看的是卢有亮老师的<嵌入式实时操作系统-μC/OS原理与实践>(第2版),同时也 ...

  10. Spring Quartz实现任务调度

    任务调度 在企业级应用中,经常会制定一些"计划任务",即在某个时间点做某件事情 核心是以时间为关注点,即在一个特定的时间点,系统执行指定的一个操作 任务调度涉及多线程并发.线程池维 ...