line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package C::Scan::Constants; |
2
|
|
|
|
|
|
|
|
3
|
4
|
|
|
4
|
|
156892
|
use 5.008003; |
|
4
|
|
|
|
|
17
|
|
|
4
|
|
|
|
|
166
|
|
4
|
4
|
|
|
4
|
|
24
|
use strict; |
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
145
|
|
5
|
4
|
|
|
4
|
|
21
|
use warnings; |
|
4
|
|
|
|
|
10
|
|
|
4
|
|
|
|
|
139
|
|
6
|
4
|
|
|
4
|
|
28
|
use Carp; |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
409
|
|
7
|
|
|
|
|
|
|
|
8
|
4
|
|
|
4
|
|
4411
|
use ExtUtils::Constant; |
|
4
|
|
|
|
|
234208
|
|
|
4
|
|
|
|
|
396
|
|
9
|
4
|
|
|
4
|
|
5198
|
use ModPerl::CScan; |
|
4
|
|
|
|
|
22
|
|
|
4
|
|
|
|
|
349
|
|
10
|
4
|
|
|
4
|
|
10300
|
use File::Temp qw( tempdir ); |
|
4
|
|
|
|
|
228251
|
|
|
4
|
|
|
|
|
419
|
|
11
|
4
|
|
|
4
|
|
9724
|
use File::Copy; |
|
4
|
|
|
|
|
12613
|
|
|
4
|
|
|
|
|
389
|
|
12
|
4
|
|
|
4
|
|
28
|
use File::Spec; |
|
4
|
|
|
|
|
11
|
|
|
4
|
|
|
|
|
68
|
|
13
|
4
|
|
|
4
|
|
22
|
use File::Path; |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
211
|
|
14
|
4
|
|
|
4
|
|
11621
|
use Data::Dumper; |
|
4
|
|
|
|
|
65155
|
|
|
4
|
|
|
|
|
351
|
|
15
|
4
|
|
|
4
|
|
10610
|
use IO::File; |
|
4
|
|
|
|
|
9604
|
|
|
4
|
|
|
|
|
975
|
|
16
|
4
|
|
|
4
|
|
34
|
use Config; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
1975
|
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
require Exporter; |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
our @ISA = qw(Exporter); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# Our functions are pretty uniquely named, and intended for |
23
|
|
|
|
|
|
|
# calling from Makefile.PL, so we simply export them be default. |
24
|
|
|
|
|
|
|
our @EXPORT = qw( extract_constants_from |
25
|
|
|
|
|
|
|
write_constants_module ); |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( 'all' => [ @EXPORT ] ); |
28
|
|
|
|
|
|
|
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
our $VERSION = "1.020"; |
31
|
|
|
|
|
|
|
$VERSION = eval $VERSION; |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# This module was originally written to support a custom pure-Perl |
34
|
|
|
|
|
|
|
# build system named Blueprint. If you know of or use Blueprint, |
35
|
|
|
|
|
|
|
# this section will mean something to you. If not, ignore it. |
36
|
|
|
|
|
|
|
my $g_use_blueprint_sections; |
37
|
|
|
|
|
|
|
BEGIN { |
38
|
|
|
|
|
|
|
# Initialize global variable(s) |
39
|
4
|
|
|
4
|
|
11
|
$g_use_blueprint_sections = 0; |
40
|
|
|
|
|
|
|
|
41
|
4
|
|
|
|
|
317
|
eval 'require Blueprint'; |
42
|
|
|
|
|
|
|
|
43
|
4
|
50
|
|
|
|
28613
|
unless ($@) { |
44
|
0
|
|
|
|
|
0
|
$g_use_blueprint_sections = 1; |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
# Now blueprint comment block protection is quietly enabled. |
48
|
|
|
|
|
|
|
# This will almost never be turned on. |
49
|
|
|
|
|
|
|
} |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
# _get_constant_data_blobs_from() |
52
|
|
|
|
|
|
|
# |
53
|
|
|
|
|
|
|
# Internal function. |
54
|
|
|
|
|
|
|
# |
55
|
|
|
|
|
|
|
# Returns a two blobs of data from the supplied file: |
56
|
|
|
|
|
|
|
# ($defines, <-- #define macros with no args |
57
|
|
|
|
|
|
|
# $typedefs) <-- #typedef enum constants |
58
|
|
|
|
|
|
|
sub _get_constant_data_blobs_from { |
59
|
5
|
|
|
5
|
|
23
|
my $file_to_relocate = shift; |
60
|
|
|
|
|
|
|
|
61
|
5
|
50
|
|
|
|
149
|
if ( ! -f $file_to_relocate ) { |
62
|
0
|
|
|
|
|
0
|
croak "$file_to_relocate does not appear to be accessible"; |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
# Create a temp directory here. |
66
|
5
|
50
|
|
|
|
476
|
my $temp_scan_dir = tempdir( 'c_scan_const_XXXXX', |
67
|
|
|
|
|
|
|
DIR => File::Spec->tmpdir(), |
68
|
|
|
|
|
|
|
CLEANUP => 1 ) |
69
|
|
|
|
|
|
|
or die "Internal error: failed to create temp dir"; |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
# copy the file into it |
72
|
5
|
|
|
|
|
170500
|
my $scan_file_basename = ( File::Spec->splitpath($file_to_relocate) )[2]; |
73
|
5
|
|
|
|
|
87
|
my $relocated_file = File::Spec->catpath( '', |
74
|
|
|
|
|
|
|
$temp_scan_dir, |
75
|
|
|
|
|
|
|
$scan_file_basename ); |
76
|
5
|
50
|
|
|
|
65
|
copy($file_to_relocate, $relocated_file) |
77
|
|
|
|
|
|
|
or croak "Could not copy $file_to_relocate to $relocated_file"; |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
# scan the file |
80
|
5
|
|
|
|
|
3557
|
my $c_header_file = ModPerl::CScan->new( filename => $relocated_file ); |
81
|
|
|
|
|
|
|
|
82
|
5
|
50
|
|
|
|
21
|
if ( !defined( $c_header_file ) ) { |
83
|
0
|
|
|
|
|
0
|
croak "Could not create ModPerl::CScan obj for $relocated_file"; |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
# Ugly hack to fix ActivePerl config bomb, i.e. expectation that "cppstdin" |
87
|
|
|
|
|
|
|
# is the cpp we'll be using. This assumes MinGW is installed, which we |
88
|
|
|
|
|
|
|
# attempted to enforce in the Makefile.PL. It probably assumes more than |
89
|
|
|
|
|
|
|
# should be safely assumed about the return data structure from Data::Flow, |
90
|
|
|
|
|
|
|
# but it seems to work. |
91
|
5
|
50
|
|
|
|
46
|
if ( $^O =~ /MSWin/i ) { |
92
|
0
|
|
|
|
|
0
|
my $cur_cppstdin = $c_header_file->get('Cpp')->{cppstdin}; |
93
|
0
|
|
|
|
|
0
|
my $cur_cc = $Config{cc}; |
94
|
0
|
0
|
0
|
|
|
0
|
unless ( $cur_cppstdin =~ /$cur_cc/ |
95
|
|
|
|
|
|
|
and $cur_cppstdin =~ /\-E/ ) { |
96
|
0
|
|
|
|
|
0
|
$c_header_file->get('Cpp')->{cppstdin} = "$cur_cc -E"; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
# Swallow STDERR temporarily |
101
|
5
|
|
|
|
|
130
|
open my $OLDERR, ">&", STDERR; |
102
|
5
|
|
|
|
|
27
|
close(STDERR); |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
# Redirect temporarily to the bit bucket, but keep it open |
105
|
|
|
|
|
|
|
# to avoid conflicting in a -w environment such as under test. |
106
|
|
|
|
|
|
|
# TBD: Make this friendlier for non-*n[u|i]x systems. |
107
|
5
|
|
|
|
|
195
|
open *STDERR, ">", "/dev/null"; |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
# We only care about unadorned macros, i.e. "defines" |
110
|
5
|
|
|
|
|
139
|
my $defs = $c_header_file->get("defines_no_args"); |
111
|
|
|
|
|
|
|
### These next lines represent possible future functionality #### |
112
|
|
|
|
|
|
|
# my $defs2 = $c_header_file->get("defines_maybe"); |
113
|
|
|
|
|
|
|
# my $defs3 = $c_header_file->get("defines_full"); |
114
|
|
|
|
|
|
|
# my $defs4 = $c_header_file->get("defines_args"); |
115
|
|
|
|
|
|
|
# my $defs5 = $c_header_file->get("defines_no_args_full"); |
116
|
|
|
|
|
|
|
# my $defs6 = $c_header_file->get("Defines"); |
117
|
|
|
|
|
|
|
################################################################## |
118
|
5
|
|
|
|
|
54
|
my $typedefs = $c_header_file->get("typedef_texts"); |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
### For debugging only ###################################################### |
122
|
|
|
|
|
|
|
### NOTE: need to send STDERR somewhere other than /dev/null for these to |
123
|
|
|
|
|
|
|
### work as intended. |
124
|
|
|
|
|
|
|
### |
125
|
|
|
|
|
|
|
# warn sprintf("[$file_to_relocate] defines_no_args = %s", Dumper($defs)); |
126
|
|
|
|
|
|
|
# warn sprintf("[$file_to_relocate] defines_maybe = %s", Dumper($defs2)); |
127
|
|
|
|
|
|
|
# warn sprintf("[$file_to_relocate] defines_full = %s", Dumper($defs3)); |
128
|
|
|
|
|
|
|
# warn sprintf("[$file_to_relocate] defines_args = %s", Dumper($defs4)); |
129
|
|
|
|
|
|
|
# warn sprintf("[$file_to_relocate] defines_no_args_full = %s", Dumper($defs5)); |
130
|
|
|
|
|
|
|
# warn sprintf("[$file_to_relocate] Defines = %s", Dumper($defs6)); |
131
|
|
|
|
|
|
|
# warn sprintf("[$file_to_relocate] enums = %s", Dumper($typedefs)); |
132
|
|
|
|
|
|
|
############################################################################# |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
# Restore STDERR and close the temp filehandle for neatness. |
135
|
5
|
|
|
|
|
233
|
close STDERR; |
136
|
5
|
|
|
|
|
121
|
open STDERR, ">&", $OLDERR; |
137
|
5
|
|
|
|
|
38
|
close $OLDERR; |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
# Return the file object returned from ModPerl::CScan->new() |
140
|
|
|
|
|
|
|
# Note: these may be empty (hashref, arrayref) |
141
|
5
|
|
|
|
|
519
|
return ($defs, $typedefs); |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
# extract_constants_from() |
148
|
|
|
|
|
|
|
# |
149
|
|
|
|
|
|
|
# Exported function. |
150
|
|
|
|
|
|
|
# |
151
|
|
|
|
|
|
|
# This function takes a list of C header (.h) files and returns a list |
152
|
|
|
|
|
|
|
# of constants information suitable for supplying as the NAME parameter |
153
|
|
|
|
|
|
|
# to ExtUtils::Constant. |
154
|
|
|
|
|
|
|
sub extract_constants_from { |
155
|
2
|
|
|
2
|
1
|
182
|
my @c_header_paths = @_; # full paths to each .h file to scan |
156
|
|
|
|
|
|
|
|
157
|
2
|
|
|
|
|
5
|
my @all_constants; |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
C_HEADER_FILE: |
160
|
2
|
|
|
|
|
7
|
foreach my $c_header_file ( @c_header_paths ) { |
161
|
5
|
|
|
|
|
32
|
my ($defs, |
162
|
|
|
|
|
|
|
$typedefs) = _get_constant_data_blobs_from( $c_header_file ); |
163
|
|
|
|
|
|
|
|
164
|
5
|
0
|
33
|
|
|
115
|
if ( ( !defined $defs || |
|
|
|
0
|
|
|
|
|
|
|
|
33
|
|
|
|
|
165
|
|
|
|
|
|
|
(defined $defs && scalar( keys %$defs ) == 0) ) and |
166
|
|
|
|
|
|
|
( !defined $typedefs || |
167
|
|
|
|
|
|
|
(defined $typedefs && scalar @$typedefs == 0) ) ) { |
168
|
0
|
|
|
|
|
0
|
warn "WARNING: Found no constants in $c_header_file."; |
169
|
0
|
|
|
|
|
0
|
next C_HEADER_FILE; |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
# Do the messy enum extraction |
173
|
5
|
|
|
|
|
72
|
my @enums = _extract_enum_constants_from( $typedefs ); |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
# We convert the base filename into something we can use |
176
|
|
|
|
|
|
|
# to avoid the error of throwing away the "filename constant" |
177
|
|
|
|
|
|
|
# e.g. #ifndef FOO_H_ |
178
|
|
|
|
|
|
|
# #define FOO_H_ |
179
|
5
|
|
|
|
|
348
|
my $all_caps_basename = uc ( ( File::Spec->splitpath($c_header_file) )[2] ); |
180
|
5
|
|
|
|
|
47
|
$all_caps_basename =~ s/[.]/_/g; |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
# Consolidate all names found into a single list. |
183
|
|
|
|
|
|
|
# Note that we discard string constants. |
184
|
577
|
|
|
|
|
527
|
my @constant_names = ( @enums, |
185
|
|
|
|
|
|
|
grep { |
186
|
5
|
|
|
|
|
209
|
my $defn = $_; |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
# Toss header file identifiers, but only |
189
|
|
|
|
|
|
|
# when they are *really* header file identifiers. |
190
|
577
|
100
|
66
|
|
|
4320
|
( $defn !~ /_H[_]?$/ |
|
|
|
66
|
|
|
|
|
191
|
|
|
|
|
|
|
or ($defn =~ /_H[_]?$/ |
192
|
|
|
|
|
|
|
and $all_caps_basename !~ /[_]?$defn[_]?/) ) |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
# Toss things ending in underscore (may not |
195
|
|
|
|
|
|
|
# be a good idea, but we'll wait to be convinced...) |
196
|
|
|
|
|
|
|
and $defn !~ /_$/ |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
# Toss string constants. |
199
|
|
|
|
|
|
|
and $defs->{$defn} !~ /^["]/ |
200
|
5
|
|
|
|
|
10
|
} keys %{$defs} ); |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
# Add these to the output |
203
|
5
|
|
|
|
|
261
|
push @all_constants, @constant_names; |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
|
206
|
2
|
|
|
|
|
159
|
return @all_constants; |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
# _extract_enum_constants_from() |
213
|
|
|
|
|
|
|
# |
214
|
|
|
|
|
|
|
# Internal function. |
215
|
|
|
|
|
|
|
# |
216
|
|
|
|
|
|
|
# Does some heinous massaging on a "typedef blob" returned from the |
217
|
|
|
|
|
|
|
# ModPerl::CScan::get() macro, ultimately spitting out a hashref for each |
218
|
|
|
|
|
|
|
# enumerated constant of the following form: |
219
|
|
|
|
|
|
|
# |
220
|
|
|
|
|
|
|
# { name => $enumerated_constant_name, |
221
|
|
|
|
|
|
|
# macro => 1 } |
222
|
|
|
|
|
|
|
# |
223
|
|
|
|
|
|
|
# See C::Scan for more details on the "typedef blob". |
224
|
|
|
|
|
|
|
sub _extract_enum_constants_from { |
225
|
|
|
|
|
|
|
|
226
|
5
|
|
|
5
|
|
11
|
my $typedefs = shift; |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
# enums will live in the @$typedefs array as follows: |
229
|
|
|
|
|
|
|
# ' enum |
230
|
|
|
|
|
|
|
# { |
231
|
|
|
|
|
|
|
# FOO_TYPE_A, FOO_TYPE_B, FOO_TYPE_C, |
232
|
|
|
|
|
|
|
# FOO_TYPE_D, FOO_TYPE_E, FOO_TYPE_F, |
233
|
|
|
|
|
|
|
# FOO_TYPE_INVALID |
234
|
|
|
|
|
|
|
# } foo_type_e' |
235
|
|
|
|
|
|
|
# We want to remove all the extraneous stuff and output the |
236
|
|
|
|
|
|
|
# following for each enum constant: |
237
|
|
|
|
|
|
|
# { name => $constant, macro => 1 } |
238
|
|
|
|
|
|
|
# This can then be fed into the NAMES parameter of WriteConstant |
239
|
|
|
|
|
|
|
# and have it do the right thing. |
240
|
30
|
|
|
|
|
87
|
my @enums = map { { name => "$_", macro => 1 } } # 7) assemble hashrefs |
|
30
|
|
|
|
|
43
|
|
241
|
|
|
|
|
|
|
# for |
242
|
|
|
|
|
|
|
# WriteConstants() |
243
|
30
|
|
|
|
|
51
|
map { s/[=][^\s]+//; $_ } # 6) discard explicit |
|
4
|
|
|
|
|
32
|
|
244
|
|
|
|
|
|
|
# val settings |
245
|
4
|
|
|
|
|
49
|
map { split ',' } # 5) split into consts |
246
|
141
|
|
|
|
|
216
|
map { s/^\s*enum.+[{]\s*//s; # 2) strip chars up |
247
|
|
|
|
|
|
|
# to 1st constant |
248
|
4
|
|
|
|
|
32
|
s/\s*[}].+_e$//s; # 3) strip chars after |
249
|
|
|
|
|
|
|
# last constant |
250
|
4
|
|
|
|
|
42
|
s/\s//sg; # 4) strip all other |
251
|
|
|
|
|
|
|
# whitespace |
252
|
4
|
|
|
|
|
15
|
$_ } |
253
|
5
|
|
|
|
|
10
|
grep { /enum/ } @{$typedefs}; # 1) find "enum" typedefs |
|
5
|
|
|
|
|
14
|
|
254
|
|
|
|
|
|
|
|
255
|
5
|
|
|
|
|
24
|
return @enums; |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
# _const_mod_header_text() |
262
|
|
|
|
|
|
|
# |
263
|
|
|
|
|
|
|
# Internal function. |
264
|
|
|
|
|
|
|
# |
265
|
|
|
|
|
|
|
# Return the block of code to be written to the top of the Symbols.pm |
266
|
|
|
|
|
|
|
# module. |
267
|
|
|
|
|
|
|
sub _const_mod_header_text { |
268
|
1
|
|
|
1
|
|
2
|
my $sub_pkg_name = shift; |
269
|
|
|
|
|
|
|
|
270
|
1
|
|
|
|
|
13
|
return <<"END_OF_MODULE_HEADER"; |
271
|
|
|
|
|
|
|
package $sub_pkg_name; |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
use 5.008003; |
274
|
|
|
|
|
|
|
use strict; |
275
|
|
|
|
|
|
|
use warnings; |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
use base 'Exporter'; |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
our \@EXPORT = qw( \@ALL ); |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
our \@ALL = qw( |
282
|
|
|
|
|
|
|
END_OF_MODULE_HEADER |
283
|
|
|
|
|
|
|
} |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
# _const_mod_symbol_names() |
289
|
|
|
|
|
|
|
# |
290
|
|
|
|
|
|
|
# Internal function. |
291
|
|
|
|
|
|
|
# |
292
|
|
|
|
|
|
|
# Return symbol names found in a list such as that which is returned |
293
|
|
|
|
|
|
|
# from extract_constants_from(). This function is typically used |
294
|
|
|
|
|
|
|
# to get text for writing to the middle portion of the Symbols.pm |
295
|
|
|
|
|
|
|
# module. |
296
|
|
|
|
|
|
|
sub _const_mod_symbol_names { |
297
|
2
|
|
|
2
|
|
7
|
my $names_ref = shift; |
298
|
|
|
|
|
|
|
|
299
|
2
|
|
|
|
|
6
|
my $symbol_names_str = ""; |
300
|
2
|
|
|
|
|
5
|
for my $symbol (@$names_ref) { |
301
|
46
|
100
|
|
|
|
74
|
if (ref $symbol) { |
302
|
30
|
|
|
|
|
55
|
$symbol_names_str .= join q{}, ' 'x4, |
303
|
|
|
|
|
|
|
$symbol->{name}, |
304
|
|
|
|
|
|
|
"\n"; |
305
|
|
|
|
|
|
|
} |
306
|
|
|
|
|
|
|
else { |
307
|
16
|
|
|
|
|
32
|
$symbol_names_str .= join q{}, ' 'x4, |
308
|
|
|
|
|
|
|
$symbol, |
309
|
|
|
|
|
|
|
"\n"; |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
} |
312
|
|
|
|
|
|
|
|
313
|
2
|
|
|
|
|
26
|
return $symbol_names_str; |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
# _const_mod_trailer_text() |
320
|
|
|
|
|
|
|
# |
321
|
|
|
|
|
|
|
# Internal function. |
322
|
|
|
|
|
|
|
# |
323
|
|
|
|
|
|
|
# Return the block of code to be written to the bottom of the Symbols.pm |
324
|
|
|
|
|
|
|
# module. |
325
|
|
|
|
|
|
|
sub _const_mod_trailer_text { |
326
|
1
|
|
|
1
|
|
3
|
return <<"END_OF_MODULE_TRAILER"; |
327
|
|
|
|
|
|
|
); |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
1; |
330
|
|
|
|
|
|
|
END_OF_MODULE_TRAILER |
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
# write_constants_module() |
336
|
|
|
|
|
|
|
# |
337
|
|
|
|
|
|
|
# Exported function. |
338
|
|
|
|
|
|
|
# |
339
|
|
|
|
|
|
|
# This function writes a Constants/C/Symbols.pm submodule into the |
340
|
|
|
|
|
|
|
# invoking Makefile.PL module's namespace. |
341
|
|
|
|
|
|
|
sub write_constants_module { |
342
|
1
|
|
|
1
|
1
|
170
|
my $pkg_name = shift; |
343
|
1
|
|
|
|
|
5
|
my @c_constants = @_; # array of symbol name blobs |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
# This is the canonical name of the submodule exporting the C symbols |
346
|
1
|
|
|
|
|
8
|
my $const_mod_base_name = 'Symbols.pm'; |
347
|
1
|
|
|
|
|
5
|
my $fwd_decl_base_name = 'ForwardDecls.pm'; |
348
|
1
|
|
|
|
|
6
|
my @const_mod_subdir_elems = qw(Constants C); |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
# turn the current package name into a directory path, creating |
351
|
|
|
|
|
|
|
# subordinate paths if needed |
352
|
1
|
|
|
|
|
7
|
my $const_mod_dir_name |
353
|
|
|
|
|
|
|
= join "/", ( 'lib', |
354
|
|
|
|
|
|
|
split( "::", $pkg_name ), |
355
|
|
|
|
|
|
|
@const_mod_subdir_elems, |
356
|
|
|
|
|
|
|
); |
357
|
|
|
|
|
|
|
|
358
|
1
|
|
|
|
|
12
|
my $const_mod_base_full_name |
359
|
|
|
|
|
|
|
= join '/', ( $const_mod_dir_name, |
360
|
|
|
|
|
|
|
$const_mod_base_name, |
361
|
|
|
|
|
|
|
); |
362
|
1
|
|
|
|
|
5
|
my $fwd_decl_base_full_name |
363
|
|
|
|
|
|
|
= join '/', ( $const_mod_dir_name, |
364
|
|
|
|
|
|
|
$fwd_decl_base_name, |
365
|
|
|
|
|
|
|
); |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
# Create directory in which to place the module |
369
|
1
|
50
|
|
|
|
37
|
unless (-d "$const_mod_dir_name") { |
370
|
1
|
50
|
|
|
|
461
|
mkpath( $const_mod_dir_name, 0, 0755) or die "mkpath failed: $!"; |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
# Create the module file to house the list of constants, as |
374
|
|
|
|
|
|
|
# well as the forward declarations file. |
375
|
1
|
50
|
|
|
|
70
|
open my $const_mod_fh, ">", "$const_mod_base_full_name" |
376
|
|
|
|
|
|
|
or die "Could not open $const_mod_base_name for writing: $!"; |
377
|
1
|
50
|
|
|
|
56
|
open my $fwd_decl_fh, ">", "$fwd_decl_base_full_name" |
378
|
|
|
|
|
|
|
or die "Could not open $fwd_decl_base_full_name for writing: $!"; |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
# Common arg list for the next threee functions |
381
|
1
|
|
|
|
|
11
|
(my $const_mod_name_prefix = $const_mod_base_name) =~ s/[.]pm$//; |
382
|
1
|
|
|
|
|
8
|
my $sub_pkg_name = join "::", ($pkg_name, |
383
|
|
|
|
|
|
|
@const_mod_subdir_elems, |
384
|
|
|
|
|
|
|
$const_mod_name_prefix); |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
# Write file contents. |
387
|
1
|
|
|
|
|
2
|
print {$const_mod_fh} _const_mod_header_text( $sub_pkg_name ); |
|
1
|
|
|
|
|
8
|
|
388
|
1
|
|
|
|
|
5
|
print {$const_mod_fh} _const_mod_symbol_names( \@c_constants ); |
|
1
|
|
|
|
|
7
|
|
389
|
1
|
|
|
|
|
2
|
print {$const_mod_fh} _const_mod_trailer_text( ); |
|
1
|
|
|
|
|
4
|
|
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
# Close file. |
392
|
1
|
|
|
|
|
53
|
close $const_mod_fh; |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
# Write forward declarations |
395
|
1
|
|
|
|
|
4
|
my @sym_names = split /\s+/, _const_mod_symbol_names( \@c_constants ); |
396
|
1
|
|
|
|
|
4
|
for my $sym (grep { ! /^\s*$/ } @sym_names) { |
|
24
|
|
|
|
|
65
|
|
397
|
23
|
|
|
|
|
28
|
print {$fwd_decl_fh} "sub $sym();\n"; |
|
23
|
|
|
|
|
46
|
|
398
|
|
|
|
|
|
|
} |
399
|
1
|
|
|
|
|
2
|
print {$fwd_decl_fh} "\n1;\n"; |
|
1
|
|
|
|
|
3
|
|
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
# Close file. |
402
|
1
|
|
|
|
|
35
|
close $fwd_decl_fh; |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
# Now write the XS stuff. This is overly simplistic. For example, |
405
|
|
|
|
|
|
|
# string constants will not be handled correctly this way. |
406
|
1
|
|
|
|
|
75
|
ExtUtils::Constant::WriteConstants( |
407
|
|
|
|
|
|
|
NAME => $pkg_name, |
408
|
|
|
|
|
|
|
NAMES => \@c_constants, |
409
|
|
|
|
|
|
|
DEFAULT_TYPE => 'IV', |
410
|
|
|
|
|
|
|
C_FILE => 'const-c.inc', |
411
|
|
|
|
|
|
|
XS_FILE => 'const-xs.inc', |
412
|
|
|
|
|
|
|
); |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
# We've now written the file, but we need to modify handling of IVs |
415
|
|
|
|
|
|
|
# to avoid seg faults on C constant access. |
416
|
1
|
50
|
|
|
|
49288
|
open CONST_XS_IN, "const-xs.inc" |
417
|
|
|
|
|
|
|
or die "Failed to open autogen'd const-xs.inc file for mods: $!"; |
418
|
1
|
|
|
|
|
68
|
my @in_code_lines = ; |
419
|
1
|
|
|
|
|
13
|
close CONST_XS_IN; |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
# Make the modification. Basically we assure that returned IVs have |
422
|
|
|
|
|
|
|
# refcounts of 1 vs. leaving it up to Perl to decide. |
423
|
1
|
|
|
|
|
3
|
my @out_code_lines; |
424
|
1
|
|
|
|
|
4
|
for my $line (@in_code_lines) { |
425
|
90
|
100
|
|
|
|
172
|
if ($line =~ /PUSHi[(]iv[)]/) { |
426
|
1
|
|
|
|
|
3
|
$line = " PUSHs(sv_2mortal(newSViv(iv)));\n"; |
427
|
|
|
|
|
|
|
} |
428
|
90
|
|
|
|
|
139
|
push @out_code_lines, $line; |
429
|
|
|
|
|
|
|
} |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
# Write out the modified file. Only one line should differ from |
432
|
|
|
|
|
|
|
# the original. |
433
|
1
|
50
|
|
|
|
85
|
open CONST_XS_OUT, ">const-xs.inc" |
434
|
|
|
|
|
|
|
or die "Failed to open const-xs.inc for writing, post mods: $!"; |
435
|
1
|
|
|
|
|
3
|
for my $line (@out_code_lines) { |
436
|
90
|
|
|
|
|
128
|
print CONST_XS_OUT $line; |
437
|
|
|
|
|
|
|
} |
438
|
1
|
|
|
|
|
36
|
close CONST_XS_OUT; |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
# Help the user out. They will need to modify their code. |
441
|
1
|
|
|
|
|
2
|
print {*STDERR} _suggested_code_snippets($pkg_name); |
|
1
|
|
|
|
|
15
|
|
442
|
|
|
|
|
|
|
|
443
|
1
|
|
|
|
|
32
|
return; |
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
# _suggested_code_snippets() |
449
|
|
|
|
|
|
|
# |
450
|
|
|
|
|
|
|
# Internal function. |
451
|
|
|
|
|
|
|
# |
452
|
|
|
|
|
|
|
# Returns a block of text that provides helpful direction to |
453
|
|
|
|
|
|
|
# someone who has just run C::Scan::Constants code, via "perl Makefile.PL" |
454
|
|
|
|
|
|
|
# so that the next time they do that they'll actually get all the |
455
|
|
|
|
|
|
|
# goodies wired into their code. |
456
|
|
|
|
|
|
|
sub _suggested_code_snippets { |
457
|
2
|
|
|
2
|
|
12
|
my $pkg_name = shift; |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
# Set up for extra decoration if needed to help out a build system |
460
|
2
|
|
|
|
|
8
|
my ($header,$trailer); |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
# As mentioned above, we include support for a custom pure-Perl |
463
|
|
|
|
|
|
|
# build system named Blueprint. If you know of or use Blueprint, |
464
|
|
|
|
|
|
|
# the "if" clause here will mean something to you. If not, ignore it. |
465
|
2
|
50
|
|
|
|
10
|
if ($g_use_blueprint_sections) { |
466
|
0
|
|
|
|
|
0
|
$header = "##### (BLUEPRINT: BEGIN EXPECTED OUTPUT) #####\n"; |
467
|
0
|
|
|
|
|
0
|
$trailer = "##### (BLUEPRINT: END EXPECTED OUTPUT) #####\n"; |
468
|
|
|
|
|
|
|
} |
469
|
|
|
|
|
|
|
else { |
470
|
|
|
|
|
|
|
# The most common situation |
471
|
2
|
|
|
|
|
4
|
$header = q{}; |
472
|
2
|
|
|
|
|
5
|
$trailer = q{}; |
473
|
|
|
|
|
|
|
} |
474
|
|
|
|
|
|
|
|
475
|
2
|
|
|
|
|
93
|
return <<"END_BEGIN_SNIPPET"; |
476
|
|
|
|
|
|
|
$header |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
You will need to add some code to your YourPkgName.pm and YourPkgName.xs |
479
|
|
|
|
|
|
|
files in order to make use of the code that has just been autogenerated |
480
|
|
|
|
|
|
|
via C::Scan::Constants. |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
If you've already added the code, just ignore this message. |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
Otherwise, do some cut-and-paste of the following snippets, |
485
|
|
|
|
|
|
|
substituting "YourPkgName" with your actual module name |
486
|
|
|
|
|
|
|
everywhere you see it in the snippets. |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
Then, simply "make" and test! It's that easy. |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
#------------- start of .pm snippet ---------------------- |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
# Do we have C symbols in a YourPkgName::Constants::C::Symbols module? |
493
|
|
|
|
|
|
|
my \$_symbols_present; |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
# Check for (and note) the existence of the C constants module. |
496
|
|
|
|
|
|
|
BEGIN { |
497
|
|
|
|
|
|
|
eval "require YourPkgName::Constants::C::Symbols"; |
498
|
|
|
|
|
|
|
\$_symbols_present = 1 unless \$\@; |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
eval "require YourPkgName::Constants::C::ForwardDecls"; |
501
|
|
|
|
|
|
|
} |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
# (Later, in your exports definition section...) |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
# Bring in the whole lot of C constants that are available. Your mileage |
506
|
|
|
|
|
|
|
# of course, may vary, e.g. alternatively do this via \@EXPORT_OK. |
507
|
|
|
|
|
|
|
our \@EXPORT = ( |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
# any other symbols you are exporting, plus: |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
\$_symbols_present ? \@YourPkgName::Constants::C::Symbols::ALL |
512
|
|
|
|
|
|
|
: (), |
513
|
|
|
|
|
|
|
); |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
# Make sure to have a $VERSION defined. |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
# Then, prior to subroutine definitions, insert the following. Note |
518
|
|
|
|
|
|
|
# that if you left autoloading turned on when you created your module |
519
|
|
|
|
|
|
|
# skeleton with h2xs (i.e. you did *not* specify -A when you ran it), |
520
|
|
|
|
|
|
|
# you already have this code in place. |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
use Carp; |
523
|
|
|
|
|
|
|
use AutoLoader; |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
sub AUTOLOAD { |
526
|
|
|
|
|
|
|
# This AUTOLOAD is used to 'autoload' constants from the constant() |
527
|
|
|
|
|
|
|
# XS function. |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
my \$constname; |
530
|
|
|
|
|
|
|
our \$AUTOLOAD; |
531
|
|
|
|
|
|
|
(\$constname = \$AUTOLOAD) =~ s/.*:://; |
532
|
|
|
|
|
|
|
croak "&YourPkgName::constant not defined" if \$constname eq 'constant' |
533
|
|
|
|
|
|
|
; |
534
|
|
|
|
|
|
|
my (\$error, \$val) = constant(\$constname); |
535
|
|
|
|
|
|
|
if (\$error) { croak \$error; } |
536
|
|
|
|
|
|
|
{ |
537
|
|
|
|
|
|
|
no strict 'refs'; |
538
|
|
|
|
|
|
|
*\$AUTOLOAD = sub { \$val }; |
539
|
|
|
|
|
|
|
} |
540
|
|
|
|
|
|
|
goto &\$AUTOLOAD; |
541
|
|
|
|
|
|
|
} |
542
|
|
|
|
|
|
|
require XSLoader; |
543
|
|
|
|
|
|
|
XSLoader::load('YourPkgName', \$VERSION); |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
#------------- start of .pm snippet ---------------------- |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
#------------- start of .xs snippet ---------------------- |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
# In YourPkgName.xs, make sure to add the following lines. |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
/* Before "MODULE =" line: */ |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
/* Specific .h files to scan */ |
554
|
|
|
|
|
|
|
#include "header_file_a.h" |
555
|
|
|
|
|
|
|
#include "header_file_b.h" |
556
|
|
|
|
|
|
|
/* ... */ |
557
|
|
|
|
|
|
|
#include "header_file_c.h" |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
/* |
560
|
|
|
|
|
|
|
* Note that if you left autoloading turned on when you created your module |
561
|
|
|
|
|
|
|
* skeleton with h2xs (i.e. you did *not* specify -A when you ran it), |
562
|
|
|
|
|
|
|
* you probably already have the code below in place and ready to use. |
563
|
|
|
|
|
|
|
*/ |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
/* Reference to autogenerated C-side binding file */ |
566
|
|
|
|
|
|
|
#include "const-c.inc" |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
/* After "MODULE =" line: */ |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
# Reference to autogenerated xs-side binding file. |
571
|
|
|
|
|
|
|
INCLUDE: const-xs.inc |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
#------------- end of .xs snippet ------------------------ |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
$trailer |
576
|
|
|
|
|
|
|
END_BEGIN_SNIPPET |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
} |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
1; |
581
|
|
|
|
|
|
|
__END__ |