1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-06 12:41:29 +00:00
Files
nmap/ndiff/setup.py
2020-10-15 17:17:34 +00:00

315 lines
11 KiB
Python

#!/usr/bin/env python
import errno
import sys
import os
import os.path
import re
from stat import ST_MODE
import distutils.command
import distutils.command.install
import distutils.core
import distutils.cmd
import distutils.errors
from distutils import log
from distutils.command.install import install
APP_NAME = "ndiff"
# The name of the file used to record the list of installed files, so that the
# uninstall command can remove them.
INSTALLED_FILES_NAME = "INSTALLED_FILES"
# path_startswith and path_strip_prefix are used to deal with the installation
# root (--root option, also known as DESTDIR).
def path_startswith(path, prefix):
"""Returns True if path starts with prefix. It's a little more intelligent
than str.startswith because it normalizes the paths to remove multiple
directory separators and down-up traversals."""
path = os.path.normpath(path)
prefix = os.path.normpath(prefix)
return path.startswith(prefix)
def path_strip_prefix(path, prefix):
"""Return path stripped of its directory prefix if it starts with prefix,
otherwise return path unmodified. This only works correctly with Unix
paths; for example it will not replace the drive letter on a Windows path.
Examples:
>>> path_strip_prefix('/tmp/destdir/usr/bin', '/tmp/destdir')
'/usr/bin'
>>> path_strip_prefix('/tmp/../tmp/destdir/usr/bin', '/tmp///destdir')
'/usr/bin'
>>> path_strip_prefix('/etc', '/tmp/destdir')
'/etc'
>>> path_strip_prefix('/etc', '/')
'/etc'
>>> path_strip_prefix('/etc', '')
'/etc'
"""
absolute = os.path.isabs(path)
path = os.path.normpath(path)
prefix = os.path.normpath(prefix)
if path.startswith(prefix) and prefix != os.sep:
path = path[len(prefix):]
# Absolute paths must remain absolute and relative paths must remain
# relative.
assert os.path.isabs(path) == absolute
return path
###############################################################################
# Distutils subclasses
class null_command(distutils.cmd.Command):
"""This is a dummy distutils command that does nothing. We use it to
replace the install_egg_info and avoid installing a .egg-info file, because
there's no option to disable that."""
def initialize_options(self):
pass
def finalize_options(self):
pass
def get_outputs(self):
return ()
def run(self):
pass
class checked_install(distutils.command.install.install):
"""This is a wrapper around the install command that checks for an error
caused by not having the python-dev package installed. By default,
distutils gives a misleading error message: "invalid Python installation."
"""
def finalize_options(self):
# Ubuntu's python2.6-2.6.4-0ubuntu3 package changes sys.prefix in
# install.finalize_options when sys.prefix is "/usr/local" (our
# default). Because we need the unchanged value later, remember it
# here.
self.saved_prefix = sys.prefix
try:
distutils.command.install.install.finalize_options(self)
except distutils.errors.DistutilsPlatformError, e:
raise distutils.errors.DistutilsPlatformError(str(e) + """
Installing your distribution's python-dev package may solve this problem.""")
def set_modules_path(self):
app_file_name = os.path.join(self.install_scripts, APP_NAME)
# Find where the modules are installed. distutils will put them in
# self.install_lib, but that path can contain the root (DESTDIR), so we
# must strip it off if necessary.
modules_dir = self.install_lib
if self.root is not None:
modules_dir = path_strip_prefix(modules_dir, self.root)
app_file = open(app_file_name, "r")
lines = app_file.readlines()
app_file.close()
for i in range(len(lines)):
if re.match(r'^INSTALL_LIB =', lines[i]):
lines[i] = "INSTALL_LIB = %s\n" % repr(modules_dir)
break
else:
raise ValueError(
"INSTALL_LIB replacement not found in %s" % app_file_name)
app_file = open(app_file_name, "w")
app_file.writelines(lines)
app_file.close()
def run(self):
install.run(self)
# These below are from Zenmap. We're only using set_modules_path right now, but
# we might consider whether the others would be useful (or, if not, whether we
# should remove them from Zenmap).
# self.set_perms()
self.set_modules_path()
# self.fix_paths()
self.create_uninstaller()
self.write_installed_files()
def get_installed_files(self):
"""Return a list of installed files and directories, each prefixed with
the installation root if given. The list of installed directories
doesn't come from distutils so it may be incomplete."""
installed_files = self.get_outputs()
for package in self.distribution.py_modules:
dir = package.replace(".", "/")
installed_files.append(os.path.join(self.install_lib, dir))
installed_files.append(
os.path.join(self.install_scripts, "uninstall_" + APP_NAME))
return installed_files
def create_uninstaller(self):
uninstaller_filename = os.path.join(
self.install_scripts, "uninstall_" + APP_NAME)
uninstaller = """\
#!/usr/bin/env python
import errno, os, os.path, sys
print 'Uninstall %(name)s'
answer = raw_input('Are you sure that you want to uninstall '
'%(name)s (yes/no) ')
if answer != 'yes' and answer != 'y':
print 'Not uninstalling.'
sys.exit(0)
""" % {'name': APP_NAME}
installed_files = []
for output in self.get_installed_files():
if self.root is not None:
# If we have a root (DESTDIR), we need to strip it off the
# front of paths so the uninstaller runs on the target host.
# The path manipulations are tricky, but made easier because
# the uninstaller only has to run on Unix.
if not path_startswith(output, self.root):
# This should never happen (everything gets installed
# inside the root), but if it does, be safe and don't
# delete anything.
uninstaller += ("print '%s was not installed inside "
"the root %s; skipping.'\n" % (output, self.root))
continue
output = path_strip_prefix(output, self.root)
assert os.path.isabs(output)
installed_files.append(output)
uninstaller += """\
INSTALLED_FILES = (
"""
for file in installed_files:
uninstaller += " %s,\n" % repr(file)
uninstaller += """\
)
# Split the list into lists of files and directories.
files = []
dirs = []
for path in INSTALLED_FILES:
if os.path.isfile(path) or os.path.islink(path):
files.append(path)
elif os.path.isdir(path):
dirs.append(path)
# Delete the files.
for file in files:
print "Removing '%s'." % file
try:
os.remove(file)
except OSError, e:
print >> sys.stderr, ' Error: %s.' % str(e)
# Delete the directories. First reverse-sort the normalized paths by
# length so that child directories are deleted before their parents.
dirs = [os.path.normpath(dir) for dir in dirs]
dirs.sort(key = len, reverse = True)
for dir in dirs:
try:
print "Removing the directory '%s'." % dir
os.rmdir(dir)
except OSError, e:
if e.errno == errno.ENOTEMPTY:
print "Directory '%s' not empty; not removing." % dir
else:
print >> sys.stderr, str(e)
"""
uninstaller_file = open(uninstaller_filename, 'w')
uninstaller_file.write(uninstaller)
uninstaller_file.close()
# Set exec bit for uninstaller
mode = ((os.stat(uninstaller_filename)[ST_MODE]) | 0555) & 07777
os.chmod(uninstaller_filename, mode)
def write_installed_files(self):
"""Write a list of installed files for use by the uninstall command.
This is similar to what happens with the --record option except that it
doesn't strip off the installation root, if any. File names containing
newline characters are not handled."""
if INSTALLED_FILES_NAME == self.record:
distutils.log.warn("warning: installation record is overwriting "
"--record file '%s'." % self.record)
with open(INSTALLED_FILES_NAME, "w") as f:
for output in self.get_installed_files():
assert "\n" not in output
print >> f, output
class my_uninstall(distutils.cmd.Command):
"""A distutils command that performs uninstallation. It reads the list of
installed files written by the install command."""
command_name = "uninstall"
description = "uninstall installed files recorded in '%s'" % (
INSTALLED_FILES_NAME)
user_options = []
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
# Read the list of installed files.
try:
f = open(INSTALLED_FILES_NAME, "r")
except IOError, e:
if e.errno == errno.ENOENT:
log.error("Couldn't open the installation record '%s'. "
"Have you installed yet?" % INSTALLED_FILES_NAME)
return
installed_files = [file.rstrip("\n") for file in f.readlines()]
f.close()
# Delete the installation record too.
installed_files.append(INSTALLED_FILES_NAME)
# Split the list into lists of files and directories.
files = []
dirs = []
for path in installed_files:
if os.path.isfile(path) or os.path.islink(path):
files.append(path)
elif os.path.isdir(path):
dirs.append(path)
# Delete the files.
for file in files:
log.info("Removing '%s'." % file)
try:
if not self.dry_run:
os.remove(file)
except OSError, e:
log.error(str(e))
# Delete the directories. First reverse-sort the normalized paths by
# length so that child directories are deleted before their parents.
dirs = [os.path.normpath(dir) for dir in dirs]
dirs.sort(key=len, reverse=True)
for dir in dirs:
try:
log.info("Removing the directory '%s'." % dir)
if not self.dry_run:
os.rmdir(dir)
except OSError, e:
if e.errno == errno.ENOTEMPTY:
log.info("Directory '%s' not empty; not removing." % dir)
else:
log.error(str(e))
distutils.core.setup(name=u"ndiff", scripts=[u"scripts/ndiff"],
py_modules=[u"ndiff"],
data_files=[(u"share/man/man1", [u"docs/ndiff.1"])],
cmdclass={
"install_egg_info": null_command,
"install": checked_install,
"uninstall": my_uninstall
})