Skip to content

Commit 8994795

Browse files
committed
Added pythonforandroid.androidndk.AndroidNDK + some changes needed in order to support build on Apple Silicon macs.
1 parent 4d45f99 commit 8994795

35 files changed

+355
-261
lines changed

.github/workflows/push.yml

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,19 @@ jobs:
9898
matrix:
9999
include:
100100
- runs_on: macos-latest
101+
ndk_version: '23b'
102+
openssl_pkg_config_path: /usr/local/opt/openssl@1.1/lib/pkgconfig
101103
- runs_on: apple-silicon-m1
102104
run_wrapper: arch -arm64 bash --noprofile --norc -eo pipefail {0}
105+
ndk_version: '24'
106+
openssl_pkg_config_path: /opt/homebrew/opt/openssl@1.1/lib/pkgconfig
103107
env:
104108
ANDROID_HOME: ${HOME}/.android
105109
ANDROID_SDK_ROOT: ${HOME}/.android/android-sdk
106110
ANDROID_SDK_HOME: ${HOME}/.android/android-sdk
107111
ANDROID_NDK_HOME: ${HOME}/.android/android-ndk
112+
ANDROID_NDK_VERSION: ${{ matrix.ndk_version }}
113+
PKG_CONFIG_PATH: ${{ matrix.openssl_pkg_config_path }}
108114
steps:
109115
- name: Checkout python-for-android
110116
uses: actions/checkout@v2
@@ -118,7 +124,7 @@ jobs:
118124
source ci/osx_ci.sh
119125
arm64_set_path_and_python_version 3.9.7
120126
python3 pythonforandroid/prerequisites.py
121-
- name: Install dependencies
127+
- name: Install dependencies (Legacy)
122128
run: |
123129
source ci/osx_ci.sh
124130
arm64_set_path_and_python_version 3.9.7
@@ -182,13 +188,19 @@ jobs:
182188
matrix:
183189
include:
184190
- runs_on: macos-latest
191+
ndk_version: '23b'
192+
openssl_pkg_config_path: /usr/local/opt/openssl@1.1/lib/pkgconfig
185193
- runs_on: apple-silicon-m1
186194
run_wrapper: arch -arm64 bash --noprofile --norc -eo pipefail {0}
195+
ndk_version: '24'
196+
openssl_pkg_config_path: /opt/homebrew/opt/openssl@1.1/lib/pkgconfig
187197
env:
188198
ANDROID_HOME: ${HOME}/.android
189199
ANDROID_SDK_ROOT: ${HOME}/.android/android-sdk
190200
ANDROID_SDK_HOME: ${HOME}/.android/android-sdk
191201
ANDROID_NDK_HOME: ${HOME}/.android/android-ndk
202+
ANDROID_NDK_VERSION: ${{ matrix.ndk_version }}
203+
PKG_CONFIG_PATH: ${{ matrix.openssl_pkg_config_path }}
192204
steps:
193205
- name: Checkout python-for-android
194206
uses: actions/checkout@v2
@@ -202,7 +214,7 @@ jobs:
202214
source ci/osx_ci.sh
203215
arm64_set_path_and_python_version 3.9.7
204216
python3 pythonforandroid/prerequisites.py
205-
- name: Install dependencies
217+
- name: Install dependencies (Legacy)
206218
run: |
207219
source ci/osx_ci.sh
208220
arm64_set_path_and_python_version 3.9.7
@@ -264,14 +276,21 @@ jobs:
264276
android_arch: ["arm64-v8a", "armeabi-v7a", "x86_64", "x86"]
265277
runs_on: [macos-latest, apple-silicon-m1]
266278
include:
279+
- runs_on: macos-latest
280+
ndk_version: '23b'
281+
openssl_pkg_config_path: /usr/local/opt/openssl@1.1/lib/pkgconfig
267282
- runs_on: apple-silicon-m1
268283
run_wrapper: arch -arm64 bash --noprofile --norc -eo pipefail {0}
284+
ndk_version: '24'
285+
openssl_pkg_config_path: /opt/homebrew/opt/openssl@1.1/lib/pkgconfig
269286
env:
270287
ANDROID_HOME: ${HOME}/.android
271288
ANDROID_SDK_ROOT: ${HOME}/.android/android-sdk
272289
ANDROID_SDK_HOME: ${HOME}/.android/android-sdk
273290
ANDROID_NDK_HOME: ${HOME}/.android/android-ndk
274291
REBUILD_UPDATED_RECIPES_EXTRA_ARGS: --arch=${{ matrix.android_arch }}
292+
ANDROID_NDK_VERSION: ${{ matrix.ndk_version }}
293+
PKG_CONFIG_PATH: ${{ matrix.openssl_pkg_config_path }}
275294
steps:
276295
- name: Checkout python-for-android
277296
uses: actions/checkout@v2
@@ -287,7 +306,7 @@ jobs:
287306
source ci/osx_ci.sh
288307
arm64_set_path_and_python_version 3.9.7
289308
python3 pythonforandroid/prerequisites.py
290-
- name: Install dependencies
309+
- name: Install dependencies (Legacy)
291310
run: |
292311
source ci/osx_ci.sh
293312
arm64_set_path_and_python_version 3.9.7

pythonforandroid/androidndk.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import sys
2+
import os
3+
4+
5+
class AndroidNDK(object):
6+
"""
7+
This class is used to get the current NDK information.
8+
"""
9+
10+
ndk_dir = ""
11+
12+
def __init__(self, ndk_dir):
13+
self.ndk_dir = ndk_dir
14+
15+
@property
16+
def host_tag(self):
17+
"""
18+
Returns the host tag for the current system.
19+
Note: The host tag is ``darwin-x86_64`` even on Apple Silicon macs.
20+
"""
21+
return f"{sys.platform}-x86_64"
22+
23+
@property
24+
def llvm_prebuilt_dir(self):
25+
return os.path.join(
26+
self.ndk_dir, "toolchains", "llvm", "prebuilt", self.host_tag
27+
)
28+
29+
@property
30+
def llvm_bin_dir(self):
31+
return os.path.join(self.llvm_prebuilt_dir, "bin")
32+
33+
@property
34+
def clang(self):
35+
return os.path.join(self.llvm_bin_dir, "clang")
36+
37+
@property
38+
def clang_cxx(self):
39+
return os.path.join(self.llvm_bin_dir, "clang++")
40+
41+
@property
42+
def llvm_binutils_prefix(self):
43+
return os.path.join(self.llvm_bin_dir, "llvm-")
44+
45+
@property
46+
def llvm_ar(self):
47+
return f"{self.llvm_binutils_prefix}ar"
48+
49+
@property
50+
def llvm_ranlib(self):
51+
return f"{self.llvm_binutils_prefix}ranlib"
52+
53+
@property
54+
def llvm_objcopy(self):
55+
return f"{self.llvm_binutils_prefix}objcopy"
56+
57+
@property
58+
def llvm_objdump(self):
59+
return f"{self.llvm_binutils_prefix}objdump"
60+
61+
@property
62+
def llvm_readelf(self):
63+
return f"{self.llvm_binutils_prefix}readelf"
64+
65+
@property
66+
def llvm_strip(self):
67+
return f"{self.llvm_binutils_prefix}strip"
68+
69+
@property
70+
def sysroot(self):
71+
return os.path.join(self.llvm_prebuilt_dir, "sysroot")
72+
73+
@property
74+
def sysroot_include_dir(self):
75+
return os.path.join(self.sysroot, "usr", "include")
76+
77+
@property
78+
def sysroot_lib_dir(self):
79+
return os.path.join(self.sysroot, "usr", "lib")
80+
81+
@property
82+
def libcxx_include_dir(self):
83+
return os.path.join(self.sysroot_include_dir, "c++", "v1")

pythonforandroid/archs.py

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class Arch:
2525

2626
common_cppflags = [
2727
'-DANDROID',
28-
'-I{ctx.ndk_sysroot}/usr/include',
28+
'-I{ctx.ndk.sysroot_include_dir}',
2929
'-I{python_includes}',
3030
]
3131

@@ -54,7 +54,11 @@ def __str__(self):
5454

5555
@property
5656
def ndk_lib_dir(self):
57-
return join(self.ctx.ndk_sysroot, 'usr', 'lib', self.command_prefix, str(self.ctx.ndk_api))
57+
return join(self.ctx.ndk.sysroot_lib_dir, self.command_prefix)
58+
59+
@property
60+
def ndk_lib_dir_versioned(self):
61+
return join(self.ndk_lib_dir, str(self.ctx.ndk_api))
5862

5963
@property
6064
def include_dirs(self):
@@ -74,18 +78,6 @@ def target(self):
7478
triplet=self.command_prefix, ndk_api=self.ctx.ndk_api
7579
)
7680

