I am currently trying to use the boost::regex library in my Android native lib on Android Studio. But I get this two linker errors:
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)'
I am using the crystax-ndk (10.3.2) to compile my native c++ code. The boost library is version 1.64.0 and I use the precompiled static libs from here.
My build.gradle looks like this:
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}"
}
I added boost_regex
to the list of libraries to link (BOOST_STATIC_LIBS
).
Beside of the regex part (regex_search
), I'm using the asio part for http, the filesystem part and the logging part without any (linker) problems.
When I examined the libboost_regex.a library with nm --demangle libboost_regex.a | grep perl_matcher | grep regex_traits | grep "find()"
, the "undefined reference" is shown in the list of results. (The other reference is also in the list)
So what can be the problem here? Am I missing something important?
I fixed my problem by changing from crystax to Android NDK r16b with CMake and setting the std
lib to c++11
.
In the build.gradle
:
...
externalNativeBuild {
cmake {
cppFlags "-std=c++11 -frtti -fexceptions"
arguments "-DANDROID_STL=c++_static"
}
}
...
Now the Boost libraries (updated to version 1.66) link without problems.