XML-Hash-XS-0.56/ 0000755 0001750 0001750 00000000000 14012055752 012040 5 ustar yura yura XML-Hash-XS-0.56/lib/ 0000755 0001750 0001750 00000000000 14012055752 012606 5 ustar yura yura XML-Hash-XS-0.56/lib/XML/ 0000755 0001750 0001750 00000000000 14012055752 013246 5 ustar yura yura XML-Hash-XS-0.56/lib/XML/Hash/ 0000755 0001750 0001750 00000000000 14012055752 014131 5 ustar yura yura XML-Hash-XS-0.56/lib/XML/Hash/XS.pm 0000644 0001750 0001750 00000025076 14012054603 015025 0 ustar yura yura package XML::Hash::XS;
use 5.008008;
use strict;
use warnings;
use vars qw($VERSION @EXPORT @EXPORT_OK);
use base 'Exporter';
@EXPORT_OK = @EXPORT = qw( hash2xml xml2hash );
$VERSION = '0.56';
require XSLoader;
XSLoader::load('XML::Hash::XS', $VERSION);
use vars qw($method $output $root $version $encoding $utf8 $indent $canonical
$use_attr $content $xml_decl $doc $max_depth $attr $text $trim $cdata
$comm $buf_size $keep_root $force_array $force_content $merge_text
$suppress_empty
);
# 'NATIVE' or 'LX'
$method = 'NATIVE';
# native options
$output = undef;
$root = 'root';
$version = '1.0';
$encoding = '';
$utf8 = 1;
$indent = 0;
$canonical = 0;
$use_attr = 0;
$content = undef;
$xml_decl = 1;
$keep_root = 0;#
$doc = 0;
$max_depth = 1024;
$buf_size = 4096;
$trim = 0;
$force_array = undef;
$force_content = 0;
$merge_text = 0;
$suppress_empty = 0;
# XML::Hash::LX options
$attr = '-';
$text = '#text';
$cdata = undef;
$comm = undef;
1;
__END__
=head1 NAME
XML::Hash::XS - Simple and fast hash to XML and XML to hash conversion written in C
=begin HTML

