makepp
view release on metacpan or search on metacpan
Mpp/CommandParser.pm view on Meta::CPAN
# $Id: CommandParser.pm,v 1.40 2012/06/09 20:36:20 pfeiffer Exp $
=head1 NAME
Mpp::CommandParser - Base class for makepp command parsers
=head1 SYNOPSIS
use Mpp::CommandParser;
our @ISA = qw/Mpp::CommandParser/;
=head1 DESCRIPTION
C<Mpp::CommandParser> is a base class for makepp(1) command parsers. When used
directly, it does nothing except add a dependency on the executable.
The parser for a particular command should derive from this class.
The parser should instantiate objects of type C<Mpp::Scanner> for
the languageZ<>(s) of the files that it scans for include directives.
=cut
use strict;
package Mpp::CommandParser;
use Mpp::Text ();
use Mpp::Subs ();
use Mpp::File;
# Two overridable methods:
(*add_more_executable_dependencies, *xparse_command) = @Mpp::Text::N;
=head1 METHODS
=head2 new
my $parser=new Mpp::CommandParser($rule, $dir);
Returns a new C<Mpp::CommandParser> object for rule $rule.
$dir is the directory (a string) in which the command runs.
The derived class may override this method and its parameters.
=cut
sub new {
my( $self, $rule, $dir ) = @_;
my $class=ref($self)||$self;
$rule && $dir or die;
UNIVERSAL::isa($rule, 'Mpp::Rule') or die;
UNIVERSAL::isa($dir, 'Mpp::File') and die;
bless {
RULE => $rule,
DIR => $dir,
}, $class;
}
=head2 parse_command
$parser->parse_command($command, $setenv_hash);
Splits the command into words, prints a log message, and calls xparse_command.
=cut
our $ignore_exe; # Emergency override.
my %is_builtin;
@is_builtin{qw(
break cd continue echo exit export false kill let pwd return
set test true ulimit umask unset wait
case esac fi for done
)} = (); # Make these exist
sub parse_command {
my( $self, $command, $setenv_hash ) = @_;
Mpp::log PARSE => $command, $self->dirinfo, $self->rule
if $Mpp::log_level;
my @cmd_words = Mpp::is_windows < 2 ?
Mpp::Text::unquote_split_on_whitespace $command :
map { tr/"//d; $_ } Mpp::Text::split_on_whitespace $command; # Don't unquote \, which is Win dir separator, TODO: \" -> "
unless( $ignore_exe || UNIVERSAL::isa($self->rule->build_check_method, 'Mpp::BuildCheck::ignore_action') ) {
$self->add_executable_dependency( $cmd_words[0] )
unless exists $is_builtin{$cmd_words[0]}
}
$self->xparse_command( \@cmd_words, $setenv_hash );
}
=head2 add_executable_dependency
Gets called before xparse_command with the first command word as the
argument.
By default, it adds a dependency on the file given by the word, but only if it
contains no shell metacharacters. It then adds runtime dependencies, if any,
and calls add_more_executable_dependencies (which the subclass can override)
with the directory name of the executable.
=cut
sub add_executable_dependency {
my( $self, $exe ) = @_;
# Ignore the executable if it has shell metacharacters, because we
# won't easily be able to figure out what it is.
if( $exe !~ m|[^-+=@%^\w:,./]| || Mpp::is_windows > 1 && $exe !~ /[^-+=@%^\w:,.\\]/ ) {
# TODO: This is not the best spot to do this, because the path doesn't get
# rechecked if the command doesn't change. It would be better to do this
# like searching for includes.
if( $exe !~ m@/@ || Mpp::is_windows > 1 && $exe !~ /\\/ ) {
return if $Mpp::no_path_executable_dependencies;
my $CWD_INFO = $CWD_INFO; # Might load a makefile and chdir there:
$exe = Mpp::Subs::f_find_program( $exe, $self->{RULE}{MAKEFILE}, $self->{RULE}{RULE_SOURCE}, 1 );
chdir $CWD_INFO;
return if $exe eq 'not-found';
}
$self->add_optional_dependency($exe);
$self->add_optional_dependency("$exe.exe")
if Mpp::is_windows && $exe !~ /\.exe$/;
my $dirinfo = $self->dirinfo;
my $finfo = file_info($exe, $dirinfo);
my @runtime_deps = values %{$finfo->{RUNTIME_DEPS} || {}};
my %visited = map { sprintf( '%x', $_ ), 1 } @runtime_deps;
while(@runtime_deps) {
my $runtime_dep = shift @runtime_deps;
$self->add_optional_dependency( relative_filename $runtime_dep, $dirinfo );
$visited{sprintf '%x', $_}++ or push @runtime_deps, $_
for values %{$runtime_dep->{RUNTIME_DEPS} || {}};
}
$self->add_more_executable_dependencies($1)
if $exe =~ m@^(.+)/[^/]+$@ || Mpp::is_windows > 1 && $exe =~ /^(.+)\\[^\\]+$/;
}
}
=head2 input_filename_regexp
$parser->input_filename_regexp($command[0], [qw(.c .C .cpp .cc .c++)]);
Returns a regular expression against which arguments will match if they
look like ordinary (positional) input files.
This is the common way to pick up custom file suffixes from
register_input_suffix statements.
=cut
sub input_filename_regexp {
my ($self, $firstword, $predefs) = @_;
my $list;
{
no strict 'refs';
my $suffix_hash = \%{$self->rule->{MAKEFILE}{PACKAGE} . '::input_suffix_hash'};
$list = $suffix_hash->{$firstword} || do {
$firstword =~ s@^.*/@@ and $suffix_hash->{$firstword}
};
}
my @list = ($list ? @$list : (), $predefs ? @$predefs : ());
return unless @list;
return '(' . join('|', map { quotemeta } @list) . ')$';
}
=head2 xparse_command
$parser->xparse_command($command_array, $setenv_hash);
The derived class should override this to set the default signature method,
to parse the $command_array command
and to add the implicit targets and dependencies to $self->rule.
$setenv_hash is a hashref indicating the command-line environment settings.
(Whether its values have shell variables expanded is not yet guaranteed.)
The current plan is that a new object of this class is created for each
command, but that might change in the future.
If it does, then this method is responsible for clearing previous command
information, such as the include paths.
A TRUE return value indicates that some meaningful scanner was successfully
employed. An undefined return value is interpreted as a failure. A failure
forces the rule to propagate failure status, without attempting to build the
target.
=cut
=head2 rule
my $rule=$parser->rule;
Returns the rule.
=cut
sub rule { $_[0]->{RULE} }
=head2 dir
my $dir=$parser->dir;
Returns the directory Mpp::File object.
=cut
sub dir { $_[0]->{DIR} }
=head2 add_dependency
=head2 add_optional_dependency
=head2 add_simple_dependency
=head2 add_target
=head2 add_env_dependency
$parser->add_dependency($name);
$parser->add_dependency($finfo, $tag, $src, $incname);
$parser->add_optional_dependency($name, $simple);
$parser->add_simple_dependency($name);
$parser->add_simple_dependency($finfo, $tag, $src, $incname);
$parser->add_target($name);
$parser->add_env_dependency($name);
Add a dependency or target on file $name relative to $self->dir.
Returns the Mpp::File object.
The 5-arg versions add a dependency on $finfo, and record the dependency
based on $tag, $src, and $incname.
$finfo can be undefined, indicating that the file was sought but not found.
The 1-arg versions add a dependency on $name (relative to $parser->dirinfo),
and record the dependency based on undef, undef, and $name relative to
Mpp/CommandParser.pm view on Meta::CPAN
=cut
sub dirinfo {
$_[0]{DIRINFO} ||=
file_info $_[0]{DIR}, $_[0]{RULE}->build_cwd;
}
sub add_dependency {
my $self = shift;
Mpp::Lexer::add_dependency( $self->{DIR}, $self->dirinfo, $self->{RULE}, @_ );
}
sub add_optional_dependency {
my $self = shift;
Mpp::Lexer::add_optional_dependency( $self->{DIR}, $self->dirinfo, $self->{RULE}, @_ );
}
sub add_simple_dependency {
my $self = shift;
Mpp::Lexer::add_simple_dependency( $self->{DIR}, $self->dirinfo, $self->{RULE}, @_ );
}
sub add_target {
my $self = shift;
Mpp::Lexer::add_target( $self->{DIR}, $self->{RULE}, @_ );
}
sub add_env_dependency {
my $self = shift;
Mpp::Lexer::add_env_dependency( $self->{RULE}, @_ );
}
#=head1 FIELDS
#
#The following fields represent the current implementation of this class,
#but they may change without notice.
#Therefore, clients outside this class should using accessor methods instead.
#
#=head2 $self->{RULE}
#
#A reference to the rule.
#
#=head2 $self->{DIR}
#
#The directory under which the present command being parsed will execute.
=head1 BUGS
There is no variant of the add_dependency method that doesn't add a
dependency if the file doesn't exist and can't be built.
(For example, an optional configuration file.)
This would be easy to implement, but there aren't any scanners that
would use it right now.
=head1 FUTURE WORK
Dependencies on the environment should also be captured by xparse_command.
I don't know if there is a way to do this at all yet, but in principle it
could be added.
The $setenv_hash parameter can be used to avoid adding dependencies on
variables that are set earlier in the action.
For search paths picked up from the environment, it would be a lot more
efficient to capture dependencies in the form
"rebuild unless file X is picked up from directory Y,"
rather than "rebuild if the path changed."
Also, if we could avoid rebuilding if the file is picked up from a different
location but has the same MD5, then that would be cool.
Perhaps there should be a finfo method that just returns
file_info($_[0], $self->dirinfo).
=cut
1;
( run in 0.660 second using v1.01-cache-2.11-cpan-39bf76dae61 )