#!/usr/bin/perl -w # # Copyright © 2005-2009 Roger Leigh # Copyright © 2009 Andres Mejia # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see # . # ####################################################################### use strict; use warnings; package Conf; sub setup { my $conf = shift; my %update_keys = ( 'DPKG_BUILDPACKAGE_OPTS' => { DEFAULT => ['-S', '-us', '-uc', ] }, 'DPKG_BUILDPACKAGE_EXTRA_OPTS' => { DEFAULT => [] }, 'SBUILD_OPTS' => { DEFAULT => [] }, 'SBUILD_EXTRA_OPTS' => { DEFAULT => [] }, 'LINTIAN_OPTS' => { DEFAULT => [] }, 'LINTIAN_EXTRA_OPTS' => { DEFAULT => [] }, 'NO_LINTIAN' => { DEFAULT => 0 }, 'PRE_DPKG_BUILDPACKAGE_COMMANDS' => { DEFAULT => [] }, 'PRE_SBUILD_COMMANDS' => { DEFAULT => [] }, 'PRE_LINTIAN_COMMANDS' => { DEFAULT => [] }, 'PRE_EXIT_COMMANDS' => { DEFAULT => [] }, ); $conf->set_allowed_keys(\%update_keys); } # This subroutine is used to read a different set of config files for # sbuild-debuild sub read_sbuild_debuild_config { my $self = shift; # Our HOME environment variable. my $HOME = $self->get('HOME'); # Variables are undefined, so config will default to DEFAULT if unset. my $dpkg_buildpackage_opts = undef; my $dpkg_buildpackage_extra_opts = undef; my $sbuild_opts = undef; my $sbuild_extra_opts = undef; my $lintian_opts = undef; my $lintian_extra_opts = undef; my $no_lintian = undef; my $pre_dpkg_buildpackage_commands = undef; my $pre_sbuild_commands = undef; my $pre_lintian_commands = undef; my $pre_exit_commands = undef; foreach ("/etc/sbuild/sbuild-debuild.conf", "$HOME/.sbuild-debuildrc") { if (-r $_) { my $e = eval `cat "$_"`; if (!defined($e)) { print STDERR "E: $_: Errors found in configuration file:\n$@"; exit(1); } } } $self->set('DPKG_BUILDPACKAGE_OPTS', $dpkg_buildpackage_opts) if ($dpkg_buildpackage_opts); push(@{$self->get('DPKG_BUILDPACKAGE_OPTS')}, @{$dpkg_buildpackage_extra_opts}) if ($dpkg_buildpackage_extra_opts); $self->set('SBUILD_OPTS', $sbuild_opts) if ($sbuild_opts); push(@{$self->get('SBUILD_OPTS')}, @{$sbuild_extra_opts}) if ($sbuild_extra_opts); $self->set('LINTIAN_OPTS', $lintian_opts) if ($lintian_opts); push(@{$self->get('LINTIAN_OPTS')}, @{$lintian_extra_opts}) if ($lintian_extra_opts); $self->set('NO_LINTIAN', $no_lintian) if ($no_lintian); $self->set('PRE_DPKG_BUILDPACKAGE_COMMANDS', $pre_dpkg_buildpackage_commands) if ($pre_dpkg_buildpackage_commands); $self->set('PRE_SBUILD_COMMANDS', $pre_sbuild_commands) if ($pre_sbuild_commands); $self->set('PRE_LINTIAN_COMMANDS', $pre_lintian_commands) if ($pre_lintian_commands); $self->set('PRE_EXIT_COMMANDS', $pre_exit_commands) if ($pre_exit_commands); } package Options; use Sbuild::OptionsBase; use Sbuild::Conf; BEGIN { use Exporter (); our (@ISA, @EXPORT); @ISA = qw(Exporter Sbuild::OptionsBase); @EXPORT = qw(); } sub set_options { my $self = shift; $self->add_options( "dpkg-buildpackage-opts=s" => sub { my @opt_values = split(/\s+/,$_[1]); $self->set_conf('DPKG_BUILDPACKAGE_OPTS', \@opt_values); }, "dpkg-buildpackage-extra-opts=s" => sub { my @opt_values = split(/\s+/,$_[1]); push(@{$self->get_conf('DPKG_BUILDPACKAGE_OPTS')}, @opt_values); }, "sbuild-opts=s" => sub { my @opt_values = split(/\s+/,$_[1]); $self->set_conf('SBUILD_OPTS', \@opt_values); }, "sbuild-extra-opts=s" => sub { my @opt_values = split(/\s+/,$_[1]); push(@{$self->get_conf('SBUILD_OPTS')}, @opt_values); }, "lintian-opts=s" => sub { my @opt_values = split(/\s+/,$_[1]); $self->set_conf('LINTIAN_OPTS', \@opt_values); }, "lintian-extra-opts=s" => sub { my @opt_values = split(/\s+/,$_[1]); push(@{$self->get_conf('LINTIAN_OPTS')}, @opt_values); }, "no-lintian" => sub { $self->set_conf('NO_LINTIAN', 1); }, "pre-dpkg-buildpackage-commands=s" => sub { push(@{$self->get_conf('PRE_DPKG_BUILDPACKAGE_COMMANDS')}, $_[1]); }, "pre-sbuild-commands=s" => sub { push(@{$self->get_conf('PRE_SBUILD_COMMANDS')}, $_[1]); }, "pre-lintian-commands=s" => sub { push(@{$self->get_conf('PRE_LINTIAN_COMMANDS')}, $_[1]); }, "pre-exit-commands=s" => sub { push(@{$self->get_conf('PRE_EXIT_COMMANDS')}, $_[1]); }, ); } package main; use Getopt::Long; use Cwd; use File::Basename; use Sbuild qw(help_text version_text usage_error check_group_membership); my $conf = Sbuild::Conf::new(); Conf::setup($conf); exit 1 if !defined($conf); $conf->read_sbuild_debuild_config(); my $options = Options->new($conf, "sbuild-debuild", "1"); exit 1 if !defined($options); check_group_membership(); # This subroutine determines the architecture we're building for. sub detect_arch { # Detect if we're building for another architecture my @values = grep {/--arch=.+/} @{$conf->get('SBUILD_OPTS')}; my $arch_opt = $values[0]; $arch_opt =~ s/--arch=// if ($arch_opt); # Determine the arch using dpkg-architecture my $dpkg_arch_command = '/usr/bin/dpkg-architecture -qDEB_HOST_ARCH'; $dpkg_arch_command .= " -a$arch_opt" if ($arch_opt); # Grab the architecture and return it. We discard output from STDERR # to suppress the "Specified GNU system ... does not match" warning that # may occur from dpkg-architecture my $arch = qx($dpkg_arch_command 2>/dev/null); chomp $arch; return $arch; } # This subroutine determines the package and version we're building for sub detect_package_and_version { my ($build_input) = @_; # chdir into package directories, but not dsc files chdir($build_input) unless ($build_input =~ /.*\.dsc$/); my $output; if ($build_input =~ m/.*\.dsc$/) { # Read the dsc file directly. open($output, '<', $build_input); } else { # Grab the output from dpkg-parsechangelog my $dpkg_parsechangelog = '/usr/bin/dpkg-parsechangelog'; open($output, '-|', $dpkg_parsechangelog); } # Grab the package and version info my ($package, $version); while (<$output>) { $package = $1 if ($_ =~ /^Source: (.*)$/); $version = $1 if ($_ =~ /^Version: (.*)$/); last if (($package) and ($version)); } return ($package, $version); } # This subroutine strips the epoch from a Debian package version. sub strip_epoch { my ($version) = @_; $version =~ s/^[^:]*?://; return $version; } # This subroutine processes the array external ommands to run at the various # stages of sbuild-debuild's run sub process_commands { my ($commands, $dsc, $source_changes, $bin_changes) = @_; # Determine which set of commands to run based on the string $commands my @commands; if ($commands eq "pre_dpkg_buildpackage_commands") { @commands = @{$conf->get('PRE_DPKG_BUILDPACKAGE_COMMANDS')}; } elsif ($commands eq "pre_sbuild_commands") { @commands = @{$conf->get('PRE_SBUILD_COMMANDS')}; } elsif ($commands eq "pre_lintian_commands") { @commands = @{$conf->get('PRE_LINTIAN_COMMANDS')}; } elsif ($commands eq "pre_exit_commands") { @commands = @{$conf->get('PRE_EXIT_COMMANDS')}; } # Run each command, substituting the various @SBUILD_DEBUILD_*@ options from # the commands to run with the appropriate subsitutions. my $returnval = 1; foreach my $command (@commands) { $command =~ s/\@SBUILD_DEBUILD_DSC@/$dsc/; $command =~ s/\@SBUILD_DEBUILD_SOURCE_CHANGES@/$source_changes/; $command =~ s/\@SBUILD_DEBUILD_BIN_CHANGES@/$bin_changes/; my @args = split(/\s+/, $command); print "Running $command\n"; system(@args); if (($? >> 8) != 0) { print "Failed to run command ($command): $?"; $returnval = 0; } } return $returnval; } # Subroutine to determine various files we'll be working with. sub detect_files { my ($build_input) = @_; # Determine the dsc and changes files that we'll use my ($package, $version) = detect_package_and_version($build_input); $version = strip_epoch($version); my $arch = detect_arch(); my ($dsc, $dirname); if ($build_input =~ /.*\.dsc$/) { $dsc = Cwd::abs_path("$build_input"); $dirname = Cwd::abs_path(dirname($build_input)); } else { $dirname = Cwd::abs_path("$build_input/.."); $dsc = "$dirname/" . $package . "_" . "$version.dsc"; } my $source_changes = "$dirname/" . $package . "_$version" . "_source.changes"; my $bin_changes = "$dirname/" . $package . "_$version" . "_$arch.changes"; return ($dsc, $source_changes, $bin_changes); } # Process a debianized package directory or .dsc file. sub process_package { my ($build_input) = @_; # We use this to determine if there was a problem processing the external # commands. my $returnval = 1; # Save the current directory we're in. my $currentdir = getcwd(); # Determine the dsc and changes files that we'll use my ($dsc, $source_changes, $bin_changes) = detect_files($build_input); print "Processing pre dpkg-buildpackage commands.\n"; $returnval = 0 unless process_commands("pre_dpkg_buildpackage_commands", $dsc, $source_changes, $bin_changes); if ($build_input !~ /.*\.dsc$/) { chdir($build_input); print "Running dpkg-buildpackage.\n"; system('/usr/bin/dpkg-buildpackage', @{$conf->get('DPKG_BUILDPACKAGE_OPTS')}); if (($? >> 8) != 0) { print "Running dpkg-buildpckage failed: $?"; chdir($currentdir); return 0; } } print "Processing pre sbuild commands.\n"; $returnval = 0 unless process_commands("pre_sbuild_commands", $dsc, $source_changes, $bin_changes); chdir(dirname($dsc)); print "Running sbuild.\n"; system('/usr/bin/sbuild', @{$conf->get('SBUILD_OPTS')}, $dsc); if (($? >> 8) != 0) { print "Running sbuild failed: $?"; chdir($currentdir); return 0; } print "Processing pre lintian commands.\n"; $returnval = 0 unless process_commands("pre_lintian_commands", $dsc, $source_changes, $bin_changes); if ((!$conf->get('NO_LINTIAN')) && (-x '/usr/bin/lintian')) { print "Running lintian.\n"; system('/usr/bin/lintian', @{$conf->get('LINTIAN_OPTS')}, '--', $bin_changes); if (($? >> 8) != 0) { print "Running lintian failed: $?"; chdir($currentdir); return 0; } } print "Processing commands run before exiting.\n"; $returnval = 0 unless process_commands("pre_exit_commands", $dsc, $source_changes, $bin_changes); # Go back to the directory we were in earlier chdir($currentdir); return $returnval; } # Process each package directory and .dsc file. my $status = 0; if (!@ARGV) { $status = 1 unless process_package(getcwd()); } else { foreach my $arg (@ARGV) { $status = 1 unless process_package($arg); } } exit $status;