Android Studio ndk 开发 cmake 使用pkgconfig

1使用步骤:

(1)创建安卓工程,拷贝依赖so库/头文件/*.pc到工程目录(这里不详细介绍)

(2)CmakeLists.txt 配置

a)因为要使用pkg-config.exe工具,所以第一步要先下载pkg-config.exe,然后拷贝到工程目录

b)  再设置pkg-config.exe的工具路径,有两种方式设置PKG_CONFIG环境变量或者设置

PKG_CONFIG_EXECUTABLE

c)再设置pkg-config的搜索路径PKG_CONFIG_PATH(也就是搜索*.pc的路径):

set(gst_lib_path ${CMAKE_SOURCE_DIR}/../../../libs/${ANDROID_ABI}/lib)
set(ENV{PKG_CONFIG_PATH} ${gst_lib_path}/pkgconfig:$ENV{PKG_CONFIG_PATH})
set(ENV{PKG_CONFIG} "${CMAKE_SOURCE_DIR}/../../../libs/${ANDROID_ABI}/pkg-config.exe")
#set(PKG_CONFIG_EXECUTABLE "${CMAKE_SOURCE_DIR}/../../../libs/${ANDROID_ABI}/pkg-config.exe")

备注:发现设置的环境变量PKG_CONFIG_PATH没有生效,问题暂存。规避方案使用默认的搜索路径,如下方法可以查找默认搜索路径。

EXECUTE_PROCESS(COMMAND ${PKG_CONFIG_EXECUTABLE} --variable pc_path pkg-config
        TIMEOUT 5
        OUTPUT_VARIABLE out
       OUTPUT_STRIP_TRAILING_WHITESPACE
       )
message("****************** out dirs:" ${out})

d) 查找依赖并获取相应值

(也就是使用pkg-config查询解析对应依赖的*.pc,这里示例gstreamer-1.0.pc)

find_package(PkgConfig REQUIRED)
#include(FindPkgConfig)
pkg_check_modules (TEST REQUIRED gstreamer-1.0 NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH)
pkg_search_module(TEST REQUIRED gstreamer-1.0  NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH)


message("****************** _PREFIX :" ${TEST_PREFIX})
message("****************** _INCLUDEDIR :" ${TEST_INCLUDEDIR})
message("****************** _LIBDIR :" ${TEST_LIBDIR})
message("****************** _CFLAGS :" ${TEST_CFLAGS})
message("*****************  _LIBRARIES :" ${TEST_LIBRARIES})
message("****************** _VERSION :" ${TEST_VERSION})
message("****************** _LIBRARY_DIRS :" ${TEST_LIBRARY_DIRS})
message("****************** _LDFLAGS :" ${TEST_LDFLAGS})
message("****************** _INCLUDE_DIRS :" ${TEST_INCLUDE_DIRS})

备注

 当check成功后,通过我们可以如下XXX_CFLAGS的形式获取对应值(xxx也就是第一个参数 prefix)

pkg_check_modules(<prefix>
                  [REQUIRED] [QUIET]
                  [NO_CMAKE_PATH]
                  [NO_CMAKE_ENVIRONMENT_PATH]
                  [IMPORTED_TARGET [GLOBAL]]
                  <moduleSpec> [<moduleSpec>...])
pkg_search_module(<prefix>
                  [REQUIRED] [QUIET]
                  [NO_CMAKE_PATH]
                  [NO_CMAKE_ENVIRONMENT_PATH]
                  [IMPORTED_TARGET [GLOBAL]]
                  <moduleSpec> [<moduleSpec>...])


The following variables may be set upon return.  Two sets of values exist:
  One for the common case (``<XXX> = <prefix>``) and another for the
  information ``pkg-config`` provides when called with the ``--static``
  option (``<XXX> = <prefix>_STATIC``).

  ``<XXX>_FOUND``
    set to 1 if module(s) exist
  ``<XXX>_LIBRARIES``
    only the libraries (without the '-l')
  ``<XXX>_LINK_LIBRARIES``
    the libraries and their absolute paths
  ``<XXX>_LIBRARY_DIRS``
    the paths of the libraries (without the '-L')
  ``<XXX>_LDFLAGS``
    all required linker flags
  ``<XXX>_LDFLAGS_OTHER``
    all other linker flags
  ``<XXX>_INCLUDE_DIRS``
    the '-I' preprocessor flags (without the '-I')
  ``<XXX>_CFLAGS``
    all required cflags
  ``<XXX>_CFLAGS_OTHER``
    the other compiler flags

  All but ``<XXX>_FOUND`` may be a :ref:`;-list <CMake Language Lists>` if the
  associated variable returned from ``pkg-config`` has multiple values.

  There are some special variables whose prefix depends on the number of
  ``<moduleSpec>`` given.  When there is only one ``<moduleSpec>``,
  ``<YYY>`` will simply be ``<prefix>``, but if two or more ``<moduleSpec>``
  items are given, ``<YYY>`` will be ``<prefix>_<moduleName>``.

  ``<YYY>_VERSION``
    version of the module
  ``<YYY>_PREFIX``
    prefix directory of the module
  ``<YYY>_INCLUDEDIR``
    include directory of the module
  ``<YYY>_LIBDIR``
    lib directory of the module

e) 然后通过设置CMAKE_CXX_FLAGS 等方式,添加头文件以及库的依赖路径:

string(REGEX REPLACE "\;"    " " TEST_LDFLAGS "${TEST_LDFLAGS}")
string(REGEX REPLACE "\;"    " " TEST_CFLAGS "${TEST_CFLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_DEBUG}  ${TEST_LDFLAGS} ${TEST_CFLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS_DEBUG}  ${TEST_LDFLAGS} ${TEST_CFLAGS}")

2遇到的问题:

(1)找不到依赖的头文件,也就是前面说的PKG_CONFIG_PATH设置后无效(暂未解决),使用默认搜索路径,默认路径确认方式如前面所讲。

(2)依赖环境设置ok后,编译报错clang++: error: unknown argument

clang++: error: unknown argument: '-pthread;-IC:/Users/sh/AndroidStudioProjects/demo/app/libs/x86_64/include/gstreamer-1.0;-IC:/Users/sh/AndroidStudioProjects/demo/app/libs/x86_64/include;-IC:/Users/sh/AndroidStudioProjects/demo/app/libs/x86_64/include/glib-2.0'

初看原因是clang++不识别-pthread,通过执行D:\SDK\ndk\21.3.6528147\toolchains\llvm\prebuilt\windows-x86_64\bin\clang++.exe --help(terminal 执行命令)可以查看所支持的参数,确认是支持-pthread,进一步分析其实是分号导致的问题。即使去掉-pthread,还是会出现头文件找不到的编译错误。因为-I 或者 -L后面也都有分号。

那么分号是哪里来的呢??

先看Cmakelists.txt中,我们并没有在命令中加入分号进行拼接,显然这个cmake自动添加的了,那我们就进一步看下cmake使用pkgconfig的逻辑了。

代码路径:

D:\software_install\SDK\cmake\3.18.1\share\cmake-3.18\Modules\FindPkgConfig.cmake

先看下pkg_check_modules:

macro(pkg_check_modules _prefix _module0)
  _pkgconfig_parse_options(_pkg_modules _pkg_is_required _pkg_is_silent _no_cmake_path _no_cmake_environment_path _imp_target _imp_target_global "${_module0}" ${ARGN})
  # check cached value
  if (NOT DEFINED __pkg_config_checked_${_prefix} OR __pkg_config_checked_${_prefix} LESS ${PKG_CONFIG_VERSION} OR NOT ${_prefix}_FOUND OR
      (NOT "${ARGN}" STREQUAL "" AND NOT "${__pkg_config_arguments_${_prefix}}" STREQUAL "${_module0};${ARGN}") OR
      (    "${ARGN}" STREQUAL "" AND NOT "${__pkg_config_arguments_${_prefix}}" STREQUAL "${_module0}"))
    _pkg_check_modules_internal("${_pkg_is_required}" "${_pkg_is_silent}" ${_no_cmake_path} ${_no_cmake_environment_path} ${_imp_target} ${_imp_target_global} "${_prefix}" ${_pkg_modules})
    _pkgconfig_set(__pkg_config_checked_${_prefix} ${PKG_CONFIG_VERSION})
    if (${_prefix}_FOUND)
      _pkgconfig_set(__pkg_config_arguments_${_prefix} "${_module0};${ARGN}")
    endif()
  else()
    if (${_prefix}_FOUND)
      _pkg_recalculate("${_prefix}" ${_no_cmake_path} ${_no_cmake_environment_path} ${_imp_target} ${_imp_target_global})
    endif()
  endif()
endmacro()

继续调用_pkg_check_modules_internal,通过execute_process执行pkgconfig查询命令

也就是 pkg-config  gstreamer-1.0  --exists 查询模块是否存在,然后设置${prefix}_FOUND为1,

这里就找到了xxx_FOUND被设值的地方。

  # execute the query
      execute_process(
        COMMAND ${PKG_CONFIG_EXECUTABLE} ${_pkg_check_modules_exist_query}
        RESULT_VARIABLE _pkgconfig_retval
        ERROR_VARIABLE _pkgconfig_error
        ERROR_STRIP_TRAILING_WHITESPACE)
      # evaluate result and tell failures
      if (_pkgconfig_retval)
        if(NOT ${_is_silent})
          message(STATUS "  ${_pkgconfig_error}")
        endif()

        set(_pkg_check_modules_failed 1)
      endif()
    endforeach()

    if(_pkg_check_modules_failed)
      # fail when requested
      if (${_is_required})
        message(FATAL_ERROR "A required package was not found")
      endif ()
    else()
      # when we are here, we checked whether requested modules
      # exist. Now, go through them and set variables
      _pkgconfig_set(${_prefix}_FOUND 1)
      list(LENGTH _pkg_check_modules_packages pkg_count)

然后我们继续看,pkg_get_variable就是读取pc文件中得值,然后设置给xxx_INCLUDEDIR"
 xxx_LIBDIR等。

  # iterate through all modules again and set individual variables
      foreach (_pkg_check_modules_pkg ${_pkg_check_modules_packages})
        # handle case when there is only one package required
        if (pkg_count EQUAL 1)
          set(_pkg_check_prefix "${_prefix}")
        else()
          set(_pkg_check_prefix "${_prefix}_${_pkg_check_modules_pkg}")
        endif()

        _pkgconfig_invoke(${_pkg_check_modules_pkg} "${_pkg_check_prefix}" VERSION    ""   --modversion )
        pkg_get_variable("${_pkg_check_prefix}_PREFIX" ${_pkg_check_modules_pkg} "prefix")
        pkg_get_variable("${_pkg_check_prefix}_INCLUDEDIR" ${_pkg_check_modules_pkg} "includedir")
        pkg_get_variable("${_pkg_check_prefix}_LIBDIR" ${_pkg_check_modules_pkg} "libdir")
        foreach (variable IN ITEMS PREFIX INCLUDEDIR LIBDIR)
          _pkgconfig_set("${_pkg_check_prefix}_${variable}" "${${_pkg_check_prefix}_${variable}}")
        endforeach ()
          _pkgconfig_set("${_pkg_check_prefix}_MODULE_NAME" "${_pkg_check_modules_pkg}")

        if (NOT ${_is_silent})
          message(STATUS "  Found ${_pkg_check_modules_pkg}, version ${_pkgconfig_VERSION}")
        endif ()
      endforeach()

然后我们继续看下去,这里是读取并设置LDFLAGS CFLAGS等值(set variables which are combined for multiple modules,也就是会进一步去读该pc中要依赖库的pc相应字段并拼接)分号也就是在这个时候引入的。

      # set variables which are combined for multiple modules
      _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" LIBRARIES     "(^| )-l"             --libs-only-l )
      _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" LIBRARY_DIRS  "(^| )-L"             --libs-only-L )
      _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" LDFLAGS       ""                    --libs )
      _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" LDFLAGS_OTHER ""                    --libs-only-other )

     
      if (APPLE AND "-framework" IN_LIST ${_prefix}_LDFLAGS_OTHER)
        _pkgconfig_extract_frameworks("${_prefix}")
      endif()

      _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" INCLUDE_DIRS  "(^| )(-I|-isystem ?)" --cflags-only-I )
      _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" CFLAGS        ""                    --cflags )
      _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" CFLAGS_OTHER  ""                    --cflags-only-other )

我们继续往下看_pkgconfig_invoke_dyn进一步调用_pkgconfig_invoke

macro(_pkgconfig_invoke_dyn _pkglist _prefix _varname cleanup_regexp)
  _pkgconfig_invoke("${_pkglist}" ${_prefix}        ${_varname} "${cleanup_regexp}" ${ARGN})
  _pkgconfig_invoke("${_pkglist}" ${_prefix} STATIC_${_varname} "${cleanup_regexp}" --static  ${ARGN})
endmacro()


# Invokes pkgconfig, cleans up the result and sets variables
macro(_pkgconfig_invoke _pkglist _prefix _varname _regexp)
  set(_pkgconfig_invoke_result)

  execute_process(
    COMMAND ${PKG_CONFIG_EXECUTABLE} ${ARGN} ${_pkglist}
    OUTPUT_VARIABLE _pkgconfig_invoke_result
    RESULT_VARIABLE _pkgconfig_failed
    OUTPUT_STRIP_TRAILING_WHITESPACE)

  message("debug***************" ${_pkgconfig_invoke_result})
  if (_pkgconfig_failed)
    set(_pkgconfig_${_varname} "")
    _pkgconfig_unset(${_prefix}_${_varname})
  else()
    string(REGEX REPLACE "[\r\n]"       " " _pkgconfig_invoke_result "${_pkgconfig_invoke_result}")

    if (NOT ${_regexp} STREQUAL "")

      string(REGEX REPLACE "${_regexp}" " " _pkgconfig_invoke_result "${_pkgconfig_invoke_result}")
    endif()
    message("debug******************" ${_pkgconfig_invoke_result})

    separate_arguments(_pkgconfig_invoke_result)

    message("debug**************" ${_pkgconfig_invoke_result})

    #message(STATUS "  ${_varname} ... ${_pkgconfig_invoke_result}")
    set(_pkgconfig_${_varname} ${_pkgconfig_invoke_result})
    _pkgconfig_set(${_prefix}_${_varname} "${_pkgconfig_invoke_result}")
  endif()
endmacro()

而_pkgconfig_invoke就是通过execute_process调用pkg-config命令完成的,通过打印可以看到

查询后的返回值是空格而不是分号的。

 我们注意到separate_arguments(_pkgconfig_invoke_result),没错正是separate_arguments将空格替换成了分号。下面是separate_arguments后的打印,因为分号需要转义 "\;",所以有了下面打印:

 

至此我们就找到了导致分号引入问题的原因了。

解决方式:如上代码,替换分号为空格

string(REGEX REPLACE "\;"    " " TEST_LDFLAGS "${TEST_LDFLAGS}")
string(REGEX REPLACE "\;"    " " TEST_CFLAGS "${TEST_CFLAGS}")

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值