[Distutils] Install a script to <prefix>/sbin instead of <prefix>/bin
Michael Jansen
info at michael-jansen.biz
Mon Dec 2 15:14:52 CET 2013
Hi
I am currently working on the cobbler (http://cobblerd.org) setup.py and trying to improve it. It
currently is only capable of installing to /usr for several reasons. Since i would like to have
virtualenv support when running it i am trying to change that. While doing that i managed to meet
one of the standard problems with distutils/setuptools - a search returns results of 2004 or older -
which still seems to be unsolved.
I would be willing to change that but my recent research leaves me a bit confused with all that
setuptools is not dead, distlibs, tuf and wheels stuff.
So here is the question. If i am willing to do the work is it possible to add something like
sbin_scripts (or admin_scripts?) to distutils?
I locally solved the problem like that http://pastebin.kde.org/pqrwrud1p (or attached).
The implementation expands INSTALL_SCHEMA and reuses install_scripts to install those
sbin_scripts too. That could be debatable. Perhaps a dedicated build|install_xxx_scripts command.
I want to add that fhs also has a libexec folder (usually /usr/lib/<project>/libexec these days)
destined for scripts that are supposed to be only called by other scripts/programs not manually.
And cobbler for installs python scripts for modwsgi into /srv/www/cobbler/ . So i guess just adding
sbin_scripts is not really the solution. Perhaps something more flexible is needed.
Or should i wait for nextgen distlib based swiss knife python build tool and for now keep my local
additions (there are more).
--
Michael Jansen
http://michael-jansen.biz
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/distutils-sig/attachments/20131202/664d1719/attachment-0001.html>
-------------- next part --------------
from distutils.command import install as dist_install
from distutils.command.build import build as _build
from distutils.command.build_scripts import build_scripts as _build_scripts
from distutils.command.build_scripts import convert_path, newer, first_line_re
from setuptools.command.install import install as _install
from setuptools.command.install_scripts import install_scripts as _install_scripts
from setuptools import setup as _setup
from setuptools.dist import Distribution as _Distribution
from stat import ST_MODE
import sys
import os
from distutils import log
if sys.version < "2.2":
dist_install.WINDOWS_SCHEME['sbin_scripts'] = '$base/Scripts'
else:
dist_install.WINDOWS_SCHEME['sbin_scripts'] = '$base/Scripts'
dist_install.INSTALL_SCHEMES['unix_prefix']['sbin_scripts'] = '$base/sbin'
dist_install.INSTALL_SCHEMES['unix_home']['sbin_scripts'] = '$base/sbin'
dist_install.INSTALL_SCHEMES['unix_user']['sbin_scripts'] = '$userbase/sbin'
dist_install.INSTALL_SCHEMES['nt_user']['sbin_scripts'] = '$userbase/Scripts'
dist_install.INSTALL_SCHEMES['os2']['sbin_scripts'] = '$base/Scripts'
dist_install.INSTALL_SCHEMES['os2_home']['sbin_scripts'] = '$userbase/sbin'
# The keys to an installation scheme; if any new types of files are to be
# installed, be sure to add an entry to every installation scheme above,
# and to SCHEME_KEYS here.
dist_install.SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data') + ('sbin_scripts',)
class install(_install):
"""Enhance install target aimed for inclusion upstream."""
user_options = _install.user_options + [
('install-sbin-scripts=', None, "installation directory for sbin scripts")
]
def initialize_options(self):
_install.initialize_options(self)
self.install_sbin_scripts = None
def finalize_options(self):
_install.finalize_options(self)
if self.root is not None:
self.change_roots('sbin_scripts')
def finalize_unix(self):
_install.finalize_unix(self)
if self.install_base is not None or self.install_platbase is not None:
if self.install_sbin_scripts is None:
raise DistutilsOptionError, \
("install-base or install-platbase supplied, but "
"installation scheme is incomplete")
return
def finalize_other(self):
_install.finalize_other(self)
# Nothing else to do here
def expand_dirs(self):
_install.expand_dirs(self)
self._expand_attrs(['install_sbin_scripts'])
# :TODO:
# In run() add it to rejectdirs
class install_scripts(_install_scripts):
user_options = _install_scripts.user_options + [
('install-sbin-dir=', 'd', "directory to install sbin scripts to")
]
def initialize_options(self):
_install_scripts.initialize_options(self)
self.install_sbin_dir = None
self.build_sbin_dir = None
def finalize_options (self):
_install_scripts.finalize_options(self)
self.set_undefined_options(
'build',
('build_sbin_scripts', 'build_sbin_dir'))
self.set_undefined_options(
'install',
('install_sbin_scripts', 'install_sbin_dir')
)
def run(self):
_install_scripts.run(self)
print self.build_sbin_dir
print self.install_sbin_dir
self.outfiles = self.copy_tree(self.build_sbin_dir, self.install_sbin_dir)
if os.name == 'posix':
# Set the executable bits (owner, group, and world) on
# all the scripts we just installed.
for file in self.get_outputs():
if self.dry_run:
log.info("changing mode of %s", file)
else:
mode = ((os.stat(file)[ST_MODE]) | 0555) & 07777
log.info("changing mode of %s to %o", file, mode)
os.chmod(file, mode)
class build(_build):
user_options = _build_scripts.user_options + [
('build-sbin-scripts=', 'd', "directory to \"build\" (copy) to")
]
def initialize_options(self):
_build.initialize_options(self)
self.build_sbin_scripts = None
def finalize_options(self):
_build.finalize_options(self)
self.build_sbin_scripts = None
if self.build_sbin_scripts is None:
self.build_sbin_scripts = os.path.join(self.build_base,
'sbin_scripts-' + sys.version[0:3])
class build_scripts(_build_scripts):
user_options = _build_scripts.user_options + [
('build-sbin-dir=', 'd', "directory to \"build\" (copy) to")
]
def initialize_options(self):
_build_scripts.initialize_options(self)
self.build_sbin_dir = None
def finalize_options(self):
_build_scripts.finalize_options(self)
self.set_undefined_options(
'build',
('build_sbin_scripts', 'build_sbin_dir')
)
self.sbin_scripts = self.distribution.sbin_scripts
def copy_scripts (self):
"""Copy each script listed in 'self.scripts'; if it's marked as a
Python script in the Unix way (first line matches 'first_line_re',
ie. starts with "\#!" and contains "python"), then adjust the first
line to refer to the current Python interpreter as we copy.
"""
_build_scripts.copy_scripts(self)
print "HERE"
_sysconfig = __import__('sysconfig')
self.mkpath(self.build_sbin_dir)
outfiles = []
for script in self.sbin_scripts:
adjust = 0
script = convert_path(script)
outfile = os.path.join(self.build_sbin_dir, os.path.basename(script))
outfiles.append(outfile)
if not self.force and not newer(script, outfile):
log.debug("not copying %s (up-to-date)", script)
continue
# Always open the file, but ignore failures in dry-run mode --
# that way, we'll get accurate feedback if we can read the
# script.
try:
f = open(script, "r")
except IOError:
if not self.dry_run:
raise
f = None
else:
first_line = f.readline()
if not first_line:
self.warn("%s is an empty file (skipping)" % script)
continue
match = first_line_re.match(first_line)
if match:
adjust = 1
post_interp = match.group(1) or ''
if adjust:
log.info("copying and adjusting %s -> %s", script,
self.build_dir)
if not self.dry_run:
outf = open(outfile, "w")
if not _sysconfig.is_python_build():
outf.write("#!%s%s\n" %
(self.executable,
post_interp))
else:
outf.write("#!%s%s\n" %
(os.path.join(
_sysconfig.get_config_var("BINDIR"),
"python%s%s" % (_sysconfig.get_config_var("VERSION"),
_sysconfig.get_config_var("EXE"))),
post_interp))
outf.writelines(f.readlines())
outf.close()
if f:
f.close()
else:
if f:
f.close()
self.copy_file(script, outfile)
if os.name == 'posix':
for file in outfiles:
if self.dry_run:
log.info("changing mode of %s", file)
else:
oldmode = os.stat(file)[ST_MODE] & 07777
newmode = (oldmode | 0555) & 07777
if newmode != oldmode:
log.info("changing mode of %s from %o to %o",
file, oldmode, newmode)
os.chmod(file, newmode)
class Distribution(_Distribution):
def __init__(self, *args, **kwargs):
self.sbin_scripts = []
_Distribution.__init__(self, *args, **kwargs)
def setup(*args, **kwargs):
commands = {
'install': install,
'install_scripts': install_scripts,
'build': build,
'build_scripts': build_scripts
}
if 'cmdclass' in kwargs:
for name in commands.keys():
if name not in kwargs['cmdclass']:
kwargs['cmdclass'][name] = commands[name]
_setup(*args, **kwargs)
More information about the Distutils-SIG
mailing list