【编译工具】CMake【开篇】

【参考】

http://blog.csdn.net/dbzhang800/article/details/6314073

http://www.cnblogs.com/lidabo/p/3976947.html

http://www.cnblogs.com/hzhida/archive/2012/08/06/2624998.html//ubuntu下设置环境变量


windows里有visual studio 这个强大的IDE环境来帮助我们建立工程、编译程序,可是到了ubuntu下就犯了难,好在有cmake这个工具来帮忙。CMakeLists.txt只用来生成makefile

【00】CMake简介

        CMake说白了就是一个为CMakeLists.txt的工程脚本配置文件。CMake可以针对不同操作系统和IDE环境生成不同的脚本或工程文件,如VS解决方案、Mac OSX的XCode文件、Unix/Linux系统的Makefile文件等等。试图直接使用子目录的CMakeList.txt是无效的,因为系统找不到许多在根目录的CMakeLists文件中配置的参数和宏,因而会产生错误提示,无法继续执行。

总之,如果工程用CMake来配置,然后一个命令就可以整齐地输出链接库和可执行文件。

以下介绍几个关于CMake的基本概念:

  • CMakelist.txt
CMake脚本代码和配置参数的载体,源代码目录中没有它的话,工程就不可能使用CMake的配置程序来完成自动Makefile脚本的生成工作
  • 源码树和二进制树
源码树(Source Tree)和二进制树(Binary Tree)含义很好理解:
Source Tree表达了一个工程的所有 头文件(.h,.hxx,.hh,无扩展名等等)、 源文件(.c,.cc,.cpp,.cxx,等等)、 CMake脚本(CMakeLists.txt),以及它们目录树结构;
Binary Tree则包括平台相关的解决方案或Makefile脚本,目标文件(.obj),编译后的动态/静态库和可执行文件,以及其它编译过程中生成的文件等。
CMake允许“源码树内生成”(in-source build)和“源码树外生成”(out-of-source build)这两种方式。前者将会在源代码的同一目录下生成对应的Makefile脚本,目标文件以及结果;后者则是在不同的目录下执行编译生成工作,源代码树则保持原样,十分有利于代码的版本更新,搜索管理以及打包再发布
对于windows用户,可在CMake-GUI的"where to build binaries"栏中输入新的工作路径以实现out-of-source的模式;Linux用户则可简单地在外部目录执行cmake指令,例如:cmake /home/myproject -DCMAKE_BUILD_TYPE=Release
这里的/home/myproject即是工程的根目录,其中必须包含有CMakeLists.txt文件
  • CMakeModules模块
一个工程需要依赖于另一个工程的头文件和库文件(.lib),无论对商业还是开源软件的开发流程来说这都是不可或缺的一部分:GUI开发库Qt的部分功能依赖于libJPEG、libPNG;商业GIS引擎Skyline依赖于GDAL;就连微软的一些大型游戏都会依赖于开源工程OpenAL。
那么,如何告诉我们的工程,这些头文件和LIB文件的位置呢?VS中在工程属性的“C/C++” 和“连接器”选项卡中可分别设置它们的路径;而Linux编程时,则需要在脚本中手动添加 -I以及 -L-I参数,保证#include宏不致无所适从,以及编译器不会产生该死的LINK2001、LINK2019错误。而对于使用CMake生成自动脚本的开发者来说,寻找头文件和库文件的工作就交给CMakeModules中的各个模块来完成。
因此,可以在目录文件中,建立一个CMakeModules文件夹,里面存放自动获取依赖库的路径信息(并不会主动将它们追加到工程属性中),不然,智能设置FREETYPE_INCLUDE_DIR,GDAL_LIBRARY等选项。这些自动搜索脚本的扩展名为.cmake
  • CMake基本宏
前文描述linux下CMake命令行时,有一个未做介绍的宏参数:-DCMAKE_BUILD_TYPE=Release。它可被简单地分给为三个部分:-D是命令前缀词,CMAKE_BUILD_TYPE是宏命令的关键字,而Release则是对其赋值。这个内置宏标志代表了设置了工程即将采用的编译类型,可使用的值通常包括Debug、Release、RelWithDebInfo和MinSizeRel四种。和VS等工具中所做设置类似,这将改变工程的调试等级和编译生成的信息等诸多内容。
除了CMAKE_BUILD_TYPE之外,CMake中还包含了一些基本的内置宏指令,典型的如:
CMAKE_MODULE_PATH,设置搜索CMakeModules模块(.cmake)的额外路径。
CMAKE_INCLUDE_PATH,设置自动查找依赖工程头文件的额外路径,默认为脚本中指定的搜索路径
CMAKE_LIBRARY_PATH,设置自动查找依赖工程库文件的额外路径,默认为脚本中指定的搜索路径
CMAKE_INSTALL_PREFIX,设置安装时的路径。这是一个重要的配置参数,当链接库和可执行文件的生成工作完毕时,往往需要将这些.lib,.dll,.exe和头文件拷贝到一个独立的文件夹下,以备调用和再次复制。在VS环境下通过INSTALL工程来完成这一安装工作,而Unix/Linux下则是熟悉的make install。默认安装目录为/usr/local或者C:/Program Files/。
  • CMake缓存信息
