makepanda: allow building for Android from Windows machine
This commit is contained in:
parent
c0b973b789
commit
eef2a93163
|
|
@ -7,6 +7,7 @@ import os
|
|||
import shutil
|
||||
import glob
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
|
||||
INSTALLER_DEB_FILE = """
|
||||
|
|
@ -837,7 +838,8 @@ def MakeInstallerAndroid(version, **kwargs):
|
|||
# off any suffix (eg. libfile.so.1.0), as Android does not support them.
|
||||
source_dir = os.path.join(outputdir, "lib")
|
||||
target_dir = os.path.join("apkroot", "lib", SDK["ANDROID_ABI"])
|
||||
oscmd("mkdir -p %s" % (target_dir))
|
||||
if not os.path.exists(target_dir):
|
||||
os.makedirs(target_dir, mode=0o755)
|
||||
|
||||
# Determine the library directories we should look in.
|
||||
libpath = [source_dir]
|
||||
|
|
@ -859,24 +861,26 @@ def MakeInstallerAndroid(version, **kwargs):
|
|||
# Already processed.
|
||||
return
|
||||
|
||||
oscmd("cp %s %s" % (source, target))
|
||||
shutil.copy(source, target)
|
||||
|
||||
# Walk through the library dependencies.
|
||||
oscmd("ldd %s | grep .so > %s/tmp/otool-libs.txt" % (target, outputdir), True)
|
||||
for line in open(outputdir + "/tmp/otool-libs.txt", "r"):
|
||||
line = line.strip()
|
||||
if not line:
|
||||
handle = subprocess.Popen(['readelf', '--dynamic', target], stdout=subprocess.PIPE)
|
||||
for line in handle.communicate()[0].splitlines():
|
||||
# The line will look something like:
|
||||
# 0x0000000000000001 (NEEDED) Shared library: [libpanda.so]
|
||||
line = line.decode('utf-8', 'replace').strip()
|
||||
if not line or '(NEEDED)' not in line or '[' not in line or ']' not in line:
|
||||
continue
|
||||
|
||||
if ' ' in line:
|
||||
line = line.split(' ', 1)[0]
|
||||
# Extract the part between square brackets.
|
||||
idx = line.index('[')
|
||||
dep = line[idx + 1 : line.index(']', idx)]
|
||||
|
||||
# Change .so.1.2 suffix to .so, as needed for loading in .apk
|
||||
if '.so.' in line:
|
||||
dep = line.rpartition('.so.')[0] + '.so'
|
||||
oscmd("patchelf --replace-needed %s %s %s" % (line, dep, target), True)
|
||||
else:
|
||||
dep = line
|
||||
if '.so.' in dep:
|
||||
orig_dep = dep
|
||||
dep = dep.rpartition('.so.')[0] + '.so'
|
||||
oscmd("patchelf --replace-needed %s %s %s" % (orig_dep, dep, target), True)
|
||||
|
||||
# Find it on the LD_LIBRARY_PATH.
|
||||
for dir in libpath:
|
||||
|
|
@ -969,11 +973,12 @@ def MakeInstallerAndroid(version, **kwargs):
|
|||
oscmd(aapt_cmd)
|
||||
|
||||
# And add all the libraries to it.
|
||||
oscmd("cd apkroot && aapt add ../%s classes.dex" % (apk_unaligned))
|
||||
oscmd("aapt add %s classes.dex" % (os.path.join('..', apk_unaligned)), cwd="apkroot")
|
||||
for path, dirs, files in os.walk('apkroot/lib'):
|
||||
if files:
|
||||
rel = os.path.relpath(path, 'apkroot')
|
||||
oscmd("cd apkroot && aapt add ../%s %s/*" % (apk_unaligned, rel))
|
||||
rel_files = [os.path.join(rel, file).replace('\\', '/') for file in files]
|
||||
oscmd("aapt add %s %s" % (os.path.join('..', apk_unaligned), ' '.join(rel_files)), cwd="apkroot")
|
||||
|
||||
# Now align the .apk, which is necessary for Android to load it.
|
||||
oscmd("zipalign -v -p 4 %s %s" % (apk_unaligned, apk_unsigned))
|
||||
|
|
@ -984,7 +989,7 @@ def MakeInstallerAndroid(version, **kwargs):
|
|||
oscmd("apksigner debug.ks %s panda3d.apk" % (apk_unsigned))
|
||||
else:
|
||||
if not os.path.isfile('debug.ks'):
|
||||
oscmd("keytool -genkey -noprompt -dname 'CN=Panda3D,O=Panda3D,C=US' -keystore debug.ks -storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 1000")
|
||||
oscmd("keytool -genkey -noprompt -dname CN=Panda3D,O=Panda3D,C=US -keystore debug.ks -storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 1000")
|
||||
oscmd("apksigner sign --ks debug.ks --ks-pass pass:android --min-sdk-version %s --out panda3d.apk %s" % (SDK["ANDROID_API"], apk_unsigned))
|
||||
|
||||
# Clean up.
|
||||
|
|
|
|||
|
|
@ -564,11 +564,12 @@ def LocateBinary(binary):
|
|||
p = os.environ["PATH"]
|
||||
|
||||
pathList = p.split(os.pathsep)
|
||||
suffixes = ['']
|
||||
|
||||
if GetHost() == 'windows':
|
||||
if not binary.endswith('.exe'):
|
||||
if not binary.lower().endswith('.exe') and not binary.lower().endswith('.bat'):
|
||||
# Append .exe if necessary
|
||||
binary += '.exe'
|
||||
suffixes = ['.exe', '.bat']
|
||||
|
||||
# On Windows the current directory is always implicitly
|
||||
# searched before anything else on PATH.
|
||||
|
|
@ -576,8 +577,9 @@ def LocateBinary(binary):
|
|||
|
||||
for path in pathList:
|
||||
binpath = os.path.join(os.path.expanduser(path), binary)
|
||||
if os.access(binpath, os.X_OK):
|
||||
return os.path.abspath(os.path.realpath(binpath))
|
||||
for suffix in suffixes:
|
||||
if os.access(binpath + suffix, os.X_OK):
|
||||
return os.path.abspath(os.path.realpath(binpath + suffix))
|
||||
return None
|
||||
|
||||
########################################################################
|
||||
|
|
@ -586,20 +588,31 @@ def LocateBinary(binary):
|
|||
##
|
||||
########################################################################
|
||||
|
||||
def oscmd(cmd, ignoreError = False):
|
||||
def oscmd(cmd, ignoreError = False, cwd=None):
|
||||
if VERBOSE:
|
||||
print(GetColor("blue") + cmd.split(" ", 1)[0] + " " + GetColor("magenta") + cmd.split(" ", 1)[1] + GetColor())
|
||||
sys.stdout.flush()
|
||||
|
||||
if sys.platform == "win32":
|
||||
exe = cmd.split()[0]
|
||||
if cmd[0] == '"':
|
||||
exe = cmd[1 : cmd.index('"', 1)]
|
||||
else:
|
||||
exe = cmd.split()[0]
|
||||
exe_path = LocateBinary(exe)
|
||||
if exe_path is None:
|
||||
exit("Cannot find "+exe+" on search path")
|
||||
|
||||
if cwd is not None:
|
||||
pwd = os.getcwd()
|
||||
os.chdir(cwd)
|
||||
|
||||
res = os.spawnl(os.P_WAIT, exe_path, cmd)
|
||||
|
||||
if cwd is not None:
|
||||
os.chdir(pwd)
|
||||
else:
|
||||
cmd = cmd.replace(';', '\\;')
|
||||
res = subprocess.call(cmd, shell=True)
|
||||
res = subprocess.call(cmd, cwd=cwd, shell=True)
|
||||
sig = res & 0x7F
|
||||
if (GetVerbose() and res != 0):
|
||||
print(ColorText("red", "Process exited with exit status %d and signal code %d" % ((res & 0xFF00) >> 8, sig)))
|
||||
|
|
@ -2468,9 +2481,15 @@ def SdkLocateAndroid():
|
|||
SDK["ANDROID_JAR"] = prefix + "/share/aapt/android.jar"
|
||||
return
|
||||
|
||||
# Find the location of the Android SDK.
|
||||
sdk_root = os.environ.get('ANDROID_HOME')
|
||||
if not sdk_root or not os.path.isdir(sdk_root):
|
||||
sdk_root = os.environ.get('ANDROID_SDK_ROOT')
|
||||
|
||||
# Try the default installation location on Windows.
|
||||
if not sdk_root and GetHost() == 'windows':
|
||||
sdk_root = os.path.expanduser(os.path.join('~', 'AppData', 'Local', 'Android', 'Sdk'))
|
||||
|
||||
if not sdk_root:
|
||||
exit('ANDROID_SDK_ROOT must be set when compiling for Android!')
|
||||
elif not os.path.isdir(sdk_root):
|
||||
|
|
@ -2550,6 +2569,8 @@ def SdkLocateAndroid():
|
|||
|
||||
# Determine the location of android.jar.
|
||||
SDK["ANDROID_JAR"] = os.path.join(sdk_root, 'platforms', 'android-%s' % (api), 'android.jar')
|
||||
if not os.path.isfile(SDK["ANDROID_JAR"]):
|
||||
exit("Cannot find %s. Install platform API level %s via the SDK manager or change the targeted API level with --target=android-#" % (SDK["ANDROID_JAR"], api))
|
||||
|
||||
# Which build tools versions do we have? Pick the latest.
|
||||
versions = []
|
||||
|
|
@ -2564,6 +2585,24 @@ def SdkLocateAndroid():
|
|||
version = versions[-1]
|
||||
SDK["ANDROID_BUILD_TOOLS"] = os.path.join(sdk_root, "build-tools", "{0}.{1}.{2}".format(*version))
|
||||
|
||||
# And find the location of the Java compiler.
|
||||
if GetHost() == "windows":
|
||||
jdk_home = os.environ.get("JDK_HOME") or os.environ.get("JAVA_HOME")
|
||||
if not jdk_home:
|
||||
# Try to use the Java shipped with Android Studio.
|
||||
studio_path = GetRegistryKey("SOFTWARE\\Android Studio", "Path", override64=False)
|
||||
if studio_path and os.path.isdir(studio_path):
|
||||
jdk_home = os.path.join(studio_path, "jre")
|
||||
|
||||
if not jdk_home or not os.path.isdir(jdk_home):
|
||||
exit("Cannot find JDK. Please set JDK_HOME or JAVA_HOME.")
|
||||
|
||||
javac = os.path.join(jdk_home, "bin", "javac.exe")
|
||||
if not os.path.isfile(javac):
|
||||
exit("Cannot find %s. Install the JDK and set JDK_HOME or JAVA_HOME." % (javac))
|
||||
|
||||
SDK["JDK"] = jdk_home
|
||||
|
||||
########################################################################
|
||||
##
|
||||
## SDK Auto-Disables
|
||||
|
|
@ -2831,6 +2870,10 @@ def SetupBuildEnvironment(compiler):
|
|||
if "ANDROID_BUILD_TOOLS" in SDK:
|
||||
AddToPathEnv("PATH", SDK["ANDROID_BUILD_TOOLS"])
|
||||
|
||||
if "JDK" in SDK:
|
||||
AddToPathEnv("PATH", os.path.join(SDK["JDK"], "bin"))
|
||||
os.environ["JAVA_HOME"] = SDK["JDK"]
|
||||
|
||||
if compiler == "MSVC":
|
||||
# Add the visual studio tools to PATH et al.
|
||||
SetupVisualStudioEnviron()
|
||||
|
|
@ -2882,10 +2925,19 @@ def SetupBuildEnvironment(compiler):
|
|||
Warn("%s failed" % (cmd))
|
||||
SYS_LIB_DIRS += [SDK.get("SYSROOT", "") + "/usr/lib"]
|
||||
|
||||
# The Android toolchain on Windows doesn't actually add this one.
|
||||
if target == 'android' and GetHost() == 'windows':
|
||||
libdir = SDK.get("SYSROOT", "") + "/usr/lib"
|
||||
if GetTargetArch() == 'x86_64':
|
||||
libdir += '64'
|
||||
SYS_LIB_DIRS += [libdir]
|
||||
|
||||
# Now extract the preprocessor's include directories.
|
||||
cmd = GetCXX() + " -x c++ -v -E " + os.devnull
|
||||
if "ANDROID_NDK" in SDK:
|
||||
cmd += " --sysroot=%s/sysroot" % (SDK["ANDROID_NDK"].replace('\\', '/'))
|
||||
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"])
|
||||
else:
|
||||
cmd += sysroot_flag
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue