Skip to content

Commit 346db77

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

File tree

35 files changed

+353
-261
lines changed

35 files changed

+353
-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/[email protected]/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/[email protected]/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
@@ -122,7 +128,7 @@ jobs:
122128
run: |
123129
source ci/osx_ci.sh
124130
arm64_set_path_and_python_version 3.9.7
125-
brew install autoconf automake libtool openssl pkg-config
131+
brew install autoconf automake libtool openssl@1.1 pkg-config cmake
126132
make --file ci/makefiles/osx.mk
127133
- name: Build multi-arch apk Python 3 (armeabi-v7a, arm64-v8a, x86_64, x86)
128134
run: |
@@ -183,13 +189,19 @@ jobs:
183189
matrix:
184190
include:
185191
- runs_on: macos-latest
192+
ndk_version: '23b'
193+
openssl_pkg_config_path: /usr/local/opt/[email protected]/lib/pkgconfig
186194
- runs_on: apple-silicon-m1
187195
run_wrapper: arch -arm64 bash --noprofile --norc -eo pipefail {0}
196+
ndk_version: '24'
197+
openssl_pkg_config_path: /opt/homebrew/opt/[email protected]/lib/pkgconfig
188198
env:
189199
ANDROID_HOME: ${HOME}/.android
190200
ANDROID_SDK_ROOT: ${HOME}/.android/android-sdk
191201
ANDROID_SDK_HOME: ${HOME}/.android/android-sdk
192202
ANDROID_NDK_HOME: ${HOME}/.android/android-ndk
203+
ANDROID_NDK_VERSION: ${{ matrix.ndk_version }}
204+
PKG_CONFIG_PATH: ${{ matrix.openssl_pkg_config_path }}
193205
steps:
194206
- name: Checkout python-for-android
195207
uses: actions/checkout@v2
@@ -207,7 +219,7 @@ jobs:
207219
run: |
208220
source ci/osx_ci.sh
209221
arm64_set_path_and_python_version 3.9.7
210-
brew install autoconf automake libtool openssl pkg-config
222+
brew install autoconf automake libtool openssl@1.1 pkg-config cmake
211223
make --file ci/makefiles/osx.mk
212224
- name: Build multi-arch aab Python 3 (armeabi-v7a, arm64-v8a, x86_64, x86)
213225
run: |
@@ -266,14 +278,21 @@ jobs:
266278
android_arch: ["arm64-v8a", "armeabi-v7a", "x86_64", "x86"]
267279
runs_on: [macos-latest, apple-silicon-m1]
268280
include:
281+
- runs_on: macos-latest
282+
ndk_version: '23b'
283+
openssl_pkg_config_path: /usr/local/opt/[email protected]/lib/pkgconfig
269284
- runs_on: apple-silicon-m1
270285
run_wrapper: arch -arm64 bash --noprofile --norc -eo pipefail {0}
286+
ndk_version: '24'
287+
openssl_pkg_config_path: /opt/homebrew/opt/[email protected]/lib/pkgconfig
271288
env:
272289
ANDROID_HOME: ${HOME}/.android
273290
ANDROID_SDK_ROOT: ${HOME}/.android/android-sdk
274291
ANDROID_SDK_HOME: ${HOME}/.android/android-sdk
275292
ANDROID_NDK_HOME: ${HOME}/.android/android-ndk
276293
REBUILD_UPDATED_RECIPES_EXTRA_ARGS: --arch=${{ matrix.android_arch }}
294+
ANDROID_NDK_VERSION: ${{ matrix.ndk_version }}
295+
PKG_CONFIG_PATH: ${{ matrix.openssl_pkg_config_path }}
277296
steps:
278297
- name: Checkout python-for-android
279298
uses: actions/checkout@v2
@@ -293,7 +312,7 @@ jobs:
293312
run: |
294313
source ci/osx_ci.sh
295314
arm64_set_path_and_python_version 3.9.7
296-
brew install autoconf automake libtool openssl pkg-config
315+
brew install autoconf automake libtool openssl@1.1 pkg-config cmake
297316
make --file ci/makefiles/osx.mk
298317
- name: Rebuild updated recipes
299318
run: |

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):
@@ -97,9 +83,7 @@ class Context:
9783

9884
ccache = None # whether to use ccache
9985

100-
ndk_standalone = None
101-
ndk_sysroot = None
102-
ndk_include_dir = None # usr/include
86+
ndk = None
10387

10488
bootstrap = None
10589
bootstrap_build_dir = None
@@ -326,7 +310,6 @@ def prepare_build_environment(self,
326310
if ndk_dir is None:
327311
raise BuildInterruptingException('Android NDK dir was not specified')
328312
self.ndk_dir = realpath(ndk_dir)
329-
330313
check_ndk_version(ndk_dir)
331314

332315
ndk_api = None
@@ -346,6 +329,8 @@ def prepare_build_environment(self,
346329

347330
check_ndk_api(ndk_api, self.android_api)
348331

332+
self.ndk = AndroidNDK(self.ndk_dir)
333+
349334
# path to some tools
350335
self.ccache = sh.which("ccache")
351336
if not self.ccache:
@@ -360,17 +345,9 @@ def prepare_build_environment(self,
360345
' a python 3 target (which is the default)'
361346
' then THINGS WILL BREAK.')
362347

363-
py_platform = sys.platform
364-
if py_platform in ['linux2', 'linux3']:
365-
py_platform = 'linux'
366-
367-
self.ndk_standalone = get_ndk_standalone(self.ndk_dir)
368-
self.ndk_sysroot, ndk_sysroot_exists = get_ndk_sysroot(self.ndk_dir)
369-
self.ndk_include_dir = join(self.ndk_sysroot, 'usr', 'include')
370-
371348
self.env["PATH"] = ":".join(
372349
[
373-
f"{self.ndk_dir}/toolchains/llvm/prebuilt/{py_platform}-x86_64/bin",
350+
self.ndk.llvm_bin_dir,
374351
self.ndk_dir,
375352
f"{self.sdk_dir}/tools",
376353
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)