summaryrefslogtreecommitdiff
path: root/Debian
diff options
context:
space:
mode:
authorModestas Vainius <modestas@vainius.eu>2009-03-23 21:32:43 +0200
committerJoey Hess <joey@gnu.kitenet.net>2009-04-10 16:03:38 -0400
commit51dea74baba625b8d63bbf7e19ad7e069d05ab14 (patch)
tree22553bf3688b23cf1e7efee4b6f26fddee2050c6 /Debian
parent93cd875ba2e3ac9eda6adc31b4334d43ef718ea0 (diff)
Modular object-orientied buildsystem implementation.
Dh_Buildsystems: A manager module for buildsystem "plugins". It deals with the following tasks: * Handles common command line and environment options. As currently implemented by the patch they are: - DH_AUTO_BUILDSYSTEM envvar, -m/--build-system - disables autoguessing of the build system and allows the user to specify which one to use. - DH_AUTO_BUILDDIRECTORY envvar, -b/--build-directory - option to enable building outside source if supported by the buildsystem. User can specify the build directory name or let it be autogenerated (currently "obj-`dpkg_architecture('DEB_BUILD_GNU_TYPE')`" as per CDBS convention). Outside source building has an advantage of avoiding sourcedir pollution which the clean routine cannot deal with properly (at least common in cmake or autotools case). The "clean" is simple in such a case - just rm -rf builddir. - -l/--list - lists all buildsystems known to Dh_Buildsystems along with their descriptions. * Manages buildsystem plugins: - provides a way to list them and collect information about them. - provides a way to force loading & use of a specific buildsystem. - determines which build system is applicable to the source in question using common API (::is_buildable() method) exposed by each build system plugin. * @BUILDSYSTEMS variable contains all buildsystems known to the manager in the order of specialization. ----------------------------- ----------------------------- Dh_Buildsystem_Bases.pm: Contains a few classes which define a common interface for buildsystem plugins and implements handling of common features (i.e. two types of the build directory support, see below). Each specific build system plugin is supposed to inherit from any of these base classes or from another build system plugin. Currently implemented classes (packages) inside this .pm are: -- Dh_Buildsystem_Basic -- a basic class describing buildsystem plugin API. It stores build directory internally (can be retrieved with ::get_builddir() or path constructed using ::get_buildpath() (useful in is_buildable())) but does nothing with it. This class is intended to be inherited by the build system plugins which do not support outside-source tree building or there is no way to control this option (as far as tell, Build.PL is like this). It also describes common buildsystem plugin API and lays down the basic architecture: * ::configure/::build/::test/::install/::clean methods - they will be called to perform a respective action. These are wrapper methods by default and provide a place to implement common features specific the action itself (like creating build directory, see Dh_Buildsystem_Chdir::configure()) before calling real buildsystem specific implementation. Default implementations call the respective *_impl() method via another invoke_impl() wrapper. * ::configure_impl/::build_impl/::test_impl/::install_impl/::clean-impl methods - placeholders for the buildsystem specific implementation of the action (by overriding the methods as needed). Default implementations do nothing. * ::invoke_impl($method_name, @args) - a convenient way to hook in the code which needs to be run before or after respective ::*_impl() of *each* action (e.g. a simple case like setting envvar, see perl_build.pm). Default implementation calls $self->$method_name(@args) by default. So we have such a chain by default (and each can be overriden by any derived class): $self->$action() calls: $self->invoke_impl("${action}_impl", @_) calls: $self->$action_impl(@_) <- does buildsystem specific stuff here; -- Dh_Buildsystem_Option -- extends Dh_Buildsystem_Basic and adds support for passing build directory name via command line option to the build script (specific plugins should override ::get_builddir_option() method). ::invoke_impl() is overriden to pass value of $self->get_builddir_option() to each ::$action_impl() method (python distutils use such a way to set "build place", i.e. --build-place=builddir, see python_distutils.pm). -- Dh_Buildsystem_Chdir -- extends Dh_Buildsystem_Option. This class implements support for outside source building when you need to chdir to the building directory before building (like e.g. makefile.pm and its derivatives: autotools.pm and cmake.pm). All the code in there deals with chdir'ing/mkdir'ing to the build directory as needed before calling ::$action_impl() and finally going back. This is done by overriding ::invoke_impl() method. ----------------------------- ----------------------------- And finally we have build system specific plugins as Debian/Debhelper/Buildsystem/*.pm. Currently I have implemented 100% functionality of the former dh_auto_* tools inside these plugins + cmake support in the cmake.pm: $ ./dh_auto_configure -l autotools - support for building GNU Autotools based packages. cmake - support for building CMake based packages (outside-source tree only). perl_build - support for building Perl Build.PL based packages (in-source only). perl_makefile - support for building Perl Makefile.PL based packages (in-source only). python_distutils - support for building Python distutils based packages. makefile - support for building Makefile based packages (make && make install). Current plugin inheritance hierarchy is like this: Buildsystem::perl_build -> Dh_Buildsystem_Basic <- Buildsystem::perl_makefile ^ (maybe it should derive from ::perl_build?) | Buildsystem::python_distutils -> Dh_Buildsystem_Option ^ | Dh_Buildsystem_Chdir ^ | Buildsystem::makefile ^ ^ / \ Buildsystem::autotools Buildsystem::cmake Signed-off-by: Modestas Vainius <modestas@vainius.eu>
Diffstat (limited to 'Debian')
-rw-r--r--Debian/Debhelper/Buildsystem/autotools.pm54
-rw-r--r--Debian/Debhelper/Buildsystem/cmake.pm49
-rw-r--r--Debian/Debhelper/Buildsystem/makefile.pm84
-rw-r--r--Debian/Debhelper/Buildsystem/perl_build.pm67
-rw-r--r--Debian/Debhelper/Buildsystem/perl_makefile.pm38
-rw-r--r--Debian/Debhelper/Buildsystem/python_distutils.pm62
-rw-r--r--Debian/Debhelper/Dh_Buildsystem_Bases.pm308
-rw-r--r--Debian/Debhelper/Dh_Buildsystems.pm175
8 files changed, 837 insertions, 0 deletions
diff --git a/Debian/Debhelper/Buildsystem/autotools.pm b/Debian/Debhelper/Buildsystem/autotools.pm
new file mode 100644
index 0000000..65694b3
--- /dev/null
+++ b/Debian/Debhelper/Buildsystem/autotools.pm
@@ -0,0 +1,54 @@
+# A buildsystem plugin for handling autotools based projects
+#
+# Copyright: © 2008 Joey Hess
+# © 2008-2009 Modestas Vainius
+# License: GPL-2+
+
+package Debian::Debhelper::Buildsystem::autotools;
+
+use strict;
+use File::Spec;
+use Debian::Debhelper::Dh_Lib;
+use base 'Debian::Debhelper::Buildsystem::makefile';
+
+sub DESCRIPTION {
+ "support for building GNU Autotools based packages"
+}
+
+sub is_buildable {
+ my $self=shift;
+ my ($action) = @_;
+ if ($action eq "configure") {
+ return -x "configure";
+ } else {
+ return $self->SUPER::is_buildable(@_);
+ }
+}
+
+sub configure_impl {
+ my $self=shift;
+
+ # Standard set of options for configure.
+ my @opts;
+ push @opts, "--build=" . dpkg_architecture_value("DEB_BUILD_GNU_TYPE");
+ push @opts, "--prefix=/usr";
+ push @opts, "--includedir=\${prefix}/include";
+ push @opts, "--mandir=\${prefix}/share/man";
+ push @opts, "--infodir=\${prefix}/share/info";
+ push @opts, "--sysconfdir=/etc";
+ push @opts, "--localstatedir=/var";
+ push @opts, "--libexecdir=\${prefix}/lib/" . $self->exec_in_topdir(\&sourcepackage);
+ push @opts, "--disable-maintainer-mode";
+ push @opts, "--disable-dependency-tracking";
+ # Provide --host only if different from --build, as recommended in
+ # autotools-dev README.Debian: When provided (even if equal) autotools
+ # 2.52+ switches to cross-compiling mode.
+ if (dpkg_architecture_value("DEB_BUILD_GNU_TYPE")
+ ne dpkg_architecture_value("DEB_HOST_GNU_TYPE")) {
+ push @opts, "--host=" . dpkg_architecture_value("DEB_HOST_GNU_TYPE");
+ }
+
+ doit($self->get_toppath("configure"), @opts, @_);
+}
+
+1;
diff --git a/Debian/Debhelper/Buildsystem/cmake.pm b/Debian/Debhelper/Buildsystem/cmake.pm
new file mode 100644
index 0000000..d7504d1
--- /dev/null
+++ b/Debian/Debhelper/Buildsystem/cmake.pm
@@ -0,0 +1,49 @@
+# A buildsystem plugin for handling CMake based projects.
+# It enforces outside-source building.
+#
+# Copyright: © 2008-2009 Modestas Vainius
+# License: GPL-2+
+
+package Debian::Debhelper::Buildsystem::cmake;
+
+use strict;
+use Debian::Debhelper::Dh_Lib;
+use base 'Debian::Debhelper::Buildsystem::makefile';
+
+sub _add_cmake_flag {
+ my ($self, $name, $val) = @_;
+ push @{$self->{cmake_flags}}, "-D$name=$val";
+}
+
+sub DESCRIPTION {
+ "support for building CMake based packages (outside-source tree only)"
+}
+
+sub is_buildable {
+ return -e "CMakeLists.txt";
+}
+
+sub new {
+ my $cls=shift;
+ my $self=$cls->SUPER::new(@_);
+ # Enfore outside-source tree builds.
+ $self->enforce_outside_source_building();
+ $self->{cmake_flags} = [];
+ return $self;
+}
+
+sub configure_impl {
+ my $self=shift;
+
+ # Standard set of cmake flags
+ $self->_add_cmake_flag("CMAKE_INSTALL_PREFIX", "/usr");
+ $self->_add_cmake_flag("CMAKE_C_FLAGS", $ENV{CFLAGS}) if (exists $ENV{CFLAGS});
+ $self->_add_cmake_flag("CMAKE_CXX_FLAGS", $ENV{CXXFLAGS}) if (exists $ENV{CXXFLAGS});
+ $self->_add_cmake_flag("CMAKE_SKIP_RPATH", "ON");
+ $self->_add_cmake_flag("CMAKE_VERBOSE_MAKEFILE", "ON");
+ # TODO: LDFLAGS
+
+ doit("cmake", $self->get_topdir(), @{$self->{cmake_flags}}, @_);
+}
+
+1;
diff --git a/Debian/Debhelper/Buildsystem/makefile.pm b/Debian/Debhelper/Buildsystem/makefile.pm
new file mode 100644
index 0000000..91a6341
--- /dev/null
+++ b/Debian/Debhelper/Buildsystem/makefile.pm
@@ -0,0 +1,84 @@
+# A buildsystem plugin for handling simple Makefile based projects.
+#
+# Copyright: © 2008 Joey Hess
+# © 2008-2009 Modestas Vainius
+# License: GPL-2+
+
+package Debian::Debhelper::Buildsystem::makefile;
+
+use strict;
+use Debian::Debhelper::Dh_Lib;
+use Debian::Debhelper::Dh_Buildsystem_Bases;
+use base 'Debian::Debhelper::Dh_Buildsystem_Chdir';
+
+sub _exists_make_target {
+ my ($cls, $target) = @_;
+ # Use make -n to check to see if the target would do
+ # anything. There's no good way to test if a target exists.
+ my $ret=`$ENV{MAKE} -s -n $target 2>/dev/null`;
+ chomp $ret;
+ return length($ret);
+}
+
+sub _make_first_existing_target {
+ my $cls = shift;
+ my $targets = shift;
+
+ $ENV{MAKE}="make" unless exists $ENV{MAKE};
+ foreach my $target (@$targets) {
+ if ($cls->_exists_make_target($target)) {
+ doit($ENV{MAKE}, $target, @_);
+ return $target;
+ }
+ }
+ return undef;
+}
+
+sub DESCRIPTION {
+ "support for building Makefile based packages (make && make install)"
+}
+
+sub is_buildable {
+ my $self=shift;
+ my ($action) = @_;
+ if (grep /^\Q$action\E$/, qw{build test install clean}) {
+ return -e $self->get_buildpath("Makefile") ||
+ -e $self->get_buildpath("makefile") ||
+ -e $self->get_buildpath("GNUmakefile");
+ } else {
+ return 1;
+ }
+}
+
+sub build_impl {
+ my $self=shift;
+ doit(exists $ENV{MAKE} ? $ENV{MAKE} : "make", @_);
+}
+
+sub test_impl {
+ my $self=shift;
+ $self->_make_first_existing_target(['test', 'check'], @_);
+}
+
+sub install_impl {
+ my $self=shift;
+ my $destdir=shift;
+
+ $ENV{MAKE}="make" unless exists $ENV{MAKE};
+ my @params="DESTDIR=$destdir";
+
+ # Special case for MakeMaker generated Makefiles.
+ if (-e "Makefile" &&
+ system('grep -q "generated automatically by MakeMaker" Makefile') == 0) {
+ push @params, "PREFIX=/usr";
+ }
+
+ $self->_make_first_existing_target(['install'], @params, @_);
+}
+
+sub clean_impl {
+ my $self=shift;
+ $self->_make_first_existing_target(['distclean', 'realclean', 'clean'], @_);
+}
+
+1;
diff --git a/Debian/Debhelper/Buildsystem/perl_build.pm b/Debian/Debhelper/Buildsystem/perl_build.pm
new file mode 100644
index 0000000..74106d9
--- /dev/null
+++ b/Debian/Debhelper/Buildsystem/perl_build.pm
@@ -0,0 +1,67 @@
+# A buildsystem plugin for handling Perl Build based projects.
+#
+# Copyright: © 2008-2009 Joey Hess
+# © 2008-2009 Modestas Vainius
+# License: GPL-2+
+
+package Debian::Debhelper::Buildsystem::perl_build;
+
+use strict;
+use Debian::Debhelper::Dh_Lib;
+use Debian::Debhelper::Dh_Buildsystem_Bases;
+use base 'Debian::Debhelper::Dh_Buildsystem_Basic';
+
+sub DESCRIPTION {
+ "support for building Perl Build.PL based packages (in-source only)"
+}
+
+sub is_buildable {
+ my ($self, $action) = @_;
+ my $ret = (-e "Build.PL");
+ if ($action ne "configure") {
+ $ret &= (-e "Build");
+ }
+ return $ret;
+}
+
+sub invoke_impl {
+ my $self=shift;
+ $ENV{MODULEBUILDRC} = "/dev/null";
+ return $self->SUPER::invoke_impl(@_);
+}
+
+sub new {
+ my $cls=shift;
+ my $self= $cls->SUPER::new(@_);
+ $self->enforce_in_source_building();
+ return $self;
+}
+
+sub configure_impl {
+ my $self=shift;
+ $ENV{PERL_MM_USE_DEFAULT}=1; # Module::Build can also use this.
+ doit("perl", "Build.PL", "installdirs=vendor", @_);
+}
+
+sub build_impl {
+ my $self=shift;
+ doit("perl", "Build", @_);
+}
+
+sub test_impl {
+ my $self=shift;
+ doit(qw/perl Build test/, @_);
+}
+
+sub install_impl {
+ my $self=shift;
+ my $destdir=shift;
+ doit("perl", "Build", "install", "destdir=$destdir", "create_packlist=0", @_);
+}
+
+sub clean_impl {
+ my $self=shift;
+ doit("perl", "Build", "--allow_mb_mismatch", 1, "distclean", @_);
+}
+
+1;
diff --git a/Debian/Debhelper/Buildsystem/perl_makefile.pm b/Debian/Debhelper/Buildsystem/perl_makefile.pm
new file mode 100644
index 0000000..67a6f44
--- /dev/null
+++ b/Debian/Debhelper/Buildsystem/perl_makefile.pm
@@ -0,0 +1,38 @@
+# A buildsystem plugin for handling Perl Build based projects.
+#
+# Copyright: © 2008-2009 Joey Hess
+# © 2008-2009 Modestas Vainius
+# License: GPL-2+
+
+package Debian::Debhelper::Buildsystem::perl_makefile;
+
+use strict;
+use Debian::Debhelper::Dh_Lib;
+use Debian::Debhelper::Dh_Buildsystem_Bases;
+use base 'Debian::Debhelper::Dh_Buildsystem_Basic';
+
+sub DESCRIPTION {
+ "support for building Perl Makefile.PL based packages (in-source only)"
+}
+
+sub is_buildable {
+ my ($self, $action) = @_;
+ return ($action eq "configure") && (-e "Makefile.PL");
+}
+
+sub new {
+ my $cls=shift;
+ my $self=$cls->SUPER::new(@_);
+ $self->enforce_in_source_building();
+ return $self;
+}
+
+sub configure_impl {
+ my $self=shift;
+ # If set to a true value then MakeMaker's prompt function will
+ # # always return the default without waiting for user input.
+ $ENV{PERL_MM_USE_DEFAULT}=1;
+ doit("perl", "Makefile.PL", "INSTALLDIRS=vendor", @_);
+}
+
+1;
diff --git a/Debian/Debhelper/Buildsystem/python_distutils.pm b/Debian/Debhelper/Buildsystem/python_distutils.pm
new file mode 100644
index 0000000..2a6df37
--- /dev/null
+++ b/Debian/Debhelper/Buildsystem/python_distutils.pm
@@ -0,0 +1,62 @@
+# A buildsystem plugin for building Python Distutils based
+# projects.
+#
+# Copyright: © 2008 Joey Hess
+# © 2008-2009 Modestas Vainius
+# License: GPL-2+
+
+package Debian::Debhelper::Buildsystem::python_distutils;
+
+use strict;
+use Debian::Debhelper::Dh_Lib;
+use Debian::Debhelper::Dh_Buildsystem_Bases;
+use base 'Debian::Debhelper::Dh_Buildsystem_Option';
+
+sub DESCRIPTION {
+ "support for building Python distutils based packages"
+}
+
+sub is_buildable {
+ return -e "setup.py";
+}
+
+sub get_builddir_option {
+ my $self=shift;
+ if ($self->get_builddir()) {
+ return "--build-base=". $self->get_builddir();
+ }
+ return;
+}
+
+sub configure_impl {
+ # Do nothing
+ 1;
+}
+
+sub build_impl {
+ my $self=shift;
+ doit("python", "setup.py", "build", @_);
+}
+
+sub test_impl {
+ 1;
+}
+
+sub install_impl {
+ my $self=shift;
+ my $destdir=shift;
+
+ doit("python", "setup.py", "install",
+ "--root=$destdir",
+ "--no-compile", "-O0", @_);
+}
+
+sub clean_impl {
+ my $self=shift;
+ doit("python", "setup.py", "clean", "-a", @_);
+ # The setup.py might import files, leading to python creating pyc
+ # files.
+ doit('find', '.', '-name', '*.pyc', '-exec', 'rm', '{}', ';');
+}
+
+1;
diff --git a/Debian/Debhelper/Dh_Buildsystem_Bases.pm b/Debian/Debhelper/Dh_Buildsystem_Bases.pm
new file mode 100644
index 0000000..ab24829
--- /dev/null
+++ b/Debian/Debhelper/Dh_Buildsystem_Bases.pm
@@ -0,0 +1,308 @@
+# Defines base debhelper buildsystem class interface.
+#
+# Copyright: © 2008-2009 Modestas Vainius
+# License: GPL-2+
+
+package Debian::Debhelper::Dh_Buildsystem_Basic;
+
+use Cwd;
+use File::Spec;
+use Debian::Debhelper::Dh_Lib;
+
+# Build system name. Defaults to the last component of the package
+# name. Do not override this method unless you know what you are
+# doing.
+sub NAME {
+ my $self = shift;
+ my $cls = ref($self) || $self;
+ return ($cls =~ m/^.+::([^:]+)$/) ? $1 : "[invalid package name]";
+}
+
+# Description of the build system to be shown to the users.
+sub DESCRIPTION {
+ "basic debhelper build system class";
+}
+
+sub new {
+ my ($cls, $builddir) = @_;
+ my $self = bless({ builddir => $builddir }, $cls);
+ if (!defined($builddir) || $builddir eq ".") {
+ $self->{builddir} = undef;
+ }
+ return $self;
+}
+
+# This instance method is called to check if the build system is capable
+# to build a source package. Additional argument $action describes which
+# operation the caller is going to perform first (either configure,
+# build, test, install or clean). You must override this method for the
+# build system module to be ever picked up automatically.
+#
+# This method is supposed to be called with source root directory being
+# working directory. Use $self->get_buildpath($path) method to get full
+# path to the files in the build directory.
+sub is_buildable {
+ my $self=shift;
+ my ($action) = @_;
+ return 0;
+}
+
+# Derived class can call this method in its constructor
+# to enforce in-source building even if the user
+# requested otherwise.
+sub enforce_in_source_building {
+ my $self=shift;
+ if ($self->{builddir}) {
+ warning("warning: ".$self->NAME()." buildsystem does not support building outside-source. In-source build enforced.");
+ $self->{builddir} = undef;
+ }
+}
+
+sub get_builddir {
+ my $self=shift;
+ return $self->{builddir};
+}
+
+sub get_buildpath {
+ my ($self, $path) = @_;
+ if ($self->get_builddir()) {
+ return File::Spec->catfile($self->get_builddir(), $path);
+ }
+ else {
+ return File::Spec->catfile('.', $path);
+ }
+}
+
+sub invoke_impl {
+ my $self=shift;
+ my $method=shift;
+
+ return $self->$method(@_);
+}
+
+# The instance methods below provide support for configuring,
+# building, testing, install and cleaning source packages.
+# These methods are wrappers around respective *_impl() methods
+# which are supposed to do real buildsystem specific work.
+
+sub configure {
+ my $self=shift;
+ return $self->invoke_impl('configure_impl', @_);
+}
+
+sub build {
+ my $self=shift;
+ return $self->invoke_impl('build_impl', @_);
+}
+
+sub test {
+ my $self=shift;
+ return $self->invoke_impl('test_impl', @_);
+}
+
+sub install {
+ my $self=shift;
+ return $self->invoke_impl('install_impl', @_);
+}
+
+sub clean {
+ my $self=shift;
+ return $self->invoke_impl('clean_impl', @_);
+}
+
+# The instance methods below should be overriden by derived classes
+# to implement buildsystem specific actions needed to build the
+# source. Arbitary number of custom action arguments might be passed.
+# Default implementations do nothing.
+
+sub configure_impl {
+ my $self=shift;
+ 1;
+}
+
+sub build_impl {
+ my $self=shift;
+ 1;
+}
+
+sub test_impl {
+ my $self=shift;
+ 1;
+}
+
+# destdir parameter specifies where to install files.
+sub install_impl {
+ my $self=shift;
+ my $destdir=shift;
+ 1;
+}
+
+sub clean_impl {
+ my $self=shift;
+ 1;
+}
+
+package Debian::Debhelper::Dh_Buildsystem_Option;
+
+use Debian::Debhelper::Dh_Buildsystems qw( DEFAULT_BUILD_DIRECTORY );
+use base 'Debian::Debhelper::Dh_Buildsystem_Basic';
+
+# Derived class can call this method in its constructor to enforce
+# outside-source building even if the user didn't request it.
+sub enforce_outside_source_building {
+ my ($self, $builddir) = @_;
+ if (!defined $self->{builddir}) {
+ $self->{builddir} = ($builddir && $builddir ne ".") ? $builddir : DEFAULT_BUILD_DIRECTORY;
+ }
+}
+
+# Constructs option to be passed to the source package buildsystem to
+# change build directory. Returns nothing by default.
+sub get_builddir_option {
+ my $self=shift;
+ return;
+}
+
+sub invoke_impl {
+ my $self=shift;
+ my $method=shift;
+
+ if ($self->get_builddir_option()) {
+ return $self->SUPER::invoke_impl($method, $self->get_builddir_option(), @_);
+ }
+ else {
+ return $self->SUPER::invoke_impl($method, @_);
+ }
+}
+
+package Debian::Debhelper::Dh_Buildsystem_Chdir;
+
+use Cwd;
+use File::Spec;
+use Debian::Debhelper::Dh_Lib;
+use base 'Debian::Debhelper::Dh_Buildsystem_Option';
+
+sub new {
+ my $cls=shift;
+ my $self=$cls->SUPER::new(@_);
+ $self->{topdir} = '.';
+ return $self;
+}
+
+sub _cd {
+ my ($cls, $dir) = @_;
+ verbose_print("cd '$dir'");
+ if (! $dh{NO_ACT}) {
+ chdir $dir or error("error: unable to chdir to $dir");
+ }
+}
+
+sub _mkdir {
+ my ($cls, $dir) = @_;
+ if (-e $dir && ! -d $dir) {
+ error("error: unable to create '$dir': object already exists and is not a directory");
+ }
+ else {
+ verbose_print("mkdir '$dir'");
+ if (! $dh{NO_ACT}) {
+ mkdir($dir, 0755) or error("error: unable to create '$dir': $!");
+ }
+ return 1;
+ }
+ return 0;
+}
+
+sub get_builddir {
+ my $self=shift;
+ if (defined $self->{builddir} && $self->{topdir} ne ".") {
+ return File::Spec->catfile($self->{topdir}, $self->{builddir});
+ }
+ return $self->SUPER::get_builddir();
+}
+
+sub get_topdir {
+ my $self=shift;
+ if ($self->{topdir} ne ".") {
+ return File::Spec->abs2rel($self->{topdir});
+ }
+ return $self->{topdir};
+}
+
+sub get_toppath {
+ my ($self, $path) = @_;
+ return File::Spec->catfile($self->get_topdir(), $path);
+}
+
+sub cd {
+ my $self = shift;
+ if ($self->get_builddir() && $self->{topdir} ne ".") {
+ $self->_cd($self->get_topdir());
+ $self->{topdir} = ".";
+ return 1;
+ }
+ return 0;
+}
+
+sub cd_to_builddir {
+ my $self = shift;
+ if ($self->get_builddir() && $self->{topdir} eq ".") {
+ $self->{topdir} = getcwd();
+ $self->_cd($self->get_builddir());
+ return 1;
+ }
+ return 0;
+}
+
+sub exec_in_topdir {
+ my $self=shift;
+ my $sub=shift;
+ my $ret;
+
+ if ($self->get_topdir() ne ".") {
+ $self->cd();
+ $ret = &$sub(@_);
+ $self->cd_to_builddir();
+ }
+ else {
+ $ret = &$sub(@_);
+ }
+ return $ret;
+}
+
+# *_impl() is run with current working directory changed to the
+# build directory if requested.
+sub invoke_impl {
+ my $self=shift;
+ my $method=shift;
+ my $ret;
+
+ $self->cd_to_builddir();
+ $ret = $self->$method(@_);
+ $self->cd();
+ return $ret;
+}
+
+sub configure {
+ my $self=shift;
+ if ($self->get_builddir()) {
+ $self->_mkdir($self->get_builddir());
+ }
+ return $self->SUPER::configure(@_);
+}
+
+# If outside-source tree building is done, whole build directory
+# gets wiped out by default. Otherwise, clean_impl() is called.
+sub clean {
+ my $self=shift;
+ if ($self->get_builddir()) {
+ if (-d $self->get_builddir()) {
+ $self->cd();
+ doit("rm", "-rf", $self->get_builddir());
+ return 1;
+ }
+ } else {
+ return $self->SUPER::clean(@_);
+ }
+}
+
+1;
diff --git a/Debian/Debhelper/Dh_Buildsystems.pm b/Debian/Debhelper/Dh_Buildsystems.pm
new file mode 100644
index 0000000..7e8ca29
--- /dev/null
+++ b/Debian/Debhelper/Dh_Buildsystems.pm
@@ -0,0 +1,175 @@
+# A module for loading and managing debhelper buildsystem plugins.
+#
+# Copyright: © 2009 Modestas Vainius
+# License: GPL-2+
+
+package Debian::Debhelper::Dh_Buildsystems;
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+use Exporter qw( import );
+our @EXPORT_OK = qw( DEFAULT_BUILD_DIRECTORY );
+
+# IMPORTANT: more specific buildsystems should go first
+my @BUILDSYSTEMS = (
+ "autotools",
+ "cmake",
+ "perl_build",
+ "perl_makefile",
+ "python_distutils",
+ "makefile",
+);
+
+sub DEFAULT_BUILD_DIRECTORY {
+ return "obj-" . dpkg_architecture_value("DEB_BUILD_GNU_TYPE");
+}
+
+sub new {
+ my $cls=shift;
+ my %opts=@_;
+ my $self = bless({
+ 'o_dir' => undef,
+ 'o_system' => undef,
+ 'loaded_buildsystems' => [] }, $cls);
+
+ if (!exists $opts{noenv}) {
+ if (exists $ENV{DH_AUTO_BUILDDIRECTORY}) {
+ $self->_set_build_directory_option("env", $ENV{DH_AUTO_BUILDDIRECTORY});
+ }
+ if (exists $ENV{DH_AUTO_BUILDSYSTEM}) {
+ $self->{o_system} = $ENV{DH_AUTO_BUILDSYSTEM};
+ }
+ }
+ return $self;
+}
+
+sub get_options {
+ my $self=shift;
+ my @options=@_;
+
+ my $set_dir = sub { $self->_set_build_directory_option(@_) };
+ my $list_bs = sub { $self->list_buildsystems(@_); exit 0 };
+
+ push @options, (
+ "b:s" => $set_dir,
+ "build-directory:s" => $set_dir,
+ "builddirectory:s" => $set_dir,
+
+ "m=s" => \$self->{o_system},
+ "build-system=s" => \$self->{o_system},
+ "buildsystem=s" => \$self->{o_system},
+
+ "l" => $list_bs,
+ "--list" => $list_bs,
+ );
+ my %options = @options;
+ return \%options;
+}
+
+sub _set_build_directory_option {
+ my ($self, $option, $value) = @_;
+ if (!$value || $value eq "auto") {
+ # Autogenerate build directory name
+ $self->{o_dir} = DEFAULT_BUILD_DIRECTORY;
+ }
+ else {
+ $self->{o_dir} = $value;
+ }
+}
+
+sub _dump_options {
+ my $self=shift;
+ for my $opt (qw(o_dir o_system)) {
+ if (defined $self->{$opt}) {
+ print $opt, ": ", $self->{$opt}, "\n";
+ }
+ }
+}
+
+sub _get_buildsystem_module {
+ my ($self, $system) = @_;
+ my $module = "Debian::Debhelper::Buildsystem::$system";
+
+ if (grep $module, @{$self->{loaded_buildsystems}} == 0) {
+ eval "use $module";
+ if ($@) {
+ error("Unable to load buildsystem '$system': $@");
+ }
+ push @{$self->{loaded_buildsystems}}, $module;
+ }
+ return $module;
+}
+
+sub load_buildsystem {
+ my ($self, $action, $system) = @_;
+
+ if (!defined $system) {
+ $system = $self->{o_system};
+ }
+ if (defined $system) {
+ my $module = $self->_get_buildsystem_module($system);
+ verbose_print("Selected buildsystem (specified): ".$module->NAME());
+ return $module->new($self->{o_dir});
+ }
+ else {
+ # Try to determine build system automatically
+ for $system (@BUILDSYSTEMS) {
+ my $module = $self->_get_buildsystem_module($system);
+ my $inst = $module->new($self->{o_dir});
+ if ($inst->is_buildable($action)) {
+ verbose_print("Selected buildsystem (auto): ".$module->NAME());
+ return $inst;
+ }
+ }
+ }
+ return;
+}
+
+sub load_all_buildsystems {
+ my $self=shift;
+ for my $system (@BUILDSYSTEMS) {
+ $self->_get_buildsystem_module($system);
+ }
+ return @{$self->{loaded_buildsystems}};
+}
+
+sub list_buildsystems {
+ my $self=shift;
+ for my $system ($self->load_all_buildsystems()) {
+ printf("%s - %s.\n", $system->NAME(), $system->DESCRIPTION());
+ }
+}
+
+sub init_dh_auto_tool {
+ my $self=shift;
+
+ Debian::Debhelper::Dh_Lib::init(
+ options => $self->get_options(@_));
+ $self->{initialized}=1;
+}
+
+sub run_dh_auto_tool {
+ my $self=shift;
+ my $toolname = basename($0);
+ my $buildsystem;
+
+ if (!exists $self->{initialized}) {
+ $self->init_dh_auto_tool();
+ }
+
+ # Guess action from the dh_auto_* name
+ $toolname =~ s/^dh_auto_//;
+ if (grep(/^\Q$toolname\E$/, qw{configure build test install clean}) == 0) {
+ error("Unrecognized dh auto tool: ".basename($0));
+ }
+
+ $buildsystem = $self->load_buildsystem($toolname);
+ if (defined $buildsystem) {
+ return $buildsystem->$toolname(@_, @{$dh{U_PARAMS}});
+ }
+ return 0;
+}
+
+1;