diff --git a/docs/src/apps.md b/docs/src/apps.md index 8fe05c1..3f4cf83 100644 --- a/docs/src/apps.md +++ b/docs/src/apps.md @@ -170,9 +170,9 @@ example, `$APPDIR` points to the AppImage mount point at runtime. Python specific environment variables, the like `PYTHONPATH`. Depending on your use case, this can be problematic. - The runtime isolation level can be changed by adding the `-s` and `-E` + The runtime isolation level can be changed by adding the `-E`, `-s` or `-I` options, when invoking the runtime. For example, - `{{ python-executable }} -sE` starts a fully isolated Python instance. + `{{ python-executable }} -I` starts a fully isolated Python instance. {% endraw %} @@ -186,8 +186,8 @@ might rather refer to the initial AppImage [Packaging Guide][APPIMAGE_PACKAGING], and use alternative tools like [linuxdeploy][LINUXDEPLOY]. -Yet, `python-appimage` can still be of use in more complex cases by extracting -its AppImages to an AppDir, as discussed in the [Advanced +However, `python-appimage` can still be of use in more complex cases by +extracting its AppImages to an AppDir, as discussed in the [Advanced installation](index.md#advanced-installation) section. The extracted AppImages contain a relocatable Python runtime, that can be used as a starting base for building more complex AppImages. diff --git a/docs/src/index.md b/docs/src/index.md index c219e62..91c9653 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -195,14 +195,16 @@ freely moved around. Executable scripts are installed under `AppDir/opt/pythonX.Y/bin` where _X_ and _Y_ in _pythonX.Y_ stand for the major and minor version numbers. Site packages are located under - `AppDir/opt/pythonX.Y/lib/pythonX.Y/site-packages`. + `AppDir/opt/pythonX.Y/lib/pythonX.Y/site-packages`. For convenience, `pip` + installed applications are also mirrored under `AppDir/usr/bin`, using + symbolic links. !!! Tip As for Python AppImages, by default the extracted runtime is [not isolated from the user environment](#isolating-from-the-user-environment). This - behaviour can be changed by editing the `AppDir/AppRun` wrapper script, and - by adding the `-s`, `-E` or `-I` option at the very bottom, where Python is - invoked. + behaviour can be changed by editing the `AppDir/usr/bin/pythonX.Y` wrapper + script, and by adding the `-s`, `-E` or `-I` option at the very bottom, + where Python is invoked. {{ begin(".capsule") }} diff --git a/python_appimage/appimage/relocate.py b/python_appimage/appimage/relocate.py index 8510e6e..9f2e724 100644 --- a/python_appimage/appimage/relocate.py +++ b/python_appimage/appimage/relocate.py @@ -208,13 +208,10 @@ def relocate_python(python=None, appdir=None): target = PYTHON_BIN + '/' + PYTHON_X_Y copy_file(source, target, update=True) - relpath = os.path.relpath(target, APPDIR_BIN) - make_tree(APPDIR_BIN) - os.symlink(relpath, APPDIR_BIN + '/' + PYTHON_X_Y) - copy_tree(HOST_PKG, PYTHON_PKG) copy_tree(HOST_INC, PYTHON_INC) + make_tree(APPDIR_BIN) pip_source = HOST_BIN + '/' + PIP_X_Y if not os.path.exists(pip_source): pip_source = HOST_BIN + '/' + PIP_X @@ -228,7 +225,8 @@ def relocate_python(python=None, appdir=None): f.write('#! /bin/sh\n') f.write(' '.join(( '"exec"', - '"$(dirname $(readlink -f ${0}))/' + PYTHON_X_Y + '"', + '"$(dirname $(readlink -f ${0}))/../../../usr/bin/' + + PYTHON_X_Y + '"', '"$0"', '"$@"\n' ))) @@ -361,18 +359,25 @@ def relocate_python(python=None, appdir=None): log('INSTALL', basename) - # Bundle the entry point - apprun = APPDIR + '/AppRun' - if not os.path.exists(apprun): - log('INSTALL', 'AppRun') + # Bundle the python wrapper + wrapper = APPDIR_BIN + '/' + PYTHON_X_Y + if not os.path.exists(wrapper): + log('INSTALL', '%s wrapper', PYTHON_X_Y) entrypoint_path = PREFIX + '/data/entrypoint.sh' entrypoint = load_template(entrypoint_path, python=PYTHON_X_Y) dictionary = {'entrypoint': entrypoint, 'shebang': '#! /bin/bash', 'tcltk-env': tcltk_env_string(PYTHON_PKG), 'cert-file': cert_file_env_string(cert_file)} - _copy_template('apprun.sh', apprun, **dictionary) + _copy_template('python-wrapper.sh', wrapper, **dictionary) + # Bundle the entry point + apprun = APPDIR + '/AppRun' + if not os.path.exists(apprun): + log('INSTALL', 'AppRun') + + relpath = os.path.relpath(wrapper, APPDIR) + os.symlink(relpath, APPDIR + '/AppRun') # Bundle the desktop file desktop_name = 'python{:}.desktop'.format(FULLVERSION) diff --git a/python_appimage/commands/build/app.py b/python_appimage/commands/build/app.py index e6fa521..15059c8 100644 --- a/python_appimage/commands/build/app.py +++ b/python_appimage/commands/build/app.py @@ -7,7 +7,7 @@ import shutil import stat import struct -from ...appimage import build_appimage, cert_file_env_string, tcltk_env_string +from ...appimage import build_appimage from ...utils.compat import decode from ...utils.deps import PREFIX from ...utils.fs import copy_file, make_tree, remove_file, remove_tree @@ -280,13 +280,10 @@ def execute(appdir, name=None, python_version=None, linux_tag=None, entrypoint = load_template(entrypoint_path, **dictionary) python_pkg = 'AppDir/opt/python{0:}/lib/python{0:}'.format( python_version) - cert_file = '/opt/_internal/certs.pem' - if not os.path.exists('AppDir' + cert_file): - cert_file = None dictionary = {'entrypoint': entrypoint, - 'shebang': shebang, - 'tcltk-env': tcltk_env_string(python_pkg), - 'cert-file': cert_file_env_string(cert_file)} + 'shebang': shebang} + if os.path.exists('AppDir/AppRun'): + os.remove('AppDir/AppRun') copy_template(PREFIX + '/data/apprun.sh', 'AppDir/AppRun', **dictionary) diff --git a/python_appimage/data/apprun.sh b/python_appimage/data/apprun.sh index 96ffdac..2605f85 100755 --- a/python_appimage/data/apprun.sh +++ b/python_appimage/data/apprun.sh @@ -1,18 +1,10 @@ {{ shebang }} -# If running from an extracted image, then export ARGV0 and APPDIR +# If running from an extracted image, then export APPDIR if [ -z "${APPIMAGE}" ]; then - export ARGV0=$0 - self="$(readlink -f -- $0)" - here="${self%/*}" - export APPDIR="${APPDIR:-${here}}" + export APPDIR="${self%/*}" fi -# Resolve the calling command (preserving symbolic links). -export APPIMAGE_COMMAND="$(command -v -- $ARGV0)" -{{ tcltk-env }} -{{ cert-file }} - -# Call the entry point +# Call the application entry point {{ entrypoint }} diff --git a/python_appimage/data/python-wrapper.sh b/python_appimage/data/python-wrapper.sh new file mode 100755 index 0000000..2f57b5b --- /dev/null +++ b/python_appimage/data/python-wrapper.sh @@ -0,0 +1,19 @@ +{{ shebang }} + +# If running from an extracted image, then export ARGV0 and APPDIR +if [ -z "${APPIMAGE}" ]; then + export ARGV0=$0 + + self="$(readlink -f -- $0)" + here="${self%/*}" + tmp="${here%/*}" + export APPDIR="${tmp%/*}" +fi + +# Resolve the calling command (preserving symbolic links). +export APPIMAGE_COMMAND="$(command -v -- $ARGV0)" +{{ tcltk-env }} +{{ cert-file }} + +# Call Python +{{ entrypoint }} diff --git a/python_appimage/data/sitecustomize.py b/python_appimage/data/sitecustomize.py index a9c4702..eb1919e 100644 --- a/python_appimage/data/sitecustomize.py +++ b/python_appimage/data/sitecustomize.py @@ -16,6 +16,11 @@ def patch_pip_install(): if not 'pip' in sys.modules: return + appdir = os.getenv('APPDIR') + python_x_y = 'python{:}.{:}'.format(*sys.version_info[:2]) + if sys.prefix != '{:}/opt/{:}'.format(appdir, python_x_y): + return + args = sys.argv[1:] if 'install' in args: for exe in os.listdir(sys.prefix + '/bin'): @@ -36,13 +41,15 @@ def patch_pip_install(): continue shebang, body = content.split(os.linesep, 1) - shebang = shebang.split() - python_x_y = os.path.basename(shebang.pop(0)) - if not python_x_y.startswith('python'): + shebang = shebang.strip().split() + executable = shebang.pop(0) + if executable != sys.executable: head, altbody = body.split(os.linesep, 1) if head.startswith("'''exec' /"): # Patch for alt shebang body = altbody.split(os.linesep, 1)[1] - python_x_y = os.path.basename(head.split()[1]) + executable = head.split()[1] + if executable != sys.executable: + continue else: continue