File Coverage

blib/lib/PDL/Core/Dev.pm
Criterion Covered Total %
statement 59 278 21.2
branch 22 138 15.9
condition 8 76 10.5
subroutine 16 33 48.4
pod 4 22 18.1
total 109 547 19.9


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             PDL::Core::Dev - PDL development module
4              
5             =head1 SYNOPSIS
6              
7             use PDL::Core::Dev;
8              
9             =head1 DESCRIPTION
10              
11             This module encapsulates most of the stuff useful for
12             PDL development and is often used from within Makefile.PL's.
13              
14             =head1 BUILD MODES
15              
16             =head2 Old Skool
17              
18             The original scheme, which still works, was one PDL module per
19             L (EUMM) "module", i.e. directory. There has
20             been work with Module::Build, but as that is now deprecated, it
21             will not be further discussed. That module would generate a C<.xs>
22             and C<.pm> file in its directory, according to its F,
23             such as linking extra objects, or other libraries.
24              
25             To have several distinct PDL modules in a CPAN
26             distribution, you made several directories, each with a F.
27             Parallel building was not normally possible between modules, though
28             see PDL's top-level F as of about 2015, which gained a
29             C target that did build in parallel despite subdirectories
30             and "recursive make". Any change to any C code caused a rebuild of
31             the whole C<.xs> file (for Slices this was upwards of 40,000 lines),
32             and being one compilation unit, it could not be parallelised.
33              
34             =head2 Multi-C files
35              
36             As of 2.058, a new "multi-C" mode was added for all internal PDL
37             modules, and external ones that opted in (by adding a 5th, true,
38             element to the array-ref describing the package). This creates one
39             C file per operation, so parallel building is possible. This makes
40             for quicker builds, both for those installing the package, and those
41             developing it.
42              
43             It can also avoid unnecessary rebuilds if only one operation got
44             changed - it only recompiles that C file.
45             This is possible due to some trickiness by L,
46             which detects if each C file that it I output is the same
47             as the one already on disk, first removing any C<#line> directives so
48             that line renumbering will not force a rebuild, and not writing
49             anything unless a real change happened.
50              
51             It is opt-in because if the module adds C functions with C
52             without scoping them appropriately, they get incorporated in each
53             generated C file, which causes linking problems. Moving those to
54             separate C files solves that.
55              
56             But parallel building (without the "cleverness" of the C
57             work) is only possible within each module.
58              
59             =head2 Deep mode
60              
61             EUMM pure-Perl distributions in the modern era have
62             a F directory, whose structure matches the hierarchy of modules.
63             Until 2.096, PDL used this in its F subdirectory, so there was e.g.
64             F under that. As of EUMM 7.12 (shipped with Perl
65             5.26), it's also possible
66             to put C<.xs> files next to their respective C<.pm> files, by giving
67             a true value for C.
68              
69             As of 2.096, another new mode was added, which automatically engages
70             "multi-C" mode. This allows you to place your C<.pd> file under a
71             F directory, whose location tells the build system its package
72             name, which means the previous schemes' need to communicate that
73             in each F is no more. Now, the configuration is
74             communicated by each C<.pd> file by setting values in a hash, e.g.:
75              
76             { no warnings 'once'; # pass info back to Makefile.PL
77             $PDL::Core::Dev::EXTRAS{$::PDLMOD}{OBJECT} .= join '', map " $::PDLBASE/$_\$(OBJ_EXT)", qw(fftn);
78             $PDL::Core::Dev::EXTRAS{$::PDLMOD}{DEFINE} .= qq{ -DFFT_FLOAT -DFFT_DOUBLE -DFFT_LDOUBLE};
79             $PDL::Core::Dev::EXTRAS{$::PDLMOD}{INC} .= qq{ "-I$::PDLBASE"};
80             }
81              
82             This works because PDL needs to make an entry in the Makefile for
83             each operation defined, which it does by loading the C<.pd> file,
84             and making its version of C just record the operation name.
85             As a side effect, the setting of the C value can be seen
86             by the build process.
87              
88             To have the only F work in this new scheme, converting
89             it from the previous one(s), you need to add this key to the
90             C call:
91              
92             VERSION_FROM => 'lib/PDL/GSL/CDF.pd',
93              
94             Note that the ones supplied by C are added for you. You I
95             need to provide overrides for C as before, and also C:
96              
97             {
98             my @pd_srcs;
99             package MY; # so that "SUPER" works right
100             sub init_PM {
101             my ($self) = @_;
102             $self->SUPER::init_PM;
103             @pd_srcs = ::pdlpp_eumm_update_deep($self);
104             }
105             sub postamble { ::pdlpp_postamble(@pd_srcs) }
106             }
107              
108             =head1 FUNCTIONS
109              
110             =cut
111              
112             # Stuff used in development/install environment of PDL Makefile.PL's
113             # - not part of PDL itself.
114              
115             package PDL::Core::Dev;
116              
117 10     10   642940 use strict;
  10         22  
  10         510  