当我们使用CMake生成了工程的解决方案或Makefile脚本之后,再进入二进制树目录,也就是编译过程文件存储的位置(in-source模式为源代码同一目录,out-of-source模式为用户自行指定的文件夹),可看到新生成CMakeCache.txt文件,即缓存信息文件。
CMakeCache.txt中存储了所有自动搜索或者手动配置的路径和脚本参数。当更新了工程的源代码并准备重新进行编译时,使用这种缓存信息文件可有效地加速CMake配置过程,方法是直接将这个文件拖动到CMake-GUI窗口中,或在命令行方式下执行:cmake -C CMakeCache.txt
此时,系统将自动读入上一次配置的所有信息,使用CMake-GUI的用户可在对话框中再次进行参数的修改,并生成新的解决方案或者Makefile文件,以供下一步的工程编译生成工作。

【01】CMake安装

        到CMake主页(https://cmake.org/)下载对应系统的cmake版本。

        主页上,CMake版本有两种:一种需要在本地进行make,一种不需要make

  • cmake-3.8.0-rc1-linux-x86_64

 1)这个版本已经下载编译好了,只需解压到需要的目录即可使用:tar xzvf cmake-3.8.0-rc1-Linux-x86_64.tar.gz -C /usr/local

2)设置环境变量:sudo gedit ~/.bashrc,在打开文件后添加:export PATH=$PATH:/usr/local/cmake-3.8.0-rc1-Linux-x86_64/bin 

在 Ubuntu 系统中有两种设置环境变量 PATH 的方法。第一种适用于为单一用户设置 PATH,第二种是为全局设置 PATH。 
第一种方法: 在用户主目录下有一个 .bashrc 文件,可以在此文件中加入 PATH 的设置如下: 
export PATH=”$PATH:/your path1/:/your path2/…..” 
注意:每一个 path 之间要用 “:“ 分隔。 注销重启 X 就可以了。 

第二种方法: 在 /etc/profile中增加。

 PATH="$PATH:/home/zhengb66/bin" 

export PATH 

环境变量时通过shell命令来设置,设置好的环境变量又可以被所有当前用户所运行的程序所使用。对于bash这个shell程序来说,可通过变量名来访问相应的 环境变量,通过export来设置环境变量

