[Module::Build] M::B actions design
Status: Beta
Brought to you by:
kwilliams
|
From: Steve P. <sp...@qu...> - 2003-10-16 10:57:51
|
On Thursday, October 16, 2003, at 12:06 am, Randy W. Sims wrote:
> There is another issue here in that the list of actions is growing
> quite large, and M::B is (IMHO) turning into a large monolithic
> module. Would it be worth the effort to re-architect actions, so that
> they are plug-ins (M::B::ACTION::html, M::B::ACTION::dist). This would
> keep M::B from becoming monolithic and would allow others to develop
> plug-ins to expand functionality without requiring invasive code
> changes.
Yes, M::B: is becoming quite monolithic. So was MakeMaker. The main
difference I find between the two is that M::B is much easier to read,
understand and extend.
Splitting out the actions is not a bad idea - I bounced a similar one
around with some friends in the past:
package My::Action;
use base qw( Module::Build::Action );
use constant depends_on => qw( foo, bar );
sub builder { } # the parent M::B obj
sub check_dependencies {
...
}
sub execute {
...
}
There are a few problems with these ideas: how do you write platform
specific code? One answer is to sub-class each action, or write a
method for each platform in the action's class. That could work, but
it could also get a bit hairy.
Then, how do you access commonly used methods? A solution might be to
bung all these methods in a common place that everybody inherits from,
say M::B::Common:
M::B::Common
|
+- M::B::Base
|
+- M::B::Action
|
+- M::B::Action::Foo
And, how do you subclass actions? If I want to write my own 'install'
action, how do I do it? At the moment, I sub-class M::B and do it
there. Sub-classing each action is easy, but how do you get M::B to
use them? One way is to say M::B::Base must have stub methods for each
action so that they can be overridden.
I think they all stem from the fact that M::B was not written with this
design in mind, so it would take a lot of effort to refactor things.
It certainly would involve a *lot* of changes, and little (if any)
backwards compatibility. There is potentially a huge gain as far as
simplicity is concerned, but it may be more work than it's worth.
That said...
> This is only half thought-out, so I'm not sure if it's practical. For
> example, I don't know how easy it would be to come up with an
> interface so that actions could get info from M::B without digging
> into the internals.
Making them plugins is a good idea, but it's even more complicated.
> <thinking out loud>
>
> Each module would contain one or more actions.
I'd limit it to one a package. That way you can get the action name
from the package name itself:
'build' => M::B::Action::Build
> M::B would search for plugin actions at startup, querying each module
> for a list of supported actions.
So M::B builds up a list of actions and their classes? And I assume it
would search particular sub-classes, starting with
Module::Build::Action? Maybe that's how you could do user level
sub-classing: push on your own 'search' package:
@M::B::SearchPackages = qw( My::Action Module::Build::Action );
Then it would find your actions first. If you have 1 action per
package, then M::B already knows what it does. So eventually you build
a map:
$build->{ActionClassMap} = {
Build => 'My::Action::Build'
Install => 'M::B::Action::Install'
};
Then to dispatch, you do something like:
sub dispatch {
my $self = shift;
my $action = shift;
my $aclass = $self->{ActionClassMap}->{$action} || die;
$aclass->new->builder( $self )->check_dependencies->execute;
}
> Each action knows what parameters it accepts and can display its own
> help info. There could be a helper class for generating actions and
> linking them by dependencies, i.e.
>
> my $action = new M::B::ACTION::Builder(
> name => 'My_lib',
> code => sub {},
> ...
> );
Dunno if that's needed - M::B already does it.
> my $top = new M::B::ACTION::Builtin( name => 'all' );
> $top->add_dependency($action);
I'd go with 'M::B::Action::All', it's more consistent.
Food for thought...
-Steve
|