77-
@property
78-
def clang_path(self):
79-
"""Full path of the clang compiler"""
80-
return join(
81-
self.ctx.ndk_dir,
82-
'toolchains',
83-
'llvm',
84-
'prebuilt',
85-
build_platform,
86-
'bin',
87-
)
88-
8981
@property
9082
def clang_exe(self):
9183
"""Full path of the clang compiler depending on the android's ndk
@@ -112,7 +104,7 @@ def get_clang_exe(self, with_target=False, plus_plus=False):
112104
)
113105
if plus_plus:
114106
compiler += '++'
115-
return join(self.clang_path, compiler)
107+
return join(self.ctx.ndk.llvm_bin_dir, compiler)
116108

117109
def get_env(self, with_flags_in_cc=True):
118110
env = {}
@@ -195,11 +187,11 @@ def get_env(self, with_flags_in_cc=True):
195187
ccache=ccache)
196188

197189
# Android's LLVM binutils
198-
env['AR'] = f'{self.clang_path}/llvm-ar'
199-
env['RANLIB'] = f'{self.clang_path}/llvm-ranlib'
200-
env['STRIP'] = f'{self.clang_path}/llvm-strip --strip-unneeded'
201-
env['READELF'] = f'{self.clang_path}/llvm-readelf'
202-
env['OBJCOPY'] = f'{self.clang_path}/llvm-objcopy'
190+
env['AR'] = self.ctx.ndk.llvm_ar
191+
env['RANLIB'] = self.ctx.ndk.llvm_ranlib
192+
env['STRIP'] = f'{self.ctx.ndk.llvm_strip} --strip-unneeded'
193+
env['READELF'] = self.ctx.ndk.llvm_readelf
194+
env['OBJCOPY'] = self.ctx.ndk.llvm_objcopy
203195

204196
env['MAKE'] = 'make -j{}'.format(str(cpu_count()))
205197

pythonforandroid/build.py

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import copy
66
import os
77
import glob
8-
import sys
98
import re
109
import sh
1110
import shutil
@@ -23,20 +22,7 @@
2322
from pythonforandroid.recommendations import (
2423
check_ndk_version, check_target_api, check_ndk_api,
2524
RECOMMENDED_NDK_API, RECOMMENDED_TARGET_API)
26-
from pythonforandroid.util import build_platform
27-
28-
29-
def get_ndk_standalone(ndk_dir):
30-
return join(ndk_dir, 'toolchains', 'llvm', 'prebuilt', build_platform)
31-
32-
33-
def get_ndk_sysroot(ndk_dir):
34-
sysroot = join(get_ndk_standalone(ndk_dir), 'sysroot')
35-
sysroot_exists = True
36-
if not exists(sysroot):
37-
warning("sysroot doesn't exist: {}".format(sysroot))
38-
sysroot_exists = False
39-
return sysroot, sysroot_exists
25+
from pythonforandroid.androidndk import AndroidNDK
4026

4127

4228
def get_targets(sdk_dir):
@@ -101,9 +87,7 @@ class Context:
10187

10288
ccache = None # whether to use ccache
10389

104-
ndk_standalone = None
105-
ndk_sysroot = None
106-
ndk_include_dir = None # usr/include
90+
ndk = None
10791

10892
bootstrap = None
10993
bootstrap_build_dir = None
@@ -330,7 +314,6 @@ def prepare_build_environment(self,
330314
if ndk_dir is None:
331315
raise BuildInterruptingException('Android NDK dir was not specified')
332316
self.ndk_dir = realpath(ndk_dir)
333-
334317
check_ndk_version(ndk_dir)
335318

336319
ndk_api = None
@@ -350,6 +333,8 @@ def prepare_build_environment(self,
350333

351334
check_ndk_api(ndk_api, self.android_api)
352335

336+
self.ndk = AndroidNDK(self.ndk_dir)
337+
353338
# path to some tools
354339
self.ccache = sh.which("ccache")
355340
if not self.ccache:
@@ -364,17 +349,9 @@ def prepare_build_environment(self,
364349
' a python 3 target (which is the default)'
365350
' then THINGS WILL BREAK.')
366351

367-
py_platform = sys.platform
368-
if py_platform in ['linux2', 'linux3']:
369-
py_platform = 'linux'
370-
371-
self.ndk_standalone = get_ndk_standalone(self.ndk_dir)
372-
self.ndk_sysroot, ndk_sysroot_exists = get_ndk_sysroot(self.ndk_dir)
373-
self.ndk_include_dir = join(self.ndk_sysroot, 'usr', 'include')
374-
375352
self.env["PATH"] = ":".join(
376353
[
377-
f"{self.ndk_dir}/toolchains/llvm/prebuilt/{py_platform}-x86_64/bin",
354+
self.ndk.llvm_bin_dir,
378355
self.ndk_dir,
379356
f"{self.sdk_dir}/tools",
380357
environ.get("PATH"),

pythonforandroid/recipe.py

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -135,24 +135,9 @@ class Recipe(with_metaclass(RecipeMeta)):
135135
starting from NDK r18 the `gnustl_shared` lib has been deprecated.
136136
'''
137137

138-
stl_lib_source = '{ctx.ndk_dir}/sources/cxx-stl/llvm-libc++'
139-
'''
140-
The source directory of the selected stl lib, defined in property
141-
`stl_lib_name`
142-
'''
143-
144-
@property
145-
def stl_include_dir(self):
146-
return join(self.stl_lib_source.format(ctx=self.ctx), 'include')
147-
148-
def get_stl_lib_dir(self, arch):
149-
return join(
150-
self.stl_lib_source.format(ctx=self.ctx), 'libs', arch.arch
151-
)
152-
153138
def get_stl_library(self, arch):
154139
return join(
155-
self.get_stl_lib_dir(arch),
140+
arch.ndk_lib_dir,
156141
'lib{name}.so'.format(name=self.stl_lib_name),
157142
)
158143

@@ -510,14 +495,14 @@ def get_recipe_env(self, arch=None, with_flags_in_cc=True):
510495

511496
if self.need_stl_shared:
512497
env['CPPFLAGS'] = env.get('CPPFLAGS', '')
513-
env['CPPFLAGS'] += ' -I{}'.format(self.stl_include_dir)
498+
env['CPPFLAGS'] += ' -I{}'.format(self.ctx.ndk.libcxx_include_dir)
514499

515500
env['CXXFLAGS'] = env['CFLAGS'] + ' -frtti -fexceptions'
516501

517502
if with_flags_in_cc:
518503
env['CXX'] += ' -frtti -fexceptions'
519504

520-
env['LDFLAGS'] += ' -L{}'.format(self.get_stl_lib_dir(arch))
505+
env['LDFLAGS'] += ' -L{}'.format(arch.ndk_lib_dir)
521506
env['LIBS'] = env.get('LIBS', '') + " -l{}".format(
522507
self.stl_lib_name
523508
)

pythonforandroid/recipes/Pillow/__init__.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,6 @@ class PillowRecipe(CompiledComponentsPythonRecipe):
3535
def get_recipe_env(self, arch=None, with_flags_in_cc=True):
3636
env = super().get_recipe_env(arch, with_flags_in_cc)
3737

38-
ndk_lib_dir = arch.ndk_lib_dir
39-
ndk_include_dir = self.ctx.ndk_include_dir
40-
4138
png = self.get_recipe('png', self.ctx)
4239
png_lib_dir = join(png.get_build_dir(arch.arch), '.libs')
4340
png_inc_dir = png.get_build_dir(arch)
@@ -71,7 +68,7 @@ def get_recipe_env(self, arch=None, with_flags_in_cc=True):
7168
cflags += f' -I{jpeg_inc_dir}'
7269
if build_with_webp_support:
7370
cflags += f' -I{join(webp_install, "include")}'
74-
cflags += f' -I{ndk_include_dir}'
71+
cflags += f' -I{self.ctx.ndk.sysroot_include_dir}'
7572

7673
# Link the basic Pillow libraries...no need to add webp's libraries
7774
# since it seems that the linkage is properly made without it :)
@@ -84,7 +81,7 @@ def get_recipe_env(self, arch=None, with_flags_in_cc=True):
8481
env['LDFLAGS'] += f' -L{jpeg_lib_dir}'
8582
if build_with_webp_support:
8683
env['LDFLAGS'] += f' -L{join(webp_install, "lib")}'
87-
env['LDFLAGS'] += f' -L{ndk_lib_dir}'
84+
env['LDFLAGS'] += f' -L{arch.ndk_lib_dir_versioned}'
8885
if cflags not in env['CFLAGS']:
8986
env['CFLAGS'] += cflags + " -lm"
9087
return env

0 commit comments

Comments
 (0)