Skip to content

Commit 499e824

Browse files
committed
Added support for NumPy builds.
1 parent 1dc24f5 commit 499e824

File tree

5 files changed

+412
-1
lines changed

5 files changed

+412
-1
lines changed

Makefile

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ XZ_FRAMEWORK-$1=build/$1/Support/XZ
330330
PYTHON_FRAMEWORK-$1=build/$1/Support/Python
331331
PYTHON_RESOURCES-$1=$$(PYTHON_FRAMEWORK-$1)/Resources
332332

333-
$1: dist/Python-$(PYTHON_VER)-$1-support.b$(BUILD_NUMBER).tar.gz
333+
$1: dist/Python-$(PYTHON_VER)-$1-support.b$(BUILD_NUMBER).tar.gz packages-$1
334334

335335
clean-$1:
336336
rm -rf build/$1
@@ -448,6 +448,39 @@ build/$1/libpython$(PYTHON_VER)m.a: $$(foreach target,$$(TARGETS-$1),$$(PYTHON_D
448448
# Create a fat binary for the libPython library
449449
mkdir -p build/$1
450450
xcrun lipo -create -output $$@ $$^
451+
452+
vars-$1: $$(foreach target,$$(TARGETS-$1),vars-$$(target))
453+
451454
endef
452455

453456
$(foreach os,$(OS),$(eval $(call build,$(os))))
457+
458+
###########################################################################
459+
# Compiling Python Libraries with binary components
460+
###########################################################################
461+
462+
HOST_PYTHON=$(CURDIR)/build/macOS/python/bin/python3
463+
HOST_PIP=$(CURDIR)/build/macOS/python/bin/pip3
464+
465+
# Ensure pip and setuptools are available
466+
pip: Python-macOS
467+
$(HOST_PYTHON) -m ensurepip
468+
469+
# Create the directory that will contain installed packages
470+
dist/app_packages:
471+
mkdir -p dist/app_packages
472+
473+
# Makefiles for individual binary packages that are supported.
474+
include patch/numpy/Makefile.numpy
475+
476+
define build-app-packages
477+
packages-$1: numpy-$1
478+
endef
479+
480+
$(foreach os,$(OS),$(eval $(call build-app-packages,$(os))))
481+
482+
app_packages: numpy
483+
484+
# Dump vars (for test)
485+
vars: $(foreach os,$(OS),vars-$(os))
486+
@echo "APP_PACKAGES: $(APP_PACKAGES)"

README.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,27 @@ This should:
4949
The build products will be in the `build` directory; the compiled frameworks
5050
will be in the `dist` directory.
5151

52+
Binary packages
53+
---------------
54+
55+
These tools are also able to compile the following packages that have binary
56+
components:
57+
58+
* `numpy <patch/numpy/README.rst>`__
59+
60+
These binary components are not compiled by default. However, the build
61+
infrastructure of this project can compile them on request. You can run::
62+
63+
make <name of package>
64+
65+
to build a specific package; or, to build all supported packages::
66+
67+
make app_packages
68+
69+
For details on how to add these binary packages to your project, see the
70+
package-specific documentation linked above.
71+
72+
5273
.. _for macOS: https://s3-us-west-2.amazonaws.com/pybee-briefcase-support/Python-Apple-support/3.4/macOS/Python-3.4-macOS-support.b7.tar.gz
5374
.. _for iOS: https://s3-us-west-2.amazonaws.com/pybee-briefcase-support/Python-Apple-support/3.4/iOS/Python-3.4-iOS-support.b7.tar.gz
5475
.. _for tvOS: https://s3-us-west-2.amazonaws.com/pybee-briefcase-support/Python-Apple-support/3.4/tvOS/Python-3.4-tvOS-support.b7.tar.gz

patch/numpy/Makefile.numpy

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
###########################################################################
2+
# NumPy
3+
###########################################################################
4+
5+
NUMPY_VERSION=1.14.1
6+
NUMPY_CONFIG=BLAS=None LAPACK=None ATLAS=None
7+
8+
# Download original numpy source code archive.
9+
downloads/numpy-$(NUMPY_VERSION).tgz:
10+
mkdir -p downloads
11+
if [ ! -e downloads/numpy-$(NUMPY_VERSION).tgz ]; then curl --fail -L https://github.com/numpy/numpy/releases/download/v$(NUMPY_VERSION)/numpy-$(NUMPY_VERSION).tar.gz -o downloads/numpy-$(NUMPY_VERSION).tgz; fi
12+
13+
define build-numpy-target
14+
NUMPY-CFLAGS-$1=$$(CFLAGS-$2)
15+
NUMPY-CC-$1=xcrun --sdk $$(SDK-$1) clang \
16+
-arch $$(ARCH-$1) \
17+
--sysroot=$$(SDK_ROOT-$1) \
18+
$$(NUMPY_CFLAGS-$1)
19+
20+
build/$2/packages/numpy/build/temp.$1-$(PYTHON_VER)/libpymath.a: build/$2/packages/numpy
21+
cd build/$2/packages/numpy && \
22+
CC="$$(NUMPY-CC-$1)" \
23+
CFLAGS="$$(NUMPY-CFLAGS-$1)" \
24+
$(NUMPY_CONFIG) \
25+
_PYTHON_HOST_PLATFORM=$1 \
26+
$(HOST_PYTHON) setup.py build_ext
27+
28+
build/$2/packages/numpy/build/temp.$1-$(PYTHON_VER)/libnumpy.a: build/$2/packages/numpy/build/temp.$1-$(PYTHON_VER)/libpymath.a
29+
cd build/$2/packages/numpy/build/temp.$1-$(PYTHON_VER) && \
30+
xcrun --sdk $$(SDK-$1) ar -q libnumpy.a `find . -name "*.o"`
31+
32+
numpy-$1: build/$2/packages/numpy/build/temp.$1-$(PYTHON_VER)/libnumpy.a
33+
34+
endef
35+
36+
define build-numpy
37+
$$(foreach target,$$(TARGETS-$1),$$(eval $$(call build-numpy-target,$$(target),$1)))
38+
39+
build/$1/packages/numpy: downloads/numpy-$(NUMPY_VERSION).tgz
40+
# Unpack numpy sources
41+
mkdir -p build/$1/packages/numpy
42+
tar zxf downloads/numpy-$(NUMPY_VERSION).tgz --strip-components 1 -C build/$1/packages/numpy
43+
# Apply patch
44+
cd build/$1/packages/numpy && patch -p1 -i $(PROJECT_DIR)/patch/numpy/numpy.patch
45+
# Install requirements for compiling Numpy
46+
$(HOST_PIP) install cython
47+
48+
ifeq ($1,macOS)
49+
# Use the macOS build as a reference installation
50+
# Just install the source as-is into the dist/app_packages directory
51+
# Then clean out all the binary artefacts
52+
53+
dist/app_packages/numpy: dist/app_packages build/$1/packages/numpy
54+
cd build/$1/packages/numpy && \
55+
$(NUMPY_CONFIG) $(HOST_PIP) install --target $(PROJECT_DIR)/dist/app_packages .
56+
find build/$1/packages/numpy -name "*.so" -exec rm {} \;
57+
58+
numpy-$1: dist/app_packages/numpy
59+
60+
else
61+
# For all other platforms, run the numpy build for each target architecture
62+
63+
dist/$1/libnumpy.a: $(foreach target,$(TARGETS-$1),numpy-$(target))
64+
mkdir -p dist/$1
65+
xcrun lipo -create -output dist/$1/libnpymath.a $(foreach target,$(TARGETS-$1),build/$1/packages/numpy/build/temp.$(target)-$(PYTHON_VER)/libnpymath.a)
66+
xcrun lipo -create -output dist/$1/libnpysort.a $(foreach target,$(TARGETS-$1),build/$1/packages/numpy/build/temp.$(target)-$(PYTHON_VER)/libnpysort.a)
67+
xcrun lipo -create -output dist/$1/libnumpy.a $(foreach target,$(TARGETS-$1),build/$1/packages/numpy/build/temp.$(target)-$(PYTHON_VER)/libnumpy.a)
68+
69+
numpy-$1: dist/$1/libnumpy.a
70+
71+
endif
72+
endef
73+
74+
# Call build-numpy for each packaged OS target
75+
$(foreach os,$(OS),$(eval $(call build-numpy,$(os))))
76+
77+
# Main entry point
78+
numpy: pip $(foreach os,$(OS),numpy-$(os))

patch/numpy/README.rst

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
Adding NumPy to your iOS project
2+
================================
3+
4+
1. Build NumPy. You can either build NumPy specficially::
5+
6+
make <name of package>
7+
8+
or build all supported binary packages::
9+
10+
make app_packages
11+
12+
This will produce:
13+
14+
* a folder named `dist/app_packages`, containing the python code required by
15+
the package
16+
17+
* a folder for each supported mobile platform (iOS, tvOS and watchOS)
18+
containing the static fat binary libraries needed to support the Python
19+
code.
20+
21+
2. Copy the contents of `dist/app_packages` into your project's `site_packages`
22+
or `app_packages` directory. This will make the Python library available to
23+
your project.
24+
25+
3. Copy the static binary libraries in the platform directory (e.g., the contents
26+
of `dist/iOS`) into your project and add them as static libraries in your
27+
project. The location where you copy the files doesn't matter - they just need
28+
to be part of the project. If you're using a BeeWare template, we'd suggest
29+
putting them in the Support folder.
30+
31+
4. Add the following function definition to the file that configures your
32+
Python environment (if you're using a BeeWare template, this will be
33+
the ``main.m`` file; in other projects, it's whichever file contains
34+
the code that invokes ``Py_Initialize()`` and ``PyEval_InitThreads()``::
35+
36+
void numpy_importer() {
37+
PyRun_SimpleString(
38+
"import sys, importlib\n" \
39+
"class NumpyImporter(object):\n" \
40+
" def find_module(self, fullname, mpath=None):\n" \
41+
" if fullname in (" \
42+
" 'numpy.core.multiarray', " \
43+
" 'numpy.core.umath', " \
44+
" 'numpy.fft.fftpack_lite', " \
45+
" 'numpy.linalg._umath_linalg', " \
46+
" 'numpy.linalg.lapack_lite', " \
47+
" 'numpy.random.mtrand', " \
48+
" ):\n" \
49+
" return self\n" \
50+
" return\n" \
51+
" def load_module(self, fullname):\n" \
52+
" f = '__' + fullname.replace('.', '_')\n" \
53+
" mod = sys.modules.get(f)\n" \
54+
" if mod is None:\n" \
55+
" mod = importlib.__import__(f)\n" \
56+
" sys.modules[fullname] = mod\n" \
57+
" return mod\n" \
58+
" return mod\n" \
59+
"sys.meta_path.append(NumpyImporter())"
60+
);
61+
}
62+
63+
5. Add the following external function declarations to the file that
64+
configures your Python enviroment::
65+
66+
extern PyMODINIT_FUNC PyInit_multiarray(void);
67+
extern PyMODINIT_FUNC PyInit_umath(void);
68+
extern PyMODINIT_FUNC PyInit_fftpack_lite(void);
69+
extern PyMODINIT_FUNC PyInit__umath_linalg(void);
70+
extern PyMODINIT_FUNC PyInit_lapack_lite(void);
71+
extern PyMODINIT_FUNC PyInit_mtrand(void);
72+
73+
6. Add the following function calls *before* invoking ``Py_Initialize()``::
74+
75+
PyImport_AppendInittab("__numpy_core_multiarray", &PyInit_multiarray);
76+
PyImport_AppendInittab("__numpy_core_umath", &PyInit_umath);
77+
PyImport_AppendInittab("__numpy_fft_fftpack_lite", &PyInit_fftpack_lite);
78+
PyImport_AppendInittab("__numpy_linalg__umath_linalg", &PyInit__umath_linalg);
79+
PyImport_AppendInittab("__numpy_linalg_lapack_lite", &PyInit_lapack_lite);
80+
PyImport_AppendInittab("__numpy_random_mtrand", &PyInit_mtrand);
81+
82+
7. Install the numpy importer, by invoking ``numpy_importer()`` *after*
83+
invoking `PyEval_InitThreads()`, but before you run any Python scripts.
84+
85+
86+
If you've followed these instructions, and you're using a BeeWare project
87+
template, your ``iOS/<project name>/main.m`` file (in the Supporting Files
88+
folder in the Xcode project) will look something like this::
89+
90+
//
91+
// main.m
92+
// A main module for starting Python projects under iOS.
93+
//
94+
95+
#import <Foundation/Foundation.h>
96+
#import <UIKit/UIKit.h>
97+
#include <Python.h>
98+
#include <dlfcn.h>
99+
100+
101+
void numpy_importer() {
102+
PyRun_SimpleString(
103+
"import sys, importlib\n" \
104+
...
105+
"sys.meta_path.append(NumpyImporter())"
106+
);
107+
}
108+
109+
110+
extern PyMODINIT_FUNC PyInit_multiarray(void);
111+
extern PyMODINIT_FUNC PyInit_umath(void);
112+
extern PyMODINIT_FUNC PyInit_fftpack_lite(void);
113+
extern PyMODINIT_FUNC PyInit__umath_linalg(void);
114+
extern PyMODINIT_FUNC PyInit_lapack_lite(void);
115+
extern PyMODINIT_FUNC PyInit_mtrand(void);
116+
117+
118+
int main(int argc, char *argv[]) {
119+
int ret = 0;
120+
unsigned int i;
121+
NSString *tmp_path;
122+
NSString *python_home;
123+
NSString *python_path;
124+
wchar_t *wpython_home;
125+
const char* main_script;
126+
wchar_t** python_argv;
127+
128+
@autoreleasepool {
129+
...
130+
131+
// iOS provides a specific directory for temp files.
132+
tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil];
133+
putenv((char *)[tmp_path UTF8String]);
134+
135+
PyImport_AppendInittab("__numpy_core_multiarray", &PyInit_multiarray);
136+
PyImport_AppendInittab("__numpy_core_umath", &PyInit_umath);
137+
PyImport_AppendInittab("__numpy_fft_fftpack_lite", &PyInit_fftpack_lite);
138+
PyImport_AppendInittab("__numpy_linalg__umath_linalg", &PyInit__umath_linalg);
139+
PyImport_AppendInittab("__numpy_linalg_lapack_lite", &PyInit_lapack_lite);
140+
PyImport_AppendInittab("__numpy_random_mtrand", &PyInit_mtrand);
141+
142+
NSLog(@"Initializing Python runtime");
143+
Py_Initialize();
144+
145+
...
146+
147+
// If other modules are using threads, we need to initialize them.
148+
PyEval_InitThreads();
149+
150+
numpy_importer();
151+
152+
// Start the main.py script
153+
NSLog(@"Running %s", main_script);
154+
155+
...
156+
}
157+
158+
exit(ret);
159+
return ret;
160+
}

0 commit comments

Comments
 (0)