makefile文件依赖关系复杂,并且跨平台不优秀,因此,使用cmake这种自动工具,
自己看的参考版本是:cmake 实践。 http://file.ncnynl.com/ros/CMake%20Practice.pdf
1)跨平台的意思是: 可以在linux/unix平台生成makefile,又可以在苹果平台生成xcode.在windows平台则可以生成MSVC的工程文件。
2)cmake需要编写的是CMakeLists.txt,注意,需要每个目录一个,使用的是cmake 语言和语法。
3)如果没有实践,那么就在这停止学习吧,因为读的再多,几天后也会忘记。
4)cmake当然需要安装,只是在linux中,好多linux版本都已经安装好了cmake软件。
5)第一个t1测试,抄,自己也将ADD_EXECUTABLE(hello ${SRC_LIST)中${}给剩下了。
出错原因以及解决方法汇总:https://blog.csdn.net/felaim/article/details/71511909
总不能一直根据readme.txt安装依赖库,然后mkdir build, cd build, cmake .., make, make install
6)首先CMakeLists.txt必须注意,CM大写,L大写,list是复数。因为这个文件名确实是 大小写相关的。但是CMakeLists.txt文件内部,则没有明确的大小写要求。
并且还有一点,就是如果工程存在多个目录,那么需要确保每个要管理的目录都存在一个CMakeLists.txt文件。
7)开始解释hello涉及的语法指令,
7.1)PROJECT(projectname [cxx] [c] [Jave])该命令,1、可以省略后面的语言支持列表,因为默认支持所有语言。2,定义了,隐藏的定义,定义了两个cmake变量variables.
这种,但是自己并没有在生成makefile成功后见到这两个DIR的name.....这两个隐藏的定义变量有什么用??而这两个默认的变量与系统通用的两个变量一模一样,为了防止用户修改了用户名,后面对于CMakeLists.txt做好多涉及名字的修改,那么使用PROJECT_BINARY_DIR 和 PROJECT_SOURCE_DIR变量即可。这两个变量是自动与会变化的projectname_BINARY_DIR 和projectname_SOURCE_DIR相互挂接的。
7.2)SET指令。SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
现阶段,我们只记住,这个SET用于显式地定义变量即可。例如:SET(SRC_LIST main.c t1.c t2.c)等,这种一串多个源文件的构成。
7.3)MASSAGE指令。
MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display" ...)
这个基本可以认为是:printf和sterr这种结合了,既能够按照设定级别报错也能够输出产生的信息。
其中[]并不是说敲命令要敲出来,而是说,方框中的是N选一而已。SEND_ERROR是产生错误,而错误会被跳过去。
STATUS是前缀加个- 表明输出信息内容。 自己打印出来貌似是两个横杠 - -
FATAL_ERROR这个表示立即停止所有的cmake过程。
中间的引号是文字,而后面的三个点是自己定义的变量啊之类的,
7.4) ADD_EXECUTABLE(hello ${SRC_LIST})
上述说明,首先前面的是生成的可执行文件名称就是最后./xxx这个执行的名字。然后后面是,生成这个名字需要用到的相关源文件列表。
7.5)cmake的基本语法:
变量引用方式:${}这种类型方式。而IF中控制语句使用的是直接放变量名字,因为判断的是这个变量名字,而不是变量内容,
指令(参数1 参数2 ...)这种
参数使用括弧括起来,参数之间使用空格或者分号分开。注意,是参数,不是变量。例如ADD_EXECUTABLE(hello main.c t1.c)变为:ADD_EXECUTABLE(hello main.c; t1.c)
上述,指令是大小写无关,但是推荐使用全部大写指令。而参数和变量是大小写相关的。
cmake很灵活的一个语法:SET(SRC_LIST main.c)以及SET(SRC_LIST "main.c")这种的是等效的。。但是加上双引号的好处是:可以将一个完整的带空格的文件名字括起来,然后当成一个文件即可。而且对于文件,自己还可以使用:无后缀的,那么cmake可以自动搜索所有相匹配的文件名字。
7.6)最简单的hello写法:不用使用SET()来定义SRC_LIST 因为只有一个main.c那么使用:
PROJECT(HELLO)
ADD_EXECUTABLE(hello main.c)就行了。。。
8)清理工程:
跟经典的make 一样,make clean即可。
而不能删除make distclean,这个make distclean是为了能够删除中间文件,因为最后发布不需要给出中间文件的。那么注意,此时由于CMakeLists.txt是关于脚本生成的,没有办法追踪临时文件,所以没有可靠的make distclean方案。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx分割线xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
1)上述是,内部构建,in-source_build,而cmake 推荐的是out-of-source build
因为,内部构建会导致:生成的临时文件,比自己的代码都多。。实在不想使用了,那么怎么办,使用外部构建即可。
2)构建hello的外部构建方法
2.1) 删除内部构建产生的所有中间文件,就保留自己的main.c 和CMakeLists.txt。其中最关键的是删除CMakeCache.txt文件。
关于删除方式,使用两种,一种是使用ls -a列出来文件夹下面所有的内容,然后,再使用 | grep -v "name1" "name2"
这样反向选择不删除的,打印出来发现全部是名称:ls -a | grep -v "name1" "name2" 这种,然后删除方法是:
rm -rf ` ls -a | grep -v "name1" "name2" `
这种就是必须使用反向的单引号来处理这个。
2.2)创建build目录,mkdir build ,
2.3) 进入build 目录,执行cmake,但是注意由于cmake虽然是自己找CMakeLists.txt文件,但是要指定正确的CMakeList.txt文件路径,所以此时的cmake后面是..指向父文件。 cmake ..
2.4)这样做了以后,出现的情况就是说: 所有的makefile以及中间文件都在build文件夹中,然后在build中执行make,就生成了目标文件。就是说,所有的东西自己将其框在一个文件中,与源代码完全隔离,不会很乱。
2.5)总结:通过外部构建,我们发现,仍然由PROJECT 默认生成了2个隐式变量,<>SOURCE_DIR,和 <>_BINARY_DIR,后者指代编译路径,是自己创建的build路径。而源代码路径与内部构建的时候,没有任何区别的。
总结:三个指令,然后PROJECT指令生成了2个隐式变量。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx分割线。外部构建out-of-source 工程构建过程。xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
1)添加src目录放置完整的源代码内容。
2)添加doc目录,来放置工程的说明文档。
3)在工程目录中添加文本文件COPYRIGHT, README;
4)在工程目录中添加一个runhello.sh脚本,用来调用hello二进制。
5)将构建后的目标文件放入构建目录的bin子目录中,最终安装这些文件,将hello 二进制与runhello.sh安装到/usr/bin和将doc目录内容以及COPYRIGHT , README安装到/usr/share/doc/cmake/t2中。
语法声明:
1)ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
这个函数厉害在于,不仅仅是指定存放源文件的子目录,而且是指定了中间二进制和目标二进制存放的位置。
当这个里面不再用binary_dir设置,那么默认产生的binary文件是在source_dir文件中的,即编译结果放在这里面。
2)不管ADD_SUBDIRECTORY怎么设置,其实我们都可以通过SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin )来重新设定保存路径,SET(LIBRARY_OUTPUT_PATH $(PROJECT_BINARY_DIR)/lib)这种,
3)如何安装,其实以前并没有学习过。
使用的是INSTALL指令。封装的是使用make的install命令,其中make的命令是:
DESTDIR= xxxxx
install:
mkdir -p $(DESTDIR) /usr/bin
install -m 755 hello $(DESTDIR) /usr/bin
上述首先,先生成usr/bin文件,然后install这个bin文件到指定的bin目录中,使用的install -m 表示的是,install是linux自有的命令,使用install --help我们可以看到:-m就是chmod的方式,来确定其可执行方式。
4)那么,helloworld怎么安装呢?需要引入一个新的cmake指令,INSTALL和一个非常有用的变量,CMAKE_INSTALL_PREFIX这个变量类似于configure脚本的-prefix,常见的使用方法看起来是这样的,
使用区别是,这个INSTALL()函数里面的参数,是:指定的,RUNTIME 类型 DESTINATION 是默认由PREFIX前缀以及后面的名字组成。
INSTALL(TARGETS targets... [[ARCHIVE|LIBRARY|RUNTIME] [DESTINATION
5)CMake中INSTALL能安装的类型有: 普通文件FILES,目录DIRECTORY,二进制TARGETS,静态库TARGETS,动态库TARGETS,非目标文件的可执行程序 PROGRAMS安装。其中既然发现二进制,静态库,动态库使用的是同一个名字,且是复数,那么就是说,其实区分在于后面的[ARCHIVE | LIBRARY | RUNTIME]
且安装的时候,对于DIRECTORY这种格式,使用的是名字后面有没有/区别很大,有/表示安装的是该目录下的文件,没有/表示的是安装的就是带这个文件夹一起安装过去了。
一般定义CMAKE_INSTALL_PREFIX的方法是: cmake -DCMAKE_INSTALL_PREFIX = /tmp/t2/usr ..
而这个上面的输入,就是平时自己用的cmake .. #既然是.. 表明了是,外部编译。只是此时在编译使用cmake命令的时候,给加上了宏定义!!!
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx分隔符号xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
静态库和动态库的构建,不再是cmake的变量CMAKE_INSTALL_PREFIX的定义了。
1)静态库和动态库的构建
注意,代码都一样,而生成可执行代码和文件库的区别是:
一个是ADD_EXECUTABLE(name SRC_LIST)一个是ADD_LIBRARY (name [SHARE | STATIC | MODULE] SRC_LIST)
这才是决定不同的生成内容。但是如果同样的名字,生成STATIC时候,会失败,因为重复名字了。一个静态一个动态名字相同了。并且ADD_LIBRARY中,name不用写hello.so 或者hello.a因为后缀会根据SHARE,STATIC,MODULE自动生成的。
2)怎么得到同样名字的动态静态库??虽然上述,改下静态库的名字就可以了,但是明显不是编程设计者想要的结果。
3)我们需要在lib/CMakeLists.txt中添加一条:SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")这样,我们就可以同时得到libhello.so、libhello.a两个库了。
4)添加版本号的时候:
同样是SET_TARGET_PROPERTIES(name PROPERIES VERSION 1.2 S0VERSION 1)这个函数,
5)生成库文件以后,那么安装共享库和头文件的方法是:
INSTALL()指令,在CMAKE中不用函数说法,都是指令内容。,前面讲过,分别用的主体是TARGETS和FILES
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx安装完成以后xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
1) 安装完成以后,怎么使用外部共享库和头文件,
我们需要给源代码src文件夹中的CMakeLists.txt添加内容,那么,内容是:INCLUDE_DIRECTORIES_BEFORE([] [] dir1 dir2 ...)
该指令是用于添加多个指定的搜索路径,头文件搜索路径,
头文件有了,那么共享库怎么添加呢??
2)TARGET_LINK_LIBRARIES(target library1
3)CMAKE_INCLUDE_PATH 和 CMAKE_LIBRARY_PATH这两种,不是cmake变量,而是环境变量,只是这两个环境变量用于指示cmake的地址的,头文件地址,可以使用export CMAKE_INCLUDE_PATH 这种命令来智能地选择。
4)