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 ## If the project path starts with ./ the code assumed that the top was
1347 ## the current directory and would end up not creating the workspace as
1348 ## it should have been. We will clean up the project directory and pass
1349 ## that to topname() instead.
1351 $clean =~ s/^\.[\/]+//;
1353 my($top, $rest) = $self->topname($clean);
1354 if (!defined $current) {
1356 push(@saved, $rest);
1357 $sinfo{$rest} = $projinfo{$prj};
1359 elsif ($top ne $current) {
1360 if ($current ne '.') {
1361 ## Write out the hierarchical workspace
1362 $self->cd($current);
1363 ($status, $errorString) = $self->generate_hierarchy($creator, \
@saved, \
%sinfo);
1365 $self->{'projects'} = \
@saved;
1366 $self->{'project_info'} = \
%sinfo;
1367 $self->{'workspace_name'} = $self->base_directory();
1368 ($status, $errorString) = $self->write_workspace($creator) if ($status);
1374 ## Start the next one
1378 $sinfo{$rest} = $projinfo{$prj};
1381 push(@saved, $rest);
1382 $sinfo{$rest} = $projinfo{$prj};
1385 if ($status && defined $current && $current ne '.') {
1386 $self->cd($current);
1387 ($status, $errorString) = $self->generate_hierarchy($creator, \
@saved, \
%sinfo);
1389 $self->{'projects'} = \
@saved;
1390 $self->{'project_info'} = \
%sinfo;
1391 $self->{'workspace_name'} = $self->base_directory();
1392 ($status, $errorString) = $self->write_workspace($creator) if ($status);
1397 return $status, $errorString;
1400 sub generate_project_files
{
1402 my $status = (scalar @
{$self->{'project_files'}} == 0 ?
1 : 0);
1406 my $creator = $self->project_creator();
1407 my $cwd = $self->getcwd();
1408 my $impl = $self->get_assignment('implicit');
1409 my $postkey = $creator->get_dynamic() .
1410 $creator->get_static() . "-$self";
1411 my $previmpl = $impl;
1412 my $prevcache = $self->{'cacheok'};
1413 my %gstate = $creator->save_state();
1414 my $genimpdep = $self->generate_implicit_project_dependencies();
1417 $Data::Dumper
::Indent
= 0;
1419 ## Save this project creator setting for later use in the
1420 ## number_target_deps() method.
1421 $self->{'dependency_is_filename'} = $creator->dependency_is_filename();
1423 ## Remove the address portion of the $self string
1424 $postkey =~ s/=.*//;
1426 ## Set the source file callback on our project creator
1427 $creator->set_source_listing_callback([\
&source_listing_callback
, $self]);
1429 foreach my $ofile (@
{$self->{'project_files'}}) {
1430 if (!$self->excluded($ofile)) {
1432 my $dir = $self->mpc_dirname($file);
1435 if (defined $self->{'scoped_assign'}->{$ofile}) {
1436 ## Handle the implicit assignment
1437 my $oi = $self->{'scoped_assign'}->{$ofile}->{'implicit'};
1443 ## Handle the cmdline assignment
1444 my $cmdline = $self->{'scoped_assign'}->{$ofile}->{'cmdline'};
1445 if (defined $cmdline && $cmdline ne '') {
1446 ## Save the cacheok value
1447 $prevcache = $self->{'cacheok'};
1449 ## Get the current parameters and process the command line
1450 my %parameters = $self->current_parameters();
1451 $self->process_cmdline($cmdline, \
%parameters);
1453 ## Set the parameters on the creator
1454 $creator->restore_state(\
%parameters);
1459 ## If we are generating implicit projects and the file is a
1460 ## directory, then we set the dir to the file and empty the file
1461 if ($impl && -d
$file) {
1465 ## If the implicit assignment value was not a number, then
1466 ## we will add this value to our base projects.
1467 if ($impl !~ /^\d+$/) {
1468 my $bps = $creator->get_baseprojs();
1469 push(@
$bps, split(/\s+/, $impl));
1471 $self->{'cacheok'} = 0;
1475 ## Generate the key for this project file
1476 my $prkey = $self->getcwd() . '/' .
1477 ($file eq '' ?
$dir : $file) . "-$postkey";
1479 ## We must change to the subdirectory for
1480 ## which this project file is intended
1481 if ($self->cd($dir)) {
1482 my $files_written = [];
1483 my $gen_proj_info = [];
1484 my $gen_lib_locs = {};
1485 if ($self->{'cacheok'} && defined $allprojects{$prkey}) {
1486 $files_written = $allprojects{$prkey};
1487 $gen_proj_info = $allprinfo{$prkey};
1488 $gen_lib_locs = $allliblocs{$prkey};
1493 $status = $creator->generate($self->mpc_basename($file));
1495 ## If any one project file fails, then stop
1496 ## processing altogether.
1498 ## We don't restore the state before we leave,
1499 ## but that's ok since we will be exiting right now.
1500 return $status, $creator,
1501 "Unable to process " . ($file eq '' ?
" in $dir" : $file);
1504 ## Get the individual project information and
1505 ## generated file name(s)
1506 $files_written = $creator->get_files_written();
1507 $gen_proj_info = $creator->get_project_info();
1508 $gen_lib_locs = $creator->get_lib_locations();
1510 if ($self->{'cacheok'}) {
1511 $allprojects{$prkey} = $files_written;
1512 $allprinfo{$prkey} = $gen_proj_info;
1513 $allliblocs{$prkey} = $gen_lib_locs;
1516 push(@
{$self->{'mpc_to_output'}->{$ofile}}, @
$files_written);
1519 $self->save_project_info($files_written, $gen_proj_info,
1520 $gen_lib_locs, $dir,
1521 \
@projects, \
%pi, \
%liblocs);
1524 ## Unable to change to the directory.
1525 ## We don't restore the state before we leave,
1526 ## but that's ok since we will be exiting soon.
1527 return 0, $creator, "Unable to change directory to $dir";
1530 ## Return things to the way they were
1531 $impl = $previmpl if (defined $self->{'scoped_assign'}->{$ofile});
1533 $self->{'cacheok'} = $prevcache;
1534 $creator->restore_state(\
%gstate);
1538 ## This one was excluded, so status is ok
1544 ## Add implicit project dependencies based on source files
1545 ## that have been used by multiple projects. If we do it here
1546 ## before we call generate_hierarchy(), we don't have to call it
1547 ## in generate_hierarchy() for each workspace.
1548 $self->{'projects'} = \
@projects;
1549 $self->{'project_info'} = \
%pi;
1551 if ($status && $genimpdep) {
1552 $self->add_implicit_project_dependencies($creator, $cwd);
1555 ## If we are generating the hierarchical workspaces, then do so
1556 $self->{'lib_locations'} = \
%liblocs;
1557 if ($self->get_hierarchy() || $self->workspace_per_project()) {
1558 my $orig = $self->{'workspace_name'};
1559 ($status, $errorString) = $self->generate_hierarchy($creator, \
@projects, \
%pi);
1560 $self->{'workspace_name'} = $orig;
1563 ## Reset the projects and project_info
1564 $self->{'projects'} = \
@projects;
1565 $self->{'project_info'} = \
%pi;
1567 return $status, $creator, $errorString;
1570 sub generate_project_files_fork
{
1572 my $status = (scalar @
{$self->{'project_files'}} == 0 ?
1 : 0);
1578 my $creator = $self->project_creator('child');
1579 my $cwd = $self->getcwd();
1580 my $impl = $self->get_assignment('implicit');
1581 my $postkey = $creator->get_dynamic() .
1582 $creator->get_static() . "-$self";
1583 my $previmpl = $impl;
1584 my $prevcache = $self->{'cacheok'};
1585 my %gstate = $creator->save_state();
1586 my $genimpdep = $self->generate_implicit_project_dependencies();
1591 $Data::Dumper
::Indent
= 0;
1593 ## Save this project creator setting for later use in the
1594 ## number_target_deps() method.
1595 $self->{'dependency_is_filename'} = $creator->dependency_is_filename();
1597 ## Remove the address portion of the $self string
1598 $postkey =~ s/=.*//;
1600 ## Set the source file callback on our project creator
1601 $creator->set_source_listing_callback([\
&source_listing_callback
, $self]);
1605 my $tmp = 'mpctmp00000000';
1607 ## remove old temp files
1608 my @tmpfiles = glob "${wdir}/mpctmp*";
1609 for my $file (@tmpfiles) {
1610 unlink $file || die "Error: Unable to delete tmp file $file in directory $wdir";
1613 my $num_tmp_files = scalar (@tmpfiles);
1615 $self->diagnostic("Multiprocess MPC removed $num_tmp_files existing files like \"mpctmp\*\" in $wdir.");
1617 foreach my $ofile (@
{$self->{'project_files'}}) {
1618 if ($#pids + 1 >= $num_workers) {
1619 waitpid(shift @pids, 0);
1624 ## open the output file in parent so it can die if there's an error
1625 open (FD
, ">${wdir}/$tmp") || die "Can't open $tmp for write";
1632 $self->{'pid'} = 'child';
1634 if (!$self->excluded($ofile)) {
1636 my $dir = $self->mpc_dirname($file);
1639 if (defined $self->{'scoped_assign'}->{$ofile}) {
1640 ## Handle the implicit assignment
1641 my $oi = $self->{'scoped_assign'}->{$ofile}->{'implicit'};
1647 ## Handle the cmdline assignment
1648 my $cmdline = $self->{'scoped_assign'}->{$ofile}->{'cmdline'};
1649 if (defined $cmdline && $cmdline ne '') {
1650 ## Save the cacheok value
1651 $prevcache = $self->{'cacheok'};
1653 ## Get the current parameters and process the command line
1654 my %parameters = $self->current_parameters();
1655 $self->process_cmdline($cmdline, \
%parameters);
1657 ## Set the parameters on the creator
1658 $creator->restore_state(\
%parameters);
1663 ## If we are generating implicit projects and the file is a
1664 ## directory, then we set the dir to the file and empty the file
1665 if ($impl && -d
$file) {
1669 ## If the implicit assignment value was not a number, then
1670 ## we will add this value to our base projects.
1671 if ($impl !~ /^\d+$/) {
1672 my $bps = $creator->get_baseprojs();
1673 push(@
$bps, split(/\s+/, $impl));
1675 $self->{'cacheok'} = 0;
1679 ## Generate the key for this project file
1680 my $prkey = $self->getcwd() . '/' .
1681 ($file eq '' ?
$dir : $file) . "-$postkey";
1683 ## We must change to the subdirectory for
1684 ## which this project file is intended
1686 if ($self->cd($dir)) {
1687 my $files_written = [];
1688 my $gen_proj_info = [];
1689 my $gen_lib_locs = {};
1691 if ($self->{'cacheok'} && defined $allprojects{$prkey}) {
1692 $files_written = $allprojects{$prkey};
1693 $gen_proj_info = $allprinfo{$prkey};
1694 $gen_lib_locs = $allliblocs{$prkey};
1699 $status = $creator->generate($self->mpc_basename($file));
1701 ## If any one project file fails, then stop
1702 ## processing altogether.
1704 # save the status info and exit. the parent will
1706 print FD
"$status|Unable to process " .
1707 ($file eq '' ?
" in $dir" : $file) . "\n";
1709 exit(1); # child error
1712 ## Get the individual project information and
1713 ## generated file name(s)
1714 $files_written = $creator->get_files_written();
1715 $gen_proj_info = $creator->get_project_info();
1716 $gen_lib_locs = $creator->get_lib_locations();
1721 print FD
"$status|''|$self->{'cacheok'}|$previmpl|$prevcache\n";
1722 print FD
"$ofile|$prkey|$dir|$cwd|$restore\n";
1724 print FD Dumper
($files_written), "\n";
1725 print FD Dumper
($gen_proj_info), "\n";
1726 print FD Dumper
($gen_lib_locs), "\n";
1728 # there's a callback that sets the project file list
1729 # since we can't callback between processes we store
1730 # the list for later
1731 print FD Dumper
($self->{'project_file_list'}), "\n";
1735 ## Unable to change to the directory.
1736 ## We don't restore the state before we leave,
1737 ## but that's ok since we will be exiting soon.
1738 print FD
"$status|Unable to change directory to $dir\n";
1740 exit (1); # child error
1745 ## This one was excluded, so status is ok
1746 ## no need to set though since the child will exit.
1750 exit(0); # child is finished
1755 # this will also reap any zombies
1759 my ($msg, $cacheok, $ofile, $prkey, $dir, $restore);
1761 # read the children's stored data
1762 my @kid_data = glob "${wdir}/mpctmp*";
1764 for my $kd (@kid_data) {
1765 open (FD
, "<$kd") || die "Can't open $kd for read";
1767 ($status, $msg, $cacheok, $previmpl, $prevcache) = split /\|/, <FD
>;
1770 return $status, $creator, $msg;
1773 ($ofile, $prkey, $dir, $cwd, $restore) = split /\|/, <FD
>;
1776 my $files_written = $VAR1;
1779 my $gen_proj_info = $VAR1;
1781 # have to reconstitute gen_lib_locs in the same order it was
1782 # created or else multi-process implicit dependency may differ from
1786 for my $k (sort { substr($a, 0 , index ($a, '|')) <=>
1787 substr ($b, 0, index ($b, '|')) } keys %$VAR1) {
1789 $gen_lib_locs->{substr ($k, index ($k, '|') + 1)} =
1793 # have to reconstitute project_file_list in the same order it was
1794 # created or else multi-process implicit dependency may differ from
1797 for my $k (sort { substr($a, 0 , index ($a, '|')) <=>
1798 substr ($b, 0, index ($b, '|')) } keys %$VAR1) {
1800 $self->{'project_file_list'}->{substr ($k, index ($k, '|') + 1)} =
1804 $self->{'cacheok'} = $cacheok;
1805 my $full = $self->path_is_relative($dir) ?
"$cwd/$dir" : $dir;
1806 if ($self->cd($full)) {
1807 if ($self->{'cacheok'} && defined $allprojects{$prkey}) {
1809 $files_written = $allprojects{$prkey};
1810 $gen_proj_info = $allprinfo{$prkey};
1811 $gen_lib_locs = $allliblocs{$prkey};
1815 # file is already generated. check status
1818 ## We don't restore the state before we leave,
1819 ## but that's ok since we will be exiting right now.
1820 return $status, $creator, $msg;
1823 ## Get the individual project information and
1824 ## generated file name(s)
1825 if ($self->{'cacheok'}) {
1827 $allprojects{$prkey} = $files_written;
1828 $allprinfo{$prkey} = $gen_proj_info;
1829 $allliblocs{$prkey} = $gen_lib_locs;
1832 push(@
{$self->{'mpc_to_output'}->{$ofile}}, @
$files_written);
1836 $self->save_project_info($files_written, $gen_proj_info,
1837 $gen_lib_locs, $dir,
1838 \
@projects, \
%pi, \
%liblocs);
1841 ## Unable to change to the directory.
1842 ## We don't restore the state before we leave,
1843 ## but that's ok since we will be exiting soon.
1844 return 0, $creator, "Unable to change directory to $full";
1847 ## Return things to the way they were
1848 $impl = $previmpl if (defined $self->{'scoped_assign'}->{$ofile});
1850 $self->{'cacheok'} = $prevcache;
1851 $creator->restore_state(\
%gstate);
1855 ## Add implicit project dependencies based on source files
1856 ## that have been used by multiple projects. If we do it here
1857 ## before we call generate_hierarchy(), we don't have to call it
1858 ## in generate_hierarchy() for each workspace.
1859 $self->{'projects'} = \
@projects;
1860 $self->{'project_info'} = \
%pi;
1862 if ($status && $genimpdep) {
1863 #print "aipd: $cwd\n", Dumper ($creator), "\n";
1864 $self->add_implicit_project_dependencies($creator, $cwd);
1867 ## If we are generating the hierarchical workspaces, then do so
1868 $self->{'lib_locations'} = \
%liblocs;
1869 if ($self->get_hierarchy() || $self->workspace_per_project()) {
1870 my $orig = $self->{'workspace_name'};
1871 ($status, $errorString) = $self->generate_hierarchy($creator, \
@projects, \
%pi);
1872 $self->{'workspace_name'} = $orig;
1875 ## Reset the projects and project_info
1876 $self->{'projects'} = \
@projects;
1877 $self->{'project_info'} = \
%pi;
1879 return $status, $creator, $errorString;
1882 sub send_to_parent
{
1887 my $sock = new IO
::Socket
::INET
(
1888 PeerAddr
=> 'localhost',
1893 if (!defined ($sock)) {
1894 die "Child could not create socket";
1897 map { print $sock "$_\n"; } @
$arr;
1901 sub generate_project_files_fork_socket
{
1903 my $status = (scalar @
{$self->{'project_files'}} == 0 ?
1 : 0);
1909 my $creator = $self->project_creator('child');
1910 my $cwd = $self->getcwd();
1911 my $impl = $self->get_assignment('implicit');
1912 my $postkey = $creator->get_dynamic() .
1913 $creator->get_static() . "-$self";
1914 my $previmpl = $impl;
1915 my $prevcache = $self->{'cacheok'};
1916 my %gstate = $creator->save_state();
1917 my $genimpdep = $self->generate_implicit_project_dependencies();
1922 $Data::Dumper
::Indent
= 0;
1924 ## Save this project creator setting for later use in the
1925 ## number_target_deps() method.
1926 $self->{'dependency_is_filename'} = $creator->dependency_is_filename();
1928 ## Remove the address portion of the $self string
1929 $postkey =~ s/=.*//;
1931 ## Set the source file callback on our project creator
1932 $creator->set_source_listing_callback([\
&source_listing_callback
, $self]);
1936 my @pdata; # parents data sent from children.
1938 ## setup workers' data
1943 my $num_prj_files = $#{$self->{'project_files'}} + 1;
1945 ## reduce the number of workers if necessary
1946 ## what if $num_workers > SOMAXCONN?? (unlikely)
1947 if ($num_workers > SOMAXCONN
) {
1948 $self->diagnostic("Multiprocess MPC reducing # workers from $num_workers to " . SOMAXCONN
. ", the max # of queued connections");
1949 $num_workers = SOMAXCONN
;
1952 if ($num_workers > $num_prj_files) {
1953 # don't fork more workers than there are jobs
1954 $self->diagnostic("Multiprocess MPC reducing # workers from $num_workers to $num_prj_files, the number of project files.");
1955 $num_workers = $num_prj_files;
1958 my $num_per_worker = int ($num_prj_files / $num_workers);
1959 my $num_lines_per_prj = 6;
1961 $self->diagnostic("Multiprocess MPC using $num_workers workers to process $num_prj_files project files.");
1963 for (my $wctr = 0; $wctr < $num_workers; ++$wctr) {
1964 $beg = $wctr * $num_per_worker;
1965 $fin = $beg + $num_per_worker - 1;
1967 @
{$wdata[$wctr]} = @
{$self->{'project_files'}}[$beg..$fin];
1970 ## give any remaining data to last worker.
1971 if ($num_prj_files > $num_per_worker * $num_workers) {
1972 push @
{$wdata[$num_workers - 1]} ,
1973 @
{$self->{'project_files'}}[$num_per_worker
1974 * $num_workers..$#{$self->{'project_files'}}];
1978 ## Setup listener. Do this before fork so that (in the rare case)
1979 ## when child tries to send data before the accept(), the socket
1980 ## is at least initialized.
1981 my $sock = new IO
::Socket
::INET
(
1982 LocalHost
=> 'localhost',
1983 LocalPort
=> $wport,
1985 Listen
=> $num_workers,
1988 if (!defined ($sock)) {
1989 die "Error setting up parent listener";
1992 ## spawn the workers.
1994 while ($id < $num_workers) {
1995 # use pipes as barrier
2001 ## after fork, child knows its id and which data to use.
2002 $self->{'pid'} = 'child';
2008 if ($self->{pid
} eq 'parent') {
2009 $self->diagnostic("Multiprocess MPC using port $wport.");
2011 # read the data from the kids
2012 for (my $ctr = 0; $ctr < $num_workers; ++$ctr) {
2013 my $handle = $sock->accept();
2014 die "Accept error" if !$handle;
2016 @
{$pdata[$id]} = <$handle>;
2018 # each project as 6 records
2019 if ((($#{$pdata[$id]} + 1) / $num_lines_per_prj) != $num_per_worker) {
2020 if ($#{$pdata[$id]} != 0) {
2021 # 0 indicates a failed status which will be delt with later
2022 if (($id == $num_workers - 1) && ((($#{$pdata[$id]} + 1) / $num_lines_per_prj) != $num_per_worker + $#{$self->{'project_files'}} + 1 - ($num_workers * $num_per_worker))) {
2023 # The last child may have more than num_per_worker records
2024 my $rec = $#{$pdata[$id]} + 1;
2025 my $exp = $num_per_worker * $num_lines_per_prj;
2026 die "There is an error in the child data. Expected $exp. Received $rec";
2031 # all data has been read
2035 ## This is the code the workers run.
2037 ## generate projects
2039 foreach my $ofile (@
{$wdata[$id]}) {
2040 if (!$self->excluded($ofile)) {
2042 my $dir = $self->mpc_dirname($file);
2045 if (defined $self->{'scoped_assign'}->{$ofile}) {
2046 ## Handle the implicit assignment
2047 my $oi = $self->{'scoped_assign'}->{$ofile}->{'implicit'};
2053 ## Handle the cmdline assignment
2054 my $cmdline = $self->{'scoped_assign'}->{$ofile}->{'cmdline'};
2055 if (defined $cmdline && $cmdline ne '') {
2056 ## Save the cacheok value
2057 $prevcache = $self->{'cacheok'};
2059 ## Get the current parameters and process the command line
2060 my %parameters = $self->current_parameters();
2061 $self->process_cmdline($cmdline, \
%parameters);
2063 ## Set the parameters on the creator
2064 $creator->restore_state(\
%parameters);
2069 ## If we are generating implicit projects and the file is a
2070 ## directory, then we set the dir to the file and empty the file
2071 if ($impl && -d
$file) {
2075 ## If the implicit assignment value was not a number, then
2076 ## we will add this value to our base projects.
2077 if ($impl !~ /^\d+$/) {
2078 my $bps = $creator->get_baseprojs();
2079 push(@
$bps, split(/\s+/, $impl));
2081 $self->{'cacheok'} = 0;
2085 ## Generate the key for this project file
2086 my $prkey = $self->getcwd() . '/' .
2087 ($file eq '' ?
$dir : $file) . "-$postkey";
2089 ## We must change to the subdirectory for
2090 ## which this project file is intended
2092 if ($self->cd($dir)) {
2093 my $files_written = [];
2094 my $gen_proj_info = [];
2095 my $gen_lib_locs = {};
2097 if ($self->{'cacheok'} && defined $allprojects{$prkey}) {
2098 $files_written = $allprojects{$prkey};
2099 $gen_proj_info = $allprinfo{$prkey};
2100 $gen_lib_locs = $allliblocs{$prkey};
2105 $status = $creator->generate($self->mpc_basename($file));
2107 ## If any one project file fails, then stop
2108 ## processing altogether.
2110 # save the status info and exit. the parent will
2113 push @cdata, "$status|Unable to process " .
2114 ($file eq '' ?
" in $dir" : $file) . "\n";
2116 $self->send_to_parent(\
@cdata);
2117 exit(1); # child error
2120 ## Get the individual project information and
2121 ## generated file name(s)
2122 $files_written = $creator->get_files_written();
2123 $gen_proj_info = $creator->get_project_info();
2124 $gen_lib_locs = $creator->get_lib_locations();
2128 push @cdata, "$status|''|$self->{'cacheok'}|$previmpl|$prevcache";
2129 push @cdata, "$ofile|$prkey|$dir|$cwd|$restore";
2130 push @cdata, Dumper
($files_written);
2131 push @cdata, Dumper
($gen_proj_info);
2132 push @cdata, Dumper
($gen_lib_locs);
2134 # there's a callback that sets the project file list
2135 # since we can't callback between processes we store
2136 # the list for later
2137 push @cdata, Dumper
($self->{'project_file_list'});
2143 ## Unable to change to the directory.
2144 ## We don't restore the state before we leave,
2145 ## but that's ok since we will be exiting soon.
2147 push @cdata, "$status|Unable to change directory to $dir\n";
2148 $self->send_to_parent(\
@cdata);
2150 exit (1); # child error
2153 ## Return things to the way they were
2154 $impl = $previmpl if (defined $self->{'scoped_assign'}->{$ofile});
2156 $self->{'cacheok'} = $prevcache;
2157 $creator->restore_state(\
%gstate);
2161 ## This one was excluded, so status is ok
2162 ## no need to set though since the child will exit.
2167 # send all the data at once.
2168 $self->send_to_parent(\
@cdata);
2175 # This is the parent again.
2178 # this will reap any zombies
2182 my ($msg, $cacheok, $ofile, $prkey, $dir, $restore);
2184 # read the children's stored data
2185 for (my $i = 0; $i < $num_workers; ++$i) {
2186 for (my $j = 0; $j < $#{$pdata[$i]} + 1; ++$j) {
2187 ($status, $msg, $cacheok, $previmpl, $prevcache) = split /\|/, ${$pdata[$i]}[$j++];
2189 # check that the child was successful
2191 return $status, $creator, $msg;
2194 ($ofile, $prkey, $dir, $cwd, $restore) = split /\|/, ${$pdata[$i]}[$j++];
2196 eval (${$pdata[$i]}[$j++]);
2197 my $files_written = $VAR1;
2199 eval (${$pdata[$i]}[$j++]);
2200 my $gen_proj_info = $VAR1;
2202 # have to reconstitute gen_lib_locs in the same order it was
2203 # created or else multi-process implicit dependency may differ from
2205 eval (${$pdata[$i]}[$j++]);
2207 for my $k (sort { substr($a, 0 , index ($a, '|')) <=>
2208 substr ($b, 0, index ($b, '|')) } keys %$VAR1) {
2210 $gen_lib_locs->{substr ($k, index ($k, '|') + 1)} =
2214 # have to reconstitute project_file_list in the same order it was
2215 # created or else multi-process implicit dependency may differ from
2217 eval (${$pdata[$i]}[$j]);
2218 for my $k (sort { substr($a, 0 , index ($a, '|')) <=>
2219 substr ($b, 0, index ($b, '|')) } keys %$VAR1) {
2221 $self->{'project_file_list'}->{substr ($k, index ($k, '|') + 1)} =
2225 $self->{'cacheok'} = $cacheok;
2226 if ($self->cd($dir)) {
2227 if ($self->{'cacheok'} && defined $allprojects{$prkey}) {
2229 $files_written = $allprojects{$prkey};
2230 $gen_proj_info = $allprinfo{$prkey};
2231 $gen_lib_locs = $allliblocs{$prkey};
2235 # file is already generated. check status
2238 ## We don't restore the state before we leave,
2239 ## but that's ok since we will be exiting right now.
2240 return $status, $creator, $msg;
2243 ## Get the individual project information and
2244 ## generated file name(s)
2245 if ($self->{'cacheok'}) {
2247 $allprojects{$prkey} = $files_written;
2248 $allprinfo{$prkey} = $gen_proj_info;
2249 $allliblocs{$prkey} = $gen_lib_locs;
2252 push(@
{$self->{'mpc_to_output'}->{$ofile}}, @
$files_written);
2256 $self->save_project_info($files_written, $gen_proj_info,
2257 $gen_lib_locs, $dir,
2258 \
@projects, \
%pi, \
%liblocs);
2262 ## Unable to change to the directory.
2263 ## We don't restore the state before we leave,
2264 ## but that's ok since we will be exiting soon.
2265 return 0, $creator, $msg;
2269 ## Return things to the way they were
2270 $impl = $previmpl if (defined $self->{'scoped_assign'}->{$ofile});
2272 $self->{'cacheok'} = $prevcache;
2273 $creator->restore_state(\
%gstate);
2278 ## Add implicit project dependencies based on source files
2279 ## that have been used by multiple projects. If we do it here
2280 ## before we call generate_hierarchy(), we don't have to call it
2281 ## in generate_hierarchy() for each workspace.
2282 $self->{'projects'} = \
@projects;
2283 $self->{'project_info'} = \
%pi;
2285 if ($status && $genimpdep) {
2286 #print "aipd: $cwd\n", Dumper ($creator), "\n";
2287 $self->add_implicit_project_dependencies($creator, $cwd);
2290 ## If we are generating the hierarchical workspaces, then do so
2291 $self->{'lib_locations'} = \
%liblocs;
2292 if ($self->get_hierarchy() || $self->workspace_per_project()) {
2293 my $orig = $self->{'workspace_name'};
2294 ($status, $errorString) = $self->generate_hierarchy($creator, \
@projects, \
%pi);
2295 $self->{'workspace_name'} = $orig;
2298 ## Reset the projects and project_info
2299 $self->{'projects'} = \
@projects;
2300 $self->{'project_info'} = \
%pi;
2302 return $status, $creator, $errorString;
2306 sub array_contains
{
2307 my($self, $left, $right) = @_;
2310 ## Initialize the hash keys with the left side array
2311 @check{@
$left} = ();
2313 ## Check each element on the right against the left.
2314 foreach my $r (@
$right) {
2315 return 1 if (exists $check{$r});
2322 sub non_intersection
{
2323 my($self, $left, $right, $over) = @_;
2327 ## Initialize the hash keys with the left side array
2328 @check{@
$left} = ();
2330 ## Check each element on the right against the left.
2331 ## Store anything that isn't in the left side in the over array.
2332 foreach my $r (@
$right) {
2333 if (exists $check{$r}) {
2344 sub indirect_dependency
{
2345 my($self, $dir, $ccheck, $cfile) = @_;
2347 $self->{'indirect_checked'}->{$ccheck} = 1;
2348 if (index($self->{'project_info'}->{$ccheck}->[ProjectCreator
::DEPENDENCIES
], $cfile) >= 0) {
2352 my $deps = $self->create_array(
2353 $self->{'project_info'}->{$ccheck}->[ProjectCreator
::DEPENDENCIES
]);
2354 foreach my $dep (@
$deps) {
2355 if (defined $self->{'project_info'}->{"$dir$dep"} &&
2356 !defined $self->{'indirect_checked'}->{"$dir$dep"} &&
2357 $self->indirect_dependency($dir, "$dir$dep", $cfile)) {
2367 sub add_implicit_project_dependencies
{
2368 my($self, $creator, $cwd) = @_;
2372 ## Take the current working directory and regular expression'ize it.
2373 $cwd = $self->escape_regex_special($cwd);
2375 ## Look at each projects file list and check it against all of the
2376 ## others. If any of the other projects file lists contains anothers
2377 ## file, then they are dependent (due to build parallelism). So, we
2378 ## append the dependency and remove the file in question from the
2379 ## project so that the next time around the foreach, we don't find it
2380 ## as a dependent on the one that we just modified.
2381 my @pflkeys = keys %{$self->{'project_file_list'}};
2383 foreach my $key (@pflkeys) {
2384 foreach my $ikey (@pflkeys) {
2385 ## Not the same project and
2386 ## The same directory and
2387 ## We've not already added a dependency to this project
2388 if ($key ne $ikey &&
2389 ($self->{'project_file_list'}->{$key}->[1] eq
2390 $self->{'project_file_list'}->{$ikey}->[1]) &&
2391 (!defined $bidir{$ikey} ||
2392 !$self->array_contains($bidir{$ikey}, [$key]))) {
2394 if ($self->non_intersection(
2395 $self->{'project_file_list'}->{$key}->[2],
2396 $self->{'project_file_list'}->{$ikey}->[2],
2398 ## The project contains shared source files, so we need to
2399 ## look into adding an implicit inter-project dependency.
2400 $save{$ikey} = $self->{'project_file_list'}->{$ikey}->[2];
2401 $self->{'project_file_list'}->{$ikey}->[2] = \
@over;
2402 if (defined $bidir{$key}) {
2403 push(@
{$bidir{$key}}, $ikey);
2406 $bidir{$key} = [$ikey];
2408 my $append = $creator->translate_value('after', $key);
2409 my $file = $self->{'project_file_list'}->{$ikey}->[0];
2410 my $dir = $self->{'project_file_list'}->{$ikey}->[1];
2411 my $cfile = $creator->translate_value('after', $ikey);
2412 ## Remove our starting directory from the projects directory
2413 ## to get the right part of the directory to prepend.
2414 $dir =~ s/^$cwd[\/\\]*//;
2416 ## Turn the append value into a key for 'project_info' and
2417 ## prepend the directory to the file.
2418 my $ccheck = $append;
2422 $ccheck = "$dir$ccheck";
2423 $file = "$dir$file";
2426 ## If the append value key contains a reference to the project
2427 ## that we were going to append the dependency value, then
2428 ## ignore the generated dependency. It is redundant and
2429 ## quite possibly wrong.
2430 $self->{'indirect_checked'} = {};
2431 if (defined $self->{'project_info'}->{$file} &&
2432 (!defined $self->{'project_info'}->{$ccheck} ||
2433 !$self->indirect_dependency($dir, $ccheck, $cfile))) {
2434 ## Append the dependency
2435 $self->{'project_info'}->{$file}->[ProjectCreator
::DEPENDENCIES
] .= " $append";
2442 ## Restore the modified values in case this method is called again
2443 ## which is the case when using the -hierarchy option.
2444 foreach my $skey (keys %save) {
2445 $self->{'project_file_list'}->{$skey}->[2] = $save{$skey};
2451 return $_[0]->{'projects'};
2455 sub get_project_info
{
2456 return $_[0]->{'project_info'};
2460 sub get_lib_locations
{
2461 return $_[0]->{'lib_locations'};
2465 sub get_first_level_directory
{
2466 my($self, $file) = @_;
2468 if (($file =~ tr/\///) > 0) {
2470 $dir =~ s/^([^\/]+\/).*/$1/;
2479 sub get_associated_projects
{
2480 return $_[0]->{'associated'};
2484 sub sort_within_group
{
2485 my($self, $list, $start, $end) = @_;
2489 my $cmax = ($end - $start) + 1;
2494 ## Put the projects in the order specified
2495 ## by the project dependencies.
2496 for (my $i = $start; $i <= $end; ++$i) {
2497 ## If our moved project equals our previously moved project then
2498 ## we count this as a possible circular dependency.
2501 (defined $$movepjs[0] && defined $$prevpjs[0] &&
2502 $$movepjs[0] == $$prevpjs[0] && $$movepjs[1] == $$prevpjs[1])) {
2509 ## Detect circular dependencies
2510 if ($ccount > $cmax) {
2512 foreach my $mvgr (@
$movepjs) {
2513 push(@prjs, $$list[$mvgr]);
2515 my $other = $$movepjs[0] - 1;
2516 if ($other < $start || $other == $$movepjs[1] || !defined $$list[$other]) {
2519 $self->warning('Circular dependency detected while processing the ' .
2520 ($self->{'current_input'} eq '' ?
2521 'default' : $self->{'current_input'}) .
2523 'The following projects are involved: ' .
2524 (defined $other ?
"$$list[$other], " : '') .
2525 join(' and ', @prjs));
2529 ## Keep track of the previous project movement
2531 $prevpjs = $movepjs;
2532 $movepjs = [] if ($previ < $i);
2535 $deps = $self->get_validated_ordering($$list[$i]);
2536 if (defined $$deps[0]) {
2537 my $baseproj = ($self->{'dependency_is_filename'} ?
2538 $self->mpc_basename($$list[$i]) :
2539 $self->{'project_info'}->{$$list[$i]}->[ProjectCreator
::PROJECT_NAME
]);
2541 foreach my $dep (@
$deps) {
2542 if ($baseproj ne $dep) {
2543 ## See if the dependency is listed after this project
2544 for (my $j = $i + 1; $j <= $end; ++$j) {
2545 my $ldep = ($self->{'dependency_is_filename'} ?
2546 $self->mpc_basename($$list[$j]) :
2547 $self->{'project_info'}->{$$list[$j]}->[ProjectCreator
::PROJECT_NAME
]);
2548 if ($ldep eq $dep) {
2549 $movepjs = [$i, $j];
2550 ## If so, move it in front of the current project.
2551 ## The original code, which had splices, didn't always
2552 ## work correctly (especially on AIX for some reason).
2553 my $save = $$list[$j];
2554 for (my $k = $j; $k > $i; --$k) {
2555 $$list[$k] = $$list[$k - 1];
2559 ## Mark that an entry has been moved
2572 sub build_dependency_chain
{
2573 my($self, $name, $len, $list, $ni, $glen, $groups, $map, $gdeps) = @_;
2574 my $deps = $self->get_validated_ordering($name);
2576 if (defined $$deps[0]) {
2577 foreach my $dep (@
$deps) {
2578 ## Find the item in the list that matches our current dependency
2579 my $mapped = $$map{$dep};
2580 if (defined $mapped) {
2581 for (my $i = 0; $i < $len; $i++) {
2582 if ($$list[$i] eq $mapped) {
2584 ## Locate the group number to which the dependency belongs
2585 for (my $j = 0; $j < $glen; $j++) {
2586 if ($i >= $$groups[$j]->[0] && $i <= $$groups[$j]->[1]) {
2589 ## Add every project in the group to the dependency chain
2590 for (my $k = $$groups[$j]->[0]; $k <= $$groups[$j]->[1]; $k++) {
2591 my $ldep = $self->mpc_basename($$list[$k]);
2592 if (!exists $$gdeps{$ldep}) {
2594 $self->build_dependency_chain($$list[$k],
2615 sub sort_by_groups
{
2616 my($self, $list, $grindex) = @_;
2617 my @groups = @
$grindex;
2618 my $llen = scalar(@
$list);
2620 ## Check for duplicates first before we attempt to sort the groups.
2621 ## If there is a duplicate, we quietly return immediately. The
2622 ## duplicates will be flagged as an error when creating the main
2625 foreach my $proj (@
$list) {
2626 my $base = $self->mpc_basename($proj);
2627 return undef if (defined $dupcheck{$base});
2628 $dupcheck{$base} = $proj;
2631 my %circular_checked;
2632 for (my $gi = 0; $gi <= $#groups; ++$gi) {
2633 ## Detect circular dependencies
2634 if (!$circular_checked{$gi}) {
2635 $circular_checked{$gi} = 1;
2636 for (my $i = $groups[$gi]->[0]; $i <= $groups[$gi]->[1]; ++$i) {
2638 $self->build_dependency_chain($$list[$i], $llen, $list, $gi,
2639 $#groups + 1, \
@groups,
2640 \
%dupcheck, \
%gdeps);
2641 if (exists $gdeps{$self->mpc_basename($$list[$i])}) {
2642 ## There was a cirular dependency, get all of the directories
2645 foreach my $gdep (keys %gdeps) {
2646 $dirs{$self->mpc_dirname($dupcheck{$gdep})} = 1;
2649 ## If the current directory was involved, translate that into
2650 ## a directory relative to the start directory.
2651 if (defined $dirs{'.'}) {
2652 my $cwd = $self->getcwd();
2653 my $start = $self->getstartdir();
2654 if ($cwd ne $start) {
2655 my $startre = $self->escape_regex_special($start);
2657 $cwd =~ s/^$startre[\\\/]//;
2662 ## Display a warining to the user
2663 my @keys = sort keys %dirs;
2664 $self->warning('Circular directory dependency detected in the ' .
2665 ($self->{'current_input'} eq '' ?
2666 'default' : $self->{'current_input'}) .
2668 'The following director' .
2669 ($#keys == 0 ?
'y is' : 'ies are') .
2670 ' involved: ' . join(', ', @keys));
2676 ## Build up the group dependencies
2678 for (my $i = $groups[$gi]->[0]; $i <= $groups[$gi]->[1]; ++$i) {
2679 my $deps = $self->get_validated_ordering($$list[$i]);
2680 @gdeps{@
$deps} = () if (defined $$deps[0]);
2683 ## Search the rest of the groups for any of the group dependencies
2684 for (my $gj = $gi + 1; $gj <= $#groups; ++$gj) {
2685 for (my $i = $groups[$gj]->[0]; $i <= $groups[$gj]->[1]; ++$i) {
2686 if (exists $gdeps{$self->mpc_basename($$list[$i])}) {
2687 ## Move this group ($gj) in front of the current group ($gi)
2689 for (my $j = $groups[$gi]->[1] + 1; $j <= $groups[$gj]->[1]; ++$j) {
2690 push(@save, $$list[$j]);
2692 my $offset = $groups[$gj]->[1] - $groups[$gi]->[1];
2693 for (my $j = $groups[$gi]->[1]; $j >= $groups[$gi]->[0]; --$j) {
2694 $$list[$j + $offset] = $$list[$j];
2696 for (my $j = 0; $j <= $#save; ++$j) {
2697 $$list[$groups[$gi]->[0] + $j] = $save[$j];
2700 ## Update the group indices
2701 my $shiftamt = ($groups[$gi]->[1] - $groups[$gi]->[0]) + 1;
2702 for (my $j = $gi + 1; $j <= $gj; ++$j) {
2703 $groups[$j]->[0] -= $shiftamt;
2704 $groups[$j]->[1] -= $shiftamt;
2706 my @grsave = @
{$groups[$gi]};
2707 $grsave[0] += $offset;
2708 $grsave[1] += $offset;
2709 for (my $j = $gi; $j < $gj; ++$j) {
2710 $groups[$j] = $groups[$j + 1];
2711 $circular_checked{$j} = $circular_checked{$j + 1};
2713 $groups[$gj] = \
@grsave;
2714 $circular_checked{$gj} = 1;
2716 ## Start over from the first group
2719 ## Exit from the outter ($gj) loop
2729 sub sort_dependencies
{
2730 my($self, $projects, $groups) = @_;
2731 my @list = sort { return $self->sort_projects_by_directory($a, $b) + 0;
2733 ## The list above is sorted by directory in order to keep projects
2734 ## within the same directory together. Otherwise, when groups are
2735 ## created we may get multiple groups for the same directory.
2737 ## Put the projects in the order specified
2738 ## by the project dependencies. We only need to do
2739 ## this if there is more than one element in the array.
2741 ## If the parameter wasn't passed in or it was passed in
2742 ## and was true, sort with directory groups in mind
2743 if (!defined $groups || $groups) {
2744 ## First determine the individual groups
2746 my $previous = [0, undef];
2747 for (my $li = 0; $li <= $#list; ++$li) {
2748 my $dir = $self->get_first_level_directory($list[$li]);
2749 if (!defined $previous->[1]) {
2750 $previous = [$li, $dir];
2752 elsif ($previous->[1] ne $dir) {
2753 push(@grindex, [$previous->[0], $li - 1]);
2754 $previous = [$li, $dir];
2757 push(@grindex, [$previous->[0], $#list]);
2759 ## Next, sort the individual groups
2760 foreach my $gr (@grindex) {
2761 $self->sort_within_group(\
@list, @
$gr) if ($$gr[0] != $$gr[1]);
2764 ## Now sort the groups as single entities
2765 $self->sort_by_groups(\
@list, \
@grindex) if ($#grindex > 0);
2768 $self->sort_within_group(\
@list, 0, $#list);
2776 sub number_target_deps
{
2777 my($self, $projects, $pjs, $targets, $groups) = @_;
2778 my @list = $self->sort_dependencies($projects, $groups);
2780 ## This block of code must be done after the list of dependencies
2781 ## has been sorted in order to get the correct project numbers.
2782 for (my $i = 0; $i <= $#list; ++$i) {
2783 my $project = $list[$i];
2784 if (defined $$pjs{$project}) {
2785 my($name, $deps) = @
{$$pjs{$project}};
2786 if (defined $deps && $deps ne '') {
2789 @dhash{@
{$self->create_array($deps)}} = ();
2791 ## For each dependency, search in the sorted list
2792 ## up to the point of this project for the projects
2793 ## that this one depends on. When the project is
2794 ## found, we put the target number in the numbers array.
2795 for (my $j = 0; $j < $i; ++$j) {
2796 ## If the dependency is a filename, then take the basename of
2797 ## the project file. Otherwise, get the project name based on
2798 ## the project file from the "project_info".
2799 my $key = ($self->{'dependency_is_filename'} ?
2800 $self->mpc_basename($list[$j]) :
2801 $self->{'project_info'}->{$list[$j]}->[ProjectCreator
::PROJECT_NAME
]);
2802 push(@numbers, $j) if (exists $dhash{$key});
2805 ## Store the array in the hash keyed on the project file.
2806 $$targets{$project} = \
@numbers if (defined $numbers[0]);
2815 sub project_target_translation
{
2816 my($self, $case) = @_;
2819 ## Translate project names to avoid target collision with
2820 ## some versions of make.
2821 foreach my $key (keys %{$self->{'project_info'}}) {
2822 my $dir = $self->mpc_dirname($key);
2823 my $name = $self->{'project_info'}->{$key}->[ProjectCreator
::PROJECT_NAME
];
2825 ## We want to compare to the upper most directory. This will be the
2826 ## one that may conflict with the project name.
2827 $dir =~ s/[\/\\].*//;
2828 if (($case && $dir eq $name) || (!$case && lc($dir) eq lc($name))) {
2829 $map{$key} = "$name-target";
2840 my($self, $str) = @_;
2841 $self->warning("$self->{'current_input'}: $str.") if (defined $str);
2845 sub process_cmdline
{
2846 my($self, $cmdline, $parameters) = @_;
2848 ## Set cache use to default.
2849 $self->{'cacheok'} = $self->default_cacheok();
2851 if (defined $cmdline && $cmdline ne '') {
2852 my $args = $self->create_array($cmdline);
2854 ## Look for environment variables
2855 foreach my $arg (@
$args) {
2856 $self->replace_env_vars(\
$arg) if ($arg =~ /\$/);
2859 my $options = $self->options('MWC', {}, 0, @
$args);
2860 if (defined $options) {
2861 foreach my $key (keys %$options) {
2862 my $type = $self->is_set($key, $options);
2864 if (!defined $type) {
2865 ## This option was not used, so we ignore it
2867 elsif ($type eq 'ARRAY') {
2868 push(@
{$parameters->{$key}}, @
{$options->{$key}});
2870 elsif ($type eq 'HASH') {
2871 my $merge = ($key eq 'addtemp' || $key eq 'addproj');
2872 foreach my $hk (keys %{$options->{$key}}) {
2873 if ($merge && defined $parameters->{$key}->{$hk}) {
2874 push(@
{$parameters->{$key}->{$hk}}, @
{$options->{$key}->{$hk}});
2877 $parameters->{$key}->{$hk} = $options->{$key}->{$hk};
2881 elsif ($type eq 'SCALAR') {
2882 $parameters->{$key} = $options->{$key};
2886 ## Some option data members are named consistently with the MPC
2887 ## option name. In this case, we can use this foreach loop.
2888 foreach my $consistent_opt ('exclude', 'for_eclipse', 'gendot',
2889 'gfeature_file', 'into',
2890 'make_coexistence', 'recurse') {
2891 ## Issue warnings for the options provided by the user
2892 if ($self->is_set($consistent_opt, $options)) {
2893 $self->optionError("-$consistent_opt is ignored");
2897 ## For those that are inconsistent, we have special code to deal
2899 if ($self->is_set('reldefs', $options)) {
2900 $self->optionError('-noreldefs is ignored');
2903 ## Make sure no input files were specified (we can't handle it).
2904 if (defined $options->{'input'}->[0]) {
2905 $self->optionError('Command line files ' .
2906 'specified in a workspace are ignored');
2909 ## Determine if it's ok to use the cache
2910 my @cacheInvalidating = ('global', 'include', 'baseprojs',
2911 'template', 'ti', 'relative', 'language',
2912 'addtemp', 'addproj', 'feature_file',
2913 'features', 'use_env', 'expand_vars');
2914 foreach my $key (@cacheInvalidating) {
2915 if ($self->is_set($key, $options)) {
2916 $self->{'cacheok'} = 0;
2925 sub current_parameters
{
2927 my %parameters = $self->save_state();
2929 ## We always want the project creator to generate a toplevel
2930 $parameters{'toplevel'} = 1;
2935 sub project_creator
{
2938 if (not defined $pid) {
2944 ## NOTE: If the subclassed WorkspaceCreator name prefix does not
2945 ## match the name prefix of the ProjectCreator, this code
2946 ## will not work and the subclassed WorkspaceCreator will
2947 ## need to override this method.
2949 $str =~ s/Workspace/Project/;
2950 $str =~ s/=HASH.*//;
2952 ## Set up values for each project creator
2953 ## If we have command line arguments in the workspace, then
2954 ## we process them before creating the project creator
2955 my $cmdline = $self->get_assignment('cmdline');
2956 my %parameters = $self->current_parameters();
2957 $self->process_cmdline($cmdline, \
%parameters);
2959 ## Create the new project creator with the updated parameters
2960 return $str->new($parameters{'global'},
2961 $parameters{'include'},
2962 $parameters{'template'},
2964 $parameters{'dynamic'},
2965 $parameters{'static'},
2966 $parameters{'relative'},
2967 $parameters{'addtemp'},
2968 $parameters{'addproj'},
2969 $parameters{'progress'},
2970 $parameters{'toplevel'},
2971 $parameters{'baseprojs'},
2972 $self->{'global_feature_file'},
2973 $parameters{'relative_file'},
2974 $parameters{'feature_file'},
2975 $parameters{'features'},
2976 $parameters{'hierarchy'},
2977 $self->{'exclude'}->{$self->{'wctype'}},
2978 $self->make_coexistence(),
2979 $parameters{'name_modifier'},
2980 $parameters{'apply_project'},
2981 $self->{'generate_ins'} || $parameters{'genins'},
2983 $parameters{'language'},
2984 $parameters{'use_env'},
2985 $parameters{'expand_vars'},
2987 $parameters{'comments'},
2988 $self->{'for_eclipse'},
2999 sub make_coexistence
{
3000 return $_[0]->{'coexistence'};
3004 sub get_modified_workspace_name
{
3005 my($self, $name, $ext, $nows) = @_;
3006 my $nmod = $self->get_name_modifier();
3009 if (defined $nmod) {
3010 $nmod =~ s/\*/$name/g;
3014 ## If this is a per project workspace, then we should not
3015 ## modify the workspace name. It may overwrite another workspace
3016 ## but that's ok, it will only be a per project workspace.
3017 ## Also, if we don't want the workspace name attached ($nows) then
3018 ## we just return the name plus the extension.
3019 return "$name$ext" if ($nows || $self->{'per_project_workspace_name'});
3021 my $pwd = $self->getcwd();
3022 my $type = $self->{'wctype'};
3023 my $wsname = $self->get_workspace_name();
3025 if (!defined $previous_workspace_name{$type}->{$pwd}) {
3026 $previous_workspace_name{$type}->{$pwd} = $wsname;
3027 $self->{'current_workspace_name'} = undef;
3030 my $prefix = ($oname eq $wsname ?
$name : "$name.$wsname");
3031 $previous_workspace_name{$type}->{$pwd} = $wsname;
3032 while ($self->file_written("$prefix" .
3033 ($self->{'modified_count'} > 0 ?
3034 ".$self->{'modified_count'}" : '') .
3036 ++$self->{'modified_count'};
3038 $self->{'current_workspace_name'} =
3039 "$prefix" . ($self->{'modified_count'} > 0 ?
3040 ".$self->{'modified_count'}" : '') . "$ext";
3043 return (defined $self->{'current_workspace_name'} ?
3044 $self->{'current_workspace_name'} : "$name$ext");
3048 sub generate_recursive_input_list
{
3049 my($self, $dir, $exclude) = @_;
3050 return $self->extension_recursive_input_list($dir, $exclude, $wsext);
3054 sub verify_build_ordering
{
3056 foreach my $project (@
{$self->{'projects'}}) {
3057 $self->get_validated_ordering($project);
3062 sub get_validated_ordering
{
3063 my($self, $project) = @_;
3066 if (defined $self->{'ordering_cache'}->{$project}) {
3067 $deps = $self->{'ordering_cache'}->{$project};
3071 if (defined $self->{'project_info'}->{$project}) {
3072 my($name, $dstr) = @
{$self->{'project_info'}->{$project}};
3073 if (defined $dstr && $dstr ne '') {
3074 $deps = $self->create_array($dstr);
3075 my $dlen = scalar(@
$deps);
3076 for (my $i = 0; $i < $dlen; $i++) {
3077 my $dep = $$deps[$i];
3079 ## Avoid circular dependencies
3080 if ($dep ne $name && $dep ne $self->mpc_basename($project)) {
3081 foreach my $p (@
{$self->{'projects'}}) {
3082 if ($dep eq $self->{'project_info'}->{$p}->[ProjectCreator
::PROJECT_NAME
] ||
3083 $dep eq $self->mpc_basename($p)) {
3089 if ($self->{'verbose_ordering'}) {
3090 $self->warning("processing '$project' and '$name' references '$dep' which has " .
3091 "not been processed.");
3093 splice(@
$deps, $i, 1);
3099 ## If a project references itself, we must remove it
3100 ## from the list of dependencies.
3101 splice(@
$deps, $i, 1);
3108 $self->{'ordering_cache'}->{$project} = $deps;
3116 sub source_listing_callback
{
3117 my($self, $project_file, $project_name, $list) = @_;
3119 # have to keep projects in the the same order as if run in
3120 # single process. otherwise implicit dependencies produces
3122 if ($self->{'pid'} ne 'parent') {
3123 $project_name = ++$self->{'imp_dep_ctr'} . '|' . $project_name;
3126 $self->{'project_file_list'}->{$project_name} = [ $project_file,
3127 $self->getcwd(), $list ];
3131 sub sort_projects_by_directory
{
3132 my($self, $left, $right) = @_;
3133 my $sa = index($left, '/');
3134 my $sb = index($right, '/');
3136 if ($sa >= 0 && $sb == -1) {
3139 elsif ($sb >= 0 && $sa == -1) {
3142 return $left cmp $right;
3146 sub get_relative_dep_file
{
3147 my($self, $creator, $project, $dep) = @_;
3149 ## If the dependency is a filename, we have to find the key that
3150 ## matches the project file.
3151 if ($creator->dependency_is_filename()) {
3152 foreach my $key (keys %{$self->{'project_file_list'}}) {
3153 if ($self->{'project_file_list'}->{$key}->[0] eq $dep) {
3160 if (defined $self->{'project_file_list'}->{$dep}) {
3161 my $base = $self->{'project_file_list'}->{$dep}->[1];
3162 my @dirs = grep(!/^$/, split('/', $base));
3164 $project =~ s/^\///;
3165 for (my $i = 0; $i <= $#dirs; $i++) {
3166 my $dir = $dirs[$i];
3167 if ($project =~ s/^$dir\///) {
3175 my $dependee = $self->{'project_file_list'}->{$dep}->[0];
3177 return $base . '/' . $dependee;
3181 for (my $i = $last + 1; $i <= $#dirs; $i++) {
3182 $built .= $dirs[$i] . '/';
3184 $built .= $dependee;
3186 ## If the project contains a portion of the current working directory,
3187 ## we need to strip it off. If the workspace is a directory below one
3188 ## of the projects, the directory count will be incorrect due to the
3189 ## use of '..' within the project path.
3191 my $dir = $self->getcwd();
3192 while($dir =~ s!^[^/]*/!! &&
3193 ($re = $dir . '/' . ('../' x
(($dir =~ tr/\///) + 1))) &&
3194 $project !~ s!^$re!!) {
3197 ## The code above is tricky
3198 $self->debug("Project on which this project depends: $project");
3200 my $dircount = ($project =~ tr/\///);
3201 return ('../' x
$dircount) . $built;
3208 sub create_command_line_string
{
3213 foreach my $arg (@args) {
3215 if ($arg =~ /\$/ && $^O
ne 'MSWin32') {
3216 ## If we're not running on Windows and the command line argument
3217 ## contains a dollar sign, we need to wrap the argument in single
3218 ## quotes so that the UNIX shell does not interpret it.
3222 ## Unfortunately, the Windows command line shell does not
3223 ## understand single quotes correctly. So, we have the distinction
3224 ## above and handle dollar signs here too.
3225 $arg = "\"$arg\"" if ($arg =~ /[\s\*\$]/);
3238 sub print_workspace_comment
{
3242 if ($self->{'workspace_comments'}) {
3243 foreach my $line (@_) {
3250 sub get_initial_relative_values
{
3252 return $self->get_relative(), $self->get_expand_vars();
3256 sub get_secondary_relative_values
{
3257 return \
%ENV, $_[0]->get_expand_vars();
3261 sub convert_all_variables
{
3267 sub workspace_file_name
{
3269 return $self->get_modified_workspace_name($self->get_workspace_name(),
3270 $self->workspace_file_extension());
3276 my $line = $self->SUPER::relative
(shift);
3281 # ************************************************************
3282 # Virtual Methods To Be Overridden
3283 # ************************************************************
3285 sub requires_make_coexistence
{
3291 sub supports_make_coexistence
{
3297 sub generate_implicit_project_dependencies
{
3303 sub workspace_file_extension
{
3309 sub workspace_per_project
{
3315 sub default_verbose_ordering
{
3316 return 0; # Don't warning if there are missing dependencies.
3323 #my $creator = shift;
3331 #my $creator = shift;
3336 sub post_workspace
{
3339 #my $creator = shift;
3343 sub requires_forward_slashes
{
3348 sub get_additional_output
{
3351 ## This method should return an array reference of array references.
3352 ## For each entry, the array should be laid out as follows:
3353 ## [ <directory or undef to use the current output directory>,
3355 ## <function to write body of file, $self and $fh are first params>,
3356 ## <optional additional parameter 1>,
3358 ## <optional additional parameter N>