]>
Commit | Line | Data |
---|---|---|
9a4968f9 WB |
1 | #!/usr/bin/env perl |
2 | ||
3 | # Create a perl package given a product and package name. | |
4 | ||
5 | use strict; | |
6 | use warnings; | |
7 | ||
8 | use File::Path qw(make_path); | |
9 | ||
10 | my @packages; | |
11 | ||
12 | my $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 | ||
39 | sub 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 | ||
51 | if (!@ARGV) { | |
52 | help(\*STDERR); | |
53 | exit(1); | |
54 | } | |
55 | ||
c05806d1 WB |
56 | my $params = { |
57 | 'include-file' => [], | |
58 | }; | |
9a4968f9 WB |
59 | ARGPARSE: 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 | ||
96 | my $lib_package = $params->{'lib-package'} | |
97 | or die "missing --lib-package parameter\n"; | |
98 | my $lib_prefix = $params->{'lib-prefix'} | |
99 | or die "missing --lib-prefix parameter\n"; | |
100 | my $lib = $params->{'lib'} | |
101 | or die "missing --lib parameter\n"; | |
102 | my $lib_tag = $params->{'lib-tag'}; | |
103 | my $debug_libpath = $params->{'debug-libpath'} // ''; | |
c05806d1 WB |
104 | my $extra_code = ''; |
105 | for 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 | |
112 | sub pkg2file { | |
113 | return ($_[0] =~ s@::@/@gr) . ".pm"; | |
114 | } | |
115 | ||
116 | sub parentdir { | |
117 | if ($_[0] =~ m@^(.*)/[^/]+@) { | |
118 | return $1 | |
119 | } else { | |
120 | die "bad path: '$_[0]', try adding a directory\n"; | |
121 | } | |
122 | } | |
123 | ||
124 | my $template = <<'EOF'; | |
125 | package {{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 | ||
143 | This is the base module of all {{LIBRARY_PREFIX}} bindings. | |
144 | Its job is to ensure the 'lib{{LIBRARY}}.so' library is loaded and provide a 'bootstrap' | |
145 | method to load the actual code. | |
146 | ||
147 | =cut | |
148 | ||
8591b61e WB |
149 | use strict; |
150 | use warnings; | |
151 | ||
9a4968f9 WB |
152 | use DynaLoader; |
153 | ||
c05806d1 | 154 | {{EXTRA_CODE}} |
c6d49a8c WB |
155 | sub library { '{{LIBRARY}}' } |
156 | ||
157 | sub autodirs { map { "$_/auto" } @INC; } | |
158 | sub envdirs { grep { length($_) } split(/:+/, $ENV{LD_LIBRARY_PATH} // '') } | |
159 | ||
160 | sub 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! |
ff168305 | 167 | sub libfile { find_lib(library()) } |
9a4968f9 WB |
168 | |
169 | sub load : prototype($) { | |
170 | my ($pkg) = @_; | |
171 | ||
172 | my $mod_name = $pkg->library(); | |
173 | ||
ff168305 | 174 | my $mod_file = libfile(); |
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 | ||
186 | sub 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 | ||
203 | BEGIN { | |
204 | __PACKAGE__->load(); | |
205 | __PACKAGE__->bootstrap(); | |
fa7d62bf | 206 | init() if __PACKAGE__->can("init"); |
9a4968f9 WB |
207 | } |
208 | ||
209 | 1; | |
210 | EOF | |
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 | ||
218 | if ($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 | ||
228 | for 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 | } |