• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kivy / python-for-android / 22259302242

21 Feb 2026 03:24PM UTC coverage: 63.887% (+4.7%) from 59.214%
22259302242

Pull #3198

github

web-flow
Merge 758a52847 into 1fc026943
Pull Request #3198: Bump SDL3 (`3.4.2`) and SDL3_image (`3.4.0`) to the latest stable releases.

1823 of 3111 branches covered (58.6%)

Branch coverage included in aggregate %.

3 of 3 new or added lines in 2 files covered. (100.0%)

788 existing lines in 24 files now uncovered.

5287 of 8018 relevant lines covered (65.94%)

5.26 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.44
/pythonforandroid/archs.py
1
from os import environ
8✔
2
from os.path import join
8✔
3
from multiprocessing import cpu_count
8✔
4
import shutil
8✔
5

6
from pythonforandroid.recipe import Recipe
8✔
7
from pythonforandroid.util import BuildInterruptingException, build_platform
8✔
8

9

10
class Arch:
8✔
11

12
    command_prefix = None
8✔
13
    '''The prefix for NDK commands such as gcc.'''
6✔
14

15
    arch = ""
8✔
16
    '''Name of the arch such as: `armeabi-v7a`, `arm64-v8a`, `x86`...'''
6✔
17

18
    arch_cflags = []
8✔
19
    '''Specific arch `cflags`, expect to be overwrote in subclass if needed.'''
6✔
20

21
    common_cflags = [
8✔
22
        '-target {target}',
23
        '-fomit-frame-pointer'
24
    ]
25

26
    common_cppflags = [
8✔
27
        '-DANDROID',
28
        '-I{ctx.ndk.sysroot_include_dir}',
29
        '-I{python_includes}',
30
    ]
31

32
    common_ldflags = ['-L{ctx_libs_dir}']
8✔
33

34
    common_ldlibs = ['-lm']
8✔
35

36
    common_ldshared = [
8✔
37
        '-pthread',
38
        '-shared',
39
        '-Wl,-O1',
40
        '-Wl,-Bsymbolic-functions',
41
    ]
42

43
    def __init__(self, ctx):
8✔
44
        self.ctx = ctx
8✔
45

46
        # Allows injecting additional linker paths used by any recipe.
47
        # This can also be modified by recipes (like the librt recipe)
48
        # to make sure that some sort of global resource is available &
49
        # linked for all others.
50
        self.extra_global_link_paths = []
8✔
51

52
    def __str__(self):
8✔
53
        return self.arch
8✔
54

55
    @property
8✔
56
    def ndk_lib_dir(self):
8✔
57
        return join(self.ctx.ndk.sysroot_lib_dir, self.command_prefix)
8✔
58

59
    @property
8✔
60
    def ndk_lib_dir_versioned(self):
8✔
61
        return join(self.ndk_lib_dir, str(self.ctx.ndk_api))
8✔
62

63
    @property
8✔
64
    def include_dirs(self):
8✔
65
        return [
8✔
66
            "{}/{}".format(
67
                self.ctx.include_dir,
68
                d.format(arch=self))
69
            for d in self.ctx.include_dirs]
70

71
    @property
8✔
72
    def target(self):
8✔
73
        # As of NDK r19, the toolchains installed by default with the
74
        # NDK may be used in-place. The make_standalone_toolchain.py script
75
        # is no longer needed for interfacing with arbitrary build systems.
76
        # See: https://developer.android.com/ndk/guides/other_build_systems
77
        return '{triplet}{ndk_api}'.format(
8✔
78
            triplet=self.command_prefix, ndk_api=self.ctx.ndk_api
79
        )
80

81
    @property
8✔
82
    def clang_exe(self):
8✔
83
        """Full path of the clang compiler depending on the android's ndk
84
        version used."""
85
        return self.get_clang_exe()
8✔
86

87
    @property
8✔
88
    def clang_exe_cxx(self):
8✔
89
        """Full path of the clang++ compiler depending on the android's ndk
90
        version used."""
91
        return self.get_clang_exe(plus_plus=True)
8✔
92

93
    def get_clang_exe(self, with_target=False, plus_plus=False):
8✔
94
        """Returns the full path of the clang/clang++ compiler, supports two
95
        kwargs:
96

97
          - `with_target`: prepend `target` to clang
98
          - `plus_plus`: will return the clang++ compiler (defaults to `False`)
99
        """
100
        compiler = 'clang'
