Android Cross-Platform Compilation - BOOST

foreword

    Android studio has been fully integrated with cmake since 2.2 to compile jni code. So our cross-platform compilation also needs to keep pace with the times and use cmake to get the job done. If you don't know cmake very well, don't worry, follow this article to explore, and you can understand some of the most basic usage (PS: The blogger didn't know it at all before, and he still doesn't know how to cross the river by feeling the stones...).

    In addition, the ndk version uses the latest r16b (16.1.4479499), which brings some problems later. In the latest ndk version, the compilation tool has been migrated from gcc to clang, and clang will be used for compilation by default.

    The toolchain address is located in ${ANDROID_SDK}/ndk-bundle/toolchains/llvm. For the relationship between llvm and clang, you can refer to this picture. (For more detailed content, you need to check it yourself, not the content of this article.)

           

    ok Since clang is used, the C++ standard library STL used is naturally the libc++ standard library.

    The header files we use in the code, such as string.h, will be located in the ${ANDROID_SDK}/ndk-bundle/source folder, and different stl libraries will use different file paths.

    There are also some in the sysroot folder, such as the jni.h file.

    There is also a very important thing in the platforms folder, this folder contains different android versions, and each version has a different instruction set folder. Each folder simulates a real machine operating environment.

    Also, before you start, I suggest you read the following articles:

    https://developer.android.com/studio/projects/add-native-code.html

    https://developer.android.com/ndk/guides/cmake.html#variables

    https://developer.android.com/ndk/guides/cpp-support.html#hr

    Of course, please open the official documentation of cmake for reference.

    

text

    The compilation of boost is actually not complicated, for example, we can directly use

                        https://github.com/moritz-wundke/Boost-for-Android

    This github project has been implemented for you, here I use version 1.65.1. The main problem is still focused on how to put it into their own projects.

    We know that cmake provides a method called find_package, please refer to the official documentation for specific parameters. The function is to use the script provided by cmake to automatically search and load the relevant library. For example, we need to load the boost library:

           FIND_PACKAGE(Boost COMPONENTS filesystem REQUIRED)

    Boost is the name of the library, and we only need to load one of the components, filesystem. REQUIRED means that it must be loaded, and an error will be reported if it is not found.

    In fact, after calling find_package, cmake goes back to find a cmake script, for example, boost goes back to find the FindBoost.cmake script. In the android studio environment, he will go to the folder /Users/yxwang/Library/Android/sdk/cmake/3.6.4111459/share/cmake-3.6/Modules to find.

    Normally, we can use 

set(Boost_ADDITIONAL_VERSIONS "1.65" "1.65.1")
set(Boost_NO_SYSTEM_PATHS ON)
set(BOOST_ROOT ${PROJECT_SOURCE_DIR}/src/main/cpp/boost/${ANDROID_ABI})
set(Boost_USE_STATIC_LIBS ON)
FIND_PACKAGE(Boost COMPONENTS filesystem REQUIRED)

    These few lines of code set up some prerequisites, and then the findboost.cmake script can run normally and set some variables for us. (where BOOST_ROOT represents the address of boost)

    But I found that there would be problems running on my side, which eventually led to the failure of find, so I read the fucking source code! !

    In the end, it was found that the problem lies in one of the find_path commands. In theory, the required header file address can be finally found according to the set path and stored in the variable Boost_INCLUDE_DIR.

    But I found that find_path is invalid on my mac. There must be a missing configuration that caused an error in finding the path. Not even an absolute find like find_path(VAL NAMES a.jpg PATH /somepath)... PS It seems to work successfully in a non-ndk environment. If anyone knows, please leave a message and tell me... After all, I'm a novice  

    So what to do? Since you can't find it, I'll tell you directly, so the code becomes like this

set(Boost_ADDITIONAL_VERSIONS "1.65" "1.65.1")
set(Boost_NO_SYSTEM_PATHS ON)
set(BOOST_ROOT ${PROJECT_SOURCE_DIR}/src/main/cpp/boost/${ANDROID_ABI})
set(Boost_INCLUDE_DIR ${BOOST_ROOT}/include/boost-1_65_1 )
set(Boost_LIBRARY_DIR ${BOOST_ROOT}/lib )
set(Boost_USE_STATIC_LIBS ON)

