1 package WorkspaceCreator
;
3 # ************************************************************
4 # Description : Base class for all workspace creators
5 # Author : Chad Elliott
6 # Create Date : 5/13/2002
7 # ************************************************************
9 # ************************************************************
11 # ************************************************************
25 @ISA = qw(Creator Options);
27 # ************************************************************
29 # ************************************************************
32 our $num_workers = 0; # single-process
33 our $wdir; # tmp directory
39 ## Valid names for assignments within a workspace
40 my %validNames = ('cmdline' => 1,
44 ## Singleton hash maps of project information
49 ## Global previous workspace names
50 my %previous_workspace_name;
52 ## Constant aggregated workspace type name
53 my $aggregated = 'aggregated_workspace';
55 my $onVMS = DirectoryManager
::onVMS
();
57 # ************************************************************
59 # ************************************************************
62 my($class, $global, $inc, $template, $ti, $dynamic,
63 $static, $relative, $addtemp, $addproj, $progress,
64 $toplevel, $baseprojs, $gfeature, $relative_f, $feature,
65 $features, $hierarchy, $exclude, $makeco, $nmod, $applypj,
66 $genins, $into, $language, $use_env, $expandvars, $gendot,
67 $comments, $foreclipse, $workers, $workers_dir,
70 my $self = Creator
::new
($class, $global, $inc,
71 $template, $ti, $dynamic, $static,
72 $relative, $addtemp, $addproj,
73 $progress, $toplevel, $baseprojs,
75 $hierarchy, $nmod, $applypj,
76 $into, $language, $use_env, $expandvars,
79 $self->{'pid'} = 'parent';
81 # implicit dependency order counter. this is
82 # incremented in the children.
83 $self->{'imp_dep_ctr'} = 0;
85 ## These need to be reset at the end of each
86 ## workspace processed within a .mwc file
87 $self->{'workspace_name'} = undef;
88 $self->{'projects'} = [];
89 $self->{'project_info'} = {};
90 $self->{'project_files'} = [];
91 $self->{'modified_count'} = 0;
92 $self->{'exclude'} = {};
93 $self->{'associated'} = {};
94 $self->{'scoped_assign'} = {};
95 $self->{'aggregated_mpc'} = {};
96 $self->{'aggregated_assign'} = {};
97 $self->{'mpc_to_output'} = {};
99 ## These are maintained/modified throughout processing
100 $self->{$self->{'type_check'}} = 0;
101 $self->{'cacheok'} = $self->default_cacheok();
102 $self->{'lib_locations'} = {};
103 $self->{'reading_parent'} = [];
104 $self->{'global_feature_file'} = $gfeature;
105 $self->{'relative_file'} = $relative_f;
106 $self->{'project_file_list'} = {};
107 $self->{'ordering_cache'} = {};
108 $self->{'handled_scopes'} = {};
109 $self->{'scoped_basedir'} = undef;
110 $self->{'current_aggregated'} = undef;
112 ## These are static throughout processing
113 $self->{'coexistence'} = $self->requires_make_coexistence() ?
1 : $makeco;
114 $self->{'for_eclipse'} = $foreclipse;
115 $self->{'workers'} = $workers;
116 $self->{'generate_dot'} = $gendot;
117 $self->{'generate_ins'} = $genins;
118 $self->{'verbose_ordering'} = $self->default_verbose_ordering();
119 $self->{'wctype'} = $self->extractType("$self");
120 $self->{'workspace_comments'} = $comments;
122 if (defined $$exclude[0]) {
123 my $type = $self->{'wctype'};
124 if (!defined $self->{'exclude'}->{$type}) {
125 $self->{'exclude'}->{$type} = [];
127 push(@
{$self->{'exclude'}->{$type}}, @
$exclude);
128 $self->{'orig_exclude'} = $self->{'exclude'};
131 $self->{'orig_exclude'} = {};
134 ## Add a hash reference for our workspace type
135 if (!defined $previous_workspace_name{$self->{'wctype'}}) {
136 $previous_workspace_name{$self->{'wctype'}} = {};
139 ## Warn users about unnecessary options
140 if ($self->get_hierarchy() && $self->workspace_per_project()) {
141 $self->warning("The -hierarchy option is unnecessary " .
142 "for the " . $self->{'wctype'} . " type.");
144 if ($self->{'coexistence'} && !$self->supports_make_coexistence()) {
145 $self->warning("Using the -make_coexistence option has " .
146 "no effect on the " . $self->{'wctype'} . " type.");
149 ## multi-process config
150 $num_workers = $workers if $workers > $num_workers;
151 $wdir = $workers_dir;
152 $wport = $workers_port;
158 sub default_cacheok
{
162 sub set_verbose_ordering
{
163 my($self, $value) = @_;
164 $self->{'verbose_ordering'} = $value;
168 sub modify_assignment_value
{
169 ## Workspace assignments do not need modification.
175 my($self, $ih, $line, $flags) = @_;
176 my($status, $error, @values) = $self->parse_known($line, $ih);
178 ## Was the line recognized?
179 if ($status && defined $values[0]) {
180 if ($values[0] eq $self->{'grammar_type'}) {
181 my $name = $values[1];
182 if (defined $name && $name eq '}') {
183 if (!defined $self->{'reading_parent'}->[0]) {
184 ## Fill in all the default values
185 $self->generate_defaults();
187 ## End of workspace; Have subclass write out the file
188 ## Generate the project files
189 my($gstat, $creator, $err);
190 if ($num_workers > 0) {
191 if (!defined ($wport)) {
192 ## use temp files for multiprocess mpc
193 ## Lock the temp directory before generating project files.
194 my $lock = 'mpc-worker.lock';
196 ## check for valid temp directory
198 if ($^O
eq 'MSWin32') {
208 die "Error: No temporary directory found. Supply one with \"-worker_dir\" option.\n";
211 $self->diagnostic("Multiprocess MPC using \"$wdir\" for temporary files.");
214 mkdir $wdir || die "Error: Can't find or create directory $wdir\n"
217 ## lock the directory
218 if (-e
"$wdir/$lock") {
219 die "Error: Another instance of MPC is using $wdir, or a previous session failed to remove the lock file $lock\n";
222 open (FDL
, ">$wdir/$lock") || die "Error reating lock file $lock in $wdir\n";
223 print FDL
"File generated by MPC process ", $$, " on ", scalar (localtime(time())), "\n";
226 $self->diagnostic("Multiprocess MPC created lock file $wdir/$lock");
229 ## generate the project files
230 ($gstat, $creator, $err) = $self->generate_project_files_fork();
232 ## Release temp directory lock;
233 if (!unlink("$wdir/$lock")) {
234 $self->error("Multiprocess MPC unable to remove lock file $wdir/$lock");
237 $self->diagnostic("Multiprocess MPC removed $wdir/$lock");
242 ## Socket-based Multiprocess MPC
243 ($gstat, $creator, $err) =
244 $self->generate_project_files_fork_socket();
248 ($gstat, $creator, $err) = $self->generate_project_files();
252 ($status, $error) = $self->write_workspace($creator, 1);
253 $self->{'assign'} = {};
260 $self->{'modified_count'} = 0;
261 $self->{'workspace_name'} = undef;
262 $self->{'projects'} = [];
263 $self->{'project_info'} = {};
264 $self->{'project_files'} = [];
265 $self->{'exclude'} = $self->{'orig_exclude'};
266 $self->{'associated'} = {};
267 $self->{'scoped_assign'} = {};
268 $self->{'aggregated_mpc'} = {};
269 $self->{'aggregated_assign'} = {};
270 $self->{'mpc_to_output'} = {};
272 $self->{$self->{'type_check'}} = 0;
275 ## Workspace Beginning
276 ## Deal with the inheritance hierarchy first
277 if (defined $values[2]) {
278 foreach my $parent (@
{$values[2]}) {
279 ## Read in the parent onto ourself
280 my $file = $self->search_include_path("$parent.$wsbase");
281 if (!defined $file) {
282 $file = $self->search_include_path("$parent.$wsext");
286 push(@
{$self->{'reading_parent'}}, 1);
287 $status = $self->parse_file($file);
288 pop(@
{$self->{'reading_parent'}});
290 $error = "Invalid parent: $parent" if (!$status);
294 $error = "Unable to locate parent: $parent";
299 ## Set up some initial values
301 if ($name =~ /[\/\\]/) {
303 $error = 'Workspaces can not have a slash ' .
304 'or a back slash in the name';
310 ## Replace any *'s with the default name
311 if (index($name, '*') >= 0) {
312 $name = $self->fill_type_name(
313 $name, $self->get_default_workspace_name());
316 $self->{'workspace_name'} = $name;
319 $self->{$self->{'type_check'}} = 1;
322 elsif ($values[0] eq '0') {
323 if (defined $validNames{$values[1]}) {
324 $self->process_assignment($values[1], $values[2], $flags);
327 $error = "Invalid assignment name: '$values[1]'";
331 elsif ($values[0] eq '1') {
332 if (defined $validNames{$values[1]}) {
333 ## This code only runs when there is a non-scoped assignment. As
334 ## such, we can safely replace all environment variables here so
335 ## that they are not incorrectly handled in aggregated
337 $self->replace_env_vars(\
$values[2]) if ($values[2] =~ /\$/);
338 $self->process_assignment_add($values[1], $values[2], $flags);
341 $error = "Invalid addition name: $values[1]";
345 elsif ($values[0] eq '-1') {
346 if (defined $validNames{$values[1]}) {
347 $self->process_assignment_sub($values[1], $values[2], $flags);
350 $error = "Invalid subtraction name: $values[1]";
354 elsif ($values[0] eq 'component') {
355 my %copy = %{defined $flags ?
$flags : $self->get_assignment_hash()};
356 ($status, $error) = $self->parse_scope($ih,
363 $error = "Unrecognized line: $line";
367 elsif ($status == -1) {
368 ## If the line contains a variable, try to replace it with an actual
370 $line = $self->relative($line) if (index($line, '$') >= 0);
372 foreach my $expfile ($line =~ /[\?\*\[\]]/ ?
$self->mpc_glob($line) :
374 if ($expfile =~ /\.$wsext$/) {
375 my %copy = %{defined $flags ?
$flags : $self->get_assignment_hash()};
376 ($status, $error) = $self->aggregated_workspace($expfile, \
%copy);
380 push(@
{$self->{'project_files'}}, $expfile);
386 return $status, $error;
390 sub aggregated_workspace
{
391 my($self, $file, $flags) = @_;
392 my $fh = new FileHandle
();
394 if (open($fh, $file)) {
395 my $oline = $self->get_line_number();
396 my $tc = $self->{$self->{'type_check'}};
397 my $ag = $self->{'handled_scopes'}->{$aggregated};
398 my $pca = $self->{'current_aggregated'};
399 my $psbd = $self->{'scoped_basedir'};
400 my $prev_assign = $self->clone($self->get_assignment_hash());
401 my($status, $error, @values) = (0, 'No recognizable lines');
403 $self->{'handled_scopes'}->{$aggregated} = undef;
404 $self->set_line_number(0);
405 $self->{$self->{'type_check'}} = 0;
406 $self->{'current_aggregated'} = $file;
407 $self->{'scoped_basedir'} = $self->mpc_dirname($file);
409 ## If the directory name for the file is the current directory, we
410 ## need to empty it out. If we don't, it will cause the file name to
411 ## not match up with itself later on where scoped_basedir is used.
412 $self->{'scoped_basedir'} = undef if ($self->{'scoped_basedir'} eq '.');
415 my $line = $self->preprocess_line($fh, $_);
416 ($status, $error, @values) = $self->parse_known($line, $fh);
418 ## Was the line recognized?
420 if (defined $values[0]) {
421 if ($values[0] eq $self->{'grammar_type'}) {
422 if (defined $values[2]) {
423 my $name = $self->mpc_basename($file);
424 $name =~ s/\.[^\.]+$//;
426 $error = 'Aggregated workspace (' . $name .
427 ') can not inherit from another workspace';
430 ($status, $error) = $self->parse_scope($fh,
439 $error = 'Unable to aggregate ' . $file;
451 $self->{'aggregated_assign'}->{$file} =
452 $self->clone($self->get_assignment_hash());
453 $self->{'assign'} = $prev_assign;
456 $self->{'scoped_basedir'} = $psbd;
457 $self->{'current_aggregated'} = $pca;
458 $self->{'handled_scopes'}->{$aggregated} = $ag;
459 $self->{$self->{'type_check'}} = $tc;
460 $self->set_line_number($oline);
462 return $status, $error;
465 return 0, 'Unable to open ' . $file;
470 my($self, $fh, $name, $type, $validNames, $flags, $elseflags) = @_;
472 if ($type eq $self->get_default_component_name()) {
473 $type = $self->{'wctype'};
476 if ($name eq 'exclude') {
477 return $self->parse_exclude($fh, $type, $flags);
479 elsif ($name eq 'associate') {
480 return $self->parse_associate($fh, $type);
482 elsif ($name eq 'specific') {
483 return $self->parse_specific($fh, $type, $validNames, $flags, $elseflags);
486 return $self->SUPER::parse_scope
($fh, $name, $type,
487 $validNames, $flags, $elseflags);
492 my($self, $typestr) = @_;
493 my $wcprops = $self->get_properties();
496 @types{split(/\s*,\s*/, $typestr)} = ();
498 ## If there is a property in the typestr, i.e., prop:, then
499 ## we need to extract it into its own collection while removing
500 ## it from the types collection.
501 if (index($typestr, 'prop:') >= 0) {
502 foreach my $key (keys %types) {
503 if ($key =~ /^prop:\s*(\w+)/) {
504 ## Add the property to the prop hash.
507 ## Remove the original property from the types.
510 elsif ($key =~ /^!prop:\s*(\w+)/) {
511 ## Negate the property.
514 ## Remove the original property from the types.
520 ## Now, process the properties and determine if this project
521 ## type should be excluded. This will be the case if the property
522 ## is valid and there exists a match between the listed properties
523 ## and the workspace properties.
524 while (my ($key, $val) = each %props) {
525 if (exists $$wcprops{$key}) {
526 if ($$wcprops{$key} == 1 and $$wcprops{$key} == $val) {
527 $types{$self->{wctype
}} = 1;
530 delete $types{$self->{wctype
}};
534 $types{$self->{wctype
}} = 1;
538 ## Remove all negated types from the collection.
539 foreach my $key (keys %types) {
540 if ($key =~ /^!\s*(\w+)/) {
541 if ($1 eq $self->{wctype
}) {
542 ## Remove the negated key
545 ## Then delete the key that was negated in the exclusion
555 my($self, $fh, $typestr, $flags) = @_;
557 my $errorString = 'Unable to process exclude';
558 my $negated = (index($typestr, '!') >= 0);
559 my $types = $self->process_types($typestr);
563 if (exists $$types{$self->{wctype
}}) {
565 my $line = $self->preprocess_line($fh, $_);
569 elsif ($line =~ /^}(.*)$/) {
571 if (defined $1 && $1 ne '') {
573 $errorString = "Trailing characters found: '$1'";
577 $errorString = undef;
579 last if ($count == 0);
582 if ($line =~ /^(\w+)\s*(\([^\)]+\))?\s*{$/) {
585 elsif ($self->parse_assignment($line, [], $fh)) {
586 ## Ignore all assignments
589 if ($line =~ /^"([^"]+)"$/) {
593 ## If the line contains a variable, try to replace it with an
595 $line = $self->relative($line) if (index($line, '$') >= 0);
597 if (defined $self->{'scoped_basedir'} &&
598 $self->path_is_relative($line)) {
599 $line = $self->{'scoped_basedir'} . '/' . $line;
601 if ($line =~ /[\?\*\[\]]/) {
602 push(@exclude, $self->mpc_glob($line));
605 push(@exclude, $line);
611 foreach my $type (keys %$types) {
612 if (!defined $self->{'exclude'}->{$type}) {
613 $self->{'exclude'}->{$type} = [];
615 push(@
{$self->{'exclude'}->{$type}}, @exclude);
620 ($status, $errorString) = $self->SUPER::parse_scope
($fh,
627 ## If this exclude block didn't match the current type and the
628 ## exclude wasn't negated, we need to eat the exclude block so that
629 ## these lines don't get included into the workspace.
631 my $line = $self->preprocess_line($fh, $_);
633 if ($line =~ /^(\w+)\s*(\([^\)]+\))?\s*{$/) {
636 elsif ($line =~ /^}(.*)$/) {
638 if (defined $1 && $1 ne '') {
640 $errorString = "Trailing characters found: '$1'";
644 $errorString = undef;
646 last if ($count == 0);
652 return $status, $errorString;
656 sub parse_associate
{
657 my($self, $fh, $assoc_key) = @_;
659 my $errorString = 'Unable to process associate';
663 if (!defined $self->{'associated'}->{$assoc_key}) {
664 $self->{'associated'}->{$assoc_key} = {};
668 my $line = $self->preprocess_line($fh, $_);
672 elsif ($line =~ /^}(.*)$/) {
674 if (defined $1 && $1 ne '') {
675 $errorString = "Trailing characters found: '$1'";
680 $errorString = undef;
682 last if ($count == 0);
685 if ($line =~ /^(\w+)\s*(\([^\)]+\))?\s*{$/) {
688 elsif ($self->parse_assignment($line, [], $fh)) {
689 $errorString = 'Assignments are not ' .
690 'allowed within an associate scope';
694 if ($line =~ /^"([^"]+)"$/) {
698 ## If the line contains a variable, try to replace it with an
700 $line = $self->relative($line) if (index($line, '$') >= 0);
702 if (defined $self->{'scoped_basedir'} &&
703 $self->path_is_relative($line)) {
704 $line = $self->{'scoped_basedir'} . '/' . $line;
706 if ($line =~ /[\?\*\[\]]/) {
707 foreach my $file ($self->mpc_glob($line)) {
708 $self->{'associated'}->{$assoc_key}->{$file} = 1;
712 $self->{'associated'}->{$assoc_key}->{$line} = 1;
718 return $status, $errorString;
723 my($self, $fh, $typestr, $validNames, $flags, $elseflags) = @_;
724 my $types = $self->process_types($typestr);
725 my $wctype = $self->{'wctype'};
726 my $matches = exists $types->{$wctype};
728 # $elseflags needs to be defined for Creator::parse_scope to allow "} else {"
729 $elseflags = {} unless defined $elseflags;
731 # Assignments within 'specific' always go to the workspace-level assignment
732 # hash table instead of the $flags bound to the scope.
733 my $assign = $self->get_assignment_hash();
735 return $self->SUPER::parse_scope
($fh, 'specific', $matches ?
$wctype : undef,
736 $validNames, $matches ?
($assign, $elseflags)
741 sub handle_unknown_assignment
{
747 $self->process_any_assignment(undef, @values);
755 my($self, $file) = @_;
757 foreach my $excluded (@
{$self->{'exclude'}->{$self->{'wctype'}}}) {
758 return 1 if ($excluded eq $file || index($file, "$excluded/") == 0);
765 sub handle_scoped_end
{
766 my($self, $type, $flags) = @_;
770 ## Replace instances of $PWD with the current directory plus the
771 ## scoped_basedir. We have to do it now otherwise, $PWD will be the
772 ## wrong directory if it's done later.
773 if (defined $$flags{'cmdline'} && defined $self->{'scoped_basedir'} &&
774 index($$flags{'cmdline'}, '$PWD') >= 0) {
775 my $dir = $self->getcwd() . '/' . $self->{'scoped_basedir'};
776 $$flags{'cmdline'} =~ s/\$PWD(\W)/$dir$1/g;
777 $$flags{'cmdline'} =~ s/\$PWD$/$dir/;
780 if ($type eq $aggregated && !defined $self->{'handled_scopes'}->{$type}) {
781 ## Go back to the previous directory and add the directory contents
782 ($status, $error) = $self->handle_scoped_unknown(undef, $type, $flags, '.');
785 $self->{'handled_scopes'}->{$type} = undef;
786 return $status, $error;
790 sub handle_scoped_unknown
{
791 my($self, $fh, $type, $flags, $line) = @_;
796 ## If $type is undef, we are in a skipped part of a specific block
797 return 1 unless defined $type;
799 if ($line =~ /^\w+.*{/) {
802 my $tc = $self->{$self->{'type_check'}};
803 $self->{$self->{'type_check'}} = 1;
804 ($status, $error, @values) = $self->parse_line($fh, $line, $flags);
805 $self->{$self->{'type_check'}} = $tc;
809 $error = 'Unhandled line: ' . $line;
811 return $status, $error;
814 ## If the line contains a variable, try to replace it with an actual
816 if (index($line, '$') >= 0) {
817 $line = $self->relative($line);
819 elsif (defined $self->{'scoped_basedir'}) {
820 if ($self->path_is_relative($line)) {
822 $line = $self->{'scoped_basedir'};
825 ## This is a relative path and the project may have been added
826 ## previously without a relative path. We need to convert the
827 ## relative path into an absolute path and, if possible, remove
828 ## the current working directory from the front. This will get
829 ## it down to a path that's relative to the current directory and
830 ## likely to match up with the addition of this file or directory
831 ## from an upper workspace.
832 my $cwd = $self->getcwd();
833 $line = $self->abs_path($self->{'scoped_basedir'} . "/$line");
834 if (index($line, $cwd) == 0) {
835 $line = substr($line, length($cwd) + 1);
841 ## We must build up the list of project files and use them as the
842 ## keys in the duplicate hash check. We need to call
843 ## search_for_files() because the user may have just listed
844 ## directories in the workspace and we need to deal with mpc files.
846 $self->search_for_files($self->{'project_files'}, \
@files);
851 ## If the aggregated workspace contains a scope (other than exclude)
852 ## it will be processed in the block above and we will eventually get
853 ## here, but by that time $type will no longer be $aggregated. So,
854 ## we just need to set it here to ensure that we don't add everything
855 ## in the scoped_basedir directory in handle_scoped_end()
856 $self->{'handled_scopes'}->{$aggregated} = 1;
860 $self->search_for_files([ $line ], \
@files, $$flags{'implicit'});
862 ## If we are generating implicit projects within a scope, then
863 ## we need to remove directories and the parent directories for which
864 ## there is an mpc file. Otherwise, the projects will be added
866 if ($$flags{'implicit'}) {
868 foreach my $file (@files) {
869 if ($file =~ /\.mpc$/) {
872 $exc = $self->mpc_dirname($exc);
874 } while ($exc ne '.' && $exc !~ /[a-z]:[\/\\]/i
);
879 foreach my $file (@files) {
880 push(@acceptable, $file) if (!defined $remove{$file});
882 @files = @acceptable;
885 foreach my $file (@files) {
886 $self->add_aggregated_mpc($file, $dupchk, $flags);
890 foreach my $expfile ($line =~ /[\?\*\[\]]/ ?
$self->mpc_glob($line) :
892 if ($expfile =~ /\.$wsext$/) {
893 ## An aggregated workspace within an aggregated workspace or scope.
894 ($status, $error) = $self->aggregated_workspace($expfile, $flags);
898 $self->add_aggregated_mpc($expfile, $dupchk, $flags);
902 $self->{'handled_scopes'}->{$type} = 1;
904 return $status, $error;
908 sub add_aggregated_mpc
{
909 my($self, $file, $dupchk, $flags) = @_;
910 if (!$self->excluded($file)) {
911 if (defined $dupchk && exists $$dupchk{$file}) {
912 $self->information("Duplicate mpc file ($file) added by an " .
913 'aggregate workspace. It will be ignored.');
916 $self->{'scoped_assign'}->{$file} = $flags;
917 push(@
{$self->{'project_files'}}, $file);
918 push(@
{$self->{'aggregated_mpc'}->{$self->{'current_aggregated'}}},
919 $file) if defined $self->{'current_aggregated'};
925 sub search_for_files
{
926 my($self, $files, $array, $impl) = @_;
929 foreach my $file (@
$files) {
931 my @f = $self->generate_default_file_list(
933 $self->{'exclude'}->{$self->{'wctype'}},
935 $self->search_for_files(\
@f, $array, $impl);
939 # Strip out ^ symbols
940 $file =~ s/\^//g if ($onVMS);
942 unshift(@
$array, $file);
945 elsif ($file =~ /\.mpc$/) {
948 # Strip out ^ symbols
949 $file =~ s/\^//g if ($onVMS);
951 unshift(@
$array, $file);
959 sub remove_duplicate_projects
{
960 my($self, $list) = @_;
961 my $count = scalar(@
$list);
963 for (my $i = 0; $i < $count; ++$i) {
964 my $file = $$list[$i];
965 foreach my $inner (@
$list) {
966 if ($file ne $inner &&
967 $file eq $self->mpc_dirname($inner) && ! -d
$inner) {
968 splice(@
$list, $i, 1);
978 sub generate_default_components
{
979 my($self, $files, $impl, $excluded) = @_;
980 my $pjf = $self->{'project_files'};
982 if (defined $$pjf[0]) {
983 ## If we have files, then process directories
985 foreach my $file (@
$pjf) {
986 if (!$self->excluded($file)) {
989 my @gen = $self->generate_default_file_list(
991 $self->{'exclude'}->{$self->{'wctype'}});
992 $self->search_for_files(\
@gen, \
@found, $impl);
993 push(@built, @found);
994 if ($impl || $self->{'scoped_assign'}->{$file}->{'implicit'}) {
1004 ## If the workspace is set to implicit remove duplicates from this
1006 $self->remove_duplicate_projects(\
@built) if ($impl);
1008 ## Set the project files
1009 $self->{'project_files'} = \
@built;
1012 ## Add all of the wanted files in this directory
1013 ## and in the subdirectories.
1014 $excluded |= $self->search_for_files($files, $pjf, $impl);
1016 ## If the workspace is set to implicit remove duplicates from this
1018 $self->remove_duplicate_projects($pjf) if ($impl);
1020 ## If no files were found, then we push the empty
1021 ## string, so the Project Creator will generate
1022 ## the default project file.
1023 push(@
$pjf, '') if (!defined $$pjf[0] && !$excluded);
1028 sub get_default_workspace_name
{
1030 my $name = $self->{'current_input'};
1033 $name = $self->base_directory();
1036 ## Since files on UNIX can have back slashes, we transform them
1037 ## into underscores.
1040 ## Take off the extension
1041 $name =~ s/\.[^\.]+$//;
1048 sub generate_defaults
{
1051 ## Generate default workspace name
1052 if (!defined $self->{'workspace_name'}) {
1053 $self->{'workspace_name'} = $self->get_default_workspace_name();
1056 ## Modify the exclude list if we have changed directory from the original
1057 ## starting directory. Just take off the difference from the front.
1059 my $top = $self->getcwd() . '/';
1060 my $start = $self->getstartdir() . '/';
1062 if ($start ne $top && $top =~ s/^$start//) {
1063 foreach my $exclude (@
{$self->{'exclude'}->{$self->{'wctype'}}}) {
1064 push(@original, $exclude);
1065 $exclude =~ s/^$top//;
1070 my @files = $self->generate_default_file_list(
1072 $self->{'exclude'}->{$self->{'wctype'}},
1075 ## Generate default components
1076 $self->generate_default_components(\
@files,
1077 $self->get_assignment('implicit'),
1080 ## Return the actual exclude list of we modified it
1081 if (defined $original[0]) {
1082 $self->{'exclude'}->{$self->{'wctype'}} = \
@original;
1087 sub get_workspace_name
{
1088 return $_[0]->{'workspace_name'};
1092 sub get_current_output_name
{
1093 return $_[0]->{'current_output'};
1097 sub write_and_compare_file
{
1098 my($self, $outdir, $oname, $func, @params) = @_;
1099 my $fh = new FileHandle
();
1101 my $errorString = undef;
1103 ## Set the output directory if one wasn't provided
1104 $outdir = $self->get_outdir() if (!defined $outdir);
1106 ## Create the full name and pull off the directory. The directory
1107 ## portion may not be the same as $outdir, since $name could possibly
1108 ## contain a directory portion too.
1109 my $name = "$outdir/$oname";
1110 my $dir = $self->mpc_dirname($name);
1112 ## Make the full path if necessary
1113 mkpath
($dir, 0, 0777) if ($dir ne '.');
1115 ## Set the current output data member to our file's full name
1116 $self->{'current_output'} = $name;
1118 if ($self->compare_output()) {
1119 ## First write the output to a temporary file
1120 my $tmp = "$outdir/MWC$>.$$";
1122 if (open($fh, ">$tmp")) {
1123 ($status, $errorString) = &$func($self, $fh, @params);
1126 $different = 0 if ($status && !$self->files_are_different($name, $tmp));
1130 $errorString = "Unable to open $tmp for output.";
1137 if (!rename($tmp, $name)) {
1139 $errorString = "Unable to open $name for output";
1143 ## There is no need to rename, so remove our temp file.
1149 if (open($fh, ">$name")) {
1150 &$func($self, $fh, @params);
1155 $errorString = "Unable to open $name for output.";
1159 return $status, $errorString;
1162 sub write_workspace
{
1164 my($self, $creator, $addfile) = @_;
1169 if ($self->get_toplevel()) {
1171 ## There is usually a progress indicator callback provided, but if
1172 ## the output is being redirected, there will be no progress
1174 my $progress = $self->get_progress_callback();
1175 &$progress() if (defined $progress);
1179 ## To be consistent across multiple project types, we disallow
1180 ## duplicate project names for all types, not just VC6.
1181 ## Note that these name are handled case-insensitive by VC6
1183 foreach my $project (@
{$self->{'projects'}}) {
1184 my $name = lc($self->{'project_info'}->{$project}->[ProjectCreator
::PROJECT_NAME
]);
1185 if (defined $names{$name}) {
1187 $self->error("Duplicate case-insensitive project '$name'. " .
1188 "Look in " . $self->mpc_dirname($project) .
1189 " and " . $self->mpc_dirname($names{$name}) .
1190 " for project name conflicts.");
1193 $names{$name} = $project;
1198 $self->{'per_project_workspace_name'} = 1;
1201 my $name = $self->transform_file_name($self->workspace_file_name());
1203 my $abort_creation = 0;
1204 if ($duplicates > 0) {
1205 $abort_creation = 1;
1206 $errorString = "Duplicate case-insensitive project names are " .
1207 "not allowed within a workspace.";
1211 if (!defined $self->{'projects'}->[0]) {
1212 $self->information('No projects were created.');
1213 $abort_creation = 1;
1217 if (!$abort_creation) {
1218 ## Verify and possibly modify the dependencies
1220 $self->verify_build_ordering();
1223 if ($addfile || !$self->file_written($name)) {
1224 ($status, $errorString) = $self->write_and_compare_file(
1227 my($self, $fh) = @_;
1228 $self->pre_workspace($fh, $creator, $addfile);
1229 my($status, $errorString) = $self->write_comps($fh, $creator, $addfile);
1230 ## If write_comps() does't return a status, set status to true.
1231 $status = 1 if (!defined $status || $status eq "");
1233 my $wsHelper = WorkspaceHelper
::get
($self);
1234 $wsHelper->perform_custom_processing($fh, $creator, $addfile);
1236 $self->post_workspace($fh, $creator, $addfile);
1238 return $status, $errorString;
1240 $self->add_file_written($name) if ($status && $addfile);
1243 my $additional = $self->get_additional_output();
1244 foreach my $entry (@
$additional) {
1245 ($status, $errorString) = $self->write_and_compare_file(@
$entry);
1251 if ($addfile && $self->{'generate_dot'}) {
1252 my $dh = new FileHandle
();
1253 my $wsname = $self->get_workspace_name();
1254 if (open($dh, ">$wsname.dot")) {
1256 my @list = $self->number_target_deps($self->{'projects'},
1257 $self->{'project_info'},
1259 ## If the workspace name contains a dot, we will replace it
1260 ## with two underscores. Graphviz does not accept names with
1262 $wsname =~ s/\./__/g;
1263 print $dh "digraph $wsname {\n";
1264 foreach my $project (@
{$self->{'projects'}}) {
1265 if (defined $targnum{$project}) {
1266 ## If the project name contains a dot, we will replace it
1267 ## with two underscores. Graphviz does not accept names
1269 my $pname = $self->{'project_info'}->{$project}->[ProjectCreator
::PROJECT_NAME
];
1270 $pname =~ s/\./__/g;
1271 foreach my $number (@
{$targnum{$project}}) {
1272 my $depr = $self->{'project_info'}->{$list[$number]}->[ProjectCreator
::PROJECT_NAME
];
1274 print $dh " $pname -> ", $depr, ";\n";
1282 $self->warning("Unable to write to $wsname.dot.");
1287 $self->{'per_project_workspace_name'} = undef if (!$addfile);
1290 return $status, $errorString;
1294 sub save_project_info
{
1295 my($self, $gen, $gpi, $gll, $dir, $projects, $pi, $ll) = @_;
1298 ## For each file written
1299 foreach my $pj (@
$gen) {
1300 ## Save the full path to the project file in the array
1301 my $full = ($dir ne '.' ?
"$dir/" : '') . $pj;
1302 push(@
$projects, $full);
1304 ## Get the corresponding generated project info and save it
1305 ## in the hash map keyed on the full project file name
1306 $$pi{$full} = $$gpi[$c];
1310 foreach my $key (keys %$gll) {
1311 $$ll{$key} = $$gll{$key};
1317 my($self, $file) = @_;
1320 if ($file =~ /^([^\/\\]+)[\
/\\](.*)/ && $1 !~ /^[a-z]:$/i) {
1328 sub generate_hierarchy
{
1329 my($self, $creator, $origproj, $originfo) = @_;
1333 my $cwd = $self->getcwd();
1337 ## Make a copy of these. We will be modifying them.
1338 ## It is necessary to sort the projects to get the correct ordering.
1339 ## Projects in the current directory must come before projects in
1340 ## other directories.
1341 my @projects = sort { return $self->sort_projects_by_directory($a, $b) + 0;
1343 my %projinfo = %{$originfo};
1345 foreach my $prj (@projects) {
1346 my($top, $rest) = $self->topname($prj);
1348 if (!defined $current) {
1350 push(@saved, $rest);
1351 $sinfo{$rest} = $projinfo{$prj};
1353 elsif ($top ne $current) {
1354 if ($current ne '.') {
1355 ## Write out the hierachical workspace
1356 $self->cd($current);
1357 ($status, $errorString) = $self->generate_hierarchy($creator, \
@saved, \
%sinfo);
1359 $self->{'projects'} = \
@saved;
1360 $self->{'project_info'} = \
%sinfo;
1361 $self->{'workspace_name'} = $self->base_directory();
1362 ($status, $errorString) = $self->write_workspace($creator) if ($status);
1368 ## Start the next one
1372 $sinfo{$rest} = $projinfo{$prj};
1375 push(@saved, $rest);
1376 $sinfo{$rest} = $projinfo{$prj};
1379 if ($status && defined $current && $current ne '.') {
1380 $self->cd($current);
1381 ($status, $errorString) = $self->generate_hierarchy($creator, \
@saved, \
%sinfo);
1383 $self->{'projects'} = \
@saved;
1384 $self->{'project_info'} = \
%sinfo;
1385 $self->{'workspace_name'} = $self->base_directory();
1386 ($status, $errorString) = $self->write_workspace($creator) if ($status);
1391 return $status, $errorString;
1394 sub generate_project_files
{
1396 my $status = (scalar @
{$self->{'project_files'}} == 0 ?
1 : 0);
1400 my $creator = $self->project_creator();
1401 my $cwd = $self->getcwd();
1402 my $impl = $self->get_assignment('implicit');
1403 my $postkey = $creator->get_dynamic() .
1404 $creator->get_static() . "-$self";
1405 my $previmpl = $impl;
1406 my $prevcache = $self->{'cacheok'};
1407 my %gstate = $creator->save_state();
1408 my $genimpdep = $self->generate_implicit_project_dependencies();
1411 $Data::Dumper
::Indent
= 0;
1413 ## Save this project creator setting for later use in the
1414 ## number_target_deps() method.
1415 $self->{'dependency_is_filename'} = $creator->dependency_is_filename();
1417 ## Remove the address portion of the $self string
1418 $postkey =~ s/=.*//;
1420 ## Set the source file callback on our project creator
1421 $creator->set_source_listing_callback([\
&source_listing_callback
, $self]);
1423 foreach my $ofile (@
{$self->{'project_files'}}) {
1424 if (!$self->excluded($ofile)) {
1426 my $dir = $self->mpc_dirname($file);
1429 if (defined $self->{'scoped_assign'}->{$ofile}) {
1430 ## Handle the implicit assignment
1431 my $oi = $self->{'scoped_assign'}->{$ofile}->{'implicit'};
1437 ## Handle the cmdline assignment
1438 my $cmdline = $self->{'scoped_assign'}->{$ofile}->{'cmdline'};
1439 if (defined $cmdline && $cmdline ne '') {
1440 ## Save the cacheok value
1441 $prevcache = $self->{'cacheok'};
1443 ## Get the current parameters and process the command line
1444 my %parameters = $self->current_parameters();
1445 $self->process_cmdline($cmdline, \
%parameters);
1447 ## Set the parameters on the creator
1448 $creator->restore_state(\
%parameters);
1453 ## If we are generating implicit projects and the file is a
1454 ## directory, then we set the dir to the file and empty the file
1455 if ($impl && -d
$file) {
1459 ## If the implicit assignment value was not a number, then
1460 ## we will add this value to our base projects.
1461 if ($impl !~ /^\d+$/) {
1462 my $bps = $creator->get_baseprojs();
1463 push(@
$bps, split(/\s+/, $impl));
1465 $self->{'cacheok'} = 0;
1469 ## Generate the key for this project file
1470 my $prkey = $self->getcwd() . '/' .
1471 ($file eq '' ?
$dir : $file) . "-$postkey";
1473 ## We must change to the subdirectory for
1474 ## which this project file is intended
1475 if ($self->cd($dir)) {
1476 my $files_written = [];
1477 my $gen_proj_info = [];
1478 my $gen_lib_locs = {};
1479 if ($self->{'cacheok'} && defined $allprojects{$prkey}) {
1480 $files_written = $allprojects{$prkey};
1481 $gen_proj_info = $allprinfo{$prkey};
1482 $gen_lib_locs = $allliblocs{$prkey};
1487 $status = $creator->generate($self->mpc_basename($file));
1489 ## If any one project file fails, then stop
1490 ## processing altogether.
1492 ## We don't restore the state before we leave,
1493 ## but that's ok since we will be exiting right now.
1494 return $status, $creator,
1495 "Unable to process " . ($file eq '' ?
" in $dir" : $file);
1498 ## Get the individual project information and
1499 ## generated file name(s)
1500 $files_written = $creator->get_files_written();
1501 $gen_proj_info = $creator->get_project_info();
1502 $gen_lib_locs = $creator->get_lib_locations();
1504 if ($self->{'cacheok'}) {
1505 $allprojects{$prkey} = $files_written;
1506 $allprinfo{$prkey} = $gen_proj_info;
1507 $allliblocs{$prkey} = $gen_lib_locs;
1510 push(@
{$self->{'mpc_to_output'}->{$ofile}}, @
$files_written);
1513 $self->save_project_info($files_written, $gen_proj_info,
1514 $gen_lib_locs, $dir,
1515 \
@projects, \
%pi, \
%liblocs);
1518 ## Unable to change to the directory.
1519 ## We don't restore the state before we leave,
1520 ## but that's ok since we will be exiting soon.
1521 return 0, $creator, "Unable to change directory to $dir";
1524 ## Return things to the way they were
1525 $impl = $previmpl if (defined $self->{'scoped_assign'}->{$ofile});
1527 $self->{'cacheok'} = $prevcache;
1528 $creator->restore_state(\
%gstate);
1532 ## This one was excluded, so status is ok
1538 ## Add implict project dependencies based on source files
1539 ## that have been used by multiple projects. If we do it here
1540 ## before we call generate_hierarchy(), we don't have to call it
1541 ## in generate_hierarchy() for each workspace.
1542 $self->{'projects'} = \
@projects;
1543 $self->{'project_info'} = \
%pi;
1545 if ($status && $genimpdep) {
1546 $self->add_implicit_project_dependencies($creator, $cwd);
1549 ## If we are generating the hierarchical workspaces, then do so
1550 $self->{'lib_locations'} = \
%liblocs;
1551 if ($self->get_hierarchy() || $self->workspace_per_project()) {
1552 my $orig = $self->{'workspace_name'};
1553 ($status, $errorString) = $self->generate_hierarchy($creator, \
@projects, \
%pi);
1554 $self->{'workspace_name'} = $orig;
1557 ## Reset the projects and project_info
1558 $self->{'projects'} = \
@projects;
1559 $self->{'project_info'} = \
%pi;
1561 return $status, $creator, $errorString;
1564 sub generate_project_files_fork
{
1566 my $status = (scalar @
{$self->{'project_files'}} == 0 ?
1 : 0);
1572 my $creator = $self->project_creator('child');
1573 my $cwd = $self->getcwd();
1574 my $impl = $self->get_assignment('implicit');
1575 my $postkey = $creator->get_dynamic() .
1576 $creator->get_static() . "-$self";
1577 my $previmpl = $impl;
1578 my $prevcache = $self->{'cacheok'};
1579 my %gstate = $creator->save_state();
1580 my $genimpdep = $self->generate_implicit_project_dependencies();
1585 $Data::Dumper
::Indent
= 0;
1587 ## Save this project creator setting for later use in the
1588 ## number_target_deps() method.
1589 $self->{'dependency_is_filename'} = $creator->dependency_is_filename();
1591 ## Remove the address portion of the $self string
1592 $postkey =~ s/=.*//;
1594 ## Set the source file callback on our project creator
1595 $creator->set_source_listing_callback([\
&source_listing_callback
, $self]);
1599 my $tmp = 'mpctmp00000000';
1601 ## remove old temp files
1602 my @tmpfiles = glob "${wdir}/mpctmp*";
1603 for my $file (@tmpfiles) {
1604 unlink $file || die "Error: Unable to delete tmp file $file in directory $wdir";
1607 my $num_tmp_files = scalar (@tmpfiles);
1609 $self->diagnostic("Multiprocess MPC removed $num_tmp_files existing files like \"mpctmp\*\" in $wdir.");
1611 foreach my $ofile (@
{$self->{'project_files'}}) {
1612 if ($#pids + 1 >= $num_workers) {
1613 waitpid(shift @pids, 0);
1618 ## open the output file in parent so it can die if there's an error
1619 open (FD
, ">${wdir}/$tmp") || die "Can't open $tmp for write";
1626 $self->{'pid'} = 'child';
1628 if (!$self->excluded($ofile)) {
1630 my $dir = $self->mpc_dirname($file);
1633 if (defined $self->{'scoped_assign'}->{$ofile}) {
1634 ## Handle the implicit assignment
1635 my $oi = $self->{'scoped_assign'}->{$ofile}->{'implicit'};
1641 ## Handle the cmdline assignment
1642 my $cmdline = $self->{'scoped_assign'}->{$ofile}->{'cmdline'};
1643 if (defined $cmdline && $cmdline ne '') {
1644 ## Save the cacheok value
1645 $prevcache = $self->{'cacheok'};
1647 ## Get the current parameters and process the command line
1648 my %parameters = $self->current_parameters();
1649 $self->process_cmdline($cmdline, \
%parameters);
1651 ## Set the parameters on the creator
1652 $creator->restore_state(\
%parameters);
1657 ## If we are generating implicit projects and the file is a
1658 ## directory, then we set the dir to the file and empty the file
1659 if ($impl && -d
$file) {
1663 ## If the implicit assignment value was not a number, then
1664 ## we will add this value to our base projects.
1665 if ($impl !~ /^\d+$/) {
1666 my $bps = $creator->get_baseprojs();
1667 push(@
$bps, split(/\s+/, $impl));
1669 $self->{'cacheok'} = 0;
1673 ## Generate the key for this project file
1674 my $prkey = $self->getcwd() . '/' .
1675 ($file eq '' ?
$dir : $file) . "-$postkey";
1677 ## We must change to the subdirectory for
1678 ## which this project file is intended
1680 if ($self->cd($dir)) {
1681 my $files_written = [];
1682 my $gen_proj_info = [];
1683 my $gen_lib_locs = {};
1685 if ($self->{'cacheok'} && defined $allprojects{$prkey}) {
1686 $files_written = $allprojects{$prkey};
1687 $gen_proj_info = $allprinfo{$prkey};
1688 $gen_lib_locs = $allliblocs{$prkey};
1693 $status = $creator->generate($self->mpc_basename($file));
1695 ## If any one project file fails, then stop
1696 ## processing altogether.
1698 # save the status info and exit. the parent will
1700 print FD
"$status|Unable to process " .
1701 ($file eq '' ?
" in $dir" : $file) . "\n";
1703 exit(1); # child error
1706 ## Get the individual project information and
1707 ## generated file name(s)
1708 $files_written = $creator->get_files_written();
1709 $gen_proj_info = $creator->get_project_info();
1710 $gen_lib_locs = $creator->get_lib_locations();
1715 print FD
"$status|''|$self->{'cacheok'}|$previmpl|$prevcache\n";
1716 print FD
"$ofile|$prkey|$dir|$cwd|$restore\n";
1718 print FD Dumper
($files_written), "\n";
1719 print FD Dumper
($gen_proj_info), "\n";
1720 print FD Dumper
($gen_lib_locs), "\n";
1722 # there's a callback that sets the project file list
1723 # since we can't callback between processes we store
1724 # the list for later
1725 print FD Dumper
($self->{'project_file_list'}), "\n";
1729 ## Unable to change to the directory.
1730 ## We don't restore the state before we leave,
1731 ## but that's ok since we will be exiting soon.
1732 print FD
"$status|Unable to change directory to $dir\n";
1734 exit (1); # child error
1739 ## This one was excluded, so status is ok
1740 ## no need to set though since the child will exit.
1744 exit(0); # child is finished
1749 # this will also reap any zombies
1753 my ($msg, $cacheok, $ofile, $prkey, $dir, $restore);
1755 # read the children's stored data
1756 my @kid_data = glob "${wdir}/mpctmp*";
1758 for my $kd (@kid_data) {
1759 open (FD
, "<$kd") || die "Can't open $kd for read";
1761 ($status, $msg, $cacheok, $previmpl, $prevcache) = split /\|/, <FD
>;
1764 return $status, $creator, $msg;
1767 ($ofile, $prkey, $dir, $cwd, $restore) = split /\|/, <FD
>;
1770 my $files_written = $VAR1;
1773 my $gen_proj_info = $VAR1;
1775 # have to reconstitute gen_lib_locs in the same order it was
1776 # created or else multi-process implicit dependency may differ from
1780 for my $k (sort { substr($a, 0 , index ($a, '|')) <=>
1781 substr ($b, 0, index ($b, '|')) } keys %$VAR1) {
1783 $gen_lib_locs->{substr ($k, index ($k, '|') + 1)} =
1787 # have to reconstitute project_file_list in the same order it was
1788 # created or else multi-process implicit dependency may differ from
1791 for my $k (sort { substr($a, 0 , index ($a, '|')) <=>
1792 substr ($b, 0, index ($b, '|')) } keys %$VAR1) {
1794 $self->{'project_file_list'}->{substr ($k, index ($k, '|') + 1)} =
1798 $self->{'cacheok'} = $cacheok;
1799 if ($self->cd($dir)) {
1800 if ($self->{'cacheok'} && defined $allprojects{$prkey}) {
1802 $files_written = $allprojects{$prkey};
1803 $gen_proj_info = $allprinfo{$prkey};
1804 $gen_lib_locs = $allliblocs{$prkey};
1808 # file is already generated. check status
1811 ## We don't restore the state before we leave,
1812 ## but that's ok since we will be exiting right now.
1813 return $status, $creator, $msg;
1816 ## Get the individual project information and
1817 ## generated file name(s)
1818 if ($self->{'cacheok'}) {
1820 $allprojects{$prkey} = $files_written;
1821 $allprinfo{$prkey} = $gen_proj_info;
1822 $allliblocs{$prkey} = $gen_lib_locs;
1825 push(@
{$self->{'mpc_to_output'}->{$ofile}}, @
$files_written);
1829 $self->save_project_info($files_written, $gen_proj_info,
1830 $gen_lib_locs, $dir,
1831 \
@projects, \
%pi, \
%liblocs);
1834 ## Unable to change to the directory.
1835 ## We don't restore the state before we leave,
1836 ## but that's ok since we will be exiting soon.
1837 return 0, $creator, $msg;
1841 ## Return things to the way they were
1842 $impl = $previmpl if (defined $self->{'scoped_assign'}->{$ofile});
1844 $self->{'cacheok'} = $prevcache;
1845 $creator->restore_state(\
%gstate);
1849 ## Add implict project dependencies based on source files
1850 ## that have been used by multiple projects. If we do it here
1851 ## before we call generate_hierarchy(), we don't have to call it
1852 ## in generate_hierarchy() for each workspace.
1853 $self->{'projects'} = \
@projects;
1854 $self->{'project_info'} = \
%pi;
1856 if ($status && $genimpdep) {
1857 #print "aipd: $cwd\n", Dumper ($creator), "\n";
1858 $self->add_implicit_project_dependencies($creator, $cwd);
1861 ## If we are generating the hierarchical workspaces, then do so
1862 $self->{'lib_locations'} = \
%liblocs;
1863 if ($self->get_hierarchy() || $self->workspace_per_project()) {
1864 my $orig = $self->{'workspace_name'};
1865 ($status, $errorString) = $self->generate_hierarchy($creator, \
@projects, \
%pi);
1866 $self->{'workspace_name'} = $orig;
1869 ## Reset the projects and project_info
1870 $self->{'projects'} = \
@projects;
1871 $self->{'project_info'} = \
%pi;
1873 return $status, $creator, $errorString;
1876 sub send_to_parent
{
1881 my $sock = new IO
::Socket
::INET
(
1882 PeerAddr
=> 'localhost',
1887 if (!defined ($sock)) {
1888 die "Child could not create socket";
1891 map { print $sock "$_\n"; } @
$arr;
1895 sub generate_project_files_fork_socket
{
1897 my $status = (scalar @
{$self->{'project_files'}} == 0 ?
1 : 0);
1903 my $creator = $self->project_creator('child');
1904 my $cwd = $self->getcwd();
1905 my $impl = $self->get_assignment('implicit');
1906 my $postkey = $creator->get_dynamic() .
1907 $creator->get_static() . "-$self";
1908 my $previmpl = $impl;
1909 my $prevcache = $self->{'cacheok'};
1910 my %gstate = $creator->save_state();
1911 my $genimpdep = $self->generate_implicit_project_dependencies();
1916 $Data::Dumper
::Indent
= 0;
1918 ## Save this project creator setting for later use in the
1919 ## number_target_deps() method.
1920 $self->{'dependency_is_filename'} = $creator->dependency_is_filename();
1922 ## Remove the address portion of the $self string
1923 $postkey =~ s/=.*//;
1925 ## Set the source file callback on our project creator
1926 $creator->set_source_listing_callback([\
&source_listing_callback
, $self]);
1930 my @pdata; # parents data sent from children.
1932 ## setup workers' data
1937 my $num_prj_files = $#{$self->{'project_files'}} + 1;
1939 ## reduce the number of workers if necessary
1940 ## what if $num_workers > SOMAXCONN?? (unlikely)
1941 if ($num_workers > SOMAXCONN
) {
1942 $self->diagnostic("Multiprocess MPC reducing # workers from $num_workers to " . SOMAXCONN
. ", the max # of queued connections");
1943 $num_workers = SOMAXCONN
;
1946 if ($num_workers > $num_prj_files) {
1947 # don't fork more workers than there are jobs
1948 $self->diagnostic("Multiprocess MPC reducing # workers from $num_workers to $num_prj_files, the number of project files.");
1949 $num_workers = $num_prj_files;
1952 my $num_per_worker = int ($num_prj_files / $num_workers);
1953 my $num_lines_per_prj = 6;
1955 $self->diagnostic("Multiprocess MPC using $num_workers workers to process $num_prj_files project files.");
1957 for (my $wctr = 0; $wctr < $num_workers; ++$wctr) {
1958 $beg = $wctr * $num_per_worker;
1959 $fin = $beg + $num_per_worker - 1;
1961 @
{$wdata[$wctr]} = @
{$self->{'project_files'}}[$beg..$fin];
1964 ## give any remaining data to last worker.
1965 if ($num_prj_files > $num_per_worker * $num_workers) {
1966 push @
{$wdata[$num_workers - 1]} ,
1967 @
{$self->{'project_files'}}[$num_per_worker
1968 * $num_workers..$#{$self->{'project_files'}}];
1972 ## Setup listener. Do this before fork so that (in the rare case)
1973 ## when child tries to send data before the accept(), the socket
1974 ## is at least initialized.
1975 my $sock = new IO
::Socket
::INET
(
1976 LocalHost
=> 'localhost',
1977 LocalPort
=> $wport,
1979 Listen
=> $num_workers,
1982 if (!defined ($sock)) {
1983 die "Error setting up parent listener";
1986 ## spawn the workers.
1988 while ($id < $num_workers) {
1989 # use pipes as barrier
1995 ## after fork, child knows its id and which data to use.
1996 $self->{'pid'} = 'child';
2002 if ($self->{pid
} eq 'parent') {
2003 $self->diagnostic("Multiprocess MPC using port $wport.");
2005 # read the data from the kids
2006 for (my $ctr = 0; $ctr < $num_workers; ++$ctr) {
2007 my $handle = $sock->accept();
2008 die "Accept error" if !$handle;
2010 @
{$pdata[$id]} = <$handle>;
2012 # each project as 6 records
2013 if ((($#{$pdata[$id]} + 1) / $num_lines_per_prj) != $num_per_worker) {
2014 if ($#{$pdata[$id]} != 0) {
2015 # 0 indicates a failed status which will be delt with later
2016 if (($id == $num_workers - 1) && ((($#{$pdata[$id]} + 1) / $num_lines_per_prj) != $num_per_worker + $#{$self->{'project_files'}} + 1 - ($num_workers * $num_per_worker))) {
2017 # The last child may have more than num_per_worker records
2018 my $rec = $#{$pdata[$id]} + 1;
2019 my $exp = $num_per_worker * $num_lines_per_prj;
2020 die "There is an error in the child data. Expected $exp. Received $rec";
2025 # all data has been read
2029 ## This is the code the workers run.
2031 ## generate projects
2033 foreach my $ofile (@
{$wdata[$id]}) {
2034 if (!$self->excluded($ofile)) {
2036 my $dir = $self->mpc_dirname($file);
2039 if (defined $self->{'scoped_assign'}->{$ofile}) {
2040 ## Handle the implicit assignment
2041 my $oi = $self->{'scoped_assign'}->{$ofile}->{'implicit'};
2047 ## Handle the cmdline assignment
2048 my $cmdline = $self->{'scoped_assign'}->{$ofile}->{'cmdline'};
2049 if (defined $cmdline && $cmdline ne '') {
2050 ## Save the cacheok value
2051 $prevcache = $self->{'cacheok'};
2053 ## Get the current parameters and process the command line
2054 my %parameters = $self->current_parameters();
2055 $self->process_cmdline($cmdline, \
%parameters);
2057 ## Set the parameters on the creator
2058 $creator->restore_state(\
%parameters);
2063 ## If we are generating implicit projects and the file is a
2064 ## directory, then we set the dir to the file and empty the file
2065 if ($impl && -d
$file) {
2069 ## If the implicit assignment value was not a number, then
2070 ## we will add this value to our base projects.
2071 if ($impl !~ /^\d+$/) {
2072 my $bps = $creator->get_baseprojs();
2073 push(@
$bps, split(/\s+/, $impl));
2075 $self->{'cacheok'} = 0;
2079 ## Generate the key for this project file
2080 my $prkey = $self->getcwd() . '/' .
2081 ($file eq '' ?
$dir : $file) . "-$postkey";
2083 ## We must change to the subdirectory for
2084 ## which this project file is intended
2086 if ($self->cd($dir)) {
2087 my $files_written = [];
2088 my $gen_proj_info = [];
2089 my $gen_lib_locs = {};
2091 if ($self->{'cacheok'} && defined $allprojects{$prkey}) {
2092 $files_written = $allprojects{$prkey};
2093 $gen_proj_info = $allprinfo{$prkey};
2094 $gen_lib_locs = $allliblocs{$prkey};
2099 $status = $creator->generate($self->mpc_basename($file));
2101 ## If any one project file fails, then stop
2102 ## processing altogether.
2104 # save the status info and exit. the parent will
2107 push @cdata, "$status|Unable to process " .
2108 ($file eq '' ?
" in $dir" : $file) . "\n";
2110 $self->send_to_parent(\
@cdata);
2111 exit(1); # child error
2114 ## Get the individual project information and
2115 ## generated file name(s)
2116 $files_written = $creator->get_files_written();
2117 $gen_proj_info = $creator->get_project_info();
2118 $gen_lib_locs = $creator->get_lib_locations();
2122 push @cdata, "$status|''|$self->{'cacheok'}|$previmpl|$prevcache";
2123 push @cdata, "$ofile|$prkey|$dir|$cwd|$restore";
2124 push @cdata, Dumper
($files_written);
2125 push @cdata, Dumper
($gen_proj_info);
2126 push @cdata, Dumper
($gen_lib_locs);
2128 # there's a callback that sets the project file list
2129 # since we can't callback between processes we store
2130 # the list for later
2131 push @cdata, Dumper
($self->{'project_file_list'});
2137 ## Unable to change to the directory.
2138 ## We don't restore the state before we leave,
2139 ## but that's ok since we will be exiting soon.
2141 push @cdata, "$status|Unable to change directory to $dir\n";
2142 $self->send_to_parent(\
@cdata);
2144 exit (1); # child error
2147 ## Return things to the way they were
2148 $impl = $previmpl if (defined $self->{'scoped_assign'}->{$ofile});
2150 $self->{'cacheok'} = $prevcache;
2151 $creator->restore_state(\
%gstate);
2155 ## This one was excluded, so status is ok
2156 ## no need to set though since the child will exit.
2161 # send all the data at once.
2162 $self->send_to_parent(\
@cdata);
2169 # This is the parent again.
2172 # this will reap any zombies
2176 my ($msg, $cacheok, $ofile, $prkey, $dir, $restore);
2178 # read the children's stored data
2179 for (my $i = 0; $i < $num_workers; ++$i) {
2180 for (my $j = 0; $j < $#{$pdata[$i]} + 1; ++$j) {
2181 ($status, $msg, $cacheok, $previmpl, $prevcache) = split /\|/, ${$pdata[$i]}[$j++];
2183 # check that the child was successful
2185 return $status, $creator, $msg;
2188 ($ofile, $prkey, $dir, $cwd, $restore) = split /\|/, ${$pdata[$i]}[$j++];
2190 eval (${$pdata[$i]}[$j++]);
2191 my $files_written = $VAR1;
2193 eval (${$pdata[$i]}[$j++]);
2194 my $gen_proj_info = $VAR1;
2196 # have to reconstitute gen_lib_locs in the same order it was
2197 # created or else multi-process implicit dependency may differ from
2199 eval (${$pdata[$i]}[$j++]);
2201 for my $k (sort { substr($a, 0 , index ($a, '|')) <=>
2202 substr ($b, 0, index ($b, '|')) } keys %$VAR1) {
2204 $gen_lib_locs->{substr ($k, index ($k, '|') + 1)} =
2208 # have to reconstitute project_file_list in the same order it was
2209 # created or else multi-process implicit dependency may differ from
2211 eval (${$pdata[$i]}[$j]);
2212 for my $k (sort { substr($a, 0 , index ($a, '|')) <=>
2213 substr ($b, 0, index ($b, '|')) } keys %$VAR1) {
2215 $self->{'project_file_list'}->{substr ($k, index ($k, '|') + 1)} =
2219 $self->{'cacheok'} = $cacheok;
2220 if ($self->cd($dir)) {
2221 if ($self->{'cacheok'} && defined $allprojects{$prkey}) {
2223 $files_written = $allprojects{$prkey};
2224 $gen_proj_info = $allprinfo{$prkey};
2225 $gen_lib_locs = $allliblocs{$prkey};
2229 # file is already generated. check status
2232 ## We don't restore the state before we leave,
2233 ## but that's ok since we will be exiting right now.
2234 return $status, $creator, $msg;
2237 ## Get the individual project information and
2238 ## generated file name(s)
2239 if ($self->{'cacheok'}) {
2241 $allprojects{$prkey} = $files_written;
2242 $allprinfo{$prkey} = $gen_proj_info;
2243 $allliblocs{$prkey} = $gen_lib_locs;
2246 push(@
{$self->{'mpc_to_output'}->{$ofile}}, @
$files_written);
2250 $self->save_project_info($files_written, $gen_proj_info,
2251 $gen_lib_locs, $dir,
2252 \
@projects, \
%pi, \
%liblocs);
2256 ## Unable to change to the directory.
2257 ## We don't restore the state before we leave,
2258 ## but that's ok since we will be exiting soon.
2259 return 0, $creator, $msg;
2263 ## Return things to the way they were
2264 $impl = $previmpl if (defined $self->{'scoped_assign'}->{$ofile});
2266 $self->{'cacheok'} = $prevcache;
2267 $creator->restore_state(\
%gstate);
2272 ## Add implict project dependencies based on source files
2273 ## that have been used by multiple projects. If we do it here
2274 ## before we call generate_hierarchy(), we don't have to call it
2275 ## in generate_hierarchy() for each workspace.
2276 $self->{'projects'} = \
@projects;
2277 $self->{'project_info'} = \
%pi;
2279 if ($status && $genimpdep) {
2280 #print "aipd: $cwd\n", Dumper ($creator), "\n";
2281 $self->add_implicit_project_dependencies($creator, $cwd);
2284 ## If we are generating the hierarchical workspaces, then do so
2285 $self->{'lib_locations'} = \
%liblocs;
2286 if ($self->get_hierarchy() || $self->workspace_per_project()) {
2287 my $orig = $self->{'workspace_name'};
2288 ($status, $errorString) = $self->generate_hierarchy($creator, \
@projects, \
%pi);
2289 $self->{'workspace_name'} = $orig;
2292 ## Reset the projects and project_info
2293 $self->{'projects'} = \
@projects;
2294 $self->{'project_info'} = \
%pi;
2296 return $status, $creator, $errorString;
2300 sub array_contains
{
2301 my($self, $left, $right) = @_;
2304 ## Initialize the hash keys with the left side array
2305 @check{@
$left} = ();
2307 ## Check each element on the right against the left.
2308 foreach my $r (@
$right) {
2309 return 1 if (exists $check{$r});
2316 sub non_intersection
{
2317 my($self, $left, $right, $over) = @_;
2321 ## Initialize the hash keys with the left side array
2322 @check{@
$left} = ();
2324 ## Check each element on the right against the left.
2325 ## Store anything that isn't in the left side in the over array.
2326 foreach my $r (@
$right) {
2327 if (exists $check{$r}) {
2338 sub indirect_dependency
{
2339 my($self, $dir, $ccheck, $cfile) = @_;
2341 $self->{'indirect_checked'}->{$ccheck} = 1;
2342 if (index($self->{'project_info'}->{$ccheck}->[ProjectCreator
::DEPENDENCIES
], $cfile) >= 0) {
2346 my $deps = $self->create_array(
2347 $self->{'project_info'}->{$ccheck}->[ProjectCreator
::DEPENDENCIES
]);
2348 foreach my $dep (@
$deps) {
2349 if (defined $self->{'project_info'}->{"$dir$dep"} &&
2350 !defined $self->{'indirect_checked'}->{"$dir$dep"} &&
2351 $self->indirect_dependency($dir, "$dir$dep", $cfile)) {
2361 sub add_implicit_project_dependencies
{
2362 my($self, $creator, $cwd) = @_;
2366 ## Take the current working directory and regular expression'ize it.
2367 $cwd = $self->escape_regex_special($cwd);
2369 ## Look at each projects file list and check it against all of the
2370 ## others. If any of the other projects file lists contains anothers
2371 ## file, then they are dependent (due to build parallelism). So, we
2372 ## append the dependency and remove the file in question from the
2373 ## project so that the next time around the foreach, we don't find it
2374 ## as a dependent on the one that we just modified.
2375 my @pflkeys = keys %{$self->{'project_file_list'}};
2377 foreach my $key (@pflkeys) {
2378 foreach my $ikey (@pflkeys) {
2379 ## Not the same project and
2380 ## The same directory and
2381 ## We've not already added a dependency to this project
2382 if ($key ne $ikey &&
2383 ($self->{'project_file_list'}->{$key}->[1] eq
2384 $self->{'project_file_list'}->{$ikey}->[1]) &&
2385 (!defined $bidir{$ikey} ||
2386 !$self->array_contains($bidir{$ikey}, [$key]))) {
2388 if ($self->non_intersection(
2389 $self->{'project_file_list'}->{$key}->[2],
2390 $self->{'project_file_list'}->{$ikey}->[2],
2392 ## The project contains shared source files, so we need to
2393 ## look into adding an implicit inter-project dependency.
2394 $save{$ikey} = $self->{'project_file_list'}->{$ikey}->[2];
2395 $self->{'project_file_list'}->{$ikey}->[2] = \
@over;
2396 if (defined $bidir{$key}) {
2397 push(@
{$bidir{$key}}, $ikey);
2400 $bidir{$key} = [$ikey];
2402 my $append = $creator->translate_value('after', $key);
2403 my $file = $self->{'project_file_list'}->{$ikey}->[0];
2404 my $dir = $self->{'project_file_list'}->{$ikey}->[1];
2405 my $cfile = $creator->translate_value('after', $ikey);
2406 ## Remove our starting directory from the projects directory
2407 ## to get the right part of the directory to prepend.
2408 $dir =~ s/^$cwd[\/\\]*//;
2410 ## Turn the append value into a key for 'project_info' and
2411 ## prepend the directory to the file.
2412 my $ccheck = $append;
2416 $ccheck = "$dir$ccheck";
2417 $file = "$dir$file";
2420 ## If the append value key contains a reference to the project
2421 ## that we were going to append the dependency value, then
2422 ## ignore the generated dependency. It is redundant and
2423 ## quite possibly wrong.
2424 $self->{'indirect_checked'} = {};
2425 if (defined $self->{'project_info'}->{$file} &&
2426 (!defined $self->{'project_info'}->{$ccheck} ||
2427 !$self->indirect_dependency($dir, $ccheck, $cfile))) {
2428 ## Append the dependency
2429 $self->{'project_info'}->{$file}->[ProjectCreator
::DEPENDENCIES
] .= " $append";
2436 ## Restore the modified values in case this method is called again
2437 ## which is the case when using the -hierarchy option.
2438 foreach my $skey (keys %save) {
2439 $self->{'project_file_list'}->{$skey}->[2] = $save{$skey};
2445 return $_[0]->{'projects'};
2449 sub get_project_info
{
2450 return $_[0]->{'project_info'};
2454 sub get_lib_locations
{
2455 return $_[0]->{'lib_locations'};
2459 sub get_first_level_directory
{
2460 my($self, $file) = @_;
2462 if (($file =~ tr/\///) > 0) {
2464 $dir =~ s/^([^\/]+\/).*/$1/;
2473 sub get_associated_projects
{
2474 return $_[0]->{'associated'};
2478 sub sort_within_group
{
2479 my($self, $list, $start, $end) = @_;
2483 my $cmax = ($end - $start) + 1;
2488 ## Put the projects in the order specified
2489 ## by the project dependencies.
2490 for (my $i = $start; $i <= $end; ++$i) {
2491 ## If our moved project equals our previously moved project then
2492 ## we count this as a possible circular dependency.
2495 (defined $$movepjs[0] && defined $$prevpjs[0] &&
2496 $$movepjs[0] == $$prevpjs[0] && $$movepjs[1] == $$prevpjs[1])) {
2503 ## Detect circular dependencies
2504 if ($ccount > $cmax) {
2506 foreach my $mvgr (@
$movepjs) {
2507 push(@prjs, $$list[$mvgr]);
2509 my $other = $$movepjs[0] - 1;
2510 if ($other < $start || $other == $$movepjs[1] || !defined $$list[$other]) {
2513 $self->warning('Circular dependency detected while processing the ' .
2514 ($self->{'current_input'} eq '' ?
2515 'default' : $self->{'current_input'}) .
2517 'The following projects are involved: ' .
2518 (defined $other ?
"$$list[$other], " : '') .
2519 join(' and ', @prjs));
2523 ## Keep track of the previous project movement
2525 $prevpjs = $movepjs;
2526 $movepjs = [] if ($previ < $i);
2529 $deps = $self->get_validated_ordering($$list[$i]);
2530 if (defined $$deps[0]) {
2531 my $baseproj = ($self->{'dependency_is_filename'} ?
2532 $self->mpc_basename($$list[$i]) :
2533 $self->{'project_info'}->{$$list[$i]}->[ProjectCreator
::PROJECT_NAME
]);
2535 foreach my $dep (@
$deps) {
2536 if ($baseproj ne $dep) {
2537 ## See if the dependency is listed after this project
2538 for (my $j = $i + 1; $j <= $end; ++$j) {
2539 my $ldep = ($self->{'dependency_is_filename'} ?
2540 $self->mpc_basename($$list[$j]) :
2541 $self->{'project_info'}->{$$list[$j]}->[ProjectCreator
::PROJECT_NAME
]);
2542 if ($ldep eq $dep) {
2543 $movepjs = [$i, $j];
2544 ## If so, move it in front of the current project.
2545 ## The original code, which had splices, didn't always
2546 ## work correctly (especially on AIX for some reason).
2547 my $save = $$list[$j];
2548 for (my $k = $j; $k > $i; --$k) {
2549 $$list[$k] = $$list[$k - 1];
2553 ## Mark that an entry has been moved
2566 sub build_dependency_chain
{
2567 my($self, $name, $len, $list, $ni, $glen, $groups, $map, $gdeps) = @_;
2568 my $deps = $self->get_validated_ordering($name);
2570 if (defined $$deps[0]) {
2571 foreach my $dep (@
$deps) {
2572 ## Find the item in the list that matches our current dependency
2573 my $mapped = $$map{$dep};
2574 if (defined $mapped) {
2575 for (my $i = 0; $i < $len; $i++) {
2576 if ($$list[$i] eq $mapped) {
2578 ## Locate the group number to which the dependency belongs
2579 for (my $j = 0; $j < $glen; $j++) {
2580 if ($i >= $$groups[$j]->[0] && $i <= $$groups[$j]->[1]) {
2583 ## Add every project in the group to the dependency chain
2584 for (my $k = $$groups[$j]->[0]; $k <= $$groups[$j]->[1]; $k++) {
2585 my $ldep = $self->mpc_basename($$list[$k]);
2586 if (!exists $$gdeps{$ldep}) {
2588 $self->build_dependency_chain($$list[$k],
2609 sub sort_by_groups
{
2610 my($self, $list, $grindex) = @_;
2611 my @groups = @
$grindex;
2612 my $llen = scalar(@
$list);
2614 ## Check for duplicates first before we attempt to sort the groups.
2615 ## If there is a duplicate, we quietly return immediately. The
2616 ## duplicates will be flagged as an error when creating the main
2619 foreach my $proj (@
$list) {
2620 my $base = $self->mpc_basename($proj);
2621 return undef if (defined $dupcheck{$base});
2622 $dupcheck{$base} = $proj;
2625 my %circular_checked;
2626 for (my $gi = 0; $gi <= $#groups; ++$gi) {
2627 ## Detect circular dependencies
2628 if (!$circular_checked{$gi}) {
2629 $circular_checked{$gi} = 1;
2630 for (my $i = $groups[$gi]->[0]; $i <= $groups[$gi]->[1]; ++$i) {
2632 $self->build_dependency_chain($$list[$i], $llen, $list, $gi,
2633 $#groups + 1, \
@groups,
2634 \
%dupcheck, \
%gdeps);
2635 if (exists $gdeps{$self->mpc_basename($$list[$i])}) {
2636 ## There was a cirular dependency, get all of the directories
2639 foreach my $gdep (keys %gdeps) {
2640 $dirs{$self->mpc_dirname($dupcheck{$gdep})} = 1;
2643 ## If the current directory was involved, translate that into
2644 ## a directory relative to the start directory.
2645 if (defined $dirs{'.'}) {
2646 my $cwd = $self->getcwd();
2647 my $start = $self->getstartdir();
2648 if ($cwd ne $start) {
2649 my $startre = $self->escape_regex_special($start);
2651 $cwd =~ s/^$startre[\\\/]//;
2656 ## Display a warining to the user
2657 my @keys = sort keys %dirs;
2658 $self->warning('Circular directory dependency detected in the ' .
2659 ($self->{'current_input'} eq '' ?
2660 'default' : $self->{'current_input'}) .
2662 'The following director' .
2663 ($#keys == 0 ?
'y is' : 'ies are') .
2664 ' involved: ' . join(', ', @keys));
2670 ## Build up the group dependencies
2672 for (my $i = $groups[$gi]->[0]; $i <= $groups[$gi]->[1]; ++$i) {
2673 my $deps = $self->get_validated_ordering($$list[$i]);
2674 @gdeps{@
$deps} = () if (defined $$deps[0]);
2677 ## Search the rest of the groups for any of the group dependencies
2678 for (my $gj = $gi + 1; $gj <= $#groups; ++$gj) {
2679 for (my $i = $groups[$gj]->[0]; $i <= $groups[$gj]->[1]; ++$i) {
2680 if (exists $gdeps{$self->mpc_basename($$list[$i])}) {
2681 ## Move this group ($gj) in front of the current group ($gi)
2683 for (my $j = $groups[$gi]->[1] + 1; $j <= $groups[$gj]->[1]; ++$j) {
2684 push(@save, $$list[$j]);
2686 my $offset = $groups[$gj]->[1] - $groups[$gi]->[1];
2687 for (my $j = $groups[$gi]->[1]; $j >= $groups[$gi]->[0]; --$j) {
2688 $$list[$j + $offset] = $$list[$j];
2690 for (my $j = 0; $j <= $#save; ++$j) {
2691 $$list[$groups[$gi]->[0] + $j] = $save[$j];
2694 ## Update the group indices
2695 my $shiftamt = ($groups[$gi]->[1] - $groups[$gi]->[0]) + 1;
2696 for (my $j = $gi + 1; $j <= $gj; ++$j) {
2697 $groups[$j]->[0] -= $shiftamt;
2698 $groups[$j]->[1] -= $shiftamt;
2700 my @grsave = @
{$groups[$gi]};
2701 $grsave[0] += $offset;
2702 $grsave[1] += $offset;
2703 for (my $j = $gi; $j < $gj; ++$j) {
2704 $groups[$j] = $groups[$j + 1];
2705 $circular_checked{$j} = $circular_checked{$j + 1};
2707 $groups[$gj] = \
@grsave;
2708 $circular_checked{$gj} = 1;
2710 ## Start over from the first group
2713 ## Exit from the outter ($gj) loop
2723 sub sort_dependencies
{
2724 my($self, $projects, $groups) = @_;
2725 my @list = sort { return $self->sort_projects_by_directory($a, $b) + 0;
2727 ## The list above is sorted by directory in order to keep projects
2728 ## within the same directory together. Otherwise, when groups are
2729 ## created we may get multiple groups for the same directory.
2731 ## Put the projects in the order specified
2732 ## by the project dependencies. We only need to do
2733 ## this if there is more than one element in the array.
2735 ## If the parameter wasn't passed in or it was passed in
2736 ## and was true, sort with directory groups in mind
2737 if (!defined $groups || $groups) {
2738 ## First determine the individual groups
2740 my $previous = [0, undef];
2741 for (my $li = 0; $li <= $#list; ++$li) {
2742 my $dir = $self->get_first_level_directory($list[$li]);
2743 if (!defined $previous->[1]) {
2744 $previous = [$li, $dir];
2746 elsif ($previous->[1] ne $dir) {
2747 push(@grindex, [$previous->[0], $li - 1]);
2748 $previous = [$li, $dir];
2751 push(@grindex, [$previous->[0], $#list]);
2753 ## Next, sort the individual groups
2754 foreach my $gr (@grindex) {
2755 $self->sort_within_group(\
@list, @
$gr) if ($$gr[0] != $$gr[1]);
2758 ## Now sort the groups as single entities
2759 $self->sort_by_groups(\
@list, \
@grindex) if ($#grindex > 0);
2762 $self->sort_within_group(\
@list, 0, $#list);
2770 sub number_target_deps
{
2771 my($self, $projects, $pjs, $targets, $groups) = @_;
2772 my @list = $self->sort_dependencies($projects, $groups);
2774 ## This block of code must be done after the list of dependencies
2775 ## has been sorted in order to get the correct project numbers.
2776 for (my $i = 0; $i <= $#list; ++$i) {
2777 my $project = $list[$i];
2778 if (defined $$pjs{$project}) {
2779 my($name, $deps) = @
{$$pjs{$project}};
2780 if (defined $deps && $deps ne '') {
2783 @dhash{@
{$self->create_array($deps)}} = ();
2785 ## For each dependency, search in the sorted list
2786 ## up to the point of this project for the projects
2787 ## that this one depends on. When the project is
2788 ## found, we put the target number in the numbers array.
2789 for (my $j = 0; $j < $i; ++$j) {
2790 ## If the dependency is a filename, then take the basename of
2791 ## the project file. Otherwise, get the project name based on
2792 ## the project file from the "project_info".
2793 my $key = ($self->{'dependency_is_filename'} ?
2794 $self->mpc_basename($list[$j]) :
2795 $self->{'project_info'}->{$list[$j]}->[ProjectCreator
::PROJECT_NAME
]);
2796 push(@numbers, $j) if (exists $dhash{$key});
2799 ## Store the array in the hash keyed on the project file.
2800 $$targets{$project} = \
@numbers if (defined $numbers[0]);
2809 sub project_target_translation
{
2810 my($self, $case) = @_;
2813 ## Translate project names to avoid target collision with
2814 ## some versions of make.
2815 foreach my $key (keys %{$self->{'project_info'}}) {
2816 my $dir = $self->mpc_dirname($key);
2817 my $name = $self->{'project_info'}->{$key}->[ProjectCreator
::PROJECT_NAME
];
2819 ## We want to compare to the upper most directory. This will be the
2820 ## one that may conflict with the project name.
2821 $dir =~ s/[\/\\].*//;
2822 if (($case && $dir eq $name) || (!$case && lc($dir) eq lc($name))) {
2823 $map{$key} = "$name-target";
2834 my($self, $str) = @_;
2835 $self->warning("$self->{'current_input'}: $str.") if (defined $str);
2839 sub process_cmdline
{
2840 my($self, $cmdline, $parameters) = @_;
2842 ## Set cache use to default.
2843 $self->{'cacheok'} = $self->default_cacheok();
2845 if (defined $cmdline && $cmdline ne '') {
2846 my $args = $self->create_array($cmdline);
2848 ## Look for environment variables
2849 foreach my $arg (@
$args) {
2850 $self->replace_env_vars(\
$arg) if ($arg =~ /\$/);
2853 my $options = $self->options('MWC', {}, 0, @
$args);
2854 if (defined $options) {
2855 foreach my $key (keys %$options) {
2856 my $type = $self->is_set($key, $options);
2858 if (!defined $type) {
2859 ## This option was not used, so we ignore it
2861 elsif ($type eq 'ARRAY') {
2862 push(@
{$parameters->{$key}}, @
{$options->{$key}});
2864 elsif ($type eq 'HASH') {
2865 my $merge = ($key eq 'addtemp' || $key eq 'addproj');
2866 foreach my $hk (keys %{$options->{$key}}) {
2867 if ($merge && defined $parameters->{$key}->{$hk}) {
2868 push(@
{$parameters->{$key}->{$hk}}, @
{$options->{$key}->{$hk}});
2871 $parameters->{$key}->{$hk} = $options->{$key}->{$hk};
2875 elsif ($type eq 'SCALAR') {
2876 $parameters->{$key} = $options->{$key};
2880 ## Some option data members are named consistently with the MPC
2881 ## option name. In this case, we can use this foreach loop.
2882 foreach my $consistent_opt ('exclude', 'for_eclipse', 'gendot',
2883 'gfeature_file', 'into',
2884 'make_coexistence', 'recurse') {
2885 ## Issue warnings for the options provided by the user
2886 if ($self->is_set($consistent_opt, $options)) {
2887 $self->optionError("-$consistent_opt is ignored");
2891 ## For those that are inconsistent, we have special code to deal
2893 if ($self->is_set('reldefs', $options)) {
2894 $self->optionError('-noreldefs is ignored');
2897 ## Make sure no input files were specified (we can't handle it).
2898 if (defined $options->{'input'}->[0]) {
2899 $self->optionError('Command line files ' .
2900 'specified in a workspace are ignored');
2903 ## Determine if it's ok to use the cache
2904 my @cacheInvalidating = ('global', 'include', 'baseprojs',
2905 'template', 'ti', 'relative', 'language',
2906 'addtemp', 'addproj', 'feature_file',
2907 'features', 'use_env', 'expand_vars');
2908 foreach my $key (@cacheInvalidating) {
2909 if ($self->is_set($key, $options)) {
2910 $self->{'cacheok'} = 0;
2919 sub current_parameters
{
2921 my %parameters = $self->save_state();
2923 ## We always want the project creator to generate a toplevel
2924 $parameters{'toplevel'} = 1;
2929 sub project_creator
{
2932 if (not defined $pid) {
2938 ## NOTE: If the subclassed WorkspaceCreator name prefix does not
2939 ## match the name prefix of the ProjectCreator, this code
2940 ## will not work and the subclassed WorkspaceCreator will
2941 ## need to override this method.
2943 $str =~ s/Workspace/Project/;
2944 $str =~ s/=HASH.*//;
2946 ## Set up values for each project creator
2947 ## If we have command line arguments in the workspace, then
2948 ## we process them before creating the project creator
2949 my $cmdline = $self->get_assignment('cmdline');
2950 my %parameters = $self->current_parameters();
2951 $self->process_cmdline($cmdline, \
%parameters);
2953 ## Create the new project creator with the updated parameters
2954 return $str->new($parameters{'global'},
2955 $parameters{'include'},
2956 $parameters{'template'},
2958 $parameters{'dynamic'},
2959 $parameters{'static'},
2960 $parameters{'relative'},
2961 $parameters{'addtemp'},
2962 $parameters{'addproj'},
2963 $parameters{'progress'},
2964 $parameters{'toplevel'},
2965 $parameters{'baseprojs'},
2966 $self->{'global_feature_file'},
2967 $parameters{'relative_file'},
2968 $parameters{'feature_file'},
2969 $parameters{'features'},
2970 $parameters{'hierarchy'},
2971 $self->{'exclude'}->{$self->{'wctype'}},
2972 $self->make_coexistence(),
2973 $parameters{'name_modifier'},
2974 $parameters{'apply_project'},
2975 $self->{'generate_ins'} || $parameters{'genins'},
2977 $parameters{'language'},
2978 $parameters{'use_env'},
2979 $parameters{'expand_vars'},
2981 $parameters{'comments'},
2982 $self->{'for_eclipse'},
2993 sub make_coexistence
{
2994 return $_[0]->{'coexistence'};
2998 sub get_modified_workspace_name
{
2999 my($self, $name, $ext, $nows) = @_;
3000 my $nmod = $self->get_name_modifier();
3003 if (defined $nmod) {
3004 $nmod =~ s/\*/$name/g;
3008 ## If this is a per project workspace, then we should not
3009 ## modify the workspace name. It may overwrite another workspace
3010 ## but that's ok, it will only be a per project workspace.
3011 ## Also, if we don't want the workspace name attached ($nows) then
3012 ## we just return the name plus the extension.
3013 return "$name$ext" if ($nows || $self->{'per_project_workspace_name'});
3015 my $pwd = $self->getcwd();
3016 my $type = $self->{'wctype'};
3017 my $wsname = $self->get_workspace_name();
3019 if (!defined $previous_workspace_name{$type}->{$pwd}) {
3020 $previous_workspace_name{$type}->{$pwd} = $wsname;
3021 $self->{'current_workspace_name'} = undef;
3024 my $prefix = ($oname eq $wsname ?
$name : "$name.$wsname");
3025 $previous_workspace_name{$type}->{$pwd} = $wsname;
3026 while ($self->file_written("$prefix" .
3027 ($self->{'modified_count'} > 0 ?
3028 ".$self->{'modified_count'}" : '') .
3030 ++$self->{'modified_count'};
3032 $self->{'current_workspace_name'} =
3033 "$prefix" . ($self->{'modified_count'} > 0 ?
3034 ".$self->{'modified_count'}" : '') . "$ext";
3037 return (defined $self->{'current_workspace_name'} ?
3038 $self->{'current_workspace_name'} : "$name$ext");
3042 sub generate_recursive_input_list
{
3043 my($self, $dir, $exclude) = @_;
3044 return $self->extension_recursive_input_list($dir, $exclude, $wsext);
3048 sub verify_build_ordering
{
3050 foreach my $project (@
{$self->{'projects'}}) {
3051 $self->get_validated_ordering($project);
3056 sub get_validated_ordering
{
3057 my($self, $project) = @_;
3060 if (defined $self->{'ordering_cache'}->{$project}) {
3061 $deps = $self->{'ordering_cache'}->{$project};
3065 if (defined $self->{'project_info'}->{$project}) {
3066 my($name, $dstr) = @
{$self->{'project_info'}->{$project}};
3067 if (defined $dstr && $dstr ne '') {
3068 $deps = $self->create_array($dstr);
3069 my $dlen = scalar(@
$deps);
3070 for (my $i = 0; $i < $dlen; $i++) {
3071 my $dep = $$deps[$i];
3073 ## Avoid circular dependencies
3074 if ($dep ne $name && $dep ne $self->mpc_basename($project)) {
3075 foreach my $p (@
{$self->{'projects'}}) {
3076 if ($dep eq $self->{'project_info'}->{$p}->[ProjectCreator
::PROJECT_NAME
] ||
3077 $dep eq $self->mpc_basename($p)) {
3083 if ($self->{'verbose_ordering'}) {
3084 $self->warning("processing '$project' and '$name' references '$dep' which has " .
3085 "not been processed.");
3087 splice(@
$deps, $i, 1);
3093 ## If a project references itself, we must remove it
3094 ## from the list of dependencies.
3095 splice(@
$deps, $i, 1);
3102 $self->{'ordering_cache'}->{$project} = $deps;
3110 sub source_listing_callback
{
3111 my($self, $project_file, $project_name, $list) = @_;
3113 # have to keep projects in the the same order as if run in
3114 # single process. otherwise implicit dependencies produces
3116 if ($self->{'pid'} ne 'parent') {
3117 $project_name = ++$self->{'imp_dep_ctr'} . '|' . $project_name;
3120 $self->{'project_file_list'}->{$project_name} = [ $project_file,
3121 $self->getcwd(), $list ];
3125 sub sort_projects_by_directory
{
3126 my($self, $left, $right) = @_;
3127 my $sa = index($left, '/');
3128 my $sb = index($right, '/');
3130 if ($sa >= 0 && $sb == -1) {
3133 elsif ($sb >= 0 && $sa == -1) {
3136 return $left cmp $right;
3140 sub get_relative_dep_file
{
3141 my($self, $creator, $project, $dep) = @_;
3143 ## If the dependency is a filename, we have to find the key that
3144 ## matches the project file.
3145 if ($creator->dependency_is_filename()) {
3146 foreach my $key (keys %{$self->{'project_file_list'}}) {
3147 if ($self->{'project_file_list'}->{$key}->[0] eq $dep) {
3154 if (defined $self->{'project_file_list'}->{$dep}) {
3155 my $base = $self->{'project_file_list'}->{$dep}->[1];
3156 my @dirs = grep(!/^$/, split('/', $base));
3158 $project =~ s/^\///;
3159 for (my $i = 0; $i <= $#dirs; $i++) {
3160 my $dir = $dirs[$i];
3161 if ($project =~ s/^$dir\///) {
3169 my $dependee = $self->{'project_file_list'}->{$dep}->[0];
3171 return $base . '/' . $dependee;
3175 for (my $i = $last + 1; $i <= $#dirs; $i++) {
3176 $built .= $dirs[$i] . '/';
3178 $built .= $dependee;
3180 ## If the project contains a portion of the current working directory,
3181 ## we need to strip it off. If the workspace is a directory below one
3182 ## of the projects, the directory count will be incorrect due to the
3183 ## use of '..' within the project path.
3185 my $dir = $self->getcwd();
3186 while($dir =~ s!^[^/]*/!! &&
3187 ($re = $dir . '/' . ('../' x
(($dir =~ tr/\///) + 1))) &&
3188 $project !~ s!^$re!!) {
3191 ## The code above is tricky
3192 $self->debug("Project on which this project depends: $project");
3194 my $dircount = ($project =~ tr/\///);
3195 return ('../' x
$dircount) . $built;
3202 sub create_command_line_string
{
3207 foreach my $arg (@args) {
3209 if ($arg =~ /\$/ && $^O
ne 'MSWin32') {
3210 ## If we're not running on Windows and the command line argument
3211 ## contains a dollar sign, we need to wrap the argument in single
3212 ## quotes so that the UNIX shell does not interpret it.
3216 ## Unfortunately, the Windows command line shell does not
3217 ## understand single quotes correctly. So, we have the distinction
3218 ## above and handle dollar signs here too.
3219 $arg = "\"$arg\"" if ($arg =~ /[\s\*\$]/);
3232 sub print_workspace_comment
{
3236 if ($self->{'workspace_comments'}) {
3237 foreach my $line (@_) {
3244 sub get_initial_relative_values
{
3246 return $self->get_relative(), $self->get_expand_vars();
3250 sub get_secondary_relative_values
{
3251 return \
%ENV, $_[0]->get_expand_vars();
3255 sub convert_all_variables
{
3261 sub workspace_file_name
{
3263 return $self->get_modified_workspace_name($self->get_workspace_name(),
3264 $self->workspace_file_extension());
3270 my $line = $self->SUPER::relative
(shift);
3275 # ************************************************************
3276 # Virtual Methods To Be Overridden
3277 # ************************************************************
3279 sub requires_make_coexistence
{
3285 sub supports_make_coexistence
{
3291 sub generate_implicit_project_dependencies
{
3297 sub workspace_file_extension
{
3303 sub workspace_per_project
{
3309 sub default_verbose_ordering
{
3310 return 0; # Don't warning if there are missing dependencies.
3317 #my $creator = shift;
3325 #my $creator = shift;
3330 sub post_workspace
{
3333 #my $creator = shift;
3337 sub requires_forward_slashes
{
3342 sub get_additional_output
{
3345 ## This method should return an array reference of array references.
3346 ## For each entry, the array should be laid out as follows:
3347 ## [ <directory or undef to use the current output directory>,
3349 ## <function to write body of file, $self and $fh are first params>,
3350 ## <optional additional parameter 1>,
3352 ## <optional additional parameter N>