Chinaunix首页 | 论坛 | 博客
  • 博客访问: 332443
  • 博文数量: 77
  • 博客积分: 1925
  • 博客等级: 上尉
  • 技术积分: 1065
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-13 17:26
文章分类
文章存档

2012年(77)

我的朋友

分类: LINUX

2012-04-26 14:31:00

8,内部构建与外部构建:
上面的例子展示的是“内部构建”,相信看到生成的临时文件比您的代码文件还要多的时候,
估计这辈子你都不希望再使用内部构建:-D
举个简单的例子来说明外部构建,以编译 wxGTK 动态库和静态库为例,在 Everest 中打包
方式是这样的:
解开 wxGTK 后。
在其中建立 static 和 shared 目录。
进入 static 目录,运行../configure –enable-static;make 会在 static 目录生
成 wxGTK 的静态库。
进入 shared 目录,运行../configure –enable-shared;make 就会在 shared 目录
生成动态库。
这就是外部编译的一个简单例子。
对于 cmake,内部编译上面已经演示过了,它生成了一些无法自动删除的中间文件,所以,
引出了我们对外部编译的探讨,外部编译的过程如下:
1,首先,请清除 t1 目录中除 main.c CmakeLists.txt 之外的所有中间文件,最关键
的是 CMakeCache.txt。
2,在 t1 目录中建立 build 目录,当然你也可以在任何地方建立 build 目录,不一定必
须在工程目录中。
3,进入 build 目录,运行 cmake ..(注意,..代表父目录,因为父目录存在我们需要的
CMakeLists.txt,如果你在其他地方建立了 build 目录,需要运行 cmake <工程的全
路径>),查看一下 build 目录,就会发现了生成了编译需要的 Makefile 以及其他的中间
文件.
4,运行 make 构建工程,就会在当前目录(build 目录)中获得目标文件 hello。
上述过程就是所谓的 out-of-source 外部编译,一个最大的好处是,对于原有的工程没
有任何影响,所有动作全部发生在编译目录。通过这一点,也足以说服我们全部采用外部编
译方式构建工程。
这里需要特别注意的是:
通过外部编译进行工程构建,HELLO_SOURCE_DIR 仍然指代工程路径,即
/backup/cmake/t1
而 HELLO_BINARY_DIR 则指代编译路径,即/backup/cmake/t1/build
9,小结:
本小节描述了使用 cmake 构建 Hello World 程序的全部过程,并介绍了三个简单的指令:
PROJECT/MESSAGE/ADD_EXECUTABLE 以及变量调用的方法,同时提及了两个隐式变量
_SOURCE_DIR 及_BINARY_DIR,演示了变量调用的方
法,从这个过程来看,有些开发者可能会想,这实在比我直接写 Makefile 要复杂多了,
甚至我都可以不编写 Makefile,直接使用 gcc main.c 即可生成需要的目标文件。是的,
正如第一节提到的,如果工程只有几个文件,还是直接编写 Makefile 最简单。但是,
kdelibs 压缩包达到了 50 多 M,您认为使用什么方案会更容易一点呢?
下一节,我们的任务是让 HelloWorld 看起来更像一个工程。
四,更好一点的 Hello World
 
    没有最好,只有更好
