Subversion Repositories gelsvn

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
107 bj 1
package WorkspaceCreator;
2
 
3
# ************************************************************
4
# Description   : Base class for all workspace creators
5
# Author        : Chad Elliott
6
# Create Date   : 5/13/2002
7
# ************************************************************
8
 
9
# ************************************************************
10
# Pragmas
11
# ************************************************************
12
 
13
use strict;
14
use FileHandle;
15
use File::Path;
16
use File::Compare;
17
use File::Basename;
18
 
19
use Creator;
20
use Options;
21
 
22
use vars qw(@ISA);
23
@ISA = qw(Creator Options);
24
 
25
# ************************************************************
26
# Data Section
27
# ************************************************************
28
 
29
my($wsext)  = 'mwc';
30
my($wsbase) = 'mwb';
31
 
32
## Valid names for assignments within a workspace
33
my(%validNames) = ('cmdline'  => 1,
34
                   'implicit' => 1,
35
                  );
36
 
37
## Singleton hash maps of project information
38
my(%allprinfo)   = ();
39
my(%allprojects) = ();
40
my(%allliblocs)  = ();
41
 
42
## Global previous workspace names
43
my(%previous_workspace_name) = ();
44
 
45
## Constant aggregated workspace type name
46
my($aggregated) = 'aggregated_workspace';
47
 
48
# ************************************************************
49
# Subroutine Section
50
# ************************************************************
51
 
52
sub new {
53
  my($class)      = shift;
54
  my($global)     = shift;
55
  my($inc)        = shift;
56
  my($template)   = shift;
57
  my($ti)         = shift;
58
  my($dynamic)    = shift;
59
  my($static)     = shift;
60
  my($relative)   = shift;
61
  my($addtemp)    = shift;
62
  my($addproj)    = shift;
63
  my($progress)   = shift;
64
  my($toplevel)   = shift;
65
  my($baseprojs)  = shift;
66
  my($gfeature)   = shift;
67
  my($feature)    = shift;
68
  my($features)   = shift;
69
  my($hierarchy)  = shift;
70
  my($exclude)    = shift;
71
  my($makeco)     = shift;
72
  my($nmod)       = shift;
73
  my($applypj)    = shift;
74
  my($genins)     = shift;
75
  my($into)       = shift;
76
  my($language)   = shift;
77
  my($use_env)    = shift;
78
  my($expandvars) = shift;
79
  my($self)       = Creator::new($class, $global, $inc,
80
                                 $template, $ti, $dynamic, $static,
81
                                 $relative, $addtemp, $addproj,
82
                                 $progress, $toplevel, $baseprojs,
83
                                 $feature, $features,
84
                                 $hierarchy, $nmod, $applypj,
85
                                 $into, $language, $use_env, $expandvars,
86
                                 'workspace');
87
 
88
  $self->{'workspace_name'}      = undef;
89
  $self->{$self->{'type_check'}} = 0;
90
  $self->{'projects'}            = [];
91
  $self->{'project_info'}        = {};
92
  $self->{'lib_locations'}       = {};
93
  $self->{'reading_parent'}      = [];
94
  $self->{'project_files'}       = [];
95
  $self->{'scoped_assign'}       = {};
96
  $self->{'cacheok'}             = 1;
97
  $self->{'exclude'}             = {};
98
  $self->{'wctype'}              = $self->extractType("$self");
99
  $self->{'modified_count'}      = 0;
100
  $self->{'global_feature_file'} = $gfeature;
101
  $self->{'coexistence'}         = $makeco;
102
  $self->{'project_file_list'}   = {};
103
  $self->{'ordering_cache'}      = {};
104
  $self->{'handled_scopes'}      = {};
105
  $self->{'generate_ins'}        = $genins;
106
  $self->{'scoped_basedir'}      = undef;
107
 
108
  if (defined $$exclude[0]) {
109
    my($type) = $self->{'wctype'};
110
    if (!defined $self->{'exclude'}->{$type}) {
111
      $self->{'exclude'}->{$type} = [];
112
    }
113
    push(@{$self->{'exclude'}->{$type}}, @$exclude);
114
  }
115
 
116
  ## Add a hash reference for our workspace type
117
  if (!defined $previous_workspace_name{$self->{'wctype'}}) {
118
    $previous_workspace_name{$self->{'wctype'}} = {};
119
  }
120
 
121
  ## Warn users about unnecessary options
122
  if ($self->get_hierarchy() && $self->workspace_per_project()) {
123
    $self->warning("The -hierarchy option is unnecessary " .
124
                   "for the " . $self->{'wctype'} . " type.");
125
  }
126
  if ($self->make_coexistence() && !$self->supports_make_coexistence()) {
127
    $self->warning("Using the -make_coexistence option has " .
128
                   "no effect on the " . $self->{'wctype'} . " type.");
129
  }
130
  return $self;
131
}
132
 
133
 
134
sub modify_assignment_value {
135
  my($self)  = shift;
136
  my($name)  = shift;
137
  my($value) = shift;
138
 
139
  ## Workspace assignments do not need modification.
140
  return $value;
141
}
142
 
143
 
144
sub parse_line {
145
  my($self)   = shift;
146
  my($ih)     = shift;
147
  my($line)   = shift;
148
  my($status, $error, @values) = $self->parse_known($line);
149
 
150
  ## Was the line recognized?
151
  if ($status && defined $values[0]) {
152
    if ($values[0] eq $self->{'grammar_type'}) {
153
      my($name) = $values[1];
154
      if (defined $name && $name eq '}') {
155
        if (!defined $self->{'reading_parent'}->[0]) {
156
          ## Fill in all the default values
157
          $self->generate_defaults();
158
 
159
          ## End of workspace; Have subclass write out the file
160
          ## Generate the project files
198 bj 161
          my($gstat, $creator, $err) = $self->generate_project_files();
107 bj 162
          if ($gstat) {
163
            ($status, $error) = $self->write_workspace($creator, 1);
164
            $self->{'assign'} = {};
165
          }
166
          else {
198 bj 167
            $error = $err;
107 bj 168
            $status = 0;
169
          }
170
 
171
          $self->{'modified_count'} = 0;
172
          $self->{'workspace_name'} = undef;
173
          $self->{'projects'}       = [];
174
          $self->{'project_info'}   = {};
175
          $self->{'project_files'}  = [];
176
        }
177
        $self->{$self->{'type_check'}} = 0;
178
      }
179
      else {
180
        ## Workspace Beginning
181
        ## Deal with the inheritance hiearchy first
182
        if (defined $values[2]) {
183
          foreach my $parent (@{$values[2]}) {
184
            ## Read in the parent onto ourself
185
            my($file) = $self->search_include_path("$parent.$wsbase");
186
            if (!defined $file) {
187
              $file = $self->search_include_path("$parent.$wsext");
188
            }
189
 
190
            if (defined $file) {
191
              push(@{$self->{'reading_parent'}}, 1);
192
              $status = $self->parse_file($file);
193
              pop(@{$self->{'reading_parent'}});
194
 
195
              if (!$status) {
196
                $error = "Invalid parent: $parent";
197
              }
198
            }
199
            else {
200
              $status = 0;
201
              $error = "Unable to locate parent: $parent";
202
            }
203
          }
204
        }
205
 
206
        ## Set up some initial values
207
        if (defined $name) {
208
          if ($name =~ /[\/\\]/) {
209
            $status = 0;
210
            $error = 'Workspaces can not have a slash ' .
211
                     'or a back slash in the name';
212
          }
213
          else {
214
            $name =~ s/^\(\s*//;
215
            $name =~ s/\s*\)$//;
216
 
217
            ## Replace any *'s with the default name
218
            if ($name =~ /\*/) {
219
              $name = $self->fill_type_name(
220
                                    $name,
221
                                    $self->get_default_workspace_name());
222
            }
223
 
224
            $self->{'workspace_name'} = $name;
225
          }
226
        }
227
        $self->{$self->{'type_check'}} = 1;
228
      }
229
    }
230
    elsif ($values[0] eq 'assignment') {
231
      if (defined $validNames{$values[1]}) {
232
        $self->process_assignment($values[1], $values[2]);
233
      }
234
      else {
235
        $error = "Invalid assignment name: $values[1]";
236
        $status = 0;
237
      }
238
    }
239
    elsif ($values[0] eq 'assign_add') {
240
      if (defined $validNames{$values[1]}) {
241
        $self->process_assignment_add($values[1], $values[2]);
242
      }
243
      else {
244
        $error = "Invalid addition name: $values[1]";
245
        $status = 0;
246
      }
247
    }
248
    elsif ($values[0] eq 'assign_sub') {
249
      if (defined $validNames{$values[1]}) {
250
        $self->process_assignment_sub($values[1], $values[2]);
251
      }
252
      else {
253
        $error = "Invalid subtraction name: $values[1]";
254
        $status = 0;
255
      }
256
    }
257
    elsif ($values[0] eq 'component') {
258
      ($status, $error) = $self->parse_scope($ih,
259
                                             $values[1],
260
                                             $values[2],
261
                                             \%validNames);
262
    }
263
    else {
264
      $error = "Unrecognized line: $line";
265
      $status = 0;
266
    }
267
  }
268
  elsif ($status == -1) {
269
    if ($line =~ /\.$wsext$/) {
270
      ($status, $error) = $self->aggregated_workspace($line);
271
    }
272
    else {
273
      push(@{$self->{'project_files'}}, $line);
274
      $status = 1;
275
    }
276
  }
277
 
278
  return $status, $error;
279
}
280
 
