File Coverage

blib/lib/LCFG/Build/Skeleton.pm
Criterion Covered Total %
statement 25 27 92.5
branch n/a
condition n/a
subroutine 9 9 100.0
pod n/a
total 34 36 94.4


line stmt bran cond sub pod time code
1             package LCFG::Build::Skeleton; # -*-perl-*-
2 1     1   835 use strict;
  1         1  
  1         39  
3 1     1   5 use warnings;
  1         2  
  1         48  
4              
5             # $Id: Skeleton.pm.in 26933 2014-12-08 16:08:31Z squinney@INF.ED.AC.UK $
6             # $Source: /var/cvs/dice/LCFG-Build-Skeleton/lib/LCFG/Build/Skeleton.pm.in,v $
7             # $Revision: 26933 $
8             # $HeadURL: https://svn.lcfg.org/svn/source/tags/LCFG-Build-Skeleton/LCFG_Build_Skeleton_0_4_1/lib/LCFG/Build/Skeleton.pm.in $
9             # $Date: 2014-12-08 16:08:31 +0000 (Mon, 08 Dec 2014) $
10              
11             our $VERSION = '0.4.1';
12              
13 1     1   617 use Email::Address ();
  1         24252  
  1         84  
14 1     1   784 use Email::Valid ();
  1         84221  
  1         31  
15 1     1   9 use File::Basename ();
  1         1  
  1         11  
16 1     1   4 use File::Path ();
  1         2  
  1         16  
17 1     1   5 use File::Spec ();
  1         2  
  1         19  
18 1     1   876 use File::Temp ();
  1         8171  
  1         26  
19 1     1   208 use LCFG::Build::PkgSpec ();
  0            
  0            
