#!/usr/bin/perl use strict; use warnings; use Getopt::Long; use Pod::Usage; use Template; use Template::Provider::FromDATA; use XML::XPath; =head1 NAME gen_method_defs - Generate the method_defs hash for the Perl bindings =head1 SYNOPSIS gen_method_defs --xml /path/to/xml --strip_prefix a_prefix [--method method_name] =head1 DESCRIPTION gen_method_defs parses the XML generated by Doxygen and uses it to generate the contents of the C<%method_defs> variable, $arg_* variables for argument processing and validation, and some stub documentation, for use in the Perl bindings (F et al). It is intended to ease the bootstrapping of these wrappers, and the results may need further modifying before they work. =head1 OPTIONS =over =item --xml The path to the XML file that will be processed. =item --strip_prefix Subversion functions are grouped by type (e.g., all the client functions) and have a prefix. This prefix is not required by the Perl bindings, as the grouping happens by using Perl namespaces. This option specifies the prefix that should be removed from the functions. E.g., C. =item --method Specifies the method to be generated. If left blank then output is produced for all methods. =back =cut my %options = ( method => '', ); GetOptions('xml=s' => \$options{xml}, 'strip_prefix=s' => \$options{strip_prefix}, 'method=s' => \$options{method}, 'help' => \$options{help} ) or pod2usage(-verbose => 1); pod2usage(-verbose => 2) if $options{help}; pod2usage(-verbose => 1) unless $options{xml} and $options{strip_prefix}; my $provider = Template::Provider::FromDATA->new(); my $template = Template->new({ LOAD_TEMPLATES => [ $provider ], }); my $xp = XML::XPath->new(filename => $options{xml}); my $strip_prefix = $options{strip_prefix}; my $method = $options{method}; my $member_nodelist = $xp->findnodes("//sectiondef[\@kind='func']/memberdef[\@kind='function']/name[text() = '${strip_prefix}${method}']/parent::*"); $template->process('template', { xp => $xp, strip_prefix => $options{strip_prefix}, method => $options{method}, }) or die $template->error(); =head1 EXAMPLES To bootstrap the generation of the SVN::Client bindings, first run Doxygen ensuring that the doxygen.conf file contains the GENERATE_XML = YES directive. For example; doxygen doc/doxygen.conf This will generate various XML files. Find the one that pertains to the SVN client bindings, and then run: gen_method_defs --xml /path/to/xml --strip_prefix svn_client_ redirecting the output to a file. This output can then be inserted into Client.pm at the appropriate place, and adjusted as necessary. To generate output for just the method C you would run gen_method_defs --xml /path/to/xml --strip_prefix svn_client_ \ --method add =head1 BUGS None known. =head1 AUTHOR Nik Clayton =head1 COPYRIGHT AND LICENSE Copyright (c) 2007 CollabNet. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: "This product includes software developed by CollabNet (http://www.Collab.Net/)." Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear. 4. The hosted project names must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact info@collab.net. 5. Products derived from this software may not use the "Tigris" name nor may "Tigris" appear in their names without prior written permission of CollabNet. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COLLABNET OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =cut __DATA__ =begin no_more_pod # The =begin directive stops the POD checker from detecting the embedded # POD in the template that follows. Unfortunately that makes this POD # fail linting. __template__ [%- members = {}; IF method == ''; member_nodelist = xp.findnodes('//sectiondef[@kind="func"]/memberdef[@kind = "function"]'); ELSE; member_nodelist = xp.findnodes("//sectiondef[@kind='func']/memberdef[@kind='function']/name[text() = '${strip_prefix}${method}']/parent::*"); END; FOREACH node = member_nodelist; member_name = xp.findvalue('name', node); members.$member_name.node = node; END -%] [%- params = xp.findnodes('//param/declname') %] [%- FOREACH param = params %] [%- NEXT IF param.string_value.match('baton') %] [%- NEXT IF param.string_value.match('ctx') %] [%- NEXT IF param.string_value.match('pool') %] my $arg_[% param.string_value | lower %] = { name => '[% param.string_value | lower %]', spec => { }, }; [% END %] my %method_defs; [%- FOREACH member = members.keys.sort %] [%- member_name = member.remove(strip_prefix) %] [%- params = xp.findnodes('param/declname', members.$member.node) %] [%- final_params = [] %] [%- FOREACH param = params %] [%- NEXT IF param.string_value.match('baton') %] [%- NEXT IF param.string_value.match('ctx') %] [%- NEXT IF param.string_value.match('pool') %] [%- final_params.push(param) %] [%- END %] [%- param_len = 0 %] $method_defs{[% member_name %]} = { type => 'obj', args => [ [%- FOREACH param = final_params %] $arg_[% param.string_value | lower %], [%- IF param.string_value.length > param_len %] [%- param_len = param.string_value.length %] [%- END %] [%- END %] ], }; =head2 [% member_name %] [%- IF final_params.size %] $ctx->[% member_name %]({ [%- FOREACH param = final_params %] [% param.string_value | lower | format('%-*s', param_len) %] => ..., [%- END %] }) [%- ELSE %] $ctx->[% member_name %]() [%- END %] =cut [%- END %]