zoukankan      html  css  js  c++  java
  • 《CMake实践》笔记二:INSTALL/CMAKE_INSTALL_PREFIX

    《CMake实践》笔记一:PROJECT/MESSAGE/ADD_EXECUTABLE

    《CMake实践》笔记二:INSTALL/CMAKE_INSTALL_PREFIX

    《CMake实践》笔记三:构建静态库与动态库 及 如何使用外部共享库和头文件

    四、更好一点的Hello World

    没有最好,只有更好

    从本小节开始,后面所有的构建我们都将采用 out-of-source 外部构建,约定的构建目录是工程目录下的build自录。

    本小节的任务是让前面的Hello World更像一个工程,我们需要作的是:

    (1)、为工程添加一个子目录src,用来放置工程源代码;

    (2)、添加一个子目录doc,用来放置这个工程的文档hello.txt

    (3)、在工程目录添加文本文件COPYRIGHT, README;

    (4)、在工程目录添加一个runhello.sh脚本,用来调用hello二进制

    (5)、将构建后的目标文件放入构建目录的bin子目录;

    (6)、最终安装这些文件:将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)

    在第一节我们提到了<projectname>_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 <dir>]
    [PERMISSIONS permissions...]
    [CONFIGURATIONS
    [Debug|Release|...]]
    [COMPONENT <component>]
    [OPTIONAL]
    ] [...])

    参数中的TARGETS后面跟的就是我们通过ADD_EXECUTABLE或者ADD_LIBRARY定义的目标文件,可能是可执行二进制、动态库、静态库。目标类型也就相对应的有三种,ARCHIVE特指静态库,LIBRARY特指动态库,RUNTIME特指可执行目标二进制。

    DESTINATION定义了安装的路径,如果路径以/开头,那么指的是绝对路径,这时候CMAKE_INSTALL_PREFIX其实就无效了。如果你希望使用CMAKE_INSTALL_PREFIX来定义安装路径,就要写成相对路径,即不要以/开头,那么安装后的路径就是${CMAKE_INSTALL_PREFIX}/<DESTINATION定义的路径>

    举个简单的例子:

    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 <dir>
    [PERMISSIONS permissions...]
    [CONFIGURATIONS [Debug|Release|...]]
    [COMPONENT <component>]
    [RENAME <name>] [OPTIONAL])

    可用于安装一般文件,并可以指定访问权限,文件名是此指令所在路径下的相对路径。如果默认不定义权限PERMISSIONS,安装后的权限为:OWNER_WRITE, OWNER_READ, GROUP_READ,和WORLD_READ,即644权限。

    非目标文件的可执行程序安装(比如脚本之类):

    INSTALL(PROGRAMS files... DESTINATION <dir>
    [PERMISSIONS permissions...]
    [CONFIGURATIONS [Debug|Release|...]]
    [COMPONENT <component>]
    [RENAME <name>] [OPTIONAL])

    跟上面的FILES指令使用方法一样,唯一的不同是安装后权限为:OWNER_EXECUTE, GROUP_EXECUTE, 和WORLD_EXECUTE,即755权限

    目录的安装:

    INSTALL(DIRECTORY dirs... DESTINATION <dir>
    [FILE_PERMISSIONS permissions...]
    [DIRECTORY_PERMISSIONS permissions...]
    [USE_SOURCE_PERMISSIONS]
    [CONFIGURATIONS [Debug|Release|...]]
    [COMPONENT <component>]
    [[PATTERN <pattern> | REGEX <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目录安装到 <prefix>/share/myproj,将scripts/中的内容安装到<prefix>/share/myproj不包含目录名为CVS的目录,对于scripts/*文件指定权限为 OWNER_EXECUTE OWNER_WRITE OWNER_READ ROUP_EXECUTE GROUP_READ。

    安装时CMAKE脚本的执行:

    INSTALL([[SCRIPT <file>] [CODE <code>]] [...])
    SCRIPT参数用于在安装时调用cmake脚本文件(也就是<abc>.cmake文件)
    CODE参数用于执行CMAKE指令,必须以双引号括起来。比如:
    INSTALL(CODE "MESSAGE("Sample install message.")")
    安装还有几个被标记为过时的指令,比如INSTALL_FILES等,这些指令已经不再推荐使用,所以,这里就不再赘述了。下面,我们就来改写我们的工程文件,让他来支持各种文件的安装,并且,我们要使用CMAKE_INSTALL_PREFIX指令。

    5、修改Helloworld支持安装

    首先我们先补上为添加的文件。

    添加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要安装到/<prefix>/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参数可以直接构建静态库和动态库吗?)

    未完,待续。。。。


    以下是我自己增加的东西。

    把这一节试了一下,感觉还是有点不爽,因为要编译这个工程,首先要自己建立一个目录,然后进入这个目录后先运行一次cmake的命令,在执行make 和 make install。因此个人将这个例子自己稍微改了一下,让安装和卸载更方便使用。

    首先目录结构还是一样的,工程目录下一个src目录,一个CMakeLists.txt,一个空的COPYRIGHT和README,一个doc目录,里面有一个空的hello.txt文件。

    然后自己增加一个install.sh和uninstall.sh脚本,用于安装这个工程和卸载这个工程,整个目录结构如下:

     

    脚本的内容也很简单,可以指定安装目录,或者用默认的安装目录:

    #!/bin/sh

    if [ "$#" -eq 0 ]
    then 
        echo "use default install dir: target"
        INSTALL_DIR="../target"
    elif [ "$#" -eq 1 ]
    then 
        echo "use custom install dir: $1"
        INSTALL_DIR=$1
    else
        echo "error"
    fi

    rm -drf bulid
    rm -drf $INSTALL_DIR

    mkdir bulid

    cd bulid
    cmake -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR ..
    make
    make install

    这样,在拿到这个共亨的gz文件后,解压,直接运行install.sh脚本,入过不带路径,安装在当前目录下的target目录下,否则安装在指定的目录下:

     

    《CMake实践》笔记一:PROJECT/MESSAGE/ADD_EXECUTABLE

    《CMake实践》笔记二:INSTALL/CMAKE_INSTALL_PREFIX

    《CMake实践》笔记三:构建静态库与动态库 及 如何使用外部共享库和头文件

  • 相关阅读:
    LeetCode 275. H-Index II
    LeetCode 274. H-Index
    LeetCode Gray Code
    LeetCode 260. Single Number III
    LeetCode Word Pattern
    LeetCode Nim Game
    LeetCode 128. Longest Consecutive Sequence
    LeetCode 208. Implement Trie (Prefix Tree)
    LeetCode 130. Surrounded Regions
    LeetCode 200. Number of Islands
  • 原文地址:https://www.cnblogs.com/52php/p/5681751.html
Copyright © 2011-2022 走看看