#!/usr/bin/perl -w require 5.006; use strict; use Config; use Getopt::Long; use File::Spec; use File::Copy; use File::Find; use Sys::Hostname; my $MAJOR_VERSION = 1; my $MINOR_VERSION = 8; my $PATCH_VERSION = 2; my $VERSION_STRING = "$MAJOR_VERSION.$MINOR_VERSION.$PATCH_VERSION"; ################################################## # Data # ################################################## my (%CPU, %OPERATING_SYSTEM, %COMPILER, %MODULES); my @DOCS = ( 'api.pdf', 'tutorial.pdf', 'fips140.pdf', 'api.tex', 'tutorial.tex', 'fips140.tex', 'credits.txt', 'license.txt', 'log.txt', 'thanks.txt', 'todo.txt', 'pgpkeys.asc'); my $TRACING = 0; ################################################## # Run main() and Quit # ################################################## my $config = {}; main(); exit; sub exec_uname { # Only exec it if we think it might actually work if(-f '/bin/uname' || -f '/usr/bin/uname' || -f '/bin/sh') { my $uname = `uname -a`; if($uname) { chomp $uname; return $uname; } } return ''; } ################################################## # Main Driver # ################################################## sub main { my $base_dir = where_am_i(); $$config{'uname'} = exec_uname(); $$config{'base-dir'} = $base_dir; $$config{'src-dir'} = File::Spec->catdir($base_dir, 'src'); $$config{'checks-dir'} = File::Spec->catdir($base_dir, 'checks'); $$config{'doc-dir'} = File::Spec->catdir($base_dir, 'doc'); $$config{'config-dir'} = File::Spec->catdir($$config{'src-dir'}, 'build-data'); $$config{'command-line'} = $0 . ' ' . join(' ', @ARGV); $$config{'timestamp'} = gmtime; $$config{'user'} = getlogin || getpwuid($<) || ''; $$config{'hostname'} = hostname; %CPU = read_info_files($config, 'arch', \&get_arch_info); %OPERATING_SYSTEM = read_info_files($config, 'os', \&get_os_info); %COMPILER = read_info_files($config, 'cc', \&get_cc_info); %MODULES = read_module_files($config); add_to($config, { 'version_major' => $MAJOR_VERSION, 'version_minor' => $MINOR_VERSION, 'version_patch' => $PATCH_VERSION, 'version' => $VERSION_STRING, }); get_options($config); my $default_value_is = sub { my ($var, $val) = @_; $$config{$var} = $val if not defined($$config{$var}); }; &$default_value_is('gcc_bug', 0); &$default_value_is('autoconfig', 1); &$default_value_is('debug', 0); &$default_value_is('shared', 'yes'); &$default_value_is('local_config', ''); # Goes into build-specific dirs (maybe) $$config{'build-dir'} = 'build'; $$config{'botan-config'} = File::Spec->catfile( $$config{'build-dir'}, 'botan-config'); $$config{'botan-pkgconfig'} = File::Spec->catfile( $$config{'build-dir'}, 'botan-' . $MAJOR_VERSION . '.' . $MINOR_VERSION . '.pc'); $$config{'makefile'} = 'Makefile'; $$config{'check_prefix'} = ''; $$config{'lib_prefix'} = ''; if(defined($$config{'with_build_dir'})) { for my $var ('build-dir', 'botan-config', 'botan-pkgconfig', 'makefile', 'check_prefix', 'lib_prefix') { $$config{$var} = File::Spec->catfile($$config{'with_build_dir'}, $$config{$var}); } } else { } choose_target($config); my $os = $$config{'os'}; my $cc = $$config{'compiler'}; &$default_value_is('prefix', os_info_for($os, 'install_root')); &$default_value_is('libdir', os_info_for($os, 'lib_dir')); &$default_value_is('docdir', os_info_for($os, 'doc_dir')); &$default_value_is('make_style', $COMPILER{$cc}{'makefile_style'}); scan_modules($config); print_enabled_modules($config); add_to($config, { 'includedir' => os_info_for($os, 'header_dir'), 'build_lib' => File::Spec->catdir($$config{'build-dir'}, 'lib'), 'build_check' => File::Spec->catdir($$config{'build-dir'}, 'checks'), 'build_include' => File::Spec->catdir($$config{'build-dir'}, 'include'), 'build_include_botan' => File::Spec->catdir($$config{'build-dir'}, 'include', 'botan'), 'mp_bits' => find_mp_bits(sort keys %{$$config{'modules'}}), 'mod_libs' => [ using_libs($os, sort keys %{$$config{'modules'}}) ], 'sources' => { }, 'includes' => { }, 'check_src' => { map_to($$config{'checks-dir'}, grep { $_ ne 'keys' and !m@\.(dat|h)$@ } dir_list($$config{'checks-dir'})) } }); load_modules($config); my @dirs = mkdirs($$config{'build-dir'}, $$config{'build_include'}, $$config{'build_include_botan'}, $$config{'build_lib'}, $$config{'build_check'}); #autoconfig('Created ' . join(' ', @dirs)) if @dirs; write_pkg_config($config); determine_config($config); process_template(File::Spec->catfile($$config{'config-dir'}, 'buildh.in'), File::Spec->catfile($$config{'build-dir'}, 'build.h'), $config); process_template(File::Spec->catfile( $$config{'config-dir'}, 'botan.doxy.in'), File::Spec->catfile($$config{'doc-dir'}, 'botan.doxy'), $config); $$config{'includes'}{'build.h'} = $$config{'build-dir'}; generate_makefile($config); copy_include_files($config); } sub where_am_i { my ($volume,$dir,$file) = File::Spec->splitpath($0); my $src_dir = File::Spec->catpath($volume, $dir, ''); return $src_dir if $src_dir; return File::Spec->curdir(); } ################################################## # Diagnostics # ################################################## sub with_diagnostic { my ($type, @args) = @_; my $args = join('', @args); my $str = "($type): "; while(length($str) < 14) { $str = ' ' . $str; } $str .= $args . "\n"; return $str; } sub croak { die with_diagnostic('error', @_); } sub warning { warn with_diagnostic('warning', @_); } sub autoconfig { print with_diagnostic('autoconfig', @_); } sub emit_help { print join('', @_); exit; } sub trace { return unless $TRACING; my (undef, undef, $line) = caller(0); my (undef, undef, undef, $func) = caller(1); $func =~ s/main:://; print with_diagnostic('trace', "at $func:$line - ", @_); } ################################################## # Display Help and Quit # ################################################## sub display_help { sub module_sets { my %modsets; for my $name (sort keys %MODULES) { my %info = %{$MODULES{$name}}; next unless (defined($info{'modset'})); for my $s (split(/,/, $info{'modset'})) { $modsets{$s} = undef; } } return sort keys %modsets; } my $sets = join(' ', module_sets()); my $listing = sub { my (@list) = @_; return '' if (@list == 0); my ($output, $len) = ('', 0); my $append = sub { my ($to_append) = @_; $output .= $to_append; $len += length $to_append; }; foreach my $name (sort @list) { next if $name eq 'defaults'; if($len > 58) { $output .= "\n "; $len = 8; } &$append($name . ' '); } chop $output; return $output; }; #my $modules = &$listing(keys %MODULES); my $compilers = &$listing(keys %COMPILER); my $oses = &$listing(keys %OPERATING_SYSTEM); my $cpus = &$listing(keys %CPU); my $helptxt = <&1`; $gcc_version = '' if not defined $gcc_version; my $has_ll_bug = 0; $has_ll_bug = 1 if($gcc_version =~ /4\.[0123]/); $has_ll_bug = 1 if($gcc_version =~ /3\.[34]/); $has_ll_bug = 1 if($gcc_version =~ /2\.25\.[0-4]/); $has_ll_bug = 1 if($gcc_version eq ''); $has_ll_bug = 0 if($arch eq 'alpha' or $arch =~ /.*64$/); if($has_ll_bug) { warning('Enabling -fpermissive to work around ', 'possible GCC bug'); $$config{'gcc_bug'} = 1; } warning('GCC 2.95.x issues many spurious warnings') if($gcc_version =~ /2\.95\.[0-4]/); } } trace("using $cc $os $arch $submodel"); add_to($config, { 'compiler' => $cc, 'os' => $os, 'arch' => $arch, 'submodel' => $submodel, }); } sub module_runs_on { my ($config, $modinfo, $mod, $noisy) = @_; my $cc = $$config{'compiler'}; my $os = $$config{'os'}; my $submodel = $$config{'submodel'}; my $arch = $$config{'arch'}; my %modinfo = %{$modinfo}; my $realname = $modinfo{'realname'}; my @arch_list = @{ $modinfo{'arch'} }; if(scalar @arch_list > 0 && !in_array($arch, \@arch_list) && !in_array($submodel, \@arch_list)) { autoconfig("$mod ($realname): skipping, " . "not compatible with " . realname($arch) . "/" . $submodel) if $noisy; return 0; } my @os_list = @{ $modinfo{'os'} }; if(scalar @os_list > 0 && !in_array($os, \@os_list)) { autoconfig("$mod ($realname): " . "skipping, not compatible with " . realname($os)) if $noisy; return 0; } my @cc_list = @{ $modinfo{'cc'} }; if(scalar @cc_list > 0 && !in_array($cc, \@cc_list)) { autoconfig("$mod ($realname): " . "skipping, not compatible with " . realname($cc)) if $noisy; return 0; } return 1; } sub can_enable_module { my ($config, $mod, $for_dep) = @_; my %modinfo = %{ $MODULES{$mod} }; my $is_enabled = 0; # If it was enabled by the user with --enable-modules, trust them if(defined($$config{'modules'}{$mod})) { return '' if($$config{'modules'}{$mod} < 0); $is_enabled = 1; } unless($is_enabled) { return '' if $modinfo{'load_on'} eq 'dep' and $for_dep == 0; return '' if $modinfo{'load_on'} eq 'request'; } # Doesn't run here, don't bother return '' unless module_runs_on($config, \%modinfo, $mod, 0); if($modinfo{'uses_tr1'} eq 'yes') { return '' unless defined($$config{'tr1'}); my $tr1 = $$config{'tr1'}; return '' unless($tr1 eq 'system' or $tr1 eq 'boost'); } # @deps is the full list of modules that must be loaded (this loop # every time is a really dumb way to do this, but it works since # there are only about 150 info.txt files total, and most don't # have complicated deps) my @deps; push @deps, $mod; LINE: foreach (@{$modinfo{'requires'}}) { for my $req_mod (split(/\|/, $_)) { next unless defined $MODULES{$req_mod}; if(can_enable_module($config, $req_mod, 1)) { push @deps, $req_mod; next LINE; } } #autoconfig("Could not get a dep match for $_ for mod $mod"); # Could not find a match return ''; } return join(' ', @deps); } sub scan_modules { my ($config) = @_; MOD: foreach my $mod (sort keys %MODULES) { my %modinfo = %{ $MODULES{$mod} }; my @mods = split(/ /, can_enable_module($config, $mod, 0)); if($#mods < 0) { trace("Will not enable $mod"); next; } foreach my $req_mod (@mods) { #autoconfig("Enabling module $req_mod"); $$config{'modules'}{$req_mod} = 1; } } } sub print_enabled_modules { my ($config) = @_; return unless($$config{'verbose'}); my %by_type; foreach my $mod (sort keys %MODULES) { my $type = $MODULES{$mod}{'type'}; my $n = 0; $n = 1 if($$config{'modules'}{$mod} && $$config{'modules'}{$mod} > 0); $by_type{$type}{$mod} = $n; } for my $type (sort keys %by_type) { my %mods = %{$by_type{$type}}; my @load_lines; if(keys %mods == 1) { my $on = $mods{$type}; if($on > 0) { print with_diagnostic('loading', $type); } else { print with_diagnostic('loading', '[', $type , ']'); } } else { my $s = $type . ': '; for my $mod (sort keys %mods) { my $on = $mods{$mod}; if($s eq '') { $s = ' ' x (length($type) + 16); } if($on > 0) { $s .= $mod . ' '; } else { $s .= '[' . $mod . '] '; } if(length($s) > 60) { push @load_lines, $s; $s = ''; } } #print "Last1 '$s'\n"; $s =~ s/\s*$//m; # strip trailing whitespace push @load_lines, $s if($s ne ''); print with_diagnostic('loading', join("\n", @load_lines)); } } } sub get_options { my ($config) = @_; my $save_option = sub { my ($opt, $val) = @_; $opt =~ s/-/_/g; $$config{$opt} = $val; }; $$config{'verbose'} = 1; $$config{'asm_ok'} = 1; $$config{'tr1'} = undef; # not enabled by default $$config{'modules'} = {}; sub arch_info { my $arg = $_[0]; my $arch = find_arch($arg); unless(defined($arch) and defined($CPU{$arch})) { warning("Unknown arch '$arg' passed to --arch-info (try --help)"); return ''; } my %info = %{ $CPU{$arch} }; my $out = "Information for $arg ($arch)\n--------\n"; if(@{$info{'aliases'}}) { $out .= 'Aliases: ' . join(' ', @{$info{'aliases'}}) . "\n"; } if(@{$info{'submodels'}}) { $out .= 'Submodels: ' . join(' ', @{$info{'submodels'}}) . "\n"; } foreach my $k (keys %{$info{'submodel_aliases'}}) { $out .= "Alias '$k' -> '" . $info{'submodel_aliases'}{$k} . "'\n"; } if(defined($info{'endian'})) { $out .= 'Default endian: ' . $info{'endian'} . "\n"; } if(defined($info{'unaligned'})) { $out .= 'Unaligned memory access: ' . $info{'unaligned'} . "\n"; } return $out; } sub add_modules { my ($config,$mods) = @_; foreach my $mod (split(/,/, $mods)) { # -1 means disabled by user, do not load $$config{'modules'}{$mod} = 1 unless( defined($$config{'modules'}{$mod}) && $$config{'modules'}{$mod} == -1); } } sub disable_modules { my ($config,$mods) = @_; foreach my $mod (split(/,/, $mods)) { # -1 means disabled by user, do not load $$config{'modules'}{$mod} = -1; } } sub add_module_sets { my ($config,$sets) = @_; foreach my $set (split(/,/, $sets)) { for my $mod (sort keys %MODULES) { my %info = %{$MODULES{$mod}}; next unless (defined($info{'modset'})); for my $s (split(/,/, $info{'modset'})) { if($s eq $set) { $$config{'modules'}{$mod} = 1 unless($$config{'modules'}{$mod} == -1); } } } } } exit 1 unless GetOptions( 'prefix=s' => sub { &$save_option(@_); }, 'exec-prefix=s' => sub { &$save_option(@_); }, 'bindir=s' => sub { &$save_option(@_); }, 'datadir' => sub { &$save_option(@_); }, 'datarootdir' => sub { &$save_option(@_); }, 'docdir=s' => sub { &$save_option(@_); }, 'dvidir' => sub { &$save_option(@_); }, 'htmldir' => sub { &$save_option(@_); }, 'includedir' => sub { &$save_option(@_); }, 'infodir' => sub { &$save_option(@_); }, 'libdir=s' => sub { &$save_option(@_); }, 'libexecdir' => sub { &$save_option(@_); }, 'localedir' => sub { &$save_option(@_); }, 'localstatedir' => sub { &$save_option(@_); }, 'mandir' => sub { &$save_option(@_); }, 'oldincludedir' => sub { &$save_option(@_); }, 'pdfdir' => sub { &$save_option(@_); }, 'psdir' => sub { &$save_option(@_); }, 'sbindir=s' => sub { &$save_option(@_); }, 'sharedstatedir' => sub { &$save_option(@_); }, 'sysconfdir' => sub { &$save_option(@_); }, 'cc=s' => sub { &$save_option('compiler', $_[1]) }, 'os=s' => sub { &$save_option(@_) }, 'cpu=s' => sub { &$save_option(@_) }, 'help' => sub { display_help(); }, 'module-info' => sub { emit_help(module_info()); }, 'version' => sub { emit_help("Botan $VERSION_STRING\n") }, 'with-tr1-implementation=s' => sub { $$config{'tr1'} = $_[1]; }, 'quiet' => sub { $$config{'verbose'} = 0; }, 'trace' => sub { $TRACING = 1; }, 'enable-asm' => sub { $$config{'asm_ok'} = 0; }, 'disable-asm' => sub { $$config{'asm_ok'} = 0; }, 'enable-autoconfig' => sub { $$config{'autoconfig'} = 1; }, 'disable-autoconfig' => sub { $$config{'autoconfig'} = 0; }, 'enable-shared' => sub { $$config{'shared'} = 'yes'; }, 'disable-shared' => sub { $$config{'shared'} = 'no'; }, 'enable-debug' => sub { &$save_option('debug', 1); }, 'disable-debug' => sub { &$save_option('debug', 0); }, 'enable-modules:s' => sub { add_modules($config, $_[1]); }, 'disable-modules:s' => sub { disable_modules($config, $_[1]); }, 'with-openssl' => sub { add_modules($config, 'openssl'); }, 'without-openssl' => sub { disable_modules($config, 'openssl'); }, 'with-gnump' => sub { add_modules($config, 'gnump'); }, 'without-gnump' => sub { disable_modules($config, 'gnump'); }, 'with-bzip2' => sub { add_modules($config, 'bzip2'); }, 'without-bzip2' => sub { disable_modules($config, 'bzip2'); }, 'with-zlib' => sub { add_modules($config, 'zlib'); }, 'without-zlib' => sub { disable_modules($config, 'zlib'); }, 'use-module-set=s' => sub { add_module_sets($config, $_[1]); }, 'with-build-dir=s' => sub { &$save_option(@_); }, 'with-endian=s' => sub { &$save_option(@_); }, 'with-unaligned-mem=s' => sub { &$save_option(@_); }, 'with-local-config=s' => sub { &$save_option('local_config', slurp_file($_[1])); }, 'modules=s' => sub { add_modules($config, $_[1]); }, 'show-arch-info=s' => sub { emit_help(arch_info($_[1])); }, 'make-style=s' => sub { &$save_option(@_); }, 'dumb-gcc|gcc295x' => sub { $$config{'gcc_bug'} = 1; } ); # All arguments should now be consumed croak("Unknown option $ARGV[0] (try --help)") unless($#ARGV == -1); } ################################################## # Functions to search the info tables # ################################################## sub find_arch { my $name = $_[0]; foreach my $arch (keys %CPU) { my %info = %{$CPU{$arch}}; return $arch if($name eq $arch); foreach my $alias (@{$info{'aliases'}}) { return $arch if($name eq $alias); } foreach my $submodel (@{$info{'submodels'}}) { return $arch if($name eq $submodel); } foreach my $submodel (keys %{$info{'submodel_aliases'}}) { return $arch if($name eq $submodel); } } return undef; }; sub figure_out_arch { my ($name) = @_; return ('generic', 'generic') if($name eq 'generic'); my $submodel_alias = sub { my ($name,$info) = @_; my %info = %{$info}; foreach my $submodel (@{$info{'submodels'}}) { return $submodel if($name eq $submodel); } return '' unless defined $info{'submodel_aliases'}; my %sm_aliases = %{$info{'submodel_aliases'}}; foreach my $alias (keys %sm_aliases) { my $official = $sm_aliases{$alias}; return $official if($alias eq $name); } return ''; }; my $arch = find_arch($name); croak("Arch type $name isn't known (try --help)") unless defined $arch; trace("mapped name '$name' to arch '$arch'"); my %archinfo = %{ $CPU{$arch} }; my $submodel = &$submodel_alias($name, \%archinfo); if($submodel eq '') { $submodel = $archinfo{'default_submodel'}; autoconfig("Using $submodel as default type for family ", realname($arch)) if($submodel ne $arch); } trace("mapped name '$name' to submodel '$submodel'"); croak("Couldn't figure out arch type of $name") unless defined($arch) and defined($submodel); return ($arch,$submodel); } sub os_alias { my $name = $_[0]; foreach my $os (keys %OPERATING_SYSTEM) { foreach my $alias (@{$OPERATING_SYSTEM{$os}{'aliases'}}) { if($alias eq $name) { trace("os_alias($name) -> $os"); return $os; } } } return $name; } sub os_info_for { my ($os,$what) = @_; die unless defined($os); croak('os_info_for called with an os of defaults (internal problem)') if($os eq 'defaults'); my $result = ''; if(defined($OPERATING_SYSTEM{$os})) { my %osinfo = %{$OPERATING_SYSTEM{$os}}; $result = $osinfo{$what}; } if(!defined($result) or $result eq '') { $result = $OPERATING_SYSTEM{'defaults'}{$what}; } croak("os_info_for: No info for $what on $os") unless defined $result; return $result; } sub my_compiler { my ($config) = @_; my $cc = $$config{'compiler'}; croak('my_compiler called, but no compiler set in config') unless defined $cc and $cc ne ''; croak("unknown compiler $cc") unless defined $COMPILER{$cc}; return %{$COMPILER{$cc}}; } sub mach_opt { my ($config) = @_; my %ccinfo = my_compiler($config); # Nothing we can do in that case return '' unless $ccinfo{'mach_opt_flags'}; my $submodel = $$config{'submodel'}; my $arch = $$config{'arch'}; if(defined($ccinfo{'mach_opt_flags'}{$submodel})) { return $ccinfo{'mach_opt_flags'}{$submodel}; } elsif(defined($ccinfo{'mach_opt_flags'}{$arch})) { my $mach_opt_flags = $ccinfo{'mach_opt_flags'}{$arch}; my $processed_modelname = $submodel; my $remove = ''; if(defined($ccinfo{'mach_opt_re'}) and defined($ccinfo{'mach_opt_re'}{$arch})) { $remove = $ccinfo{'mach_opt_re'}{$arch}; } $processed_modelname =~ s/$remove//; $mach_opt_flags =~ s/SUBMODEL/$processed_modelname/g; return $mach_opt_flags; } return ''; } ################################################## # # ################################################## sub using_libs { my ($os,@using) = @_; my %libs; foreach my $mod (@using) { my %MOD_LIBS = %{ $MODULES{$mod}{'libs'} }; foreach my $mod_os (keys %MOD_LIBS) { next if($mod_os =~ /^all!$os$/); next if($mod_os =~ /^all!$os,/); next if($mod_os =~ /^all!.*,${os}$/); next if($mod_os =~ /^all!.*,$os,.*/); next unless($mod_os eq $os or ($mod_os =~ /^all.*/)); my @liblist = split(/,/, $MOD_LIBS{$mod_os}); foreach my $lib (@liblist) { $libs{$lib} = 1; } } } return sort keys %libs; } sub libs { my ($prefix,$suffix,@libs) = @_; my $output = ''; foreach my $lib (@libs) { $output .= ' ' if($output ne ''); $output .= $prefix . $lib . $suffix; } return $output; } ################################################## # Path and file manipulation utilities # ################################################## sub portable_symlink { my ($from, $to_dir, $to_fname) = @_; #trace("portable_symlink($from, $to_dir, $to_fname)"); my $can_symlink = 0; my $can_link = 0; unless($^O eq 'MSWin32' or $^O eq 'dos' or $^O eq 'cygwin') { $can_symlink = eval { symlink("",""); 1 }; $can_link = eval { link("",""); 1 }; } chdir $to_dir or croak("Can't chdir to $to_dir ($!)"); if($can_symlink) { symlink $from, $to_fname or croak("Can't symlink $from to $to_fname ($!)"); } elsif($can_link) { link $from, $to_fname or croak("Can't link $from to $to_fname ($!)"); } else { copy ($from, $to_fname) or croak("Can't copy $from to $to_fname ($!)"); } my $go_up = File::Spec->splitdir($to_dir); for(my $j = 0; $j != $go_up; $j++) # return to where we were { chdir File::Spec->updir(); } } sub copy_include_files { my ($config) = @_; my $include_dir = $$config{'build_include_botan'}; trace('Copying to ', $include_dir); foreach my $file (dir_list($include_dir)) { my $path = File::Spec->catfile($include_dir, $file); unlink $path or croak("Could not unlink $path ($!)"); } my $link_up = sub { my ($dir, $file) = @_; my $updir = File::Spec->updir(); portable_symlink(File::Spec->catfile($updir, $updir, $updir, $dir, $file), $include_dir, $file); }; my $files = $$config{'includes'}; foreach my $file (keys %$files) { &$link_up($$files{$file}, $file); } } sub dir_list { my ($dir) = @_; opendir(DIR, $dir) or croak("Couldn't read directory '$dir' ($!)"); my @listing = grep { !/#/ and -f File::Spec->catfile($dir, $_) and $_ ne File::Spec->curdir() and $_ ne File::Spec->updir() } readdir DIR; closedir DIR; return @listing; } sub mkdirs { my (@dirs) = @_; my @created; foreach my $dir (@dirs) { next if( -e $dir and -d $dir ); # skip it if it's already there mkdir($dir, 0777) or croak("Could not create directory $dir ($!)"); push @created, $dir; } return @created; } sub slurp_file { my $file = $_[0]; return '' if(!defined($file) or $file eq ''); croak("'$file': No such file") unless(-e $file); croak("'$file': Not a regular file") unless(-f $file); open FILE, "<$file" or croak("Couldn't read $file ($!)"); my $output = ''; while() { $output .= $_; } close FILE; return $output; } sub which { my $file = $_[0]; my @paths = split(/:/, $ENV{PATH}); foreach my $path (@paths) { my $file_path = File::Spec->catfile($path, $file); return $file_path if(-e $file_path and -r $file_path); } return ''; } # Return a hash mapping every var in a list to a constant value sub map_to { my $var = shift; return map { $_ => $var } @_; } sub in_array { my($target, $array) = @_; return 0 unless defined($array); foreach (@$array) { return 1 if($_ eq $target); } return 0; } sub add_to { my ($to,$from) = @_; foreach my $key (keys %$from) { $$to{$key} = $$from{$key}; } } ################################################## # # ################################################## sub find_mp_bits { my(@modules_list) = @_; my $mp_bits = 32; # default, good for most systems my $seen_mp_module = undef; foreach my $modname (@modules_list) { croak("Unknown module $modname") unless defined $MODULES{$modname}; my %modinfo = %{ $MODULES{$modname} }; if($modinfo{'mp_bits'}) { if(defined($seen_mp_module) and $modinfo{'mp_bits'} != $mp_bits) { croak('Inconsistent mp_bits requests from modules ', $seen_mp_module, ' and ', $modname); } $seen_mp_module = $modname; $mp_bits = $modinfo{'mp_bits'}; } } return $mp_bits; } ################################################## # # ################################################## sub realname { my $arg = $_[0]; return $COMPILER{$arg}{'realname'} if defined $COMPILER{$arg}; return $OPERATING_SYSTEM{$arg}{'realname'} if defined $OPERATING_SYSTEM{$arg}; return $CPU{$arg}{'realname'} if defined $CPU{$arg}; return $arg; } ################################################## # # ################################################## sub load_module { my ($config, $modname) = @_; #trace("load_module($modname)"); croak("Unknown module $modname") unless defined($MODULES{$modname}); my %module = %{$MODULES{$modname}}; my $works_on = sub { my ($what, $lst_ref) = @_; my @lst = @{$lst_ref}; return 1 if not @lst; # empty list -> no restrictions return 1 if $what eq 'generic'; # trust the user return in_array($what, \@lst); }; # Check to see if everything is OK WRT system requirements my $os = $$config{'os'}; croak("Module '$modname' does not run on $os") unless(&$works_on($os, $module{'os'})); my $arch = $$config{'arch'}; my $sub = $$config{'submodel'}; croak("Module '$modname' does not run on $arch/$sub") unless(&$works_on($arch, $module{'arch'}) or &$works_on($sub, $module{'arch'})); my $cc = $$config{'compiler'}; croak("Module '$modname' does not work with $cc") unless(&$works_on($cc, $module{'cc'})); my $handle_files = sub { my($lst, $func) = @_; return unless defined($lst); foreach (sort @$lst) { &$func($module{'moddirs'}, $config, $_); } }; &$handle_files($module{'ignore'}, \&ignore_file); &$handle_files($module{'add'}, \&add_file); &$handle_files($module{'replace'}, sub { ignore_file(@_); add_file(@_); }); warning($modname, ': ', $module{'note'}) if(defined($module{'note'})); } sub load_modules { my ($config) = @_; my @mod_names; foreach my $mod (sort keys %{$$config{'modules'}}) { next unless($$config{'modules'}{$mod} > 0); load_module($config, $mod); push @mod_names, $mod; } $$config{'mod-list'} = join("\n", @mod_names); my $unaligned_ok = 0; my $gen_defines = sub { my @macro_list; my $os = $$config{'os'}; if($os ne 'generic') { push @macro_list, '#define BOTAN_TARGET_OS_IS_' . uc $os; my @features = @{$OPERATING_SYSTEM{$os}{'target_features'}}; for my $feature (@features) { push @macro_list, '#define BOTAN_TARGET_OS_HAS_' . uc $feature; } push @macro_list, ""; } my $arch = $$config{'arch'}; if($arch ne 'generic') { my %cpu_info = %{$CPU{$arch}}; my $endian = $cpu_info{'endian'}; if(defined($$config{'with_endian'})) { $endian = $$config{'with_endian'}; $endian = undef unless($endian eq 'little' || $endian eq 'big'); } elsif(defined($endian)) { autoconfig("Since arch is $arch, assuming $endian endian mode"); } push @macro_list, "#define BOTAN_TARGET_ARCH_IS_" . (uc $arch); my $submodel = $$config{'submodel'}; if($arch ne $submodel) { $submodel = uc $submodel; $submodel =~ tr/-/_/; push @macro_list, "#define BOTAN_TARGET_CPU_IS_$submodel"; } if(defined($endian)) { $endian = uc $endian; push @macro_list, "#define BOTAN_TARGET_CPU_IS_${endian}_ENDIAN"; # See if the user set --with-unaligned-mem if(defined($$config{'with_unaligned_mem'})) { my $spec = $$config{'with_unaligned_mem'}; if($spec eq 'yes') { $unaligned_ok = 1; } elsif($spec eq 'no') { $unaligned_ok = 0; } else { warning('Unknown arg to --with-unaligned-mem (' . $spec . ') will ignore'); $unaligned_ok = 0; } } # Otherwise, see if the CPU has a default setting elsif(defined($cpu_info{'unaligned'}) and $cpu_info{'unaligned'} eq 'ok') { autoconfig("Since arch is $arch, " . 'assuming unaligned memory access is OK'); $unaligned_ok = 1; } } } # variable is always set (one or zero) push @macro_list, "#define BOTAN_TARGET_UNALIGNED_LOADSTOR_OK $unaligned_ok"; if(defined($$config{'tr1'})) { my $tr1 = $$config{'tr1'}; if($tr1 eq 'system') { push @macro_list, '#define BOTAN_USE_STD_TR1'; } elsif($tr1 eq 'boost') { push @macro_list, '#define BOTAN_USE_BOOST_TR1'; } elsif($tr1 ne 'none') { croak("Unknown --with-tr1= option value '$tr1' (try --help)"); } } my %defines; foreach my $mod (sort keys %{$$config{'modules'}}) { next unless $$config{'modules'}{$mod} > 0; my $defs = $MODULES{$mod}{'define'}; next unless $defs; push @{$defines{$MODULES{$mod}{'type'}}}, split(/,/, $defs); } foreach my $type (sort keys %defines) { push @macro_list, "\n/* $type */"; for my $macro (@{$defines{$type}}) { die unless(defined $macro and $macro ne ''); push @macro_list, "#define BOTAN_HAS_$macro"; } } return join("\n", @macro_list); }; $$config{'defines'} = &$gen_defines(); } ################################################## # # ################################################## sub file_type { my ($file) = @_; return 'sources' if($file =~ /\.cpp$/ or $file =~ /\.c$/ or $file =~ /\.S$/); return 'includes' if($file =~ /\.h$/); croak('file_type() - don\'t know what sort of file ', $file, ' is'); } sub add_file { my ($mod_dir, $config, $file) = @_; check_for_file($config, $file, $mod_dir, $mod_dir); my $do_add_file = sub { my ($type) = @_; croak("File $file already added from ", $$config{$type}{$file}) if(defined($$config{$type}{$file})); if($file =~ /(.*):(.*)/) { my @dirs = File::Spec->splitdir($mod_dir); $dirs[$#dirs-1] = $1; $$config{$type}{$2} = File::Spec->catdir(@dirs); } else { $$config{$type}{$file} = $mod_dir; } }; &$do_add_file(file_type($file)); } sub ignore_file { my ($mod_dir, $config, $file) = @_; check_for_file($config, $file, undef, $mod_dir); my $do_ignore_file = sub { my ($type, $ok_if_from) = @_; if(defined ($$config{$type}{$file})) { croak("$mod_dir - File $file modified from ", $$config{$type}{$file}) if($$config{$type}{$file} ne $ok_if_from); delete $$config{$type}{$file}; } }; &$do_ignore_file(file_type($file)); } sub check_for_file { my ($config, $file, $added_from, $mod_dir) = @_; #trace("check_for_file($file, $added_from, $mod_dir)"); my $full_path = sub { my ($file,$mod_dir) = @_; if($file =~ /(.*):(.*)/) { return File::Spec->catfile($mod_dir, '..', $1, $2); } else { return File::Spec->catfile($mod_dir, $file) if(defined($mod_dir)); my @typeinfo = file_type($config, $file); return File::Spec->catfile($typeinfo[1], $file); } }; $file = &$full_path($file, $added_from); croak("Module $mod_dir requires that file $file exist. This error\n ", 'should never occur; please contact the maintainers with details.') unless(-e $file); } ################################################## # # ################################################## sub process_template { my ($in, $out, $config) = @_; trace("process_template: $in -> $out"); my $contents = slurp_file($in); foreach my $name (keys %$config) { my $val = $$config{$name}; unless(defined $val) { trace("Undefined variable $name in $in"); next; } $contents =~ s/@\{var:$name\}/$val/g; unless($val eq 'no' or $val eq 'false') { $contents =~ s/\@\{if:$name (.*)\}/$1/g; $contents =~ s/\@\{if:$name (.*) (.*)\}/$1/g; } else { $contents =~ s/\@\{if:$name (.*)\}//g; $contents =~ s/\@\{if:$name (.*) (.*)\}/$2/g; } } if($contents =~ /@\{var:([a-z_]*)\}/ or $contents =~ /@\{if:(.*) /) { sub summarize { my ($n, $s) = @_; $s =~ s/\n/\\n/; # escape newlines return $s if(length($s) <= $n); return substr($s, 0, 57) . '...'; } foreach my $key (sort keys %$config) { print with_diagnostic("debug", "In %config:", $key, " -> ", summarize(60, $$config{$key})); } croak("Unbound variable '$1' in $in"); } open OUT, ">$out" or croak("Couldn't write $out ($!)"); print OUT $contents; close OUT; } ################################################## # # ################################################## sub read_list { my ($line, $reader, $marker, $func) = @_; if($line =~ m@^<$marker>$@) { while(1) { $line = &$reader(); die "EOF while searching for $marker" unless $line; last if($line =~ m@^$@); &$func($line); } } } sub list_push { my ($listref) = @_; return sub { push @$listref, $_[0]; } } sub match_any_of { my ($line, $hash, $quoted, @any_of) = @_; $quoted = ($quoted eq 'quoted') ? 1 : 0; foreach my $what (@any_of) { $$hash{$what} = $1 if(not $quoted and $line =~ /^$what (.*)/); $$hash{$what} = $1 if($quoted and $line =~ /^$what \"(.*)\"/); } } ################################################## # # ################################################## sub make_reader { my $filename = $_[0]; croak("make_reader(): Arg was undef") if not defined $filename; open FILE, "<$filename" or croak("Couldn't read $filename ($!)"); return sub { my $line = ''; while(1) { my $line = ; last unless defined($line); chomp($line); $line =~ s/#.*//; $line =~ s/^\s*//; $line =~ s/\s*$//; $line =~ s/\s\s*/ /; $line =~ s/\t/ /; return $line if $line ne ''; } close FILE; return undef; } } ################################################## # # ################################################## sub read_info_files { my ($config, $dir, $func) = @_; $dir = File::Spec->catdir($$config{'config-dir'}, $dir); my %allinfo; foreach my $file (dir_list($dir)) { my $fullpath = File::Spec->catfile($dir, $file); trace("reading $fullpath"); %{$allinfo{$file}} = &$func($file, $fullpath); } return %allinfo; } sub read_module_files { my ($config) = @_; my %allinfo; my @modinfos; File::Find::find( { wanted => sub { if(-f $_ && /^info\.txt\z/s) { my $name = $File::Find::name; push @modinfos, $name; } } }, $$config{'src-dir'}); foreach my $modfile (@modinfos) { trace("reading $modfile"); my ($volume,$dirs,$file) = File::Spec->splitpath($modfile); my @dirs = File::Spec->splitdir($dirs); my $moddir = $dirs[$#dirs-1]; trace("module $moddir in $dirs $modfile"); %{$allinfo{$moddir}} = get_module_info($dirs, $moddir, $modfile); } return %allinfo; } ################################################## # # ################################################## sub get_module_info { my ($dirs, $name, $modfile) = @_; my $reader = make_reader($modfile); my %info; $info{'name'} = $name; $info{'modinfo'} = $modfile; $info{'moddirs'} = $dirs; # Default module settings $info{'load_on'} = 'request'; # default unless specified $info{'uses_tr1'} = 'no'; $info{'libs'} = {}; $info{'use'} = 'no'; my @dir_arr = File::Spec->splitdir($dirs); $info{'type'} = $dir_arr[$#dir_arr-2]; # cipher, hash, ... if($info{'type'} eq 'src') { $info{'type'} = $dir_arr[$#dir_arr-1]; } while($_ = &$reader()) { match_any_of($_, \%info, 'quoted', 'realname', 'note', 'type'); match_any_of($_, \%info, 'unquoted', 'define', 'mp_bits', 'modset', 'load_on', 'uses_tr1'); read_list($_, $reader, 'arch', list_push(\@{$info{'arch'}})); read_list($_, $reader, 'cc', list_push(\@{$info{'cc'}})); read_list($_, $reader, 'os', list_push(\@{$info{'os'}})); read_list($_, $reader, 'add', list_push(\@{$info{'add'}})); read_list($_, $reader, 'replace', list_push(\@{$info{'replace'}})); read_list($_, $reader, 'ignore', list_push(\@{$info{'ignore'}})); read_list($_, $reader, 'requires', list_push(\@{$info{'requires'}})); read_list($_, $reader, 'libs', sub { my $line = $_[0]; $line =~ m/^([\w!,]*) -> ([\w,-]*)$/; $info{'libs'}{$1} = $2; }); if(/^require_version /) { if(/^require_version (\d+)\.(\d+)\.(\d+)$/) { my $version = "$1.$2.$3"; my $needed_version = 100*$1 + 10*$2 + $3; my $have_version = 100*$MAJOR_VERSION + 10*$MINOR_VERSION + $PATCH_VERSION; if($needed_version > $have_version) { warning("Module $name needs v$version; disabling"); return (); } } else { croak("In module $name, bad version requirement '$_'"); } } } return %info; } ################################################## # # ################################################## sub get_arch_info { my ($name,$file) = @_; my $reader = make_reader($file); my %info; $info{'name'} = $name; while($_ = &$reader()) { match_any_of($_, \%info, 'quoted', 'realname'); match_any_of($_, \%info, 'unquoted', 'default_submodel', 'endian', 'unaligned'); read_list($_, $reader, 'aliases', list_push(\@{$info{'aliases'}})); read_list($_, $reader, 'submodels', list_push(\@{$info{'submodels'}})); read_list($_, $reader, 'submodel_aliases', sub { my $line = $_[0]; $line =~ m/^(\S*) -> (\S*)$/; $info{'submodel_aliases'}{$1} = $2; }); } return %info; } ################################################## # # ################################################## sub get_os_info { my ($name,$file) = @_; my $reader = make_reader($file); my %info; $info{'name'} = $name; while($_ = &$reader()) { match_any_of($_, \%info, 'quoted', 'realname', 'ar_command'); match_any_of($_, \%info, 'unquoted', 'os_type', 'obj_suffix', 'so_suffix', 'static_suffix', 'install_root', 'header_dir', 'lib_dir', 'doc_dir', 'ar_needs_ranlib', 'install_cmd_data', 'install_cmd_exec'); read_list($_, $reader, 'aliases', list_push(\@{$info{'aliases'}})); read_list($_, $reader, 'target_features', list_push(\@{$info{'target_features'}})); read_list($_, $reader, 'supports_shared', list_push(\@{$info{'supports_shared'}})); } return %info; } ################################################## # Read a file from misc/config/cc and set the values from # there into a hash for later reference ################################################## sub get_cc_info { my ($name,$file) = @_; my $reader = make_reader($file); my %info; $info{'name'} = $name; while($_ = &$reader()) { match_any_of($_, \%info, 'quoted', 'realname', 'binary_name', 'compile_option', 'output_to_option', 'add_include_dir_option', 'add_lib_dir_option', 'add_lib_option', 'lib_opt_flags', 'check_opt_flags', 'dll_import_flags', 'dll_export_flags', 'lang_flags', 'warning_flags', 'shared_flags', 'ar_command', 'debug_flags', 'no_debug_flags'); match_any_of($_, \%info, 'unquoted', 'makefile_style', 'compiler_has_tr1'); sub quoted_mapping { my $hashref = $_[0]; return sub { my $line = $_[0]; $line =~ m/^(\S*) -> \"(.*)\"$/; $$hashref{$1} = $2; } } read_list($_, $reader, 'mach_abi_linking', quoted_mapping(\%{$info{'mach_abi_linking'}})); read_list($_, $reader, 'so_link_flags', quoted_mapping(\%{$info{'so_link_flags'}})); read_list($_, $reader, 'mach_opt', sub { my $line = $_[0]; $line =~ m/^(\S*) -> \"(.*)\" ?(.*)?$/; $info{'mach_opt_flags'}{$1} = $2; $info{'mach_opt_re'}{$1} = $3; }); } return %info; } ################################################## # # ################################################## sub write_pkg_config { my ($config) = @_; return if($$config{'os'} eq 'generic' or $$config{'os'} eq 'windows'); $$config{'link_to'} = libs('-l', '', 'm', @{$$config{'mod_libs'}}); my $botan_config = $$config{'botan-config'}; process_template( File::Spec->catfile($$config{'config-dir'}, 'botan-config.in'), $botan_config, $config); chmod 0755, $botan_config; process_template( File::Spec->catfile($$config{'config-dir'}, 'botan.pc.in'), $$config{'botan-pkgconfig'}, $config); delete $$config{'link_to'}; } ################################################## # # ################################################## sub file_list { my ($put_in, $from, $to, %files) = @_; my $list = ''; my $spaces = 16; foreach (sort keys %files) { my $file = $_; $file =~ s/$from/$to/ if(defined($from) and defined($to)); my $dir = $files{$_}; $dir = $put_in if defined $put_in; if(defined($dir)) { $list .= File::Spec->catfile ($dir, $file); } else { $list .= $file; } $list .= " \\\n "; } $list =~ s/\\\n +$//; # remove trailing escape return $list; } sub build_cmds { my ($config, $dir, $flags, $files) = @_; my $obj_suffix = $$config{'obj_suffix'}; my %ccinfo = my_compiler($config); my $inc = $ccinfo{'add_include_dir_option'}; my $from = $ccinfo{'compile_option'}; my $to = $ccinfo{'output_to_option'}; my $inc_dir = $$config{'build_include'}; # Probably replace by defaults to -I -c -o croak('undef value found in build_cmds') unless defined($inc) and defined($from) and defined($to); my $bld_line = "\t\$(CXX) $inc$inc_dir $flags $from\$? $to\$@"; my @output_lines; foreach (sort keys %$files) { my $src_file = File::Spec->catfile($$files{$_}, $_); my $obj_file = File::Spec->catfile($dir, $_); $obj_file =~ s/\.cpp$/.$obj_suffix/; $obj_file =~ s/\.c$/.$obj_suffix/; $obj_file =~ s/\.S$/.$obj_suffix/; push @output_lines, "$obj_file: $src_file\n$bld_line"; } return join("\n\n", @output_lines); } sub determine_config { my ($config) = @_; sub os_ar_command { return os_info_for(shift, 'ar_command'); } sub append_if { my($var,$addme,$cond) = @_; croak('append_if: reference was undef') unless defined $var; if($cond and $addme ne '') { $$var .= ' ' unless($$var eq '' or $$var =~ / $/); $$var .= $addme; } } sub append_ifdef { my($var,$addme) = @_; append_if($var, $addme, defined($addme)); } my $empty_if_nil = sub { my $val = $_[0]; return $val if defined($val); return ''; }; my %ccinfo = my_compiler($config); my $lang_flags = ''; append_ifdef(\$lang_flags, $ccinfo{'lang_flags'}); append_if(\$lang_flags, "-fpermissive", $$config{'gcc_bug'}); my $debug = $$config{'debug'}; my $lib_opt_flags = ''; append_ifdef(\$lib_opt_flags, $ccinfo{'lib_opt_flags'}); append_ifdef(\$lib_opt_flags, $ccinfo{'debug_flags'}) if($debug); append_ifdef(\$lib_opt_flags, $ccinfo{'no_debug_flags'}) if(!$debug); # This is a default that works on most Unix and Unix-like systems my $ar_command = 'ar crs'; my $ranlib_command = 'true'; # almost no systems need it anymore # See if there are any over-riding methods. We presume if CC is creating # the static libs, it knows how to create the index itself. my $os = $$config{'os'}; if($ccinfo{'ar_command'}) { $ar_command = $ccinfo{'ar_command'}; } elsif(os_ar_command($os)) { $ar_command = os_ar_command($os); $ranlib_command = 'ranlib' if(os_info_for($os, 'ar_needs_ranlib') eq 'yes'); } my $arch = $$config{'arch'}; my $abi_opts = ''; append_ifdef(\$abi_opts, $ccinfo{'mach_abi_linking'}{$arch}); append_ifdef(\$abi_opts, $ccinfo{'mach_abi_linking'}{$os}); append_ifdef(\$abi_opts, $ccinfo{'mach_abi_linking'}{'all'}); $abi_opts = ' ' . $abi_opts if($abi_opts ne ''); if($$config{'shared'} eq 'yes' and (in_array('all', $OPERATING_SYSTEM{$os}{'supports_shared'}) or in_array($$config{'compiler'}, $OPERATING_SYSTEM{$os}{'supports_shared'}))) { $$config{'shared_flags'} = &$empty_if_nil($ccinfo{'shared_flags'}); $$config{'so_link'} = &$empty_if_nil($ccinfo{'so_link_flags'}{$os}); if($$config{'so_link'} eq '') { $$config{'so_link'} = &$empty_if_nil($ccinfo{'so_link_flags'}{'default'}) } if($$config{'shared_flags'} eq '' and $$config{'so_link'} eq '') { $$config{'shared'} = 'no'; warning($$config{'compiler'}, ' has no shared object flags set ', "for $os; disabling shared"); } } else { autoconfig("No shared library generated with " . $$config{'compiler'} . " on " . $$config{'os'}); $$config{'shared'} = 'no'; $$config{'shared_flags'} = ''; $$config{'so_link'} = ''; } add_to($config, { 'cc' => $ccinfo{'binary_name'} . $abi_opts, 'lib_opt' => $lib_opt_flags, 'check_opt' => &$empty_if_nil($ccinfo{'check_opt_flags'}), 'mach_opt' => mach_opt($config), 'lang_flags' => $lang_flags, 'warn_flags' => &$empty_if_nil($ccinfo{'warning_flags'}), 'ar_command' => $ar_command, 'ranlib_command' => $ranlib_command, 'static_suffix' => os_info_for($os, 'static_suffix'), 'so_suffix' => os_info_for($os, 'so_suffix'), 'obj_suffix' => os_info_for($os, 'obj_suffix'), 'dll_export_flags' => $ccinfo{'dll_export_flags'}, 'dll_import_flags' => $ccinfo{'dll_import_flags'}, 'install_cmd_exec' => os_info_for($os, 'install_cmd_exec'), 'install_cmd_data' => os_info_for($os, 'install_cmd_data'), }); } sub generate_makefile { my ($config) = @_; my $is_in_doc_dir = sub { -e File::Spec->catfile($$config{'doc-dir'}, $_[0]) }; my $docs = file_list(undef, undef, undef, map_to($$config{'doc-dir'}, grep { &$is_in_doc_dir($_); } @DOCS)); $docs .= File::Spec->catfile($$config{'base-dir'}, 'readme.txt'); my $includes = file_list(undef, undef, undef, map_to($$config{'build_include_botan'}, keys %{$$config{'includes'}})); my $lib_objs = file_list($$config{'build_lib'}, '(\.cpp$|\.c$|\.S$)', '.' . $$config{'obj_suffix'}, %{$$config{'sources'}}); my $check_objs = file_list($$config{'build_check'}, '.cpp', '.' . $$config{'obj_suffix'}, %{$$config{'check_src'}}), my $lib_build_cmds = build_cmds($config, $$config{'build_lib'}, '$(LIB_FLAGS)', $$config{'sources'}); my $check_build_cmds = build_cmds($config, $$config{'build_check'}, '$(CHECK_FLAGS)', $$config{'check_src'}); add_to($config, { 'lib_objs' => $lib_objs, 'check_objs' => $check_objs, 'lib_build_cmds' => $lib_build_cmds, 'check_build_cmds' => $check_build_cmds, 'doc_files' => $docs, 'include_files' => $includes }); my $template_dir = File::Spec->catdir($$config{'config-dir'}, 'makefile'); my $template = undef; my $make_style = $$config{'make_style'}; if($make_style eq 'unix') { $template = File::Spec->catfile($template_dir, 'unix.in'); $template = File::Spec->catfile($template_dir, 'unix_shr.in') if($$config{'shared'} eq 'yes'); add_to($config, { 'link_to' => libs('-l', '', 'm', @{$$config{'mod_libs'}}), }); } elsif($make_style eq 'nmake') { $template = File::Spec->catfile($template_dir, 'nmake.in'); add_to($config, { 'shared' => 'no', 'link_to' => libs('', '.'.$$config{'static_suffix'}, @{$$config{'mod_libs'}}), }); } croak("Don't know about makefile format '$make_style'") unless defined $template; trace("'$make_style' -> '$template'"); process_template($template, $$config{'makefile'}, $config); autoconfig("Wrote ${make_style}-style makefile in $$config{'makefile'}"); } ################################################## # Configuration Guessing # ################################################## sub guess_cpu_from_this { my $cpuinfo = lc $_[0]; $cpuinfo =~ s/\(r\)//g; $cpuinfo =~ s/\(tm\)//g; $cpuinfo =~ s/ //g; trace("guess_cpu_from_this($cpuinfo)"); # The 32-bit SPARC stuff is impossible to match to arch type easily, and # anyway the uname stuff will pick up that it's a SPARC so it doesn't # matter. If it's an Ultra, assume a 32-bit userspace, no 64-bit code # possible; that's the most common setup right now anyway return 'sparc32-v9' if($cpuinfo =~ /ultrasparc/); # Should probably do this once and cache it my @names; my %all_alias; foreach my $arch (keys %CPU) { my %info = %{$CPU{$arch}}; foreach my $submodel (@{$info{'submodels'}}) { push @names, $submodel; } if(defined($info{'submodel_aliases'})) { my %submodel_aliases = %{$info{'submodel_aliases'}}; foreach my $sm_alias (keys %submodel_aliases) { push @names, $sm_alias; $all_alias{$sm_alias} = $submodel_aliases{$sm_alias}; } } } @names = sort { length($b) <=> length($a) } @names; foreach my $name (@names) { if($cpuinfo =~ $name) { trace("Matched '$cpuinfo' against '$name'"); return $all_alias{$name} if defined($all_alias{$name}); return $name; } } trace("Couldn't match $cpuinfo against any submodels"); # No match? Try arch names. Reset @names @names = (); foreach my $arch (keys %CPU) { my %info = %{$CPU{$arch}}; push @names, $info{'name'}; foreach my $alias (@{$info{'aliases'}}) { push @names, $alias; } } @names = sort { length($b) <=> length($a) } @names; foreach my $name (@names) { if($cpuinfo =~ $name) { trace("Matched '$cpuinfo' against '$name'"); return $name; } } return ''; } # Do some WAGing and see if we can figure out what system we are. Think about # this as a really moronic config.guess sub guess_compiler { my @CCS = ('gcc', 'msvc', 'icc', 'compaq', 'kai'); # First try the CC enviornmental variable, if it's set if(defined($ENV{CC})) { my @new_CCS = ($ENV{CC}); foreach my $cc (@CCS) { push @new_CCS, $cc; } @CCS = @new_CCS; } foreach (@CCS) { my $bin_name = $COMPILER{$_}{'binary_name'}; if(which($bin_name) ne '') { autoconfig("Guessing to use $_ as the compiler " . "(use --cc to set)"); return $_; } } croak( "Can't find a usable C++ compiler, is PATH right?\n" . "You might need to run with the --cc option (try $0 --help)\n"); } sub guess_os { sub recognize_os { my $os = os_alias($_[0]); if(defined($OPERATING_SYSTEM{$os})) { autoconfig("Guessing operating system is $os (use --os to set)"); return $os; } return undef; } my $guess = recognize_os($^O); return $guess if $guess; trace("Can't guess os from $^O"); my $uname = $$config{'uname'}; if($uname ne '') { $guess = recognize_os($uname); return $guess if $guess; trace("Can't guess os from $uname"); } warning("Unknown OS ('$^O', '$uname'), falling back to generic code"); return 'generic'; } sub guess_cpu { # If we have /proc/cpuinfo, try to get nice specific information about # what kind of CPU we're running on. my $cpuinfo = '/proc/cpuinfo'; if(defined($ENV{'CPUINFO'})) { my $cpuinfo_env = $ENV{'CPUINFO'}; if(-e $cpuinfo_env and -r $cpuinfo_env) { autoconfig("Will use $cpuinfo_env as /proc/cpuinfo"); $cpuinfo = $cpuinfo_env; } else { warn("Could not read from ENV /proc/cpuinfo ($cpuinfo_env)"); } } if(-e $cpuinfo and -r $cpuinfo) { open CPUINFO, $cpuinfo or die "Could not read $cpuinfo\n"; while() { chomp; $_ =~ s/\t/ /g; $_ =~ s/ +/ /g; if($_ =~ /^cpu +: (.*)/ or $_ =~ /^model name +: (.*)/) { my $cpu = guess_cpu_from_this($1); if($cpu ne '') { autoconfig("Guessing CPU using $cpuinfo line '$_'"); autoconfig("Guessing CPU is a $cpu (use --cpu to set)"); return $cpu; } } } autoconfig("*** Could not figure out CPU based on $cpuinfo"); autoconfig("*** Please mail contents to lloyd\@randombit.net"); } sub known_arch { my ($name) = @_; foreach my $arch (keys %CPU) { my %info = %{$CPU{$arch}}; return 1 if $name eq $info{'name'}; foreach my $submodel (@{$info{'submodels'}}) { return 1 if $name eq $submodel; } foreach my $alias (@{$info{'aliases'}}) { return 1 if $name eq $alias; } if(defined($info{'submodel_aliases'})) { my %submodel_aliases = %{$info{'submodel_aliases'}}; foreach my $sm_alias (keys %submodel_aliases) { return 1 if $name eq $sm_alias; } } } my $guess = guess_cpu_from_this($name); return 0 if($guess eq $name or $guess eq ''); return known_arch($guess); } my $uname = $$config{'uname'}; if($uname ne '') { my $cpu = guess_cpu_from_this($uname); if($cpu ne '') { autoconfig("Guessing CPU using uname output '$uname'"); autoconfig("Guessing CPU is a $cpu (use --cpu to set)"); return $cpu if known_arch($cpu); } } my $config_archname = $Config{'archname'}; my $cpu = guess_cpu_from_this($config_archname); if($cpu ne '') { autoconfig("Guessing CPU using Config{archname} '$config_archname'"); autoconfig("Guessing CPU is a $cpu (use --cpu to set)"); return $cpu if known_arch($cpu); } warning("Could not determine CPU type (try --cpu option)"); return 'generic'; }