Browse Source

Relocate python wrapper

Valentin Niess 3 năm trước cách đây
mục cha
commit
61fe3cedf9

+ 4 - 4
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.

+ 6 - 4
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") }}

+ 15 - 10
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)

+ 4 - 7
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)
 

+ 3 - 11
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 }}

+ 19 - 0
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 }}

+ 11 - 4
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