From 683f6060d8304d6d4e83bd76e5ac624a35b43442 Mon Sep 17 00:00:00 2001 From: Modestas Vainius Date: Tue, 14 Apr 2009 15:12:14 +0300 Subject: Modular object-orientied buildsystem implementation (try 2). Major changess: * Dh_Buildsystem_Option dropped and Dh_Buildsystem_Chdir functionality partitially merged into Dh_Buildsystem_Basic. Dh_Buildsystem_Bases.pm renamed to Dh_Buildsystem_Basic.pm to match classname. * *_impl() ditched completely. Previous {configure,build,test,install,clean}_impl() renamed to just configure(), build(), test(), install(), clean() instead. Added pre_action($action) and post_action($action) hooks instead which are called by Dh_Buildsystems::buildsystems_do(). * Builddir is handled via mkdir_builddir(), doit_in_buildddir(), clean_builddir() methods which buildsystems should call directly. Removed get_top* method, added get_rel2builddir_path(). * is_buildable() method renamed to is_auto_buildable() to reflect its purpose more. * ::perl_makefile renamed to ::perl_makemaker and which is based on ::makefile now. MakeMaker hack moved from ::makefile to ::perl_makemaker where it belongs (thanks for the tip). * Dh_Buildsystems refactored into a simple perl module rather than OO class and simplified a bit. * @BUILDSYSTEMS and is_auto_buildable() modified to 100% match historical order. TODO: user documentation (e.g. DH_AUTO_BUILDDIRECTORY and DH_AUTO_BUILDSYSTEM environment variables and common dh_auto_* options (--buildsystem and --builddirectory)). Current plugin inheritance hierarchy is like this: Buildsystem::perl_build -> Dh_Buildsystem_Basic <- Buildsystem::python_distutils ^ | Buildsystem::makefile <- Buildsystem::perl_makemaker ^ ^ ^ / | \ Buildsystem::autotools Buildsystem::cmake Buildsystem::python_distutils Signed-off-by: Modestas Vainius --- Debian/Debhelper/Buildsystem/autotools.pm | 27 +- Debian/Debhelper/Buildsystem/cmake.pm | 44 ++- Debian/Debhelper/Buildsystem/makefile.pm | 92 +++--- Debian/Debhelper/Buildsystem/perl_build.pm | 37 ++- Debian/Debhelper/Buildsystem/perl_makefile.pm | 38 --- Debian/Debhelper/Buildsystem/perl_makemaker.pm | 71 +++++ Debian/Debhelper/Buildsystem/python_distutils.pm | 49 ++-- Debian/Debhelper/Dh_Buildsystem_Bases.pm | 352 ----------------------- Debian/Debhelper/Dh_Buildsystem_Basic.pm | 277 ++++++++++++++++++ Debian/Debhelper/Dh_Buildsystems.pm | 216 ++++++-------- 10 files changed, 558 insertions(+), 645 deletions(-) delete mode 100644 Debian/Debhelper/Buildsystem/perl_makefile.pm create mode 100644 Debian/Debhelper/Buildsystem/perl_makemaker.pm delete mode 100644 Debian/Debhelper/Dh_Buildsystem_Bases.pm create mode 100644 Debian/Debhelper/Dh_Buildsystem_Basic.pm (limited to 'Debian') diff --git a/Debian/Debhelper/Buildsystem/autotools.pm b/Debian/Debhelper/Buildsystem/autotools.pm index 945ca408..1ebc9384 100644 --- a/Debian/Debhelper/Buildsystem/autotools.pm +++ b/Debian/Debhelper/Buildsystem/autotools.pm @@ -15,17 +15,18 @@ sub DESCRIPTION { "support for building GNU Autotools based packages" } -sub is_buildable { +sub is_auto_buildable { my $self=shift; - my ($action) = @_; + my $action=shift; + + # Handle configure; the rest - next class if ($action eq "configure") { return -x "configure"; - } else { - return $self->SUPER::is_buildable(@_); } + return 0; } -sub configure_impl { +sub configure { my $self=shift; # Standard set of options for configure. @@ -37,17 +38,7 @@ sub configure_impl { push @opts, "--infodir=\${prefix}/share/info"; push @opts, "--sysconfdir=/etc"; push @opts, "--localstatedir=/var"; - # XXX JEH this is where the sheer evil of Dh_Buildsystem_Chdir - # becomes evident. Why is exec_in_topdir needed here? - # Because: - # - The parent class happens to be derived from Dh_Buildsystem_Chdir. - # - sourcepage() happens to, like many other parts of debhelper's - # library, assume it's being run in the top of the source tree, - # and fails if it's not. - # Having to worry about interactions like that for every line of - # every derived method is simply not acceptable. - # Dh_Buildsystem_Chdir must die! -- JEH - push @opts, "--libexecdir=\${prefix}/lib/" . $self->exec_in_topdir(\&sourcepackage); + push @opts, "--libexecdir=\${prefix}/lib/" . sourcepackage(); push @opts, "--disable-maintainer-mode"; push @opts, "--disable-dependency-tracking"; # Provide --host only if different from --build, as recommended in @@ -62,7 +53,9 @@ sub configure_impl { # but does not need to in the is_buildable method is not clear, # unless one is familiar with the implementation of its parent # class. I think that speaks to a bad design.. - doit($self->get_toppath("configure"), @opts, @_); + # XXX MDX It should be more explicit now. + $self->mkdir_builddir(); + $self->doit_in_builddir($self->get_rel2builddir_path("configure"), @opts, @_); } 1; diff --git a/Debian/Debhelper/Buildsystem/cmake.pm b/Debian/Debhelper/Buildsystem/cmake.pm index 00f6be4d..026004a0 100644 --- a/Debian/Debhelper/Buildsystem/cmake.pm +++ b/Debian/Debhelper/Buildsystem/cmake.pm @@ -10,45 +10,43 @@ 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 is_auto_buildable { + my $self=shift; + my ($action)=@_; + my $ret = -e "CMakeLists.txt"; + $ret &&= $self->SUPER::is_auto_buildable(@_) if $action ne "configure"; + return $ret; } sub new { my $cls=shift; my $self=$cls->SUPER::new(@_); - # Enfore outside-source tree builds. + # Enforce outside-source tree builds. $self->enforce_outside_source_building(); - $self->{cmake_flags} = []; return $self; } -sub configure_impl { +sub configure { my $self=shift; + my @flags; # 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 - # XXX JEH why are we using a method and an object - # field to build up a simple one-time-use list? - # my @flags; - # push @flags, ... if $foo - - # XXX JEH again a non-sequitor get_topdir. - doit("cmake", $self->get_topdir(), @{$self->{cmake_flags}}, @_); + push @flags, "-DCMAKE_INSTALL_PREFIX=/usr"; + push @flags, "-DCMAKE_C_FLAGS=$ENV{CFLAGS}" if (exists $ENV{CFLAGS}); + push @flags, "-DCMAKE_CXX_FLAGS=$ENV{CXXFLAGS}" if (exists $ENV{CXXFLAGS}); + push @flags, "-DCMAKE_LD_FLAGS=$ENV{LDFLAGS}" if (exists $ENV{LDFLAGS}); + push @flags, "-DCMAKE_SKIP_RPATH=ON"; + push @flags, "-DCMAKE_VERBOSE_MAKEFILE=ON"; + + # XXX JEH again a non-sequitor get_topdir. + # XXX MDX I cannot avoid it as I need to pass the path to the sourcedir + # to cmake which is relative to the builddir. + $self->mkdir_builddir(); + $self->doit_in_builddir("cmake", $self->get_rel2builddir_path(), @flags); } 1; diff --git a/Debian/Debhelper/Buildsystem/makefile.pm b/Debian/Debhelper/Buildsystem/makefile.pm index cbd9e3c3..7ffb048e 100644 --- a/Debian/Debhelper/Buildsystem/makefile.pm +++ b/Debian/Debhelper/Buildsystem/makefile.pm @@ -8,30 +8,41 @@ 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'; +use base 'Debian::Debhelper::Dh_Buildsystem_Basic'; + +# XXX JEH setting this env var is dodgy, +# probably better to test if it exists with a default value. +# (Factor out to helper function?) +# XXX MDX Done. See new(). + +sub get_makecmd_C { + my $self=shift; + if ($self->get_builddir()) { + return $self->{makecmd} . " -C " . $self->get_builddir(); + } + return $self->{makecmd}; +} # XXX JEH I *like* this. Yay for factoring out ugly ugly stuff! -sub _exists_make_target { - my ($cls, $target) = @_; +# XXX MDX TODO: this could use dh debian/rules parser. +sub exists_make_target { + my ($self, $target) = @_; + my $makecmd=$self->get_makecmd_C(); + # 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`; + my $ret=`$makecmd -s -n $target 2>/dev/null`; chomp $ret; return length($ret); } -sub _make_first_existing_target { - my $cls = shift; - my $targets = shift; +sub make_first_existing_target { + my $self=shift; + my $targets=shift; - # XXX JEH setting this env var is dodgy, - # probably better to test if it exists with a default value. - # (Factor out to helper function?) - $ENV{MAKE}="make" unless exists $ENV{MAKE}; foreach my $target (@$targets) { - if ($cls->_exists_make_target($target)) { - doit($ENV{MAKE}, $target, @_); + if ($self->exists_make_target($target)) { + $self->doit_in_builddir($self->{makecmd}, $target, @_); return $target; } } @@ -42,56 +53,49 @@ sub DESCRIPTION { "support for building Makefile based packages (make && make install)" } -sub is_buildable { +sub new { + my $cls=shift; + my $self=$cls->SUPER::new(@_); + $self->{makecmd} = (exists $ENV{MAKE}) ? $ENV{MAKE} : "make"; + return $self; +} + +sub is_auto_buildable { my $self=shift; my ($action) = @_; + + # Handles build, test, install, clean; configure - next class if (grep /^\Q$action\E$/, qw{build test install clean}) { - # XXX JEH why does get_buildpath need to be used - # here? is_buildable is run at the top of the source - # directory, so -e 'Makefile' should be the same + # This is always called in the source directory, but generally + # Makefiles are created (or live) in the the build directory. return -e $self->get_buildpath("Makefile") || -e $self->get_buildpath("makefile") || -e $self->get_buildpath("GNUmakefile"); - } else { - # XXX JEH why return 1 here? - return 1; } + return 0; } -sub build_impl { +sub build { my $self=shift; - doit(exists $ENV{MAKE} ? $ENV{MAKE} : "make", @_); + $self->doit_in_builddir($self->{makecmd}, @_); } -sub test_impl { +sub test { my $self=shift; - $self->_make_first_existing_target(['test', 'check'], @_); + $self->make_first_existing_target(['test', 'check'], @_); } -sub install_impl { +sub install { my $self=shift; my $destdir=shift; - - # XXX JEH again with the setting the env var, see above.. - $ENV{MAKE}="make" unless exists $ENV{MAKE}; - my @params="DESTDIR=$destdir"; - - # Special case for MakeMaker generated Makefiles. - # XXX JEH This is a really unfortunate breaking of the - # encapsulation of the perl_makefile module. Perhaps it would be - # better for that module to contain some hack that injects that - # test into this one? - if (-e "Makefile" && - system('grep -q "generated automatically by MakeMaker" Makefile') == 0) { - push @params, "PREFIX=/usr"; - } - - $self->_make_first_existing_target(['install'], @params, @_); + $self->make_first_existing_target(['install'], "DESTDIR=$destdir", @_); } -sub clean_impl { +sub clean { my $self=shift; - $self->_make_first_existing_target(['distclean', 'realclean', 'clean'], @_); + if (!$self->clean_builddir()) { + $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 index a43d65da..9be71a27 100644 --- a/Debian/Debhelper/Buildsystem/perl_build.pm +++ b/Debian/Debhelper/Buildsystem/perl_build.pm @@ -8,26 +8,27 @@ 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 { +sub is_auto_buildable { my ($self, $action) = @_; - my $ret = (-e "Build.PL"); + + # Handles everything + my $ret = -e "Build.PL"; if ($action ne "configure") { - $ret &= (-e "Build"); + $ret &&= -e "Build"; } return $ret; } -sub invoke_impl { +sub do_perl { my $self=shift; $ENV{MODULEBUILDRC} = "/dev/null"; - return $self->SUPER::invoke_impl(@_); + doit("perl", @_); } sub new { @@ -37,33 +38,31 @@ sub new { return $self; } -sub configure_impl { +sub configure { my $self=shift; - # XXX JEH I think the below comment is inherited from elsewhere; - # doesn't really make sense now. - $ENV{PERL_MM_USE_DEFAULT}=1; # Module::Build can also use this. - doit("perl", "Build.PL", "installdirs=vendor", @_); + $ENV{PERL_MM_USE_DEFAULT}=1; + $self->do_perl("Build.PL", "installdirs=vendor", @_); } -sub build_impl { +sub build { my $self=shift; - doit("perl", "Build", @_); + $self->do_perl("Build", @_); } -sub test_impl { +sub test { my $self=shift; - doit(qw/perl Build test/, @_); + $self->do_perl("Build", "test", @_); } -sub install_impl { +sub install { my $self=shift; my $destdir=shift; - doit("perl", "Build", "install", "destdir=$destdir", "create_packlist=0", @_); + $self->do_perl("Build", "install", "destdir=$destdir", "create_packlist=0", @_); } -sub clean_impl { +sub clean { my $self=shift; - doit("perl", "Build", "--allow_mb_mismatch", 1, "distclean", @_); + $self->do_perl("Build", "--allow_mb_mismatch", 1, "distclean", @_); } 1; diff --git a/Debian/Debhelper/Buildsystem/perl_makefile.pm b/Debian/Debhelper/Buildsystem/perl_makefile.pm deleted file mode 100644 index 67a6f441..00000000 --- a/Debian/Debhelper/Buildsystem/perl_makefile.pm +++ /dev/null @@ -1,38 +0,0 @@ -# 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/perl_makemaker.pm b/Debian/Debhelper/Buildsystem/perl_makemaker.pm new file mode 100644 index 00000000..15539588 --- /dev/null +++ b/Debian/Debhelper/Buildsystem/perl_makemaker.pm @@ -0,0 +1,71 @@ +# A buildsystem plugin for handling Perl MakeMaker based projects. +# +# Copyright: © 2008-2009 Joey Hess +# © 2008-2009 Modestas Vainius +# License: GPL-2+ + +package Debian::Debhelper::Buildsystem::perl_makemaker; + +use strict; +use Debian::Debhelper::Dh_Lib; +use base 'Debian::Debhelper::Buildsystem::makefile'; + +sub DESCRIPTION { + "support for building Perl MakeMaker based packages (in-source only)" +} + +sub is_auto_buildable { + my ($self, $action)=@_; + + # Handles configure, install; the rest - next class + if ($action eq "install") { + # This hack is needed to keep full 100% compatibility with previous + # debhelper versions. + if (-e "Makefile" && + system('grep -q "generated automatically by MakeMaker" Makefile') == 0) { + return 1; + } + } + elsif ($action eq "configure") { + return -e "Makefile.PL"; + } + else { + return 0; + } +} + +sub new { + my $cls=shift; + my $self=$cls->SUPER::new(@_); + $self->enforce_in_source_building(); + return $self; +} + +sub configure { + 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", @_); +} + +sub install { + my $self=shift; + my $destdir=shift; + # XXX JEH This is a really unfortunate breaking of the + # encapsulation of the perl_makefile module. Perhaps it would be + # better for that module to contain some hack that injects that + # test into this one? + # XXX MDX Solved. perl_makemaker will need come before makefile in + # @BUILDSYSTEMS. See also hack in is_auto_buildable(). + # This is a safety check needed to keep 100% compatibility with + # earlier debhelper behaviour. This if is very unlikely to be false. + if (-e "Makefile" && + system('grep -q "generated automatically by MakeMaker" Makefile') == 0) { + $self->SUPER::install($destdir, "PREFIX=/usr", @_); + } else { + $self->SUPER::install($destdir, @_); + } +} + +1; diff --git a/Debian/Debhelper/Buildsystem/python_distutils.pm b/Debian/Debhelper/Buildsystem/python_distutils.pm index 2e7eacbb..a69b36f4 100644 --- a/Debian/Debhelper/Buildsystem/python_distutils.pm +++ b/Debian/Debhelper/Buildsystem/python_distutils.pm @@ -9,54 +9,47 @@ 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'; +use base 'Debian::Debhelper::Dh_Buildsystem_Basic'; sub DESCRIPTION { "support for building Python distutils based packages" } -sub is_buildable { - return -e "setup.py"; +sub is_auto_buildable { + my $self=shift; + my $action=shift; + + # Handle build install clean; the rest - next class + if (grep(/^\Q$action\E$/, qw{build install clean})) { + return -e "setup.py"; + } + return 0; } -sub get_builddir_option { +sub setup_py { my $self=shift; + my $act=shift; + if ($self->get_builddir()) { - return "--build-base=". $self->get_builddir(); + unshift @_, "--build-base=" . $self->get_builddir(); } - return; + doit("python", "setup.py", $act, @_); } -# XXX JEH the default for all these methods is to do nothing successfully. -# So either this, or those default stubs, need to be removed. -sub configure_impl { - # Do nothing - 1; -} - -sub build_impl { +sub build { my $self=shift; - doit("python", "setup.py", "build", @_); -} - -# XXX JEH see anove comment -sub test_impl { - 1; + $self->setup_py("build", @_); } -sub install_impl { +sub install { my $self=shift; my $destdir=shift; - - doit("python", "setup.py", "install", - "--root=$destdir", - "--no-compile", "-O0", @_); + $self->setup_py("install", "--root=$destdir", "--no-compile", "-O0", @_); } -sub clean_impl { +sub clean { my $self=shift; - doit("python", "setup.py", "clean", "-a", @_); + $self->setup_py("clean", "-a", @_); # The setup.py might import files, leading to python creating pyc # files. doit('find', '.', '-name', '*.pyc', '-exec', 'rm', '{}', ';'); diff --git a/Debian/Debhelper/Dh_Buildsystem_Bases.pm b/Debian/Debhelper/Dh_Buildsystem_Bases.pm deleted file mode 100644 index 24164537..00000000 --- a/Debian/Debhelper/Dh_Buildsystem_Bases.pm +++ /dev/null @@ -1,352 +0,0 @@ -# Defines base debhelper buildsystem class interface. -# -# Copyright: © 2008-2009 Modestas Vainius -# License: GPL-2+ - -# XXX JEH file name does not match package name (Bases vs Basic) -# That will cause problems when using 'use' to load the module. -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. -# XXX JEH s/package/class/ in above comment for clarity.. -sub NAME { - my $self = shift; - my $cls = ref($self) || $self; - return ($cls =~ m/^.+::([^:]+)$/) ? $1 : "[invalid package name]"; - # XXX JEH s/package/class/ in above message for clarity.. - # (maybe dying on this unusual error would be better tho?) -} - -# Description of the build system to be shown to the users. -sub DESCRIPTION { - "basic debhelper build system class"; -} - -sub new { - my ($cls, $builddir) = @_; - # XXX JEH probably would be better to use named parameters here - # Also, if the builddir value is not specified, it could use - # DEFAULT_BUILD_DIRECTORY here. Which would allow moving that - # sub from Dh_Buildsystems to here, since it would no longer need - # to be used there. - 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); - } -} - -# XXX JEH this method seems entirely useless. -# $self->invoke_impl('foo', @_); -# $self->foo(@_); -# The second is easier to both read and write. -# -# Turns out that one build system overrides this method, -# perl_build uses it to set an env vatiable before each method -# call. But that's unnecessary; it could just set it in its constructor. -# -# And there's another version of this method in the derifed class below, -# which just adds another parameter to the method call. That is used only -# by the python_distutils build system, to add a --build-base parameter -# to two calls to python. It could be manually added to those two calls -# with less fuss. -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. - -# XXX JEH if invoke_impl is done away with, these can be replaced -# with the bodies of the foo_impl methods they call. That layer of -# indirection is not really needed. - -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. -# XXX JEH I don't see where this destdir parameter is passed in -# to a call to $object->install ? In Dh_Buildsystems it does: -# return $buildsystem->$toolname(@_, @{$dh{U_PARAMS}}); -# Which passes a different parameter, to ALL these methods. -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. -# -# XXX JEH is there a reason for this to be in -# Dh_Buildsystem_Option instead of the base class? -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; -} - -# XXX JEH if the reasoning for removing invoke_impl from above is followed, -# then this whole derived class ends up not being needed. -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; - -# XXX JEH I'm very leery of code that chdirs, it can be very hard to follow -# and cause a lot of mess. (As we'll see in the buildsystem modules that -# use this class.) It seems to me that this entire class could be -# basically replaced by a doit_in_builddir() function. - -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_Buildsystem_Basic.pm b/Debian/Debhelper/Dh_Buildsystem_Basic.pm new file mode 100644 index 00000000..cd4e448f --- /dev/null +++ b/Debian/Debhelper/Dh_Buildsystem_Basic.pm @@ -0,0 +1,277 @@ +# Defines basic debhelper buildsystem class interface. +# +# Copyright: © 2008-2009 Modestas Vainius +# License: GPL-2+ + +package Debian::Debhelper::Dh_Buildsystem_Basic; + +use strict; +use warnings; +use Cwd; +use File::Spec; +use Debian::Debhelper::Dh_Lib; + +# Build system name. Defaults to the last component of the class +# name. Do not override this method unless you know what you are +# doing. +sub NAME { + my $self=shift; + my $cls = ref($self) || $self; + if ($cls =~ m/^.+::([^:]+)$/) { + return $1; + } + else { + error("ınvalid buildsystem class name: $cls"); + } +} + +# Description of the build system to be shown to the users. +sub DESCRIPTION { + "basic debhelper build system class (please provide description)"; +} + +# Default build directory. Can be overriden in the derived +# class if really needed. +sub DEFAULT_BUILD_DIRECTORY { + "obj-" . dpkg_architecture_value("DEB_BUILD_GNU_TYPE"); +} + +# XXX JEH Turns out that one build system overrides this method, +# perl_build uses it to set an env vatiable before each method +# call. But that's unnecessary; it could just set it in its constructor. +# XXX MDX See comment below. is_auto is currently unused but I think it +# is a good addition to the API for future cases. + +# Constructs a new build system object. Named parameters: +# - builddir - specifies build directory to use. If not specified, +# in-source build will be performed. If undef or empty, +# default DEFAULT_BUILD_DIRECTORY will be used. +# - is_auto - might be used by the derived classes to determine if +# the build system has been picked up automatically. +# Derived class can override the constructor to initialize common parameters. +# Constructor SHOULD NOT be used to initialize build environment because +# constructed class may not be eventually used to build the package (if e.g. +# is_auto_buildable() returns 0). +sub new { + my ($cls, %opts)=@_; + + my $self = bless({ builddir => undef, is_auto => $opts{is_auto} }, $cls); + if (exists $opts{builddir}) { + if ($opts{builddir}) { + $self->{builddir} = $opts{builddir}; + } + else { + $self->{builddir} = $self->DEFAULT_BUILD_DIRECTORY(); + } + } + return $self; +} + +# This instance method is called to check if the build system is capable +# to auto build a source package. Additional argument $action describes +# which operation the caller is going to perform (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 +# used in conjuction with @Dh_Buildsystems::BUILDSYSTEMS. +# +# 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_auto_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}) { + # Since this method is called in the constructor, emitting + # warnings immediatelly may cause too much noise when + # scanning for auto buildsystems or listing them. + push @{$self->{warnings}}, + $self->NAME()." buildsystem does not support building outside-source. In-source build enforced."; + $self->{builddir} = undef; + } +} + +# 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 : $self->DEFAULT_BUILD_DIRECTORY(); + } +} + +# Get path to the specified build directory +sub get_builddir { + my $self=shift; + return $self->{builddir}; +} + +# Construct absolute path to the file from the given path that is relative +# to the build directory. +sub get_buildpath { + my ($self, $path) = @_; + if ($self->get_builddir()) { + return File::Spec->catfile($self->get_builddir(), $path); + } + else { + return File::Spec->catfile('.', $path); + } +} + +# When given a relative path in the source tree, converts it +# to the path that is relative to the build directory. +# If $path is not given, returns relative path to the root of the +# source tree from the build directory. +sub get_rel2builddir_path { + my $self=shift; + my $path=shift; + + if (defined $path) { + $path = File::Spec->catfile(Cwd::getcwd(), $path); + } + else { + $path = Cwd::getcwd(); + } + if ($self->get_builddir()) { + return File::Spec->abs2rel($path, Cwd::abs_path($self->get_builddir())); + } + return $path; +} + +# XXX JEH I'm very leery of code that chdirs, it can be very hard to follow +# and cause a lot of mess. (As we'll see in the buildsystem modules that +# use this class.) It seems to me that this entire class could be +# basically replaced by a doit_in_builddir() function. +# XXX MDX implemented. + +sub _mkdir { + my ($cls, $dir)=@_; + if (-e $dir && ! -d $dir) { + error("error: unable to create '$dir': object already exists and is not a directory"); + } + elsif (! -d $dir) { + verbose_print("mkdir '$dir'"); + if (! $dh{NO_ACT}) { + mkdir($dir, 0755) or error("error: unable to create '$dir': $!"); + } + return 1; + } + return 0; +} + +sub _cd { + my ($cls, $dir)=@_; + if (! $dh{NO_ACT}) { + chdir $dir or error("error: unable to chdir to $dir"); + } +} + +# Creates a build directory. Returns 1 if the directory was created +# or 0 if it already exists or there is no need to create it. +sub mkdir_builddir { + my $self=shift; + if ($self->get_builddir()) { + return $self->_mkdir($self->get_builddir()); + } + return 0; +} + +# Changes working directory the build directory (if needed), calls doit(@_) +# and changes working directory back to the source directory. +sub doit_in_builddir { + my $self=shift; + if ($self->get_builddir()) { + my $builddir = $self->get_builddir(); + my $sourcedir = $self->get_rel2builddir_path(); + verbose_print("cd to the build directory: $builddir"); + $self->_cd($builddir); + doit(@_); + verbose_print("cd back to the source directory: $sourcedir"); + $self->_cd($sourcedir); + } + else { + doit(@_); + } + return 1; +} + +# In case of outside-source tree building, whole build directory +# gets wiped (if it exists) and 1 is returned. Otherwise, nothing +# is done and 0 is returned. +sub clean_builddir { + my $self=shift; + if ($self->get_builddir()) { + if (-d $self->get_builddir()) { + doit("rm", "-rf", $self->get_builddir()); + } + return 1; + } + return 0; +} + + +# Instance method that is called before performing any action (see below). +# Action name is passed as an argument. Derived classes overriding this +# method should also call SUPER implementation of it. +sub pre_action { + my $self=shift; + my $action=shift; + + # Emit warnings pre action. + if (exists $self->{warnings}) { + for my $msg (@{$self->{warnings}}) { + warning("warning: " . $msg); + } + } +} + +# Instance method that is called after performing any action (see below). +# Action name is passed as an argument. Derived classes overriding this +# method should also call SUPER implementation of it. +sub post_action { + my $self=shift; + my $action=shift; +} + +# The instance methods below provide support for configuring, +# building, testing, install and cleaning source packages. +# In case of failure, the method may just error() out. +# +# These methods 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 { + my $self=shift; +} + +sub build { + my $self=shift; +} + +sub test { + my $self=shift; +} + +# destdir parameter specifies where to install files. +# XXX JEH I don't see where this destdir parameter is passed in +# to a call to $object->install ? In Dh_Buildsystems it does: +# return $buildsystem->$toolname(@_, @{$dh{U_PARAMS}}); +# Which passes a different parameter, to ALL these methods. +# XXX MDX destdir is used in the dh_auto_install tool. +sub install { + my $self=shift; + my $destdir=shift; +} + +sub clean { + my $self=shift; +} + +1; diff --git a/Debian/Debhelper/Dh_Buildsystems.pm b/Debian/Debhelper/Dh_Buildsystems.pm index aa2dff99..676551b9 100644 --- a/Debian/Debhelper/Dh_Buildsystems.pm +++ b/Debian/Debhelper/Dh_Buildsystems.pm @@ -1,4 +1,5 @@ # A module for loading and managing debhelper buildsystem plugins. +# This module is intended to be used by all dh_auto_* helper commands. # # Copyright: © 2009 Modestas Vainius # License: GPL-2+ @@ -9,124 +10,67 @@ use strict; use warnings; use Debian::Debhelper::Dh_Lib; -use Exporter qw( import ); -our @EXPORT_OK = qw( DEFAULT_BUILD_DIRECTORY ); +use base 'Exporter'; +our @EXPORT=qw(&buildsystems_init &buildsystems_do &load_buildsystem); -# IMPORTANT: more specific buildsystems should go first # XXX JEH as noted, this has to match historical order for back-compat -my @BUILDSYSTEMS = ( +# XXX MDX Current dh_auto_* look like: +# configure: autotools, perl_makemaker, perl_build +# build: makefile, python_distutils, perl_build +# test: makefile, perl_build +# install: makefile (with perl_makermaker) hack, python_distutils, perl_build +# clean: makefile, python_distutils, perl_build +# So historical @BUILDSYSTEMS order (as per autodetection, see +# is_auto_buildable() of the respective classes): +# autotools (+configure; the rest - next class) +# python_distutils (+build +install +clean; the rest - next class) +# perl_makemaker (+configure +install (special hack); the rest - next class) +# makefile (+build +test +install +clean; configure - next class) +# perl_build (handles everything) + +# Historical order must be kept for backwards compatibility. New +# buildsystems MUST be added to the END of the list. +our @BUILDSYSTEMS = ( "autotools", - "cmake", - "perl_build", - "perl_makefile", "python_distutils", + "perl_makemaker", "makefile", + "perl_build", + "cmake", ); -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); - - # XXX JEH AFAICS, these 2 env variables are never used or documented - 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 { - # XXX JEH option argument is not used, would be less confusing to - # not pass extra getopt value in - 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 create_buildsystem_instance { + my $system=shift; + my %bsopts=@_; + my $module = "Debian::Debhelper::Buildsystem::$system"; -# XXX JEH this sub is not used -sub _dump_options { - my $self=shift; - for my $opt (qw(o_dir o_system)) { - if (defined $self->{$opt}) { - print $opt, ": ", $self->{$opt}, "\n"; - } + eval "use $module"; + if ($@) { + error("unable to load buildsystem class '$system': $@"); } -} - -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; + if (!exists $bsopts{builddir} && exists $dh{BUILDDIR}) { + $bsopts{builddir} = $dh{BUILDDIR}; } - return $module; + return $module->new(%bsopts); } sub load_buildsystem { # XXX JEH the $system param is never passed # by any call to this function - my ($self, $action, $system) = @_; - - if (!defined $system) { - $system = $self->{o_system}; - } + # XXX MDX Yes, it was sort of redudant. But see buildsystems_do() now. + my ($action, $system)=@_; if (defined $system) { - my $module = $self->_get_buildsystem_module($system); - verbose_print("Selected buildsystem (specified): ".$module->NAME()); - return $module->new($self->{o_dir}); + my $inst = create_buildsystem_instance($system); + verbose_print("Selected buildsystem (specified): ".$inst->NAME()); + return $inst; } 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()); + my $inst = create_buildsystem_instance($system, is_auto=>1); + if ($inst->is_auto_buildable($action)) { + verbose_print("Selected buildsystem (auto): ". $inst->NAME()); return $inst; } } @@ -134,48 +78,67 @@ sub load_buildsystem { return; } -sub load_all_buildsystems { - my $self=shift; +sub list_buildsystems { for my $system (@BUILDSYSTEMS) { - $self->_get_buildsystem_module($system); + my $inst = create_buildsystem_instance($system); + printf("%s - %s.\n", $inst->NAME(), $inst->DESCRIPTION()); } - 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 buildsystems_init { + my %args=@_; + + # XXX JEH AFAICS, these 2 env variables are never used or documented + # XXX MDX They are used (see below), not documented though. + # TODO: Not documented in the manual pages yet. + # Initialize options from environment variables + if (exists $ENV{DH_AUTO_BUILDDIRECTORY}) { + $dh{BUILDDIR} = $ENV{DH_AUTO_BUILDDIRECTORY}; } -} + if (exists $ENV{DH_AUTO_BUILDSYSTEM}) { + $dh{BUILDSYS} = $ENV{DH_AUTO_BUILDSYSTEM}; + } + + # Available command line options + my $list_bs = sub { list_buildsystems(); exit 0 }; + my $set_builddir = sub { $dh{BUILDDIR} = $_[1] }; + my %options = ( + "b:s" => $set_builddir, + "build-directory:s" => $set_builddir, + "builddirectory:s" => $set_builddir, -sub init_dh_auto_tool { - my $self=shift; + "m=s" => \$dh{BUILDSYS}, + "build-system=s" => \$dh{BUILDSYS}, + "buildsystem=s" => \$dh{BUILDSYS}, - Debian::Debhelper::Dh_Lib::init( - options => $self->get_options(@_)); - $self->{initialized}=1; + "l" => $list_bs, + "--list" => $list_bs, + ); + map { $args{options}{$_} = $options{$_} } keys(%options); + Debian::Debhelper::Dh_Lib::init(%args); } -sub run_dh_auto_tool { - my $self=shift; - my $toolname = basename($0); - my $buildsystem; +sub buildsystems_do { + my $action=shift; - # XXX JEH does this if ever not fire? - if (!exists $self->{initialized}) { - $self->init_dh_auto_tool(); + if (!defined $action) { + $action = basename($0); + $action =~ s/^dh_auto_//; } - # 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)); + # XXX JEH does this if ever not fire? + # XXX MDX See dh_auto_install. I'm removing this anyway + # and making buildsystem_init() call in dh_auto_* mandatory. + + if (grep(/^\Q$action\E$/, qw{configure build test install clean}) == 0) { + error("unrecognized auto action: ".basename($0)); } - $buildsystem = $self->load_buildsystem($toolname); + my $buildsystem = load_buildsystem($action, $dh{BUILDSYS}); if (defined $buildsystem) { - return $buildsystem->$toolname(@_, @{$dh{U_PARAMS}}); + $buildsystem->pre_action($action); + $buildsystem->$action(@_, @{$dh{U_PARAMS}}); + $buildsystem->post_action($action); } return 0; } @@ -186,5 +149,10 @@ sub run_dh_auto_tool { # that parses the command line, loads the specified system, and uses it, # passing it the build directory. It would be both shorter and easier to # understand. +# XXX I refactored this into a module rather than OO class. I do not agree +# about a single sub though as it is more complicated than that and +# I think it is more clear to have the code segmented a bit. See also +# dh_auto_install why both buildsystems_init() and buildsystems_do() +# are needed. 1; -- cgit v1.2.3