C++编程 之 CMake实践

Posted 算法妖怪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++编程 之 CMake实践相关的知识,希望对你有一定的参考价值。

今天讲代码构建,为什么呢?因为包括妖哥我本人发现,研究深度学习机器学习这帮人,以至于部分在校学生都习惯使用python或matlab,很少直接使用C++ /C做完整项目,从而相关的一些操作和技能掌握并不全面。面试算法攻城狮岗位,算法不算法我不知道,前提必须首先是攻城狮,算法是其上锦上添花的东西。所以2021年,我会更多关注于落地项目要求的一些技能,同时在涉及机器学习深度学习方面,除了盘点完基础知识外,会以研究方向,热门论文,数学理论等不同角度写点东西。多看多写多交流。

好了,说CMake,使用起来很简单,在不使用IDE的情况下,我们才使用CMake(www.cmake.org).

本篇文章是在《CMake Practice》一文基础上实践和删减而成。文末附带下载连接。

CMake特点:

1.开源;

2.跨平台(makefile, xcode, MSVC);

3.简化编译构建工程和编译过程,方便管理大型项目;

4.高效,可扩展;

5.简单,也不是很简单,编写过程实际上也是编程过程。

使用建议:

1.如果没有实际需求可以不学;

2.如果简单只有几个文件,可以直接写Makefile;

3.如果是C/C++/java之外的语言可以不学;

4.如果有IDE等非常完备的构建体系,可以不学。


初试CMake

1.准备工作:

创建一个目录t1,在该目录下创建文件main.cpp和CMakeLists.txt(注意大小写):

main.cpp中写入:

#include <iostream>int main(){    std::cout<<"hello world from CMakelist."<<std::endl; return 0;}

CMakeLists.txt内容:

PROJECT(HELLO)
SET(SRC_LIST main.cpp)
MESSAGE(STATUS "This is BINARY dir "${HELLO_BINARY_DIR})
MESSAGE(STATUS "This is SOURCE dir "${HELLO_SOURCE_DIR})
ADD_EXECUTABLE(hello ${SRC_LIST})


2.开始构建

在t1目录下 cmake .

可见生成了:CMakeFiles, CMakeCache.txt, cmake_install.cmake, Makefile等文件.

再 make

生成hello 二进制执行文件。

运行 ./hello

得到输出 hello world from CMakelist.

如果需要看到make构建的详细过程,可以使用 make VERBOSE=1或者VERBOSE=1 make 进行构建。


3.简单解释

CMakeLists.txt 是CMake的构建定义文件,如果工程存在多个目录,可以每个目录都存在一个CMakeLists.txt。看其内容:

PROJECT(projectname [CXX] [C] [Java])

定义工程名称,并可指定工程支持的语言(可忽略)。这个指令隐式定义两个cmake变量:

<projectname>_BINARY_DIR 以及<projectname>_SOURCE_DIR

同时 cmake 系统也帮助我们预定义了 PROJECT_BINARY_DIR 和 PROJECT_SOURCE_DIR 变量,他们的值分别跟 <projectname>_BINARY_DIR  和<projectname>_SOURCE_DIR 一致。

SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])

现阶段,你只需要了解 SET 指令可以用来显式的定义变量即可。


MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display"

...)

这个指令用于向终端输出用户定义的信息,包含了三种类型:

SEND_ERROR,产生错误,生成过程被跳过;

SATUS,输出前缀为—的信息;

FATAL_ERROR,立即终止所有 cmake 过程.。


ADD_EXECUTABLE(hello ${SRC_LIST})

定义了这个工程会生成一个文件名为 hello 的可执行文件,相关的源文件是 SRC_LIST 中定义的源文件列表, 本例中你也可以直接写成 ADD_EXECUTABLE(hello main.cpp)。


将本例改写成一个最简化的 CMakeLists.txt:

PROJECT(HELLO)

ADD_EXECUTABLE(hello main.cpp)


4.基本语法规则

变量使用${}方式取值,但是在 IF 控制语句中是直接使用变量名;

指令(参数 1 参数 2...) 参数使用括弧括起,参数之间使用空格或分号分开;

指令是大小写无关的,参数和变量是大小写相关的。推荐全部使用大写指令;

这里需要特别解释的是作为工程名的 HELLO 和生成的可执行文件 hello 是没有任何关系的。hello 定义了可执行文件的文件名,你完全可以写成:ADD_EXECUTABLE(t1 main.cpp)

编译后会生成一个 t1 可执行文件。


5.语法的疑惑

cmake 的语法还是比较灵活而且考虑到各种情况,比如 SET(SRC_LIST main.cpp)也可以写成 SET(SRC_LIST “main.cpp”) 是没有区别的,但是假设一个源文件的文件名是 fu nc.cpp(文件名中间包含了空格)。这时候就必须使用双引号,如果写成了 SET(SRC_LIST fu nc.cpp),就会出现错误,提示你找不到 fu 文件和 nc.cpp 文件。这种情况,就必须写成: SET(SRC_LIST “fu nc.cpp”)

