File Coverage

blib/lib/RPerl/Compiler.pm
Criterion Covered Total %
statement 66 67 98.5
branch 3 4 75.0
condition n/a
subroutine 21 21 100.0
pod 0 1 0.0
total 90 93 96.7


line stmt bran cond sub pod time code
1             ## no critic qw(ProhibitExcessMainComplexity) # SYSTEM SPECIAL 4: allow complex code outside subroutines, must be on line 1
2             # [[[ PREPROCESSOR ]]]
3             # <<< TYPE_CHECKING: OFF >>>
4              
5             # [[[ HEADER ]]]
6             package RPerl::Compiler;
7 4     4   3226 use strict;
  4         10  
  4         111  
8 4     4   24 use warnings;
  4         9  
  4         97  
9 4     4   52 use RPerl::AfterSubclass;
  4         11  
  4         559  
10             our $VERSION = 0.025_000;
11              
12             # [[[ OO INHERITANCE ]]]
13 4     4   29 use parent qw(RPerl::CompileUnit::Module::Class);
  4         9  
  4         27  
14 4     4   249 use RPerl::CompileUnit::Module::Class;
  4         12  
  4         86  
15              
16             # [[[ CRITICS ]]]
17             ## no critic qw(ProhibitUselessNoCritic ProhibitMagicNumbers RequireCheckedSyscalls) # USER DEFAULT 1: allow numeric values & print operator
18             ## no critic qw(RequireInterpolationOfMetachars) # USER DEFAULT 2: allow single-quoted control characters & sigils
19             ## no critic qw(ProhibitStringyEval) # SYSTEM DEFAULT 1: allow eval()
20             ## no critic qw(RequireBriefOpen) # SYSTEM SPECIAL 10: allow complex processing with open filehandle
21              
22             # [[[ INCLUDES ]]]
23 4     4   401 use RPerl::Parser;
  4         11  
  4         121  
24 4     4   28 use RPerl::Generator;
  4         10  
  4         109  
25 4     4   28 use File::Temp qw(tempfile);
  4         13  
  4         247  
26 4     4   27 use File::Basename;
  4         9  
  4         250  
27 4     4   28 use English qw(-no_match_vars); # for $OSNAME; why isn't this included from 'require RPerl::Config', which is included from 'use RPerl' above?
  4         8  
  4         44  
28 4     4   1527 use IPC::Cmd qw(can_run); # to check for `perltidy` and `astyle`
  4         10  
  4         268  
29 4     4   31 use List::MoreUtils qw(uniq);
  4         8  
  4         58  
30 4     4   2511 use File::Spec;
  4         11  
  4         91  
31 4     4   22 use Config;
  4         9  
  4         183  
32 4     4   23 use Config qw(config_re);
  4         10  
  4         162  
33             #use IPC::Open3; ## 93r
34             #use IO::Select; ## 93r
35 4     4   2169 use IPC::Run3 qw(run3);
  4         11797  
  4         243  
36 4     4   37 use Cwd;
  4         12  
  4         242  
37 4     4   34 use File::Copy; # for move()
  4         10  
  4         214  
38 4     4   1511 use Alien::astyle;
  4         35186  
  4         39  
39 4     4   19639 use Env qw(@PATH);
  4         5971  
  4         29  
