cmake简单使用及编译项目打包成so文件

简介

CMake是一个跨平台的编译自动配置工具,它使用一个名为CMakeLists.txt的文件来描述构建过程,能够产生标准的构建文件。它能够用简单的语句来描述全部平台的安装(编译过程)。它可以输出各类各样的makefile或者project文件,能测试编译器所支持的C++特性,相似UNIX下的automake。CMake并不直接建构出最终的软件,而是产生标准的建构档(如Unix的Makefile或Windows Visual C++的projects/workspaces),而后再依通常的建构方式使用。html

CMake能够编译源代码、制做程式库、产生适配器(wrapper)、还能够用任意的顺序建构执行档。CMake支持in-place建构(二进档和源代码在同一个目录树中)和out-of-place建构(二进档在别的目录里),所以能够很容易从同一个源代码目录树中建构出多个二进档。CMake也支持静态与动态程式库的建构。CMake是一个比make更高级的编译配置工具。python

         CMake的组态档取名为CMakeLists.txt。组态档是用一种建构软件专用的特殊编程语言写的CMake脚本。文件CMakeLists.txt须要手工编写,也能够经过编写脚本进行半自动的生成。经过编写CMakeLists.txt,能够控制生成的Makefile,从而控制编译过程。linux

CMake主要特色:

(1)、开放源代码,使用类BSD许可发布;正则表达式

(2)、跨平台,并可生成native编译配置文件,在Linux/Unix平台,生成makefile;在苹果平台,能够生成xcode;在windows平台,能够生成msvc的工程文件express

(3)、可以管理大型项目;编程

(4)、简化编译构建过程和编译过程,CMake的工具链很是简单:cmake+makebootstrap

(5)、高效率;windows

(6)、可扩展,能够为cmake编写特定功能的模块,扩充cmake功能。数组

查看Ubuntu14.0464位机上是否安装了CMake及版本号,可经过执行一下语句来验证:xcode

cmake --version  

 

Linux下安装cmake

一、从官网下载cmake安装包,输入指令解压缩

sudo tar -zxvf cmake-3.8.2.tar.gz 
二、进入解压缩后的文件夹中,依次输入

  1. sudo ./bootstrap
  2. sudo make
  3. sudo make install

或者直接命令安装:

apt-get install cmake

三、安装检查:输入命令,

cmake --version

若出现对应cmake的版本,则说明安装成功,通常默认安装在 /usr/local/bin 目录下。

简单使用

a、新建目录hello, 依次建立文件main.c, CMakeLists.txt, build目录

b、main.c 文件代码以下

#include <stdio.h>
int main(void)
{
    printf("Hello\n");
	return 0;
}

c、CMakeLists.txt 文件代码以下

CMAKE_MINIMUM_REQUIRED(VERSION 3.8) #cmake最低版本需求,不加入此行会受到警告信息
PROJECT(HELLO) #项目名称  
AUX_SOURCE_DIRECTORY(. SRC_LIST) #把当前目录(.)下全部源代码文件和头文件加入变量SRC_LIST
ADD_EXECUTABLE(hello ${SRC_LIST}) #生成应用程序 hello (在windows下会自动生成hello.exe)

d、进入build 目录,依次输入

cmake ..
make
./hello

没错,就这么简单。

过程以下:

 

经常使用命令介绍

CMakeLists.txt的语法比较简单,由命令注释空格组成,其中命令是不区分大小写的,参数和变量是大小写相关的,但,推荐所有使用大写指令。符号”#”后面的内容被认为是注释。命令由命令名称、小括号和参数组成,参数之间使用空格或分号进行间隔。变量使用${xxx}引用。

 

1)project 命令

命令语法:project(<projectname> [languageName1 languageName2 … ] )

命令简述:用于指定项目的名称

使用范例:project(Main)

2)cmake_minimum_required命令

命令语法:cmake_minimum_required(VERSION major[.minor[.patch[.tweak]]][FATAL_ERROR])

命令简述:用于指定须要的 CMake 的最低版本

使用范例:cmake_minimum_required(VERSION 2.8)

3)aux_source_directory命令

