Browse Source

makepanda: update Android cross-compile for clang and NDK r16

Tested on Windows.
rdb 7 years ago
parent
commit
2dba9357bb
2 changed files with 134 additions and 88 deletions
  1. 56 25
      makepanda/makepanda.py
  2. 78 63
      makepanda/makepandacore.py

+ 56 - 25
makepanda/makepanda.py

@@ -821,9 +821,6 @@ if (COMPILER=="GCC"):
         SmartPkgEnable("FREETYPE",  "freetype2", ("freetype"), ("freetype2", "freetype2/freetype/freetype.h"))
         SmartPkgEnable("HARFBUZZ",  "harfbuzz",  ("harfbuzz"), ("harfbuzz", "harfbuzz/hb-ft.h"))
         SmartPkgEnable("GL",        "gl",        ("GL"), ("GL/gl.h"), framework = "OpenGL")
-        SmartPkgEnable("GLES",      "glesv1_cm", ("GLESv1_CM"), ("GLES/gl.h"), framework = "OpenGLES")
-        SmartPkgEnable("GLES2",     "glesv2",    ("GLESv2"), ("GLES2/gl2.h")) #framework = "OpenGLES"?
-        SmartPkgEnable("EGL",       "egl",       ("EGL"), ("EGL/egl.h"))
         SmartPkgEnable("NVIDIACG",  "",          ("Cg"), "Cg/cg.h", framework = "Cg")
         SmartPkgEnable("ODE",       "",          ("ode"), "ode/ode.h", tool = "ode-config")
         SmartPkgEnable("OPENAL",    "openal",    ("openal"), "AL/al.h", framework = "OpenAL")
@@ -837,6 +834,12 @@ if (COMPILER=="GCC"):
         SmartPkgEnable("JPEG",      "",          ("jpeg"), "jpeglib.h")
         SmartPkgEnable("PNG",       "libpng",    ("png"), "png.h", tool = "libpng-config")
 
+        # On Android, these are system libraries.
+        if GetHost() != "android":
+            SmartPkgEnable("GLES",  "glesv1_cm", (), ("GLES/gl.h"), framework = "OpenGLES")
+            SmartPkgEnable("GLES2", "glesv2",    (), ("GLES2/gl2.h")) #framework = "OpenGLES"?
+            SmartPkgEnable("EGL",   "egl",       (), ("EGL/egl.h"))
+
         if not PkgSkip("FFMPEG"):
             if GetTarget() == "darwin":
                 LibName("FFMPEG", "-Wl,-read_only_relocs,suppress")
@@ -1251,7 +1254,13 @@ def CompileCxx(obj,src,opts):
                     cmd += " -arch %s" % arch
 
         if "SYSROOT" in SDK:
-            cmd += ' --sysroot=%s -no-canonical-prefixes' % (SDK["SYSROOT"])
+            if GetTarget() != "android":
+                cmd += ' --sysroot=%s' % (SDK["SYSROOT"])
+            else:
+                ndk_dir = SDK["ANDROID_NDK"].replace('\\', '/')
+                cmd += ' -isystem %s/sysroot/usr/include' % (ndk_dir)
+                cmd += ' -isystem %s/sysroot/usr/include/%s' % (ndk_dir, SDK["ANDROID_TRIPLE"])
+            cmd += ' -no-canonical-prefixes'
 
         # Android-specific flags.
         arch = GetTargetArch()
@@ -1261,33 +1270,38 @@ def CompileCxx(obj,src,opts):
             # just copied from the default Android Makefiles.
             if "ANDROID_API" in SDK:
                 cmd += ' -D__ANDROID_API__=' + str(SDK["ANDROID_API"])