=end HTML
=head1 SYNOPSIS
use XML::Hash::XS;
my $xmlstr = hash2xml \%hash;
hash2xml \%hash, output => $fh;
my $hash = xml2hash $xmlstr;
my $hash = xml2hash \$xmlstr;
my $hash = xml2hash 'test.xml', encoding => 'cp1251';
my $hash = xml2hash $fh;
my $hash = xml2hash *STDIN;
Or OOP way:
use XML::Hash::XS qw();
my $conv = XML::Hash::XS->new(utf8 => 0, encoding => 'utf-8')
my $xmlstr = $conv->hash2xml(\%hash, utf8 => 1);
my $hash = $conv->xml2hash($xmlstr, encoding => 'cp1251');
=head1 DESCRIPTION
This module implements simple hash to XML and XML to hash conversion written in C.
During conversion uses minimum of memory, XML or hash is written directly without building DOM.
Some features are optional and are available with appropriate libraries:
=over 2
=item * XML::LibXML library is required in order to build DOM
=item * ICU or iconv library is required in order to perform charset conversions
=back
=head1 FUNCTIONS
=head2 hash2xml $hash, [ %options ]
$hash is reference to hash
hash2xml
{
node1 => 'value1',
node2 => [ 'value21', { node22 => 'value22' } ],
node3 => \'value3',
node4 => sub { return 'value4' },
node5 => sub { return { node51 => 'value51' } },
},
canonical => 1,
indent => 2,
;
will convert to:
value1
value21
value22
value3
value4
value51
and (use_attr=1):
hash2xml
{
node1 => 'value1',
node2 => [ 'value21', { node22 => 'value22' } ],
node3 => \'value3',
node4 => sub { return 'value4' },
node5 => sub { return { node51 => 'value51' } },
},
use_attr => 1,
canonical => 1,
indent => 2,
;
will convert to:
value21
=head2 xml2hash $xml, [ %options ]
$xml may be string, reference to string, file handle or tied file handle:
xml2hash 'text';
# output: 'text'
xml2hash 'text';
# output: { a => '1', b => '2', content => 'text' }
open(my $fh, '<', 'test.xml');
xml2hash $fh;
xml2hash *STDIN;
=head1 OPTIONS
=over 4
=item doc [ => 0 ] I<# hash2xml>
if doc is '1', then returned value is L.
=item root [ = 'root' ] I<# hash2xml>
Root node name.
=item version [ = '1.0' ] I<# hash2xml>
XML document version
=item encoding [ = 'utf-8' ] I<# hash2xml+xml2hash>
XML input/output encoding
=item indent [ = 0 ] I<# hash2xml>
if indent great than "0", XML output should be indented according to its hierarchic structure.
This value determines the number of spaces.
if indent is "0", XML output will all be on one line.
=item output [ = undef ] I<# hash2xml>
XML output method
if output is undefined, XML document dumped into string.
if output is FH, XML document writes directly to a filehandle or a stream.
=item canonical [ = 0 ] I<# hash2xml>
if canonical is "1", converter will be write hashes sorted by key.
if canonical is "0", order of the element will be pseudo-randomly.
=item use_attr [ = 0 ] I<# hash2xml>
if use_attr is "1", converter will be use the attributes.
if use_attr is "0", converter will be use tags only.
=item content [ = undef ] I<# hash2xml+xml2hash>
if defined that the key name for the text content(used only if use_attr=1).
=item force_array => [ = undef ] I<# xml2hash>
This option is similar to "ForceArray" from XML::Simple module: L.
=item force_content => [ = 0 ] I<# xml2hash>
This option is similar to "ForceContent" from XML::Simple module: L.
=item merge_text [ = 0 ] I<# xml2hash>
Setting this option to "1" will cause merge adjacent text nodes.
=item xml_decl [ = 1 ] I<# hash2xml>
if xml_decl is "1", output will start with the XML declaration ''.
if xml_decl is "0", XML declaration will not be output.
=item trim [ = 0 ] I<# hash2xml+xml2hash>
Trim leading and trailing whitespace from text nodes.
=item suppress_empty => [ = 0 ] I<# xml2hash>
This option is similar to "SuppressEmpty" from XMl::Simple module: L.
=item utf8 [ = 1 ] I<# hash2xml+xml2hash>
Turn on utf8 flag for strings if enabled.
=item max_depth [ = 1024 ] I<# xml2hash>
Maximum recursion depth.
=item buf_size [ = 4096 ] I<# hash2xml+xml2hash>
Buffer size for reading end encoding data.
=item keep_root [ = 0 ] I<# xml2hash>
Keep root element.
=item filter [ = undef ] I<# xml2hash>
Filter nodes matched by pattern and return reference to array of nodes.
Sample:
my $xml = <<'XML';
111
222
333
XML
my $nodes = xml2hash($xml, filter => '/root/item1');
# $nodes = [ 111 ]
my $nodes = xml2hash($xml, filter => ['/root/item1', '/root/item2']);
# $nodes = [ 111, 222 ]
my $nodes = xml2hash($xml, filter => qr[/root/item\d$]);
# $nodes = [ 111, 222, 333 ]
It may be used to parse large XML because does not require a lot of memory.
=item cb [ = undef ] I<# xml2hash>
This option is used in conjunction with "filter" option and defines callback
that will called for each matched node.
Sample:
xml2hash($xml, filter => qr[/root/item\d$], cb => sub {
print $_[0], "\n";
});
# 111
# 222
# 333
=item method [ = 'NATIVE' ] I<# hash2xml>
experimental support the conversion methods other libraries
if method is 'LX' then conversion result is the same as using L library
Note: for 'LX' method following additional options are available:
attr
cdata
text
comm
=back
=head1 OBJECT SERIALISATION(hash2xml)
=over 2
=item 1. When object has a "toString" method
In this case, the method of object is invoked in scalar context.
It must return a single scalar that can be directly encoded into XML.
Example:
use XML::LibXML;
local $XML::LibXML::skipXMLDeclaration = 1;
my $doc = XML::LibXML->new->parse_string('');
print hash2xml({ doc => $doc }, indent => 2, xml_decl => 0);
=>
=item 2. When object has overloaded stringification
In this case, the stringification method of object is invoked and result is directly encoded into XML.
Example:
package Test {
use overload '""' => sub { shift->stringify }, fallback => 1;
sub new {
my ($class, $str) = @_;
bless { str => $str }, $class;
}
sub stringify {
shift->{str}
}
}
my $obj = Test->new('test string');
print hash2xml({ obj => $obj }, indent => 2, xml_decl => 0);
=>
test string
=item 3. When object has a "iternext" method ("NATIVE" method only)
In this case, the method method will invoke a few times until the return value is not undefined.
Example:
my $count = 0;
my $o = bless {}, 'Iterator';
*Iterator::iternext = sub { $count++ < 3 ? { count => $count } : undef };
print hash2xml({ item => $o }, use_attr => 1, indent => 2, xml_decl => 0);
=>
This can be used to generate a large XML using minimum memory, example with DBI:
my $sth = $dbh->prepare('SELECT * FROM foo WHERE bar=?');
$sth->execute(...);
my $o = bless {}, 'Iterator';
*Iterator::iternext = sub { $sth->fetchrow_hashref() };
open(my $fh, '>', 'data.xml');
hash2xml({ row => $o }, use_attr => 1, indent => 2, xml_decl => 0, output => $fh);
=>
...
=back
=head1 BENCHMARK
Performance benchmark in comparison with some popular modules(hash2xml):
Rate XML::Hash XML::Hash::LX XML::Simple XML::Hash::XS
XML::Hash 65.0/s -- -6% -37% -99%
XML::Hash::LX 68.8/s 6% -- -33% -99%
XML::Simple 103/s 58% 49% -- -98%
XML::Hash::XS 4879/s 7404% 6988% 4658% --
Benchmark was done on L
=head1 AUTHOR
Yuriy Ustushenko, Eyoreek@yahoo.comE
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2012-2021 Yuriy Ustushenko
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.
=cut
XML-Hash-XS-0.56/MANIFEST 0000644 0001750 0001750 00000002173 14012055752 013174 0 ustar yura yura benchmark/benchmark.pl
Changes
inc/Devel/CheckLib.pm
lib/XML/Hash/XS.pm
LICENSE
Makefile.PL
MANIFEST This list of files
MANIFEST.SKIP
README
src/ppport.h
src/xh.c
src/xh.h
src/xh_buffer.c
src/xh_buffer.h
src/xh_buffer_helper.h
src/xh_config.h
src/xh_core.h
src/xh_dom.c
src/xh_dom.h
src/xh_encoder.c
src/xh_encoder.h
src/xh_h2x.c
src/xh_h2x.h
src/xh_h2x_lx.c
src/xh_h2x_native.c
src/xh_h2x_native_attr.c
src/xh_log.c
src/xh_log.h
src/xh_param.c
src/xh_param.h
src/xh_perl_buffer.c
src/xh_perl_buffer.h
src/xh_reader.c
src/xh_reader.h
src/xh_sort.c
src/xh_sort.h
src/xh_stack.c
src/xh_stack.h
src/xh_stash.c
src/xh_stash.h
src/xh_string.h
src/xh_writer.c
src/xh_writer.h
src/xh_x2h.c
src/xh_x2h.h
src/xh_xml.h
t/00-load.t
t/01-h2x.t
t/02-h2d.t
t/03-h2x-oop.t
t/04-h2x-lx.t
t/05-h2d-lx.t
t/06-x2h.t
t/07-x2h_filter.t
t/test.xml
t/test_cp1251.xml
t/test_cp1251_wo_decl.xml
t/test_null_terminated.xml
t/test_utf8.xml
typemap
XS.xs
xt/91-pod-syntax.t
xt/92-critic.t
xt/93-changes.t
META.yml Module YAML meta-data (added by MakeMaker)
META.json Module JSON meta-data (added by MakeMaker)
XML-Hash-XS-0.56/META.json 0000644 0001750 0001750 00000003256 14012055752 013467 0 ustar yura yura {
"abstract" : "Simple and fast hash to XML and XML to hash conversion written in C",
"author" : [
"Yuriy Ustushenko "
],
"dynamic_config" : 1,
"generated_by" : "ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150010",
"license" : [
"perl_5"
],
"meta-spec" : {
"url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
"version" : 2
},
"name" : "XML-Hash-XS",
"no_index" : {
"directory" : [
"t",
"inc"
]
},
"prereqs" : {
"build" : {
"requires" : {
"Config" : "0",
"ExtUtils::MakeMaker" : "0",
"File::Spec" : "0"
}
},
"configure" : {
"requires" : {
"Config" : "0",
"ExtUtils::MakeMaker" : "0",
"File::Spec" : "0"
}
},
"runtime" : {
"requires" : {
"Exporter" : "5.57",
"XSLoader" : "0",
"base" : "0"
}
},
"test" : {
"requires" : {
"Data::Dumper" : "0",
"Test::More" : "0"
}
}
},
"release_status" : "stable",
"resources" : {
"bugtracker" : {
"web" : "https://github.com/yoreek/XML-Hash-XS/issues"
},
"homepage" : "https://github.com/yoreek/XML-Hash-XS",
"license" : [
"http://dev.perl.org/licenses/"
],
"repository" : {
"type" : "git",
"url" : "https://github.com/yoreek/XML-Hash-XS.git",
"web" : "https://github.com/yoreek/XML-Hash-XS"
}
},
"version" : "0.56",
"x_serialization_backend" : "JSON::PP version 4.02"
}
XML-Hash-XS-0.56/XS.xs 0000644 0001750 0001750 00000003656 13325066767 012776 0 ustar yura yura #include "src/xh_config.h"
#include "src/xh_core.h"
MODULE = XML::Hash::XS PACKAGE = XML::Hash::XS
PROTOTYPES: DISABLE
xh_opts_t *
new(CLASS,...)
PREINIT:
xh_opts_t *opts;
CODE:
dXCPT;
if ((opts = xh_create_opts()) == NULL)
croak("Malloc error in new()");
XCPT_TRY_START
{
xh_parse_param(opts, 1, ax, items);
} XCPT_TRY_END
XCPT_CATCH
{
xh_destroy_opts(opts);
XCPT_RETHROW;
}
RETVAL = opts;
OUTPUT:
RETVAL
SV *
hash2xml(...)
PREINIT:
xh_h2x_ctx_t ctx;
SV *result;
CODE:
dXCPT;
XCPT_TRY_START
{
xh_h2x_init_ctx(&ctx, ax, items);
/* hack */
#ifdef XH_HAVE_DOM
if (ctx.opts.doc) {
result = xh_h2d(&ctx);
}
else {
result = xh_h2x(&ctx);
}
#else
result = xh_h2x(&ctx);
#endif
} XCPT_TRY_END
XCPT_CATCH
{
xh_h2x_destroy_ctx(&ctx);
XCPT_RETHROW;
}
if (ctx.opts.output != NULL) result = NULL;
xh_h2x_destroy_ctx(&ctx);
if (result == NULL) XSRETURN_UNDEF;
RETVAL = result;
OUTPUT:
RETVAL
SV *
xml2hash(...)
PREINIT:
xh_x2h_ctx_t ctx;
SV *result;
CODE:
dXCPT;
XCPT_TRY_START
{
xh_x2h_init_ctx(&ctx, ax, items);
result = xh_x2h(&ctx);
} XCPT_TRY_END
XCPT_CATCH
{
xh_x2h_destroy_ctx(&ctx);
XCPT_RETHROW;
}
if (ctx.opts.cb != NULL) result = NULL;
xh_x2h_destroy_ctx(&ctx);
if (result == NULL) XSRETURN_UNDEF;
RETVAL = result;
OUTPUT:
RETVAL
void
DESTROY(opts)
xh_opts_t *opts;
CODE:
xh_destroy_opts(opts);
free(opts);
XML-Hash-XS-0.56/benchmark/ 0000755 0001750 0001750 00000000000 14012055752 013772 5 ustar yura yura XML-Hash-XS-0.56/benchmark/benchmark.pl 0000755 0001750 0001750 00000003166 13177671425 016306 0 ustar yura yura #!/usr/bin/env perl
use FindBin;
use lib ("$FindBin::Bin/../blib/lib", "$FindBin::Bin/../blib/arch");
use LWP::Simple 'get';
use XML::Hash::LX;
use XML::Hash;
use XML::Simple;
use XML::LibXML;
use XML::Hash::XS qw();
use Benchmark qw(:all);
use XML::Bare;
my $xml_converter = XML::Hash->new();
my $xml = getXml();
my $xh_hash = $xml_converter->fromXMLStringtoHash($xml);
my $lx_hash = xml2hash($xml);
my $xs_hash = XMLin($xml);
my $xs_conv = XML::Hash::XS->new();
print "\nHash to XML:\n";
cmpthese -3, {
'XML::Hash' => sub {
my $oxml = $xml_converter->fromHashtoXMLString($xh_hash);
},
'XML::Simple' => sub {
my $oxml = XMLout($xs_hash);
},
'XML::Hash::LX' => sub {
my $oxml = hash2xml($lx_hash);
},
'XML::Hash::XS' => sub {
my $oxml = XML::Hash::XS::hash2xml($xs_hash);
},
};
print "\nXML to Hash:\n";
cmpthese -3, {
# 'XML::Hash' => sub {
# my $oxml = $xml_converter->fromXMLStringtoHash($xh_hash);
# },
'XML::Simple' => sub {
my $oxml = XMLin($xml);
},
'XML::Hash::LX' => sub {
my $oxml = hash2xml($lx_hash);
},
'XML::Hash::XS' => sub {
my $oxml = XML::Hash::XS::xml2hash($xml);
},
'XML::Bare' => sub {
my $oxml = XML::Bare->new(text => $xml)->parse();
},
};
sub getXml {
my $fn = "$FindBin::Bin/uploads.rdf";
open my $f, '<',$fn or return do {
warn "Fetching file\n";
my $data = get 'http://search.cpan.org/uploads.rdf';
open my $fo, '>', $fn;
print $fo $data;
close $fo;
$data;
};
warn "Have preloaded file\n";
local $/;
<$f>
}
XML-Hash-XS-0.56/typemap 0000644 0001750 0001750 00000001531 12455473143 013451 0 ustar yura yura TYPEMAP
xh_opts_t * T_CONV
xmlNodePtr O_NODE_OBJECT
INPUT
O_NODE_OBJECT
if( sv_isobject($arg) && (SvTYPE(SvRV($arg)) == SVt_PVMG) ) {
$var = INT2PTR($type,PmmSvNode($arg));
if ( $var == NULL ) {
croak( \"${Package}::$func_name() -- $var contains no data\" );
XSRETURN_UNDEF;
}
}
else {
croak( \"${Package}::$func_name() -- $var is not a blessed SV reference\" );
XSRETURN_UNDEF;
}
T_CONV
if (sv_isa($arg, \"XML::Hash::XS\")) {
IV tmp = SvIV((SV *) SvRV($arg));
$var = INT2PTR(xh_opts_t *, tmp);
} else
Perl_croak(aTHX_ \"%s: %s is not of type XML::Hash::XS\",
${$ALIAS?\q[GvNAME(CvGV(cv))]:\qq[\"$pname\"]},
\"$var\")
OUTPUT
T_CONV
sv_setref_pv($arg, \"XML::Hash::XS\", (void *) $var);
XML-Hash-XS-0.56/Makefile.PL 0000644 0001750 0001750 00000035562 14012054616 014023 0 ustar yura yura # Makefile.PL for XML::Hash::XS.
#
# This program is free software; you can redistribute it and/or modify it
# under the same terms as Perl itself.
# Copyright 2012-2021 Yuriy Ustushenko, all rights reserved.
#
# Derived from the module XML::LibXML
# Copyright 2001-2003 AxKit.com Ltd., 2002-2006 Christian Glahn, 2006-2009 Petr Pajas
use 5.008008;
use warnings;
use strict;
use lib qw(inc);
use Devel::CheckLib;
use ExtUtils::MakeMaker;
use Config;
use File::Spec;
$| = 0;
our %PARAMS = parse_params();
my %WriteMakefileArgs = (
NAME => 'XML::Hash::XS',
VERSION_FROM => 'lib/XML/Hash/XS.pm',
ABSTRACT_FROM => 'lib/XML/Hash/XS.pm',
AUTHOR => 'Yuriy Ustushenko ',
test => {
TESTS => 't/*.t'. ($ENV{AUTHOR_TESTING} ? ' xt/*.t' : '')
},
META_MERGE => {
'meta-spec' => { version => 2 },
resources => {
license => 'http://dev.perl.org/licenses/',
homepage => 'https://github.com/yoreek/XML-Hash-XS',
repository => {
type => 'git',
url => 'https://github.com/yoreek/XML-Hash-XS.git',
web => 'https://github.com/yoreek/XML-Hash-XS',
},
bugtracker => {
web => 'https://github.com/yoreek/XML-Hash-XS/issues',
},
},
},
META_ADD => {
prereqs => {
configure => {
requires => {
'ExtUtils::MakeMaker' => 0,
'Config' => 0,
'File::Spec' => 0,
},
},
build => {
requires => {
'ExtUtils::MakeMaker' => 0,
'Config' => 0,
'File::Spec' => 0,
},
},
runtime => {
requires => {
'Exporter' => 5.57,
'base' => 0,
'XSLoader' => 0,
},
},
test => {
requires => {
'Test::More' => 0,
'Data::Dumper' => 0,
},
},
},
},
MIN_PERL_VERSION => 5.008008,
LICENSE => 'perl_5',
CCFLAGS => $Config{ccflags} . ' -o $@',
OBJECT => join(' ', '$(BASEEXT)$(OBJ_EXT)', map { s/\.c$/\$(OBJ_EXT)/; $_ } glob('src/*.c')),
C => [ 'XS.c', glob('src/*.c') ],
H => [ glob('src/*.h') ],
clean => { FILES => 'src/*.o' },
extra_config(),
);
my $eumm_version = eval $ExtUtils::MakeMaker::VERSION;
for (qw(configure build test runtime)) {
my $key = $_ eq 'runtime' ? 'PREREQ_PM' : uc $_.'_REQUIRES';
next unless exists $WriteMakefileArgs{META_ADD}{prereqs}{$_}
or exists $WriteMakefileArgs{$key};
my $r = $WriteMakefileArgs{$key} = {
%{$WriteMakefileArgs{META_ADD}{prereqs}{$_}{requires} || {}},
%{delete $WriteMakefileArgs{$key} || {}},
};
defined $r->{$_} or delete $r->{$_} for keys %$r;
}
$WriteMakefileArgs{BUILD_REQUIRES} = {
%{$WriteMakefileArgs{BUILD_REQUIRES} || {}},
%{delete $WriteMakefileArgs{TEST_REQUIRES}}
} if $eumm_version < 6.63_03;
$WriteMakefileArgs{PREREQ_PM} = {
%{$WriteMakefileArgs{PREREQ_PM}},
%{delete $WriteMakefileArgs{BUILD_REQUIRES}}
} if $eumm_version < 6.55_01;
delete $WriteMakefileArgs{CONFIGURE_REQUIRES}
if $eumm_version < 6.51_03;
delete @WriteMakefileArgs{qw(META_ADD META_MERGE)}
if $eumm_version < 6.46;
delete $WriteMakefileArgs{MIN_PERL_VERSION}
if $eumm_version < 6.48;
delete $WriteMakefileArgs{LICENSE}
if $eumm_version < 6.31;
WriteMakefile(%WriteMakefileArgs);
sub extra_config {
my $available_libs = get_available_libs();
return unless scalar %$available_libs;
my (%config, %summary_config);
my @options = qw(lib libpath incpath);
# remove unneded libxml2
delete $available_libs->{xml2} unless exists $available_libs->{'XML::LibXML'};
foreach my $lib_config (values %$available_libs) {
foreach my $option (@options) {
my $value = $lib_config->{$option} or next;
$value = [ split(/\s+/, $value) ] unless ref $value;
$config{$option}{$_} = 1 for @$value;
}
}
if ( $PARAMS{LIBS} ) {
$summary_config{LIBS} = [ $PARAMS{LIBS} ];
}
else {
my @libs = (
# order is important!
( map { "-L$_" } keys %{ $config{libpath} || {} } ),
( map {
$Config{cc} eq 'cl' && $_ !~ /\.lib$/ ? "-l$_.lib" : "-l$_"
} keys %{ $config{lib} || {} }
),
);
$summary_config{LIBS} = [$Config::Config{ldflags} || '', @libs]
if scalar @libs;
}
if ( $PARAMS{INC} ) {
$summary_config{INC} = [ $PARAMS{INC} ];
}
else {
my @inc = map { "-I$_" } keys %{ $config{incpath} || {} };
$summary_config{INC} = \@inc if scalar @inc;
}
my @define = map { s/:/_/g; "-DXH_HAVE_" . uc($_) } keys %$available_libs;
$summary_config{DEFINE} = \@define if scalar @define;
# convert array to string
$summary_config{$_} = join(' ', @{ $summary_config{$_} }) for keys %summary_config;
# CCFLAGS
$summary_config{CCFLAGS} = ( $Config{ccflags} || '' ) . ' -o $@';
$summary_config{CCFLAGS} .= ' -g -Wall -Werror -Wextra -pedantic -Wno-clobbered -std=c99 -DPERL_GCC_PEDANTIC -DWITH_DEBUG -O0'
if $PARAMS{DEBUG};
$summary_config{CCFLAGS} .= ' -DWITH_TRACE'
if $PARAMS{DEBUG} && $PARAMS{TRACE};
# Fix from Glib module.
# On OpenBSD, any program that directly or indirectly wants to load
# libpthread.so must do so from the start. But when perl is built without
# ithreads, it will also most likely not be compiled with "-pthread". When
# libglib/libgobject then go and try to load libpthread.so, the loader will
# error out.
if ($^O =~ /^openbsd|netbsd$/
&& $Config::Config{ldflags} !~ m/-pthread\b/
&& $Config::Config{ccflags} !~ m/-pthread\b/) {
warn " ***\n *** on OpenBSD, we either need perl linked with '-pthread',\n",
" *** or we need to set LD_PRELOAD=libpthread.so; doing the latter now...\n ***\n";
$summary_config{macro} = { FULLPERLRUN => 'LD_PRELOAD=/usr/lib/libpthread.so $(FULLPERL)' };
}
return %summary_config;
}
sub get_available_libs {
my @COMMON_LIB_PATH = (
'/usr/lib',
'/usr/local/lib',
'/usr/pkg/lib',
'/opt/local/lib',
);
my @COMMON_INC_PATH = (
'/usr/include',
'/usr/local/include',
'/usr/pkg/include',
'/opt/local/include',
);
my @REQUIRED_LIBS = (
{
perl => 1,
name => 'XML::LibXML',
version => 1.62,
mandatory => 0,
},
{
name => 'xml2',
mandatory => 0,
configs => [
{
lib => sub {
my $results = $PARAMS{WIN32} ? undef : backtick(
join('/bin/', grep { $_ } ($ENV{XMLPREFIX} || $PARAMS{XMLPREFIX}, 'xml2-config')),
'--libs',
);
$results ? [ map { substr($_, 2) } grep { $_ =~ /^-l/ } split(/\s+/, $results) ] : undef;
},
libpath => sub {
my $results = $PARAMS{WIN32} ? undef : backtick(
join('/bin/', grep { $_ } ($ENV{XMLPREFIX} || $PARAMS{XMLPREFIX}, 'xml2-config')),
'--libs',
);
$results ? [ map { substr($_, 2) } grep { $_ =~ /^-L/ } split(/\s+/, $results) ] : undef;
},
incpath => sub {
my $results = $PARAMS{WIN32} ? undef : backtick(
join('/bin/', grep { $_ } ($ENV{XMLPREFIX} || $PARAMS{XMLPREFIX}, 'xml2-config')),
'--cflags',
);
$results ? [ map { substr($_, 2) } grep { $_ =~ /^-I/ } split(/\s+/, $results) ] : undef;
},
header => 'libxml/parser.h',
},
{
lib => 'xml2 m z',
libpath => \@COMMON_LIB_PATH,
incpath => [
@COMMON_INC_PATH,
'/usr/include/libxml2',
'/usr/local/include/libxml2',
'/usr/pkg/include/libxml2',
'/opt/local/include/libxml2',
],
header => 'libxml/parser.h',
},
],
},
{
name => 'iconv',
configs => [
{
lib => 'c',
header => 'iconv.h',
function => 'iconv_t iconv = iconv_open("UTF-8", "UTF-8");(void) iconv_close(iconv);return 0;',
},
{
lib => 'iconv',
header => 'iconv.h',
function => 'iconv_t iconv = iconv_open("UTF-8", "UTF-8");(void) iconv_close(iconv);return 0;',
},
],
},
{
name => 'icu',
configs => [
{
lib => sub {
my $results = $PARAMS{WIN32} ? undef : backtick('icu-config', '--ldflags-libsonly');
$results ? [ map { substr($_, 2) } grep { $_ =~ /^-l/ } split(/\s+/, $results) ] : undef;
},
libpath => sub {
my $results = $PARAMS{WIN32} ? undef : backtick('icu-config', '--ldflags-searchpath');
$results ? [ map { substr($_, 2) } grep { $_ =~ /^-L/ } split(/\s+/, $results) ] : undef;
},
incpath => sub {
my $results = $PARAMS{WIN32} ? undef : backtick('icu-config', '--cppflags-searchpath');
$results ? [ map { substr($_, 2) } grep { $_ =~ /^-I/ } split(/\s+/, $results) ] : undef;
},
header => [
'unicode/utypes.h',
'unicode/ucnv.h',
],
function => 'UErrorCode status = U_ZERO_ERROR;UConverter *uconv = ucnv_open("UTF-8", &status);ucnv_close(uconv);return 0;',
},
{
lib => 'icui18n icuuc icudata',
libpath => \@COMMON_LIB_PATH,
incpath => \@COMMON_INC_PATH,
header => [
'unicode/utypes.h',
'unicode/ucnv.h',
],
function => 'UErrorCode status = U_ZERO_ERROR;UConverter *uconv = ucnv_open("UTF-8", &status);ucnv_close(uconv);return 0;',
},
],
},
);
my %libs;
my @options = qw(lib libpath incpath header function);
foreach my $info (@REQUIRED_LIBS) {
my $lib = $info->{name};
if ( $info->{perl} ) {
my $ver = $info->{version} || '';
print "Checking for '$lib'... ";
eval "use $lib $ver";
if (my $err = $@) {
print "no\n";
print $err if $PARAMS{DEBUG}
}
else {
print "yes\n";
$libs{$lib} = {};
}
}
else {
foreach my $lib_config (@{ $info->{configs} }) {
my %config;
@config{@options} = map {
my $results = $lib_config->{$_};
$results = ($results and ref($results) eq 'CODE') ? &$results() : $results;
($results and $_ ne 'function' and !ref($results))
? [ split(/\s+/, $results) ]
: $results
;
} @options;
next unless $config{lib} || $config{perl_lib};
print "Checking for '$lib'... ";
delete $config{$_} for grep { !$config{$_} } keys %config;
if ( $PARAMS{LIBS} ) {
delete $config{lib};
delete $config{libpath};
$config{LIBS} = $PARAMS{LIBS};
}
if ( $PARAMS{INC} ) {
delete $config{incpath};
$config{INC} = $PARAMS{INC};
}
if ( check_lib(debug => $PARAMS{DEBUG}, %config) ) {
print "yes\n";
$libs{$lib} = \%config;
last;
}
print "no\n";
}
}
if ($info->{mandatory} && !$libs{$lib}) {
print STDERR <<"DEATH";
'$lib' library not found
Try setting LIBS and INC values on the command line
Also, you may try to run perl Makefile.PL with the DEBUG=1 parameter
to see the exact reason why the detection of '$lib' installation
failed or why Makefile.PL was not able to compile a test program.
DEATH
exit 1;
}
}
return \%libs;
}
sub parse_params {
my %params;
@params{qw(DEBUG TRACE INC LIBS XMLPREFIX)} = ();
@ARGV = grep {
my ($key, $val) = split(/=/, $_, 2);
if (exists $params{$key}) {
$params{$key} = $val; 0
} else { 1 }
} @ARGV;
$params{WIN32} = ($^O =~ /Win32/);
if ( $params{WIN32} ) {
$params{DEVNULL} = 'DEVNULL';
}
else {
$params{DEVNULL} = eval { File::Spec->devnull };
if ($@) { $params{DEVNULL} = '/dev/null' }
}
return %params;
}
sub backtick {
my $command = join(' ', @_);
if ($PARAMS{DEBUG}) {
print "Cmd: $command \n";
my $results = `$command`;
if ($? != 0) {
warn "Backticks call to '$command' failed";
return;
}
chomp $results;
return $results;
}
local *OLDOUT;
local *OLDERR;
open(OLDOUT, ">&STDOUT");
open(OLDERR, ">&STDERR");
open(STDOUT, ">$PARAMS{DEVNULL}");
open(STDERR, ">$PARAMS{DEVNULL}");
my $results = `$command`;
my $retval = $?;
open(STDOUT, ">&OLDOUT");
open(STDERR, ">&OLDERR");
if ($retval != 0) {
if ($PARAMS{DEBUG}) {
warn "backticks call to '$command' failed";
}
return;
}
chomp $results;
return $results;
}
package MY;
# Fix bug occurs while using multiple job compile:
# gcc: error: xh_buffer.c: No such file or directory
# Replace $* to $<
# $*: dir/file.o -> file.c
# $<: dir/file.o -> dir/file.c
sub c_o {
my $inherited = shift->SUPER::c_o(@_);
$inherited =~ s/\$\*.*/\$