File Coverage

blib/lib/SPVM/Builder.pm
Criterion Covered Total %
statement 96 144 66.6
branch 11 26 42.3
condition 3 9 33.3
subroutine 18 23 78.2
pod 0 10 0.0
total 128 212 60.3


line stmt bran cond sub pod time code
1             package SPVM::Builder;
2              
3 278     278   95348 use strict;
  278         635  
  278         8212  
4 278     278   1379 use warnings;
  278         549  
  278         7135  
5              
6 278     278   1442 use Carp 'confess';
  278         584  
  278         12612  
7 278     278   1650 use Scalar::Util 'weaken';
  278         666  
  278         14217  
8 278     278   1813 use File::Path 'mkpath';
  278         576  
  278         21316  
9 278     278   2282 use File::Basename 'dirname', 'basename';
  278         765  
  278         15523  
10              
11 278     278   7633 use SPVM ();
  278         715  
  278         7824  
12              
13 278     278   148222 use SPVM::Builder::CC;
  278         1124  
  278         11790  
14 278     278   123223 use SPVM::Builder::Compiler;
  278         821  
  278         8935  
15 278     278   125329 use SPVM::Builder::Runtime;
  278         798  
  278         8851  
16 278     278   116998 use SPVM::Builder::Env;
  278         1137  
  278         9518  
17 278     278   113970 use SPVM::Builder::Stack;
  278         1064  
  278         368876  
