File Coverage

File:config/auto/icu.pm
Coverage:96.6%

linestmtbrancondsubcode
1# Copyright (C) 2001-2006, Parrot Foundation.
2# $Id: icu.pm 43920 2010-02-12 21:03:49Z coke $
3
4 - 17
=head1 NAME

config/auto/icu.pm - Detect International Components for Unicode (ICU)

=head1 DESCRIPTION

Determines whether ICU is available.  If so, configures ICU and adds
appropriate targets to the Makefile.

From the ICU home page (L<http://www.icu-project.org/>):  "ICU is a mature,
widely used set of C/C++ and Java libraries providing Unicode and
Globalization support for software applications."

=cut
18
19package auto::icu;
20
21
81
81
81
use strict;
22
81
81
81
use warnings;
23
24
81
81
81
use base qw(Parrot::Configure::Step);
25
26
81
81
81
use Cwd qw(cwd);
27
81
81
81
use File::Basename;
28
81
81
81
use lib qw( lib );
29
81
81
81
use Parrot::Configure::Utils qw(capture_output);
30
31
32sub _init {
33
86
    my $self = shift;
34
86
    my %data;
35
86
    $data{description} = q{Is ICU installed};
36
86
    $data{result} = q{};
37    # The following key-value pairs are defined here rather than being buried
38    # deep inside subroutines below. Also, so that they can be overridden
39    # during testing.
40
86
    $data{icuconfig_default} = q{icu-config};
41
86
    $data{icu_headers} = [ qw(ucnv.h utypes.h uchar.h) ];
42
86
    $data{icu_shared_pattern} = qr/-licui18n\w*/;
43
86
    return \%data;
44}
45
46sub runstep {
47
86
    my ( $self, $conf ) = @_;
48
49
86
    my ( $verbose, $icushared_opt, $icuheaders_opt,
50        $icuconfig_opt, $without_opt ) = $conf->options->get( qw|
51            verbose
52            icushared
53            icuheaders
54            icu-config
55            without-icu
56    | );
57
58    # If we haven't provided the path to a specific ICU configuration program
59    # on the command line, then we probe to see if the program 'icu-config' is
60    # available.
61
62    # From the icu-config(1) man page
63    # (L<http://linux.die.net/man/1/icu-config>):
64
65    # "icu-config simplifies the task of building and linking
66    # against ICU as compared to manually configuring user
67    # makefiles or equivalent. Because icu-config is an executable
68    # script, it also solves the problem of locating the ICU
69    # libraries and headers, by allowing the system PATH to locate
70    # it."
71
72    # $icuconfig is a string holding the name of an executable program.
73    # So if it's not provided on the command line -- or if it's explicitly
74    # ruled out by being provided with the value 'none' -- an empty string
75    # is its most appropriate value.
76
77
86
    my $icuconfig = $self->_handle_icuconfig_opt($icuconfig_opt);
78
79    # Oooh, how I wish we had Perl 5.10's defined-or operator available!
80
86
    my $icushared = (defined $icushared_opt)
81        ? $icushared_opt
82        : undef;
83
86
    my $icuheaders = (defined $icuheaders_opt)
84        ? $icuheaders_opt
85        : undef;
86
87    # $without_opt holds user's command-line value for --without-icu=?
88    # If it's a true value, there's no point in going further. We set the
89    # values needed in the Parrot::Configure object, set the step result and
90    # return. If, however, it's a false value, then we're going to try to
91    # configure with ICU and we proceed to probe for ICU.
92
93    # 1st possible return point
94
86
    if ( $without_opt ) {
95
2
        $self->_set_no_configure_with_icu($conf, q{not requested});
96
2
        return 1;
97    }
98
99
84
    my $autodetect = ( ! defined($icushared) )
100                            &&
101                        ( ! defined($icuheaders) );
102
103
84
    my $without = 0;
104
84
    ($icuconfig, $autodetect, $without) =
105        $self->_handle_autodetect( {
106            icuconfig => $icuconfig,
107            autodetect => $autodetect,
108            without => $without,
109            verbose => $verbose,
110    } );
111    # Inside _handle_autodetect(), $without can be set to 1 by
112    # _handle_search_for_icu_config(). In that case, we're abandoning our
113    # attempt to configure with ICU and so may return here.
114    # 2nd possible return point
115
84
    if ( $without ) {
116
2
        $self->_set_no_configure_with_icu($conf, q{no icu-config});
117
2
        print "Could not locate an icu-config program\n"
118            if $verbose;
119
2
        return 1;
120    }
121
122
82
    ($without, $icushared, $icuheaders) =
123        $self->_try_icuconfig(
124            $conf,
125            {
126                without => $without,
127                autodetect => $autodetect,
128                icuconfig => $icuconfig,
129                verbose => $verbose,
130                icushared => $icushared,
131                icuheaders => $icuheaders,
132            },
133        );
134    # 3rd possible return point
135
82
    if ( $without ) {
136
0
        $self->_set_no_configure_with_icu($conf, q{not found});
137
0
        return 1;
138    }
139
140
82
    _verbose_report($verbose, $icuconfig, $icushared, $icuheaders);
141
142
82
    $icuheaders = $self->_handle_icuconfig_errors( {
143        icushared => $icushared,
144        icuheaders => $icuheaders,
145    } );
146    # 4th possible return point. This one is a step configuration failure
147    # because we would have really expected it to succeed.
148
82
    return unless defined $icuheaders;
149
150
80
    my $icudir = dirname($icuheaders);
151
80
    $conf->data->set(
152        has_icu => 1,
153        icu_shared => $icushared,
154        icu_dir => $icudir,
155    );
156
157    # Add -I $Icuheaders if necessary.
158
80
    my $header = "unicode/ucnv.h";
159
80
    $conf->data->set( TEMP_testheaders => "#include <$header>\n" );
160
80
    $conf->data->set( TEMP_testheader => "$header" );
161
80
    $conf->cc_gen('config/auto/headers/test_c.in');
162
163    # Clean up.
164
80
    $conf->data->set( TEMP_testheaders => undef );
165
80
    $conf->data->set( TEMP_testheader => undef );
166
80
80
    eval { $conf->cc_build(); };
167
80
    my $ccflags_status = ( ! $@ && $conf->cc_run() =~ /^$header OK/ );
168
80
    _handle_ccflags_status(
169        $conf,
170        {
171            ccflags_status => $ccflags_status,
172            verbose => $verbose,
173            icuheaders => $icuheaders,
174        },
175    );
176
80
    $conf->cc_clean();
177
80
    $self->set_result("yes");
178    # 5th possible return point; this is the only really successful return.
179
80
    return 1;
180}
181
182########## INTERNAL SUBROUTINES ##########
183
184sub _set_no_configure_with_icu {
185
5
    my ($self, $conf, $result) = @_;
186
5
    $conf->data->set(
187        has_icu => 0,
188        icu_shared => '', # used for generating src/dynpmc/Makefile
189        icu_dir => '',
190    );
191
5
    $self->set_result($result);
192}
193
194sub _handle_icuconfig_opt {
195
90
    my ($self, $icuconfig_opt) = @_;
196
90
    my $icuconfig;
197
90
    if ( ( ! $icuconfig_opt ) or ( $icuconfig_opt eq q{none} ) ) {
198
87
        $icuconfig = q{};
199    }
200    elsif ( $icuconfig_opt eq '1' ) {
201
2
        $icuconfig = $self->{icuconfig_default};
202    }
203    else {
204
1
        $icuconfig = $icuconfig_opt;
205    }
206
90
    return $icuconfig;
207}
208
209sub _die_message {
210
4
    my $die = <<"HELP";
211Something is wrong with your ICU installation!
212
213   If you do not have a full ICU installation:
214
215   --without-icu Build parrot without ICU support
216   --icu-config=(file) Location of icu-config
217   --icuheaders=(path) Location of ICU headers without /unicode
218   --icushared=(flags) Full linker command to create shared libraries
219HELP
220#/
221
4
    return $die;
222}
223
224sub _handle_search_for_icu_config {
225
86
    my $self = shift;
226
86
    my $arg = shift;
227
86
    if ( $arg->{ret} ) {
228
4
        undef $arg->{icuconfig};
229
4
        $arg->{autodetect} = 0;
230
4
        $arg->{without} = 1;
231    }
232    else {
233
82
        $arg->{icuconfig} = $self->{icuconfig_default};
234
82
        if ($arg->{verbose}) {
235
1
            print "icu-config found... good!\n";
236        }
237    }
238
86
    return ( $arg->{icuconfig}, $arg->{autodetect}, $arg->{without} );
239}
240
241sub _handle_autodetect {
242
86
    my $self = shift;
243
86
    my $arg = shift;
244
86
    if ( $arg->{autodetect} ) {
245
83
        if ( ! $arg->{icuconfig} ) {
246
247
82
            my ( undef, undef, $ret ) =
248                capture_output( $self->{icuconfig_default}, "--exists" );
249
250
82
            print "Discovered $self->{icuconfig_default} --exists returns $ret\n"
251                if $arg->{verbose};
252
253
82
            ($arg->{icuconfig}, $arg->{autodetect}, $arg->{without}) =
254                $self->_handle_search_for_icu_config( {
255                    icuconfig => $arg->{icuconfig},
256                    autodetect => $arg->{autodetect},
257                    without => $arg->{without},
258                    verbose => $arg->{verbose},
259                    ret => $ret,
260            } );
261        }
262    } # end $autodetect true
263    else {
264
3
        if ($arg->{verbose}) {
265
1
            print "Specified an ICU config parameter,\n";
266
1
            print "ICU autodetection disabled.\n";
267        }
268    } # end $autodetect false
269
86
    return ( $arg->{icuconfig}, $arg->{autodetect}, $arg->{without} );
270}
271
272sub _try_icuconfig {
273
86
    my $self = shift;
274
86
    my $conf = shift;
275
86
    my $arg = shift;
276
86
    my $icushared = ( defined $arg->{icushared} )
277        ? $arg->{icushared}
278        : undef;
279
86
    my $icuheaders = ( defined $arg->{icuheaders} )
280        ? $arg->{icuheaders}
281        : undef;
282
86
    if (
283        ( ! $arg->{without} ) &&
284        $arg->{autodetect} &&
285        $arg->{icuconfig}
286    ) {
287        # ldflags
288
81
        print "Trying $arg->{icuconfig} with '--ldflags'\n"
289            if $arg->{verbose};
290
81
        $icushared = capture_output("$arg->{icuconfig} --ldflags");
291
81
        chomp $icushared;
292
81
        print "icushared: captured $icushared\n"
293            if $arg->{verbose};
294
81
        ($icushared, $arg->{without}) =
295            $self->_handle_icushared($icushared, $arg->{without});
296
81
        print "For icushared, found $icushared and $arg->{without}\n"
297            if $arg->{verbose};
298
299        # location of header files
300
81
        print "Trying $arg->{icuconfig} with '--prefix'\n"
301            if $arg->{verbose};
302
81
        $icuheaders = capture_output("$arg->{icuconfig} --prefix");
303
81
        chomp($icuheaders);
304
81
        print "icuheaders: captured $icuheaders\n"
305            if $arg->{verbose};
306
81
        ($icuheaders, $arg->{without}) =
307            $self->_handle_icuheaders($conf, $icuheaders, $arg->{without});
308
81
        print "For icuheaders, found $icuheaders and $arg->{without}\n"
309            if $arg->{verbose};
310    }
311
312
86
    return ($arg->{without}, $icushared, $icuheaders);
313}
314
315sub _handle_icushared {
316
84
    my $self = shift;
317
84
    my ($icushared, $without) = @_;
318
84
    if ( defined $icushared ) {
319
83
        chomp $icushared;
320
83
        $icushared =~ s/$self->{icu_shared_pattern}//; # "-licui18n32" too
321
83
        if (length $icushared == 0) {
322
1
            $without = 1;
323        }
324        else {
325            # on MacOS X there's sometimes an errornous \c at the end of the
326            # output line. Remove it.
327
82
            $icushared =~ s/\s\\c$//;
328        }
329    }
330
331
84
    return ($icushared, $without);
332}
333
334sub _handle_icuheaders {
335
85
    my $self = shift;
336
85
    my ($conf, $icuheaders, $without) = @_;
337
85
    if ( defined $icuheaders ) {
338
84
        chomp $icuheaders;
339
84
        if (! -d $icuheaders) {
340
1
            $without = 1;
341        }
342
84
        $icuheaders .= "/include";
343
84
        if (! -d $icuheaders) {
344
2
            $without = 1;
345        }
346    }
347
85
    return ($icuheaders, $without);
348}
349
350sub _verbose_report {
351
86
    my ($verbose, $icuconfig, $icushared, $icuheaders) = @_;
352
86
    if ($verbose) {
353
3
        print "icuconfig: $icuconfig\n" if defined $icuconfig;
354
3
        print "icushared='$icushared'\n" if defined $icushared;
355
3
        print "headers='$icuheaders'\n" if defined $icuheaders;
356    }
357}
358
359sub _handle_icuconfig_errors {
360
83
    my $self = shift;
361
83
    my $arg = shift;
362
83
    my $icuconfig_errors = 0;
363
364
83
    if ( ! defined $arg->{icushared} ) {
365
2
        warn "error: icushared not defined\n";
366
2
        $icuconfig_errors++;
367    }
368
369
83
    if ( ! ( defined $arg->{icuheaders} and -d $arg->{icuheaders} ) ) {
370
3
        warn "error: icuheaders not defined or invalid\n";
371
3
        $icuconfig_errors++;
372    }
373    else {
374
80
        $arg->{icuheaders} =~ s![\\/]$!!;
375
80
80
        foreach my $header ( @{ $self->{icu_headers} } ) {
376
240
            $header = "$arg->{icuheaders}/unicode/$header";
377
240
            if ( ! -e $header ) {
378
0
                $icuconfig_errors++;
379
0
                warn "error: ICU header '$header' not found\n";
380            }
381        }
382    }
383
384
83
    if ($icuconfig_errors) {
385
3
        warn _die_message();
386
3
        return;
387    }
388    else {
389
80
        return $arg->{icuheaders};
390    }
391}
392
393sub _handle_ccflags_status {
394
83
    my $conf = shift;
395
83
    my $arg = shift;
396
83
    if ($arg->{ccflags_status}) {
397        # Ok, we don't need anything more.
398
81
        if ($arg->{verbose}) {
399
1
            print "Your compiler found the icu headers... good!\n";
400        }
401    }
402    else {
403
2
        my $icuheaders = $arg->{icuheaders};
404
405
2
        my $icuflags;
406
2
        if ($icuheaders =~ /\s/) {
407
0
            $icuflags = "-I \"$arg->{icuheaders}\"";
408        }
409        else {
410
2
            $icuflags = "-I $arg->{icuheaders}";
411        }
412
413
2
        if ($arg->{verbose}) {
414
1
            print "Adding $icuflags to ccflags for icu headers.\n";
415        }
416
2
        $conf->data->add( ' ', ccflags => $icuflags );
417    }
418}
419
4201;
421
422# Local Variables:
423# mode: cperl
424# cperl-indent-level: 4
425# fill-column: 100
426# End:
427# vim: expandtab shiftwidth=4: