1 package AutomakeWorkspaceCreator
;
3 # ************************************************************
4 # Description : A Automake Workspace (Makefile) creator
5 # Author : J.T. Conklin & Steve Huston
6 # Create Date : 5/13/2002
7 # ************************************************************
9 # ************************************************************
11 # ************************************************************
16 use AutomakeProjectCreator
;
22 @ISA = qw(MakePropertyBase WorkspaceCreator);
24 # ************************************************************
26 # ************************************************************
28 my $acfile = 'configure.ac';
29 my $acmfile = 'configure.ac.Makefiles';
31 # ************************************************************
33 # ************************************************************
39 ## Can't cache as some intermediate project files are deleted
40 ## and must be regenerated if a project is regenerated.
45 sub files_are_different
{
46 my($self, $old, $new) = @_;
49 my $lh = new FileHandle
();
50 my $rh = new FileHandle
();
51 if (open($lh, $old)) {
52 if (open($rh, $new)) {
65 $diff = 1 if ($lline ne $rline);
72 $diff = 1 if (defined $rline);
75 } while(!$done && !$diff);
85 sub workspace_file_name
{
86 return $_[0]->get_modified_workspace_name('Makefile', '.am');
90 sub workspace_per_project
{
98 my $crlf = $self->crlf();
100 $self->print_workspace_comment($fh,
101 '## Process this file with automake to create Makefile.in', $crlf,
103 '## ', '$', 'Id', '$', $crlf,
105 '## This file was generated by MPC. Any changes made directly to', $crlf,
106 '## this file will be lost the next time it is generated.', $crlf,
108 '## MPC Command:', $crlf,
109 '## ', $self->create_command_line_string($0, @ARGV), $crlf, $crlf);
114 my($self, $fh, $creator, $toplevel) = @_;
115 my $projects = $self->get_projects();
116 my @list = $self->sort_dependencies($projects);
117 my $crlf = $self->crlf();
122 my $have_subdirs = 0;
123 my $outdir = $self->get_outdir();
126 ## This step writes a configure.ac.Makefiles list into the starting
127 ## directory. The list contains of all the Makefiles generated down
128 ## the tree. configure.ac can include this to get an up-to-date list
129 ## of all the involved Makefiles.
133 my $need_acmfile = 1;
134 if (! -e
"$outdir/$acfile") {
135 my $acfh = new FileHandle
();
136 if (open($acfh, ">$outdir/$acfile")) {
137 print $acfh "AC_INIT(", $self->get_workspace_name(), ", 1.0)$crlf",
138 "AM_INIT_AUTOMAKE([1.9])$crlf",
141 "AC_PROG_CXXCPP$crlf",
142 "AC_PROG_LIBTOOL$crlf",
145 my $fp = $creator->get_feature_parser();
146 my $features = $fp->get_names();
147 my %assoc = %{$self->get_associated_projects()};
148 foreach my $feature (sort @
$features) {
149 print $acfh 'AM_CONDITIONAL(BUILD_', uc($feature),
150 ', ', ($fp->get_value($feature) ?
'true' : 'false'),
152 delete $assoc{$feature};
154 foreach my $akey (keys %assoc) {
155 print $acfh 'AM_CONDITIONAL(BUILD_', uc($akey), ', true)', $crlf
160 "m4_include([$acmfile])$crlf",
167 $self->information("$acfile already exists.");
168 $need_acmfile = !$self->edit_config_ac("$outdir/$acfile", \
@list);
172 unlink("$outdir/$acmfile");
173 $mfh = new FileHandle
();
174 open($mfh, ">$outdir/$acmfile");
175 ## The top-level is never listed as a dependency, so it needs to be
177 $makefile = $self->mpc_basename($self->get_current_output_name());
178 $makefile =~ s/\.am$//;
179 print $mfh "AC_CONFIG_FILES([ $makefile ])$crlf";
180 $proj_dir_seen{'.'} = 1;
184 ## If we're writing a configure.ac.Makefiles file, every seen project
185 ## goes into it. Since we only write this at the starting directory
186 ## level, it'll include all projects processed at this level and below.
187 foreach my $dep (@list) {
189 ## There should be a Makefile at each level, but it's not a project,
190 ## it's a workspace; therefore, it's not in the list of projects.
191 ## Since we're consolidating all the project files into one workspace
192 ## Makefile.am per directory level, be sure to add that Makefile.am
193 ## entry at each level there's a project dependency.
194 my $dep_dir = $self->mpc_dirname($dep);
195 if (!defined $proj_dir_seen{$dep_dir}) {
196 $proj_dir_seen{$dep_dir} = 1;
197 ## If there are directory levels between project-containing
198 ## directories (for example, at this time in
199 ## ACE_wrappers/apps/JAWS/server, there are no projects at the
200 ## apps or apps/JAWS level) we need to insert the Makefile
201 ## entries for the levels without projects. They won't be listed
202 ## in @list but are needed for make to traverse intervening directory
203 ## levels down to where the project(s) to build are.
204 my @dirs = split /\//, $dep_dir;
206 foreach my $dep (@dirs) {
208 if (!defined $proj_dir_seen{$inter_dir}) {
209 $proj_dir_seen{$inter_dir} = 1;
210 print $mfh "AC_CONFIG_FILES([ $inter_dir/$makefile ])$crlf";
214 print $mfh "AC_CONFIG_FILES([ $dep_dir/$makefile ])$crlf";
218 ## Get a unique list of next-level directories for SUBDIRS.
219 ## To make sure we keep the dependencies correct, insert the '.' for
220 ## any local projects in the proper place. Remember if any subdirs
221 ## are seen to know if we need a SUBDIRS entry generated.
222 my $dir = $self->get_first_level_directory($dep);
223 if (!defined $unique{$dir}) {
225 unshift(@dirs, $dir);
228 ## At each directory level, each project is written into a separate
229 ## Makefile.<project>.am file. To bring these back into the build
230 ## process, they'll be sucked back into the workspace Makefile.am file.
231 ## Remember which ones to pull in at this level.
232 unshift(@locals, $dep);
238 close($mfh) if ($mfh);
240 # The Makefile.<project>.am files append values to build target macros
241 # for each program/library to build. When using conditionals, however,
242 # a plain empty assignment is done outside the conditional to be sure
243 # that each append can be done regardless of the condition test. Because
244 # automake fails if the first isn't a plain assignment, we need to resolve
245 # these situations when combining the files. The code below makes sure
246 # that there's always a plain assignment, whether it's one outside a
247 # conditional or the first append is changed to a simple assignment.
249 # We should consider extending this to support all macros that match
250 # automake's uniform naming convention. A true perl wizard probably
251 # would be able to do this in a single line of code.
254 my %conditional_targets;
255 my %unconditional_targets;
256 my %first_instance_unconditional;
257 my $installable_headers;
258 my $installable_pkgconfig;
264 ## To avoid unnecessarily emitting blank assignments, rip through the
265 ## Makefile.<project>.am files and check for conditions.
267 my $pfh = new FileHandle
();
268 foreach my $local (reverse @locals) {
269 if ($local =~ /Makefile\.(.*)\.am/) {
273 $project_name = 'nobase';
276 if (open($pfh, "$outdir/$local")) {
277 my $in_condition = 0;
278 my $regok = $self->escape_regex_special($project_name);
279 my $inc_pattern = $regok . '_include_HEADERS';
280 my $pkg_pattern = $regok . '_pkginclude_HEADERS';
282 # Don't look at comments
285 $in_condition++ if (/^if\s*/);
286 $in_condition-- if (/^endif\s*/);
288 if ( /(^[a-zA-Z][a-zA-Z0-9_]*_(PROGRAMS|LIBRARIES|LTLIBRARIES|LISP|PYTHON|JAVA|SCRIPTS|DATA|SOURCES|HEADERS|MANS|TEXINFOS|LIBADD|LDADD|DEPENDENCIES))\s*\+=\s*/
289 || /(^CLEANFILES)\s*\+=\s*/
290 || /(^EXTRA_DIST)\s*\+=\s*/
294 $conditional_targets{$1}++;
297 $first_instance_unconditional{$1} = 1;
299 $unconditional_targets{$1}++;
303 $installable_pkgconfig= 1 if (/^pkgconfig_DATA/);
304 $installable_headers = 1
305 if (/^$inc_pattern\s*\+=\s*/ || /^$pkg_pattern\s*\+=\s*/);
307 elsif (/includedir\s*=\s*(.*)/) {
316 $errorString = "Unable to open $local for reading.";
328 ## Print out the Makefile.am.
329 my $wsHelper = WorkspaceHelper
::get
($self);
330 my $convert_header_name;
331 if ($status && ((!defined $includedir && $installable_headers)
332 || $installable_pkgconfig)) {
333 if (!defined $includedir && $installable_headers) {
334 my $incdir = $wsHelper->modify_value('includedir',
335 $self->get_includedir());
337 print $fh "includedir = \@includedir\@$incdir$crlf";
338 $convert_header_name = 1;
341 if ($installable_pkgconfig) {
342 print $fh "pkgconfigdir = \@libdir\@/pkgconfig$crlf";
348 if ($status && @locals) {
349 ($status, $errorString) = $wsHelper->write_settings($self, $fh, @locals);
352 ## Create the SUBDIRS setting. If there are associated projects, then
353 ## we will also set up conditionals for it as well.
354 if ($status && $have_subdirs == 1) {
355 my $assoc = $self->get_associated_projects();
358 my $entry = " \\$crlf ";
359 print $fh 'SUBDIRS =';
360 foreach my $dir (reverse @dirs) {
362 foreach my $akey (keys %$assoc) {
363 if (defined $$assoc{$akey}->{$dir}) {
364 if ($akey eq $cond) {
366 print $fh $entry, '@', $dir, '@';
371 push(@aorder, $akey);
372 push(@
{$afiles{$akey}}, $dir);
377 elsif ($toplevel && defined $$assoc{$akey}->{uc($dir)} &&
379 print $fh $entry, '@', uc($dir), '@';
384 print $fh $entry, $dir if (!$found);
388 foreach my $aorder (@aorder) {
389 if (defined $afiles{$aorder}) {
392 'if BUILD_', uc($aorder), "\n",
394 foreach my $afile (@
{$afiles{$aorder}}) {
397 delete $afiles{$aorder};
398 print $fh $crlf, 'endif', $crlf;
401 print $fh $crlf if ($second);
404 ## Now, for each target used in a conditional, emit a blank assignment
405 ## and mark that we've seen that target to avoid changing the += to =
406 ## as the individual files are pulled in.
407 if ($status && %conditional_targets) {
411 while ( ($primary, $count) = each %conditional_targets) {
412 if (! $first_instance_unconditional{$primary}
413 && ($unconditional_targets{$primary} || ($count > 1)))
415 print $fh "$primary =$crlf";
423 ## Take the local Makefile.<project>.am files and insert each one here,
425 if ($status && @locals) {
426 my $pfh = new FileHandle
();
427 my $liblocs = $self->get_lib_locations();
428 my $here = $self->getcwd();
429 my $start = $self->getstartdir();
431 foreach my $local (reverse @locals) {
432 if (open($pfh, "$outdir/$local")) {
433 print $fh "## $local", $crlf;
435 my $look_for_libs = 0;
440 # Don't emit comments
443 # Check for explicit targets
449 $in_explicit = undef;
452 elsif ($prev_line !~ /\\$/ && /^([\w\/\
.\
-\s
]+):/) {
456 if (defined $explicit{$target}) {
461 $explicit{$target} = 1;
465 if ($convert_header_name) {
466 if ($local =~ /Makefile\.(.*)\.am/) {
470 $project_name = 'nobase';
472 my $regok = $self->escape_regex_special($project_name);
473 my $inc_pattern = $regok . '_include_HEADERS';
474 my $pkg_pattern = $regok . '_pkginclude_HEADERS';
475 if (/^$inc_pattern\s*\+=\s*/ || /^$pkg_pattern\s*\+=\s*/) {
476 $_ =~ s/^$regok/nobase/;
480 if ( /(^[a-zA-Z][a-zA-Z0-9_]*_(PROGRAMS|LIBRARIES|LTLIBRARIES|LISP|PYTHON|JAVA|SCRIPTS|DATA|SOURCES|HEADERS|MANS|TEXINFOS|LIBADD|LDADD|DEPENDENCIES))\s*\+=\s*/
481 || /(^CLEANFILES)\s*\+=\s*/
482 || /(^EXTRA_DIST)\s*\+=\s*/
484 if (!defined ($seen{$1})) {
490 ## This scheme relies on automake.mpd emitting the 'la' libs first.
491 ## Look for all the libXXXX.la, find out where they are located
492 ## relative to the start of the MPC run, and relocate the reference
493 ## to that location under $top_builddir. Unless the referred-to
494 ## library is in the current directory, then leave it undecorated
495 ## so the automake-generated dependency orders the build correctly.
496 if ($look_for_libs) {
497 my @libs = /\s+(lib(\w+).la)/gm;
498 my $libcount = @libs / 2;
499 for(my $i = 0; $i < $libcount; ++$i) {
500 my $libfile = $libs[$i * 2];
501 my $libname = $libs[$i * 2 + 1];
502 my $reldir = $$liblocs{$libname};
504 ## If we could not find a relative directory for this
505 ## library, it may be that it is a decorated library name.
506 ## We will search for an approximate match.
507 if (!defined $reldir) {
508 my $tmpname = $libname;
509 while($tmpname ne '') {
510 $tmpname = substr($tmpname, 0, length($tmpname) - 1);
511 if (defined $$liblocs{$tmpname}) {
512 $reldir = $$liblocs{$tmpname};
513 $self->warning("Relative directory for $libname " .
514 "was approximated with $tmpname.");
520 if (defined $reldir) {
521 my $append = ($reldir eq '' ?
'' : "/$reldir");
522 if ("$start$append" ne $here) {
523 my $mod = $wsHelper->modify_libpath($_, $reldir, $libfile);
528 s/$libfile/\$(top_builddir)$append\/$libfile/;
533 my $mod = $wsHelper->modify_libpath($_, $reldir, $libfile);
538 $self->warning("No reldir found for $libname ($libfile).");
542 $look_for_libs = 0 if ($libcount == 0);
544 $look_for_libs = 1 if (/_LDADD = \\$/ || /_LIBADD = \\$/);
546 ## I have introduced a one line delay so that I can simplify
547 ## the automake template. If our current line is empty, then
548 ## we will remove the trailing backslash before printing the
549 ## previous line. Automake is horribly unforgiving so we must
550 ## avoid this situation at all cost.
551 if (defined $prev_line) {
552 $prev_line =~ s/\s*\\$// if ($_ =~ /^\s*$/);
553 print $fh $prev_line;
557 ## The one line delay requires that we print out the previous
558 ## line (if there was one) when we reach the end of the file.
559 if (defined $prev_line) {
560 $prev_line =~ s/\s*\\$//;
561 print $fh $prev_line;
565 unlink("$outdir/$local");
569 $errorString = "Unable to open $local for reading.";
576 ## If this is the top-level Makefile.am, it needs the directives to pass
577 ## autoconf/automake flags down the tree when running autoconf.
578 ## *** This may be too closely tied to how we have things set up in ACE,
579 ## even though it's recommended practice. ***
580 if ($status && $toplevel) {
583 'ACLOCAL = @ACLOCAL@', $crlf,
584 'ACLOCAL_AMFLAGS = ',
586 $wsHelper->modify_value('amflags', $m4inc) : $m4inc), $crlf,
587 'AUTOMAKE_OPTIONS = foreign', $crlf, $crlf,
589 $wsHelper->modify_value('extra', '') : '');
592 ## Finish up with the cleanup specs.
593 if ($status && @locals) {
594 ## There is no reason to emit this if there are no local targets.
595 ## An argument could be made that it shouldn't be emitted in any
596 ## case because it could be handled by CLEANFILES or a verbatim
599 print $fh '## Clean up template repositories, etc.', $crlf,
600 'clean-local:', $crlf,
601 "\t-rm -f *~ *.bak *.rpo *.sym lib*.*_pure_* core core.*",
603 "\t-rm -f gcctemp.c gcctemp so_locations *.ics", $crlf,
604 "\t-rm -rf cxx_repository ptrepository ti_files", $crlf,
605 "\t-rm -rf templateregistry ir.out", $crlf,
606 "\t-rm -rf ptrepository SunWS_cache Templates.DB", $crlf;
609 return $status, $errorString;
615 my $value = $self->getcwd();
616 my $start = $self->getstartdir();
618 ## Take off the starting directory
619 $value =~ s/\Q$start\E//;
625 my($self, $file, $files) = @_;
626 my $fh = new FileHandle
();
629 if (open($fh, $file)) {
630 my $crlf = $self->crlf();
633 my $assoc = $self->get_associated_projects();
636 my $in_config_files = 0;
642 ## Remove comments and trailing space
643 $line =~ s/\bdnl\s+.*//;
648 elsif ($line =~ /^\s*if\s+test\s+["]?([^"]+)["]?\s*=\s*\w+;\s*then/) {
649 ## Entering an if test, save the name
652 $name =~ s/.*_build_//;
655 elsif ($line =~ /^\s*if\s+test\s+-d\s+(.+);\s*then/) {
656 ## Entering an if test -d, save the name
659 $name =~ s/\$srcdir\///;
662 elsif ($line =~ /^\s*fi$/) {
665 elsif ($line =~ /^(\s*AC_CONFIG_FILES\s*\(\s*\[)/) {
666 ## Entering an AC_CONFIG_FILES section, start ignoring the entries
668 push(@lines, "$1\n");
670 if ($lines[$#lines] =~ /^(\s+)/) {
673 $in_config_files = 1;
675 elsif ($in_config_files) {
676 if ($line =~ /(.*)\]\s*\).*/) {
677 ## We've reached the end of the AC_CONFIG_FILES section
678 my $olast = pop(@lines);
679 if ($olast =~ /^[^\s]+(\s*\]\s*\).*)/) {
682 ## Add in the Makefiles for this configuration
683 if ($#in < 0 && !defined $proj_dir_seen{'.'}) {
684 push(@lines, $indent . 'Makefile' . $crlf);
685 $proj_dir_seen{'.'} = 1;
688 foreach my $dep (@
$files) {
689 ## First things first, see if we've already seen this
690 ## project's directory. If we have, then there's nothing
691 ## else we need to do with it.
692 my $dep_dir = $self->mpc_dirname($dep);
693 if (!defined $proj_dir_seen{$dep_dir}) {
695 my @dirs = split(/\//, $dep_dir);
698 if ($base =~ s/\/.*//) {
700 foreach my $akey (keys %$assoc) {
701 if (defined $$assoc{$akey}->{$base} ||
702 defined $$assoc{$akey}->{uc($base)}) {
704 if (index($base, $in[0]) >= 0) {
707 for(my $i = 0; $i <= $#in; $i++) {
708 if (!defined $dirs[$i] ||
709 index($dirs[$i], $in[$i]) < 0) {
716 ## We need to see into the future here. :-)
717 ## If the second element of @dirs matches an
718 ## association key, we'll guess that there will
719 ## be a "build" section devoted to it.
720 if (!defined $dirs[1] ||
721 !defined $$assoc{$dirs[1]}) {
742 $proj_dir_seen{$dep_dir} = 1;
744 foreach my $dep (@dirs) {
746 if (!defined $proj_dir_seen{$inter_dir}) {
747 $proj_dir_seen{$inter_dir} = 1;
748 push(@lines, $indent . $inter_dir . "/Makefile$crlf");
752 push(@lines, $indent . $dep_dir . "/Makefile$crlf");
756 push(@lines, $olast);
757 $in_config_files = 0;
767 ## Make a backup and create the new file
768 my $backup = $file . '.bak';
769 if (copy
($file, $backup)) {
770 my @buf = stat($file);
771 if (defined $buf[8] && defined $buf[9]) {
772 utime($buf[8], $buf[9], $backup);
774 if (open($fh, ">$file")) {
775 foreach my $line (@lines) {
783 $self->warning("Unable to create backup file: $backup");