18              
19             # Fields
20             sub build_dir {
21 839     839 0 2172 my $self = shift;
22 839 50       2944 if (@_) {
23 0         0 $self->{build_dir} = $_[0];
24 0         0 return $self;
25             }
26             else {
27 839         3240 return $self->{build_dir};
28             }
29             }
30              
31             sub include_dirs {
32 2023     2023 0 7858 my $self = shift;
33 2023 50       5300 if (@_) {
34 0         0 $self->{include_dirs} = $_[0];
35 0         0 return $self;
36             }
37             else {
38 2023         12020 return $self->{include_dirs};
39             }
40             }
41              
42             sub new {
43 2090     2090 0 3400630 my $class = shift;
44            
45             my $self = {
46 2090         8123 include_dirs => [map { "$_/SPVM" } @INC],
  24489         61787  
47             @_
48             };
49            
50 2090   33     13032 bless $self, ref $class || $class;
51            
52 2090         6257 return $self;
53             }
54              
55             sub build_dynamic_lib_dist {
56 0     0 0 0 my ($self, $basic_type_name, $category) = @_;
57            
58             # Create the compiler
59 0         0 my $compiler = SPVM::Builder::Compiler->new(
60             include_dirs => $self->include_dirs
61             );
62            
63 0         0 my $success = $compiler->compile($basic_type_name, __FILE__, __LINE__);
64 0 0       0 unless ($success) {
65 0         0 $compiler->print_error_messages(*STDERR);
66 0         0 exit(255);
67             }
68 0         0 my $runtime = $compiler->get_runtime;
69 0         0 my $class_file = $runtime->get_class_file($basic_type_name);
70 0         0 my $method_names = $runtime->get_method_names($basic_type_name, $category);
71 0         0 my $precompile_source = $runtime->build_precompile_module_source($basic_type_name);
72 0         0 my $dl_func_list = SPVM::Builder::Util::create_dl_func_list($basic_type_name, $method_names, {category => $category});
73            
74 0         0 $self->build_dist($basic_type_name, {category => $category, class_file => $class_file, dl_func_list => $dl_func_list, precompile_source => $precompile_source});
75             }
76              
77             sub build_dist {
78 0     0 0 0 my ($self, $basic_type_name, $options) = @_;
79            
80 0   0     0 $options ||= {};
81            
82 0         0 my $build_dir = $self->build_dir;
83            
84 0         0 my $dl_func_list = $options->{dl_func_list};
85 0         0 my $class_file = $options->{class_file};
86 0         0 my $precompile_source = $options->{precompile_source};
87            
88 0         0 my $category = $options->{category};
89            
90 0         0 my $build_src_dir;
91 0 0       0 if ($category eq 'precompile') {
    0          
92 0         0 $build_src_dir = SPVM::Builder::Util::create_build_src_path($build_dir);
93 0         0 mkpath $build_src_dir;
94            
95 0         0 my $cc = SPVM::Builder::CC->new(
96             build_dir => $build_dir,
97             );
98            
99 0         0 $cc->build_precompile_module_source_file(
100             $basic_type_name,
101             {
102             output_dir => $build_src_dir,
103             precompile_source => $precompile_source,
104             class_file => $class_file,
105             }
106             );
107             }
108             elsif ($category eq 'native') {
109 0         0 $build_src_dir = 'lib';
110             }
111              
112 0         0 my $build_object_dir = SPVM::Builder::Util::create_build_object_path($build_dir);
113 0         0 mkpath $build_object_dir;
114            
115 0         0 my $build_lib_dir = 'blib/lib';
116            
117 0         0 $self->build(
118             $basic_type_name,
119             {
120             compile_input_dir => $build_src_dir,
121             compile_output_dir => $build_object_dir,
122             link_output_dir => $build_lib_dir,
123             category => $category,
124             class_file => $class_file,
125             dl_func_list => $dl_func_list,
126             }
127             );
128             }
129              
130             sub build_dynamic_lib_dist_precompile {
131 0     0 0 0 my ($self, $basic_type_name) = @_;
132            
133 0         0 $self->build_dynamic_lib_dist($basic_type_name, 'precompile');
134             }
135              
136             sub build_dynamic_lib_dist_native {
137 0     0 0 0 my ($self, $basic_type_name) = @_;
138            
139 0         0 $self->build_dynamic_lib_dist($basic_type_name, 'native');
140             }
141              
142             sub build_at_runtime {
143 329     329 0 1490 my ($self, $basic_type_name, $options) = @_;
144            
145 329   50     1369 $options ||= {};
146            
147 329         2138 my $build_dir = $self->build_dir;
148            
149 329         1215 my $dl_func_list = $options->{dl_func_list};
150 329         895 my $class_file = $options->{class_file};
151 329         1446 my $precompile_source = $options->{precompile_source};
152              
153 329         1114 my $category = $options->{category};
154            
155             # Build directory
156 329 50       1391 if (defined $build_dir) {
157 329         24379 mkpath $build_dir;
158             }
159             else {
160 0         0 confess "The \"build_dir\" field must be defined to build a $category method at runtime. Perhaps the setting of the SPVM_BUILD_DIR environment variable is forgotten";
161             }
162            
163             # Source directory
164 329         1459 my $build_src_dir;
165 329 100       2001 if ($category eq 'precompile') {
    50          
166 307         2841 $build_src_dir = SPVM::Builder::Util::create_build_src_path($build_dir);
167 307         10293 mkpath $build_src_dir;
168            
169 307         5559 my $cc = SPVM::Builder::CC->new(
170             build_dir => $build_dir,
171             at_runtime => 1,
172             );
173            
174 307         3368 $cc->build_precompile_module_source_file(
175             $basic_type_name,
176             {
177             output_dir => $build_src_dir,
178             precompile_source => $precompile_source,
179             class_file => $class_file,
180             }
181             );
182             }
183             elsif ($category eq 'native') {
184 22         78 my $class_file = $options->{class_file};
185 22         216 $build_src_dir = SPVM::Builder::Util::remove_basic_type_name_part_from_file($class_file, $basic_type_name);
186             }
187            
188             # Object directory
189 329         3432 my $build_object_dir = SPVM::Builder::Util::create_build_object_path($build_dir);
190 329         15614 mkpath $build_object_dir;
191            
192             # Lib directory
193 329         3693 my $build_lib_dir = SPVM::Builder::Util::create_build_lib_path($build_dir);
194 329         9247 mkpath $build_lib_dir;
195            
196 329         5124 my $build_file = $self->build(
197             $basic_type_name,
198             {
199             compile_input_dir => $build_src_dir,
200             compile_output_dir => $build_object_dir,
201             link_output_dir => $build_lib_dir,
202             category => $category,
203             class_file => $class_file,
204             dl_func_list => $dl_func_list,
205             at_runtime => 1,
206             }
207             );
208            
209 329         22353 return $build_file;
210             }
211              
212             sub build {
213 329     329 0 1577 my ($self, $basic_type_name, $options) = @_;
214            
215 329   50     2027 $options ||= {};
216            
217 329         1444 my $build_dir = $self->build_dir;
218            
219 329         1199 my $at_runtime = $options->{at_runtime};
220 329         2855 my $cc = SPVM::Builder::CC->new(
221             build_dir => $build_dir,
222             at_runtime => $at_runtime,
223             );
224            
225 329         1239 my $dl_func_list = $options->{dl_func_list};
226            
227 329         1161 my $category = $options->{category};
228            
229             # Class file
230 329         967 my $class_file = $options->{class_file};
231 329 50       1364 unless (defined $class_file) {
232 0         0 my $config_file = SPVM::Builder::Util::get_config_file_from_basic_type_name($basic_type_name);
233 0 0       0 if ($config_file) {
234 0         0 $class_file = $config_file;
235 0         0 $class_file =~ s/\.config$/\.spvm/;
236             }
237             else {
238 0         0 confess "The class file \"$class_file\" is not found";
239             }
240             }
241            
242 329         727 my $config;
243 329 100       1919 if ($category eq 'native') {
    50          
244 22         160 $config = $self->create_native_config_from_class_file($class_file);
245             }
246             elsif ($category eq 'precompile') {
247 307         2470 $config = SPVM::Builder::Util::API::create_default_config();
248             }
249            
250 329         2474 $config->class_name($basic_type_name);
251            
252             # Compile source file and create object files
253             my $compile_options = {
254             input_dir => $options->{compile_input_dir},
255             output_dir => $options->{compile_output_dir},
256 329         2072 config => $config,
257             category => $category,
258             };
259              
260 329         3601 my $object_files = $cc->compile_source_files($basic_type_name, $compile_options);
261            
262             # Link object files and create dynamic library
263             my $link_options = {
264             output_dir => $options->{link_output_dir},
265 329         10058 config => $config,
266             category => $category,
267             dl_func_list => $dl_func_list,
268             };
269 329         6931 my $output_file = $cc->link(
270             $basic_type_name,
271             $object_files,
272             $link_options
273             );
274            
275 329         65492 return $output_file;
276             }
277              
278             sub create_native_config_from_class_file {
279 23     23 0 105 my ($self, $class_file) = @_;
280            
281 23         66 my $config;
282 23         68 my $config_file = $class_file;
283 23         258 $config_file =~ s/\.spvm$/.config/;
284              
285             # Config file
286 23 50       537 if (-f $config_file) {
287 23         471 $config = SPVM::Builder::Config->load_config($config_file);
288             }
289             else {
290 0         0 my $error = $self->_error_message_find_config($config_file);
291 0         0 confess $error;
292             }
293            
294 23         81 return $config;
295             }
296              
297             sub _error_message_find_config {
298 0     0     my ($self, $config_file) = @_;
299            
300 0           my $error = <<"EOS";
301             Can't find the native config file \"$config_file\".
302              
303             The config file must contain at least the following code.
304             ----------------------------------------------
305             use strict;
306             use warnings;
307              
308             use SPVM::Builder::Config;
309             my \$config = SPVM::Builder::Config->new_gnu99(file => __FILE__);
310              
311             \$config;
312             ----------------------------------------------
313             EOS
314            
315             }
316              
317             1;
318              
319             =encoding utf8
320              
321             =head1 Name
322              
323             SPVM::Builder - Build Dynamic Libraries for SPVM Distribution
324              
325             =head1 Description
326              
327             The SPVM::Builder class has methods to build dynamic librares for a SPVM distribution.
328              
329             =head1 Copyright & License
330              
331             Copyright (c) 2023 Yuki Kimoto
332              
333             MIT License