FIND_PACKAGE(Boost COMPONENTS filesystem REQUIRED)

    I manually set the location of Boost_INCLUDE_DIR and the location of Boost_LIBRARY_DIR. (Please pay attention to the capitalization, this capitalization wasted half a day of my time...)

    Ok, then you can use it like other static or dynamic introductions, such as importing header files

include_directories(${Boost_INCLUDE_DIR})

    For example, the introduction of Link library

target_link_libraries(TKAT PRIVATE ${Boost_LIBRARIES} )

    In fact, after calling find_package, the variable rules for defining many components are the same. For example, BOOST_FILESYSTEM_LIBRARY represents a single library of filesystem.

 

question

    Here the author encountered a very serious problem, the library packaging of the armeabi-v7a instruction set failed. This is a very serious problem. If the most widely used instruction set package cannot be typed, it is basically useless.

    I looked at build-android.sh and found that there is a part of it

if [ "$TOOLSET" = "clang" ]; then
      cp configs/user-config-boost-${BOOST_VER}.jam $BOOST_DIR/tools/build/src/user-config.jam || exit 1
      for FILE in configs/user-config-boost-${BOOST_VER}-*.jam; do
          ARCH="`echo $FILE | sed s%configs/user-config-boost-${BOOST_VER}-%% | sed s/[.]jam//`"
          if [ "$ARCH" = "common" ]; then
              continue
          fi
          JAMARCH="`echo ${ARCH} | tr -d '_-'`" # Remove all dashes, bjam does not like them
          sed "s/%ARCH%/${JAMARCH}/g" configs/user-config-boost-${BOOST_VER}-common.jam >> $BOOST_DIR/tools/build/src/user-config.jam || exit 1
          cat configs/user-config-boost-${BOOST_VER}-$ARCH.jam >> $BOOST_DIR/tools/build/src/user-config.jam || exit 1
          echo ';' >> $BOOST_DIR/tools/build/src/user-config.jam || exit 1
      done
  else
      cp configs/user-config-boost-${BOOST_VER}.jam $BOOST_DIR/tools/build/v2/user-config.jam || exit 1
  fi

    The content of this part is to copy some pre-set compilation parameters of this project into user-config.jam, and then put it into the real source code for compilation.

    Some files will be used

                                   

    Than I when we need to compile the instruction set of armeabi-v7a, the script will make user-config-boost-1_65_1.jam user-config-boost-1_65_1-common.jam user-config-boost-1_65_1-armeabi-v7a.jam merge. Then when compiling, something like

"/Users/yxwang/Library/Android/sdk/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++" -x c++ -O3 -O3 -Wno-inline -Wall -fexceptions -frtti -ffunction-sections -funwind-tables -fstack-protector-strong -Wno-invalid-command-line-argument -Wno-unused-command-line-argument -no-canonical-prefixes -I/Users/yxwang/Library/Android/sdk/ndk-bundle/sources/cxx-stl/llvm-libc++/include -I/Users/yxwang/Library/Android/sdk/ndk-bundle/sources/cxx-stl/llvm-libc++abi/include -I/Users/yxwang/Library/Android/sdk/ndk-bundle/sources/android/support/include -DANDROID -Wa,--noexecstack -Wformat -Werror=format-security -DNDEBUG -O2 -g -gcc-toolchain /Users/yxwang/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64 -target armv7-none-linux-androideabi -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb -fpic --std=c++1z -fno-integrated-as --sysroot /Users/yxwang/Library/Android/sdk/ndk-bundle/sysroot -isystem /Users/yxwang/Library/Android/sdk/ndk-bundle/sysroot/usr/include/arm-linux-androideabi -D__ANDROID_API__=15 -DBOOST_ALL_NO_LIB=1 -DBOOST_SYSTEM_STATIC_LINK=1 -DNDEBUG -I"." -c -o "../build/build/armeabi-v7a/boost/bin.v2/libs/system/build/clang-darwin-armeabiv7a/release/link-static/target-os-android/threading-multi/error_code.o" "libs/system/src/error_code.cpp"

    such a compile command.

    I looked at this command carefully and compared it with other projects that compile successfully and finally found a problem

