build.py 6.48 KB
Newer Older
Max Kellermann's avatar
Max Kellermann committed
1 2
#!/usr/bin/env python3

3
import os, os.path
4
import sys, subprocess
Max Kellermann's avatar
Max Kellermann committed
5

6 7
if len(sys.argv) < 4:
    print("Usage: build.py SDK_PATH NDK_PATH ABI [configure_args...]", file=sys.stderr)
Max Kellermann's avatar
Max Kellermann committed
8 9 10 11
    sys.exit(1)

sdk_path = sys.argv[1]
ndk_path = sys.argv[2]
12 13
android_abi = sys.argv[3]
configure_args = sys.argv[4:]
Max Kellermann's avatar
Max Kellermann committed
14 15 16 17 18 19 20 21 22

if not os.path.isfile(os.path.join(sdk_path, 'tools', 'android')):
    print("SDK not found in", ndk_path, file=sys.stderr)
    sys.exit(1)

if not os.path.isdir(ndk_path):
    print("NDK not found in", ndk_path, file=sys.stderr)
    sys.exit(1)

23 24 25 26
android_abis = {
    'armeabi-v7a': {
        'arch': 'arm-linux-androideabi',
        'ndk_arch': 'arm',
27
        'toolchain_arch': 'arm-linux-androideabi',
28 29 30
        'llvm_triple': 'armv7-none-linux-androideabi',
        'cflags': '-march=armv7-a -mfpu=vfp -mfloat-abi=softfp',
    },
31

32 33 34 35 36 37 38 39 40
    'arm64-v8a': {
        'android_api_level': '21',
        'arch': 'aarch64-linux-android',
        'ndk_arch': 'arm64',
        'toolchain_arch': 'aarch64-linux-android',
        'llvm_triple': 'aarch64-none-linux-android',
        'cflags': '',
    },

41 42 43 44 45 46 47
    'x86': {
        'arch': 'i686-linux-android',
        'ndk_arch': 'x86',
        'toolchain_arch': 'x86',
        'llvm_triple': 'i686-none-linux-android',
        'cflags': '-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32',
    },
48 49
}

50
# select the NDK target
51 52
abi_info = android_abis[android_abi]
arch = abi_info['arch']
53

Max Kellermann's avatar
Max Kellermann committed
54
# the path to the MPD sources
55
mpd_path = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]) or '.', '..'))
56
sys.path[0] = os.path.join(mpd_path, 'python')
Max Kellermann's avatar
Max Kellermann committed
57 58

# output directories
59
from build.dirs import lib_path, tarball_path, src_path
60
from build.meson import configure as run_meson
61

62
arch_path = os.path.join(lib_path, arch)
63
build_path = os.path.join(arch_path, 'build')
Max Kellermann's avatar
Max Kellermann committed
64 65 66 67 68 69

# build host configuration
build_arch = 'linux-x86_64'

# set up the NDK toolchain

70
class AndroidNdkToolchain:
71
    def __init__(self, tarball_path, src_path, build_path,
72
                 use_cxx):
73 74 75 76
        self.tarball_path = tarball_path
        self.src_path = src_path
        self.build_path = build_path

77
        ndk_arch = abi_info['ndk_arch']
78
        android_api_level = '21'
79
        ndk_platform = 'android-' + android_api_level
80 81 82 83 84

        # select the NDK compiler
        gcc_version = '4.9'

        ndk_platform_path = os.path.join(ndk_path, 'platforms', ndk_platform)
85
        sysroot = os.path.join(ndk_path, 'sysroot')
86
        target_root = os.path.join(ndk_platform_path, 'arch-' + ndk_arch)
87 88 89 90 91 92 93

        install_prefix = os.path.join(arch_path, 'root')

        self.arch = arch
        self.install_prefix = install_prefix
        self.sysroot = sysroot

94
        toolchain_path = os.path.join(ndk_path, 'toolchains', abi_info['toolchain_arch'] + '-' + gcc_version, 'prebuilt', build_arch)
95
        llvm_path = os.path.join(ndk_path, 'toolchains', 'llvm', 'prebuilt', build_arch)
96
        llvm_triple = abi_info['llvm_triple']
97

98
        common_flags = '-Os -g'
99
        common_flags += ' -fPIC'
100
        common_flags += ' ' + abi_info['cflags']
101 102

        toolchain_bin = os.path.join(toolchain_path, 'bin')
103 104 105 106
        llvm_bin = os.path.join(llvm_path, 'bin')
        self.cc = os.path.join(llvm_bin, 'clang')
        self.cxx = os.path.join(llvm_bin, 'clang++')
        common_flags += ' -target ' + llvm_triple + ' -integrated-as -gcc-toolchain ' + toolchain_path
