diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..ec15d98 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,6 @@ +os: osx +sudo: required +language: generic + +install: + - "./build.sh" diff --git a/README.md b/README.md new file mode 100644 index 0000000..19272a8 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# PythonDotApp + +Skeleton to build Python 3 together with a custom hello world application and package it as OS X Application. + +Before you start, install brew. + +To install additional packages add them to requirements.txt + +If you need more packages from brew you have to edit build.sh diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..eb3e37e --- /dev/null +++ b/build.sh @@ -0,0 +1,220 @@ +#!/bin/bash +set -e + +NAME="PythonSkeleton" +IDENTIFIER="com.example.PythonSkeleton" +OSXVERSION=10.9 +APPLICATION=application + +BASE=`pwd` +PREFIX="$BASE/dist" +BREW=/usr/local + +brew update + +function brew_install { + pkg=$1 + brew list | grep -q $pkg || brew install $pkg + brew outdated $pkg || brew upgrade $pkg +} + +brew_install openssl +brew_install sqlite +brew_install xz +brew_install readline + +# add lxml dependencies +brew_install libxml2 +brew_install libxslt + +# Python +CPPFLAGS="-I$BREW/opt/openssl/include/openssl" +LDFLAGS="" +for pkg in openssl sqlite readline xz; do + CPPFLAGS="$CPPFLAGS -I$BREW/opt/$pkg/include" + LDFLAGS="$LDFLAGS -L$BREW/opt/$pkg/lib" +done +export CPPFLAGS +export LDFLAGS + +version=3.5.2 +url="https://www.python.org/ftp/python/${version}/Python-${version}.tar.xz" +name=`basename $url .tar.xz` +tar=`basename $url` + +test -e $tar || curl -O $url +test -e $name || tar xf $tar +cd $name +./configure MACOSX_DEPLOYMENT_TARGET=$OSXVERSION --prefix="$PREFIX" +make -j8 +make altinstall + +unset CPPFLAGS +unset LDFLAGS + +ln -sf pip3.5 "$PREFIX/bin/pip" +ln -sf pip3.5 "$PREFIX/bin/pip3" +ln -sf python3.5 "$PREFIX/bin/python3" + + +PATH="$PREFIX/bin:$PATH" +cd "$BASE" +test -e requirements.txt && pip3.5 install -r requirements.txt + +chmod -R +rw "$PREFIX/lib" +# make self contained +for lib in \ + opt/libxml2/lib/libxml2.2.dylib \ + opt/libxslt/lib/libexslt.0.dylib \ + opt/libxslt/lib/libxslt.1.dylib \ + opt/openssl/lib/libcrypto.1.0.0.dylib \ + opt/openssl/lib/libssl.1.0.0.dylib \ + opt/readline/lib/libreadline.6.dylib \ + opt/sqlite/lib/libsqlite3.0.dylib \ + opt/xz/lib/liblzma.5.dylib \ +; do + target="$PREFIX/lib/`basename "$lib"`" + rm -f "$target" + cp -a "$BREW/$lib" "$target" +done +chmod -R +rw "$PREFIX/lib" +mkdir -p "$PREFIX/etc/openssl/certs" +cp /usr/local/etc/openssl/cert.pem "$PREFIX/etc/openssl" + +# cleanup +rm -rf \ + "$PREFIX/lib/python3.5/test" \ + "$PREFIX/bin/openssl" \ + "$PREFIX/etc/openssl/man" \ + "$PREFIX/bin/c_rehash" \ + "$PREFIX/bin/2to3-3.5" \ + "$PREFIX/bin/easy_install-3.5" \ + "$PREFIX/bin/idle3.5" \ + "$PREFIX/bin/pyvenv-3.5" \ + "$PREFIX/bin/c_rehash" \ + "$PREFIX/bin/pydoc3.5" + +for bin in $PREFIX/bin/pip3.5 $PREFIX/bin/python3.5m-config; do + sed "s#$PREFIX/bin/python3.5#/usr/bin/env python3.5#g" "$bin" > "$bin.t" + mv "$bin.t" "$bin" + chmod +x "$bin" +done + +find "$PREFIX" -d -name "__pycache__" -type d -exec rm -r "{}" \; +find "$PREFIX" -name "*.pyc" -exec rm "{}" \; +find "$PREFIX" -name "*.a" -exec rm -f "{}" \; + +for plib in \ + $PREFIX/lib/python3.5/site-packages/lxml/etree.cpython-35m-darwin.so \ + $PREFIX/lib/python3.5/site-packages/lxml/objectify.cpython-35m-darwin.so \ + $PREFIX/lib/python3.5/lib-dynload/_hashlib.cpython-35m-darwin.so \ + $PREFIX/lib/python3.5/lib-dynload/_lzma.cpython-35m-darwin.so \ + $PREFIX/lib/python3.5/lib-dynload/_sqlite3.cpython-35m-darwin.so \ + $PREFIX/lib/python3.5/lib-dynload/_ssl.cpython-35m-darwin.so \ + $PREFIX/lib/python3.5/lib-dynload/readline.cpython-35m-darwin.so \ +; do + if [ -e "$plib" ]; then + for lib in \ + $BREW/Cellar/libxslt/1.1.28_1/lib/libxslt.1.dylib \ + $BREW/Cellar/openssl/1.0.2d_1/lib/libcrypto.1.0.0.dylib \ + $BREW/opt/libxml2/lib/libxml2.2.dylib \ + $BREW/opt/libxslt/lib/libexslt.0.dylib \ + $BREW/opt/libxslt/lib/libxslt.1.dylib \ + $BREW/opt/openssl/lib/libcrypto.1.0.0.dylib \ + $BREW/opt/openssl/lib/libssl.1.0.0.dylib \ + $BREW/opt/readline/lib/libreadline.6.dylib \ + $BREW/opt/sqlite/lib/libsqlite3.0.dylib \ + $BREW/opt/xz/lib/liblzma.5.dylib \ + $PREFIX/lib/libcrypto.1.0.0.dylib \ + $PREFIX/lib/libexslt.0.dylib \ + $PREFIX/lib/liblzma.5.dylib \ + $PREFIX/lib/libreadline.6.dylib \ + $PREFIX/lib/libsqlite3.0.dylib \ + $PREFIX/lib/libssl.1.0.0.dylib \ + $PREFIX/lib/libxml2.2.dylib \ + $PREFIX/lib/libxslt.1.dylib \ + /usr/lib/libexslt.0.dylib \ + /usr/lib/libreadline.6.dylib \ + /usr/lib/libxml2.2.dylib \ + /usr/lib/libxslt.1.dylib \ + ; do + name=`basename $lib` + otool -L "$plib" | grep -q "$lib" && install_name_tool -change "$lib" "@executable_path/../lib/$name" "$plib" + done + otool -L "$plib" + fi +done + +mkdir -p "${NAME}.app/Contents/MacOS" +mv $PREFIX "${NAME}.app/Contents/Python" +cat > "$NAME.app/Contents/MacOS/$NAME" << EOF +#!/bin/bash +cd "\$(dirname "\$0")" +PREFIX="\$(dirname "\$(pwd)")/Python" +export SSL_CERT_FILE="\$PREFIX/etc/openssl/cert.pem" +export SSL_CERT_DIR="\$PREFIX/etc/openssl/certs" +"\$PREFIX/bin/python3.5" -m $APPLICATION +EOF +chmod +x "$NAME.app/Contents/MacOS/$NAME" + +cat > "$NAME.app/Contents/Info.plist" << EOF + + + + + BuildMachineOSBuild + 15C50 + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${NAME} + CFBundleIconFile + AppIcon + CFBundleIdentifier + ${IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 0.8 + CFBundleSignature + ???? + CFBundleSupportedPlatforms + + MacOSX + + CFBundleVersion + 1 + DTPlatformBuild + 7C68 + DTPlatformVersion + GM + DTSDKBuild + 15C43 + DTSDKName + macosx10.10 + DTXcode + 0720 + DTXcodeBuild + 7C68 + LSMinimumSystemVersion + ${OSXVERSION} + LSUIElement + + NSHumanReadableCopyright + No Copyright + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + +EOF + +test -e test.py && "${NAME}.app/Contents/Python/bin/python3.5" test.py + +find "${NAME}.app" -d -name "__pycache__" -type d -exec rm -r "{}" \; +find "${NAME}.app" -name "*.pyc" -exec rm "{}" \; diff --git a/example/application/__init__.py b/example/application/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/example/application/__main__.py b/example/application/__main__.py new file mode 100644 index 0000000..953288d --- /dev/null +++ b/example/application/__main__.py @@ -0,0 +1,24 @@ +import tkinter as tk + +class Application(tk.Frame): + def __init__(self, master=None): + super().__init__(master) + self.pack() + self.create_widgets() + + def create_widgets(self): + self.hi_there = tk.Button(self) + self.hi_there["text"] = "Hello World\n(click me)" + self.hi_there["command"] = self.say_hi + self.hi_there.pack(side="top") + + self.quit = tk.Button(self, text="QUIT", fg="red", + command=root.destroy) + self.quit.pack(side="bottom") + + def say_hi(self): + print("hi there, everyone!") + +root = tk.Tk() +app = Application(master=root) +app.mainloop() diff --git a/example/application/__pycache__/__init__.cpython-35.pyc b/example/application/__pycache__/__init__.cpython-35.pyc new file mode 100644 index 0000000..901954b Binary files /dev/null and b/example/application/__pycache__/__init__.cpython-35.pyc differ diff --git a/example/application/__pycache__/__main__.cpython-35.pyc b/example/application/__pycache__/__main__.cpython-35.pyc new file mode 100644 index 0000000..2e77c53 Binary files /dev/null and b/example/application/__pycache__/__main__.cpython-35.pyc differ diff --git a/example/setup.py b/example/setup.py new file mode 100644 index 0000000..539fa74 --- /dev/null +++ b/example/setup.py @@ -0,0 +1,3 @@ +from distutils.core import setup + +setup(name="application", packages=['application']) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..bf03dc6 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,8 @@ +# add additional dependencies here +lxml +# i.e. add PyQt5 +#PyQt5 + +# main application +# replace with your module that can be run via python3 -m application +./example diff --git a/test.py b/test.py new file mode 100644 index 0000000..5978943 --- /dev/null +++ b/test.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 +import ssl +print('loaded ssl') +import sqlite3 +print('loaded sqlite3') +import lxml +print('loaded lxml') + +print('TEST OK')