diff options
Diffstat (limited to 'configure.py')
-rwxr-xr-x | configure.py | 336 |
1 files changed, 185 insertions, 151 deletions
diff --git a/configure.py b/configure.py index 09efeec2a..b4f238b10 100755 --- a/configure.py +++ b/configure.py @@ -113,11 +113,34 @@ class Version(object): return None +class SourcePaths(object): + """ + A collection of paths defined by the project structure and + independent of user configurations. + All paths are relative to the base_dir, which may be relative as well (e.g. ".") + """ + + def __init__(self, base_dir): + self.base_dir = base_dir + self.doc_dir = os.path.join(self.base_dir, 'doc') + self.src_dir = os.path.join(self.base_dir, 'src') + + # dirs in src/ + self.build_data_dir = os.path.join(self.src_dir, 'build-data') + self.lib_dir = os.path.join(self.src_dir, 'lib') + self.python_dir = os.path.join(self.src_dir, 'python') + self.scripts_dir = os.path.join(self.src_dir, 'scripts') + + # dirs in src/build-data/ + self.sphinx_config_dir = os.path.join(self.build_data_dir, 'sphinx') + self.makefile_dir = os.path.join(self.build_data_dir, 'makefile') + + class BuildPaths(object): # pylint: disable=too-many-instance-attributes """ Constructor """ - def __init__(self, options, modules): + def __init__(self, source_paths, options, modules): self.build_dir = os.path.join(options.with_build_dir, 'build') self.libobj_dir = os.path.join(self.build_dir, 'obj', 'lib') @@ -143,9 +166,6 @@ class BuildPaths(object): # pylint: disable=too-many-instance-attributes self.public_headers = sorted(flatten([m.public_headers() for m in modules])) - self.doc_dir = os.path.join(options.base_dir, 'doc') - self.src_dir = os.path.join(options.base_dir, 'src') - def find_sources_in(basedir, srcdir): for (dirpath, _, filenames) in os.walk(os.path.join(basedir, srcdir)): for filename in filenames: @@ -158,11 +178,9 @@ class BuildPaths(object): # pylint: disable=too-many-instance-attributes if filename.endswith('.h') and not filename.startswith('.'): yield os.path.join(dirpath, filename) - self.cli_sources = list(find_sources_in(self.src_dir, 'cli')) - self.cli_headers = list(find_headers_in(self.src_dir, 'cli')) - self.test_sources = list(find_sources_in(self.src_dir, 'tests')) - - self.python_dir = os.path.join(options.src_dir, 'python') + self.cli_sources = list(find_sources_in(source_paths.src_dir, 'cli')) + self.cli_headers = list(find_headers_in(source_paths.src_dir, 'cli')) + self.test_sources = list(find_sources_in(source_paths.src_dir, 'tests')) def build_dirs(self): out = [ @@ -190,7 +208,7 @@ class BuildPaths(object): # pylint: disable=too-many-instance-attributes PKG_CONFIG_FILENAME = 'botan-%d.pc' % (Version.major) -def make_build_doc_commands(build_paths, options): +def make_build_doc_commands(source_paths, build_paths, options): def build_manual_command(src_dir, dst_dir): if options.with_sphinx: sphinx = 'sphinx-build -c $(SPHINX_CONFIG) $(SPHINX_OPTS) ' @@ -202,7 +220,7 @@ def make_build_doc_commands(build_paths, options): return '$(COPY) %s%s*.rst %s' % (src_dir, os.sep, dst_dir) cmds = [ - build_manual_command(os.path.join(build_paths.doc_dir, 'manual'), build_paths.doc_output_dir_manual) + build_manual_command(os.path.join(source_paths.doc_dir, 'manual'), build_paths.doc_output_dir_manual) ] if options.with_doxygen: cmds += ['doxygen %s%sbotan.doxy' % (build_paths.build_dir, os.sep)] @@ -212,6 +230,8 @@ def make_build_doc_commands(build_paths, options): def process_command_line(args): # pylint: disable=too-many-locals """ Handle command line options + Do not use logging in this method as command line options need to be + available before logging is setup. """ parser = optparse.OptionParser( @@ -242,8 +262,6 @@ def process_command_line(args): # pylint: disable=too-many-locals help='set compiler ABI flags', default='') - target_group.add_option('--chost', help=optparse.SUPPRESS_HELP) - target_group.add_option('--with-endian', metavar='ORDER', default=None, help='override byte order guess') @@ -1687,16 +1705,52 @@ class MakefileListsGenerator(object): src, self._cc.output_to_option) - def generate(self, var): + def generate(self): + out = {} for t in ['lib', 'cli', 'test']: obj_key = '%s_objs' % (t) src_list, src_dir = self._build_paths.src_info(t) src_list.sort() - var[obj_key] = makefile_list(self._objectfile_list(src_list, src_dir)) + out[obj_key] = makefile_list(self._objectfile_list(src_list, src_dir)) build_key = '%s_build_cmds' % (t) - var[build_key] = '\n'.join(self._build_commands(src_list, src_dir, t.upper())) + out[build_key] = '\n'.join(self._build_commands(src_list, src_dir, t.upper())) + return out + + +class HouseEccCurve(object): + def __init__(self, house_curve): + p = house_curve.split(",") + if len(p) != 4: + raise UserError('--house-curve must have 4 comma separated parameters. See --help') + # make sure TLS curve id is in reserved for private use range (0xFE00..0xFEFF) + curve_id = int(p[3], 16) + if curve_id < 0xfe00 or curve_id > 0xfeff: + raise UserError('TLS curve ID not in reserved range (see RFC 4492)') + + self._defines = [ + 'HOUSE_ECC_CURVE_NAME \"' + p[1] + '\"', + 'HOUSE_ECC_CURVE_OID \"' + p[2] + '\"', + 'HOUSE_ECC_CURVE_PEM ' + self._read_pem(filepath=p[0]), + 'HOUSE_ECC_CURVE_TLS_ID ' + hex(curve_id), + ] + + def defines(self): + return self._defines + + @staticmethod + def _read_pem(filepath): + try: + with open(filepath) as f: + lines = [line.rstrip() for line in f] + except IOError: + raise UserError("Error reading file '%s'" % filepath) + + for ndx, _ in enumerate(lines): + lines[ndx] = ' \"%s\"' % lines[ndx] + return "\\\n" + ' \\\n'.join(lines) -def create_template_vars(build_config, options, modules, cc, arch, osinfo): + +def create_template_vars(source_paths, build_config, options, modules, cc, arch, osinfo): """ Create the template variables needed to process the makefile, build.h, etc """ @@ -1741,29 +1795,6 @@ def create_template_vars(build_config, options, modules, cc, arch, osinfo): logging.warning('Unknown arch in innosetup_arch %s' % (arch)) return None - def read_pem(filename): - lines = [line.rstrip() for line in open(filename)] - for ndx, _ in enumerate(lines): - lines[ndx] = ''.join(('\"', lines[ndx], '\" \\', '\n')) - return ''.join(lines) - - def misc_config(): - opts = list() - if options.house_curve: - p = options.house_curve.split(",") - if len(p) < 4: - logging.error('Too few parameters to --in-house-curve') - # make sure TLS curve id is in reserved for private use range (0xFE00..0xFEFF) - curve_id = int(p[3], 16) - if curve_id < 0xfe00 or curve_id > 0xfeff: - logging.error('TLS curve ID not in reserved range (see RFC 4492)') - opts.append('HOUSE_ECC_CURVE_NAME \"' + p[1] + '\"') - opts.append('HOUSE_ECC_CURVE_OID \"' + p[2] + '\"') - opts.append('HOUSE_ECC_CURVE_PEM ' + read_pem(filename=p[0])) - opts.append('HOUSE_ECC_CURVE_TLS_ID ' + hex(curve_id)) - - return opts - def configure_command_line(): # Cut absolute path from main executable (e.g. configure.py or python interpreter) # to get the same result when configuring the same thing on different machines @@ -1783,9 +1814,9 @@ def create_template_vars(build_config, options, modules, cc, arch, osinfo): 'distribution_info': options.distribution_info, - 'base_dir': options.base_dir, - 'src_dir': options.src_dir, - 'doc_dir': build_config.doc_dir, + 'base_dir': source_paths.base_dir, + 'src_dir': source_paths.src_dir, + 'doc_dir': source_paths.doc_dir, 'command_line': configure_command_line(), 'local_config': slurp_file(options.local_config), @@ -1805,7 +1836,7 @@ def create_template_vars(build_config, options, modules, cc, arch, osinfo): 'out_dir': options.with_build_dir or os.path.curdir, 'build_dir': build_config.build_dir, - 'scripts_dir': os.path.join(build_config.src_dir, 'scripts'), + 'scripts_dir': source_paths.scripts_dir, 'build_shared_lib': options.build_shared_lib, @@ -1815,10 +1846,10 @@ def create_template_vars(build_config, options, modules, cc, arch, osinfo): 'doc_output_dir': build_config.doc_output_dir, - 'build_doc_commands': make_build_doc_commands(build_config, options), + 'build_doc_commands': make_build_doc_commands(source_paths, build_config, options), - 'python_dir': build_config.python_dir, - 'sphinx_config_dir': os.path.join(options.build_data, 'sphinx'), + 'python_dir': source_paths.python_dir, + 'sphinx_config_dir': source_paths.sphinx_config_dir, 'os': options.os, 'arch': options.arch, @@ -1874,10 +1905,13 @@ def create_template_vars(build_config, options, modules, cc, arch, osinfo): 'python_version': options.python_version, 'with_sphinx': options.with_sphinx, - - 'misc_config': make_cpp_macros(misc_config()) } + if options.house_curve: + variables['house_ecc_curve_defines'] = make_cpp_macros(HouseEccCurve(options.house_curve).defines()) + else: + variables['house_ecc_curve_defines'] = '' + if options.build_shared_lib: if osinfo.soname_pattern_base != None: @@ -1912,7 +1946,7 @@ def create_template_vars(build_config, options, modules, cc, arch, osinfo): variables['cli_post_link_cmd'] = '' variables['test_post_link_cmd'] = '' - MakefileListsGenerator(build_config, options, modules, cc, arch, osinfo).generate(variables) + variables.update(MakefileListsGenerator(build_config, options, modules, cc, arch, osinfo).generate()) if options.os == 'windows': if options.with_debug_info: @@ -1926,16 +1960,20 @@ def create_template_vars(build_config, options, modules, cc, arch, osinfo): # This can be made consistent over all platforms in the future variables['libname'] = 'botan-%d' % (Version.major) - variables["header_in"] = process_template(os.path.join(options.makefile_dir, 'header.in'), variables) + variables["header_in"] = process_template(os.path.join(source_paths.makefile_dir, 'header.in'), variables) if variables["makefile_style"] == "gmake": - variables["gmake_commands_in"] = process_template(os.path.join(options.makefile_dir, 'gmake_commands.in'), - variables) - variables["gmake_dso_in"] = process_template(os.path.join(options.makefile_dir, 'gmake_dso.in'), variables) \ - if options.build_shared_lib else '' - variables["gmake_coverage_in"] = process_template(os.path.join(options.makefile_dir, 'gmake_coverage.in'), - variables) \ - if options.with_coverage_info else '' + variables["gmake_commands_in"] = process_template( + os.path.join(source_paths.makefile_dir, 'gmake_commands.in'), + variables) + variables["gmake_dso_in"] = process_template( + os.path.join(source_paths.makefile_dir, 'gmake_dso.in'), + variables + ) if options.build_shared_lib else '' + variables["gmake_coverage_in"] = process_template( + os.path.join(source_paths.makefile_dir, 'gmake_coverage.in'), + variables + ) if options.with_coverage_info else '' return variables @@ -2463,6 +2501,84 @@ def have_program(program): logging.debug('Program %s not found' % (program)) return False + +class BotanConfigureLogHandler(logging.StreamHandler, object): + def emit(self, record): + # Do the default stuff first + super(BotanConfigureLogHandler, self).emit(record) + # Exit script if and ERROR or worse occurred + if record.levelno >= logging.ERROR: + sys.exit(1) + + +def setup_logging(options): + if options.verbose: + log_level = logging.DEBUG + elif options.quiet: + log_level = logging.WARNING + else: + log_level = logging.INFO + + lh = BotanConfigureLogHandler(sys.stdout) + lh.setFormatter(logging.Formatter('%(levelname) 7s: %(message)s')) + logging.getLogger().addHandler(lh) + logging.getLogger().setLevel(log_level) + + +def load_info_files(descr, search_dir, filename_matcher, class_t): + info = {} + + def filename_matches(filename): + if isinstance(filename_matcher, str): + return filename == filename_matcher + else: + return filename_matcher.match(filename) is not None + + for (dirpath, _, filenames) in os.walk(search_dir): + for filename in filenames: + filepath = os.path.join(dirpath, filename) + if filename_matches(filename): + info_obj = class_t(filepath) + info[info_obj.basename] = info_obj + + if info: + infotxt_basenames = ' '.join(sorted([key for key in info])) + logging.debug('Loaded %d %s files: %s' % (len(info), descr, infotxt_basenames)) + else: + logging.warning('Failed to load any %s files' % (descr)) + + return info + + +# Workaround for Windows systems where antivirus is enabled GH #353 +def robust_rmtree(path, max_retries=5): + for _ in range(max_retries): + try: + shutil.rmtree(path) + return + except OSError: + time.sleep(0.1) + + # Final attempt, pass any exceptions up to caller. + shutil.rmtree(path) + + +# Workaround for Windows systems where antivirus is enabled GH #353 +def robust_makedirs(directory, max_retries=5): + for _ in range(max_retries): + try: + os.makedirs(directory) + return + except OSError as e: + if e.errno == errno.EEXIST: + raise + else: + time.sleep(0.1) + + # Final attempt, pass any exceptions up to caller. + os.makedirs(directory) + + def main(argv=None): """ Main driver @@ -2471,28 +2587,9 @@ def main(argv=None): if argv is None: argv = sys.argv - class BotanConfigureLogHandler(logging.StreamHandler, object): - def emit(self, record): - # Do the default stuff first - super(BotanConfigureLogHandler, self).emit(record) - # Exit script if and ERROR or worse occurred - if record.levelno >= logging.ERROR: - sys.exit(1) - - lh = BotanConfigureLogHandler(sys.stdout) - lh.setFormatter(logging.Formatter('%(levelname) 7s: %(message)s')) - logging.getLogger().addHandler(lh) - options = process_command_line(argv[1:]) - def log_level(): - if options.verbose: - return logging.DEBUG - if options.quiet: - return logging.WARNING - return logging.INFO - - logging.getLogger().setLevel(log_level()) + setup_logging(options) logging.info('%s invoked with options "%s"' % ( argv[0], ' '.join(argv[1:]))) @@ -2503,37 +2600,13 @@ def main(argv=None): if options.os == "java": raise UserError("Jython detected: need --os and --cpu to set target") - options.base_dir = os.path.dirname(argv[0]) - options.src_dir = os.path.join(options.base_dir, 'src') - options.lib_dir = os.path.join(options.src_dir, 'lib') - - options.build_data = os.path.join(options.src_dir, 'build-data') - options.makefile_dir = os.path.join(options.build_data, 'makefile') + source_paths = SourcePaths(os.path.dirname(argv[0])) - def find_files_named(desired_name, in_path): - for (dirpath, _, filenames) in os.walk(in_path): - if desired_name in filenames: - yield os.path.join(dirpath, desired_name) - - modules = dict([(mod.basename, mod) for mod in - [ModuleInfo(info) for info in - find_files_named('info.txt', options.lib_dir)]]) + modules = load_info_files('Modules', source_paths.lib_dir, "info.txt", ModuleInfo) def load_build_data(descr, subdir, class_t): - info = {} - - subdir = os.path.join(options.build_data, subdir) - - for fsname in os.listdir(subdir): - if fsname.endswith('.txt'): - info[fsname.replace('.txt', '')] = class_t(os.path.join(subdir, fsname)) - if len(info) == 0: - logging.warning('Failed to load any %s files' % (descr)) - else: - infotxt_basenames = ' '.join(sorted([key for key in info])) - logging.debug('Loaded %d %s files (%s)' % (len(info), descr, infotxt_basenames)) - - return info + matcher = re.compile(r'[_a-z0-9]+\.txt$') + return load_info_files(descr, os.path.join(source_paths.build_data_dir, subdir), matcher, class_t) info_arch = load_build_data('CPU info', 'arch', ArchInfo) info_os = load_build_data('OS info', 'os', OsInfo) @@ -2558,15 +2631,6 @@ def main(argv=None): print(k) sys.exit(0) - if options.chost: - chost = options.chost.split('-') - - if options.cpu is None and len(chost) > 0: - options.cpu = chost[0] - - if options.os is None and len(chost) > 2: - options.os = '-'.join(chost[2:]) - if options.os is None: options.os = platform.system().lower() @@ -2673,44 +2737,17 @@ def main(argv=None): using_mods = [modules[modname] for modname in loaded_module_names] - build_config = BuildPaths(options, using_mods) + build_config = BuildPaths(source_paths, options, using_mods) build_config.public_headers.append(os.path.join(build_config.build_dir, 'build.h')) - template_vars = create_template_vars(build_config, options, using_mods, cc, arch, osinfo) + template_vars = create_template_vars(source_paths, build_config, options, using_mods, cc, arch, osinfo) - makefile_template = os.path.join(options.makefile_dir, '%s.in' % (template_vars['makefile_style'])) + makefile_template = os.path.join(source_paths.makefile_dir, '%s.in' % (template_vars['makefile_style'])) logging.debug('Using makefile template %s' % (makefile_template)) # Now begin the actual IO to setup the build - # Workaround for Windows systems where antivirus is enabled GH #353 - def robust_rmtree(path, max_retries=5): - for _ in range(max_retries): - try: - shutil.rmtree(path) - return - except OSError: - time.sleep(0.1) - - # Final attempt, pass any exceptions up to caller. - shutil.rmtree(path) - - # Workaround for Windows systems where antivirus is enabled GH #353 - def robust_makedirs(directory, max_retries=5): - for _ in range(max_retries): - try: - os.makedirs(directory) - return - except OSError as e: - if e.errno == errno.EEXIST: - raise - else: - time.sleep(0.1) - - # Final attempt, pass any exceptions up to caller. - os.makedirs(directory) - try: if options.clean_build_tree: robust_rmtree(build_config.build_dir) @@ -2726,16 +2763,13 @@ def main(argv=None): logging.error('Error while creating "%s": %s' % (build_dir, e)) def write_template(sink, template): - try: - f = open(sink, 'w') + with open(sink, 'w') as f: f.write(process_template(template, template_vars)) - finally: - f.close() def in_build_dir(p): return os.path.join(build_config.build_dir, p) def in_build_data(p): - return os.path.join(options.build_data, p) + return os.path.join(source_paths.build_data_dir, p) write_template(in_build_dir('build.h'), in_build_data('buildh.in')) write_template(in_build_dir('botan.doxy'), in_build_data('botan.doxy.in')) @@ -2773,7 +2807,7 @@ def main(argv=None): if options.amalgamation: amalgamation_cpp_files = AmalgamationGenerator(build_config, using_mods, options).generate() build_config.lib_sources = sorted(amalgamation_cpp_files) - MakefileListsGenerator(build_config, options, using_mods, cc, arch, osinfo).generate(template_vars) + template_vars.update(MakefileListsGenerator(build_config, options, using_mods, cc, arch, osinfo).generate()) if options.with_bakefile: gen_bakefile(build_config, options, template_vars['link_to']) |