From 485e7216440ef4bed14785b9434e8763f307c735 Mon Sep 17 00:00:00 2001 From: Tzafrir Cohen Date: Tue, 14 Apr 2009 21:01:53 +0300 Subject: dummy-select: a version for asteris 1.6.2/trunk --- menuselect/.gitignore | 6 + menuselect/Makefile | 17 ++ menuselect/menuselect | 617 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 640 insertions(+) create mode 100644 menuselect/.gitignore create mode 100644 menuselect/Makefile create mode 100755 menuselect/menuselect (limited to 'menuselect') diff --git a/menuselect/.gitignore b/menuselect/.gitignore new file mode 100644 index 0000000..fc5c7d9 --- /dev/null +++ b/menuselect/.gitignore @@ -0,0 +1,6 @@ +linkedlists.h +menuselect.c +menuselect.h +menuselect_curses.c +menuselect_stub.c +.*.swp diff --git a/menuselect/Makefile b/menuselect/Makefile new file mode 100644 index 0000000..b8e5d5e --- /dev/null +++ b/menuselect/Makefile @@ -0,0 +1,17 @@ +all: + +clean: + +dist-clean: + +makeopts: + +menuselect: + ./menuselelct + +# Generate some dummy files to satifsy dependencies from the main +# Makefile. +dummies: + touch linkedlists.h menuselect.c menuselect_curses.c \ + menuselect.h menuselect_stub.c + diff --git a/menuselect/menuselect b/menuselect/menuselect new file mode 100755 index 0000000..24fcecd --- /dev/null +++ b/menuselect/menuselect @@ -0,0 +1,617 @@ +#!/usr/bin/perl -w + +# menuselect - a simple drop-in replacement of the batch-mode menuselect +# included with Asterisk. +# +# Copyright (C) 2008 by Tzafrir Cohen +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA + +# Installation: copy this script to menuselect/menuselect . Copy the +# included Makefile as menuselect/Makefile and run: +# +# make -C makefile dummies +# +# It takes configuration from build_tools/conf . Sample config file: +# +# By default all modules will be built (except those marked not be +# used by default) +# +# # exclude: Don't try to build the following modules. +# #exclude app_test +# +# # You can have multiple items in each line, and multiple lines. +# # Each item is a perl regular expression that must match the whole +# # module name. +# #exclude res_config_.* +# +# # include: syntax is the same as exclude. Overrides exclude and +# # modules that are marked as disabled by defualt: +# #include res_config_sqlite3 app_skel +# +# # If you want to make sure some modules will be conifgured to build, +# # you can require them. If modules that match any of the 'require' +# # pattern are not configured to build, menuselect will panic. +# # Same pattern rules apply here. Why would you want that? I have no +# # idea. +# #require chan_h323 app_directory +# +# # random - the value for this keyword is a number between 1 and +# # 100. The higher it is, more chances not to include each module. +# # Writes the list of modules that got hit to +# # build_tools/mods_removed_random . +# # Note that unlike 'make randomconfig' and such the random +# # configuration changes each time you run 'make', thus if a build +# # failed you should first read build_tools/mods_removed_random +# # before re-running make. +# #random 10 +# +# # Anything after a '#' is ignored, and likewise empty lines. +# # Naturally. + +use strict; +use Getopt::Long; + +# Holds global dependncy information. Keys are module names. +my %ModInfo = (); + +# extract configuration from kernel modules: +my $AutoconfDepsFile = "build_tools/menuselect-deps"; + +# configuration file to read for some directives: +my $ConfFile = "build_tools/conf"; + +my $DumpFile = 'build_tools/dump_deps'; + +# Modules removed randomely: +my $RandomeModsFile = "build_tools/mods_removed_random"; + +my $MakedepsFile = "menuselect.makedeps"; + +my $MakeoptsFile = "menuselect.makeopts"; + +# If those modules are not present, the build will fail (PCRE patterns) +my @RequiredModules = (); + +my @Subdirs = qw/apps cdr channels codecs formats funcs main pbx res utils/; + +my @XmlCategories = 'cflags'; + +# Modules should not bother building (PCRE patterns) +my @ExcludedModules = (); + +# Do try building those. Overrides 'exclude' and 'defaultenable: no' +my @IncludedModules = (); + +# A chance to rule-out a module randomely. +my $RandomKnockoutFactor = 0; + +sub warning($) { + my $msg = shift; + print STDERR "$0: Warning: $msg\n"; +} + +# Convert XML syntax to mail-header-like syntax: +# value --> Var: value +sub extract_xml_key($) { + my $xml_line = shift; + if ($xml_line !~ m{^\s*<([a-zA-Z0-9]*)>([^<]*)}) { + warning "parsed empty value from XML line $xml_line"; + return ('', ''); # warn? + } + my ($var, $val) = ($1, $2); + $var =~ s{^[a-z]}{\u$&}; + return ($var, $val); +} + +# Get information embedded in source files from a subdirectory. +# First parameter is the subdirectory and further ones are the actual +# source files. +sub get_subdir_module_info { + my $subdir = shift; + my @files = @_; + + my $dir = uc($subdir); + + foreach my $src (@files) { + open SRC,$src or die "Can't read from source file $src: $!\n"; + $src =~ m|.*/([^/]*)\.c|; + my $mod_name = $1; + my %data = ( + Type=>'module', + Module=>$mod_name, + Dir=> $dir, + Avail=>1 + ); + + while () { + next unless (m|^/\*\*\* MODULEINFO| .. m|^ ?\*\*\*/|); + next unless (m|^[A-Z]| || m|^\s*<|); + + # At this point we can assume we're in the module + # info section. + chomp; + my ($var, $val) = extract_xml_key($_); + + if ($var =~ /^(Depend|Use)$/i) { + # use uppercase for dependency names; + $val = uc($val); + } + if ( ! exists $data{$var} ) { + $data{$var} = [$val]; + } else { + push @{$data{$var}},($val); + } + } + close SRC; + + $ModInfo{uc($mod_name)} = \%data; + } +} + +# extract embedded information in all the source tree. +sub extract_subdirs { + for my $subdir(@_) { + get_subdir_module_info($subdir, <$subdir/*.c> , <$subdir/*.cc>); + } +} + +# parse a partial XML document that is included as an input +# for menuselect in a few places. Naturally a full-fledged XML parsing +# will not be done here. A line-based parsing that happens to work will +# have to do. +sub parse_menuselect_xml_file($) { + my $file_name = shift; + open XML,$file_name or + die "Failed opening XML file $file_name: $!.\n"; + + my $header = ; + $header =~ /^\s*){ + next unless (m{^\s*<(/?[a-z]+)[>\s]}); + my $tag = $1; + + if ($tag eq 'member') { + if (! m{^\s*}){ + warning "Bad XML member line: $_ ($file_name:$.)\n"; + next; + } + my ($name, $display_name) = ($1, $2); + + $member = { + Type => 'XML', + Dir => $category, + Module => $1, + DisplayName => $2, + Avail => 1, + + }; + } elsif ($tag eq '/member') { + $ModInfo{$member->{Module}} = $member; + } elsif ($tag eq '/category') { + last; + } else { + if (! m/^\s*<([a-z]+)>([^<]+){$key}) { + $member->{$key} = []; + } + push @{$member->{$key}}, ($val); + } + } + + + close XML; +} + +# Dump our data structure to a file. +sub dump_deps($) { + my $file = shift; + open OUTPUT,">$file" or + die "cannot open category file $file for writing: $!\n"; + + foreach my $mod_name (sort keys %ModInfo) { + print OUTPUT "Key: $mod_name\n"; + my $data = $ModInfo{$mod_name}; + foreach my $var (sort keys %{$data} ) { + my $val = $$data{$var}; + if (ref($val) eq 'ARRAY') { + print OUTPUT $var.": ". (join ", ", @$val)."\n"; + } else { + print OUTPUT "$var: $val\n"; + } + } + print OUTPUT "\n"; + } + close OUTPUT; +} + +# Get the available libraries that autoconf generated. +sub get_autoconf_deps() { + open DEPS, $AutoconfDepsFile or + die "Failed to open $AutoconfDepsFile. Aborting: $!\n"; + + my @deps_list = (); + foreach (@deps_list){ + chomp; + my ($lib, $avail_val) = split(/=/); + my ($avail, $avail_old) = split(/:/, $avail_val); + my $disabled = 0; + if ($avail == -1) { + $disabled = 1; + $avail = 0; + } + $ModInfo{$lib} = { + Type=>'lib', Avail=>$avail, Disabled => $disabled + }; + if (defined $avail_old) { + $ModInfo{$lib}{AvailOld} = $avail_old; + } + # FIXME: + if (($avail ne "0") && ($avail ne "1")) { + warning "Library $lib has invalid availability ". + "value <$avail> (check $AutoconfDepsFile).\n"; + } + } + close DEPS; +} + +# Read our specific config file. +# +# Its format: +# +# keyword values +# +# values are always a spaces-separated list. +sub read_conf() { + open CONF,$ConfFile or return; + + while () { + # remove comments and empty lines: + chomp; + s/#.*$//; + next if /^\s*$/; + + my ($keyword, @value) = split; + + if ($keyword eq 'exclude') { + push @ExcludedModules, @value; + } elsif ($keyword eq 'include') { + push @IncludedModules, @value; + } elsif ($keyword eq 'require') { + push @RequiredModules, @value; + } elsif ($keyword eq 'random') { + $RandomKnockoutFactor = $value[0] / 100; + } else { + warning "unknown keyword $keyword in line $. of $ConfFile."; + } + } +} + +# generate menuselect.makedeps. +# In this file menuselect writes dependecies of each module. CFLAGS will +# then automatically include for each module the _INCLUDE and LDFLAGS +# will include the _LIBS from all the depedencies of the module. +sub gen_makedeps() { + open MAKEDEPSS, ">$MakedepsFile" or + die "Failed to open deps file $MakedepsFile for writing. Aborting: $!\n"; + + for my $mod_name (sort keys %ModInfo) { + next unless ($ModInfo{$mod_name}{Type} eq 'module'); + + my $mod = $ModInfo{$mod_name}; + my @deps = (); + + # if we have Depend or Use, put their values into + # @deps . If we have none, move on. + push @deps, @{$mod->{Depend}} if (exists $mod->{Depend}); + push @deps, @{$mod->{Use}} if (exists $mod->{Use}); + next unless @deps; + + # TODO: don't print dependencies that are not external libs. + # Not done yet until I figure out if this is safe. + my $dep = join(' ', @deps); + print MAKEDEPSS "MENUSELECT_DEPENDS_".$mod->{Module}."=$dep\n"; + } + + close MAKEDEPSS; +} + +# Set modules from patterns specified by 'exclude' in the configuration file +# to exclude modules from building (mark them as unavailable). +sub apply_excluded_patterns() { + foreach my $pattern (@ExcludedModules) { + my @excluded = grep {/^$pattern$/i} (keys %ModInfo); + foreach (@excluded) { + $ModInfo{$_}{Avail} = 0; + } + } +} + +# Set modules from patterns specified by 'include' in the configuration +# file to exclude from building (mark them as available). +sub apply_included_patterns() { + foreach my $pattern (@IncludedModules) { + my @included = grep {/^$pattern$/i} (keys %ModInfo); + foreach (@included) { + $ModInfo{$_}{Avail} = 1; + } + } +} + +# If user set the "random" config to anything > 0, drop some random +# modules. May help expose wrong dependencies. +sub apply_random_drop() { + return if ($RandomKnockoutFactor <= 0); + + open MODS_LIST, ">$RandomeModsFile" or + die "Failed to open modules list file $RandomeModsFile for writing. Aborting: $!\n"; + for my $mod (keys %ModInfo) { + next unless ($ModInfo{$mod}{Type} eq 'module'); + next unless (rand() < $RandomKnockoutFactor); + $ModInfo{$mod}{Avail} = 0; + $ModInfo{$mod}{RandomKill} = 1; + print MODS_LIST $ModInfo{$mod}{Module}."\n"; + } + + close MODS_LIST; + + +} + +sub check_required_patterns() { + my @failed = (); + foreach my $pattern (@RequiredModules) { + my @required = grep {/^$pattern$/i} (keys %ModInfo); + foreach my $mod (@required) { + if ((! exists $ModInfo{$mod}{Checked}) || + (! $ModInfo{$mod}{Checked}) ) + { + push @failed, $mod; + } + } + } + return unless (@failed); + + my $failed_str = join ' ',@failed; + die("Missing dependencies for the following modules: $failed_str\n"); +} + +# Disable building for modules that were marked in the embedded module +# information as disabled for building by default. +sub apply_default_enabled() { + foreach my $mod (keys %ModInfo) { + if ((exists $ModInfo{$mod}{Defaultenabled}) && + $ModInfo{$mod}{Defaultenabled}[0] eq 'no') + { + $ModInfo{$mod}{Avail} = 0; + } + } +} + +# recursively check dependency for a module. +# +# We run a scan for modules. Modules marked as 'Checked' are ones we +# have already fully verified to have proper dependencies. +# +# We can only use a module or library marked as Avail => 1 (library +# available or module not excluded). +sub check_module($); +sub check_module($) { + my $mod = shift; + + # we checked it: + if (exists $ModInfo{$mod}{Checked}) { + return $ModInfo{$mod}{Checked}; + } + # A library has no dependencies of its own. + if ($ModInfo{$mod}{Type} eq 'lib') { + return ($ModInfo{$mod}{Avail} || 0); + } + # An excluded module. + if ($ModInfo{$mod}{Avail} == 0) { + return 0; + } + # XML inputs have a reversed logic: no 'defaultenabled' means 'no' + # And we need to actually print enabled ones, rather than disabled + # ones. + if ($ModInfo{$mod}{Type} eq 'XML') { + my $res = ((not exists $ModInfo{$mod}{Defaultenabled}) || + ($ModInfo{$mod}{Defaultenabled}[0] ne 'yes') ); + $ModInfo{$mod}{Checked} = $res; + return $res; + } + # no dependencies to check: + if (! exists $ModInfo{$mod}{Depend}) { + $ModInfo{$mod}{Checked} = 1; + return 1; + } + + my $deps_checked = 1; # may be reset below on failures: + + if (exists $ModInfo{$mod}{Tested}) { + # this probably means a circular dependency of some sort. + warning "Got to module $mod that is already tested."; + } + $ModInfo{$mod}{Tested} = 1; + + foreach my $dep_mod (@{$ModInfo{$mod}{Depend}} ) { + if (!exists ${ModInfo}{$dep_mod}) { + # TODO: die here? This should never happen. + warning "module $mod depends on $dep_mod that does not exist."; + next; + } + $deps_checked &= check_module($dep_mod); + last if(!$deps_checked) # no point testing further if we failed. + } + + $ModInfo{$mod}{Checked} = $deps_checked; + return $deps_checked; +} + +# The main dependency resolver function. +sub resolve_deps() { + apply_default_enabled(); + apply_excluded_patterns(); + apply_included_patterns(); + + foreach my $mod (keys %ModInfo) { + check_module($mod); + } +} + +# generate menuselect.makeopts. Please let me know if some parts are +# still missing. +sub gen_makeopts() { + open MAKEDEPS, ">$MakeoptsFile" or + die "Failed to open opts file $MakeoptsFile for writing. Aborting: $!\n"; + + my %Subdirs; + foreach my $mod (sort keys %ModInfo) { + next unless ($ModInfo{$mod}{Type} =~ /^(module|XML)$/); + next if ($ModInfo{$mod}{Dir} eq 'CFLAGS'); + next if ($ModInfo{$mod}{Checked}); + my $dir = $ModInfo{$mod}{Dir}; + if (! exists $Subdirs{$dir}) { + $Subdirs{$dir} = []; + } + push @{$Subdirs{$dir}},( $ModInfo{$mod}{Module} ); + } + foreach my $dir (sort keys %Subdirs) { + my $deps = join(' ', @{$Subdirs{$dir}}); + print MAKEDEPS "MENUSELECT_$dir=$deps\n"; + } + + close MAKEDEPS; +} + +# Main function for --check-deps +sub check_dependencies() { + read_conf(); + + extract_subdirs(@Subdirs); + + parse_menuselect_xml_file('build_tools/cflags.xml'); + parse_menuselect_xml_file('sounds/sounds.xml'); + + apply_random_drop(); + + get_autoconf_deps(); + + #dump_deps('build_tools/dump_deps_before_resolve'); + resolve_deps(); + + # Handy debugging: + dump_deps($DumpFile); + + check_required_patterns(); + + gen_makedeps(); + + gen_makeopts(); +} + +# +# The main program start here +# + +sub read_dump() { + my %items = (); + my $saved_rs = $/; + $/ = "\n\n"; + open DUMP_FILE,$DumpFile or die "Can't read from dump file $DumpFile\n"; + while () { + my %item = (); + my @item_lines = split /\n\r?/; + foreach (@item_lines) { + my ($var, $value) = split /: /, $_, 2; + $item{$var} = $value; + } + $items{$item{Key}} = \%item; + } + close DUMP_FILE; + $/ = $saved_rs; + return \%items; +} + +sub print_module_status { + my $items = read_dump(); + my %items_matched = (); + + foreach my $pattern (@_) { + foreach (keys %$items) { + if (/$pattern/i) { + $items_matched{$_} = 1; + } + } + } + + my @items_list = sort keys %items_matched; + + foreach my $item_name (@items_list) { + my $item = $items->{$item_name}; + printf "%s %-8s %-30s\n", + (($item->{Avail})? 'Y':'n'), + $item->{Type}, + $item->{Key}; + } +} + +sub usage() { + print "$0: menuselect reimplementation\n"; + print "\n"; + print "Usage:\n"; + print "$0 # menuselect processing\n"; + print "$0 -m|--modinfo|--moduls-info PATTERN # Status of modules\n"; + print "\n"; + print "PATTERN is a partial perl regex. Use '-m .' to list all.\n"; +} + +my @module_status = (); + +my $action = ''; + +my $rc = GetOptions( + 'modinfo|modules-info|m=s' => \@module_status, + 'check-deps|c:s' => sub { $action = 'check_dependencies'}, + 'help|h' => sub { usage(); exit 0 }, +); +if (!$rc) { + usage(); + exit $rc; +} + +if (@module_status) { + $action = 'module_status'; +} + +if ($action eq 'module_status') { + exit print_module_status(@module_status); +} elsif ( $action eq 'check_dependencies') { + check_dependencies(); +} else { + usage(); exit(1); +} -- cgit v1.2.3