命令语法:aux_source_directory(<dir> <variable>)

命令简述:用于将 dir 目录下的全部源文件的名字保存在变量 variable 中,该命令会把参数<dir>中全部的源文件(不包括头文件)名称赋值给参数<variable>;

使用范例:aux_source_directory(.  DIR_SRCS)

4)add_executable 命令

命令语法:add_executable(<name> [WIN32] [MACOSX_BUNDLE][EXCLUDE_FROM_ALL] source1 source2 … sourceN)

命令简述:用于指定从一组源文件 source1 source2 … sourceN 编译出一个可执行文件且命名为 name

使用范例:add_executable(Main ${DIR_SRCS})

5)add_library 命令

命令语法:add_library([STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] source1source2 … sourceN)

命令简述:用于指定从一组源文件 source1 source2 … sourceN 编译出一个库文件且命名为 name

能够设置要生成的连接库为SHARED或者STATIC,还能够设置MODULE(插件,可动态调用,但不做为其它工程的依赖);

使用范例:add_library(Lib ${DIR_SRCS}) ;add_library(utils SHARED ${utils_dir}) 

6)add_dependencies 命令

命令语法:add_dependencies(target-name depend-target1 depend-target2 …)

命令简述:用于指定某个目标(可执行文件或者库文件)依赖于其余的目标。这里的目标必须是 add_executable、add_library、add_custom_target 命令建立的目标

7)add_subdirectory 命令

命令语法:add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

命令简述:用于添加一个须要进行构建的子目录

使用范例:add_subdirectory(Lib)

8)target_link_libraries命令

命令语法:target_link_libraries(<target> [item1 [item2 […]]][[debug|optimized|general] ] …)

命令简述:用于指定 target 须要连接 item1 item2 …。这里 target 必须已经被建立,连接的 item 能够是已经存在的 target(依赖关系会自动添加)

使用范例:target_link_libraries(Main Lib) ; target_link_libraries(myproj ${OpenCV_LIBS} utils facedetect)

9)set 命令

命令语法:set(<variable> <value> [[CACHE <type><docstring> [FORCE]] | PARENT_SCOPE])

命令简述:用于设定变量 variable 的值为 value。若是指定了 CACHE 变量将被放入 Cache(缓存)中。

使用范例:set(ProjectName Main)

10)unset 命令

命令语法:unset(<variable> [CACHE])

命令简述:用于移除变量 variable。若是指定了 CACHE 变量将被从 Cache 中移除。

使用范例:unset(VAR CACHE)

11)message 命令

命令语法:message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] “message todisplay”…)

命令简述:用于输出信息

使用范例:message(“Hello World”)

12)include_directories 命令

命令语法:include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 …)

命令简述:用于设定目录,这些设定的目录将被编译器用来查找 include 文件

                 指定头文件的搜索路径,用来向工程添加多个特定的头文件搜索路径,能够屡次调用以设置多个路径,至关于指定gcc的-I参数;

使用范例:include_directories(${PROJECT_SOURCE_DIR}/lib),include_directories(facedetect utils)

13)find_path 命令

命令语法:find_path(<VAR> name1 [path1 path2 …])

命令简述:用于查找包含文件 name1 的路径,若是找到则将路径保存在 VAR 中(此路径为一个绝对路径),若是没有找到则结果为 <VAR>-NOTFOUND。默认的状况下,VAR 会被保存在 Cache 中,这时候咱们须要清除 VAR 才能够进行下一次查询(使用 unset 命令)。

使用范例:

find_path(LUA_INCLUDE_PATH lua.h${LUA_INCLUDE_FIND_PATH})

if(NOT LUA_INCLUDE_PATH)

   message(SEND_ERROR "Header file lua.h not found")

endif()

14)find_library 命令

命令语法:find_library(<VAR> name1 [path1 path2 …])

命令简述:用于查找库文件 name1 的路径,若是找到则将路径保存在 VAR 中(此路径为一个绝对路径),若是没有找到则结果为 <VAR>-NOTFOUND。一个相似的命令 link_directories 已经不太建议使用了

15)add_definitions 命令

