[pypy-commit] pypy default: add a --make-portable option to package.py

mattip pypy.commits at gmail.com
Fri Nov 1 17:43:14 EDT 2019


Author: Matti Picus <matti.picus at gmail.com>
Branch: 
Changeset: r97930:b655ef00bd4b
Date: 2019-11-01 05:52 -0400
http://bitbucket.org/pypy/pypy/changeset/b655ef00bd4b/

Log:	add a --make-portable option to package.py

diff --git a/pypy/tool/release/make_portable.py b/pypy/tool/release/make_portable.py
new file mode 100644
--- /dev/null
+++ b/pypy/tool/release/make_portable.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python
+
+bundle = ['sqlite3', 'ssl', 'crypto', 'ffi', 'expat', 'tcl', 'tk', 'gdbm', 'lzma', 'ncursesw', 'panelw', 'tinfow']
+
+from os import chdir, mkdir, symlink
+from os.path import dirname, relpath, join, exists, basename, realpath
+from shutil import copy2
+import sys
+from glob import glob
+from subprocess import check_output, check_call
+
+
+def get_deps(binary):
+    deps = {}
+    output = check_output(['ldd', binary])
+    for line in output.splitlines():
+        if '=>' not in line:
+            continue
+        line = line.strip()
+        needed, path = line.split(' => ')
+        if path == 'not found':
+            print('Broken dependency in ' + binary)
+        path = path.split(' ')[0]
+        path = realpath(path)
+        if not path:
+            continue
+
+        if needed[3:].split('.', 1)[0] not in bundle:
+            continue
+
+        deps[needed] = path
+        deps.update(get_deps(path))
+
+    return deps
+
+
+def gather_deps(binaries):
+    deps = {}
+    for binary in binaries:
+        deps.update(get_deps(binary))
+
+    return deps
+
+
+def copy_deps(deps):
+    copied = {}
+
+    for needed, path in deps.items():
+        bname = basename(path)
+
+        copy2(path, 'lib/' + bname)
+        copied[path] = 'lib/' + bname
+
+        if not exists('lib/' + needed):
+            symlink(bname, 'lib/' + needed)
+
+    return copied
+
+
+def rpath_binaries(binaries):
+    rpaths = {}
+
+    for binary in binaries:
+        rpath = join('$ORIGIN', relpath('lib', dirname(binary)))
+        check_call(['patchelf', '--set-rpath', rpath, binary])
+
+        rpaths[binary] = rpath
+
+    return rpaths
+
+
+def make_portable():
+    binaries = glob('bin/libpypy*.so')
+    if not binaries:
+        raise ValueError('Could not find bin/libpypy*.so')
+    binaries.extend(glob('lib_pypy/*_cffi.pypy*.so'))
+    binaries.extend(glob('lib_pypy/_pypy_openssl*.so'))
+    binaries.extend(glob('lib_pypy/_tkinter/*_cffi.pypy*.so'))
+
+    deps = gather_deps(binaries)
+
+    copied = copy_deps(deps)
+
+    for path, item in copied.items():
+        print('Copied {0} to {1}'.format(path, item))
+
+    binaries.extend(copied.values())
+
+    rpaths = rpath_binaries(binaries)
+    for binary, rpath in rpaths.items():
+        print('Set RPATH of {0} to {1}'.format(binary, rpath))
+
+    return deps
+
+
+if __name__ == '__main__':
+    try:
+        chdir(sys.argv[1])
+    except:
+        print('Call as %s <path/to/pypy/topdir' % sys.argv[0])
+        exit(-1)
+
+    try:
+        mkdir('lib')
+    except OSError:
+        pass
+
+    make_portable()
+
diff --git a/pypy/tool/release/package.py b/pypy/tool/release/package.py
--- a/pypy/tool/release/package.py
+++ b/pypy/tool/release/package.py
@@ -235,7 +235,11 @@
                 else:
                     archive = bindir.join(target)
                 smartstrip(archive, keep_debug=options.keep_debug)
-        #
+
+            # make the package portable by adding rpath=$ORIGIN/..lib,
+            # bundling dependencies
+            if options.make_portable:
+                make_portable()
         if USE_ZIPFILE_MODULE:
             import zipfile
             archive = str(builddir.join(name + '.zip'))
@@ -319,6 +323,12 @@
                         default=(sys.platform == 'darwin'),
                         help='whether to embed dependencies in CFFI modules '
                         '(default on OS X)')
+    parser.add_argument('--make-portable', '--no-make-portable',
+                        dest='make_portable',
+                        action=NegateAction,
+                        default=(platform.linux_distribution() in ('CentOS',)),
+                        help='whether to make the package portable by shipping '
+                            'dependent shared objects and mangling RPATH')
     options = parser.parse_args(args)
 
     if os.environ.has_key("PYPY_PACKAGE_NOKEEPDEBUG"):
@@ -329,6 +339,10 @@
         options.embed_dependencies = True
     elif os.environ.has_key("PYPY_NO_EMBED_DEPENDENCIES"):
         options.embed_dependencies = False
+    if os.environ.has_key("PYPY_MAKE_PORTABLE"):
+        options.embed_dependencies = True
+    elif os.environ.has_key("PYPY_NO_MAKE_PORTABLE"):
+        options.embed_dependencies = False
     if not options.builddir:
         # The import actually creates the udir directory
         from rpython.tool.udir import udir


More information about the pypy-commit mailing list