-            if "ANDROID_STL" in SDK:
-                cmd += ' -I%s/include' % (SDK["ANDROID_STL"])
-                cmd += ' -I%s/libs/%s/include' % (SDK["ANDROID_STL"], SDK["ANDROID_ABI"])
+            if "ANDROID_GCC_TOOLCHAIN" in SDK:
+                cmd += ' -gcc-toolchain ' + SDK["ANDROID_GCC_TOOLCHAIN"].replace('\\', '/')
             cmd += ' -ffunction-sections -funwind-tables'
             if arch == 'armv7a':
-                cmd += ' -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__'
-                cmd += ' -fstack-protector -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16'
+                cmd += ' -target armv7-none-linux-androideabi'
+                cmd += ' -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16'
+                cmd += ' -fno-integrated-as'
             elif arch == 'arm':
-                cmd += ' -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__'
-                cmd += ' -fstack-protector -march=armv5te -mtune=xscale -msoft-float'
+                cmd += ' -target armv5te-none-linux-androideabi'
+                cmd += ' -march=armv5te -mtune=xscale -msoft-float'
+                cmd += ' -fno-integrated-as'
+            elif arch == 'aarch64':
+                cmd += ' -target aarch64-none-linux-android'
             elif arch == 'mips':
-                cmd += ' -finline-functions -fmessage-length=0'
-                cmd += ' -fno-inline-functions-called-once -fgcse-after-reload'
-                cmd += ' -frerun-cse-after-loop -frename-registers'
+                cmd += ' -target mipsel-none-linux-android'
+                cmd += ' -mips32'
+            elif arch == 'mips64':
+                cmd += ' -target mips64el-none-linux-android'
+                cmd += ' -fintegrated-as'
+            elif arch == 'x86':
+                cmd += ' -target i686-none-linux-android'
+                cmd += ' -mstackrealign'
+            elif arch == 'x86_64':
+                cmd += ' -target x86_64-none-linux-android'
 
             cmd += " -Wa,--noexecstack"
 
-            # Now add specific release/debug flags.
-            if optlevel >= 3:
-                cmd += " -fomit-frame-pointer"
-                if arch.startswith('arm'):
-                    cmd += ' -finline-limit=64 -mthumb'
-                elif arch == 'mips':
-                    cmd += ' -funswitch-loops -finline-limit=300'
-            else:
-                cmd += ' -fno-omit-frame-pointer'
-                if arch.startswith('arm'):
+            # Do we want thumb or arm instructions?
+            if arch.startswith('arm'):
+                if optlevel >= 3:
+                    cmd += ' -mthumb'
+                else:
                     cmd += ' -marm'
 
             # Enable SIMD instructions if requested
@@ -1745,9 +1759,26 @@ def CompileLink(dll, obj, opts):
                     cmd += " -arch %s" % arch
 
         elif GetTarget() == 'android':
+            arch = GetTargetArch()
+            if "ANDROID_GCC_TOOLCHAIN" in SDK:
+                cmd += ' -gcc-toolchain ' + SDK["ANDROID_GCC_TOOLCHAIN"].replace('\\', '/')
             cmd += " -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now"
-            if GetTargetArch() == 'armv7a':
+            if arch == 'armv7a':
+                cmd += ' -target armv7-none-linux-androideabi'
                 cmd += " -march=armv7-a -Wl,--fix-cortex-a8"
+            elif arch == 'arm':
+                cmd += ' -target armv5te-none-linux-androideabi'
+            elif arch == 'aarch64':
+                cmd += ' -target aarch64-none-linux-android'
+            elif arch == 'mips':
+                cmd += ' -target mipsel-none-linux-android'
+                cmd += ' -mips32'
+            elif arch == 'mips64':
+                cmd += ' -target mips64el-none-linux-android'
+            elif arch == 'x86':
+                cmd += ' -target i686-none-linux-android'
+            elif arch == 'x86_64':
+                cmd += ' -target x86_64-none-linux-android'
             cmd += ' -lc -lm'
         else:
             cmd += " -pthread"

+ 78 - 63
makepanda/makepandacore.py

