Test zlib on linux
[MPC.git] / modules / Creator.pm
blob1d70dabedca9eec05dab6fd5a0fede75d08d9af1
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 ## NOTE: We call the constant as a function to support Perl 5.6.
40 my %languages = (cplusplus() => 1,
41 csharp() => 1,
42 java() => 1,
43 vb() => 1,
46 my $assign_key = 'assign';
47 my $gassign_key = 'global_assign';
48 my %non_convert = ('prebuild' => 1,
49 'postbuild' => 1,
50 'postclean' => 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',
60 my %all_written;
61 my $onVMS = DirectoryManager::onVMS();
63 # ************************************************************
64 # Subroutine Section
65 # ************************************************************
67 sub new {
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;
80 $self->{'ti'} = $ti;
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();
111 return $self;
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);
123 return $line;
127 sub generate_default_input {
128 my $self = shift;
129 my($status,
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);
138 return $status;
142 sub parse_file {
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);
151 if (!$status) {
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() . ':');
161 $status = 0;
163 $self->set_line_number($oline);
165 return $status;
169 sub generate {
170 my($self, $input) = @_;
171 my $status = 1;
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;
187 if ($status) {
188 $self->{'current_input'} = $input;
190 ## An empty input file name says that we
191 ## should generate a default input file and use that
192 if ($input eq '') {
193 $status = $self->generate_default_input();
195 else {
196 $status = $self->parse_file($input);
200 return $status;
203 # split an inheritance list like ": a,b, c" into components
204 sub parse_parents {
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
212 ## is an error.
213 $$errorStringRef = 'No parents listed';
214 $$statusRef = 0;
216 return \@parents;
218 return undef;
222 sub parse_known {
223 my($self, $line, $fh) = @_;
224 my $status = 1;
225 my $errorString;
226 my $type = $self->{'grammar_type'};
227 my @values;
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
234 ## possible.
236 if ($line eq '') {
238 elsif ($line =~ /^$type\s*(\([^\)]+\))?\s*(:.*)?\s*{$/) {
239 my $name = $1;
240 my $parents = $2;
241 if ($self->{$self->{'type_check'}}) {
242 $errorString = "Did not find the end of the $type";
243 $status = 0;
245 else {
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);
254 else {
255 $errorString = "Did not find the beginning of the $type";
256 $status = 0;
259 elsif ($line =~ /^(feature)\s*\(([^\)]+)\)\s*(:.*)?\s*{$/) {
260 my $type = $1;
261 my $name = $2;
262 my $parents = $3;
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";
269 $status = 0;
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*{$/) {
275 my $comp = lc($1);
276 my $name = $2;
277 my $parents = $3;
279 if (defined $name) {
280 $name =~ s/^\(\s*//;
281 $name =~ s/\s*\)$//;
283 else {
284 $name = $self->get_default_component_name();
286 $parents = parse_parents($parents, \$errorString, \$status);
287 push(@values, 'component', $comp, $name, $parents);
289 else {
290 $errorString = "Unrecognized line: $line";
291 $status = -1;
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] {
304 ## This spans
305 ## multiple lines
306 ## }
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*{$/) {
314 my $comp = lc($1);
315 my $op = $2;
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;
320 while(<$fh>) {
321 ## This is not an error,
322 ## this is the end of the bracketed assignment.
323 last if ($_ =~ /^\s*}\s*$/);
325 ## Strip comments.
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);
343 sub parse_scope {
344 my($self, $fh, $name, $type, $validNames, $flags, $elseflags) = @_;
345 my $status = 0;
346 my $errorString = "Unable to process $name";
348 ## Make sure $flags has a hash map reference
349 $flags = {} if (!defined $flags);
351 while(<$fh>) {
352 my $line = $self->preprocess_line($fh, $_);
354 if ($line eq '') {
356 elsif ($line =~ /^}$/) {
357 ($status, $errorString) = $self->handle_scoped_end($type, $flags);
358 last;
360 elsif ($line =~ /^}\s*else\s*{$/) {
361 if (defined $elseflags) {
362 ## From here on out anything after this goes into the $elseflags
363 $flags = $elseflags;
364 $elseflags = undef;
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.
370 if (defined $type) {
371 $type = undef;
373 else {
374 $type = $self->get_default_component_name();
377 else {
378 $status = 0;
379 $errorString = 'An else is not allowed in this context';
380 last;
383 else {
384 my @values;
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
389 ## away anyway.
390 if (defined $type) {
391 $self->process_any_assignment($flags, @values);
394 else {
395 ($status,
396 $errorString) = $self->handle_unknown_assignment($type,
397 @values);
398 last if (!$status);
401 else {
402 ($status, $errorString) = $self->handle_scoped_unknown($fh,
403 $type,
404 $flags,
405 $line);
406 last if (!$status);
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);
429 sub base_directory {
430 my $self = shift;
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();
438 my @files;
440 if (opendir($dh, $dir)) {
441 my $prefix = ($dir ne '.' ? "$dir/" : '');
442 my $have_exc = (defined $$exclude[0]);
443 my $skip = 0;
444 foreach my $file (grep(!/^\.\.?$/,
445 ($onVMS ? map {$_ =~ s/\.dir$//; $_} readdir($dh) :
446 readdir($dh)))) {
447 ## Prefix each file name with the directory only if it's not '.'
448 my $full = $prefix . $file;
450 if ($have_exc) {
451 foreach my $exc (@$exclude) {
452 if ($full eq $exc) {
453 $skip = 1;
454 last;
459 if ($skip) {
460 $skip = 0;
461 $$fileexc = 1 if (defined $fileexc);
463 else {
464 if ($recurse && -d $full) {
465 push(@files,
466 $self->generate_default_file_list($full, $exclude,
467 $fileexc, $recurse));
469 else {
470 # Strip out ^ symbols
471 $full =~ s/\^//g if ($onVMS);
473 push(@files, $full);
478 if ($self->sort_files()) {
479 @files = sort { $self->file_sorter($a, $b) } @files;
482 closedir($dh);
484 return @files;
488 sub transform_file_name {
489 my($self, $name) = @_;
491 $name =~ s/[\s\-]/_/g;
492 return $name;
496 sub file_written {
497 my($self, $file) = @_;
498 return (defined $all_written{$self->getcwd() . '/' . $file});
502 sub add_file_written {
503 my($self, $file) = @_;
504 my $key = lc($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."));
512 else {
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();
524 my @files;
526 if (opendir($fh, $dir)) {
527 my $prefix = ($dir ne '.' ? "$dir/" : '');
528 my $skip = 0;
529 foreach my $file (grep(!/^\.\.?$/,
530 ($onVMS ? map {$_ =~ s/\.dir$//; $_} readdir($fh) :
531 readdir($fh)))) {
532 my $full = $prefix . $file;
534 ## Check for command line exclusions
535 if (defined $$exclude[0]) {
536 foreach my $exc (@$exclude) {
537 if ($full eq $exc) {
538 $skip = 1;
539 last;
544 ## If we are not skipping this directory or file, then check it out
545 if ($skip) {
546 $skip = 0;
548 else {
549 if (-d $full) {
550 push(@files, $self->extension_recursive_input_list($full,
551 $exclude,
552 $ext));
554 elsif ($full =~ /$ext$/) {
555 push(@files, $full);
559 closedir($fh);
562 return @files;
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/" : '');
572 my $skip = 0;
573 if (defined $$exclude[0]) {
574 foreach my $exc (@$exclude) {
575 if ($dir eq $exc) {
576 $skip = 1;
577 last;
581 if ($skip) {
582 $skip = 0;
584 else {
585 $directories .= ' ' . $dir;
588 foreach my $file (grep(!/^\.\.?$/,
589 ($onVMS ? map {$_ =~ s/\.dir$//; $_} readdir($fh) :
590 readdir($fh)))) {
591 my $full = $prefix . $file;
593 if ($file eq '.svn' || $file eq 'CVS') {
594 $skip = 1;
596 else {
597 ## Check for command line exclusions
598 if (defined $$exclude[0]) {
599 foreach my $exc (@$exclude) {
600 if ($full eq $exc) {
601 $skip = 1;
602 last;
608 ## If we are not skipping this directory or file, then check it out
609 if ($skip) {
610 $skip = 0;
612 else {
613 if (-d $full) {
614 $directories .= $self->recursive_directory_list($full, $exclude);
618 closedir($fh);
621 return $directories;
625 sub modify_assignment_value {
626 my($self, $name, $value) = @_;
628 if ($self->{'convert_slashes'} &&
629 index($name, 'flags') == -1 && !defined $non_convert{$name}) {
630 $value =~ s/\//\\/g;
633 return $value;
637 sub get_assignment_hash {
638 ## NOTE: If anything in this block changes, then you must make the
639 ## same change in process_assignment.
640 my $self = shift;
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) {
657 $value =~ s/^\s+//;
658 $value =~ s/\s+$//;
660 ## Modify the assignment value before saving it
661 $$assign{$name} = $self->modify_assignment_value($name, $value);
663 else {
664 $$assign{$name} = undef;
669 sub addition_core {
670 my($self, $name, $value, $nval, $assign) = @_;
672 if (defined $nval) {
673 if ($self->preserve_assignment_order($name)) {
674 $nval .= " $value";
676 else {
677 $nval = "$value $nval";
680 else {
681 $nval = $value;
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) = @_;
704 if (defined $nval) {
705 my $last = 1;
706 my $found;
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++) {
714 if ($i == $last) {
715 ## If we did not find the string to subtract in the original
716 ## value, try again after expanding template variables for
717 ## subtraction.
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$//) {
729 $found = 1;
732 if ($found) {
733 $self->process_assignment($name, $nval, $assign, -1);
734 last;
737 last if ($found);
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),
755 $nval, $assign);
759 sub fill_type_name {
760 my($self, $names, $def) = @_;
761 my $array = ($names =~ /\s/ ? $self->create_array($names) : [$names]);
763 $names = '';
764 foreach my $name (@$array) {
765 if ($name =~ /\*/) {
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.
777 $name =~ s/_$//;
778 $name =~ s/_\s/ /g;
779 $name =~ s/\s_/ /g;
781 ## If any one word is capitalized then capitalize each word
782 if ($name =~ /[A-Z][0-9a-z_]+/) {
783 ## Do the first word
784 if ($name =~ /^([a-z])([^_]+)/) {
785 my $first = uc($1);
786 my $rest = $2;
787 $name =~ s/^[a-z][^_]+/$first$rest/;
789 ## Do subsequent words
790 while($name =~ /(_[a-z])([^_]+)/) {
791 my $first = uc($1);
792 my $rest = $2;
793 $name =~ s/_[a-z][^_]+/$first$rest/;
798 $names .= $name . ' ';
800 $names =~ s/\s+$//;
802 return $names;
806 sub clone {
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')) {
813 my $new = {};
814 foreach my $key (keys %$obj) {
815 $$new{$key} = $self->clone($$obj{$key});
817 return $new;
819 elsif (UNIVERSAL::isa($obj, 'ARRAY')) {
820 my $new = [];
821 foreach my $o (@$obj) {
822 push(@$new, $self->clone($o));
824 return $new;
827 return $obj;
831 sub save_state {
832 my($self, $selected) = @_;
833 my %state;
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});
845 return %state;
849 sub restore_state {
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;
869 else {
870 $self->{$skey} = $state->{$skey};
872 $self->restore_state_helper($skey, $old, $self->{$skey});
877 sub get_global_cfg {
878 return $_[0]->{'global'};
882 sub get_template_override {
883 return $_[0]->{'template'};
887 sub get_ti_override {
888 return $_[0]->{'ti'};
892 sub get_relative {
893 return $_[0]->{'relative'};
897 sub get_progress_callback {
898 return $_[0]->{'progress'};
902 sub get_addtemp {
903 return $_[0]->{'addtemp'};
907 sub get_addproj {
908 return $_[0]->{'addproj'};
912 sub get_toplevel {
913 return $_[0]->{'toplevel'};
917 sub get_into {
918 return $_[0]->{'into'};
922 sub get_use_env {
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'};
937 sub get_assignment {
938 my $self = shift;
939 my $name = $self->resolve_alias(shift);
940 my $assign = 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);
958 sub get_baseprojs {
959 return $_[0]->{'baseprojs'};
963 sub get_dynamic {
964 return $_[0]->{'dynamic'};
968 sub get_static {
969 return $_[0]->{'static'};
973 sub get_default_component_name {
974 #my $self = shift;
975 return 'default';
979 sub get_features {
980 return $_[0]->{'features'};
984 sub get_hierarchy {
985 return $_[0]->{'hierarchy'};
989 sub get_name_modifier {
990 return $_[0]->{'name_modifier'};
994 sub get_apply_project {
995 return $_[0]->{'apply_project'};
999 sub get_language {
1000 return $_[0]->{'language'};
1004 sub get_outdir {
1005 my $self = shift;
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.
1015 my $orig = $outdir;
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;
1024 else {
1025 return '.';
1030 sub aggressively_replace {
1031 my($self, $icwd, $val) = @_;
1032 my $count = 0;
1033 my $wd = $icwd;
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
1040 $count++;
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
1047 ## and return it.
1048 my $prefix = $1;
1049 my $suffix = substr($val, length($prefix));
1050 return ('../' x $count) . $suffix;
1054 ## We never found a match
1055 return undef;
1058 sub expand_variables {
1059 my($self, $value, $rel, $expand_template, $scopes, $expand, $warn) = @_;
1060 my $cwd = $self->getcwd();
1061 my $start = 0;
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) =~ /(\$\(([^)]+)\))/) {
1070 my $whole = $1;
1071 my $name = $2;
1072 if (defined $$rel{$name}) {
1073 my $val = $$rel{$name};
1074 if ($expand) {
1075 $val =~ s/\//\\/g if ($forward_slashes);
1076 substr($value, $start) =~ s/\$\([^)]+\)/$val/;
1077 $whole = $val;
1079 else {
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.
1092 my $append;
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) = '';
1098 $ivlen -= $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) {
1108 $ival = '.';
1110 else {
1111 $ival = '../' x $dircount;
1112 $ival =~ s/\/$//;
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.
1119 $ival =~ s!^\./!!;
1121 ## Convert the slashes if necessary
1122 $ival =~ s/\//\\/g if ($self->{'convert_slashes'});
1123 substr($value, $start) =~ s/\$\([^)]+\)/$ival/;
1124 $whole = $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).
1132 my $aggressive_rel;
1133 if ($aggrep &&
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;
1139 else {
1140 $val =~ s/\//\\/g if ($self->{'convert_slashes'});
1141 substr($value, $start) =~ s/\$\([^)]+\)/$val/;
1142 $whole = $val;
1145 else {
1146 my $aggressive_rel;
1147 if ($aggrep &&
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;
1153 else {
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);
1164 my @snames;
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);
1185 else {
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/\$\([^)]+\)//;
1197 $whole = '';
1199 else {
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'});
1208 return $value;
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+)/) {
1218 my $name = $1;
1219 my $val = '';
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}) {
1227 $val = $ENV{$name};
1229 else {
1230 ## Keep track of an environment variable not being set.
1231 $one_empty = 1;
1233 $$lref =~ s/\$\w+/$val/;
1235 return $one_empty;
1239 sub relative {
1240 my($self, $value, $expand_template, $scopes) = @_;
1242 if (defined $value) {
1243 if (UNIVERSAL::isa($value, 'ARRAY')) {
1244 my @built;
1245 foreach my $val (@$value) {
1246 my $rel = $self->relative($val, $expand_template, $scopes);
1247 if (UNIVERSAL::isa($rel, 'ARRAY')) {
1248 push(@built, @$rel);
1250 else {
1251 push(@built, $rel);
1254 return \@built;
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,
1271 $how, 1);
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);
1282 return $value;
1286 ## Static function. Returns the default language for MPC.
1287 sub defaultLanguage {
1288 return $deflang;
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]};
1305 sub languageIs {
1306 #my($self, $language) = @_;
1307 return $_[0]->{'language'} eq $_[1];
1310 # ************************************************************
1311 # Virtual Methods To Be Overridden
1312 # ************************************************************
1314 sub restore_state_helper {
1315 #my $self = shift;
1316 #my $skey = shift;
1317 #my $old = shift;
1318 #my $new = shift;
1322 sub get_initial_relative_values {
1323 #my $self = shift;
1324 return {}, 0;
1328 sub get_secondary_relative_values {
1329 my $self = shift;
1330 return ($self->{'use_env'} ? \%ENV :
1331 $self->{'relative'}), $self->{'expand_vars'};
1335 sub aggressive_relative_replacement {
1336 #my $self = shift;
1337 return 0;
1341 sub convert_all_variables {
1342 #my $self = shift;
1343 return 0;
1347 sub expand_variables_from_template_values {
1348 #my $self = shift;
1349 return 0;
1353 sub preserve_assignment_order {
1354 #my $self = shift;
1355 #my $name = shift;
1356 return 1;
1360 sub compare_output {
1361 #my $self = shift;
1362 return 0;
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 {
1373 #my $self = shift;
1374 #my $type = shift;
1375 #my $flags = shift;
1376 return 1, undef;
1379 sub handle_unknown_assignment {
1380 my $self = shift;
1381 my $type = shift;
1382 my @values = @_;
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) = @_;
1395 return $value;
1399 sub generate_recursive_input_list {
1400 #my $self = shift;
1401 #my $dir = shift;
1402 #my $exclude = shift;
1403 return ();
1407 sub reset_values {
1408 #my $self = shift;
1412 sub sort_files {
1413 #my $self = shift;
1414 return 1;
1418 sub file_sorter {
1419 #my $self = shift;
1420 #my $left = shift;
1421 #my $right = shift;
1422 return $_[1] cmp $_[2];
1426 sub read_global_configuration {
1427 #my $self = shift;
1428 #my $input = shift;
1429 return 1;
1433 sub set_verbose_ordering {
1434 #my $self = shift;
1435 #my $value = shift;
1439 sub get_properties {
1440 my $self = shift;
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());
1448 return \%props;