Android: Linking to prebuilt static libraries

2019-06-16 09:51发布

I have compiled some static and shared libraries for Android. Specifically, I have the libraries

libcoinblas.a   libcoinlapack.a   libcoinmetis.a   libcoinmumps.a   libipopt.a
libcoinblas.so  libcoinlapack.so  libcoinmetis.so  libcoinmumps.so  libipopt.so

Furthermore, these libraries are inter-dependent, that is,

Lapack requires Blas
Mumps  requires Blas and Metis
Ipopt  requires Mumps, Metis, and Lapack

The Android project correctly links and runs when using the shared libraries, but fails to build with the static libraries.

In the shared case, I am using the cmake file

cmake_minimum_required(VERSION 3.4.1)

add_library( native-lib
             SHARED
             src/main/cpp/cpp_example.cpp
             src/main/cpp/MyNLP.cpp)

# Add dependent libraries
add_library(blas SHARED IMPORTED)
set_property(TARGET blas PROPERTY IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libcoinblas.so)

add_library(lapack SHARED IMPORTED)
set_property(TARGET lapack PROPERTY IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libcoinlapack.so)

add_library(metis SHARED IMPORTED)
set_property(TARGET metis PROPERTY IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libcoinmetis.so)

add_library(mumps SHARED IMPORTED)
set_property(TARGET mumps PROPERTY IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libcoinmumps.so)

add_library(ipopt SHARED IMPORTED)
set_property(TARGET ipopt PROPERTY IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libipopt.so)

# Location of header files
include_directories(${CMAKE_SOURCE_DIR}/libs/include
                    ${CMAKE_SOURCE_DIR}/libs/include/ThirdParty)

target_link_libraries( native-lib

                       blas
                       lapack
                       metis
                       mumps
                       ipopt
                       )

and in the static case

cmake_minimum_required(VERSION 3.4.1)

add_library( native-lib
             SHARED
             src/main/cpp/cpp_example.cpp
             src/main/cpp/MyNLP.cpp)

# Add dependent libraries
add_library(blas STATIC IMPORTED)
set_property(TARGET blas PROPERTY IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libcoinblas.a)

add_library(lapack STATIC IMPORTED)
set_property(TARGET lapack PROPERTY IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libcoinlapack.a)

add_library(metis STATIC IMPORTED)
set_property(TARGET metis PROPERTY IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libcoinmetis.a)

add_library(mumps STATIC IMPORTED)
set_property(TARGET mumps PROPERTY IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libcoinmumps.a)

add_library(ipopt STATIC IMPORTED)
set_property(TARGET ipopt PROPERTY IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libipopt.a)

# Location of header files
include_directories(${CMAKE_SOURCE_DIR}/libs/include
                    ${CMAKE_SOURCE_DIR}/libs/include/ThirdParty)

target_link_libraries( native-lib

                       blas
                       lapack
                       metis
                       mumps
                       ipopt
                       )

I presumed that I simply need to change how the libraries are added from

add_library(libxxx SHARED IMPORTED)
set_property(TARGET libxxx PROPERTY ... libxxx.so)

to

add_library(libxxx STATIC IMPORTED)
set_property(TARGET libxxx PROPERTY ... libxxx.a)

But that does not work. Specifically, in the static case, I get a bunch (hundreds) of

undefined reference to xxx 

errors. For example,

../../../../libs/arm64-v8a/libipopt.a(IpLapack.o): In function `Ipopt::IpLapackDppsv(int, int, double const*, double*, int, int&)':
IpLapack.cpp:(.text+0x3d4): undefined reference to `dppsv_'

Although the errors are not just due to missing Lapack functions, but also Mumps and others.


EDIT

Looking at the specific failed command, I believe that the libraries were specified in the correct order:

FAILED: cmd.exe /C "cd . && clang++.exe --target=aarch64-none-linux-android --gcc-toolchain=C:/Android/android-sdk/ndk-bundle/toolchains/aarch64-linux-android-4.9/prebuilt/windows-x86_64 --sysroot=sysroot -fPIC -isystem C:/Android/android-sdk/ndk-bundle/sysroot/usr/include/aarch64-linux-android -D__ANDROID_API__=23 -g -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -Wa,--noexecstack -Wformat -Werror=format-security -O0 -fno-limit-debug-info -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libatomic.a --sysroot C:/Android/android-sdk/ndk-bundle/platforms/android-23/arch-arm64 -Wl,--build-id -Wl,--warn-shared-textrel -Wl,--fatal-warnings -Wl,--no-undefined -Wl,-z,noexecstack -Qunused-arguments -Wl,-z,relro -Wl,-z,now -shared -Wl,-soname,libnative-lib.so -o ........\build\intermediates\cmake\debug\obj\arm64-v8a\libnative-lib.so CMakeFiles/native-lib.dir/src/main/cpp/cpp_example.cpp.o CMakeFiles/native-lib.dir/src/main/cpp/MyNLP.cpp.o libcoinblas.a libcoinlapack.a libcoinmetis.a libcoinmumps.a libipopt.a -latomic -lm "C:/Android/android-sdk/ndk-bundle/sources/cxx-stl/gnu-libstdc++/4.9/libs/arm64-v8a/libgnustl_static.a" && cd ."