118 10     10   67 use warnings;
  10         35  
  10         754  
119 10     10   61 use File::Path;
  10         21  
  10         958  
120 10     10   63 use File::Basename;
  10         92  
  10         1018  
121             require Exporter;
122 10     10   84 use Config;
  10         25  
  10         473  
123 10     10   5386 use File::Spec::Functions;
  10         9297  
  10         4512  
124             eval { require Devel::CheckLib };
125              
126             our @ISA = qw( Exporter );
127              
128             our @EXPORT = qw( isbigendian
129             PDL_INCLUDE PDL_TYPEMAP
130             PDL_AUTO_INCLUDE PDL_BOOT
131             PDL_INST_INCLUDE PDL_INST_TYPEMAP
132             pdlpp_eumm_update_deep
133             pdlpp_postamble_int pdlpp_stdargs_int
134             pdlpp_postamble pdlpp_stdargs write_dummy_make
135             unsupported trylink get_maths_libs
136             pdlpp_mkgen
137             got_complex_version
138             );
139              
140             # Installation locations
141 6     6 0 29 sub PDL_INCLUDE { '"-I'.catdir(whereami_any(), 'Core').'"' }
142 8     8 0 39 sub PDL_TYPEMAP { catfile(whereami_any(), qw(Core typemap)) }
143              
144             # The INST are here still just in case we want to change something later.
145             *PDL_INST_INCLUDE = \&PDL_INCLUDE;
146             *PDL_INST_TYPEMAP = \&PDL_TYPEMAP;
147              
148             sub PDL_AUTO_INCLUDE {
149 1     1 0 4 my ($symname) = @_;
150 1   50     5 $symname ||= 'PDL';
151 1         4 return << "EOR";
152             #include
153             static Core* $symname; /* Structure holds core C functions */
154             EOR
155             }
156              
157             sub PDL_BOOT {
158 2     2 0 6 my ($symname, $module) = @_;
159 2   50     6 $symname ||= 'PDL';
160 2   50     4 $module ||= 'The code';
161 2         25 return << "EOR";
162             perl_require_pv ("PDL/Core.pm"); /* make sure PDL::Core is loaded */
163             #ifndef aTHX_
164             #define aTHX_
165             #endif
166             if (SvTRUE (ERRSV)) Perl_croak(aTHX_ "%s",SvPV_nolen (ERRSV));
167             SV* CoreSV = perl_get_sv("PDL::SHARE",FALSE); /* var with core structure */
168             if (!CoreSV)
169             Perl_croak(aTHX_ "We require the PDL::Core module, which was not found");
170             if (!($symname = INT2PTR(Core*,SvIV( CoreSV )))) /* Core* value */
171             Perl_croak(aTHX_ "Got NULL pointer for $symname");
172             if ($symname->Version != PDL_CORE_VERSION)
173             Perl_croak(aTHX_ "[$symname->Version: \%ld PDL_CORE_VERSION: \%ld XS_VERSION: \%s] $module needs to be recompiled against the newly installed PDL", (long int)$symname->Version, (long int)PDL_CORE_VERSION, XS_VERSION);
174             EOR
175             }
176              
177 10     10   83 use Cwd qw/abs_path/;
  10         24  
  10         61260  
