Quantcast
Channel: Sam的技术Blog
Viewing all articles
Browse latest Browse all 158

CMake使用记录

$
0
0
作者: Sam (甄峰)  sam_code@hotmail.com

最近尝试再次编译OpenCV4Android。因为Android SDK, NDK, Cmake版本问题,意料之中的遇到N多问题。分析和解决问题大都需要修改CMake file. 现记录一些信息如下。

0. CMake基本语法记录: 
0.1. CMake中的赋值:
  1. set(variable value1 value2 value3 ... valueN)
 调用这个命令后,variable变量将是一个列表,其中包含值"value1,value2,...valueN"。

  1. set(myvar "a" "b")
  2. message("${myvar}")
  3. message(${myvar})
此时,控制台中将分别打印出"a;b"和"ab"。这是因为,不带引号时,${myvar}是一个列表,包含了两个值,而message中相当于接收到了两个参数"a"、"b",因此输出"ab"。而带有引号时,引号中的内容整体将作为一个参数存在



0.2.  CMake中循环:
CMake中的循环有两种:foreach()...endforeach()和while()...endwhile()

cmake_minimum_required(VERSION 2.6)
#project (V4L2_Utils)

set(mylist "a" "b" c "d")
foreach(_var ${mylist})
     message("current var: ${_var}")
endforeach()  

#cmake ../ -G"Unix Makefiles"
结果:
current var: a
current var: b
current var: c
current var: d


还有一种比较实用的方法是:foreach(loop_var RANGE start stop [step])
cmake_minimum_required(VERSION 2.6)
#project (V4L2_Utils)

set(mylist "a" "b" c "d")
foreach(_var ${mylist})
     message("current var: ${_var}")
endforeach()   

set(result 0)
foreach(_var RANGE 0 100)
     math(EXPR result "${result}+${_var}")
endforeach()
message("from 0 plus to 100 is:${result}")

#cmake ../ -G"Unix Makefiles"
结果:
current var: a
current var: b
current var: c
current var: d
from 0 plus to 100 is: 5050



0.3.  CMake中使用Maco:

Start recording a macro for later invocation as a command.
开始记录一个宏,未来可以把它当作命令来调用:

macro(< name > [arg1 [arg2 [arg3 ...]]])
  COMMAND1(ARGS ...)
  COMMAND2(ARGS ...)
  ...
endmacro( < name >)

例:

macro(sum outvar)


     set(_args ${ARGN})

message("${ARGN}")


     list(LENGTH _args argLength)


     if(NOT argLength LESS 4)


         message(FATAL_ERROR "to much args!")


     endif()


     set(result 0)


     foreach(_var ${ARGN})


         math(EXPR result "${result}+${_var}")


     endforeach()


     set(${outvar} ${result})


endmacro()


sum(addResult 1 2 3)


message("Result is :${addResult}")

"${ARGN}"是CMake中的一个变量,指代宏中传入的多余参数因为我们这个宏sum中只定义了一个参数"outvar",其余需要求和的数字都是不定形式传入的,所以需要先将多余的参数传入一个单独的变量中



1. OpenCV4Android(OpenCV3.1)编译时遇到问题
1.0: 编译准备工作:
Sam要编译32bit和64bit两个版本,所以创建了两个不同的设置环境变量的脚本:
setenv_32bit, setenv_64bit.

setenv_32bit: 
export ANDROID_NDK=/opt/android-ndk-r14b/
export ANDROID_SDK=/home/sam/Android/Sdk/
export ANDROID_ABI=armeabi-v7a

export ANT_HOME=/usr/share/ant
export PATH=${PATH}:${ANT_HOME}/bin



setenv_64bit:
export ANDROID_NDK=/opt/android-ndk-r14b/
export ANDROID_SDK=/home/sam/Android/Sdk/
export ANDROID_ABI=arm64-v8a

export ANT_HOME=/usr/share/ant
export PATH=${PATH}:${ANT_HOME}/bin

开始编译:
$cd platforms
$sh scripts/cmake_android_arm.sh


1.1: 错误抓取和分析:
CMake Error at platforms/android/android.toolchain.cmake:812 (message):
  Specified Android native API level 'android-8' is not supported by your
  NDK/toolchain.
Call Stack (most recent call first):
  platforms/build_android_arm/CMakeFiles/3.9.1/CMakeSystem.cmake:6 (include)
  CMakeLists.txt:95 (project)

问题解析:
概念1:Android native API level:
在使用NDK编译时,需要指定系统头文件和库所在目录。而Android版本如此之多,要使用哪个版本的头文件和库,就是个问题。
在Application.mk中,可以使用:APP_PLATFORM=android-12 来指定系统头文件和系统库采用NDK/platforms/android-12/下的版本。
那Android native API level就指这个版本。
--sysroot=/opt/android-ndk-r14b/platforms/android-12/arch-arm

如在NDK R10e中,platforms目录包含从android-3到android-21.
在NDK R14中,platforms目录包含从android-9到android-24.

那如果指定的APP_PLATFORM不包含在NDK指定目录内呢?例如:
APP_PLATFORM=android-8, 但又使用NDK R14.  那系统头文件和库该使用哪个呢?
在NDK编译时,它会指定当前最新的版本:
--sysroot=/opt/android-ndk-r14b/platforms/android-24/arch-arm

那问题就清晰了, OpenCV  Cmake 认为我们指定了Android Native API为8。但NDK R14又不支持。
这就奇怪了,我们并没有指定Native API阿。看CMakefile


A:找到报错地点:
list( FIND ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_NATIVE_API_LEVEL}" __levelIdx )
if( __levelIdx EQUAL -1 )
 message( SEND_ERROR "Specified Android native API level 'android-${ANDROID_NATIVE_API_LEVEL}' is not supported by your NDK/toolchain." )

利用list(FIND), 查找list--ANDROID_SUPPORTED_NATIVE_API_LEVELS中是否有${ANDROID_NATIVE_API_LEVEL},如果有,则返回index到 --levelIdx. 
显然,这个list中并没有。

那就需要看哪里得到两个值:
list: ANDROID_SUPPORTED_NATIVE_API_LEVELS
ANDROID_NATIVE_API_LEVEL


B: 找两个变量:
B1:list ANDROID_SUPPORTED_NATIVE_API_LEVELS:
__DETECT_NATIVE_API_LEVEL( 
ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_STANDALONE_TOOLCHAIN}/sysroot/usr/include/android/api-level.h" )
这个宏__DETECT_NATIVE_API_LEVEL()的功能是,从NDK/sysroot/usr/include/android/api-leve文件中,获取它所支持的api-level. 

B2: ANDROID_NATIVE_API_LEVEL:
__INIT_VARIABLE( 
ANDROID_NATIVE_API_LEVEL 
ENV_ANDROID_NATIVE_API_LEVEL 
ANDROID_API_LEVEL 
ENV_ANDROID_API_LEVEL 
ANDROID_STANDALONE_TOOLCHAIN_API_LEVEL ANDROID_DEFAULT_NDK_API_LEVEL_${ANDROID_ARCH_NAME} ANDROID_DEFAULT_NDK_API_LEVEL )

它最终是因为:set( ANDROID_DEFAULT_NDK_API_LEVEL 8 )
而被设置为8.
8: 不包含在9-26的list中,所以报错。

解决方法;
到这一步,解决方法就很明显了:
cmake -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON -DANDROID_NATIVE_API_LEVEL=14 -DCMAKE_TOOLCHAIN_FILE=../android/android.toolchain.cmake $@ ../..






GDB: 
set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")



 

Viewing all articles
Browse latest Browse all 158

Trending Articles