此外,你可以可以忽略掉 source 列表中的源文件后缀,比如可以写成 ADD_EXECUTABLE(t1 main),cmake 会自动的在本目录查找 main.c 或者 main.cpp 等,当然,最好不要偷这个懒,以免这个目录确实存在一个 main.cpp, 一个 main.

同时参数也可以使用分号来进行分割。下面的例子也是合法的:

ADD_EXECUTABLE(t1 main.c t1.cpp)可以写成 ADD_EXECUTABLE(t1 main.c;t1.cpp).

MESSAGE(STATUS “This is BINARY dir” ${HELLO_BINARY_DIR}) 也可以写成:MESSAGE(STATUS “This is BINARY dir ${HELLO_BINARY_DIR}”)


6.清理工程

make clean


7.内部构建和外部构建

上面的例子展示的是“内部构建”,相信看到生成的临时文件比您的代码文件还要多的时候,估计这辈子你都不希望再使用内部构建。

对于 cmake,内部编译上面已经演示过了,它生成了一些无法自动删除的中间文件,所以,引出了我们对外部编译的探讨,外部编译的过程如下:

首先,清除 t1 目录中除 main.cpp CmakeLists.txt 之外的所有中间文件,最关键的是 CMakeCache.txt。

在 t1 目录中建立 build 目录,当然你也可以在任何地方建立 build 目录,不一定必须在工程目录中。

进入 build 目录,运行 cmake ..(注意,..代表父目录,因为父目录存在我们需要的CMakeLists.txt,如果你在其他地方建立了 build 目录,需要运行 cmake <工程的全路径>),查看一下 build 目录,就会发现了生成了编译需要的 Makefile 以及其他的中间 文件.

运行 make 构建工程,就会在当前目录(build 目录)中获得目标文件 hello。

上述过程就是所谓的 out-of-source 外部编译,一个最大的好处是,对于原有的工程没 有任何影响,所有动作全部发生在编译目录。

这里需要特别注意的是:通过外部编译进行工程构建,HELLO_SOURCE_DIR 仍然指代工程路径,即

XXX/t1,而 HELLO_BINARY_DIR 则指代编译路径,即XXX /t1/build。


外部构建与安装

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

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

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

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

在工程目录添加文本文件 COPYRIGHT, README;

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

将构建后的目标文件放入构建目录的 bin 子目录;

最终安装这些文件:将 hello 二进制与 runhello.sh 安装至/usr/bin,将 doc 目录 的内容以及 COPYRIGHT/README 安装到/usr/share/doc/cmake/t2。


1.准备工作

创建 t2目录,将t1中的main.cpp和CMakeLists.txt拷贝到t2中;

添加子目录src,将main.cpp移动到src 目录中,在src 中创建一个CMakeLists.txt,编写内容如下:

ADD_EXECUTABLE(hello main.cpp);

将t2目录下的所谓工程CMakeLists.txt内容修改为:

PROJECT(HELLO)ADD_SUBDIRECTORY(src bin)

然后建立build目录,进入该目录,执行

cmake ..

make

生成的文件hello位于 build/bin目录中。


2.语法解释

ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

这个指令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置。EXCLUDE_FROM_ALL 参数的含义是将这个目录从编译过程中排除。上面的例子定义了将 src 子目录加入工程,并指定编译输出(包含编译中间结果)路径为 bin 目录。如果不进行 bin 目录的指定,那么编译结果(包括中间结果)都将存放在 build/src 目录(这个目录跟原有的 src 目录对应),指定 bin 目录后,相当于在编译时将 src 重命名为 bin,所有的中间结果和目标二进制都将存放在 bin 目录。


3.另存为目标二进制

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目录。应该把这两条指令写在工程的 CMakeLists.txt 还是 src 目录下的 CMakeLists.txt?,把握一个简单的原则,在哪里 ADD_EXECUTABLE 或 ADD_LIBRARY,如果需要改变目标存放路径,就在哪里加入上述的定义。


4.如何安装

需要引入一个新的 cmake 指令 INSTALL 和一个非常有用的变量 CMAKE_INSTALL_PREFIX。

CMAKE_INSTALL_PREFIX 变量类似于 configure 脚本的 –prefix,常见的使用方法看起来是这个样子:

cmake -DCMAKE_INSTALL_PREFIX=/usr .

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 GROUP_EXECUTE GROUP_READ.


5  修改 Helloworld 支持安装

添加目录和文件:t2/doc  t2/doc/hello.txt  t2/runhello.sh  t2/COPYRIGHT  t2/README

安装 COPYRIGHT/README,直接修改主工程文件 CMakelists.txt,加入以下指令:

INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/t2)

安装 runhello.sh,直接修改主工程文件 CMakeLists.txt,加入如下指令:

INSTALL(PROGRAMS runhello.sh DESTINATION bin)

因为 hello.txt 要安装到/<prefix>/share/doc/cmake/t2,所以我们不能直接安装整个 doc 目录,这里采用的方式是安装 doc 目录中的内容,也就是使用”doc/”在工程文件中添加:

INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake/t2)


6 make & install

cmake -DCMAKE_INSTALL_PREFIX=/tmp/t2/usr ..

make

make install

如果我没有定义 CMAKE_INSTALL_PREFIX 会安装到什么地方?你可以尝试以下,cmake ..;make;make install,你会发现CMAKE_INSTALL_PREFIX 的默认定义是/usr/local。



静态库与共享库构建

1.准备工作

创建t3目录,再创建lib目录,CMakeLists.txt,CMakeLists.txt内容:

PROJECT(HELLOLIB)

ADD_SUBDIRECTORY(lib)

在lib目录下创建hello.cpp和hello.h:

hello.cpp内容:

#include "hello.h"void HelloFunc(){ std::cout<<"hello world ! "<<std::endl;}

hello.h内容:

#ifndef HELLO_H#define HELLO_H#include <iostream>void HelloFunc();#endif

在lib目录下创建CMakeLists.txt,内容:

SET(LIBHELLO_SRC hello.cpp)

ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})


2.编译共享库(动态库)

仍然采用 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(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 参数的意思是这个库不会被默认构建,除非有其他的组件依赖或者手工构建。


3.添加静态库

ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})

然后再在 build 目录进行外部编译,我们会发现,静态库根本没有被构建,仍然只生成了一个动态库。因为 hello 作为一个 target 是不能重名的,所以,静态库构建指令无效。

修改为 ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC}) 就可以构建一个 libhello_static.a 的静态库了。但是名字改变了。

SET_TARGET_PROPERTIES(target1 target2 ...

PROPERTIES prop1 value1

prop2 value2 ...)

这条指令可以用来设置输出的名称,对于动态库,还可以用来指定动态库版本和 API 版本。在本例中,我们需要作的是向 lib/CMakeLists.txt 中添加一条:SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello") 这样,我们就可以同时得到 libhello.so/libhello.a 两个库了。与他对应的指令是:GET_TARGET_PROPERTY(VAR target property) 。

libhello.a 已经构建完成,位于 build/lib 目录中,但是 libhello.so 去消失了。这个问题的原因是:cmake 在构建一个新的 target 时,会尝试清理掉其他使用这个名字的库,因为,在构建 libhello.a 时, 就会清理掉libhello.so.

为了回避这个问题,比如再次使用 SET_TARGET_PROPERTIES 定义

CLEAN_DIRECT_OUTPUT 属性。向 lib/CMakeLists.txt 中添加:

SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)

SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)

重新构建,会发现 build/lib 目录中同时生成了 libhello.so 和 libhello.a 。



4.动态库版本号

SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)

VERSION 指代动态库版本,SOVERSION 指代 API 版本。将上述指令加入 lib/CMakeLists.txt 中,重新构建看看结果。在 build/lib 目录会生成:

libhello.so.1.2

libhello.so.1->libhello.so.1.2

libhello.so ->libhello.so.1


5.安装共享库和头文件

我们向 lib/CMakeLists.txt 中添加如下指令:

INSTALL(TARGETS hello hello_staticLIBRARY DESTINATION lib

ARCHIVE DESTINATION lib)

INSTALL(FILES hello.h DESTINATION include/hello)

注意,静态库要使用 ARCHIVE 关键字通过:

cmake -DCMAKE_INSTALL_PREFIX=/usr ..

make

make install

我们就可以将头文件和共享库安装到系统目录/usr/lib 和/usr/include/hello 中了。


如何调用外部共享库和头文件

上一节我们已经完成了 libhello 动态库的构建以及安装,本节我们的任务很简单:编写一个程序使用我们上一节构建的共享库。

1.准备工作

创建t4目录,创建src子目录,在该目录下编写 main.cpp,内容如下:

#include <hello.h>int main(){    HelloFunc(); return 0;}

t4目录下编写工程CMakeLists.txt:

PROJECT(NEWHELLO)ADD_SUBDIRECTORY(src)

编写src/CMakeLists.txt:

ADD_EXECUTABLE(main main.cpp)


2.外部构建

构建失败,如果需要查看细节,make VERBOSE=1 来构建,错误为找不到hello.h。

为了让我们的工程能够找到 hello.h 头文件,我们需要引入一个新的指令 INCLUDE_DIRECTORIES,其完整语法为:

INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)