281
 
282
sub aggregated_workspace {
283
  my($self) = shift;
284
  my($file) = shift;
285
  my($fh)   = new FileHandle();
286
 
287
  if (open($fh, $file)) {
288
    my($oline) = $self->get_line_number();
289
    my($tc)    = $self->{$self->{'type_check'}};
290
    my($ag)    = $self->{'handled_scopes'}->{$aggregated};
291
    my($psbd)  = $self->{'scoped_basedir'};
292
    my($status, $error, @values) = (0, 'No recognizable lines');
293
 
294
    $self->{'handled_scopes'}->{$aggregated} = undef;
295
    $self->set_line_number(0);
296
    $self->{$self->{'type_check'}} = 0;
297
    $self->{'scoped_basedir'} = $self->mpc_dirname($file);
298
 
299
    while(<$fh>) {
300
      my($line) = $self->preprocess_line($fh, $_);
301
      ($status, $error, @values) = $self->parse_known($line);
302
 
303
      ## Was the line recognized?
304
      if ($status) {
305
        if (defined $values[0]) {
306
          if ($values[0] eq $self->{'grammar_type'}) {
307
            if (defined $values[2]) {
308
              my($name) = basename($file);
309
              $name =~ s/\.[^\.]+$//;
310
              $status = 0;
311
              $error  = 'Aggregated workspace (' . $name .
312
                        ') can not inherit from another workspace';
313
            }
314
            else {
315
              ($status, $error) = $self->parse_scope($fh,
316
                                                     '',
317
                                                     $aggregated,
318
                                                     \%validNames);
319
            }
320
          }
321
          else {
322
            $status = 0;
323
            $error = 'Unable to aggregate ' . $file;
324
          }
325
          last;
326
        }
327
      }
328
      else {
329
        last;
330
      }
331
    }
332
    close($fh);
333
 
334
    $self->{'scoped_basedir'} = $psbd;
335
    $self->{'handled_scopes'}->{$aggregated} = $ag;
336
    $self->{$self->{'type_check'}} = $tc;
337
    $self->set_line_number($oline);
338
 
339
    return $status, $error;
340
  }
341
 
342
  return 0, 'Unable to open ' . $file;
343
}
344
 
345
 
346
sub parse_scope {
347
  my($self)        = shift;
348
  my($fh)          = shift;
349
  my($name)        = shift;
350
  my($type)        = shift;
351
  my($validNames)  = shift;
352
  my($flags)       = shift;
353
  my($elseflags)   = shift;
354
 
355
  if ($name eq 'exclude') {
356
    return $self->parse_exclude($fh, $type);
357
  }
358
  else {
198 bj 359
    ## We need to make a copy of the current assignment hash
360
    ## to ensure that multiply scoped assignments/additions/subtractions
361
    ## work and contain the non-scoped assignments/additions/subtractions
362
    if (!defined $flags) {
363
      my(%copy) = %{$self->get_assignment_hash()};
364
      $flags = \%copy;
365
    }
107 bj 366
    return $self->SUPER::parse_scope($fh, $name, $type,
367
                                     $validNames, $flags, $elseflags);
368
  }
369
}
370
 
371
sub parse_exclude {
372
  my($self)        = shift;
373
  my($fh)          = shift;
374
  my($typestr)     = shift;
375
  my($status)      = 0;
376
  my($errorString) = 'Unable to process exclude';
377
  my($negated)     = undef;
378
 
379
  if ($typestr eq $self->get_default_component_name()) {
380
    $typestr = $self->{'wctype'};
381
  }
382
 
383
  my(@exclude) = ();
384
  my(%types)   = ();
385
  @types{split(/\s*,\s*/, $typestr)} = ();
386
 
387
  ## If there is a negation at all, add our
388
  ## current type, it may be removed below
389
  if ($typestr =~ /!/) {
390
    $negated = 1;
391
    $types{$self->{wctype}} = 1;
392
 
393
    ## Process negated exclusions
394
    foreach my $key (keys %types) {
395
      if ($key =~ /^!\s*(\w+)/) {
396
        ## Remove the negated key
397
        delete $types{$key};
398
 
399
        ## Then delete the key that was negated in the exclusion.
400
        delete $types{$1};
401
      }
402
    }
403
  }
404
 
405
  if (exists $types{$self->{wctype}}) {
406
    while(<$fh>) {
407
      my($line) = $self->preprocess_line($fh, $_);
408
 
409
      if ($line eq '') {
410
      }
411
      elsif ($line =~ /^}(.*)$/) {
412
        if (defined $1 && $1 ne '') {
413
          $status = 0;
414
          $errorString = "Trailing characters found: '$1'";
415
        }
416
        else {
417
          $status = 1;
418
          $errorString = undef;
419
        }
420
        last;
421
      }
422
      else {
423
        if (defined $self->{'scoped_basedir'}) {
424
          $line = $self->{'scoped_basedir'} . '/' . $line;
425
        }
426
        push(@exclude, $line);
427
      }
428
    }
429
 
430
    foreach my $type (keys %types) {
431
      if (!defined $self->{'exclude'}->{$type}) {
432
        $self->{'exclude'}->{$type} = [];
433
      }
434
      push(@{$self->{'exclude'}->{$type}}, @exclude);
435
    }
436
  }
437
  else {
438
    if ($negated) {
439
      ($status, $errorString) = $self->SUPER::parse_scope($fh,
440
                                                          'exclude',
441
                                                          $typestr,
442
                                                          \%validNames);
443
    }
444
    else {
445
      ## If this exclude block didn't match the current type and the
446
      ## exclude wasn't negated, we need to eat the exclude block so that
447
      ## these lines don't get included into the workspace.
448
      while(<$fh>) {
449
        my($line) = $self->preprocess_line($fh, $_);
450
 
451
        if ($line =~ /^}(.*)$/) {
452
          if (defined $1 && $1 ne '') {
453
            $status = 0;
454
            $errorString = "Trailing characters found: '$1'";
455
          }
456
          else {
457
            $status = 1;
458
            $errorString = undef;
459
          }
460
          last;
461
        }
462
      }
463
    }
464
  }
465
 
466
  return $status, $errorString;
467
}
468
 
469
 
470
sub excluded {
471
  my($self) = shift;
472
  my($file) = shift;
473
 
474
  foreach my $excluded (@{$self->{'exclude'}->{$self->{'wctype'}}}) {
475
    if ($excluded eq $file || $file =~ /^$excluded\//) {
476
      return 1;
477
    }
478
  }
479
 
480
  return 0;
481
}
482
 
483
 
484
sub handle_scoped_end {
485
  my($self)   = shift;
486
  my($type)   = shift;
487
  my($flags)  = shift;
488
  my($status) = 1;
489
  my($error)  = undef;
490
 
491
  if ($type eq $aggregated &&
492
      !defined $self->{'handled_scopes'}->{$type}) {
493
    ## Replace instances of $PWD with the current directory plus the
494
    ## scoped_basedir.  We have to do it now otherwise, $PWD will be the
495
    ## wrong directory if it's done later.
496
    if (defined $$flags{'cmdline'}) {
497
      my($dir) = $self->getcwd() . '/' . $self->{'scoped_basedir'};
498
      $$flags{'cmdline'} =~ s/\$PWD(\W)/$dir$1/g;
499
      $$flags{'cmdline'} =~ s/\$PWD$/$dir/;
500
    }
501
 
502
    ## Go back to the previous directory and add the directory contents
503
    ($status, $error) = $self->handle_scoped_unknown(undef, $type, $flags, '.');
504
  }
505
 
506
  $self->{'handled_scopes'}->{$type} = undef;
507
  return $status, $error;
508
}
509
 
510
 