20             use List::MoreUtils qw(none);
21             use Sys::Hostname ();
22             use Template ();
23             use UNIVERSAL::require;
24             use YAML::Syck ();
25              
26             my $TMPLDIR
27             = exists $ENV{LCFG_BUILD_TMPLDIR}
28             ? $ENV{LCFG_BUILD_TMPLDIR}
29             : '/usr/share/lcfgbuild/templates';
30              
31             use Moose;
32             use Moose::Util::TypeConstraints;
33              
34             with 'MooseX::Getopt';
35              
36             subtype 'LCFG::Types::ComponentName'
37             => as 'Str'
38             => where { m/^[A-Za-z][A-Za-z0-9_]+$/ };
39              
40             subtype 'LCFG::Types::EmailAddress'
41             => as 'Str'
42             => where { Email::Valid->address( -address => $_ ) }
43             => message { "Address ($_) for report must be a valid email address" };
44              
45             subtype 'LCFG::Types::Response'
46             => as 'Str'
47             => where { $_ eq 'yes' || $_ eq 'no' };
48              
49             coerce 'LCFG::Types::Response' => from 'Str' =>
50             via { $_ && ( $_ eq '1' || m/^ye(s|p|ah)!?$/i ) ? 'yes' : 'no' };
51              
52             MooseX::Getopt::OptionTypeMap->add_option_type_to_map(
53             'LCFG::Types::Response' => q{!}, );
54              
55             has 'configfile' => (
56             is => 'ro',
57             isa => 'Str',
58             default => sub { File::Spec->catfile( $ENV{HOME}, '.lcfg',
59             'skeleton', 'defaults.yml' ) },
60             predicate => 'has_configfile',
61             documentation => 'Where defaults should be stored',
62             );
63              
64             has 'tmpldir' => (
65             is => 'ro',
66             isa => 'Str',
67             default => sub { File::Spec->catdir( $ENV{HOME}, '.lcfg',
68             'skeleton', 'templates' ) },
69             documentation => 'Local templates directory',
70             );
71              
72             has 'usage' => (
73             metaclass => 'NoGetopt',
74             is => 'ro',
75             isa => 'Str',
76             );
77              
78             has 'help' => (
79             is => 'ro',
80             isa => 'Bool',
81             default => 0,
82             documentation => 'Display the help text',
83             );
84              
85             has 'name' => (
86             is => 'rw',
87             isa => 'LCFG::Types::ComponentName',
88             documentation => 'Name of the project',
89             );
90              
91             has 'abstract' => (
92             is => 'rw',
93             isa => 'Str',
94             lazy => 1,
95             documentation => 'Short description of the project',
96             default => sub {
97             $_[0]->lcfg_component eq 'yes'
98             ? 'The LCFG ' . $_[0]->name . ' component'
99             : q{};
100             },
101             );
102              
103             has 'author_name' => (
104             is => 'rw',
105             isa => 'Str',
106             default => sub { ( getpwuid $< )[6] },
107             documentation => 'Name of the author',
108             );
109              
110             has 'author_email' => (
111             is => 'rw',
112             isa => 'LCFG::Types::EmailAddress',
113             builder => '_default_email',
114             documentation => 'Email address for the author',
115             );
116              
117             sub _default_email {
118              
119             my $email;
120             if ( $ENV{EMAIL} ) {
121             $email = $ENV{EMAIL};
122             } else {
123             my $username = ( getpwuid $< )[0];
124              
125             my ( $hostname, @domain ) = split /\./, Sys::Hostname::hostname;
126              
127             my $domain = join q{.}, @domain;
128             $email = join q{@}, $username, $domain;
129             }
130              
131             return $email;
132             }
133              
134             has 'lang' => (
135             is => 'rw',
136             isa => enum( [qw/perl shell/] ),
137             documentation => 'Language for component (perl/shell)',
138             default => 'perl',
139             );
140              
141             has 'vcs' => (
142             is => 'rw',
143             isa => enum( [qw/SVN CVS None/] ),
144             documentation => 'Version Control System (SVN/CVS/None)',
145             default => 'CVS',
146             );
147              
148             has 'platforms' => (
149             is => 'rw',
150             isa => 'Maybe[Str]',
151             documentation => 'Supported platforms',
152             );
153              
154             has 'license' => (
155             is => 'rw',
156             isa => 'Str',
157             documentation => 'Distribution license',
158             default => 'GPLv2',
159             );
160              
161             has 'restart' => (
162             is => 'rw',
163             isa => 'LCFG::Types::Response',
164             coerce => 1,
165             documentation => 'Restart component on RPM update (yes/no)',
166             default => 'yes',
167             );
168              
169             has 'gencmake' => (
170             is => 'rw',
171             isa => 'LCFG::Types::Response',
172             coerce => 1,
173             documentation => 'Use the CMake build system (yes/no)',
174             default => 'yes',
175             );
176              
177             has 'genchangelog' => (
178             is => 'rw',
179             isa => 'LCFG::Types::Response',
180             coerce => 1,
181             documentation => 'Generate the ChangeLog from the Revision-Control log? (yes/no)',
182             default => 'no',
183             );
184              
185             has 'checkcommitted' => (
186             is => 'rw',
187             isa => 'LCFG::Types::Response',
188             coerce => 1,
189             documentation => 'Check all changes are committed before a release? (yes/no)',
190             default => 'yes',
191             );
192              
193             has 'lcfg_component' => (
194             is => 'rw',
195             isa => 'LCFG::Types::Response',
196             coerce => 1,
197             documentation => 'Is this an LCFG component? (yes/no)',
198             default => 'yes',
199             );
200              
201             has 'interactive' => (
202             is => 'ro',
203             isa => 'Bool',
204             documentation => 'Interactively query the user',
205             default => 1,
206             );
207              
208             has 'force' => (
209             is => 'ro',
210             isa => 'Bool',
211             documentation => 'Forceably remove an old project directory',
212             default => 0,
213             );
214              
215             sub get_config_from_file {
216             my ( $self, $file ) = @_;
217              
218             my $cfg = {};
219             if ( -f $file ) {
220             $cfg = YAML::Syck::LoadFile($file);
221             }
222              
223             return $cfg;
224             }
225              
226             # The code for new_with_options() is mostly stolen from
227             # MooseX::Getopt. It has modifications to allow us to ignore the need
228             # to check for a MooseX::ConfigFromFile role. That module just pulls
229             # in far too many extra dependencies for our fairly small
230             # requirements. This code also handles the default value for the
231             # configfile being a code-reference.
232              
233             sub new_with_options {
234             my ( $class, @params ) = @_;
235              
236             my $config_from_file;
237             {
238             local @ARGV = @ARGV;
239              
240             my $configfile;
241             my $opt_parser
242             = Getopt::Long::Parser->new( config => [qw(pass_through)] );
243              
244             $opt_parser->getoptions( 'configfile=s' => \$configfile );
245              
246             if ( !defined $configfile ) {
247             my $cfmeta = $class->meta->find_attribute_by_name('configfile');
248             if ( $cfmeta->has_default ) {
249             $configfile = $cfmeta->default;
250             }
251             if ( 'CODE' eq ref $configfile ) {
252             $configfile = $configfile->();
253             }
254             }
255              
256             if ( defined $configfile ) {
257             $config_from_file = $class->get_config_from_file($configfile);
258             }
259              
260             }
261              
262             my $constructor_params = ( @params == 1 ? $params[0] : {@params} );
263              
264             if ( ref $constructor_params ne 'HASH' ) {
265             die "Single parameters to new_with_options() must be a HASH ref\n";
266             }
267              
268             my %processed = $class->_parse_argv(
269             options => [ $class->_attrs_to_options($config_from_file) ],
270             params => $constructor_params,
271             );
272              
273             my $params
274             = $config_from_file
275             ? { %{$config_from_file}, %{ $processed{params} } }
276             : $processed{params};
277              
278             return $class->new(
279             ARGV => $processed{argv_copy},
280             extra_argv => $processed{argv},
281             usage => "$processed{usage}", # stringify immediately
282             @params, # explicit params to ->new
283             %{$params}, # params from CLI
284             );
285              
286             }
287              
288             my @questions = qw(
289             name
290             lcfg_component
291             abstract
292             author_name
293             author_email
294             lang
295             vcs
296             platforms
297             license
298             restart
299             gencmake
300             checkcommitted
301             genchangelog
302             );
303              
304             sub store_answers {
305             my ($self) = @_;
306              
307             my @ignore = qw(name lcfg_component abstract);
308             my @extra = qw(tmpldir);
309              
310             my %store;
311             for my $question ( @questions, @extra ) {
312             if ( none { $question eq $_ } @ignore ) {
313             $store{$question} = $self->$question;
314             }
315             }
316              
317             my $cfg = $self->configfile;
318              
319             my ( $name, $path ) = File::Basename::fileparse($cfg);
320             if ( !-d $path ) {
321             eval { File::Path::mkpath($path) };
322             if ($@) {
323             die "Failed to create directory, $cfg: $!\n";
324             }
325             }
326              
327             YAML::Syck::DumpFile( $cfg, \%store );
328              
329             return;
330             }
331              
332             sub query_user {
333             my ($self) = @_;
334              
335             if ( $self->interactive ) {
336             for my $question (@questions) {
337              
338             my $doc = $self->meta->get_attribute($question)->documentation;
339              
340             my $default = $self->$question;
341              
342             my $defstring = q{};
343             if ( defined $default ) {
344             $defstring = ' [' . $default . ']';
345             }
346              
347             while (1) {
348             print $doc . $defstring . q{: };
349             chomp( my $answer = <STDIN> );
350              
351             # trim any whitespace from the response
352             $answer =~ s/^\s+//;
353             $answer =~ s/\s+$//;
354              
355             if ( length $answer > 0 ) {
356             eval { $self->$question($answer) };
357             }
358              
359             if ($@) {
360             print "Error: Bad choice, please try again.\n";
361             } else {
362             last;
363             }
364              
365             }
366             }
367             }
368              
369             # always store the answers as they may have come from the command line
370             $self->store_answers;
371              
372             return;
373             }
374              
375             sub create_package {
376             my ($self) = @_;
377              
378             # Make an attempt to sanitise whatever the user gave us as an
379             # email address.
380              
381             my ($addr) = Email::Address->parse($self->author_email);
382              
383             my $new_addr = Email::Address->new( $self->author_name,
384             $addr->address );
385             my $author = $new_addr->format;
386              
387             my @platforms;
388             if ( $self->platforms ) {
389             @platforms = split /\s*,\s*/, $self->platforms;
390             }
391              
392             # Sometimes people mistakenly put a 'lcfg-' prefix on the name.
393             my $name = $self->name;
394             if ( $self->lcfg_component eq 'yes' ) {
395             $name =~ s/^lcfg-//;
396             }
397              
398             my $pkgspec = LCFG::Build::PkgSpec->new(
399             name => $name,
400             version => '0.0.1',
401             release => '1',
402             author => [$author],
403             abstract => $self->abstract,
404             license => $self->license,
405             translate => ['*.cin'],
406             platforms => [@platforms],
407             );
408              
409             if ( $self->lcfg_component eq 'yes' ) {
410             $pkgspec->schema(1);
411             $pkgspec->base('lcfg');
412             $pkgspec->group('LCFG');
413             }
414              
415             my $localdir = $pkgspec->fullname;
416             if ( -e $localdir ) {
417             die "There is already a local directory or file named \"$localdir\".\n Please move it aside and try again\n";
418             }
419              
420             if ( $self->gencmake eq 'yes' ) {
421             $pkgspec->set_buildinfo( gencmake => 1 );
422             }
423             else { # not essential but good to be explicit here
424             $pkgspec->set_buildinfo( gencmake => 0 );
425             }
426              
427             # version control information
428              
429             $pkgspec->set_vcsinfo( logname => 'ChangeLog' );
430              
431             if ( $self->checkcommitted eq 'yes' ) {
432             $pkgspec->set_vcsinfo( checkcommitted => 1 );
433             }
434             if ( $self->genchangelog eq 'yes' ) {
435             $pkgspec->set_vcsinfo( genchangelog => 1 );
436             }
437              
438             my $tempdir = File::Temp::tempdir( CLEANUP => 1 );
439              
440             my $new_metafile = File::Spec->catfile( $tempdir, 'lcfg.yml' );
441             $pkgspec->metafile($new_metafile);
442             $pkgspec->save_metafile();
443              
444             print "Stored LCFG build metadata\n";
445              
446             my @include;
447             if ( -d $self->tmpldir ) {
448             push @include, $self->tmpldir;
449             }
450             push @include, $TMPLDIR;
451              
452             my $tt = Template->new(
453             { INCLUDE_PATH => \@include,
454             FILTERS => { to_bool => sub { $_ eq 'yes' ? 1 : 0 }, },
455             PRE_CHOMP => 1,
456             }
457             ) or die $Template::ERROR;
458              
459             # Key is the template filename
460             # Value is the target file path stored as a ref to a list of parts
461              
462             my %files = (
463             'specfile.tt' => ['specfile'],
464             'ChangeLog.tt' => ['ChangeLog'],
465             'README.BUILD.tt' => ['README.BUILD'],
466             'README.tt' => ['README'],
467             );
468              
469             # Create some essential directories if the project is perl based
470              
471             if ( $self->lang eq 'perl' ) {
472             my $libdir = File::Spec->catdir( $tempdir, 'lib' );
473             mkdir $libdir
474             or die "Could not Perl library directory, $libdir: $!\n";
475              
476             if ( $self->lcfg_component eq 'yes' ) {
477             my $compdir =
478             File::Spec->catdir( $tempdir, 'lib', 'LCFG', 'Component' );
479             eval { File::Path::make_path($compdir) };
480             if ($@) {
481             die "Failed to create LCFG component perl directory: $@\n";
482             }
483             } else {
484             $files{'README.perl.tt'} = [ 'lib', 'README' ];
485             }
486              
487             my $testdir = File::Spec->catdir( $tempdir, 't' );
488             mkdir $testdir
489             or die "Could not create tests directory, $testdir: $!\n";
490             }
491              
492             my @exefiles;
493              
494             if ( $self->lcfg_component eq 'yes' ) {
495             my $comp = $pkgspec->name;
496              
497             if ( $self->lang eq 'perl' ) {
498             $files{'COMPONENT.pl.tt'} = ["$comp.cin"];
499             $files{'COMPONENT.pm.tt'} = ['lib','LCFG','Component',"\u$comp.pm.cin"];
500             $files{'perlcomp_cmake.tt'} = ['CMakeLists.txt'];
501             }
502             else {
503             $files{'COMPONENT.sh.tt'} = ["$comp.cin"];
504             }
505             push @exefiles, "$comp.cin";
506              
507             $files{'COMPONENT.def.tt'} = ["$comp.def.cin"];
508             $files{'COMPONENT.pod.tt'} = ["$comp.pod.cin"];
509              
510             my $nagios_dir = File::Spec->catdir( $tempdir, 'nagios' );
511             mkdir $nagios_dir
512             or die "Could not create nagios directory, $nagios_dir: $!\n";
513              
514             $files{'README.nagios.tt'} = [ 'nagios', 'README' ];
515              
516             my $templates_dir = File::Spec->catdir( $tempdir, 'templates' );
517             mkdir $templates_dir
518             or die "Could not create templates directory, $templates_dir: $!\n";
519              
520             $files{'README.templates.tt'} = [ 'templates', 'README' ];
521             }
522              
523             for my $template ( keys %files ) {
524              
525             my @file = @{$files{$template}};
526             my $output = File::Spec->catfile( $tempdir, @file );
527              
528             print "Generating $output\n";
529             $tt->process(
530             $template,
531             { skel => $self,
532             pkgspec => $pkgspec,
533             },
534             $output
535             ) or warn $tt->error();
536             }
537              
538             for my $exe (@exefiles) {
539             my $path = File::Spec->catfile( $tempdir, $exe );
540             chmod 0755, $path;
541             }
542              
543             eval {
544             my $vcsmodule = 'LCFG::Build::VCS::' . $self->vcs;
545              
546             $vcsmodule->require or die $@;
547              
548             my $vcs = $vcsmodule->new(
549             quiet => 0,
550             dryrun => 0,
551             module => $pkgspec->fullname,
552             );
553              
554             $vcs->import_project( $tempdir, $pkgspec->version,
555             'Created with lcfg-skeleton' );
556              
557             $vcs->checkout_project();
558              
559             };
560              
561             if ($@) {
562             die "Failed to import project to your chosen version-control system:\n $@\n";
563             }
564              
565             print "Successfully imported your project into your version-control system.\n";
566              
567             return $pkgspec;
568             }
569              
570             1;
571             __END__
572              
573             =head1 NAME
574              
575             LCFG::Build::Skeleton - LCFG software package generator
576              
577             =head1 VERSION
578              
579             This documentation refers to LCFG::Build::Skeleton version 0.4.1
580              
581             =head1 SYNOPSIS
582              
583             my $skel = LCFG::Build::Skeleton->new_with_options();
584              
585             $skel->query_user();
586              
587             $skel->create_package();
588              
589             =head1 DESCRIPTION
590              
591             This module handles the creation of the skeleton of an LCFG software
592             project. Typically, it prompts the user to answer a set of standard
593             questions and then generates the necessary files from a set of
594             templates. These generated files include the necessary metadata, build
595             files and, for LCFG components, example code. It can also add the new
596             project into the revision-control system of choice.
597              
598             =head1 ATTRIBUTES
599              
600             If using the new_with_options() method then any of the attributes can
601             be set from the commandline (or, more precisely, via the @ARGV
602             list). An attribute named C<foo> is accessible as the commandline
603             option C<--foo>. If it is a boolean value then the module will also
604             support the C<--no-foo> form to turn off a feature
605             (e.g. --no-gencmake).
606              
607             =over 4
608              
609             =item name
610              
611             The name of the project. Note that in the case of an LCFG component
612             this should be C<foo> B<NOT> C<lcfg-foo>.
613              
614             =item abstract
615              
616             A short description of the project. If this is an LCFG component the
617             default value suggested to the user is "The LCFG $name component".
618              
619             =item author_name
620              
621             The name of the author (i.e you!). The default is the string stored in
622             the gecos field of the passwd entry.
623              
624             =item author_email
625              
626             The email address for the author. The default is built from the
627             current username and domain name.
628              
629             =item lcfg_component
630              
631             This controls whether the generated project is an LCFG component. This
632             is a yes/no answer and it defaults to "yes" (it is handled in the same
633             way as a boolean value on the command line).
634              
635             =item lang
636              
637             The language which will be used, this is either "perl" or
638             "shell". This only really has an affect if you are creating an LCFG
639             component.
640              
641             =item vcs
642              
643             Which revision-control system you intend to use for the
644             project. Currently only "CVS" and "None" are supported. You will need
645             the relevant LCFG::Build::VCS helper module installed for this to
646             work.
647              
648             =item platforms
649              
650             The comma-separate list of platforms which are supported by this code
651             (e.g. ScientificLinux5).
652              
653             =item license
654              
655             The license under which the source code can be distributed. This
656             defaults to "GPLv2".
657              
658             =item restart
659              
660             This controls whether, if this is an LCFG component, should it be
661             restarted after package upgrade if the component is already
662             running. This is a yes/no answer and it defaults to "yes" (it is
663             handled in the same way as a boolean value on the command line).
664              
665             =item gencmake
666              
667             This controls whether the LCFG CMake infrastructure will be used to
668             build the project. This is a yes/no answer and it defaults to "yes"
669             (it is handled in the same way as a boolean value on the command
670             line).
671              
672             =item genchangelog
673              
674             This controls whether or not to generate the project changelog from
675             the revision-control commit logs. This is a yes/no answer and it
676             defaults to "no" (it is handled in the same way as a boolean value on
677             the command line).
678              
679             =item checkcommitted
680              
681             This controls whether the revision-control tools should check that all
682             files are committed before making a new release. This is a yes/no
683             answer and it defaults to "yes" (it is handled in the same way as a
684             boolean value on the command line).
685              
686             =item interactive
687              
688             This controls whether the L<query_user()> method will actually interact with the user or just store the values taken from the defaults file and any commandline options. This is a boolean value which defaults to false (zero).
689              
690             =item force
691              
692             This controls whether an existing project directory will be removed if the name matches that required for the new skeleton project. This is a boolean value which defaults to false (zero).
693              
694             =item configfile
695              
696             This is the configuration file which is used to store the defaults
697             between calls to the lcfg-skeleton tool. Normally you should not need
698             to modify this and it defaults to C<~/.lcfg/skeleton/defaults.yml>.
699              
700             =item tmpldir
701              
702             This is the directory into which local versions of templates should be
703             placed. Normally you should not need to modify this and it defaults to
704             C<~/.lcfg/skeleton/templates/>. For reference, the standard templates
705             are normally stored in C</usr/share/lcfgbuild/templates>.
706              
707             =back
708              
709              
710             =head1 SUBROUTINES/METHODS
711              
712             =over 4
713              
714             =item new(%hash_of_options)
715              
716             Creates a new object of the LCFG::Build::Skeleton class. Values for
717             any attribute can be specified in the hash of options.
718              
719             =item new_with_options(%hash_of_options)
720              
721             Creates a new object of the LCFG::Build::Skeleton class and if any
722             attribute values were specified on the command line those will be set
723             in the returned instance. Values for any attribute can be specified in
724             the hash of options.
725              
726             =item query_user()
727              
728             This prompts the user to answer a set of standard questions (except
729             when the C<interactive> option is set to false) and stores the answer
730             in the object attributes. If an invalid value is given the user will
731             be prompted again. For convenience, the answers are also stored in a
732             file and used as the defaults in the next run of the command. The
733             default value is shown in the prompt between square-brackets and just
734             pressing return is enough to accept the default.
735              
736             =item create_package()
737              
738             This uses the skeleton object attribute values to generate the
739             skeleton tree of files for the new project. If a project directory of
740             the desired name already exists you will need to move it aside, choose
741             a different project name or set the C<force> attribute to true.
742              
743             =item store_answers()
744              
745             This is primarily intended for internal usage. It will store the
746             values of the answers given be the user (except the project name and
747             abstract) so that they can be used as defaults in future calls to the
748             lcfg-skeleton command. The default values are stored in the file name
749             specified in the C<configfile> attribute, that defaults to
750             C<~/lcfg/skeleton/defaults.yml>
751              
752             =item get_config_from_file($filename)
753              
754             This is primarily intended for internal usage. This retrieves the
755             configuration data from the specified file, which must be in YAML
756             format, and returns it as a reference to a hash.
757              
758             =back
759              
760             =head1 CONFIGURATION AND ENVIRONMENT
761              
762             The default values for the answers are stored in the file referred to
763             in the C<configfile> attribute. This is normally
764             C<~/.lcfg/skeleton/defaults.yml> but that can be overridden by the
765             user. If the file does not exist it will be created when this tool is
766             first run.
767              
768             It is possible to override any of the standard templates used to
769             generate the skeleton project by placing your own version into the
770             directory referred to in the C<tmpldir> attribute. This is normally
771             C<~/.lcfg/skeleton/templates/> but that can be overridden by the
772             user. For reference, the standard templates are normally stored in
773             C</usr/share/lcfgbuild/templates>.
774              
775             =head1 DEPENDENCIES
776              
777             This module is L<Moose> powered and uses L<MooseX::Getopt> to provide
778             a new_with_options() method for creating new instances from the
779             options specified in @ARGV (typically via the commandline). The
780             L<YAML::Syck> module is used to parse the file which holds the default
781             values for the answers. You will also need the L<List::MoreUtils> and
782             L<UNIVERSAL::require> modules.
783              
784             The Perl Template Toolkit is required to generate the files for the
785             skeleton project.
786              
787             The following LCFG Build Tools modules are also required:
788             L<LCFG::Build::PkgSpec>(3), L<LCFG::Build::VCS>(3) and VCS helper modules.
789              
790             =head1 SEE ALSO
791              
792             L<LCFG::Build::Tools>, lcfg-skeleton(1), lcfg-reltool(1)
793              
794             =head1 PLATFORMS
795              
796             This is the list of platforms on which we have tested this
797             software. We expect this software to work on any Unix-like platform
798             which is supported by Perl.
799              
800             ScientificLinux6, EnterpriseLinux7, MacOSX
801              
802             =head1 BUGS AND LIMITATIONS
803              
804             There are no known bugs in this application. Please report any
805             problems to bugs@lcfg.org, feedback and patches are also always very
806             welcome.
807              
808             =head1 AUTHOR
809              
810             Stephen Quinney <squinney@inf.ed.ac.uk>
811              
812             =head1 LICENSE AND COPYRIGHT
813              
814             Copyright (C) 2008 University of Edinburgh
815              
816             This library is free software; you can redistribute it and/or modify
817             it under the terms of the GPL, version 2 or later.
818              
819             =cut