Makefile-Parser-0.215/0000755000175000001440000000000011622773367013715 5ustar agentzusersMakefile-Parser-0.215/Changes0000644000175000001440000001462211622773310015201 0ustar agentzusersRevision history for Perl extension Makefile::Parser. 0.215 2011-08-18 * fixed RT #41595: makesimple error: Can't locate object method "last_element" via package "MDOM::Token::Whitespace". thanks raleighr3 and jean. 0.214 2011-08-18 * fixed a bug when dealing with trailing backslashes in variable values. this fixed RT #69319: Error parsing line continuations. thanks Marcelo. 0.213 2011-08-17 * applied the patch from Philip Allison and fixed a bug regarding clearing initial set makefile variables before parsing makefiles. 0.212 2011-08-17 * Updated the author name, author email address, and code repository in the documentation. * Updated the bundled Module::Install to the latest version, 1.01. 0.211 2008-03-16 * Added pointer to Makefile::DOM to Makefile::Parser's POD. 0.210 2008-03-16 * Added support for multiple single-colon rules to Makefile::Parser. * Added support for multiple double-colon rules to Makefile::parser. 0.209 2008-03-12 * Calling external GNU make via "make" rather than "/usr/bin/env make". 0.208 2008-03-10 * Added $VERSION to Makefile::AST and Makefile::AST::Evaluator. * Always calling '/usr/bin/env make' in makesimple. 0.207 2008-03-10 * Added POD for Makefile::AST. * Added POD for Makefile::AST::Evaluator. * Added POD for pgmake-db. 0.206 2008-03-10 * Added POD for Makefile::Parser::GmakeDB. * Added POD for the makesimple script. * Added pointers for the GmakeDB parser to Makefile::Parser's POD. 0.205 2008-03-10 * Removed bogus debugging code from the makesimple script. * Fixed the test suite a bit. 0.204 2008-03-08 * Added missing external dependencies to Makefile.PL 0.203 2008-03-08 * Added File::Slurp to Makefile.PL 0.202 2008-03-08 * Added missing dependencies to Makefile.PL * updated inc/ using the latest Module::Install 0.201 2008-03-08 * Added t/lib from the mdom repos. 0.20 2008-03-07 * added Makefile::Parser::GmakeDB and Makefile::AST 0.17 2007-03-16 * added support for the builtins 'error', 'warning', and 'info' 0.16 2007-03-16 * the parser now removes comments in variable assignments * added support for the following GNU make builtin functions: ** if ** and ** or ** shell ** foreach * removed /r from Parsre.pm * added support for the shell function * degraded the version of Test::More that Makefile::Parser requires 0.15 2007-03-14 * fixed the "too-many-args" issue * added support for the following GNU make makefile builtin functions: ** word ** wordlist ** words ** firstword ** lastword ** dir ** notdir ** suffix ** basename ** addprefix ** join ** wildcard ** realpath ** abspath * added _split_args method to do proper function arugment splitting * refactored the second half of _process_refs out to _solve_refs_in_tokens * merged the code of _check_func_args into _split_args * _split_args now splits func arguments lazily * updated the POD to reflect recent changes 0.14 2007-03-10 * added some more POD to plmake 0.13 2007-03-10 * minor POD fixes: C<< ... >> and SVN repos URL 0.12 2007-03-10 * added the C method to Makefile::Parser * added POD accordingly (deprecated C) * explaned the current status and plans for this module in the POD * checked in a naive "make" command-line utility "plmake" which uses Makefile::Parser * added basic support for commands after ';' in rules * added a second optional argument to the C method so as to pass initial variable settings to the parser * updated plmake to pass %ENV and variables specified on the command-line to the parser * forced to use "/bin/sh" in run_commands * implemented GNU make function 'subst', 'patsubst', 'strip', 'findstring', 'filter', 'filter-out' and 'sort'. * implemented the Substitution References in GNU makefiles, for example, $(objects:.o=.c) * fixed a bug regarding single-letter variable expansion 0.11 10/17/2005 * User-defined variable names are defined as /\w+/ * GNUMake's variable expansion sytax ${FOO} is now supported. Guretz++ * Add support for variable definition sytax FOO := blah blah blah * Expand $@ in commands as expected * Optimize the parser effectively by following Guretz Maxim's enlightening suggestion. Guretz++ 0.10 10/16/2005 * Update the version number to 0.10 * Fix the platform-specific tests, stripped CR from test files, and test both against Cygwin and Win32. Slaven++ 0.09 10/15/2005 * Fix a stupid bug in Parser.pm. The order of the two suffixes is inverted accidentally. * Update the POD document, implementation, and tests accordingly. 0.08 10/10/2005 * Make Makefile::Parser->parse to raise syntax errors under strict mode * Add full support for implicit pattern rules: %.o : %.c $(CC) -c $< The order of the implicit rules is not significant. Whee! * Add more tests for pattern rules, expand $< and $* as expected, and clean up the stderr output. * Add support for implicit suffix rules: .c.o: $(CC) -c $< Currently .SUFFIXES is a no-op. So suffix rules will be applied to any suffix-like targets. Internally the parser converts the suffix rules to pattern rules, thus saving me a lot of coding. 0.07 10/5/2005 * Host this module to a SVN repository at OSSF * Add sections "Syntax Supported" and "TODO" to POD doc 0.06 10/5/2005 * Add string-ify overloading to the Makefile::Target class * Use tar+gzip to compress the distribution. 0.05 10/1/2005 * Add support for the syntax ^\ 0.04 9/30/2005 * Fix some issues in the POD doc 0.03 9/30/2005 * Force the user to call the ->parse method after he/she constructs every Makefile::Parser object. That is to say, the constructor of the Makefile::Parser class won't call ->parse internally from now on. * Add error checking code and corresponding tests for Parser objects which has never parsed anything. * Add support of default target to the ->target method of the Makefile::Parser class. * Add method ->roots to the Makefile::Parser class which returns the "root targets" for the Makefile. * Use Devel::Cover to check the code coverage. * Use Test::Pod to check the validity of the POD docs * Use Test::Pod::Coverage to check the integrity of POD docs 0.02 9/25/2005 * Fix a bug in the SYNOPSIS of the POD doc. * Add many other stuff to the POD doc too. 0.01 Sat Sep 24 10:22:01 2005 * original version; created by h2xs 1.23 with options -XA -b 5.6.1 Makefile::Parser Makefile-Parser-0.215/Makefile.PL0000644000175000001440000000146111622744377015670 0ustar agentzusersuse strict; use lib '.'; use inc::Module::Install; name ('Makefile-Parser'); perl_version ('5.006001'); all_from ('lib/Makefile/Parser.pm'); repository 'http://github.com/agentzh/makefile-parser-pm'; install_script ('script/plmake'); install_script ('script/pgmake-db'); install_script ('script/makesimple'); requires ('Text::Balanced'); requires ('List::Util'); requires ('List::MoreUtils'); requires ('File::Spec'); requires ('Class::Accessor::Fast'); requires ('Cwd'); requires ('File::Slurp'); requires ('Makefile::DOM' => '0.005'); requires ('Class::Trigger' => '0.13'); requires ('Time::HiRes'); build_requires ('Test::More'); build_requires ('IPC::Run3' => '0.036'); use_test_base; auto_install; WriteAll; Makefile-Parser-0.215/MANIFEST0000644000175000001440000000222611622765710015041 0ustar agentzusersChanges inc/Module/AutoInstall.pm inc/Module/Install.pm inc/Module/Install/AutoInstall.pm inc/Module/Install/Base.pm inc/Module/Install/Can.pm inc/Module/Install/Fetch.pm inc/Module/Install/Include.pm inc/Module/Install/Makefile.pm inc/Module/Install/Metadata.pm inc/Module/Install/Scripts.pm inc/Module/Install/TestBase.pm inc/Module/Install/Win32.pm inc/Module/Install/WriteAll.pm inc/Spiffy.pm inc/Test/Base.pm inc/Test/Base/Filter.pm inc/Test/Builder.pm inc/Test/Builder/Module.pm inc/Test/More.pm lib/Makefile/AST.pm lib/Makefile/AST/Command.pm lib/Makefile/AST/Evaluator.pm lib/Makefile/AST/Rule.pm lib/Makefile/AST/Rule/Base.pm lib/Makefile/AST/Rule/Implicit.pm lib/Makefile/AST/StemMatch.pm lib/Makefile/AST/Variable.pm lib/Makefile/Parser.pm lib/Makefile/Parser/GmakeDB.pm Makefile.PL MANIFEST MANIFEST.SKIP META.yml README script/makesimple script/pgmake-db script/plmake t/99-pod-coverage.t t/99-pod.t t/ast-basic.t t/ast-rule.t t/ast-stem.t t/ast-var.t t/lib/Test/Make/Base.pm t/lib/Test/Make/Util.pm t/lib/Test/Util.pm t/lib/Test/Util/Base.pm t/Makefile t/Makefile-Parser.t t/Makefile2 t/Makefile3 t/Makefile4 t/Makefile5 t/Makefile6 t/makesimple.t t/Makefile7 Makefile-Parser-0.215/lib/0000755000175000001440000000000011622773367014463 5ustar agentzusersMakefile-Parser-0.215/lib/Makefile/0000755000175000001440000000000011622773367016200 5ustar agentzusersMakefile-Parser-0.215/lib/Makefile/AST/0000755000175000001440000000000011622773367016627 5ustar agentzusersMakefile-Parser-0.215/lib/Makefile/AST/Variable.pm0000644000175000001440000000026311622773122020700 0ustar agentzuserspackage Makefile::AST::Variable; use strict; use warnings; use base 'Class::Accessor::Fast'; __PACKAGE__->mk_accessors(qw{ name value flavor origin lineno file }); 1; Makefile-Parser-0.215/lib/Makefile/AST/Rule.pm0000644000175000001440000001201611622773122020061 0ustar agentzuserspackage Makefile::AST::Rule; use strict; use warnings; #use Smart::Comments; use base 'Makefile::AST::Rule::Base'; use Makefile::AST::Command; use List::MoreUtils; __PACKAGE__->mk_accessors(qw{ stem target other_targets shell }); # XXX: generate description for the rule sub as_str ($) { my $self = shift; my $order_part = ''; ## as_str: order_prereqs: $self->order_prereqs if (@{ $self->order_prereqs }) { $order_part = " | " . join(" ",@{ $self->order_prereqs }); } ### colon: $self->colon my $str = $self->target . " " . $self->colon . " " . join(" ", @{ $self->normal_prereqs }) . "$order_part ; " . join("", map { "[$_]" } @{ $self->commands }); $str =~ s/\n+//g; $str =~ s/ +/ /g; $str; } sub prepare_command ($$) { my ($self, $ast, $raw_cmd, $silent, $tolerant, $critical) = @_; ## $raw_cmd my @tokens = $raw_cmd->elements; # try to recognize modifiers: my $modifier; while (@tokens) { if ($tokens[0]->class eq 'MDOM::Token::Whitespace') { shift @tokens; next; } last unless $tokens[0]->class eq 'MDOM::Token::Modifier'; $modifier = shift @tokens; if ($modifier eq '+') { # XXX is this the right thing to do? $critical = 1; } elsif ($modifier eq '-') { $tolerant = 1; } elsif ($modifier eq '@') { $silent = 1; } else { die "Unknown modifier: $modifier"; } } local $. = $raw_cmd->lineno; ## TOKENS (BEFORE): @tokens my $cmd = $ast->solve_refs_in_tokens(\@tokens); ### cmd after solve (1): $cmd $cmd =~ s/^\s+|\s+$//gs; return () if $cmd =~ /^(\\\n)*\\?$/s; ### cmd after modifier extraction: $cmd ### critical (+): $critical ### tolerant (-): $tolerant ### silent (@): $silent if ($cmd =~ /(?new; $cmd->__add_elements(@tokens); # XXX upper-level's modifiers should take in # effect in the recursive calls: push @ast_cmds, $self->prepare_command($ast, $cmd, $silent, $tolerant, $critical); } return @ast_cmds; } while (1) { if ($cmd =~ s/^\s*\+//) { # XXX is this the right thing to do? $critical = 1; } elsif ($cmd =~ s/^\s*-//) { $tolerant = 1; } elsif ($cmd =~ s/^\s*\@//) { $silent = 1; } else { last; } } $cmd =~ s/^\s+|\s+$//gs; return () if $cmd =~ /^(\\\n)*\\?$/s; return Makefile::AST::Command->new({ silent => $silent, tolerant => $tolerant, critical => $critical, content => $cmd, target => $self->target, }); } sub prepare_commands ($$) { my ($self, $ast) = @_; my @normal_prereqs = @{ $self->normal_prereqs }; my @order_prereqs = @{ $self->order_prereqs }; ## @normal_prereqs ## @order_prereqs ### run_commands: target: $self->target ### run_commands: Stem: $self->stem $self->shell($ast->eval_var_value('SHELL')); $ast->enter_pad; $ast->add_auto_var( '@' => [$self->target], '<' => [$normal_prereqs[0]], # XXX better solutions? '*' => [$self->stem], '^' => [join(" ", List::MoreUtils::uniq(@normal_prereqs))], '+' => [join(" ", @normal_prereqs)], '|' => [join(" ", List::MoreUtils::uniq(@order_prereqs))], # XXX add more automatic vars' defs here ); ### auto $*: $ast->get_var('*') my @ast_cmds; for my $cmd (@{ $self->commands }) { $Makefile::AST::Evaluator::CmdRun = 1; push @ast_cmds, $self->prepare_command($ast, $cmd); } $ast->leave_pad; return @ast_cmds; } sub run_command ($$) { my ($self, $ast_cmd) = @_; my $cmd = $ast_cmd->content; if (!$Makefile::AST::Evaluator::Quiet && (!$ast_cmd->silent || $Makefile::AST::Evaluator::JustPrint)) { print "$cmd\n"; } if (! $Makefile::AST::Evaluator::JustPrint) { system($self->shell, '-c', $cmd); if ($? != 0) { my $retval = $? >> 8; my $target = $ast_cmd->target; if (!$Makefile::AST::Evaluator::IgnoreErrors && (!$ast_cmd->tolerant || $ast_cmd->critical)) { # XXX better handling for tolerance die "$::MAKE: *** [$target] Error $retval\n"; } else { warn "$::MAKE: [$target] Error $retval (ignored)\n"; } } } } sub run_commands ($@) { my $self = shift; for my $ast_cmd (@_) { $self->run_command($ast_cmd); } } 1; Makefile-Parser-0.215/lib/Makefile/AST/Rule/0000755000175000001440000000000011622773367017536 5ustar agentzusersMakefile-Parser-0.215/lib/Makefile/AST/Rule/Implicit.pm0000644000175000001440000000557011622773122021642 0ustar agentzuserspackage Makefile::AST::Rule::Implicit; use strict; use warnings; #use Smart::Comments; #use Smart::Comments '####'; use base 'Makefile::AST::Rule::Base'; use List::Util qw( first ); __PACKAGE__->mk_ro_accessors(qw{ targets }); sub as_str ($) { my $self = shift; my $order_part = ''; ## as_str: order_prereqs: $self->order_prereqs if (@{ $self->order_prereqs }) { $order_part = " | " . join(" ",@{ $self->order_prereqs }); } ### colon: $self->colon my $str = join(" ", @{ $self->targets }) . " " . $self->colon . " " . join(" ", @{ $self->normal_prereqs }) . "$order_part ; " . join("", map { "[$_]" } @{ $self->commands }); $str =~ s/\n+//g; $str =~ s/ +/ /g; $str; } # judge if $self is a match anything rule sub match_anything ($) { my $self = shift; first { $_ eq '%' } $self->targets; } sub is_terminal ($) { $_[0]->colon eq '::'; } sub match_target ($$) { my ($self, $target) = @_; for my $pat (@{ $self->targets }) { ### match_target: pattern: $pat ### match_target: target: $target my $match = Makefile::AST::StemMatch->new( { target => $target, pattern => $pat } ); return $match if $match; } return undef; } # apply the current rule to the given target sub apply ($$$@) { my ($self, $ast, $target, $opts) = @_; #### applying implicit rule to target: $target my $recursive; $recursive = $opts->{recursive} if $opts; #### $recursive my $match = $self->match_target($target); ## $match return undef if !$match; my (@other_targets, @normal_prereqs, @order_prereqs); for (@{ $self->targets }) { next if $_ eq $match->pattern; push @other_targets, $match->subs_stem($_); } for (@{ $self->normal_prereqs }) { push @normal_prereqs, $match->subs_stem($_); } for (@{ $self->order_prereqs }) { push @order_prereqs, $match->subs_stem($_); } for my $prereq (@order_prereqs, @normal_prereqs) { #### Test whether the prereq exists or ought to exist: $prereq #### target exists? : $ast->target_exists($prereq) ## file test: -e 'bar.hpp' #### Target ought to exists? : $ast->target_ought_to_exist($prereq) next if $ast->target_exists($prereq) or $ast->target_ought_to_exist($prereq); #### Failed to pass... # XXX mark intermedia files here next if $recursive and $ast->apply_implicit_rules($prereq); return undef; } return Makefile::AST::Rule->new( { target => $target, colon => $self->colon, stem => $match->stem, normal_prereqs => \@normal_prereqs, order_prereqs => \@order_prereqs, other_targets => \@other_targets, commands => $self->commands, } ); } 1; Makefile-Parser-0.215/lib/Makefile/AST/Rule/Base.pm0000644000175000001440000000056711622773122020743 0ustar agentzuserspackage Makefile::AST::Rule::Base; use strict; use warnings; use base 'Class::Accessor::Fast'; __PACKAGE__->mk_ro_accessors(qw{ normal_prereqs order_prereqs commands colon target }); sub add_command ($$) { my ($self, $cmd) = @_; push @{ $self->{commands} }, $cmd; } sub has_command ($) { my ($self) = @_; return scalar @{ $self->commands }; } 1; Makefile-Parser-0.215/lib/Makefile/AST/Command.pm0000644000175000001440000000066711622773122020541 0ustar agentzuserspackage Makefile::AST::Command; use strict; use warnings; use base 'Class::Accessor::Fast'; #use Smart::Comments; __PACKAGE__->mk_accessors(qw{ silent tolerant critical content target }); sub as_str { my $self = shift; my $str; if ($self->silent) { $str .= '@'; } if ($self->tolerant) { $str .= '-'; } if ($self->critical) { $str .= '+'; } $str .= $self->content; } 1; Makefile-Parser-0.215/lib/Makefile/AST/Evaluator.pm0000644000175000001440000003107311622773122021120 0ustar agentzuserspackage Makefile::AST::Evaluator; use strict; use warnings; our $VERSION = '0.215'; #use Smart::Comments; #use Smart::Comments '####'; use File::stat; use Class::Trigger qw(firing_rule); # XXX put these globals to some better place our ( $Quiet, $JustPrint, $IgnoreErrors, $AlwaysMake, $Question ); sub new ($$) { my $class = ref $_[0] ? ref shift : shift; my $ast = shift; return bless { ast => $ast, updated => {}, mtime_cache => {}, # this is better for the AST? parent_target => undef, targets_making => {}, required_targets => {}, }, $class; } sub ast ($) { $_[0]->{ast} } sub mark_as_updated ($$) { my ($self, $target) = @_; ### marking target as updated: $target $self->{updated}->{$target} = 1; } # XXX this should be moved to the AST sub is_updated ($$) { my ($self, $target) = @_; $self->{updated}->{$target}; } # update the mtime cache with -M $file sub update_mtime ($$@) { my ($self, $file, $cache) = @_; $cache ||= $self->{mtime_cache}; if (-e $file) { my $stat = stat $file or die "$::MAKE: *** stat failed on $file: $!\n"; ### set mtime for file: $file ### mtime: $stat->mtime return ($cache->{$file} = $stat->mtime); } else { ## file not found: $file return ($cache->{$file} = undef); } } # get -M $file from cache (if any) or set the cache # key-value pair otherwise sub get_mtime ($$) { my ($self, $file) = @_; my $cache = $self->{mtime_cache}; if (!exists $cache->{$file}) { # set the cache return $self->update_mtime($file, $cache); } return $cache->{$file}; } sub set_required_target ($$) { my ($self, $target) = @_; $self->{required_targets}->{$target} = 1; } sub is_required_target ($$) { my ($self, $target) = @_; $self->{required_targets}->{$target}; } sub make ($$) { my ($self, $target) = @_; return 'UP_TO_DATE' if $self->is_updated($target); my $making = $self->{targets_making}; if ($making->{$target}) { warn "$::MAKE: Circular $target <- $target ". "dependency dropped.\n"; return 'UP_TO_DATE'; } else { $making->{$target} = 1; } my $retval; my @rules = $self->ast->apply_explicit_rules($target); ### number of explicit rules: scalar(@rules) if (@rules == 0) { ### no rule matched the target: $target ### trying to make implicitly here... my $ret = $self->make_implicitly($target); delete $making->{$target}; if (!$ret) { return $self->make_by_rule($target => undef); } else { return $ret; } } # run the double-colon rules serially or run the # single matched single-colon rule: for my $rule (@rules) { my $ret; ### explicit rule for: $target ### explicit rule: $rule->as_str if (!$rule->has_command) { # XXX is this really necessary? ### The explicit rule has no command, so ### trying to make implicitly... $ret = $self->make_implicitly($target); $retval = $ret if !$retval || $ret eq 'REBUILT'; } $ret = $self->make_by_rule($target => $rule); ### make_by_rule returned: $ret $retval = $ret if !$retval || $ret eq 'REBUILT'; } delete $making->{$target}; # postpone the timestamp propagation until all individual # rules have been updated: $self->update_mtime($target); $self->mark_as_updated($target); return $retval; } sub make_implicitly ($$) { my ($self, $target) = @_; if ($self->ast->is_phony_target($target)) { ### make_implicitly skipped target since it's phony: $target return undef; } my $rule = $self->ast->apply_implicit_rules($target); if (!$rule) { return undef; } ### implicit rule: $rule->as_str my $retval = $self->make_by_rule($target => $rule); if ($retval eq 'REBUILT') { for my $target ($rule->other_targets) { $self->mark_as_updated($target); } } return $retval; } sub make_by_rule ($$$) { my ($self, $target, $rule) = @_; ### make_by_rule (target): $target return 'UP_TO_DATE' if $self->is_updated($target) and $rule->colon eq ':'; # XXX the parent should be passed via arguments or local vars my $parent = $self->{parent_target}; ## Retrieving parent target: $parent if (!$rule) { ## HERE! ## exists? : -f $target if (-f $target) { return 'UP_TO_DATE'; } else { if ($self->is_required_target($target)) { my $msg = "$::MAKE: *** No rule to make target `$target'"; if (defined $parent) { $msg .= ", needed by `$parent'"; } print STDERR "$msg."; if ($Makefile::AST::Runtime) { die " Stop.\n"; } else { warn " Ignored.\n"; $self->mark_as_updated($target); return 'UP_TO_DATE'; } } else { return 'UP_TO_DATE'; } } } ### make_by_rule (rule): $rule->as_str ### stem: $rule->stem # XXX solve pattern-specific variables here... # enter pads for target-specific variables: # XXX in order to solve '+=' and '?=', # XXX we actually should NOT call enter pad # XXX directly here... my $saved_stack_len = $self->ast->pad_stack_len; $self->ast->enter_pad($rule->target); ## pad stack: $self->ast->{pad_stack}->[0] my $target_mtime = $self->get_mtime($target); my $out_of_date = $self->ast->is_phony_target($target) || !defined $target_mtime; my $prereq_rebuilt; ## Setting parent target to: $target $self->{parent_target} = $target; # process normal prereqs: for my $prereq (@{ $rule->normal_prereqs }) { # XXX handle order-only prepreqs here ### processing prereq: $prereq $self->set_required_target($prereq); my $res = $self->make($prereq); ### make returned: $res if ($res and $res eq 'REBUILT') { $out_of_date++; $prereq_rebuilt++; } elsif ($res and $res eq 'UP_TO_DATE') { if (!$out_of_date) { if ($self->get_mtime($prereq) > $target_mtime) { ### prereq file is newer: $prereq $out_of_date = 1; } } } else { die "make_by_rule: Unexpected returned value for prereq $prereq: $res"; } } # process order-only prepreqs: for my $prereq (@{ $rule->order_prereqs }) { ## process order-only prereq: $prereq $self->set_required_target($prereq); $self->make($prereq); } $self->{parent_target} = undef; if ($AlwaysMake || $out_of_date) { my @ast_cmds = $rule->prepare_commands($self->ast); $self->call_trigger('firing_rule', $rule, \@ast_cmds); if (!$Question) { ### firing rule's commands: $rule->as_str $rule->run_commands(@ast_cmds); } $self->mark_as_updated($rule->target) if $rule->colon eq ':'; if (my $others = $rule->other_targets) { # mark "other targets" as updated too: for my $other (@$others) { ### marking "other target" as updated: $other $self->mark_as_updated($other); } } $self->ast->leave_pad( $self->ast->pad_stack_len - $saved_stack_len ); #### AST Commands: @ast_cmds return 'REBUILT' if @ast_cmds or $prereq_rebuilt; } $self->ast->leave_pad( $self->ast->pad_stack_len - $saved_stack_len ); return 'UP_TO_DATE'; } 1; __END__ =head1 NAME Makefile::AST::Evaluator - Evaluator and runtime for Makefile::AST instances =head1 SYNOPSIS use Makefile::AST::Evaluator; $Makefile::AST::Evaluator::JustPrint = 0; $Makefile::AST::Evaluator::Quiet = 1; $Makefile::AST::Evaluator::IgnoreErrors = 1; $Makefile::AST::Evaluator::AlwaysMake = 1; $Makefile::AST::Evaluator::Question = 1; # $ast is a Makefile::AST instance: my $eval = Makefile::AST::Evaluator->new($ast); Makefile::AST::Evaluator->add_trigger( firing_rule => sub { my ($self, $rule, $ast_cmds) = @_; my $target = $rule->target; my $colon = $rule->colon; my @normal_prereqs = @{ $rule->normal_prereqs }; # ... } ); $eval->set_required_target($user_makefile) $eval->make($goal); =head1 DESCRIPTION This module implementes an evaluator or a runtime for makefile ASTs represented by L instances. It "executes" the specified GNU make AST by the GNU makefile semantics. Note that, "execution" not necessarily mean building a project tree by firing makefile rule commands. Actually you can defining your own triggers by calling the L method. (See the L for examples.) In other words, you can do more interesting things like plotting the call path tree of a Makefile using Graphviz, or translating the original makefile to another form (like what the L script does). It's worth mentioning that, most of the construction algorithm for topological graph s (including implicit rule application) have already been implemented in L and its child node classes. =head1 CONFIGURE VARIABLES This module provides several package variables (i.e. static class variables) for controlling the behavior of the evaluator. Particularly the user needs to set the C<$AlwaysMake> variable to true and C<$Question> to true, if she wants to use the evaluator to do special tasks like plotting dependency graphs and translating GNU makefiles to other format. Setting L<$AlwaysMake> to true will force the evaluator to ignore the timestamps of external files appeared in the makefiles while setting L<$Question> to true will prevent the evaluator from executing the shell commands specified in the makefile rules. Here's the detailed listing for all the config variables: =over =item C<$Question> This variable corresponds to the command-line option C<-q> or <--question> in GNU make. Its purpose is to make the evaluator enter the "questioning mode", i.e., a mode in which C will never try executing rule commands unless it has to, C echoing is suppressed at the same time. =item C<$AlwaysMake> This variable corresponds to the command-line option C<-B> or C<--always-make>. It forces re-constructing all the rule's targets related to the goal, ignoring the timestamp or existence of targets' dependencies. =item C<$Quiet> It corresponds to GNU make's command-line option C<-s>, C<--silent>, or C<--quiet>. Its effect is to cancel the echoing of shell commands being executed. =item C<$JustPrint> This variable corresponds to GNU make's command line option C<-n>, C<--just-print>, C<--dry-run>, or C<--recon>. Its effect is to print out the shell commands requiring execution but without actually executing them. =item C<$IgnoreErrors> This variable corresponds to GNU make's command line option C<-i> or C<--ignore-errors>,It's used to ignore the errors of shell commands being executed during the make process. The default behavior is quitting as soon as a shell command without the C<-> modifier fails. =back =head1 CLASS TRIGGERS The C method of this class defines a trigger named C via the L module. Everytime the C method reaches the trigger point, it will invoke the user's processing handler with the following three arguments: the self object, the L object, and the corresponding C object in the context. By registering his own processing handlers for the C trigger, the user's code can reuse the evaluator to do his own cool things without traversing the makefile ASTs himself. See the L for code examples. =head1 CODE REPOSITORY For the very latest version of this script, check out the source from L. There is anonymous access to all. =head1 AUTHOR Zhang "agentzh" Yichun C<< >> =head1 COPYRIGHT AND LICENSE Copyright (c) 2007-2008 by Zhang "agentzh" Yichun (agentzh). This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L, L, L, L, L. Makefile-Parser-0.215/lib/Makefile/AST/StemMatch.pm0000644000175000001440000000303111622773122021034 0ustar agentzuserspackage Makefile::AST::StemMatch; use strict; use warnings; use base 'Class::Accessor::Fast'; __PACKAGE__->mk_ro_accessors(qw{ pattern target stem dir notdir }); sub _split_path ($) { my ($path) = @_; my ($dir, $notdir); if ($path =~ m{.*/}) { $dir = $&; $notdir = $'; } else { $dir = ''; $notdir = $path; } return ($dir, $notdir); } sub _pat2re ($@) { my ($pat, $capture) = @_; $pat = quotemeta $pat; if ($capture) { $pat =~ s/\\\%/(\\S*)/; } else { $pat =~ s/\\\%/\\S*/; } $pat; } sub new ($$) { my $class = ref $_[0] ? ref shift : shift; my $opts = shift; my $pattern = $opts->{pattern}; my $target = $opts->{target}; my ($dir, $notdir) = _split_path($target); my $re = _pat2re($pattern, 1); my $stem; if ($pattern =~ m{/}) { if ($target =~ $re) { $stem = $1; } } else { if ($notdir =~ $re) { $stem = $1; } } if (defined $stem) { return $class->SUPER::new( { pattern => $pattern, target => $target, stem => $stem, dir => $dir, notdir => $notdir, } ); } else { return undef; } } sub subs_stem ($$) { my ($self, $other_pat) = @_; my $stem = $self->stem; $other_pat =~ s/\%/$stem/; if ($self->pattern !~ m{/}) { $other_pat = $self->dir . $other_pat; } return $other_pat; } 1; Makefile-Parser-0.215/lib/Makefile/Parser/0000755000175000001440000000000011622773367017434 5ustar agentzusersMakefile-Parser-0.215/lib/Makefile/Parser/GmakeDB.pm0000644000175000001440000004622511622773154021227 0ustar agentzuserspackage Makefile::Parser::GmakeDB; use strict; use warnings; #use Smart::Comments '####'; #use Smart::Comments '###', '####'; use List::Util qw( first ); use List::MoreUtils qw( none ); use MDOM::Document::Gmake; use Makefile::AST; our $VERSION = '0.215'; # XXX This should not be hard-coded this way... our @Suffixes = ( '.out', '.a', '.ln', '.o', '.c', '.cc', '.C', '.cpp', '.p', '.f', '.F', '.r', '.y', '.l', '.s', '.S', '.mod', '.sym', '.def', '.h', '.info', '.dvi', '.tex', '.texinfo', '.texi', '.txinfo', '.w', '.ch', '.web', '.sh', '.elc', '.el' ); # need a better place for this sub: sub solve_escaped ($) { my $ref = shift; $$ref =~ s/\\ ([\#\\:\n])/$1/gx; } sub _match_suffix ($@); sub _match_suffix ($@) { my ($target, $full_match) = @_; ## $target ## $full_match if ($full_match) { return first { $_ eq $target } @Suffixes; } else { my ($fst, $snd); for my $suffix (@Suffixes) { my $len = length($suffix); ## prefix 1: substr($target, 0, $len) ## prefix 2: $suffix if (substr($target, 0, $len) eq $suffix) { $fst = $suffix; ## first suffix recognized: $suffix ## suffix 1: substr($target, $len) $snd = _match_suffix(substr($target, $len), 1); ## $snd next if !defined $snd; return ($fst, $snd); } } return undef; } } sub parse ($$) { shift; my $ast = Makefile::AST->new; my $dom = MDOM::Document::Gmake->new(shift); my ($var_origin, $orig_lineno, $orig_file); my $rule; # The last rule in the context my ($not_a_target, $directive); my $db_section = 'null'; my $next_var_lineno = 0; # lineno for the next var assignment for my $elem ($dom->elements) { ## elem class: $elem->class ## elem lineno: $elem->lineno ## NEXT VAR LINENO: $next_var_lineno ## CURRENT LINENO: $elem->lineno if ($elem =~ /^# Variables$/) { ### Setting DB section to 'var': $elem->content $db_section = 'var'; next; } if ($elem =~ /^# (?:Implicit Rules|Directives|Files)$/) { ### Setting DB section to 'rule': $elem->content $db_section = 'rule'; next; } if ($elem =~ /^# (?:Pattern-specific Variable Values)$/) { ### Setting DB section to 'patspec': $elem->content $db_section = 'patspec'; next; } if ($directive and $elem->class !~ /Directive$/) { # XXX yes, this is hacky ### pushing value to value: $elem push @{ $directive->{value} }, $elem->clone; next; } next if $elem->isa('MDOM::Token::Whitespace'); if ($db_section eq 'var' and $elem->isa('MDOM::Assignment')) { ## Found assignment: $elem->source if (!$var_origin) { my $lineno = $elem->lineno; die "ERROR: line $lineno: No flavor found for the assignment"; } else { my $lhs = $elem->lhs; my $rhs = $elem->rhs; my $op = $elem->op; my $flavor; if ($op eq '=') { $flavor = 'recursive'; } elsif ($op eq ':=') { $flavor = 'simple'; } else { # XXX add support for ?= and += die "Unknown op: $op"; } my $name = join '', @$lhs; # XXX solve refs? my @value_tokens = map { $_->clone } @$rhs; #map { $_ = "$_" } @$rhs; ## LHS: $name ## RHS: $rhs my $var = Makefile::AST::Variable->new({ name => $name, flavor => $flavor, origin => $var_origin, value => \@value_tokens, lineno => $orig_lineno, file => $orig_file, }); $ast->add_var($var); undef $var_origin; } } elsif ($elem =~ /^#\s+(automatic|makefile|default|environment|command line)/) { $var_origin = $1; $var_origin = 'file' if $var_origin eq 'makefile'; $next_var_lineno = $elem->lineno + 1; } elsif ($elem =~ /^# `(\S+)' directive \(from `(\S+)', line (\d+)\)/) { ($var_origin, $orig_file, $orig_lineno) = ($1, $2, $3); $next_var_lineno = $elem->lineno + 1; ### directive origin: $var_origin ### directive lineno: $orig_lineno } elsif ($elem =~ /^#\s+.*\(from `(\S+)', line (\d+)\)/) { ($orig_file, $orig_lineno) = ($1, $2); ## lineno: $orig_lineno } elsif ($db_section eq 'rule' and $elem =~ /^# Not a target:$/) { $not_a_target = 1; } elsif ($elem =~ /^# Implicit\/static pattern stem: `(\S+)'/) { #### Setting pattern stem for solved implicit rule: $1 $rule->{stem} = $1; } elsif ($db_section eq 'rule' and $elem =~ /^# Also makes: (.*)/) { my @other_targets = split /\s+/, $1; $rule->{other_targets} = \@other_targets; #### Setting other targets: @other_targets } elsif ($db_section ne 'var' and $next_var_lineno == $elem->lineno and $elem =~ /^# (\S.*?) (:=|\?=|\+=|=) (.*)/) { #die "HERE!"; my ($name, $op, $value) = ($1, $2, $3); # XXX tokenize the $value here? if (!$rule) { die "error: target/parttern-specific variables found where there is no rule in the context"; } my $flavor; if ($op eq ':=') { $flavor = 'simple'; } else { # XXX we should treat '?=' and '+=' specifically here? $flavor = 'recursive'; } #### Adding local variable: $name my $handle = sub { my $ast = shift; my $old_value = $ast->eval_var_value($name); #warn "VALUE!!! $value"; $value = "$old_value $value" if $op eq '+='; my $var = Makefile::AST::Variable->new({ name => $name, flavor => $flavor, origin => $var_origin, value => $flavor eq 'recursive' ? $value : [$value], lineno => $orig_lineno, file => $orig_file, }); $ast->enter_pad(); $ast->add_var($var); }; $ast->add_pad_trigger($rule->target => $handle); undef $var_origin; } elsif ($db_section ne 'var' and $elem->isa('MDOM::Rule::Simple')) { ### Found rule: $elem->source ### not a target? : $not_a_target if ($rule) { # The db output tends to produce # trailing empty commands, so we remove it: if ($rule->{commands}->[-1] and $rule->{commands}->[-1] eq "\n") { pop @{ $rule->{commands} }; } } if ($not_a_target) { $not_a_target = 0; next; } my $targets = $elem->targets; my $colon = $elem->colon; my $normal_prereqs = $elem->normal_prereqs; my $order_prereqs = $elem->order_prereqs; my $command = $elem->command; ## Target (raw): $targets ## Prereq (raw): $prereqs my $target = join '', @$targets; my @order_prereqs = split /\s+/, join '', @$order_prereqs; my @normal_prereqs = split /\s+/, join '', @$normal_prereqs; # Solve escaped chars: solve_escaped(\$target); map { solve_escaped(\$_) } @normal_prereqs, @order_prereqs; @order_prereqs = grep { my $value = $_; none { $_ eq $value } @normal_prereqs } @order_prereqs if @normal_prereqs; #### Target: $target ### Normal Prereqs: @normal_prereqs ### Order-only Prereqs: @order_prereqs #map { $_ = "$_" } @normal_prereqs, @order_prereqs; # XXX suffix rules allow order-only prepreqs? not sure... if ($target !~ /\s/ and $target !~ /\%/ and !@normal_prereqs and !@order_prereqs) { ## try to recognize suffix rule: $target my ($fst, $snd); $fst = _match_suffix($target, 1); if (!defined $fst) { ($fst, $snd) = _match_suffix($target); ## got first: $fst ## got second: $snd if (defined $fst) { ## found suffix rule/2: $target $target = '%' . $snd; @normal_prereqs = ('%' . $fst); } } else { ## found suffix rule rule/1: $target $target = '%' . $fst; } } my $rule_struct = { order_prereqs => [], normal_prereqs => \@normal_prereqs, order_prereqs => \@order_prereqs, commands => [defined $command ? $command : ()], colon => $colon, target => $target, }; if ($target =~ /\%/) { ## implicit rule found: $target my $targets = [split /\s+/, $target]; $rule_struct->{targets} = $targets, $rule = Makefile::AST::Rule::Implicit->new($rule_struct); $ast->add_implicit_rule($rule) if $db_section eq 'rule'; } else { $rule = Makefile::AST::Rule->new($rule_struct); $ast->add_explicit_rule($rule) if $db_section eq 'rule'; } } elsif ($elem->isa('MDOM::Command')) { ## Found command: $elem if (!$rule) { die "error: line " . $elem->lineno . ": Command not allowed here"; } else { #my @tokens = map { "$_" } $elem->elements; #my @tokens = $elem #shift @tokens if $tokens[0] eq "\t"; #pop @tokens if $tokens[-1] eq "\n"; #push @{ $rule->{commands} }, \@tokens; ## parser: CMD: $elem my $first = $elem->first_element; ## $first $elem->remove_child($first) if $first->class eq 'MDOM::Token::Separator'; ### elem source: $elem->source #if ($elem->source eq "\n") { # die "Matched!"; #} ## lineno2: $orig_lineno $elem->{lineno} = $orig_lineno if $orig_lineno; $rule->add_command($elem->clone); # XXX why clone? ### Command added: $elem->content } } elsif ($elem->class =~ /MDOM::Directive/) { ### directive name: $elem->name ### directive value: $elem->value if ($elem->name eq 'define') { # XXX set lineno to $orig_lineno here? $directive = { name => $elem->value, value => [], # needs to be fed later flavor => 'recursive', origin => $var_origin, lineno => $orig_lineno, file => $orig_file, }; next; } if ($elem->name eq 'endef') { ### parsed a define directive: $directive # trim the trailing new lines in the value: #warn "HERE!!! "; #warn quotemeta($directive->{value}->[-1]); my $last = $directive->{value}->[-1]; if ("$last" =~ /^\s*$/s) { pop @{ $directive->{value} } } elsif ($last->can('last_element')) { my $last_elem = $last->last_element; #warn "LAST: '$last'\n"; if ($last_elem and "$last_elem" =~ /^\s*$/s) { $last->remove_child($last_elem); } } my $var = Makefile::AST::Variable->new($directive); $ast->add_var($var); undef $var_origin; undef $directive; } else { warn "warning: line " . $elem->lineno . ": Unknown directive: " . $elem->source; } } elsif ($elem->class =~ /Unknown/) { # XXX Note that output from $(info ...) may skew up stdout # XXX This hack is used to make features/conditionals.t pass print $elem if $elem eq "success\n"; # XXX The 'hello, world' hack to used to make sanity/func-refs.t pass warn "warning: line " . $elem->lineno . ": Unknown GNU make database struct: " . $elem->source if $elem !~ /hello.*world/ and $elem ne "success\n"; } } { my $default = $ast->eval_var_value('.DEFAULT_GOAL'); ## default goal's value: $var $ast->default_goal($default) if $default; ### DEFAULT GOAL: $ast->default_goal my $rule = $ast->apply_explicit_rules('.PHONY'); if ($rule) { ### PHONY RULE: $rule ### phony targets: @{ $rule->normal_prereqs } for my $phony (@{ $rule->normal_prereqs }) { $ast->set_phony_target($phony); } } ## foo var: $ast->get_var('foo') } $ast; } 1; __END__ =head1 NAME Makefile::Parser::GmakeDB - GNU makefile parser using GNU make's database dump =head1 VERSION This document describes Makefile::Parser::GmakeDB 0.215 released on 18 August 2011. =head1 SYNOPSIS use Makefile::Parser::GmakeDB; my $db_listing = `make --print-data-base -pqRrs -f Makefile`; my $ast = Makefile::Parser::GmakeDB->parse(\$db_listing); =head1 DESCRIPTION This module serves as a parser for GNU makefiles. However, it does not parse user's original makefile directly. Instead it uses L to parse the "data base output listing" produced by GNU make (via its C<--print-data-base> option). So essentially it reuses the C implementation of GNU make. This parser has been tested as a component of the L utility and has successfully passed 51% of GNU make 3.81's official test suite. The result of the parser is a makefile AST defined by L. The "data base output listing" generated by C is a detailed listing for GNU make's internal data structures, which is essentially the AST used by C. According to GNU make's current maintainer, Paul Smith, this feature is provided primarily for debuging the user's own makefiles, and it also helps the GNU make developer team to diagnose the flaws in make itself. Incidentally this output is conformed to the GNU makefile syntax, and a lot of important information is provided in the form of makefile comments. Therefore, my GmakeDB parser is able to reuse the L module to parse this output listing. The data base output from GNU make can be divided into several clearly-separated segments. They're file header, "Variables", "Files", "VPATH Search Paths", as well as the last resource stats information. The contents of these segments are mostly obvious. The Files segment may deserve some explanation. It is the place for explict rules. Now let's take the Variables segment as an example to demonstrate the format of the data base listing: # Variables # automatic executable of GNU make 3.81 is required to work with this module. =item L =back =head1 BUGS =over =item * GNU make does not escape meta characters appeared in rule targes and prerequisites in its data base listing. Examples are C<:>, C<\>, and C<#>. This bug has been reported to the GNU make team as C. This bug has not yet been fixed on the C side, so I have to work around this issue by preprocessing the data base listing in the L script. =item * The data base listing produced by GNU make lacks the information regarding the C and C directives. It gives rise to the lack of information in the resulting AST structures constructed by this module. Hence the current AST and runtime do not implement the C and C directives. To make it even worse, there's no known way to work around it. I've already reported this issue to the GNU make team as Savannah bug #20069. =back =head1 CODE REPOSITORY For the very latest version of this script, check out the source from L. There is anonymous access to all. =head1 AUTHOR Zhang "agentzh" Yichun C<< >> =head1 COPYRIGHT AND LICENSE Copyright (c) 2005-2008 by Zhang "agentzh" Yichun (agentzh). This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L, L, L, L, L. Makefile-Parser-0.215/lib/Makefile/AST.pm0000644000175000001440000006265611622773122017171 0ustar agentzuserspackage Makefile::AST; use strict; use warnings; our $VERSION = '0.215'; #use Smart::Comments; #use Smart::Comments '####'; use Makefile::AST::StemMatch; use Makefile::AST::Rule::Implicit; use Makefile::AST::Rule; use Makefile::AST::Variable; use base 'Class::Accessor::Fast'; __PACKAGE__->mk_ro_accessors(qw{ phony_targets targets prereqs makefile pad_stack named_pads pad_triggers }); __PACKAGE__->mk_accessors(qw{ default_goal }); use List::Util 'first'; use List::MoreUtils qw( uniq pairwise ) ; use Cwd qw/ realpath /; use File::Spec; use MDOM::Util 'trim_tokens'; use MDOM::Document::Gmake; # XXX better name? our $Runtime = undef; sub new ($@) { my $class = ref $_[0] ? ref shift : shift; my $makefile = shift; return bless { explicit_rules => {}, implicit_rules => [], pad_stack => [{}], # the last scope is # the default GLOBAL # scope named_pads => {}, # hooks for target-specific # variables pad_triggers => {}, targets => {}, prereqs => {}, phony_targets => {}, makefile => $makefile, }, $class; } sub is_phony_target ($$) { my ($self, $target) = @_; $self->phony_targets->{$target}; } sub set_phony_target ($$) { my ($self, $target) = @_; $self->phony_targets->{$target} = 1; } sub target_exists ($$) { my $self = shift; # XXX provide hooks for mocking file systems # XXX access the mtime cache instead in the future my $target = shift; #### Test if target exists: $target #### Result: -e $target return -e $target; } sub target_ought_to_exist ($$) { my ($self, $target) = @_; my $res = $self->targets->{$target} || $self->prereqs->{$target}; ### Test if $target ought to exist: $res $res; } sub apply_explicit_rules ($$) { my ($self, $target) = @_; my $list = $self->{explicit_rules}->{$target} || []; wantarray ? @$list : $list->[0]; } sub get_var ($$) { my ($self, $name) = @_; my $pads = $self->pad_stack; for my $pad (@$pads) { if (my $var = $pad->{$name}) { return $var; } } return undef; } # XXX sub find_var # find_var(name => $name, flavor => $flavor) # enter the pad for a lexical scope sub enter_pad ($@) { my ($self, $name) = @_; #### Entering pad named: $name my $stack = $self->pad_stack; my $pad; if (defined $name) { $pad = $self->named_pads->{$name} ||= {}; } else { $pad = {}; } unshift @$stack, $pad; if (defined $name) { my $list = $self->pad_triggers->{$name}; if ($list) { for my $trigger (@$list) { #### Firing pad trigger for: $name $trigger->($self); } } } } sub leave_pad ($@) { my ($self, $count) = @_; #### Leaving pad... my $stack = $self->pad_stack; $count = 1 if !defined $count; for (1..$count) { shift @$stack if @$stack > 1; } } sub pad_stack_len ($) { scalar(@{ $_[0]->pad_stack }); } sub add_pad_trigger ($$$) { my ($self, $name, $sub) = @_; my $list = $self->pad_triggers->{$name} ||= []; push @$list, $sub; } sub add_var ($$) { my ($self, $var) = @_; # XXX variable overridding check ## variable name: $var->name() if (!ref $var->value) { $var->value( [MDOM::Document::Gmake::_tokenize_command( $var->value )] ); } $self->pad_stack->[0]->{$var->name()} = $var; } sub add_auto_var ($$$@) { my $self = shift; my %pairs = @_; while (my ($name, $value) = each %pairs) { my $var = Makefile::AST::Variable->new( { name => $name, flavor => 'simple', origin => 'automatic', value => $value, } ); $self->add_var($var); } } sub explicit_rules ($) { my $self = shift; my @items = values %{ $self->{explicit_rules} }; my @rules = map { @$_ } @items; \@rules; } sub implicit_rules ($) { $_[0]->{implicit_rules}; } sub add_explicit_rule ($$) { my ($self, $rule) = @_; if (!defined $self->default_goal) { my $target = $rule->target; ### check if it's the default target: $target # XXX skip the makefile itself if ($target !~ m{^\./Makefile_\S+} and (substr($target, 0, 1) ne '.' or $target =~ m{/})) { $self->default_goal($target); } } if ($rule->colon eq ':') { # XXX check single colon rules for conflicts # XXX merge prereqs if no cmd given $self->{explicit_rules}->{$rule->target} = [$rule]; } else { # XXX check double colon rules for conflicts my $list = $self->{explicit_rules}->{$rule->target} ||= []; # XXX check if $list is an ARRAY ref push @$list, $rule; } for my $prereq (@{$rule->normal_prereqs}, @{$rule->order_prereqs}) { $self->prereqs->{$prereq} = 1; } $self->targets->{$rule->target} = 1; } sub add_implicit_rule ($$) { my ($self, $rule) = @_; # XXX cancel a built-in implicit rule by defining # a pattern rule with the same target and # prerequisites, but no commands for my $target (@{ $rule->targets }) { # XXX better pattern recognition next if $target =~ /\%/; $self->targets->{$target} = 1; } for my $prereq (@{$rule->normal_prereqs}, @{$rule->order_prereqs}) { next if $prereq =~ /\%/; $self->prereqs->{$prereq} = 1; } my $list = $self->{implicit_rules}; unshift @$list, $rule; } # implementation for the implicit rule search # algorithm sub apply_implicit_rules ($$) { my ($self, $target) = @_; # XXX handle archive(member) here #### step 2... my @rules = grep { $_->match_target($target) } @{ $self->implicit_rules }; #### rules: map { $_->as_str } @rules return undef if !@rules; #### step 3... if (first { ! $_->match_anything } @rules) { @rules = grep { !( $_->match_anything && !$_->is_terminal ) } @rules; } #### rules: map { $_->as_str } @rules #### step 4... @rules = grep { @{ $_->commands } > 0 } @rules; #### rules: map { $_->as_str } @rules #### step 5... # XXX This is hacky...not sure if it's the right # XXX thing to do (it's unspec'd afaik) @rules = sort { -( scalar( @{ $a->normal_prereqs } ) <=> scalar( @{ $b->normal_prereqs } ) ) } @rules; for my $rule (@rules) { #### target: $target #### rule: $rule->as_str #### file test: -e 'bar.hpp' my $applied = $rule->apply($self, $target); if ($applied) { #### applied rule: $applied->as_str return $applied; } } ### step 6... for my $rule (@rules) { next if $rule->is_terminal; #### applying the implicit rule recursively my $applied = $rule->apply( $self, $target, { recursive => 1 }); if ($applied) { return $applied; } #### Failed to apply the rule recursively } ### step 7... my $applied = $self->apply_explicit_rules('.DEFAULT'); if ($applied) { $applied->target($target); return $applied; } return undef; } sub _pat2re ($@) { my ($pat, $capture) = @_; $pat = quotemeta $pat; if ($capture) { $pat =~ s/\\\%/(\\S*)/g; } else { $pat =~ s/\\\%/\\S*/g; } $pat; } sub _split_args($$$$) { my ($self, $func, $s, $m, $n) = @_; $n ||= $m; my @tokens = ''; my @args; ### $n while (@args <= $n) { ### split args: @args ### split tokens: @tokens if ($s =~ /\G\s+/gc) { push @tokens, $&, ''; } elsif ($s =~ /\G[^\$,]+/gc) { $tokens[-1] .= $&; } elsif ($s =~ /\G,/gc) { if (@args < $n - 1) { push @args, [grep { $_ ne '' } @tokens]; @tokens = ''; } else { $tokens[-1] .= $&; } } elsif (my $res = MDOM::Document::Gmake::extract_interp($s)) { #die $res; push @tokens, MDOM::Token::Interpolation->new($res), ''; } elsif ($s =~ /\G\$./gc) { push @tokens, MDOM::Token::Interpolation->new($&), ''; } elsif ($s =~ /\G./gc) { $tokens[-1] .= $&; } else { if (@args <= $n - 1) { push @args, [grep { $_ ne '' } @tokens]; } last if @args >= $m and @args <= $n; warn $self->makefile, ":$.: ", "*** insufficient number of arguments (", scalar(@args), ") to function `$func'. Stop.\n"; exit(2); } } return @args; } sub eval_var_value ($$) { my ($self, $name) = @_; if (my $var = $self->get_var($name)) { ### eval_var_value: $var if ($var->flavor eq 'recursive') { ## HERE! eval_var_value ## eval recursive var: $var my $val = $self->solve_refs_in_tokens( $var->value ); $val =~ s/^\s+|\s+$//gs; #warn "value: $val\n"; return $val; } else { # don't complain about uninitialized value: no warnings 'uninitialized'; my $val = join '', @{$var->value}; $val =~ s/^\s+|\s+$//gs; return $val; } } else { # process undefined var: return ''; } } sub _text2words ($) { my ($text) = @_; $text =~ s/^\s+|\s+$//g; split /\s+/, $text; } sub _check_numeric ($$$$) { my ($self, $func, $order, $n) = @_; if ($n !~ /^\d+$/) { warn $self->makefile, ":$.: ", "*** non-numeric $order argument to `$func' function: '$n'. Stop.\n"; exit(2); } } sub _check_greater_than ($$$$$) { my ($self, $func, $order, $n, $value) = @_; if ($n <= $value) { warn $self->makefile, ":$.: *** $order argument to `$func' function must be greater than $value. Stop.\n"; exit(2); } } sub _trim ($@) { for (@_) { s/^\s+|\s+$//g; } } sub _process_func_ref ($$$) { my ($self, $name, $args) = @_; ### process func ref: $name # XXX $name = $self->_process_refs($name); my @args; my $nargs = scalar(@args); if ($name eq 'subst') { my @args = $self->_split_args($name, $args, 3); map { $_ = $self->solve_refs_in_tokens($_) } @args; ### arguments: @args my ($from, $to, $text) = @args; $from = quotemeta($from); $text =~ s/$from/$to/g; return $text; } if ($name eq 'patsubst') { my @args = $self->_split_args($name, $args, 3); map { $_ = $self->solve_refs_in_tokens($_) } @args; my ($pattern, $replacement, $text) = @args; my $re = _pat2re($pattern, 1); $replacement =~ s/\%/\${1}/g; $replacement = qq("$replacement"); #### pattern: $re #### replacement: $replacement #### text: $text my $code = "s/^$re\$/$replacement/e"; #### code: $code my @words = _text2words($text); map { eval $code; } @words; return join ' ', grep { $_ ne '' } @words; } if ($name eq 'strip') { my @args = $self->_split_args($name, $args, 1); map { $_ = $self->solve_refs_in_tokens($_) } @args; my ($string) = @args; $string =~ s/^\s+|\s+$//g; $string =~ s/\s+/ /g; return $string; } if ($name eq 'findstring') { my @args = $self->_split_args($name, $args, 2); map { $_ = $self->solve_refs_in_tokens($_) } @args; my ($find, $in) = @args; if (index($in, $find) >= 0) { return $find; } else { return ''; } my ($patterns, $text) = @args; my @regexes = map { _pat2re($_) } split /\s+/, $patterns; ## regexes: @regexes my $regex = join '|', map { "(?:$_)" } @regexes; ## regex: $regex my @words = _text2words($text); return join ' ', grep /^$regex$/, @words; } if ($name eq 'filter') { my @args = $self->_split_args($name, $args, 2); map { $_ = $self->solve_refs_in_tokens($_) } @args; my ($patterns, $text) = @args; my @regexes = map { _pat2re($_) } split /\s+/, $patterns; ## regexes: @regexes my $regex = join '|', map { "(?:$_)" } @regexes; ## regex: $regex my @words = _text2words($text); return join ' ', grep /^$regex$/, @words; } if ($name eq 'filter-out') { my @args = $self->_split_args($name, $args, 2); map { $_ = $self->solve_refs_in_tokens($_) } @args; my ($patterns, $text) = @args; my @regexes = map { _pat2re($_) } split /\s+/, $patterns; ## regexes: @regexes my $regex = join '|', map { "(?:$_)" } @regexes; ## regex: $regex my @words = _text2words($text); return join ' ', grep !/^$regex$/, @words; } if ($name eq 'sort') { my @args = $self->_split_args($name, $args, 1); map { $_ = $self->solve_refs_in_tokens($_) } @args; my ($list) = @args; _trim($list); return join ' ', uniq sort split /\s+/, $list; } if ($name eq 'words') { my @args = $self->_split_args($name, $args, 1); map { $_ = $self->solve_refs_in_tokens($_) } @args; my ($text) = @args; my @words = _text2words($text); return scalar(@words); } if ($name eq 'word') { my @args = $self->_split_args($name, $args, 2); map { $_ = $self->solve_refs_in_tokens($_) } @args; my ($n, $text) = @args; _trim($n); $self->_check_numeric('word', 'first', $n); $self->_check_greater_than('word', 'first', $n, 0); my @words = _text2words($text); return $n > @words ? '' : $words[$n - 1]; } if ($name eq 'wordlist') { my @args = $self->_split_args($name, $args, 3); map { $_ = $self->solve_refs_in_tokens($_) } @args; my ($s, $e, $text) = @args; _trim($s, $e, $text); $self->_check_numeric('wordlist', 'first', $s); $self->_check_numeric('wordlist', 'second', $e); $self->_check_greater_than('wordlist', 'first', $s, 0); $self->_check_greater_than('wordlist', 'second', $s, -1); my @words = _text2words($text); if ($s > $e || $s > @words || $e == 0) { return ''; } $e = @words if $e > @words; return join ' ', @words[$s-1..$e-1]; } if ($name eq 'firstword') { my @args = $self->_split_args($name, $args, 1); map { $_ = $self->solve_refs_in_tokens($_) } @args; my ($text) = @args; my @words = _text2words($text); return @words > 0 ? $words[0] : ''; } if ($name eq 'lastword') { my @args = $self->_split_args($name, $args, 1); map { $_ = $self->solve_refs_in_tokens($_) } @args; my ($text) = @args; my @words = _text2words($text); return @words > 0 ? $words[-1] : ''; } if ($name eq 'dir') { my @args = $self->_split_args($name, $args, 1); map { $_ = $self->solve_refs_in_tokens($_) } @args; my ($text) = @args; my @names = _text2words($text); return join ' ', map { /.*\// ? $& : './' } @names; } if ($name eq 'notdir') { my @args = $self->_split_args($name, $args, 1); map { $_ = $self->solve_refs_in_tokens($_) } @args; my ($text) = @args; my @names = _text2words($text); return join ' ', map { s/.*\///; $_ } @names; } if ($name eq 'suffix') { my @args = $self->_split_args($name, $args, 1); map { $_ = $self->solve_refs_in_tokens($_) } @args; my ($text) = @args; my @names = _text2words($text); my $s = join ' ', map { /.*(\..*)/ ? $1 : '' } @names; $s =~ s/\s+$//g; return $s; } if ($name eq 'basename') { my @args = $self->_split_args($name, $args, 1); map { $_ = $self->solve_refs_in_tokens($_) } @args; my ($text) = @args; my @names = _text2words($text); my $s = join ' ', map { /(.*)\./ ? $1 : $_ } @names; $s =~ s/\s+$//g; return $s; } if ($name eq 'addsuffix') { my @args = $self->_split_args($name, $args, 2); map { $_ = $self->solve_refs_in_tokens($_) } @args; my ($suffix, $text) = @args; #_trim($suffix); my @names = _text2words($text); return join ' ', map { $_ . $suffix } @names; } if ($name eq 'addprefix') { my @args = $self->_split_args($name, $args, 2); map { $_ = $self->solve_refs_in_tokens($_) } @args; my ($suffix, $text) = @args; #_trim($suffix); my @names = _text2words($text); return join ' ', map { $suffix . $_ } @names; } if ($name eq 'join') { my @args = $self->_split_args($name, $args, 2); map { $_ = $self->solve_refs_in_tokens($_) } @args; my ($list_1, $list_2) = @args; my @list_1 = _text2words($list_1); my @list_2 = _text2words($list_2); return join ' ', pairwise { no warnings 'uninitialized'; $a . $b } @list_1, @list_2; } if ($name eq 'wildcard') { my @args = $self->_split_args($name, $args, 1); map { $_ = $self->solve_refs_in_tokens($_) } @args; my ($pattern) = @args; return join ' ', grep { -e $_ } glob $pattern; } if ($name eq 'realpath') { no warnings 'uninitialized'; my @args = $self->_split_args($name, $args, 1); map { $_ = $self->solve_refs_in_tokens($_) } @args; my ($text) = @args; my @names = _text2words($text); return join ' ', map { realpath($_) } @names; } if ($name eq 'abspath') { my @args = $self->_split_args($name, $args, 1); map { $_ = $self->solve_refs_in_tokens($_) } @args; my ($text) = @args; my @names = _text2words($text); my @paths = map { File::Spec->rel2abs($_) } @names; for my $path (@paths) { my @f = split '/', $path; my @new_f; for (@f) { if ($_ eq '..') { pop @new_f; } else { push @new_f, $_; } } $path = join '/', @new_f; } return join ' ', @paths; } if ($name eq 'shell') { my @args = $self->_split_args($name, $args, 1); map { $_ = $self->solve_refs_in_tokens($_) } @args; my ($cmd) = @args; my $output = `$cmd`; $output =~ s/(?:\r?\n)+$//g; $output =~ s/\r?\n/ /g; return $output; } if ($name eq 'if') { my @args = $self->_split_args($name, $args, 2, 3); #map { $_ = $self->solve_refs_in_tokens($_) } @args; my ($condition, $then_part, $else_part) = @args; trim_tokens($condition); $condition = $self->solve_refs_in_tokens($condition); return $condition eq '' ? $self->solve_refs_in_tokens($else_part) : $self->solve_refs_in_tokens($then_part); } if ($name eq 'or') { my @args = $self->_split_args($name, $args, 1, 1000_000_000); #map { $_ = $self->solve_refs_in_tokens($_) } @args; for my $arg (@args) { trim_tokens($arg); my $value = $self->solve_refs_in_tokens($arg); return $value if $value ne ''; } return ''; } if ($name eq 'and') { my @args = $self->_split_args($name, $args, 1, 1000_000_000); #map { $_ = $self->solve_refs_in_tokens($_) } @args; ## arguments for 'and': @args my $value; for my $arg (@args) { trim_tokens($arg); $value = $self->solve_refs_in_tokens($arg); return '' if $value eq ''; } return $value; } if ($name eq 'foreach') { my @args = $self->_split_args($name, $args, 3); my ($var, $list, $text) = @args; $var = $self->solve_refs_in_tokens($var); $list = $self->solve_refs_in_tokens($list); my @words = _text2words($list); # save the original status of $var my $rvars = $self->{_vars}; my $not_exist = !exists $rvars->{$var}; my $old_val = $rvars->{$var}; my @results; for my $word (@words) { $rvars->{$var} = $word; #warn "$word"; push @results, $self->solve_refs_in_tokens($text); } # restore the original status of $var if ($not_exist) { delete $rvars->{$var}; } else { $rvars->{$var} = $old_val; } return join ' ', @results; } if ($name eq 'error') { my ($text) = $self->_split_args($name, $args, 1); $text = $self->solve_refs_in_tokens($text); warn $self->makefile, ":$.: *** $text. Stop.\n"; exit(2) if $Runtime; return ''; } if ($name eq 'warning') { my ($text) = $self->_split_args($name, $args, 1); $text = $self->solve_refs_in_tokens($text); warn $self->makefile, ":$.: $text\n"; return ''; } if ($name eq 'info') { my ($text) = $self->_split_args($name, $args, 1); $text = $self->solve_refs_in_tokens($text); print "$text\n"; return ''; } return undef; } sub solve_refs_in_tokens ($$) { my ($self, $tokens) = @_; return '' if !$tokens; my @new_tokens; for my $token (@$tokens) { if (!ref $token or !$token->isa('MDOM::Token::Interpolation')) { ### solve_refs: non-var-ref token: $token push @new_tokens, $token; next; } if ($token =~ /^\$[{(](.*)[)}]$/) { my $s = $1; if ($s =~ /^([-\w]+)\s+(.*)$/) { my $res = $self->_process_func_ref($1, $2); if (defined $res) { push @new_tokens, $res; next; } } elsif ($s =~ /^(\S+?):(\S+?)=(\S+)$/) { my ($var, $from, $to) = ($1, $2, $3); my $res = $self->_process_func_ref( 'patsubst', "\%$from,\%$to,\$($var)" ); if (defined $res) { push @new_tokens, $res; next; } } ### found variable reference: $1 ### evaluating variable : $s push @new_tokens, $self->eval_var_value($s); next; } elsif ($token =~ /^\$\$$/) { push @new_tokens, '$'; next; } elsif ($token =~ /^\$(.)$/) { push @new_tokens, $self->eval_var_value($1); next; } push @new_tokens, $token; } ### solving results: join '', @new_tokens return join '', @new_tokens; } 1; __END__ =head1 NAME Makefile::AST - AST for (GNU) makefiles =head1 DESCRIPTION The structure of this (GNU) makefile AST is designed based on GNU make's data base listing output produced by C<--print-data-base>. This AST library provides the following classes: =over =item Makefile::AST The primary class for ASTs. Provides interface for node adding and querying, such as C, C, C, C, C, C, C, as well as lots of other utility functions, like method C for computing the ultimate values of makefile variables, method C and C for local variable's scoping pad. =item L This is the base class for the rule nodes in the AST. It has properties like C, C, C, and C. =item L This class represents the de-sugared form of simple rules and implicite rules I application. It inherits from L, and adds new properties C and C. =item L This class represents the implicit rule nodes in the AST. It inherits from L, and adds new properties C, C, and C. =item L This class encapsulates the file pattern matching (file names containing C<%>) and stem substitution algorithms. =item L It represents the makefile variable nodes in the AST, including C, C, C, and C. =item L Used to encapsulate information regarding makefile rule commands (e.g. command body, command modifiers C<@>, C<->, C<+>, and etc.) as a whole. =back =head1 LIMITATIONS AND TODO Adding support for other flavors' makes into this AST library should make a huge amount of sense. The most interesting candiate is Microsoft's NMAKE. =head1 CODE REPOSITORY For the very latest version of this script, check out the source from L. There is anonymous access to all. =head1 AUTHOR Zhang "agentzh" Yichun C<< >> =head1 COPYRIGHT AND LICENSE Copyright (c) 2007-2008 by Zhang "agentzh" Yichun (agentzh). This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L, L, L, L, L. Makefile-Parser-0.215/lib/Makefile/Parser.pm0000644000175000001440000012040111622773164017763 0ustar agentzuserspackage Makefile::Parser; use strict; use warnings; use File::Spec; use Cwd qw/ realpath /; use List::MoreUtils qw( uniq pairwise ) ; use Text::Balanced qw( gen_extract_tagged ); #use Smart::Comments; #our $Debug = 0; our $Strict = 0; our $VERSION = '0.215'; our $Error; our $Runtime = undef; # usage: $class->new; sub new { my $proto = shift; my $class = ref $proto || $proto; my $self = bless { _vars => {}, # all the definitions of variables _tars => undef, # all the targets _default => undef, # default target _depends => {}, # all the dependencies _imps => [], # targets in implicit rules }, $class; return $self; } my $extract_interp_1 = gen_extract_tagged('\$[(]', '[)]', ''); my $extract_interp_2 = gen_extract_tagged('\$[{]', '[}]', ''); sub _extract_interp { my ($res) = $extract_interp_1->($_[0]); if (!$res) { ($res) = $extract_interp_2->($_[0]); } $res; } # usage: $obj->parse($filename); sub parse { my ($self, $file, $vars) = @_; $file ||= 'Makefile'; my %init_vars = %$vars if $vars; $self->{_file} = $file; $self->{_vars} = { MAKE => $0, CC => 'cc', SHELL => 'sh', %init_vars, }; undef $self->{_tars}; undef $self->{_default}; $self->{_depends} = {}; $self->{_imps} = []; my $rvars = $self->{_vars}; my $in; unless (open $in, $file) { $Error = "Cannot open $file for reading: $!"; return undef; } my $state = 'S_IDLE'; my ($var, $value, $tar_name, $tar, $colon_type, $depends, $cmd); my @cmds; my %tars; #%$rvars = (); my $first_tar = 1; while (<$in>) { next if /^\s*#/ and $state ne 'S_IN_VAL'; next if /^\s*$/ and $state ne 'S_IN_VAL'; #$tar_name = '' unless defined $var; #warn "(tar: $tar_name) Switching to tate $state with $_"; #warn $state if $state ne 'S_IDLE'; chomp; #if (/TEST_VERBOSE=/) { #### line: $_ #### state: $state #} # expand the value of use-defined variables: #s/\$[\{\(](\w+)[\}\)]/exists $rvars->{$1} ? $rvars->{$1} : $&/ge; $_ = $self->_process_refs($_); if (($state eq 'S_IDLE' or $state eq 'S_CMD') and /^(\w+) \s* :?= \s* (.*)$/xo) { $var = $1; $value = $2; #warn "matched $var = $value\n"; if ($value =~ m{\\\s*$}) { $value .= "\n"; $state = 'S_IN_VAL' ; } else { $value =~ s/#.*//m; $rvars->{$var} = $value; ### variable: $var ### value: $value $state = 'S_IDLE'; } #warn "$1 * $2 * $3"; } elsif ($state eq 'S_IN_VAL') { #warn $1; my $line = $_; #warn "adding value line $line\n"; $value .= "$line\n"; if ($line !~ m{\\\s*$}) { $state = 'S_IDLE' ; #warn "Processing value '$value'\n"; $value =~ s/[ \t]*\\\n[ \t]*/ /sg; $value =~ s/#.*//smg; #warn "Finale value '$value'\n"; $value =~ s/\n//gs; $value =~ s/^\s+|\s+$//gs; $rvars->{$var} = $value; #warn "$var <=> $value\n"; } } elsif (($state eq 'S_IDLE' or $state eq 'S_CMD') and /^(\.\w+) (\.\w+) \s* (::?)\s*$/xo) { $_ = "%$2 $3 %$1\n"; #warn $_; redo; } elsif (($state eq 'S_IDLE' or $state eq 'S_CMD') and /^(\S[^:]*) (::?) \s* (.*)$/xo) { $tar_name = $1; $colon_type = $2; $depends = $3; $tar_name =~ s/^\s+|\s+$//g; my $cmd; if ($depends =~ s/;(.*)//) { $cmd = $1; } # Ignore .SUFFIXES currently: next if $tar_name eq '.SUFFIXES'; #warn "Adding target $tar_name...\n"; $tar = Makefile::Target->new($tar_name, $colon_type); if (my $old_tars = $tars{$tar_name}) { if ($colon_type eq ':') { $tar->add_prereq($old_tars->[0]->prereqs); if (my @cmd = $old_tars->[0]->commands) { $tar->add_command(@cmd); } @$old_tars = $tar; } else { push @$old_tars, $tar; } } else { $tars{$tar_name} = [$tar]; } if ($tar_name =~ m/%/) { push @{$self->{_imps}}, $tar_name; } if ($first_tar) { $self->{_default} = $tar; $first_tar = 0; } if ($depends =~ s/\s+\\$//o) { $state = 'S_IN_DEPENDS'; } else { $depends =~ s/\^\\$/\\/; $state = 'S_CMD'; } my @depends = split /\s+/, $depends; map { $self->{_depends}->{$_} = 1 } @depends; $tar->add_depend(@depends); $tar->add_command($cmd) if defined $cmd; } elsif ($state eq 'S_IN_DEPENDS' and /^\s+ (.*)$/xo) { $depends = $1; if ($depends !~ s/\s+\\$//o) { $depends =~ s/\^\\$/\\/; my @depends = split /\s+/, $depends; map { $self->{_depends}->{$_} = 1 } @depends; $tar->add_depend(@depends); $state = 'S_CMD'; } } elsif ($state eq 'S_CMD' and /^\s+(.*)/o) { $cmd = $1; if ($cmd =~ s/\s+\\$//o) { $state = 'S_IN_CMD'; } else { $tar->add_command($cmd); } } elsif ($state eq 'S_IN_CMD' and /^\s+(.*)/o) { $cmd .= " $1"; if ($cmd !~ s/\s+\\$//o) { $tar->add_command($cmd); $state = 'S_CMD'; } } elsif ($Strict) { $Error = "syntax error: line $.: $_\n"; return undef; } else { warn "I dunno how to do with it: $_\n"; } } $self->{_tars} = \%tars; $self->post_parse; #warn Data::Dumper->Dump([\%tars], ['TARGETS']); close $in; return $self; } sub post_parse { my $self = shift; my $rdepends = $self->{_depends}; my $rimps = $self->{_imps}; for (keys %$rdepends) { next if /%/; #warn "Trying to match implicit rules one by one against $_...\n"; $self->solve_imp($_); } for (@$rimps) { delete $self->{_tars}->{$_}; } } sub solve_imp { my ($self, $depend) = @_; my $rimps = $self->{_imps}; for my $imp (@$rimps) { my $obj = $self->target($imp); die "Rules for $imp not found" unless $obj and ref $obj; my $regex = quotemeta($imp); $regex =~ s/\\%/(.+)/; # `%' can match any nonempty substring #warn "Processing regex $regex...\n"; if ($depend =~ m/^$regex$/) { #warn "Succeeded to match $imp against $depend!\n"; my $matched_part = $1; my $tar = Makefile::Target->new($depend, $obj->colon_type); my $dep; my @deps = map { s/%/$matched_part/; $self->{_depends}->{$_} = 1; #warn "Recursively solving dependent gole $_...\n"; $self->solve_imp($_); $dep = $_; $_ } $obj->depends; $tar->add_depend(@deps); my @cmds = map { s/\$commands; $tar->add_command(@cmds); $self->{_tars}->{$depend} = [$tar]; } } } sub var { my ($self, $var) = @_; $self->parse if !defined $self->{_file}; return $self->{_vars}->{$var}; } sub vars { my $self = shift; $self->parse if !defined $self->{_file}; return keys %{$self->{_vars}}; } sub target { my ($self, $tar_name) = @_; $self->parse if !defined $self->{_file}; return $self->{_default} if !defined $tar_name; my $tars = $self->{_tars}->{$tar_name}; $tars ||= []; wantarray ? @$tars : $tars->[0]; } sub targets { my $self = shift; $self->parse if !defined $self->{_file}; return map { @$_ } values %{$self->{_tars}}; } sub roots { my $self = shift; $self->parse if !defined $self->{_file}; my %depends = %{$self->{_depends}}; my %tars = %{$self->{_tars}}; my @roots = (); my ($key, $val); while (($key, $val) = each %tars) { #next if $key =~ m/%/; next if $depends{$key}; push @roots, $key; } return @roots; } sub error { return $Error; } sub _solve_refs_in_tokens ($$) { my ($self, $tokens) = @_; return '' if !$tokens; my $rvars = $self->{_vars}; my @new_tokens; for my $token (@$tokens) { if ($token =~ /^\$[{(](.*)[)}]$/) { my $s = $1; if ($s =~ /^([-\w]+)\s+(.*)$/) { my $res = $self->_process_func_ref($1, $2); if (defined $res) { push @new_tokens, $res; next; } } elsif ($s =~ /^(\S+?):(\S+?)=(\S+)$/) { my ($var, $from, $to) = ($1, $2, $3); my $res = $self->_process_func_ref( 'patsubst', "\%$from,\%$to,\$($var)" ); if (defined $res) { push @new_tokens, $res; next; } } if (exists $rvars->{$s}) { push @new_tokens, $rvars->{$s}; next; } else { # FIXME: undefined var == '' #push @new_tokens, ''; #next; } } elsif ($token =~ /^\$[@<|]$/) { # currently do nothing with the automatic vars } elsif ($token =~ /^\$\$$/) { push @new_tokens, '$'; next; } elsif ($token =~ /^\$(.)$/) { if (exists $rvars->{$1}) { push @new_tokens, $rvars->{$1}; next; } else { # FIXME: undef var == '' # push @new_tokens, ''; # next; } ### found single-letter variable: $1 ### value: $rvars->{$1} ### token: $token } push @new_tokens, $token; } ### retval: join '', @$tokens return join '', @new_tokens; } sub _process_refs { my ($self, $s) = @_; my @tokens = ''; while (1) { if ($s =~ /\G[^\$]+/gc) { $tokens[-1] .= $&; } elsif (my $res = _extract_interp($s)) { push @tokens, $res, ''; } elsif ($s =~ /\G\$./gc) { push @tokens, $&, ''; } elsif ($s =~ /\G./gc) { $tokens[-1] .= $&; } else { last; } } ### tokens: @tokens return $self->_solve_refs_in_tokens(\@tokens); } sub _pat2re ($@) { my ($pat, $capture) = @_; $pat = quotemeta $pat; if ($capture) { $pat =~ s/\\\%/(\\S*)/g; } else { $pat =~ s/\\\%/\\S*/g; } $pat; } sub _text2words ($) { my ($text) = @_; $text =~ s/^\s+|\s+$//g; split /\s+/, $text; } sub _check_numeric ($$$$) { my ($self, $func, $order, $n) = @_; if ($n !~ /^\d+$/) { warn $self->{_file}, ":$.: ", "*** non-numeric $order argument to `$func' function: '$n'. Stop.\n"; exit(2); } } sub _check_greater_than ($$$$$) { my ($self, $func, $order, $n, $value) = @_; if ($n <= $value) { warn $self->{_file}, ":$.: *** $order argument to `$func' function must be greater than $value. Stop.\n"; exit(2); } } sub _trim ($@) { for (@_) { s/^\s+|\s+$//g; } } sub _split_args($$$$) { my ($self, $func, $s, $m, $n) = @_; $n ||= $m; my @tokens = ''; my @args; ### $n while (@args <= $n) { ### split args: @args ### split tokens: @tokens if ($s =~ /\G\s+/gc) { push @tokens, $&, ''; } elsif ($s =~ /\G[^\$,]+/gc) { $tokens[-1] .= $&; } elsif ($s =~ /\G,/gc) { if (@args < $n - 1) { push @args, [grep { $_ ne '' } @tokens]; @tokens = ''; } else { $tokens[-1] .= $&; } } elsif (my $res = _extract_interp($s)) { push @tokens, $res, ''; } elsif ($s =~ /\G\$./gc) { push @tokens, $&, ''; } elsif ($s =~ /\G./gc) { $tokens[-1] .= $&; } else { if (@args <= $n - 1) { push @args, [grep { $_ ne '' } @tokens]; } last if @args >= $m and @args <= $n; warn $self->{_file}, ":$.: ", "*** insufficient number of arguments (", scalar(@args), ") to function `$func'. Stop.\n"; exit(2); } } return @args; } sub _trim_tokens ($) { my $tokens = shift; return if !@$tokens; if ($tokens->[0] =~ /^\s+$/) { shift @$tokens; } return if !@$tokens; if ($tokens->[-1] =~ /^\s+$/) { pop @$tokens; } } sub _process_func_ref ($$$) { my ($self, $name, $args) = @_; #### process func ref: $name $name = $self->_process_refs($name); my @args; my $nargs = scalar(@args); if ($name eq 'subst') { my @args = $self->_split_args($name, $args, 3); map { $_ = $self->_solve_refs_in_tokens($_) } @args; ### arguments: @args my ($from, $to, $text) = @args; $from = quotemeta($from); $text =~ s/$from/$to/g; return $text; } if ($name eq 'patsubst') { my @args = $self->_split_args($name, $args, 3); map { $_ = $self->_solve_refs_in_tokens($_) } @args; my ($pattern, $replacement, $text) = @args; my $re = _pat2re($pattern, 1); $replacement =~ s/\%/\${1}/g; $replacement = qq("$replacement"); #### pattern: $re #### replacement: $replacement #### text: $text my $code = "s/^$re\$/$replacement/e"; #### code: $code my @words = _text2words($text); map { eval $code; } @words; return join ' ', grep { $_ ne '' } @words; } if ($name eq 'strip') { my @args = $self->_split_args($name, $args, 1); map { $_ = $self->_solve_refs_in_tokens($_) } @args; my ($string) = @args; $string =~ s/^\s+|\s+$//g; $string =~ s/\s+/ /g; return $string; } if ($name eq 'findstring') { my @args = $self->_split_args($name, $args, 2); map { $_ = $self->_solve_refs_in_tokens($_) } @args; my ($find, $in) = @args; if (index($in, $find) >= 0) { return $find; } else { return ''; } my ($patterns, $text) = @args; my @regexes = map { _pat2re($_) } split /\s+/, $patterns; ### regexes: @regexes my $regex = join '|', map { "(?:$_)" } @regexes; ### regex: $regex my @words = _text2words($text); return join ' ', grep /^$regex$/, @words; } if ($name eq 'filter') { my @args = $self->_split_args($name, $args, 2); map { $_ = $self->_solve_refs_in_tokens($_) } @args; my ($patterns, $text) = @args; my @regexes = map { _pat2re($_) } split /\s+/, $patterns; ### regexes: @regexes my $regex = join '|', map { "(?:$_)" } @regexes; ### regex: $regex my @words = _text2words($text); return join ' ', grep /^$regex$/, @words; } if ($name eq 'filter-out') { my @args = $self->_split_args($name, $args, 2); map { $_ = $self->_solve_refs_in_tokens($_) } @args; my ($patterns, $text) = @args; my @regexes = map { _pat2re($_) } split /\s+/, $patterns; ### regexes: @regexes my $regex = join '|', map { "(?:$_)" } @regexes; ### regex: $regex my @words = _text2words($text); return join ' ', grep !/^$regex$/, @words; } if ($name eq 'sort') { my @args = $self->_split_args($name, $args, 1); map { $_ = $self->_solve_refs_in_tokens($_) } @args; my ($list) = @args; _trim($list); return join ' ', uniq sort split /\s+/, $list; } if ($name eq 'words') { my @args = $self->_split_args($name, $args, 1); map { $_ = $self->_solve_refs_in_tokens($_) } @args; my ($text) = @args; my @words = _text2words($text); return scalar(@words); } if ($name eq 'word') { my @args = $self->_split_args($name, $args, 2); map { $_ = $self->_solve_refs_in_tokens($_) } @args; my ($n, $text) = @args; _trim($n); $self->_check_numeric('word', 'first', $n); $self->_check_greater_than('word', 'first', $n, 0); my @words = _text2words($text); return $n > @words ? '' : $words[$n - 1]; } if ($name eq 'wordlist') { my @args = $self->_split_args($name, $args, 3); map { $_ = $self->_solve_refs_in_tokens($_) } @args; my ($s, $e, $text) = @args; _trim($s, $e, $text); $self->_check_numeric('wordlist', 'first', $s); $self->_check_numeric('wordlist', 'second', $e); $self->_check_greater_than('wordlist', 'first', $s, 0); $self->_check_greater_than('wordlist', 'second', $s, -1); my @words = _text2words($text); if ($s > $e || $s > @words || $e == 0) { return ''; } $e = @words if $e > @words; return join ' ', @words[$s-1..$e-1]; } if ($name eq 'firstword') { my @args = $self->_split_args($name, $args, 1); map { $_ = $self->_solve_refs_in_tokens($_) } @args; my ($text) = @args; my @words = _text2words($text); return @words > 0 ? $words[0] : ''; } if ($name eq 'lastword') { my @args = $self->_split_args($name, $args, 1); map { $_ = $self->_solve_refs_in_tokens($_) } @args; my ($text) = @args; my @words = _text2words($text); return @words > 0 ? $words[-1] : ''; } if ($name eq 'dir') { my @args = $self->_split_args($name, $args, 1); map { $_ = $self->_solve_refs_in_tokens($_) } @args; my ($text) = @args; my @names = _text2words($text); return join ' ', map { /.*\// ? $& : './' } @names; } if ($name eq 'notdir') { my @args = $self->_split_args($name, $args, 1); map { $_ = $self->_solve_refs_in_tokens($_) } @args; my ($text) = @args; my @names = _text2words($text); return join ' ', map { s/.*\///; $_ } @names; } if ($name eq 'suffix') { my @args = $self->_split_args($name, $args, 1); map { $_ = $self->_solve_refs_in_tokens($_) } @args; my ($text) = @args; my @names = _text2words($text); my $s = join ' ', map { /.*(\..*)/ ? $1 : '' } @names; $s =~ s/\s+$//g; return $s; } if ($name eq 'basename') { my @args = $self->_split_args($name, $args, 1); map { $_ = $self->_solve_refs_in_tokens($_) } @args; my ($text) = @args; my @names = _text2words($text); my $s = join ' ', map { /(.*)\./ ? $1 : $_ } @names; $s =~ s/\s+$//g; return $s; } if ($name eq 'addsuffix') { my @args = $self->_split_args($name, $args, 2); map { $_ = $self->_solve_refs_in_tokens($_) } @args; my ($suffix, $text) = @args; #_trim($suffix); my @names = _text2words($text); return join ' ', map { $_ . $suffix } @names; } if ($name eq 'addprefix') { my @args = $self->_split_args($name, $args, 2); map { $_ = $self->_solve_refs_in_tokens($_) } @args; my ($suffix, $text) = @args; #_trim($suffix); my @names = _text2words($text); return join ' ', map { $suffix . $_ } @names; } if ($name eq 'join') { my @args = $self->_split_args($name, $args, 2); map { $_ = $self->_solve_refs_in_tokens($_) } @args; my ($list_1, $list_2) = @args; my @list_1 = _text2words($list_1); my @list_2 = _text2words($list_2); return join ' ', pairwise { no warnings 'uninitialized'; $a . $b } @list_1, @list_2; } if ($name eq 'wildcard') { my @args = $self->_split_args($name, $args, 1); map { $_ = $self->_solve_refs_in_tokens($_) } @args; my ($pattern) = @args; return join ' ', grep { -e $_ } glob $pattern; } if ($name eq 'realpath') { no warnings 'uninitialized'; my @args = $self->_split_args($name, $args, 1); map { $_ = $self->_solve_refs_in_tokens($_) } @args; my ($text) = @args; my @names = _text2words($text); return join ' ', map { realpath($_) } @names; } if ($name eq 'abspath') { my @args = $self->_split_args($name, $args, 1); map { $_ = $self->_solve_refs_in_tokens($_) } @args; my ($text) = @args; my @names = _text2words($text); my @paths = map { File::Spec->rel2abs($_) } @names; for my $path (@paths) { my @f = split '/', $path; my @new_f; for (@f) { if ($_ eq '..') { pop @new_f; } else { push @new_f, $_; } } $path = join '/', @new_f; } return join ' ', @paths; } if ($name eq 'shell') { my @args = $self->_split_args($name, $args, 1); map { $_ = $self->_solve_refs_in_tokens($_) } @args; my ($cmd) = @args; my $output = `$cmd`; $output =~ s/(?:\r?\n)+$//g; $output =~ s/\r?\n/ /g; return $output; } if ($name eq 'if') { my @args = $self->_split_args($name, $args, 2, 3); #map { $_ = $self->_solve_refs_in_tokens($_) } @args; my ($condition, $then_part, $else_part) = @args; _trim_tokens($condition); $condition = $self->_solve_refs_in_tokens($condition); return $condition eq '' ? $self->_solve_refs_in_tokens($else_part) : $self->_solve_refs_in_tokens($then_part); } if ($name eq 'or') { my @args = $self->_split_args($name, $args, 1, 1000_000_000); #map { $_ = $self->_solve_refs_in_tokens($_) } @args; for my $arg (@args) { _trim_tokens($arg); my $value = $self->_solve_refs_in_tokens($arg); return $value if $value ne ''; } return ''; } if ($name eq 'and') { my @args = $self->_split_args($name, $args, 1, 1000_000_000); #map { $_ = $self->_solve_refs_in_tokens($_) } @args; ### arguments for 'and': @args my $value; for my $arg (@args) { _trim_tokens($arg); $value = $self->_solve_refs_in_tokens($arg); return '' if $value eq ''; } return $value; } if ($name eq 'foreach') { my @args = $self->_split_args($name, $args, 3); my ($var, $list, $text) = @args; $var = $self->_solve_refs_in_tokens($var); $list = $self->_solve_refs_in_tokens($list); my @words = _text2words($list); # save the original status of $var my $rvars = $self->{_vars}; my $not_exist = !exists $rvars->{$var}; my $old_val = $rvars->{$var}; my @results; for my $word (@words) { $rvars->{$var} = $word; #warn "$word"; push @results, $self->_solve_refs_in_tokens($text); } # restore the original status of $var if ($not_exist) { delete $rvars->{$var}; } else { $rvars->{$var} = $old_val; } return join ' ', @results; } if ($name eq 'error') { my ($text) = $self->_split_args($name, $args, 1); $text = $self->_solve_refs_in_tokens($text); warn $self->{_file}, ":$.: *** $text. Stop.\n"; exit(2) if $Runtime; return ''; } if ($name eq 'warning') { my ($text) = $self->_split_args($name, $args, 1); $text = $self->_solve_refs_in_tokens($text); warn $self->{_file}, ":$.: $text\n"; return ''; } if ($name eq 'info') { my ($text) = $self->_split_args($name, $args, 1); $text = $self->_solve_refs_in_tokens($text); print "$text\n"; return ''; } return undef; } ####################################### package Makefile::Target; use overload '""' => sub { shift->name }, 'cmp' => sub { my ($a,$b) = @_; "$a" cmp "$b" }, 'eq' => sub { my ($a,$b) = @_; "$a" eq "$b" }, 'lt' => sub { my ($a,$b) = @_; "$a" lt "$b" }; # usage: $class->new($name, $colon_type) sub new { my $class = shift; my $self = { _name => shift, _colon_type => shift, _commands => [], _depends => [], }; return bless $self, $class; } sub name { return shift->{_name}; } sub colon_type { return shift->{_colon_type}; } sub prereqs { return @{shift->{_depends}}; } *depends = \&prereqs; sub add_prereq { push @{shift->{_depends}}, @_; } *add_depend = \&add_prereq; sub commands { return @{shift->{_commands}}; } sub add_command { my $self = shift; my @cmds = @_; my $name = $self->name; if ($name !~ m/%/) { map { s/\$\@/$self->{_name}/g } @cmds; } push @{$self->{_commands}}, @cmds; } sub run_commands { my $self = shift; my @cmd = $self->commands; for my $cmd (@cmd) { my ($quiet, $continue); while (1) { if ($cmd =~ s/^\s*\@//) { $quiet = 1; } elsif ($cmd =~ s/^\s*-//) { $continue = 1; } else { last; } } $cmd =~ s/^\s+|\s+$//gs; next if $cmd =~ /^$/; print "$cmd\n" unless $quiet; # currently only 'sh' is specified system('/bin/sh', '-c', $cmd); if ($? != 0 && !$continue) { die "$cmd returns nonzero status value: $?\n"; } } } 1; __END__ =encoding utf-8 =head1 NAME Makefile::Parser - A simple parser for Makefiles =head1 VERSION This document describes Makefile::Parser 0.215 released on 18 August 2011. =head1 SYNOPSIS use Makefile::Parser; $parser = Makefile::Parser->new; # equivalent to ->parse('Makefile'); $parser->parse or die Makefile::Parser->error; # get last value assigned to the specified variable 'CC': print $parser->var('CC'); # get all the variable names defined in the Makefile: @vars = $parser->vars; print join(' ', sort @vars); @roots = $parser->roots; # Get all the "root targets" print $roots[0]->name; @tars = $parser->targets; # Get all the targets $tar = join("\n", $tars[0]->commands); # get the default target, say, the first target # defined in Makefile: $tar = $parser->target; $tar = $parser->target('install'); # get the name of the target, say, 'install' here: print $tar->name; # get the dependencies for the target 'install': @depends = $tar->depends; # access the shell command used to build the current target. @cmds = $tar->commands; # parse another file using the same Parser object: $parser->parse('Makefile.old') or die Makefile::Parser->error; # get the target who is specified by variable EXE_FILE $tar = $parser->target($parser->var('EXE_FILE')); =head1 DESCRIPTION This is a simple parser for Makefiles. At this very early stage, the parser only supports a limited set of features, so it may not recognize most of the advanced features provided by certain make tools like GNU make. Its initial purpose is to provide basic support for another module named L, which is aimed to render the building process specified by a Makefile using the amazing GraphViz library. The L module is not satisfactory for this purpose, so I decided to build one of my own. B This stuff is highly experimental and is currently at B stage, so production use is strongly discouraged. Right now it's just a toy for parsing trivial makefiles. B If you're looking for something more serious for parsing GNU makefiles, please see L instead. The GmakeDB parser has passed 51% of GNU make's official test suite as of this writing. If you're looking for something that can parse makefiles losslessly, take a look at the L module which may fit your needs. =head2 SYNTAX SUPPORTED The current parser implementation has been trying to support a common feature set of both MS NMAKE and GNU make. In the future, different formats of Makefiles will be handled by individual subclasses such as Makefile::Parser::Gmake. =over =item Variable Definition MIN_T_FILES = $(PAT_COVER_FILES) t\optest.t t\my_perl.exe.t t\types.cod.t \ t\catln.t t\exe2hex.t t\hex2bin.t t\bin2hex.t t\bin2asm.t t\ndisasmi.t \ t\Idu.t t\pat_tree.t t\state_mac.t t\Idu-Util.t t\cidu.t \ t\opname.t t\error.t t\operand.t t\01disasm.t t\02disasm.t t\03disasm.t \ t\disasm_cover.t t\ndisasm.t T_FILES = t\main.cod.t t\bin2hex.exe.t t\hex2bin.exe.t $(MIN_T_FILES) DIRFILESEP = ^\ "Simply expanded" variables' definition sytax in GUN make is also supported: FOO := blah blah blah which is considered invalid in Win32 NMake. "Recursively expanded" variables are currently treated as "simply expanded" variables. Variable redefinition can be handled as well: CC = cl %.obj : %.c $(CC) /nologo /c $< CC = gcc %.o : %.c $(CC) -c $< Variable expansion sytax ${abc} is accepted, whereas Win32 NMAKE will complain about it. Currently, environment variables defined in the command-line are not imported. I have no idea what default value should be assigned to built-in variables like $(MAKE) and $(CC). Currently they will be left untouched if they're not set explicitly in the Makefile. Due to the current implementation, expansion of unrecognized built-in varaibles and variables not previously defined by Makefile will NOT be performed. This behavior is different from any practial make tools, but is reasonable at this early stage of this parser. =item Explicit Rules $(CIDU_DLL) : C\idu.obj C\idu.def link /dll /nologo /debug /out:$@ /def:C\idu.def C\idu.obj $(CIDU_LIB) : $(CIDU_DLL) C\idu.obj : C\idu.c C\idu.h cd C cl /nologo /c /I . idu.c cd .. smoke : all pat_cover t\pat_cover.t \ t/pat_cover.ast.ast perl util\run-smoke.pl . smoke.html perl txt2html.pl t\*.t t\*.ast clean: copy t\pat_cover.ast.ast.html ..\ /Y $(RM_F) encoding.html encoding.pod state_mac.xml encoding.ast \ pat_tree.ast state_mac.ast \ main.cod pat_cover.pod pat_cover.html types.cod \ hex2bin.exe hex2bin.obj Specital variable $@ will be expanded using its value in the context. =item Implicit Rules =over =item Pattern Rules %.obj : %.asm masm /t $<; %.exe : %.obj link /BATCH /NOLOGO $<; The special varaibles $< and $* will be expanded according to the context. =item Old-Fashioned Suffix Rules Currently only double-suffix rules are supported: .SUFFIXES: .obj .asm .exe .asm.obj : masm /t $< .obj.exe : link /nologo $< At this moment, .SUFFIXES is a no-op. So any suffix-like things will be treated as suffixes, excluding the following example: .c.o: foo.h $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $< In suffix rules, B prerequisites are allowed according to most make tools. =back =item Substitution References objects = foo.o bar.o baz.o sources = $(objects:.o=.c) # foo.c bar.c baz.c =item Functions Currently the following GNU make makefile builtin functions are supported: =over =item C< $(subst from,to,text) > =item C< $(patsubst pattern,replacement,text) > =item C< $(strip string) > =item C< $(findstring find,text) > =item C< $(filter pattern...,text) > =item C< $(filter-out pattern...,text) > =item C< $(sort list) > =item C< $(word n,text) > =item C< $(words text) > =item C< $(wordlist s,e,text) > =item C< $(firstword names...) > =item C< $(lastword names...) > =item C< $(dir names...) > =item C< $(notdir names...) > =item C< $(suffix names...) > =item C< $(basename names...) > =item C< $(addsuffix suffix,names...) > =item C< $(addprefix prefix,names...) > =item C< $(join list1,list2) > =item C< $(wildcard pattern...) > =item C< $(realpath names...) > =item C< $(abspath names...) > =item C< $(if condition,then-part[,else-part]) > =item C< $(or condition1[,condition2[,condition3...]]) > =item C< $(and condition1[,condition2[,condition3...]]) > =item C< $(foreach var,list,text) > =item C< $(error argument...) > =item C< $(warning argument...) > =item C< $(info argument...) > =item C< $(shell cmd...) > =back Please consult the GNU make Manual for details and also take a look at the following file for some use cases: L =item Commands after ';' all : ; echo 'hello, world!' Specital variable $@ will be expanded using its value in the context. =back For the list of features which will be added very soon, take a look at the L section. =head1 The Makefile::Parser Class This class provides the main interface to the Makefile parser. =head2 METHODS =over =item C<< $obj = Makefile::Parser->new() >> It's the constructor for the Parser class. You may provide the path of your Makefile as the argument which . It is worth mentioning that the constructor will I call ->parse method internally, so please remember calling ->parse after you construct the parser object. =item C<< $obj->parse() >> =item C<< $obj->parse($Makefile_name) >> =item C<< $obj->parse($Makefile_name, { var => value, ... }) >> This method parse the specified Makefile (default to 'Makefile'). When an error occurs during the parsing procedure, C will return undef. Otherwise, a reference to Parser object itself is returned. It is recommended to check the return value every time you call this method. The detailed error info can be obtained by calling the C method. You can also pass a hash reference to specify initial variables and their values. Note that these variables are treated as "defaults" so assignments in the makefile have higher priority. =item C<< $obj->error() >> It returns the error info set by the most recent failing operation, such as a parsing failure. =item C<< $obj->var($variable_name) >> The var method returns the value of the given variable. Since the value of variables can be reset multiple times in the Makefile, so what you get is always the last value set to the variable. It's worth noting that variable reassignment can be handled appropriately during parsing since the whole parsing process is a one-pass operation compared to the multiple-pass strategy used by the CPAN module L. =item C<< @vars = $obj->vars >> This will return all the variables defined in the Makefile. The order may be quite different from the order they appear in the Makefile. =item C<< $obj->target($target_name) >> This method returns a Makefile::Target object with the name specified. It will returns undef if the rules for the given target is not described in the Makefile. It is worth noting that only targets with a definition body will be considered as a I here. When $target_name is omitted, this method will return the default target, say, the first target defined in Makefile, to the user. This can be handy if you try to build a make tool on top of this module. It is important not to send something like "$(MY_LIB)" as the target name. Only raw values are acceptable. If you really want to do something like this, please use the following code: my $tar = $parser->target($parser->var('MY_LIB')); but this code will break if you have reassigned values to variable MY_LIB in your Makefile. =item C<< @targets = $obj->targets() >> This returns all the targets in Makefile. The order can be completely different from the order they appear in Makefile. So the following code will not work if you want to get the default target (the first target): @tars = $parser->targets; print $tars[0]; Please use the following syntax instead: print $parser->target; The type of the returned list is an array of Makefile::Target objects. =item C<< @roots = $obj->roots() >> The C method returns the "root targets" in Makefile. The targets which there're no other targets depends on are called the I. For example, I, I, and I are all root targets in the Makefile generated by the I module. On the other hand, I and I are not, which may be somewhat counterintuitive. That's because there're some other targets depend on I, I, or both. The type of the returned list is an array of Makefile::Target objects. =back =head2 PACKAGE VARIABLES =over =item $Makefile::Parser::Strict When this variable is set to true, the parser will sense syntax errors and semantic errors in the Makefile. Default off. =item $Makefile::Parser::Debug When this variable is set to true, the parser will enter Debug Mode. This variable is not supposed to be used directly by the user. =back =head1 INTERNAL METHODS =over =item post_parse Iterate the Makefile AST to apply implicit rules in the following form: %.o : %.c $(CC) -c $< =item solve_imp($depend) Solve implicit rules as many as possible using one target name that appears in other target's dependency list. =back =head1 The Makefile::Target Class This class overloads the "" operator so its instances can be automatically converted to strings using their names. =head2 METHODS =over =item C<< $class->new($target_name, $colon_type) >> This is the constructor for class Makefile::Target. The first argument is the target name which can't be a Makefile variable, the second one is a single colon or a double colon which is used by the rule definition in Makefile. This method is usually called internally by the Makefile::Parser class. It doesn't make much sense to me if the user has a need to call it manually. =item C<< $obj->name() >> It will return the name of the current Target object. =item C<< @prereqs = $obj->prereqs() >> You can get the list of prerequisites (or dependencies) for the current target. If no dependency is specified in the Makefile for the target, an empty list will be returned. =item C<< @prereqs = $obj->depends() >> Alias to the C method. This method is only preserved for the sake of backward-compatibility. Please use C instead. =item C<< $obj->commands() >> This method returns a list of shell commands used to build the current target. If no shell commands is given in the Makefile, an empty array will be returned. =back =head1 CODE REPOSITORY For the very latest version of this module, check out the source from L. There is anonymous access to all. =head1 TODO The following syntax will be implemented soon: =over =item * Add support the remaining GNU make makefile builtin functions: C, C, C, C, and C. =item * Add support for recursively-expanded variables. =item * Implement rules with multiple targets =item * Serious support for "Recursively expanded" variables in GUN make =item * Comments that span multiple lines via trailing backslash =item * Lines that don't contain just comments =item * Literal "#" escaped by a leading backslash =item * The include directive =item * Look for 'GNUmakefile' and 'makefile' automatically =item * MAKEFILES Variable =item * MAKEFILE_LIST Variable =item * .VARIABLES Variable =back =head1 BUGS Please feel free to report bugs or send your wish-list to L. =head1 SEE ALSO L, L, L, L, L. =head1 AUTHOR Zhang "agentzh" Yichun, C<< >> =head1 COPYRIGHT AND LICENSE Copyright (c) 2005-2011 by Zhang "agentzh" Yichun (章亦春). This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Makefile-Parser-0.215/MANIFEST.SKIP0000644000175000001440000000035211622773335015606 0ustar agentzusers^\.git ^lib/.*\.txt ^t/tmp print\.txt ^log\.txt$ \.old$ ^a\.\w+$ \.png$ ^smerge \.bak$ ^Makefile$ \.swp$ \.tar\.gz$ ^blib/ ^pm_to_blib$ ~$ ^util\.o$ ^XS\.bs$ ^XS\.c$ ^XS\.o$ make-simple\.mk ^Makefile8\.txt ^Makefile-Parser- ^reindex$ Makefile-Parser-0.215/README0000644000175000001440000003636711622773340014603 0ustar agentzusersNAME Makefile::Parser - A simple parser for Makefiles VERSION This document describes Makefile::Parser 0.215 released on 18 August 2011. SYNOPSIS use Makefile::Parser; $parser = Makefile::Parser->new; # equivalent to ->parse('Makefile'); $parser->parse or die Makefile::Parser->error; # get last value assigned to the specified variable 'CC': print $parser->var('CC'); # get all the variable names defined in the Makefile: @vars = $parser->vars; print join(' ', sort @vars); @roots = $parser->roots; # Get all the "root targets" print $roots[0]->name; @tars = $parser->targets; # Get all the targets $tar = join("\n", $tars[0]->commands); # get the default target, say, the first target # defined in Makefile: $tar = $parser->target; $tar = $parser->target('install'); # get the name of the target, say, 'install' here: print $tar->name; # get the dependencies for the target 'install': @depends = $tar->depends; # access the shell command used to build the current target. @cmds = $tar->commands; # parse another file using the same Parser object: $parser->parse('Makefile.old') or die Makefile::Parser->error; # get the target who is specified by variable EXE_FILE $tar = $parser->target($parser->var('EXE_FILE')); DESCRIPTION This is a simple parser for Makefiles. At this very early stage, the parser only supports a limited set of features, so it may not recognize most of the advanced features provided by certain make tools like GNU make. Its initial purpose is to provide basic support for another module named Makefile::GraphViz, which is aimed to render the building process specified by a Makefile using the amazing GraphViz library. The Make module is not satisfactory for this purpose, so I decided to build one of my own. WARNING!!! This stuff is highly experimental and is currently at pre-alpha stage, so production use is strongly discouraged. Right now it's just a toy for parsing trivial makefiles. IMPORTANT!!! If you're looking for something more serious for parsing GNU makefiles, please see Makefile::Parser::GmakeDB instead. The GmakeDB parser has passed 51% of GNU make's official test suite as of this writing. If you're looking for something that can parse makefiles losslessly, take a look at the Makefile::DOM module which may fit your needs. SYNTAX SUPPORTED The current parser implementation has been trying to support a common feature set of both MS NMAKE and GNU make. In the future, different formats of Makefiles will be handled by individual subclasses such as Makefile::Parser::Gmake. Variable Definition MIN_T_FILES = $(PAT_COVER_FILES) t\optest.t t\my_perl.exe.t t\types.cod.t \ t\catln.t t\exe2hex.t t\hex2bin.t t\bin2hex.t t\bin2asm.t t\ndisasmi.t \ t\Idu.t t\pat_tree.t t\state_mac.t t\Idu-Util.t t\cidu.t \ t\opname.t t\error.t t\operand.t t\01disasm.t t\02disasm.t t\03disasm.t \ t\disasm_cover.t t\ndisasm.t T_FILES = t\main.cod.t t\bin2hex.exe.t t\hex2bin.exe.t $(MIN_T_FILES) DIRFILESEP = ^\ "Simply expanded" variables' definition sytax in GUN make is also supported: FOO := blah blah blah which is considered invalid in Win32 NMake. "Recursively expanded" variables are currently treated as "simply expanded" variables. Variable redefinition can be handled as well: CC = cl %.obj : %.c $(CC) /nologo /c $< CC = gcc %.o : %.c $(CC) -c $< Variable expansion sytax ${abc} is accepted, whereas Win32 NMAKE will complain about it. Currently, environment variables defined in the command-line are not imported. I have no idea what default value should be assigned to built-in variables like $(MAKE) and $(CC). Currently they will be left untouched if they're not set explicitly in the Makefile. Due to the current implementation, expansion of unrecognized built-in varaibles and variables not previously defined by Makefile will NOT be performed. This behavior is different from any practial make tools, but is reasonable at this early stage of this parser. Explicit Rules $(CIDU_DLL) : C\idu.obj C\idu.def link /dll /nologo /debug /out:$@ /def:C\idu.def C\idu.obj $(CIDU_LIB) : $(CIDU_DLL) C\idu.obj : C\idu.c C\idu.h cd C cl /nologo /c /I . idu.c cd .. smoke : all pat_cover t\pat_cover.t \ t/pat_cover.ast.ast perl util\run-smoke.pl . smoke.html perl txt2html.pl t\*.t t\*.ast clean: copy t\pat_cover.ast.ast.html ..\ /Y $(RM_F) encoding.html encoding.pod state_mac.xml encoding.ast \ pat_tree.ast state_mac.ast \ main.cod pat_cover.pod pat_cover.html types.cod \ hex2bin.exe hex2bin.obj Specital variable $@ will be expanded using its value in the context. Implicit Rules Pattern Rules %.obj : %.asm masm /t $<; %.exe : %.obj link /BATCH /NOLOGO $<; The special varaibles $< and $* will be expanded according to the context. Old-Fashioned Suffix Rules Currently only double-suffix rules are supported: .SUFFIXES: .obj .asm .exe .asm.obj : masm /t $< .obj.exe : link /nologo $< At this moment, .SUFFIXES is a no-op. So any suffix-like things will be treated as suffixes, excluding the following example: .c.o: foo.h $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $< In suffix rules, no prerequisites are allowed according to most make tools. Substitution References objects = foo.o bar.o baz.o sources = $(objects:.o=.c) # foo.c bar.c baz.c Functions Currently the following GNU make makefile builtin functions are supported: " $(subst from,to,text) " " $(patsubst pattern,replacement,text) " " $(strip string) " " $(findstring find,text) " " $(filter pattern...,text) " " $(filter-out pattern...,text) " " $(sort list) " " $(word n,text) " " $(words text) " " $(wordlist s,e,text) " " $(firstword names...) " " $(lastword names...) " " $(dir names...) " " $(notdir names...) " " $(suffix names...) " " $(basename names...) " " $(addsuffix suffix,names...) " " $(addprefix prefix,names...) " " $(join list1,list2) " " $(wildcard pattern...) " " $(realpath names...) " " $(abspath names...) " " $(if condition,then-part[,else-part]) " " $(or condition1[,condition2[,condition3...]]) " " $(and condition1[,condition2[,condition3...]]) " " $(foreach var,list,text) " " $(error argument...) " " $(warning argument...) " " $(info argument...) " " $(shell cmd...) " Please consult the GNU make Manual for details and also take a look at the following file for some use cases: Commands after ';' all : ; echo 'hello, world!' Specital variable $@ will be expanded using its value in the context. For the list of features which will be added very soon, take a look at the "TODO" section. The Makefile::Parser Class This class provides the main interface to the Makefile parser. METHODS "$obj = Makefile::Parser->new()" It's the constructor for the Parser class. You may provide the path of your Makefile as the argument which . It is worth mentioning that the constructor will *not* call ->parse method internally, so please remember calling ->parse after you construct the parser object. "$obj->parse()" "$obj->parse($Makefile_name)" "$obj->parse($Makefile_name, { var => value, ... })" This method parse the specified Makefile (default to 'Makefile'). When an error occurs during the parsing procedure, "parse" will return undef. Otherwise, a reference to Parser object itself is returned. It is recommended to check the return value every time you call this method. The detailed error info can be obtained by calling the "error" method. You can also pass a hash reference to specify initial variables and their values. Note that these variables are treated as "defaults" so assignments in the makefile have higher priority. "$obj->error()" It returns the error info set by the most recent failing operation, such as a parsing failure. "$obj->var($variable_name)" The var method returns the value of the given variable. Since the value of variables can be reset multiple times in the Makefile, so what you get is always the last value set to the variable. It's worth noting that variable reassignment can be handled appropriately during parsing since the whole parsing process is a one-pass operation compared to the multiple-pass strategy used by the CPAN module Make. "@vars = $obj->vars" This will return all the variables defined in the Makefile. The order may be quite different from the order they appear in the Makefile. "$obj->target($target_name)" This method returns a Makefile::Target object with the name specified. It will returns undef if the rules for the given target is not described in the Makefile. It is worth noting that only targets with a definition body will be considered as a *target* here. When $target_name is omitted, this method will return the default target, say, the first target defined in Makefile, to the user. This can be handy if you try to build a make tool on top of this module. It is important not to send something like "$(MY_LIB)" as the target name. Only raw values are acceptable. If you really want to do something like this, please use the following code: my $tar = $parser->target($parser->var('MY_LIB')); but this code will break if you have reassigned values to variable MY_LIB in your Makefile. "@targets = $obj->targets()" This returns all the targets in Makefile. The order can be completely different from the order they appear in Makefile. So the following code will not work if you want to get the default target (the first target): @tars = $parser->targets; print $tars[0]; Please use the following syntax instead: print $parser->target; The type of the returned list is an array of Makefile::Target objects. "@roots = $obj->roots()" The "roots" method returns the "root targets" in Makefile. The targets which there're no other targets depends on are called the *root targets*. For example, *install*, *uninstall*, and *veryclean* are all root targets in the Makefile generated by the *ExtUtils::MakeMaker* module. On the other hand, *clean* and *test* are not, which may be somewhat counterintuitive. That's because there're some other targets depend on *clean*, *test*, or both. The type of the returned list is an array of Makefile::Target objects. PACKAGE VARIABLES $Makefile::Parser::Strict When this variable is set to true, the parser will sense syntax errors and semantic errors in the Makefile. Default off. $Makefile::Parser::Debug When this variable is set to true, the parser will enter Debug Mode. This variable is not supposed to be used directly by the user. INTERNAL METHODS post_parse Iterate the Makefile AST to apply implicit rules in the following form: %.o : %.c $(CC) -c $< solve_imp($depend) Solve implicit rules as many as possible using one target name that appears in other target's dependency list. The Makefile::Target Class This class overloads the "" operator so its instances can be automatically converted to strings using their names. METHODS "$class->new($target_name, $colon_type)" This is the constructor for class Makefile::Target. The first argument is the target name which can't be a Makefile variable, the second one is a single colon or a double colon which is used by the rule definition in Makefile. This method is usually called internally by the Makefile::Parser class. It doesn't make much sense to me if the user has a need to call it manually. "$obj->name()" It will return the name of the current Target object. "@prereqs = $obj->prereqs()" You can get the list of prerequisites (or dependencies) for the current target. If no dependency is specified in the Makefile for the target, an empty list will be returned. "@prereqs = $obj->depends()" Alias to the "prereqs" method. This method is only preserved for the sake of backward-compatibility. Please use "prereqs" instead. "$obj->commands()" This method returns a list of shell commands used to build the current target. If no shell commands is given in the Makefile, an empty array will be returned. CODE REPOSITORY For the very latest version of this module, check out the source from . There is anonymous access to all. TODO The following syntax will be implemented soon: * Add support the remaining GNU make makefile builtin functions: "origin", "value", "call", "flavor", and "eval". * Add support for recursively-expanded variables. * Implement rules with multiple targets * Serious support for "Recursively expanded" variables in GUN make * Comments that span multiple lines via trailing backslash * Lines that don't contain just comments * Literal "#" escaped by a leading backslash * The include directive * Look for 'GNUmakefile' and 'makefile' automatically * MAKEFILES Variable * MAKEFILE_LIST Variable * .VARIABLES Variable BUGS Please feel free to report bugs or send your wish-list to . SEE ALSO plmake, makesimple, Makefile::Parser::GmakeDB, Makefile::GraphViz, Make. AUTHOR Zhang "agentzh" Yichun, "" COPYRIGHT AND LICENSE Copyright (c) 2005-2011 by Zhang "agentzh" Yichun (章亦春). This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Makefile-Parser-0.215/META.yml0000644000175000001440000000147011622773340015157 0ustar agentzusers--- abstract: 'A simple parser for Makefiles' author: - "Zhang \"agentzh\" Yichun, C<< >>" build_requires: ExtUtils::MakeMaker: 6.42 IPC::Run3: 0.036 configure_requires: ExtUtils::MakeMaker: 6.42 distribution_type: module generated_by: 'Module::Install version 1.01' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: Makefile-Parser no_index: directory: - inc - t requires: Class::Accessor::Fast: 0 Class::Trigger: 0.13 Cwd: 0 File::Slurp: 0 File::Spec: 0 Filter::Util::Call: 0 List::MoreUtils: 0 List::Util: 0 Makefile::DOM: 0.005 Text::Balanced: 0 Time::HiRes: 0 perl: 5.6.1 resources: license: http://dev.perl.org/licenses/ repository: http://github.com/agentzh/makefile-parser-pm version: 0.215 Makefile-Parser-0.215/t/0000755000175000001440000000000011622773367014160 5ustar agentzusersMakefile-Parser-0.215/t/makesimple.t0000644000175000001440000002000411622772605016462 0ustar agentzusersmy $reason; BEGIN { my $line = (split /\n/, `make -v`)[0]; if ($line) { warn $line, "\n"; if ($line =~ /GNU Make (\d+\.\d+)(\s+(?:alpha|beta))?/) { my ($make_ver, $modifier) = ($1, $2); if ($make_ver < 3.81 || ($make_ver == 3.81 && $modifier)) { $reason = 'GNU make too old (at least 3.81 final is required).'; } } else { $reason = 'No GNU make found.'; } } else { $reason = 'No make found in env.'; } } use Test::Base $reason ? (skip_all => $reason) : (); use File::Slurp; use IPC::Run3; use Cwd; use lib 't/lib'; use Test::Make::Util; plan tests => 3 * blocks(); my $makefile = 'makesimple.tmp.mk'; my $saved_cwd = cwd; run { my $block = shift; my $name = $block->name; chdir $saved_cwd; system('rm -rf t/tmp'); system('mkdir t/tmp'); chdir 't/tmp'; write_file($makefile, $block->in); my ($stdout, $stderr, @options); if ($block->options) { @options = split /\s+/, $block->options; } my $touch = $block->touch; if ($touch) { for my $file (split /\s+/, $touch) { touch($file); } } run3( [$^X, "$saved_cwd/script/makesimple", '-f', $makefile, @options], undef, \$stdout, \$stderr, ); is(($? >> 8), 0, "$name - process returned the 0 status"); is $stdout, $block->out, "$name - script/makesimple generated the right output"; is $stderr, $block->err, "$name - script/makesimple generated the right error"; }; __DATA__ === TEST 1: basics --- in FOO = world all: ; @ echo hello $(FOO) --- out all: @echo hello world --- err === TEST 2: canned sequence of commands --- in define FOO @echo -touch : endef all: @$(FOO) --- out all: @echo @-touch @: --- err === TEST 3: double-colon rules --- in all: foo foo:: bar @echo $@ $< foo:: blah blue -echo $^ --- out all: foo foo:: bar @echo foo bar foo:: blah blue -echo blah blue --- err makesimple: *** No rule to make target `bar', needed by `foo'. Ignored. makesimple: *** No rule to make target `blah', needed by `foo'. Ignored. makesimple: *** No rule to make target `blue', needed by `foo'. Ignored. === TEST 4: double-colon rules (no warnings) --- in all: foo foo:: bar @echo $@ $< foo:: blah blue -echo $^ --- out all: foo foo:: bar @echo foo bar foo:: blah blue -echo blah blue --- touch: bar blah blue --- err === TEST 5: .DEFAUL_GOAL --- in .DEFAULT_GOAL = foo all: foo @echo $< foo: bah ; : --- out foo: bah : all: foo @echo foo --- err makesimple: *** No rule to make target `bah', needed by `foo'. Ignored. === TEST 6: order-only prereqs --- in all : a b \ | c \ ; echo --- out all: a b | c echo --- err makesimple: *** No rule to make target `a', needed by `all'. Ignored. makesimple: *** No rule to make target `b', needed by `all'. Ignored. makesimple: *** No rule to make target `c', needed by `all'. Ignored. === TEST 7: multi-target rules --- in foo bar: a.h foo: blah ; echo $< > $@ --- out foo: blah a.h echo blah > foo bar: a.h --- err makesimple: *** No rule to make target `blah', needed by `foo'. Ignored. makesimple: *** No rule to make target `a.h', needed by `foo'. Ignored. === TEST 8: pattern rules (no match) --- in all: foo.x bar.w %.x: %.h touch $@ %.w: %.hpp ; $(CC) --- out all: foo.x bar.w --- err makesimple: *** No rule to make target `foo.x', needed by `all'. Ignored. makesimple: *** No rule to make target `bar.w', needed by `all'. Ignored. === TEST 9: pattern rules (no warnings) --- in all: foo.x bar.w %.x: %.h touch $@ %.w: %.hpp ; $(CC) --- touch: foo.x bar.w --- out all: foo.x bar.w --- err === TEST 10: pattern rules (with match) --- in all: foo.x bar.w %.x: %.h touch $@ %.w: %.hpp ; echo '$(CC)' --- touch: foo.h bar.hpp --- out all: foo.x bar.w foo.x: foo.h touch foo.x bar.w: bar.hpp echo '' --- err === TEST 11: chained implicit rules --- in all: foo.a bar.a baz.a %.a: %.b ; @touch $@ %.b: %.d ; @touch $@ --- touch: foo.d bar.d baz.d --- out all: foo.a bar.a baz.a foo.b: foo.d @touch foo.b foo.a: foo.b @touch foo.a bar.b: bar.d @touch bar.b bar.a: bar.b @touch bar.a baz.b: baz.d @touch baz.b baz.a: baz.b @touch baz.a --- err === TEST 12: extra goals given from the command line --- in all: foo.a %.a: %.b ; @touch $@ %.b: %.d ; @touch $@ --- options: bar.a --- touch: foo.d bar.d --- out all: foo.a bar.b: bar.d @touch bar.b bar.a: bar.b @touch bar.a foo.b: foo.d @touch foo.b foo.a: foo.b @touch foo.a --- err === TEST 13: target-specific variables --- in FOO = foo default: all any all: FOO += one all: FOO += two all: BAR = bar all: FOO += three all: BAR += baz all: ; @echo $(FOO); echo $(BAR) any: ; @echo $(FOO); echo $(BAR) end --- out default: all any all: @echo foo one two three; echo bar baz any: @echo foo; echo end --- err === TEST 14: ditto (override cmd line vars) --- in all: override FOO = foo all: ; @echo $(FOO) --- options: FOO=cmd --- out all: @echo foo --- err === TEST 15: ditto (cmd line vars) (2) --- in all: FOO = foo all: ; @echo $(FOO) --- options: FOO=cmd --- out all: @echo cmd --- err === TEST 16: static pattern rules --- in CC = gcc CFLAGS = objects = foo.o bar.o all: $(objects) $(objects): %.o: %.c $(CC) -c $(CFLAGS) $< -o $@ --- out all: foo.o bar.o foo.o: foo.c gcc -c foo.c -o foo.o bar.o: bar.c gcc -c bar.c -o bar.o --- err makesimple: *** No rule to make target `foo.c', needed by `foo.o'. Ignored. makesimple: *** No rule to make target `bar.c', needed by `bar.o'. Ignored. === TEST 17: static pattern rules (no warnings) --- in CC = gcc CFLAGS = -O objects = foo.o bar.o all: $(objects) $(objects): %.o: %.c $(CC) -c $(CFLAGS) $< -o $@ --- touch: foo.c bar.c --- out all: foo.o bar.o foo.o: foo.c gcc -c -O foo.c -o foo.o bar.o: bar.c gcc -c -O bar.c -o bar.o --- err === TEST 18: conditionals - ifdef $(foo) --- in bar = true foo = bar ifdef $(foo) all: ; @echo hello else foo: bar touch $@ endif -rm blahblah --- out all: @echo hello -rm blahblah --- err === TEST 19: conditionals - ifdef foo --- in foo = bar ifdef foo all: ; @echo hello else foo: bar touch $@ endif -rm blahblah --- out all: @echo hello -rm blahblah --- err === TEST 20: conditionals - override var foo via cmd line options --- in foo = bar ifdef foo all: ; @echo hello else foo: bar touch $@ endif -rm blahblah --- options: foo= --- out foo: bar touch foo -rm blahblah --- err makesimple: *** No rule to make target `bar', needed by `foo'. Ignored. === TEST 21: functions in the first pass --- in objects = foo.o bar.o baz.o all : $(objects:.o=.c) @ echo $^ --- out all: foo.c bar.c baz.c @echo foo.c bar.c baz.c --- err makesimple: *** No rule to make target `foo.c', needed by `all'. Ignored. makesimple: *** No rule to make target `bar.c', needed by `all'. Ignored. makesimple: *** No rule to make target `baz.c', needed by `all'. Ignored. === TEST 22: functions in the second pass --- in objects = foo.o bar.o baz.o all : echo $(patsubst %.o,%.c,${objects}) --- out all: echo foo.c bar.c baz.c --- err === TEST 23: functions in the both passes --- in objects = $(sort $(wildcard *.o)) all : ; echo $(patsubst %.o,%.c,${objects}) --- touch: foo.o bar.o baz.o --- out all: echo bar.c baz.c foo.c --- err === TEST 24: commands spanning multiple lines --- in foo=hello bar=my baz=world all: @echo $(foo) \ $(bar) \ $(baz) --- out all: @echo hello \ my \ world --- err === TEST 25: dynamics --- in head = all: $(head) @echo $@ --- out all: @echo all --- err === TEST 26: dynamics (2) --- in head = all: $(head) @echo $@ --- options: head=all:bar --- out all: bar @echo all --- err makesimple: *** No rule to make target `bar', needed by `all'. Ignored. === TEST 27: ifeq/endif --- in FOO=123 ifeq ($(FOO), 123) FOO = bar endif all: ; echo $(FOO) --- out all: echo bar --- err === TEST 28: define/endef --- in define foo = bar endef all: ; echo $(foo) --- out all: echo bar --- err Makefile-Parser-0.215/t/Makefile20000644000175000001440000000041211622741050015657 0ustar agentzusersEXE_FILES = sum1.exe sum2.exe OBJ_FILES = sum1.obj sum2.obj ast++.sum.o all : $(EXE_FILES) ast++.sum.o %.exe : %.obj link /BATCH /NOLOGO $<; %.obj : %.asm masm /t $<; ast++.%.o: ast++.%.c cl /L ast++.$*.lib $< > $@ clean : $(RM_F) $(EXE_FILES) $(OBJ_FILES) Makefile-Parser-0.215/t/lib/0000755000175000001440000000000011622773367014726 5ustar agentzusersMakefile-Parser-0.215/t/lib/Test/0000755000175000001440000000000011622773367015645 5ustar agentzusersMakefile-Parser-0.215/t/lib/Test/Make/0000755000175000001440000000000011622773367016522 5ustar agentzusersMakefile-Parser-0.215/t/lib/Test/Make/Base.pm0000644000175000001440000001267611622747171017740 0ustar agentzusers#: t/Backend/Base.pm package Test::Make::Base; #use Smart::Comments; use Test::Base -Base; use Test::Make::Util; use File::Temp qw( tempdir tempfile ); use Cwd (); use File::Spec (); use FindBin; use IPC::Run3; use Time::HiRes qw( time ); #use Data::Dumper::Simple; our @EXPORT = qw( run_test run_tests create_file use_source_ditto $MAKE $PERL $SHELL $PWD ); our @EXPORT_BASE = qw(set_make set_shell set_filters); our ($SHELL, $PERL, $MAKE, $MAKEPATH, $MAKEFILE, $PWD); our (@MakeExe, %Filters); our ($UseSourceDitto, $SavedSource); # default filters for expected values #filters { # stdout => [qw< preprocess >], # stdour_like => [qw< preprocess_like >], # stderr => [qw< preprocess >], # stderr_like => [qw< preprocess_like >], #}; sub set_make ($$) { my ($env_name, $default) = @_; $MAKEPATH = $ENV{$env_name} || $default; $MAKEPATH =~ s,\\,/,g; my $stderr; run3 [split(/\s+/, $MAKEPATH), '-f', 'no/no/no'], \undef, \undef, \$stderr; #die $stderr; if ($stderr =~ /^(\S+)\s*:/) { $MAKE = $1; $MAKE =~ s/(.*[\\\/])//; } else { $MAKE = ''; } ### $MAKE #$MAKE =~ s{\\}{/}g; } sub set_shell ($$) { my ($env_name, $default) = @_; $SHELL = $ENV{$env_name} || $default; } BEGIN { if ($^O =~ / /) { $PERL = 'perl'; } else { $PERL = $^X; } #warn $PERL; # Get a clean environment clean_env(); # Delay the Test::Base filters filters_delay(); } sub use_source_ditto () { $UseSourceDitto = 1; } sub run_test ($) { my $block = shift; my $tempdir = tempdir( 'backend_XXXXXX', TMPDIR => 1, CLEANUP => 1 ); my $saved_cwd = Cwd::cwd; chdir $tempdir; $PWD = $tempdir; $PWD =~ s,\\,/,g; %::ExtraENV = (); my $filename = $block->filename; chomp $filename if $filename; my $source = $block->source; if (defined $source) { my $fh; if (not $filename) { ($fh, $filename) = tempfile( "Makefile_XXXXX", DIR => '.', UNLINK => 1 ); } else { open $fh, "> $filename" or confess("can't open $filename for writing: $!"); } $MAKEFILE = $filename; $MAKEFILE =~ s,\\,/,g; $block->run_filters; $SavedSource = $block->source if $UseSourceDitto; print $fh $block->source; close $fh; } else { $block->run_filters; $filename = $block->filename; } process_pre($block); process_touch($block); process_utouch($block); { no warnings 'uninitialized'; local %ENV = %ENV; %ENV = (%ENV, %::ExtraENV) if %::ExtraENV; run_make($block, $filename); process_post($block); process_found($block); process_not_found($block); %::ExtraENV = (); } chdir $saved_cwd; #warn "\nstderr: $stderr\nstdout: $stdout\n"; } sub run_tests () { for my $block (blocks()) { run_test($block); } } sub create_file ($$) { my ($filename, $content) = @_; my $fh; if (not $filename) { ($fh, $filename) = tempfile( "create_file_XXXXX", DIR => '.', UNLINK => 1 ); } else { open $fh, "> $filename" or confess("can't open $filename for writing: $!"); } #$content .= "\n\nSHELL=$SHELL" if $SHELL; print $fh $content; close $fh; return $filename; } sub process_touch ($) { my $block = shift; my $buf = $block->touch; return if not $buf; touch(split /\s+/, $buf); } sub process_utouch ($) { my $block = shift; my $buf = $block->utouch; return if not $buf; my @pairs = split /\s+/, $buf; ### @pairs while (@pairs) { my $time = shift @pairs; my $file = shift @pairs; utouch($time => $file); } } sub set_filters (@) { %Filters = @_; } # returns ($errcode, $stdout, $stderr) or $errcode sub run_make($$) { my ($block, $filename) = @_; my $options = $block->options || ''; my $goals = $block->goals || ''; @MakeExe = split_arg($MAKEPATH) if not @MakeExe; #warn Dumper($filename); my (@pre, @post); if ($filename and $options !~ /-f\s+\S+/) { push @pre, '-f', $filename; } if ($SHELL and $options !~ m/SHELL\s*=\s*/ and $^O eq 'MSWin32') { push @post, "SHELL=$SHELL"; } my $cmd = [ @MakeExe, @pre, process_args("$options $goals"), @post, ]; #warn Dumper($cmd); test_shell_command( $block, $cmd, %Filters ); } package Test::Make::Base::Filter; use Test::Base::Filter -Base; sub quote { qq/"$_[0]"/; } sub preprocess { my $s = shift; return if not defined $s; ### $Test::Make::Bae::MAKE $s =~ s/\#MAKE\#/$Test::Make::Base::MAKE/gsi; $s =~ s/\#MAKEPATH\#/$Test::Make::Base::MAKEPATH/gs; $s =~ s/\#MAKEFILE\#/$Test::Make::Base::MAKEFILE/gs; $s =~ s/\#PWD\#/$Test::Make::Base::PWD/gs; return $s; } sub preprocess_like { my $s = shift; return if not defined $s; $s =~ s/\#MAKE\#/quotemeta $Test::Make::Base::MAKE/gse; $s =~ s/\#MAKEPATH\#/quotemeta $Test::Make::Base::MAKEPATH/gse; $s =~ s/\#MAKEFILE\#/quotemeta $Test::Make::Base::MAKEFILE/gse; $s =~ s/\#PWD\#/quotemeta $Test::Make::Base::PWD/gse; return $s; } sub expand { my $s = shift; return if not $s; return eval(qq{"$s"}); } sub ditto { if (!defined $UseSourceDitto) { die "Error: ditto found while no use_source_ditto call.\n"; } $SavedSource; } 1; Makefile-Parser-0.215/t/lib/Test/Make/Util.pm0000644000175000001440000000361411622747171017773 0ustar agentzuserspackage Test::Make::Util; use Test::Util -Base; #use Data::Dumper::Simple; our @EXPORT = qw( process_args touch utouch clean_env ); sub process_args ($) { my $text = shift; my @args = split_arg($text); foreach (@args) { #warn "----------\n"; #warn Dumper(@args, $_); #warn "----------\n"; if (/^"(.*)"$/) { #warn "---------"; #warn qq{Pusing "$1" into args\n}; $_ = $1; process_escape( $_, q{"\\$@\#} ); } elsif (/^'(.*)'$/) { #warn " Pusing '$1' into args\n"; $_ = $1; } } return @args; } sub touch (@) { utouch(0, @_); } # Touch with a time offset. To DTRT, call touch() then use stat() to get the # access/mod time for each file and apply the offset. sub utouch ($@) { my $off = shift; my @files = @_; foreach my $file (@files) { my $in; open $in, ">>$file" or print $in '' or close $in or die "Can't touch $file: $!"; } my (@s) = stat($files[0]); utime($s[8] + $off, $s[9] + $off, @files); } # the current implementation of clean_env is buggy. haven't found a better approach sub clean_env () { # Get a clean environment my %makeENV = (); # Pull in benign variables from the user's environment # foreach (# UNIX-specific things 'TZ', 'LANG', 'TMPDIR', 'HOME', 'USER', 'LOGNAME', 'PATH', # Purify things 'PURIFYOPTIONS', # Windows NT-specific stuff 'Path', 'SystemRoot', 'TMP', 'SystemDrive', 'TEMP', 'OS', 'HOMEPATH', # DJGPP-specific stuff 'DJDIR', 'DJGPP', 'SHELL', 'COMSPEC', 'HOSTNAME', 'LFN', 'FNCASE', '387', 'EMU387', 'GROUP', 'GNU_MAKE_PATH', 'GNU_SHELL_PATH', 'INC', 'path', ) { $makeENV{$_} = $ENV{$_} if defined $ENV{$_}; } %ENV = (); %ENV = %makeENV; } 1; Makefile-Parser-0.215/t/lib/Test/Util/0000755000175000001440000000000011622773367016562 5ustar agentzusersMakefile-Parser-0.215/t/lib/Test/Util/Base.pm0000644000175000001440000000222211622747171017762 0ustar agentzusers#: facilities used by script/sh and also shared by the testers package Test::Util::Base; use Spiffy -Base; use Text::Balanced qw( gen_delimited_pat ); our @EXPORT = qw( split_arg process_escape ); our $DelimPat; BEGIN { $DelimPat = gen_delimited_pat(q{"}); } sub extract_many (@) { my $text = shift; my @flds; while (1) { #warn '@flds = ', Dumper(@flds); if ($text =~ /\G\s* ( ; | >>? | < | \|\| | \&\& ) /gcox) { push @flds, $1; } elsif ($text =~ /\G\s* ( (?:\\.)+ [^'";><\|\&\s]* )/gcox) { push @flds, $1; } elsif ($text =~ /\G\s*('[^']*')/gco) { push @flds, $1; } elsif ($text =~ /\G\s*($DelimPat)/gco) { push @flds, $1; } elsif ($text =~ /\G\s*( \S (?:[^ ; > < ' " \s \\ \| \& ]|\\.)* )/gcox) { push @flds, $1; } else { last; } } return @flds; } sub split_arg ($) { my $text = shift; return () if not defined $text; return extract_many($text); } sub process_escape (@) { return if $_[0] !~ /\\/; my $list = quotemeta $_[1]; $_[0] =~ s/\\[$list]/substr($&,1,1)/eg; } 1; Makefile-Parser-0.215/t/lib/Test/Util.pm0000644000175000001440000000667111622747171017124 0ustar agentzuserspackage Test::Util; use Test::Util::Base -Base; use Carp qw( confess ); use IPC::Run3; #use Data::Dumper::Simple; our @EXPORT = qw( test_shell_command run_shell split_arg join_list process_pre process_post process_found process_not_found ); sub process_pre ($) { my $block = shift; my $code = $block->pre; return if not $code; { package main; eval $code; } confess "error in `pre' section: $@" if $@; } sub process_post ($) { my $block = shift; my $code = $block->post; return if not $code; { package main; eval $code; } confess "error in `post' section: $@" if $@; } sub process_found ($) { my $block = shift; my $buf = $block->found; return if not $buf; my @files = split /\s+/s, $buf; for my $file (@files) { Test::More::ok( (-f $file), "File $file should be found - ".$block->name ); } } sub process_not_found ($) { my $block = shift; my $buf = $block->not_found; return if not $buf; my @files = split /\s+/s, $buf; for my $file (@files) { Test::More::ok( !(-f $file), "File $file should NOT be found - ".$block->name ); } } sub compare ($$$) { my ($got, $expected, $desc) = @_; return if not defined $expected; if ($desc =~ /\w+_like/) { Test::More::like($got, qr/^$expected$/ms, $desc); } else { Test::More::is($got, $expected, $desc); } } sub join_list (@) { my @args = @_; for (@args) { if (ref $_ eq 'ARRAY') { $_ = join('', @$_); } } return wantarray ? @args : $args[0]; } sub test_shell_command ($$@) { my $block = shift; my $cmd = shift; my %filters = @_; return if not defined $cmd; my ($stdout, $stderr); run3($cmd, \undef, \$stdout, \$stderr); my $errcode = $?; $errcode >>= 8; my $success = ($errcode == 0); my $errcode2 = $block->error_code; if ($errcode2 and $errcode2 =~ /\d+/) { $errcode2 = $&; } my $success2 = $block->success; if ($success2 and $success2 =~ /\w+/) { $success2 = lc($&); } my $name = $block->name; while (my ($key, $val) = each %filters) { #warn "$key $val"; if ($key eq 'stdout') { $stdout = $val->($stdout); } elsif ($key eq 'stderr') { $stderr = $val->($stderr); } } #warn "!!!~~~~ $stdout"; #warn "!!!~~~~ ", $block->stdout; #use Test::Differences; #eq_or_diff $stdout, $block->stdout; compare $stdout, $block->stdout, "stdout - $name"; compare $stdout, $block->stdout_like, "stdout_like - $name"; compare $stderr, $block->stderr, "stderr - $name"; compare $stderr, $block->stderr_like, "stderr_like - $name"; compare $errcode, $errcode2, "error_code - $name"; compare ( $success ? 'true' : 'false', $success2, "success - $name", ); if (not defined $block->stderr() and not defined $block->stderr_like() and $stderr) { warn $stderr; } } # returns ($error_code, $stdout, $stderr) sub run_shell (@) { my ($cmd, $verbose) = @_; #$IPC::Cmd::USE_IPC_RUN = 1; #confess Dumper($cmd); my ($stdout, $stderr); run3($cmd, \undef, \$stdout, \$stderr); my $errcode = $?; #warn "HERE!"; #warn "^^^ Output: $res[2][0]"; return ($errcode, $stdout, $stderr); } 1; Makefile-Parser-0.215/t/Makefile-Parser.t0000644000175000001440000002347211622756460017317 0ustar agentzusers#: Makefile-Parser.t #: Test script for Makefile/Parser.pm #: v0.12 #: Copyright (c) 2005 Zhang "agentzh" Yichun #: 2005-09-24 2005-10-28 use strict; use warnings; my $dir = -d 't' ? 't' : '.'; use Test::More tests => 175; use Makefile::Parser; #$Makefile::Parser::Debug = 0; $Makefile::Parser::Strict = 1; my $pack = 'Makefile::Parser'; my $mk = $pack->new; ok $mk, 'object defined'; isa_ok $mk, 'Makefile::Parser'; ok $mk->parse("$dir/Makefile"); #warn Makefile::Parser->error; is $mk->{_file}, "$dir/Makefile"; can_ok $mk, 'error'; ok !defined $pack->error; is $mk->var('FOO'), ""; is $mk->var('FOO2'), "a b c"; #exit; is $mk->var('IDU_LIB'), "inc\\Idu.pm"; is $mk->var('DISASM_LIB'), "inc\\Disasm.pm"; is $mk->var('CIDU_DLL'), "C\\idu.dll"; is $mk->var('CIDU_LIB'), "C\\idu.lib"; is $mk->var('RAW_AST'), "encoding.ast"; is $mk->var('GLOB_AST'), "..\\Config.ast"; is $mk->var('STAT_AST'), "state_mac.ast"; is $mk->var('PAT_AST'), "pat_tree.ast"; is $mk->var('CIDU_TT'), "C\\idu.c.tt"; is $mk->var('RM_F'), "perl -MExtUtils::Command -e rm_f"; is $mk->var('PAT_COVER_FILES'), "t\\pat_cover.ast.t t\\pat_cover.t"; is $mk->var('MIN_T_FILES'), join(' ', qw/t\\pat_cover.ast.t t\\pat_cover.t t\optest.t t\my_perl.exe.t t\types.cod.t t\catln.t t\exe2hex.t t\hex2bin.t t\bin2hex.t t\bin2asm.t t\ndisasmi.t t\Idu.t t\pat_tree.t t\state_mac.t t\Idu-Util.t t\cidu.t t\opname.t t\error.t t\operand.t t\01disasm.t t\02disasm.t t\03disasm.t t\disasm_cover.t t\ndisasm.t/); is $mk->var('C_PAT_COVER_FILES'), "t\\cpat_cover.ast.t t\\cpat_cover.t"; is $mk->var('C_MIN_T_FILES'), join(' ', qw/t\\cpat_cover.ast.t t\\cpat_cover.t t\cmy_perl.exe.t t\ctypes.cod.t t\cidu.t t\copname.t t\cerror.t t\coperand.t/); is $mk->var('EXE'), "hex2bin.exe"; is $mk->var('COD'), "main.cod"; is scalar($mk->vars), 26; #{ #open my $out, ">b.txt" or die $!; #print $out join("\n", sort $mk->vars); #} my @tars = $mk->targets; is scalar(@tars), 82; isa_ok $tars[0], 'Makefile::Target'; my @roots = $mk->roots; is join(' ', sort @roots), 'clean cmintest ctest doc foo foo2 mintest smoke test'; my $tar = $mk->target('all'); ok $tar; isa_ok $tar, 'Makefile::Target'; is $tar->name, 'all'; my @deps = qw/inc\\Idu.pm hex2bin.exe bin2hex.exe t_dir C\\idu.dll C\\idu.lib C\idui.exe inc\\Disasm.pm/; my @depends = $tar->depends; is scalar(@depends), scalar(@deps); is join(' ', @depends), join(' ', @deps); is join("\n", $tar->commands), ''; is $tar->colon_type, '::'; my $tar2 = $mk->target; is $tar, $tar2; $tar = $mk->target($mk->var('IDU_LIB')); ok $tar; isa_ok $tar, 'Makefile::Target'; is $tar->name, $mk->var('IDU_LIB'); @deps = ($mk->var('IDU_TT'), $mk->var('GLOB_AST'), $mk->var('STAT_AST')); @depends = $tar->depends; is scalar(@depends), scalar(@deps); is join(' ', @depends), join(' ', @deps); is join("\n", $tar->commands), 'astt -o inc\Idu.pm -t ' . join(' ', @deps); is $tar->colon_type, ':'; $tar = $mk->target('foo'); ok $tar; isa_ok $tar, 'Makefile::Target'; is $tar->name, 'foo'; @depends = $tar->depends; is scalar(@depends), 3; is join(' ', @depends), "a b \\"; is $tar->colon_type, ':'; $tar = $mk->target('foo2'); ok $tar; isa_ok $tar, 'Makefile::Target'; is $tar->name, 'foo2'; is $tar, 'foo2'; @depends = $tar->depends; is scalar(@depends), 5; is join(' ', @depends), "a b c d \\"; is $tar->colon_type, ':'; $tar = $mk->target('t_dir'); ok $tar; isa_ok $tar, 'Makefile::Target'; is $tar->name, 't_dir'; @depends = $tar->depends; is scalar(@depends), 0; is join("\n", $tar->commands), "cd t\n$0 /nologo\ncd.."; is $tar->colon_type, ':'; $tar = $mk->target('run_test'); ok $tar; isa_ok $tar, 'Makefile::Target'; is $tar->name, 'run_test'; @depends = $tar->depends; is scalar(@depends), 0; my $var = $mk->var('T_FILES'); is join("\n", $tar->commands)."\n", <<"_EOC_"; set HARNESS_OK_SLOW = 1 perl -MExtUtils::Command::MM -e "\@ARGV = map glob, \@ARGV; test_harness(0, '.', '.');" $var _EOC_ is $tar->colon_type, ':'; $tar = $mk->target('t\cpat_cover.ast.t'); ok $tar; isa_ok $tar, 'Makefile::Target'; is $tar->name, 't\cpat_cover.ast.t'; @deps = qw(coptest.tt t\pat_cover.ast.ast); @depends = $tar->depends; is scalar(@depends), scalar(@deps); is join(' ', @depends), join(' ', @deps); is join("\n", $tar->commands)."\n", <<'_EOC_'; echo $ast = { 'ast_file', 't/pat_cover.ast.ast' }; > t\tmp astt -o t\cpat_cover.ast.t -t coptest.tt t\tmp t\pat_cover.ast.ast del t\tmp _EOC_ is $tar->colon_type, ':'; ok !defined($mk->parse('Makefile.bar.bar')), 'object not defined'; like(Makefile::Parser->error, qr/Cannot open Makefile.bar.bar for reading:.*/); ok !$mk->parse('Makefile.bar.bar'); ok defined $mk, 'object defined'; like(Makefile::Parser->error, qr/Cannot open Makefile.bar.bar for reading:.*/); chdir('./t'); $mk = $pack->new; $tar = $mk->target('test'); ok $tar; isa_ok $tar, 'Makefile::Target'; is $tar->name, 'test'; is $mk->{_file}, "Makefile"; $mk = $pack->new; $tar = $mk->target; ok $tar; isa_ok $tar, 'Makefile::Target'; is $tar->name, 'all'; is $mk->{_file}, "Makefile"; $mk = $pack->new; $var = $mk->var('IDU_LIB'); ok $var; is $var, 'inc\\Idu.pm'; is $mk->{_file}, "Makefile"; $mk = $pack->new; my @vars = $mk->vars; ok @vars > 5; ok $vars[0]; is $mk->{_file}, "Makefile"; $mk = $pack->new; @tars = $mk->targets; ok @tars > 5; isa_ok $tars[0], 'Makefile::Target'; is $mk->{_file}, "Makefile"; $mk = $pack->new; @tars = $mk->roots; ok @tars > 5; is join(' ', sort @tars), 'clean cmintest ctest doc foo foo2 mintest smoke test'; #is $tars[0], 'all'; is $mk->{_file}, "Makefile"; my $mk2 = $mk->new; isa_ok $mk, 'Makefile::Parser'; chdir('..'); ##### # Makefile2 #### #warn "!!! Makefile2 !!!\n"; my $ps = Makefile::Parser->new; $ps->parse('t/Makefile2'); @roots = $ps->roots; is join(' ', sort @roots), 'all clean'; $tar = $ps->target('sum2.exe'); ok $tar; isa_ok $tar, 'Makefile::Target'; is $tar->name, 'sum2.exe'; @depends = $tar->depends; is join(' ', @depends), 'sum2.obj'; $tar = $ps->target('sum2.obj'); ok $tar; isa_ok $tar, 'Makefile::Target'; is $tar->name, 'sum2.obj'; @depends = $tar->depends; is join(' ', @depends), 'sum2.asm'; $tar = $ps->target('ast++.sum.o'); ok $tar; isa_ok $tar, 'Makefile::Target'; is $tar->name, 'ast++.sum.o'; my @cmds = $tar->commands; is join("\n", @cmds), 'cl /L ast++.sum.lib ast++.sum.c > ast++.sum.o'; @depends = $tar->depends; is join(' ', @depends), 'ast++.sum.c'; @tars = $ps->targets; is join(' ', sort @tars), 'all ast++.sum.o clean sum1.exe sum1.obj sum2.exe sum2.obj'; #### # Makefile3 #### #warn "!!! Makefile3 !!!\n"; ok $ps->parse('t/Makefile3'); @roots = $ps->roots; is join(' ', sort @roots), 'all clean'; $tar = $ps->target('sum2.exe'); ok $tar; isa_ok $tar, 'Makefile::Target'; is $tar->name, 'sum2.exe'; @depends = $tar->depends; is join(' ', @depends), 'sum2.obj'; $tar = $ps->target('sum2.obj'); ok $tar; isa_ok $tar, 'Makefile::Target'; is $tar->name, 'sum2.obj'; @depends = $tar->depends; is join(' ', @depends), 'sum2.asm'; $tar = $ps->target('ast++.sum.o'); ok $tar; isa_ok $tar, 'Makefile::Target'; is $tar->name, 'ast++.sum.o'; @depends = $tar->depends; is join(' ', @depends), 'ast++.sum.c'; @tars = $ps->targets; is join(' ', sort @tars), 'all ast++.sum.o clean sum1.exe sum1.obj sum2.exe sum2.obj'; ##### # Makefile4 #### #warn "!!! Mafefile4 !!!\n"; $ps->parse('t/Makefile4'); @roots = $ps->roots; is join(' ', sort @roots), 'all clean'; $tar = $ps->target('sum2.exe'); ok $tar; isa_ok $tar, 'Makefile::Target'; is $tar->name, 'sum2.exe'; @depends = $tar->depends; is join(' ', @depends), 'sum2.obj'; $tar = $ps->target('sum2.obj'); ok $tar; isa_ok $tar, 'Makefile::Target'; is $tar->name, 'sum2.obj'; @depends = $tar->depends; is join(' ', @depends), 'sum2.asm'; $tar = $ps->target('ast++.sum.o'); ok $tar; isa_ok $tar, 'Makefile::Target'; is $tar->name, 'ast++.sum.o'; @depends = $tar->depends; is join(' ', @depends), 'ast++.sum.c'; @tars = $ps->targets; is join(' ', sort @tars), 'all ast++.sum.o clean sum1.exe sum1.obj sum2.exe sum2.obj'; #warn "!!! Makefile5 !!!\n"; ok $ps->parse('t/Makefile5'); @roots = $ps->roots; is join(' ', sort @roots), 'abc'; $tar = $ps->target('abc'); ok $tar; isa_ok $tar, 'Makefile::Target'; is $tar->name, 'abc'; @depends = $tar->depends; is join(' ', @depends), 'foo.obj'; is join("\n", $tar->commands), "link 5 5 $0 cc > abc"; $tar = $ps->target('foo.obj'); ok $tar; isa_ok $tar, 'Makefile::Target'; is $tar->name, 'foo.obj'; @depends = $tar->depends; is join(' ', @depends), ''; is join("\n", $tar->commands), 'echo foo.obj'; ok $ps->parse('t/Makefile6'); my @tar = $ps->target('all'); is scalar(@tar), 1, 'all is a single-colon rule'; $tar = $tar[0]; is $tar->name, 'all'; @depends = $tar->prereqs; my @cmd = $tar->commands; is join(' ', @depends), 'foo bar'; is join("\n", @cmd), 'echo hallo'; @tar = $ps->target('any'); is scalar(@tar), 1, 'any is a single-colon rule'; $tar = $tar[0]; is $tar->name, 'any'; @depends = $tar->prereqs; @cmd = $tar->commands; is join(' ', @depends), 'foo hiya blah blow'; is join("\n", @cmd), "echo larry\necho howdy"; @tar = $ps->target('foo'); is scalar(@tar), 2, 'foo is a double-colon rule with 2 instances'; $tar = $tar[0]; is $tar->name, 'foo'; @depends = $tar->prereqs; @cmd = $tar->commands; is join(' ', @depends), 'blah'; is join("\n", @cmd), "echo Hi"; $tar = $tar[1]; is $tar->name, 'foo'; @depends = $tar->prereqs; @cmd = $tar->commands; is join(' ', @depends), 'howdy'; is join("\n", @cmd), "echo Hey"; ##### # Makefile5 #### #warn "!!! Mafefile4 !!!\n"; $ps = Makefile::Parser->new; ok $ps->parse('t/Makefile7'), "Makefile7 parsed"; #die Makefile::Parser->error; is $ps->var("FOO"), '1 2 3', "FOO in Makefile7 ok"; Makefile-Parser-0.215/t/ast-basic.t0000644000175000001440000000450511622741050016177 0ustar agentzusersuse strict; use warnings; use Test::More tests => 27; use Makefile::AST; my $ast = Makefile::AST->new; ok $ast, 'ast obj ok'; isa_ok $ast, 'Makefile::AST', 'ast class ok'; my $var = Makefile::AST::Variable->new({ name => 'foo', flavor => 'simple', origin => 'makefile', value => 'bar', }); $ast->add_var($var); my $var2 = $ast->get_var('foo'); is $var2, $var, 'get_var ok'; $ast->add_auto_var('@' => 'blah'); $var = $ast->get_var('@'); is $var->name, '@'; is $var->flavor, 'simple'; is $var->origin, 'automatic'; is $var->value->[0], 'blah'; my $rules = $ast->implicit_rules(); is_deeply $rules, []; my $rule = Makefile::AST::Rule::Implicit->new({ targets => ['%.pm','%.c'], normal_prereqs => ['%.cpp', '%.h', 'foo.h'], order_prereqs => ['foo', '%.bar'], commands => ['echo', 'hello', 'world'], colon => ':', }); $ast->add_implicit_rule($rule); ok $ast->target_ought_to_exist('foo'); ok ! $ast->target_ought_to_exist('bar'); ok $ast->target_ought_to_exist('foo.h'); ok !$ast->target_ought_to_exist('bar.pm'); ok !$ast->target_ought_to_exist('%.pm'); ok !$ast->target_ought_to_exist('%.c'); ok !$ast->target_ought_to_exist('%.cpp'); ok !$ast->target_ought_to_exist('%.h'); $rules = $ast->implicit_rules(); is_deeply $rules, [$rule]; my $applied = $ast->apply_explicit_rules('foo.pm'); is $applied, undef; $applied = $ast->apply_implicit_rules('foo.pm'); is $applied, undef; $ast->{targets}->{'foo.cpp'} = 1; # $ast->{targets}->{'foo.bar'} = 1; $applied = $ast->apply_implicit_rules('foo.pm'); is $applied, undef; $ast->{targets}->{'foo.cpp'} = 1; # $ast->{targets}->{'foo.bar'} = 1; $applied = $ast->apply_implicit_rules('foo.pm'); is $applied, undef; $ast->{targets}->{'foo.cpp'} = 1; $ast->{targets}->{'foo.bar'} = 1; $applied = $ast->apply_implicit_rules('foo.pm'); ok $applied; is $applied->target, 'foo.pm'; is join(' ', @{ $applied->other_targets }), 'foo.c'; is join(' ', @{ $applied->normal_prereqs }), 'foo.cpp foo.h foo.h'; is join(' ', @{ $applied->order_prereqs }), 'foo foo.bar'; $rule = Makefile::AST::Rule->new({ target => 'blah.exe', normal_prereqs => ['blah.cpp', 'blah.h'], order_prereqs => [], commands => ['echo'], colon => ':', }); $ast->add_explicit_rule($rule); my ($rule2) = $ast->apply_explicit_rules('blah.exe'); is $rule2, $rule; Makefile-Parser-0.215/t/ast-rule.t0000644000175000001440000000341711622741050016066 0ustar agentzusersuse strict; use warnings; use Test::More tests => 21; use Makefile::AST::Rule; use Makefile::AST::Rule::Implicit; { my $rule = Makefile::AST::Rule::Base->new( { normal_prereqs => [qw(a b c)], order_prereqs => [qw(d e f)], commands => [qw(c1 c2 c3)], colon => ':', } ); ok $rule, 'rule obj ok'; isa_ok $rule, 'Makefile::AST::Rule::Base', 'rule class okay'; is join(' ', @{ $rule->normal_prereqs }), 'a b c'; is join(' ', @{ $rule->order_prereqs }), 'd e f'; is join(' ', @{ $rule->commands }), 'c1 c2 c3'; is $rule->colon, ':'; } { my $rule = Makefile::AST::Rule->new( { normal_prereqs => [qw(a b c)], order_prereqs => [qw(d e f)], commands => [qw(c1 c2 c3)], colon => ':', target => 'blah', stem => 'foo', other_targets => [qw(baz boz)], } ); ok $rule, 'rule obj ok'; isa_ok $rule, 'Makefile::AST::Rule::Base', 'rule class okay'; is join(' ', @{ $rule->normal_prereqs }), 'a b c'; is join(' ', @{ $rule->order_prereqs }), 'd e f'; is join(' ', @{ $rule->commands }), 'c1 c2 c3'; is $rule->colon, ':'; is $rule->target, 'blah', 'target is readable'; $rule->target('hey'); is $rule->target, 'hey', 'target is writable'; } { my $rule = Makefile::AST::Rule::Implicit->new( { normal_prereqs => [qw(a b c)], order_prereqs => [qw(d e f)], commands => [qw(c1 c2 c3)], colon => '::', targets => [qw( %.lib %.dll )], } ); ok $rule, 'rule obj ok'; isa_ok $rule, 'Makefile::AST::Rule::Base', 'rule class okay'; is join(' ', @{ $rule->normal_prereqs }), 'a b c'; is join(' ', @{ $rule->order_prereqs }), 'd e f'; is join(' ', @{ $rule->commands }), 'c1 c2 c3'; is $rule->colon, '::'; is join(' ', @{ $rule->targets }), '%.lib %.dll', 'targets are readable'; } Makefile-Parser-0.215/t/Makefile40000644000175000001440000000045511622741050015670 0ustar agentzusersRM_F = perl -MExtUtils::Command -e rm_f EXE_FILES = sum1.exe sum2.exe OBJ_FILES = sum1.obj sum2.obj ast++.sum.o all : $(EXE_FILES) ast++.sum.o ast++.%.o: ast++.%.c cl /L ast++.$*.lib $< %.obj : %.asm masm /t $<; %.exe : %.obj link /BATCH /NOLOGO $<; clean : $(RM_F) $(EXE_FILES) $(OBJ_FILES) Makefile-Parser-0.215/t/Makefile60000644000175000001440000000020011622741050015656 0ustar agentzusersall: foo all: bar echo hallo any: foo hiya echo larry echo howdy any: blah blow foo:: blah echo Hi foo:: howdy echo Hey Makefile-Parser-0.215/t/Makefile0000644000175000001440000002517311622752524015620 0ustar agentzusersIDU_LIB = inc\Idu.pm DISASM_LIB = inc\Disasm.pm CIDU_DLL = C\idu.dll CIDU_LIB = C\idu.lib RAW_AST = encoding.ast GLOB_AST = ..\Config.ast STAT_AST = state_mac.ast PAT_AST = pat_tree.ast IDU_TT = inc\Idu.pm.tt CIDU_TT = C\idu.c.tt RM_F = perl -MExtUtils::Command -e rm_f PAT_COVER_FILES = t\pat_cover.ast.t t\pat_cover.t MIN_T_FILES = $(PAT_COVER_FILES) t\optest.t t\my_perl.exe.t t\types.cod.t \ t\catln.t t\exe2hex.t t\hex2bin.t t\bin2hex.t t\bin2asm.t t\ndisasmi.t \ t\Idu.t t\pat_tree.t t\state_mac.t t\Idu-Util.t t\cidu.t \ t\opname.t t\error.t t\operand.t t\01disasm.t t\02disasm.t t\03disasm.t \ t\disasm_cover.t t\ndisasm.t T_FILES = t\main.cod.t t\bin2hex.exe.t t\hex2bin.exe.t $(MIN_T_FILES) C_PAT_COVER_FILES = t\cpat_cover.ast.t t\cpat_cover.t C_MIN_T_FILES = $(C_PAT_COVER_FILES) t\cmy_perl.exe.t t\ctypes.cod.t \ t\cidu.t t\copname.t t\cerror.t t\coperand.t C_T_FILES = $(C_MIN_T_FILES) t\cmain.cod.t t\cbin2hex.exe.t t\chex2bin.exe.t # --------------------------------------------------------- # nmake # --------------------------------------------------------- all :: $(IDU_LIB) hex2bin.exe bin2hex.exe t_dir $(CIDU_DLL) $(CIDU_LIB) \ C\idui.exe $(DISASM_LIB) $(IDU_LIB) : $(IDU_TT) $(GLOB_AST) $(STAT_AST) astt -o $@ -t $(IDU_TT) $(GLOB_AST) $(STAT_AST) $(STAT_AST) : state_mac.pl inc\state_mac.pm $(PAT_AST) perl state_mac.pl $(PAT_AST) $(PAT_AST) : pat_tree.pl inc\pat_tree.pm inc\state_mac.pm $(RAW_AST) perl pat_tree.pl $(RAW_AST) $(RAW_AST) : encoding.txt parse_enc.pl perl parse_enc.pl encoding.txt > $@ hex2bin.exe : hex2bin.c cl /nologo /O2 hex2bin.c bin2hex.exe : bin2hex.c cl /nologo /O2 bin2hex.c t_dir : cd t $(MAKE) /nologo cd.. $(CIDU_DLL) : C\idu.obj C\idu.def link /dll /nologo /debug /out:$@ /def:C\idu.def C\idu.obj $(CIDU_LIB) : $(CIDU_DLL) C\idu.obj : C\idu.c C\idu.h cd C cl /nologo /c /I . idu.c cd .. C\idu.c : $(CIDU_TT) $(GLOB_AST) $(STAT_AST) astt -o $@ -t $(CIDU_TT) $(GLOB_AST) $(STAT_AST) C\idui.exe : C\idui.c C\idu.h $(CIDU_LIB) $(CIDU_DLL) cl /nologo /I C /o $@ C\idui.c /link $(CIDU_LIB) $(DISASM_LIB) : $(DISASM_LIB).tt $(GLOB_AST) asm_tpl.ast asm_tpl2.ast astt -o $@ -t $(DISASM_LIB).tt $(GLOB_AST) asm_tpl.ast asm_tpl2.ast asm_tpl.ast : encoding.ast gen_asm_tpl.pl perl gen_asm_tpl.pl encoding.ast asm_tpl2.ast : asm_tpl2.ast.tt tpage asm_tpl2.ast.tt > $@ foo : a b ^\ foo2 : a b \ c d ^\ FOO = \ FOO2 = a b \ c\ # --------------------------------------------------------- # nmake doc # --------------------------------------------------------- doc : encoding.html pat_cover.html state_mac.xml encoding.html : encoding.pod podhtm -s docstyle.css encoding.pod del *.tmp encoding.pod : encoding.pod.tt $(RAW_AST) astt -o $@ -t encoding.pod.tt $(RAW_AST) pat_cover.html: pat_cover.pod podhtm -o $@ -s docstyle.css pat_cover.pod del *.tmp pat_cover.pod : ast_doc.tt t\pat_cover.ast.ast astt -o $@ -t ast_doc.tt t\pat_cover.ast.ast state_mac.xml : state_mac.xml.tt state_mac.ast astt -o $@ -t state_mac.xml.tt state_mac.ast # --------------------------------------------------------- # nmake test # --------------------------------------------------------- test : all tester run_test tester : $(MIN_T_FILES) pat_cover $(T_FILES) ## # Test the tester back-end separately: t\optest.t : optest.tt t\optest.ast astt -o $@ -t optest.tt t\optest.ast optest.tt : optest.tt.tt tpage --define lang=Perl optest.tt.tt > $@ t\optest.ast : t\optest.ast.tt tpage t\optest.ast.tt > $@ ## # Applying the CL tester front-end to types.c BASE = types COD = $(BASE).cod C_FILE = $(BASE).c t\$(COD).t : optest.tt t\$(COD).ast echo $ast = { 'ast_file', 't/$(COD).ast' }; > t\tmp astt -o $@ -t optest.tt t\tmp t\$(COD).ast del t\tmp t\$(COD).ast : cod2ast.pl $(COD) perl cod2ast.pl $(COD) > $@ $(COD): $(C_FILE) cl /nologo /c /FAsc $(C_FILE) del $(BASE).obj ## # Applying the CL tester front-end to main.c and bstree.h: COD = main.cod t\$(COD).t : optest.tt t\$(COD).ast echo $ast = { 'ast_file', 't/$(COD).ast' }; > t\tmp astt -o $@ -t optest.tt t\tmp t\$(COD).ast del t\tmp t\$(COD).ast : cod2ast.pl $(COD) perl cod2ast.pl $(COD) > $@ $(COD): main.c bstree.h optest.tt cl /nologo /c /FAsc main.c del main.obj ## # Applying the Ndisasm tester front-end to bin2hex.exe: EXE = bin2hex.exe t\$(EXE).t : optest.tt t\$(EXE).ast echo $ast = { 'ast_file', 't/$(EXE).ast' }; > t\tmp astt -o $@ -t optest.tt t\tmp t\$(EXE).ast del t\tmp t\$(EXE).ast : asm2ast.pl t\$(EXE).asm perl asm2ast.pl t\$(EXE).asm > $@ t\$(EXE).asm : bin2asm.pl t\$(EXE).bin perl bin2asm.pl t\$(EXE).bin > $@ t\$(EXE).bin : hex2bin.exe t\$(EXE).hex hex2bin.exe t\$(EXE).hex $@ t\$(EXE).hex : exe2hex.pl $(EXE) perl exe2hex.pl $(EXE) > $@ ## # Applying the Ndisasm tester front-end to my_perl.exe: EXE = my_perl.exe t\$(EXE).t : optest.tt t\$(EXE).ast echo $ast = { 'ast_file', 't/$(EXE).ast' }; > t\tmp astt -o $@ -t optest.tt t\tmp t\$(EXE).ast del t\tmp t\$(EXE).ast : asm2ast.pl t\$(EXE).asm perl asm2ast.pl t\$(EXE).asm > $@ t\$(EXE).asm : bin2asm.pl t\$(EXE).bin perl bin2asm.pl t\$(EXE).bin > $@ t\$(EXE).bin : hex2bin.exe t\$(EXE).hex hex2bin.exe t\$(EXE).hex $@ t\$(EXE).hex : exe2hex.pl $(EXE) perl exe2hex.pl $(EXE) > $@ ## # Applying the PEDasm tester front-end to my_perl.exe: EXE = hex2bin.exe t\$(EXE).t : t\$(EXE).ast optest.tt echo $ast = { 'ast_file', 't/$(EXE).ast' }; > t\tmp astt -o $@ -t optest.tt t\tmp t\$(EXE).ast del t\tmp t\$(EXE).ast : pe2ast.pl t\$(EXE).asm perl pe2ast.pl -o $@ t\$(EXE).asm t\$(EXE).asm : $(EXE) pedasm -o $@ -r $(EXE) > tmp del tmp ## # Applying the Ndisasm tester front-end to the instructions # generated by pat_cover.pl: BASE = pat_cover EXE = $(BASE).ast t\$(EXE).t : optest.tt t\$(EXE).ast echo $ast = { 'ast_file', 't/$(EXE).ast' }; > t\tmp astt -o $@ -t optest.tt t\tmp t\$(EXE).ast del t\tmp t\$(EXE).ast : asm2ast.pl t\$(EXE).asm perl asm2ast.pl -d t\$(EXE).asm > $@ t\$(EXE).asm : bin2asm.pl t\$(EXE).bin perl bin2asm.pl t\$(EXE).bin > $@ t\$(EXE).bin : hex2bin.exe t\$(EXE).hex hex2bin.exe t\$(EXE).hex $@ t\$(EXE).hex : ast2hex.pl $(BASE).ast perl ast2hex.pl $(BASE).ast > $@ $(BASE).ast : pat_cover pat_cover : perl $(BASE).pl $(RAW_AST) $(GLOB_AST) > $(BASE).ast ## t\pat_cover.t : t\pat_cover.t.tt tpage --define lang=Perl t\pat_cover.t.tt > $@ t\opname.t : t\opname.t.tt tpage --define lang=Perl t\opname.t.tt > $@ t\error.t : t\error.t.tt tpage --define lang=Perl t\error.t.tt > $@ t\operand.t : t\operand.t.tt echo $ast = { 'lang', 'Perl' }; > t\tmp astt -o $@ -t t\operand.t.tt t\tmp $(GLOB_AST) del t\tmp ## run_test: set HARNESS_OK_SLOW = 1 perl -MExtUtils::Command::MM \ -e "@ARGV = map glob, @ARGV; test_harness(0, '.', '.');" $(T_FILES) # --------------------------------------------------------- # nmake mintest # --------------------------------------------------------- mintest : all min_tester run_min_test min_tester : $(MIN_T_FILES) pat_cover run_min_test : set HARNESS_OK_SLOW = 1 perl -MExtUtils::Command::MM \ -e "@ARGV = map glob, @ARGV; test_harness(0, '.', '.');" $(MIN_T_FILES) # --------------------------------------------------------- # nmake ctest # --------------------------------------------------------- ctest : all ctester run_ctest ctester : pat_cover $(C_T_FILES) $(C_PAT_COVER_FILES) run_ctest : set HARNESS_OK_SLOW = 1 perl -MExtUtils::Command::MM \ -e "@ARGV = map glob, @ARGV; test_harness(0, '.', '.');" $(C_T_FILES) BASE = cpat_cover EXE = pat_cover.ast t\cpat_cover.ast.t : coptest.tt t\$(EXE).ast echo $ast = { 'ast_file', 't/$(EXE).ast' }; > t\tmp astt -o $@ -t coptest.tt t\tmp t\$(EXE).ast del t\tmp coptest.tt : optest.tt.tt tpage --define lang=C optest.tt.tt > $@ ## # Applying the CL tester front-end to types.c BASE = types COD = $(BASE).cod C_FILE = $(BASE).c t\c$(COD).t : coptest.tt t\$(COD).ast echo $ast = { 'ast_file', 't/$(COD).ast' }; > t\tmp astt -o $@ -t coptest.tt t\tmp t\$(COD).ast del t\tmp ## # Applying the CL tester front-end to main.c and bstree.h: COD = main.cod t\c$(COD).t : coptest.tt t\$(COD).ast echo $ast = { 'ast_file', 't/$(COD).ast' }; > t\tmp astt -o $@ -t coptest.tt t\tmp t\$(COD).ast del t\tmp ## # Applying the Ndisasm tester front-end to bin2hex.exe: EXE = bin2hex.exe t\c$(EXE).t : coptest.tt t\$(EXE).ast echo $ast = { 'ast_file', 't/$(EXE).ast' }; > t\tmp astt -o $@ -t coptest.tt t\tmp t\$(EXE).ast del t\tmp ## # Applying the Ndisasm tester front-end to my_perl.exe: EXE = my_perl.exe t\c$(EXE).t : coptest.tt t\$(EXE).ast echo $ast = { 'ast_file', 't/$(EXE).ast' }; > t\tmp astt -o $@ -t coptest.tt t\tmp t\$(EXE).ast del t\tmp ## # Applying the PEDasm tester front-end to my_perl.exe: EXE = hex2bin.exe t\c$(EXE).t : t\$(EXE).ast coptest.tt echo $ast = { 'ast_file', 't/$(EXE).ast' }; > t\tmp astt -o $@ -t coptest.tt t\tmp t\$(EXE).ast del t\tmp ## t\cpat_cover.t : t\pat_cover.t.tt tpage --define lang=C t\pat_cover.t.tt > $@ t\copname.t : t\opname.t.tt tpage --define lang=C t\opname.t.tt > $@ t\cerror.t : t\error.t.tt tpage --define lang=C t\error.t.tt > $@ t\coperand.t : t\operand.t.tt echo $ast = { 'lang', 'C' }; > t\tmp astt -o $@ -t t\operand.t.tt t\tmp $(GLOB_AST) del t\tmp # --------------------------------------------------------- # nmake cmintest # --------------------------------------------------------- cmintest : all cmintester run_cmintest cmintester : pat_cover $(C_MIN_T_FILES) run_cmintest : set HARNESS_OK_SLOW = 1 perl -MExtUtils::Command::MM \ -e "@ARGV = map glob, @ARGV; test_harness(0, '.', '.');" \ $(C_MIN_T_FILES) # --------------------------------------------------------- # nmake clean # --------------------------------------------------------- clean: copy t\pat_cover.ast.ast.html ..\ /Y $(RM_F) encoding.html encoding.pod state_mac.xml encoding.ast \ pat_tree.ast state_mac.ast \ main.cod pat_cover.pod pat_cover.html types.cod \ hex2bin.exe hex2bin.obj \ bin2hex.exe bin2hex.obj \ *tmp* \ t\optest.t t\optest.ast \ *.def *.inc pat_cover.ast* t\pat_cover.ast* \ $(CIDU_DLL) C\idu.obj C\idu.exp $(CIDU_LIB) C\idu.ilk \ C\idu.pdb C\idu.c C\idui.obj C\idui.exe \ t\cpat_cover.ast.t t\cmy_perl.exe.t t\ctypes.cod.t \ t\cmain.cod.t t\cbin2hex.exe.t t\chex2bin.exe.t \ optest.tt coptest.tt $(C_PAT_COVER_FILES) $(PAT_COVER_FILES) \ t\error.t t\cerror.t t\opname.t t\copname.t t\operand.t t\coperand.t \ $(DISASM_LIB) tests.yml asm_tpl2.ast asm_tpl.ast cd t $(MAKE) /nologo clean cd .. copy ..\pat_cover.ast.ast.html t\ /Y # --------------------------------------------------------- # nmake smoke # --------------------------------------------------------- smoke : all pat_cover t\pat_cover.t t/pat_cover.ast.ast perl util\run-smoke.pl . smoke.html perl txt2html.pl t\*.t t\*.ast Makefile-Parser-0.215/t/Makefile70000644000175000001440000000005711622752163015700 0ustar agentzusersFOO = 1 \ 2\ 3 all: @echo $(FOO) Makefile-Parser-0.215/t/99-pod.t0000644000175000001440000000020211622741050015340 0ustar agentzusersuse Test::More; eval "use Test::Pod 1.00"; plan skip_all => "Test::Pod 1.00 required for testing POD" if $@; all_pod_files_ok(); Makefile-Parser-0.215/t/99-pod-coverage.t0000644000175000001440000000041211622741050017134 0ustar agentzusersuse Test::More; # XXX we need more POD... my $skip_all = 1; eval "use Test::Pod::Coverage"; plan skip_all => "We know we don't have enough POD :(" if $skip_all; plan skip_all => "Test::Pod::Coverage required for testing POD coverage" if $@; all_pod_coverage_ok(); Makefile-Parser-0.215/t/Makefile30000644000175000001440000000046511622741050015670 0ustar agentzusersRM_F = perl -MExtUtils::Command -e rm_f EXE_FILES = sum1.exe sum2.exe OBJ_FILES = sum1.obj sum2.obj ast++.sum.o .SUFFIXES: .obj .asm .exe all: $(EXE_FILES) ast++.sum.o clean: $(RM_F) $(EXE_FILES) $(OBJ_FILES) ast++.%.o: ast++.%.c cl /L ast++.$*.lib $< .asm.obj: masm $<; .obj.exe:: link /NOLOGO $<; Makefile-Parser-0.215/t/ast-var.t0000644000175000001440000000064011622741050015702 0ustar agentzusersuse strict; use warnings; use Test::More tests => 6; use Makefile::AST::Variable; my $var = Makefile::AST::Variable->new( { name => 'foo', value => 'hello', flavor => 'simple', origin => 'makefile', } ); ok $var, 'var obj ok'; isa_ok $var, 'Makefile::AST::Variable', 'var class ok'; is $var->name, 'foo'; is $var->value, 'hello'; is $var->flavor, 'simple'; is $var->origin, 'makefile'; Makefile-Parser-0.215/t/ast-stem.t0000644000175000001440000000264311622741050016067 0ustar agentzusersuse Test::Base; use Makefile::AST::StemMatch; plan tests => 8 * blocks() + 1; my $match = Makefile::AST::StemMatch->new( { pattern => '%.o', target => 'foo.c' } ); ok !defined $match, 'match failed expectedly'; run { my $block = shift; my $name = $block->name; my $pattern = $block->pattern; my $target = $block->target; my $stem = $block->stem; my $dir = $block->dir; my $notdir = $block->notdir; my $match = Makefile::AST::StemMatch->new( { pattern => $pattern, target => $target } ); ok $match, "$name - obj ok"; isa_ok $match, 'Makefile::AST::StemMatch', "$name - class ok"; is $match->pattern, $pattern, "$name - pattern ok"; is $match->target, $target, "$name - target ok"; is $match->stem, $stem, "$name - stem ok"; is $match->dir, $dir, "$name - dir ok"; is $match->notdir, $notdir, "$name - notdir ok"; my @prereqs = split /\s+/, $block->in_prereqs; map { $_ = $match->subs_stem($_) } @prereqs; is join(' ', @prereqs), $block->out_prereqs, "$name - subs_stem ok"; }; __DATA__ === TEST 1: --- pattern: %.o --- target: foo.o --- stem: foo --- dir: --- notdir: foo.o --- in_prereqs: %.c lib/%.cpp --- out_prereqs: foo.c lib/foo.cpp === TEST 2: slash in target --- pattern: %.o --- target: lib/foo.o --- stem: foo --- dir: lib/ --- notdir: foo.o --- in_prereqs: %.c lib/%.cpp --- out_prereqs: lib/foo.c lib/lib/foo.cpp Makefile-Parser-0.215/t/Makefile50000644000175000001440000000014611622741050015666 0ustar agentzusers32A = 5 52 := foo.obj CL = abc : $(52) link ${32A} $(32A) $(MAKE) $(CC) $(CL) > $@ ${52}:: echo $@ Makefile-Parser-0.215/script/0000755000175000001440000000000011622773367015221 5ustar agentzusersMakefile-Parser-0.215/script/makesimple0000755000175000001440000001303511622741141017261 0ustar agentzusers#!/usr/bin/env perl use strict; use warnings; #use lib qw(/home/agentz/gmake-db/lib /home/agentz/mdom-gmake/lib); #use Smart::Comments; #use Smart::Comments '####'; use Getopt::Long; use Makefile::Parser::GmakeDB; use IPC::Run3; use File::Slurp; use Makefile::AST::Evaluator; use List::Util 'first'; my $VERSION = $Makefile::Parser::GmakeDB::VERSION; my @DefaultMakefile = ( 'GNUmakefile', 'makefile', 'Makefile' ); my $user_makefile; my $print_version; my ($makefile, $njobs, @goals); Getopt::Long::Configure ("bundling"); GetOptions( 'f|file|makefile=s' => \$user_makefile, 'v|version' => \$print_version, ) or die "Usage: $0 [-f makefile] goals...\n"; ### $makefile ### @ARGV $Makefile::AST::Evaluator::JustPrint = 0; $Makefile::AST::Evaluator::Quiet = 1; $Makefile::AST::Evaluator::IgnoreErrors = 1; $Makefile::AST::Evaluator::AlwaysMake = 1; $Makefile::AST::Evaluator::Question = 1; if ($print_version) { print <<"_EOC_"; makesimple $VERSION _EOC_ exit 0; } our $MAKE; my @var_defs; for my $arg (@ARGV) { if ($arg =~ /(.*?)=(.*)/) { my ($var, $value) = ($1, $2); if ($var eq 'MAKE') { $MAKE = $value; } push @var_defs, $arg; } else { push @goals, $arg; } } if (!defined $MAKE) { ($MAKE = $0) =~ s/.*[\\\/]//; } $makefile = $user_makefile; if (!defined $makefile) { $makefile = first { -f $_ } @DefaultMakefile; } elsif ($makefile ne '-' and !-f $makefile) { warn "$MAKE: $makefile: No such file or directory\n"; push @goals, $makefile; # This is required } ### var defs via command line: @var_defs my $level = $ENV{MAKESIMPLE_LEVEL}; if (!defined $level) { $level = 0; } else { $level++ } #### %ENV $ENV{MAKELEVEL} = $level; $ENV{MAKESIMPLE_LEVEL} = $level; my ($stdout, $stderr); run3 ['make', '-pqRrs', '-f', $makefile, @var_defs], undef, \$stdout, \$stderr; ## $stderr my $exit_code = $? >> 8; if ($stderr and $exit_code == 2 and $stderr !~ /^make:/) { $stderr =~ s/^make:/$MAKE:/msg; warn $stderr; exit $exit_code; } if ($stderr =~ /warning: (overriding|ignoring old) commands for target/) { warn $stderr; } #die "GNU make stdout: $stdout\n"; # XXX debug only #write_file('/home/agentz/mdom-gmake/make.db', $stdout); # patch the database output to work around gmake bugs patch_database(\$stdout); # XXX debug only #write_file('/home/agentz/mdom-gmake/make.db.patched', $stdout); #if ($stdout =~ m{^\s*\./Makefile_\S+\s*:\s*[^\n]*$}ms) { # die $&; #} #print $stdout; #exit 0; $Makefile::AST::Runtime = 0; my $ast = Makefile::Parser::GmakeDB->parse(\$stdout); $ast->{makefile} = $makefile; ## $ast ## var a: $ast->get_var('a') ## var b: $ast->get_var('b') #die; my $default_goal = $ast->default_goal; push @goals, $ast->default_goal if !@goals && defined $default_goal; ### @goals if (!@goals && !defined $makefile) { warn "$MAKE: *** No targets specified and no makefile found. Stop.\n"; exit(2); } # XXX uniq @goals? push @goals, keys %{ $ast->targets }, keys %{ $ast->prereqs }; $ast->add_var(Makefile::AST::Variable->new({ name => 'MAKE', flavor => 'simple', value => ['$(MAKE)'], origin => 'default', })); my $eval = Makefile::AST::Evaluator->new($ast); my @simple_rules; my @str_for_default; my @str_for_others; Makefile::AST::Evaluator->add_trigger( firing_rule => sub { my ($self, $rule, $ast_cmds) = @_; ### $rule ### $ast_cmds my $str; my $target = $rule->target; my $colon = $rule->colon; my @normal_prereqs = @{ $rule->normal_prereqs }; my $normal_prereqs = @normal_prereqs ? " @normal_prereqs" : ''; my @order_prereqs = @{ $rule->order_prereqs }; my $order_prereqs = @order_prereqs ? " | @order_prereqs" : ''; $str .= $target.$colon.$normal_prereqs.$order_prereqs."\n"; for my $cmd (@$ast_cmds) { $str .= "\t" . $cmd->as_str . "\n"; } if ($target eq $default_goal) { push @str_for_default, $str; } else { push @str_for_others, $str; } } ); $eval->set_required_target($user_makefile) if defined $user_makefile; #warn "Default goal: $default_goal\n"; for my $goal (@goals) { ### goal: $goal $eval->make($goal); } print join "\n", @str_for_default, @str_for_others; # XXX promote the fixes on the GNU make side sub patch_database { my $ref = shift; #$$ref =~ s/(\n\S+)#/$1\\#$2/gsm; $$ref =~ s/^([^\n]*)(? simplest.mk =head1 DESCRIPTION The makesimple script is a makefile simplifier. It converts a full-fledged GNU makefile to a highly de-sugared basic makefile which is almost a call-path tree dump. =head1 SVN REPOSITORY For the very latest version of this script, check out the source from L. There is anonymous access to all. =head1 AUTHOR Zhang "agentzh" Yichun, C<< >> =head1 COPYRIGHT AND LICENSE Copyright (c) 2005-2008 by Zhang "agentzh" Yichun (agentzh). This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L, L, L. Makefile-Parser-0.215/script/plmake0000644000175000001440000000511211622741050016374 0ustar agentzusers#!/usr/bin/env perl use strict; use warnings; use constant { UP_TO_DATE => 1, REBUILT => 2, }; use Makefile::Parser; use Getopt::Std; #use Smart::Comments; $Makefile::Parser::Runtime = 1; my $parser; sub get_rule_by_target ($) { $parser->target($_[0]); } sub make ($); sub make ($) { my $goal = shift; my $rule = get_rule_by_target($goal); if (!$rule) { if (-f $goal) { return UP_TO_DATE; } else { die "No rule to build target $goal"; } } my $out_of_date = !-f $goal; for my $prereq ($rule->prereqs) { my $res = make($prereq); if ($res == REBUILT) { $out_of_date = 1; } elsif ($res == UP_TO_DATE) { if (!$out_of_date) { if (-M $prereq < -M $goal) { ### prereq file is newer: $prereq $out_of_date = 1; } } } else { die "Unexpected returned value: $res"; } } if ($out_of_date) { $rule->run_commands; return REBUILT; } return UP_TO_DATE; } my %opts; getopts('f:', \%opts); my $makefile = $opts{f} || 'Makefile'; $parser = Makefile::Parser->new; #die $parser->var('TEST_VERBOSE'); my %vars = %ENV; my @goals; for my $arg (@ARGV) { if ($arg =~ /(.*?)=(.*)/) { $vars{$1} = $2; } else { push @goals, $arg; } } $parser->parse($makefile, \%vars) or die Makefile::Parser->error; push @goals, $parser->target->name if !@goals; for my $goal (@goals) { my $res = make($goal); ### goal: $goal ### result: $res } __END__ =head1 NAME plmake - Experimental "make" utility based on Makefile::Parser =head1 VERSION This document describes plmake 0.14 released on March 10, 2007. =head1 SYNOPSIS $ plmake $ plmake test $ plmake -f Makefile all =head1 DESCRIPTION This utility mainly serves as a quick check for what does and what does not work in L. Please don't use it in production. =head1 LIMITATIONS There are quite a lot of limitations in the underlying L: =over =item * Directives are not supported. =item * Double colon rules don't work =item * Multi-target rules are not supported. =item * .PHONY is not supported. =back =head1 AUTHOR Zhang "agentzh" Yichun Eagentzh@gmail.comE =head1 COPYRIGHT Copyright (c) 2007 by Zhang "agentzh" Yichun. All rights reserved. This program is free-software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L, L, L. Makefile-Parser-0.215/script/pgmake-db0000755000175000001440000001212211622741050016754 0ustar agentzusers#!/usr/bin/env perl use strict; use warnings; use lib qw(/home/agentz/gmake-db/lib /home/agentz/mdom-gmake/lib); #use Smart::Comments; use Getopt::Long; use Makefile::Parser::GmakeDB; use IPC::Run3; use File::Slurp; use Makefile::AST::Evaluator; use List::Util 'first'; my $VERSION = $Makefile::Parser::GmakeDB::VERSION; my @DefaultMakefile = ( 'GNUmakefile', 'makefile', 'Makefile' ); my $user_makefile; my $print_version; my ($makefile, $njobs, @goals); Getopt::Long::Configure ("bundling"); GetOptions( 'f|file|makefile=s' => \$user_makefile, 'j=s' => \$njobs, # job server is not really supported 'n|just-print|dry-run|recon' => \$Makefile::AST::Evaluator::JustPrint, 's|silent|quiet' => \$Makefile::AST::Evaluator::Quiet, 'i|ignore-errors' => \$Makefile::AST::Evaluator::IgnoreErrors, 'B|always-make' => \$Makefile::AST::Evaluator::AlwaysMake, 'q|question' => \$Makefile::AST::Evaluator::Question, 'v|version' => \$print_version, ) or die "Usage: $0 [-f makefile] goals...\n"; ### $makefile ### @ARGV if ($print_version) { print <<"_EOC_"; pgmake-db $VERSION _EOC_ exit 0; } if ($Makefile::AST::Evaluator::Question) { $Makefile::AST::Evaluator::Quiet = 1; } our $MAKE; my @var_defs; for my $arg (@ARGV) { if ($arg =~ /(.*?)=(.*)/) { my ($var, $value) = ($1, $2); if ($var eq 'MAKE') { $MAKE = $value; } push @var_defs, $arg; } else { push @goals, $arg; } } if (!defined $MAKE) { ($MAKE = $0) =~ s/.*[\\\/]//; push @var_defs, "MAKE=$MAKE"; } $makefile = $user_makefile; if (!defined $makefile) { $makefile = first { -f $_ } @DefaultMakefile; } elsif ($makefile ne '-' and !-f $makefile) { warn "$MAKE: $makefile: No such file or directory\n"; push @goals, $makefile; # This is required } ### var defs via command line: @var_defs my ($stdout, $stderr); run3 ['make', '-pqRrs', '-f', $makefile, @var_defs], undef, \$stdout, \$stderr; ## $stderr my $exit_code = $? >> 8; if ($stderr and $exit_code == 2 and $stderr !~ /^make:/) { $stderr =~ s/^make:/$MAKE:/msg; warn $stderr; exit $exit_code; } if ($stderr =~ /warning: (overriding|ignoring old) commands for target/) { warn $stderr; } #die "GNU make stdout: $stdout\n"; # XXX debug only write_file('/home/agentz/mdom-gmake/make.db', $stdout); # patch the database output to work around gmake bugs patch_database(\$stdout); # XXX debug only write_file('/home/agentz/mdom-gmake/make.db.patched', $stdout); #if ($stdout =~ m{^\s*\./Makefile_\S+\s*:\s*[^\n]*$}ms) { # die $&; #} #print $stdout; #exit 0; $Makefile::AST::Runtime = 1; my $ast = Makefile::Parser::GmakeDB->parse(\$stdout); $ast->{makefile} = $makefile; ## $ast ## var a: $ast->get_var('a') ## var b: $ast->get_var('b') #die; my $default_goal = $ast->default_goal; push @goals, $ast->default_goal if !@goals && defined $default_goal; ### @goals if (!@goals && !defined $makefile) { warn "$MAKE: *** No targets specified and no makefile found. Stop.\n"; exit(2); } my $eval = Makefile::AST::Evaluator->new($ast); $eval->set_required_target($user_makefile) if defined $user_makefile; my $up_to_date = 1; for my $goal (@goals) { ### goal: $goal $Makefile::AST::Evaluator::CmdRun = 0; my $res = $eval->make($goal); ### result: $res if ($res and $res eq 'UP_TO_DATE') { if (!$Makefile::AST::Evaluator::Question and !$Makefile::AST::Evaluator::CmdRun) { print "$::MAKE: Nothing to be done for `$goal'.\n"; } } else { $up_to_date = 0; } } if ($Makefile::AST::Evaluator::Question) { exit 0 if $up_to_date; exit 1; } # XXX promote the fixes on the GNU make side sub patch_database { my $ref = shift; #$$ref =~ s/(\n\S+)#/$1\\#$2/gsm; $$ref =~ s/^([^\n]*)(? tool using L, L, and L. This script is primary for testing the whole toolchain via running GNU make's official test suite. As of this writing, pgmake-db has already passed 51% of GNU make 3.81's test suite. =head1 SVN REPOSITORY For the very latest version of this script, check out the source from L. There is anonymous access to all. =head1 AUTHOR Zhang "agentzh" Yichun C<< >> =head1 COPYRIGHT AND LICENSE Copyright (c) 2007-2008 by Zhang "agentzh" Yichun (agentzh). This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L, L, L, L, L, L, L. Makefile-Parser-0.215/inc/0000755000175000001440000000000011622773367014466 5ustar agentzusersMakefile-Parser-0.215/inc/Spiffy.pm0000644000175000001440000003623111622773337016266 0ustar agentzusers#line 1 package Spiffy; use strict; use 5.006001; use warnings; use Carp; require Exporter; our $VERSION = '0.30'; our @EXPORT = (); our @EXPORT_BASE = qw(field const stub super); our @EXPORT_OK = (@EXPORT_BASE, qw(id WWW XXX YYY ZZZ)); our %EXPORT_TAGS = (XXX => [qw(WWW XXX YYY ZZZ)]); my $stack_frame = 0; my $dump = 'yaml'; my $bases_map = {}; sub WWW; sub XXX; sub YYY; sub ZZZ; # This line is here to convince "autouse" into believing we are autousable. sub can { ($_[1] eq 'import' and caller()->isa('autouse')) ? \&Exporter::import # pacify autouse's equality test : $_[0]->SUPER::can($_[1]) # normal case } # TODO # # Exported functions like field and super should be hidden so as not to # be confused with methods that can be inherited. # sub new { my $class = shift; $class = ref($class) || $class; my $self = bless {}, $class; while (@_) { my $method = shift; $self->$method(shift); } return $self; } my $filtered_files = {}; my $filter_dump = 0; my $filter_save = 0; our $filter_result = ''; sub import { no strict 'refs'; no warnings; my $self_package = shift; # XXX Using parse_arguments here might cause confusion, because the # subclass's boolean_arguments and paired_arguments can conflict, causing # difficult debugging. Consider using something truly local. my ($args, @export_list) = do { local *boolean_arguments = sub { qw( -base -Base -mixin -selfless -XXX -dumper -yaml -filter_dump -filter_save ) }; local *paired_arguments = sub { qw(-package) }; $self_package->parse_arguments(@_); }; return spiffy_mixin_import(scalar(caller(0)), $self_package, @export_list) if $args->{-mixin}; $filter_dump = 1 if $args->{-filter_dump}; $filter_save = 1 if $args->{-filter_save}; $dump = 'yaml' if $args->{-yaml}; $dump = 'dumper' if $args->{-dumper}; local @EXPORT_BASE = @EXPORT_BASE; if ($args->{-XXX}) { push @EXPORT_BASE, @{$EXPORT_TAGS{XXX}} unless grep /^XXX$/, @EXPORT_BASE; } spiffy_filter() if ($args->{-selfless} or $args->{-Base}) and not $filtered_files->{(caller($stack_frame))[1]}++; my $caller_package = $args->{-package} || caller($stack_frame); push @{"$caller_package\::ISA"}, $self_package if $args->{-Base} or $args->{-base}; for my $class (@{all_my_bases($self_package)}) { next unless $class->isa('Spiffy'); my @export = grep { not defined &{"$caller_package\::$_"}; } ( @{"$class\::EXPORT"}, ($args->{-Base} or $args->{-base}) ? @{"$class\::EXPORT_BASE"} : (), ); my @export_ok = grep { not defined &{"$caller_package\::$_"}; } @{"$class\::EXPORT_OK"}; # Avoid calling the expensive Exporter::export # if there is nothing to do (optimization) my %exportable = map { ($_, 1) } @export, @export_ok; next unless keys %exportable; my @export_save = @{"$class\::EXPORT"}; my @export_ok_save = @{"$class\::EXPORT_OK"}; @{"$class\::EXPORT"} = @export; @{"$class\::EXPORT_OK"} = @export_ok; my @list = grep { (my $v = $_) =~ s/^[\!\:]//; $exportable{$v} or ${"$class\::EXPORT_TAGS"}{$v}; } @export_list; Exporter::export($class, $caller_package, @list); @{"$class\::EXPORT"} = @export_save; @{"$class\::EXPORT_OK"} = @export_ok_save; } } sub spiffy_filter { require Filter::Util::Call; my $done = 0; Filter::Util::Call::filter_add( sub { return 0 if $done; my ($data, $end) = ('', ''); while (my $status = Filter::Util::Call::filter_read()) { return $status if $status < 0; if (/^__(?:END|DATA)__\r?$/) { $end = $_; last; } $data .= $_; $_ = ''; } $_ = $data; my @my_subs; s[^(sub\s+\w+\s+\{)(.*\n)] [${1}my \$self = shift;$2]gm; s[^(sub\s+\w+)\s*\(\s*\)(\s+\{.*\n)] [${1}${2}]gm; s[^my\s+sub\s+(\w+)(\s+\{)(.*)((?s:.*?\n))\}\n] [push @my_subs, $1; "\$$1 = sub$2my \$self = shift;$3$4\};\n"]gem; my $preclare = ''; if (@my_subs) { $preclare = join ',', map "\$$_", @my_subs; $preclare = "my($preclare);"; } $_ = "use strict;use warnings;$preclare${_};1;\n$end"; if ($filter_dump) { print; exit } if ($filter_save) { $filter_result = $_; $_ = $filter_result; } $done = 1; } ); } sub base { push @_, -base; goto &import; } sub all_my_bases { my $class = shift; return $bases_map->{$class} if defined $bases_map->{$class}; my @bases = ($class); no strict 'refs'; for my $base_class (@{"${class}::ISA"}) { push @bases, @{all_my_bases($base_class)}; } my $used = {}; $bases_map->{$class} = [grep {not $used->{$_}++} @bases]; } my %code = ( sub_start => "sub {\n", set_default => " \$_[0]->{%s} = %s\n unless exists \$_[0]->{%s};\n", init => " return \$_[0]->{%s} = do { my \$self = \$_[0]; %s }\n" . " unless \$#_ > 0 or defined \$_[0]->{%s};\n", weak_init => " return do {\n" . " \$_[0]->{%s} = do { my \$self = \$_[0]; %s };\n" . " Scalar::Util::weaken(\$_[0]->{%s}) if ref \$_[0]->{%s};\n" . " \$_[0]->{%s};\n" . " } unless \$#_ > 0 or defined \$_[0]->{%s};\n", return_if_get => " return \$_[0]->{%s} unless \$#_ > 0;\n", set => " \$_[0]->{%s} = \$_[1];\n", weaken => " Scalar::Util::weaken(\$_[0]->{%s}) if ref \$_[0]->{%s};\n", sub_end => " return \$_[0]->{%s};\n}\n", ); sub field { my $package = caller; my ($args, @values) = do { no warnings; local *boolean_arguments = sub { (qw(-weak)) }; local *paired_arguments = sub { (qw(-package -init)) }; Spiffy->parse_arguments(@_); }; my ($field, $default) = @values; $package = $args->{-package} if defined $args->{-package}; die "Cannot have a default for a weakened field ($field)" if defined $default && $args->{-weak}; return if defined &{"${package}::$field"}; require Scalar::Util if $args->{-weak}; my $default_string = ( ref($default) eq 'ARRAY' and not @$default ) ? '[]' : (ref($default) eq 'HASH' and not keys %$default ) ? '{}' : default_as_code($default); my $code = $code{sub_start}; if ($args->{-init}) { my $fragment = $args->{-weak} ? $code{weak_init} : $code{init}; $code .= sprintf $fragment, $field, $args->{-init}, ($field) x 4; } $code .= sprintf $code{set_default}, $field, $default_string, $field if defined $default; $code .= sprintf $code{return_if_get}, $field; $code .= sprintf $code{set}, $field; $code .= sprintf $code{weaken}, $field, $field if $args->{-weak}; $code .= sprintf $code{sub_end}, $field; my $sub = eval $code; die $@ if $@; no strict 'refs'; *{"${package}::$field"} = $sub; return $code if defined wantarray; } sub default_as_code { require Data::Dumper; local $Data::Dumper::Sortkeys = 1; my $code = Data::Dumper::Dumper(shift); $code =~ s/^\$VAR1 = //; $code =~ s/;$//; return $code; } sub const { my $package = caller; my ($args, @values) = do { no warnings; local *paired_arguments = sub { (qw(-package)) }; Spiffy->parse_arguments(@_); }; my ($field, $default) = @values; $package = $args->{-package} if defined $args->{-package}; no strict 'refs'; return if defined &{"${package}::$field"}; *{"${package}::$field"} = sub { $default } } sub stub { my $package = caller; my ($args, @values) = do { no warnings; local *paired_arguments = sub { (qw(-package)) }; Spiffy->parse_arguments(@_); }; my ($field, $default) = @values; $package = $args->{-package} if defined $args->{-package}; no strict 'refs'; return if defined &{"${package}::$field"}; *{"${package}::$field"} = sub { require Carp; Carp::confess "Method $field in package $package must be subclassed"; } } sub parse_arguments { my $class = shift; my ($args, @values) = ({}, ()); my %booleans = map { ($_, 1) } $class->boolean_arguments; my %pairs = map { ($_, 1) } $class->paired_arguments; while (@_) { my $elem = shift; if (defined $elem and defined $booleans{$elem}) { $args->{$elem} = (@_ and $_[0] =~ /^[01]$/) ? shift : 1; } elsif (defined $elem and defined $pairs{$elem} and @_) { $args->{$elem} = shift; } else { push @values, $elem; } } return wantarray ? ($args, @values) : $args; } sub boolean_arguments { () } sub paired_arguments { () } # get a unique id for any node sub id { if (not ref $_[0]) { return 'undef' if not defined $_[0]; \$_[0] =~ /\((\w+)\)$/o or die; return "$1-S"; } require overload; overload::StrVal($_[0]) =~ /\((\w+)\)$/o or die; return $1; } #=============================================================================== # It's super, man. #=============================================================================== package DB; { no warnings 'redefine'; sub super_args { my @dummy = caller(@_ ? $_[0] : 2); return @DB::args; } } package Spiffy; sub super { my $method; my $frame = 1; while ($method = (caller($frame++))[3]) { $method =~ s/.*::// and last; } my @args = DB::super_args($frame); @_ = @_ ? ($args[0], @_) : @args; my $class = ref $_[0] ? ref $_[0] : $_[0]; my $caller_class = caller; my $seen = 0; my @super_classes = reverse grep { ($seen or $seen = ($_ eq $caller_class)) ? 0 : 1; } reverse @{all_my_bases($class)}; for my $super_class (@super_classes) { no strict 'refs'; next if $super_class eq $class; if (defined &{"${super_class}::$method"}) { ${"$super_class\::AUTOLOAD"} = ${"$class\::AUTOLOAD"} if $method eq 'AUTOLOAD'; return &{"${super_class}::$method"}; } } return; } #=============================================================================== # This code deserves a spanking, because it is being very naughty. # It is exchanging base.pm's import() for its own, so that people # can use base.pm with Spiffy modules, without being the wiser. #=============================================================================== my $real_base_import; my $real_mixin_import; BEGIN { require base unless defined $INC{'base.pm'}; $INC{'mixin.pm'} ||= 'Spiffy/mixin.pm'; $real_base_import = \&base::import; $real_mixin_import = \&mixin::import; no warnings; *base::import = \&spiffy_base_import; *mixin::import = \&spiffy_mixin_import; } # my $i = 0; # while (my $caller = caller($i++)) { # next unless $caller eq 'base' or $caller eq 'mixin'; # croak <isa('Spiffy'); } @base_classes; my $inheritor = caller(0); for my $base_class (@base_classes) { next if $inheritor->isa($base_class); croak "Can't mix Spiffy and non-Spiffy classes in 'use base'.\n", "See the documentation of Spiffy.pm for details\n " unless $base_class->isa('Spiffy'); $stack_frame = 1; # tell import to use different caller import($base_class, '-base'); $stack_frame = 0; } } sub mixin { my $self = shift; my $target_class = ref($self); spiffy_mixin_import($target_class, @_) } sub spiffy_mixin_import { my $target_class = shift; $target_class = caller(0) if $target_class eq 'mixin'; my $mixin_class = shift or die "Nothing to mixin"; eval "require $mixin_class"; my @roles = @_; my $pseudo_class = join '-', $target_class, $mixin_class, @roles; my %methods = spiffy_mixin_methods($mixin_class, @roles); no strict 'refs'; no warnings; @{"$pseudo_class\::ISA"} = @{"$target_class\::ISA"}; @{"$target_class\::ISA"} = ($pseudo_class); for (keys %methods) { *{"$pseudo_class\::$_"} = $methods{$_}; } } sub spiffy_mixin_methods { my $mixin_class = shift; no strict 'refs'; my %methods = spiffy_all_methods($mixin_class); map { $methods{$_} ? ($_, \ &{"$methods{$_}\::$_"}) : ($_, \ &{"$mixin_class\::$_"}) } @_ ? (get_roles($mixin_class, @_)) : (keys %methods); } sub get_roles { my $mixin_class = shift; my @roles = @_; while (grep /^!*:/, @roles) { @roles = map { s/!!//g; /^!:(.*)/ ? do { my $m = "_role_$1"; map("!$_", $mixin_class->$m); } : /^:(.*)/ ? do { my $m = "_role_$1"; ($mixin_class->$m); } : ($_) } @roles; } if (@roles and $roles[0] =~ /^!/) { my %methods = spiffy_all_methods($mixin_class); unshift @roles, keys(%methods); } my %roles; for (@roles) { s/!!//g; delete $roles{$1}, next if /^!(.*)/; $roles{$_} = 1; } keys %roles; } sub spiffy_all_methods { no strict 'refs'; my $class = shift; return if $class eq 'Spiffy'; my %methods = map { ($_, $class) } grep { defined &{"$class\::$_"} and not /^_/ } keys %{"$class\::"}; my %super_methods; %super_methods = spiffy_all_methods(${"$class\::ISA"}[0]) if @{"$class\::ISA"}; %{{%super_methods, %methods}}; } # END of naughty code. #=============================================================================== # Debugging support #=============================================================================== sub spiffy_dump { no warnings; if ($dump eq 'dumper') { require Data::Dumper; $Data::Dumper::Sortkeys = 1; $Data::Dumper::Indent = 1; return Data::Dumper::Dumper(@_); } require YAML; $YAML::UseVersion = 0; return YAML::Dump(@_) . "...\n"; } sub at_line_number { my ($file_path, $line_number) = (caller(1))[1,2]; " at $file_path line $line_number\n"; } sub WWW { warn spiffy_dump(@_) . at_line_number; return wantarray ? @_ : $_[0]; } sub XXX { die spiffy_dump(@_) . at_line_number; } sub YYY { print spiffy_dump(@_) . at_line_number; return wantarray ? @_ : $_[0]; } sub ZZZ { require Carp; Carp::confess spiffy_dump(@_); } 1; __END__ #line 1066 Makefile-Parser-0.215/inc/Module/0000755000175000001440000000000011622773367015713 5ustar agentzusersMakefile-Parser-0.215/inc/Module/Install.pm0000644000175000001440000003013511622773337017656 0ustar agentzusers#line 1 package Module::Install; # For any maintainers: # The load order for Module::Install is a bit magic. # It goes something like this... # # IF ( host has Module::Install installed, creating author mode ) { # 1. Makefile.PL calls "use inc::Module::Install" # 2. $INC{inc/Module/Install.pm} set to installed version of inc::Module::Install # 3. The installed version of inc::Module::Install loads # 4. inc::Module::Install calls "require Module::Install" # 5. The ./inc/ version of Module::Install loads # } ELSE { # 1. Makefile.PL calls "use inc::Module::Install" # 2. $INC{inc/Module/Install.pm} set to ./inc/ version of Module::Install # 3. The ./inc/ version of Module::Install loads # } use 5.005; use strict 'vars'; use Cwd (); use File::Find (); use File::Path (); use vars qw{$VERSION $MAIN}; BEGIN { # All Module::Install core packages now require synchronised versions. # This will be used to ensure we don't accidentally load old or # different versions of modules. # This is not enforced yet, but will be some time in the next few # releases once we can make sure it won't clash with custom # Module::Install extensions. $VERSION = '1.01'; # Storage for the pseudo-singleton $MAIN = undef; *inc::Module::Install::VERSION = *VERSION; @inc::Module::Install::ISA = __PACKAGE__; } sub import { my $class = shift; my $self = $class->new(@_); my $who = $self->_caller; #------------------------------------------------------------- # all of the following checks should be included in import(), # to allow "eval 'require Module::Install; 1' to test # installation of Module::Install. (RT #51267) #------------------------------------------------------------- # Whether or not inc::Module::Install is actually loaded, the # $INC{inc/Module/Install.pm} is what will still get set as long as # the caller loaded module this in the documented manner. # If not set, the caller may NOT have loaded the bundled version, and thus # they may not have a MI version that works with the Makefile.PL. This would # result in false errors or unexpected behaviour. And we don't want that. my $file = join( '/', 'inc', split /::/, __PACKAGE__ ) . '.pm'; unless ( $INC{$file} ) { die <<"END_DIE" } Please invoke ${\__PACKAGE__} with: use inc::${\__PACKAGE__}; not: use ${\__PACKAGE__}; END_DIE # This reportedly fixes a rare Win32 UTC file time issue, but # as this is a non-cross-platform XS module not in the core, # we shouldn't really depend on it. See RT #24194 for detail. # (Also, this module only supports Perl 5.6 and above). eval "use Win32::UTCFileTime" if $^O eq 'MSWin32' && $] >= 5.006; # If the script that is loading Module::Install is from the future, # then make will detect this and cause it to re-run over and over # again. This is bad. Rather than taking action to touch it (which # is unreliable on some platforms and requires write permissions) # for now we should catch this and refuse to run. if ( -f $0 ) { my $s = (stat($0))[9]; # If the modification time is only slightly in the future, # sleep briefly to remove the problem. my $a = $s - time; if ( $a > 0 and $a < 5 ) { sleep 5 } # Too far in the future, throw an error. my $t = time; if ( $s > $t ) { die <<"END_DIE" } Your installer $0 has a modification time in the future ($s > $t). This is known to create infinite loops in make. Please correct this, then run $0 again. END_DIE } # Build.PL was formerly supported, but no longer is due to excessive # difficulty in implementing every single feature twice. if ( $0 =~ /Build.PL$/i ) { die <<"END_DIE" } Module::Install no longer supports Build.PL. It was impossible to maintain duel backends, and has been deprecated. Please remove all Build.PL files and only use the Makefile.PL installer. END_DIE #------------------------------------------------------------- # To save some more typing in Module::Install installers, every... # use inc::Module::Install # ...also acts as an implicit use strict. $^H |= strict::bits(qw(refs subs vars)); #------------------------------------------------------------- unless ( -f $self->{file} ) { foreach my $key (keys %INC) { delete $INC{$key} if $key =~ /Module\/Install/; } local $^W; require "$self->{path}/$self->{dispatch}.pm"; File::Path::mkpath("$self->{prefix}/$self->{author}"); $self->{admin} = "$self->{name}::$self->{dispatch}"->new( _top => $self ); $self->{admin}->init; @_ = ($class, _self => $self); goto &{"$self->{name}::import"}; } local $^W; *{"${who}::AUTOLOAD"} = $self->autoload; $self->preload; # Unregister loader and worker packages so subdirs can use them again delete $INC{'inc/Module/Install.pm'}; delete $INC{'Module/Install.pm'}; # Save to the singleton $MAIN = $self; return 1; } sub autoload { my $self = shift; my $who = $self->_caller; my $cwd = Cwd::cwd(); my $sym = "${who}::AUTOLOAD"; $sym->{$cwd} = sub { my $pwd = Cwd::cwd(); if ( my $code = $sym->{$pwd} ) { # Delegate back to parent dirs goto &$code unless $cwd eq $pwd; } unless ($$sym =~ s/([^:]+)$//) { # XXX: it looks like we can't retrieve the missing function # via $$sym (usually $main::AUTOLOAD) in this case. # I'm still wondering if we should slurp Makefile.PL to # get some context or not ... my ($package, $file, $line) = caller; die <<"EOT"; Unknown function is found at $file line $line. Execution of $file aborted due to runtime errors. If you're a contributor to a project, you may need to install some Module::Install extensions from CPAN (or other repository). If you're a user of a module, please contact the author. EOT } my $method = $1; if ( uc($method) eq $method ) { # Do nothing return; } elsif ( $method =~ /^_/ and $self->can($method) ) { # Dispatch to the root M:I class return $self->$method(@_); } # Dispatch to the appropriate plugin unshift @_, ( $self, $1 ); goto &{$self->can('call')}; }; } sub preload { my $self = shift; unless ( $self->{extensions} ) { $self->load_extensions( "$self->{prefix}/$self->{path}", $self ); } my @exts = @{$self->{extensions}}; unless ( @exts ) { @exts = $self->{admin}->load_all_extensions; } my %seen; foreach my $obj ( @exts ) { while (my ($method, $glob) = each %{ref($obj) . '::'}) { next unless $obj->can($method); next if $method =~ /^_/; next if $method eq uc($method); $seen{$method}++; } } my $who = $self->_caller; foreach my $name ( sort keys %seen ) { local $^W; *{"${who}::$name"} = sub { ${"${who}::AUTOLOAD"} = "${who}::$name"; goto &{"${who}::AUTOLOAD"}; }; } } sub new { my ($class, %args) = @_; delete $INC{'FindBin.pm'}; { # to suppress the redefine warning local $SIG{__WARN__} = sub {}; require FindBin; } # ignore the prefix on extension modules built from top level. my $base_path = Cwd::abs_path($FindBin::Bin); unless ( Cwd::abs_path(Cwd::cwd()) eq $base_path ) { delete $args{prefix}; } return $args{_self} if $args{_self}; $args{dispatch} ||= 'Admin'; $args{prefix} ||= 'inc'; $args{author} ||= ($^O eq 'VMS' ? '_author' : '.author'); $args{bundle} ||= 'inc/BUNDLES'; $args{base} ||= $base_path; $class =~ s/^\Q$args{prefix}\E:://; $args{name} ||= $class; $args{version} ||= $class->VERSION; unless ( $args{path} ) { $args{path} = $args{name}; $args{path} =~ s!::!/!g; } $args{file} ||= "$args{base}/$args{prefix}/$args{path}.pm"; $args{wrote} = 0; bless( \%args, $class ); } sub call { my ($self, $method) = @_; my $obj = $self->load($method) or return; splice(@_, 0, 2, $obj); goto &{$obj->can($method)}; } sub load { my ($self, $method) = @_; $self->load_extensions( "$self->{prefix}/$self->{path}", $self ) unless $self->{extensions}; foreach my $obj (@{$self->{extensions}}) { return $obj if $obj->can($method); } my $admin = $self->{admin} or die <<"END_DIE"; The '$method' method does not exist in the '$self->{prefix}' path! Please remove the '$self->{prefix}' directory and run $0 again to load it. END_DIE my $obj = $admin->load($method, 1); push @{$self->{extensions}}, $obj; $obj; } sub load_extensions { my ($self, $path, $top) = @_; my $should_reload = 0; unless ( grep { ! ref $_ and lc $_ eq lc $self->{prefix} } @INC ) { unshift @INC, $self->{prefix}; $should_reload = 1; } foreach my $rv ( $self->find_extensions($path) ) { my ($file, $pkg) = @{$rv}; next if $self->{pathnames}{$pkg}; local $@; my $new = eval { local $^W; require $file; $pkg->can('new') }; unless ( $new ) { warn $@ if $@; next; } $self->{pathnames}{$pkg} = $should_reload ? delete $INC{$file} : $INC{$file}; push @{$self->{extensions}}, &{$new}($pkg, _top => $top ); } $self->{extensions} ||= []; } sub find_extensions { my ($self, $path) = @_; my @found; File::Find::find( sub { my $file = $File::Find::name; return unless $file =~ m!^\Q$path\E/(.+)\.pm\Z!is; my $subpath = $1; return if lc($subpath) eq lc($self->{dispatch}); $file = "$self->{path}/$subpath.pm"; my $pkg = "$self->{name}::$subpath"; $pkg =~ s!/!::!g; # If we have a mixed-case package name, assume case has been preserved # correctly. Otherwise, root through the file to locate the case-preserved # version of the package name. if ( $subpath eq lc($subpath) || $subpath eq uc($subpath) ) { my $content = Module::Install::_read($subpath . '.pm'); my $in_pod = 0; foreach ( split //, $content ) { $in_pod = 1 if /^=\w/; $in_pod = 0 if /^=cut/; next if ($in_pod || /^=cut/); # skip pod text next if /^\s*#/; # and comments if ( m/^\s*package\s+($pkg)\s*;/i ) { $pkg = $1; last; } } } push @found, [ $file, $pkg ]; }, $path ) if -d $path; @found; } ##################################################################### # Common Utility Functions sub _caller { my $depth = 0; my $call = caller($depth); while ( $call eq __PACKAGE__ ) { $depth++; $call = caller($depth); } return $call; } # Done in evals to avoid confusing Perl::MinimumVersion eval( $] >= 5.006 ? <<'END_NEW' : <<'END_OLD' ); die $@ if $@; sub _read { local *FH; open( FH, '<', $_[0] ) or die "open($_[0]): $!"; my $string = do { local $/; }; close FH or die "close($_[0]): $!"; return $string; } END_NEW sub _read { local *FH; open( FH, "< $_[0]" ) or die "open($_[0]): $!"; my $string = do { local $/; }; close FH or die "close($_[0]): $!"; return $string; } END_OLD sub _readperl { my $string = Module::Install::_read($_[0]); $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg; $string =~ s/(\n)\n*__(?:DATA|END)__\b.*\z/$1/s; $string =~ s/\n\n=\w+.+?\n\n=cut\b.+?\n+/\n\n/sg; return $string; } sub _readpod { my $string = Module::Install::_read($_[0]); $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg; return $string if $_[0] =~ /\.pod\z/; $string =~ s/(^|\n=cut\b.+?\n+)[^=\s].+?\n(\n=\w+|\z)/$1$2/sg; $string =~ s/\n*=pod\b[^\n]*\n+/\n\n/sg; $string =~ s/\n*=cut\b[^\n]*\n+/\n\n/sg; $string =~ s/^\n+//s; return $string; } # Done in evals to avoid confusing Perl::MinimumVersion eval( $] >= 5.006 ? <<'END_NEW' : <<'END_OLD' ); die $@ if $@; sub _write { local *FH; open( FH, '>', $_[0] ) or die "open($_[0]): $!"; foreach ( 1 .. $#_ ) { print FH $_[$_] or die "print($_[0]): $!"; } close FH or die "close($_[0]): $!"; } END_NEW sub _write { local *FH; open( FH, "> $_[0]" ) or die "open($_[0]): $!"; foreach ( 1 .. $#_ ) { print FH $_[$_] or die "print($_[0]): $!"; } close FH or die "close($_[0]): $!"; } END_OLD # _version is for processing module versions (eg, 1.03_05) not # Perl versions (eg, 5.8.1). sub _version ($) { my $s = shift || 0; my $d =()= $s =~ /(\.)/g; if ( $d >= 2 ) { # Normalise multipart versions $s =~ s/(\.)(\d{1,3})/sprintf("$1%03d",$2)/eg; } $s =~ s/^(\d+)\.?//; my $l = $1 || 0; my @v = map { $_ . '0' x (3 - length $_) } $s =~ /(\d{1,3})\D?/g; $l = $l . '.' . join '', @v if @v; return $l + 0; } sub _cmp ($$) { _version($_[0]) <=> _version($_[1]); } # Cloned from Params::Util::_CLASS sub _CLASS ($) { ( defined $_[0] and ! ref $_[0] and $_[0] =~ m/^[^\W\d]\w*(?:::\w+)*\z/s ) ? $_[0] : undef; } 1; # Copyright 2008 - 2011 Adam Kennedy. Makefile-Parser-0.215/inc/Module/Install/0000755000175000001440000000000011622773367017321 5ustar agentzusersMakefile-Parser-0.215/inc/Module/Install/Fetch.pm0000644000175000001440000000462711622773340020710 0ustar agentzusers#line 1 package Module::Install::Fetch; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.01'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub get_file { my ($self, %args) = @_; my ($scheme, $host, $path, $file) = $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return; if ( $scheme eq 'http' and ! eval { require LWP::Simple; 1 } ) { $args{url} = $args{ftp_url} or (warn("LWP support unavailable!\n"), return); ($scheme, $host, $path, $file) = $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return; } $|++; print "Fetching '$file' from $host... "; unless (eval { require Socket; Socket::inet_aton($host) }) { warn "'$host' resolve failed!\n"; return; } return unless $scheme eq 'ftp' or $scheme eq 'http'; require Cwd; my $dir = Cwd::getcwd(); chdir $args{local_dir} or return if exists $args{local_dir}; if (eval { require LWP::Simple; 1 }) { LWP::Simple::mirror($args{url}, $file); } elsif (eval { require Net::FTP; 1 }) { eval { # use Net::FTP to get past firewall my $ftp = Net::FTP->new($host, Passive => 1, Timeout => 600); $ftp->login("anonymous", 'anonymous@example.com'); $ftp->cwd($path); $ftp->binary; $ftp->get($file) or (warn("$!\n"), return); $ftp->quit; } } elsif (my $ftp = $self->can_run('ftp')) { eval { # no Net::FTP, fallback to ftp.exe require FileHandle; my $fh = FileHandle->new; local $SIG{CHLD} = 'IGNORE'; unless ($fh->open("|$ftp -n")) { warn "Couldn't open ftp: $!\n"; chdir $dir; return; } my @dialog = split(/\n/, <<"END_FTP"); open $host user anonymous anonymous\@example.com cd $path binary get $file $file quit END_FTP foreach (@dialog) { $fh->print("$_\n") } $fh->close; } } else { warn "No working 'ftp' program available!\n"; chdir $dir; return; } unless (-f $file) { warn "Fetching failed: $@\n"; chdir $dir; return; } return if exists $args{size} and -s $file != $args{size}; system($args{run}) if exists $args{run}; unlink($file) if $args{remove}; print(((!exists $args{check_for} or -e $args{check_for}) ? "done!" : "failed! ($!)"), "\n"); chdir $dir; return !$?; } 1; Makefile-Parser-0.215/inc/Module/Install/Scripts.pm0000644000175000001440000000101111622773337021274 0ustar agentzusers#line 1 package Module::Install::Scripts; use strict 'vars'; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.01'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub install_script { my $self = shift; my $args = $self->makemaker_args; my $exe = $args->{EXE_FILES} ||= []; foreach ( @_ ) { if ( -f $_ ) { push @$exe, $_; } elsif ( -d 'script' and -f "script/$_" ) { push @$exe, "script/$_"; } else { die("Cannot find script '$_'"); } } } 1; Makefile-Parser-0.215/inc/Module/Install/Makefile.pm0000644000175000001440000002703211622773337021375 0ustar agentzusers#line 1 package Module::Install::Makefile; use strict 'vars'; use ExtUtils::MakeMaker (); use Module::Install::Base (); use Fcntl qw/:flock :seek/; use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.01'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub Makefile { $_[0] } my %seen = (); sub prompt { shift; # Infinite loop protection my @c = caller(); if ( ++$seen{"$c[1]|$c[2]|$_[0]"} > 3 ) { die "Caught an potential prompt infinite loop ($c[1]|$c[2]|$_[0])"; } # In automated testing or non-interactive session, always use defaults if ( ($ENV{AUTOMATED_TESTING} or -! -t STDIN) and ! $ENV{PERL_MM_USE_DEFAULT} ) { local $ENV{PERL_MM_USE_DEFAULT} = 1; goto &ExtUtils::MakeMaker::prompt; } else { goto &ExtUtils::MakeMaker::prompt; } } # Store a cleaned up version of the MakeMaker version, # since we need to behave differently in a variety of # ways based on the MM version. my $makemaker = eval $ExtUtils::MakeMaker::VERSION; # If we are passed a param, do a "newer than" comparison. # Otherwise, just return the MakeMaker version. sub makemaker { ( @_ < 2 or $makemaker >= eval($_[1]) ) ? $makemaker : 0 } # Ripped from ExtUtils::MakeMaker 6.56, and slightly modified # as we only need to know here whether the attribute is an array # or a hash or something else (which may or may not be appendable). my %makemaker_argtype = ( C => 'ARRAY', CONFIG => 'ARRAY', # CONFIGURE => 'CODE', # ignore DIR => 'ARRAY', DL_FUNCS => 'HASH', DL_VARS => 'ARRAY', EXCLUDE_EXT => 'ARRAY', EXE_FILES => 'ARRAY', FUNCLIST => 'ARRAY', H => 'ARRAY', IMPORTS => 'HASH', INCLUDE_EXT => 'ARRAY', LIBS => 'ARRAY', # ignore '' MAN1PODS => 'HASH', MAN3PODS => 'HASH', META_ADD => 'HASH', META_MERGE => 'HASH', PL_FILES => 'HASH', PM => 'HASH', PMLIBDIRS => 'ARRAY', PMLIBPARENTDIRS => 'ARRAY', PREREQ_PM => 'HASH', CONFIGURE_REQUIRES => 'HASH', SKIP => 'ARRAY', TYPEMAPS => 'ARRAY', XS => 'HASH', # VERSION => ['version',''], # ignore # _KEEP_AFTER_FLUSH => '', clean => 'HASH', depend => 'HASH', dist => 'HASH', dynamic_lib=> 'HASH', linkext => 'HASH', macro => 'HASH', postamble => 'HASH', realclean => 'HASH', test => 'HASH', tool_autosplit => 'HASH', # special cases where you can use makemaker_append CCFLAGS => 'APPENDABLE', DEFINE => 'APPENDABLE', INC => 'APPENDABLE', LDDLFLAGS => 'APPENDABLE', LDFROM => 'APPENDABLE', ); sub makemaker_args { my ($self, %new_args) = @_; my $args = ( $self->{makemaker_args} ||= {} ); foreach my $key (keys %new_args) { if ($makemaker_argtype{$key}) { if ($makemaker_argtype{$key} eq 'ARRAY') { $args->{$key} = [] unless defined $args->{$key}; unless (ref $args->{$key} eq 'ARRAY') { $args->{$key} = [$args->{$key}] } push @{$args->{$key}}, ref $new_args{$key} eq 'ARRAY' ? @{$new_args{$key}} : $new_args{$key}; } elsif ($makemaker_argtype{$key} eq 'HASH') { $args->{$key} = {} unless defined $args->{$key}; foreach my $skey (keys %{ $new_args{$key} }) { $args->{$key}{$skey} = $new_args{$key}{$skey}; } } elsif ($makemaker_argtype{$key} eq 'APPENDABLE') { $self->makemaker_append($key => $new_args{$key}); } } else { if (defined $args->{$key}) { warn qq{MakeMaker attribute "$key" is overriden; use "makemaker_append" to append values\n}; } $args->{$key} = $new_args{$key}; } } return $args; } # For mm args that take multiple space-seperated args, # append an argument to the current list. sub makemaker_append { my $self = shift; my $name = shift; my $args = $self->makemaker_args; $args->{$name} = defined $args->{$name} ? join( ' ', $args->{$name}, @_ ) : join( ' ', @_ ); } sub build_subdirs { my $self = shift; my $subdirs = $self->makemaker_args->{DIR} ||= []; for my $subdir (@_) { push @$subdirs, $subdir; } } sub clean_files { my $self = shift; my $clean = $self->makemaker_args->{clean} ||= {}; %$clean = ( %$clean, FILES => join ' ', grep { length $_ } ($clean->{FILES} || (), @_), ); } sub realclean_files { my $self = shift; my $realclean = $self->makemaker_args->{realclean} ||= {}; %$realclean = ( %$realclean, FILES => join ' ', grep { length $_ } ($realclean->{FILES} || (), @_), ); } sub libs { my $self = shift; my $libs = ref $_[0] ? shift : [ shift ]; $self->makemaker_args( LIBS => $libs ); } sub inc { my $self = shift; $self->makemaker_args( INC => shift ); } sub _wanted_t { } sub tests_recursive { my $self = shift; my $dir = shift || 't'; unless ( -d $dir ) { die "tests_recursive dir '$dir' does not exist"; } my %tests = map { $_ => 1 } split / /, ($self->tests || ''); require File::Find; File::Find::find( sub { /\.t$/ and -f $_ and $tests{"$File::Find::dir/*.t"} = 1 }, $dir ); $self->tests( join ' ', sort keys %tests ); } sub write { my $self = shift; die "&Makefile->write() takes no arguments\n" if @_; # Check the current Perl version my $perl_version = $self->perl_version; if ( $perl_version ) { eval "use $perl_version; 1" or die "ERROR: perl: Version $] is installed, " . "but we need version >= $perl_version"; } # Make sure we have a new enough MakeMaker require ExtUtils::MakeMaker; if ( $perl_version and $self->_cmp($perl_version, '5.006') >= 0 ) { # MakeMaker can complain about module versions that include # an underscore, even though its own version may contain one! # Hence the funny regexp to get rid of it. See RT #35800 # for details. my $v = $ExtUtils::MakeMaker::VERSION =~ /^(\d+\.\d+)/; $self->build_requires( 'ExtUtils::MakeMaker' => $v ); $self->configure_requires( 'ExtUtils::MakeMaker' => $v ); } else { # Allow legacy-compatibility with 5.005 by depending on the # most recent EU:MM that supported 5.005. $self->build_requires( 'ExtUtils::MakeMaker' => 6.42 ); $self->configure_requires( 'ExtUtils::MakeMaker' => 6.42 ); } # Generate the MakeMaker params my $args = $self->makemaker_args; $args->{DISTNAME} = $self->name; $args->{NAME} = $self->module_name || $self->name; $args->{NAME} =~ s/-/::/g; $args->{VERSION} = $self->version or die <<'EOT'; ERROR: Can't determine distribution version. Please specify it explicitly via 'version' in Makefile.PL, or set a valid $VERSION in a module, and provide its file path via 'version_from' (or 'all_from' if you prefer) in Makefile.PL. EOT $DB::single = 1; if ( $self->tests ) { my @tests = split ' ', $self->tests; my %seen; $args->{test} = { TESTS => (join ' ', grep {!$seen{$_}++} @tests), }; } elsif ( $Module::Install::ExtraTests::use_extratests ) { # Module::Install::ExtraTests doesn't set $self->tests and does its own tests via harness. # So, just ignore our xt tests here. } elsif ( -d 'xt' and ($Module::Install::AUTHOR or $ENV{RELEASE_TESTING}) ) { $args->{test} = { TESTS => join( ' ', map { "$_/*.t" } grep { -d $_ } qw{ t xt } ), }; } if ( $] >= 5.005 ) { $args->{ABSTRACT} = $self->abstract; $args->{AUTHOR} = join ', ', @{$self->author || []}; } if ( $self->makemaker(6.10) ) { $args->{NO_META} = 1; #$args->{NO_MYMETA} = 1; } if ( $self->makemaker(6.17) and $self->sign ) { $args->{SIGN} = 1; } unless ( $self->is_admin ) { delete $args->{SIGN}; } if ( $self->makemaker(6.31) and $self->license ) { $args->{LICENSE} = $self->license; } my $prereq = ($args->{PREREQ_PM} ||= {}); %$prereq = ( %$prereq, map { @$_ } # flatten [module => version] map { @$_ } grep $_, ($self->requires) ); # Remove any reference to perl, PREREQ_PM doesn't support it delete $args->{PREREQ_PM}->{perl}; # Merge both kinds of requires into BUILD_REQUIRES my $build_prereq = ($args->{BUILD_REQUIRES} ||= {}); %$build_prereq = ( %$build_prereq, map { @$_ } # flatten [module => version] map { @$_ } grep $_, ($self->configure_requires, $self->build_requires) ); # Remove any reference to perl, BUILD_REQUIRES doesn't support it delete $args->{BUILD_REQUIRES}->{perl}; # Delete bundled dists from prereq_pm, add it to Makefile DIR my $subdirs = ($args->{DIR} || []); if ($self->bundles) { my %processed; foreach my $bundle (@{ $self->bundles }) { my ($mod_name, $dist_dir) = @$bundle; delete $prereq->{$mod_name}; $dist_dir = File::Basename::basename($dist_dir); # dir for building this module if (not exists $processed{$dist_dir}) { if (-d $dist_dir) { # List as sub-directory to be processed by make push @$subdirs, $dist_dir; } # Else do nothing: the module is already present on the system $processed{$dist_dir} = undef; } } } unless ( $self->makemaker('6.55_03') ) { %$prereq = (%$prereq,%$build_prereq); delete $args->{BUILD_REQUIRES}; } if ( my $perl_version = $self->perl_version ) { eval "use $perl_version; 1" or die "ERROR: perl: Version $] is installed, " . "but we need version >= $perl_version"; if ( $self->makemaker(6.48) ) { $args->{MIN_PERL_VERSION} = $perl_version; } } if ($self->installdirs) { warn qq{old INSTALLDIRS (probably set by makemaker_args) is overriden by installdirs\n} if $args->{INSTALLDIRS}; $args->{INSTALLDIRS} = $self->installdirs; } my %args = map { ( $_ => $args->{$_} ) } grep {defined($args->{$_} ) } keys %$args; my $user_preop = delete $args{dist}->{PREOP}; if ( my $preop = $self->admin->preop($user_preop) ) { foreach my $key ( keys %$preop ) { $args{dist}->{$key} = $preop->{$key}; } } my $mm = ExtUtils::MakeMaker::WriteMakefile(%args); $self->fix_up_makefile($mm->{FIRST_MAKEFILE} || 'Makefile'); } sub fix_up_makefile { my $self = shift; my $makefile_name = shift; my $top_class = ref($self->_top) || ''; my $top_version = $self->_top->VERSION || ''; my $preamble = $self->preamble ? "# Preamble by $top_class $top_version\n" . $self->preamble : ''; my $postamble = "# Postamble by $top_class $top_version\n" . ($self->postamble || ''); local *MAKEFILE; open MAKEFILE, "+< $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!"; eval { flock MAKEFILE, LOCK_EX }; my $makefile = do { local $/; }; $makefile =~ s/\b(test_harness\(\$\(TEST_VERBOSE\), )/$1'inc', /; $makefile =~ s/( -I\$\(INST_ARCHLIB\))/ -Iinc$1/g; $makefile =~ s/( "-I\$\(INST_LIB\)")/ "-Iinc"$1/g; $makefile =~ s/^(FULLPERL = .*)/$1 "-Iinc"/m; $makefile =~ s/^(PERL = .*)/$1 "-Iinc"/m; # Module::Install will never be used to build the Core Perl # Sometimes PERL_LIB and PERL_ARCHLIB get written anyway, which breaks # PREFIX/PERL5LIB, and thus, install_share. Blank them if they exist $makefile =~ s/^PERL_LIB = .+/PERL_LIB =/m; #$makefile =~ s/^PERL_ARCHLIB = .+/PERL_ARCHLIB =/m; # Perl 5.005 mentions PERL_LIB explicitly, so we have to remove that as well. $makefile =~ s/(\"?)-I\$\(PERL_LIB\)\1//g; # XXX - This is currently unused; not sure if it breaks other MM-users # $makefile =~ s/^pm_to_blib\s+:\s+/pm_to_blib :: /mg; seek MAKEFILE, 0, SEEK_SET; truncate MAKEFILE, 0; print MAKEFILE "$preamble$makefile$postamble" or die $!; close MAKEFILE or die $!; 1; } sub preamble { my ($self, $text) = @_; $self->{preamble} = $text . $self->{preamble} if defined $text; $self->{preamble}; } sub postamble { my ($self, $text) = @_; $self->{postamble} ||= $self->admin->postamble; $self->{postamble} .= $text if defined $text; $self->{postamble} } 1; __END__ #line 541 Makefile-Parser-0.215/inc/Module/Install/TestBase.pm0000644000175000001440000000103311622773337021363 0ustar agentzusers#line 1 package Module::Install::TestBase; use strict; use warnings; use Module::Install::Base; use vars qw($VERSION @ISA); BEGIN { $VERSION = '0.60'; @ISA = 'Module::Install::Base'; } sub use_test_base { my $self = shift; $self->include('Test::Base'); $self->include('Test::Base::Filter'); $self->include('Spiffy'); $self->include('Test::More'); $self->include('Test::Builder'); $self->include('Test::Builder::Module'); $self->requires('Filter::Util::Call'); } 1; =encoding utf8 #line 70 Makefile-Parser-0.215/inc/Module/Install/WriteAll.pm0000644000175000001440000000237611622773340021401 0ustar agentzusers#line 1 package Module::Install::WriteAll; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.01'; @ISA = qw{Module::Install::Base}; $ISCORE = 1; } sub WriteAll { my $self = shift; my %args = ( meta => 1, sign => 0, inline => 0, check_nmake => 1, @_, ); $self->sign(1) if $args{sign}; $self->admin->WriteAll(%args) if $self->is_admin; $self->check_nmake if $args{check_nmake}; unless ( $self->makemaker_args->{PL_FILES} ) { # XXX: This still may be a bit over-defensive... unless ($self->makemaker(6.25)) { $self->makemaker_args( PL_FILES => {} ) if -f 'Build.PL'; } } # Until ExtUtils::MakeMaker support MYMETA.yml, make sure # we clean it up properly ourself. $self->realclean_files('MYMETA.yml'); if ( $args{inline} ) { $self->Inline->write; } else { $self->Makefile->write; } # The Makefile write process adds a couple of dependencies, # so write the META.yml files after the Makefile. if ( $args{meta} ) { $self->Meta->write; } # Experimental support for MYMETA if ( $ENV{X_MYMETA} ) { if ( $ENV{X_MYMETA} eq 'JSON' ) { $self->Meta->write_mymeta_json; } else { $self->Meta->write_mymeta_yaml; } } return 1; } 1; Makefile-Parser-0.215/inc/Module/Install/Base.pm0000644000175000001440000000214711622773337020532 0ustar agentzusers#line 1 package Module::Install::Base; use strict 'vars'; use vars qw{$VERSION}; BEGIN { $VERSION = '1.01'; } # Suspend handler for "redefined" warnings BEGIN { my $w = $SIG{__WARN__}; $SIG{__WARN__} = sub { $w }; } #line 42 sub new { my $class = shift; unless ( defined &{"${class}::call"} ) { *{"${class}::call"} = sub { shift->_top->call(@_) }; } unless ( defined &{"${class}::load"} ) { *{"${class}::load"} = sub { shift->_top->load(@_) }; } bless { @_ }, $class; } #line 61 sub AUTOLOAD { local $@; my $func = eval { shift->_top->autoload } or return; goto &$func; } #line 75 sub _top { $_[0]->{_top}; } #line 90 sub admin { $_[0]->_top->{admin} or Module::Install::Base::FakeAdmin->new; } #line 106 sub is_admin { ! $_[0]->admin->isa('Module::Install::Base::FakeAdmin'); } sub DESTROY {} package Module::Install::Base::FakeAdmin; use vars qw{$VERSION}; BEGIN { $VERSION = $Module::Install::Base::VERSION; } my $fake; sub new { $fake ||= bless(\@_, $_[0]); } sub AUTOLOAD {} sub DESTROY {} # Restore warning handler BEGIN { $SIG{__WARN__} = $SIG{__WARN__}->(); } 1; #line 159 Makefile-Parser-0.215/inc/Module/Install/Metadata.pm0000644000175000001440000004312311622773337021377 0ustar agentzusers#line 1 package Module::Install::Metadata; use strict 'vars'; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.01'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } my @boolean_keys = qw{ sign }; my @scalar_keys = qw{ name module_name abstract version distribution_type tests installdirs }; my @tuple_keys = qw{ configure_requires build_requires requires recommends bundles resources }; my @resource_keys = qw{ homepage bugtracker repository }; my @array_keys = qw{ keywords author }; *authors = \&author; sub Meta { shift } sub Meta_BooleanKeys { @boolean_keys } sub Meta_ScalarKeys { @scalar_keys } sub Meta_TupleKeys { @tuple_keys } sub Meta_ResourceKeys { @resource_keys } sub Meta_ArrayKeys { @array_keys } foreach my $key ( @boolean_keys ) { *$key = sub { my $self = shift; if ( defined wantarray and not @_ ) { return $self->{values}->{$key}; } $self->{values}->{$key} = ( @_ ? $_[0] : 1 ); return $self; }; } foreach my $key ( @scalar_keys ) { *$key = sub { my $self = shift; return $self->{values}->{$key} if defined wantarray and !@_; $self->{values}->{$key} = shift; return $self; }; } foreach my $key ( @array_keys ) { *$key = sub { my $self = shift; return $self->{values}->{$key} if defined wantarray and !@_; $self->{values}->{$key} ||= []; push @{$self->{values}->{$key}}, @_; return $self; }; } foreach my $key ( @resource_keys ) { *$key = sub { my $self = shift; unless ( @_ ) { return () unless $self->{values}->{resources}; return map { $_->[1] } grep { $_->[0] eq $key } @{ $self->{values}->{resources} }; } return $self->{values}->{resources}->{$key} unless @_; my $uri = shift or die( "Did not provide a value to $key()" ); $self->resources( $key => $uri ); return 1; }; } foreach my $key ( grep { $_ ne "resources" } @tuple_keys) { *$key = sub { my $self = shift; return $self->{values}->{$key} unless @_; my @added; while ( @_ ) { my $module = shift or last; my $version = shift || 0; push @added, [ $module, $version ]; } push @{ $self->{values}->{$key} }, @added; return map {@$_} @added; }; } # Resource handling my %lc_resource = map { $_ => 1 } qw{ homepage license bugtracker repository }; sub resources { my $self = shift; while ( @_ ) { my $name = shift or last; my $value = shift or next; if ( $name eq lc $name and ! $lc_resource{$name} ) { die("Unsupported reserved lowercase resource '$name'"); } $self->{values}->{resources} ||= []; push @{ $self->{values}->{resources} }, [ $name, $value ]; } $self->{values}->{resources}; } # Aliases for build_requires that will have alternative # meanings in some future version of META.yml. sub test_requires { shift->build_requires(@_) } sub install_requires { shift->build_requires(@_) } # Aliases for installdirs options sub install_as_core { $_[0]->installdirs('perl') } sub install_as_cpan { $_[0]->installdirs('site') } sub install_as_site { $_[0]->installdirs('site') } sub install_as_vendor { $_[0]->installdirs('vendor') } sub dynamic_config { my $self = shift; unless ( @_ ) { warn "You MUST provide an explicit true/false value to dynamic_config\n"; return $self; } $self->{values}->{dynamic_config} = $_[0] ? 1 : 0; return 1; } sub perl_version { my $self = shift; return $self->{values}->{perl_version} unless @_; my $version = shift or die( "Did not provide a value to perl_version()" ); # Normalize the version $version = $self->_perl_version($version); # We don't support the reall old versions unless ( $version >= 5.005 ) { die "Module::Install only supports 5.005 or newer (use ExtUtils::MakeMaker)\n"; } $self->{values}->{perl_version} = $version; } sub all_from { my ( $self, $file ) = @_; unless ( defined($file) ) { my $name = $self->name or die( "all_from called with no args without setting name() first" ); $file = join('/', 'lib', split(/-/, $name)) . '.pm'; $file =~ s{.*/}{} unless -e $file; unless ( -e $file ) { die("all_from cannot find $file from $name"); } } unless ( -f $file ) { die("The path '$file' does not exist, or is not a file"); } $self->{values}{all_from} = $file; # Some methods pull from POD instead of code. # If there is a matching .pod, use that instead my $pod = $file; $pod =~ s/\.pm$/.pod/i; $pod = $file unless -e $pod; # Pull the different values $self->name_from($file) unless $self->name; $self->version_from($file) unless $self->version; $self->perl_version_from($file) unless $self->perl_version; $self->author_from($pod) unless @{$self->author || []}; $self->license_from($pod) unless $self->license; $self->abstract_from($pod) unless $self->abstract; return 1; } sub provides { my $self = shift; my $provides = ( $self->{values}->{provides} ||= {} ); %$provides = (%$provides, @_) if @_; return $provides; } sub auto_provides { my $self = shift; return $self unless $self->is_admin; unless (-e 'MANIFEST') { warn "Cannot deduce auto_provides without a MANIFEST, skipping\n"; return $self; } # Avoid spurious warnings as we are not checking manifest here. local $SIG{__WARN__} = sub {1}; require ExtUtils::Manifest; local *ExtUtils::Manifest::manicheck = sub { return }; require Module::Build; my $build = Module::Build->new( dist_name => $self->name, dist_version => $self->version, license => $self->license, ); $self->provides( %{ $build->find_dist_packages || {} } ); } sub feature { my $self = shift; my $name = shift; my $features = ( $self->{values}->{features} ||= [] ); my $mods; if ( @_ == 1 and ref( $_[0] ) ) { # The user used ->feature like ->features by passing in the second # argument as a reference. Accomodate for that. $mods = $_[0]; } else { $mods = \@_; } my $count = 0; push @$features, ( $name => [ map { ref($_) ? ( ref($_) eq 'HASH' ) ? %$_ : @$_ : $_ } @$mods ] ); return @$features; } sub features { my $self = shift; while ( my ( $name, $mods ) = splice( @_, 0, 2 ) ) { $self->feature( $name, @$mods ); } return $self->{values}->{features} ? @{ $self->{values}->{features} } : (); } sub no_index { my $self = shift; my $type = shift; push @{ $self->{values}->{no_index}->{$type} }, @_ if $type; return $self->{values}->{no_index}; } sub read { my $self = shift; $self->include_deps( 'YAML::Tiny', 0 ); require YAML::Tiny; my $data = YAML::Tiny::LoadFile('META.yml'); # Call methods explicitly in case user has already set some values. while ( my ( $key, $value ) = each %$data ) { next unless $self->can($key); if ( ref $value eq 'HASH' ) { while ( my ( $module, $version ) = each %$value ) { $self->can($key)->($self, $module => $version ); } } else { $self->can($key)->($self, $value); } } return $self; } sub write { my $self = shift; return $self unless $self->is_admin; $self->admin->write_meta; return $self; } sub version_from { require ExtUtils::MM_Unix; my ( $self, $file ) = @_; $self->version( ExtUtils::MM_Unix->parse_version($file) ); # for version integrity check $self->makemaker_args( VERSION_FROM => $file ); } sub abstract_from { require ExtUtils::MM_Unix; my ( $self, $file ) = @_; $self->abstract( bless( { DISTNAME => $self->name }, 'ExtUtils::MM_Unix' )->parse_abstract($file) ); } # Add both distribution and module name sub name_from { my ($self, $file) = @_; if ( Module::Install::_read($file) =~ m/ ^ \s* package \s* ([\w:]+) \s* ; /ixms ) { my ($name, $module_name) = ($1, $1); $name =~ s{::}{-}g; $self->name($name); unless ( $self->module_name ) { $self->module_name($module_name); } } else { die("Cannot determine name from $file\n"); } } sub _extract_perl_version { if ( $_[0] =~ m/ ^\s* (?:use|require) \s* v? ([\d_\.]+) \s* ; /ixms ) { my $perl_version = $1; $perl_version =~ s{_}{}g; return $perl_version; } else { return; } } sub perl_version_from { my $self = shift; my $perl_version=_extract_perl_version(Module::Install::_read($_[0])); if ($perl_version) { $self->perl_version($perl_version); } else { warn "Cannot determine perl version info from $_[0]\n"; return; } } sub author_from { my $self = shift; my $content = Module::Install::_read($_[0]); if ($content =~ m/ =head \d \s+ (?:authors?)\b \s* ([^\n]*) | =head \d \s+ (?:licen[cs]e|licensing|copyright|legal)\b \s* .*? copyright .*? \d\d\d[\d.]+ \s* (?:\bby\b)? \s* ([^\n]*) /ixms) { my $author = $1 || $2; # XXX: ugly but should work anyway... if (eval "require Pod::Escapes; 1") { # Pod::Escapes has a mapping table. # It's in core of perl >= 5.9.3, and should be installed # as one of the Pod::Simple's prereqs, which is a prereq # of Pod::Text 3.x (see also below). $author =~ s{ E<( (\d+) | ([A-Za-z]+) )> } { defined $2 ? chr($2) : defined $Pod::Escapes::Name2character_number{$1} ? chr($Pod::Escapes::Name2character_number{$1}) : do { warn "Unknown escape: E<$1>"; "E<$1>"; }; }gex; } elsif (eval "require Pod::Text; 1" && $Pod::Text::VERSION < 3) { # Pod::Text < 3.0 has yet another mapping table, # though the table name of 2.x and 1.x are different. # (1.x is in core of Perl < 5.6, 2.x is in core of # Perl < 5.9.3) my $mapping = ($Pod::Text::VERSION < 2) ? \%Pod::Text::HTML_Escapes : \%Pod::Text::ESCAPES; $author =~ s{ E<( (\d+) | ([A-Za-z]+) )> } { defined $2 ? chr($2) : defined $mapping->{$1} ? $mapping->{$1} : do { warn "Unknown escape: E<$1>"; "E<$1>"; }; }gex; } else { $author =~ s{E}{<}g; $author =~ s{E}{>}g; } $self->author($author); } else { warn "Cannot determine author info from $_[0]\n"; } } #Stolen from M::B my %license_urls = ( perl => 'http://dev.perl.org/licenses/', apache => 'http://apache.org/licenses/LICENSE-2.0', apache_1_1 => 'http://apache.org/licenses/LICENSE-1.1', artistic => 'http://opensource.org/licenses/artistic-license.php', artistic_2 => 'http://opensource.org/licenses/artistic-license-2.0.php', lgpl => 'http://opensource.org/licenses/lgpl-license.php', lgpl2 => 'http://opensource.org/licenses/lgpl-2.1.php', lgpl3 => 'http://opensource.org/licenses/lgpl-3.0.html', bsd => 'http://opensource.org/licenses/bsd-license.php', gpl => 'http://opensource.org/licenses/gpl-license.php', gpl2 => 'http://opensource.org/licenses/gpl-2.0.php', gpl3 => 'http://opensource.org/licenses/gpl-3.0.html', mit => 'http://opensource.org/licenses/mit-license.php', mozilla => 'http://opensource.org/licenses/mozilla1.1.php', open_source => undef, unrestricted => undef, restrictive => undef, unknown => undef, ); sub license { my $self = shift; return $self->{values}->{license} unless @_; my $license = shift or die( 'Did not provide a value to license()' ); $license = __extract_license($license) || lc $license; $self->{values}->{license} = $license; # Automatically fill in license URLs if ( $license_urls{$license} ) { $self->resources( license => $license_urls{$license} ); } return 1; } sub _extract_license { my $pod = shift; my $matched; return __extract_license( ($matched) = $pod =~ m/ (=head \d \s+ L(?i:ICEN[CS]E|ICENSING)\b.*?) (=head \d.*|=cut.*|)\z /xms ) || __extract_license( ($matched) = $pod =~ m/ (=head \d \s+ (?:C(?i:OPYRIGHTS?)|L(?i:EGAL))\b.*?) (=head \d.*|=cut.*|)\z /xms ); } sub __extract_license { my $license_text = shift or return; my @phrases = ( '(?:under )?the same (?:terms|license) as (?:perl|the perl (?:\d )?programming language)' => 'perl', 1, '(?:under )?the terms of (?:perl|the perl programming language) itself' => 'perl', 1, 'Artistic and GPL' => 'perl', 1, 'GNU general public license' => 'gpl', 1, 'GNU public license' => 'gpl', 1, 'GNU lesser general public license' => 'lgpl', 1, 'GNU lesser public license' => 'lgpl', 1, 'GNU library general public license' => 'lgpl', 1, 'GNU library public license' => 'lgpl', 1, 'GNU Free Documentation license' => 'unrestricted', 1, 'GNU Affero General Public License' => 'open_source', 1, '(?:Free)?BSD license' => 'bsd', 1, 'Artistic license 2\.0' => 'artistic_2', 1, 'Artistic license' => 'artistic', 1, 'Apache (?:Software )?license' => 'apache', 1, 'GPL' => 'gpl', 1, 'LGPL' => 'lgpl', 1, 'BSD' => 'bsd', 1, 'Artistic' => 'artistic', 1, 'MIT' => 'mit', 1, 'Mozilla Public License' => 'mozilla', 1, 'Q Public License' => 'open_source', 1, 'OpenSSL License' => 'unrestricted', 1, 'SSLeay License' => 'unrestricted', 1, 'zlib License' => 'open_source', 1, 'proprietary' => 'proprietary', 0, ); while ( my ($pattern, $license, $osi) = splice(@phrases, 0, 3) ) { $pattern =~ s#\s+#\\s+#gs; if ( $license_text =~ /\b$pattern\b/i ) { return $license; } } return ''; } sub license_from { my $self = shift; if (my $license=_extract_license(Module::Install::_read($_[0]))) { $self->license($license); } else { warn "Cannot determine license info from $_[0]\n"; return 'unknown'; } } sub _extract_bugtracker { my @links = $_[0] =~ m#L<( https?\Q://rt.cpan.org/\E[^>]+| https?\Q://github.com/\E[\w_]+/[\w_]+/issues| https?\Q://code.google.com/p/\E[\w_\-]+/issues/list )>#gx; my %links; @links{@links}=(); @links=keys %links; return @links; } sub bugtracker_from { my $self = shift; my $content = Module::Install::_read($_[0]); my @links = _extract_bugtracker($content); unless ( @links ) { warn "Cannot determine bugtracker info from $_[0]\n"; return 0; } if ( @links > 1 ) { warn "Found more than one bugtracker link in $_[0]\n"; return 0; } # Set the bugtracker bugtracker( $links[0] ); return 1; } sub requires_from { my $self = shift; my $content = Module::Install::_readperl($_[0]); my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+([\d\.]+)/mg; while ( @requires ) { my $module = shift @requires; my $version = shift @requires; $self->requires( $module => $version ); } } sub test_requires_from { my $self = shift; my $content = Module::Install::_readperl($_[0]); my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+([\d\.]+)/mg; while ( @requires ) { my $module = shift @requires; my $version = shift @requires; $self->test_requires( $module => $version ); } } # Convert triple-part versions (eg, 5.6.1 or 5.8.9) to # numbers (eg, 5.006001 or 5.008009). # Also, convert double-part versions (eg, 5.8) sub _perl_version { my $v = $_[-1]; $v =~ s/^([1-9])\.([1-9]\d?\d?)$/sprintf("%d.%03d",$1,$2)/e; $v =~ s/^([1-9])\.([1-9]\d?\d?)\.(0|[1-9]\d?\d?)$/sprintf("%d.%03d%03d",$1,$2,$3 || 0)/e; $v =~ s/(\.\d\d\d)000$/$1/; $v =~ s/_.+$//; if ( ref($v) ) { # Numify $v = $v + 0; } return $v; } sub add_metadata { my $self = shift; my %hash = @_; for my $key (keys %hash) { warn "add_metadata: $key is not prefixed with 'x_'.\n" . "Use appopriate function to add non-private metadata.\n" unless $key =~ /^x_/; $self->{values}->{$key} = $hash{$key}; } } ###################################################################### # MYMETA Support sub WriteMyMeta { die "WriteMyMeta has been deprecated"; } sub write_mymeta_yaml { my $self = shift; # We need YAML::Tiny to write the MYMETA.yml file unless ( eval { require YAML::Tiny; 1; } ) { return 1; } # Generate the data my $meta = $self->_write_mymeta_data or return 1; # Save as the MYMETA.yml file print "Writing MYMETA.yml\n"; YAML::Tiny::DumpFile('MYMETA.yml', $meta); } sub write_mymeta_json { my $self = shift; # We need JSON to write the MYMETA.json file unless ( eval { require JSON; 1; } ) { return 1; } # Generate the data my $meta = $self->_write_mymeta_data or return 1; # Save as the MYMETA.yml file print "Writing MYMETA.json\n"; Module::Install::_write( 'MYMETA.json', JSON->new->pretty(1)->canonical->encode($meta), ); } sub _write_mymeta_data { my $self = shift; # If there's no existing META.yml there is nothing we can do return undef unless -f 'META.yml'; # We need Parse::CPAN::Meta to load the file unless ( eval { require Parse::CPAN::Meta; 1; } ) { return undef; } # Merge the perl version into the dependencies my $val = $self->Meta->{values}; my $perl = delete $val->{perl_version}; if ( $perl ) { $val->{requires} ||= []; my $requires = $val->{requires}; # Canonize to three-dot version after Perl 5.6 if ( $perl >= 5.006 ) { $perl =~ s{^(\d+)\.(\d\d\d)(\d*)}{join('.', $1, int($2||0), int($3||0))}e } unshift @$requires, [ perl => $perl ]; } # Load the advisory META.yml file my @yaml = Parse::CPAN::Meta::LoadFile('META.yml'); my $meta = $yaml[0]; # Overwrite the non-configure dependency hashs delete $meta->{requires}; delete $meta->{build_requires}; delete $meta->{recommends}; if ( exists $val->{requires} ) { $meta->{requires} = { map { @$_ } @{ $val->{requires} } }; } if ( exists $val->{build_requires} ) { $meta->{build_requires} = { map { @$_ } @{ $val->{build_requires} } }; } return $meta; } 1; Makefile-Parser-0.215/inc/Module/Install/Win32.pm0000644000175000001440000000340311622773340020550 0ustar agentzusers#line 1 package Module::Install::Win32; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.01'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } # determine if the user needs nmake, and download it if needed sub check_nmake { my $self = shift; $self->load('can_run'); $self->load('get_file'); require Config; return unless ( $^O eq 'MSWin32' and $Config::Config{make} and $Config::Config{make} =~ /^nmake\b/i and ! $self->can_run('nmake') ); print "The required 'nmake' executable not found, fetching it...\n"; require File::Basename; my $rv = $self->get_file( url => 'http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe', ftp_url => 'ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe', local_dir => File::Basename::dirname($^X), size => 51928, run => 'Nmake15.exe /o > nul', check_for => 'Nmake.exe', remove => 1, ); die <<'END_MESSAGE' unless $rv; ------------------------------------------------------------------------------- Since you are using Microsoft Windows, you will need the 'nmake' utility before installation. It's available at: http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe or ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe Please download the file manually, save it to a directory in %PATH% (e.g. C:\WINDOWS\COMMAND\), then launch the MS-DOS command line shell, "cd" to that directory, and run "Nmake15.exe" from there; that will create the 'nmake.exe' file needed by this module. You may then resume the installation process described in README. ------------------------------------------------------------------------------- END_MESSAGE } 1; Makefile-Parser-0.215/inc/Module/Install/Include.pm0000644000175000001440000000101511622773337021234 0ustar agentzusers#line 1 package Module::Install::Include; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.01'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub include { shift()->admin->include(@_); } sub include_deps { shift()->admin->include_deps(@_); } sub auto_include { shift()->admin->auto_include(@_); } sub auto_include_deps { shift()->admin->auto_include_deps(@_); } sub auto_include_dependent_dists { shift()->admin->auto_include_dependent_dists(@_); } 1; Makefile-Parser-0.215/inc/Module/Install/AutoInstall.pm0000644000175000001440000000363211622773337022117 0ustar agentzusers#line 1 package Module::Install::AutoInstall; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.01'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub AutoInstall { $_[0] } sub run { my $self = shift; $self->auto_install_now(@_); } sub write { my $self = shift; $self->auto_install(@_); } sub auto_install { my $self = shift; return if $self->{done}++; # Flatten array of arrays into a single array my @core = map @$_, map @$_, grep ref, $self->build_requires, $self->requires; my @config = @_; # We'll need Module::AutoInstall $self->include('Module::AutoInstall'); require Module::AutoInstall; my @features_require = Module::AutoInstall->import( (@config ? (-config => \@config) : ()), (@core ? (-core => \@core) : ()), $self->features, ); my %seen; my @requires = map @$_, map @$_, grep ref, $self->requires; while (my ($mod, $ver) = splice(@requires, 0, 2)) { $seen{$mod}{$ver}++; } my @build_requires = map @$_, map @$_, grep ref, $self->build_requires; while (my ($mod, $ver) = splice(@build_requires, 0, 2)) { $seen{$mod}{$ver}++; } my @configure_requires = map @$_, map @$_, grep ref, $self->configure_requires; while (my ($mod, $ver) = splice(@configure_requires, 0, 2)) { $seen{$mod}{$ver}++; } my @deduped; while (my ($mod, $ver) = splice(@features_require, 0, 2)) { push @deduped, $mod => $ver unless $seen{$mod}{$ver}++; } $self->requires(@deduped); $self->makemaker_args( Module::AutoInstall::_make_args() ); my $class = ref($self); $self->postamble( "# --- $class section:\n" . Module::AutoInstall::postamble() ); } sub auto_install_now { my $self = shift; $self->auto_install(@_); Module::AutoInstall::do_install(); } 1; Makefile-Parser-0.215/inc/Module/Install/Can.pm0000644000175000001440000000333311622773340020351 0ustar agentzusers#line 1 package Module::Install::Can; use strict; use Config (); use File::Spec (); use ExtUtils::MakeMaker (); use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.01'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } # check if we can load some module ### Upgrade this to not have to load the module if possible sub can_use { my ($self, $mod, $ver) = @_; $mod =~ s{::|\\}{/}g; $mod .= '.pm' unless $mod =~ /\.pm$/i; my $pkg = $mod; $pkg =~ s{/}{::}g; $pkg =~ s{\.pm$}{}i; local $@; eval { require $mod; $pkg->VERSION($ver || 0); 1 }; } # check if we can run some command sub can_run { my ($self, $cmd) = @_; my $_cmd = $cmd; return $_cmd if (-x $_cmd or $_cmd = MM->maybe_command($_cmd)); for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), '.') { next if $dir eq ''; my $abs = File::Spec->catfile($dir, $_[1]); return $abs if (-x $abs or $abs = MM->maybe_command($abs)); } return; } # can we locate a (the) C compiler sub can_cc { my $self = shift; my @chunks = split(/ /, $Config::Config{cc}) or return; # $Config{cc} may contain args; try to find out the program part while (@chunks) { return $self->can_run("@chunks") || (pop(@chunks), next); } return; } # Fix Cygwin bug on maybe_command(); if ( $^O eq 'cygwin' ) { require ExtUtils::MM_Cygwin; require ExtUtils::MM_Win32; if ( ! defined(&ExtUtils::MM_Cygwin::maybe_command) ) { *ExtUtils::MM_Cygwin::maybe_command = sub { my ($self, $file) = @_; if ($file =~ m{^/cygdrive/}i and ExtUtils::MM_Win32->can('maybe_command')) { ExtUtils::MM_Win32->maybe_command($file); } else { ExtUtils::MM_Unix->maybe_command($file); } } } } 1; __END__ #line 156 Makefile-Parser-0.215/inc/Module/AutoInstall.pm0000644000175000001440000005423111622773337020512 0ustar agentzusers#line 1 package Module::AutoInstall; use strict; use Cwd (); use ExtUtils::MakeMaker (); use vars qw{$VERSION}; BEGIN { $VERSION = '1.03'; } # special map on pre-defined feature sets my %FeatureMap = ( '' => 'Core Features', # XXX: deprecated '-core' => 'Core Features', ); # various lexical flags my ( @Missing, @Existing, %DisabledTests, $UnderCPAN, $HasCPANPLUS ); my ( $Config, $CheckOnly, $SkipInstall, $AcceptDefault, $TestOnly, $AllDeps ); my ( $PostambleActions, $PostambleUsed ); # See if it's a testing or non-interactive session _accept_default( $ENV{AUTOMATED_TESTING} or ! -t STDIN ); _init(); sub _accept_default { $AcceptDefault = shift; } sub missing_modules { return @Missing; } sub do_install { __PACKAGE__->install( [ $Config ? ( UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config} ) : () ], @Missing, ); } # initialize various flags, and/or perform install sub _init { foreach my $arg ( @ARGV, split( /[\s\t]+/, $ENV{PERL_AUTOINSTALL} || $ENV{PERL_EXTUTILS_AUTOINSTALL} || '' ) ) { if ( $arg =~ /^--config=(.*)$/ ) { $Config = [ split( ',', $1 ) ]; } elsif ( $arg =~ /^--installdeps=(.*)$/ ) { __PACKAGE__->install( $Config, @Missing = split( /,/, $1 ) ); exit 0; } elsif ( $arg =~ /^--default(?:deps)?$/ ) { $AcceptDefault = 1; } elsif ( $arg =~ /^--check(?:deps)?$/ ) { $CheckOnly = 1; } elsif ( $arg =~ /^--skip(?:deps)?$/ ) { $SkipInstall = 1; } elsif ( $arg =~ /^--test(?:only)?$/ ) { $TestOnly = 1; } elsif ( $arg =~ /^--all(?:deps)?$/ ) { $AllDeps = 1; } } } # overrides MakeMaker's prompt() to automatically accept the default choice sub _prompt { goto &ExtUtils::MakeMaker::prompt unless $AcceptDefault; my ( $prompt, $default ) = @_; my $y = ( $default =~ /^[Yy]/ ); print $prompt, ' [', ( $y ? 'Y' : 'y' ), '/', ( $y ? 'n' : 'N' ), '] '; print "$default\n"; return $default; } # the workhorse sub import { my $class = shift; my @args = @_ or return; my $core_all; print "*** $class version " . $class->VERSION . "\n"; print "*** Checking for Perl dependencies...\n"; my $cwd = Cwd::cwd(); $Config = []; my $maxlen = length( ( sort { length($b) <=> length($a) } grep { /^[^\-]/ } map { ref($_) ? ( ( ref($_) eq 'HASH' ) ? keys(%$_) : @{$_} ) : '' } map { +{@args}->{$_} } grep { /^[^\-]/ or /^-core$/i } keys %{ +{@args} } )[0] ); # We want to know if we're under CPAN early to avoid prompting, but # if we aren't going to try and install anything anyway then skip the # check entirely since we don't want to have to load (and configure) # an old CPAN just for a cosmetic message $UnderCPAN = _check_lock(1) unless $SkipInstall; while ( my ( $feature, $modules ) = splice( @args, 0, 2 ) ) { my ( @required, @tests, @skiptests ); my $default = 1; my $conflict = 0; if ( $feature =~ m/^-(\w+)$/ ) { my $option = lc($1); # check for a newer version of myself _update_to( $modules, @_ ) and return if $option eq 'version'; # sets CPAN configuration options $Config = $modules if $option eq 'config'; # promote every features to core status $core_all = ( $modules =~ /^all$/i ) and next if $option eq 'core'; next unless $option eq 'core'; } print "[" . ( $FeatureMap{ lc($feature) } || $feature ) . "]\n"; $modules = [ %{$modules} ] if UNIVERSAL::isa( $modules, 'HASH' ); unshift @$modules, -default => &{ shift(@$modules) } if ( ref( $modules->[0] ) eq 'CODE' ); # XXX: bugward combatability while ( my ( $mod, $arg ) = splice( @$modules, 0, 2 ) ) { if ( $mod =~ m/^-(\w+)$/ ) { my $option = lc($1); $default = $arg if ( $option eq 'default' ); $conflict = $arg if ( $option eq 'conflict' ); @tests = @{$arg} if ( $option eq 'tests' ); @skiptests = @{$arg} if ( $option eq 'skiptests' ); next; } printf( "- %-${maxlen}s ...", $mod ); if ( $arg and $arg =~ /^\D/ ) { unshift @$modules, $arg; $arg = 0; } # XXX: check for conflicts and uninstalls(!) them. my $cur = _load($mod); if (_version_cmp ($cur, $arg) >= 0) { print "loaded. ($cur" . ( $arg ? " >= $arg" : '' ) . ")\n"; push @Existing, $mod => $arg; $DisabledTests{$_} = 1 for map { glob($_) } @skiptests; } else { if (not defined $cur) # indeed missing { print "missing." . ( $arg ? " (would need $arg)" : '' ) . "\n"; } else { # no need to check $arg as _version_cmp ($cur, undef) would satisfy >= above print "too old. ($cur < $arg)\n"; } push @required, $mod => $arg; } } next unless @required; my $mandatory = ( $feature eq '-core' or $core_all ); if ( !$SkipInstall and ( $CheckOnly or ($mandatory and $UnderCPAN) or $AllDeps or _prompt( qq{==> Auto-install the } . ( @required / 2 ) . ( $mandatory ? ' mandatory' : ' optional' ) . qq{ module(s) from CPAN?}, $default ? 'y' : 'n', ) =~ /^[Yy]/ ) ) { push( @Missing, @required ); $DisabledTests{$_} = 1 for map { glob($_) } @skiptests; } elsif ( !$SkipInstall and $default and $mandatory and _prompt( qq{==> The module(s) are mandatory! Really skip?}, 'n', ) =~ /^[Nn]/ ) { push( @Missing, @required ); $DisabledTests{$_} = 1 for map { glob($_) } @skiptests; } else { $DisabledTests{$_} = 1 for map { glob($_) } @tests; } } if ( @Missing and not( $CheckOnly or $UnderCPAN ) ) { require Config; print "*** Dependencies will be installed the next time you type '$Config::Config{make}'.\n"; # make an educated guess of whether we'll need root permission. print " (You may need to do that as the 'root' user.)\n" if eval '$>'; } print "*** $class configuration finished.\n"; chdir $cwd; # import to main:: no strict 'refs'; *{'main::WriteMakefile'} = \&Write if caller(0) eq 'main'; return (@Existing, @Missing); } sub _running_under { my $thing = shift; print <<"END_MESSAGE"; *** Since we're running under ${thing}, I'll just let it take care of the dependency's installation later. END_MESSAGE return 1; } # Check to see if we are currently running under CPAN.pm and/or CPANPLUS; # if we are, then we simply let it taking care of our dependencies sub _check_lock { return unless @Missing or @_; my $cpan_env = $ENV{PERL5_CPAN_IS_RUNNING}; if ($ENV{PERL5_CPANPLUS_IS_RUNNING}) { return _running_under($cpan_env ? 'CPAN' : 'CPANPLUS'); } require CPAN; if ($CPAN::VERSION > '1.89') { if ($cpan_env) { return _running_under('CPAN'); } return; # CPAN.pm new enough, don't need to check further } # last ditch attempt, this -will- configure CPAN, very sorry _load_cpan(1); # force initialize even though it's already loaded # Find the CPAN lock-file my $lock = MM->catfile( $CPAN::Config->{cpan_home}, ".lock" ); return unless -f $lock; # Check the lock local *LOCK; return unless open(LOCK, $lock); if ( ( $^O eq 'MSWin32' ? _under_cpan() : == getppid() ) and ( $CPAN::Config->{prerequisites_policy} || '' ) ne 'ignore' ) { print <<'END_MESSAGE'; *** Since we're running under CPAN, I'll just let it take care of the dependency's installation later. END_MESSAGE return 1; } close LOCK; return; } sub install { my $class = shift; my $i; # used below to strip leading '-' from config keys my @config = ( map { s/^-// if ++$i; $_ } @{ +shift } ); my ( @modules, @installed ); while ( my ( $pkg, $ver ) = splice( @_, 0, 2 ) ) { # grep out those already installed if ( _version_cmp( _load($pkg), $ver ) >= 0 ) { push @installed, $pkg; } else { push @modules, $pkg, $ver; } } return @installed unless @modules; # nothing to do return @installed if _check_lock(); # defer to the CPAN shell print "*** Installing dependencies...\n"; return unless _connected_to('cpan.org'); my %args = @config; my %failed; local *FAILED; if ( $args{do_once} and open( FAILED, '.#autoinstall.failed' ) ) { while () { chomp; $failed{$_}++ } close FAILED; my @newmod; while ( my ( $k, $v ) = splice( @modules, 0, 2 ) ) { push @newmod, ( $k => $v ) unless $failed{$k}; } @modules = @newmod; } if ( _has_cpanplus() and not $ENV{PERL_AUTOINSTALL_PREFER_CPAN} ) { _install_cpanplus( \@modules, \@config ); } else { _install_cpan( \@modules, \@config ); } print "*** $class installation finished.\n"; # see if we have successfully installed them while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) { if ( _version_cmp( _load($pkg), $ver ) >= 0 ) { push @installed, $pkg; } elsif ( $args{do_once} and open( FAILED, '>> .#autoinstall.failed' ) ) { print FAILED "$pkg\n"; } } close FAILED if $args{do_once}; return @installed; } sub _install_cpanplus { my @modules = @{ +shift }; my @config = _cpanplus_config( @{ +shift } ); my $installed = 0; require CPANPLUS::Backend; my $cp = CPANPLUS::Backend->new; my $conf = $cp->configure_object; return unless $conf->can('conf') # 0.05x+ with "sudo" support or _can_write($conf->_get_build('base')); # 0.04x # if we're root, set UNINST=1 to avoid trouble unless user asked for it. my $makeflags = $conf->get_conf('makeflags') || ''; if ( UNIVERSAL::isa( $makeflags, 'HASH' ) ) { # 0.03+ uses a hashref here $makeflags->{UNINST} = 1 unless exists $makeflags->{UNINST}; } else { # 0.02 and below uses a scalar $makeflags = join( ' ', split( ' ', $makeflags ), 'UNINST=1' ) if ( $makeflags !~ /\bUNINST\b/ and eval qq{ $> eq '0' } ); } $conf->set_conf( makeflags => $makeflags ); $conf->set_conf( prereqs => 1 ); while ( my ( $key, $val ) = splice( @config, 0, 2 ) ) { $conf->set_conf( $key, $val ); } my $modtree = $cp->module_tree; while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) { print "*** Installing $pkg...\n"; MY::preinstall( $pkg, $ver ) or next if defined &MY::preinstall; my $success; my $obj = $modtree->{$pkg}; if ( $obj and _version_cmp( $obj->{version}, $ver ) >= 0 ) { my $pathname = $pkg; $pathname =~ s/::/\\W/; foreach my $inc ( grep { m/$pathname.pm/i } keys(%INC) ) { delete $INC{$inc}; } my $rv = $cp->install( modules => [ $obj->{module} ] ); if ( $rv and ( $rv->{ $obj->{module} } or $rv->{ok} ) ) { print "*** $pkg successfully installed.\n"; $success = 1; } else { print "*** $pkg installation cancelled.\n"; $success = 0; } $installed += $success; } else { print << "."; *** Could not find a version $ver or above for $pkg; skipping. . } MY::postinstall( $pkg, $ver, $success ) if defined &MY::postinstall; } return $installed; } sub _cpanplus_config { my @config = (); while ( @_ ) { my ($key, $value) = (shift(), shift()); if ( $key eq 'prerequisites_policy' ) { if ( $value eq 'follow' ) { $value = CPANPLUS::Internals::Constants::PREREQ_INSTALL(); } elsif ( $value eq 'ask' ) { $value = CPANPLUS::Internals::Constants::PREREQ_ASK(); } elsif ( $value eq 'ignore' ) { $value = CPANPLUS::Internals::Constants::PREREQ_IGNORE(); } else { die "*** Cannot convert option $key = '$value' to CPANPLUS version.\n"; } } else { die "*** Cannot convert option $key to CPANPLUS version.\n"; } } return @config; } sub _install_cpan { my @modules = @{ +shift }; my @config = @{ +shift }; my $installed = 0; my %args; _load_cpan(); require Config; if (CPAN->VERSION < 1.80) { # no "sudo" support, probe for writableness return unless _can_write( MM->catfile( $CPAN::Config->{cpan_home}, 'sources' ) ) and _can_write( $Config::Config{sitelib} ); } # if we're root, set UNINST=1 to avoid trouble unless user asked for it. my $makeflags = $CPAN::Config->{make_install_arg} || ''; $CPAN::Config->{make_install_arg} = join( ' ', split( ' ', $makeflags ), 'UNINST=1' ) if ( $makeflags !~ /\bUNINST\b/ and eval qq{ $> eq '0' } ); # don't show start-up info $CPAN::Config->{inhibit_startup_message} = 1; # set additional options while ( my ( $opt, $arg ) = splice( @config, 0, 2 ) ) { ( $args{$opt} = $arg, next ) if $opt =~ /^force$/; # pseudo-option $CPAN::Config->{$opt} = $arg; } local $CPAN::Config->{prerequisites_policy} = 'follow'; while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) { MY::preinstall( $pkg, $ver ) or next if defined &MY::preinstall; print "*** Installing $pkg...\n"; my $obj = CPAN::Shell->expand( Module => $pkg ); my $success = 0; if ( $obj and _version_cmp( $obj->cpan_version, $ver ) >= 0 ) { my $pathname = $pkg; $pathname =~ s/::/\\W/; foreach my $inc ( grep { m/$pathname.pm/i } keys(%INC) ) { delete $INC{$inc}; } my $rv = $args{force} ? CPAN::Shell->force( install => $pkg ) : CPAN::Shell->install($pkg); $rv ||= eval { $CPAN::META->instance( 'CPAN::Distribution', $obj->cpan_file, ) ->{install} if $CPAN::META; }; if ( $rv eq 'YES' ) { print "*** $pkg successfully installed.\n"; $success = 1; } else { print "*** $pkg installation failed.\n"; $success = 0; } $installed += $success; } else { print << "."; *** Could not find a version $ver or above for $pkg; skipping. . } MY::postinstall( $pkg, $ver, $success ) if defined &MY::postinstall; } return $installed; } sub _has_cpanplus { return ( $HasCPANPLUS = ( $INC{'CPANPLUS/Config.pm'} or _load('CPANPLUS::Shell::Default') ) ); } # make guesses on whether we're under the CPAN installation directory sub _under_cpan { require Cwd; require File::Spec; my $cwd = File::Spec->canonpath( Cwd::cwd() ); my $cpan = File::Spec->canonpath( $CPAN::Config->{cpan_home} ); return ( index( $cwd, $cpan ) > -1 ); } sub _update_to { my $class = __PACKAGE__; my $ver = shift; return if _version_cmp( _load($class), $ver ) >= 0; # no need to upgrade if ( _prompt( "==> A newer version of $class ($ver) is required. Install?", 'y' ) =~ /^[Nn]/ ) { die "*** Please install $class $ver manually.\n"; } print << "."; *** Trying to fetch it from CPAN... . # install ourselves _load($class) and return $class->import(@_) if $class->install( [], $class, $ver ); print << '.'; exit 1; *** Cannot bootstrap myself. :-( Installation terminated. . } # check if we're connected to some host, using inet_aton sub _connected_to { my $site = shift; return ( ( _load('Socket') and Socket::inet_aton($site) ) or _prompt( qq( *** Your host cannot resolve the domain name '$site', which probably means the Internet connections are unavailable. ==> Should we try to install the required module(s) anyway?), 'n' ) =~ /^[Yy]/ ); } # check if a directory is writable; may create it on demand sub _can_write { my $path = shift; mkdir( $path, 0755 ) unless -e $path; return 1 if -w $path; print << "."; *** You are not allowed to write to the directory '$path'; the installation may fail due to insufficient permissions. . if ( eval '$>' and lc(`sudo -V`) =~ /version/ and _prompt( qq( ==> Should we try to re-execute the autoinstall process with 'sudo'?), ((-t STDIN) ? 'y' : 'n') ) =~ /^[Yy]/ ) { # try to bootstrap ourselves from sudo print << "."; *** Trying to re-execute the autoinstall process with 'sudo'... . my $missing = join( ',', @Missing ); my $config = join( ',', UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config} ) if $Config; return unless system( 'sudo', $^X, $0, "--config=$config", "--installdeps=$missing" ); print << "."; *** The 'sudo' command exited with error! Resuming... . } return _prompt( qq( ==> Should we try to install the required module(s) anyway?), 'n' ) =~ /^[Yy]/; } # load a module and return the version it reports sub _load { my $mod = pop; # class/instance doesn't matter my $file = $mod; $file =~ s|::|/|g; $file .= '.pm'; local $@; return eval { require $file; $mod->VERSION } || ( $@ ? undef: 0 ); } # Load CPAN.pm and it's configuration sub _load_cpan { return if $CPAN::VERSION and $CPAN::Config and not @_; require CPAN; # CPAN-1.82+ adds CPAN::Config::AUTOLOAD to redirect to # CPAN::HandleConfig->load. CPAN reports that the redirection # is deprecated in a warning printed at the user. # CPAN-1.81 expects CPAN::HandleConfig->load, does not have # $CPAN::HandleConfig::VERSION but cannot handle # CPAN::Config->load # Which "versions expect CPAN::Config->load? if ( $CPAN::HandleConfig::VERSION || CPAN::HandleConfig->can('load') ) { # Newer versions of CPAN have a HandleConfig module CPAN::HandleConfig->load; } else { # Older versions had the load method in Config directly CPAN::Config->load; } } # compare two versions, either use Sort::Versions or plain comparison # return values same as <=> sub _version_cmp { my ( $cur, $min ) = @_; return -1 unless defined $cur; # if 0 keep comparing return 1 unless $min; $cur =~ s/\s+$//; # check for version numbers that are not in decimal format if ( ref($cur) or ref($min) or $cur =~ /v|\..*\./ or $min =~ /v|\..*\./ ) { if ( ( $version::VERSION or defined( _load('version') )) and version->can('new') ) { # use version.pm if it is installed. return version->new($cur) <=> version->new($min); } elsif ( $Sort::Versions::VERSION or defined( _load('Sort::Versions') ) ) { # use Sort::Versions as the sorting algorithm for a.b.c versions return Sort::Versions::versioncmp( $cur, $min ); } warn "Cannot reliably compare non-decimal formatted versions.\n" . "Please install version.pm or Sort::Versions.\n"; } # plain comparison local $^W = 0; # shuts off 'not numeric' bugs return $cur <=> $min; } # nothing; this usage is deprecated. sub main::PREREQ_PM { return {}; } sub _make_args { my %args = @_; $args{PREREQ_PM} = { %{ $args{PREREQ_PM} || {} }, @Existing, @Missing } if $UnderCPAN or $TestOnly; if ( $args{EXE_FILES} and -e 'MANIFEST' ) { require ExtUtils::Manifest; my $manifest = ExtUtils::Manifest::maniread('MANIFEST'); $args{EXE_FILES} = [ grep { exists $manifest->{$_} } @{ $args{EXE_FILES} } ]; } $args{test}{TESTS} ||= 't/*.t'; $args{test}{TESTS} = join( ' ', grep { !exists( $DisabledTests{$_} ) } map { glob($_) } split( /\s+/, $args{test}{TESTS} ) ); my $missing = join( ',', @Missing ); my $config = join( ',', UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config} ) if $Config; $PostambleActions = ( ($missing and not $UnderCPAN) ? "\$(PERL) $0 --config=$config --installdeps=$missing" : "\$(NOECHO) \$(NOOP)" ); return %args; } # a wrapper to ExtUtils::MakeMaker::WriteMakefile sub Write { require Carp; Carp::croak "WriteMakefile: Need even number of args" if @_ % 2; if ($CheckOnly) { print << "."; *** Makefile not written in check-only mode. . return; } my %args = _make_args(@_); no strict 'refs'; $PostambleUsed = 0; local *MY::postamble = \&postamble unless defined &MY::postamble; ExtUtils::MakeMaker::WriteMakefile(%args); print << "." unless $PostambleUsed; *** WARNING: Makefile written with customized MY::postamble() without including contents from Module::AutoInstall::postamble() -- auto installation features disabled. Please contact the author. . return 1; } sub postamble { $PostambleUsed = 1; return <<"END_MAKE"; config :: installdeps \t\$(NOECHO) \$(NOOP) checkdeps :: \t\$(PERL) $0 --checkdeps installdeps :: \t$PostambleActions END_MAKE } 1; __END__ #line 1071 Makefile-Parser-0.215/inc/Test/0000755000175000001440000000000011622773367015405 5ustar agentzusersMakefile-Parser-0.215/inc/Test/Builder.pm0000644000175000001440000007376511622773337017350 0ustar agentzusers#line 1 package Test::Builder; use 5.006; use strict; use warnings; our $VERSION = '0.92'; $VERSION = eval $VERSION; ## no critic (BuiltinFunctions::ProhibitStringyEval) BEGIN { if( $] < 5.008 ) { require Test::Builder::IO::Scalar; } } # Make Test::Builder thread-safe for ithreads. BEGIN { use Config; # Load threads::shared when threads are turned on. # 5.8.0's threads are so busted we no longer support them. if( $] >= 5.008001 && $Config{useithreads} && $INC{'threads.pm'} ) { require threads::shared; # Hack around YET ANOTHER threads::shared bug. It would # occassionally forget the contents of the variable when sharing it. # So we first copy the data, then share, then put our copy back. *share = sub (\[$@%]) { my $type = ref $_[0]; my $data; if( $type eq 'HASH' ) { %$data = %{ $_[0] }; } elsif( $type eq 'ARRAY' ) { @$data = @{ $_[0] }; } elsif( $type eq 'SCALAR' ) { $$data = ${ $_[0] }; } else { die( "Unknown type: " . $type ); } $_[0] = &threads::shared::share( $_[0] ); if( $type eq 'HASH' ) { %{ $_[0] } = %$data; } elsif( $type eq 'ARRAY' ) { @{ $_[0] } = @$data; } elsif( $type eq 'SCALAR' ) { ${ $_[0] } = $$data; } else { die( "Unknown type: " . $type ); } return $_[0]; }; } # 5.8.0's threads::shared is busted when threads are off # and earlier Perls just don't have that module at all. else { *share = sub { return $_[0] }; *lock = sub { 0 }; } } #line 117 my $Test = Test::Builder->new; sub new { my($class) = shift; $Test ||= $class->create; return $Test; } #line 139 sub create { my $class = shift; my $self = bless {}, $class; $self->reset; return $self; } #line 158 our $Level; sub reset { ## no critic (Subroutines::ProhibitBuiltinHomonyms) my($self) = @_; # We leave this a global because it has to be localized and localizing # hash keys is just asking for pain. Also, it was documented. $Level = 1; $self->{Have_Plan} = 0; $self->{No_Plan} = 0; $self->{Have_Output_Plan} = 0; $self->{Original_Pid} = $$; share( $self->{Curr_Test} ); $self->{Curr_Test} = 0; $self->{Test_Results} = &share( [] ); $self->{Exported_To} = undef; $self->{Expected_Tests} = 0; $self->{Skip_All} = 0; $self->{Use_Nums} = 1; $self->{No_Header} = 0; $self->{No_Ending} = 0; $self->{Todo} = undef; $self->{Todo_Stack} = []; $self->{Start_Todo} = 0; $self->{Opened_Testhandles} = 0; $self->_dup_stdhandles; return; } #line 219 my %plan_cmds = ( no_plan => \&no_plan, skip_all => \&skip_all, tests => \&_plan_tests, ); sub plan { my( $self, $cmd, $arg ) = @_; return unless $cmd; local $Level = $Level + 1; $self->croak("You tried to plan twice") if $self->{Have_Plan}; if( my $method = $plan_cmds{$cmd} ) { local $Level = $Level + 1; $self->$method($arg); } else { my @args = grep { defined } ( $cmd, $arg ); $self->croak("plan() doesn't understand @args"); } return 1; } sub _plan_tests { my($self, $arg) = @_; if($arg) { local $Level = $Level + 1; return $self->expected_tests($arg); } elsif( !defined $arg ) { $self->croak("Got an undefined number of tests"); } else { $self->croak("You said to run 0 tests"); } return; } #line 275 sub expected_tests { my $self = shift; my($max) = @_; if(@_) { $self->croak("Number of tests must be a positive integer. You gave it '$max'") unless $max =~ /^\+?\d+$/; $self->{Expected_Tests} = $max; $self->{Have_Plan} = 1; $self->_output_plan($max) unless $self->no_header; } return $self->{Expected_Tests}; } #line 299 sub no_plan { my($self, $arg) = @_; $self->carp("no_plan takes no arguments") if $arg; $self->{No_Plan} = 1; $self->{Have_Plan} = 1; return 1; } #line 333 sub _output_plan { my($self, $max, $directive, $reason) = @_; $self->carp("The plan was already output") if $self->{Have_Output_Plan}; my $plan = "1..$max"; $plan .= " # $directive" if defined $directive; $plan .= " $reason" if defined $reason; $self->_print("$plan\n"); $self->{Have_Output_Plan} = 1; return; } #line 384 sub done_testing { my($self, $num_tests) = @_; # If done_testing() specified the number of tests, shut off no_plan. if( defined $num_tests ) { $self->{No_Plan} = 0; } else { $num_tests = $self->current_test; } if( $self->{Done_Testing} ) { my($file, $line) = @{$self->{Done_Testing}}[1,2]; $self->ok(0, "done_testing() was already called at $file line $line"); return; } $self->{Done_Testing} = [caller]; if( $self->expected_tests && $num_tests != $self->expected_tests ) { $self->ok(0, "planned to run @{[ $self->expected_tests ]} ". "but done_testing() expects $num_tests"); } else { $self->{Expected_Tests} = $num_tests; } $self->_output_plan($num_tests) unless $self->{Have_Output_Plan}; $self->{Have_Plan} = 1; return 1; } #line 429 sub has_plan { my $self = shift; return( $self->{Expected_Tests} ) if $self->{Expected_Tests}; return('no_plan') if $self->{No_Plan}; return(undef); } #line 446 sub skip_all { my( $self, $reason ) = @_; $self->{Skip_All} = 1; $self->_output_plan(0, "SKIP", $reason) unless $self->no_header; exit(0); } #line 468 sub exported_to { my( $self, $pack ) = @_; if( defined $pack ) { $self->{Exported_To} = $pack; } return $self->{Exported_To}; } #line 498 sub ok { my( $self, $test, $name ) = @_; # $test might contain an object which we don't want to accidentally # store, so we turn it into a boolean. $test = $test ? 1 : 0; lock $self->{Curr_Test}; $self->{Curr_Test}++; # In case $name is a string overloaded object, force it to stringify. $self->_unoverload_str( \$name ); $self->diag(<<"ERR") if defined $name and $name =~ /^[\d\s]+$/; You named your test '$name'. You shouldn't use numbers for your test names. Very confusing. ERR # Capture the value of $TODO for the rest of this ok() call # so it can more easily be found by other routines. my $todo = $self->todo(); my $in_todo = $self->in_todo; local $self->{Todo} = $todo if $in_todo; $self->_unoverload_str( \$todo ); my $out; my $result = &share( {} ); unless($test) { $out .= "not "; @$result{ 'ok', 'actual_ok' } = ( ( $self->in_todo ? 1 : 0 ), 0 ); } else { @$result{ 'ok', 'actual_ok' } = ( 1, $test ); } $out .= "ok"; $out .= " $self->{Curr_Test}" if $self->use_numbers; if( defined $name ) { $name =~ s|#|\\#|g; # # in a name can confuse Test::Harness. $out .= " - $name"; $result->{name} = $name; } else { $result->{name} = ''; } if( $self->in_todo ) { $out .= " # TODO $todo"; $result->{reason} = $todo; $result->{type} = 'todo'; } else { $result->{reason} = ''; $result->{type} = ''; } $self->{Test_Results}[ $self->{Curr_Test} - 1 ] = $result; $out .= "\n"; $self->_print($out); unless($test) { my $msg = $self->in_todo ? "Failed (TODO)" : "Failed"; $self->_print_to_fh( $self->_diag_fh, "\n" ) if $ENV{HARNESS_ACTIVE}; my( undef, $file, $line ) = $self->caller; if( defined $name ) { $self->diag(qq[ $msg test '$name'\n]); $self->diag(qq[ at $file line $line.\n]); } else { $self->diag(qq[ $msg test at $file line $line.\n]); } } return $test ? 1 : 0; } sub _unoverload { my $self = shift; my $type = shift; $self->_try(sub { require overload; }, die_on_fail => 1); foreach my $thing (@_) { if( $self->_is_object($$thing) ) { if( my $string_meth = overload::Method( $$thing, $type ) ) { $$thing = $$thing->$string_meth(); } } } return; } sub _is_object { my( $self, $thing ) = @_; return $self->_try( sub { ref $thing && $thing->isa('UNIVERSAL') } ) ? 1 : 0; } sub _unoverload_str { my $self = shift; return $self->_unoverload( q[""], @_ ); } sub _unoverload_num { my $self = shift; $self->_unoverload( '0+', @_ ); for my $val (@_) { next unless $self->_is_dualvar($$val); $$val = $$val + 0; } return; } # This is a hack to detect a dualvar such as $! sub _is_dualvar { my( $self, $val ) = @_; # Objects are not dualvars. return 0 if ref $val; no warnings 'numeric'; my $numval = $val + 0; return $numval != 0 and $numval ne $val ? 1 : 0; } #line 649 sub is_eq { my( $self, $got, $expect, $name ) = @_; local $Level = $Level + 1; $self->_unoverload_str( \$got, \$expect ); if( !defined $got || !defined $expect ) { # undef only matches undef and nothing else my $test = !defined $got && !defined $expect; $self->ok( $test, $name ); $self->_is_diag( $got, 'eq', $expect ) unless $test; return $test; } return $self->cmp_ok( $got, 'eq', $expect, $name ); } sub is_num { my( $self, $got, $expect, $name ) = @_; local $Level = $Level + 1; $self->_unoverload_num( \$got, \$expect ); if( !defined $got || !defined $expect ) { # undef only matches undef and nothing else my $test = !defined $got && !defined $expect; $self->ok( $test, $name ); $self->_is_diag( $got, '==', $expect ) unless $test; return $test; } return $self->cmp_ok( $got, '==', $expect, $name ); } sub _diag_fmt { my( $self, $type, $val ) = @_; if( defined $$val ) { if( $type eq 'eq' or $type eq 'ne' ) { # quote and force string context $$val = "'$$val'"; } else { # force numeric context $self->_unoverload_num($val); } } else { $$val = 'undef'; } return; } sub _is_diag { my( $self, $got, $type, $expect ) = @_; $self->_diag_fmt( $type, $_ ) for \$got, \$expect; local $Level = $Level + 1; return $self->diag(<<"DIAGNOSTIC"); got: $got expected: $expect DIAGNOSTIC } sub _isnt_diag { my( $self, $got, $type ) = @_; $self->_diag_fmt( $type, \$got ); local $Level = $Level + 1; return $self->diag(<<"DIAGNOSTIC"); got: $got expected: anything else DIAGNOSTIC } #line 746 sub isnt_eq { my( $self, $got, $dont_expect, $name ) = @_; local $Level = $Level + 1; if( !defined $got || !defined $dont_expect ) { # undef only matches undef and nothing else my $test = defined $got || defined $dont_expect; $self->ok( $test, $name ); $self->_isnt_diag( $got, 'ne' ) unless $test; return $test; } return $self->cmp_ok( $got, 'ne', $dont_expect, $name ); } sub isnt_num { my( $self, $got, $dont_expect, $name ) = @_; local $Level = $Level + 1; if( !defined $got || !defined $dont_expect ) { # undef only matches undef and nothing else my $test = defined $got || defined $dont_expect; $self->ok( $test, $name ); $self->_isnt_diag( $got, '!=' ) unless $test; return $test; } return $self->cmp_ok( $got, '!=', $dont_expect, $name ); } #line 797 sub like { my( $self, $this, $regex, $name ) = @_; local $Level = $Level + 1; return $self->_regex_ok( $this, $regex, '=~', $name ); } sub unlike { my( $self, $this, $regex, $name ) = @_; local $Level = $Level + 1; return $self->_regex_ok( $this, $regex, '!~', $name ); } #line 821 my %numeric_cmps = map { ( $_, 1 ) } ( "<", "<=", ">", ">=", "==", "!=", "<=>" ); sub cmp_ok { my( $self, $got, $type, $expect, $name ) = @_; my $test; my $error; { ## no critic (BuiltinFunctions::ProhibitStringyEval) local( $@, $!, $SIG{__DIE__} ); # isolate eval my($pack, $file, $line) = $self->caller(); $test = eval qq[ #line 1 "cmp_ok [from $file line $line]" \$got $type \$expect; ]; $error = $@; } local $Level = $Level + 1; my $ok = $self->ok( $test, $name ); # Treat overloaded objects as numbers if we're asked to do a # numeric comparison. my $unoverload = $numeric_cmps{$type} ? '_unoverload_num' : '_unoverload_str'; $self->diag(<<"END") if $error; An error occurred while using $type: ------------------------------------ $error ------------------------------------ END unless($ok) { $self->$unoverload( \$got, \$expect ); if( $type =~ /^(eq|==)$/ ) { $self->_is_diag( $got, $type, $expect ); } elsif( $type =~ /^(ne|!=)$/ ) { $self->_isnt_diag( $got, $type ); } else { $self->_cmp_diag( $got, $type, $expect ); } } return $ok; } sub _cmp_diag { my( $self, $got, $type, $expect ) = @_; $got = defined $got ? "'$got'" : 'undef'; $expect = defined $expect ? "'$expect'" : 'undef'; local $Level = $Level + 1; return $self->diag(<<"DIAGNOSTIC"); $got $type $expect DIAGNOSTIC } sub _caller_context { my $self = shift; my( $pack, $file, $line ) = $self->caller(1); my $code = ''; $code .= "#line $line $file\n" if defined $file and defined $line; return $code; } #line 920 sub BAIL_OUT { my( $self, $reason ) = @_; $self->{Bailed_Out} = 1; $self->_print("Bail out! $reason"); exit 255; } #line 933 *BAILOUT = \&BAIL_OUT; #line 944 sub skip { my( $self, $why ) = @_; $why ||= ''; $self->_unoverload_str( \$why ); lock( $self->{Curr_Test} ); $self->{Curr_Test}++; $self->{Test_Results}[ $self->{Curr_Test} - 1 ] = &share( { 'ok' => 1, actual_ok => 1, name => '', type => 'skip', reason => $why, } ); my $out = "ok"; $out .= " $self->{Curr_Test}" if $self->use_numbers; $out .= " # skip"; $out .= " $why" if length $why; $out .= "\n"; $self->_print($out); return 1; } #line 985 sub todo_skip { my( $self, $why ) = @_; $why ||= ''; lock( $self->{Curr_Test} ); $self->{Curr_Test}++; $self->{Test_Results}[ $self->{Curr_Test} - 1 ] = &share( { 'ok' => 1, actual_ok => 0, name => '', type => 'todo_skip', reason => $why, } ); my $out = "not ok"; $out .= " $self->{Curr_Test}" if $self->use_numbers; $out .= " # TODO & SKIP $why\n"; $self->_print($out); return 1; } #line 1062 sub maybe_regex { my( $self, $regex ) = @_; my $usable_regex = undef; return $usable_regex unless defined $regex; my( $re, $opts ); # Check for qr/foo/ if( _is_qr($regex) ) { $usable_regex = $regex; } # Check for '/foo/' or 'm,foo,' elsif(( $re, $opts ) = $regex =~ m{^ /(.*)/ (\w*) $ }sx or ( undef, $re, $opts ) = $regex =~ m,^ m([^\w\s]) (.+) \1 (\w*) $,sx ) { $usable_regex = length $opts ? "(?$opts)$re" : $re; } return $usable_regex; } sub _is_qr { my $regex = shift; # is_regexp() checks for regexes in a robust manner, say if they're # blessed. return re::is_regexp($regex) if defined &re::is_regexp; return ref $regex eq 'Regexp'; } sub _regex_ok { my( $self, $this, $regex, $cmp, $name ) = @_; my $ok = 0; my $usable_regex = $self->maybe_regex($regex); unless( defined $usable_regex ) { local $Level = $Level + 1; $ok = $self->ok( 0, $name ); $self->diag(" '$regex' doesn't look much like a regex to me."); return $ok; } { ## no critic (BuiltinFunctions::ProhibitStringyEval) my $test; my $code = $self->_caller_context; local( $@, $!, $SIG{__DIE__} ); # isolate eval # Yes, it has to look like this or 5.4.5 won't see the #line # directive. # Don't ask me, man, I just work here. $test = eval " $code" . q{$test = $this =~ /$usable_regex/ ? 1 : 0}; $test = !$test if $cmp eq '!~'; local $Level = $Level + 1; $ok = $self->ok( $test, $name ); } unless($ok) { $this = defined $this ? "'$this'" : 'undef'; my $match = $cmp eq '=~' ? "doesn't match" : "matches"; local $Level = $Level + 1; $self->diag( sprintf <<'DIAGNOSTIC', $this, $match, $regex ); %s %13s '%s' DIAGNOSTIC } return $ok; } # I'm not ready to publish this. It doesn't deal with array return # values from the code or context. #line 1162 sub _try { my( $self, $code, %opts ) = @_; my $error; my $return; { local $!; # eval can mess up $! local $@; # don't set $@ in the test local $SIG{__DIE__}; # don't trip an outside DIE handler. $return = eval { $code->() }; $error = $@; } die $error if $error and $opts{die_on_fail}; return wantarray ? ( $return, $error ) : $return; } #line 1191 sub is_fh { my $self = shift; my $maybe_fh = shift; return 0 unless defined $maybe_fh; return 1 if ref $maybe_fh eq 'GLOB'; # its a glob ref return 1 if ref \$maybe_fh eq 'GLOB'; # its a glob return eval { $maybe_fh->isa("IO::Handle") } || # 5.5.4's tied() and can() doesn't like getting undef eval { ( tied($maybe_fh) || '' )->can('TIEHANDLE') }; } #line 1235 sub level { my( $self, $level ) = @_; if( defined $level ) { $Level = $level; } return $Level; } #line 1267 sub use_numbers { my( $self, $use_nums ) = @_; if( defined $use_nums ) { $self->{Use_Nums} = $use_nums; } return $self->{Use_Nums}; } #line 1300 foreach my $attribute (qw(No_Header No_Ending No_Diag)) { my $method = lc $attribute; my $code = sub { my( $self, $no ) = @_; if( defined $no ) { $self->{$attribute} = $no; } return $self->{$attribute}; }; no strict 'refs'; ## no critic *{ __PACKAGE__ . '::' . $method } = $code; } #line 1353 sub diag { my $self = shift; $self->_print_comment( $self->_diag_fh, @_ ); } #line 1368 sub note { my $self = shift; $self->_print_comment( $self->output, @_ ); } sub _diag_fh { my $self = shift; local $Level = $Level + 1; return $self->in_todo ? $self->todo_output : $self->failure_output; } sub _print_comment { my( $self, $fh, @msgs ) = @_; return if $self->no_diag; return unless @msgs; # Prevent printing headers when compiling (i.e. -c) return if $^C; # Smash args together like print does. # Convert undef to 'undef' so its readable. my $msg = join '', map { defined($_) ? $_ : 'undef' } @msgs; # Escape the beginning, _print will take care of the rest. $msg =~ s/^/# /; local $Level = $Level + 1; $self->_print_to_fh( $fh, $msg ); return 0; } #line 1418 sub explain { my $self = shift; return map { ref $_ ? do { $self->_try(sub { require Data::Dumper }, die_on_fail => 1); my $dumper = Data::Dumper->new( [$_] ); $dumper->Indent(1)->Terse(1); $dumper->Sortkeys(1) if $dumper->can("Sortkeys"); $dumper->Dump; } : $_ } @_; } #line 1447 sub _print { my $self = shift; return $self->_print_to_fh( $self->output, @_ ); } sub _print_to_fh { my( $self, $fh, @msgs ) = @_; # Prevent printing headers when only compiling. Mostly for when # tests are deparsed with B::Deparse return if $^C; my $msg = join '', @msgs; local( $\, $", $, ) = ( undef, ' ', '' ); # Escape each line after the first with a # so we don't # confuse Test::Harness. $msg =~ s{\n(?!\z)}{\n# }sg; # Stick a newline on the end if it needs it. $msg .= "\n" unless $msg =~ /\n\z/; return print $fh $msg; } #line 1506 sub output { my( $self, $fh ) = @_; if( defined $fh ) { $self->{Out_FH} = $self->_new_fh($fh); } return $self->{Out_FH}; } sub failure_output { my( $self, $fh ) = @_; if( defined $fh ) { $self->{Fail_FH} = $self->_new_fh($fh); } return $self->{Fail_FH}; } sub todo_output { my( $self, $fh ) = @_; if( defined $fh ) { $self->{Todo_FH} = $self->_new_fh($fh); } return $self->{Todo_FH}; } sub _new_fh { my $self = shift; my($file_or_fh) = shift; my $fh; if( $self->is_fh($file_or_fh) ) { $fh = $file_or_fh; } elsif( ref $file_or_fh eq 'SCALAR' ) { # Scalar refs as filehandles was added in 5.8. if( $] >= 5.008 ) { open $fh, ">>", $file_or_fh or $self->croak("Can't open scalar ref $file_or_fh: $!"); } # Emulate scalar ref filehandles with a tie. else { $fh = Test::Builder::IO::Scalar->new($file_or_fh) or $self->croak("Can't tie scalar ref $file_or_fh"); } } else { open $fh, ">", $file_or_fh or $self->croak("Can't open test output log $file_or_fh: $!"); _autoflush($fh); } return $fh; } sub _autoflush { my($fh) = shift; my $old_fh = select $fh; $| = 1; select $old_fh; return; } my( $Testout, $Testerr ); sub _dup_stdhandles { my $self = shift; $self->_open_testhandles; # Set everything to unbuffered else plain prints to STDOUT will # come out in the wrong order from our own prints. _autoflush($Testout); _autoflush( \*STDOUT ); _autoflush($Testerr); _autoflush( \*STDERR ); $self->reset_outputs; return; } sub _open_testhandles { my $self = shift; return if $self->{Opened_Testhandles}; # We dup STDOUT and STDERR so people can change them in their # test suites while still getting normal test output. open( $Testout, ">&STDOUT" ) or die "Can't dup STDOUT: $!"; open( $Testerr, ">&STDERR" ) or die "Can't dup STDERR: $!"; # $self->_copy_io_layers( \*STDOUT, $Testout ); # $self->_copy_io_layers( \*STDERR, $Testerr ); $self->{Opened_Testhandles} = 1; return; } sub _copy_io_layers { my( $self, $src, $dst ) = @_; $self->_try( sub { require PerlIO; my @src_layers = PerlIO::get_layers($src); binmode $dst, join " ", map ":$_", @src_layers if @src_layers; } ); return; } #line 1631 sub reset_outputs { my $self = shift; $self->output ($Testout); $self->failure_output($Testerr); $self->todo_output ($Testout); return; } #line 1657 sub _message_at_caller { my $self = shift; local $Level = $Level + 1; my( $pack, $file, $line ) = $self->caller; return join( "", @_ ) . " at $file line $line.\n"; } sub carp { my $self = shift; return warn $self->_message_at_caller(@_); } sub croak { my $self = shift; return die $self->_message_at_caller(@_); } #line 1697 sub current_test { my( $self, $num ) = @_; lock( $self->{Curr_Test} ); if( defined $num ) { $self->{Curr_Test} = $num; # If the test counter is being pushed forward fill in the details. my $test_results = $self->{Test_Results}; if( $num > @$test_results ) { my $start = @$test_results ? @$test_results : 0; for( $start .. $num - 1 ) { $test_results->[$_] = &share( { 'ok' => 1, actual_ok => undef, reason => 'incrementing test number', type => 'unknown', name => undef } ); } } # If backward, wipe history. Its their funeral. elsif( $num < @$test_results ) { $#{$test_results} = $num - 1; } } return $self->{Curr_Test}; } #line 1739 sub summary { my($self) = shift; return map { $_->{'ok'} } @{ $self->{Test_Results} }; } #line 1794 sub details { my $self = shift; return @{ $self->{Test_Results} }; } #line 1823 sub todo { my( $self, $pack ) = @_; return $self->{Todo} if defined $self->{Todo}; local $Level = $Level + 1; my $todo = $self->find_TODO($pack); return $todo if defined $todo; return ''; } #line 1845 sub find_TODO { my( $self, $pack ) = @_; $pack = $pack || $self->caller(1) || $self->exported_to; return unless $pack; no strict 'refs'; ## no critic return ${ $pack . '::TODO' }; } #line 1863 sub in_todo { my $self = shift; local $Level = $Level + 1; return( defined $self->{Todo} || $self->find_TODO ) ? 1 : 0; } #line 1913 sub todo_start { my $self = shift; my $message = @_ ? shift : ''; $self->{Start_Todo}++; if( $self->in_todo ) { push @{ $self->{Todo_Stack} } => $self->todo; } $self->{Todo} = $message; return; } #line 1935 sub todo_end { my $self = shift; if( !$self->{Start_Todo} ) { $self->croak('todo_end() called without todo_start()'); } $self->{Start_Todo}--; if( $self->{Start_Todo} && @{ $self->{Todo_Stack} } ) { $self->{Todo} = pop @{ $self->{Todo_Stack} }; } else { delete $self->{Todo}; } return; } #line 1968 sub caller { ## no critic (Subroutines::ProhibitBuiltinHomonyms) my( $self, $height ) = @_; $height ||= 0; my $level = $self->level + $height + 1; my @caller; do { @caller = CORE::caller( $level ); $level--; } until @caller; return wantarray ? @caller : $caller[0]; } #line 1985 #line 1999 #'# sub _sanity_check { my $self = shift; $self->_whoa( $self->{Curr_Test} < 0, 'Says here you ran a negative number of tests!' ); $self->_whoa( $self->{Curr_Test} != @{ $self->{Test_Results} }, 'Somehow you got a different number of results than tests ran!' ); return; } #line 2020 sub _whoa { my( $self, $check, $desc ) = @_; if($check) { local $Level = $Level + 1; $self->croak(<<"WHOA"); WHOA! $desc This should never happen! Please contact the author immediately! WHOA } return; } #line 2044 sub _my_exit { $? = $_[0]; ## no critic (Variables::RequireLocalizedPunctuationVars) return 1; } #line 2056 sub _ending { my $self = shift; my $real_exit_code = $?; # Don't bother with an ending if this is a forked copy. Only the parent # should do the ending. if( $self->{Original_Pid} != $$ ) { return; } # Ran tests but never declared a plan or hit done_testing if( !$self->{Have_Plan} and $self->{Curr_Test} ) { $self->diag("Tests were run but no plan was declared and done_testing() was not seen."); } # Exit if plan() was never called. This is so "require Test::Simple" # doesn't puke. if( !$self->{Have_Plan} ) { return; } # Don't do an ending if we bailed out. if( $self->{Bailed_Out} ) { return; } # Figure out if we passed or failed and print helpful messages. my $test_results = $self->{Test_Results}; if(@$test_results) { # The plan? We have no plan. if( $self->{No_Plan} ) { $self->_output_plan($self->{Curr_Test}) unless $self->no_header; $self->{Expected_Tests} = $self->{Curr_Test}; } # Auto-extended arrays and elements which aren't explicitly # filled in with a shared reference will puke under 5.8.0 # ithreads. So we have to fill them in by hand. :( my $empty_result = &share( {} ); for my $idx ( 0 .. $self->{Expected_Tests} - 1 ) { $test_results->[$idx] = $empty_result unless defined $test_results->[$idx]; } my $num_failed = grep !$_->{'ok'}, @{$test_results}[ 0 .. $self->{Curr_Test} - 1 ]; my $num_extra = $self->{Curr_Test} - $self->{Expected_Tests}; if( $num_extra != 0 ) { my $s = $self->{Expected_Tests} == 1 ? '' : 's'; $self->diag(<<"FAIL"); Looks like you planned $self->{Expected_Tests} test$s but ran $self->{Curr_Test}. FAIL } if($num_failed) { my $num_tests = $self->{Curr_Test}; my $s = $num_failed == 1 ? '' : 's'; my $qualifier = $num_extra == 0 ? '' : ' run'; $self->diag(<<"FAIL"); Looks like you failed $num_failed test$s of $num_tests$qualifier. FAIL } if($real_exit_code) { $self->diag(<<"FAIL"); Looks like your test exited with $real_exit_code just after $self->{Curr_Test}. FAIL _my_exit($real_exit_code) && return; } my $exit_code; if($num_failed) { $exit_code = $num_failed <= 254 ? $num_failed : 254; } elsif( $num_extra != 0 ) { $exit_code = 255; } else { $exit_code = 0; } _my_exit($exit_code) && return; } elsif( $self->{Skip_All} ) { _my_exit(0) && return; } elsif($real_exit_code) { $self->diag(<<"FAIL"); Looks like your test exited with $real_exit_code before it could output anything. FAIL _my_exit($real_exit_code) && return; } else { $self->diag("No tests run!\n"); _my_exit(255) && return; } $self->_whoa( 1, "We fell off the end of _ending()" ); } END { $Test->_ending if defined $Test and !$Test->no_ending; } #line 2236 1; Makefile-Parser-0.215/inc/Test/Builder/0000755000175000001440000000000011622773367016773 5ustar agentzusersMakefile-Parser-0.215/inc/Test/Builder/Module.pm0000644000175000001440000000261611622773337020560 0ustar agentzusers#line 1 package Test::Builder::Module; use strict; use Test::Builder; require Exporter; our @ISA = qw(Exporter); our $VERSION = '0.92'; $VERSION = eval $VERSION; ## no critic (BuiltinFunctions::ProhibitStringyEval) # 5.004's Exporter doesn't have export_to_level. my $_export_to_level = sub { my $pkg = shift; my $level = shift; (undef) = shift; # redundant arg my $callpkg = caller($level); $pkg->export( $callpkg, @_ ); }; #line 82 sub import { my($class) = shift; # Don't run all this when loading ourself. return 1 if $class eq 'Test::Builder::Module'; my $test = $class->builder; my $caller = caller; $test->exported_to($caller); $class->import_extra( \@_ ); my(@imports) = $class->_strip_imports( \@_ ); $test->plan(@_); $class->$_export_to_level( 1, $class, @imports ); } sub _strip_imports { my $class = shift; my $list = shift; my @imports = (); my @other = (); my $idx = 0; while( $idx <= $#{$list} ) { my $item = $list->[$idx]; if( defined $item and $item eq 'import' ) { push @imports, @{ $list->[ $idx + 1 ] }; $idx++; } else { push @other, $item; } $idx++; } @$list = @other; return @imports; } #line 145 sub import_extra { } #line 175 sub builder { return Test::Builder->new; } 1; Makefile-Parser-0.215/inc/Test/Base.pm0000644000175000001440000004306311622773337016620 0ustar agentzusers#line 1 package Test::Base; use 5.006001; use Spiffy 0.30 -Base; use Spiffy ':XXX'; our $VERSION = '0.60'; my @test_more_exports; BEGIN { @test_more_exports = qw( ok isnt like unlike is_deeply cmp_ok skip todo_skip pass fail eq_array eq_hash eq_set plan can_ok isa_ok diag use_ok $TODO ); } use Test::More import => \@test_more_exports; use Carp; our @EXPORT = (@test_more_exports, qw( is no_diff blocks next_block first_block delimiters spec_file spec_string filters filters_delay filter_arguments run run_compare run_is run_is_deeply run_like run_unlike skip_all_unless_require is_deep run_is_deep WWW XXX YYY ZZZ tie_output no_diag_on_only find_my_self default_object croak carp cluck confess )); field '_spec_file'; field '_spec_string'; field _filters => [qw(norm trim)]; field _filters_map => {}; field spec => -init => '$self->_spec_init'; field block_list => -init => '$self->_block_list_init'; field _next_list => []; field block_delim => -init => '$self->block_delim_default'; field data_delim => -init => '$self->data_delim_default'; field _filters_delay => 0; field _no_diag_on_only => 0; field block_delim_default => '==='; field data_delim_default => '---'; my $default_class; my $default_object; my $reserved_section_names = {}; sub default_object { $default_object ||= $default_class->new; return $default_object; } my $import_called = 0; sub import() { $import_called = 1; my $class = (grep /^-base$/i, @_) ? scalar(caller) : $_[0]; if (not defined $default_class) { $default_class = $class; } # else { # croak "Can't use $class after using $default_class" # unless $default_class->isa($class); # } unless (grep /^-base$/i, @_) { my @args; for (my $ii = 1; $ii <= $#_; ++$ii) { if ($_[$ii] eq '-package') { ++$ii; } else { push @args, $_[$ii]; } } Test::More->import(import => \@test_more_exports, @args) if @args; } _strict_warnings(); goto &Spiffy::import; } # Wrap Test::Builder::plan my $plan_code = \&Test::Builder::plan; my $Have_Plan = 0; { no warnings 'redefine'; *Test::Builder::plan = sub { $Have_Plan = 1; goto &$plan_code; }; } my $DIED = 0; $SIG{__DIE__} = sub { $DIED = 1; die @_ }; sub block_class { $self->find_class('Block') } sub filter_class { $self->find_class('Filter') } sub find_class { my $suffix = shift; my $class = ref($self) . "::$suffix"; return $class if $class->can('new'); $class = __PACKAGE__ . "::$suffix"; return $class if $class->can('new'); eval "require $class"; return $class if $class->can('new'); die "Can't find a class for $suffix"; } sub check_late { if ($self->{block_list}) { my $caller = (caller(1))[3]; $caller =~ s/.*:://; croak "Too late to call $caller()" } } sub find_my_self() { my $self = ref($_[0]) eq $default_class ? splice(@_, 0, 1) : default_object(); return $self, @_; } sub blocks() { (my ($self), @_) = find_my_self(@_); croak "Invalid arguments passed to 'blocks'" if @_ > 1; croak sprintf("'%s' is invalid argument to blocks()", shift(@_)) if @_ && $_[0] !~ /^[a-zA-Z]\w*$/; my $blocks = $self->block_list; my $section_name = shift || ''; my @blocks = $section_name ? (grep { exists $_->{$section_name} } @$blocks) : (@$blocks); return scalar(@blocks) unless wantarray; return (@blocks) if $self->_filters_delay; for my $block (@blocks) { $block->run_filters unless $block->is_filtered; } return (@blocks); } sub next_block() { (my ($self), @_) = find_my_self(@_); my $list = $self->_next_list; if (@$list == 0) { $list = [@{$self->block_list}, undef]; $self->_next_list($list); } my $block = shift @$list; if (defined $block and not $block->is_filtered) { $block->run_filters; } return $block; } sub first_block() { (my ($self), @_) = find_my_self(@_); $self->_next_list([]); $self->next_block; } sub filters_delay() { (my ($self), @_) = find_my_self(@_); $self->_filters_delay(defined $_[0] ? shift : 1); } sub no_diag_on_only() { (my ($self), @_) = find_my_self(@_); $self->_no_diag_on_only(defined $_[0] ? shift : 1); } sub delimiters() { (my ($self), @_) = find_my_self(@_); $self->check_late; my ($block_delimiter, $data_delimiter) = @_; $block_delimiter ||= $self->block_delim_default; $data_delimiter ||= $self->data_delim_default; $self->block_delim($block_delimiter); $self->data_delim($data_delimiter); return $self; } sub spec_file() { (my ($self), @_) = find_my_self(@_); $self->check_late; $self->_spec_file(shift); return $self; } sub spec_string() { (my ($self), @_) = find_my_self(@_); $self->check_late; $self->_spec_string(shift); return $self; } sub filters() { (my ($self), @_) = find_my_self(@_); if (ref($_[0]) eq 'HASH') { $self->_filters_map(shift); } else { my $filters = $self->_filters; push @$filters, @_; } return $self; } sub filter_arguments() { $Test::Base::Filter::arguments; } sub have_text_diff { eval { require Text::Diff; 1 } && $Text::Diff::VERSION >= 0.35 && $Algorithm::Diff::VERSION >= 1.15; } sub is($$;$) { (my ($self), @_) = find_my_self(@_); my ($actual, $expected, $name) = @_; local $Test::Builder::Level = $Test::Builder::Level + 1; if ($ENV{TEST_SHOW_NO_DIFFS} or not defined $actual or not defined $expected or $actual eq $expected or not($self->have_text_diff) or $expected !~ /\n./s ) { Test::More::is($actual, $expected, $name); } else { $name = '' unless defined $name; ok $actual eq $expected, $name . "\n" . Text::Diff::diff(\$expected, \$actual); } } sub run(&;$) { (my ($self), @_) = find_my_self(@_); my $callback = shift; for my $block (@{$self->block_list}) { $block->run_filters unless $block->is_filtered; &{$callback}($block); } } my $name_error = "Can't determine section names"; sub _section_names { return @_ if @_ == 2; my $block = $self->first_block or croak $name_error; my @names = grep { $_ !~ /^(ONLY|LAST|SKIP)$/; } @{$block->{_section_order}[0] || []}; croak "$name_error. Need two sections in first block" unless @names == 2; return @names; } sub _assert_plan { plan('no_plan') unless $Have_Plan; } sub END { run_compare() unless $Have_Plan or $DIED or not $import_called; } sub run_compare() { (my ($self), @_) = find_my_self(@_); $self->_assert_plan; my ($x, $y) = $self->_section_names(@_); local $Test::Builder::Level = $Test::Builder::Level + 1; for my $block (@{$self->block_list}) { next unless exists($block->{$x}) and exists($block->{$y}); $block->run_filters unless $block->is_filtered; if (ref $block->$x) { is_deeply($block->$x, $block->$y, $block->name ? $block->name : ()); } elsif (ref $block->$y eq 'Regexp') { my $regexp = ref $y ? $y : $block->$y; like($block->$x, $regexp, $block->name ? $block->name : ()); } else { is($block->$x, $block->$y, $block->name ? $block->name : ()); } } } sub run_is() { (my ($self), @_) = find_my_self(@_); $self->_assert_plan; my ($x, $y) = $self->_section_names(@_); local $Test::Builder::Level = $Test::Builder::Level + 1; for my $block (@{$self->block_list}) { next unless exists($block->{$x}) and exists($block->{$y}); $block->run_filters unless $block->is_filtered; is($block->$x, $block->$y, $block->name ? $block->name : () ); } } sub run_is_deeply() { (my ($self), @_) = find_my_self(@_); $self->_assert_plan; my ($x, $y) = $self->_section_names(@_); for my $block (@{$self->block_list}) { next unless exists($block->{$x}) and exists($block->{$y}); $block->run_filters unless $block->is_filtered; is_deeply($block->$x, $block->$y, $block->name ? $block->name : () ); } } sub run_like() { (my ($self), @_) = find_my_self(@_); $self->_assert_plan; my ($x, $y) = $self->_section_names(@_); for my $block (@{$self->block_list}) { next unless exists($block->{$x}) and defined($y); $block->run_filters unless $block->is_filtered; my $regexp = ref $y ? $y : $block->$y; like($block->$x, $regexp, $block->name ? $block->name : () ); } } sub run_unlike() { (my ($self), @_) = find_my_self(@_); $self->_assert_plan; my ($x, $y) = $self->_section_names(@_); for my $block (@{$self->block_list}) { next unless exists($block->{$x}) and defined($y); $block->run_filters unless $block->is_filtered; my $regexp = ref $y ? $y : $block->$y; unlike($block->$x, $regexp, $block->name ? $block->name : () ); } } sub skip_all_unless_require() { (my ($self), @_) = find_my_self(@_); my $module = shift; eval "require $module; 1" or Test::More::plan( skip_all => "$module failed to load" ); } sub is_deep() { (my ($self), @_) = find_my_self(@_); require Test::Deep; Test::Deep::cmp_deeply(@_); } sub run_is_deep() { (my ($self), @_) = find_my_self(@_); $self->_assert_plan; my ($x, $y) = $self->_section_names(@_); for my $block (@{$self->block_list}) { next unless exists($block->{$x}) and exists($block->{$y}); $block->run_filters unless $block->is_filtered; is_deep($block->$x, $block->$y, $block->name ? $block->name : () ); } } sub _pre_eval { my $spec = shift; return $spec unless $spec =~ s/\A\s*<<<(.*?)>>>\s*$//sm; my $eval_code = $1; eval "package main; $eval_code"; croak $@ if $@; return $spec; } sub _block_list_init { my $spec = $self->spec; $spec = $self->_pre_eval($spec); my $cd = $self->block_delim; my @hunks = ($spec =~ /^(\Q${cd}\E.*?(?=^\Q${cd}\E|\z))/msg); my $blocks = $self->_choose_blocks(@hunks); $self->block_list($blocks); # Need to set early for possible filter use my $seq = 1; for my $block (@$blocks) { $block->blocks_object($self); $block->seq_num($seq++); } return $blocks; } sub _choose_blocks { my $blocks = []; for my $hunk (@_) { my $block = $self->_make_block($hunk); if (exists $block->{ONLY}) { diag "I found ONLY: maybe you're debugging?" unless $self->_no_diag_on_only; return [$block]; } next if exists $block->{SKIP}; push @$blocks, $block; if (exists $block->{LAST}) { return $blocks; } } return $blocks; } sub _check_reserved { my $id = shift; croak "'$id' is a reserved name. Use something else.\n" if $reserved_section_names->{$id} or $id =~ /^_/; } sub _make_block { my $hunk = shift; my $cd = $self->block_delim; my $dd = $self->data_delim; my $block = $self->block_class->new; $hunk =~ s/\A\Q${cd}\E[ \t]*(.*)\s+// or die; my $name = $1; my @parts = split /^\Q${dd}\E +\(?(\w+)\)? *(.*)?\n/m, $hunk; my $description = shift @parts; $description ||= ''; unless ($description =~ /\S/) { $description = $name; } $description =~ s/\s*\z//; $block->set_value(description => $description); my $section_map = {}; my $section_order = []; while (@parts) { my ($type, $filters, $value) = splice(@parts, 0, 3); $self->_check_reserved($type); $value = '' unless defined $value; $filters = '' unless defined $filters; if ($filters =~ /:(\s|\z)/) { croak "Extra lines not allowed in '$type' section" if $value =~ /\S/; ($filters, $value) = split /\s*:(?:\s+|\z)/, $filters, 2; $value = '' unless defined $value; $value =~ s/^\s*(.*?)\s*$/$1/; } $section_map->{$type} = { filters => $filters, }; push @$section_order, $type; $block->set_value($type, $value); } $block->set_value(name => $name); $block->set_value(_section_map => $section_map); $block->set_value(_section_order => $section_order); return $block; } sub _spec_init { return $self->_spec_string if $self->_spec_string; local $/; my $spec; if (my $spec_file = $self->_spec_file) { open FILE, $spec_file or die $!; $spec = ; close FILE; } else { $spec = do { package main; no warnings 'once'; ; }; } return $spec; } sub _strict_warnings() { require Filter::Util::Call; my $done = 0; Filter::Util::Call::filter_add( sub { return 0 if $done; my ($data, $end) = ('', ''); while (my $status = Filter::Util::Call::filter_read()) { return $status if $status < 0; if (/^__(?:END|DATA)__\r?$/) { $end = $_; last; } $data .= $_; $_ = ''; } $_ = "use strict;use warnings;$data$end"; $done = 1; } ); } sub tie_output() { my $handle = shift; die "No buffer to tie" unless @_; tie *$handle, 'Test::Base::Handle', $_[0]; } sub no_diff { $ENV{TEST_SHOW_NO_DIFFS} = 1; } package Test::Base::Handle; sub TIEHANDLE() { my $class = shift; bless \ $_[0], $class; } sub PRINT { $$self .= $_ for @_; } #=============================================================================== # Test::Base::Block # # This is the default class for accessing a Test::Base block object. #=============================================================================== package Test::Base::Block; our @ISA = qw(Spiffy); our @EXPORT = qw(block_accessor); sub AUTOLOAD { return; } sub block_accessor() { my $accessor = shift; no strict 'refs'; return if defined &$accessor; *$accessor = sub { my $self = shift; if (@_) { Carp::croak "Not allowed to set values for '$accessor'"; } my @list = @{$self->{$accessor} || []}; return wantarray ? (@list) : $list[0]; }; } block_accessor 'name'; block_accessor 'description'; Spiffy::field 'seq_num'; Spiffy::field 'is_filtered'; Spiffy::field 'blocks_object'; Spiffy::field 'original_values' => {}; sub set_value { no strict 'refs'; my $accessor = shift; block_accessor $accessor unless defined &$accessor; $self->{$accessor} = [@_]; } sub run_filters { my $map = $self->_section_map; my $order = $self->_section_order; Carp::croak "Attempt to filter a block twice" if $self->is_filtered; for my $type (@$order) { my $filters = $map->{$type}{filters}; my @value = $self->$type; $self->original_values->{$type} = $value[0]; for my $filter ($self->_get_filters($type, $filters)) { $Test::Base::Filter::arguments = $filter =~ s/=(.*)$// ? $1 : undef; my $function = "main::$filter"; no strict 'refs'; if (defined &$function) { local $_ = (@value == 1 and not defined($value[0])) ? undef : join '', @value; my $old = $_; @value = &$function(@value); if (not(@value) or @value == 1 and defined($value[0]) and $value[0] =~ /\A(\d+|)\z/ ) { if ($value[0] && $_ eq $old) { Test::Base::diag("Filters returning numbers are supposed to do munging \$_: your filter '$function' apparently doesn't."); } @value = ($_); } } else { my $filter_object = $self->blocks_object->filter_class->new; die "Can't find a function or method for '$filter' filter\n" unless $filter_object->can($filter); $filter_object->current_block($self); @value = $filter_object->$filter(@value); } # Set the value after each filter since other filters may be # introspecting. $self->set_value($type, @value); } } $self->is_filtered(1); } sub _get_filters { my $type = shift; my $string = shift || ''; $string =~ s/\s*(.*?)\s*/$1/; my @filters = (); my $map_filters = $self->blocks_object->_filters_map->{$type} || []; $map_filters = [ $map_filters ] unless ref $map_filters; my @append = (); for ( @{$self->blocks_object->_filters}, @$map_filters, split(/\s+/, $string), ) { my $filter = $_; last unless length $filter; if ($filter =~ s/^-//) { @filters = grep { $_ ne $filter } @filters; } elsif ($filter =~ s/^\+//) { push @append, $filter; } else { push @filters, $filter; } } return @filters, @append; } { %$reserved_section_names = map { ($_, 1); } keys(%Test::Base::Block::), qw( new DESTROY ); } __DATA__ =encoding utf8 #line 1374 Makefile-Parser-0.215/inc/Test/More.pm0000644000175000001440000004054511622773337016652 0ustar agentzusers#line 1 package Test::More; use 5.006; use strict; use warnings; #---- perlcritic exemptions. ----# # We use a lot of subroutine prototypes ## no critic (Subroutines::ProhibitSubroutinePrototypes) # Can't use Carp because it might cause use_ok() to accidentally succeed # even though the module being used forgot to use Carp. Yes, this # actually happened. sub _carp { my( $file, $line ) = ( caller(1) )[ 1, 2 ]; return warn @_, " at $file line $line\n"; } our $VERSION = '0.92'; $VERSION = eval $VERSION; ## no critic (BuiltinFunctions::ProhibitStringyEval) use Test::Builder::Module; our @ISA = qw(Test::Builder::Module); our @EXPORT = qw(ok use_ok require_ok is isnt like unlike is_deeply cmp_ok skip todo todo_skip pass fail eq_array eq_hash eq_set $TODO plan done_testing can_ok isa_ok new_ok diag note explain BAIL_OUT ); #line 163 sub plan { my $tb = Test::More->builder; return $tb->plan(@_); } # This implements "use Test::More 'no_diag'" but the behavior is # deprecated. sub import_extra { my $class = shift; my $list = shift; my @other = (); my $idx = 0; while( $idx <= $#{$list} ) { my $item = $list->[$idx]; if( defined $item and $item eq 'no_diag' ) { $class->builder->no_diag(1); } else { push @other, $item; } $idx++; } @$list = @other; return; } #line 216 sub done_testing { my $tb = Test::More->builder; $tb->done_testing(@_); } #line 289 sub ok ($;$) { my( $test, $name ) = @_; my $tb = Test::More->builder; return $tb->ok( $test, $name ); } #line 367 sub is ($$;$) { my $tb = Test::More->builder; return $tb->is_eq(@_); } sub isnt ($$;$) { my $tb = Test::More->builder; return $tb->isnt_eq(@_); } *isn't = \&isnt; #line 411 sub like ($$;$) { my $tb = Test::More->builder; return $tb->like(@_); } #line 426 sub unlike ($$;$) { my $tb = Test::More->builder; return $tb->unlike(@_); } #line 471 sub cmp_ok($$$;$) { my $tb = Test::More->builder; return $tb->cmp_ok(@_); } #line 506 sub can_ok ($@) { my( $proto, @methods ) = @_; my $class = ref $proto || $proto; my $tb = Test::More->builder; unless($class) { my $ok = $tb->ok( 0, "->can(...)" ); $tb->diag(' can_ok() called with empty class or reference'); return $ok; } unless(@methods) { my $ok = $tb->ok( 0, "$class->can(...)" ); $tb->diag(' can_ok() called with no methods'); return $ok; } my @nok = (); foreach my $method (@methods) { $tb->_try( sub { $proto->can($method) } ) or push @nok, $method; } my $name = (@methods == 1) ? "$class->can('$methods[0]')" : "$class->can(...)" ; my $ok = $tb->ok( !@nok, $name ); $tb->diag( map " $class->can('$_') failed\n", @nok ); return $ok; } #line 572 sub isa_ok ($$;$) { my( $object, $class, $obj_name ) = @_; my $tb = Test::More->builder; my $diag; if( !defined $object ) { $obj_name = 'The thing' unless defined $obj_name; $diag = "$obj_name isn't defined"; } else { my $whatami = ref $object ? 'object' : 'class'; # We can't use UNIVERSAL::isa because we want to honor isa() overrides my( $rslt, $error ) = $tb->_try( sub { $object->isa($class) } ); if($error) { if( $error =~ /^Can't call method "isa" on unblessed reference/ ) { # Its an unblessed reference $obj_name = 'The reference' unless defined $obj_name; if( !UNIVERSAL::isa( $object, $class ) ) { my $ref = ref $object; $diag = "$obj_name isn't a '$class' it's a '$ref'"; } } elsif( $error =~ /Can't call method "isa" without a package/ ) { # It's something that can't even be a class $diag = "$obj_name isn't a class or reference"; } else { die <isa on your $whatami and got some weird error. Here's the error. $error WHOA } } else { $obj_name = "The $whatami" unless defined $obj_name; if( !$rslt ) { my $ref = ref $object; $diag = "$obj_name isn't a '$class' it's a '$ref'"; } } } my $name = "$obj_name isa $class"; my $ok; if($diag) { $ok = $tb->ok( 0, $name ); $tb->diag(" $diag\n"); } else { $ok = $tb->ok( 1, $name ); } return $ok; } #line 650 sub new_ok { my $tb = Test::More->builder; $tb->croak("new_ok() must be given at least a class") unless @_; my( $class, $args, $object_name ) = @_; $args ||= []; $object_name = "The object" unless defined $object_name; my $obj; my( $success, $error ) = $tb->_try( sub { $obj = $class->new(@$args); 1 } ); if($success) { local $Test::Builder::Level = $Test::Builder::Level + 1; isa_ok $obj, $class, $object_name; } else { $tb->ok( 0, "new() died" ); $tb->diag(" Error was: $error"); } return $obj; } #line 690 sub pass (;$) { my $tb = Test::More->builder; return $tb->ok( 1, @_ ); } sub fail (;$) { my $tb = Test::More->builder; return $tb->ok( 0, @_ ); } #line 753 sub use_ok ($;@) { my( $module, @imports ) = @_; @imports = () unless @imports; my $tb = Test::More->builder; my( $pack, $filename, $line ) = caller; my $code; if( @imports == 1 and $imports[0] =~ /^\d+(?:\.\d+)?$/ ) { # probably a version check. Perl needs to see the bare number # for it to work with non-Exporter based modules. $code = <ok( $eval_result, "use $module;" ); unless($ok) { chomp $eval_error; $@ =~ s{^BEGIN failed--compilation aborted at .*$} {BEGIN failed--compilation aborted at $filename line $line.}m; $tb->diag(<builder; my $pack = caller; # Try to deterine if we've been given a module name or file. # Module names must be barewords, files not. $module = qq['$module'] unless _is_module_name($module); my $code = <ok( $eval_result, "require $module;" ); unless($ok) { chomp $eval_error; $tb->diag(<builder; unless( @_ == 2 or @_ == 3 ) { my $msg = <<'WARNING'; is_deeply() takes two or three args, you gave %d. This usually means you passed an array or hash instead of a reference to it WARNING chop $msg; # clip off newline so carp() will put in line/file _carp sprintf $msg, scalar @_; return $tb->ok(0); } my( $got, $expected, $name ) = @_; $tb->_unoverload_str( \$expected, \$got ); my $ok; if( !ref $got and !ref $expected ) { # neither is a reference $ok = $tb->is_eq( $got, $expected, $name ); } elsif( !ref $got xor !ref $expected ) { # one's a reference, one isn't $ok = $tb->ok( 0, $name ); $tb->diag( _format_stack({ vals => [ $got, $expected ] }) ); } else { # both references local @Data_Stack = (); if( _deep_check( $got, $expected ) ) { $ok = $tb->ok( 1, $name ); } else { $ok = $tb->ok( 0, $name ); $tb->diag( _format_stack(@Data_Stack) ); } } return $ok; } sub _format_stack { my(@Stack) = @_; my $var = '$FOO'; my $did_arrow = 0; foreach my $entry (@Stack) { my $type = $entry->{type} || ''; my $idx = $entry->{'idx'}; if( $type eq 'HASH' ) { $var .= "->" unless $did_arrow++; $var .= "{$idx}"; } elsif( $type eq 'ARRAY' ) { $var .= "->" unless $did_arrow++; $var .= "[$idx]"; } elsif( $type eq 'REF' ) { $var = "\${$var}"; } } my @vals = @{ $Stack[-1]{vals} }[ 0, 1 ]; my @vars = (); ( $vars[0] = $var ) =~ s/\$FOO/ \$got/; ( $vars[1] = $var ) =~ s/\$FOO/\$expected/; my $out = "Structures begin differing at:\n"; foreach my $idx ( 0 .. $#vals ) { my $val = $vals[$idx]; $vals[$idx] = !defined $val ? 'undef' : _dne($val) ? "Does not exist" : ref $val ? "$val" : "'$val'"; } $out .= "$vars[0] = $vals[0]\n"; $out .= "$vars[1] = $vals[1]\n"; $out =~ s/^/ /msg; return $out; } sub _type { my $thing = shift; return '' if !ref $thing; for my $type (qw(ARRAY HASH REF SCALAR GLOB CODE Regexp)) { return $type if UNIVERSAL::isa( $thing, $type ); } return ''; } #line 1059 sub diag { return Test::More->builder->diag(@_); } sub note { return Test::More->builder->note(@_); } #line 1085 sub explain { return Test::More->builder->explain(@_); } #line 1151 ## no critic (Subroutines::RequireFinalReturn) sub skip { my( $why, $how_many ) = @_; my $tb = Test::More->builder; unless( defined $how_many ) { # $how_many can only be avoided when no_plan is in use. _carp "skip() needs to know \$how_many tests are in the block" unless $tb->has_plan eq 'no_plan'; $how_many = 1; } if( defined $how_many and $how_many =~ /\D/ ) { _carp "skip() was passed a non-numeric number of tests. Did you get the arguments backwards?"; $how_many = 1; } for( 1 .. $how_many ) { $tb->skip($why); } no warnings 'exiting'; last SKIP; } #line 1238 sub todo_skip { my( $why, $how_many ) = @_; my $tb = Test::More->builder; unless( defined $how_many ) { # $how_many can only be avoided when no_plan is in use. _carp "todo_skip() needs to know \$how_many tests are in the block" unless $tb->has_plan eq 'no_plan'; $how_many = 1; } for( 1 .. $how_many ) { $tb->todo_skip($why); } no warnings 'exiting'; last TODO; } #line 1293 sub BAIL_OUT { my $reason = shift; my $tb = Test::More->builder; $tb->BAIL_OUT($reason); } #line 1332 #'# sub eq_array { local @Data_Stack = (); _deep_check(@_); } sub _eq_array { my( $a1, $a2 ) = @_; if( grep _type($_) ne 'ARRAY', $a1, $a2 ) { warn "eq_array passed a non-array ref"; return 0; } return 1 if $a1 eq $a2; my $ok = 1; my $max = $#$a1 > $#$a2 ? $#$a1 : $#$a2; for( 0 .. $max ) { my $e1 = $_ > $#$a1 ? $DNE : $a1->[$_]; my $e2 = $_ > $#$a2 ? $DNE : $a2->[$_]; push @Data_Stack, { type => 'ARRAY', idx => $_, vals => [ $e1, $e2 ] }; $ok = _deep_check( $e1, $e2 ); pop @Data_Stack if $ok; last unless $ok; } return $ok; } sub _deep_check { my( $e1, $e2 ) = @_; my $tb = Test::More->builder; my $ok = 0; # Effectively turn %Refs_Seen into a stack. This avoids picking up # the same referenced used twice (such as [\$a, \$a]) to be considered # circular. local %Refs_Seen = %Refs_Seen; { # Quiet uninitialized value warnings when comparing undefs. no warnings 'uninitialized'; $tb->_unoverload_str( \$e1, \$e2 ); # Either they're both references or both not. my $same_ref = !( !ref $e1 xor !ref $e2 ); my $not_ref = ( !ref $e1 and !ref $e2 ); if( defined $e1 xor defined $e2 ) { $ok = 0; } elsif( !defined $e1 and !defined $e2 ) { # Shortcut if they're both defined. $ok = 1; } elsif( _dne($e1) xor _dne($e2) ) { $ok = 0; } elsif( $same_ref and( $e1 eq $e2 ) ) { $ok = 1; } elsif($not_ref) { push @Data_Stack, { type => '', vals => [ $e1, $e2 ] }; $ok = 0; } else { if( $Refs_Seen{$e1} ) { return $Refs_Seen{$e1} eq $e2; } else { $Refs_Seen{$e1} = "$e2"; } my $type = _type($e1); $type = 'DIFFERENT' unless _type($e2) eq $type; if( $type eq 'DIFFERENT' ) { push @Data_Stack, { type => $type, vals => [ $e1, $e2 ] }; $ok = 0; } elsif( $type eq 'ARRAY' ) { $ok = _eq_array( $e1, $e2 ); } elsif( $type eq 'HASH' ) { $ok = _eq_hash( $e1, $e2 ); } elsif( $type eq 'REF' ) { push @Data_Stack, { type => $type, vals => [ $e1, $e2 ] }; $ok = _deep_check( $$e1, $$e2 ); pop @Data_Stack if $ok; } elsif( $type eq 'SCALAR' ) { push @Data_Stack, { type => 'REF', vals => [ $e1, $e2 ] }; $ok = _deep_check( $$e1, $$e2 ); pop @Data_Stack if $ok; } elsif($type) { push @Data_Stack, { type => $type, vals => [ $e1, $e2 ] }; $ok = 0; } else { _whoa( 1, "No type in _deep_check" ); } } } return $ok; } sub _whoa { my( $check, $desc ) = @_; if($check) { die <<"WHOA"; WHOA! $desc This should never happen! Please contact the author immediately! WHOA } } #line 1465 sub eq_hash { local @Data_Stack = (); return _deep_check(@_); } sub _eq_hash { my( $a1, $a2 ) = @_; if( grep _type($_) ne 'HASH', $a1, $a2 ) { warn "eq_hash passed a non-hash ref"; return 0; } return 1 if $a1 eq $a2; my $ok = 1; my $bigger = keys %$a1 > keys %$a2 ? $a1 : $a2; foreach my $k ( keys %$bigger ) { my $e1 = exists $a1->{$k} ? $a1->{$k} : $DNE; my $e2 = exists $a2->{$k} ? $a2->{$k} : $DNE; push @Data_Stack, { type => 'HASH', idx => $k, vals => [ $e1, $e2 ] }; $ok = _deep_check( $e1, $e2 ); pop @Data_Stack if $ok; last unless $ok; } return $ok; } #line 1522 sub eq_set { my( $a1, $a2 ) = @_; return 0 unless @$a1 == @$a2; no warnings 'uninitialized'; # It really doesn't matter how we sort them, as long as both arrays are # sorted with the same algorithm. # # Ensure that references are not accidentally treated the same as a # string containing the reference. # # Have to inline the sort routine due to a threading/sort bug. # See [rt.cpan.org 6782] # # I don't know how references would be sorted so we just don't sort # them. This means eq_set doesn't really work with refs. return eq_array( [ grep( ref, @$a1 ), sort( grep( !ref, @$a1 ) ) ], [ grep( ref, @$a2 ), sort( grep( !ref, @$a2 ) ) ], ); } #line 1735 1; Makefile-Parser-0.215/inc/Test/Base/0000755000175000001440000000000011622773367016257 5ustar agentzusersMakefile-Parser-0.215/inc/Test/Base/Filter.pm0000644000175000001440000001576611622773337020056 0ustar agentzusers#line 1 #=============================================================================== # This is the default class for handling Test::Base data filtering. #=============================================================================== package Test::Base::Filter; use Spiffy -Base; use Spiffy ':XXX'; field 'current_block'; our $arguments; sub current_arguments { return undef unless defined $arguments; my $args = $arguments; $args =~ s/(\\s)/ /g; $args =~ s/(\\[a-z])/'"' . $1 . '"'/gee; return $args; } sub assert_scalar { return if @_ == 1; require Carp; my $filter = (caller(1))[3]; $filter =~ s/.*:://; Carp::croak "Input to the '$filter' filter must be a scalar, not a list"; } sub _apply_deepest { my $method = shift; return () unless @_; if (ref $_[0] eq 'ARRAY') { for my $aref (@_) { @$aref = $self->_apply_deepest($method, @$aref); } return @_; } $self->$method(@_); } sub _split_array { map { [$self->split($_)]; } @_; } sub _peel_deepest { return () unless @_; if (ref $_[0] eq 'ARRAY') { if (ref $_[0]->[0] eq 'ARRAY') { for my $aref (@_) { @$aref = $self->_peel_deepest(@$aref); } return @_; } return map { $_->[0] } @_; } return @_; } #=============================================================================== # these filters work on the leaves of nested arrays #=============================================================================== sub Join { $self->_peel_deepest($self->_apply_deepest(join => @_)) } sub Reverse { $self->_apply_deepest(reverse => @_) } sub Split { $self->_apply_deepest(_split_array => @_) } sub Sort { $self->_apply_deepest(sort => @_) } sub append { my $suffix = $self->current_arguments; map { $_ . $suffix } @_; } sub array { return [@_]; } sub base64_decode { $self->assert_scalar(@_); require MIME::Base64; MIME::Base64::decode_base64(shift); } sub base64_encode { $self->assert_scalar(@_); require MIME::Base64; MIME::Base64::encode_base64(shift); } sub chomp { map { CORE::chomp; $_ } @_; } sub chop { map { CORE::chop; $_ } @_; } sub dumper { no warnings 'once'; require Data::Dumper; local $Data::Dumper::Sortkeys = 1; local $Data::Dumper::Indent = 1; local $Data::Dumper::Terse = 1; Data::Dumper::Dumper(@_); } sub escape { $self->assert_scalar(@_); my $text = shift; $text =~ s/(\\.)/eval "qq{$1}"/ge; return $text; } sub eval { $self->assert_scalar(@_); my @return = CORE::eval(shift); return $@ if $@; return @return; } sub eval_all { $self->assert_scalar(@_); my $out = ''; my $err = ''; Test::Base::tie_output(*STDOUT, $out); Test::Base::tie_output(*STDERR, $err); my $return = CORE::eval(shift); no warnings; untie *STDOUT; untie *STDERR; return $return, $@, $out, $err; } sub eval_stderr { $self->assert_scalar(@_); my $output = ''; Test::Base::tie_output(*STDERR, $output); CORE::eval(shift); no warnings; untie *STDERR; return $output; } sub eval_stdout { $self->assert_scalar(@_); my $output = ''; Test::Base::tie_output(*STDOUT, $output); CORE::eval(shift); no warnings; untie *STDOUT; return $output; } sub exec_perl_stdout { my $tmpfile = "/tmp/test-blocks-$$"; $self->_write_to($tmpfile, @_); open my $execution, "$^X $tmpfile 2>&1 |" or die "Couldn't open subprocess: $!\n"; local $/; my $output = <$execution>; close $execution; unlink($tmpfile) or die "Couldn't unlink $tmpfile: $!\n"; return $output; } sub flatten { $self->assert_scalar(@_); my $ref = shift; if (ref($ref) eq 'HASH') { return map { ($_, $ref->{$_}); } sort keys %$ref; } if (ref($ref) eq 'ARRAY') { return @$ref; } die "Can only flatten a hash or array ref"; } sub get_url { $self->assert_scalar(@_); my $url = shift; CORE::chomp($url); require LWP::Simple; LWP::Simple::get($url); } sub hash { return +{ @_ }; } sub head { my $size = $self->current_arguments || 1; return splice(@_, 0, $size); } sub join { my $string = $self->current_arguments; $string = '' unless defined $string; CORE::join $string, @_; } sub lines { $self->assert_scalar(@_); my $text = shift; return () unless length $text; my @lines = ($text =~ /^(.*\n?)/gm); return @lines; } sub norm { $self->assert_scalar(@_); my $text = shift; $text = '' unless defined $text; $text =~ s/\015\012/\n/g; $text =~ s/\r/\n/g; return $text; } sub prepend { my $prefix = $self->current_arguments; map { $prefix . $_ } @_; } sub read_file { $self->assert_scalar(@_); my $file = shift; CORE::chomp $file; open my $fh, $file or die "Can't open '$file' for input:\n$!"; CORE::join '', <$fh>; } sub regexp { $self->assert_scalar(@_); my $text = shift; my $flags = $self->current_arguments; if ($text =~ /\n.*?\n/s) { $flags = 'xism' unless defined $flags; } else { CORE::chomp($text); } $flags ||= ''; my $regexp = eval "qr{$text}$flags"; die $@ if $@; return $regexp; } sub reverse { CORE::reverse(@_); } sub slice { die "Invalid args for slice" unless $self->current_arguments =~ /^(\d+)(?:,(\d))?$/; my ($x, $y) = ($1, $2); $y = $x if not defined $y; die "Invalid args for slice" if $x > $y; return splice(@_, $x, 1 + $y - $x); } sub sort { CORE::sort(@_); } sub split { $self->assert_scalar(@_); my $separator = $self->current_arguments; if (defined $separator and $separator =~ s{^/(.*)/$}{$1}) { my $regexp = $1; $separator = qr{$regexp}; } $separator = qr/\s+/ unless $separator; CORE::split $separator, shift; } sub strict { $self->assert_scalar(@_); <<'...' . shift; use strict; use warnings; ... } sub tail { my $size = $self->current_arguments || 1; return splice(@_, @_ - $size, $size); } sub trim { map { s/\A([ \t]*\n)+//; s/(?<=\n)\s*\z//g; $_; } @_; } sub unchomp { map { $_ . "\n" } @_; } sub write_file { my $file = $self->current_arguments or die "No file specified for write_file filter"; if ($file =~ /(.*)[\\\/]/) { my $dir = $1; if (not -e $dir) { require File::Path; File::Path::mkpath($dir) or die "Can't create $dir"; } } open my $fh, ">$file" or die "Can't open '$file' for output\n:$!"; print $fh @_; close $fh; return $file; } sub yaml { $self->assert_scalar(@_); require YAML; return YAML::Load(shift); } sub _write_to { my $filename = shift; open my $script, ">$filename" or die "Couldn't open $filename: $!\n"; print $script @_; close $script or die "Couldn't close $filename: $!\n"; } __DATA__ #line 636