@@ -37,7 +37,8 @@ TARGET_ARCH = None
 HAS_TARGET_ARCH = False
 TOOLCHAIN_PREFIX = ""
 ANDROID_ABI = None
-ANDROID_API = 14
+ANDROID_TRIPLE = None
+ANDROID_API = None
 SYS_LIB_DIRS = []
 SYS_INC_DIRS = []
 DEBUG_DEPENDENCIES = False
@@ -359,40 +360,48 @@ def SetTarget(target, arch=None):
             if host == 'android':
                 arch = host_arch
             else:
-                arch = 'arm'
+                arch = 'armv7a'
 
         # Did we specify an API level?
+        global ANDROID_API
         target, _, api = target.partition('-')
         if api:
-            global ANDROID_API
             ANDROID_API = int(api)
+        elif arch in ('mips64', 'aarch64', 'x86_64'):
+            # 64-bit platforms were introduced in Android 21.
+            ANDROID_API = 21
+        else:
+            # Default to the lowest API level supported by NDK r16.
+            ANDROID_API = 14
 
         # Determine the prefix for our gcc tools, eg. arm-linux-androideabi-gcc
-        global ANDROID_ABI
+        global ANDROID_ABI, ANDROID_TRIPLE
         if arch == 'armv7a':
             ANDROID_ABI = 'armeabi-v7a'
-            TOOLCHAIN_PREFIX = 'arm-linux-androideabi-'
+            ANDROID_TRIPLE = 'arm-linux-androideabi'
         elif arch == 'arm':
             ANDROID_ABI = 'armeabi'
-            TOOLCHAIN_PREFIX = 'arm-linux-androideabi-'
+            ANDROID_TRIPLE = 'arm-linux-androideabi'
         elif arch == 'aarch64':
             ANDROID_ABI = 'arm64-v8a'
-            TOOLCHAIN_PREFIX = 'aarch64-linux-android-'
+            ANDROID_TRIPLE = 'aarch64-linux-android'
         elif arch == 'mips':
             ANDROID_ABI = 'mips'
-            TOOLCHAIN_PREFIX = 'mipsel-linux-android-'
+            ANDROID_TRIPLE = 'mipsel-linux-android'
         elif arch == 'mips64':
             ANDROID_ABI = 'mips64'
-            TOOLCHAIN_PREFIX = 'mips64el-linux-android-'
+            ANDROID_TRIPLE = 'mips64el-linux-android'
         elif arch == 'x86':
             ANDROID_ABI = 'x86'
-            TOOLCHAIN_PREFIX = 'i686-linux-android-'
+            ANDROID_TRIPLE = 'i686-linux-android'
         elif arch == 'x86_64':
             ANDROID_ABI = 'x86_64'
-            TOOLCHAIN_PREFIX = 'x86_64-linux-android-'
+            ANDROID_TRIPLE = 'x86_64-linux-android'
         else:
             exit('Android architecture must be arm, armv7a, aarch64, mips, mips64, x86 or x86_64')
 
+        TOOLCHAIN_PREFIX = ANDROID_TRIPLE + '-'
+
     elif target == 'linux':
         if arch is not None:
             TOOLCHAIN_PREFIX = '%s-linux-gnu-' % arch
@@ -2359,6 +2368,8 @@ def SdkLocateAndroid():
     """This actually locates the Android NDK, not the Android SDK.
     NDK_ROOT must be set to its root directory."""
 
+    global TOOLCHAIN_PREFIX
+
     if GetTarget() != 'android':
         return
 
@@ -2368,6 +2379,7 @@ def SdkLocateAndroid():
 
     abi = ANDROID_ABI
     SDK["ANDROID_ABI"] = abi
+    SDK["ANDROID_TRIPLE"] = ANDROID_TRIPLE
 
     if GetHost() == 'android':
         return
