【原】Cmake新手使用日记(1)【C++11下的初体验】
第一次使用Cmake,搜索了很多使用教程,包括《Cmake实践》、《Cmake手册》等,但是在针对最新的C++11条件下编程还是会存在一点点问题,需要实验很多次错误并搜索大量文章才能解决问题。这里用新手日记的方式告诉一个Cmake小白如何在C++11下使用Cmake。
一般情况下,直接阅读文档会不太适应新手,没有感官的认识,没有经验。这里将自己的学习过程记录下来,方便自己查阅,也方便其他新手学习。
首先先声明一下Cmake脚本,其实就是CMakeLists.txt的基本语法规则:
- 变量使用${}方式取值,但是在 IF 控制语句中是直接使用变量名
- 指令(参数 1 参数 2...) 参数使用括弧括起,参数之间使用空格或分号分开。
- 指令是大小写无关的,参数和变量是大小写相关的。但,推荐你全部使用大写指令。
一、单文件编译
(1)操作过程
<1>创建一个目录t1.
<2>编写代码:
main.cpp
1 #include <iostream> 2 #include <vector> 3 int main() 4 { 5 6 using namespace std; 7 vector<int> V(5,3); 8 for(auto e:V) 9 cout << e << endl; 10 cout << "OK" << endl; 11 return 0; 12 13 }
<3>编写CMakeLists.txt
【注意】不要忘记“CMakeLists.txt”中“.”前还有一个“s”,如果你很粗心,估计会在这里抓耳挠腮。
PROJECT (HELLO)
SET(CMAKE_C_COMPILER g++) if(CMAKE_COMPILER_IS_GNUCXX) add_compile_options(-std=c++11)
message(STATUS "optional:-std=c++11") endif(CMAKE_COMPILER_IS_GNUCXX)
SET(SRC_LIST main.cpp) ADD_EXECUTABLE(hello ${SRC_LIST})
<4>在t1文件夹下编译
先运行,注意Cmake后面的小点点“.”表示当前目录。
Cmake .
再运行
make
<5>运行程序
此时,再t1文件夹下会产生hello文件,运行
./hello
程序就正确运行啦。
输出
@ubuntu:~/t1$ ./hello 3 3 3 3 3 OK
(2)过程解释
首先,在CMakeLists.txt中,命令可以使用大写、小写、混写。
PROJECT (HELLO)是CMakeLists.txt的第一句,告诉编译器这个工程名叫hello。
SET(CMAKE_C_COMPILER g++)声明使用g++编译器,因为如果是.c文件的话通常使用默认的gcc编译器。
add_compile_options(-std=c++11)告诉编译器使用的是c++11,但是如果不设置g++编译器,又不判断一下编译器的话会出现下面的运行结果:
@ubuntu:~/t1$ make Scanning dependencies of target hello [ 50%] Building CXX object CMakeFiles/hello.dir/main.o c++: error: unrecognized command line option ‘-std=C++11’ CMakeFiles/hello.dir/build.make:62: recipe for target 'CMakeFiles/hello.dir/main.o' failed make[2]: *** [CMakeFiles/hello.dir/main.o] Error 1 CMakeFiles/Makefile2:67: recipe for target 'CMakeFiles/hello.dir/all' failed make[1]: *** [CMakeFiles/hello.dir/all] Error 2 Makefile:83: recipe for target 'all' failed make: *** [all] Error 2
有的博客里说能够编译完成,但是在这里有时候就完不成。当然有时候也能完成,这个可能和具体的系统有关吧。
当出现上述问题时,可以使用设置编译器的方式解决,也可以使用if语句进行判断,这里为了能够将所有情况都讲清楚,就暂时多此一举这个写了。其中CMAKE_COMPILER_IS_GNUCXX用于判断编译器类型。
但是作者也碰到这样的问题,如果不设置编译器为g++,第一次设置编译器为std=c++11属性时报错,但是一旦设置完成功运行之后,即使吧编译器设置的语句去掉,把t1下面除了main.cpp和CMakeLists.txt之外的语句去掉,也能正常编译。真的有些奇怪。
message(STATUS "optional:-std=c++11")可以用于打印一些提示信息,这里在运行完CMake .之后,就把“”中的文字打印出来了。实际上, 除了用STATUS打印普通标识,message还有一些其他的打印功能,如用SEND_ERROR,产生错误信息,生成过程被跳过。如果使用 FATAL_ERROR,立即终止所有 cmake 过程。
接着SET(SRC_LIST main.cpp) 和ADD_EXECUTABLE(hello ${SRC_LIST}) 语句。定义了这个工程会生成一个文件名为 hello 的可执行文件,相关的源文件是 SRC_LIST 中 定义的源文件列表, 本例中也可以直接写成 ADD_EXECUTABLE(hello main.c)。
${}来引用变量,这是 cmake 的变量应用方式,但是,有一些例外,比 如在 IF 控制语句,变量是直接使用变量名引用(如这里的CMAKE_COMPILER_IS_GNUCXX),而不需要${}。如果使用了${}去应用变量,其实 IF 会去判断名为${}所代表的值的变量,那当然是不存在的了。
(3)一点注意
在CMakeLists.txt脚本中,设置编译选项可以通过add_compile_options
命令,也可以通过set命令修改CMAKE_CXX_FLAGS
或CMAKE_C_FLAGS
。
使用这两种方式在有的情况下效果是一样的,但请注意它们还是有区别的: add_compile_options
命令添加的编译选项是针对所有编译器的(包括c和c++编译器),而set命令设置CMAKE_C_FLAGS
或CMAKE_CXX_FLAGS
变量则是分别只针对c和c++编译器的。因此,下面的代码也是一样的效果哦!
PROJECT (HELLO) SET(CMAKE_C_COMPILER g++) if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}") endif(CMAKE_COMPILER_IS_GNUCXX) SET(SRC_LIST main.cpp) ADD_EXECUTABLE(hello ${SRC_LIST})