3)有的时候需要创建链接, ln -s /usr/local/cmake-3.8.0-rc1-Linux-x86_64/bin/* /usr/bin/

4)执行命令检查是否安装成功

cmake --version

cmake version 3.8.0

   注:在嵌入式平面可能不能用这种方法来安装,因为按照上述方法装时可能会提示:cannot execute binary file: Exec format error,只能下载另外一个版本,在本地make

  •   cmake-3.8.0-rc1-tar.gz:

1)将文件解压tar xzvf cmake-3.8.0-r1-tar.gz

2)   进入文件夹中

  ./bootstrap

  make

  make install//如果显示没有权限,则用sudo make install

3)检查是否安装成功cmake --version


【02】CMake实例

以下实例都是在ubuntu下编译

  1. 简单地例子 --单个cpp文件工程
目标:在本地新建一个CMakeListsSample文件夹,编写一个.cpp文件,用CMakeLists.txt管理,实现编译
实现:用out-of-source构建
CMakeLIstsSample __main.cpp
|__CMakeLists.txt
|__+build文件夹
//main.cpp
#include <iostream>
int main()
{
std::cout << "Hello World!\n" << std::endl;
return 0;
}

//CMakeLists.txt
project(HELLO)
set(SRC_LIST main.cpp)
add_executable(hello ${SRC_LIST})
在build文件夹执行
cmake ..
make

总结:
CMakeLists.txt,
首先,CMakeLists.txt中,名称有s
齐次,CMakeLists.txt中命名名字是不区分大小写,而参数和变量时大小写相关的
第三,第一行project不是强制性的,但最好都始终加上,加后,第一行会引入两个变量:HELLO_BINARY_DIR和HELLO_SOURCE_DIR,同时cmake自动定义了两个等价变量PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR,因为是out-of-source 方式构建,故要时刻区分这两个变量对应的目录。可通过message来输出变量值
message(${PROJECT_SOURCE_DIR})
第四,set命令用来设置变量
第五,add_executable告诉工程生成一个可执行文件,add_library则告诉生成一个库文件

cmake命令后跟一个路径(..),用来指出CMakeLists.txt所在位置;
由于系统中可能有多套构建环境,可通过-G来指定生成那种工程文件,如cmake .. -G"NMake Makefiles"、cmake .. -G"MinGW Makefile",再通过cmake -h可得到详细信息;
要显示执行构建过程中详细信息(比如为了得到更详细的出错信息),可在CMakeLists.txt内加入:
SET(CMAKE_VERBOSE_MAKEFILE on)
或执行make时, make VERBOSE=1
export VERBOSE=1
make
2.稍微复杂点的--多个.cpp文件和头文件
//hello.h 头文件
#ifndef DB_HELLO_
#define DB_HELLO_
void hello(const char* name);
#endif
//hello.cpp
#include <iostream>
void hello(const char* name)
{
std::cout << name << std::endl;
}

//main.cpp
#include <iostream>
int main()
{
hello("Hello_World!\n");
return 0;
}

//CMakeLists.txt文件
project(HELLO)
set(SRC_LIST main.cpp hello.cpp)
add_executable(hello ${SRC_LIST})

3.接着上面,将hello.cpp生成一个库,然后再使用
只用修改CMakeLists.txt文件:
project(HELLO)
set(LIB_SRC hello.cpp)
set(APP_SRC main.cpp)
add_library(libhello ${LIB_SRC})
add_executable(hello ${APP_SRC})
target_link_libraries(hello libhello)
想要生成hello.lib(或hello.a)文件,直接添加一行
set_target_properties(libhello PROPERTIES OUTPUT_NAME "hello")

4.生成的库与源代码各方不同的路径下
+
|
+--- CMakeList.txt
+--+ src/
|  |
|  +--- main.cpp
|  /--- CMakeList.txt
|
+--+ libhello/
|  |
|  +--- hello.h
|  +--- hello.cpp
|  /--- CMakeList.txt
|
/--+ build/
	这需要3个CMakeLists.txt文件,每个源文件目录都需要一个。
  
  
  • 顶层CMakeLists.txt文件
project(HELLO)
add_subdirectory(src)
add_subdirectory(libhello)
  • src中的CMakeLists.txt文件
include_directories(${PROJECT_SOURCE_DIR}/libhello)
set(APP_SRC main.cpp)
add_executable(hello ${APP_SRC})
target_link_libraries(hello libhello)
  • libhello中的CMakeLists.txt文件
set(LIB_SRC hello.cpp)
add_library(libhello ${LIB_SRC})
set_target_properties(libhello PROPERTIES OUTPUT_NAME "hello")
编写完后,在顶层文件夹内新建一个build文件夹,
cmake ..
make
5.在之前的基础上增加:让编译出的可执行文件在bin目录下,库文件在lib目录下:
   + build/
   |
   +--+ bin/
   |  |
   |  /--- hello.exe
   |
   /--+ lib/
      |
      /--- hello.lib
  • 一种方法:修改顶级的CMakeLists.txt文件
project(HELLO)
add_subdirectory(src bin)
add_subdirectory(libhello lib)
这样build中的目录默认和源代码中结构一样,也可指定其对应目录在build中的名字,当然还有其他中间产物。
  • 另外一种方法:不修改顶级文件,修改其他两个文件。 使用该种方法不会有其他中间产物
src/CMakeLists.txt文件:
include_directories(${PROJECT_SOURCE_DIR}/libhello)
#link_directories(${PROJECT_BINARY_DIR}/lib)
set(APP_SRC main.cpp)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
add_executable(hello ${APP_SRC})
target_link_libraries(hello libhello)
libhello/CMakeLists.txt文件:
set(LIB_SRC hello.cpp)
add_library(libhello ${LIB_SRC})
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
set_target_properties(libhello PROPERTIES OUTPUT_NAME "hello")

6.上面生成的都是静态库,下面实现动态库生成。 .a文件为静态库,.so为动态链接库
如果不考虑windows下,只需在上面例子libhello/CMakeLists.txt文件中的add_library命令加入一个SHARED参数:
add_library(libhello SHARED ${LIB_SRC})
使用CMake的特性就是能兼顾不同平台,因此做如下修改:
  • 修改hello.h文件
#ifndef DB_HELLO_
#define DB_HELLO_
#if defined _WIN32
#if LIBHELLO_BUILD
#define LIBHELLO_API_declspec(dllexport)
#else
#define LIBHELLO_API_declspec(dllimport)
#endif
#else
#define LIBHELLO_API
#endif
LIBHELLO_API void hello(const char* name);
#endif
  • 修改libhello/CMakeList.txt文件
set(LIB_SRC hello.cpp)
add_definitions("-DLIBHELLO_BUILD")
add_library(libhello SHARED ${LIB_SRC})
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
set_target_properties(libhello PROPERTIES OUTPUT_NAME "hello")

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值