Skip to content

[WIP][LIBS - PART IV] Rework of shapely and libgeos #1967

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 29, 2019
Merged
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
74 changes: 41 additions & 33 deletions pythonforandroid/recipes/libgeos/__init__.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,52 @@
from pythonforandroid.toolchain import Recipe, shprint, shutil, current_directory
from os.path import exists, join
import sh
from pythonforandroid.util import current_directory, ensure_dir
from pythonforandroid.toolchain import shprint
from pythonforandroid.recipe import Recipe
from multiprocessing import cpu_count
from os.path import join
import sh


class LibgeosRecipe(Recipe):
version = '3.5'
# url = 'http://download.osgeo.org/geos/geos-{version}.tar.bz2'
url = 'https://github.com/libgeos/libgeos/archive/svn-{version}.zip'
version = '3.7.1'
url = 'https://github.com/libgeos/libgeos/archive/{version}.zip'
depends = []

def should_build(self, arch):
super(LibgeosRecipe, self).should_build(arch)
return not exists(join(self.ctx.get_libs_dir(arch.arch), 'libgeos_c.so'))
built_libraries = {
'libgeos.so': 'install_target/lib',
'libgeos_c.so': 'install_target/lib'
}
need_stl_shared = True

def build_arch(self, arch):
super(LibgeosRecipe, self).build_arch(arch)
env = self.get_recipe_env(arch)

with current_directory(self.get_build_dir(arch.arch)):
dst_dir = join(self.get_build_dir(arch.arch), 'dist')
bash = sh.Command('bash')
print("If this fails make sure you have autoconf and libtool installed")
shprint(bash, 'autogen.sh') # Requires autoconf and libtool
shprint(bash, 'configure', '--host=arm-linux-androideabi', '--enable-shared', '--prefix={}'.format(dst_dir), _env=env)
shprint(sh.make, '-j', str(cpu_count()), _env=env)
source_dir = self.get_build_dir(arch.arch)
build_target = join(source_dir, 'build_target')
install_target = join(source_dir, 'install_target')

ensure_dir(build_target)
with current_directory(build_target):
env = self.get_recipe_env(arch)
shprint(sh.cmake, source_dir,
'-DANDROID_ABI={}'.format(arch.arch),
'-DANDROID_NATIVE_API_LEVEL={}'.format(self.ctx.ndk_api),
'-DANDROID_STL=' + self.stl_lib_name,

'-DCMAKE_TOOLCHAIN_FILE={}'.format(
join(self.ctx.ndk_dir, 'build', 'cmake',
'android.toolchain.cmake')),
'-DCMAKE_INSTALL_PREFIX={}'.format(install_target),
'-DCMAKE_BUILD_TYPE=Release',

'-DGEOS_ENABLE_TESTS=OFF',

'-DBUILD_SHARED_LIBS=1',

_env=env)
shprint(sh.make, '-j' + str(cpu_count()), _env=env)

# We make the install because this way we will have all the
# includes in one place (mostly we are interested in `geos_c.h`,
# which is not in the include folder, so this way we make easier to
# link with this library...case of shapely's recipe)
shprint(sh.make, 'install', _env=env)
shutil.copyfile('{}/lib/libgeos_c.so'.format(dst_dir), join(self.ctx.get_libs_dir(arch.arch), 'libgeos_c.so'))

def get_recipe_env(self, arch):
env = super(LibgeosRecipe, self).get_recipe_env(arch)
env['CXXFLAGS'] += ' -I{}/sources/cxx-stl/gnu-libstdc++/4.8/include'.format(self.ctx.ndk_dir)
env['CXXFLAGS'] += ' -I{}/sources/cxx-stl/gnu-libstdc++/4.8/libs/{}/include'.format(
self.ctx.ndk_dir, arch)
env['CXXFLAGS'] += ' -L{}/sources/cxx-stl/gnu-libstdc++/4.8/libs/{}'.format(
self.ctx.ndk_dir, arch)
env['CXXFLAGS'] += ' -lgnustl_shared'
env['LDFLAGS'] += ' -L{}/sources/cxx-stl/gnu-libstdc++/4.8/libs/{}'.format(
self.ctx.ndk_dir, arch)
return env


recipe = LibgeosRecipe()
37 changes: 27 additions & 10 deletions pythonforandroid/recipes/shapely/__init__.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,38 @@
from pythonforandroid.recipe import Recipe, CythonRecipe
from pythonforandroid.recipe import CythonRecipe
from os.path import join