511
sub handle_scoped_unknown {
512
  my($self)   = shift;
513
  my($fh)     = shift;
514
  my($type)   = shift;
515
  my($flags)  = shift;
516
  my($line)   = shift;
517
  my($status) = 1;
518
  my($error)  = undef;
519
  my($dupchk) = undef;
520
 
521
  if ($line =~ /^\w+.*{/) {
522
    if (defined $fh) {
523
      my(@values) = ();
524
      my($tc) = $self->{$self->{'type_check'}};
525
      $self->{$self->{'type_check'}} = 1;
526
      ($status, $error, @values) = $self->parse_line($fh, $line);
527
      $self->{$self->{'type_check'}} = $tc;
528
    }
529
    else {
530
      $status = 0;
531
      $error  = 'Unhandled line: ' . $line;
532
    }
533
    return $status, $error;
534
  }
535
 
536
  if ($type eq $aggregated) {
537
    $line = $self->{'scoped_basedir'} . ($line ne '.' ? "/$line" : '');
538
    my(%dup) = ();
539
    @dup{@{$self->{'project_files'}}} = ();
540
    $dupchk = \%dup;
541
  }
542
 
543
  if (-d $line) {
544
    my(@files) = ();
545
    $self->search_for_files([ $line ], \@files, $$flags{'implicit'});
546
 
547
    ## If we are generating implicit projects within a scope, then
548
    ## we need to remove directories and the parent directories for which
549
    ## there is an mpc file.  Otherwise, the projects will be added
550
    ## twice.
551
    if ($$flags{'implicit'}) {
552
      my(%remove) = ();
553
      foreach my $file (@files) {
554
        if ($file =~ /\.mpc$/) {
555
          my($exc) = $file;
556
          do {
557
            $exc = $self->mpc_dirname($exc);
558
            $remove{$exc} = 1;
559
          } while($exc ne '.' && $exc !~ /[a-z]:[\/\\]/i);
560
        }
561
      }
562
 
563
      my(@acceptable) = ();
564
      foreach my $file (@files) {
565
        if (!defined $remove{$file}) {
566
          push(@acceptable, $file);
567
        }
568
      }
569
      @files = @acceptable;
570
    }
571
 
572
    foreach my $file (@files) {
573
      if (!$self->excluded($file)) {
574
        if (defined $dupchk && exists $$dupchk{$file}) {
575
          $self->warning("Duplicate mpc file ($file) added by an " .
576
                         'aggregate workspace.  It will be ignored.');
577
        }
578
        else {
579
          $self->{'scoped_assign'}->{$file} = $flags;
580
          push(@{$self->{'project_files'}}, $file);
581
        }
582
      }
583
    }
584
  }
585
  else {
586
    if ($line =~ /\.$wsext$/) {
587
      ## An aggregated workspace within an aggregated workspace.
588
      ($status, $error) = $self->aggregated_workspace($line);
589
    }
590
    else {
591
      if (!$self->excluded($line)) {
592
        if (defined $dupchk && exists $$dupchk{$line}) {
593
          $self->warning("Duplicate mpc file ($line) added by an " .
594
                         'aggregate workspace.  It will be ignored.');
595
        }
596
        else {
597
          $self->{'scoped_assign'}->{$line} = $flags;
598
          push(@{$self->{'project_files'}}, $line);
599
        }
600
      }
601
    }
602
  }
603
  $self->{'handled_scopes'}->{$type} = 1;
604
 
605
  return $status, $error;
606
}
607
 
608
 
609
sub search_for_files {
610
  my($self)  = shift;
611
  my($files) = shift;
612
  my($array) = shift;
613
  my($impl)  = shift;
614
 
615
  foreach my $file (@$files) {
616
    if (-d $file) {
617
      my(@f) = $self->generate_default_file_list(
618
                         $file,
619
                         $self->{'exclude'}->{$self->{'wctype'}});
620
      $self->search_for_files(\@f, $array, $impl);
621
      if ($impl) {
622
        $file =~ s/^\.\///;
623
        unshift(@$array, $file);
624
      }
625
    }
626
    else {
627
      if ($file =~ /\.mpc$/) {
628
        $file =~ s/^\.\///;
629
        unshift(@$array, $file);
630
      }
631
    }
632
  }
633
}
634
 
635
 
636
sub remove_duplicate_projects {
637
  my($self)  = shift;
638
  my($list)  = shift;
639
  my($count) = scalar(@$list);
640
 
641
  for(my $i = 0; $i < $count; ++$i) {
642
    my($file) = $$list[$i];
643
    foreach my $inner (@$list) {
644
      if ($file ne $inner && $file eq $self->mpc_dirname($inner) && ! -d $inner) {
645
        splice(@$list, $i, 1);
646
        --$count;
647
        --$i;
648
        last;
649
      }
650
    }
651
  }
652
}
653
 
654
 
655
sub generate_default_components {
656
  my($self)  = shift;
657
  my($files) = shift;
658
  my($impl)  = shift;
659
  my($pjf)   = $self->{'project_files'};
660
 
661
  if (defined $$pjf[0]) {
662
    ## If we have files, then process directories
663
    my(@built) = ();
664
    foreach my $file (@$pjf) {
665
      if (!$self->excluded($file)) {
666
        if (-d $file) {
667
          my(@found) = ();
668
          my(@gen)   = $self->generate_default_file_list(
669
                                $file,
670
                                $self->{'exclude'}->{$self->{'wctype'}});
671
          $self->search_for_files(\@gen, \@found, $impl);
672
          push(@built, @found);
673
          if ($impl || $self->{'scoped_assign'}->{$file}->{'implicit'}) {
674
            push(@built, $file);
675
          }
676
        }
677
        else {
678
          push(@built, $file);
679
        }
680
      }
681
    }
682
 
683
    ## If the workspace is set to implicit
684
    if ($impl) {
685
      ## Remove duplicates from this list
686
      $self->remove_duplicate_projects(\@built);
687
    }
688
 
689
    ## Set the project files
690
    $self->{'project_files'} = \@built;
691
  }
692
  else {
693
    ## Add all of the wanted files in this directory
694
    ## and in the subdirectories.
695
    $self->search_for_files($files, $pjf, $impl);
696
 
697
    ## If the workspace is set to implicit
698
    if ($impl) {
699
      ## Remove duplicates from this list
700
      $self->remove_duplicate_projects($pjf);
701
    }
702
 
703
    ## If no files were found, then we push the empty
704
    ## string, so the Project Creator will generate
705
    ## the default project file.
706
    if (!defined $$pjf[0]) {
707
      push(@$pjf, '');
708
    }
709
  }
710
}
711
 
712
 
713
sub get_default_workspace_name {
714
  my($self) = shift;
715
  my($name) = $self->{'current_input'};
716
 
717
  if ($name eq '') {
718
    $name = $self->base_directory();
719
  }
720
  else {
721
    ## Since files on UNIX can have back slashes, we transform them
722
    ## into underscores.
723
    $name =~ s/\\/_/g;
724
 
725
    ## Take off the extension
726
    $name =~ s/\.[^\.]+$//;
727
  }
728
 
729
  return $name;
730
}
731
 
732
 
733
sub generate_defaults {
734
  my($self) = shift;
735
 
736
  ## Generate default workspace name
737
  if (!defined $self->{'workspace_name'}) {
738
    $self->{'workspace_name'} = $self->get_default_workspace_name();
739
  }
740
 
741
  my(@files) = $self->generate_default_file_list(
742
                        '.',
743
                        $self->{'exclude'}->{$self->{'wctype'}});
744
 
745
  ## Generate default components
746
  $self->generate_default_components(\@files,
747
                                     $self->get_assignment('implicit'));
748
}
749
 
750
 
751
sub get_workspace_name {
752
  my($self) = shift;
753
  return $self->{'workspace_name'};
754
}
755
 
756
 
757
sub get_current_output_name {
758
  my($self) = shift;
759
  return $self->{'current_output'};
760
}
761
 
762
 
763
sub write_workspace {
764
  my($self)      = shift;
765
  my($creator)   = shift;
766
  my($addfile)   = shift;
767
  my($status)    = 1;
768
  my($error)     = undef;
769
  my($duplicates) = 0;
770
 
771
  if ($self->get_toplevel()) {
772
    my($progress) = $self->get_progress_callback();
773
 
774
    if (defined $progress) {
775
      &$progress();
776
    }
777
 
778
    if ($addfile) {
779
      ## To be consistent across multiple project types, we disallow
780
      ## duplicate project names for all types, not just VC6.
781
      ## Note that these name are handled case-insensitive by VC6
782
      my(%names) = ();
783
      foreach my $project (@{$self->{'projects'}}) {
784
        my($name) = lc($self->{'project_info'}->{$project}->[0]);
785
        if (defined $names{$name}) {
786
          ++$duplicates;
787
          $self->error("Duplicate case-insensitive project '$name'. " .
788
                       "Look in " . $self->mpc_dirname($project) . " and " .
789
                       $self->mpc_dirname($names{$name}) .
790
                       " for project name conflicts.");
791
        }
792
        else {
793
          $names{$name} = $project;
794
        }
795
      }
796
    }
797
    else {
798
      $self->{'per_project_workspace_name'} = 1;
799
    }
800
 
801
    my($name)   = $self->transform_file_name($self->workspace_file_name());
802
    my($outdir) = $self->get_outdir();
803
    my($oname)  = $name;
804
 
805
    $name = "$outdir/$name";
806
 
807
    my($abort_creation) = 0;
808
    if ($duplicates > 0) {
809
      $abort_creation = 1;
810
      $error = "Duplicate case-insensitive project names are " .
811
               "not allowed within a workspace.";
812
      $status = 0;
813
    }
814
    else {
815
      if (!defined $self->{'projects'}->[0]) {
816
        $self->information('No projects were created.');
817
        $abort_creation = 1;
818
      }
819
    }
820
 
821
    if (!$abort_creation) {
822
      my($fh)  = new FileHandle();
823
      my($dir) = $self->mpc_dirname($name);
824
 
825
      ## Verify and possibly modify the dependencies
826
      if ($addfile) {
827
        $self->verify_build_ordering();
828
      }
829
 
830
      if ($dir ne '.') {
831
        mkpath($dir, 0, 0777);
832
      }
833
 
834
      if ($addfile || !$self->file_written($name)) {
835
        $self->{'current_output'} = $name;
836
        if ($self->compare_output()) {
837
          ## First write the output to a temporary file
838
          my($tmp) = "$outdir/MWC$>.$$";
839
          my($different) = 1;
840
          if (open($fh, ">$tmp")) {
841
            $self->pre_workspace($fh);
842
            $self->write_comps($fh, $creator, $addfile);
843
            $self->post_workspace($fh);
844
            close($fh);
845
 
846
            if (-r $name &&
847
                -s $tmp == -s $name && compare($tmp, $name) == 0) {
848
              $different = 0;
849
            }
850
          }
851
          else {
852
            $error = "Unable to open $tmp for output.";
853
            $status = 0;
854
          }
855
 
856
          if ($status) {
857
            if ($different) {
858
              unlink($name);
859
              if (rename($tmp, $name)) {
860
                if ($addfile) {
861
                  $self->add_file_written($oname);
862
                }
863
              }
864
              else {
865
                $error = 'Unable to open ' . $self->getcwd() .
866
                         "/$name for output";
867
                $status = 0;
868
              }
869
            }
870
            else {
871
              ## We will pretend that we wrote the file
872
              unlink($tmp);
873
              if ($addfile) {
874
                $self->add_file_written($oname);
875
              }
876
            }
877
          }
878
        }
879
        else {
880
          if (open($fh, ">$name")) {
881
            $self->pre_workspace($fh);
882
            $self->write_comps($fh, $creator, $addfile);
883
            $self->post_workspace($fh);
884
            close($fh);
885
 
886
            if ($addfile) {
887
              $self->add_file_written($oname);
888
            }
889
          }
890
          else {
891
            $error = "Unable to open $name for output.";
892
            $status = 0;
893
          }
894
        }
895
      }
896
    }
897
 
898
    if (!$addfile) {
899
      $self->{'per_project_workspace_name'} = undef;
900
    }
901
  }
902
 
903
  return $status, $error;
904
}
905
 
906
 
907
sub save_project_info {
908
  my($self)     = shift;
909
  my($gen)      = shift;
910
  my($gpi)      = shift;
911
  my($gll)      = shift;
912
  my($dir)      = shift;
913
  my($projects) = shift;
914
  my($pi)       = shift;
915
  my($ll)       = shift;
916
  my($c)        = 0;
917
 
918
  ## For each file written
919
  foreach my $pj (@$gen) {
920
    ## Save the full path to the project file in the array
921
    my($full) = ($dir ne '.' ? "$dir/" : '') . $pj;
922
    push(@$projects, $full);
923
 
924
    ## Get the corresponding generated project info and save it
925
    ## in the hash map keyed on the full project file name
926
    $$pi{$full} = $$gpi[$c];
927
    $c++;
928
  }
929
 
930
  foreach my $key (keys %$gll) {
931
    $$ll{$key} = $$gll{$key};
932
  }
933
}
934
 
935
 
936
sub topname {
937
  my($self) = shift;
938
  my($file) = shift;
939
  my($dir)  = '.';
940
  my($rest) = $file;
941
  if ($file =~ /^([^\/\\]+)[\/\\](.*)/) {
942
    $dir  = $1;
943
    $rest = $2;
944
  }
945
  return $dir, $rest;
946
}
947
 
948
 
949
sub generate_hierarchy {
950
  my($self)      = shift;
951
  my($creator)   = shift;
952
  my($origproj)  = shift;
953
  my($originfo)  = shift;
954
  my($current)   = undef;
955
  my(@saved)     = ();
956
  my(%sinfo)     = ();
957
  my($cwd)       = $self->getcwd();
958
 
959
  ## Make a copy of these.  We will be modifying them.
960
  ## It is necessary to sort the projects to get the correct ordering.
961
  ## Projects in the current directory must come before projects in
962
  ## other directories.
198 bj 963
  my(@projects)  = sort { return $self->sort_projects_by_directory($a, $b) + 0;
107 bj 964
                        } @{$origproj};
965
  my(%projinfo)  = %{$originfo};
966
 
967
  foreach my $prj (@projects) {
968
    my($top, $rest) = $self->topname($prj);
969
 
970
    if (!defined $current) {
971
      $current = $top;
972
      push(@saved, $rest);
973
      $sinfo{$rest} = $projinfo{$prj};
974
    }
975
    elsif ($top ne $current) {
976
      if ($current ne '.') {
977
        ## Write out the hierachical workspace
978
        $self->cd($current);
979
        $self->generate_hierarchy($creator, \@saved, \%sinfo);
980
 
981
        $self->{'projects'}       = \@saved;
982
        $self->{'project_info'}   = \%sinfo;
983
        $self->{'workspace_name'} = $self->base_directory();
984
 
985
        my($status, $error) = $self->write_workspace($creator);
986
        if (!$status) {
987
          $self->error($error);
988
        }
989
        $self->cd($cwd);
990
      }
991
 
992
      ## Start the next one
993
      $current = $top;
994
      @saved = ($rest);
995
      %sinfo = ();
996
      $sinfo{$rest} = $projinfo{$prj};
997
    }
998
    else {
999
      push(@saved, $rest);
1000
      $sinfo{$rest} = $projinfo{$prj};
1001
    }
1002
  }
1003
  if (defined $current && $current ne '.') {
1004
    $self->cd($current);
1005
    $self->generate_hierarchy($creator, \@saved, \%sinfo);
1006
 
1007
    $self->{'projects'}       = \@saved;
1008
    $self->{'project_info'}   = \%sinfo;
1009
    $self->{'workspace_name'} = $self->base_directory();
1010
 
1011
    my($status, $error) = $self->write_workspace($creator);
1012
    if (!$status) {
1013
      $self->error($error);
1014
    }
1015
    $self->cd($cwd);
1016
  }
1017
}
1018
 
1019
 
1020
sub generate_project_files {
1021
  my($self)      = shift;
1022
  my($status)    = (scalar @{$self->{'project_files'}} == 0 ? 1 : 0);
1023
  my(@projects)  = ();
1024
  my(%pi)        = ();
1025
  my(%liblocs)   = ();
1026
  my($creator)   = $self->project_creator();
1027
  my($cwd)       = $self->getcwd();
1028
  my($impl)      = $self->get_assignment('implicit');
1029
  my($postkey)   = $creator->get_dynamic() .
1030
                   $creator->get_static() . "-$self";
1031
  my($previmpl)  = $impl;
1032
  my($prevcache) = $self->{'cacheok'};
1033
  my(%gstate)    = $creator->save_state();
1034
  my($genimpdep) = $self->generate_implicit_project_dependencies();
1035
 
1036
  ## Remove the address portion of the $self string
1037
  $postkey =~ s/=.*//;
1038
 
1039
  ## Set the source file callback on our project creator
1040
  $creator->set_source_listing_callback([\&source_listing_callback, $self]);
1041
 
1042
  foreach my $ofile (@{$self->{'project_files'}}) {
1043
    if (!$self->excluded($ofile)) {
1044
      my($file)    = $ofile;
1045
      my($dir)     = $self->mpc_dirname($file);
1046
      my($restore) = 0;
1047
 
1048
      if (defined $self->{'scoped_assign'}->{$ofile}) {
1049
        ## Handle the implicit assignment
1050
        my($oi) = $self->{'scoped_assign'}->{$ofile}->{'implicit'};
1051
        if (defined $oi) {
1052
          $previmpl = $impl;
1053
          $impl     = $oi;
1054
        }
1055
 
1056
        ## Handle the cmdline assignment
1057
        my($cmdline) = $self->{'scoped_assign'}->{$ofile}->{'cmdline'};
1058
        if (defined $cmdline && $cmdline ne '') {
1059
          ## Save the cacheok value
1060
          $prevcache = $self->{'cacheok'};
1061
 
1062
          ## Get the current parameters and process the command line
1063
          my(%parameters) = $self->current_parameters();
1064
          $self->process_cmdline($cmdline, \%parameters);
1065
 
1066
          ## Set the parameters on the creator
1067
          $creator->restore_state(\%parameters);
1068
          $restore = 1;
1069
        }
1070
      }
1071
 
1072
      ## If we are generating implicit projects and the file is a
1073
      ## directory, then we set the dir to the file and empty the file
1074
      if ($impl && -d $file) {
1075
        $dir  = $file;
1076
        $file = '';
1077
 
1078
        ## If the implicit assignment value was not a number, then
1079
        ## we will add this value to our base projects.
1080
        if ($impl !~ /^\d+$/) {
1081
          my($bps) = $creator->get_baseprojs();
1082
          push(@$bps, split(/\s+/, $impl));
1083
          $restore = 1;
1084
          $self->{'cacheok'} = 0;
1085
        }
1086
      }
1087
 
1088
      ## Generate the key for this project file
1089
      my($prkey) = $self->getcwd() . '/' .
1090
                   ($file eq '' ? $dir : $file) . "-$postkey";
1091
 
1092
      ## We must change to the subdirectory for
1093
      ## which this project file is intended
1094
      if ($self->cd($dir)) {
1095
        my($files_written) = [];
1096
        my($gen_proj_info) = [];
1097
        my($gen_lib_locs)  = {};
1098
        if ($self->{'cacheok'} && defined $allprojects{$prkey}) {
1099
          $files_written = $allprojects{$prkey};
1100
          $gen_proj_info = $allprinfo{$prkey};
1101
          $gen_lib_locs  = $allliblocs{$prkey};
1102
          $status = 1;
1103
        }
1104
        else {
1105
          $status = $creator->generate(basename($file));
1106
 
1107
          ## If any one project file fails, then stop
1108
          ## processing altogether.
1109
          if (!$status) {
1110
            ## We don't restore the state before we leave,
198 bj 1111
            ## but that's ok since we will be exiting right now.
1112
            return $status, $creator,
1113
                   "Unable to process " . ($file eq '' ? " in $dir" : $file);
107 bj 1114
          }
1115
 
1116
          ## Get the individual project information and
1117
          ## generated file name(s)
1118
          $files_written = $creator->get_files_written();
1119
          $gen_proj_info = $creator->get_project_info();
1120
          $gen_lib_locs  = $creator->get_lib_locations();
1121
 
1122
          if ($self->{'cacheok'}) {
1123
            $allprojects{$prkey} = $files_written;
1124
            $allprinfo{$prkey}   = $gen_proj_info;
1125
            $allliblocs{$prkey}  = $gen_lib_locs;
1126
          }
1127
        }
1128
        $self->cd($cwd);
1129
        $self->save_project_info($files_written, $gen_proj_info,
1130
                                 $gen_lib_locs, $dir,
1131
                                 \@projects, \%pi, \%liblocs);
1132
      }
1133
      else {
1134
        ## Unable to change to the directory.
1135
        ## We don't restore the state before we leave,
1136
        ## but that's ok since we will be exiting soon.
198 bj 1137
        return 0, $creator, "Unable to change directory to $dir";
107 bj 1138
      }
1139
 
1140
      ## Return things to the way they were
1141
      if (defined $self->{'scoped_assign'}->{$ofile}) {
1142
        $impl = $previmpl;
1143
      }
1144
      if ($restore) {
1145
        $self->{'cacheok'} = $prevcache;
1146
        $creator->restore_state(\%gstate);
1147
      }
1148
    }
1149
    else {
1150
      ## This one was excluded, so status is ok
1151
      $status = 1;
1152
    }
1153
  }
1154
 
1155
  ## Add implict project dependencies based on source files
1156
  ## that have been used by multiple projects.  If we do it here
1157
  ## before we call generate_hierarchy(), we don't have to call it
1158
  ## in generate_hierarchy() for each workspace.
1159
  $self->{'projects'}     = \@projects;
1160
  $self->{'project_info'} = \%pi;
1161
  if ($status && $genimpdep) {
1162
    $self->add_implicit_project_dependencies($creator, $cwd);
1163
  }
1164
 
1165
  ## If we are generating the hierarchical workspaces, then do so
1166
  $self->{'lib_locations'} = \%liblocs;
1167
  if ($self->get_hierarchy() || $self->workspace_per_project()) {
1168
    my($orig) = $self->{'workspace_name'};
1169
    $self->generate_hierarchy($creator, \@projects, \%pi);
1170
    $self->{'workspace_name'} = $orig;
1171
  }
1172
 
1173
  ## Reset the projects and project_info
1174
  $self->{'projects'}      = \@projects;
1175
  $self->{'project_info'}  = \%pi;
1176
 
1177
  return $status, $creator;
1178
}
1179
 
1180
 
1181
sub array_contains {
1182
  my($self)   = shift;
1183
  my($left)   = shift;
1184
  my($right)  = shift;
1185
  my(%check)  = ();
1186
 
1187
  ## Initialize the hash keys with the left side array
1188
  @check{@$left} = ();
1189
 
1190
  ## Check each element on the right against the left.
1191
  foreach my $r (@$right) {
1192
    if (exists $check{$r}) {
1193
      return 1;
1194
    }
1195
  }
1196
 
1197
  return 0;
1198
}
1199
 
1200
 
1201
sub non_intersection {
1202
  my($self)   = shift;
1203
  my($left)   = shift;
1204
  my($right)  = shift;
1205
  my($over)   = shift;
1206
  my($status) = 0;
1207
  my(%check)  = ();
1208
 
1209
  ## Initialize the hash keys with the left side array
1210
  @check{@$left} = ();
1211
 
1212
  ## Check each element on the right against the left.
1213
  ## Store anything that isn't in the left side in the over array.
1214
  foreach my $r (@$right) {
1215
    if (exists $check{$r}) {
1216
      $status = 1;
1217
    }
1218
    else {
1219
      push(@$over, $r);
1220
    }
1221
  }
1222
  return $status;
1223
}
1224
 
1225
 
1226
sub indirect_depdency {
1227
  my($self)   = shift;
1228
  my($dir)    = shift;
1229
  my($ccheck) = shift;
1230
  my($cfile)  = shift;
1231
 
1232
  if ($self->{'project_info'}->{$ccheck}->[1] =~ /$cfile/) {
1233
    return 1;
1234
  }
1235
  else {
1236
    my($deps) = $self->create_array(
1237
                         $self->{'project_info'}->{$ccheck}->[1]);
1238
    foreach my $dep (@$deps) {
1239
      if (defined $self->{'project_info'}->{"$dir$dep"} &&
1240
          $self->indirect_depdency($dir, "$dir$dep", $cfile)) {
1241
        return 1;
1242
      }
1243
    }
1244
  }
1245
 
1246
  return 0;
1247
}
1248
 
1249
 
1250
sub add_implicit_project_dependencies {
1251
  my($self)      = shift;
1252
  my($creator)   = shift;
1253
  my($cwd)       = shift;
1254
  my(%bidir)     = ();
1255
  my(%save)      = ();
1256
 
1257
  ## Take the current working directory and regular expression'ize it.
1258
  $cwd = $self->escape_regex_special($cwd);
1259
 
1260
  ## Look at each projects file list and check it against all of the
1261
  ## others.  If any of the other projects file lists contains anothers
1262
  ## file, then they are dependent (due to build parallelism).  So, we
1263
  ## append the dependency and remove the file in question from the
1264
  ## project so that the next time around the foreach, we don't find it
1265
  ## as a dependent on the one that we just modified.
1266
  my(@pflkeys) = keys %{$self->{'project_file_list'}};
1267
  foreach my $key (@pflkeys) {
1268
    foreach my $ikey (@pflkeys) {
1269
      ## Not the same project and
1270
      ## The same directory and
1271
      ## We've not already added a dependency to this project
1272
      if ($key ne $ikey &&
1273
          ($self->{'project_file_list'}->{$key}->[1] eq
1274
           $self->{'project_file_list'}->{$ikey}->[1]) &&
1275
          (!defined $bidir{$ikey} ||
1276
           !$self->array_contains($bidir{$ikey}, [$key]))) {
1277
        my(@over) = ();
1278
        if ($self->non_intersection(
1279
                      $self->{'project_file_list'}->{$key}->[2],
1280
                      $self->{'project_file_list'}->{$ikey}->[2],
1281
                      \@over)) {
1282
          ## The project contains shared source files, so we need to
1283
          ## look into adding an implicit inter-project dependency.
1284
          $save{$ikey} = $self->{'project_file_list'}->{$ikey}->[2];
1285
          $self->{'project_file_list'}->{$ikey}->[2] = \@over;
1286
          if (defined $bidir{$key}) {
1287
            push(@{$bidir{$key}}, $ikey);
1288
          }
1289
          else {
1290
            $bidir{$key} = [$ikey];
1291
          }
1292
          my($append) = $creator->translate_value('after', $key);
1293
          my($file)   = $self->{'project_file_list'}->{$ikey}->[0];
1294
          my($dir)    = $self->{'project_file_list'}->{$ikey}->[1];
1295
          my($cfile)  = $self->escape_regex_special(
1296
                              $creator->translate_value('after', $ikey));
1297
          ## Remove our starting directory from the projects directory
1298
          ## to get the right part of the directory to prepend.
1299
          $dir =~ s/^$cwd[\/\\]*//;
1300
 
1301
          ## Turn the append value into a key for 'project_info' and
1302
          ## prepend the directory to the file.
1303
          my($ccheck) = $append;
1304
          $ccheck =~ s/"//g;
1305
          if ($dir ne '') {
1306
            $dir .= '/';
1307
            $ccheck = "$dir$ccheck";
1308
            $file = "$dir$file";
1309
          }
1310
 
1311
          ## If the append value key contains a reference to the project
1312
          ## that we were going to append the dependency value, then
1313
          ## ignore the generated dependency.  It is redundant and
1314
          ## quite possibly wrong.
1315
          if (!defined $self->{'project_info'}->{$ccheck} ||
1316
              !$self->indirect_depdency($dir, $ccheck, $cfile)) {
1317
            ## Append the dependency
1318
            $self->{'project_info'}->{$file}->[1] .= " $append";
1319
          }
1320
        }
1321
      }
1322
    }
1323
  }
1324
 
1325
  ## Restore the modified values in case this method is called again
1326
  ## which is the case when using the -hierarchy option.
1327
  foreach my $skey (keys %save) {
1328
    $self->{'project_file_list'}->{$skey}->[2] = $save{$skey};
1329
  }
1330
}
1331
 
1332
 
1333
sub get_projects {
1334
  my($self) = shift;
1335
  return $self->{'projects'};
1336
}
1337
 
1338
 
1339
sub get_project_info {
1340
  my($self) = shift;
1341
  return $self->{'project_info'};
1342
}
1343
 
1344
 
1345
sub get_lib_locations {
1346
  my($self) = shift;
1347
  return $self->{'lib_locations'};
1348
}
1349
 
1350
 
1351
sub get_first_level_directory {
1352
  my($self) = shift;
1353
  my($file) = shift;
1354
  my($dir)  = undef;
1355
  if (($file =~ tr/\///) > 0) {
1356
    $dir = $file;
1357
    $dir =~ s/^([^\/]+\/).*/$1/;
1358
    $dir =~ s/\/+$//;
1359
  }
1360
  else {
1361
    $dir = '.';
1362
  }
1363
  return $dir;
1364
}
1365
 
1366
 
1367
sub sort_within_group {
1368
  my($self)    = shift;
1369
  my($list)    = shift;
1370
  my($start)   = shift;
1371
  my($end)     = shift;
1372
  my($deps)    = undef;
1373
  my($ccount)  = 0;
1374
  my($cmax)    = ($end - $start) + 1;
1375
  my($previ)   = -1;
1376
  my($prevpjs) = [];
1377
  my($movepjs) = [];
1378
 
1379
  ## Put the projects in the order specified
1380
  ## by the project dpendencies.
1381
  for(my $i = $start; $i <= $end; ++$i) {
1382
    ## If our moved project equals our previously moved project then
1383
    ## we count this as a possible circular dependency.
1384
    if (defined $$movepjs[0] && defined $$prevpjs[0] &&
1385
        $$movepjs[0] == $$prevpjs[0] && $$movepjs[1] == $$prevpjs[1]) {
1386
      ++$ccount;
1387
    }
1388
    else {
1389
      $ccount = 0;
1390
    }
1391
 
1392
    ## Detect circular dependencies
1393
    if ($ccount > $cmax) {
1394
      my(@prjs) = ();
1395
      foreach my $mvgr (@$movepjs) {
1396
        push(@prjs, $$list[$mvgr]);
1397
      }
1398
      my($other) = $$movepjs[0] - 1;
1399
      if ($other < $start || $other == $$movepjs[1] || !defined $$list[$other]) {
1400
        $other = undef;
1401
      }
1402
      $self->warning('Circular dependency detected while processing the ' .
1403
                     ($self->{'current_input'} eq '' ?
1404
                       'default' : $self->{'current_input'}) .
1405
                     ' workspace. ' .
1406
                     'The following projects are involved: ' .
1407
                     (defined $other ? "$$list[$other], " : '') .
1408
                     join(' and ', @prjs));
1409
      return;
1410
    }
1411
 
1412
    ## Keep track of the previous project movement
1413
    $prevpjs = $movepjs;
1414
    if ($previ < $i) {
1415
      $movepjs = [];
1416
    }
1417
    $previ = $i;
1418
 
1419
    $deps = $self->get_validated_ordering($$list[$i]);
1420
    if ($deps ne '') {
1421
      my($baseproj) = basename($$list[$i]);
1422
      my($darr) = $self->create_array($deps);
1423
 
1424
      my($moved) = 0;
1425
      foreach my $dep (@$darr) {
1426
        if ($baseproj ne $dep) {
1427
          ## See if the dependency is listed after this project
1428
          for(my $j = $i + 1; $j <= $end; ++$j) {
1429
            if (basename($$list[$j]) eq $dep) {
1430
              $movepjs = [$i, $j];
1431
              ## If so, move it in front of the current project.
1432
              ## The original code, which had splices, didn't always
1433
              ## work correctly (especially on AIX for some reason).
1434
              my($save) = $$list[$j];
1435
              for(my $k = $j; $k > $i; --$k) {
1436
                $$list[$k] = $$list[$k - 1];
1437
              }
1438
              $$list[$i] = $save;
1439
 
1440
              ## Mark that an entry has been moved
1441
              $moved = 1;
1442
              $j--;
1443
            }
1444
          }
1445
        }
1446
      }
1447
      if ($moved) {
1448
        $i--;
1449
      }
1450
    }
1451
  }
1452
}
1453
 
1454
 
1455
sub sort_by_groups {
1456
  my($self)    = shift;
1457
  my($list)    = shift;
1458
  my($grindex) = shift;
1459
  my(@groups)  = @$grindex;
1460
  my($ccount)  = 0;
1461
  my($cmax)    = $#groups;
1462
  my($prevgi)  = -1;
1463
  my($prevgrs) = [];
1464
  my($movegrs) = [];
1465
 
1466
  for(my $gi = 0; $gi <= $#groups; ++$gi) {
1467
    ## If our moved group equals our previously moved group then
1468
    ## we count this as a possible circular dependency.
1469
    if (defined $$movegrs[0] && defined $$prevgrs[0] &&
1470
        $$movegrs[0] == $$prevgrs[0] && $$movegrs[1] == $$prevgrs[1]) {
1471
      ++$ccount;
1472
    }
1473
    else {
1474
      $ccount = 0;
1475
    }
1476
 
1477
    ## Detect circular dependencies
1478
    if ($ccount > $cmax) {
1479
      my(@dirs) = ();
1480
      foreach my $mvgr (@$movegrs) {
1481
        push(@dirs, $$list[$groups[$mvgr]->[0]]);
1482
        $dirs[$#dirs] =~ s/[\/\\].*//;
1483
      }
1484
      $self->warning('Circular dependency detected while processing the ' .
1485
                     ($self->{'current_input'} eq '' ?
1486
                       'default' : $self->{'current_input'}) .
1487
                     ' workspace. ' .
198 bj 1488
                     'The following directories or projects are involved: ' .
107 bj 1489
                     join(' and ', @dirs));
1490
      return;
1491
    }
1492
 
1493
    ## Build up the group dependencies
1494
    my(%gdeps) = ();
1495
    for(my $i = $groups[$gi]->[0]; $i <= $groups[$gi]->[1]; ++$i) {
1496
      my($deps) = $self->get_validated_ordering($$list[$i]);
1497
      if ($deps ne '') {
1498
        my($darr) = $self->create_array($deps);
1499
        foreach my $dep (@$darr) {
1500
          $gdeps{$dep} = 1;
1501
        }
1502
      }
1503
    }
1504
 
1505
    ## Keep track of the previous group movement
1506
    $prevgrs = $movegrs;
1507
    if ($prevgi < $gi) {
1508
      $movegrs = [];
1509
    }
1510
    $prevgi = $gi;
1511
 
1512
    ## Search the rest of the groups for any of the group dependencies
1513
    my($moved) = 0;
1514
    for(my $gj = $gi + 1; $gj <= $#groups; ++$gj) {
1515
      for(my $i = $groups[$gj]->[0]; $i <= $groups[$gj]->[1]; ++$i) {
1516
        if (defined $gdeps{basename($$list[$i])}) {
1517
          ## Move this group ($gj) in front of the current group ($gi)
1518
          $movegrs = [$gj, $gi];
1519
          my(@save) = ();
1520
          for(my $j = $groups[$gi]->[1] + 1; $j <= $groups[$gj]->[1]; ++$j) {
1521
            push(@save, $$list[$j]);
1522
          }
1523
          my($offset) = $groups[$gj]->[1] - $groups[$gi]->[1];
1524
          for(my $j = $groups[$gi]->[1]; $j >= $groups[$gi]->[0]; --$j) {
1525
            $$list[$j + $offset] = $$list[$j];
1526
          }
1527
          for(my $j = 0; $j <= $#save; ++$j) {
1528
            $$list[$groups[$gi]->[0] + $j] = $save[$j];
1529
          }
1530
 
1531
          ## Update the group indices
1532
          my($shiftamt) = ($groups[$gi]->[1] - $groups[$gi]->[0]) + 1;
1533
          for(my $j = $gi + 1; $j <= $gj; ++$j) {
1534
            $groups[$j]->[0] -= $shiftamt;
1535
            $groups[$j]->[1] -= $shiftamt;
1536
          }
1537
          my(@grsave) = @{$groups[$gi]};
1538
          $grsave[0] += $offset;
1539
          $grsave[1] += $offset;
1540
          for(my $j = $gi; $j < $gj; ++$j) {
1541
            $groups[$j] = $groups[$j + 1];
1542
          }
1543
          $groups[$gj] = \@grsave;
1544
 
1545
          ## Signify that we have moved a group
1546
          $moved = 1;
1547
          last;
1548
        }
1549
      }
1550
      if ($moved) {
1551
        ## Start over from the first group
1552
        $gi = -1;
1553
        last;
1554
      }
1555
    }
1556
  }
1557
}
1558
 
1559
 
1560
sub sort_dependencies {
1561
  my($self)     = shift;
1562
  my($projects) = shift;
198 bj 1563
  my(@list)     = sort { return $self->sort_projects_by_directory($a, $b) + 0;
1564
                       } @$projects;
107 bj 1565
 
1566
  ## Put the projects in the order specified
1567
  ## by the project dpendencies.  We only need to do
1568
  ## this if there is more than one element in the array.
1569
  if ($#list > 0) {
1570
    ## First determine the individual groups
1571
    my(@grindex)  = ();
1572
    my($previous) = [0, undef];
1573
    for(my $li = 0; $li <= $#list; ++$li) {
1574
      my($dir) = $self->get_first_level_directory($list[$li]);
1575
      if (!defined $previous->[1]) {
1576
        $previous = [$li, $dir];
1577
      }
1578
      elsif ($previous->[1] ne $dir) {
1579
        push(@grindex, [$previous->[0], $li - 1]);
1580
        $previous = [$li, $dir];
1581
      }
1582
    }
198 bj 1583
    push(@grindex, [$previous->[0], $#list]);
107 bj 1584
 
1585
    ## Next, sort the individual groups
1586
    foreach my $gr (@grindex) {
1587
      if ($$gr[0] != $$gr[1]) {
1588
        $self->sort_within_group(\@list, @$gr);
1589
      }
1590
    }
1591
 
1592
    ## Now sort the groups as single entities
1593
    if ($#grindex > 0) {
1594
      $self->sort_by_groups(\@list, \@grindex);
1595
    }
1596
  }
1597
 
1598
  return @list;
1599
}
1600
 
1601
 
1602
sub number_target_deps {
1603
  my($self)     = shift;
1604
  my($projects) = shift;
1605
  my($pjs)      = shift;
1606
  my($targets)  = shift;
1607
  my(@list)     = $self->sort_dependencies($projects);
1608
 
1609
  ## This block of code must be done after the list of dependencies
1610
  ## has been sorted in order to get the correct project numbers.
1611
  for(my $i = 0; $i <= $#list; ++$i) {
1612
    my($project) = $list[$i];
1613
    if (defined $$pjs{$project}) {
1614
      my($name, $deps) = @{$$pjs{$project}};
1615
      if (defined $deps && $deps ne '') {
1616
        my(%targetnumbers) = ();
1617
        my($darr) = $self->create_array($deps);
1618
 
1619
        ## For each dependency, search in the sorted list
1620
        ## up to the point of this project for the projects
1621
        ## that this one depends on.  When the project is
1622
        ## found, we put the target number in a hash map (to avoid
1623
        ## duplicates).
1624
        foreach my $dep (@$darr) {
1625
          for(my $j = 0; $j < $i; ++$j) {
1626
            if (basename($list[$j]) eq $dep) {
1627
              $targetnumbers{$j} = 1;
1628
            }
1629
          }
1630
        }
1631
 
1632
        ## Get the keys of the hash map and store the
1633
        ## array in the hash keyed on the project file.
1634
        my(@numbers) = sort { $a <=> $b } keys %targetnumbers;
1635
        if (defined $numbers[0]) {
1636
          $$targets{$project} = \@numbers;
1637
        }
1638
      }
1639
    }
1640
  }
1641
 
1642
  return @list;
1643
}
1644
 
1645
 
1646
sub project_target_translation {
198 bj 1647
  my($self) = shift;
1648
  my($case) = shift;
1649
  my(%map)  = ();
107 bj 1650
 
1651
  ## Translate project names to avoid target collision with
1652
  ## some versions of make.
1653
  foreach my $key (keys %{$self->{'project_info'}}) {
1654
    my($dir)  = $self->mpc_dirname($key);
1655
    my($name) = $self->{'project_info'}->{$key}->[0];
1656
 
198 bj 1657
    ## We want to compare to the upper most directory.  This will be the
1658
    ## one that may conflict with the project name.
1659
    $dir =~ s/[\/\\].*//;
1660
    if (($case && $dir eq $name) || (!$case && lc($dir) eq lc($name))) {
107 bj 1661
      $map{$key} = "$name-target";
1662
    }
1663
    else {
1664
      $map{$key} = $name;
1665
    }
1666
  }
1667
  return \%map;
1668
}
1669
 
1670
 
1671
sub optionError {
1672
  my($self) = shift;
1673
  my($str)  = shift;
1674
  $self->warning("$self->{'current_input'}: $str.");
1675
}
1676
 
1677
 
1678
sub process_cmdline {
1679
  my($self)       = shift;
1680
  my($cmdline)    = shift;
1681
  my($parameters) = shift;
1682
 
1683
  ## It's ok to use the cache
1684
  $self->{'cacheok'} = 1;
1685
 
1686
  if (defined $cmdline && $cmdline ne '') {
1687
    my($args) = $self->create_array($cmdline);
1688
 
1689
    ## Look for environment variables
1690
    foreach my $arg (@$args) {
1691
      while($arg =~ /\$(\w+)/) {
1692
        my($name) = $1;
1693
        my($val)  = '';
1694
        if ($name eq 'PWD') {
1695
          $val = $self->getcwd();
1696
        }
1697
        elsif (defined $ENV{$name}) {
1698
          $val = $ENV{$name};
1699
        }
1700
        $arg =~ s/\$\w+/$val/;
1701
      }
1702
    }
1703
 
1704
    my($options) = $self->options('MWC', {}, 0, @$args);
1705
    if (defined $options) {
1706
      foreach my $key (keys %$options) {
1707
        my($type) = $self->is_set($key, $options);
1708
 
1709
        if (!defined $type) {
1710
          ## This option was not used, so we ignore it
1711
        }
1712
        elsif ($type eq 'ARRAY') {
1713
          push(@{$parameters->{$key}}, @{$options->{$key}});
1714
        }
1715
        elsif ($type eq 'HASH') {
1716
          foreach my $hk (keys %{$options->{$key}}) {
1717
            $parameters->{$key}->{$hk} = $options->{$key}->{$hk};
1718
          }
1719
        }
1720
        elsif ($type eq 'SCALAR') {
1721
          $parameters->{$key} = $options->{$key};
1722
        }
1723
      }
1724
 
1725
      ## Issue warnings for these options
1726
      if (defined $options->{'recurse'}) {
1727
        $self->optionError('-recurse is ignored');
1728
      }
1729
      if (defined $options->{'reldefs'}) {
1730
        $self->optionError('-noreldefs is ignored');
1731
      }
1732
      if (defined $options->{'coexistence'}) {
1733
        $self->optionError('-make_coexistence is ignored');
1734
      }
1735
      if (defined $options->{'genins'}) {
1736
        $self->optionError('-genins is ignored');
1737
      }
1738
      if (defined $options->{'into'}) {
1739
        $self->optionError('-into is ignored');
1740
      }
1741
      if (defined $options->{'language'}) {
1742
        $self->optionError('-language is ignored');
1743
      }
1744
      if (defined $options->{'input'}->[0]) {
1745
        $self->optionError('Command line files ' .
1746
                           'specified in a workspace are ignored');
1747
      }
1748
 
1749
      ## Determine if it's ok to use the cache
1750
      my(@cacheInvalidating) = ('global', 'include', 'baseprojs',
1751
                                'template', 'ti', 'relative',
1752
                                'addtemp', 'addproj', 'feature_file',
1753
                                'features', 'use_env', 'expand_vars');
1754
      foreach my $key (@cacheInvalidating) {
1755
        if ($self->is_set($key, $options)) {
1756
          $self->{'cacheok'} = 0;
1757
          last;
1758
        }
1759
      }
1760
    }
1761
  }
1762
}
1763
 
1764
 
1765
sub current_parameters {
1766
  my($self) = shift;
1767
  my(%parameters) = $self->save_state();
1768
 
1769
  ## We always want the project creator to generate a toplevel
1770
  $parameters{'toplevel'} = 1;
1771
  return %parameters;
1772
}
1773
 
1774
 
1775
sub project_creator {
1776
  my($self) = shift;
1777
  my($str)  = "$self";
1778
 
1779
  ## NOTE: If the subclassed WorkspaceCreator name prefix does not
1780
  ##       match the name prefix of the ProjectCreator, this code
1781
  ##       will not work and the subclassed WorkspaceCreator will
1782
  ##       need to override this method.
1783
 
1784
  $str =~ s/Workspace/Project/;
1785
  $str =~ s/=HASH.*//;
1786
 
1787
  ## Set up values for each project creator
1788
  ## If we have command line arguments in the workspace, then
1789
  ## we process them before creating the project creator
1790
  my($cmdline)    = $self->get_assignment('cmdline');
1791
  my(%parameters) = $self->current_parameters();
1792
  $self->process_cmdline($cmdline, \%parameters);
1793
 
1794
  ## Create the new project creator with the updated parameters
1795
  return $str->new($parameters{'global'},
1796
                   $parameters{'include'},
1797
                   $parameters{'template'},
1798
                   $parameters{'ti'},
1799
                   $parameters{'dynamic'},
1800
                   $parameters{'static'},
1801
                   $parameters{'relative'},
1802
                   $parameters{'addtemp'},
1803
                   $parameters{'addproj'},
1804
                   $parameters{'progress'},
1805
                   $parameters{'toplevel'},
1806
                   $parameters{'baseprojs'},
1807
                   $self->{'global_feature_file'},
1808
                   $parameters{'feature_file'},
1809
                   $parameters{'features'},
1810
                   $parameters{'hierarchy'},
1811
                   $self->{'exclude'}->{$self->{'wctype'}},
1812
                   $self->make_coexistence(),
1813
                   $parameters{'name_modifier'},
1814
                   $parameters{'apply_project'},
1815
                   $self->{'generate_ins'},
1816
                   $parameters{'into'},
1817
                   $self->get_language(),
1818
                   $parameters{'use_env'},
1819
                   $parameters{'expand_vars'});
1820
}
1821
 
1822
 
1823
sub sort_files {
1824
  #my($self) = shift;
1825
  return 0;
1826
}
1827
 
1828
 
1829
sub make_coexistence {
1830
  my($self) = shift;
1831
  return $self->{'coexistence'};
1832
}
1833
 
1834
 
1835
sub get_modified_workspace_name {
1836
  my($self)   = shift;
1837
  my($name)   = shift;
1838
  my($ext)    = shift;
1839
  my($nows)   = shift;
1840
  my($nmod)   = $self->get_name_modifier();
1841
 
1842
  if (defined $nmod) {
1843
    $nmod =~ s/\*/$name/g;
1844
    $name = $nmod;
1845
  }
1846
 
1847
  ## If this is a per project workspace, then we should not
1848
  ## modify the workspace name.  It may overwrite another workspace
1849
  ## but that's ok, it will only be a per project workspace.
1850
  ## Also, if we don't want the workspace name attached ($nows) then
1851
  ## we just return the name plus the extension.
1852
  if ($nows || $self->{'per_project_workspace_name'}) {
1853
    return "$name$ext";
1854
  }
1855
 
1856
  my($pwd)    = $self->getcwd();
1857
  my($type)   = $self->{'wctype'};
1858
  my($wsname) = $self->get_workspace_name();
1859
 
1860
  if (!defined $previous_workspace_name{$type}->{$pwd}) {
1861
    $previous_workspace_name{$type}->{$pwd} = $wsname;
1862
    $self->{'current_workspace_name'} = undef;
1863
  }
1864
  else {
1865
    my($prefix) = ($name eq $wsname ? $name : "$name.$wsname");
1866
    $previous_workspace_name{$type}->{$pwd} = $wsname;
1867
    while($self->file_written("$prefix" .
1868
                              ($self->{'modified_count'} > 0 ?
1869
                                   ".$self->{'modified_count'}" : '') .
1870
                              "$ext")) {
1871
      ++$self->{'modified_count'};
1872
    }
1873
    $self->{'current_workspace_name'} =
1874
               "$prefix" . ($self->{'modified_count'} > 0 ?
1875
                                ".$self->{'modified_count'}" : '') . "$ext";
1876
  }
1877
 
1878
  return (defined $self->{'current_workspace_name'} ?
1879
                  $self->{'current_workspace_name'} : "$name$ext");
1880
}
1881
 
1882
 
1883
sub generate_recursive_input_list {
1884
  my($self)    = shift;
1885
  my($dir)     = shift;
1886
  my($exclude) = shift;
1887
  return $self->extension_recursive_input_list($dir, $exclude, $wsext);
1888
}
1889
 
1890
 
1891
sub verify_build_ordering {
1892
  my($self) = shift;
1893
  foreach my $project (@{$self->{'projects'}}) {
1894
    $self->get_validated_ordering($project);
1895
  }
1896
}
1897
 
1898
 
1899
sub get_validated_ordering {
1900
  my($self)    = shift;
1901
  my($project) = shift;
1902
  my($deps)    = undef;
1903
 
1904
  if (defined $self->{'ordering_cache'}->{$project}) {
1905
    $deps = $self->{'ordering_cache'}->{$project};
1906
  }
1907
  else {
1908
    $deps = '';
1909
    if (defined $self->{'project_info'}->{$project}) {
1910
      my($name) = undef;
1911
      ($name, $deps) = @{$self->{'project_info'}->{$project}};
1912
      if (defined $deps && $deps ne '') {
1913
        my($darr) = $self->create_array($deps);
1914
        foreach my $dep (@$darr) {
1915
          my($found) = 0;
1916
          ## Avoid circular dependencies
1917
          if ($dep ne $name && $dep ne basename($project)) {
1918
            foreach my $p (@{$self->{'projects'}}) {
1919
              if ($dep eq $self->{'project_info'}->{$p}->[0] ||
1920
                  $dep eq basename($p)) {
1921
                $found = 1;
1922
                last;
1923
              }
1924
            }
1925
            if (!$found) {
1926
              if (defined $ENV{MPC_VERBOSE_ORDERING}) {
1927
                $self->warning("'$name' references '$dep' which has " .
1928
                               "not been processed.");
1929
              }
1930
              my($reg) = $self->escape_regex_special($dep);
1931
              $deps =~ s/\s*"$reg"\s*/ /g;
1932
            }
1933
          }
1934
        }
1935
 
1936
        $deps =~ s/^\s+//;
1937
        $deps =~ s/\s+$//;
1938
      }
1939
 
1940
      $self->{'ordering_cache'}->{$project} = $deps;
1941
    }
1942
  }
1943
 
1944
  return $deps;
1945
}
1946
 
1947
 
1948
sub source_listing_callback {
1949
  my($self)         = shift;
1950
  my($project_file) = shift;
1951
  my($project_name) = shift;
1952
  my(@files)        = @_;
1953
  my($cwd)          = $self->getcwd();
1954
  $self->{'project_file_list'}->{$project_name} = [ $project_file,
1955
                                                    $cwd, \@files ];
1956
}
1957
 
198 bj 1958
 
1959
sub sort_projects_by_directory {
1960
  my($self)  = shift;
1961
  my($left)  = shift;
1962
  my($right) = shift;
1963
  my($sa)    = ($left =~ /\//);
1964
  my($sb)    = ($right =~ /\//);
1965
 
1966
  if ($sa && !$sb) {
1967
    return 1;
1968
  }
1969
  elsif ($sb && !$sa) {
1970
    return -1;
1971
  }
1972
  return $left cmp $right;
1973
}
1974
 
107 bj 1975
# ************************************************************
1976
# Virtual Methods To Be Overridden
1977
# ************************************************************
1978
 
1979
sub supports_make_coexistence {
1980
  #my($self) = shift;
1981
  return 0;
1982
}
1983
 
1984
 
1985
sub generate_implicit_project_dependencies {
1986
  #my($self) = shift;
1987
  return 0;
1988
}
1989
 
1990
 
1991
sub workspace_file_name {
1992
  #my($self) = shift;
1993
  return '';
1994
}
1995
 
1996
 
1997
sub workspace_per_project {
1998
  #my($self) = shift;
1999
  return 0;
2000
}
2001
 
2002
 
2003
sub pre_workspace {
2004
  #my($self) = shift;
2005
  #my($fh)   = shift;
2006
}
2007
 
2008
 
2009
sub write_comps {
2010
  #my($self) = shift;
2011
  #my($fh)   = shift;
2012
  #my($gens) = shift;
2013
  #my($top)  = shift;
2014
}
2015
 
2016
 
2017
sub post_workspace {
2018
  #my($self) = shift;
2019
  #my($fh)   = shift;
2020
}
2021
 
2022
 
2023
1;