Wed Jun 9 07:35:19 UTC 2010 Johnny Willemsen <jwillemsen@remedy.nl>
[MPC.git] / modules / Creator.pm
blob0f2fc795af74ba325d0be78263e0627867a4cd58
1 package Creator;
3 # ************************************************************
4 # Description : Base class for workspace and project creators
5 # Author : Chad Elliott
6 # Create Date : 5/13/2002
7 # ************************************************************
9 # ************************************************************
10 # Pragmas
11 # ************************************************************
13 use strict;
14 use FileHandle;
15 use File::Compare;
17 use Parser;
19 use vars qw(@ISA);
20 @ISA = qw(Parser);
22 # ************************************************************
23 # Data Section
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 my %languages = (cplusplus => 1,
40 csharp => 1,
41 java => 1,
42 vb => 1,
45 my $assign_key = 'assign';
46 my $gassign_key = 'global_assign';
47 my %non_convert = ('prebuild' => 1,
48 'postbuild' => 1,
49 'postclean' => 1,
51 my @statekeys = ('global', 'include', 'template', 'ti',
52 'dynamic', 'static', 'relative', 'addtemp',
53 'addproj', 'progress', 'toplevel', 'baseprojs',
54 'features', 'feature_file', 'hierarchy',
55 'name_modifier', 'apply_project', 'into', 'use_env',
56 'expand_vars', 'language',
59 my %all_written;
60 my $onVMS = DirectoryManager::onVMS();
62 # ************************************************************
63 # Subroutine Section
64 # ************************************************************
66 sub new {
67 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) = @_;
68 my $self = Parser::new($class, $inc);
70 $self->{'relative'} = $relative;
71 $self->{'template'} = $template;
72 $self->{'ti'} = $ti;
73 $self->{'global'} = $global;
74 $self->{'grammar_type'} = $type;
75 $self->{'type_check'} = $type . '_defined';
76 $self->{'global_read'} = 0;
77 $self->{'current_input'} = '';
78 $self->{'progress'} = $progress;
79 $self->{'addtemp'} = $addtemp;
80 $self->{'addproj'} = $addproj;
81 $self->{'toplevel'} = $toplevel;
82 $self->{'files_written'} = {};
83 $self->{'real_fwritten'} = [];
84 $self->{'reading_global'} = 0;
85 $self->{$gassign_key} = {};
86 $self->{$assign_key} = {};
87 $self->{'baseprojs'} = $baseprojs;
88 $self->{'dynamic'} = $dynamic;
89 $self->{'static'} = $static;
90 $self->{'feature_file'} = $feature;
91 $self->{'features'} = $features;
92 $self->{'hierarchy'} = $hierarchy;
93 $self->{'name_modifier'} = $nmodifier;
94 $self->{'apply_project'} = $applypj;
95 $self->{'into'} = $into;
96 $self->{'language'} = defined $language ? $language : $deflang;
97 $self->{'use_env'} = $use_env;
98 $self->{'expand_vars'} = $expandvars;
99 $self->{'convert_slashes'} = $self->convert_slashes();
100 $self->{'requires_forward_slashes'} = $self->requires_forward_slashes();
101 $self->{'case_tolerant'} = $self->case_insensitive();
103 return $self;
107 sub preprocess_line {
108 my($self, $fh, $line) = @_;
110 $line = $self->strip_line($line);
111 while ($line =~ /\\$/) {
112 $line =~ s/\s*\\$/ /;
113 my $next = $fh->getline();
114 $line .= $self->strip_line($next) if (defined $next);
116 return $line;
120 sub generate_default_input {
121 my $self = shift;
122 my($status,
123 $error) = $self->parse_line(undef, "$self->{'grammar_type'} {");
125 ## Parse the finish line if there was no error
126 ($status, $error) = $self->parse_line(undef, '}') if ($status);
128 ## Display the error if there was one
129 $self->error($error) if (!$status);
131 return $status;
135 sub parse_file {
136 my($self, $input) = @_;
138 ## Save the last line number so we can put it back later
139 my $oline = $self->get_line_number();
141 ## Read the input file
142 my($status, $errorString) = $self->read_file($input);
144 if (!$status) {
145 $self->error($errorString,
146 "$input: line " . $self->get_line_number() . ':');
148 elsif ($self->{$self->{'type_check'}}) {
149 ## If we are at the end of the file and the type we are looking at
150 ## is still defined, then we have an error
151 $self->error("Did not " .
152 "find the end of the $self->{'grammar_type'}",
153 "$input: line " . $self->get_line_number() . ':');
154 $status = 0;
156 $self->set_line_number($oline);
158 return $status;
162 sub generate {
163 my($self, $input) = @_;
164 my $status = 1;
166 ## Reset the files_written hash array between processing each file
167 $self->{'files_written'} = {};
168 $self->{'real_fwritten'} = [];
170 ## Allow subclasses to reset values before
171 ## each call to generate().
172 $self->reset_values();
174 ## Read the global configuration file
175 if (!$self->{'global_read'}) {
176 $status = $self->read_global_configuration();
177 $self->{'global_read'} = 1;
180 if ($status) {
181 $self->{'current_input'} = $input;
183 ## An empty input file name says that we
184 ## should generate a default input file and use that
185 if ($input eq '') {
186 $status = $self->generate_default_input();
188 else {
189 $status = $self->parse_file($input);
193 return $status;
197 # split an inheritance list like ": a,b, c" into components
198 sub parse_parents {
199 my($parents, $errorStringRef, $statusRef) = @_;
200 if (defined $parents) {
201 $parents =~ s/^:\s*//;
202 $parents =~ s/\s+$//;
203 my @parents = split(/\s*,\s*/, $parents);
204 if (!defined $parents[0]) {
205 ## The : was used, but no parents followed. This
206 ## is an error.
207 $$errorStringRef = 'No parents listed';
208 $$statusRef = 0;
210 return \@parents;
212 return undef;
216 sub parse_known {
217 my($self, $line) = @_;
218 my $status = 1;
219 my $errorString;
220 my $type = $self->{'grammar_type'};
221 my @values;
224 ## Each regexp that looks for the '{' looks for it at the
225 ## end of the line. It is purposely this way to decrease
226 ## the amount of extra lines in each file. This
227 ## allows for the most compact file as human readably
228 ## possible.
230 if ($line eq '') {
232 elsif ($line =~ /^$type\s*(\([^\)]+\))?\s*(:.*)?\s*{$/) {
233 my $name = $1;
234 my $parents = $2;
235 if ($self->{$self->{'type_check'}}) {
236 $errorString = "Did not find the end of the $type";
237 $status = 0;
239 else {
240 $parents = parse_parents($parents, \$errorString, \$status);
241 push(@values, $type, $name, $parents);
244 elsif ($line =~ /^}$/) {
245 if ($self->{$self->{'type_check'}}) {
246 push(@values, $type, $line);
248 else {
249 $errorString = "Did not find the beginning of the $type";
250 $status = 0;
253 elsif ($line =~ /^(feature)\s*\(([^\)]+)\)\s*(:.*)?\s*{$/) {
254 my $type = $1;
255 my $name = $2;
256 my $parents = $3;
257 my @names = split(/\s*,\s*/, $name);
258 $parents = parse_parents($parents, \$errorString, \$status);
259 push(@values, $type, \@names, $parents);
261 elsif (!$self->{$self->{'type_check'}}) {
262 $errorString = "No $type was defined";
263 $status = 0;
265 elsif ($self->parse_assignment($line, \@values)) {
266 ## If this returns true, then we've found an assignment
268 elsif ($line =~ /^(\w+)\s*(\([^\)]+\))?\s*(:.*)?\s*{$/) {
269 my $comp = lc($1);
270 my $name = $2;
271 my $parents = $3;
273 if (defined $name) {
274 $name =~ s/^\(\s*//;
275 $name =~ s/\s*\)$//;
277 else {
278 $name = $self->get_default_component_name();
280 $parents = parse_parents($parents, \$errorString, \$status);
281 push(@values, 'component', $comp, $name, $parents);
283 else {
284 $errorString = "Unrecognized line: $line";
285 $status = -1;
288 return $status, $errorString, @values;
292 sub parse_scope {
293 my($self, $fh, $name, $type, $validNames, $flags, $elseflags) = @_;
294 my $status = 0;
295 my $errorString = "Unable to process $name";
297 ## Make sure $flags has a hash map reference
298 $flags = {} if (!defined $flags);
300 while(<$fh>) {
301 my $line = $self->preprocess_line($fh, $_);
303 if ($line eq '') {
305 elsif ($line =~ /^}$/) {
306 ($status, $errorString) = $self->handle_scoped_end($type, $flags);
307 last;
309 elsif ($line =~ /^}\s*else\s*{$/) {
310 if (defined $elseflags) {
311 ## From here on out anything after this goes into the $elseflags
312 $flags = $elseflags;
313 $elseflags = undef;
315 ## We need to adjust the type also. If there was a type
316 ## then the first part of the clause was used. If there was
317 ## no type, then the first part was ignored and the second
318 ## part will be used.
319 if (defined $type) {
320 $type = undef;
322 else {
323 $type = $self->get_default_component_name();
326 else {
327 $status = 0;
328 $errorString = 'An else is not allowed in this context';
329 last;
332 else {
333 my @values;
334 if (defined $validNames && $self->parse_assignment($line, \@values)) {
335 if (defined $$validNames{$values[1]}) {
336 ## If $type is not defined, we don't even need to bother with
337 ## processing the assignment as we will be throwing the value
338 ## away anyway.
339 if (defined $type) {
340 if ($values[0] == 0) {
341 $self->process_assignment($values[1], $values[2], $flags);
343 elsif ($values[0] == 1) {
344 $self->process_assignment_add($values[1], $values[2], $flags);
346 elsif ($values[0] == -1) {
347 $self->process_assignment_sub($values[1], $values[2], $flags);
351 else {
352 ($status,
353 $errorString) = $self->handle_unknown_assignment($type,
354 @values);
355 last if (!$status);
358 else {
359 ($status, $errorString) = $self->handle_scoped_unknown($fh,
360 $type,
361 $flags,
362 $line);
363 last if (!$status);
367 return $status, $errorString;
371 sub base_directory {
372 my $self = shift;
373 return $self->mpc_basename($self->getcwd());
377 sub generate_default_file_list {
378 my($self, $dir, $exclude, $fileexc, $recurse) = @_;
379 my $dh = new FileHandle();
380 my @files;
382 if (opendir($dh, $dir)) {
383 my $prefix = ($dir ne '.' ? "$dir/" : '');
384 my $have_exc = (defined $$exclude[0]);
385 my $skip = 0;
386 foreach my $file (grep(!/^\.\.?$/,
387 ($onVMS ? map {$_ =~ s/\.dir$//; $_} readdir($dh) :
388 readdir($dh)))) {
389 ## Prefix each file name with the directory only if it's not '.'
390 my $full = $prefix . $file;
392 if ($have_exc) {
393 foreach my $exc (@$exclude) {
394 if ($full eq $exc) {
395 $skip = 1;
396 last;
401 if ($skip) {
402 $skip = 0;
403 $$fileexc = 1 if (defined $fileexc);
405 else {
406 if ($recurse && -d $full) {
407 push(@files,
408 $self->generate_default_file_list($full, $exclude,
409 $fileexc, $recurse));
411 else {
412 # Strip out ^ symbols
413 $full =~ s/\^//g if ($onVMS);
415 push(@files, $full);
420 if ($self->sort_files()) {
421 @files = sort { $self->file_sorter($a, $b) } @files;
424 closedir($dh);
426 return @files;
430 sub transform_file_name {
431 my($self, $name) = @_;
433 $name =~ s/[\s\-]/_/g;
434 return $name;
438 sub file_written {
439 my($self, $file) = @_;
440 return (defined $all_written{$self->getcwd() . '/' . $file});
444 sub add_file_written {
445 my($self, $file) = @_;
446 my $key = lc($file);
448 if (defined $self->{'files_written'}->{$key}) {
449 $self->warning("$self->{'grammar_type'} $file " .
450 ($self->{'case_tolerant'} ?
451 "has been overwritten." :
452 "of differing case has been processed."));
454 else {
455 $self->{'files_written'}->{$key} = $file;
456 push(@{$self->{'real_fwritten'}}, $file);
459 $all_written{$self->getcwd() . '/' . $file} = 1;
463 sub extension_recursive_input_list {
464 my($self, $dir, $exclude, $ext) = @_;
465 my $fh = new FileHandle();
466 my @files;
468 if (opendir($fh, $dir)) {
469 my $prefix = ($dir ne '.' ? "$dir/" : '');
470 my $skip = 0;
471 foreach my $file (grep(!/^\.\.?$/,
472 ($onVMS ? map {$_ =~ s/\.dir$//; $_} readdir($fh) :
473 readdir($fh)))) {
474 my $full = $prefix . $file;
476 ## Check for command line exclusions
477 if (defined $$exclude[0]) {
478 foreach my $exc (@$exclude) {
479 if ($full eq $exc) {
480 $skip = 1;
481 last;
486 ## If we are not skipping this directory or file, then check it out
487 if ($skip) {
488 $skip = 0;
490 else {
491 if (-d $full) {
492 push(@files, $self->extension_recursive_input_list($full,
493 $exclude,
494 $ext));
496 elsif ($full =~ /$ext$/) {
497 push(@files, $full);
501 closedir($fh);
504 return @files;
507 sub recursive_directory_list {
508 my($self, $dir, $exclude) = @_;
509 my $directories = '';
510 my $fh = new FileHandle();
512 if (opendir($fh, $dir)) {
513 my $prefix = ($dir ne '.' ? "$dir/" : '');
514 my $skip = 0;
515 if (defined $$exclude[0]) {
516 foreach my $exc (@$exclude) {
517 if ($dir eq $exc) {
518 $skip = 1;
519 last;
523 if ($skip) {
524 $skip = 0;
526 else {
527 $directories .= ' ' . $dir;
530 foreach my $file (grep(!/^\.\.?$/,
531 ($onVMS ? map {$_ =~ s/\.dir$//; $_} readdir($fh) :
532 readdir($fh)))) {
533 my $full = $prefix . $file;
535 if ($file eq '.svn' || $file eq 'CVS') {
536 $skip = 1;
538 else {
539 ## Check for command line exclusions
540 if (defined $$exclude[0]) {
541 foreach my $exc (@$exclude) {
542 if ($full eq $exc) {
543 $skip = 1;
544 last;
550 ## If we are not skipping this directory or file, then check it out
551 if ($skip) {
552 $skip = 0;
554 else {
555 if (-d $full) {
556 $directories .= $self->recursive_directory_list($full, $exclude);
560 closedir($fh);
563 return $directories;
567 sub modify_assignment_value {
568 my($self, $name, $value) = @_;
570 if ($self->{'convert_slashes'} &&
571 index($name, 'flags') == -1 && !defined $non_convert{$name}) {
572 $value =~ s/\//\\/g;
575 return $value;
579 sub get_assignment_hash {
580 ## NOTE: If anything in this block changes, then you must make the
581 ## same change in process_assignment.
582 my $self = shift;
583 return $self->{$self->{'reading_global'} ? $gassign_key : $assign_key};
587 sub process_assignment {
588 my($self, $name, $value, $assign) = @_;
590 ## If no hash table was passed in
591 if (!defined $assign) {
592 ## NOTE: If anything in this block changes, then you must make the
593 ## same change in get_assignment_hash.
594 $assign = $self->{$self->{'reading_global'} ?
595 $gassign_key : $assign_key};
598 if (defined $value) {
599 $value =~ s/^\s+//;
600 $value =~ s/\s+$//;
602 ## Modify the assignment value before saving it
603 $$assign{$name} = $self->modify_assignment_value($name, $value);
605 else {
606 $$assign{$name} = undef;
611 sub addition_core {
612 my($self, $name, $value, $nval, $assign) = @_;
614 if (defined $nval) {
615 if ($self->preserve_assignment_order($name)) {
616 $nval .= " $value";
618 else {
619 $nval = "$value $nval";
622 else {
623 $nval = $value;
625 $self->process_assignment($name, $nval, $assign, 1);
629 sub process_assignment_add {
630 my($self, $name, $value, $assign) = @_;
631 my $nval = $self->get_assignment_for_modification($name, $assign);
633 ## Remove all duplicate parts from the value to be added.
634 ## Whether anything gets removed or not is up to the implementation
635 ## of the sub classes.
636 $value = $self->remove_duplicate_addition($name, $value, $nval);
638 ## If there is anything to add, then do so
639 $self->addition_core($name, $value, $nval, $assign) if ($value ne '');
643 sub subtraction_core {
644 my($self, $name, $value, $nval, $assign) = @_;
646 if (defined $nval) {
647 my $last = 1;
648 my $found;
650 ## Escape any regular expression special characters
651 $value = $self->escape_regex_special($value);
653 ## If necessary, split the value into an array
654 my $elements = ($value =~ /\s/ ? $self->create_array($value) : [$value]);
655 for(my $i = 0; $i <= $last; $i++) {
656 if ($i == $last) {
657 ## If we did not find the string to subtract in the original
658 ## value, try again after expanding template variables for
659 ## subtraction.
660 $nval = $self->get_assignment_for_modification($name, $assign, 1);
662 for(my $j = 0; $j <= $last; $j++) {
663 ## Try to remove each individual element and then set the new
664 ## value if any of the elements were found in the original value
665 foreach my $elem (@$elements) {
666 ## First try with quotes, then try again without them
667 my $re = ($j == 0 ? '"' . $elem . '"' : $elem);
669 if ($nval =~ s/\s+$re\s+/ / || $nval =~ s/\s+$re$// ||
670 $nval =~ s/^$re\s+// || $nval =~ s/^$re$//) {
671 $found = 1;
674 if ($found) {
675 $self->process_assignment($name, $nval, $assign, -1);
676 last;
679 last if ($found);
685 sub process_assignment_sub {
686 my($self, $name, $value, $assign) = @_;
687 my $nval = $self->get_assignment_for_modification($name, $assign);
689 ## Remove double quotes if there are any
690 $value =~ s/^\"(.*)\"$/$1/;
692 ## Call to the core function to perform the subtraction. We must also
693 ## pass the value through the assignment modifier to ensure that
694 ## slashes are in the project native format.
695 $self->subtraction_core($name,
696 $self->modify_assignment_value($name, $value),
697 $nval, $assign);
701 sub fill_type_name {
702 my($self, $names, $def) = @_;
703 my $array = ($names =~ /\s/ ? $self->create_array($names) : [$names]);
705 $names = '';
706 foreach my $name (@$array) {
707 if ($name =~ /\*/) {
708 my $pre = $def . '_';
709 my $mid = '_' . $def . '_';
710 my $post = '_' . $def;
712 ## Replace the beginning and end first then the middle
713 $name =~ s/^\*/$pre/;
714 $name =~ s/\*$/$post/;
715 $name =~ s/\*/$mid/g;
717 ## Remove any trailing underscore or any underscore that is followed
718 ## by a space. This value could be a space separated list.
719 $name =~ s/_$//;
720 $name =~ s/_\s/ /g;
721 $name =~ s/\s_/ /g;
723 ## If any one word is capitalized then capitalize each word
724 if ($name =~ /[A-Z][0-9a-z_]+/) {
725 ## Do the first word
726 if ($name =~ /^([a-z])([^_]+)/) {
727 my $first = uc($1);
728 my $rest = $2;
729 $name =~ s/^[a-z][^_]+/$first$rest/;
731 ## Do subsequent words
732 while($name =~ /(_[a-z])([^_]+)/) {
733 my $first = uc($1);
734 my $rest = $2;
735 $name =~ s/_[a-z][^_]+/$first$rest/;
740 $names .= $name . ' ';
742 $names =~ s/\s+$//;
744 return $names;
748 sub clone {
749 my($self, $obj) = @_;
751 ## Check for various types of data. Those that are not found to be
752 ## types that we need to deep copy are just assigned to new values.
753 ## All others are copied by recursively calling this method.
754 if (UNIVERSAL::isa($obj, 'HASH')) {
755 my $new = {};
756 foreach my $key (keys %$obj) {
757 $$new{$key} = $self->clone($$obj{$key});
759 return $new;
761 elsif (UNIVERSAL::isa($obj, 'ARRAY')) {
762 my $new = [];
763 foreach my $o (@$obj) {
764 push(@$new, $self->clone($o));
766 return $new;
769 return $obj;
773 sub save_state {
774 my($self, $selected) = @_;
775 my %state;
777 ## Make a deep copy of each state value. That way our array
778 ## references and hash references do not get accidentally modified.
779 foreach my $skey (defined $selected ? $selected : @statekeys) {
780 if (defined $self->{$skey}) {
781 ## It is necessary to clone each value so that nested complex data
782 ## types do not get unknowingly modified.
783 $state{$skey} = $self->clone($self->{$skey});
787 return %state;
791 sub restore_state {
792 my($self, $state, $selected) = @_;
794 ## Make a deep copy of each state value. That way our array
795 ## references and hash references do not get accidentally modified.
796 ## It's not necessary to do a recursive deep copy (i.e., use the
797 ## clone() method) because the value coming in will now be owned by
798 ## this object and will not be modified unknowingly.
799 foreach my $skey (defined $selected ? $selected : @statekeys) {
800 my $old = $self->{$skey};
801 if (defined $state->{$skey} &&
802 UNIVERSAL::isa($state->{$skey}, 'ARRAY')) {
803 my @arr = @{$state->{$skey}};
804 $self->{$skey} = \@arr;
806 elsif (defined $state->{$skey} &&
807 UNIVERSAL::isa($state->{$skey}, 'HASH')) {
808 my %hash = %{$state->{$skey}};
809 $self->{$skey} = \%hash;
811 else {
812 $self->{$skey} = $state->{$skey};
814 $self->restore_state_helper($skey, $old, $self->{$skey});
819 sub get_global_cfg {
820 return $_[0]->{'global'};
824 sub get_template_override {
825 return $_[0]->{'template'};
829 sub get_ti_override {
830 return $_[0]->{'ti'};
834 sub get_relative {
835 return $_[0]->{'relative'};
839 sub get_progress_callback {
840 return $_[0]->{'progress'};
844 sub get_addtemp {
845 return $_[0]->{'addtemp'};
849 sub get_addproj {
850 return $_[0]->{'addproj'};
854 sub get_toplevel {
855 return $_[0]->{'toplevel'};
859 sub get_into {
860 return $_[0]->{'into'};
864 sub get_use_env {
865 return $_[0]->{'use_env'};
869 sub get_expand_vars {
870 return $_[0]->{'expand_vars'};
874 sub get_files_written {
875 return $_[0]->{'real_fwritten'};
879 sub get_assignment {
880 my $self = shift;
881 my $name = $self->resolve_alias(shift);
882 my $assign = shift;
884 ## If no hash table was passed in
885 if (!defined $assign) {
886 $assign = $self->{$self->{'reading_global'} ?
887 $gassign_key : $assign_key};
890 return $$assign{$name};
894 sub get_assignment_for_modification {
895 my($self, $name, $assign, $subtraction) = @_;
896 return $self->get_assignment($name, $assign);
900 sub get_baseprojs {
901 return $_[0]->{'baseprojs'};
905 sub get_dynamic {
906 return $_[0]->{'dynamic'};
910 sub get_static {
911 return $_[0]->{'static'};
915 sub get_default_component_name {
916 #my $self = shift;
917 return 'default';
921 sub get_features {
922 return $_[0]->{'features'};
926 sub get_hierarchy {
927 return $_[0]->{'hierarchy'};
931 sub get_name_modifier {
932 return $_[0]->{'name_modifier'};
936 sub get_apply_project {
937 return $_[0]->{'apply_project'};
941 sub get_language {
942 return $_[0]->{'language'};
946 sub get_outdir {
947 my $self = shift;
948 if (defined $self->{'into'}) {
949 my $outdir = $self->getcwd();
950 my $re = $self->escape_regex_special($self->getstartdir());
952 $outdir =~ s/^$re//;
953 return $self->{'into'} . $outdir;
955 else {
956 return '.';
961 sub expand_variables {
962 my($self, $value, $rel, $expand_template, $scope, $expand, $warn) = @_;
963 my $cwd = $self->getcwd();
964 my $start = 0;
965 my $forward_slashes = $self->{'convert_slashes'} ||
966 $self->{'requires_forward_slashes'};
968 ## Fix up the value for Windows switch the \\'s to /
969 $cwd =~ s/\\/\//g if ($forward_slashes);
971 while(substr($value, $start) =~ /(\$\(([^)]+)\))/) {
972 my $whole = $1;
973 my $name = $2;
974 if (defined $$rel{$name}) {
975 my $val = $$rel{$name};
976 if ($expand) {
977 $val =~ s/\//\\/g if ($forward_slashes);
978 substr($value, $start) =~ s/\$\([^)]+\)/$val/;
979 $whole = $val;
981 else {
982 ## Fix up the value for Windows switch the \\'s to /
983 $val =~ s/\\/\//g if ($forward_slashes);
985 my $icwd = ($self->{'case_tolerant'} ? lc($cwd) : $cwd);
986 my $ival = ($self->{'case_tolerant'} ? lc($val) : $val);
987 my $iclen = length($icwd);
988 my $ivlen = length($ival);
990 ## If the relative value contains the current working
991 ## directory plus additional subdirectories, we must pull
992 ## off the additional directories into a temporary where
993 ## it can be put back after the relative replacement is done.
994 my $append;
995 if (index($ival, $icwd) == 0 && $iclen != $ivlen &&
996 substr($ival, $iclen, 1) eq '/') {
997 my $diff = $ivlen - $iclen;
998 $append = substr($ival, $iclen);
999 substr($ival, $iclen, $diff) = '';
1000 $ivlen -= $diff;
1003 if (index($icwd, $ival) == 0 &&
1004 ($iclen == $ivlen || substr($icwd, $ivlen, 1) eq '/')) {
1005 my $current = $icwd;
1006 substr($current, 0, $ivlen) = '';
1008 my $dircount = ($current =~ tr/\///);
1009 if ($dircount == 0) {
1010 $ival = '.';
1012 else {
1013 $ival = '../' x $dircount;
1014 $ival =~ s/\/$//;
1016 $ival .= $append if (defined $append);
1018 ## We have to remove the leading ./ if there is one.
1019 ## Otherwise, if this value is used as an exclude value it will
1020 ## not match up correctly.
1021 $ival =~ s!^\./!!;
1023 ## Convert the slashes if necessary
1024 $ival =~ s/\//\\/g if ($self->{'convert_slashes'});
1025 substr($value, $start) =~ s/\$\([^)]+\)/$ival/;
1026 $whole = $ival;
1028 elsif ($self->convert_all_variables() && $warn) {
1029 ## The user did not choose to expand $() variables directly,
1030 ## but we could not convert it into a relative path. So,
1031 ## instead of leaving it we will expand it. But, we will only
1032 ## get into this section if this is the secondary attempt to
1033 ## replace the variable (indicated by the $warn boolean).
1034 $val =~ s/\//\\/g if ($self->{'convert_slashes'});
1035 substr($value, $start) =~ s/\$\([^)]+\)/$val/;
1036 $whole = $val;
1038 else {
1039 my $loc = index(substr($value, $start), $whole);
1040 $start += $loc if ($loc > 0);
1044 elsif ($expand_template ||
1045 $self->expand_variables_from_template_values()) {
1046 my $ti = $self->get_template_input();
1047 my $val = (defined $ti ? $ti->get_value($name) : undef);
1048 my $sname = (defined $scope ? $scope . "::$name" : undef);
1049 my $arr = $self->adjust_value([$sname, $name],
1050 (defined $val ? $val : []));
1051 if (UNIVERSAL::isa($arr, 'HASH')) {
1052 $self->warning("$name conflicts with a template variable scope");
1054 elsif (UNIVERSAL::isa($arr, 'ARRAY') && defined $$arr[0]) {
1055 $val = $self->modify_assignment_value(lc($name), "@$arr");
1056 substr($value, $start) =~ s/\$\([^)]+\)/$val/;
1058 ## We have replaced the template value, but that template
1059 ## value may contain a $() construct that may need to get
1060 ## replaced too. However, if the name of the template variable
1061 ## is the same as the original $() variable name, we need to
1062 ## leave it alone to avoid looping infinitely.
1063 $whole = '' if ($whole ne $val);
1065 else {
1066 $self->warning("Unable to expand $name.") if ($expand && $warn);
1067 my $loc = index(substr($value, $start), $whole);
1068 $start += $loc if ($loc > 0);
1071 elsif ($self->convert_all_variables() && $warn) {
1072 ## We could not find a value to correspond to the variable name.
1073 ## Instead of leaving it we will expand it. But, we will only
1074 ## get into this section if this is the secondary attempt to
1075 ## replace the variable (indicated by the $warn boolean).
1076 substr($value, $start) =~ s/\$\([^)]+\)//;
1077 $whole = '';
1079 else {
1080 my $loc = index(substr($value, $start), $whole);
1081 $start += $loc if ($loc > 0);
1083 $start += length($whole);
1086 $value =~ s/\\/\//g if ($self->{'requires_forward_slashes'});
1088 return $value;
1092 sub replace_env_vars {
1093 my($self, $lref) = @_;
1094 my $one_empty = undef;
1096 ## Loop through the string until we find no more environment variables.
1097 while($$lref =~ /\$(\w+)/) {
1098 my $name = $1;
1099 my $val = '';
1101 ## PWD is a special variable. It isn't set on Windows, but in MPC we
1102 ## must guarantee that it is always there.
1103 if ($name eq 'PWD') {
1104 $val = $self->getcwd();
1106 elsif (defined $ENV{$name}) {
1107 $val = $ENV{$name};
1109 else {
1110 ## Keep track of an environment variable not being set.
1111 $one_empty = 1;
1113 $$lref =~ s/\$\w+/$val/;
1115 return $one_empty;
1119 sub relative {
1120 my($self, $value, $expand_template, $scope) = @_;
1122 if (defined $value) {
1123 if (UNIVERSAL::isa($value, 'ARRAY')) {
1124 my @built;
1125 foreach my $val (@$value) {
1126 my $rel = $self->relative($val, $expand_template, $scope);
1127 if (UNIVERSAL::isa($rel, 'ARRAY')) {
1128 push(@built, @$rel);
1130 else {
1131 push(@built, $rel);
1134 return \@built;
1136 elsif (index($value, '$') >= 0) {
1137 ## A form of this code lives in
1138 ## ProjectCreator::create_recursive_settings. If you are changing
1139 ## something in this area, please look at the method in
1140 ## ProjectCreator.pm to see if it needs changing too.
1142 my $ovalue = $value;
1143 my($rel, $how) = $self->get_initial_relative_values();
1144 $value = $self->expand_variables($value, $rel,
1145 $expand_template, $scope, $how);
1147 if ($ovalue eq $value || index($value, '$') >= 0) {
1148 ($rel, $how) = $self->get_secondary_relative_values();
1149 $value = $self->expand_variables($value, $rel,
1150 $expand_template, $scope,
1151 $how, 1);
1156 ## Values that have two or more strings enclosed in double quotes are
1157 ## to be interpreted as elements of an array
1158 if (defined $value && $value =~ /^"[^"]+"(\s+"[^"]+")+$/) {
1159 $value = $self->create_array($value);
1162 return $value;
1166 ## Static function. Returns the default language for MPC.
1167 sub defaultLanguage {
1168 return $deflang;
1172 ## Static function. Returns an array of valid languages.
1173 sub validLanguages {
1174 return keys %languages;
1178 ## Static function. The one and only argument is the language
1179 ## string to check for validity.
1180 sub isValidLanguage {
1181 return defined $languages{$_[0]};
1185 sub languageIs {
1186 #my($self, $language) = @_;
1187 return $_[0]->{'language'} eq $_[1];
1190 # ************************************************************
1191 # Virtual Methods To Be Overridden
1192 # ************************************************************
1194 sub restore_state_helper {
1195 #my $self = shift;
1196 #my $skey = shift;
1197 #my $old = shift;
1198 #my $new = shift;
1202 sub get_initial_relative_values {
1203 #my $self = shift;
1204 return {}, 0;
1208 sub get_secondary_relative_values {
1209 my $self = shift;
1210 return ($self->{'use_env'} ? \%ENV :
1211 $self->{'relative'}), $self->{'expand_vars'};
1215 sub convert_all_variables {
1216 #my $self = shift;
1217 return 0;
1221 sub expand_variables_from_template_values {
1222 #my $self = shift;
1223 return 0;
1227 sub preserve_assignment_order {
1228 #my $self = shift;
1229 #my $name = shift;
1230 return 1;
1234 sub compare_output {
1235 #my $self = shift;
1236 return 0;
1240 sub files_are_different {
1241 my($self, $old, $new) = @_;
1242 return !(-r $old && -s $new == -s $old && compare($new, $old) == 0);
1246 sub handle_scoped_end {
1247 #my $self = shift;
1248 #my $type = shift;
1249 #my $flags = shift;
1250 return 1, undef;
1253 sub handle_unknown_assignment {
1254 my $self = shift;
1255 my $type = shift;
1256 my @values = @_;
1257 return 0, "Invalid assignment name: '$values[1]'";
1261 sub handle_scoped_unknown {
1262 my($self, $fh, $type, $flags, $line) = @_;
1263 return 0, "Unrecognized line: $line";
1267 sub remove_duplicate_addition {
1268 my($self, $name, $value, $current) = @_;
1269 return $value;
1273 sub generate_recursive_input_list {
1274 #my $self = shift;
1275 #my $dir = shift;
1276 #my $exclude = shift;
1277 return ();
1281 sub reset_values {
1282 #my $self = shift;
1286 sub sort_files {
1287 #my $self = shift;
1288 return 1;
1292 sub file_sorter {
1293 #my $self = shift;
1294 #my $left = shift;
1295 #my $right = shift;
1296 return $_[1] cmp $_[2];
1300 sub read_global_configuration {
1301 #my $self = shift;
1302 #my $input = shift;
1303 return 1;
1307 sub set_verbose_ordering {
1308 #my $self = shift;
1309 #my $value = shift;
1313 sub get_properties {
1314 my $self = shift;
1316 ## Create the property hash map with the language property
1317 my %props = ($self->get_language() => 1);
1319 ## Set the 'static' property only if the project is static
1320 $props{'static'} = 1 if ($self->get_static());
1322 return \%props;