107

108 109
        common_flags += ' -fvisibility=hidden -fdata-sections -ffunction-sections'

110
        self.ar = os.path.join(toolchain_bin, arch + '-ar')
111
        self.ranlib = os.path.join(toolchain_bin, arch + '-ranlib')
112 113 114
        self.nm = os.path.join(toolchain_bin, arch + '-nm')
        self.strip = os.path.join(toolchain_bin, arch + '-strip')

115 116
        self.cflags = common_flags
        self.cxxflags = common_flags
117 118 119
        self.cppflags = '--sysroot=' + sysroot + \
            ' -isystem ' + os.path.join(install_prefix, 'include') + \
            ' -isystem ' + os.path.join(sysroot, 'usr', 'include', arch) + \
120
            ' -D__ANDROID_API__=' + android_api_level
121 122 123 124 125
        self.ldflags = '--sysroot=' + sysroot + \
            ' -L' + os.path.join(install_prefix, 'lib') + \
            ' -L' + os.path.join(target_root, 'usr', 'lib') + \
            ' -B' + os.path.join(target_root, 'usr', 'lib') + \
            ' ' + common_flags
126 127
        self.libs = ''

128
        self.is_arm = ndk_arch == 'arm'
129
        self.is_armv7 = self.is_arm and 'armv7' in self.cflags
130
        self.is_aarch64 = ndk_arch == 'arm64'
131 132
        self.is_windows = False

133 134 135
        libcxx_path = os.path.join(ndk_path, 'sources/cxx-stl/llvm-libc++')
        libcxx_libs_path = os.path.join(libcxx_path, 'libs', android_abi)

136
        libstdcxx_flags = ''
137
        libstdcxx_cxxflags = libstdcxx_flags + ' -isystem ' + os.path.join(libcxx_path, 'include') + ' -isystem ' + os.path.join(ndk_path, 'sources/android/support/include')
138 139
        libstdcxx_ldflags = libstdcxx_flags + ' -L' + libcxx_libs_path
        libstdcxx_libs = '-lc++_static -lc++abi'
140 141

        if use_cxx:
142 143
            self.cxxflags += ' ' + libstdcxx_cxxflags
            self.ldflags += ' ' + libstdcxx_ldflags
144
            self.libs += ' ' + libstdcxx_libs
145 146 147 148 149

        self.env = dict(os.environ)

        # redirect pkg-config to use our root directory instead of the
        # default one on the build host
150 151 152 153 154 155 156 157 158
        import shutil
        bin_dir = os.path.join(install_prefix, 'bin')
        try:
            os.makedirs(bin_dir)
        except:
            pass
        self.pkg_config = shutil.copy(os.path.join(mpd_path, 'build', 'pkg-config.sh'),
                                      os.path.join(bin_dir, 'pkg-config'))
        self.env['PKG_CONFIG'] = self.pkg_config
Max Kellermann's avatar
Max Kellermann committed
159 160

# a list of third-party libraries to be used by MPD on Android
161
from build.libs import *
Max Kellermann's avatar
Max Kellermann committed
162
thirdparty_libs = [
163
    libmpdclient,
164 165 166 167 168 169 170
    libogg,
    libvorbis,
    opus,
    flac,
    libid3tag,
    ffmpeg,
    curl,
171
    libexpat,
172
    libnfs,
173
    boost,
Max Kellermann's avatar
Max Kellermann committed
174 175 176 177
]

# build the third-party libraries
for x in thirdparty_libs:
178
    toolchain = AndroidNdkToolchain(tarball_path, src_path, build_path,
179
                                    use_cxx=x.use_cxx)
180 181
    if not x.is_installed(toolchain):
        x.build(toolchain)
Max Kellermann's avatar
Max Kellermann committed
182 183

# configure and build MPD
184
toolchain = AndroidNdkToolchain(tarball_path, src_path, build_path,
185
                                use_cxx=True)
Max Kellermann's avatar
Max Kellermann committed
186

187 188 189 190 191 192 193 194 195 196
configure_args += [
    '-Dandroid_sdk=' + sdk_path,
    '-Dandroid_ndk=' + ndk_path,
    '-Dandroid_abi=' + android_abi,
    '-Dandroid_strip=' + toolchain.strip,
]

from build.meson import configure as run_meson
run_meson(toolchain, mpd_path, '.', configure_args)
subprocess.check_call(['/usr/bin/ninja'], env=toolchain.env)