Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: replace develop command #22

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/setuptools_dso/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import os
from setuptools import setup as _setup
from .dsocmd import DSO, Extension, build_dso, build_ext, bdist_egg
from .develop import develop
from .runtime import dylink_prepare_dso, find_dso
from .probe import ProbeToolchain

Expand Down Expand Up @@ -48,6 +49,7 @@ def cmdclass_setdefault(name, klass, error=True):
cmdclass_setdefault('bdist_egg', bdist_egg)
cmdclass_setdefault('build_dso', build_dso)
cmdclass_setdefault('build_ext', build_ext)
cmdclass_setdefault('develop', develop, error=False)
try:
from .dsocmd import bdist_wheel
except ImportError:
Expand Down
113 changes: 113 additions & 0 deletions src/setuptools_dso/develop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Copyright 2022 Michael Davidsaver
# SPDX-License-Identifier: BSD
# See LICENSE

import sys
import os
from distutils import log
from distutils.dir_util import mkpath

from setuptools import Command
from setuptools.command.develop import develop as _develop

class link_develop(Command):
"""
Use of .egg-link (really 'easy-install.pth') doesn't work in conjunction with RPATH.
Further, simply sym-linking directories into the install tree doesn't work as path
traversal follows sym-links. eg. "adir/link2dir/../other/lib" does not reach "adir/other/lib".

So instead, we hijack the develop command entirely to install sym-links for each individual
file (.py and .so/dylib). This is not 100% as 'develop' must be re-run each time new files
are created in the source tree.
"""

description = _develop.description

# pull in the full set of options as we don't know which ones
# pip can/will use.

user_options = _develop.user_options
boolean_options = _develop.boolean_options

def initialize_options(self):
_develop.initialize_options(self) # evil!
self.uninstall = None
self.no_deps = None

def finalize_options(self):
pass

def run(self):
if self.uninstall:
self.run_uninstall()
else:
self.run_install()

def run_install(self):
log.info('setuptools_dso develop mode')

install_lib = self.get_finalized_command('install_lib')

# note: doc comment on get_inputs() claims the returned list is one-to-one
# with get_outputs(). However, this is only true if compile=False,
# otherwise get_outputs() emits bytecode files between .py and .so
install_lib.compile = False

install_lib.build()

for inp, out in zip(install_lib.get_inputs(), install_lib.get_outputs()):
assert os.path.basename(inp)==os.path.basename(out), (inp, out)

outdir = os.path.dirname(out)
inp = os.path.relpath(inp, outdir)

mkpath(outdir, verbose=self.verbose, dry_run=self.dry_run)

log.info('ln -s %s %s', inp, out)

# note: samefile() follows symlinks by default
if not (os.path.exists(out) and os.path.samefile(inp, out)) and not self.dry_run:
os.symlink(inp, out)

# TODO: need to create/install *.dist_info for "pip uninstall ..."
#dist_info = self.get_finalized_command('dist_info')
#dist_info.run()

def run_uninstall(self):
log.info('setuptools_dso develop mode, uninstall')

install_lib = self.get_finalized_command('install_lib')
# leave install_lib.compile (maybe) set so that bytecode is also removed

# From an abundance of caution, only removing known output files
# and empty directories containing them.

pkgdirs = set()
for out in install_lib.get_outputs():
pkgdirs.add(os.path.dirname(out))
if os.path.isfile(out):
log.info('rm %s', out)
if not self.dry_run:
os.remove(out)

# delete (longer) sub-directories first
pkgdirs = list(pkgdirs)
pkgdirs.sort()
pkgdirs.reverse()

for odir in pkgdirs:
if os.path.isdir(odir):
try:
if not self.dry_run:
os.rmdir(odir)
except OSError as e:
log.warn('Unable to remove directory: %s : %s', odir, e.args)
else:
log.info('rmdir %s', odir)


if sys.platform=='win32':
develop = _develop

else:
develop = link_develop