命令语法:add_definitions(-DFOO -DBAR …)

命令简述:用于添加编译器命令行标志(选项),一般的状况下咱们使用其来添加预处理器定义

使用范例:add_definitions(-D_UNICODE -DUNICODE)

16)execute_process 命令

命令语法:

execute_process(COMMAND <cmd1>[args1...]]

                  [COMMAND <cmd2>[args2...] [...]]

                  [WORKING_DIRECTORY<directory>]

                  [TIMEOUT <seconds>]

                  [RESULT_VARIABLE<variable>]

                  [OUTPUT_VARIABLE<variable>]

                  [ERROR_VARIABLE<variable>]

                  [INPUT_FILE <file>]

                  [OUTPUT_FILE <file>]

                  [ERROR_FILE <file>]

                  [OUTPUT_QUIET]

                  [ERROR_QUIET]

                 [OUTPUT_STRIP_TRAILING_WHITESPACE]

                 [ERROR_STRIP_TRAILING_WHITESPACE])

命令简述:用于执行一个或者多个外部命令。每个命令的标准输出经过管道转为下一个命令的标准输入。WORKING_DIRECTORY 用于指定外部命令的工做目录,RESULT_VARIABLE 用于指定一个变量保存外部命令执行的结果,这个结果多是最后一个执行的外部命令的退出码或者是一个描述错误条件的字符串,OUTPUT_VARIABLE 或者 ERROR_VARIABLE 用于指定一个变量保存标准输出或者标准错误,OUTPUT_QUIET 或者 ERROR_QUIET 用于忽略标准输出和标准错误。

使用范例:execute_process(COMMAND ls)

18)file 命令

命令简述:此命令提供了丰富的文件和目录的相关操做(这里仅说一下比较经常使用的)

使用范例:

# 目录的遍历

# GLOB 用于产生一个文件(目录)路径列表并保存在variable 中

# 文件路径列表中的每一个文件的文件名都能匹配globbing expressions(非正则表达式,可是相似)

# 若是指定了 RELATIVE 路径,那么返回的文件路径列表中的路径为相对于 RELATIVE 的路径

# file(GLOB variable [RELATIVE path][globbing expressions]...)

 

# 获取当前目录下的全部的文件(目录)的路径并保存到 ALL_FILE_PATH 变量中

