mpd/python/build/toolchain.py
2023-12-22 15:05:41 +01:00

192 lines
6.8 KiB
Python

import os.path
import platform
import shutil
from typing import Union
def with_ccache(command: str) -> str:
ccache = shutil.which('ccache')
if ccache is None:
return command
else:
return f'{ccache} {command}'
android_abis = {
'armeabi-v7a': {
'arch': 'armv7a-linux-androideabi',
'ndk_arch': 'arm',
'cflags': '-fpic -mfpu=neon -mfloat-abi=softfp',
},
'arm64-v8a': {
'arch': 'aarch64-linux-android',
'ndk_arch': 'arm64',
'cflags': '-fpic',
},
'x86': {
'arch': 'i686-linux-android',
'ndk_arch': 'x86',
'cflags': '-fPIC -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32',
},
'x86_64': {
'arch': 'x86_64-linux-android',
'ndk_arch': 'x86_64',
'cflags': '-fPIC -m64',
},
}
# https://developer.android.com/ndk/guides/other_build_systems
def build_arch() :
platforms = {
'Linux': 'linux-x86_64',
'Windows': 'windows-x86_64',
'Darwin': 'darwin-x86_64' # Despite the x86_64 tag in the Darwin name, those are fat binaries that include M1 support.
}
return platforms.get(platform.system())
class AndroidNdkToolchain:
def __init__(self, top_path: str, lib_path: str,
tarball_path: str, src_path: str,
ndk_path: str, android_abi: str,
use_cxx):
# select the NDK target
abi_info = android_abis[android_abi]
host_triplet = abi_info['arch']
arch_path = os.path.join(lib_path, host_triplet)
self.tarball_path = tarball_path
self.src_path = src_path
self.build_path = os.path.join(arch_path, 'build')
ndk_arch = abi_info['ndk_arch']
android_api_level = '24'
install_prefix = os.path.join(arch_path, 'root')
self.host_triplet = host_triplet
self.install_prefix = install_prefix
llvm_path = os.path.join(ndk_path, 'toolchains', 'llvm', 'prebuilt', build_arch())
llvm_triple = host_triplet + android_api_level
common_flags = '-Os -g'
common_flags += ' ' + abi_info['cflags']
llvm_bin = os.path.join(llvm_path, 'bin')
self.cc = with_ccache(os.path.join(llvm_bin, 'clang'))
self.cxx = with_ccache(os.path.join(llvm_bin, 'clang++'))
common_flags += ' -target ' + llvm_triple
common_flags += ' -fvisibility=hidden -fdata-sections -ffunction-sections'
self.ar = os.path.join(llvm_bin, 'llvm-ar')
self.arflags = 'rcs'
self.ranlib = os.path.join(llvm_bin, 'llvm-ranlib')
self.nm = os.path.join(llvm_bin, 'llvm-nm')
self.strip = os.path.join(llvm_bin, 'llvm-strip')
self.cflags = common_flags
self.cxxflags = common_flags
self.cppflags = ' -isystem ' + os.path.join(install_prefix, 'include')
self.ldflags = ' -L' + os.path.join(install_prefix, 'lib') + \
' -Wl,--exclude-libs=ALL' + \
' ' + common_flags
self.ldflags = common_flags
self.libs = ''
self.is_arm = ndk_arch == 'arm'
self.is_armv7 = self.is_arm and 'armv7' in self.cflags
self.is_aarch64 = ndk_arch == 'arm64'
self.is_windows = False
self.is_android = True
self.is_darwin = False
libstdcxx_flags = ''
libstdcxx_cxxflags = ''
libstdcxx_ldflags = ''
libstdcxx_libs = '-static-libstdc++'
if self.is_armv7:
# On 32 bit ARM, clang generates no ".eh_frame" section;
# instead, the LLVM unwinder library is used for unwinding
# the stack after a C++ exception was thrown
libstdcxx_libs += ' -lunwind'
if use_cxx:
self.cxxflags += ' ' + libstdcxx_cxxflags
self.ldflags += ' ' + libstdcxx_ldflags
self.libs += ' ' + libstdcxx_libs
self.env = dict(os.environ)
# redirect pkg-config to use our root directory instead of the
# default one on the build host
bin_dir = os.path.join(install_prefix, 'bin')
os.makedirs(bin_dir, exist_ok=True)
self.pkg_config = shutil.copy(os.path.join(top_path, 'build', 'pkg-config.sh'),
os.path.join(bin_dir, 'pkg-config'))
self.env['PKG_CONFIG'] = self.pkg_config
class MingwToolchain:
def __init__(self, top_path: str,
toolchain_path, host_triplet, x64: bool,
tarball_path, src_path, build_path, install_prefix):
self.host_triplet = host_triplet
self.tarball_path = tarball_path
self.src_path = src_path
self.build_path = build_path
self.install_prefix = install_prefix
toolchain_bin = os.path.join(toolchain_path, 'bin')
self.cc = with_ccache(os.path.join(toolchain_bin, host_triplet + '-gcc'))
self.cxx = with_ccache(os.path.join(toolchain_bin, host_triplet + '-g++'))
self.ar = os.path.join(toolchain_bin, host_triplet + '-ar')
self.arflags = 'rcs'
self.ranlib = os.path.join(toolchain_bin, host_triplet + '-ranlib')
self.nm = os.path.join(toolchain_bin, host_triplet + '-nm')
self.strip = os.path.join(toolchain_bin, host_triplet + '-strip')
self.windres = os.path.join(toolchain_bin, host_triplet + '-windres')
common_flags = '-O2 -g'
if not x64:
# enable SSE support which is required for LAME
common_flags += ' -march=pentium3'
self.cflags = common_flags
self.cxxflags = common_flags
self.cppflags = '-isystem ' + os.path.join(install_prefix, 'include') + \
' -DWINVER=0x0600 -D_WIN32_WINNT=0x0600'
self.ldflags = '-L' + os.path.join(install_prefix, 'lib') + \
' -static-libstdc++ -static-libgcc'
self.libs = ''
# Explicitly disable _FORTIFY_SOURCE because it is broken with
# mingw. This prevents some libraries such as libFLAC to
# enable it.
self.cppflags += ' -D_FORTIFY_SOURCE=0'
self.is_arm = host_triplet.startswith('arm')
self.is_armv7 = self.is_arm and 'armv7' in self.cflags
self.is_aarch64 = host_triplet == 'aarch64'
self.is_windows = 'mingw32' in host_triplet
self.is_android = False
self.is_darwin = False
self.env = dict(os.environ)
# redirect pkg-config to use our root directory instead of the
# default one on the build host
import shutil
bin_dir = os.path.join(install_prefix, 'bin')
os.makedirs(bin_dir, exist_ok=True)
self.pkg_config = shutil.copy(os.path.join(top_path, 'build', 'pkg-config.sh'),
os.path.join(bin_dir, 'pkg-config'))
self.env['PKG_CONFIG'] = self.pkg_config
AnyToolchain = Union[AndroidNdkToolchain, MingwToolchain]