Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion demo/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ android {

// Python unit tests
pip {
install "murmurhash==0.28.0" // Requires chaquopy-libcxx
install "chaquopy-flac==1.3.3"
install "murmurhash==0.28.0"
}
staticProxy("chaquopy.test.static_proxy.basic", "chaquopy.test.static_proxy.header",
"chaquopy.test.static_proxy.method")
Expand Down
23 changes: 22 additions & 1 deletion product/runtime/src/main/python/java/android/importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from zipimport import zipimporter

import java.chaquopy
from java._vendor.elftools.elf.elffile import ELFFile
from java._vendor.elftools.elf.elffile import ELFError, ELFFile
from java.chaquopy_android import AssetFile

from android.os import Build
Expand Down Expand Up @@ -71,12 +71,33 @@ def hook(path):
not any(finder.exists(f"{name}/__init__{suffix}") for suffix in LOADERS):
finder.extract_dir(name)

# Extract executables.
if finder.isdir("chaquopy/bin"):
for name in finder.listdir("chaquopy/bin"):
# If chaquopy/ is a Python package, it won't have been extracted above.
filename = finder.extract_if_changed(f"chaquopy/bin/{name}")
os.chmod(filename, 0o755)
try:
finder.extract_needed(filename)
except ELFError:
pass # Maybe it's a shell script or something.

prepend_path("PATH", f"{finder.path}/chaquopy/bin")
prepend_path("LD_LIBRARY_PATH", f"{finder.path}/chaquopy/lib")

# We do this here instead of in AssetFinder.__init__ because code in the .pth files may
# require the finder to be fully available to the system, which isn't the case until
# get_importer returns.
site.addsitedir(finder.extract_root)


def prepend_path(variable, path):
old_path = os.environ.get(variable)
if old_path:
path += ":" + old_path
os.environ[variable] = path


def initialize_ctypes():
import ctypes.util
import sysconfig
Expand Down
33 changes: 30 additions & 3 deletions product/runtime/src/test/python/chaquopy/test/test_android.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
# Flags from PEP 3149.
ABI_FLAGS = ""

REQUIREMENTS = ["chaquopy-libcxx", "murmurhash", "Pygments"]
REQUIREMENTS = ["chaquopy-libcxx", "chaquopy-flac", "chaquopy-libogg",
"murmurhash", "Pygments"]

try:
from android.os import Build
Expand Down Expand Up @@ -164,6 +165,24 @@ def test_so(self):
# Library extraction caused by importing a Python module linked against it.
self.check_extract_if_changed(mod, LIBCXX_FILENAME)

def test_bin(self):
chaquopy_dir = asset_path(REQS_ABI_ZIP, "chaquopy")
self.assertCountEqual(["bin", "lib"], os.listdir(chaquopy_dir))

# Execution of flac is covered by test_subprocess.
self.assertCountEqual(["flac", "metaflac"], os.listdir(f"{chaquopy_dir}/bin"))

# Library extraction caused by executables in chaquopy/bin. Notes:
# * libFLAC and libogg are needed by the flac executable.
# * libFLAC++ isn't needed by any executable, so it shouldn't exist at this point.
# However, its presence in the APK is checked by test_ctypes.
# * libc++_shared may exist if the tests have been run before.
actual = os.listdir(f"{chaquopy_dir}/lib")
expected = ["libFLAC.so", "libogg.so"]
if len(actual) > len(expected):
expected.append("libc++_shared.so")
self.assertCountEqual(expected, actual)

def test_non_package_data(self):
for dir_name, dir_description in [("", "root"), ("non_package_data", "directory"),
("non_package_data/subdir", "subdirectory")]:
Expand Down Expand Up @@ -774,11 +793,14 @@ def test_ctypes(self):

# Library extraction caused by find_library.
os.remove(LIBCXX_FILENAME)
find_library_result = find_library("c++_shared")
find_library_result = find_library("FLAC++")
self.assertIsInstance(find_library_result, str)
if platform.architecture()[0] == "32bit" or Build.VERSION.SDK_INT >= 23:
self.assertRegex(find_library_result, r"^/")
libflacxx_filename = asset_path(REQS_ABI_ZIP, "chaquopy/lib/libFLAC++.so")
self.assertPredicate(exists, libflacxx_filename)
self.assertPredicate(exists, LIBCXX_FILENAME)
os.remove(libflacxx_filename) # Clean up for test_bin.

# Work around double-underscore mangling of __android_log_write.
def assertHasSymbol(dll, name):
Expand Down Expand Up @@ -915,9 +937,14 @@ def test_ssl(self):
self.assertRegex(resp.info()["Content-type"], r"^text/html")

def test_subprocess(self):
# An executable on the PATH.
# A system executable.
subprocess.run(["ls", os.environ["HOME"]])

# An executable in the requirements.
self.assertEqual("flac 1.3.3\n",
subprocess.run(["flac", "--version"], capture_output=True, text=True)
.stdout)

# A nonexistent executable.
for name in ["nonexistent", # PATH search
"/system/bin/nonexistent"]: # Absolute filename
Expand Down