[Templates-cvs] cvs commit: Template2/bin ttree

cvs@template-toolkit.org cvs@template-toolkit.org
Wed, 08 Oct 2003 18:49:45 +0100


cvs         03/10/08 17:49:45

  Modified:    bin      ttree
  Log:
  * cleanups to dependency code and other minor fixes
  
  Revision  Changes    Path
  2.66      +138 -203  Template2/bin/ttree
  
  Index: ttree
  ===================================================================
  RCS file: /template-toolkit/Template2/bin/ttree,v
  retrieving revision 2.65
  retrieving revision 2.66
  diff -u -r2.65 -r2.66
  --- ttree	2003/10/08 13:53:07	2.65
  +++ ttree	2003/10/08 17:49:44	2.66
  @@ -23,7 +23,7 @@
   #
   #------------------------------------------------------------------------
   #
  -# $Id: ttree,v 2.65 2003/10/08 13:53:07 abw Exp $
  +# $Id: ttree,v 2.66 2003/10/08 17:49:44 abw Exp $
   #
   #========================================================================
   
  @@ -36,15 +36,16 @@
   use File::Basename;
   use Text::ParseWords qw(quotewords);
   
  -
  -#------------------------------------------------------------------------
  -# config
  -#------------------------------------------------------------------------
   my $NAME     = "ttree";
  -my $VERSION  = sprintf("%d.%02d", q$Revision: 2.65 $ =~ /(\d+)\.(\d+)/);
  +my $VERSION  = sprintf("%d.%02d", q$Revision: 2.66 $ =~ /(\d+)\.(\d+)/);
   my $HOME     = $ENV{ HOME } || '';
   my $RCFILE   = $ENV{"\U${NAME}rc"} || "$HOME/.${NAME}rc";
   
  +
  +#------------------------------------------------------------------------
  +# configuration options
  +#------------------------------------------------------------------------
  +
   # offer create a sample config file if it doesn't exist, unless a '-f'
   # has been specified on the command line
   unless (-f $RCFILE or grep(/^-f$/, @ARGV) ) {
  @@ -52,8 +53,8 @@
   	  "(file: $RCFILE)   [y/n]: ");
       my $y = <STDIN>;
       if ($y =~ /^y(es)?/i) {
  -	write_config($RCFILE);
  -	exit(0);
  +        write_config($RCFILE);
  +        exit(0);
       }
   }
   
  @@ -75,9 +76,9 @@
   my $depends  = $config->depend;
   my $depsfile = $config->depend_file;
   my $srcdir   = $config->src
  -	       || die "Source directory not set (-s)\n";
  +    || die "Source directory not set (-s)\n";
   my $destdir  = $config->dest
  -	       || die "Destination directory not set (-d)\n";
  +    || die "Destination directory not set (-d)\n";
   die "Source and destination directories may not be the same:\n  $srcdir\n"
       if $srcdir eq $destdir;
   
  @@ -94,32 +95,58 @@
   # get all template variable definitions
   my $replace = $config->get('define');
   
  -#print "replace hash: ", join(', ', map { "$_ => $replace->{ $_ }"} 
  -#			     keys %$replace), "\n";
  -
   # now create complete parameter hash for creating template processor
   my $ttopts   = {
       %$ucttopts,
       RELATIVE     => $relative,
       ABSOLUTE     => $absolute,
  -#    INCLUDE_PATH => [ @$libdir, '.' ],
       INCLUDE_PATH => [ $srcdir, @$libdir ],
       OUTPUT_PATH  => $destdir,
   };
   
  +
  +#------------------------------------------------------------------------
  +# inter-file dependencies
  +#------------------------------------------------------------------------
  +
   if ($depsfile or $depends) {
  -	$depends = read_depends($depsfile, $depends);
  -    print "depends: ", join(', ', map { "$_ => [ @{$depends->{ $_ }} ]"} 
  -                                 keys %$depends), "\n";
  +	$depends = dependencies($depsfile, $depends);
   } 
   else {
  -	$depends = undef;
  +	$depends = { };
  +}
  +
  +my $global_deps = $depends->{'*'} || [ ];
  +
  +# add any PRE_PROCESS, etc., templates as global dependencies
  +foreach my $ttopt (qw( PRE_PROCESS POST_PROCESS PROCESS WRAPPER )) {
  +    my $deps = $ucttopts->{ $ttopt } || next;
  +    my @deps = ref $deps eq 'ARRAY' ? (@$deps) : ($deps);
  +    next unless @deps;
  +    push(@$global_deps, @deps);
   }
   
  +# remove any duplicates
  +$global_deps = { map { ($_ => 1) } @$global_deps };
  +$global_deps = [ keys %$global_deps ];
   
  +# update $depends hash or delete it if there are no dependencies
  +if (@$global_deps) {
  +    $depends->{'*'} = $global_deps;
  +}
  +else {
  +    delete $depends->{'*'};
  +    $global_deps = undef;
  +}
  +$depends = undef
  +    unless keys %$depends;
  +
  +
  +
   #------------------------------------------------------------------------
   # pre-amble
   #------------------------------------------------------------------------
  +
   print "$NAME $VERSION (Template Toolkit version $Template::VERSION)\n\n"
       if $verbose;
   
  @@ -139,28 +166,10 @@
       	if $dryrun;
   }
   
  -#if ($debug) {
  -#    local $" = ', ';
  -#    print STDERR "Template Toolkit configuration:\n";
  -#    foreach (keys %$ucttopts) {
  -#        my $val = $ucttopts->{$_};
  -#        next unless $val;
  -#        if (ref($val) eq 'ARRAY') {
  -#            next unless @$val;
  -#            $val = "[ @$val ]";
  -#        }
  -#        printf STDERR "  %-12s => $val\n", $_;
  -#    }
  -#    print STDERR "\n";
  -#}
  -
  -
   #------------------------------------------------------------------------
  -# main-amble
  +# main processing loop
   #------------------------------------------------------------------------
   
  -#chdir($srcdir) || die "$srcdir: $!\n";
  -
   my $template = Template->new($ttopts);
   
   if (@ARGV) {
  @@ -174,7 +183,14 @@
       process_tree();
   }
   
  +exit(0);
   
  +
  +#========================================================================
  +# END 
  +#========================================================================
  +
  +
   #------------------------------------------------------------------------
   # process_tree($dir)
   #
  @@ -283,10 +299,9 @@
           $desttime = ( stat($dest) )[9];
   
   		if (defined $depends) {
  -			my $depfiles = get_dependant_files($file, $depends);
  -			my $time  = get_newest_file($depfiles);
  -			if (defined $time && ($srctime < $time)) {
  -				$srctime = $time;
  +			my $deptime  = depend_time($file, $depends);
  +			if (defined $deptime && ($srctime < $deptime)) {
  +				$srctime = $deptime;
   			}
   		}
   	
  @@ -344,35 +359,36 @@
   
   
   #------------------------------------------------------------------------
  -# read_depends($file)
  +# dependencies($file, $depends)
   # 
  -# Reads the dependency file and returns a hash of arrays.
  +# Read the dependencies from $file, if defined, and merge in with 
  +# those passed in as the hash array $depends, if defined.
   #------------------------------------------------------------------------
   
  -sub read_depends {
  +sub dependencies {
   	my ($file, $depend) = @_;
   	my %depends = ();
   
   	if (defined $file) {
  -		my ($fh, $line);
  -
  +		my ($fh, $text, $line);
   		open $fh, $file or die "Can't open $file, $!";
  +        local $/ = undef;
  +        $text = <$fh>;
  +        close($fh);
  +        $text =~ s[\\\n][]mg;
   		
  -		while (defined( $line = getline($fh) )) {
  +        foreach $line (split("\n", $text)) {
   			next if $line =~ /^\s*#/;
  +            chomp $line;
   			my ($file, @files) = quotewords('\s*:\s*', 0, $line);
   			$file =~ s/^\s+//;
   			@files = grep(defined, quotewords('\s+', 0, @files));
   			$depends{$file} = \@files;
   		}
  -
  -		close $fh;
   	}
   
   	if (defined $depend) {
  -		my ($key);
  -
  -		foreach $key (keys %$depend) {
  +		foreach my $key (keys %$depend) {
   			$depends{$key} = [ quotewords(',', 0, $depend->{$key}) ];
   		}
   	}
  @@ -381,120 +397,57 @@
   }
   
   
  +
   #------------------------------------------------------------------------
  -# get_newest_file(\@files)
  +# depend_time($file, \%depends)
   #
  -# Returns the mtime of the ``newest'' file in @files.
  +# Returns the mtime of the most recent in @files.
   #------------------------------------------------------------------------
   
  -sub get_newest_file {
  -    my $files = shift;
  -    my @absfiles;
  +sub depend_time {
  +	my ($file, $depends) = @_;
  +    my ($deps, $absfile, $modtime);
  +    my $maxtime = 0;
  +    my @pending = ($file);
  +    my @files;
  +    my %seen;
  +
  +    # push any global dependencies onto the pending list
  +    if ($deps = $depends->{'*'}) {
  +        push(@pending, @$deps);
  +    }
   
  -    FILE: foreach my $file (@$files) {
  -        if (File::Spec->file_name_is_absolute($file)) {
  -            push(@absfiles, $file);
  -            next FILE;
  +    # iterate through the list of pending files
  +    while (@pending) {
  +        $file = shift @pending;
  +        next if $seen{ $file }++;
  +
  +        if (File::Spec->file_name_is_absolute($file) && -f $file) {
  +            $modtime = (stat($file))[9];
           }
  -        foreach my $dir ($srcdir, @$libdir) {
  -            my $absfile = File::Spec->catfile($dir, $file);
  -            if (-f $absfile) {
  -                push(@absfiles, $absfile);
  -                next FILE;
  +        else {
  +            $modtime = 0;
  +            foreach my $dir ($srcdir, @$libdir) {
  +                $absfile = File::Spec->catfile($dir, $file);
  +                if (-f $absfile) {
  +                    $modtime = (stat($absfile))[9];
  +                    last;
  +                }
               }
           }
  -    }
  -	my @mtimes = map { (stat($_))[9] } @absfiles;
  +        $maxtime = $modtime
  +            if $modtime > $maxtime;
   
  -	# Get the index of the file with the largest mtime.
  -	my $i = do {
  -		my ($max, $m, $i, $index);
  -		$max = $i = 0;
  -
  -		foreach $m (@mtimes) {
  -			if ($max < $m) {
  -				$max = $m;
  -				$index = $i;
  -			}
  -			$i++;
  -		}
   
  -		$index;
  -	};
  -
  -	if (defined $i) {
  -		return $mtimes[$i];
  -	} else {
  -		return undef;
  -	}
  -}
  -
  -
  -
  -#------------------------------------------------------------------------
  -# get_dependant_files($file, $depends)
  -#
  -# Gathers and returns a list of files that $file depends on from the
  -# hash of arrays, $depends.
  -#------------------------------------------------------------------------
  -
  -sub get_dependant_files {
  -	my ($file, $depends) = @_;
  -	my @files = ();
  -	$depends = { %{$depends} }; # Copy $depends, because be modify it.
  -
  -	if (exists $depends->{$file}) {
  -		my ($i) = 0;
  -		# [dylan] No, this doesn't delete a real file, hehe!
  -		@files = (@{ delete $depends->{$file} });
  -
  -		while (exists $files[$i]) {
  -			my $file = $files[$i];
  -			if (not defined $file) {
  -				die "dependant file $i is undefined!!\n";
  -			}
  -			if (exists $depends->{$file}) {
  -				push(@files, @{ delete $depends->{$file} });
  -			}
  -		} 
  -        continue {
  -			$i++;
  -		}
  -	}
  -	
  -	return \@files;
  -}
  -
  -
  -#------------------------------------------------------------------------
  -# getline($fh)
  -# 
  -# This is a wrapper around readline($fh) that returns a single line, but
  -# allows that 'line' to span several newsline if the continuation
  -# charecter \ is used.
  -#------------------------------------------------------------------------
  -
  -sub getline {
  -	my ($fh) = @_;
  -	my ($buf, @buffer);
  -
  -	while (defined ( $buf = readline($fh) ) ) {
  -		chomp $buf;
  -		if (substr($buf, -1, 1) ne '\\') {
  -			push(@buffer, $buf);
  -			last;
  -		} else {
  -			chop $buf;
  -			push(@buffer, $buf);
  -		}
  -	}
  +        if ($deps = $depends->{ $file }) {
  +            push(@pending, @$deps);
  +        }
  +    }
   
  -	
  -	return @buffer ? join('', @buffer, "\n") : undef;
  +    return $maxtime;
   }
   
   
  -
   #------------------------------------------------------------------------
   # read_config($file)
   #
  @@ -594,72 +547,54 @@
   #     perldoc ttree
   #     ttree -h
   #
  -# NOTE: The directories specified below adopt the UNIX convention of
  -# specifying a user's home directory with the '~' character.  This
  -# feature may not be available on other platforms in which case you
  -# should specify the directory in entirety.
   #------------------------------------------------------------------------
   
  -#------------------------------------------------------------------------
  -# General options
  +# directory containing other ttree configuration files
  +# this option is only valid in the main .ttreerc 
  +#
  +#     cfg = /path/to/ttree/config/directory
   
  -# print summary of what's going on (-v)
  -verbose 
  +# directory containing source page templates
  +src = /path/to/your/source/page/templates
   
  -# recurse into any sub-directories and process files (-r)
  -recurse
  +# directory where output files should be written
  +dest = /path/to/your/html/output/directory
   
  +# additional directories of library templates
  +lib = /first/path/to/your/library/templates
  +lib = /second/path/to/your/library/templates
   
  -#------------------------------------------------------------------------
  -# The 'cfg' option defines a directory in which other ttree configuration
  -# files can be found;  you can specify a file using the '-f' option,
  -# 'ttree -f myconfig' and the script will look for the file in this
  -# directory.  Alteratively, provide an absolute path as an argument,
  -# 'ttree -f /tmp/foo'.
  -# 
  -# By default, this option is commented out.  You will need to create a 
  -# directory, uncomment the following line and set the value appropriately.
  -# Having done that, you can then create files exactly like this in that
  -# location.
  -
  -#cfg = ~/.ttree
  -
  -#------------------------------------------------------------------------
  -# The remaining options define the default behaviour when you run ttree.
  -# This file is always processed before any file specified by '-f'.  If
  -# you define the 'src' and 'dest' options then these will be used by
  -# default.  Values for these options defined in files loaded with '-f'
  -# will override these default.  Other options such as 'lib', 'ignore',
  -# 'copy' and 'accept' are accumulative.
  -
  -# The 'src' option defines the location of the template files that
  -# you want to process
  -src  = ~/websrc/public_html
  -
  -# The 'dest' option specifies where the output should go.  The script
  -# compares the modification dates of files in the 'src' and 'dest'
  -# directories to work out which need to be processed. 
  -dest = ~/public_html
  -
  -# 'lib' tells the processor (via INCLUDE_PATH) where to find any
  -# template files that may be INCLUDE'd.  You can specify many.
  -lib = ~/websrc/templates
  -lib = /usr/local/templates/lib
  +# print summary of what's going on 
  +verbose 
  +
  +# recurse into any sub-directories and process files
  +recurse
   
  -# Things that aren't templates and should be ignored, specified as Perl
  -# regexen.
  +# regexen of things that aren't templates and should be ignored
   ignore = \\b(CVS|RCS)\\b
   ignore = ^#
   
  -# Things that should be copied rather than processed.
  +# ditto for things that should be copied rather than processed.
   copy = \\.png\$ 
   copy = \\.gif\$ 
   
  -# By default, everything not ignored or copied is accepted; add 'accept'
  +# by default, everything not ignored or copied is accepted; add 'accept'
   # lines if you want to filter further. e.g.
  -# accept = \\.html\$
  -# accept = \\.atml\$
  +#
  +#    accept = \\.html\$
  +#    accept = \\.tt2\$
  +
  +# options to rewrite files suffixes (htm => html, tt2 => html)
  +#
  +#    suffix htm=html
  +#    suffix tt2=html
   
  +# options to define dependencies between templates
  +#
  +#    depend *=header,footer,menu
  +#    depend index.html=mainpage,sidebar
  +#    depend menu=menuitem,menubar
  +# 
   END_OF_CONFIG
   
       close(CONFIG);