Note that I sanitized the paths a little bit above so that they are slightly readable, but towards the end you can see that the libraries are listed in the order

libcoinblas.a libcoinlapack.a libcoinmetis.a libcoinmumps.a libipopt.a

EDIT

I also tried changing the link command from target_link_library to link_library:

link_libraries(native-lib blas lapack metis mumps ipopt)

but this also fails. For some reason, in this case, the link command doesn't even include the libraries that it is supposed to link:

FAILED: cmd.exe /C "cd . && C:\Android\android-sdk\ndk-bundle\toolchains\llvm\prebuilt\windows-x86_64\bin\clang++.exe --target=aarch64-none-linux-android --gcc-toolchain=C:/Android/android-sdk/ndk-bundle/toolchains/aarch64-linux-android-4.9/prebuilt/windows-x86_64 --sysroot=C:/Android/android-sdk/ndk-bundle/sysroot -fPIC -isystem C:/Android/android-sdk/ndk-bundle/sysroot/usr/include/aarch64-linux-android -D__ANDROID_API__=23 -g -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -Wa,--noexecstack -Wformat -Werror=format-security -O0 -fno-limit-debug-info -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libatomic.a --sysroot C:/Android/android-sdk/ndk-bundle/platforms/android-23/arch-arm64 -Wl,--build-id -Wl,--warn-shared-textrel -Wl,--fatal-warnings -Wl,--no-undefined -Wl,-z,noexecstack -Qunused-arguments -Wl,-z,relro -Wl,-z,now -shared -Wl,-soname,libnative-lib.so -o ........\build\intermediates\cmake\debug\obj\arm64-v8a\libnative-lib.so CMakeFiles/native-lib.dir/src/main/cpp/cpp_example.cpp.o CMakeFiles/native-lib.dir/src/main/cpp/MyNLP.cpp.o -latomic -lm "C:/Android/android-sdk/ndk-bundle/sources/cxx-stl/gnu-libstdc++/4.9/libs/arm64-v8a/libgnustl_static.a" && cd ." CMakeFiles/native-lib.dir/src/main/cpp/cpp_example.cpp.o: In function `Java_io_jeti_ipopt_1static_MainActivity_stringFromJNI':

2条回答
小情绪 Triste *
2楼-- · 2019-06-16 09:56

Your libraries are interdependent:

Lapack requires Blas
Mumps  requires Blas and Metis
Ipopt  requires Mumps, Metis, and Lapack

This means that the order for linking them should be reverse:

ipopt
mumps
metis
lapack
blas 

If you don't want to waste your time on figuring out the best order, but rather let the linker to find out (this may slow your builds significantly), you can use

target_link_libraries(native-lib -Wl,--start-group blas lapack metis mumps ipopt -Wl,--end-group).

You can also teach CMake about the dependencies between imported static libs, via IMPORTED_LINK_INTERFACE_LIBRARIES, e.g.

set_target_properties(lapack 
  PROPERTIES IMPORTED_LINK_INTERFACE_LIBRARIES 
  blas)

and so on.

This will translate

target_link_libraries( native-lib
                   blas
                   lapack
                   )

To

clang++ -o libnative-lib.so … libblas.a libnlapack.a libblas.a
查看更多
Anthone
3楼-- · 2019-06-16 10:21

This might not be anything to do with libraries at all. It could be to do with the way that dppsv () is prototyped.

The source file corresponding to the linker error you quote in your post is here:

https://github.com/coin-or/Ipopt/blob/master/Ipopt/src/LinAlg/IpLapack.cpp

And this contains the following code snippet:

extern "C" {

/** LAPACK Fortran subroutine DPPSV. */
void F77_FUNC(dppsv,DPPSV)(char *uplo, ipfint *n,
                           ipfint *nrhs, const double *A,
                           double *B, ipfint *ldB, ipfint *info);
}

The F77_FUNC macro is evidently designed to map to the function naming convention used by whatever Fortran compiler you happen to be using, see here:

https://github.com/coin-or/Ipopt/blob/master/Ipopt/src/Common/IpoptConfig.h

Now this is not my area of expertise but it could well be that this macro is not doing the right thing in your case. You can run nm on the relevant .o file produced by the Fortran compiler to see what it is generating with your particular build setup. If it's not dppsv_ then you know what's wrong.

查看更多
登录 后发表回答