file(GLOB ALL_FILE_PATH ./*)

# 获取当前目录下的 .h 文件的文件名并保存到ALL_H_FILE 变量中

# 这里的变量CMAKE_CURRENT_LIST_DIR 表示正在处理的 CMakeLists.txt 文件的所在的目录的绝对路径(2.8.3 以及之后版本才支持)

file(GLOB ALL_H_FILE RELATIVE${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/*.h)

19)find_package(name)

在指定的模块目录中搜索一个名为Find<name>.cmake(例如,FindOSG.cmake)的CMake脚本模块文件,执行其中的内容,意图搜索到指定的外部依赖库头文件和库文件位置;

20) include(file):

在当前文件中包含另外一个CMake脚本文件的内容,用来载入CMakeLists.txt文件,也用于载入预约义的cmake模块

21)set_target_properties:

用来设置输出的名称,对于动态库,还能够用来指定动态库版本和API版本

set_target_properties(utils PROPERTIES output_name "utils")

编译项目打包成so文件

1. 借助CMake打.so包

把目录hello_make下的文件打出.so包。

在上述文件里,我将build删除,而后新加了两个文件,so_test.h,so_test.c,内容很简单就是输出一个test,这里不贴代码了。另外main里调用了下test_so函数。

从新编辑CMakeLists.txt

CMAKE_MINIMUM_REQUIRED(VERSION 3.5)
PROJECT(HELLO) #项目名称  
AUX_SOURCE_DIRECTORY(. SRC_LIST) #把当前目录(.)下全部源代码文件和头文件加入变量SRC_LIST
ADD_EXECUTABLE(hello ${SRC_LIST}) #生成应用程序 hello (在windows下会自动生成hello.exe)

add_library(utils SHARED ${SRC_LIST})

set_target_properties(utils PROPERTIES output_name "utils")

注意:前面的关键字能够大写也能够小写,括号内的关键字必须大写。

而后创建build文件目录,进去编译,生成了共享库libutils.so

固然也能够不创建build直接 cmake . 当前目录下生成,可是不建议这样,太乱了。

以下,生成的全部文件

 

 

2. 利用Cython打包整个项目

这里过程很简单,

1.首先安装gcc :sudo apt-get install build-dev gcc 
安装cython :pip3 install Cython

环境部署。

2.编译整个目录: 
将须要编译的目录和setup.py放在同一目录下,执行python3 setup.py

'''
# 打包单个.py文件
from distutils.core import setup
from Cython.Build import cythonize
 
setup(
  name = 'Derain',
  ext_modules = cythonize("derain.py"),
)
'''

#-* -coding: UTF-8 -* -

"""
执行前提:
    系统安装python-devel 和 gcc
    Python安装cython

编译整个当前目录:
    python py-setup.py
编译某个文件夹:
    python py-setup.py BigoModel

生成结果:
    目录 build 下

生成完成后:
    启动文件还须要py/pyc担当,须将启动的py/pyc拷贝到编译目录并删除so文件

"""

import sys, os, shutil, time
from distutils.core import setup
from Cython.Build import cythonize

starttime = time.time()
currdir = os.path.abspath('.')
parentpath = sys.argv[1] if len(sys.argv)>1 else ""
setupfile= os.path.join(os.path.abspath('.'), __file__)
build_dir = "build"
build_tmp_dir = build_dir + "/temp"

def getpy(basepath=os.path.abspath('.'), parentpath='', name='', excepts=(), copyOther=False,delC=False):
    """
    获取py文件的路径
    :param basepath: 根路径
    :param parentpath: 父路径
    :param name: 文件/夹
    :param excepts: 排除文件
    :param copy: 是否copy其余文件
    :return: py文件的迭代器
    """
    fullpath = os.path.join(basepath, parentpath, name)
    for fname in os.listdir(fullpath):
        ffile = os.path.join(fullpath, fname)
        #print basepath, parentpath, name,file
        if os.path.isdir(ffile) and fname != build_dir and not fname.startswith('.'):
            for f in getpy(basepath, os.path.join(parentpath, name), fname, excepts, copyOther, delC):
                yield f
        elif os.path.isfile(ffile):
            ext = os.path.splitext(fname)[1]
            if ext == ".c":
                if delC and os.stat(ffile).st_mtime > starttime:
                    os.remove(ffile)
            elif ffile not in excepts and os.path.splitext(fname)[1] not in('.pyc', '.pyx'):
                if os.path.splitext(fname)[1] in('.py', '.pyx') and not fname.startswith('__'):
                    yield os.path.join(parentpath, name, fname)
                elif copyOther:
                        dstdir = os.path.join(basepath, build_dir, parentpath, name)
                        if not os.path.isdir(dstdir): os.makedirs(dstdir)
                        shutil.copyfile(ffile, os.path.join(dstdir, fname))
        else:
            pass

#获取py列表
module_list = list(getpy(basepath=currdir,parentpath=parentpath, excepts=(setupfile)))
try:
    setup(ext_modules = cythonize(module_list),script_args=["build_ext", "-b", build_dir, "-t", build_tmp_dir])
except Exception as e:
    print (e)
else:
    module_list = list(getpy(basepath=currdir, parentpath=parentpath, excepts=(setupfile), copyOther=True))
module_list = list(getpy(basepath=currdir, parentpath=parentpath, excepts=(setupfile), delC=True))
if os.path.exists(build_tmp_dir): shutil.rmtree(build_tmp_dir)
print ("complete! time:", time.time()-starttime, 's')

上述代码在linux下执行获得.so,在windows下则为pyd文件。

 

其余参考:

cython将py文件编译成so文件

 

 

 

参考文献:

1.https://www.cnblogs.com/AbnerShen/p/7399010.html

2.https://blog.csdn.net/wang4959520/article/details/73550923

3.http://www.javashuo.com/article/p-fihtylck-m.html

4.http://www.cnblogs.com/zhongjiangfeng/p/7478243.html