将 Android 项目(未定义引用)中的 Boost::regex 与 crystax ndk 链接时出错
Error linking Boost::regex in Android Project (undefined reference) with crystax ndk
我目前正尝试在 Android Studio 上的 Android 本机库中使用 boost::regex 库。但是我得到了这两个 linker 错误:
F:\Tools\dev\Android\ndks\crystax-ndk-10.3.2/sources/boost/1.64.0/include/boost/regex/v4/regex_search.hpp:56: error: undefined reference to 'boost::re_detail_106400::perl_matcher<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<boost::sub_match<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, boost::regex_traits<char, boost::cpp_regex_traits<char> > >::find()'
F:\Tools\dev\Android\ndks\crystax-ndk-10.3.2/sources/boost/1.64.0/include/boost/regex/v4/perl_matcher.hpp:383: error: undefined reference to 'boost::re_detail_106400::perl_matcher<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<boost::sub_match<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, boost::regex_traits<char, boost::cpp_regex_traits<char> > >::construct_init(boost::basic_regex<char, boost::regex_traits<char, boost::cpp_regex_traits<char> > > const&, boost::regex_constants::_match_flags)'
我正在使用 crystax-ndk (10.3.2) 编译我的本机 c++ 代码。
boost 库是 1.64.0 版,我使用来自 here.
的预编译静态库
我的 build.gradle 看起来像这样:
import org.gradle.internal.os.OperatingSystem
apply plugin: 'com.android.model.application'
final APP_ABIS = ["armeabi", "armeabi-v7a", "x86"]
final BOOST_STATIC_LIBS = ["boost_system", "boost_filesystem", "boost_thread", "boost_regex", "boost_iostreams", "boost_log_setup", "boost_log"]
final OPENSSL_SHARED_LIBS = ["ssl", "crypto"]
model {
android {
compileSdkVersion = 23
buildToolsVersion = "25.0.2"
defaultConfig.with {
applicationId = "de.linux13524.youtube_list_downloader"
minSdkVersion.apiLevel = 17
targetSdkVersion.apiLevel = compileSdkVersion as Integer
versionCode = 1
versionName = "1.0"
}
}
android.ndk {
toolchain = "gcc"
toolchainVersion = "5"
moduleName = "native-lib"
cppFlags.add("-std=c++11")
cppFlags.add("-fexceptions")
cppFlags.add("-frtti")
cppFlags.add("-DANDROID")
cppFlags.add("-I" + getBoostIncDir())
cppFlags.add("-I" + getOpenSSLIncDir())
cppFlags.add("-I" + file("../../lib").absolutePath)
cppFlags.add("-I" + file("../../lib/nowide_standalone").absolutePath)
cppFlags.add("-I" + file("../../include").absolutePath)
ldLibs.addAll BOOST_STATIC_LIBS
ldLibs.addAll OPENSSL_SHARED_LIBS
ldLibs.add("log")
stl = "gnustl_shared"
}
android.buildTypes {
release {
minifyEnabled = false
proguardFiles.add(file('proguard-rules.pro'))
}
}
android.productFlavors {
APP_ABIS.each { abi ->
create(getFlavorName(abi)) {
ndk.with {
abiFilters.add(abi)
getPrebuiltLibPaths(abi).each { path ->
ldFlags.add("-L" + path)
}
}
}
}
}
android.sources {
main {
jni {
source {
srcDirs = ["/src/main/jni".toString(),
"../../src".toString(),
"../../include".toString(),
"../../lib".toString()]
}
}
}
}
}
tasks.all {
task ->
if (task.name.startsWith('link')) {
task.dependsOn copyNativeLibs, stripNativeLibs
}
}
task copyNativeLibs {
["debug", "release"].each { buildType ->
APP_ABIS.each { abi ->
def libs = [:]
BOOST_STATIC_LIBS.each { name ->
libs[name] = "${getBoostLibDir(abi)}/lib${name}.a"
}
OPENSSL_SHARED_LIBS.each { name ->
libs[name] = "${getOpenSSLLibDir(abi)}/lib${name}.so"
}
libs.crystax = getLibCrystax(abi)
libs.each { name, file ->
dependsOn tasks.create(name: "copy-native-library-${name}-${abi}-${buildType}", type: Copy) {
from file
into getTargetLibDir(abi, buildType)
}
}
}
}
}
task stripNativeLibs(dependsOn: copyNativeLibs) {
["debug", "release"].each { buildType ->
APP_ABIS.each { abi ->
def libs_static = []
def libs_dynamic = []
libs_static.addAll(BOOST_STATIC_LIBS)
libs_dynamic.addAll(OPENSSL_SHARED_LIBS)
libs_dynamic.add("crystax")
libs_static.each { name ->
dependsOn tasks.create(name: "strip-native-library-${name}-${abi}-${buildType}", type: Exec) {
commandLine getStripExecutable(abi), "--strip-unneeded", "${getTargetLibDir(abi, buildType)}/lib${name}.a"
}
}
libs_dynamic.each { name ->
dependsOn tasks.create(name: "strip-native-library-${name}-${abi}-${buildType}", type: Exec) {
commandLine getStripExecutable(abi), "--strip-unneeded", "${getTargetLibDir(abi, buildType)}/lib${name}.so"
}
}
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:design:23.4.0'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
}
def getNdkDir() {
if (System.env.ANDROID_NDK_ROOT != null)
return System.env.ANDROID_NDK_ROOT
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
def ndkdir = properties.getProperty('ndk.dir', null)
if (ndkdir == null)
throw new GradleException("""\
NDK location not found.
Define location with ndk.dir in the local.properties file
or with an ANDROID_NDK_ROOT environment variable.""")
return ndkdir
}
def getCrystaxNdkDir() {
def ndkDir = getNdkDir()
if (!(new File(ndkDir, "sources/crystax").exists()))
throw new GradleException("""\
'${ndkDir}' is not a CrystaX NDK.
Edit ndk.dir in local.properties or set ANDROID_NDK_ROOT
environment variable pointing to CrystaX NDK""")
return ndkDir
}
static def getFlavorName(abi) {
switch (abi) {
case "armeabi":
return "arm"
case "armeabi-v7a":
return "arm7"
case "arm64-v8a":
return "arm64"
default:
return abi.replaceAll('-', '_')
}
}
static def getToolchainName(abi) {
switch (abi) {
case ~/^armeabi.*/:
return "arm-linux-androideabi"
case ~/^arm64.*/:
return "aarch64-linux-android"
case "mips":
return "mipsel-linux-android"
case "mips64":
return "mips64el-linux-android"
case ["x86", "x86_64"]:
return abi
default:
throw new GradleException("Unsupported ABI: '${abi}'")
}
}
static def getToolchainPrefix(abi) {
switch (abi) {
case ~/^armeabi.*/:
return "arm-linux-androideabi"
case ~/^arm64.*/:
return "aarch64-linux-android"
case "mips":
return "mipsel-linux-android"
case "mips64":
return "mips64el-linux-android"
case "x86":
return "i686-linux-android"
case "x86_64":
return "x86_64-linux-android"
default:
throw new GradleException("Unsupported ABI: '${abi}'")
}
}
static def getHostOS() {
if (OperatingSystem.current().isLinux())
return "linux"
if (OperatingSystem.current().isMacOsX())
return "darwin"
if (OperatingSystem.current().isWindows())
return "windows"
throw new GradleException("Unsupported host OS")
}
static def getHostArch() {
def arch = System.getProperty("os.arch")
switch (arch) {
case ["x86_64", "amd64"]:
return "x86_64"
case ~/^i[3456]86/:
case "x86":
return "x86"
default:
throw new GradleException("Can't detect host's CPU architecture: '${arch}'")
}
}
static def getHostTag() {
def tag = getHostOS()
def arch = getHostArch()
if (tag != "windows" || arch != "x86")
tag += "-${arch}"
return tag
}
def getStripExecutable(abi) {
def ndk = getCrystaxNdkDir()
def toolchainName = getToolchainName(abi)
def toolchainPrefix = getToolchainPrefix(abi)
def hostTag = getHostTag()
def strip = "${ndk}/toolchains/${toolchainName}-5/prebuilt/${hostTag}/bin/${toolchainPrefix}-strip"
if (OperatingSystem.current().isWindows())
strip = strip.replaceAll('/', '\\') + '.exe'
return strip
}
def getPrebuiltLibPaths(abi) {
def paths = []
paths += getBoostLibDir(abi)
paths += getOpenSSLLibDir(abi)
paths += getLibCrystaxDir(abi)
return paths
}
def getTargetLibDir(abi, buildType) {
return "${buildDir}/intermediates/binaries/${buildType}/${getFlavorName(abi)}/lib/${abi}"
}
def getLibCrystaxDir(abi) {
return "${getCrystaxNdkDir()}/sources/crystax/libs/${abi}"
}
def getLibCrystax(abi) {
return "${getLibCrystaxDir(abi)}/libcrystax.so"
}
def getBoostDir() {
return "${getCrystaxNdkDir()}/sources/boost/1.64.0"
}
def getBoostIncDir() {
return "${getBoostDir()}/include"
}
def getBoostLibDir(abi) {
return "${getBoostDir()}/${abi}/lib"
}
def getOpenSSLDir() {
return "${getCrystaxNdkDir()}/sources/openssl/1.0.2h"
}
def getOpenSSLIncDir() {
return "${getOpenSSLDir()}/include"
}
def getOpenSSLLibDir(abi) {
return "${getOpenSSLDir()}/libs/${abi}"
}
我将 boost_regex
添加到 link (BOOST_STATIC_LIBS
) 的库列表中。
除了正则表达式部分 (regex_search
),我还在使用 http 的 asio 部分、文件系统部分和日志记录部分,没有任何 (linker) 问题。
当我用 nm --demangle libboost_regex.a | grep perl_matcher | grep regex_traits | grep "find()"
检查 libboost_regex.a 库时,"undefined reference" 显示在结果列表中。 (另一个参考也在列表中)
那么这里可能是什么问题呢?我错过了什么重要的东西吗?
我通过使用 CMake 从 crystax 更改为 Android NDK r16b 并将 std
库设置为 c++11
.
来解决我的问题
在build.gradle
中:
...
externalNativeBuild {
cmake {
cppFlags "-std=c++11 -frtti -fexceptions"
arguments "-DANDROID_STL=c++_static"
}
}
...
现在 Boost 库(更新到版本 1.66)link 没有问题。
我目前正尝试在 Android Studio 上的 Android 本机库中使用 boost::regex 库。但是我得到了这两个 linker 错误:
F:\Tools\dev\Android\ndks\crystax-ndk-10.3.2/sources/boost/1.64.0/include/boost/regex/v4/regex_search.hpp:56: error: undefined reference to 'boost::re_detail_106400::perl_matcher<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<boost::sub_match<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, boost::regex_traits<char, boost::cpp_regex_traits<char> > >::find()'
F:\Tools\dev\Android\ndks\crystax-ndk-10.3.2/sources/boost/1.64.0/include/boost/regex/v4/perl_matcher.hpp:383: error: undefined reference to 'boost::re_detail_106400::perl_matcher<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<boost::sub_match<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, boost::regex_traits<char, boost::cpp_regex_traits<char> > >::construct_init(boost::basic_regex<char, boost::regex_traits<char, boost::cpp_regex_traits<char> > > const&, boost::regex_constants::_match_flags)'
我正在使用 crystax-ndk (10.3.2) 编译我的本机 c++ 代码。 boost 库是 1.64.0 版,我使用来自 here.
的预编译静态库我的 build.gradle 看起来像这样:
import org.gradle.internal.os.OperatingSystem
apply plugin: 'com.android.model.application'
final APP_ABIS = ["armeabi", "armeabi-v7a", "x86"]
final BOOST_STATIC_LIBS = ["boost_system", "boost_filesystem", "boost_thread", "boost_regex", "boost_iostreams", "boost_log_setup", "boost_log"]
final OPENSSL_SHARED_LIBS = ["ssl", "crypto"]
model {
android {
compileSdkVersion = 23
buildToolsVersion = "25.0.2"
defaultConfig.with {
applicationId = "de.linux13524.youtube_list_downloader"
minSdkVersion.apiLevel = 17
targetSdkVersion.apiLevel = compileSdkVersion as Integer
versionCode = 1
versionName = "1.0"
}
}
android.ndk {
toolchain = "gcc"
toolchainVersion = "5"
moduleName = "native-lib"
cppFlags.add("-std=c++11")
cppFlags.add("-fexceptions")
cppFlags.add("-frtti")
cppFlags.add("-DANDROID")
cppFlags.add("-I" + getBoostIncDir())
cppFlags.add("-I" + getOpenSSLIncDir())
cppFlags.add("-I" + file("../../lib").absolutePath)
cppFlags.add("-I" + file("../../lib/nowide_standalone").absolutePath)
cppFlags.add("-I" + file("../../include").absolutePath)
ldLibs.addAll BOOST_STATIC_LIBS
ldLibs.addAll OPENSSL_SHARED_LIBS
ldLibs.add("log")
stl = "gnustl_shared"
}
android.buildTypes {
release {
minifyEnabled = false
proguardFiles.add(file('proguard-rules.pro'))
}
}
android.productFlavors {
APP_ABIS.each { abi ->
create(getFlavorName(abi)) {
ndk.with {
abiFilters.add(abi)
getPrebuiltLibPaths(abi).each { path ->
ldFlags.add("-L" + path)
}
}
}
}
}
android.sources {
main {
jni {
source {
srcDirs = ["/src/main/jni".toString(),
"../../src".toString(),
"../../include".toString(),
"../../lib".toString()]
}
}
}
}
}
tasks.all {
task ->
if (task.name.startsWith('link')) {
task.dependsOn copyNativeLibs, stripNativeLibs
}
}
task copyNativeLibs {
["debug", "release"].each { buildType ->
APP_ABIS.each { abi ->
def libs = [:]
BOOST_STATIC_LIBS.each { name ->
libs[name] = "${getBoostLibDir(abi)}/lib${name}.a"
}
OPENSSL_SHARED_LIBS.each { name ->
libs[name] = "${getOpenSSLLibDir(abi)}/lib${name}.so"
}
libs.crystax = getLibCrystax(abi)
libs.each { name, file ->
dependsOn tasks.create(name: "copy-native-library-${name}-${abi}-${buildType}", type: Copy) {
from file
into getTargetLibDir(abi, buildType)
}
}
}
}
}
task stripNativeLibs(dependsOn: copyNativeLibs) {
["debug", "release"].each { buildType ->
APP_ABIS.each { abi ->
def libs_static = []
def libs_dynamic = []
libs_static.addAll(BOOST_STATIC_LIBS)
libs_dynamic.addAll(OPENSSL_SHARED_LIBS)
libs_dynamic.add("crystax")
libs_static.each { name ->
dependsOn tasks.create(name: "strip-native-library-${name}-${abi}-${buildType}", type: Exec) {
commandLine getStripExecutable(abi), "--strip-unneeded", "${getTargetLibDir(abi, buildType)}/lib${name}.a"
}
}
libs_dynamic.each { name ->
dependsOn tasks.create(name: "strip-native-library-${name}-${abi}-${buildType}", type: Exec) {
commandLine getStripExecutable(abi), "--strip-unneeded", "${getTargetLibDir(abi, buildType)}/lib${name}.so"
}
}
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:design:23.4.0'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
}
def getNdkDir() {
if (System.env.ANDROID_NDK_ROOT != null)
return System.env.ANDROID_NDK_ROOT
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
def ndkdir = properties.getProperty('ndk.dir', null)
if (ndkdir == null)
throw new GradleException("""\
NDK location not found.
Define location with ndk.dir in the local.properties file
or with an ANDROID_NDK_ROOT environment variable.""")
return ndkdir
}
def getCrystaxNdkDir() {
def ndkDir = getNdkDir()
if (!(new File(ndkDir, "sources/crystax").exists()))
throw new GradleException("""\
'${ndkDir}' is not a CrystaX NDK.
Edit ndk.dir in local.properties or set ANDROID_NDK_ROOT
environment variable pointing to CrystaX NDK""")
return ndkDir
}
static def getFlavorName(abi) {
switch (abi) {
case "armeabi":
return "arm"
case "armeabi-v7a":
return "arm7"
case "arm64-v8a":
return "arm64"
default:
return abi.replaceAll('-', '_')
}
}
static def getToolchainName(abi) {
switch (abi) {
case ~/^armeabi.*/:
return "arm-linux-androideabi"
case ~/^arm64.*/:
return "aarch64-linux-android"
case "mips":
return "mipsel-linux-android"
case "mips64":
return "mips64el-linux-android"
case ["x86", "x86_64"]:
return abi
default:
throw new GradleException("Unsupported ABI: '${abi}'")
}
}
static def getToolchainPrefix(abi) {
switch (abi) {
case ~/^armeabi.*/:
return "arm-linux-androideabi"
case ~/^arm64.*/:
return "aarch64-linux-android"
case "mips":
return "mipsel-linux-android"
case "mips64":
return "mips64el-linux-android"
case "x86":
return "i686-linux-android"
case "x86_64":
return "x86_64-linux-android"
default:
throw new GradleException("Unsupported ABI: '${abi}'")
}
}
static def getHostOS() {
if (OperatingSystem.current().isLinux())
return "linux"
if (OperatingSystem.current().isMacOsX())
return "darwin"
if (OperatingSystem.current().isWindows())
return "windows"
throw new GradleException("Unsupported host OS")
}
static def getHostArch() {
def arch = System.getProperty("os.arch")
switch (arch) {
case ["x86_64", "amd64"]:
return "x86_64"
case ~/^i[3456]86/:
case "x86":
return "x86"
default:
throw new GradleException("Can't detect host's CPU architecture: '${arch}'")
}
}
static def getHostTag() {
def tag = getHostOS()
def arch = getHostArch()
if (tag != "windows" || arch != "x86")
tag += "-${arch}"
return tag
}
def getStripExecutable(abi) {
def ndk = getCrystaxNdkDir()
def toolchainName = getToolchainName(abi)
def toolchainPrefix = getToolchainPrefix(abi)
def hostTag = getHostTag()
def strip = "${ndk}/toolchains/${toolchainName}-5/prebuilt/${hostTag}/bin/${toolchainPrefix}-strip"
if (OperatingSystem.current().isWindows())
strip = strip.replaceAll('/', '\\') + '.exe'
return strip
}
def getPrebuiltLibPaths(abi) {
def paths = []
paths += getBoostLibDir(abi)
paths += getOpenSSLLibDir(abi)
paths += getLibCrystaxDir(abi)
return paths
}
def getTargetLibDir(abi, buildType) {
return "${buildDir}/intermediates/binaries/${buildType}/${getFlavorName(abi)}/lib/${abi}"
}
def getLibCrystaxDir(abi) {
return "${getCrystaxNdkDir()}/sources/crystax/libs/${abi}"
}
def getLibCrystax(abi) {
return "${getLibCrystaxDir(abi)}/libcrystax.so"
}
def getBoostDir() {
return "${getCrystaxNdkDir()}/sources/boost/1.64.0"
}
def getBoostIncDir() {
return "${getBoostDir()}/include"
}
def getBoostLibDir(abi) {
return "${getBoostDir()}/${abi}/lib"
}
def getOpenSSLDir() {
return "${getCrystaxNdkDir()}/sources/openssl/1.0.2h"
}
def getOpenSSLIncDir() {
return "${getOpenSSLDir()}/include"
}
def getOpenSSLLibDir(abi) {
return "${getOpenSSLDir()}/libs/${abi}"
}
我将 boost_regex
添加到 link (BOOST_STATIC_LIBS
) 的库列表中。
除了正则表达式部分 (regex_search
),我还在使用 http 的 asio 部分、文件系统部分和日志记录部分,没有任何 (linker) 问题。
当我用 nm --demangle libboost_regex.a | grep perl_matcher | grep regex_traits | grep "find()"
检查 libboost_regex.a 库时,"undefined reference" 显示在结果列表中。 (另一个参考也在列表中)
那么这里可能是什么问题呢?我错过了什么重要的东西吗?
我通过使用 CMake 从 crystax 更改为 Android NDK r16b 并将 std
库设置为 c++11
.
在build.gradle
中:
...
externalNativeBuild {
cmake {
cppFlags "-std=c++11 -frtti -fexceptions"
arguments "-DANDROID_STL=c++_static"
}
}
...
现在 Boost 库(更新到版本 1.66)link 没有问题。