diff --git a/doc/source/quickstart.rst b/doc/source/quickstart.rst index 8e46b2c96d..3797ffacec 100644 --- a/doc/source/quickstart.rst +++ b/doc/source/quickstart.rst @@ -63,6 +63,7 @@ p4a has several dependencies that must be installed: - ccache (optional) - autoconf (for ffpyplayer_codecs recipe) - libtool (for ffpyplayer_codecs recipe) +- cmake (required for some native code recipes like jpeg's recipe) On recent versions of Ubuntu and its derivatives you may be able to install most of these with:: diff --git a/pythonforandroid/recipes/Pillow/__init__.py b/pythonforandroid/recipes/Pillow/__init__.py index 3dda58d909..14c9d2b26c 100644 --- a/pythonforandroid/recipes/Pillow/__init__.py +++ b/pythonforandroid/recipes/Pillow/__init__.py @@ -1,7 +1,5 @@ from pythonforandroid.recipe import CompiledComponentsPythonRecipe -from pythonforandroid.toolchain import shprint -from os.path import join, dirname -import sh +from os.path import join class PillowRecipe(CompiledComponentsPythonRecipe): @@ -9,64 +7,52 @@ class PillowRecipe(CompiledComponentsPythonRecipe): version = '5.2.0' url = 'https://github.com/python-pillow/Pillow/archive/{version}.tar.gz' site_packages_name = 'Pillow' - depends = [ - 'png', - 'jpeg', - 'freetype', - 'setuptools' - ] - patches = [ - join('patches', 'fix-docstring.patch'), - join('patches', 'fix-setup.patch') - ] + depends = ['png', 'jpeg', 'freetype', 'setuptools'] + patches = [join('patches', 'fix-docstring.patch'), + join('patches', 'fix-setup.patch')] call_hostpython_via_targetpython = False - def get_recipe_env(self, arch=None): - env = super(PillowRecipe, self).get_recipe_env(arch) - py_ver = self.ctx.python_recipe.version[0:3] + def get_recipe_env(self, arch=None, with_flags_in_cc=True): + env = super(PillowRecipe, self).get_recipe_env(arch, with_flags_in_cc) - ndk_dir = self.ctx.ndk_platform - ndk_lib_dir = join(ndk_dir, 'usr', 'lib') - ndk_include_dir = ( - join(self.ctx.ndk_dir, 'sysroot', 'usr', 'include') - if py_ver == '2.7' else join(ndk_dir, 'usr', 'include')) + env['ANDROID_ROOT'] = join(self.ctx.ndk_platform, 'usr') + ndk_lib_dir = join(self.ctx.ndk_platform, 'usr', 'lib') + ndk_include_dir = join(self.ctx.ndk_dir, 'sysroot', 'usr', 'include') png = self.get_recipe('png', self.ctx) png_lib_dir = png.get_lib_dir(arch) png_jni_dir = png.get_jni_dir(arch) jpeg = self.get_recipe('jpeg', self.ctx) - jpeg_lib_dir = jpeg.get_lib_dir(arch) - jpeg_jni_dir = jpeg.get_jni_dir(arch) + jpeg_inc_dir = jpeg_lib_dir = jpeg.get_build_dir(arch.arch) - env['JPEG_ROOT'] = '{}|{}'.format(jpeg_lib_dir, jpeg_jni_dir) + freetype = self.get_recipe('freetype', self.ctx) + free_lib_dir = join(freetype.get_build_dir(arch.arch), 'objs', '.libs') + free_inc_dir = join(freetype.get_build_dir(arch.arch), 'include') + + # harfbuzz is a direct dependency of freetype and we need the proper + # flags to successfully build the Pillow recipe, so we add them here. + harfbuzz = self.get_recipe('harfbuzz', self.ctx) + harf_lib_dir = join(harfbuzz.get_build_dir(arch.arch), 'src', '.libs') + harf_inc_dir = harfbuzz.get_build_dir(arch.arch) + + env['JPEG_ROOT'] = '{}|{}'.format(jpeg_lib_dir, jpeg_inc_dir) + env['FREETYPE_ROOT'] = '{}|{}'.format(free_lib_dir, free_inc_dir) env['ZLIB_ROOT'] = '{}|{}'.format(ndk_lib_dir, ndk_include_dir) - cflags = ' -nostdinc' - cflags += ' -I{} -L{}'.format(png_jni_dir, png_lib_dir) - cflags += ' -I{} -L{}'.format(jpeg_jni_dir, jpeg_lib_dir) - cflags += ' -I{} -L{}'.format(ndk_include_dir, ndk_lib_dir) - - gcc_lib = shprint( - sh.gcc, '-print-libgcc-file-name').stdout.decode('utf-8').split('\n')[0] - gcc_include = join(dirname(gcc_lib), 'include') - cflags += ' -I{}'.format(gcc_include) - - if self.ctx.ndk == 'crystax': - py_inc_dir = join( - self.ctx.ndk_dir, 'sources', 'python', py_ver, 'include', 'python') - py_lib_dir = join( - self.ctx.ndk_dir, 'sources', 'python', py_ver, 'libs', arch.arch) - cflags += ' -I{}'.format(py_inc_dir) - env['LDFLAGS'] += ' -L{} -lpython{}m'.format(py_lib_dir, py_ver) - - env['LDFLAGS'] += ' {} -L{}'.format(env['CFLAGS'], self.ctx.libs_dir) + cflags = ' -I{}'.format(png_jni_dir) + cflags += ' -I{} -I{}'.format(harf_inc_dir, join(harf_inc_dir, 'src')) + cflags += ' -I{}'.format(free_inc_dir) + cflags += ' -I{}'.format(jpeg_inc_dir) + cflags += ' -I{}'.format(ndk_include_dir) + + env['LIBS'] = ' -lpng -lfreetype -lharfbuzz -ljpeg -lturbojpeg' + + env['LDFLAGS'] += ' -L{} -L{} -L{} -L{}'.format( + png_lib_dir, harf_lib_dir, jpeg_lib_dir, ndk_lib_dir) if cflags not in env['CFLAGS']: env['CFLAGS'] += cflags - env['LDSHARED'] = '{} {}'.format( - env['CC'], - '-pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions') return env diff --git a/pythonforandroid/recipes/freetype/__init__.py b/pythonforandroid/recipes/freetype/__init__.py index 2c10c1a630..36171ff3f6 100644 --- a/pythonforandroid/recipes/freetype/__init__.py +++ b/pythonforandroid/recipes/freetype/__init__.py @@ -1,5 +1,6 @@ - -from pythonforandroid.toolchain import Recipe, current_directory, shprint +from pythonforandroid.toolchain import Recipe +from pythonforandroid.util import current_directory +from pythonforandroid.logger import shprint from os.path import exists, join, realpath import sh @@ -7,12 +8,13 @@ class FreetypeRecipe(Recipe): version = '2.5.5' - url = 'http://download.savannah.gnu.org/releases/freetype/freetype-{version}.tar.gz' + url = 'http://download.savannah.gnu.org/releases/freetype/freetype-{version}.tar.gz' # noqa depends = ['harfbuzz'] def should_build(self, arch): - if exists(join(self.get_build_dir(arch.arch), 'objs', '.libs', 'libfreetype.so')): + if exists(join(self.get_build_dir(arch.arch), + 'objs', '.libs', 'libfreetype.a')): return False return True @@ -22,17 +24,21 @@ def build_arch(self, arch): harfbuzz_recipe = Recipe.get_recipe('harfbuzz', self.ctx) env['LDFLAGS'] = ' '.join( [env['LDFLAGS'], - '-L{}'.format(join(harfbuzz_recipe.get_build_dir(arch.arch), 'src', '.libs'))]) + '-L{}'.format(join(harfbuzz_recipe.get_build_dir(arch.arch), + 'src', '.libs'))]) with current_directory(self.get_build_dir(arch.arch)): configure = sh.Command('./configure') - shprint(configure, '--host=arm-linux-androideabi', + shprint(configure, + '--host=arm-linux-androideabi', '--prefix={}'.format(realpath('.')), - '--without-zlib', '--with-png=no', '--enable-shared', + '--without-zlib', + '--with-png=no', + '--disable-shared', _env=env) shprint(sh.make, '-j5', _env=env) - shprint(sh.cp, 'objs/.libs/libfreetype.so', self.ctx.libs_dir) + shprint(sh.cp, 'objs/.libs/libfreetype.a', self.ctx.libs_dir) recipe = FreetypeRecipe() diff --git a/pythonforandroid/recipes/harfbuzz/__init__.py b/pythonforandroid/recipes/harfbuzz/__init__.py index 7b4e35611d..32f4e513b9 100644 --- a/pythonforandroid/recipes/harfbuzz/__init__.py +++ b/pythonforandroid/recipes/harfbuzz/__init__.py @@ -1,15 +1,17 @@ - -from pythonforandroid.toolchain import Recipe, current_directory, shprint +from pythonforandroid.toolchain import Recipe +from pythonforandroid.util import current_directory +from pythonforandroid.logger import shprint from os.path import exists, join import sh class HarfbuzzRecipe(Recipe): version = '0.9.40' - url = 'http://www.freedesktop.org/software/harfbuzz/release/harfbuzz-{version}.tar.bz2' + url = 'http://www.freedesktop.org/software/harfbuzz/release/harfbuzz-{version}.tar.bz2' # noqa def should_build(self, arch): - if exists(join(self.get_build_dir(arch.arch), 'src', '.libs', 'libharfbuzz.so')): + if exists(join(self.get_build_dir(arch.arch), + 'src', '.libs', 'libharfbuzz.a')): return False return True @@ -22,11 +24,16 @@ def build_arch(self, arch): with current_directory(self.get_build_dir(arch.arch)): configure = sh.Command('./configure') shprint(configure, '--without-icu', '--host=arm-linux=androideabi', - '--prefix={}'.format(join(self.ctx.build_dir, 'python-install')), - '--without-freetype', '--without-glib', _env=env) + '--prefix={}'.format( + join(self.ctx.build_dir, 'python-install')), + '--without-freetype', + '--without-glib', + '--disable-shared', + _env=env) shprint(sh.make, '-j5', _env=env) - shprint(sh.cp, '-L', join('src', '.libs', 'libharfbuzz.so'), self.ctx.libs_dir) + shprint(sh.cp, '-L', join('src', '.libs', 'libharfbuzz.a'), + self.ctx.libs_dir) recipe = HarfbuzzRecipe() diff --git a/pythonforandroid/recipes/jpeg/__init__.py b/pythonforandroid/recipes/jpeg/__init__.py index 7801af2102..1969d2c1c5 100644 --- a/pythonforandroid/recipes/jpeg/__init__.py +++ b/pythonforandroid/recipes/jpeg/__init__.py @@ -1,35 +1,78 @@ -from pythonforandroid.recipe import NDKRecipe +from pythonforandroid.recipe import Recipe from pythonforandroid.logger import shprint from pythonforandroid.util import current_directory from os.path import join, exists +from os import environ, uname +from glob import glob import sh -class JpegRecipe(NDKRecipe): +class JpegRecipe(Recipe): + ''' + .. versionchanged:: 0.6.0 + rewrote recipe to be build with clang and updated libraries to latest + version of the official git repo. + ''' name = 'jpeg' - version = 'linaro-android' - url = 'git+https://git.linaro.org/people/tomgall/libjpeg-turbo/libjpeg-turbo' + version = '2.0.1' + url = 'https://github.com/libjpeg-turbo/libjpeg-turbo/archive/{version}.tar.gz' # noqa + # we will require this below patch to build the shared library + # patches = ['remove-version.patch'] - patches = ['build-static.patch'] + def should_build(self, arch): + return not exists(join(self.get_build_dir(arch.arch), + 'libturbojpeg.a')) - generated_libraries = ['libjpeg.a'] + def build_arch(self, arch): + super(JpegRecipe, self).build_arch(arch) + build_dir = self.get_build_dir(arch.arch) - def prebuild_arch(self, arch): - super(JpegRecipe, self).prebuild_arch(arch) + # TODO: Fix simd/neon + with current_directory(build_dir): + env = self.get_recipe_env(arch) + toolchain_file = join(self.ctx.ndk_dir, + 'build/cmake/android.toolchain.cmake') - build_dir = self.get_build_dir(arch.arch) - app_mk = join(build_dir, 'Application.mk') - if not exists(app_mk): - shprint(sh.cp, join(self.get_recipe_dir(), 'Application.mk'), app_mk) - jni_ln = join(build_dir, 'jni') - if not exists(jni_ln): - shprint(sh.ln, '-s', build_dir, jni_ln) + shprint(sh.rm, '-f', 'CMakeCache.txt', 'CMakeFiles/') + shprint(sh.cmake, '-G', 'Unix Makefiles', + '-DCMAKE_SYSTEM_NAME=Android', + '-DCMAKE_SYSTEM_PROCESSOR={cpu}'.format(cpu='arm'), + '-DCMAKE_POSITION_INDEPENDENT_CODE=1', + '-DCMAKE_ANDROID_ARCH_ABI={arch}'.format(arch=arch.arch), + '-DCMAKE_ANDROID_NDK=' + self.ctx.ndk_dir, + '-DCMAKE_C_COMPILER={toolchain}/bin/clang'.format( + toolchain=env['TOOLCHAIN']), + '-DCMAKE_CXX_COMPILER={toolchain}/bin/clang++'.format( + toolchain=env['TOOLCHAIN']), + '-DCMAKE_BUILD_TYPE=Release', + '-DCMAKE_INSTALL_PREFIX=./install', + '-DCMAKE_TOOLCHAIN_FILE=' + toolchain_file, - def build_arch(self, arch): - super(JpegRecipe, self).build_arch(arch) - with current_directory(self.get_lib_dir(arch)): - shprint(sh.mv, 'libjpeg.a', 'libjpeg-orig.a') - shprint(sh.ar, '-rcT', 'libjpeg.a', 'libjpeg-orig.a', 'libsimd.a') + '-DANDROID_ABI={arch}'.format(arch=arch.arch), + '-DANDROID_ARM_NEON=ON', + '-DENABLE_NEON=ON', + # '-DREQUIRE_SIMD=1', + + # Force disable shared, with the static ones is enough + '-DENABLE_SHARED=0', + '-DENABLE_STATIC=1', + _env=env) + shprint(sh.make, _env=env) + + # copy static libs to libs collection + for lib in glob(join(build_dir, '*.a')): + shprint(sh.cp, '-L', lib, self.ctx.libs_dir) + + def get_recipe_env(self, arch=None, with_flags_in_cc=False, clang=True): + env = environ.copy() + + build_platform = '{system}-{machine}'.format( + system=uname()[0], machine=uname()[-1]).lower() + env['TOOLCHAIN'] = join(self.ctx.ndk_dir, 'toolchains/llvm/' + 'prebuilt/{build_platform}'.format( + build_platform=build_platform)) + + return env recipe = JpegRecipe() diff --git a/pythonforandroid/recipes/jpeg/remove-version.patch b/pythonforandroid/recipes/jpeg/remove-version.patch new file mode 100644 index 0000000000..311aa33bc4 --- /dev/null +++ b/pythonforandroid/recipes/jpeg/remove-version.patch @@ -0,0 +1,12 @@ +--- jpeg/CMakeLists.txt.orig 2018-11-12 20:20:28.000000000 +0100 ++++ jpeg/CMakeLists.txt 2018-12-14 12:43:45.338704504 +0100 +@@ -573,6 +573,9 @@ + add_library(turbojpeg SHARED ${TURBOJPEG_SOURCES}) + set_property(TARGET turbojpeg PROPERTY COMPILE_FLAGS + "-DBMP_SUPPORTED -DPPM_SUPPORTED") ++ set_property(TARGET jpeg PROPERTY NO_SONAME 1) ++ set_property(TARGET turbojpeg PROPERTY NO_SONAME 1) ++ set(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "") + if(WIN32) + set_target_properties(turbojpeg PROPERTIES DEFINE_SYMBOL DLLDEFINE) + endif() diff --git a/pythonforandroid/recipes/pil/__init__.py b/pythonforandroid/recipes/pil/__init__.py index 03c58c2c63..1b99dabfb4 100644 --- a/pythonforandroid/recipes/pil/__init__.py +++ b/pythonforandroid/recipes/pil/__init__.py @@ -1,40 +1,78 @@ -from os.path import join - +from os.path import join, exists from pythonforandroid.recipe import CompiledComponentsPythonRecipe +from pythonforandroid.toolchain import shprint +import sh class PILRecipe(CompiledComponentsPythonRecipe): name = 'pil' version = '1.1.7' url = 'http://effbot.org/downloads/Imaging-{version}.tar.gz' - depends = ['python2', 'png', 'jpeg'] + depends = [('python2', 'python2legacy'), 'png', 'jpeg', 'setuptools'] + opt_depends = ['freetype'] site_packages_name = 'PIL' patches = ['disable-tk.patch', 'fix-directories.patch'] - def get_recipe_env(self, arch=None): - env = super(PILRecipe, self).get_recipe_env(arch) + def get_recipe_env(self, arch=None, with_flags_in_cc=True): + env = super(PILRecipe, self).get_recipe_env(arch, with_flags_in_cc) + + env['PYTHON_INCLUDE_ROOT'] = self.ctx.python_recipe.include_root(arch.arch) + env['PYTHON_LINK_ROOT'] = self.ctx.python_recipe.link_root(arch.arch) + + ndk_lib_dir = join(self.ctx.ndk_platform, 'usr', 'lib') + ndk_include_dir = join(self.ctx.ndk_dir, 'sysroot', 'usr', 'include') png = self.get_recipe('png', self.ctx) png_lib_dir = png.get_lib_dir(arch) png_jni_dir = png.get_jni_dir(arch) + jpeg = self.get_recipe('jpeg', self.ctx) - jpeg_lib_dir = jpeg.get_lib_dir(arch) - jpeg_jni_dir = jpeg.get_jni_dir(arch) - env['JPEG_ROOT'] = '{}|{}'.format(jpeg_lib_dir, jpeg_jni_dir) - - cflags = ' -I{} -L{} -I{} -L{}'.format(png_jni_dir, png_lib_dir, jpeg_jni_dir, jpeg_lib_dir) - env['CFLAGS'] += cflags - env['CXXFLAGS'] += cflags - env['CC'] += cflags - env['CXX'] += cflags - - ndk_dir = self.ctx.ndk_platform - ndk_lib_dir = join(ndk_dir, 'usr', 'lib') - ndk_include_dir = join(ndk_dir, 'usr', 'include') + jpeg_inc_dir = jpeg_lib_dir = jpeg.get_build_dir(arch.arch) + + if 'freetype' in self.ctx.recipe_build_order: + freetype = self.get_recipe('freetype', self.ctx) + free_lib_dir = join(freetype.get_build_dir(arch.arch), 'objs', '.libs') + free_inc_dir = join(freetype.get_build_dir(arch.arch), 'include') + # hack freetype to be found by pil + freetype_link = join(free_inc_dir, 'freetype') + if not exists(freetype_link): + shprint(sh.ln, '-s', join(free_inc_dir), freetype_link) + + harfbuzz = self.get_recipe('harfbuzz', self.ctx) + harf_lib_dir = join(harfbuzz.get_build_dir(arch.arch), 'src', '.libs') + harf_inc_dir = harfbuzz.get_build_dir(arch.arch) + + env['FREETYPE_ROOT'] = '{}|{}'.format(free_lib_dir, free_inc_dir) + + env['JPEG_ROOT'] = '{}|{}'.format(jpeg_lib_dir, jpeg_inc_dir) env['ZLIB_ROOT'] = '{}|{}'.format(ndk_lib_dir, ndk_include_dir) + cflags = ' -std=c99' + cflags += ' -I{}'.format(png_jni_dir) + if 'freetype' in self.ctx.recipe_build_order: + cflags += ' -I{} -I{}'.format(harf_inc_dir, join(harf_inc_dir, 'src')) + cflags += ' -I{}'.format(free_inc_dir) + cflags += ' -I{}'.format(jpeg_inc_dir) + cflags += ' -I{}'.format(ndk_include_dir) + + py_v = self.ctx.python_recipe.major_minor_version_string + if py_v[0] == '3': + py_v += 'm' + + env['LIBS'] = ' -lpython{version} -lpng'.format(version=py_v) + if 'freetype' in self.ctx.recipe_build_order: + env['LIBS'] += ' -lfreetype -lharfbuzz' + env['LIBS'] += ' -ljpeg -lturbojpeg' + + env['LDFLAGS'] += ' -L{} -L{}'.format(env['PYTHON_LINK_ROOT'], png_lib_dir) + if 'freetype' in self.ctx.recipe_build_order: + env['LDFLAGS'] += ' -L{} -L{}'.format(harf_lib_dir, free_lib_dir) + env['LDFLAGS'] += ' -L{} -L{}'.format(jpeg_lib_dir, ndk_lib_dir) + + if cflags not in env['CFLAGS']: + env['CFLAGS'] += cflags return env diff --git a/pythonforandroid/recipes/pil/fix-directories.patch b/pythonforandroid/recipes/pil/fix-directories.patch index d54cd0cb52..b9daee3a1a 100644 --- a/pythonforandroid/recipes/pil/fix-directories.patch +++ b/pythonforandroid/recipes/pil/fix-directories.patch @@ -1,7 +1,6 @@ -diff -Naur pil/setup.py b/setup.py ---- pil/setup.py 2015-12-11 16:42:40.817701332 -0600 -+++ b/setup.py 2015-12-11 17:07:34.778477132 -0600 -@@ -34,10 +34,10 @@ +--- pil/setup.py.orig 2009-11-15 17:06:10.000000000 +0100 ++++ pil/setup.py 2019-01-04 11:08:47.302974315 +0100 +@@ -34,10 +34,10 @@ def libinclude(root): # TIFF_ROOT = libinclude("/opt/tiff") TCL_ROOT = None @@ -15,7 +14,7 @@ diff -Naur pil/setup.py b/setup.py LCMS_ROOT = None # FIXME: add mechanism to explicitly *disable* the use of a library -@@ -127,29 +127,6 @@ +@@ -127,33 +127,10 @@ class pil_build_ext(build_ext): add_directory(include_dirs, "libImaging") @@ -42,10 +41,17 @@ diff -Naur pil/setup.py b/setup.py - add_directory(library_dirs, "/usr/local/lib") - # FIXME: check /opt/stuff directories here? - - prefix = sysconfig.get_config_var("prefix") +- prefix = sysconfig.get_config_var("prefix") ++ prefix = os.environ.get('PYTHON_LINK_ROOT') if prefix: - add_directory(library_dirs, os.path.join(prefix, "lib")) -@@ -199,22 +176,6 @@ +- add_directory(library_dirs, os.path.join(prefix, "lib")) +- add_directory(include_dirs, os.path.join(prefix, "include")) ++ add_directory(library_dirs, os.environ.get('PYTHON_LINK_ROOT')) ++ add_directory(include_dirs, os.environ.get('PYTHON_INCLUDE_ROOT')) + + # + # locate tkinter libraries +@@ -199,22 +176,6 @@ class pil_build_ext(build_ext): add_directory(include_dirs, include_root) # @@ -68,7 +74,7 @@ diff -Naur pil/setup.py b/setup.py # insert new dirs *before* default libs, to avoid conflicts # between Python PYD stub libs and real libraries -@@ -299,8 +260,6 @@ +@@ -299,8 +260,6 @@ class pil_build_ext(build_ext): defs.append(("HAVE_LIBZ", None)) if sys.platform == "win32": libs.extend(["kernel32", "user32", "gdi32"]) diff --git a/pythonforandroid/recipes/png/__init__.py b/pythonforandroid/recipes/png/__init__.py index b604dd5469..5b69688825 100644 --- a/pythonforandroid/recipes/png/__init__.py +++ b/pythonforandroid/recipes/png/__init__.py @@ -3,7 +3,14 @@ class PngRecipe(NDKRecipe): name = 'png' - version = '1.6.15' + # This version is the last `sha commit` published in the repo (it's more + # than one year old...) and it's for libpng version `1.6.29`. We set a + # commit for a version because the author of the github's repo never + # released/tagged it, despite He performed the necessary changes in + # master branch. + version = 'b43b4c6' + + # TODO: Try to move the repo to mainline url = 'https://github.com/julienr/libpng-android/archive/{version}.zip' generated_libraries = ['libpng.a']