从本小节开始,后面所有的构建我们都将采用 out-of-source 外部构建,约定的构建目
录是工程目录下的 build 自录。
本小节的任务是让前面的 Hello World 更像一个工程,我们需要作的是:
1,为工程添加一个子目录 src,用来放置工程源代码;
2,添加一个子目录 doc,用来放置这个工程的文档 hello.txt
3,在工程目录添加文本文件 COPYRIGHT, README;
4,在工程目录添加一个 runhello.sh 脚本,用来调用 hello 二进制
4,将构建后的目标文件放入构建目录的 bin 子目录;
5,最终安装这些文件:将 hello 二进制与 runhello.sh 安装至/usr/bin,将 doc 目录
的内容以及 COPYRIGHT/README 安装到/usr/share/doc/cmake/t2,将
1,准备工作:
在/backup/cmake/目录下建立 t2 目录。
将 t1 工程的 main.c 和 CMakeLists.txt 拷贝到 t2 目录中。
2,添加子目录 src:
mkdir src
mv main.c src
现在的工程看起来是这个样子:
一个子目录 src,一个 CMakeLists.txt。
上一节我们提到,需要为任何子目录建立一个 CMakeLists.txt,
进入子目录 src,编写 CMakeLists.txt 如下:
ADD_EXECUTABLE(hello main.c)
将 t2 工程的 CMakeLists.txt 修改为:
PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)
然后建立 build 目录,进入 build 目录进行外部编译。
cmake ..
make
构建完成后,你会发现生成的目标文件 hello 位于 build/bin 目录中。
语法解释:
ADD_SUBDIRECTORY 指令
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
这个指令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存
放的位置。EXCLUDE_FROM_ALL 参数的含义是将这个目录从编译过程中排除,比如,工程
的 example,可能就需要工程构建完成后,再进入 example 目录单独进行构建(当然,你
也可以通过定义依赖来解决此类问题)。
上面的例子定义了将 src 子目录加入工程,并指定编译输出(包含编译中间结果)路径为
bin 目录。如果不进行 bin 目录的指定,那么编译结果(包括中间结果)都将存放在
build/src 目录(这个目录跟原有的 src 目录对应),指定 bin 目录后,相当于在编译时
将 src 重命名为 bin,所有的中间结果和目标二进制都将存放在 bin 目录。
这里需要提一下的是 SUBDIRS 指令,使用方法是:
SUBDIRS(dir1 dir2...),但是这个指令已经不推荐使用。它可以一次添加多个子目录,
并且,即使外部编译,子目录体系仍然会被保存。
如果我们在上面的例子中将 ADD_SUBDIRECTORY (src bin)修改为 SUBDIRS(src)。
那么在 build 目录中将出现一个 src 目录,生成的目标代码 hello 将存放在 src 目录中。
3,换个地方保存目标二进制
不论是 SUBDIRS 还是 ADD_SUBDIRECTORY 指令(不论是否指定编译输出目录),我们都可
以通过 SET 指令重新定义 EXECUTABLE_OUTPUT_PATH 和 LIBRARY_OUTPUT_PATH 变量
来指定最终的目标二进制的位置(指最终生成的 hello 或者最终的共享库,不包含编译生成
的中间文件)
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
在第一节我们提到了_BINARY_DIR 和 PROJECT_BINARY_DIR 变量,他
们指的编译发生的当前目录,如果是内部编译,就相当于 PROJECT_SOURCE_DIR 也就是
工程代码所在目录,如果是外部编译,指的是外部编译所在目录,也就是本例中的 build
目录。
所以,上面两个指令分别定义了:
可执行二进制的输出路径为 build/bin 和库的输出路径为 build/lib.
本节我们没有提到共享库和静态库的构建,所以,你可以不考虑第二条指令。
问题是,我应该把这两条指令写在工程的 CMakeLists.txt 还是 src 目录下的
CMakeLists.txt,把握一个简单的原则,在哪里 ADD_EXECUTABLE 或 ADD_LIBRARY,
如果需要改变目标存放路径,就在哪里加入上述的定义。
在这个例子里,当然就是指 src 下的 CMakeLists.txt 了。
4,如何安装。
安装的需要有两种,一种是从代码编译后直接 make install 安装,一种是打包时的指定
目录安装。
所以,即使最简单的手工编写的 Makefile,看起来也是这个样子的:
DESTDIR=
install:
     mkdir -p $(DESTDIR)/usr/bin
     install -m 755 hello $(DESTDIR)/usr/bin
你可以通过:
make install
将 hello 直接安装到/usr/bin 目录,也可以通过 make install
DESTDIR=/tmp/test 将他安装在
/tmp/test/usr/bin 目录,打包时这个方式经常被使用。
稍微复杂一点的是还需要定义 PREFIX,一般 autotools 工程,会运行这样的指令:
./configure –prefix=/usr 或者./configure --prefix=/usr/local 来指定
PREFIX
比如上面的 Makefile 就可以改写成:
DESTDIR=
PREFIX=/usr
install:
     mkdir -p $(DESTDIR)/$(PREFIX)/bin
     install -m 755 hello $(DESTDIR)/$(PREFIX)/bin
