]> git.proxmox.com Git - perlmod.git/blame - perlmod-bin/genpackage.pl
perlmod-bin: actually use 'libfile'
[perlmod.git] / perlmod-bin / genpackage.pl
CommitLineData
9a4968f9
WB
1#!/usr/bin/env perl
2
3# Create a perl package given a product and package name.
4
5use strict;
6use warnings;
7
8use File::Path qw(make_path);
9
10my @packages;
11
12my $opts = {
13 'lib-tag' => [
14 'TAG',
15 'An identifier used to avoid loading multiple libraries with the same shared code',
16 ],
17 'lib-package' => [
18 'Package',
19 'Main package to generate for loading the library',
20 ],
21 'lib-prefix' => [
22 'Prefix',
23 'Package prefix used for documentation in the library package.',
24 ],
25 'lib' => [
26 'LIBNAME',
27 "The .so name without the 'lib' prefix.",
28 ],
29 'debug-libpath' => [
30 'PATH',
31 "Path to a debug library, usually ./target/debug.",
32 ],
c05806d1
WB
33 'include-file' => [
34 'PATH',
35 "Path to additional perl code to include in the package after the 'use' statements",
36 ],
9a4968f9
WB
37};
38
39sub help {
40 my ($fd) = @_;
41
42 print {$fd} "usage: $0 OPTIONS <packages...>\n";
43 print {$fd} "mandatory OPTIONS are:\n";
44 for my $o (sort keys %$opts) {
45 my ($arg, $desc) = $opts->{$o}->@*;
46 my $p = "--$o=$arg";
47 printf {$fd} " %20s %s\n", $p, $desc;
48 }
49}
50
51if (!@ARGV) {
52 help(\*STDERR);
53 exit(1);
54}
55
c05806d1
WB
56my $params = {
57 'include-file' => [],
58};
9a4968f9
WB
59ARGPARSE: while (@ARGV) {
60 my $arg = shift @ARGV;
61
62 last if $arg eq '--';
63
64 if ($arg eq '-h' || $arg eq '--help') {
65 help(\*STDOUT);
66 exit(0);
67 }
68
69 for my $o (keys %$opts) {
70 if ($arg =~ /^(?:--\Q$o\E=)(.+)$/) {
c05806d1 71 $arg = $1;
9a4968f9
WB
72 } elsif ($arg =~ /^--\Q$o\E$/) {
73 $arg = shift @ARGV;
c05806d1
WB
74 } else {
75 next;
76 };
77 die "--$o requires an argument\n" if !defined($arg);
78 if (ref($params->{$o}) eq 'ARRAY') {
79 push $params->{$o}->@*, $arg;
80 } else {
9a4968f9 81 die "multiple --$o options provided\n" if defined($params->{$o});
9a4968f9 82 $params->{$o} = $arg;
9a4968f9 83 }
c05806d1 84 next ARGPARSE;
9a4968f9
WB
85 }
86
87 if ($arg =~ /^-/) {
88 help(\*STDERR);
89 exit(1);
90 }
91
92 unshift @ARGV, $arg;
93 last;
94}
95
96my $lib_package = $params->{'lib-package'}
97 or die "missing --lib-package parameter\n";
98my $lib_prefix = $params->{'lib-prefix'}
99 or die "missing --lib-prefix parameter\n";
100my $lib = $params->{'lib'}
101 or die "missing --lib parameter\n";
102my $lib_tag = $params->{'lib-tag'};
103my $debug_libpath = $params->{'debug-libpath'} // '';
c05806d1
WB
104my $extra_code = '';
105for my $file ($params->{'include-file'}->@*) {
106 open(my $fh, '<', $file) or die "failed to open file '$file' - $!\n";
107 my $more = do { local $/ = undef; <$fh> };
108 die "error reading '$file': $!\n" if !defined($more);
109 $extra_code .= $more;
110}
9a4968f9
WB
111
112sub pkg2file {
113 return ($_[0] =~ s@::@/@gr) . ".pm";
114}
115
116sub parentdir {
117 if ($_[0] =~ m@^(.*)/[^/]+@) {
118 return $1
119 } else {
120 die "bad path: '$_[0]', try adding a directory\n";
121 }
122}
123
124my $template = <<'EOF';
125package {{LIBRARY_PACKAGE}};
126
127=head1 NAME
128
129{{LIBRARY_PACKAGE}} - base module for {{LIBRARY_PREFIX}} rust bindings
130
131=head1 SYNOPSIS
132
133 package {{LIBRARY_PREFIX}}::RS::SomeBindings;
134
135 use base '{{LIBRARY_PACKAGE}}';
136
137 BEGIN { __PACKAGE__->bootstrap(); }
138
139 1;
140
141=head1 DESCRIPTION
142
143This is the base module of all {{LIBRARY_PREFIX}} bindings.
144Its job is to ensure the 'lib{{LIBRARY}}.so' library is loaded and provide a 'bootstrap'
145method to load the actual code.
146
147=cut
148
8591b61e
WB
149use strict;
150use warnings;
151
9a4968f9
WB
152use DynaLoader;
153
c05806d1 154{{EXTRA_CODE}}
c6d49a8c
WB
155sub library { '{{LIBRARY}}' }
156
157sub autodirs { map { "$_/auto" } @INC; }
158sub envdirs { grep { length($_) } split(/:+/, $ENV{LD_LIBRARY_PATH} // '') }
159
160sub find_lib {
161 my ($mod_name) = @_;
162 my @dirs = map { "-L$_" } (envdirs(), autodirs());
163 return DynaLoader::dl_findfile(@dirs, $mod_name);
9a4968f9
WB
164}
165
c6d49a8c 166# Keep on a single line, potentially modified by testsuite!
760f47bd 167sub libfile { find_lib($pkg->library()) }
9a4968f9
WB
168
169sub load : prototype($) {
170 my ($pkg) = @_;
171
172 my $mod_name = $pkg->library();
173
760f47bd 174 my $mod_file = libfile($mod_name);
9a4968f9
WB
175 die "failed to locate shared library for $mod_name (lib${mod_name}.so)\n" if !$mod_file;
176
177 my $lib = DynaLoader::dl_load_file($mod_file)
178 or die "failed to load library '$mod_file'\n";
179
180 my $data = ($::{'{{LIBRARY_TAG}}-rs-library'} //= {});
181 $data->{$mod_name} = $lib;
182 $data->{-current} //= $lib;
183 $data->{-package} //= $pkg;
184}
185
186sub bootstrap {
187 my ($pkg) = @_;
188
189 my $mod_name = $pkg->library();
190
191 my $bootstrap_name = 'boot_' . ($pkg =~ s/::/__/gr);
192
193 my $lib = $::{'{{LIBRARY_TAG}}-rs-library'}
194 or die "rust library not available for '{{LIBRARY_PREFIX}}'\n";
195 $lib = $lib->{$mod_name};
196
197 my $sym = DynaLoader::dl_find_symbol($lib, $bootstrap_name);
198 die "failed to locate '$bootstrap_name'\n" if !defined $sym;
199 my $boot = DynaLoader::dl_install_xsub($bootstrap_name, $sym, "src/FIXME.rs");
200 $boot->();
201}
202
203BEGIN {
204 __PACKAGE__->load();
205 __PACKAGE__->bootstrap();
fa7d62bf 206 init() if __PACKAGE__->can("init");
9a4968f9
WB
207}
208
2091;
210EOF
c05806d1 211$template =~ s/\{\{EXTRA_CODE\}\}/$extra_code/g;
9a4968f9
WB
212$template =~ s/\{\{LIBRARY_PACKAGE\}\}/$lib_package/g;
213$template =~ s/\{\{LIBRARY_PREFIX\}\}/$lib_prefix/g;
214$template =~ s/\{\{LIBRARY_TAG\}\}/$lib_tag/g;
215$template =~ s/\{\{LIBRARY\}\}/$lib/g;
216$template =~ s/\{\{DEBUG_LIBPATH\}\}/$debug_libpath/g;
217
218if ($lib ne '-') {
219 my $path = pkg2file($lib_package);
220 print "Generating $path\n";
221
222 make_path(parentdir($path), { mode => 0755 });
223 open(my $fh, '>', $path) or die "failed to open '$path' for writing: $!\n";
224 print {$fh} $template;
225 close($fh);
226}
227
228for my $package (@ARGV) {
229 my $path = ($package =~ s@::@/@gr) . ".pm";
230
231 print "Generating $path\n";
232
233 $path =~ m@^(.*)/[^/]+@;
234 make_path($1, { mode => 0755 });
235
236 open(my $fh, '>', $path) or die "failed to open '$path' for writing: $!\n";
237 print {$fh} "package $package;\n";
238 print {$fh} "use base '$lib_package';\n";
239 print {$fh} "BEGIN { __PACKAGE__->bootstrap(); }\n";
240 print {$fh} "1;\n";
241 close($fh);
242}