| {- # -*- Mode: perl -*- |
| |
| use File::Basename; |
| |
| # A cache of objects for which a recipe has already been generated |
| my %cache; |
| |
| # resolvedepends and reducedepends work in tandem to make sure |
| # there are no duplicate dependencies and that they are in the |
| # right order. This is especially used to sort the list of |
| # libraries that a build depends on. |
| sub extensionlesslib { |
| my @result = map { $_ =~ /(\.a)?$/; $` } @_; |
| return @result if wantarray; |
| return $result[0]; |
| } |
| sub resolvedepends { |
| my $thing = shift; |
| my $extensionlessthing = extensionlesslib($thing); |
| my @listsofar = @_; # to check if we're looping |
| my @list = @{$unified_info{depends}->{$extensionlessthing}}; |
| my @newlist = (); |
| if (scalar @list) { |
| foreach my $item (@list) { |
| my $extensionlessitem = extensionlesslib($item); |
| # It's time to break off when the dependency list starts looping |
| next if grep { extensionlesslib($_) eq $extensionlessitem } @listsofar; |
| push @newlist, $item, resolvedepends($item, @listsofar, $item); |
| } |
| } |
| @newlist; |
| } |
| sub reducedepends { |
| my @list = @_; |
| my @newlist = (); |
| while (@list) { |
| my $item = shift @list; |
| my $extensionlessitem = extensionlesslib($item); |
| push @newlist, $item |
| unless grep { $extensionlessitem eq extensionlesslib($_) } @list; |
| } |
| @newlist; |
| } |
| |
| # is_installed checks if a given file will be installed (i.e. they are |
| # not defined _NO_INST in build.info) |
| sub is_installed { |
| my $product = shift; |
| if (grep { $product eq $_ } |
| map { (@{$unified_info{install}->{$_}}) } |
| keys %{$unified_info{install}}) { |
| return 1; |
| } |
| return 0; |
| } |
| |
| # dogenerate is responsible for producing all the recipes that build |
| # generated source files. It recurses in case a dependency is also a |
| # generated source file. |
| sub dogenerate { |
| my $src = shift; |
| return "" if $cache{$src}; |
| my $obj = shift; |
| my $bin = shift; |
| my %opts = @_; |
| if ($unified_info{generate}->{$src}) { |
| die "$src is generated by Configure, should not appear in build file\n" |
| if ref $unified_info{generate}->{$src} eq ""; |
| my $script = $unified_info{generate}->{$src}->[0]; |
| $OUT .= generatesrc(src => $src, |
| generator => $unified_info{generate}->{$src}, |
| generator_incs => $unified_info{includes}->{$script}, |
| generator_deps => $unified_info{depends}->{$script}, |
| deps => $unified_info{depends}->{$src}, |
| incs => [ @{$unified_info{includes}->{$bin}}, |
| @{$unified_info{includes}->{$obj}} ], |
| %opts); |
| foreach (@{$unified_info{depends}->{$src}}) { |
| dogenerate($_, $obj, $bin, %opts); |
| } |
| } |
| $cache{$src} = 1; |
| } |
| |
| # doobj is responsible for producing all the recipes that build |
| # object files as well as dependency files. |
| sub doobj { |
| my $obj = shift; |
| return "" if $cache{$obj}; |
| (my $obj_no_o = $obj) =~ s|\.o$||; |
| my $bin = shift; |
| my %opts = @_; |
| if (@{$unified_info{sources}->{$obj}}) { |
| $OUT .= src2obj(obj => $obj_no_o, |
| product => $bin, |
| srcs => $unified_info{sources}->{$obj}, |
| deps => $unified_info{depends}->{$obj}, |
| incs => [ @{$unified_info{includes}->{$bin}}, |
| @{$unified_info{includes}->{$obj}} ], |
| %opts); |
| foreach ((@{$unified_info{sources}->{$obj}}, |
| @{$unified_info{depends}->{$obj}})) { |
| dogenerate($_, $obj, $bin, %opts); |
| } |
| } |
| $cache{$obj} = 1; |
| } |
| |
| # dolib is responsible for building libraries. It will call |
| # libobj2shlib is shared libraries are produced, and obj2lib in all |
| # cases. It also makes sure all object files for the library are |
| # built. |
| sub dolib { |
| my $lib = shift; |
| return "" if $cache{$lib}; |
| unless ($disabled{shared}) { |
| my %ordinals = |
| $unified_info{ordinals}->{$lib} |
| ? (ordinals => $unified_info{ordinals}->{$lib}) : (); |
| $OUT .= libobj2shlib(shlib => $unified_info{sharednames}->{$lib}, |
| lib => $lib, |
| objs => [ map { (my $x = $_) =~ s|\.o$||; $x } |
| (@{$unified_info{sources}->{$lib}}, |
| @{$unified_info{shared_sources}->{$lib}}) ], |
| deps => [ reducedepends(resolvedepends($lib)) ], |
| installed => is_installed($lib), |
| %ordinals); |
| foreach (@{$unified_info{shared_sources}->{$lib}}) { |
| doobj($_, $lib, intent => "lib", installed => is_installed($lib)); |
| } |
| } |
| $OUT .= obj2lib(lib => $lib, |
| objs => [ map { (my $x = $_) =~ s|\.o$||; $x } |
| @{$unified_info{sources}->{$lib}} ]); |
| foreach (@{$unified_info{sources}->{$lib}}) { |
| doobj($_, $lib, intent => "lib"); |
| } |
| $cache{$lib} = 1; |
| } |
| |
| # doengine is responsible for building engines. It will call |
| # obj2dso, and also makes sure all object files for the library |
| # are built. |
| sub doengine { |
| my $lib = shift; |
| return "" if $cache{$lib}; |
| $OUT .= obj2dso(lib => $lib, |
| objs => [ map { (my $x = $_) =~ s|\.o$||; $x } |
| (@{$unified_info{sources}->{$lib}}, |
| @{$unified_info{shared_sources}->{$lib}}) ], |
| deps => [ resolvedepends($lib) ], |
| installed => is_installed($lib)); |
| foreach ((@{$unified_info{sources}->{$lib}}, |
| @{$unified_info{shared_sources}->{$lib}})) { |
| doobj($_, $lib, intent => "dso", installed => is_installed($lib)); |
| } |
| $cache{$lib} = 1; |
| } |
| |
| # dobin is responsible for building programs. It will call obj2bin, |
| # and also makes sure all object files for the library are built. |
| sub dobin { |
| my $bin = shift; |
| return "" if $cache{$bin}; |
| my $deps = [ reducedepends(resolvedepends($bin)) ]; |
| $OUT .= obj2bin(bin => $bin, |
| objs => [ map { (my $x = $_) =~ s|\.o$||; $x } |
| @{$unified_info{sources}->{$bin}} ], |
| deps => $deps, |
| installed => is_installed($bin)); |
| foreach (@{$unified_info{sources}->{$bin}}) { |
| doobj($_, $bin, intent => "bin", installed => is_installed($bin)); |
| } |
| $cache{$bin} = 1; |
| } |
| |
| # dobin is responsible for building scripts from templates. It will |
| # call in2script. |
| sub doscript { |
| my $script = shift; |
| return "" if $cache{$script}; |
| $OUT .= in2script(script => $script, |
| sources => $unified_info{sources}->{$script}, |
| installed => is_installed($script)); |
| $cache{$script} = 1; |
| } |
| |
| sub dodir { |
| my $dir = shift; |
| return "" if !exists(&generatedir) or $cache{$dir}; |
| $OUT .= generatedir(dir => $dir, |
| deps => $unified_info{dirinfo}->{$dir}->{deps}, |
| %{$unified_info{dirinfo}->{$_}->{products}}); |
| $cache{$dir} = 1; |
| } |
| |
| # Start with populating the cache with all the overrides |
| %cache = map { $_ => 1 } @{$unified_info{overrides}}; |
| |
| # For convenience collect information regarding directories where |
| # files are generated, those generated files and the end product |
| # they end up in where applicable. Then, add build rules for those |
| # directories |
| if (exists &generatedir) { |
| my %loopinfo = ( "dso" => [ @{$unified_info{engines}} ], |
| "lib" => [ @{$unified_info{libraries}} ], |
| "bin" => [ @{$unified_info{programs}} ], |
| "script" => [ @{$unified_info{scripts}} ] ); |
| foreach my $type (keys %loopinfo) { |
| foreach my $product (@{$loopinfo{$type}}) { |
| my %dirs = (); |
| my $pd = dirname($product); |
| |
| # We already have a "test" target, and the current directory |
| # is just silly to make a target for |
| $dirs{$pd} = 1 unless $pd eq "test" || $pd eq "."; |
| |
| foreach (@{$unified_info{sources}->{$product}}) { |
| my $d = dirname($_); |
| |
| # We don't want to create targets for source directories |
| # when building out of source |
| next if ($config{sourcedir} ne $config{builddir} |
| && $d =~ m|^\Q$config{sourcedir}\E|); |
| # We already have a "test" target, and the current directory |
| # is just silly to make a target for |
| next if $d eq "test" || $d eq "."; |
| |
| $dirs{$d} = 1; |
| push @{$unified_info{dirinfo}->{$d}->{deps}}, $_ |
| if $d ne $pd; |
| } |
| foreach (keys %dirs) { |
| push @{$unified_info{dirinfo}->{$_}->{products}->{$type}}, |
| $product; |
| } |
| } |
| } |
| } |
| |
| # Build mandatory generated headers |
| foreach (@{$unified_info{depends}->{""}}) { dogenerate($_); } |
| |
| # Build all known libraries, engines, programs and scripts. |
| # Everything else will be handled as a consequence. |
| foreach (@{$unified_info{libraries}}) { dolib($_); } |
| foreach (@{$unified_info{engines}}) { doengine($_); } |
| foreach (@{$unified_info{programs}}) { dobin($_); } |
| foreach (@{$unified_info{scripts}}) { doscript($_); } |
| |
| foreach (sort keys %{$unified_info{dirinfo}}) { dodir($_); } |
| |
| # Finally, should there be any applicable BEGINRAW/ENDRAW sections, |
| # they are added here. |
| $OUT .= $_."\n" foreach @{$unified_info{rawlines}}; |
| -} |