那么我们的 HelloWorld 应该怎么进行安装呢?
这里需要引入一个新的 cmake 指令 INSTALL 和一个非常有用的变量
CMAKE_INSTALL_PREFIX。
CMAKE_INSTALL_PREFIX 变量类似于 configure 脚本的 –prefix,常见的使用方法看
起来是这个样子:
cmake -DCMAKE_INSTALL_PREFIX=/usr .
INSTALL 指令用于定义安装规则,安装的内容可以包括目标二进制、动态库、静态库以及
文件、目录、脚本等。
INSTALL 指令包含了各种安装类型,我们需要一个个分开解释:
目标文件的安装:
        INSTALL(TARGETS targets...
            [[ARCHIVE|LIBRARY|RUNTIME]
                        [DESTINATION ]
                        [PERMISSIONS permissions...]
                        [CONFIGURATIONS
          [Debug|Release|...]]
                        [COMPONENT ]
                        [OPTIONAL]
                       ] [...])
参数中的 TARGETS 后面跟的就是我们通过 ADD_EXECUTABLE 或者 ADD_LIBRARY 定义的
目标文件,可能是可执行二进制、动态库、静态库。
目标类型也就相对应的有三种,ARCHIVE 特指静态库,LIBRARY 特指动态库,RUNTIME
特指可执行目标二进制。
DESTINATION 定义了安装的路径,如果路径以/开头,那么指的是绝对路径,这时候
CMAKE_INSTALL_PREFIX 其实就无效了。如果你希望使用 CMAKE_INSTALL_PREFIX 来
定义安装路径,就要写成相对路径,即不要以/开头,那么安装后的路径就是
${CMAKE_INSTALL_PREFIX}/
举个简单的例子:
INSTALL(TARGETS myrun mylib mystaticlib
     RUNTIME DESTINATION bin
     LIBRARY DESTINATION lib
     ARCHIVE DESTINATION libstatic
     )
上面的例子会将:
可执行二进制 myrun 安装到${CMAKE_INSTALL_PREFIX}/bin 目录
动态库 libmylib 安装到${CMAKE_INSTALL_PREFIX}/lib 目录
静态库 libmystaticlib 安装到${CMAKE_INSTALL_PREFIX}/libstatic 目录
特别注意的是你不需要关心 TARGETS 具体生成的路径,只需要写上 TARGETS 名称就可以
了。
普通文件的安装:
         INSTALL(FILES files... DESTINATION
             [PERMISSIONS permissions...]
             [CONFIGURATIONS [Debug|Release|...]]
             [COMPONENT ]
             [RENAME ] [OPTIONAL])
可用于安装一般文件,并可以指定访问权限,文件名是此指令所在路径下的相对路径。如果
默认不定义权限 PERMISSIONS,安装后的权限为:
OWNER_WRITE, OWNER_READ,   GROUP_READ,和 WORLD_READ,即 644 权限。
非目标文件的可执行程序安装(比如脚本之类):
         INSTALL(PROGRAMS files... DESTINATION
             [PERMISSIONS permissions...]
             [CONFIGURATIONS [Debug|Release|...]]
             [COMPONENT ]
             [RENAME ] [OPTIONAL])
跟上面的 FILES 指令使用方法一样,唯一的不同是安装后权限为:
OWNER_EXECUTE, GROUP_EXECUTE, 和 WORLD_EXECUTE,即 755 权限
目录的安装:
         INSTALL(DIRECTORY dirs... DESTINATION
             [FILE_PERMISSIONS permissions...]
             [DIRECTORY_PERMISSIONS permissions...]
             [USE_SOURCE_PERMISSIONS]
             [CONFIGURATIONS [Debug|Release|...]]
             [COMPONENT ]
             [[PATTERN | REGEX ]
              [EXCLUDE] [PERMISSIONS permissions...]] [...])