8✔
101
        if with_target:
8✔
102
            compiler = '{target}-{compiler}'.format(
8✔
103
                target=self.target, compiler=compiler
104
            )
105
        if plus_plus:
8✔
106
            compiler += '++'
8✔
107
        return join(self.ctx.ndk.llvm_bin_dir, compiler)
8✔
108

109
    def get_env(self, with_flags_in_cc=True):
8✔
110
        env = {}
8✔
111

112
        # HOME: User's home directory
113
        #
114
        # Many tools including p4a store outputs in the user's home
115
        # directory. This is found from the HOME environment variable
116
        # and falls back to the system account database. Setting HOME
117
        # can be used to globally divert these tools to use a different
118
        # path. Furthermore, in containerized environments the user may
119
        # not exist in the account database, so if HOME isn't set than
120
        # these tools will fail.
121
        if 'HOME' in environ:
8!
122
            env['HOME'] = environ['HOME']
8✔
123

124
        # CFLAGS/CXXFLAGS: the processor flags
125
        env['CFLAGS'] = ' '.join(self.common_cflags).format(target=self.target)
8✔
126
        if self.arch_cflags:
8✔
127
            # each architecture may have has his own CFLAGS
128
            env['CFLAGS'] += ' ' + ' '.join(self.arch_cflags)
8✔
129
        env['CXXFLAGS'] = env['CFLAGS']
8✔
130

131
        # CPPFLAGS (for macros and includes)
132
        env['CPPFLAGS'] = ' '.join(self.common_cppflags).format(
8✔
133
            ctx=self.ctx,
134
            command_prefix=self.command_prefix,
135
            python_includes=join(Recipe.get_recipe("python3", self.ctx).include_root(self.arch))
136
        )
137

138
        # LDFLAGS: Link the extra global link paths first before anything else
139
        # (such that overriding system libraries with them is possible)
140
        env['LDFLAGS'] = (
8✔
141
            ' '
142
            + " ".join(
143
                [
144
                    "-L"
145
                    + link_path
146
                    for link_path in self.extra_global_link_paths
147
                ]
148
            )
149
            + ' ' + ' '.join(self.common_ldflags).format(
150
                ctx_libs_dir=self.ctx.get_libs_dir(self.arch)
151
            )
152
        )
153

154
        # LDLIBS: Library flags or names given to compilers when they are
155
        # supposed to invoke the linker.
156
        env['LDLIBS'] = ' '.join(self.common_ldlibs)
8✔
157

158
        # CCACHE
159
        ccache = ''
8✔
160
        if self.ctx.ccache and bool(int(environ.get('USE_CCACHE', '1'))):
8✔
161
            # print('ccache found, will optimize builds')
162
            ccache = self.ctx.ccache + ' '
8✔
163
            env['USE_CCACHE'] = '1'
8✔
164
            env['NDK_CCACHE'] = self.ctx.ccache
8✔
165
            env.update(
8✔
166
                {k: v for k, v in environ.items() if k.startswith('CCACHE_')}
167
            )
168

169
        # Compiler: `CC` and `CXX` (and make sure that the compiler exists)
170
        env['PATH'] = self.ctx.env['PATH']
8✔
171
        cc = shutil.which(self.clang_exe, path=env['PATH'])
8✔
172
        if cc is None:
8✔
173
            print('Searching path are: {!r}'.format(env['PATH']))
8✔
174
            raise BuildInterruptingException(
8✔
175
                'Couldn\'t find executable for CC. This indicates a '
176
                'problem locating the {} executable in the Android '
177
                'NDK, not that you don\'t have a normal compiler '
178
                'installed. Exiting.'.format(self.clang_exe))
179

180
        if with_flags_in_cc:
8✔
181
            env['CC'] = '{ccache}{exe} {cflags}'.format(
8✔
182
                exe=self.clang_exe,
183
                ccache=ccache,
184
                cflags=env['CFLAGS'])
185
            env['CXX'] = '{ccache}{execxx} {cxxflags}'.format(
8✔
186
                execxx=self.clang_exe_cxx,
187
                ccache=ccache,
188
                cxxflags=env['CXXFLAGS'])
189
        else:
190
            env['CC'] = '{ccache}{exe}'.format(
8✔
191
                exe=self.clang_exe,
192
                ccache=ccache)
193
            env['CXX'] = '{ccache}{execxx}'.format(
8✔
194
                execxx=self.clang_exe_cxx,
195
                ccache=ccache)
