diff options
Diffstat (limited to 'lib/Debian/Debhelper/Dh_Buildsystems.pm')
-rw-r--r-- | lib/Debian/Debhelper/Dh_Buildsystems.pm | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/lib/Debian/Debhelper/Dh_Buildsystems.pm b/lib/Debian/Debhelper/Dh_Buildsystems.pm new file mode 100644 index 0000000..53b9c06 --- /dev/null +++ b/lib/Debian/Debhelper/Dh_Buildsystems.pm @@ -0,0 +1,321 @@ +# A module for loading and managing debhelper build system classes. +# This module is intended to be used by all dh_auto_* programs. +# +# Copyright: © 2009 Modestas Vainius +# License: GPL-2+ + +package Debian::Debhelper::Dh_Buildsystems; + +use strict; +use warnings; +use Debian::Debhelper::Buildsystem; +use Debian::Debhelper::Dh_Lib; +use File::Spec; + +use Exporter qw(import); +our @EXPORT=qw(&buildsystems_init &buildsystems_do &load_buildsystem &load_all_buildsystems); + +use constant BUILD_STEPS => qw(configure build test install clean); + +# Historical order must be kept for backwards compatibility. New +# build systems MUST be added to the END of the list. +our @BUILDSYSTEMS = ( + "autoconf", + (! compat(7) ? "perl_build" : ()), + "perl_makemaker", + "makefile", + "python_distutils", + (compat(7) ? "perl_build" : ()), + "cmake+makefile", + "cmake+ninja", + "ant", + "qmake", + "qmake_qt4", + "qmake6", + "meson+ninja", + "ninja", +); + +our @THIRD_PARTY_BUILDSYSTEMS = ( + 'maven', + 'gradle', + 'mkcmake', + 'bmake', + 'golang', +); + +# Visible for dh_assistant's sake; not API for external tools! +our $opt_buildsys; +my $opt_sourcedir; +my $opt_builddir; +my $opt_list; +my $opt_parallel; + +*create_buildsystem_instance = \&Debian::Debhelper::Buildsystem::_create_buildsystem_instance; + +sub _insert_cmd_opts { + my (%bsopts) = @_; + if (!exists $bsopts{builddir} && defined $opt_builddir) { + $bsopts{builddir} = ($opt_builddir eq "") ? undef : $opt_builddir; + } + if (!exists $bsopts{sourcedir} && defined $opt_sourcedir) { + $bsopts{sourcedir} = ($opt_sourcedir eq "") ? undef : $opt_sourcedir; + } + if (!exists $bsopts{parallel}) { + $bsopts{parallel} = $opt_parallel; + } + return %bsopts; +} + +# Autoselect a build system from the list of instances +sub autoselect_buildsystem { + my $step=shift; + my $selected; + my $selected_level = 0; + + foreach my $inst (@_) { + # Only more specific build system can be considered beyond + # the currently selected one. + if (defined($selected)) { + my $ok = $inst->isa(ref($selected)) ? 1 : 0; + if (not $ok and $inst->IS_GENERATOR_BUILD_SYSTEM) { + $ok = 1 if $inst->get_targetbuildsystem->NAME eq $selected->NAME; + } + next if not $ok; + } + + # If the build system says it is auto-buildable at the current + # step and it can provide more specific information about its + # status than its parent (if any), auto-select it. + my $level = $inst->check_auto_buildable($step); + if ($level > $selected_level) { + $selected = $inst; + $selected_level = $level; + } + } + return $selected; +} + +# Similar to create_buildsystem_instance(), but it attempts to autoselect +# a build system if none was specified. In case autoselection fails or an +# explicit “none” is requested, undef is returned. +sub load_buildsystem { + my $system=shift; + my $step=shift; + my %opts = _insert_cmd_opts(@_); + my $system_options; + if (defined($system) && ref($system) eq 'HASH') { + $system_options = $system; + $system = $system_options->{'system'}; + } + if (defined $system) { + return undef if $system eq 'none'; + my $inst = create_buildsystem_instance($system, 1, %opts); + return $inst; + } + else { + # Try to determine build system automatically + my @buildsystems; + foreach $system (@BUILDSYSTEMS) { + push @buildsystems, create_buildsystem_instance($system, 1, %opts); + } + if (!$system_options || $system_options->{'enable-thirdparty'}) { + foreach $system (@THIRD_PARTY_BUILDSYSTEMS) { + push @buildsystems, create_buildsystem_instance($system, 0, %opts); + } + } + return autoselect_buildsystem($step, @buildsystems); + } +} + +sub load_all_buildsystems { + my $incs=shift || \@INC; + my %opts = _insert_cmd_opts(@_); + my (%buildsystems, %genbuildsystems, @buildsystems); + + foreach my $inc (@$incs) { + my $path = File::Spec->catdir($inc, "Debian/Debhelper/Buildsystem"); + if (-d $path) { + foreach my $module_path (glob "$path/*.pm") { + my $name = basename($module_path); + $name =~ s/\.pm$//; + next if exists $buildsystems{$name} or exists $genbuildsystems{$name}; + my $system = create_buildsystem_instance($name, 1, %opts); + if ($system->IS_GENERATOR_BUILD_SYSTEM) { + $genbuildsystems{$name} = 1; + for my $target_name ($system->SUPPORTED_TARGET_BUILD_SYSTEMS) { + my $full_name = "${name}+${target_name}"; + my $full_system = create_buildsystem_instance($name, 1, %opts, + 'targetbuildsystem' => $target_name); + $buildsystems{$full_name} = $full_system; + } + } else { + $buildsystems{$name} = $system; + } + } + } + } + + # Standard debhelper build systems first + foreach my $name (@BUILDSYSTEMS) { + error("standard debhelper build system '$name' could not be found/loaded") + if not exists $buildsystems{$name}; + push @buildsystems, $buildsystems{$name}; + delete $buildsystems{$name}; + } + + foreach my $name (@THIRD_PARTY_BUILDSYSTEMS) { + next if not exists $buildsystems{$name}; + my $inst = $buildsystems{$name}; + $inst->{thirdparty} = 1; + push(@buildsystems, $inst); + delete($buildsystems{$name}); + } + + # The rest are 3rd party build systems + foreach my $name (sort(keys(%buildsystems))) { + my $inst = $buildsystems{$name}; + $inst->{thirdparty} = 1; + push @buildsystems, $inst; + } + + return @buildsystems; +} + +sub buildsystems_init { + my %args=@_; + + # Compat 10 defaults to --parallel by default + my $max_parallel = compat(9) ? 1 : -1; + + # Available command line options + my %options = ( + "D=s" => \$opt_sourcedir, + "sourcedir=s" => \$opt_sourcedir, + "sourcedirectory=s" => \$opt_sourcedir, + + "B:s" => \$opt_builddir, + "builddir:s" => \$opt_builddir, + "builddirectory:s" => \$opt_builddir, + + "S=s" => \$opt_buildsys, + "buildsystem=s" => \$opt_buildsys, + + "l" => \$opt_list, + "list" => \$opt_list, + + "parallel" => sub { $max_parallel = -1 }, + 'no-parallel' => sub { $max_parallel = 1 }, + "max-parallel=i" => \$max_parallel, + + 'reload-all-buildenv-variables' => sub { + Debian::Debhelper::Dh_Lib::reset_buildflags(); + }, + ); + if (compat(8)) { + # This option only works in compat 9+ where we actually set buildflags + $options{'reload-all-buildenv-variables'} = sub { + die("--reload-all-buildenv-variables only work reliably in compat 9+.\n"); + }; + } + $args{options}{$_} = $options{$_} foreach keys(%options); + Debian::Debhelper::Dh_Lib::init(%args); + Debian::Debhelper::Dh_Lib::setup_buildenv(); + set_parallel($max_parallel); +} + +sub set_parallel { + my $max=shift; + + # Get number of processes from parallel=n option, limiting it + # with $max if needed + $opt_parallel=get_buildoption("parallel") || 1; + + if ($max > 0 && $opt_parallel > $max) { + $opt_parallel = $max; + } +} + +sub buildsystems_list { + my $step=shift; + + my @buildsystems = load_all_buildsystems(); + my %auto_selectable = map { $_ => 1 } @THIRD_PARTY_BUILDSYSTEMS; + my $auto = autoselect_buildsystem($step, grep { ! $_->{thirdparty} || $auto_selectable{$_->NAME} } @buildsystems); + my $specified_text; + + if ($opt_buildsys) { + for my $inst (@buildsystems) { + my $full_name = $inst->NAME; + if ($full_name eq $opt_buildsys) { + $specified_text = $full_name; + } elsif ($inst->IS_GENERATOR_BUILD_SYSTEM and ref($inst)->NAME eq $opt_buildsys) { + my $default = $inst->DEFAULT_TARGET_BUILD_SYSTEM; + $specified_text = "${opt_buildsys}+${default} (default for ${opt_buildsys})"; + } + } + } + + # List build systems (including auto and specified status) + foreach my $inst (@buildsystems) { + printf("%-20s %s", $inst->NAME(), $inst->FULL_DESCRIPTION()); + print " [3rd party]" if $inst->{thirdparty}; + print "\n"; + } + print "\n"; + print "Auto-selected: ", $auto->NAME(), "\n" if defined $auto; + print "Specified: ", $specified_text, "\n" if defined $specified_text; + print "No system auto-selected or specified\n" + if ! defined $auto && ! defined $specified_text; +} + +sub buildsystems_do { + my $step=shift; + + if (!defined $step) { + $step = basename($0); + $step =~ s/^dh_auto_//; + } + + if (grep(/^\Q$step\E$/, BUILD_STEPS) == 0) { + error("unrecognized build step: " . $step); + } + + if ($opt_list) { + buildsystems_list($step); + exit 0; + } + + my $buildsystem = load_buildsystem($opt_buildsys, $step); + if (defined $buildsystem) { + my ($xdg_runtime_dir, $err, $ref); + local $SIG{'INT'} = sub { $ref = 'INT'; die(\$ref); }; + local $SIG{'TERM'} = sub { $ref = 'TERM'; die(\$ref); }; + if ($step eq 'test' and not compat(12)) { + require File::Temp; + $xdg_runtime_dir = File::Temp->newdir('dh-xdg-rundir-XXXXXXXX', + TMPDIR => 1, + CLEANUP => 1, + ); + chmod(0700, $xdg_runtime_dir->dirname) or error("chmod(0700, $xdg_runtime_dir) failed: $!"); + $ENV{'XDG_RUNTIME_DIR'} = $xdg_runtime_dir->dirname; + } + eval { + $buildsystem->pre_building_step($step); + $buildsystem->$step(@_, @{$dh{U_PARAMS}}); + $buildsystem->post_building_step($step); + }; + $err = $@; + doit('rm', '-fr', '--', $xdg_runtime_dir) if $xdg_runtime_dir; + if ($err) { + my $sig; + die($err) if $err ne \$ref; + $sig = $ref; + delete($SIG{$sig}); + kill($sig => $$); + } + } + return 0; +} + +1 |