1#!/usr/local/bin/python3.8 2 3""" 4Configuration program for botan (http://botan.randombit.net/) 5 (C) 2009-2011 Jack Lloyd 6 Distributed under the terms of the Botan license 7 8Tested with CPython 2.6, 2.7, 3.1 and PyPy 1.5 9 10Python 2.5 works if you change the exception catching syntax: 11 perl -pi -e 's/except (.*) as (.*):/except $1, $2:/g' configure.py 12 13Jython - Target detection does not work (use --os and --cpu) 14 15CPython 2.4 and earlier are not supported 16 17Has not been tested with IronPython 18""" 19 20import sys 21import os 22import os.path 23import platform 24import re 25import shlex 26import shutil 27import string 28import subprocess 29import logging 30import getpass 31import time 32import errno 33import optparse 34 35# Avoid useless botan_version.pyc (Python 2.6 or higher) 36if 'dont_write_bytecode' in sys.__dict__: 37 sys.dont_write_bytecode = True 38 39import botan_version 40 41def flatten(l): 42 return sum(l, []) 43 44def get_vc_revision(): 45 try: 46 mtn = subprocess.Popen(['mtn', 'automate', 'heads'], 47 stdout=subprocess.PIPE, 48 stderr=subprocess.PIPE, 49 universal_newlines=True) 50 51 (stdout, stderr) = mtn.communicate() 52 53 if mtn.returncode != 0: 54 logging.debug('Error getting rev from monotone - %d (%s)' 55 % (mtn.returncode, stderr)) 56 return 'unknown' 57 58 rev = str(stdout).strip() 59 logging.debug('Monotone reported revision %s' % (rev)) 60 61 return 'mtn:' + rev 62 except Exception as e: 63 logging.debug('Error getting rev from monotone - %s' % (e)) 64 return 'unknown' 65 66 67class BuildConfigurationInformation(object): 68 69 """ 70 Version information 71 """ 72 version_major = botan_version.release_major 73 version_minor = botan_version.release_minor 74 version_patch = botan_version.release_patch 75 version_so_rev = botan_version.release_so_abi_rev 76 77 version_datestamp = botan_version.release_datestamp 78 79 version_vc_rev = botan_version.release_vc_rev 80 version_string = '%d.%d.%d' % (version_major, version_minor, version_patch) 81 82 """ 83 Constructor 84 """ 85 def __init__(self, options, modules): 86 87 if self.version_vc_rev is None: 88 self.version_vc_rev = get_vc_revision() 89 90 self.build_dir = os.path.join(options.with_build_dir, 'build') 91 92 self.checkobj_dir = os.path.join(self.build_dir, 'checks') 93 self.libobj_dir = os.path.join(self.build_dir, 'lib') 94 95 self.python_dir = os.path.join(options.src_dir, 'wrap', 'python') 96 97 self.boost_python = options.boost_python 98 99 self.doc_output_dir = os.path.join(self.build_dir, 'docs') 100 101 self.pyobject_dir = os.path.join(self.build_dir, 'python') 102 103 self.include_dir = os.path.join(self.build_dir, 'include') 104 self.botan_include_dir = os.path.join(self.include_dir, 'botan') 105 self.internal_include_dir = os.path.join(self.botan_include_dir, 'internal') 106 107 self.sources = sorted(flatten([mod.sources() for mod in modules])) 108 self.internal_headers = sorted(flatten([m.internal_headers() for m in modules])) 109 110 if options.via_amalgamation: 111 self.build_sources = ['botan_all.cpp'] 112 self.build_internal_headers = [] 113 else: 114 self.build_sources = self.sources 115 self.build_internal_headers = self.internal_headers 116 117 self.public_headers = sorted(flatten([m.public_headers() for m in modules])) 118 119 checks_dir = os.path.join(options.base_dir, 'checks') 120 121 self.check_sources = sorted( 122 [os.path.join(checks_dir, file) for file in os.listdir(checks_dir) 123 if file.endswith('.cpp')]) 124 125 self.python_sources = sorted( 126 [os.path.join(self.python_dir, file) 127 for file in os.listdir(self.python_dir) 128 if file.endswith('.cpp')]) 129 130 self.manual_dir = os.path.join(self. doc_output_dir, 'manual') 131 132 def build_doc_commands(): 133 yield '$(COPY) readme.txt %s' % (self.doc_output_dir) 134 135 if options.with_sphinx: 136 yield 'sphinx-build $(SPHINX_OPTS) -b html doc %s' % ( 137 self.manual_dir) 138 else: 139 yield '$(COPY) doc/*.txt %s' % (self.manual_dir) 140 141 if options.with_doxygen: 142 yield 'doxygen %s/botan.doxy' % (self.build_dir) 143 144 self.build_doc_commands = '\n'.join(['\t' + s for s in build_doc_commands()]) 145 146 def build_dirs(): 147 yield self.checkobj_dir 148 yield self.libobj_dir 149 yield self.botan_include_dir 150 yield self.internal_include_dir 151 yield os.path.join(self.doc_output_dir, 'manual') 152 if options.with_doxygen: 153 yield os.path.join(self.doc_output_dir, 'doxygen') 154 155 if self.boost_python: 156 yield self.pyobject_dir 157 158 self.build_dirs = list(build_dirs()) 159 160 def pkg_config_file(self): 161 return 'botan-%d.%d.pc' % (self.version_major, 162 self.version_minor) 163 164 def config_shell_script(self): 165 return 'botan-config-%d.%d' % (self.version_major, 166 self.version_minor) 167 168 def username(self): 169 return getpass.getuser() 170 171 def hostname(self): 172 return platform.node() 173 174 def timestamp(self): 175 return time.ctime() 176 177""" 178Handle command line options 179""" 180def process_command_line(args): 181 182 parser = optparse.OptionParser( 183 formatter = optparse.IndentedHelpFormatter(max_help_position = 50), 184 version = BuildConfigurationInformation.version_string) 185 186 parser.add_option('--verbose', action='store_true', default=False, 187 help='Show debug messages') 188 parser.add_option('--quiet', action='store_true', default=False, 189 help='Show only warnings and errors') 190 191 target_group = optparse.OptionGroup(parser, 'Target options') 192 193 target_group.add_option('--cpu', 194 help='set the target processor type/model') 195 196 target_group.add_option('--os', 197 help='set the target operating system') 198 199 target_group.add_option('--cc', dest='compiler', 200 help='set the desired build compiler') 201 202 target_group.add_option('--cc-bin', dest='compiler_binary', 203 metavar='BINARY', 204 help='set the name of the compiler binary') 205 206 target_group.add_option('--with-endian', metavar='ORDER', default=None, 207 help='override guess of CPU byte order') 208 209 target_group.add_option('--with-unaligned-mem', 210 dest='unaligned_mem', action='store_true', 211 default=None, 212 help='enable unaligned memory accesses') 213 214 target_group.add_option('--without-unaligned-mem', 215 dest='unaligned_mem', action='store_false', 216 help=optparse.SUPPRESS_HELP) 217 218 for isa_extn_name in ['SSE2', 'SSSE3', 'AltiVec', 'AES-NI', 'movbe']: 219 isa_extn = isa_extn_name.lower() 220 221 target_group.add_option('--enable-%s' % (isa_extn), 222 help='enable use of %s' % (isa_extn_name), 223 action='append_const', 224 const=isa_extn, 225 dest='enable_isa_extns') 226 227 target_group.add_option('--disable-%s' % (isa_extn), 228 help=optparse.SUPPRESS_HELP, 229 action='append_const', 230 const=isa_extn, 231 dest='disable_isa_extns') 232 233 build_group = optparse.OptionGroup(parser, 'Build options') 234 235 build_group.add_option('--enable-shared', dest='build_shared_lib', 236 action='store_true', default=True, 237 help=optparse.SUPPRESS_HELP) 238 build_group.add_option('--disable-shared', dest='build_shared_lib', 239 action='store_false', 240 help='disable building a shared library') 241 242 build_group.add_option('--enable-asm', dest='asm_ok', 243 action='store_true', default=True, 244 help=optparse.SUPPRESS_HELP) 245 build_group.add_option('--disable-asm', dest='asm_ok', 246 action='store_false', 247 help='disallow use of assembler') 248 249 build_group.add_option('--enable-debug', dest='debug_build', 250 action='store_true', default=False, 251 help='enable debug build') 252 build_group.add_option('--disable-debug', dest='debug_build', 253 action='store_false', help=optparse.SUPPRESS_HELP) 254 255 build_group.add_option('--no-optimizations', dest='no_optimizations', 256 action='store_true', default=False, 257 help=optparse.SUPPRESS_HELP) 258 259 build_group.add_option('--gen-amalgamation', dest='gen_amalgamation', 260 default=False, action='store_true', 261 help='generate amalgamation files') 262 263 build_group.add_option('--via-amalgamation', dest='via_amalgamation', 264 default=False, action='store_true', 265 help='build via amalgamation') 266 267 build_group.add_option('--with-tr1-implementation', metavar='WHICH', 268 dest='with_tr1', default=None, 269 help='enable TR1 (choices: none, system, boost)') 270 271 build_group.add_option('--with-build-dir', 272 metavar='DIR', default='', 273 help='setup the build in DIR') 274 275 build_group.add_option('--makefile-style', metavar='STYLE', default=None, 276 help='choose a makefile style (unix or nmake)') 277 278 build_group.add_option('--with-local-config', 279 dest='local_config', metavar='FILE', 280 help='include the contents of FILE into build.h') 281 282 build_group.add_option('--distribution-info', metavar='STRING', 283 help='set distribution specific versioning', 284 default='unspecified') 285 286 build_group.add_option('--with-sphinx', action='store_true', 287 default=None, 288 help='Use Sphinx to generate HTML manual') 289 290 build_group.add_option('--without-sphinx', action='store_false', 291 dest='with_sphinx', help=optparse.SUPPRESS_HELP) 292 293 build_group.add_option('--with-visibility', action='store_true', 294 default=None, help=optparse.SUPPRESS_HELP) 295 296 build_group.add_option('--without-visibility', action='store_false', 297 dest='with_visibility', help=optparse.SUPPRESS_HELP) 298 299 build_group.add_option('--with-doxygen', action='store_true', 300 default=False, 301 help='Use Doxygen to generate HTML API docs') 302 303 build_group.add_option('--without-doxygen', action='store_false', 304 dest='with_doxygen', help=optparse.SUPPRESS_HELP) 305 306 build_group.add_option('--dumb-gcc', dest='dumb_gcc', 307 action='store_true', default=False, 308 help=optparse.SUPPRESS_HELP) 309 310 build_group.add_option('--maintainer-mode', dest='maintainer_mode', 311 action='store_true', default=False, 312 help=optparse.SUPPRESS_HELP) 313 314 build_group.add_option('--dirty-tree', dest='clean_build_tree', 315 action='store_false', default=True, 316 help=optparse.SUPPRESS_HELP) 317 318 build_group.add_option('--link-method', 319 default=None, 320 help=optparse.SUPPRESS_HELP) 321 322 wrapper_group = optparse.OptionGroup(parser, 'Wrapper options') 323 324 wrapper_group.add_option('--with-boost-python', dest='boost_python', 325 default=False, action='store_true', 326 help='enable Boost.Python wrapper') 327 328 wrapper_group.add_option('--without-boost-python', 329 dest='boost_python', 330 action='store_false', 331 help=optparse.SUPPRESS_HELP) 332 333 wrapper_group.add_option('--with-python-version', dest='python_version', 334 metavar='N.M', 335 default='.'.join(map(str, sys.version_info[0:2])), 336 help='specify Python to build against (eg %default)') 337 338 mods_group = optparse.OptionGroup(parser, 'Module selection') 339 340 mods_group.add_option('--enable-modules', dest='enabled_modules', 341 metavar='MODS', action='append', 342 help='enable specific modules') 343 mods_group.add_option('--disable-modules', dest='disabled_modules', 344 metavar='MODS', action='append', 345 help='disable specific modules') 346 mods_group.add_option('--no-autoload', action='store_true', default=False, 347 help='disable automatic loading') 348 349 for lib in ['OpenSSL', 'GNU MP', 'Bzip2', 'Zlib']: 350 351 mod = lib.lower().replace(' ', '') 352 353 mods_group.add_option('--with-%s' % (mod), 354 help='add support for using %s' % (lib), 355 action='append_const', 356 const=mod, 357 dest='enabled_modules') 358 359 mods_group.add_option('--without-%s' % (mod), 360 help=optparse.SUPPRESS_HELP, 361 action='append_const', 362 const=mod, 363 dest='disabled_modules') 364 365 install_group = optparse.OptionGroup(parser, 'Installation options') 366 367 install_group.add_option('--prefix', metavar='DIR', 368 help='set the base install directory') 369 install_group.add_option('--docdir', metavar='DIR', 370 help='set the documentation install directory') 371 install_group.add_option('--libdir', metavar='DIR', 372 help='set the library install directory') 373 install_group.add_option('--includedir', metavar='DIR', 374 help='set the include file install directory') 375 376 parser.add_option_group(target_group) 377 parser.add_option_group(build_group) 378 parser.add_option_group(mods_group) 379 parser.add_option_group(wrapper_group) 380 parser.add_option_group(install_group) 381 382 # These exist only for autoconf compatability (requested by zw for mtn) 383 compat_with_autoconf_options = [ 384 'bindir', 385 'datadir', 386 'datarootdir', 387 'dvidir', 388 'exec-prefix', 389 'htmldir', 390 'infodir', 391 'libexecdir', 392 'localedir', 393 'localstatedir', 394 'mandir', 395 'oldincludedir', 396 'pdfdir', 397 'psdir', 398 'sbindir', 399 'sharedstatedir', 400 'sysconfdir' 401 ] 402 403 for opt in compat_with_autoconf_options: 404 parser.add_option('--' + opt, help=optparse.SUPPRESS_HELP) 405 406 (options, args) = parser.parse_args(args) 407 408 if args != []: 409 raise Exception('Unhandled option(s): ' + ' '.join(args)) 410 if options.with_endian != None and \ 411 options.with_endian not in ['little', 'big']: 412 raise Exception('Bad value to --with-endian "%s"' % ( 413 options.with_endian)) 414 415 def parse_multiple_enable(modules): 416 if modules is None: 417 return [] 418 return sorted(set(flatten([s.split(',') for s in modules]))) 419 420 options.enabled_modules = parse_multiple_enable(options.enabled_modules) 421 options.disabled_modules = parse_multiple_enable(options.disabled_modules) 422 423 options.enable_isa_extns = parse_multiple_enable(options.enable_isa_extns) 424 options.disable_isa_extns = parse_multiple_enable(options.disable_isa_extns) 425 426 def enabled_or_disabled_isa(isa): 427 if isa in options.enable_isa_extns: 428 return True 429 if isa in options.disable_isa_extns: 430 return True 431 return False 432 433 isa_deps = { 434 'ssse3': 'sse2', 435 'aes-ni': 'sse2' 436 } 437 438 if 'sse2' in options.disable_isa_extns: 439 for isa in [k for (k,v) in isa_deps.items() if v == 'sse2']: 440 # If explicitly enabled, allow it even if a dependency 441 # violation; trust the user to know what they want 442 if not enabled_or_disabled_isa(isa): 443 options.disable_isa_extns.append(isa) 444 445 for isa in options.enable_isa_extns: 446 if isa in isa_deps: 447 for dep in isa_deps.get(isa, '').split(','): 448 if not enabled_or_disabled_isa(dep): 449 options.enable_isa_extns.append(dep) 450 451 return options 452 453""" 454Generic lexer function for info.txt and src/build-data files 455""" 456def lex_me_harder(infofile, to_obj, allowed_groups, name_val_pairs): 457 458 # Format as a nameable Python variable 459 def py_var(group): 460 return group.replace(':', '_') 461 462 class LexerError(Exception): 463 def __init__(self, msg, line): 464 self.msg = msg 465 self.line = line 466 467 def __str__(self): 468 return '%s at %s:%d' % (self.msg, infofile, self.line) 469 470 (dirname, basename) = os.path.split(infofile) 471 472 to_obj.lives_in = dirname 473 if basename == 'info.txt': 474 (obj_dir,to_obj.basename) = os.path.split(dirname) 475 if os.access(os.path.join(obj_dir, 'info.txt'), os.R_OK): 476 to_obj.parent_module = os.path.basename(obj_dir) 477 else: 478 to_obj.parent_module = None 479 else: 480 to_obj.basename = basename.replace('.txt', '') 481 482 lexer = shlex.shlex(open(infofile), infofile, posix=True) 483 lexer.wordchars += '|:.<>/,-!+' # handle various funky chars in info.txt 484 485 for group in allowed_groups: 486 to_obj.__dict__[py_var(group)] = [] 487 for (key,val) in name_val_pairs.items(): 488 to_obj.__dict__[key] = val 489 490 def lexed_tokens(): # Convert to an interator 491 token = lexer.get_token() 492 while token != None: 493 yield token 494 token = lexer.get_token() 495 496 for token in lexed_tokens(): 497 match = re.match('<(.*)>', token) 498 499 # Check for a grouping 500 if match is not None: 501 group = match.group(1) 502 503 if group not in allowed_groups: 504 raise LexerError('Unknown group "%s"' % (group), 505 lexer.lineno) 506 507 end_marker = '</' + group + '>' 508 509 token = lexer.get_token() 510 while token != end_marker: 511 to_obj.__dict__[py_var(group)].append(token) 512 token = lexer.get_token() 513 if token is None: 514 raise LexerError('Group "%s" not terminated' % (group), 515 lexer.lineno) 516 517 elif token in name_val_pairs.keys(): 518 next_val = lexer.get_token() 519 520 if type(to_obj.__dict__[token]) is list: 521 to_obj.__dict__[token].append(next_val) 522 else: 523 to_obj.__dict__[token] = next_val 524 525 else: # No match -> error 526 raise LexerError('Bad token "%s"' % (token), lexer.lineno) 527 528""" 529Convert a lex'ed map (from build-data files) from a list to a dict 530""" 531def force_to_dict(l): 532 return dict(zip(l[::3],l[2::3])) 533 534""" 535Represents the information about a particular module 536""" 537class ModuleInfo(object): 538 539 def __init__(self, infofile): 540 541 lex_me_harder(infofile, self, 542 ['source', 'header:internal', 'header:public', 543 'requires', 'os', 'arch', 'cc', 'libs', 544 'comment', 'warning'], 545 { 546 'load_on': 'auto', 547 'define': [], 548 'uses_tr1': 'false', 549 'need_isa': None, 550 'mp_bits': 0 }) 551 552 def extract_files_matching(basedir, suffixes): 553 for (dirpath, dirnames, filenames) in os.walk(basedir): 554 if dirpath == basedir: 555 for filename in filenames: 556 if filename.startswith('.'): 557 continue 558 559 for suffix in suffixes: 560 if filename.endswith(suffix): 561 yield filename 562 563 if self.source == []: 564 self.source = list(extract_files_matching(self.lives_in, ['.cpp', '.S'])) 565 566 if self.header_internal == [] and self.header_public == []: 567 self.header_public = list(extract_files_matching(self.lives_in, ['.h'])) 568 569 # Coerce to more useful types 570 def convert_lib_list(l): 571 result = {} 572 for (targetlist, vallist) in zip(l[::3], l[2::3]): 573 vals = vallist.split(',') 574 for target in targetlist.split(','): 575 result[target] = result.setdefault(target, []) + vals 576 return result 577 578 self.libs = convert_lib_list(self.libs) 579 580 def add_dir_name(filename): 581 if filename.count(':') == 0: 582 return os.path.join(self.lives_in, filename) 583 584 # modules can request to add files of the form 585 # MODULE_NAME:FILE_NAME to add a file from another module 586 # For these, assume other module is always in a 587 # neighboring directory; this is true for all current uses 588 return os.path.join(os.path.split(self.lives_in)[0], 589 *filename.split(':')) 590 591 self.source = [add_dir_name(s) for s in self.source] 592 self.header_internal = [add_dir_name(s) for s in self.header_internal] 593 self.header_public = [add_dir_name(s) for s in self.header_public] 594 595 self.mp_bits = int(self.mp_bits) 596 597 self.uses_tr1 = (True if self.uses_tr1 == 'yes' else False) 598 599 if self.comment != []: 600 self.comment = ' '.join(self.comment) 601 else: 602 self.comment = None 603 604 if self.warning != []: 605 self.warning = ' '.join(self.warning) 606 else: 607 self.warning = None 608 609 def sources(self): 610 return self.source 611 612 def public_headers(self): 613 return self.header_public 614 615 def internal_headers(self): 616 return self.header_internal 617 618 def defines(self): 619 return ['HAS_' + d for d in self.define] 620 621 def compatible_cpu(self, archinfo, options): 622 623 arch_name = archinfo.basename 624 cpu_name = options.cpu 625 626 if self.arch != []: 627 if arch_name not in self.arch and cpu_name not in self.arch: 628 return False 629 630 if self.need_isa != None: 631 if self.need_isa in options.disable_isa_extns: 632 return False # explicitly disabled 633 634 if self.need_isa in options.enable_isa_extns: 635 return True # explicitly enabled 636 637 # Default to whatever the CPU is supposed to support 638 return self.need_isa in archinfo.isa_extensions_in(cpu_name) 639 640 return True 641 642 def compatible_os(self, os): 643 return self.os == [] or os in self.os 644 645 def compatible_compiler(self, cc): 646 return self.cc == [] or cc in self.cc 647 648 def tr1_ok(self, with_tr1): 649 if self.uses_tr1: 650 return with_tr1 in ['boost', 'system'] 651 else: 652 return True 653 654 def dependencies(self): 655 # utils is an implicit dep (contains types, etc) 656 deps = self.requires + ['utils'] 657 if self.parent_module != None: 658 deps.append(self.parent_module) 659 return deps 660 661 """ 662 Ensure that all dependencies of this module actually exist, warning 663 about any that do not 664 """ 665 def dependencies_exist(self, modules): 666 all_deps = [s.split('|') for s in self.dependencies()] 667 668 for missing in [s for s in flatten(all_deps) if s not in modules]: 669 logging.warn("Module '%s', dep of '%s', does not exist" % ( 670 missing, self.basename)) 671 672 def __cmp__(self, other): 673 if self.basename < other.basename: 674 return -1 675 if self.basename == other.basename: 676 return 0 677 return 1 678 679class ArchInfo(object): 680 def __init__(self, infofile): 681 lex_me_harder(infofile, self, 682 ['aliases', 'submodels', 'submodel_aliases', 'isa_extn'], 683 { 'endian': None, 684 'family': None, 685 'unaligned': 'no' 686 }) 687 688 def convert_isa_list(input): 689 isa_info = {} 690 for line in self.isa_extn: 691 (isa,cpus) = line.split(':') 692 for cpu in cpus.split(','): 693 isa_info.setdefault(cpu, []).append(isa) 694 return isa_info 695 696 self.isa_extn = convert_isa_list(self.isa_extn) 697 698 self.submodel_aliases = force_to_dict(self.submodel_aliases) 699 700 self.unaligned_ok = (1 if self.unaligned == 'ok' else 0) 701 702 """ 703 Return ISA extensions specific to this CPU 704 """ 705 def isa_extensions_in(self, cpu_type): 706 return sorted(self.isa_extn.get(cpu_type, []) + 707 self.isa_extn.get('all', [])) 708 709 """ 710 Return a list of all submodels for this arch, ordered longest 711 to shortest 712 """ 713 def all_submodels(self): 714 return sorted([(k,k) for k in self.submodels] + 715 [k for k in self.submodel_aliases.items()], 716 key = lambda k: len(k[0]), reverse = True) 717 718 """ 719 Return CPU-specific defines for build.h 720 """ 721 def defines(self, options): 722 def form_macro(cpu_name): 723 return cpu_name.upper().replace('.', '').replace('-', '_') 724 725 macros = ['TARGET_ARCH_IS_%s' % 726 (form_macro(self.basename.upper()))] 727 728 if self.basename != options.cpu: 729 macros.append('TARGET_CPU_IS_%s' % (form_macro(options.cpu))) 730 731 enabled_isas = set(self.isa_extensions_in(options.cpu) + 732 options.enable_isa_extns) 733 disabled_isas = set(options.disable_isa_extns) 734 735 isa_extensions = sorted(enabled_isas - disabled_isas) 736 737 for isa in isa_extensions: 738 macros.append('TARGET_CPU_HAS_%s' % (form_macro(isa))) 739 740 endian = options.with_endian or self.endian 741 742 if endian != None: 743 macros.append('TARGET_CPU_IS_%s_ENDIAN' % (endian.upper())) 744 logging.info('Assuming CPU is %s endian' % (endian)) 745 746 unaligned_ok = options.unaligned_mem 747 if unaligned_ok is None: 748 unaligned_ok = self.unaligned_ok 749 if unaligned_ok: 750 logging.info('Assuming unaligned memory access works') 751 752 if self.family is not None: 753 macros.append('TARGET_CPU_IS_%s_FAMILY' % (self.family.upper())) 754 755 macros.append('TARGET_UNALIGNED_MEMORY_ACCESS_OK %d' % (unaligned_ok)) 756 757 return macros 758 759class CompilerInfo(object): 760 def __init__(self, infofile): 761 lex_me_harder(infofile, self, 762 ['so_link_flags', 'mach_opt', 'mach_abi_linking'], 763 { 'binary_name': None, 764 'macro_name': None, 765 'compile_option': '-c ', 766 'output_to_option': '-o ', 767 'add_include_dir_option': '-I', 768 'add_lib_dir_option': '-L', 769 'add_lib_option': '-l', 770 'lib_opt_flags': '', 771 'check_opt_flags': '', 772 'debug_flags': '', 773 'no_debug_flags': '', 774 'shared_flags': '', 775 'lang_flags': '', 776 'warning_flags': '', 777 'maintainer_warning_flags': '', 778 'visibility_build_flags': '', 779 'visibility_attribute': '', 780 'ar_command': None, 781 'makefile_style': '', 782 'has_tr1': False, 783 }) 784 785 self.so_link_flags = force_to_dict(self.so_link_flags) 786 self.mach_abi_linking = force_to_dict(self.mach_abi_linking) 787 788 self.mach_opt_flags = {} 789 790 while self.mach_opt != []: 791 proc = self.mach_opt.pop(0) 792 if self.mach_opt.pop(0) != '->': 793 raise Exception('Parsing err in %s mach_opt' % (self.basename)) 794 795 flags = self.mach_opt.pop(0) 796 regex = '' 797 798 if len(self.mach_opt) > 0 and \ 799 (len(self.mach_opt) == 1 or self.mach_opt[1] != '->'): 800 regex = self.mach_opt.pop(0) 801 802 self.mach_opt_flags[proc] = (flags,regex) 803 804 del self.mach_opt 805 806 """ 807 Return the shared library build flags, if any 808 """ 809 def gen_shared_flags(self, options): 810 def flag_builder(): 811 if options.build_shared_lib: 812 yield self.shared_flags 813 if options.with_visibility: 814 yield self.visibility_build_flags 815 816 return ' '.join(list(flag_builder())) 817 818 def gen_visibility_attribute(self, options): 819 if options.build_shared_lib and options.with_visibility: 820 return self.visibility_attribute 821 return '' 822 823 """ 824 Return the machine specific ABI flags 825 """ 826 def mach_abi_link_flags(self, osname, arch, submodel, debug_p): 827 828 def all(): 829 if debug_p: 830 return 'all-debug' 831 return 'all' 832 833 abi_link = set() 834 for what in [all(), osname, arch, submodel]: 835 if self.mach_abi_linking.get(what) != None: 836 abi_link.add(self.mach_abi_linking.get(what)) 837 838 if len(abi_link) == 0: 839 return '' 840 return ' ' + ' '.join(abi_link) 841 842 """ 843 Return the flags for MACH_OPT 844 """ 845 def mach_opts(self, arch, submodel): 846 847 def submodel_fixup(tup): 848 return tup[0].replace('SUBMODEL', submodel.replace(tup[1], '')) 849 850 if submodel == arch: 851 return '' 852 853 if submodel in self.mach_opt_flags: 854 return submodel_fixup(self.mach_opt_flags[submodel]) 855 if arch in self.mach_opt_flags: 856 return submodel_fixup(self.mach_opt_flags[arch]) 857 858 return '' 859 860 """ 861 Return the flags for LIB_OPT 862 """ 863 def library_opt_flags(self, options): 864 def gen_flags(): 865 if options.debug_build: 866 yield self.debug_flags 867 868 if not options.no_optimizations: 869 yield self.lib_opt_flags 870 871 if not options.debug_build: 872 yield self.no_debug_flags 873 874 return (' '.join(gen_flags())).strip() 875 876 """ 877 Return the command needed to link a shared object 878 """ 879 def so_link_command_for(self, osname): 880 if osname in self.so_link_flags: 881 return self.so_link_flags[osname] 882 if 'default' in self.so_link_flags: 883 return self.so_link_flags['default'] 884 return '' 885 886 """ 887 Return defines for build.h 888 """ 889 def defines(self, with_tr1): 890 891 def tr1_macro(): 892 if with_tr1: 893 if with_tr1 == 'boost': 894 return ['USE_BOOST_TR1'] 895 elif with_tr1 == 'system': 896 return ['USE_STD_TR1'] 897 elif self.has_tr1: 898 return ['USE_STD_TR1'] 899 return [] 900 901 return ['BUILD_COMPILER_IS_' + self.macro_name] + tr1_macro() 902 903class OsInfo(object): 904 def __init__(self, infofile): 905 lex_me_harder(infofile, self, 906 ['aliases', 'target_features'], 907 { 'os_type': None, 908 'obj_suffix': 'o', 909 'so_suffix': 'so', 910 'static_suffix': 'a', 911 'ar_command': 'ar crs', 912 'ar_needs_ranlib': False, 913 'install_root': '/usr/local', 914 'header_dir': 'include', 915 'lib_dir': 'lib', 916 'doc_dir': 'share/doc', 917 'build_shared': 'yes', 918 'install_cmd_data': 'install -m 644', 919 'install_cmd_exec': 'install -m 755' 920 }) 921 922 self.ar_needs_ranlib = bool(self.ar_needs_ranlib) 923 924 self.build_shared = (True if self.build_shared == 'yes' else False) 925 926 def ranlib_command(self): 927 return ('ranlib' if self.ar_needs_ranlib else 'true') 928 929 def defines(self): 930 return ['TARGET_OS_IS_%s' % (self.basename.upper())] + \ 931 ['TARGET_OS_HAS_' + feat.upper() 932 for feat in sorted(self.target_features)] 933 934def fixup_proc_name(proc): 935 proc = proc.lower().replace(' ', '') 936 for junk in ['(tm)', '(r)']: 937 proc = proc.replace(junk, '') 938 return proc 939 940def canon_processor(archinfo, proc): 941 proc = fixup_proc_name(proc) 942 943 # First, try to search for an exact match 944 for ainfo in archinfo.values(): 945 if ainfo.basename == proc or proc in ainfo.aliases: 946 return (ainfo.basename, ainfo.basename) 947 948 for (match,submodel) in ainfo.all_submodels(): 949 if proc == submodel or proc == match: 950 return (ainfo.basename, submodel) 951 952 logging.debug('Could not find an exact match for CPU "%s"' % (proc)) 953 954 # Now, try searching via regex match 955 for ainfo in archinfo.values(): 956 for (match,submodel) in ainfo.all_submodels(): 957 if re.search(match, proc) != None: 958 logging.debug('Possible match "%s" with "%s" (%s)' % ( 959 proc, match, submodel)) 960 return (ainfo.basename, submodel) 961 962 logging.debug('Known CPU names: ' + ' '.join( 963 sorted(flatten([[ainfo.basename] + \ 964 ainfo.aliases + \ 965 [x for (x,_) in ainfo.all_submodels()] 966 for ainfo in archinfo.values()])))) 967 968 raise Exception('Unknown or unidentifiable processor "%s"' % (proc)) 969 970def guess_processor(archinfo): 971 base_proc = platform.machine() 972 973 if base_proc == '': 974 raise Exception('Could not determine target CPU; set with --cpu') 975 976 full_proc = fixup_proc_name(platform.processor()) or base_proc 977 978 for ainfo in archinfo.values(): 979 if ainfo.basename == base_proc or base_proc in ainfo.aliases: 980 for (match,submodel) in ainfo.all_submodels(): 981 if re.search(match, full_proc) != None: 982 return (ainfo.basename, submodel) 983 984 return canon_processor(archinfo, ainfo.basename) 985 986 # No matches, so just use the base proc type 987 return canon_processor(archinfo, base_proc) 988 989""" 990Read a whole file into memory as a string 991""" 992def slurp_file(filename): 993 if filename is None: 994 return '' 995 return ''.join(open(filename).readlines()) 996 997""" 998Perform template substitution 999""" 1000def process_template(template_file, variables): 1001 class PercentSignTemplate(string.Template): 1002 delimiter = '%' 1003 1004 try: 1005 template = PercentSignTemplate(slurp_file(template_file)) 1006 return template.substitute(variables) 1007 except KeyError as e: 1008 raise Exception('Unbound var %s in template %s' % (e, template_file)) 1009 1010""" 1011Create the template variables needed to process the makefile, build.h, etc 1012""" 1013def create_template_vars(build_config, options, modules, cc, arch, osinfo): 1014 def make_cpp_macros(macros): 1015 return '\n'.join(['#define BOTAN_' + macro for macro in macros]) 1016 1017 """ 1018 Figure out what external libraries are needed based on selected modules 1019 """ 1020 def link_to(): 1021 libs = set() 1022 for module in modules: 1023 for (osname,link_to) in module.libs.items(): 1024 if osname == 'all' or osname == osinfo.basename: 1025 libs |= set(link_to) 1026 else: 1027 match = re.match('^all!(.*)', osname) 1028 if match is not None: 1029 exceptions = match.group(1).split(',') 1030 if osinfo.basename not in exceptions: 1031 libs |= set(link_to) 1032 return sorted(libs) 1033 1034 def objectfile_list(sources, obj_dir): 1035 for src in sources: 1036 (dir,file) = os.path.split(os.path.normpath(src)) 1037 1038 if dir.startswith('src'): 1039 parts = dir.split(os.sep)[1:] 1040 if file == parts[-1] + '.cpp': 1041 name = '_'.join(dir.split(os.sep)[1:]) + '.cpp' 1042 else: 1043 name = '_'.join(dir.split(os.sep)[1:]) + '_' + file 1044 else: 1045 name = file 1046 1047 for src_suffix in ['.cpp', '.S']: 1048 name = name.replace(src_suffix, '.' + osinfo.obj_suffix) 1049 1050 yield os.path.join(obj_dir, name) 1051 1052 1053 def choose_mp_bits(): 1054 mp_bits = [mod.mp_bits for mod in modules if mod.mp_bits != 0] 1055 1056 if mp_bits == []: 1057 return 32 # default 1058 1059 # Check that settings are consistent across modules 1060 for mp_bit in mp_bits[1:]: 1061 if mp_bit != mp_bits[0]: 1062 raise Exception('Incompatible mp_bits settings found') 1063 1064 return mp_bits[0] 1065 1066 """ 1067 Form snippets of makefile for building each source file 1068 """ 1069 def build_commands(sources, obj_dir, flags): 1070 for (obj_file,src) in zip(objectfile_list(sources, obj_dir), sources): 1071 yield '%s: %s\n\t$(CXX) %s%s $(%s_FLAGS) %s$? %s$@\n' % ( 1072 obj_file, src, 1073 cc.add_include_dir_option, 1074 build_config.include_dir, 1075 flags, 1076 cc.compile_option, 1077 cc.output_to_option) 1078 1079 def makefile_list(items): 1080 items = list(items) # force evaluation so we can slice it 1081 return (' '*16).join([item + ' \\\n' for item in items[:-1]] + 1082 [items[-1]]) 1083 1084 def prefix_with_build_dir(path): 1085 if options.with_build_dir != None: 1086 return os.path.join(options.with_build_dir, path) 1087 return path 1088 1089 def warning_flags(normal_flags, 1090 maintainer_flags, 1091 maintainer_mode): 1092 if maintainer_mode and maintainer_flags != '': 1093 return maintainer_flags 1094 return normal_flags 1095 1096 return { 1097 'version_major': build_config.version_major, 1098 'version_minor': build_config.version_minor, 1099 'version_patch': build_config.version_patch, 1100 'version_vc_rev': build_config.version_vc_rev, 1101 'so_abi_rev': build_config.version_so_rev, 1102 'version': build_config.version_string, 1103 1104 'distribution_info': options.distribution_info, 1105 1106 'version_datestamp': build_config.version_datestamp, 1107 1108 'timestamp': build_config.timestamp(), 1109 'user': build_config.username(), 1110 'hostname': build_config.hostname(), 1111 'command_line': ' '.join(sys.argv), 1112 'local_config': slurp_file(options.local_config), 1113 'makefile_style': options.makefile_style or cc.makefile_style, 1114 1115 'makefile_path': prefix_with_build_dir('Makefile'), 1116 1117 'prefix': options.prefix or osinfo.install_root, 1118 'libdir': options.libdir or osinfo.lib_dir, 1119 'includedir': options.includedir or osinfo.header_dir, 1120 'docdir': options.docdir or osinfo.doc_dir, 1121 1122 'build_dir': build_config.build_dir, 1123 'doc_output_dir': build_config.doc_output_dir, 1124 1125 'build_doc_commands': build_config.build_doc_commands, 1126 1127 'python_dir': build_config.python_dir, 1128 1129 'os': options.os, 1130 'arch': options.arch, 1131 'submodel': options.cpu, 1132 1133 'mp_bits': choose_mp_bits(), 1134 1135 'cc': (options.compiler_binary or cc.binary_name) + 1136 cc.mach_abi_link_flags(options.os, options.arch, 1137 options.cpu, options.debug_build), 1138 1139 'lib_opt': cc.library_opt_flags(options), 1140 'mach_opt': cc.mach_opts(options.arch, options.cpu), 1141 'check_opt': '' if options.no_optimizations else cc.check_opt_flags, 1142 'lang_flags': cc.lang_flags + options.extra_flags, 1143 'warn_flags': warning_flags(cc.warning_flags, 1144 cc.maintainer_warning_flags, 1145 options.maintainer_mode), 1146 1147 'shared_flags': cc.gen_shared_flags(options), 1148 'visibility_attribute': cc.gen_visibility_attribute(options), 1149 1150 'so_link': cc.so_link_command_for(osinfo.basename), 1151 1152 'link_to': ' '.join([cc.add_lib_option + lib for lib in link_to()]), 1153 1154 'module_defines': make_cpp_macros(sorted(flatten([m.defines() for m in modules]))), 1155 1156 'target_os_defines': make_cpp_macros(osinfo.defines()), 1157 1158 'target_compiler_defines': make_cpp_macros( 1159 cc.defines(options.with_tr1)), 1160 1161 'target_cpu_defines': make_cpp_macros(arch.defines(options)), 1162 1163 'include_files': makefile_list(build_config.public_headers), 1164 1165 'lib_objs': makefile_list( 1166 objectfile_list(build_config.build_sources, 1167 build_config.libobj_dir)), 1168 1169 'check_objs': makefile_list( 1170 objectfile_list(build_config.check_sources, 1171 build_config.checkobj_dir)), 1172 1173 'lib_build_cmds': '\n'.join( 1174 build_commands(build_config.build_sources, 1175 build_config.libobj_dir, 'LIB')), 1176 1177 'check_build_cmds': '\n'.join( 1178 build_commands(build_config.check_sources, 1179 build_config.checkobj_dir, 'CHECK')), 1180 1181 'python_obj_dir': build_config.pyobject_dir, 1182 1183 'python_objs': makefile_list( 1184 objectfile_list(build_config.python_sources, 1185 build_config.pyobject_dir)), 1186 1187 'python_build_cmds': '\n'.join( 1188 build_commands(build_config.python_sources, 1189 build_config.pyobject_dir, 'PYTHON')), 1190 1191 'ar_command': cc.ar_command or osinfo.ar_command, 1192 'ranlib_command': osinfo.ranlib_command(), 1193 'install_cmd_exec': osinfo.install_cmd_exec, 1194 'install_cmd_data': osinfo.install_cmd_data, 1195 1196 'check_prefix': prefix_with_build_dir(''), 1197 'lib_prefix': prefix_with_build_dir(''), 1198 1199 'static_suffix': osinfo.static_suffix, 1200 'so_suffix': osinfo.so_suffix, 1201 1202 'botan_config': prefix_with_build_dir( 1203 os.path.join(build_config.build_dir, 1204 build_config.config_shell_script())), 1205 1206 'botan_pkgconfig': prefix_with_build_dir( 1207 os.path.join(build_config.build_dir, 1208 build_config.pkg_config_file())), 1209 1210 'mod_list': '\n'.join(sorted([m.basename for m in modules])), 1211 1212 'python_version': options.python_version 1213 } 1214 1215""" 1216Determine which modules to load based on options, target, etc 1217""" 1218def choose_modules_to_use(modules, archinfo, options): 1219 1220 for mod in modules.values(): 1221 mod.dependencies_exist(modules) 1222 1223 to_load = [] 1224 maybe_dep = [] 1225 not_using_because = {} 1226 1227 def cannot_use_because(mod, reason): 1228 not_using_because.setdefault(reason, []).append(mod) 1229 1230 for modname in options.enabled_modules: 1231 if modname not in modules: 1232 logging.warning("Unknown enabled module %s" % (modname)) 1233 1234 for modname in options.disabled_modules: 1235 if modname not in modules: 1236 logging.warning("Unknown disabled module %s" % (modname)) 1237 1238 for (modname, module) in modules.items(): 1239 if modname in options.disabled_modules: 1240 cannot_use_because(modname, 'disabled by user') 1241 elif modname in options.enabled_modules: 1242 to_load.append(modname) # trust the user 1243 1244 elif not module.compatible_os(options.os): 1245 cannot_use_because(modname, 'incompatible OS') 1246 elif not module.compatible_compiler(options.compiler): 1247 cannot_use_because(modname, 'incompatible compiler') 1248 elif not module.compatible_cpu(archinfo, options): 1249 cannot_use_because(modname, 'incompatible CPU') 1250 elif not module.tr1_ok(options.with_tr1): 1251 cannot_use_because(modname, 'missing TR1') 1252 1253 else: 1254 if module.load_on == 'never': 1255 cannot_use_because(modname, 'disabled as buggy') 1256 elif module.load_on == 'request': 1257 cannot_use_because(modname, 'by request only') 1258 elif module.load_on == 'dep': 1259 maybe_dep.append(modname) 1260 1261 elif module.load_on == 'always': 1262 to_load.append(modname) 1263 1264 elif module.load_on == 'asm_ok': 1265 if options.asm_ok: 1266 if options.no_autoload: 1267 maybe_dep.append(modname) 1268 else: 1269 to_load.append(modname) 1270 else: 1271 cannot_use_because(modname, 1272 'uses assembly and --disable-asm set') 1273 elif module.load_on == 'auto': 1274 if options.no_autoload: 1275 maybe_dep.append(modname) 1276 else: 1277 to_load.append(modname) 1278 else: 1279 logging.warning('Unknown load_on %s in %s' % ( 1280 module.load_on, modname)) 1281 1282 dependency_failure = True 1283 1284 while dependency_failure: 1285 dependency_failure = False 1286 for modname in to_load: 1287 for deplist in [s.split('|') for s in modules[modname].dependencies()]: 1288 1289 dep_met = False 1290 for mod in deplist: 1291 if dep_met is True: 1292 break 1293 1294 if mod in to_load: 1295 dep_met = True 1296 elif mod in maybe_dep: 1297 maybe_dep.remove(mod) 1298 to_load.append(mod) 1299 dep_met = True 1300 1301 if dep_met == False: 1302 dependency_failure = True 1303 if modname in to_load: 1304 to_load.remove(modname) 1305 if modname in maybe_dep: 1306 maybe_dep.remove(modname) 1307 cannot_use_because(modname, 'dependency failure') 1308 1309 for not_a_dep in maybe_dep: 1310 cannot_use_because(not_a_dep, 'loaded only if needed by dependency') 1311 1312 for reason in sorted(not_using_because.keys()): 1313 disabled_mods = sorted(set([mod for mod in not_using_because[reason]])) 1314 1315 if disabled_mods != []: 1316 logging.info('Skipping, %s - %s' % ( 1317 reason, ' '.join(disabled_mods))) 1318 1319 for mod in sorted(to_load): 1320 if mod.startswith('mp_'): 1321 logging.info('Using MP module ' + mod) 1322 if mod.startswith('simd_') and mod != 'simd_engine': 1323 logging.info('Using SIMD module ' + mod) 1324 if modules[mod].comment: 1325 logging.info('%s: %s' % (mod, modules[mod].comment)) 1326 if modules[mod].warning: 1327 logging.warning('%s: %s' % (mod, modules[mod].warning)) 1328 1329 logging.debug('Loading modules %s', ' '.join(sorted(to_load))) 1330 1331 return [modules[mod] for mod in to_load] 1332 1333""" 1334Load the info files about modules, targets, etc 1335""" 1336def load_info_files(options): 1337 1338 def find_files_named(desired_name, in_path): 1339 for (dirpath, dirnames, filenames) in os.walk(in_path): 1340 if desired_name in filenames: 1341 yield os.path.join(dirpath, desired_name) 1342 1343 modules = dict([(mod.basename, mod) for mod in 1344 [ModuleInfo(info) for info in 1345 find_files_named('info.txt', options.src_dir)]]) 1346 1347 def list_files_in_build_data(subdir): 1348 for (dirpath, dirnames, filenames) in \ 1349 os.walk(os.path.join(options.build_data, subdir)): 1350 for filename in filenames: 1351 if filename.endswith('.txt'): 1352 yield os.path.join(dirpath, filename) 1353 1354 def form_name(filepath): 1355 return os.path.basename(filepath).replace('.txt', '') 1356 1357 archinfo = dict([(form_name(info), ArchInfo(info)) 1358 for info in list_files_in_build_data('arch')]) 1359 1360 osinfo = dict([(form_name(info), OsInfo(info)) 1361 for info in list_files_in_build_data('os')]) 1362 1363 ccinfo = dict([(form_name(info), CompilerInfo(info)) 1364 for info in list_files_in_build_data('cc')]) 1365 1366 def info_file_load_report(type, num): 1367 if num > 0: 1368 logging.debug('Loaded %d %s info files' % (num, type)) 1369 else: 1370 logging.warning('Failed to load any %s info files' % (type)) 1371 1372 info_file_load_report('CPU', len(archinfo)); 1373 info_file_load_report('OS', len(osinfo)) 1374 info_file_load_report('compiler', len(ccinfo)) 1375 1376 return (modules, archinfo, ccinfo, osinfo) 1377 1378""" 1379Perform the filesystem operations needed to setup the build 1380""" 1381def setup_build(build_config, options, template_vars): 1382 1383 """ 1384 Choose the link method based on system availablity and user request 1385 """ 1386 def choose_link_method(req_method): 1387 1388 def useable_methods(): 1389 if 'symlink' in os.__dict__: 1390 yield 'symlink' 1391 if 'link' in os.__dict__: 1392 yield 'hardlink' 1393 yield 'copy' 1394 1395 for method in useable_methods(): 1396 if req_method is None or req_method == method: 1397 return method 1398 1399 logging.info('Could not use requested link method %s' % (req_method)) 1400 return 'copy' 1401 1402 """ 1403 Copy or link the file, depending on what the platform offers 1404 """ 1405 def portable_symlink(filename, target_dir, method): 1406 1407 if not os.access(filename, os.R_OK): 1408 logging.warning('Missing file %s' % (filename)) 1409 return 1410 1411 if method == 'symlink': 1412 def count_dirs(dir, accum = 0): 1413 if dir in ['', '/', os.path.curdir]: 1414 return accum 1415 (dir,basename) = os.path.split(dir) 1416 return accum + 1 + count_dirs(dir) 1417 1418 dirs_up = count_dirs(target_dir) 1419 1420 source = os.path.join(os.path.join(*[os.path.pardir]*dirs_up), 1421 filename) 1422 1423 target = os.path.join(target_dir, os.path.basename(filename)) 1424 1425 os.symlink(source, target) 1426 1427 elif method == 'hardlink': 1428 os.link(filename, 1429 os.path.join(target_dir, os.path.basename(filename))) 1430 1431 elif method == 'copy': 1432 shutil.copy(filename, target_dir) 1433 1434 else: 1435 raise Exception('Unknown link method %s' % (method)) 1436 1437 def choose_makefile_template(style): 1438 if style == 'nmake': 1439 return 'nmake.in' 1440 elif style == 'unix': 1441 return ('unix_shr.in' if options.build_shared_lib else 'unix.in') 1442 else: 1443 raise Exception('Unknown makefile style "%s"' % (style)) 1444 1445 # First delete the build tree, if existing 1446 try: 1447 if options.clean_build_tree: 1448 shutil.rmtree(build_config.build_dir) 1449 except OSError as e: 1450 if e.errno != errno.ENOENT: 1451 logging.error('Problem while removing build dir: %s' % (e)) 1452 1453 for dir in build_config.build_dirs: 1454 try: 1455 os.makedirs(dir) 1456 except OSError as e: 1457 if e.errno != errno.EEXIST: 1458 logging.error('Error while creating "%s": %s' % (dir, e)) 1459 1460 makefile_template = os.path.join( 1461 options.makefile_dir, 1462 choose_makefile_template(template_vars['makefile_style'])) 1463 1464 logging.debug('Using makefile template %s' % (makefile_template)) 1465 1466 templates_to_proc = { 1467 makefile_template: template_vars['makefile_path'] 1468 } 1469 1470 def templates_to_use(): 1471 yield (options.build_data, 'buildh.in', 'build.h') 1472 yield (options.build_data, 'botan.doxy.in', 'botan.doxy') 1473 1474 if options.os != 'windows': 1475 yield (options.build_data, 'botan.pc.in', build_config.pkg_config_file()) 1476 yield (options.build_data, 'botan-config.in', build_config.config_shell_script()) 1477 1478 if options.os == 'windows': 1479 yield (options.build_data, 'innosetup.in', 'botan.iss') 1480 1481 if options.boost_python: 1482 yield (options.makefile_dir, 'python.in', 'Makefile.python') 1483 1484 for (template_dir, template, sink) in templates_to_use(): 1485 source = os.path.join(template_dir, template) 1486 if template_dir == options.build_data: 1487 sink = os.path.join(build_config.build_dir, sink) 1488 templates_to_proc[source] = sink 1489 1490 for (template, sink) in templates_to_proc.items(): 1491 try: 1492 f = open(sink, 'w') 1493 f.write(process_template(template, template_vars)) 1494 finally: 1495 f.close() 1496 1497 link_method = choose_link_method(options.link_method) 1498 logging.info('Using %s to link files into build directory' % (link_method)) 1499 1500 def link_headers(header_list, type, dir): 1501 logging.debug('Linking %d %s header files in %s' % ( 1502 len(header_list), type, dir)) 1503 1504 for header_file in header_list: 1505 try: 1506 portable_symlink(header_file, dir, link_method) 1507 except OSError as e: 1508 if e.errno != errno.EEXIST: 1509 logging.error('Error linking %s into %s: %s' % ( 1510 header_file, dir, e)) 1511 1512 link_headers(build_config.public_headers, 'public', 1513 build_config.botan_include_dir) 1514 1515 link_headers(build_config.build_internal_headers, 'internal', 1516 build_config.internal_include_dir) 1517 1518""" 1519Generate Amalgamation 1520""" 1521def generate_amalgamation(build_config): 1522 def ending_with_suffix(suffix): 1523 def predicate(val): 1524 return val.endswith(suffix) 1525 return predicate 1526 1527 def strip_header_goop(header_name, contents): 1528 header_guard = re.compile('^#define BOTAN_.*_H__$') 1529 1530 while len(contents) > 0: 1531 if header_guard.match(contents[0]): 1532 contents = contents[1:] 1533 break 1534 1535 contents = contents[1:] 1536 1537 if len(contents) == 0: 1538 raise Exception("No header guard found in " + header_name) 1539 1540 while contents[0] == '\n': 1541 contents = contents[1:] 1542 1543 while contents[-1] == '\n': 1544 contents = contents[0:-1] 1545 if contents[-1] == '#endif\n': 1546 contents = contents[0:-1] 1547 1548 return contents 1549 1550 botan_include = re.compile('#include <botan/(.*)>$') 1551 std_include = re.compile('#include <([^/\.]+)>$') 1552 1553 class Amalgamation_Generator: 1554 def __init__(self, input_list): 1555 1556 self.included_already = set() 1557 self.all_std_includes = set() 1558 1559 self.file_contents = {} 1560 for f in sorted(input_list): 1561 contents = strip_header_goop(f, open(f).readlines()) 1562 self.file_contents[os.path.basename(f)] = contents 1563 1564 self.contents = '' 1565 for name in self.file_contents: 1566 self.contents += ''.join(list(self.header_contents(name))) 1567 1568 self.header_includes = '' 1569 for std_header in self.all_std_includes: 1570 self.header_includes += '#include <%s>\n' % (std_header) 1571 self.header_includes += '\n' 1572 1573 def header_contents(self, name): 1574 name = name.replace('internal/', '') 1575 1576 if name in self.included_already: 1577 return 1578 1579 self.included_already.add(name) 1580 1581 if name not in self.file_contents: 1582 return 1583 1584 for line in self.file_contents[name]: 1585 match = botan_include.search(line) 1586 if match: 1587 for c in self.header_contents(match.group(1)): 1588 yield c 1589 else: 1590 match = std_include.search(line) 1591 1592 if match and match.group(1) != 'functional': 1593 self.all_std_includes.add(match.group(1)) 1594 else: 1595 yield line 1596 1597 amalg_basename = 'botan_all' 1598 1599 header_name = '%s.h' % (amalg_basename) 1600 1601 botan_h = open(header_name, 'w') 1602 1603 pub_header_amalag = Amalgamation_Generator(build_config.public_headers) 1604 1605 amalg_header = """/* 1606* Botan %s Amalgamation 1607* (C) 1999-2011 Jack Lloyd and others 1608* 1609* Distributed under the terms of the Botan license 1610*/ 1611""" % (build_config.version_string) 1612 1613 botan_h.write(amalg_header) 1614 1615 botan_h.write(""" 1616#ifndef BOTAN_AMALGAMATION_H__ 1617#define BOTAN_AMALGAMATION_H__ 1618 1619""") 1620 1621 botan_h.write(pub_header_amalag.header_includes) 1622 botan_h.write(pub_header_amalag.contents) 1623 botan_h.write("\n#endif\n") 1624 1625 internal_header_amalag = Amalgamation_Generator( 1626 [s for s in build_config.internal_headers 1627 if s.find('asm_macr_') == -1]) 1628 1629 botan_cpp = open('%s.cpp' % (amalg_basename), 'w') 1630 1631 botan_cpp.write(amalg_header) 1632 1633 botan_cpp.write('\n#include "%s"\n' % (header_name)) 1634 1635 botan_cpp.write(internal_header_amalag.header_includes) 1636 botan_cpp.write(internal_header_amalag.contents) 1637 1638 for src in build_config.sources: 1639 if src.endswith('.S'): 1640 continue 1641 1642 contents = open(src).readlines() 1643 for line in contents: 1644 if botan_include.search(line): 1645 continue 1646 else: 1647 botan_cpp.write(line) 1648 1649""" 1650Test for the existence of a program 1651""" 1652def have_program(program): 1653 1654 def exe_test(path, program): 1655 exe_file = os.path.join(path, program) 1656 1657 if os.path.exists(exe_file) and os.access(exe_file, os.X_OK): 1658 logging.debug('Found program %s in %s' % (program, path)) 1659 return True 1660 else: 1661 return False 1662 1663 exe_suffixes = ['', '.exe'] 1664 1665 for path in os.environ['PATH'].split(os.pathsep): 1666 for suffix in exe_suffixes: 1667 if exe_test(path, program + suffix): 1668 return True 1669 1670 return False 1671 1672""" 1673Main driver 1674""" 1675def main(argv = None): 1676 if argv is None: 1677 argv = sys.argv 1678 1679 logging.basicConfig(stream = sys.stdout, 1680 format = '%(levelname) 7s: %(message)s') 1681 1682 options = process_command_line(argv[1:]) 1683 1684 def log_level(): 1685 if options.verbose: 1686 return logging.DEBUG 1687 if options.quiet: 1688 return logging.WARNING 1689 return logging.INFO 1690 1691 logging.getLogger().setLevel(log_level()) 1692 1693 logging.debug('%s invoked with options "%s"' % ( 1694 argv[0], ' '.join(argv[1:]))) 1695 1696 logging.debug('Platform: OS="%s" machine="%s" proc="%s"' % ( 1697 platform.system(), platform.machine(), platform.processor())) 1698 1699 if options.os == "java": 1700 raise Exception("Jython detected: need --os and --cpu to set target") 1701 1702 options.base_dir = os.path.dirname(argv[0]) 1703 options.src_dir = os.path.join(options.base_dir, 'src') 1704 1705 options.build_data = os.path.join(options.src_dir, 'build-data') 1706 options.makefile_dir = os.path.join(options.build_data, 'makefile') 1707 1708 (modules, archinfo, ccinfo, osinfo) = load_info_files(options) 1709 1710 if options.compiler is None: 1711 if options.os == 'windows': 1712 if have_program('g++') and not have_program('cl'): 1713 options.compiler = 'gcc' 1714 else: 1715 options.compiler = 'msvc' 1716 else: 1717 options.compiler = 'gcc' 1718 logging.info('Guessing to use compiler %s (use --cc to set)' % ( 1719 options.compiler)) 1720 1721 if options.os is None: 1722 options.os = platform.system().lower() 1723 1724 if re.match('^cygwin_.*', options.os): 1725 logging.debug("Converting '%s' to 'cygwin'", options.os) 1726 options.os = 'cygwin' 1727 1728 if options.os == 'windows' and options.compiler == 'gcc': 1729 logging.warning('Detected GCC on Windows; use --os=cygwin or --os=mingw?') 1730 1731 logging.info('Guessing target OS is %s (use --os to set)' % (options.os)) 1732 1733 if options.compiler not in ccinfo: 1734 raise Exception('Unknown compiler "%s"; available options: %s' % ( 1735 options.compiler, ' '.join(sorted(ccinfo.keys())))) 1736 1737 if options.os not in osinfo: 1738 1739 def find_canonical_os_name(os): 1740 for (name, info) in osinfo.items(): 1741 if os in info.aliases: 1742 return name 1743 return os # not found 1744 1745 options.os = find_canonical_os_name(options.os) 1746 1747 if options.os not in osinfo: 1748 raise Exception('Unknown OS "%s"; available options: %s' % ( 1749 options.os, ' '.join(sorted(osinfo.keys())))) 1750 1751 if options.cpu is None: 1752 (options.arch, options.cpu) = guess_processor(archinfo) 1753 logging.info('Guessing target processor is a %s/%s (use --cpu to set)' % ( 1754 options.arch, options.cpu)) 1755 else: 1756 cpu_from_user = options.cpu 1757 (options.arch, options.cpu) = canon_processor(archinfo, options.cpu) 1758 logging.info('Canonicalizized --cpu=%s to %s/%s' % ( 1759 cpu_from_user, options.arch, options.cpu)) 1760 1761 logging.info('Target is %s-%s-%s-%s' % ( 1762 options.compiler, options.os, options.arch, options.cpu)) 1763 1764 cc = ccinfo[options.compiler] 1765 1766 # Kind of a hack... 1767 options.extra_flags = '' 1768 if options.compiler == 'gcc': 1769 1770 def get_gcc_version(gcc_bin): 1771 try: 1772 gcc_proc = subprocess.Popen( 1773 gcc_bin.split(' ') + ['-dumpversion'], 1774 stdout=subprocess.PIPE, 1775 stderr=subprocess.PIPE, 1776 universal_newlines=True) 1777 1778 (stdout, stderr) = gcc_proc.communicate() 1779 1780 if gcc_proc.returncode != 0: 1781 logging.warning("GCC returned non-zero result %s" % (stderr)) 1782 return None 1783 1784 gcc_version = stdout.strip() 1785 1786 logging.info('Detected gcc version %s' % (gcc_version)) 1787 return [int(v) for v in gcc_version.split('.')] 1788 except OSError: 1789 logging.warning('Could not execute %s for version check' % (gcc_bin)) 1790 return None 1791 1792 def is_64bit_arch(arch): 1793 if arch.endswith('64') or arch in ['alpha', 's390x']: 1794 return True 1795 return False 1796 1797 gcc_version = get_gcc_version(options.compiler_binary or cc.binary_name) 1798 1799 def gcc_version_matches(matches): 1800 for match in matches.items(): 1801 if gcc_version[0] != match[0]: 1802 continue 1803 1804 for minor in match[1]: 1805 if minor == gcc_version[1]: 1806 return True 1807 return False 1808 1809 if gcc_version: 1810 1811 if not is_64bit_arch(options.arch) and not options.dumb_gcc: 1812 if gcc_version_matches({ 4 : [0, 1, 2, 3, 4], 3 : [3, 4], 2 : [95] }): 1813 options.dumb_gcc = True 1814 1815 if options.with_tr1 == None and \ 1816 gcc_version_matches({ 4 : [0], 3 : [0,1,2,3,4], 2 : [95] }): 1817 logging.info('Disabling TR1 support for this gcc, too old') 1818 options.with_tr1 = 'none' 1819 1820 if options.with_visibility == None and \ 1821 gcc_version_matches({ 3 : [0,1,2,3,4], 2 : [95] }): 1822 logging.info('Disabling DSO visibility support for this gcc, too old') 1823 options.with_visibility = False 1824 1825 if options.dumb_gcc is True: 1826 logging.info('Setting -fpermissive to work around gcc bug') 1827 options.extra_flags = ' -fpermissive' 1828 1829 if options.with_visibility is None: 1830 options.with_visibility = True 1831 1832 if options.with_tr1 == None: 1833 if cc.has_tr1: 1834 logging.info('Assuming %s has TR1 (use --with-tr1=none to disable)' % ( 1835 options.compiler)) 1836 options.with_tr1 = 'system' 1837 else: 1838 options.with_tr1 = 'none' 1839 1840 if options.with_sphinx is None: 1841 if have_program('sphinx-build'): 1842 logging.info('Found sphinx-build, will use it ' + 1843 '(use --without-sphinx to disable)') 1844 options.with_sphinx = True 1845 1846 if options.via_amalgamation: 1847 options.gen_amalgamation = True 1848 1849 if options.gen_amalgamation: 1850 if options.asm_ok: 1851 logging.info('Disabling assembly code, cannot use in amalgamation') 1852 options.asm_ok = False 1853 1854 modules_to_use = choose_modules_to_use(modules, 1855 archinfo[options.arch], 1856 options) 1857 1858 if not osinfo[options.os].build_shared: 1859 if options.build_shared_lib: 1860 logging.info('Disabling shared lib on %s' % (options.os)) 1861 options.build_shared_lib = False 1862 1863 build_config = BuildConfigurationInformation(options, modules_to_use) 1864 build_config.public_headers.append( 1865 os.path.join(build_config.build_dir, 'build.h')) 1866 1867 template_vars = create_template_vars(build_config, options, 1868 modules_to_use, 1869 cc, 1870 archinfo[options.arch], 1871 osinfo[options.os]) 1872 1873 # Performs the I/O 1874 setup_build(build_config, options, template_vars) 1875 1876 if options.gen_amalgamation: 1877 generate_amalgamation(build_config) 1878 1879 logging.info('Botan %s build setup is complete' % ( 1880 build_config.version_string)) 1881 1882if __name__ == '__main__': 1883 try: 1884 main() 1885 except Exception as e: 1886 logging.error(str(e)) 1887 import traceback 1888 logging.debug(traceback.format_exc()) 1889 sys.exit(1) 1890 sys.exit(0) 1891