178             my $MY_FILE = abs_path(__FILE__); # capture at load-time because EUMM chdirs
179             my $MY_DIR2 = dirname(dirname($MY_FILE));
180 14     14 0 245 sub whereami_any { $MY_DIR2 } # something containing "Core/Dev.pm"
181              
182             =head2 isbigendian
183              
184             =for ref
185              
186             Is the machine big or little endian?
187              
188             =for example
189              
190             print "Your machins is big endian.\n" if isbigendian();
191              
192             returns 1 if the machine is big endian, 0 if little endian,
193             or dies if neither. It uses the C element of
194             perl's C<%Config> array.
195              
196             =for usage
197              
198             my $retval = isbigendian();
199              
200             =cut
201              
202             # ' emacs parsing dummy
203              
204             # big/little endian?
205             sub isbigendian {
206             my $byteorder = $Config{byteorder} ||
207 0   0 0 1 0 die "ERROR: Unable to find 'byteorder' in perl's Config\n";
208 0 0       0 return 1 if $byteorder eq "4321";
209 0 0       0 return 1 if $byteorder eq "87654321";
210 0 0       0 return 0 if $byteorder eq "1234";
211 0 0       0 return 0 if $byteorder eq "12345678";
212 0         0 die "ERROR: PDL does not understand your machine's byteorder ($byteorder)\n";
213             }
214              
215             sub _oneliner {
216 0     0   0 my ($cmd, @flags) = @_;
217 0         0 require ExtUtils::MM;
218 0         0 my $MM = bless { NAME => 'Fake' }, 'MM';
219 0         0 $MM->oneliner($cmd, \@flags);
220             }
221              
222             # Expects list in format:
223             # [gtest.pd, GTest, PDL::GTest[, PDL::XSPkg] ], [...]
224             # source, prefix,module/package, optional pp_addxs destination
225             # The idea is to support in future several packages in same dir - EUMM
226             # 7.06 supports
227              
228             my %flist_cache;
229             sub _pp_call_arg {
230 0     0   0 "-MPDL::PP=".join ',', @_
231             }
232             sub _postamble {
233 0     0   0 my ($w, $internal, $src, $base, $mod, $callpack, $multi_c, $deep) = @_;
234 0   0     0 $callpack //= '';
235 0         0 $w = dirname($w);
236 0         0 my $perlrun = "\$(PERLRUN) \"-I$w\"";
237 0         0 my ($pmdep, $install, $cdep) = ($src, '', '');
238 0 0 0     0 my ($ppc, $ppo) = ($multi_c && $flist_cache{File::Spec::Functions::rel2abs($src)})
239             ? map "\$($_)", pdlpp_mod_vars($mod)
240             : pdlpp_mod_values($internal, $src, $base, $multi_c);
241 0 0       0 if ($internal) {
242 0         0 my $ppdir = File::Spec::Functions::abs2rel(catdir($w, qw(PDL)));
243 0         0 $pmdep .= join ' ', '', catfile($ppdir, 'PP.pm'), glob(catfile($ppdir, 'PP/*'));
244 0         0 $cdep .= join ' ', $ppo, ':', map catfile($ppdir, qw(Core), $_),
245             qw(pdl.h pdlcore.h pdlbroadcast.h pdlmagic.h);
246             } else {
247 0         0 my $oneliner = _oneliner(qq{exit if \$ENV{DESTDIR}; use PDL::Doc; eval { PDL::Doc::add_module(q{$mod}); }});
248 0         0 $install = qq|\ninstall ::\n\t\@echo "Updating PDL documentation database...";\n\t$oneliner\n|;
249             }
250 0   0     0 my $pp_call_arg = _pp_call_arg($mod, $mod, $base, $callpack, $multi_c||'',$deep||'');
      0        
251 0         0 qq|
252              
253             $base.pm : $pmdep
254             $perlrun \"$pp_call_arg\" $src
255             \$(TOUCH) $base.pm
256              
257             $ppc : $base.pm
258             \$(NOECHO) \$(NOOP)
259              
260             $cdep
261             $install|
262             }
263              
264             sub pdlpp_postamble_int {
265 0     0 0 0 my $w = whereami_any();
266 0         0 join '', map _postamble($w, 1, @$_[0..3], 1, @$_[5..$#$_]), @_;
267             }
268              
269             # This is the function to be used outside the PDL tree.
270             # same format as pdlpp_postamble_int
271             sub pdlpp_postamble {
272 0     0 0 0 my $w = whereami_any();
273 0         0 join '', map _postamble($w, 0, @$_), @_;
274             }
275              
276             our %EXTRAS;
277             sub pdlpp_eumm_update_deep {
278 0     0 0 0 my ($eumm) = @_;
279 0         0 my $pm = $eumm->{PM};
280 0         0 delete @$pm{grep /(?:\.(?:c|xs|bs)|\Q$Config{obj_ext}\E)$/, keys %$pm};
281 0   0     0 my $macro = $eumm->{macro} ||= {};
282 0   0     0 my $xsb = $eumm->{XSBUILD}{xs} ||= {};
283 0   0     0 $eumm->{clean}{FILES} ||= '';
284 0   0     0 $eumm->{OBJECT} ||= '';
285 0   0     0 $eumm->{INC} ||= '';
286 0         0 my $pdl_inc = PDL_INCLUDE();
287 0 0       0 $eumm->{INC} .= ' '.$pdl_inc if index($eumm->{INC}, $pdl_inc) == -1;
288 0   0     0 my $tms = $eumm->{TYPEMAPS} ||= [];
289 0         0 my $pdl_tm = PDL_TYPEMAP();
290 0 0       0 push @$tms, $pdl_tm if !grep $_ eq $pdl_tm, @$tms;
291 0   0     0 $eumm->{XSMULTI} ||= 1;
292 0   0     0 $eumm->{dist}{PREOP} ||= '$(PERLRUNINST) -MPDL::Core::Dev -e pdlpp_mkgen $(DISTVNAME)';
293 0   0     0 my $xs = $eumm->{XS} ||= {};
294 0         0 my $global_version = $eumm->parse_version($eumm->{VERSION_FROM});
295 0         0 my @pd_srcs;
296 0         0 for my $f (grep /\.pd$/, keys %$pm) {
297 0         0 delete $pm->{$f};
298 0         0 my $nolib = (my $base = $f =~ s/\.pd$//r) =~ s#^lib/##r;
299 0         0 $xs->{ "$base.xs" } = "$base.c";
300 0         0 my $pmfile = "$base.pm";
301 0         0 $pm->{$pmfile} = "\$(INST_LIB)/$nolib.pm";
302 0         0 my @macro_vars = pdlpp_mod_vars(my $mod = join '::', split /\//, $nolib);
303 0         0 @$macro{@macro_vars} = pdlpp_mod_values(1, $f, $base, 1, 1);
304 0         0 $eumm->{OBJECT} .= " $base\$(OBJ_EXT)";
305 0         0 $xsb->{$base}{OBJECT} = "\$($macro_vars[1])";
306 0 0       0 $xsb->{$base}{OBJECT} .= $EXTRAS{$f}{OBJECT} if $EXTRAS{$f}{OBJECT};
307 0 0       0 $eumm->{DEFINE} .= $EXTRAS{$f}{DEFINE} if $EXTRAS{$f}{DEFINE}; # global
308 0 0       0 $eumm->{INC} .= " $EXTRAS{$f}{INC}" if $EXTRAS{$f}{INC}; # global
309 0   0     0 my $mtime = (stat $f)[9] // die "$f: $!";
310 0 0       0 open my $fh, ">", $pmfile or die "$pmfile: $!"; # XSMULTI needs this
311 0         0 print $fh "package $mod;\nour \$VER"."SION = '$global_version';\n1;\n"; # break is so cpanm doesn't try to parse as version
312 0         0 close $fh;
313 0         0 utime $mtime - 120, $mtime - 120, $pmfile; # so is out of date
314 0         0 push @pd_srcs, [$f, $base, $mod, '', 1, 1];
315 0         0 my $clean_extra = join ' ', '', $pmfile, map "\$($_)", @macro_vars;
316 0 0       0 $clean_extra .= $EXTRAS{$f}{OBJECT} if $EXTRAS{$f}{OBJECT};
317 0 0       0 if (ref $eumm->{clean}{FILES}) {
318 0         0 push @{$eumm->{clean}{FILES}}, $clean_extra;
  0         0  
319             } else {
320 0         0 $eumm->{clean}{FILES} .= $clean_extra;
321             }
322             }
323 0         0 delete @$pm{grep /\.c$/, keys %$pm};
324 0         0 @pd_srcs;
325             }
326              
327             sub pdlpp_list_functions {
328 0     0 0 0 my ($src, $internal, $base) = @_;
329 0         0 my $abs_src = File::Spec::Functions::rel2abs($src);
330 0 0       0 if (!$flist_cache{$abs_src}) {
331 0         0 my $w = whereami_any();
332 0 0       0 if (!$INC{'PDL/Types.pm'}) {
333 0         0 my $typespm = catfile($w, 'Types.pm');
334 0         0 require $typespm;
335 0         0 $INC{'PDL/Types.pm'} = 1;
336             }
337 0         0 require ''.catfile($w, qw(PP.pm));
338 0         0 $::PDLBASE = $base;
339 0         0 $flist_cache{$abs_src} = [ PDL::PP::list_functions($src) ];
340             }
341 0         0 @{ $flist_cache{$abs_src} };
  0         0  
342             }
343              
344             sub pdlpp_mod_vars {
345 0     0 0 0 my @parts = split /::/, $_[0];
346 0 0       0 shift @parts if $parts[0] eq 'PDL';
347 0         0 my $mangled = join '_', @parts;
348 0         0 map "PDL_MULTIC_${mangled}_$_", qw(C O);
349             }
350             sub pdlpp_mod_values {
351 0     0 0 0 my ($internal, $src, $base, $multi_c, $deep) = @_;
352 0 0       0 return ("$base.xs", "$base\$(OBJ_EXT)") if !$multi_c;
353 0 0       0 my $cfileprefix = $deep ? "$base-" : '';
354 0         0 my @cbase = map $cfileprefix."pp-$_", pdlpp_list_functions($src, $internal, $base);
355 0         0 (join(' ', "$base.xs", map "$_.c", @cbase),
356             join(' ', map "$_\$(OBJ_EXT)", $base, @cbase));
357             }
358             sub _stdargs {
359 2     2   9 my ($w, $internal, $src, $base, $mod, $callpack, $multi_c) = @_;
360 2         23 my ($clean, %hash) = '';
361 2 50       9 if ($multi_c) {
362 0         0 my ($mangled_c, $mangled_o) = pdlpp_mod_vars($mod);
363 0         0 my ($mangled_c_val, $mangled_o_val) = pdlpp_mod_values($internal, $src, $base, $multi_c);
364 0         0 %hash = (%hash,
365             macro => {
366             $mangled_c => $mangled_c_val, $mangled_o => $mangled_o_val,
367             },
368             OBJECT => "\$($mangled_o)",
369             );
370 0         0 $clean .= " \$($mangled_c)";
371             } else {
372 2         14 %hash = (%hash, OBJECT => "$base\$(OBJ_EXT)");
373 2         6 $clean .= " $base.xs";
374             }
375 2 50       8 if ($internal) {
376             $hash{depend} = {
377 0         0 "$base\$(OBJ_EXT)" => File::Spec::Functions::abs2rel(catfile($w, qw(PDL Core pdlperl.h))),
378             };
379             }
380             (
381 2 50       10 NAME => $mod,
    50          
382             VERSION_FROM => ($internal ? catfile($w, qw(PDL Core.pm)) : $src),
383             TYPEMAPS => [PDL_TYPEMAP()],
384             PM => {"$base.pm" => "\$(INST_LIBDIR)/$base.pm"},
385             MAN3PODS => {"$base.pm" => "\$(INST_MAN3DIR)/$mod.\$(MAN3EXT)"},
386             INC => PDL_INCLUDE(),
387             LIBS => [''],
388             clean => {FILES => "$base.pm $base.c$clean"},
389             %hash,
390             ($internal
391             ? (NO_MYMETA => 1)
392             : (dist => {PREOP => '$(PERLRUNINST) -MPDL::Core::Dev -e pdlpp_mkgen $(DISTVNAME)' })
393             ),
394             );
395             }
396              
397             sub pdlpp_stdargs_int {
398 0     0 0 0 _stdargs(dirname($MY_DIR2), 1, @{$_[0]}[0..3], 1);
  0         0  
399             }
400              
401             sub pdlpp_stdargs {
402 2     2 0 5 _stdargs(undef, 0, @{$_[0]});
  2         15  
403             }
404              
405             # pdlpp_mkgen($dir)
406             # - scans $dir/MANIFEST for all *.pd files and creates corresponding *.pm files
407             # in $dir/GENERATED/ subdir; needed for proper doc rendering at metacpan.org
408             # - it is used in Makefile.PL like:
409             # dist => { PREOP=>'$(PERL) -MPDL::Core::Dev -e pdlpp_mkgen $(DISTVNAME)' }
410             # so all the magic *.pm generation happens during "make dist"
411             # - it is intended to be called as a one-liner:
412             # perl -MPDL::Core::Dev -e pdlpp_mkgen DirName
413             # - it relies on finding "=head1 NAME" and the module name in *.pd, though can be in comment
414             #
415             sub pdlpp_mkgen {
416 0     0 0 0 require File::Copy;
417 0         0 require ExtUtils::Manifest;
418 0 0       0 my $dir = @_ > 0 ? $_[0] : $ARGV[0];
419 0 0 0     0 die "pdlpp_mkgen: unspecified directory" unless defined $dir && -d $dir;
420 0         0 my $file = "$dir/MANIFEST";
421 0 0       0 die "pdlpp_mkgen: non-existing '$file\'" unless -f $file;
422 0         0 my @pairs = ();
423 0         0 my $manifest = ExtUtils::Manifest::maniread($file);
424 0   0     0 for (grep !/^(t|xt)\// && /\.pd$/ && -f, sort keys %$manifest) {
425 0         0 my $content = do { local $/; open my $in, '<', $_; <$in> };
  0         0  
  0         0  
  0         0  
426 0 0       0 warn("pdlpp_mkgen: unknown module name for '$_' (use proper '=head1 NAME' section)\n"), next
427             if !(my ($name) = $content =~ /=head1\s+NAME\s+(\S+)\s+/sg);
428 0         0 push @pairs, [$_, $name];
429             }
430 0         0 my %added = ();
431 0         0 my @in = map "-I".File::Spec::Functions::rel2abs($_), @INC;
432 0         0 for (@pairs) {
433 0         0 my ($pd, $mod) = @$_;
434 0         0 (my $prefix = $mod) =~ s|::|/|g;
435 0         0 my $outfile = File::Spec::Functions::rel2abs("$dir/GENERATED/$prefix.pm");
436 0         0 File::Path::mkpath(dirname($outfile));
437 0         0 my $old_cwd = Cwd::cwd();
438 0         0 my $maybe_lib_base = "lib/$prefix";
439 0         0 my $maybe_lib_path = "$maybe_lib_base.pd";
440 0         0 my $is_lib_path = substr($pd, -length $maybe_lib_path) eq $maybe_lib_path;
441 0 0       0 my $todir = $is_lib_path ? substr($pd, 0, -length($maybe_lib_path)-1) : dirname($pd);
442 0 0       0 chdir $todir if $todir;
443 0 0       0 my $basename = $is_lib_path ? $maybe_lib_base : (split '/', $prefix)[-1];
444 0         0 my $pp_call_arg = _pp_call_arg($mod, $mod, $basename, '', 0); # 0 so guarantee not create pp-*.c
445             #there is no way to use PDL::PP from perl code, thus calling via system()
446 0 0       0 my $rv = system $^X, @in, $pp_call_arg, $is_lib_path ? "$basename.pd" : basename($pd);
447 0         0 my $basefile = "$basename.pm";
448 0 0 0     0 die "pdlpp_mkgen: cannot convert '$pd'\n" unless $rv == 0 && -f $basefile;
449 0 0       0 File::Copy::copy($basefile, $outfile) or die "$outfile: $!";
450 0         0 unlink $basefile; # Transform::Proj4.pm is wrong without GIS::Proj built
451 0         0 unlink "$basename.xs"; # since may have been recreated wrong
452 0 0       0 chdir $old_cwd or die "chdir $old_cwd: $!";
453 0         0 $added{"GENERATED/$prefix.pm"} = "mod=$mod pd=$pd (added by pdlpp_mkgen)";
454             }
455 0 0       0 if (scalar(keys %added) > 0) {
456             #maniadd works only with this global variable
457 0         0 local $ExtUtils::Manifest::MANIFEST = $file;
458 0         0 ExtUtils::Manifest::maniadd(\%added);
459             }
460             }
461              
462             sub unsupported {
463 0     0 0 0 my ($package,$os) = @_;
464 0         0 "No support for $package on $os platform yet. Will skip build process";
465             }
466              
467             sub write_dummy_make {
468 0     0 0 0 my ($msg) = @_;
469 0         0 $msg =~ s#\n*\z#\n#;
470 0         0 $msg =~ s#^\s*#\n#gm;
471 0         0 print $msg;
472 0         0 require ExtUtils::MakeMaker;
473 0         0 ExtUtils::MakeMaker::WriteEmptyMakefile(NAME => 'Dummy', DIR => []);
474             }
475              
476             sub getcyglib {
477 0     0 0 0 my ($lib) = @_;
478 0         0 my $lp = `gcc -print-file-name=lib$lib.a`;
479 0         0 $lp =~ s|/[^/]+$||;
480 0         0 $lp =~ s|^([a-z,A-Z]):|//$1|g;
481 0         0 return "-L$lp -l$lib";
482             }
483              
484             =head2 trylink
485              
486             =for ref
487              
488             a perl configure clone
489              
490             =for example
491              
492             if (trylink 'libGL', '', 'char glBegin(); glBegin();', '-lGL') {
493             $libs = '-lGLU -lGL';
494             $have_GL = 1;
495             } else {
496             $have_GL = 0;
497             }
498             $maybe =
499             trylink 'libwhatever', '', $body, $libs, $cflags,
500             {MakeMaker=>1, Hide=>0, Clean=>1};
501              
502             Try to link some C-code making up the body of a function
503             with a given set of library specifiers
504              
505             return 1 if successful, 0 otherwise
506              
507             =for usage
508              
509             trylink $infomsg, $include, $progbody, $libs [,$cflags,{OPTIONS}];
510              
511             Takes 4 + 2 optional arguments.
512              
513             =over 5
514              
515             =item *
516              
517             an informational message to print (can be empty)
518              
519             =item *
520              
521             any commands to be included at the top of the generated C program
522             (typically something like C<#include "mylib.h">)
523              
524             =item *
525              
526             the body of the program (in function main)
527              
528             =item *
529              
530             library flags to use for linking. Preprocessing
531             by MakeMaker should be performed as needed (see options and example).
532              
533             =item *
534              
535             compilation flags. For example, something like C<-I/usr/local/lib>.
536             Optional argument. Empty if omitted.
537              
538             =item *
539              
540             OPTIONS
541              
542             =over
543              
544             =item MakeMaker
545              
546             Preprocess library strings in the way MakeMaker does things. This is
547             advisable to ensure that your code will actually work after the link
548             specs have been processed by MakeMaker.
549              
550             =item Hide
551              
552             Controls if linking output etc is hidden from the user or not.
553             On by default but overridable with environment variable C if set.
554              
555             =item Clean
556              
557             Remove temporary files. Enabled by default. You might want to switch
558             it off during debugging.
559              
560             =back
561              
562             =back
563              
564             =cut
565              
566             sub trylink {
567 0 0   0 1 0 my $opt = ref $_[$#_] eq 'HASH' ? pop : {};
568 0         0 my ($txt,$inc,$body,$libs,$cflags) = @_;
569 0   0     0 $cflags ||= '';
570 0         0 require File::Temp;
571             # check if MakeMaker should be used to preprocess the libs
572 0         0 for my $key(keys %$opt) {$opt->{lc $key} = $opt->{$key}}
  0         0  
573 0   0     0 my $mmprocess = exists $opt->{makemaker} && $opt->{makemaker};
574 0   0     0 my $hide = $opt->{hide} // $ENV{HIDE_TRYLINK} // 1;
      0        
575 0 0       0 my $clean = exists $opt->{clean} ? $opt->{clean} : 1;
576 0 0       0 if ($mmprocess) {
577 0         0 require ExtUtils::MakeMaker;
578 0         0 require ExtUtils::Liblist;
579 0         0 my $self = ExtUtils::MakeMaker->new({DIR => [],'NAME' => 'NONE'});
580 0         0 my @libs = $self->ext($libs, 0);
581 0 0       0 print "processed LIBS: $libs[0]\n" unless $hide;
582 0         0 $libs = $libs[0]; # replace by preprocessed libs
583             }
584 0 0       0 print " Trying $txt...\n " if $txt =~ /\S/;
585 0 0       0 my $HIDE = !$hide ? '' : '>/dev/null 2>&1';
586 0 0       0 if($^O =~ /mswin32/i) {$HIDE = '>NUL 2>&1'}
  0         0  
587 0   0     0 my $tempd = File::Temp::tempdir(CLEANUP=>1) || die "trylink: could not make temp dir";
588 0         0 my ($tc,$te) = map catfile($tempd,"testfile$_"), ('.c','');
589 0 0       0 open FILE,">$tc" or die "trylink: couldn't open testfile `$tc' for writing, $!";
590 0         0 my $prog = <<"EOF";
591             $inc
592             int main(void) {
593             $body
594             return 0;
595             }
596             EOF
597 0         0 print FILE $prog;
598 0         0 close FILE;
599             # print "test prog:\n$prog\n";
600             # make sure we can overwrite the executable. shouldn't need this,
601             # but if it fails and HIDE is on, the user will never see the error.
602 0 0       0 open(T, ">$te") or die( "unable to write to test executable `$te'");
603 0         0 close T;
604 0         0 my $cmd = "$Config{cc} $cflags -o $te $tc $libs $HIDE";
605 0 0       0 print "$cmd ...\n" unless $hide;
606 0 0 0     0 my $success = (system($cmd) == 0) && -e $te ? 1 : 0;
607 0 0       0 unlink $te, $tc if $clean;
608 0 0       0 print $success ? "\t\tYES\n" : "\t\tNO\n" unless $txt =~ /^\s*$/;
    0          
609 0 0 0     0 print $success ? "\t\tSUCCESS\n" : "\t\tFAILED\n"
    0          
610             if $txt =~ /^\s*$/ && !$hide;
611 0         0 return $success;
612             }
613              
614             sub get_maths_libs {
615 0 0   0 0 0 return '' if $^O =~ /MSWin/;
616 0 0       0 return getcyglib('m') if $^O =~ /cygwin/;
617 0 0 0     0 return '-lm' if !($^O eq 'solaris' or $^O eq 'sunos');
618 0         0 my $libs = '-lm';
619             # try to guess where sunmath is
620 0         0 my @d = split /:+/, $ENV{LD_LIBRARY_PATH};
621 0         0 my $ok = 0;
622 0         0 for my $d (@d) {
623 0 0 0     0 if (-e "$d/libsunmath.so" or -e "$d/libsunmath.a" ) {
624 0         0 $libs = "-lsunmath $libs";
625 0         0 $ok = 1;
626 0         0 last;
627             }
628             }
629 0 0       0 return $libs if $ok;
630 0         0 print "libsunmath not found in LD_LIBRARY_PATH: looking elsewhere\n";
631             # get root directory of compiler; may be off of there
632 0         0 require File::Which;
633 0         0 my @dirs = map dirname($_).'/lib', grep defined, scalar File::Which::which($Config{cc});
634 0         0 push @dirs, '/opt/SUNWspro/lib'; # default location if all else fails
635 0         0 for my $d ( @dirs ) {
636 0 0       0 if (-e "$d/libsunmath.so") {
637 0         0 $libs = "-R$d -L$d -lsunmath $libs";
638 0         0 $ok = 1;
639 0         0 last;
640             }
641 0 0       0 if (-e "$d/libsunmath.a") {
642 0         0 $libs = "-L$d -lsunmath $libs";
643 0         0 $ok = 1;
644 0         0 last;
645             }
646             }
647 0 0       0 print <<'EOF' if !$ok;
648             Couldn't find sunmath library in standard places
649             If you can find libsunmath.a or libsunmath.so
650             please let us know at pdl-devel@lists.sourceforge.net
651             EOF
652 0         0 $libs;
653             }
654              
655             =head2 generate_core_flags
656              
657             =for ref
658              
659             prints on C XS text with core flags, for F.
660              
661             =cut
662              
663             my %flags = (
664             hdrcpy => { set => 1 },
665             fflows => { FLAG => "DATAFLOW_F" },
666             is_readonly => { FLAG => "READONLY" },
667             is_inplace => { FLAG => "INPLACE", postset => 1 },
668             set_inplace => { FLAG => "INPLACE", noret => 1 },
669             donttouch => { FLAG => "DONTTOUCHDATA" },
670             allocated => { },
671             vaffine => { FLAG => "OPT_VAFFTRANSOK" },
672             anychgd => { FLAG => "ANYCHANGED" },
673             datachgd => { FLAG => "PARENTDATACHANGED" },
674             dimschgd => { FLAG => "PARENTDIMSCHANGED" },
675             );
676              
677             sub generate_core_flags {
678             # access (read, if set is true then write as well; if postset true then
679             # read first and write new value after that)
680             # to ndarray's state
681 1     1 1 15 foreach my $name ( sort keys %flags ) {
682 11   66     49 my $flag = "PDL_" . ($flags{$name}{FLAG} || uc($name));
683 11         17 my $ref = $flags{$name};
684 11         31 my $with_mode = grep $ref->{$_}, qw(set postset noret);
685 11 100       32 my $mode_dflt = (grep $ref->{$_}, qw(set postset)) ? "=0" : "";
686 11 100       30 my @mode = $with_mode ? (",mode$mode_dflt", "\n int mode") : ('', '');
687 11 100       54 printf <<'EOF', $ref->{noret} ? 'void' : 'int', $name, @mode;
688             %s
689             %s(x%s)
690             pdl *x%s
691             CODE:
692             EOF
693 11 100       26 my $cond = $ref->{noret} ? "" : "if (items>1) ";
694 11         18 my $set = " ${cond}setflag(x->state,$flag,mode);\n";
695 11         24 my $ret = " RETVAL = ((x->state & $flag) > 0);\n";
696 11 100 100     42 print $set if $ref->{set} || $ref->{noret};
697 11 100       27 print $ret if !$ref->{noret};
698 11 100       25 print $set if $ref->{postset};
699 11 100       25 print " OUTPUT:\n RETVAL\n" if !$ref->{noret};
700 11         152 print "\n";
701             } # foreach: keys %flags
702             }
703              
704             =head2 got_complex_version
705              
706             =for ref
707              
708             PDL::Core::Dev::got_complex_version($func_name, $num_params)
709              
710             For a given function appearing in C99's C, will return a
711             boolean of whether the system being compiled on has the complex version
712             of that. E.g. for C, will test whether C exists (before 2.069,
713             would only check for C, causing build failures on non-C99 compliant
714             C which mandates long-double versions).
715              
716             =cut
717              
718             my %got_complex_cache;
719             sub got_complex_version {
720 10     10 1 399965 my ($name, $params) = @_;
721 10 50       71 return $got_complex_cache{$name} if defined $got_complex_cache{$name};
722 10         52 my $args = join ',', ('(double complex)1') x $params;
723             $got_complex_cache{$name} = Devel::CheckLib::check_lib(
724 10 50       465 ($Config{gccversion} ? (ccflags => '-O0') : ()), # stop GCC optimising test code away
725             lib => 'm',
726             header => 'complex.h',
727             function => sprintf('double num; num = creal(c%sl(%s)); return 0;', $name, $args),
728             );
729             }
730              
731             1;