Android : merging static libraries into single one - android

I use Android NDK r8 to generate multiple static libraries with include $(BUILD_STATIC_LIBRARY) and I successfully get : lib1.a, lib2.a, lib3.a, etc.
Now I would like to merge these static libraries into single one.
I try do it with ar.exe from Android NDK :
android-ndk-r8\toolchains\arm-linux-androideabi-4.4.3\prebuilt\windows\arm-linux-androideabi\bin\ar.exe r libALL.a lib1.a lib2.a lib3.a
But when I use libAll.a into Android NDK makefile, it fails saying there is no index.
How can I add this index ?
Other question :
When I display contents of archive libAll.a, I see lib1.a, lib2.a, lib3.a instead of .o symbols from these libraries.
How can I change that (= extract .o from static libraries to merge it in libAll.a) ?
Thanks

ar is simply an archiving tool like zip. It takes the given input files and produces an .aarchive. If you want to include all .ofiles in a single archive, you have to specify each individual file. I don't know how to do this on WIndows, but on Linux you can use somthing like ar rs $(find . -name *.o).

Related

How to add rpath $ORIGIN to native executable for android cross build

I am trying to build an executable for Android with cross compiling, everything works but the executable complains that it could not find the .so file it needs, which is in the same directory as the executable.
So what I did is to add the following lines
set(TARGET myapp)
# following 4 lines added to add RPATH of ./ to the binary
# so it searches the .so in the same directory
SET(CMAKE_SKIP_BUILD_RPATH FALSE)
SET(CMAKE_SKIP_RPATH FALSE)
set(CMAKE_INSTALL_RPATH $ORIGIN)
SET(CMAKE_BUILD_WITH_INSTALL_RPATH ON)
# add source code to target
add_executable(${TARGET} src.cpp)
...
However, it builds the executable, but RPATH seems not working no matter how I play with the four lines above, I just could not find any RPATH info in the binary using readelf or objdump.
I also tried set_target_properties(${TARGET} PROPERTIES INSTALL_RPATH $ORIGIN) but still not working.
Did I miss use anything here for RPATH configuration?
update
just to note that if I build the app for host(Linux) (using the same cmake file except using the android ndk tool chain) then everything is fine, I see $ORIGIN in the binary RPATH using readelf.
although i dont know what is been done in android ndk tool chain
This is probably not what you want:
(I am mentioning it just to be complete with my answer)
I assume that $ORIGIN is an environment variable. If that is the case you need to explain to CMake that it is such an variable. You can use $ENV{VAR} to do this, e.g.:
set(CMAKE_INSTALL_RPATH $ENV{ORIGIN})
This is probably what you want:
Ofcourse if the variable is not accessible during CMake generation step. You can try to use bracket arguments, however I do not think that alone would work (see last note at the bottom). Bracket arguments [=[...]=] tell CMake to skip the evaluation, because $ is a special character. e.g.:
set(CMAKE_INSTALL_RPATH [=[$ORIGIN]=])
To understand what [=[]=] do here is a simple example:
set(FOO "bar")
message(STATUS ${FOO})
message(STATUS [=[${FOO}]=])
Should output
bar
${FOO} #<-- evaluation of ${FOO} was skipped
Also if I'm not mistaken you also need to pass $ORIGIN to linker with single quotes so that it doesn't get evaluated during linking, i.e.
'$ORIGIN'
#and not $ORIGIN

Android NDK path variable for "strip" command in CMake build tool chain

I am trying to add a strip debug symbols step for my Android library which includes native shared libraries for different ABIs, e.g. x86/native-lib.so, x86_64/native-lib.so, arm64-v8a/native-lib.so, etc.
I understand that the strip command must be respective to each ABI. So, I need to invoke the correct strip command, for which I need to know its correct path during build time.
For example, for ABI x86_64, I need to have below path setting:
set(STRIP ~/Library/Android/android-ndk-r16b/toolchains/x86_64-4.9/prebuilt/darwin-x86_64/bin/x86_64-linux-android-strip)
add_custom_command(TARGET ${SHARED_LIBRARY_NAME} POST_BUILD
COMMAND ${STRIP}
"${DIST_LIBS_DIR}/${LIB_BUILD_TYPE}/${ANDROID_ABI}/lib${SHARED_LIBRARY_NAME}.so"
COMMENT "Strip debug symbols done on final binary.")
The path I need is illustrated like below:
So, my questions are:
Is there an existing CMake variable to point at this path, i.e. /android-ndk-r16b/toolchains/???/prebuilt/???/bin/???-???-???-strip?
If not, is there a way to form this path utilising other known Android CMake variable, e.g. ANDROID_NDK, ANDROID_ABI, etc?
Thanks #Alex Cohn a lot for pointing out the file android.toolchain.cmake which usually exists at directory ~/Library/Android/sdk/cmake/cmake_version_xxx/android.toolchain.cmake on macOS.
There are many useful Android CMake variables already configured inside, e.g.
ANDROID_NDK
ANDROID_TOOLCHAIN
ANDROID_ABI
ANDROID_PLATFORM
ANDROID_STL
ANDROID_PIE
ANDROID_CPP_FEATURES
ANDROID_ALLOW_UNDEFINED_SYMBOLS
ANDROID_ARM_MODE
ANDROID_ARM_NEON
ANDROID_DISABLE_NO_EXECUTE
ANDROID_DISABLE_RELRO
ANDROID_DISABLE_FORMAT_STRING_CHECKS
ANDROID_CCACHE
And the one ANDROID_TOOLCHAIN_PREFIX is exactly what I looked for, so my final CMake script comes into below:
add_custom_command(TARGET ${SHARED_LIBRARY_NAME} POST_BUILD
COMMAND "${ANDROID_TOOLCHAIN_PREFIX}strip" -g -S -d --strip-debug --verbose
"${DIST_LIBS_DIR}/${LIB_BUILD_TYPE}/${ANDROID_ABI}/lib${SHARED_LIBRARY_NAME}.so"
COMMENT "Strip debug symbols done on final binary.")
And I don't need to explicitly pass any additional arguments, i.e. DCMAKE_TOOLCHAIN_FILE=android.toolchain.cmake, from command line to the build process. Because, this file, i.e. android.toolchain.cmake, was already taken into account automatically by Android native build system.
You can use ${CMAKE_STRIP}. It is set appropriately when you use -DCMAKE_TOOLCHAIN_FILE=android.toolchain.cmake. I hope it is OK also if you work with 'built-in' Android support with supported NDK version.

Android CMake: Could NOT find OpenSSL

