aboutsummaryrefslogtreecommitdiffstats
path: root/configure.py
diff options
context:
space:
mode:
Diffstat (limited to 'configure.py')
-rwxr-xr-xconfigure.py320
1 files changed, 163 insertions, 157 deletions
diff --git a/configure.py b/configure.py
index 703b85699..528f121ab 100755
--- a/configure.py
+++ b/configure.py
@@ -4,7 +4,7 @@
Configuration program for botan
(C) 2009,2010,2011,2012,2013,2014,2015,2016,2017 Jack Lloyd
-(C) 2015,2016 Simon Warta (Kullo GmbH)
+(C) 2015,2016,2017 Simon Warta (Kullo GmbH)
Botan is released under the Simplified BSD License (see license.txt)
@@ -51,17 +51,46 @@ def chunks(l, n):
for i in range(0, len(l), n):
yield l[i:i+n]
-def get_vc_revision():
- def get_vc_revision_impl(cmdlist):
- try:
- cmdname = cmdlist[0]
+class Version(object):
+ """
+ Version information are all static members
+ """
+ major = botan_version.release_major
+ minor = botan_version.release_minor
+ patch = botan_version.release_patch
+ so_rev = botan_version.release_so_abi_rev
+ release_type = botan_version.release_type
+ datestamp = botan_version.release_datestamp
+ packed = major * 1000 + minor # Used on Darwin for dylib versioning
+ _vc_rev = None
+
+ @staticmethod
+ def as_string():
+ return '%d.%d.%d' % (Version.major, Version.minor, Version.patch)
+
+ @staticmethod
+ def vc_rev():
+ # Lazy load to ensure _local_repo_vc_revision() does not run before logger is set up
+ if Version._vc_rev is None:
+ Version._vc_rev = botan_version.release_vc_rev
+ if Version._vc_rev is None:
+ Version._vc_rev = Version._local_repo_vc_revision()
+ if Version._vc_rev is None:
+ Version._vc_rev = 'unknown'
+ return Version._vc_rev
- vc = subprocess.Popen(cmdlist,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- universal_newlines=True)
+ @staticmethod
+ def _local_repo_vc_revision():
+ vc_command = ['git', 'rev-parse', 'HEAD']
+ cmdname = vc_command[0]
+ try:
+ vc = subprocess.Popen(
+ vc_command,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ universal_newlines=True)
(stdout, stderr) = vc.communicate()
if vc.returncode != 0:
@@ -77,64 +106,34 @@ def get_vc_revision():
logging.debug('Error getting rev from %s - %s' % (cmdname, e.strerror))
return None
- vc_command = ['git', 'rev-parse', 'HEAD']
- rev = get_vc_revision_impl(vc_command)
- if rev is not None:
- return rev
- else:
- return 'unknown'
-
-class BuildConfigurationInformation(object):
-
- """
- Version information
- """
- version_major = botan_version.release_major
- version_minor = botan_version.release_minor
- version_patch = botan_version.release_patch
- version_so_rev = botan_version.release_so_abi_rev
-
- version_release_type = botan_version.release_type
-
- version_datestamp = botan_version.release_datestamp
-
- version_vc_rev = botan_version.release_vc_rev
- version_string = '%d.%d.%d' % (version_major, version_minor, version_patch)
-
- # This is used on Darwin for dylib versioning
- version_packed = version_major * 1000 + version_minor
+class BuildPaths(object): # pylint: disable=too-many-instance-attributes
"""
Constructor
"""
def __init__(self, options, modules):
-
- if self.version_vc_rev is None:
- self.version_vc_rev = get_vc_revision()
-
self.build_dir = os.path.join(options.with_build_dir, 'build')
- self.obj_dir = os.path.join(self.build_dir, 'obj')
- self.libobj_dir = os.path.join(self.obj_dir, 'lib')
- self.cliobj_dir = os.path.join(self.obj_dir, 'cli')
- self.testobj_dir = os.path.join(self.obj_dir, 'test')
+ self.libobj_dir = os.path.join(self.build_dir, 'obj', 'lib')
+ self.cliobj_dir = os.path.join(self.build_dir, 'obj', 'cli')
+ self.testobj_dir = os.path.join(self.build_dir, 'obj', 'test')
self.doc_output_dir = os.path.join(self.build_dir, 'docs')
+ self.doc_output_dir_manual = os.path.join(self.doc_output_dir, 'manual')
+ self.doc_output_dir_doxygen = os.path.join(self.doc_output_dir, 'doxygen') if options.with_doxygen else None
self.include_dir = os.path.join(self.build_dir, 'include')
self.botan_include_dir = os.path.join(self.include_dir, 'botan')
self.internal_include_dir = os.path.join(self.botan_include_dir, 'internal')
self.external_include_dir = os.path.join(self.include_dir, 'external')
- self.modules = modules
- self.sources = sorted(flatten([mod.sources() for mod in modules]))
self.internal_headers = sorted(flatten([m.internal_headers() for m in modules]))
self.external_headers = sorted(flatten([m.external_headers() for m in modules]))
if options.amalgamation:
- self.build_sources = ['botan_all.cpp']
+ self.lib_sources = ['botan_all.cpp']
else:
- self.build_sources = self.sources
+ self.lib_sources = sorted(flatten([mod.sources() for mod in modules]))
self.public_headers = sorted(flatten([m.public_headers() for m in modules]))
@@ -159,55 +158,49 @@ class BuildConfigurationInformation(object):
self.python_dir = os.path.join(options.src_dir, 'python')
- def build_doc_commands():
-
- def get_doc_cmd():
- if options.with_sphinx:
- sphinx = 'sphinx-build -c $(SPHINX_CONFIG) $(SPHINX_OPTS) '
- if options.quiet:
- sphinx += '-q '
- sphinx += '%s %s'
- return sphinx
- else:
- return '$(COPY) %s' + os.sep + '*.rst %s'
-
- doc_cmd = get_doc_cmd()
-
- def cmd_for(src):
- return doc_cmd % (os.path.join(self.doc_dir, src),
- os.path.join(self.doc_output_dir, src))
-
- yield cmd_for('manual')
-
- if options.with_doxygen:
- yield 'doxygen %s%sbotan.doxy' % (self.build_dir, os.sep)
-
- self.build_doc_commands = '\n'.join(['\t' + s for s in build_doc_commands()])
-
- def build_dirs():
- yield self.libobj_dir
- yield self.cliobj_dir
- yield self.testobj_dir
- yield self.botan_include_dir
- yield self.internal_include_dir
- yield self.external_include_dir
- yield os.path.join(self.doc_output_dir, 'manual')
-
- if options.with_doxygen:
- yield os.path.join(self.doc_output_dir, 'doxygen')
-
- self.build_dirs = list(build_dirs())
+ def build_dirs(self):
+ out = [
+ self.libobj_dir,
+ self.cliobj_dir,
+ self.testobj_dir,
+ self.botan_include_dir,
+ self.internal_include_dir,
+ self.external_include_dir,
+ self.doc_output_dir_manual,
+ ]
+ if self.doc_output_dir_doxygen:
+ out += [self.doc_output_dir_doxygen]
+ return out
def src_info(self, typ):
if typ == 'lib':
- return (self.build_sources, self.libobj_dir)
+ return (self.lib_sources, self.libobj_dir)
elif typ == 'cli':
return (self.cli_sources, self.cliobj_dir)
elif typ == 'test':
return (self.test_sources, self.testobj_dir)
- def pkg_config_file(self):
- return 'botan-%d.pc' % (self.version_major)
+
+PKG_CONFIG_FILENAME = 'botan-%d.pc' % (Version.major)
+
+
+def make_build_doc_commands(build_paths, options):
+ def build_manual_command(src_dir, dst_dir):
+ if options.with_sphinx:
+ sphinx = 'sphinx-build -c $(SPHINX_CONFIG) $(SPHINX_OPTS) '
+ if options.quiet:
+ sphinx += '-q '
+ sphinx += '%s %s' % (src_dir, dst_dir)
+ return sphinx
+ else:
+ 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)
+ ]
+ if options.with_doxygen:
+ cmds += ['doxygen %s%sbotan.doxy' % (build_paths.build_dir, os.sep)]
+ return '\n'.join(['\t' + cmd for cmd in cmds])
def process_command_line(args): # pylint: disable=too-many-locals
@@ -217,7 +210,7 @@ def process_command_line(args): # pylint: disable=too-many-locals
parser = optparse.OptionParser(
formatter=optparse.IndentedHelpFormatter(max_help_position=50),
- version=BuildConfigurationInformation.version_string)
+ version=Version.as_string())
parser.add_option('--verbose', action='store_true', default=False,
help='Show debug messages')
@@ -523,6 +516,29 @@ class LexResult(object):
pass
+class LexerError(ConfigureError):
+ def __init__(self, msg, lexfile, line):
+ super(LexerError, self).__init__(msg)
+ self.msg = msg
+ self.lexfile = lexfile
+ self.line = line
+
+ def __str__(self):
+ return '%s at %s:%d' % (self.msg, self.lexfile, self.line)
+
+
+def parse_lex_dict(as_list):
+ if len(as_list) % 3 != 0:
+ raise ConfigureError("Lex dictionary has invalid format")
+
+ result = {}
+ for key, sep, value in [as_list[3*i:3*i+3] for i in range(0, len(as_list)//3)]:
+ if sep != '->':
+ raise ConfigureError("Lex dictionary has invalid format")
+ result[key] = value
+ return result
+
+
def lex_me_harder(infofile, allowed_groups, name_val_pairs):
"""
Generic lexer function for info.txt and src/build-data files
@@ -533,15 +549,6 @@ def lex_me_harder(infofile, allowed_groups, name_val_pairs):
def py_var(group):
return group.replace(':', '_')
- class LexerError(ConfigureError):
- def __init__(self, msg, line):
- super(LexerError, self).__init__(msg)
- self.msg = msg
- self.line = line
-
- def __str__(self):
- return '%s at %s:%d' % (self.msg, infofile, self.line)
-
lexer = shlex.shlex(open(infofile), infofile, posix=True)
lexer.wordchars += '|:.<>/,-!+' # handle various funky chars in info.txt
@@ -551,10 +558,12 @@ def lex_me_harder(infofile, allowed_groups, name_val_pairs):
out.__dict__[key] = val
def lexed_tokens(): # Convert to an interator
- token = lexer.get_token()
- while token != None:
- yield token
+ while True:
token = lexer.get_token()
+ if token != lexer.eof:
+ yield token
+ else:
+ return
for token in lexed_tokens():
match = re.match('<(.*)>', token)
@@ -565,7 +574,7 @@ def lex_me_harder(infofile, allowed_groups, name_val_pairs):
if group not in allowed_groups:
raise LexerError('Unknown group "%s"' % (group),
- lexer.lineno)
+ infofile, lexer.lineno)
end_marker = '</' + group + '>'
@@ -575,28 +584,20 @@ def lex_me_harder(infofile, allowed_groups, name_val_pairs):
token = lexer.get_token()
if token is None:
raise LexerError('Group "%s" not terminated' % (group),
- lexer.lineno)
+ infofile, lexer.lineno)
elif token in name_val_pairs.keys():
if isinstance(out.__dict__[token], list):
out.__dict__[token].append(lexer.get_token())
-
- # Dirty hack
- if token == 'define':
- nxt = lexer.get_token()
- if not nxt:
- raise LexerError('No version set for API', lexer.lineno)
- if not re.match('^[0-9]{8}$', nxt):
- raise LexerError('Bad API rev "%s"' % (nxt), lexer.lineno)
- out.__dict__[token].append(nxt)
else:
out.__dict__[token] = lexer.get_token()
else: # No match -> error
- raise LexerError('Bad token "%s"' % (token), lexer.lineno)
+ raise LexerError('Bad token "%s"' % (token), infofile, lexer.lineno)
return out
+
def force_to_dict(l):
"""
Convert a lex'ed map (from build-data files) from a list to a dict
@@ -635,12 +636,11 @@ class ModuleInfo(InfoObject):
lex = lex_me_harder(
infofile,
[
- 'header:internal', 'header:public', 'header:external', 'requires',
+ 'defines', 'header:internal', 'header:public', 'header:external', 'requires',
'os', 'arch', 'cc', 'libs', 'frameworks', 'comment', 'warning'
],
{
'load_on': 'auto',
- 'define': [],
'need_isa': ''
})
@@ -692,7 +692,8 @@ class ModuleInfo(InfoObject):
self.arch = lex.arch
self.cc = lex.cc
self.comment = ' '.join(lex.comment) if lex.comment else None
- self.define = lex.define
+ self._defines = parse_lex_dict(lex.defines)
+ self._validate_defines_content(self._defines)
self.frameworks = convert_lib_list(lex.frameworks)
self.libs = convert_lib_list(lex.libs)
self.load_on = lex.load_on
@@ -733,6 +734,14 @@ class ModuleInfo(InfoObject):
intersect_check('public', self.header_public, 'external', self.header_external)
intersect_check('external', self.header_external, 'internal', self.header_internal)
+ @staticmethod
+ def _validate_defines_content(defines):
+ for key, value in defines.items():
+ if not re.match('^[0-9A-Za-z_]{3,30}$', key):
+ raise ConfigureError('Module defines key has invalid format: "%s"' % key)
+ if not re.match('^[0-9]{8}$', value):
+ raise ConfigureError('Module defines value has invalid format: "%s"' % value)
+
def cross_check(self, arch_info, os_info, cc_info):
for supp_os in self.os:
if supp_os not in os_info:
@@ -757,7 +766,7 @@ class ModuleInfo(InfoObject):
return self.header_external
def defines(self):
- return ['HAS_' + d[0] + ' ' + d[1] for d in chunks(self.define, 2)]
+ return ['HAS_%s %s' % (key, value) for key, value in self._defines.items()]
def compatible_cpu(self, archinfo, options):
arch_name = archinfo.basename
@@ -1369,7 +1378,7 @@ def gen_bakefile(build_config, options, external_libs):
# shared library project
f.write('shared-library botan {\n')
f.write('\tdefines = "BOTAN_DLL=__declspec(dllexport)";\n')
- bakefile_sources(f, build_config.sources)
+ bakefile_sources(f, build_config.lib_sources)
f.write('}\n')
# cli project
@@ -1608,21 +1617,18 @@ def create_template_vars(build_config, options, modules, cc, arch, osinfo):
return opts
variables = {
- 'version_major': build_config.version_major,
- 'version_minor': build_config.version_minor,
- 'version_patch': build_config.version_patch,
- 'version_vc_rev': build_config.version_vc_rev,
- 'so_abi_rev': build_config.version_so_rev,
- 'version': build_config.version_string,
-
- 'version_packed': build_config.version_packed,
-
- 'release_type': build_config.version_release_type,
+ 'version_major': Version.major,
+ 'version_minor': Version.minor,
+ 'version_patch': Version.patch,
+ 'version_vc_rev': Version.vc_rev(),
+ 'so_abi_rev': Version.so_rev,
+ 'version': Version.as_string(),
+ 'version_packed': Version.packed,
+ 'release_type': Version.release_type,
+ 'version_datestamp': Version.datestamp,
'distribution_info': options.distribution_info,
- 'version_datestamp': build_config.version_datestamp,
-
'base_dir': options.base_dir,
'src_dir': options.src_dir,
'doc_dir': build_config.doc_dir,
@@ -1655,7 +1661,7 @@ def create_template_vars(build_config, options, modules, cc, arch, osinfo):
'doc_output_dir': build_config.doc_output_dir,
- 'build_doc_commands': build_config.build_doc_commands,
+ 'build_doc_commands': make_build_doc_commands(build_config, options),
'python_dir': build_config.python_dir,
'sphinx_config_dir': os.path.join(options.build_data, 'sphinx'),
@@ -1711,20 +1717,20 @@ def create_template_vars(build_config, options, modules, cc, arch, osinfo):
'static_suffix': osinfo.static_suffix,
'soname_base': osinfo.soname_pattern_base.format(
- version_major=build_config.version_major,
- version_minor=build_config.version_minor,
- version_patch=build_config.version_patch,
- abi_rev=build_config.version_so_rev),
+ version_major=Version.major,
+ version_minor=Version.minor,
+ version_patch=Version.patch,
+ abi_rev=Version.so_rev),
'soname_abi': osinfo.soname_pattern_abi.format(
- version_major=build_config.version_major,
- version_minor=build_config.version_minor,
- version_patch=build_config.version_patch,
- abi_rev=build_config.version_so_rev),
+ version_major=Version.major,
+ version_minor=Version.minor,
+ version_patch=Version.patch,
+ abi_rev=Version.so_rev),
'soname_patch': osinfo.soname_pattern_patch.format(
- version_major=build_config.version_major,
- version_minor=build_config.version_minor,
- version_patch=build_config.version_patch,
- abi_rev=build_config.version_so_rev),
+ version_major=Version.major,
+ version_minor=Version.minor,
+ version_patch=Version.patch,
+ abi_rev=Version.so_rev),
'mod_list': '\n'.join(sorted([m.basename for m in modules])),
@@ -1753,11 +1759,11 @@ def create_template_vars(build_config, options, modules, cc, arch, osinfo):
else:
variables['libname'] = 'botan'
else:
- variables['botan_pkgconfig'] = os.path.join(build_config.build_dir, build_config.pkg_config_file())
+ variables['botan_pkgconfig'] = os.path.join(build_config.build_dir, PKG_CONFIG_FILENAME)
# 'botan' or 'botan-2'. Used in Makefile and install script
# This can be made consistent over all platforms in the future
- variables['libname'] = 'botan-%d' % (build_config.version_major)
+ variables['libname'] = 'botan-%d' % (Version.major)
variables["header_in"] = process_template(os.path.join(options.makefile_dir, 'header.in'), variables)
@@ -1974,7 +1980,7 @@ def portable_symlink(file_path, target_dir, method):
else:
raise ConfigureError('Unknown link method %s' % (method))
-def generate_amalgamation(build_config, options):
+def generate_amalgamation(build_config, modules, options):
"""
Generate the amalgamation
"""
@@ -2071,7 +2077,7 @@ def generate_amalgamation(build_config, options):
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
-""" % (build_config.version_string)
+""" % (Version.as_string())
botan_h.write(amalg_header)
@@ -2115,7 +2121,7 @@ def generate_amalgamation(build_config, options):
botan_amalg_files = {}
headers_written = {}
- for mod in build_config.modules:
+ for mod in modules:
tgt = ''
if not options.single_amalgamation_file:
@@ -2396,7 +2402,7 @@ def main(argv=None):
using_mods = [modules[m] for m in loaded_mods]
- build_config = BuildConfigurationInformation(options, using_mods)
+ build_config = BuildPaths(options, using_mods)
build_config.public_headers.append(os.path.join(build_config.build_dir, 'build.h'))
@@ -2441,7 +2447,7 @@ def main(argv=None):
if e.errno != errno.ENOENT:
logging.error('Problem while removing build dir: %s' % (e))
- for build_dir in build_config.build_dirs:
+ for build_dir in build_config.build_dirs():
try:
robust_makedirs(build_dir)
except OSError as e:
@@ -2464,7 +2470,7 @@ def main(argv=None):
write_template(in_build_dir('botan.doxy'), in_build_data('botan.doxy.in'))
if options.os != 'windows':
- write_template(in_build_dir(build_config.pkg_config_file()), in_build_data('botan.pc.in'))
+ write_template(in_build_dir(PKG_CONFIG_FILENAME), in_build_data('botan.pc.in'))
if options.os == 'windows':
write_template(in_build_dir('botan.iss'), in_build_data('innosetup.in'))
@@ -2494,8 +2500,8 @@ def main(argv=None):
json.dump(template_vars, f, sort_keys=True, indent=2)
if options.amalgamation:
- amalgamation_cpp_files = generate_amalgamation(build_config, options)
- build_config.build_sources = amalgamation_cpp_files
+ amalgamation_cpp_files = generate_amalgamation(build_config, using_mods, options)
+ build_config.lib_sources = amalgamation_cpp_files
gen_makefile_lists(template_vars, build_config, options, using_mods, cc, arch, osinfo)
if options.with_bakefile:
@@ -2509,10 +2515,10 @@ def main(argv=None):
return 'dated %d' % (datestamp)
logging.info('Botan %s (VC %s) (%s %s) build setup is complete' % (
- build_config.version_string,
- build_config.version_vc_rev,
- build_config.version_release_type,
- release_date(build_config.version_datestamp)))
+ Version.as_string(),
+ Version.vc_rev(),
+ Version.release_type,
+ release_date(Version.datestamp)))
if options.unsafe_fuzzer_mode:
logging.warning("The fuzzer mode flag is labeled unsafe for a reason, this version is for testing only")