1#!/usr/bin/perl -w 2#======================================================================== 3# 4# ttree 5# 6# DESCRIPTION 7# Script for processing all directory trees containing templates. 8# Template files are processed and the output directed to the 9# relvant file in an output tree. The timestamps of the source and 10# destination files can then be examined for future invocations 11# to process only those files that have changed. In other words, 12# it's a lot like 'make' for templates. 13# 14# AUTHOR 15# Andy Wardley <abw@wardley.org> 16# 17# COPYRIGHT 18# Copyright (C) 1996-2012 Andy Wardley. All Rights Reserved. 19# Copyright (C) 1998-2003 Canon Research Centre Europe Ltd. 20# 21# This module is free software; you can redistribute it and/or 22# modify it under the same terms as Perl itself. 23# 24#======================================================================== 25 26use strict; 27use Template; 28use AppConfig qw( :expand ); 29use File::Copy; 30use File::Path; 31use File::Spec; 32use File::Basename; 33use Text::ParseWords qw(quotewords); 34 35my $NAME = "ttree"; 36my $VERSION = 2.90; 37my $HOME = $ENV{ HOME } || ''; 38my $RCFILE = $ENV{"\U${NAME}rc"} || "$HOME/.${NAME}rc"; 39my $TTMODULE = 'Template'; 40 41#------------------------------------------------------------------------ 42# configuration options 43#------------------------------------------------------------------------ 44 45# offer create a sample config file if it doesn't exist, unless a '-f' 46# has been specified on the command line 47unless (-f $RCFILE or grep(/^(-f|-h|--help)$/, @ARGV) ) { 48 print("Do you want me to create a sample '.ttreerc' file for you?\n", 49 "(file: $RCFILE) [y/n]: "); 50 my $y = <STDIN>; 51 if ($y =~ /^y(es)?/i) { 52 write_config($RCFILE); 53 exit(0); 54 } 55} 56 57# read configuration file and command line arguments - I need to remember 58# to fix varlist() and varhash() in AppConfig to make this nicer... 59my $config = read_config($RCFILE); 60my $dryrun = $config->nothing; 61my $verbose = $config->verbose || $dryrun; 62my $colour = $config->colour; 63my $summary = $config->summary; 64my $recurse = $config->recurse; 65my $preserve = $config->preserve; 66my $all = $config->all; 67my $libdir = $config->lib; 68my $ignore = $config->ignore; 69my $copy = $config->copy; 70my $link = $config->link; 71my $accept = $config->accept; 72my $absolute = $config->absolute; 73my $relative = $config->relative; 74my $suffix = $config->suffix; 75my $binmode = $config->binmode; 76my $depends = $config->depend; 77my $depsfile = $config->depend_file; 78my ($n_proc, $n_unmod, $n_skip, $n_copy, $n_link, $n_mkdir) = (0) x 6; 79 80my $srcdir = $config->src 81 || die "Source directory not set (-s)\n"; 82my $destdir = $config->dest 83 || die "Destination directory not set (-d)\n"; 84die "Source and destination directories may not be the same:\n $srcdir\n" 85 if $srcdir eq $destdir; 86 87# unshift any perl5lib directories onto front of INC 88unshift(@INC, @{ $config->perl5lib }); 89 90# get all template_* options from the config and fold keys to UPPER CASE 91my %ttopts = $config->varlist('^template_', 1); 92my $ttmodule = delete($ttopts{ module }); 93my $ucttopts = { 94 map { my $v = $ttopts{ $_ }; defined $v ? (uc $_, $v) : () } 95 keys %ttopts, 96}; 97 98# get all template variable definitions 99my $replace = $config->get('define'); 100 101# now create complete parameter hash for creating template processor 102my $ttopts = { 103 %$ucttopts, 104 RELATIVE => $relative, 105 ABSOLUTE => $absolute, 106 INCLUDE_PATH => [ $srcdir, @$libdir ], 107 OUTPUT_PATH => $destdir, 108}; 109 110# load custom template module 111if ($ttmodule) { 112 my $ttpkg = $ttmodule; 113 $ttpkg =~ s[::][/]g; 114 $ttpkg .= '.pm'; 115 require $ttpkg; 116} 117else { 118 $ttmodule = $TTMODULE; 119} 120 121 122#------------------------------------------------------------------------ 123# inter-file dependencies 124#------------------------------------------------------------------------ 125 126if ($depsfile or $depends) { 127 $depends = dependencies($depsfile, $depends); 128} 129else { 130 $depends = { }; 131} 132 133my $global_deps = $depends->{'*'} || [ ]; 134 135# add any PRE_PROCESS, etc., templates as global dependencies 136foreach my $ttopt (qw( PRE_PROCESS POST_PROCESS PROCESS WRAPPER )) { 137 my $deps = $ucttopts->{ $ttopt } || next; 138 my @deps = ref $deps eq 'ARRAY' ? (@$deps) : ($deps); 139 next unless @deps; 140 push(@$global_deps, @deps); 141} 142 143# remove any duplicates 144$global_deps = { map { ($_ => 1) } @$global_deps }; 145$global_deps = [ keys %$global_deps ]; 146 147# update $depends hash or delete it if there are no dependencies 148if (@$global_deps) { 149 $depends->{'*'} = $global_deps; 150} 151else { 152 delete $depends->{'*'}; 153 $global_deps = undef; 154} 155$depends = undef 156 unless keys %$depends; 157 158my $DEP_DEBUG = $config->depend_debug(); 159 160 161#------------------------------------------------------------------------ 162# pre-amble 163#------------------------------------------------------------------------ 164 165if ($colour) { 166 no strict 'refs'; 167 *red = \&_red; 168 *green = \&_green; 169 *yellow = \&_yellow; 170 *blue = \&_blue; 171} 172else { 173 no strict 'refs'; 174 *red = \&_white; 175 *green = \&_white; 176 *yellow = \&_white; 177 *blue = \&_white; 178} 179 180if ($verbose) { 181 local $" = ', '; 182 183 184 print "$NAME $VERSION (Template Toolkit version $Template::VERSION)\n\n"; 185 186 my $sfx = join(', ', map { "$_ => $suffix->{$_}" } keys %$suffix); 187 188 print(" Source: $srcdir\n", 189 " Destination: $destdir\n", 190 "Include Path: [ @$libdir ]\n", 191 " Ignore: [ @$ignore ]\n", 192 " Copy: [ @$copy ]\n", 193 " Link: [ @$link ]\n", 194 " Accept: [ @$accept ]\n", 195 " Suffix: [ $sfx ]\n"); 196 print(" Module: $ttmodule ", $ttmodule->module_version(), "\n") 197 unless $ttmodule eq $TTMODULE; 198 199 if ($depends && $DEP_DEBUG) { 200 print "Dependencies:\n"; 201 foreach my $key ('*', grep { !/\*/ } keys %$depends) { 202 printf( " %-16s %s\n", $key, 203 join(', ', @{ $depends->{ $key } }) ) 204 if defined $depends->{ $key }; 205 206 } 207 } 208 print "\n" if $verbose > 1; 209 print red("NOTE: dry run, doing nothing...\n") 210 if $dryrun; 211} 212 213#------------------------------------------------------------------------ 214# main processing loop 215#------------------------------------------------------------------------ 216 217my $template = $ttmodule->new($ttopts) 218 || die $ttmodule->error(); 219 220if (@ARGV) { 221 # explicitly process files specified on command lines 222 foreach my $file (@ARGV) { 223 my $path = $srcdir ? File::Spec->catfile($srcdir, $file) : $file; 224 if ( -d $path ) { 225 process_tree($file); 226 } 227 else { 228 process_file($file, $path, force => 1); 229 } 230 } 231} 232else { 233 # implicitly process all file in source directory 234 process_tree(); 235} 236 237if ($summary || $verbose) { 238 my $format = "%13d %s %s\n"; 239 print "\n" if $verbose > 1; 240 print( 241 " Summary: ", 242 $dryrun ? red("This was a dry run. Nothing was actually done\n") : "\n", 243 green(sprintf($format, $n_proc, $n_proc == 1 ? 'file' : 'files', 'processed')), 244 green(sprintf($format, $n_copy, $n_copy == 1 ? 'file' : 'files', 'copied')), 245 green(sprintf($format, $n_link, $n_link == 1 ? 'file' : 'files', 'linked')), 246 green(sprintf($format, $n_mkdir, $n_mkdir == 1 ? 'directory' : 'directories', 'created')), 247 yellow(sprintf($format, $n_unmod, $n_unmod == 1 ? 'file' : 'files', 'skipped (not modified)')), 248 yellow(sprintf($format, $n_skip, $n_skip == 1 ? 'file' : 'files', 'skipped (ignored)')) 249 ); 250} 251 252exit(0); 253 254 255#======================================================================== 256# END 257#======================================================================== 258 259 260#------------------------------------------------------------------------ 261# process_tree($dir) 262# 263# Walks the directory tree starting at $dir or the current directory 264# if unspecified, processing files as found. 265#------------------------------------------------------------------------ 266 267sub process_tree { 268 my $dir = shift; 269 my ($file, $path, $abspath, $check); 270 my $target; 271 local *DIR; 272 273 my $absdir = join('/', $srcdir ? $srcdir : (), defined $dir ? $dir : ()); 274 $absdir ||= '.'; 275 276 opendir(DIR, $absdir) || do { warn "$absdir: $!\n"; return undef; }; 277 278 FILE: while (defined ($file = readdir(DIR))) { 279 next if $file eq '.' || $file eq '..'; 280 $path = defined $dir ? "$dir/$file" : $file; 281 $abspath = "$absdir/$file"; 282 283 next unless -e $abspath; 284 285 # check against ignore list 286 foreach $check (@$ignore) { 287 if ($path =~ /$check/) { 288 printf yellow(" - %-32s (ignored, matches /$check/)\n"), $path 289 if $verbose > 1; 290 $n_skip++; 291 next FILE; 292 } 293 } 294 295 # check against acceptance list 296 if (@$accept) { 297 unless ((-d $abspath && $recurse) || grep { $path =~ /$_/ } @$accept) { 298 printf yellow(" - %-32s (not accepted)\n"), $path 299 if $verbose > 1; 300 $n_skip++; 301 next FILE; 302 } 303 } 304 305 if (-d $abspath) { 306 if ($recurse) { 307 my ($uid, $gid, $mode); 308 309 (undef, undef, $mode, undef, $uid, $gid, undef, undef, 310 undef, undef, undef, undef, undef) = stat($abspath); 311 312 # create target directory if required 313 $target = "$destdir/$path"; 314 unless (-d $target || $dryrun) { 315 mkpath($target, $verbose, $mode) or 316 die red("Could not mkpath ($target): $!\n"); 317 318 # commented out by abw on 2000/12/04 - seems to raise a warning? 319 # chown($uid, $gid, $target) || warn "chown($target): $!\n"; 320 321 $n_mkdir++; 322 printf green(" + %-32s (created target directory)\n"), $path 323 if $verbose; 324 } 325 # recurse into directory 326 process_tree($path); 327 } 328 else { 329 $n_skip++; 330 printf yellow(" - %-32s (directory, not recursing)\n"), $path 331 if $verbose > 1; 332 } 333 } 334 else { 335 process_file($path, $abspath); 336 } 337 } 338 closedir(DIR); 339} 340 341 342#------------------------------------------------------------------------ 343# process_file() 344# 345# File filtering and processing sub-routine called by process_tree() 346#------------------------------------------------------------------------ 347 348sub process_file { 349 my ($file, $absfile, %options) = @_; 350 my ($dest, $destfile, $filename, $check, 351 $srctime, $desttime, $mode, $uid, $gid); 352 my ($old_suffix, $new_suffix); 353 my $is_dep = 0; 354 my $copy_file = 0; 355 my $link_file = 0; 356 357 $absfile ||= $file; 358 $filename = basename($file); 359 $destfile = $file; 360 361 # look for any relevant suffix mapping 362 if (%$suffix) { 363 if ($filename =~ m/\.(.+)$/) { 364 $old_suffix = $1; 365 if ($new_suffix = $suffix->{ $old_suffix }) { 366 $destfile =~ s/$old_suffix$/$new_suffix/; 367 } 368 } 369 } 370 $dest = $destdir ? "$destdir/$destfile" : $destfile; 371 372# print "proc $file => $dest\n"; 373 374 # check against link list 375 foreach my $link_pattern (@$link) { 376 if ($filename =~ /$link_pattern/) { 377 $link_file = $copy_file = 1; 378 $check = $link_pattern; 379 last; 380 } 381 } 382 383 unless ($copy_file) { 384 # check against copy list 385 foreach my $copy_pattern (@$copy) { 386 if ($filename =~ /$copy_pattern/) { 387 $copy_file = 1; 388 $check = $copy_pattern; 389 last; 390 } 391 } 392 } 393 394 # stat the source file unconditionally, so we can preserve 395 # mode and ownership 396 ( undef, undef, $mode, undef, $uid, $gid, undef, 397 undef, undef, $srctime, undef, undef, undef ) = stat($absfile); 398 399 # test modification time of existing destination file 400 if (! $all && ! $options{ force } && -f $dest) { 401 $desttime = ( stat($dest) )[9]; 402 403 if (defined $depends and not $copy_file) { 404 my $deptime = depend_time($file, $depends); 405 if (defined $deptime && ($srctime < $deptime)) { 406 $srctime = $deptime; 407 $is_dep = 1; 408 } 409 } 410 411 if ($desttime >= $srctime) { 412 printf yellow(" - %-32s (not modified)\n"), $file 413 if $verbose > 1; 414 $n_unmod++; 415 return; 416 } 417 } 418 419 # check against link list 420 if ($link_file) { 421 unless ($dryrun) { 422 if (link($absfile, $dest) == 1) { 423 $copy_file = 0; 424 } 425 else { 426 warn red("Could not link ($absfile to $dest) : $!\n"); 427 } 428 } 429 430 unless ($copy_file) { 431 $n_link++; 432 printf green(" > %-32s (linked, matches /$check/)\n"), $file 433 if $verbose; 434 return; 435 } 436 } 437 438 # check against copy list 439 if ($copy_file) { 440 $n_copy++; 441 unless ($dryrun) { 442 copy($absfile, $dest) or die red("Could not copy ($absfile to $dest) : $!\n"); 443 444 if ($preserve) { 445 chown($uid, $gid, $dest) || warn red("chown($dest): $!\n"); 446 chmod($mode, $dest) || warn red("chmod($dest): $!\n"); 447 } 448 } 449 450 printf green(" > %-32s (copied, matches /$check/)\n"), $file 451 if $verbose; 452 453 return; 454 } 455 456 $n_proc++; 457 458 if ($verbose) { 459 printf(green(" + %-32s"), $file); 460 print(green(" (changed suffix to $new_suffix)")) if $new_suffix; 461 print "\n"; 462 } 463 464 # process file 465 unless ($dryrun) { 466 $template->process($file, $replace, $destfile, 467 $binmode ? {binmode => $binmode} : {}) 468 || print(red(" ! "), $template->error(), "\n"); 469 470 if ($preserve) { 471 chown($uid, $gid, $dest) || warn red("chown($dest): $!\n"); 472 chmod($mode, $dest) || warn red("chmod($dest): $!\n"); 473 } 474 } 475} 476 477 478#------------------------------------------------------------------------ 479# dependencies($file, $depends) 480# 481# Read the dependencies from $file, if defined, and merge in with 482# those passed in as the hash array $depends, if defined. 483#------------------------------------------------------------------------ 484 485sub dependencies { 486 my ($file, $depend) = @_; 487 my %depends = (); 488 489 if (defined $file) { 490 my ($fh, $text, $line); 491 open $fh, $file or die "Can't open $file, $!"; 492 local $/ = undef; 493 $text = <$fh>; 494 close($fh); 495 $text =~ s[\\\n][]mg; 496 497 foreach $line (split("\n", $text)) { 498 next if $line =~ /^\s*(#|$)/; 499 chomp $line; 500 my ($file, @files) = quotewords('\s*:\s*', 0, $line); 501 $file =~ s/^\s+//; 502 @files = grep(defined, quotewords('(,|\s)\s*', 0, @files)); 503 $depends{$file} = \@files; 504 } 505 } 506 507 if (defined $depend) { 508 foreach my $key (keys %$depend) { 509 $depends{$key} = [ quotewords(',', 0, $depend->{$key}) ]; 510 } 511 } 512 513 return \%depends; 514} 515 516 517 518#------------------------------------------------------------------------ 519# depend_time($file, \%depends) 520# 521# Returns the mtime of the most recent in @files. 522#------------------------------------------------------------------------ 523 524sub depend_time { 525 my ($file, $depends) = @_; 526 my ($deps, $absfile, $modtime); 527 my $maxtime = 0; 528 my @pending = ($file); 529 my @files; 530 my %seen; 531 532 # push any global dependencies onto the pending list 533 if ($deps = $depends->{'*'}) { 534 push(@pending, @$deps); 535 } 536 537 print " # checking dependencies for $file...\n" 538 if $DEP_DEBUG; 539 540 # iterate through the list of pending files 541 while (@pending) { 542 $file = shift @pending; 543 next if $seen{ $file }++; 544 545 if (File::Spec->file_name_is_absolute($file) && -f $file) { 546 $modtime = (stat($file))[9]; 547 print " # $file [$modtime]\n" 548 if $DEP_DEBUG; 549 } 550 else { 551 $modtime = 0; 552 foreach my $dir ($srcdir, @$libdir) { 553 $absfile = File::Spec->catfile($dir, $file); 554 if (-f $absfile) { 555 $modtime = (stat($absfile))[9]; 556 print " # $absfile [$modtime]\n" 557 if $DEP_DEBUG; 558 last; 559 } 560 } 561 } 562 $maxtime = $modtime 563 if $modtime > $maxtime; 564 565 if ($deps = $depends->{ $file }) { 566 push(@pending, @$deps); 567 print " # depends on ", join(', ', @$deps), "\n" 568 if $DEP_DEBUG; 569 } 570 } 571 572 return $maxtime; 573} 574 575 576#------------------------------------------------------------------------ 577# read_config($file) 578# 579# Handles reading of config file and/or command line arguments. 580#------------------------------------------------------------------------ 581 582sub read_config { 583 my $file = shift; 584 my $verbose = 0; 585 my $verbinc = sub { 586 my ($state, $var, $value) = @_; 587 $state->{ VARIABLE }->{ verbose } = $value ? ++$verbose : --$verbose; 588 }; 589 my $config = AppConfig->new( 590 { 591 ERROR => sub { die(@_, "\ntry `$NAME --help'\n") } 592 }, 593 'help|h' => { ACTION => \&help }, 594 'src|s=s' => { EXPAND => EXPAND_ALL }, 595 'dest|d=s' => { EXPAND => EXPAND_ALL }, 596 'lib|l=s@' => { EXPAND => EXPAND_ALL }, 597 'cfg|c=s' => { EXPAND => EXPAND_ALL, DEFAULT => '.' }, 598 'verbose|v' => { DEFAULT => 0, ACTION => $verbinc }, 599 'recurse|r' => { DEFAULT => 0 }, 600 'nothing|n' => { DEFAULT => 0 }, 601 'preserve|p' => { DEFAULT => 0 }, 602 'absolute' => { DEFAULT => 0 }, 603 'relative' => { DEFAULT => 0 }, 604 'colour|color'=> { DEFAULT => 0 }, 605 'summary' => { DEFAULT => 0 }, 606 'all|a' => { DEFAULT => 0 }, 607 'define=s%', 608 'suffix=s%', 609 'binmode=s', 610 'ignore=s@', 611 'copy=s@', 612 'link=s@', 613 'accept=s@', 614 'depend=s%', 615 'depend_debug|depdbg', 616 'depend_file|depfile=s' => { EXPAND => EXPAND_ALL }, 617 'template_module|module=s', 618 'template_anycase|anycase', 619 'template_encoding|encoding=s', 620 'template_eval_perl|eval_perl', 621 'template_load_perl|load_perl', 622 'template_interpolate|interpolate', 623 'template_pre_chomp|pre_chomp|prechomp', 624 'template_post_chomp|post_chomp|postchomp', 625 'template_trim|trim', 626 'template_pre_process|pre_process|preprocess=s@', 627 'template_post_process|post_process|postprocess=s@', 628 'template_process|process=s', 629 'template_wrapper|wrapper=s', 630 'template_recursion|recursion', 631 'template_expose_blocks|expose_blocks', 632 'template_default|default=s', 633 'template_error|error=s', 634 'template_debug|debug=s', 635 'template_start_tag|start_tag|starttag=s', 636 'template_end_tag|end_tag|endtag=s', 637 'template_tag_style|tag_style|tagstyle=s', 638 'template_compile_ext|compile_ext=s', 639 'template_compile_dir|compile_dir=s' => { EXPAND => EXPAND_ALL }, 640 'template_plugin_base|plugin_base|pluginbase=s@' => { EXPAND => EXPAND_ALL }, 641 'perl5lib|perllib=s@' => { EXPAND => EXPAND_ALL }, 642 ); 643 644 # add the 'file' option now that we have a $config object that we 645 # can reference in a closure 646 $config->define( 647 'file|f=s@' => { 648 EXPAND => EXPAND_ALL, 649 ACTION => sub { 650 my ($state, $item, $file) = @_; 651 $file = $state->cfg . "/$file" 652 unless $file =~ /^[\.\/]|(?:\w:)/; 653 $config->file($file) } 654 } 655 ); 656 657 # process main config file, then command line args 658 $config->file($file) if -f $file; 659 $config->args(); 660 661 $config; 662} 663 664 665sub ANSI_escape { 666 my $attr = shift; 667 my $text = join('', @_); 668 return join("\n", 669 map { 670 # look for an existing escape start sequence and add new 671 # attribute to it, otherwise add escape start/end sequences 672 s/ \e \[ ([1-9][\d;]*) m/\e[$1;${attr}m/gx 673 ? $_ 674 : "\e[${attr}m" . $_ . "\e[0m"; 675 } 676 split(/\n/, $text, -1) # -1 prevents it from ignoring trailing fields 677 ); 678} 679 680sub _red(@) { ANSI_escape(31, @_) } 681sub _green(@) { ANSI_escape(32, @_) } 682sub _yellow(@) { ANSI_escape(33, @_) } 683sub _blue(@) { ANSI_escape(34, @_) } 684sub _white(@) { @_ } # nullop 685 686 687#------------------------------------------------------------------------ 688# write_config($file) 689# 690# Writes a sample configuration file to the filename specified. 691#------------------------------------------------------------------------ 692 693sub write_config { 694 my $file = shift; 695 696 open(CONFIG, ">$file") || die "failed to create $file: $!\n"; 697 print(CONFIG <<END_OF_CONFIG); 698#------------------------------------------------------------------------ 699# sample .ttreerc file created automatically by $NAME version $VERSION 700# 701# This file originally written to $file 702# 703# For more information on the contents of this configuration file, see 704# 705# perldoc ttree 706# ttree -h 707# 708#------------------------------------------------------------------------ 709 710# The most flexible way to use ttree is to create a separate directory 711# for configuration files and simply use the .ttreerc to tell ttree where 712# it is. 713# 714# cfg = /path/to/ttree/config/directory 715 716# print summary of what's going on 717verbose 718 719# recurse into any sub-directories and process files 720recurse 721 722# regexen of things that aren't templates and should be ignored 723ignore = \\b(CVS|RCS)\\b 724ignore = ^# 725 726# ditto for things that should be copied rather than processed. 727copy = \\.png\$ 728copy = \\.gif\$ 729 730# ditto for things that should be linked rather than copied / processed. 731# link = \\.flv\$ 732 733# by default, everything not ignored or copied is accepted; add 'accept' 734# lines if you want to filter further. e.g. 735# 736# accept = \\.html\$ 737# accept = \\.tt2\$ 738 739# options to rewrite files suffixes (htm => html, tt2 => html) 740# 741# suffix htm=html 742# suffix tt2=html 743 744# options to define dependencies between templates 745# 746# depend *=header,footer,menu 747# depend index.html=mainpage,sidebar 748# depend menu=menuitem,menubar 749# 750 751#------------------------------------------------------------------------ 752# The following options usually relate to a particular project so 753# you'll probably want to put them in a separate configuration file 754# in the directory specified by the 'cfg' option and then invoke tree 755# using '-f' to tell it which configuration you want to use. 756# However, there's nothing to stop you from adding default 'src', 757# 'dest' or 'lib' options in the .ttreerc. The 'src' and 'dest' options 758# can be re-defined in another configuration file, but be aware that 'lib' 759# options accumulate so any 'lib' options defined in the .ttreerc will 760# be applied every time you run ttree. 761#------------------------------------------------------------------------ 762# # directory containing source page templates 763# src = /path/to/your/source/page/templates 764# 765# # directory where output files should be written 766# dest = /path/to/your/html/output/directory 767# 768# # additional directories of library templates 769# lib = /first/path/to/your/library/templates 770# lib = /second/path/to/your/library/templates 771 772END_OF_CONFIG 773 774 close(CONFIG); 775 print "$file created. Please edit accordingly and re-run $NAME\n"; 776} 777 778 779#------------------------------------------------------------------------ 780# help() 781# 782# Prints help message and exits. 783#------------------------------------------------------------------------ 784 785sub help { 786 print<<END_OF_HELP; 787$NAME $VERSION (Template Toolkit version $Template::VERSION) 788 789usage: $NAME [options] [files] 790 791Options: 792 -a (--all) Process all files, regardless of modification 793 -r (--recurse) Recurse into sub-directories 794 -p (--preserve) Preserve file ownership and permission 795 -n (--nothing) Do nothing, just print summary (enables -v) 796 -v (--verbose) Verbose mode. Use twice for more verbosity: -v -v 797 -h (--help) This help 798 -s DIR (--src=DIR) Source directory 799 -d DIR (--dest=DIR) Destination directory 800 -c DIR (--cfg=DIR) Location of configuration files 801 -l DIR (--lib=DIR) Library directory (INCLUDE_PATH) (multiple) 802 -f FILE (--file=FILE) Read named configuration file (multiple) 803 804Display options: 805 --colour / --color Enable colo(u)rful verbose output. 806 --summary Show processing summary. 807 808File search specifications (all may appear multiple times): 809 --ignore=REGEX Ignore files matching REGEX 810 --copy=REGEX Copy files matching REGEX 811 --link=REGEX Link files matching REGEX 812 --accept=REGEX Process only files matching REGEX 813 814File Dependencies Options: 815 --depend foo=bar,baz Specify that 'foo' depends on 'bar' and 'baz'. 816 --depend_file FILE Read file dependancies from FILE. 817 --depend_debug Enable debugging for dependencies 818 819File suffix rewriting (may appear multiple times) 820 --suffix old=new Change any '.old' suffix to '.new' 821 822File encoding options 823 --binmode=value Set binary mode of output files 824 --encoding=value Set encoding of input files 825 826Additional options to set Template Toolkit configuration items: 827 --define var=value Define template variable 828 --interpolate Interpolate '\$var' references in text 829 --anycase Accept directive keywords in any case. 830 --pre_chomp Chomp leading whitespace 831 --post_chomp Chomp trailing whitespace 832 --trim Trim blank lines around template blocks 833 --eval_perl Evaluate [% PERL %] ... [% END %] code blocks 834 --load_perl Load regular Perl modules via USE directive 835 --absolute Enable the ABSOLUTE option 836 --relative Enable the RELATIVE option 837 --pre_process=TEMPLATE Process TEMPLATE before each main template 838 --post_process=TEMPLATE Process TEMPLATE after each main template 839 --process=TEMPLATE Process TEMPLATE instead of main template 840 --wrapper=TEMPLATE Process TEMPLATE wrapper around main template 841 --default=TEMPLATE Use TEMPLATE as default 842 --error=TEMPLATE Use TEMPLATE to handle errors 843 --debug=STRING Set TT DEBUG option to STRING 844 --start_tag=STRING STRING defines start of directive tag 845 --end_tag=STRING STRING defined end of directive tag 846 --tag_style=STYLE Use pre-defined tag STYLE 847 --plugin_base=PACKAGE Base PACKAGE for plugins 848 --compile_ext=STRING File extension for compiled template files 849 --compile_dir=DIR Directory for compiled template files 850 --perl5lib=DIR Specify additional Perl library directories 851 --template_module=MODULE Specify alternate Template module 852 853See 'perldoc ttree' for further information. 854 855END_OF_HELP 856 857 exit(0); 858} 859 860__END__ 861 862 863#------------------------------------------------------------------------ 864# IMPORTANT NOTE 865# This documentation is generated automatically from source 866# templates. Any changes you make here may be lost. 867# 868# The 'docsrc' documentation source bundle is available for download 869# from http://www.template-toolkit.org/docs.html and contains all 870# the source templates, XML files, scripts, etc., from which the 871# documentation for the Template Toolkit is built. 872#------------------------------------------------------------------------ 873 874=head1 NAME 875 876Template::Tools::ttree - Process entire directory trees of templates 877 878=head1 SYNOPSIS 879 880 ttree [options] [files] 881 882=head1 DESCRIPTION 883 884The F<ttree> script is used to process entire directory trees containing 885template files. The resulting output from processing each file is then 886written to a corresponding file in a destination directory. The script 887compares the modification times of source and destination files (where 888they already exist) and processes only those files that have been modified. 889In other words, it is the equivalent of 'make' for the Template Toolkit. 890 891It supports a number of options which can be used to configure 892behaviour, define locations and set Template Toolkit options. The 893script first reads the F<.ttreerc> configuration file in the HOME 894directory, or an alternative file specified in the TTREERC environment 895variable. Then, it processes any command line arguments, including 896any additional configuration files specified via the C<-f> (file) 897option. 898 899=head2 The F<.ttreerc> Configuration File 900 901When you run F<ttree> for the first time it will ask you if you want 902it to create a F<.ttreerc> file for you. This will be created in your 903home directory. 904 905 $ ttree 906 Do you want me to create a sample '.ttreerc' file for you? 907 (file: /home/abw/.ttreerc) [y/n]: y 908 /home/abw/.ttreerc created. Please edit accordingly and re-run ttree 909 910The purpose of this file is to set any I<global> configuration options 911that you want applied I<every> time F<ttree> is run. For example, you 912can use the C<ignore> and C<copy> / C<link> options to provide regular 913expressions that specify which files should be ignored and which 914should be copied or linked rather than being processed as templates. 915You may also want to set flags like C<verbose> and C<recurse> 916according to your preference. 917 918A minimal F<.ttreerc>: 919 920 # ignore these files 921 ignore = \b(CVS|RCS)\b 922 ignore = ^# 923 ignore = ~$ 924 925 # copy these files 926 copy = \.(gif|png|jpg|pdf)$ 927 928 # recurse into directories 929 recurse 930 931 # provide info about what's going on 932 verbose 933 934In most cases, you'll want to create a different F<ttree> configuration 935file for each project you're working on. The C<cfg> option allows you 936to specify a directory where F<ttree> can find further configuration 937files. 938 939 cfg = /home/abw/.ttree 940 941The C<-f> command line option can be used to specify which configuration 942file should be used. You can specify a filename using an absolute or 943relative path: 944 945 $ ttree -f /home/abw/web/example/etc/ttree.cfg 946 $ ttree -f ./etc/ttree.cfg 947 $ ttree -f ../etc/ttree.cfg 948 949If the configuration file does not begin with C</> or C<.> or something 950that looks like a MS-DOS absolute path (e.g. C<C:\\etc\\ttree.cfg>) then 951F<ttree> will look for it in the directory specified by the C<cfg> option. 952 953 $ ttree -f test1 # /home/abw/.ttree/test1 954 955The C<cfg> option can only be used in the F<.ttreerc> file. All the 956other options can be used in the F<.ttreerc> or any other F<ttree> 957configuration file. They can all also be specified as command line 958options. 959 960Remember that F<.ttreerc> is always processed I<before> any 961configuration file specified with the C<-f> option. Certain options 962like C<lib> can be used any number of times and accumulate their values. 963 964For example, consider the following configuration files: 965 966F</home/abw/.ttreerc>: 967 968 cfg = /home/abw/.ttree 969 lib = /usr/local/tt2/templates 970 971F</home/abw/.ttree/myconfig>: 972 973 lib = /home/abw/web/example/templates/lib 974 975When F<ttree> is invoked as follows: 976 977 $ ttree -f myconfig 978 979the C<lib> option will be set to the following directories: 980 981 /usr/local/tt2/templates 982 /home/abw/web/example/templates/lib 983 984Any templates located under F</usr/local/tt2/templates> will be used 985in preference to those located under 986F</home/abw/web/example/templates/lib>. This may be what you want, 987but then again, it might not. For this reason, it is good practice to 988keep the F<.ttreerc> as simple as possible and use different 989configuration files for each F<ttree> project. 990 991=head2 Directory Options 992 993The C<src> option is used to define the directory containing the 994source templates to be processed. It can be provided as a command 995line option or in a configuration file as shown here: 996 997 src = /home/abw/web/example/templates/src 998 999Each template in this directory typically corresponds to a single 1000web page or other document. 1001 1002The C<dest> option is used to specify the destination directory for the 1003generated output. 1004 1005 dest = /home/abw/web/example/html 1006 1007The C<lib> option is used to define one or more directories containing 1008additional library templates. These templates are not documents in 1009their own right and typically comprise of smaller, modular components 1010like headers, footers and menus that are incorporated into pages templates. 1011 1012 lib = /home/abw/web/example/templates/lib 1013 lib = /usr/local/tt2/templates 1014 1015The C<lib> option can be used repeatedly to add further directories to 1016the search path. 1017 1018A list of templates can be passed to F<ttree> as command line arguments. 1019 1020 $ ttree foo.html bar.html 1021 1022It looks for these templates in the C<src> directory and processes them 1023through the Template Toolkit, using any additional template components 1024from the C<lib> directories. The generated output is then written to 1025the corresponding file in the C<dest> directory. 1026 1027If F<ttree> is invoked without explicitly specifying any templates 1028to be processed then it will process every file in the C<src> directory. 1029If the C<-r> (recurse) option is set then it will additionally iterate 1030down through sub-directories and process and other template files it finds 1031therein. 1032 1033 $ ttree -r 1034 1035If a template has been processed previously, F<ttree> will compare the 1036modification times of the source and destination files. If the source 1037template (or one it is dependant on) has not been modified more 1038recently than the generated output file then F<ttree> will not process 1039it. The F<-a> (all) option can be used to force F<ttree> to process 1040all files regardless of modification time. 1041 1042 $ tree -a 1043 1044Any templates explicitly named as command line argument are always 1045processed and the modification time checking is bypassed. 1046 1047=head2 File Options 1048 1049The C<ignore>, C<copy>, C<link> and C<accept> options are used to 1050specify Perl regexen to filter file names. Files that match any of the 1051C<ignore> options will not be processed. Remaining files that match 1052any of the C<copy> or C<link> regexen will be copied or linked to the 1053destination directory. Remaining files that then match any of the 1054C<accept> criteria are then processed via the Template Toolkit. If no 1055C<accept> parameter is specified then all files will be accepted for 1056processing if not already copied or ignored. 1057 1058 # ignore these files 1059 ignore = \b(CVS|RCS)\b 1060 ignore = ^# 1061 ignore = ~$ 1062 1063 # copy these files 1064 copy = \.(gif|png|jpg|pdf)$ 1065 1066 # accept only .tt2 templates 1067 accept = \.tt2$ 1068 1069The C<suffix> option is used to define mappings between the file 1070extensions for source templates and the generated output files. The 1071following example specifies that source templates with a C<.tt2> 1072suffix should be output as C<.html> files: 1073 1074 suffix tt2=html 1075 1076Or on the command line, 1077 1078 --suffix tt2=html 1079 1080You can provide any number of different suffix mappings by repeating 1081this option. 1082 1083The C<binmode> option is used to set the encoding of the output file. 1084For example use C<--binmode=:utf8> to set the output format to unicode. 1085 1086=head2 Template Dependencies 1087 1088The C<depend> and C<depend_file> options allow you to specify 1089how any given template file depends on another file or group of files. 1090The C<depend> option is used to express a single dependency. 1091 1092 $ ttree --depend foo=bar,baz 1093 1094This command line example shows the C<--depend> option being used to 1095specify that the F<foo> file is dependant on the F<bar> and F<baz> 1096templates. This option can be used many time on the command line: 1097 1098 $ ttree --depend foo=bar,baz --depend crash=bang,wallop 1099 1100or in a configuration file: 1101 1102 depend foo=bar,baz 1103 depend crash=bang,wallop 1104 1105The file appearing on the left of the C<=> is specified relative to 1106the C<src> or C<lib> directories. The file(s) appearing on the right 1107can be specified relative to any of these directories or as absolute 1108file paths. 1109 1110For example: 1111 1112 $ ttree --depend foo=bar,/tmp/baz 1113 1114To define a dependency that applies to all files, use C<*> on the 1115left of the C<=>. 1116 1117 $ ttree --depend *=header,footer 1118 1119or in a configuration file: 1120 1121 depend *=header,footer 1122 1123Any templates that are defined in the C<pre_process>, C<post_process>, 1124C<process> or C<wrapper> options will automatically be added to the 1125list of global dependencies that apply to all templates. 1126 1127The C<depend_file> option can be used to specify a file that contains 1128dependency information. 1129 1130 $ ttree --depend_file=/home/abw/web/example/etc/ttree.dep 1131 1132Here is an example of a dependency file: 1133 1134 # This is a comment. It is ignored. 1135 1136 index.html: header footer menubar 1137 1138 header: titlebar hotlinks 1139 1140 menubar: menuitem 1141 1142 # spanning multiple lines with the backslash 1143 another.html: header footer menubar \ 1144 sidebar searchform 1145 1146Lines beginning with the C<#> character are comments and are ignored. 1147Blank lines are also ignored. All other lines should provide a 1148filename followed by a colon and then a list of dependant files 1149separated by whitespace, commas or both. Whitespace around the colon 1150is also optional. Lines ending in the C<\> character are continued 1151onto the following line. 1152 1153Files that contain spaces can be quoted. That is only necessary 1154for files after the colon (':'). The file before the colon may be 1155quoted if it contains a colon. 1156 1157As with the command line options, the C<*> character can be used 1158as a wildcard to specify a dependency for all templates. 1159 1160 * : config,header 1161 1162=head2 Template Toolkit Options 1163 1164F<ttree> also provides access to the usual range of Template Toolkit 1165options. For example, the C<--pre_chomp> and C<--post_chomp> F<ttree> 1166options correspond to the C<PRE_CHOMP> and C<POST_CHOMP> options. 1167 1168Run C<ttree -h> for a summary of the options available. 1169 1170=head1 AUTHORS 1171 1172Andy Wardley E<lt>abw@andywardley.comE<gt> 1173 1174L<http://www.andywardley.com/|http://www.andywardley.com/> 1175 1176With contributions from Dylan William Hardison (support for 1177dependencies), Bryce Harrington (C<absolute> and C<relative> options), 1178Mark Anderson (C<suffix> and C<debug> options), Harald Joerg and Leon 1179Brocard who gets everywhere, it seems. 1180 1181=head1 VERSION 1182 11832.68, distributed as part of the 1184Template Toolkit version 2.19, released on 27 April 2007. 1185 1186=head1 COPYRIGHT 1187 1188 Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved. 1189 1190 1191This module is free software; you can redistribute it and/or 1192modify it under the same terms as Perl itself. 1193 1194=head1 SEE ALSO 1195 1196L<tpage|Template::Tools::tpage> 1197 1198