diff options
Diffstat (limited to 'ACE/MPC/modules/Creator.pm')
-rw-r--r-- | ACE/MPC/modules/Creator.pm | 1310 |
1 files changed, 1310 insertions, 0 deletions
diff --git a/ACE/MPC/modules/Creator.pm b/ACE/MPC/modules/Creator.pm new file mode 100644 index 00000000000..a3566b5d1d2 --- /dev/null +++ b/ACE/MPC/modules/Creator.pm @@ -0,0 +1,1310 @@ +package Creator; + +# ************************************************************ +# Description : Base class for workspace and project creators +# Author : Chad Elliott +# Create Date : 5/13/2002 +# ************************************************************ + +# ************************************************************ +# Pragmas +# ************************************************************ + +use strict; +use FileHandle; +use File::Compare; + +use Parser; + +use vars qw(@ISA); +@ISA = qw(Parser); + +# ************************************************************ +# Data Section +# ************************************************************ + +## Constants for use throughout the project +use constant cplusplus => 'cplusplus'; +use constant csharp => 'csharp'; +use constant java => 'java'; +use constant vb => 'vb'; +use constant website => 'website'; + +## The default language for MPC +my $deflang = 'cplusplus'; + +## A map of all of the allowed languages. The 'website' value +## is not here because it isn't really a language. It is used +## as a language internally by some project types though. +my %languages = (cplusplus => 1, + csharp => 1, + java => 1, + vb => 1, + ); + +my $assign_key = 'assign'; +my $gassign_key = 'global_assign'; +my %non_convert = ('prebuild' => 1, + 'postbuild' => 1, + 'postclean' => 1, + ); +my @statekeys = ('global', 'include', 'template', 'ti', + 'dynamic', 'static', 'relative', 'addtemp', + 'addproj', 'progress', 'toplevel', 'baseprojs', + 'features', 'feature_file', 'hierarchy', + 'name_modifier', 'apply_project', 'into', 'use_env', + 'expand_vars', 'language', + ); + +my %all_written; +my $onVMS = DirectoryManager::onVMS(); + +# ************************************************************ +# Subroutine Section +# ************************************************************ + +sub new { + my($class, $global, $inc, $template, $ti, $dynamic, $static, $relative, $addtemp, $addproj, $progress, $toplevel, $baseprojs, $feature, $features, $hierarchy, $nmodifier, $applypj, $into, $language, $use_env, $expandvars, $type) = @_; + my $self = Parser::new($class, $inc); + + $self->{'relative'} = $relative; + $self->{'template'} = $template; + $self->{'ti'} = $ti; + $self->{'global'} = $global; + $self->{'grammar_type'} = $type; + $self->{'type_check'} = $type . '_defined'; + $self->{'global_read'} = 0; + $self->{'current_input'} = ''; + $self->{'progress'} = $progress; + $self->{'addtemp'} = $addtemp; + $self->{'addproj'} = $addproj; + $self->{'toplevel'} = $toplevel; + $self->{'files_written'} = {}; + $self->{'real_fwritten'} = []; + $self->{'reading_global'} = 0; + $self->{$gassign_key} = {}; + $self->{$assign_key} = {}; + $self->{'baseprojs'} = $baseprojs; + $self->{'dynamic'} = $dynamic; + $self->{'static'} = $static; + $self->{'feature_file'} = $feature; + $self->{'features'} = $features; + $self->{'hierarchy'} = $hierarchy; + $self->{'name_modifier'} = $nmodifier; + $self->{'apply_project'} = $applypj; + $self->{'into'} = $into; + $self->{'language'} = defined $language ? $language : $deflang; + $self->{'use_env'} = $use_env; + $self->{'expand_vars'} = $expandvars; + $self->{'convert_slashes'} = $self->convert_slashes(); + $self->{'requires_forward_slashes'} = $self->requires_forward_slashes(); + $self->{'case_tolerant'} = $self->case_insensitive(); + + return $self; +} + + +sub preprocess_line { + my($self, $fh, $line) = @_; + + $line = $self->strip_line($line); + while ($line =~ /\\$/) { + $line =~ s/\s*\\$/ /; + my $next = $fh->getline(); + $line .= $self->strip_line($next) if (defined $next); + } + return $line; +} + + +sub generate_default_input { + my $self = shift; + my($status, + $error) = $self->parse_line(undef, "$self->{'grammar_type'} {"); + + ## Parse the finish line if there was no error + ($status, $error) = $self->parse_line(undef, '}') if ($status); + + ## Display the error if there was one + $self->error($error) if (!$status); + + return $status; +} + + +sub parse_file { + my($self, $input) = @_; + + ## Save the last line number so we can put it back later + my $oline = $self->get_line_number(); + + ## Read the input file + my($status, $errorString) = $self->read_file($input); + + if (!$status) { + $self->error($errorString, + "$input: line " . $self->get_line_number() . ':'); + } + elsif ($self->{$self->{'type_check'}}) { + ## If we are at the end of the file and the type we are looking at + ## is still defined, then we have an error + $self->error("Did not " . + "find the end of the $self->{'grammar_type'}", + "$input: line " . $self->get_line_number() . ':'); + $status = 0; + } + $self->set_line_number($oline); + + return $status; +} + + +sub generate { + my($self, $input) = @_; + my $status = 1; + + ## Reset the files_written hash array between processing each file + $self->{'files_written'} = {}; + $self->{'real_fwritten'} = []; + + ## Allow subclasses to reset values before + ## each call to generate(). + $self->reset_values(); + + ## Read the global configuration file + if (!$self->{'global_read'}) { + $status = $self->read_global_configuration(); + $self->{'global_read'} = 1; + } + + if ($status) { + $self->{'current_input'} = $input; + + ## An empty input file name says that we + ## should generate a default input file and use that + if ($input eq '') { + $status = $self->generate_default_input(); + } + else { + $status = $self->parse_file($input); + } + } + + return $status; +} + + +sub parse_known { + my($self, $line) = @_; + my $status = 1; + my $errorString; + my $type = $self->{'grammar_type'}; + my @values; + + ## + ## Each regexp that looks for the '{' looks for it at the + ## end of the line. It is purposely this way to decrease + ## the amount of extra lines in each file. This + ## allows for the most compact file as human readably + ## possible. + ## + if ($line eq '') { + } + elsif ($line =~ /^$type\s*(\([^\)]+\))?\s*(:.*)?\s*{$/) { + my $name = $1; + my $parents = $2; + if ($self->{$self->{'type_check'}}) { + $errorString = "Did not find the end of the $type"; + $status = 0; + } + else { + if (defined $parents) { + $parents =~ s/^:\s*//; + $parents =~ s/\s+$//; + my @parents = split(/\s*,\s*/, $parents); + if (!defined $parents[0]) { + ## The : was used, but no parents followed. This + ## is an error. + $errorString = 'No parents listed'; + $status = 0; + } + $parents = \@parents; + } + push(@values, $type, $name, $parents); + } + } + elsif ($line =~ /^}$/) { + if ($self->{$self->{'type_check'}}) { + push(@values, $type, $line); + } + else { + $errorString = "Did not find the beginning of the $type"; + $status = 0; + } + } + elsif ($line =~ /^(feature)\s*\(([^\)]+)\)\s*(:.*)?\s*{$/) { + my $type = $1; + my $name = $2; + my $parents = $3; + my @names = split(/\s*,\s*/, $name); + + if (defined $parents) { + $parents =~ s/^:\s*//; + $parents =~ s/\s+$//; + my @parents = split(/\s*,\s*/, $parents); + if (!defined $parents[0]) { + ## The : was used, but no parents followed. This + ## is an error. + $errorString = 'No parents listed'; + $status = 0; + } + $parents = \@parents; + } + push(@values, $type, \@names, $parents); + } + elsif (!$self->{$self->{'type_check'}}) { + $errorString = "No $type was defined"; + $status = 0; + } + elsif ($self->parse_assignment($line, \@values)) { + ## If this returns true, then we've found an assignment + } + elsif ($line =~ /^(\w+)\s*(\([^\)]+\))?\s*{$/) { + my $comp = lc($1); + my $name = $2; + + if (defined $name) { + $name =~ s/^\(\s*//; + $name =~ s/\s*\)$//; + } + else { + $name = $self->get_default_component_name(); + } + push(@values, 'component', $comp, $name); + } + else { + $errorString = "Unrecognized line: $line"; + $status = -1; + } + + return $status, $errorString, @values; +} + + +sub parse_scope { + my($self, $fh, $name, $type, $validNames, $flags, $elseflags) = @_; + my $status = 0; + my $errorString = "Unable to process $name"; + + ## Make sure $flags has a hash map reference + $flags = {} if (!defined $flags); + + while(<$fh>) { + my $line = $self->preprocess_line($fh, $_); + + if ($line eq '') { + } + elsif ($line =~ /^}$/) { + ($status, $errorString) = $self->handle_scoped_end($type, $flags); + last; + } + elsif ($line =~ /^}\s*else\s*{$/) { + if (defined $elseflags) { + ## From here on out anything after this goes into the $elseflags + $flags = $elseflags; + $elseflags = undef; + + ## We need to adjust the type also. If there was a type + ## then the first part of the clause was used. If there was + ## no type, then the first part was ignored and the second + ## part will be used. + if (defined $type) { + $type = undef; + } + else { + $type = $self->get_default_component_name(); + } + } + else { + $status = 0; + $errorString = 'An else is not allowed in this context'; + last; + } + } + else { + my @values; + if (defined $validNames && $self->parse_assignment($line, \@values)) { + if (defined $$validNames{$values[1]}) { + ## If $type is not defined, we don't even need to bother with + ## processing the assignment as we will be throwing the value + ## away anyway. + if (defined $type) { + if ($values[0] == 0) { + $self->process_assignment($values[1], $values[2], $flags); + } + elsif ($values[0] == 1) { + $self->process_assignment_add($values[1], $values[2], $flags); + } + elsif ($values[0] == -1) { + $self->process_assignment_sub($values[1], $values[2], $flags); + } + } + } + else { + ($status, + $errorString) = $self->handle_unknown_assignment($type, + @values); + last if (!$status); + } + } + else { + ($status, $errorString) = $self->handle_scoped_unknown($fh, + $type, + $flags, + $line); + last if (!$status); + } + } + } + return $status, $errorString; +} + + +sub base_directory { + my $self = shift; + return $self->mpc_basename($self->getcwd()); +} + + +sub generate_default_file_list { + my($self, $dir, $exclude, $fileexc, $recurse) = @_; + my $dh = new FileHandle(); + my @files; + + if (opendir($dh, $dir)) { + my $prefix = ($dir ne '.' ? "$dir/" : ''); + my $have_exc = (defined $$exclude[0]); + my $skip = 0; + foreach my $file (grep(!/^\.\.?$/, + ($onVMS ? map {$_ =~ s/\.dir$//; $_} readdir($dh) : + readdir($dh)))) { + ## Prefix each file name with the directory only if it's not '.' + my $full = $prefix . $file; + + if ($have_exc) { + foreach my $exc (@$exclude) { + if ($full eq $exc) { + $skip = 1; + last; + } + } + } + + if ($skip) { + $skip = 0; + $$fileexc = 1 if (defined $fileexc); + } + else { + if ($recurse && -d $full) { + push(@files, + $self->generate_default_file_list($full, $exclude, + $fileexc, $recurse)); + } + else { + # Strip out ^ symbols + $full =~ s/\^//g if ($onVMS); + + push(@files, $full); + } + } + } + + if ($self->sort_files()) { + @files = sort { $self->file_sorter($a, $b) } @files; + } + + closedir($dh); + } + return @files; +} + + +sub transform_file_name { + my($self, $name) = @_; + + $name =~ s/[\s\-]/_/g; + return $name; +} + + +sub file_written { + my($self, $file) = @_; + return (defined $all_written{$self->getcwd() . '/' . $file}); +} + + +sub add_file_written { + my($self, $file) = @_; + my $key = lc($file); + + if (defined $self->{'files_written'}->{$key}) { + $self->warning("$self->{'grammar_type'} $file " . + ($self->{'case_tolerant'} ? + "has been overwritten." : + "of differing case has been processed.")); + } + else { + $self->{'files_written'}->{$key} = $file; + push(@{$self->{'real_fwritten'}}, $file); + } + + $all_written{$self->getcwd() . '/' . $file} = 1; +} + + +sub extension_recursive_input_list { + my($self, $dir, $exclude, $ext) = @_; + my $fh = new FileHandle(); + my @files; + + if (opendir($fh, $dir)) { + my $prefix = ($dir ne '.' ? "$dir/" : ''); + my $skip = 0; + foreach my $file (grep(!/^\.\.?$/, + ($onVMS ? map {$_ =~ s/\.dir$//; $_} readdir($fh) : + readdir($fh)))) { + my $full = $prefix . $file; + + ## Check for command line exclusions + if (defined $$exclude[0]) { + foreach my $exc (@$exclude) { + if ($full eq $exc) { + $skip = 1; + last; + } + } + } + + ## If we are not skipping this directory or file, then check it out + if ($skip) { + $skip = 0; + } + else { + if (-d $full) { + push(@files, $self->extension_recursive_input_list($full, + $exclude, + $ext)); + } + elsif ($full =~ /$ext$/) { + push(@files, $full); + } + } + } + closedir($fh); + } + + return @files; +} + +sub recursive_directory_list { + my($self, $dir, $exclude) = @_; + my $directories = ''; + my $fh = new FileHandle(); + + if (opendir($fh, $dir)) { + my $prefix = ($dir ne '.' ? "$dir/" : ''); + my $skip = 0; + if (defined $$exclude[0]) { + foreach my $exc (@$exclude) { + if ($dir eq $exc) { + $skip = 1; + last; + } + } + } + if ($skip) { + $skip = 0; + } + else { + $directories .= ' ' . $dir; + } + + foreach my $file (grep(!/^\.\.?$/, + ($onVMS ? map {$_ =~ s/\.dir$//; $_} readdir($fh) : + readdir($fh)))) { + my $full = $prefix . $file; + + if ($file eq '.svn' || $file eq 'CVS') { + $skip = 1; + } + else { + ## Check for command line exclusions + if (defined $$exclude[0]) { + foreach my $exc (@$exclude) { + if ($full eq $exc) { + $skip = 1; + last; + } + } + } + } + + ## If we are not skipping this directory or file, then check it out + if ($skip) { + $skip = 0; + } + else { + if (-d $full) { + $directories .= $self->recursive_directory_list($full, $exclude); + } + } + } + closedir($fh); + } + + return $directories; +} + + +sub modify_assignment_value { + my($self, $name, $value) = @_; + + if ($self->{'convert_slashes'} && + index($name, 'flags') == -1 && !defined $non_convert{$name}) { + $value =~ s/\//\\/g; + } + + return $value; +} + + +sub get_assignment_hash { + ## NOTE: If anything in this block changes, then you must make the + ## same change in process_assignment. + my $self = shift; + return $self->{$self->{'reading_global'} ? $gassign_key : $assign_key}; +} + + +sub process_assignment { + my($self, $name, $value, $assign) = @_; + + ## If no hash table was passed in + if (!defined $assign) { + ## NOTE: If anything in this block changes, then you must make the + ## same change in get_assignment_hash. + $assign = $self->{$self->{'reading_global'} ? + $gassign_key : $assign_key}; + } + + if (defined $value) { + $value =~ s/^\s+//; + $value =~ s/\s+$//; + + ## Modify the assignment value before saving it + $$assign{$name} = $self->modify_assignment_value($name, $value); + } + else { + $$assign{$name} = undef; + } +} + + +sub addition_core { + my($self, $name, $value, $nval, $assign) = @_; + + if (defined $nval) { + if ($self->preserve_assignment_order($name)) { + $nval .= " $value"; + } + else { + $nval = "$value $nval"; + } + } + else { + $nval = $value; + } + $self->process_assignment($name, $nval, $assign, 1); +} + + +sub process_assignment_add { + my($self, $name, $value, $assign) = @_; + my $nval = $self->get_assignment_for_modification($name, $assign); + + ## Remove all duplicate parts from the value to be added. + ## Whether anything gets removed or not is up to the implementation + ## of the sub classes. + $value = $self->remove_duplicate_addition($name, $value, $nval); + + ## If there is anything to add, then do so + $self->addition_core($name, $value, $nval, $assign) if ($value ne ''); +} + + +sub subtraction_core { + my($self, $name, $value, $nval, $assign) = @_; + + if (defined $nval) { + my $last = 1; + my $found; + + ## Escape any regular expression special characters + $value = $self->escape_regex_special($value); + + ## If necessary, split the value into an array + my $elements = ($value =~ /\s/ ? $self->create_array($value) : [$value]); + for(my $i = 0; $i <= $last; $i++) { + if ($i == $last) { + ## If we did not find the string to subtract in the original + ## value, try again after expanding template variables for + ## subtraction. + $nval = $self->get_assignment_for_modification($name, $assign, 1); + } + for(my $j = 0; $j <= $last; $j++) { + ## Try to remove each individual element and then set the new + ## value if any of the elements were found in the original value + foreach my $elem (@$elements) { + ## First try with quotes, then try again without them + my $re = ($j == 0 ? '"' . $elem . '"' : $elem); + + if ($nval =~ s/\s+$re\s+/ / || $nval =~ s/\s+$re$// || + $nval =~ s/^$re\s+// || $nval =~ s/^$re$//) { + $found = 1; + } + } + if ($found) { + $self->process_assignment($name, $nval, $assign, -1); + last; + } + } + last if ($found); + } + } +} + + +sub process_assignment_sub { + my($self, $name, $value, $assign) = @_; + my $nval = $self->get_assignment_for_modification($name, $assign); + + ## Remove double quotes if there are any + $value =~ s/^\"(.*)\"$/$1/; + + $self->subtraction_core($name, $value, $nval, $assign); +} + + +sub fill_type_name { + my($self, $names, $def) = @_; + my $array = ($names =~ /\s/ ? $self->create_array($names) : [$names]); + + $names = ''; + foreach my $name (@$array) { + if ($name =~ /\*/) { + my $pre = $def . '_'; + my $mid = '_' . $def . '_'; + my $post = '_' . $def; + + ## Replace the beginning and end first then the middle + $name =~ s/^\*/$pre/; + $name =~ s/\*$/$post/; + $name =~ s/\*/$mid/g; + + ## Remove any trailing underscore or any underscore that is followed + ## by a space. This value could be a space separated list. + $name =~ s/_$//; + $name =~ s/_\s/ /g; + $name =~ s/\s_/ /g; + + ## If any one word is capitalized then capitalize each word + if ($name =~ /[A-Z][0-9a-z_]+/) { + ## Do the first word + if ($name =~ /^([a-z])([^_]+)/) { + my $first = uc($1); + my $rest = $2; + $name =~ s/^[a-z][^_]+/$first$rest/; + } + ## Do subsequent words + while($name =~ /(_[a-z])([^_]+)/) { + my $first = uc($1); + my $rest = $2; + $name =~ s/_[a-z][^_]+/$first$rest/; + } + } + } + + $names .= $name . ' '; + } + $names =~ s/\s+$//; + + return $names; +} + + +sub clone { + my($self, $obj) = @_; + + ## Check for various types of data. Those that are not found to be + ## types that we need to deep copy are just assigned to new values. + ## All others are copied by recursively calling this method. + if (UNIVERSAL::isa($obj, 'HASH')) { + my $new = {}; + foreach my $key (keys %$obj) { + $$new{$key} = $self->clone($$obj{$key}); + } + return $new; + } + elsif (UNIVERSAL::isa($obj, 'ARRAY')) { + my $new = []; + foreach my $o (@$obj) { + push(@$new, $self->clone($o)); + } + return $new; + } + + return $obj; +} + + +sub save_state { + my($self, $selected) = @_; + my %state; + + ## Make a deep copy of each state value. That way our array + ## references and hash references do not get accidentally modified. + foreach my $skey (defined $selected ? $selected : @statekeys) { + if (defined $self->{$skey}) { + ## It is necessary to clone each value so that nested complex data + ## types do not get unknowingly modified. + $state{$skey} = $self->clone($self->{$skey}); + } + } + + return %state; +} + + +sub restore_state { + my($self, $state, $selected) = @_; + + ## Make a deep copy of each state value. That way our array + ## references and hash references do not get accidentally modified. + ## It's not necessary to do a recursive deep copy (i.e., use the + ## clone() method) because the value coming in will now be owned by + ## this object and will not be modified unknowingly. + foreach my $skey (defined $selected ? $selected : @statekeys) { + my $old = $self->{$skey}; + if (defined $state->{$skey} && + UNIVERSAL::isa($state->{$skey}, 'ARRAY')) { + my @arr = @{$state->{$skey}}; + $self->{$skey} = \@arr; + } + elsif (defined $state->{$skey} && + UNIVERSAL::isa($state->{$skey}, 'HASH')) { + my %hash = %{$state->{$skey}}; + $self->{$skey} = \%hash; + } + else { + $self->{$skey} = $state->{$skey}; + } + $self->restore_state_helper($skey, $old, $self->{$skey}); + } +} + + +sub get_global_cfg { + return $_[0]->{'global'}; +} + + +sub get_template_override { + return $_[0]->{'template'}; +} + + +sub get_ti_override { + return $_[0]->{'ti'}; +} + + +sub get_relative { + return $_[0]->{'relative'}; +} + + +sub get_progress_callback { + return $_[0]->{'progress'}; +} + + +sub get_addtemp { + return $_[0]->{'addtemp'}; +} + + +sub get_addproj { + return $_[0]->{'addproj'}; +} + + +sub get_toplevel { + return $_[0]->{'toplevel'}; +} + + +sub get_into { + return $_[0]->{'into'}; +} + + +sub get_use_env { + return $_[0]->{'use_env'}; +} + + +sub get_expand_vars { + return $_[0]->{'expand_vars'}; +} + + +sub get_files_written { + return $_[0]->{'real_fwritten'}; +} + + +sub get_assignment { + my $self = shift; + my $name = $self->resolve_alias(shift); + my $assign = shift; + + ## If no hash table was passed in + if (!defined $assign) { + $assign = $self->{$self->{'reading_global'} ? + $gassign_key : $assign_key}; + } + + return $$assign{$name}; +} + + +sub get_assignment_for_modification { + my($self, $name, $assign, $subtraction) = @_; + return $self->get_assignment($name, $assign); +} + + +sub get_baseprojs { + return $_[0]->{'baseprojs'}; +} + + +sub get_dynamic { + return $_[0]->{'dynamic'}; +} + + +sub get_static { + return $_[0]->{'static'}; +} + + +sub get_default_component_name { + #my $self = shift; + return 'default'; +} + + +sub get_features { + return $_[0]->{'features'}; +} + + +sub get_hierarchy { + return $_[0]->{'hierarchy'}; +} + + +sub get_name_modifier { + return $_[0]->{'name_modifier'}; +} + + +sub get_apply_project { + return $_[0]->{'apply_project'}; +} + + +sub get_language { + return $_[0]->{'language'}; +} + + +sub get_outdir { + my $self = shift; + if (defined $self->{'into'}) { + my $outdir = $self->getcwd(); + my $re = $self->escape_regex_special($self->getstartdir()); + + $outdir =~ s/^$re//; + return $self->{'into'} . $outdir; + } + else { + return '.'; + } +} + + +sub expand_variables { + my($self, $value, $rel, $expand_template, $scope, $expand, $warn) = @_; + my $cwd = $self->getcwd(); + my $start = 0; + my $forward_slashes = $self->{'convert_slashes'} || + $self->{'requires_forward_slashes'}; + + ## Fix up the value for Windows switch the \\'s to / + $cwd =~ s/\\/\//g if ($forward_slashes); + + while(substr($value, $start) =~ /(\$\(([^)]+)\))/) { + my $whole = $1; + my $name = $2; + if (defined $$rel{$name}) { + my $val = $$rel{$name}; + if ($expand) { + $val =~ s/\//\\/g if ($forward_slashes); + substr($value, $start) =~ s/\$\([^)]+\)/$val/; + $whole = $val; + } + else { + ## Fix up the value for Windows switch the \\'s to / + $val =~ s/\\/\//g if ($forward_slashes); + + my $icwd = ($self->{'case_tolerant'} ? lc($cwd) : $cwd); + my $ival = ($self->{'case_tolerant'} ? lc($val) : $val); + my $iclen = length($icwd); + my $ivlen = length($ival); + + ## If the relative value contains the current working + ## directory plus additional subdirectories, we must pull + ## off the additional directories into a temporary where + ## it can be put back after the relative replacement is done. + my $append; + if (index($ival, $icwd) == 0 && $iclen != $ivlen && + substr($ival, $iclen, 1) eq '/') { + my $diff = $ivlen - $iclen; + $append = substr($ival, $iclen); + substr($ival, $iclen, $diff) = ''; + $ivlen -= $diff; + } + + if (index($icwd, $ival) == 0 && + ($iclen == $ivlen || substr($icwd, $ivlen, 1) eq '/')) { + my $current = $icwd; + substr($current, 0, $ivlen) = ''; + + my $dircount = ($current =~ tr/\///); + if ($dircount == 0) { + $ival = '.'; + } + else { + $ival = '../' x $dircount; + $ival =~ s/\/$//; + } + $ival .= $append if (defined $append); + + ## We have to remove the leading ./ if there is one. + ## Otherwise, if this value is used as an exclude value it will + ## not match up correctly. + $ival =~ s!^\./!!; + + ## Convert the slashes if necessary + $ival =~ s/\//\\/g if ($self->{'convert_slashes'}); + substr($value, $start) =~ s/\$\([^)]+\)/$ival/; + $whole = $ival; + } + elsif ($self->convert_all_variables() && $warn) { + ## The user did not choose to expand $() variables directly, + ## but we could not convert it into a relative path. So, + ## instead of leaving it we will expand it. But, we will only + ## get into this section if this is the secondary attempt to + ## replace the variable (indicated by the $warn boolean). + $val =~ s/\//\\/g if ($self->{'convert_slashes'}); + substr($value, $start) =~ s/\$\([^)]+\)/$val/; + $whole = $val; + } + else { + my $loc = index(substr($value, $start), $whole); + $start += $loc if ($loc > 0); + } + } + } + elsif ($expand_template || + $self->expand_variables_from_template_values()) { + my $ti = $self->get_template_input(); + my $val = (defined $ti ? $ti->get_value($name) : undef); + my $sname = (defined $scope ? $scope . "::$name" : undef); + my $arr = $self->adjust_value([$sname, $name], + (defined $val ? $val : [])); + if (UNIVERSAL::isa($arr, 'HASH')) { + $self->warning("$name conflicts with a template variable scope"); + } + elsif (UNIVERSAL::isa($arr, 'ARRAY') && defined $$arr[0]) { + $val = $self->modify_assignment_value(lc($name), "@$arr"); + substr($value, $start) =~ s/\$\([^)]+\)/$val/; + + ## We have replaced the template value, but that template + ## value may contain a $() construct that may need to get + ## replaced too. However, if the name of the template variable + ## is the same as the original $() variable name, we need to + ## leave it alone to avoid looping infinitely. + $whole = '' if ($whole ne $val); + } + else { + $self->warning("Unable to expand $name.") if ($expand && $warn); + my $loc = index(substr($value, $start), $whole); + $start += $loc if ($loc > 0); + } + } + elsif ($self->convert_all_variables() && $warn) { + ## We could not find a value to correspond to the variable name. + ## Instead of leaving it we will expand it. But, we will only + ## get into this section if this is the secondary attempt to + ## replace the variable (indicated by the $warn boolean). + substr($value, $start) =~ s/\$\([^)]+\)//; + $whole = ''; + } + else { + my $loc = index(substr($value, $start), $whole); + $start += $loc if ($loc > 0); + } + $start += length($whole); + } + + $value =~ s/\\/\//g if ($self->{'requires_forward_slashes'}); + + return $value; +} + + +sub replace_env_vars { + my($self, $lref) = @_; + my $one_empty = undef; + + ## Loop through the string until we find no more environment variables. + while($$lref =~ /\$(\w+)/) { + my $name = $1; + my $val = ''; + + ## PWD is a special variable. It isn't set on Windows, but in MPC we + ## must guarantee that it is always there. + if ($name eq 'PWD') { + $val = $self->getcwd(); + } + elsif (defined $ENV{$name}) { + $val = $ENV{$name}; + } + else { + ## Keep track of an environment variable not being set. + $one_empty = 1; + } + $$lref =~ s/\$\w+/$val/; + } + return $one_empty; +} + + +sub relative { + my($self, $value, $expand_template, $scope) = @_; + + if (defined $value) { + if (UNIVERSAL::isa($value, 'ARRAY')) { + my @built; + foreach my $val (@$value) { + my $rel = $self->relative($val, $expand_template, $scope); + if (UNIVERSAL::isa($rel, 'ARRAY')) { + push(@built, @$rel); + } + else { + push(@built, $rel); + } + } + return \@built; + } + elsif (index($value, '$') >= 0) { + ## A form of this code lives in + ## ProjectCreator::create_recursive_settings. If you are changing + ## something in this area, please look at the method in + ## ProjectCreator.pm to see if it needs changing too. + + my $ovalue = $value; + my($rel, $how) = $self->get_initial_relative_values(); + $value = $self->expand_variables($value, $rel, + $expand_template, $scope, $how); + + if ($ovalue eq $value || index($value, '$') >= 0) { + ($rel, $how) = $self->get_secondary_relative_values(); + $value = $self->expand_variables($value, $rel, + $expand_template, $scope, + $how, 1); + } + } + } + + ## Values that have two or more strings enclosed in double quotes are + ## to be interpreted as elements of an array + if (defined $value && $value =~ /^"[^"]+"(\s+"[^"]+")+$/) { + $value = $self->create_array($value); + } + + return $value; +} + + +## Static function. Returns the default language for MPC. +sub defaultLanguage { + return $deflang; +} + + +## Static function. Returns an array of valid languages. +sub validLanguages { + return keys %languages; +} + + +## Static function. The one and only argument is the language +## string to check for validity. +sub isValidLanguage { + return defined $languages{$_[0]}; +} + + +sub languageIs { + #my($self, $language) = @_; + return $_[0]->{'language'} eq $_[1]; +} + +# ************************************************************ +# Virtual Methods To Be Overridden +# ************************************************************ + +sub restore_state_helper { + #my $self = shift; + #my $skey = shift; + #my $old = shift; + #my $new = shift; +} + + +sub get_initial_relative_values { + #my $self = shift; + return {}, 0; +} + + +sub get_secondary_relative_values { + my $self = shift; + return ($self->{'use_env'} ? \%ENV : + $self->{'relative'}), $self->{'expand_vars'}; +} + + +sub convert_all_variables { + #my $self = shift; + return 0; +} + + +sub expand_variables_from_template_values { + #my $self = shift; + return 0; +} + + +sub preserve_assignment_order { + #my $self = shift; + #my $name = shift; + return 1; +} + + +sub compare_output { + #my $self = shift; + return 0; +} + + +sub files_are_different { + my($self, $old, $new) = @_; + return !(-r $old && -s $new == -s $old && compare($new, $old) == 0); +} + + +sub handle_scoped_end { + #my $self = shift; + #my $type = shift; + #my $flags = shift; + return 1, undef; +} + +sub handle_unknown_assignment { + my $self = shift; + my $type = shift; + my @values = @_; + return 0, "Invalid assignment name: '$values[1]'"; +} + + +sub handle_scoped_unknown { + my($self, $fh, $type, $flags, $line) = @_; + return 0, "Unrecognized line: $line"; +} + + +sub remove_duplicate_addition { + my($self, $name, $value, $current) = @_; + return $value; +} + + +sub generate_recursive_input_list { + #my $self = shift; + #my $dir = shift; + #my $exclude = shift; + return (); +} + + +sub reset_values { + #my $self = shift; +} + + +sub sort_files { + #my $self = shift; + return 1; +} + + +sub file_sorter { + #my $self = shift; + #my $left = shift; + #my $right = shift; + return $_[1] cmp $_[2]; +} + + +sub read_global_configuration { + #my $self = shift; + #my $input = shift; + return 1; +} + + +sub set_verbose_ordering { + #my $self = shift; + #my $value = shift; +} + + +1; |