keywords: Android NDK, to_string, Build Failed, UE4, Unreal Engine 4

Steps in ordinary Anroid NDK Projects

Case:
Build with Android NDK failed:

error: 'to_string' is not a member of 'std'

Solution:

Add C++11 support in Application.mk if building static libraries:

APP_STL := c++_static

or use c++_shared if building shared libraries:

APP_STL := c++_shared

It doesn’t take effect if add APP_STL := gnustl_static in Application.mk, because gnustl is the C++ standard library of GCC and it doesn’t support C++11 fully.
c++_static and c++_shared means using libc++ which is the C++ standard library of LLVM, not GCC.

LLVM’s libc++ was stable in NDK r18, and GCC had been removed from r18, Clang is the default compiler.

Steps in UE4 Android Packaging

1, Modify function SetUpSpecificEnvironment which was in UEBuildAndroid.cs.
path:

\Engine\Source\Programs\UnrealBuildTool\Platform\Android\UEBuildAndroid.cs
  • Modification 1st:
    Origin:

      string GccVersion = "4.6";
      int NDKVersionInt = ToolChain.GetNdkApiLevelInt();
      if (Directory.Exists(Path.Combine(NDKPath, @"sources/cxx-stl/gnu-libstdc++/4.9")))
      {
          GccVersion = "4.9";
      } else
      if (Directory.Exists(Path.Combine(NDKPath, @"sources/cxx-stl/gnu-libstdc++/4.8")))
      {
          GccVersion = "4.8";
      }
    

    New:

      string GccVersion = "4.6";
      int NDKVersionInt = ToolChain.GetNdkApiLevelInt();
      if (Directory.Exists(Path.Combine(NDKPath, @"sources/cxx-stl/llvm-libc++/4.9")))
      {
          GccVersion = "4.9";
      } else
      if (Directory.Exists(Path.Combine(NDKPath, @"sources/cxx-stl/llvm-libc++/4.8")))
      {
          GccVersion = "4.8";
      }
    
  • Modification 2nd:
    Origin:

      DirectoryReference NdkDir = new DirectoryReference(NDKPath);
      CompileEnvironment.SystemIncludePaths.Add(DirectoryReference.Combine(NdkDir, "sources/cxx-stl/gnu-libstdc++/" + GccVersion + "/include"));
    
      // the toolchain will actually filter these out
      CompileEnvironment.SystemIncludePaths.Add(DirectoryReference.Combine(NdkDir, "sources/cxx-stl/gnu-libstdc++/" + GccVersion + "/libs/armeabi-v7a/include"));
      CompileEnvironment.SystemIncludePaths.Add(DirectoryReference.Combine(NdkDir, "sources/cxx-stl/gnu-libstdc++/" + GccVersion + "/libs/arm64-v8a/include"));
      CompileEnvironment.SystemIncludePaths.Add(DirectoryReference.Combine(NdkDir, "sources/cxx-stl/gnu-libstdc++/" + GccVersion + "/libs/x86/include"));
      CompileEnvironment.SystemIncludePaths.Add(DirectoryReference.Combine(NdkDir, "sources/cxx-stl/gnu-libstdc++/" + GccVersion + "/libs/x86_64/include"));
    
      LinkEnvironment.LibraryPaths.Add(DirectoryReference.Combine(NdkDir, "sources/cxx-stl/gnu-libstdc++/" + GccVersion + "/libs/armeabi-v7a"));
      LinkEnvironment.LibraryPaths.Add(DirectoryReference.Combine(NdkDir, "sources/cxx-stl/gnu-libstdc++/" + GccVersion + "/libs/arm64-v8a"));
      LinkEnvironment.LibraryPaths.Add(DirectoryReference.Combine(NdkDir, "sources/cxx-stl/gnu-libstdc++/" + GccVersion + "/libs/x86"));
      LinkEnvironment.LibraryPaths.Add(DirectoryReference.Combine(NdkDir, "sources/cxx-stl/gnu-libstdc++/" + GccVersion + "/libs/x86_64"));
    

    New:

      DirectoryReference NdkDir = new DirectoryReference(NDKPath);
      CompileEnvironment.SystemIncludePaths.Add(DirectoryReference.Combine(NdkDir, "sources/cxx-stl/llvm-libc++/include"));
    
      LinkEnvironment.LibraryPaths.Add(DirectoryReference.Combine(NdkDir, "sources/cxx-stl/llvm-libc++/libs/armeabi-v7a"));
      LinkEnvironment.LibraryPaths.Add(DirectoryReference.Combine(NdkDir, "sources/cxx-stl/llvm-libc++/libs/arm64-v8a"));
      LinkEnvironment.LibraryPaths.Add(DirectoryReference.Combine(NdkDir, "sources/cxx-stl/llvm-libc++/libs/x86"));
      LinkEnvironment.LibraryPaths.Add(DirectoryReference.Combine(NdkDir, "sources/cxx-stl/llvm-libc++/libs/x86_64"));
    
      CompileEnvironment.SystemIncludePaths.Add(DirectoryReference.Combine(NdkDir, "sources/cxx-stl/llvm-libc++abi/include"));
    
  • Modification 3rd:
    Origin:

      LinkEnvironment.AdditionalLibraries.Add("gnustl_shared");
    

    New:

      LinkEnvironment.AdditionalLibraries.Add("c++_shared");
    