这里主要介绍其中的 DIRECTORY、PATTERN 以及 PERMISSIONS 参数。
DIRECTORY 后面连接的是所在 Source 目录的相对路径,但务必注意:
abc 和 abc/有很大的区别。
如果目录名不以/结尾,那么这个目录将被安装为目标路径下的 abc,如果目录名以/结尾,
代表将这个目录中的内容安装到目标路径,但不包括这个目录本身。
PATTERN 用于使用正则表达式进行过滤,PERMISSIONS 用于指定 PATTERN 过滤后的文件
权限。
我们来看一个例子:
         INSTALL(DIRECTORY icons scripts/ DESTINATION share/myproj
            PATTERN "CVS" EXCLUDE
            PATTERN "scripts/*"
            PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ
                    GROUP_EXECUTE GROUP_READ)
这条指令的执行结果是:
将 icons 目录安装到 /share/myproj,将 scripts/中的内容安装到
/share/myproj
不包含目录名为 CVS 的目录,对于 scripts/*文件指定权限为 OWNER_EXECUTE
OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ.
安装时 CMAKE 脚本的执行:
INSTALL([[SCRIPT ] [CODE ]] [...])
SCRIPT 参数用于在安装时调用 cmake 脚本文件(也就是.cmake 文件)
CODE 参数用于执行 CMAKE 指令,必须以双引号括起来。比如:
INSTALL(CODE "MESSAGE(\"Sample install message.\")")
安装还有几个被标记为过时的指令,比如 INSTALL_FILES 等,这些指令已经不再推荐使
用,所以,这里就不再赘述了。
下面,我们就来改写我们的工程文件,让他来支持各种文件的安装,并且,我们要使用
CMAKE_INSTALL_PREFIX 指令。
5,修改 Helloworld 支持安装
在本节开头我们定义了本节的任务如下:
1,为工程添加一个子目录 src,用来存储源代码;
2,添加一个子目录 doc,用来存储这个工程的文档 hello.txt
3,在工程目录添加文本文件 COPYRIGHT, README;
4,在工程目录添加一个 runhello.sh 脚本,用来调用 hello 二进制
4,将构建后的目标文件放入构建目录的 bin 子目录;
5,最终安装这些文件:将 hello 二进制与 runhello.sh 安装至//bin,将
doc 目录中的 hello.txt 以及 COPYRIGHT/README 安装到
//share/doc/cmake/t2,将
首先我们先补上为添加的文件。
添加 doc 目录及文件:
cd /backup/cmake/t2
mkdir doc
vi doc/hello.txt
随便填写一些内容并保存
在工程目录添加 runhello.sh 脚本,内容为:
hello
添加工程目录中的 COPYRIGHT 和 README
touch COPYRIGHT
touch README
下面改写各目录的 CMakeLists.txt 文件。
1,安装 COPYRIGHT/README,直接修改主工程文件 CMakelists.txt,加入以下指令:
INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/t2)
2,安装 runhello.sh,直接修改主工程文件 CMakeLists.txt,加入如下指令:
INSTALL(PROGRAMS runhello.sh DESTINATION bin)
3,安装 doc 中的 hello.txt,这里有两种方式:一是通过在 doc 目录建立
CMakeLists.txt 并将 doc 目录通过 ADD_SUBDIRECTORY 加入工程来完成。另一种方法
是直接在工程目录通过
INSTALL(DIRECTORY 来完成),前者比较简单,各位可以根据兴趣自己完成,我们来尝试
后者,顺便演示以下 DIRECTORY 的安装。
因为 hello.txt 要安装到//share/doc/cmake/t2,所以我们不能直接安装
整个 doc 目录,这里采用的方式是安装 doc 目录中的内容,也就是使用”doc/”
在工程文件中添加
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake/t2)
6,尝试我们修改的结果:
现在进入 build 目录进行外部编译,注意使用 CMAKE_INSTALL_PREFIX 参数,这里我们
将它安装到了/tmp/t2 目录:
cmake -DCMAKE_INSTALL_PREFIX=/tmp/t2/usr ..
然后运行
make
make install
让我们进入/tmp/t2 目录看一下安装结果:
./usr
./usr/share
./usr/share/doc
./usr/share/doc/cmake
./usr/share/doc/cmake/t2
./usr/share/doc/cmake/t2/hello.txt
./usr/share/doc/cmake/t2/README
./usr/share/doc/cmake/t2/COPYRIGHT
./usr/bin
./usr/bin/hello
./usr/bin/runhello.sh
如果你要直接安装到系统,可以使用如下指令:
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
7,一个疑问
如果我没有定义 CMAKE_INSTALL_PREFIX 会安装到什么地方?
你可以尝试以下,cmake ..;make;make install,你会发现
CMAKE_INSTALL_PREFIX 的默认定义是/usr/local
8,小结:
本小节主要描述了如何在工程中使用多目录、各种安装指令以及
CMAKE_INSTALL_PREFIX 变量(你真够牛的,这么点东西居然罗唆了这么多文字)
在下一小节,我们将探讨如何在 cmake 中构建动态库和静态库,以及如何使用外部头文件
和外部共享库,毕竟,这是程序编写中最长使用的(对了,你知道用怎样的 gcc 参数可以
直接构建静态库和动态库吗?)
五,静态库与动态库构建
读者云,太能罗唆了,一个 Hello World 就折腾了两个大节。OK,从本节开始,我们不
再折腾 Hello World 了,我们来折腾 Hello World 的共享库。
本节的任务:
1,建立一个静态库和动态库,提供 HelloFunc 函数供其他程序编程使用,HelloFunc
向终端输出 Hello World 字符串。
2,安装头文件与共享库。
一,准备工作:
在/backup/cmake 目录建立 t3 目录,用于存放本节涉及到的工程
二,建立共享库
cd /backup/cmake/t3
mkdir lib
在 t3 目录下建立 CMakeLists.txt,内容如下:
PROJECT(HELLOLIB)
ADD_SUBDIRECTORY(lib)
在 lib 目录下建立两个源文件 hello.c 与 hello.h
hello.c 内容如下:
#include “hello.h”
void HelloFunc()
{
     printf(“Hello World\n”);
}
hello.h 内容如下:
#ifndef HELLO_H
#define HELLO_H
#include
void HelloFunc();
#endif
在 lib 目录下建立 CMakeLists.txt,内容如下:
SET(LIBHELLO_SRC hello.c)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
三,编译共享库:
仍然采用 out-of-source 编译的方式,按照习惯,我们建立一个 build 目录,在 build
目录中
cmake ..
make
这时,你就可以在 lib 目录得到一个 libhello.so,这就是我们期望的共享库。
如果你要指定 libhello.so 生成的位置,可以通过在主工程文件 CMakeLists.txt 中修
改 ADD_SUBDIRECTORY(lib)指令来指定一个编译输出位置或者
在 lib/CMakeLists.txt 中添加
SET(LIBRARY_OUTPUT_PATH <路径>)来指定一个新的位置。
这两者的区别我们上一节已经提到了,所以,这里不再赘述,下面,我们解释一下一个新的
指令 ADD_LIBRARY
        ADD_LIBRARY(libname     [SHARED|STATIC|MODULE]
           [EXCLUDE_FROM_ALL]
                 source1 source2 ... sourceN)
你不需要写全 libhello.so,只需要填写 hello 即可,cmake 系统会自动为你生成
libhello.X
类型有三种:
SHARED,动态库
STATIC,静态库
MODULE,在使用 dyld 的系统有效,如果不支持 dyld,则被当作 SHARED 对待。
EXCLUDE_FROM_ALL 参数的意思是这个库不会被默认构建,除非有其他的组件依赖或者手
工构建。
阅读(1951) | 评论(0) | 转发(0) |
0

上一篇:CMake-2

下一篇:CMake-4

给主人留下些什么吧!~~