CMake 相关
一.第一次尝试结果:
我将源码目录建为src,编译目录建为build.
然后在src下建立main,用于放main相关的文件,再在src下建立lib1,用于放一个小库。
Magic Happens like this:
(1)main和lib1中的,只需要写上和Build Target相关的command。这里是ADD_LIBRARY()或ADD_EXECUTABLE(),另外因为main要链接lib1库,所以要添加Build Flags(Options)相关的:TARGET_LINK_LIBRARIES()。
(2)然后在main和lib1的同级目录,即src下,建立工程的总的。这里面就放SUBDIRS()以及和Build Flags(Options)相关的就好。Build Flags(Options)可用INCLUDE_DIRECTORIES()来将lib1写入,这样,main中的程序要用lib1库,就只需要写"lib.h"就好,而不需要给出路径。然后,不用写LINK_DIRECTORIES(),可能有些版本要写。但我这个版本,不用给出工程中生成的库的路径,就可以链接。
还有一个Magic是,只需要一遍cmake,之后如果源文件或做了更改,都不必要cmake,直接make,它会看情况rerun cmake.
我习惯于:公共的东西,放在top的里,各个库和执行文件相关的放在各个目录的里,比如各个库怎么装。top的的例子:PROJECT(helloworld)
SUBDIRS(main lib1 lib2)
#设置工程需要的头文件路径(包括工程内的)
INCLUDE_DIRECTORIES(./lib1 ./lib2 ${CMAKE_BINARY_DIR}/config)
#设置用户选项
OPTION(HELLO1 "Using Lib1" ON)
#将config.h.in转为config.h
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/config/config.h.in
${CMAKE_BINARY_DIR}/config/config.h)
#设置工程变量
SET(CMAKE_INSTALL_PREFIX /home/evan/local)
二. 小结:
1、基本命令:
- Build Targets:
SET()
SUBDIRS()
ADD_LIBRARY()
ADD_EXECUTABLE()
PROJECT()
- Build Flags and Options:
INCLUDE_DIRECTORIES()
LINK_DIRECTORIES()
TARGET_LINK_LIBRARIES()
- Flow Control Constructs
IF的例子:
IF(UNIX)
IF(APPLE)
SET(GUI "Cocoa"
ELSE(APPLE)
SET(GUI "X11"
ENDIF(APPLE)
ELSE(UNIX)
IF(WIN32)
SET(GUI "Win32"
ELSE(WIN32)
SET(GUI "Unknown"
ENDIF(WIN32)
ENDIF(UNIX)
MESSAGE的例子:
MESSAGE("GUI system is ${GUI}")
FOREACH例子:就是可以简化对多个命令的调用,相当于bash的for语句。SET(SOURCES source1 source2 source3)
FOREACH(source ${SOURCES})
ADD_EXECUTABLE(${source} ${source}.c)
ENDFOREACH(source)
- Useful Variables:
最常用的:
CMAKE_BINARY_DIR: build的top目录
CMAKE_SOURCE_DIR: src的top目录
CMAKE_INSTALL_PREFIX: 安装路径的前缀
CMAKE_BUILD_TYPE: 如Release, Debug等。
EXECUTABLE_OUTPUT_PATH:
LIBRARY_OUTPUT_PATH:
见CMake Useful Variables文档(html)
为什么现在都用cmake
2、条件编译:
(1)给用户提供选项:
3 steps:
* 取好宏的名字,比如LINK_LIB1
* 在top的中写上OPTION(LINK_LIB1 "Using Lib1" ON)命令。当然写在top的是因为为了方便查看。
* 在用宏的文件的中写上
IF(LINK_LIB1)
SET_SOURCE_FILES_PROPERTIES(main.cpp COMPILE_FLAGS -DLINK_LIB1) ENDIF(LINK_LIB1)
其中,SET_SOURCE_.... 是对main.cpp这个文件,设置其编译选项。
你也可以用ADD_DEFINITIONS(-DLINK_LIB1),这样的话,所有的文件在编译时都会加上这个选项。
就这三步就好了!!!
然后,想控制选项的打开关闭,可以在命令行敲:
cmake -DLINK_LIB1:BOOL=OFF src/ (默认是打开,因为你写了个ON在那里)
其他辅助的命令: (但这个不常用,而是用cmake提供的modules代替,如ake,可见man)
FIND_PATH(PYTHON_INCLUDE_PATH Python.h
/usr/include
/usr/local/include)
表明你取了个变量名:PYTHON_INCLUDE_PATH,用于存放搜索Python.h的结果。
(2)检测不同平台,头文件,库等:
不同平台:IF(WIN32) IF(UNIX) 都是预先定义好的。可直接使用。
头文件:这个可加载cmake的modules:
INCLUDE(${CMAKE_ROOT}/ake)
然后使用时,命令名和那个modules的名字一样,HAVE_UNISTD_H为自己定义的变量
CHECK_INCLUDE_FILES("unistd.h" HAVE_UNISTD_H)
之后,就可以向编译器传递变量HAVE_UNISTD_H了,如,这样使用:
IF(HAVE_UNISTD_H)
ADD_DEFINITIONS(-DHAVE_UNISTD_H)
ENDIF(HAVE_UNISTD_H)
当然,你的源文件需要含有检测HAVE_UNISTD_H的宏。
库:其他的检测,如库,函数,符号等存在与否,都可以加载相应的modules,见CMake: How To Write Platform Checks.末尾。
(3)更好的办法:
看看上面(1)(2)两点,他们提供的特性,就是autotools提供的两大基本特性。但为了完成这种条件编译,方法是给编译器传递-D标志(-D标志这种特性为大多数编译器所支持)。而还有一种方法,是autoto
ols用的,就是生成一个config.件,然后让需要条件编译的源文件包含这个头文件。即,所有-D标志,现在在config.h中了,不用向编译器传递了。
这样,就不要用ADD_DEFINITIONS()之类的命令了,cmake会根据OPTION(),CHECK_INCLUDE_FILES()等命令,自动填好config.h。
方法如下:
* 自己个地方,写config.件(任何名字都行),里面内容:
#ifndef CONFIG_H
#define CONFIG_H
#cmakedefine LINK_LIB1
#endif
其中,LINK_LIB1会被自动换为如#define LINK_LIB1 或#undefine ..
当然,这个头文件,你需要添加到INCLUDE_DIRECTORIES() 中去。
* 在(我的是在top的那个里面)加上CONFIGURE_FILE()命令,见man。
只是注意,两个路径,都要是full path。第一个路径为包括config.h.in的全路径,第二个路径为生成的config.h的全路径,一般习惯是放在你的build目录下去。所以,第一个路径可以使用系统预定义的变量:CMAKE_SOURCE_DIR来代替你的src的全路径,再加上你的子路径如/config/config.h.in即可,第二个路径可以使用
CMAKE_BINARY_DIR,指代的是build的全路径,再加上你的子路径,如
/config/config.h即可。当然,这第二个路径要放到INCLUDE_DIRECTORIES(),才能让你的程序使用到。
* 这样就可以了!!如OPTION里面定义的值,只要你写到config.h中去了,就会被CONFIGURE_FILE()命令转换为正常的#define或#undefine.
其他的IF(LINK_LIB1)等,仍然可以使用,这样,你可以根据用户的选择,来用TARGET_LINK_LIBRARIES()链接不同的库。
注:当然,如果是用OPTION()命令接受用户的选择,那么用户只能在cmake
-
DLINK_LIB1:BOOL=OFF src/ 这里设置。也就是说,如果你把这个包给用户的话,用户要熟悉用cmake这样来设置。或者使用你写的或别人写的wrapper,如ccmake.
3. 如何在使用一个库时,加上-I, -L, -l 和-D
-I, -L, -l和-D是传递给编译器的主要参数,意义很明显。
cmake提供的众多module中,主要有两个可以干这事情:
(1)UsePkgConfig模块:背后使用的是系统的pkg-config
pkg-config的好处,太明显了,如编译和链接gtk程序:
gcc helloworld.c `pkg-config --cflags --libs gtk+-2.0`
它的原理就是把prefix/lib/pkgconfig目录下的,你指定名字的xx.pc文件中的信息读出来给你,你要--libs,就给你-l,查看.pc就知道了。得到-I通过--cflags,要链接的库-l 通过--libs,省得你手敲的麻烦,你不信,试试pkg-config命令,它了一大堆头文件路径和库名出来。
cmake提供了module UsePkgConfig来对pkg-config支持,具体可man,如编译链接gtk 可这样:
假设编译的执行文件是helloworld
ADD_EXECUTABLE(helloworld b.c)
加上gtk的支持可以这样:
INCLUDE(UsePkgConfig)
PKGCONFIG(gtk+-2.0 includedir libdir linkflags cflags)
然后,PKGCONFIG中的变量就被填上了,就可以用cmake告知编译器参数的如下四条命令:
INCLUDE_DIRECTORIES(${includedir}) #-I。
LINK_DIRECTORIES(${libdir}) #-L
TARGET_LINK_LIBRARIES(helloworld ${linkflags}) #-l
ADD_DEFINITIONS(${cflags}) #-D
注意:
a. INCLUDE 和INCLUDE_DIRECTORIES 是不一样的,前者是cmake用来包含入其他的cmake lists文件或是cmake的模块文件,而后者是用于传给编译器头文件路径的参数。
b. 由于wxWidgets不采用普通的pkg-config,而是自己带了一个wx-config,所以,没有普通的xx.pc文件可供UsePkgConfig使用,所以,应该采取FIND_PACKAGE模块,见下。
(2)FIND_PACKAGE
如你写FIND_PACKAGE(wxWidgets),它就去调用module中的ake 这个模块,然后你可以得到几个变量,man下,并搜索FindwxWidgets,就知道可以得到哪些变量,就知道怎么用了。这个ake 就是使用了wx-config!而不是pkg-config,所以,看起来FIND_PACKAGE比UsePkgConfig灵活。
基本模式是:
FIND_PACKAGE(wxWidgets REQUIRED)
IF(wxWidgets_FOUND)
INCLUDE(${wxWidgets_USE_FILE}) #这个变量是便利的-D -I... 如果要分开,看man吧。
TARGET_LINK_LIBRARIES(rose ${wxWidgets_LIBRARIES})
ENDIF(wxWidgets_FOUND)
4. INSTALL命令:
这个命令可以使得cmake生成的Makefile文件含有install目标。
- INSTALL(FILES 源文件,资源文件等DESTINATION 路径)
- INSTALL(PROGRAMS 脚本等非二进制的但可执行文件DESTINATION 路径)
- INSTALL(TARGETS 二进制程序动态库静态库
RUNTIME DESTINATION 路径
LIBRARY DESTINATION 路径
ARCHIVE DESTINATION 路径)
例子:
INSTALL(TARGETS myExe mySharedLib myStaticLib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论