From 1de954ab0e9654f5b1ea2237f1dd6ff6412b95d3 Mon Sep 17 00:00:00 2001 From: "Juan E. Sanchez" Date: Fri, 8 Sep 2023 00:52:11 -0500 Subject: [PATCH 01/11] matplotlib kiwisolver cpplink --- kivy_ios/recipes/kiwisolver/__init__.py | 39 ++++++ kivy_ios/recipes/matplotlib/__init__.py | 121 +++++++++++++++++ kivy_ios/recipes/matplotlib/_tri.cpp.patch | 15 ++ kivy_ios/recipes/matplotlib/_tri.h.patch | 17 +++ .../recipes/matplotlib/_tri_wrapper.cpp.patch | 12 ++ .../matplotlib/libfreetype.pc.template | 10 ++ .../recipes/matplotlib/setup.cfg.template | 38 ++++++ kivy_ios/recipes/matplotlib/setupext.py.patch | 44 ++++++ kivy_ios/tools/cpplink | 128 ++++++++++++++++++ 9 files changed, 424 insertions(+) create mode 100644 kivy_ios/recipes/kiwisolver/__init__.py create mode 100644 kivy_ios/recipes/matplotlib/__init__.py create mode 100644 kivy_ios/recipes/matplotlib/_tri.cpp.patch create mode 100644 kivy_ios/recipes/matplotlib/_tri.h.patch create mode 100644 kivy_ios/recipes/matplotlib/_tri_wrapper.cpp.patch create mode 100644 kivy_ios/recipes/matplotlib/libfreetype.pc.template create mode 100644 kivy_ios/recipes/matplotlib/setup.cfg.template create mode 100644 kivy_ios/recipes/matplotlib/setupext.py.patch create mode 100755 kivy_ios/tools/cpplink diff --git a/kivy_ios/recipes/kiwisolver/__init__.py b/kivy_ios/recipes/kiwisolver/__init__.py new file mode 100644 index 00000000..5664473b --- /dev/null +++ b/kivy_ios/recipes/kiwisolver/__init__.py @@ -0,0 +1,39 @@ +''' +This file is derived from the p4a recipe for kiwisolver. +It is a dependency of matplotlib. + +It is a C++ library, and it utilizes the cpplink script to handle +creating the library files needed for inclusion in an iOS project. + +It also depends on the headers from the cppy package. +''' + +from kivy_ios.toolchain import CythonRecipe, shprint +from os.path import join +import sh +import shutil + + +class KiwiSolverRecipe(CythonRecipe): + + site_packages_name = 'kiwisolver' + version = '1.3.2' + url = 'https://github.com/nucleic/kiwi/archive/{version}.zip' + depends = ["python"] + hostpython_prerequisites = ["cppy"] + cythonize = False + library = "libkiwisolver.a" + + def get_recipe_env(self, arch=None, with_flags_in_cc=True): + env = super().get_recipe_env(arch) + + #cpplink setup + env['CXX_ORIG'] = env['CXX'] + env['CXX'] = join(self.ctx.root_dir, "tools", "cpplink") + + # setuptools uses CC for compiling and CXX for linking + env['CC'] = env['CXX'] + env['CFLAGS'] += ' -isysroot {}'.format(env['IOSSDKROOT']) + return env + +recipe = KiwiSolverRecipe() diff --git a/kivy_ios/recipes/matplotlib/__init__.py b/kivy_ios/recipes/matplotlib/__init__.py new file mode 100644 index 00000000..004d4775 --- /dev/null +++ b/kivy_ios/recipes/matplotlib/__init__.py @@ -0,0 +1,121 @@ +''' +This file is derived from the p4a recipe for matplotlib. +It is a dependency of matplotlib. + +It is a C++ library, and it utilizes the cpplink script to handle +creating the library files needed for inclusion in an iOS project. + +In addition to the original patch files for p4a, additional patch files +are necessary to prevent duplicate symbols from appearing in the final +link of a kivy-ios application. +''' + +from kivy_ios.toolchain import CythonRecipe, shprint, ensure_dir +from os.path import join +import sh +import shutil + +from os.path import join +from os.path import abspath +import shutil + + +class MatplotlibRecipe(CythonRecipe): + version = '3.5.2' + url = 'https://github.com/matplotlib/matplotlib/archive/v{version}.zip' + library = 'libmatplotlib.a' + depends = ['kiwisolver', 'numpy', 'pillow', 'freetype'] + pre_build_ext = True + python_depends = ['cycler', 'fonttools', 'packaging', + 'pyparsing', 'python-dateutil'] + cythonize = False + + def generate_libraries_pc_files(self, arch): + """ + Create *.pc files for libraries that `matplotib` depends on. + + Because, for unix platforms, the mpl install script uses `pkg-config` + to detect libraries installed in non standard locations (our case... + well...we don't even install the libraries...so we must trick a little + the mlp install). + """ + pkg_config_path = self.get_recipe_env(arch)['PKG_CONFIG_PATH'] + ensure_dir(pkg_config_path) + + lib_to_pc_file = { + # `pkg-config` search for version freetype2.pc, our current + # version for freetype, but we have our recipe named without + # the version...so we add it in here for our pc file + 'freetype': 'freetype2.pc', + } + + for lib_name in {'freetype'}: + pc_template_file = join( + abspath(self.recipe_dir), + f'lib{lib_name}.pc.template' + ) + # read template file into buffer + with open(pc_template_file) as template_file: + text_buffer = template_file.read() + # set the library absolute path and library version + lib_recipe = self.get_recipe(lib_name, self.ctx) + text_buffer = text_buffer.replace( + 'path_to_built', lib_recipe.get_build_dir(arch.arch), + ) + text_buffer = text_buffer.replace( + 'library_version', lib_recipe.version, + ) + + # write the library pc file into our defined dir `PKG_CONFIG_PATH` + pc_dest_file = join(pkg_config_path, lib_to_pc_file[lib_name]) + with open(pc_dest_file, 'w') as pc_file: + pc_file.write(text_buffer) + + def prebuild_arch(self, arch): + if self.has_marker("patched"): + return + shutil.copyfile( + join(abspath(self.recipe_dir), "setup.cfg.template"), + join(self.get_build_dir(arch.arch), "mplsetup.cfg"), + ) + self.generate_libraries_pc_files(arch) + self.apply_patch('_tri.cpp.patch') + self.apply_patch('_tri.h.patch') + self.apply_patch('_tri_wrapper.cpp.patch') + self.apply_patch('setupext.py.patch') + self.set_marker("patched") + + def get_recipe_env(self, arch=None, with_flags_in_cc=True): + env = super().get_recipe_env(arch) + + # we make use of the same directory than `XDG_CACHE_HOME`, for our + # custom library pc files, so we have all the install files that we + # generate at the same place + env['XDG_CACHE_HOME'] = join( + self.get_build_dir(arch.arch), + 'p4a_files' + ) + env['PKG_CONFIG_PATH'] = env['XDG_CACHE_HOME'] + + # creating proper *.pc files for our libraries does not seem enough to + # success with our build (without depending on system development + # libraries), but if we tell the compiler where to find our libraries + # and includes, then the install success :) + freetype = self.get_recipe('freetype', self.ctx) + free_inc_dir = join(freetype.get_build_dir(arch.arch), 'include') + + numpytype = self.get_recipe('numpy', self.ctx) + numpy_inc_dir = join(numpytype.get_build_dir(arch.arch), + 'build', 'src.macosx-13.5-arm64-3.10', + 'numpy', 'core', 'include', 'numpy') + + env['CFLAGS'] += f' -I{free_inc_dir} -I{numpy_inc_dir}' + env['CXX_ORIG'] = env['CXX'] + env['CXX'] = join(self.ctx.root_dir, "tools", "cpplink") + + # setuptools uses CC for compiling and CXX for linking + env['CFLAGS'] += ' -isysroot {}'.format(env['IOSSDKROOT']) + + return env + +recipe = MatplotlibRecipe() diff --git a/kivy_ios/recipes/matplotlib/_tri.cpp.patch b/kivy_ios/recipes/matplotlib/_tri.cpp.patch new file mode 100644 index 00000000..1aa7db01 --- /dev/null +++ b/kivy_ios/recipes/matplotlib/_tri.cpp.patch @@ -0,0 +1,15 @@ +--- matplotlib-3.5.2.orig/src/tri/_tri.cpp 2022-05-02 22:49:57 ++++ matplotlib-3.5.2/src/tri/_tri.cpp 2023-09-08 13:17:37 +@@ -13,6 +13,7 @@ + #include + #include + ++namespace tri { + + TriEdge::TriEdge() + : tri(-1), edge(-1) +@@ -2069,3 +2070,4 @@ + _seed = (_seed*_a + _c) % _m; + return (_seed*max_value) / _m; + } ++} diff --git a/kivy_ios/recipes/matplotlib/_tri.h.patch b/kivy_ios/recipes/matplotlib/_tri.h.patch new file mode 100644 index 00000000..1191a2c0 --- /dev/null +++ b/kivy_ios/recipes/matplotlib/_tri.h.patch @@ -0,0 +1,17 @@ +--- matplotlib-3.5.2.orig/src/tri/_tri.h 2022-05-02 22:49:57 ++++ matplotlib-3.5.2/src/tri/_tri.h 2023-09-08 13:17:12 +@@ -72,6 +72,7 @@ + #include + + ++namespace tri { + + /* An edge of a triangle consisting of an triangle index in the range 0 to + * ntri-1 and an edge index in the range 0 to 2. Edge i goes from the +@@ -814,5 +815,6 @@ + const unsigned long _m, _a, _c; + unsigned long _seed; + }; ++} + + #endif diff --git a/kivy_ios/recipes/matplotlib/_tri_wrapper.cpp.patch b/kivy_ios/recipes/matplotlib/_tri_wrapper.cpp.patch new file mode 100644 index 00000000..4cd87f61 --- /dev/null +++ b/kivy_ios/recipes/matplotlib/_tri_wrapper.cpp.patch @@ -0,0 +1,12 @@ +--- matplotlib-3.5.2.orig/src/tri/_tri_wrapper.cpp 2022-05-02 22:49:57 ++++ matplotlib-3.5.2/src/tri/_tri_wrapper.cpp 2023-09-08 13:23:47 +@@ -1,7 +1,9 @@ + #include "_tri.h" + #include "../mplutils.h" + #include "../py_exceptions.h" ++ + ++using namespace tri; + + /* Triangulation */ + diff --git a/kivy_ios/recipes/matplotlib/libfreetype.pc.template b/kivy_ios/recipes/matplotlib/libfreetype.pc.template new file mode 100644 index 00000000..df5ef288 --- /dev/null +++ b/kivy_ios/recipes/matplotlib/libfreetype.pc.template @@ -0,0 +1,10 @@ +prefix=path_to_built +exec_prefix=${prefix} +includedir=${prefix}/include +libdir=${exec_prefix}/objs/.libs + +Name: freetype2 +Description: The freetype2 library +Version: library_version +Cflags: -I${includedir} +Libs: -L${libdir} -lfreetype diff --git a/kivy_ios/recipes/matplotlib/setup.cfg.template b/kivy_ios/recipes/matplotlib/setup.cfg.template new file mode 100644 index 00000000..96ef80d4 --- /dev/null +++ b/kivy_ios/recipes/matplotlib/setup.cfg.template @@ -0,0 +1,38 @@ +# Rename this file to mplsetup.cfg to modify Matplotlib's build options. + +[libs] +# By default, Matplotlib builds with LTO, which may be slow if you re-compile +# often, and don't need the space saving/speedup. +enable_lto = False +# By default, Matplotlib downloads and builds its own copies of FreeType and of +# Qhull. You may set the following to True to instead link against a system +# FreeType/Qhull. As an exception, Matplotlib defaults to the system version +# of FreeType on AIX. +system_freetype = True +#system_qhull = False + +[packages] +# There are a number of data subpackages from Matplotlib that are +# considered optional. All except 'tests' data (meaning the baseline +# image files) are installed by default, but that can be changed here. +#tests = False + +[gui_support] +# Matplotlib supports multiple GUI toolkits, known as backends. +# The MacOSX backend requires the Cocoa headers included with XCode. +# You can select whether to build it by uncommenting the following line. +# It is never built on Linux or Windows, regardless of the config value. +# +macosx = False + +[rc_options] +# User-configurable options +# +# Default backend, one of: Agg, Cairo, GTK3Agg, GTK3Cairo, GTK4Agg, GTK4Cairo, +# MacOSX, Pdf, Ps, QtAgg, QtCairo, SVG, TkAgg, WX, WXAgg. +# +# The Agg, Ps, Pdf and SVG backends do not require external dependencies. Do +# not choose MacOSX if you have disabled the relevant extension modules. The +# default is determined by fallback. +# +#backend = Agg \ No newline at end of file diff --git a/kivy_ios/recipes/matplotlib/setupext.py.patch b/kivy_ios/recipes/matplotlib/setupext.py.patch new file mode 100644 index 00000000..fb338b89 --- /dev/null +++ b/kivy_ios/recipes/matplotlib/setupext.py.patch @@ -0,0 +1,44 @@ +--- matplotlib-3.5.2.orig/setupext.py 2023-09-08 14:01:18 ++++ matplotlib-3.5.2/setupext.py 2023-09-08 13:09:10 +@@ -404,7 +404,7 @@ + "matplotlib._contour", [ + "src/_contour.cpp", + "src/_contour_wrapper.cpp", +- "src/py_converters.cpp", ++# "src/py_converters.cpp", + ]) + add_numpy_flags(ext) + add_libagg_flags(ext) +@@ -414,7 +414,7 @@ + "matplotlib.ft2font", [ + "src/ft2font.cpp", + "src/ft2font_wrapper.cpp", +- "src/py_converters.cpp", ++# "src/py_converters.cpp", + ]) + FreeType.add_flags(ext) + add_numpy_flags(ext) +@@ -424,19 +424,19 @@ + ext = Extension( + "matplotlib._image", [ + "src/_image_wrapper.cpp", +- "src/py_converters.cpp", ++# "src/py_converters.cpp", + ]) + add_numpy_flags(ext) +- add_libagg_flags_and_sources(ext) ++ add_libagg_flags(ext) + yield ext + # path + ext = Extension( + "matplotlib._path", [ +- "src/py_converters.cpp", ++# "src/py_converters.cpp", + "src/_path_wrapper.cpp", + ]) + add_numpy_flags(ext) +- add_libagg_flags_and_sources(ext) ++ add_libagg_flags(ext) + yield ext + # qhull + ext = Extension( diff --git a/kivy_ios/tools/cpplink b/kivy_ios/tools/cpplink new file mode 100755 index 00000000..1924f265 --- /dev/null +++ b/kivy_ios/tools/cpplink @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 +''' +C++ libraries are linked using environ['CXX'] compiler. This is different +from C libraries, which are linked using environ['LDSHARED']. + +This script behaves like a C++ compiler or a linker, depending on +whether its output file ends with '.so'. + +If for compiling, this script forwards all arguments in a call to the +compiler specified as environ['CXX_ORIG']. + +If for linking, this script collects the same object and dependent +library data. It then generates the same files as liblink would for an +ld target. +''' + +import sys +import subprocess +from os import environ + + +output = None +get_next = False + +for arg in sys.argv: + if get_next: + output = arg + break + elif arg.startswith('-o'): + if arg == '-o': + get_next = True + else: + so_output = arg[2:] + break + +if not output.endswith('.so'): + result = subprocess.run([environ['CXX_ORIG'], *sys.argv[1:]]) + sys.exit(result.returncode) + + +libs = [] +objects = [] + +i = 1 +while i < len(sys.argv): + opt = sys.argv[i] + i += 1 + + if opt == "-o": + output = sys.argv[i] + i += 1 + continue + + if opt.startswith("-l") or opt.startswith("-L"): + libs.append(opt) + continue + + if opt in ("-r", "-pipe", "-no-cpp-precomp"): + continue + + if opt in ( + "--sysroot", "-isysroot", "-framework", "-undefined", + "-macosx_version_min" + ): + i += 1 + continue + + if opt.startswith("-I"): + continue + + if opt.startswith("-m"): + continue + + if opt.startswith("-f"): + continue + + if opt.startswith("-O"): + continue + + if opt.startswith("-g"): + continue + + if opt.startswith("-D"): + continue + + if opt.startswith('-arch'): + continue + + if opt.startswith("-Wl,"): + continue + + if opt.startswith("-W"): + continue + + if opt.startswith("-stdlib="): + continue + + if opt.startswith("-"): + print(sys.argv) + print("Unknown option: ", opt) + sys.exit(1) + + if not opt.endswith('.o'): + continue + + objects.append(opt) + + +f = open(output, "w") +f.close() + +f = open(output + ".libs", "w") +f.write(" ".join(libs)) +f.close() + +print('Liblink redirect linking with', objects) +ld = environ.get('ARM_LD') +arch = environ.get('ARCH', 'arm64') +if 'arm' in arch: + min_version_flag = '-ios_version_min' +else: + min_version_flag = '-ios_simulator_version_min' +call = [ld, '-r', '-o', output + '.o', min_version_flag, '9.0', '-arch', arch] +if min_version_flag == "-ios_version_min": + call += ["-bitcode_bundle"] +call += objects +print("Linking: {}".format(" ".join(call))) +subprocess.call(call) From aba35b72853a4c88e918865a4732288356c70027 Mon Sep 17 00:00:00 2001 From: "Juan E. Sanchez" Date: Sat, 9 Sep 2023 07:39:45 -0500 Subject: [PATCH 02/11] fix flake8 issues --- kivy_ios/recipes/kiwisolver/__init__.py | 7 +++---- kivy_ios/recipes/matplotlib/__init__.py | 7 ++----- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/kivy_ios/recipes/kiwisolver/__init__.py b/kivy_ios/recipes/kiwisolver/__init__.py index 5664473b..d71f7d3a 100644 --- a/kivy_ios/recipes/kiwisolver/__init__.py +++ b/kivy_ios/recipes/kiwisolver/__init__.py @@ -8,10 +8,8 @@ It also depends on the headers from the cppy package. ''' -from kivy_ios.toolchain import CythonRecipe, shprint +from kivy_ios.toolchain import CythonRecipe from os.path import join -import sh -import shutil class KiwiSolverRecipe(CythonRecipe): @@ -27,7 +25,7 @@ class KiwiSolverRecipe(CythonRecipe): def get_recipe_env(self, arch=None, with_flags_in_cc=True): env = super().get_recipe_env(arch) - #cpplink setup + # cpplink setup env['CXX_ORIG'] = env['CXX'] env['CXX'] = join(self.ctx.root_dir, "tools", "cpplink") @@ -36,4 +34,5 @@ def get_recipe_env(self, arch=None, with_flags_in_cc=True): env['CFLAGS'] += ' -isysroot {}'.format(env['IOSSDKROOT']) return env + recipe = KiwiSolverRecipe() diff --git a/kivy_ios/recipes/matplotlib/__init__.py b/kivy_ios/recipes/matplotlib/__init__.py index 004d4775..993e4133 100644 --- a/kivy_ios/recipes/matplotlib/__init__.py +++ b/kivy_ios/recipes/matplotlib/__init__.py @@ -10,11 +10,7 @@ link of a kivy-ios application. ''' -from kivy_ios.toolchain import CythonRecipe, shprint, ensure_dir -from os.path import join -import sh -import shutil - +from kivy_ios.toolchain import CythonRecipe, ensure_dir from os.path import join from os.path import abspath import shutil @@ -118,4 +114,5 @@ def get_recipe_env(self, arch=None, with_flags_in_cc=True): return env + recipe = MatplotlibRecipe() From e2774436d480256c4374d7157d77792f205fb267 Mon Sep 17 00:00:00 2001 From: "Juan E. Sanchez" Date: Sat, 9 Sep 2023 07:47:32 -0500 Subject: [PATCH 03/11] fix startswith issue --- kivy_ios/tools/cpplink | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/kivy_ios/tools/cpplink b/kivy_ios/tools/cpplink index 1924f265..413584f4 100755 --- a/kivy_ios/tools/cpplink +++ b/kivy_ios/tools/cpplink @@ -65,34 +65,7 @@ while i < len(sys.argv): i += 1 continue - if opt.startswith("-I"): - continue - - if opt.startswith("-m"): - continue - - if opt.startswith("-f"): - continue - - if opt.startswith("-O"): - continue - - if opt.startswith("-g"): - continue - - if opt.startswith("-D"): - continue - - if opt.startswith('-arch'): - continue - - if opt.startswith("-Wl,"): - continue - - if opt.startswith("-W"): - continue - - if opt.startswith("-stdlib="): + if opt.startswith(("-I", "-m", "-f", "-O", "-g", "-D", "-arch", "-Wl", "-W", "-stdlib=")): continue if opt.startswith("-"): From 4f075a43084057d037343197d24e4e5ef09db07e Mon Sep 17 00:00:00 2001 From: "Juan E. Sanchez" Date: Sat, 9 Sep 2023 16:08:38 -0500 Subject: [PATCH 04/11] fix manifest for cpplink and refactor code --- MANIFEST.in | 4 +- kivy_ios/tools/cpplink | 204 ++++++++++++++++++++++++++--------------- 2 files changed, 130 insertions(+), 78 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 0bcb1877..0641be56 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,6 @@ recursive-include kivy_ios *.py recursive-include kivy_ios/recipes *.py *.patch *.diff *.rst ModulesSetup ModulesSetup.mobile *.m *.h *.pyx *.so -recursive-include kivy_ios/tools *.py biglink liblink +recursive-include kivy_ios/tools *.py biglink liblink cpplink recursive-include kivy_ios/tools/templates * -prune .git \ No newline at end of file +prune .git diff --git a/kivy_ios/tools/cpplink b/kivy_ios/tools/cpplink index 413584f4..3278f9f3 100755 --- a/kivy_ios/tools/cpplink +++ b/kivy_ios/tools/cpplink @@ -11,91 +11,143 @@ compiler specified as environ['CXX_ORIG']. If for linking, this script collects the same object and dependent library data. It then generates the same files as liblink would for an -ld target. +ld target. The linker called is specified in environ['ARM_LD']. ''' import sys import subprocess -from os import environ +from os import environ, remove -output = None -get_next = False +# this section is to quickly bypass the full script for cases of c++ compiling -for arg in sys.argv: - if get_next: - output = arg - break - elif arg.startswith('-o'): - if arg == '-o': - get_next = True - else: - so_output = arg[2:] - break - -if not output.endswith('.so'): - result = subprocess.run([environ['CXX_ORIG'], *sys.argv[1:]]) - sys.exit(result.returncode) - - -libs = [] -objects = [] - -i = 1 -while i < len(sys.argv): - opt = sys.argv[i] - i += 1 - - if opt == "-o": - output = sys.argv[i] - i += 1 - continue - - if opt.startswith("-l") or opt.startswith("-L"): - libs.append(opt) - continue +def get_output(args): + ''' + gets output file + ''' + output = None + get_next = False - if opt in ("-r", "-pipe", "-no-cpp-precomp"): - continue - - if opt in ( - "--sysroot", "-isysroot", "-framework", "-undefined", - "-macosx_version_min" - ): + for arg in args: + if get_next: + output = arg + break + elif arg.startswith('-o'): + if arg == '-o': + get_next = True + else: + output = arg[2:] + break + + return output + + +def call_cpp(args): + ''' + call the c++ compiler and return error code + throws a RuntimeError if there is an exception in processing + ''' + result = subprocess.run([environ['CXX_ORIG'], *args]) + if result.returncode != 0: + raise RuntimeError("Compiling C++ failed") + + +def parse_linker_args(args): + ''' + parse arguments to the linker + ''' + libs = [] + objects = [] + i = 0 + while i < len(args): + opt = args[i] i += 1 - continue - - if opt.startswith(("-I", "-m", "-f", "-O", "-g", "-D", "-arch", "-Wl", "-W", "-stdlib=")): - continue - - if opt.startswith("-"): - print(sys.argv) - print("Unknown option: ", opt) - sys.exit(1) - - if not opt.endswith('.o'): - continue - objects.append(opt) + if opt == "-o": + i += 1 + continue + elif opt.startswith("-l") or opt.startswith("-L"): + libs.append(opt) + continue + elif opt in ("-r", "-pipe", "-no-cpp-precomp"): + continue + elif opt in ( + "--sysroot", "-isysroot", "-framework", "-undefined", + "-macosx_version_min" + ): + i += 1 + continue + elif opt.startswith(("-I", "-m", "-f", "-O", "-g", "-D", "-arch", "-Wl", "-W", "-stdlib=")): + continue + elif opt.startswith("-"): + raise RuntimeError(str(args) + "\nUnknown option: " + opt) + elif not opt.endswith('.o'): + continue + + objects.append(opt) + + if not objects: + raise RuntimeError('C++ Linker arguments contain no object files') + + return libs, objects + + +def call_linker(objects, output): + ''' + calls linker (environ['ARM_LD']) and returns error code + throws a RuntimeError if there is an exception in processing + ''' + print('Liblink redirect linking with', objects) + ld = environ.get('ARM_LD') + arch = environ.get('ARCH', 'arm64') + if 'arm' in arch: + min_version_flag = '-ios_version_min' + else: + min_version_flag = '-ios_simulator_version_min' + call = [ld, '-r', '-o', output + '.o', min_version_flag, '9.0', '-arch', arch] + if min_version_flag == "-ios_version_min": + call += ["-bitcode_bundle"] + call += objects + print("Linking: {}".format(" ".join(call))) + result = subprocess.run(call) + + if result.returncode != 0: + raise RuntimeError("C++ Linking failed") + + +def delete_so_files(output): + ''' + delete shared object files needed for proper module loading + ''' + try: + remove(output) + remove(output + '.libs') + except FileNotFoundError: + pass + + +def write_so_files(output, libs): + ''' + Writes empty .so and .so.libs file which is needed for proper module loading + ''' + with open(output, "w") as f: + f.write('') + + with open(output + ".libs", "w") as f: + f.write(" ".join(libs)) + + +# command line arguments to the C++ Compiler/Linker +args = sys.argv[1:] +# get the output files +output = get_output(args) - -f = open(output, "w") -f.close() - -f = open(output + ".libs", "w") -f.write(" ".join(libs)) -f.close() - -print('Liblink redirect linking with', objects) -ld = environ.get('ARM_LD') -arch = environ.get('ARCH', 'arm64') -if 'arm' in arch: - min_version_flag = '-ios_version_min' +if not output.endswith('.so'): + # C++ Compiling + subprocess.run([environ['CXX_ORIG'], *args]) else: - min_version_flag = '-ios_simulator_version_min' -call = [ld, '-r', '-o', output + '.o', min_version_flag, '9.0', '-arch', arch] -if min_version_flag == "-ios_version_min": - call += ["-bitcode_bundle"] -call += objects -print("Linking: {}".format(" ".join(call))) -subprocess.call(call) + # C++ Linking + libs, objects = parse_linker_args(args) + delete_so_files(output) + call_linker(objects, output) + write_so_files(output, libs) From 4cd413a40145f2e6b2008ecc748d75ddc2a015db Mon Sep 17 00:00:00 2001 From: "Juan E. Sanchez" Date: Sat, 9 Sep 2023 16:17:01 -0500 Subject: [PATCH 05/11] minor tweak in removing stale so files --- kivy_ios/tools/cpplink | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kivy_ios/tools/cpplink b/kivy_ios/tools/cpplink index 3278f9f3..a3cd88a9 100755 --- a/kivy_ios/tools/cpplink +++ b/kivy_ios/tools/cpplink @@ -121,6 +121,10 @@ def delete_so_files(output): ''' try: remove(output) + except FileNotFoundError: + pass + + try: remove(output + '.libs') except FileNotFoundError: pass From c758d06b862b23e1591b859be2fdcc911740bd3c Mon Sep 17 00:00:00 2001 From: "Juan E. Sanchez" Date: Sat, 9 Sep 2023 16:24:06 -0500 Subject: [PATCH 06/11] fix message --- kivy_ios/tools/cpplink | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kivy_ios/tools/cpplink b/kivy_ios/tools/cpplink index a3cd88a9..dac8a671 100755 --- a/kivy_ios/tools/cpplink +++ b/kivy_ios/tools/cpplink @@ -97,7 +97,7 @@ def call_linker(objects, output): calls linker (environ['ARM_LD']) and returns error code throws a RuntimeError if there is an exception in processing ''' - print('Liblink redirect linking with', objects) + print('cpplink redirect linking with', objects) ld = environ.get('ARM_LD') arch = environ.get('ARCH', 'arm64') if 'arm' in arch: From 862a0ca3866fcfd8f4fbc80516166d817174230e Mon Sep 17 00:00:00 2001 From: "Juan E. Sanchez" Date: Sat, 9 Sep 2023 17:11:42 -0500 Subject: [PATCH 07/11] need to include templates in the manifest --- MANIFEST.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 0641be56..92611cf7 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,5 @@ recursive-include kivy_ios *.py -recursive-include kivy_ios/recipes *.py *.patch *.diff *.rst ModulesSetup ModulesSetup.mobile *.m *.h *.pyx *.so +recursive-include kivy_ios/recipes *.py *.patch *.diff *.rst ModulesSetup ModulesSetup.mobile *.m *.h *.pyx *.so *.template recursive-include kivy_ios/tools *.py biglink liblink cpplink recursive-include kivy_ios/tools/templates * From cb4d7f33c3013528f762bafca9d40a7e5021ab3d Mon Sep 17 00:00:00 2001 From: "Juan E. Sanchez" Date: Sat, 9 Sep 2023 22:25:49 -0500 Subject: [PATCH 08/11] remove numpy direct dependency --- kivy_ios/recipes/matplotlib/setupext.py.patch | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/kivy_ios/recipes/matplotlib/setupext.py.patch b/kivy_ios/recipes/matplotlib/setupext.py.patch index fb338b89..83d8cdd5 100644 --- a/kivy_ios/recipes/matplotlib/setupext.py.patch +++ b/kivy_ios/recipes/matplotlib/setupext.py.patch @@ -1,5 +1,5 @@ --- matplotlib-3.5.2.orig/setupext.py 2023-09-08 14:01:18 -+++ matplotlib-3.5.2/setupext.py 2023-09-08 13:09:10 ++++ matplotlib-3.5.2/setupext.py 2023-09-09 22:23:24 @@ -404,7 +404,7 @@ "matplotlib._contour", [ "src/_contour.cpp", @@ -42,3 +42,14 @@ yield ext # qhull ext = Extension( +@@ -499,8 +499,8 @@ + + + def add_numpy_flags(ext): +- import numpy as np +- ext.include_dirs.append(np.get_include()) ++# import numpy as np ++# ext.include_dirs.append(np.get_include()) + ext.define_macros.extend([ + # Ensure that PY_ARRAY_UNIQUE_SYMBOL is uniquely defined for each + # extension. From 186e899c5921f24d5a2fe6d9e1a5ea45768e10c9 Mon Sep 17 00:00:00 2001 From: "Juan E. Sanchez" Date: Sat, 9 Sep 2023 22:32:48 -0500 Subject: [PATCH 09/11] add setup.py patch --- kivy_ios/recipes/matplotlib/setup.py.patch | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 kivy_ios/recipes/matplotlib/setup.py.patch diff --git a/kivy_ios/recipes/matplotlib/setup.py.patch b/kivy_ios/recipes/matplotlib/setup.py.patch new file mode 100644 index 00000000..84c197f7 --- /dev/null +++ b/kivy_ios/recipes/matplotlib/setup.py.patch @@ -0,0 +1,20 @@ +--- matplotlib-3.5.2.orig/setup.py 2022-05-02 22:49:57 ++++ matplotlib-3.5.2/setup.py 2023-09-09 22:22:57 +@@ -315,7 +315,7 @@ + python_requires='>={}'.format('.'.join(str(n) for n in py_min_version)), + setup_requires=[ + "certifi>=2020.06.20", +- "numpy>=1.17", ++# "numpy>=1.17", + "setuptools_scm>=4", + "setuptools_scm_git_archive", + ], +@@ -323,7 +323,7 @@ + "cycler>=0.10", + "fonttools>=4.22.0", + "kiwisolver>=1.0.1", +- "numpy>=1.17", ++# "numpy>=1.17", + "packaging>=20.0", + "pillow>=6.2.0", + "pyparsing>=2.2.1", From c48b813af22b418f3df455b36a5d0cfb299e5a30 Mon Sep 17 00:00:00 2001 From: "Juan E. Sanchez" Date: Sat, 9 Sep 2023 22:45:04 -0500 Subject: [PATCH 10/11] add patch file --- kivy_ios/recipes/matplotlib/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kivy_ios/recipes/matplotlib/__init__.py b/kivy_ios/recipes/matplotlib/__init__.py index 993e4133..b5280b8b 100644 --- a/kivy_ios/recipes/matplotlib/__init__.py +++ b/kivy_ios/recipes/matplotlib/__init__.py @@ -79,6 +79,7 @@ def prebuild_arch(self, arch): self.apply_patch('_tri.h.patch') self.apply_patch('_tri_wrapper.cpp.patch') self.apply_patch('setupext.py.patch') + self.apply_patch('setup.py.patch') self.set_marker("patched") def get_recipe_env(self, arch=None, with_flags_in_cc=True): From 470eff9e62dcf300b4dd4d398dfce852896b3813 Mon Sep 17 00:00:00 2001 From: "Juan E. Sanchez" Date: Sat, 9 Sep 2023 23:11:30 -0500 Subject: [PATCH 11/11] find special numpy include using a recursive glob --- kivy_ios/recipes/matplotlib/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/kivy_ios/recipes/matplotlib/__init__.py b/kivy_ios/recipes/matplotlib/__init__.py index b5280b8b..628055dd 100644 --- a/kivy_ios/recipes/matplotlib/__init__.py +++ b/kivy_ios/recipes/matplotlib/__init__.py @@ -11,9 +11,9 @@ ''' from kivy_ios.toolchain import CythonRecipe, ensure_dir -from os.path import join -from os.path import abspath +from os.path import join, abspath, dirname import shutil +import sh class MatplotlibRecipe(CythonRecipe): @@ -106,6 +106,9 @@ def get_recipe_env(self, arch=None, with_flags_in_cc=True): 'build', 'src.macosx-13.5-arm64-3.10', 'numpy', 'core', 'include', 'numpy') + # this numpy include directory is not in the dist directory + numpy_inc_dir = dirname(sh.glob(numpytype.get_build_dir(arch.arch) + '/**/_numpyconfig.h', recursive=True)[0]) + env['CFLAGS'] += f' -I{free_inc_dir} -I{numpy_inc_dir}' env['CXX_ORIG'] = env['CXX'] env['CXX'] = join(self.ctx.root_dir, "tools", "cpplink")