class ShapelyRecipe(CythonRecipe):
version = '1.5'
url = 'https://github.com/Toblerity/Shapely/archive/master.zip'
version = '1.7a1'
url = 'https://github.com/Toblerity/Shapely/archive/{version}.tar.gz'
depends = ['setuptools', 'libgeos']

# Actually, this recipe seems to compile/install fine for python2, but it
# fails at runtime when importing module with:
# `[Errno 2] No such file or directory`
conflicts = ['python2']

call_hostpython_via_targetpython = False

patches = ['setup.patch'] # Patch to force setup to fail when C extention fails to build
# Patch to avoid libgeos check (because it fails), insert environment
# variables for our libgeos build (includes, lib paths...) and force
# the cython's compilation to raise an error in case that it fails
patches = ['setup.patch']

# Don't Force Cython
# setup_extra_args = ['sdist']

def get_recipe_env(self, arch=None, with_flags_in_cc=True):
env = super(ShapelyRecipe, self).get_recipe_env(arch)

# setup_extra_args = ['sdist'] # DontForce Cython
libgeos_install = join(self.get_recipe(
'libgeos', self.ctx).get_build_dir(arch.arch), 'install_target')
# All this `GEOS_X` variables should be string types, separated
# by commas in case that we need to pass more than one value
env['GEOS_INCLUDE_DIRS'] = join(libgeos_install, 'include')
env['GEOS_LIBRARY_DIRS'] = join(libgeos_install, 'lib')
env['GEOS_LIBRARIES'] = 'geos_c,geos'

def get_recipe_env(self, arch, with_flags_in_cc=True):
""" Add libgeos headers to path """
env = super(ShapelyRecipe, self).get_recipe_env(arch, with_flags_in_cc)
libgeos_dir = Recipe.get_recipe('libgeos', self.ctx).get_build_dir(arch.arch)
env['CFLAGS'] += " -I{}/dist/include".format(libgeos_dir)
return env


Expand Down
56 changes: 44 additions & 12 deletions pythonforandroid/recipes/shapely/setup.patch
Original file line number Diff line number Diff line change
@@ -1,12 +1,44 @@
*** shapely/setup.py 2016-06-29 11:29:49.000000000 -0400
--- b/setup.py 2016-07-09 01:51:37.759670990 -0400
***************
*** 359,364 ****
--- 359,365 ----
construct_build_ext(existing_build_ext)
setup(ext_modules=ext_modules, **setup_args)
except BuildFailed as ex:
+ raise # Force python only build to fail
BUILD_EXT_WARNING = "The C extension could not be compiled, " \
"speedups are not enabled."
log.warn(ex)
This patch does three things:
- disable the libgeos check, because, even setting the proper env variables,
it fails to load our libgeos library, so we skip that because it's not
mandatory for the cythonizing.
- sets some environment variables into the setup.py file, so we can pass
our libgeos information (includes, lib path and libraries)
- force to raise an error when cython file to compile (our current build
system relies on this failure to do the proper `cythonizing`, if we don't
raise the error, we will end up with the package installed without the
speed optimizations.
--- Shapely-1.7a1/setup.py.orig 2018-07-29 22:53:13.000000000 +0200
+++ Shapely-1.7a1/setup.py 2019-02-24 14:26:19.178610660 +0100
@@ -82,8 +82,8 @@ if not (py_version == (2, 7) or py_versi

# Get geos_version from GEOS dynamic library, which depends on
# GEOS_LIBRARY_PATH and/or GEOS_CONFIG environment variables
-from shapely._buildcfg import geos_version_string, geos_version, \
- geos_config, get_geos_config
+# from shapely._buildcfg import geos_version_string, geos_version, \
+# geos_config, get_geos_config

logging.basicConfig()
log = logging.getLogger(__file__)
@@ -248,9 +248,9 @@ if sys.platform == 'win32':
setup_args['package_data']['shapely'].append('shapely/DLLs/*.dll')

# Prepare build opts and args for the speedups extension module.
-include_dirs = []
-library_dirs = []
-libraries = []
+include_dirs = os.environ.get('GEOS_INCLUDE_DIRS', '').split(',')
+library_dirs = os.environ.get('GEOS_LIBRARY_DIRS', '').split(',')
+libraries = os.environ.get('GEOS_LIBRARIES', '').split(',')
extra_link_args = []

# If NO_GEOS_CONFIG is set in the environment, geos-config will not
@@ -375,6 +375,7 @@ try:
construct_build_ext(existing_build_ext)
setup(ext_modules=ext_modules, **setup_args)
except BuildFailed as ex:
+ raise # Force python only build to fail
BUILD_EXT_WARNING = "The C extension could not be compiled, " \
"speedups are not enabled."
log.warn(ex)