\Engine\Build\Android\Java\jni\Application.mk doesn’t take effect any more.

2, Change system environment variables of NDK, e.g. the newest NDK version UE4 support is 14b(from v4.21).

NDK Environment variables using in UEBuildAndroid.cs called NDKROOT.

3, Modify PhysX3

Source Path:

\Engine\Source\ThirdParty\PhysX3\PxShared\include\foundation\unix\PxUnixIntrinsics.h

Add #include <cmath> in the top of source.
Origin:

#if PX_ANDROID
    // If cmath is included after math.h, it will undefine isfinite. If that's the case, use the version in the std namespace instead.
    #ifndef isfinite
        using std::isfinite;
    #endif
#endif

New:

#if PX_ANDROID
    // If cmath is included after math.h, it will undefine isfinite. If that's the case, use the version in the std namespace instead.
    #ifndef isfinite
        #include <cmath>
        using std::isfinite;
    #endif
#endif

4, Remove plugin OnlineSubsystemGooglePlay and third party GooglePlay.
Directory:

\Engine\Plugins\Online\Android\OnlineSubsystemGooglePlay\

\Engine\Source\ThirdParty\GooglePlay\

Or replace them with newest version of these third parties sources if using them.

5, Application would crash on startup, it is probably caused by missing libc++_shared.so.
Solution:
Modify source:

\Engine\Source\Programs\UnrealBuildTool\Platform\Android\UEDeployAndroid.cs

Add following code inside function CopySTL:

// copy libc++_shared.so
string SourceSTLSONameLLVM = Environment.ExpandEnvironmentVariables("%NDKROOT%/sources/cxx-stl/llvm-libc++/libs/") + NDKArch + "/libc++_shared.so";
string FinalSTLSONameLLVM = UE4BuildPath + "/libs/" + NDKArch + "/libc++_shared.so";

// remove from location for Ant if previously copied
string WrongSTLSOName = UE4BuildPath + "/libs/" + NDKArch + "/libgnustl_shared.so";
SafeDeleteFile(WrongSTLSOName);

// check to see if libgnustl_shared.so is newer than last time we copied
bool bFileExists = File.Exists(FinalSTLSONameLLVM);
TimeSpan Diff = File.GetLastWriteTimeUtc(FinalSTLSONameLLVM) - File.GetLastWriteTimeUtc(SourceSTLSONameLLVM);
if (!bFileExists || Diff.TotalSeconds < -1 || Diff.TotalSeconds > 1)
{
    SafeDeleteFile(FinalSTLSOName);
    Directory.CreateDirectory(Path.GetDirectoryName(FinalSTLSONameLLVM));
    File.Copy(SourceSTLSONameLLVM, FinalSTLSONameLLVM, true);
}

6, If want to use a version of NDK higher than maximum version which defined in UE4, following source needs to be modified:

\Engine\Source\Programs\UnrealBuildTool\Platform\Android\AndroidToolChain.cs

Limitation of Minimum and Maxmum version of NDK:

// Android NDK toolchain that must be used for C++ compiling
readonly int MinimumNDKToolchain = 140100;
readonly int MaximumNDKToolchain = 180100;
readonly int RecommendedNDKToolchain = 140100;
Reference

Android ndk std::to_string support
https://stackoverflow.com/questions/22774009/android-ndk-stdto-string-support%3E

error: ’to_string’ is not a member of ‘std’
https://stackoverflow.com/questions/26095886/error-to-string-is-not-a-member-of-std

C++ Library Support
https://developer.android.com/ndk/guides/cpp-support


与其更好,不如不同。