diff options
author | Zoltan Gyarmati <[email protected]> | 2017-07-05 02:24:12 +0200 |
---|---|---|
committer | Simon Warta <[email protected]> | 2017-08-04 11:49:13 +0200 |
commit | f09289c293807e51dfd3e4b5fff5a4d48d75f754 (patch) | |
tree | 1ab1039e51297fbb9e69e02061fe7a1f425a2715 /src/scripts | |
parent | 8a29dc8209c6e93581075bbc4c39ff5bf0cdace5 (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-x | src/scripts/install.py | 100 | ||||
-rwxr-xr-x | src/scripts/python_uniitests.py | 31 |
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() |