[PATCH 2 of 2] setup.py: don't rewrite @LIBDIR@ when creating wheels

Gregory Szorc gregory.szorc at gmail.com
Sat Dec 5 20:15:04 CST 2015


# HG changeset patch
# User Gregory Szorc <gregory.szorc at gmail.com>
# Date 1449366770 28800
#      Sat Dec 05 17:52:50 2015 -0800
# Node ID b1cbdb3cf79b4749a05b8a73ca27a2e1dedd90c1
# Parent  6550ffef53c01743d5d0efbd32a13bb9a344579f
setup.py: don't rewrite @LIBDIR@ when creating wheels

This is necessary to produce wheels that install properly. More
details are captured in an in-line comment.

After this patch, produced wheels can be installed via `pip install`
and appear to "just work," including on Windows.

diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -60,16 +60,17 @@ else:
         import bz2
         bz2.BZ2Compressor # silence unused import warning
     except ImportError:
         raise SystemExit(
             "Couldn't import standard bz2 (incomplete Python install).")
 
 ispypy = "PyPy" in sys.version
 
+import inspect
 import os, stat, subprocess, time
 import re
 import shutil
 import tempfile
 from distutils import log
 if 'FORCE_SETUPTOOLS' in os.environ:
     from setuptools import setup
 else:
@@ -476,16 +477,51 @@ class hginstallscripts(install_scripts):
     def finalize_options(self):
         install_scripts.finalize_options(self)
         self.set_undefined_options('install',
                                    ('install_lib', 'install_lib'))
 
     def run(self):
         install_scripts.run(self)
 
+        # It only makes sense to replace @LIBDIR@ with the install path if
+        # the install path is known. For wheels, the logic below calculates
+        # the libdir to be "../..". This is because the internal layout of a
+        # wheel archive looks like:
+        #
+        #   mercurial-3.6.1.data/scripts/hg
+        #   mercurial/__init__.py
+        #
+        # When installing wheels, the subdirectories of the "<pkg>.data"
+        # directory are translated to system local paths and files therein
+        # are copied in place. The mercurial/* files are installed into the
+        # site-packages directory. However, the site-packages directory
+        # isn't known until wheel install time. This means we have no clue
+        # at wheel generation time what the installed site-packages directory
+        # will be. And, wheels don't appear to provide the ability to register
+        # custom code to run during wheel installation. This all means that
+        # we can't reliably set the libdir in wheels: the default behavior
+        # of looking in sys.path must do.
+        #
+        # There is no formal API in distutils that exposes the command
+        # currently being processed. So, we walk the stack. The next best
+        # alternative is to look at ``self.distribution.commands`` and
+        # ``self.distribution.have_run``. However, since you can execute
+        # multiple commands in one setup.py invocation, this isn't reliable.
+        for frame in inspect.stack():
+            # We're looking for the method run() of a bdist_wheel class from
+            # the wheel/bdist_wheel.py file.
+            code = frame[0].f_code
+            if code.co_name != 'run':
+                continue
+
+            if inspect.getsourcefile(code).endswith('bdist_wheel.py'):
+                log.info('not rewriting @LIBDIR@ because building wheel')
+                return
+
         if (os.path.splitdrive(self.install_dir)[0] !=
             os.path.splitdrive(self.install_lib)[0]):
             # can't make relative paths from one drive to another, so use an
             # absolute path instead
             libdir = self.install_lib
         else:
             common = os.path.commonprefix((self.install_dir, self.install_lib))
             rest = self.install_dir[len(common):]


More information about the Mercurial-devel mailing list