这条指令可以用来向工程添加多个特定的头文件搜索路径,路径之间用空格分割,如果路径中包含了空格,可以使用双引号将它括起来,默认的行为是追加到当前的头文件搜索路径的后面,你可以通过两种方式来进行控制搜索路径添加的方式:

CMAKE_INCLUDE_DIRECTORIES_BEFORE,通过 SET 这个 cmake 变量为 on,可以将添加的头文件搜索路径放在已有路径的前面。

通过 AFTER 或者 BEFORE 参数,也可以控制是追加还是置前。

现在我们在 src/CMakeLists.txt 中添加一个头文件搜索路径,方式很简单,加入:

INCLUDE_DIRECTORIES(/usr/include/hello)

重新构建,出现新错误,链接不上HelloFunc,因为我们没有link到共享库libhello.so。

TARGET_LINK_LIBRARIES(target library1

<debug | optimized> library2

...)

这个指令可以用来为 target 添加需要链接的共享库,本例中是一个可执行文件,但是同样可以用于为自己编写的共享库添加共享库链接。src/CMakeLists.txt 中添加如下指令:

TARGET_LINK_LIBRARIES(main hello)

也可以写成

TARGET_LINK_LIBRARIES(main libhello.so)

这里的 hello 指的是我们上一节构建的共享库 libhello.

让我们来检查一下 main 的链接情况:

ldd src/main

linux-gate.so.1 => (0xb7ee7000)

libhello.so.1 => /usr/lib/libhello.so.1 (0xb7ece000)

libc.so.6 => /lib/libc.so.6 (0xb7d77000)

/lib/ld-linux.so.2 (0xb7ee8000)

可以清楚的看到 main 确实链接了共享库 libhello,而且链接的是动态库 libhello.so.1。

那如何链接到静态库呢?

方法很简单:

将 TARGET_LINK_LIBRRARIES 指令修改为:

TARGET_LINK_LIBRARIES(main libhello.a)

ldd src/main

linux-gate.so.1 => (0xb7fa8000)

libc.so.6 => /lib/libc.so.6 (0xb7e3a000)

/lib/ld-linux.so.2 (0xb7fa9000)


3.特殊的环境变量 CMAKE_INCLUDE_PATH 和 CMAKE_LIBRARY_PATH

务必注意,这两个是环境变量而不是 cmake 变量。使用方法是要在 bash 中用 export 或者在 csh 中使用 set 命令设置或者 CMAKE_INCLUDE_PATH=/home/include cmake ..等方式。

以上是4个demo说明一些使用场景,基本够用了。至于CMake其他常用命令和常用变量,常用环境变量,请详见 《Cmake Practice》,这里不再介绍。

这里妖哥给出一个自己在实际使用的例子(android):

cmake_minimum_required(VERSION 3.4.1)
include_directories( ${CMAKE_SOURCE_DIR}/libs/native/XXX/ )#eigeninclude_directories(${CMAKE_SOURCE_DIR}/libs/native/eigen3)
# mnninclude_directories(${CMAKE_SOURCE_DIR}/libs/native/MNN)
# opencvinclude_directories(${CMAKE_SOURCE_DIR}/libs/native/opencv/jni/include)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fvisibility-inlines-hidden -fomit-frame-pointer -fstrict-aliasing -ffunction-sections -fdata-sections -ffast-math -fno-rtti -fno-exceptions -flax-vector-conversions")
find_package (Threads)
# opencvset(LIBOPENCV_DIR ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI})add_library(opencv_java3 SHARED IMPORTED)set_target_properties(opencv_java3 PROPERTIES IMPORTED_LOCATION ${LIBOPENCV_DIR}/libopencv_java3.so)
add_library( mnn SHARED IMPORTED )set_target_properties( mnn PROPERTIES IMPORTED_LOCATION ${LIBOPENCV_DIR}/libMNN.so)
add_library( mnn_cl SHARED IMPORTED )set_target_properties( mnn_cl PROPERTIES IMPORTED_LOCATION ${LIBOPENCV_DIR}/libMNN_CL.so)
add_library( XXX SHARED src/main/cpp/config.cpp src/main/cpp/XXX.cpp ... )
find_library( log-lib log )
target_link_libraries( XXX mnn mnn_cl opencv_java3 m z gomp ${log-lib} )

《Cmake Practice》相关链接:

链接:https://pan.baidu.com/s/1Ufq0VQK0CkeWvmQbsk2Y2g 提取码:hz5t


以上是关于C++编程 之 CMake实践的主要内容,如果未能解决你的问题,请参考以下文章

Linux C与C++一线开发实践之六 多线程高级编程

Linux C与C++一线开发实践之六 多线程高级编程

Linux C与C++一线开发实践之五 多线程基本编程

Linux C与C++一线开发实践之五 多线程基本编程

面向面试编程代码片段之GC

ROS系统C++代码测试之gtest