aboutsummaryrefslogtreecommitdiffstats
path: root/src/scripts
diff options
context:
space:
mode:
authorZoltan Gyarmati <[email protected]>2017-07-05 02:24:12 +0200
committerSimon Warta <[email protected]>2017-08-04 11:49:13 +0200
commitf09289c293807e51dfd3e4b5fff5a4d48d75f754 (patch)
tree1ab1039e51297fbb9e69e02061fe7a1f425a2715 /src/scripts
parent8a29dc8209c6e93581075bbc4c39ff5bf0cdace5 (diff)
Turn --destdir option into an environment variable, fixes #1101, fixes #996, supersedes #997
This commit removes the (broken) --destdir command line option and introduces the DESTDIR environment variable for the 'make install' target. When it's set, the installation will take place in the DESTDIR directory, under the prefix set by --prefix, with all of the internal references (pkgconfig file, etc) containing only the prefix. This behavior is more standard (as in autotools and co.), so makes packaging easier. Based on work of danimo and zgyarmati
Diffstat (limited to 'src/scripts')
-rwxr-xr-xsrc/scripts/install.py100
-rwxr-xr-xsrc/scripts/python_uniitests.py31
2 files changed, 105 insertions, 26 deletions
diff --git a/src/scripts/install.py b/src/scripts/install.py
index 601502657..1875bd62d 100755
--- a/src/scripts/install.py
+++ b/src/scripts/install.py
@@ -33,7 +33,7 @@ def parse_command_line(args):
parser.add_option_group(build_group)
install_group = optparse.OptionGroup(parser, 'Installation options')
- install_group.add_option('--destdir', default='/usr/local',
+ install_group.add_option('--prefix', default='/usr/local',
help='Set output directory (default %default)')
install_group.add_option('--bindir', default='bin', metavar='DIR',
help='Set binary subdir (default %default)')
@@ -63,6 +63,53 @@ def parse_command_line(args):
return (options, args)
+
+def prepend_destdir(path):
+ """
+ Needed because os.path.join() discards the first path if the
+ second one is absolute, which is usually the case here. Still, we
+ want relative paths to work and leverage the os awareness of
+ os.path.join().
+ """
+ destdir = os.environ.get('DESTDIR', "")
+
+ if destdir != "":
+ #DESTDIR is non-empty, but we cannot join all prefix paths.
+
+ #These will be rejected via an exception:
+ # C:/foo
+ # C:foo
+ # \\foo (Python >3.1 only)
+ # \\foo\bar (Python >3.1 only)
+ # ../somewhere/else
+
+ #These will be normalized to a relative path and joined with DESTDIR:
+ # /absolute/dir
+ # relative/dir
+ # /dir/with/../inside
+ # ./relative/to/me
+ # ~/botan-install-test
+
+ # ".." makes no sense, as it would certainly escape the DESTDIR prefix
+ if path.startswith(".."):
+ raise Exception('With DESTDIR set, a prefix starting in ".." would escape the destdir. Aborting.')
+
+ # Will only trigger on Windows, see the splitdrive() doc for details
+ drive, _ = os.path.splitdrive(path)
+ if drive != "":
+ raise Exception('DESTDIR set, but drive or UNC detected in prefix path. Aborting.')
+
+ # resolved ~, ~user
+ path = os.path.expanduser(path)
+ # native slashes, ".." inside (not in front of) pathes normalized
+ path = os.path.normpath(path)
+ # Remove / or \ prefixes if existent to accomodate for os.path.join()
+ path = path.lstrip(os.path.sep)
+ path = os.path.join(destdir, path)
+
+ return path
+
+
def makedirs(dirname, exist_ok=True):
try:
logging.debug('Creating directory %s' % (dirname))
@@ -104,8 +151,8 @@ def main(args):
shutil.copyfile(src, dst)
def copy_executable(src, dst):
- logging.debug('Copying %s to %s' % (src, dst))
copy_file(src, dst)
+ logging.debug('Make %s executable' % dst)
os.chmod(dst, exe_mode)
with open(os.path.join(options.build_dir, 'build_config.json')) as f:
@@ -129,12 +176,12 @@ def main(args):
target_os = cfg['os']
build_shared_lib = bool(cfg['build_shared_lib'])
- bin_dir = os.path.join(options.destdir, options.bindir)
- lib_dir = os.path.join(options.destdir, options.libdir)
- target_doc_dir = os.path.join(options.destdir,
+ bin_dir = os.path.join(options.prefix, options.bindir)
+ lib_dir = os.path.join(options.prefix, options.libdir)
+ target_doc_dir = os.path.join(options.prefix,
options.docdir,
'botan-%d.%d.%d' % (ver_major, ver_minor, ver_patch))
- target_include_dir = os.path.join(options.destdir,
+ target_include_dir = os.path.join(options.prefix,
options.includedir,
'botan-%d' % (ver_major),
'botan')
@@ -145,8 +192,8 @@ def main(args):
else:
app_exe = process_template('botan%{program_suffix}')
- for d in [options.destdir, lib_dir, bin_dir, target_doc_dir, target_include_dir]:
- makedirs(d)
+ for d in [options.prefix, lib_dir, bin_dir, target_doc_dir, target_include_dir]:
+ makedirs(prepend_destdir(d))
build_include_dir = os.path.join(options.build_dir, 'include', 'botan')
@@ -154,42 +201,43 @@ def main(args):
if include == 'internal':
continue
copy_file(os.path.join(build_include_dir, include),
- os.path.join(target_include_dir, include))
+ prepend_destdir(os.path.join(target_include_dir, include)))
build_external_include_dir = os.path.join(options.build_dir, 'include', 'external')
for include in sorted(os.listdir(build_external_include_dir)):
copy_file(os.path.join(build_external_include_dir, include),
- os.path.join(target_include_dir, include))
+ prepend_destdir(os.path.join(target_include_dir, include)))
static_lib = process_template('%{lib_prefix}%{libname}.%{static_suffix}')
copy_file(os.path.join(out_dir, static_lib),
- os.path.join(lib_dir, os.path.basename(static_lib)))
+ prepend_destdir(os.path.join(lib_dir, os.path.basename(static_lib))))
if build_shared_lib:
if target_os == "windows":
libname = process_template('%{libname}')
soname_base = libname + '.dll'
copy_executable(os.path.join(out_dir, soname_base),
- os.path.join(lib_dir, soname_base))
+ prepend_destdir(os.path.join(lib_dir, soname_base)))
else:
soname_patch = process_template('%{soname_patch}')
soname_abi = process_template('%{soname_abi}')
soname_base = process_template('%{soname_base}')
copy_executable(os.path.join(out_dir, soname_patch),
- os.path.join(lib_dir, soname_patch))
+ prepend_destdir(os.path.join(lib_dir, soname_patch)))
if target_os != "openbsd":
prev_cwd = os.getcwd()
try:
- os.chdir(lib_dir)
+ os.chdir(prepend_destdir(lib_dir))
force_symlink(soname_patch, soname_abi)
force_symlink(soname_patch, soname_base)
finally:
os.chdir(prev_cwd)
- copy_executable(os.path.join(out_dir, app_exe), os.path.join(bin_dir, app_exe))
+ copy_executable(os.path.join(out_dir, app_exe),
+ prepend_destdir(os.path.join(bin_dir, app_exe)))
# On Darwin, if we are using shared libraries and we install, we should fix
# up the library name, otherwise the botan command won't work; ironically
@@ -206,29 +254,31 @@ def main(args):
os.path.join(bin_dir, app_exe)])
if 'botan_pkgconfig' in cfg:
- pkgconfig_dir = os.path.join(options.destdir, options.libdir, options.pkgconfigdir)
- makedirs(pkgconfig_dir)
+ pkgconfig_dir = os.path.join(options.prefix, options.libdir, options.pkgconfigdir)
+ makedirs(prepend_destdir(pkgconfig_dir))
copy_file(cfg['botan_pkgconfig'],
- os.path.join(pkgconfig_dir, os.path.basename(cfg['botan_pkgconfig'])))
+ prepend_destdir(os.path.join(pkgconfig_dir, os.path.basename(cfg['botan_pkgconfig']))))
if 'ffi' in cfg['mod_list'].split('\n'):
for ver in cfg['python_version'].split(','):
py_lib_path = os.path.join(lib_dir, 'python%s' % (ver), 'site-packages')
logging.debug('Installing python module to %s' % (py_lib_path))
- makedirs(py_lib_path)
+ makedirs(prepend_destdir(py_lib_path))
py_dir = cfg['python_dir']
for py in os.listdir(py_dir):
- copy_file(os.path.join(py_dir, py), os.path.join(py_lib_path, py))
+ copy_file(os.path.join(py_dir, py), prepend_destdir(os.path.join(py_lib_path, py)))
- shutil.rmtree(target_doc_dir, True)
- shutil.copytree(cfg['doc_output_dir'], target_doc_dir)
+ shutil.rmtree(prepend_destdir(target_doc_dir), True)
+ shutil.copytree(cfg['doc_output_dir'], prepend_destdir(target_doc_dir))
for f in [f for f in os.listdir(cfg['doc_dir']) if f.endswith('.txt')]:
- copy_file(os.path.join(cfg['doc_dir'], f), os.path.join(target_doc_dir, f))
+ copy_file(os.path.join(cfg['doc_dir'], f), prepend_destdir(os.path.join(target_doc_dir, f)))
- copy_file(os.path.join(cfg['base_dir'], 'license.txt'), os.path.join(target_doc_dir, 'license.txt'))
- copy_file(os.path.join(cfg['base_dir'], 'news.rst'), os.path.join(target_doc_dir, 'news.txt'))
+ copy_file(os.path.join(cfg['base_dir'], 'license.txt'),
+ prepend_destdir(os.path.join(target_doc_dir, 'license.txt')))
+ copy_file(os.path.join(cfg['base_dir'], 'news.rst'),
+ prepend_destdir(os.path.join(target_doc_dir, 'news.txt')))
logging.info('Botan %s installation complete', cfg['version'])
return 0
diff --git a/src/scripts/python_uniitests.py b/src/scripts/python_uniitests.py
index fff32e03f..c804fae83 100755
--- a/src/scripts/python_uniitests.py
+++ b/src/scripts/python_uniitests.py
@@ -10,13 +10,14 @@ Requires Python 3.
Botan is released under the Simplified BSD License (see license.txt)
"""
+import os
import sys
import unittest
sys.path.append("../..") # Botan repo root
from configure import CompilerDetector # pylint: disable=wrong-import-position
from configure import ModulesChooser # pylint: disable=wrong-import-position
-
+from install import prepend_destdir # pylint: disable=wrong-import-position
class CompilerDetection(unittest.TestCase):
@@ -269,6 +270,34 @@ class ModulesChooserResolveDependencies(unittest.TestCase):
self.assertEqual(modules, set(["G", "A", "C", "E"]))
+class PrependDestdir(unittest.TestCase):
+ def test_base(self):
+ os.environ["DESTDIR"] = "/"
+ self.assertEqual(prepend_destdir("/home/me"), "/home/me")
+ self.assertEqual(prepend_destdir("relative_path"), "/relative_path")
+ self.assertEqual(prepend_destdir("./relative_path"), "/relative_path")
+ self.assertEqual(prepend_destdir("relative/sub"), "/relative/sub")
+
+ self.assertEqual(prepend_destdir("/home/me/"), "/home/me")
+ self.assertEqual(prepend_destdir("relative_path/"), "/relative_path")
+
+ self.assertEqual(prepend_destdir("/home/me/../me2"), "/home/me2")
+ self.assertEqual(prepend_destdir("relative/sub/../sub2"), "/relative/sub2")
+
+ os.environ["DESTDIR"] = "/opt"
+ self.assertEqual(prepend_destdir("/home/me"), "/opt/home/me")
+ self.assertEqual(prepend_destdir("relative_path"), "/opt/relative_path")
+ self.assertEqual(prepend_destdir("./relative_path"), "/opt/relative_path")
+ self.assertEqual(prepend_destdir("relative/sub"), "/opt/relative/sub")
+
+ self.assertEqual(prepend_destdir("/home/me/"), "/opt/home/me")
+ self.assertEqual(prepend_destdir("relative_path/"), "/opt/relative_path")
+
+ self.assertEqual(prepend_destdir("/home/me/../me2"), "/opt/home/me2")
+ self.assertEqual(prepend_destdir("relative/sub/../sub2"), "/opt/relative/sub2")
+
+
+
if __name__ == '__main__':
unittest.TestCase.longMessage = True
unittest.main()