3 # ************************************************************
4 # Description : Base class for workspace and project creators
5 # Author : Chad Elliott
6 # Create Date : 5/13/2002
7 # ************************************************************
9 # ************************************************************
11 # ************************************************************
22 # ************************************************************
24 # ************************************************************
26 ## Constants for use throughout the project
27 use constant cplusplus
=> 'cplusplus';
28 use constant csharp
=> 'csharp';
29 use constant java
=> 'java';
30 use constant vb
=> 'vb';
31 use constant website
=> 'website';
33 ## The default language for MPC
34 my $deflang = 'cplusplus';
36 ## A map of all of the allowed languages. The 'website' value
37 ## is not here because it isn't really a language. It is used
38 ## as a language internally by some project types though.
39 ## NOTE: We call the constant as a function to support Perl 5.6.
40 my %languages = (cplusplus
() => 1,
46 my $assign_key = 'assign';
47 my $gassign_key = 'global_assign';
48 my %non_convert = ('prebuild' => 1,
52 my @statekeys = ('global', 'include', 'template', 'ti',
53 'dynamic', 'static', 'relative', 'addtemp',
54 'addproj', 'progress', 'toplevel', 'baseprojs',
55 'features', 'feature_file', 'hierarchy',
56 'name_modifier', 'apply_project', 'into', 'use_env',
57 'expand_vars', 'language',
61 my $onVMS = DirectoryManager
::onVMS
();
63 # ************************************************************
65 # ************************************************************
68 my($class, $global, $inc, $template,
69 $ti, $dynamic, $static, $relative,
70 $addtemp, $addproj, $progress,
71 $toplevel, $baseprojs, $feature,
72 $features, $hierarchy, $nmodifier,
73 $applypj, $into, $language, $use_env,
74 $expandvars, $type) = @_;
76 my $self = Parser
::new
($class, $inc);
78 $self->{'relative'} = $relative;
79 $self->{'template'} = $template;
81 $self->{'global'} = $global;
82 $self->{'grammar_type'} = $type;
83 $self->{'type_check'} = $type . '_defined';
84 $self->{'global_read'} = 0;
85 $self->{'current_input'} = '';
86 $self->{'progress'} = $progress;
87 $self->{'addtemp'} = $addtemp;
88 $self->{'addproj'} = $addproj;
89 $self->{'toplevel'} = $toplevel;
90 $self->{'files_written'} = {};
91 $self->{'real_fwritten'} = [];
92 $self->{'reading_global'} = 0;
93 $self->{$gassign_key} = {};
94 $self->{$assign_key} = {};
95 $self->{'baseprojs'} = $baseprojs;
96 $self->{'dynamic'} = $dynamic;
97 $self->{'static'} = $static;
98 $self->{'feature_file'} = $feature;
99 $self->{'features'} = $features;
100 $self->{'hierarchy'} = $hierarchy;
101 $self->{'name_modifier'} = $nmodifier;
102 $self->{'apply_project'} = $applypj;
103 $self->{'into'} = $into;
104 $self->{'language'} = defined $language ?
$language : $deflang;
105 $self->{'use_env'} = $use_env;
106 $self->{'expand_vars'} = $expandvars;
107 $self->{'convert_slashes'} = $self->convert_slashes();
108 $self->{'requires_forward_slashes'} = $self->requires_forward_slashes();
109 $self->{'case_tolerant'} = $self->case_insensitive();
114 sub preprocess_line
{
115 my($self, $fh, $line) = @_;
117 $line = $self->strip_line($line);
118 while ($line =~ /\\$/) {
119 $line =~ s/\s*\\$/ /;
120 my $next = $fh->getline();
121 $line .= $self->strip_line($next) if (defined $next);
127 sub generate_default_input
{
130 $error) = $self->parse_line(undef, "$self->{'grammar_type'} {");
132 ## Parse the finish line if there was no error
133 ($status, $error) = $self->parse_line(undef, '}') if ($status);
135 ## Display the error if there was one
136 $self->error($error) if (!$status);
143 my($self, $input) = @_;
145 ## Save the last line number so we can put it back later
146 my $oline = $self->get_line_number();
148 ## Read the input file
149 my($status, $errorString) = $self->read_file($input);
152 $self->error($errorString,
153 "$input: line " . $self->get_line_number() . ':');
155 elsif ($self->{$self->{'type_check'}}) {
156 ## If we are at the end of the file and the type we are looking at
157 ## is still defined, then we have an error
158 $self->error("Did not " .
159 "find the end of the $self->{'grammar_type'}",
160 "$input: line " . $self->get_line_number() . ':');
163 $self->set_line_number($oline);
170 my($self, $input) = @_;
173 ## Reset the files_written hash array between processing each file
174 $self->{'files_written'} = {};
175 $self->{'real_fwritten'} = [];
177 ## Allow subclasses to reset values before
178 ## each call to generate().
179 $self->reset_values();
181 ## Read the global configuration file
182 if (!$self->{'global_read'}) {
183 $status = $self->read_global_configuration();
184 $self->{'global_read'} = 1;
188 $self->{'current_input'} = $input;
190 ## An empty input file name says that we
191 ## should generate a default input file and use that
193 $status = $self->generate_default_input();
196 $status = $self->parse_file($input);
203 # split an inheritance list like ": a,b, c" into components
205 my($parents, $errorStringRef, $statusRef) = @_;
206 if (defined $parents) {
207 $parents =~ s/^:\s*//;
208 $parents =~ s/\s+$//;
209 my @parents = split(/\s*,\s*/, $parents);
210 if (!defined $parents[0]) {
211 ## The : was used, but no parents followed. This
213 $$errorStringRef = 'No parents listed';
223 my($self, $line, $fh) = @_;
226 my $type = $self->{'grammar_type'};
230 ## Each regexp that looks for the '{' looks for it at the
231 ## end of the line. It is purposely this way to decrease
232 ## the amount of extra lines in each file. This
233 ## allows for the most compact file as human readably
238 elsif ($line =~ /^$type\s*(\([^\)]+\))?\s*(:.*)?\s*{$/) {
241 if ($self->{$self->{'type_check'}}) {
242 $errorString = "Did not find the end of the $type";
246 $parents = parse_parents
($parents, \
$errorString, \
$status);
247 push(@values, $type, $name, $parents);
250 elsif ($line =~ /^}$/) {
251 if ($self->{$self->{'type_check'}}) {
252 push(@values, $type, $line);
255 $errorString = "Did not find the beginning of the $type";
259 elsif ($line =~ /^(feature)\s*\(([^\)]+)\)\s*(:.*)?\s*{$/) {
263 my @names = split(/\s*,\s*/, $name);
264 $parents = parse_parents
($parents, \
$errorString, \
$status);
265 push(@values, $type, \
@names, $parents);
267 elsif (!$self->{$self->{'type_check'}}) {
268 $errorString = "No $type was defined";
271 elsif ($self->parse_assignment($line, \
@values, $fh)) {
272 ## If this returns true, then we've found an assignment
274 elsif ($line =~ /^(\w+)\s*(\([^\)]+\))?\s*(:.*)?\s*{$/) {
284 $name = $self->get_default_component_name();
286 $parents = parse_parents
($parents, \
$errorString, \
$status);
287 push(@values, 'component', $comp, $name, $parents);
290 $errorString = "Unrecognized line: $line";
294 return $status, $errorString, @values;
297 ## Parse an assignment that is bracketed by curly braces so it can span multiple lines.
298 ## This method parses the bracketed assignment into a regular assignment
299 ## and then calls SUPER::parse_assigment.
301 ## A bracketed assigment has the form of:
303 ## keyword <operator> [optional flags] {
308 ## Optional flags are \s to retain leading white space and
309 ## \n to retain new lines. These flags are be combined.
310 sub parse_assignment
{
311 my($self, $line, $values, $fh) = @_;
313 if ($line =~ /^(\w+)\s*([\-+]?=)\s*(\\[sn]{1,2})?\s*{$/) {
316 my $keep_leading_whitespace = ($3 eq "\\s" || $3 eq "\\ns" || $3 eq "\\sn");
317 my $keep_new_lines = ($3 eq "\\n" || $3 eq "\\ns" || $3 eq "\\sn");
319 my $bracketed_assignment;
321 ## This is not an error,
322 ## this is the end of the bracketed assignment.
323 last if ($_ =~ /^\s*}\s*$/);
326 my $current_line = $self->strip_comments($_);
327 ## Skip blank lines unless we're keeping new lines.
328 next if (!$keep_new_lines && $self->is_blank_line($current_line));
330 $bracketed_assignment .= "\n" if defined $bracketed_assignment && $keep_new_lines;
332 $bracketed_assignment .= $self->strip_lt_whitespace($current_line, $keep_leading_whitespace);
335 if (defined $bracketed_assignment) {
336 $line = $comp . $op . $bracketed_assignment;
340 return $self->SUPER::parse_assignment
($line, \@
$values);
344 my($self, $fh, $name, $type, $validNames, $flags, $elseflags) = @_;
346 my $errorString = "Unable to process $name";
348 ## Make sure $flags has a hash map reference
349 $flags = {} if (!defined $flags);
352 my $line = $self->preprocess_line($fh, $_);
356 elsif ($line =~ /^}$/) {
357 ($status, $errorString) = $self->handle_scoped_end($type, $flags);
360 elsif ($line =~ /^}\s*else\s*{$/) {
361 if (defined $elseflags) {
362 ## From here on out anything after this goes into the $elseflags
366 ## We need to adjust the type also. If there was a type
367 ## then the first part of the clause was used. If there was
368 ## no type, then the first part was ignored and the second
369 ## part will be used.
374 $type = $self->get_default_component_name();
379 $errorString = 'An else is not allowed in this context';
385 if (defined $validNames && $self->parse_assignment($line, \
@values, $fh)) {
386 if (defined $$validNames{$values[1]}) {
387 ## If $type is not defined, we don't even need to bother with
388 ## processing the assignment as we will be throwing the value
391 $self->process_any_assignment($flags, @values);
396 $errorString) = $self->handle_unknown_assignment($type,
402 ($status, $errorString) = $self->handle_scoped_unknown($fh,
410 return $status, $errorString;
414 sub process_any_assignment
{
415 my($self, $flags, @values) = @_;
417 if ($values[0] == 0) {
418 $self->process_assignment($values[1], $values[2], $flags);
420 elsif ($values[0] == 1) {
421 $self->process_assignment_add($values[1], $values[2], $flags);
423 elsif ($values[0] == -1) {
424 $self->process_assignment_sub($values[1], $values[2], $flags);
431 return $self->mpc_basename($self->getcwd());
435 sub generate_default_file_list
{
436 my($self, $dir, $exclude, $fileexc, $recurse) = @_;
437 my $dh = new FileHandle
();
440 if (opendir($dh, $dir)) {
441 my $prefix = ($dir ne '.' ?
"$dir/" : '');
442 my $have_exc = (defined $$exclude[0]);
444 foreach my $file (grep(!/^\.\.?$/,
445 ($onVMS ?
map {$_ =~ s/\.dir$//; $_} readdir($dh) :
447 ## Prefix each file name with the directory only if it's not '.'
448 my $full = $prefix . $file;
451 foreach my $exc (@
$exclude) {
461 $$fileexc = 1 if (defined $fileexc);
464 if ($recurse && -d
$full) {
466 $self->generate_default_file_list($full, $exclude,
467 $fileexc, $recurse));
470 # Strip out ^ symbols
471 $full =~ s/\^//g if ($onVMS);
478 if ($self->sort_files()) {
479 @files = sort { $self->file_sorter($a, $b) } @files;
488 sub transform_file_name
{
489 my($self, $name) = @_;
491 $name =~ s/[\s\-]/_/g;
497 my($self, $file) = @_;
498 return (defined $all_written{$self->getcwd() . '/' . $file});
502 sub add_file_written
{
503 my($self, $file) = @_;
506 if (defined $self->{'files_written'}->{$key}) {
507 $self->warning("$self->{'grammar_type'} $file " .
508 ($self->{'case_tolerant'} ?
509 "has been overwritten." :
510 "of differing case has been processed."));
513 $self->{'files_written'}->{$key} = $file;
514 push(@
{$self->{'real_fwritten'}}, $file);
517 $all_written{$self->getcwd() . '/' . $file} = 1;
521 sub extension_recursive_input_list
{
522 my($self, $dir, $exclude, $ext) = @_;
523 my $fh = new FileHandle
();
526 if (opendir($fh, $dir)) {
527 my $prefix = ($dir ne '.' ?
"$dir/" : '');
529 foreach my $file (grep(!/^\.\.?$/,
530 ($onVMS ?
map {$_ =~ s/\.dir$//; $_} readdir($fh) :
532 my $full = $prefix . $file;
534 ## Check for command line exclusions
535 if (defined $$exclude[0]) {
536 foreach my $exc (@
$exclude) {
544 ## If we are not skipping this directory or file, then check it out
550 push(@files, $self->extension_recursive_input_list($full,
554 elsif ($full =~ /$ext$/) {
565 sub recursive_directory_list
{
566 my($self, $dir, $exclude) = @_;
567 my $directories = '';
568 my $fh = new FileHandle
();
570 if (opendir($fh, $dir)) {
571 my $prefix = ($dir ne '.' ?
"$dir/" : '');
573 if (defined $$exclude[0]) {
574 foreach my $exc (@
$exclude) {
585 $directories .= ' ' . $dir;
588 foreach my $file (grep(!/^\.\.?$/,
589 ($onVMS ?
map {$_ =~ s/\.dir$//; $_} readdir($fh) :
591 my $full = $prefix . $file;
593 if ($file eq '.svn' || $file eq 'CVS') {
597 ## Check for command line exclusions
598 if (defined $$exclude[0]) {
599 foreach my $exc (@
$exclude) {
608 ## If we are not skipping this directory or file, then check it out
614 $directories .= $self->recursive_directory_list($full, $exclude);
625 sub modify_assignment_value
{
626 my($self, $name, $value) = @_;
628 if ($self->{'convert_slashes'} &&
629 index($name, 'flags') == -1 && !defined $non_convert{$name}) {
637 sub get_assignment_hash
{
638 ## NOTE: If anything in this block changes, then you must make the
639 ## same change in process_assignment.
641 return $self->{$self->{'reading_global'} ?
$gassign_key : $assign_key};
645 sub process_assignment
{
646 my($self, $name, $value, $assign) = @_;
648 ## If no hash table was passed in
649 if (!defined $assign) {
650 ## NOTE: If anything in this block changes, then you must make the
651 ## same change in get_assignment_hash.
652 $assign = $self->{$self->{'reading_global'} ?
653 $gassign_key : $assign_key};
656 if (defined $value) {
660 ## Modify the assignment value before saving it
661 $$assign{$name} = $self->modify_assignment_value($name, $value);
664 $$assign{$name} = undef;
670 my($self, $name, $value, $nval, $assign) = @_;
673 if ($self->preserve_assignment_order($name)) {
677 $nval = "$value $nval";
683 $self->process_assignment($name, $nval, $assign, 1);
687 sub process_assignment_add
{
688 my($self, $name, $value, $assign) = @_;
689 my $nval = $self->get_assignment_for_modification($name, $assign);
691 ## Remove all duplicate parts from the value to be added.
692 ## Whether anything gets removed or not is up to the implementation
693 ## of the sub classes.
694 $value = $self->remove_duplicate_addition($name, $value, $nval);
696 ## If there is anything to add, then do so
697 $self->addition_core($name, $value, $nval, $assign) if ($value ne '');
701 sub subtraction_core
{
702 my($self, $name, $value, $nval, $assign) = @_;
708 ## Escape any regular expression special characters
709 $value = $self->escape_regex_special($value);
711 ## If necessary, split the value into an array
712 my $elements = ($value =~ /\s/ ?
$self->create_array($value) : [$value]);
713 for(my $i = 0; $i <= $last; $i++) {
715 ## If we did not find the string to subtract in the original
716 ## value, try again after expanding template variables for
718 $nval = $self->get_assignment_for_modification($name, $assign, 1);
720 for(my $j = 0; $j <= $last; $j++) {
721 ## Try to remove each individual element and then set the new
722 ## value if any of the elements were found in the original value
723 foreach my $elem (@
$elements) {
724 ## First try with quotes, then try again without them
725 my $re = ($j == 0 ?
'"' . $elem . '"' : $elem);
727 if ($nval =~ s/\s+$re\s+/ / || $nval =~ s/\s+$re$// ||
728 $nval =~ s/^$re\s+// || $nval =~ s/^$re$//) {
733 $self->process_assignment($name, $nval, $assign, -1);
743 sub process_assignment_sub
{
744 my($self, $name, $value, $assign) = @_;
745 my $nval = $self->get_assignment_for_modification($name, $assign);
747 ## Remove double quotes if there are any
748 $value =~ s/^\"(.*)\"$/$1/;
750 ## Call to the core function to perform the subtraction. We must also
751 ## pass the value through the assignment modifier to ensure that
752 ## slashes are in the project native format.
753 $self->subtraction_core($name,
754 $self->modify_assignment_value($name, $value),
760 my($self, $names, $def) = @_;
761 my $array = ($names =~ /\s/ ?
$self->create_array($names) : [$names]);
764 foreach my $name (@
$array) {
766 my $pre = $def . '_';
767 my $mid = '_' . $def . '_';
768 my $post = '_' . $def;
770 ## Replace the beginning and end first then the middle
771 $name =~ s/^\*/$pre/;
772 $name =~ s/\*$/$post/;
773 $name =~ s/\*/$mid/g;
775 ## Remove any trailing underscore or any underscore that is followed
776 ## by a space. This value could be a space separated list.
781 ## If any one word is capitalized then capitalize each word
782 if ($name =~ /[A-Z][0-9a-z_]+/) {
784 if ($name =~ /^([a-z])([^_]+)/) {
787 $name =~ s/^[a-z][^_]+/$first$rest/;
789 ## Do subsequent words
790 while($name =~ /(_[a-z])([^_]+)/) {
793 $name =~ s/_[a-z][^_]+/$first$rest/;
798 $names .= $name . ' ';
807 my($self, $obj) = @_;
809 ## Check for various types of data. Those that are not found to be
810 ## types that we need to deep copy are just assigned to new values.
811 ## All others are copied by recursively calling this method.
812 if (UNIVERSAL
::isa
($obj, 'HASH')) {
814 foreach my $key (keys %$obj) {
815 $$new{$key} = $self->clone($$obj{$key});
819 elsif (UNIVERSAL
::isa
($obj, 'ARRAY')) {
821 foreach my $o (@
$obj) {
822 push(@
$new, $self->clone($o));
832 my($self, $selected) = @_;
835 ## Make a deep copy of each state value. That way our array
836 ## references and hash references do not get accidentally modified.
837 foreach my $skey (defined $selected ?
$selected : @statekeys) {
838 if (defined $self->{$skey}) {
839 ## It is necessary to clone each value so that nested complex data
840 ## types do not get unknowingly modified.
841 $state{$skey} = $self->clone($self->{$skey});
850 my($self, $state, $selected) = @_;
852 ## Make a deep copy of each state value. That way our array
853 ## references and hash references do not get accidentally modified.
854 ## It's not necessary to do a recursive deep copy (i.e., use the
855 ## clone() method) because the value coming in will now be owned by
856 ## this object and will not be modified unknowingly.
857 foreach my $skey (defined $selected ?
$selected : @statekeys) {
858 my $old = $self->{$skey};
859 if (defined $state->{$skey} &&
860 UNIVERSAL
::isa
($state->{$skey}, 'ARRAY')) {
861 my @arr = @
{$state->{$skey}};
862 $self->{$skey} = \
@arr;
864 elsif (defined $state->{$skey} &&
865 UNIVERSAL
::isa
($state->{$skey}, 'HASH')) {
866 my %hash = %{$state->{$skey}};
867 $self->{$skey} = \
%hash;
870 $self->{$skey} = $state->{$skey};
872 $self->restore_state_helper($skey, $old, $self->{$skey});
878 return $_[0]->{'global'};
882 sub get_template_override
{
883 return $_[0]->{'template'};
887 sub get_ti_override
{
888 return $_[0]->{'ti'};
893 return $_[0]->{'relative'};
897 sub get_progress_callback
{
898 return $_[0]->{'progress'};
903 return $_[0]->{'addtemp'};
908 return $_[0]->{'addproj'};
913 return $_[0]->{'toplevel'};
918 return $_[0]->{'into'};
923 return $_[0]->{'use_env'};
927 sub get_expand_vars
{
928 return $_[0]->{'expand_vars'};
932 sub get_files_written
{
933 return $_[0]->{'real_fwritten'};
939 my $name = $self->resolve_alias(shift);
942 ## If no hash table was passed in
943 if (!defined $assign) {
944 $assign = $self->{$self->{'reading_global'} ?
945 $gassign_key : $assign_key};
948 return $$assign{$name};
952 sub get_assignment_for_modification
{
953 my($self, $name, $assign, $subtraction) = @_;
954 return $self->get_assignment($name, $assign);
959 return $_[0]->{'baseprojs'};
964 return $_[0]->{'dynamic'};
969 return $_[0]->{'static'};
973 sub get_default_component_name
{
980 return $_[0]->{'features'};
985 return $_[0]->{'hierarchy'};
989 sub get_name_modifier
{
990 return $_[0]->{'name_modifier'};
994 sub get_apply_project
{
995 return $_[0]->{'apply_project'};
1000 return $_[0]->{'language'};
1006 if (defined $self->{'into'}) {
1007 ## First, try to remove our starting directory from the current
1008 ## working directory.
1009 my $outdir = $self->getcwd();
1010 my $re = $self->escape_regex_special($self->getstartdir());
1011 if ($outdir !~ s/^$re//) {
1012 ## If that fails and we're running on an OS that supports drive
1013 ## letters, we need to try to remove the drive letter. We also
1014 ## warn the user that it's not likely to work properly.
1016 if ((($^O
eq 'MSWin32' || $^O
eq 'cygwin') &&
1017 $outdir =~ s/^[a-z]://i) || $outdir =~ m!^/!) {
1018 $self->warning("Unable to use $orig with the -into option");
1022 return $self->{'into'} . $outdir;
1030 sub aggressively_replace
{
1031 my($self, $icwd, $val) = @_;
1034 my $ival = ($self->{'case_tolerant'} ?
lc($val) : $val);
1036 ## Search back up the directories until we either find a match or we
1037 ## run out of directories.
1038 while($wd =~ s/[^\/]+[\/]?
$//) {
1039 ## We have gone up one directory
1042 ## Make a regular expression and see if we have found a match
1043 ## with our provided directory value.
1044 my $re = $self->escape_regex_special($wd);
1045 if ($ival =~ /^($re)/) {
1046 ## We have found how it is relative. Now make the relative path
1049 my $suffix = substr($val, length($prefix));
1050 return ('../' x
$count) . $suffix;
1054 ## We never found a match
1058 sub expand_variables
{
1059 my($self, $value, $rel, $expand_template, $scopes, $expand, $warn) = @_;
1060 my $cwd = $self->getcwd();
1062 my $forward_slashes = $self->{'convert_slashes'} ||
1063 $self->{'requires_forward_slashes'};
1064 my $aggrep = $self->aggressive_relative_replacement();
1066 ## Fix up the value for Windows switch the \\'s to /
1067 $cwd =~ s/\\/\//g
if ($forward_slashes);
1069 while(substr($value, $start) =~ /(\$\(([^)]+)\))/) {
1072 if (defined $$rel{$name}) {
1073 my $val = $$rel{$name};
1075 $val =~ s/\//\\/g
if ($forward_slashes);
1076 substr($value, $start) =~ s/\$\([^)]+\)/$val/;
1080 ## Fix up the value for Windows switch the \\'s to /
1081 $val =~ s/\\/\//g
if ($forward_slashes);
1083 my $icwd = ($self->{'case_tolerant'} ?
lc($cwd) : $cwd);
1084 my $ival = ($self->{'case_tolerant'} ?
lc($val) : $val);
1085 my $iclen = length($icwd);
1086 my $ivlen = length($ival);
1088 ## If the relative value contains the current working
1089 ## directory plus additional subdirectories, we must pull
1090 ## off the additional directories into a temporary where
1091 ## it can be put back after the relative replacement is done.
1093 if (index($ival, $icwd) == 0 && $iclen != $ivlen &&
1094 substr($ival, $iclen, 1) eq '/') {
1095 my $diff = $ivlen - $iclen;
1096 $append = substr($ival, $iclen);
1097 substr($ival, $iclen, $diff) = '';
1101 if (index($icwd, $ival) == 0 &&
1102 ($iclen == $ivlen || substr($icwd, $ivlen, 1) eq '/')) {
1103 my $current = $icwd;
1104 substr($current, 0, $ivlen) = '';
1106 my $dircount = ($current =~ tr/\///);
1107 if ($dircount == 0) {
1111 $ival = '../' x
$dircount;
1114 $ival .= $append if (defined $append);
1116 ## We have to remove the leading ./ if there is one.
1117 ## Otherwise, if this value is used as an exclude value it will
1118 ## not match up correctly.
1121 ## Convert the slashes if necessary
1122 $ival =~ s/\//\\/g
if ($self->{'convert_slashes'});
1123 substr($value, $start) =~ s/\$\([^)]+\)/$ival/;
1126 elsif ($self->convert_all_variables() && $warn) {
1127 ## The user did not choose to expand $() variables directly,
1128 ## but we could not convert it into a relative path. So,
1129 ## instead of leaving it we will expand it. But, we will only
1130 ## get into this section if this is the secondary attempt to
1131 ## replace the variable (indicated by the $warn boolean).
1134 ($aggressive_rel = $self->aggressively_replace($icwd, $val))) {
1135 $aggressive_rel =~ s/\//\\/g
if ($self->{'convert_slashes'});
1136 substr($value, $start) =~ s/\$\([^)]+\)/$aggressive_rel/;
1137 $whole = $aggressive_rel;
1140 $val =~ s/\//\\/g
if ($self->{'convert_slashes'});
1141 substr($value, $start) =~ s/\$\([^)]+\)/$val/;
1148 ($aggressive_rel = $self->aggressively_replace($icwd, $val))) {
1149 $aggressive_rel =~ s/\//\\/g
if ($self->{'convert_slashes'});
1150 substr($value, $start) =~ s/\$\([^)]+\)/$aggressive_rel/;
1151 $whole = $aggressive_rel;
1154 my $loc = index(substr($value, $start), $whole);
1155 $start += $loc if ($loc > 0);
1160 elsif ($expand_template ||
1161 $self->expand_variables_from_template_values()) {
1162 my $ti = $self->get_template_input();
1163 my $val = (defined $ti ?
$ti->get_value($name) : undef);
1165 if (defined $scopes) {
1166 @snames = map { (defined $_ ?
$_ : '') . '::' . $name } @
$scopes;
1168 push(@snames, $name);
1169 my $arr = $self->adjust_value(\
@snames,
1170 (defined $val ?
$val : []));
1171 if (UNIVERSAL
::isa
($arr, 'HASH')) {
1172 $self->warning("$name conflicts with a template variable scope");
1174 elsif (UNIVERSAL
::isa
($arr, 'ARRAY') && defined $$arr[0]) {
1175 $val = $self->modify_assignment_value(lc($name), "@$arr");
1176 substr($value, $start) =~ s/\$\([^)]+\)/$val/;
1178 ## We have replaced the template value, but that template
1179 ## value may contain a $() construct that may need to get
1180 ## replaced too. However, if the name of the template variable
1181 ## is the same as the original $() variable name, we need to
1182 ## leave it alone to avoid looping infinitely.
1183 $whole = '' if ($whole ne $val);
1186 $self->warning("Unable to expand $name.") if ($expand && $warn);
1187 my $loc = index(substr($value, $start), $whole);
1188 $start += $loc if ($loc > 0);
1191 elsif ($self->convert_all_variables() && $warn) {
1192 ## We could not find a value to correspond to the variable name.
1193 ## Instead of leaving it we will expand it. But, we will only
1194 ## get into this section if this is the secondary attempt to
1195 ## replace the variable (indicated by the $warn boolean).
1196 substr($value, $start) =~ s/\$\([^)]+\)//;
1200 my $loc = index(substr($value, $start), $whole);
1201 $start += $loc if ($loc > 0);
1203 $start += length($whole);
1206 $value =~ s/\\/\//g
if ($self->{'requires_forward_slashes'});
1212 sub replace_env_vars
{
1213 my($self, $lref) = @_;
1214 my $one_empty = undef;
1216 ## Loop through the string until we find no more environment variables.
1217 while($$lref =~ /\$(\w+)/) {
1221 ## PWD is a special variable. It isn't set on Windows, but in MPC we
1222 ## must guarantee that it is always there.
1223 if ($name eq 'PWD') {
1224 $val = $self->getcwd();
1226 elsif (defined $ENV{$name}) {
1230 ## Keep track of an environment variable not being set.
1233 $$lref =~ s/\$\w+/$val/;
1240 my($self, $value, $expand_template, $scopes) = @_;
1242 if (defined $value) {
1243 if (UNIVERSAL
::isa
($value, 'ARRAY')) {
1245 foreach my $val (@
$value) {
1246 my $rel = $self->relative($val, $expand_template, $scopes);
1247 if (UNIVERSAL
::isa
($rel, 'ARRAY')) {
1248 push(@built, @
$rel);
1256 elsif (index($value, '$') >= 0) {
1257 ## A form of this code lives in
1258 ## ProjectCreator::create_recursive_settings. If you are changing
1259 ## something in this area, please look at the method in
1260 ## ProjectCreator.pm to see if it needs changing too.
1262 my $ovalue = $value;
1263 my($rel, $how) = $self->get_initial_relative_values();
1264 $value = $self->expand_variables($value, $rel,
1265 $expand_template, $scopes, $how, 0);
1267 if ($ovalue eq $value || index($value, '$') >= 0) {
1268 ($rel, $how) = $self->get_secondary_relative_values();
1269 $value = $self->expand_variables($value, $rel,
1270 $expand_template, $scopes,
1276 ## Values that have two or more strings enclosed in double quotes are
1277 ## to be interpreted as elements of an array
1278 if (defined $value && $value =~ /^"[^"]+"(\s+"[^"]+")+$/) {
1279 $value = $self->create_array($value);
1286 ## Static function. Returns the default language for MPC.
1287 sub defaultLanguage
{
1292 ## Static function. Returns an array of valid languages.
1293 sub validLanguages
{
1294 return keys %languages;
1298 ## Static function. The one and only argument is the language
1299 ## string to check for validity.
1300 sub isValidLanguage
{
1301 return defined $languages{$_[0]};
1306 #my($self, $language) = @_;
1307 return $_[0]->{'language'} eq $_[1];
1310 # ************************************************************
1311 # Virtual Methods To Be Overridden
1312 # ************************************************************
1314 sub restore_state_helper
{
1322 sub get_initial_relative_values
{
1328 sub get_secondary_relative_values
{
1330 return ($self->{'use_env'} ? \
%ENV :
1331 $self->{'relative'}), $self->{'expand_vars'};
1335 sub aggressive_relative_replacement
{
1341 sub convert_all_variables
{
1347 sub expand_variables_from_template_values
{
1353 sub preserve_assignment_order
{
1360 sub compare_output
{
1366 sub files_are_different
{
1367 my($self, $old, $new) = @_;
1368 return !(-r
$old && -s
$new == -s
$old && compare
($new, $old) == 0);
1372 sub handle_scoped_end
{
1379 sub handle_unknown_assignment
{
1383 return 0, "Invalid assignment name: '$values[1]'";
1387 sub handle_scoped_unknown
{
1388 my($self, $fh, $type, $flags, $line) = @_;
1389 return 0, "Unrecognized line: $line";
1393 sub remove_duplicate_addition
{
1394 my($self, $name, $value, $current) = @_;
1399 sub generate_recursive_input_list
{
1402 #my $exclude = shift;
1422 return $_[1] cmp $_[2];
1426 sub read_global_configuration
{
1433 sub set_verbose_ordering
{
1439 sub get_properties
{
1442 ## Create the property hash map with the language property
1443 my %props = ($self->get_language() => 1);
1445 ## Set the 'static' property only if the project is static
1446 $props{'static'} = 1 if ($self->get_static());