-gcc-toolchain /Users/yxwang/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64

    This parameter problem, because I am using a mac os system, the address of this toolchain is wrong, it should be

    /toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64 (the last platform folder name is wrong)

    We can modify this parameter in user-config-boost-1_65_1-armeabi-v7a.jam.

    After modification, it can be packaged normally.

    PS: He turns out <compileflags>-target
                    <compileflags>armv7-none-linux-androideabi15 

    It seems to be written like this, but I changed it to

                    <compileflags>-target
                    <compileflags>armv7-none-linux-androideabi

    Because I found that the target is written like this under normal compilation.

    

   It is very important. When the compilation fails, it is a good way to debug by comparing the compilation commands.

 

    I encountered a problem when using the compiled system library,

FAILED: : && /Users/yxwang/Library/Android/sdk/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++  --target=aarch64-none-linux-android --gcc-toolchain=/Users/yxwang/Library/Android/sdk/ndk-bundle/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64 --sysroot=/Users/yxwang/Library/Android/sdk/ndk-bundle/sysroot -fPIC -isystem /Users/yxwang/Library/Android/sdk/ndk-bundle/sysroot/usr/include/aarch64-linux-android -D__ANDROID_API__=21 -g -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -Wa,--noexecstack -Wformat -Werror=format-security -std=c++11 -frtti -fexceptions --std=c++1z -O0 -fno-limit-debug-info  -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libatomic.a --sysroot /Users/yxwang/Library/Android/sdk/ndk-bundle/platforms/android-21/arch-arm64 -Wl,--build-id -Wl,--warn-shared-textrel -Wl,--fatal-warnings -L/Users/yxwang/Library/Android/sdk/ndk-bundle/sources/cxx-stl/llvm-libc++/libs/arm64-v8a -Wl,--no-undefined -Wl,-z,noexecstack -Qunused-arguments -Wl,-z,relro -Wl,-z,now -shared -Wl,-soname,libTKATCLIENTBACKENDNETWORK.so -o ../../../../build/intermediates/cmake/debug/obj/arm64-v8a/libTKATCLIENTBACKENDNETWORK.so src/main/cpp/client/backend/tkat.client.backend.network/CMakeFiles/TKATCLIENTBACKENDNETWORK.dir/src/network-driver.cpp.o src/main/cpp/client/backend/tkat.client.backend.network/CMakeFiles/TKATCLIENTBACKENDNETWORK.dir/src/socket-id.cpp.o  ../../../../src/main/cpp/boost/arm64-v8a/lib/libboost_system.a ../../../../build/intermediates/cmake/debug/obj/arm64-v8a/libTKAT.so -latomic -lm "/Users/yxwang/Library/Android/sdk/ndk-bundle/sources/cxx-stl/llvm-libc++/libs/arm64-v8a/libc++.a" && :
src/main/cpp/client/backend/tkat.client.backend.network/CMakeFiles/TKATCLIENTBACKENDNETWORK.dir/src/network-driver.cpp.o: In function `__cxx_global_var_init':
/Users/yxwang/workspace/gittest/CmakeTest/app/.externalNativeBuild/cmake/debug/arm64-v8a/../../../../src/main/cpp/boost/arm64-v8a/include/boost-1_65_1/boost/system/error_code.hpp:207: undefined reference to `b
oost::system::generic_category()'
src/main/cpp/client/backend/tkat.client.backend.network/CMakeFiles/TKATCLIENTBACKENDNETWORK.dir/src/network-driver.cpp.o: In function `__cxx_global_var_init.1':
/Users/yxwang/workspace/gittest/CmakeTest/app/.externalNativeBuild/cmake/debug/arm64-v8a/../../../../src/main/cpp/boost/arm64-v8a/include/boost-1_65_1/boost/system/error_code.hpp:209: undefined reference to `boost::system::generic_category()'
src/main/cpp/client/backend/tkat.client.backend.network/CMakeFiles/TKATCLIENTBACKENDNETWORK.dir/src/network-driver.cpp.o: In function `__cxx_global_var_init.2':
/Users/yxwang/workspace/gittest/CmakeTest/app/.externalNativeBuild/cmake/debug/arm64-v8a/../../../../src/main/cpp/boost/arm64-v8a/include/boost-1_65_1/boost/system/error_code.hpp:211: undefined reference to `boost::system::system_category()'
src/main/cpp/client/backend/tkat.client.backend.network/CMakeFiles/TKATCLIENTBACKENDNETWORK.dir/src/network-driver.cpp.o: In function `boost::asio::error::get_system_category()':
/Users/yxwang/workspace/gittest/CmakeTest/app/.externalNativeBuild/cmake/debug/arm64-v8a/../../../../src/main/cpp/boost/arm64-v8a/include/boost-1_65_1/boost/asio/error.hpp:230: undefined reference to `boost::system::system_category()'
src/main/cpp/client/backend/tkat.client.backend.network/CMakeFiles/TKATCLIENTBACKENDNETWORK.dir/src/network-driver.cpp.o: In function `error_code':
/Users/yxwang/workspace/gittest/CmakeTest/app/.externalNativeBuild/cmake/debug/arm64-v8a/../../../../src/main/cpp/boost/arm64-v8a/include/boost-1_65_1/boost/system/error_code.hpp:440: undefined reference to `boost::system::system_category()'
src/main/cpp/client/backend/tkat.client.backend.network/CMakeFiles/TKATCLIENTBACKENDNETWORK.dir/src/network-driver.cpp.o: In function `boost::system::error_category::std_category::equivalent(int, std::__ndk1::error_condition const&) const':
/Users/yxwang/workspace/gittest/CmakeTest/app/.externalNativeBuild/cmake/debug/arm64-v8a/../../../../src/main/cpp/boost/arm64-v8a/include/boost-1_65_1/boost/syste
m/error_code.hpp:657: undefined reference to `boost::system::generic_category()'
/Users/yxwang/workspace/gittest/CmakeTest/app/.externalNativeBuild/cmake/debug/arm64-v8a/../../../../src/main/cpp/boost/arm64-v8a/include/boost-1_65_1/boost/system/error_code.hpp:660: undefined reference to `boost::system::generic_category()'
src/main/cpp/client/backend/tkat.client.backend.network/CMakeFiles/TKATCLIENTBACKENDNETWORK.dir/src/network-driver.cpp.o: In function `boost::system::error_category::std_category::equivalent(std::__ndk1::error_code const&, int) const':
/Users/yxwang/workspace/gittest/CmakeTest/app/.externalNativeBuild/cmake/debug/arm64-v8a/../../../../src/main/cpp/boost/arm64-v8a/include/boost-1_65_1/boost/system/error_code.hpp:687: undefined reference to `boost::system::generic_category()'
/Users/yxwang/workspace/gittest/CmakeTest/app/.externalNativeBuild/cmake/debug/arm64-v8a/../../../../src/main/cpp/boost/arm64-v8a/include/boost-1_65_1/boost/system/error_code.hpp:690: undefined reference to `boost::system::generic_category()'
/Users/yxwang/workspace/gittest/CmakeTest/app/.externalNativeBuild/cmake/debug/arm64-v8a/../../../../src/main/cpp/boost/arm64-v8a/include/boost-1_65_1/boost/system/error_code.hpp:702: undefined reference to `boost::system::generic_category()'
clang++: error: linker command failed with exit code 1 (use -v to see invocation)
ninja: build stopped: subcommand failed.
:app:externalNativeBuildDebug FAILED

    Just can't find the definition of the generic_category method in the system module, but we do

target_link_libraries(TKATCLIENTBACKENDNETWORK PUBLIC ${Boost_SYSTEM_LIBRARY})

    link to this library. Could it be that there is an error in the packaging of libboost_system.a and these two methods are not defined? So use the nm command to view

localhost:lib yxwang$ nm -g --defined-only libboost_system.a 

error_code.o:
0000000000000000 V DW.ref.__gxx_personality_v0
0000000000000000 W _ZN5boost6system14error_category12std_categoryD0Ev
0000000000000000 W _ZN5boost6system14error_category12std_categoryD2Ev
0000000000000000 W _ZN5boost6system14error_categoryD2Ev
0000000000000000 T _ZN5boost6system15system_categoryEv
0000000000000000 T _ZN5boost6system16generic_categoryEv
0000000000000000 B _ZN5boost6system6throwsE
0000000000000000 W _ZNK5boost6system14error_category10equivalentERKNS0_10error_codeEi
0000000000000000 W _ZNK5boost6system14error_category10equivalentEiRKNS0_15error_conditionE
0000000000000000 W _ZNK5boost6system14error_category12std_category4nameEv
0000000000000000 W _ZNK5boost6system14error_category23default_error_conditionEi
0000000000000000 W _ZNKSt6__ndk121__basic_string_commonILb1EE20__throw_length_errorEv
0000000000000000 W _ZNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED2Ev
0000000000000000 V _ZTIN5boost12noncopyable_11noncopyableE
0000000000000000 V _ZTIN5boost6system14error_category12std_categoryE
0000000000000000 V _ZTIN5boost6system14error_categoryE
0000000000000000 V _ZTSN5boost12noncopyable_11noncopyableE
0000000000000000 V _ZTSN5boost6system14error_category12std_categoryE
0000000000000000 V _ZTSN5boost6system14error_categoryE
0000000000000000 V _ZTVN5boost6system14error_category12std_categoryE

    We found that these two methods do exist

0000000000000000 T _ZN5boost6system15system_categoryEv
0000000000000000 T _ZN5boost6system16generic_categoryEv

    So where is the problem? See where the error is reported

#ifdef BOOST_ERROR_CODE_HEADER_ONLY
    inline const error_category &  system_category() BOOST_SYSTEM_NOEXCEPT;
    inline const error_category &  generic_category() BOOST_SYSTEM_NOEXCEPT;
#else
    BOOST_SYSTEM_DECL const error_category &  system_category() BOOST_SYSTEM_NOEXCEPT;
    BOOST_SYSTEM_DECL const error_category &  generic_category() BOOST_SYSTEM_NOEXCEPT;
#endif

    The first is no problem... Wait, the method defined by the header file is different when BOOST_ERROR_CODE_HEADER_ONLY is defined and not defined.

    Could it be that the method I call into the static library when compiling the library is different from the method I use when I am currently calling it? That is, the value BOOST_ERROR_CODE_HEADER_ONLY is defined when I package it, but it is not defined when it is currently used (or vice versa.)

    If so, of course it would result in the method not being found, since the method definitions are fundamentally different, and of course not when libboost_system.a looks up the symbol table.

    So I try to add in cmake

target_compile_definitions(TKATCLIENTBACKENDNETWORK PRIVATE BOOST_ERROR_CODE_HEADER_ONLY)

    Indicates that the variable BOOST_ERROR_CODE_HEADER_ONLY is defined at compile time. Then found that the error disappeared.

    So try to make sure that the library uses the same configuer when it is compiled and when it is used.

 

    PS: The latest Boost-for-android has supported 1.66.0, and has also fixed the above problems. But I have other problems when using armeabi-v7a. reported when compiling   

                                        no archive symbol table (run ranlib)

    Solution, open the configs/user-config-boost-1_66_0-common.jam file in the project, we see that although we have configured <archiver>$(AndroidBinariesPath)/llvm-ar But we have not configured the address of ranlib.

    PS: Whether to use this ar is a question worth studying, because according to the ndk-bundle/build/cmake/android.toolchain.cmake file, we can also find the configuration addresses of ar and ranlib, but not using llvm-ar this ar.

    So where exactly is ranlib? This has different addresses according to different instruction sets. In armeabi-v7a, the address is $(AndroidNDKRoot)/toolchains/arm-linux-androideabi-4.9/prebuilt/${PlatformOS}-x86_64/bin/arm-linux- androideabi-ranlib

    So my solution was, open configs/user-config-boost-1_66_0-armeabi-v7a.jam and add <ranlib>$(AndroidNDKRoot)/toolchains/arm-linux-androideabi-4.9/prebuilt/${ PlatformOS}-x86_64/bin/arm-linux-androideabi-ranlib 

 

    There is a big problem when using Boost, that is, the boost library is too big, there are a lot of header files, and the interdependence between the header files is complicated, it is very likely that we only use the very simple ones in Boost. function, but had to put all the header files of the entire boost into it. To solve this problem, boost provides a bcp tool for tailoring the boost library.

    first run

                bootstrap.sh

    Then

                ./b2 tools/bcp

    Compile the bcp tool, and a bcp tool will be generated in dist/bin.

    ./bcp --boost=<boost directory> [header files to be used, such as boost/algorithm/string.hpp] output address

    You can cut bcp. The cropped thing contains some cpp files of lib, we can compile it through b2, but since I am compiling with ndk, I have not yet figured out how to set up the compilation, so I copied the header file directly and compiled it with the previous article. The library is used to reduce the number of header files.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325054557&siteId=291194637