@@ -2383,36 +2395,58 @@ def SdkLocateAndroid():
     SDK["ANDROID_NDK"] = ndk_root
 
     # Determine the toolchain location.
-    gcc_ver = '4.8'
+    prebuilt_dir = os.path.join(ndk_root, 'toolchains', 'llvm', 'prebuilt')
+    if not os.path.isdir(prebuilt_dir):
+        exit('Not found: %s' % (prebuilt_dir))
+
+    host_tag = GetHost() + '-x86'
+    if host_64:
+        host_tag += '_64'
+    elif host_tag == 'windows-x86':
+        host_tag = 'windows'
+
+    prebuilt_dir = os.path.join(prebuilt_dir, host_tag)
+    if host_tag == 'windows-x86_64' and not os.path.isdir(prebuilt_dir):
+        # Try the 32-bits toolchain instead.
+        host_tag = 'windows'
+        prebuilt_dir = os.path.join(prebuilt_dir, host_tag)
+
+    SDK["ANDROID_TOOLCHAIN"] = prebuilt_dir
+
+    # And locate the GCC toolchain, which is needed for some tools (eg. as/ld)
     arch = GetTargetArch()
-    if arch == 'armv7a' or arch == 'arm':
-        arch = 'arm'
-        toolchain = 'arm-linux-androideabi-' + gcc_ver
-    elif arch == 'aarch64':
-        toolchain = 'aarch64-linux-android-' + gcc_ver
-    elif arch == 'mips':
-        toolchain = 'mipsel-linux-android-' + gcc_ver
-    elif arch == 'mips64':
-        toolchain = 'mips64el-linux-android-' + gcc_ver
-    elif arch == 'x86':
-        toolchain = 'x86-' + gcc_ver
-    elif arch == 'x86_64':
-        toolchain = 'x86_64-' + gcc_ver
-    SDK["ANDROID_TOOLCHAIN"] = os.path.join(ndk_root, 'toolchains', toolchain)
+    for opt in (TOOLCHAIN_PREFIX + '4.9', arch + '-4.9', TOOLCHAIN_PREFIX + '4.8', arch + '-4.8'):
+        if os.path.isdir(os.path.join(ndk_root, 'toolchains', opt)):
+            SDK["ANDROID_GCC_TOOLCHAIN"] = os.path.join(ndk_root, 'toolchains', opt, 'prebuilt', host_tag)
+            break
+
+    # The prebuilt binaries have no toolchain prefix.
+    TOOLCHAIN_PREFIX = ''
 
     # Determine the sysroot directory.
-    SDK["SYSROOT"] = os.path.join(ndk_root, 'platforms', 'android-%s' % (api), 'arch-%s' % (arch))
+    if arch == 'armv7a':
+        arch_dir = 'arch-arm'
+    elif arch == 'aarch64':
+        arch_dir = 'arch-arm64'
+    else:
+        arch_dir = 'arch-' + arch
+    SDK["SYSROOT"] = os.path.join(ndk_root, 'platforms', 'android-%s' % (api), arch_dir).replace('\\', '/')
     #IncDirectory("ALWAYS", os.path.join(SDK["SYSROOT"], 'usr', 'include'))
 
-    stdlibc = os.path.join(ndk_root, 'sources', 'cxx-stl', 'gnu-libstdc++', gcc_ver)
-    SDK["ANDROID_STL"] = stdlibc
+    # Starting with NDK r16, libc++ is the recommended STL to use.
+    stdlibc = os.path.join(ndk_root, 'sources', 'cxx-stl', 'llvm-libc++')
+    IncDirectory("ALWAYS", os.path.join(stdlibc, 'include').replace('\\', '/'))
+    LibDirectory("ALWAYS", os.path.join(stdlibc, 'libs', abi).replace('\\', '/'))
 
