Home | History | Annotate | Line # | Download | only in Configurations
      1 package gentemplate;
      2 
      3 use strict;
      4 use warnings;
      5 use Carp;
      6 
      7 use Exporter;
      8 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
      9 @ISA = qw(Exporter);
     10 @EXPORT = qw(gentemplate);
     11 
     12 use File::Basename;
     13 
     14 sub gentemplate {
     15     my %opts = @_;
     16 
     17     my $generator = OpenSSL::GenTemplate->new(%opts);
     18 
     19     # Build mandatory header file generators
     20     foreach (@{$generator->{info}->{depends}->{""}}) { $generator->dogenerate($_); }
     21 
     22     # Build all known targets, libraries, modules, programs and scripts.
     23     # Everything else will be handled as a consequence.
     24     foreach (@{$generator->{info}->{targets}})   { $generator->dotarget($_); }
     25     foreach (@{$generator->{info}->{libraries}}) { $generator->dolib($_);    }
     26     foreach (@{$generator->{info}->{modules}})   { $generator->domodule($_); }
     27     foreach (@{$generator->{info}->{programs}})  { $generator->dobin($_);    }
     28     foreach (@{$generator->{info}->{scripts}})   { $generator->doscript($_); }
     29     foreach (sort keys %{$generator->{info}->{htmldocs}}) { $generator->dodocs('html', $_); }
     30     foreach (sort keys %{$generator->{info}->{mandocs}})  { $generator->dodocs('man', $_); }
     31     foreach (sort keys %{$generator->{info}->{dirinfo}})  { $generator->dodir($_); }
     32 }
     33 
     34 package OpenSSL::GenTemplate;
     35 
     36 use OpenSSL::Util;
     37 
     38 sub new {
     39     my $class = shift;
     40     my %opts = @_;
     41 
     42     my $data = {
     43         output   => $opts{output},
     44         config   => $opts{config} // {},
     45         disabled => $opts{disabled} // {},
     46         info     => $opts{unified_info} // {},
     47     };
     48 
     49     return bless $data, $class;
     50 };
     51 
     52 sub emit {
     53     my $self = shift;
     54     my $name = shift;
     55     my %opts = @_;
     56     my $fh = $self->{output};
     57 
     58     die "No name?" unless $name;
     59     print $fh "{-\n ", $name, '(', dump_data(\%opts), ');', " \n-}"
     60         unless defined $opts{attrs}->{skip};
     61 }
     62 
     63 my $debug_resolvedepends = $ENV{BUILDFILE_DEBUG_DEPENDS};
     64 my $debug_rules = $ENV{BUILDFILE_DEBUG_RULES};
     65 
     66 # A cache of objects for which a recipe has already been generated
     67 our %cache;
     68 
     69 # collectdepends, expanddepends and reducedepends work together to make
     70 # sure there are no duplicate or weak dependencies and that they are in
     71 # the right order.  This is used to sort the list of libraries  that a
     72 # build depends on.
     73 sub extensionlesslib {
     74     my @result = map { $_ =~ /(\.a)?$/; $` } @_;
     75     return @result if wantarray;
     76     return $result[0];
     77 }
     78 
     79 # collectdepends dives into the tree of dependencies and returns
     80 # a list of all the non-weak ones.
     81 sub collectdepends {
     82     my $self = shift;
     83     return () unless @_;
     84 
     85     my $thing = shift;
     86     my $extensionlessthing = extensionlesslib($thing);
     87     my @listsofar = @_;    # to check if we're looping
     88     my @list = @{ $self->{info}->{depends}->{$thing} //
     89                   $self->{info}->{depends}->{$extensionlessthing}
     90                   // [] };
     91     my @newlist = ();
     92 
     93     print STDERR "DEBUG[collectdepends] $thing > ", join(' ', @listsofar), "\n"
     94         if $debug_resolvedepends;
     95     foreach my $item (@list) {
     96         my $extensionlessitem = extensionlesslib($item);
     97         # It's time to break off when the dependency list starts looping
     98         next if grep { extensionlesslib($_) eq $extensionlessitem } @listsofar;
     99         # Don't add anything here if the dependency is weak
    100         next if defined $self->{info}->{attributes}->{depends}->{$thing}->{$item}->{'weak'};
    101         my @resolved = $self->collectdepends($item, @listsofar, $item);
    102         push @newlist, $item, @resolved;
    103     }
    104     print STDERR "DEBUG[collectdepends] $thing < ", join(' ', @newlist), "\n"
    105         if $debug_resolvedepends;
    106     @newlist;
    107 }
    108 
    109 # expanddepends goes through a list of stuff, checks if they have any
    110 # dependencies, and adds them at the end of the current position if
    111 # they aren't already present later on.
    112 sub expanddepends {
    113     my $self = shift;
    114     my @after = ( @_ );
    115     print STDERR "DEBUG[expanddepends]> ", join(' ', @after), "\n"
    116         if $debug_resolvedepends;
    117     my @before = ();
    118     while (@after) {
    119         my $item = shift @after;
    120         print STDERR "DEBUG[expanddepends]\\  ", join(' ', @before), "\n"
    121             if $debug_resolvedepends;
    122         print STDERR "DEBUG[expanddepends] - ", $item, "\n"
    123             if $debug_resolvedepends;
    124         my @middle = (
    125             $item,
    126             map {
    127                 my $x = $_;
    128                 my $extlessx = extensionlesslib($x);
    129                 if (grep { $extlessx eq extensionlesslib($_) } @before
    130                     and
    131                     !grep { $extlessx eq extensionlesslib($_) } @after) {
    132                     print STDERR "DEBUG[expanddepends] + ", $x, "\n"
    133                         if $debug_resolvedepends;
    134                     ( $x )
    135                 } else {
    136                     print STDERR "DEBUG[expanddepends] ! ", $x, "\n"
    137                         if $debug_resolvedepends;
    138                     ()
    139                 }
    140             } @{$self->{info}->{depends}->{$item} // []}
    141             );
    142         print STDERR "DEBUG[expanddepends] = ", join(' ', @middle), "\n"
    143             if $debug_resolvedepends;
    144         print STDERR "DEBUG[expanddepends]/  ", join(' ', @after), "\n"
    145             if $debug_resolvedepends;
    146         push @before, @middle;
    147     }
    148     print STDERR "DEBUG[expanddepends]< ", join(' ', @before), "\n"
    149         if $debug_resolvedepends;
    150     @before;
    151 }
    152 
    153 # reducedepends looks through a list, and checks if each item is
    154 # repeated later on.  If it is, the earlier copy is dropped.
    155 sub reducedepends {
    156     my @list = @_;
    157     print STDERR "DEBUG[reducedepends]> ", join(' ', @list), "\n"
    158         if $debug_resolvedepends;
    159     my @newlist = ();
    160     my %replace = ();
    161     while (@list) {
    162         my $item = shift @list;
    163         my $extensionlessitem = extensionlesslib($item);
    164         if (grep { $extensionlessitem eq extensionlesslib($_) } @list) {
    165             if ($item ne $extensionlessitem) {
    166                 # If this instance of the library is explicitly static, we
    167                 # prefer that to any shared library name, since it must have
    168                 # been done on purpose.
    169                 $replace{$extensionlessitem} = $item;
    170             }
    171         } else {
    172             push @newlist, $item;
    173         }
    174     }
    175     @newlist = map { $replace{$_} // $_; } @newlist;
    176     print STDERR "DEBUG[reducedepends]< ", join(' ', @newlist), "\n"
    177         if $debug_resolvedepends;
    178     @newlist;
    179 }
    180 
    181 # Do it all
    182 # This takes multiple inputs and combine them into a single list of
    183 # interdependent things.  The returned value will include all the input.
    184 # Callers are responsible for taking away the things they are building.
    185 sub resolvedepends {
    186     my $self = shift;
    187     print STDERR "DEBUG[resolvedepends] START (", join(', ', @_), ")\n"
    188         if $debug_resolvedepends;
    189     my @all =
    190         reducedepends($self->expanddepends(map { ( $_, $self->collectdepends($_) ) } @_));
    191     print STDERR "DEBUG[resolvedepends] END (", join(', ', @_), ") : ",
    192         join(',', map { "\n    $_" } @all), "\n"
    193         if $debug_resolvedepends;
    194     @all;
    195 }
    196 
    197 # dogenerate is responsible for producing all the recipes that build
    198 # generated source files.  It recurses in case a dependency is also a
    199 # generated source file.
    200 sub dogenerate {
    201     my $self = shift;
    202     my $src = shift;
    203     # Safety measure
    204     return "" unless defined $self->{info}->{generate}->{$src};
    205     return "" if $cache{$src};
    206     my $obj = shift;
    207     my $bin = shift;
    208     my %opts = @_;
    209     if ($self->{info}->{generate}->{$src}) {
    210         die "$src is generated by Configure, should not appear in build file\n"
    211             if ref $self->{info}->{generate}->{$src} eq "";
    212         my $script = $self->{info}->{generate}->{$src}->[0];
    213         my %attrs = %{$self->{info}->{attributes}->{generate}->{$src} // {}};
    214         $self->emit('generatesrc',
    215              src => $src,
    216              product => $bin,
    217              generator => $self->{info}->{generate}->{$src},
    218              generator_incs => $self->{info}->{includes}->{$script} // [],
    219              generator_deps => $self->{info}->{depends}->{$script} // [],
    220              deps => $self->{info}->{depends}->{$src} // [],
    221              incs => [ defined $obj ? @{$self->{info}->{includes}->{$obj} // []} : (),
    222                        defined $bin ? @{$self->{info}->{includes}->{$bin} // []} : () ],
    223              defs => [ defined $obj ? @{$self->{info}->{defines}->{$obj} // []} : (),
    224                        defined $bin ? @{$self->{info}->{defines}->{$bin} // []} : () ],
    225              attrs => { %attrs },
    226              %opts);
    227         foreach (@{$self->{info}->{depends}->{$src} // []}) {
    228             $self->dogenerate($_, $obj, $bin, %opts);
    229         }
    230         # The generator itself may be is generated
    231         if ($self->{info}->{generate}->{$script}) {
    232             $self->dogenerate($script, $obj, $bin, %opts);
    233         }
    234     }
    235     $cache{$src} = 1;
    236 }
    237 
    238 sub dotarget {
    239     my $self = shift;
    240     my $target = shift;
    241     return "" if $cache{$target};
    242     $self->emit('generatetarget',
    243          target => $target,
    244          deps => $self->{info}->{depends}->{$target} // []);
    245     foreach (@{$self->{info}->{depends}->{$target} // []}) {
    246         $self->dogenerate($_);
    247     }
    248     $cache{$target} = 1;
    249 }
    250 
    251 # doobj is responsible for producing all the recipes that build
    252 # object files as well as dependency files.
    253 sub doobj {
    254     my $self = shift;
    255     my $obj = shift;
    256     return "" if $cache{$obj};
    257     my $bin = shift;
    258     my %opts = @_;
    259     if (@{$self->{info}->{sources}->{$obj} // []}) {
    260         my @srcs = @{$self->{info}->{sources}->{$obj}};
    261         my @deps = @{$self->{info}->{depends}->{$obj} // []};
    262         my @incs = ( @{$self->{info}->{includes}->{$obj} // []},
    263                      @{$self->{info}->{includes}->{$bin} // []} );
    264         my @defs = ( @{$self->{info}->{defines}->{$obj} // []},
    265                      @{$self->{info}->{defines}->{$bin} // []} );
    266         print STDERR "DEBUG[doobj] \@srcs for $obj ($bin) : ",
    267             join(",", map { "\n    $_" } @srcs), "\n"
    268             if $debug_rules;
    269         print STDERR "DEBUG[doobj] \@deps for $obj ($bin) : ",
    270             join(",", map { "\n    $_" } @deps), "\n"
    271             if $debug_rules;
    272         print STDERR "DEBUG[doobj] \@incs for $obj ($bin) : ",
    273             join(",", map { "\n    $_" } @incs), "\n"
    274             if $debug_rules;
    275         print STDERR "DEBUG[doobj] \@defs for $obj ($bin) : ",
    276             join(",", map { "\n    $_" } @defs), "\n"
    277             if $debug_rules;
    278         print STDERR "DEBUG[doobj] \%opts for $obj ($bin) : ", ,
    279             join(",", map { "\n    $_ = $opts{$_}" } sort keys %opts), "\n"
    280             if $debug_rules;
    281         $self->emit('src2obj',
    282              obj => $obj, product => $bin,
    283              srcs => [ @srcs ], deps => [ @deps ],
    284              incs => [ @incs ], defs => [ @defs ],
    285              %opts);
    286         foreach ((@{$self->{info}->{sources}->{$obj}},
    287                   @{$self->{info}->{depends}->{$obj} // []})) {
    288             $self->dogenerate($_, $obj, $bin, %opts);
    289         }
    290     }
    291     $cache{$obj} = 1;
    292 }
    293 
    294 # Helper functions to grab all applicable intermediary files.
    295 # This is particularly useful when a library is given as source
    296 # rather than a dependency.  In that case, we consider it to be a
    297 # container with object file references, or possibly references
    298 # to further libraries to pilfer in the same way.
    299 sub getsrclibs {
    300     my $self = shift;
    301     my $section = shift;
    302 
    303     # For all input, see if it sources static libraries.  If it does,
    304     # return them together with the result of a recursive call.
    305     map { ( $_, getsrclibs($section, $_) ) }
    306     grep { $_ =~ m|\.a$| }
    307     map { @{$self->{info}->{$section}->{$_} // []} }
    308     @_;
    309 }
    310 
    311 sub getlibobjs {
    312     my $self = shift;
    313     my $section = shift;
    314 
    315     # For all input, see if it's an intermediary file (library or object).
    316     # If it is, collect the result of a recursive call, or if that returns
    317     # an empty list, the element itself.  Return the result.
    318     map {
    319         my @x = $self->getlibobjs($section, @{$self->{info}->{$section}->{$_}});
    320         @x ? @x : ( $_ );
    321     }
    322     grep { defined $self->{info}->{$section}->{$_} }
    323     @_;
    324 }
    325 
    326 # dolib is responsible for building libraries.  It will call
    327 # obj2shlib if shared libraries are produced, and obj2lib in all
    328 # cases.  It also makes sure all object files for the library are
    329 # built.
    330 sub dolib {
    331     my $self = shift;
    332     my $lib = shift;
    333     return "" if $cache{$lib};
    334 
    335     my %attrs = %{$self->{info}->{attributes}->{libraries}->{$lib} // {}};
    336 
    337     my @deps = ( $self->resolvedepends(getsrclibs('sources', $lib)) );
    338 
    339     # We support two types of objs, those who are specific to this library
    340     # (they end up in @objs) and those that we get indirectly, via other
    341     # libraries (they end up in @foreign_objs).  We get the latter any time
    342     # someone has done something like this in build.info:
    343     #     SOURCE[libfoo.a]=libbar.a
    344     # The indirect object files must be kept in a separate array so they
    345     # don't get rebuilt unnecessarily (and with incorrect auxiliary
    346     # information).
    347     #
    348     # Object files can't be collected commonly for shared and static
    349     # libraries, because we contain their respective object files in
    350     # {shared_sources} and {sources}, and because the implications are
    351     # slightly different for each library form.
    352     #
    353     # We grab all these "foreign" object files recursively with getlibobjs().
    354 
    355     unless ($self->{disabled}->{shared} || $lib =~ /\.a$/) {
    356         # If this library sources other static libraries and those
    357         # libraries are marked {noinst}, there's no need to include
    358         # all of their object files.  Instead, we treat those static
    359         # libraries as dependents alongside any other library this
    360         # one depends on, and let symbol resolution do its job.
    361         my @sourced_libs = ();
    362         my @objs = ();
    363         my @foreign_objs = ();
    364         my @deps = ();
    365         foreach (@{$self->{info}->{shared_sources}->{$lib} // []}) {
    366             if ($_ !~ m|\.a$|) {
    367                 push @objs, $_;
    368             } elsif ($self->{info}->{attributes}->{libraries}->{$_}->{noinst}) {
    369                 push @deps, $_;
    370             } else {
    371                 push @deps, $self->getsrclibs('sources', $_);
    372                 push @foreign_objs, $self->getlibobjs('sources', $_);
    373             }
    374         }
    375         @deps = ( grep { $_ ne $lib } $self->resolvedepends($lib, @deps) );
    376         print STDERR "DEBUG[dolib:shlib] \%attrs for $lib : ", ,
    377             join(",", map { "\n    $_ = $attrs{$_}" } sort keys %attrs), "\n"
    378             if %attrs && $debug_rules;
    379         print STDERR "DEBUG[dolib:shlib] \@deps for $lib : ",
    380             join(",", map { "\n    $_" } @deps), "\n"
    381             if @deps && $debug_rules;
    382         print STDERR "DEBUG[dolib:shlib] \@objs for $lib : ",
    383             join(",", map { "\n    $_" } @objs), "\n"
    384             if @objs && $debug_rules;
    385         print STDERR "DEBUG[dolib:shlib] \@foreign_objs for $lib : ",
    386             join(",", map { "\n    $_" } @foreign_objs), "\n"
    387             if @foreign_objs && $debug_rules;
    388         $self->emit('obj2shlib',
    389              lib => $lib,
    390              attrs => { %attrs },
    391              objs => [ @objs, @foreign_objs ],
    392              deps => [ @deps ]);
    393         foreach (@objs) {
    394             # If this is somehow a compiled object, take care of it that way
    395             # Otherwise, it might simply be generated
    396             if (defined $self->{info}->{sources}->{$_}) {
    397                 if($_ =~ /\.a$/) {
    398                     $self->dolib($_);
    399                 } else {
    400                     $self->doobj($_, $lib, intent => "shlib", attrs => { %attrs });
    401                 }
    402             } else {
    403                 $self->dogenerate($_, undef, undef, intent => "lib");
    404             }
    405         }
    406     }
    407     {
    408         # When putting static libraries together, we cannot rely on any
    409         # symbol resolution, so for all static libraries used as source for
    410         # this one, as well as other libraries they depend on, we simply
    411         # grab all their object files unconditionally,
    412         # Symbol resolution will happen when any program, module or shared
    413         # library is linked with this one.
    414         my @objs = ();
    415         my @sourcedeps = ();
    416         my @foreign_objs = ();
    417         foreach (@{$self->{info}->{sources}->{$lib}}) {
    418             if ($_ !~ m|\.a$|) {
    419                 push @objs, $_;
    420             } else {
    421                 push @sourcedeps, $_;
    422             }
    423         }
    424         @sourcedeps = ( grep { $_ ne $lib } $self->resolvedepends(@sourcedeps) );
    425         print STDERR "DEBUG[dolib:lib] : \@sourcedeps for $_ : ",
    426             join(",", map { "\n    $_" } @sourcedeps), "\n"
    427             if @sourcedeps && $debug_rules;
    428         @foreign_objs = $self->getlibobjs('sources', @sourcedeps);
    429         print STDERR "DEBUG[dolib:lib] \%attrs for $lib : ", ,
    430             join(",", map { "\n    $_ = $attrs{$_}" } sort keys %attrs), "\n"
    431             if %attrs && $debug_rules;
    432         print STDERR "DEBUG[dolib:lib] \@objs for $lib : ",
    433             join(",", map { "\n    $_" } @objs), "\n"
    434             if @objs && $debug_rules;
    435         print STDERR "DEBUG[dolib:lib] \@foreign_objs for $lib : ",
    436             join(",", map { "\n    $_" } @foreign_objs), "\n"
    437             if @foreign_objs && $debug_rules;
    438         $self->emit('obj2lib',
    439              lib => $lib, attrs => { %attrs },
    440              objs => [ @objs, @foreign_objs ]);
    441         foreach (@objs) {
    442             $self->doobj($_, $lib, intent => "lib", attrs => { %attrs });
    443         }
    444     }
    445     $cache{$lib} = 1;
    446 }
    447 
    448 # domodule is responsible for building modules.  It will call
    449 # obj2dso, and also makes sure all object files for the library
    450 # are built.
    451 sub domodule {
    452     my $self = shift;
    453     my $module = shift;
    454     return "" if $cache{$module};
    455     my %attrs = %{$self->{info}->{attributes}->{modules}->{$module} // {}};
    456     my @objs = @{$self->{info}->{sources}->{$module}};
    457     my @deps = ( grep { $_ ne $module }
    458                  $self->resolvedepends($module) );
    459     print STDERR "DEBUG[domodule] \%attrs for $module :",
    460         join(",", map { "\n    $_ = $attrs{$_}" } sort keys %attrs), "\n"
    461         if $debug_rules;
    462     print STDERR "DEBUG[domodule] \@objs for $module : ",
    463         join(",", map { "\n    $_" } @objs), "\n"
    464         if $debug_rules;
    465     print STDERR "DEBUG[domodule] \@deps for $module : ",
    466         join(",", map { "\n    $_" } @deps), "\n"
    467         if $debug_rules;
    468     $self->emit('obj2dso',
    469          module => $module,
    470          attrs => { %attrs },
    471          objs => [ @objs ],
    472          deps => [ @deps ]);
    473     foreach (@{$self->{info}->{sources}->{$module}}) {
    474         # If this is somehow a compiled object, take care of it that way
    475         # Otherwise, it might simply be generated
    476         if (defined $self->{info}->{sources}->{$_}) {
    477             $self->doobj($_, $module, intent => "dso", attrs => { %attrs });
    478         } else {
    479             $self->dogenerate($_, undef, $module, intent => "dso");
    480         }
    481     }
    482     $cache{$module} = 1;
    483 }
    484 
    485 # dobin is responsible for building programs.  It will call obj2bin,
    486 # and also makes sure all object files for the library are built.
    487 sub dobin {
    488     my $self = shift;
    489     my $bin = shift;
    490     return "" if $cache{$bin};
    491     my %attrs = %{$self->{info}->{attributes}->{programs}->{$bin} // {}};
    492     my @objs = @{$self->{info}->{sources}->{$bin}};
    493     my @deps = ( grep { $_ ne $bin } $self->resolvedepends($bin) );
    494     print STDERR "DEBUG[dobin] \%attrs for $bin : ",
    495         join(",", map { "\n    $_ = $attrs{$_}" } sort keys %attrs), "\n"
    496         if %attrs && $debug_rules;
    497     print STDERR "DEBUG[dobin] \@objs for $bin : ",
    498         join(",", map { "\n    $_" } @objs), "\n"
    499         if @objs && $debug_rules;
    500     print STDERR "DEBUG[dobin] \@deps for $bin : ",
    501         join(",", map { "\n    $_" } @deps), "\n"
    502         if @deps && $debug_rules;
    503     $self->emit('obj2bin',
    504          bin => $bin,
    505          attrs => { %attrs },
    506          objs => [ @objs ],
    507          deps => [ @deps ]);
    508     foreach (@objs) {
    509         $self->doobj($_, $bin, intent => "bin", attrs => { %attrs });
    510     }
    511     $cache{$bin} = 1;
    512 }
    513 
    514 # doscript is responsible for building scripts from templates.  It will
    515 # call in2script.
    516 sub doscript {
    517     my $self = shift;
    518     my $script = shift;
    519     return "" if $cache{$script};
    520     $self->emit('in2script',
    521          script => $script,
    522          attrs => $self->{info}->{attributes}->{scripts}->{$script} // {},
    523          sources => $self->{info}->{sources}->{$script});
    524     $cache{$script} = 1;
    525 }
    526 
    527 sub dodir {
    528     my $self = shift;
    529     my $dir = shift;
    530     return "" if !exists(&generatedir) or $cache{$dir};
    531     $self->emit('generatedir',
    532          dir => $dir,
    533          deps => $self->{info}->{dirinfo}->{$dir}->{deps} // [],
    534          %{$self->{info}->{dirinfo}->{$_}->{products}});
    535     $cache{$dir} = 1;
    536 }
    537 
    538 # dodocs is responsible for building documentation from .pods.
    539 # It will call generatesrc.
    540 sub dodocs {
    541     my $self = shift;
    542     my $type = shift;
    543     my $section = shift;
    544     foreach my $doc (@{$self->{info}->{"${type}docs"}->{$section}}) {
    545         next if $cache{$doc};
    546         $self->emit('generatesrc',
    547              src => $doc,
    548              generator => $self->{info}->{generate}->{$doc});
    549         foreach ((@{$self->{info}->{depends}->{$doc} // []})) {
    550             $self->dogenerate($_, undef, undef);
    551         }
    552         $cache{$doc} = 1;
    553     }
    554 }
    555 
    556 1;
    557