home *** CD-ROM | disk | FTP | other *** search
- package Make::Rule::Vars;
- use Carp;
- use strict;
-
- # Package to handle 'magic' variables pertaining to rules e.g. $@ $* $^ $?
- # by using tie to this package 'subsvars' can work with array of
- # hash references to possible sources of variable definitions.
-
- sub TIEHASH
- {
- my ($class,$rule) = @_;
- return bless \$rule,$class;
- }
-
- sub FETCH
- {
- my $self = shift;
- local $_ = shift;
- my $rule = $$self;
- return undef unless (/^[\@^<?*]$/);
- # print STDERR "FETCH $_ for ",$rule->Name,"\n";
- return $rule->Name if ($_ eq '@');
- return $rule->Base if ($_ eq '*');
- return join(' ',$rule->exp_depend) if ($_ eq '^');
- return join(' ',$rule->out_of_date) if ($_ eq '?');
- # Next one is dubious - I think $< is really more subtle ...
- return ($rule->exp_depend)[0] if ($_ eq '<');
- return undef;
- }
-
- package Make::Rule;
- use Carp;
- use strict;
-
- # Bottom level 'rule' package
- # An instance exists for each ':' or '::' rule in the makefile.
- # The commands and dependancies are kept here.
-
- sub target
- {
- return shift->{TARGET};
- }
-
- sub Name
- {
- return shift->target->Name;
- }
-
- sub Base
- {
- my $name = shift->target->Name;
- $name =~ s/\.[^.]+$//;
- return $name;
- }
-
- sub Info
- {
- return shift->target->Info;
- }
-
- sub depend
- {
- my $self = shift;
- if (@_)
- {
- my $dep = shift;
- confess "dependants $dep are not an array reference" unless ('ARRAY' eq ref $dep);
- my $file;
- foreach $file (@$dep)
- {
- unless (exists $self->{DEPHASH}{$file})
- {
- $self->{DEPHASH}{$file} = 1;
- push(@{$self->{DEPEND}},$file);
- }
- }
- }
- return (wantarray) ? @{$self->{DEPEND}} : $self->{DEPEND};
- }
-
- sub command
- {
- my $self = shift;
- if (@_)
- {
- my $cmd = shift;
- confess "commands $cmd are not an array reference" unless ('ARRAY' eq ref $cmd);
- if (@$cmd)
- {
- if (@{$self->{COMMAND}})
- {
- warn "Command for ".$self->Name," redefined";
- print STDERR "Was:",join("\n",@{$self->{COMMAND}}),"\n";
- print STDERR "Now:",join("\n",@$cmd),"\n";
- }
- $self->{COMMAND} = $cmd;
- }
- else
- {
- if (@{$self->{COMMAND}})
- {
- # warn "Command for ".$self->Name," retained";
- # print STDERR "Was:",join("\n",@{$self->{COMMAND}}),"\n";
- }
- }
- }
- return (wantarray) ? @{$self->{COMMAND}} : $self->{COMMAND};
- }
-
- #
- # The key make test - is target out-of-date as far as this rule is concerned
- # In scalar context - boolean value of 'do we need to apply the rule'
- # In list context the things we are out-of-date with e.g. magic $? variable
- #
- sub out_of_date
- {
- my $self = shift;
- my $info = $self->Info;
- my @dep = ();
- my $tdate = $self->target->date;
- my $dep;
- my $count = 0;
- foreach $dep ($self->exp_depend)
- {
- my $date = $info->date($dep);
- $count++;
- if (!defined($date) || !defined($tdate) || $date < $tdate)
- {
- push(@dep,$dep);
- }
- }
- # Note special case of no dependencies means it is always out-of-date!
- return (wantarray) ? @dep : (!$count || @dep);
- }
-
- #
- # Return list of things rule depends on with variables expanded
- # - May need pathname and vpath processing as well
- #
- sub exp_depend
- {
- my $self = shift;
- my $info = $self->Info;
- my @dep = map(split(/\s+/,$info->subsvars($_)),$self->depend);
- return (wantarray) ? @dep : \@dep;
- }
-
- #
- # Return commands to apply rule with variables expanded
- # - No pathname processing needed, commands should always chdir()
- # to logical place (at least till we get very clever at bourne shell parsing).
- # - May need vpath processing
- #
- sub exp_command
- {
- my $self = shift;
- my $info = $self->Info;
- my $base = $self->Name;
- my %var;
- tie %var,'Make::Rule::Vars',$self;
- my @cmd = map($info->subsvars($_,\%var),$self->command);
- return (wantarray) ? @cmd : \@cmd;
- }
-
- #
- # clone creates a new rule derived from an existing rule, but
- # with a different target. Used when left hand side was a variable.
- # perhaps should be used for dot/pattern rule processing too.
- #
- sub clone
- {
- my ($self,$target) = @_;
- my %hash = %$self;
- $hash{TARGET} = $target;
- return bless \%hash,ref $self;
- }
-
- sub new
- {
- my $class = shift;
- my $target = shift;
- my $kind = shift;
- # return shift->clone($target) if (@_ == 1);
- my $self = bless { TARGET => $target, # parent target (left hand side)
- KIND => $kind, # : or ::
- DEPEND => [], DEPHASH => {}, # right hand args
- COMMAND => [] # command(s)
- },$class;
- $self->depend(shift) if (@_);
- $self->command(shift) if (@_);
- return $self;
- }
-
- #
- # This code has to go somewhere but no good home obvious yet.
- # - only applies to ':' rules, but needs top level database
- # - perhaps in ->commands of derived ':' class?
- #
- sub find_commands
- {
- my ($self) = @_;
- if (!@{$self->{COMMAND}} && @{$self->{DEPEND}})
- {
- my $info = $self->Info;
- my $name = $self->Name;
- my @dep = $self->depend;
- my @rule = $info->patrule($self->Name);
- if (@rule)
- {
- $self->depend($rule[0]);
- $self->command($rule[1]);
- }
- }
- }
-
- #
- # Spew a shell script to perfom the 'make' e.g. make -n
- #
- sub Script
- {
- my $self = shift;
- return unless $self->out_of_date;
- my @cmd = $self->exp_command;
- if (@cmd)
- {
- my $file;
- my $com = ($^O eq 'MSWin32') ? 'rem ': ($^O eq 'riscos') ? '| ' : '# ';
- print $com,$self->Name,"\n";
- foreach $file ($self->exp_command)
- {
- $file =~ s/^[\@\s-]*//;
- print "$file\n";
- }
- }
- }
-
- #
- # Normal 'make' method
- #
- sub Make
- {
- my $self = shift;
- my $file;
- return unless $self->out_of_date;
- my @cmd = $self->exp_command;
- my $info = $self->Info;
- if (@cmd)
- {
- # print "# ",$self->Name,"\n";
- foreach my $file ($self->exp_command)
- {
- $file =~ s/^([\@\s-]*)//;
- my $prefix = $1;
- print "$file\n" unless ($prefix =~ /\@/);
- my $code = $info->exec($file);
- if ($code && $prefix !~ /-/)
- {
- die "Code $code from $file";
- }
- }
- }
- }
-
- #
- # Print rule out in makefile syntax
- # - currently has variables expanded as debugging aid.
- # - will eventually become make -p
- # - may be useful for writing makefiles from MakeMaker too...
- #
- sub Print
- {
- my $self = shift;
- my $file;
- print $self->Name,' ',$self->{KIND},' ';
- foreach $file ($self->exp_depend)
- {
- print " \\\n $file";
- }
- print "\n";
- my @cmd = $self->exp_command;
- if (@cmd)
- {
- foreach $file ($self->exp_command)
- {
- print "\t",$file,"\n";
- }
- }
- else
- {
- print STDERR "No commands for ",$self->Name,"\n" unless ($self->target->phony);
- }
- print "\n";
- }
-
- package Make::Target;
- use Carp;
- use strict;
- use Cwd;
-
- #
- # Intermediate 'target' package
- # There is an instance of this for each 'target' that apears on
- # the left hand side of a rule i.e. for each thing that can be made.
- #
- sub new
- {
- my ($class,$info,$target) = @_;
- return bless { NAME => $target, # name of thing
- MAKEFILE => $info, # Makefile context
- Pass => 0 # Used to determine if 'done' this sweep
- },$class;
- }
-
- sub date
- {
- my $self = shift;
- my $info = $self->Info;
- return $info->date($self->Name);
- }
-
- sub phony
- {
- my $self = shift;
- return $self->Info->phony($self->Name);
- }
-
- sub colon
- {
- my $self = shift;
- if (@_)
- {
- if (exists $self->{COLON})
- {
- my $dep = $self->{COLON};
- if (@_ == 1)
- {
- my $other = shift;
- $dep->depend(scalar $other->depend);
- $dep->command(scalar $other->command);
- }
- else
- {
- $dep->depend(shift);
- $dep->command(shift);
- }
- }
- else
- {
- $self->{COLON} = (@_ == 1) ? shift->clone($self) : Make::Rule->new($self,':',@_);
- }
- }
- if (exists $self->{COLON})
- {
- return (wantarray) ? ($self->{COLON}) : $self->{COLON};
- }
- else
- {
- return (wantarray) ? () : undef;
- }
- }
-
- sub dcolon
- {
- my $self = shift;
- if (@_)
- {
- my $rule = (@_ == 1) ? shift->clone($self) : Make::Rule->new($self,'::',@_);
- $self->{DCOLON} = [] unless (exists $self->{DCOLON});
- push(@{$self->{DCOLON}},$rule);
- }
- return (exists $self->{DCOLON}) ? @{$self->{DCOLON}} : ();
- }
-
- sub Name
- {
- return shift->{NAME};
- }
-
- sub Info
- {
- return shift->{MAKEFILE};
- }
-
- sub ProcessColon
- {
- my ($self) = @_;
- my $c = $self->colon;
- $c->find_commands if $c;
- }
-
- sub ExpandTarget
- {
- my ($self) = @_;
- my $target = $self->Name;
- my $info = $self->Info;
- my $colon = delete $self->{COLON};
- my $dcolon = delete $self->{DCOLON};
- foreach my $expand (split(/\s+/,$info->subsvars($target)))
- {
- next unless defined($expand);
- my $t = $info->Target($expand);
- if (defined $colon)
- {
- $t->colon($colon);
- }
- foreach my $d (@{$dcolon})
- {
- $t->dcolon($d);
- }
- }
- }
-
- sub done
- {
- my $self = shift;
- my $info = $self->Info;
- my $pass = $info->pass;
- return 1 if ($self->{Pass} == $pass);
- $self->{Pass} = $pass;
- return 0;
- }
-
- sub recurse
- {
- my ($self,$method,@args) = @_;
- my $info = $self->Info;
- my $rule;
- foreach $rule ($self->colon,$self->dcolon)
- {
- my $dep;
- foreach $dep ($rule->exp_depend)
- {
- my $t = $info->{Depend}{$dep};
- if (defined $t)
- {
- $t->$method(@args)
- }
- else
- {
- my $dir = cwd();
- die "Cannot recurse $method - no target $dep in $dir" unless ($info->exists($dep));
- }
- }
- }
- }
-
- sub Script
- {
- my $self = shift;
- my $info = $self->Info;
- my $rule = $self->colon;
- return if ($self->done);
- $self->recurse('Script');
- foreach $rule ($self->colon,$self->dcolon)
- {
- $rule->Script;
- }
- }
-
- sub Make
- {
- my $self = shift;
- my $info = $self->Info;
- my $rule = $self->colon;
- return if ($self->done);
- $self->recurse('Make');
- foreach $rule ($self->colon,$self->dcolon)
- {
- $rule->Make;
- }
- }
-
- sub Print
- {
- my $self = shift;
- my $info = $self->Info;
- return if ($self->done);
- my $rule = $self->colon;
- foreach $rule ($self->colon,$self->dcolon)
- {
- $rule->Print;
- }
- $self->recurse('Print');
- }
-
- package Make;
- use 5.005; # Need look-behind assertions
- use Carp;
- use strict;
- use Config;
- use Cwd;
- use File::Spec;
- use vars qw($VERSION);
- $VERSION = '0.03';
-
- sub phony
- {
- my ($self,$name) = @_;
- return exists $self->{PHONY}{$name};
- }
-
- sub suffixes
- {
- my ($self) = @_;
- return keys %{$self->{'SUFFIXES'}};
- }
-
- #
- # Construct a new 'target' (or find old one)
- # - used by parser to add to data structures
- #
- sub Target
- {
- my ($self,$target) = @_;
- unless (exists $self->{Depend}{$target})
- {
- my $t = Make::Target->new($self,$target);
- $self->{Depend}{$target} = $t;
- if ($target =~ /%/)
- {
- $self->{Pattern}{$target} = $t;
- }
- elsif ($target =~ /^\./)
- {
- $self->{Dot}{$target} = $t;
- }
- else
- {
- push(@{$self->{Targets}},$t);
- }
- }
- return $self->{Depend}{$target};
- }
-
- #
- # Utility routine for patching %.o type 'patterns'
- #
- sub patmatch
- {
- my $key = shift;
- local $_ = shift;
- my $pat = $key;
- $pat =~ s/\./\\./;
- $pat =~ s/%/(\[^\/\]*)/;
- if (/$pat$/)
- {
- return $1;
- }
- return undef;
- }
-
- #
- # old vpath lookup routine
- #
- sub locate
- {
- my $self = shift;
- local $_ = shift;
- return $_ if (-r $_);
- my $key;
- foreach $key (keys %{$self->{vpath}})
- {
- my $Pat;
- if (defined($Pat = patmatch($key,$_)))
- {
- my $dir;
- foreach $dir (split(/:/,$self->{vpath}{$key}))
- {
- return "$dir/$_" if (-r "$dir/$_");
- }
- }
- }
- return undef;
- }
-
- #
- # Convert traditional .c.o rules into GNU-like into %o : %c
- #
- sub dotrules
- {
- my ($self) = @_;
- my $t;
- foreach $t (keys %{$self->{Dot}})
- {
- my $e = $self->subsvars($t);
- $self->{Dot}{$e} = delete $self->{Dot}{$t} unless ($t eq $e);
- }
- my (@suffix) = $self->suffixes;
- foreach $t (@suffix)
- {
- my $d;
- my $r = delete $self->{Dot}{$t};
- if (defined $r)
- {
- my @rule = ($r->colon) ? $r->colon->depend : ();
- if (@rule)
- {
- delete $self->{Dot}{$t->Name};
- print STDERR $t->Name," has dependants\n";
- push(@{$self->{Targets}},$r);
- }
- else
- {
- # print STDERR "Build \% : \%$t\n";
- $self->Target('%')->dcolon(['%'.$t],scalar $r->colon->command);
- }
- }
- foreach $d (@suffix)
- {
- $r = delete $self->{Dot}{$t.$d};
- if (defined $r)
- {
- # print STDERR "Build \%$d : \%$t\n";
- $self->Target('%'.$d)->dcolon(['%'.$t],scalar $r->colon->command);
- }
- }
- }
- foreach $t (keys %{$self->{Dot}})
- {
- push(@{$self->{Targets}},delete $self->{Dot}{$t});
- }
- }
-
- #
- # Return 'full' pathname of name given directory info.
- # - may be the place to do vpath stuff ?
- #
- sub pathname
- {
- my ($self,$name) = @_;
- return $name if File::Spec->file_name_is_absolute($name);
- $name =~ s,^\./,,;
- return File::Spec->catfile($self->{Dir},$name);
- }
-
- #
- # Return modified date of name if it exists
- #
- sub date
- {
- my ($self,$name) = @_;
- my $path = $self->pathname($name);
- my $r = -M $path;
- # print STDERR "$path date is $r\n" if (defined $r);
- return $r;
- }
-
- #
- # Check to see if name is a target we can make or an existing
- # file - used to see if pattern rules are valid
- # - Needs extending to do vpath lookups
- #
- sub exists
- {
- my ($self,$name) = @_;
- return 1 if (exists $self->{Depend}{$name});
- my $path = $self->pathname($name);
- return 1 if (-e $path);
- # print STDERR "$name '$path' does not exist\n";
- return 0;
- }
-
- #
- # See if we can find a %.o : %.c rule for target
- # .c.o rules are already converted to this form
- #
- sub patrule
- {
- my ($self,$target) = @_;
- my $key;
- # print STDERR "Trying pattern for $target\n";
- foreach $key (keys %{$self->{Pattern}})
- {
- my $Pat;
- if (defined($Pat = patmatch($key,$target)))
- {
- my $t = $self->{Pattern}{$key};
- my $rule;
- foreach $rule ($t->dcolon)
- {
- my @dep = $rule->exp_depend;
- if (@dep)
- {
- my $dep = $dep[0];
- $dep =~ s/%/$Pat/g;
- # print STDERR "Try $target : $dep\n";
- if ($self->exists($dep))
- {
- foreach (@dep)
- {
- s/%/$Pat/g;
- }
- return (\@dep,scalar $rule->command);
- }
- }
- }
- }
- }
- return ();
- }
-
- #
- # Old code to handle vpath stuff - not used yet
- #
- sub needs
- {my ($self,$target) = @_;
- unless ($self->{Done}{$target})
- {
- if (exists $self->{Depend}{$target})
- {
- my @depend = split(/\s+/,$self->subsvars($self->{Depend}{$target}));
- foreach (@depend)
- {
- $self->needs($_);
- }
- }
- else
- {
- my $vtarget = $self->locate($target);
- if (defined $vtarget)
- {
- $self->{Need}{$vtarget} = $target;
- }
- else
- {
- $self->{Need}{$target} = $target;
- }
- }
- }
- }
-
- #
- # Substitute $(xxxx) and $x style variable references
- # - should handle ${xxx} as well
- # - recurses still they all go rather than doing one level,
- # which may need fixing
- #
- sub subsvars
- {
- my $self = shift;
- local $_ = shift;
- my @var = @_;
- push(@var,$self->{Override},$self->{Vars},\%ENV);
- croak("Trying to subsitute undef value") unless (defined $_);
- while (/(?<!\$)\$\(([^()]+)\)/ || /(?<!\$)\$([<\@^?*])/)
- {
- my ($key,$head,$tail) = ($1,$`,$');
- my $value;
- if ($key =~ /^([\w._]+|\S)(?::(.*))?$/)
- {
- my ($var,$op) = ($1,$2);
- foreach my $hash (@var)
- {
- $value = $hash->{$var};
- if (defined $value)
- {
- last;
- }
- }
- unless (defined $value)
- {
- die "$var not defined in '$_'" unless (length($var) > 1);
- $value = '';
- }
- if (defined $op)
- {
- if ($op =~ /^s(.).*\1.*\1/)
- {
- local $_ = $self->subsvars($value);
- $op =~ s/\\/\\\\/g;
- eval $op.'g';
- $value = $_;
- }
- else
- {
- die "$var:$op = '$value'\n";
- }
- }
- }
- elsif ($key =~ /wildcard\s*(.*)$/)
- {
- $value = join(' ',glob($self->pathname($1)));
- }
- elsif ($key =~ /shell\s*(.*)$/)
- {
- $value = join(' ',split('\n',`$1`));
- }
- elsif ($key =~ /addprefix\s*([^,]*),(.*)$/)
- {
- $value = join(' ',map($1 . $_,split('\s+',$2)));
- }
- elsif ($key =~ /notdir\s*(.*)$/)
- {
- my @files = split(/\s+/,$1);
- foreach (@files)
- {
- s#^.*/([^/]*)$#$1#;
- }
- $value = join(' ',@files);
- }
- elsif ($key =~ /dir\s*(.*)$/)
- {
- my @files = split(/\s+/,$1);
- foreach (@files)
- {
- s#^(.*)/[^/]*$#$1#;
- }
- $value = join(' ',@files);
- }
- elsif ($key =~ /^subst\s+([^,]*),([^,]*),(.*)$/)
- {
- my ($a,$b) = ($1,$2);
- $value = $3;
- $a =~ s/\./\\./;
- $value =~ s/$a/$b/;
- }
- elsif ($key =~ /^mktmp,(\S+)\s*(.*)$/)
- {
- my ($file,$content) = ($1,$2);
- open(TMP,">$file") || die "Cannot open $file:$!";
- $content =~ s/\\n//g;
- print TMP $content;
- close(TMP);
- $value = $file;
- }
- else
- {
- warn "Cannot evaluate '$key' in '$_'\n";
- }
- $_ = "$head$value$tail";
- }
- s/\$\$/\$/g;
- return $_;
- }
-
- #
- # Split a string into tokens - like split(/\s+/,...) but handling
- # $(keyword ...) with embedded \s
- # Perhaps should also understand "..." and '...' ?
- #
- sub tokenize
- {
- local $_ = $_[0];
- my @result = ();
- s/\s+$//;
- while (length($_))
- {
- s/^\s+//;
- last unless (/^\S/);
- my $token = "";
- while (/^\S/)
- {
- if (s/^\$([\(\{])//)
- {
- $token .= $&;
- my $paren = $1 eq '(';
- my $brace = $1 eq '{';
- my $count = 1;
- while (length($_) && ($paren || $brace))
- {
- s/^.//;
- $token .= $&;
- $paren += ($& eq '(');
- $paren -= ($& eq ')');
- $brace += ($& eq '{');
- $brace -= ($& eq '}');
- }
- die "Mismatched {} in $_[0]" if ($brace);
- die "Mismatched () in $_[0]" if ($paren);
- }
- elsif (s/^(\$\S?|[^\s\$]+)//)
- {
- $token .= $&;
- }
- }
- push(@result,$token);
- }
- return (wantarray) ? @result : \@result;
- }
-
-
- #
- # read makefile (or fragment of one) either as a result
- # of a command line, or an 'include' in another makefile.
- #
- sub makefile
- {
- my ($self,$makefile,$name) = @_;
- local $_;
- print STDERR "Reading $name\n";
- Makefile:
- while (<$makefile>)
- {
- last unless (defined $_);
- chomp($_);
- if (/\\$/)
- {
- chop($_);
- s/\s*$//;
- my $more = <$makefile>;
- $more =~ s/^\s*/ /;
- $_ .= $more;
- redo;
- }
- next if (/^\s*#/);
- next if (/^\s*$/);
- s/#.*$//;
- s/^\s+//;
- if (/^(-?)include\s+(.*)$/)
- {
- my $opt = $1;
- my $file;
- foreach $file (tokenize($self->subsvars($2)))
- {
- local *Makefile;
- my $path = $self->pathname($file);
- if (open(Makefile,"<$path"))
- {
- $self->makefile(\*Makefile,$path);
- close(Makefile);
- }
- else
- {
- warn "Cannot open $path:$!" unless ($opt eq '-') ;
- }
- }
- }
- elsif (/^\s*([\w._]+)\s*:?=\s*(.*)$/)
- {
- $self->{Vars}{$1} = (defined $2) ? $2 : "";
- # print STDERR "$1 = ",$self->{Vars}{$1},"\n";
- }
- elsif (/^vpath\s+(\S+)\s+(.*)$/)
- {my ($pat,$path) = ($1,$2);
- $self->{Vpath}{$pat} = $path;
- }
- elsif (/^\s*([^:]*)(::?)\s*(.*)$/)
- {
- my ($target,$kind,$depend) = ($1,$2,$3);
- my @cmnds;
- if ($depend =~ /^([^;]*);(.*)$/)
- {
- ($depend,$cmnds[0]) = ($1,$2);
- }
- while (<$makefile>)
- {
- next if (/^\s*#/);
- next if (/^\s*$/);
- last unless (/^\t/);
- chop($_);
- if (/\\$/)
- {
- chop($_);
- $_ .= ' ';
- $_ .= <$makefile>;
- redo;
- }
- next if (/^\s*$/);
- s/^\s+//;
- push(@cmnds,$_);
- }
- $depend =~ s/\s\s+/ /;
- $target =~ s/\s\s+/ /;
- my @depend = tokenize($depend);
- foreach (tokenize($target))
- {
- my $t = $self->Target($_);
- my $index = 0;
- if ($kind eq '::' || /%/)
- {
- $t->dcolon(\@depend,\@cmnds);
- }
- else
- {
- $t->colon(\@depend,\@cmnds);
- }
- }
- redo Makefile;
- }
- else
- {
- warn "Ignore '$_'\n";
- }
- }
- }
-
- sub pseudos
- {
- my $self = shift;
- my $key;
- foreach $key (qw(SUFFIXES PHONY PRECIOUS PARALLEL))
- {
- my $t = delete $self->{Dot}{'.'.$key};
- if (defined $t)
- {
- my $dep;
- $self->{$key} = {};
- foreach $dep ($t->colon->exp_depend)
- {
- $self->{$key}{$dep} = 1;
- }
- }
- }
- }
-
-
- sub ExpandTarget
- {
- my $self = shift;
- foreach my $t (@{$self->{'Targets'}})
- {
- $t->ExpandTarget;
- }
- foreach my $t (@{$self->{'Targets'}})
- {
- $t->ProcessColon;
- }
- }
-
- sub parse
- {
- my ($self,$file) = @_;
- if (defined $file)
- {
- $file = $self->pathname($file);
- }
- else
- {
- my @files = qw(makefile Makefile);
- unshift(@files,'GNUmakefile') if ($self->{GNU});
- my $name;
- foreach $name (@files)
- {
- $file = $self->pathname($name);
- if (-r $file)
- {
- $self->{Makefile} = $name;
- last;
- }
- }
- }
- local (*Makefile);
- open(Makefile,"<$file") || croak("Cannot open $file:$!");
- $self->makefile(\*Makefile,$file);
- close(Makefile);
-
- # Next bits should really be done 'lazy' on need.
-
- $self->pseudos; # Pull out .SUFFIXES etc.
- $self->dotrules; # Convert .c.o into %.o : %.c
- }
-
- sub PrintVars
- {
- my $self = shift;
- local $_;
- foreach (keys %{$self->{Vars}})
- {
- print "$_ = ",$self->{Vars}{$_},"\n";
- }
- print "\n";
- }
-
- sub exec
- {
- my $self = shift;
- if ($^O eq 'MSWin32' or $^O eq 'riscos')
- {
- my $cwd = cwd();
- my $ret;
- chdir $self->{Dir};
- if ($^O eq 'riscos') {
- my $com = join ' ', @_;
- $com =~ tr/\t/ /;
- # shortcut known no-ops (OSCLI comments start with a '|')
- $ret = ($com =~ /^\*\|/) ? 0 : system('dumbshell', '-c', $com);
- } else {
- $ret = system(@_);
- }
- chdir $cwd;
- return $ret;
- }
- else
- {
- my $pid = fork;
- if ($pid)
- {
- waitpid $pid,0;
- return $?;
- }
- else
- {
- my $dir = $self->{Dir};
- chdir($dir) || die "Cannot cd to $dir";
- # handle leading VAR=value here ?
- # To handle trivial cases like ': libpTk.a' force using /bin/sh
- exec("/bin/sh","-c",@_) || confess "Cannot exec ".join(' ',@_);
- }
- }
- }
-
- sub NextPass { shift->{Pass}++ }
- sub pass { shift->{Pass} }
-
- sub apply
- {
- my $self = shift;
- my $method = shift;
- $self->NextPass;
- my @targets = ();
- # print STDERR join(' ',Apply => $method,@_),"\n";
- foreach (@_)
- {
- if (/^(\w+)=(.*)$/)
- {
- # print STDERR "OVERRIDE: $1 = $2\n";
- $self->{Override}{$1} = $2;
- }
- else
- {
- push(@targets,$_);
- }
- }
- #
- # This expansion is dubious as it alters the database
- # as a function of current values of Override.
- #
- $self->ExpandTarget; # Process $(VAR) :
- @targets = ($self->{'Targets'}[0])->Name unless (@targets);
- # print STDERR join(' ',Targets => $method,map($_->Name,@targets)),"\n";
- foreach (@targets)
- {
- my $t = $self->{Depend}{$_};
- unless (defined $t)
- {
- print STDERR join(' ',$method,@_),"\n";
- die "Cannot `$method' - no target $_"
- }
- $t->$method();
- }
- }
-
- sub Script
- {
- shift->apply(Script => @_);
- }
-
- sub Print
- {
- shift->apply(Print => @_);
- }
-
- sub Make
- {
- shift->apply(Make => @_);
- }
-
- sub new
- {
- my ($class,%args) = @_;
- unless (defined $args{Dir})
- {
- chomp($args{Dir} = cwd());
- }
- my $self = bless { %args,
- Pattern => {}, # GNU style %.o : %.c
- Dot => {}, # Trad style .c.o
- Vpath => {}, # vpath %.c info
- Vars => {}, # Variables defined in makefile
- Depend => {}, # hash of targets
- Targets => [], # ordered version so we can find 1st one
- Pass => 0, # incremented each sweep
- Need => {},
- Done => {},
- },$class;
- $self->{Vars}{CC} = $Config{cc};
- $self->{Vars}{AR} = $Config{ar};
- $self->{Vars}{CFLAGS} = $Config{optimize};
- $self->makefile(\*DATA,__FILE__);
- $self->parse($self->{Makefile});
- return $self;
- }
-
- =head1 NAME
-
- Make - module for processing makefiles
-
- =head1 SYNOPSIS
-
- require Make;
- my $make = Make->new(...);
- $make->parse($file);
- $make->Script(@ARGV)
- $make->Make(@ARGV)
- $make->Print(@ARGV)
-
- my $targ = $make->Target($name);
- $targ->colon([dependancy...],[command...]);
- $targ->dolon([dependancy...],[command...]);
- my @depends = $targ->colon->depend;
- my @commands = $targ->colon->command;
-
- =head1 DESCRIPTION
-
- Make->new creates an object if C<new(Makefile =E<gt> $file)> is specified
- then it is parsed. If not the usual makefile Makefile sequence is
- used. (If GNU => 1 is passed to new then GNUmakefile is looked for first.)
-
- C<$make-E<gt>Make(target...)> 'makes' the target(s) specified
- (or the first 'real' target in the makefile).
-
- C<$make-E<gt>Print> can be used to 'print' to current C<select>'ed stream
- a form of the makefile with all variables expanded.
-
- C<$make-E<gt>Script(target...)> can be used to 'print' to
- current C<select>'ed stream the equivalent bourne shell script
- that a make would perform i.e. the output of C<make -n>.
-
- There are other methods (used by parse) which can be used to add and
- manipulate targets and their dependants. There is a hierarchy of classes
- which is still evolving. These classes and their methods will be documented when
- they are a little more stable.
-
- The syntax of makefile accepted is reasonably generic, but I have not re-read
- any documentation yet, rather I have implemented my own mental model of how
- make works (then fixed it...).
-
- In addition to traditional
-
- .c.o :
- $(CC) -c ...
-
- GNU make's 'pattern' rules e.g.
-
- %.o : %.c
- $(CC) -c ...
-
- Likewise a subset of GNU makes $(function arg...) syntax is supported.
-
- Via pmake Make has built perl/Tk from the C<MakeMaker> generated Makefiles...
-
- =head1 BUGS
-
- At present C<new> must always find a makefile, and
- C<$make-E<gt>parse($file)> can only be used to augment that file.
-
- More attention needs to be given to using the package to I<write> makefiles.
-
- The rules for matching 'dot rules' e.g. .c.o and/or pattern rules e.g. %.o : %.c
- are suspect. For example give a choice of .xs.o vs .xs.c + .c.o behaviour
- seems a little odd.
-
- Variables are probably substituted in different 'phases' of the process
- than in make(1) (or even GNU make), so 'clever' uses will probably not
- work.
-
- UNIXisms abound.
-
- =head1 SEE ALSO
-
- L<pmake>
-
- =head1 AUTHOR
-
- Nick Ing-Simmons
-
- =cut
-
- 1;
- #
- # Remainder of file is in makefile syntax and constitutes
- # the built in rules
- #
- __DATA__
-
- .SUFFIXES: .o .c .y .h .sh .cps
-
- .c.o :
- $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
-
- .c :
- $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $< $(LDFLAGS) $(LDLIBS)
-
- .y.o:
- $(YACC) $<
- $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ y.tab.c
- $(RM) y.tab.c
-
- .y.c:
- $(YACC) $<
- mv y.tab.c $@
-
-
-