-    #IncDirectory("ALWAYS", os.path.join(stdlibc, 'include'))
-    #IncDirectory("ALWAYS", os.path.join(stdlibc, 'libs', abi, 'include'))
+    stl_lib = os.path.join(stdlibc, 'libs', abi, 'libc++_shared.so')
+    LibName("ALWAYS", stl_lib.replace('\\', '/'))
+    CopyFile(os.path.join(GetOutputDir(), 'lib', 'libc++_shared.so'), stl_lib)
 
-    stl_lib = os.path.join(stdlibc, 'libs', abi, 'libgnustl_shared.so')
-    LibName("ALWAYS", stl_lib)
-    CopyFile(os.path.join(GetOutputDir(), 'libs', abi, 'libgnustl_shared.so'), stl_lib)
+    # The Android support library polyfills C++ features not available in the
+    # STL that ships with Android.
+    support = os.path.join(ndk_root, 'sources', 'android', 'support', 'include')
+    IncDirectory("ALWAYS", support.replace('\\', '/'))
+    LibName("ALWAYS", "-landroid_support")
 
 ########################################################################
 ##
@@ -2675,6 +2709,10 @@ def SetupBuildEnvironment(compiler):
     os.environ["LC_ALL"] = "en_US.UTF-8"
     os.environ["LANGUAGE"] = "en"
 
+    # In the case of Android, we have to put the toolchain on the PATH in order to use it.
+    if GetTarget() == 'android' and GetHost() != 'android':
+        AddToPathEnv("PATH", os.path.join(SDK["ANDROID_TOOLCHAIN"], "bin"))
+
     if compiler == "MSVC":
         # Add the visual studio tools to PATH et al.
         SetupVisualStudioEnviron()
@@ -2709,11 +2747,15 @@ def SetupBuildEnvironment(compiler):
                 continue
 
             line = line[12:].strip()
-            for libdir in line.split(':'):
-                libdir = os.path.normpath(libdir)
+            libdirs = line.split(':')
+            while libdirs:
+                libdir = os.path.normpath(libdirs.pop(0))
                 if os.path.isdir(libdir):
                     if libdir not in SYS_LIB_DIRS:
                         SYS_LIB_DIRS.append(libdir)
+                elif len(libdir) == 1:
+                    # Oops, is this a drive letter?  Prepend it to the next.
+                    libdirs[0] = libdir + ':' + libdirs[0]
                 elif GetVerbose():
                     print("Ignoring non-existent library directory %s" % (libdir))
 
@@ -2771,33 +2813,6 @@ def SetupBuildEnvironment(compiler):
             for dir in SYS_INC_DIRS:
                 print("  " + dir)
 
-    # In the case of Android, we have to put the toolchain on the PATH in order to use it.
-    if GetTarget() == 'android' and GetHost() != 'android':
-        # Locate the directory where the toolchain binaries reside.
-        prebuilt_dir = os.path.join(SDK['ANDROID_TOOLCHAIN'], 'prebuilt')
-        if not os.path.isdir(prebuilt_dir):
-            exit('Not found: %s' % (prebuilt_dir))
-
-        host_tag = GetHost() + '-x86'
-        if host_64:
-            host_tag += '_64'
-        elif host_tag == 'windows-x86':
-            host_tag = 'windows'
-
-        prebuilt_dir = os.path.join(prebuilt_dir, host_tag)
-        if host_64 and not os.path.isdir(prebuilt_dir):
-            # Try the 32-bits toolchain instead.
-            prebuilt_dir = os.path.join(prebuilt_dir, host_tag)
-
-        if not os.path.isdir(prebuilt_dir):
-            if host_64:
-                exit('Not found: %s or %s' % (prebuilt_dir, host_tag))
-            else:
-                exit('Not found: %s' % (prebuilt_dir))
-
-        # Then, add it to the PATH.
-        AddToPathEnv("PATH", os.path.join(prebuilt_dir, 'bin'))
-
     # If we're cross-compiling, no point in putting our output dirs on the path.
     if CrossCompiling():
         return