diff options
Diffstat (limited to 'SBO-Lib/lib/SBO')
| -rw-r--r-- | SBO-Lib/lib/SBO/Lib/Build.pm | 783 | 
1 files changed, 783 insertions, 0 deletions
diff --git a/SBO-Lib/lib/SBO/Lib/Build.pm b/SBO-Lib/lib/SBO/Lib/Build.pm new file mode 100644 index 0000000..0f41be4 --- /dev/null +++ b/SBO-Lib/lib/SBO/Lib/Build.pm @@ -0,0 +1,783 @@ +package SBO::Lib::Build; + +use 5.016; +use strict; +use warnings; + +our $VERSION = '2.0'; + +use SBO::Lib::Util qw/ :const script_error get_sbo_from_loc get_arch check_multilib uniq %config in /; +use SBO::Lib::Tree qw/ get_sbo_location /; +use SBO::Lib::Info qw/ get_sbo_version check_x32 get_requires /; +use SBO::Lib::Download qw/ get_sbo_downloads get_dl_fns get_filename_from_link check_distfiles /; + +use Exporter 'import'; +use Fcntl qw(F_SETFD F_GETFD); +use File::Copy; # copy() and move() +use File::Path qw/ make_path remove_tree /; +use File::Temp qw/ tempdir tempfile /; +use Tie::File; + +our @EXPORT_OK = qw{ +  do_convertpkg +  do_slackbuild +  do_upgradepkg +  get_build_queue +  get_dc_regex +  get_pkg_name +  get_src_dir +  get_tmp_extfn +  make_clean +  make_distclean +  merge_queues +  perform_sbo +  process_sbos +  revert_slackbuild +  rewrite_slackbuild + +  $tempdir +  $tmpd +  $env_tmp +}; + +our %EXPORT_TAGS = ( +  all => \@EXPORT_OK, +); + +=head1 NAME + +SBO::Lib::Build - Routines for building slackware packages from SlackBuilds.org. + +=head1 SYNOPSIS + +  use SBO::Lib::Build qw/ perform_sbo /; + +  my ($foo, $bar, $exit) = perform_sbo(LOCATION => $location, ARCH => 'x86_64'); + +=head1 VARIABLES + +=head2 $env_tmp + +This will reflect the C<$TMP> from the environment, being C<undef> if it is not +set. + +=head2 $tmpd + +Will be the same as C<$TMP> if it is set, otherwise it will be C</tmp/SBo>. + +=head2 $tempdir + +This is a temporary directory created for sbotools' use, and it should be +cleaned up when sbotools exits. + +=cut + +# get $TMP from the env, if defined - we use two variables here because there +# are times when we need to know if the environment variable is set, and other +# times where it doesn't matter. +our $env_tmp = $ENV{TMP}; +our $tmpd = $env_tmp ? $env_tmp : '/tmp/SBo'; +make_path($tmpd) unless -d $tmpd; + +our $tempdir = tempdir(CLEANUP => 1, DIR => $tmpd); + +=head1 SUBROUTINES + +=cut + +=head2 do_convertpkg + +  my ($name32, $exit) = do_convertpkg($name64); + +C<do_convertpkg()> runs C<convertpkg> on the package in C<$name64>. + +It returns two values. If the second value is true, the first will contain an +error message. Otherwise it will contain the name of the converted package. + +=cut + +# run convertpkg on a package to turn it into a -compat32 thing +sub do_convertpkg { +  script_error('do_convertpkg requires an argument.') unless @_ == 1; +  my $pkg = shift; +  my $tempfh = tempfile(DIR => $tempdir); +  my $fn = get_tmp_extfn($tempfh); + +  # get a tempfile to store the exit status of the slackbuild +  my $exit_temp = tempfile(DIR => $tempdir); +  my ($exit_fn, $exit) = get_tmp_extfn($exit_temp); +  return $exit_fn, undef, $exit if $exit; + +  my $c32tmpd = $env_tmp // '/tmp'; +  my $cmd = "( /bin/bash -c '/usr/sbin/convertpkg-compat32 -i $pkg -d $c32tmpd'; echo \$? > $exit_fn ) | tee $fn"; +  my $ret = system('/bin/bash', '-c', $cmd); + +  # If the system call worked, check the saved exit status +  seek $exit_temp, 0, 0; +  $ret = do {local $/; <$exit_temp>} if $ret == 0; + +  if ($ret != 0) { +    return "convertpkg-compt32 returned non-zero exit status\n", +      _ERR_CONVERTPKG; +  } +  unlink $pkg; +  return get_pkg_name($tempfh); +} + +=head2 do_slackbuild + +  my ($ver, $pkg, $src, $exit) = do_slackbuild(LOCATION => $location); + +C<do_slackbuild()> will make some checks and set up the C<perform_sbo()> call, +if needed run C<do_convertpkg()>, and return the results. + +It will return a list of four values. If the fourth one is a true value, the +first one will be an error message. Otherwise the first will be the version, +the second will be the package, and the third will be an array reference to the +source directories created by the build. + +=cut + +# "public interface", sort of thing. +sub do_slackbuild { +  my %args = ( +    OPTS      => 0, +    JOBS      => 0, +    LOCATION  => '', +    COMPAT32  => 0, +    @_ +  ); +  $args{LOCATION} or script_error('do_slackbuild requires LOCATION.'); +  my $location = $args{LOCATION}; +  my $sbo = get_sbo_from_loc($location); +  my $arch = get_arch(); +  my $multilib = check_multilib(); +  my $version = get_sbo_version($location); +  my $x32; +  # ensure x32 stuff is set correctly, or that we're setup for it +  if ($args{COMPAT32}) { +    unless ($multilib) { +      return "compat32 requires multilib.\n", (undef) x 2, +        _ERR_NOMULTILIB; +    } +    unless (-f '/usr/sbin/convertpkg-compat32') { +      return "compat32 requires /usr/sbin/convertpkg-compat32.\n", +        (undef) x 2, _ERR_NOCONVERTPKG; +    } +  } else { +    if ($arch eq 'x86_64') { +      $x32 = check_x32 $args{LOCATION}; +      if ($x32 && ! $multilib) { +        my $warn = +          "$sbo is 32-bit which requires multilib on x86_64.\n"; +        return $warn, (undef) x 2, _ERR_NOMULTILIB; +      } +    } +  } +  # setup and run the .SlackBuild itself +  my ($pkg, $src, $exit) = perform_sbo( +    OPTS => $args{OPTS}, +    JOBS => $args{JOBS}, +    LOCATION => $location, +    ARCH => $arch, +    C32 => $args{COMPAT32}, +    X32 => $x32, +  ); +  return $pkg, (undef) x 2, $exit if $exit; +  if ($args{COMPAT32}) { +    ($pkg, $exit) = do_convertpkg($pkg); +    return $pkg, (undef) x 2, $exit if $exit; +  } +  return $version, $pkg, $src; +} + +=head2 do_upgradepkg + +  do_upgradepkg($pkg); + +C<do_upgradepkg()> runs C<upgradepkg> on C<$pkg>. + +There is no useful return value. + +=cut + +# run upgradepkg for a created package +sub do_upgradepkg { +  script_error('do_upgradepkg requires an argument.') unless @_ == 1; +  system('/sbin/upgradepkg', '--reinstall', '--install-new', shift); +  return 1; +} + +=head2 get_build_queue + +  my @queue = @{ get_build_queue($sbo, my $warnings) }; + +C<get_build_queue()> gets the prerequisites for C<$sbo>, and updates the +C<$warnings> hash reference with any C<%README%> encountered. It returns the +prerequisites and the C<$sbo> in the order in which they need to be built. + +=cut + +sub get_build_queue { +  script_error('get_build_queue requires two arguments.') unless @_ == 2; +  return [ _build_queue(@_) ]; +} + +=head2 get_dc_regex + +  my ($rx, $initial) = get_dc_regex($line); + +C<get_dc_regex()> when given a line that is an untar or similar command, creates +a regular expression which should match the filename. This is returned, together +with the C<$initial> character which will start the filename match. + +=cut + +# given a line that looks like it's decompressing something, try to return a +# valid filename regex +sub get_dc_regex { +  my $line = shift; +  # get rid of initial 'tar x'whatever stuff +  $line =~ s/^.*(?<![a-z])(tar|p7zip|unzip|ar|rpm2cpio|sh)\s+[^\s]+\s+//; +  # need to know preceeding character - should be safe to assume it's either +  # a slash or a space +  my $initial = $line =~ qr|/| ? '/' : ' '; +  # get rid of initial path info +  $line =~ s|^\$[^/]+/||; +  # convert any instances of command substitution to [^-]+ +  $line =~ s/\$\([^)]+\)/[^-]+/g; +  # convert any bash variables to [^-]+ +  $line =~ s/\$({|)[A-Za-z0-9_]+(}|)/[^-]+/g; +  # get rid of anything excess at the end +  $line =~ s/\s+.*$//; +  # fix .?z* at the end +  $line =~ s/\.\?z\*/\.[a-z]z.*/; +  # return what's left as a regex +  my $regex = qr/$initial$line/; +  return $regex, $initial; +} + +=head2 get_pkg_name + +  my $name = get_pkg_name($fh); + +C<get_pkg_name()> reads the $fh filehandle for text matching the output of +C<makepkg> where it outputs the filename of the package it made, and returns it. + +=cut + +# pull the created package name from the temp file we tee'd to +sub get_pkg_name { +  my $fh = shift; +  seek $fh, 0, 0; +  my $regex = qr/^Slackware\s+package\s+([^\s]+)\s+created\.$/; +  my $out; +  FIRST: while (my $line = <$fh>) { +    last FIRST if $out = ($line =~ $regex)[0]; +  } +  return $out; +} + +=head2 get_src_dir + +  my @dirs = @{ get_src_dir($fh) }; + +C<get_src_dir()> returns a list of the directories under C</tmp/SBo> or C<$TMP> +that aren't mentioned in C<$fh>. + +=cut + +sub get_src_dir { +  script_error('get_src_dir requires an argument') unless @_ == 1; +  my $fh = shift; +  my @src_dirs; +  # scripts use either $TMP or /tmp/SBo +  if (opendir(my $tsbo_dh, $tmpd)) { +    FIRST: while (my $ls = readdir $tsbo_dh) { +      next FIRST if in($ls => qw/ . .. /, qr/^package-/); +      next FIRST unless -d "$tmpd/$ls"; +      my $found = 0; +      seek $fh, 0, 0; +      SECOND: while (my $line = <$fh>) { +        chomp ($line); +        if ($line eq $ls) { +          $found++; +          last SECOND; +        } +      } +      push @src_dirs, $ls unless $found; +    } +    close $tsbo_dh; +  } +  close $fh; +  return \@src_dirs; +} + +=head2 get_tmp_extfn + +  my ($ret, $exit) = get_tmp_extfn($fh); + +C<get_tmp_extfn()> gets the filename in the form of C</dev/fd/X> for the C<$fh> +passed in, setting flags on it that make it usable from other processes without +messing things up. + +It returns a list of two values. If the second value is true, the first will +contain an error message. Otherwise, the first value will hold the filename. + +=cut + +# return a filename from a temp fh for use externally +sub get_tmp_extfn { +  script_error('get_tmp_extfn requires an argument.') unless @_ == 1; +  my $fh = shift; +  unless (fcntl($fh, F_SETFD, 0)) { +    return "Can't unset exec-on-close bit.\n", _ERR_F_SETFD; +  } +  return '/dev/fd/'. fileno $fh; +} + +=head2 make_clean + +  make_clean(SBO => $sbo, SRC => $src, VERSION => $ver); + +C<make_clean()> removes source directories, package directories, and compat32 +directories that are left over from a slackbuild run. + +It has no useful return value. + +=cut + +# remove work directories (source and packaging dirs under /tmp/SBo or $TMP and /tmp or $OUTPUT) +sub make_clean { +  my %args = ( +    SBO      => '', +    SRC      => '', +    VERSION  => '', +    @_ +  ); +  unless ($args{SBO} && $args{SRC} && $args{VERSION}) { +    script_error('make_clean requires three arguments.'); +  } +  my $src = $args{SRC}; +  say "Cleaning for $args{SBO}-$args{VERSION}..."; +  for my $dir (@$src) { +    remove_tree("$tmpd/$dir") if -d "$tmpd/$dir"; +  } + +  my $output = $ENV{OUTPUT} // '/tmp'; +  remove_tree("$output/package-$args{SBO}") if +    -d "$output/package-$args{SBO}"; + +  if ($args{SBO} =~ /^(.+)-compat32$/) { +    my $pkg_name = $1; +    remove_tree("/tmp/package-$args{SBO}") if +      not defined $env_tmp and +      -d "/tmp/package-$args{SBO}"; +    remove_tree("$tmpd/package-$pkg_name") if +      -d "$tmpd/package-$pkg_name"; +  } +  return 1; +} + +=head2 make_distclean + +  make_distclean(SRC => $src, VERSION => $ver, LOCATION => $loc); + +C<make_distclean()> does everything C<make_clean()> does, but in addition it +also removes distribution files, such as the downloaded source tarballs. + +It has no useful return value. + +=cut + +# remove distfiles +sub make_distclean { +  my %args = ( +    SRC       => '', +    VERSION   => '', +    LOCATION  => '', +    @_ +  ); +  unless ($args{SRC} && $args{VERSION} && $args{LOCATION}) { +    script_error('make_distclean requires four arguments.'); +  } +  my $sbo = get_sbo_from_loc($args{LOCATION}); +  make_clean(SBO => $sbo, SRC => $args{SRC}, VERSION => $args{VERSION}); +  say "Distcleaning for $sbo-$args{VERSION}..."; +  # remove any distfiles for this particular SBo. +  my $downloads = get_sbo_downloads(LOCATION => $args{LOCATION}); +  for my $key (keys %$downloads) { +    my $filename = get_filename_from_link($key); +    unlink $filename if -f $filename; +  } +  return 1; +} + +=head2 merge_queues + +  my @merged = @{ merge_queues([@queue1], [@queue2]) }; + +C<merge_queues> takes two array references and merges them with C<@queue1> in +front, and then anything in C<@queue2> that wasn't already in C<@queue1>. This +is then returned as an array reference. + +=cut + +sub merge_queues { +  # Usage: merge_queues(\@queue_a, \@queue_b); +  # Results in queue_b being merged into queue_a (without duplicates) +  script_error('merge_queues requires two arguments.') unless @_ == 2; + +  return [ uniq @{$_[0]}, @{$_[1]} ]; +} + +=head2 perform_sbo + +  my ($pkg, $src, $exit) = perform_sbo(LOCATION => $location, ARCH => $arch); + +C<perform_sbo()> preps and runs a .SlackBuild. It returns a list of three +values, and if the third one is a true value, the first one will be an error +message. Otherwise the first one will be the package name that was built, and +the second one will be an array reference containing the source directories +that were created. + +=cut + +# prep and run .SlackBuild +sub perform_sbo { +  my %args = ( +    OPTS      => 0, +    JOBS      => 0, +    LOCATION  => '', +    ARCH      => '', +    C32       => 0, +    X32       => 0, +    @_ +  ); +  unless ($args{LOCATION} && $args{ARCH}) { +    script_error('perform_sbo requires LOCATION and ARCH.'); +  } + +  my $location = $args{LOCATION}; +  my $sbo = get_sbo_from_loc($location); +  my ($cmd, %changes); +  # set any changes we need to make to the .SlackBuild, setup the command + +  $cmd = '( '; + +  if ($args{ARCH} eq 'x86_64' and ($args{C32} || $args{X32})) { +    if ($args{C32}) { +      $changes{libdirsuffix} = ''; +    } elsif ($args{X32}) { +      $changes{arch_out} = 'i486'; +    } +    $cmd .= '. /etc/profile.d/32dev.sh &&'; +  } +  if ($args{JOBS} and $args{JOBS} ne 'FALSE') { +    $changes{jobs} = 1; +  } +  $cmd .= " $args{OPTS}" if $args{OPTS}; +  $cmd .= " MAKEOPTS=\"-j$args{JOBS}\"" if $args{JOBS}; +  # we need to get a listing of /tmp/SBo, or $TMP, if we can, before we run +  # the SlackBuild so that we can compare to a listing taken afterward. +  my $src_ls_fh = tempfile(DIR => $tempdir); +  if (opendir(my $tsbo_dh, $tmpd)) { +    FIRST: while (my $dir = readdir $tsbo_dh) { +      next FIRST if in($dir => qw/ . .. /); +      say {$src_ls_fh} $dir; +    } +  } +  # get a tempfile to store the exit status of the slackbuild +  my $exit_temp = tempfile(DIR => $tempdir); +  my ($exit_fn, $exit) = get_tmp_extfn($exit_temp); +  return $exit_fn, undef, $exit if $exit; +  # set TMP/OUTPUT if set in the environment +  $cmd .= " TMP=$env_tmp" if $env_tmp; +  $cmd .= " OUTPUT=$ENV{OUTPUT}" if defined $ENV{OUTPUT}; +  $cmd .= " /bin/bash $location/$sbo.SlackBuild; echo \$? > $exit_fn )"; +  my $tempfh = tempfile(DIR => $tempdir); +  my $fn; +  ($fn, $exit) = get_tmp_extfn($tempfh); +  return $fn, undef, $exit if $exit; +  $cmd .= " | tee -a $fn"; +  # attempt to rewrite the slackbuild, or exit if we can't +  my $fail; +  ($fail, $exit) = rewrite_slackbuild( +    SBO => $sbo, +    SLACKBUILD => "$location/$sbo.SlackBuild", +    CHANGES => \%changes, +    C32 => $args{C32}, +  ); +  return $fail, undef, $exit if $exit; +  # run the slackbuild, grab its exit status, revert our changes +  chdir $location, system $cmd; +  seek $exit_temp, 0, 0; +  my $out = do {local $/; <$exit_temp>}; +  close $exit_temp; +  revert_slackbuild("$location/$sbo.SlackBuild"); +  # return error now if the slackbuild didn't exit 0 +  return "$sbo.SlackBuild return non-zero\n", undef, _ERR_BUILD if $out != 0; +  my $pkg = get_pkg_name($tempfh); +  return "$sbo.SlackBuild didn't create a package\n", undef, _ERR_BUILD if not defined $pkg; +  my $src = get_src_dir($src_ls_fh); +  return $pkg, $src; +} + +=head2 process_sbos + +  my ($failures, $exit) = process_sbos(TODO => [@queue]); + +C<process_sbos()> processes the C<@queue> of slackbuilds and returns a list of +two values containing any failed builds in an array ref in the first value, and +the exit status in the second. + +=cut + +# do the things with the provided sbos - whether upgrades or new installs. +sub process_sbos { +  my %args = ( +    TODO       => '', +    CMDS       => '', +    OPTS       => '', +    JOBS       => 'FALSE', +    LOCATIONS  => '', +    NOINSTALL  => 0, +    NOCLEAN    => 'FALSE', +    DISTCLEAN  => 'FALSE', +    NON_INT    => 0, +    @_ +  ); +  my $todo = $args{TODO}; +  my $cmds = $args{CMDS}; +  my $opts = $args{OPTS}; +  my $locs = $args{LOCATIONS}; +  my $jobs = $args{JOBS} =~ /^\d+$/ ? $args{JOBS} : 0; +  @$todo >= 1 or script_error('process_sbos requires TODO.'); +  my (@failures, @symlinks, $err); +  FIRST: for my $sbo (@$todo) { +    my $compat32 = $sbo =~ /-compat32$/ ? 1 : 0; +    my ($temp_syms, $exit) = check_distfiles( +      LOCATION => $$locs{$sbo}, COMPAT32 => $compat32 +    ); +    # if $exit is defined, prompt to proceed or return with last $exit +    if ($exit) { +      $err = $exit; +      my $fail = $temp_syms; +      push @failures, {$sbo => $fail}; +      # return now if we're not interactive +      return \@failures, $exit if $args{NON_INT}; +      say "Unable to download/verify source file(s) for $sbo:"; +      say "  $fail"; +      print 'Do you want to proceed? [n] '; +      if (<STDIN> =~ /^[yY]/) { +        next FIRST; +      } else { +        unlink for @symlinks; +        return \@failures, $exit; +      } +    } else { +      push @symlinks, @$temp_syms; +    } +  } +  my $count = 0; +  FIRST: for my $sbo (@$todo) { +    $count++; +    my $options = $$opts{$sbo} // 0; +    my $cmds = $$cmds{$sbo} // []; +    for my $cmd (@$cmds) { +      system($cmd) == 0 or warn "\"$cmd\" exited non-zero\n"; +    } +    # switch compat32 on if upgrading/installing a -compat32 +    # else make sure compat32 is off +    my $compat32 = $sbo =~ /-compat32$/ ? 1 : 0; +    my ($version, $pkg, $src, $exit) = do_slackbuild( +      OPTS      => $options, +      JOBS      => $jobs, +      LOCATION  => $$locs{$sbo}, +      COMPAT32  => $compat32, +    ); +    if ($exit) { +      my $fail = $version; +      push @failures, {$sbo => $fail}; +      # return now if we're not interactive +      return \@failures, $exit if $args{NON_INT}; +      # or if this is the last $sbo +      return \@failures, $exit if $count == @$todo; +      say "Failure encountered while building $sbo:"; +      say "  $fail"; +      print 'Do you want to proceed [n] '; +      if (<STDIN> =~ /^[yY]/) { +        next FIRST; +      } else { +        unlink for @symlinks; +        return \@failures, $exit; +      } +    } + +    do_upgradepkg($pkg) unless $args{NOINSTALL}; + +    unless ($args{DISTCLEAN}) { +      make_clean(SBO => $sbo, SRC => $src, VERSION => $version) +        unless $args{NOCLEAN}; +    } else { +      make_distclean( +        SBO       => $sbo, +        SRC       => $src, +        VERSION   => $version, +        LOCATION  => $$locs{$sbo}, +      ); +    } +    # move package to $config{PKG_DIR} if defined +    unless ($config{PKG_DIR} eq 'FALSE') { +      my $dir = $config{PKG_DIR}; +      unless (-d $dir) { +        mkdir($dir) or warn "Unable to create $dir\n"; +      } +      if (-d $dir) { +        move($pkg, $dir), say "$pkg stored in $dir"; +      } else { +        warn "$pkg left in $tmpd\n"; +      } +    } elsif ($args{DISTCLEAN}) { +      unlink $pkg; +    } +  } +  unlink for @symlinks; +  return \@failures, $err; +} + +=head2 revert_slackbuild + +  revert_slackbuild($path); + +C<revert_slackbuild()> moves back a slackbuild that was rewritten by +C<rewrite_slackbuild()>. + +There is no useful return value. + +=cut + +# move a backed-up .SlackBuild file back into place +sub revert_slackbuild { +  script_error('revert_slackbuild requires an argument') unless @_ == 1; +  my $slackbuild = shift; +  if (-f "$slackbuild.orig") { +    unlink $slackbuild if -f $slackbuild; +    rename "$slackbuild.orig", $slackbuild; +  } +  return 1; +} + +=head2 rewrite_slackbuild + +  my ($ret, $exit) = rewrite_slackbuild(SLACKBUILD => $path); + +C<rewrite_slackbuild()> when given a path and some changes to make, will move +and copy the C<$path> and rewrite the copy with the needed changes. + +It returns a list of two values. The second value is the exit status, and if it +is true, the first value will be an error message. + +=cut + +# make a backup of the existent SlackBuild, and rewrite the original as needed +sub rewrite_slackbuild { +  my %args = ( +    SBO         => '', +    SLACKBUILD  => '', +    CHANGES     => {}, +    C32         => 0, +    @_ +  ); +  $args{SLACKBUILD} or script_error('rewrite_slackbuild requires SLACKBUILD.'); +  my $slackbuild = $args{SLACKBUILD}; +  my $changes = $args{CHANGES}; + +  # $status will be undefined if either the rename or the copy fails, otherwise it will be 1 +  my $status = eval { +    rename($slackbuild, "$slackbuild.orig") or die "not ok"; +    copy("$slackbuild.orig", $slackbuild) or die "not ok"; +    1; +  }; +  if (not $status) { +    rename "$slackbuild.orig", $slackbuild if not -f $slackbuild; +    return "Unable to backup $slackbuild to $slackbuild.orig\n", +      _ERR_OPENFH; +  } + +  my $libdir_regex = qr/^\s*LIBDIRSUFFIX="64"\s*$/; +  my $arch_regex = qr/\$VERSION-\$ARCH-\$BUILD/; +  my $dc_regex = qr/(?<![a-z])(tar|p7zip|unzip|ar|rpm2cpio|sh)\s+/; +  my $make_regex = qr/^\s*make\s*$/; +  # tie the slackbuild, because this is the easiest way to handle this. +  tie my @sb_file, 'Tie::File', $slackbuild; +  # if we're dealing with a compat32, we need to change the tar line(s) so +  # that the 32-bit source is untarred +  if ($args{C32}) { +    my $location = get_sbo_location($args{SBO}); +    my $downloads = get_sbo_downloads( +      LOCATION => $location, +      32 => 1, +    ); +    my $fns = get_dl_fns([keys %$downloads]); +    for my $line (@sb_file) { +      if ($line =~ $dc_regex) { +        my ($regex, $initial) = get_dc_regex($line); +        for my $fn (@$fns) { +          $fn = "$initial$fn"; +          $line =~ s/$regex/$fn/ if $fn =~ $regex; +        } +      } +    } +  } +  for my $line (@sb_file) { +    # then check for and apply any other %$changes +    if (exists $$changes{libdirsuffix}) { +      $line =~ s/64/$$changes{libdirsuffix}/ if $line =~ $libdir_regex; +    } +    if (exists $$changes{arch_out}) { +      $line =~ s/\$ARCH/$$changes{arch_out}/ if $line =~ $arch_regex; +    } +    if (exists $changes->{jobs}) { +      $line =~ s/make/make \$MAKEOPTS/ if $line =~ $make_regex; +    } +  } +  untie @sb_file; +  return 1; +} + +=head1 AUTHORS + +SBO::Lib was originally written by Jacob Pipkin <j@dawnrazor.net> with +contributions from Luke Williams <xocel@iquidus.org> and Andreas +Guldstrand <andreas.guldstrand@gmail.com>. + +=head1 LICENSE + +The sbotools are licensed under the WTFPL <http://sam.zoy.org/wtfpl/COPYING>. + +Copyright (C) 2012-2016, Jacob Pipkin, Luke Williams, Andreas Guldstrand. + +=cut + +sub _build_queue { +  my ($sbos, $warnings) = @_; +  my @queue = @$sbos; +  my @result; + +  while (my $sbo = shift @queue) { +    next if $sbo eq "%README%"; +    my $reqs = get_requires($sbo); +    if (defined $reqs) { +      push @result, _build_queue($reqs, $warnings); +      foreach my $req (@$reqs) { +        $warnings->{$sbo}="%README%" if $req eq "%README%"; +      } +    } +    push @result, $sbo; +  } + +  return uniq @result; +} + +1;  | 