196

197
        # Android's LLVM binutils
198
        env['AR'] = self.ctx.ndk.llvm_ar
8✔
199
        env['RANLIB'] = self.ctx.ndk.llvm_ranlib
8✔
200
        env['STRIP'] = f'{self.ctx.ndk.llvm_strip} --strip-unneeded'
8✔
201
        env['READELF'] = self.ctx.ndk.llvm_readelf
8✔
202
        env['OBJCOPY'] = self.ctx.ndk.llvm_objcopy
8✔
203

204
        env['MAKE'] = 'make -j{}'.format(str(cpu_count()))
8✔
205

206
        # Android's arch/toolchain
207
        env['ARCH'] = self.arch
8✔
208
        env['NDK_API'] = 'android-{}'.format(str(self.ctx.ndk_api))
8✔
209

210
        # Custom linker options
211
        env['LDSHARED'] = env['CC'] + ' ' + ' '.join(self.common_ldshared)
8✔
212

213
        # Host python (used by some recipes)
214
        hostpython_recipe = Recipe.get_recipe(
8✔
215
            'host' + self.ctx.python_recipe.name, self.ctx)
216
        env['BUILDLIB_PATH'] = join(
8✔
217
            hostpython_recipe.get_build_dir(self.arch),
218
            'native-build',
219
            'build',
220
            'lib.{}-{}'.format(
221
                build_platform,
222
                self.ctx.python_recipe.major_minor_version_string,
223
            ),
224
        )
225

226
        # for reproducible builds
227
        if 'SOURCE_DATE_EPOCH' in environ:
8!
UNCOV
228
            for k in 'LC_ALL TZ SOURCE_DATE_EPOCH PYTHONHASHSEED BUILD_DATE BUILD_TIME'.split():
×
UNCOV
229
                if k in environ:
×
UNCOV
230
                    env[k] = environ[k]
×
231

232
        return env
8✔
233

234

235
class ArchARM(Arch):
8✔
236
    arch = "armeabi"
8✔
237
    command_prefix = 'arm-linux-androideabi'
8✔
238

239
    @property
8✔
240
    def target(self):
8✔
241
        target_data = self.command_prefix.split('-')
8✔
242
        return '{triplet}{ndk_api}'.format(
8✔
243
            triplet='-'.join(['armv7a', target_data[1], target_data[2]]),
244
            ndk_api=self.ctx.ndk_api,
245
        )
246

247

248
class ArchARMv7_a(ArchARM):
8✔
249
    arch = 'armeabi-v7a'
8✔
250
    arch_cflags = [
8✔
251
        '-march=armv7-a',
252
        '-mfloat-abi=softfp',
253
        '-mfpu=vfp',
254
        '-mthumb',
255
        '-fPIC',
256
    ]
257

258

259
class Archx86(Arch):
8✔
260
    arch = 'x86'
8✔
261
    command_prefix = 'i686-linux-android'
8✔
262
    arch_cflags = [
8✔
263
        '-march=i686',
264
        '-mssse3',
265
        '-mfpmath=sse',
266
        '-m32',
267
        '-fPIC',
268
    ]
269

270

271
class Archx86_64(Arch):
8✔
272
    arch = 'x86_64'
8✔
273
    command_prefix = 'x86_64-linux-android'
8✔
274
    arch_cflags = [
8✔
275
        '-march=x86-64',
276
        '-msse4.2',
277
        '-mpopcnt',
278
        '-m64',
279
        '-fPIC',
280
    ]
281

282

283
class ArchAarch_64(Arch):
8✔
284
    arch = 'arm64-v8a'
8✔
285
    command_prefix = 'aarch64-linux-android'
8✔
286
    arch_cflags = [
8✔
287
        '-march=armv8-a',
288
        '-fPIC'
289
        # '-I' + join(dirname(__file__), 'includes', 'arm64-v8a'),
290
    ]
291

292
    # Note: This `EXTRA_CFLAGS` below should target the commented `include`
293
    # above in `arch_cflags`. The original lines were added during the Sdl2's
294
    # bootstrap creation, and modified/commented during the migration to the
295
    # NDK r19 build system, because it seems that we don't need it anymore,
296
    # do we need them?
297
    # def get_env(self, with_flags_in_cc=True):
298
    #     env = super().get_env(with_flags_in_cc)
299
    #     env['EXTRA_CFLAGS'] = self.arch_cflags[-1]
300
    #     return env
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc