diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 9d206f46ae..51846163d7 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -3160,6 +3160,13 @@ .. versionadded:: 3.8 +.. data:: allows_subprocesses + + Boolean that describes whether subprocesses can be by the operating system. + Some platforms (e.g., iOS mobile devices) *implement* calls like + :func:`execv` and :func:`spawnv`, but will raise errors or break if + called. Calls to create subprocesses should only be invoked if + :data:`os.allows_subprocesses` is `True`. .. data:: MFD_CLOEXEC MFD_ALLOW_SEALING diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index 3150aa6070..d37e0d3a97 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -25,6 +25,11 @@ :pep:`324` -- PEP proposing the subprocess module +:mod:`subprocess` can only be used on platforms that support subprocess +creation. Some platforms (especially mobile platforms) may not support +subprocesses; if :data:`os.allows_subprocesses` is `False`, any calls in +this module that request a subprocess be created will raise a +:exc:`RuntimeError`. Using the :mod:`subprocess` Module ---------------------------------- diff --git a/Include/datetime.h b/Include/datetime.h index 5d9f2558f9..fc66471923 100644 --- a/Include/datetime.h +++ b/Include/datetime.h @@ -179,7 +179,6 @@ #define PyDateTime_CAPSULE_NAME "datetime.datetime_CAPI" - /* This block is only used as part of the public API and should not be * included in _datetimemodule.c, which does not use the C API capsule. * See bpo-35081 for more details. diff --git a/Lib/ctypes/test/test_as_parameter.py b/Lib/ctypes/test/test_as_parameter.py index f9d27cb89d..da172b345a 100644 --- a/Lib/ctypes/test/test_as_parameter.py +++ b/Lib/ctypes/test/test_as_parameter.py @@ -1,9 +1,11 @@ +import os import unittest from ctypes import * from ctypes.test import need_symbol import _ctypes_test -dll = CDLL(_ctypes_test.__file__) + +dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) try: CALLBACK_FUNCTYPE = WINFUNCTYPE diff --git a/Lib/ctypes/test/test_bitfields.py b/Lib/ctypes/test/test_bitfields.py index 992b8c4da3..e99c3e21a2 100644 --- a/Lib/ctypes/test/test_bitfields.py +++ b/Lib/ctypes/test/test_bitfields.py @@ -24,7 +24,7 @@ ("R", c_short, 6), ("S", c_short, 7)] -func = CDLL(_ctypes_test.__file__).unpack_bitfields +func = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])).unpack_bitfields func.argtypes = POINTER(BITS), c_char ##for n in "ABCDEFGHIMNOPQRS": diff --git a/Lib/ctypes/test/test_callbacks.py b/Lib/ctypes/test/test_callbacks.py index d8e9c5a760..52820932b0 100644 --- a/Lib/ctypes/test/test_callbacks.py +++ b/Lib/ctypes/test/test_callbacks.py @@ -160,7 +160,7 @@ def test_integrate(self): # Derived from some then non-working code, posted by David Foster - dll = CDLL(_ctypes_test.__file__) + dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) # The function prototype called by 'integrate': double func(double); CALLBACK = CFUNCTYPE(c_double, c_double) @@ -211,7 +211,7 @@ def test_callback_register_int(self): # Issue #8275: buggy handling of callback args under Win64 # NOTE: should be run on release builds as well - dll = CDLL(_ctypes_test.__file__) + dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) CALLBACK = CFUNCTYPE(c_int, c_int, c_int, c_int, c_int, c_int) # All this function does is call the callback with its args squared func = dll._testfunc_cbk_reg_int @@ -227,7 +227,7 @@ def test_callback_register_double(self): # Issue #8275: buggy handling of callback args under Win64 # NOTE: should be run on release builds as well - dll = CDLL(_ctypes_test.__file__) + dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) CALLBACK = CFUNCTYPE(c_double, c_double, c_double, c_double, c_double, c_double) # All this function does is call the callback with its args squared diff --git a/Lib/ctypes/test/test_cfuncs.py b/Lib/ctypes/test/test_cfuncs.py index ac2240fa19..b08b5f7581 100644 --- a/Lib/ctypes/test/test_cfuncs.py +++ b/Lib/ctypes/test/test_cfuncs.py @@ -1,6 +1,7 @@ # A lot of failures in these tests on Mac OS X. # Byte order related? +import os import unittest from ctypes import * from ctypes.test import need_symbol @@ -8,7 +9,7 @@ import _ctypes_test class CFunctions(unittest.TestCase): - _dll = CDLL(_ctypes_test.__file__) + _dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) def S(self): return c_longlong.in_dll(self._dll, "last_tf_arg_s").value @@ -206,7 +207,7 @@ @need_symbol('WinDLL') class stdcallCFunctions(CFunctions): - _dll = stdcall_dll(_ctypes_test.__file__) + _dll = stdcall_dll(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) if __name__ == '__main__': unittest.main() diff --git a/Lib/ctypes/test/test_checkretval.py b/Lib/ctypes/test/test_checkretval.py index e9567dc391..3c3fbf103c 100644 --- a/Lib/ctypes/test/test_checkretval.py +++ b/Lib/ctypes/test/test_checkretval.py @@ -1,3 +1,4 @@ +import os import unittest from ctypes import * @@ -14,7 +15,7 @@ def test_checkretval(self): import _ctypes_test - dll = CDLL(_ctypes_test.__file__) + dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) self.assertEqual(42, dll._testfunc_p_p(42)) dll._testfunc_p_p.restype = CHECKED diff --git a/Lib/ctypes/test/test_funcptr.py b/Lib/ctypes/test/test_funcptr.py index e0b9b54e97..a5b1b55294 100644 --- a/Lib/ctypes/test/test_funcptr.py +++ b/Lib/ctypes/test/test_funcptr.py @@ -1,4 +1,4 @@ -import unittest +import os, unittest from ctypes import * try: @@ -8,7 +8,10 @@ WINFUNCTYPE = CFUNCTYPE import _ctypes_test -lib = CDLL(_ctypes_test.__file__) + + +lib = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) + class CFuncPtrTestCase(unittest.TestCase): def test_basic(self): diff --git a/Lib/ctypes/test/test_functions.py b/Lib/ctypes/test/test_functions.py index 7562892420..bf8f4dc92c 100644 --- a/Lib/ctypes/test/test_functions.py +++ b/Lib/ctypes/test/test_functions.py @@ -7,6 +7,7 @@ from ctypes import * from ctypes.test import need_symbol +import os import sys, unittest try: @@ -16,7 +17,9 @@ WINFUNCTYPE = CFUNCTYPE import _ctypes_test -dll = CDLL(_ctypes_test.__file__) + + +dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) if sys.platform == "win32": windll = WinDLL(_ctypes_test.__file__) diff --git a/Lib/ctypes/test/test_libc.py b/Lib/ctypes/test/test_libc.py index 56285b5ff8..f78a152ade 100644 --- a/Lib/ctypes/test/test_libc.py +++ b/Lib/ctypes/test/test_libc.py @@ -1,9 +1,12 @@ +import os import unittest from ctypes import * import _ctypes_test -lib = CDLL(_ctypes_test.__file__) + +lib = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) + def three_way_cmp(x, y): """Return -1 if x < y, 0 if x == y and 1 if x > y""" diff --git a/Lib/ctypes/test/test_parameters.py b/Lib/ctypes/test/test_parameters.py index 38af7ac13d..db0d4986d6 100644 --- a/Lib/ctypes/test/test_parameters.py +++ b/Lib/ctypes/test/test_parameters.py @@ -140,7 +140,7 @@ import _ctypes_test from ctypes import CDLL, c_void_p, ArgumentError - func = CDLL(_ctypes_test.__file__)._testfunc_p_p + func = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE']))._testfunc_p_p func.restype = c_void_p # TypeError: has no from_param method self.assertRaises(TypeError, setattr, func, "argtypes", (object,)) diff --git a/Lib/ctypes/test/test_pickling.py b/Lib/ctypes/test/test_pickling.py index c4a79b9779..833608b629 100644 --- a/Lib/ctypes/test/test_pickling.py +++ b/Lib/ctypes/test/test_pickling.py @@ -1,8 +1,11 @@ import unittest +import os import pickle from ctypes import * import _ctypes_test -dll = CDLL(_ctypes_test.__file__) + + +dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) class X(Structure): _fields_ = [("a", c_int), ("b", c_double)] diff --git a/Lib/ctypes/test/test_pointers.py b/Lib/ctypes/test/test_pointers.py index e97515879f..d678be3800 100644 --- a/Lib/ctypes/test/test_pointers.py +++ b/Lib/ctypes/test/test_pointers.py @@ -1,3 +1,4 @@ +import os import unittest, sys from ctypes import * @@ -20,7 +21,7 @@ self.assertRaises(TypeError, A, c_ulong(33)) def test_pass_pointers(self): - dll = CDLL(_ctypes_test.__file__) + dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) func = dll._testfunc_p_p if sizeof(c_longlong) == sizeof(c_void_p): func.restype = c_longlong @@ -38,7 +39,7 @@ self.assertEqual(res[0], 12345678) def test_change_pointers(self): - dll = CDLL(_ctypes_test.__file__) + dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) func = dll._testfunc_p_p i = c_int(87654) @@ -77,7 +78,7 @@ return 0 callback = PROTOTYPE(func) - dll = CDLL(_ctypes_test.__file__) + dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) # This function expects a function pointer, # and calls this with an integer pointer as parameter. # The int pointer points to a table containing the numbers 1..10 @@ -143,7 +144,7 @@ def test_charpp(self): """Test that a character pointer-to-pointer is correctly passed""" - dll = CDLL(_ctypes_test.__file__) + dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) func = dll._testfunc_c_p_p func.restype = c_char_p argv = (c_char_p * 2)() diff --git a/Lib/ctypes/test/test_prototypes.py b/Lib/ctypes/test/test_prototypes.py index cd0c649de3..539351f798 100644 --- a/Lib/ctypes/test/test_prototypes.py +++ b/Lib/ctypes/test/test_prototypes.py @@ -1,3 +1,4 @@ +import os from ctypes import * from ctypes.test import need_symbol import unittest @@ -23,7 +24,9 @@ # In this case, there would have to be an additional reference to the argument... import _ctypes_test -testdll = CDLL(_ctypes_test.__file__) + + +testdll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) # Return machine address `a` as a (possibly long) non-negative integer. # Starting with Python 2.5, id(anything) is always non-negative, and diff --git a/Lib/ctypes/test/test_refcounts.py b/Lib/ctypes/test/test_refcounts.py index f2edfa6400..0e4dd7c126 100644 --- a/Lib/ctypes/test/test_refcounts.py +++ b/Lib/ctypes/test/test_refcounts.py @@ -1,3 +1,4 @@ +import os import unittest from test import support import ctypes @@ -7,7 +8,10 @@ OtherCallback = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_ulonglong) import _ctypes_test -dll = ctypes.CDLL(_ctypes_test.__file__) + + +dll = ctypes.CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) + class RefcountTestCase(unittest.TestCase): diff --git a/Lib/ctypes/test/test_returnfuncptrs.py b/Lib/ctypes/test/test_returnfuncptrs.py index 1974f40df6..7b76fae44c 100644 --- a/Lib/ctypes/test/test_returnfuncptrs.py +++ b/Lib/ctypes/test/test_returnfuncptrs.py @@ -1,5 +1,6 @@ import unittest from ctypes import * +import os import _ctypes_test @@ -8,7 +9,7 @@ def test_with_prototype(self): # The _ctypes_test shared lib/dll exports quite some functions for testing. # The get_strchr function returns a *pointer* to the C strchr function. - dll = CDLL(_ctypes_test.__file__) + dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) get_strchr = dll.get_strchr get_strchr.restype = CFUNCTYPE(c_char_p, c_char_p, c_char) strchr = get_strchr() @@ -20,7 +21,7 @@ self.assertRaises(TypeError, strchr, b"abcdef") def test_without_prototype(self): - dll = CDLL(_ctypes_test.__file__) + dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) get_strchr = dll.get_strchr # the default 'c_int' would not work on systems where sizeof(int) != sizeof(void *) get_strchr.restype = c_void_p @@ -34,7 +35,7 @@ self.assertRaises(TypeError, strchr, b"abcdef") def test_from_dll(self): - dll = CDLL(_ctypes_test.__file__) + dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) # _CFuncPtr instances are now callable with a tuple argument # which denotes a function name and a dll: strchr = CFUNCTYPE(c_char_p, c_char_p, c_char)(("my_strchr", dll)) @@ -50,13 +51,13 @@ if key == 0: return "my_strchr" if key == 1: - return CDLL(_ctypes_test.__file__) + return CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) raise IndexError # _CFuncPtr instances are now callable with a tuple argument # which denotes a function name and a dll: strchr = CFUNCTYPE(c_char_p, c_char_p, c_char)( - BadSequence(("my_strchr", CDLL(_ctypes_test.__file__)))) + BadSequence(("my_strchr", CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE']))))) self.assertTrue(strchr(b"abcdef", b"b"), "bcdef") self.assertEqual(strchr(b"abcdef", b"x"), None) self.assertRaises(ArgumentError, strchr, b"abcdef", 3.0) diff --git a/Lib/ctypes/test/test_slicing.py b/Lib/ctypes/test/test_slicing.py index a3932f1767..6d7bfff8f2 100644 --- a/Lib/ctypes/test/test_slicing.py +++ b/Lib/ctypes/test/test_slicing.py @@ -1,3 +1,4 @@ +import os import unittest from ctypes import * from ctypes.test import need_symbol @@ -62,7 +63,7 @@ def test_char_ptr(self): s = b"abcdefghijklmnopqrstuvwxyz" - dll = CDLL(_ctypes_test.__file__) + dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) dll.my_strdup.restype = POINTER(c_char) dll.my_free.restype = None res = dll.my_strdup(s) @@ -94,7 +95,7 @@ dll.my_free(res) def test_char_ptr_with_free(self): - dll = CDLL(_ctypes_test.__file__) + dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) s = b"abcdefghijklmnopqrstuvwxyz" class allocated_c_char_p(c_char_p): @@ -130,7 +131,7 @@ def test_wchar_ptr(self): s = "abcdefghijklmnopqrstuvwxyz\0" - dll = CDLL(_ctypes_test.__file__) + dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) dll.my_wcsdup.restype = POINTER(c_wchar) dll.my_wcsdup.argtypes = POINTER(c_wchar), dll.my_free.restype = None diff --git a/Lib/ctypes/test/test_stringptr.py b/Lib/ctypes/test/test_stringptr.py index c20951f4ce..fde0eef1c7 100644 --- a/Lib/ctypes/test/test_stringptr.py +++ b/Lib/ctypes/test/test_stringptr.py @@ -1,10 +1,12 @@ +import os import unittest from test import support from ctypes import * import _ctypes_test -lib = CDLL(_ctypes_test.__file__) + +lib = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) class StringPtrTestCase(unittest.TestCase): diff --git a/Lib/ctypes/test/test_unicode.py b/Lib/ctypes/test/test_unicode.py index c200af7b65..ea3945d56b 100644 --- a/Lib/ctypes/test/test_unicode.py +++ b/Lib/ctypes/test/test_unicode.py @@ -1,3 +1,4 @@ +import os import unittest import ctypes from ctypes.test import need_symbol @@ -7,7 +8,7 @@ @need_symbol('c_wchar') class UnicodeTestCase(unittest.TestCase): def test_wcslen(self): - dll = ctypes.CDLL(_ctypes_test.__file__) + dll = ctypes.CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) wcslen = dll.my_wcslen wcslen.argtypes = [ctypes.c_wchar_p] @@ -26,7 +27,7 @@ self.assertEqual(buf[::2], 'a\xe4\xfc') self.assertEqual(buf[6:5:-1], "") -func = ctypes.CDLL(_ctypes_test.__file__)._testfunc_p_p +func = ctypes.CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE']))._testfunc_p_p class StringTestCase(UnicodeTestCase): def setUp(self): diff --git a/Lib/ctypes/test/test_values.py b/Lib/ctypes/test/test_values.py index 87eb9198ad..e40b82506f 100644 --- a/Lib/ctypes/test/test_values.py +++ b/Lib/ctypes/test/test_values.py @@ -2,6 +2,7 @@ A testcase which accesses *values* in a dll. """ +import os import unittest import sys from ctypes import * @@ -13,7 +14,7 @@ def test_an_integer(self): # This test checks and changes an integer stored inside the # _ctypes_test dll/shared lib. - ctdll = CDLL(_ctypes_test.__file__) + ctdll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) an_integer = c_int.in_dll(ctdll, "an_integer") x = an_integer.value self.assertEqual(x, ctdll.get_an_integer()) @@ -25,7 +26,7 @@ self.assertEqual(x, ctdll.get_an_integer()) def test_undefined(self): - ctdll = CDLL(_ctypes_test.__file__) + ctdll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) self.assertRaises(ValueError, c_int.in_dll, ctdll, "Undefined_Symbol") class PythonValuesTestCase(unittest.TestCase): diff --git a/Lib/ctypes/test/test_win32.py b/Lib/ctypes/test/test_win32.py index e51bdc8ad6..78ba8e6786 100644 --- a/Lib/ctypes/test/test_win32.py +++ b/Lib/ctypes/test/test_win32.py @@ -1,6 +1,7 @@ # Windows specific tests from ctypes import * +import os import unittest, sys from test import support @@ -102,7 +103,7 @@ ("right", c_long), ("bottom", c_long)] - dll = CDLL(_ctypes_test.__file__) + dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) pt = POINT(15, 25) left = c_long.in_dll(dll, 'left') diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py index 0c2510e161..6c3c43f11d 100644 --- a/Lib/ctypes/util.py +++ b/Lib/ctypes/util.py @@ -67,7 +67,7 @@ return fname return None -elif os.name == "posix" and sys.platform == "darwin": +elif os.name == "posix" and sys.platform in ('darwin', 'ios', 'tvos', 'watchos'): from ctypes.macholib.dyld import dyld_find as _dyld_find def find_library(name): possible = ['lib%s.dylib' % name, diff --git a/Lib/distutils/tests/test_build_ext.py b/Lib/distutils/tests/test_build_ext.py index 808c0dc287..b4813efe0c 100644 --- a/Lib/distutils/tests/test_build_ext.py +++ b/Lib/distutils/tests/test_build_ext.py @@ -52,6 +52,8 @@ def build_ext(self, *args, **kwargs): return build_ext(*args, **kwargs) + @unittest.skipUnless(os.name == 'nt' or (os.name == 'posix' and hasattr(os, 'fork') and os.allows_subprocesses), + "distutils cannot spawn child processes") def test_build_ext(self): cmd = support.missing_compiler_executable() if cmd is not None: @@ -328,6 +330,8 @@ cmd.run() self.assertEqual(cmd.compiler, 'unix') + @unittest.skipUnless(os.name == 'nt' or (os.name == 'posix' and hasattr(os, 'fork') and os.allows_subprocesses), + "distutils cannot spawn child processes") def test_get_outputs(self): cmd = support.missing_compiler_executable() if cmd is not None: diff --git a/Lib/distutils/tests/test_build_py.py b/Lib/distutils/tests/test_build_py.py index 0712e92c6a..52a0121250 100644 --- a/Lib/distutils/tests/test_build_py.py +++ b/Lib/distutils/tests/test_build_py.py @@ -106,6 +106,8 @@ ['boiledeggs.%s.pyc' % sys.implementation.cache_tag]) @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') + @unittest.skipUnless(os.name == 'nt' or (os.name == 'posix' and hasattr(os, 'fork') and os.allows_subprocesses), + "distutils cannot spawn child processes") def test_byte_compile_optimized(self): project_dir, dist = self.create_dist(py_modules=['boiledeggs']) os.chdir(project_dir) diff --git a/Lib/distutils/tests/test_config_cmd.py b/Lib/distutils/tests/test_config_cmd.py index 9aeab07b46..3c6dfc0439 100644 --- a/Lib/distutils/tests/test_config_cmd.py +++ b/Lib/distutils/tests/test_config_cmd.py @@ -38,6 +38,7 @@ self.assertEqual(len(self._logs), numlines+1) @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_search_cpp(self): cmd = missing_compiler_executable(['preprocessor']) if cmd is not None: diff --git a/Lib/distutils/tests/test_cygwinccompiler.py b/Lib/distutils/tests/test_cygwinccompiler.py index 9dc869de4c..176a87f8a8 100644 --- a/Lib/distutils/tests/test_cygwinccompiler.py +++ b/Lib/distutils/tests/test_cygwinccompiler.py @@ -5,11 +5,14 @@ from io import BytesIO from test.support import run_unittest -from distutils import cygwinccompiler -from distutils.cygwinccompiler import (check_config_h, - CONFIG_H_OK, CONFIG_H_NOTOK, - CONFIG_H_UNCERTAIN, get_versions, - get_msvcr) +# Importing cygwinccompiler attempts to import other tools +# that may not exist unless you're on win32. +if sys.platform == 'win32': + from distutils import cygwinccompiler + from distutils.cygwinccompiler import (check_config_h, + CONFIG_H_OK, CONFIG_H_NOTOK, + CONFIG_H_UNCERTAIN, get_versions, + get_msvcr) from distutils.tests import support class FakePopen(object): @@ -25,6 +28,7 @@ self.stdout = os.popen(cmd, 'r') +@unittest.skipUnless(sys.platform == "win32", "These tests are only for win32") class CygwinCCompilerTestCase(support.TempdirManager, unittest.TestCase): @@ -118,7 +122,7 @@ def test_get_msvcr(self): # none - sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) ' + sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) ' '\n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]') self.assertEqual(get_msvcr(), None) diff --git a/Lib/distutils/tests/test_install.py b/Lib/distutils/tests/test_install.py index 51c80e0421..11008da5f2 100644 --- a/Lib/distutils/tests/test_install.py +++ b/Lib/distutils/tests/test_install.py @@ -197,6 +197,8 @@ 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] self.assertEqual(found, expected) + @unittest.skipUnless(os.name == 'nt' or (os.name == 'posix' and hasattr(os, 'fork') and os.allows_subprocesses), + "distutils cannot spawn child processes") def test_record_extensions(self): cmd = test_support.missing_compiler_executable() if cmd is not None: diff --git a/Lib/distutils/tests/test_install_lib.py b/Lib/distutils/tests/test_install_lib.py index fda6315bbc..63c71a8d46 100644 --- a/Lib/distutils/tests/test_install_lib.py +++ b/Lib/distutils/tests/test_install_lib.py @@ -35,6 +35,8 @@ self.assertEqual(cmd.optimize, 2) @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') + @unittest.skipUnless(os.name == 'nt' or (os.name == 'posix' and hasattr(os, 'fork') and os.allows_subprocesses), + "distutils cannot spawn child processes") def test_byte_compile(self): project_dir, dist = self.create_dist() os.chdir(project_dir) diff --git a/Lib/distutils/tests/test_spawn.py b/Lib/distutils/tests/test_spawn.py index ad5038142f..856afe86c1 100644 --- a/Lib/distutils/tests/test_spawn.py +++ b/Lib/distutils/tests/test_spawn.py @@ -15,8 +15,8 @@ support.LoggingSilencer, unittest.TestCase): - @unittest.skipUnless(os.name in ('nt', 'posix'), - 'Runs only under posix or nt') + @unittest.skipUnless(os.name == 'nt' or (os.name == 'posix' and hasattr(os, 'fork') and os.allows_subprocesses), + "distutils cannot spawn child processes") def test_spawn(self): tmpdir = self.mkdtemp() diff --git a/Lib/distutils/tests/test_sysconfig.py b/Lib/distutils/tests/test_sysconfig.py index 236755d095..721bc91093 100644 --- a/Lib/distutils/tests/test_sysconfig.py +++ b/Lib/distutils/tests/test_sysconfig.py @@ -244,6 +244,7 @@ self.assertIsNotNone(vars['SO']) self.assertEqual(vars['SO'], vars['EXT_SUFFIX']) + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_customize_compiler_before_get_config_vars(self): # Issue #21923: test that a Distribution compiler # instance can be called without an explicit call to diff --git a/Lib/distutils/tests/test_util.py b/Lib/distutils/tests/test_util.py index bf0d4333f9..09ba51bcea 100644 --- a/Lib/distutils/tests/test_util.py +++ b/Lib/distutils/tests/test_util.py @@ -233,6 +233,8 @@ # XXX platforms to be covered: mac + @unittest.skipUnless(os.name == 'nt' or (os.name == 'posix' and hasattr(os, 'fork') and os.allows_subprocesses), + "distutils cannot spawn child processes") def test_check_environ(self): util._environ_checked = 0 os.environ.pop('HOME', None) diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 25a3f8c0e0..560307ffad 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -21,7 +21,7 @@ # Bootstrap-related code ###################################################### _CASE_INSENSITIVE_PLATFORMS_STR_KEY = 'win', -_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY = 'cygwin', 'darwin' +_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY = 'cygwin', 'darwin', 'ios', 'tvos', 'watchos' _CASE_INSENSITIVE_PLATFORMS = (_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY + _CASE_INSENSITIVE_PLATFORMS_STR_KEY) diff --git a/Lib/os.py b/Lib/os.py index b794159f86..449cf932b6 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -36,7 +36,7 @@ __all__ = ["altsep", "curdir", "pardir", "sep", "pathsep", "linesep", "defpath", "name", "path", "devnull", "SEEK_SET", "SEEK_CUR", "SEEK_END", "fsencode", "fsdecode", "get_exec_path", "fdopen", - "popen", "extsep"] + "popen", "extsep", "allows_subprocesses"] def _exists(name): return name in globals() @@ -830,6 +830,13 @@ fsencode, fsdecode = _fscodec() del _fscodec + +if sys.platform in ('iOS', 'tvos', 'watchos'): + allows_subprocesses = False +else: + allows_subprocesses = True + + # Supply spawn*() (probably only for Unix) if _exists("fork") and not _exists("spawnv") and _exists("execv"): diff --git a/Lib/platform.py b/Lib/platform.py index 6258827d0e..af32c6c70c 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -613,7 +613,7 @@ default in case the command should fail. """ - if sys.platform in ('dos', 'win32', 'win16'): + if sys.platform in ('dos', 'win32', 'win16', 'ios', 'tvos', 'watchos'): # XXX Others too ? return default diff --git a/Lib/site.py b/Lib/site.py index 9e617afb00..41305298d3 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -271,6 +271,9 @@ if sys.platform == 'darwin' and sys._framework: return f'{userbase}/lib/python/site-packages' + elif sys.platform in ('ios', 'tvos', 'watchos'): + from sysconfig import get_path + return get_path('purelib', sys.platform) return f'{userbase}/lib/python{version[0]}.{version[1]}/site-packages' diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 0311e3a1f8..ca01e9aa16 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -762,6 +762,9 @@ pass_fds=(), *, user=None, group=None, extra_groups=None, encoding=None, errors=None, text=None, umask=-1): """Create new Popen instance.""" + if not os.allows_subprocesses: + raise RuntimeError("Subprocesses are not supported on this platform.") + _cleanup() # Held while anything is calling waitpid before returncode has been # updated to prevent clobbering returncode if wait() or poll() are @@ -1834,7 +1837,7 @@ else: self.returncode = waitstatus_to_exitcode(sts) - def _internal_poll(self, _deadstate=None, _waitpid=os.waitpid, + def _internal_poll(self, _deadstate=None, _waitpid=None, _WNOHANG=os.WNOHANG, _ECHILD=errno.ECHILD): """Check if child process has terminated. Returns returncode attribute. @@ -1843,6 +1846,8 @@ outside of the local scope (nor can any methods it calls). """ + if _waitpid is None: + _waitpid = os.waitpid if self.returncode is None: if not self._waitpid_lock.acquire(False): # Something else is busy calling waitpid. Don't allow two diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py index e3f79bfde5..2850eaeb5e 100644 --- a/Lib/sysconfig.py +++ b/Lib/sysconfig.py @@ -84,6 +84,33 @@ 'scripts': '{userbase}/bin', 'data': '{userbase}', }, + 'ios': { + 'stdlib': '{installed_base}/lib/python%s' % sys.version[:3], + 'platstdlib': '{installed_base}/lib/python%s' % sys.version[:3], + 'purelib': '{installed_base}/lib/python%s/site-packages' % sys.version[:3], + 'platlib': '{installed_base}/lib/python%s/site-packages' % sys.version[:3], + 'include': '{installed_base}/include', + 'scripts': '{installed_base}/bin', + 'data': '{installed_base}/Resources', + }, + 'tvos': { + 'stdlib': '{installed_base}/lib/python%s' % sys.version[:3], + 'platstdlib': '{installed_base}/lib/python%s' % sys.version[:3], + 'purelib': '{installed_base}/lib/python%s/site-packages' % sys.version[:3], + 'platlib': '{installed_base}/lib/python%s/site-packages' % sys.version[:3], + 'include': '{installed_base}/include', + 'scripts': '{installed_base}/bin', + 'data': '{installed_base}/Resources', + }, + 'watchos': { + 'stdlib': '{installed_base}/lib/python%s' % sys.version[:3], + 'platstdlib': '{installed_base}/lib/python%s' % sys.version[:3], + 'purelib': '{installed_base}/lib/python%s/site-packages' % sys.version[:3], + 'platlib': '{installed_base}/lib/python%s/site-packages' % sys.version[:3], + 'include': '{installed_base}/include', + 'scripts': '{installed_base}/bin', + 'data': '{installed_base}/Resources', + }, } _SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include', @@ -185,6 +212,8 @@ if os.name == 'posix': # the default scheme for posix is posix_prefix return 'posix_prefix' + elif sys.platform in ('ios', 'tvos', 'watchos'): + return sys.platform return os.name @@ -205,6 +234,11 @@ if sys.platform == "darwin" and sys._framework: return joinuser("~", "Library", sys._framework, "%d.%d" % sys.version_info[:2]) + elif sys.platform in ('ios', 'tvos', 'watchos'): + if env_base: + return env_base + else: + return _BASE_PREFIX return joinuser("~", ".local") diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index aee3737ffc..b7060d9d1a 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -782,8 +782,8 @@ # TESTFN_UNICODE is a non-ascii filename TESTFN_UNICODE = TESTFN_ASCII + "-\xe0\xf2\u0258\u0141\u011f" -if sys.platform == 'darwin': - # In Mac OS X's VFS API file names are, by definition, canonically +if sys.platform in ('darwin', 'ios', 'tvos', 'watchos'): + # In iOS and Mac OS X, VFS API file names are, by definition, canonically # decomposed Unicode, encoded using UTF-8. See QA1173: # http://developer.apple.com/mac/library/qa/qa2001/qa1173.html import unicodedata @@ -809,8 +809,8 @@ 'Unicode filename tests may not be effective' % (TESTFN_UNENCODABLE, TESTFN_ENCODING)) TESTFN_UNENCODABLE = None -# Mac OS X denies unencodable filenames (invalid utf-8) -elif sys.platform != 'darwin': +# Mac OS X and iOS deny unencodable filenames (invalid utf-8) +elif sys.platform not in ('darwin', 'ios', 'tvos', 'watchos'): try: # ascii and utf-8 cannot encode the byte 0xff b'\xff'.decode(TESTFN_ENCODING) diff --git a/Lib/test/support/script_helper.py b/Lib/test/support/script_helper.py index 37e576d4a7..1d7176bf13 100644 --- a/Lib/test/support/script_helper.py +++ b/Lib/test/support/script_helper.py @@ -8,6 +8,7 @@ import os.path import subprocess import py_compile +import unittest import zipfile from importlib.util import source_from_cache @@ -17,6 +18,8 @@ # Cached result of the expensive test performed in the function below. __cached_interp_requires_environment = None + +@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def interpreter_requires_environment(): """ Returns True if our sys.executable interpreter requires environment @@ -136,6 +139,7 @@ rc = proc.returncode return _PythonRunResult(rc, out, err), cmd_line +@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def _assert_python(expected_success, /, *args, **env_vars): res, cmd_line = run_python_until_end(*args, **env_vars) if (res.rc and expected_success) or (not res.rc and not expected_success): @@ -165,6 +169,8 @@ """ return _assert_python(False, *args, **env_vars) + +@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def spawn_python(*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kw): """Run a Python subprocess with the given arguments. diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index fa6c49d89d..1e3db27552 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -516,6 +516,8 @@ self._basetest_create_connection(conn_fut) @socket_helper.skip_unless_bind_unix_socket + @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_connection(self): # Issue #20682: On Mac OS X Tiger, getsockname() returns a # zero-length address for UNIX socket. @@ -608,6 +610,8 @@ self.assertEqual(cm.exception.reason, 'CERTIFICATE_VERIFY_FAILED') @unittest.skipIf(ssl is None, 'No ssl module') + @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_ssl_connection(self): with test_utils.run_test_server(use_ssl=True) as httpd: create_connection = functools.partial( @@ -619,6 +623,8 @@ @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') + @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_ssl_unix_connection(self): # Issue #20682: On Mac OS X Tiger, getsockname() returns a # zero-length address for UNIX socket. @@ -842,6 +848,8 @@ return server, path @socket_helper.skip_unless_bind_unix_socket + @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server(self): proto = MyProto(loop=self.loop) server, path = self._make_unix_server(lambda: proto) @@ -870,6 +878,8 @@ server.close() @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'No UNIX Sockets') + @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server_path_socket_error(self): proto = MyProto(loop=self.loop) sock = socket.socket() @@ -935,6 +945,8 @@ @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') + @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server_ssl(self): proto = MyProto(loop=self.loop) server, path = self._make_ssl_unix_server( @@ -965,6 +977,8 @@ server.close() @unittest.skipIf(ssl is None, 'No ssl module') + @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_server_ssl_verify_failed(self): proto = MyProto(loop=self.loop) server, host, port = self._make_ssl_server( @@ -995,6 +1009,8 @@ @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') + @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server_ssl_verify_failed(self): proto = MyProto(loop=self.loop) server, path = self._make_ssl_unix_server( @@ -1055,6 +1071,8 @@ @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') + @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server_ssl_verified(self): proto = MyProto(loop=self.loop) server, path = self._make_ssl_unix_server( @@ -1714,6 +1732,7 @@ next(it) +@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') class SubprocessTestsMixin: def check_terminated(self, returncode): diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py index 71de82dfc7..53d5b774a5 100644 --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py @@ -5,6 +5,7 @@ import queue import pickle import socket +import subprocess import sys import threading import unittest @@ -67,6 +68,8 @@ self._basetest_open_connection(conn_fut) @socket_helper.skip_unless_bind_unix_socket + @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't fully support UNIX sockets." % sys.platform) def test_open_unix_connection(self): with test_utils.run_test_unix_server() as httpd: conn_fut = asyncio.open_unix_connection(httpd.address, @@ -101,6 +104,8 @@ @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') + @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't fully support UNIX sockets." % sys.platform) def test_open_unix_connection_no_loop_ssl(self): with test_utils.run_test_unix_server(use_ssl=True) as httpd: conn_fut = asyncio.open_unix_connection( @@ -131,6 +136,8 @@ self._basetest_open_connection_error(conn_fut) @socket_helper.skip_unless_bind_unix_socket + @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't fully support UNIX sockets." % sys.platform) def test_open_unix_connection_error(self): with test_utils.run_test_unix_server() as httpd: conn_fut = asyncio.open_unix_connection(httpd.address, @@ -656,6 +663,8 @@ self.assertEqual(messages, []) @socket_helper.skip_unless_bind_unix_socket + @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't fully support UNIX sockets." % sys.platform) def test_start_unix_server(self): class MyServer: @@ -732,6 +741,7 @@ self.assertEqual(messages, []) @unittest.skipIf(sys.platform == 'win32', "Don't have pipes") + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_read_all_from_pipe_reader(self): # See asyncio issue 168. This test is derived from the example # subprocess_attach_read_pipe.py, but we configure the diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index 6657a88e65..27555ec15e 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -103,6 +103,7 @@ transport.close() +@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') class SubprocessMixin: def test_stdin_stdout(self): diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py index 10bd46dea1..90e80ef851 100644 --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py @@ -267,6 +267,8 @@ @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'UNIX Sockets are not supported') +@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't fully support UNIX sockets." % sys.platform) class SelectorEventLoopUnixSocketTests(test_utils.TestCase): def setUp(self): @@ -1134,6 +1136,7 @@ )) +@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') class ChildWatcherTestsMixin: ignore_warnings = mock.patch.object(log.logger, "warning") diff --git a/Lib/test/test_base64.py b/Lib/test/test_base64.py index 1dbeac41dc..73e63d1c31 100644 --- a/Lib/test/test_base64.py +++ b/Lib/test/test_base64.py @@ -644,7 +644,7 @@ def test_ErrorHeritage(self): self.assertTrue(issubclass(binascii.Error, ValueError)) - +@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') class TestMain(unittest.TestCase): def tearDown(self): if os.path.exists(support.TESTFN): diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index 5150d5770f..6e0f0bfd5e 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -54,6 +54,7 @@ self.assertEqual(testfunction.attribute, "test") self.assertRaises(AttributeError, setattr, inst.testfunction, "attribute", "test") + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_no_FatalError_infinite_loop(self): with support.SuppressCrashReport(): p = subprocess.Popen([sys.executable, "-c", @@ -669,6 +670,7 @@ self.assertFalse(hasattr(binascii.Error, "foobar")) +@unittest.skipUnless(threading, 'Threading required for this test.') class TestThreadState(unittest.TestCase): @support.reap_threads diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index a3560b4b1f..082f74ce76 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -67,6 +67,7 @@ rc, out, err = assert_python_ok('-vv') self.assertNotIn(b'stack overflow', err) + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') @unittest.skipIf(interpreter_requires_environment(), 'Cannot run -E tests when PYTHON env vars are required.') def test_xoptions(self): @@ -85,6 +86,7 @@ opts = get_xoptions('-Xa', '-Xb=c,d=e') self.assertEqual(opts, {'a': True, 'b': 'c,d=e'}) + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_showrefcount(self): def run_python(*args): # this is similar to assert_python_ok but doesn't strip @@ -158,6 +160,7 @@ # arguments as unicode (using wmain() instead of main()). @unittest.skipIf(sys.platform == 'win32', 'Windows has a native unicode API') + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_undecodable_code(self): undecodable = b"\xff" env = os.environ.copy() @@ -273,6 +276,7 @@ 'False False False\n' 'False False True\n') + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_unbuffered_output(self): # Test expected operation of the '-u' switch for stream in ('stdout', 'stderr'): @@ -331,6 +335,7 @@ # for empty and unset PYTHONPATH self.assertEqual(out1, out2) + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_displayhook_unencodable(self): for encoding in ('ascii', 'latin-1', 'utf-8'): env = os.environ.copy() @@ -349,6 +354,7 @@ escaped = repr(text).encode(encoding, 'backslashreplace') self.assertIn(escaped, data) + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def check_input(self, code, expected): with tempfile.NamedTemporaryFile("wb+") as stdin: sep = os.linesep.encode('ASCII') @@ -424,6 +430,7 @@ @unittest.skipIf(os.name != 'posix', "test needs POSIX semantics") @unittest.skipIf(sys.platform == "vxworks", "test needs preexec support in subprocess.Popen") + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def _test_no_stdio(self, streams): code = """if 1: import os, sys diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py index 3bbc6817f8..75c479cc3b 100644 --- a/Lib/test/test_compileall.py +++ b/Lib/test/test_compileall.py @@ -192,7 +192,8 @@ compileall.compile_dir(self.directory, quiet=True, workers=5) self.assertTrue(pool_mock.called) - def test_compile_workers_non_positive(self): + @mock.patch('compileall.ProcessPoolExecutor') + def test_compile_workers_non_positive(self, pool_mock): with self.assertRaisesRegex(ValueError, "workers must be greater or equal to 0"): compileall.compile_dir(self.directory, workers=-1) diff --git a/Lib/test/test_concurrent_futures.py b/Lib/test/test_concurrent_futures.py index 4fe4bf7927..2b1cf4136c 100644 --- a/Lib/test/test_concurrent_futures.py +++ b/Lib/test/test_concurrent_futures.py @@ -154,6 +154,7 @@ executor_type = futures.ThreadPoolExecutor +@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') class ProcessPoolForkMixin(ExecutorMixin): executor_type = futures.ProcessPoolExecutor ctx = "fork" @@ -164,11 +165,13 @@ return super().get_context() +@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') class ProcessPoolSpawnMixin(ExecutorMixin): executor_type = futures.ProcessPoolExecutor ctx = "spawn" +@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') class ProcessPoolForkserverMixin(ExecutorMixin): executor_type = futures.ProcessPoolExecutor ctx = "forkserver" diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index 3efe5dafc2..087207cac7 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -2823,7 +2823,12 @@ TestResults(failed=1, attempted=1) """ -def test_CLI(): r""" +if sys.platform in ('iOS', 'tvos', 'watchos'): + # Mobile platforms can't invoke doctest from the command line, + # so skip this test. + pass +else: + def test_CLI(): r""" The doctest module can be used to run doctests against an arbitrary file. These tests test this CLI functionality. diff --git a/Lib/test/test_eintr.py b/Lib/test/test_eintr.py index a5f8f6465e..00287fea2b 100644 --- a/Lib/test/test_eintr.py +++ b/Lib/test/test_eintr.py @@ -9,6 +9,7 @@ @unittest.skipUnless(os.name == "posix", "only supported on Unix") +@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') class EINTRTests(unittest.TestCase): @unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()") diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index c64afe88c2..2e57075f6b 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -53,6 +53,7 @@ finally: support.unlink(filename) + class FaultHandlerTests(unittest.TestCase): def get_output(self, code, filename=None, fd=None): """ @@ -352,6 +353,7 @@ finally: sys.stderr = orig_stderr + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_disabled_by_default(self): # By default, the module should be disabled code = "import faulthandler; print(faulthandler.is_enabled())" @@ -360,6 +362,7 @@ output = subprocess.check_output(args) self.assertEqual(output.rstrip(), b"False") + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_sys_xoptions(self): # Test python -X faulthandler code = "import faulthandler; print(faulthandler.is_enabled())" @@ -372,6 +375,7 @@ output = subprocess.check_output(args, env=env) self.assertEqual(output.rstrip(), b"True") + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_env_var(self): # empty env var code = "import faulthandler; print(faulthandler.is_enabled())" diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py index 9ab68c6724..4f271dd655 100644 --- a/Lib/test/test_fcntl.py +++ b/Lib/test/test_fcntl.py @@ -23,7 +23,7 @@ start_len = "qq" if (sys.platform.startswith(('netbsd', 'freebsd', 'openbsd')) - or sys.platform == 'darwin'): + or sys.platform in ('darwin', 'ios', 'tvos', 'watchos')): if struct.calcsize('l') == 8: off_t = 'l' pid_t = 'i' diff --git a/Lib/test/test_file_eintr.py b/Lib/test/test_file_eintr.py index f1efd266ff..db6d3ec5ad 100644 --- a/Lib/test/test_file_eintr.py +++ b/Lib/test/test_file_eintr.py @@ -69,6 +69,7 @@ self.fail('Error from IO process %s:\nSTDOUT:\n%sSTDERR:\n%s\n' % (why, stdout.decode(), stderr.decode())) + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def _test_reading(self, data_to_write, read_and_verify_code): """Generic buffered read method test harness to validate EINTR behavior. diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py index 38c9cb7123..c9d8d90ce0 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -680,6 +680,8 @@ del x gc.set_debug(%s) """ + + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def run_command(code): p = subprocess.Popen([sys.executable, "-Wd", "-c", code], stdout=subprocess.PIPE, diff --git a/Lib/test/test_gdb.py b/Lib/test/test_gdb.py index 22c75bae98..49c44f3de0 100644 --- a/Lib/test/test_gdb.py +++ b/Lib/test/test_gdb.py @@ -33,6 +33,8 @@ # This is what "no gdb" looks like. There may, however, be other # errors that manifest this way too. raise unittest.SkipTest("Couldn't find gdb on the path") + except RuntimeError: + raise unittest.SkipTest('Test requires support for subprocesses.') # Regex to parse: # 'GNU gdb (GDB; SUSE Linux Enterprise 12) 7.7\n' -> 7.7 diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 8df0b5251f..69912c4643 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -398,7 +398,7 @@ with open(os.path.join(self.tempdir, filename), 'wb') as f: f.write(support.TESTFN_UNDECODABLE) response = self.request(self.base_url + '/') - if sys.platform == 'darwin': + if sys.platform in ('darwin', 'ios', 'tvos', 'watchos'): # On Mac OS the HFS+ filesystem replaces bytes that aren't valid # UTF-8 into a percent-encoded value. for name in os.listdir(self.tempdir): @@ -598,6 +598,7 @@ @unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0, "This test can't be run reliably as root (issue #13308).") +@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') class CGIHTTPServerTestCase(BaseTestCase): class request_handler(NoLogRequestHandler, CGIHTTPRequestHandler): pass diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py index fe394dc50c..6974ae7ea8 100644 --- a/Lib/test/test_imp.py +++ b/Lib/test/test_imp.py @@ -18,7 +18,7 @@ """Decorator to skip a test if not running under CPython or lacking imp.load_dynamic().""" meth = support.cpython_only(meth) - return unittest.skipIf(not hasattr(imp, 'load_dynamic'), + return unittest.skipIf(not getattr(imp, 'load_dynamic'), 'imp.load_dynamic() required')(meth) diff --git a/Lib/test/test_importlib/extension/test_finder.py b/Lib/test/test_importlib/extension/test_finder.py index c9b4a3772c..1d87c8df65 100644 --- a/Lib/test/test_importlib/extension/test_finder.py +++ b/Lib/test/test_importlib/extension/test_finder.py @@ -3,10 +3,13 @@ machinery = util.import_importlib('importlib.machinery') +import sys import unittest import warnings +@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + '%s does not support dynamic loading' % sys.platform) class FinderTests(abc.FinderTests): """Test the finder for extension modules.""" diff --git a/Lib/test/test_importlib/extension/test_loader.py b/Lib/test/test_importlib/extension/test_loader.py index abd612fcd9..ebf7bc9aa5 100644 --- a/Lib/test/test_importlib/extension/test_loader.py +++ b/Lib/test/test_importlib/extension/test_loader.py @@ -11,6 +11,8 @@ import importlib from test.support.script_helper import assert_python_failure +@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + '%s does not support dynamic loading' % sys.platform) class LoaderTests(abc.LoaderTests): """Test load_module() for extension modules.""" @@ -82,6 +84,9 @@ Source_LoaderTests ) = util.test_both(LoaderTests, machinery=machinery) + +@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + '%s does not support dynamic loading' % sys.platform) class MultiPhaseExtensionModuleTests(abc.LoaderTests): """Test loading extension modules with multi-phase initialization (PEP 489) """ diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 1f0210f857..3c689d7493 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -602,7 +602,7 @@ # On Windows and Mac OSX this test consumes large resources; It takes # a long time to build the >2 GiB file and takes >2 GiB of disk space # therefore the resource must be enabled to run this test. - if sys.platform[:3] == 'win' or sys.platform == 'darwin': + if sys.platform[:3] == 'win' or sys.platform in ('darwin', 'ios', 'tvos', 'watchos'): support.requires( 'largefile', 'test requires %s bytes and a long time to run' % self.LARGE) diff --git a/Lib/test/test_json/test_tool.py b/Lib/test/test_json/test_tool.py index fc2a7a4fca..0871b05ac8 100644 --- a/Lib/test/test_json/test_tool.py +++ b/Lib/test/test_json/test_tool.py @@ -84,6 +84,7 @@ } """) + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_stdin_stdout(self): args = sys.executable, '-m', 'json.tool' process = subprocess.run(args, input=self.data, capture_output=True, text=True, check=True) diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index a6cd291c9a..2ad76905b8 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -1764,9 +1764,21 @@ # just need a name - file can't be present, or we'll get an # 'address already in use' error. os.remove(fn) + # Check the size of the socket file name. If it exceeds 108 + # characters (UNIX_PATH_MAX), it can't be used as a UNIX socket. + # In this case, fall back to a path constructed somewhere that + # is known to be short. + if len(fn) > 108: + fd, fn = tempfile.mkstemp(prefix='test_logging_', suffix='.sock', dir='/tmp') + os.close(fd) + # just need a name - file can't be present, or we'll get an + # 'address already in use' error. + os.remove(fn) return fn @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") +@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't fully support UNIX sockets." % sys.platform) class UnixSocketHandlerTest(SocketHandlerTest): """Test for SocketHandler with unix sockets.""" @@ -1848,6 +1860,8 @@ self.assertEqual(self.log_output, "spam\neggs\n") @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") +@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't fully support UNIX sockets." % sys.platform) class UnixDatagramHandlerTest(DatagramHandlerTest): """Test for DatagramHandler using Unix sockets.""" @@ -1932,6 +1946,8 @@ self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m') @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") +@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't fully support UNIX sockets." % sys.platform) class UnixSysLogHandlerTest(SysLogHandlerTest): """Test for SysLogHandler with Unix sockets.""" diff --git a/Lib/test/test_mailcap.py b/Lib/test/test_mailcap.py index c08423c670..9673b07df3 100644 --- a/Lib/test/test_mailcap.py +++ b/Lib/test/test_mailcap.py @@ -1,6 +1,7 @@ import mailcap import os import copy +import sys import test.support import unittest @@ -212,7 +213,8 @@ ] self._run_cases(cases) - @unittest.skipUnless(os.name == "posix", "Requires 'test' command on system") + @unittest.skipUnless(os.name == "posix" and sys.platform not in('ios', 'tvos', 'watchos'), + "Requires 'test' command on system") def test_test(self): # findmatch() will automatically check any "test" conditions and skip # the entry if the check fails. diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py index ace1593999..69cc2cd06a 100644 --- a/Lib/test/test_marshal.py +++ b/Lib/test/test_marshal.py @@ -240,7 +240,10 @@ if os.name == 'nt': MAX_MARSHAL_STACK_DEPTH = 1000 else: - MAX_MARSHAL_STACK_DEPTH = 2000 + if sys.platform in ('ios', 'tvos', 'watchos'): + MAX_MARSHAL_STACK_DEPTH = 1500 + else: + MAX_MARSHAL_STACK_DEPTH = 2000 for i in range(MAX_MARSHAL_STACK_DEPTH - 2): last.append([0]) last = last[-1] diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py index 5400f25f50..5c7fb278ea 100644 --- a/Lib/test/test_mmap.py +++ b/Lib/test/test_mmap.py @@ -230,7 +230,7 @@ with open(TESTFN, "r+b") as f: self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize, access=4) - if os.name == "posix": + if os.name == "posix" and sys.platform not in ('iOS', 'tvos', 'watchos'): # Try incompatible flags, prot and access parameters. with open(TESTFN, "r+b") as f: self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize, @@ -805,7 +805,7 @@ unlink(TESTFN) def _make_test_file(self, num_zeroes, tail): - if sys.platform[:3] == 'win' or sys.platform == 'darwin': + if sys.platform[:3] == 'win' or sys.platform in ('darwin', 'ios', 'tvos', 'watchos'): requires('largefile', 'test requires %s bytes and a long time to run' % str(0x180000000)) f = open(TESTFN, 'w+b') diff --git a/Lib/test/test_multiprocessing_fork.py b/Lib/test/test_multiprocessing_fork.py index 5000edb7c5..5d1d2917bf 100644 --- a/Lib/test/test_multiprocessing_fork.py +++ b/Lib/test/test_multiprocessing_fork.py @@ -1,4 +1,9 @@ +import os import unittest + +if not os.allows_subprocesses: + raise unittest.SkipTest('Test requires support for subprocesses.') + import test._test_multiprocessing import sys diff --git a/Lib/test/test_multiprocessing_forkserver.py b/Lib/test/test_multiprocessing_forkserver.py index 6ad5faf9e8..4e05efb6df 100644 --- a/Lib/test/test_multiprocessing_forkserver.py +++ b/Lib/test/test_multiprocessing_forkserver.py @@ -1,4 +1,9 @@ +import os import unittest + +if not os.allows_subprocesses: + raise unittest.SkipTest('Test requires support for subprocesses.') + import test._test_multiprocessing import sys diff --git a/Lib/test/test_multiprocessing_spawn.py b/Lib/test/test_multiprocessing_spawn.py index 6558952308..a121e8c2dd 100644 --- a/Lib/test/test_multiprocessing_spawn.py +++ b/Lib/test/test_multiprocessing_spawn.py @@ -1,4 +1,9 @@ +import os import unittest + +if not os.allows_subprocesses: + raise unittest.SkipTest('Test requires support for subprocesses.') + import test._test_multiprocessing from test import support diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 35933e9c8c..c0c902162f 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -854,6 +854,7 @@ # Bug 1110478 @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), 'requires a shell') + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_update2(self): os.environ.clear() os.environ.update(HELLO="World") @@ -863,6 +864,7 @@ @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), 'requires a shell') + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_os_popen_iter(self): with os.popen("%s -c 'echo \"line1\nline2\nline3\"'" % unix_shell) as popen: @@ -1814,6 +1816,7 @@ @unittest.skipUnless(hasattr(os, 'execv'), "need os.execv()") +@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') class ExecTests(unittest.TestCase): @unittest.skipIf(USING_LINUXTHREADS, "avoid triggering a linuxthreads bug: see issue #4970") @@ -2135,6 +2138,7 @@ self.assertRaises(OverflowError, os.setreuid, 0, self.UID_OVERFLOW) @unittest.skipUnless(hasattr(os, 'setreuid'), 'test needs os.setreuid()') + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_setreuid_neg1(self): # Needs to accept -1. We run this in a subprocess to avoid # altering the test runner's process state (issue8045). @@ -2143,6 +2147,7 @@ 'import os,sys;os.setreuid(-1,-1);sys.exit(0)']) @unittest.skipUnless(hasattr(os, 'setregid'), 'test needs os.setregid()') + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_setregid(self): if os.getuid() != 0 and not HAVE_WHEEL_GROUP: self.assertRaises(OSError, os.setregid, 0, 0) @@ -2152,6 +2157,7 @@ self.assertRaises(OverflowError, os.setregid, 0, self.GID_OVERFLOW) @unittest.skipUnless(hasattr(os, 'setregid'), 'test needs os.setregid()') + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_setregid_neg1(self): # Needs to accept -1. We run this in a subprocess to avoid # altering the test runner's process state (issue8045). @@ -2782,6 +2788,7 @@ class PidTests(unittest.TestCase): @unittest.skipUnless(hasattr(os, 'getppid'), "test needs os.getppid") + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_getppid(self): p = subprocess.Popen([sys.executable, '-c', 'import os; print(os.getppid())'], @@ -2808,6 +2815,9 @@ self.assertEqual(os.waitstatus_to_exitcode(status), exitcode) self.assertEqual(pid2, pid) + @unittest.skipUnless(hasattr(os, 'spawnv'), "test needs os.spawnv") + @unittest.skipUnless(hasattr(os, 'waitpid'), "test needs os.waitpid") + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_waitpid(self): self.check_waitpid(code='pass', exitcode=0) @@ -3477,6 +3487,7 @@ self.assertGreaterEqual(size.columns, 0) self.assertGreaterEqual(size.lines, 0) + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_stty_match(self): """Check if stty returns the same results diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 0da449e33e..212930ec25 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -1231,6 +1231,7 @@ def tearDown(self): support.unlink(support.TESTFN) + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def _run_pdb(self, pdb_args, commands): self.addCleanup(support.rmtree, '__pycache__') cmd = [sys.executable, '-m', 'pdb'] + pdb_args @@ -1323,6 +1324,7 @@ ('bœr', 1), ) + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_issue7964(self): # open the file as binary so we can force \r\n newline with open(support.TESTFN, 'wb') as f: diff --git a/Lib/test/test_pipes.py b/Lib/test/test_pipes.py index 1d538b9898..3c09f7a603 100644 --- a/Lib/test/test_pipes.py +++ b/Lib/test/test_pipes.py @@ -1,12 +1,15 @@ import pipes import os import string +import sys import unittest import shutil from test.support import TESTFN, run_unittest, unlink, reap_children if os.name != 'posix': raise unittest.SkipTest('pipes module only works on posix') +if sys.platform in ('ios', 'tvos', 'watchos'): + raise unittest.SkipTest('pipes tests cannot run on %s' % sys.platform) TESTFN2 = TESTFN + "2" diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index 9f04c79e09..8c92a660ed 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -9,7 +9,6 @@ from test import support - class PlatformTest(unittest.TestCase): def clear_caches(self): platform._platform_cache.clear() @@ -20,6 +19,7 @@ res = platform.architecture() @support.skip_unless_symlink + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_architecture_via_symlink(self): # issue3762 with support.PythonSymlink() as py: cmd = "-c", "import platform; print(platform.architecture())" @@ -254,7 +254,7 @@ def test_mac_ver(self): res = platform.mac_ver() - if platform.uname().system == 'Darwin': + if platform.uname().system == 'Darwin' and sys.platform not in ('ios', 'tvos', 'watchos'): # We are on a macOS system, check that the right version # information is returned output = subprocess.check_output(['sw_vers'], text=True) diff --git a/Lib/test/test_poll.py b/Lib/test/test_poll.py index ef966bf0f5..6484e65b7f 100644 --- a/Lib/test/test_poll.py +++ b/Lib/test/test_poll.py @@ -117,6 +117,7 @@ # Another test case for poll(). This is copied from the test case for # select(), modified to use poll() instead. + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_poll2(self): cmd = 'for i in 0 1 2 3 4 5 6 7 8 9; do echo testing...; sleep 1; done' proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, diff --git a/Lib/test/test_popen.py b/Lib/test/test_popen.py index ab1bc77655..60be660d25 100644 --- a/Lib/test/test_popen.py +++ b/Lib/test/test_popen.py @@ -16,6 +16,8 @@ if ' ' in python: python = '"' + python + '"' # quote embedded space for cmdline + +@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') class PopenTest(unittest.TestCase): def _do_test_commandline(self, cmdline, expected): diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index f4edb8bd95..12ead59a8a 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -60,15 +60,21 @@ # no side-effects which we need to cleanup (e.g., fork, wait, abort) NO_ARG_FUNCTIONS = [ "ctermid", "getcwd", "getcwdb", "uname", "times", "getloadavg", - "getegid", "geteuid", "getgid", "getgroups", + "getegid", "geteuid", "getgid", "getpid", "getpgrp", "getppid", "getuid", "sync", ] + if sys.platform not in ('ios', 'tvos', 'watchos'): + NO_ARG_FUNCTIONS.append("getgroups") + for name in NO_ARG_FUNCTIONS: posix_func = getattr(posix, name, None) if posix_func is not None: - posix_func() - self.assertRaises(TypeError, posix_func, 1) + try: + posix_func() + self.assertRaises(TypeError, posix_func, 1) + except Exception as e: + self.fail('Problem invoking %s: %s' % (name, e)) @unittest.skipUnless(hasattr(posix, 'getresuid'), 'test needs posix.getresuid()') @@ -744,9 +750,10 @@ check_stat(uid, gid) self.assertRaises(OSError, chown_func, first_param, 0, -1) check_stat(uid, gid) - if 0 not in os.getgroups(): - self.assertRaises(OSError, chown_func, first_param, -1, 0) - check_stat(uid, gid) + if hasattr(os, 'getgroups') and sys.platform not in ('ios', 'tvos', 'watchos'): + if 0 not in os.getgroups(): + self.assertRaises(OSError, chown_func, first_param, -1, 0) + check_stat(uid, gid) # test illegal types for t in str, float: self.assertRaises(TypeError, chown_func, first_param, t(uid), gid) @@ -1019,6 +1026,7 @@ os.chdir(curdir) support.rmtree(base_path) + @unittest.skipUnless(hasattr(posix, 'getgrouplist'), "test needs posix.getgrouplist()") @unittest.skipUnless(hasattr(pwd, 'getpwuid'), "test needs pwd.getpwuid()") @unittest.skipUnless(hasattr(os, 'getuid'), "test needs os.getuid()") @@ -1027,8 +1035,8 @@ group = pwd.getpwuid(os.getuid())[3] self.assertIn(group, posix.getgrouplist(user, group)) - @unittest.skipUnless(hasattr(os, 'getegid'), "test needs os.getegid()") + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_getgroups(self): with os.popen('id -G 2>/dev/null') as idg: groups = idg.read().strip() @@ -1296,7 +1304,7 @@ self.assertIsInstance(hi, int) self.assertGreaterEqual(hi, lo) # OSX evidently just returns 15 without checking the argument. - if sys.platform != "darwin": + if sys.platform not in ('darwin', 'ios', 'tvos', 'watchos'): self.assertRaises(OSError, posix.sched_get_priority_min, -23) self.assertRaises(OSError, posix.sched_get_priority_max, -23) diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py index 9c32467cbb..ce273f0b4d 100644 --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py @@ -138,6 +138,7 @@ # to ignore this signal. os.close(master_fd) + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_fork(self): debug("calling pty.fork()") pid, master_fd = pty.fork() diff --git a/Lib/test/test_quopri.py b/Lib/test/test_quopri.py index 715544c8a9..6e66178816 100644 --- a/Lib/test/test_quopri.py +++ b/Lib/test/test_quopri.py @@ -180,6 +180,7 @@ for p, e in self.HSTRINGS: self.assertEqual(quopri.decodestring(e, header=True), p) + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_scriptencode(self): (p, e) = self.STRINGS[-1] process = subprocess.Popen([sys.executable, "-mquopri"], @@ -196,6 +197,7 @@ self.assertEqual(cout[i], e[i]) self.assertEqual(cout, e) + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_scriptdecode(self): (p, e) = self.STRINGS[-1] process = subprocess.Popen([sys.executable, "-mquopri", "-d"], diff --git a/Lib/test/test_script_helper.py b/Lib/test/test_script_helper.py index 4ade2cbc0d..83ce283a04 100644 --- a/Lib/test/test_script_helper.py +++ b/Lib/test/test_script_helper.py @@ -1,5 +1,6 @@ """Unittests for test.support.script_helper. Who tests the test helper?""" +import os import subprocess import sys import os @@ -35,6 +36,7 @@ self.assertIn('import sys; sys.exit(0)', error_msg, msg='unexpected command line.') + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') @mock.patch('subprocess.Popen') def test_assert_python_isolated_when_env_not_required(self, mock_popen): with mock.patch.object(script_helper, @@ -53,6 +55,7 @@ self.assertIn('-I', popen_command) self.assertNotIn('-E', popen_command) # -I overrides this + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') @mock.patch('subprocess.Popen') def test_assert_python_not_isolated_when_env_is_required(self, mock_popen): """Ensure that -I is not passed when the environment is required.""" @@ -82,6 +85,7 @@ # Reset the private cached state. script_helper.__dict__['__cached_interp_requires_environment'] = None + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') @mock.patch('subprocess.check_call') def test_interpreter_requires_environment_true(self, mock_check_call): with mock.patch.dict(os.environ): @@ -91,6 +95,7 @@ self.assertTrue(script_helper.interpreter_requires_environment()) self.assertEqual(1, mock_check_call.call_count) + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') @mock.patch('subprocess.check_call') def test_interpreter_requires_environment_false(self, mock_check_call): with mock.patch.dict(os.environ): @@ -100,6 +105,7 @@ self.assertFalse(script_helper.interpreter_requires_environment()) self.assertEqual(1, mock_check_call.call_count) + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') @mock.patch('subprocess.check_call') def test_interpreter_requires_environment_details(self, mock_check_call): with mock.patch.dict(os.environ): diff --git a/Lib/test/test_select.py b/Lib/test/test_select.py index 458998a62f..577cf5f385 100644 --- a/Lib/test/test_select.py +++ b/Lib/test/test_select.py @@ -5,7 +5,8 @@ import unittest from test import support -@unittest.skipIf((sys.platform[:3]=='win'), + +@unittest.skipIf((sys.platform[:3] == 'win'), "can't easily test on this system") class SelectTestCase(unittest.TestCase): @@ -44,6 +45,7 @@ self.assertIsNot(r, x) self.assertIsNot(w, x) + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_select(self): cmd = 'for i in 0 1 2 3 4 5 6 7 8 9; do echo testing...; sleep 1; done' with os.popen(cmd) as p: diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index e378033acd..6b77a61a20 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -1681,6 +1681,8 @@ check_chown(dirname, uid, gid) +@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't support other executable." % sys.platform) class TestWhich(BaseTest, unittest.TestCase): def setUp(self): @@ -2548,6 +2550,7 @@ self.assertGreaterEqual(size.lines, 0) @unittest.skipUnless(os.isatty(sys.__stdout__.fileno()), "not on tty") + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') @unittest.skipUnless(hasattr(os, 'get_terminal_size'), 'need os.get_terminal_size()') def test_stty_match(self): diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index ffba139dbe..b41d185666 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -197,6 +197,7 @@ @unittest.skipUnless(site.ENABLE_USER_SITE, "requires access to PEP 370 " "user-site (site.ENABLE_USER_SITE)") + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_s_option(self): # (ncoghlan) Change this to use script_helper... usersite = site.USER_SITE @@ -387,6 +388,7 @@ """Restore sys.path""" sys.path[:] = self.sys_path + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_abs_paths(self): # Make sure all imported modules have their __file__ and __cached__ # attributes as absolute paths. Arranging to put the Lib directory on @@ -520,6 +522,7 @@ class StartupImportTests(unittest.TestCase): + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_startup_imports(self): # Get sys.path in isolated mode (python3 -I) popen = subprocess.Popen([sys.executable, '-I', '-c', diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index aefba4f397..fd3f1108cf 100755 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1033,6 +1033,12 @@ with self.assertRaises(OSError, msg=explanation): socket.gethostbyaddr(addr) + @unittest.skipUnless(socket.has_ipv6, "test needs IPv6 support") + def test_host_resolution_ipv6(self): + for addr in ['::1q', '::1::2', '1:1:1:1:1:1:1:1:1']: + self.assertRaises(OSError, socket.gethostbyname, addr) + self.assertRaises(OSError, socket.gethostbyaddr, addr) + @unittest.skipUnless(hasattr(socket, 'sethostname'), "test needs socket.sethostname()") @unittest.skipUnless(hasattr(socket, 'gethostname'), "test needs socket.gethostname()") def test_sethostname(self): @@ -1146,7 +1152,7 @@ # I've ordered this by protocols that have both a tcp and udp # protocol, at least for modern Linuxes. if (sys.platform.startswith(('freebsd', 'netbsd', 'gnukfreebsd')) - or sys.platform in ('linux', 'darwin')): + or sys.platform in ('linux', 'darwin', 'ios', 'tvos', 'watchos')): # avoid the 'echo' service on this platform, as there is an # assumption breaking non-standard port/protocol entry services = ('daytime', 'qotd', 'domain') @@ -3500,7 +3506,8 @@ def _testFDPassCMSG_LEN(self): self.createAndSendFDs(1) - @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958") + @unittest.skipIf(sys.platform in ("darwin", 'iOS', 'tvos', 'watchos'), + "skipping, see issue #12958") @unittest.skipIf(AIX, "skipping, see issue #22397") @requireAttrs(socket, "CMSG_SPACE") def testFDPassSeparate(self): @@ -3511,7 +3518,8 @@ maxcmsgs=2) @testFDPassSeparate.client_skip - @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958") + @unittest.skipIf(sys.platform in ("darwin", 'iOS', 'tvos', 'watchos'), + "skipping, see issue #12958") @unittest.skipIf(AIX, "skipping, see issue #22397") def _testFDPassSeparate(self): fd0, fd1 = self.newFDs(2) @@ -3524,7 +3532,8 @@ array.array("i", [fd1]))]), len(MSG)) - @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958") + @unittest.skipIf(sys.platform in ("darwin", 'iOS', 'tvos', 'watchos'), + "skipping, see issue #12958") @unittest.skipIf(AIX, "skipping, see issue #22397") @requireAttrs(socket, "CMSG_SPACE") def testFDPassSeparateMinSpace(self): @@ -3538,7 +3547,8 @@ maxcmsgs=2, ignoreflags=socket.MSG_CTRUNC) @testFDPassSeparateMinSpace.client_skip - @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958") + @unittest.skipIf(sys.platform in ("darwin", 'iOS', 'tvos', 'watchos'), + "skipping, see issue #12958") @unittest.skipIf(AIX, "skipping, see issue #22397") def _testFDPassSeparateMinSpace(self): fd0, fd1 = self.newFDs(2) @@ -3562,7 +3572,8 @@ nbytes = self.sendmsgToServer([msg]) self.assertEqual(nbytes, len(msg)) - @unittest.skipIf(sys.platform == "darwin", "see issue #24725") + @unittest.skipIf(sys.platform in ("darwin", 'iOS', 'tvos', 'watchos'), + "skipping, see issue #12958") def testFDPassEmpty(self): # Try to pass an empty FD array. Can receive either no array # or an empty array. @@ -4382,28 +4393,38 @@ pass @requireAttrs(socket.socket, "sendmsg") +@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX") class SendmsgUnixStreamTest(SendmsgStreamTests, SendrecvmsgUnixStreamTestBase): pass @requireAttrs(socket.socket, "recvmsg") +@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX") class RecvmsgUnixStreamTest(RecvmsgTests, RecvmsgGenericStreamTests, SendrecvmsgUnixStreamTestBase): pass @requireAttrs(socket.socket, "recvmsg_into") +@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX") class RecvmsgIntoUnixStreamTest(RecvmsgIntoTests, RecvmsgGenericStreamTests, SendrecvmsgUnixStreamTestBase): pass @requireAttrs(socket.socket, "sendmsg", "recvmsg") +@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX", "SOL_SOCKET", "SCM_RIGHTS") class RecvmsgSCMRightsStreamTest(SCMRightsTest, SendrecvmsgUnixStreamTestBase): pass @requireAttrs(socket.socket, "sendmsg", "recvmsg_into") +@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX", "SOL_SOCKET", "SCM_RIGHTS") class RecvmsgIntoSCMRightsStreamTest(RecvmsgIntoMixin, SCMRightsTest, SendrecvmsgUnixStreamTestBase): diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py index c9455adfd8..3ada09dcba 100644 --- a/Lib/test/test_socketserver.py +++ b/Lib/test/test_socketserver.py @@ -8,6 +8,7 @@ import select import signal import socket +import sys import tempfile import threading import unittest @@ -26,7 +27,7 @@ HAVE_UNIX_SOCKETS = hasattr(socket, "AF_UNIX") requires_unix_sockets = unittest.skipUnless(HAVE_UNIX_SOCKETS, 'requires Unix sockets') -HAVE_FORKING = hasattr(os, "fork") +HAVE_FORKING = hasattr(os, "fork") and os.allows_subprocesses requires_forking = unittest.skipUnless(HAVE_FORKING, 'requires forking') def signal_alarm(n): @@ -194,12 +195,16 @@ self.stream_examine) @requires_unix_sockets + @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't fully support UNIX sockets." % sys.platform) def test_UnixStreamServer(self): self.run_server(socketserver.UnixStreamServer, socketserver.StreamRequestHandler, self.stream_examine) @requires_unix_sockets + @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't fully support UNIX sockets." % sys.platform) def test_ThreadingUnixStreamServer(self): self.run_server(socketserver.ThreadingUnixStreamServer, socketserver.StreamRequestHandler, diff --git a/Lib/test/test_source_encoding.py b/Lib/test/test_source_encoding.py index 5ca43461d9..1a66b1d258 100644 --- a/Lib/test/test_source_encoding.py +++ b/Lib/test/test_source_encoding.py @@ -63,6 +63,7 @@ # two bytes in common with the UTF-8 BOM self.assertRaises(SyntaxError, eval, b'\xef\xbb\x20') + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_20731(self): sub = subprocess.Popen([sys.executable, os.path.join(os.path.dirname(__file__), diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index e8f9699ef7..d2f14cc6c9 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -41,6 +41,9 @@ mswindows = (sys.platform == "win32") +if not os.allows_subprocesses: + raise unittest.SkipTest('Test requires support for subprocesses.') + # # Depends on the following external programs: Python # diff --git a/Lib/test/test_sundry.py b/Lib/test/test_sundry.py index 2accad1aee..4824b6c8d0 100644 --- a/Lib/test/test_sundry.py +++ b/Lib/test/test_sundry.py @@ -20,7 +20,8 @@ import distutils.bcppcompiler import distutils.ccompiler - import distutils.cygwinccompiler + if sys.platform.startswith('win'): + import distutils.cygwinccompiler import distutils.filelist import distutils.text_file import distutils.unixccompiler diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index ed9b1770ab..ec3157d230 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -108,6 +108,7 @@ # Python/pythonrun.c::PyErr_PrintEx() is tricky. + class SysModuleTest(unittest.TestCase): def tearDown(self): @@ -271,6 +272,7 @@ # The error message is specific to CPython @test.support.cpython_only + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_recursionlimit_fatalerror(self): # A fatal error occurs if a second recursion limit is hit when recovering # from a first one. @@ -583,6 +585,7 @@ def test_clear_type_cache(self): sys._clear_type_cache() + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_ioencoding(self): env = dict(os.environ) @@ -630,6 +633,7 @@ 'requires OS support of non-ASCII encodings') @unittest.skipUnless(sys.getfilesystemencoding() == locale.getpreferredencoding(False), 'requires FS encoding to match locale') + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_ioencoding_nonascii(self): env = dict(os.environ) @@ -642,6 +646,7 @@ @unittest.skipIf(sys.base_prefix != sys.prefix, 'Test is not venv-compatible') + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_executable(self): # sys.executable should be absolute self.assertEqual(os.path.abspath(sys.executable), sys.executable) @@ -676,6 +681,7 @@ expected = None self.check_fsencoding(fs_encoding, expected) + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def c_locale_get_error_handler(self, locale, isolated=False, encoding=None): # Force the POSIX locale env = os.environ.copy() diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py index 0ca5c9390d..32430b41eb 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py @@ -229,10 +229,12 @@ def test_get_scheme_names(self): wanted = ('nt', 'nt_user', 'osx_framework_user', - 'posix_home', 'posix_prefix', 'posix_user') + 'posix_home', 'posix_prefix', 'posix_user', + 'tvos', 'watchos') self.assertEqual(get_scheme_names(), wanted) @skip_unless_symlink + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_symlink(self): # Issue 7880 with PythonSymlink() as py: cmd = "-c", "import sysconfig; print(sysconfig.get_platform())" diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index fcc706ede5..47836af2d8 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -19,7 +19,7 @@ has_textmode = (tempfile._text_openflags != tempfile._bin_openflags) -has_spawnl = hasattr(os, 'spawnl') +has_spawnl = hasattr(os, 'spawnl') and os.allows_subprocesses # TEST_FILES may need to be tweaked for systems depending on the maximum # number of files that can be opened at one time (see ulimit -n) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index c21cdf8eb7..5cc9808f82 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -986,6 +986,8 @@ os.set_blocking(r, False) return (r, w) + @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't have os.pipe" % sys.platform) def test_threads_join(self): # Non-daemon threads should be joined at subinterpreter shutdown # (issue #18808) @@ -1014,6 +1016,8 @@ # The thread was joined properly. self.assertEqual(os.read(r, 1), b"x") + @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), + "%s doesn't have os.pipe" % sys.platform) def test_threads_join_2(self): # Same as above, but a delay gets introduced after the thread's # Python code returned but before the thread state is deleted. diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 5bb3a58b2a..47f9b5e8f9 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -5,7 +5,9 @@ import linecache import sys import unittest +import os import re +import subprocess from test import support from test.support import TESTFN, Error, captured_output, unlink, cpython_only, ALWAYS_EQ from test.support.script_helper import assert_python_ok @@ -105,6 +107,7 @@ str_name = '.'.join([X.__module__, X.__qualname__]) self.assertEqual(err[0], "%s: %s\n" % (str_name, str_value)) + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_encoded_file(self): # Test that tracebacks are correctly printed for encoded source files: # - correct line number (Issue2384) diff --git a/Lib/test/test_unicodedata.py b/Lib/test/test_unicodedata.py index edfd860fd5..edbc8ce641 100644 --- a/Lib/test/test_unicodedata.py +++ b/Lib/test/test_unicodedata.py @@ -8,6 +8,7 @@ import hashlib from http.client import HTTPException +import os import sys import unicodedata import unittest @@ -225,6 +226,7 @@ class UnicodeMiscTest(UnicodeDatabaseTest): + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_failed_import_during_compiling(self): # Issue 4367 # Decoding \N escapes requires the unicodedata module. If it can't be diff --git a/Lib/test/test_urllib2net.py b/Lib/test/test_urllib2net.py index ba4c500e8e..1534b59cef 100644 --- a/Lib/test/test_urllib2net.py +++ b/Lib/test/test_urllib2net.py @@ -8,6 +8,10 @@ import urllib.error import urllib.request import sys +try: + import ssl +except ImportError: + ssl = None support.requires("network") @@ -158,6 +162,7 @@ ## self._test_urls(urls, self._extra_handlers()+[bauth, dauth]) + @unittest.skipIf(ssl is None, 'test requires ssl module') def test_urlwithfrag(self): urlwith_frag = "http://www.pythontest.net/index.html#frag" with socket_helper.transient_internet(urlwith_frag): @@ -166,6 +171,7 @@ self.assertEqual(res.geturl(), "http://www.pythontest.net/index.html#frag") + @unittest.skipIf(ssl is None, 'test requires ssl module') def test_redirect_url_withfrag(self): redirect_url_with_frag = "http://www.pythontest.net/redir/with_frag/" with socket_helper.transient_internet(redirect_url_with_frag): diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py index b1c92427dd..5141c9dece 100644 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -639,6 +639,7 @@ equal(str(u), v) @unittest.skipUnless(os.name == 'posix', 'requires Posix') + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def testIssue8621(self): # On at least some versions of OSX self.uuid.uuid4 generates # the same sequence of UUIDs in the parent and any @@ -824,6 +825,7 @@ @unittest.skipUnless(_uuid._ifconfig_getnode in _uuid._GETTERS, "ifconfig is not used for introspection on this platform") + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_ifconfig_getnode(self): node = self.uuid._ifconfig_getnode() self.check_node(node, 'ifconfig') @@ -836,6 +838,7 @@ @unittest.skipUnless(_uuid._arp_getnode in _uuid._GETTERS, "arp is not used for introspection on this platform") + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_arp_getnode(self): node = self.uuid._arp_getnode() self.check_node(node, 'arp') @@ -848,6 +851,7 @@ @unittest.skipUnless(_uuid._netstat_getnode in _uuid._GETTERS, "netstat is not used for introspection on this platform") + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_netstat_getnode(self): node = self.uuid._netstat_getnode() self.check_node(node, 'netstat') diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index ca003d55d7..e4ac9113f2 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -173,6 +173,7 @@ builder.upgrade_dependencies(fake_context) @requireVenvCreate + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_prefixes(self): """ Test that the prefix values are as expected. @@ -310,6 +311,7 @@ # point to the venv being used to run the test, and we lose the link # to the source build - so Python can't initialise properly. @requireVenvCreate + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_executable(self): """ Test that the sys.executable value is as expected. @@ -323,6 +325,7 @@ self.assertEqual(out.strip(), envpy.encode()) @unittest.skipUnless(can_symlink(), 'Needs symlinks') + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_executable_symlinks(self): """ Test that the sys.executable value is as expected. @@ -408,6 +411,7 @@ @requireVenvCreate class EnsurePipTest(BaseTest): """Test venv module installation of pip.""" + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def assert_pip_not_installed(self): envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe) @@ -532,6 +536,7 @@ # Issue #26610: pip/pep425tags.py requires ctypes @unittest.skipUnless(ctypes, 'pip requires ctypes') + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') @requires_zlib() def test_with_pip(self): self.do_test_with_pip(False) diff --git a/Lib/test/test_webbrowser.py b/Lib/test/test_webbrowser.py index 519a9432ab..63008a88fe 100644 --- a/Lib/test/test_webbrowser.py +++ b/Lib/test/test_webbrowser.py @@ -4,8 +4,11 @@ import sys import subprocess from unittest import mock +import sys from test import support +if sys.platform in ('ios', 'tvos', 'watchos'): + raise unittest.SkipTest("Can't run webbrowser tests on %s" % sys.platform) URL = 'http://www.example.com' CMD_NAME = 'test' diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index 999197a0e3..320992bdb1 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -1151,6 +1151,7 @@ self.skipTest('requires write access to the installed location') unlink(filename) + @unittest.skipIf(sys.dont_write_bytecode, "Test requires ability to write bytecode") def test_write_pyfile(self): self.requiresWriteAccess(os.path.dirname(__file__)) with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: @@ -1195,6 +1196,7 @@ self.assertCompiledIn('email/__init__.py', names) self.assertCompiledIn('email/mime/text.py', names) + @unittest.skipIf(sys.dont_write_bytecode, "Test requires ability to write bytecode") def test_write_filtered_python_package(self): import test packagedir = os.path.dirname(test.__file__) diff --git a/Lib/unittest/test/test_runner.py b/Lib/unittest/test/test_runner.py index dd9a1b6d9a..89a1ea93fb 100644 --- a/Lib/unittest/test/test_runner.py +++ b/Lib/unittest/test/test_runner.py @@ -934,6 +934,7 @@ expectedresult = (runner.stream, DESCRIPTIONS, VERBOSITY) self.assertEqual(runner._makeResult(), expectedresult) + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_warnings(self): """ Check that warnings argument of TextTestRunner correctly affects the diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py index 6023c1e138..ae5803c4c8 100755 --- a/Lib/webbrowser.py +++ b/Lib/webbrowser.py @@ -590,6 +590,57 @@ # what to do if _tryorder is now empty? +# +# Platform support for iOS +# +if sys.platform == 'ios': + class MobileSafari(BaseBrowser): + def open(self, url, new=0, autoraise=True): + # This code is the equivalent of: + # NSURL *nsurl = [NSURL URLWithString:url]; + # [[UIApplication sharedApplication] openURL:nsurl]; + from ctypes import cdll, c_void_p, c_char_p, c_uint32 + from ctypes import util + objc = cdll.LoadLibrary(util.find_library(b'objc')) + cf = cdll.LoadLibrary(util.find_library(b'CoreFoundation')) + objc.objc_getClass.restype = c_void_p + objc.objc_getClass.argtypes = [c_char_p] + objc.sel_registerName.restype = c_void_p + objc.sel_registerName.argtypes = [c_char_p] + cf.CFStringCreateWithCString.restype = c_void_p + cf.CFStringCreateWithCString.argtypes = [c_void_p, c_char_p, c_uint32] + + # Get an NSString describing the URL + kCFStringEncodingUTF8 = 0x08000100 + url = c_void_p(cf.CFStringCreateWithCString(None, url.encode('utf-8'), kCFStringEncodingUTF8)) + autorelease = c_void_p(objc.sel_registerName(b'autorelease')) + objc.objc_msgSend.argtypes = [c_void_p, c_void_p] + objc.objc_msgSend.restype = c_void_p + objc.objc_msgSend(url, autorelease) + + # Get an NSURL object representing the URL + NSURL = c_void_p(objc.objc_getClass(b'NSURL')) + urlWithString_ = c_void_p(objc.sel_registerName(b'URLWithString:')) + objc.objc_msgSend.restype = c_void_p + objc.objc_msgSend.argtypes = [c_void_p, c_void_p, c_void_p] + nsurl = c_void_p(objc.objc_msgSend(NSURL, urlWithString_, url)) + + # Get the shared UIApplication instance + UIApplication = c_void_p(objc.objc_getClass(b'UIApplication')) + sharedApplication = c_void_p(objc.sel_registerName(b'sharedApplication')) + objc.objc_msgSend.argtypes = [c_void_p, c_void_p] + objc.objc_msgSend.restype = c_void_p + shared_app = c_void_p(objc.objc_msgSend(UIApplication, sharedApplication)) + + # Open the URL on the shared application + openURL_ = c_void_p(objc.sel_registerName(b'openURL:')) + objc.objc_msgSend.argtypes = [c_void_p, c_void_p, c_void_p] + objc.objc_msgSend.restype = None + objc.objc_msgSend(shared_app, openURL_, nsurl) + + return True + + register("mobilesafari", None, MobileSafari(), preferred=True) # # Platform support for Windows diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 18984d15ab..5ce47c4b0a 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -1461,14 +1461,14 @@ if (!PyArg_ParseTuple(args, "O", &name)) return NULL; - + if (name == Py_None) Py_RETURN_FALSE; - + if (PyUnicode_FSConverter(name, &name2) == 0) return NULL; name_str = PyBytes_AS_STRING(name2); - + r = _dyld_shared_cache_contains_path(name_str); Py_DECREF(name2); diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index d64e0a1cfa..38770238b2 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -566,11 +566,15 @@ saved_errno = 0; for (i = 0; exec_array[i] != NULL; ++i) { const char *executable = exec_array[i]; +#if defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) || defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) + errno = ENOTSUP; +#else if (envp) { execve(executable, argv, envp); } else { execv(executable, argv); } +#endif if (errno != ENOENT && errno != ENOTDIR && saved_errno == 0) { saved_errno = errno; } @@ -841,7 +845,12 @@ need_after_fork = 1; } +#if defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) || defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) + pid = -1; + errno = ENOTSUP; +#else pid = fork(); +#endif if (pid == 0) { /* Child process */ /* diff --git a/Modules/faulthandler.c b/Modules/faulthandler.c index e7a2850330..4747720023 100644 --- a/Modules/faulthandler.c +++ b/Modules/faulthandler.c @@ -1,3 +1,7 @@ +#ifdef __APPLE__ +# include "TargetConditionals.h" +#endif /* __APPLE__ */ + #include "Python.h" #include "pycore_initconfig.h" #include "pycore_traceback.h" @@ -15,6 +19,11 @@ # include #endif +// tvOS and watchOS don't provide a number of important POSIX functions. +#if defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) || defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) +# undef HAVE_SIGALTSTACK +#endif /* TVOS || WATCHOS */ + /* Allocate at maximum 100 MiB of the stack to raise the stack overflow */ #define STACK_OVERFLOW_MAX_SIZE (100 * 1024 * 1024) diff --git a/Modules/makesetup b/Modules/makesetup index fefe3fd129..e4ca74c610 100755 --- a/Modules/makesetup +++ b/Modules/makesetup @@ -133,7 +133,7 @@ # Output DEFS in reverse order so first definition overrides case $line in - *=*) DEFS="$line$NL$DEFS"; continue;; + [A-Z]*=*) DEFS="$line$NL$DEFS"; continue;; 'include '*) DEFS="$line$NL$DEFS"; continue;; '*noobjects*') case $noobjects in @@ -162,9 +162,12 @@ esac case $arg in -framework) libs="$libs $arg"; skip=libs; - # OSX/OSXS/Darwin framework link cmd + # OSX/iOS/Darwin framework ;; - -[IDUCfF]*) cpps="$cpps $arg";; + -F*) libs="$libs $arg"; skip=libs; + # OSX/iOS/Darwin framework directory + ;; + -[IDUCf]*) cpps="$cpps $arg";; -Xcompiler) skip=cpps;; -Xlinker) libs="$libs $arg"; skip=libs;; -rpath) libs="$libs $arg"; skip=libs;; @@ -182,6 +185,7 @@ *.c++) srcs="$srcs $arg";; *.cxx) srcs="$srcs $arg";; *.cpp) srcs="$srcs $arg";; + *.S) srcs="$srcs $arg";; \$*) libs="$libs $arg" cpps="$cpps $arg";; *.*) echo 1>&2 "bad word $arg in $line" @@ -219,6 +223,7 @@ *.C) obj=`basename $src .C`.o; cc='$(CXX)';; *.cxx) obj=`basename $src .cxx`.o; cc='$(CXX)';; *.cpp) obj=`basename $src .cpp`.o; cc='$(CXX)';; + *.S) obj=`basename $src .S`.o; cc='$(CC)';; # Assembly *.m) obj=`basename $src .m`.o; cc='$(CC)';; # Obj-C *) continue;; esac diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 4aa7e6559a..511d77024c 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -64,6 +64,10 @@ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=76bc7002685dd942]*/ +#ifdef __APPLE__ +# include "TargetConditionals.h" +#endif /* __APPLE__ */ + /* sin(pi*x), giving accurate results for all finite x (especially x integral or close to an integer). This is here for use in the diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index f83150f1ef..e696c09cb1 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -56,6 +56,8 @@ */ #if defined(__APPLE__) +#include "TargetConditionals.h" + #if defined(__has_builtin) #if __has_builtin(__builtin_available) #define HAVE_BUILTIN_AVAILABLE 1 @@ -175,7 +177,6 @@ disguised Unix interface). Refer to the library manual and\n\ corresponding Unix manual entries for more information on calls."); - #ifdef HAVE_SYS_UIO_H # include #endif @@ -332,6 +333,26 @@ # endif /* _MSC_VER */ #endif /* ! __WATCOMC__ || __QNX__ */ +// iOS/tvOS/watchOS *define* a number of POSIX functions, but you can't use them +// because iOS isn't a conventional multiprocess environment. +#if TARGET_OS_IPHONE +# undef HAVE_EXECV +# undef HAVE_FORK +# undef HAVE_FORK1 +# undef HAVE_FORKPTY +# undef HAVE_GETGROUPS +# undef HAVE_POSIX_SPAWN +# undef HAVE_POSIX_SPAWNP +# undef HAVE_SCHED_H +# undef HAVE_SENDFILE +# undef HAVE_SETPRIORITY +# undef HAVE_SPAWNV +# undef HAVE_WAIT +# undef HAVE_WAIT3 +# undef HAVE_WAIT4 +# undef HAVE_WAITPID +#endif + _Py_IDENTIFIER(__fspath__); /*[clinic input] @@ -555,27 +576,33 @@ } } } +#endif void PyOS_BeforeFork(void) { +#ifdef HAVE_FORK run_at_forkers(_PyInterpreterState_GET()->before_forkers, 1); _PyImport_AcquireLock(); +#endif } void PyOS_AfterFork_Parent(void) { +#ifdef HAVE_FORK if (_PyImport_ReleaseLock() <= 0) Py_FatalError("failed releasing import lock after fork"); run_at_forkers(_PyInterpreterState_GET()->after_forkers_parent, 0); +#endif } void PyOS_AfterFork_Child(void) { +#ifdef HAVE_FORK _PyRuntimeState *runtime = &_PyRuntime; _PyGILState_Reinit(runtime); _PyEval_ReInitThreads(runtime); @@ -585,8 +612,10 @@ _PyInterpreterState_DeleteExceptMain(runtime); run_at_forkers(_PyInterpreterState_GET()->after_forkers_child, 0); +#endif } +#ifdef HAVE_FORK static int register_at_forker(PyObject **lst, PyObject *func) { @@ -606,9 +635,7 @@ void PyOS_AfterFork(void) { -#ifdef HAVE_FORK PyOS_AfterFork_Child(); -#endif } @@ -1521,7 +1548,9 @@ */ #include #elif !defined(_MSC_VER) && (!defined(__WATCOMC__) || defined(__QNX__) || defined(__VXWORKS__)) +# if !TARGET_OS_TV && !TARGET_OS_WATCH extern char **environ; +# endif #endif /* !_MSC_VER */ static PyObject * @@ -1537,6 +1566,7 @@ d = PyDict_New(); if (d == NULL) return NULL; +#if !TARGET_OS_TV && !TARGET_OS_WATCH #ifdef MS_WINDOWS /* _wenviron must be initialized in this way if the program is started through main() instead of wmain(). */ @@ -1592,6 +1622,7 @@ Py_DECREF(k); Py_DECREF(v); } +#endif return d; } @@ -4751,7 +4782,12 @@ } Py_BEGIN_ALLOW_THREADS +#if TARGET_OS_IPHONE + result = -1; + errno = ENOTSUP; +#else result = system(bytes); +#endif Py_END_ALLOW_THREADS return result; } @@ -13347,6 +13383,7 @@ int is_symlink; int need_stat; #endif +#if !TARGET_OS_TV && !TARGET_OS_WATCH #ifdef MS_WINDOWS unsigned long dir_bits; #endif @@ -13407,6 +13444,7 @@ #endif return result; +#endif error: Py_XDECREF(st_mode); diff --git a/Modules/pwdmodule.c b/Modules/pwdmodule.c index 901a3ed9a2..5a7536cc7b 100644 --- a/Modules/pwdmodule.c +++ b/Modules/pwdmodule.c @@ -1,6 +1,10 @@ /* UNIX password file access module */ +#ifdef __APPLE__ +# include "TargetConditionals.h" +#endif /* __APPLE__ */ + #include "Python.h" #include "posixmodule.h" @@ -186,6 +190,22 @@ if (nomem == 1) { return PyErr_NoMemory(); } + +// iPhone has a "user" with UID 501, username "mobile"; but the simulator +// doesn't reflect this. Generate a simulated response. +#if TARGET_IPHONE_SIMULATOR + if (uid == 501) { + struct passwd mp; + mp.pw_name = "mobile"; + mp.pw_passwd = "/smx7MYTQIi2M"; + mp.pw_uid = 501; + mp.pw_gid = 501; + mp.pw_gecos = "Mobile User"; + mp.pw_dir = "/var/mobile"; + mp.pw_shell = "/bin/sh"; + return mkpwent(&mp); + } +#endif PyObject *uid_obj = _PyLong_FromUid(uid); if (uid_obj == NULL) return NULL; @@ -269,6 +289,22 @@ PyErr_NoMemory(); } else { +// iPhone has a "user" with UID 501, username "mobile"; but the simulator +// doesn't reflect this. Generate a simulated response. +#if TARGET_IPHONE_SIMULATOR + if (strcmp(name, "mobile") == 0) { + struct passwd mp; + mp.pw_name = "mobile"; + mp.pw_passwd = "/smx7MYTQIi2M"; + mp.pw_uid = 501; + mp.pw_gid = 501; + mp.pw_gecos = "Mobile User"; + mp.pw_dir = "/var/mobile"; + mp.pw_shell = "/bin/sh"; + retval = mkpwent(&mp); + goto out; + } +#endif PyErr_Format(PyExc_KeyError, "getpwnam(): name not found: %R", name); } diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 80eab30c95..d8fdc60ebe 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -62,6 +62,11 @@ #define SEC_TO_NS (1000 * 1000 * 1000) +#ifdef __APPLE__ +# include "TargetConditionals.h" +#endif /* __APPLE__ */ + + /* Forward declarations */ static int pysleep(_PyTime_t); @@ -245,11 +250,13 @@ if (_PyTime_AsTimespec(t, &tp) == -1) return NULL; +#if !TARGET_OS_IPHONE ret = clock_settime((clockid_t)clk_id, &tp); if (ret != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; } +#endif Py_RETURN_NONE; } @@ -278,11 +285,13 @@ return NULL; } +#if !TARGET_OS_IPHONE ret = clock_settime((clockid_t)clk_id, &ts); if (ret != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; } +#endif Py_RETURN_NONE; } diff --git a/Python/bootstrap_hash.c b/Python/bootstrap_hash.c index a212f69870..8d37e2eb4c 100644 --- a/Python/bootstrap_hash.c +++ b/Python/bootstrap_hash.c @@ -35,6 +35,10 @@ #endif +#ifdef __APPLE__ +# include "TargetConditionals.h" +#endif /* __APPLE__ */ + #ifdef Py_DEBUG int _Py_HashSecret_Initialized = 0; #else @@ -205,6 +209,9 @@ } #elif defined(HAVE_GETENTROPY) +// iOS, tvOS and watchOS have an incomplete definitions of getentropy +// so it is *found* by configure, but doesn't actually exist. +#elif defined(HAVE_GETENTROPY) && !TARGET_OS_IPHONE #define PY_GETENTROPY 1 /* Fill buffer with size pseudo-random bytes generated by getentropy(): diff --git a/Python/marshal.c b/Python/marshal.c index c4538bd373..c385a04ea3 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -14,6 +14,10 @@ #include "marshal.h" #include "pycore_hashtable.h" +#ifdef __APPLE__ +# include "TargetConditionals.h" +#endif /* __APPLE__ */ + /*[clinic input] module marshal [clinic start generated code]*/ @@ -33,9 +37,13 @@ * #if defined(MS_WINDOWS) && defined(_DEBUG) */ #if defined(MS_WINDOWS) -#define MAX_MARSHAL_STACK_DEPTH 1000 +# define MAX_MARSHAL_STACK_DEPTH 1000 #else -#define MAX_MARSHAL_STACK_DEPTH 2000 +# if TARGET_OS_IPHONE +# define MAX_MARSHAL_STACK_DEPTH 1500 +# else +# define MAX_MARSHAL_STACK_DEPTH 2000 +# endif #endif #define TYPE_NULL '0' --- /dev/null +++ b/Tools/iOS-test/app/iOS-test/main.py @@ -0,0 +1,12 @@ +from datetime import datetime +import platform +from test import regrtest + +regrtest.start = datetime.now() +print("Testing on %s" % platform.machine()) +print("START:", regrtest.start) +regrtest.main_in_temp_cwd() +regrtest.end = datetime.now() +print("END:", regrtest.end) +print("Duration:", regrtest.end - regrtest.start) + --- /dev/null +++ b/Tools/iOS-test/app_packages/README @@ -0,0 +1 @@ +This directory exists so that 3rd party packages can be installed here. \ No newline at end of file --- /dev/null +++ b/Tools/iOS-test/iOS-test.xcodeproj/project.pbxproj @@ -0,0 +1,369 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE519190F4100A9926B /* Foundation.framework */; }; + 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE719190F4100A9926B /* CoreGraphics.framework */; }; + 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE919190F4100A9926B /* UIKit.framework */; }; + 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 60796EEE19190F4100A9926B /* InfoPlist.strings */; }; + 60796EF219190F4100A9926B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 60796EF119190F4100A9926B /* main.m */; }; + 60796EF819190F4100A9926B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 60796EF719190F4100A9926B /* Images.xcassets */; }; + 60796F2C1919C70800A9926B /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F2B1919C70800A9926B /* Python.framework */; }; + 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F38191CDBBA00A9926B /* CoreFoundation.framework */; }; + 60EAF0931C26F7310003B8F5 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 60EAF0921C26F7310003B8F5 /* libsqlite3.tbd */; }; + 60EAF0951C26F73D0003B8F5 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 60EAF0941C26F73D0003B8F5 /* libz.tbd */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 60796EE219190F4100A9926B /* iOS-test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "iOS-test.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 60796EE519190F4100A9926B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 60796EE719190F4100A9926B /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 60796EE919190F4100A9926B /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 60796EED19190F4100A9926B /* iOS-test-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "iOS-test-Info.plist"; sourceTree = ""; }; + 60796EEF19190F4100A9926B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 60796EF119190F4100A9926B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 60796EF319190F4100A9926B /* iOS-test-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "iOS-test-Prefix.pch"; sourceTree = ""; }; + 60796EF719190F4100A9926B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 60796F2B1919C70800A9926B /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Python.framework; sourceTree = ""; }; + 60796F38191CDBBA00A9926B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; + 60DBD4B01B47DEF700068095 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = SOURCE_ROOT; }; + 60EAF0921C26F7310003B8F5 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; + 60EAF0941C26F73D0003B8F5 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; + 60F0BABF191FC868006EC268 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = SOURCE_ROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 60796EDF19190F4100A9926B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 60EAF0951C26F73D0003B8F5 /* libz.tbd in Frameworks */, + 60EAF0931C26F7310003B8F5 /* libsqlite3.tbd in Frameworks */, + 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */, + 60796F2C1919C70800A9926B /* Python.framework in Frameworks */, + 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */, + 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */, + 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 60796ED919190F4100A9926B = { + isa = PBXGroup; + children = ( + 60796EEB19190F4100A9926B /* iOS-test */, + 60796EE419190F4100A9926B /* Frameworks */, + 60796EE319190F4100A9926B /* Products */, + ); + sourceTree = ""; + }; + 60796EE319190F4100A9926B /* Products */ = { + isa = PBXGroup; + children = ( + 60796EE219190F4100A9926B /* iOS-test.app */, + ); + name = Products; + sourceTree = ""; + }; + 60796EE419190F4100A9926B /* Frameworks */ = { + isa = PBXGroup; + children = ( + 60EAF0941C26F73D0003B8F5 /* libz.tbd */, + 60EAF0921C26F7310003B8F5 /* libsqlite3.tbd */, + 60796F2B1919C70800A9926B /* Python.framework */, + 60796EE519190F4100A9926B /* Foundation.framework */, + 60796F38191CDBBA00A9926B /* CoreFoundation.framework */, + 60796EE719190F4100A9926B /* CoreGraphics.framework */, + 60796EE919190F4100A9926B /* UIKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 60796EEB19190F4100A9926B /* iOS-test */ = { + isa = PBXGroup; + children = ( + 60DBD4B01B47DEF700068095 /* app */, + 60F0BABF191FC868006EC268 /* app_packages */, + 60796EF719190F4100A9926B /* Images.xcassets */, + 60796EEC19190F4100A9926B /* Supporting Files */, + ); + path = "iOS-test"; + sourceTree = ""; + }; + 60796EEC19190F4100A9926B /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 60796EED19190F4100A9926B /* iOS-test-Info.plist */, + 60796EEE19190F4100A9926B /* InfoPlist.strings */, + 60796EF119190F4100A9926B /* main.m */, + 60796EF319190F4100A9926B /* iOS-test-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 60796EE119190F4100A9926B /* iOS-test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "iOS-test" */; + buildPhases = ( + 60796F2F1919C7E700A9926B /* Refresh Python source */, + 60796EDE19190F4100A9926B /* Sources */, + 60796EDF19190F4100A9926B /* Frameworks */, + 60796EE019190F4100A9926B /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "iOS-test"; + productName = "iOS-test"; + productReference = 60796EE219190F4100A9926B /* iOS-test.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 60796EDA19190F4100A9926B /* Project object */ = { + isa = PBXProject; + attributes = { + CLASSPREFIX = Py; + LastUpgradeCheck = 0720; + ORGANIZATIONNAME = "Python Software Foundation"; + TargetAttributes = { + 60796EE119190F4100A9926B = { + DevelopmentTeam = 383DLEZ2K4; + }; + }; + }; + buildConfigurationList = 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "iOS-test" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 60796ED919190F4100A9926B; + productRefGroup = 60796EE319190F4100A9926B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 60796EE119190F4100A9926B /* iOS-test */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 60796EE019190F4100A9926B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */, + 60796EF819190F4100A9926B /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 60796F2F1919C7E700A9926B /* Refresh Python source */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Refresh Python source"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "mkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nmkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/lib $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/include $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app_packages/ $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 60796EDE19190F4100A9926B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 60796EF219190F4100A9926B /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 60796EEE19190F4100A9926B /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 60796EEF19190F4100A9926B /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 60796F0C19190F4100A9926B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_BITCODE = NO; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 60796F0D19190F4100A9926B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_BITCODE = NO; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 60796F0F19190F4100A9926B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "iOS-test/iOS-test-Prefix.pch"; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", + ); + INFOPLIST_FILE = "iOS-test/iOS-test-Info.plist"; + PRODUCT_BUNDLE_IDENTIFIER = "org.python.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + USER_HEADER_SEARCH_PATHS = include/python2.7; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 60796F1019190F4100A9926B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "iOS-test/iOS-test-Prefix.pch"; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", + ); + INFOPLIST_FILE = "iOS-test/iOS-test-Info.plist"; + PRODUCT_BUNDLE_IDENTIFIER = "org.python.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + USER_HEADER_SEARCH_PATHS = include/python2.7; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "iOS-test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 60796F0C19190F4100A9926B /* Debug */, + 60796F0D19190F4100A9926B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "iOS-test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 60796F0F19190F4100A9926B /* Debug */, + 60796F1019190F4100A9926B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 60796EDA19190F4100A9926B /* Project object */; +} --- /dev/null +++ b/Tools/iOS-test/iOS-test/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file --- /dev/null +++ b/Tools/iOS-test/iOS-test/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,51 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "subtype" : "retina4", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file --- /dev/null +++ b/Tools/iOS-test/iOS-test/en.lproj/InfoPlist.strings @@ -0,0 +1 @@ +/* Localized versions of Info.plist keys */ --- /dev/null +++ b/Tools/iOS-test/iOS-test/iOS-test-Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + --- /dev/null +++ b/Tools/iOS-test/iOS-test/iOS-test-Prefix.pch @@ -0,0 +1,16 @@ +// +// Prefix header +// +// The contents of this file are implicitly included at the beginning of every source file. +// + +#import + +#ifndef __IPHONE_3_0 +#warning "This project uses features only available in iOS SDK 3.0 and later." +#endif + +#ifdef __OBJC__ + #import + #import +#endif \ No newline at end of file --- /dev/null +++ b/Tools/iOS-test/iOS-test/main.m @@ -0,0 +1,149 @@ +// +// main.m +// A main module for starting Python projects under iOS. +// + +#import +#import +#include +#include + +int main(int argc, char *argv[]) { + int ret = 0; + unsigned int i; + NSString *tmp_path; + NSString *exe; + NSString *python_home; + wchar_t *wpython_home; + const char* main_script; + wchar_t** python_argv; + @autoreleasepool { + + NSString * resourcePath = [[NSBundle mainBundle] resourcePath]; + + // Special environment to avoid writing bytecode because + // the process will not have write attribute on the device. + putenv("PYTHONDONTWRITEBYTECODE=1"); + + python_home = [NSString stringWithFormat:@"%@/Library/Python.framework/Resources", resourcePath, nil]; + NSLog(@"PythonHome is: %@", python_home); + wpython_home = Py_DecodeLocale([python_home UTF8String], NULL); + Py_SetPythonHome(wpython_home); + + // iOS provides a specific directory for temp files. + tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil]; + putenv((char *)[tmp_path UTF8String]); + + // Since iOS doesn't allow dynamic linking, we have to know + // the name of the executable so that we can find the ctypes + // test objects. However, sys.argv[0] will be updated to + // reflect the script name; the TEST_EXECUTABLE environment + // variable provides the mechanism for specifying the filename. + exe = [NSString stringWithFormat:@"TEST_EXECUTABLE=%s", argv[0], nil]; + putenv((char *)[exe UTF8String]); + + NSLog(@"Initializing Python runtime..."); + Py_Initialize(); + + /******************************************************* + To tell lldb not to stop on signals, use the following commands: + process handle SIGPIPE -n true -p true -s false + process handle SIGINT -n true -p true -s false + process handle SIGXFSZ -n true -p true -s false + process handle SIGUSR1 -n true -p true -s false + process handle SIGUSR2 -n true -p true -s false + *******************************************************/ + + // Arguments to pass to test runner + char *test_args[] = { + "-j", "1", + "-u", "all,-audio,-curses,-largefile,-subprocess,-gui", +// "-v", // Verbose test output + "-W", // Display test output on failure + + "-x", // Arguments are tests to *exclude* +// Simulator failures +// "test_coroutines", // docstring not being populated +// "test_module", // docstring not being populated + +// ARM64 failures +// "test_coroutines", // docstring not being populated +// "test_ctypes", // DL loading? +// "test_module" // docstring not being populated +// "test_threading", // ctypes related; missing symbol PyThreadState_SetAsyncExc +// "test_unicode", // encoding problem + +// ARMv7 failures +// "test_cmath", // math domain error +// "test_ctypes", // DL loading? +// "test_float", // rounding? +// "test_math", // math domain error +// "test_numeric_tower", // +// "test_strtod", // +// "test_importlib", // Thread locking problem +// "test_threading", // ctypes related; missing symbol PyThreadState_SetAsyncExc + +// COMMON FAILURES + "test_bytes" // HARD CRASH ctypes related; PyBytes_FromFormat + + }; + + // Set the name of the main script + main_script = [ + [[NSBundle mainBundle] pathForResource:@"Library/Application Support/org.python.iOS-test/app/iOS-test/main" + ofType:@"py"] cStringUsingEncoding:NSUTF8StringEncoding]; + + if (main_script == NULL) { + NSLog(@"Unable to locate app/iOS-test/main.py file"); + exit(-1); + } + + // Construct argv for the interpreter + int n_test_args = sizeof(test_args) / sizeof (*test_args) + 1; + + python_argv = PyMem_RawMalloc(sizeof(wchar_t*) * n_test_args); + python_argv[0] = Py_DecodeLocale(main_script, NULL); + for (i = 1; i < n_test_args; i++) { + python_argv[i] = Py_DecodeLocale(test_args[i-1], NULL); + } + + PySys_SetArgv(n_test_args, python_argv); + + // If other modules are using thread, we need to initialize them before. + PyEval_InitThreads(); + + // Start the main.py script + NSLog(@"Running %s", main_script); + + @try { + FILE* fd = fopen(main_script, "r"); + if (fd == NULL) { + ret = 1; + NSLog(@"Unable to open main.py, abort."); + } else { + ret = PyRun_SimpleFileEx(fd, main_script, 1); + if (ret != 0) { + NSLog(@"Application quit abnormally!"); + } + } + } + @catch (NSException *exception) { + NSLog(@"Python runtime error: %@", [exception reason]); + } + @finally { + Py_Finalize(); + } + + PyMem_RawFree(wpython_home); + if (python_argv) { + for (i = 0; i < argc; i++) { + PyMem_RawFree(python_argv[i]); + } + PyMem_RawFree(python_argv); + } + NSLog(@"Leaving"); + } + + exit(ret); + return ret; +} \ No newline at end of file --- /dev/null +++ b/Tools/tvOS-test/app/README @@ -0,0 +1,3 @@ +Your application code should be placed in this directory. + +The native code will be looking for a tvOS-test/__main__.py file as the entry point. \ No newline at end of file --- /dev/null +++ b/Tools/tvOS-test/app/tvOS-test/main.py @@ -0,0 +1,14 @@ +from __future__ import print_function + +from datetime import datetime +import platform +from test import regrtest + +regrtest.start = datetime.now() +print("Testing on %s" % platform.machine()) +print("START:", regrtest.start) +regrtest.main_in_temp_cwd() +regrtest.end = datetime.now() +print("END:", regrtest.end) +print("Duration:", regrtest.end - regrtest.start) + --- /dev/null +++ b/Tools/tvOS-test/app_packages/README @@ -0,0 +1 @@ +This directory exists so that 3rd party packages can be installed here. --- /dev/null +++ b/Tools/tvOS-test/tvOS-test.xcodeproj/project.pbxproj @@ -0,0 +1,356 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 6023B2AE1C28BA7A006F2562 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6023B2AD1C28BA7A006F2562 /* main.m */; }; + 6023B2B71C28BA7A006F2562 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6023B2B51C28BA7A006F2562 /* Main.storyboard */; }; + 6023B2B91C28BA7A006F2562 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6023B2B81C28BA7A006F2562 /* Assets.xcassets */; }; + 6023B2C71C28BD44006F2562 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2C11C28BD44006F2562 /* CoreFoundation.framework */; }; + 6023B2C81C28BD44006F2562 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2C21C28BD44006F2562 /* CoreGraphics.framework */; }; + 6023B2C91C28BD44006F2562 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2C31C28BD44006F2562 /* Foundation.framework */; }; + 6023B2CA1C28BD44006F2562 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2C41C28BD44006F2562 /* libsqlite3.tbd */; }; + 6023B2CB1C28BD44006F2562 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2C51C28BD44006F2562 /* libz.tbd */; }; + 6023B2CC1C28BD44006F2562 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2C61C28BD44006F2562 /* UIKit.framework */; }; + 6023B2D01C28BDA3006F2562 /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2CE1C28BDA3006F2562 /* Python.framework */; }; + 6023B2D31C28BDB7006F2562 /* app in Resources */ = {isa = PBXBuildFile; fileRef = 6023B2D11C28BDB7006F2562 /* app */; }; + 6023B2D41C28BDB7006F2562 /* app_packages in Resources */ = {isa = PBXBuildFile; fileRef = 6023B2D21C28BDB7006F2562 /* app_packages */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 6023B2A91C28BA7A006F2562 /* tvOS-test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "tvOS-test.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 6023B2AD1C28BA7A006F2562 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 6023B2B61C28BA7A006F2562 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 6023B2B81C28BA7A006F2562 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 6023B2BA1C28BA7A006F2562 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 6023B2C11C28BD44006F2562 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; + 6023B2C21C28BD44006F2562 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 6023B2C31C28BD44006F2562 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 6023B2C41C28BD44006F2562 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; + 6023B2C51C28BD44006F2562 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; + 6023B2C61C28BD44006F2562 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 6023B2CD1C28BDA3006F2562 /* OpenSSL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OpenSSL.framework; sourceTree = ""; }; + 6023B2CE1C28BDA3006F2562 /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Python.framework; sourceTree = ""; }; + 6023B2D11C28BDB7006F2562 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; name = app; path = ../app; sourceTree = ""; }; + 6023B2D21C28BDB7006F2562 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; name = app_packages; path = ../app_packages; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 6023B2A61C28BA7A006F2562 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 6023B2CB1C28BD44006F2562 /* libz.tbd in Frameworks */, + 6023B2CA1C28BD44006F2562 /* libsqlite3.tbd in Frameworks */, + 6023B2D01C28BDA3006F2562 /* Python.framework in Frameworks */, + 6023B2C71C28BD44006F2562 /* CoreFoundation.framework in Frameworks */, + 6023B2C81C28BD44006F2562 /* CoreGraphics.framework in Frameworks */, + 6023B2C91C28BD44006F2562 /* Foundation.framework in Frameworks */, + 6023B2CC1C28BD44006F2562 /* UIKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 6023B2A01C28BA7A006F2562 = { + isa = PBXGroup; + children = ( + 6023B2AB1C28BA7A006F2562 /* tvOS-test */, + 6023B2C01C28BD23006F2562 /* Frameworks */, + 6023B2AA1C28BA7A006F2562 /* Products */, + ); + sourceTree = ""; + }; + 6023B2AA1C28BA7A006F2562 /* Products */ = { + isa = PBXGroup; + children = ( + 6023B2A91C28BA7A006F2562 /* tvOS-test.app */, + ); + name = Products; + sourceTree = ""; + }; + 6023B2AB1C28BA7A006F2562 /* tvOS-test */ = { + isa = PBXGroup; + children = ( + 6023B2D11C28BDB7006F2562 /* app */, + 6023B2D21C28BDB7006F2562 /* app_packages */, + 6023B2B81C28BA7A006F2562 /* Assets.xcassets */, + 6023B2AC1C28BA7A006F2562 /* Supporting Files */, + ); + path = "tvOS-test"; + sourceTree = ""; + }; + 6023B2AC1C28BA7A006F2562 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 6023B2B51C28BA7A006F2562 /* Main.storyboard */, + 6023B2BA1C28BA7A006F2562 /* Info.plist */, + 6023B2AD1C28BA7A006F2562 /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 6023B2C01C28BD23006F2562 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 6023B2C41C28BD44006F2562 /* libsqlite3.tbd */, + 6023B2C51C28BD44006F2562 /* libz.tbd */, + 6023B2CD1C28BDA3006F2562 /* OpenSSL.framework */, + 6023B2CE1C28BDA3006F2562 /* Python.framework */, + 6023B2C11C28BD44006F2562 /* CoreFoundation.framework */, + 6023B2C21C28BD44006F2562 /* CoreGraphics.framework */, + 6023B2C31C28BD44006F2562 /* Foundation.framework */, + 6023B2C61C28BD44006F2562 /* UIKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 6023B2A81C28BA7A006F2562 /* tvOS-test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6023B2BD1C28BA7A006F2562 /* Build configuration list for PBXNativeTarget "tvOS-test" */; + buildPhases = ( + 6023B2D61C28CB97006F2562 /* Refresh Python source */, + 6023B2A51C28BA7A006F2562 /* Sources */, + 6023B2A61C28BA7A006F2562 /* Frameworks */, + 6023B2A71C28BA7A006F2562 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "tvOS-test"; + productName = "tvOS-test"; + productReference = 6023B2A91C28BA7A006F2562 /* tvOS-test.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 6023B2A11C28BA7A006F2562 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0720; + ORGANIZATIONNAME = "Python Software Foundation"; + TargetAttributes = { + 6023B2A81C28BA7A006F2562 = { + CreatedOnToolsVersion = 7.2; + }; + }; + }; + buildConfigurationList = 6023B2A41C28BA7A006F2562 /* Build configuration list for PBXProject "tvOS-test" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 6023B2A01C28BA7A006F2562; + productRefGroup = 6023B2AA1C28BA7A006F2562 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 6023B2A81C28BA7A006F2562 /* tvOS-test */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 6023B2A71C28BA7A006F2562 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6023B2D31C28BDB7006F2562 /* app in Resources */, + 6023B2B91C28BA7A006F2562 /* Assets.xcassets in Resources */, + 6023B2B71C28BA7A006F2562 /* Main.storyboard in Resources */, + 6023B2D41C28BDB7006F2562 /* app_packages in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 6023B2D61C28CB97006F2562 /* Refresh Python source */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Refresh Python source"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "mkdir -p \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\"\nmkdir -p \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application Support/org.python.tvOS-test\"\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git \"$PROJECT_DIR/Python.framework/Resources/lib\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\"\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git \"$PROJECT_DIR/Python.framework/Resources/include\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\"\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git \"$PROJECT_DIR/app\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application Support/org.python.tvOS-test\"\nmkdir -p \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\"\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git \"$PROJECT_DIR/app_packages/\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\"\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 6023B2A51C28BA7A006F2562 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6023B2AE1C28BA7A006F2562 /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 6023B2B51C28BA7A006F2562 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 6023B2B61C28BA7A006F2562 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 6023B2BB1C28BA7A006F2562 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 9.1; + }; + name = Debug; + }; + 6023B2BC1C28BA7A006F2562 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 9.1; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 6023B2BE1C28BA7A006F2562 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + INFOPLIST_FILE = "tvOS-test/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "org.python.tvOS-test"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 6023B2BF1C28BA7A006F2562 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + INFOPLIST_FILE = "tvOS-test/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "org.python.tvOS-test"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 6023B2A41C28BA7A006F2562 /* Build configuration list for PBXProject "tvOS-test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6023B2BB1C28BA7A006F2562 /* Debug */, + 6023B2BC1C28BA7A006F2562 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6023B2BD1C28BA7A006F2562 /* Build configuration list for PBXNativeTarget "tvOS-test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6023B2BE1C28BA7A006F2562 /* Debug */, + 6023B2BF1C28BA7A006F2562 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 6023B2A11C28BA7A006F2562 /* Project object */; +} --- /dev/null +++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "tv", + "filename" : "large.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file --- /dev/null +++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file --- /dev/null +++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "layers" : [ + { + "filename" : "Front.imagestacklayer" + }, + { + "filename" : "Middle.imagestacklayer" + }, + { + "filename" : "Back.imagestacklayer" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} --- /dev/null +++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "tv", + "filename" : "large.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file --- /dev/null +++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file --- /dev/null +++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "tv", + "filename" : "large.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file --- /dev/null +++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file --- /dev/null +++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "tv", + "filename" : "small.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file --- /dev/null +++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file --- /dev/null +++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "layers" : [ + { + "filename" : "Front.imagestacklayer" + }, + { + "filename" : "Middle.imagestacklayer" + }, + { + "filename" : "Back.imagestacklayer" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} --- /dev/null +++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "tv", + "filename" : "small.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file --- /dev/null +++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file --- /dev/null +++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "tv", + "filename" : "small.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file --- /dev/null +++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file --- /dev/null +++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json @@ -0,0 +1,26 @@ +{ + "assets" : [ + { + "size" : "1280x768", + "idiom" : "tv", + "filename" : "App Icon - Large.imagestack", + "role" : "primary-app-icon" + }, + { + "size" : "400x240", + "idiom" : "tv", + "filename" : "App Icon - Small.imagestack", + "role" : "primary-app-icon" + }, + { + "size" : "1920x720", + "idiom" : "tv", + "filename" : "Top Shelf Image.imageset", + "role" : "top-shelf-image" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file --- /dev/null +++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "tv", + "filename" : "shelf.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file --- /dev/null +++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file --- /dev/null +++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "orientation" : "landscape", + "idiom" : "tv", + "filename" : "launch.png", + "extent" : "full-screen", + "minimum-system-version" : "9.0", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file --- /dev/null +++ b/Tools/tvOS-test/tvOS-test/Base.lproj/Main.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + --- /dev/null +++ b/Tools/tvOS-test/tvOS-test/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + + --- /dev/null +++ b/Tools/tvOS-test/tvOS-test/main.m @@ -0,0 +1,149 @@ +// +// main.m +// A main module for starting Python projects under tvOS. +// + +#import +#import +#include +#include + +int main(int argc, char *argv[]) { + int ret = 0; + unsigned int i; + NSString *tmp_path; + NSString *exe; + NSString *python_home; + wchar_t *wpython_home; + const char* main_script; + wchar_t** python_argv; + @autoreleasepool { + + NSString * resourcePath = [[NSBundle mainBundle] resourcePath]; + + // Special environment to avoid writing bytecode because + // the process will not have write attribute on the device. + putenv("PYTHONDONTWRITEBYTECODE=1"); + + python_home = [NSString stringWithFormat:@"%@/Library/Python.framework/Resources", resourcePath, nil]; + NSLog(@"PythonHome is: %@", python_home); + wpython_home = Py_DecodeLocale([python_home UTF8String], NULL); + Py_SetPythonHome(wpython_home); + + // tvOS provides a specific directory for temp files. + tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil]; + putenv((char *)[tmp_path UTF8String]); + + // Since tvOS doesn't allow dynamic linking, we have to know + // the name of the executable so that we can find the ctypes + // test objects. However, sys.argv[0] will be updated to + // reflect the script name; the TEST_EXECUTABLE environment + // variable provides the mechanism for specifying the filename. + exe = [NSString stringWithFormat:@"TEST_EXECUTABLE=%s", argv[0], nil]; + putenv((char *)[exe UTF8String]); + + NSLog(@"Initializing Python runtime..."); + Py_Initialize(); + + /******************************************************* + To tell lldb not to stop on signals, use the following commands: + process handle SIGPIPE -n true -p true -s false + process handle SIGINT -n true -p true -s false + process handle SIGXFSZ -n true -p true -s false + process handle SIGUSR1 -n true -p true -s false + process handle SIGUSR2 -n true -p true -s false + *******************************************************/ + + // Arguments to pass to test runner + char *test_args[] = { + "-j", "1", + "-u", "all,-audio,-curses,-largefile,-subprocess,-gui", +// "-v", // Verbose test output + "-W", // Display test output on failure + + "-x", // Arguments are tests to *exclude* +// Simulator failures +// "test_coroutines", // docstring not being populated +// "test_module", // docstring not being populated + +// ARM64 failures +// "test_coroutines", // docstring not being populated +// "test_ctypes", // DL loading? +// "test_module" // docstring not being populated +// "test_threading", // ctypes related; missing symbol PyThreadState_SetAsyncExc +// "test_unicode", // encoding problem + +// ARMv7 failures +// "test_cmath", // math domain error +// "test_ctypes", // DL loading? +// "test_float", // rounding? +// "test_math", // math domain error +// "test_numeric_tower", // +// "test_strtod", // +// "test_importlib", // Thread locking problem +// "test_threading", // ctypes related; missing symbol PyThreadState_SetAsyncExc + +// COMMON FAILURES + "test_bytes" // HARD CRASH ctypes related; PyBytes_FromFormat + + }; + + // Set the name of the main script + main_script = [ + [[NSBundle mainBundle] pathForResource:@"Library/Application Support/org.python.tvOS-test/app/tvOS-test/main" + ofType:@"py"] cStringUsingEncoding:NSUTF8StringEncoding]; + + if (main_script == NULL) { + NSLog(@"Unable to locate app/tvOS-test/main.py file"); + exit(-1); + } + + // Construct argv for the interpreter + int n_test_args = sizeof(test_args) / sizeof (*test_args) + 1; + + python_argv = PyMem_RawMalloc(sizeof(wchar_t*) * n_test_args); + python_argv[0] = Py_DecodeLocale(main_script, NULL); + for (i = 1; i < n_test_args; i++) { + python_argv[i] = Py_DecodeLocale(test_args[i-1], NULL); + } + + PySys_SetArgv(n_test_args, python_argv); + + // If other modules are using thread, we need to initialize them before. + PyEval_InitThreads(); + + // Start the main.py script + NSLog(@"Running %s", main_script); + + @try { + FILE* fd = fopen(main_script, "r"); + if (fd == NULL) { + ret = 1; + NSLog(@"Unable to open main.py, abort."); + } else { + ret = PyRun_SimpleFileEx(fd, main_script, 1); + if (ret != 0) { + NSLog(@"Application quit abnormally!"); + } + } + } + @catch (NSException *exception) { + NSLog(@"Python runtime error: %@", [exception reason]); + } + @finally { + Py_Finalize(); + } + + PyMem_RawFree(wpython_home); + if (python_argv) { + for (i = 0; i < argc; i++) { + PyMem_RawFree(python_argv[i]); + } + PyMem_RawFree(python_argv); + } + NSLog(@"Leaving"); + } + + exit(ret); + return ret; +} diff --git a/aclocal.m4 b/aclocal.m4 index 04342a4982..b027647376 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -365,7 +365,7 @@ AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no -AC_MSG_CHECKING([for $1]) +AC_MSG_CHECKING([for $2]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) @@ -375,11 +375,11 @@ See the pkg-config man page for more details.]) if test $pkg_failed = yes; then - AC_MSG_RESULT([no]) + AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` - else + else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs @@ -396,7 +396,7 @@ _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then - AC_MSG_RESULT([no]) + AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full diff --git a/config.sub b/config.sub index ba37cf99e2..9815c16a5c 100755 --- a/config.sub +++ b/config.sub @@ -249,7 +249,7 @@ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arceb \ - | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv6m | armv[78][arm] \ + | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv6m | armv[78][armk] \ | avr | avr32 \ | ba \ | be32 | be64 \ @@ -1526,6 +1526,12 @@ ;; -ios) ;; + -ios*) + ;; + -tvos*) + ;; + -watchos*) + ;; -none) ;; *) diff --git a/configure b/configure index c7a7291fea..c7bd520c96 100755 --- a/configure +++ b/configure @@ -3301,6 +3301,15 @@ *-*-cygwin*) ac_sys_system=Cygwin ;; + *-apple-ios) + ac_sys_system=iOS + ;; + *-apple-tvos) + ac_sys_system=tvOS + ;; + *-apple-watchos) + ac_sys_system=watchOS + ;; *-*-vxworks*) ac_sys_system=VxWorks ;; @@ -3351,6 +3360,15 @@ *-*-cygwin*) _host_cpu= ;; + *-apple-*) + case "$host_cpu" in + arm*) + _host_cpu=arm + ;; + *) + _host_cpu=$host_cpu + esac + ;; *-*-vxworks*) _host_cpu=$host_cpu ;; @@ -3426,6 +3444,13 @@ define_xopen_source=no;; Darwin/[12][0-9].*) define_xopen_source=no;; + # On iOS, defining _POSIX_C_SOURCE also disables platform specific features. + iOS/*) + define_xopen_source=no;; + tvOS/*) + define_xopen_source=no;; + watchOS/*) + define_xopen_source=no;; # On AIX 4 and 5.1, mbstate_t is defined only when _XOPEN_SOURCE == 500 but # used in wcsnrtombs() and mbsnrtowcs() even if _XOPEN_SOURCE is not defined # or has another value. By not (re)defining it, the defaults come in place. @@ -6206,11 +6231,17 @@ fi if test "$cross_compiling" = yes; then - case "$READELF" in - readelf|:) - as_fn_error $? "readelf for the host is required for cross builds" "$LINENO" 5 - ;; - esac + case "$host" in + *-apple-*os) + # readelf not required for iOS cross builds. + ;; + *) + case "$READELF" in + readelf|:) + as_fn_error $? "readelf for the host is required for cross builds" "$LINENO" 5 + ;; + esac + esac fi @@ -6961,8 +6992,6 @@ # tweak BASECFLAGS based on compiler and platform case $GCC in yes) - CFLAGS_NODIST="$CFLAGS_NODIST -std=c99" - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -Wextra" >&5 $as_echo_n "checking for -Wextra... " >&6; } ac_save_cc="$CC" @@ -11661,6 +11690,10 @@ fi ;; hp*|HP*) DYNLOADFILE="dynload_hpux.o";; + # Disable dynamic loading on iOS + iOS/*) DYNLOADFILE="dynload_stub.o";; + tvOS/*) DYNLOADFILE="dynload_stub.o";; + watchOS/*) DYNLOADFILE="dynload_stub.o";; *) # use dynload_shlib.c and dlopen() if we have it; otherwise stub # out any dynamic loading @@ -18961,7 +18994,7 @@ echo "creating Modules/Setup.local" >&6 if test ! -f Modules/Setup.local then - echo "# Edit this file for local setup changes" >Modules/Setup.local + echo "# Edit this file for local setup changes" >Modules/Setup.local fi echo "creating Makefile" >&6 @@ -18978,4 +19011,3 @@ echo "" >&6 echo "" >&6 fi - diff --git a/configure.ac b/configure.ac index 45e0af4493..2701b68cc3 100644 --- a/configure.ac +++ b/configure.ac @@ -398,6 +398,15 @@ *-*-cygwin*) ac_sys_system=Cygwin ;; + *-apple-ios) + ac_sys_system=iOS + ;; + *-apple-tvos) + ac_sys_system=tvOS + ;; + *-apple-watchos) + ac_sys_system=watchOS + ;; *-*-vxworks*) ac_sys_system=VxWorks ;; @@ -447,6 +456,15 @@ *-*-cygwin*) _host_cpu= ;; + *-apple-*) + case "$host_cpu" in + arm*) + _host_cpu=arm + ;; + *) + _host_cpu=$host_cpu + esac + ;; *-*-vxworks*) _host_cpu=$host_cpu ;; @@ -519,6 +537,13 @@ define_xopen_source=no;; Darwin/@<:@[12]@:>@@<:@0-9@:>@.*) define_xopen_source=no;; + # On iOS, defining _POSIX_C_SOURCE also disables platform specific features. + iOS/*) + define_xopen_source=no;; + tvOS/*) + define_xopen_source=no;; + watchOS/*) + define_xopen_source=no;; # On AIX 4 and 5.1, mbstate_t is defined only when _XOPEN_SOURCE == 500 but # used in wcsnrtombs() and mbsnrtowcs() even if _XOPEN_SOURCE is not defined # or has another value. By not (re)defining it, the defaults come in place. @@ -1214,11 +1239,17 @@ AC_CHECK_TOOLS([READELF], [readelf], [:]) if test "$cross_compiling" = yes; then - case "$READELF" in - readelf|:) - AC_MSG_ERROR([readelf for the host is required for cross builds]) - ;; - esac + case "$host" in + *-apple-*os) + # readelf not required for iOS cross builds. + ;; + *) + case "$READELF" in + readelf|:) + AC_MSG_ERROR([readelf for the host is required for cross builds]) + ;; + esac + esac fi AC_SUBST(READELF) @@ -1601,8 +1632,6 @@ # tweak BASECFLAGS based on compiler and platform case $GCC in yes) - CFLAGS_NODIST="$CFLAGS_NODIST -std=c99" - AC_MSG_CHECKING(for -Wextra) ac_save_cc="$CC" CC="$CC -Wextra -Werror" @@ -3651,6 +3680,10 @@ fi ;; hp*|HP*) DYNLOADFILE="dynload_hpux.o";; + # Disable dynamic loading on iOS + iOS/*) DYNLOADFILE="dynload_stub.o";; + tvOS/*) DYNLOADFILE="dynload_stub.o";; + watchOS/*) DYNLOADFILE="dynload_stub.o";; *) # use dynload_shlib.c and dlopen() if we have it; otherwise stub # out any dynamic loading @@ -5828,7 +5861,7 @@ echo "creating Modules/Setup.local" >&AS_MESSAGE_FD if test ! -f Modules/Setup.local then - echo "# Edit this file for local setup changes" >Modules/Setup.local + echo "# Edit this file for local setup changes" >Modules/Setup.local fi echo "creating Makefile" >&AS_MESSAGE_FD @@ -5845,4 +5878,3 @@ echo "" >&AS_MESSAGE_FD echo "" >&AS_MESSAGE_FD fi - --- /dev/null +++ b/iOS/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + Python + CFBundleIdentifier + org.python + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + FMWK + CFBundleSignature + ???? + CFBundleVersion + xxxVERSIONxxx + + --- /dev/null +++ b/iOS/README @@ -0,0 +1,165 @@ +==================== +Python on iOS README +==================== + +:Authors: + Russell Keith-Magee (2015) + +:Version: 3.5.2 + +This document provides a overview of eccentricities of building and using +Python on iOS. + +Build instructions +================== + +The iOS build must be run on an Mac with XCode installed. To build the iOS +framework, unpack the Python sources, move into the iOS subdirectory, and +run ``make``. There are no configuration options to this build process - +it will use XCode utilities to identify the location of compilers, +resource directories, and so on. + +The build process will configure and build Python 6 times, producing: + + * A "host" version of Python + * A version of Python compiled for the x86-64 iOS Simulator + * A version of Python compiled for the i386 iOS Simulator + * A version of Python compiled for ARM64 iOS devices + * A version of Python compiled for ARMv7s iOS devices + * A version of Python compiled for ARMv7 iOS devices + +Build products will be "installed" into iOS/build. The built products will +then be combined into a single "fat" ``Python.framework`` that can be added to +an XCode project. The resulting framework will be located in the root +directory of the Python source tree. + +A ``make clean`` target also exists to clean out all build products; +``make distclean`` will clean out all user-specific files from the test and +sample projects. + +Test instructions +----------------- + +The ``Tools`` directory contains an ``iOS-Test`` project that enables you to +run the Python regression test suite. When you run ``make`` in the iOS +directory, a copy of ``Python.framework`` will also be installed into this +test project. + +To run the test project, load the project into XCode, and run (either on a +device or in the simulator). The test suite takes around 10 minutes to run on +an iPhone6S. + +.. note:: If you run the test project in debug mode, the XCode debugger will + stop whenever a signal is raised. The Python regression test suite checks + a number of signal handlers, and the test suite will stop mid-execution + when this occurs. + + To disable this signal handling, set a breakpoint at the start of + ``main.c``; when execution stops at the breakpoint, run the following + commands in the debugger (at the ``(lldb)`` prompt in the console log + window):: + + process handle SIGPIPE -n true -p true -s false + process handle SIGINT -n true -p true -s false + process handle SIGXFSZ -n true -p true -s false + process handle SIGUSR1 -n true -p true -s false + process handle SIGUSR2 -n true -p true -s false + + Unfortunately, this has to be done every time the test suite is executed. + +iOS-specific details +==================== + +* ``import sys; sys.platform`` will report as `'ios'`, regardless of whether you + are on a simulator or a real platform. + +* ``import platform; platform.machine()`` will return the device identifier. + For example, an iPhone 5S will return `'iPhone6,2'` + +* The following modules are not currently supported: + + - ``bsddb`` + - ``bz2`` + - ``curses`` + - ``dbm`` + - ``gdbm`` + - ``hotshot`` + - ``idlelib`` + - ``lzma`` + - ``nis`` + - ``ossaudiodev`` + - ``readline`` + - ``spwd`` + - ``sqlite3`` + - ``ssl`` + - ``tkinter`` + - ``turtledemo`` + - ``wsgiref`` + +* Due to limitations in using dynamic loading on iOS, binary Python modules must be + statically-linked into the executable. The framework package produced by the iOS + ``make install`` statically links all the supported standard library modules. + If you have a third-party Python binary module, you'll need to incorporate the + source files for that module into the sources for your own app. + + If you want to add or remove a binary module from the set that is included in the + Python library, you can do so by providing module setup files for each platform. + There are three default module configuration files: + + - ``Modules/Setup.iOS-aarch64`` for ARM64 iOS builds + - ``Modules/Setup.iOS-arm`` for ARMv7 iOS builds + - ``Modules/Setup.iOS-x86_64`` for x86_64 iOS simulator builds + + If you copy these files to a ``.local`` version (e.g., + ``Modules/Setup.iOS-aarch64.local``), the local version will override the + default. You can then make modifications to the modules that will be included + in the iOS framework, and the flags passed to the compiler when compiling those + modules. + +Adding Python to an iOS project +=============================== + +The iOS subdirectory contains a sample XCode 6.1 project to demonstrate how +Python can be added to an iOS project. After building the Python iOS framework, +copy it into the ``iOS/XCode-sample`` directory. You should end up with a directory +structure that looks like this:: + + XCode-sample/ + Python.framework/ - Manually copied into the project + app/ + sample/ + __init__.py + main.py - The Python script to be executed + app_packages/ - A directory that will be added to the `PYTHONPATH` at runtime + sample + Images.xcassets + en.lproj + main.c - The main() definition for the iOS application + sample-Info.plist + sample-Prefix.pch + sample.xcodeproj - The XCode project file + +If you open the project file is project and run it, you should get output +similar to the following:: + + 2015-03-14 22:15:19.595 sample[84454:22100187] PythonHome is: /Users/rkm/Library/Developer/CoreSimulator/Devices/19FE988F-E5C3-4A6C-8752-C12DE9BF079D/data/Containers/Bundle/Application/A949B323-FD20-4C76-B370-99AFF294E9D5/sample.app + 2015-03-14 22:15:19.597 sample[84454:22100187] Initializing Python runtime + 2015-03-14 22:15:19.758 sample[84454:22100187] Running /Users/rkm/Library/Developer/CoreSimulator/Devices/19FE988F-E5C3-4A6C-8752-C12DE9BF079D/data/Containers/Bundle/Application/A949B323-FD20-4C76-B370-99AFF294E9D5/sample.app/app/sample/main.py + Hello, World. + 2015-03-14 22:15:19.792 sample[84454:22100187] Leaving + +You can now modify the provide Python source code, import and use +code from the Python standard library, and add third-party modules to +app_packages. + +The sample app is a console-only app, so it isn't of any real practical use. +Python can be embedded into any Objective-C project using the normal Python +APIs for embedding; but if you want to write a full iOS app in Python, or +you want to access iOS services from within embedded code, you'll need to +bridge between the Objective-C environment and the Python environment. +This binding isn't something that Python does out of the box; you'll need +to use a third-party library like `Rubicon ObjC`_, `Pyobjus`_ or `PyObjC`_. + +.. _Rubicon ObjC: http://pybee.org/rubicon +.. _Pyobjus: http://pyobjus.readthedocs.org/ +.. _PyObjC: https://pythonhosted.org/pyobjc/ --- /dev/null +++ b/iOS/XCode-sample/app/sample/main.py @@ -0,0 +1,3 @@ + +if __name__ == '__main__': + print("Hello, World.") --- /dev/null +++ b/iOS/XCode-sample/app_packages/README @@ -0,0 +1 @@ +This directory exists so that 3rd party packages can be installed here. \ No newline at end of file --- /dev/null +++ b/iOS/XCode-sample/sample.xcodeproj/project.pbxproj @@ -0,0 +1,353 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE519190F4100A9926B /* Foundation.framework */; }; + 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE719190F4100A9926B /* CoreGraphics.framework */; }; + 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE919190F4100A9926B /* UIKit.framework */; }; + 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 60796EEE19190F4100A9926B /* InfoPlist.strings */; }; + 60796EF219190F4100A9926B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 60796EF119190F4100A9926B /* main.m */; }; + 60796EF819190F4100A9926B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 60796EF719190F4100A9926B /* Images.xcassets */; }; + 60796F1919190FBB00A9926B /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F1819190FBB00A9926B /* libz.dylib */; }; + 60796F201919174D00A9926B /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F1F1919174D00A9926B /* libsqlite3.dylib */; }; + 60796F2C1919C70800A9926B /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F2B1919C70800A9926B /* Python.framework */; }; + 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F38191CDBBA00A9926B /* CoreFoundation.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 60796EE219190F4100A9926B /* sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 60796EE519190F4100A9926B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 60796EE719190F4100A9926B /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 60796EE919190F4100A9926B /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 60796EED19190F4100A9926B /* sample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "sample-Info.plist"; sourceTree = ""; }; + 60796EEF19190F4100A9926B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 60796EF119190F4100A9926B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 60796EF319190F4100A9926B /* sample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "sample-Prefix.pch"; sourceTree = ""; }; + 60796EF719190F4100A9926B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 60796F1819190FBB00A9926B /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; + 60796F1F1919174D00A9926B /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; + 60796F2B1919C70800A9926B /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Python.framework; sourceTree = ""; }; + 60796F38191CDBBA00A9926B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; + 60F0BABD191FC83F006EC268 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = SOURCE_ROOT; }; + 60F0BABF191FC868006EC268 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = SOURCE_ROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 60796EDF19190F4100A9926B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */, + 60796F2C1919C70800A9926B /* Python.framework in Frameworks */, + 60796F201919174D00A9926B /* libsqlite3.dylib in Frameworks */, + 60796F1919190FBB00A9926B /* libz.dylib in Frameworks */, + 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */, + 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */, + 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 60796ED919190F4100A9926B = { + isa = PBXGroup; + children = ( + 60796EEB19190F4100A9926B /* sample */, + 60796EE419190F4100A9926B /* Frameworks */, + 60796EE319190F4100A9926B /* Products */, + ); + sourceTree = ""; + }; + 60796EE319190F4100A9926B /* Products */ = { + isa = PBXGroup; + children = ( + 60796EE219190F4100A9926B /* sample.app */, + ); + name = Products; + sourceTree = ""; + }; + 60796EE419190F4100A9926B /* Frameworks */ = { + isa = PBXGroup; + children = ( + 60796F1F1919174D00A9926B /* libsqlite3.dylib */, + 60796F1819190FBB00A9926B /* libz.dylib */, + 60796F38191CDBBA00A9926B /* CoreFoundation.framework */, + 60796EE719190F4100A9926B /* CoreGraphics.framework */, + 60796EE519190F4100A9926B /* Foundation.framework */, + 60796F2B1919C70800A9926B /* Python.framework */, + 60796EE919190F4100A9926B /* UIKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 60796EEB19190F4100A9926B /* sample */ = { + isa = PBXGroup; + children = ( + 60F0BABD191FC83F006EC268 /* app */, + 60F0BABF191FC868006EC268 /* app_packages */, + 60796EF719190F4100A9926B /* Images.xcassets */, + 60796EEC19190F4100A9926B /* Supporting Files */, + ); + path = sample; + sourceTree = ""; + }; + 60796EEC19190F4100A9926B /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 60796EED19190F4100A9926B /* sample-Info.plist */, + 60796EEE19190F4100A9926B /* InfoPlist.strings */, + 60796EF119190F4100A9926B /* main.m */, + 60796EF319190F4100A9926B /* sample-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 60796EE119190F4100A9926B /* sample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "sample" */; + buildPhases = ( + 60796F2F1919C7E700A9926B /* Refresh Python source */, + 60796EDE19190F4100A9926B /* Sources */, + 60796EDF19190F4100A9926B /* Frameworks */, + 60796EE019190F4100A9926B /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = sample; + productName = sample; + productReference = 60796EE219190F4100A9926B /* sample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 60796EDA19190F4100A9926B /* Project object */ = { + isa = PBXProject; + attributes = { + CLASSPREFIX = Py; + LastUpgradeCheck = 0630; + ORGANIZATIONNAME = "Example Corp"; + }; + buildConfigurationList = 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "sample" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 60796ED919190F4100A9926B; + productRefGroup = 60796EE319190F4100A9926B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 60796EE119190F4100A9926B /* sample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 60796EE019190F4100A9926B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */, + 60796EF819190F4100A9926B /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 60796F2F1919C7E700A9926B /* Refresh Python source */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Refresh Python source"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "mkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nmkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/lib $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/include $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app_packages/ $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 60796EDE19190F4100A9926B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 60796EF219190F4100A9926B /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 60796EEE19190F4100A9926B /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 60796EEF19190F4100A9926B /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 60796F0C19190F4100A9926B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 60796F0D19190F4100A9926B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 60796F0F19190F4100A9926B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "sample/sample-Prefix.pch"; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", + ); + INFOPLIST_FILE = "sample/sample-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + USER_HEADER_SEARCH_PATHS = include/python2.7; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 60796F1019190F4100A9926B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "sample/sample-Prefix.pch"; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", + ); + INFOPLIST_FILE = "sample/sample-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + USER_HEADER_SEARCH_PATHS = include/python2.7; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 60796F0C19190F4100A9926B /* Debug */, + 60796F0D19190F4100A9926B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 60796F0F19190F4100A9926B /* Debug */, + 60796F1019190F4100A9926B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 60796EDA19190F4100A9926B /* Project object */; +} --- /dev/null +++ b/iOS/XCode-sample/sample/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,53 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file --- /dev/null +++ b/iOS/XCode-sample/sample/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,51 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "subtype" : "retina4", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file --- /dev/null +++ b/iOS/XCode-sample/sample/en.lproj/InfoPlist.strings @@ -0,0 +1 @@ +/* Localized versions of Info.plist keys */ --- /dev/null +++ b/iOS/XCode-sample/sample/main.m @@ -0,0 +1,111 @@ +// +// main.m +// A main module for starting Python projects under iOS. +// + +#import +#import +#include +#include + +int main(int argc, char *argv[]) { + int ret = 0; + unsigned int i; + NSString *tmp_path; + NSString *python_home; + wchar_t *wpython_home; + const char* main_script; + wchar_t** python_argv; + + @autoreleasepool { + + NSString * resourcePath = [[NSBundle mainBundle] resourcePath]; + + // Special environment to avoid writing bytecode because + // the process will not have write attribute on the device. + putenv("PYTHONDONTWRITEBYTECODE=1"); + + python_home = [NSString stringWithFormat:@"%@/Library/Python.framework/Resources", resourcePath, nil]; + NSLog(@"PythonHome is: %@", python_home); + wpython_home = _Py_char2wchar([python_home UTF8String], NULL); + Py_SetPythonHome(wpython_home); + + // iOS provides a specific directory for temp files. + tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil]; + putenv((char *)[tmp_path UTF8String]); + + NSLog(@"Initializing Python runtime"); + Py_Initialize(); + + // Set the name of the main script + main_script = [ + [[NSBundle mainBundle] pathForResource:@"Library/Application Support/org.python.sample/app/sample/main" + ofType:@"py"] cStringUsingEncoding:NSUTF8StringEncoding]; + + if (main_script == NULL) { + NSLog(@"Unable to locate app/sample/main.py file"); + exit(-1); + } + + // Construct argv for the interpreter + python_argv = PyMem_RawMalloc(sizeof(wchar_t*) * argc); + + python_argv[0] = _Py_char2wchar(main_script, NULL); + for (i = 1; i < argc; i++) { + python_argv[i] = _Py_char2wchar(argv[i], NULL); + } + + PySys_SetArgv(argc, python_argv); + + // If other modules are using threads, we need to initialize them. + PyEval_InitThreads(); + + // Start the main.py script + NSLog(@"Running %s", main_script); + + @try { + FILE* fd = fopen(main_script, "r"); + if (fd == NULL) { + ret = 1; + NSLog(@"Unable to open main.py, abort."); + } else { + ret = PyRun_SimpleFileEx(fd, main_script, 1); + if (ret != 0) { + NSLog(@"Application quit abnormally!"); + } else { + // In a normal iOS application, the following line is what + // actually runs the application. It requires that the + // Objective-C runtime environment has a class named + // "PythonAppDelegate". This project doesn't define + // one, because Objective-C bridging isn't something + // Python does out of the box. You'll need to use + // a library like Rubicon-ObjC [1], Pyobjus [2] or + // PyObjC [3] if you want to run an *actual* iOS app. + // [1] http://pybee.org/rubicon + // [2] http://pyobjus.readthedocs.org/ + // [3] https://pythonhosted.org/pyobjc/ + + UIApplicationMain(argc, argv, nil, @"PythonAppDelegate"); + } + } + } + @catch (NSException *exception) { + NSLog(@"Python runtime error: %@", [exception reason]); + } + @finally { + Py_Finalize(); + } + + PyMem_RawFree(wpython_home); + if (python_argv) { + for (i = 0; i < argc; i++) { + PyMem_RawFree(python_argv[i]); + } + PyMem_RawFree(python_argv); + } + NSLog(@"Leaving"); + } + + exit(ret); + return ret; +} \ No newline at end of file --- /dev/null +++ b/iOS/XCode-sample/sample/sample-Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.example.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + \ No newline at end of file --- /dev/null +++ b/iOS/XCode-sample/sample/sample-Prefix.pch @@ -0,0 +1,16 @@ +// +// Prefix header +// +// The contents of this file are implicitly included at the beginning of every source file. +// + +#import + +#ifndef __IPHONE_3_0 +#warning "This project uses features only available in iOS SDK 3.0 and later." +#endif + +#ifdef __OBJC__ + #import + #import +#endif \ No newline at end of file --- /dev/null +++ b/tvOS/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + Python + CFBundleIdentifier + org.python + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + FMWK + CFBundleSignature + ???? + CFBundleVersion + xxxVERSIONxxx + + --- /dev/null +++ b/tvOS/README @@ -0,0 +1,161 @@ +===================== +Python on tvOS README +===================== + +:Authors: + Russell Keith-Magee (2015) + +:Version: 3.5.2 + +This document provides a overview of eccentricities of building and using +Python on tvOS devices (i.e., AppleTV). + +Build instructions +================== + +The tvOS build must be run on an Mac with XCode installed. To build the tvOS +framework, unpack the Python sources, move into the tvOS subdirectory, and +run ``make``. There are no configuration options to this build process - +it will use XCode utilities to identify the location of compilers, +resource directories, and so on. + +The build process will configure and build Python 3 times, producing: + + * A "host" version of Python + * A version of Python compiled for the x86-64 tvOS Simulator + * A version of Python compiled for ARM64 tvOS devices + +Build products will be "installed" into tvOS/build. The built products will +then be combined into a single "fat" ``Python.framework`` that can be added to +an XCode project. The resulting framework will be located in the root +directory of the Python source tree. + +A ``make clean`` target also exists to clean out all build products; +``make distclean`` will clean out all user-specific files from the test and +sample projects. + +Test instructions +----------------- + +The ``Tools`` directory contains an ``tvOS-Test`` project that enables you to +run the Python regression test suite. When you run ``make`` in the tvOS +directory, a copy of ``Python.framework`` will also be installed into this +test project. + +To run the test project, load the project into XCode, and run (either on a +device or in the simulator). The test suite takes around 10 minutes to run on +an gen 4 AppleTV. + +.. note:: If you run the test project in debug mode, the XCode debugger will + stop whenever a signal is raised. The Python regression test suite checks + a number of signal handlers, and the test suite will stop mid-execution + when this occurs. + + To disable this signal handling, set a breakpoint at the start of + ``main.c``; when execution stops at the breakpoint, run the following + commands in the debugger (at the ``(lldb)`` prompt in the console log + window):: + + process handle SIGPIPE -n true -p true -s false + process handle SIGINT -n true -p true -s false + process handle SIGXFSZ -n true -p true -s false + process handle SIGUSR1 -n true -p true -s false + process handle SIGUSR2 -n true -p true -s false + + Unfortunately, this has to be done every time the test suite is executed. + +tvOS-specific details +==================== + +* ``import sys; sys.platform`` will report as `'tvos'`, regardless of whether you + are on a simulator or a real platform. + +* ``import platform; platform.machine()`` will return the device identifier. + For example, a Generation 4 Apple TV will return `'AppleTV5,3'` + +* The following modules are not currently supported: + + - ``bsddb`` + - ``bz2`` + - ``curses`` + - ``dbm`` + - ``gdbm`` + - ``hotshot`` + - ``idlelib`` + - ``lzma`` + - ``nis`` + - ``ossaudiodev`` + - ``readline`` + - ``spwd`` + - ``sqlite3`` + - ``ssl`` + - ``tkinter`` + - ``turtledemo`` + - ``wsgiref`` + +* Due to limitations in using dynamic loading on tvOS, binary Python modules must be + statically-linked into the executable. The framework package produced by the tvOS + ``make install`` statically links all the supported standard library modules. + If you have a third-party Python binary module, you'll need to incorporate the + source files for that module into the sources for your own app. + + If you want to add or remove a binary module from the set that is included in the + Python library, you can do so by providing module setup files for each platform. + There are two default module configuration files: + + - ``Modules/Setup.tvOS-aarch64`` for ARM64 tvOS builds + - ``Modules/Setup.tvOS-x86_64`` for x86_64 tvOS simulator builds + + If you copy these files to a ``.local`` version (e.g., + ``Modules/Setup.tvOS-aarch64.local``), the local version will override the + default. You can then make modifications to the modules that will be included + in the tvOS framework, and the flags passed to the compiler when compiling those + modules. + +Adding Python to an tvOS project +=============================== + +The tvOS subdirectory contains a sample XCode 6.1 project to demonstrate how +Python can be added to an tvOS project. After building the Python tvOS framework, +copy it into the ``tvOS/XCode-sample`` directory. You should end up with a directory +structure that looks like this:: + + XCode-sample/ + Python.framework/ - Manually copied into the project + app/ + sample/ + __init__.py + main.py - The Python script to be executed + app_packages/ - A directory that will be added to the `PYTHONPATH` at runtime + sample + Images.xcassets + en.lproj + main.c - The main() definition for the tvOS application + sample-Info.plist + sample-Prefix.pch + sample.xcodeproj - The XCode project file + +If you open the project file is project and run it, you should get output +similar to the following:: + + 2015-03-14 22:15:19.595 sample[84454:22100187] PythonHome is: /Users/rkm/Library/Developer/CoreSimulator/Devices/19FE988F-E5C3-4A6C-8752-C12DE9BF079D/data/Containers/Bundle/Application/A949B323-FD20-4C76-B370-99AFF294E9D5/sample.app + 2015-03-14 22:15:19.597 sample[84454:22100187] Initializing Python runtime + 2015-03-14 22:15:19.758 sample[84454:22100187] Running /Users/rkm/Library/Developer/CoreSimulator/Devices/19FE988F-E5C3-4A6C-8752-C12DE9BF079D/data/Containers/Bundle/Application/A949B323-FD20-4C76-B370-99AFF294E9D5/sample.app/app/sample/main.py + Hello, World. + 2015-03-14 22:15:19.792 sample[84454:22100187] Leaving + +You can now modify the provide Python source code, import and use +code from the Python standard library, and add third-party modules to +app_packages. + +The sample app is a console-only app, so it isn't of any real practical use. +Python can be embedded into any Objective-C project using the normal Python +APIs for embedding; but if you want to write a full tvOS app in Python, or +you want to access tvOS services from within embedded code, you'll need to +bridge between the Objective-C environment and the Python environment. +This binding isn't something that Python does out of the box; you'll need +to use a third-party library like `Rubicon ObjC`_, `Pyobjus`_ or `PyObjC`_. + +.. _Rubicon ObjC: http://pybee.org/rubicon +.. _Pyobjus: http://pyobjus.readthedocs.org/ +.. _PyObjC: https://pythonhosted.org/pyobjc/ --- /dev/null +++ b/tvOS/XCode-sample/app/sample/main.py @@ -0,0 +1,3 @@ + +if __name__ == '__main__': + print("Hello, World.") --- /dev/null +++ b/tvOS/XCode-sample/app_packages/README @@ -0,0 +1 @@ +This directory exists so that 3rd party packages can be installed here. \ No newline at end of file --- /dev/null +++ b/tvOS/XCode-sample/sample.xcodeproj/project.pbxproj @@ -0,0 +1,353 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE519190F4100A9926B /* Foundation.framework */; }; + 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE719190F4100A9926B /* CoreGraphics.framework */; }; + 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE919190F4100A9926B /* UIKit.framework */; }; + 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 60796EEE19190F4100A9926B /* InfoPlist.strings */; }; + 60796EF219190F4100A9926B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 60796EF119190F4100A9926B /* main.m */; }; + 60796EF819190F4100A9926B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 60796EF719190F4100A9926B /* Images.xcassets */; }; + 60796F1919190FBB00A9926B /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F1819190FBB00A9926B /* libz.dylib */; }; + 60796F201919174D00A9926B /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F1F1919174D00A9926B /* libsqlite3.dylib */; }; + 60796F2C1919C70800A9926B /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F2B1919C70800A9926B /* Python.framework */; }; + 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F38191CDBBA00A9926B /* CoreFoundation.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 60796EE219190F4100A9926B /* sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 60796EE519190F4100A9926B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 60796EE719190F4100A9926B /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 60796EE919190F4100A9926B /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 60796EED19190F4100A9926B /* sample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "sample-Info.plist"; sourceTree = ""; }; + 60796EEF19190F4100A9926B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 60796EF119190F4100A9926B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 60796EF319190F4100A9926B /* sample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "sample-Prefix.pch"; sourceTree = ""; }; + 60796EF719190F4100A9926B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 60796F1819190FBB00A9926B /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; + 60796F1F1919174D00A9926B /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; + 60796F2B1919C70800A9926B /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Python.framework; sourceTree = ""; }; + 60796F38191CDBBA00A9926B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; + 60F0BABD191FC83F006EC268 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = SOURCE_ROOT; }; + 60F0BABF191FC868006EC268 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = SOURCE_ROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 60796EDF19190F4100A9926B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */, + 60796F2C1919C70800A9926B /* Python.framework in Frameworks */, + 60796F201919174D00A9926B /* libsqlite3.dylib in Frameworks */, + 60796F1919190FBB00A9926B /* libz.dylib in Frameworks */, + 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */, + 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */, + 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 60796ED919190F4100A9926B = { + isa = PBXGroup; + children = ( + 60796EEB19190F4100A9926B /* sample */, + 60796EE419190F4100A9926B /* Frameworks */, + 60796EE319190F4100A9926B /* Products */, + ); + sourceTree = ""; + }; + 60796EE319190F4100A9926B /* Products */ = { + isa = PBXGroup; + children = ( + 60796EE219190F4100A9926B /* sample.app */, + ); + name = Products; + sourceTree = ""; + }; + 60796EE419190F4100A9926B /* Frameworks */ = { + isa = PBXGroup; + children = ( + 60796F1F1919174D00A9926B /* libsqlite3.dylib */, + 60796F1819190FBB00A9926B /* libz.dylib */, + 60796F38191CDBBA00A9926B /* CoreFoundation.framework */, + 60796EE719190F4100A9926B /* CoreGraphics.framework */, + 60796EE519190F4100A9926B /* Foundation.framework */, + 60796F2B1919C70800A9926B /* Python.framework */, + 60796EE919190F4100A9926B /* UIKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 60796EEB19190F4100A9926B /* sample */ = { + isa = PBXGroup; + children = ( + 60F0BABD191FC83F006EC268 /* app */, + 60F0BABF191FC868006EC268 /* app_packages */, + 60796EF719190F4100A9926B /* Images.xcassets */, + 60796EEC19190F4100A9926B /* Supporting Files */, + ); + path = sample; + sourceTree = ""; + }; + 60796EEC19190F4100A9926B /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 60796EED19190F4100A9926B /* sample-Info.plist */, + 60796EEE19190F4100A9926B /* InfoPlist.strings */, + 60796EF119190F4100A9926B /* main.m */, + 60796EF319190F4100A9926B /* sample-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 60796EE119190F4100A9926B /* sample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "sample" */; + buildPhases = ( + 60796F2F1919C7E700A9926B /* Refresh Python source */, + 60796EDE19190F4100A9926B /* Sources */, + 60796EDF19190F4100A9926B /* Frameworks */, + 60796EE019190F4100A9926B /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = sample; + productName = sample; + productReference = 60796EE219190F4100A9926B /* sample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 60796EDA19190F4100A9926B /* Project object */ = { + isa = PBXProject; + attributes = { + CLASSPREFIX = Py; + LastUpgradeCheck = 0630; + ORGANIZATIONNAME = "Example Corp"; + }; + buildConfigurationList = 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "sample" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 60796ED919190F4100A9926B; + productRefGroup = 60796EE319190F4100A9926B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 60796EE119190F4100A9926B /* sample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 60796EE019190F4100A9926B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */, + 60796EF819190F4100A9926B /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 60796F2F1919C7E700A9926B /* Refresh Python source */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Refresh Python source"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "mkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nmkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/lib $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/include $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app_packages/ $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 60796EDE19190F4100A9926B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 60796EF219190F4100A9926B /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 60796EEE19190F4100A9926B /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 60796EEF19190F4100A9926B /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 60796F0C19190F4100A9926B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 60796F0D19190F4100A9926B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 60796F0F19190F4100A9926B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "sample/sample-Prefix.pch"; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", + ); + INFOPLIST_FILE = "sample/sample-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + USER_HEADER_SEARCH_PATHS = include/python2.7; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 60796F1019190F4100A9926B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "sample/sample-Prefix.pch"; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", + ); + INFOPLIST_FILE = "sample/sample-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + USER_HEADER_SEARCH_PATHS = include/python2.7; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 60796F0C19190F4100A9926B /* Debug */, + 60796F0D19190F4100A9926B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 60796F0F19190F4100A9926B /* Debug */, + 60796F1019190F4100A9926B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 60796EDA19190F4100A9926B /* Project object */; +} --- /dev/null +++ b/tvOS/XCode-sample/sample/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,53 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file --- /dev/null +++ b/tvOS/XCode-sample/sample/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,51 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "subtype" : "retina4", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file --- /dev/null +++ b/tvOS/XCode-sample/sample/en.lproj/InfoPlist.strings @@ -0,0 +1 @@ +/* Localized versions of Info.plist keys */ --- /dev/null +++ b/tvOS/XCode-sample/sample/main.m @@ -0,0 +1,111 @@ +// +// main.m +// A main module for starting Python projects under iOS. +// + +#import +#import +#include +#include + +int main(int argc, char *argv[]) { + int ret = 0; + unsigned int i; + NSString *tmp_path; + NSString *python_home; + wchar_t *wpython_home; + const char* main_script; + wchar_t** python_argv; + + @autoreleasepool { + + NSString * resourcePath = [[NSBundle mainBundle] resourcePath]; + + // Special environment to avoid writing bytecode because + // the process will not have write attribute on the device. + putenv("PYTHONDONTWRITEBYTECODE=1"); + + python_home = [NSString stringWithFormat:@"%@/Library/Python.framework/Resources", resourcePath, nil]; + NSLog(@"PythonHome is: %@", python_home); + wpython_home = _Py_char2wchar([python_home UTF8String], NULL); + Py_SetPythonHome(wpython_home); + + // iOS provides a specific directory for temp files. + tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil]; + putenv((char *)[tmp_path UTF8String]); + + NSLog(@"Initializing Python runtime"); + Py_Initialize(); + + // Set the name of the main script + main_script = [ + [[NSBundle mainBundle] pathForResource:@"Library/Application Support/org.python.sample/app/sample/main" + ofType:@"py"] cStringUsingEncoding:NSUTF8StringEncoding]; + + if (main_script == NULL) { + NSLog(@"Unable to locate app/sample/main.py file"); + exit(-1); + } + + // Construct argv for the interpreter + python_argv = PyMem_RawMalloc(sizeof(wchar_t*) * argc); + + python_argv[0] = _Py_char2wchar(main_script, NULL); + for (i = 1; i < argc; i++) { + python_argv[i] = _Py_char2wchar(argv[i], NULL); + } + + PySys_SetArgv(argc, python_argv); + + // If other modules are using threads, we need to initialize them. + PyEval_InitThreads(); + + // Start the main.py script + NSLog(@"Running %s", main_script); + + @try { + FILE* fd = fopen(main_script, "r"); + if (fd == NULL) { + ret = 1; + NSLog(@"Unable to open main.py, abort."); + } else { + ret = PyRun_SimpleFileEx(fd, main_script, 1); + if (ret != 0) { + NSLog(@"Application quit abnormally!"); + } else { + // In a normal iOS application, the following line is what + // actually runs the application. It requires that the + // Objective-C runtime environment has a class named + // "PythonAppDelegate". This project doesn't define + // one, because Objective-C bridging isn't something + // Python does out of the box. You'll need to use + // a library like Rubicon-ObjC [1], Pyobjus [2] or + // PyObjC [3] if you want to run an *actual* iOS app. + // [1] http://pybee.org/rubicon + // [2] http://pyobjus.readthedocs.org/ + // [3] https://pythonhosted.org/pyobjc/ + + UIApplicationMain(argc, argv, nil, @"PythonAppDelegate"); + } + } + } + @catch (NSException *exception) { + NSLog(@"Python runtime error: %@", [exception reason]); + } + @finally { + Py_Finalize(); + } + + PyMem_RawFree(wpython_home); + if (python_argv) { + for (i = 0; i < argc; i++) { + PyMem_RawFree(python_argv[i]); + } + PyMem_RawFree(python_argv); + } + NSLog(@"Leaving"); + } + + exit(ret); + return ret; +} \ No newline at end of file --- /dev/null +++ b/tvOS/XCode-sample/sample/sample-Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.example.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + \ No newline at end of file --- /dev/null +++ b/tvOS/XCode-sample/sample/sample-Prefix.pch @@ -0,0 +1,16 @@ +// +// Prefix header +// +// The contents of this file are implicitly included at the beginning of every source file. +// + +#import + +#ifndef __IPHONE_3_0 +#warning "This project uses features only available in iOS SDK 3.0 and later." +#endif + +#ifdef __OBJC__ + #import + #import +#endif \ No newline at end of file --- /dev/null +++ b/watchOS/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + Python + CFBundleIdentifier + org.python + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + FMWK + CFBundleSignature + ???? + CFBundleVersion + xxxVERSIONxxx + + --- /dev/null +++ b/watchOS/README @@ -0,0 +1,161 @@ +======================== +Python on watchOS README +======================== + +:Authors: + Russell Keith-Magee (2015) + +:Version: 3.5.2 + +This document provides a overview of eccentricities of building and using +Python on watchOS devices (i.e., AppleTV). + +Build instructions +================== + +The watchOS build must be run on an Mac with XCode installed. To build the watchOS +framework, unpack the Python sources, move into the watchOS subdirectory, and +run ``make``. There are no configuration options to this build process - +it will use XCode utilities to identify the location of compilers, +resource directories, and so on. + +The build process will configure and build Python 3 times, producing: + + * A "host" version of Python + * A version of Python compiled for the i386 watchOS Simulator + * A version of Python compiled for ARMv7k watchOS devices + +Build products will be "installed" into watchOS/build. The built products will +then be combined into a single "fat" ``Python.framework`` that can be added to +an XCode project. The resulting framework will be located in the root +directory of the Python source tree. + +A ``make clean`` target also exists to clean out all build products; +``make distclean`` will clean out all user-specific files from the test and +sample projects. + +Test instructions +----------------- + +The ``Tools`` directory contains an ``watchOS-Test`` project that enables you to +run the Python regression test suite. When you run ``make`` in the watchOS +directory, a copy of ``Python.framework`` will also be installed into this +test project. + +To run the test project, load the project into XCode, and run (either on a +device or in the simulator). The test suite takes around 10 minutes to run on +an Apple Watch. + +.. note:: If you run the test project in debug mode, the XCode debugger will + stop whenever a signal is raised. The Python regression test suite checks + a number of signal handlers, and the test suite will stop mid-execution + when this occurs. + + To disable this signal handling, set a breakpoint at the start of + ``main.c``; when execution stops at the breakpoint, run the following + commands in the debugger (at the ``(lldb)`` prompt in the console log + window):: + + process handle SIGPIPE -n true -p true -s false + process handle SIGINT -n true -p true -s false + process handle SIGXFSZ -n true -p true -s false + process handle SIGUSR1 -n true -p true -s false + process handle SIGUSR2 -n true -p true -s false + + Unfortunately, this has to be done every time the test suite is executed. + +watchOS-specific details +==================== + +* ``import sys; sys.platform`` will report as `'watchos'`, regardless of whether you + are on a simulator or a real platform. + +* ``import platform; platform.machine()`` will return the device identifier. + For example, an origianl Apple Watch will return `'Watch1,1'` + +* The following modules are not currently supported: + + - ``bsddb`` + - ``bz2`` + - ``curses`` + - ``dbm`` + - ``gdbm`` + - ``hotshot`` + - ``idlelib`` + - ``lzma`` + - ``nis`` + - ``ossaudiodev`` + - ``readline`` + - ``spwd`` + - ``sqlite3`` + - ``ssl`` + - ``tkinter`` + - ``turtledemo`` + - ``wsgiref`` + +* Due to limitations in using dynamic loading on watchOS, binary Python modules must be + statically-linked into the executable. The framework package produced by the watchOS + ``make install`` statically links all the supported standard library modules. + If you have a third-party Python binary module, you'll need to incorporate the + source files for that module into the sources for your own app. + + If you want to add or remove a binary module from the set that is included in the + Python library, you can do so by providing module setup files for each platform. + There are two default module configuration files: + + - ``Modules/Setup.watchOS-aarch64`` for ARM64 watchOS builds + - ``Modules/Setup.watchOS-x86_64`` for x86_64 watchOS simulator builds + + If you copy these files to a ``.local`` version (e.g., + ``Modules/Setup.watchOS-aarch64.local``), the local version will override the + default. You can then make modifications to the modules that will be included + in the watchOS framework, and the flags passed to the compiler when compiling those + modules. + +Adding Python to an watchOS project +=============================== + +The watchOS subdirectory contains a sample XCode 6.1 project to demonstrate how +Python can be added to an watchOS project. After building the Python watchOS framework, +copy it into the ``watchOS/XCode-sample`` directory. You should end up with a directory +structure that looks like this:: + + XCode-sample/ + Python.framework/ - Manually copied into the project + app/ + sample/ + __init__.py + main.py - The Python script to be executed + app_packages/ - A directory that will be added to the `PYTHONPATH` at runtime + sample + Images.xcassets + en.lproj + main.c - The main() definition for the watchOS application + sample-Info.plist + sample-Prefix.pch + sample.xcodeproj - The XCode project file + +If you open the project file is project and run it, you should get output +similar to the following:: + + 2015-03-14 22:15:19.595 sample[84454:22100187] PythonHome is: /Users/rkm/Library/Developer/CoreSimulator/Devices/19FE988F-E5C3-4A6C-8752-C12DE9BF079D/data/Containers/Bundle/Application/A949B323-FD20-4C76-B370-99AFF294E9D5/sample.app + 2015-03-14 22:15:19.597 sample[84454:22100187] Initializing Python runtime + 2015-03-14 22:15:19.758 sample[84454:22100187] Running /Users/rkm/Library/Developer/CoreSimulator/Devices/19FE988F-E5C3-4A6C-8752-C12DE9BF079D/data/Containers/Bundle/Application/A949B323-FD20-4C76-B370-99AFF294E9D5/sample.app/app/sample/main.py + Hello, World. + 2015-03-14 22:15:19.792 sample[84454:22100187] Leaving + +You can now modify the provide Python source code, import and use +code from the Python standard library, and add third-party modules to +app_packages. + +The sample app is a console-only app, so it isn't of any real practical use. +Python can be embedded into any Objective-C project using the normal Python +APIs for embedding; but if you want to write a full watchOS app in Python, or +you want to access watchOS services from within embedded code, you'll need to +bridge between the Objective-C environment and the Python environment. +This binding isn't something that Python does out of the box; you'll need +to use a third-party library like `Rubicon ObjC`_, `Pyobjus`_ or `PyObjC`_. + +.. _Rubicon ObjC: http://pybee.org/rubicon +.. _Pyobjus: http://pyobjus.readthedocs.org/ +.. _PyObjC: https://pythonhosted.org/pyobjc/ --- /dev/null +++ b/watchOS/XCode-sample/app/sample/main.py @@ -0,0 +1,3 @@ + +if __name__ == '__main__': + print("Hello, World.") --- /dev/null +++ b/watchOS/XCode-sample/app_packages/README @@ -0,0 +1 @@ +This directory exists so that 3rd party packages can be installed here. \ No newline at end of file --- /dev/null +++ b/watchOS/XCode-sample/sample.xcodeproj/project.pbxproj @@ -0,0 +1,353 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE519190F4100A9926B /* Foundation.framework */; }; + 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE719190F4100A9926B /* CoreGraphics.framework */; }; + 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE919190F4100A9926B /* UIKit.framework */; }; + 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 60796EEE19190F4100A9926B /* InfoPlist.strings */; }; + 60796EF219190F4100A9926B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 60796EF119190F4100A9926B /* main.m */; }; + 60796EF819190F4100A9926B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 60796EF719190F4100A9926B /* Images.xcassets */; }; + 60796F1919190FBB00A9926B /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F1819190FBB00A9926B /* libz.dylib */; }; + 60796F201919174D00A9926B /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F1F1919174D00A9926B /* libsqlite3.dylib */; }; + 60796F2C1919C70800A9926B /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F2B1919C70800A9926B /* Python.framework */; }; + 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F38191CDBBA00A9926B /* CoreFoundation.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 60796EE219190F4100A9926B /* sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 60796EE519190F4100A9926B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 60796EE719190F4100A9926B /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 60796EE919190F4100A9926B /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 60796EED19190F4100A9926B /* sample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "sample-Info.plist"; sourceTree = ""; }; + 60796EEF19190F4100A9926B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 60796EF119190F4100A9926B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 60796EF319190F4100A9926B /* sample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "sample-Prefix.pch"; sourceTree = ""; }; + 60796EF719190F4100A9926B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 60796F1819190FBB00A9926B /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; + 60796F1F1919174D00A9926B /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; + 60796F2B1919C70800A9926B /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Python.framework; sourceTree = ""; }; + 60796F38191CDBBA00A9926B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; + 60F0BABD191FC83F006EC268 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = SOURCE_ROOT; }; + 60F0BABF191FC868006EC268 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = SOURCE_ROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 60796EDF19190F4100A9926B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */, + 60796F2C1919C70800A9926B /* Python.framework in Frameworks */, + 60796F201919174D00A9926B /* libsqlite3.dylib in Frameworks */, + 60796F1919190FBB00A9926B /* libz.dylib in Frameworks */, + 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */, + 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */, + 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 60796ED919190F4100A9926B = { + isa = PBXGroup; + children = ( + 60796EEB19190F4100A9926B /* sample */, + 60796EE419190F4100A9926B /* Frameworks */, + 60796EE319190F4100A9926B /* Products */, + ); + sourceTree = ""; + }; + 60796EE319190F4100A9926B /* Products */ = { + isa = PBXGroup; + children = ( + 60796EE219190F4100A9926B /* sample.app */, + ); + name = Products; + sourceTree = ""; + }; + 60796EE419190F4100A9926B /* Frameworks */ = { + isa = PBXGroup; + children = ( + 60796F1F1919174D00A9926B /* libsqlite3.dylib */, + 60796F1819190FBB00A9926B /* libz.dylib */, + 60796F38191CDBBA00A9926B /* CoreFoundation.framework */, + 60796EE719190F4100A9926B /* CoreGraphics.framework */, + 60796EE519190F4100A9926B /* Foundation.framework */, + 60796F2B1919C70800A9926B /* Python.framework */, + 60796EE919190F4100A9926B /* UIKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 60796EEB19190F4100A9926B /* sample */ = { + isa = PBXGroup; + children = ( + 60F0BABD191FC83F006EC268 /* app */, + 60F0BABF191FC868006EC268 /* app_packages */, + 60796EF719190F4100A9926B /* Images.xcassets */, + 60796EEC19190F4100A9926B /* Supporting Files */, + ); + path = sample; + sourceTree = ""; + }; + 60796EEC19190F4100A9926B /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 60796EED19190F4100A9926B /* sample-Info.plist */, + 60796EEE19190F4100A9926B /* InfoPlist.strings */, + 60796EF119190F4100A9926B /* main.m */, + 60796EF319190F4100A9926B /* sample-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 60796EE119190F4100A9926B /* sample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "sample" */; + buildPhases = ( + 60796F2F1919C7E700A9926B /* Refresh Python source */, + 60796EDE19190F4100A9926B /* Sources */, + 60796EDF19190F4100A9926B /* Frameworks */, + 60796EE019190F4100A9926B /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = sample; + productName = sample; + productReference = 60796EE219190F4100A9926B /* sample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 60796EDA19190F4100A9926B /* Project object */ = { + isa = PBXProject; + attributes = { + CLASSPREFIX = Py; + LastUpgradeCheck = 0630; + ORGANIZATIONNAME = "Example Corp"; + }; + buildConfigurationList = 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "sample" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 60796ED919190F4100A9926B; + productRefGroup = 60796EE319190F4100A9926B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 60796EE119190F4100A9926B /* sample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 60796EE019190F4100A9926B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */, + 60796EF819190F4100A9926B /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 60796F2F1919C7E700A9926B /* Refresh Python source */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Refresh Python source"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "mkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nmkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/lib $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/include $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app_packages/ $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 60796EDE19190F4100A9926B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 60796EF219190F4100A9926B /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 60796EEE19190F4100A9926B /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 60796EEF19190F4100A9926B /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 60796F0C19190F4100A9926B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 60796F0D19190F4100A9926B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 60796F0F19190F4100A9926B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "sample/sample-Prefix.pch"; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", + ); + INFOPLIST_FILE = "sample/sample-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + USER_HEADER_SEARCH_PATHS = include/python2.7; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 60796F1019190F4100A9926B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "sample/sample-Prefix.pch"; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", + ); + INFOPLIST_FILE = "sample/sample-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + USER_HEADER_SEARCH_PATHS = include/python2.7; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 60796F0C19190F4100A9926B /* Debug */, + 60796F0D19190F4100A9926B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 60796F0F19190F4100A9926B /* Debug */, + 60796F1019190F4100A9926B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 60796EDA19190F4100A9926B /* Project object */; +} --- /dev/null +++ b/watchOS/XCode-sample/sample/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,53 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file --- /dev/null +++ b/watchOS/XCode-sample/sample/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,51 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "subtype" : "retina4", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file --- /dev/null +++ b/watchOS/XCode-sample/sample/en.lproj/InfoPlist.strings @@ -0,0 +1 @@ +/* Localized versions of Info.plist keys */ --- /dev/null +++ b/watchOS/XCode-sample/sample/main.m @@ -0,0 +1,111 @@ +// +// main.m +// A main module for starting Python projects under iOS. +// + +#import +#import +#include +#include + +int main(int argc, char *argv[]) { + int ret = 0; + unsigned int i; + NSString *tmp_path; + NSString *python_home; + char *wpython_home; + const char* main_script; + char** python_argv; + @autoreleasepool { + + NSString * resourcePath = [[NSBundle mainBundle] resourcePath]; + + // Special environment to avoid writing bytecode because + // the process will not have write attribute on the device. + putenv("PYTHONDONTWRITEBYTECODE=1"); + + python_home = [NSString stringWithFormat:@"%@/Library/Python.framework/Resources", resourcePath, nil]; + NSLog(@"PythonHome is: %@", python_home); + wpython_home = strdup([python_home UTF8String]); + Py_SetPythonHome(wpython_home); + + // iOS provides a specific directory for temp files. + tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil]; + putenv((char *)[tmp_path UTF8String]); + + NSLog(@"Initializing Python runtime"); + Py_Initialize(); + + // Set the name of the main script + main_script = [ + [[NSBundle mainBundle] pathForResource:@"Library/Application Support/org.python.sample/app/sample/main" + ofType:@"py"] cStringUsingEncoding:NSUTF8StringEncoding]; + + if (main_script == NULL) { + NSLog(@"Unable to locate app/sample/main.py file"); + exit(-1); + } + + // Construct argv for the interpreter + python_argv = PyMem_Malloc(sizeof(char *) * argc); + + + python_argv[0] = strdup(main_script); + for (i = 1; i < argc; i++) { + python_argv[i] = argv[i]; + } + + PySys_SetArgv(argc, python_argv); + + // If other modules are using threads, we need to initialize them. + PyEval_InitThreads(); + + // Start the main.py script + NSLog(@"Running %s", main_script); + + @try { + FILE* fd = fopen(main_script, "r"); + if (fd == NULL) { + ret = 1; + NSLog(@"Unable to open main.py, abort."); + } else { + ret = PyRun_SimpleFileEx(fd, main_script, 1); + if (ret != 0) { + NSLog(@"Application quit abnormally!"); + } else { + // In a normal iOS application, the following line is what + // actually runs the application. It requires that the + // Objective-C runtime environment has a class named + // "PythonAppDelegate". This project doesn't define + // one, because Objective-C bridging isn't something + // Python does out of the box. You'll need to use + // a library like Rubicon-ObjC [1], Pyobjus [2] or + // PyObjC [3] if you want to run an *actual* iOS app. + // [1] http://pybee.org/rubicon + // [2] http://pyobjus.readthedocs.org/ + // [3] https://pythonhosted.org/pyobjc/ + + UIApplicationMain(argc, argv, nil, @"PythonAppDelegate"); + } + } + } + @catch (NSException *exception) { + NSLog(@"Python runtime error: %@", [exception reason]); + } + @finally { + Py_Finalize(); + } + + PyMem_Free(wpython_home); + if (python_argv) { + for (i = 0; i < argc; i++) { + PyMem_Free(python_argv[i]); + } + PyMem_Free(python_argv); + } + NSLog(@"Leaving"); + } + + exit(ret); + return ret; +} \ No newline at end of file --- /dev/null +++ b/watchOS/XCode-sample/sample/sample-Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.example.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + \ No newline at end of file --- /dev/null +++ b/watchOS/XCode-sample/sample/sample-Prefix.pch @@ -0,0 +1,16 @@ +// +// Prefix header +// +// The contents of this file are implicitly included at the beginning of every source file. +// + +#import + +#ifndef __IPHONE_3_0 +#warning "This project uses features only available in iOS SDK 3.0 and later." +#endif + +#ifdef __OBJC__ + #import + #import +#endif \ No newline at end of file