I'm pretty new to Android with NDK / CMake. However, I'm trying to embed a native CMake library into an Android Application. However, this library depends on OpenSSL.
That's why I downloaded a precompiled version of OpenSSL for Android.
However, when I try to sync the project I get the following error:
Could NOT find OpenSSL, try to set the path to OpenSSL root folder
in the system variable OPENSSL_ROOT_DIR (missing: OPENSSL_LIBRARIES)
(found version "1.1.0f")
Here is my (minimal) project structure
<app-name>
-app
- src
- main
- cpp
- library
- CMakeLists.txt
CMakeLists.txt
- distribution
- openssl
- armeabi
- include
- openssl
...
- lib
libcrypto.a, libssl.a
In my build.gradle I've defined the following:
externalNativeBuild {
cmake {
path 'src/main/cpp/CMakeLists.txt'
}
}
The /app/src/main/cpp/CmakeLists.txt looks as follows:
cmake_minimum_required(VERSION 3.4.1)
set(distribution_DIR ${CMAKE_SOURCE_DIR}/../../../../distribution)
set(OPENSSL_ROOT_DIR ${distribution_DIR}/openssl/${ANDROID_ABI})
set(OPENSSL_LIBRARIES "${OPENSSL_ROOT_DIR}/lib")
set(OPENSSL_INCLUDE_DIR ${OPENSSL_ROOT_DIR}/include)
message("OPENSSL_LIBRARIES ${OPENSSL_LIBRARIES}")
message("OPENSSL_INCLUDE_DIR ${OPENSSL_INCLUDE_DIR}")
find_package(OpenSSL REQUIRED)
add_subdirectory(library)
find_package(...) searches for libraries in a few standard locations (read here - search for "Search paths specified in cmake"). In your case, it fails because it can't find OpenSSL on the machine you're trying to cross-compile the code for Android.
I know I also had various attempts on linking OpenSSL with my native c++ Android code, and the only way I managed it make it work, was the following:
SET(distribution_DIR ${CMAKE_SOURCE_DIR}/../../../../distribution)
SET(OPENSSL_ROOT_DIR ${distribution_DIR}/openssl/${ANDROID_ABI})
SET(OPENSSL_LIBRARIES_DIR "${OPENSSL_ROOT_DIR}/lib")
SET(OPENSSL_INCLUDE_DIR ${OPENSSL_ROOT_DIR}/include)
SET(OPENSSL_LIBRARIES "ssl" "crypto")
#<----Other cmake code/defines here--->
LINK_DIRECTORIES(${OPENSSL_LIBRARIES_DIR})
ADD_LIBRARY(library #your other params here#)
TARGET_INCLUDE_DIRECTORIES(library PUBLIC ${OPENSSL_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(library ${OPENSSL_LIBRARIES})
I know I also tried to make find_package work correctly, by playing with some of it configuration properties, such as CMAKE_FIND_ROOT_PATH, and some other approaches, but I couldn't get it done.
Don't know if the solution I provided is the best approach, cmake-wise. Maybe someone has a better way of doing it, but alas, it solved my problem at the time.
Hope this helps
The reason OpenSSL is not found is because all the find_*() commands also rely on the variable CMAKE_SYSROOT variable, which is used to prefix paths searched by those commands. This is used when cross-compiling to point to the root directory of the target environment.
A solution is to add the path where OpenSSL is located to CMAKE_FIND_ROOT_PATH; the documentation of find_library() states:
The CMake variable CMAKE_FIND_ROOT_PATH specifies one or more
directories to be prepended to all other search directories. This
effectively “re-roots” the entire search under given locations.
This solution works for me:
set(OPENSSL_ROOT_DIR "/path/to/openssl/for/android")
list(APPEND CMAKE_FIND_ROOT_PATH "${OPENSSL_ROOT_DIR}")
find_package(OpenSSL)
For me it worked to import openssl via gradle and then linking the fetched lib like described here
Either append NO_CMAKE_FIND_ROOT_PATH to your find_* command, e.g.
find_package(OpenSSL REQUIRED NO_CMAKE_FIND_ROOT_PATH)
Or set CMAKE_FIND_ROOT_PATH_MODE_* variables (like this) to NEVER. The following options fixed the Android and iOS builds on macOS for me:
-DOPENSSL_USE_STATIC_LIBS=TRUE \
-DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=NEVER \
-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=NEVER

Cross-Compiling Swiften library for ARM-embedded with SCons makery

Swiften is a XMPP client library and my objective was to build it for a ARM-embedded target running Linux.
I hacked my way to a successful cross-compile with little knowledge of SCons. I'll lay out my hack here with the hope that someone can point me to a maintainable solution using the two makery files, SConscript.boot and SConstruct.
I had two tasks (neither accomplished satisfactorily):
Successfully switching the tool-chain from native-compile to cross-compile
Ensuring that OpenSSL libraries were successfully linked (not supplied by the swiftim project; they has to be installed and built in the 3rdParty folder).
Switching the tool-chain from native-compile to cross-compile for ARM
My ARM cross tool-chain components, gcc, g++, ld, etc are located here.
/opt/toolchain/gcc-linaro-arm-linux-gnueabihf-4.7-2013.01-20130125_linux/arm-linux-gnueabihf/bin/
I couldn't find a way to tell scons to use the cross tool-chain (from the above location) instead of the native tool (in the usual place, /usr/bin). Prefacing the invocation (./scons Swiften) with the fully-qualified values for the environment variables, CC and CXX didn't work (while not recommended, its alluded to in one place).
Scons would only pick up the native tool-chain even after many ad hoc changes to the makery.
So, as a hack, I had to change the native tool-chain to point to the cross tool-chain.
/usr/bin/gcc -> /opt/toolchain/gcc-linaro-arm-linux-gnueabihf-4.7-2013.01-20130125_linux/bin/arm-linux-gnueabihf-gcc-4.7.3*
/usr/bin/g++ -> /opt/toolchain/gcc-linaro-arm-linux-gnueabihf-4.7-2013.01-20130125_linux/bin/arm-linux-gnueabihf-g++*
The first compile-break for ARM was fixed by adding the line below to the default portion of the build script, SConscript.boot.
env.Append(CPPDEFINES = ["_LITTLE_ENDIAN"])
The next compile-break has to do with the OpenSSL header files not being found. To fix the location issue, I had to introduce the line below into SConscript.boot
vars.Add(PackageVariable("openssl", "OpenSSL location", "/home/auro-tripathy/swiftim/swift/3rdParty/OpenSSL/openssl-1.0.1c/"))
Linking with OpenSSL
For the sample Switften programs to link with the OpenSSL libraries, I had to move libssl.a and libcrypto.a (built separately) from the location they were built to the toolchain library-location like so.
mv ~/swiftim/swift/3rdParty/OpenSSL/openssl-1.0.1c/libcrypto.a /opt/toolchain/gcc-linaro-arm-linux-gnueabihf-4.7-2013.01-20130125_linux/lib/gcc/arm-linux-gnueabihf/4.7.3/.
Help
Not understanding of the working of scons, I've made some hacks to get it to work.
I’d like some help to:
Introduce a new target called ARM-embedded, just like other targets; iPhone, android, etc
Clean way to integrate OpenSSL into the build .
Update
Per dirkbaechle, retried the script below and it works
export CC=/opt/toolchain/gcc-linaro-arm-linux-gnueabihf-4.7-2013.01-20130125_linux/arm-linux-gnueabihf/bin/gcc
export CXX=/opt/toolchain/gcc-linaro-arm-linux-gnueabihf-4.7-2013.01-20130125_linux/arm-linux-gnueabihf/bin/g++
./scons Swiften
Brady's answer is correct, regarding how you'd do it in plain SCons. I'd just like to mention that the top-level SConstruct of Swiften already provides arguments like "cc=" and "cxx=" for using local toolchains.
You might want to inspect the ouput of scons -h for a complete list of available options.
In addition, the SConscript for the OpenSSL build expects the sources to be located in the relative folder named "openssl", not "openssl-1.0.1c" as in your case. Maybe that's where your build problems are mainly coming from.
I left a comment above regarding the cross-compilation. Its already been answered in the link provided, but basically you just need to set the appropriate construction variables: CC, CXX, LINK, etc.
As for a "Clean way to integrate OpenSSL into the build" this can be performed simply by adding library and include paths appropriately as follows replacing the quoted values appropriately:
(without having to copy/move the original files)
# This sets the location of the OpenSSL Include paths
env.Append(CPPPATH="path/to/openssl/includes")
# This sets the location of the OpenSSL Libraries
env.Append(LIBPATH="path/to/openssl/libraries")
# These are the OpenSSL libraries to be linked into the binary
env.Append(LIBS=["OpenSSL_lib", "OpenSSL_lib2"])
The choice of compiler, and additional flags, can all be set in Swift's config.py file. A snippet from config.py using a custom compiler and flags is below (the one I use on one of my dev boxes):
cc = link = "/usr/local/llvm-git/bin/clang"
cxx = "/usr/local/llvm-git/bin/clang++"
bothflags = " -std=c++11 -stdlib=libc++ -nostdinc++"
cxxflags = bothflags + " -I/usr/local/libcxx/include -Wno-deprecated"
linkflags = bothflags + " -L/usr/local/libcxx/lib"
This should work for cross-compiling in the same manner.
To use a bundled openssl, you should just be able to extract into 3rdParty/OpenSSL, and add openssl_force_bundled = True to your config.py. You should not need to fiddle with setting include paths to this yourself. It's conceivable that this is tied to a particular openssl release as I've not compiled a bundled openssl since 1.0.0a, but if it doesn't work with the current version it's probably a bug that ought to be fixed. You could also cross-compile openssl yourself and use openssl='/path/to/openssl', but that's a little more of a nuisance for you.

Native libraries .so.XY failing to link at runtime

I have built FFmpeg for Android and want to use it in an application like so:
System.loadLibrary("avutil");
System.loadLibrary("swscale");
System.loadLibrary("avcodec");
System.loadLibrary("avformat");
The build output are lib*.so, lib*.so.MAJOR and lib*.so.MAJOR.MINOR.OTHER files. Inside the shared objects are references like lib*.so.MAJOR, for example libswscale.so.2 requires libavutil.so.52.
Now if I put the *.so files in the project's libs folder (more exactly libs/armeabi-v7a), I of course get
01-25 12:06:40.270: E/AndroidRuntime(2905): java.lang.UnsatisfiedLinkError: Cannot load library: link_image[1936]: 107 could not load needed library 'libavutil.so.52' for 'libswscale.so' (load_library[1091]: Library 'libavutil.so.52' not found)
However if instead I put the *.so.MAJOR files in the libs folder to solve the linking exception, I get the same error when running the app from Eclipse. And I noticed the files do not even get exported if I create an APK! So how do I tell Eclipse to package the *.so.MAJOR files as libraries? Or alternatively, how do I compile the shared objects in a way that they reference each other by *.so instead of *.so.MAJOR?
EDIT: It seems there's no way to package *.so.XYZ files automatically.
Didn't find a solution to take the .so.MAJOR files as I wished. It seems to me that the Android build system only copies *.so files automatically and doesn't allow other file extensions.
So I rewrote the FFmpeg makefiles to have the shared objects reference each other by libXXX.so.
In library.mak, I replaced $(SHFLAGS) in the following recipe
$(SUBDIR)$(SLIBNAME_WITH_MAJOR): $(OBJS) $(SUBDIR)lib$(NAME).ver
$(SLIB_CREATE_DEF_CMD)
$$(LD) $(SHFLAGS) $(LDFLAGS) $$(LD_O) $$(filter %.o,$$^) $(FFEXTRALIBS)
$(SLIB_EXTRA_CMD)
so that the third line looks like
$(subst $$(#F),$(SLIBNAME),$(SHFLAGS))
For those who don't understand the substitution (like me before), check this directly related answer.
Actually, you don't need to do that.
What you need to do is edit the configure file.
And find out this:
SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'
SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR) $(SLIBNAME)'
Modified them to be:
SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
SLIB_INSTALL_LINKS='$(SLIBNAME)'

Categories

Resources