| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package ExtUtils::CFeatureTest; |
|
2
|
|
|
|
|
|
|
our $VERSION= '0.002'; # VERSION |
|
3
|
1
|
|
|
1
|
|
133161
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
41
|
|
|
4
|
1
|
|
|
1
|
|
7
|
use warnings; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
59
|
|
|
5
|
1
|
|
|
1
|
|
735
|
use IO::Handle; |
|
|
1
|
|
|
|
|
8847
|
|
|
|
1
|
|
|
|
|
67
|
|
|
6
|
1
|
|
|
1
|
|
674
|
use ExtUtils::CBuilder; |
|
|
1
|
|
|
|
|
86480
|
|
|
|
1
|
|
|
|
|
4438
|
|
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 NAME |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
ExtUtils::CFeatureTest - Test a host for available C language features and libraries |
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
use "./inc"; |
|
15
|
|
|
|
|
|
|
use ExtUtils::CFeatureTest; |
|
16
|
|
|
|
|
|
|
my $ftest= ExtUtils::CFeatureTest->new; |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
# Test if a header exists. If found, set macro HAVE_STDBOOL_H, and all |
|
19
|
|
|
|
|
|
|
# compilation attempts below this will automatically include the header. |
|
20
|
|
|
|
|
|
|
$ftest->header('stdbool.h'); |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# Compile and run the snippet of code, and set HAVE_BOOL if it succeeds. |
|
23
|
|
|
|
|
|
|
$ftest->feature(HAVE_BOOL => 'bool x= true; return x? 0 : 1;'); |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
# Compile and run the snippet of code, and try various permutations of |
|
26
|
|
|
|
|
|
|
# headers and libs until it works. This one is required, so warn and |
|
27
|
|
|
|
|
|
|
# exit if it isn't available. |
|
28
|
|
|
|
|
|
|
$ftest->require_feature(HAVE_LIBSSL => |
|
29
|
|
|
|
|
|
|
'unsigned char buf[1]; return RAND_bytes(buf, 1) == 1? 0 : 1;', |
|
30
|
|
|
|
|
|
|
{ h => 'openssl/rand.h', pkg_config => ['libressl'] }, |
|
31
|
|
|
|
|
|
|
{ h => 'openssl/rand.h', pkg_config => ['openssl'] }, |
|
32
|
|
|
|
|
|
|
{ h => 'openssl/rand.h', -l => [ 'ssl', 'crypto' ] }); |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
# Export all the things we learned into a header to be included by all |
|
35
|
|
|
|
|
|
|
# source units in the project. |
|
36
|
|
|
|
|
|
|
$ftest->write_config_header('MyModule_config.h'); |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
# Export the compiler flags into ExtUtils::Depends to be used and/or |
|
39
|
|
|
|
|
|
|
# installed for other modules to use. |
|
40
|
|
|
|
|
|
|
my $dep= ExtUtils::Depends->new('MyModule'); |
|
41
|
|
|
|
|
|
|
$ftest->export_deps($dep); |
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
This is a module for testing aspects the C compiler and available libraries prior to building an |
|
46
|
|
|
|
|
|
|
XS distribution. It borrows many ideas from L and L. |
|
47
|
|
|
|
|
|
|
The main difference is that instead of simply building a list of compiler flags, it |
|
48
|
|
|
|
|
|
|
builds an entire header file for you that helps remove boilerplate from your other C files. |
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
For example, a traditional approach is to test for a C header like C, and if found |
|
51
|
|
|
|
|
|
|
define a preprocessor macro like C, and then in your source code you write |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
#ifdef HAVE_STDINT_H |
|
54
|
|
|
|
|
|
|
#include |
|
55
|
|
|
|
|
|
|
#endif |
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
This results in a bulky list of C<< -DHAVE_SOME_FEATURE >> options to your compiler, and also |
|
58
|
|
|
|
|
|
|
a lot of boilerplate within each C file. |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
This module eliminates the middleman by generating a header of its own with everything it |
|
61
|
|
|
|
|
|
|
learned from feature detection I the workarounds your Makefile.PL have added based on that |
|
62
|
|
|
|
|
|
|
knowledge. For the above example, if it finds C it adds the include statement |
|
63
|
|
|
|
|
|
|
directly to the generated header, saving you the C<#ifdef> boilerplate and avoiding any |
|
64
|
|
|
|
|
|
|
commandline arguments for the compiler. |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
=head1 INTEGRATION |
|
67
|
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
I strongly recommend copying C into your distribution as |
|
69
|
|
|
|
|
|
|
C<< inc/ExtUtils/CFeatureTest.pm >> rather than installing a system-wide copy. This ensures |
|
70
|
|
|
|
|
|
|
that future changes to CFeatureTest don't break existing distributions. While I don't change |
|
71
|
|
|
|
|
|
|
APIs frivolously, I'm not committing to full back-compat until it reaches version 1.0. |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
To this end, C is a single file with no non-core dependencies (since C |
|
74
|
|
|
|
|
|
|
which added L) so literally all you need to do is copy one file into your |
|
75
|
|
|
|
|
|
|
distribution as C<< inc/ExtUtils/CFeatureTest.pm >> and add C<< use lib "./inc" >> to the top |
|
76
|
|
|
|
|
|
|
of your C. |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
See the C of L for a complete example. |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
=head1 CONSTRUCTOR |
|
81
|
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
=head2 new |
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
$test= ExtUtils::CFeatureTest->new(%attributes); |
|
85
|
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
=cut |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
sub new { |
|
89
|
1
|
|
|
1
|
1
|
254419
|
my $self= bless {}, shift; |
|
90
|
1
|
|
50
|
|
|
15
|
$self->{verbose}= $ENV{EXTUTILS_CFEATURETEST_VERBOSE} || 0; |
|
91
|
1
|
|
|
|
|
12
|
$self->{emit_tty}= -t \*STDOUT; |
|
92
|
|
|
|
|
|
|
$self->{emit_unicode}= $self->{emit_tty} |
|
93
|
|
|
|
|
|
|
&& grep(defined($_) && /UTF-?8/i, @ENV{qw( LANG LC_ALL LC_CTYPE )}) |
|
94
|
1
|
|
33
|
|
|
7
|
&& ($^O ne 'MSWin32' || $ENV{WT_SESSION} || $ENV{TERM}); |
|
95
|
1
|
|
|
|
|
4
|
$self->{config_includes}= ''; |
|
96
|
1
|
|
|
|
|
4
|
$self->{config_include_set}= {}; |
|
97
|
1
|
|
|
|
|
4
|
$self->{config_pkg_set}= {}; |
|
98
|
1
|
|
|
|
|
6
|
$self->{config_macros}= {}; |
|
99
|
1
|
|
|
|
|
4
|
$self->{config_local}= ''; |
|
100
|
1
|
|
|
|
|
3
|
$self->{last_err}= ''; |
|
101
|
1
|
|
|
|
|
3
|
$self->{last_compile_output}= ''; |
|
102
|
1
|
|
|
|
|
4
|
$self->{last_exec_output}= ''; |
|
103
|
1
|
|
|
|
|
3
|
$self->{include_dirs}= []; |
|
104
|
1
|
|
|
|
|
4
|
$self->{extra_compiler_flags}= []; |
|
105
|
1
|
|
|
|
|
3
|
$self->{extra_linker_flags}= []; |
|
106
|
1
|
50
|
|
|
|
5
|
@_ & 1 and die "Expected even-length list of attribute => value"; |
|
107
|
1
|
|
|
|
|
5
|
for (my $i= 0; $i < @_; $i+= 2) { |
|
108
|
0
|
|
|
|
|
0
|
my ($attr, $val)= @_[$i,$i+1]; |
|
109
|
0
|
|
|
|
|
0
|
my $setter= "_set_$attr"; |
|
110
|
0
|
|
|
|
|
0
|
$self->$setter($val); |
|
111
|
|
|
|
|
|
|
} |
|
112
|
1
|
|
|
|
|
4
|
$self; |
|
113
|
|
|
|
|
|
|
} |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=head2 verbose |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
If true, emit diagnostics, including output from the compiler. The default comes from |
|
120
|
|
|
|
|
|
|
C<$ENV{EXTUTILS_CFEATURETEST_VERBOSE}>. A future version might make this into an integer of |
|
121
|
|
|
|
|
|
|
"log levels". |
|
122
|
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
=head2 emit_tty |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
If true, enable fancy colorized output. The default is based on whether STDOUT is a terminal. |
|
126
|
|
|
|
|
|
|
This also triggers for MSWin32 consoles, but the latest versions of the Windows console do |
|
127
|
|
|
|
|
|
|
support TTY color codes. |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
=head2 emit_unicode |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
If true, enable fancy unicode indicators in the output. The default is true if any locale |
|
132
|
|
|
|
|
|
|
environment variables contain 'utf-8'. |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=cut |
|
135
|
|
|
|
|
|
|
|
|
136
|
1
|
50
|
|
1
|
1
|
32
|
sub verbose { @_ > 1? shift->_set_verbose(@_) : $_[0]{verbose} } |
|
137
|
0
|
|
|
0
|
|
0
|
sub _set_verbose { $_[0]{verbose}= !!$_[1]; $_[0] } |
|
|
0
|
|
|
|
|
0
|
|
|
138
|
|
|
|
|
|
|
|
|
139
|
0
|
0
|
|
0
|
1
|
0
|
sub emit_tty { @_ > 1? shift->_set_emit_tty(@_) : $_[0]{emit_tty} } |
|
140
|
0
|
|
|
0
|
|
0
|
sub _set_emit_tty { $_[0]{emit_tty}= !!$_[1]; $_[0] } |
|
|
0
|
|
|
|
|
0
|
|
|
141
|
|
|
|
|
|
|
|
|
142
|
4
|
50
|
|
4
|
1
|
120
|
sub emit_unicode { @_ > 1? shift->_set_emit_unicode(@_) : $_[0]{emit_unicode} } |
|
143
|
0
|
|
|
0
|
|
0
|
sub _set_emit_unicode { $_[0]{emit_unicode}= !!$_[1]; $_[0] } |
|
|
0
|
|
|
|
|
0
|
|
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
my ($green, $red, $reset, $uchar_check, $uchar_x) |
|
146
|
|
|
|
|
|
|
= ("\e[32m","\e[31m","\e[0m","\x{2713}","\x{2715}"); |
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
sub _emit { |
|
149
|
7
|
|
|
7
|
|
35
|
my ($self, $handle, $msg)= @_; |
|
150
|
|
|
|
|
|
|
# This method is not efficient, but not a hot code path so not worth optimizing. |
|
151
|
|
|
|
|
|
|
$msg =~ s/\e\[.*?m//g |
|
152
|
7
|
50
|
|
|
|
118
|
unless $self->{emit_tty}; |
|
153
|
7
|
50
|
33
|
|
|
51
|
if ($self->{emit_unicode} && utf8::is_utf8($msg)) { |
|
154
|
0
|
|
|
|
|
0
|
require PerlIO; |
|
155
|
0
|
0
|
|
|
|
0
|
utf8::encode($msg) |
|
156
|
|
|
|
|
|
|
unless grep /utf-?8/i, PerlIO::get_layers($handle); |
|
157
|
|
|
|
|
|
|
} |
|
158
|
7
|
|
|
|
|
111
|
$handle->print($msg."\n"); |
|
159
|
|
|
|
|
|
|
} |
|
160
|
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=head2 config_header_text |
|
162
|
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
The C source code generated so far by the detection methods, including the L |
|
164
|
|
|
|
|
|
|
and L. The config source code is structured as |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
/* attribute config_includes */ |
|
167
|
|
|
|
|
|
|
#include |
|
168
|
|
|
|
|
|
|
#include |
|
169
|
|
|
|
|
|
|
... |
|
170
|
|
|
|
|
|
|
/* attribute config_macros */ |
|
171
|
|
|
|
|
|
|
#define HAS_HEADER1 |
|
172
|
|
|
|
|
|
|
#define HAS_HEADER2 |
|
173
|
|
|
|
|
|
|
... |
|
174
|
|
|
|
|
|
|
/* attribute config_local */ |
|
175
|
|
|
|
|
|
|
... |
|
176
|
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
This structure ensures that system headers are included before the pollution from your local |
|
178
|
|
|
|
|
|
|
macros and other symbols. |
|
179
|
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=head2 config_includes |
|
181
|
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
The C code of C<#include> statements generated so far by the detection methods. |
|
183
|
|
|
|
|
|
|
Also any code you added with L. |
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
=head2 config_include_set |
|
186
|
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
A hashref where each key is a header name which has been added to C. |
|
188
|
|
|
|
|
|
|
(The hashref is used as a cache, not a declaration that drives code generation) |
|
189
|
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=head2 config_pkg_set |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
A hashref of the C library names which have been added. Read-only. |
|
193
|
|
|
|
|
|
|
(The hashref is used as a cache, not a declaration that drives code generation) |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
=head2 config_macros |
|
196
|
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
A hashref where each key is a C macro name and the value is the definition of the macro. |
|
198
|
|
|
|
|
|
|
You may modify this hashref to define or remove macros. |
|
199
|
|
|
|
|
|
|
These will always come after the text of C so as not to pollute global |
|
200
|
|
|
|
|
|
|
namespace before system headers get included. If you wish to define a macro I the |
|
201
|
|
|
|
|
|
|
inclusion of system headers (such as C<_GNU_SOURCE> or C) use |
|
202
|
|
|
|
|
|
|
L. |
|
203
|
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
=head2 config_local |
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
A string of custom C code to append following C and C. |
|
207
|
|
|
|
|
|
|
This is intended for code you want defined for your entire project but don't want defined until |
|
208
|
|
|
|
|
|
|
after all system headers are included. See L. |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
=cut |
|
211
|
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
sub config_header_text { |
|
213
|
1
|
|
|
1
|
1
|
4
|
my $self= shift; |
|
214
|
1
|
|
|
|
|
7
|
return $self->config_includes |
|
215
|
|
|
|
|
|
|
. $self->_config_macros_text |
|
216
|
|
|
|
|
|
|
. $self->config_local; |
|
217
|
|
|
|
|
|
|
} |
|
218
|
|
|
|
|
|
|
|
|
219
|
7
|
50
|
|
7
|
1
|
198
|
sub config_includes { @_ > 1? shift->_set_config_includes(@_) : $_[0]{config_includes} } |
|
220
|
|
|
|
|
|
|
sub _set_config_includes { |
|
221
|
0
|
0
|
|
0
|
|
0
|
ref $_[1] and die "config_includes must be a string of C code"; |
|
222
|
0
|
|
|
|
|
0
|
$_[0]{config_includes}= $_[1]; |
|
223
|
0
|
|
|
|
|
0
|
$_[0] |
|
224
|
|
|
|
|
|
|
} |
|
225
|
|
|
|
|
|
|
|
|
226
|
3
|
|
|
3
|
1
|
31
|
sub config_include_set { $_[0]{config_include_set} } |
|
227
|
0
|
|
|
0
|
1
|
0
|
sub config_pkg_set { $_[0]{config_pkg_set} } |
|
228
|
|
|
|
|
|
|
|
|
229
|
6
|
50
|
|
6
|
1
|
50
|
sub config_macros { @_ > 1? shift->_set_config_macros(@_) : $_[0]{config_macros} } |
|
230
|
|
|
|
|
|
|
sub _set_config_macros { |
|
231
|
0
|
0
|
|
0
|
|
0
|
ref $_[1] eq 'HASH' or die "config_macros must be a hashref"; |
|
232
|
0
|
|
|
|
|
0
|
$_[0]{config_macros}= $_[1]; |
|
233
|
0
|
|
|
|
|
0
|
$_[0] |
|
234
|
|
|
|
|
|
|
} |
|
235
|
|
|
|
|
|
|
sub _config_macros_text { |
|
236
|
5
|
|
|
5
|
|
17
|
my $self= shift; |
|
237
|
5
|
|
|
|
|
26
|
my $code= ''; |
|
238
|
5
|
|
|
|
|
31
|
my $macros= $self->config_macros; |
|
239
|
5
|
|
|
|
|
38
|
for (sort keys %$macros) { |
|
240
|
6
|
50
|
|
|
|
21
|
if (defined(my $val= $macros->{$_})) { |
|
241
|
6
|
|
|
|
|
24
|
$val =~ s/\n\z//; # automatically add the backslashes on multiline macros |
|
242
|
6
|
|
|
|
|
45
|
$val =~ s/\n/\\\n/g; |
|
243
|
6
|
|
|
|
|
28
|
$code .= "#define $_ $val\n"; |
|
244
|
|
|
|
|
|
|
} else { |
|
245
|
0
|
|
|
|
|
0
|
$code .= "#define $_\n"; |
|
246
|
|
|
|
|
|
|
} |
|
247
|
|
|
|
|
|
|
} |
|
248
|
5
|
|
|
|
|
28
|
return $code; |
|
249
|
|
|
|
|
|
|
} |
|
250
|
|
|
|
|
|
|
|
|
251
|
5
|
50
|
|
5
|
1
|
94
|
sub config_local { @_ > 1? shift->_set_config_local(@_) : $_[0]{config_local} } |
|
252
|
|
|
|
|
|
|
sub _set_config_local { |
|
253
|
0
|
0
|
|
0
|
|
0
|
ref $_[1] and die "config_local must be a string of C code"; |
|
254
|
0
|
|
|
|
|
0
|
$_[0]{config_local}= $_[1]; |
|
255
|
0
|
|
|
|
|
0
|
$_[0] |
|
256
|
|
|
|
|
|
|
} |
|
257
|
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
=head2 cbuilder |
|
259
|
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
An instance of L (a core module in modern perls), lazy-built. |
|
261
|
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
=head2 last_err |
|
263
|
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
The exception generated by the last call to L, if any. |
|
265
|
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
=head2 last_compile_output |
|
267
|
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
The stdout/stderr generated by the last invocation of compiler and/or linker. |
|
269
|
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
=head2 last_exec_output |
|
271
|
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
The stdout/stderr generated by the last execution of a built executable. |
|
273
|
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
=cut |
|
275
|
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
sub cbuilder { |
|
277
|
|
|
|
|
|
|
@_ > 1? shift->_set_cbuilder(@_) |
|
278
|
13
|
50
|
66
|
13
|
1
|
396
|
: ($_[0]{cbuilder} ||= ExtUtils::CBuilder->new) |
|
279
|
|
|
|
|
|
|
} |
|
280
|
0
|
|
|
0
|
|
0
|
sub _set_cbuilder { $_[0]{cbuilder}= $_[1]; $_[0] } |
|
|
0
|
|
|
|
|
0
|
|
|
281
|
|
|
|
|
|
|
|
|
282
|
0
|
0
|
|
0
|
1
|
0
|
sub last_err { @_ > 1? shift->_set_last_err(@_) : $_[0]{last_err} } |
|
283
|
0
|
|
|
0
|
|
0
|
sub _set_last_err { $_[0]{last_err}= $_[1]; $_[0] } |
|
|
0
|
|
|
|
|
0
|
|
|
284
|
|
|
|
|
|
|
|
|
285
|
0
|
0
|
|
0
|
1
|
0
|
sub last_compile_output { @_ > 1? shift->_set_last_compile_output(@_) : $_[0]{last_compile_output} } |
|
286
|
0
|
|
|
0
|
|
0
|
sub _set_last_compile_output { $_[0]{last_compile_output}= $_[1]; $_[0] } |
|
|
0
|
|
|
|
|
0
|
|
|
287
|
|
|
|
|
|
|
|
|
288
|
0
|
0
|
|
0
|
1
|
0
|
sub last_exec_output { @_ > 1? shift->_set_last_exec_output(@_) : $_[0]{last_exec_output} } |
|
289
|
0
|
|
|
0
|
|
0
|
sub _set_last_exec_output { $_[0]{last_exec_output}= $_[1]; $_[0] } |
|
|
0
|
|
|
|
|
0
|
|
|
290
|
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
=head2 include_dirs |
|
292
|
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
An arrayref of directories to be passed to the compiler as C<< -Ipath >>. |
|
294
|
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
=head2 extra_compiler_flags |
|
296
|
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
An arrayref of command line arguments to pass to the C compiler. |
|
298
|
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
=head2 extra_linker_flags |
|
300
|
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
An arrayref of command line arguments to pass to the C linker. |
|
302
|
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
=cut |
|
304
|
|
|
|
|
|
|
|
|
305
|
8
|
50
|
|
8
|
1
|
40
|
sub include_dirs { @_ > 1? shift->_set_include_dirs(@_) : $_[0]{include_dirs} } |
|
306
|
0
|
0
|
|
0
|
|
0
|
sub _set_include_dirs { my $self= shift; $self->{include_dirs}= [ ref $_[0] eq 'ARRAY'? @{$_[0]} : @_ ]; $self } |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
307
|
|
|
|
|
|
|
|
|
308
|
8
|
50
|
|
8
|
1
|
57
|
sub extra_compiler_flags { @_ > 1? shift->_set_extra_compiler_flags(@_) : $_[0]{extra_compiler_flags} } |
|
309
|
0
|
0
|
|
0
|
|
0
|
sub _set_extra_compiler_flags { my $self= shift; $self->{extra_compiler_flags}= [ ref $_[0] eq 'ARRAY'? @{$_[0]} : @_ ]; $self } |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
310
|
|
|
|
|
|
|
|
|
311
|
8
|
50
|
|
8
|
1
|
63
|
sub extra_linker_flags { @_ > 1? shift->_set_extra_linker_flags(@_) : $_[0]{extra_linker_flags} } |
|
312
|
0
|
0
|
|
0
|
|
0
|
sub _set_extra_linker_flags { my $self= shift; $self->{extra_linker_flags}= [ ref $_[0] eq 'ARRAY'? @{$_[0]} : @_ ]; $self } |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
313
|
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
sub _spew { |
|
315
|
8
|
|
|
8
|
|
23
|
my $fname= shift; |
|
316
|
8
|
50
|
|
|
|
1510
|
open my $fh, '>', $fname or die "open($fname): $!"; |
|
317
|
8
|
50
|
|
|
|
126
|
$fh->print(@_) or die "write($fname): $!"; |
|
318
|
8
|
50
|
|
|
|
363
|
$fh->close or die "close($fname): $!"; |
|
319
|
|
|
|
|
|
|
} |
|
320
|
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
sub _maybe_list { |
|
322
|
0
|
0
|
0
|
0
|
|
0
|
ref $_[0] eq 'ARRAY'? (grep length, @{ $_[0] }) |
|
|
0
|
0
|
|
|
|
0
|
|
|
323
|
|
|
|
|
|
|
: defined $_[0] && length $_[0]? ( $_[0] ) |
|
324
|
|
|
|
|
|
|
: () |
|
325
|
|
|
|
|
|
|
} |
|
326
|
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
sub _capture_output { |
|
328
|
12
|
|
|
12
|
|
46
|
my ($self, $code)= @_; |
|
329
|
12
|
|
|
|
|
207
|
my $outfile= "ftest-$$-" . ++$self->{seq} . "-out.txt"; |
|
330
|
12
|
50
|
|
|
|
2120
|
open my $out_fh, '+>', $outfile or die "open($outfile): $!"; |
|
331
|
12
|
50
|
|
|
|
327
|
open my $stdout_save, ">&STDOUT" or die "dup(STDOUT): $!"; |
|
332
|
12
|
50
|
|
|
|
283
|
open my $stderr_save, ">&STDERR" or die "dup(STDERR): $!"; |
|
333
|
12
|
50
|
|
|
|
377
|
open STDOUT, ">&" . fileno $out_fh or die "Can't redirect STDOUT: $!"; |
|
334
|
12
|
50
|
|
|
|
432
|
open STDERR, ">&" . fileno $out_fh or die "Can't redirect STDERR: $!"; |
|
335
|
12
|
|
|
|
|
53
|
my ($ex, $out_txt); |
|
336
|
12
|
50
|
|
|
|
28
|
eval { $code->(); 1 } |
|
|
12
|
|
|
|
|
43
|
|
|
|
12
|
|
|
|
|
80
|
|
|
337
|
|
|
|
|
|
|
or $ex= $@; |
|
338
|
|
|
|
|
|
|
# restore handles |
|
339
|
12
|
50
|
|
|
|
676
|
open STDERR, ">&" . fileno $stderr_save or die "Can't restore STDERR: $!"; |
|
340
|
12
|
50
|
|
|
|
355
|
open STDOUT, ">&" . fileno $stdout_save or die "Can't restore STDOUT: $!"; |
|
341
|
|
|
|
|
|
|
# Slurp contents of compiler output |
|
342
|
12
|
|
|
|
|
125
|
seek($out_fh, 0, 0); |
|
343
|
12
|
|
|
|
|
36
|
{ local $/; $out_txt= <$out_fh> } |
|
|
12
|
|
|
|
|
191
|
|
|
|
12
|
|
|
|
|
766
|
|
|
344
|
12
|
|
|
|
|
174
|
close $out_fh; |
|
345
|
12
|
|
|
|
|
1378
|
unlink $outfile; |
|
346
|
12
|
50
|
|
|
|
179
|
$out_txt .= "\n".$ex if defined $ex; |
|
347
|
12
|
|
|
|
|
947
|
return $out_txt; |
|
348
|
|
|
|
|
|
|
} |
|
349
|
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
sub _capture_cmd { |
|
351
|
0
|
|
|
0
|
|
0
|
my ($self, @cmd)= @_; |
|
352
|
0
|
|
|
|
|
0
|
my $wstat; |
|
353
|
0
|
|
|
0
|
|
0
|
my $out= $self->_capture_output(sub { system { $cmd[0] } @cmd; $wstat= $?; }); |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
354
|
0
|
|
|
|
|
0
|
return ($wstat, $out); |
|
355
|
|
|
|
|
|
|
} |
|
356
|
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
=head1 METHODS |
|
358
|
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
=head2 compile_and_run |
|
360
|
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
$bool= $ftest->compile_and_run($code, %options); |
|
362
|
|
|
|
|
|
|
# %options: |
|
363
|
|
|
|
|
|
|
# include_dirs => [ ... ], |
|
364
|
|
|
|
|
|
|
# extra_compiler_flags => [ ... ], |
|
365
|
|
|
|
|
|
|
# extra_linker_flags => [ ... ], |
|
366
|
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
Attempt to compile and execute the specified C program text. The compiler will be given all |
|
368
|
|
|
|
|
|
|
include paths, compiler flags, and linker flags that have been detected so far, in addition to |
|
369
|
|
|
|
|
|
|
the ones that you pass to this method. C<$code> must be the complete program; the accumulated |
|
370
|
|
|
|
|
|
|
configuration code in L is not automatically applied. |
|
371
|
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
Returns boolean of whether it succeeded (meaning compile, link, and executable all exited with |
|
373
|
|
|
|
|
|
|
code 0). |
|
374
|
|
|
|
|
|
|
The compiler output is stored in attribute L, perl exceptions are stored |
|
375
|
|
|
|
|
|
|
in attribute L, and output of the text executable is stored in L. |
|
376
|
|
|
|
|
|
|
Nothing is printed to stdout/stderr. |
|
377
|
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
=cut |
|
379
|
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
sub compile_and_run { |
|
381
|
8
|
|
|
8
|
1
|
100
|
my ($self, $code, %opts)= @_; |
|
382
|
8
|
|
|
|
|
42
|
$self->{last_err}= ''; |
|
383
|
8
|
|
|
|
|
23
|
$self->{last_exec_output}= ''; |
|
384
|
8
|
|
|
|
|
37
|
$self->{last_compile_output}= ''; |
|
385
|
8
|
|
|
|
|
41
|
for (qw( include_dirs extra_compiler_flags extra_linker_flags )) { |
|
386
|
24
|
50
|
|
|
|
59
|
$opts{$_}= [ @{ $self->$_ }, @{ $opts{$_} || [] } ]; |
|
|
24
|
|
|
|
|
160
|
|
|
|
24
|
|
|
|
|
193
|
|
|
387
|
|
|
|
|
|
|
} |
|
388
|
|
|
|
|
|
|
|
|
389
|
8
|
|
|
|
|
156
|
my $srcfile= "ftest-$$-" . ++$self->{seq} . ".c"; |
|
390
|
8
|
|
|
|
|
71
|
_spew($srcfile, $code); |
|
391
|
8
|
|
|
|
|
618
|
my ($objfile, $exefile, $err, $success); |
|
392
|
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
# Compiler is rather noisy. Redirect output to temp file. |
|
394
|
|
|
|
|
|
|
$self->{last_compile_output}= $self->_capture_output(sub { |
|
395
|
8
|
|
|
8
|
|
18
|
$success= eval { |
|
396
|
8
|
|
|
|
|
18
|
$err= "compile failed"; |
|
397
|
8
|
|
|
|
|
65
|
$objfile= $self->cbuilder->compile(%opts, source => $srcfile); |
|
398
|
5
|
|
|
|
|
398434
|
$err= "link failed"; |
|
399
|
5
|
|
|
|
|
116
|
$exefile= $self->cbuilder->link_executable(%opts, objects => $objfile); |
|
400
|
|
|
|
|
|
|
}; |
|
401
|
8
|
50
|
|
|
|
414210
|
chomp($self->{last_err}= $@? "$err: $@" : $err) unless $success; |
|
|
|
100
|
|
|
|
|
|
|
402
|
8
|
|
|
|
|
205
|
}); |
|
403
|
8
|
100
|
|
|
|
344
|
if ($success) { |
|
404
|
|
|
|
|
|
|
$self->{last_exec_output}= $self->_capture_output(sub { |
|
405
|
4
|
|
|
4
|
|
25
|
$success= eval { |
|
406
|
4
|
|
|
|
|
42
|
$err= "execute"; |
|
407
|
4
|
|
|
|
|
23917
|
system("./$exefile"); |
|
408
|
4
|
50
|
|
|
|
174
|
if ($?) { $err= "execute failed: ".($? & 0xFF? "signal $?" : "exit code ".($? >> 8)) } |
|
|
1
|
100
|
|
|
|
84
|
|
|
409
|
4
|
|
|
|
|
90
|
$? == 0 |
|
410
|
|
|
|
|
|
|
}; |
|
411
|
4
|
50
|
|
|
|
95
|
chomp($self->{last_err}= $@? "$err: $@" : $err) unless $success; |
|
|
|
100
|
|
|
|
|
|
|
412
|
4
|
|
|
|
|
97
|
}); |
|
413
|
|
|
|
|
|
|
} |
|
414
|
8
|
|
|
|
|
2123
|
unlink grep defined, $srcfile, $objfile, $exefile; |
|
415
|
8
|
|
|
|
|
514
|
return $success; |
|
416
|
|
|
|
|
|
|
} |
|
417
|
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
=head2 header |
|
419
|
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
$ftest->header('some_header.h', @test_inc_paths); |
|
421
|
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
Attempt to compile a simple C program that includes the named header. The first compilation |
|
423
|
|
|
|
|
|
|
attempt will use the existing include path, and if not found, it will try compilation again |
|
424
|
|
|
|
|
|
|
for each element of C<@test_inc_paths> added to the include path until one succeeds. |
|
425
|
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
If any attempt succeeds, append the C<#include> directive to the L attribute |
|
427
|
|
|
|
|
|
|
and define macro C in attribute L. |
|
428
|
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
This means all future tests will automatically have this header loaded, if it exists. |
|
430
|
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
Returns a boolean of whether it added the header. |
|
432
|
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
=head2 require_header |
|
434
|
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
Like L, but warn+exit if it fails. i.e. the header is mandatory for the build. |
|
436
|
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
=cut |
|
438
|
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
sub header { |
|
440
|
2
|
|
|
2
|
1
|
18
|
my ($self, $header, @paths)= @_; |
|
441
|
2
|
50
|
|
|
|
23
|
return 1 if $self->{config_include_set}{$header}; |
|
442
|
2
|
|
|
|
|
35
|
(my $macro= 'HAVE_'.uc($header)) =~ s/\W/_/g; |
|
443
|
2
|
|
|
|
|
9
|
my $code= <
|
|
444
|
2
|
|
|
|
|
25
|
@{[ $self->config_includes ]} |
|
445
|
|
|
|
|
|
|
#include <$header> |
|
446
|
2
|
|
|
|
|
18
|
@{[ $self->_config_macros_text ]} |
|
447
|
2
|
|
|
|
|
17
|
@{[ $self->config_local ]} |
|
448
|
|
|
|
|
|
|
int main(int argc, char **argv) { return 0; } |
|
449
|
|
|
|
|
|
|
END_C |
|
450
|
2
|
|
|
|
|
18
|
for my $path (undef, @paths) { |
|
451
|
2
|
50
|
|
|
|
22
|
if ($self->compile_and_run($code, (defined $path? (include_dirs => [$path]) : ()))) { |
|
|
|
100
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
# It worked. Add the header to our list, and add a macro for having detected it. |
|
453
|
1
|
|
|
|
|
18
|
$self->{config_includes} .= "#include <$header>\n"; |
|
454
|
1
|
|
|
|
|
23
|
$self->{config_include_set}{$header}= 1; |
|
455
|
1
|
50
|
|
|
|
11
|
push @{$self->{include_dirs}}, $path if defined $path; |
|
|
0
|
|
|
|
|
0
|
|
|
456
|
1
|
|
|
|
|
12
|
$self->{config_macros}{$macro}= 1; |
|
457
|
1
|
50
|
|
|
|
32
|
$self->_emit(\*STDOUT, "Found $header".(defined $path? " at $path" : " in existing include dirs")); |
|
458
|
1
|
50
|
|
|
|
48
|
$self->_emit(\*STDOUT, ($self->emit_unicode? "$uchar_check " : '+') . "$green$macro$reset"); |
|
459
|
1
|
|
|
|
|
41
|
return 1; |
|
460
|
|
|
|
|
|
|
} |
|
461
|
|
|
|
|
|
|
} |
|
462
|
1
|
50
|
|
|
|
38
|
$self->_emit(\*STDOUT, ($self->emit_unicode? "$uchar_x " : '-') . "$red$macro$reset"); |
|
463
|
1
|
|
|
|
|
75
|
return 0; |
|
464
|
|
|
|
|
|
|
} |
|
465
|
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
sub require_header { |
|
467
|
0
|
|
|
0
|
1
|
0
|
my ($self, $header, @args)= @_; |
|
468
|
0
|
|
|
|
|
0
|
my $success= $self->header($header, @args); |
|
469
|
0
|
0
|
|
|
|
0
|
if (!$success) { |
|
470
|
0
|
|
|
|
|
0
|
STDOUT->flush; |
|
471
|
0
|
|
|
|
|
0
|
warn $self->last_err; |
|
472
|
0
|
|
|
|
|
0
|
warn $self->last_compile_output; |
|
473
|
0
|
|
|
|
|
0
|
$self->_emit(\*STDERR, "${red}Can't proceed without $header$reset"); |
|
474
|
0
|
|
|
|
|
0
|
exit 1; |
|
475
|
|
|
|
|
|
|
} |
|
476
|
0
|
|
|
|
|
0
|
1; |
|
477
|
|
|
|
|
|
|
} |
|
478
|
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
=head2 feature |
|
480
|
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
$bool= $ftest->feature(MACRO_NAME => $c_code_snippet, |
|
482
|
|
|
|
|
|
|
{ # one possible set of options known to work |
|
483
|
|
|
|
|
|
|
h => \@header_names, |
|
484
|
|
|
|
|
|
|
include_dirs => \@paths, |
|
485
|
|
|
|
|
|
|
extra_compiler_flags => \@commandline_options, |
|
486
|
|
|
|
|
|
|
extra_linker_flags => \@commandline_options, |
|
487
|
|
|
|
|
|
|
pkg_config => \@module_names, |
|
488
|
|
|
|
|
|
|
}, |
|
489
|
|
|
|
|
|
|
{ # another possible set of options known to work |
|
490
|
|
|
|
|
|
|
# using convenient aliases for the attributes above |
|
491
|
|
|
|
|
|
|
h => $header, -I => $path, -D => $macro, -L => $path, -l => $lib |
|
492
|
|
|
|
|
|
|
}, |
|
493
|
|
|
|
|
|
|
... # as many attempts as you want |
|
494
|
|
|
|
|
|
|
); |
|
495
|
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
This attempts to compile and execute C<$c_code_snippet>. It attempts compilation once without |
|
497
|
|
|
|
|
|
|
any configuration changes, and then attempts again using each of a supplied list of |
|
498
|
|
|
|
|
|
|
configurations until one succeeds. You can specify the configurations using full attribute |
|
499
|
|
|
|
|
|
|
names, or with shorthand aliases that resemble the gcc command line flags. |
|
500
|
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
Again, note that any compiler/linker flags are I to any others that were previously |
|
502
|
|
|
|
|
|
|
detected (the attributes L, L, and L) |
|
503
|
|
|
|
|
|
|
and the generated source code automatically includes the L that |
|
504
|
|
|
|
|
|
|
CFeatureTest is in the process of building. |
|
505
|
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
Also note that the C option attempts to load I of the C<@module_names> and |
|
507
|
|
|
|
|
|
|
proceeds to attempt compilation if I of them were found. |
|
508
|
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
=head2 require_feature |
|
510
|
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
Like L, but warn+exit if it fails. i.e. the feature is mandatory for the build. |
|
512
|
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
=cut |
|
514
|
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
sub feature { |
|
516
|
2
|
|
|
2
|
1
|
21
|
my ($self, $macro, $code, @permutations)= @_; |
|
517
|
|
|
|
|
|
|
# Single function name? just take the address of it |
|
518
|
2
|
50
|
|
|
|
31
|
if ($code =~ /^\w+\z/) { |
|
519
|
|
|
|
|
|
|
# Compilers might optimize a simple "fn != NULL" to a constant expression and then not |
|
520
|
|
|
|
|
|
|
# even link it. Compare to another pointer like argv hoping they can't optimize that. |
|
521
|
2
|
|
|
|
|
9
|
$code= "void *fn= (void *) $code; return fn != argv? 0 : 1;"; |
|
522
|
|
|
|
|
|
|
} |
|
523
|
|
|
|
|
|
|
# Bare snippet without 'main' function wrapping it? |
|
524
|
2
|
50
|
|
|
|
11
|
unless ($code =~ /int main\(/) { |
|
525
|
|
|
|
|
|
|
# Is it a snippet belonging inside main? |
|
526
|
2
|
50
|
|
|
|
23
|
if ($code =~ /return [^{}]+;/) { |
|
527
|
2
|
|
|
|
|
7
|
$code= "int main(int argc, char **argv) { $code }\n"; |
|
528
|
|
|
|
|
|
|
} else { |
|
529
|
0
|
|
|
|
|
0
|
$code= "$code\nint main(int argc, char **argv) { return 0; }\n"; |
|
530
|
|
|
|
|
|
|
} |
|
531
|
|
|
|
|
|
|
} |
|
532
|
2
|
|
|
|
|
18
|
$self->_emit(\*STDOUT, "Test for feature $macro"); |
|
533
|
2
|
|
|
|
|
41
|
for my $p (undef, @permutations) { |
|
534
|
2
|
|
|
|
|
7
|
my $prefix= $self->config_includes; |
|
535
|
2
|
|
|
|
|
5
|
my (@headers, @pkg_found); |
|
536
|
2
|
50
|
|
|
|
7
|
if ($p) { |
|
537
|
|
|
|
|
|
|
# clone $p before making changes |
|
538
|
0
|
|
|
|
|
0
|
$p= { %$p }; |
|
539
|
|
|
|
|
|
|
$p->{$_}= [ _maybe_list($p->{$_}) ] |
|
540
|
0
|
|
|
|
|
0
|
for qw( include_dirs extra_compiler_flags extra_linker_flags ); |
|
541
|
|
|
|
|
|
|
# optional header attempts |
|
542
|
0
|
|
|
|
|
0
|
@headers= grep !$self->{config_include_set}{$_}, _maybe_list(delete $p->{h}); |
|
543
|
0
|
|
|
|
|
0
|
$prefix .= "#include <$_>\n" for @headers; |
|
544
|
|
|
|
|
|
|
# expand convenient aliases |
|
545
|
0
|
|
|
|
|
0
|
push @{ $p->{include_dirs} }, _maybe_list(delete $p->{-I}) |
|
546
|
0
|
0
|
|
|
|
0
|
if defined $p->{-I}; |
|
547
|
0
|
|
|
|
|
0
|
push @{ $p->{extra_compiler_flags} }, map "-D$_", _maybe_list(delete $p->{-D}) |
|
548
|
0
|
0
|
|
|
|
0
|
if defined $p->{-D}; |
|
549
|
0
|
|
|
|
|
0
|
push @{ $p->{extra_linker_flags} }, map "-L$_", _maybe_list(delete $p->{-L}) |
|
550
|
0
|
0
|
|
|
|
0
|
if defined $p->{-L}; |
|
551
|
0
|
|
|
|
|
0
|
push @{ $p->{extra_linker_flags} }, map "-l$_", _maybe_list(delete $p->{-l}) |
|
552
|
0
|
0
|
|
|
|
0
|
if defined $p->{-l}; |
|
553
|
|
|
|
|
|
|
# If any pkg_config modules were requested, add those to the options. |
|
554
|
|
|
|
|
|
|
# If none are available, skip the compilation attempt. |
|
555
|
0
|
0
|
|
|
|
0
|
if (my @mod= _maybe_list(delete $p->{pkg_config})) { |
|
556
|
0
|
|
|
|
|
0
|
my $msg= " pkg-config"; |
|
557
|
0
|
|
|
|
|
0
|
for (@mod) { |
|
558
|
0
|
0
|
|
|
|
0
|
if ($self->get_pkg_config($_, $p)) { |
|
559
|
0
|
|
|
|
|
0
|
push @pkg_found, $_; |
|
560
|
0
|
|
|
|
|
0
|
$msg .= " $green$_$reset (found)"; |
|
561
|
|
|
|
|
|
|
} else { |
|
562
|
0
|
|
|
|
|
0
|
$msg .= " $red$_$reset (not found)"; |
|
563
|
|
|
|
|
|
|
} |
|
564
|
|
|
|
|
|
|
} |
|
565
|
0
|
|
|
|
|
0
|
$self->_emit(\*STDOUT, $msg); |
|
566
|
0
|
0
|
|
|
|
0
|
next unless @pkg_found; |
|
567
|
|
|
|
|
|
|
} |
|
568
|
|
|
|
|
|
|
} |
|
569
|
2
|
|
|
|
|
11
|
$prefix .= $self->_config_macros_text . $self->config_local; |
|
570
|
2
|
50
|
|
|
|
15
|
if ($p) { |
|
571
|
0
|
|
|
|
|
0
|
my @show_options; |
|
572
|
0
|
|
|
|
|
0
|
for (sort keys %$p) { |
|
573
|
0
|
|
|
|
|
0
|
my $val= $p->{$_}; |
|
574
|
0
|
0
|
|
|
|
0
|
next unless defined $val; |
|
575
|
0
|
0
|
|
|
|
0
|
if (ref $val eq 'ARRAY') { |
|
576
|
0
|
0
|
|
|
|
0
|
next unless @$val; |
|
577
|
0
|
|
|
|
|
0
|
$val= join ' ', @$val; |
|
578
|
|
|
|
|
|
|
} |
|
579
|
0
|
0
|
|
|
|
0
|
next unless length $val; |
|
580
|
0
|
|
|
|
|
0
|
push @show_options, "$_=$val"; |
|
581
|
|
|
|
|
|
|
} |
|
582
|
0
|
0
|
|
|
|
0
|
if (@show_options) { |
|
583
|
0
|
|
|
|
|
0
|
print " test with $_\n" for shift @show_options; |
|
584
|
0
|
|
|
|
|
0
|
print " $_\n" for @show_options; |
|
585
|
|
|
|
|
|
|
} |
|
586
|
|
|
|
|
|
|
} |
|
587
|
2
|
50
|
|
|
|
28
|
if ($self->compile_and_run($prefix.$code, $p? (%$p) : ())) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
588
|
1
|
50
|
|
|
|
17
|
if ($p) { |
|
589
|
0
|
|
|
|
|
0
|
for (qw( include_dirs extra_compiler_flags extra_linker_flags )) { |
|
590
|
0
|
0
|
|
|
|
0
|
push @{$self->{$_}}, @{$p->{$_}} if $p->{$_}; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
591
|
|
|
|
|
|
|
} |
|
592
|
0
|
|
|
|
|
0
|
for (@headers) { |
|
593
|
0
|
|
|
|
|
0
|
$self->{config_includes} .= "#include <$_>\n"; |
|
594
|
0
|
|
|
|
|
0
|
$self->{config_include_set}{$_}= 1; |
|
595
|
|
|
|
|
|
|
} |
|
596
|
|
|
|
|
|
|
} |
|
597
|
1
|
50
|
33
|
|
|
24
|
if (defined $macro && length $macro) { |
|
598
|
1
|
|
|
|
|
10
|
$self->{config_macros}{$macro}= 1; |
|
599
|
1
|
50
|
|
|
|
26
|
$self->_emit(\*STDOUT, ($self->emit_unicode? "$uchar_check " : '+') . "$green$macro$reset"); |
|
600
|
|
|
|
|
|
|
} |
|
601
|
1
|
|
|
|
|
66
|
return 1; |
|
602
|
|
|
|
|
|
|
} elsif ($self->verbose) { |
|
603
|
0
|
|
|
|
|
0
|
my $msg= $self->last_err."\n".$self->last_compile_output."\n"; |
|
604
|
0
|
|
|
|
|
0
|
$msg =~ s/^/ /mg; |
|
605
|
0
|
|
|
|
|
0
|
print $msg; |
|
606
|
|
|
|
|
|
|
} |
|
607
|
|
|
|
|
|
|
} |
|
608
|
1
|
50
|
33
|
|
|
69
|
$self->_emit(\*STDOUT, ($self->emit_unicode? "$uchar_x " : '-') . "$red$macro$reset") |
|
|
|
50
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
if defined $macro && length $macro; |
|
610
|
1
|
|
|
|
|
70
|
return 0; |
|
611
|
|
|
|
|
|
|
} |
|
612
|
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
sub require_feature { |
|
614
|
0
|
|
|
0
|
1
|
|
my ($self, $macro, @args)= @_; |
|
615
|
0
|
|
|
|
|
|
my $success= $self->feature($macro, @args); |
|
616
|
0
|
0
|
|
|
|
|
if (!$success) { |
|
617
|
0
|
|
|
|
|
|
STDOUT->flush; |
|
618
|
0
|
|
|
|
|
|
warn $self->last_err; |
|
619
|
0
|
|
|
|
|
|
warn $self->last_compile_output; |
|
620
|
0
|
|
|
|
|
|
warn "Can't proceed without $macro"; |
|
621
|
0
|
|
|
|
|
|
exit 1; |
|
622
|
|
|
|
|
|
|
} |
|
623
|
0
|
|
|
|
|
|
1; |
|
624
|
|
|
|
|
|
|
} |
|
625
|
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
=head2 get_pkg_config |
|
627
|
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
$bool= $ftest->get_pkg_config($package_name, \%options_out); |
|
629
|
|
|
|
|
|
|
$bool= $ftest->get_pkg_config(\@package_names, \%options_out); |
|
630
|
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
For a named package, retrieve the C<--cflags> and C<--libs> and store the values into |
|
632
|
|
|
|
|
|
|
C<%options_out>. |
|
633
|
|
|
|
|
|
|
If the package is not installed or C executable is missing, this returns false. |
|
634
|
|
|
|
|
|
|
You can customize the C executable with C<$ENV{PKG_CONFIG}>. |
|
635
|
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
If you specify an array of names to check, B will be attempted, and success will be |
|
637
|
|
|
|
|
|
|
determined by whether B of them existed. This is intended for cases where you have one |
|
638
|
|
|
|
|
|
|
specific package in mind, but it may be available as an alternate name or divided into |
|
639
|
|
|
|
|
|
|
sub-modules depending on the OS distribution. |
|
640
|
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
=cut |
|
642
|
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
my $have_pkg_config; |
|
644
|
|
|
|
|
|
|
sub get_pkg_config { |
|
645
|
0
|
|
|
0
|
1
|
|
my ($self, $modules, $options_out)= @_; |
|
646
|
0
|
|
|
|
|
|
my $pc = 'pkg-config'; |
|
647
|
0
|
0
|
|
|
|
|
if (defined $ENV{PKG_CONFIG}) { |
|
648
|
|
|
|
|
|
|
# Disallow shell metacharacters in executable name, just in case. |
|
649
|
0
|
0
|
|
|
|
|
($pc= $ENV{PKG_CONFIG}) !~ /[\0-\x1F"'\$\%{}\x7F]/ |
|
650
|
|
|
|
|
|
|
or die "Unsafe value of PKG_CONFIG environment variable"; |
|
651
|
|
|
|
|
|
|
} |
|
652
|
|
|
|
|
|
|
#print " called get_pkg_config($modules)\n"; |
|
653
|
0
|
0
|
|
|
|
|
unless (defined $have_pkg_config) { |
|
654
|
|
|
|
|
|
|
# only warn about it once |
|
655
|
0
|
|
|
|
|
|
my ($wstat, $out)= $self->_capture_cmd($pc, '--version'); |
|
656
|
0
|
0
|
|
|
|
|
chomp($have_pkg_config= $wstat == 0? $out : ''); |
|
657
|
0
|
0
|
|
|
|
|
print "$pc not found (override with PKG_CONFIG=path)\n" |
|
658
|
|
|
|
|
|
|
unless $have_pkg_config; |
|
659
|
|
|
|
|
|
|
} |
|
660
|
0
|
|
|
|
|
|
my $success; |
|
661
|
0
|
0
|
|
|
|
|
if ($have_pkg_config) { |
|
662
|
0
|
|
|
|
|
|
for my $m (_maybe_list($modules)) { |
|
663
|
0
|
0
|
|
|
|
|
if (!exists $self->{config_pkg_set}{$m}) { |
|
664
|
|
|
|
|
|
|
# Existence check first |
|
665
|
0
|
0
|
|
|
|
|
if ((system { $pc } $pc, '--exists', $m) == 0) { |
|
|
0
|
|
|
|
|
|
|
|
666
|
0
|
|
|
|
|
|
my ($cf_wstat, $cflags) = $self->_capture_cmd($pc, '--cflags', $m); |
|
667
|
0
|
|
|
|
|
|
my ($l_wstat, $libs) = $self->_capture_cmd($pc, '--libs', $m); |
|
668
|
0
|
|
|
|
|
|
chomp($cflags, $libs); |
|
669
|
0
|
0
|
0
|
|
|
|
if ($self->verbose || $cf_wstat || $l_wstat) { |
|
|
|
|
0
|
|
|
|
|
|
670
|
0
|
0
|
|
|
|
|
print " pkg-config module $m cflags: ".($cf_wstat? 'FAILED: ':'')."$cflags\n"; |
|
671
|
0
|
0
|
|
|
|
|
print " pkg-config module $m libs : ".($l_wstat? 'FAILED: ':'')."$libs\n"; |
|
672
|
|
|
|
|
|
|
} |
|
673
|
0
|
0
|
|
|
|
|
$self->{config_pkg_set}{$m}{cflags}= $cf_wstat? '' : $cflags; |
|
674
|
0
|
0
|
|
|
|
|
$self->{config_pkg_set}{$m}{libs}= $l_wstat? '' : $libs; |
|
675
|
|
|
|
|
|
|
} |
|
676
|
|
|
|
|
|
|
else { |
|
677
|
|
|
|
|
|
|
# warn once if module not on this host |
|
678
|
0
|
0
|
|
|
|
|
print " pkg-config module $m not found\n" if $self->verbose; |
|
679
|
0
|
|
|
|
|
|
$self->{config_pkg_set}{$m}= undef; |
|
680
|
|
|
|
|
|
|
} |
|
681
|
|
|
|
|
|
|
} |
|
682
|
0
|
0
|
|
|
|
|
if (my $cfg= $self->{config_pkg_set}{$m}) { |
|
683
|
0
|
|
|
|
|
|
for (_shellwords($cfg->{cflags})) { |
|
684
|
0
|
0
|
|
|
|
|
if (/^-I(.+)$/) { push @{$options_out->{include_dirs}}, $1; } |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
685
|
0
|
|
|
|
|
|
else { push @{$options_out->{extra_compiler_flags}}, $_; } |
|
|
0
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
} |
|
687
|
0
|
|
|
|
|
|
push @{$options_out->{extra_linker_flags}}, _shellwords($cfg->{libs}) |
|
688
|
0
|
0
|
|
|
|
|
if length $cfg->{libs}; |
|
689
|
0
|
|
|
|
|
|
$success= 1; |
|
690
|
|
|
|
|
|
|
} |
|
691
|
|
|
|
|
|
|
} |
|
692
|
|
|
|
|
|
|
} |
|
693
|
0
|
|
|
|
|
|
return $success; |
|
694
|
|
|
|
|
|
|
} |
|
695
|
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
# Extract a list of program arguments from a commandline string. |
|
697
|
|
|
|
|
|
|
# This doesn't fully respect shell rules, just enough for what pkg-config is likely |
|
698
|
|
|
|
|
|
|
# to emit for the --cflags or --libs. Win32 would throw a wrench into proper shell |
|
699
|
|
|
|
|
|
|
# word parsing, anyway. |
|
700
|
|
|
|
|
|
|
sub _shellwords { |
|
701
|
0
|
|
|
0
|
|
|
my ($s)= @_; |
|
702
|
0
|
|
|
|
|
|
my @out; |
|
703
|
0
|
|
0
|
|
|
|
while (defined $s && length $s) { |
|
704
|
0
|
|
|
|
|
|
$s =~ s/^\s+//; |
|
705
|
0
|
0
|
|
|
|
|
last unless length $s; |
|
706
|
0
|
0
|
|
|
|
|
if ($s =~ s/^"((?:\\.|[^"])*)"//) { |
|
|
|
0
|
|
|
|
|
|
|
707
|
0
|
|
|
|
|
|
(my $w=$1)=~s/\\"/"/g; $w=~s/\\\\/\\/g; |
|
|
0
|
|
|
|
|
|
|
|
708
|
0
|
|
|
|
|
|
push @out, $w; |
|
709
|
|
|
|
|
|
|
} |
|
710
|
|
|
|
|
|
|
elsif ($s =~ s/^'((?:\\.|[^'])*)'//) { |
|
711
|
0
|
|
|
|
|
|
(my $w=$1)=~s/\\'/'/g; $w=~s/\\\\/\\/g; |
|
|
0
|
|
|
|
|
|
|
|
712
|
0
|
|
|
|
|
|
push @out, $w; |
|
713
|
|
|
|
|
|
|
} |
|
714
|
|
|
|
|
|
|
else { |
|
715
|
0
|
0
|
|
|
|
|
$s =~ s/^([^\s]+)// or last; |
|
716
|
0
|
|
|
|
|
|
push @out, $1; |
|
717
|
|
|
|
|
|
|
} |
|
718
|
|
|
|
|
|
|
} |
|
719
|
0
|
|
|
|
|
|
return @out; |
|
720
|
|
|
|
|
|
|
} |
|
721
|
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
=head2 append_config_includes |
|
723
|
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
$ftest->append_config_includes(@c_code); |
|
725
|
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
Append custom lines of C code to the L attribute. |
|
727
|
|
|
|
|
|
|
Each element of C<@c_code> will be given a trailing newline if it lacks one. |
|
728
|
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
=head2 append_config_local |
|
730
|
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
$ftest->append_config_local(@c_code); |
|
732
|
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
Append custom lines of C code to the L attribute. |
|
734
|
|
|
|
|
|
|
Each element of C<@c_code> will be given a trailing newline if it lacks one. |
|
735
|
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
=cut |
|
737
|
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
sub append_config_includes { |
|
739
|
0
|
|
|
0
|
1
|
|
my ($self, @c_code)= @_; |
|
740
|
0
|
|
|
|
|
|
s/\n?\z/\n/ for @c_code; |
|
741
|
0
|
|
|
|
|
|
$self->{config_includes} .= join '', @c_code; |
|
742
|
0
|
|
|
|
|
|
$self; |
|
743
|
|
|
|
|
|
|
} |
|
744
|
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
sub append_config_local { |
|
746
|
0
|
|
|
0
|
1
|
|
my ($self, @c_code)= @_; |
|
747
|
0
|
|
|
|
|
|
s/\n?\z/\n/ for @c_code; |
|
748
|
0
|
|
|
|
|
|
$self->{config_local} .= join '', @c_code; |
|
749
|
0
|
|
|
|
|
|
$self; |
|
750
|
|
|
|
|
|
|
} |
|
751
|
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
=head2 write_config_header |
|
753
|
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
$ftest->write_config_header($filename); |
|
755
|
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
Write the contents of L to a file, also with a standard include-guard of |
|
757
|
|
|
|
|
|
|
|
|
758
|
|
|
|
|
|
|
#ifndef FILENAME_H |
|
759
|
|
|
|
|
|
|
#define FILENAME_H |
|
760
|
|
|
|
|
|
|
... |
|
761
|
|
|
|
|
|
|
#endif |
|
762
|
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
You should choose a filename distinct to your project. |
|
764
|
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
=cut |
|
766
|
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
sub write_config_header { |
|
768
|
0
|
|
|
0
|
1
|
|
my ($self, $fname)= @_; |
|
769
|
0
|
|
|
|
|
|
(my $guard_macro= uc($fname)) =~ s/\W/_/g; |
|
770
|
0
|
|
|
|
|
|
_spew($fname, |
|
771
|
|
|
|
|
|
|
"#ifndef $guard_macro\n" |
|
772
|
|
|
|
|
|
|
."#define $guard_macro\n\n" |
|
773
|
|
|
|
|
|
|
.$self->config_header_text |
|
774
|
|
|
|
|
|
|
."\n#endif\n"); |
|
775
|
0
|
|
|
|
|
|
print "Wrote config to $fname\n"; |
|
776
|
0
|
|
|
|
|
|
return $self; |
|
777
|
|
|
|
|
|
|
} |
|
778
|
|
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
=head2 export_deps |
|
780
|
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
$ftest->export_deps($extutils_depends_obj); |
|
782
|
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
Export the include paths, compiler flags, and linker flags required for using this into the |
|
784
|
|
|
|
|
|
|
L object. |
|
785
|
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
=cut |
|
787
|
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
sub export_deps { |
|
789
|
0
|
|
|
0
|
1
|
|
my ($self, $extutils_depends)= @_; |
|
790
|
|
|
|
|
|
|
my @inc= ( |
|
791
|
0
|
|
|
|
|
|
map("-I$_", grep length, @{ $self->{include_dirs} }), |
|
792
|
0
|
|
|
|
|
|
@{ $self->{extra_compiler_flags} }, |
|
|
0
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
); |
|
794
|
0
|
0
|
|
|
|
|
$extutils_depends->set_inc(join ' ', @inc) if @inc; |
|
795
|
0
|
|
|
|
|
|
$extutils_depends->set_libs(join ' ', @{$self->{extra_linker_flags}}) |
|
796
|
0
|
0
|
|
|
|
|
if @{$self->{extra_linker_flags}}; |
|
|
0
|
|
|
|
|
|
|
|
797
|
0
|
|
|
|
|
|
return $self; |
|
798
|
|
|
|
|
|
|
} |
|
799
|
|
|
|
|
|
|
|
|
800
|
|
|
|
|
|
|
1; |
|
801
|
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
__END__ |