1#!/usr/bin/env perl
2#
3# Copyright (c) 2009-2021 Cisco Systems, Inc.  All rights reserved
4# Copyright (c) 2010      Oracle and/or its affiliates.  All rights reserved.
5# Copyright (c) 2013      Mellanox Technologies, Inc.
6#                         All rights reserved.
7# Copyright (c) 2013-2014 Intel, Inc.  All rights reserved.
8# Copyright (c) 2015-2020 Research Organization for Information Science
9#                         and Technology (RIST).  All rights reserved.
10# Copyright (c) 2015      IBM Corporation.  All rights reserved.
11#
12# $COPYRIGHT$
13#
14# Additional copyrights may follow
15#
16# $HEADER$
17#
18
19use strict;
20
21use Cwd;
22use File::Basename;
23use File::Find;
24use Data::Dumper;
25use Getopt::Long;
26
27#
28# Global variables
29#
30
31# Sentinel file to remove if we fail
32my $sentinel;
33
34# The m4 file we'll write at the end
35my $m4_output_file = "config/autogen_found_items.m4";
36my $m4;
37# Sanity check file
38my $topdir_file = "opal/include/opal_config_bottom.h";
39my $dnl_line = "dnl ---------------------------------------------------------------------------";
40
41# Data structures to fill up with all the stuff we find
42my $mca_found;
43my $mpiext_found;
44my $mpicontrib_found;
45my @subdirs;
46
47# Command line parameters
48my $no_ompi_arg = 0;
49my $no_orte_arg = 0;
50my $no_oshmem_arg = 0;
51my $quiet_arg = 0;
52my $debug_arg = 0;
53my $help_arg = 0;
54my $platform_arg = 0;
55my $include_arg = 0;
56my $exclude_arg = 0;
57my $force_arg = 0;
58
59# Include/exclude lists
60my $include_list;
61my $exclude_list;
62
63# Minimum versions
64my $ompi_automake_version = "1.12.2";
65my $ompi_autoconf_version = "2.69";
66my $ompi_libtool_version = "2.4.2";
67
68# Search paths
69my $ompi_autoconf_search = "autoconf";
70my $ompi_automake_search = "automake";
71my $ompi_libtoolize_search = "libtoolize;glibtoolize";
72
73# One-time setup
74my $username;
75my $hostname;
76my $full_hostname;
77
78# Patch program
79my $patch_prog = "patch";
80# Solaris "patch" doesn't understand unified diffs, and will cause
81# autogen.pl to hang with a "File to patch:" prompt. Default to Linux
82# "patch", but use "gpatch" on Solaris.
83if ($^O eq "solaris") {
84    $patch_prog = "gpatch";
85}
86
87$username = getpwuid($>);
88$full_hostname = `hostname`;
89chomp($full_hostname);
90$hostname = $full_hostname;
91$hostname =~ s/^([\w\-]+)\..+/\1/;
92
93##############################################################################
94
95sub my_die {
96    unlink($sentinel)
97        if ($sentinel);
98    die @_;
99}
100
101sub my_exit {
102    my ($ret) = @_;
103    unlink($sentinel)
104        if ($sentinel && $ret != 0);
105    exit($ret);
106}
107
108##############################################################################
109
110sub verbose {
111    print @_
112        if (!$quiet_arg);
113}
114
115sub debug {
116    print @_
117        if ($debug_arg);
118}
119
120sub debug_dump {
121    my $d = new Data::Dumper([@_]);
122    $d->Purity(1)->Indent(1);
123    debug $d->Dump;
124}
125
126##############################################################################
127
128sub read_config_params {
129    my ($filename, $dir_prefix) = @_;
130
131    my $dir = dirname($filename);
132    open(FILE, $filename) ||
133        my_die "Can't open $filename";
134    my $file;
135    $file .= $_
136        while(<FILE>);
137    close(FILE);
138
139    # Save all lines of the form "foo = bar" in a hash
140    my $ret;
141    while ($file =~ s/^\s*(\w+)\s*=\s*(.+)\s*$//m) {
142        my $key = $1;
143        my $val = $2;
144
145        # Strip off any leading and trailing "'s
146        $val = $1
147            if ($val =~ m/^\"(.+)\"$/);
148
149        $ret->{$key} = $val;
150    }
151
152    # Split PARAM_CONFIG_FILES into an array
153    if (exists($ret->{PARAM_CONFIG_FILES})) {
154        my @out;
155        foreach my $f (split(/\s+/, $ret->{PARAM_CONFIG_FILES})) {
156            push(@out, "$dir_prefix/$f");
157        }
158        $ret->{PARAM_CONFIG_FILES} = \@out;
159    }
160
161    debug_dump($ret);
162    return $ret;
163}
164
165##############################################################################
166
167# Process a "subdir", meaning that the directory isn't a component or
168# an extension; it probably just needs an autoreconf, autogen, etc.
169sub process_subdir {
170    my ($dir) = @_;
171
172    # Chdir to the subdir
173    print "\n=== Processing subdir: $dir\n";
174    my $start = Cwd::cwd();
175    chdir($dir);
176
177    # Run an action depending on what we find in that subdir
178    if (-x "autogen.pl") {
179        print "--- Found autogen.pl; running...\n";
180        safe_system("./autogen.pl");
181    } elsif (-x "autogen.sh") {
182        print "--- Found autogen.sh; running...\n";
183        safe_system("./autogen.sh");
184    } elsif (-f "configure.in" || -f "configure.ac") {
185        print "--- Found configure.in|ac; running autoreconf...\n";
186        safe_system("autoreconf -ivf");
187        print "--- Patching autotools output... :-(\n";
188    } else {
189        my_die "Found subdir, but no autogen.sh or configure.in|ac to do anything";
190    }
191
192    # Ensure that we got a good configure executable.
193    my_die "Did not generate a \"configure\" executable in $dir.\n"
194        if (! -x "configure");
195
196    # Fix known issues in Autotools output
197    patch_autotools_output($start);
198
199    # Chdir back to where we came from
200    chdir($start);
201}
202
203##############################################################################
204
205sub process_autogen_subdirs {
206    my ($dir) = @_;
207
208    my $file = "$dir/autogen.subdirs";
209    if (-f $file) {
210        open(FILE, $file) || my_die "Can't open $file";
211        while (<FILE>) {
212            chomp;
213            $_ =~ s/#.*$//;
214            $_ =~ s/^\s*//;
215            $_ =~ s/\s*$//;
216            if ($_ ne "") {
217                print "    Found subdir: $_ (will process later)\n";
218
219                # Note: there's no real technical reason to defer
220                # processing the subdirs.  It's more of an aesthetic
221                # reason -- don't interrupt the current flow of
222                # finding mca / ext / contribs (which is a nice, fast
223                # process).  Then process the subdirs (which is a slow
224                # process) all at once.
225                push(@subdirs, "$dir/$_");
226            }
227        }
228        close(FILE);
229    }
230}
231
232##############################################################################
233
234sub mca_process_component {
235    my ($topdir, $project, $framework, $component) = @_;
236
237    my $pname = $project->{name};
238    my $pdir = $project->{dir};
239    my $cdir = "$topdir/$pdir/mca/$framework/$component";
240
241    return
242        if (! -d $cdir);
243
244    # Process this directory (pretty much the same treatment as for
245    # mpiext, so it's in a sub).
246    my $found_component;
247
248    # Does this directory have a configure.m4 file?
249    if (-f "$cdir/configure.m4") {
250        $found_component->{"configure.m4"} = 1;
251        verbose "    Found configure.m4 file\n";
252    }
253
254    $found_component->{"name"} = $component;
255
256    # Push the results onto the $mca_found hash array
257    push(@{$mca_found->{$pname}->{$framework}->{"components"}},
258         $found_component);
259
260    # Is there an autogen.subdirs in here?
261    process_autogen_subdirs($cdir);
262}
263
264##############################################################################
265
266sub ignored {
267    my ($dir) = @_;
268
269    # If this directory does not have .opal_ignore, or if it has a
270    # .opal_unignore that has my username in it, then add it to the
271    # list of components.
272    my $ignored = 0;
273
274    if (-f "$dir/.opal_ignore") {
275        $ignored = 1;
276    }
277    if (-f "$dir/.opal_unignore") {
278        open(UNIGNORE, "$dir/.opal_unignore") ||
279            my_die "Can't open $dir/.opal_unignore file";
280        my $unignore;
281        $unignore .= $_
282            while (<UNIGNORE>);
283        close(UNIGNORE);
284
285        $ignored = 0
286            if ($unignore =~ /^$username$/m ||
287                $unignore =~ /^$username\@$hostname$/m ||
288                $unignore =~ /^$username\@$full_hostname$/m);
289    }
290
291    return $ignored;
292}
293
294##############################################################################
295
296sub mca_process_framework {
297    my ($topdir, $project, $framework) = @_;
298
299    my $pname = $project->{name};
300    my $pdir = $project->{dir};
301
302    # Does this framework have a configure.m4 file?
303    my $dir = "$topdir/$pdir/mca/$framework";
304    if (-f "$dir/configure.m4") {
305        $mca_found->{$pname}->{$framework}->{"configure.m4"} = 1;
306        verbose "    Found framework configure.m4 file\n";
307    }
308
309    # Did we exclude all components for this framework?
310    if (exists($exclude_list->{$framework}) &&
311        $exclude_list->{$framework}[0] eq "AGEN_EXCLUDE_ALL") {
312        verbose "    => Excluded\n";
313    } else {
314        # Look for component directories in this framework
315        if (-d $dir) {
316            $mca_found->{$pname}->{$framework}->{found} = 1;
317            opendir(DIR, $dir) ||
318                my_die "Can't open $dir directory";
319            foreach my $d (sort(readdir(DIR))) {
320                # Skip any non-directory, "base", or any dir that
321                # begins with "."
322                next
323                    if (! -d "$dir/$d" || $d eq "base" ||
324                        substr($d, 0, 1) eq ".");
325
326                # Skip any component that doesn't have a configure.m4
327                # or Makefile.am as we couldn't build it anyway
328                if (! -f "$dir/$d/configure.m4" &&
329                    ! -f "$dir/$d/Makefile.am" &&
330                    ! -f "$dir/$d/configure.ac" &&
331                    ! -f "$dir/$d/configure.in") {
332                     verbose "    => No sentinel file found in $dir/$d -> Excluded\n";
333                     next;
334                }
335
336                verbose "--- Found $pname / $framework / $d component\n";
337
338                # Skip if specifically excluded
339                if (exists($exclude_list->{$framework}) &&
340                    $exclude_list->{$framework}[0] eq $d) {
341                    verbose "    => Excluded\n";
342                    next;
343                }
344
345                # Skip if the framework is on the include list, but
346                # doesn't contain this component
347                if (exists($include_list->{$framework})) {
348                    my $tst = 0;
349                    foreach my $ck (@{$include_list->{$framework}}) {
350                        if ($ck ne $d) {
351                            verbose "    => Not included\n";
352                            $tst = 1;
353                            last;
354                        }
355                    }
356                    if ($tst) {
357                        next;
358                    }
359                }
360
361                # Check ignore status
362                if (ignored("$dir/$d")) {
363                    verbose "    => Ignored (found .opal_ignore file)\n";
364                } else {
365                    mca_process_component($topdir, $project, $framework, $d);
366                }
367            }
368        }
369        closedir(DIR);
370    }
371}
372
373##############################################################################
374
375sub mca_generate_framework_header(\$\@) {
376    my ($project, @frameworks) = @_;
377    my $framework_array_output="";
378    my $framework_decl_output="";
379
380    foreach my $framework (@frameworks) {
381        # There is no common framework object
382        if ($framework ne "common") {
383            my $framework_name = "${project}_${framework}_base_framework";
384            $framework_array_output .= "    &$framework_name,\n";
385            $framework_decl_output .= "extern mca_base_framework_t $framework_name;\n";
386        }
387    }
388
389    my $ifdef_string = uc "${project}_FRAMEWORKS_H";
390    open(FRAMEWORKS_OUT, ">$project/include/$project/frameworks.h");
391    printf FRAMEWORKS_OUT "%s", "/*
392 * This file is autogenerated by autogen.pl. Do not edit this file by hand.
393 */
394#ifndef $ifdef_string
395#define $ifdef_string
396
397#include <opal/mca/base/mca_base_framework.h>
398
399$framework_decl_output
400static mca_base_framework_t *${project}_frameworks[] = {
401$framework_array_output    NULL
402};
403
404#endif /* $ifdef_string */\n\n";
405    close(FRAMEWORKS_OUT);
406}
407
408##############################################################################
409
410sub mca_process_project {
411    my ($topdir, $project) = @_;
412
413    my $pname = $project->{name};
414    my $pdir = $project->{dir};
415
416    # Does this project have a configure.m4 file?
417    if (-f "$topdir/$pdir/configure.m4") {
418        $mca_found->{$pname}->{"configure.m4"} = 1;
419        verbose "    Found $topdir/$pdir/configure.m4 file\n";
420    }
421
422    # Look for framework directories in this project
423    my $dir = "$topdir/$pdir/mca";
424    if (-d $dir) {
425        opendir(DIR, $dir) ||
426            my_die "Can't open $dir directory";
427        my @my_dirs = readdir(DIR);
428        @my_dirs = sort(@my_dirs);
429
430        foreach my $d (@my_dirs) {
431            # Skip any non-directory, "base", or any dir that begins with "."
432            next
433                if (! -d "$dir/$d" || $d eq "base" || substr($d, 0, 1) eq ".");
434
435            my $framework_header = "$dir/$d/$d.h";
436
437            # If there's a $dir/$d/autogen.options file, read it
438            my $ao_file = "$dir/$d/autogen.options";
439            if (-r $ao_file) {
440                verbose "\n>>> Found $dir/$d/autogen.options file\n";
441                open(IN, $ao_file) ||
442                    die "$ao_file present, but cannot open it";
443                while (<IN>) {
444                    if (m/\s*framework_header\s*=\s*(.+?)\s*$/) {
445                        verbose "    Framework header entry: $1\n";
446                        $framework_header = "$dir/$d/$1";
447                    }
448                }
449                close(IN);
450            }
451
452            # If this directory has a framework header and a base/
453            # subdirectory, or its name is "common", then it's a
454            # framework.
455            if ("common" eq $d || !$project->{need_base} ||
456                (-f $framework_header && -d "$dir/$d/base")) {
457                verbose "\n=== Found $pname / $d framework\n";
458                mca_process_framework($topdir, $project, $d);
459            }
460        }
461        closedir(DIR);
462    }
463}
464
465##############################################################################
466
467sub mca_run_global {
468    my ($projects) = @_;
469
470    # For each project, go find a list of frameworks, and for each of
471    # those, go find a list of components.
472    my $topdir = Cwd::cwd();
473    foreach my $p (@$projects) {
474        if (-d "$topdir/$p->{dir}") {
475            verbose "\n*** Found $p->{name} project\n";
476            mca_process_project($topdir, $p);
477        }
478    }
479
480    # Debugging output
481    debug_dump($mca_found);
482
483    # Save (just) the list of MCA projects in the m4 file
484    my $str;
485    foreach my $p (@$projects) {
486        my $pname = $p->{name};
487        # Check if this project is an MCA project (contains MCA framework)
488        if (exists($mca_found->{$pname})) {
489            $str .= "$p->{name}, ";
490        }
491    }
492    $str =~ s/, $//;
493    $m4 .= "\ndnl List of MCA projects found by autogen.pl
494m4_define([mca_project_list], [$str])\n";
495
496    #-----------------------------------------------------------------------
497
498    $m4 .= "\n$dnl_line
499$dnl_line
500$dnl_line
501
502dnl MCA information\n";
503
504    # Array for all the m4_includes that we'll need to pick up the
505    # configure.m4's.
506    my @includes;
507
508    # Next, for each project, write the list of frameworks
509    foreach my $p (@$projects) {
510
511        my $pname = $p->{name};
512        my $pdir = $p->{dir};
513
514        if (exists($mca_found->{$pname})) {
515            my $frameworks_comma;
516
517            # Does this project have a configure.m4 file?
518            push(@includes, "$pdir/configure.m4")
519                if (exists($mca_found->{$p}->{"configure.m4"}));
520
521            # Print out project-level info
522            my @mykeys = keys(%{$mca_found->{$pname}});
523            @mykeys = sort(@mykeys);
524
525            # Ensure that the "common" framework is listed first
526            # (if it exists)
527            my @tmp;
528            push(@tmp, "common")
529                if (grep(/common/, @mykeys));
530            foreach my $f (@mykeys) {
531                push(@tmp, $f)
532                    if ($f ne "common");
533            }
534            @mykeys = @tmp;
535
536            foreach my $f (@mykeys) {
537                $frameworks_comma .= ", $f";
538
539                # Does this framework have a configure.m4 file?
540                push(@includes, "$pdir/mca/$f/configure.m4")
541                    if (exists($mca_found->{$pname}->{$f}->{"configure.m4"}));
542
543                # This framework does have a Makefile.am (or at least,
544                # it should!)
545                my_die "Missing $pdir/mca/$f/Makefile.am"
546                    if (! -f "$pdir/mca/$f/Makefile.am");
547            }
548            $frameworks_comma =~ s/^, //;
549
550            &mca_generate_framework_header($pname, @mykeys);
551
552            $m4 .= "$dnl_line
553
554dnl Frameworks in the $pname project and their corresponding directories
555m4_define([mca_${pname}_framework_list], [$frameworks_comma])
556
557";
558
559            # Print out framework-level info
560            foreach my $f (@mykeys) {
561                my $components;
562                my $m4_config_component_list;
563                my $no_config_component_list;
564
565                # Troll through each of the found components
566                foreach my $comp (@{$mca_found->{$pname}->{$f}->{components}}) {
567                    my $c = $comp->{name};
568                    $components .= "$c ";
569
570                    # Does this component have a configure.m4 file?
571                    if (exists($comp->{"configure.m4"})) {
572                        push(@includes, "$pdir/mca/$f/$c/configure.m4");
573                        $m4_config_component_list .= ", $c";
574                    } else {
575                        $no_config_component_list .= ", $c";
576                    }
577                }
578                $m4_config_component_list =~ s/^, //;
579                $no_config_component_list =~ s/^, //;
580
581                $m4 .= "dnl Components in the $pname / $f framework
582m4_define([mca_${pname}_${f}_m4_config_component_list], [$m4_config_component_list])
583m4_define([mca_${pname}_${f}_no_config_component_list], [$no_config_component_list])
584
585";
586            }
587        }
588    }
589
590    # List out all the m4_include
591    $m4 .= "$dnl_line
592
593dnl List of configure.m4 files to include\n";
594    foreach my $i (@includes) {
595        $m4 .= "m4_include([$i])\n";
596    }
597}
598
599##############################################################################
600
601sub mpiext_process_extension {
602    my ($topdir, $ext_prefix, $extdir) = @_;
603
604    my $edir = "$topdir/$ext_prefix/$extdir";
605    return
606        if (! -d $edir);
607
608    # Process this directory (pretty much the same treatment as for
609    # MCA components, so it's in a sub).
610    my $found_ext;
611
612    $found_ext->{"name"} = $extdir;
613
614    # Push the results onto the hash array
615    push(@{$mpiext_found}, $found_ext);
616
617    # Is there an autogen.subdirs in here?
618    process_autogen_subdirs($edir);
619}
620
621##############################################################################
622
623sub mpiext_run_global {
624    my ($ext_prefix) = @_;
625
626    my $topdir = Cwd::cwd();
627
628    my $dir = "$topdir/$ext_prefix";
629    opendir(DIR, $dir) ||
630        my_die "Can't open $dir directory";
631    foreach my $d (sort(readdir(DIR))) {
632        # Skip any non-directory, "base", or any dir that begins with "."
633        next
634            if (! -d "$dir/$d" || $d eq "base" || substr($d, 0, 1) eq ".");
635
636        # If this directory has a configure.m4, then it's an
637        # extension.
638        if (-f "$dir/$d/configure.m4") {
639            verbose "=== Found $d MPI extension";
640
641            # Check ignore status
642            if (ignored("$dir/$d")) {
643                verbose " (ignored)\n";
644            } else {
645                verbose "\n";
646                mpiext_process_extension($topdir, $ext_prefix, $d);
647            }
648        }
649    }
650    closedir(DIR);
651    debug_dump($mpiext_found);
652
653    #-----------------------------------------------------------------------
654
655    $m4 .= "\n$dnl_line
656$dnl_line
657$dnl_line
658
659dnl Open MPI extensions information
660$dnl_line\n\n";
661
662    # Array for all the m4_includes that we'll need to pick up the
663    # configure.m4's.
664    my @includes;
665    my $m4_config_ext_list;
666
667    # Troll through each of the found exts
668    foreach my $ext (@{$mpiext_found}) {
669        my $e = $ext->{name};
670        push(@includes, "$ext_prefix/$e/configure.m4");
671        $m4_config_ext_list .= ", $e";
672    }
673
674    $m4_config_ext_list =~ s/^, //;
675
676    # List the M4 and no configure exts
677    $m4 .= "dnl List of all MPI extensions
678m4_define([ompi_mpiext_list], [$m4_config_ext_list])\n";
679    # List out all the m4_include
680    $m4 .= "\ndnl List of configure.m4 files to include\n";
681    foreach my $i (@includes) {
682        $m4 .= "m4_include([$i])\n";
683    }
684}
685
686##############################################################################
687
688sub mpicontrib_process {
689    my ($topdir, $contrib_prefix, $contribdir) = @_;
690
691    my $cdir = "$topdir/$contrib_prefix/$contribdir";
692    return
693        if (! -d $cdir);
694
695    # Process this directory (pretty much the same treatment as for
696    # MCA components, so it's in a sub).
697    my $found_contrib;
698
699    $found_contrib->{"name"} = $contribdir;
700
701    # Push the results onto the hash array
702    push(@{$mpicontrib_found}, $found_contrib);
703
704    # Is there an autogen.subdirs in here?
705    process_autogen_subdirs($cdir);
706}
707
708##############################################################################
709
710sub mpicontrib_run_global {
711    my ($contrib_prefix) = @_;
712
713    my $topdir = Cwd::cwd();
714
715    my $dir = "$topdir/$contrib_prefix";
716    opendir(DIR, $dir) ||
717        my_die "Can't open $dir directory";
718    foreach my $d (sort(readdir(DIR))) {
719        # Skip any non-directory, "base", or any dir that begins with "."
720        next
721            if (! -d "$dir/$d" || $d eq "base" || substr($d, 0, 1) eq ".");
722
723        # If this directory has a configure.m4, then it's an
724        # contrib.
725        if (-f "$dir/$d/configure.m4") {
726            verbose "=== Found $d MPI contrib";
727
728            # Check ignore status
729            if (ignored("$dir/$d")) {
730                verbose " (ignored)\n";
731            } else {
732                verbose "\n";
733                mpicontrib_process($topdir, $contrib_prefix, $d);
734            }
735        }
736    }
737    closedir(DIR);
738    debug_dump($mpicontrib_found);
739
740    #-----------------------------------------------------------------------
741
742    $m4 .= "\n$dnl_line
743$dnl_line
744$dnl_line
745
746dnl Open MPI contrib information
747$dnl_line\n\n";
748
749    # Array for all the m4_includes that we'll need to pick up the
750    # configure.m4's.
751    my @includes;
752    my $m4_config_contrib_list;
753
754    # Troll through each of the found contribs
755    foreach my $contrib (@{$mpicontrib_found}) {
756        my $c = $contrib->{name};
757        push(@includes, "$contrib_prefix/$c/configure.m4");
758        $m4_config_contrib_list .= ", $c";
759    }
760
761    $m4_config_contrib_list =~ s/^, //;
762
763    # List the M4 and no configure contribs
764    $m4 .= "dnl List of all MPI contribs
765m4_define([ompi_mpicontrib_list], [$m4_config_contrib_list])\n";
766    # List out all the m4_include
767    $m4 .= "\ndnl List of configure.m4 files to include\n";
768    foreach my $i (@includes) {
769        $m4 .= "m4_include([$i])\n";
770    }
771}
772
773##############################################################################
774# Find and remove stale files
775
776sub find_and_delete {
777    foreach my $file (@_) {
778        my $removed = 0;
779        if (-f $file) {
780            unlink($file);
781            $removed = 1;
782        }
783        if (-f "config/$file") {
784            unlink("config/$file");
785            $removed = 1;
786        }
787        debug "    Removed stale copy of $file\n"
788            if ($removed);
789    }
790}
791
792##############################################################################
793# Find a specific executable and ensure that it is a recent enough
794# version.
795
796sub find_and_check {
797    my ($app, $app_name, $req_version) = @_;
798
799    my @search_path = split(/;/, $app_name);
800    my @min_version = split(/\./, $req_version);
801    my @versions_found = ();
802
803    foreach (@search_path) {
804        verbose "   Searching for $_\n";
805        my $version = `$_ --version`;
806        if (!defined($version)) {
807            verbose "  $_ not found\n";
808            next;
809        }
810
811	# Matches a version string with 1 or more parts possibly prefixed with a letter (ex:
812	# v2.2) or followed by a letter (ex: 2.2.6b). This regex assumes there is a space
813	# before the version string and that the version is ok if there is no version.
814	if (!($version =~ m/\s[vV]?(\d[\d\.]*\w?)/m)) {
815	    verbose "  WARNING: $_ does not appear to support --version. Assuming it is ok\n";
816
817	    return;
818	}
819
820	$version = $1;
821
822        verbose "     Found $_ version $version; checking version...\n";
823        push(@versions_found, $version);
824
825        my @parts = split(/\./, $version);
826        my $i = 0;
827        # Check every component of the version number
828        while ($i <= $#min_version) {
829            verbose "       Found version component $parts[$i] -- need $min_version[$i]\n";
830
831            # Check to see if there are any characters (!) in the
832            # version number (e.g., Libtool's "2.2.6b" -- #%@#$%!!!).
833            # Do separate comparisons between the number and any
834            # trailing digits.  You can't just "lt" compare the whole
835            # string because "10 lt 2b" will return true.  #@$@#$#@$
836            # Libtool!!
837            $parts[$i] =~ m/(\d+)([a-z]*)/i;
838            my $pn = $1;
839            my $pa = $2;
840            $min_version[$i] =~ m/(\d+)([a-z]*)/i;
841            my $mn = $1;
842            my $ma = $2;
843
844            # If the version is higher, we're done.
845            if ($pn > $mn) {
846                verbose "     ==> ACCEPTED\n";
847                return;
848            }
849            # If the version is lower, we're done.
850            elsif ($pn < $mn ||
851                ($pn == $mn && $pa lt $ma)) {
852                verbose "     ==> Too low!  Skipping this version\n";
853                last;
854            }
855
856            # If the version was equal, keep checking.
857            ++$i;
858        }
859
860        # If we found a good version, return.
861        if ($i > $#min_version) {
862            verbose "     ==> ACCEPTED\n";
863            return;
864        }
865    }
866
867    # if no acceptable version found, reject it
868    print "
869=================================================================
870I could not find a recent enough copy of $app.
871I need at least $req_version, but only found the following versions:\n\n";
872
873    my $i = 0;
874    foreach (@search_path) {
875        print "    $_: $versions_found[$i]\n";
876        $i++;
877    }
878
879    print "\nI am gonna abort.  :-(
880
881Please make sure you are using at least the following versions of the
882tools:
883
884    GNU Autoconf: $ompi_autoconf_version
885    GNU Automake: $ompi_automake_version
886    GNU Libtool: $ompi_libtool_version
887=================================================================\n";
888    my_exit(1);
889}
890
891##############################################################################
892
893sub safe_system {
894    print "Running: " . join(/ /, @_) . "\n";
895    my $ret = system(@_);
896    $ret >>= 8;
897    if (0 != $ret) {
898        print "Command failed: @_\n";
899        my_exit($ret);
900    }
901    $ret;
902}
903
904##############################################################################
905
906sub patch_autotools_output {
907    my ($topdir) = @_;
908
909    # Set indentation string for verbose output depending on current directory.
910    my $indent_str = "    ";
911    if ($topdir eq ".") {
912        $indent_str = "=== ";
913    }
914
915    # Patch ltmain.sh error for PGI version numbers.  Redirect stderr to
916    # /dev/null because this patch is only necessary for some versions of
917    # Libtool (e.g., 2.2.6b); it'll [rightfully] fail if you have a new
918    # enough Libtool that doesn't need this patch.  But don't alarm the
919    # user and make them think that autogen failed if this patch fails --
920    # make the errors be silent.
921    # Also patch ltmain.sh for NAG compiler
922    if (-f "config/ltmain.sh") {
923        verbose "$indent_str"."Patching PGI compiler version numbers in ltmain.sh\n";
924        system("$patch_prog -N -p0 < $topdir/config/ltmain_pgi_tp.diff >/dev/null 2>&1");
925        unlink("config/ltmain.sh.rej");
926
927        verbose "$indent_str"."Patching \"-pthread\" option for NAG compiler in ltmain.sh\n";
928        system("$patch_prog -N -p0 < $topdir/config/ltmain_nag_pthread.diff >/dev/null 2>&1");
929        unlink("config/ltmain.sh.rej");
930    }
931
932    # If there's no configure script, there's nothing else to do.
933    return
934        if (! -f "configure");
935    my @verbose_out;
936
937    # Total ugh.  We have to patch the configure script itself.  See below
938    # for explanations why.
939    open(IN, "configure") || my_die "Can't open configure";
940    my $c;
941    $c .= $_
942        while(<IN>);
943    close(IN);
944    my $c_orig = $c;
945
946    # LT <=2.2.6b need to be patched for the PGI 10.0 fortran compiler
947    # name (pgfortran).  The following comes from the upstream LT patches:
948    # http://lists.gnu.org/archive/html/libtool-patches/2009-11/msg00012.html
949    # http://lists.gnu.org/archive/html/bug-libtool/2009-11/msg00045.html
950    # Note that that patch is part of Libtool (which is not in this OMPI
951    # source tree); we can't fix it.  So all we can do is patch the
952    # resulting configure script.  :-(
953    push(@verbose_out, $indent_str . "Patching configure for Libtool PGI 10 fortran compiler name\n");
954    $c =~ s/gfortran g95 xlf95 f95 fort ifort ifc efc pgf95 lf95 ftn/gfortran g95 xlf95 f95 fort ifort ifc efc pgfortran pgf95 lf95 ftn/g;
955    $c =~ s/pgcc\* \| pgf77\* \| pgf90\* \| pgf95\*\)/pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*)/g;
956    $c =~ s/pgf77\* \| pgf90\* \| pgf95\*\)/pgf77* | pgf90* | pgf95* | pgfortran*)/g;
957
958    # Similar issue as above -- the PGI 10 version number broke <=LT
959    # 2.2.6b's version number checking regexps.  Again, we can't fix the
960    # Libtool install; all we can do is patch the resulting configure
961    # script.  :-( The following comes from the upstream patch:
962    # http://lists.gnu.org/archive/html/libtool-patches/2009-11/msg00016.html
963    push(@verbose_out, $indent_str . "Patching configure for Libtool PGI version number regexps\n");
964    $c =~ s/\*pgCC\\ \[1-5\]\* \| \*pgcpp\\ \[1-5\]\*/*pgCC\\ [1-5]\.* | *pgcpp\\ [1-5]\.*/g;
965
966    # Similar issue as above -- fix the case statements that handle the Sun
967    # Fortran version strings.
968    #
969    # Note: we have to use octal escapes to match '*Sun\ F*) and the
970    # four succeeding lines in the bourne shell switch statement.
971    #   \ = 134
972    #   ) = 051
973    #   * = 052
974    #
975    # Below is essentially an upstream patch for Libtool which we want
976    # made available to Open MPI users running older versions of Libtool
977
978    foreach my $tag (("", "_FC")) {
979
980        # We have to change the search pattern and substitution on each
981        # iteration to take into account the tag changing
982        my $search_string = '# icc used to be incompatible with GCC.\n\s+' .
983                            '# ICC 10 doesn\047t accept -KPIC any more.\n.*\n\s+' .
984	                    "lt_prog_compiler_wl${tag}=";
985        my $replace_string = "# Flang compiler
986      *flang)
987	lt_prog_compiler_wl${tag}='-Wl,'
988	lt_prog_compiler_pic${tag}='-fPIC -DPIC'
989	lt_prog_compiler_static${tag}='-static'
990        ;;
991      # icc used to be incompatible with GCC.
992      # ICC 10 doesn't accept -KPIC any more.
993      icc* | ifort*)
994	lt_prog_compiler_wl${tag}=";
995
996        push(@verbose_out, $indent_str . "Patching configure for flang Fortran ($tag)\n");
997        $c =~ s/$search_string/$replace_string/;
998    }
999
1000    foreach my $tag (("", "_FC")) {
1001
1002        # We have to change the search pattern and substitution on each
1003        # iteration to take into account the tag changing
1004        my $search_string = '\052Sun\134 F\052.*\n.*\n\s+' .
1005            "lt_prog_compiler_pic${tag}" . '.*\n.*\n.*\n.*\n';
1006        my $replace_string = "
1007        *Sun\\ Ceres\\ Fortran* | *Sun*Fortran*\\ [[1-7]].* | *Sun*Fortran*\\ 8.[[0-3]]*)
1008          # Sun Fortran 8.3 passes all unrecognized flags to the linker
1009          lt_prog_compiler_pic${tag}='-KPIC'
1010          lt_prog_compiler_static${tag}='-Bstatic'
1011          lt_prog_compiler_wl${tag}=''
1012          ;;
1013        *Sun\\ F* | *Sun*Fortran*)
1014          lt_prog_compiler_pic${tag}='-KPIC'
1015          lt_prog_compiler_static${tag}='-Bstatic'
1016          lt_prog_compiler_wl${tag}='-Qoption ld '
1017          ;;
1018";
1019
1020        push(@verbose_out, $indent_str . "Patching configure for Sun Studio Fortran version strings ($tag)\n");
1021        $c =~ s/$search_string/$replace_string/;
1022    }
1023
1024    foreach my $tag (("", "_FC")) {
1025
1026        # We have to change the search pattern and substitution on each
1027        # iteration to take into account the tag changing
1028        my $search_string = 'lf95\052.*# Lahey Fortran 8.1\n\s+' .
1029            "whole_archive_flag_spec${tag}=" . '\n\s+' .
1030            "tmp_sharedflag='--shared' ;;" . '\n\s+' .
1031            'xl';
1032        my $replace_string = "lf95*)				# Lahey Fortran 8.1
1033	  whole_archive_flag_spec${tag}=
1034	  tmp_sharedflag='--shared' ;;
1035	nagfor*)			# NAGFOR 5.3
1036	  tmp_sharedflag='-Wl,-shared';;
1037	xl";
1038
1039        push(@verbose_out, $indent_str . "Patching configure for NAG compiler ($tag)\n");
1040        $c =~ s/$search_string/$replace_string/;
1041    }
1042
1043    # Oracle has apparently begun (as of 12.5-beta) removing the "Sun" branding.
1044    # So this patch (cumulative over the previous one) is required.
1045    push(@verbose_out, $indent_str . "Patching configure for Oracle Studio Fortran version strings\n");
1046    $c =~ s/\*Sun\*Fortran\*\)/*Sun*Fortran* | *Studio*Fortran*)/g;
1047    $c =~ s/\*Sun\\ F\*\)(.*\n\s+tmp_sharedflag=)/*Sun\\ F* | *Studio*Fortran*)$1/g;
1048
1049    # See http://git.savannah.gnu.org/cgit/libtool.git/commit/?id=v2.2.6-201-g519bf91 for details
1050    # Note that this issue was fixed in LT 2.2.8, however most distros are still using 2.2.6b
1051
1052    push(@verbose_out, $indent_str . "Patching configure for IBM xlf libtool bug\n");
1053    $c =~ s/(\$LD -shared \$libobjs \$deplibs \$)compiler_flags( -soname \$soname)/$1linker_flags$2/g;
1054
1055    #Check if we are using a recent enough libtool that supports PowerPC little endian
1056    if(index($c, 'powerpc64le-*linux*)') == -1) {
1057        push(@verbose_out, $indent_str . "Patching configure for PowerPC little endian support\n");
1058        my $replace_string = "x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*|";
1059        $c =~ s/x86_64-\*kfreebsd\*-gnu\|x86_64-\*linux\*\|ppc\*-\*linux\*\|powerpc\*-\*linux\*\|/$replace_string/g;
1060        $replace_string =
1061        "powerpc64le-*linux*)\n\t    LD=\"\${LD-ld} -m elf32lppclinux\"\n\t    ;;\n\t  powerpc64-*linux*)";
1062        $c =~ s/ppc64-\*linux\*\|powerpc64-\*linux\*\)/$replace_string/g;
1063        $replace_string =
1064        "powerpcle-*linux*)\n\t    LD=\"\${LD-ld} -m elf64lppc\"\n\t    ;;\n\t  powerpc-*linux*)";
1065        $c =~ s/ppc\*-\*linux\*\|powerpc\*-\*linux\*\)/$replace_string/g;
1066    }
1067
1068    # Fix consequence of broken libtool.m4
1069    # see http://lists.gnu.org/archive/html/bug-libtool/2015-07/msg00002.html and
1070    # https://github.com/open-mpi/ompi/issues/751
1071    push(@verbose_out, $indent_str . "Patching configure for -L/-R libtool.m4 bug\n");
1072    # patch for libtool < 2.4.3
1073    $c =~ s/# Some compilers place space between "-\{L,R\}" and the path.\n       # Remove the space.\n       if test \$p = \"-L\" \|\|/# Some compilers place space between "-\{L,-l,R\}" and the path.\n       # Remove the spaces.\n       if test \$p = \"-L\" \|\|\n          test \$p = \"-l\" \|\|/g;
1074    # patch for libtool >= 2.4.3
1075    $c =~ s/# Some compilers place space between "-\{L,R\}" and the path.\n       # Remove the space.\n       if test x-L = \"\$p\" \|\|\n          test x-R = \"\$p\"\; then/# Some compilers place space between "-\{L,-l,R\}" and the path.\n       # Remove the spaces.\n       if test x-L = \"x\$p\" \|\|\n          test x-l = \"x\$p\" \|\|\n          test x-R = \"x\$p\"\; then/g;
1076
1077    # Fix OS X Big Sur (11.0.x) support
1078    # From https://lists.gnu.org/archive/html/libtool-patches/2020-06/msg00001.html
1079    push(@verbose_out, $indent_str . "Patching configure for MacOS Big Sur libtool.m4 bug\n");
1080    # Some versions of Libtool use ${wl} consistently, but others did
1081    # not (e.g., they used $wl).  Make the regexp be able to handle
1082    # both.  Additionally, the case string searching for 10.[012]*
1083    # changed over time.  So make sure it can handle both of the case
1084    # strings that we're aware of.
1085    my $WL = '(\$\{wl\}|\$wl)';
1086    my $SOMETIMES = '(\[,.\])*';
1087    my $search_string = 'darwin\*\) # darwin 5.x on
1088      # if running on 10.5 or later, the deployment target defaults
1089      # to the OS version, if on x86, and 10.4, the deployment
1090      # target defaults to 10.4. Don\'t you love it\?
1091      case \$\{MACOSX_DEPLOYMENT_TARGET-10.0\},\$host in
1092	10.0,\*86\*-darwin8\*\|10.0,\*-darwin\[91\]\*\)
1093	  _lt_dar_allow_undefined=\'' . $WL . '-undefined ' . $WL . 'dynamic_lookup\' ;;
1094	10.\[012\]' . $SOMETIMES . '\*\)
1095	  _lt_dar_allow_undefined=\'' . $WL . '-flat_namespace ' . $WL . '-undefined ' . $WL . 'suppress\' ;;
1096	10.\*\)';
1097    my $replace_string = 'darwin*)
1098      # Open MPI patched for Darwin / MacOS Big Sur.  See
1099      # http://lists.gnu.org/archive/html/bug-libtool/2015-07/msg00001.html
1100      case ${MACOSX_DEPLOYMENT_TARGET},$host in
1101      10.[012],*|,*powerpc*)
1102	  _lt_dar_allow_undefined=\'${wl}-flat_namespace ${wl}-undefined ${wl}suppress\' ;;
1103      *)';
1104    $c =~ s/$search_string/$replace_string/g;
1105
1106    # Only write out verbose statements and a new configure if the
1107    # configure content actually changed
1108    return
1109        if ($c eq $c_orig);
1110    foreach my $str (@verbose_out) {
1111        verbose($str);
1112    }
1113
1114    open(OUT, ">configure.patched") || my_die "Can't open configure.patched";
1115    print OUT $c;
1116    close(OUT);
1117    # Use cp so that we preserve permissions on configure
1118    safe_system("cp configure.patched configure");
1119    unlink("configure.patched");
1120}
1121
1122sub in_tarball {
1123    my $tarball = 0;
1124    open(IN, "VERSION") || my_die "Can't open VERSION";
1125    # If repo_rev is not an empty string, we are in a tarball
1126    while (<IN>) {
1127          my $line = $_;
1128          my @fields = split(/=/,$line);
1129          if ($fields[0] eq "repo_rev") {
1130              if ($fields[1] ne "\n") {
1131                  $tarball = 1;
1132                  last;
1133              }
1134          }
1135    }
1136    close(IN);
1137    return $tarball;
1138}
1139
1140##############################################################################
1141
1142sub replace_config_sub_guess {
1143    # This could be simpler if we could use some Perl modules for this
1144    # functionality (e.g., DateTime).  But I don't want to introduce
1145    # any CPAN dependencies here, so just do sometime simple, even if
1146    # it's a bit laborious. Use a few private helper functions for
1147    # this kind of functionality.
1148
1149    sub _get_timestamp {
1150        my $filename = shift;
1151
1152        my $ret;
1153        if (-x $filename) {
1154            my $out = `$filename --version`;
1155            $out =~ m/GNU config\.[a-z]+ \((.+)\)/;
1156            $ret = $1;
1157        }
1158
1159        return $ret;
1160    }
1161
1162    sub _split_timestamp {
1163        my $ts = shift;
1164
1165        $ts =~ m/(\d+)-(\d+)-(\d+)/;
1166        return $1, $2, $3;
1167    }
1168
1169    # Returns true if timestamp $a > timestamp $b.
1170    sub _timestamp_gt {
1171        my ($a, $b) = @_;
1172
1173        my ($year_a, $month_a, $day_a) = _split_timestamp($a);
1174        my ($year_b, $month_b, $day_b) = _split_timestamp($b);
1175
1176        # Don't try to be clever -- just do a simple set of explicit
1177        # comparisons.
1178        if ($year_a > $year_b) {
1179            return 1;
1180        } elsif ($year_a < $year_b) {
1181            return 0;
1182        } else {
1183            if ($month_a > $month_b) {
1184                return 1;
1185            } elsif ($month_a < $month_b) {
1186                return 0;
1187            } else {
1188                if ($day_a > $day_b) {
1189                    return 1;
1190                } else {
1191                    return 0;
1192                }
1193            }
1194        }
1195    }
1196
1197    my ($topdir) = @_;
1198
1199    # Find the stashed known-good files, and get their version
1200    # timestamps.
1201    my $cached_dir = "$topdir/config/from-savannah";
1202    my @files = qw/config.guess config.sub/;
1203    my %known_good_timestamps;
1204    foreach my $file (@files) {
1205        my $filename = "$cached_dir/upstream-$file";
1206        my_die("Cannot find $filename")
1207            if (! -f $filename);
1208
1209        my $ts = _get_timestamp($filename);
1210        $known_good_timestamps{$file} = $ts;
1211    }
1212
1213    # Find all config.guess/config.sub files in the tree.  If their
1214    # versions are older than the stashed known-good files, update
1215    # them from the stash.
1216    my @files;
1217    File::Find::find(sub {
1218        push(@files, $File::Find::name)
1219            if ($_ eq "config.guess" ||
1220                $_ eq "config.sub") }, $topdir);
1221
1222    foreach my $file (@files) {
1223        # Skip anything in the 3rd-party tree
1224        next
1225            if ($file =~ /\/3rd-party\//);
1226
1227        my $base = basename($file);
1228        my $ts = _get_timestamp($file);
1229        if (_timestamp_gt($known_good_timestamps{$base}, $ts)) {
1230            print("=== Replacing $file with newer version\n");
1231            safe_system("cp -f $cached_dir/upstream-$base $file");
1232        }
1233    }
1234}
1235
1236##############################################################################
1237##############################################################################
1238## main - do the real work...
1239##############################################################################
1240##############################################################################
1241
1242# Command line parameters
1243
1244my $ok = Getopt::Long::GetOptions("no-ompi" => \$no_ompi_arg,
1245                                  "no-orte" => \$no_orte_arg,
1246                                  "no-oshmem" => \$no_oshmem_arg,
1247                                  "quiet|q" => \$quiet_arg,
1248                                  "debug|d" => \$debug_arg,
1249                                  "help|h" => \$help_arg,
1250                                  "platform=s" => \$platform_arg,
1251                                  "include=s" => \$include_arg,
1252                                  "exclude=s" => \$exclude_arg,
1253                                  "force|f" => \$force_arg,
1254    );
1255
1256if (!$ok || $help_arg) {
1257    print "Invalid command line argument.\n\n"
1258        if (!$ok);
1259    print "Options:
1260  --no-ompi | -no-ompi          Do not build the Open MPI layer
1261  --no-orte | -no-orte          Do not build the ORTE layer
1262  --no-oshmem | -no-oshmem      Do not build the OSHMEM layer
1263  --quiet | -q                  Do not display normal verbose output
1264  --debug | -d                  Output lots of debug information
1265  --help | -h                   This help list
1266  --platform | -p               Specify a platform file to be parsed for no_build
1267                                and only_build directives
1268  --include | -i                Comma-separated list of framework-component pairs
1269                                to be exclusively built - i.e., all other components
1270                                will be ignored and only those specified will be marked
1271                                to build
1272  --exclude | -e                Comma-separated list of framework or framework-component
1273                                to be excluded from the build
1274  --force | -f                  Run even if invoked from the source tree of an expanded
1275                                distribution tarball\n";
1276    my_exit($ok ? 0 : 1);
1277}
1278
1279#---------------------------------------------------------------------------
1280
1281# Check for project existence
1282my $project_name_long = "Open MPI";
1283my $project_name_short = "openmpi";
1284
1285if (! -e "ompi") {
1286    $no_ompi_arg = 1;
1287    debug "No ompi subdirectory found - will not build MPI layer\n";
1288}
1289if (! -e "orte") {
1290    $no_orte_arg = 1;
1291    debug "No orte subdirectory found - will not build ORTE\n";
1292}
1293if (! -e "oshmem") {
1294    $no_oshmem_arg = 1;
1295    debug "No oshmem subdirectory found - will not build OSHMEM\n";
1296}
1297
1298if (-e "orcm") {
1299    # bozo check - ORCM requires ORTE
1300    if ($no_orte_arg == 1) {
1301        print "Cannot build ORCM without ORTE\n";
1302        my_exit(1);
1303    }
1304    $project_name_long = "Open Resilient Cluster Manager";
1305    $project_name_short = "open-rcm";
1306} elsif ($no_ompi_arg == 1) {
1307    if ($no_orte_arg == 0) {
1308        $project_name_long = "Open MPI Run Time Environment";
1309        $project_name_short = "open-rte";
1310    } else {
1311        $project_name_long = "Open Portability Access Layer";
1312        $project_name_short = "open-pal";
1313    }
1314}
1315
1316#---------------------------------------------------------------------------
1317
1318$full_hostname = `hostname`;
1319chomp($full_hostname);
1320
1321$m4 = "dnl
1322dnl \$HEADER\$
1323dnl
1324$dnl_line
1325dnl This file is automatically created by autogen.pl; it should not
1326dnl be edited by hand!!
1327dnl
1328dnl Generated by $username at " . localtime($ENV{SOURCE_DATE_EPOCH} || time) . "
1329dnl on $full_hostname.
1330$dnl_line\n\n";
1331
1332#---------------------------------------------------------------------------
1333
1334# Verify that we're in the OMPI root directorty by checking for a token file.
1335
1336my_die "Not at the root directory of an OMPI source tree"
1337    if (! -f "config/opal_try_assemble.m4");
1338
1339my_die "autogen.pl has been invoked in the source tree of an Open MPI distribution tarball; aborting...
1340You likely do not need to invoke \"autogen.pl\" -- you can probably run \"configure\" directly.
1341If you really know what you are doing, and really need to run autogen.pl, use the \"--force\" flag."
1342    if (!$force_arg && in_tarball());
1343
1344# Now that we've verified that we're in the top-level OMPI directory,
1345# set the sentinel file to remove if we abort.
1346$sentinel = Cwd::cwd() . "/configure";
1347
1348#---------------------------------------------------------------------------
1349
1350my $step = 1;
1351verbose "Open MPI autogen (buckle up!)
1352
1353$step. Checking tool versions\n\n";
1354
1355# Check the autotools revision levels
1356&find_and_check("autoconf", $ompi_autoconf_search, $ompi_autoconf_version);
1357&find_and_check("libtool", $ompi_libtoolize_search, $ompi_libtool_version);
1358&find_and_check("automake", $ompi_automake_search, $ompi_automake_version);
1359
1360#---------------------------------------------------------------------------
1361
1362# Save the platform file in the m4
1363$m4 .= "dnl Platform file\n";
1364
1365# Process platform arg, if provided
1366if ($platform_arg) {
1367    $m4 .= "m4_define([autogen_platform_file], [$platform_arg])\n\n";
1368    open(IN, $platform_arg) || my_die "Can't open $platform_arg";
1369    # Read all lines from the file
1370    while (<IN>) {
1371        my $line = $_;
1372        my @fields = split(/=/,$line);
1373        if ($fields[0] eq "enable_mca_no_build") {
1374            if ($exclude_arg) {
1375                print "The specified platform file includes an
1376enable_mca_no_build line. However, your command line
1377also contains an exclude specification. Only one of
1378these directives can be given.\n";
1379                my_exit(1);
1380            }
1381            $exclude_arg = $fields[1];
1382        } elsif ($fields[0] eq "enable_mca_only_build") {
1383            if ($include_arg) {
1384                print "The specified platform file includes an
1385enable_mca_only_build line. However, your command line
1386also contains an include specification. Only one of
1387these directives can be given.\n";
1388                my_exit(1);
1389            }
1390            $include_arg = $fields[1];
1391        }
1392    }
1393    close(IN);
1394} else {
1395    # No platform file -- write an empty list
1396    $m4 .= "m4_define([autogen_platform_file], [])\n\n";
1397}
1398
1399if ($exclude_arg) {
1400    debug "Using exclude list: $exclude_arg";
1401    my @list = split(/,/, $exclude_arg);
1402    foreach (@list) {
1403        my @pairs = split(/-/, $_);
1404        if (exists($pairs[1])) {
1405        # Remove any trailing newlines
1406            chomp($pairs[1]);
1407            debug "    Adding ".$pairs[0]."->".$pairs[1]." to exclude list\n";
1408            push(@{$exclude_list->{$pairs[0]}}, $pairs[1]);
1409        } else {
1410            debug "    Adding $pairs[0] to exclude list\n";
1411            push(@{$exclude_list->{$pairs[0]}}, "AGEN_EXCLUDE_ALL");
1412        }
1413    }
1414}
1415if ($include_arg) {
1416    debug "Using include list: $include_arg";
1417    my @list = split(/,/, $include_arg);
1418    foreach (@list) {
1419        my @pairs = split(/-/, $_);
1420        if (exists($pairs[1])) {
1421        # Remove any trailing newlines
1422            chomp($pairs[1]);
1423            debug "    Adding ".$pairs[0]."->".$pairs[1]." to include list\n";
1424            push(@{$include_list->{$pairs[0]}}, $pairs[1]);
1425        }
1426        # NOTE: it makes no sense to include all as that is the default
1427        # so ignore that scenario here, if given
1428    }
1429}
1430
1431#---------------------------------------------------------------------------
1432
1433++$step;
1434verbose "\n$step. Running template-generating scripts\n\n";
1435
1436# These scripts generate fortran header files of different types, but
1437# guaranteed to have the same value (i.e., so humans don't have to
1438# maintain two sets of files, and potentially have values get out of
1439# sync).
1440
1441my @scripts;
1442push(@scripts, "ompi/include/mpif-values.pl");
1443
1444foreach my $s (@scripts) {
1445    verbose "=== $s\n";
1446    if (! -x $s) {
1447        print "Cannot find executable $s!\nAborting.\n";
1448        my_exit(1);
1449    }
1450    if (system($s) != 0) {
1451        print "Script failed: $s\n";
1452        my_exit(1);
1453    }
1454}
1455
1456#---------------------------------------------------------------------------
1457
1458# Find projects, frameworks, components
1459++$step;
1460verbose "\n$step. Searching for projects, MCA frameworks, and MCA components\n";
1461
1462my $ret;
1463
1464# Figure out if we're at the top level of the OMPI tree or not.
1465if (! (-f "VERSION" && -f "configure.ac" && -f $topdir_file)) {
1466    print("\n\nYou must run this script from the top-level directory of the Open MPI tree.\n\n");
1467    my_exit(1);
1468}
1469
1470# Top-level projects to examine
1471my $projects;
1472push(@{$projects}, { name => "opal", dir => "opal", need_base => 1 });
1473push(@{$projects}, { name => "orte", dir => "orte", need_base => 1 })
1474    if (!$no_orte_arg);
1475push(@{$projects}, { name => "ompi", dir => "ompi", need_base => 1 })
1476    if (!$no_ompi_arg);
1477push(@{$projects}, { name => "oshmem", dir => "oshmem", need_base => 1 })
1478    if (!$no_ompi_arg && !$no_orte_arg && !$no_oshmem_arg);
1479push(@{$projects}, { name => "orcm", dir => "orcm", need_base => 1 })
1480    if (-e "orcm");
1481
1482$m4 .= "dnl Separate m4 define for each project\n";
1483foreach my $p (@$projects) {
1484    $m4 .= "m4_define([project_$p->{name}], [1])\n";
1485}
1486
1487$m4 .= "\ndnl Project names
1488m4_define([project_name_long], [$project_name_long])
1489m4_define([project_name_short], [$project_name_short])\n";
1490
1491# Setup MCA
1492mca_run_global($projects);
1493
1494#---------------------------------------------------------------------------
1495
1496# Find MPI extensions and contribs
1497if (!$no_ompi_arg) {
1498    ++$step;
1499    verbose "\n$step. Searching for Open MPI extensions\n\n";
1500    mpiext_run_global("ompi/mpiext");
1501
1502    ++$step;
1503    verbose "\n$step. Searching for Open MPI contribs\n\n";
1504    mpicontrib_run_global("ompi/contrib");
1505}
1506
1507#---------------------------------------------------------------------------
1508
1509# Process all subdirs that we found in previous steps
1510++$step;
1511verbose "\n$step. Processing autogen.subdirs directories\n";
1512
1513if ($#subdirs >= 0) {
1514    foreach my $d (@subdirs) {
1515        process_subdir($d);
1516    }
1517} else {
1518    print "<none found>\n";
1519}
1520
1521#---------------------------------------------------------------------------
1522
1523# If we got here, all was good.  Run the auto tools.
1524++$step;
1525verbose "\n$step. Running autotools on top-level tree\n\n";
1526
1527# Remove old versions of the files (this is probably overkill, but...)
1528verbose "==> Remove stale files\n";
1529find_and_delete(qw/config.guess config.sub depcomp compile install-sh ltconfig
1530    ltmain.sh missing mkinstalldirs libtool/);
1531
1532# Remove the old m4 file and write the new one
1533verbose "==> Writing m4 file with autogen.pl results\n";
1534unlink($m4_output_file);
1535open(M4, ">$m4_output_file") ||
1536    my_die "Can't open $m4_output_file";
1537print M4 $m4;
1538close(M4);
1539
1540# Generate the version checking script with autom4te
1541verbose "==> Generating opal_get_version.sh\n";
1542chdir("config");
1543safe_system("autom4te --language=m4sh opal_get_version.m4sh -o opal_get_version.sh");
1544
1545# Run autoreconf
1546verbose "==> Running autoreconf\n";
1547chdir("..");
1548my $cmd = "autoreconf -ivf --warnings=all,no-obsolete,no-override -I config";
1549foreach my $project (@{$projects}) {
1550    $cmd .= " -I $project->{dir}/config"
1551        if (-d "$project->{dir}/config");
1552}
1553safe_system($cmd);
1554
1555patch_autotools_output(".");
1556
1557# Per https://github.com/open-mpi/ompi/issues/8410, replace config.sub
1558# and config.guess with known-good versions if the Autoconf-installed
1559# versions are older.
1560replace_config_sub_guess(".");
1561
1562#---------------------------------------------------------------------------
1563
1564verbose "
1565================================================
1566Open MPI autogen: completed successfully.  w00t!
1567================================================\n\n";
1568
1569# Done!
1570exit(0);
1571