package Make::Rule::Vars; use Carp; use strict; my $generation = 0; # lexical cross-package scope used! # 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 (/^[\@^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 $name = $self->Name; 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 $array = wantarray; 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) { # warn $self->Name." ood wrt ".$dep."\n"; return 1 unless $array; push(@dep,$dep); } } return @dep if $array; # Note special case of no dependencies means it is always out-of-date! return !$count; } # # 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; $hash{DEPEND} = [@{$self->{DEPEND}}]; $hash{DEPHASH} = {%{$self->{DEPHASH}}}; my $obj = bless \%hash,ref $self; return $obj; } sub new { my $class = shift; my $target = shift; my $kind = shift; 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 ': '# '; 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) { 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->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) { # merging an existing rule 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; my $i = 0; foreach $rule ($self->colon,$self->dcolon) { my $dep; my $j = 0; foreach $dep ($rule->exp_depend) { my $t = $info->{Depend}{$dep}; if (defined $t) { $t->$method(@args) } else { unless ($info->exists($dep)) { my $dir = cwd(); die "Cannot recurse $method - no target $dep in $dir" } } } } } 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 = '1.00'; my %date; 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 ? # my %pathname; sub pathname { my ($self,$name) = @_; my $hash = $self->{'Pathname'}; unless (exists $hash->{$name}) { if (File::Spec->file_name_is_absolute($name)) { $hash->{$name} = $name; } else { $name =~ s,^\./,,; $hash->{$name} = File::Spec->catfile($self->{Dir},$name); } } return $hash->{$name}; } # # Return modified date of name if it exists # sub date { my ($self,$name) = @_; my $path = $self->pathname($name); unless (exists $date{$path}) { $date{$path} = -M $path; } return $date{$path}; } # # 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}); return 1 if defined $self->date($name); # 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 till 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 (/(?{$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; undef %date; $generation++; if ($^O eq 'MSWin32') { my $cwd = cwd(); my $ret; chdir $self->{Dir}; $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} = getcwd()); } 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 Pathname => {}, # cache of expanded names 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 $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-EMake(target...)> 'makes' the target(s) specified (or the first 'real' target in the makefile). C<$make-EPrint> can be used to 'print' to current C'ed stream the equivalent bourne shell script that a make would perform i.e. the output of C. 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 generated Makefiles... =head1 BUGS At present C must always find a makefile, and C<$make-Eparse($file)> can only be used to augment that file. More attention needs to be given to using the package to I 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 =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 $@