PageRenderTime 26ms CodeModel.GetById 1ms app.highlight 21ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/Module/Build/SDL.pm

http://github.com/PerlGameDev/SDL
Perl | 351 lines | 251 code | 89 blank | 11 comment | 31 complexity | a73bb814e70498e5b07172e79462fa40 MD5 | raw file
  1package Module::Build::SDL;
  2use strict;
  3use warnings;
  4use base 'Module::Build';
  5
  6__PACKAGE__->add_property(parinput  => '');
  7__PACKAGE__->add_property(paroutput => '');
  8__PACKAGE__->add_property(parlibs   => [ qw/SDL SDL-1.2 SDLmain/ ]);
  9__PACKAGE__->add_property(parmods   => []);
 10
 11use File::Spec;
 12use File::Find qw[finddepth];
 13use Archive::Zip qw( :ERROR_CODES :CONSTANTS );
 14use Alien::SDL;
 15
 16sub new {
 17  my $self = shift;
 18  my %args = @_;  
 19  $args{share_dir} ||= 'data'; #set default sharedir name 'data' instead of 'share'
 20  $self->SUPER::new(%args);
 21}
 22
 23sub ACTION_par {
 24  my ($self) = @_;
 25  $self->depends_on('code');
 26  $self->depends_on('installdeps');
 27  
 28  #checking if we have the pp installed
 29  die 'Need PAR::Packer' if !( eval ' use PAR::Packer; 1' );
 30
 31  #here comes the code from https://github.com/PerlGameDev/SDL-Par-Packager/blob/master/SDLpp.pl
 32  my $output = $self->paroutput || (($^O eq 'MSWin32') ? 'a.exe' : 'a');
 33  my $input = $self->parinput;
 34  my @sdl_libs = @{$self->parlibs};  
 35  my $extra = join ' ', (map {"-M$_"} @{$self->parmods}); # = '-MModname::A -MModname::B ...'
 36  my $Include = './lib';
 37
 38  die 'parinput needs to be specified' unless $input;
 39
 40  print "BUILDING PAR \n";
 41  my $exclude_modules = '-X Alien::SDL::ConfigData -X SDL::ConfigData';
 42  my $include_modules = '-M ExtUtils::CBuilder::Base -M Data::Dumper -M SDL -M Alien::SDL';
 43  $include_modules .= " $extra" if $extra;
 44
 45  my $out_par = $output . '.par';
 46  my $par_cmd = "pp -B $exclude_modules $include_modules";
 47  $par_cmd .= " -I $Include" if $Include;
 48  $par_cmd .= " -p -o $out_par $input";
 49  print "\t $par_cmd \n";
 50  `$par_cmd` if !-e $out_par;
 51
 52  print "PAR: $out_par\n" if -e $out_par;
 53
 54  print "SEARCHING FOR ConfigData files \n";
 55  my $lib;
 56  my $AS_path;
 57  my $SD_path;
 58
 59  finddepth( sub {
 60               my ($f, $d) = ($File::Find::name, $File::Find::dir);
 61  	       if ( $_ =~ /ConfigData/ ) {
 62  		  $AS_path = $f if $f =~ 'Alien/SDL/ConfigData.pm';
 63  		  $SD_path = $f if $f =~ 'SDL/ConfigData.pm' && $f !~ 'Alien/SDL/ConfigData.pm';
 64  		  $lib = $d if ( $AS_path && $SD_path );
 65  	       }
 66             }, @INC );
 67
 68  die "Cannot find lib/SDL/ConfigData.pm or lib/Alien/SDL/ConfigData.pm \n" if ( !$AS_path || !$SD_path );
 69
 70  print "Found ConfigData files in $lib \n";
 71
 72  print "READING PAR FILE \n";
 73
 74  my $par_file = Archive::Zip->new();
 75  unless ( $par_file->read($out_par) == AZ_OK ) {
 76    die 'read error on ' . $out_par;
 77  }
 78
 79  $par_file->addFile( $AS_path, 'lib/Alien/SDL/ConfigData.pm' );
 80  $par_file->addFile( $SD_path, 'lib/SDL/ConfigData.pm' );
 81
 82  my $share = Alien::SDL::ConfigData->config('share_subdir');
 83
 84  my @shares = $par_file->membersMatching($share);
 85
 86  my $alien_sdl_auto = $shares[0]->fileName;
 87
 88  $alien_sdl_auto =~ s/$share(\S+)// if $alien_sdl_auto;
 89
 90  my @auto_folder = $par_file->membersMatching("$alien_sdl_auto(?!$share)");
 91
 92  my @sdl_not_runtime = $par_file->membersMatching( $share . '/include' ); #TODO remove extra fluff in share_dri
 93  push @sdl_not_runtime, @auto_folder;                                     #remove non share dir stuff
 94  push @sdl_not_runtime, $par_file->membersMatching( $share . '/etc' );
 95  push @sdl_not_runtime, $par_file->membersMatching( $share . '/share' );
 96  push @sdl_not_runtime, $par_file->membersMatching( $share . '/lib' ) if $^O eq 'MSWin32';
 97
 98  my @non              = ();
 99  my @sdl_libs_to_keep = ();
100
101  foreach (@sdl_libs) {
102    if ( $^O eq 'MSWin32' ) {
103      @non = $par_file->membersMatching( $share . "/bin(\\S+)" );
104      #push @sdl_not_runtime ,$par_file->membersMatching( $share."/bin(\\S+)(?!$_)" )
105    }
106    else {
107      @non = $par_file->membersMatching( $share . "/lib(\\S+)" );
108    }
109
110    print "Removing non $_ shared objs \n";
111    my $lib_look = 'lib' . $_;
112    map {
113      my $n = $_->fileName;
114      if ( $n =~ /$lib_look\.(so|a|dll|dylib)/ ) {
115        push( @sdl_libs_to_keep, $_ );
116      }
117    } @non;
118  }
119
120  print "found $#sdl_libs_to_keep sdl libs to keep \n";
121  my $regex_search = ']';
122  map {
123    print "\t " . $_->fileName . "\n";
124    $regex_search .= ']' . $_->fileName
125  } @sdl_libs_to_keep;
126
127  $regex_search =~ s/\]\]//g;
128  $regex_search =~ s/\]/\|/g;
129
130  $regex_search = '(' . $regex_search . ')';
131
132  map {
133    my $n    = $_->fileName;
134    my $star = ' ';
135
136    if ( $n !~ $regex_search ) {
137      push @sdl_not_runtime, $_;
138    }
139  } @non;
140
141  push @sdl_not_runtime, $par_file->membersMatching( $share . '/bin' )
142  	unless $^O eq 'MSWin32';
143  print "REMOVING NON RUNTIME $#sdl_not_runtime files from  \n";
144  open( my $FH, '>', 'DeleteRecords.txt' ) or die $!;
145  foreach (@sdl_not_runtime) {
146    if ( $_->fileName eq $alien_sdl_auto . $share ) {
147      print $FH "Not deleting " . $_->fileName . " \n";
148    }
149    else {
150      $par_file->removeMember($_);
151      print $FH $_->fileName . "\n";
152    }
153  }
154  close $FH;
155
156  my @config_members = $par_file->membersMatching('ConfigData.pm');
157
158  foreach (@config_members) {
159    $_->desiredCompressionLevel(1);
160    $_->unixFileAttributes(0644);
161  }
162
163  unlink $out_par . '2';
164  unless ( $par_file->writeToFileNamed( $out_par . '2' ) == AZ_OK ) {
165    die 'write error';
166  }
167
168  $par_cmd = "pp -o $output " . $out_par . "2";
169
170  `$par_cmd`;
171
172  print "MADE $output \n" if -e $output;
173  unlink $out_par . '2';
174  unlink $out_par; 
175}
176
177sub ACTION_run {
178  my ($self) = @_;
179  $self->depends_on('code');
180  $self->depends_on('installdeps');
181  my $bd = $self->{properties}->{base_dir};
182
183  # prepare INC
184  local @INC = @INC;
185  local @ARGV = @{$self->args->{ARGV}};
186  my $script = shift @ARGV;
187  unshift @INC, (File::Spec->catdir($bd, $self->blib, 'lib'), File::Spec->catdir($bd, $self->blib, 'arch'));
188
189  if ($script) {
190    # scenario: ./Build run bin/scriptname param1 param2
191    do($script);
192  }
193  else {
194    # scenario: ./Build run
195    my ($first_script) = ( glob('bin/*') ); # take the first script in bin subdir
196    print STDERR "No params given to run action - gonna start: '$first_script'\n";
197    do($first_script);
198  }
199}
200
201# TODO: later move app skeleton generation into SDL::Devel (or something like this)
202sub generate_sdl_module {
203  my ($path, $name) = @_;
204
205  #Make the path and directory stuff
206  mkdir $path or
207  Carp::croak "Cannot make a SDL based module at $path : $!";
208
209  mkdir "$path/lib";
210  mkdir "$path/bin";
211  mkdir "$path/data";
212
213  open my $FH, ">>$path/bin/sdl_app.pl";
214
215  print $FH "use string;\nuse warnings;\nuse SDL;\n";
216
217  close $FH;
218
219  open  $FH, ">>$path/Build.PL";
220
221  print $FH 
222  "use strict;\nuse warnings;\nuse Module::Build::SDL;
223  my \$builder = Module::Build::SDL->new(
224    module_name => '$name',
225    dist_version => '1.01',
226    dist_abstract => 'Put something in here',
227    dist_author => 'developer <developer\@example.com>',
228    license => 'perl',
229  )->create_build_script();
230  ";
231}
232
2331;
234
235__END__
236
237=head1 NAME
238
239Module::Build::SDL - Module::Build subclass for building SDL apps/games [not stable yet]
240
241=head1 SYNOPSIS
242
243When creating a new SDL application/game you can create Build.PL like this:
244
245 use Module::Build::SDL;
246   
247 my $builder = Module::Build::SDL->new(
248     module_name   => 'Games::Demo',
249     dist_version  => '1.00',
250     dist_abstract => 'Demo game based on Module::Build::SDL',
251     dist_author   => 'coder@cpan.org',
252     license       => 'perl',
253     requires      => {
254         'SDL'     => 0,
255     },
256     #+ others Module::Build options
257 )->create_build_script();
258
259Once you have created a SDL application/game via Module::Build::SDL as described
260above you can use some extra build targets/actions:
261
262=over
263
264=item * you can create a PAR distribution like:
265
266 $ perl ./Build.PL
267 $ ./Build
268 $ ./Build par
269
270There are some extra parameters related to 'par' action you can pass to Module::Build::SDL->new():
271
272 parinput  => 'bin/scriptname.pl'
273 paroutput => 'filename.par.exe',
274 parlibs   => [ qw/SDL SDL_main SDL_gfx/ ],  #external libraries (.so/.dll) to be included into PAR
275 parmods   => [ qw/Module::A Module::B/ ],   #extra modules to be included into PAR
276
277=item * to run the game from distribution directory you can use:
278
279 $ perl ./Build.PL
280 $ ./Build
281 $ ./Build run
282
283=item * TODO: maybe some additional actions: parexe, parmsi, deb, rpm
284
285=back
286
287=head1 DESCRIPTION
288
289Module::Build::SDL is a subclass of L<Module::Build|Module::Build> created
290to make easy some tasks specific to SDL applications - e.g. packaging SDL
291application/game into PAR archive.
292
293=head1 APPLICATION/GAME LAYOUT
294
295Module::Build::SDL expects the following layout in project directory:
296
297 #example: game with the main *.pl script + data files + modules (*.pm)
298 Build.PL
299 lib/
300     Games/
301           Demo.pm
302 bin/
303     game-script.pl
304 data/
305     whatever_data_files_you_need.jpg
306
307the most simple game should look like:
308
309 #example: simple one-script apllication/game
310 Build.PL
311 bin/
312    game-script.pl
313
314In short - there are 3 expected subdirectories:
315
316=over
317
318=item * B<bin> - one or more perl scripts (*.pl) to start the actual
319application/game
320
321=item * B<lib> - application/game specific modules (*.pm) organized
322in dir structure in "usual perl manners"
323
324=item * B<data> - directory for storing application data (pictures, 
325sounds etc.). This subdirectory is handled as a "ShareDir"
326(see L<File::ShareDir|File::ShareDir> for more details)
327
328=item * As the project is (or could be) composed as a standard perl
329distribution it also support standard subdirectory B<'t'> (with tests).
330
331=back
332
333=head1 RULES TO FOLLOW
334
335When creating a SDL application/game based on Module::Build::SDL it is
336recommended to follow these rules:
337
338=over
339
340=item * Use the name for your game from I<Games::*> namespace; it will make
341the later release to CPAN much easier.
342
343=item * Put all data files into B<data> subdirectory and access the B<data>
344subdir only via L<File::ShareDir|File::ShareDir> 
345(namely by calling L<distdir()|File::ShareDir/dist_dir> function)
346
347=item * TODO: maybe add more
348
349=back
350
351=cut