tools/createsrc
changeset 0 7f656887cf89
equal deleted inserted replaced
-1:000000000000 0:7f656887cf89
       
     1 #!perl
       
     2 # createsrc
       
     3 # 
       
     4 # Copyright (c) 2009 - 2010 Accenture. All rights reserved.
       
     5 # This component and the accompanying materials are made available
       
     6 # under the terms of the "Eclipse Public License v1.0"
       
     7 # which accompanies this distribution, and is available
       
     8 # at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     9 # 
       
    10 # Initial Contributors:
       
    11 # Accenture - Initial contribution
       
    12 #
       
    13 
       
    14 use strict;
       
    15 use Getopt::Long;
       
    16 use Cwd;
       
    17 use FindBin;
       
    18 
       
    19 # forward decls
       
    20 sub homeDir();
       
    21 sub processCommandLine();
       
    22 sub Usage();
       
    23 sub make4CharId($);
       
    24 sub caseRename($$$);
       
    25 sub renameFileCopy($$);
       
    26 sub GetMacroValue($);
       
    27 sub PromptMacro($);
       
    28 sub CheckMacro($$);
       
    29 sub CheckUid($);
       
    30 sub CheckIdentifier($);
       
    31 sub listTemplates($);
       
    32 sub getTemplates();
       
    33 sub query($$);
       
    34 sub getConfigFileName();
       
    35 sub getConfig($);
       
    36 sub setConfig($$);
       
    37 sub setCopyright($);
       
    38 
       
    39 
       
    40 #globals
       
    41 my $template;
       
    42 my $newName;
       
    43 my $templateDir;
       
    44 my $templateName = "SkeletonTemplate";
       
    45 my $dest;
       
    46 my $help;
       
    47 my $verbose = 0;
       
    48 my %macroValue;
       
    49 my %macroDescription = ( 'UID' => 'a UID', 'COPYRIGHT' => 'a copyright notice' );
       
    50 my $homeDir;
       
    51 my $listTemplates;
       
    52 my $overwrite = 0;
       
    53 my $addToPerforce = 0;
       
    54 my $addToMercurial = 0;
       
    55 my $interactive = 0;
       
    56 
       
    57 processCommandLine();
       
    58 
       
    59 unless (defined $macroValue{'COPYRIGHT'}) {
       
    60 	$macroValue{'COPYRIGHT'} = getConfig('copyright');
       
    61 	$macroValue{'COPYRIGHT'} = "Copyright (c) [[YEAR]] Accenture. All rights reserved." unless $macroValue{'COPYRIGHT'};
       
    62 }
       
    63 
       
    64 
       
    65 $homeDir = homeDir() unless defined ($homeDir);
       
    66 
       
    67 my $year = [localtime()]->[5]+1900;
       
    68 $macroValue{'COPYRIGHT'} =~ s|\[\[YEAR\]\]|$year|g;
       
    69 
       
    70 if ($interactive) {
       
    71   my $templates = getTemplates();
       
    72   print "Available Templates:\n";
       
    73   my $count = 1;
       
    74   my $options="";
       
    75   foreach my $template (@$templates) {
       
    76     print "$count) $template->[0] - $template->[1]\n";
       
    77     $options=$options . $count;
       
    78     $count++;
       
    79   }
       
    80   do {
       
    81   	print "Which template do you want to use? ";
       
    82   	my $in = <>;
       
    83   	chomp $in;
       
    84   	my $choice;
       
    85   	$choice = eval "int($in)";
       
    86   	unless ($@) {
       
    87   	  if (($choice>0) && ($choice <= scalar(@$templates))) {
       
    88   	    $template = $templates->[$choice-1]->[0];
       
    89   	  }
       
    90   	}
       
    91   	my $max = $count-1;
       
    92     print "Please enter a number between 1 and $max\n" unless defined $template;
       
    93   }	until (defined $template);
       
    94   print "Using template $template\n" if $verbose;
       
    95   
       
    96   do {
       
    97     print "Name for the new code? ";
       
    98     $newName = <>;
       
    99     chomp $newName;
       
   100     
       
   101     unless (CheckIdentifier($newName)) {
       
   102       print "$newName is not a valid C++ identifier\n";
       
   103       undef $newName;
       
   104     }
       
   105    
       
   106   } until ((defined $newName) && (length $newName));
       
   107   
       
   108    my $cwd = cwd();
       
   109    print "Directoy to create new code in? [$cwd] ";
       
   110    $dest = <>;
       
   111    chomp $dest;
       
   112    $dest = $cwd unless (length $dest);
       
   113   
       
   114 } else {
       
   115 
       
   116   $dest = cwd() unless $dest;
       
   117   
       
   118 }
       
   119 
       
   120 $templateDir = $homeDir."/createsrc-templates/$template";
       
   121 die "ERROR: Template $template not found at $templateDir\n" unless -d $templateDir;
       
   122 
       
   123 $dest =~ s|\\|/|g;
       
   124 $dest =~ s|/$||;
       
   125 print "Creating $newName at $dest from template $template at $templateDir\n" if ($verbose);
       
   126 
       
   127 # read [[MACRO]] descriptions from .txt file
       
   128 my $infoFile;
       
   129 my $infoFileName = "$templateDir.txt";
       
   130 if ((-f $infoFileName) && (open $infoFile, $infoFileName)) {
       
   131 	print "Reading $infoFileName\n" if ($verbose);
       
   132 	<$infoFile>; # skip template description
       
   133 	while (my $line = <$infoFile>) {
       
   134 		chomp $line;
       
   135 		$line =~ s|#.*||; # remove perl style comments
       
   136 		if ($line =~ m|^\s*(\w[\w\d]*)\s*\:\s*(.*)$|) {
       
   137 			my ($macro, $desc) = ($1, $2);
       
   138 			print "Macro '$macro': '$desc'\n" if ($verbose>1);
       
   139 			$macroDescription{$macro} = $desc;
       
   140 		} elsif ($line =~ m|^\s*$|) {
       
   141 			# blank line
       
   142 		} else {
       
   143 			die "Error: cannot parse line $. of $infoFileName\n";
       
   144 		}
       
   145 	}
       
   146 	
       
   147 	close $infoFile;
       
   148 }
       
   149 
       
   150 unless (-d $dest) {
       
   151   mkdir($dest) or die "ERROR: Couldn't create directory $dest: $!\n";
       
   152   print "Created directory $dest\n" if ($verbose>1);
       
   153 }
       
   154 
       
   155 my @dirStack;
       
   156 push @dirStack, "";
       
   157 
       
   158 
       
   159 while ($#dirStack>=0) {
       
   160   my $dir = pop(@dirStack);
       
   161 
       
   162   my $dirName = $dir;
       
   163   $dir = "/$dir" unless ($dir eq "") || ($dir =~ m|^/|);
       
   164 
       
   165   # create destination directory
       
   166   unless (-d "$dest$dir") {
       
   167     mkdir "$dest$dir" or die "ERROR: cannot create directory $dest$dir: $!\n";
       
   168     print "Created directory $dest$dir\n" if ($verbose);
       
   169   }
       
   170 
       
   171   # read template directory contents
       
   172   opendir DIR, "$templateDir$dir";
       
   173   my @dirContents = readdir(DIR);
       
   174   closedir(DIR);
       
   175 
       
   176   foreach my $f (@dirContents) {
       
   177     if (-f "$templateDir$dir/$f") {
       
   178       my $newFile = caseRename($f, $templateName, $newName);
       
   179       print "Renaming from $templateDir$dir/$f to $dest$dir/$newFile\n" if ($verbose);
       
   180       renameFileCopy("$templateDir$dir/$f", "$dest$dir/$newFile"); 
       
   181     } elsif (-d "$templateDir$dir/$f") {
       
   182       unless ($f =~ m|^\.\.?$|) {
       
   183         print "Adding $dirName/$f to stack\n" if ($verbose);
       
   184         push @dirStack, "$dirName/$f";
       
   185       }
       
   186     } else {
       
   187       print "WARNING: $templateDir$dir/$f is neither a file nor a directory";
       
   188     }
       
   189   }
       
   190 
       
   191 }
       
   192 
       
   193 
       
   194 
       
   195 sub homeDir() {
       
   196   my $homeDir = $FindBin::Bin;
       
   197   print "homeDir=$homeDir\n" if ($verbose > 1);
       
   198   return $homeDir;
       
   199 }
       
   200 
       
   201 sub processCommandLine() {
       
   202   my $setCopyright = undef;
       
   203   Getopt::Long::Configure("bundling");
       
   204   my @macroDefs;
       
   205   GetOptions('h'   => \$help,
       
   206              'v+'  => \$verbose,
       
   207              'd=s' => \$dest,
       
   208              's=s' => \$homeDir,
       
   209              't=s' => \$templateName,
       
   210              'l'   => \$listTemplates,
       
   211              'o'   => \$overwrite,
       
   212              'p'   => \$addToPerforce,
       
   213 	     'm'   => \$addToMercurial,
       
   214              'i'   => \$interactive,
       
   215              'c=s' => \$setCopyright,
       
   216 	     'D=s' => \@macroDefs);
       
   217 
       
   218   Usage() if ($help);
       
   219 
       
   220   listTemplates(1) if ($listTemplates);
       
   221   
       
   222   setCopyright($setCopyright) if (defined $setCopyright);
       
   223 
       
   224   unless ($interactive) {
       
   225     if ($#ARGV < 1) {
       
   226       print "Insufficient arguments; entering interactive mode\n";
       
   227       $interactive = 1;
       
   228     } else {
       
   229       $template = shift(@ARGV);
       
   230       $newName = shift(@ARGV);
       
   231       die "$newName is not a valid C++ identifier\n" unless CheckIdentifier($newName);
       
   232     }
       
   233   }
       
   234   foreach my $d (@macroDefs) {
       
   235 	if ($d =~ m|^(\w[\w\d]*)=(.*)$|) {
       
   236 		my ($macro, $value) = ($1, $2);
       
   237 		print "Got macro '$macro'='$value' from command line\n" if ($verbose>1);
       
   238 		die "Error: '$value' is not a valid value for $macro\n" unless CheckMacro($macro, $value);
       
   239 		$macroValue{$macro} = $value;
       
   240 	} else {
       
   241 		die "Error: Incorrect usage of -D: $d: expected a string of the format 'id=value'\n";
       
   242 	}
       
   243   }  
       
   244   print "Warning: ignoring " . scalar(@ARGV) . " extra arguments\n" if scalar(@ARGV);
       
   245 }
       
   246 
       
   247 sub Usage() {
       
   248   require Pod::Text;
       
   249   my $parser = Pod::Text->new();
       
   250   $parser->parse_from_file($0);
       
   251   exit;
       
   252 }
       
   253 
       
   254 sub make4CharId($) {
       
   255 	my $longId = shift;
       
   256 	while (length($longId)<2) {
       
   257 		$longId = $longId . '_';
       
   258 	}
       
   259 	return uc( substr($longId, 0, 2) . substr($longId, length($longId)-2, 2) );
       
   260 }
       
   261 
       
   262 sub caseRename($$$) {
       
   263   my $string = shift;
       
   264   my $oldName = shift;
       
   265   my $newName = shift;
       
   266 
       
   267   my $oldUpper = $oldName;
       
   268   $oldUpper =~ tr/a-z/A-Z/;
       
   269 
       
   270   my $oldLower = $oldName;
       
   271   $oldLower =~ tr/A-Z/a-z/;
       
   272 
       
   273   my $oldLowerFirst = "\l$oldName;\E";
       
   274 
       
   275   my $uppercase = $newName;
       
   276   $uppercase =~ tr/a-z/A-Z/;
       
   277 
       
   278   my $lowercase = $newName;
       
   279   $lowercase =~ tr/A-Z/a-z/;
       
   280 
       
   281   my $lowerFirst = "\l$newName\E";
       
   282   
       
   283   my $old4Char = make4CharId($oldName);
       
   284   my $new4Char = make4CharId($newName);
       
   285 
       
   286   $string =~ s/$oldName/$newName/g;
       
   287   $string =~ s/$oldUpper/$uppercase/g;
       
   288   $string =~ s/$oldLower/$lowercase/g;
       
   289   $string =~ s/$oldLowerFirst/$lowerFirst/g;
       
   290   $string =~ s/$old4Char/$new4Char/g;
       
   291   return $string;
       
   292 }
       
   293 
       
   294 sub renameFileCopy($$) {
       
   295   my $src = shift;
       
   296   my $dest = shift;
       
   297   open SOURCE, "<$src" or die "ERROR: can't open $src for reading: $!\n";
       
   298   unless ($overwrite) {
       
   299     die "ERROR: $dest already exists. Use -o to overwrite. Aborting.\n" if (-f $dest);
       
   300   }
       
   301   open DEST, ">$dest" or die "ERROR: can't open $dest for writing: $!\n";
       
   302   while (my $line = <SOURCE>) {
       
   303     while ($line =~ m|\[\[(\w[\w\d]*)\]\]|) {
       
   304 	my $id = $1;
       
   305 	my $value = GetMacroValue($id);
       
   306 	$line =~ s|\[\[$id\]\]|$value|g;
       
   307     }
       
   308     print DEST caseRename($line, $templateName, $newName);
       
   309     
       
   310   }
       
   311   close (SOURCE);
       
   312   close (DEST);
       
   313   if ($addToPerforce) {
       
   314     print "p4 add $dest\n";
       
   315     `p4 add $dest` ;
       
   316   }
       
   317   if ($addToMercurial) {
       
   318     print "hg add $dest\n";
       
   319     `hg add $dest` ;
       
   320   }
       
   321 }
       
   322 
       
   323 sub GetMacroValue($) {
       
   324 	my $macro = shift;
       
   325 	if (!defined($macroValue{$macro})) {
       
   326 		$macroValue{$macro} = PromptMacro($macro);
       
   327 	}
       
   328 	return $macroValue{$macro};
       
   329 }
       
   330 
       
   331 sub PromptMacro($) {
       
   332 	my $macro = shift;
       
   333 	my $description = $macroDescription{$macro};
       
   334 	$description = $macro unless defined $description;		
       
   335 	
       
   336 	print "Enter $description for $newName: ";
       
   337 	my $value;
       
   338 	do  {
       
   339 		$value = <>;
       
   340 		chomp $value;
       
   341 	} while (!CheckMacro($macro, $value));
       
   342 	return $value;
       
   343 }
       
   344 
       
   345 sub CheckMacro($$) {
       
   346 	my ($macro, $value) = @_;
       
   347 	if ($macro eq 'UID') {
       
   348 		return CheckUid($value);
       
   349 	}
       
   350 	return 1;
       
   351 }
       
   352 
       
   353 sub CheckUid($) {
       
   354   my $uid = shift;
       
   355   return 1 if ($uid =~ m|^0x[0-9a-f]{1,8}$|i);
       
   356   return 1 if ($uid =~ m|^[0-9]{1,10}$|);
       
   357   return 0;
       
   358 }
       
   359 
       
   360 sub CheckIdentifier($)	{
       
   361   my $id = shift;
       
   362   return ($id =~ m|[A-Za-z_][A-Za-z_0-9]*|);
       
   363 }
       
   364 
       
   365 sub getTemplates() {
       
   366   $homeDir = homeDir() unless defined ($homeDir);
       
   367   opendir TEMPLATES, "$homeDir/createsrc-templates" or die "Can't read directory $homeDir/createsrc-templates: $!\n";
       
   368   my @templateNames = grep((-d "$homeDir/createsrc-templates/$_")&&!(m|\.\.?|), readdir(TEMPLATES));
       
   369   close TEMPLATES;
       
   370   die "No templates found at $homeDir/createsrc-templates\n" unless scalar(@templateNames);
       
   371   my @templates = ();
       
   372   foreach my $template (@templateNames) {
       
   373   	my $desc;
       
   374     my $descFile = "$homeDir/createsrc-templates/$template.txt";
       
   375     if ((-f $descFile)&&(open DESC, $descFile)) {
       
   376       $desc = <DESC>;
       
   377       chomp $desc;
       
   378       close DESC;
       
   379     } else {
       
   380      $desc = "<no description>";
       
   381     }
       
   382     push @templates, [$template, $desc];
       
   383   }
       
   384   @templates = sort {$a->[0] cmp $b->[0]} @templates;
       
   385   return \@templates;
       
   386 }
       
   387 
       
   388 sub listTemplates($) {
       
   389   my $exitWhenDone = shift;
       
   390   my $templates = getTemplates();
       
   391   foreach my $template (@$templates) {
       
   392     print $template->[0] . " - " . $template->[1] . "\n";
       
   393   }
       
   394   exit if $exitWhenDone;
       
   395 }
       
   396 
       
   397 sub getConfigFileName() {
       
   398   my $fn;
       
   399   if (defined $ENV{'USERPROFILE'}) {
       
   400     $fn = $ENV{'USERPROFILE'};
       
   401   } else {
       
   402     $fn = homeDir();
       
   403   }
       
   404   $fn =~ s|[/\\]$||;
       
   405   $fn .= "\\createsrc.cfg";
       
   406   return $fn;
       
   407 }
       
   408 
       
   409 sub getConfig($) {
       
   410   my $findKey = shift;
       
   411   my $cfg = getConfigFileName();
       
   412   my $foundValue = undef;
       
   413   open CFGFILE, "<$cfg" or return undef;
       
   414   print "Reading $cfg\n" if ($verbose);
       
   415   while (my $line = <CFGFILE>) {
       
   416     chomp $line;
       
   417     if ($line =~ m|^(.*?)\s*=\s*(.*)$|)	{
       
   418       my ($key, $value) = ($1, $2);
       
   419       print "Read '$key'='$value'\n" if ($verbose>1);
       
   420       $foundValue = $value if ($key eq $findKey);
       
   421     } else {
       
   422       print "ignoring line '$line'\n" if ($verbose>1);
       
   423     }
       
   424   }
       
   425   return $foundValue;
       
   426   close CFGFILE;  
       
   427 }
       
   428 
       
   429 sub setConfig($$) {
       
   430   my ($key, $value) = @_;
       
   431   my $cfg = getConfigFileName();
       
   432   my @lines;
       
   433   if (open CFGFILE, "<$cfg") {
       
   434     print "Reading $cfg\n";
       
   435     @lines = <CFGFILE>;
       
   436     close CFGFILE;
       
   437   }
       
   438   open CFGFILE, ">$cfg" or die "Cannot write to $cfg: $!\n";
       
   439   print "Writing $cfg\n" if ($verbose);
       
   440   my $written = 0;
       
   441   foreach my $line (@lines) {
       
   442     if ($line =~ m|^(.*?)\s*=\s*(.*)$|)	{
       
   443       my ($foundKey, $foundValue) = ($1, $2);
       
   444       print "Read '$foundKey'='$foundValue'\n" if ($verbose>1);
       
   445       if ($key eq $foundKey) {
       
   446         $foundValue = $value;
       
   447         $written = 1;
       
   448         print "Updated key $foundKey to '$foundValue'\n" if ($verbose>1);
       
   449       }
       
   450       print CFGFILE "$foundKey=$foundValue\n";
       
   451     } else {
       
   452       print "ignoring line '$line'\n" if ($verbose>1);
       
   453       print CFGFILE $line;
       
   454     }
       
   455   }
       
   456   unless ($written) {
       
   457     print "Adding $key=$value\n";
       
   458     print CFGFILE "$key=$value\n";
       
   459   }
       
   460   close CFGFILE;
       
   461   
       
   462 }
       
   463 
       
   464 sub setCopyright($) {
       
   465   my $copyright =  shift;
       
   466   print "Setting copyright notice to '$copyright'\n" if ($verbose);
       
   467   setConfig('copyright', $copyright);
       
   468   exit(1);
       
   469 }
       
   470 
       
   471 
       
   472 __END__
       
   473 
       
   474 =head1 NAME
       
   475 
       
   476 createsrc - creates new code from templates
       
   477 
       
   478 =head1 SYNOPSIS
       
   479 
       
   480 	createsrc [options] [TemplateName NewName]
       
   481 
       
   482 options:
       
   483 
       
   484 =over
       
   485 
       
   486 =item -i
       
   487 
       
   488 Use L<Interactive mode>. This will be the default if C<TemplateName> and C<NewName> are not given.
       
   489 
       
   490 =item -d <directory>
       
   491 
       
   492 Put the new code in the specified directory. Defaults to current directory.
       
   493 
       
   494 =item -DMACRO=value
       
   495 
       
   496 Specfies a value for a macro. See "Macros" section below.
       
   497 
       
   498 =item -c "Copyright Notice"
       
   499 
       
   500 Sets the default copyright to use when none other is specified. See L<Copyright Notices> below.
       
   501 
       
   502 =item -o
       
   503 
       
   504 Overwrites any existing files when creating the new code.
       
   505 
       
   506 =item -p
       
   507 
       
   508 Add the new files to perforce as they are created.
       
   509 
       
   510 =item -m
       
   511 
       
   512 Add the new files to mercurial as they are created.
       
   513 
       
   514 =item -s <directory>
       
   515 
       
   516 Specifies the home directory of the script, where the templates are located. Defaults to the directory where the script is located.
       
   517 
       
   518 =item -t <TemplateText>
       
   519 
       
   520 Specified the text to be replaced in the template. Defaults to C<SkeletonTemplate>. It's not usually necessary to change this.
       
   521 
       
   522 =item -l
       
   523 
       
   524 List available templates, then exits.
       
   525 
       
   526 =item -h
       
   527 
       
   528 Shows this help.
       
   529 
       
   530 =item -v
       
   531 
       
   532 Verbose mode (-vv very verbose).
       
   533 
       
   534 =back
       
   535 
       
   536 =head1 DESCRIPTION
       
   537 
       
   538 This script creates new projects or code snippets from templates. For example, assuming you have a template C<UiqBasic> that consists of a basic UIQ application, you could create a HelloWorld application as follows:
       
   539 
       
   540 	createsrc UiqBasic HelloWorld
       
   541 
       
   542 When creating the new code, it performs the following steps:
       
   543 
       
   544 =over
       
   545 
       
   546 =item 1.
       
   547 
       
   548 Copies and renames the files as appropriate.
       
   549 
       
   550 =item 2.
       
   551 
       
   552 Replaces text within the files as appropriate.
       
   553 
       
   554 =item 3.
       
   555 
       
   556 Substitutes macros enclosed in double square brackets, C<[[MACRO]]>.
       
   557 
       
   558 =back
       
   559 
       
   560 When renaming files or substituting text withing the files, case is preseved as follows:
       
   561 
       
   562 =over
       
   563 
       
   564 =item 1.
       
   565 
       
   566 C<MixedCaseText> is preserved - i.e. C<TemplateText> becomes C<NewText>.
       
   567 
       
   568 =item 2.
       
   569 
       
   570 C<lowercasetext> is preserved - i.e. C<templatetext> becomes C<newtext>.
       
   571 
       
   572 =item 3.
       
   573 
       
   574 C<UPPERCASETEXT> is preserved - i.e. C<TEMPLATETEXT> becomes C<NEWTEXT>.
       
   575 
       
   576 =item 4.
       
   577 
       
   578 C<lowerCaseFirst> is preserved - i.e. C<templateText> becomes C<newText>.
       
   579 
       
   580 =back
       
   581 
       
   582 This should cover all forms of capitalisation used within normal Symbian code.
       
   583 
       
   584 In addition, the capitalised first and last two letters of C<TemplateText> is replaced with the same letters of C<NewText> - i.e. C<TEXT> becomes C<NEXT>. This is to support the C<NAME> value in an applications resource file, which must always have 4 letters.
       
   585 
       
   586 If the new text has less than 4 characters, it will be padded with underscores. For example, an application called C<A> will be given a C<NAME> of C<A_A_>; C<Ap> becomes C<APAP>; C<HelloWorld> becomes C<HELD> and so on.
       
   587 
       
   588 =head2 Interactive mode
       
   589 
       
   590 If the C<-i> option is given, of if no C<TemplateName> and C<TemplateText> are given, interactive mode will be used:
       
   591 
       
   592 	D:\work\new>createsrc
       
   593 	Insufficient arguments; entering interactive mode
       
   594 	Available Templates:
       
   595 	1) UiqBasic - Basic UIQ application
       
   596 	2) UiqImaging - Basic UIQ application with one large control, suitable for displaying an image, a video, etc.
       
   597 	3) UiqList - Basic UIQ application with a list in the view.
       
   598 	4) server - Symbian 9 transient server
       
   599 	Which template do you want to use? 1
       
   600 	Name for the new code? HelloWorld
       
   601 	Directoy to create new code in? [D:/work/new]
       
   602 	Enter a UID for HelloWorld: 0x10002000
       
   603 
       
   604 When prompted for the directory to create the code in, simply hit enter to use the suggestion in square brackets. Or you can enter a relative or absolute path name to use instead.
       
   605 
       
   606 =head2 Macros
       
   607 
       
   608 Macros allow extra custom strings to be inserted into the generate code. Macro values can be specified on the command line using the C<-D> option; if no value is specified then it will be prompted for at runtime.
       
   609 
       
   610 There are two macros that are used by all (or most) templates:
       
   611 
       
   612 =over
       
   613 
       
   614 =item C<COPYRIGHT>
       
   615 
       
   616 Defines a copyright notice for the code, usually inserted in a comment at the top of each source file. See L<Copyright Notices> below.
       
   617 
       
   618 =item C<UID>
       
   619 
       
   620 Specifies a UID for the new code. This will be verified as a valid integer.
       
   621 
       
   622 =back
       
   623 
       
   624 Some templates may define other, custom macros. To define a macro value on the command line, use -D syntax in a similar way the a C preprocessor:
       
   625 
       
   626 	createsrc -DUID=0x01234567
       
   627 
       
   628 
       
   629 =head2 Copyright Notices
       
   630 
       
   631 Copyright notices are automatically inserted into the source files, including the current year. When using the C<-DCOPYRIGHT=...> and C<-c> options, the copyright text may include C<[[YEAR]]>, which will be replaced with the current year when the code was created.
       
   632 
       
   633 For example,
       
   634 
       
   635 	createsrc -DCOPYRIGHT="Copyright (c) [[YEAR]] Joe Bloggs. All rights reserved"
       
   636 
       
   637 The default copyright notice is C<Copyright (c) [[YEAR]] Accenture. All rights reserved.>
       
   638 
       
   639 When the C<-c> option is used, the copyright notice is stored in C<%USERPROFILE%\createsrc.cfg> and so is persisted for future runs of C<createsrc>.
       
   640 
       
   641 =head2 Templates
       
   642 
       
   643 The templates are located in F<\epoc32\tools\Templates\TemplateName>. Each template usually contains an entire buildable project, but this may vary depending upon the nature of the template. The text that is replaced in template filenames and files is C<SkeletonTemplate> by default, but this can be modified with the C<-t TemplateText> option (although it is not recommended).
       
   644 
       
   645 In the template, macros are enclosed in double square brackets, for example C<[[UID]]>. Templates should make use of the standard C<[[UID]]> and C<[[COPYRIGHT]]> macros and can use others if necessary.
       
   646 
       
   647 If a F<TemplateName.txt> file is found in F<\epoc32\tools\Templates>, the first line of this file will be used as the description of the template when the C<-l> flag is given. The remainder of the file is expected to contain lines of the format:
       
   648 
       
   649 	MACRO: description
       
   650 
       
   651 giving descriptions for any custom macros used in the project. The descriptions will be used instead of the macro name when prmopting the user for a value at runtime.
       
   652 
       
   653 =head1 COPYRIGHT
       
   654 
       
   655 Copyright (c) 2008-2010 Accenture. All rights reserved.
       
   656 
       
   657 =cut
       
   658