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

kivy / python-for-android / 3770697291

pending completion
3770697291

push

github

GitHub
Merge pull request #2718 from kivy/release-2022.12.20

877 of 2011 branches covered (43.61%)

Branch coverage included in aggregate %.

38 of 86 new or added lines in 16 files covered. (44.19%)

10 existing lines in 4 files now uncovered.

4515 of 6886 relevant lines covered (65.57%)

2.59 hits per line

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

92.81
/pythonforandroid/archs.py
1
from distutils.spawn import find_executable
4✔
2
from os import environ
4✔
3
from os.path import join
4✔
4
from multiprocessing import cpu_count
4✔
5

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

9

10
class Arch:
4✔
11

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

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

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

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

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

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

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

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

43
    def __init__(self, ctx):
4✔
44
        self.ctx = ctx
4✔
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 = []
4✔
51

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

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

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

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

71
    @property
4✔
72
    def target(self):
3✔
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(
4✔
78
            triplet=self.command_prefix, ndk_api=self.ctx.ndk_api
79
        )
80

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

87
    @property
4✔
88
    def clang_exe_cxx(self):
3✔
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)
4✔
92

93
    def get_clang_exe(self, with_target=False, plus_plus=False):
4✔
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'
4✔
101
        if with_target:
4✔
102
            compiler = '{target}-{compiler}'.format(
4✔
103
                target=self.target, compiler=compiler
104
            )
105
        if plus_plus:
4✔
106
            compiler += '++'
4✔
107
        return join(self.ctx.ndk.llvm_bin_dir, compiler)
4✔
108

109
    def get_env(self, with_flags_in_cc=True):
4✔
110
        env = {}
4✔
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:
4!
NEW
122
            env['HOME'] = environ['HOME']
×
123

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

131
        # CPPFLAGS (for macros and includes)
132
        env['CPPFLAGS'] = ' '.join(self.common_cppflags).format(
4✔
133
            ctx=self.ctx,
134
            command_prefix=self.command_prefix,
135
            python_includes=join(
136
                self.ctx.get_python_install_dir(self.arch),
137
                'include/python{}'.format(self.ctx.python_recipe.version[0:3]),
138
            ),
139
        )
140

141
        # LDFLAGS: Link the extra global link paths first before anything else
142
        # (such that overriding system libraries with them is possible)
143
        env['LDFLAGS'] = (
4✔
144
            ' '
145
            + " ".join(
146
                [
147
                    "-L'"
148
                    + link_path.replace("'", "'\"'\"'")
149
                    + "'"  # no shlex.quote in py2
150
                    for link_path in self.extra_global_link_paths
151
                ]
152
            )
153
            + ' ' + ' '.join(self.common_ldflags).format(
154
                ctx_libs_dir=self.ctx.get_libs_dir(self.arch)
155
            )
156
        )
157

158
        # LDLIBS: Library flags or names given to compilers when they are
159
        # supposed to invoke the linker.
160
        env['LDLIBS'] = ' '.join(self.common_ldlibs)
4✔
161

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

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

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

201
        # Android's LLVM binutils
202
        env['AR'] = self.ctx.ndk.llvm_ar
4✔
203
        env['RANLIB'] = self.ctx.ndk.llvm_ranlib
4✔
204
        env['STRIP'] = f'{self.ctx.ndk.llvm_strip} --strip-unneeded'
4✔
205
        env['READELF'] = self.ctx.ndk.llvm_readelf
4✔
206
        env['OBJCOPY'] = self.ctx.ndk.llvm_objcopy
4✔
207

208
        env['MAKE'] = 'make -j{}'.format(str(cpu_count()))
4✔
209

210
        # Android's arch/toolchain
211
        env['ARCH'] = self.arch
4✔
212
        env['NDK_API'] = 'android-{}'.format(str(self.ctx.ndk_api))
4✔
213

214
        # Custom linker options
215
        env['LDSHARED'] = env['CC'] + ' ' + ' '.join(self.common_ldshared)
4✔
216

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

230
        # for reproducible builds
231
        if 'SOURCE_DATE_EPOCH' in environ:
4!
232
            for k in 'LC_ALL TZ SOURCE_DATE_EPOCH PYTHONHASHSEED BUILD_DATE BUILD_TIME'.split():
×
233
                if k in environ:
×
234
                    env[k] = environ[k]
×
235

236
        return env
4✔
237

238

239
class ArchARM(Arch):
4✔
240
    arch = "armeabi"
4✔
241
    command_prefix = 'arm-linux-androideabi'
4✔
242

243
    @property
4✔
244
    def target(self):
3✔
245
        target_data = self.command_prefix.split('-')
4✔
246
        return '{triplet}{ndk_api}'.format(
4✔
247
            triplet='-'.join(['armv7a', target_data[1], target_data[2]]),
248
            ndk_api=self.ctx.ndk_api,
249
        )
250

251

252
class ArchARMv7_a(ArchARM):
4✔
253
    arch = 'armeabi-v7a'
4✔
254
    arch_cflags = [
4✔
255
        '-march=armv7-a',
256
        '-mfloat-abi=softfp',
257
        '-mfpu=vfp',
258
        '-mthumb',
259
        '-fPIC',
260
    ]
261

262

263
class Archx86(Arch):
4✔
264
    arch = 'x86'
4✔
265
    command_prefix = 'i686-linux-android'
4✔
266
    arch_cflags = [
4✔
267
        '-march=i686',
268
        '-mssse3',
269
        '-mfpmath=sse',
270
        '-m32',
271
        '-fPIC',
272
    ]
273

274

275
class Archx86_64(Arch):
4✔
276
    arch = 'x86_64'
4✔
277
    command_prefix = 'x86_64-linux-android'
4✔
278
    arch_cflags = [
4✔
279
        '-march=x86-64',
280
        '-msse4.2',
281
        '-mpopcnt',
282
        '-m64',
283
        '-fPIC',
284
    ]
285

286

287
class ArchAarch_64(Arch):
4✔
288
    arch = 'arm64-v8a'
4✔
289
    command_prefix = 'aarch64-linux-android'
4✔
290
    arch_cflags = [
4✔
291
        '-march=armv8-a',
292
        '-fPIC'
293
        # '-I' + join(dirname(__file__), 'includes', 'arm64-v8a'),
294
    ]
295

296
    # Note: This `EXTRA_CFLAGS` below should target the commented `include`
297
    # above in `arch_cflags`. The original lines were added during the Sdl2's
298
    # bootstrap creation, and modified/commented during the migration to the
299
    # NDK r19 build system, because it seems that we don't need it anymore,
300
    # do we need them?
301
    # def get_env(self, with_flags_in_cc=True):
302
    #     env = super().get_env(with_flags_in_cc)
303
    #     env['EXTRA_CFLAGS'] = self.arch_cflags[-1]
304
    #     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

© 2025 Coveralls, Inc