File Coverage

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

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