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}")