<div dir="ltr">I don't think this is working on macOS. The configuration step is generating error messages like,<div><br></div><div><font face="monospace, monospace">Ignored: [Errno 2] No such file or directory: <command path></font></div><div><br></div><div>In lines 1640-1643,</div><div><br></div><div><div><font face="monospace, monospace">self.write_dispatcher(</font></div><div><font face="monospace, monospace"> source_file=libtbx.env.under_base(os.path.join('bin', <a href="http://ep.name" target="_blank">ep.name</a>)),</font></div><div><font face="monospace, monospace"> target_file=os.path.join('bin', 'libtbx.' + <a href="http://ep.name" target="_blank">ep.name</a>),</font></div><div><font face="monospace, monospace">)</font></div></div><div><br></div><div>the source_file argument is always being constructed as <base>/bin/<command> instead of using the directory from self.get_setuptools_script_dir(). Should it be the bin_directory variable instead (e.g. <font face="monospace, monospace">source_file=os.path.join(bin_directory, <a href="http://ep.name">ep.name</a>)</font>)?</div><div><br></div><div>As a side note, conda will put everything in bin because there is no framework. For access to the window manager, you would call pythonw, which points to a python inside a .app bundle.<br><div><br></div><div><br clear="all"><div><div dir="ltr" class="m_-494864551476270440gmail_signature"><div dir="ltr"><div><div dir="ltr"><div><div dir="ltr"><div><div dir="ltr"><div><div dir="ltr"><div dir="ltr"><div dir="ltr"><div><div>--</div><div><span style="font-size:12.8000001907349px">Billy K. Poon</span><br></div></div><div>Research Scientist, Molecular Biophysics and Integrated Bioimaging</div><div>Lawrence Berkeley National Laboratory</div><div>1 Cyclotron Road, M/S 33R0345</div><div>Berkeley, CA 94720</div><div>Tel: (510) 486-5709</div><div>Fax: (510) 486-5909</div><div>Web: <a href="https://phenix-online.org" target="_blank">https://phenix-online.org</a></div></div></div></div></div></div></div></div></div></div></div></div></div></div><br></div></div></div><br><div class="gmail_quote"><div dir="ltr">On Fri, Jun 1, 2018 at 3:15 AM CCTBX commit <<a href="mailto:diamondlightsource.jenkins@gmail.com" target="_blank">diamondlightsource.jenkins@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div><tt>Repository : ssh://<a href="http://g18-sc-serv-04.diamond.ac.uk/cctbx" target="_blank">g18-sc-serv-04.diamond.ac.uk/cctbx</a><br>On branch : master<br><br><hr><br><br>commit ad736e2c8e65407615a7e6604b0544687ed8058c<br>Author: Markus Gerstel <<a href="mailto:markus.gerstel@diamond.ac.uk" target="_blank">markus.gerstel@diamond.ac.uk</a>><br>Date: Thu May 31 08:27:09 2018 +0100<br><br> Generate libtbx.* wrappers from correct directory<br> <br> Python console scripts don't necessarily land in base/bin, specifically<br> on MacOS this is not the case. Use setuptools etc. to find the actual<br> Python console script location and use that directory instead. Fixes #177<br> <br> Add a function to regenerate all Python console scripts which can be<br> called from an installer to relocate paths.<br><br><br><hr><br><br>ad736e2c8e65407615a7e6604b0544687ed8058c<br> libtbx/env_config.py | 74 +++++++++++++++++++++++++-----<br> libtbx/fastentrypoints.py | 112 ++++++++++++++++++++++++++++++++++++++++++++++<br> 2 files changed, 176 insertions(+), 10 deletions(-)<br><br>diff --git a/libtbx/env_config.py b/libtbx/env_config.py<br>index de5fc72dd..25daa2cb3 100644<br><tt style="color:#800">--- a/libtbx/env_config.py</tt><br><tt style="color:#008">+++ b/libtbx/env_config.py</tt><br>@@ -1562,23 +1562,77 @@ selfx:<br> source_file=source_file,<br> target_file=module_name+"."+command)<br> <br><tt style="color:#008">+ def get_setuptools_script_dir():</tt><br><tt style="color:#008">+ '''</tt><br><tt style="color:#008">+ Find the location of python entry point console_scripts, ie. things like</tt><br><tt style="color:#008">+ 'pip', 'pytest', ...</tt><br><tt style="color:#008">+ This is different from simple /base/bin, eg. on MacOS.</tt><br><tt style="color:#008">+</tt><br><tt style="color:#008">+ <a href="https://stackoverflow.com/questions/25066084/get-entry-point-script-file-location-in-setuputils-package" target="_blank">https://stackoverflow.com/questions/25066084/get-entry-point-script-file-location-in-setuputils-package</a></tt><br><tt style="color:#008">+ '''</tt><br><tt style="color:#008">+ from setuptools import Distribution</tt><br><tt style="color:#008">+ from setuptools.command.install import install</tt><br><tt style="color:#008">+ class OnlyGetScriptPath(install):</tt><br><tt style="color:#008">+ def run(self):</tt><br><tt style="color:#008">+ # does not call install.run() by design</tt><br><tt style="color:#008">+ self.distribution.install_scripts = self.install_scripts</tt><br><tt style="color:#008">+ dist = Distribution({'cmdclass': {'install': OnlyGetScriptPath}})</tt><br><tt style="color:#008">+ dist.dry_run = True # not sure if necessary, but to be safe</tt><br><tt style="color:#008">+ dist.parse_config_files()</tt><br><tt style="color:#008">+ command = dist.get_command_obj('install')</tt><br><tt style="color:#008">+ command.ensure_finalized()</tt><br><tt style="color:#008">+ command.run()</tt><br><tt style="color:#008">+ return dist.install_scripts</tt><br><tt style="color:#008">+</tt><br><tt style="color:#008">+ def regenerate_entry_point_console_scripts(self, verbose=True):</tt><br><tt style="color:#008">+ '''</tt><br><tt style="color:#008">+ Creates all console_scripts entry point scripts from scratch and overwrites existing ones.</tt><br><tt style="color:#008">+ This is intended to be used by installers to relocate the entry point script paths.</tt><br><tt style="color:#008">+ '''</tt><br><tt style="color:#008">+ try:</tt><br><tt style="color:#008">+ import distutils.dist</tt><br><tt style="color:#008">+ import libtbx.fastentrypoints # monkeypatches setuptools</tt><br><tt style="color:#008">+ import pkg_resources</tt><br><tt style="color:#008">+ import setuptools.command.easy_install</tt><br><tt style="color:#008">+ except ImportError:</tt><br><tt style="color:#008">+ return</tt><br><tt style="color:#008">+</tt><br><tt style="color:#008">+ # Prepare generic script generator</tt><br><tt style="color:#008">+ distribution = distutils.dist.Distribution({'name': 'setuptools'})</tt><br><tt style="color:#008">+ command = setuptools.command.easy_install.easy_install(distribution)</tt><br><tt style="color:#008">+ command.args = ['wheel'] # dummy argument</tt><br><tt style="color:#008">+ command.finalize_options()</tt><br><tt style="color:#008">+</tt><br><tt style="color:#008">+ # Force regeneration of all known console_scripts</tt><br><tt style="color:#008">+ for pkg_resources_dist in pkg_resources.working_set:</tt><br><tt style="color:#008">+ console_scripts = pkg_resources_dist.get_entry_map().get('console_scripts')</tt><br><tt style="color:#008">+ if console_scripts:</tt><br><tt style="color:#008">+ if verbose:</tt><br><tt style="color:#008">+ print("Regenerating commands for %s: %s" % (</tt><br><tt style="color:#008">+ pkg_resources_dist,</tt><br><tt style="color:#008">+ list(console_scripts),</tt><br><tt style="color:#008">+ ))</tt><br><tt style="color:#008">+ command.install_wrapper_scripts(pkg_resources_dist)</tt><br><tt style="color:#008">+</tt><br> def generate_entry_point_dispatchers(self):<br><tt style="color:#800">- # Write indirect dispatcher scripts for all console_scripts entry points</tt><br><tt style="color:#800">- # that have existing dispatcher scripts in the base/bin directory, but</tt><br><tt style="color:#800">- # add a 'libtbx.' prefix.</tt><br><tt style="color:#800">- base_bin_directory = libtbx.env.under_base('bin')</tt><br><tt style="color:#800">- if not os.path.isdir(base_bin_directory):</tt><br><tt style="color:#008">+ '''</tt><br><tt style="color:#008">+ Write indirect dispatcher scripts for all console_scripts entry points</tt><br><tt style="color:#008">+ that have existing dispatcher scripts in the base/bin directory, but</tt><br><tt style="color:#008">+ add a 'libtbx.' prefix.</tt><br><tt style="color:#008">+ '''</tt><br><tt style="color:#008">+ try:</tt><br><tt style="color:#008">+ import pkg_resources</tt><br><tt style="color:#008">+ bin_directory = get_setuptools_script_dir()</tt><br><tt style="color:#008">+ except ImportError:</tt><br><tt style="color:#008">+ return</tt><br><tt style="color:#008">+ if not os.path.isdir(bin_directory):</tt><br> return # do not create console_scripts dispatchers, only point to them<br> <br><tt style="color:#800">- base_bin_dispatchers = set(os.listdir(base_bin_directory))</tt><br><tt style="color:#008">+ base_bin_dispatchers = set(os.listdir(bin_directory))</tt><br> existing_dispatchers = filter(lambda f: f.startswith('libtbx.'), self.bin_path.listdir())<br> existing_dispatchers = set(map(lambda f: f[7:], existing_dispatchers))<br> entry_point_candidates = base_bin_dispatchers - existing_dispatchers<br> <br><tt style="color:#800">- try:</tt><br><tt style="color:#800">- import pkg_resources</tt><br><tt style="color:#800">- except ImportError:</tt><br><tt style="color:#800">- return</tt><br> entry_points = pkg_resources.iter_entry_points('console_scripts')<br> entry_points = filter(lambda ep: <a href="http://ep.name" target="_blank">ep.name</a> in entry_point_candidates, entry_points)<br> for ep in entry_points:<br>diff --git a/libtbx/fastentrypoints.py b/libtbx/fastentrypoints.py<br>new file mode 100644<br>index 000000000..9707f74a3<br><tt style="color:#800">--- /dev/null</tt><br><tt style="color:#008">+++ b/libtbx/fastentrypoints.py</tt><br>@@ -0,0 +1,112 @@<br><tt style="color:#008">+# noqa: D300,D400</tt><br><tt style="color:#008">+# Copyright (c) 2016, Aaron Christianson</tt><br><tt style="color:#008">+# All rights reserved.</tt><br><tt style="color:#008">+#</tt><br><tt style="color:#008">+# Redistribution and use in source and binary forms, with or without</tt><br><tt style="color:#008">+# modification, are permitted provided that the following conditions are</tt><br><tt style="color:#008">+# met:</tt><br><tt style="color:#008">+#</tt><br><tt style="color:#008">+# 1. Redistributions of source code must retain the above copyright</tt><br><tt style="color:#008">+# notice, this list of conditions and the following disclaimer.</tt><br><tt style="color:#008">+#</tt><br><tt style="color:#008">+# 2. Redistributions in binary form must reproduce the above copyright</tt><br><tt style="color:#008">+# notice, this list of conditions and the following disclaimer in the</tt><br><tt style="color:#008">+# documentation and/or other materials provided with the distribution.</tt><br><tt style="color:#008">+#</tt><br><tt style="color:#008">+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS</tt><br><tt style="color:#008">+# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED</tt><br><tt style="color:#008">+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A</tt><br><tt style="color:#008">+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT</tt><br><tt style="color:#008">+# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,</tt><br><tt style="color:#008">+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED</tt><br><tt style="color:#008">+# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR</tt><br><tt style="color:#008">+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF</tt><br><tt style="color:#008">+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING</tt><br><tt style="color:#008">+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS</tt><br><tt style="color:#008">+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</tt><br><tt style="color:#008">+'''</tt><br><tt style="color:#008">+Monkey patch setuptools to write faster console_scripts with this format:</tt><br><tt style="color:#008">+</tt><br><tt style="color:#008">+ import sys</tt><br><tt style="color:#008">+ from mymodule import entry_function</tt><br><tt style="color:#008">+ sys.exit(entry_function())</tt><br><tt style="color:#008">+</tt><br><tt style="color:#008">+This is better.</tt><br><tt style="color:#008">+</tt><br><tt style="color:#008">+(c) 2016, Aaron Christianson</tt><br><tt style="color:#008">+<a href="http://github.com/ninjaaron/fast-entry_points" target="_blank">http://github.com/ninjaaron/fast-entry_points</a></tt><br><tt style="color:#008">+'''</tt><br><tt style="color:#008">+from setuptools.command import easy_install</tt><br><tt style="color:#008">+import re</tt><br><tt style="color:#008">+TEMPLATE = '''\</tt><br><tt style="color:#008">+# -*- coding: utf-8 -*-</tt><br><tt style="color:#008">+# EASY-INSTALL-ENTRY-SCRIPT: '{3}','{4}','{5}'</tt><br><tt style="color:#008">+__requires__ = '{3}'</tt><br><tt style="color:#008">+import re</tt><br><tt style="color:#008">+import sys</tt><br><tt style="color:#008">+</tt><br><tt style="color:#008">+from {0} import {1}</tt><br><tt style="color:#008">+</tt><br><tt style="color:#008">+if __name__ == '__main__':</tt><br><tt style="color:#008">+ sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])</tt><br><tt style="color:#008">+ sys.exit({2}())'''</tt><br><tt style="color:#008">+</tt><br><tt style="color:#008">+</tt><br><tt style="color:#008">+@classmethod</tt><br><tt style="color:#008">+def get_args(cls, dist, header=None): # noqa: D205,D400</tt><br><tt style="color:#008">+ """</tt><br><tt style="color:#008">+ Yield write_script() argument tuples for a distribution's</tt><br><tt style="color:#008">+ console_scripts and gui_scripts entry points.</tt><br><tt style="color:#008">+ """</tt><br><tt style="color:#008">+ if header is None:</tt><br><tt style="color:#008">+ # pylint: disable=E1101</tt><br><tt style="color:#008">+ header = cls.get_header()</tt><br><tt style="color:#008">+ spec = str(dist.as_requirement())</tt><br><tt style="color:#008">+ for type_ in 'console', 'gui':</tt><br><tt style="color:#008">+ group = type_ + '_scripts'</tt><br><tt style="color:#008">+ for name, ep in dist.get_entry_map(group).items():</tt><br><tt style="color:#008">+ # ensure_safe_name</tt><br><tt style="color:#008">+ if re.search(r'[\\/]', name):</tt><br><tt style="color:#008">+ raise ValueError("Path separators not allowed in script names")</tt><br><tt style="color:#008">+ script_text = TEMPLATE.format(</tt><br><tt style="color:#008">+ ep.module_name, ep.attrs[0], '.'.join(ep.attrs),</tt><br><tt style="color:#008">+ spec, group, name)</tt><br><tt style="color:#008">+ # pylint: disable=E1101</tt><br><tt style="color:#008">+ args = cls._get_script_args(type_, name, header, script_text)</tt><br><tt style="color:#008">+ for res in args:</tt><br><tt style="color:#008">+ yield res</tt><br><tt style="color:#008">+</tt><br><tt style="color:#008">+</tt><br><tt style="color:#008">+# pylint: disable=E1101</tt><br><tt style="color:#008">+easy_install.ScriptWriter.get_args = get_args</tt><br><tt style="color:#008">+</tt><br><tt style="color:#008">+</tt><br><tt style="color:#008">+def main():</tt><br><tt style="color:#008">+ import os</tt><br><tt style="color:#008">+ import re</tt><br><tt style="color:#008">+ import shutil</tt><br><tt style="color:#008">+ import sys</tt><br><tt style="color:#008">+ dests = sys.argv[1:] or ['.']</tt><br><tt style="color:#008">+ filename = re.sub('\.pyc$', '.py', __file__)</tt><br><tt style="color:#008">+</tt><br><tt style="color:#008">+ for dst in dests:</tt><br><tt style="color:#008">+ shutil.copy(filename, dst)</tt><br><tt style="color:#008">+ manifest_path = os.path.join(dst, 'MANIFEST.in')</tt><br><tt style="color:#008">+ setup_path = os.path.join(dst, 'setup.py')</tt><br><tt style="color:#008">+</tt><br><tt style="color:#008">+ # Insert the include statement to MANIFEST.in if not present</tt><br><tt style="color:#008">+ with open(manifest_path, 'a+') as manifest:</tt><br><tt style="color:#008">+ manifest.seek(0)</tt><br><tt style="color:#008">+ manifest_content = manifest.read()</tt><br><tt style="color:#008">+ if 'include fastentrypoints.py' not in manifest_content:</tt><br><tt style="color:#008">+ manifest.write(('\n' if manifest_content else '') +</tt><br><tt style="color:#008">+ 'include fastentrypoints.py')</tt><br><tt style="color:#008">+</tt><br><tt style="color:#008">+ # Insert the import statement to setup.py if not present</tt><br><tt style="color:#008">+ with open(setup_path, 'a+') as setup:</tt><br><tt style="color:#008">+ setup.seek(0)</tt><br><tt style="color:#008">+ setup_content = setup.read()</tt><br><tt style="color:#008">+ if 'import fastentrypoints' not in setup_content:</tt><br><tt style="color:#008">+ setup.seek(0)</tt><br><tt style="color:#008">+ setup.truncate()</tt><br><tt style="color:#008">+ setup.write('import fastentrypoints\n' + setup_content)</tt><br></tt></div>
<br>
<hr>
<p align="center">To unsubscribe from the CCTBX-COMMIT list, click the following link:<br>
<a href="https://www.jiscmail.ac.uk/cgi-bin/webadmin?SUBED1=CCTBX-COMMIT&A=1" target="_blank">https://www.jiscmail.ac.uk/cgi-bin/webadmin?SUBED1=CCTBX-COMMIT&A=1</a>
</p></blockquote></div>