From 987314376bed055853f8f5030787d26144027493 Mon Sep 17 00:00:00 2001 From: RJ Burnham Date: Mon, 11 Jul 2016 15:00:03 -0400 Subject: [PATCH 01/12] Update python loader to load from frameworks as well as main exe (i.e. we have kivy embedded in a framework) --- recipes/python/dynload.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recipes/python/dynload.patch b/recipes/python/dynload.patch index 7cb73f6..3c80a50 100644 --- a/recipes/python/dynload.patch +++ b/recipes/python/dynload.patch @@ -10,7 +10,7 @@ + * of trying to dlopen, directly do the dlsym. + * -- Mathieu + */ -+ return (dl_funcptr) dlsym(RTLD_MAIN_ONLY, funcname); ++ return (dl_funcptr) dlsym(RTLD_SELF, funcname); + +#if 0 if (fp != NULL) { From 48761f778d7daafe8183b6f78c3191a4d8a77429 Mon Sep 17 00:00:00 2001 From: Filipe Correia Date: Tue, 6 Sep 2016 00:04:08 +0100 Subject: [PATCH 02/12] Recipe for libpng. --- recipes/libpng/__init__.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 recipes/libpng/__init__.py diff --git a/recipes/libpng/__init__.py b/recipes/libpng/__init__.py new file mode 100644 index 0000000..7d07c83 --- /dev/null +++ b/recipes/libpng/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +from toolchain import Recipe, shprint +from os.path import join +import sh + +class PngRecipe(Recipe): + version = '1.6.24' + url = 'http://vorboss.dl.sourceforge.net/project/libpng/libpng16/1.6.24/libpng-1.6.24.tar.gz' + depends = ["python"] + library = '.libs/libpng16.a' + + def build_arch(self, arch): + build_env = arch.get_env() + configure = sh.Command(join(self.build_dir, "configure")) + shprint(configure, + "CC={}".format(build_env["CC"]), + "LD={}".format(build_env["LD"]), + "CFLAGS={}".format(build_env["CFLAGS"]), + "LDFLAGS={}".format(build_env["LDFLAGS"]), + "--prefix=/", + "--host={}".format(arch.triple), + "--disable-shared") + shprint(sh.make, "clean") + shprint(sh.make, _env=build_env) + +recipe = PngRecipe() \ No newline at end of file From 654900d4018b79d8c32acb49f5c6ec5b3373a792 Mon Sep 17 00:00:00 2001 From: bearnun Date: Tue, 4 Oct 2016 18:39:56 -0500 Subject: [PATCH 03/12] Update cython to 0.23 from 0.21 Because of "Kivy requires at least Cython version 0.23" from: https://kivy.org/docs/installation/installation.html --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index cea324e..603f64d 100644 --- a/README.rst +++ b/README.rst @@ -32,10 +32,10 @@ Currently, the toolchain requires a few tools for compilation. You will need: brew install autoconf automake libtool pkg-config brew link libtool -#. Install Cython (0.21):: +#. Install Cython (0.23):: # pip method if available (sudo might be needed.) - pip install cython==0.21 + pip install cython==0.23 Using the toolchain From 73adae58c3d8c4214f0edc0240b75accf5482716 Mon Sep 17 00:00:00 2001 From: Robert Niederreiter Date: Fri, 14 Oct 2016 10:31:18 +0200 Subject: [PATCH 04/12] fix host_setuptools --- recipes/host_setuptools/__init__.py | 18 ++---------------- recipes/host_setuptools/setuptools/README.rst | 1 + recipes/hostpython/ModulesSetup.openssl | 1 + recipes/hostpython/__init__.py | 12 ++++++++++-- 4 files changed, 14 insertions(+), 18 deletions(-) create mode 100644 recipes/host_setuptools/setuptools/README.rst create mode 100644 recipes/hostpython/ModulesSetup.openssl diff --git a/recipes/host_setuptools/__init__.py b/recipes/host_setuptools/__init__.py index ccd0ffe..cc687be 100644 --- a/recipes/host_setuptools/__init__.py +++ b/recipes/host_setuptools/__init__.py @@ -9,25 +9,11 @@ import shutil class HostSetuptools(Recipe): depends = ["hostpython"] archs = ["x86_64"] - url = "" + url = "setuptools" def prebuild_arch(self, arch): hostpython = sh.Command(self.ctx.hostpython) sh.curl("-O", "https://bootstrap.pypa.io/ez_setup.py") - dest_dir = join(self.ctx.dist_dir, "root", "python") - build_env = arch.get_env() - build_env['PYTHONPATH'] = join(dest_dir, 'lib', 'python2.7', 'site-packages') - # shprint(hostpython, "./ez_setup.py", "--to-dir", dest_dir) - shprint(hostpython, "./ez_setup.py", _env=build_env) - - # def install(self): - # arch = list(self.filtered_archs)[0] - # build_dir = self.get_build_dir(arch.arch) - # os.chdir(build_dir) - # hostpython = sh.Command(self.ctx.hostpython) - # build_env = arch.get_env() - # dest_dir = join(self.ctx.dist_dir, "root", "python") - # build_env['PYTHONPATH'] = join(dest_dir, 'lib', 'python2.7', 'site-packages') - # shprint(hostpython, "setup.py", "install", "--prefix", dest_dir, _env=build_env) + shprint(hostpython, "./ez_setup.py") recipe = HostSetuptools() diff --git a/recipes/host_setuptools/setuptools/README.rst b/recipes/host_setuptools/setuptools/README.rst new file mode 100644 index 0000000..f244a47 --- /dev/null +++ b/recipes/host_setuptools/setuptools/README.rst @@ -0,0 +1 @@ +Make toolchain happy diff --git a/recipes/hostpython/ModulesSetup.openssl b/recipes/hostpython/ModulesSetup.openssl new file mode 100644 index 0000000..331ec43 --- /dev/null +++ b/recipes/hostpython/ModulesSetup.openssl @@ -0,0 +1 @@ +_ssl _ssl.c -DUSE_SSL -lssl -lcrypto diff --git a/recipes/hostpython/__init__.py b/recipes/hostpython/__init__.py index e0e1588..0fc7274 100644 --- a/recipes/hostpython/__init__.py +++ b/recipes/hostpython/__init__.py @@ -8,7 +8,8 @@ import shutil class HostpythonRecipe(Recipe): version = "2.7.1" url = "https://www.python.org/ftp/python/{version}/Python-{version}.tar.bz2" - depends = ["hostlibffi", ] + depends = ["hostlibffi"] + optional_depends = ["openssl"] archs = ["x86_64"] def init_with_ctx(self, ctx): @@ -19,13 +20,15 @@ class HostpythonRecipe(Recipe): print("Global: hostpgen located at {}".format(self.ctx.hostpgen)) def prebuild_arch(self, arch): - if self.has_marker("patched"): + if self.has_marker("patched"): return self.copy_file("_scproxy.py", "Lib/_scproxy.py") self.apply_patch("ssize-t-max.patch") self.apply_patch("dynload.patch") self.apply_patch("static-_sqlite3.patch") self.copy_file("ModulesSetup", "Modules/Setup.local") + if "openssl.build_all" in self.ctx.state: + self.append_file("ModulesSetup.openssl", "Modules/Setup.local") self.set_marker("patched") def postbuild_arch(self, arch): @@ -59,6 +62,11 @@ class HostpythonRecipe(Recipe): "--sysroot={}".format(sdk_path), "-I{}".format(join(self.ctx.dist_dir, "hostlibffi", "usr", "local", "include")) ]) + + if "openssl.build_all" in self.ctx.state: + build_env["CFLAGS"] += " -I{}".format(join(self.ctx.dist_dir, "include", + "x86_64", "openssl")) + configure = sh.Command(join(self.build_dir, "configure")) shprint(configure, "--prefix={}".format(join(self.ctx.dist_dir, "hostpython")), From b810f31f765eda54d2a77e6a4d07d14e3bad3d96 Mon Sep 17 00:00:00 2001 From: Robert Niederreiter Date: Fri, 14 Oct 2016 10:36:02 +0200 Subject: [PATCH 05/12] reset kivy download url --- recipes/kivy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recipes/kivy/__init__.py b/recipes/kivy/__init__.py index 3da11e7..c941f26 100644 --- a/recipes/kivy/__init__.py +++ b/recipes/kivy/__init__.py @@ -4,7 +4,7 @@ from os.path import join class KivyRecipe(CythonRecipe): version = "1.9.1" - url = "https://github.com/rnixx/kivy/archive/master.zip" + url = "https://github.com/kivy/kivy/archive/{version}.zip" library = "libkivy.a" depends = ["python", "sdl2", "sdl2_image", "sdl2_mixer", "sdl2_ttf", "ios"] pbx_frameworks = ["OpenGLES", "Accelerate"] From d79e7cf1a9113f7f0d5d84296ca63522e9d88be8 Mon Sep 17 00:00:00 2001 From: Robert Niederreiter Date: Fri, 14 Oct 2016 10:31:18 +0200 Subject: [PATCH 06/12] fix host_setuptools reset kivy download url --- recipes/host_setuptools/__init__.py | 18 ++---------------- recipes/host_setuptools/setuptools/README.rst | 1 + recipes/hostpython/ModulesSetup.openssl | 1 + recipes/hostpython/__init__.py | 12 ++++++++++-- recipes/kivy/__init__.py | 2 +- 5 files changed, 15 insertions(+), 19 deletions(-) create mode 100644 recipes/host_setuptools/setuptools/README.rst create mode 100644 recipes/hostpython/ModulesSetup.openssl diff --git a/recipes/host_setuptools/__init__.py b/recipes/host_setuptools/__init__.py index ccd0ffe..cc687be 100644 --- a/recipes/host_setuptools/__init__.py +++ b/recipes/host_setuptools/__init__.py @@ -9,25 +9,11 @@ import shutil class HostSetuptools(Recipe): depends = ["hostpython"] archs = ["x86_64"] - url = "" + url = "setuptools" def prebuild_arch(self, arch): hostpython = sh.Command(self.ctx.hostpython) sh.curl("-O", "https://bootstrap.pypa.io/ez_setup.py") - dest_dir = join(self.ctx.dist_dir, "root", "python") - build_env = arch.get_env() - build_env['PYTHONPATH'] = join(dest_dir, 'lib', 'python2.7', 'site-packages') - # shprint(hostpython, "./ez_setup.py", "--to-dir", dest_dir) - shprint(hostpython, "./ez_setup.py", _env=build_env) - - # def install(self): - # arch = list(self.filtered_archs)[0] - # build_dir = self.get_build_dir(arch.arch) - # os.chdir(build_dir) - # hostpython = sh.Command(self.ctx.hostpython) - # build_env = arch.get_env() - # dest_dir = join(self.ctx.dist_dir, "root", "python") - # build_env['PYTHONPATH'] = join(dest_dir, 'lib', 'python2.7', 'site-packages') - # shprint(hostpython, "setup.py", "install", "--prefix", dest_dir, _env=build_env) + shprint(hostpython, "./ez_setup.py") recipe = HostSetuptools() diff --git a/recipes/host_setuptools/setuptools/README.rst b/recipes/host_setuptools/setuptools/README.rst new file mode 100644 index 0000000..f244a47 --- /dev/null +++ b/recipes/host_setuptools/setuptools/README.rst @@ -0,0 +1 @@ +Make toolchain happy diff --git a/recipes/hostpython/ModulesSetup.openssl b/recipes/hostpython/ModulesSetup.openssl new file mode 100644 index 0000000..331ec43 --- /dev/null +++ b/recipes/hostpython/ModulesSetup.openssl @@ -0,0 +1 @@ +_ssl _ssl.c -DUSE_SSL -lssl -lcrypto diff --git a/recipes/hostpython/__init__.py b/recipes/hostpython/__init__.py index e0e1588..0fc7274 100644 --- a/recipes/hostpython/__init__.py +++ b/recipes/hostpython/__init__.py @@ -8,7 +8,8 @@ import shutil class HostpythonRecipe(Recipe): version = "2.7.1" url = "https://www.python.org/ftp/python/{version}/Python-{version}.tar.bz2" - depends = ["hostlibffi", ] + depends = ["hostlibffi"] + optional_depends = ["openssl"] archs = ["x86_64"] def init_with_ctx(self, ctx): @@ -19,13 +20,15 @@ class HostpythonRecipe(Recipe): print("Global: hostpgen located at {}".format(self.ctx.hostpgen)) def prebuild_arch(self, arch): - if self.has_marker("patched"): + if self.has_marker("patched"): return self.copy_file("_scproxy.py", "Lib/_scproxy.py") self.apply_patch("ssize-t-max.patch") self.apply_patch("dynload.patch") self.apply_patch("static-_sqlite3.patch") self.copy_file("ModulesSetup", "Modules/Setup.local") + if "openssl.build_all" in self.ctx.state: + self.append_file("ModulesSetup.openssl", "Modules/Setup.local") self.set_marker("patched") def postbuild_arch(self, arch): @@ -59,6 +62,11 @@ class HostpythonRecipe(Recipe): "--sysroot={}".format(sdk_path), "-I{}".format(join(self.ctx.dist_dir, "hostlibffi", "usr", "local", "include")) ]) + + if "openssl.build_all" in self.ctx.state: + build_env["CFLAGS"] += " -I{}".format(join(self.ctx.dist_dir, "include", + "x86_64", "openssl")) + configure = sh.Command(join(self.build_dir, "configure")) shprint(configure, "--prefix={}".format(join(self.ctx.dist_dir, "hostpython")), diff --git a/recipes/kivy/__init__.py b/recipes/kivy/__init__.py index 3da11e7..c941f26 100644 --- a/recipes/kivy/__init__.py +++ b/recipes/kivy/__init__.py @@ -4,7 +4,7 @@ from os.path import join class KivyRecipe(CythonRecipe): version = "1.9.1" - url = "https://github.com/rnixx/kivy/archive/master.zip" + url = "https://github.com/kivy/kivy/archive/{version}.zip" library = "libkivy.a" depends = ["python", "sdl2", "sdl2_image", "sdl2_mixer", "sdl2_ttf", "ios"] pbx_frameworks = ["OpenGLES", "Accelerate"] From 7cca79030a6ce0a643fb81d582ea3f2b98d04196 Mon Sep 17 00:00:00 2001 From: Robert Niederreiter Date: Fri, 14 Oct 2016 16:46:33 +0200 Subject: [PATCH 07/12] Avoid errors from setuptools if installed as egg with .pth mapping on subsequent package installs with hostpython --- recipes/host_setuptools/__init__.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/recipes/host_setuptools/__init__.py b/recipes/host_setuptools/__init__.py index cc687be..5ee6056 100644 --- a/recipes/host_setuptools/__init__.py +++ b/recipes/host_setuptools/__init__.py @@ -7,7 +7,7 @@ import shutil class HostSetuptools(Recipe): - depends = ["hostpython"] + depends = ["openssl", "hostpython"] archs = ["x86_64"] url = "setuptools" @@ -15,5 +15,20 @@ class HostSetuptools(Recipe): hostpython = sh.Command(self.ctx.hostpython) sh.curl("-O", "https://bootstrap.pypa.io/ez_setup.py") shprint(hostpython, "./ez_setup.py") + # Extract setuptools egg and remove .pth files. Otherwise subsequent + # python package installations using setuptools will raise exceptions. + # Setuptools version 28.3.0 + site_packages_path = join( + self.ctx.dist_dir, 'hostpython', + 'lib', 'python2.7', 'site-packages') + os.chdir(site_packages_path) + with open('setuptools.pth', 'r') as f: + setuptools_egg_path = f.read().strip('./').strip('\n') + unzip = sh.Command('unzip') + shprint(unzip, setuptools_egg_path) + os.remove(setuptools_egg_path) + os.remove('setuptools.pth') + os.remove('easy-install.pth') + shutil.rmtree('EGG-INFO') recipe = HostSetuptools() From d4e36358020d616a374bcff4bd0a398a761ceee4 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 4 Nov 2016 12:47:04 +0200 Subject: [PATCH 08/12] Removed std out re-direction --- tools/templates/{{ cookiecutter.project_name }}-ios/main.m | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tools/templates/{{ cookiecutter.project_name }}-ios/main.m b/tools/templates/{{ cookiecutter.project_name }}-ios/main.m index 99f2325..3627339 100644 --- a/tools/templates/{{ cookiecutter.project_name }}-ios/main.m +++ b/tools/templates/{{ cookiecutter.project_name }}-ios/main.m @@ -121,13 +121,6 @@ void load_custom_builtin_importer() { "import sys, imp\n" \ "from os import environ\n" \ "from os.path import exists, join\n" \ - "# Fake redirection when we run the app without xcode\n" \ - "if 'CFLOG_FORCE_STDERR' not in environ:\n" \ - " class fakestd(object):\n" \ - " def write(self, *args, **kw): pass\n" \ - " def flush(self, *args, **kw): pass\n" \ - " sys.stdout = fakestd()\n" \ - " sys.stderr = fakestd()\n" \ "# Custom builtin importer for precompiled modules\n" \ "class CustomBuiltinImporter(object):\n" \ " def find_module(self, fullname, mpath=None):\n" \ From 0cb7a8ff10d2e5f70ad006a77721e5c68992f54e Mon Sep 17 00:00:00 2001 From: gfyoung Date: Sun, 6 Nov 2016 03:30:58 -0500 Subject: [PATCH 09/12] Clean up temp files before urlretrieve --- toolchain.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/toolchain.py b/toolchain.py index 4c7f951..e1f958f 100755 --- a/toolchain.py +++ b/toolchain.py @@ -19,9 +19,9 @@ import shutil import fnmatch from datetime import datetime try: - from urllib.request import FancyURLopener + from urllib.request import FancyURLopener, urlcleanup except ImportError: - from urllib import FancyURLopener + from urllib import FancyURLopener, urlcleanup curdir = dirname(__file__) sys.path.insert(0, join(curdir, "tools", "external")) @@ -410,6 +410,9 @@ class Recipe(object): if exists(filename): unlink(filename) + # Clean up temporary files just in case before downloading. + urlcleanup() + print('Downloading {0}'.format(url)) urlretrieve(url, filename, report_hook) return filename From f48f66363cd18092e7bf8516d6de7e28b70a8b23 Mon Sep 17 00:00:00 2001 From: Richard Date: Sun, 6 Nov 2016 14:46:31 +0200 Subject: [PATCH 10/12] Added corrected check for KIVY_NO_CONSOLE flag --- .../templates/{{ cookiecutter.project_name }}-ios/main.m | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/templates/{{ cookiecutter.project_name }}-ios/main.m b/tools/templates/{{ cookiecutter.project_name }}-ios/main.m index 3627339..80468a8 100644 --- a/tools/templates/{{ cookiecutter.project_name }}-ios/main.m +++ b/tools/templates/{{ cookiecutter.project_name }}-ios/main.m @@ -121,6 +121,14 @@ void load_custom_builtin_importer() { "import sys, imp\n" \ "from os import environ\n" \ "from os.path import exists, join\n" \ + "# Fake redirection to supress console output\n" \ + "if environ.get('KIVY_NO_CONSOLE', '0') == '1':\n" \ + " print('Killing console. Remove me!')\n" \ + " class fakestd(object):\n" \ + " def write(self, *args, **kw): pass\n" \ + " def flush(self, *args, **kw): pass\n" \ + " sys.stdout = fakestd()\n" \ + " sys.stderr = fakestd()\n" \ "# Custom builtin importer for precompiled modules\n" \ "class CustomBuiltinImporter(object):\n" \ " def find_module(self, fullname, mpath=None):\n" \ From d661336935a8920e1e5c5971ab454b36773e6a92 Mon Sep 17 00:00:00 2001 From: Richard Date: Sun, 6 Nov 2016 14:47:55 +0200 Subject: [PATCH 11/12] Removed test line --- tools/templates/{{ cookiecutter.project_name }}-ios/main.m | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/templates/{{ cookiecutter.project_name }}-ios/main.m b/tools/templates/{{ cookiecutter.project_name }}-ios/main.m index 80468a8..a5f6c4d 100644 --- a/tools/templates/{{ cookiecutter.project_name }}-ios/main.m +++ b/tools/templates/{{ cookiecutter.project_name }}-ios/main.m @@ -123,7 +123,6 @@ void load_custom_builtin_importer() { "from os.path import exists, join\n" \ "# Fake redirection to supress console output\n" \ "if environ.get('KIVY_NO_CONSOLE', '0') == '1':\n" \ - " print('Killing console. Remove me!')\n" \ " class fakestd(object):\n" \ " def write(self, *args, **kw): pass\n" \ " def flush(self, *args, **kw): pass\n" \ From 5a07f17f86c99d16720e2dcff5a75f5358b7e45d Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 9 Nov 2016 18:24:16 +0000 Subject: [PATCH 12/12] The recipe don't download the archive. If I try download the archive manually, this url "http://vorboss.dl.sourceforge.net/project/libpng/libpng16/1.6.24/libpng-1.6.24.tar.gz" redirect to "https://sourceforge.net/projects/libpng/files/" ... Extract libpng Extract libpng for i386 Traceback (most recent call last): File "./toolchain.py", line 1311, in ToolchainCL() File "./toolchain.py", line 1082, in __init__ getattr(self, args.command)() File "./toolchain.py", line 1106, in build build_recipes(args.recipe, ctx) File "./toolchain.py", line 974, in build_recipes recipe.execute() File "/Users/prrcarvalho/Development/kivy-ios-master/toolchain.py", line 585, in execute self.extract() File "/Users/prrcarvalho/Development/kivy-ios-master/toolchain.py", line 56, in _cache_execution f(self, *args, **kwargs) File "/Users/prrcarvalho/Development/kivy-ios-master/toolchain.py", line 621, in extract self.extract_arch(arch.arch) File "/Users/prrcarvalho/Development/kivy-ios-master/toolchain.py", line 625, in extract_arch dest_dir = join(build_dir, self.archive_root) File "/Users/prrcarvalho/Development/kivy-ios-master/toolchain.py", line 577, in archive_root value = self.get_archive_rootdir(self.archive_fn) File "/Users/prrcarvalho/Development/kivy-ios-master/toolchain.py", line 442, in get_archive_rootdir archive = tarfile.open(filename) File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/tarfile.py", line 1673, in open return func(name, "r", fileobj, **kwargs) File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/tarfile.py", line 1738, in gzopen fileobj = gzip.GzipFile(name, mode, compresslevel, fileobj) File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/gzip.py", line 94, in __init__ fileobj = self.myfileobj = __builtin__.open(filename, mode or 'rb') IOError: [Errno 2] No such file or directory: '/Users/prrcarvalho/Development/kivy-ios-master/.cache/libpng-libpng-1.6.24.tar.gz' --- recipes/libpng/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/recipes/libpng/__init__.py b/recipes/libpng/__init__.py index 7d07c83..fd2e3d0 100644 --- a/recipes/libpng/__init__.py +++ b/recipes/libpng/__init__.py @@ -4,8 +4,8 @@ from os.path import join import sh class PngRecipe(Recipe): - version = '1.6.24' - url = 'http://vorboss.dl.sourceforge.net/project/libpng/libpng16/1.6.24/libpng-1.6.24.tar.gz' + version = '1.6.26' + url = 'http://downloads.sourceforge.net/sourceforge/libpng/libpng-{version}.tar.gz' depends = ["python"] library = '.libs/libpng16.a' @@ -23,4 +23,4 @@ class PngRecipe(Recipe): shprint(sh.make, "clean") shprint(sh.make, _env=build_env) -recipe = PngRecipe() \ No newline at end of file +recipe = PngRecipe()