1package Alien::SDL; 2use strict; 3use warnings; 4use Alien::SDL::ConfigData; 5use File::ShareDir qw(dist_dir); 6use File::Spec; 7use File::Find; 8use File::Spec::Functions qw(catdir catfile rel2abs); 9use File::Temp; 10use Capture::Tiny; 11use Config; 12 13=head1 NAME 14 15Alien::SDL - building, finding and using SDL binaries 16 17=head1 VERSION 18 19Version 1.446 20 21=cut 22 23our $VERSION = '1.446'; 24$VERSION = eval $VERSION; 25 26=head1 SYNOPSIS 27 28Alien::SDL tries (in given order) during its installation: 29 30=over 31 32=item * When given C<--with-sdl-config> option use specified sdl-config 33script to locate SDL libs. 34 35 perl Build.PL --with-sdl-config=/opt/sdl/bin/sdl-config 36 37or using default script name 'sdl-config' by running: 38 39 perl Build.PL --with-sdl-config 40 41B<IMPORTANT NOTE:> Using --with-sdl-config avoids considering any other 42build methods; no prompt with other available build options. 43 44=item * Locate an already installed SDL via 'sdl-config' script. 45 46=item * Check for SDL libs in directory specified by SDL_INST_DIR variable. 47In this case the module performs SDL library detection via 48'$SDL_INST_DIR/bin/sdl-config' script. 49 50 SDL_INST_DIR=/opt/sdl perl ./Build.PL 51 52=item * Download prebuilt SDL binaries (if available for your platform). 53 54=item * Build SDL binaries from source codes (if possible on your system). 55 56=back 57 58Later you can use Alien::SDL in your module that needs to link agains SDL 59and/or related libraries like this: 60 61 # Sample Makefile.pl 62 use ExtUtils::MakeMaker; 63 use Alien::SDL; 64 65 WriteMakefile( 66 NAME => 'Any::SDL::Module', 67 VERSION_FROM => 'lib/Any/SDL/Module.pm', 68 LIBS => Alien::SDL->config('libs', [-lAdd_Lib]), 69 INC => Alien::SDL->config('cflags'), 70 # + additional params 71 ); 72 73=head1 DESCRIPTION 74 75Please see L<Alien> for the manifesto of the Alien namespace. 76 77In short C<Alien::SDL> can be used to detect and get 78configuration settings from an installed SDL and related libraries. 79Based on your platform it offers the possibility to download and 80install prebuilt binaries or to build SDL & co. from source codes. 81 82The important facts: 83 84=over 85 86=item * The module does not modify in any way the already existing SDL 87installation on your system. 88 89=item * If you reinstall SDL libs on your system you do not need to 90reinstall Alien::SDL (providing that you use the same directory for 91the new installation). 92 93=item * The prebuild binaries and/or binaries built from sources are always 94installed into perl module's 'share' directory. 95 96=item * If you use prebuild binaries and/or binaries built from sources 97it happens that some of the dynamic libraries (*.so, *.dll) will not 98automaticly loadable as they will be stored somewhere under perl module's 99'share' directory. To handle this scenario Alien::SDL offers some special 100functionality (see below). 101 102=back 103 104=head1 METHODS 105 106=head2 config() 107 108This function is the main public interface to this module. Basic 109functionality works in a very similar maner to 'sdl-config' script: 110 111 Alien::SDL->config('prefix'); # gives the same string as 'sdl-config --prefix' 112 Alien::SDL->config('version'); # gives the same string as 'sdl-config --version' 113 Alien::SDL->config('libs'); # gives the same string as 'sdl-config --libs' 114 Alien::SDL->config('cflags'); # gives the same string as 'sdl-config --cflags' 115 116On top of that this function supports special parameters: 117 118 Alien::SDL->config('ld_shared_libs'); 119 120Returns a list of full paths to shared libraries (*.so, *.dll) that will be 121required for running the resulting binaries you have linked with SDL libs. 122 123 Alien::SDL->config('ld_paths'); 124 125Returns a list of full paths to directories with shared libraries (*.so, *.dll) 126that will be required for running the resulting binaries you have linked with 127SDL libs. 128 129 Alien::SDL->config('ld_shlib_map'); 130 131Returns a reference to hash of value pairs '<libnick>' => '<full_path_to_shlib'>, 132where '<libnick>' is shortname for SDL related library like: SDL, SDL_gfx, SDL_net, 133SDL_sound ... + some non-SDL shortnames e.g. smpeg, jpeg, png. 134 135NOTE: config('ld_<something>') return an empty list/hash if you have decided to 136use SDL libraries already installed on your system. This concerns 'sdl-config' 137detection and detection via '$SDL_INST_DIR/bin/sdl-config'. 138 139=head2 check_header() 140 141This function checks the availability of given header(s) when using compiler 142options provided by "Alien::SDL->config('cflags')". 143 144 Alien::SDL->check_header('SDL.h'); 145 Alien::SDL->check_header('SDL.h', 'SDL_net.h'); 146 147Returns 1 if all given headers are available, 0 otherwise. 148 149=head2 get_header_version() 150 151Tries to find a header file specified as a param in SDL prefix direcotry and 152based on "#define" macros inside this header file tries to get a version triplet. 153 154 Alien::SDL->get_header_version('SDL_mixer.h'); 155 Alien::SDL->get_header_version('SDL_version.h'); 156 Alien::SDL->get_header_version('SDL_gfxPrimitives.h'); 157 Alien::SDL->get_header_version('SDL_image.h'); 158 Alien::SDL->get_header_version('SDL_mixer.h'); 159 Alien::SDL->get_header_version('SDL_net.h'); 160 Alien::SDL->get_header_version('SDL_ttf.h'); 161 Alien::SDL->get_header_version('smpeg.h'); 162 163Returns string like '1.2.3' or undef if not able to find and parse version info. 164 165=head1 BUGS 166 167Please post issues and bugs at L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Alien-SDL> 168 169=head1 AUTHOR 170 171 Kartik Thakore 172 CPAN ID: KTHAKORE 173 Thakore.Kartik@gmail.com 174 http://yapgh.blogspot.com 175 176=head1 ACKNOWLEDGEMENTS 177 178 kmx - complete redesign between versions 0.7.x and 0.8.x 179 180=head1 COPYRIGHT 181 182This program is free software; you can redistribute 183it and/or modify it under the same terms as Perl itself. 184 185The full text of the license can be found in the 186LICENSE file included with this module. 187 188=cut 189 190### get config params 191sub config 192{ 193 my $package = shift; 194 my @params = @_; 195 return _sdl_config_via_script(@params) if(Alien::SDL::ConfigData->config('script')); 196 return _sdl_config_via_config_data(@params) if(Alien::SDL::ConfigData->config('config')); 197} 198 199### get version info from given header file 200sub get_header_version { 201 my ($package, $header) = @_; 202 return unless $header; 203 204 # try to find header 205 my $root = Alien::SDL->config('prefix'); 206 #warn 'Finding in '.$root.'/include'; 207 my $include = File::Spec->catfile($root, 'include'); 208 my @files; 209 find({ wanted => sub { push @files, rel2abs($_) if /\Q$header\E$/ }, follow => 1, no_chdir => 1, follow_skip => 2 }, $include); 210 return unless @files; 211 212 # get version info 213 open(DAT, $files[0]) || return; 214 my @raw=<DAT>; 215 close(DAT); 216 217 # generic magic how to get version major/minor/patchlevel 218 my ($v_maj) = grep(/^#define[ \t]+[A-Z_]+?MAJOR[A-Z_]*[ \t]+[0-9]+/, @raw); 219 $v_maj =~ s/^#define[ \t]+[A-Z_]+[ \t]+([0-9]+)[.\r\n]*$/$1/; 220 my ($v_min) = grep(/^#define[ \t]+[A-Z_]+MINOR[A-Z_]*[ \t]+[0-9]+/, @raw); 221 $v_min =~ s/^#define[ \t]+[A-Z_]+[ \t]+([0-9]+)[.\r\n]*$/$1/; 222 my ($v_pat) = grep(/^#define[ \t]+[A-Z_]+(PATCHLEVEL|MICRO|RELEASE)[A-Z_]*[ \t]+[0-9]+/, @raw); 223 $v_pat =~ s/^#define[ \t]+[A-Z_]+[ \t]+([0-9]+)[.\r\n]*$/$1/; 224 return if (($v_maj eq '')||($v_min eq '')||($v_pat eq '')); 225 return "$v_maj.$v_min.$v_pat"; 226} 227 228### check presence of header(s) specified as params 229sub check_header { 230 my ($package, @header) = @_; 231 print STDERR "[$package] Testing header(s): " . join(', ', @header); 232 233 require ExtUtils::CBuilder; # PAR packer workaround 234 235 my $config = {}; 236 if($^O eq 'cygwin') { 237 my $ccflags = $Config{ccflags}; 238 $ccflags =~ s/-fstack-protector//; 239 $config = { ld => 'gcc', cc => 'gcc', ccflags => $ccflags }; 240 } 241 242 my $cb = ExtUtils::CBuilder->new( quiet => 1, config => $config ); 243 my ($fs, $src) = File::Temp::tempfile('aaXXXX', SUFFIX => '.c', UNLINK => 1); 244 my $inc = ''; 245 my $i = 0; 246 foreach (@header) { 247 @header = (splice(@header, 0, $i) , 'stdio.h', splice(@header, $i)) if $_ eq 'jpeglib.h'; 248 $i++; 249 } 250 $inc .= "#include <$_>\n" for @header; 251 syswrite($fs, <<MARKER); # write test source code 252#if defined(_WIN32) && !defined(__CYGWIN__) 253/* GL/gl.h on Win32 requires windows.h being included before */ 254#include <windows.h> 255#endif 256$inc 257int demofunc(void) { return 0; } 258 259MARKER 260 close($fs); 261 my $obj; 262 my $stdout = ''; 263 my $stderr = ''; 264 ($stdout, $stderr) = Capture::Tiny::capture { 265 $obj = eval { $cb->compile( source => $src, extra_compiler_flags => Alien::SDL->config('cflags')); }; 266 }; 267 268 if($obj) { 269 print STDERR "\n"; 270 unlink $obj; 271 return 1; 272 } 273 else { 274 if( $stderr ) { 275 $stderr =~ s/[\r\n]$//; 276 $stderr =~ s/^\Q$src\E[\d\s:]*//; 277 print STDERR " NOK: ($stderr)\n"; 278 } 279 # on Windows (MSVC) stdout is set, but not stderr 280 else { 281 $stdout =~ s/[\r\n]$//; 282 $stdout =~ s/.+[\r\n]//; 283 $stdout =~ s/^\Q$src\E[\(\)\d\s:]*//; 284 print STDERR " NOK: ($stdout)\n"; 285 } 286 287 return 0; 288 } 289} 290 291### internal functions 292sub _sdl_config_via_script 293{ 294 my $param = shift; 295 my @add_libs = @_; 296 my $devnull = File::Spec->devnull(); 297 my $script = Alien::SDL::ConfigData->config('script'); 298 return unless ($script && ($param =~ /[a-z0-9_]*/i)); 299 my $val = `$script --$param 2>$devnull`; 300 $val =~ s/[\r\n]*$//; 301 if($param eq 'cflags') { 302 $val .= ' ' . Alien::SDL::ConfigData->config('additional_cflags'); 303 } 304 elsif($param eq 'libs') { 305 $val .= ' ' . join(' ', @add_libs) if scalar @add_libs; 306 $val .= ' ' . Alien::SDL::ConfigData->config('additional_libs'); 307 } 308 elsif($param =~ /^(ld_shlib_map|ld_shared_libs|ld_paths)$/) { 309 $val = Alien::SDL::ConfigData->config('config')->{$param}; 310 } 311 return $val; 312} 313 314sub _sdl_config_via_config_data 315{ 316 my $param = shift; 317 my @add_libs = @_; 318 my $share_dir = dist_dir('Alien-SDL'); 319 my $subdir = Alien::SDL::ConfigData->config('share_subdir'); 320 return unless $subdir; 321 my $real_prefix = catdir($share_dir, $subdir); 322 return unless ($param =~ /[a-z0-9_]*/i); 323 my $val = Alien::SDL::ConfigData->config('config')->{$param}; 324 return unless $val; 325 # handle additional flags 326 if($param eq 'cflags') { 327 $val .= ' ' . Alien::SDL::ConfigData->config('additional_cflags'); 328 } 329 elsif($param eq 'libs') { 330 $val .= ' ' . join(' ', @add_libs) if scalar @add_libs; 331 $val .= ' ' . Alien::SDL::ConfigData->config('additional_libs'); 332 } 333 # handle @PrEfIx@ replacement 334 if ($param =~ /^(ld_shared_libs|ld_paths)$/) { 335 s/\@PrEfIx\@/$real_prefix/g foreach (@{$val}); 336 } 337 elsif ($param =~ /^(ld_shlib_map)$/) { 338 while (my ($k, $v) = each %$val ) { 339 $val->{$k} =~ s/\@PrEfIx\@/$real_prefix/g; 340 } 341 } 342 else { 343 $val =~ s/\@PrEfIx\@/$real_prefix/g; 344 } 345 return $val; 346} 347 3481; 349