40             unshift @PATH, Alien::astyle->bin_dir();
41              
42             #our string_arrayref_hashref_hashref $filename_suffixes_supported = {
43             our hashref_hashref $filename_suffixes_supported = {
44             INPUT_SOURCE => { PL => ['.pl'], PM => ['.pm'] },
45             OUTPUT_SOURCE => { CPP => ['.cpp'], H => ['.h'], PMC => ['.pmc'], OPENMP_CPP => ['.openmp.cpp'] },
46             OUTPUT_BINARY => { O => ['.o'], A => ['.a'], SO => ['.so'], EXE => [ q{}, '.exe' ], OPENMP_EXE => [ '.openmp', '.openmp.exe' ] }
47              
48             # NEED ANSWER: what are the correct Windows file extensions?
49             # OUTPUT_BINARY => { O => ['.o', '.lib'], A => ['.a', '.lib'], SO => ['.so', '.dll'], EXE => [q{}, '.exe'], OPENMP_EXE => ['.openmp', '.openmp.exe']}
50             };
51              
52             # [[[ SUBROUTINES ]]]
53              
54             our string_arrayref $find_parents = sub {
55             ( my string $file_name, my boolean $find_grandparents_recurse, my string_hashref $modes ) = @_;
56             # RPerl::diag( 'in Compiler::find_parents(), received $file_name = ' . $file_name . "\n" );
57              
58             # trim unnecessary (and possibly problematic) absolute paths from input file name
59             $file_name = post_processor__absolute_path_delete($file_name);
60             # RPerl::diag( 'in Compiler::find_parents(), have possibly-trimmed $file_name = ' . $file_name . "\n" );
61              
62             my string_arrayref $parents = [];
63              
64             if ( not -f $file_name ) {
65             die 'ERROR ECOCOPA00, COMPILER, FIND PARENTS: File not found, ' . q{'} . $file_name . q{'} . ', dying' . "\n";
66             }
67              
68             open my filehandleref $FILE_HANDLE, '<', $file_name
69             or die 'ERROR ECOCOPA01, COMPILER, FIND PARENTS: Cannot open file ' . q{'} . $file_name . q{'} . ' for reading, ' . $OS_ERROR . ', dying' . "\n";
70              
71             # read in input file, match on 'use' includes for parents
72             my string $file_line;
73             my string $top_level_package_name = undef;
74             my boolean $use_rperl = 0;
75              
76             # NEED FIX: do not make recursive calls until after closing file, to avoid
77             # ERROR ECOCOPA01, COMPILER, FIND PARENTS: Cannot open file Foo/Bar.pm for reading, Too many open files, dying
78             while ( $file_line = <$FILE_HANDLE> ) {
79             # RPerl::diag('in Compiler::find_parents(), top of while loop, have $file_line = ' . $file_line . "\n");
80              
81             if ( $file_line =~ /^\s*package\s+[\w:]+\s*;\s*$/xms ) {
82             if ( not defined $top_level_package_name ) {
83             $top_level_package_name = $file_line;
84             $top_level_package_name =~ s/^\s*package\s+([\w:]+)\s*;\s*$/$1/gxms;
85             }
86             # DEV NOTE: for monolithic modules (more than one package), we only find parents of the first package, to avoid incorrect parent lists & infinite recursion
87             else {
88             last;
89             }
90             }
91              
92             if ( $file_line =~ /^\s*use\s+[\w:]+/xms ) {
93             # RPerl::diag('in Compiler::find_parents(), found use line, have $file_line = ' . $file_line . "\n");
94             if (( $file_line =~ /use\s+RPerl\s*;/ ) or
95             ( $file_line =~ /use\s+RPerl::AfterSubclass\s*;/ )) {
96             $use_rperl = 1;
97             next;
98             }
99             elsif ( $file_line =~ /use\s+lib/ ) {
100             die
101             q{ERROR ECOCOPA02, COMPILER, FIND PARENTS: 'use lib...' not currently supported, please set @INC using the PERL5LIB environment variable, file }
102             . q{'}
103             . $file_name . q{'}
104             . ', dying' . "\n";
105             }
106             elsif ( $file_line !~ /use\s+parent/ )
107             {
108             # safely ignore these not-parent uses
109             next;
110             }
111              
112             # 'use RPerl;' must appear before any other 'use Foo;' statements, or else this is not a valid RPerl input file and we return empty deps
113             if (not $use_rperl) {
114             last;
115             }
116              
117             my string $package_file_name_included;
118             my string $package_name = $file_line;
119             # remove everything except the package name
120             $package_name =~ s/^(\s*)//gxms; # strip leading whitespace
121             substr $package_name, 0, 14, q{}; # strip leading 'use parent qw('
122             $package_name =~ s/([\w:]+)(.*)$/$1/gxms; # strip trailing everything
123             # RPerl::diag('in Compiler::find_parents(), have $package_name = ' . $package_name . "\n\n");
124              
125             # safely skip base class for no parent inheritance
126             if ($package_name eq 'RPerl::CompileUnit::Module::Class') {
127             next;
128             }
129              
130             my string $package_file_name = $package_name;
131             $package_file_name =~ s/::/\//gxms; # replace double-colon :: scope delineator with forward-slash / directory delineator
132             $package_file_name .= '.pm';
133              
134             # find specific included dependency file in @INC
135             foreach my string $INC_directory (@INC) {
136             # RPerl::diag( 'in Compiler::find_parents(), top of @INC foreach loop, have $INC_directory = ' . $INC_directory . "\n" );
137             $package_file_name_included = $INC_directory . '/' . $package_file_name;
138             # RPerl::diag( 'in Compiler::find_parents(), inside @INC foreach loop, have $package_file_name_included = ' . $package_file_name_included . "\n" );
139             if (-e $package_file_name_included) {
140             # RPerl::diag( 'in Compiler::find_parents(), inside @INC foreach loop, have EXISTING $package_file_name_included = ' . $package_file_name_included . "\n" );
141             last;
142             }
143             else {
144             $package_file_name_included = q{};
145             }
146             }
147             if ($package_file_name_included eq q{}) {
148             die 'ERROR ECOCOPA04, COMPILER, FIND PARENTS: Failed to find package file ', q{'}, $package_file_name, q{'},
149             ' in @INC, included from file ', q{'}, $file_name, q{'}, ', dying', "\n";
150             }
151              
152             # RPerl::diag( 'in Compiler::find_parents(), have $package_file_name_included = ' . $package_file_name_included . "\n" );
153              
154             my string $package_file_name_included_relative = post_processor__absolute_path_delete( $package_file_name_included );
155             push @{$parents}, $package_file_name_included_relative;
156            
157             # RPerl::diag( 'in Compiler::find_parents(), have PRE-SUBDEPS $parents = ' . Dumper($parents) . "\n" );
158              
159             if ($find_grandparents_recurse) {
160            
161             # recursively find grandparents
162             my string_arrayref $grandparents = find_parents( $package_file_name_included, $find_grandparents_recurse, $modes );
163            
164             # discard duplicate parents that now appear in grandparents
165             $parents = [ uniq @{$grandparents}, @{$parents} ];
166            
167             # RPerl::diag( 'in Compiler::find_parents(), have POST-SUBDEPS $parents = ' . Dumper($parents) . "\n" );
168             }
169             }
170             }
171              
172             close $FILE_HANDLE
173             or die 'ERROR ECOCOPA05, COMPILER, FIND PARENTS: Cannot close file ' . q{'}
174             . $file_name . q{'}
175             . ' after reading, '
176             . $OS_ERROR
177             . ', dying' . "\n";
178              
179             # RPerl::diag( 'in Compiler::find_parents(), returning $parents = ' . Dumper($parents) . "\n" );
180             # RPerl::diag('in Compiler::find_parents(), about to return, have $modes->{_enable_sse} = ' . Dumper($modes->{_enable_sse}) . "\n");
181             # RPerl::diag('in Compiler::find_parents(), about to return, have $modes->{_enable_gmp} = ' . Dumper($modes->{_enable_gmp}) . "\n");
182             return $parents;
183             };
184              
185             our string_arrayref $find_dependencies = sub {
186             ( my string $file_name, my boolean $find_subdependencies_recurse, my string_hashref $modes ) = @_;
187             # RPerl::diag( 'in Compiler::find_dependencies(), received $file_name = ' . $file_name . "\n" );
188              
189             # trim unnecessary (and possibly problematic) absolute paths from input file name
190             $file_name = post_processor__absolute_path_delete($file_name);
191             # RPerl::diag( 'in Compiler::find_dependencies(), have possibly-trimmed $file_name = ' . $file_name . "\n" );
192              
193             my string_arrayref $dependencies = [];
194             # my string_arrayref $pmc_disable_paths = []; # DISABLE_DYNAMIC_DEPS_ANALYSIS
195              
196             if ( not -f $file_name ) {
197             die 'ERROR ECOCODE00, COMPILER, FIND DEPENDENCIES: File not found, ' . q{'} . $file_name . q{'} . ', dying' . "\n";
198             }
199              
200             open my filehandleref $FILE_HANDLE, '<', $file_name
201             or die 'ERROR ECOCODE01, COMPILER, FIND DEPENDENCIES: Cannot open file ' . q{'} . $file_name . q{'} . ' for reading, ' . $OS_ERROR . ', dying' . "\n";
202              
203             # read in input file, match on 'use' includes for dependencies
204             my string $file_line;
205             my string $top_level_package_name = undef;
206             my boolean $use_rperl = 0;
207              
208             # NEED FIX: do not make recursive calls until after closing file, to avoid
209             # ERROR ECOCODE01, COMPILER, FIND DEPENDENCIES: Cannot open file Foo/Bar.pm for reading, Too many open files, dying
210             while ( $file_line = <$FILE_HANDLE> ) {
211             # RPerl::diag('in Compiler::find_dependencies(), top of while loop, have $file_line = ' . $file_line . "\n");
212              
213             if ( ( $file_line =~ /^\s*package\s+[\w:]+\s*;\s*$/xms ) and ( not defined $top_level_package_name ) ) {
214             # disable top-level PMC file before finding subdependencies
215             $top_level_package_name = $file_line;
216             $top_level_package_name =~ s/^\s*package\s+([\w:]+)\s*;\s*$/$1/gxms;
217             =DISABLE_DYNAMIC_DEPS_ANALYSIS
218             my string $pmc_disable_path = pmc_disable($top_level_package_name);
219             if ($pmc_disable_path eq q{}) {
220             # my integer $eval_retval = eval_use_dependencies($top_level_package_name); # NEED ANSWER: do we need to care about $eval_retval?
221             eval_use_dependencies($top_level_package_name);
222             }
223             else {
224             push @{$pmc_disable_paths}, $pmc_disable_path;
225             }
226             =cut
227             }
228              
229             # NEED FIX: remove hard-coded list of not-subdependency uses
230             if ( $file_line =~ /^\s*use\s+[\w:]+/xms ) {
231             # RPerl::diag('in Compiler::find_dependencies(), found use line, have $file_line = ' . $file_line . "\n");
232             if (( $file_line =~ /use\s+RPerl\s*;/ ) or
233             ( $file_line =~ /use\s+RPerl::AfterSubclass\s*;/ )) {
234             $use_rperl = 1;
235             next;
236             }
237             elsif ( ( $file_line =~ /use\s+strict\s*;/ )
238             or ( $file_line =~ /use\s+warnings\s*;/ )
239             or ( $file_line =~ /use\s+RPerl::CompileUnit::Module::Class\s*;/ )
240             or ( $file_line =~ /use\s+RPerl::Class\s*;/ )
241             or ( $file_line =~ /use\s+RPerl::Config\s*;/ )
242             or ( $file_line =~ /use\s+\w+Perl::Config\s*;/ ) # DEV NOTE, CORRELATION #rp027: MathPerl::Config, PhysicsPerl::Config, etc
243             or ( $file_line =~ /use\s+parent/ )
244             or ( $file_line =~ /use\s+constant/ )
245             or ( $file_line =~ /use\s+overload/ )
246             or ( $file_line =~ /use\s+integer/ )
247             or ( $file_line =~ /use\s+[0-9]/ )
248             )
249             {
250             # safely ignore these possibly-valid but not-subdependency uses
251             next;
252             }
253             elsif ( $file_line =~ /use\s+rperlsse\s*;/ ) {
254              
255             # RPerl::diag('in Compiler::find_dependencies(), found rperlsse line, have $modes->{_enable_sse} = ' . Dumper($modes->{_enable_sse}) . "\n");
256             if ( ( substr $Config{archname}, 0, 3 ) eq 'arm' ) {
257             die q{ERROR ECOCODE06, COMPILER, FIND DEPENDENCIES: 'use rperlsse;' command found but SSE not supported on ARM architecture, file }
258             . q{'}
259             . $file_name . q{'}
260             . ', dying' . "\n";
261             }
262             if ( ( not exists $modes->{_enable_sse} ) or ( not defined $modes->{_enable_sse} ) ) {
263             $modes->{_enable_sse} = {};
264             }
265             $modes->{_enable_sse}->{$file_name} = 1;
266              
267             # RPerl::diag('in Compiler::find_dependencies(), after finding rperlsse line, have $modes->{_enable_sse} = ' . Dumper($modes->{_enable_sse}) . "\n");
268             next;
269             }
270             elsif ( $file_line =~ /use\s+rperlgmp\s*;/ ) {
271             # RPerl::diag('in Compiler::find_dependencies(), found rperlgmp line, have $modes->{_enable_gmp} = ' . Dumper($modes->{_enable_gmp}) . "\n");
272             if ( ( not exists $modes->{_enable_gmp} ) or ( not defined $modes->{_enable_gmp} ) ) {
273             $modes->{_enable_gmp} = {};
274             }
275             $modes->{_enable_gmp}->{$file_name} = 1;
276              
277             # RPerl::diag('in Compiler::find_dependencies(), after finding rperlgmp line, have $modes->{_enable_gmp} = ' . Dumper($modes->{_enable_gmp}) . "\n");
278             next;
279             }
280             elsif ( $file_line =~ /use\s+lib/ ) {
281             die
282             q{ERROR ECOCODE02, COMPILER, FIND DEPENDENCIES: 'use lib...' not currently supported, please set @INC using the PERL5LIB environment variable, file }
283             . q{'}
284             . $file_name . q{'}
285             . ', dying' . "\n";
286             }
287              
288             # 'use RPerl;' or 'use RPerl::AfterSubclass;' must appear before any other 'use Foo;' statements, or else this is not a valid RPerl input file and we return empty deps
289             if (not $use_rperl) {
290             last;
291             }
292              
293             my string $package_file_name_included;
294             my string $package_name = $file_line;
295             $package_name =~ s/^\s*use\s+([\w:]+)\s*.*\s*;\s*$/$1/gxms; # remove everything except the package name
296              
297             # disable PMC file before finding subdependencies
298             # my string $pmc_disable_path = pmc_disable($package_name); # DISABLE_DYNAMIC_DEPS_ANALYSIS
299              
300             my string $package_file_name = $package_name;
301             $package_file_name =~ s/::/\//gxms; # replace double-colon :: scope delineator with forward-slash / directory delineator
302             $package_file_name .= '.pm';
303              
304             # find specific included dependency file in either %INC or @INC
305             =DISABLE_DYNAMIC_DEPS_ANALYSIS
306             if ($pmc_disable_path eq q{}) {
307             eval_use_dependencies($package_name);
308             if ( not exists $INC{$package_file_name} ) {
309             die 'ERROR ECOCODE03, COMPILER, FIND DEPENDENCIES: Failed to find package file ', q{'}, $package_file_name, q{'},
310             ' in %INC, included from file ', q{'}, $file_name, q{'}, ', dying', "\n";
311             }
312             $package_file_name_included = $INC{$package_file_name};
313             }
314             else {
315             =cut
316             foreach my string $INC_directory (@INC) {
317             # RPerl::diag( 'in Compiler::find_dependencies(), top of @INC foreach loop, have $INC_directory = ' . $INC_directory . "\n" );
318             $package_file_name_included = $INC_directory . '/' . $package_file_name;
319             # RPerl::diag( 'in Compiler::find_dependencies(), inside @INC foreach loop, have $package_file_name_included = ' . $package_file_name_included . "\n" );
320             if (-e $package_file_name_included) {
321             # RPerl::diag( 'in Compiler::find_dependencies(), inside @INC foreach loop, have EXISTING $package_file_name_included = ' . $package_file_name_included . "\n" );
322             last;
323             }
324             else {
325             $package_file_name_included = q{};
326             }
327             }
328             if ($package_file_name_included eq q{}) {
329             die 'ERROR ECOCODE04, COMPILER, FIND DEPENDENCIES: Failed to find package file ', q{'}, $package_file_name, q{'},
330             ' in @INC, included from file ', q{'}, $file_name, q{'}, ', dying', "\n";
331             }
332             =DISABLE_DYNAMIC_DEPS_ANALYSIS
333             push @{$pmc_disable_paths}, $pmc_disable_path;
334             }
335             =cut
336              
337             # RPerl::diag( 'in Compiler::find_dependencies(), have $package_file_name_included = ' . $package_file_name_included . "\n" );
338              
339             my string $package_file_name_included_relative = post_processor__absolute_path_delete( $package_file_name_included );
340             push @{$dependencies}, $package_file_name_included_relative;
341            
342             # RPerl::diag( 'in Compiler::find_dependencies(), have PRE-SUBDEPS $dependencies = ' . Dumper($dependencies) . "\n" );
343              
344             if ($find_subdependencies_recurse) {
345            
346             # recursively find subdependencies
347             my string_arrayref $subdependencies = find_dependencies( $package_file_name_included, $find_subdependencies_recurse, $modes );
348            
349             # discard duplicate dependencies that now appear in subdependencies
350             $dependencies = [ uniq @{$subdependencies}, @{$dependencies} ];
351            
352             # RPerl::diag( 'in Compiler::find_dependencies(), have POST-SUBDEPS $dependencies = ' . Dumper($dependencies) . "\n" );
353             }
354             }
355             }
356              
357             close $FILE_HANDLE
358             or die 'ERROR ECOCODE05, COMPILER, FIND DEPENDENCIES: Cannot close file ' . q{'}
359             . $file_name . q{'}
360             . ' after reading, '
361             . $OS_ERROR
362             . ', dying' . "\n";
363              
364             =DISABLE_DYNAMIC_DEPS_ANALYSIS
365             # re-enable all PMC files after finding dependencies
366             while (scalar @{$pmc_disable_paths}) {
367             pmc_reenable(pop @{$pmc_disable_paths});
368             }
369             =cut
370              
371             # RPerl::diag( 'in Compiler::find_dependencies(), returning $dependencies = ' . Dumper($dependencies) . "\n" );
372             # RPerl::diag('in Compiler::find_dependencies(), about to return, have $modes->{_enable_sse} = ' . Dumper($modes->{_enable_sse}) . "\n");
373             # RPerl::diag('in Compiler::find_dependencies(), about to return, have $modes->{_enable_gmp} = ' . Dumper($modes->{_enable_gmp}) . "\n");
374             return $dependencies;
375             };
376              
377             =DISABLE_DYNAMIC_DEPS_ANALYSIS
378             # temporarily disable a package's PMC file, if it exists
379             our string $pmc_disable = sub {
380             ( my string $package_name ) = @_;
381             # RPerl::diag( 'in Compiler::pmc_disable(), received $package_name = ' . $package_name . "\n" );
382              
383             my string $pmc_file_path_absolute;
384             my string $pmc_file_path_absolute_disabled = q{};
385             my string $pmc_file_path_relative = $package_name;
386             $pmc_file_path_relative =~ s/::/\//gxms; # replace double-colon :: scope delineator with forward-slash / directory delineator
387             $pmc_file_path_relative .= '.pmc';
388              
389             foreach my string $INC_directory (@INC) {
390             # RPerl::diag( 'in Compiler::pmc_disable(), top of foreach loop, have $INC_directory = ' . $INC_directory . "\n" );
391             $pmc_file_path_absolute = $INC_directory . '/' . $pmc_file_path_relative;
392             # RPerl::diag( 'in Compiler::pmc_disable(), inside foreach loop, have $pmc_file_path_absolute = ' . $pmc_file_path_absolute . "\n" );
393             if (-e $pmc_file_path_absolute) {
394             # RPerl::diag( 'in Compiler::pmc_disable(), inside foreach loop, have EXISTING $pmc_file_path_absolute = ' . $pmc_file_path_absolute . "\n" );
395             $pmc_file_path_absolute_disabled = $pmc_file_path_absolute . '.PMC_DISABLED';
396             my boolean $move_success = move($pmc_file_path_absolute, $pmc_file_path_absolute_disabled);
397             if (not $move_success) {
398             die 'ERROR ECOCODE07, COMPILER, PMC DISABLE: Failed to temporarily disable package file ', q{'}, $pmc_file_path_absolute, q{'; },
399             $OS_ERROR, ', dying', "\n";
400             }
401             # RPerl::diag( 'in Compiler::pmc_disable(), DISABLED $pmc_file_path_absolute = ' . $pmc_file_path_absolute . "\n" );
402             last;
403             }
404             }
405             return $pmc_file_path_absolute_disabled;
406             };
407              
408             # re-enable a package's temporarily-disabled PMC file, if it exists
409             our boolean $pmc_reenable = sub {
410             ( my string $file_name ) = @_;
411             # RPerl::diag( 'in Compiler::pmc_reenable(), received $file_name = ' . $file_name . "\n" );
412             if ((defined $file_name) and ($file_name ne q{})) {
413             if ((substr $file_name, -13, 13) ne '.PMC_DISABLED') {
414             die 'ERROR ECOCODE08, COMPILER, PMC RE-ENABLE: Temporarily-disabled package file name ', q{'}, $file_name, q{'},
415             ' does not with .PMC_DISABLED, dying', "\n";
416             }
417             if (-e $file_name) {
418             my string $file_name_original = $file_name;
419             substr $file_name_original, -13, 13, q{}; # strip trailing .PMC_DISABLED
420             my boolean $move_success = move($file_name, $file_name_original);
421             if (not $move_success) {
422             die 'ERROR ECOCODE09, COMPILER, PMC RE-ENABLE: Failed to re-enable temporarily-disabled package file ', q{'}, $file_name, q{'; },
423             $OS_ERROR, ', dying', "\n";
424             }
425             # RPerl::diag( 'in Compiler::pmc_disable(), RE-ENABLED $file_name = ' . $file_name . "\n" );
426             }
427             else {
428             die 'ERROR ECOCODE10, COMPILER, PMC RE-ENABLE: Failed to re-enable temporarily-disabled package file ', q{'}, $file_name, q{'; },
429             ' file does not exist, dying', "\n";
430             }
431             return 1;
432             }
433             else { return 0; }
434             };
435              
436             # call RPerl::eval_use() to perform a runtime use on a package, with dependencies-specific warning message
437             our integer $eval_use_dependencies = sub {
438             ( my string $package_name ) = @_;
439             # RPerl::diag( 'in Compiler::eval_use(), received $package_name = ' . $package_name . "\n" );
440              
441             my integer $eval_retval = RPerl::eval_use($package_name, 0);
442              
443             # RPerl::diag('in Compiler::find_dependencies(), have POST-EVAL NON-DEP %INC = ' . Dumper(\%INC) . "\n");
444             # warn instead of dying on eval error here and below, in order to preserve proper parser errors instead of weird eval errors
445             # in RPerl/Test/*/*Bad*.pm and RPerl/Test/*/*bad*.pl
446             if ( ( not defined $eval_retval ) or ( $EVAL_ERROR ne q{} ) ) {
447             RPerl::warning( 'WARNING WCOCODE00, COMPILER, FIND DEPENDENCIES: Failed to eval-use package ' . q{'}
448             . $package_name . q{'} . ', fatal error trapped and delayed' . "\n" );
449             RPerl::diag( ' Trapped the following error message...' . "\n\n" . $EVAL_ERROR . "\n" );
450             RPerl::warning("\n");
451             }
452             # RPerl::diag( 'in Compiler::pmc_disable(), EVAL USED $package_name = ' . $package_name . "\n" );
453              
454             return $eval_retval;
455             };
456             =cut
457              
458             # [[[ COMPILE RPERL TO RPERL, TEST MODE ]]]
459             # [[[ COMPILE RPERL TO RPERL, TEST MODE ]]]
460             # [[[ COMPILE RPERL TO RPERL, TEST MODE ]]]
461              
462             our string_hashref $rperl_to_rperl__parse_generate = sub {
463             ( my string $rperl_input_file_name, my string_hashref $rperl_output_file_name_group, my string_hashref $rperl_source_group, my string_hashref $modes ) = @_;
464             my object $rperl_ast;
465              
466             # RPerl::diag( 'in Compiler->rperl_to_rperl__parse_generate(), received $rperl_input_file_name = ' . $rperl_input_file_name . "\n" );
467             # RPerl::diag( 'in Compiler->rperl_to_rperl__parse_generate(), received $rperl_output_file_name_group = ' . "\n" . Dumper($rperl_output_file_name_group) . "\n" );
468             # RPerl::diag( 'in Compiler->rperl_to_rperl__parse_generate(), received $rperl_source_group = ' . "\n" . Dumper($rperl_source_group) . "\n" );
469             # RPerl::diag( 'in Compiler->rperl_to_rperl__parse_generate(), received $modes = ' . "\n" . Dumper($modes) . "\n" );
470              
471             # [[[ PARSE RPERL TO AST ]]]
472              
473             if ( ( $modes->{compile} eq 'PARSE' )
474             or ( $modes->{compile} eq 'GENERATE' )
475             or ( $modes->{compile} eq 'SAVE' ) )
476             {
477             $rperl_ast = RPerl::Parser::rperl_to_ast__parse($rperl_input_file_name);
478             }
479              
480             # [[[ GENERATE AST TO RPERL ]]]
481              
482             if ( ( $modes->{compile} eq 'GENERATE' )
483             or ( $modes->{compile} eq 'SAVE' ) )
484             {
485             $rperl_source_group = RPerl::Generator::ast_to_rperl__generate( $rperl_ast, $modes );
486             }
487              
488             # [[[ SAVE RPERL TO DISK ]]]
489              
490             if ( ( $modes->{compile} eq 'SAVE' )
491             or ( $modes->{compile} eq 'SAVE_DEFERRED' ) )
492             {
493             save_source_files( $rperl_source_group, $rperl_output_file_name_group, $modes );
494             }
495              
496             # always return $rperl_source_group to maintain consistent return type,
497             # only utilized for GENERATE compile mode during dependencies
498             return $rperl_source_group;
499             };
500              
501             # [[[ COMPILE RPERL TO XS & BINARY ]]]
502             # [[[ COMPILE RPERL TO XS & BINARY ]]]
503             # [[[ COMPILE RPERL TO XS & BINARY ]]]
504              
505             our string_hashref $rperl_to_xsbinary__parse_generate_compile = sub {
506             ( my string $rperl_input_file_name, my string_hashref $cpp_output_file_name_group, my string_hashref $cpp_source_group, my string_hashref $modes ) = @_;
507             my object $rperl_ast;
508              
509             # RPerl::diag( 'in Compiler->rperl_to_xsbinary__parse_generate_compile(), received $modes->{_symbol_table} = ' . "\n" . Dumper($modes->{_symbol_table}) . "\n" );
510              
511             # [[[ PARSE RPERL TO AST ]]]
512              
513             if ( ( $modes->{compile} eq 'PARSE' )
514             or ( $modes->{compile} eq 'GENERATE' )
515             or ( $modes->{compile} eq 'SAVE' )
516             or ( $modes->{compile} eq 'SUBCOMPILE' ) )
517             {
518             $rperl_ast = RPerl::Parser::rperl_to_ast__parse($rperl_input_file_name);
519             }
520              
521             # [[[ GENERATE AST TO C++ ]]]
522              
523             if ( ( $modes->{compile} eq 'GENERATE' )
524             or ( $modes->{compile} eq 'SAVE' )
525             or ( $modes->{compile} eq 'SUBCOMPILE' ) )
526             {
527             $cpp_source_group = RPerl::Generator::ast_to_cpp__generate( $rperl_ast, $modes );
528             }
529              
530             # [[[ SAVE C++ TO DISK ]]]
531              
532             if ( ( $modes->{compile} eq 'SAVE' )
533             or ( $modes->{compile} eq 'SAVE_DEFERRED' )
534             or ( $modes->{compile} eq 'SUBCOMPILE' )
535             or ( $modes->{compile} eq 'SUBCOMPILE_DEFERRED' ) )
536             {
537             save_source_files( $cpp_source_group, $cpp_output_file_name_group, $modes );
538             }
539              
540             # [[[ AUTO-PARALLELIZE C++ VIA PLUTO & OPENMP ]]]
541              
542             if ( $modes->{parallel} eq 'OPENMP' ) {
543             cpp_to_openmp_cpp( $cpp_output_file_name_group, $modes );
544             }
545              
546             # [[[ SUBCOMPILE C++ TO XS & BINARY ]]]
547              
548             if ( ( $modes->{compile} eq 'SUBCOMPILE' )
549             or ( $modes->{compile} eq 'SUBCOMPILE_DEFERRED' ) )
550             {
551             cpp_to_xsbinary__subcompile( $cpp_output_file_name_group, $modes );
552             }
553              
554             # always return $cpp_source_group to maintain consistent return type,
555             # only utilized for GENERATE compile mode during dependencies
556             return $cpp_source_group;
557             };
558              
559             # generate output file name group(s) based on input file name(s)
560             #sub generate_output_file_names {
561             our hashref_arrayref $generate_output_file_names = sub {
562             ( my string_arrayref $input_file_names, my string_arrayref $output_file_name_prefixes, my integer $input_files_count, my string_hashref $modes ) = @_;
563              
564             # RPerl::diag('in Compiler::generate_output_file_names(), received $input_file_names = ' . "\n" . Dumper($input_file_names) . "\n");
565             # RPerl::diag('in Compiler::generate_output_file_names(), received $output_file_name_prefixes = ' . "\n" . Dumper($output_file_name_prefixes) . "\n");
566             # RPerl::diag('in Compiler::generate_output_file_names(), received $input_files_count = ' . $input_files_count . "\n");
567             # RPerl::diag( 'in Compiler::generate_output_file_names(), received $modes = ' . "\n" . Dumper($modes) . "\n" );
568              
569             # NEED FIX: add string_hashref_arrayref type
570             # my string_hashref_arrayref $output_file_name_groups = [];
571             my hashref_arrayref $output_file_name_groups = [];
572             my string $input_file_name;
573             my string $input_file_name_path;
574             my string $input_file_name_prefix;
575             my string $input_file_name_suffix;
576              
577             for my $i ( 0 .. ( $input_files_count - 1 ) ) {
578             $input_file_name = $input_file_names->[$i];
579              
580             $output_file_name_groups->[$i] = {};
581              
582             # if output file prefix(es) provided, then use to generate output file name(s)
583             if ( defined $output_file_name_prefixes->[$i] ) {
584              
585             # explicitly provided option should already be only prefix, but fileparse() to make sure
586             ( $input_file_name_prefix, $input_file_name_path, $input_file_name_suffix ) = fileparse( $output_file_name_prefixes->[$i], qr/[.][^.]*/xms );
587             if ( $input_file_name_prefix eq q{} ) {
588             die "ERROR EAR17: Invalid RPerl source code output file command-line argument specified, dying\n";
589             }
590             }
591              
592             # if output file prefix(es) not provided, then generate output file name(s) from input file name(s)
593             else {
594             # RPerl::diag('in Compiler::generate_output_file_names(), have $input_file_name = ' . $input_file_name . "\n");
595             # should not already be only prefix, fileparse() to isolate prefix
596             ( $input_file_name_prefix, $input_file_name_path, $input_file_name_suffix ) = fileparse( $input_file_name, qr/[.][^.]*/xms );
597             }
598              
599             my string $output_file_name_path_prefix = $input_file_name_path . $input_file_name_prefix;
600              
601             # *.pl input files may generate *.o, *.a, *.so, *.exe, and/or non-suffix output files
602             if ( $input_file_name =~ /[.]pl$/xms ) {
603             if ( $modes->{subcompile} eq 'ASSEMBLE' ) {
604              
605             # NEED ANSWER: does Micro$oft Windows use *.lib file extension (suffix) for both *.o and *.a assembled object files?
606             # but does that only apply when using the M$ VC++ compiler? so does it apply here?
607             # apply answer to ARCHIVE mode elsif block immediately below; and also for ASSEMBLE & ARCHIVE blocks in *.pm else block below that;
608             # ask similar question for *.so in *NIX vs *.dll in M$, apply to .so elsif blocks below and $filename_suffixes_supported in script/rperl
609             # if ( $OSNAME eq 'MSWin32' ) {
610             # $output_file_name_groups->[$i]->{LIB} = $output_file_name_path_prefix . $filename_suffixes_supported->{OUTPUT_BINARY}->{O}->[1];
611             # }
612             # *NIX uses *.o file extension (suffix) for assembled object files
613             # else {
614             $output_file_name_groups->[$i]->{O} = $output_file_name_path_prefix . $filename_suffixes_supported->{OUTPUT_BINARY}->{O}->[0];
615              
616             # }
617             }
618              
619             elsif ( $modes->{subcompile} eq 'ARCHIVE' ) {
620             $output_file_name_groups->[$i]->{O} = $output_file_name_path_prefix . $filename_suffixes_supported->{OUTPUT_BINARY}->{O}->[0];
621             $output_file_name_groups->[$i]->{_O_label} = ' (temporary)';
622             $output_file_name_groups->[$i]->{A} = $output_file_name_path_prefix . $filename_suffixes_supported->{OUTPUT_BINARY}->{A}->[0];
623             }
624             elsif ( $modes->{subcompile} eq 'SHARED' ) {
625             $output_file_name_groups->[$i]->{SO} = $output_file_name_path_prefix . $filename_suffixes_supported->{OUTPUT_BINARY}->{SO}->[0];
626             }
627             elsif (
628             ( $modes->{subcompile} eq 'STATIC' )
629             or ( $modes->{subcompile} eq 'DYNAMIC' )
630             or (( $modes->{subcompile} eq 'OFF' )
631             and ( ( $modes->{compile} eq 'PARSE' )
632             or ( $modes->{compile} eq 'GENERATE' )
633             or ( $modes->{compile} eq 'SAVE' )
634             or ( $modes->{compile} eq 'SUBCOMPILE' ) )
635             )
636             )
637             {
638             # Micro$oft Windows uses *.exe file extension (suffix) for compiled executables
639             if ( $OSNAME eq 'MSWin32' ) {
640             if ( $modes->{parallel} eq 'OFF' ) {
641             $output_file_name_groups->[$i]->{EXE} = $output_file_name_path_prefix . $filename_suffixes_supported->{OUTPUT_BINARY}->{EXE}->[1];
642             }
643             elsif ( $modes->{parallel} eq 'OPENMP' ) {
644             $output_file_name_groups->[$i]->{OPENMP_EXE}
645             = $output_file_name_path_prefix . $filename_suffixes_supported->{OUTPUT_BINARY}->{OPENMP_EXE}->[1];
646             }
647             }
648              
649             # traditionally, *NIX has no file extension (suffix) for compiled executables, non-suffix
650             else {
651             if ( $modes->{parallel} eq 'OFF' ) {
652             $output_file_name_groups->[$i]->{EXE} = $output_file_name_path_prefix . $filename_suffixes_supported->{OUTPUT_BINARY}->{EXE}->[0];
653             }
654             elsif ( $modes->{parallel} eq 'OPENMP' ) {
655             $output_file_name_groups->[$i]->{OPENMP_EXE}
656             = $output_file_name_path_prefix . $filename_suffixes_supported->{OUTPUT_BINARY}->{OPENMP_EXE}->[0];
657             }
658             }
659             }
660              
661             # NEED ANSWER: allow this subroutine to be called even when we return empty results?
662             else {
663             die "ERROR EAR18: Invalid compile mode '"
664             . $modes->{compile}
665             . "' and/or subcompile mode '"
666             . $modes->{subcompile}
667             . "' command-line arguments specified, dying\n";
668             }
669             }
670             else { # *.pm input files may generate *.o, *.a, *.so, and/or *.pmc output files
671             if ( $modes->{subcompile} eq 'ASSEMBLE' ) {
672             $output_file_name_groups->[$i]->{O} = $output_file_name_path_prefix . $filename_suffixes_supported->{OUTPUT_BINARY}->{O}->[0];
673             }
674             elsif ( $modes->{subcompile} eq 'ARCHIVE' ) {
675             $output_file_name_groups->[$i]->{O} = $output_file_name_path_prefix . $filename_suffixes_supported->{OUTPUT_BINARY}->{O}->[0];
676             $output_file_name_groups->[$i]->{_O_label} = ' (temporary)';
677             $output_file_name_groups->[$i]->{A} = $output_file_name_path_prefix . $filename_suffixes_supported->{OUTPUT_BINARY}->{A}->[0];
678             }
679             elsif ( $modes->{subcompile} eq 'SHARED' ) {
680             $output_file_name_groups->[$i]->{SO} = $output_file_name_path_prefix . $filename_suffixes_supported->{OUTPUT_BINARY}->{SO}->[0];
681             }
682             elsif ( $modes->{subcompile} eq 'STATIC' ) {
683              
684             # DEV NOTE: correlates to errors EAR* in script/rperl
685             die 'ERROR EAR15: Incompatible command-line arguments provided, both --static subcompile mode flag and *.pm Perl module input file, dying'
686             . "\n";
687             }
688             elsif (
689             ( $modes->{subcompile} eq 'DYNAMIC' )
690             or (( $modes->{subcompile} eq 'OFF' )
691             and ( ( $modes->{compile} eq 'PARSE' )
692             or ( $modes->{compile} eq 'GENERATE' )
693             or ( $modes->{compile} eq 'SAVE' )
694             or ( $modes->{compile} eq 'SUBCOMPILE' ) )
695             )
696             )
697             {
698             $output_file_name_groups->[$i]->{PMC} = $output_file_name_path_prefix . $filename_suffixes_supported->{OUTPUT_SOURCE}->{PMC}->[0];
699             }
700              
701             # NEED ANSWER: allow this subroutine to be called even when we return empty results?
702             else {
703             die "ERROR EAR18: Invalid compile mode '"
704             . $modes->{compile}
705             . "' and/or subcompile mode '"
706             . $modes->{subcompile}
707             . "' command-line arguments specified, dying\n";
708             }
709             }
710              
711             # all CPP ops modes require CPP output files; H output files may optionally be generated as needed
712             if ( $modes->{ops} eq 'CPP' ) {
713             $output_file_name_groups->[$i]->{CPP} = $output_file_name_path_prefix . $filename_suffixes_supported->{OUTPUT_SOURCE}->{CPP}->[0];
714             $output_file_name_groups->[$i]->{H} = $output_file_name_path_prefix . $filename_suffixes_supported->{OUTPUT_SOURCE}->{H}->[0];
715             $output_file_name_groups->[$i]->{_H_label} = ' (if needed)';
716             if ( $modes->{parallel} eq 'OPENMP' ) {
717             $output_file_name_groups->[$i]->{OPENMP_CPP} = $output_file_name_path_prefix . $filename_suffixes_supported->{OUTPUT_SOURCE}->{OPENMP_CPP}->[0];
718             }
719             }
720              
721             # RPerl::diag('in Compiler::generate_output_file_names(), bottom of loop ' . $i . ' of ' . ($input_files_count - 1) . ", have \$output_file_name_groups->[$i] = \n" . Dumper( $output_file_name_groups->[$i] ) . "\n");
722             }
723              
724             return $output_file_name_groups;
725             };
726              
727             # Write Source Code Files To File System
728             our void $save_source_files = sub {
729             ( my string_hashref $source_group, my string_hashref $file_name_group, my string_hashref $modes ) = @_;
730              
731             # RPerl::diag( q{in Compiler::save_source_files(), received $source_group =} . "\n" . Dumper($source_group) . "\n" );
732             # RPerl::diag( q{in Compiler::save_source_files(), received $file_name_group =} . "\n" . Dumper($file_name_group) . "\n" );
733             # RPerl::diag( 'in Compiler::save_source_files(), received $modes =' . "\n" . Dumper($modes) . "\n" );
734             # RPerl::diag( 'in Compiler::save_source_files(), received $modes->{_symbol_table} =' . "\n" . Dumper($modes->{_symbol_table}) . "\n" );
735             # RPerl::diag( "\n" . 'in Compiler::save_source_files(), received $modes->{subcompile} =' . "\n" . Dumper($modes->{subcompile}) . "\n" );
736              
737             foreach my string $suffix_key ( sort keys %{$source_group} ) {
738             if ( ( substr $suffix_key, 0, 1 ) eq '_' ) { next; }
739             if ( ( not exists $file_name_group->{$suffix_key} )
740             or ( not defined $file_name_group->{$suffix_key} )
741             or ( $file_name_group->{$suffix_key} eq q{} ) )
742             {
743             croak("\nERROR ECOCOFI00, COMPILER, SAVE OUTPUT FILES: Expecting file name for suffix '$suffix_key', but received empty or no value, croaking");
744             }
745             }
746              
747             # CPPOPS POST-PROCESSING: set H paths in CPP files & finally create PMC file, as needed
748             if ( $modes->{ops} eq 'CPP' ) {
749             RPerl::verbose('SAVE PHASE 0: Final file modifications... ');
750              
751             $source_group->{CPP} = post_processor_cpp__header_or_cpp_path( $source_group->{CPP}, $file_name_group->{H} );
752              
753             # MODULE POST-PROCESSING
754             if ( $modes->{_input_file_name} =~ /[.]pm$/xms ) {
755              
756             $source_group = post_processor_cpp__types_change( $source_group, $modes );
757              
758             post_processor_cpp__pmc_generate( $source_group, $file_name_group, $modes );
759             }
760             RPerl::verbose( ' done.' . "\n" );
761             }
762              
763             RPerl::verbose('SAVE PHASE 1: Format & write files to disk...');
764              
765             # RPerl::diag( 'in Compiler::save_source_files(), have [sort keys %{$source_group}] = ' . Dumper([sort keys %{$source_group}]) . "\n" );
766             # RPerl::diag( 'in Compiler::save_source_files(), have $source_group->{H} = ' . Dumper($source_group->{H}) . "\n" );
767             # RPerl::diag( 'in Compiler::save_source_files(), have $source_group = ' . Dumper($source_group) . "\n" );
768              
769             # foreach my string $suffix_key ( sort keys %{$file_name_group} ) { ## no critic qw(ProhibitPostfixControls) # SYSTEM SPECIAL 6: PERL CRITIC FILED ISSUE #639, not postfix foreach or if
770             foreach my string $suffix_key ( sort keys %{$source_group} ) { ## no critic qw(ProhibitPostfixControls) # SYSTEM SPECIAL 6: PERL CRITIC FILED ISSUE #639, not postfix foreach or if
771             if ( ( substr $suffix_key, 0, 1 ) eq '_' ) { next; }
772             if ( ( not exists $source_group->{$suffix_key} )
773             or ( not defined $source_group->{$suffix_key} )
774             or ( $source_group->{$suffix_key} eq q{} ) )
775             {
776             croak("\nERROR ECOCOFI05, COMPILER, SAVE OUTPUT FILES: Expecting source code for suffix '$suffix_key', but received empty or no value, croaking");
777             }
778             my filehandleref $SOURCE_FILE_HANDLE;
779             my string $file_name = $file_name_group->{$suffix_key};
780             my string $source = $source_group->{$suffix_key};
781              
782             if ( $file_name eq '_TEMPFILE' ) {
783             ( $SOURCE_FILE_HANDLE, $file_name )
784             = tempfile( 'tempfileXXXX', SUFFIX => ( lc $suffix_key ), UNLINK => 1, TMPDIR => 1 );
785              
786             print {$SOURCE_FILE_HANDLE} $source
787             or croak("\nERROR ECOCOFI06, COMPILER, FILE SYSTEM: Attempting to save new file '$file_name', cannot write to file,\ncroaking: $OS_ERROR");
788              
789             close $SOURCE_FILE_HANDLE
790             or croak("\nERROR ECOCOFI09, COMPILER, FILE SYSTEM: Attempting to save new file '$file_name', cannot close file,\ncroaking: $OS_ERROR");
791             }
792             else {
793             # overwrite existing file
794             if ( -f $file_name ) {
795             unlink $file_name
796             or croak(
797             "\nERROR ECOCOFI07, COMPILER, FILE SYSTEM: Attempting to save new file '$file_name', cannot delete existing file,\ncroaking: $OS_ERROR");
798             }
799              
800             open $SOURCE_FILE_HANDLE, '>', $file_name
801             or
802             croak("\nERROR ECOCOFI08, COMPILER, FILE SYSTEM: Attempting to save new file '$file_name', cannot open file for writing,\ncroaking: $OS_ERROR");
803              
804             print {$SOURCE_FILE_HANDLE} $source
805             or croak("\nERROR ECOCOFI06, COMPILER, FILE SYSTEM: Attempting to save new file '$file_name', cannot write to file,\ncroaking: $OS_ERROR");
806              
807             close $SOURCE_FILE_HANDLE
808             or croak("\nERROR ECOCOFI09, COMPILER, FILE SYSTEM: Attempting to save new file '$file_name', cannot close file,\ncroaking: $OS_ERROR");
809             }
810              
811             # format output code
812             if ( ( $suffix_key eq 'PMC' ) or ( $suffix_key eq 'EXE' ) ) {
813             my string $perltidy_path = undef;
814             $perltidy_path = can_run('perltidy'); # DEV NOTE: comment this line to disable perltidy
815             if ( defined $perltidy_path ) {
816             # system $perltidy_path, '-pbp', '--ignore-side-comment-lengths', '--converge', '-l=160', '-b', '-nst', q{-bext='/'}, '-q', $file_name;
817             system $perltidy_path, '-pbp', '--ignore-side-comment-lengths', '--converge', '-l=' . RPerl::Generator::PERLTIDY_LINE_WIDTH(), '-b', '-nst', q{-bext='/'}, '-q', $file_name;
818             }
819             else {
820             RPerl::warning(
821             "\n" . 'WARNING WCOCOFO00, COMPILER, PERL CODE FORMATTING: Perltidy command `perltidy` not found, abandoning formatting' . "\n" );
822             }
823             }
824             elsif ( ( $suffix_key eq 'H' ) or ( $suffix_key eq 'CPP' ) ) {
825             my string $astyle_path = can_run('astyle');
826             if ( defined $astyle_path ) {
827              
828             # system $astyle_path, '-q', $file_name;
829             # don't insert extra newlines, which causes accessors, mutators, and ops_types reporting subroutines to be broken into multiple lines
830             system $astyle_path, '-q', '--keep-one-line-blocks', '--keep-one-line-statements', $file_name;
831             if ( -f $file_name . '.orig' ) {
832             unlink( $file_name . '.orig' )
833             or croak( "\n"
834             . 'ERROR ECOCOFI10, COMPILER, FILE SYSTEM: Cannot delete Artistic Style original file ' . q{'}
835             . $file_name . '.orig' . q{'} . ',' . "\n"
836             . 'croaking:'
837             . $OS_ERROR );
838             }
839             }
840             else {
841             RPerl::warning( 'WARNING WCOCOFO01, COMPILER, C++ CODE FORMATTING: Artistic Style command `astyle` not found, abandoning formatting' . "\n" );
842             }
843             }
844             }
845              
846             RPerl::verbose( ' done.' . "\n" );
847             };
848              
849             # replace __NEED_HEADER_PATH or __NEED_CPP_PATH with proper C++ header path
850             our string $post_processor_cpp__header_or_cpp_path = sub {
851             ( my string $source_CPP, my string $file_path ) = @_;
852              
853             # remove leading '.\' or './' if present
854             if ( $OSNAME eq 'MSWin32' ) {
855             if ( ( substr $file_path, 0, 2 ) eq q{.\\} ) {
856             substr $file_path, 0, 2, q{};
857             }
858             }
859             else {
860             if ( ( substr $file_path, 0, 2 ) eq './' ) {
861             substr $file_path, 0, 2, q{};
862             }
863             }
864              
865             $file_path = post_processor_cpp__lib_path_delete($file_path);
866              
867             # DEV NOTE, CORRELATION #rp033: deferred, finally set path to H module header file in CPP module file
868             $source_CPP =~ s/__NEED_HEADER_PATH/$file_path/gxms;
869             $source_CPP =~ s/__NEED_CPP_PATH/$file_path/gxms;
870             return $source_CPP;
871             };
872              
873             # remove leading library path if present, because it should already be enabled in RPerl/Inline.pm via -Ifoo subcompiler argument
874             our string $post_processor_cpp__lib_path_delete = sub {
875             ( my string $path ) = @_;
876              
877             # DEV NOTE: sometimes MS Windows OS has forward slashes in the 'blib/lib/' part of the path, so we do not differentiate by OS
878             if ( ( substr $path, 0, 4 ) eq 'lib\\' ) {
879             substr $path, 0, 4, q{};
880             }
881             # elsif ( ( substr $path, 0, 5 ) eq '\\lib\\' ) { # NEED ANSWER: same question as below
882             # substr $path, 0, 5, q{};
883             # }
884             elsif ( ( substr $path, 0, 6 ) eq '.\\lib\\' ) {
885             substr $path, 0, 6, q{};
886             }
887             elsif ( ( substr $path, 0, 9 ) eq 'blib\\lib\\' ) {
888             substr $path, 0, 9, q{};
889             }
890             # elsif ( ( substr $path, 0, 10 ) eq '\\blib\\lib\\' ) { # NEED ANSWER: same question as below
891             # substr $path, 0, 10, q{};
892             # }
893             elsif ( ( substr $path, 0, 11 ) eq '.\\blib\\lib\\' ) {
894             substr $path, 0, 11, q{};
895             }
896             elsif ( ( substr $path, 0, 4 ) eq 'lib/' ) {
897             substr $path, 0, 4, q{};
898             }
899             # elsif ( ( substr $path, 0, 5 ) eq '/lib/' ) { # NEED ANSWER: is there ever a case where '/lib/' would appear instead of 'lib/' or './lib/' ???
900             # substr $path, 0, 5, q{};
901             # }
902             elsif ( ( substr $path, 0, 6 ) eq './lib/' ) {
903             substr $path, 0, 6, q{};
904             }
905             elsif ( ( substr $path, 0, 9 ) eq 'blib/lib/' ) {
906             substr $path, 0, 9, q{};
907             }
908             # elsif ( ( substr $path, 0, 10 ) eq '/blib/lib/' ) { # NEED ANSWER: same question as above
909             # substr $path, 0, 10, q{};
910             # }
911             elsif ( ( substr $path, 0, 11 ) eq './blib/lib/' ) {
912             substr $path, 0, 11, q{};
913             }
914              
915             return $path;
916             };
917              
918             # replace hard-coded PERLOPS_PERLTYPES with CPPOPS_*TYPES
919             our string_hashref $post_processor_cpp__types_change = sub {
920             ( my string_hashref $source_group, my string_hashref $modes ) = @_;
921             my string $mode_tagline = $modes->{ops} . 'OPS_' . $modes->{types} . 'TYPES';
922             if ( exists $source_group->{H} ) {
923             $source_group->{H} =~ s/PERLOPS_PERLTYPES/$mode_tagline/gxms;
924             }
925             if ( exists $source_group->{CPP} ) {
926             $source_group->{CPP} =~ s/PERLOPS_PERLTYPES/$mode_tagline/gxms;
927             }
928             return $source_group;
929             };
930              
931             # remove Perl comments
932             our string $post_processor_perl__comments_whitespace_delete = sub {
933             ( my string $input_source_code ) = @_;
934              
935             my string_arrayref $input_source_code_split = [ ( split /\n/xms, $input_source_code ) ];
936             my string_arrayref $input_source_code_split_tmp = [];
937              
938             my boolean $inside_comment = 0;
939             my boolean $inside_string = 0;
940             my boolean $inside_heredoc = 0;
941             my boolean $inside_indent;
942             my string $open_quote_string;
943             my string $open_quote_heredoc;
944             foreach my string $input_source_code_line ( @{$input_source_code_split} ) {
945              
946             # RPerl::diag( 'in Compiler::post_processor_perl__comments_whitespace_delete(), have $input_source_code_line = ' . q{'} . $input_source_code_line . q{'} . "\n" );
947             # RPerl::diag( 'in C::ppp__cwd(), $iscl = ' . q{'} . $input_source_code_line . q{'} . "\n" );
948             if ($inside_comment) {
949             if ( $input_source_code_line =~ m/^=cut$/xms ) { $inside_comment = 0; next; } # delete end of multi-line POD =COMMENT
950             next; # delete middle of multi-line POD =COMMENT
951             }
952             if ($inside_heredoc) {
953             if ( $input_source_code_line eq $open_quote_heredoc ) { $inside_heredoc = 0; }
954             push @{$input_source_code_split_tmp}, $input_source_code_line;
955             next;
956             }
957             if ( $input_source_code_line =~ m/^\s*$/xms ) { next; } # delete blank or all-whitespace line
958             if ( $input_source_code_line =~ m/^\s*[#][^#!]/xms ) { next; } # delete whole-line # COMMENT
959             if ( $input_source_code_line =~ m/^=\w+/xms ) { $inside_comment = 1; next; } # delete beginning of multi-line POD =COMMENT
960              
961             $inside_indent = 1;
962              
963             # delete partial-line & multi-line comments, properly handling strings which contain comment characters
964             my string $input_source_code_line_tmp = q{};
965             my string $current_character;
966             my string $next_character;
967             my boolean $advance_one = 0;
968             for my integer $i ( 0 .. ( ( length $input_source_code_line ) - 1 ) ) {
969             $current_character = substr $input_source_code_line, $i, 1;
970             if ( ($inside_indent) and ( $current_character !~ m/[ \t]/xms ) ) {
971             $inside_indent = 0;
972             }
973              
974             # advance one extra character for q{ OR #! OR ##
975             if ($advance_one) {
976             $advance_one--;
977             $input_source_code_line_tmp .= $current_character;
978             next;
979             }
980             if ( not $inside_string ) {
981             if ( $current_character eq '#' ) {
982             $next_character = substr $input_source_code_line, ( $i + 1 ), 1;
983             if ( ( $next_character eq '!' ) or ( $next_character eq '#' ) ) { $advance_one = 1; } # do not delete shebang #! or critics ##
984             else { last; } # delete partial-line # COMMENT
985             }
986             elsif ( $current_character eq q{'} ) {
987             $inside_string = 1;
988             $open_quote_string = q{'};
989             }
990             elsif ( $current_character eq q{"} ) {
991             $inside_string = 1;
992             $open_quote_string = q{"};
993             }
994             elsif ( $current_character eq 'q' ) {
995             $next_character = substr $input_source_code_line, ( $i + 1 ), 1;
996             if ( $next_character eq '{' ) {
997             $inside_string = 1;
998             $advance_one = 1;
999             $open_quote_string = 'q{';
1000             }
1001             }
1002             elsif ( $current_character eq '<' ) {
1003             $next_character = substr $input_source_code_line, ( $i + 1 ), 1;
1004             if ( $next_character eq '<' ) {
1005             $inside_heredoc = 1;
1006             $open_quote_heredoc = substr $input_source_code_line, ( $i + 2 );
1007             if ( ( substr $open_quote_heredoc, 0, 1 ) eq q{'} ) { substr $open_quote_heredoc, 0, 1, q{}; }
1008             if ( ( substr $open_quote_heredoc, 0, 1 ) eq q{"} ) { substr $open_quote_heredoc, 0, 1, q{}; }
1009             $open_quote_heredoc =~ s/\s+$//xms; # delete trailing whitespace after heredoc open quote and semicolon
1010             if ( ( substr $open_quote_heredoc, -1, 1 ) eq q{;} ) { substr $open_quote_heredoc, -1, 1, q{}; }
1011             $open_quote_heredoc =~ s/\s+$//xms; # delete whitespace between heredoc open quote and semicolon
1012             if ( ( substr $open_quote_heredoc, -1, 1 ) eq q{'} ) { substr $open_quote_heredoc, -1, 1, q{}; }
1013             if ( ( substr $open_quote_heredoc, -1, 1 ) eq q{"} ) { substr $open_quote_heredoc, -1, 1, q{}; }
1014             }
1015             }
1016              
1017             # delete extra whitespace inserted by Perl::Tidy
1018             elsif ( ( not $inside_indent ) and ( $current_character =~ m/[ \t]/xms ) ) {
1019             $next_character = substr $input_source_code_line, ( $i + 1 ), 1;
1020             if ( $next_character =~ m/[ \t]/xms ) { next; } # delete extra whitespace
1021             }
1022             }
1023             else { # $inside_string
1024             if ( ( $current_character eq q{'} ) and ( $open_quote_string eq q{'} ) ) { $inside_string = 0; }
1025             elsif ( ( $current_character eq q{"} ) and ( $open_quote_string eq q{"} ) ) { $inside_string = 0; }
1026             elsif ( ( $current_character eq '}' ) and ( $open_quote_string eq 'q{' ) ) { $inside_string = 0; }
1027             }
1028             $input_source_code_line_tmp .= $current_character;
1029             }
1030             $input_source_code_line = $input_source_code_line_tmp;
1031              
1032             $input_source_code_line =~ s/[ \t]+$//xms; # delete trailing whitespace, if present
1033              
1034             push @{$input_source_code_split_tmp}, $input_source_code_line;
1035             }
1036             return join "\n", @{$input_source_code_split_tmp};
1037             };
1038              
1039             # remove C++ comments
1040             # NEED TEST: create full tests for this subroutine
1041             our string $post_processor_cpp__comments_whitespace_delete = sub {
1042             ( my string $input_source_code ) = @_;
1043              
1044             my string_arrayref $input_source_code_split = [ ( split /\n/xms, $input_source_code ) ];
1045             my string_arrayref $input_source_code_split_tmp = [];
1046              
1047             my boolean $inside_comment = 0;
1048             my boolean $inside_string = 0;
1049             my string $open_quote;
1050             foreach my string $input_source_code_line ( @{$input_source_code_split} ) {
1051             if ($inside_comment) {
1052             if ( $input_source_code_line =~ m!\*/!xms ) {
1053             $input_source_code_line =~ s!^(.*\*/)!!xms; # delete end of multi-line /* COMMENT */
1054             $inside_comment = 0;
1055             }
1056             else { next; } # delete middle of multi-line /* COMMENT */
1057             }
1058             if ( $input_source_code_line =~ m/^\s*$/xms ) { next; } # delete blank or all-whitespace line
1059             if ( $input_source_code_line =~ m!^\s*//!xms ) { next; } # delete whole-line // COMMENT
1060             if ( $input_source_code_line =~ m!^\s*/\*.*\*/\s*$!xms ) { next; } # delete whole-line /* COMMENT */
1061              
1062             # delete partial-line & multi-line comments, properly handling strings which contain comment characters
1063             my string $input_source_code_line_tmp = q{};
1064             my string $current_character;
1065             my string $next_character;
1066             my boolean $advance_one = 0;
1067             for my integer $i ( 0 .. ( ( length $input_source_code_line ) - 1 ) ) {
1068             $current_character = substr $input_source_code_line, $i, 1;
1069              
1070             # advance one extra character for \' or \" or /* or */
1071             if ($advance_one) {
1072             $advance_one = 0;
1073             $input_source_code_line_tmp .= $current_character;
1074             next;
1075             }
1076             if ( not $inside_string ) {
1077             if ($inside_comment) {
1078             if ( $current_character eq '*' ) {
1079             $next_character = substr $input_source_code_line, ( $i + 1 ), 1;
1080             if ( $next_character eq '/' ) {
1081              
1082             # delete end of partial-line /* COMMENT */
1083             $advance_one = 1;
1084             $inside_comment = 0;
1085             next;
1086             }
1087             }
1088             else { next; } # delete middle of partial-line /* COMMENT */
1089             }
1090             else { # not $inside_comment
1091             if ( $current_character eq '/' ) {
1092             $next_character = substr $input_source_code_line, ( $i + 1 ), 1;
1093             if ( $next_character eq '/' ) { last; } # delete partial-line // COMMENT
1094             elsif ( $next_character eq '*' ) {
1095              
1096             # delete beginning of partial-line or multi-line /* COMMENT */
1097             $advance_one = 1;
1098             $inside_comment = 1;
1099             next;
1100             }
1101             }
1102             elsif ( $current_character eq q{'} ) {
1103             $inside_string = 1;
1104             $open_quote = q{'};
1105             }
1106             elsif ( $current_character eq q{"} ) {
1107             $inside_string = 1;
1108             $open_quote = q{"};
1109             }
1110              
1111             # NEED UPGRADE: can not delete extra whitespace characters here, because it destroys indentation
1112             # elsif ( $current_character =~ m/[ \t]/ ) {
1113             # $next_character = substr $input_source_code_line, ( $i + 1 ), 1;
1114             # if ( $next_character =~ m/[ \t]/ ) { next; } # delete extra whitespace
1115             # }
1116             }
1117             }
1118             else { # $inside_string
1119             if ( ( $current_character eq q{'} ) and ( $open_quote eq q{'} ) ) { $inside_string = 0; }
1120             elsif ( ( $current_character eq q{"} ) and ( $open_quote eq q{"} ) ) { $inside_string = 0; }
1121             elsif ( $current_character eq '\\' ) {
1122             $next_character = substr $input_source_code_line, ( $i + 1 ), 1;
1123              
1124             # backslash-escaped quotes do not close a string
1125             if ( ( $next_character eq q{'} ) and ( $open_quote eq q{'} ) ) { $advance_one = 1; }
1126             if ( ( $next_character eq q{"} ) and ( $open_quote eq q{"} ) ) { $advance_one = 1; }
1127             }
1128             }
1129             $input_source_code_line_tmp .= $current_character;
1130             }
1131             $input_source_code_line = $input_source_code_line_tmp;
1132              
1133             $input_source_code_line =~ s/[ \t]+$//xms; # delete trailing whitespace, if present
1134              
1135             push @{$input_source_code_split_tmp}, $input_source_code_line;
1136             }
1137             return join "\n", @{$input_source_code_split_tmp};
1138             };
1139              
1140             # remove unnecessary absolute paths
1141             #our string $post_processor__absolute_path_delete = sub { # DEV NOTE: must have non-typed sub header to be called from BEGIN block in t/10_precompiled_oo_inherit.t
1142             sub post_processor__absolute_path_delete {
1143 7637     7637 0 1850193 ( my string $input_path ) = @_;
1144              
1145             # RPerl::diag( 'in Compiler::post_processor__absolute_path_delete(), received $input_path = ' . $input_path . "\n" );
1146              
1147 7637 50       31405 if ( $OSNAME eq 'MSWin32' ) {
1148 0         0 $input_path =~ s/\\/\//gxms;
1149             # RPerl::diag( 'in Compiler::post_processor__absolute_path_delete(), Windows OS detected, have possibly-reformatted $input_path = ' . $input_path . "\n" );
1150             }
1151              
1152 7637         41738 my string $current_working_directory = getcwd;
1153              
1154             # RPerl::diag( 'in Compiler::post_processor__absolute_path_delete(), have $current_working_directory = ' . $current_working_directory . "\n" );
1155              
1156 7637 100       23530 if ( ( substr $input_path, 0, ( length $current_working_directory ) ) eq $current_working_directory ) {
1157 6406         24149 return substr $input_path, ( ( length $current_working_directory ) + 1 );
1158             }
1159             else {
1160 1231         4119 return $input_path;
1161             }
1162             }
1163              
1164             # generate PMC file
1165             our string $post_processor_cpp__pmc_generate = sub {
1166             ( my string_hashref $source_group, my string_hashref $file_name_group, my string_hashref $modes ) = @_;
1167              
1168             # NEED FIX WIN32: handle back-slash for Win32 instead of forward-slash only for *NIX
1169             my string $cpp_file_path = $file_name_group->{CPP};
1170             $cpp_file_path = post_processor_cpp__lib_path_delete($cpp_file_path);
1171              
1172             # DEV NOTE: barely-documented Inline::CPP bug, must have leading './' if no other directories in path
1173             if ( $cpp_file_path !~ /\// ) {
1174             if ( $OSNAME eq 'MSWin32' ) {
1175             $cpp_file_path .= q{.\\};
1176             }
1177             else {
1178             $cpp_file_path .= q{./};
1179             }
1180             }
1181              
1182             # DEV NOTE: only generate PMC output file in dynamic (default) subcompile mode
1183             if ( $modes->{subcompile} eq 'DYNAMIC' ) {
1184             if ( ( exists $source_group->{PMC} ) and ( defined $source_group->{PMC} ) and ( $source_group->{PMC} ne q{} ) ) {
1185              
1186             # RPerl::diag( q{in Compiler::save_source_files(), have $source_group = } . Dumper($source_group) . "\n" );
1187             die 'ERROR ECOCOFI01, COMPILER, SAVE OUTPUT FILES, MODULE TEMPLATE COPY: Received non-empty PMC source, dying' . "\n";
1188             }
1189              
1190             # RPerl::diag( q{in Compiler::save_source_files(), have %INC = } . Dumper(\%INC) . "\n" );
1191             # RPerl::diag( q{in Compiler::save_source_files(), have @INC = } . Dumper(\@INC) . "\n" );
1192             # RPerl::diag( q{in Compiler::save_source_files(), have $source_group->{_package_names_underscores} = } . Dumper($source_group->{_package_names_underscores}) . "\n" );
1193             # RPerl::diag( q{in Compiler::save_source_files(), have $source_group->{_package_names} = } . Dumper($source_group->{_package_names}) . "\n" );
1194              
1195             my string_arrayref $module_names_split = [ ( split /\n/, $source_group->{_package_names} ) ];
1196             my string_arrayref $module_names_underscores_split = [ ( split /\n/, $source_group->{_package_names_underscores} ) ];
1197              
1198             # RPerl::diag( q{in Compiler::save_source_files(), have $module_names_split = } . Dumper($module_names_split) . "\n" );
1199              
1200             my integer $module_count = scalar @{$module_names_split};
1201             my string $module_name = shift @{$module_names_split};
1202             my string $module_name_underscores = shift @{$module_names_underscores_split};
1203             my integer $i = 0;
1204              
1205             # deferred, finally insert constants shims
1206             # RPerl::diag('in Compiler::save_source_files(), have $source_group->{_H_constants_shims}->{$module_name_underscores} = ' . $source_group->{_H_constants_shims}->{$module_name_underscores} . "\n");
1207              
1208             while ( defined $module_name_underscores ) {
1209              
1210             # RPerl::diag( q{in Compiler::save_source_files(), have $cpp_file_path = } . $cpp_file_path . "\n" );
1211             # RPerl::diag( q{in Compiler::save_source_files(), have $module_name_underscores = } . $module_name_underscores . "\n" );
1212              
1213             # utilize modified copies of Module PMC template file
1214             my string $module_pmc_filename_manual;
1215             if ( $module_count == 1 ) {
1216             $module_pmc_filename_manual = $RPerl::INCLUDE_PATH . '/RPerl/CompileUnit/Module.pmc.CPPOPS_DUALTYPES_TEMPLATE';
1217             }
1218             else {
1219             if ( $i == ( $module_count - 1 ) ) {
1220             $module_pmc_filename_manual = $RPerl::INCLUDE_PATH . '/RPerl/CompileUnit/Module.pmc.CPPOPS_DUALTYPES_TEMPLATE_MONOLITH';
1221             }
1222             else {
1223             $module_pmc_filename_manual = $RPerl::INCLUDE_PATH . '/RPerl/CompileUnit/Module.pmc.CPPOPS_DUALTYPES_TEMPLATE_MONOLITH_SECONDARY';
1224             }
1225             }
1226              
1227             # RPerl::diag( 'in Compiler::save_source_files(), have $module_pmc_filename_manual = ' . $module_pmc_filename_manual . "\n" );
1228             # RPerl::diag( 'in Compiler::save_source_files(), have $source_group->{_PMC_accessors_mutators_shims} = ' . Dumper($source_group->{_PMC_accessors_mutators_shims}) . "\n" );
1229             # RPerl::diag( 'in Compiler::save_source_files(), have $source_group->{_PMC_subroutines_shims} = ' . Dumper($source_group->{_PMC_subroutines_shims}) . "\n" );
1230             # RPerl::diag( 'in Compiler::save_source_files(), have $source_group->{_PMC_includes} = ' . Dumper($source_group->{_PMC_includes}) . "\n" );
1231              
1232             if ( not -f $module_pmc_filename_manual ) {
1233             die 'ERROR ECOCOFI02, COMPILER, SAVE OUTPUT FILES, MODULE TEMPLATE COPY: File not found, ' . q{'}
1234             . $module_pmc_filename_manual . q{'} . "\n"
1235             . ', dying' . "\n";
1236             }
1237              
1238             open my filehandleref $FILE_HANDLE, '<', $module_pmc_filename_manual
1239             or die 'ERROR ECOCOFI03, COMPILER, SAVE OUTPUT FILES, MODULE TEMPLATE COPY: Cannot open file '
1240             . $module_pmc_filename_manual
1241             . ' for reading, '
1242             . $OS_ERROR
1243             . ', dying' . "\n";
1244              
1245             # deferred, finally read in Module PMC template file, replace package name and paths, add accessor/mutator shim methods
1246             my string $file_line;
1247             my string $file_string = q{};
1248             my string $pm_file_path = $file_name_group->{PMC};
1249             chop $pm_file_path; # remove the 'c' from 'pmc' file suffix
1250             while ( $file_line = <$FILE_HANDLE> ) {
1251              
1252             # $file_line =~ s/lib\/RPerl\/CompileUnit\/Module\.cpp/$cpp_file_path/gxms;
1253             $file_line =~ s/RPerl\/CompileUnit\/Module\.cpp/$cpp_file_path/gxms;
1254             $file_line =~ s/RPerl::CompileUnit::Module/$module_name/gxms;
1255             $file_line =~ s/RPerl__CompileUnit__Module/$module_name_underscores/gxms;
1256             if ( $file_line eq
1257             ( '# <<< OO PROPERTIES, ACCESSORS & MUTATORS, SHIMS >>> # <<< CHANGE_ME: add real shims after this line or delete it >>>' . "\n" ) )
1258             {
1259             if ( ( exists $source_group->{_PMC_accessors_mutators_shims}->{$module_name_underscores} )
1260             and ( defined $source_group->{_PMC_accessors_mutators_shims}->{$module_name_underscores} ) )
1261             {
1262             $file_line
1263             = ( substr $file_line, 0, 52 ) . "\n" . $source_group->{_PMC_accessors_mutators_shims}->{$module_name_underscores} . "\n\n";
1264             }
1265             else { $file_line = undef; }
1266             }
1267             elsif (
1268             $file_line eq ( '# <<< OO PROPERTIES, SUBROUTINES, SHIMS >>> # <<< CHANGE_ME: add real shims after this line or delete it >>>' . "\n" ) )
1269             {
1270             if ( ( exists $source_group->{_PMC_subroutines_shims}->{$module_name_underscores} )
1271             and ( defined $source_group->{_PMC_subroutines_shims}->{$module_name_underscores} ) )
1272             {
1273             $file_line = ( substr $file_line, 0, 43 ) . "\n" . $source_group->{_PMC_subroutines_shims}->{$module_name_underscores} . "\n\n";
1274             }
1275             else { $file_line = undef; }
1276             }
1277             elsif ( $file_line eq ( '# <<< CHANGE_ME: add distribution-specific config include here >>>' . "\n" ) ) {
1278             my string $distribution_package = ( split /::/, $source_group->{_package_name} )[0];
1279             $file_line = 'use ' . $distribution_package . '::Config;' . "\n";
1280             }
1281             elsif ( $file_line eq ( '# <<< CHANGE_ME: add user-defined includes here >>>' . "\n" ) ) {
1282             if ( ( exists $source_group->{_PMC_includes}->{$module_name_underscores} )
1283             and ( defined $source_group->{_PMC_includes}->{$module_name_underscores} ) )
1284             {
1285             $file_line = $source_group->{_PMC_includes}->{$module_name_underscores} . "\n\n";
1286             }
1287             else { $file_line = undef; }
1288             }
1289             elsif ( $file_line eq ( ' # <<< CHANGE_ME: enable optional SSE support here >>>' . "\n" ) ) {
1290              
1291             # RPerl::diag( 'in Compiler::save_source_files(), have $modes->{_enable_sse} = ' . Dumper($modes->{_enable_sse}) . "\n" );
1292             if ( ( exists $modes->{_enable_sse} )
1293             and ( defined $modes->{_enable_sse} )
1294             and ( exists $modes->{_enable_sse}->{$pm_file_path} )
1295             and ( defined $modes->{_enable_sse}->{$pm_file_path} )
1296             and $modes->{_enable_sse}->{$pm_file_path} )
1297             {
1298             $file_line = q( $RPerl::Inline::ARGS{optimize} .= ' -mfpmath=sse -msse3'; # enable SSE support) . "\n";
1299             $file_line
1300             .= q( $RPerl::Inline::ARGS{auto_include} = ['#include <immintrin.h>', @{$RPerl::Inline::ARGS{auto_include}}]; # enable SSE support)
1301             . "\n";
1302             }
1303             else { $file_line = undef; }
1304             }
1305             elsif ( $file_line eq ( ' # <<< CHANGE_ME: enable optional GMP support here >>>' . "\n" ) ) {
1306              
1307             # RPerl::diag( 'in Compiler::save_source_files(), have $modes->{_enable_gmp} = ' . Dumper($modes->{_enable_gmp}) . "\n" );
1308             # RPerl::diag( 'in Compiler::save_source_files(), have $pm_file_path = ' . $pm_file_path . "\n" );
1309             if ( ( exists $modes->{_enable_gmp} )
1310             and ( defined $modes->{_enable_gmp} )
1311             and ( exists $modes->{_enable_gmp}->{$pm_file_path} )
1312             and ( defined $modes->{_enable_gmp}->{$pm_file_path} )
1313             and $modes->{_enable_gmp}->{$pm_file_path} )
1314             {
1315             $file_line = q( $RPerl::Inline::ARGS{libs} = '-lgmpxx -lgmp'; # enable GMP support) . "\n";
1316             $file_line
1317             .= q( $RPerl::Inline::ARGS{auto_include} = [ @{ $RPerl::Inline::ARGS{auto_include} }, '#include <gmpxx.h>', '#include <gmp.h>' ]; # enable GMP support)
1318             . "\n";
1319             }
1320             else { $file_line = undef; }
1321             }
1322             if ( defined $file_line ) { $source_group->{PMC} .= $file_line; }
1323             }
1324              
1325             close $FILE_HANDLE
1326             or die 'ERROR ECOCOFI04, COMPILER, SAVE OUTPUT FILES, MODULE TEMPLATE COPY: Cannot close file '
1327             . $module_pmc_filename_manual
1328             . ' after reading, '
1329             . $OS_ERROR
1330             . ', dying' . "\n";
1331              
1332             $module_name = shift @{$module_names_split};
1333             $module_name_underscores = shift @{$module_names_underscores_split};
1334             $i++;
1335             }
1336             }
1337             };
1338              
1339             # Auto-Parallelize from Serial C++ File to Parallel C++ File via Pluto PolyCC & OpenMP
1340             our void $cpp_to_openmp_cpp = sub {
1341             ( my string_hashref $cpp_output_file_name_group, my string_hashref $modes ) = @_;
1342              
1343             RPerl::diag( q{in Compiler::cpp_to_openmp_cpp(), received $cpp_output_file_name_group =} . "\n" . Dumper($cpp_output_file_name_group) . "\n" );
1344              
1345             # RPerl::diag( q{in Compiler::cpp_to_openmp_cpp(), received $modes =} . "\n" . Dumper($modes) . "\n" );
1346              
1347             # RPerl::diag( q{in Compiler::cpp_to_openmp_cpp(), NOT DOING ANYTHING YET} . "\n" );
1348             # return;
1349             # die 'TMP DEBUG';
1350              
1351             # START HERE: modify pluto min/max macros & calls, modify final g++ command
1352             # START HERE: modify pluto min/max macros & calls, modify final g++ command
1353             # START HERE: modify pluto min/max macros & calls, modify final g++ command
1354              
1355             # THEN START HERE, NEED FIX PARALLEL: enable non-pluto min/max sub calls, re-enable prints
1356             RPerl::verbose('PARALLELIZE: Generate OpenMP Code... ');
1357              
1358             my string $polycc_path = can_run('polycc');
1359             if ( not defined $polycc_path ) {
1360             die 'ERROR Exxxxx, COMPILER, PARALLELIZATION: Pluto PolyCC command `polycc` not found, dying';
1361             }
1362             my string $polycc_command
1363             = $polycc_path . q{ } . $cpp_output_file_name_group->{CPP} . ' -o ' . $cpp_output_file_name_group->{OPENMP_CPP} . ' --parallel --tile';
1364              
1365             RPerl::diag( 'in Compiler::cpp_to_openmp_cpp(), have $polycc_command =' . "\n\n" . $polycc_command . "\n" );
1366              
1367             # ACTUALLY RUN POLYCC COMMAND
1368             # my $pid = open3( 0, \*POLYCC_STDOUT, \*POLYCC_STDERR, $polycc_command ); # disable STDIN w/ 0
1369             #
1370             # my $stdout_select;
1371             # my $stderr_select;
1372             # if ( $OSNAME ne 'MSWin32' ) {
1373             # $stdout_select = IO::Select->new();
1374             # $stderr_select = IO::Select->new();
1375             # $stdout_select->add( \*POLYCC_STDOUT );
1376             # $stderr_select->add( \*POLYCC_STDERR );
1377             # }
1378             #
1379              
1380             my string $polycc_command_stdout = q{};
1381             my string $polycc_command_stderr = q{};
1382              
1383             #if ( $OSNAME eq 'MSWin32' || $stdout_select->can_read(0) ) { sysread POLYCC_STDOUT, $polycc_command_stdout, 4096; }
1384             # if ( $OSNAME eq 'MSWin32' || $stderr_select->can_read(0) ) { sysread POLYCC_STDERR, $polycc_command_stderr, 4096; }
1385             # waitpid $pid, 0;
1386             # if ( $OSNAME eq 'MSWin32' || $stdout_select->can_read(0) ) { my $s; sysread POLYCC_STDOUT, $s, 4096; $polycc_command_stdout .= $s; }
1387             # if ( $OSNAME eq 'MSWin32' || $stderr_select->can_read(0) ) { my $s; sysread POLYCC_STDERR, $s, 4096; $polycc_command_stderr .= $s; }
1388              
1389             #my $pid = open3( 0, \*POLYCC_STDOUT, \*POLYCC_STDERR, $polycc_command ); # disable STDIN w/ 0
1390             run3( $polycc_command, \undef, \$polycc_command_stdout, \$polycc_command_stderr );
1391              
1392             my $test_exit_status = $CHILD_ERROR >> 8;
1393              
1394             # RPerl::diag( 'in Compiler::cpp_to_openmp_cpp(), have $CHILD_ERROR = ' . $CHILD_ERROR . "\n" );
1395             # RPerl::diag( 'in Compiler::cpp_to_openmp_cpp(), have $test_exit_status = ' . $test_exit_status . "\n" );
1396              
1397             RPerl::verbose( ' done.' . "\n" );
1398              
1399             # if ($polycc_command_stdout) { RPerl::diag( "===STDOUT=BEGIN===\n" . $polycc_command_stdout . "===STDOUT=END===\n" ); }
1400             # if ($polycc_command_stderr) { RPerl::diag( "===STDERR=BEGIN===\n" . $polycc_command_stderr . "===STDERR=END===\n" ); }
1401             my boolean $polycc_command_stdout_content = ( ( defined $polycc_command_stdout ) and ( $polycc_command_stdout =~ m/[^\s]+/g ) );
1402             my boolean $polycc_command_stderr_content = ( ( defined $polycc_command_stderr ) and ( $polycc_command_stderr =~ m/[^\s]+/g ) );
1403              
1404             if ( $polycc_command_stdout_content or $polycc_command_stderr_content ) {
1405             RPerl::diag("\n");
1406             if ($polycc_command_stdout_content) {
1407             RPerl::diag( '[[[ POLYCC STDOUT ]]]' . "\n\n" . $polycc_command_stdout . "\n" );
1408             }
1409             if ($polycc_command_stderr_content) {
1410             RPerl::diag( '[[[ POLYCC STDERR ]]]' . "\n\n" . $polycc_command_stderr . "\n" );
1411             }
1412             # NEED FIX PARALLEL: actually test polycc output for failure or error messages, etc.
1413             # if ( $test_exit_status == 0 ) { # UNIX process return code 0, success
1414             # RPerl::warning( 'WARNING WCOCOSU00, COMPILER, POLYCC: Pluto PolyCC compiler returned success code but produced output which may indicate an error,' . "\n" . 'please run again with `rperl -D` command or RPERL_DEBUG=1 environmental variable for error messages or other output if none appear above' . "\n" );
1415             # }
1416             }
1417              
1418             if ($test_exit_status) { # UNIX process return code not 0, error
1419             if ( not( $polycc_command_stdout_content or $polycc_command_stderr_content ) ) {
1420             RPerl::diag( "\n" . '[[[ POLYCC STDOUT & STDERR ARE BOTH EMPTY ]]]' . "\n\n" );
1421             }
1422             croak 'ERROR Exxxxx, COMPILER, POLYCC: Pluto PolyCC compiler returned error code,' . "\n"
1423             . 'please run again with `rperl -D` command or RPERL_DEBUG=1 environmental variable for error messages if none appear above,' . "\n"
1424             . 'croaking';
1425             }
1426            
1427             # NEED FIX PARALLEL: temporarily disable all user-defined or non-pluto uses of string 'min' and 'max' within parallel loop
1428              
1429             if (( not -e $cpp_output_file_name_group->{OPENMP_CPP} ) or ( not -f $cpp_output_file_name_group->{OPENMP_CPP} ) or ( not -T $cpp_output_file_name_group->{OPENMP_CPP} )) {
1430             die 'ERROR Exxxxx, COMPILER, PARALLELIZATION: Missing or invalid Pluto PolyCC output file, ' . q{'} . $cpp_output_file_name_group->{OPENMP_CPP} . q{'} . "\n" . ', dying' . "\n";
1431             }
1432              
1433             open my filehandleref $FILE_HANDLE_POLYCC, '<', $cpp_output_file_name_group->{OPENMP_CPP}
1434             or die 'ERROR Exxxxx, COMPILER, PARALLELIZATION: Cannot open Pluto PolyCC output file ' . q{'} . $cpp_output_file_name_group->{OPENMP_CPP} . q{'} . ' for reading,' . $OS_ERROR . ', dying' . "\n";
1435              
1436             # read in file, strip blank lines
1437             my string $file_line_polycc;
1438             my string $string_polycc = q{};
1439             while ( $file_line_polycc = <$FILE_HANDLE_POLYCC> ) {
1440             $file_line_polycc =~ s/min/polyccmin/gxms;
1441             $file_line_polycc =~ s/max/polyccmax/gxms;
1442             $string_polycc .= $file_line_polycc;
1443             }
1444              
1445             close $FILE_HANDLE_POLYCC
1446             or die 'ERROR Exxxxx, COMPILER, PARALLELIZATION: Cannot close file ' . q{'} . $cpp_output_file_name_group->{OPENMP_CPP} . q{'} . ' after reading, ' . $OS_ERROR . ', dying' . "\n";
1447              
1448             open $FILE_HANDLE_POLYCC, '>', $cpp_output_file_name_group->{OPENMP_CPP}
1449             or die 'ERROR Exxxxx, COMPILER, PARALLELIZATION: Cannot open Pluto PolyCC output file ' . q{'} . $cpp_output_file_name_group->{OPENMP_CPP} . q{'} . ' for writing,' . $OS_ERROR . ', dying' . "\n";
1450              
1451             print {$FILE_HANDLE_POLYCC} $string_polycc;
1452              
1453             close $FILE_HANDLE_POLYCC
1454             or die 'ERROR Exxxxx, COMPILER, PARALLELIZATION: Cannot close file ' . q{'} . $cpp_output_file_name_group->{OPENMP_CPP} . q{'} . ' after writing, ' . $OS_ERROR . ', dying' . "\n";
1455             };
1456              
1457             # Sub-Compile from C++-Parsable String to Perl-Linkable XS & Machine-Readable Binary
1458             our void $cpp_to_xsbinary__subcompile = sub {
1459             ( my string_hashref $cpp_output_file_name_group, my string_hashref $modes ) = @_;
1460              
1461             # RPerl::diag( q{in Compiler::cpp_to_xsbinary__subcompile(), received $cpp_output_file_name_group =} . "\n" . Dumper($cpp_output_file_name_group) . "\n" );
1462             # RPerl::diag( q{in Compiler::cpp_to_xsbinary__subcompile(), received $modes =} . "\n" . Dumper($modes) . "\n" );
1463              
1464             if ( ( $modes->{_input_file_name} =~ /[.]pl$/xms ) or ( $modes->{subcompile} ne 'DYNAMIC' ) ) {
1465             RPerl::verbose('SUBCOMPILE: Generate binary... ');
1466              
1467             if ( $modes->{subcompile} eq 'OFF' ) {
1468             croak 'ERROR ECOCOSU00, COMPILER, SUBCOMPILE: Received incorrect subcompile mode OFF while inside subcompile subroutine, croaking';
1469             }
1470             elsif ( ( $modes->{subcompile} ne 'ASSEMBLE' )
1471             and ( $modes->{subcompile} ne 'ARCHIVE' )
1472             and ( $modes->{subcompile} ne 'SHARED' )
1473             and ( $modes->{subcompile} ne 'STATIC' )
1474             and ( $modes->{subcompile} ne 'DYNAMIC' ) )
1475             {
1476             croak 'ERROR ECOCOSU01, COMPILER, SUBCOMPILE: Received invalid subcompile mode ' . q{'}
1477             . $modes->{subcompile} . q{'}
1478             . ' while inside subcompile subroutine, croaking';
1479             }
1480              
1481             my string $subcompile_command = $modes->{CXX};
1482              
1483             if ( ( $modes->{subcompile} eq 'ASSEMBLE' )
1484             or ( $modes->{subcompile} eq 'ARCHIVE' ) )
1485             {
1486             # stop the subcompiler after the assemble phase, output .o file; not in original Inline::CPP subcompile command
1487             $subcompile_command .= q{ } . '-c';
1488             }
1489             elsif (( $modes->{subcompile} eq 'STATIC' )
1490             or ( $modes->{subcompile} eq 'DYNAMIC' ) )
1491             {
1492             # Perl requires pthreads, at least Perls compiled with thread support do; not in original Inline::CPP subcompile command
1493             # NEED ANSWER: test for non-threaded Perl to avoid including pthread support?
1494             $subcompile_command .= q{ } . '-pthread';
1495             }
1496              
1497             my string $ccflags = [ config_re('ccflags') ]->[0];
1498             substr $ccflags, 0, 9, q{}; # remove leading ccflags='
1499             substr $ccflags, -1, 1, q{}; # remove trailing '
1500             $subcompile_command .= q{ } . $ccflags;
1501              
1502             $subcompile_command .= q{ } . '-xc++'; # force C++ language mode
1503              
1504             # using RPerl::BASE_PATH instead of substr $RPerl::INCLUDE_PATH
1505             # if ( ( ( substr $RPerl::INCLUDE_PATH, -4, 4 ) eq '/lib' ) or ( ( substr $RPerl::INCLUDE_PATH, -4, 4 ) eq '\lib' ) ) {
1506             # $subcompile_command .= q{ } . '-I"' . ( substr $RPerl::INCLUDE_PATH, 0, -4 ) . '"'; # remove trailing /lib or \lib
1507             # }
1508              
1509             $subcompile_command .= q{ } . '-I"' . $RPerl::BASE_PATH . '"';
1510             $subcompile_command .= q{ } . '-I"' . $RPerl::INCLUDE_PATH . '"'; # different than original Inline::CPP subcompile command, double-quotes added to encapsulate user-name directories
1511             $subcompile_command .= q{ } . '-Ilib';
1512              
1513             $subcompile_command .= q{ } . $RPerl::Inline::CCFLAGSEX;
1514             $subcompile_command .= q{ } . '-D__' . $modes->{types} . '__TYPES'; # same as #define __PERL__TYPES or #define__CPP__TYPES; don't just use hard-coded $RPerl::TYPES_CCFLAG
1515             $subcompile_command .= q{ } . '-D__TYPE__INTEGER__' . $modes->{type_integer};
1516             $subcompile_command .= q{ } . '-D__TYPE__NUMBER__' . $modes->{type_number};
1517             $subcompile_command .= q{ } . $RPerl::Inline::ARGS{optimize};
1518              
1519             $subcompile_command .= q{ } . '-DVERSION=\"0.00\" -DXS_VERSION=\"0.00\"'; # NEED ANSWER: what does this do?
1520              
1521             my string $cccdlflags = [ config_re('cccdlflags') ]->[0];
1522             substr $cccdlflags, 0, 12, q{}; # remove leading cccdlflags='
1523             substr $cccdlflags, -1, 1, q{}; # remove trailing '
1524             $subcompile_command .= q{ } . $cccdlflags;
1525              
1526             if ( $RPerl::CORE_PATH eq q{} ) {
1527             croak 'ERROR ECOCOSU02, COMPILER, SUBCOMPILE: Perl source code CORE directory or CORE/perl.h file not found in @INC path listing, croaking';
1528             }
1529             $subcompile_command .= q{ } . '"-I' . $RPerl::CORE_PATH . '"';
1530              
1531              
1532              
1533              
1534             if ($modes->{parallel} eq 'OFF') {
1535             $subcompile_command .= q{ } . $cpp_output_file_name_group->{CPP};
1536             $subcompile_command .= q{ } . '-o ';
1537            
1538             if ( ( $modes->{subcompile} eq 'ASSEMBLE' )
1539             or ( $modes->{subcompile} eq 'ARCHIVE' ) )
1540             {
1541             $subcompile_command .= q{ } . $cpp_output_file_name_group->{O};
1542             }
1543             elsif ( $modes->{subcompile} eq 'SHARED' ) {
1544             $subcompile_command .= q{ } . $cpp_output_file_name_group->{SO};
1545             }
1546             elsif (( $modes->{subcompile} eq 'STATIC' )
1547             or ( $modes->{subcompile} eq 'DYNAMIC' ) )
1548             {
1549             $subcompile_command .= q{ } . $cpp_output_file_name_group->{EXE};
1550             }
1551             }
1552             elsif ($modes->{parallel} eq 'OPENMP') {
1553             $subcompile_command .= q{ } . '-mtune=native -ftree-vectorize -DTIME -fopenmp';
1554             $subcompile_command .= q{ } . $cpp_output_file_name_group->{OPENMP_CPP};
1555             $subcompile_command .= q{ } . '-o ';
1556            
1557             # NEED FIX PARALLEL: handle other subcompile modes???
1558             if (( $modes->{subcompile} eq 'STATIC' )
1559             or ( $modes->{subcompile} eq 'DYNAMIC' ) )
1560             {
1561             $subcompile_command .= q{ } . $cpp_output_file_name_group->{OPENMP_EXE};
1562             }
1563             }
1564            
1565             if ( $modes->{subcompile} eq 'SHARED' ) {
1566             $subcompile_command .= q{ } . '-shared';
1567             }
1568             elsif ( $modes->{subcompile} eq 'STATIC' ) {
1569             $subcompile_command .= q{ } . '-static';
1570             }
1571              
1572             if ( ( $modes->{subcompile} eq 'STATIC' )
1573             or ( $modes->{subcompile} eq 'DYNAMIC' ) )
1574             {
1575             if ($modes->{parallel} eq 'OPENMP') {
1576             $subcompile_command .= q{ } . '-lm'; # not in original Inline::CPP subcompile command
1577             }
1578             $subcompile_command .= q{ } . '-lperl'; # not in original Inline::CPP subcompile command
1579             }
1580              
1581             if ( $modes->{subcompile} eq 'STATIC' ) {
1582             $subcompile_command .= q{ } . '-lcrypt'; # not in original Inline::CPP subcompile command
1583             }
1584              
1585             if ( $modes->{subcompile} eq 'ARCHIVE' ) {
1586             $subcompile_command .= q{ } . ' ; ar -cvq ' . $cpp_output_file_name_group->{A} . q{ } . $cpp_output_file_name_group->{O};
1587              
1588             # NEED ANSWER: is this always the correct output redirect mechanism M$ Windows? I think it is correct for cmd.exe, but what about Cygwin, etc?
1589             if ( $OSNAME eq 'MSWin32' ) { $subcompile_command .= q{ } . ' > nul'; }
1590             else { $subcompile_command .= q{ } . ' > /dev/null'; }
1591             }
1592             # my $pid = open3( 0, \*SUBCOMPILE_STDOUT, \*SUBCOMPILE_STDERR, $subcompile_command ); # disable STDIN w/ 0
1593             #
1594             # my $stdout_select;
1595             # my $stderr_select;
1596             # if ( $OSNAME ne 'MSWin32' ) {
1597             # $stdout_select = IO::Select->new();
1598             # $stderr_select = IO::Select->new();
1599             # $stdout_select->add( \*SUBCOMPILE_STDOUT );
1600             # $stderr_select->add( \*SUBCOMPILE_STDERR );
1601             # }
1602             #
1603              
1604             # RPerl::diag( "\n" . 'in Compiler::cpp_to_xsbinary__subcompile(), have $subcompile_command =' . "\n\n" . $subcompile_command . "\n\n" );
1605             RPerl::diag( "\n\n" . $subcompile_command . "\n\n" );
1606             if ( $ENV{RPERL_VERBOSE} or $RPerl::VERBOSE ) { RPerl::diag( q{SUBCOMPILE: Generate binary... } ); }
1607              
1608             # if ( $OSNAME eq 'MSWin32' || $stdout_select->can_read(0) ) { sysread SUBCOMPILE_STDOUT, $subcompile_command_stdout, 4096; }
1609             # if ( $OSNAME eq 'MSWin32' || $stderr_select->can_read(0) ) { sysread SUBCOMPILE_STDERR, $subcompile_command_stderr, 4096; }
1610             # waitpid $pid, 0;
1611             # if ( $OSNAME eq 'MSWin32' || $stdout_select->can_read(0) ) { my $s; sysread SUBCOMPILE_STDOUT, $s, 4096; $subcompile_command_stdout .= $s; }
1612             # if ( $OSNAME eq 'MSWin32' || $stderr_select->can_read(0) ) { my $s; sysread SUBCOMPILE_STDERR, $s, 4096; $subcompile_command_stderr .= $s; }
1613              
1614             # ACTUALLY RUN SUBCOMPILE COMMAND
1615             my string $subcompile_command_stdout = q{};
1616             my string $subcompile_command_stderr = q{};
1617              
1618             #my $pid = open3( 0, \*SUBCOMPILE_STDOUT, \*SUBCOMPILE_STDERR, $subcompile_command ); # disable STDIN w/ 0
1619             run3( $subcompile_command, \undef, \$subcompile_command_stdout, \$subcompile_command_stderr );
1620              
1621             my $test_exit_status = $CHILD_ERROR >> 8;
1622              
1623             # RPerl::diag( 'in Compiler::cpp_to_xsbinary__subcompile(), have $CHILD_ERROR = ' . $CHILD_ERROR . "\n" );
1624             # RPerl::diag( 'in Compiler::cpp_to_xsbinary__subcompile(), have $test_exit_status = ' . $test_exit_status . "\n" );
1625              
1626             RPerl::verbose( ' done.' . "\n" );
1627              
1628             # delete temporary .o file
1629             if ( $modes->{subcompile} eq 'ARCHIVE' ) {
1630             if ( -f $cpp_output_file_name_group->{O} ) {
1631             unlink( $cpp_output_file_name_group->{O} )
1632             or croak( "\n"
1633             . 'ERROR ECOCOSU03, COMPILER, SUBCOMPILE: Cannot delete temporary object file ' . q{'}
1634             . $cpp_output_file_name_group->{O} . q{'} . ',' . "\n"
1635             . 'croaking:'
1636             . $OS_ERROR );
1637             }
1638             }
1639              
1640             # if ($subcompile_command_stdout) { RPerl::diag( "===STDOUT=BEGIN===\n" . $subcompile_command_stdout . "===STDOUT=END===\n" ); }
1641             # if ($subcompile_command_stderr) { RPerl::diag( "===STDERR=BEGIN===\n" . $subcompile_command_stderr . "===STDERR=END===\n" ); }
1642             my boolean $subcompile_command_stdout_content = ( ( defined $subcompile_command_stdout ) and ( $subcompile_command_stdout =~ m/[^\s]+/g ) );
1643             my boolean $subcompile_command_stderr_content = ( ( defined $subcompile_command_stderr ) and ( $subcompile_command_stderr =~ m/[^\s]+/g ) );
1644              
1645             if ( $subcompile_command_stdout_content or $subcompile_command_stderr_content ) {
1646             RPerl::diag("\n");
1647             if ($subcompile_command_stdout_content) {
1648             RPerl::diag( '[[[ SUBCOMPILE STDOUT ]]]' . "\n\n" . $subcompile_command_stdout . "\n" );
1649             }
1650             if ($subcompile_command_stderr_content) {
1651             RPerl::diag( '[[[ SUBCOMPILE STDERR ]]]' . "\n\n" . $subcompile_command_stderr . "\n" );
1652             }
1653             if ( $test_exit_status == 0 ) { # UNIX process return code 0, success
1654             RPerl::warning(
1655             'WARNING WCOCOSU00, COMPILER, SUBCOMPILE: C++ compiler returned success code but produced output which may indicate an error,' . "\n"
1656             . 'please run again with `rperl -D` command or RPERL_DEBUG=1 environmental variable for error messages or other output if none appear above'
1657             . "\n" );
1658             }
1659             }
1660              
1661             if ($test_exit_status) { # UNIX process return code not 0, error
1662             if ( not( $subcompile_command_stdout_content or $subcompile_command_stderr_content ) ) {
1663             RPerl::diag( "\n" . '[[[ SUBCOMPILE STDOUT & STDERR ARE BOTH EMPTY ]]]' . "\n\n" );
1664             }
1665             croak 'ERROR ECOCOSU04, COMPILER, SUBCOMPILE: C++ compiler returned error code,' . "\n"
1666             . 'please run again with `rperl -D` command or RPERL_DEBUG=1 environmental variable for error messages if none appear above,' . "\n"
1667             . 'croaking';
1668             }
1669             }
1670             else { # *.pm module files
1671             RPerl::verbose('SUBCOMPILE: Generate XS & binary...');
1672              
1673             ( my string $volume_pmc, my string $directories_pmc, my string $file_pmc )
1674             = File::Spec->splitpath( $cpp_output_file_name_group->{PMC}, my $no_file = 0 );
1675              
1676             #RPerl::diag( q{in Compiler::cpp_to_xsbinary__subcompile(), have $directories_pmc = } . $directories_pmc . "\n" );
1677              
1678             # strip trailing / or \ as long as they are not the only characters, which could indicate the root directory
1679             if ( ( ( length $directories_pmc ) > 1 )
1680             and ( ( ( substr $directories_pmc, -1, 1 ) eq q{/} ) or ( ( substr $directories_pmc, -1, 1 ) eq q{\\} ) ) )
1681             {
1682             substr $directories_pmc, -1, 1, q{};
1683             }
1684              
1685             my @INC_sorted = sort { length $b <=> length $a } @INC;
1686              
1687             #RPerl::diag( q{in Compiler::cpp_to_xsbinary__subcompile(), have @INC =} . "\n" . Dumper(\@INC) . "\n" );
1688             #RPerl::diag( q{in Compiler::cpp_to_xsbinary__subcompile(), have @INC_sorted =} . "\n" . Dumper(\@INC_sorted) . "\n" );
1689              
1690             # strip leading INC directory if present
1691             foreach my string $INC_directory (@INC_sorted) {
1692              
1693             #RPerl::diag( q{in Compiler::cpp_to_xsbinary__subcompile(), have $INC_directory = } . $INC_directory . "\n" );
1694             if ( $directories_pmc =~ /^$INC_directory/ ) {
1695             substr $directories_pmc, 0, ( length $INC_directory ), q{};
1696             last;
1697             }
1698             }
1699              
1700             #RPerl::diag( q{in Compiler::cpp_to_xsbinary__subcompile(), have POSSIBLY-MODIFIED $directories_pmc = } . $directories_pmc . "\n" );
1701              
1702             my string_arrayref $directories_pmc_split = [ File::Spec->splitdir($directories_pmc) ];
1703              
1704             #RPerl::diag( q{in Compiler::cpp_to_xsbinary__subcompile(), have $directories_pmc_split =} . "\n" . Dumper($directories_pmc_split) . "\n" );
1705              
1706             # discard '.' or empty directory names
1707             my $directories_pmc_split_tmp = [];
1708             foreach my $directory ( @{$directories_pmc_split} ) {
1709             if ( ( $directory ne q{.} ) and ( $directory ne q{} ) ) {
1710             push @{$directories_pmc_split_tmp}, $directory;
1711             }
1712             }
1713             $directories_pmc_split = $directories_pmc_split_tmp;
1714              
1715             # strip trailing .pmc file suffix
1716             substr $file_pmc, -4, 4, q{};
1717              
1718             my string $eval_string = join '::', @{$directories_pmc_split}, $file_pmc;
1719             $eval_string = 'use ' . $eval_string . ';';
1720              
1721             #RPerl::diag( q{in Compiler::cpp_to_xsbinary__subcompile(), have $eval_string =} . "\n" . $eval_string . "\n" );
1722              
1723             # NEED FIX: why does Inline::CPP require double-subcompiling???
1724             # DEV NOTE: exec() and system() don't work, only backticks
1725              
1726             # `export RPERL_WARNINGS=0; perl -e '$eval_string'`; # should build
1727             #RPerl::diag( q{in Compiler::cpp_to_xsbinary__subcompile(), done with backticks 1...} . "\n" );
1728              
1729             # `export RPERL_WARNINGS=0; perl -e '$eval_string'`; # should not build, but does
1730             #RPerl::diag( q{in Compiler::cpp_to_xsbinary__subcompile(), done with backticks 2...} . "\n" );
1731              
1732             # `export RPERL_WARNINGS=0; perl -e '$eval_string'`; # should not build, does not seem to
1733             #RPerl::diag( q{in Compiler::cpp_to_xsbinary__subcompile(), done with backticks 3...} . "\n" );
1734              
1735             RPerl::verbose( ' deferred.' . "\n" );
1736             }
1737             };
1738              
1739             1; # end of class